Skip to main content

Webhooks API

Register webhook endpoints programmatically, manage event subscriptions, verify payload signatures, and understand the retry policy for failed deliveries.

Key Concepts

Webhook endpoints

MethodEndpointDescription
POST/webhooksRegister a new webhook endpoint
GET/webhooksList all registered webhooks
PATCH/webhooks/:idUpdate name, URL, secret, event subscriptions, or enabled state
DELETE/webhooks/:idDelete a webhook and stop deliveries
GET/webhooks/:id/deliveriesDelivery history for one webhook (there is no per-webhook GET; the list route returns each endpoint's details)
POST/webhooks/:id/testQueue a webhook.test event to that endpoint
POST/webhooks/:id/deliveries/:delivery_id/redeliverReplay a previous delivery

Payload format

Webhook payloads are JSON with a consistent structure:

{ "event": "agent.drift_detected", "user_id": "…", "timestamp": "2026-03-09T12:00:00+00:00", "data": { "agent_id": "…", ... } }

Each request also carries X-Clevername-Event (the event name), X-Clevername-Event-Version, X-Clevername-Delivery (stable across retries), and X-Clevername-Attempt.

Signature verification

Every webhook delivery includes anX-Clevername-Signature header in the form sha256=<hex>. Strip the sha256= prefix, compute an HMAC-SHA256 hex digest of the raw request body using the secret you registered, and compare the two. Reject any request where the signatures do not match.

Step-by-Step Guide
1

Register a webhook via API

Create a webhook by sending a POST request. name, url, and a secret of at least 16 characters are required; an empty events list subscribes to every event your plan allows, and listing an event above your tier returns 403:

POST /api/hub/webhooks Authorization: Bearer cn-live-your_token { "name": "Ops receiver", "url": "https://your-server.com/clevername-webhook", "secret": "<a random string of 16+ characters>", "events": ["agent.drift_detected", "agent.auto_restricted"] }

The response includes the webhook ID, URL, and subscribed events. The secret is the one you supplied, so keep your own copy.

API response showing webhook ID, URL, events, and enabled state
The response echoes the registration. Store the secret you chose for signature verification.
2

Verify signatures in your handler

In your webhook handler, verify the signature before processing the payload:

// Node.js example const crypto = require('crypto'); function verifySignature(body, signatureHeader, secret) { // Header looks like "sha256=<hex>" — strip the prefix first. const received = signatureHeader.replace(/^sha256=/, ''); const expected = crypto .createHmac('sha256', secret) .update(body, 'utf8') .digest('hex'); if (received.length !== expected.length) return false; return crypto.timingSafeEqual( Buffer.from(received), Buffer.from(expected) ); }
Important
Always use timing-safe comparison to prevent timing attacks. Never use simple string equality (===) for signature verification.
3

Respond to deliveries

Return a 2xx status code within 10 seconds to acknowledge receipt. A 5xx, a 429, or a timeout triggers the retry policy; any other 4xx is treated as a rejected payload and is not retried.

4

Understand the retry policy

Each delivery is attempted up to 3 times in total:

  • Attempt 1 — immediately
  • Attempt 2 — 1 second after a retryable failure
  • Attempt 3 — 4 seconds after the second failure

After the third failed attempt the delivery is marked failed and the endpoint's consecutive-failure counter increments; after 10 consecutive failed deliveries the endpoint is disabled automatically and must be re-enabled (PATCH enabled: true or the dashboard toggle). Check the delivery history in the dashboard or via the API to diagnose issues.