Webhooks
Implement webhook notifications for verification updates, delivery retries, authentication, signature verification, and troubleshooting.
Webhook NotificationsWe strongly recommend enabling webhook notifications. Buró de Ingresos' verification processes vary by data source and can take anywhere from 10 seconds to 8 minutes depending on your configuration.
When properly implemented, webhooks allow you to automatically trigger backend processes that rely on employment and income verification data.
Below, we provide use cases and examples to help you integrate webhook notifications successfully.
Make sure your endpoint responds with
200or201within 30 seconds. Process the data asynchronously after acknowledging — do not wait for processing to complete before responding.
Introduction
Use webhooks to receive real-time verification updates from Buró de Ingresos. Register a public endpoint URL, then handle HTTP POST notifications containing event details in JSON format.
Create Your First Webhook
Register a webhook with the POST /webhooks endpoint:
curl --request POST \
--url https://api.burodeingresos.com/webhooks \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"description": "Production webhooks",
"endpoint_url": "https://webhook.site/0d4af195-9868-4f72-b5d0-c1173190a012"
}
'Successful Response:
{
"id": "d1e2f3a4-b5c6-d7e8-f9a0-b1c2d3e4f5a6",
"company_id": "a4b5c6d7-e8f9-a0b1-c2d3-e4f5a6b7c8d9",
"description": "Production webhooks",
"endpoint_url": "https://webhook.site/0d4af195-9868-4f72-b5d0-c1173190a012",
"secret_key": "ZxyWvUt987654321SecretKeyExample0",
"is_active": true,
"created_at": "2025-07-10T19:25:45.123456Z",
"updated_at": "2025-07-10T19:25:45.123456Z"
}Webhook Event Schema
Webhook events use the following JSON payload structure:
{
"event": "string",
"verification_id": "string (UUID format)",
"identifier": "string",
"status": "string (enum: 'completed', etc.)",
"data_available": "boolean",
"can_retry": "boolean",
"entities": "array<string>",
"last_updated_at": "string (ISO 8601 datetime)",
"timestamp": "string (ISO 8601 datetime)",
"external_id": "string | null"
}Webhook Events Catalog
Buró de Ingresos triggers the following webhook event during the verification process:
| Event | Description |
|---|---|
verification.completed | Indicates that a verification process has been completed across all configured data sources. This event is triggered regardless of whether data was found or not. |
verification.historical (Deprecated) | Deprecated. Previously sent when a verification resolved with historical data. It has been removed from existing webhook subscriptions and is no longer delivered by default. It still works if you explicitly subscribe to it, but use verification.completed instead, this event may be removed in the future. |
⚠️ verification.historical is deprecated. We've removed it from existing webhook subscriptions, so it is no longer delivered by default. Keep using verification.completed. The event still works if you explicitly subscribe to it, but we recommend against it and may remove it in the future.
Understanding Verification Status
The verification process can result in different outcomes:
- in_progress: Verification is in progress.
- completed: Verification was completed.
Retries
If we do not receive a 200 or 201 status code within 30 seconds after the first POST request to your webhook URL, we will make up to three additional attempts (stopping if we receive a 200 or 201), waiting approximately 30 seconds between each attempt.
Handling Webhook Events
To ensure reliable webhook delivery, your endpoint must follow these best practices:
Response Codes
Return an HTTP 200 or 201 status code to confirm receipt. Any other response code (3xx, 4xx, 5xx) is treated as a failed delivery and will trigger a retry.
Timing
Acknowledge the webhook immediately upon receipt. Do not wait for internal processing to complete — delays can cause timeouts and unnecessary retries.
Asynchronous Processing
Do not execute complex business logic synchronously inside the webhook handler. The recommended pattern is:
- Receive the webhook
- Respond with
200or201immediately - Process the data asynchronously
Auto-disable Failed Webhooks
If your endpoint repeatedly fails to return a successful response, it will be automatically disabled to prevent queue buildup. You will receive an email notification when this happens. To re-enable it, update the webhook configuration via the API or the dashboard.
Example Events
Successful Verification with Full Data
{
"event": "verification.completed",
"verification_id": "bd830637-ea6e-4888-80ab-a09f01fc9209",
"identifier": "OICE940722HGFRST08",
"status": "completed",
"data_available": true,
"can_retry": false,
"entities": ["profile", "invoices", "employment", "employment_files"],
"last_updated_at": "2025-04-29T10:37:25Z",
"timestamp": "2025-04-29T10:37:25Z",
"external_id": null
}Successful Verification with Partial Data
{
"event": "verification.completed",
"verification_id": "bd830637-ea6e-4888-80ab-a09f01fc9217",
"identifier": "OICE940722HGFRST08",
"status": "completed",
"data_available": true,
"can_retry": false,
"entities": ["profile"],
"last_updated_at": "2025-04-29T10:38:25Z",
"timestamp": "2025-04-29T10:38:25Z",
"external_id": null
}Verification with No Data Found
{
"event": "verification.completed",
"verification_id": "bd830637-ea6e-4888-80ab-a09f01fc9211",
"identifier": "OICE940722HGFRST08",
"status": "completed",
"data_available": false,
"can_retry": false,
"entities": [],
"last_updated_at": "2025-04-29T10:32:15Z",
"timestamp": "2025-04-29T10:32:15Z",
"external_id": null
}Verification Error
{
"event": "verification.completed",
"verification_id": "bd830637-ea6e-4888-80ab-a09f01fc9212",
"identifier": "OICE940722HGFRST08",
"status": "completed",
"data_available": false,
"can_retry": true,
"entities": [],
"last_updated_at": "2025-04-29T10:33:15Z",
"timestamp": "2025-04-29T10:33:15Z",
"external_id": null
}Available Entities
When a verification is successful, the entities array will contain one or more of the following:
- profile: Basic personal information, address and employment status
- invoices: Invoice and payment history from payroll and invoicing systems
- employment: Detailed employment records from IMSS/ISSSTE
- employment_files: Employment documents from IMSS/ISSSTE
- earnings: Income history from gig economy platforms (e.g., Uber, DiDi)
- business_profile: Legal name, commercial name, and RFC for business identifiers
IP Whitelisting
If you restrict inbound traffic by IP address, fetch the current outbound IP list instead of hardcoding values.
While IPs may change, the list returned by this endpoint is guaranteed to remain valid for at least 7 days.
We recommend implementing an automated weekly refresh to keep your allow list up to date.
curl --request GET \
--url https://api.burodeingresos.com/outbound_ips \
--header 'accept: application/json' \
--header 'content-type: application/json'Response
[
{
"ip_address": "string"
}
]Webhook Authentication
Buró de Ingresos supports multiple authentication methods for webhook delivery. These ensure secure communication between our system and your endpoints.
Authentication Types
When creating or updating a webhook, you can configure authentication to automatically include credentials with each request.
1. JWT Bearer Token
Generates and attaches a JWT token to each webhook request.
Configuration:
{
"description": "Production webhook with JWT",
"endpoint_url": "https://yourapi.com/webhook/jwt",
"authentication": {
"type": "jwt",
"secret": "your-jwt-secret-key",
"expiration_seconds": 3600
}
}How it works:
- A JWT token is generated using your provided secret
- The token expires based on expiration_seconds
- Your endpoint should validate the JWT with the same secret
Example webhook request headers:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
X-Signature: 42FhFAPEEba//HkW3/8HSyB3j2S9hY2eTHAowk5PJcM=
Content-Type: application/json2. Custom Headers
Sends predefined headers with every webhook request.
Configuration:
{
"description": "Webhook with custom headers",
"endpoint_url": "https://yourapi.com/webhook/headers",
"authentication": {
"type": "custom_headers",
"headers": {
"X-Api-Key": "your-api-key-here",
"X-Client-Id": "your-client-id"
}
}
}How it works:
- The specified headers are included in each request
- Your endpoint validates them for authentication
- Useful for API key–based or custom authentication schemes
Example webhook request headers:
X-Api-Key: your-api-key-here
X-Client-Id: your-client-id
X-Custom-Auth: custom-value
X-Signature: 42FhFAPEEba//HkW3/8HSyB3j2S9hY2eTHAowk5PJcM=
Content-Type: application/json3. Token Endpoint
Fetches a token from your authentication server before sending the webhook.
Configuration:
{
"description": "Webhook with token endpoint",
"endpoint_url": "https://yourapi.com/webhook/",
"authentication": {
"type": "token_endpoint",
"url": "https://authserver.com/get-token",
"method": "POST",
"request_headers": {
"Content-Type": "application/json",
"Accept": "application/json"
},
"request_body": {
"client_id": "your-client-id",
"client_secret": "your-client-secret",
"grant_type": "client_credentials"
},
"response_key": "access_token"
}
}How it works:
- Before sending a webhook, our system requests a token from your endpoint
- The response is parsed to extract the token using
response_key - The token is included in the webhook request headers
Token endpoint request example:
POST https://authserver.com/get-token
Content-Type: application/json
{
"client_id": "your-client-id",
"client_secret": "your-client-secret",
"grant_type": "client_credentials"
}Expected token endpoint response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}Example webhook request headers:
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
X-Signature: 42FhFAPEEba//HkW3/8HSyB3j2S9hY2eTHAowk5PJcM=
Content-Type: application/jsonCreating Webhooks with Authentication
Use the POST /webhooks endpoint to create a webhook with authentication:
curl --request POST \
--url https://api.burodeingresos.com/webhooks \
--header 'X-API-Key: your-api-key' \
--header 'Content-Type: application/json' \
--data '{
"description": "Production webhook with JWT auth",
"endpoint_url": "https://yourapi.com/webhook/handler",
"authentication": {
"type": "jwt",
"secret": "your-jwt-secret",
"expiration_seconds": 3600
}
}'Updating Webhook Authentication
Use the PATCH /webhooks/{webhook_id} endpoint to update authentication settings:
curl --request PATCH \
--url https://api.burodeingresos.com/webhooks/your-webhook-id \
--header 'X-API-Key: your-api-key' \
--header 'Content-Type: application/json' \
--data '{
"authentication": {
"type": "custom_headers",
"headers": {
"X-Api-Key": "new-api-key",
"X-Environment": "production"
}
}
}'Verify Webhook Signatures
For security, every webhook event sent by the Buró de Ingresos includes a signature header that lets you verify the authenticity of the request. This ensures the event truly comes from us and that the payload hasn't been altered in transit.
How it works
- When you create a webhook, you receive a
secret_keyin the response.
Save this securely — it is used to verify all webhook events.
- Each webhook notification includes an
X-Signatureheader and the raw request body (the JSON payload).
Verification steps
- Compute an HMAC using SHA-256 over the raw JSON body of the webhook request, with your
secret_key. - Compare the computed signature with the value in
X-Signature. - If they match, the webhook is valid. Otherwise, discard the request.
Example Verification (Node.js)
const crypto = require('crypto');
// Header from webhook request
const signatureHeader = '42FhFAPEEba//HkW3/8HSyB3j2S9hY2eTHAowk5PJcM=';
// Payload body from webhook request
const payload = {
"event": "verification.completed",
"verification_id": "019906e3-0a7a-71bc-a81c-14d2d8dbef9d",
"identifier": "CERC660204HMCLCR09",
"status": "completed",
"data_available": true,
"can_retry": false,
"entities": ["profile", "employment", "employment_files"],
"last_updated_at": "2025-09-01T20:06:39.976067400Z",
"timestamp": "2025-09-01T20:06:39.976067400Z",
"external_id": null
};
// Secret key from webhook creation response
const secretKey = 'p4AaudslHLBF9h5k7Brl0aGs3ijt0QM4yHhUWyQTp14';
const decodedSecret = Buffer.from(secretKey, 'base64url');
// Compute HMAC
const hmac = crypto.createHmac('sha256', decodedSecret);
hmac.update(JSON.stringify(payload), 'utf8');
const expectedSignature = hmac.digest('base64');
// Compare securely
const sigBuf = Buffer.from(signatureHeader, 'base64');
const expectedBuf = Buffer.from(expectedSignature, 'base64');
if (sigBuf.length !== expectedBuf.length) {
console.log("Invalid signature header");
} else {
const isValidSignature = crypto.timingSafeEqual(sigBuf, expectedBuf);
}Troubleshooting
Common Issues
- Webhook not receiving events: Confirm that your endpoint is publicly accessible and returns
200or201. - Timeout errors: Return a successful response within 30 seconds, then process the event asynchronously.
- Missing data: Check the
entitiesarray to identify which datasets are available.
Updated about 23 hours ago
- Set up your webhook endpoint
- Register it in the API
- Test with sample verification requests
- Implement proper error handling and retry logic
- Monitor webhook delivery
