Skip to content
V2 (Legacy) API ReferenceGet started

Create a new chat

client.chats.create(ChatCreateParams { from, message, to } body, RequestOptionsoptions?): ChatCreateResponse { chat }
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

{
  "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.

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.

ParametersExpand Collapse
body: ChatCreateParams { from, message, to }
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 { parts, effect, idempotency_key, 2 more }

Message content container. Groups all message-related fields together, separating the โ€œwhatโ€ (message content) from the โ€œwhereโ€ (routing fields like from/to).

parts: Array<TextPart { type, value, text_decorations } | MediaPart { type, attachment_id, url } | LinkPart { type, value } >

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.
One of the following:
TextPart { type, value, text_decorations }
type: "text"

Indicates this is a text message part

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

minLength1
maxLength10000
text_decorations?: Array<TextDecoration { range, animation, style } >

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<number>

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.

One of the following:
"big"
"small"
"shake"
"nod"
"explode"
"ripple"
"bloom"
"jitter"
style?: "bold" | "italic" | "strikethrough" | "underline"

Text style to apply. Mutually exclusive with animation.

One of the following:
"bold"
"italic"
"strikethrough"
"underline"
MediaPart { type, attachment_id, url }
type: "media"

Indicates this is a media attachment part

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.

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

formaturi
effect?: MessageEffect { name, type }

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

One of the following:
"screen"
"bubble"
idempotency_key?: string

Optional idempotency key for this message. Use this to prevent duplicate sends of the same message.

maxLength255
preferred_service?: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
reply_to?: ReplyTo { message_id, part_index }

Reply to another message to create a threaded conversation

message_id: string

The ID of the message to reply to

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

formatint32
minimum0
to: Array<string>

Array of recipient handles (phone numbers in E.164 format or email addresses). For individual chats, provide one recipient. For group chats, provide multiple.

ReturnsExpand Collapse
ChatCreateResponse { chat }

Response for creating a new chat with an initial message

chat: Chat { id, display_name, handles, 4 more }
id: string

Unique identifier for the created chat (UUID)

formatuuid
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<ChatHandle { id, handle, joined_at, 4 more } >

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

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"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)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
is_group: boolean

Whether this is a group chat

message: SentMessage { id, created_at, delivery_status, 9 more }

A message that was sent (used in CreateChat and SendMessage responses)

id: string

Message identifier (UUID)

formatuuid
created_at: string

When the message was created

formatdate-time
delivery_status: "pending" | "queued" | "sent" | 2 more

Current delivery status of a message

One of the following:
"pending"
"queued"
"sent"
"delivered"
"failed"
is_read: boolean

Whether the message has been read

parts: Array<TextPartResponse { reactions, type, value, text_decorations } | MediaPartResponse { id, filename, mime_type, 4 more } | LinkPartResponse { reactions, type, value } >

Message parts in order (text, media, and link)

One of the following:
TextPartResponse { reactions, type, value, text_decorations }

A text message part

reactions: Array<Reaction { handle, is_me, type, 2 more } > | null

Reactions on this message part

handle: ChatHandle { id, handle, joined_at, 4 more }
id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"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)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
is_me: boolean

Whether this reaction is from the current user

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.

One of the following:
"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).

formaturi
width?: number

Sticker image width in pixels

type: "text"

Indicates this is a text message part

value: string

The text content

text_decorations?: Array<TextDecoration { range, animation, style } > | null

Text decorations applied to character ranges in the value

range: Array<number>

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.

One of the following:
"big"
"small"
"shake"
"nod"
"explode"
"ripple"
"bloom"
"jitter"
style?: "bold" | "italic" | "strikethrough" | "underline"

Text style to apply. Mutually exclusive with animation.

One of the following:
"bold"
"italic"
"strikethrough"
"underline"
MediaPartResponse { id, filename, mime_type, 4 more }

A media attachment part

id: string

Unique attachment identifier

formatuuid
filename: string

Original filename

mime_type: string

MIME type of the file

reactions: Array<Reaction { handle, is_me, type, 2 more } > | null

Reactions on this message part

handle: ChatHandle { id, handle, joined_at, 4 more }
id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"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)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
is_me: boolean

Whether this reaction is from the current user

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.

One of the following:
"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).

formaturi
width?: number

Sticker image width in pixels

size_bytes: number

File size in bytes

type: "media"

Indicates this is a media attachment part

url: string

Presigned URL for downloading the attachment (expires in 1 hour).

formaturi
handle: ChatHandle { id, handle, joined_at, 4 more }
id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"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)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
is_me: boolean

Whether this reaction is from the current user

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.

One of the following:
"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).

formaturi
width?: number

Sticker image width in pixels

sent_at: string | null

When the message was actually sent (null if still queued)

formatdate-time
delivered_at?: string | null

When the message was delivered

formatdate-time
effect?: MessageEffect { name, type } | 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

One of the following:
"screen"
"bubble"
from_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

The sender of this message as a full handle object

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"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)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
preferred_service?: ServiceType | null

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
reply_to?: ReplyTo { message_id, part_index } | null

Indicates this message is a threaded reply to another message

message_id: string

The ID of the message to reply to

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

formatint32
minimum0
service?: ServiceType | null

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
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.

minimum0
maximum100
updated_at: string

When this health score was last computed.

formatdate-time

Create a new chat

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);
{
  "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"
    }
  }
}
{
  "error": {
    "status": 400,
    "code": 1002,
    "message": "Phone number must be in E.164 format"
  },
  "success": false
}
{
  "error": {
    "status": 401,
    "code": 2004,
    "message": "Unauthorized - missing or invalid authentication token"
  },
  "success": false
}
{
  "error": {
    "status": 500,
    "code": 3006,
    "message": "Internal server error"
  },
  "success": false
}
Returns Examples
{
  "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"
    }
  }
}
{
  "error": {
    "status": 400,
    "code": 1002,
    "message": "Phone number must be in E.164 format"
  },
  "success": false
}
{
  "error": {
    "status": 401,
    "code": 2004,
    "message": "Unauthorized - missing or invalid authentication token"
  },
  "success": false
}
{
  "error": {
    "status": 500,
    "code": 3006,
    "message": "Internal server error"
  },
  "success": false
}