Creating a Webhook Endpoint
Configure endpoints in Settings > Integrations in your Dashboard, or via the Integrations API. Each endpoint has:- URL — your HTTPS endpoint (HTTPS required in production)
- Event types — which events to subscribe to, from the event catalog. Prefix matching is supported: subscribing to
"recall"receives everyrecall.*event, including ones added later — the recommended form for safety-critical domains. Values outside the catalog are rejected with a422, so a typo can never leave you silently subscribed to nothing. - API version — the envelope version this endpoint receives. Pinned at creation; we never move it.
- Filters — optional per-event-type filtering rules (AND logic between filter keys, OR within a key)
- Signing secret — auto-generated 64-byte hex secret for payload verification
Event Catalog
Fetch the live catalog — this is the authoritative list, and it is also published in the OpenAPI spec:Recalls & safety
Consumer signals
Serial verification
System
Deprecated
offer.updated, product.recalled, and retailer.created predate the catalog. They remain valid in a subscription so existing integrations keep working, but no emitter is wired for them. product.recalled is superseded by the recall.* lifecycle above — build new integrations against that.
The catalog response also has a
planned section listing event types on our roadmap. Those are not subscribable yet: subscribing returns a 422 naming the tracking issue, rather than accepting a subscription that would silently never fire.Envelope Versioning
Every endpoint carries anapi_version that pins the shape of the envelope it receives. We never move an endpoint’s pin. Shipping a new envelope version does not change what your existing integration receives — change api_version yourself, with a PATCH, once your parser is ready.
2026-07-01
CloudEvents-shaped field names, plus a cursor for replay:
v1
cursor (v1 predates the replay feed). The data block is identical across versions — versioning governs the envelope only, never the event payload.
Replay on Reconnect
Retries cover an endpoint that is failing. They do not cover an endpoint that was simply down, was auto-disabled after sustained failures, or did not exist yet. For a recall feed that gap matters more than anything else in this document: a subscriber who missed a recall and can’t tell they missed it is worse off than one with no feed at all. So every event is written to a durable log when it is emitted — before any delivery is attempted — and you can walk that log forward from wherever you left off:- Persist the
cursorof the last event you durably processed — not the last one you received. - On reconnect, call the endpoint with
?after=<that cursor>. - Process the page, then repeat using the last cursor on the page, until a page comes back short.
envelope that is byte-identical to what a live delivery would have POSTed, so a replayed event goes through the same handler with no special casing, and event_id is stable across the live delivery and every replay — deduplicate on it.
The cursor is a keyset over
(occurred_at, id), not a timestamp. That is what makes the walk both gap-free and duplicate-free when two events share a timestamp — a plain since=<timestamp> filter can only pick one of those two failure modes.Request Headers
Every delivery includes these headers:Verifying Signatures
TheX-Closient-Signature header uses a Stripe-style format that includes a timestamp for replay protection:
- Extract the timestamp (
t) and signature (v1) from the header. - Construct the signed payload:
{timestamp}.{raw_json_body}. - Compute HMAC-SHA256 using your signing secret.
- Compare your computed signature with
v1using a constant-time comparison. - Check the timestamp is within your tolerance window (recommended: 5 minutes).
- Python
- Node.js
Secret Rotation
When you rotate a signing secret via the API (POST /endpoints/{id}/rotate-secret), the previous secret remains valid for a 24-hour grace period. During this window, deliveries include both signatures:
v1 first, then fall back to v1old. After the grace period, only v1 is included.
Delivery Lifecycle
Each delivery progresses through these statuses:Retry Policy
Failed deliveries are retried with exponential backoff and jitter:
After 8 attempts, the delivery moves to
dead_letter. You can replay dead-lettered deliveries via the API:
Auto-Disable
If an endpoint has a 100% failure rate over the last 24 hours (with at least one delivery attempt), Closient automatically disables it and sends an email notification. Re-enable it from the Dashboard or API after resolving the issue.Testing
Send a test event to verify your endpoint is reachable and signature verification works:test.ping event synchronously and returns the HTTP status code from your endpoint.
Best Practices
- Return 2xx quickly — acknowledge receipt, then process asynchronously. Closient times out after 30 seconds.
- Handle duplicates — use the
idfield (event_idonv1) for idempotency. The same event may arrive more than once via retries or a replay. - Checkpoint your cursor — persist the
cursorof the last event you durably processed, so an outage becomes a replay rather than a silent gap. For recall events this is the difference between a feed you can rely on and one that manufactures false confidence. - Verify signatures — always validate
X-Closient-Signaturebefore processing. Reject unsigned or expired requests. - Use HTTPS — required in production. HTTP is only allowed in development.
- Monitor health — check delivery status in the Dashboard or via the Integrations API. Address failures before they accumulate into auto-disable.