Webhooks deliver real-time notifications to your server when events occur in Closient. Each webhook endpoint receives signed HTTP POST requests with JSON payloads.
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 (e.g.,
["offer.updated", "product.claimed"])
- Filters — optional per-event-type filtering rules
- Signing secret — auto-generated 64-byte hex secret for payload verification
{
"event_id": "evt_abc123def456",
"event_type": "product.updated",
"timestamp": "2026-04-01T12:00:00Z",
"data": {
"id": "ack3p9tw6x7r",
"gtin": "00012345678905",
"changes": ["product_name", "description"]
}
}
Verifying Signatures
Every webhook request includes an X-Webhook-Signature header containing an HMAC-SHA256 signature of the payload body.
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode("utf-8"),
payload,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)
Always verify the signature before processing a webhook payload. This prevents forged requests from being acted upon.
Secret Rotation
When you rotate a signing secret, the previous secret remains valid for a 24-hour grace period. During this window, verify against both the current and previous secrets to avoid rejecting legitimate deliveries.
Delivery Lifecycle
Each delivery progresses through these statuses:
| Status | Description |
|---|
PENDING | Queued for delivery |
DELIVERED | Received 2xx response |
FAILED | Non-2xx response or connection error |
RATE_LIMITED | Endpoint is being rate-limited |
DEAD_LETTER | All retry attempts exhausted |
Retry Policy
Failed deliveries are retried with exponential backoff:
| Attempt | Delay |
|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
After all retry attempts are exhausted, the delivery moves to DEAD_LETTER status. You can inspect dead-lettered deliveries in the Dashboard or via the API.
Best Practices
- Return 2xx quickly — do heavy processing asynchronously after acknowledging receipt
- Handle duplicates — use the
event_id field for idempotency
- Verify signatures — always validate
X-Webhook-Signature before processing
- Monitor delivery health — check for consecutive failures in the Dashboard
See the Integrations API reference for endpoint management and delivery inspection.