Payout webhooks
Signed notifications about payout status changes
We send a signed POST request on every payout status change. Polling GET /v1/payouts/{payoutId} is a fallback, not a replacement.
Where notifications are sent
The address is resolved in this order:
callbackUrlpassed in the body ofPOST /v1/payouts: applies to this payout only;callbackUrlfrom your merchant settings;- if neither is set, no notifications are sent for the payout.
The address is fixed when the payout is created. Changing the merchant settings later does not move notifications of an already created payout to the new address.
callbackUrl requirements
| Requirement | Value |
|---|---|
| Scheme | http or https |
| Length | up to 2048 characters |
| Validation | invalid URL → 400 Validation failed |
The field is returned in the payout object. null means the merchant settings are used.
When notifications are sent
A notification is generated on every status change with three exceptions:
CREATED: no notification;COMPLETED: the notification is queued within the payout completion transaction, once the final fee is known;- a manual change of a
COMPLETEDpayout because of an error on the bank side: the notification is sent on request, ask your manager.
Your manager can also resend a notification manually. Duplicate records are excluded by the combination of address, payout ID and status among unsent notifications.
Notification format
POST {callbackUrl}
Content-Type: application/json
X-Type: PAYOUT_UPDATE
X-Signature: HMAC-SHA512(request body, API token) in hexBody
{
"id": "0198c3f1-2a4b-7c3d-9e0f-1a2b3c4d5e6f",
"status": "COMPLETED",
"amount": "1000.5",
"fee": "60",
"currency": "RUB",
"type": "C2C",
"note": null,
"externalId": "payout-123",
"merchantId": "0198c3f1-1111-7222-8333-444455556666",
"settlementCurrency": "USD",
"settlementAmount": "11.28",
"createdAt": "2026-07-29T10:00:00.000Z",
"completedAt": "2026-07-29T10:04:12.317Z"
}| Field | Type | Description |
|---|---|---|
id | string (UUID) | Payout ID |
status | string | Current status, see Payout statuses |
amount | string | Payout amount |
fee | string | Fee charged for the payout |
currency | string | Payout currency |
type | string | Payment type |
note | string / null | Payout note |
externalId | string / null | Payout ID in your system |
merchantId | string (UUID) | Your merchant ID |
settlementCurrency | string / null | Settlement currency |
settlementAmount | string / null | Amount in the settlement currency |
createdAt | string | Creation time |
completedAt | string / null | Completion time |
callbackUrl is not included in the body: it defines the delivery address and is not part of the payout state.
Verify the signature
Every notification carries the X-Signature header: an HMAC-SHA512 hash of the raw request body in hex, computed with your API token as the secret. The same scheme is used for payment webhooks.
import crypto from "crypto";
// Your API token is the HMAC secret
const apiToken = process.env.API_TOKEN;
app.post("/webhooks/payments", (req, res) => {
const signature = req.headers["x-signature"];
const body = JSON.stringify(req.body);
// Calculate HMAC-SHA512 of the raw request body and compare in hex
const expected = crypto.createHmac("sha512", apiToken).update(body).digest("hex");
if (expected !== signature) {
return res.status(403).send("Invalid signature");
}
// Signature is valid — process the notification idempotently
const data = req.body;
if (data.status === "COMPLETED") {
console.log(`Order ${data.externalId} has been paid`);
}
res.status(200).send("OK");
});If one endpoint receives both payment and payout notifications, distinguish them by the X-Type: PAYOUT_UPDATE header.
Retry policy
| Parameter | Value |
|---|---|
| Attempts | up to 30 |
| Interval | 30s × 2^n, capped at 30 minutes |
| Success criterion | HTTP status 200–399 |
A circuit breaker is also applied: an address that consistently fails is temporarily excluded from delivery.
Webhook recommendations
- Always respond with HTTP
200to confirm receipt. If your server does not respond, we retry the notification. - Verify the
X-Signatureheader before processing the body. - Process notifications idempotently: the same notification may be delivered more than once.
- Match the notification to your records by
externalId, not by amount. - Handle every possible status value, including the unsuccessful ones.