Withdrawals
Send money to a beneficiary by SPEI or debit card, approve it, and follow it to settlement: how to create it, both status vocabularies, the webhooks and the test data.
Withdrawals (payouts) send money from your balance to a beneficiary's 18-digit CLABE by SPEI, or to a 16-digit debit card. They are available for beneficiaries in Mexico, in MXN. For payouts in Argentina (ARS) to a CBU, CVU or alias, see Argentina; for payouts in Chile (CLP) to a checking or sight account, see Withdrawals in Chile.
This guide is written for the integrator. It covers creating a withdrawal and the rest of the cycle: approval, queries, statuses and webhooks.
Tonder picks the rail. The institution that executes the transfer is resolved by Tonder from your account's configuration. You don't need to know it or build logic on it: it can change without any change to your integration.
| Aspect | SPEI | Debit card |
|---|---|---|
| Processing time | Instant (seconds) | Instant (seconds) |
| Availability | 24/7 | 24/7 |
| Destination | 18-digit CLABE | 16-digit card number |
| Best for | Beneficiaries with a CLABE, larger amounts | Beneficiaries who only have a card |
Environments and authentication
Withdrawals live on the API Direct host:
| Service | Sandbox | Production |
|---|---|---|
| API Direct (S2S) | https://stage.tonder.io/api/v1/ | https://app.tonder.io/api/v1/ |
| Hosted Checkout | https://api-stage.tonder.io/checkout/v1/ | https://api.tonder.io/checkout/v1/ |
| SPEI deposits (CLABE per user) | https://api-stage.tonder.io/deposits/ | https://api.tonder.io/deposits/ |
| Card tokenization | https://token-sandbox.tonder.io/ | https://token.tonder.io/ |
| Raw card data (PCI L1) | https://process-sandbox.tonder.io/ | https://process.tonder.io/ |
| Dashboard | https://dashboard-stage.tonder.io/ | https://dashboard.tonder.io/ |
Every request carries your public API key. The secret key does not work for this flow.
Authorization: Token <API_KEY>
Content-Type: application/json| Case | Response |
|---|---|
No Authorization header | 401 · Authentication credentials were not provided. |
| Unknown token | 401 · Invalid token. |
| Token of a disabled user | 401 · User inactive or deleted. |
Create a withdrawal
Create it with POST /api/v1/process/, the same endpoint payments use. What sets a withdrawal
apart is operation_type.
curl -X POST "https://stage.tonder.io/api/v1/process/" \
-H "Authorization: Token YOUR_TEST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operation_type": "withdrawal",
"amount": 20.00,
"currency": "MXN",
"client_reference": "payout-001",
"transfer_method": "SPEI",
"description": "Pago de comisiones",
"beneficiary": {
"account": "012969100000000110",
"name": "Ana Maria Gonzalez",
"rfc": "GOAN850315AB2",
"institution": "40012",
"email": "ana.gonzalez@email.com"
},
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"order_reference": "ORD-001"
},
"fee_model": "01"
}'curl -X POST "https://app.tonder.io/api/v1/process/" \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operation_type": "withdrawal",
"amount": 20.00,
"currency": "MXN",
"client_reference": "payout-001",
"transfer_method": "SPEI",
"description": "Pago de comisiones",
"beneficiary": {
"account": "012969100000000110",
"name": "Ana Maria Gonzalez",
"rfc": "GOAN850315AB2",
"institution": "40012",
"email": "ana.gonzalez@email.com"
},
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"order_reference": "ORD-001"
},
"fee_model": "01"
}'| Field | Required | Description |
|---|---|---|
operation_type | yes | Always "withdrawal" |
amount | yes | The amount. Must be greater than zero |
currency | yes | "MXN". Argentina uses "ARS" — see Argentina; Chile uses "CLP" — see Withdrawals in Chile |
transfer_method | yes | "SPEI" or "DEBIT_CARD"; "BANK_TRANSFER" in Argentina; "CLP_TRANSFER" in Chile |
client_reference | recommended | Your internal reference. Comes back in the response and in the webhooks |
description | no | Payment concept. Defaults to Retiro de fondos if omitted |
beneficiary.account | yes | 18-digit CLABE for SPEI, 16-digit card number for debit card |
beneficiary.name | yes | The holder's name as it appears on the account |
beneficiary.rfc | yes | The beneficiary's RFC or CURP. If you don't have it, send "ND" |
beneficiary.institution | yes | Banxico institution code. See Institution codes |
beneficiary.email | yes | The beneficiary's email |
metadata | no | Your own data. Stored and returned as is |
fee_model | yes | "01" |
Debit card. Compared to SPEI, two things change and nothing else: transfer_method becomes
"DEBIT_CARD" and beneficiary.account carries the 16-digit card number.
Geolocation. metadata.latitude and metadata.longitude are recommended for operations in
Mexico. If your integration already sends them, keep them.
HTTP 201:
{
"id": "a1e2f918-1851-4988-b9de-36d57f41ed56",
"withdrawal_id": "ad3069ed-598e-4a1b-82e0-2279d5af292d",
"operation_type": "withdrawal",
"status": "Pending",
"amount": 20.0,
"currency": "MXN",
"client_reference": "payout-001",
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"order_reference": "ORD-001"
},
"transfer_method": "SPEI",
"created_at": "2026-10-01T17:33:41.364685Z",
"status_code": 201
}Store both identifiers. They are different and not interchangeable.
withdrawal_id is the withdrawal's: approve it, reject it, read its detail.
id is the transaction's: check the status of the operation.
Approve
A newly created withdrawal stays pending and does not advance on its own, unless your account has an automatic-approval rule. If your withdrawals stay there, this is the step that is missing.
From the Dashboard. Open the withdrawal in Withdrawals and press Approve.
By API:
curl -X POST "https://stage.tonder.io/api/v1/withdrawals/perform_action/" \
-H "Authorization: Token YOUR_TEST_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "id": "ad3069ed-598e-4a1b-82e0-2279d5af292d", "action": "approve" }'curl -X POST "https://app.tonder.io/api/v1/withdrawals/perform_action/" \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "id": "ad3069ed-598e-4a1b-82e0-2279d5af292d", "action": "approve" }'Mind which identifier you send here. It's the withdrawal_id of the /process/ response,
not its id.
Sending the id returns 404 Withdrawal not found: 'Item', which misleads because it sounds as
if the withdrawal did not exist.
Actions: approve, reject, cancel, fail, refund.
Idempotency. A second approve on a settled withdrawal answers 409 with
Invalid transition from PAID_FULL to PROCESSING. On a withdrawal in a terminal state, 409 with
Cannot modify withdrawal in terminal state. Retrying does not duplicate the payment.
A nonexistent action answers 400 with Invalid action.
Query
Transaction status
GET /api/v1/transactions/<id>/Takes the transaction id: the id field of the /process/ response.
Withdrawal detail
curl "https://stage.tonder.io/api/v1/withdrawals/ad3069ed-598e-4a1b-82e0-2279d5af292d" \
-H "Authorization: Token YOUR_TEST_API_KEY"curl "https://app.tonder.io/api/v1/withdrawals/ad3069ed-598e-4a1b-82e0-2279d5af292d" \
-H "Authorization: Token YOUR_API_KEY"No trailing slash. With one, it answers 404. An unknown id answers 200 with
{"detail": "Withdrawal not found"} and no record: check for detail before reading the fields.
It is the only endpoint that carries the reason for a failure, in status_changes[].reason and
action.reason:
{
"id": "7b57edd2-6594-4ef6-813b-df0d3d091a5c",
"business_id": "69",
"status": "FAILED",
"amount": 1.7,
"currency": "MXN",
"transfer_method": "SPEI",
"tracking_key": "a61c4f05b1de4645a1d2",
"provider_reference": null,
"account_data": { "...": "..." },
"status_changes": [
{ "from_status": "PENDING", "to_status": "PROCESSING",
"timestamp": "2026-10-13T18:40:23.497368", "reason": null },
{ "from_status": "PROCESSING", "to_status": "FAILED",
"timestamp": "2026-10-13T18:40:23.497375",
"reason": "Provider rejected: invalid_institution_id" }
],
"action": { "action": "FAIL", "reason": "Provider rejected: invalid_institution_id" },
"url_cep": "",
"created_at": "2026-10-13T02:06:17.772297Z",
"paid_at": null
}List
curl "https://stage.tonder.io/api/v1/withdrawals/query/?page=1&page_size=50&start_date=2026-10-01&end_date=2026-10-07" \
-H "Authorization: Token YOUR_TEST_API_KEY"curl "https://app.tonder.io/api/v1/withdrawals/query/?page=1&page_size=50&start_date=2026-10-01&end_date=2026-10-07" \
-H "Authorization: Token YOUR_API_KEY"| Parameter | |
|---|---|
page, page_size | Pagination |
start_date, end_date | Range, YYYY-MM-DD |
The response is { "count", "results", "search_mode" }, where each item in results is the full
withdrawal record of the detail.
The id filter is not supported: it is ignored silently and the full list comes back. For one
specific withdrawal use the detail above.
An inverted range, with start_date after end_date, answers 500.
Available balance
curl "https://stage.tonder.io/api/v1/withdrawals/balance" \
-H "Authorization: Token YOUR_TEST_API_KEY"curl "https://app.tonder.io/api/v1/withdrawals/balance" \
-H "Authorization: Token YOUR_API_KEY"{
"available": "32894.55",
"current_balance": "32894.55"
}GET only. With POST it answers 405.
The shape of the response depends on your account's configuration. It may carry only
current_balance, or current_balance together with available. Read it defensively and don't
assume both fields exist.
Account activity
curl "https://stage.tonder.io/api/v1/withdrawals/account_activity/query/?page=1&page_size=50&tz=America/Mexico_City&start_date=2026-10-01&end_date=2026-10-02" \
-H "Authorization: Token YOUR_TEST_API_KEY"curl "https://app.tonder.io/api/v1/withdrawals/account_activity/query/?page=1&page_size=50&tz=America/Mexico_City&start_date=2026-10-01&end_date=2026-10-02" \
-H "Authorization: Token YOUR_API_KEY"Returns the movements of your account, withdrawals and deposits, as { "results", "next_token" }.
The range must span at least two days. With start_date equal to end_date it returns an
empty list even if that day has movements. To query a single day, set the next day as end_date.
Movements arrive by two routes: an immediate one, within seconds, and an hourly reconciliation that completes some fields. A movement that was just paid can take up to an hour to show complete.
Statuses
The full cycle, in the order it happens:
| Status | Type | Meaning |
|---|---|---|
PENDING | Initial | Created. Waiting for approval |
PROCESSING | Intermediate | Approved, in progress internally |
SENT_TO_PROVIDER | Intermediate | Sent. No confirmation yet |
PAID_FULL | Terminal | Settled |
FAILED | Terminal | Failed. The detail carries the reason |
REJECTED | Terminal | Rejected, by you or by validation |
CANCELED | Terminal | Cancelled |
REFUNDED | Terminal | It was paid, and the receiving bank returned the funds |
Two vocabularies
The creation response and the webhooks use readable names, while the query endpoints return the internal names. They are the same status:
| In creation and in the webhooks | In the queries |
|---|---|
Pending | PENDING |
Processing | PROCESSING / SENT_TO_PROVIDER |
Success | PAID_FULL |
Declined | REJECTED |
Cancelled | CANCELED |
Failed | FAILED |
Returned | REFUNDED |
Handle both. If your integration only recognises one of the two sets, it will ignore real events.
When reconciling
PROCESSING and SENT_TO_PROVIDER are intermediate. Settling a withdrawal when you see them
produces wrong results. Always wait for a terminal status.
Treat REFUNDED as final and credit the reversal. A returned withdrawal went through
PAID_FULL first, so a handler that only reacts to the payment will never see that the money came
back.
The five terminal statuses are PAID_FULL, FAILED, REJECTED, CANCELED and REFUNDED.
Reconcile against all five.
Webhooks
Two are emitted per withdrawal: one when it is created, with the pending status, and one when it reaches a terminal status. If the funds are returned after being paid, a third one arrives.
There are two payload formats, and which one you receive depends on how your account is configured. Confirm it with us before writing the handler. Both carry the same essential information: status, method, amount and your references.
Flat format
{
"id": "a1e2f918-1851-4988-b9de-36d57f41ed56",
"operation_type": "withdrawal",
"amount": "20",
"currency": "MXN",
"client_reference": "payout-001",
"status": "Pending",
"transfer_method_type": "SPEI",
"created": "2026-05-21T19:15:32.029134Z",
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"external_id": "payout-001"
},
"event_type": "withdrawal_Pending",
"action": "MODIFY"
}| Field | |
|---|---|
id | The transaction id. It repeats on every event of the same withdrawal: correlate with it |
operation_type | "withdrawal" |
status | The status reached |
transfer_method_type | SPEI or DEBIT_CARD |
event_type | withdrawal_ plus the status |
client_reference | Your reference, as you sent it |
metadata | What you sent, plus external_id |
action | "MODIFY" |
The payload can carry other fields that identify the institution that executed the transfer and its reference. Don't build logic on them: they depend on your account's configuration and can change.
Extended format
Nests everything under withdrawal and adds the status history, the fees and the transfer's
detail. The status travels in the internal names of the Statuses section.
{
"withdrawal": {
"id": "ad3069ed-598e-4a1b-82e0-2279d5af292d",
"business_id": "69",
"status": "PAID_FULL",
"business_id_status": "69#PAID_FULL",
"monetary_amount": { "amount": "20.00", "currency": "MXN" },
"transfer_method": "SPEI",
"tracking_key": "2da0310418da4be18419",
"provider_reference": "ref_2da0310418da4be1",
"account_data": {
"nombre_beneficiario": "Ana Maria Gonzalez",
"cuenta_beneficiario": "012969100000000110",
"institucion_contraparte": "40012",
"tipo_cuenta_beneficiario": "40",
"rfc_curp_beneficiario": "GOAN850315AB2",
"concepto_pago": "Pago de comisiones",
"email": "ana.gonzalez@email.com"
},
"status_changes": [
{ "from_status": "PENDING", "to_status": "PROCESSING",
"timestamp": "2026-09-15T19:33:20.112", "reason": null },
{ "from_status": "PROCESSING", "to_status": "PAID_FULL",
"timestamp": "2026-09-15T19:35:52.912", "reason": "succeeded" }
],
"metadata": { "order_reference": "ORD-001" },
"fee_model": "01",
"created_at": "2026-09-15T19:33:14.262113+00:00",
"modified_at": "2026-09-15T19:35:52.989243+00:00",
"paid_at": "2026-09-15T19:35:52.989260+00:00"
}
}It also carries a block with the raw notification from the institution that executed the transfer.
Inside that block the amount is in centavos, unlike monetary_amount, which is in pesos. It
can also carry a tracking key different from the top-level one: that second one is the real SPEI
key before Banxico, the one that traces the payment on its portal. The beneficiary's data arrives
masked.
The five events
withdrawal_Pending — the withdrawal was created and awaits approval.
{ "status": "Pending", "event_type": "withdrawal_Pending", "...": "..." }withdrawal_Success — settled. The money went out.
{ "status": "Success", "event_type": "withdrawal_Success", "...": "..." }withdrawal_Declined — rejected, usually because of the beneficiary's data.
{ "status": "Declined", "event_type": "withdrawal_Declined", "...": "..." }withdrawal_Failed — it failed when sent.
{ "status": "Failed", "event_type": "withdrawal_Failed", "...": "..." }withdrawal_Returned — the receiving bank returned the funds after a successful payment. It is
the third webhook of that withdrawal, and arrives minutes or hours after the Success.
{ "status": "Returned", "event_type": "withdrawal_Returned", "...": "..." }With Webhooks 2.0, a cancelled withdrawal also
sends withdrawal_Canceled — spelled with one L.
Recommendations
Be idempotent. The same event can arrive more than once. With Webhooks 2.0, deduplicate on the
webhook-id header; without it, use id together with event_type as the key.
metadata.external_id repeats on every event of the withdrawal, so it correlates but cannot tell
Pending from Success.
Don't assume order. The terminal event can arrive before the pending one.
Some fields identify who executed the transfer and with which reference. Don't build logic on
those values: they depend on your account's configuration and can change. To react, use status,
event_type and your own identifiers.
Answer 2xx fast and process afterwards. Reconcile anything your endpoint missed with the
withdrawal detail; how deliveries are retried and resent is in
Delivery and retries.
Institution codes
beneficiary.institution is the code of the beneficiary's bank in Banxico's catalogue, four or
five digits. The non-bank participants of the catalogue work too. The full list is in the
Banking reference.
The institution code and the CLABE must belong to the same bank. The system does not check that correspondence when the withdrawal is created, but a mismatch can cause a rejection when it is sent.
Test data
Valid only in Sandbox, and always with institution 40012: two CLABEs that pay, one that
fails, one whose funds come back minutes to an hour after paying — the way to exercise the third
webhook, withdrawal_Returned — and one debit card that pays. The accounts and how to force each
terminal state are in Test cases — Withdrawals.
If this data is rejected, write to us before going on: your test account may need a different configuration.
Payment receipt (CEP)
Settled withdrawals deliver a receipt in the url_cep field, which points to Banxico's portal
with the payment's data.
It takes between 4 and 30 minutes to appear after settlement. Reading it right after approving returns empty without that meaning it is missing. Don't conclude anything before half an hour.
A REFUNDED withdrawal keeps its receipt: the transfer happened and was returned afterwards.
Errors
| Response | What happened |
|---|---|
400 · Amount must be greater than 0 | Zero or negative amount |
400 · Transfer method must be SPEI or DEBIT_CARD | Unknown method |
400 · beneficiary.account: This field is required | The destination account is missing |
400 · Invalid action | The action sent does not exist |
401 · Invalid token | Wrong API key |
401 · User inactive or deleted | The user of that key is disabled |
404 · Withdrawal not found: 'Item' | You sent the transaction id where the withdrawal_id belongs |
200 · {"detail": "Withdrawal not found"} | The id in the detail URL does not exist (the detail endpoint answers 200, not 404) |
405 · Method not allowed | The balance endpoint only accepts GET |
409 · Invalid transition | The withdrawal is already in a terminal state |
Provider rejected: invalid_institution_id | The institution code does not correspond |
The generic HTTP codes are in Codes.
Before going to production
The amount is not validated against your balance at creation. A withdrawal above the available balance is accepted and stays pending. The validation happens at settlement, and that is where it fails.
The balance is not reserved while the withdrawal is pending. It is deducted when paid. If you accumulate many pending withdrawals, the available balance does not reflect that commitment.
The beneficiary's name has a practical limit of 40 characters. Longer names can be rejected when sent, not when created. Validate it on your side first. A frequent case is a surname the end user typed twice.
A pending withdrawal is dispatched with the configuration in force when it is approved, not the one it had when created.
The amount limit is set by the institution that executes the transfer, not by Tonder.
Questions
If something in this guide does not match what you observe, write to
support with the withdrawal_id, the transaction id and the
approximate time. With that we can trace the whole case.
API reference
You create a withdrawal with Process Transaction. The rest of the cycle:
Authorization
Authorization Your public API key, with the word Token and a space in front of it. Example: Token 6534bc0a7e1f4d2b9c8e3a5f7b1d2c4e6f8a9b0c — that whole value is the header. Sending the key alone returns 401 'Authentication credentials were not provided'. In this playground you may paste just the key; the prefix is added for you.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/withdrawals/perform_action/" \ -H "Content-Type: application/json" \ -d '{ "id": "ad3069ed-598e-4a1b-82e0-2279d5af292d", "action": "approve" }'{
"id": "ad3069ed-598e-4a1b-82e0-2279d5af292d",
"business_id": "69",
"status": "PROCESSING",
"amount": 20,
"currency": "MXN",
"transfer_method": "SPEI",
"status_changes": [
{
"from_status": "PENDING",
"to_status": "PROCESSING",
"timestamp": "2026-10-01T17:35:02.118402",
"reason": null
}
],
"created_at": "2026-10-01T17:33:41.364685Z",
"modified_at": "2026-10-01T17:35:02.201173Z",
"paid_at": null
}{
"error": "Invalid action"
}{
"detail": "Invalid token."
}{
"error": "Withdrawal not found: 'Item'"
}{
"detail": "Cannot modify withdrawal in terminal state"
}Authorization
Authorization Your public API key, with the word Token and a space in front of it. Example: Token 6534bc0a7e1f4d2b9c8e3a5f7b1d2c4e6f8a9b0c — that whole value is the header. Sending the key alone returns 401 'Authentication credentials were not provided'. In this playground you may paste just the key; the prefix is added for you.
In: header
Path Parameters
The withdrawal id: withdrawal_id from POST /process/.
Response Body
application/json
application/json
curl -X GET "https://example.com/withdrawals/7b57edd2-6594-4ef6-813b-df0d3d091a5c"{
"id": "7b57edd2-6594-4ef6-813b-df0d3d091a5c",
"business_id": "69",
"status": "FAILED",
"amount": 1.7,
"currency": "MXN",
"transfer_method": "SPEI",
"tracking_key": "a61c4f05b1de4645a1d2",
"provider_reference": null,
"account_data": {
"nombre_beneficiario": "Ana Maria Gonzalez",
"cuenta_beneficiario": "012969100000000013",
"institucion_contraparte": "40012",
"tipo_cuenta_beneficiario": 40,
"rfc_curp_beneficiario": "GOAN850315AB2",
"concepto_pago": "Pago de comisiones",
"email": "ana.gonzalez@email.com"
},
"metadata": {
"order_reference": "ORD-001"
},
"status_changes": [
{
"from_status": "PENDING",
"to_status": "PROCESSING",
"timestamp": "2026-10-13T18:40:23.497368",
"reason": null
},
{
"from_status": "PROCESSING",
"to_status": "FAILED",
"timestamp": "2026-10-13T18:40:23.497375",
"reason": "Provider rejected: invalid_institution_id"
}
],
"action": {
"action": "FAIL",
"reason": "Provider rejected: invalid_institution_id"
},
"url_cep": "",
"created_at": "2026-10-13T02:06:17.772297Z",
"modified_at": "2026-10-13T18:40:23.512117Z",
"paid_at": null
}{
"detail": "Invalid token."
}Authorization
Authorization Your public API key, with the word Token and a space in front of it. Example: Token 6534bc0a7e1f4d2b9c8e3a5f7b1d2c4e6f8a9b0c — that whole value is the header. Sending the key alone returns 401 'Authentication credentials were not provided'. In this playground you may paste just the key; the prefix is added for you.
In: header
Query Parameters
Page number, starting at 1.
Items per page.
First creation date to include, YYYY-MM-DD.
Last creation date to include, YYYY-MM-DD. Must not precede start_date.
Response Body
application/json
application/json
application/json
curl -X GET "https://example.com/withdrawals/query/"{
"count": 1,
"results": [
{
"id": "ea6ac67d-b577-481a-9b99-3c3c275ae527",
"business_id": "69",
"status": "PAID_FULL",
"amount": 20,
"currency": "MXN",
"transfer_method": "SPEI",
"tracking_key": "2da0310418da4be18419",
"account_data": {
"nombre_beneficiario": "Ana Maria Gonzalez",
"cuenta_beneficiario": "012969100000000110",
"institucion_contraparte": "40012",
"tipo_cuenta_beneficiario": 40,
"rfc_curp_beneficiario": "GOAN850315AB2",
"concepto_pago": "Pago de comisiones",
"email": "ana.gonzalez@email.com"
},
"metadata": {
"order_reference": "ORD-001"
},
"status_changes": [
{
"from_status": "PENDING",
"to_status": "PROCESSING",
"timestamp": "2026-10-01T17:35:02.118402",
"reason": null
},
{
"from_status": "PROCESSING",
"to_status": "PAID_FULL",
"timestamp": "2026-10-01T17:37:41.902188",
"reason": "succeeded"
}
],
"url_cep": "https://www.banxico.org.mx/cep/",
"created_at": "2026-10-01T17:33:41.364685Z",
"modified_at": "2026-10-01T17:37:41.998211Z",
"paid_at": "2026-10-01T17:37:41.998227Z"
}
],
"search_mode": "date_range"
}{
"detail": "Invalid token."
}{
"error": "Failed to fetch withdrawals"
}Authorization
Authorization Your public API key, with the word Token and a space in front of it. Example: Token 6534bc0a7e1f4d2b9c8e3a5f7b1d2c4e6f8a9b0c — that whole value is the header. Sending the key alone returns 401 'Authentication credentials were not provided'. In this playground you may paste just the key; the prefix is added for you.
In: header
Response Body
application/json
application/json
application/json
curl -X GET "https://example.com/withdrawals/balance"{
"available": "32894.55",
"current_balance": "32894.55"
}{
"detail": "Invalid token."
}{
"detail": "Method \"POST\" not allowed."
}Authorization
Authorization Your public API key, with the word Token and a space in front of it. Example: Token 6534bc0a7e1f4d2b9c8e3a5f7b1d2c4e6f8a9b0c — that whole value is the header. Sending the key alone returns 401 'Authentication credentials were not provided'. In this playground you may paste just the key; the prefix is added for you.
In: header
Query Parameters
Page number, starting at 1.
Items per page.
IANA time zone the dates are interpreted in.
First day to include, YYYY-MM-DD.
Last day to include, YYYY-MM-DD. Must be at least one day after start_date.
Response Body
application/json
application/json
curl -X GET "https://example.com/withdrawals/account_activity/query/"{
"results": [
{
"id": "9c1d7f32-5b0e-4a7d-9f2e-1c8a4b6d2e10",
"type": "withdrawal",
"amount": "20.00",
"business_id": "69",
"status": "succeeded",
"operation_date": "2026-10-01T17:37:41Z",
"created_at": "2026-10-01T17:37:48.668244Z",
"metadata": {
"tonder_withdrawal_id": "ea6ac67d-b577-481a-9b99-3c3c275ae527",
"beneficiary_account": "012969******0110",
"tracking_key": "2da0310418da4be18419",
"currency": "MXN"
},
"raw_json": {
"amount": 2000,
"currency": "MXN",
"status": "succeeded",
"tracking_key": "2da0310418da4be18419"
}
}
],
"next_token": null
}{
"detail": "Invalid token."
}Next steps
Test cases — Withdrawals
The sandbox accounts and how to force each terminal state.
Account Validation
Confirm who owns the CLABE — name and RFC from the bank — before you pay out.
Webhooks
Delivery, the payload and server-side confirmation.
API Direct overview
The unified /process/ endpoint and the status lookup.
