Webhooks overview
Webhooks let your systems react to events in your PeakCommerce tenant in near real time. This page is the developer reference; for the admin UI walk-through see the Webhooks page under Settings → Developer Tools. For the API to manage subscriptions see API reference: webhook subscriptions.
Architecture
PeakCommerce
│ (event happens)
▼
Webhook subscription ── HMAC-signed POST ──▶ Your endpoint
▲ │
│ 2xx within 10s
│ (retry up to 5× on failure) ▼
└──────────────────────────────────────── Delivered, recordedDelivery format
Each delivery is a POST with Content-Type: application/json and this body:
{
"event": "subscription.changed",
"timestamp": "2026-05-02T14:01:23Z",
"tenantId": "ten_01H...",
"data": { /* the resource that changed */ }
}Headers:
X-Webhook-Signature
sha256=<hex_hmac_sha256> — HMAC of the raw body (see below)
X-Webhook-Event
The event type (e.g. subscription.changed)
X-Webhook-Timestamp
ISO 8601 time of the event
Signature verification
Each subscription has a signing secret, returned once when you create it (POST /webhooks) — store it securely; it is never shown again.
The signature is HMAC-SHA256(secret, rawBody), hex-encoded, sent in X-Webhook-Signature with a sha256= prefix. Verify it over the raw request body (do not parse first), using a constant-time comparison:
Reject any request whose signature does not match, and confirm the tenantId in the body matches the tenant you expect.
Event types (selected)
The event field carries the event type. A selection:
account.created, account.updated
Account CRUD
subscription.created, subscription.changed, subscription.cancelled, subscription.renewed
Subscription lifecycle
order.created, order.updated
Order lifecycle
payment.session_created, payment.succeeded, payment.failed
Payment lifecycle
invoice.created, invoice.paid, invoice.failed
Invoice lifecycle
webhook.test
Manually sent from the admin UI
The complete, live list is in GET /api/v1/manifest under capabilities.eventTypes.
Resolved entitlements on subscription events
Subscription events carry an extra subscription object holding the customer's resolved entitlement set, so you can react to subscription.created without a second call to the Entitlements API:
Two details worth reading carefully:
An absent
subscriptionkey is not the same as an empty one.entitlements: {}means the set resolved and this plan grants nothing. A missingsubscriptionkey means the set could not be resolved for this event — treat it as "unknown" and call the Entitlements API, rather than as "no entitlements". Gating to zero on a missing key would deny a paying customer their plan.The same resolution rules as the API apply:
nullon alimitfeature means unlimited, andresolvedFromnames the product the values came from (add-on products do not contribute).
Retries & dead-lettering
A delivery succeeds on any
2xxresponse within 10 seconds.On failure (non-
2xx, network error, or timeout) the delivery is retried up to 5 attempts total, with exponential backoff of2^attemptseconds (≈2s, 4s, 8s, 16s between attempts).After the 5th failed attempt the delivery is marked
dead_letterand recorded under Admin → Webhooks → Deliveries.List dead-lettered deliveries with
GET /api/v1/webhooks/{id}/deliveries?status=dead_letter, and re-enqueue any of them withPOST /api/v1/webhooks/{id}/deliveries/{deliveryId}/replay.
Best practices
Respond quickly — return a
2xxwithin 10 seconds. Move slow work to a queue.Be idempotent — deliveries may be retried, so process the same event safely more than once.
Verify the signature on every request.
Don't assume ordering — events may arrive out of order; use the resource's own
updatedAt/timestampas your watermark.
Related
Webhooks (admin app: Settings → Developer Tools → Webhooks)
Last updated
Was this helpful?
