Skip to main content
This guide explains the structure of webhook notifications sent by the Tonder Withdrawals API when withdrawal statuses change.

Webhook Overview

Webhooks are HTTP POST requests sent to your configured endpoint whenever a withdrawal status changes. They provide real-time notifications so you don’t need to poll the API for status updates.

Notification Structure

The webhook notification is an object containing the full withdrawal details, with an audit trail in the status_changes array.

Payload Structure

The webhook payload contains the complete withdrawal object with all details:

Withdrawal Object

FieldTypeDescription
idstringUnique withdrawal identifier
user_idstringUser ID in the Tonder system
business_idstringBusiness/merchant identifier
currencystringCurrency code (e.g., “MXN”)
amountdecimalWithdrawal amount
statusstringCurrent withdrawal status
transfer_methodstringTransfer method (“SPEI” or “DEBIT_CARD”)
created_atstringISO 8601 timestamp of creation
modified_atstringISO 8601 timestamp of last modification
provider_referencestringReference from the payment provider
fixed_feestringFixed fee amount
taxstringTax percentage
fee_amountstring/nullCalculated fee amount

Account Data Object

The account_data object includes complete bank and account details:
FieldTypeDescription
cuenta_ordenantestringOriginator account number (CLABE)
nombre_ordenantestringOriginator name
rfc_curp_ordenantestringOriginator RFC or CURP
cuenta_beneficiariostringBeneficiary account number
nombre_beneficiariostringBeneficiary name
rfc_curp_beneficiariostringBeneficiary RFC or CURP
institucion_contrapartestringCounterparty institution code
tipo_cuenta_beneficiariointegerBeneficiary account type (40 for SPEI, 3 for DEBIT_CARD)
emailstringContact email
concepto_pagostringPayment concept/description

Status Changes Array

The status_changes array provides a complete audit trail of status transitions:
FieldTypeDescription
from_statusstringPrevious status
to_statusstringNew status
timestampstringISO 8601 timestamp of the change
reasonstring (optional)Reason for the status change

Metadata Object

The metadata object contains additional information:
FieldTypeDescription
latitudestringGeographic latitude
longitudestringGeographic longitude
operation_datestringOperation date (optional)
customer_emailstringCustomer email (optional)
business_userstringBusiness user identifier (optional)
customer_idstringCustomer ID (optional)
order_idstringOrder ID (optional)

Example Webhook Payload

The webhook payload is the complete withdrawal object. Here’s an example:
{
  "id": "40f19a6b-4ce4-424e-92fe-1b564c07dbd7",
  "user_id": "1",
  "business_id": "21",
  "currency": "MXN",
  "amount": 100.0,
  "status": "PROCESSING",
  "account_data": {
    "cuenta_ordenante": "646180567300000006",
    "nombre_ordenante": "TRES_COMAS",
    "rfc_curp_ordenante": "ND",
    "cuenta_beneficiario": "846180000400000001",
    "nombre_beneficiario": "Fabio",
    "rfc_curp_beneficiario": "ND",
    "institucion_contraparte": "97846",
    "tipo_cuenta_beneficiario": 40,
    "email": "fabio@tonder.io",
    "concepto_pago": "test"
  },
  "status_changes": [
    {
      "from_status": null,
      "to_status": "PENDING",
      "timestamp": "2025-10-02T01:09:37.371365Z",
      "reason": "Withdrawal request created"
    },
    {
      "from_status": "PENDING",
      "to_status": "PROCESSING",
      "timestamp": "2025-10-02T01:10:00.000000Z",
      "reason": "Withdrawal approved and processing started"
    }
  ],
  "action": null,
  "metadata": {
    "latitude": "22.8870221",
    "longitude": "-109.911775",
    "operation_date": "2024-01-15",
    "customer_email": "fabio@tonder.io",
    "business_user": "admin_user_001",
    "customer_id": "CUST_12345",
    "order_id": "ORDER_ABC_789"
  },
  "created_at": "2025-10-02T01:09:37.371365Z",
  "modified_at": "2025-10-02T01:10:00.000000Z",
  "provider_reference": "PENDING",
  "transfer_method": "SPEI",
  "fixed_fee": "3.5",
  "tax": "16.0",
  "fee_amount": null
}

Status Change Examples

The webhook payload includes the complete withdrawal object with updated status. The status_changes array shows the transition history.

PENDING → PROCESSING

{
  "id": "40f19a6b-4ce4-424e-92fe-1b564c07dbd7",
  "status": "PROCESSING",
  "status_changes": [
    {
      "from_status": "PENDING",
      "to_status": "PROCESSING",
      "timestamp": "2025-10-02T01:10:00.000000Z",
      "reason": "Withdrawal approved and processing started"
    }
  ],
  "modified_at": "2025-10-02T01:10:00.000000Z",
  ...
}

PROCESSING → SENT_TO_PROVIDER

{
  "id": "40f19a6b-4ce4-424e-92fe-1b564c07dbd7",
  "status": "SENT_TO_PROVIDER",
  "status_changes": [
    {
      "from_status": "PROCESSING",
      "to_status": "SENT_TO_PROVIDER",
      "timestamp": "2025-10-02T01:15:00.000000Z"
    }
  ],
  "modified_at": "2025-10-02T01:15:00.000000Z",
  ...
}

SENT_TO_PROVIDER → PAID_FULL

{
  "id": "40f19a6b-4ce4-424e-92fe-1b564c07dbd7",
  "status": "PAID_FULL",
  "status_changes": [
    {
      "from_status": "SENT_TO_PROVIDER",
      "to_status": "PAID_FULL",
      "timestamp": "2025-10-02T02:00:00.000000Z",
      "reason": "Transfer completed successfully"
    }
  ],
  "modified_at": "2025-10-02T02:00:00.000000Z",
  "provider_reference": "SPEI-20251002-001234",
  ...
}

PENDING → REJECTED

{
  "id": "40f19a6b-4ce4-424e-92fe-1b564c07dbd7",
  "status": "REJECTED",
  "status_changes": [
    {
      "from_status": "PENDING",
      "to_status": "REJECTED",
      "timestamp": "2025-10-02T01:09:45.000000Z",
      "reason": "Invalid beneficiary account"
    }
  ],
  "modified_at": "2025-10-02T01:09:45.000000Z",
  ...
}

Webhook Headers

Webhook requests include standard HTTP headers. You may also receive custom headers for verification:
POST /your-webhook-endpoint HTTP/1.1
Host: your-server.com
Content-Type: application/json
X-Tonder-Event: withdrawal.status_changed
X-Tonder-Signature: <signature-for-verification>

Webhook Security

Verify Webhook AuthenticityAlways verify that webhooks are coming from Tonder. Check the signature header if provided, and validate the request source IP addresses.

Handling Webhooks

Best Practices

  1. Idempotency: Handle duplicate webhooks gracefully using the withdrawal ID
  2. Acknowledgment: Return HTTP 200 status quickly, then process asynchronously
  3. Retry Logic: Implement retry logic for failed webhook processing
  4. Logging: Log all webhook receipts for debugging and audit purposes
  5. Status Updates: Update your local records based on webhook status changes

Error Handling

If your webhook endpoint returns an error (4xx or 5xx), Tonder will retry the webhook delivery according to the retry policy. Ensure your endpoint can handle:
  • Network timeouts
  • Temporary server errors
  • Duplicate deliveries
  • Out-of-order deliveries

Testing Webhooks

When testing in the Stage environment:
  • Use the test institution code (97846)
  • Verify webhook payload structure
  • Test all status transitions
  • Validate error scenarios
See Testing Requirements for more details.

Next Steps