Publish an event once; HookSift fans it out to every subscribed endpoint with per-subscription ordering, endpoint-level exactly-once, and configurable retries.
All requests go to the edge base URL below. Every request is authenticated and signed. Unauthenticated requests to the root return 403 Forbidden from hooksift-edge — this is expected; the API never serves an open root.
https://events.hooksift.com
The API is versioned in the path (/v1 is implied by the current edge; unversioned routes resolve to v1). All request and response bodies are JSON.
Authenticate with a per-tenant bearer key in the Authorization header. Keys are scoped to a single tenant and are issued during design-partner onboarding.
Authorization: Bearer hs_live_m8r2k4_5a1f…
Publish an event to a tenant channel. HookSift resolves every subscription on that channel and fans the event out.
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer key for the tenant. |
Hooksift-Idempotency-Key | Recommended | Collapses redeliveries of the same logical event. See Idempotency. |
Content-Type | Yes | application/json |
curl -X POST https://events.hooksift.com/dispatch/tenant/m8r2k4/channel/orders \
-H "Authorization: Bearer $HOOKSIFT_KEY" \
-H "Hooksift-Idempotency-Key: evt_9Fh2kQ" \
-H "Content-Type: application/json" \
-d '{
"type": "orders.paid",
"data": { "order_id": "o_5521", "amount": 4900, "currency": "usd" }
}'
| Field | Type | Description |
|---|---|---|
type | string | Event type, e.g. orders.paid. Subscriptions filter on this. |
data | object | Arbitrary JSON payload delivered verbatim to endpoints. |
dedupe_key | string? | Optional explicit dedupe key; overrides the idempotency header. |
order_key | string? | Optional ordering key. Events sharing a key deliver in publish order per endpoint. Defaults to the channel. |
202 Accepted{
"dispatch_id": "dsp_7yQ2fD",
"channel": "orders",
"endpoints": 4,
"status": "queued"
}
A 202 means the event was accepted and queued for fan-out — not that every endpoint has received it. Track per-endpoint outcomes in the delivery log.
Send Hooksift-Idempotency-Key on every publish. Two requests with the same key inside the dedupe window collapse to a single logical event, so a client retry after a network blip never fans out twice. The default window is 24 hours.
Deduplication also applies at the endpoint: if a delivery is retried, the receiver sees the same Hooksift-Event-Id and can safely no-op. Exactly-once at the endpoint is the combination of sender-side dedup and this stable event id.
Each subscription carries its own retry policy. Set it when you create the endpoint, or update it later. Endpoints that exhaust their attempts are moved to the dead-letter queue and stop receiving new deliveries until re-enabled.
{
"max_attempts": 8,
"backoff": "exponential",
"base_ms": 500,
"timeout_ms": 5000,
"dead_letter": true
}
| Backoff | Delay between attempts |
|---|---|
exponential | base_ms · 2^(n-1), capped at 1 h, ±10% jitter |
linear | base_ms · n |
constant | base_ms every attempt |
Every attempt is recorded: endpoint, attempt number, status, latency, and the response code. Query the log to debug failures, then replay a single delivery or a time range — without republishing from your source.
{
"deliveries": [
{
"id": "dlv_3kP…",
"endpoint": "https://hooks.dev-app.com/ep",
"attempt": 2,
"status": "retrying",
"response_code": 503,
"latency_ms": 4812
}
]
}
Log retention depends on plan: 7 days (Signal), 30 days (Fan-out), 90 days (Dispatch).
Every delivery includes a Hooksift-Signature header. Recompute the HMAC over t + "." + body with your endpoint secret and compare in constant time. Reject deliveries whose timestamp is more than 5 minutes old to defeat replays.
# header: Hooksift-Signature: t=1720051200,v1=4f9a…
import hmac, hashlib, time
def verify(secret, header, body):
parts = dict(p.split("=") for p in header.split(","))
ts, sig = parts["t"], parts["v1"]
if abs(time.time() - int(ts)) > 300: return False # stale
mac = hmac.new(secret, f"{ts}.{body}".encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, sig)
| Code | Meaning |
|---|---|
202 | Accepted — event queued for fan-out. |
401 | Missing or invalid bearer key. |
403 | Unauthenticated root, or tenant disabled. The API root always returns 403. |
409 | Idempotency conflict — key reused with a different body. |
422 | Malformed payload (missing type, invalid JSON). |
429 | Rate limited — back off per the Retry-After header. |
503 | Edge draining — retry with backoff; publishes are never dropped. |
Tenant keys are provisioned to design partners after onboarding.
Apply for a tenant →