Skip to main content
A group chat is an ordinary chat with type: "group". You start one by sending a first message to two or more recipients, then send follow-ups, read its participants, add or remove people, rename it and take one of your numbers out of it with the endpoints on this page. Group chats need a Dedicated phone number. On the Free Plan, every group operation returns 400 FEATURE_NOT_ENABLED. An AI Agent number can’t start a group or send to one, including replies in a group a contact added it to (403 CONTACT_MUST_TEXT_FIRST); it still receives that group’s messages. See Plans.

Start a group chat

Send POST /v1/chats with from and two or more phone numbers in to. from is required for a group. Blujay creates the group on that phone number and queues the first message.
curl
Response (202)
Keep these rules in mind for a group’s first message:
  • to takes 2 to 31 US phone numbers. Email addresses aren’t accepted.
  • Group chats don’t accept preferred_service (400 VALIDATION_ERROR). Blujay picks the service for the whole group: iMessage when it can confirm every recipient on iMessage, otherwise SMS/MMS. The chat’s service reports which one (iMessage or SMS), or null until Blujay knows.
  • If your number already has a group with exactly these recipients, Blujay sends into that group instead of creating a new one.
  • The same first-message rules apply as for direct chats: no links, media or contact cards, 300 characters, no effects and no reply_to. They apply to every POST /v1/chats request, even into an existing group. See Sending rules.
Send an Idempotency-Key so you can retry safely; see Idempotency.

Send follow-ups

Send the rest of the conversation to POST /v1/chats/{chat_id}/messages, exactly as you would in a direct chat.
curl
Text, link, media and contact card parts all work in groups once someone in the group has replied; until then the pre-reply rules apply to the group as a whole, and anyone’s reply counts. A follow-up is rejected with 403 RECIPIENT_SUPPRESSED if anyone currently in the group has opted out. See Opt-outs.

Know which features need an established iMessage group

Two terms matter on this page:
  • An active group is one Blujay still treats as current. Blujay checks this before every group operation. There is no status field on the chat today; for a group, phone_numbers[].can_send and sendability_reasons on GET /v1/chats/{chat_id} are the closest check.
  • An established iMessage group is an active group that iMessage has created on your phone number and linked to this chat. A group you start with POST /v1/chats becomes established once its first message has gone out over iMessage and iMessage reports the group back to Blujay. A group carried by SMS/MMS never becomes one. While the link is missing, sendability_reasons contains group_not_ready.

Read a group and its participants

GET /v1/chats/{chat_id} returns the group. Your own Blujay numbers in the group are listed in phone_numbers, and everyone else is in participants. Each entry in phone_numbers has can_send, which tells you whether that number can send to the group right now.
curl
Response (200)
When can_send is false, sendability_reasons says why: number_inactive, number_setup_incomplete or group_not_ready. The chat lists only people who are still in the group. To include people who left, call GET /v1/chats/{chat_id}/participants. It returns every participant with status set to active or left, plus joined_at and left_at. contact_id appears only when the participant matches a contact in your workspace.
curl

Add or remove a participant

Add a phone number or email address with POST /v1/chats/{chat_id}/participants, and remove one with DELETE on the same path. One of your Blujay numbers in the group makes the change, and everyone in the group sees it. Both endpoints honor Idempotency-Key. The 202 means the phone accepted the change; a moment later Blujay records it the same way as a change made on the phone, with a participant.added or participant.removed webhook and an entry in the chat.
curl
Response (202)
To remove someone, send the same body with DELETE:
curl
What to expect:
  • Adding someone who is already in the group returns 200 with status: "already_active". Removing someone who already left returns 200 with status: "already_removed". Neither contacts the phone.
  • Removing someone who was never in the group returns 404 NOT_FOUND with the message “This handle is not a participant of the group”. Read GET /v1/chats/{chat_id}/participants to see who is in it.
  • If more than one of your numbers is in the group, set from_phone_number_id to choose which one makes the change. Without it you get 400 PHONE_NUMBER_REQUIRED.
  • A removal that would leave the group with fewer than three members returns 409 GROUP_MINIMUM_PARTICIPANTS, the same code as leaving in that situation.
  • Your own Blujay numbers can’t be added or removed here (400 VALIDATION_ERROR). To take one of your numbers out of a group, leave the group.
  • If your number isn’t ready to manage the group right now, you get 503 PHONE_NUMBER_UNAVAILABLE. Retry later with the same Idempotency-Key.

Confirming the change

202 means Blujay accepted the request and handed it to your phone number. The change is confirmed when the participant.added or participant.removed webhook arrives for that chat_id and handle, or when GET /v1/chats/{chat_id}/participants shows the new state. If iMessage refuses the change, no failure event is sent today: the participant list stays as it was, and you learn that only by reading it back. Give the change a minute, then read the participants before you retry.

Rename a group

Set a new group name with PATCH /v1/chats/{chat_id} and display_name (1 to 100 characters). Everyone in the group sees the new name. You can’t clear a group name. Rename ignores Idempotency-Key; repeating a rename with the same name is harmless. The 202 means the phone accepted the rename; a moment later Blujay records it the same way as a rename made on the phone, with a chat.group_name_updated webhook and a “named the conversation” entry in the chat.
curl
Response (202)

Confirming the change

The rename is confirmed by the chat.group_name_updated webhook, or by display_name on GET /v1/chats/{chat_id}. If iMessage refuses the rename, no failure event is sent today; display_name keeps the old value. A group icon can’t be set through the API: group_chat_icon on this endpoint returns 400 FEATURE_NOT_ENABLED.

Leave a group

POST /v1/chats/{chat_id}/leave takes one of your Blujay numbers out of an established iMessage group. A group must always keep at least one of your numbers, so you can only leave a group that has two or more of your numbers in it. from_phone_number_id names the number that should leave. It’s optional when only one of your numbers is in the group (then the body can be {}), and required otherwise.
curl
Response (202)

Confirming the change

Leaving ignores Idempotency-Key and has no webhook event. Confirm it by reading the chat: the number disappears from phone_numbers on GET /v1/chats/{chat_id}. If a leave request times out, read phone_numbers before you retry. If iMessage refuses the leave, no failure event is sent; the number stays in phone_numbers.

Receive group messages and changes

When someone replies in a group, the message.received webhook identifies them with a sender object instead of the top-level contact_id that direct replies carry. sender has type (phone or email) and address, plus contact_id when the sender matches a contact in your workspace. Reading the same message with GET /v1/messages/{message_id} returns the same sender object.
message.received
To follow membership and name changes, including ones made outside the API, subscribe to participant.added, participant.removed and chat.group_name_updated. Deliveries can arrive out of order, so treat GET /v1/chats/{chat_id} and GET /v1/chats/{chat_id}/participants as the current state. See Webhook events and Receive messages.
Common mistakes
  • from is required for a group and is a string. Leaving it out returns 400 VALIDATION_ERROR.
  • A second POST /v1/chats into an existing group is still a first message: no links or media, even after replies. Use POST /v1/chats/{chat_id}/messages.
  • 202 on add, remove, rename or leave means accepted, not done. Confirm with the webhook or a read.
  • A group chat has no preferred_service; its service reports what the group runs on. Don’t wait for delivered on group messages; see Track a message.
  • Retrying a failed group message returns 409 RETRY_NOT_ALLOWED.

Limitations

  • Dedicated numbers only. Free Plan: 400 FEATURE_NOT_ENABLED. AI Agent: 403 CONTACT_MUST_TEXT_FIRST on create and send. See Plans.
  • 2 to 31 recipients on create; phone numbers only (400 VALIDATION_ERROR).
  • No preferred_service; Blujay picks for the whole group and the chat doesn’t report the service.
  • Pre-reply rules apply to the group until anyone in it replies. See Sending rules.
  • Participant changes, rename, leave, threaded replies, effects and typing need an established iMessage group (400 UNSUPPORTED_CHAT_OPERATION or 400 FEATURE_NOT_ENABLED). Text styles never work in groups.
  • Group icon isn’t supported (400 FEATURE_NOT_ENABLED).
  • A group must keep at least three members (409 GROUP_MINIMUM_PARTICIPANTS on remove and on leave) and at least one of your numbers (409 LAST_PHONE_NUMBER_IN_GROUP).
  • Your own numbers can’t be added through the participants endpoint. There’s no API to add a second Blujay number to a group.
  • A failed add, remove, rename or leave has no failure event today.
  • Group messages can’t be retried (409 RETRY_NOT_ALLOWED), and delivered/read may never be reported for them.

Next steps

Threaded replies

Reply to a specific message in a direct chat or an established iMessage group.

Effects and text styles

Add screen and bubble effects to group follow-ups.

Webhook events

See every event, including group participant and name changes.

Idempotency

Retry group sends and participant changes without doing them twice.