Skip to main content
Crypto Checkout fires events at each stage of a payment’s lifecycle — from the moment a session is created to final confirmation, expiry, or edge-case outcomes like underpayment. Understanding which events to subscribe to and what data each one carries lets you build reliable, event-driven fulfillment flows without polling the API.

Common event envelope

Every event — regardless of type — shares the same top-level envelope structure. The data object contains the full checkout session at the time the event was generated.
{
  "id": "evt_abc123",
  "type": "session.paid",
  "livemode": true,
  "createdAt": "2024-01-15T10:30:00.000Z",
  "data": { ... }
}
FieldTypeDescription
idstringUnique event identifier (evt_…). Use this to deduplicate redeliveries.
typestringThe event type string (see table below)
livemodebooleantrue for live payments; false for test-mode events
createdAtstringISO 8601 timestamp when the event was created
dataobjectThe checkout session object at the time of the event

Event types

EventTrigger
session.createdA new checkout session is created
session.detectedPayment detected on-chain (pending required confirmations)
session.paidPayment confirmed with the required number of confirmations
session.paid_latePayment confirmed after session expiry, within the grace window
session.expiredSession TTL elapsed with no payment detected
session.underpaidPayment received but the amount was less than required
session.overpaidPayment received but the amount exceeded what was required
session.failedSession encountered a terminal error
Subscribe to "*" to receive all event types, including any new ones added in future releases. If you subscribe to specific types, you will need to update your endpoint when new event types are introduced.

Example payloads

session.paid

Fired when on-chain confirmations reach the required threshold. This is the primary event to trigger order fulfillment.
{
  "id": "evt_01HXYZ",
  "type": "session.paid",
  "livemode": true,
  "createdAt": "2024-01-15T10:35:00.000Z",
  "data": {
    "id": "sess_01HABC",
    "object": "checkout_session",
    "status": "paid",
    "address": "0xabc...",
    "amount": { "wei": "50000000000000000", "eth": "0.05" },
    "fiat": { "amount": "150.00", "currency": "USD" },
    "txHash": "0xdef...",
    "chainId": 1,
    "paidAt": "2024-01-15T10:34:55.000Z",
    "metadata": { "orderId": "ORD-123" }
  }
}

session.detected

Fired as soon as the on-chain transfer is seen, before confirmations are complete. Useful for showing a “payment detected, awaiting confirmation” state in your UI — do not fulfil orders based on this event alone.
{
  "id": "evt_01HDET",
  "type": "session.detected",
  "livemode": true,
  "createdAt": "2024-01-15T10:33:10.000Z",
  "data": {
    "id": "sess_01HABC",
    "object": "checkout_session",
    "status": "detected",
    "address": "0xabc...",
    "amount": { "wei": "50000000000000000", "eth": "0.05" },
    "fiat": { "amount": "150.00", "currency": "USD" },
    "txHash": "0xdef...",
    "chainId": 1,
    "paidAt": null,
    "metadata": { "orderId": "ORD-123" }
  }
}

session.paid_late

Fired when a payment arrives and confirms after the session’s TTL but within the late-payment grace window. The status is paid_late. You can choose to fulfil these orders or flag them for manual review.
{
  "id": "evt_01HLATE",
  "type": "session.paid_late",
  "livemode": true,
  "createdAt": "2024-01-15T10:42:00.000Z",
  "data": {
    "id": "sess_01HABC",
    "object": "checkout_session",
    "status": "paid_late",
    "address": "0xabc...",
    "amount": { "wei": "50000000000000000", "eth": "0.05" },
    "fiat": { "amount": "150.00", "currency": "USD" },
    "txHash": "0xdef...",
    "chainId": 1,
    "paidAt": "2024-01-15T10:41:50.000Z",
    "metadata": { "orderId": "ORD-123" }
  }
}

session.expired

Fired when the session’s TTL elapses and no payment has been detected. Use this to release any reserved inventory or notify the customer.
{
  "id": "evt_01HEXP",
  "type": "session.expired",
  "livemode": true,
  "createdAt": "2024-01-15T10:35:00.000Z",
  "data": {
    "id": "sess_01HABC",
    "object": "checkout_session",
    "status": "expired",
    "address": "0xabc...",
    "amount": { "wei": "50000000000000000", "eth": "0.05" },
    "fiat": { "amount": "150.00", "currency": "USD" },
    "txHash": null,
    "chainId": 1,
    "paidAt": null,
    "metadata": { "orderId": "ORD-123" }
  }
}

session.underpaid and session.overpaid

Fired when the confirmed on-chain amount differs from the session’s required amount. The data.amount reflects the actual amount received; data.fiat reflects the required fiat amount. These sessions require manual resolution — Crypto Checkout does not automatically issue refunds in v1.

The livemode field

Every event carries a livemode boolean:
  • true — the event was triggered by a real payment on a live network.
  • false — the event was generated in test mode or via the dashboard’s Test button.
Your handler should check livemode and route test events to your test order pipeline rather than triggering live fulfillment.

Idempotency and deduplication

Crypto Checkout guarantees at-least-once delivery. The same event may be delivered more than once if your endpoint fails to return a 2xx status code. Always store the id of processed events and skip any delivery whose id you have already handled:
const eventId = event.id; // e.g. "evt_01HXYZ"

if (await db.events.exists(eventId)) {
  return res.sendStatus(200); // already processed — acknowledge and skip
}

await db.events.markProcessed(eventId);
// ... fulfil the order

Viewing event history

Every fired event is persisted to the event log and queryable via the API, regardless of delivery success:
# List recent events
GET /api/v1/events

# Filter by type
GET /api/v1/events?type=session.paid

# Retrieve a specific event
GET /api/v1/events/evt_01HXYZ

Events API Reference

Full reference for the Events resource, including filtering, pagination, and response schemas