Webhooks
Outbound HTTP notifications when things change in your library — so your integrations react instead of polling.
Setup
Owners and admins manage endpoints under Settings → Webhooks. Each subscription has an HTTPS URL, a set of subscribed events, and a signing secret — shown once at creation, store it in your secrets vault. Use the Send test ping button to verify your receiver end-to-end.
Events
| Event | Fires when |
|---|---|
ping | Test delivery from the UI |
asset.created | Figma import, upload, or POST /api/v1/assets |
asset.updated | Asset metadata edited |
asset.deleted | Asset deleted |
collection.created | Collection created |
collection.shared | Collection shared via public link |
member.invited | Teammate invited |
member.joined | Invitation accepted |
Delivery format
Each delivery is a JSON POST:
{
"id": "5f3c…", // delivery id — dedup on it, retries reuse it
"event": "asset.created",
"organization_id": "a1b2…",
"emitted_at": "2026-06-07T14:00:00Z",
"data": { … } // event-specific payload
}With headers:
| Header | Content |
|---|---|
X-DesignVault-Event | Event name |
X-DesignVault-Delivery-Id | Unique id, stable across retries |
X-DesignVault-Timestamp | Unix seconds |
X-DesignVault-Signature | t=<timestamp>,v1=<hex> |
Verifying signatures
The signature is HMAC-SHA256 of `${timestamp}.${rawBody}` using your endpoint secret. Always verify before trusting a delivery, and reject timestamps older than ~5 minutes (replay protection):
import { createHmac, timingSafeEqual } from 'node:crypto'
function verify(rawBody: string, header: string, secret: string): boolean {
const [t, v1] = header.split(',').map((p) => p.split('=')[1])
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
const expected = createHmac('sha256', secret)
.update(`${t}.${rawBody}`)
.digest('hex')
return timingSafeEqual(Buffer.from(expected), Buffer.from(v1))
}Retries
Respond with any 2xx within 10 seconds to acknowledge. Anything else (including timeouts) triggers retries with exponential backoff — 5s, 30s, 5m, 30m, 2h — for up to 5 attempts, then the delivery is marked failed. Owners and admins can inspect the delivery history and replay failed deliveries from Settings → Webhooks.
Good practices
- Dedup on the delivery id — retries reuse it; process-at-most-once is your receiver's job.
- Acknowledge fast, process async — enqueue the payload and return 200; don't do slow work inline.
- HTTPS only — HTTP endpoints are rejected at creation.
- Pair with the read API — the webhook tells you something changed; fetch the fresh state with
GET /api/v1/assets/{id}.