message.send
Send text, rich text, a link preview or a poll into a conversation, or start a new one with a recipient.
POST /v1/commands/message.sendSends a message. The payload is a union discriminated by kind; plain text
omits kind entirely.
Target
Accepts a conversation target (reply into an existing thread) or a recipient target (start a new one).
A group is addressed as a conversation — kind: "conversation" with the group's
conversationId — not as its own target kind. That is why the capability line
below names three flags for two target kinds: a DM conversation and a group
conversation are granted separately. See
Capabilities.
Prop
Type
A recipient target sends plain text only, and it fails quietly rather than
loudly. The endpoint forwards payload.text and discards every other key
— so a recipient send carrying kind: "rich_text" and an effect returns
202 and delivers the bare text, with the effect silently dropped. You only
get a 400 when recipientExternalId or text is missing entirely.
Two more limits are specific to this target and are enforced synchronously, so
they come back as 422 rather than on the stream: the address must be an E.164
phone number (an email is recipient_not_allowed), and the text is trimmed
and capped at 16,384 bytes rather than the 65,536 a conversation send
allows (invalid_text).
To send rich content to someone new: send plain text first, wait for the conversation to appear on the stream, then send into it with a conversation target.
Routing path: a conversation target uses the agent-turn path and honours
Idempotency-Key. A recipient target uses the admin path and does not.
Payload
Text
The default. No kind field.
Prop
Type
{ "text": "hello" }rich_text
Text plus reply threading, a screen effect, or a subject line.
Prop
Type
{
"kind": "rich_text",
"text": "Congratulations",
"effect": "confetti",
"replyToMessageExternalId": "guid-1"
}The payload is closed: rich_text accepts the five keys above and rejects any other. In
particular there is no per-range styling field — no bold, italic or per-word animation — so the
whole body arrives in one style.
rich_link
Sends a URL that iMessage renders as a link preview.
Prop
Type
{ "kind": "rich_link", "url": "https://example.com/launch" }poll
Prop
Type
{
"kind": "poll",
"question": "Lunch?",
"options": ["Tacos", "Ramen", "Salad"]
}Reading votes back is covered in Polls.
attachment
Prop
Type
The transferId is the id returned by POST /v1/media: stage the file there first,
then put its transferId here to attach it — a command body never carries bytes. The filename's
extension sets how it renders: a .vcf arrives as a contact card, a .jpg as an image. A send
whose transferId has not landed yet settles failed on the response
stream.
sticker
Not yet available
You can stage sticker bytes with POST /v1/media, but connectors do not yet advertise
sticker sends, so this payload settles failed on the stream. Attachments send today.
Prop
Type
Request
curl $MAPIER_BASE_URL/v1/commands/message.send \
-H "Authorization: Bearer $MAPIER_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-2481-shipped" \
-d '{
"target": {
"kind": "conversation",
"conversationId": "a0000000-0000-4000-8000-000000000001"
},
"payload": { "text": "Your order shipped." }
}'Starting a new conversation instead:
{
"target": { "kind": "recipient", "recipientExternalId": "+14155550100" },
"payload": { "text": "hi" }
}Response
{
"commandId": "cmd-1",
"status": "queued",
"disposition": "queued"
}Then, on the response stream:
{
"commandId": "cmd-1",
"logicalActionId": "order-2481-shipped",
"status": "succeeded",
"errorCode": null
}{
"commandId": "cmd-1",
"logicalActionId": "order-2481-shipped",
"status": "failed",
"errorCode": "stale_target"
}Errors
| Status | Code | Cause |
|---|---|---|
400 | recipient_send_requires_recipientExternalId_and_text | Recipient target missing recipientExternalId or text |
400 | invalid_conversationId | conversationId is not a UUID |
404 | conversation_not_found | Unknown conversation, or another account's |
422 | recipient_not_allowed | Recipient target address is not an E.164 phone number |
422 | invalid_text | Recipient target text is empty or over 16,384 bytes |
503 | no_live_connector | Conversation target: no Mac holds a live lease |
503 | connector_unavailable | Recipient target: no Mac holds a live lease |
503 | outbound_paused | Recipient target: sending is administratively paused |
Settlement codes you will see for this command: stale_target (reply target
aged out), invalid_target, gateway_rejected, send_returned_not_ok,
outbound_echo_unconfirmed. See Error codes.
Capabilities
Requires command.message_send.conversation.v1, .group.v1 or .recipient.v1
depending on the target. Rich kinds additionally require
command.message_send.content.v1 with the kind listed in its supportedKinds.
See Capabilities.