General
Webhooks
Get told the moment something happens. Subscribe a URL to an event; we POST a JSON payload to it. The shape Zapier and Make expect.
/v1/hooks/eventsList events
GET/v1/hooks/sampleGet a sample payload
POST/v1/hooksSubscribe
DEL/v1/hooks/{id}Unsubscribe
List events
/api/v1/hooks/eventsEvery event you can subscribe to.
Any API key, read-only included.
Errors
401Missing or invalid key429More than 120 requests a minute
curl https://app.zicreach.com/api/v1/hooks/events \
-H "Authorization: Bearer zk_your_key"
{
"data": [
{
"key": "lead.replied",
"label": "A recipient replied"
},
{
"key": "lead.bounced",
"label": "A message hard-bounced"
},
{
"key": "lead.unsubscribed",
"label": "A recipient unsubscribed"
},
{
"key": "lead.opened",
"label": "A recipient opened a message"
},
{
"key": "lead.clicked",
"label": "A recipient clicked a link"
},
{
"key": "campaign.completed",
"label": "A campaign finished sending"
}
]
}
Get a sample payload
/api/v1/hooks/sampleAn example of what an event delivers, for mapping fields.
Any API key, read-only included.
Parameters
| Name | Type | Description |
|---|---|---|
event query |
string | Event key. Default lead.replied. |
Errors
401Missing or invalid key422Invalid input, or not ready; the body has message and an errors object, one entry per field429More than 120 requests a minute
curl https://app.zicreach.com/api/v1/hooks/sample \
-H "Authorization: Bearer zk_your_key"
{
"data": [
{
"event": "lead.replied",
"created_at": "2026-09-24T10:31:00Z",
"data": {
"lead_id": "9d1c6a2f-8a3e-41c7-b2d4-6e5f0a9c1b22",
"email": "jane@example.com",
"first_name": "Jane",
"company": "Example Ltd"
}
}
]
}
Subscribe
/api/v1/hooksStarts sending an event to your URL. The URL must be https.
Needs a read and write API key.
Body
| Name | Type | Description |
|---|---|---|
target_urlrequired |
string | Where to POST events. https only. |
eventrequired |
string | One event key from List events. |
Errors
401Missing or invalid key403Read-only key used for a change, or a key made by someone who is no longer an owner or admin422Invalid input, or not ready; the body has message and an errors object, one entry per field429More than 120 requests a minute
curl -X POST https://app.zicreach.com/api/v1/hooks \
-H "Authorization: Bearer zk_your_key" \
-H "Content-Type: application/json" \
-d '{"target_url":"https://hooks.example.com/zicreach","event":"lead.replied"}'
{
"id": "9d1c6a2e-4b7f-4e0a-9a51-2f8c3b6d7e10",
"event": "lead.replied",
"target": "https://hooks.example.com/zicreach"
}
Unsubscribe
/api/v1/hooks/{id}Stops the subscription.
Needs a read and write API key.
Parameters
| Name | Type | Description |
|---|---|---|
idrequired path |
uuid | Subscription id. |
Errors
401Missing or invalid key403Read-only key used for a change, or a key made by someone who is no longer an owner or admin404Not found in this workspace422Invalid input, or not ready; the body has message and an errors object, one entry per field429More than 120 requests a minute
curl -X DELETE https://app.zicreach.com/api/v1/hooks/9d1c6a2e-4b7f-4e0a-9a51-2f8c3b6d7e10 \
-H "Authorization: Bearer zk_your_key"
{
"message": "Unsubscribed."
}
How deliveries work
Each event is an HTTP POST with a JSON body to your https URL. Any 2xx answer within 15 seconds counts as delivered; a failed delivery is tried again a few times, and an endpoint that fails 15 times in a row is paused.
Events
| Event | When |
|---|---|
lead.replied | A recipient replied |
lead.bounced | A message hard-bounced |
lead.unsubscribed | A recipient unsubscribed |
lead.opened | A recipient opened a message |
lead.clicked | A recipient clicked a link |
campaign.completed | A campaign finished sending |
Headers
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | ZicReach-Webhooks/1.0 |
X-ZicReach-Event | The event key, for example lead.replied |
X-ZicReach-Timestamp | Unix time of the delivery, in seconds |
X-ZicReach-Signature | With Signature (HMAC) authentication: v1= and the signature |
{
"event": "lead.replied",
"created_at": "2026-09-24T10:31:00+00:00",
"data": {
"lead_id": "9b1f2c3d-0000-4000-8000-000000000001",
"email": "jane@acme.com",
"first_name": "Jane",
"company": "Acme Ltd",
"campaign_id": "9b1f2c3d-0000-4000-8000-0000000000c1",
"subject": "Re: Quick question",
"reply_type": "interested",
"reply": "Hi Sara, yes, send it over. Thursday afternoon works for a call.\n\nJane",
"body": "Hi Sara, yes, send it over. Thursday afternoon works for a call.\n\nJane\n\nOn Mon, 28 Sept 2026, 2:00 pm Sara <sara@yourcompany.com> wrote:\n> Hi Jane,\n> Would you like me to send it over?"
}
}
Checking signatures
With Signature (HMAC) authentication, compute an HMAC SHA-256 of the timestamp, a dot and the raw body, using the endpoint's signing secret, and compare it with the header.
const crypto = require('crypto');
function verify(req, rawBody, secret) {
const ts = req.headers['x-zicreach-timestamp'];
const expected = 'v1=' + crypto.createHmac('sha256', secret).update(ts + '.' + rawBody).digest('hex');
const given = req.headers['x-zicreach-signature'] || '';
return given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}
$ts = $_SERVER['HTTP_X_ZICREACH_TIMESTAMP'] ?? '';
$body = file_get_contents('php://input');
$expected = 'v1=' . hash_hmac('sha256', $ts . '.' . $body, $secret);
$valid = hash_equals($expected, $_SERVER['HTTP_X_ZICREACH_SIGNATURE'] ?? '');