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_serviceis the service you asked for (nullfor Auto).serviceis the service that actually carried the message, once confirmed. Webhooks spell services in lowercase (imessage,rcs,sms); message reads useiMessage,RCSandSMS.- Not every message gets every event. SMS usually stops at
sent, because carriers don’t report delivery or read status reliably. iMessage reportsdelivered, andreadwhen the recipient has read receipts on, and it often skipssentaltogether when delivery is confirmed within the same moment. Treat each event as the message’s newest state rather than expecting the full sequence. degradationslists 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 thechat_idfrom yourPOST /v1/chatsresponse next to your own record, or readGET /v1/chats/{chat_id}: a direct chat’sparticipantsentry carries thecontact_id. Onlymessage.receivednames the sender directly.
Failures
Amessage.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 hascontact_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:
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.addedorreaction.removed, with the message it applies to. It never arrives asmessage.received, and it isn’t listed byGET /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.receivedwith"parts": []and notext. You can’t tell from the event what kind of content it was. - Stickers, voice memos and contact cards arrive as
mediaparts: a sticker sent as an image is image media, a voice memo is audio media, and a shared contact card is atext/vcardmedia part. - Effects and text styles on inbound messages aren’t reported.
- The service can be
nullon an inbound message when Blujay hasn’t confirmed it.
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.editedcarries the newtextandparts, plusedited_atandedit_count. Read earlier versions withGET /v1/messages/{message_id}?include=edit_history.message.unsentcarriesunsent_atandunsent_scopebut never the text. Withunsent_scope: "all"the whole message was removed. With"text"only its caption was removed and its attachments remain.
message.edited
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:
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 atexpires_ateven if nostoppedarrives; - per chat, ignore an event older than the last one you applied, and let
stoppedwin 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:
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
400FEATURE_NOT_ENABLEDon subscribe. - Delivery events don’t name the contact. Keep the
chat_idtocontact_idmapping yourself or read the chat. - A
message.receivedsnapshot 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-03and 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.