Webhooks
MAXFI sends an HTTP POST with a JSON event to your webhook URL when the status of a payment or a payout changes.
Set the URL in the merchant portal under Developer Hub → Webhooks, switch webhooks on and choose the events. The signing secret (whsec_…) is created when you first save the settings and is shown on the same page. A webhook_url passed to POST /v2/payments/checkout receives that session’s payment.requires_action, payment.succeeded and payment.failed events even when webhooks are switched off in the portal; the event selection and the account secret still apply. payment.refunded and payment.expired always go to the account URL.
Delivery
- A delivery succeeds when your endpoint answers with any 2xx status within 10 seconds. Another status, a timeout or a network error leads to the next attempt — up to 8 attempts (see the schedule).
- Redirects are not followed: a 3xx answer counts as a failure.
- The URL must be a public HTTPS address.
- An event can arrive more than once, and events can arrive out of order. Store
id(also sent inX-MAXFI-Event-Id) and skip events you have already processed.
Envelope
{
"id": "evt_mhx3k2a1_4f8e2b9c1d7a",
"type": "payment.succeeded",
"livemode": true,
"created_at": "2026-11-13T06:04:14.512Z",
"data": {
"payment_id": "cs_9f2c4e7a1b3d5f6081a2c3e4f5a6b7c8",
"session_id": "cs_9f2c4e7a1b3d5f6081a2c3e4f5a6b7c8",
"order_number": "order_2842",
"amount": 4900,
"currency": "AZN",
"status": "succeeded",
"payment_method": "card",
"card_last4": "4242",
"card_brand": "visa",
"customer_email": "ada@example.com",
"metadata": {
"user_id": "u_842",
"channel_id": "5b0e6f2a-3c1d-4e8f-9a7b-2c4d6e8f0a1b"
},
"created_at": "2026-11-13T05:58:29.104Z",
"updated_at": "2026-11-13T06:04:14.498Z"
}
}livemode is false for test-mode payments. data.payment_id is always the MAXFI payment ID: cs_… for a payment on the payment page, pay_… for a payment created with POST /v2/payments; use it with GET /v2/payments/{id}. data.payment_method is the method the customer used, for example card, sbp or google_pay. data.metadata holds your metadata and the channel_id that MAXFI adds.
Headers
| Header | Description |
|---|---|
| Content-Type | application/json |
| User-Agent | MAXFI-Webhooks/1.0 |
| X-MAXFI-Event | Event type, the same as type. |
| X-MAXFI-Event-Id | Event ID, the same as id. Use it to skip duplicates. |
| X-MAXFI-Delivery | Delivery ID. It stays the same across the attempts of one event — useful in support requests. |
| X-MAXFI-Attempt | Attempt number, starting from 1. |
| X-MAXFI-Signature | sha256=<hex> — HMAC-SHA256 of the raw request body with your signing secret. Sent only when a signing secret is set. |
Verify the signature
Compute the HMAC over the raw bytes of the body, before parsing JSON, and compare it with the header in constant time. The key is the whole secret as shown in the portal, including whsec_. Reject requests whose signature does not match.
Payment events
MAXFI sends these events about payments.
| Type | When it is sent |
|---|---|
| payment.requires_action | The customer has to complete an extra step, for example bank verification (3-D Secure). Also sent when POST /v2/payments answers requires_action. |
| payment.succeeded | The payment succeeded. Fulfil the order on this event. It is also sent when POST /v2/payments returns the result right away, and after a capture (data.amount is the captured amount). Each payment gets it once. |
| payment.failed | The payment was declined or cancelled. data.error_code is a decline code from the MAXFI list; data.error_message is the fixed text of that code. |
| payment.refunded | A refund was accepted. For refunds made through the API, data.refund holds the amount, total_refunded and is_partial. |
| payment.expired | No final result arrived within 72 hours (data.error_code: reconcile_timeout). Check the payment before you fulfil or refund it. |
Payout events
These events report the status of card payouts. They go to the same URL as payment events, with the same headers and signature. data is the payout object without payout_url. Select the payout events in the merchant portal under Developer Hub → Webhooks: events that are not selected are not sent. Each event type is queued at most once per payout; a delivery can still repeat, so skip event IDs you have already processed.
| Type | When it is sent |
|---|---|
| payout.processing | The recipient entered a card and MAXFI sent the transfer to the bank. |
| payout.succeeded | The transfer succeeded: the money was sent to the recipient’s card. |
| payout.failed | The transfer did not go through. data.failure_code gives the reason. |
| payout.canceled | You canceled the payout before the recipient entered a card. |
| payout.expired | The link expired before the recipient entered a card (data.failure_code: link_expired). |
| payout.reversed | The bank returned the transfer after it had succeeded. |
{
"id": "evt_mhy4b7c2_9a1f3e5d7c2b",
"type": "payout.succeeded",
"livemode": false,
"created_at": "2026-11-13T12:03:41.207Z",
"data": {
"id": "po_3f9c2a7b1d4e5f60718293ab",
"object": "payout",
"method": "card",
"status": "succeeded",
"amount": 1500,
"currency": "AZN",
"description": "Payout for order 1042",
"reference": "order-1042",
"metadata": { "order_id": "1042" },
"expires_at": "2026-11-14T12:00:00.000Z",
"card": { "bin": "424242", "last4": "4242" },
"recipient": { "name": "Test Recipient" },
"failure_code": null,
"failure_message": null,
"livemode": false,
"created_at": "2026-11-13T12:00:00.000Z",
"completed_at": "2026-11-13T12:03:41.198Z"
}
}Send a test event
The event simulator in the merchant portal (Developer Hub → Sandbox) sends a test event to the webhook URL from your settings. It is signed with your webhook secret exactly like real deliveries — X-MAXFI-Signature: sha256=<hex>, if a secret is set — and comes with the same headers and in the same envelope, with "livemode": false. Use it to check that your endpoint verifies the signature and answers with 2xx.
A test event is sent once, without retries, even when webhooks are switched off or the event is not selected in your settings; in that case the portal warns that real events of this type would not be sent to that address. The portal also shows your endpoint’s response code and the event body. The event’s data is made up: the payment or payout ID does not exist in your account. The simulator offers the same event types that MAXFI sends: the payment and payout events above.
