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

# iMessage

> Let enrolled end users text your agent from Messages on Apple devices through a shared-pool number

After this guide, people you enroll can open Messages on an iPhone or Mac, text a number Lua assigns, and talk to your [agent](/concepts/agents) with native markdown, attachments, and link previews. iMessage is delivered through Photon; the platform creates and owns the Photon project for your agent, so there is nothing to obtain from Apple. The channel is set up in the admin dashboard  or over the admin REST API; there is no CLI command.

*Verified against lua-cli 3.33.0.*

**Before you begin**

* An agent in the admin dashboard, or an API key with the `channels:manage` scope on it.
* The phone numbers of the people who will text the agent. On the shared tier, only enrolled numbers can reach it.

<Steps>
  <Step title="Dashboard: add the iMessage channel">
    Add a channel of type iMessage to the agent (`POST /admin/agents/<agentId>/channels` with `{ "type": "imessage" }`; no other fields). The platform creates a Photon project named after the agent, enables iMessage on it, registers a signed webhook, and stores the credentials. The call is idempotent: an agent has at most one iMessage channel, and a second request returns the existing one.

    The channel starts on the `shared` tier, a pool of rotating sender numbers. A `dedicated` tier with your own number exists in the data model but isn't provisioned yet.
  </Step>

  <Step title="Dashboard: enroll a recipient">
    Enroll each person with `POST /admin/agents/<agentId>/channels/imessage/users` and `{ "phoneNumber": "+15551234567" }` (optionally `firstName`, `lastName`, and `msg`, a pre-filled first message). The response carries `assignedPhoneNumber`, the pool number that person texts, and `redirectUrl`, an `https` link that redirects into Messages addressed to that number (with the pre-filled text when you passed `msg`). Share the link, or build your own `sms:` link from `assignedPhoneNumber`.

    The conversation is inbound-first on purpose: the person sends the first message, so Messages shows no junk warning. `GET …/channels/imessage/users` lists enrollments with the tier, the cap, and the count; `DELETE …/channels/imessage/users/<userId>` removes one, after which that person can no longer text the agent.
  </Step>

  <Step title="Verify">
    Text the assigned number from an enrolled device. The agent shows a typing indicator while it works and replies in the same thread; `Lua.request.channel` inside your tools is `imessage`. Confirm the turn with `lua logs --type agent_response`.
  </Step>
</Steps>

## Channel behavior

| Capability                         | Supported                                                                                                                           |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| Inbound text, links, contact cards | Yes; a shared link arrives as its URL, a contact card as `[Shared contact card: <name> — <phones>]`                                 |
| Inbound attachments                | Images, video, audio, and documents, singly or as an album; stored on the Lua CDN and passed to the agent as images or files        |
| Inbound reactions                  | Recorded, never answered                                                                                                            |
| Outbound text                      | Markdown, rendered natively                                                                                                         |
| Images and documents               | Sent as attachments; a document's caption follows as text                                                                           |
| Links                              | Rich link previews                                                                                                                  |
| Actions, list items, payment links | Degraded to text: a numbered list, a text summary with the item image, the payment URL                                              |
| Reactions                          | The first `:::reaction` becomes a tapback when the emoji maps to love, like, dislike, laugh, emphasize, or question; otherwise text |
| Proactive sends                    | Not in `Channels.send`, and `user.send()` can't reach it                                                                            |
| Identity                           | The sender's phone number; an Apple ID email sender is matched by email                                                             |

The model is told it is in an iMessage conversation and to keep replies short. A person who texts from a phone number and later from an Apple ID email resolves to two different end users. Every thread with a person shares one conversation.

## Limits

| Limit                                  | Value                                                                         |
| -------------------------------------- | ----------------------------------------------------------------------------- |
| Enrolled recipients on the shared tier | 10                                                                            |
| iMessage channels per agent            | 1                                                                             |
| Outbound attachment                    | 25 MB, fetched from a public `https` URL; larger or private files are skipped |
| Duplicate delivery window              | 10 minutes; a redelivered message id is ignored                               |

## Troubleshooting

<AccordionGroup>
  <Accordion title="Agent <id> has no iMessage channel. Create one first.">
    Enrollment and listing need the channel to exist. Add the iMessage channel to the agent, then enroll recipients.
  </Accordion>

  <Accordion title="Photon createSharedUser failed">
    The shared pool allows 10 enrolled recipients, and the provider refuses the eleventh. `GET …/channels/imessage/users` returns `tier`, `cap`, and `count` so you can check before adding; free a slot with `DELETE …/channels/imessage/users/<userId>`.
  </Accordion>

  <Accordion title="An image or file the agent sent never arrived">
    Attachments are fetched by the platform before sending. The URL must be `https`, publicly reachable without redirects, and at most 25 MB; otherwise the attachment is skipped and the rest of the reply is still delivered.
  </Accordion>
</AccordionGroup>

## Next steps

<Columns cols={2}>
  <Card title="Response formatting" href="/channels/formatting/overview">What each component becomes on iMessage.</Card>
  <Card title="Identify users" href="/build/identify-users">Recognize the same person across channels.</Card>
  <Card title="About channels" href="/concepts/channels">Inbound and outbound channel vocabularies.</Card>
  <Card title="SMS and phone numbers" href="/channels/sms-and-phone-numbers">Plain texting for everyone else.</Card>
</Columns>
