Webhooks: delivery and troubleshooting
Register endpoints with POST /v1/webhook-endpoints (scope webhooks:manage) or in the portal. Formats: native_v1 (signed JSON) and the CloudSignal formats cloudsignal_v2 / cloudsignal_v1_1 for existing integrations.
1. Verify the signature
Each delivery carries x-cloudprinter-signature: t=<unix>,v1=<hex> where v1 = HMAC-SHA256(signing_secret, t + "." + raw_body), and x-cloudprinter-delivery (unique per delivery). The signing secret is returned once when you create the endpoint.
// Node: verify x-cloudprinter-signature: t=<unix seconds>,v1=<hex HMAC-SHA256>
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(rawBody: string, header: string | undefined, secret: string, toleranceS = 300): boolean {
const parts = Object.fromEntries((header ?? '').split(',').map((p) => p.split('=') as [string, string]));
const t = Number(parts.t);
if (!parts.v1 || !Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceS) return false; // replay window
const expected = createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
return expected.length === parts.v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}- Compute the HMAC over the raw request body bytes, before any JSON parsing or re-serialization.
- Reject timestamps outside your tolerance window to limit replay; store recent delivery ids and ignore repeats.
- Compare in constant time.
2. Respond quickly with 2xx
Any 2xx marks the delivery succeeded. Do the work asynchronously. Timeouts (10 s), connection errors and non-2xx responses are retried. Redirects are not followed.
3. Retries, backoff and the dead-letter queue
- Delivery is at-least-once: the same event can arrive more than once. Make handlers idempotent on the event id.
- Failed attempts are retried with exponential backoff and jitter, up to 8 attempts; then the delivery is moved to the dead-letter state (dead) and stays visible in the delivery log.
- Events are not guaranteed to arrive in order. Use the timestamps and the order’s current state from
GET /v1/orders/{id}rather than assuming sequence.
4. Replay
A replay creates a new delivery of the same payload (with a new delivery id). It does not create a new order event and has no production or financial effect. Replays are scoped: a customer can only replay its own deliveries, operations staff any delivery.
5. Checklist when deliveries fail
- Endpoint URL must be publicly reachable (use HTTPS). Private, loopback, link-local and metadata addresses are refused when you register the endpoint and again at delivery time (SSRF protection).
- Endpoint
statusmust beactiveand itseventslist (if set) must include the event type. - Check the endpoint mode: sandbox events only go to sandbox endpoints, live to live.
- Signature mismatch usually means the body was re-serialized or the wrong secret is used (each endpoint has its own).
- Keep responses small; response bodies are truncated in the delivery log.