This guide explains how to send payouts, or withdrawals, to beneficiaries using the Tonder API. Withdrawals are processed through the same unified Process Transaction endpoint as payments, by setting the operation_type to "withdrawal".
Availability NoticeWithdrawals are currently only available for beneficiaries in Mexico. Support for additional countries will be added in future releases.
Available Transfer MethodsThere are two transfer methods available:
- SPEI: Bank transfers processed same business day during banking hours.
- Debit Card: Instant deposits available within 5-15 minutes, 24/7.
Critical: Geolocation Required for MexicoThe metadata.latitude and metadata.longitude fields are mandatory for processing withdrawals in Mexico. Omitting these fields or providing erroneous coordinates will result in a failed transaction. If the merchant cannot obtain the user’s device geolocation, the merchant’s own office location can be used instead, as long as it is within Mexican territory.
Step 1: Choose Your Transfer Method
Select the transfer method that fits your payout needs. Here you can find a quick comparison between the two methods:
| Aspect | SPEI | Debit Card |
|---|
| Processing Time | Same business day | 5-15 minutes |
| Availability | Banking hours only | 24/7 |
| Account Type | 18-digit CLABE | 16-digit card number |
| Best For | Regular payments, larger amounts | Urgent payments, instant cashouts |
SPEI Bank Transfer
SPEI (Sistema de Pagos Electrónicos Interbancarios) is Mexico’s standard electronic payment system for bank-to-bank transfers.
You should use SPEI when:
- Making regular business payments, such as commissions or vendor payments.
- Processing payroll and salary transfers.
- Sending larger amounts where cost-effectiveness is important.
- Same-day processing during business hours is acceptable.
Key requirementsPay attention to the following specifics for SPEI withdrawals:
- The
transfer_method value must be "SPEI".
- The
beneficiary.account must be a valid 18-digit CLABE (Clave Bancaria Estandarizada).
- Processing occurs during banking hours (typically 9 AM - 5 PM MXT, Monday-Friday).
Debit Card Instant Transfer
Debit card withdrawals provide near-instant fund delivery directly to the beneficiary’s debit card.
You should use Debit Card withdrawals when:
- Making urgent or emergency payments that can’t wait for business hours.
- Paying gig economy workers who need immediate access to funds.
- Providing instant cashouts or on-demand withdrawals.
- Processing customer refunds that require immediate availability.
Key requirementsPay attention to the following specifics for debit card withdrawals:
- The
transfer_method value must be "DEBIT_CARD".
- The
beneficiary.account must be a valid 16-digit debit card number.
- Processing occurs 24/7, including weekends and holidays.
- Funds are typically available within 5-15 minutes.
Step 2: Make the Withdrawal Request
Create and send your withdrawal request using the Process Transaction endpoint with operation_type set to "withdrawal".
The request must have the following fields:
| Field | Description |
|---|
operation_type | Always set to "withdrawal" for payout operations |
amount | The withdrawal amount in the specified currency |
currency | Currency code (currently only "MXN" is supported) |
client_reference | Your internal reference identifier for this withdrawal |
transfer_method | Either "SPEI" or "DEBIT_CARD" |
beneficiary | Complete beneficiary information object |
metadata | Additional required information (must include latitude and longitude) |
The beneficiary object contains the recipient’s information for the withdrawal. This object specifies who will receive the funds and how they should be delivered. You can see the properties of the beneficiary object in the table below:
| Field | Description |
|---|
account | The destination account number (18-digit CLABE for SPEI transfers, or 16-digit card number for debit card deposits) |
name | Full legal name of the beneficiary as it appears on their bank account or card |
institution | Bank institution code (you can find codes in our Mexican Banking Reference) |
email | Beneficiary’s email address for notifications and record-keeping |
rfc | Mexican tax ID (RFC), used for compliance and verification |
curp | Mexican CURP, 18-character alphanumeric personal identification number |
RFC and CURP requirementsEither rfc or curp must be provided for every withdrawal request. If neither is available, send "ND" (No Disponible) as the value for one of the two fields.
Here is a confirmed working example for a SPEI transfer:
{
"operation_type": "withdrawal",
"amount": 6.00,
"currency": "MXN",
"client_reference": "card-payout-002",
"transfer_method": "SPEI",
"description": "Instant payout",
"beneficiary": {
"account": "846180000400000001",
"name": "Ana María González",
"rfc": "GOAN850315AB2",
"institution": "40012",
"email": "ana.gonzalez@email.com"
},
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"orderId": "pruebaTonderPayout-00001"
}
}
The metadata object is required for all withdrawal requests and must include geolocation information:
| Field | Type | Required | Description |
|---|
latitude | string | Yes | Geographic latitude coordinate of the transaction origin |
longitude | string | Yes | Geographic longitude coordinate of the transaction origin |
You can also include optional fields in the metadata object for tracking purposes: operation_date, customer_email, business_user, customer_id, or order_id (sent as orderId in the confirmed example above).
Make a POST request to the Process Transaction endpoint with your withdrawal data:
curl -X POST https://stage.tonder.io/api/v1/process/ \
-H "Authorization: Token YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operation_type": "withdrawal",
"amount": 6.00,
"currency": "MXN",
"client_reference": "card-payout-002",
"transfer_method": "SPEI",
"description": "Instant payout",
"beneficiary": {
"account": "846180000400000001",
"name": "Ana María González",
"rfc": "GOAN850315AB2",
"institution": "40012",
"email": "ana.gonzalez@email.com"
},
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"orderId": "pruebaTonderPayout-00001"
}
}'
Step 3: Handle the Response
Upon successful request, the API will return an immediate acknowledgment. Here you find a description of the fields in the response:
| Field | Description |
|---|
id | Unique transaction identifier |
operation_type | Confirms this is a withdrawal operation |
status | Current transaction status (initially PENDING) |
amount | Withdrawal amount as submitted |
currency | Currency code |
merchant_reference | Your internal reference identifier |
created_at | ISO 8601 timestamp when the withdrawal was created |
status_code | HTTP status code (201 for successful creation) |
The response structure is as follows:
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"operation_type": "withdrawal",
"status": "PENDING",
"amount": 6.00,
"currency": "MXN",
"merchant_reference": "card-payout-002",
"created_at": "2024-07-26T10:30:00Z",
"status_code": 201
}
After receiving the response, save the transaction ID from the id field to monitor progress.
Step 4: Withdrawal Lifecycle and Statuses
Withdrawals are asynchronous operations. As your withdrawal progresses, it moves through the following statuses:
| Status | Type | Description |
|---|
PENDING | Initial | The withdrawal request has been received and is queued |
PROCESSING | Intermediate | The transfer is currently being approved and prepared |
SENT_TO_PROVIDER | Intermediate | The request has been sent to the bank or payment provider |
PAID_FULL | Success | The transfer was completed successfully |
REJECTED | Terminal | The transfer was declined (for example, invalid account) |
REFUNDED | Terminal | Funds from a previously paid withdrawal were reversed by SPEI |
PAID_FULL is not always final. A PAID_FULL (Success) withdrawal, and also a declined one, can still transition to REFUNDED if the SPEI system reverses the disbursement afterward. Do not treat PAID_FULL as the end of the lifecycle in your integration logic. See Refunds below.
Internal Status vs Webhook Status
Only two webhook notifications are sent per withdrawal: an initial Pending notification, and one terminal notification.
| Internal stage | Webhook status you receive |
|---|
PENDING | Pending |
PROCESSING, SENT_TO_PROVIDER | No separate webhook, internal only |
PAID_FULL | Success |
REJECTED (rejected before reaching the provider) | Declined |
| Failure after being sent to the provider | Failed |
REFUNDED (can follow either a Success or a Declined withdrawal) | See Refunds below |
Monitoring Options
You can monitor withdrawal status using one of these methods:
-
Webhooks (Recommended): configure webhooks to receive real-time status updates automatically.
-
Status Polling: check transaction status periodically using the Get Transaction Status endpoint.
Refunds
A withdrawal refund (REFUNDED) is distinct from a payment refund. It means the disbursed funds were returned to the merchant’s sub account, not to the end customer’s bank.
A refund occurs when the SPEI system reverses a previously completed or declined disbursement. Tonder detects this automatically and transitions the withdrawal to REFUNDED — no action is required from your integration to trigger it. This can happen after a PAID_FULL (Success) withdrawal, and also after a Declined one.
How It Works
- The withdrawal reaches
PAID_FULL status. A webhook notification is dispatched to your endpoint after a brief delay.
- If SPEI notifies Tonder that the disbursement has been reversed before that delay elapses, Tonder suppresses the pending
PAID_FULL webhook and moves the withdrawal directly to REFUNDED.
- If the reversal happens after the
PAID_FULL webhook has already been sent, you will receive a separate REFUNDED webhook afterward.
REFUNDED is a terminal state — no further transitions occur.
- The refunded amount is credited back to the merchant’s sub account. Notify the withdrawal recipient and initiate a new withdrawal request if appropriate.
Design your integration to handle a REFUNDED notification arriving without a prior PAID_FULL notification. Do not assume PAID_FULL will always be received first.
Notification Ordering
| Scenario | Notifications you receive |
|---|
| Successful disbursement, no reversal | PENDING → PAID_FULL |
Fast reversal, before the PAID_FULL webhook is dispatched | PENDING → REFUNDED |
Reversal after the PAID_FULL webhook was already sent | PENDING → PAID_FULL → REFUNDED |
Common Refund Reasons
| Reason | Description |
|---|
| Cuenta inexistente | The destination account does not exist |
| CLABE incorrecta | The CLABE number is invalid or formatted incorrectly |
| Cuenta cancelada | The destination account has been closed |
| Fondos insuficientes | Insufficient funds at the receiving institution |
| Operación no permitida | The transfer type is not permitted for this account |
This list is not exhaustive. Always surface the reason to your operations team for investigation.
Handling a Refund Notification
- Acknowledge the notification immediately (HTTP 200) — do not delay the response while processing business logic.
- Use the withdrawal
id to check for idempotency. If you already processed a REFUNDED event for this withdrawal, skip further processing.
- Credit the refunded amount to the withdrawal recipient in your own system.
- Notify the recipient that the transfer was reversed, including the reason if available.
- If the reversal was due to a correctable error (for example, an incorrect CLABE), allow the recipient to submit a new withdrawal request after verifying their account details.
Webhooks
Withdrawal webhooks for API Direct follow the same format as API Direct payment webhooks, with a few differences:
operation_type is "withdrawal"
transfer_method_type is used instead of payment_method_type
event_type uses the withdrawal_ prefix (for example, withdrawal_Pending, withdrawal_Success)
provider is "STP"
metadata contains the withdrawal fields defined by the merchant (latitude, longitude, beneficiary_rfc, and so on)
Two webhooks are always generated per withdrawal: first a Pending notification, then a terminal notification (Success, Declined, or Failed).
Successful SPEI Withdrawal
{
"id": "f50e4a1f-7145-4737-836f-32250eee38c2",
"operation_type": "withdrawal",
"amount": "55",
"currency": "MXN",
"client_reference": "card-payout-002",
"status": "Pending",
"provider": "STP",
"transfer_method_type": "SPEI",
"created": "2026-02-27T06:41:50.338733Z",
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"beneficiary_rfc": "QUIG850315H42"
},
"event_type": "withdrawal_Pending",
"action": "MODIFY"
}
{
"id": "f50e4a1f-7145-4737-836f-32250eee38c2",
"operation_type": "withdrawal",
"amount": "55",
"currency": "MXN",
"client_reference": "card-payout-002",
"status": "Success",
"provider": "STP",
"transfer_method_type": "SPEI",
"created": "2026-02-27T06:41:50.338733Z",
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"beneficiary_rfc": "QUIG850315H42"
},
"event_type": "withdrawal_Success",
"action": "MODIFY"
}
Declined SPEI Withdrawal
A Declined withdrawal means the transfer was rejected by Tonder before being handed off to the provider.
{
"id": "c7b14546-84bd-4a81-89fb-3dc660f47011",
"operation_type": "withdrawal",
"amount": "60",
"currency": "MXN",
"client_reference": "card-payout-002",
"status": "Pending",
"provider": "STP",
"transfer_method_type": "SPEI",
"created": "2026-02-27T06:47:09.205388Z",
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"beneficiary_rfc": "QUIG850315H42"
},
"event_type": "withdrawal_Pending",
"action": "MODIFY"
}
{
"id": "c7b14546-84bd-4a81-89fb-3dc660f47011",
"operation_type": "withdrawal",
"amount": "60",
"currency": "MXN",
"client_reference": "card-payout-002",
"status": "Declined",
"provider": "STP",
"transfer_method_type": "SPEI",
"created": "2026-02-27T06:47:09.205388Z",
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"beneficiary_rfc": "QUIG850315H42"
},
"event_type": "withdrawal_Declined",
"action": "MODIFY"
}
Failed SPEI Withdrawal
A Failed withdrawal is sent to the provider but STP is unable to process the transfer, which is different from Declined.
The examples below are illustrative and follow the standard API Direct webhook pattern.
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"operation_type": "withdrawal",
"amount": "75",
"currency": "MXN",
"client_reference": "card-payout-003",
"status": "Pending",
"provider": "STP",
"transfer_method_type": "SPEI",
"created": "2026-02-27T07:00:00.000000Z",
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"beneficiary_rfc": "QUIG850315H42"
},
"event_type": "withdrawal_Pending",
"action": "MODIFY"
}
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"operation_type": "withdrawal",
"amount": "75",
"currency": "MXN",
"client_reference": "card-payout-003",
"status": "Failed",
"provider": "STP",
"transfer_method_type": "SPEI",
"created": "2026-02-27T07:00:00.000000Z",
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"beneficiary_rfc": "QUIG850315H42"
},
"event_type": "withdrawal_Failed",
"action": "MODIFY"
}
Best Practices
- Idempotency: handle duplicate notifications gracefully using the withdrawal
id.
- Acknowledgment: return HTTP 200 quickly, then process asynchronously.
- Two notifications per withdrawal: always expect a Pending notification followed by exactly one terminal notification (Success, Declined, or Failed).
- Logging: log all notification receipts for debugging and audit purposes.
Testing
Use the following institution codes in the Stage environment:
| Method | Institution code |
|---|
| SPEI | 97846 |
| DEBIT_CARD | 97846 |
For production, use real institution codes from the Mexican Banking Reference.
Next Steps
- Set up webhooks to receive real-time notifications when withdrawal statuses change.
- Check out HTTP response codes to understand how to handle different API responses and error scenarios.
- Explore rate limits to ensure your withdrawal processing doesn’t exceed API usage limits.