Webhooks
Webhooks push workspace events to an HTTPS endpoint you host, as they happen. Workspace owners manage endpoints in the application under Settings → Webhooks — add a URL, copy the signing secret (shown exactly once), and events start flowing.
Managing endpoints with the API
Section titled “Managing endpoints with the API”Software can manage endpoints too, with a credential granted the
workspace.webhooks:write scope (reading needs workspace.webhooks:read; a personal
access token must also belong to a workspace owner):
curl -X POST \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/hooks" }' \ https://api.limitry.com/v1/workspace/webhook-endpointsThe response carries the endpoint’s signing secret — once. The API also lists endpoints, re-enables one after repeated failures, sends a test event, and lists and redelivers deliveries; see the API reference.
AI agents connected to the workspace can read endpoints and delivery history, send test events and redeliver — but not add, remove or re-enable endpoints: those change where your workspace’s data is sent, so a person configures them.
The delivery
Section titled “The delivery”Each delivery is an HTTP POST with a JSON body:
{ "id": "evt_5f0c…", "type": "workspace.member.joined", "createdAt": "2026-08-20T12:00:00.000Z", "data": { "userId": "…", "email": "casey@example.com" }}and three signature headers:
webhook-id: del_9a1b…webhook-timestamp: 1755691200webhook-signature: v1,MEQCIB…webhook-ididentifies this delivery. It stays the same across retries — use it to deduplicate.idinside the body identifies the event. If you registered multiple endpoints, each receives its own delivery of the same event — deduplicate across endpoints by eventidif you need to.
Verifying signatures
Section titled “Verifying signatures”Deliveries are signed with your endpoint’s secret (whsec_…) using the
same scheme as Svix,
so any standard Svix library verifies them:
import { Webhook } from "svix";
const wh = new Webhook(process.env.WEBHOOK_SECRET);
// Express-style handler; `payload` must be the RAW request body string.app.post("/webhooks", (req, res) => { let event; try { event = wh.verify(req.body, req.headers); } catch { return res.status(400).send("bad signature"); } // handle event… res.status(200).send("ok");});Verifying by hand: the signature is v1, followed by a Base64
HMAC-SHA256 of `${webhookId}.${timestamp}.${body}` keyed with the
secret after its whsec_ prefix (Base64-decoded). Always verify against
the raw body — re-serializing JSON breaks the signature — and reject
timestamps older than a few minutes to prevent replays.
Respond fast, process later
Section titled “Respond fast, process later”Return a 2xx within 10 seconds. If your processing is slow, acknowledge
first and process asynchronously — a timeout counts as a failed delivery.
Retries and failures
Section titled “Retries and failures”Failed deliveries (non-2xx, or timeout) retry automatically with backoff,
up to 6 attempts, with the same webhook-id. An endpoint that fails
20 consecutive deliveries is disabled automatically — delete and
re-add it (new secret) once your endpoint is healthy.
Under Settings → Webhooks → Deliveries you can see each delivery’s
status and attempts, send a test event (type: "ping"), and redeliver
any recorded delivery — a redelivery arrives with a fresh webhook-id
but the same event id, so event-level deduplication still applies.
Events
Section titled “Events”| Type | Fires when | data |
|---|---|---|
workspace.member.joined |
An invitation is accepted. | userId, email |
ping |
You send a test from Settings. | a test message |
Event payloads only ever gain fields — build tolerant parsers.
GET /v1/workspace/event-types returns the full list, with what each means.
Choosing events
Section titled “Choosing events”An endpoint receives every event unless you choose: in Settings →
Webhooks pick “Only these” when adding it, or pass eventTypes when
creating it through the API — only listed types are accepted:
curl -X POST https://api.limitry.com/v1/workspace/webhook-endpoints \ -H "Authorization: Bearer YOUR_API_KEY" -H "content-type: application/json" \ -d '{"url":"https://example.com/hooks","eventTypes":["workspace.member.joined"]}'A test event (ping) always reaches the endpoint you test.