---
title: Migrating from V2 to V3 | API Docs
description: Step-by-step guide for upgrading your integration from the Linq V2 API to V3.
---

V3 is a full redesign — not a patch release. The core change is a shift from **synchronous** responses to an **asynchronous** model: the API accepts your request immediately and returns a `trace_id`, while webhooks tell you what happened. Along the way, the message structure, authentication header, endpoint paths, and error shape all changed.

This guide walks through every breaking change and new capability.

## Why migrate?

| You want to…                             | V2         | V3                                  |
| ---------------------------------------- | ---------- | ----------------------------------- |
| Know when messages actually fail         | ❌          | ✅ `message.failed` webhook          |
| Know exactly when messages are delivered | ❌          | ✅ `message.delivered` webhook       |
| Debug issues quickly                     | ❌          | ✅ `trace_id` everywhere             |
| Choose message protocols                 | ❌          | ✅ `preferred_service: iMessage`     |
| Send confetti/effects                    | ❌          | ✅ 15 effects (11 screen + 4 bubble) |
| Use custom emoji reactions               | ❌          | ✅ Any emoji                         |
| Send Office docs, ZIP files              | ❌          | ✅ 18 document types                 |
| Know when group names/icons change       | ❌          | ✅ Group metadata webhooks           |
| Reply to specific messages               | ⚠️ Limited | ✅ Full threading                    |
| Send voice memos                         | ❌          | ✅ Voice memo API                    |

---

## Quick migration checklist

- [ ] Replace `X-LINQ-INTEGRATION-TOKEN` header with `Authorization: Bearer <token>`
- [ ] Update all base paths from `/api/partner/v2/` to `/api/partner/v3/`
- [ ] Map existing V2 chat IDs to their V3 UUIDs via the [migration endpoint](#migrating-existing-chats)
- [ ] Convert message body: `text`/`attachments` → `parts` array
- [ ] Parse `success` boolean and log the `trace_id` on every response
- [ ] Update webhook handlers for the new payload structure; use `event_id` for deduplication
- [ ] Switch to the presigned upload flow for attachments
- [ ] Register webhook subscriptions programmatically (see [Webhook Subscriptions](/channel/imessage/guides/webhooks/subscriptions/index.md))

---

## Authentication

The token value stays the same — only the header name changes.

|        | V2                                  | V3                              |
| ------ | ----------------------------------- | ------------------------------- |
| Header | `X-LINQ-INTEGRATION-TOKEN: <token>` | `Authorization: Bearer <token>` |

Terminal window

```
# V2
curl -H "X-LINQ-INTEGRATION-TOKEN: $LINQ_API_KEY" ...


# V3
curl -H "Authorization: Bearer $LINQ_API_KEY" ...
```

See [Authentication](/channel/imessage/getting-started/authentication/index.md) for full details.

---

## Migrating existing chats

Chat IDs changed between versions: V2 chat IDs are integers (e.g. `12345`), while V3 chat IDs are UUIDs (e.g. `550e8400-e29b-41d4-a716-446655440000`). To keep an in-flight V2 conversation going under V3, look up its V3 chat ID with the migration endpoint:

```
GET /api/partner/v2/chats/{v2ChatID}/migrate_to_v3
```

This is a **V2 endpoint**, so it uses the V2 authentication header (`X-LINQ-INTEGRATION-TOKEN`), not the V3 `Authorization: Bearer` header. It takes no request body.

**Path parameters:**

| Param      | Type    | Required | Notes          |
| ---------- | ------- | -------- | -------------- |
| `v2ChatID` | integer | ✅        | The V2 chat ID |

Terminal window

```
curl https://api.linqapp.com/api/partner/v2/chats/12345/migrate_to_v3 \
  -H "X-LINQ-INTEGRATION-TOKEN: your_token_here"
```

The call is **idempotent** — migrating an already-migrated chat returns the same response as the first migration. Once you have the `v3_chat_id`, use it as the `chatId` in every V3 endpoint.

### Responses

**`200 OK`** — the chat was migrated (or was already migrated). Both cases return the same shape:

```
{
  "data": {
    "v3_chat_id": "550e8400-e29b-41d4-a716-446655440000",
    "v2_chat_id": 12345,
    "migrated": true
  }
}
```

**`422 Unprocessable Entity`** — the chat could not be migrated. `v3_chat_id` is `null` and `migrated` is `false`:

```
{
  "data": {
    "v3_chat_id": null,
    "v2_chat_id": 12345,
    "migrated": false
  }
}
```

**`404 Not Found`** — the chat doesn’t exist, or belongs to another organization. This returns the V2 error shape:

```
{
  "errors": [
    {
      "status": 404,
      "code": "not_found",
      "title": "Not found",
      "detail": "Chat not found"
    }
  ]
}
```

---

## Sending messages

### Endpoint path

The flat `/v2/chat_messages` endpoint is gone. V3 nests messages under the chat resource:

| Operation    | V2                       | V3                                 |
| ------------ | ------------------------ | ---------------------------------- |
| Send message | `POST /v2/chat_messages` | `POST /v3/chats/{chatId}/messages` |

### Message structure

V2 used top-level `text` and `attachments` fields. V3 uses a unified `parts` array where each part has an explicit `type`.

**V2 request body:**

```
{
  "chat_id": "chat-uuid",
  "text": "Hello, world!",
  "attachments": [{ "url": "https://example.com/image.jpg" }]
}
```

**V3 request body:**

```
{
  "message": {
    "parts": [
      { "type": "text", "value": "Hello, world!" },
      { "type": "media", "attachment_id": "att-uuid" }
    ]
  }
}
```

The `chat_id` moves from the body to the URL path. Message content is wrapped in a `message` object. Attachments must be pre-uploaded and referenced by `attachment_id` — see [Attachments](#attachments) below.

See [Sending Messages](/channel/imessage/guides/messaging/sending-messages/index.md) and the [Send Message](/channel/imessage/api/resources/chats/subresources/messages/methods/send/index.md) API reference for the full schema.

---

## Response format

V3 success responses return the resource directly — there is no `data` wrapper or `success` field on success.

**V2 response:**

```
{
  "data": { "chat_message": { "id": "msg-uuid", "text": "Hello" } },
  "status": 200
}
```

**V3 success response (send message):**

```
{
  "chat_id": "chat-uuid",
  "message": {
    "id": "msg-uuid",
    "delivery_status": "pending",
    "service": null,
    "parts": [...],
  }
}
```

**V3 error response:**

```
{
  "success": false,
  "error": {
    "status": 400,
    "code": 1005,
    "message": "invalid chatId format: must be a valid UUID",
    "doc_url": "https://docs.linqapp.com/error/codes/1xxx/1005/"
  },
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
```

Key changes:

- `success: false` and `trace_id` appear in **error responses only** — not in the success body
- `trace_id` on successful requests is returned in the `X-Trace-ID` **response header**; log it for every request
- `service` is `null` on the initial response; it is confirmed via the `message.delivered` webhook for iMessage and RCS — SMS does not produce a delivery receipt

---

## Async processing and trace IDs

V2 was synchronous — you waited for a result. V3 is asynchronous:

1. The API accepts your request and returns a `message_id` in the body plus a `trace_id` in the `X-Trace-ID` response header immediately.
2. Processing happens in the background.
3. Webhooks notify you of the outcome: `message.sent` → `message.delivered` or `message.failed`.

This means **silent failures are gone**. Any failure will produce a `message.failed` webhook with error details.

Always log the `trace_id` — it’s how you correlate an API request with its webhook events and is the first thing support will ask for.

See [Webhooks](/channel/imessage/guides/webhooks/index.md) and [Debugging](/channel/imessage/guides/platform/debugging/index.md) for more.

---

## Error handling

V3 uses structured error responses with numeric codes.

**V2 error:**

```
{ "error": "Invalid chat_id", "status": 400 }
```

**V3 error:**

```
{
  "success": false,
  "error": {
    "status": 400,
    "code": 1005,
    "message": "invalid chatId format: must be a valid UUID",
    "doc_url": "https://docs.linqapp.com/error/codes/1xxx/1005/"
  },
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}
```

Update your error handling to read `error.code` (numeric) rather than parsing the `error` string. See the [Errors](/channel/imessage/error/index.md) guide for the full code reference.

---

## Webhooks

V3 webhook payloads have a new structure with versioning and deduplication support.

**V2 payload:**

```
{ "event": "message.sent", "data": { "chat_message": { "id": "msg-uuid" } } }
```

**V3 payload:**

```
{
  "api_version": "v3",
  "event_type": "message.sent",
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "created_at": "2025-11-23T17:30:00Z",
  "trace_id": "abc123def456",
  "data": { "message_id": "msg-uuid", "service": "iMessage" }
}
```

Key differences:

- `event_type` replaces `event`
- `event_id` is a stable UUID per event — use it for deduplication; webhooks are delivered at-least-once with up to 10 retries over \~25 minutes
- `trace_id` links the event back to the originating API request
- `data` shape changed — update your payload destructuring
- Register and manage subscriptions via the API — see [Webhook Subscriptions](/channel/imessage/guides/webhooks/subscriptions/index.md)

**New events in V3:**

| Event                           | Description                         |
| ------------------------------- | ----------------------------------- |
| `message.delivered`             | Explicit delivery confirmation      |
| `message.failed`                | Delivery failure with error details |
| `chat.group_name_updated`       | Group name was changed              |
| `chat.group_icon_updated`       | Group icon was changed              |
| `chat.group_name_update_failed` | Group name update failed            |
| `chat.group_icon_update_failed` | Group icon update failed            |

See the [Webhook Events](/channel/imessage/guides/webhooks/events/index.md) guide for the full event reference.

---

## Attachments

V2 accepted a direct URL in the message body. V3 uses a two-step presigned upload flow.

**Step 1 — Request an upload URL:**

Terminal window

```
curl -X POST https://api.linqapp.com/api/partner/v3/attachments \
  -H "Authorization: Bearer $LINQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "filename": "document.pdf", "content_type": "application/pdf", "size_bytes": 1024000 }'
```

**Step 2 — Upload to the presigned S3 URL:**

Terminal window

```
curl -X PUT "https://uploads.linqapp.com/..." \
  -H "Content-Type: application/pdf" \
  --data-binary @document.pdf
```

**Step 3 — Reference the `attachment_id` in your message:**

```
{ "message": { "parts": [{ "type": "media", "attachment_id": "att-uuid" }] } }
```

V3 also expands supported document types from 2 to 18, including Word, Excel, PowerPoint, Pages, Numbers, Keynote, PDF, TXT, RTF, CSV, HTML, ePub, ZIP, and `.ics` files.

See the [Attachments](/channel/imessage/guides/messaging/attachments/index.md) guide for the full list and size limits.

---

## Group chats

V3 simplifies group creation by combining it with the initial message, and adds explicit participant management endpoints.

**V2 creation:**

```
POST /api/partner/v2/chats
{ "phone_numbers": ["+12025550001", "+12025550002"], "display_name": "My Group" }
```

**V3 creation:**

```
POST /api/partner/v3/chats
{
  "from": "+12025550100",
  "to": ["+12025550001", "+12025550002"],
  "message": {
    "parts": [{ "type": "text", "value": "Welcome to the group!" }]
  }
}
```

**New participant management endpoints:**

| Operation             | V3 Path                                  |
| --------------------- | ---------------------------------------- |
| Add participants      | `POST /v3/chats/{chatId}/participants`   |
| Remove participants   | `DELETE /v3/chats/{chatId}/participants` |
| Leave group chat      | `POST /v3/chats/{chatId}/leave`          |
| Update group metadata | `PUT /v3/chats/{chatId}`                 |

See the [Group Chats](/channel/imessage/guides/chats/group-chats/index.md) guide for details.

---

## Not supported yet

The following V2 features are not yet available in V3.

### Contacts API

V2 included a full contacts management API (`GET/POST/PUT/PATCH/DELETE /v2/contacts`). V3 does not currently have a contacts API. If your integration relies on creating or managing contacts via V2, you will need to continue using V2 for this or handle contact management outside of the Linq API for now.

---

## Full endpoint reference

**V2 → V3 path changes:**

| Operation                      | V2                                               | V3                                           |
| ------------------------------ | ------------------------------------------------ | -------------------------------------------- |
| List chats                     | `GET /v2/chats`                                  | `GET /v3/chats`                              |
| Create chat                    | `POST /v2/chats`                                 | `POST /v3/chats`                             |
| Get chat                       | `GET /v2/chats/{id}`                             | `GET /v3/chats/{chatId}`                     |
| Mark as read                   | `PUT /v2/chats/{id}/mark_as_read`                | `POST /v3/chats/{chatId}/read`               |
| Share contact card             | `POST /v2/chats/{id}/share_contact`              | `POST /v3/chats/{chatId}/share_contact_card` |
| Start typing                   | `POST /v2/chats/{id}/start_typing`               | `POST /v3/chats/{chatId}/typing`             |
| Stop typing                    | `DELETE /v2/chats/{id}/stop_typing`              | `DELETE /v3/chats/{chatId}/typing`           |
| List messages                  | `GET /v2/chats/{id}/chat_messages`               | `GET /v3/chats/{chatId}/messages`            |
| Send message                   | `POST /v2/chats/{id}/chat_messages`              | `POST /v3/chats/{chatId}/messages`           |
| Get message                    | `GET /v2/chats/{id}/chat_messages/{msgId}`       | `GET /v3/messages/{messageId}`               |
| Edit message                   | `POST /v2/chats/{id}/chat_messages/{msgId}/edit` | `PATCH /v3/messages/{messageId}`             |
| Delete message                 | `DELETE /v2/chats/{id}/chat_messages/{msgId}`    | `DELETE /v3/messages/{messageId}`            |
| Add/remove reaction            | `POST /v2/chat_messages/{id}/reactions`          | `POST /v3/messages/{messageId}/reactions`    |
| List phone numbers             | `GET /v2/phone_numbers`                          | `GET /v3/phone_numbers`                      |
| Update phone number forwarding | `PUT /v2/phone_numbers/{id}`                     | `PUT /v3/phone_numbers/{phoneNumberId}`      |
| Webhook subscriptions          | `/v2/webhook_subscriptions`                      | `/v3/webhook-subscriptions`                  |
| iMessage availability          | `POST /v2/i_message_availability/check`          | `POST /v3/capability/check_imessage`         |

To resolve an existing V2 chat’s integer ID to its V3 UUID, call `GET /v2/chats/{v2ChatID}/migrate_to_v3` — see [Migrating existing chats](#migrating-existing-chats).

**New in V3 (no V2 equivalent):**

| Operation               | V3 Path                                                             |
| ----------------------- | ------------------------------------------------------------------- |
| Update chat metadata    | `PUT /v3/chats/{chatId}`                                            |
| Add participants        | `POST /v3/chats/{chatId}/participants`                              |
| Remove participants     | `DELETE /v3/chats/{chatId}/participants`                            |
| Leave group chat        | `POST /v3/chats/{chatId}/leave`                                     |
| Send voice memo         | `POST /v3/chats/{chatId}/voicememo`                                 |
| Get thread              | `GET /v3/messages/{messageId}/thread`                               |
| Upload attachment       | `POST /v3/attachments`                                              |
| Get attachment metadata | `GET /v3/attachments/{attachmentId}`                                |
| …and more               | [Explore the full V3 API reference](/channel/imessage/api/index.md) |

---

## New V3 features

These capabilities have no V2 equivalent.

**[Protocol selection](/channel/imessage/guides/messaging/protocol-selection/index.md)** — Use `preferred_service` on any message to target a specific channel (`iMessage`, `RCS`, or `SMS`). Useful for iMessage-only features or compliance requirements.

**[Message effects](/channel/imessage/guides/messaging/message-effects/index.md)** — Attach iMessage screen effects (confetti, fireworks, lasers, sparkles) or bubble effects (slam, loud, gentle, invisible ink) via the `effect` object.

**[Text decorations](/channel/imessage/guides/messaging/sending-messages#text-decorations/index.md)** — Apply bold, italic, strikethrough, underline, and animated styles to character ranges within a text part. iMessage only.

**[Threaded replies](/channel/imessage/guides/messaging/sending-messages#replying-to-messages/index.md)** — Reply to a specific message using `reply_to: { message_id, part_index }`.

**[Custom reactions](/channel/imessage/guides/messaging/reactions/index.md)** — V2 supported the six standard iMessage tapbacks. V3 adds custom emoji reactions on top of those.

**[Voice memos](/channel/imessage/guides/messaging/voice-memos/index.md)** — Send audio messages as iMessage voice memo bubbles via `POST /v3/chats/{chatId}/voicememo`.

**[Participant management](/channel/imessage/guides/chats/group-chats/index.md)** — Add and remove participants from existing group chats, and leave a group, via dedicated endpoints.

**[Rich link previews](/channel/imessage/guides/messaging/rich-link-previews/index.md)** — Send a `link` part to render a rich preview card on iMessage and RCS.

**[Presigned attachment uploads](/channel/imessage/guides/messaging/attachments/index.md)** — Upload files via a presigned S3 URL and reference them by `attachment_id`. Supports 18 document types vs. 2 in V2.

…and more — [explore the full V3 API reference](/channel/imessage/api/index.md) for everything else V3 adds.
