> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spherepay.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Event Payloads

> The structure of every webhook payload — the envelope fields, how the sequence field orders events, and how applicationId scopes each event.

Every webhook delivery carries a JSON body with the same envelope structure, regardless of event type. This page explains each field and the two most important ones to get right in your integration: `sequence` and `applicationId`.

## The envelope

```json theme={"dark"}
{
  "id": "event_01HXPA3M9R7DEF456",
  "type": "transfer.succeeded",
  "apiVersion": "v2",
  "originalCreateDate": "2026-08-07T14:05:30.000Z",
  "livemode": true,
  "sequence": 5,
  "data": {
    "id": "payout_d243ab2b1de4447d8a046d87fefe58cf",
    "type": "transfer",
    "applicationId": "application_1324354657",
    "customerId": "customer_f31121c389624d3697cbf3ea8830b7a4",
    "transferType": "oneTimeTransfer",
    "previousStatus": "processing",
    "previousStatusAt": "2026-08-07T14:02:15.000Z",
    "status": "succeeded",
    "statusAt": "2026-08-07T14:05:30.000Z",
    "cause": null
  }
}
```

| Field                | Description                                                                                                                                                                                                                                                                      |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                 | Unique event identifier (`event_` prefix). Stable across every delivery and replay of the same event — use it as your idempotency key.                                                                                                                                           |
| `type`               | The event name from the [event catalog](/concepts/webhooks/event-catalog), e.g. `customer.approved`.                                                                                                                                                                             |
| `apiVersion`         | The version of the event schema (currently `v2`). This is distinct from the dated `apiVersion` you set on the webhook endpoint.                                                                                                                                                  |
| `originalCreateDate` | ISO 8601 timestamp of when the resource state change that triggered this event originally occurred. Never changes, even on replays — unlike the `Sphere-Timestamp` header, which reflects when each delivery attempt was sent.                                                   |
| `livemode`           | `true` for production events, `false` for test-mode events.                                                                                                                                                                                                                      |
| `sequence`           | Monotonically increasing integer used to order events for a resource. See [below](#the-sequence-field).                                                                                                                                                                          |
| `data`               | The subject of the event: the resource's ID, its `applicationId`, the previous status, the new status, and a `cause` field reserved for failure reasons (currently always `null`). Fields vary slightly by resource — see the [event catalog](/concepts/webhooks/event-catalog). |

The body is byte-for-byte identical across every delivery and replay of the same event. Anything that varies per delivery attempt — the delivery ID, attempt count, timestamp, signature — lives in the [HTTP headers](/concepts/webhooks/verifying-signatures#delivery-headers) instead.

## Events, deliveries, and attempts

Three distinct records stand behind every webhook that reaches you, and each carries its own identifier and its own timestamps. Keeping them straight is the key to reconciling the various timestamps and the `sequence` logic below.

| Record               | What it is                                                                                                   | Identifier                                                                                            | Timestamps                                                                                                                         | Where you see it                                                                              |
| -------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| **Event**            | The immutable fact that something happened. One per occurrence, shared by every endpoint that receives it.   | `id` in the body (`event_...`)                                                                        | `originalCreateDate` — when the resource state change that triggered the event originally occurred. Never changes, even on replay. | The payload body; [`GET /v2/events`](/concepts/webhooks/events-and-replays)                   |
| **Event delivery**   | The routing of one event to one endpoint. Two subscribed endpoints produce two deliveries of the same event. | `Sphere-Delivery-Id` header (`eventDelivery_...`)                                                     | `deliveredAt` — when your endpoint acknowledged with a `2xx`.                                                                      | The headers; the `delivery` object in the Events API                                          |
| **Delivery attempt** | A single HTTP request to your endpoint — the original send, or a manual replay.                              | `Sphere-Delivery-Attempt` counter and `Sphere-Delivery-Type` header; `attempts[]` in the event detail | `Sphere-Timestamp` header / `attemptedAt` — when this particular request was sent. Fresh on every attempt.                         | The headers; [`GET /v2/events/{id}`](/concepts/webhooks/events-and-replays#retrieve-an-event) |

The split follows one rule: anything intrinsic to the **event** — `id`, `type`, `sequence`, `originalCreateDate`, `data` — lives in the body and never changes; anything describing **this particular HTTP request** — delivery ID, attempt number, attempt type, timestamp, signature — lives in the headers and is regenerated per attempt.

<Warning>
  When ordering business state, trust the event, never the attempt. A replay of an old event arrives with a fresh `Sphere-Timestamp` (so it passes signature tolerance checks), but its `sequence` and `originalCreateDate` still reflect when the state change actually occurred. Ordering by `Sphere-Timestamp` would make a replayed old event look newer than events that superseded it — ordering by `sequence` keeps replays harmless.
</Warning>

## Payloads carry the state change, not the full resource

Webhook payloads deliberately contain the minimum you need to react: the resource ID, and the transition from `previousStatus` to `status`. They do **not** contain the full resource object.

Most reactions — routing on the new status, updating your own database row, notifying your operations team — work from the payload alone. When you need the complete resource (full verification criteria, bank account details, transfer amounts), fetch it from the resource's `GET` endpoint:

```bash theme={"dark"}
# The payload told you payout_d243... succeeded; fetch the full object:
curl https://api.spherepay.co/v2/transfer/payout_d243ab2b1de4447d8a046d87fefe58cf \
  -H "Authorization: Bearer {{api_key}}"
```

<Tip>
  Because the payload is minimal and immutable, a replayed event never carries stale data pretending to be fresh — it describes a transition that happened at `originalCreateDate`. If you always `GET` the resource for its current state before acting on side effects, replays are harmless.
</Tip>

## The `sequence` field

`sequence` is a monotonically increasing integer **scoped to a single resource instance** — one counter per customer, one per transfer, and so on, keyed by `data.id`. It is assigned when the event is created and never changes, so it reflects the true order in which state changes occurred even when deliveries arrive out of order.

Because delivery order is not guaranteed, use `sequence` to guard against stale updates:

1. Store the highest `sequence` you have processed **per resource instance** — for example, a `lastSphereSequence` column on the customer row in your own database.
2. When an event arrives, compare its `sequence` to the stored value for that `data.id`.
3. If `incoming.sequence <= stored.sequence`, the event is stale or a duplicate — acknowledge with a `2xx` and skip the update.
4. Otherwise, apply the update and store the new `sequence`.

You do not need a global counter across your account — just per-entity tracking, colocated with the resource state you already maintain.

<Note>
  Do not assume sequence values you receive are contiguous. If your endpoint subscribes to a subset of a resource's events, you will observe gaps — use the `<=` staleness comparison above, never gap detection.
</Note>

### Why both `sequence` and `originalCreateDate`?

They answer different questions. `originalCreateDate` tells you **when** a state change happened — wall-clock time, useful for display, audit trails, and time-window queries. But wall-clock time is an unreliable *ordering* key: two rapid transitions on the same resource can land close enough together that their timestamps collide, and a timestamp comparison has no way to break the tie. `sequence` exists to make ordering unambiguous — a strictly increasing integer per resource instance, so any two events for the same resource always have a definite order, no matter how close together they occurred.

Rule of thumb: **order and deduplicate by `sequence`; display and audit by `originalCreateDate`.**

## The `applicationId` field

Every payload includes `data.applicationId` — the SpherePay application the event belongs to. Webhook endpoints are registered per application, and events are only ever delivered for resources owned by the endpoint's application.

This matters most when you operate **multiple SpherePay applications that share one webhook receiver URL**. Registering the same URL under two applications is perfectly valid — but your handler then receives events from both, and each application's endpoint has its own signing secret. Use `applicationId` to route each event to the right application context in your system (and to select the right secret when verifying signatures).

<Tip>
  You can find each application's ID on the **Settings** page of the SpherePay integrator dashboard.
</Tip>
