creator.message.* events mirror a creator’s messaging activity as it happens. They replace the legacy message.received and message.read events and add sent, mass_sent, read_by_fan, deleted and reaction. There is no legacy message.sent event, so creator.message.sent is the only event for a creator’s outbound message. To mirror whole chats into your own store, Keep a local copy of chats in sync combines these events with the chat API.
All message events require the
read:chat scope. Each event arrives in the envelope, and the tables below describe data. The conversation is addressed by the fan (fan.uuid); there is no separate chat id.
creator.message.received and creator.message.sent both carry the message content (text, media_uuids, price and more), and so does creator.message.mass_sent on its own mass message resource. The two content-bearing events don’t carry an identical set; Content fields shows which event carries what. creator.message.deleted is metadata-only, so use the chat API to fetch content for that one.
Message resource
creator.message.received, creator.message.sent and creator.message.deleted share this resource. A mass message has its own; see Mass message resource.
Core fields
Always present on all three events.Content fields
creator.message.received and creator.message.sent carry the message itself. Every content field is optional, and when the value doesn’t apply, the key is absent from the payload, not null and not [].
creator.message.sent is not metadata-only. It carries text, media_uuids, price, gif, message_type and unread_messages_count. It does not carry is_muted, tip or is_automated.
Content on sent is hydrated best-effort after the event is triggered. A lookup failure omits the fields rather than delaying or dropping the delivery, so an absent text on sent means “no text or not resolved”. Fall back to the chat API if you need certainty.
Fan object
avatar_url is the only identity field limited to creator.message.received. creator.message.sent and creator.message.deleted carry handle and display_name but no avatar.
Example: creator.message.received
A pay-to-view message with text and two media attachments:
media_uuids and price entirely; the keys are not present at all.
Example: creator.message.sent
The same envelope and object as received. sender is always creator, and is_muted, tip and is_automated are never present.
Example: creator.message.deleted
The core fields only. sender names which party unsent their own message, deleted_at is the unsend time, and created_at is always null.
creator.message.deleted fires only for a single-message unsend, from the creator web app, the public API or agency automation. Mass-message unsends, moderation takedowns and failed scheduled sends don’t emit it, so don’t treat this event as a complete record of disappearing messages.
Mass message resource
creator.message.mass_sent uses this resource. It fires when a creator sends a mass message, either a BROADCAST or a scheduled GHOST_PROMOTION.
The event describes the send, not each copy of it. A mass message is one message row fanned out over many conversations, so exactly one event is delivered per send, whatever the audience size.
A mass message never emits creator.message.sent. The two topics don’t overlap, so subscribe to both to see every creator-to-fan send. Doing so produces no duplicates.
Fields
Content fields follow the same omitted-not-nulled rule as the message resource: when the value doesn’t apply, the key is absent.
There is no
fan object. A mass message has no single recipient, so the resource names only the creator. The event also carries no unread_messages_count, is_muted, tip or is_automated.
Personalisation tokens
text is the body as the creator wrote it, with its personalisation tokens ({{handle}} and the others) left intact. The event is the send rather than one fan’s copy, so there is no recipient to render it against. Substitute per fan yourself if you need the text a given fan saw.
Recipient count
recipient_count is the size of the whole audience the send was addressed to, read from the mass message’s own record. A large send is dispatched in throttled batches, and this number covers all of them.
One case reports less. If the audience hasn’t yet been stamped on the record when the event is built, the count falls back to the batch that published it. That is exact for a send small enough to fit one batch, and under-reports for anything larger rather than guessing.
Which fans received it
The event does not carry the recipient list, and the recipient list is not exposed on the API.recipient_count is a size, not a set.
Sends that are never announced
- A retracted send. If the mass message is unsent while delivery is still running, no event is delivered at all.
- A mass unsend. Taking a mass message back emits nothing, on this topic or any other. In particular
creator.message.deleteddoes not fire for it, so don’t treat these two events as a matched pair.
Deduplicating a mass message
The eventid is a hash of the creator UUID and the mass message UUID, so it is stable per send and a valid idempotency key with no extra work. A delivery job that is re-driven for the same mass message publishes the same id again. See Deduplicating events.
Example: creator.message.mass_sent
A pay-to-view broadcast with one media attachment and a personalisation token in the body:
Read-receipt resource
creator.message.read uses this resource. It is a per-conversation read receipt, not a message entity, so it reports counts rather than a message UUID.
The event fires only when the read cleared unread state. Opening an already-read conversation emits nothing, so
creator.message.read is not a signal that the creator opened the chat.
Clearing a manual unread mark counts as clearing unread state, so this also fires with read_messages_count: 0 when the creator reads a conversation they had marked unread by hand. A 0 there is the marker being cleared, not a no-op.
The event covers the creator as reader direction only. For the fan reading the creator’s messages, see Read-by-fan receipt resource.
Ordering against a manual unread mark
creator.message.read is the clear half of the creator’s inbox badge. The set half is creator.chat.marked_unread, which fires when the creator deliberately marks a conversation unread.
The two are published independently and are not ordered against each other. Compare this event’s read_at with that event’s marked_at and apply whichever is later, rather than applying whichever arrived last. Both are stamped when the action was handled and stay put across delivery retries, which the envelope timestamp does not.
Deduplicating a read receipt
The eventid is a hash of the two parties and read_at, so each read occurrence has its own id. A creator who reads, marks unread and reads again with no new message in between gets two events with two ids. The id alone is a valid idempotency key. See Deduplicating events.
Example: creator.message.read
Read-by-fan receipt resource
creator.message.read_by_fan uses this resource. It fires when a fan marks the creator’s messages as read, which is what lets you render a “read by recipient” tick on the creator’s side.
Both parties are bare UUIDs on this event, unlike every other message event. There is no
email, handle, display_name or avatar_url, and no Fan object; you already hold the fan’s identity from the message events for the same conversation. There is no unread count either, because on this direction that is the fan’s own inbox state.
Like creator.message.read, this fires only when the read cleared unread state and at least one message was read. A fan opening an already-read conversation emits nothing, so this is not a signal that the fan opened the chat.
The event belongs to the creator whose messages were read, and is delivered to that creator’s consumers. A creator reading a fan’s messages produces creator.message.read instead, and a read whose counterpart is not a creator produces nothing.
Read time vs envelope timestamp
Useread_at, not the envelope timestamp. The envelope timestamp is stamped when the event is published, so it moves on a retry or a replay. read_at is stamped when the read was handled and is the only stable read time on the payload.
Deduplicating a read-by-fan receipt
Theid on this topic includes the UUID of the newest message in the conversation, so it carries an occurrence component and is a valid idempotency key on its own. See Deduplicating events.
Example: creator.message.read_by_fan
Reaction resource
creator.message.reaction uses this resource. Either party can react, so it names three roles rather than two.
On a creator-initiated reaction,
actor and creator carry the same UUID. Use sender to tell the direction and fan to identify the conversation, because actor alone can’t do it.
Only added reactions emit. Removing a reaction produces no event, and a swapped emoji arrives as a new creator.message.reaction.