Skip to main content

Webhooks — concepts

Webhooks are the mechanism Max Pay uses to notify events to the integrator's ERP in near-real time. Without webhooks, the only alternative is polling, which is inefficient and latent.

This page covers the conceptual model. To implement (create the endpoint, verify the HMAC signature, send a test delivery), see Webhooks → Getting started. For the operational reference (endpoints, pause/resume, rotate the secret), see Webhooks → Configuration. For the event catalog, see Events.

Model

If the integrator's endpoint does not respond 2xx within 10 seconds, Max Pay retries with exponential backoff.

Delivery

Each event is sent as a POST to the configured URL with:

Headers

HeaderDescription
X-MaxPay-SignatureHMAC signature. See verification
X-MaxPay-EventEvent type. E.g. receivable.created
X-MaxPay-Delivery-IdUUID of this delivery (changes between retries of the same event)

Body

{
"id": "evt_b3f8a1c2d4e5f6789012345678901234",
"type": "receivable.created",
"createdAt": "2026-05-15T14:30:05-03:00",
"data": {
"receivable": { "id": 1234, "amount": 45000, "..." : "..." }
}
}

Events are immutable. If the underlying data changes (e.g. the receivable is updated), a new event is emitted (receivable.updated); the original is not modified.

Signature verification

Every delivery carries the X-MaxPay-Signature signature:

X-MaxPay-Signature: t=1715812800,v1=5257a869e7ecebeda...

Where:

  • t: Unix timestamp at signing time (same value as X-MaxPay-Timestamp).
  • v1: HMAC-SHA256 computed over <timestamp>.<body> using the webhook secret as the key.

The integrator must recompute the HMAC with their secret and compare in constant time. It is also good practice to reject deliveries with an old timestamp (typically > 5 minutes) to prevent replay attacks.

For a complete implementation with example code, see Webhooks → Getting started.

Always verify the signature

Without verification, any attacker with your public URL can inject fake events into your ERP. Verification is the difference between a robust integration and an attack vector.

Retries

If the endpoint responds with anything other than 2xx, Max Pay retries on this schedule:

AttemptDelay from previous
1(immediate)
21 minute
35 minutes
430 minutes
52 hours
612 hours

Total: ~14 hours from the original event. After that window, the delivery is discarded and the webhook is marked as FAILED. To diagnose and resend failed deliveries, see Delivery diagnostics.

Event deduplication

Because of the "at-least-once" retry model, the integrator may receive the same event more than once. The event.id is unique and immutable for each event, and stays constant across retries of the same event. The standard strategy is to persist processed event.ids and discard (responding 200 OK) any that are already on the list.

For the implementation pattern, see Webhooks → Getting started.

Order and concurrency

Events close together in time may arrive out of order due to internal concurrency. The integrator must not assume a sequence between distinct events (e.g. receivable.created may arrive after receivable.paid if retries of the former lagged).

For rollback handling: a reversed transaction is notified as a movement.created with operationType: ROLLBACK referencing the original movement. The integrator's matching logic must contemplate this case.