> ## Documentation Index
> Fetch the complete documentation index at: https://docs.entryway.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks, Zapier and Make

> Send accepted invitations, replies, booked meetings and campaign events to Zapier, Make, n8n or your own server as they happen.

Webhooks send your workspace's LinkedIn outcomes to another tool the moment Growth Link records them. Use them to add people to your CRM, post to Slack, update a spreadsheet or start any automation.

Owners and admins manage webhooks in **Settings → Integrations & API**. A workspace can have up to 5 webhooks.

## Events

Each webhook sends only the events you choose.

| Event | Sent when |
| - | - |
| `invitation.accepted` | LinkedIn reports that someone accepted a connection invitation one of your campaigns sent. |
| `reply.received` | A new message from a person is recorded, by the live LinkedIn connection or an Inbox sync. |
| `meeting.booked` | A teammate marks a conversation as **Meeting booked** in Inbox. |
| `lead.added_to_campaign` | A person is enrolled when a campaign launches or more people are added to it. |
| `campaign.finished` | Every person in a launched campaign has finished the sequence, replied or been stopped (checked every 5 minutes). |

Events are sent only for things Growth Link actually recorded. A repeated notice from LinkedIn does not send a second event. Messages older than 72 hours when they are first imported (for example, old history from an Inbox sync) do not send `reply.received`. A webhook receives only events that happen after it was added.

## Connect Zapier

1. In Zapier, create a Zap and choose **Webhooks by Zapier** as the trigger, with the **Catch Hook** event.
2. Copy the webhook URL Zapier shows you (it starts with `https://hooks.zapier.com/hooks/catch/`).
3. In Growth Link, open **Settings → Integrations & API**, choose **Add webhook**, paste the URL and pick your events.
4. Choose **Send test** so Zapier receives a sample request, then continue setting up your Zap.

Webhooks by Zapier is available on Zapier's paid plans.

## Connect Make

1. In Make, add the **Webhooks** module **Custom webhook** as the first module of a scenario and create a webhook.
2. Copy its address (for example `https://hook.us1.make.com/…`).
3. In Growth Link, add it as a webhook and choose **Send test**. Make uses the test request to learn the data structure.

n8n works the same way with its **Webhook** trigger node (use the production URL, which must be public HTTPS).

## Payload

Every request is a `POST` with a JSON body like this:

```json theme={null}
{
  "id": "evt_6f1c2b9a0d4e8f7a6b5c4d3e",
  "type": "invitation.accepted",
  "api_version": "2026-10-17",
  "created_at": "2026-10-17T14:03:12.481Z",
  "occurred_at": "2026-10-17T14:03:12.402Z",
  "workspace_id": "c0a8012e-5b7d-4b0e-9f53-8f3c2a1d9e77",
  "data": {
    "person": {
      "id": "8d2f4a61-0c3b-4e5f-9a7d-1b2c3d4e5f60",
      "name": "Person Name",
      "linkedin_url": "https://www.linkedin.com/in/example/"
    },
    "campaign": {
      "id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
      "name": "Campaign name"
    }
  }
}
```

* `id` is unique per event and stays the same on every retry. Use it to ignore duplicates.
* `occurred_at` is when it happened; `created_at` is when Growth Link recorded the event.
* `person` is `null` for `campaign.finished`. `campaign` is `null` when the person is in no campaign. `name` and `linkedin_url` can be `null` when they are not known.
* Message text is **never** included, and no email addresses or phone numbers are sent.
* **Send test** sends `"type": "webhook.test"` with `"data": { "person": null, "campaign": null, "test": true }`.

Each request also has these headers:

| Header | Value |
| - | - |
| `Entryway-Signature` | `t=<unix seconds>,v1=<hex HMAC-SHA256>` |
| `Entryway-Event-Id` | The event `id` |
| `Entryway-Event-Type` | The event `type` |
| `Entryway-Delivery-Id` | This delivery (one event to one webhook) |
| `Content-Type` | `application/json` |

## Verify signatures

Each webhook has its own signing secret (it starts with `whsec_`). It is shown once when you add the webhook or rotate the secret; afterwards only its last four characters are shown. Zapier and Make work without it, but your own server should check it.

The signature is an HMAC-SHA256, keyed with the whole secret, of the timestamp, a period and the exact raw request body. Example for Node.js 18 or later with Express:

```js theme={null}
import crypto from 'node:crypto';
import express from 'express';

const secret = process.env.GROWTH_LINK_WEBHOOK_SECRET; // whsec_...
const app = express();

app.post('/growth-link', express.raw({ type: 'application/json' }), (req, res) => {
  const header = req.get('Entryway-Signature') || '';
  const parts = Object.fromEntries(header.split(',').map((part) => part.split('=')));
  const timestamp = Number(parts.t);
  const body = req.body.toString('utf8'); // the raw body, before JSON parsing

  // Reject old requests (5 minutes) to limit replays.
  if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > 300) return res.sendStatus(400);

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${body}`)
    .digest('hex');
  const given = Buffer.from(parts.v1 || '', 'utf8');
  if (given.length !== expected.length || !crypto.timingSafeEqual(given, Buffer.from(expected))) {
    return res.sendStatus(400);
  }

  const event = JSON.parse(body);
  // Deduplicate on event.id, then handle event.type.
  res.sendStatus(200); // answer quickly; do slow work afterwards
});

app.listen(3000);
```

**Rotate secret** replaces the secret immediately: update your server straight after rotating.

## Deliveries and retries

* Answer with any `2xx` status within 10 seconds. Anything else, a timeout or a redirect counts as a failure. Redirects are never followed.
* Failed deliveries are retried up to 8 attempts in total, after about 1 minute, 5 minutes, 30 minutes, 2 hours, 5 hours, 8 hours and 8 hours (about 24 hours altogether).
* Delivery is at least once and not strictly in order: use `id` to drop duplicates and `occurred_at` to order events.
* Deliveries are sent by a background job that runs every minute and sends a limited number per run, so a large campaign launch can take a while to arrive in full.
* **Deliveries** shows the last 50 attempts for a webhook: the event, status, HTTP status code and the first 200 characters of the response. Nothing else from your response is stored. Delivery history is kept for 30 days.
* If every delivery fails for more than 24 hours (and at least 8 attempts in a row), the webhook is **turned off** and its queued events are cancelled. A `410 Gone` response (for example, from a deleted Zap) turns it off immediately. Fix the receiver, choose **Send test**, then **Turn on**. Events from while it was off are not resent.

## URL rules

To protect your workspace and our servers, a webhook URL must:

* start with `https://` and use the standard port (443);
* use a public domain name, not an IP address, `localhost`, or an internal name such as `.local` or `.internal`;
* not contain a user name or password.

## Connecting AI tools

Webhooks push events out. To let an AI assistant or agent read and prepare work in Growth Link, use the [MCP endpoint with a personal access token](/workspace/mcp-access) instead.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.