> ## Documentation Index
> Fetch the complete documentation index at: https://api.fanvue.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Creator Chat State

> Reference for the creator.chat.marked_unread inbox event: payload fields, pairing with creator.message.read, ordering, and deduplication.

`creator.chat.*` events report **conversation state** rather than message state. One event exists today:

| Event (`type`) | `data.object` | Fires when |
| - | - | - |
| `creator.chat.marked_unread` | chat\_unread\_marker | The creator deliberately marks a conversation unread |

It requires the `read:chat` scope, the same as the [message events](/docs/creator/messages), and is delivered in the [Standard-Webhooks envelope](/docs/creator/overview#event-envelope); the fields below describe the `data` object. It has no legacy flat equivalent.

<Warning>
  **No message's read state changes when this fires.** Marking a conversation unread flips a conversation-level flag; every message in the thread keeps the read status it already had.

  If you render read ticks from [`creator.message.read_by_fan`](/docs/creator/messages#read-by-fan-receipt-resource), **do not revert them** on this event. That is why this is a `creator.chat.*` event and not a `creator.message.*` one.
</Warning>

## The inbox badge has two halves

The Fanvue sidebar shows a conversation as unread when **either** of two things is true, and you need both events to mirror it:

| Half | Event | What it means |
| - | - | - |
| Set | `creator.chat.marked_unread` | The creator manually marked the conversation unread |
| Clear | [`creator.message.read`](/docs/creator/messages#read-receipt-resource) | The creator read the conversation, which also clears a manual marker |

Render a conversation as unread when the marker is set **or** `unread_messages_count` is above zero. Clearing a manual marker on a conversation with no unread messages emits `creator.message.read` with `read_messages_count: 0`, which is the marker being cleared rather than a no-op.

<Note>
  Subscribe to both. Taking only `creator.chat.marked_unread` gives you a badge you can never turn off.
</Note>

## Ordering the two halves

**The two halves are not ordered against each other.** Each event is published independently, so a mark and a read that happen close together can arrive in either order.

Compare the times on the payloads and apply whichever is later, rather than applying whichever landed last:

```ts theme={null}
// `marked_at` on creator.chat.marked_unread, `read_at` on creator.message.read.
const at = new Date(event.data.marked_at ?? event.data.read_at);
if (at <= conversation.inboxStateAt) return; // stale, ignore
conversation.inboxStateAt = at;
conversation.unread = event.type === "creator.chat.marked_unread";
```

Both times are stamped when the action was handled, not when the event was published, so they stay put across delivery retries. The envelope `timestamp` does not; do not order on it.

## Chat unread marker resource

| Field | Type | Description |
| - | - | - |
| `object` | string | Always `"chat_unread_marker"` |
| `marked_at` | string | ISO 8601 time the mark was handled. The stable ordering key, see [Ordering the two halves](#ordering-the-two-halves) |
| `unread_messages_count` | integer | The conversation's real unread message count, which the mark does **not** change. `0` is normal, see below |
| `fan` | object | The conversation counterpart, see [Fan object](#fan-object) |
| `creator` | object | `{ uuid }` of the creator who marked it unread |
| `metadata` | object | Reserved; **always `{}`** on this event |

The conversation is addressed by `fan.uuid`; there is no separate chat id, the same as on the [message events](/docs/creator/messages).

### `unread_messages_count` is usually 0

Marking a conversation unread does not create unread messages, so the count is whatever it already was, and in the common case, a creator flagging a chat they have already read so they remember to come back to it, that is `0`. **A `0` here does not contradict the event.** It is why the badge rule above is an `OR` rather than a count check.

The count is resolved best-effort when the event is built. If that lookup fails the event is still delivered, with `0`, rather than being dropped, so treat `0` as "no unread messages, or not resolved" and fall back to [`GET /chats`](/docs/api-reference/get-list-of-chats) if you need the exact number.

### Fan object

| Field | Type | Description |
| - | - | - |
| `uuid` | string | The counterpart's user UUID. This also addresses the conversation |
| `email` | null | **Always `null`**. Present for shape parity only; do not rely on it |
| `display_name` | string | The counterpart's display name. **Omitted** when the identity lookup does not resolve it |
| `handle` | string | The counterpart's public handle. **Omitted** when the identity lookup does not resolve it |

Same shape as the [Fan object](/docs/creator/messages#fan-object) on the message events, minus `avatar_url`. The identity lookup is best-effort and runs after the delivery decision, so an absent `handle` or `display_name` is a lookup that did not resolve, not a change to the fan's profile.

`creator` stays a bare `{ uuid }`. The creator is the account you already hold a token for, so this event does not restate their identity.

## Delivery

Each event reaches:

* apps holding `read:chat` for the creator, **and**
* the creator's connector destination, when one is subscribed.

The two destination kinds resolve independently: a failure to resolve one does not cost the other its delivery.

## What does not fire it

* **Marking a conversation that is already unread.** The event reports the transition into unread, not the action, so a repeat mark with no read in between emits nothing. Marking, reading, and marking again does emit twice, because the read cleared the marker in between.
* **A fan marking a creator's chat unread.** The event is creator-owned and reports the creator's own inbox, so a mark by a non-creator produces nothing, even though `read:chat` is a scope a fan's app can hold.
* **A conversation going unread because a new message arrived.** That is [`creator.message.received`](/docs/creator/messages), which already carries `unread_messages_count`. This event is the deliberate manual mark only.
* **Archiving or muting a conversation.** Muting is [`creator.fan.status_changed`](/docs/creator/fan-status); archiving emits nothing today.
* **Internal AI and support conversations**, which are never exposed to integrations.

## Deduplicating a mark

The event `id` is a hash of the two parties and `marked_at`, so it carries an occurrence component and **the id alone is a valid idempotency key**. A creator who marks a conversation unread, opens it, and marks it unread again gets two events with two ids, with no new message needed in between. See [Deduplicating events](/docs/webhooks/delivery-and-idempotency#deduplicating-events).

This event is not in the [read-receipt carve-out](/docs/webhooks/delivery-and-idempotency#read-receipts) that applies to `creator.message.read`.

## Example: `creator.chat.marked_unread`

A creator flagging a conversation they have already read:

```json theme={null}
{
  "id": "c93a1f7e5b2d48601a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f7081",
  "type": "creator.chat.marked_unread",
  "timestamp": "2026-06-09T08:52:17.418Z",
  "data": {
    "object": "chat_unread_marker",
    "marked_at": "2026-06-09T08:52:17.106Z",
    "unread_messages_count": 0,
    "fan": {
      "uuid": "fan-uuid",
      "email": null,
      "display_name": "Jane D",
      "handle": "janed"
    },
    "creator": { "uuid": "creator-uuid" },
    "metadata": {}
  }
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.