API reference
Webhooks
Subscribe to events so your systems react the moment syncs, renders, and audits complete.
Event types
| Event | Description |
|---|---|
| sync.completed | A source sync finished (scheduled, on-demand, or a catalog push). |
| feeds.rendered | The feed artifacts were regenerated. |
| audit.completed | An audit run finished, with the new score and grade. |
Managing webhooks
/api/v1/webhooksRegister an HTTPS endpoint for one or more events. Requires the write scope. The response includes the signing secret (prefixed whsec_) once — store it immediately; it is never returned again.
| Parameter | Type | Description |
|---|---|---|
urlreq | string | HTTPS endpoint to deliver events to. |
eventsreq | string[] | One or more of the event types above. |
curl -X POST https://useshelfready.com/api/v1/webhooks \
-H "Authorization: Bearer $SHELFREADY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/hooks/shelfready",
"events": ["sync.completed", "audit.completed"]
}'/api/v1/webhooksList registered endpoints (signing secrets are never returned). Requires the read scope.
/api/v1/webhooksRemove an endpoint by ID. Requires the write scope.
curl -X DELETE https://useshelfready.com/api/v1/webhooks \
-H "Authorization: Bearer $SHELFREADY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "id": "9e8d7c6b-5a49-3827-1605-f4e3d2c1b0a9" }'Deliveries
Every delivery is a JSON POST with a top-level event, created_at, and an event-specific data object. Requests carry X-ShelfReady-Signature and X-ShelfReady-Event headers and the User-Agent ShelfReady-Webhooks/1.0, and time out after 10 seconds — respond with a 2xx quickly and do the work asynchronously.
{
"event": "audit.completed",
"created_at": "2026-07-17T09:20:44.000Z",
"data": {
"run_id": "0f1e2d3c-4b5a-6978-8796-a5b4c3d2e1f0",
"catalog_score": 72,
"grade": "B"
}
}Verifying signatures
The X-ShelfReady-Signature header has the form t=<unix ts>,v1=<hex HMAC-SHA256>, where the MAC is computed over <ts>.<body> with your whsec_… secret. Verify every delivery and reject stale timestamps.
import { createHmac, timingSafeEqual } from "node:crypto"
export function verifyShelfReady(secret: string, body: string, header: string): boolean {
// header: "t=<unix ts>,v1=<hex hmac>"
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)))
const t = Number(parts.t)
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > 300) return false
const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest()
const given = Buffer.from(parts.v1 ?? "", "hex")
return given.length === expected.length && timingSafeEqual(given, expected)
}Compute the signature against the exact raw bytes you received. Parsing to JSON first can reorder keys and break verification.
Retries
A delivery that fails (non-2xx, timeout, or connection error) is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, and 12 hours. After the last failed attempt the delivery is marked dead and not retried again.