Skip to content
Last updated

Event ordering

Reconcile the order of webhook events.


Webhook event notifications may not arrive in the order they occur due to retries, network conditions, or internal processing.

To determine the correct chronological order of events, use the ordering fields available within each event.

Event payloads can have two types of ordering fields:

  • Timestamp: Most event types include a timestamp field that represents when the business event actually happened. Use this to determine the chronological order of events across a resource.
  • Sequence ID: Some event types include a monotonically increasing sequence field you can use to reconcile the order of events within a single transaction.
    • A higher value always means a later event.
    • Values may not be contiguous and a gap between numbers does not indicate a missing event.
    • Currently, balances#update is the only event type that supports this (available in v3.0.0+). See Sequence IDs for more details.

A few event types do not yet have a dedicated ordering field. See the Ordering fields by event type table for full details. We aim to improve ordering support across event types over time.

The sent_at field reflects when the notification was dispatched, which may differ from when the event occurred, especially during retries.

For event types that do not yet have a dedicated ordering field, you can use sent_at as a best-effort fallback, depending on your requirements.

How to reconcile event order

If your application processes events in the order they are received, it may end up with an inaccurate view of the current state of a resource. To avoid this, compare the ordering fields of incoming events to determine when each event actually occurred.

Example payloads

In this example, use data.occurred_at (2024-06-15T10:30:00.123Z) to determine event order, rather than sent_at.

{
  "data": {
    "resource": { "type": "transfer", "id": 111 },
    "current_state": "processing",
    "previous_state": "incoming_payment_waiting",
    "occurred_at": "2024-06-15T10:30:00.123Z"
  },
  "subscription_id": "01234567-89ab-cdef-0123-456789abcdef",
  "event_type": "transfers#state-change",
  "schema_version": "4.0.0",
  "sent_at": "2024-06-15T10:30:01.456Z"
}

See the Ordering fields by event type table for a list of which ordering fields each event type uses.

Timestamp precision

Historically, webhook timestamps used second-level precision, and formatting was not always consistent across event types. From schema version 4.0.0, all timestamps use a consistent ISO 8601 format with millisecond precision:

2024-06-15T10:30:00.123Z

This makes it possible to correctly order events that occur within the same second. If using an older schema version, timestamps may only have second-level precision (for example, 2024-06-15T10:30:00Z), which can make it impossible to distinguish the order of rapid successive events.

For consistent millisecond-precision timestamps, use schema version 4.0.0 or later when creating subscriptions.

Ordering fields by event type

This table lists the ordering field available for each event type. Use this field to reconcile event order.

Event typeTimestamp ordering fieldSequence fieldNotes
transfers#state-changedata.occurred_at
transfers#active-casesCurrently no ordering field available.
account-details-payment#state-changedata.occurred_at
balances#creditdata.occurred_at
balances#updatedata.occurred_atdata.step_idUse occurred_at across transactions, step_id within a transaction. Available in v3.0.0+.
balances#account-state-changedata.occurred_at
profiles#verification-state-changedata.occurred_at
batch-payment-initiations#state-changedata.occurred_at
transfers#payout-failuredata.occurred_at
transfers#refunddata.occurred_at
swift-in#creditdata.occurred_at
swift#message-receiveddata.occurred_at
cards#transaction-state-changedata.occurred_at
profiles#cdd-check-state-changedata.occurred_at
cards#card-status-changedata.occurred_at
cards#card-order-status-changedata.occurred_at
cards#card-production-status-changedata.occurred_at
partner-support#case-changeddata.occurred_at
transaction-disputes#updatedata.occurred_at
bulk-settlement#payment-receiveddata.occurred_at
users#state-changedata.occurred_at
kyc-reviews#state-changedata.resource.updatedAtUses camelCase.
cards#3ds-challengedata.occurred_at
profiles#overdraft-limit-thresholdCurrently no ordering field available.
account-details-order#order-state-changedata.modification_time
profiles#state-changedata.occurred_at
hold-limit-breach#updatedata.occurred_at
  1. Subscribe using schema version 4.0.0 or later for consistent millisecond-precision timestamps across all event types.
  2. Use each event type's ordering field(s) (as listed in the Ordering fields by event type table) to determine the true sequence of events.
    • Compare timestamp values to sort events chronologically.
    • For balances#update, consider also using data.step_id to reconcile the order of events within a single transaction. See the Sequence IDs section for more details.
  3. Handle out-of-order delivery. Events may not arrive in chronological order. Your application should use the ordering fields to reconstruct the correct sequence (for example, by reordering events before processing, updating state only when a newer event is received, or any approach that suits your use case).

Sequence IDs

Some event types include a monotonically increasing sequence field that tracks the order of events. Where available, use this field to reliably determine the order of events regardless of timestamp values. A higher value means the event occurred later in the sequence.

Sequence IDs are not guaranteed to be contiguous and there may be gaps between values. A gap does not indicate a missing event. They should only be used to determine relative order.

OrderSequence ID
1st100
2nd101
3rd105

See the Ordering fields by event type table for which event types support a sequence field.