# Chats ## Create a new chat `client.chats.create(ChatCreateParamsbody, RequestOptionsoptions?): ChatCreateResponse` **post** `/v3/chats` Create a new chat with specified participants and send an initial message. The initial message is required when creating a chat. ## Message Effects You can add iMessage effects to make your messages more expressive. Effects are optional and can be either screen effects (full-screen animations) or bubble effects (message bubble animations). **Screen Effects:** `confetti`, `fireworks`, `lasers`, `sparkles`, `celebration`, `hearts`, `love`, `balloons`, `happy_birthday`, `echo`, `spotlight` **Bubble Effects:** `slam`, `loud`, `gentle`, `invisible` Only one effect type can be applied per message. ## Inline Text Decorations (iMessage only) Use the `text_decorations` array on a text part to apply styling and animations to character ranges. Each decoration specifies a `range: [start, end)` and exactly one of `style` or `animation`. **Styles:** `bold`, `italic`, `strikethrough`, `underline` **Animations:** `big`, `small`, `shake`, `nod`, `explode`, `ripple`, `bloom`, `jitter` ```json { "type": "text", "value": "Hello world", "text_decorations": [ { "range": [0, 5], "style": "bold" }, { "range": [6, 11], "animation": "shake" } ] } ``` **Note:** Style ranges (bold, italic, etc.) may overlap, but animation ranges must not overlap with other animations or styles. Text decorations only render for iMessage recipients. For SMS/RCS, text decorations are not applied. ## First-Message Link Restriction To protect sender deliverability, the **first outbound message** of a new chat cannot be a link. The request is rejected with `400` (error code `1005`) when: - The message contains a `link` part (explicit rich-preview link), or - Any `text` part contains a URL. This rule applies only to `POST /v3/chats`. Follow-up messages on an existing chat (`POST /v3/chats/{chatId}/messages`) are not subject to this restriction. ### Parameters - `body: ChatCreateParams` - `from: string` Sender phone number in E.164 format. Must be a phone number that the authenticated partner has permission to send from. - `message: MessageContent` Message content container. Groups all message-related fields together, separating the "what" (message content) from the "where" (routing fields like from/to). - `parts: Array` Array of message parts. Each part can be text, media, or link. Parts are displayed in order. Text and media can be mixed freely, but a `link` part must be the only part in the message. **Rich Link Previews:** - Use a `link` part to send a URL with a rich preview card - A `link` part must be the **only** part in the message - To send a URL as plain text (no preview), use a `text` part instead **Supported Media:** - Images: .jpg, .jpeg, .png, .gif, .heic, .heif, .tif, .tiff, .bmp - Videos: .mp4, .mov, .m4v, .mpeg, .mpg, .3gp - Audio: .m4a, .mp3, .aac, .caf, .wav, .aiff, .amr - Documents: .pdf, .txt, .rtf, .csv, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .pages, .numbers, .key, .epub, .zip, .html, .htm - Contact & Calendar: .vcf, .ics **Audio:** - Audio files (.m4a, .mp3, .aac, .caf, .wav, .aiff, .amr) are fully supported as media parts - To send audio as an **iMessage voice memo bubble** (inline playback UI), use the dedicated `/v3/chats/{chatId}/voicememo` endpoint instead **Validation Rules:** - A `link` part must be the **only** part in the message. It cannot be combined with text or media parts. - Consecutive text parts are not allowed. Text parts must be separated by media parts. For example, [text, text] is invalid, but [text, media, text] is valid. - Maximum of **100 parts** total. - Media parts using a public `url` (downloaded by the server on send) are capped at **40**. Parts using `attachment_id` or presigned URLs are exempt from this sub-limit. For bulk media sends exceeding 40 files, pre-upload via `POST /v3/attachments` and reference by `attachment_id` or `download_url`. - `TextPart` - `type: "text"` Indicates this is a text message part - `"text"` - `value: string` The text content of the message. This value is sent as-is with no parsing or transformation — Markdown syntax will be delivered as plain text. Use `text_decorations` to apply inline formatting and animations (iMessage only). - `text_decorations?: Array` Optional array of text decorations applied to character ranges in the `value` field (iMessage only). Each decoration specifies a character range `[start, end)` and exactly one of `style` or `animation`. **Styles:** `bold`, `italic`, `strikethrough`, `underline` **Animations:** `big`, `small`, `shake`, `nod`, `explode`, `ripple`, `bloom`, `jitter` Style ranges may overlap (e.g. bold + italic on the same text), but animation ranges must not overlap with other animations or styles. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* **Note:** Text decorations only render for iMessage recipients. For SMS/RCS, text decorations are not applied. - `range: Array` Character range `[start, end)` in the `value` string where the decoration applies. `start` is inclusive, `end` is exclusive. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* - `animation?: "big" | "small" | "shake" | 5 more` Animated text effect to apply. Mutually exclusive with `style`. - `"big"` - `"small"` - `"shake"` - `"nod"` - `"explode"` - `"ripple"` - `"bloom"` - `"jitter"` - `style?: "bold" | "italic" | "strikethrough" | "underline"` Text style to apply. Mutually exclusive with `animation`. - `"bold"` - `"italic"` - `"strikethrough"` - `"underline"` - `MediaPart` - `type: "media"` Indicates this is a media attachment part - `"media"` - `attachment_id?: string` Reference to a file pre-uploaded via `POST /v3/attachments` (optional). The file is already stored, so sends using this ID skip the download step — useful when sending the same file to many recipients. Either `url` or `attachment_id` must be provided, but not both. - `url?: string` Any publicly accessible HTTPS URL to the media file. The server downloads and sends the file automatically — no pre-upload step required. **Size limit:** 10MB maximum for URL-based downloads. For larger files (up to 100MB), use the pre-upload flow: `POST /v3/attachments` to get a presigned URL, upload directly, then reference by `attachment_id`. **Requirements:** - URL must use HTTPS - File content must be a supported format (the server validates the actual file content) **Supported formats:** - Images: .jpg, .jpeg, .png, .gif, .heic, .heif, .tif, .tiff, .bmp - Videos: .mp4, .mov, .m4v, .mpeg, .mpg, .3gp - Audio: .m4a, .mp3, .aac, .caf, .wav, .aiff, .amr - Documents: .pdf, .txt, .rtf, .csv, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .pages, .numbers, .key, .epub, .zip, .html, .htm - Contact & Calendar: .vcf, .ics **Tip:** Audio sent here appears as a regular file attachment. To send audio as an iMessage voice memo bubble (with inline playback), use `/v3/chats/{chatId}/voicememo`. For repeated sends of the same file, use `attachment_id` to avoid redundant downloads. Either `url` or `attachment_id` must be provided, but not both. - `LinkPart` - `type: "link"` Indicates this is a rich link preview part - `"link"` - `value: string` URL to send with a rich link preview. The recipient will see an inline card with the page's title, description, and preview image (when available). A `link` part must be the **only** part in the message. To send a URL as plain text (no preview card), use a `text` part instead. - `effect?: MessageEffect` iMessage effect to apply to this message (screen or bubble effect) - `name?: string` Name of the effect. Common values: - Screen effects: confetti, fireworks, lasers, sparkles, celebration, hearts, love, balloons, happy_birthday, echo, spotlight - Bubble effects: slam, loud, gentle, invisible - `type?: "screen" | "bubble"` Type of effect - `"screen"` - `"bubble"` - `idempotency_key?: string` Optional idempotency key for this message. Use this to prevent duplicate sends of the same message. - `preferred_service?: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `reply_to?: ReplyTo` Reply to another message to create a threaded conversation - `message_id: string` The ID of the message to reply to - `part_index?: number` The specific message part to reply to (0-based index). Defaults to 0 (first part) if not provided. Use this when replying to a specific part of a multipart message. - `to: Array` Array of recipient handles (phone numbers in E.164 format or email addresses). For individual chats, provide one recipient. For group chats, provide multiple. ### Returns - `ChatCreateResponse` Response for creating a new chat with an initial message - `chat: Chat` - `id: string` Unique identifier for the created chat (UUID) - `display_name: string | null` Display name for the chat. Defaults to a comma-separated list of recipient handles. Can be updated for group chats. - `handles: Array` List of participants in the chat. Always contains at least two handles (your phone number and the other participant). - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `"active"` - `"left"` - `"removed"` - `is_group: boolean` Whether this is a group chat - `message: SentMessage` A message that was sent (used in CreateChat and SendMessage responses) - `id: string` Message identifier (UUID) - `created_at: string` When the message was created - `delivery_status: "pending" | "queued" | "sent" | 2 more` Current delivery status of a message - `"pending"` - `"queued"` - `"sent"` - `"delivered"` - `"failed"` - `is_read: boolean` Whether the message has been read - `parts: Array` Message parts in order (text, media, and link) - `TextPartResponse` A text message part - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `"love"` - `"like"` - `"dislike"` - `"laugh"` - `"emphasize"` - `"question"` - `"custom"` - `"sticker"` - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `file_name?: string` Filename of the sticker - `height?: number` Sticker image height in pixels - `mime_type?: string` MIME type of the sticker image - `url?: string` Presigned URL for downloading the sticker image (expires in 1 hour). - `width?: number` Sticker image width in pixels - `type: "text"` Indicates this is a text message part - `"text"` - `value: string` The text content - `text_decorations?: Array | null` Text decorations applied to character ranges in the value - `range: Array` Character range `[start, end)` in the `value` string where the decoration applies. `start` is inclusive, `end` is exclusive. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* - `animation?: "big" | "small" | "shake" | 5 more` Animated text effect to apply. Mutually exclusive with `style`. - `"big"` - `"small"` - `"shake"` - `"nod"` - `"explode"` - `"ripple"` - `"bloom"` - `"jitter"` - `style?: "bold" | "italic" | "strikethrough" | "underline"` Text style to apply. Mutually exclusive with `animation`. - `"bold"` - `"italic"` - `"strikethrough"` - `"underline"` - `MediaPartResponse` A media attachment part - `id: string` Unique attachment identifier - `filename: string` Original filename - `mime_type: string` MIME type of the file - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `size_bytes: number` File size in bytes - `type: "media"` Indicates this is a media attachment part - `"media"` - `url: string` Presigned URL for downloading the attachment (expires in 1 hour). - `LinkPartResponse` A rich link preview part - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `type: "link"` Indicates this is a rich link preview part - `"link"` - `value: string` The URL - `sent_at: string | null` When the message was actually sent (null if still queued) - `delivered_at?: string | null` When the message was delivered - `effect?: MessageEffect | null` iMessage effect applied to a message (screen or bubble effect) - `name?: string` Name of the effect. Common values: - Screen effects: confetti, fireworks, lasers, sparkles, celebration, hearts, love, balloons, happy_birthday, echo, spotlight - Bubble effects: slam, loud, gentle, invisible - `type?: "screen" | "bubble"` Type of effect - `"screen"` - `"bubble"` - `from_handle?: ChatHandle | null` The sender of this message as a full handle object - `preferred_service?: ServiceType | null` Messaging service type - `reply_to?: ReplyTo | null` Indicates this message is a threaded reply to another message - `message_id: string` The ID of the message to reply to - `part_index?: number` The specific message part to reply to (0-based index). Defaults to 0 (first part) if not provided. Use this when replying to a specific part of a multipart message. - `service?: ServiceType | null` Messaging service type - `service: ServiceType` Messaging service type - `health_score?: HealthScore | null` **[BETA]** Health assessment for a chat. Higher `score` is healthier. `null` when a score isn't available yet. Scoring may change during beta. - `reason: string` Short summary of what's affecting the score. Empty when the score is 100. - `score: number` Health score from 0 to 100. Higher is healthier. - `updated_at: string` When this health score was last computed. ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); const chat = await client.chats.create({ from: '+12052535597', message: { parts: [{ type: 'text', value: 'Hello! How can I help you today?' }] }, to: ['+12052532136'], }); console.log(chat.chat); ``` #### Response ```json { "chat": { "id": "94c6bf33-31d9-40e3-a0e9-f94250ecedb9", "display_name": "+14155551234, +14155559876", "handles": [ { "id": "550e8400-e29b-41d4-a716-446655440010", "handle": "+14155551234", "joined_at": "2025-05-21T15:30:00.000Z", "service": "iMessage", "is_me": true, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" }, { "id": "550e8400-e29b-41d4-a716-446655440011", "handle": "+14155559876", "joined_at": "2025-05-21T15:30:00.000Z", "service": "iMessage", "is_me": false, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" } ], "is_group": false, "message": { "id": "69a37c7d-af4f-4b5e-af42-e28e98ce873a", "created_at": "2025-10-23T13:07:55.019-05:00", "delivery_status": "pending", "is_read": false, "parts": [ { "reactions": [ { "handle": { "id": "69a37c7d-af4f-4b5e-af42-e28e98ce873a", "handle": "+15551234567", "joined_at": "2025-05-21T15:30:00.000-05:00", "service": "iMessage", "is_me": false, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" }, "is_me": false, "type": "love", "custom_emoji": null, "sticker": { "file_name": "sticker.png", "height": 420, "mime_type": "image/png", "url": "https://cdn.linqapp.com/attachments/a1b2c3d4/sticker.png?signature=...", "width": 420 } } ], "type": "text", "value": "Hello!", "text_decorations": [ { "range": [ 0, 5 ], "animation": "shake", "style": "bold" } ] } ], "sent_at": null, "delivered_at": null, "effect": { "name": "confetti", "type": "screen" }, "from_handle": { "id": "550e8400-e29b-41d4-a716-446655440000", "handle": "+15551234567", "joined_at": "2025-05-21T15:30:00.000-05:00", "service": "iMessage", "is_me": false, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" }, "preferred_service": "iMessage", "reply_to": { "message_id": "550e8400-e29b-41d4-a716-446655440000", "part_index": 0 }, "service": "iMessage" }, "service": "iMessage", "health_score": { "reason": "Not enough engagement", "score": 35, "updated_at": "2026-05-01T18:28:25Z" } } } ``` ## List all chats `client.chats.listChats(ChatListChatsParamsquery?, RequestOptionsoptions?): ListChatsPagination` **get** `/v3/chats` Retrieves a paginated list of chats for the authenticated partner. **Filtering:** - If `from` is provided, returns chats for that specific phone number - If `from` is omitted, returns chats across all phone numbers owned by the partner - If `to` is provided, only returns chats where the specified handle is a participant **Pagination:** - Use `limit` to control page size (default: 20, max: 100) - The response includes `next_cursor` for fetching the next page - When `next_cursor` is `null`, there are no more results to fetch - Pass the `next_cursor` value as the `cursor` parameter for the next request **Example pagination flow:** 1. First request: `GET /v3/chats?from=%2B12223334444&limit=20` 1. Response includes `next_cursor: "20"` (more results exist) 1. Next request: `GET /v3/chats?from=%2B12223334444&limit=20&cursor=20` 1. Response includes `next_cursor: null` (no more results) ### Parameters - `query: ChatListChatsParams` - `cursor?: string` Pagination cursor from the previous response's `next_cursor` field. Omit this parameter for the first page of results. - `from?: string` Phone number to filter chats by. Returns chats made from this phone number. Must be in E.164 format (e.g., `+13343284472`). The `+` is automatically URL-encoded by HTTP clients. If omitted, returns chats across all phone numbers owned by the partner. - `limit?: number` Maximum number of chats to return per page - `to?: string` Filter chats by a participant handle. Only returns chats where this handle is a participant. Can be an E.164 phone number (e.g., `+13343284472`) or an email address (e.g., `user@example.com`). For phone numbers, the `+` is automatically URL-encoded by HTTP clients. ### Returns - `Chat` - `id: string` Unique identifier for the chat - `created_at: string` When the chat was created - `display_name: string | null` Display name for the chat. Defaults to a comma-separated list of recipient handles. Can be updated for group chats. - `handles: Array` List of chat participants with full handle details. Always contains at least two handles (your phone number and the other participant). - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `"active"` - `"left"` - `"removed"` - `is_archived: boolean` Whether the chat is archived - `is_group: boolean` Whether this is a group chat - `updated_at: string` When the chat was last updated - `health_score?: HealthScore | null` **[BETA]** Health assessment for a chat. Higher `score` is healthier. `null` when a score isn't available yet. Scoring may change during beta. - `reason: string` Short summary of what's affecting the score. Empty when the score is 100. - `score: number` Health score from 0 to 100. Higher is healthier. - `updated_at: string` When this health score was last computed. - `service?: ServiceType | null` Messaging service type ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); // Automatically fetches more pages as needed. for await (const chat of client.chats.listChats()) { console.log(chat.id); } ``` #### Response ```json { "chats": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "created_at": "2024-01-15T10:30:00Z", "display_name": "+14155551234, +14155559876", "handles": [ { "id": "550e8400-e29b-41d4-a716-446655440010", "handle": "+14155551234", "joined_at": "2025-05-21T15:30:00.000Z", "service": "iMessage", "is_me": true, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" }, { "id": "550e8400-e29b-41d4-a716-446655440011", "handle": "+14155559876", "joined_at": "2025-05-21T15:30:00.000Z", "service": "iMessage", "is_me": false, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" } ], "is_archived": true, "is_group": true, "updated_at": "2024-01-15T10:30:00Z", "health_score": { "reason": "Not enough engagement", "score": 35, "updated_at": "2026-05-01T18:28:25Z" }, "service": "iMessage" } ], "next_cursor": "next_cursor" } ``` ## Get a chat by ID `client.chats.retrieve(stringchatID, RequestOptionsoptions?): Chat` **get** `/v3/chats/{chatId}` Retrieve a chat by its unique identifier. ### Parameters - `chatID: string` ### Returns - `Chat` - `id: string` Unique identifier for the chat - `created_at: string` When the chat was created - `display_name: string | null` Display name for the chat. Defaults to a comma-separated list of recipient handles. Can be updated for group chats. - `handles: Array` List of chat participants with full handle details. Always contains at least two handles (your phone number and the other participant). - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `"active"` - `"left"` - `"removed"` - `is_archived: boolean` Whether the chat is archived - `is_group: boolean` Whether this is a group chat - `updated_at: string` When the chat was last updated - `health_score?: HealthScore | null` **[BETA]** Health assessment for a chat. Higher `score` is healthier. `null` when a score isn't available yet. Scoring may change during beta. - `reason: string` Short summary of what's affecting the score. Empty when the score is 100. - `score: number` Health score from 0 to 100. Higher is healthier. - `updated_at: string` When this health score was last computed. - `service?: ServiceType | null` Messaging service type ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); const chat = await client.chats.retrieve('550e8400-e29b-41d4-a716-446655440000'); console.log(chat.id); ``` #### Response ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "created_at": "2024-01-15T10:30:00Z", "display_name": "+14155551234, +14155559876", "handles": [ { "id": "550e8400-e29b-41d4-a716-446655440010", "handle": "+14155551234", "joined_at": "2025-05-21T15:30:00.000Z", "service": "iMessage", "is_me": true, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" }, { "id": "550e8400-e29b-41d4-a716-446655440011", "handle": "+14155559876", "joined_at": "2025-05-21T15:30:00.000Z", "service": "iMessage", "is_me": false, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" } ], "is_archived": true, "is_group": true, "updated_at": "2024-01-15T10:30:00Z", "health_score": { "reason": "Not enough engagement", "score": 35, "updated_at": "2026-05-01T18:28:25Z" }, "service": "iMessage" } ``` ## Update a chat `client.chats.update(stringchatID, ChatUpdateParamsbody, RequestOptionsoptions?): ChatUpdateResponse` **put** `/v3/chats/{chatId}` Update chat properties such as display name and group chat icon. Listen for `chat.group_name_updated`, `chat.group_icon_updated`, `chat.group_name_update_failed`, or `chat.group_icon_update_failed` webhook events to confirm the outcome. ### Parameters - `chatID: string` - `body: ChatUpdateParams` - `display_name?: string` New display name for the chat (group chats only) - `group_chat_icon?: string` URL of an image to set as the group chat icon (group chats only) ### Returns - `ChatUpdateResponse` - `chat_id?: string` - `status?: string` ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); const chat = await client.chats.update('550e8400-e29b-41d4-a716-446655440000', { display_name: 'Team Discussion', }); console.log(chat.chat_id); ``` #### Response ```json { "chat_id": "550e8400-e29b-41d4-a716-446655440000", "status": "pending" } ``` ## Mark chat as read `client.chats.markAsRead(stringchatID, RequestOptionsoptions?): void` **post** `/v3/chats/{chatId}/read` Mark all messages in a chat as read. ### Parameters - `chatID: string` ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); await client.chats.markAsRead('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e'); ``` #### Response ```json { "error": { "status": 401, "code": 2004, "message": "Unauthorized - missing or invalid authentication token" }, "success": false } ``` ## Leave a group chat `client.chats.leaveChat(stringchatID, RequestOptionsoptions?): ChatLeaveChatResponse` **post** `/v3/chats/{chatId}/leave` Removes your phone number from a group chat. Once you leave, you will no longer receive messages from the group and all interaction endpoints (send message, typing, mark read, etc.) will return 409. A `participant.removed` webhook will fire once the leave has been processed. **Supported** - iMessage group chats with 4 or more active participants (including yourself) **Not supported** - DM (1-on-1) chats — use the chat directly to continue the conversation ### Parameters - `chatID: string` ### Returns - `ChatLeaveChatResponse` - `message?: string` - `status?: string` - `trace_id?: string` ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); const response = await client.chats.leaveChat('550e8400-e29b-41d4-a716-446655440000'); console.log(response.trace_id); ``` #### Response ```json { "message": "Leave group chat queued", "status": "accepted", "trace_id": "trace_id" } ``` ## Share your contact card with a chat `client.chats.shareContactCard(stringchatID, RequestOptionsoptions?): void` **post** `/v3/chats/{chatId}/share_contact_card` Share your contact information (Name and Photo Sharing) with a chat. **Note:** A contact card must be configured before sharing. You can set up your contact card via the [Contact Card API](#tag/Contact-Card) or on the [Linq dashboard](https://dashboard.linqapp.com/contact-cards). ### Parameters - `chatID: string` ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); await client.chats.shareContactCard('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e'); ``` #### Response ```json { "error": { "status": 401, "code": 2004, "message": "Unauthorized - missing or invalid authentication token" }, "success": false } ``` ## Send a voice memo to a chat `client.chats.sendVoicememo(stringchatID, ChatSendVoicememoParamsbody, RequestOptionsoptions?): ChatSendVoicememoResponse` **post** `/v3/chats/{chatId}/voicememo` Send an audio file as an **iMessage voice memo bubble** to all participants in a chat. Voice memos appear with iMessage's native inline playback UI, unlike regular audio attachments sent via media parts which appear as downloadable files. **Supported audio formats:** - MP3 (audio/mpeg) - M4A (audio/x-m4a, audio/mp4) - AAC (audio/aac) - CAF (audio/x-caf) - Core Audio Format - WAV (audio/wav) - AIFF (audio/aiff, audio/x-aiff) - AMR (audio/amr) ### Parameters - `chatID: string` - `body: ChatSendVoicememoParams` - `attachment_id?: string` Reference to a voice memo file pre-uploaded via `POST /v3/attachments`. The file is already stored, so sends using this ID skip the download step. Either `voice_memo_url` or `attachment_id` must be provided, but not both. - `voice_memo_url?: string` URL of the voice memo audio file. Must be a publicly accessible HTTPS URL. Either `voice_memo_url` or `attachment_id` must be provided, but not both. ### Returns - `ChatSendVoicememoResponse` Response for sending a voice memo to a chat - `voice_memo: VoiceMemo` - `id: string` Message identifier - `chat: Chat` - `id: string` Chat identifier - `handles: Array` Chat participants - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `"active"` - `"left"` - `"removed"` - `is_active: boolean` Whether the chat is active - `is_group: boolean` Whether this is a group chat - `service: ServiceType` Messaging service type - `created_at: string` When the voice memo was created - `from: string` Sender phone number - `status: string` Current delivery status - `to: Array` Recipient handles (phone numbers or email addresses) - `voice_memo: VoiceMemo` - `id: string` Attachment identifier - `filename: string` Original filename - `mime_type: string` Audio MIME type - `size_bytes: number` File size in bytes - `url: string` CDN URL for downloading the voice memo - `duration_ms?: number | null` Duration in milliseconds - `service?: ServiceType | null` Messaging service type ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); const response = await client.chats.sendVoicememo('f19ee7b8-8533-4c5c-83ec-4ef8d6d1ddbd', { voice_memo_url: 'https://example.com/voice-memo.m4a', }); console.log(response.voice_memo); ``` #### Response ```json { "voice_memo": { "id": "69a37c7d-af4f-4b5e-af42-e28e98ce873a", "chat": { "id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", "handles": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "handle": "+15551234567", "joined_at": "2025-05-21T15:30:00.000-05:00", "service": "iMessage", "is_me": false, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" } ], "is_active": true, "is_group": true, "service": "iMessage" }, "created_at": "2019-12-27T18:11:19.117Z", "from": "+12052535597", "status": "queued", "to": [ "+12052532136" ], "voice_memo": { "id": "550e8400-e29b-41d4-a716-446655440000", "filename": "voice-memo.m4a", "mime_type": "audio/x-m4a", "size_bytes": 524288, "url": "https://cdn.linqapp.com/voice-memos/abc123.m4a", "duration_ms": 15000 }, "service": "iMessage" } } ``` ## Domain Types ### Chat - `Chat` - `id: string` Unique identifier for the chat - `created_at: string` When the chat was created - `display_name: string | null` Display name for the chat. Defaults to a comma-separated list of recipient handles. Can be updated for group chats. - `handles: Array` List of chat participants with full handle details. Always contains at least two handles (your phone number and the other participant). - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `"active"` - `"left"` - `"removed"` - `is_archived: boolean` Whether the chat is archived - `is_group: boolean` Whether this is a group chat - `updated_at: string` When the chat was last updated - `health_score?: HealthScore | null` **[BETA]** Health assessment for a chat. Higher `score` is healthier. `null` when a score isn't available yet. Scoring may change during beta. - `reason: string` Short summary of what's affecting the score. Empty when the score is 100. - `score: number` Health score from 0 to 100. Higher is healthier. - `updated_at: string` When this health score was last computed. - `service?: ServiceType | null` Messaging service type ### Link Part - `LinkPart` - `type: "link"` Indicates this is a rich link preview part - `"link"` - `value: string` URL to send with a rich link preview. The recipient will see an inline card with the page's title, description, and preview image (when available). A `link` part must be the **only** part in the message. To send a URL as plain text (no preview card), use a `text` part instead. ### Media Part - `MediaPart` - `type: "media"` Indicates this is a media attachment part - `"media"` - `attachment_id?: string` Reference to a file pre-uploaded via `POST /v3/attachments` (optional). The file is already stored, so sends using this ID skip the download step — useful when sending the same file to many recipients. Either `url` or `attachment_id` must be provided, but not both. - `url?: string` Any publicly accessible HTTPS URL to the media file. The server downloads and sends the file automatically — no pre-upload step required. **Size limit:** 10MB maximum for URL-based downloads. For larger files (up to 100MB), use the pre-upload flow: `POST /v3/attachments` to get a presigned URL, upload directly, then reference by `attachment_id`. **Requirements:** - URL must use HTTPS - File content must be a supported format (the server validates the actual file content) **Supported formats:** - Images: .jpg, .jpeg, .png, .gif, .heic, .heif, .tif, .tiff, .bmp - Videos: .mp4, .mov, .m4v, .mpeg, .mpg, .3gp - Audio: .m4a, .mp3, .aac, .caf, .wav, .aiff, .amr - Documents: .pdf, .txt, .rtf, .csv, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .pages, .numbers, .key, .epub, .zip, .html, .htm - Contact & Calendar: .vcf, .ics **Tip:** Audio sent here appears as a regular file attachment. To send audio as an iMessage voice memo bubble (with inline playback), use `/v3/chats/{chatId}/voicememo`. For repeated sends of the same file, use `attachment_id` to avoid redundant downloads. Either `url` or `attachment_id` must be provided, but not both. ### Message Content - `MessageContent` Message content container. Groups all message-related fields together, separating the "what" (message content) from the "where" (routing fields like from/to). - `parts: Array` Array of message parts. Each part can be text, media, or link. Parts are displayed in order. Text and media can be mixed freely, but a `link` part must be the only part in the message. **Rich Link Previews:** - Use a `link` part to send a URL with a rich preview card - A `link` part must be the **only** part in the message - To send a URL as plain text (no preview), use a `text` part instead **Supported Media:** - Images: .jpg, .jpeg, .png, .gif, .heic, .heif, .tif, .tiff, .bmp - Videos: .mp4, .mov, .m4v, .mpeg, .mpg, .3gp - Audio: .m4a, .mp3, .aac, .caf, .wav, .aiff, .amr - Documents: .pdf, .txt, .rtf, .csv, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .pages, .numbers, .key, .epub, .zip, .html, .htm - Contact & Calendar: .vcf, .ics **Audio:** - Audio files (.m4a, .mp3, .aac, .caf, .wav, .aiff, .amr) are fully supported as media parts - To send audio as an **iMessage voice memo bubble** (inline playback UI), use the dedicated `/v3/chats/{chatId}/voicememo` endpoint instead **Validation Rules:** - A `link` part must be the **only** part in the message. It cannot be combined with text or media parts. - Consecutive text parts are not allowed. Text parts must be separated by media parts. For example, [text, text] is invalid, but [text, media, text] is valid. - Maximum of **100 parts** total. - Media parts using a public `url` (downloaded by the server on send) are capped at **40**. Parts using `attachment_id` or presigned URLs are exempt from this sub-limit. For bulk media sends exceeding 40 files, pre-upload via `POST /v3/attachments` and reference by `attachment_id` or `download_url`. - `TextPart` - `type: "text"` Indicates this is a text message part - `"text"` - `value: string` The text content of the message. This value is sent as-is with no parsing or transformation — Markdown syntax will be delivered as plain text. Use `text_decorations` to apply inline formatting and animations (iMessage only). - `text_decorations?: Array` Optional array of text decorations applied to character ranges in the `value` field (iMessage only). Each decoration specifies a character range `[start, end)` and exactly one of `style` or `animation`. **Styles:** `bold`, `italic`, `strikethrough`, `underline` **Animations:** `big`, `small`, `shake`, `nod`, `explode`, `ripple`, `bloom`, `jitter` Style ranges may overlap (e.g. bold + italic on the same text), but animation ranges must not overlap with other animations or styles. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* **Note:** Text decorations only render for iMessage recipients. For SMS/RCS, text decorations are not applied. - `range: Array` Character range `[start, end)` in the `value` string where the decoration applies. `start` is inclusive, `end` is exclusive. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* - `animation?: "big" | "small" | "shake" | 5 more` Animated text effect to apply. Mutually exclusive with `style`. - `"big"` - `"small"` - `"shake"` - `"nod"` - `"explode"` - `"ripple"` - `"bloom"` - `"jitter"` - `style?: "bold" | "italic" | "strikethrough" | "underline"` Text style to apply. Mutually exclusive with `animation`. - `"bold"` - `"italic"` - `"strikethrough"` - `"underline"` - `MediaPart` - `type: "media"` Indicates this is a media attachment part - `"media"` - `attachment_id?: string` Reference to a file pre-uploaded via `POST /v3/attachments` (optional). The file is already stored, so sends using this ID skip the download step — useful when sending the same file to many recipients. Either `url` or `attachment_id` must be provided, but not both. - `url?: string` Any publicly accessible HTTPS URL to the media file. The server downloads and sends the file automatically — no pre-upload step required. **Size limit:** 10MB maximum for URL-based downloads. For larger files (up to 100MB), use the pre-upload flow: `POST /v3/attachments` to get a presigned URL, upload directly, then reference by `attachment_id`. **Requirements:** - URL must use HTTPS - File content must be a supported format (the server validates the actual file content) **Supported formats:** - Images: .jpg, .jpeg, .png, .gif, .heic, .heif, .tif, .tiff, .bmp - Videos: .mp4, .mov, .m4v, .mpeg, .mpg, .3gp - Audio: .m4a, .mp3, .aac, .caf, .wav, .aiff, .amr - Documents: .pdf, .txt, .rtf, .csv, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .pages, .numbers, .key, .epub, .zip, .html, .htm - Contact & Calendar: .vcf, .ics **Tip:** Audio sent here appears as a regular file attachment. To send audio as an iMessage voice memo bubble (with inline playback), use `/v3/chats/{chatId}/voicememo`. For repeated sends of the same file, use `attachment_id` to avoid redundant downloads. Either `url` or `attachment_id` must be provided, but not both. - `LinkPart` - `type: "link"` Indicates this is a rich link preview part - `"link"` - `value: string` URL to send with a rich link preview. The recipient will see an inline card with the page's title, description, and preview image (when available). A `link` part must be the **only** part in the message. To send a URL as plain text (no preview card), use a `text` part instead. - `effect?: MessageEffect` iMessage effect to apply to this message (screen or bubble effect) - `name?: string` Name of the effect. Common values: - Screen effects: confetti, fireworks, lasers, sparkles, celebration, hearts, love, balloons, happy_birthday, echo, spotlight - Bubble effects: slam, loud, gentle, invisible - `type?: "screen" | "bubble"` Type of effect - `"screen"` - `"bubble"` - `idempotency_key?: string` Optional idempotency key for this message. Use this to prevent duplicate sends of the same message. - `preferred_service?: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `reply_to?: ReplyTo` Reply to another message to create a threaded conversation - `message_id: string` The ID of the message to reply to - `part_index?: number` The specific message part to reply to (0-based index). Defaults to 0 (first part) if not provided. Use this when replying to a specific part of a multipart message. ### Text Part - `TextPart` - `type: "text"` Indicates this is a text message part - `"text"` - `value: string` The text content of the message. This value is sent as-is with no parsing or transformation — Markdown syntax will be delivered as plain text. Use `text_decorations` to apply inline formatting and animations (iMessage only). - `text_decorations?: Array` Optional array of text decorations applied to character ranges in the `value` field (iMessage only). Each decoration specifies a character range `[start, end)` and exactly one of `style` or `animation`. **Styles:** `bold`, `italic`, `strikethrough`, `underline` **Animations:** `big`, `small`, `shake`, `nod`, `explode`, `ripple`, `bloom`, `jitter` Style ranges may overlap (e.g. bold + italic on the same text), but animation ranges must not overlap with other animations or styles. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* **Note:** Text decorations only render for iMessage recipients. For SMS/RCS, text decorations are not applied. - `range: Array` Character range `[start, end)` in the `value` string where the decoration applies. `start` is inclusive, `end` is exclusive. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* - `animation?: "big" | "small" | "shake" | 5 more` Animated text effect to apply. Mutually exclusive with `style`. - `"big"` - `"small"` - `"shake"` - `"nod"` - `"explode"` - `"ripple"` - `"bloom"` - `"jitter"` - `style?: "bold" | "italic" | "strikethrough" | "underline"` Text style to apply. Mutually exclusive with `animation`. - `"bold"` - `"italic"` - `"strikethrough"` - `"underline"` ### Chat Create Response - `ChatCreateResponse` Response for creating a new chat with an initial message - `chat: Chat` - `id: string` Unique identifier for the created chat (UUID) - `display_name: string | null` Display name for the chat. Defaults to a comma-separated list of recipient handles. Can be updated for group chats. - `handles: Array` List of participants in the chat. Always contains at least two handles (your phone number and the other participant). - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `"active"` - `"left"` - `"removed"` - `is_group: boolean` Whether this is a group chat - `message: SentMessage` A message that was sent (used in CreateChat and SendMessage responses) - `id: string` Message identifier (UUID) - `created_at: string` When the message was created - `delivery_status: "pending" | "queued" | "sent" | 2 more` Current delivery status of a message - `"pending"` - `"queued"` - `"sent"` - `"delivered"` - `"failed"` - `is_read: boolean` Whether the message has been read - `parts: Array` Message parts in order (text, media, and link) - `TextPartResponse` A text message part - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `"love"` - `"like"` - `"dislike"` - `"laugh"` - `"emphasize"` - `"question"` - `"custom"` - `"sticker"` - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `file_name?: string` Filename of the sticker - `height?: number` Sticker image height in pixels - `mime_type?: string` MIME type of the sticker image - `url?: string` Presigned URL for downloading the sticker image (expires in 1 hour). - `width?: number` Sticker image width in pixels - `type: "text"` Indicates this is a text message part - `"text"` - `value: string` The text content - `text_decorations?: Array | null` Text decorations applied to character ranges in the value - `range: Array` Character range `[start, end)` in the `value` string where the decoration applies. `start` is inclusive, `end` is exclusive. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* - `animation?: "big" | "small" | "shake" | 5 more` Animated text effect to apply. Mutually exclusive with `style`. - `"big"` - `"small"` - `"shake"` - `"nod"` - `"explode"` - `"ripple"` - `"bloom"` - `"jitter"` - `style?: "bold" | "italic" | "strikethrough" | "underline"` Text style to apply. Mutually exclusive with `animation`. - `"bold"` - `"italic"` - `"strikethrough"` - `"underline"` - `MediaPartResponse` A media attachment part - `id: string` Unique attachment identifier - `filename: string` Original filename - `mime_type: string` MIME type of the file - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `size_bytes: number` File size in bytes - `type: "media"` Indicates this is a media attachment part - `"media"` - `url: string` Presigned URL for downloading the attachment (expires in 1 hour). - `LinkPartResponse` A rich link preview part - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `type: "link"` Indicates this is a rich link preview part - `"link"` - `value: string` The URL - `sent_at: string | null` When the message was actually sent (null if still queued) - `delivered_at?: string | null` When the message was delivered - `effect?: MessageEffect | null` iMessage effect applied to a message (screen or bubble effect) - `name?: string` Name of the effect. Common values: - Screen effects: confetti, fireworks, lasers, sparkles, celebration, hearts, love, balloons, happy_birthday, echo, spotlight - Bubble effects: slam, loud, gentle, invisible - `type?: "screen" | "bubble"` Type of effect - `"screen"` - `"bubble"` - `from_handle?: ChatHandle | null` The sender of this message as a full handle object - `preferred_service?: ServiceType | null` Messaging service type - `reply_to?: ReplyTo | null` Indicates this message is a threaded reply to another message - `message_id: string` The ID of the message to reply to - `part_index?: number` The specific message part to reply to (0-based index). Defaults to 0 (first part) if not provided. Use this when replying to a specific part of a multipart message. - `service?: ServiceType | null` Messaging service type - `service: ServiceType` Messaging service type - `health_score?: HealthScore | null` **[BETA]** Health assessment for a chat. Higher `score` is healthier. `null` when a score isn't available yet. Scoring may change during beta. - `reason: string` Short summary of what's affecting the score. Empty when the score is 100. - `score: number` Health score from 0 to 100. Higher is healthier. - `updated_at: string` When this health score was last computed. ### Chat Update Response - `ChatUpdateResponse` - `chat_id?: string` - `status?: string` ### Chat Leave Chat Response - `ChatLeaveChatResponse` - `message?: string` - `status?: string` - `trace_id?: string` ### Chat Send Voicememo Response - `ChatSendVoicememoResponse` Response for sending a voice memo to a chat - `voice_memo: VoiceMemo` - `id: string` Message identifier - `chat: Chat` - `id: string` Chat identifier - `handles: Array` Chat participants - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `"active"` - `"left"` - `"removed"` - `is_active: boolean` Whether the chat is active - `is_group: boolean` Whether this is a group chat - `service: ServiceType` Messaging service type - `created_at: string` When the voice memo was created - `from: string` Sender phone number - `status: string` Current delivery status - `to: Array` Recipient handles (phone numbers or email addresses) - `voice_memo: VoiceMemo` - `id: string` Attachment identifier - `filename: string` Original filename - `mime_type: string` Audio MIME type - `size_bytes: number` File size in bytes - `url: string` CDN URL for downloading the voice memo - `duration_ms?: number | null` Duration in milliseconds - `service?: ServiceType | null` Messaging service type # Participants ## Add a participant to a chat `client.chats.participants.add(stringchatID, ParticipantAddParamsbody, RequestOptionsoptions?): ParticipantAddResponse` **post** `/v3/chats/{chatId}/participants` Add a new participant to an existing group chat. **Requirements:** - Group chats only (3+ existing participants) - New participant must support the same messaging service as the group - Cross-service additions not allowed (e.g., can't add RCS-only user to iMessage group) - For cross-service scenarios, create a new chat instead ### Parameters - `chatID: string` - `body: ParticipantAddParams` - `handle: string` Phone number (E.164 format) or email address of the participant to add ### Returns - `ParticipantAddResponse` - `message?: string` - `status?: string` - `trace_id?: string` ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); const response = await client.chats.participants.add('550e8400-e29b-41d4-a716-446655440000', { handle: '+12052499136', }); console.log(response.trace_id); ``` #### Response ```json { "message": "Participant addition queued", "status": "accepted", "trace_id": "trace_id" } ``` ## Remove a participant from a chat `client.chats.participants.remove(stringchatID, ParticipantRemoveParamsbody, RequestOptionsoptions?): ParticipantRemoveResponse` **delete** `/v3/chats/{chatId}/participants` Remove a participant from an existing group chat. **Requirements:** - Group chats only - Must have 3+ participants after removal ### Parameters - `chatID: string` - `body: ParticipantRemoveParams` - `handle: string` Phone number (E.164 format) or email address of the participant to remove ### Returns - `ParticipantRemoveResponse` - `message?: string` - `status?: string` - `trace_id?: string` ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); const participant = await client.chats.participants.remove('550e8400-e29b-41d4-a716-446655440000', { handle: '+12052499136', }); console.log(participant.trace_id); ``` #### Response ```json { "message": "Participant removal queued", "status": "accepted", "trace_id": "trace_id" } ``` ## Domain Types ### Participant Add Response - `ParticipantAddResponse` - `message?: string` - `status?: string` - `trace_id?: string` ### Participant Remove Response - `ParticipantRemoveResponse` - `message?: string` - `status?: string` - `trace_id?: string` # Typing ## Start typing indicator `client.chats.typing.start(stringchatID, RequestOptionsoptions?): void` **post** `/v3/chats/{chatId}/typing` Send a typing indicator to show that someone is typing in the chat. ## Behavior & Limitations Typing indicators are best-effort signals with the following limitations: - **Active conversations only:** The recipient must have sent or received a message in this chat within the **last 5 minutes**. If the chat is inactive, the request is still accepted (`204`) but the indicator will not reach the recipient's device. - **No delivery guarantee:** Even for active chats, a `204` response only indicates the request was accepted for processing. - **Group chats not supported:** Attempting to start a typing indicator in a group chat will return a `403` error. ### Parameters - `chatID: string` ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); await client.chats.typing.start('550e8400-e29b-41d4-a716-446655440000'); ``` #### Response ```json { "error": { "status": 400, "code": 1002, "message": "Phone number must be in E.164 format" }, "success": false } ``` ## Stop typing indicator `client.chats.typing.stop(stringchatID, RequestOptionsoptions?): void` **delete** `/v3/chats/{chatId}/typing` Stop the typing indicator for the chat. Typing indicators are automatically stopped when a message is sent, so calling this endpoint after sending a message is unnecessary. See the `POST` endpoint above for behavior details and limitations. **Note:** Group chats are not supported and will return a `403` error. ### Parameters - `chatID: string` ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); await client.chats.typing.stop('550e8400-e29b-41d4-a716-446655440000'); ``` #### Response ```json { "error": { "status": 400, "code": 1002, "message": "Phone number must be in E.164 format" }, "success": false } ``` # Messages ## Send a message to an existing chat `client.chats.messages.send(stringchatID, MessageSendParamsbody, RequestOptionsoptions?): MessageSendResponse` **post** `/v3/chats/{chatId}/messages` Send a message to an existing chat. Use this endpoint when you already have a chat ID and want to send additional messages to it. ## Message Effects You can add iMessage effects to make your messages more expressive. Effects are optional and can be either screen effects (full-screen animations) or bubble effects (message bubble animations). **Screen Effects:** `confetti`, `fireworks`, `lasers`, `sparkles`, `celebration`, `hearts`, `love`, `balloons`, `happy_birthday`, `echo`, `spotlight` **Bubble Effects:** `slam`, `loud`, `gentle`, `invisible` Only one effect type can be applied per message. ## Inline Text Decorations (iMessage only) Use the `text_decorations` array on a text part to apply styling and animations to character ranges. Each decoration specifies a `range: [start, end)` and exactly one of `style` or `animation`. **Styles:** `bold`, `italic`, `strikethrough`, `underline` **Animations:** `big`, `small`, `shake`, `nod`, `explode`, `ripple`, `bloom`, `jitter` ```json { "type": "text", "value": "Hello world", "text_decorations": [ { "range": [0, 5], "style": "bold" }, { "range": [6, 11], "animation": "shake" } ] } ``` **Note:** Style ranges (bold, italic, etc.) may overlap, but animation ranges must not overlap with other animations or styles. Text decorations only render for iMessage recipients. For SMS/RCS, text decorations are not applied. ### Parameters - `chatID: string` - `body: MessageSendParams` - `message: MessageContent` Message content container. Groups all message-related fields together, separating the "what" (message content) from the "where" (routing fields like from/to). - `parts: Array` Array of message parts. Each part can be text, media, or link. Parts are displayed in order. Text and media can be mixed freely, but a `link` part must be the only part in the message. **Rich Link Previews:** - Use a `link` part to send a URL with a rich preview card - A `link` part must be the **only** part in the message - To send a URL as plain text (no preview), use a `text` part instead **Supported Media:** - Images: .jpg, .jpeg, .png, .gif, .heic, .heif, .tif, .tiff, .bmp - Videos: .mp4, .mov, .m4v, .mpeg, .mpg, .3gp - Audio: .m4a, .mp3, .aac, .caf, .wav, .aiff, .amr - Documents: .pdf, .txt, .rtf, .csv, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .pages, .numbers, .key, .epub, .zip, .html, .htm - Contact & Calendar: .vcf, .ics **Audio:** - Audio files (.m4a, .mp3, .aac, .caf, .wav, .aiff, .amr) are fully supported as media parts - To send audio as an **iMessage voice memo bubble** (inline playback UI), use the dedicated `/v3/chats/{chatId}/voicememo` endpoint instead **Validation Rules:** - A `link` part must be the **only** part in the message. It cannot be combined with text or media parts. - Consecutive text parts are not allowed. Text parts must be separated by media parts. For example, [text, text] is invalid, but [text, media, text] is valid. - Maximum of **100 parts** total. - Media parts using a public `url` (downloaded by the server on send) are capped at **40**. Parts using `attachment_id` or presigned URLs are exempt from this sub-limit. For bulk media sends exceeding 40 files, pre-upload via `POST /v3/attachments` and reference by `attachment_id` or `download_url`. - `TextPart` - `type: "text"` Indicates this is a text message part - `"text"` - `value: string` The text content of the message. This value is sent as-is with no parsing or transformation — Markdown syntax will be delivered as plain text. Use `text_decorations` to apply inline formatting and animations (iMessage only). - `text_decorations?: Array` Optional array of text decorations applied to character ranges in the `value` field (iMessage only). Each decoration specifies a character range `[start, end)` and exactly one of `style` or `animation`. **Styles:** `bold`, `italic`, `strikethrough`, `underline` **Animations:** `big`, `small`, `shake`, `nod`, `explode`, `ripple`, `bloom`, `jitter` Style ranges may overlap (e.g. bold + italic on the same text), but animation ranges must not overlap with other animations or styles. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* **Note:** Text decorations only render for iMessage recipients. For SMS/RCS, text decorations are not applied. - `range: Array` Character range `[start, end)` in the `value` string where the decoration applies. `start` is inclusive, `end` is exclusive. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* - `animation?: "big" | "small" | "shake" | 5 more` Animated text effect to apply. Mutually exclusive with `style`. - `"big"` - `"small"` - `"shake"` - `"nod"` - `"explode"` - `"ripple"` - `"bloom"` - `"jitter"` - `style?: "bold" | "italic" | "strikethrough" | "underline"` Text style to apply. Mutually exclusive with `animation`. - `"bold"` - `"italic"` - `"strikethrough"` - `"underline"` - `MediaPart` - `type: "media"` Indicates this is a media attachment part - `"media"` - `attachment_id?: string` Reference to a file pre-uploaded via `POST /v3/attachments` (optional). The file is already stored, so sends using this ID skip the download step — useful when sending the same file to many recipients. Either `url` or `attachment_id` must be provided, but not both. - `url?: string` Any publicly accessible HTTPS URL to the media file. The server downloads and sends the file automatically — no pre-upload step required. **Size limit:** 10MB maximum for URL-based downloads. For larger files (up to 100MB), use the pre-upload flow: `POST /v3/attachments` to get a presigned URL, upload directly, then reference by `attachment_id`. **Requirements:** - URL must use HTTPS - File content must be a supported format (the server validates the actual file content) **Supported formats:** - Images: .jpg, .jpeg, .png, .gif, .heic, .heif, .tif, .tiff, .bmp - Videos: .mp4, .mov, .m4v, .mpeg, .mpg, .3gp - Audio: .m4a, .mp3, .aac, .caf, .wav, .aiff, .amr - Documents: .pdf, .txt, .rtf, .csv, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .pages, .numbers, .key, .epub, .zip, .html, .htm - Contact & Calendar: .vcf, .ics **Tip:** Audio sent here appears as a regular file attachment. To send audio as an iMessage voice memo bubble (with inline playback), use `/v3/chats/{chatId}/voicememo`. For repeated sends of the same file, use `attachment_id` to avoid redundant downloads. Either `url` or `attachment_id` must be provided, but not both. - `LinkPart` - `type: "link"` Indicates this is a rich link preview part - `"link"` - `value: string` URL to send with a rich link preview. The recipient will see an inline card with the page's title, description, and preview image (when available). A `link` part must be the **only** part in the message. To send a URL as plain text (no preview card), use a `text` part instead. - `effect?: MessageEffect` iMessage effect to apply to this message (screen or bubble effect) - `name?: string` Name of the effect. Common values: - Screen effects: confetti, fireworks, lasers, sparkles, celebration, hearts, love, balloons, happy_birthday, echo, spotlight - Bubble effects: slam, loud, gentle, invisible - `type?: "screen" | "bubble"` Type of effect - `"screen"` - `"bubble"` - `idempotency_key?: string` Optional idempotency key for this message. Use this to prevent duplicate sends of the same message. - `preferred_service?: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `reply_to?: ReplyTo` Reply to another message to create a threaded conversation - `message_id: string` The ID of the message to reply to - `part_index?: number` The specific message part to reply to (0-based index). Defaults to 0 (first part) if not provided. Use this when replying to a specific part of a multipart message. ### Returns - `MessageSendResponse` Response for sending a message to a chat - `chat_id: string` Unique identifier of the chat this message was sent to - `message: SentMessage` A message that was sent (used in CreateChat and SendMessage responses) - `id: string` Message identifier (UUID) - `created_at: string` When the message was created - `delivery_status: "pending" | "queued" | "sent" | 2 more` Current delivery status of a message - `"pending"` - `"queued"` - `"sent"` - `"delivered"` - `"failed"` - `is_read: boolean` Whether the message has been read - `parts: Array` Message parts in order (text, media, and link) - `TextPartResponse` A text message part - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `"active"` - `"left"` - `"removed"` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `"love"` - `"like"` - `"dislike"` - `"laugh"` - `"emphasize"` - `"question"` - `"custom"` - `"sticker"` - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `file_name?: string` Filename of the sticker - `height?: number` Sticker image height in pixels - `mime_type?: string` MIME type of the sticker image - `url?: string` Presigned URL for downloading the sticker image (expires in 1 hour). - `width?: number` Sticker image width in pixels - `type: "text"` Indicates this is a text message part - `"text"` - `value: string` The text content - `text_decorations?: Array | null` Text decorations applied to character ranges in the value - `range: Array` Character range `[start, end)` in the `value` string where the decoration applies. `start` is inclusive, `end` is exclusive. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* - `animation?: "big" | "small" | "shake" | 5 more` Animated text effect to apply. Mutually exclusive with `style`. - `"big"` - `"small"` - `"shake"` - `"nod"` - `"explode"` - `"ripple"` - `"bloom"` - `"jitter"` - `style?: "bold" | "italic" | "strikethrough" | "underline"` Text style to apply. Mutually exclusive with `animation`. - `"bold"` - `"italic"` - `"strikethrough"` - `"underline"` - `MediaPartResponse` A media attachment part - `id: string` Unique attachment identifier - `filename: string` Original filename - `mime_type: string` MIME type of the file - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `size_bytes: number` File size in bytes - `type: "media"` Indicates this is a media attachment part - `"media"` - `url: string` Presigned URL for downloading the attachment (expires in 1 hour). - `LinkPartResponse` A rich link preview part - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `type: "link"` Indicates this is a rich link preview part - `"link"` - `value: string` The URL - `sent_at: string | null` When the message was actually sent (null if still queued) - `delivered_at?: string | null` When the message was delivered - `effect?: MessageEffect | null` iMessage effect applied to a message (screen or bubble effect) - `name?: string` Name of the effect. Common values: - Screen effects: confetti, fireworks, lasers, sparkles, celebration, hearts, love, balloons, happy_birthday, echo, spotlight - Bubble effects: slam, loud, gentle, invisible - `type?: "screen" | "bubble"` Type of effect - `"screen"` - `"bubble"` - `from_handle?: ChatHandle | null` The sender of this message as a full handle object - `preferred_service?: ServiceType | null` Messaging service type - `reply_to?: ReplyTo | null` Indicates this message is a threaded reply to another message - `message_id: string` The ID of the message to reply to - `part_index?: number` The specific message part to reply to (0-based index). Defaults to 0 (first part) if not provided. Use this when replying to a specific part of a multipart message. - `service?: ServiceType | null` Messaging service type ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); const response = await client.chats.messages.send('550e8400-e29b-41d4-a716-446655440000', { message: { parts: [{ type: 'text', value: 'Hello, world!' }] }, }); console.log(response.chat_id); ``` #### Response ```json { "chat_id": "550e8400-e29b-41d4-a716-446655440000", "message": { "id": "69a37c7d-af4f-4b5e-af42-e28e98ce873a", "created_at": "2025-10-23T13:07:55.019-05:00", "delivery_status": "pending", "is_read": false, "parts": [ { "reactions": [ { "handle": { "id": "69a37c7d-af4f-4b5e-af42-e28e98ce873a", "handle": "+15551234567", "joined_at": "2025-05-21T15:30:00.000-05:00", "service": "iMessage", "is_me": false, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" }, "is_me": false, "type": "love", "custom_emoji": null, "sticker": { "file_name": "sticker.png", "height": 420, "mime_type": "image/png", "url": "https://cdn.linqapp.com/attachments/a1b2c3d4/sticker.png?signature=...", "width": 420 } } ], "type": "text", "value": "Hello!", "text_decorations": [ { "range": [ 0, 5 ], "animation": "shake", "style": "bold" } ] } ], "sent_at": null, "delivered_at": null, "effect": { "name": "confetti", "type": "screen" }, "from_handle": { "id": "550e8400-e29b-41d4-a716-446655440000", "handle": "+15551234567", "joined_at": "2025-05-21T15:30:00.000-05:00", "service": "iMessage", "is_me": false, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" }, "preferred_service": "iMessage", "reply_to": { "message_id": "550e8400-e29b-41d4-a716-446655440000", "part_index": 0 }, "service": "iMessage" } } ``` ## Get messages from a chat `client.chats.messages.list(stringchatID, MessageListParamsquery?, RequestOptionsoptions?): ListMessagesPagination` **get** `/v3/chats/{chatId}/messages` Retrieve messages from a specific chat with pagination support. ### Parameters - `chatID: string` - `query: MessageListParams` - `cursor?: string` Pagination cursor from previous next_cursor response - `limit?: number` Maximum number of messages to return ### Returns - `Message` - `id: string` Unique identifier for the message - `chat_id: string` ID of the chat this message belongs to - `created_at: string` When the message was created - `is_delivered: boolean` Whether the message has been delivered - `is_from_me: boolean` Whether this message was sent by the authenticated user - `is_read: boolean` Whether the message has been read - `updated_at: string` When the message was last updated - `delivered_at?: string | null` When the message was delivered - `effect?: MessageEffect | null` iMessage effect applied to a message (screen or bubble effect) - `name?: string` Name of the effect. Common values: - Screen effects: confetti, fireworks, lasers, sparkles, celebration, hearts, love, balloons, happy_birthday, echo, spotlight - Bubble effects: slam, loud, gentle, invisible - `type?: "screen" | "bubble"` Type of effect - `"screen"` - `"bubble"` - `from?: string | null` DEPRECATED: Use from_handle instead. Phone number of the message sender. - `from_handle?: ChatHandle | null` The sender of this message as a full handle object - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `"active"` - `"left"` - `"removed"` - `parts?: Array | null` Message parts in order (text, media, and link) - `TextPartResponse` A text message part - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `"love"` - `"like"` - `"dislike"` - `"laugh"` - `"emphasize"` - `"question"` - `"custom"` - `"sticker"` - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `file_name?: string` Filename of the sticker - `height?: number` Sticker image height in pixels - `mime_type?: string` MIME type of the sticker image - `url?: string` Presigned URL for downloading the sticker image (expires in 1 hour). - `width?: number` Sticker image width in pixels - `type: "text"` Indicates this is a text message part - `"text"` - `value: string` The text content - `text_decorations?: Array | null` Text decorations applied to character ranges in the value - `range: Array` Character range `[start, end)` in the `value` string where the decoration applies. `start` is inclusive, `end` is exclusive. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* - `animation?: "big" | "small" | "shake" | 5 more` Animated text effect to apply. Mutually exclusive with `style`. - `"big"` - `"small"` - `"shake"` - `"nod"` - `"explode"` - `"ripple"` - `"bloom"` - `"jitter"` - `style?: "bold" | "italic" | "strikethrough" | "underline"` Text style to apply. Mutually exclusive with `animation`. - `"bold"` - `"italic"` - `"strikethrough"` - `"underline"` - `MediaPartResponse` A media attachment part - `id: string` Unique attachment identifier - `filename: string` Original filename - `mime_type: string` MIME type of the file - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `size_bytes: number` File size in bytes - `type: "media"` Indicates this is a media attachment part - `"media"` - `url: string` Presigned URL for downloading the attachment (expires in 1 hour). - `LinkPartResponse` A rich link preview part - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `type: "link"` Indicates this is a rich link preview part - `"link"` - `value: string` The URL - `preferred_service?: ServiceType | null` Messaging service type - `read_at?: string | null` When the message was read - `reply_to?: ReplyTo | null` Indicates this message is a threaded reply to another message - `message_id: string` The ID of the message to reply to - `part_index?: number` The specific message part to reply to (0-based index). Defaults to 0 (first part) if not provided. Use this when replying to a specific part of a multipart message. - `sent_at?: string | null` When the message was sent - `service?: ServiceType | null` Messaging service type ### Example ```typescript import LinqAPIV3 from '@linqapp/sdk'; const client = new LinqAPIV3({ apiKey: process.env['LINQ_API_V3_API_KEY'], // This is the default and can be omitted }); // Automatically fetches more pages as needed. for await (const message of client.chats.messages.list('550e8400-e29b-41d4-a716-446655440000')) { console.log(message.id); } ``` #### Response ```json { "messages": [ { "id": "69a37c7d-af4f-4b5e-af42-e28e98ce873a", "chat_id": "94c6bf33-31d9-40e3-a0e9-f94250ecedb9", "created_at": "2024-01-15T10:30:00Z", "is_delivered": true, "is_from_me": true, "is_read": false, "updated_at": "2024-01-15T10:30:00Z", "delivered_at": "2024-01-15T10:30:10Z", "effect": { "name": "confetti", "type": "screen" }, "from": "+12052535597", "from_handle": { "id": "550e8400-e29b-41d4-a716-446655440000", "handle": "+15551234567", "joined_at": "2025-05-21T15:30:00.000-05:00", "service": "iMessage", "is_me": false, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" }, "parts": [ { "reactions": [ { "handle": { "id": "69a37c7d-af4f-4b5e-af42-e28e98ce873a", "handle": "+15551234567", "joined_at": "2025-05-21T15:30:00.000-05:00", "service": "iMessage", "is_me": false, "left_at": "2019-12-27T18:11:19.117Z", "status": "active" }, "is_me": false, "type": "love", "custom_emoji": null, "sticker": { "file_name": "sticker.png", "height": 420, "mime_type": "image/png", "url": "https://cdn.linqapp.com/attachments/a1b2c3d4/sticker.png?signature=...", "width": 420 } } ], "type": "text", "value": "Hello!", "text_decorations": [ { "range": [ 0, 5 ], "animation": "shake", "style": "bold" } ] } ], "preferred_service": "iMessage", "read_at": "2024-01-15T10:35:00Z", "reply_to": { "message_id": "550e8400-e29b-41d4-a716-446655440000", "part_index": 0 }, "sent_at": "2024-01-15T10:30:05Z", "service": "iMessage" } ], "next_cursor": "next_cursor" } ``` ## Domain Types ### Sent Message - `SentMessage` A message that was sent (used in CreateChat and SendMessage responses) - `id: string` Message identifier (UUID) - `created_at: string` When the message was created - `delivery_status: "pending" | "queued" | "sent" | 2 more` Current delivery status of a message - `"pending"` - `"queued"` - `"sent"` - `"delivered"` - `"failed"` - `is_read: boolean` Whether the message has been read - `parts: Array` Message parts in order (text, media, and link) - `TextPartResponse` A text message part - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `"active"` - `"left"` - `"removed"` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `"love"` - `"like"` - `"dislike"` - `"laugh"` - `"emphasize"` - `"question"` - `"custom"` - `"sticker"` - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `file_name?: string` Filename of the sticker - `height?: number` Sticker image height in pixels - `mime_type?: string` MIME type of the sticker image - `url?: string` Presigned URL for downloading the sticker image (expires in 1 hour). - `width?: number` Sticker image width in pixels - `type: "text"` Indicates this is a text message part - `"text"` - `value: string` The text content - `text_decorations?: Array | null` Text decorations applied to character ranges in the value - `range: Array` Character range `[start, end)` in the `value` string where the decoration applies. `start` is inclusive, `end` is exclusive. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* - `animation?: "big" | "small" | "shake" | 5 more` Animated text effect to apply. Mutually exclusive with `style`. - `"big"` - `"small"` - `"shake"` - `"nod"` - `"explode"` - `"ripple"` - `"bloom"` - `"jitter"` - `style?: "bold" | "italic" | "strikethrough" | "underline"` Text style to apply. Mutually exclusive with `animation`. - `"bold"` - `"italic"` - `"strikethrough"` - `"underline"` - `MediaPartResponse` A media attachment part - `id: string` Unique attachment identifier - `filename: string` Original filename - `mime_type: string` MIME type of the file - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `size_bytes: number` File size in bytes - `type: "media"` Indicates this is a media attachment part - `"media"` - `url: string` Presigned URL for downloading the attachment (expires in 1 hour). - `LinkPartResponse` A rich link preview part - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `type: "link"` Indicates this is a rich link preview part - `"link"` - `value: string` The URL - `sent_at: string | null` When the message was actually sent (null if still queued) - `delivered_at?: string | null` When the message was delivered - `effect?: MessageEffect | null` iMessage effect applied to a message (screen or bubble effect) - `name?: string` Name of the effect. Common values: - Screen effects: confetti, fireworks, lasers, sparkles, celebration, hearts, love, balloons, happy_birthday, echo, spotlight - Bubble effects: slam, loud, gentle, invisible - `type?: "screen" | "bubble"` Type of effect - `"screen"` - `"bubble"` - `from_handle?: ChatHandle | null` The sender of this message as a full handle object - `preferred_service?: ServiceType | null` Messaging service type - `reply_to?: ReplyTo | null` Indicates this message is a threaded reply to another message - `message_id: string` The ID of the message to reply to - `part_index?: number` The specific message part to reply to (0-based index). Defaults to 0 (first part) if not provided. Use this when replying to a specific part of a multipart message. - `service?: ServiceType | null` Messaging service type ### Message Send Response - `MessageSendResponse` Response for sending a message to a chat - `chat_id: string` Unique identifier of the chat this message was sent to - `message: SentMessage` A message that was sent (used in CreateChat and SendMessage responses) - `id: string` Message identifier (UUID) - `created_at: string` When the message was created - `delivery_status: "pending" | "queued" | "sent" | 2 more` Current delivery status of a message - `"pending"` - `"queued"` - `"sent"` - `"delivered"` - `"failed"` - `is_read: boolean` Whether the message has been read - `parts: Array` Message parts in order (text, media, and link) - `TextPartResponse` A text message part - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `id: string` Unique identifier for this handle - `handle: string` Phone number (E.164) or email address of the participant - `joined_at: string` When this participant joined the chat - `service: ServiceType` Messaging service type - `"iMessage"` - `"SMS"` - `"RCS"` - `is_me?: boolean | null` Whether this handle belongs to the sender (your phone number) - `left_at?: string | null` When they left (if applicable) - `status?: "active" | "left" | "removed" | null` Participant status - `"active"` - `"left"` - `"removed"` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `"love"` - `"like"` - `"dislike"` - `"laugh"` - `"emphasize"` - `"question"` - `"custom"` - `"sticker"` - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `file_name?: string` Filename of the sticker - `height?: number` Sticker image height in pixels - `mime_type?: string` MIME type of the sticker image - `url?: string` Presigned URL for downloading the sticker image (expires in 1 hour). - `width?: number` Sticker image width in pixels - `type: "text"` Indicates this is a text message part - `"text"` - `value: string` The text content - `text_decorations?: Array | null` Text decorations applied to character ranges in the value - `range: Array` Character range `[start, end)` in the `value` string where the decoration applies. `start` is inclusive, `end` is exclusive. *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.* - `animation?: "big" | "small" | "shake" | 5 more` Animated text effect to apply. Mutually exclusive with `style`. - `"big"` - `"small"` - `"shake"` - `"nod"` - `"explode"` - `"ripple"` - `"bloom"` - `"jitter"` - `style?: "bold" | "italic" | "strikethrough" | "underline"` Text style to apply. Mutually exclusive with `animation`. - `"bold"` - `"italic"` - `"strikethrough"` - `"underline"` - `MediaPartResponse` A media attachment part - `id: string` Unique attachment identifier - `filename: string` Original filename - `mime_type: string` MIME type of the file - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `size_bytes: number` File size in bytes - `type: "media"` Indicates this is a media attachment part - `"media"` - `url: string` Presigned URL for downloading the attachment (expires in 1 hour). - `LinkPartResponse` A rich link preview part - `reactions: Array | null` Reactions on this message part - `handle: ChatHandle` - `is_me: boolean` Whether this reaction is from the current user - `type: ReactionType` Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type "custom" with the actual emoji in the custom_emoji field. Sticker reactions have type "sticker" with sticker attachment details in the sticker field. - `custom_emoji?: string | null` Custom emoji if type is "custom", null otherwise - `sticker?: Sticker | null` Sticker attachment details when reaction_type is "sticker". Null for non-sticker reactions. - `type: "link"` Indicates this is a rich link preview part - `"link"` - `value: string` The URL - `sent_at: string | null` When the message was actually sent (null if still queued) - `delivered_at?: string | null` When the message was delivered - `effect?: MessageEffect | null` iMessage effect applied to a message (screen or bubble effect) - `name?: string` Name of the effect. Common values: - Screen effects: confetti, fireworks, lasers, sparkles, celebration, hearts, love, balloons, happy_birthday, echo, spotlight - Bubble effects: slam, loud, gentle, invisible - `type?: "screen" | "bubble"` Type of effect - `"screen"` - `"bubble"` - `from_handle?: ChatHandle | null` The sender of this message as a full handle object - `preferred_service?: ServiceType | null` Messaging service type - `reply_to?: ReplyTo | null` Indicates this message is a threaded reply to another message - `message_id: string` The ID of the message to reply to - `part_index?: number` The specific message part to reply to (0-based index). Defaults to 0 (first part) if not provided. Use this when replying to a specific part of a multipart message. - `service?: ServiceType | null` Messaging service type