Skip to navigation

message.received

View as Markdown

Emitted when an inbound message (SMS, MMS, an iMessage, or a WhatsApp message) arrives on one of your Dial numbers. This is the event you wait for when receiving a one-time code or any inbound reply.

Messages sent to a group conversation arrive on this same event, carrying groupId.

Schema

Shares the common event envelope (id, object, type, version, createdAt, relatedObject). relatedObject is { id: <messageId>, type: "message", url: null } — messages have no get-by-id endpoint yet. The data payload:

data fieldTypeDescription
messageIdstringDial-issued ID for the message. Matches the id returned by GET /api/v1/messages.
fromstringSender phone number, E.164. On a group message, the participant who sent it — not the group.
tostring | nullYour Dial number that received the message, E.164 — and null when groupId is set, because a group message is addressed to the group. Which of your numbers the conversation is on is the message’s phoneNumberId.
groupIdstring | nullThe group conversation the message belongs to, or null for a one-to-one conversation. A Dial ID — never the channel’s own group identifier. Filter on it with filters: { "groupId": "grp_123" } to wait for one group’s traffic.
channelstringThe channel the message arrived on: "sms", "imessage", "rcs", or "whatsapp". A group message is "whatsapp" or "imessage".
bodystringMessage text.
mediaarrayMedia attachments (MMS on SMS numbers, attachments on iMessage numbers), in received order. Empty for text-only messages. Each item: id (public media ID), url (stable unauthenticated Dial URL serving the media — safe to fetch or render immediately), contentType (MIME type), originalUrl (always null on inbound messages; media is served from url).
replyToIdstring | nullOn iMessage numbers, when the inbound message is a threaded reply, a reaction, or a poll vote targeting one of your messages: the targeted message’s ID. Null for ordinary messages, or when the target isn’t a message on your account.
reactionstring | nullOn iMessage numbers, when the inbound message is a reaction (e.g. a Tapback): the reaction’s emoji. body is empty for reactions. Null for ordinary messages and threaded replies.
pollVoteobject | nullOn iMessage numbers, when the inbound message is a vote on one of your polls: { "optionId": "opt_k3m9x2", "selected": true }. selected: false is a retracted vote. replyToId is the poll message and body is empty. Null for every other message.
sourcestring"external" for messages delivered by a carrier; "internal" for messages synthesized by Dial (e.g. test injections). Filter on "external" if you only care about real inbound traffic.

Example

{
"id": "evt_3f9a2b1c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f90",
"object": "event",
"type": "message.received",
"version": 1,
"createdAt": "2026-05-28T14:32:08.412Z",
"relatedObject": { "id": "msg_01HW3X7P5QF6K2YQ9YJ7Q8R5N3", "type": "message", "url": null },
"data": {
"messageId": "msg_01HW3X7P5QF6K2YQ9YJ7Q8R5N3",
"from": "+14155550123",
"to": "+14155559876",
"channel": "sms",
"body": "Your verification code is 123456",
"media": [
{
"id": "a3f9c2d41e8b4f0a9c6d2e7b5a1f8c30",
"url": "https://getdial.ai/public-media/a3f9c2d41e8b4f0a9c6d2e7b5a1f8c30.jpg",
"contentType": "image/jpeg",
"originalUrl": null
}
],
"replyToId": null,
"reaction": null,
"pollVote": null,
"source": "external"
}
}

A group message

Same event, with the destination moved from to to groupId:

{
"data": {
"messageId": "msg_01HW3X7P5QF6K2YQ9YJ7Q8R5N4",
"from": "+14155550123",
"to": null,
"groupId": "grp_123",
"channel": "whatsapp",
"body": "I'll bring the cake",
"media": [],
"replyToId": null,
"reaction": null,
"pollVote": null,
"source": "external"
}
}

from is the participant who sent it. To know which of your numbers the conversation is on, read the message’s phoneNumberId from GET /api/v1/messages?groupId=grp_123.

A poll vote

A vote on a poll you sent. replyToId is the poll message; read GET /api/v1/messages for the poll’s current poll.options[].votes tally.

{
"data": {
"messageId": "msg_01HW3X7P5QF6K2YQ9YJ7Q8R5N5",
"from": "+14155550123",
"to": "+14155559876",
"groupId": null,
"channel": "imessage",
"body": "",
"media": [],
"replyToId": "msg_01HW3X7P5QF6K2YQ9YJ7Q8R5N2",
"reaction": null,
"pollVote": { "optionId": "opt_k3m9x2", "selected": true },
"source": "external"
}
}

Filter on the poll with filters: { "replyToId": "msg_01HW3X7P5QF6K2YQ9YJ7Q8R5N2" } to wait for one poll’s votes.

When it fires

  • Every time a message is delivered to one of your Dial numbers (SMS, MMS, an iMessage/RCS message on an iMessage number, or a WhatsApp message on a line with WhatsApp connected).
  • For a message sent to a group your line is in — once per line that is in the group.
  • For every vote and retracted vote on a poll you sent from an iMessage number.
  • Once per inbound message — no retries on the stream itself.
  • Internal test traffic (from dashboard or /api/v1/test/* helpers) carries data.source: "internal"; ignore those unless you’re explicitly testing.