Group chats
iMessage group chats use the existing messages and conversations endpoints. They are available only to identities with an active attached dedicated_outbound iMessage number. Shared and dedicated inbound numbers remain 1:1.
This is separate from group SMS/MMS: iMessage groups use iMessage conversation IDs and iMessage delivery state.
Start or select a group POST
POST /messagesPass 2-8 distinct E.164 phone numbers in to. An admin API key also passes the sending identity as agent_identity_id; an identity-scoped API key always sends as its own identity.
The server normalizes the requested member set before resolving the conversation:
- One unresolved local group with the same requested members is reused before a remote thread has been established, whether creation is still pending or being retried.
- One established group with the same best-known members is reused only when its current membership can be verified and still matches the request.
- If no reusable match remains, a new group conversation is created.
- If multiple groups match, the request returns
409 ambiguous_group_conversationinstead of choosing one. Continue a specific thread by itsconversation_id.
A to request never adds or removes members from an existing group. A different member set selects another exact match or creates a separate group. A scalar to value and a one-item to list keep their existing 1:1 behavior. to and conversation_id are mutually exclusive.
The 201 response uses the regular Message object. Group-specific fields look like this:
Each previously unknown participant counts toward the dedicated outbound line's limit of 40 new contacts per rolling 24 hours. The whole group request is rejected if the line does not have enough remaining slots.
Creation lifecycle
Group creation is asynchronous. The initial 201 means Inkbox stored the local conversation and queued the first message; it does not mean the remote thread is ready yet. Read the conversation or opt groups into the conversation list to inspect group_creation_status:
| Value | Meaning |
|---|---|
creating | The initial group creation attempt is still resolving |
not_created | No group has been created yet, or the latest creation attempt failed definitively |
ready | The group is ready and later sends use the same remote thread |
While the conversation is creating, another send to the same conversation_id or exact to set is stored as another pending message on that local conversation and waits behind the elected creation send instead of initiating creation independently. If the initial attempt fails, the local conversation keeps the same conversation_id and the failed message remains visible in its history with status: "error". Send the next message with that conversation_id to retry creation. A successful retry changes the conversation to ready; later sends continue the newly bound group without creating another local conversation.
Continue the same group
Keep the conversation_id from the first response and use it for every later send into that exact thread:
conversation_id is the canonical group key. Do not reconstruct a thread from its participants when you need to continue a specific conversation: membership can change, and more than one real group can have the same members. A send by conversation_id continues that native thread with its verified current membership; all current recipients must pass the send preflight.
Group creation and conversation replies support the same 13 expressive send_style values as 1:1 messages. A style can accompany text, one media_urls item, or both:
List and read groups
Existing list endpoints exclude groups by default so current 1:1 callers keep the same result set and pagination. Pass include_groups=true to opt in:
GET /conversations?include_groups=true
GET /messages?include_groups=true
GET /conversation-projections?include_groups=trueKnown conversation IDs work without the list flag:
GET /conversations/{conversation_id}
GET /messages?conversation_id={conversation_id}
GET /conversation-projections/{conversation_id}REST conversation reads return is_group: true, participants, group_creation_status, and null assignment_id, assignment_status, and remote_number fields. Projection and MCP conversation reads expose is_group, participants, and the same null assignment/recipient fields, but do not include group_creation_status. Group messages return a participant snapshot. On inbound group messages, sender_number identifies who sent the message and remote_number mirrors that value for compatibility.
Membership semantics
Conversation participants is an append-only, best-known history of E.164 phone numbers observed in the thread. A newly added member may not appear until later group activity, and a member who leaves is not removed from the stored list. Do not use this field as an authoritative current-membership or authorization check. Before an existing-group send or tapback is accepted, Inkbox resolves the thread's current recipients and applies opt-out and contact-rule checks to each one.
There is no API operation to add or remove a group member in V1. Membership changes happen in the native group conversation, and Inkbox updates its best-known participant history as activity reveals changes. After that history gains a member, sending to the original subset can create a separate group instead of continuing this one; use conversation_id whenever thread continuity matters.
Delivery and current limitations
Outbound group messages include one recipients entry per participant. Group delivery transitions are message-level in V1, so the aggregate state is applied to those participant entries together. See iMessage webhooks.
Ready groups support tapbacks with the same targeting, replacement, and removal semantics as 1:1 conversations. Group reactions have assignment_id: null, and an inbound group reaction's remote_number identifies the participant who reacted.
Read receipts and typing indicators return 409 operation_not_supported_for_group_conversation. MCP can opt group conversations into reads, but MCP group sends and actions are not available yet.
Detaching or reassigning the dedicated line leaves the old conversation readable as history to its original organization and identity, but sends and actions on it return 409. A later line owner does not inherit the old group.
Error responses
| Status | Description |
|---|---|
| 400 | The sending identity is not enabled for iMessage, or an admin send by to omitted agent_identity_id |
| 402 | The organization's current billing-cycle iMessage capacity is exhausted |
| 403 | At least one participant is blocked or opted out |
| 404 | The sending identity or requested conversation_id was not found or is not visible to the caller |
| 409 | The identity has no active dedicated outbound iMessage number, multiple groups match the member set, current membership cannot be resolved consistently or changes while the request is resolving, the line attachment changed, or the requested group action is not supported |
| 422 | A to list has more than 8 E.164 recipients, contains a duplicate or the sending line, or the body is otherwise invalid. A one-item list keeps 1:1 behavior |
| 429 | The identity's message limit or the line's new-contact limit was reached; honor Retry-After |
| 503 | group_membership_unavailable because current membership for an existing group could not be verified; retry with backoff |
Use the response's detail.error to choose the recovery path. Select a specific conversation_id for ambiguous_group_conversation. For group_membership_changed, verify the intended members and repeat the request so matching runs again. Retry group_conversation_not_ready only after the conversation is ready on its current line. Membership-consistency conflicts and group_membership_unavailable may be retried with backoff, while attachment and unsupported-operation conflicts require changing the line or operation instead of retrying unchanged.