A withdrawal refund (
refunded) is distinct from a payment refund. It means the disbursed funds were returned to your merchant sub-account — not to the end customer’s bank.Prerequisites
Before reading this guide, ensure you are familiar with:- Withdrawal API overview — how to create and manage withdrawals
- Withdrawal webhooks — how status change notifications are delivered
- Withdrawal status reference — the full lifecycle
How Withdrawal Refunds Work
A refund occurs when the SPEI system reverses a previously completed disbursement. Tonder detects this automatically and transitions the withdrawal torefunded — no action is required from your integration to trigger it.
Withdrawal completes
The withdrawal reaches
paid_full status, and a webhook notification is dispatched to your endpoint (after a brief delay — see PAID_FULL notification delay).Provider signals reversal
The SPEI system notifies Tonder that the disbursement has been reversed. Tonder processes this automatically and transitions the withdrawal to
refunded.Status transitions to refunded
The withdrawal moves from
paid_full → refunded. This is a terminal state — no further transitions are possible.Webhook notification delivered
A webhook is sent to your configured endpoint with
status: "refunded". The refunded terminal notification also suppresses any pending paid_full webhook that has not yet been dispatched.Withdrawal Status Reference
Understanding the full status lifecycle helps you handle refunds correctly. The statuses relevant to the refund flow are:| Status | Type | Description |
|---|---|---|
pending | Initial | Withdrawal request created |
processing | Intermediate | Approved and being sent to provider |
sent_to_provider | Intermediate | Submitted to SPEI network |
in_transit | Intermediate | Funds actively transferring |
on_hold | Intermediate | Paused for manual review |
paid_full | Intermediate | Disbursement confirmed by provider |
refunded | Terminal | Funds reversed and returned to sub-account |
failed | Terminal | Withdrawal failed |
cancelled | Terminal | Withdrawal cancelled |
expired | Terminal | Withdrawal timed out |
Terminal statuses (
refunded, failed, cancelled, expired) are final. Once a withdrawal reaches a terminal state, no further transitions can occur — including via the API.Webhook Notifications for Refunds
Refund webhook payload
When a withdrawal is refunded, your webhook endpoint receives a notification withstatus: "refunded":
Unique identifier for the withdrawal.
Will be
refunded for a reversed disbursement.The reversal reason provided by the SPEI system (e.g.,
"Cuenta inexistente", "CLABE incorrecta"). May be "Unknown error" if no reason was provided.The SPEI system transaction reference used to identify the inbound notification.
PAID_FULL notification delay
Tonder applies a short delay (default: 60 seconds) before sending thepaid_full webhook. This is intentional: it allows time for the SPEI system to signal a reversal and suppress the paid_full notification if the disbursement is immediately reversed.
This means your integration may observe only a refunded webhook — with no preceding paid_full webhook — for very fast reversals.
Notification ordering guarantees
| Scenario | Webhooks you receive |
|---|---|
| Successful disbursement, no reversal | pending → … → paid_full |
Fast reversal (before paid_full webhook dispatches) | pending → … → refunded |
Reversal after paid_full webhook was already sent | pending → … → paid_full → refunded |
refunded, failed, cancelled) has been sent, no further webhooks are dispatched for that withdrawal — including any delayed paid_full that may still be queued.
Handling Refunds in Your Integration
Recommended response to a refunded webhook
Acknowledge the webhook
Respond with HTTP
200 immediately upon receipt. Do not delay the response while processing business logic.Check idempotency
Use the
withdrawal_id to look up the withdrawal in your system. If you have already processed a refunded event for this withdrawal, skip further processing.Credit funds to your customer
The refunded amount has been returned to your merchant sub-account. Credit the corresponding amount to the withdrawal recipient in your system.
Notify the recipient
Inform the withdrawal recipient that the transfer was reversed and provide the reason if available (from the
reason field).Querying withdrawal status via API
You can confirm a withdrawal’s current status at any time using the Withdrawals API:- cURL
- JavaScript
- Python
Error Handling
409 Conflict — attempt to transition a terminal withdrawal
409 Conflict — attempt to transition a terminal withdrawal
Cause: Your integration attempted to perform an action (e.g.,
CANCEL) on a withdrawal that is already in a terminal state (refunded, failed, cancelled).Solution: Check the current withdrawal status before attempting any action. Once a withdrawal is refunded, no further actions are valid. Poll the status endpoint or rely on webhooks to stay up to date.Refunded webhook received without prior paid_full webhook
Refunded webhook received without prior paid_full webhook
Cause: The SPEI provider reversed the disbursement before Tonder’s delayed
paid_full notification was dispatched. This is expected behavior, not an error.Solution: Ensure your webhook handler can process refunded independently of paid_full. Do not rely on paid_full being received first. Use the withdrawal_id to correlate events regardless of order.Duplicate refunded webhook received
Duplicate refunded webhook received
Cause: Webhook delivery uses at-least-once semantics. In rare cases, the same event may be delivered more than once.Solution: Implement idempotent webhook handling. Store processed
withdrawal_id + status combinations and skip re-processing if the pair has already been handled.reason field is 'Unknown error'
reason field is 'Unknown error'
Cause: The SPEI system returned a reversal notification without a reason value.Solution: Treat this as an unspecified reversal. Present a generic message to the recipient (e.g., “The transfer was returned by the recipient’s bank. Please verify the account details and try again.”). Contact soporte@tonder.io if you need additional details for investigation.
Common Refund Reasons
Thereason field in the webhook payload reflects the reversal reason returned by the SPEI system. Common values include:
| 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. The SPEI system may return additional reason values. Always surface the
reason field to your operations team for investigation.Next Steps
Withdrawal Webhooks
Configure and verify webhook delivery for all withdrawal status changes
Create a Withdrawal
Learn how to initiate a new withdrawal after a refund
Withdrawal Status Reference
Full status definitions, transitions, and terminal state behavior
Dashboard — Transactions
Monitor and investigate refunded withdrawals in the Tonder Dashboard

