Webhooks overview
A webhook tells your server that something happened in the CRM, a few seconds to a minute after it happened. You register an HTTPS URL and pick the events you want; agentlyleads sends each one as a JSON POST.
Use webhooks when you would otherwise poll GET /api/v1/...?updatedSince= on a timer. Use the API to read or change data; use webhooks to find out when to do it.
Add an endpoint
Two ways, both need the workspace owner or an API key with write:webhooks:
- In the app: Settings, Developer, Webhooks. Enter the URL, tick the events, and copy the signing secret that appears.
- With the API:
POST /api/v1/webhooks.
curl https://agentlyleads.com/api/v1/webhooks \
-H "Authorization: Bearer $AGENTLYLEADS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.example.com/agentlyleads",
"events": ["lead.created", "deal.stage_changed"],
"description": "Mirror new leads into billing"
}'The response includes secret (it starts with whsec_). It is shown once. Store it with your receiver's other secrets; you need it to verify signatures. If you lose it, rotate it.
Subscribe to ["*"] to get every event, including ones added later.
The URL must be public and use https. Hosts that are, or resolve to, private, loopback or link-local addresses (for example localhost, 10.x.x.x, 169.254.169.254) are refused, and redirects are not followed. To develop locally, expose your receiver through a tunnel such as ngrok or Cloudflare Tunnel.
What a request looks like
POST /agentlyleads HTTP/1.1
Host: hooks.example.com
Content-Type: application/json
User-Agent: AgentlyLeads-Webhooks/1.0 (+https://agentlyleads.com/docs/webhooks)
X-AgentlyLeads-Event: lead.created
X-AgentlyLeads-Delivery: cmq9dlvr0alk34567890
X-AgentlyLeads-Timestamp: 1790517600
X-AgentlyLeads-Signature: sha256=6f1c0b...e93a
{
"id": "evt_5b1f0c7d2a9e4f6b8c3d1e0a",
"type": "lead.created",
"createdAt": "2026-09-27T14:00:00.000Z",
"workspaceId": "cmq1wksp0alk34567890",
"data": { "lead": { "id": "cmq2lead0alk34567891", "name": "Eve Park", "...": "..." } }
}| Field | Meaning |
|---|---|
id | The event ID. The same event sent again (a retry or a redelivery) keeps the same id, so de-duplicate on it |
type | The event name. See Events |
createdAt | When the event happened, ISO 8601 UTC |
workspaceId | The workspace the event belongs to |
data | The payload. Records use the same shape the v1 API returns for them |
| Header | Meaning |
|---|---|
X-AgentlyLeads-Event | Same as type |
X-AgentlyLeads-Delivery | The delivery ID. It stays the same across retries of this delivery; a manual redelivery gets a new one. Useful in support requests and for redelivery |
X-AgentlyLeads-Timestamp | Unix seconds when this request was signed |
X-AgentlyLeads-Signature | sha256= and a hex HMAC. See Verifying signatures |
How to respond
Answer with any 2xx status within 10 seconds. The body is ignored. Anything else (a 3xx, 4xx or 5xx, a timeout, or a connection error) counts as a failure and the event is retried.
If your work takes longer than a few seconds, put the event on a queue and answer 200 straight away.
Things to know
- Order is not guaranteed. Events are sent in batches every minute, several at a time, and retries go out later. Use
createdAtand the record'supdatedAtif order matters. - At least once. A network error after your server processed the request makes us send it again. De-duplicate on
id. - Re-read when in doubt. A payload is a snapshot taken when the event was queued. For the current state, fetch the record from the API.
- One change can raise several events. A booking from a new person, for example, sends
lead.created,task.createdandbooking.created. - Limits. Up to 10 endpoints per workspace. Delivery history is kept for 30 days.