Skip to content
Get started

Send a message (auto-selected from-number)

messages.create(MessageCreateParams**kwargs) -> MessageCreateResponse
POST/v3/messages

Send a message to one or more recipients without supplying a from number. Linq resolves both the sending line and the target chat for you, then returns exactly which line was used, which chat the message landed in, whether a new chat was created, and every resulting message id.

This fuses “create chat” and “send message” behind a single message-centric resource. Provide only the recipients (to) and the message; the platform decides the rest.

How the from-number and chat are chosen

  • Reuse — if a chat with exactly these recipients already exists and the line it lives on is healthy, the message is sent into that chat on its existing line (from_selection.reason = reused_active_chat).
  • New — if no such chat exists, a new chat is created on the best available line (from_selection.reason = new_best_number).
  • Failover — if a matching chat exists but its line has been flagged, a new chat is created on a fresh best line and the flagged chat is abandoned (from_selection.reason = failover_flagged, previous_chat_id set). If you supply continuation_message, that text is sent as the single message INSTEAD of message (useful as a fresh-number-appropriate opener). Exactly one message is sent either way.

Recipients (to) are an order-independent set: a single handle is a direct chat, multiple handles a group chat.

Differences from POST /v3/chats

  • The first message may contain a link (including for a newly created chat). Note: sending a link as the very first message on a freshly selected line can elevate that line’s flagging risk — it is allowed, not recommended.
  • Voice memos are not supported here. To send an iMessage voice-memo bubble, use POST /v3/chats/{chatId}/voicememo with a known chat id.

Service preference, effects, decorations

Set message.preferred_service (iMessage | RCS | SMS), message.effect, and per-part text_decorations exactly as on the other send endpoints.

Always responds 202 Accepted — chat creation is incidental to the send.

ParametersExpand Collapse

Message content container. Groups all message-related fields together, separating the “what” (message content) from the “where” (routing fields like from/to).

parts: List[Part]

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:
class TextPart:
type: Literal["text"]

Indicates this is a text message part

value: str

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: Optional[List[TextDecoration]]

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: List[int]

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: Optional[Literal["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: Optional[Literal["bold", "italic", "strikethrough", "underline"]]

Text style to apply. Mutually exclusive with animation.

One of the following:
"bold"
"italic"
"strikethrough"
"underline"
class MediaPart:
type: Literal["media"]

Indicates this is a media attachment part

attachment_id: Optional[str]

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: Optional[str]

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
class PartIMessageAppPart:

An iMessage app card, backed by a Messages app extension. iMessage only — an imessage_app part must be the only part in the message and is never delivered over SMS/RCS. See the IMessageAppServiceUnsupported (2018) and RecipientUnsupportedMessageType (4005) error codes.

app: PartIMessageAppPartApp

Identifies the iMessage app (Messages app extension) that backs the card.

bundle_id: str

Bundle identifier of the Messages app extension. Must not contain :.

minLength1
maxLength255
name: str

Display name of the app, shown by Messages’ fallback UI.

minLength1
maxLength64
team_id: str

The app’s 10-character uppercase alphanumeric team identifier.

app_store_id: Optional[int]

The owning app’s App Store id (optional). When set, recipients without the iMessage app installed see a “Get the app” affordance.

formatint64
minimum1
layout: PartIMessageAppPartLayout

Visible layout of the card. At least one of caption, subcaption, trailing_caption, trailing_subcaption, or image_url must be set, otherwise the card renders as an empty bubble.

image_url displays a preview image at the top of the card. The image renders on the recipient’s card whether or not they have your app installed. The small icon beside the caption is the app’s own icon and is not settable here.

* Note - requires a trusted chat w/ inbound activity

image_title and image_subtitle render as text overlaid on the image (title bold, subtitle beneath it). They only appear when image_url is set — without an image there is nothing to overlay — so setting either without image_url is rejected.

caption: Optional[str]

Primary label, top-left and bold.

maxLength512
image_subtitle: Optional[str]

Text shown below image_title, overlaid on the card image. Requires image_url.

maxLength512
image_title: Optional[str]

Bold text overlaid on the card image. Requires image_url (rejected without it).

maxLength512
image_url: Optional[str]

URL of a JPEG image to display as the card’s preview image; an unreachable or non-image URL returns a validation error. Renders for all recipients regardless of whether they have the app. Note - requires a trusted chat w/ inbound activity.

formaturi
maxLength2048
subcaption: Optional[str]

Secondary label, below caption on the left.

maxLength512
trailing_caption: Optional[str]

Label shown top-right.

maxLength512
trailing_subcaption: Optional[str]

Label shown below trailing_caption, on the right.

maxLength512
type: Literal["imessage_app"]

Indicates this is an iMessage app card part.

fallback_text: Optional[str]

Text shown on surfaces that cannot render the card (notifications, lock screen). Defaults to the caption when omitted.

maxLength1024
interactive: Optional[bool]

Whether the card renders as your app’s interactive balloon for recipients who have your iMessage app installed. true (default) lets your installed extension draw its live, interactive view for those recipients; everyone else sees the static card built from layout. false always shows the static layout card, even to recipients who have the app installed. Recipients without your app always see the static card regardless of this flag.

url: Optional[str]

URL the recipient’s app opens when they tap the card.

formaturi
maxLength2048
effect: Optional[MessageEffect]

iMessage effect to apply to this message (screen or bubble effect)

name: Optional[str]

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: Optional[Literal["screen", "bubble"]]

Type of effect

One of the following:
"screen"
"bubble"
idempotency_key: Optional[str]

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

maxLength255
preferred_service: Optional[ServiceType]

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
reply_to: Optional[ReplyTo]

Reply to another message to create a threaded conversation

message_id: str

The ID of the message to reply to

formatuuid
part_index: Optional[int]

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: Sequence[str]

Recipient handles (E.164 phone numbers or email addresses). One handle is a direct chat; multiple handles a group chat. Order-independent — the set identifies the chat.

continuation_message: Optional[ContinuationMessage]

Text-only fallback that replaces message ONLY on the failover branch — when a chat with these recipients already existed but its line was flagged, so a new chat is created on a fresh line. On that branch this text is sent as the single message instead of message (the recipient is on a new number, so you typically want a fresh-number-appropriate opener rather than the original content). Ignored otherwise (a healthy reuse, or genuine first contact). Carries no parts, media, or effects — exactly one message is ever sent.

text: str

The replacement message text, sent as the single message on failover.

minLength1
maxLength10000
idempotency_key: Optional[str]
maxLength255
ReturnsExpand Collapse
class MessageCreateResponse:

Result of an auto-from send. Self-describing: which line was used, which chat the message landed in, whether a new chat was created, and the resulting message id(s).

chat_id: str

The resolved chat (reused or newly created) the message landed in.

formatuuid
created_new_chat: bool

True when a new chat was created (new or failover), false on reuse.

from_: str

The line (E.164) the message was actually sent from.

from_selection: FromSelection

Why this line/chat was chosen.

reason: Literal["reused_active_chat", "new_best_number", "failover_flagged"]
  • reused_active_chat — reused an existing chat on its healthy line
  • new_best_number — created a new chat on the best available line
  • failover_flagged — prior chat’s line was flagged; created a new chat on a fresh line
One of the following:
"reused_active_chat"
"new_best_number"
"failover_flagged"
reused_existing_chat: bool

True only when an existing chat was reused.

handles: List[ChatHandle]

Participants of the resolved chat.

id: str

Unique identifier for this handle

formatuuid
handle: str

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

joined_at: datetime

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me: Optional[bool]

Whether this handle belongs to the sender (your phone number)

left_at: Optional[datetime]

When they left (if applicable)

formatdate-time
status: Optional[Literal["active", "left", "removed"]]

Participant status

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

Whether the resolved chat is a group chat.

message: SentMessage

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

id: str

Message identifier (UUID)

formatuuid
created_at: datetime

When the message was created

formatdate-time
delivery_status: Literal["pending", "queued", "sent", 4 more]

Current delivery status of a message

One of the following:
"pending"
"queued"
"sent"
"delivered"
"received"
"read"
"failed"
Deprecatedis_read: bool

DEPRECATED: Use delivery_status == "read" instead. Whether the message has been read.

parts: List[Part]

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

One of the following:
class TextPartResponse:

A text message part

reactions: Optional[List[Reaction]]

Reactions on this message part

handle: ChatHandle
id: str

Unique identifier for this handle

formatuuid
handle: str

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

joined_at: datetime

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me: Optional[bool]

Whether this handle belongs to the sender (your phone number)

left_at: Optional[datetime]

When they left (if applicable)

formatdate-time
status: Optional[Literal["active", "left", "removed"]]

Participant status

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

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: Optional[str]

Custom emoji if type is “custom”, null otherwise

sticker: Optional[Sticker]

Sticker attachment details when reaction_type is “sticker”. Null for non-sticker reactions.

file_name: Optional[str]

Filename of the sticker

height: Optional[int]

Sticker image height in pixels

mime_type: Optional[str]

MIME type of the sticker image

url: Optional[str]

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

formaturi
width: Optional[int]

Sticker image width in pixels

type: Literal["text"]

Indicates this is a text message part

value: str

The text content

text_decorations: Optional[List[TextDecoration]]

Text decorations applied to character ranges in the value

range: List[int]

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: Optional[Literal["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: Optional[Literal["bold", "italic", "strikethrough", "underline"]]

Text style to apply. Mutually exclusive with animation.

One of the following:
"bold"
"italic"
"strikethrough"
"underline"
class MediaPartResponse:

A media attachment part

id: str

Unique attachment identifier

formatuuid
filename: str

Original filename

mime_type: str

MIME type of the file

reactions: Optional[List[Reaction]]

Reactions on this message part

handle: ChatHandle
id: str

Unique identifier for this handle

formatuuid
handle: str

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

joined_at: datetime

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me: Optional[bool]

Whether this handle belongs to the sender (your phone number)

left_at: Optional[datetime]

When they left (if applicable)

formatdate-time
status: Optional[Literal["active", "left", "removed"]]

Participant status

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

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: Optional[str]

Custom emoji if type is “custom”, null otherwise

sticker: Optional[Sticker]

Sticker attachment details when reaction_type is “sticker”. Null for non-sticker reactions.

file_name: Optional[str]

Filename of the sticker

height: Optional[int]

Sticker image height in pixels

mime_type: Optional[str]

MIME type of the sticker image

url: Optional[str]

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

formaturi
width: Optional[int]

Sticker image width in pixels

size_bytes: int

File size in bytes

type: Literal["media"]

Indicates this is a media attachment part

url: str

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

formaturi
handle: ChatHandle
id: str

Unique identifier for this handle

formatuuid
handle: str

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

joined_at: datetime

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me: Optional[bool]

Whether this handle belongs to the sender (your phone number)

left_at: Optional[datetime]

When they left (if applicable)

formatdate-time
status: Optional[Literal["active", "left", "removed"]]

Participant status

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

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: Optional[str]

Custom emoji if type is “custom”, null otherwise

sticker: Optional[Sticker]

Sticker attachment details when reaction_type is “sticker”. Null for non-sticker reactions.

file_name: Optional[str]

Filename of the sticker

height: Optional[int]

Sticker image height in pixels

mime_type: Optional[str]

MIME type of the sticker image

url: Optional[str]

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

formaturi
width: Optional[int]

Sticker image width in pixels

class PartIMessageAppPartResponse:

An iMessage app card part.

app: PartIMessageAppPartResponseApp

Identifies the iMessage app (Messages app extension) that backs the card.

bundle_id: str

Bundle identifier of the Messages app extension. Must not contain :.

minLength1
maxLength255
name: str

Display name of the app, shown by Messages’ fallback UI.

minLength1
maxLength64
team_id: str

The app’s 10-character uppercase alphanumeric team identifier.

app_store_id: Optional[int]

The owning app’s App Store id (optional). When set, recipients without the iMessage app installed see a “Get the app” affordance.

formatint64
minimum1
layout: PartIMessageAppPartResponseLayout

Visible layout of the card. At least one of caption, subcaption, trailing_caption, trailing_subcaption, or image_url must be set, otherwise the card renders as an empty bubble.

image_url displays a preview image at the top of the card. The image renders on the recipient’s card whether or not they have your app installed. The small icon beside the caption is the app’s own icon and is not settable here.

* Note - requires a trusted chat w/ inbound activity

image_title and image_subtitle render as text overlaid on the image (title bold, subtitle beneath it). They only appear when image_url is set — without an image there is nothing to overlay — so setting either without image_url is rejected.

caption: Optional[str]

Primary label, top-left and bold.

maxLength512
image_subtitle: Optional[str]

Text shown below image_title, overlaid on the card image. Requires image_url.

maxLength512
image_title: Optional[str]

Bold text overlaid on the card image. Requires image_url (rejected without it).

maxLength512
image_url: Optional[str]

URL of a JPEG image to display as the card’s preview image; an unreachable or non-image URL returns a validation error. Renders for all recipients regardless of whether they have the app. Note - requires a trusted chat w/ inbound activity.

formaturi
maxLength2048
subcaption: Optional[str]

Secondary label, below caption on the left.

maxLength512
trailing_caption: Optional[str]

Label shown top-right.

maxLength512
trailing_subcaption: Optional[str]

Label shown below trailing_caption, on the right.

maxLength512
reactions: Optional[List[Reaction]]

Reactions on this message part

handle: ChatHandle
id: str

Unique identifier for this handle

formatuuid
handle: str

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

joined_at: datetime

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me: Optional[bool]

Whether this handle belongs to the sender (your phone number)

left_at: Optional[datetime]

When they left (if applicable)

formatdate-time
status: Optional[Literal["active", "left", "removed"]]

Participant status

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

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: Optional[str]

Custom emoji if type is “custom”, null otherwise

sticker: Optional[Sticker]

Sticker attachment details when reaction_type is “sticker”. Null for non-sticker reactions.

file_name: Optional[str]

Filename of the sticker

height: Optional[int]

Sticker image height in pixels

mime_type: Optional[str]

MIME type of the sticker image

url: Optional[str]

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

formaturi
width: Optional[int]

Sticker image width in pixels

type: Literal["imessage_app"]

Indicates this is an iMessage app card part.

url: str

The URL delivered to the iMessage app on tap.

formaturi
fallback_text: Optional[str]

Fallback text for surfaces that cannot render the card.

sent_at: Optional[datetime]

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

formatdate-time
delivered_at: Optional[datetime]

When the message was delivered

formatdate-time
effect: Optional[MessageEffect]

iMessage effect applied to a message (screen or bubble effect)

name: Optional[str]

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: Optional[Literal["screen", "bubble"]]

Type of effect

One of the following:
"screen"
"bubble"
from_handle: Optional[ChatHandle]

The sender of this message as a full handle object

id: str

Unique identifier for this handle

formatuuid
handle: str

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

joined_at: datetime

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me: Optional[bool]

Whether this handle belongs to the sender (your phone number)

left_at: Optional[datetime]

When they left (if applicable)

formatdate-time
status: Optional[Literal["active", "left", "removed"]]

Participant status

One of the following:
"active"
"left"
"removed"
preferred_service: Optional[ServiceType]

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
reply_to: Optional[ReplyTo]

Indicates this message is a threaded reply to another message

message_id: str

The ID of the message to reply to

formatuuid
part_index: Optional[int]

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: Optional[ServiceType]

Messaging service type

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

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
previous_chat_id: Optional[str]

Set ONLY on failover_flagged: the abandoned flagged chat that was NOT sent into. Null otherwise.

formatuuid

Send a message (auto-selected from-number)

import os
from linq import LinqAPIV3

client = LinqAPIV3(
    api_key=os.environ.get("LINQ_API_V3_API_KEY"),  # This is the default and can be omitted
)
message = client.messages.create(
    message={
        "parts": [{
            "type": "text",
            "value": "Hi! Thanks for reaching out — how can we help?",
        }]
    },
    to=["+14155559876"],
)
print(message.chat_id)
{
  "chat_id": "94c6bf33-31d9-40e3-a0e9-f94250ecedb9",
  "created_new_chat": false,
  "from": "+12052535597",
  "from_selection": {
    "reason": "reused_active_chat",
    "reused_existing_chat": true
  },
  "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_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",
  "previous_chat_id": null
}
{
  "error": {
    "status": 400,
    "code": 1002,
    "message": "Phone number must be in E.164 format",
    "doc_url": "https://docs.linqapp.com/error/codes/1xxx/1002/"
  },
  "success": false
}
{
  "error": {
    "status": 401,
    "code": 2004,
    "message": "Unauthorized - missing or invalid authentication token",
    "doc_url": "https://docs.linqapp.com/error/codes/2xxx/2004/"
  },
  "success": false
}
{
  "error": {
    "status": 403,
    "code": 2005,
    "message": "Access denied - insufficient permissions for this resource",
    "doc_url": "https://docs.linqapp.com/error/codes/2xxx/2005/"
  },
  "success": false
}
{
  "error": {
    "status": 409,
    "code": 2015,
    "message": "no eligible sending line available",
    "doc_url": "https://docs.linqapp.com/error/codes/2xxx/2015/"
  },
  "success": false
}
{
  "error": {
    "status": 500,
    "code": 3006,
    "message": "Internal server error",
    "doc_url": "https://docs.linqapp.com/error/codes/3xxx/3006/"
  },
  "success": false
}
Returns Examples
{
  "chat_id": "94c6bf33-31d9-40e3-a0e9-f94250ecedb9",
  "created_new_chat": false,
  "from": "+12052535597",
  "from_selection": {
    "reason": "reused_active_chat",
    "reused_existing_chat": true
  },
  "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_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",
  "previous_chat_id": null
}
{
  "error": {
    "status": 400,
    "code": 1002,
    "message": "Phone number must be in E.164 format",
    "doc_url": "https://docs.linqapp.com/error/codes/1xxx/1002/"
  },
  "success": false
}
{
  "error": {
    "status": 401,
    "code": 2004,
    "message": "Unauthorized - missing or invalid authentication token",
    "doc_url": "https://docs.linqapp.com/error/codes/2xxx/2004/"
  },
  "success": false
}
{
  "error": {
    "status": 403,
    "code": 2005,
    "message": "Access denied - insufficient permissions for this resource",
    "doc_url": "https://docs.linqapp.com/error/codes/2xxx/2005/"
  },
  "success": false
}
{
  "error": {
    "status": 409,
    "code": 2015,
    "message": "no eligible sending line available",
    "doc_url": "https://docs.linqapp.com/error/codes/2xxx/2015/"
  },
  "success": false
}
{
  "error": {
    "status": 500,
    "code": 3006,
    "message": "Internal server error",
    "doc_url": "https://docs.linqapp.com/error/codes/3xxx/3006/"
  },
  "success": false
}