Webhooks Model
Tonder's two webhook formats, which integration receives each, and how to read them.
Webhooks are automated messages Tonder sends when transaction events occur. Instead of repeatedly polling our API for status changes, webhooks notify you in real time the moment something happens — a payment completing, failing, or requiring additional authentication.
Two formats: Short and Long
Tonder sends two different webhook formats. Which one you receive depends on your integration mode:
- API Direct, Lite SDK 2.0 and Hosted Checkout 2.0 → Short format
- Mobile SDKs / Legacy (including Hosted Checkout accounts still on the legacy flow) → Long format
| Characteristic | Short format | Long format |
|---|---|---|
| Root structure | Top-level fields | Everything inside data{} |
| Status field | status: Pending / Success | transaction_status: Pending / Success |
| Event indicator | event_type: payment_Pending | event: created / confirmed |
| Order reference | client_reference | metadata.external_id / order_id |
| SPEI CLABE | Not included | data.clabe |
| Sender info (SPEI Success) | Not included | sender_name, sender_clabe, sender_bank… |
_incoming_request | Not included | metadata._incoming_request (full object) |
| Integration modes | API Direct · Lite SDK 2.0 · Frictionless SPEI · Hosted Checkout 2.0 | Mobile SDKs · Legacy |
Same transaction, two structures
The example below is a SPEI event in both formats:
{
"id": "78eb98ef-65a8-4038-a2a0",
"operation_type": "payment",
"amount": "500",
"currency": "MXN",
"client_reference": "ORD-001",
"status": "Pending",
"provider": "tonder",
"transaction_id": "26292298-de5e-4edc",
"payment_method_type": "SPEI",
"created": "2026-05-21T19:51:18Z",
"metadata": {
"order_id": "ORD-001",
"external_id": "ORD-001",
"transaction_type": "deposit"
},
"event_type": "payment_Pending",
"action": "MODIFY"
}{
"operation_type": "payment",
"action": "payment",
"event": "created",
"type": "deposit",
"data": {
"id": "3195511b-fd2e-4844-bf02",
"transaction_status": "Pending",
"amount": 500.0,
"currency_code": "MXN",
"payment_id": 5117665,
"payment_method_name": "spei",
"clabe": "710969000312511566",
"concept": "SPEI deposit for payment...",
"metadata": {
"external_id": "ORD-001",
"order_id": "ORD-001",
"transaction_type": "deposit"
}
}
}It's the same transaction with two completely different structures. Confirm your format with your Tonder integration manager before building your webhook handler.
Withdrawals
Withdrawal webhooks have two formats of their own, and which one you receive depends on how your
account is configured. The flat format has the same
shape as the Short payment format (status: Pending, event_type: withdrawal_Pending,
transfer_method_type instead of payment_method_type). The extended format nests the full
withdrawal record under withdrawal{} and carries the status in the internal names
(PAID_FULL). Both are documented, with examples, in
Withdrawals.
Correlation key
Reference field names differ across modes, but metadata.external_id arrives in every
format and event. Use it as your correlation key.
| Mode | Request field (you send) | Webhook field (you read) |
|---|---|---|
| Hosted Checkout | external_id | metadata.external_id |
| SDK (any platform) | orderReference | metadata.external_id or metadata.order_id |
| API Direct | client_reference | client_reference (top-level) + metadata.external_id |
| Hybrid | both, per leg | both formats — correlate on metadata.external_id |
Universal rule: populate metadata.external_id on every request — it's the only field
guaranteed to arrive in every webhook of every format.
