> ## 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.

# Migrating from legacy webhooks

> Update your webhook integration to the new event format

## Overview

We've expanded our webhook system with many new event types and a new event schema. This guide covers how to migrate from legacy webhooks to the new format.

<Note>
  Legacy webhooks only supported three campaign target events. The new system
  adds support for threads, messages, prospects, tags, statuses, and
  campaigns—all using a consistent dot notation format.
</Note>

## What's changed

### Event types

Legacy webhooks only supported three events in camelCase format:

* `targetContacted`
* `targetFollowUpSent`
* `targetReplied`

The new system uses dot notation (`resource.action`) and adds many more event types. See the [full event reference](#event-reference) below.

### Payload schema

The payload structure has changed significantly. The legacy format uses a flat structure with an `external` field, while the new format uses nested objects with a `prospect` field and additional context.

**Legacy format:**

```json theme={null}
{
  "event": "targetContacted",
  "version": "1.0",
  "timestamp": "2025-01-15T12:00:00.000Z",
  "platform": "twitter",
  "data": {
    "team": { ... },
    "accountLink": { ... },
    "message": { ... },
    "external": {
      "id": "hzcai5t59nn9vsck3rbuepyg",
      "platformId": "1954360649393295360",
      "platform": "twitter",
      "displayName": "John Doe",
      "username": "johndoe",
      "image": "https://pbs.twimg.com/...",
      "data": { }
    },
    "thread": { ... },
    "campaign": { ... }
  }
}
```

**New format:**

```json theme={null}
{
  "id": "ck9v2m5nj0xp4wq7ybftrae8",
  "seq": 42,
  "teamId": "hzcai5t59nn9vsck3rbuepyg",
  "type": "target.contacted",
  "timestamp": "2025-01-15T12:00:00.000Z",
  "version": "1.0",
  "data": {
    "campaign": { ... },
    "thread": { ... },
    "message": { ... },
    "prospect": {
      "id": "hzcai5t59nn9vsck3rbuepyg",
      "platformId": "1954360649393295360",
      "platform": "twitter",
      "displayName": "John Doe",
      "username": "johndoe",
      "image": "https://pbs.twimg.com/...",
      "context": {
        "tags": [...],
        "status": { ... },
        "notes": "...",
        "valuation": 1000
      }
    }
  }
}
```

Key differences:

* The `external` field is now `prospect` and includes full context (tags, status, notes, valuation)
* New envelope fields: `id`, `seq`, `teamId` at the top level
* The `event` field is now `type`
* The `platform` field has moved from the top level into the data objects

### Retry behavior

Legacy webhooks used aggressive retry logic (up to 10 retries over 24 hours). The new system attempts delivery once. Use the Events API to recover any missed events.

### Events API

A new endpoint is available to replay missed events from the last 7 days:

```bash theme={null}
GET /api/v1/events?afterSeq=0&limit=100
```

**Parameters:**

* `afterSeq` - Return events after this sequence number (omit to start from the beginning)
* `limit` - Maximum events to return (default: 100, max: 1000)

**Response:**

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

Use `lastSeq` as the `afterSeq` parameter for subsequent requests to paginate through all events.

## Migration steps

### 1. update your webhook handler

Since the schema is different, you'll need to update your handler to process the new format. You can switch over at any time—disabling "Use legacy events" in your webhook configuration will immediately start sending the new format.

```javascript theme={null}
function handleEvent(event) {
  // New format
  if (event.type === "target.contacted") {
    const { campaign, thread, message, prospect } = event.data;
    // prospect.context contains tags, status, notes, valuation
  }
}
```

### 2. implement missed event recovery

Add a background job that periodically polls the Events API to catch any missed webhooks:

```javascript theme={null}
let lastSeq = await getStoredSequenceNumber(); // Persist this value

async function syncMissedEvents() {
  const response = await fetch(
    `https://api.inboxapp.com/v1/events?afterSeq=${lastSeq}&limit=1000`,
    { headers: { Authorization: `Bearer ${API_TOKEN}` } },
  );

  const { events, hasMore, lastSeq: newSeq } = await response.json();

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

  if (newSeq) {
    lastSeq = newSeq;
    await storeSequenceNumber(lastSeq);
  }

  if (hasMore) {
    await syncMissedEvents(); // Continue pagination
  }
}
```

### 3. update webhook configuration

Once your handler supports the new format, go to **Settings → Webhooks** and disable the **"Use legacy events"** option on your webhook configuration.

### 4. subscribe to new event types

With the new system, you can now subscribe to many more event types beyond the original three. Update your webhook configuration to include any additional events you want to receive.

## Event reference

All available event types in the new system:

**Thread events**

* `thread.created` - A new thread was created
* `thread.deleted` - A thread was deleted
* `thread.assigned` - A thread was assigned to a team member (**deprecated** — use `prospect.assigneeChanged`)
* `thread.unassigned` - A thread was unassigned from a team member (**deprecated** — use `prospect.assigneeChanged`)
* `thread.archived` - A thread was archived
* `thread.unarchived` - A thread was unarchived
* `thread.typing` - Someone is typing in a thread

**Message events**

* `message.created` - A new message was sent or received
* `message.edited` - A message was edited
* `message.deleted` - A message was deleted
* `message.reactionAdded` - A reaction was added to a message
* `message.reactionRemoved` - A reaction was removed from a message

**Prospect events**

* `prospect.created` - A new prospect was added
* `prospect.statusChanged` - A prospect's pipeline status changed
* `prospect.tagsChanged` - A prospect's tags were updated
* `prospect.notesChanged` - A prospect's notes were updated
* `prospect.valuationChanged` - A prospect's valuation was updated
* `prospect.enriched` - A prospect was enriched with additional data
* `prospect.assigneeChanged` - A prospect's assignee was set, changed, or cleared

**Tag events**

* `tag.created` - A new tag was created
* `tag.updated` - A tag was updated
* `tag.deleted` - A tag was deleted

**Status events**

* `status.created` - A new pipeline status was created
* `status.updated` - A pipeline status was updated
* `status.deleted` - A pipeline status was deleted

**Campaign events**

* `campaign.created` - A new campaign was created
* `campaign.started` - A campaign was started
* `campaign.paused` - A campaign was paused
* `campaign.resumed` - A campaign was resumed
* `campaign.completed` - A campaign completed

**Target events** (previously the only events available)

* `target.contacted` - A prospect was first contacted by a campaign (previously `targetContacted`)
* `target.followUpSent` - A follow-up message was sent to a prospect (previously `targetFollowUpSent`)
* `target.replied` - A prospect replied to a campaign message (previously `targetReplied`)

## Need help?

If you have questions about migrating your webhooks, reach out to us at [support@inboxapp.com](mailto:support@inboxapp.com).
