# Track read and unread mail

Use an optional read label to find unread messages, mark mail read, and mark it unread again through HTTP or MCP.

Source: https://cherami.to/docs/guides/read-unread



Use a `read` label to keep a shared record of which messages you have read. Cherami has no built-in read state: this is an optional convention, and fetching content never changes labels.

Under this convention, a message with `read` is read; one without it is unread. You do not need a separate `unread` label. Newly arrived unlabeled mail qualifies as unread, as does older mail without `read`, even if someone previously fetched it. Cherami cannot reconstruct past reading activity.

## Find unread messages [#find-unread-messages]

For the curl examples, set `CHERAMI_API_KEY` to your existing API key and `INBOX_ID` to the inbox you want to check. If you have not connected yet, follow the [HTTP quickstart](https://cherami.to/docs/quickstart) or [MCP connection guide](https://cherami.to/docs/mcp).

```bash
curl --get "https://cherami.to/v1/inboxes/$INBOX_ID/messages" \
  -H "Authorization: Bearer $CHERAMI_API_KEY" \
  --data-urlencode 'labels_none=read' \
  --data-urlencode 'limit=20'
```

With MCP, call `list_messages` with these arguments, replacing `INBOX_ID` with your inbox ID:

```json
{"inbox_id":"INBOX_ID","labels_none":["read"],"limit":20}
```

The returned `messages` are the newest matching received messages. An empty array means no messages match at that moment. Follow `next_cursor` with the same filter to see additional pages; start each new check without a cursor. Results are a live view, so arrivals and label changes can affect later pages.

For an unread count under this convention, use the same filter on the count endpoint:

```bash
curl --get "https://cherami.to/v1/inboxes/$INBOX_ID/messages/count" \
  -H "Authorization: Bearer $CHERAMI_API_KEY" \
  --data-urlencode 'labels_none=read'
```

The MCP equivalent is `count_messages`:

```json
{"inbox_id":"INBOX_ID","labels_none":["read"]}
```

The returned `count` includes received messages without `read`. Without the filter, it counts all received messages in the inbox.

## Read a message and mark it read [#read-a-message-and-mark-it-read]

Set `MESSAGE_ID` to an ID from the listing and fetch its content:

```bash
curl "https://cherami.to/v1/messages/$MESSAGE_ID" \
  -H "Authorization: Bearer $CHERAMI_API_KEY"
```

With MCP, call `get_message`:

```json
{"message_id":"MESSAGE_ID"}
```

Check `processing_status` before treating content as available. If preparation is unfinished or failed, follow the [receiving guidance](https://cherami.to/docs/guides/receiving#wait-for-content-not-just-receipt).

After reading, explicitly add the label:

```bash
curl --request PATCH "https://cherami.to/v1/messages/$MESSAGE_ID" \
  -H "Authorization: Bearer $CHERAMI_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"add_labels":["read"]}'
```

With MCP, call `update_message_labels`:

```json
{"message_id":"MESSAGE_ID","add_labels":["read"]}
```

Both return the message ID and its resulting labels. For a previously unlabeled message:

```json
{"id":"22222222-2222-4222-8222-222222222222","labels":["read"]}
```

Other labels are preserved. The message no longer matches `labels_none: ["read"]`.

## Mark it unread again [#mark-it-unread-again]

Remove `read` from the same message:

```bash
curl --request PATCH "https://cherami.to/v1/messages/$MESSAGE_ID" \
  -H "Authorization: Bearer $CHERAMI_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"remove_labels":["read"]}'
```

With MCP, call `update_message_labels`:

```json
{"message_id":"MESSAGE_ID","remove_labels":["read"]}
```

The resulting `labels` array no longer contains `read`, so the message appears in the unread view again.

## Agree on what read means [#agree-on-what-read-means]

Labels are shared across the account, not private to a person, agent, or connection. If one agent adds `read`, every connection using this convention sees that message as read. The label belongs to the individual message, not its whole conversation; replies do not inherit it.

Track completed work separately from reading. A `read` label neither authorizes action nor prevents another agent from handling the same message.

For other label conventions and batch workflows, see [Organize mail with labels](https://cherami.to/docs/guides/labels). Exact update rules and filters live in the [labels reference](https://cherami.to/docs/api/labels). For repeated checks, keep polling bounded as described in [Receive and poll for mail](https://cherami.to/docs/guides/receiving).
