Skip to main content
Send and list 1:1 or group iMessages. iMessage requests are scoped to the agent identity and keyed by conversation_id; callers never choose a sending number in the message request. Shared lines remain hidden, while an attached dedicated line lets the identity start a conversation or group.

Get message

Read a message by UUID, including its current delivery status. The response is a single message object with the same fields as a message-list entry. You can pass agent_identity_id to restrict the lookup to that identity. Identity-scoped credentials remain restricted to their own visible messages. An inaccessible or unknown message returns 404. error_code: "send_outcome_ambiguous" means delivery could not be confirmed. After a 15-minute confirmation window, the message can have status: "error" and error_reason: "delivery_unconfirmed". This is not proof that it was never delivered. Keep the original message rather than sending another copy; a later delivery update can still resolve it.

Send message

Send Idempotency-Key with Prefer: idempotency-replay to protect retries. The same key and input replay the original queued response and message ID. Read the message for current delivery status; do not create another message just because delivery is pending. See message send retries and lookup.
Send an outbound iMessage. Pass conversation_id to reply into an existing conversation, or to as either one E.164 phone number or a list of 1-8 E.164 phone numbers — exactly one of the two. A dedicated line can receive messages from non-phone sender addresses. Those conversations are receive-only: sending by conversation_id returns 422 with detail.error: "recipient_not_e164". The to field still requires E.164 phone numbers. Standard shared-service identities can reply only after the recipient has started a conversation. A dedicated line can start a new 1:1 conversation or a group chat; lists with 2-8 recipients select group behavior, while a scalar or one-item list keeps the 1:1 behavior. Organizations with their own organization-owned shared pool can submit the first message through POST /connect, available only by request. Subsequent sends use this message endpoint. Until the recipient replies, they return 409 with error: "imessage_awaiting_inbound", unless prior direct engagement on the assigned number already satisfies the reply requirement. Reply into a conversation:
JSON
Or address the connected recipient directly:
JSON
Or start or select a group from a dedicated line:
JSON
For groups, an unresolved local conversation with the same requested members is reused before a remote thread has been established. An established best-known match is reused only when its current membership can be verified and still matches the request. If no reusable match remains, a new group is created; multiple best-known matches return 409. A different to set never edits an existing group’s membership. Keep the returned conversation_id and use it to continue the exact same thread. See Group chats for membership and delivery semantics.

Query parameters

Request body

Preconditions

  • Identity opt-in. The sending identity must have imessage_enabled: true; otherwise sends return 400.
  • Line capability. Standard shared service requires recipient-first contact. A dedicated line can start 1:1 conversations and is required for groups.
  • Group participants. Group to lists contain 2-8 distinct remote E.164 phone numbers and cannot include the attached sending line. Every participant must pass opt-out and contact-rule checks before the message is queued.
  • Connection still active. If the recipient disconnected from the agent, sends into the old conversation return 409. Reconnect through the router for standard shared service, or through connect for a organization-owned shared pool, subject to its eligibility checks.
  • Contact rules. Sends to a recipient blocked by the identity’s contact rules return 403 before anything is sent.

Before the first reply

Before a qualifying recipient reply, every 1:1 message must contain fewer than 160 characters (159 maximum), without phone numbers, links, or media. These content requirements apply to all line types, including dedicated lines. They do not apply to group sends or grant permission to send before a connection is ready. At most three 1:1 messages total are allowed before the recipient’s first qualifying reply. When this limit rejects a send, the API returns 422 imessage_unanswered_followup_limit; wait for a reply before sending again. This ceiling does not grant additional sends when a connection requires an earlier reply. The three-message cap ends after the first qualifying reply, but other unanswered-message limits still apply. Violations return 422 before the message is queued, with the error code in detail.error. A numeric identifier that resembles a phone number can also produce imessage_reply_required_for_phone_numbers. Wait for the recipient’s reply before including that identifier. After a qualifying reply, these pre-reply content restrictions no longer apply; other send requirements and limits still do.

Rate limits

Each agent identity can send up to 100 iMessages per rolling 24-hour window. When the cap is reached, 429 responses include: Dedicated lines carry additional guardrails on agent-initiated conversations. A dedicated line can start conversations with at most 10 new recipients per rolling 1-hour window and 40 new recipients per rolling 24-hour window, which keeps the line in good standing. Both limits apply only to recipients the line hasn’t reached before:
  • It does not apply to a recipient the agent has already connected with on that line.
  • It does not apply when the recipient messages the line first.
Repeat sends to an already-connected recipient never count against either limit. In a group chat, each previously unknown participant counts as one new recipient, and the whole request is rejected if the line lacks enough remaining slots in either window. At a cap, sends return 429 with error: "outbound_line_new_contact_rate_limited" and the same Retry-After and X-RateLimit-* headers. Those headers describe the limit that rejected the request, and Retry-After counts down until enough new-recipient slots in that window age out.

Response (201)

The queued message, wrapped in a message envelope. Delivery state updates asynchronously. Re-read the message to observe every outcome. Delivery-lifecycle webhooks (imessage.sent, imessage.delivered, imessage.delivery_failed) report delivery updates, but do not cover every failure before sending.
JSON

Error responses

The 404 and 409 connection errors carry a structured detail — read detail.error to branch and detail.message for the sentence to relay: The expiry error returns 409 whether you address the expired connection by conversation_id or the recipient’s to number. It does not expose the previous shared sending number. Conversation history stays readable. See inactive shared connections for the default expiry policy. Compatibility note: a send addressed by to whose latest connection expired now returns 409 imessage_connection_expired instead of 404 imessage_no_assignment. Update any handling that assumes every recipient without an active connection returns 404. Ordinary missing connections and manual disconnects retain their existing error behavior. Reactions, read receipts, and typing indicators also reject inactive connections. They return imessage_connection_expired when the connection expired due to inactivity. Delivery happens asynchronously after the 201, so later failures do not change the send response. Re-read the message to observe every outcome. Delivery updates can emit webhooks, but do not cover every failure before sending. If a reply target becomes unavailable before sending, the message reports status: "error" and error_code: "imessage_reply_target_unavailable". The first message in a new group also creates the group asynchronously. Check the conversation’s group_creation_status: "creating" while that attempt resolves, "ready" after success, or "not_created" when no confirmed group is available. During creating, later messages wait for the first send. If creation remains unconfirmed after 15 minutes, the first message gets an unknown-delivery outcome. Waiting messages that were never sent get error_code: "group_creation_unconfirmed" and error_reason: "request_not_sent". A fresh explicit send can attempt group creation again; the uncertain original is never automatically resent. All attempts remain in the conversation’s history. Existing-group membership is rechecked before delivery, so a change after the 201 can move the queued message to status: "error". A later conversation_id send resolves its current membership again.

List messages

List iMessages visible to the caller, newest first, across all of the caller’s conversations or narrowed to one.

Query parameters

Identity-scoped API keys never see contact-rule-blocked messages, regardless of is_blocked. Admin API keys and the Inkbox Console see blocked and non-blocked messages by default; use is_blocked=true for a blocked-only audit listing. start_datetime, end_datetime, and tz filter on created_at. Both bounds are optional and non-breaking — omit them for unchanged behavior. See Filtering by date.

Response (200)

Returns a list[IMessage]. Live tapbacks ride along on each message — see the Message object. Within an accessible conversation, an unknown or inaccessible thread_id returns 200 with []. In contrast, Get conversation thread returns 404 when that thread is not visible.
JSON

Error responses


Reply to a specific message

Native replies work within both 1:1 and group iMessage conversations. A conversation is the whole chat; a reply thread groups a message and its replies. Ordinary conversation message lists remain flat and include all messages. Pass the existing conversation_id and the message’s Inkbox UUID as reply_to_message_id:
JSON
You can target a received message or a confirmed sent message, including another reply. The target must belong to the same conversation and be accessible to you. Missing, inaccessible, pending, failed, or unresolved targets remain errors; fallback does not bypass these checks. plain_reply_fallback defaults to true. Inkbox uses a native reply when supported. If the target is known to use SMS/RCS or was downgraded, Inkbox sends an ordinary message in the same conversation, using normal transport selection. This does not force SMS/RCS, create a new conversation, or change a group’s audience. Set plain_reply_fallback: false to require native threading instead; an unsupported target then returns 422. Fallback is decided before sending, from what is known about the target. If the target was recorded as iMessage but native delivery is later rejected, Inkbox does not retry it as an ordinary message, even with plain_reply_fallback: true. A delivery failure, timeout, or uncertain result also does not trigger an automatic resend. A queued response is not confirmation of delivery. Poll GET /messages/{message_id} for the current status and error fields. If the target becomes unavailable before sending, the message reports status: "error" and error_code: "imessage_reply_target_unavailable". Delivery webhooks do not cover every failure before sending, so they supplement message reads rather than replace them. An ordinary fallback has no reply_to_message_id and does not join the target’s native thread. It may have its own standalone thread_id. A queued response can initially contain the requested native parent; an allowed ordinary fallback decided before sending can clear that relationship. Re-read the message for current metadata rather than treating the requested target or initial queued response as the final reply relationship. Omit reply_to_message_id or pass null for the existing ordinary-message behavior; plain_reply_fallback has no effect then. A reply remains one message in the same conversation, with the same usage accounting and send limits. Changing the target or its fallback policy changes the send input: use a new idempotency key for a different intended send. Each message can expose three nullable fields:
  • reply_to_message_id: its immediate parent message, when visible and resolved. A non-null value identifies a reply, not an ordinary message.
  • thread_root_message_id: the message that started the thread. For a standalone message, this can be its own id.
  • thread_id: the opaque identifier used to retrieve the whole thread. This is not the root message’s ID, and its presence does not mean replies exist.
A newly received or confirmed sent ordinary message can have its own thread_id before anyone replies. Reading that thread returns the message itself. A null parent alone does not prove a message is standalone: a reply’s parent can be unresolved or hidden. If B replies to A and C replies to B, their immediate parents differ, but A, B, and C belong to the same thread. Incoming native replies preserve these relationships in 1:1 and group chats. Fields can be null while a message is pending or a reference is unresolved or not visible to you. Older message history might not have thread metadata; clients must tolerate missing or null fields.

Get a reply thread

Read the stored, visible root and replies using any message in the thread. An ordinary standalone message returns itself. A missing root does not prevent accessible replies from being read.

Response (200)

Continue with the returned cursor on the same thread and conversation. Do not parse or construct cursors yourself. Reads only return history you can access; they do not fetch missing messages from participants’ devices. An inaccessible or unknown anchor returns 404; an invalid cursor or pagination parameter returns 422. If you already have conversation_id and thread_id, use Get conversation thread. For the existing newest-first list shape and offset pagination, use GET /messages?conversation_id=...&thread_id=... instead.

Upload media

Upload a file and get back a URL you can pass in media_urls on a send. The request is multipart/form-data with a single file field.

Request

Response (201)

JSON

Error responses


Send styles

Pass send_style on a 1:1 or group send to apply one of Apple’s expressive effects. Dedicated-line groups support styles on both creation and conversation_id replies, including messages that also carry the single allowed media item: Send styles render on the recipient’s device when the message is delivered over iMessage; they do not apply to SMS fallback.

Message object