## Update an iMessage app card in place

`messages.update_app_card(strmessage_id, MessageUpdateAppCardParams**kwargs)  -> MessageUpdateAppCardResponse`

**post** `/v3/messages/{messageId}/update`

Replaces a previously delivered `imessage_app` card on the recipient's screen with new
content, instead of posting a new bubble (like a game move redrawing the board).

The update is delivered as a **new message** with its own id and delivery lifecycle
(`message.sent` / `message.delivered` / `message.failed` webhooks fire for the new id).
To update the card again, reference the message id returned by this call.

Constraints:

- The referenced message must be an `imessage_app` card sent by you (`400` otherwise —
  inbound cards cannot be updated).
- The referenced card must already be delivered (`409` otherwise — retry after the
  `message.delivered` webhook for it).
- The app identity (`team_id`, `bundle_id`, name) is inherited from the original card and
  cannot change; only `url`, `fallback_text`, and `layout` are replaced.
- iMessage-only, like all app cards.
- Concurrent updates against the same card are not serialized server-side; the last one
  delivered wins on the recipient's screen. Serialize updates by always referencing the
  message id returned by the previous call.

### Parameters

- `message_id: str`

- `layout: Layout`

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

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

  `* Note - requires a trusted chat w/ inbound activity`

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

  - `caption: Optional[str]`

    Primary label, top-left and bold.

  - `image_subtitle: Optional[str]`

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

  - `image_title: Optional[str]`

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

  - `image_url: Optional[str]`

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

  - `subcaption: Optional[str]`

    Secondary label, below `caption` on the left.

  - `trailing_caption: Optional[str]`

    Label shown top-right.

  - `trailing_subcaption: Optional[str]`

    Label shown below `trailing_caption`, on the right.

- `fallback_text: Optional[str]`

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

- `interactive: Optional[bool]`

  Whether the updated card renders as your app's interactive balloon for recipients who
  have your iMessage app installed. `true` (default) lets your installed extension draw its
  live view; `false` always shows the static `layout` card. Recipients without your app
  always see the static card regardless of this flag.

  Defaults to `true` when omitted — it is **not** inherited from the original card. To keep a
  card static across updates, re-send `interactive: false` on each update.

- `url: Optional[str]`

  URL the recipient's app opens when they tap the updated card.

### Returns

- `class MessageUpdateAppCardResponse: …`

  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", 4 more]`

      Current delivery status of a message

      - `"pending"`

      - `"queued"`

      - `"sent"`

      - `"delivered"`

      - `"received"`

      - `"read"`

      - `"failed"`

    - `is_read: bool`

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

    - `parts: List[Part]`

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

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

      - `class PartIMessageAppPartResponse: …`

        An iMessage app card part.

        - `app: PartIMessageAppPartResponseApp`

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

          - `bundle_id: str`

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

          - `name: str`

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

          - `team_id: str`

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

          - `app_store_id: Optional[int]`

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

        - `layout: PartIMessageAppPartResponseLayout`

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

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

          `* Note - requires a trusted chat w/ inbound activity`

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

          - `caption: Optional[str]`

            Primary label, top-left and bold.

          - `image_subtitle: Optional[str]`

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

          - `image_title: Optional[str]`

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

          - `image_url: Optional[str]`

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

          - `subcaption: Optional[str]`

            Secondary label, below `caption` on the left.

          - `trailing_caption: Optional[str]`

            Label shown top-right.

          - `trailing_subcaption: Optional[str]`

            Label shown below `trailing_caption`, on the right.

        - `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["imessage_app"]`

          Indicates this is an iMessage app card part.

          - `"imessage_app"`

        - `url: str`

          The URL delivered to the iMessage app on tap.

        - `fallback_text: Optional[str]`

          Fallback text for surfaces that cannot render the card.

    - `sent_at: Optional[datetime]`

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

    - `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.messages.update_app_card(
    message_id="69a37c7d-af4f-4b5e-af42-e28e98ce873a",
    layout={
        "caption": "Score: 2 – 1"
    },
    fallback_text="Score update",
    url="https://app.example.com/card?game=7f3a&move=2",
)
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"
  }
}
```
