# Event reference (/en/integrate/webhooks/event-reference)

Every webhook event XPay can deliver, when it fires, and the object it carries.

Events are how XPay tells your server something happened: a payment succeeded, a refund landed, a customer was created. Every event delivered to your endpoint shares the same JSON envelope; what changes is the value of `type` and the resource carried inside `data.object`.

This page is the canonical list of events you can subscribe to. For the click path to subscribe, see [Setting up an endpoint](/integrate/webhooks/setting-up-an-endpoint). For the verifier code, see [Verifying signatures](/integrate/webhooks/verifying-signatures).

## The event envelope [#the-event-envelope]

Every webhook delivery has the same top-level shape:

```json
{
  "id": "evt_test_AbC123...",
  "object": "event",
  "api_version": "3.1.0",
  "created": "2026-05-01T12:00:00.000Z",
  "type": "checkout.session.completed",
  "livemode": false,
  "data": {
    "object": { "id": "cs_test_...", "object": "checkout.session" }
  }
}
```

| Field         | Type            | Meaning                                                                                                                                                                                                                       |
| ------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`          | string          | Event ID, prefixed `evt_test_*` or `evt_live_*`. Stable across retries. Use as your idempotency key.                                                                                                                          |
| `object`      | string          | Always `"event"`.                                                                                                                                                                                                             |
| `api_version` | string          | API version used to render `data.object`.                                                                                                                                                                                     |
| `created`     | ISO 8601 string | When the event was first recorded.                                                                                                                                                                                            |
| `type`        | string          | What kind of event (see the tables below).                                                                                                                                                                                    |
| `livemode`    | boolean         | `true` for live-mode events, `false` for test-mode.                                                                                                                                                                           |
| `data.object` | object          | The resource the event is about. Identical shape to `GET /<resource>/:id`.                                                                                                                                                    |
| `request`     | object \| null  | The API request that created the event: `{ id, idempotency_key }`. `request.idempotency_key` is the `Idempotency-Key` you sent on that request (see [Idempotency](/integrate/idempotency)), or `null` if you didn't send one. |

The `data.object` is identical to what the corresponding GET endpoint returns. If you can read a Checkout Session from `GET /checkout/sessions/:id`, your `checkout.session.completed` handler can read the same fields by the same names off `data.object`.

## Checkout Session events [#checkout-session-events]

Fire on the lifecycle of a <ApiLink href="/api-reference/objects/checkout-session">Checkout Session</ApiLink>. These are the events most integrations key off.

| Event                        | Fires when                                                                              | `data.object`                                                                      |
| ---------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `checkout.session.completed` | A customer's payment on the session succeeds. The session's `status` is now `complete`. | <ApiLink href="/api-reference/objects/checkout-session">Checkout Session</ApiLink> |

Subscribe to `checkout.session.completed` to fulfill the order. The payload carries the resolved `paymentIntent`, `customer`, and `lineItems`, so a single event is enough for most fulfillment logic. `lineItems` lists only what the customer bought; optional add-ons they didn't add aren't included.

## Charge events [#charge-events]

Fire on the lifecycle of a <ApiLink href="/api-reference/objects/charge">Charge</ApiLink>. A Charge is one attempt to move money on the customer's payment method. A successful payment produces one Charge; a customer who retried after a decline produces several.

| Event              | Fires when                                                                                                                                                                         | `data.object`                                                  |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `charge.succeeded` | A Charge captures successfully. Fires before `checkout.session.completed` for hosted-checkout payments.                                                                            | <ApiLink href="/api-reference/objects/charge">Charge</ApiLink> |
| `charge.failed`    | A Charge attempt fails. Carries `failureCode` and `failureMessage` describing why.                                                                                                 | <ApiLink href="/api-reference/objects/charge">Charge</ApiLink> |
| `charge.refunded`  | A successful Refund (full or partial) is applied to the Charge. The refunded amount is in `amountRefunded`. The Charge's `refunds.data` array carries every Refund applied so far. | <ApiLink href="/api-reference/objects/charge">Charge</ApiLink> |

Use `charge.*` events when your data model tracks money at the charge level (e.g. multiple captures, retried payments). For most integrations, listening to `checkout.session.completed` and `refund.*` is enough.

## Refund events [#refund-events]

Fire on the lifecycle of a <ApiLink href="/api-reference/objects/refund">Refund</ApiLink>.

| Event            | Fires when                                                 | `data.object`                                                  |
| ---------------- | ---------------------------------------------------------- | -------------------------------------------------------------- |
| `refund.created` | A new Refund record is created.                            | <ApiLink href="/api-reference/objects/refund">Refund</ApiLink> |
| `refund.failed`  | A Refund attempt fails. The Refund's `status` is `failed`. | <ApiLink href="/api-reference/objects/refund">Refund</ApiLink> |

Both API-issued and dashboard-issued refunds emit these. See [Refunds](/integrate/refunds) for the API path.

`charge.refunded` (above) carries the parent Charge with the new Refund nested inside `refunds.data`, so you can key fulfillment off either side depending on which entity your data model tracks.

## Customer events [#customer-events]

Fire on the lifecycle of a <ApiLink href="/api-reference/objects/customer">Customer</ApiLink>.

| Event              | Fires when                                                   | `data.object`                                                      |
| ------------------ | ------------------------------------------------------------ | ------------------------------------------------------------------ |
| `customer.created` | A Customer record is created (via the API or the dashboard). | <ApiLink href="/api-reference/objects/customer">Customer</ApiLink> |
| `customer.updated` | A Customer record's properties change.                       | <ApiLink href="/api-reference/objects/customer">Customer</ApiLink> |
| `customer.deleted` | A Customer record is deleted.                                | <ApiLink href="/api-reference/objects/customer">Customer</ApiLink> |

For the difference between Registered and Guest customers, and how guest matching works, see [Customer lifecycle](/integrate/checkout-session/customer-lifecycle).

## Webhook monitoring [#webhook-monitoring]

When a delivery exhausts all retries, XPay emails your account's owners, admins, and developers. The email names the endpoint URL and the event type that couldn't be delivered, so you know which integration to look at. These alerts cover Live mode deliveries only. Failures come by email rather than a webhook on purpose, since a webhook to a broken endpoint would just fail too.

For the retry schedule behind that alert, and how to replay a delivery once you've fixed the handler, see [Replaying & retries](/integrate/webhooks/replaying-and-retries).

## Order on a successful payment [#order-on-a-successful-payment]

A single payment fires more than one event. They arrive close together but not necessarily in order; treat each event as independent and dedup on `event.id`.

| Order | Event                        | Why                                                 |
| ----- | ---------------------------- | --------------------------------------------------- |
| 1     | `charge.succeeded`           | The money moved.                                    |
| 2     | `checkout.session.completed` | The session is now `complete`. Fulfillment trigger. |

For a refund:

| Order | Event             | Why                                               |
| ----- | ----------------- | ------------------------------------------------- |
| 1     | `refund.created`  | A Refund record was created.                      |
| 2     | `charge.refunded` | The parent Charge's `amountRefunded` was updated. |

You don't need to listen to all of them. Pick the events your data model needs and ignore the rest.

## Reading `data.object` [#reading-dataobject]

The `data.object` field is the same shape you'd get from the matching GET endpoint. Don't reach for the API to re-fetch a resource right after receiving its event; the payload already has everything that endpoint would return.

For the canonical shape of each resource, see the API Reference Object pages:

* <ApiLink href="/api-reference/objects/checkout-session">
    Checkout Session
  </ApiLink>
* <ApiLink href="/api-reference/objects/payment-intent">
    Payment Intent
  </ApiLink>
* <ApiLink href="/api-reference/objects/charge">
    Charge
  </ApiLink>
* <ApiLink href="/api-reference/objects/refund">
    Refund
  </ApiLink>
* <ApiLink href="/api-reference/objects/customer">
    Customer
  </ApiLink>

For how those resources fit together (and which IDs to keep on your order record), see [Object model](/integrate/object-model).

## Idempotency reminder [#idempotency-reminder]

Every event can be delivered more than once: XPay retries on non-2xx, you may manually replay from the Workbench, and a successful response that didn't make it back to XPay produces a duplicate. Use `event.id` as the dedup key in your handler.

For the full pattern, see [Verifying signatures → Idempotency](/integrate/webhooks/verifying-signatures#idempotency).

## Where to next [#where-to-next]

<Cards>
  <Card icon="<Cable />" title="Setting up an endpoint" href="/integrate/webhooks/setting-up-an-endpoint">
    Add an endpoint, pick events, copy the signing secret.
  </Card>

  <Card icon="<ShieldCheck />" title="Verifying signatures" href="/integrate/webhooks/verifying-signatures">
    The HMAC-SHA256 verifier and idempotency pattern.
  </Card>

  <Card icon="<RefreshCw />" title="Replaying and retries" href="/integrate/webhooks/replaying-and-retries">
    Automatic retry schedule, manual resends, and the lifecycle of a delivery.
  </Card>

  <Card icon="<Terminal />" title="Local development" href="/integrate/webhooks/local-development">
    Tunnel deliveries to your laptop while you build the handler.
  </Card>

  <Card icon="<Network />" title="Object model" href="/integrate/object-model">
    How Checkout Session, Payment Intent, Charge, Refund, and Customer relate.
  </Card>
</Cards>