Payload envelope
The canonical JSON envelope and its identity invariants.
Every notification webhook carries a single JSON object with a fixed top-level shape. The envelope is identical across all notification classes; only the value of notification_class and the two conditional fields (finality_outcome, hold_reason) vary.
Canonical shape
{
"delivery_record_id": "string",
"payment_intent_id": "string",
"merchant_id": "string",
"notification_class": "payment_observed | payment_finalized | payment_held | duplicate_payment_incident",
"attempt_id": "string | null",
"chain_id": "string | null",
"finality_outcome": "paid | refunded | failed | null",
"hold_reason": "sanctions | kyt_timeout | null"
}Field rules
| Field | Type | Rules |
|---|---|---|
delivery_record_id | string | Identifies the semantic notification record being delivered. This is the deduplication anchor: every retry and every manual re-delivery of the same notification carries the same delivery_record_id. |
payment_intent_id | string | The canonical merchant-facing correlation key. Always present. Correlate the notification to a payment in your system by this value. |
merchant_id | string | The merchant the notification is addressed to. Always present. |
notification_class | enum | One of payment_observed, payment_finalized, payment_held, duplicate_payment_incident. Always present. Determines how the conditional fields below are populated. |
attempt_id | string | null | An upstream payment-attempt reference for traceability only. MUST NOT be interpreted as the identity of this notification record. null when not available. |
chain_id | string | null | Numeric chain identifier encoded as a string, when available; otherwise null. |
finality_outcome | enum | null | Present (non-null) only when notification_class is payment_finalized. One of paid, refunded, failed. null for every other class. |
hold_reason | enum | null | Present (non-null) only when notification_class is payment_held. One of sanctions, kyt_timeout. null for every other class. |
finality_outcome values in plain terms
| Value | What it means |
|---|---|
paid | Payment confirmed on-chain and reconciled — the deposit was swept to the merchant's approved destination, and the protocol fee was collected on-chain. |
refunded | The merchant returned a reject decision with a refund destination; the deposit was returned to the customer, and the protocol fee was collected on-chain. |
failed | The payment reached a terminal failure state without settlement or merchant-directed refund. No funds moved. |
A fourth value, duplicate_payment, is not carried on a payment_finalized notification — a duplicate is surfaced as its own duplicate_payment_incident notification, and on the API status projection. See Statuses and projections.
Conditional-field invariants
finality_outcomeMUST be non-nullif and only ifnotification_classispayment_finalized.hold_reasonMUST be non-nullif and only ifnotification_classispayment_held.- For
payment_observedandduplicate_payment_incident, bothfinality_outcomeandhold_reasonMUST benull.
The full per-class field matrix is tabulated in the Event catalog.
Notification identity
A notification record is identified by the triple (endpoint, payment_intent_id, notification_class). Exactly one record exists per registered endpoint, per payment intent, per class. Consequences:
- A single
payment_intent_idMAY produce several records — for example apayment_heldrecord and a laterpayment_finalizedrecord, or apayment_finalizedrecord alongside aduplicate_payment_incidentrecord. Each is a distinct envelope with its owndelivery_record_id. - All deliveries of one record — first send, automatic retries, manual re-delivery — share one
delivery_record_id. This is the value to deduplicate on.
The notification is not the truth
A webhook is a side effect of a payment event, not the authoritative record of it. The envelope reflects state at the moment the notification was constructed. Reconcile against canonical state through the API and the signed Proof-of-Payment artifact — never treat envelope receipt, or its absence, as settlement truth. See Delivery semantics.
Signature
The raw bytes of this JSON body are the message signed by the notification HMAC. Verify the signature against the exact bytes received, before parsing — see Signature verification.