Skip to content
Inkbox

Inkbox

DocsPricingBlogContact
GuidesAPI ReferenceChangelog

Ctrl K

GuidesAPI ReferenceChangelog

Jump to

Messages

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 number determines whether the identity can start a conversation or group.


Send message POST

POST /messages

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.

Shared and dedicated inbound numbers can reply only after the recipient has started a conversation. A dedicated outbound number 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.

Reply into a conversation:

JSONJSON

Or address the connected recipient directly:

JSONJSON

Or start or select a group from a dedicated outbound number:

JSONJSON

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

ParameterTypeDescription
agent_identity_idUUIDThe identity to send as. Required for admin API keys when sending by to; ignored for identity-scoped API keys, which always send as their own identity

Request body

FieldTypeRequiredDescription
tostring | string[]ConditionalOne E.164 phone number, or 1-8 distinct E.164 phone numbers. A list with 2 or more recipients starts or selects a group. Mutually exclusive with conversation_id
conversation_idUUIDConditionalExisting conversation to reply into. Mutually exclusive with to
textstring | nullNoMessage body, up to 18,995 characters. Required unless media_urls has an item
media_urlsstring[] | nullNoPublicly fetchable media URL to attach — at most 1 entry. Use Upload media to turn raw bytes into a URL. Required unless text is present
send_stylestring | nullNoExpressive send style applied to a 1:1 or group message, including group creation and conversation replies. Can accompany text, media, or both

Preconditions

  • Identity opt-in. The sending identity must have imessage_enabled: true; otherwise sends return 400.
  • Number capability. Shared and dedicated inbound numbers require recipient-first contact. A dedicated outbound number 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 until they reconnect through the router.
  • Contact rules. Sends to a recipient blocked by the identity's contact rules return 403 before anything is sent.

Rate limits

Each agent identity can send up to 100 iMessages per rolling 24-hour window. When the cap is reached, 429 responses include:

HeaderDescription
X-RateLimit-LimitMaximum sends allowed per identity per 24 hours
X-RateLimit-RemainingRemaining sends in the current window
X-RateLimit-ResetISO 8601 timestamp at which the oldest send in the window will fall off
Retry-AfterSeconds until at least one slot frees up

Response (201)

The queued message, wrapped in a message envelope. Delivery state updates asynchronously — re-read the message, or subscribe to the delivery-lifecycle webhooks (imessage.sent, imessage.delivered, imessage.delivery_failed) to track it without polling.

JSONJSON

Error responses

StatusDescription
400Sending identity is not enabled for iMessage, or agent_identity_id is missing on an admin send by to
402The organization's current billing-cycle iMessage capacity is exhausted
403A recipient is blocked by a contact rule or has opted out
404No connection exists for this recipient — the response tells you the connect command and router number to relay
404conversation_id not found
409The recipient hasn't messaged this agent yet, has disconnected, a group send lacks a dedicated outbound number, multiple groups match, current group membership is inconsistent or changes while resolving, or the line attachment changed
422Invalid body: both/neither to and conversation_id, invalid or duplicate recipients, no text or media, more than one media_urls entry, or text over 18,995 characters
42924-hour per-identity send limit or dedicated-line new-contact limit reached; respect the Retry-After header
503group_membership_unavailable because current membership for an existing group could not be verified; retry with backoff

Delivery happens asynchronously after the 201, so delivery failures never surface as send-request errors. Re-read the message to observe every outcome; delivery-lifecycle transitions also emit webhooks.

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" after a definitive failure. During creating, the local conversation and pending messages exist before the remote thread does; another send to the same conversation waits behind the elected creation send. A failed first message remains in the same conversation's history. Send another message using that conversation_id to retry group creation; a successful retry binds that same local conversation for later sends. Existing-group membership is rechecked before delivery, so a change after the 201 can instead move the queued message to status: "error". If you still intend to continue that native thread, a later conversation_id retry resolves its current membership again.


List messages GET

GET /messages

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

Query parameters

ParameterTypeDefaultDescription
agent_identity_idUUID-Narrow to one identity. Ignored for identity-scoped API keys, which always see their own identity
conversation_idUUID-Narrow to one conversation
include_groupsbooleanfalseInclude group messages in list results. A known group conversation_id is read directly even when this remains false
limitinteger50Number of results (1-200)
offsetinteger0Pagination offset
is_readboolean-Filter by read state. Omit for all messages
is_blockedboolean-Filter by blocked state. true returns only blocked rows, false only non-blocked rows, and omitting it applies the caller's default visibility
start_datetimestring-Only messages created on or after this date/time
end_datetimestring-Only messages created on or before this date/time; a bare date includes the whole day
tzstringUTCIANA zone for interpreting zone-less start_datetime/end_datetime values. Defaults to UTC

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.

JSONJSON

Error responses

StatusDescription
400agent_identity_id names an identity that is not enabled for iMessage
403Identity-scoped key passed an agent_identity_id other than its own
404agent_identity_id or conversation_id not found, or not visible to the caller

Upload media POST

POST /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

FieldTypeRequiredDescription
filefileYesThe file to upload, up to 10 MiB. The part's content type and filename are preserved

Response (201)

JSONJSON

Error responses

StatusDescription
400Caller's identity is not enabled for iMessage
413File exceeds 10 MiB
502Upstream upload failure — safe to retry

Send styles

Pass send_style on a 1:1 or group send to apply one of Apple's expressive effects. Dedicated-outbound groups support styles on both creation and conversation_id replies, including messages that also carry the single allowed media item:

Bubble effectsFull-screen effects
slamcelebration
loudshooting_star
gentlefireworks
invisible (invisible ink)lasers
love
confetti
balloons
spotlight
echo

Send styles render on the recipient's device when the message is delivered over iMessage; they do not apply to SMS fallback.

Message object

FieldTypeDescription
idstring (UUID)Message ID
conversation_idstring (UUID)The conversation this message belongs to
assignment_idstring (UUID) | nullThe 1:1 connection carrying the conversation; null for group messages
directionstring"inbound" or "outbound"
remote_numberstring | nullThe human's E.164 phone number for 1:1 messages. On inbound group messages it mirrors sender_number; null on outbound group messages
sender_numberstring | nullSender's E.164 phone number on an inbound group message; null for 1:1 and outbound group messages
participantsstring[] | nullBest-known E.164 participant snapshot for a group message; null for 1:1 messages
is_groupbooleanWhether the message belongs to an iMessage group chat
contentstring | nullMessage body. Null for media-only messages
message_typestring"message", or "carousel" for rich multi-part messages
servicestringTransport actually used: "imessage", "sms", or "rcs"
send_stylestring | nullSend style applied to the message, if any
mediaarray | nullMedia attachments. Each item has url, content_type, and size. URLs for stored inbound media are presigned and expire after 1 hour
was_downgradedboolean | nullWhether delivery fell back from iMessage to SMS
statusstring | nullDelivery lifecycle: "registered", "pending", "queued", "accepted", "sent", "delivered", "declined", "error", or "received" for inbound
error_codestring | nullDelivery error code, when delivery failed
error_messagestring | nullShort delivery error description
error_reasonstring | nullDelivery error reason, when reported
error_detailstring | nullHuman-readable detail accompanying the error
is_readbooleanRead state. For inbound messages, set by mark-read
is_blockedbooleanWhether the message was blocked by a contact rule or by default-block in whitelist mode. Identity-scoped API keys never receive rows where this is true
recipientsarray | nullPer-recipient delivery state for outbound messages, including one entry per group participant; null for inbound. Each entry has remote_number, delivery_status, service, error fields, and sent_at / delivered_at / failed_at timestamps
reactionsarray | nullLive tapbacks targeting this message, oldest first. Each entry has id, direction, reaction, custom_emoji, remote_number, part_index, and created_at
created_atstring (ISO 8601)Creation timestamp
updated_atstring (ISO 8601)Last update timestamp

Inkbox

Copyright © 2026 Inkbox

This site is protected by reCAPTCHA.

Google Privacy Policy and Terms of Service apply.

Website

Inkbox

Copyright © 2026 Inkbox

This site is protected by reCAPTCHA.

Google Privacy Policy and Terms of Service apply.

Website

Y CombinatorBacked by Y Combinator
Messages