Webhook events
Webhook event types
Every event type matches its status transition and carries the same payload shape ({ event_id, event_type, created_at, livemode, data: { invoice } }).
| Event type | Fired when | May fire more than once? |
|---|---|---|
invoice.created | New invoice via POST /v1/invoices | no |
invoice.payment_seen | Payment in mempool, 0 conf | no, once per invoice |
invoice.paid | Cumulative confirmed amount within tolerance, before expiry | no |
invoice.late_paid | Cumulative confirmed amount within tolerance, after expiry but inside grace | no |
invoice.expired | pending invoice's expires_at elapsed with no payment. Note: seen invoices never receive invoice.expired; if you got invoice.payment_seen, wait for the next event. | no |
invoice.underpaid | Cumulative confirmed amount below amount_sats − tolerance. Fires on every short payment (top-ups can produce multiple). | yes |
invoice.overpaid | Cumulative confirmed amount exceeds amount_sats + tolerance | no |
invoice.payment_reverted | A reorg orphaned the block containing the payment. Reverse fulfillment if you already shipped. Rare. | very rare |
invoice.requires_review | Routing produced "no-match" or cross-check disagreed | rare |
invoice.cancelled | Vendor or admin cancelled | no |
invoice.grace_ending | Opt-in: fires once near the end of the late-payment grace window (endpoint must enable graceEndingEnabled) | no |
invoice.reopened | Invoice reopened after a prior terminal-ish state (lifecycle edge case) | rare |
Always deduplicate by
event_id, never byinvoice.id+event_type. Top-ups produce repeatedinvoice.underpaidevents, and dispatcher retries reuse the sameevent_id.