group.create

Start a new group chat with two or more participants and an opening message.

POST /v1/commands/group.create

Creates a group chat and sends its first message in one operation. iMessage has no concept of an empty group, so the opening message is required.

Once the group exists it is a conversation too. A message someone writes in it arrives under the group's own conversationId, with isGroup: true; on that id you can send into the group, rename it or change its members with group.update, and leave it with group.leave.

On a live line Idempotency-Key is ignored — see the warning below.

Target

Prop

Type

The platform reads each participant the way the channel reads an address — a phone number to E.164, an email lower-cased — and checks the list before anything is sent, in test and live alike. The first participant is read first, as the recipient the command's line is resolved for. A participant that is refused, the first included, is reported at the list's own key — the key you wrote, participantExternalIds or participants — and its index. Each refusal is 422 validation_failed, the line's channel in the entry:

  • a participant the channel cannot read, at target.participantExternalIds[i] (or target.participants[i]);
  • any participant, the first included, that is an email with a control character in it, over 512 characters or not in composed Unicode (NFC), at target.participantExternalIds[i] (or target.participants[i]);
  • fewer than 2 or more than 32 people once a repeat — the same person written two ways — is counted once, at target.participantExternalIds (or target.participants).

A first participant who opted out, is blocked or is paused is 403 recipient_not_allowed, with details.reason saying which, as a send to them would be.

Payload

Prop

Type

Request

curl https://api.mapier.ai/v1/commands/group.create \
  -H "Authorization: Bearer $MAPIER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target": {
      "kind": "new_group",
      "channel": "imessage",
      "participantExternalIds": ["+14155550100", "+14155550111"]
    },
    "payload": { "firstMessage": "kickoff" }
  }'

Retrying this request on a live line creates a second group. The line mints a fresh identity per call, so an Idempotency-Key header is accepted and ignored. If a group.create times out, wait for the settlement on the stream before deciding whether to retry. In test the sandbox honours the key, so a rehearsal will not show you this.

Response

202 Accepted
{
  "commandId": "cmd-5",
  "status": "queued",
  "disposition": "queued",
  "lineId": "0192a7b0-3e4f-7051-8c6d-7e8f9a0b1c2d",
  "lineSelection": "reused_active_chat"
}

Its command frame names no conversation: conversationId is null, because group.create opens the thread the transport then reports. The group's own id reaches you on its message frames once someone writes in it.

event: command
{
  "commandId": "cmd-5",
  "logicalActionId": "cmd-5",
  "status": "succeeded",
  "errorCode": null,
  "conversationId": null,
  "failureClass": null
}

Errors

A command answers the one set of HTTP refusals POST /v1/commands/{type} publishes — every type the same codes, service_busy and no_line_capacity (capacity_exhausted from 2026-10-15) among them — so that table is the one to handle. What is particular to this type:

  • No participants is 422 validation_failed at target.participants, before a line is resolved; a participant list the channel cannot take is refused as above, before anything is sent.
  • In the sandbox, a group.create whose participants pass that check is accepted and settles failed / unsupported_capability.
  • firstMessage empty or over 16,384 bytes: 422 validation_failed, reason invalid_text.

Settlement failures: gateway_rejected when iMessage refuses the group.

group_content_unverified is not a failure — its command frame settles ambiguous, and it means the line could not prove the group was created rather than that it was not. A blind retry there makes a second group. See Groups.

On this page