> ## 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.

# Working with Chats and Messages

> Read a chat correctly: tell pay-to-view, tips, and automated messages apart, check purchase and read status, and attribute a message to a team member.

If you are building a chat or messaging surface on top of Fanvue, most of what you need is in one place: the message object returned by `GET /chats/{userUuid}/messages`. This guide explains the fields that matter and how to answer the questions that come up most.

<Tip>Keeping chats in sync? Don't re-poll this endpoint per chat — receive messages in real time with webhooks and pull deltas in bulk. See [Efficient Chat Sync](/docs/tutorials/efficient-chat-sync).</Tip>

## The message object

Each message in the `data` array carries these fields:

| Field                                   | What it tells you                                                                                  |
| --------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `uuid`                                  | Stable ID for the message.                                                                         |
| `text`                                  | The message body. Can be null (for example a media-only message).                                  |
| `sentAt`                                | When it was sent.                                                                                  |
| `sender` / `recipient`                  | Each has a `uuid` and a `handle` (the user's @ handle).                                            |
| `hasMedia` / `mediaType` / `mediaUuids` | Whether the message has attachments, their type, and their ordered IDs.                            |
| `type`                                  | What kind of message this is (see below).                                                          |
| `pricing`                               | Present for pay-to-view messages. `pricing.USD.price` is **in cents**. Null for ordinary messages. |
| `purchasedAt`                           | Timestamp when the fan purchased this message, or null if not purchased.                           |
| `isRead`                                | Whether the recipient has read the message.                                                        |
| `sentByUserId`                          | The team member who sent it on the creator's behalf, or null if the creator sent it directly.      |

## Tell message types apart

The `type` field distinguishes ordinary messages, tips, and the platform's automated messages. The values are:

* **Ordinary messages:** `SINGLE_RECIPIENT`, `CHAT_TEXT_REPLY`, `CHAT_TEXT_GENERATION`, `CHAT_TEXT_REWRITE`.
* **Tips:** `TIP`.
* **Voice calls:** `VOICE_CALL`.
* **Automated messages:** `AUTOMATED_FIRST_MESSAGE_REPLY`, `AUTOMATED_NEW_SUBSCRIBER`, `AUTOMATED_NEW_FOLLOWER`, `AUTOMATED_NEW_PURCHASE`, `AUTOMATED_RE_SUBSCRIBED`, `AUTOMATED_RENEWED`, `AUTOMATED_CANCELED`, `AUTOMATED_CHAT_MESSAGE_REPLY`.

So:

* A **tip** is `type === "TIP"`.
* A **pay-to-view (PPV) message** has a non-null `pricing` (and usually `hasMedia: true`).
* An **ordinary message** is one of the text types with `pricing: null`.
* An **automated or welcome message** carries one of the `AUTOMATED_*` types. The automatic messages a fan receives (for example after subscribing or following) surface here, so you can filter them out of, or single them out from, a human conversation by checking for an `AUTOMATED_` prefix.

## Has the fan paid for a PPV message?

Use `purchasedAt`. It is the timestamp the fan purchased the message, or null if they have not. So:

* `purchasedAt` is set: the fan has unlocked and paid for it.
* `purchasedAt` is null on a message with `pricing`: it is still awaiting purchase.

<Tip>Render "Paid" when `purchasedAt` is present and "Awaiting purchase" when a priced message has a null `purchasedAt`. Remember `pricing.USD.price` is in cents, so divide by 100 for display.</Tip>

## Has the message been read?

Each message has an `isRead` boolean, so you can show read state (the "two ticks" pattern) directly from the message list. For a live surface that updates as soon as a fan reads a message, subscribe to the [message-read webhook](/docs/webhooks/message-read) rather than polling.

## Which team member sent a message?

If an agency or team operates the account, `sentByUserId` is the UUID of the team member who sent the message on the creator's behalf (null when the creator sent it directly). This is what you use to attribute a message, and any purchase it drove, to a specific chatter.

## Sending a message

Send with `POST /chats/{userUuid}/message`. The body accepts `text`, `mediaUuids`, `price`, and `templateUuid`. To send pay-to-view media, include the `mediaUuids` you are selling and a `price`. To resolve and display the media you receive or send, see [Working with Media](/docs/tutorials/working-with-media).

### Pay-to-view price limits

`price` is in **cents**. The allowed range for a chat pay-to-view message is:

| Bound   | Value                 | Applies to                                                         |
| ------- | --------------------- | ------------------------------------------------------------------ |
| Minimum | `300` (\$3.00)        | Every creator                                                      |
| Maximum | `50000` (\$500.00)    | Every creator, by default                                          |
| Maximum | `250000` (\$2,500.00) | Only creators whose fan spending limits have been lifted by Fanvue |

<Warning>
  The send endpoint validates the **minimum only**. A `price` above the creator's
  ceiling is accepted, and the message is delivered looking like a normal
  pay-to-view — but the purchase is rejected when the fan tries to pay, so the
  message can never be unlocked and the creator earns nothing.

  Enforce the \$500 ceiling in your own code before you send. Do not rely on the
  API to reject an over-ceiling price; validating it up front is a pending
  platform fix.
</Warning>

<Note>
  $2,500 is frequently quoted as the chat pay-to-view maximum. It is not the
      default — it applies only to creators Fanvue has explicitly exempted from fan
      spending limits. Assume $500 unless you have confirmed otherwise for that
  specific creator. There is no API field that exposes which ceiling applies.
</Note>

## A note on user names

Two name fields appear throughout the API, and they are not the same thing:

* `handle` is the user's unique @ handle.
* `displayName` is the changeable display name a user shows publicly.

When you need a stable identifier use the `uuid`. When you show a name, use `displayName`, and fall back to `handle`. Both appear on the authenticated user (`GET /users/me`) and on the `sender` and `recipient` of every message.
