Checkout webhook events

Session lifecycle, payments, refunds, and risk holds.

Checkout webhooks use the same Svix application as custodial events. See Checkout links & sessions for the payment flow.

Two payload shapes

Most checkout events use an envelope built at send time:

{
"id": "evt_a1b2c3d4e5f6…",
"type": "checkout.payment.completed",
"created_at": "2026-01-01T12:00:00.000Z",
"data": { }
}

The data object always includes the fields below (plus event-specific extras). Amounts are human token units (same as the checkout session), not wei or lamports.

FieldDescription
checkout_idCheckout UUID
session_idSession UUID
idempotency_keyMerchant idempotency key from checkout create
line_itemsCart snapshot (product_id, sku, quantity, USD prices)
chainNetwork (for example ETHEREUM, SOLANA)
tokenToken symbol (for example USDC, ETH)
amount_requiredRequired crypto amount for this session
amount_receivedConfirmed deposit total
overpayment_amountamount_received − amount_required, or "0"
tx_hashesConfirmed deposit transaction hashes
metadataCheckout metadata object from create
risk_flagSession risk metadata, or null

Risk / quarantine events use a flat payload (no envelope):

{
"eventType": "checkout.flagged",
"eventId": "evt_checkout_checkout.flagged_session-uuid",
"checkoutId": "checkout-uuid",
"sessionId": "session-uuid",
"status": "FLAGGED_PENDING_REVIEW",
"idempotency_key": "your-idempotency-key",
"riskFlag": { }
}

Event catalog

Event typeShapeWhen
checkout.payment.completedEnvelopeSession reaches PAID (first time).
checkout.payment.overpaidEnvelopeSession reaches OVERPAID (first time).
checkout.wrong_token_receivedEnvelopeWrong token sent to the deposit address.
checkout.refund.initiatedEnvelopeRefund worker starts (REFUNDING).
checkout.refund.completedEnvelopeRefund transaction confirmed.
checkout.refund.failedEnvelopeRefund could not complete.
checkout.flagged.acceptedEnvelopeMerchant accepted a flagged session.
checkout.flagged.rejectedEnvelopeMerchant rejected a flagged session (refund queued).
checkout.flaggedFlatCompany-tier risk hold (FLAGGED_PENDING_REVIEW).
checkout.flagged.platform_heldFlatPlatform-tier sanctions hold.
checkout.flagged.auto_returnedFlatQuarantine window expired; auto-return queued.

Several checkout.* types exist in the API enum for future lifecycle hooks (for example checkout.session.created, checkout.payment.detected) but are not emitted today. Subscribe only to the types above unless release notes say otherwise.

Test events

Send a sample of any event in the catalog above from Dashboard → Webhooks → Send test event. Test payloads have the real shape and placeholder data, and they don’t create sessions or move funds. They carry a test flag:

  • Envelope events get "is_test": true at the top level, next to id and type.
  • Flat risk events (checkout.flagged, checkout.flagged.platform_held, checkout.flagged.auto_returned) get "isTest": true.

Real events never include the flag, so a missing flag means the event is real.


Shared data example

The following data block is representative for envelope events (payment completed shown; other events reuse the same core fields):

{
"id": "evt_a1b2c3d4e5f6789012345678901234567890",
"type": "checkout.payment.completed",
"created_at": "2026-01-01T12:00:00.000Z",
"data": {
"checkout_id": "3f1a7c2e-9b4d-4a61-8f0c-2d5e7a9b1c34",
"session_id": "8b2c9d1e-4f5a-6b7c-8d9e-0f1a2b3c4d5e",
"idempotency_key": "order-2026-001",
"line_items": [
{
"product_id": "prod-1",
"sku": "SKU-A",
"quantity": 2,
"unit_price_usd": "10.00",
"line_total_usd": "20.00"
}
],
"chain": "ETHEREUM",
"token": "USDC",
"amount_required": "20.50",
"amount_received": "20.50",
"overpayment_amount": "0",
"tx_hashes": ["0xabc…"],
"metadata": { "orderId": "order-2026-001" },
"risk_flag": null,
"platform_fee": "0.21",
"net_credited": "20.29"
}
}

platform_fee and net_credited appear only on checkout.payment.completed and checkout.payment.overpaid. They are estimated from org checkout config at payment time (fee is accrued to the monthly platform bill, not deducted on-chain from the sweep).


checkout.payment.overpaid

Same envelope as completed. type is checkout.payment.overpaid and overpayment_amount is greater than "0".


checkout.wrong_token_received

Same core data fields, plus:

"token_expected": "USDC",
"token_received": "USDT"

checkout.refund.initiated

Extra fields merged into data:

"checkoutId": "3f1a7c2e-9b4d-4a61-8f0c-2d5e7a9b1c34",
"status": "REFUNDING"

(checkoutId duplicates checkout_id — both are present in the payload today.)


checkout.refund.completed

"checkoutId": "3f1a7c2e-9b4d-4a61-8f0c-2d5e7a9b1c34",
"txHash": "0xrefund…"

checkout.refund.failed

"checkoutId": "3f1a7c2e-9b4d-4a61-8f0c-2d5e7a9b1c34",
"reason": "Insufficient gas on deposit address"

checkout.flagged.accepted / checkout.flagged.rejected

Standard envelope and shared data fields. type is checkout.flagged.accepted or checkout.flagged.rejected. Settlement or refund is queued after the webhook.


Flat risk events

checkout.flagged (merchant review):

{
"eventType": "checkout.flagged",
"eventId": "evt_checkout_checkout.flagged_8b2c9d1e-4f5a-6b7c-8d9e-0f1a2b3c4d5e",
"checkoutId": "3f1a7c2e-9b4d-4a61-8f0c-2d5e7a9b1c34",
"sessionId": "8b2c9d1e-4f5a-6b7c-8d9e-0f1a2b3c4d5e",
"status": "FLAGGED_PENDING_REVIEW",
"idempotency_key": "order-2026-001",
"riskFlag": {
"tier": "company",
"score": 25,
"severity": "HIGH_RISK",
"entityType": "MIXER",
"entityName": "Example Mixer",
"flaggedAt": "2026-01-01T12:00:00.000Z"
}
}

checkout.flagged.platform_held — same shape; status is FLAGGED_PLATFORM_HELD.

checkout.flagged.auto_returned — same shape; status is REFUNDING (session moves to refund after the quarantine deadline). The checkout row is marked FLAGGED_AUTO_RETURNED in the dashboard, but that value is not sent in this webhook.

riskFlag is the session’s stored risk metadata; fields vary by scan outcome.