Addressing a conversation
The three target shapes, and how to get the conversation id you need.
Every command has a target that says who it acts on. There are three
shapes, and picking the right one is usually the first question you answer.
conversation — reply into an existing thread
The one you will use most.
{ "kind": "conversation", "conversationId": "a0000000-0000-4000-8000-000000000001" }conversationId is a Mapier UUID, not an iMessage GUID. You get it from
the message event on the response stream:
{
"conversationId": "a0000000-0000-4000-8000-000000000001",
"from": "+14155550100",
"text": "is it here yet?",
"externalId": "guid-1",
"replyToExternalId": null,
"occurredAt": "2026-08-24T18:03:11.000Z",
"attachmentIds": []
}Store that id against your own records — a customer, a ticket, a thread — and you can reply at any time. It is stable for the life of the conversation.
Works for both direct messages and groups. Some commands accept only one or the other; each reference page says which.
A conversation id from another account returns conversation_not_found — the same response as an
id that does not exist. If you are getting this unexpectedly, check the id came from your own
stream.
recipient — start a new thread
When you have a phone number or email but no conversation yet.
{ "kind": "recipient", "recipientExternalId": "+14155550100" }Prop
Type
Two limits apply, and both surprise people:
- Plain text only, and it degrades silently. The endpoint forwards
payload.textand drops every other key, so a recipient send carrying an effect or a reply target returns202and delivers the bare text with those parts missing. Nothing tells you they were ignored. Rich content needs a conversation target. - No idempotency. Recipient sends take the admin routing path, where an
Idempotency-Keyis accepted and ignored. A retry sends a second message.
The usual pattern is therefore: send a plain text opener, wait for the
conversation to appear on the stream, then use its conversationId for
everything after.
Check reachability first if you are unsure the address is on iMessage — POST /v1/handle-check answers synchronously and saves you a message that goes
nowhere.
new_group — create a group
Only group.create accepts this.
{
"kind": "new_group",
"participantExternalIds": ["+14155550100", "+14155550111"]
}Between 2 and 32 participants, each unique, each in the same E.164-or-email format as a recipient. Do not include yourself. Order does not matter.
Address formats
| Kind | Format | Valid | Invalid |
|---|---|---|---|
| Phone | E.164, leading +, 7–15 digits | +14155550100 | 4155550100, (415) 555-0100 |
| Lowercase, NFC-normalised | person@example.com | Person@Example.com |
Addresses that fail these rules are rejected with invalid_address on handle
check, or 422 on a send.
Conversation ids go stale
A group's id is resolved against its membership at the time you use it. Change
the membership — add or remove someone — and commands issued against the old
id settle stale_target.
The fix is to wait for a new message in the group, which carries a fresh
conversationId, then use that. See Groups.