Skip to main content

Subscription, payment and recovery events

Subscribe your HTTPS endpoint to the event types your integration uses. Events describe persisted outcomes; receipt order is not a billing schedule. Verify the signature before acting and use authenticated reads when an event is missing or your application needs current state.

Delivered subscription events

EventMeaningEvent-specific fields
subscription.createdSubscription createdsubscriptionId, nullable customerId
subscription.billedSuccessful billing cyclesubscriptionId, amount, currency, nullable transactionId
subscription.payment_failedA billing attempt failedsubscriptionId, nullable reason
subscription.past_dueEntered past-due statussubscriptionId
subscription.cancelledCancellation took effectsubscriptionId
subscription.pausedSubscription pausedsubscriptionId
subscription.resumedSubscription resumedsubscriptionId
subscription.completedFinished the configured cycle/date limitsubscriptionId, reason, cyclesBilled
subscription.trial_startedTrial beganSee event schema
subscription.trial_endedTrial endedSee event schema
subscription.trial_cancelledTrial cancelledSee event schema

Completion reason is MAX_CYCLES or END_DATE; cyclesBilled counts successful cycles. Amounts are integer minor units. A failed billing attempt is not a successful charge, and a card update alone is not a recovered payment.

Subscription item changes also deliver subscription_item.created, subscription_item.updated and subscription_item.deleted. Their full payload variant includes item, proration, subscriptionAmount, prorationChargeStatus, nullable prorationChargeMessage and organizationId. The unavailable-details variant contains only organizationId. Use the published event schema for the variants and nested shapes.

Payment, setup and dunning events

  • Payments: payment.completed, payment.declined, payment.pending, payment.failed, payment.settled, payment.returned, payment.refunded, payment.voided, payment.flagged_for_review and payment.fraud_detected.
  • Card setup: setup_intent.created, setup_intent.processing, setup_intent.requires_confirmation, setup_intent.succeeded, setup_intent.failed and setup_intent.canceled.
  • Recovery: dunning.case_opened, dunning.case_recovered, dunning.case_exhausted and dunning.payment_link_sent.

Dunning payload fields use snake_case, including case_id, subscription_id, customer_id, merchant_id, amount_at_risk, currency, status and retry_count. Do not rename these into the camelCase subscription payload shape. Use the event schema in openapi.json for every field.

For ACH, acceptance and settlement are distinct. Apply the payment outcome version for the same original debit, not delivery order; a later return can supersede settlement. See ACH lifecycle.

Delivery envelope and golden payloads

The signed JSON body is flat, not wrapped in a payload property. The envelope contains eventId, eventType, timestamp, organizationId and merchantId, plus the legacy type discriminator where supplied. Location-scoped event shapes can include locationId; subscription lifecycle payloads do not guarantee that field. Resolve the subscription if you need its location rather than assuming locationId is always present.

These are the exact event-specific payload fields from the subscription wire-contract golden fixtures, not captured customer traffic and not complete signed deliveries:

subscription.created

{"subscriptionId":"sub_wire","customerId":"cus_wire"}

subscription.billed

{"subscriptionId":"sub_wire","amount":2500,"currency":"USD","transactionId":"txn_wire"}

subscription.payment_failed

{"subscriptionId":"sub_wire","reason":"DECLINED"}

Subscription status changes

{"subscriptionId":"sub_wire"}

The same one-field shape is used for subscription.past_due, subscription.cancelled, subscription.paused and subscription.resumed.

subscription.completed

{"subscriptionId":"sub_wire","reason":"MAX_CYCLES","cyclesBilled":12}

In a real delivery, these fields sit alongside the envelope fields above. The delivery's eventType determines which shape to decode; do not infer it from shared fields such as subscriptionId. The cancelled event's wire name is subscription.cancelled (two “l”s). Do not route on the legacy type: older cancellation deliveries retain a SubscriptionCanceled discriminator, while the generated SDK variant is SubscriptionCancelled. The public eventType is the stable dispatch key.

Verify the signature

The X-Peak-Signature header has the form t=<unix-seconds>,v1=<hex-signature>. Compute HMAC-SHA256 with your endpoint secret over the exact bytes of:

<t>.<rawBody>

Compare signatures in constant time, reject malformed headers and reject timestamps more than 300 seconds from your receiver's clock. Do not parse and reserialize JSON before verification, and do not substitute the JSON timestamp for the signed header timestamp.

The TypeScript SDK provides verifyPlatformWebhook(rawBody, signatureHeader, secret). The Kotlin SDK provides WebhookVerifier(secret).verifySignatureHeader(rawBody, signatureHeader).

Deduplicate and apply safely

After successful verification, persist eventId atomically with your business-state update. A duplicate event is a no-op. Keep handlers fast, acknowledge only once the work is durable, and tolerate out-of-order notifications by reading authoritative subscription/transaction state where needed. Scope records by organization and the resource's authorized location; a signed event is not permission to access another tenant.

See event log and replay for recovering a missing notification. Replay resends an event; it does not bill a subscription again.