# Chats

## Create a new chat

`chats.create(ChatCreateParams**kwargs)  -> 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

- `from_: str`

  Sender phone number in E.164 format. Must be a phone number that the
  authenticated partner has permission to send from.

- `message: MessageContentParam`

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

    - `class TextPart: …`

      - `type: Literal["text"]`

        Indicates this is a text message part

        - `"text"`

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

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

          - `"big"`

          - `"small"`

          - `"shake"`

          - `"nod"`

          - `"explode"`

          - `"ripple"`

          - `"bloom"`

          - `"jitter"`

        - `style: Optional[Literal["bold", "italic", "strikethrough", "underline"]]`

          Text style to apply. Mutually exclusive with `animation`.

          - `"bold"`

          - `"italic"`

          - `"strikethrough"`

          - `"underline"`

    - `class MediaPart: …`

      - `type: Literal["media"]`

        Indicates this is a media attachment part

        - `"media"`

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

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

    - `class LinkPart: …`

      - `type: Literal["link"]`

        Indicates this is a rich link preview part

        - `"link"`

      - `value: str`

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

      - `"screen"`

      - `"bubble"`

  - `idempotency_key: Optional[str]`

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

  - `preferred_service: Optional[ServiceType]`

    Messaging service type

    - `"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

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

- `to: Sequence[str]`

  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

- `class ChatCreateResponse: …`

  Response for creating a new chat with an initial message

  - `chat: Chat`

    - `id: str`

      Unique identifier for the created chat (UUID)

    - `display_name: Optional[str]`

      Display name for the chat. Defaults to a comma-separated list of recipient handles. Can be updated for group chats.

    - `handles: List[ChatHandle]`

      List of participants in the chat. Always contains at least two handles (your phone number and the other participant).

      - `id: str`

        Unique identifier for this handle

      - `handle: str`

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

      - `joined_at: datetime`

        When this participant joined the chat

      - `service: ServiceType`

        Messaging service type

        - `"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)

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

        Participant status

        - `"active"`

        - `"left"`

        - `"removed"`

    - `health_status: ChatHealthStatus`

      **[BETA]** Current health for a chat. Always present — chats start at `HEALTHY` and may shift based on engagement and delivery signals on the conversation. Many `AT_RISK` or `CRITICAL` chats on a single line increase the risk of line flagging.

      Switch on `status` to gate sends or surface line health in your UI — the enum is the long-term contract. Each status carries a `doc_url` that deep-links to the relevant section of the Chat Health guide.

      See the [Chat Health guide](/guides/chats/chat-health) for what each status means and how to react.

      - `doc_url: str`

        Deep-link to the relevant section of the Chat Health guide for this status.

      - `status: Literal["HEALTHY", "AT_RISK", "CRITICAL", "OPTED_OUT"]`

        Current health bucket for the chat. See the [Chat Health guide](/guides/chats/chat-health) for what each value means and how to react. `doc_url` deep-links to the relevant section.

        - `"HEALTHY"`

        - `"AT_RISK"`

        - `"CRITICAL"`

        - `"OPTED_OUT"`

      - `updated_at: datetime`

        When this status last changed.

    - `is_group: bool`

      Whether this is a group chat

    - `message: SentMessage`

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

      - `id: str`

        Message identifier (UUID)

      - `created_at: datetime`

        When the message was created

      - `delivery_status: Literal["pending", "queued", "sent", 2 more]`

        Current delivery status of a message

        - `"pending"`

        - `"queued"`

        - `"sent"`

        - `"delivered"`

        - `"failed"`

      - `is_read: bool`

        Whether the message has been read

      - `parts: List[Part]`

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

        - `class TextPartResponse: …`

          A text message part

          - `reactions: Optional[List[Reaction]]`

            Reactions on this message part

            - `handle: ChatHandle`

              - `id: str`

                Unique identifier for this handle

              - `handle: str`

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

              - `joined_at: datetime`

                When this participant joined the chat

              - `service: ServiceType`

                Messaging service type

              - `is_me: Optional[bool]`

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

              - `left_at: Optional[datetime]`

                When they left (if applicable)

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

                Participant status

            - `is_me: bool`

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

              - `width: Optional[int]`

                Sticker image width in pixels

          - `type: Literal["text"]`

            Indicates this is a text message part

            - `"text"`

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

              - `"big"`

              - `"small"`

              - `"shake"`

              - `"nod"`

              - `"explode"`

              - `"ripple"`

              - `"bloom"`

              - `"jitter"`

            - `style: Optional[Literal["bold", "italic", "strikethrough", "underline"]]`

              Text style to apply. Mutually exclusive with `animation`.

              - `"bold"`

              - `"italic"`

              - `"strikethrough"`

              - `"underline"`

        - `class MediaPartResponse: …`

          A media attachment part

          - `id: str`

            Unique attachment identifier

          - `filename: str`

            Original filename

          - `mime_type: str`

            MIME type of the file

          - `reactions: Optional[List[Reaction]]`

            Reactions on this message part

            - `handle: ChatHandle`

            - `is_me: bool`

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

          - `size_bytes: int`

            File size in bytes

          - `type: Literal["media"]`

            Indicates this is a media attachment part

            - `"media"`

          - `url: str`

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

        - `class LinkPartResponse: …`

          A rich link preview part

          - `reactions: Optional[List[Reaction]]`

            Reactions on this message part

            - `handle: ChatHandle`

            - `is_me: bool`

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

          - `type: Literal["link"]`

            Indicates this is a rich link preview part

            - `"link"`

          - `value: str`

            The URL

      - `sent_at: Optional[datetime]`

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

      - `delivered_at: Optional[datetime]`

        When the message was delivered

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

          - `"screen"`

          - `"bubble"`

      - `from_handle: Optional[ChatHandle]`

        The sender of this message as a full handle object

      - `preferred_service: Optional[ServiceType]`

        Messaging service type

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

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

      - `service: Optional[ServiceType]`

        Messaging service type

    - `service: ServiceType`

      Messaging service type

### Example

```python
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
)
chat = client.chats.create(
    from_="+12052535597",
    message={
        "parts": [{
            "type": "text",
            "value": "Hello! How can I help you today?",
        }]
    },
    to=["+12052532136"],
)
print(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"
      }
    ],
    "health_status": {
      "doc_url": "https://docs.linqapp.com/guides/chats/chat-health#at-risk",
      "status": "AT_RISK",
      "updated_at": "2026-05-01T18:28:25Z"
    },
    "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"
  }
}
```

## List all chats

`chats.list_chats(ChatListChatsParams**kwargs)  -> SyncListChatsPagination[Chat]`

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

- `cursor: Optional[str]`

  Pagination cursor from the previous response's `next_cursor` field.
  Omit this parameter for the first page of results.

- `from_: Optional[str]`

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

  Maximum number of chats to return per page

- `to: Optional[str]`

  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

- `class Chat: …`

  - `id: str`

    Unique identifier for the chat

  - `created_at: datetime`

    When the chat was created

  - `display_name: Optional[str]`

    Display name for the chat. Defaults to a comma-separated list of recipient handles. Can be updated for group chats.

  - `handles: List[ChatHandle]`

    List of chat participants with full handle details. Always contains at least two handles (your phone number and the other participant).

    - `id: str`

      Unique identifier for this handle

    - `handle: str`

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

    - `joined_at: datetime`

      When this participant joined the chat

    - `service: ServiceType`

      Messaging service type

      - `"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)

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

      Participant status

      - `"active"`

      - `"left"`

      - `"removed"`

  - `health_status: HealthStatus`

    **[BETA]** Current health for a chat. Always present — chats start at `HEALTHY` and may shift based on engagement and delivery signals on the conversation. Many `AT_RISK` or `CRITICAL` chats on a single line increase the risk of line flagging.

    Switch on `status` to gate sends or surface line health in your UI — the enum is the long-term contract. Each status carries a `doc_url` that deep-links to the relevant section of the Chat Health guide.

    See the [Chat Health guide](/guides/chats/chat-health) for what each status means and how to react.

    - `doc_url: str`

      Deep-link to the relevant section of the Chat Health guide for this status.

    - `status: Literal["HEALTHY", "AT_RISK", "CRITICAL", "OPTED_OUT"]`

      Current health bucket for the chat. See the [Chat Health guide](/guides/chats/chat-health) for what each value means and how to react. `doc_url` deep-links to the relevant section.

      - `"HEALTHY"`

      - `"AT_RISK"`

      - `"CRITICAL"`

      - `"OPTED_OUT"`

    - `updated_at: datetime`

      When this status last changed.

  - `is_archived: bool`

    **DEPRECATED:** This field is deprecated and will be removed in a future API version.

  - `is_group: bool`

    Whether this is a group chat

  - `updated_at: datetime`

    When the chat was last updated

  - `service: Optional[ServiceType]`

    Messaging service type

### Example

```python
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
)
page = client.chats.list_chats()
page = page.chats[0]
print(page.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"
        }
      ],
      "health_status": {
        "doc_url": "https://docs.linqapp.com/guides/chats/chat-health#at-risk",
        "status": "AT_RISK",
        "updated_at": "2026-05-01T18:28:25Z"
      },
      "is_archived": true,
      "is_group": true,
      "updated_at": "2024-01-15T10:30:00Z",
      "service": "iMessage"
    }
  ],
  "next_cursor": "next_cursor"
}
```

## Get a chat by ID

`chats.retrieve(strchat_id)  -> Chat`

**get** `/v3/chats/{chatId}`

Retrieve a chat by its unique identifier.

### Parameters

- `chat_id: str`

### Returns

- `class Chat: …`

  - `id: str`

    Unique identifier for the chat

  - `created_at: datetime`

    When the chat was created

  - `display_name: Optional[str]`

    Display name for the chat. Defaults to a comma-separated list of recipient handles. Can be updated for group chats.

  - `handles: List[ChatHandle]`

    List of chat participants with full handle details. Always contains at least two handles (your phone number and the other participant).

    - `id: str`

      Unique identifier for this handle

    - `handle: str`

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

    - `joined_at: datetime`

      When this participant joined the chat

    - `service: ServiceType`

      Messaging service type

      - `"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)

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

      Participant status

      - `"active"`

      - `"left"`

      - `"removed"`

  - `health_status: HealthStatus`

    **[BETA]** Current health for a chat. Always present — chats start at `HEALTHY` and may shift based on engagement and delivery signals on the conversation. Many `AT_RISK` or `CRITICAL` chats on a single line increase the risk of line flagging.

    Switch on `status` to gate sends or surface line health in your UI — the enum is the long-term contract. Each status carries a `doc_url` that deep-links to the relevant section of the Chat Health guide.

    See the [Chat Health guide](/guides/chats/chat-health) for what each status means and how to react.

    - `doc_url: str`

      Deep-link to the relevant section of the Chat Health guide for this status.

    - `status: Literal["HEALTHY", "AT_RISK", "CRITICAL", "OPTED_OUT"]`

      Current health bucket for the chat. See the [Chat Health guide](/guides/chats/chat-health) for what each value means and how to react. `doc_url` deep-links to the relevant section.

      - `"HEALTHY"`

      - `"AT_RISK"`

      - `"CRITICAL"`

      - `"OPTED_OUT"`

    - `updated_at: datetime`

      When this status last changed.

  - `is_archived: bool`

    **DEPRECATED:** This field is deprecated and will be removed in a future API version.

  - `is_group: bool`

    Whether this is a group chat

  - `updated_at: datetime`

    When the chat was last updated

  - `service: Optional[ServiceType]`

    Messaging service type

### Example

```python
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
)
chat = client.chats.retrieve(
    "550e8400-e29b-41d4-a716-446655440000",
)
print(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"
    }
  ],
  "health_status": {
    "doc_url": "https://docs.linqapp.com/guides/chats/chat-health#at-risk",
    "status": "AT_RISK",
    "updated_at": "2026-05-01T18:28:25Z"
  },
  "is_archived": true,
  "is_group": true,
  "updated_at": "2024-01-15T10:30:00Z",
  "service": "iMessage"
}
```

## Update a chat

`chats.update(strchat_id, ChatUpdateParams**kwargs)  -> 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

- `chat_id: str`

- `display_name: Optional[str]`

  New display name for the chat (group chats only)

- `group_chat_icon: Optional[str]`

  URL of an image to set as the group chat icon (group chats only)

### Returns

- `class ChatUpdateResponse: …`

  - `chat_id: Optional[str]`

  - `status: Optional[str]`

### Example

```python
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
)
chat = client.chats.update(
    chat_id="550e8400-e29b-41d4-a716-446655440000",
    display_name="Team Discussion",
)
print(chat.chat_id)
```

#### Response

```json
{
  "chat_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending"
}
```

## Mark chat as read

`chats.mark_as_read(strchat_id)`

**post** `/v3/chats/{chatId}/read`

Mark all messages in a chat as read.

### Parameters

- `chat_id: str`

### Example

```python
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
)
client.chats.mark_as_read(
    "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
```

#### Response

```json
{
  "error": {
    "status": 401,
    "code": 2004,
    "message": "Unauthorized - missing or invalid authentication token",
    "doc_url": "https://docs.linqapp.com/error/codes/2xxx/2004/"
  },
  "success": false
}
```

## Leave a group chat

`chats.leave_chat(strchat_id)  -> 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

- `chat_id: str`

### Returns

- `class ChatLeaveChatResponse: …`

  - `message: Optional[str]`

  - `status: Optional[str]`

  - `trace_id: Optional[str]`

### Example

```python
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
)
response = client.chats.leave_chat(
    "550e8400-e29b-41d4-a716-446655440000",
)
print(response.trace_id)
```

#### Response

```json
{
  "message": "Leave group chat queued",
  "status": "accepted",
  "trace_id": "trace_id"
}
```

## Share your contact card with a chat

`chats.share_contact_card(strchat_id)`

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

- `chat_id: str`

### Example

```python
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
)
client.chats.share_contact_card(
    "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
```

#### Response

```json
{
  "error": {
    "status": 401,
    "code": 2004,
    "message": "Unauthorized - missing or invalid authentication token",
    "doc_url": "https://docs.linqapp.com/error/codes/2xxx/2004/"
  },
  "success": false
}
```

## Send a voice memo to a chat

`chats.send_voicememo(strchat_id, ChatSendVoicememoParams**kwargs)  -> 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

- `chat_id: str`

- `attachment_id: Optional[str]`

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

  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

- `class ChatSendVoicememoResponse: …`

  Response for sending a voice memo to a chat

  - `voice_memo: VoiceMemo`

    - `id: str`

      Message identifier

    - `chat: VoiceMemoChat`

      - `id: str`

        Chat identifier

      - `handles: List[ChatHandle]`

        Chat participants

        - `id: str`

          Unique identifier for this handle

        - `handle: str`

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

        - `joined_at: datetime`

          When this participant joined the chat

        - `service: ServiceType`

          Messaging service type

          - `"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)

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

          Participant status

          - `"active"`

          - `"left"`

          - `"removed"`

      - `is_active: bool`

        Whether the chat is active

      - `is_group: bool`

        Whether this is a group chat

      - `service: ServiceType`

        Messaging service type

    - `created_at: datetime`

      When the voice memo was created

    - `from_: str`

      Sender phone number

    - `status: str`

      Current delivery status

    - `to: List[str]`

      Recipient handles (phone numbers or email addresses)

    - `voice_memo: VoiceMemoVoiceMemo`

      - `id: str`

        Attachment identifier

      - `filename: str`

        Original filename

      - `mime_type: str`

        Audio MIME type

      - `size_bytes: int`

        File size in bytes

      - `url: str`

        CDN URL for downloading the voice memo

      - `duration_ms: Optional[int]`

        Duration in milliseconds

    - `service: Optional[ServiceType]`

      Messaging service type

### Example

```python
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
)
response = client.chats.send_voicememo(
    chat_id="f19ee7b8-8533-4c5c-83ec-4ef8d6d1ddbd",
    voice_memo_url="https://example.com/voice-memo.m4a",
)
print(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

- `class Chat: …`

  - `id: str`

    Unique identifier for the chat

  - `created_at: datetime`

    When the chat was created

  - `display_name: Optional[str]`

    Display name for the chat. Defaults to a comma-separated list of recipient handles. Can be updated for group chats.

  - `handles: List[ChatHandle]`

    List of chat participants with full handle details. Always contains at least two handles (your phone number and the other participant).

    - `id: str`

      Unique identifier for this handle

    - `handle: str`

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

    - `joined_at: datetime`

      When this participant joined the chat

    - `service: ServiceType`

      Messaging service type

      - `"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)

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

      Participant status

      - `"active"`

      - `"left"`

      - `"removed"`

  - `health_status: HealthStatus`

    **[BETA]** Current health for a chat. Always present — chats start at `HEALTHY` and may shift based on engagement and delivery signals on the conversation. Many `AT_RISK` or `CRITICAL` chats on a single line increase the risk of line flagging.

    Switch on `status` to gate sends or surface line health in your UI — the enum is the long-term contract. Each status carries a `doc_url` that deep-links to the relevant section of the Chat Health guide.

    See the [Chat Health guide](/guides/chats/chat-health) for what each status means and how to react.

    - `doc_url: str`

      Deep-link to the relevant section of the Chat Health guide for this status.

    - `status: Literal["HEALTHY", "AT_RISK", "CRITICAL", "OPTED_OUT"]`

      Current health bucket for the chat. See the [Chat Health guide](/guides/chats/chat-health) for what each value means and how to react. `doc_url` deep-links to the relevant section.

      - `"HEALTHY"`

      - `"AT_RISK"`

      - `"CRITICAL"`

      - `"OPTED_OUT"`

    - `updated_at: datetime`

      When this status last changed.

  - `is_archived: bool`

    **DEPRECATED:** This field is deprecated and will be removed in a future API version.

  - `is_group: bool`

    Whether this is a group chat

  - `updated_at: datetime`

    When the chat was last updated

  - `service: Optional[ServiceType]`

    Messaging service type

### Link Part

- `class LinkPart: …`

  - `type: Literal["link"]`

    Indicates this is a rich link preview part

    - `"link"`

  - `value: str`

    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

- `class MediaPart: …`

  - `type: Literal["media"]`

    Indicates this is a media attachment part

    - `"media"`

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

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

### Message Content

- `class MessageContent: …`

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

    - `class TextPart: …`

      - `type: Literal["text"]`

        Indicates this is a text message part

        - `"text"`

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

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

          - `"big"`

          - `"small"`

          - `"shake"`

          - `"nod"`

          - `"explode"`

          - `"ripple"`

          - `"bloom"`

          - `"jitter"`

        - `style: Optional[Literal["bold", "italic", "strikethrough", "underline"]]`

          Text style to apply. Mutually exclusive with `animation`.

          - `"bold"`

          - `"italic"`

          - `"strikethrough"`

          - `"underline"`

    - `class MediaPart: …`

      - `type: Literal["media"]`

        Indicates this is a media attachment part

        - `"media"`

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

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

    - `class LinkPart: …`

      - `type: Literal["link"]`

        Indicates this is a rich link preview part

        - `"link"`

      - `value: str`

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

      - `"screen"`

      - `"bubble"`

  - `idempotency_key: Optional[str]`

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

  - `preferred_service: Optional[ServiceType]`

    Messaging service type

    - `"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

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

### Text Part

- `class TextPart: …`

  - `type: Literal["text"]`

    Indicates this is a text message part

    - `"text"`

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

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

      - `"big"`

      - `"small"`

      - `"shake"`

      - `"nod"`

      - `"explode"`

      - `"ripple"`

      - `"bloom"`

      - `"jitter"`

    - `style: Optional[Literal["bold", "italic", "strikethrough", "underline"]]`

      Text style to apply. Mutually exclusive with `animation`.

      - `"bold"`

      - `"italic"`

      - `"strikethrough"`

      - `"underline"`

### Chat Create Response

- `class ChatCreateResponse: …`

  Response for creating a new chat with an initial message

  - `chat: Chat`

    - `id: str`

      Unique identifier for the created chat (UUID)

    - `display_name: Optional[str]`

      Display name for the chat. Defaults to a comma-separated list of recipient handles. Can be updated for group chats.

    - `handles: List[ChatHandle]`

      List of participants in the chat. Always contains at least two handles (your phone number and the other participant).

      - `id: str`

        Unique identifier for this handle

      - `handle: str`

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

      - `joined_at: datetime`

        When this participant joined the chat

      - `service: ServiceType`

        Messaging service type

        - `"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)

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

        Participant status

        - `"active"`

        - `"left"`

        - `"removed"`

    - `health_status: ChatHealthStatus`

      **[BETA]** Current health for a chat. Always present — chats start at `HEALTHY` and may shift based on engagement and delivery signals on the conversation. Many `AT_RISK` or `CRITICAL` chats on a single line increase the risk of line flagging.

      Switch on `status` to gate sends or surface line health in your UI — the enum is the long-term contract. Each status carries a `doc_url` that deep-links to the relevant section of the Chat Health guide.

      See the [Chat Health guide](/guides/chats/chat-health) for what each status means and how to react.

      - `doc_url: str`

        Deep-link to the relevant section of the Chat Health guide for this status.

      - `status: Literal["HEALTHY", "AT_RISK", "CRITICAL", "OPTED_OUT"]`

        Current health bucket for the chat. See the [Chat Health guide](/guides/chats/chat-health) for what each value means and how to react. `doc_url` deep-links to the relevant section.

        - `"HEALTHY"`

        - `"AT_RISK"`

        - `"CRITICAL"`

        - `"OPTED_OUT"`

      - `updated_at: datetime`

        When this status last changed.

    - `is_group: bool`

      Whether this is a group chat

    - `message: SentMessage`

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

      - `id: str`

        Message identifier (UUID)

      - `created_at: datetime`

        When the message was created

      - `delivery_status: Literal["pending", "queued", "sent", 2 more]`

        Current delivery status of a message

        - `"pending"`

        - `"queued"`

        - `"sent"`

        - `"delivered"`

        - `"failed"`

      - `is_read: bool`

        Whether the message has been read

      - `parts: List[Part]`

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

        - `class TextPartResponse: …`

          A text message part

          - `reactions: Optional[List[Reaction]]`

            Reactions on this message part

            - `handle: ChatHandle`

              - `id: str`

                Unique identifier for this handle

              - `handle: str`

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

              - `joined_at: datetime`

                When this participant joined the chat

              - `service: ServiceType`

                Messaging service type

              - `is_me: Optional[bool]`

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

              - `left_at: Optional[datetime]`

                When they left (if applicable)

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

                Participant status

            - `is_me: bool`

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

              - `width: Optional[int]`

                Sticker image width in pixels

          - `type: Literal["text"]`

            Indicates this is a text message part

            - `"text"`

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

              - `"big"`

              - `"small"`

              - `"shake"`

              - `"nod"`

              - `"explode"`

              - `"ripple"`

              - `"bloom"`

              - `"jitter"`

            - `style: Optional[Literal["bold", "italic", "strikethrough", "underline"]]`

              Text style to apply. Mutually exclusive with `animation`.

              - `"bold"`

              - `"italic"`

              - `"strikethrough"`

              - `"underline"`

        - `class MediaPartResponse: …`

          A media attachment part

          - `id: str`

            Unique attachment identifier

          - `filename: str`

            Original filename

          - `mime_type: str`

            MIME type of the file

          - `reactions: Optional[List[Reaction]]`

            Reactions on this message part

            - `handle: ChatHandle`

            - `is_me: bool`

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

          - `size_bytes: int`

            File size in bytes

          - `type: Literal["media"]`

            Indicates this is a media attachment part

            - `"media"`

          - `url: str`

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

        - `class LinkPartResponse: …`

          A rich link preview part

          - `reactions: Optional[List[Reaction]]`

            Reactions on this message part

            - `handle: ChatHandle`

            - `is_me: bool`

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

          - `type: Literal["link"]`

            Indicates this is a rich link preview part

            - `"link"`

          - `value: str`

            The URL

      - `sent_at: Optional[datetime]`

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

      - `delivered_at: Optional[datetime]`

        When the message was delivered

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

          - `"screen"`

          - `"bubble"`

      - `from_handle: Optional[ChatHandle]`

        The sender of this message as a full handle object

      - `preferred_service: Optional[ServiceType]`

        Messaging service type

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

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

      - `service: Optional[ServiceType]`

        Messaging service type

    - `service: ServiceType`

      Messaging service type

### Chat Update Response

- `class ChatUpdateResponse: …`

  - `chat_id: Optional[str]`

  - `status: Optional[str]`

### Chat Leave Chat Response

- `class ChatLeaveChatResponse: …`

  - `message: Optional[str]`

  - `status: Optional[str]`

  - `trace_id: Optional[str]`

### Chat Send Voicememo Response

- `class ChatSendVoicememoResponse: …`

  Response for sending a voice memo to a chat

  - `voice_memo: VoiceMemo`

    - `id: str`

      Message identifier

    - `chat: VoiceMemoChat`

      - `id: str`

        Chat identifier

      - `handles: List[ChatHandle]`

        Chat participants

        - `id: str`

          Unique identifier for this handle

        - `handle: str`

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

        - `joined_at: datetime`

          When this participant joined the chat

        - `service: ServiceType`

          Messaging service type

          - `"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)

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

          Participant status

          - `"active"`

          - `"left"`

          - `"removed"`

      - `is_active: bool`

        Whether the chat is active

      - `is_group: bool`

        Whether this is a group chat

      - `service: ServiceType`

        Messaging service type

    - `created_at: datetime`

      When the voice memo was created

    - `from_: str`

      Sender phone number

    - `status: str`

      Current delivery status

    - `to: List[str]`

      Recipient handles (phone numbers or email addresses)

    - `voice_memo: VoiceMemoVoiceMemo`

      - `id: str`

        Attachment identifier

      - `filename: str`

        Original filename

      - `mime_type: str`

        Audio MIME type

      - `size_bytes: int`

        File size in bytes

      - `url: str`

        CDN URL for downloading the voice memo

      - `duration_ms: Optional[int]`

        Duration in milliseconds

    - `service: Optional[ServiceType]`

      Messaging service type

# Participants

## Add a participant to a chat

`chats.participants.add(strchat_id, ParticipantAddParams**kwargs)  -> 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

- `chat_id: str`

- `handle: str`

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

### Returns

- `class ParticipantAddResponse: …`

  - `message: Optional[str]`

  - `status: Optional[str]`

  - `trace_id: Optional[str]`

### Example

```python
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
)
response = client.chats.participants.add(
    chat_id="550e8400-e29b-41d4-a716-446655440000",
    handle="+12052499136",
)
print(response.trace_id)
```

#### Response

```json
{
  "message": "Participant addition queued",
  "status": "accepted",
  "trace_id": "trace_id"
}
```

## Remove a participant from a chat

`chats.participants.remove(strchat_id, ParticipantRemoveParams**kwargs)  -> 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

- `chat_id: str`

- `handle: str`

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

### Returns

- `class ParticipantRemoveResponse: …`

  - `message: Optional[str]`

  - `status: Optional[str]`

  - `trace_id: Optional[str]`

### Example

```python
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
)
participant = client.chats.participants.remove(
    chat_id="550e8400-e29b-41d4-a716-446655440000",
    handle="+12052499136",
)
print(participant.trace_id)
```

#### Response

```json
{
  "message": "Participant removal queued",
  "status": "accepted",
  "trace_id": "trace_id"
}
```

## Domain Types

### Participant Add Response

- `class ParticipantAddResponse: …`

  - `message: Optional[str]`

  - `status: Optional[str]`

  - `trace_id: Optional[str]`

### Participant Remove Response

- `class ParticipantRemoveResponse: …`

  - `message: Optional[str]`

  - `status: Optional[str]`

  - `trace_id: Optional[str]`

# Typing

## Start typing indicator

`chats.typing.start(strchat_id)`

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

- `chat_id: str`

### Example

```python
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
)
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",
    "doc_url": "https://docs.linqapp.com/error/codes/1xxx/1002/"
  },
  "success": false
}
```

## Stop typing indicator

`chats.typing.stop(strchat_id)`

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

- `chat_id: str`

### Example

```python
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
)
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",
    "doc_url": "https://docs.linqapp.com/error/codes/1xxx/1002/"
  },
  "success": false
}
```

# Messages

## Send a message to an existing chat

`chats.messages.send(strchat_id, MessageSendParams**kwargs)  -> 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

- `chat_id: str`

- `message: MessageContentParam`

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

    - `class TextPart: …`

      - `type: Literal["text"]`

        Indicates this is a text message part

        - `"text"`

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

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

          - `"big"`

          - `"small"`

          - `"shake"`

          - `"nod"`

          - `"explode"`

          - `"ripple"`

          - `"bloom"`

          - `"jitter"`

        - `style: Optional[Literal["bold", "italic", "strikethrough", "underline"]]`

          Text style to apply. Mutually exclusive with `animation`.

          - `"bold"`

          - `"italic"`

          - `"strikethrough"`

          - `"underline"`

    - `class MediaPart: …`

      - `type: Literal["media"]`

        Indicates this is a media attachment part

        - `"media"`

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

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

    - `class LinkPart: …`

      - `type: Literal["link"]`

        Indicates this is a rich link preview part

        - `"link"`

      - `value: str`

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

      - `"screen"`

      - `"bubble"`

  - `idempotency_key: Optional[str]`

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

  - `preferred_service: Optional[ServiceType]`

    Messaging service type

    - `"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

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

### Returns

- `class MessageSendResponse: …`

  Response for sending a message to a chat

  - `chat_id: str`

    Unique identifier of the chat this message was sent to

  - `message: SentMessage`

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

    - `id: str`

      Message identifier (UUID)

    - `created_at: datetime`

      When the message was created

    - `delivery_status: Literal["pending", "queued", "sent", 2 more]`

      Current delivery status of a message

      - `"pending"`

      - `"queued"`

      - `"sent"`

      - `"delivered"`

      - `"failed"`

    - `is_read: bool`

      Whether the message has been read

    - `parts: List[Part]`

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

      - `class TextPartResponse: …`

        A text message part

        - `reactions: Optional[List[Reaction]]`

          Reactions on this message part

          - `handle: ChatHandle`

            - `id: str`

              Unique identifier for this handle

            - `handle: str`

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

            - `joined_at: datetime`

              When this participant joined the chat

            - `service: ServiceType`

              Messaging service type

              - `"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)

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

              Participant status

              - `"active"`

              - `"left"`

              - `"removed"`

          - `is_me: bool`

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

            - `width: Optional[int]`

              Sticker image width in pixels

        - `type: Literal["text"]`

          Indicates this is a text message part

          - `"text"`

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

            - `"big"`

            - `"small"`

            - `"shake"`

            - `"nod"`

            - `"explode"`

            - `"ripple"`

            - `"bloom"`

            - `"jitter"`

          - `style: Optional[Literal["bold", "italic", "strikethrough", "underline"]]`

            Text style to apply. Mutually exclusive with `animation`.

            - `"bold"`

            - `"italic"`

            - `"strikethrough"`

            - `"underline"`

      - `class MediaPartResponse: …`

        A media attachment part

        - `id: str`

          Unique attachment identifier

        - `filename: str`

          Original filename

        - `mime_type: str`

          MIME type of the file

        - `reactions: Optional[List[Reaction]]`

          Reactions on this message part

          - `handle: ChatHandle`

          - `is_me: bool`

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

        - `size_bytes: int`

          File size in bytes

        - `type: Literal["media"]`

          Indicates this is a media attachment part

          - `"media"`

        - `url: str`

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

      - `class LinkPartResponse: …`

        A rich link preview part

        - `reactions: Optional[List[Reaction]]`

          Reactions on this message part

          - `handle: ChatHandle`

          - `is_me: bool`

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

        - `type: Literal["link"]`

          Indicates this is a rich link preview part

          - `"link"`

        - `value: str`

          The URL

    - `sent_at: Optional[datetime]`

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

    - `delivered_at: Optional[datetime]`

      When the message was delivered

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

        - `"screen"`

        - `"bubble"`

    - `from_handle: Optional[ChatHandle]`

      The sender of this message as a full handle object

    - `preferred_service: Optional[ServiceType]`

      Messaging service type

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

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

    - `service: Optional[ServiceType]`

      Messaging service type

### Example

```python
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
)
response = client.chats.messages.send(
    chat_id="550e8400-e29b-41d4-a716-446655440000",
    message={
        "parts": [{
            "type": "text",
            "value": "Hello, world!",
        }]
    },
)
print(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

`chats.messages.list(strchat_id, MessageListParams**kwargs)  -> SyncListMessagesPagination[Message]`

**get** `/v3/chats/{chatId}/messages`

Retrieve messages from a specific chat with pagination support.

### Parameters

- `chat_id: str`

- `cursor: Optional[str]`

  Pagination cursor from previous next_cursor response

- `limit: Optional[int]`

  Maximum number of messages to return

### Returns

- `class Message: …`

  - `id: str`

    Unique identifier for the message

  - `chat_id: str`

    ID of the chat this message belongs to

  - `created_at: datetime`

    When the message was created

  - `is_delivered: bool`

    Whether the message has been delivered

  - `is_from_me: bool`

    Whether this message was sent by the authenticated user

  - `is_read: bool`

    Whether the message has been read

  - `updated_at: datetime`

    When the message was last updated

  - `delivered_at: Optional[datetime]`

    When the message was delivered

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

      - `"screen"`

      - `"bubble"`

  - `from_: Optional[str]`

    DEPRECATED: Use from_handle instead. Phone number of the message sender.

  - `from_handle: Optional[ChatHandle]`

    The sender of this message as a full handle object

    - `id: str`

      Unique identifier for this handle

    - `handle: str`

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

    - `joined_at: datetime`

      When this participant joined the chat

    - `service: ServiceType`

      Messaging service type

      - `"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)

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

      Participant status

      - `"active"`

      - `"left"`

      - `"removed"`

  - `parts: Optional[List[Part]]`

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

    - `class TextPartResponse: …`

      A text message part

      - `reactions: Optional[List[Reaction]]`

        Reactions on this message part

        - `handle: ChatHandle`

        - `is_me: bool`

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

          - `width: Optional[int]`

            Sticker image width in pixels

      - `type: Literal["text"]`

        Indicates this is a text message part

        - `"text"`

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

          - `"big"`

          - `"small"`

          - `"shake"`

          - `"nod"`

          - `"explode"`

          - `"ripple"`

          - `"bloom"`

          - `"jitter"`

        - `style: Optional[Literal["bold", "italic", "strikethrough", "underline"]]`

          Text style to apply. Mutually exclusive with `animation`.

          - `"bold"`

          - `"italic"`

          - `"strikethrough"`

          - `"underline"`

    - `class MediaPartResponse: …`

      A media attachment part

      - `id: str`

        Unique attachment identifier

      - `filename: str`

        Original filename

      - `mime_type: str`

        MIME type of the file

      - `reactions: Optional[List[Reaction]]`

        Reactions on this message part

        - `handle: ChatHandle`

        - `is_me: bool`

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

      - `size_bytes: int`

        File size in bytes

      - `type: Literal["media"]`

        Indicates this is a media attachment part

        - `"media"`

      - `url: str`

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

    - `class LinkPartResponse: …`

      A rich link preview part

      - `reactions: Optional[List[Reaction]]`

        Reactions on this message part

        - `handle: ChatHandle`

        - `is_me: bool`

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

      - `type: Literal["link"]`

        Indicates this is a rich link preview part

        - `"link"`

      - `value: str`

        The URL

  - `preferred_service: Optional[ServiceType]`

    Messaging service type

  - `read_at: Optional[datetime]`

    When the message was read

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

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

  - `sent_at: Optional[datetime]`

    When the message was sent

  - `service: Optional[ServiceType]`

    Messaging service type

### Example

```python
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
)
page = client.chats.messages.list(
    chat_id="550e8400-e29b-41d4-a716-446655440000",
)
page = page.messages[0]
print(page.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

- `class SentMessage: …`

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

  - `id: str`

    Message identifier (UUID)

  - `created_at: datetime`

    When the message was created

  - `delivery_status: Literal["pending", "queued", "sent", 2 more]`

    Current delivery status of a message

    - `"pending"`

    - `"queued"`

    - `"sent"`

    - `"delivered"`

    - `"failed"`

  - `is_read: bool`

    Whether the message has been read

  - `parts: List[Part]`

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

    - `class TextPartResponse: …`

      A text message part

      - `reactions: Optional[List[Reaction]]`

        Reactions on this message part

        - `handle: ChatHandle`

          - `id: str`

            Unique identifier for this handle

          - `handle: str`

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

          - `joined_at: datetime`

            When this participant joined the chat

          - `service: ServiceType`

            Messaging service type

            - `"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)

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

            Participant status

            - `"active"`

            - `"left"`

            - `"removed"`

        - `is_me: bool`

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

          - `width: Optional[int]`

            Sticker image width in pixels

      - `type: Literal["text"]`

        Indicates this is a text message part

        - `"text"`

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

          - `"big"`

          - `"small"`

          - `"shake"`

          - `"nod"`

          - `"explode"`

          - `"ripple"`

          - `"bloom"`

          - `"jitter"`

        - `style: Optional[Literal["bold", "italic", "strikethrough", "underline"]]`

          Text style to apply. Mutually exclusive with `animation`.

          - `"bold"`

          - `"italic"`

          - `"strikethrough"`

          - `"underline"`

    - `class MediaPartResponse: …`

      A media attachment part

      - `id: str`

        Unique attachment identifier

      - `filename: str`

        Original filename

      - `mime_type: str`

        MIME type of the file

      - `reactions: Optional[List[Reaction]]`

        Reactions on this message part

        - `handle: ChatHandle`

        - `is_me: bool`

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

      - `size_bytes: int`

        File size in bytes

      - `type: Literal["media"]`

        Indicates this is a media attachment part

        - `"media"`

      - `url: str`

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

    - `class LinkPartResponse: …`

      A rich link preview part

      - `reactions: Optional[List[Reaction]]`

        Reactions on this message part

        - `handle: ChatHandle`

        - `is_me: bool`

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

      - `type: Literal["link"]`

        Indicates this is a rich link preview part

        - `"link"`

      - `value: str`

        The URL

  - `sent_at: Optional[datetime]`

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

  - `delivered_at: Optional[datetime]`

    When the message was delivered

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

      - `"screen"`

      - `"bubble"`

  - `from_handle: Optional[ChatHandle]`

    The sender of this message as a full handle object

  - `preferred_service: Optional[ServiceType]`

    Messaging service type

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

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

  - `service: Optional[ServiceType]`

    Messaging service type

### Message Send Response

- `class MessageSendResponse: …`

  Response for sending a message to a chat

  - `chat_id: str`

    Unique identifier of the chat this message was sent to

  - `message: SentMessage`

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

    - `id: str`

      Message identifier (UUID)

    - `created_at: datetime`

      When the message was created

    - `delivery_status: Literal["pending", "queued", "sent", 2 more]`

      Current delivery status of a message

      - `"pending"`

      - `"queued"`

      - `"sent"`

      - `"delivered"`

      - `"failed"`

    - `is_read: bool`

      Whether the message has been read

    - `parts: List[Part]`

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

      - `class TextPartResponse: …`

        A text message part

        - `reactions: Optional[List[Reaction]]`

          Reactions on this message part

          - `handle: ChatHandle`

            - `id: str`

              Unique identifier for this handle

            - `handle: str`

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

            - `joined_at: datetime`

              When this participant joined the chat

            - `service: ServiceType`

              Messaging service type

              - `"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)

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

              Participant status

              - `"active"`

              - `"left"`

              - `"removed"`

          - `is_me: bool`

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

            - `width: Optional[int]`

              Sticker image width in pixels

        - `type: Literal["text"]`

          Indicates this is a text message part

          - `"text"`

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

            - `"big"`

            - `"small"`

            - `"shake"`

            - `"nod"`

            - `"explode"`

            - `"ripple"`

            - `"bloom"`

            - `"jitter"`

          - `style: Optional[Literal["bold", "italic", "strikethrough", "underline"]]`

            Text style to apply. Mutually exclusive with `animation`.

            - `"bold"`

            - `"italic"`

            - `"strikethrough"`

            - `"underline"`

      - `class MediaPartResponse: …`

        A media attachment part

        - `id: str`

          Unique attachment identifier

        - `filename: str`

          Original filename

        - `mime_type: str`

          MIME type of the file

        - `reactions: Optional[List[Reaction]]`

          Reactions on this message part

          - `handle: ChatHandle`

          - `is_me: bool`

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

        - `size_bytes: int`

          File size in bytes

        - `type: Literal["media"]`

          Indicates this is a media attachment part

          - `"media"`

        - `url: str`

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

      - `class LinkPartResponse: …`

        A rich link preview part

        - `reactions: Optional[List[Reaction]]`

          Reactions on this message part

          - `handle: ChatHandle`

          - `is_me: bool`

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

        - `type: Literal["link"]`

          Indicates this is a rich link preview part

          - `"link"`

        - `value: str`

          The URL

    - `sent_at: Optional[datetime]`

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

    - `delivered_at: Optional[datetime]`

      When the message was delivered

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

        - `"screen"`

        - `"bubble"`

    - `from_handle: Optional[ChatHandle]`

      The sender of this message as a full handle object

    - `preferred_service: Optional[ServiceType]`

      Messaging service type

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

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

    - `service: Optional[ServiceType]`

      Messaging service type
