A webhook is a web callback that Belvo uses to send notifications about a specific link. You will need to set up webhooks in order to use Belvo's APIs.
You can use services like Webhook.site or Pipedream to create temporary endpoints for testing.
To set up a webhook:
- Log in to your Direct Debit Portal. (Sandbox Login | Production Login)
- Go to Developers -> Webhooks. (Sandbox Webhooks | Production Webhooks)
- Click New webhook.
- Enter your URL.
- Select the events you want to receive, or select All events.
- Optionally, enter an authentication token. See Authentication.
- Click Create webhook.
✅ Your webhook is successfully added.

You can add up to 5 webhooks. To edit a webhook, click its row in the list. Use the Active / Inactive toggle to pause or resume deliveries to a webhook without deleting it, and the delete icon to remove it. Inactive webhooks do not receive any events.
Each event is delivered to every active webhook that is subscribed to its eventType (or to All events). Every webhook receives the same payload.
If two webhooks are subscribed to the same event, both receive it. This is intentional, so you can send the same events to different systems, such as your backend and a monitoring tool.
Each webhook can have its own authentication token. When a token is set, every request to that webhook includes it in the Authorization header:
Authorization: Bearer <token>Verify this header on your server to make sure the request comes from Belvo.
To check that your webhook is reachable, click Test on the webhook's row and select an event type. The list shows the event types the webhook is subscribed to (all of them if it is subscribed to All events). Belvo sends a sample payload for the selected event type to your URL.

The Event delivery panel shows the request that was sent, along with the response code and body returned by your server. Click Resend to send it again. Test deliveries are not retried.
{
"eventType": "payment_request_update",
"eventCode": "payment_request_successful",
"datetime": "2022-01-01T12:34:56.789Z",
"details": {
"id": "3118128a-6792-4b06-bd61-4acf6f6ad6b5",
"reference": "your_reference_here",
"status": "successful",
"amount": 100.5,
"failedReason": null,
"failedMessage": null
}
}The result of a penny validation is delivered through the payment method registration webhooks. A successful validation sends payment_method_registration_successful (the payment method becomes active), while a failed validation sends payment_method_registration_failed with a failedReason of account_validation_failed. For more information, see our dedicated Penny Validation guide.
| Parameter | Type | Description |
|---|---|---|
eventType | string | The type of the API resource event. Can be one of: consent_update, customer_update, payment_method_update, payment_request_update, or withdrawal_update. |
eventCode | string | The code of the webhook event. For details regarding the possible values, please see the Webhook Event Codes section below. |
datetime | string | The ISO-8601 timestamp when the event was sent. |
details | object | An object containing specific data about the event. |
details.id | string | The Belvo ID of the API resource (Customer, Payment Method, Payment Request, or Withdrawal) that the event is related to. |
details.reference | string | If you provided a reference when creating the resource, an optional description of the object. |
details.status | string | The status of the resource. |
details.amount | number | The amount of the payment request. This field is present for all payment_request event codes. |
| string | If the status is For details regarding the expected |
| string | If the status is For details regarding the expected |
details.documentNumber | string | If the event is related to a customer, this field contains the RFC or CURP of the customer related to the direct debit payment method. This field is only available for customer-related events. |
details.paymentMethodId | string | If the event is related to a consent, this field contains the ID of the associated payment method. This field is only available for consent-related events. |
details.reasons | array | If the event is consent_rejected or consent_incomplete_information, this field lists the files that failed validation. Each item has a fileType (id_front, id_back, selfie, or contract) and a code. For the list of possible codes, see Consent rejection reasons. This field is only available for these two consent events, and only when there is at least one reason to report. |
details.fromDatetime | string | If the event is related to a withdrawal, this field contains the ISO-8601 timestamp of the start of the settlement period. This field is only available for withdrawal-related events. |
details.toDatetime | string | If the event is related to a withdrawal, this field contains the ISO-8601 timestamp of the end of the settlement period. This field is only available for withdrawal-related events. |
details.extra | object | If the event is related to a withdrawal, this field contains additional information about the withdrawal. This field is only available for withdrawal-related events. |
For information about specific payloads for a given API resource and webhook code, just click on the webhook Event Code in the table below.
The Event Type column is what you subscribe to when you set up a webhook.
| Resource | Event Type | Event Code | Sent whenever... |
|---|---|---|---|
| Customers | customer_update | customer_blocked | the customer related to the direct debit payment method was blocked due to suspicious activity. This webhook is sent globally to all merchants, even if they don't have the blocked customer registered, since the block list is shared across all merchants. |
| Customers | customer_update | customer_unblocked | the customer related to the direct debit payment method was unblocked after review by relevant parties. |
| Consents | consent_update | consent_submitted | all required documents have been uploaded for the consent. |
| Consents | consent_update | consent_confirmed | the consent has been approved and is now active. |
| Consents | consent_update | consent_incomplete_information | documents are missing or invalid for the consent. |
| Consents | consent_update | consent_rejected | the consent has been denied. |
| Payment Methods | payment_method_update | payment_method_registration_successful | the registration of the direct debit payment method was successful. |
| Payment Methods | payment_method_update | payment_method_registration_failed | the registration of the direct debit payment method failed. |
| Payment Methods | payment_method_update | payment_method_registration_canceled | the direct debit registration was canceled (usually by the owner). |
| Payment Requests | payment_request_update | payment_request_successful | the payout was successful and we received confirmation from the payment infrastructure provider. |
| Payment Requests | payment_request_update | payment_request_failed | an error is reported by the payment infrastructure provider. |
| Payment Requests | payment_request_update | payment_request_chargeback | a chargeback has been made by your customer. |
| Withdrawals | withdrawal_update | withdrawal_created | a withdrawal has been created and funds are being settled to the merchant. |
When a customer is blocked due to a chargeback, all merchants receive the customer_blocked webhook, regardless of whether they have that customer registered. This is because Belvo maintains a global block list shared across all merchants to protect the entire ecosystem from fraudulent activity. The webhook is delivered to your webhooks subscribed to customer_update or to All events.
The webhook payload includes the blocked customer's documentNumber (RFC or CURP) so you can check if the customer exists in your system and take appropriate action.
The customer_blocked webhook is sent only once to each merchant (in other words, if you already received one and a new chargeback occurs for the same customer but for another merchant, you will not receive another webhook for that customer).
The system sends webhook notifications whenever a consent status changes to allow real-time tracking of the consent lifecycle. No webhook is sent for the initial awaiting_information status.
When a consent moves to rejected or incomplete_information because of its files, the webhook includes details.reasons. It has one item for each file that failed validation, with the fileType and a code that explains the problem. If one file has several problems, we send the main one.
| Code | Meaning |
|---|---|
wrong_document_type | The file is not the expected type of document. |
unreadable_document | The file cannot be read, or the face is not clear enough to compare. |
invalid_id_front | The file is not a valid front of a Mexican government ID. |
invalid_id_back | The file is not a valid back of a Mexican government ID. |
invalid_selfie | The file is not a valid selfie. |
invalid_contract | The file is not a valid contract. |
faces_do_not_match | The ID and the selfie are valid, but the faces do not belong to the same person. We send it for both id_front and selfie. |
duplicate_file | The same file was uploaded for more than one file type. The consent moves to incomplete_information. |
validation_failed | The file did not pass validation, and we have no more specific reason. Upload a new file. |
validation_unavailable | The validation service was temporarily unavailable, or a related file is missing, for example an ID front without a selfie. Try again or upload the missing file. |
{
"eventType": "consent_update",
"eventCode": "consent_incomplete_information",
"datetime": "2025-10-30T12:34:56.789Z",
"details": {
"id": "4fa8f2b1-3c7d-4e9a-b5c6-111111111101",
"paymentMethodId": "4fa8f2b1-3c7d-4e9a-b5c6-111111111102",
"status": "incomplete_information",
"reasons": [
{ "fileType": "id_front", "code": "duplicate_file" },
{ "fileType": "id_back", "code": "duplicate_file" },
{ "fileType": "selfie", "code": "duplicate_file" },
{ "fileType": "contract", "code": "duplicate_file" }
]
}
}When you receive a webhook from Belvo, make sure that you respond with a 2XX status code (for example, a 200). See our Retry policy for which failures we retry.
We retry a webhook delivery only when your server is unreachable, times out, or responds with an HTTP 5xx status code. In those cases we retry every 60 minutes for up to 3 attempts (the initial delivery plus 2 retries).
We do not retry HTTP 4xx responses (for example 400, 404, or 418). Those are treated as client errors and the webhook is not sent again.
For example, if the first attempt fails with a 503, our system waits 60 minutes before trying again, and continues until it receives a 2XX response or reaches the maximum of 2 retries.
Retries are per webhook. If one of your webhooks fails, only that webhook is retried. Your other webhooks are not affected.
Some test services (including webhook.site's default response) return 418. That is a 4xx status, so Belvo will not retry it. Configure the test endpoint to return 2XX if you want to confirm successful delivery.