/v1/messages API surface, with additional fields for routing.
Conversation kind
Messages carry aconversation_kind field — on the message.inbound webhook and on GET /v1/messages/{id} / POST /v1/messages responses:
one_to_one— a single contact plus your iBubbl linegroup— your iBubbl line plus 2 or more contacts
conversation_kind. The same message lifecycle applies to both kinds — only the addressing differs. (One exception: message.queued fires only for 1:1 sends.)
Group identity
Each group conversation has an opaquechat_guid, for example any;+;067ace6caf6c43aaa2f7d643975e15ea. Treat it as an opaque string: don’t parse it, and branch on conversation_kind rather than its prefix.
No webhook event carries chat_guid. To learn it:
- A group that messaged you: take the
message_idfrom themessage.inboundevent and callGET /v1/messages/{id}— the response includes the group’schat_guid. - A group you created: poll
GET /v1/messages/{id}for your first message untilchat_guidis non-null (see Lifecycle of a new group). - A group you’ve sent to: the
POST /v1/messagesresponse for an existing-group send includes it.
chat_guid can change. When a participant is added to a group (from the iBubbl dashboard), the group gets a new chat_guid, and a send to the old value returns 404 group_not_found. If that happens, look up the current value again with GET /v1/messages/{id} on a recent message from the group.
Use chat_guid to send into an existing group — see Sending messages.
Participants
Theparticipants array on the message.inbound webhook lists the group’s participants other than your iBubbl line:
Sender attribution
On inbound webhook events for group conversations,sender_phone identifies which specific participant sent the message:
sender_phone always equals from_number; both are kept so 1:1 integrations work unchanged. Either can be an Apple ID email address rather than a phone number when the sender uses one for iMessage.
Suppression in groups
If any participant of a group is on your suppression list, iBubbl blocks all outbound to the group with422 group_outbound_blocked_suppression:
participant_phones) and some of the numbers are opted out, the error also lists them in blocked_phones (as stored on your suppression list, so the format can differ from what you sent). A send into an existing group doesn’t include blocked_phones.
This is deliberate: sending into a group where one participant opted out would deliver to that participant, defeating the opt-out. Because participant removals aren’t seen (above), and an opt-out can’t be undone (see Suppression & Opt-Outs), a group with an opted-out participant stays blocked. To keep messaging the others, create a new group without that person.
Sending to groups
Two of the four request shapes onPOST /v1/messages are for groups:
Existing group
Send into a group you already know by
chat_guid (see Group identity for how to get it).New group
Pass
participant_phones (2+ E.164 numbers) and an optional group_name. iBubbl creates the iMessage group and sends your first message in one operation.POST /v1/messages for full examples.
Lifecycle of a new group
When you create a new group viaparticipant_phones:
- iBubbl validates the request: at least 2 distinct people (
422 insufficient_participantsif the numbers resolve to fewer — for example the same number twice), and no opted-out numbers. - The first message is accepted as
pending, and the response returns right away withchat_guid: null. - When the first message goes out, the iMessage group is created and the group’s
chat_guidis recorded. If the message is held for the contacts’ sending hours, that happens when the hold ends. - The message moves to
sentand themessage.sentwebhook fires.
GET /v1/messages/{id} until chat_guid is non-null, store it, and use the existing-group shape from then on.
The
message.sent event for a new group’s first message can occasionally be delivered twice. De-duplicate on data.message_id plus type.Limitations
These apply in v1:- No participant management. The API can’t add or remove participants, and removals made elsewhere aren’t seen (see Participants). Adding a participant from the dashboard changes the group’s
chat_guid. - No renaming.
group_namecan only be set when the group is created, and later renames aren’t picked up. - No editing or unsending. The v1 API can’t edit or unsend a message you sent. When a contact edits a message they sent you, you receive
message.edited; when a contact unsends one, no event fires. - No attachments.
POST /v1/messagessends text only, to 1:1 contacts and groups alike. - At least 2 participants. Single-participant “groups” aren’t a thing in iMessage; use the
toorcontact_idshape for 1:1 sends.

