Withdrawals
Send money to a beneficiary via SPEI or debit card, and track the status through to settlement.
Withdrawals (payouts) to beneficiaries are processed through the same unified /process/ endpoint
as payments, using operation_type: "withdrawal".
Availability. Withdrawals via API Direct are currently only available for beneficiaries in Mexico.
Available transfer methods
| Aspect | SPEI | Debit Card |
|---|---|---|
| Processing time | Instant (seconds) | Instant (seconds) |
| Availability | 24/7 | 24/7 |
| Account type | 18-digit CLABE | 16-digit card number |
| Best for | Beneficiaries with a CLABE, larger amounts | Beneficiaries who only have a card |
The payout rail is set on your account. Tonder provisions your account on a payout rail — STP
or Fintoc — for each transfer method. Both rails support SPEI and debit card. The request body is
identical on both: there is no provider or rail field to send. The rail only shows up in the
provider value of the webhooks.
Geolocation required for Mexico. The metadata.latitude and metadata.longitude fields are
mandatory for processing withdrawals in Mexico. Omitting them or sending erroneous coordinates
results in a failed transaction.
Step 1: make the withdrawal request
Send your request to the /process/ endpoint with operation_type set to "withdrawal":
| Field | Description |
|---|---|
operation_type | Always "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 specifies who receives the funds and how:
| Field | Description |
|---|---|
account | Destination account (18-digit CLABE for SPEI, or 16-digit card number for debit card). |
name | Beneficiary's full legal name, as it appears on their account or card. |
rfc | Beneficiary's RFC. |
curp | Beneficiary's CURP (18-character alphanumeric code). |
institution | Bank institution code. See Banking reference. |
email | Beneficiary's email for notifications and record-keeping. |
RFC/CURP requirement. You must provide either rfc or curp on every withdrawal request. If
you don't have one of the two available, send "ND" (Not Available) as the value for that field.
Institution codes for Stage testing depend on your payout rail. On the STP rail, use 97846
as the institution to simulate both SPEI and debit card withdrawals. On the Fintoc rail, 97846
is rejected with invalid_institution_id: use a real 5-digit Banxico code instead, such as
40012. The test accounts for each rail are in
Test cases — Withdrawals. In production, use the real codes
from the Banking reference.
Confirmed, working example — SPEI transfer:
{
"operation_type": "withdrawal",
"amount": 20.00,
"currency": "MXN",
"client_reference": "payout-001",
"transfer_method": "SPEI",
"description": "Commission payment",
"beneficiary": {
"account": "846180000400000001",
"name": "Ana María González",
"rfc": "GOAN850315AB2",
"institution": "97846",
"email": "ana.gonzalez@email.com"
},
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775"
}
}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": 20.00,
"currency": "MXN",
"client_reference": "payout-001",
"transfer_method": "SPEI",
"description": "Commission payment",
"beneficiary": {
"account": "846180000400000001",
"name": "Ana María González",
"rfc": "GOAN850315AB2",
"institution": "97846",
"email": "ana.gonzalez@email.com"
},
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775"
}
}'Step 2: handle the response
On a successful request, the API returns an immediate acknowledgment:
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"withdrawal_id": "dc5f481c-6219-438b-9612-7ca1bba6864e",
"operation_type": "withdrawal",
"status": "Pending",
"amount": 20.00,
"currency": "MXN",
"client_reference": "payout-001",
"created_at": "2026-10-01T17:33:41.364685Z",
"status_code": 201
}| Field | Description |
|---|---|
id | Unique transaction identifier — store it to monitor progress. |
withdrawal_id | Identifier used to approve, reject and refund the withdrawal. Different from id. |
operation_type | Confirms this is a withdrawal operation. |
status | Current transaction status (initially Pending). |
amount | Withdrawal amount as submitted. |
currency | Currency code. |
client_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 carries two different identifiers. id is the transaction: use it to check the
status. withdrawal_id is the withdrawal itself: use it to approve, reject or refund it. Store
both.
Step 3: approve the withdrawal
Unless automatic approval is enabled for your account, a withdrawal does not advance on its own:
it stays in Pending until it is approved. If your withdrawals stay in Pending, this is the
step that is missing.
From the Dashboard. Open the withdrawal detail 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": "dc5f481c-6219-438b-9612-7ca1bba6864e", "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": "dc5f481c-6219-438b-9612-7ca1bba6864e", "action": "approve" }'Available actions: approve, reject, cancel, fail, refund.
Once approved, the withdrawal advances on its own: Pending → Processing → Success.
The id in this body is the withdrawal_id, not the transaction id, even though the field
is named id. Sending the wrong one returns 404 Withdrawal not found.
Step 4: check the transaction status
Withdrawals are asynchronous operations. Use the id from the response to check the status at the
GET /api/v1/transactions/{id}/ endpoint, or monitor via webhooks.
As your withdrawal progresses, it moves through different statuses:
| Status | Type | Description | Previously |
|---|---|---|---|
Pending | Initial | The request was received and is queued or on hold. | PENDING, ON_HOLD |
Processing | Intermediate | The request was sent to the bank or payment provider. | SENT_TO_PROVIDER |
Success | Success | The transfer completed successfully. | PAID_FULL |
Declined | Terminal | The withdrawal was rejected (e.g., invalid account). | REJECTED |
Cancelled | Terminal | The withdrawal was cancelled. | CANCELED |
Failed | Terminal | The withdrawal failed. | FAILED |
Returned | Terminal | The transfer was sent and processed, but the funds were subsequently returned by the receiving bank. | REFUNDED |
Renamed statuses. Withdrawals now use the same status vocabulary as payments. If your
integration expected the previous names (PENDING, SENT_TO_PROVIDER, PAID_FULL, REJECTED,
CANCELED, FAILED, REFUNDED), update it using the "Previously" column in the table.
Treat Returned as final. A Returned withdrawal already reached Success before the funds came
back, so a handler that only credits on Success will never see the reversal. Reconcile on
Success, Failed, Declined, Cancelled and Returned — all five are terminal.
Webhooks
API Direct withdrawal webhooks follow the same flat payload as payment webhooks, with these differences:
| Field | Difference |
|---|---|
operation_type | "withdrawal". |
transfer_method_type | Used instead of payment_method_type (SPEI or DEBIT_CARD). |
event_type | Uses the withdrawal_ prefix (withdrawal_Pending, withdrawal_Success, withdrawal_Declined, withdrawal_Failed, withdrawal_Returned). |
provider | The payout provider assigned to your account — currently STP or FINTOC. It depends on your account's rail; do not hardcode it and treat the list as open-ended. |
Two webhooks are generated per withdrawal: first a Pending notification, then one terminal
notification (Success, Declined, or Failed). If the receiving bank returns the funds after
Success, a third one follows: withdrawal_Returned.
The provider value is STP or FINTOC depending on your account's rail. Both carry identical
event semantics — match on event_type and status, not on provider.
Pending
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"operation_type": "withdrawal",
"amount": "20",
"currency": "MXN",
"client_reference": "payout-001",
"status": "Pending",
"provider": "STP",
"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"
}Success
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"operation_type": "withdrawal",
"amount": "20",
"currency": "MXN",
"client_reference": "payout-001",
"status": "Success",
"provider": "STP",
"transfer_method_type": "SPEI",
"created": "2026-05-21T19:20:47.029134Z",
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"external_id": "payout-001"
},
"event_type": "withdrawal_Success",
"action": "MODIFY"
}Declined
{
"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": "DEBIT_CARD",
"created": "2026-05-21T19:20:47.029134Z",
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"external_id": "card-payout-002"
},
"event_type": "withdrawal_Declined",
"action": "MODIFY"
}Failed
{
"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": "DEBIT_CARD",
"created": "2026-05-21T19:20:47.029134Z",
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"external_id": "card-payout-003"
},
"event_type": "withdrawal_Failed",
"action": "MODIFY"
}Returned
Sent only when the funds come back after Success — the third webhook on that withdrawal.
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"operation_type": "withdrawal",
"amount": "20",
"currency": "MXN",
"client_reference": "payout-001",
"status": "Returned",
"provider": "STP",
"transfer_method_type": "SPEI",
"created": "2026-05-22T04:26:32.523705Z",
"metadata": {
"latitude": "22.8870221",
"longitude": "-109.911775",
"external_id": "payout-001"
},
"event_type": "withdrawal_Returned",
"action": "MODIFY"
}Fintoc-rail accounts receive the same payloads with "provider": "FINTOC".
Recommended monitoring: use webhooks to receive real-time status updates. Alternatively, you can poll the transaction status endpoint.
Check your balance
Before dispersing, check the available balance:
curl https://stage.tonder.io/api/v1/withdrawals/balance \
-H "Authorization: Token YOUR_API_KEY"curl https://app.tonder.io/api/v1/withdrawals/balance \
-H "Authorization: Token YOUR_API_KEY"{
"message": "Data retrieved successfully",
"current_balance": "32894.55"
}A withdrawal for more than the available balance is rejected. The endpoint's schema is in the API Reference.
