---
title: "Mark a message read or unread"
method: PATCH
path: "/messages/{id}"
tags: ["messages"]
---

# Mark a message read or unread

`PATCH /messages/{id}`

## Request

- Base URL: `https://dash.mailsocket.app/api/v1`
- URL: `https://dash.mailsocket.app/api/v1/messages/{id}`
- Auth: HTTP bearer

Sets the message's `read` state. Setting `read: true` records
`read_at` (the first time only; a repeat `read: true` is a no-op, not
an error). Setting `read: false` clears `read_at`. Idempotent either
way. Returns the full updated message.

## Path parameters

- `id` string, required

## Request body

- object
  - `read` boolean, required

## Response `200`

The updated message.

- object
  - `data` Message, required: Full message representation: `MessageSummary` minus `has_html`, plus `envelope_from`, `envelope_recipient`, `text` and `html`.
    - `id` string, required
    - `inbox_id` string, required
    - `from` string, required: The From header.
    - `to` string[], required
    - `cc` string[], required
    - `subject` string, required
    - `received_at` string, date-time, required
    - `status` 'received' | 'parsing' | 'parsed' | 'failed', required
    - `otp` string, nullable
    - `otp_confidence` number, nullable
    - `magic_link` string, nullable
    - `links` string[], nullable
    - `detected_otp` string, nullable
    - `otp_context` string, nullable: A short (≤160 character) plain-text snippet around `otp`; null when no OTP or not locatable.
    - `otp_candidates` object[], nullable: Ranked plausible codes beyond the primary `otp`; null/empty when only one or none.
      - `code` string, required
      - `confidence` number, required
    - `provider_hint` string, nullable: Best-effort sending-service label from the From domain; null when unknown.
    - `envelope_from` string, required
    - `envelope_recipient` string, required
    - `text` string, required: The plain-text body.
    - `html` string, required: The sanitized HTML body.
    - `read` boolean: Whether the message has been marked read (`read_at IS NOT NULL`).
    - `metadata` object, required: The owning inbox's `metadata` (`{}` when unset). Present in poll/latest/wait/batch fetch AND the webhook delivery payload (which is this exact schema).

## Other responses

- `401`: Missing or invalid API key.
- `403`: Account is not permitted to use the API.
- `404`: Resource not found (unknown or not owned).
- `422`: Request validation failed (malformed body or invalid parameter).
- `429`: Rate limit exceeded.

---

[API](https://skmtc.dev/mailsocket/apis/mailsocket-api.md) · [All operations](https://skmtc.dev/mailsocket/apis/mailsocket-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mailsocket/apis/mailsocket-api/revisions/e579500dfb7e?raw)
