- Fundamentals
- API conventions
Webhooks
Receive Kustom events by HTTPS POST instead of polling — the event types, the slim payload shape, Standard Webhooks signature verification, secret rotation and why your handler must be idempotent.
A webhook tells you that something happened in Kustom without you having to poll for it.
You register an HTTPS endpoint in the Kustom Portal, subscribe it to event types, and Kustom
POSTs to it when one occurs. Payloads are deliberately slim — they identify the thing that
changed, not its full state — so a handler almost always ends by fetching the object from the
relevant API.
What a webhook delivery contains
Section titled “What a webhook delivery contains”{ "id": "kevt_1YcaJdWrmlw9yp1GiiVgad", "merchant_id": "DM11917000", "timestamp": "2026-04-10T08:27:07.248645321Z", "type": "capture.created", "data": { "order_id": "25ed76ed-6477-46bb-8444-63945789ccfb", "capture_id": "070ddfa9-f7a8-4fb0-8291-b5b31a300a23", "created_at": "2026-04-10T08:27:02.648627Z" }}| Field | What it is |
|---|---|
id | Unique ID for the event, stable across retry attempts. Use it for deduplication. |
merchant_id | Your Kustom Merchant ID. |
type | The event type, from the table below. |
timestamp | ISO 8601 time the event was generated. |
data | Identifiers for the affected objects. Contents vary by event type. |
Treat data as a pointer, not a snapshot. By the time you handle the event the object may
have moved on again, so read the current state through the API using the identifiers given.
Event types Kustom sends
Section titled “Event types Kustom sends”| Event type | Fires when |
|---|---|
order.created | A new order has been created. |
capture.created | A capture has been made on an order. |
refund.created | A refund has been issued on an order. |
dispute.created | A new dispute has been opened on an order. |
dispute.updated | An existing dispute changes, for example its status. |
ipp.session.created | An in-person payment session has been created. |
ipp.session.activated | An in-person payment session has been activated. |
ipp.payment.failed | An in-person payment attempt has failed. |
ipp.session.finalized | An in-person payment session has been finalized. |
ipp.session.cancelled | An in-person payment session has been cancelled. |
New event types are added over time. A handler that throws on an unrecognized type will
break the first time one appears — ignore what you haven’t subscribed to and log it.
Setting up an endpoint
Section titled “Setting up an endpoint”- Expose an HTTPS endpoint that accepts
POST, returns a2xxpromptly and does its real work asynchronously. Anything other than a2xx, or a timeout, is a failed delivery and will be retried. - Register it in the Kustom Portal, under Integrations → Webhooks, and choose the event types it should receive.
- Verify the signature on every request before you act on the body.
Acknowledge first, process second. A handler that captures, refunds or writes to a slow database before replying is a handler that will be sent duplicates.
Verifying the signature
Section titled “Verifying the signature”Kustom signs every payload following the Standard Webhooks specification, so an off-the-shelf Standard Webhooks library will verify a Kustom delivery unmodified.
| Header | What it carries |
|---|---|
webhook-id | Unique message ID, identical across retry attempts. Use for deduplication. |
webhook-timestamp | Unix timestamp in seconds of when the message was sent. |
webhook-signature | One or more space-delimited HMAC-SHA256 signatures. |
Your signing secret is generated when you configure the destination and has the form
whsec_<base64-encoded-secret>. Retrieve it from the Portal, store it as a secret and check
the webhook-timestamp against your own clock to reject replayed deliveries. Implementation
guidance is in the Standard Webhooks
specification.
webhook-signature can hold more than one signature. Verify against any accepted secret,
not only the first — that’s what makes rotation non-breaking.
Rotating the signing secret
Section titled “Rotating the signing secret”Rotate from the Portal at any time. During rotation Kustom signs with both the old and the
new secret, space-delimited in webhook-signature, so a verifier that accepts either keeps
working throughout. Rotation takes up to 10 minutes to propagate because of caching. Once
your code is verifying with the new secret, retire the old one.
Retries and idempotent handling
Section titled “Retries and idempotent handling”If your endpoint doesn’t return 2xx, or times out, Kustom retries with exponential
backoff for up to 3 days.
Deliveries are therefore at-least-once, and your handler must be idempotent. Deduplicate on
id in the body, or equivalently on the webhook-id header — both are stable across retries
of the same event. Record the ID before doing the work and discard anything you’ve already
seen.
Related
Section titled “Related”- Fetching the object a webhook points at — the read call a handler ends with
- Retrying calls you make to Kustom — the mirror image of this page
- Reading an error body — when the follow-up read fails
- Where events sit in the order lifecycle — what
capture.createdandrefund.createdmean
This page is also available as Markdown — append .md to its URL — and is indexed in llms.txt.