Skip to main content
Choose events with event_types when you create or update a subscription. Payloads below are for the default version, 2026-10-03. Every event uses the common envelope; only data is shown.

Reserved event types

Nine more event types appear in the API schema but can’t be subscribed to today: chat.created, chat.group_icon_updated, chat.group_name_update_failed, chat.group_icon_update_failed, contact.created, contact.updated, contact.deleted, contact.suppressed and contact.unsuppressed. Putting one in event_types returns 400 FEATURE_NOT_ENABLED. They may never be sent. In particular, there is no event for an opt-out and no event when a group rename fails: poll opted_out on the contact (Opt-outs) and read the group after a rename (Group chats). An event type that isn’t in the schema at all returns 400 UNSUPPORTED_WEBHOOK_EVENT.

Message lifecycle

message.accepted, message.sent, message.delivered, message.read and message.failed follow an outbound message. They cover messages sent from the Blujay apps, workflows, scheduled texts, integrations and the API. message.accepted is sent only for messages sent through the API.
  • requested_service is the service you asked for (null for Auto). service is the service that actually carried the message, once confirmed. Webhooks spell services in lowercase (imessage, rcs, sms); message reads use iMessage, RCS and SMS.
  • Not every message gets every event. SMS usually stops at sent, because carriers don’t report delivery or read status reliably. iMessage reports delivered, and read when the recipient has read receipts on, and it often skips sent altogether when delivery is confirmed within the same moment. Treat each event as the message’s newest state rather than expecting the full sequence.
  • degradations lists anything that was not delivered because the message went out as SMS or RCS, for example {"code": "effect_not_delivered", "reason": "…"}.
  • Lifecycle events carry no contact_id. They identify the chat. To know the person, keep the chat_id from your POST /v1/chats response next to your own record, or read GET /v1/chats/{chat_id}: a direct chat’s participants entry carries the contact_id. Only message.received names the sender directly.

Failures

A message.failed event has failed_at and a failure object:
Media problems also set failure_code to ATTACHMENT_FETCH_FAILED, ATTACHMENT_TOO_LARGE or UNSUPPORTED_ATTACHMENT_TYPE. The same failure object, failed_at and failure_code are on the message itself (GET /v1/messages/{message_id} and the lists), together with retryable. See Track a message.

message.received

Sent when someone messages one of your numbers. A direct message has contact_id:
text is the first text part, so you can read a plain reply without walking parts. It is absent when the message has no text. A message in a group chat has a sender instead of contact_id. sender.contact_id is present only when the sender is a contact in your workspace; a participant you never saved has none:
A media part can arrive with only type and attachment_id while the file is still being processed. The event is a snapshot and never changes, so read the message or the attachment again later for its details. See Media.

What you don’t get inbound

message.received is built from the text and files Blujay stored for the message. Anything else is either reported another way or not at all:
  • Tapbacks are reactions, not messages. A reaction arrives only as reaction.added or reaction.removed, with the message it applies to. It never arrives as message.received, and it isn’t listed by GET /v1/messages.
  • Content with no part type. A message Blujay stores with no text and no file, such as an iMessage app payload (Apple Pay, a poll, a shared location), arrives as message.received with "parts": [] and no text. You can’t tell from the event what kind of content it was.
  • Stickers, voice memos and contact cards arrive as media parts: a sticker sent as an image is image media, a voice memo is audio media, and a shared contact card is a text/vcard media part.
  • Effects and text styles on inbound messages aren’t reported.
  • The service can be null on an inbound message when Blujay hasn’t confirmed it.
For the full inbound walkthrough, see Receive messages.

message.edited and message.unsent

Sent when an iMessage changes after it was sent, whether you changed it through Blujay or the contact changed theirs. A contact’s edits and unsends are tracked for messages up to a week old. Both events carry the same message fields as a lifecycle event, plus the edit or unsend details.
  • message.edited carries the new text and parts, plus edited_at and edit_count. Read earlier versions with GET /v1/messages/{message_id}?include=edit_history.
  • message.unsent carries unsent_at and unsent_scope but never the text. With unsent_scope: "all" the whole message was removed. With "text" only its caption was removed and its attachments remain.
message.edited
A contact’s edited or unsent message carries contact_id in a direct chat or sender in a group, as on message.received. See Edit and unsend.
When a message is unsent, delete its text on your side too. Earlier events, such as message.received, keep the text they carried, including if they are delivered again.

Reactions

reaction.added and reaction.removed report the standard reactions: love, like, dislike, laugh, emphasize and question. They can arrive about a minute after the reaction.
message_id is the message that was reacted to. occurred_at is when the person reacted; observed_at is when Blujay saw it. When the reaction comes from one of your own numbers, reactor is {"type": "phone_number", "phone_number_id": "phone_abc123"}. Each event is one add or one removal; it doesn’t describe the full set of reactions on the message. A reaction from the contact counts as a response under the sending rules.

Typing

chat.typing_indicator.started and chat.typing_indicator.stopped report a contact typing in an iMessage chat:
In a group chat, the event adds participants: everyone typing in the group at that moment. started lists at least one person; stopped has an empty list. Replace your whole list of typers with each newer event. Group typing is sent only on webhook version 2026-10-03, and only for your Dedicated numbers (access_type: "dedicated"); earlier versions and other numbers never receive it. See Typing and read receipts. To show typing correctly:
  • ignore events after their expires_at, and clear the indicator at expires_at even if no stopped arrives;
  • per chat, ignore an event older than the last one you applied, and let stopped win when timestamps are equal.

Group changes

participant.added, participant.removed and chat.group_name_updated report changes to iMessage group chats your numbers are in.
chat.group_name_updated carries display_name instead of participant. For the current state of a group, read GET /v1/chats/{chat_id} and GET /v1/chats/{chat_id}/participants. There is no event when an add or rename fails; read the group to confirm. If two of your numbers are in the same group, each number reports the change: you get one event per number, each with its own phone_number_id and chat_id. To receive one, limit the subscription to one number with phone_number_ids.

Contacts

contact.tag_added carries the full contact and the tag. contact.assigned carries the contact, its new assigned_team_member (null when unassigned) and assignment_changed: true. Contact events always cover the whole workspace.

Phone numbers

phone_number.status_updated is sent when one of your numbers is replaced by a new one. You get one event for the old number and one for the new number:
Switch your sends to the new number’s phone_number_id. No event is sent when a number is suspended or its health changes; read GET /v1/phone-numbers for that.

Timestamps

Limitations

  • No event for opt-outs, contact creation, updates or deletion, chat creation, or a failed group rename or icon change. Those types return 400 FEATURE_NOT_ENABLED on subscribe.
  • Delivery events don’t name the contact. Keep the chat_id to contact_id mapping yourself or read the chat.
  • A message.received snapshot never updates. Media details that weren’t ready are only available by reading the attachment again.
  • Inbound effects, text styles, and the kind of an iMessage app payload aren’t reported.
  • Group typing needs version 2026-10-03 and a Dedicated number.

Next steps

Versions

What each payload version adds.

Track a message

Statuses and how to track a message.

Receive messages

Handle replies, media, groups and reactions.

Delivery and retries

What happens when your endpoint is down.