- Dedicated line: Can start new 1:1 conversations and group chats.
- Shared service: Dynamically assigned when someone connects to your agent; the human starts the conversation.
- Organization-owned shared pool (Beta), available only by request: Your organization has its own pool of shared iMessage numbers. Connect a recipient through the API and submit one opening message before their first reply.
contact_sharing_enabled to false to remove the automatic sharing profile for that dedicated line. The preference belongs to the identity and remains saved while no line is attached.
Beta: Organization-owned iMessage routers are available upon request. They give an organization its own configurable router number for connecting humans to agents. An organization-owned router serves the organization rather than attaching to one agent. Organizations without one continue to use the shared Inkbox router.
Conversations remain the stable key in every setup. Agent-facing APIs identify threads by conversation_id; outbound 1:1 messages can also be addressed by the recipient’s E.164 phone number. Every conversation belongs to one agent identity in your organization, and an identity-scoped API key sees only that identity’s threads.
Shared-service identities support 1:1 conversations. Dedicated lines also support group chats with 2-8 remote participants.
Receive-only conversations
Dedicated lines can receive 1:1 messages from non-phone sender addresses, such assender@example.com. These messages appear in conversation and message reads and use the normal imessage.received webhook. Shared service and group chats still require E.164 phone numbers.
Treat remote_number as a sender address, not a guaranteed phone number. Preserve the string as returned and use conversation_id to read its history. The same address can appear in connection responses and a 1:1 conversation’s participants list. SDK fields remain strings; Python uses remote_number, and TypeScript uses remoteNumber. Raw webhook JSON uses remote_number.
Conversations with non-phone senders are receive-only. Sending a message, tapback, typing indicator, or explicit read receipt into one returns 422 with detail.error: "recipient_not_e164". Outbound to values must still be E.164 phone numbers. An active connection does not override this requirement.
Before an automated reply, check that the 1:1 sender address is an E.164 phone number and the connection is active. Do not reject an incoming message just because you cannot reply to it. Continue to handle send errors: a phone-number address alone does not guarantee that a send is allowed or will be delivered.
Enabling iMessage on an identity
iMessage reachability is opt-in per identity and defaults to off. Enable it at create time or later:Connecting a human to your agent
Organizations with their own organization-owned shared iMessage pool can use the connect endpoint instead of the router. This access is available only by request through support@inkbox.ai. An admin API key submits the agent identity, recipient number, required opening message, and a client-generatedIdempotency-Key. Success means the opening message was accepted, not delivered.
Each new connection accepts at most one opening; up to three countable openings
are allowed across reconnects on the same number before a reply. Further messages
wait for the recipient’s reply unless prior direct engagement
on the selected number already satisfies that requirement.
A router can also connect recipients to an organization-owned shared pool; using
a router does not determine which kind of shared number carries the conversation.
For standard shared service, humans connect by texting a command to the router. Resolve the router number at runtime — it can change, so never hardcode it:
connect @my-agent to the router number. The router replies, the connection is created, and everything the human sends after that lands in your agent’s conversation. A human can be connected to more than one agent at the same time; each connection is its own conversation.
Here’s what that looks like from the human’s side — they text the connect command to the router, and the router confirms the connection and shares the agent’s contact card:

Inactive shared connections
Standard shared-service connections can expire after inactivity. By default, both conditions must hold: the connection is at least 14 days old, and it has had no incoming or outgoing message recorded for 14 days. A recorded outgoing message counts as activity even if delivery fails. Dedicated lines and organization-owned shared pools are not subject to this expiry policy. Expiry disconnects the recipient without deleting conversation history. It does not send a notification at the time of expiry. If the recipient later messages the expired connection, they can receive an automatic reply. If they already have a valid active connection to the same agent on another number, the reply asks them to resend their message to that current number. Otherwise, it directs them to reconnect through the router. It never reveals another agent’s connection. The original message is not forwarded, delivered to the agent, or used to reopen the expired connection. Repeated messages may not repeat the notice immediately. The conversation reportsassignment_status: "released".
Sending to an expired connection returns 409 with
detail.error: "imessage_connection_expired" and a readable explanation in
detail.message.
If reconnection is needed, ask the recipient to text connect @my-agent to the
current router number, replacing
my-agent with your agent’s handle. Sending to the old conversation does not
reconnect them automatically. Wait for the recipient to reconnect and message
your agent before sending again.
Shared numbers can be reused. If the recipient later connects to a different
agent on the same number, their messaging app may show those messages in the
same number-based thread. The connection confirmation identifies the agent;
your agent does not gain access to another agent’s conversation history.
Reading and replying
Once a sender has messaged your agent, list conversations and read messages. Reply only to a conversation that supports outbound messages. Replies can target the conversation by ID or the recipient by E.164 phone number:404 tells the recipient to text the connect command to the router; an expired connection instead returns 409 imessage_connection_expired. A dedicated line can start a new conversation. For a organization-owned shared pool, use connect, subject to its eligibility checks. If a previous conversation is no longer active, sends into it return 409. Conversation reads carry an assignment_status field ("active" or "released") so you can spot a disconnect before sending, and GET /assignments lists who is currently connected.
Rolling 24-hour outbound limits vary by plan. See Pricing for current limits. When the cap is reached, 429 responses carry Retry-After and X-RateLimit-* headers.
Dedicated lines also limit agent-initiated conversations to 10 new recipients per rolling hour and 40 per rolling 24 hours. Existing recipients and recipients who message the line first do not consume these first-contact slots. If either window is full, sends to new recipients return 429 until enough slots age out.
Before the first reply
For 1:1 messages on every line type, including dedicated lines, wait for a qualifying recipient reply before sending phone numbers, links, or media. Until then, keep each message under 160 characters (159 maximum). These content restrictions do not apply to group sends. They do not let a shared-service identity send before its connection is ready. Send at most three 1:1 messages total before the recipient’s first qualifying reply. This ceiling does not grant additional sends when a connection requires an earlier reply. After the first qualifying reply, the three-message cap ends; other unanswered-message limits still apply. Exceeding the pre-reply message cap or violating these content requirements returns422 before queuing a message. Read detail.error to identify the reason:
A numeric identifier that resembles a phone number can also trigger the phone-number error. Wait for a reply before including it. Once a qualifying reply arrives, these pre-reply content restrictions no longer apply; other send requirements and limits still do. See Send message for the API contract.
Reply to a specific message
Use a conversation that supports outbound messages. A reply target does not make a receive-only conversation replyable. With SDK and CLI0.7.13 or later, reply to a message in a 1:1 or group conversation. Keep conversation_id for the whole chat, and use reply_to_message_id to choose the exact message. Native threading is used when supported. The default plain_reply_fallback: true allows an ordinary response in that same conversation when the target is known to use SMS/RCS or was downgraded. Without a target, sending behaves as before.
reply_to_message_id identifies the immediate parent when visible and resolved. A non-null parent identifies a reply. thread_root_message_id identifies the original message when available. thread_id is a separate opaque UUID for reading the whole thread, not a signal that replies exist. A newly received or confirmed sent standalone message can have its own thread ID and itself as the root before anyone replies. Thread reads return the visible root and replies oldest first, with cursor pagination. A normal conversation read stays flat and includes all messages. Filter that list with conversation_id and thread_id when you need its existing newest-first order.
The same fields appear when another person replies natively, including within a group. Metadata may be null or absent for pending messages, older history, or unavailable references. A null parent does not rule out an unresolved or hidden reply relationship. An ordinary fallback has no parent and does not join the target’s native thread; it can have its own standalone thread ID. Use the returned message metadata, not the requested target, to decide how to display a response.
To require native threading, pass plain_reply_fallback=False in Python, plainReplyFallback: false in TypeScript, or --no-plain-reply-fallback in the CLI. Missing, inaccessible, pending, failed, or unresolved targets remain errors in either mode. Fallback does not change recipients or send limits, force SMS/RCS, or automatically resend after delivery rejection, a timeout, or an uncertain result. It only covers a target already known to have an unsupported transport before sending. If an apparently eligible native reply is rejected, it fails even when fallback is enabled.
A queued response does not guarantee delivery. Poll Get message for its current status and errors. A target that becomes unavailable before sending produces status: "error" and error_code: "imessage_reply_target_unavailable". Delivery webhooks do not cover every failure before sending, so they supplement polling rather than replace it.
In the Inkbox Console, click a message to focus the reply composer, then write your reply and send. Quoted context stays above each reply in the conversation. Click a quoted original to find and highlight it without opening a separate thread view. The console uses the default reply fallback described above. Cancel the quoted target to return to an ordinary message without losing your draft. Reply targeting is unavailable in a disconnected 1:1 conversation.
Through MCP, send a targeted reply with inkbox_imessage_send, including conversation_id, reply_to_message_id, and the expected recipient or group recipient list. plain_reply_fallback defaults to true; set it to false to require native threading. See MCP reply inputs.
To read a thread, use inkbox_conversation_get with channel: "imessage", the conversation_id, and either reply_thread_id or reply_thread_message_id. Thread pages use cursor and return next_cursor; omit the thread selector to keep the ordinary conversation read.
See Messages for the request fields and Get conversation thread for reading by thread ID.
Dedicated-line group chats
Companion mode supports sponsor-invited participation in eligible dedicated-line groups. It requires a new sponsor message, verified current membership, and an available group. A dedicated line alone does not admit every externally started group. Reply using its canonical conversation ID; shared iMessage service remains 1:1. An identity with an active attached dedicated iMessage line can start a group by passing a list of 2-8 distinct E.164 phone numbers to the existing send endpoint. The response’sconversation_id is the canonical thread key; use it for every later reply into that exact group.
include_groups=true when listing iMessage conversations or messages, or fetch a known group directly by conversation_id. Group messages expose sender_number, a participant snapshot, and per-recipient delivery state. The MCP conversation-list tool keeps the same opt-in switch. Through MCP, inkbox_imessage_send accepts a singular recipient for unchanged 1:1 sends or a list of 2-8 recipients for group creation. Group replies include both conversation_id and the expected current recipient list.
Group creation is asynchronous. Conversations report group_creation_status as creating, not_created, or ready. While creating, the conversation and queued messages exist locally before the remote thread is established; later sends to the same conversation wait for the creation send to resolve. If the first attempt fails, its message remains visible in the same local conversation; send the next message using that conversation_id to retry creation. A successful retry keeps the same local conversation and establishes the group used by later sends.
Conversation membership history is best-known and append-only: newly added members may appear only after later activity, while members who leave remain in the stored history. Sends into an existing group resolve its current recipients and apply send checks to each one. There is no group-member add/remove API in V1, and changing a direct to list selects or creates another group instead of editing membership. After the stored history grows, an old participant subset can create a separate group; use conversation_id to preserve thread continuity. Tapbacks are supported for ready groups, and expressive send styles and media are supported on group creation and replies.
MCP can create a dedicated group for 2-8 recipients, reply to a known group, read group delivery state, and react to inbound group messages. A group reply includes the expected participant set from the conversation read; if live membership changed, Inkbox rejects the reply before sending.
See Group chats for exact request, response, matching, and error semantics.
Tapbacks
Choose a phone-number conversation or a ready group. Receive-only conversations do not support outbound tapbacks. Agents can react to inbound messages in 1:1 and ready group conversations withlove, like, dislike, laugh, emphasize, question, or eyes (which displays as 👀). iMessage has no concept of reacting to your own messages, so an agent can’t tapback its own.
reaction: "custom" with the emoji in custom_emoji. Arbitrary custom-emoji tapbacks are receive-only; outbound sends accept the classic six plus the named eyes value, not a literal emoji. Group reactions have assignment_id: null and use remote_number to identify the participant.
Read receipts, typing, and media
Use a phone-number conversation for these 1:1 actions. Reading history is supported for receive-only conversations, but sending a read receipt or typing indicator is not. Round out the native feel in 1:1 conversations: send a read receipt when your agent has read the thread and show a typing indicator while it works. These operations are available through the API, SDKs, and CLI, but are intentionally not exposed as MCP tools. Media also works in group sends, but group read receipts and typing indicators are not supported.send_style with values like slam, confetti, lasers, or invisible (invisible ink); styles can also accompany media. See the Messages reference for the full list.
Filtering who can reach your agent
iMessage uses the same phone communication list as SMS and calls, for incoming and outgoing communication. The identity owns the list whether it uses shared service or a dedicated line. You can configure the list without assigning a dedicated number. For a shared policy,imessage_filter_mode is an alias of phone_filter_mode. Setting either sets both receiving and sending defaults; conflicting values in one update return 422. New rules omit direction by default and cover both:
blacklist(default): unmatched numbers are permitted.whitelist: unmatched numbers are denied.
Directional permissions and connections
With SDK/CLI0.7.3 or later, use phone_inbound_filter_mode and phone_outbound_filter_mode for independent defaults. There is no separate iMessage pair. Inbound means from the person to your agent; outbound means from your agent to the person. Rules accept direction: "inbound", "outbound", or "both". Inbound permission alone does not permit a reply. See the phone configuration example.
Shared-line connect and reconnect require both effective permissions. A both rule is not required when the defaults already permit both directions. Connecting never inserts a rule or changes permissions. Existing connections remain inspectable after policy changes; subsequent traffic checks its own direction. Dedicated-line traffic retains per-direction checks.
A message needs ordinary or valid Companion permission to reach the agent. An outbound denial returns 403. See iMessage contact rules for directional list filters, consolidation, and atomic one-side updates.
Reacting to events in real time
Inbound messages and tapbacks are delivered through webhook subscriptions owned by the agent identity, regardless of whether it uses shared service or a dedicated line. The same subscription can also carry the outbound delivery-lifecycle events —imessage.sent, imessage.delivered, and imessage.delivery_failed — so your agent knows when a reply actually landed:
auth_token="your-endpoint-token" (Python), authToken (TypeScript), or --auth-token-stdin (CLI) to the create call — see Authenticating to your endpoint.
How messages are delivered
For ordinary messages, Inkbox tries iMessage first. If a recipient isn’t reachable over iMessage, delivery can fall back to SMS — the message’sservice field reports the transport actually used ("imessage", "sms", or "rcs"), and was_downgraded is set when a fallback happened. You do not select the transport in a send request. A native reply requires iMessage support; plain_reply_fallback can instead select an ordinary message before sending when the target is already known to be unsupported. It does not retry a rejected native send without threading. Check service and was_downgraded on the current message for the reported delivery outcome.
Retry a message request
The SDK preserves a generated request key during bounded retries. Use an explicit key when a workflow repeats the same send across calls; the CLI accepts--idempotency-key on imessage send. The same key and input replay the original
response. Read the message for current delivery status. See
message send retries and ID lookup.

