fan_status resource and are discriminated by change_type, but ride separate topics so you can subscribe to one without the other:
Neither has a legacy flat equivalent. Both require the
read:chat scope and are delivered in the Standard-Webhooks envelope; the fields below describe the data object.
Delivery
Both topics use the same delivery model. Each event reaches:- apps holding
read:chatfor the creator, and - the creator’s connector destination, when one is subscribed.
read:chat does not opt you into presence, subscribe to creator.fan.presence_changed explicitly. Presence is much higher volume than mute: one event per fan per online/offline transition, for every creator that fan is a contact of.
Presence is rolling out per creator, so a creator who has not been enabled produces no presence events yet. Mute changes fire on every mute/unmute action with no such gate.
When presence fires
online, the fan’s first socket connects. Repeated connections while already online do not re-fire.offline, the fan’s presence record lapses. There is no disconnect signal to key off, soofflineis reported when the platform stops considering the fan online, which is later than the moment they actually left.
Treat presence as a hint, not a ledger
Presence describes state that is true only for a moment, and webhook delivery retries. A redeliveredonline can arrive after the offline that superseded it. Build for that rather than assuming ordered, exactly-once delivery:
- Compare
changed_atagainst the state you already hold and discard anything older. Do not apply an event just because it arrived. - Reconcile from GET /creators//subscribers/online on reconnect, and periodically if presence drives anything a user sees. That endpoint is the source of truth; the event tells you when to look.
- Expect
offlineto be missed more often thanonline. Do not build a session timer that depends on receiving both halves.
Fan status resource
Fan object
handle and display_name are omitted from the payload rather than sent as null, so treat an absent key as “not resolved”, the same convention the message and payment events use. Both are public profile fields; no email is carried on any creator.* event.
The identity lookup is best-effort and runs after the delivery decision. A lookup failure omits
handle and display_name rather than delaying or dropping the event, so do not treat their absence as a change to the fan’s profile.Example: creator.fan.status_changed (mute)
Example: creator.fan.presence_changed (online)
Example: creator.fan.presence_changed (offline)
Identical apart from state. changed_at is when the platform stopped considering the fan online, not the moment they closed the app.