Event delivery to your webhook
eventDeliveryJeton sends this request to the webhook URL set in the merchant portal.
Every delivery is an Event envelope, signed as the Standard Webhooks specification
(https://www.standardwebhooks.com/) defines, so any of its libraries verifies it. Verify the signature before
trusting the body:
key = base64decode(secret without its "whsec_" prefix)
expected = "v1," + base64(HMAC-SHA256(key, "{webhook-id}.{webhook-timestamp}.{rawBody}"))
Compare in constant time against each space-separated entry of webhook-signature, and reject if
|now - webhook-timestamp| > 300 s. While you rotate the secret, verify with the new one and fall back to the
old one. webhook-id is the event id: a retried delivery carries the same one.
Every event goes to the v2 webhook's one address. The address and the secret are read at each attempt, so a change in the portal reaches deliveries still pending.
Respond 2xx within 10 s to acknowledge. Anything else — another status, no answer, an address that
does not resolve or resolves to a private one — is retried 12 times over about three and a half days
(immediately, then 3 s, 5 s, 15 s, 30 s, 1 m, 5 m, 15 m, 30 m, 1 h, 6 h, 24 h, 48 h), after which the
delivery is exhausted and your webhook settings in the portal show the webhook failing until a later
delivery succeeds. The event stays readable in GET /events.
Delivery semantics
- At-least-once, unordered. Retries can reorder events. Deduplicate on
id; for a givendata.id, ignore any snapshot whosedata.updatedAtis older than the last one you processed — the same fieldGETreturns — or simplyGETthe resource when in doubt. datais the full resource exactly asGETreturned it at the moment of the event.- Only outcomes are delivered (listed in the introduction): one notification per result.
GET /eventsalso records the intermediate statuses, creation (*.pending) included, for UI and reconciliation. - Merchants are independent — another merchant's failing or slow endpoint never delays yours.
Parameters
webhook-idstringheaderrequiredThe event id. A retried delivery carries the same one.Example:
evt_019985c24a6e7c3b9f102d5e8a1b3c4dpattern ^evt_[0-9a-f]{32}$
webhook-timestampinteger (int64)headerrequiredUnix seconds when the delivery was signed.webhook-signaturestringheaderrequiredv1,followed by the base64 signature; more than one space-separated entry is possible.Example:
v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=
Request body application/jsonrequired
objectstringrequiredAllowed values:
eventidstringrequiredStable across retries — deduplicate on this.Example:
evt_019985c24a6e7c3b9f102d5e8a1b3c4dpattern ^evt_[0-9a-f]{32}$
typestringrequiredAllowed values:
payment.pendingpayment.processingpayment.completedpayment.failedpayment.expiredpayment.cancelledpayment.refundedpayout.pendingpayout.processingpayout.completedpayout.failedpayout.cancelledtransfer.pendingtransfer.completedtransfer.failedfx_exchange.pendingfx_exchange.processingfx_exchange.completedfx_exchange.failedfx_exchange.cancelledversionstringrequiredEnvelope version. Additive changes — a new field, a new event type, a new enum value — never bump it.Allowed values:
1createdAtstring (date-time)requiredRFC 3339, UTC, with millisecond precision.Example:
2026-09-18T10:15:30.123ZdataobjectrequiredThe full resource asGETwould return it at the time of the event.One of
Payment
settledAmount,descriptionandmetadataare not reported yet.feeandcompletedAtappear once the payment is completed; payments completed before they were recorded do not carry them.updatedAtis the last change — a new status, or the customer opening the checkout — and never moves backwards.objectstringrequiredAllowed values:
paymentidstringrequiredA payment's id. Opaque — use it as you received it.Example:
pay_019985c24a6e7c3b9f102d5e8a1b3c4dpattern ^pay_[0-9a-f]{32}$
merchantReferencestringrequiredYour own reference for this payment, as you sent it.statusstringrequiredpendingawaiting the customer — also after a failed attempt, while the customer may retry ·processingcustomer acted, settlement in flight ·completedfunds credited ·failednot used for payments: one never paid endsexpired·cancelled·expiredcheckout TTL elapsed unpaid (a customer who committed just before the deadline can still turn itcompleted) ·refunded. (partially_refundedis not reported yet.)Allowed values:
pendingprocessingcompletedfailedcancelledexpiredrefundedamountMoneyrequiredDecimal amount as a string — never a float, never minor units. An amount you send may carry no more decimal places than its currency has (and never more than 8); trailing zeros do not count, so10.100 EURis fine and10.123 EURis400 validation_failedonamount.value.feeobjectPresent once the payment is completed: the fee charged to you on it, in the payment's currency. You were creditedamountless this.methodstringrequiredExample:
jeton_walletcheckoutobjectPresent while the payment ispendingorprocessing.customerCustomerThe payer. Only the e-mail is reported for now; name, phone, country and locale are not provided yet.payerobjectPresent once the payment is completed: the Jeton user who actually paid. Distinct fromcustomer, which is what you sent. Passpayer.customerNumberasdestination.customerNumberonPOST /payoutsto pay the same customer back.languagestringcreatedAtstring (date-time)requiredRFC 3339, UTC, with millisecond precision.Example:
2026-09-18T10:15:30.123ZupdatedAtstring (date-time)requiredLast change. It never moves backwards, so use it to discard stale webhook snapshots.Example:
2026-09-18T10:15:30.123ZcompletedAtstring (date-time)Present once the payment is completed — when the funds were credited.Example:
2026-09-18T10:15:30.123Z
Payout
objectstringrequiredAllowed values:
payoutidstringrequiredThe ledger id of the payout's debit — the same entryGET /accounts/{accountId}/transactionsshows.Example:
txn_AAAAAAAAB3Ypattern ^txn_[A-Za-z0-9_-]{11}$
merchantReferencestringYour own reference for this payout, as you sent it. Absent on a payout made without one (portal, batch).statusstringrequiredLifecycle of a payout.pendingaccepted, not sent yet (a payout held for review included — the hold is never named) ·processingexecuting on the rail ·completed·failed·cancelledwithdrawn by the platform before it was sent. Terminal:completed,failed,cancelled.Allowed values:
pendingprocessingcompletedfailedcancelledmethodstringrequiredWhich payout product moved the money — the same vocabulary payments use for money coming in. Derived by the server from the destination; it is never sent. It decides the fee, the limits and how the payout behaves:jeton_walletsettles instantly on the internal ledger,bank_transfertravels on a bank rail,cryptosettles on-chain.Allowed values:
jeton_walletbank_transfercryptoamountMoneyrequiredDecimal amount as a string — never a float, never minor units. An amount you send may carry no more decimal places than its currency has (and never more than 8); trailing zeros do not count, so10.100 EURis fine and10.123 EURis400 validation_failedonamount.value.feeobjectrequiredFee charged on top ofamount, in the payout currency. On thePOSTresponse, the fee quoted the moment before the payout was sent — a bank payout is charged exactly that; a read shows what the ledger charged.totalDebitedobjectrequiredamount + fee: what leaves the source account.destinationone ofrequiredWhere the money went, as the ledger records it: a bank account'siban,accountHolderName,bicandcountry; a crypto wallet'snetworkandaddress; a Jeton payout's individual byaccountHolderName, the name the ledger shows — never by thecustomerNumber,emailorphoneit was sent to, and not on thePOSTresponse.sourceAccountIdstringrequiredOpaque account identifier. Treat as a string; never parse or construct it.Example:
acc_AAAAAAAAAAcpattern ^acc_[A-Za-z0-9_-]{11}$
railstringHow the money travelled within its method.sepa·sepa_instant·swift·instant_bank_transferforbank_transfer,cryptoforcrypto,jeton_internalforjeton_wallet.Allowed values:
sepasepa_instantswiftinstant_bank_transfercryptojeton_internaldescriptionstringfailureobjectPresent whenstatusisfailed.updatedAtstring (date-time)requiredThe last change the ledger recorded on the payout.Example:
2026-09-18T10:15:30.123ZcreatedAtstring (date-time)requiredRFC 3339, UTC, with millisecond precision.Example:
2026-09-18T10:15:30.123ZcompletedAtstring (date-time)RFC 3339, UTC, with millisecond precision.Example:
2026-09-18T10:15:30.123Z
Transfer
objectstringrequiredAllowed values:
transferidstringrequiredThe ledger id of the transfer's debit — the same entryGET /accounts/{accountId}/transactionsshows.Example:
txn_AAAAAAAAB3Ypattern ^txn_[A-Za-z0-9_-]{11}$
merchantReferencestringAbsent on a transfer made in the portal.statusstringrequiredpendingaccepted, not settled yet ·completedsettled ·failedrefused, rejected or expired — no money moved.Allowed values:
pendingcompletedfailedamountMoneyrequiredDecimal amount as a string — never a float, never minor units. An amount you send may carry no more decimal places than its currency has (and never more than 8); trailing zeros do not count, so10.100 EURis fine and10.123 EURis400 validation_failedonamount.value.feeobjectrequiredCharged on top ofamount; zero between your own accounts.totalDebitedobjectrequiredWhat leaves the source account:amount + fee.sourceAccountIdstringrequiredOpaque account identifier. Treat as a string; never parse or construct it.Example:
acc_AAAAAAAAAAcpattern ^acc_[A-Za-z0-9_-]{11}$
destinationTransferDestinationrequiredown_accountwithaccountId, orbusinesswith exactly one ofemailandmerchantNumber. On the resource: the type, and forown_accounttheaccountId. A business's e-mail or number is not kept, and its account is never shown;counterpartyNameon the ledger entry says who received it.descriptionstringfailureobjectPresent whenstatusisfailed.createdAtstring (date-time)requiredRFC 3339, UTC, with millisecond precision.Example:
2026-09-18T10:15:30.123ZupdatedAtstring (date-time)requiredRFC 3339, UTC, with millisecond precision.Example:
2026-09-18T10:15:30.123ZcompletedAtstring (date-time)RFC 3339, UTC, with millisecond precision.Example:
2026-09-18T10:15:30.123Z
FxExchange
objectstringrequiredAllowed values:
fx_exchangeidstringrequiredAn exchange's id. Opaque — use it as you received it.Example:
fx_019985c24a6e7c3b9f102d5e8a1b3c4dpattern ^fx_[0-9a-f]{32}$
merchantReferencestringAbsent on an exchange made in the portal.statusstringrequiredAllowed values:
pendingprocessingcompletedfailedcancelledsellMoneyrequiredDecimal amount as a string — never a float, never minor units. An amount you send may carry no more decimal places than its currency has (and never more than 8); trailing zeros do not count, so10.100 EURis fine and10.123 EURis400 validation_failedonamount.value.buyobjectThe quoted amount until a venue exchange settles; then what it filled.ratestringThe rate applied, as a string.feeobjectAbsent while it is not known — as on a portal exchange that has not executed yet, and on some older portal exchanges.totalDebitedobjectPresent withfee.sellAccountIdstringrequiredOpaque account identifier. Treat as a string; never parse or construct it.Example:
acc_AAAAAAAAAAcpattern ^acc_[A-Za-z0-9_-]{11}$
buyAccountIdstringOpaque account identifier. Treat as a string; never parse or construct it.Example:
acc_AAAAAAAAAAcpattern ^acc_[A-Za-z0-9_-]{11}$
createdAtstring (date-time)requiredRFC 3339, UTC, with millisecond precision.Example:
2026-09-18T10:15:30.123ZupdatedAtstring (date-time)requiredRFC 3339, UTC, with millisecond precision.Example:
2026-09-18T10:15:30.123ZcompletedAtstring (date-time)RFC 3339, UTC, with millisecond precision.Example:
2026-09-18T10:15:30.123Z
deliveryobjectHow the event's delivery to your webhook stands. Present inGET /eventsresponses, never in the delivered payload — and absent for an intermediate status (only outcomes are delivered) and when your webhook did not take native v2 events when the event happened.failingis retrying after a failed attempt;exhaustedran out of retries.attemptsintegerlastAttemptAtstring (date-time)Absent until the first attempt.Example:
2026-09-18T10:15:30.123ZlastStatusCodeintegernullablestatusstringAllowed values:
pendingdeliveredfailingexhausted
Responses
Acknowledged.