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

# Webhooks overview

> Get notified in real time when events happen in your Inbox workspace

## How webhooks work

Webhooks send HTTP POST requests to your server when events occur in Inbox — a new message arrives, a thread is assigned, a campaign target replies, and more.

Instead of polling the API for changes, register a webhook URL and Inbox pushes events to you as they happen.

```
Your server ◀── POST /your-webhook ── Inbox
```

<Note>
  Configure webhooks in **Settings → Webhooks** in your Inbox dashboard. You choose which event types each webhook receives.
</Note>

## Event envelope

Every webhook delivery wraps event-specific data in a consistent envelope:

<ResponseField name="id" type="string" required>
  Unique event ID. Use this to deduplicate deliveries.
</ResponseField>

<ResponseField name="seq" type="number" required>
  Monotonically increasing sequence number per team. Use this to detect missed events and maintain ordering.
</ResponseField>

<ResponseField name="teamId" type="string" required>
  The team this event belongs to.
</ResponseField>

<ResponseField name="type" type="string" required>
  The event type in `resource.action` format (e.g., `thread.created`, `message.created`).
</ResponseField>

<ResponseField name="timestamp" type="string" required>
  ISO 8601 timestamp of when the event occurred.
</ResponseField>

<ResponseField name="version" type="string" required>
  Schema version. Currently always `"1.0"`.
</ResponseField>

<ResponseField name="data" type="object" required>
  The event payload. Structure varies by event type — see individual event pages for details.
</ResponseField>

<ResponseExample>
  ```json Example envelope theme={null}
  {
    "id": "ck9v2m5nj0xp4wq7ybftrae8",
    "seq": 42,
    "teamId": "hzcai5t59nn9vsck3rbuepyg",
    "type": "thread.created",
    "timestamp": "2025-01-15T10:30:00.000Z",
    "version": "1.0",
    "data": {
      "thread": { "..." },
      "prospect": { "..." }
    }
  }
  ```
</ResponseExample>

## Supported event types

Inbox supports 33 event types across 7 categories. Subscribe to individual events or entire categories.

### Thread events

| Event                                                      | Description                                                                                                                                          |
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`thread.created`](/webhooks/threads/thread-created)       | A new thread is created                                                                                                                              |
| [`thread.deleted`](/webhooks/threads/thread-deleted)       | A thread is deleted                                                                                                                                  |
| [`thread.assigned`](/webhooks/threads/thread-assigned)     | A thread is assigned to a team member. **Deprecated** — use [`prospect.assigneeChanged`](/webhooks/prospects/prospect-assignee-changed) instead.     |
| [`thread.unassigned`](/webhooks/threads/thread-unassigned) | A thread is unassigned from a team member. **Deprecated** — use [`prospect.assigneeChanged`](/webhooks/prospects/prospect-assignee-changed) instead. |
| [`thread.archived`](/webhooks/threads/thread-archived)     | A thread is marked as done                                                                                                                           |
| [`thread.unarchived`](/webhooks/threads/thread-unarchived) | A thread is reopened                                                                                                                                 |
| [`thread.typing`](/webhooks/threads/thread-typing)         | A prospect is typing in an X Chat thread                                                                                                             |

### Message events

| Event                                                                    | Description                          |
| ------------------------------------------------------------------------ | ------------------------------------ |
| [`message.created`](/webhooks/messages/message-created)                  | A message is sent or received        |
| [`message.edited`](/webhooks/messages/message-edited)                    | A message is edited                  |
| [`message.deleted`](/webhooks/messages/message-deleted)                  | A message is deleted                 |
| [`message.reactionAdded`](/webhooks/messages/message-reaction-added)     | A reaction is added to a message     |
| [`message.reactionRemoved`](/webhooks/messages/message-reaction-removed) | A reaction is removed from a message |

### Prospect events

| Event                                                                         | Description                                       |
| ----------------------------------------------------------------------------- | ------------------------------------------------- |
| [`prospect.created`](/webhooks/prospects/prospect-created)                    | A new prospect is created                         |
| [`prospect.statusChanged`](/webhooks/prospects/prospect-status-changed)       | A prospect's pipeline status changes              |
| [`prospect.tagsChanged`](/webhooks/prospects/prospect-tags-changed)           | Tags are added or removed from a prospect         |
| [`prospect.notesChanged`](/webhooks/prospects/prospect-notes-changed)         | A prospect's notes are updated                    |
| [`prospect.valuationChanged`](/webhooks/prospects/prospect-valuation-changed) | A prospect's valuation changes                    |
| [`prospect.assigneeChanged`](/webhooks/prospects/prospect-assignee-changed)   | A prospect's assignee is set, changed, or cleared |
| [`prospect.enriched`](/webhooks/prospects/prospect-enriched)                  | A prospect is enriched with fresh platform data   |

### Tag events

| Event                                       | Description          |
| ------------------------------------------- | -------------------- |
| [`tag.created`](/webhooks/tags/tag-created) | A new tag is created |
| [`tag.updated`](/webhooks/tags/tag-updated) | A tag is updated     |
| [`tag.deleted`](/webhooks/tags/tag-deleted) | A tag is deleted     |

### Status events

| Event                                                 | Description                      |
| ----------------------------------------------------- | -------------------------------- |
| [`status.created`](/webhooks/statuses/status-created) | A new pipeline status is created |
| [`status.updated`](/webhooks/statuses/status-updated) | A pipeline status is updated     |
| [`status.deleted`](/webhooks/statuses/status-deleted) | A pipeline status is deleted     |

### Campaign events

| Event                                                          | Description               |
| -------------------------------------------------------------- | ------------------------- |
| [`campaign.created`](/webhooks/campaigns/campaign-created)     | A new campaign is created |
| [`campaign.started`](/webhooks/campaigns/campaign-started)     | A campaign is started     |
| [`campaign.paused`](/webhooks/campaigns/campaign-paused)       | A campaign is paused      |
| [`campaign.resumed`](/webhooks/campaigns/campaign-resumed)     | A campaign is resumed     |
| [`campaign.completed`](/webhooks/campaigns/campaign-completed) | A campaign completes      |

### Target events

| Event                                                            | Description                                    |
| ---------------------------------------------------------------- | ---------------------------------------------- |
| [`target.contacted`](/webhooks/targets/target-contacted)         | A campaign target receives the initial message |
| [`target.followUpSent`](/webhooks/targets/target-follow-up-sent) | A follow-up message is sent to a target        |
| [`target.replied`](/webhooks/targets/target-replied)             | A campaign target replies                      |

## Delivery behavior

Inbox attempts delivery **once** with no retries. Responses are not checked — delivery is fire-and-forget.

Use the [Events API](#events-api) to fetch any events your webhook missed. Events are retained for 7 days.

### Handling webhooks

Your endpoint should:

1. Deduplicate using `event.id` in case of rare duplicate deliveries
2. Handle events idempotently
3. Track the latest `seq` value so you can backfill missed events via the Events API

<CodeGroup>
  ```typescript handler.ts theme={null}
  import express from 'express';

  const app = express();
  app.use(express.json());

  app.post('/webhooks/inbox', async (req, res) => {
    const event = req.body;

    // Return 200 immediately
    res.sendStatus(200);

    // Process asynchronously
    switch (event.type) {
      case 'message.created':
        await handleNewMessage(event.data);
        break;
      case 'prospect.assigneeChanged':
        await handleAssigneeChanged(event.data);
        break;
      case 'prospect.statusChanged':
        await syncProspectToCRM(event.data);
        break;
    }
  });

  app.listen(3000);
  ```

  ```javascript handler.js theme={null}
  const express = require('express');

  const app = express();
  app.use(express.json());

  app.post('/webhooks/inbox', async (req, res) => {
    const event = req.body;

    // Return 200 immediately
    res.sendStatus(200);

    // Process asynchronously
    switch (event.type) {
      case 'message.created':
        await handleNewMessage(event.data);
        break;
      case 'prospect.assigneeChanged':
        await handleAssigneeChanged(event.data);
        break;
      case 'prospect.statusChanged':
        await syncProspectToCRM(event.data);
        break;
    }
  });

  app.listen(3000);
  ```
</CodeGroup>

## Events API

The Events API lets you replay events from the last 7 days. Use it to recover missed webhooks or backfill data.

```bash cURL theme={null}
curl "https://inboxapp.com/api/v1/events?afterSeq=0&limit=100" \
  -H "Authorization: Bearer $INBOX_API_TOKEN"
```

**Response:**

```json theme={null}
{
  "events": [
    {
      "id": "ck9v2m5nj0xp4wq7ybftrae8",
      "seq": 1,
      "teamId": "hzcai5t59nn9vsck3rbuepyg",
      "type": "message.created",
      "timestamp": "2025-01-15T10:30:00.000Z",
      "version": "1.0",
      "data": { }
    }
  ],
  "hasMore": true,
  "lastSeq": 100
}
```

| Parameter  | Type     | Description                                                                 |
| ---------- | -------- | --------------------------------------------------------------------------- |
| `afterSeq` | `number` | Return events after this sequence number. Omit to start from the beginning. |
| `limit`    | `number` | Maximum events to return. Default: `100`, max: `1000`.                      |

Use `lastSeq` from the response as `afterSeq` in your next request to paginate through all events.

<Tip>
  Store the last processed `seq` value persistently. Run a background job that periodically polls the Events API using your stored `seq` to catch anything your webhook handler missed.
</Tip>

### Missed event recovery

<CodeGroup>
  ```typescript sync-events.ts theme={null}
  import axios from 'axios';

  const client = axios.create({
    baseURL: 'https://inboxapp.com/api/v1',
    headers: {
      'Authorization': `Bearer ${process.env.INBOX_API_TOKEN}`,
    },
  });

  async function syncMissedEvents(lastSeq: number): Promise<number> {
    let afterSeq = lastSeq;

    while (true) {
      const { data } = await client.get('/events', {
        params: { afterSeq, limit: 1000 },
      });

      for (const event of data.events) {
        await processEvent(event); // Deduplicate using event.id
      }

      if (!data.hasMore) {
        return data.lastSeq ?? afterSeq;
      }

      afterSeq = data.lastSeq;
    }
  }
  ```

  ```javascript sync-events.js theme={null}
  const axios = require('axios');

  const client = axios.create({
    baseURL: 'https://inboxapp.com/api/v1',
    headers: {
      'Authorization': `Bearer ${process.env.INBOX_API_TOKEN}`,
    },
  });

  async function syncMissedEvents(lastSeq) {
    let afterSeq = lastSeq;

    while (true) {
      const { data } = await client.get('/events', {
        params: { afterSeq, limit: 1000 },
      });

      for (const event of data.events) {
        await processEvent(event); // Deduplicate using event.id
      }

      if (!data.hasMore) {
        return data.lastSeq ?? afterSeq;
      }

      afterSeq = data.lastSeq;
    }
  }
  ```
</CodeGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Thread events" icon="messages-square" href="/webhooks/threads/thread-created">
    Get notified about new, assigned, and archived threads
  </Card>

  <Card title="Message events" icon="message-circle" href="/webhooks/messages/message-created">
    Track messages, edits, and reactions
  </Card>

  <Card title="Campaign events" icon="send" href="/webhooks/campaigns/campaign-created">
    Monitor campaign lifecycle changes
  </Card>

  <Card title="Migrating from legacy webhooks" icon="arrow-right" href="/webhooks/migrating-from-legacy">
    Update from the old event format
  </Card>
</CardGroup>
