## Update an iMessage app card in place

`client.Messages.UpdateAppCard(ctx, messageID, body) (*MessageUpdateAppCardResponse, error)`

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

- `messageID string`

- `body MessageUpdateAppCardParams`

  - `Layout param.Field[MessageUpdateAppCardParamsLayout]`

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

      Primary label, top-left and bold.

    - `ImageSubtitle string`

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

    - `ImageTitle string`

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

    - `ImageURL string`

      URL of an image (JPEG, PNG, HEIF, or WebP) 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. In responses, this is the re-hosted `cdn.linqapp.com` copy of the image you supplied, not your original URL.

    - `Subcaption string`

      Secondary label, below `caption` on the left.

    - `TrailingCaption string`

      Label shown top-right.

    - `TrailingSubcaption string`

      Label shown below `trailing_caption`, on the right.

  - `App param.Field[MessageUpdateAppCardParamsApp]`

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

    - `BundleID string`

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

    - `Name string`

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

    - `TeamID string`

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

    - `AppStoreID int64`

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

  - `Experience param.Field[MessageUpdateAppCardParamsExperience]`

    Invokes an action on an experience — a third party that renders inside
    Linq's iMessage app. Linq resolves the recipient's connection, mints any
    session the action needs, composes the card and sends it; none of that
    is visible to you.

    Call `GET /v3/experiences/{experience}` for the actions you may invoke
    and the fields each accepts.

    - `Action string`

      Which of its actions, e.g. `attach_card`.

    - `Name string`

      The experience to invoke, e.g. `agentcard` or `agentpay`.

    - `Params map[string, any]`

      Values for the fields this action exposes. Keys are exactly the
      field names listed for the action — no mapping, no nesting.

      Display copy only, except a `url`-type field — that value sets the
      destination, and must be an absolute `https` URL.

      Some fields are read rather than sent: `agentpay`'s
      `request_payment` takes only a `checkout_url` and resolves the
      amount and reason from that payment request itself, so the card
      cannot state a figure the checkout will not charge.

  - `FallbackText param.Field[string]`

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

  - `Interactive param.Field[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 param.Field[string]`

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

    Mutually exclusive with `experience` and `raw_payload_data`.

### Returns

- `type MessageUpdateAppCardResponse struct{…}`

  Response for sending a message to a chat

  - `ChatID string`

    Unique identifier of the chat this message was sent to

  - `Message SentMessage`

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

    - `ID string`

      Message identifier (UUID)

    - `CreatedAt Time`

      When the message was created

    - `DeliveryStatus SentMessageDeliveryStatus`

      Current delivery status of a message

      - `const SentMessageDeliveryStatusPending SentMessageDeliveryStatus = "pending"`

      - `const SentMessageDeliveryStatusQueued SentMessageDeliveryStatus = "queued"`

      - `const SentMessageDeliveryStatusSent SentMessageDeliveryStatus = "sent"`

      - `const SentMessageDeliveryStatusDelivered SentMessageDeliveryStatus = "delivered"`

      - `const SentMessageDeliveryStatusReceived SentMessageDeliveryStatus = "received"`

      - `const SentMessageDeliveryStatusRead SentMessageDeliveryStatus = "read"`

      - `const SentMessageDeliveryStatusFailed SentMessageDeliveryStatus = "failed"`

    - `IsRead bool`

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

    - `Parts []SentMessagePartUnion`

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

      - `type TextPartResponse struct{…}`

        A text message part

        - `Reactions []Reaction`

          Reactions on this message part

          - `Handle ChatHandle`

            - `ID string`

              Unique identifier for this handle

            - `Handle string`

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

            - `JoinedAt Time`

              When this participant joined the chat

            - `Service ServiceType`

              Messaging service type

              - `const ServiceTypeIMessage ServiceType = "iMessage"`

              - `const ServiceTypeSMS ServiceType = "SMS"`

              - `const ServiceTypeRCS ServiceType = "RCS"`

            - `IsMe bool`

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

            - `LeftAt Time`

              When they left (if applicable)

            - `Status ChatHandleStatus`

              Participant status

              - `const ChatHandleStatusActive ChatHandleStatus = "active"`

              - `const ChatHandleStatusLeft ChatHandleStatus = "left"`

              - `const ChatHandleStatusRemoved ChatHandleStatus = "removed"`

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

            - `const ReactionTypeLove ReactionType = "love"`

            - `const ReactionTypeLike ReactionType = "like"`

            - `const ReactionTypeDislike ReactionType = "dislike"`

            - `const ReactionTypeLaugh ReactionType = "laugh"`

            - `const ReactionTypeEmphasize ReactionType = "emphasize"`

            - `const ReactionTypeQuestion ReactionType = "question"`

            - `const ReactionTypeCustom ReactionType = "custom"`

            - `const ReactionTypeSticker ReactionType = "sticker"`

          - `ID string`

            Identifier for this reaction. Pass it to
            `PATCH /v3/messages/{messageId}/reactions/{reactionId}` to move a sticker.

            Stickers placed before this API shipped can be read but not moved: the
            device-side reference needed to reposition them was never recorded, so
            `PATCH` returns 404 for those.

          - `CustomEmoji string`

            Custom emoji if type is "custom", null otherwise

          - `Sticker ReactionSticker`

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

            - `FileName string`

              Filename of the sticker

            - `Height int64`

              Sticker image height in pixels

            - `MimeType string`

              MIME type of the sticker image

            - `URL string`

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

            - `Width int64`

              Sticker image width in pixels

        - `Type TextPartResponseType`

          Indicates this is a text message part

          - `const TextPartResponseTypeText TextPartResponseType = "text"`

        - `Value string`

          The text content

        - `Mention string`

          DEPRECATED: Use `mentions` instead. Handle (E.164 phone number or Apple ID email)
          of the **first** mention on this part. A part may carry several mentions; this
          field shows only the first in `value` order, so it cannot be used to determine
          whether a given participant was mentioned. `null` when the part carries no mention.

        - `MentionRange []int64`

          DEPRECATED: Use `mentions[].range` instead. Character range `[start, end)` in
          `value` highlighted as the **first** mention only. `null` when the range was
          omitted (the whole `value` is highlighted) or the part carries no mention.
          *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.*

        - `Mentions []TextPartResponseMention`

          Every mention on this part, in the order they appear in `value`. `null` when the
          part carries no mention. A part can carry several mentions of different people —
          check `is_me` to tell whether this line was one of them.

          Only iMessage carries mentions. On a received message this is populated when the
          sender was on iMessage; SMS and RCS have no way to mark a mention, so a message
          from an SMS or RCS participant arrives as plain text with `mentions` null, even in
          a group where other participants are on iMessage.

          - `Handle string`

            Address of the mentioned participant, exactly as the device recorded it — an E.164
            phone number or an email address.

          - `IsMe bool`

            Whether the mentioned participant is this line.

          - `Range []int64`

            Character range `[start, end)` in `value` highlighted as this mention.
            *Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.*

        - `TextDecorations []TextDecoration`

          Text decorations applied to character ranges in the value

          - `Range []int64`

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

            Animated text effect to apply. Mutually exclusive with `style`.

            - `const TextDecorationAnimationBig TextDecorationAnimation = "big"`

            - `const TextDecorationAnimationSmall TextDecorationAnimation = "small"`

            - `const TextDecorationAnimationShake TextDecorationAnimation = "shake"`

            - `const TextDecorationAnimationNod TextDecorationAnimation = "nod"`

            - `const TextDecorationAnimationExplode TextDecorationAnimation = "explode"`

            - `const TextDecorationAnimationRipple TextDecorationAnimation = "ripple"`

            - `const TextDecorationAnimationBloom TextDecorationAnimation = "bloom"`

            - `const TextDecorationAnimationJitter TextDecorationAnimation = "jitter"`

          - `Style TextDecorationStyle`

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

            - `const TextDecorationStyleBold TextDecorationStyle = "bold"`

            - `const TextDecorationStyleItalic TextDecorationStyle = "italic"`

            - `const TextDecorationStyleStrikethrough TextDecorationStyle = "strikethrough"`

            - `const TextDecorationStyleUnderline TextDecorationStyle = "underline"`

      - `type MediaPartResponse struct{…}`

        A media attachment part

        - `ID string`

          Unique attachment identifier

        - `Filename string`

          Original filename

        - `MimeType string`

          MIME type of the file

        - `Reactions []Reaction`

          Reactions on this message part

          - `Handle ChatHandle`

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

          - `ID string`

            Identifier for this reaction. Pass it to
            `PATCH /v3/messages/{messageId}/reactions/{reactionId}` to move a sticker.

            Stickers placed before this API shipped can be read but not moved: the
            device-side reference needed to reposition them was never recorded, so
            `PATCH` returns 404 for those.

          - `CustomEmoji string`

            Custom emoji if type is "custom", null otherwise

          - `Sticker ReactionSticker`

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

        - `SizeBytes int64`

          File size in bytes

        - `Type MediaPartResponseType`

          Indicates this is a media attachment part

          - `const MediaPartResponseTypeMedia MediaPartResponseType = "media"`

        - `URL string`

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

      - `type LinkPartResponse struct{…}`

        A rich link preview part

        - `Reactions []Reaction`

          Reactions on this message part

          - `Handle ChatHandle`

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

          - `ID string`

            Identifier for this reaction. Pass it to
            `PATCH /v3/messages/{messageId}/reactions/{reactionId}` to move a sticker.

            Stickers placed before this API shipped can be read but not moved: the
            device-side reference needed to reposition them was never recorded, so
            `PATCH` returns 404 for those.

          - `CustomEmoji string`

            Custom emoji if type is "custom", null otherwise

          - `Sticker ReactionSticker`

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

        - `Type LinkPartResponseType`

          Indicates this is a rich link preview part

          - `const LinkPartResponseTypeLink LinkPartResponseType = "link"`

        - `Value string`

          The URL

      - `type SentMessagePartIMessageAppPartResponse struct{…}`

        An iMessage app card part.

        - `App SentMessagePartIMessageAppPartResponseApp`

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

          - `BundleID string`

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

          - `Name string`

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

          - `TeamID string`

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

          - `AppStoreID int64`

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

        - `Layout SentMessagePartIMessageAppPartResponseLayout`

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

            Primary label, top-left and bold.

          - `ImageSubtitle string`

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

          - `ImageTitle string`

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

          - `ImageURL string`

            URL of an image (JPEG, PNG, HEIF, or WebP) 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. In responses, this is the re-hosted `cdn.linqapp.com` copy of the image you supplied, not your original URL.

          - `Subcaption string`

            Secondary label, below `caption` on the left.

          - `TrailingCaption string`

            Label shown top-right.

          - `TrailingSubcaption string`

            Label shown below `trailing_caption`, on the right.

        - `Reactions []Reaction`

          Reactions on this message part

          - `Handle ChatHandle`

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

          - `ID string`

            Identifier for this reaction. Pass it to
            `PATCH /v3/messages/{messageId}/reactions/{reactionId}` to move a sticker.

            Stickers placed before this API shipped can be read but not moved: the
            device-side reference needed to reposition them was never recorded, so
            `PATCH` returns 404 for those.

          - `CustomEmoji string`

            Custom emoji if type is "custom", null otherwise

          - `Sticker ReactionSticker`

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

        - `Type string`

          Indicates this is an iMessage app card part.

          - `const SentMessagePartIMessageAppPartResponseTypeIMessageApp SentMessagePartIMessageAppPartResponseType = "imessage_app"`

        - `URL string`

          The URL delivered to the iMessage app on tap.

        - `FallbackText string`

          Fallback text for surfaces that cannot render the card.

      - `type SentMessagePartAppClipPartResponse struct{…}`

        An App Clip card part

        - `Reactions []Reaction`

          Reactions on this message part

          - `Handle ChatHandle`

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

          - `ID string`

            Identifier for this reaction. Pass it to
            `PATCH /v3/messages/{messageId}/reactions/{reactionId}` to move a sticker.

            Stickers placed before this API shipped can be read but not moved: the
            device-side reference needed to reposition them was never recorded, so
            `PATCH` returns 404 for those.

          - `CustomEmoji string`

            Custom emoji if type is "custom", null otherwise

          - `Sticker ReactionSticker`

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

        - `Type string`

          Indicates this is an App Clip card part

          - `const SentMessagePartAppClipPartResponseTypeAppClip SentMessagePartAppClipPartResponseType = "app_clip"`

        - `Value string`

          The App Clip link the card opens

        - `Description string`

          The card's summary line, composed by Linq from the App Clip page

        - `ImageURL string`

          The card's preview image

        - `Title string`

          The card's headline, composed by Linq from the App Clip page

    - `SentAt Time`

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

    - `DeliveredAt Time`

      When the message was delivered

    - `Effect MessageEffect`

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

      - `Name string`

        Name of the effect. Common values:

        - Screen effects: confetti, fireworks, lasers, sparkles, celebration, hearts, love, balloons, happy_birthday, echo, spotlight
        - Bubble effects: slam, loud, gentle, invisible

      - `Type MessageEffectType`

        Type of effect

        - `const MessageEffectTypeScreen MessageEffectType = "screen"`

        - `const MessageEffectTypeBubble MessageEffectType = "bubble"`

    - `FromHandle ChatHandle`

      The sender of this message as a full handle object

    - `PreferredService ServiceType`

      Messaging service type

    - `ReplyTo ReplyTo`

      Indicates this message is a threaded reply to another message

      - `MessageID string`

        The ID of the message to reply to

      - `PartIndex int64`

        The specific message part to reply to (0-based index).
        Defaults to 0 (first part) if not provided.
        Use this when replying to a specific part of a multipart message.

    - `Service ServiceType`

      Messaging service type

### Example

```go
package main

import (
  "context"
  "fmt"

  "github.com/linq-team/linq-go"
  "github.com/linq-team/linq-go/option"
)

func main() {
  client := linqgo.NewClient(
    option.WithAPIKey("My API Key"),
  )
  response, err := client.Messages.UpdateAppCard(
    context.TODO(),
    "69a37c7d-af4f-4b5e-af42-e28e98ce873a",
    linqgo.MessageUpdateAppCardParams{
      Layout: linqgo.MessageUpdateAppCardParamsLayout{
        Caption: linqgo.String("Score: 2 – 1"),
      },
      FallbackText: linqgo.String("Score update"),
      URL: linqgo.String("https://app.example.com/card?game=7f3a&move=2"),
    },
  )
  if err != nil {
    panic(err.Error())
  }
  fmt.Printf("%+v\n", response.ChatID)
}
```

#### 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",
            "id": "9f8b1c2d-3e4f-5061-7283-94a5b6c7d8e9",
            "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!",
        "mention": "+14155551234",
        "mention_range": [
          4,
          9
        ],
        "mentions": [
          {
            "handle": "+14155550123",
            "is_me": true,
            "range": [
              4,
              9
            ]
          }
        ],
        "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"
  }
}
```
