Skip to main content

Webhook events explained

What each event means, when it's sent, and what it contains.

Written by Ezus team

When you set up a webhook, you choose which events it receives.

This article explains each one: when it's sent, what's inside, and what it's typically used for.

The full technical reference, with every field, is in the app: Settings → Integrations → Documentation → Webhooks.

The four events

Event

Sent when…

payment.succeeded

A traveler paid through a payment link

payment.failed

A payment attempt was declined

payment.refunded

A refund has gone through

booking.fully_paid

An alternative of a booking is fully paid

Payments you record by hand in Ezus Pay (bank transfers, cash, cheques) don't trigger any event: they didn't go through Ezus Pay, and you already know about them.

What every message looks like

Every message has the same outer structure, whatever the event:

{   "id": "6f1c0b3a-7f2e-4a1d-9c55-4b2f1e0a8d33",   "type": "payment.succeeded",   "created": 1758700800123,   "api_version": "2026-05-01",   "agency_id": "0b8a4c2e-…",   "data": {     "object": { … }   } }
  • id: the message's unique identifier. It stays the same if the message is sent again, so your tool can recognize a duplicate.

  • type: which event it is.

  • created: when the event occurred (a timestamp in milliseconds).

  • data.object: the event's content, described below.

The event type is also sent in the Ezuspay-Event-Type header, so your tool can route a message without reading it.

payment.succeeded: a payment was received

Sent when a traveler completes a payment on a payment link, by card or SEPA debit.

What's inside:

Field

What it is

transaction_id

The payment's ID in Ezus Pay. Use it to get the full details through the API, or to refund it

booking

The booking: its ID, its reference, and its Ezus project ID if it comes from Ezus

alternative

The alternative the payment was made on

payment_link

The payment link and its label, e.g. Deposit 30%

amount_cents, currency

The amount paid, in cents

payment_method

card or sepa_debit

paid_at

When the payment was made

custom_fields

The traveler's answers to your checkout fields, or null if the link had none

Example:

{   "transaction_id": "3b9d…",   "booking": { "id": "…", "ezus_project_id": "…", "reference": "EZ-2026-001" },   "alternative": { "id": "…", "ezus_alternative_id": "…" },   "payment_link": { "id": "…", "label": "Deposit 30%" },   "amount_cents": 150000,   "currency": "EUR",   "payment_method": "card",   "paid_at": "2026-09-24T10:12:00.000Z",   "custom_fields": [     { "key": "phone", "label": "Phone number", "type": "PHONE", "value": "+33 6 12 34 56 78" }   ] }

Typical uses: record the payment in your accounting tool, notify the salesperson, update the trip status in your CRM.

Good to know: the webhook doesn't include the payer's name. To get it, along with the refundable balance and the refund history, call GET /transactions/:id with the transaction_id.

payment.failed: a payment attempt was declined

Sent when a payment attempt fails: card declined, insufficient funds, expired card, failed authentication…

What's inside: the same transaction_id, booking, alternative, payment_link, amount and currency as above, plus:

Field

What it is

failure_code

Why it failed, e.g. insufficient_funds, expired_card, card_declined

failure_message

A readable explanation, in English, as provided by the card network

failed_at

When the attempt failed

Typical uses: remind the traveler to try again, flag the booking to your team.

Good to know:

  • A traveler who retries generates one event per failed attempt, all with the same transaction_id.

  • A failure can be followed by a success. If a payment.succeeded arrives later with the same transaction_id, the traveler managed to pay: don't send them a reminder.

  • No money has moved. It's a signal to act on, not an accounting entry.

payment.refunded: a refund went through

Sent when a refund has actually been processed, whether it was made from Ezus Pay or through the API. A SEPA refund can take a few days before this event is sent.

What's inside: transaction_id, booking, alternative, payment_link and currency, plus:

Field

What it is

refund_id

The refund's ID. A payment refunded in several steps generates one event per refund

amount_refunded_cents

The amount of this refund

total_refunded_cents

The total refunded so far on this payment, all refunds included

fully_refunded

true once the whole payment has been refunded

reason

duplicate, fraudulent, requested_by_customer, other, or null

refunded_at

When the refund went through

Typical uses: record the refund in your accounting tool, update the balance due in your CRM.

Good to know: rely on total_refunded_cents to know where a payment stands. It stays correct even if your tool missed an earlier refund event.

booking.fully_paid: an alternative is fully paid

Sent when the payments collected on an alternative reach its total amount.

What's inside:

Field

What it is

booking

The booking: its ID, its reference, its Ezus project ID

alternative

The alternative that is now fully paid

total_amount_cents, currency

The alternative's total amount

Typical uses: confirm the trip, trigger the final documents, celebrate in your team's Slack channel.

Good to know: it arrives together with the payment.succeeded of the payment that completed the balance. If you already process every payment.succeeded, you may not need this one.

The test event: ping

When you click Send a test on a webhook's page, your tool receives a ping event. It contains no business data and is never sent otherwise. Your tool should simply answer 200 to it. See Set up a webhook.

Rules that apply to every event

  • Amounts are in cents, as integers: 150000 means €1,500.00.

  • Some fields can be null. For example, ezus_project_id and reference are empty on a booking created outside Ezus.

  • The same message can arrive twice. Store each message's id and ignore one you've already processed.

  • Messages can arrive out of order. A message sent again after a failure arrives after newer ones. Rely on dates and totals rather than on arrival order.

  • The list of events and fields will grow. Your tool should ignore fields it doesn't know, and answer 200 to an event type it doesn't handle.

See what was actually sent

Every message sent to your webhook is kept for 30 days. On the webhook's page, click a line in the Delivery log to see the exact content sent and how your tool answered.

Did this answer your question?