Agent-readable docs index: /docs/llms.txt. Full docs in one file: /docs/llms-full.txt. Download /docs/docs.zip to grep all markdown files locally.

Webhook Event Catalog & Schemas

When significant events occur in your workspace, Shaf dispatches signed HTTP POST requests to your registered webhook endpoints in real-time. All payloads use a consistent envelope schema with a typed data field specific to each event.
For setup instructions and signature verification code, see Configuring Webhook Endpoints.

Request Headers

Every webhook HTTP request includes these headers:
HeaderValueDescription
Content-Typeapplication/jsonPayload encoding
User-AgentShaf-Webhook/2.0Identifies the sender
X-Shaf-Evente.g. link.clickedThe event type string
X-Shaf-DeliveryUUIDUnique delivery attempt identifier
X-Shaf-SignatureHMAC-SHA256 hexSignature of the raw JSON body

Base Payload Envelope

All events share this outer envelope structure:
{ "id": "evt_9823471029384", "event": "link.clicked", "createdAt": "2026-09-19T20:25:00.000Z", "organizationId": "org_789xyz", "data": { } }
FieldTypeDescription
idstringUnique event identifier (for deduplication)
eventstringEvent type constant (see catalog below)
createdAtstringISO 8601 timestamp of when the event occurred
organizationIdstringThe workspace that generated the event
dataobjectEvent-specific payload (see each event below)

Event Catalog

Fires when a new short link is created via the console or REST API.
{ "id": "evt_abc123", "event": "link.created", "createdAt": "2026-09-19T20:00:00.000Z", "organizationId": "org_789xyz", "data": { "slug": "summer-deal", "domain": "link.yourbrand.com", "destinationUrl": "https://yourbrand.com/summer", "tags": ["sale", "email"], "createdBy": "user_456", "expiresAt": null, "passwordProtected": false } }

Deduplication

The id field is a stable unique identifier per event. In rare cases of network retries, your endpoint may receive the same event twice. Store processed event IDs and skip duplicates:
const processedEvents = new Set<string>() function handleWebhook(event: ShafEvent) { if (processedEvents.has(event.id)) { return // Already processed } processedEvents.add(event.id) // Process event... }
For production systems, use a persistent store (Redis, database) instead of an in-memory Set.