Direct Debit · Guides
Create charges
Create a charge with inline data or with stored customer and instrument objects, retry safely, and handle the result.
There are two ways to create a charge through the API. Both use the same endpoint, POST /charges:
- Inline charge: send the customer and instrument details in the charge request.
- Charge with stored objects: reference a stored customer and instrument by ID.
All requests need your API key in the x-api-key header. See Quickstart.
Retry safely with an idempotency key
Send an Idempotency-Key header with a UUID on POST /charges so you can retry, for example after a network timeout, without creating a duplicate charge.
Idempotency-Key: 11111111-1111-4111-8111-111111111111| Situation | Result |
|---|---|
| Retry with the same key and an identical body | The original charge is returned. No new charge is created. |
| Same key, different body | 422 Unprocessable Entity |
| The original request is still being processed | 409 Conflict. Wait a moment and retry. |
| The key is not a valid UUID | 400 Bad Request |
Keys are scoped to your organization and expire 24 hours after the request. After that, the same key creates a new charge. The header is optional: requests without it work as usual.
Option 1: Inline charge
Send the customer and instrument details directly in the charge request. You don’t need to create any objects first. Use this when you don’t need to store or reuse the data, or when you are migrating from a system that already holds it.
curl -X POST https://directdebit.monato.com/charges \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Idempotency-Key: 11111111-1111-4111-8111-111111111111' \
--data '{
"currency": "mxn",
"amount": 100,
"reference": "subscription-0001",
"inline_instrument": {
"type": "mx_clabe",
"identifier": "000000000000000001"
},
"inline_customer": {
"name": "Jane Doe",
"document_type": "mx_rfc",
"document_number": "XXXX000000XXX"
}
}'{
"id": "22222222-2222-4222-8222-222222222222",
"org_id": "00000000-0000-4000-8000-000000000000",
"status": "pending",
"amount": 100.0,
"currency": "mxn",
"reference": "subscription-0001",
"instrument_id": null,
"customer_id": null,
"inline_instrument": {
"type": "mx_clabe",
"identifier": "000000000000000001",
"bank": null
},
"inline_customer": {
"name": "Jane Doe",
"document_type": "mx_rfc",
"document_number": "XXXX000000XXX",
"email": null,
"phone_number": null
},
"declined_reason": null,
"declined_reason_rail": null,
"risk_status": null,
"risk_evaluated_at": null,
"risk_reasons": null,
"created_at": "2026-01-15T10:00:00.000000Z",
"updated_at": null,
"result_at": null,
"chargeback_at": null
}The charge is created as pending. Save the id to match it with the result webhook.
If penny validation is enabled for your organization, a charge with inline_instrument and inline_customer always runs penny validation. It starts as verification_in_progress and moves to pending or canceled when the check completes. See Penny validation.
Option 2: Charge with stored objects
Reference a stored customer and instrument. This lets you filter charges by customer or instrument, and Monato tokenizes the payment data for you.
Step 1: Create the customer
curl -X POST https://directdebit.monato.com/customers \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data '{
"name": "Jane Doe",
"document_type": "mx_rfc",
"document_number": "XXXX000000XXX",
"email": "jane.doe@example.com"
}'{
"id": "33333333-3333-4333-8333-333333333333",
"org_id": "00000000-0000-4000-8000-000000000000",
"name": "Jane Doe",
"document_type": "mx_rfc",
"document_number": "XXXX000000XXX",
"email": "jane.doe@example.com",
"phone_number": null,
"customer_metadata": null,
"created_at": "2026-01-15T10:01:00.000000Z",
"updated_at": null
}Save the id.
Step 2: Create the instrument
curl -X POST https://directdebit.monato.com/instruments \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data '{
"type": "mx_direct_debit_card",
"customer_id": "33333333-3333-4333-8333-333333333333",
"mx_direct_debit_card": {
"card_number": "0000000000000001",
"bank": "mx_santander"
}
}'{
"id": "44444444-4444-4444-8444-444444444444",
"org_id": "00000000-0000-4000-8000-000000000000",
"customer_id": "33333333-3333-4333-8333-333333333333",
"type": "mx_direct_debit_card",
"status": "active",
"mx_direct_debit_card": {
"card_number": "0000000000000001",
"bank": "mx_santander"
},
"mx_clabe": null,
"currency": "mxn",
"description": null,
"ownership_verification_result": null,
"ownership_verification_result_at": null,
"created_at": "2026-01-15T10:02:00.000000Z",
"updated_at": null
}Save the id. Valid bank values are listed in Banks and institutions.
For new instruments, Monato runs penny validation automatically to confirm account ownership, and sends an instrument_ownership_verification_result webhook when it completes. See Penny validation.
Step 3: Create the charge
curl -X POST https://directdebit.monato.com/charges \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data '{
"currency": "mxn",
"amount": 100,
"customer_id": "33333333-3333-4333-8333-333333333333",
"instrument_id": "44444444-4444-4444-8444-444444444444"
}'{
"id": "55555555-5555-4555-8555-555555555555",
"org_id": "00000000-0000-4000-8000-000000000000",
"status": "pending",
"amount": 100.0,
"currency": "mxn",
"reference": null,
"instrument_id": "44444444-4444-4444-8444-444444444444",
"customer_id": "33333333-3333-4333-8333-333333333333",
"inline_instrument": null,
"inline_customer": null,
"declined_reason": null,
"declined_reason_rail": null,
"risk_status": null,
"risk_evaluated_at": null,
"risk_reasons": null,
"created_at": "2026-01-15T10:03:00.000000Z",
"updated_at": null,
"result_at": null,
"chargeback_at": null
}If the instrument is not active (for example, it failed penny validation or validation is still in progress), this request is rejected with instrument_in_invalid_state. Wait for the instrument to become active before you charge it. See Penny validation.
Handle the result
When the banking network processes the charge, Monato sends a charge_result webhook to your endpoint. Match it to your charge with charge_id.
{
"event": "charge_result",
"timestamp": "2026-01-16T12:00:00.000000+00:00",
"data": {
"charge_id": "55555555-5555-4555-8555-555555555555",
"charge_result": "declined",
"charge_reference": null,
"amount": 100.0,
"declined_reason": "insufficient_funds",
"risk_status": "ok",
"risk_reasons": null,
"client_debt_id": null,
"instrument_identifier": null,
"is_test": false
}
}See Webhook events for every field, Charge statuses for result values, and Error codes for decline reasons.
Retrieve, list and update charges
| Action | Endpoint |
|---|---|
| Get one charge | GET /charges/{charge_id} |
| List charges | GET /charges |
Update a charge’s reference |
PUT /charges/{charge_id} |
GET /charges uses cursor pagination (limit up to 1000, default 100, and cursor from the previous response’s next_cursor). You can filter by customer_id, instrument_id, reference, instrument_identifier (searches inline and linked instruments), created_at_from, created_at_to, result_at_from and result_at_to.
curl 'https://directdebit.monato.com/charges?customer_id=33333333-3333-4333-8333-333333333333' \
--header 'x-api-key: YOUR_API_KEY'Charges can’t be canceled through the API. PUT /charges/{charge_id} only changes the reference.