Billing/Webhooks
Webhook Security, Event Types, & Best Practices
Webhook Signature Verification
To ensure authenticity, Acta signs every webhook request using HMAC-SHA256. You must verify this signature to confirm that the payload came from Acta and wasn't tampered with.
Headers in Every Webhook Request:
Header | Description |
---|---|
x-actalink-signature | Final HMAC-SHA256 signature of the timestamp and payload |
x-actalink-timestamp | Unix timestamp (in milliseconds) used in the signature |
How to Verify the Webhook Signature:
Extract the following from the request
x-actalink-signature
header (Acta's signature)x-actalink-timestamp
header (timestamp used during signing)- Raw JSON request body (exactly as received, not parsed)
Recompute the signature
- Wrap the raw payload in an object.
- Serialize it with
JSON.stringify()
(no added whitespace). - Compute HMAC-SHA256 of the stringified wrapper using your webhook secret.
- Concatenate the timestamp + "." + that hash.
- Compute HMAC-SHA256 of that final string.
Compare your computed signature with the one from x-actalink-signature.
Optionally, reject the webhook if the timestamp is too old (e.g. older than 5 minutes) to prevent replay attacks.
Node.js Example
const crypto = require("crypto");
function verifyWebhookSignature({
rawBody,
timestamp,
receivedSignature,
secret,
}) {
const payload = JSON.parse(rawBody);
const wrapped = JSON.stringify({ payload });
const intermediateSig = crypto
.createHmac("sha256", secret)
.update(wrapped)
.digest("hex");
const finalSig = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${intermediateSig}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(finalSig),
Buffer.from(receivedSignature)
);
}
const rawBody = JSON.stringify({
id: "c837a151-a962-44e0-b3e3-b4f61743d7bb",
eventType: "subscription.billing.due",
eventData: {
data: {
id: "sub_jeRBLFCOuK2jE8Q1209xnPh5g3ELmfnP",
token: {
name: "USD Coin",
amount: "0x249f0",
symbol: "USDC",
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
chainId: 137,
fiatISO: "USD",
logoURI: "https://api.acta.link/deposit/v1/logos/usdc.png",
decimals: 6,
logoSourceURI: "https://api.acta.link/deposit/v1/logos/usdc.png",
},
reason: "BALANCE",
status: "DUE",
chainId: 137,
network: {
name: "Polygon",
chainId: 137,
},
attempts: 0,
currency: "USDC",
transaction: null,
feeInclusive: false,
intervalUnit: "5mins",
tokenAddress: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
intervalCount: 2,
senderAddress: "0x061BA68bc8208F4AddBeE86F74F17D77129cCF70",
nextExecutionAt: "2025-08-16T14:27:02.098Z",
receiverAddress: "0xEBFa37194fA74bA3e8195446948FC3B9c72E08AB",
effectedInterval: 1,
expectedExecutionAt: "2025-08-16T14:20:55.000Z",
latestIntervalCount: 1,
},
type: "subscription",
paylink: {
id: "paylink_xsOVX8sNubCCpPSsmMlmy7llRAkhHyvM",
name: "Hooli Nucleus Subscription",
status: "active",
paylinkType: "subscription",
},
plan: {
id: "prod_jy7Z3JdTuTf1uBqhFFl7x23AdoPJjIRX",
name: "Hooi Nucleus",
price: {
id: "price_kBN95XmlS6kUCNJu98ZpAoc4WT6UX3A2",
price: "0.15",
currency: "USDC",
currencyType: "crypto",
intervalUnit: "5mins",
intervalCount: 2
},
status: "ACTIVE",
imageUrl: "",
description: "Monthly subscription for Hooli Nucleus",
paymentType: "recurring"
}
organisationId: "SwHuo5b9UCc43wFCphlC0uQCkQQVvcga",
},
createdAt: "2025-08-16T14:22:02.125Z",
});
const timestamp = 1755354122183; // x-actalink-timestamp
const receivedSignature =
"e5d8cf0d6cd3294adec97c1eaa2cfa42a5d3e6a9f421530d231bdf57a56222af"; // x-actalink-signature
const secret = "..."; // from Acta HUB dashboard
const result = verifyWebhookSignature({
rawBody,
timestamp,
receivedSignature,
secret,
});
if (result) {
console.log("✅ Signature is valid");
} else {
console.log("❌ Signature is invalid");
}
Webhook Event Types
Acta Deposit supports the following webhook event types:
single.billing.executed
{
"id": "1f3d9b55-2930-4cbc-a96e-210f25a64944",
"eventType": "single.billing.executed",
"eventData": {
"data": {
"id": "txn_E8v3z08dxUtyZFivjbAidJbdDS1pcrvz",
"token": {
"name": "USD Coin",
"amount": "0xf4240",
"symbol": "USDC",
"address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"chainId": 137,
"fiatISO": "USD",
"logoURI": "https://api.acta.link/deposit/v1/logos/usdc.png",
"decimals": 6,
"logoSourceURI": "https://api.acta.link/deposit/v1/logos/usdc.png"
},
"status": "EXECUTED",
"chainId": 137,
"network": {
"name": "Polygon",
"chainId": 137
},
"currency": "USDC",
"transaction": {
"id": "txn_E8v3z08dxUtyZFivjbAidJbdDS1pcrvz",
"fee": "0x2f6ed",
"hash": "0x66961cffada0290a4872ee42e92e7faca4bf30493cbc589786fbefe544e0c95e",
"amount": "0xf4240",
"status": "EXECUTED",
"chainId": 137,
"paylinkId": "paylink_V2iUszhF1qH1KhEn1wQB2lkx9boHnqeh",
"executedAt": "2025-08-16T14:03:47.833Z",
"executedBy": "0x061BA68bc8208F4AddBeE86F74F17D77129cCF70",
"paymentType": "one-time",
"feeInclusive": false,
"tokenAddress": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"senderAddress": "0x061BA68bc8208F4AddBeE86F74F17D77129cCF70",
"organizationId": "SwHuo5b9UCc43wFCphlC0uQCkQQVvcga",
"receiverAddress": "0xEBFa37194fA74bA3e8195446948FC3B9c72E08AB"
},
"feeInclusive": false,
"tokenAddress": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"senderAddress": "0x061BA68bc8208F4AddBeE86F74F17D77129cCF70",
"receiverAddress": "0xEBFa37194fA74bA3e8195446948FC3B9c72E08AB"
},
"type": "one-time",
"paylink": {
"id": "paylink_V2iUszhF1qH1KhEn1wQB2lkx9boHnqeh",
"name": "Hooli Music One time",
"status": "active",
"paylinkType": "one-time"
},
"plan": {
"id": "prod_jy7Z3JdTuTf1uBqhFFl7x23AdoPJjIRX",
"name": "Hooi Nucleus",
"price": {
"id": "price_kBN95XmlS6kUCNJu98ZpAoc4WT6UX3A2",
"price": "0.15",
"currency": "USDC",
"currencyType": "crypto",
"intervalUnit": "5mins",
"intervalCount": 2
},
"status": "ACTIVE",
"imageUrl": "",
"description": "Monthly subscription for Hooli Nucleus",
"paymentType": "recurring"
}
"organisationId": "SwHuo5b9UCc43wFCphlC0uQCkQQVvcga"
},
"createdAt": "2025-08-16T14:03:47.832Z"
}
subscription.billing.executed
{
"id": "8694f943-753a-433c-b28e-4b3576682079",
"eventType": "subscription.billing.executed",
"eventData": {
"data": {
"id": "sub_PPBseoxbK9p5fMTe3SttfoYIBeDAHii7",
"token": {
"name": "USD Coin",
"amount": "0x1adb0",
"symbol": "USDC",
"address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"chainId": 137,
"fiatISO": "USD",
"logoURI": "https://api.acta.link/deposit/v1/logos/usdc.png",
"decimals": 6,
"logoSourceURI": "https://api.acta.link/deposit/v1/logos/usdc.png"
},
"status": "ONGOING",
"chainId": 137,
"network": {
"name": "Polygon",
"chainId": 137
},
"currency": "USDC",
"startedAt": "2025-08-16T14:04:40.000Z",
"transaction": {
"id": "txn_aYkHIR0b36sRiJTccCU8wSqI6ku4jAJw",
"fee": "0xe1075",
"hash": "0xfe8e7c5e60c8834ecc7e1ef38e34dd169c5ec17c8f136d61b20f1ddf2ae2c90a",
"amount": "0x1adb0",
"status": "EXECUTED",
"chainId": 137,
"paylinkId": "paylink_vJGyzXpjuQb7Z7zXpcuIwVvjkkFFhMmI",
"executedAt": "2025-08-16T14:05:06.311Z",
"executedBy": "0xFF01d8625923C382c4e6fb1307749f10c48908AF",
"paymentType": "subscription",
"feeInclusive": false,
"tokenAddress": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"senderAddress": "0x061BA68bc8208F4AddBeE86F74F17D77129cCF70",
"organizationId": "SwHuo5b9UCc43wFCphlC0uQCkQQVvcga",
"receiverAddress": "0xEBFa37194fA74bA3e8195446948FC3B9c72E08AB"
},
"feeInclusive": false,
"intervalUnit": "5mins",
"tokenAddress": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"intervalCount": 2,
"senderAddress": "0x061BA68bc8208F4AddBeE86F74F17D77129cCF70",
"nextExecutionAt": "2025-08-16T14:10:06.305Z",
"receiverAddress": "0xEBFa37194fA74bA3e8195446948FC3B9c72E08AB",
"effectedInterval": 1,
"latestIntervalCount": 1
},
"type": "subscription",
"paylink": {
"id": "paylink_vJGyzXpjuQb7Z7zXpcuIwVvjkkFFhMmI",
"name": "Hooli Music Subscription",
"status": "active",
"paylinkType": "subscription"
},
"plan": {
"id": "prod_jy7Z3JdTuTf1uBqhFFl7x23AdoPJjIRX",
"name": "Hooi Nucleus",
"price": {
"id": "price_kBN95XmlS6kUCNJu98ZpAoc4WT6UX3A2",
"price": "0.15",
"currency": "USDC",
"currencyType": "crypto",
"intervalUnit": "5mins",
"intervalCount": 2
}
},
"organisationId": "SwHuo5b9UCc43wFCphlC0uQCkQQVvcga"
},
"createdAt": "2025-08-16T14:05:11.956Z"
}
subscription.billing.failed
{
"id": "e91848ca-cb47-46f3-86f1-86b47010d5e0",
"eventType": "subscription.billing.failed",
"eventData": {
"toJSON": {
"data": {
"id": "sub_jeRBLFCOuK2jE8Q1209xnPh5g3ELmfnP",
"token": {
"name": "USD Coin",
"amount": "3000000",
"symbol": "USDC",
"address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"chainId": 137,
"fiatISO": "USD",
"logoURI": "https://api.acta.link/deposit/v1/logos/usdc.png",
"decimals": 6,
"logoSourceURI": "https://api.acta.link/deposit/v1/logos/usdc.png"
},
"reason": "BALANCE",
"status": "FAILED",
"chainId": 137,
"network": {
"name": "Polygon",
"chainId": 137
},
"attempts": 3,
"currency": "USDC",
"startedAt": "2025-07-27T15:46:00.000Z",
"feeInclusive": true,
"intervalUnit": "5mins",
"tokenAddress": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"intervalCount": 3,
"senderAddress": "0x061BA68bc8208F4AddBeE86F74F17D77129cCF70",
"nextExecutionAt": "2025-07-27T16:05:01.240Z",
"receiverAddress": "0xEBFa37194fA74bA3e8195446948FC3B9c72E08AB",
"effectedInterval": 3,
"latestIntervalCount": 3
},
"type": "subscription",
"paylink": {
"id": "paylink_xsOVX8sNubCCpPSsmMlmy7llRAkhHyvM",
"name": "Hooli Nucleus Subscription",
"status": "active",
"paylinkType": "subscription"
},
"plan": {
"id": "prod_jy7Z3JdTuTf1uBqhFFl7x23AdoPJjIRX",
"name": "Hooi Nucleus",
"price": {
"id": "price_kBN95XmlS6kUCNJu98ZpAoc4WT6UX3A2",
"price": "0.15",
"currency": "USDC",
"currencyType": "crypto",
"intervalUnit": "5mins",
"intervalCount": 2
}
},
"organisationId": "f6df600c-eb64-428c-bf58-4b969522572b"
}
},
"createdAt": "2025-07-27T16:24:01.467Z"
}
subscription.billing.scheduled
{
"id": "0fccb912-d1f0-4936-86ca-fdfb0b72c6ab",
"eventType": "subscription.billing.scheduled",
"eventData": {
"data": {
"id": "sub_PPBseoxbK9p5fMTe3SttfoYIBeDAHii7",
"token": {
"name": "USD Coin",
"amount": "0x1adb0",
"symbol": "USDC",
"address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"chainId": 137,
"fiatISO": "USD",
"logoURI": "https://api.acta.link/deposit/v1/logos/usdc.png",
"decimals": 6,
"logoSourceURI": "https://api.acta.link/deposit/v1/logos/usdc.png"
},
"status": "ONGOING",
"chainId": 137,
"network": {
"name": "Polygon",
"chainId": 137
},
"currency": "USDC",
"startedAt": "2025-08-16T14:04:40.000Z",
"transaction": null,
"feeInclusive": false,
"intervalUnit": "5mins",
"tokenAddress": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"intervalCount": 2,
"senderAddress": "0x061BA68bc8208F4AddBeE86F74F17D77129cCF70",
"nextExecutionAt": "2025-08-16T14:04:40.000Z",
"receiverAddress": "0xEBFa37194fA74bA3e8195446948FC3B9c72E08AB",
"latestIntervalCount": 0
},
"type": "subscription",
"paylink": {
"id": "paylink_vJGyzXpjuQb7Z7zXpcuIwVvjkkFFhMmI",
"name": "Hooli Music Subscription",
"status": "active",
"paylinkType": "subscription"
},
"plan": {
"id": "prod_jy7Z3JdTuTf1uBqhFFl7x23AdoPJjIRX",
"name": "Hooi Nucleus",
"price": {
"id": "price_kBN95XmlS6kUCNJu98ZpAoc4WT6UX3A2",
"price": "0.15",
"currency": "USDC",
"currencyType": "crypto",
"intervalUnit": "5mins",
"intervalCount": 2
}
},
"organisationId": "SwHuo5b9UCc43wFCphlC0uQCkQQVvcga"
},
"createdAt": "2025-08-16T14:04:50.137Z"
}
subscription.billing.completed
{
"id": "337288e2-40de-4e4e-b855-f725eca89e92",
"eventType": "subscription.billing.completed",
"eventData": {
"data": {
"id": "sub_PPBseoxbK9p5fMTe3SttfoYIBeDAHii7",
"token": {
"name": "USD Coin",
"amount": "0x1adb0",
"symbol": "USDC",
"address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"chainId": 137,
"fiatISO": "USD",
"logoURI": "https://api.acta.link/deposit/v1/logos/usdc.png",
"decimals": 6,
"logoSourceURI": "https://api.acta.link/deposit/v1/logos/usdc.png"
},
"status": "COMPLETED",
"chainId": 137,
"network": {
"name": "Polygon",
"chainId": 137
},
"currency": "USDC",
"startedAt": "2025-08-16T14:04:40.000Z",
"transaction": {
"id": "txn_8fRiLX0pEtJ1B1Dx0jnun1hfPDqZcYpW",
"fee": "0x4aaa8",
"hash": "0x80618cd40f89283948d681adbfac8890fd647c4ca9b241a1ac188443056d138f",
"amount": "0x1adb0",
"status": "EXECUTED",
"chainId": 137,
"paylinkId": "paylink_vJGyzXpjuQb7Z7zXpcuIwVvjkkFFhMmI",
"executedAt": "2025-08-16T14:11:03.854Z",
"executedBy": "0xFF01d8625923C382c4e6fb1307749f10c48908AF",
"paymentType": "subscription",
"feeInclusive": false,
"tokenAddress": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"senderAddress": "0x061BA68bc8208F4AddBeE86F74F17D77129cCF70",
"organizationId": "SwHuo5b9UCc43wFCphlC0uQCkQQVvcga",
"receiverAddress": "0xEBFa37194fA74bA3e8195446948FC3B9c72E08AB"
},
"feeInclusive": false,
"intervalUnit": "5mins",
"tokenAddress": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"intervalCount": 2,
"senderAddress": "0x061BA68bc8208F4AddBeE86F74F17D77129cCF70",
"nextExecutionAt": "2025-08-16T14:16:03.847Z",
"receiverAddress": "0xEBFa37194fA74bA3e8195446948FC3B9c72E08AB",
"effectedInterval": 2,
"latestIntervalCount": 2
},
"type": "subscription",
"paylink": {
"id": "paylink_vJGyzXpjuQb7Z7zXpcuIwVvjkkFFhMmI",
"name": "Hooli Music Subscription",
"status": "active",
"paylinkType": "subscription"
},
"plan": {
"id": "prod_jy7Z3JdTuTf1uBqhFFl7x23AdoPJjIRX",
"name": "Hooi Nucleus",
"price": {
"id": "price_kBN95XmlS6kUCNJu98ZpAoc4WT6UX3A2",
"price": "0.15",
"currency": "USDC",
"currencyType": "crypto",
"intervalUnit": "5mins",
"intervalCount": 2
}
},
"organisationId": "SwHuo5b9UCc43wFCphlC0uQCkQQVvcga"
},
"createdAt": "2025-08-16T14:11:09.755Z"
}
subscription.billing.due
{
"id": "c837a151-a962-44e0-b3e3-b4f61743d7bb",
"eventType": "subscription.billing.due",
"eventData": {
"data": {
"id": "sub_jeRBLFCOuK2jE8Q1209xnPh5g3ELmfnP",
"token": {
"name": "USD Coin",
"amount": "0x249f0",
"symbol": "USDC",
"address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"chainId": 137,
"fiatISO": "USD",
"logoURI": "https://api.acta.link/deposit/v1/logos/usdc.png",
"decimals": 6,
"logoSourceURI": "https://api.acta.link/deposit/v1/logos/usdc.png"
},
"reason": "BALANCE",
"status": "DUE",
"chainId": 137,
"network": {
"name": "Polygon",
"chainId": 137
},
"attempts": 0,
"currency": "USDC",
"transaction": null,
"feeInclusive": false,
"intervalUnit": "5mins",
"tokenAddress": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"intervalCount": 2,
"senderAddress": "0x061BA68bc8208F4AddBeE86F74F17D77129cCF70",
"nextExecutionAt": "2025-08-16T14:27:02.098Z",
"receiverAddress": "0xEBFa37194fA74bA3e8195446948FC3B9c72E08AB",
"effectedInterval": 1,
"expectedExecutionAt": "2025-08-16T14:20:55.000Z",
"latestIntervalCount": 1
},
"type": "subscription",
"paylink": {
"id": "paylink_xsOVX8sNubCCpPSsmMlmy7llRAkhHyvM",
"name": "Hooli Nucleus Subscription",
"status": "active",
"paylinkType": "subscription"
},
"plan": {
"id": "prod_jy7Z3JdTuTf1uBqhFFl7x23AdoPJjIRX",
"name": "Hooi Nucleus",
"price": {
"id": "price_kBN95XmlS6kUCNJu98ZpAoc4WT6UX3A2",
"price": "0.15",
"currency": "USDC",
"currencyType": "crypto",
"intervalUnit": "5mins",
"intervalCount": 2
}
},
"organisationId": "SwHuo5b9UCc43wFCphlC0uQCkQQVvcga"
},
"createdAt": "2025-08-16T14:22:02.125Z"
}
Best Practices for Webhooks
✅ Always verify the webhook signature to avoid spoofed requests.
✅ Respond quickly with a 200 OK
— don’t wait for downstream logic to finish.
✅ Handle retries properly. If Acta doesn't receive a 2xx
status, we retry using exponential backoff.
Retry Strategy
- Max Attempts: 10
- Initial Delay: 30 seconds
- Backoff Strategy: Exponential (doubles every attempt)
- No Delay Cap: Keeps doubling until final attempt
- Total Retry Window: ~4h 16m
Retry schedule
Attempt | Delay (s) | Delay (min) | Cumulative Time |
---|---|---|---|
1st | 30 | 0.5 | 0:00:30 |
2nd | 60 | 1 | 0:01:30 |
3rd | 120 | 2 | 0:03:30 |
4th | 240 | 4 | 0:07:30 |
5th | 480 | 8 | 0:15:30 |
6th | 960 | 16 | 0:31:30 |
7th | 1920 | 32 | 1:03:30 |
8th | 3840 | 64 | 2:07:30 |
9th | 7680 | 128 | 4:15:30 |
10th | 15360 | 256 | 8:31:30 |
🔁 If all retries fail, the webhook is marked as unsuccessful.