Skip to content

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.

List events

GET/api/v1/hooks/events

Every event you can subscribe to.

Any API key, read-only included.

Errors

  • 401Missing or invalid key
  • 429More than 120 requests a minute
Request
curl https://app.zicreach.com/api/v1/hooks/events \
  -H "Authorization: Bearer zk_your_key"
Response200
{
    "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

GET/api/v1/hooks/sample

An example of what an event delivers, for mapping fields.

Any API key, read-only included.

Parameters

NameTypeDescription
event query string Event key. Default lead.replied.

Errors

  • 401Missing or invalid key
  • 422Invalid input, or not ready; the body has message and an errors object, one entry per field
  • 429More than 120 requests a minute
Request
curl https://app.zicreach.com/api/v1/hooks/sample \
  -H "Authorization: Bearer zk_your_key"
Response200
{
    "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

POST/api/v1/hooks

Starts sending an event to your URL. The URL must be https.

Needs a read and write API key.

Body

NameTypeDescription
target_urlrequired string Where to POST events. https only.
eventrequired string One event key from List events.

Errors

  • 401Missing or invalid key
  • 403Read-only key used for a change, or a key made by someone who is no longer an owner or admin
  • 422Invalid input, or not ready; the body has message and an errors object, one entry per field
  • 429More than 120 requests a minute
Request
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"}'
Response201
{
    "id": "9d1c6a2e-4b7f-4e0a-9a51-2f8c3b6d7e10",
    "event": "lead.replied",
    "target": "https://hooks.example.com/zicreach"
}

Unsubscribe

DELETE/api/v1/hooks/{id}

Stops the subscription.

Needs a read and write API key.

Parameters

NameTypeDescription
idrequired path uuid Subscription id.

Errors

  • 401Missing or invalid key
  • 403Read-only key used for a change, or a key made by someone who is no longer an owner or admin
  • 404Not found in this workspace
  • 422Invalid input, or not ready; the body has message and an errors object, one entry per field
  • 429More than 120 requests a minute
Request
curl -X DELETE https://app.zicreach.com/api/v1/hooks/9d1c6a2e-4b7f-4e0a-9a51-2f8c3b6d7e10 \
  -H "Authorization: Bearer zk_your_key"
Response200
{
    "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

EventWhen
lead.repliedA recipient replied
lead.bouncedA message hard-bounced
lead.unsubscribedA recipient unsubscribed
lead.openedA recipient opened a message
lead.clickedA recipient clicked a link
campaign.completedA campaign finished sending

Headers

HeaderValue
Content-Typeapplication/json
User-AgentZicReach-Webhooks/1.0
X-ZicReach-EventThe event key, for example lead.replied
X-ZicReach-TimestampUnix time of the delivery, in seconds
X-ZicReach-SignatureWith Signature (HMAC) authentication: v1= and the signature
Payload: lead.replied
{
    "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.

Node.js
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));
}
PHP
$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'] ?? '');