Migrating from V2 to V3
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?
Section titled “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 | ❌ | ✅ 12 effects |
| 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
Section titled “Quick migration checklist”- Replace
X-LINQ-INTEGRATION-TOKENheader withAuthorization: Bearer <token> - Update all base paths from
/api/partner/v2/to/api/partner/v3/ - Convert message body:
text/attachments→partsarray - Parse
successboolean and log thetrace_idon every response - Update webhook handlers for the new payload structure; use
event_idfor deduplication - Switch to the presigned upload flow for attachments
- Register webhook subscriptions programmatically (see Webhook Subscriptions)
Authentication
Section titled “Authentication”The token value stays the same — only the header name changes.
| V2 | V3 | |
|---|---|---|
| Header | X-LINQ-INTEGRATION-TOKEN: <token> | Authorization: Bearer <token> |
# V2curl -H "X-LINQ-INTEGRATION-TOKEN: $LINQ_API_KEY" ...
# V3curl -H "Authorization: Bearer $LINQ_API_KEY" ...See Authentication for full details.
Sending messages
Section titled “Sending messages”Endpoint path
Section titled “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
Section titled “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 below.
See Sending Messages and the Send Message API reference for the full schema.
Response format
Section titled “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" }, "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"}Key changes:
success: falseandtrace_idappear in error responses only — not in the success bodytrace_idon successful requests is returned in theX-Trace-IDresponse header; log it for every requestserviceisnullon the initial response; it is confirmed via themessage.deliveredwebhook for iMessage and RCS — SMS does not produce a delivery receipt
Async processing and trace IDs
Section titled “Async processing and trace IDs”V2 was synchronous — you waited for a result. V3 is asynchronous:
- The API accepts your request and returns a
message_idin the body plus atrace_idin theX-Trace-IDresponse header immediately. - Processing happens in the background.
- Webhooks notify you of the outcome:
message.sent→message.deliveredormessage.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 and Debugging for more.
Error handling
Section titled “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" }, "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"}Update your error handling to read error.code (numeric) rather than parsing the error string. See the Errors guide for the full code reference.
Webhooks
Section titled “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_typereplaceseventevent_idis a stable UUID per event — use it for deduplication; webhooks are delivered at-least-once with up to 10 retries over ~25 minutestrace_idlinks the event back to the originating API requestdatashape changed — update your payload destructuring- Register and manage subscriptions via the API — see Webhook Subscriptions
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 guide for the full event reference.
Attachments
Section titled “Attachments”V2 accepted a direct URL in the message body. V3 uses a two-step presigned upload flow.
Step 1 — Request an upload URL:
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:
curl -X PUT "https://uploads.linqapp.com/..." \ -H "Content-Type: application/pdf" \ --data-binary @document.pdfStep 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 guide for the full list and size limits.
Group chats
Section titled “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 guide for details.
Not supported yet
Section titled “Not supported yet”The following V2 features are not yet available in V3.
Contacts API
Section titled “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.
Phone number forwarding
Section titled “Phone number forwarding”V2 exposed PUT /v2/phone_numbers/{id} to update a number’s forwarding configuration. This is not yet available in V3.
Full endpoint reference
Section titled “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 |
| Webhook subscriptions | /v2/webhook_subscriptions | /v3/webhook-subscriptions |
| iMessage availability | POST /v2/i_message_availability/check | POST /v3/chats/{chatId}/capability |
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} |
New V3 features
Section titled “New V3 features”These capabilities have no V2 equivalent.
Protocol selection — 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 — Attach iMessage screen effects (confetti, fireworks, lasers, sparkles) or bubble effects (slam, loud, gentle, invisible ink) via the effect object.
Text decorations — Apply bold, italic, strikethrough, underline, and animated styles to character ranges within a text part. iMessage only.
Threaded replies — Reply to a specific message using reply_to: { message_id, part_index }.
Custom reactions — V2 supported the six standard iMessage tapbacks. V3 adds custom emoji reactions on top of those.
Voice memos — Send audio messages as iMessage voice memo bubbles via POST /v3/chats/{chatId}/voicememo.
Participant management — Add and remove participants from existing group chats, and leave a group, via dedicated endpoints.
Rich link previews — Send a link part to render a rich preview card on iMessage and RCS.
Presigned attachment uploads — Upload files via a presigned S3 URL and reference them by attachment_id. Supports 18 document types vs. 2 in V2.