Skip to content
Review environment of the Cloudprinter Parallel build — fictional data, not the production Cloudprinter service.

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 status must be active and its events list (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.