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

# Webhooks overview

> Receive real-time notifications when database events occur

Subscribe to database events and receive an HTTP POST at your URL instead of polling. To create a subscription, see [Create Webhook](/api/webhooks/create-webhook).

## Event types

| Event            | Fires when                                   |
| ---------------- | -------------------------------------------- |
| `record.created` | A record is added to a table                 |
| `record.updated` | An existing record is modified               |
| `record.deleted` | A record is deleted                          |
| `table.created`  | A table is added                             |
| `table.updated`  | A table's properties (e.g. name) change      |
| `table.deleted`  | A table is deleted                           |
| `field.created`  | A field is added to a table                  |
| `field.updated`  | A field's properties or configuration change |
| `field.deleted`  | A field is removed                           |

## Payload

Each delivery is a JSON POST body:

```json theme={null}
{
  "id": "evt_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "type": "record.created",
  "timestamp": "2025-01-15T10:30:00.000Z",
  "data": {
    "tableId": "tbl_abc123",
    "recordIds": ["rec_xyz789"],
    "records": [
      { "id": "rec_xyz789", "data": { "Name": "John Doe", "Email": "john@example.com" } }
    ]
  },
  "metadata": {
    "webhookId": 123,
    "organizationId": 456,
    "baseId": "base_def456",
    "attempt": 1,
    "source": "ui"
  }
}
```

| Field                     | Type   | Description                                            |
| ------------------------- | ------ | ------------------------------------------------------ |
| `id`                      | string | Unique event ID — use for idempotency                  |
| `type`                    | string | Event type (e.g. `record.created`)                     |
| `timestamp`               | string | ISO 8601 time the event occurred                       |
| `data`                    | object | Event-specific data (see below)                        |
| `metadata.webhookId`      | number | ID of the webhook subscription                         |
| `metadata.organizationId` | number | Your organization ID                                   |
| `metadata.baseId`         | string | The database ID                                        |
| `metadata.attempt`        | number | Delivery attempt (1 = first)                           |
| `metadata.source`         | string | Origin: `ui`, `public_api`, `zapier`, `make`, `import` |

The `data` object varies by event family:

| Event family | `data` fields                                             | Also present on `*.updated` |
| ------------ | --------------------------------------------------------- | --------------------------- |
| Record       | `tableId`, `recordIds`, `records[]`                       | `previousRecords[]`         |
| Table        | `tableId`, `table` `{ id, name, order, primaryFieldId }`  | `previousTable`             |
| Field        | `tableId`, `fieldId`, `field` `{ id, name, type, order }` | `previousField`             |

## Request headers

| Header                | Value                                |
| --------------------- | ------------------------------------ |
| `Content-Type`        | `application/json`                   |
| `X-Webhook-Signature` | HMAC-SHA256 signature of the payload |
| `X-Webhook-Event`     | The event type                       |
| `X-Webhook-ID`        | The unique event ID                  |
| `User-Agent`          | `Zite-Webhooks/1.0`                  |

## Verifying signatures

Every request carries an `X-Webhook-Signature` header — an HMAC-SHA256 of the payload keyed with your webhook secret (returned once on [creation](/api/webhooks/create-webhook)). Verify it before processing:

```javascript theme={null}
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// In your webhook handler
app.post('/webhooks/zite', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  if (!verifyWebhookSignature(req.body, signature, YOUR_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  res.status(200).send('OK'); // then process asynchronously
});
```

## Delivery, retries & limits

* Return a **2xx within 30 s** to acknowledge; process asynchronously if needed. Use HTTPS.
* Non-2xx or timeout is retried up to **5 times** with exponential backoff: 5s, 10s, 20s, 40s, 80s.
* Deduplicate on the event `id` — retries reuse it.

| Limit                     | Value            |
| ------------------------- | ---------------- |
| Max webhooks per database | 100              |
| Max events per webhook    | 20               |
| Max URL length            | 2,048 characters |
| Max payload size          | 256 KB           |
| Delivery timeout          | 30 seconds       |
