Introduction
Send and receive iMessage from your own code — one HTTP API, one event stream.
Mapier gives your application a real iMessage presence. You send messages, react to them, start group chats and receive everything people send back — over plain HTTP, from any language, with a sandbox to rehearse against before a real number is involved.
There is no SDK to install. Every example in these docs is a curl command or
a fetch call you can run right now, and the whole surface is one OpenAPI
document at https://api.mapier.ai/openapi.json for any generator you already
use. If a coding agent is writing that code, point it at these docs
— every page is also published as plain markdown, and two things about this
API reliably trip a model up.
curl https://api.mapier.ai/v1/commands/message.send \
-H "Authorization: Bearer $MAPIER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"target": { "kind": "recipient", "channel": "imessage", "recipientExternalId": "+14155550100" },
"payload": { "text": "Your table is ready." }
}'How it works
The API has two halves, and understanding the split makes everything else straightforward.
You send commands. Every action — sending a message, adding a reaction,
starting a group — is a POST to one endpoint with a target (who) and a
payload (what). The response is 202 Accepted, meaning the command was
durably queued.
You read results from one stream. A single server-sent events connection
tells you how each command settled and carries every inbound message, and — when
you ask — each delivery or read receipt and each "not delivered" verdict. Every
frame carries an id:, and reconnecting with Last-Event-ID resumes where you
left off, so a drop costs a reconnect and nothing more. That is why the
quickstart has you open the stream before sending
anything. For message.send, succeeded is the transport's settlement, not a
delivery receipt; receipts are their own frames — see
Receipts and delivery verdicts.
A 202 means queued, not delivered. That one sentence explains most of the surprises in this API
— keep the stream open and you will not be surprised by any of them.
What you can build
- Support and sales conversations that live where customers already are, with a human or an agent on your side of the thread.
- Transactional notifications — order updates, appointment reminders, delivery windows — as a real conversation rather than a no-reply channel.
- Group coordination: start a group chat with everyone a piece of work needs, and its opening message, in one request.
- Lightweight acknowledgements: a tapback on a customer's message, so they know it was seen before the full answer arrives.
Start here
Send your first message
A working request and a settlement you can watch arrive, in about five minutes.
Core concepts
Targets, commands, settlements, capabilities — the vocabulary the rest of the docs assumes.
API reference
Every endpoint, every field, every error code.
The response stream
How to consume events, and what the stream does not guarantee.
What the API does today
Being straight about the edges saves you finding them the hard way. A row that reads Not available yet is a row you would otherwise have to establish by absence.
Channels
Eleven channels are named on the wire; one carries traffic today. "Not available yet" is a statement about the live path. In test, a project's sandbox world starts with an iMessage line, and a send on a channel the world holds no line for is refused.
| Channel | channel | State |
|---|---|---|
| iMessage | imessage | Available |
| RCS | rcs | Not available yet |
| SMS | sms | Not available yet |
whatsapp | Not available yet | |
| Voice | voice | Not available yet |
| Telegram | telegram | Not available yet |
| Signal | signal | Not available yet |
| Apple Business | apple_business | Not available yet |
| Discord | discord | Not available yet |
| Slack | slack | Not available yet |
| Messenger | messenger | Not available yet |
Platform
| Capability | State |
|---|---|
Separate live and test keys (mp_live_ / mp_test_), with nothing shared between the two | Available |
| The sandbox: a fake iMessage world per project, with a phone you hold as the contact | Available |
Idempotency-Key on every command in both environments, except group.create, typing.start and message.read on a live line | Available |
| Signed webhooks, with retries and console redelivery | Available |
Response stream: message and command frames, receipt, delivery_failed, location, moderation and poll on request, resumable with Last-Event-ID | Available |
| Sending text; tapback reactions; the typing bubble; marking a conversation read; creating groups; editing and unsending the line's own messages; reading and setting the line's name and photo; sharing the contact card; voting on polls | Available |
| Sending into a group, renaming it, changing its members or leaving it | Available on an iMessage line, in a group chat the transport has reported: a send reaches every member, and group.update renames the group or adds or removes one member. A leave cannot be undone, and its success means it was sent, not proven. A group photo is not carried yet, and a test line has no group chat. See Groups |
| Setting and removing a conversation's background | Available on an iMessage line, from the published presets, in a conversation the transport has confirmed is with one person or a group chat it has reported. On a live line a change in a one-to-one conversation also arrives as a message with no text, which your handler should skip. See chat_background.set |
Sending effects, subject lines, threaded replies, rich links and stickers with message.send | Available on an iMessage line into a conversation the contact has written into — effects, subjects and replies beside text, a photo or file by https link (media), an emoji sticker, a rich_link, a multipart (text and photos as one message), a location_request and a poll in place of it, and bold, animated or mention ranges with textFormatting. See What a line carries |
| Receiving messages and their attachments on the stream; new conversations by webhook | Available. A contact's shared location and their edits and unsends are frames on request, ?events=locations and ?events=moderation, and webhooks you subscribe to by name. |
| Delivered, read and "not delivered" | Available, on request: the stream's receipt and delivery_failed frames, the matching webhooks, and GET /v1/messages/{messageId}/receipts to ask about one message now. succeeded on a send is not a receipt. |
| Reading a conversation's history | Available: GET /v1/conversations/{conversationId}/messages |
| Message templates | Not available yet |
| Receiving reactions | Available as the reaction.added and reaction.removed webhooks, not a stream frame. Tapbacks in a group chat are not delivered yet |
Conventions in these docs
Phone numbers are E.164 (+14155550100) and email addresses are lowercase.
Examples use the reserved +1415555xxxx range, so nothing here can dial a real
person. Where a detail is not yet settled, you will see a callout saying so
rather than a confident guess.