Skip to navigation

Group conversations

View as Markdown

A group is a conversation that isn’t a phone number. Dial gives each one an ID of its own, and you address it the way you’d otherwise address a recipient — with groupId instead of to.

Groups exist on WhatsApp lines and on iMessage numbers. The group ID is channel-neutral — the same field, the same endpoints, whichever channel the conversation is on.

Your line has to already be in the group. Dial can send into a group someone added your number to; it can’t create one, join one, or accept an invite link on your behalf.

What each channel can do in a group

WhatsAppiMessage
Send textYesYes
Send mediaYes — one attachment per messageYes
Typing indicatorYesYes
Send a pollNoYes
Reply to, or react to, a message inside a groupYesNo

Everything else on this page works the same on both.

List your groups

dial group list
{
"groups": [
{ "id": "grp_123", "channel": "whatsapp", "name": "Planning bday party", "createdAt": "2026-09-01T10:22:04.000Z" },
{ "id": "grp_456", "channel": "imessage", "name": null, "createdAt": "2026-09-02T18:03:41.000Z" }
]
}

channel is the channel the group is on — whatsapp or imessage, the same value its messages carry — and it decides what you can do there. A group never changes channel.

createdAt is when Dial first learned of the group — either your line being added to it, or the first message that named it.

name can be null, and that isn’t an error. Participants rename groups whenever they like, so Dial doesn’t keep a copy of the subject — a stored name would be a cache with nothing to invalidate it. The name is read live from the line holding the conversation instead.

When a lookup can’t answer in time, that group comes back with name: null rather than failing the whole request. The IDs are Dial’s own and always available, so one quiet line never hides a group or blanks another one’s name. Retry if you need the name.

Send into a group

Give groupId where you’d normally give to:

dial message --group grp_123 --body "On my way"

to and groupId are mutually exclusive. Exactly one — sending both, or neither, is rejected with 400.

You don’t need to say which number it goes out from. A group already belongs to a line, so Dial uses that one and fromNumber / fromNumberId are optional here. You may pass one, and if it names a different line than the group’s, the send is refused with 400 rather than quietly sent from somewhere else — a message that left from an unexpected number can’t be taken back.

A group send is an ordinary message on that group’s channel, so the channel’s own rules apply. On a WhatsApp group that means at most one attachment per message, and one send at a time per line. On an iMessage group, media works exactly as it does in a one-to-one conversation.

Naming a channel that the group isn’t on is refused with a 400 rather than sent on the other one — a message delivered to a rail you didn’t ask for can’t be taken back. You can leave channel out entirely; the group already knows which one it’s on.

Show a typing indicator in a group

Give groupId where you’d normally give toNumber. Every member of the group sees it.

dial typing start --group grp_123
dial typing stop --group grp_123

The same rules as a one-to-one indicator apply: it’s fire-and-forget and free, nothing is persisted and no event is emitted, and sending a message into the group clears it natively — so start it again after a send if you’re still composing. Dial never clears it for you; send value: false when you stop composing without sending.

toNumber and groupId are mutually exclusive here too, and as with a send, fromNumber is optional because the group already names its line.

Leave channel out. A group is on exactly one channel, so the group already says which rail to show the indicator on; naming a different one is rejected with 400.

Read a group’s messages

dial message list --group grp_123

groupId combines with the other filters, so ?groupId=grp_123&direction=inbound is just the inbound half of that conversation.

On a group message, to is null — in both directions. The message is addressed to the group, and the group is not a phone number, so there’s nothing to put there. groupId carries the destination instead.

Which of your numbers the conversation is on is phoneNumberId. That field is set on every message Dial has ever recorded, group or not. If your integration reads to as “my number”, that’s the line to change — on a group message it’s the destination, and the destination is the group.

{
"id": "msg_789",
"phoneNumberId": "pn_123",
"from": "+14155550123",
"to": null,
"groupId": "grp_123",
"body": "I'll bring the cake",
"direction": "inbound",
"channel": "whatsapp"
}

On an inbound group message, from is the participant who actually sent it — not the group, and not your own line.

React to group messages

Inbound group messages arrive as ordinary message.received events carrying groupId, so you can wait for one group’s traffic specifically:

dial wait-for message.received --field groupId=grp_123 --timeout 60

Delivery and read receipts work as they do elsewhere, with one difference worth knowing: in a group, read means every recipient. A message counts as read once every participant has read it — never “somebody read it”.

On an iMessage group, a read may never arrive, and that isn’t a failure. Read receipts are something each recipient’s device chooses to send — the setting is off by default — and a receipt that can’t be attributed to a particular reader is discarded rather than counted as a guess. So the tally is never wrong, but it can stay incomplete indefinitely. Don’t build a flow that waits for read in a group; treat it as confirmation when it comes and nothing when it doesn’t.

Delivery in an iMessage group is inferred, because the channel sends no delivery receipts of its own: the first read Dial sees proves the message reached at least one device, so deliveryState advances then. On a WhatsApp group it stays what it is elsewhere on that channel.

Finding out about a new group is a poll, on both channels. There’s no join event: when someone adds your line to a group, that shows up as a new entry from GET /v1/groups (and the group’s first message arrives normally either way). List groups when you need to know what’s there.

Next