mapier docs

API reference

Base URL, request and response conventions, and the shape of every Mapier /v1 endpoint.

This API is in preview. Details marked below are not final.

The Mapier API is a small HTTP surface over iMessage. You queue commands, and you read the results back on a single server-sent events stream. There are four endpoints and no client library — every example in these docs is a plain fetch or curl you can paste into a terminal.

Base URL

Every example in these docs reads the host from an environment variable:

export MAPIER_BASE_URL="https://<the host issued with your credentials>"

Every path is prefixed with /v1. The version is part of the path; there is no header or query-parameter negotiation, and a future /v2 would sit alongside /v1 rather than replacing it in place.

Unconfirmed

There is no stable public hostname yet. During preview the host is issued to you along with your credentials, and it will change when the production environment lands — so keep it in configuration rather than hard-coding it. These docs deliberately do the same.

The shape of every request

curl $MAPIER_BASE_URL/v1/commands/message.send \
  -H "Authorization: Bearer $MAPIER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target": { "kind": "recipient", "recipientExternalId": "+14155550100" },
    "payload": { "text": "hello" }
  }'

Three things are always true of a command request:

  • It is a POST with a JSON object body. Anything else — an array, a string, a number — is rejected with body_not_object.
  • The body has exactly two top-level keys: target (who) and payload (what). Both must be objects. See Addressing a conversation.
  • The body is at most 65,536 bytes. Larger is rejected with body_too_large before any parsing happens.

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"
}

Prop

Type

Errors are a bare JSON object with a single error key:

{ "error": "conversation_not_found" }

A few carry one extra field — unknown_command adds command, rate_limited adds retryAfter, and handle_check_unavailable adds reason. There is no envelope, no message, and no request id. See Error codes for the complete list.

Unconfirmed

The error body has no stable envelope yet. If you are writing a client, key on the HTTP status and the error string, and tolerate additional fields appearing alongside them.

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 a Mac, executed against iMessage, and settles asynchronously — success or failure arrives on the response stream as a command event, typically within a few seconds.

Request payloads are not validated at the API boundary. A malformed payload is accepted with 202 and fails later with a failed settlement. If you are testing a new payload shape, watch the stream — the HTTP response will not tell you it was wrong.

Endpoints

MethodPathPurpose
POST/v1/commands/:typeQueue a command
GET/v1/response_streamSettlements and inbound messages (SSE)
GET/v1/attachments/:idDownload an inbound attachment
POST/v1/handle-checkTest whether an address is reachable on iMessage

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.

Next

On this page