Webhooks
Register signed webhook endpoints, receive monitor-matched events in real time, verify the HMAC signature, and read the delivery log.
Webhooks
Webhooks are how monitors reach you in real time. Register an endpoint, attach it to a monitor, and Clous POSTs each matching event to your URL with an HMAC signature you can verify.
- Base URL:
https://api.clous.ai - Auth:
Authorization: Bearer clous_live_...
Register an endpoint
POST /v1/webhooks/endpointsProp
Type
curl -s -X POST "https://api.clous.ai/v1/webhooks/endpoints" \
-H "Authorization: Bearer clous_live_..." \
-H "Content-Type: application/json" \
-d '{ "url": "https://api.acme.com/webhooks/clous", "description": "Production events" }'import requests
resp = requests.post(
"https://api.clous.ai/v1/webhooks/endpoints",
headers={"Authorization": "Bearer clous_live_..."},
json={"url": "https://api.acme.com/webhooks/clous", "description": "Production events"},
)
endpoint = resp.json()["data"][0]
secret = endpoint["secret"] # store this — used to verify signaturesExample response
{
"data": [
{
"id": "7b1ff0c8-fe92-421f-b8ed-ffa246d394de",
"url": "https://api.acme.com/webhooks/clous",
"secret": "9bccb20ea569…",
"description": "Production events",
"status": "active",
"created_at": "2026-06-13T16:27:30.569725+00:00"
}
],
"source": "clous_monitoring",
"warnings": []
}The secret is returned once on creation. Store it — it signs every delivery to
this endpoint and is what you verify against.
Receiving a webhook
When a monitor matches, Clous POSTs the event to your URL with these headers:
| Header | Value |
|---|---|
Clous-Event-Id | The event id. |
Clous-Delivery-Id | Unique per delivery attempt — use it for idempotency. |
Clous-Timestamp | ISO-8601 timestamp, part of the signed payload. |
Clous-Signature | sha256=<hex> — HMAC-SHA256 of "{timestamp}.{raw_body}" keyed by your endpoint secret. |
Body:
{
"type": "sec.filing.new",
"monitor_id": "9107bc79-2e59-40f1-905e-7d3267563ecc",
"delivery_id": "…",
"event": { "id": "535754cd-…", "event_type": "sec.filing.new", "importance": "medium",
"entity_name": "NVIDIA Corp", "entity_cik": "0001045810", "accession": "…",
"summary": "…", "evidence": [ … ] }
}Verify the signature
Recompute the HMAC and compare in constant time. Reject if it doesn't match or the timestamp is stale.
import hmac, hashlib
def verify(secret: str, headers, raw_body: bytes) -> bool:
ts = headers["Clous-Timestamp"]
expected = "sha256=" + hmac.new(
secret.encode(), f"{ts}.{raw_body.decode()}".encode(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, headers["Clous-Signature"])Delivery log & retries
Deliveries that don't get a 2xx are retried with backoff
(1 min → 5 min → 30 min → 2 h, then marked failed). Inspect the log:
GET /v1/webhooks/deliveriesReturns recent deliveries for your endpoints with status
(pending / success / failed), attempts, response_status, and last_error.
Status codes
| Status | Meaning |
|---|---|
200 / 201 | Endpoint registered or deliveries returned. |
400 | url is not https://. |
401 | Missing or invalid API key. |