Skip to content
Docs
Developers

Webhooks

A webhook sends a signed POST to your server when something changes, so your app can react without polling. There are two kinds: workspace webhooks for everything in a workspace, and subscriptions to one brand system.

Workspace webhooks

Workspace owners and admins add endpoints in Settings, API keys, Webhooks; other members see the list and the delivery log read-only. Each workspace can have up to 10 endpoints, each a public https:// URL.

The signing secret (whsec_...) is shown once after you create or rotate it. An owner or admin can reveal it again later, and each reveal is recorded in the audit log.

EventWhen it fires
system.activatedA brand system was approved and its tokens are live
design.readyA design finished its identity run (mood board, logo, imagery)
design.publishedA design version was published with its finished deliverables
project.review_readyA project finished its design run and is waiting for review
project.finalizedA project was approved and finalized
webhook.testSent only by Send test in settings

Every delivery is a POST with a JSON body. data holds ids and names only, never a secret or brief text.

Workspace delivery body
{
  "id": "<delivery id>",
  "event": "design.ready",
  "createdAt": "2026-10-13T09:30:00.000Z",
  "workspaceId": "...",
  "data": { "designId": "...", "designName": "..." }
}
HeaderValue
X-BrandPilot-EventThe event name
X-BrandPilot-DeliveryThe delivery id, the same as id in the body
X-Webhook-TimestampUnix seconds when this attempt was signed
X-Webhook-SignatureHex HMAC-SHA256 of the timestamp, a dot and the raw body, with the endpoint secret

Retries: a non-2xx answer, a timeout (10 seconds) or a failed connection is retried twice, three attempts in total. Redirects are not followed. The delivery log keeps the latest 50 deliveries per endpoint for 30 days. Answer 2xx quickly and ignore a delivery id you have already handled.

Verify the signature

Check the signature against the raw body before you parse it, and reject a timestamp more than 5 minutes from your clock so a captured delivery cannot be replayed:

Signature check (Node.js)
import crypto from 'crypto';

const TOLERANCE_SECONDS = 300;

function verifyWebhook(body: string, signature: string | null, timestamp: string | null, secret: string): boolean {
  if (!signature || !timestamp || !/^\d{1,12}$/.test(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > TOLERANCE_SECONDS) return false;
  if (!/^[0-9a-f]{64}$/i.test(signature)) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.${body}`).digest();
  const provided = Buffer.from(signature, 'hex');
  if (provided.length !== expected.length) return false;
  return crypto.timingSafeEqual(provided, expected);
}

Brand system subscriptions

To follow one brand system, subscribe while signed in. The response includes a secret for the signature check above, shown once.

Create a subscription
POST /api/v1/systems/{name}/webhooks
Content-Type: application/json

{
  "targetUrl": "https://your-app.com/api/design-webhook",
  "events": ["system.published", "system.updated"]
}
EventWhen it fires
system.publishedThe system is approved for the first time and goes live
system.updatedThe foundation is approved, a live system is re-approved, or a design version of it is published

Retries of a system delivery keep the original timestamp and signature and land inside the 5-minute window. Make your handler safe to run twice for the same system, version and event.