Skip to main content
Ringup posts an event to your server when a Checkout Session is paid. This is how your system learns, out of band, that an order was paid, without waiting on the call. It is the same across every platform, so you write one handler.

The envelope

Every event has the same shape:
{
  "id": "evt_9f2",               // unique per event; use it to dedupe
  "type": "checkout.completed",  // see the catalog below
  "created": "2026-07-07T18:05:12Z",
  "data": { ... }                // shape depends on type
}

Event catalog

TypeWhen it fires
checkout.completedA Checkout Session was paid (on a saved card, or a first-time caller paid via the texted link).
checkout.failedThe charge was declined or errored.
merchant.connectedA merchant finished connecting their payment processor to Ringup.
The catalog is deliberately small. A first-time link payment is a checkout.completed with method: "link", not a separate event.

checkout.completed

The one you build against. Its data is the completed Checkout Session with its Payment, and it carries everything you need to mark an order paid:
"data": {
  "checkout_id": "cs_x9f2",      // the Checkout Session
  "payment_id": "pay_abc",       // the Payment
  "method": "saved_card",        // or "link"
  "amount_cents": 1900,
  "currency": "USD",
  "order_id": "sq_abc",          // the order id you passed, echoed; null if you passed none
  "confirmation": "2PJJZY",
  "caller": "+14155551234",      // the recognized caller's number
  "merchant": "mch_x",
  "card": { "brand": "VISA", "last_4": "5858" },
  "call_id": "..."               // the platform call id, to match back to the call
}
Match on the order_id you passed (or the call_id), store the payment_id, and flip your order to paid. checkout.failed carries the same fields plus a reason.

Statuses

A Payment moves through a small set of states:
  • succeeded: the charge settled. This is the only status that means money moved.
  • failed: the charge was declined or errored; nothing was charged.
  • pending: a first-time caller was texted a link and has not paid yet. You see this only if you poll a Checkout Session during a call; a pending Payment does not send a checkout.completed webhook until it settles.

Getting it right

Three rules cover almost every webhook bug:
  1. Verify the signature. Each request carries a Ringup-Signature header, an HMAC of the raw body plus a timestamp. Recompute it with your endpoint’s signing secret and reject a mismatch or an old timestamp, so nobody can forge an event.
  2. Dedupe on id. Delivery is at-least-once: Ringup retries on any non-2xx response, so the same event can arrive more than once. Treat a repeated id as a no-op.
  3. Acknowledge fast. Return 2xx immediately and do your work asynchronously. Ringup retries with backoff and parks a persistently failing endpoint after several attempts.

Registering an endpoint

Add your endpoint URL in the Ringup dashboard (or via the API). Ringup shows the signing secret once at creation. You can register more than one endpoint, and test mode delivers to its own.
Signature scheme. Ringup-Signature is an HMAC-SHA256 over the timestamp and the raw request body, signed with your endpoint’s secret. When you register an endpoint the dashboard shows the exact recipe and a copy-paste verification snippet next to your signing secret.

Next steps

Testing

Trigger real events against the sandbox and watch them arrive.

API reference

Every field of every tool, with a live playground.