> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tied.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rich Feature Messages

> The eight interactive message types Apple Messages for Business supports, and the envelope they share.

Apple Messages for Business is not a text channel with extras bolted on. Most of what you send is a **structured, interactive object** that renders natively in the Messages app — a picker, a form, a payment sheet — and returns structured data when the customer interacts with it.

Tied supports eight of these. All eight are schema-validated before dispatch, so a malformed message is marked failed in the thread rather than rendering wrong at Apple.

## The envelope six of them share

Six primitives wrap their payload in the same two-part envelope. Understanding it once explains most of the channel:

<ResponseField name="sendDisplay" type="required">
  What the customer sees **in the thread before they interact** — a title, subtitle and optional image.
  This is the bubble sitting in their Messages app.
</ResponseField>

<ResponseField name="responseDisplay" type="required">
  What the bubble becomes **after they respond**. The picker collapses into a confirmation showing what
  they chose.
</ResponseField>

<ResponseField name="<primitive>" type="required">
  The actual payload — `listPicker`, `timePicker`, `form`, `pay`, `oAuth` or `imessageExtension`.
</ResponseField>

<Info>
  **Why this matters:** the thread has to read sensibly after the interaction is over. A customer
  scrolling back three weeks later should see *"Appointment confirmed — Tuesday 3pm"*, not an inert
  picker they can no longer use. `responseDisplay` is what makes that happen, and it is required, not
  optional.
</Info>

The two exceptions are **Simple Message** and **Rich Link** — one-shot sends with no interaction to display a response for.

## The eight primitives

<AccordionGroup>
  <Accordion title="Simple Message — text, quick replies and attachments" icon="message-square">
    The workhorse. Requires either `text` or `quickReplies` (or both).

    | Field | Notes |
    | - | - |
    | `text` | Plain text body |
    | `quickReplies[]` | Each needs both `text` (the label) and `key` (your identifier) |
    | `quickRepliesSummary` | **Max 40 characters.** Shown as the collapsed summary |
    | `attachments[]` | Each needs `name` and a `url` |

    <Warning>
      **The quick-reply key gap.** The schema requires you to set a `key` on every quick reply, but
      Apple's inbound payload returns **only the label text** — the key does not come back. So you have
      to match the reply on its visible label.

      Practical consequences: keep labels unique within a message, avoid labels that differ only by
      punctuation or case, and be careful changing label copy — it is effectively your matching
      identifier. If you need a stable identifier that survives a copy change, use
      **[List Picker](#list-picker)** instead, which does return its own identifiers.
    </Warning>
  </Accordion>

  <Accordion title="Rich Link — a card with an image or video" icon="link">
    The only primitive that takes **video** as well as an image. Requires just `richLink` — no envelope,
    because there is nothing to respond to.

    Use it for a product, an article, an order confirmation — anything where a tappable card beats a
    bare URL. It renders as a proper preview card rather than a link.

    | Media slot | |
    | - | - |
    | `richLink.image` | Upload or URL |
    | `richLink.video` | Upload or URL — unique to this primitive |
  </Accordion>

  <Accordion title="List Picker — choose one or more from a list" icon="list">
    A native list the customer scrolls and taps. Supports sections, per-item images and multi-select.

    **This is the only primitive with dependable identifiers on the way back.** If your flow needs to
    branch on exactly what the customer picked, and needs that branch to survive a copy change, use a
    List Picker rather than quick replies.

    Media slots: `sendDisplay.image`, `responseDisplay.image`, and a per-item `image`.
  </Accordion>

  <Accordion title="Time Picker — pick an appointment slot" icon="calendar-clock">
    Presents timeslots natively; the customer picks one without leaving Messages. The reply comes back
    as a structured selection, so booking flows do not have to parse free text like "tuesday afternoon
    works".

    Media slots: `sendDisplay.image`, `responseDisplay.image`, and a third slot on the primitive
    itself, `timePicker.image`.
  </Accordion>

  <Accordion title="Form — a multi-page guided flow" icon="clipboard-list">
    The largest schema of the eight, and the most capable. A multi-page sequence with a splash screen,
    typed inputs, select pages and picker pages — collected inside Messages, returned as structured data.

    Use it where a web form would otherwise break the thread: returns intake, warranty registration,
    onboarding questionnaires.

    Media slots: `sendDisplay.image`, `responseDisplay.image`, `splash.image`, and per-item `image` on
    select and picker pages.
  </Accordion>

  <Accordion title="Apple Pay — take payment in the thread" icon="credit-card">
    A native payment sheet. The customer authenticates with Face ID or Touch ID and pays without leaving
    the conversation — no redirect, no checkout page, no re-entering card details.

    This is the single most commercially significant primitive on the channel. It also has a billing
    consequence worth knowing: a resolution where the agent takes a payment is a `write_financial`, and
    because the processor holds the card number and Tied acts against a token, it does **not** derive
    `compliance_gated`. See [Resolution pricing](/pricing/resolution-pricing).

    Media slots: `sendDisplay.image`, `responseDisplay.image`.
  </Accordion>

  <Accordion title="OAuth — authenticate the customer" icon="key-round">
    Runs an OAuth flow inside the thread, so you know who you are talking to before doing anything
    sensitive. The customer signs in with your identity provider and control returns to the conversation.

    This is what turns an anonymous thread into an authenticated session — the precondition for account
    changes, order lookups against a real identity, and anything you would not do for a stranger.

    Media slots: `sendDisplay.image`, `responseDisplay.image`.
  </Accordion>

  <Accordion title="iMessage Extension — your own app surface in the thread" icon="app-window">
    Embeds a custom iMessage app inside the conversation. The most powerful and the most work: it needs a
    companion iOS app extension you build and ship.

    Reach for it when none of the other seven fit — a seat picker, a configurator, a map-based selector.

    Media slots: `sendDisplay.image`, `responseDisplay.image`. Unlike the others, it also carries a
    **required** `icon` URI on the primitive itself.
  </Accordion>
</AccordionGroup>

## Every primitive is validated before it sends

All eight are checked against their JSON Schema before dispatch — at send time, and again at
flow-dispatch time. A message missing a required field, or carrying an unexpected one, does not
reach Apple: it is accepted into the thread and then marked failed, rather than rendering wrong on
the customer's phone.

That is deliberate: Apple's own failure modes are opaque, and a message that silently does not render is far harder to debug than one that fails visibly with a reason.

<Note>
  All eight primitives set `additionalProperties: false` — at the top level and on every nested
  object. Simple Message alone sets it four separate times, once per nested shape in its schema.
  Fields Apple does not recognise are not passed through anywhere in any primitive — they are a
  validation error. If you are porting content from another platform, expect to map fields
  explicitly rather than pass a superset and hope.
</Note>

## Choosing between them

| If you need… | Use |
| - | - |
| A quick yes/no or two-to-three options | **Simple Message** with quick replies |
| A choice your flow must branch on reliably | **List Picker** — stable identifiers |
| An appointment | **Time Picker** |
| More than two fields of input | **Form** |
| Money | **Apple Pay** |
| To know who they are first | **OAuth** |
| A link that looks like something | **Rich Link** |
| Something none of the above can render | **iMessage Extension** |

## Next

<CardGroup cols={2}>
  <Card title="Known limitations" icon="triangle-alert" href="/channels/apple-messages/limitations">
    The constraints worth knowing before you design a flow around these.
  </Card>

  <Card title="Channel-native flow nodes" icon="workflow" href="/chatbot/channel-nodes">
    Emitting these primitives from inside a chatbot flow.
  </Card>

  <Card title="Entry points" icon="scan-line" href="/channels/apple-messages/entry-points">
    The eight ways a customer can start the conversation in the first place.
  </Card>

  <Card title="Apple Messages fees" icon="receipt" href="/pricing/channel-fees/apple-messages">
    What delivery costs. Apple Messages for Business has no agent fees.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.