Subscription Webhooks

Set a notifyUrl when you create a subscription, and Confirmo POSTs a JSON envelope to that URL for every lifecycle and payment event.

The event envelope

Every event shares one envelope. The data object has the same shape as the matching REST resource: a Subscription for lifecycle events, a Payment for payment events. So you rarely need a follow-up call.

{
  "id": "01977f2a-3c4d-7e5f-8a9b-0c1d2e3f4a5b",
  "type": "subscription.payment.succeeded",
  "timestamp": "2026-06-29T10:30:00Z",
  "sequence": 4,
  "resourceType": "subscription",
  "resourceId": "5c2e8d4a-7f1b-4e3c-8a9d-6b0f2c4e8a1d",
  "data": { "...Payment or Subscription object..." }
}

Two fields do the work in your handler: id (unique per event — dedupe on it) and sequence (goes up per subscription — apply events in order). resourceId is always the subscription ID, even for payment events.

The events

The two events most integrations act on first:

  • subscription.activated — the subscriber completed checkout and the first payment confirmed. Provision access.
  • subscription.payment.succeeded — a recurring charge settled. Extend access for the new period.

Here's the full list:

EventSent whenWhat you do
subscription.createdA subscription is created, usually in PENDING.Nothing, informational.
subscription.activatedSubscription checkout is done, and billing is on schedule.Provision access.
subscription.past_dueA charge failed and the subscription entered PAST_DUE.Prompt the subscriber to top up.
subscription.resumedA PAST_DUE subscription recovered within the configured grace period.Restore full access.
subscription.canceledCanceled before its natural end. See data.cancellation.reason.Turn off access.
subscription.expiredEnded naturally or after a terminal failure. See data.expiration.reason.Turn off access.
subscription.payment.succeededA recurring charge settled.Extend access for the new period.
subscription.payment.failedA recurring charge failed. See data.failure.reason.Note the reason; expect past_due.

Handling webhooks safely

  1. Reply fast. Return any 2xx as soon as you've stored the event, then do the real work in the background. A non-2xx or a timeout means we retry.
  2. Dedupe on id. The same event can arrive more than once. Process each id only once.
  3. Apply in order using sequence. Events for one subscription carry an increasing sequence. Apply an event only if it's newer than the last one you handled, and ignore older arrivals.
💡

Always verify a webhook before you trust it

Requests are signed with Ed25519. Verify the signature against the raw request body, and reject anything older than the replay window. The exact signing string, JWKS endpoint, retry schedule, rate limits, and redirect rules are all in the Subscriptions API Specification — build your verification against that.