JsonCutDocs

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-Id
  • X-JsonCut-Timestamp
  • X-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}/deliveries returns recent safe delivery state without downstream response bodies.
  • POST /api/v2/webhooks/{id}/test creates one signed test event.
  • POST /api/v2/webhooks/{id}/deliveries/{deliveryId}/replay retries a dead-letter delivery while preserving its event identity.

Respond with a 2xx status quickly and process work asynchronously. Delivery uses bounded exponential retries.