Signed webhooks
Receive render completion, failure and cancellation events without aggressive polling.
Create an endpoint with the event types you need:
curl -X POST "$JSONCUT_API/api/v2/webhooks" \
-H "X-API-Key: $JSONCUT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Production renders","url":"https://example.com/jsoncut-events","eventTypes":["render.completed","render.failed","render.cancelled"]}'The signing secret is returned once. Store it securely.
Verify a delivery
JsonCut sends:
X-JsonCut-Event-IdX-JsonCut-TimestampX-JsonCut-Signature
Compute HMAC-SHA256 over <timestamp>.<raw-body> using the endpoint secret. Compare it with the v1= signature using a constant-time comparison. Verify the raw request bytes before parsing JSON, reject stale timestamps, and deduplicate event IDs.
import crypto from 'node:crypto';
const expected = `v1=${crypto
.createHmac('sha256', process.env.JSONCUT_WEBHOOK_SECRET)
.update(`${timestamp}.${rawBody}`)
.digest('hex')}`;
const valid = crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature),
);Delivery operations
GET /api/v2/webhooks/{id}/deliveriesreturns recent safe delivery state without downstream response bodies.POST /api/v2/webhooks/{id}/testcreates one signed test event.POST /api/v2/webhooks/{id}/deliveries/{deliveryId}/replayretries a dead-letter delivery while preserving its event identity.
Respond with a 2xx status quickly and process work asynchronously. Delivery uses bounded exponential retries.