Skip to main content
Register an endpoint and verify an Ionic event, all in test mode with a sk_v1_test_… key.

1. Stand up a receiver

A webhook endpoint is an HTTPS URL that accepts a POST. Start with a handler that captures the raw body (the signature covers the exact bytes, so a body that’s been parsed and re-serialized by framework middleware will not verify) and applies the delivery acknowledgment contract. On Node, webhooks.unwrap() from the server SDK checks the signature and replay window, then returns a typed event:
The receiver supplies these inputs to the SDK:
An invalid signature or replay-window check throws WebhookVerificationError. An authenticated payload that is not a valid event envelope throws WebhookParseError. Your receiver must reject unverified deliveries. A 2xx acknowledges acceptance; Idempotency & retries defines the persistence and redelivery contract. Not on Node? The signature scheme is standard HMAC-SHA256 over {id}.{timestamp}.{body} — Verify signatures has the full manual implementation in Node, Python, and Go.
Developing locally? Endpoints must be HTTPS, so localhost alone won’t work. Put an HTTPS tunnel in front of your server and register the tunnel’s public URL in the next step.

2. Register the endpoint

Tell Ionic where to deliver and which events you want. For this walkthrough, subscribe to payment_intent.created.
The response includes a secret that starts with whsec_. This is the only time it’s shown. Save it where your server can read it:

3. Verify every delivery

Your handler already does: unwrap recomputes the signature over the raw body with constant-time comparison, rejects deliveries older than the replay window, and throws on anything that doesn’t match. Don’t skip verification: an unverified endpoint will accept forged events from anyone who learns your URL. If you’re implementing it yourself, Verify signatures walks through the same checks step by step.

4. Trigger an event

Create a PaymentIntent. This needs only an amount and currency, with no card, and emits payment_intent.created.
The verified envelope has type: "payment_intent.created" and an evt_… ID. Its data.object is the created PaymentIntent.

5. You’re receiving webhooks

unwrap returns a typed event. The type field identifies the payload shape in data.object; the event catalog documents each shape. Verification and receipt do not implement payment or fulfillment decisions. Those belong to your application. From here:

Event catalog

Subscribe to the events you actually handle — payment_intent.succeeded, refund.succeeded, and the rest.

Idempotency & retries

Make your handler safe against duplicate and out-of-order deliveries before you ship.

Developing locally

Ionic can’t deliver webhooks to localhost. Two ways to work while local:
  • Read the current status. Fetch the resource directly when you need an answer — for example GET /v1/checkout/sessions/{id} returns payment_status on demand. This works for most local testing.
  • Tunnel your receiver. Expose your local port (for example ngrok http 3000), register the tunnel URL as a webhook endpoint, and update the endpoint’s url whenever the tunnel address changes.
In production, fulfill from a verified webhook so paid orders do not depend on the buyer returning to your site.

What’s next

Verify signatures

Check each delivery by hand, or understand what unwrap does.

Manage endpoints

Update, disable, and rotate the signing secret on an endpoint.

Handle retries and duplicates

Deduplicate on the event id and handle out-of-order delivery.