API reference
Base URL, request and response conventions, and the shape of every Mapier /v1 endpoint.
The Mapier API is a small HTTP surface over iMessage. You queue commands,
you read the results back on a single server-sent events stream or as
signed webhooks, and everything you can reach with a key is described in
one OpenAPI document — every example in these docs is a plain fetch or curl
you can paste into a terminal, and any generator will turn the document into a
typed client.
Base URL
https://api.mapier.aiEvery path is prefixed with /v1. The major version is part of the path, and a
future /v2 would sit alongside /v1 rather than replacing it in place. Within
/v1, the changes a deployed integration could trip over — a request resolved
differently, or a word it switches on renamed — ship behind a dated version you
choose with the x-mapier-version header, which every route reads — see
Versions.
The OpenAPI document
https://api.mapier.ai/openapi.jsonIt describes /v1 and only /v1 — every field with a sentence, every error
code each operation can answer with and the status it answers with it.
| Operation | |
|---|---|
POST /v1/commands/{type} | Send a command; the 202 is a handle. |
GET /v1/response_stream | Settlements, inbound messages and, on request, receipts (SSE). |
GET /v1/whoami | What this key is, what it can send from, and its command ceiling. |
POST /v1/handle-check | Is this address reachable. |
GET /v1/attachments/{id} | Fetch an attachment. |
GET /v1/messages/{messageId}/receipts | Ask what one message's receipts say, now. |
GET /v1/conversations/{conversationId}/messages | Read a conversation's history, newest first. |
GET /v1/lines/available | Which of your numbers somebody new would get now. |
The document describes these eight and nothing else.
The shape of every request
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": "hello" }
}'Three things are always true of a command request:
- It is a
POSTwhose body, when it has one, is a JSON object. A body that is not JSON at all is400 bad_request; JSON that is not an object — an array, a string, a number — is422 validation_failed. - The body has two top-level keys:
target(who) andpayload(what). Both are objects; a command that needs neither may omit the body. See Addressing a conversation. - The body is at most 65,536 bytes. Larger is rejected with
413 payload_too_largebefore any parsing happens — and before the key is checked, because reading a body it is going to refuse is the one thing an unauthenticated caller can make the service do.
See Authentication for how requests are identified.
The shape of every response
Successful command submissions return 202 Accepted:
{
"commandId": "3f1a0c2e-8b47-4d19-9a5e-2c6f0b8d4e71",
"status": "queued",
"disposition": "queued",
"lineId": "0192a7b0-3e4f-7051-8c6d-7e8f9a0b1c2d",
"lineSelection": "reused_active_chat"
}Prop
Type
The 202 is always queued, and the command frame is the one that
settles: succeeded, failed, cancelled or expired, or ambiguous or
no_effect when the transport took the command and could not prove what it
changed.
Every refusal is one envelope:
{ "error": { "code": "not_found", "message": "That conversation does not exist." } }Switch on code: it is a closed set and each value maps to exactly one HTTP
status. message is a sentence for a person and may change; never parse it.
details, when present, is an array of { path, message, … } entries on a
422 validation_failed — one per field that failed — and an object on the
other refusals that carry one: a reason to switch on, such as a
503 line_unavailable's, and the fields that go with it. A 429 from a
route's own quota also carries a Retry-After header. See
Error codes for the complete list.
202 does not mean it worked
This is the single most important thing to understand about this API.
A 202 means the command was durably queued, not that it was delivered.
The command is dispatched to the line, executed against iMessage, and settles
asynchronously — success or failure arrives on the
response stream as a command event, typically within
a few seconds.
Not every payload is checked before the 202. A message.send payload is checked before
anything is written — a key the line cannot carry is a 422 validation_failed at
payload.<key>, never dropped. A reaction.tapback, name_photo.share or group.create
payload can be accepted with 202 and then settle failed on the stream when the transport
refuses it — see what is checked before anything is
queued. If you are testing a new
payload shape, watch the stream.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /v1/commands/:type | Queue a command |
GET | /v1/response_stream | Settlements, inbound messages and, on request, receipts (SSE) |
GET | /v1/whoami | What this key is, its lines, and its command ceiling |
POST | /v1/handle-check | Test whether an address is reachable |
GET | /v1/attachments/:id | Download an inbound attachment |
GET | /v1/messages/:messageId/receipts | Ask what one message's receipts say, now |
GET | /v1/conversations/:conversationId/messages | Read a conversation's history, newest first |
GET | /v1/lines/available | Which of your numbers somebody new would get now |
Every one of them reads x-mapier-version.
Conventions
Identifiers. Phone numbers are E.164 (+14155550100). Email addresses are
lowercase and NFC-normalised. Conversation ids are Mapier UUIDs, not iMessage
GUIDs — you get them from inbound message events.
Timestamps. ISO 8601 with a Z suffix. occurredAt on an inbound message
is when iMessage says it was sent, not when Mapier received it.
Forward compatibility. Fields are added to responses and events without a
version bump. Parse defensively: ignore keys you do not recognise, and treat
enum values you do not recognise — especially errorCode — as unknown rather
than impossible. A change your integration could not absorb that way — a
request resolved differently, a word renamed — waits behind a dated
version instead.
Next
Send your first message
A working request in about five minutes.
Core concepts
The vocabulary: targets, commands, settlements, capabilities.
POST /v1/commands/:type
The command envelope every verb shares.
GET /v1/response_stream
message, command and error; receipt, delivery_failed, location, moderation and poll on request. Resumable by id.
GET /v1/whoami
The key's project, environment, lines and command ceiling.
Versions
The x-mapier-version header, your project's default, and what each version changes.
Read a conversation's history
GET /v1/conversations/{conversationId}/messages, newest first, both ways.
Error codes
Every code, what it means, and what to do about it.