Webhooks
Zazpay webhooks allow you to receive automatic real-time notifications about important events that occur in the payment system. This functionality is particularly useful for keeping point-of-sale systems synchronized with transaction status without the need for constant API queries.
Webhooks are delivered as HTTP requests to a URL configured by the merchant. Each notification includes contextual data about the event and headers to help you verify authenticity.
How to configure a webhook
Step 1: Access the Webhooks Dashboard
- Navigate to hub.zazpay.mx/webhooks
- Ensure you are logged in with an admin or developer user account
Step 2: View Current Webhooks
- You will see a list of all available webhooks
- Each webhook displays:
- Brief description of the event
- Current status (enabled/disabled)
- Configuration details
Step 3: Configure a Webhook
-
Click on the webhook you want to configure
-
You will be taken to the configuration page where you can set up:
Basic Settings:
- Enable/Disable Switch: Toggle the webhook on or off
- Webhook URL: Enter the endpoint URL where notifications will be sent
- Request Method: Choose one of: GET, HEAD, POST, PUT, PATCH, DELETE
Advanced Settings:
- Custom Headers: Add any additional HTTP headers required by your endpoint
- Body Structure: Define the fields included in the request body
- Content Type: Select one of:
text/plain,application/json,application/xml,application/x-www-form-urlencoded
Step 4: Save Configuration
- Review your settings
- Click save to activate the webhook configuration
- The webhook will immediately start sending notifications to your specified endpoint
Security and verification
To verify that webhook requests originate from zazpay, implement one of the following:
- Bearer token: Configure a static token and validate it from the
Authorization: Bearer <token>header.
If both are configured, verify both. Reject requests that fail verification with HTTP 401.
Events and payload variables
Different events expose different payload variables. Select the event to see which variables are available to include in the payload.
Transaction Created
The event is emitted when a transaction is created.
Available variables:
| Variable | Type | Description |
|---|---|---|
folio | string | Transaction folio |
status | string | Current status at creation time |
clientEmail | string | Client email |
clientPhoneNumber | string | Client phone number |
amount | number | Transaction amount |
ticket | string | Optional reference/ticket |
storeName | string | Store name |
storeId | string | External store identifier |
createdAt | string | Creation timestamp (ISO 8601) |
approvedAt | string | Approval timestamp if available (ISO 8601) |
salesmanReference | string | Salesman reference/identifier |
salesmanName | string | Salesman name |
Transaction Status Change
The event is emitted when a transaction status changes (e.g., IN_PROGRESS → APPROVED/REJECTED/CANCELED).
Available variables:
| Variable | Type | Description |
|---|---|---|
folio | string | Transaction folio |
status | string | New status |
clientEmail | string | Client email |
clientPhoneNumber | string | Client phone number |
amount | number | Transaction amount |
ticket | string | Optional reference/ticket |
storeName | string | Store name |
storeId | string | External store identifier |
createdAt | string | Creation timestamp (ISO 8601) |
approvedAt | string | Approval timestamp if available (ISO 8601) |
salesmanReference | string | Salesman reference/identifier |
salesmanName | string | Salesman name |
Transaction Returned
The event is emitted when a sale is returned (devolución) via /commerce/return-transaction, or in sandbox when a sale is force-returned via /commerce/resolve-transaction with status: "RETURNED". A return also emits Transaction Status Change with status: RETURNED — configure whichever fits your integration (or both).
Available variables: the same 12 variables as Transaction Status Change, plus:
| Variable | Type | Description |
|---|---|---|
returnedAt | string | Return timestamp (ISO 8601) |
returnReason | string | Reason provided by the commerce (empty string when omitted) |
returnedAmount | number | Total amount returned |
settlementImpact | string | NOT_YET_SETTLED or DISCOUNT_NEXT_SETTLEMENT |
Client Affiliated
The event is emitted when a client is created/affiliated in your company.
Available variables:
| Variable | Type | Description |
|---|---|---|
name | string | Client given name |
firstSurname | string | Client paternal surname |
secondSurname | string | Client maternal surname |
fullName | string | Full name |
email | string | Client email |
phoneNumber | string | Client phone number |
affiliationDate | string | Affiliation timestamp (ISO 8601) |
zipCode | string | ZIP/postal code |
Client Updated
The event is emitted when client data is updated.
Available variables:
| Variable | Type | Description |
|---|---|---|
name | string | Client given name |
firstSurname | string | Client paternal surname |
secondSurname | string | Client maternal surname |
fullName | string | Full name |
email | string | Client email |
phoneNumber | string | Client phone number |
affiliationDate | string | Original affiliation timestamp (ISO 8601) |
zipCode | string | ZIP/postal code |
Payment Received (Conciliación)
The event is emitted when your weekly settlement (corte / conciliación) is paid out and the bank transfer is confirmed. Unlike the other events, this webhook has a fixed, versioned payload — the body structure is not customizable and any configured body template is ignored.
Payload contract (version: "1.0"; future changes will be additive):
{
"event": "PAYMENT_RECEIVED",
"version": "1.0",
"paymentId": 123,
"period": { "startDate": "2026-06-22", "endDate": "2026-06-28" },
"company": {
"name": "My Commerce",
"razonSocial": "My Commerce SA de CV",
"rfc": "MCO010101AAA"
},
"paymentAccount": { "clabe": "**************1234" },
"summary": {
"transactionCount": 42,
"subtotal": 100000.0,
"commission": 3500.0,
"commissionIva": 560.0,
"grossPaid": 95940.0,
"returnCount": 1,
"returnedTotal": 1500.0,
"totalPaid": 94440.0
},
"files": {
"json": { "url": "https://…", "expiresAt": "2026-07-03T18:30:00-06:00" },
"xlsx": { "url": "https://…", "expiresAt": "2026-07-03T18:30:00-06:00" }
},
"sentAt": "2026-07-02T18:30:00-06:00",
"test": false
}
| Field | Type | Description |
|---|---|---|
event | string | Always PAYMENT_RECEIVED |
version | string | Payload contract version (1.0) |
paymentId | number | Settlement identifier — use it as your idempotency key to deduplicate retries |
period.startDate/endDate | string | Settlement period (YYYY-MM-DD, America/Mexico_City) |
company | object | Commerce name, legal name (razón social) and RFC |
paymentAccount.clabe | string | Payout CLABE masked to the last 4 digits |
summary | object | Transaction count, subtotal, commission, commission VAT, and settlement totals |
summary.grossPaid | number | Amount paid before subtracting returns |
summary.returnCount | number | Number of returned sales (devoluciones) in the period |
summary.returnedTotal | number | Total amount of returns subtracted from grossPaid |
summary.totalPaid | number | Net amount received: grossPaid − returnedTotal |
files.json / files.xlsx | object | Signed download links for the settlement detail. Links expire 24 hours after emission (expiresAt). Contact support or your account manager if you need them re-issued |
sentAt | string | Delivery timestamp (ISO 8601, America/Mexico_City) |
test | boolean | true only for sandbox test deliveries |
Settlement detail file (files.json)
The JSON file contains the full settlement detail: { period, company, summary, transactions[], returns[] }. period, company and summary match the webhook payload above. Each entry in transactions[] looks like:
{
"transactionId": "tx-1",
"folio": "1001",
"folioExternal": "EXT-A1",
"ticket": "T-1",
"date": "2026-06-23",
"storeName": "Main Store",
"storeId": "ext_store_456",
"salesmanName": "John Doe",
"amount": 1000.0,
"commission": 35.0,
"commissionIva": 5.6,
"totalPaid": 959.4
}
| Field | Type | Description |
|---|---|---|
transactionId | string | ZazPay transaction identifier |
folio | string | ZazPay folio |
folioExternal | string | null | Your transaction identifier, as sent when creating the sale (null if none) |
ticket | string | Ticket reference (- if none) |
date | string | Sale date |
storeName | string | Store name |
storeId | string | null | Your external store identifier — same meaning as in the transaction webhooks |
salesmanName | string | Salesman name |
amount | number | Sale amount |
commission | number | Commission |
commissionIva | number | VAT on the commission |
totalPaid | number | Net amount paid for this sale |
Returns (devoluciones) that occurred within the settlement period are listed under returns[]. Each returned sale's full amount is subtracted from grossPaid to produce the net totalPaid. Each entry looks like:
{
"transactionId": "55",
"folio": "900",
"folioExternal": "EXT-900",
"ticket": "T-900",
"returnedAt": "2026-06-24T10:00:00-06:00",
"storeName": "Main Store",
"storeId": "ext_store_456",
"salesmanName": "John Doe",
"amount": 400.0,
"returnReason": "Producto defectuoso",
"settlementImpact": "NOT_YET_SETTLED"
}
| Field | Type | Description |
|---|---|---|
folio | string | ZazPay folio of the returned sale |
folioExternal | string | null | Your transaction identifier (null if none) |
returnedAt | string | When the return was registered (ISO 8601, America/Mexico_City) |
storeName/storeId | string | Store name and your external store id |
amount | number | Full amount of the returned sale (subtracted from grossPaid) |
returnReason | string | null | Reason given at return time |
settlementImpact | string | NOT_YET_SETTLED (sale had not been paid yet) or DISCOUNT_NEXT_SETTLEMENT (sale was paid in a prior corte) |
The XLSX file contains the same detail in spreadsheet form: a Transacciones sheet (with the Folio externo and ID externo de tienda columns) and, when there are returns, a Devoluciones sheet.
Notes:
- Delivery may be retried, so the same
paymentIdcan arrive more than once — deduplicate on it. totalPaidis net of returns (grossPaid − returnedTotal). It reflects the settlement figure reported here; reconcile against your own records usinggrossPaidand thereturns[]detail.- To try this webhook end to end without waiting for a real settlement, see Testing the Payment Received (Conciliación) webhook in sandbox.
HTTP methods and content types
- Allowed methods: GET, HEAD, POST, PUT, PATCH, DELETE
- For GET and HEAD, no request body is sent. Use headers and query string if needed.
- For POST, PUT, PATCH, and DELETE, the request body is sent using the configured content type.
- Supported content types:
text/plain,application/json,application/xml,application/x-www-form-urlencoded
Example payload (Transaction Status Change)
{
"folio": "12345678",
"status": "APPROVED",
"clientEmail": "customer@example.com",
"clientPhoneNumber": "9999999999",
"amount": 1500.0,
"ticket": "TCK-12345",
"storeName": "Main Store",
"storeId": "ext_store_456",
"createdAt": "2024-01-15T10:30:00Z",
"approvedAt": "2024-01-15T10:32:15Z",
"salesmanReference": "9999999999",
"salesmanName": "John Doe"
}
Notes:
- For GET/HEAD deliveries, the same fields can be provided as query parameters, depending on your configuration.
- Store the
idempotency keyheader we include to deduplicate retries.