---
title: "Read a conversation"
method: POST
path: "/client/dm/conversation"
tags: ["Account actions"]
---

# Read a conversation

`POST /client/dm/conversation`

## Request

- Base URL: `https://api.twittrapi.com`
- URL: `https://api.twittrapi.com/client/dm/conversation`
- Auth: API key in header `X-API-KEY`

Messages of one conversation, decrypted, newest first, with a cursor for older messages.

Paginated: pass the bottom cursor of one page as cursor to get the next. Each page is one billed call.

Reference: https://www.twittrapi.com/docs/account-actions/client-dm-conversation

## Query parameters

- `format` 'raw' | 'simple'

## Request body

- object
  - `cookie` string, required: Cookie header of your own logged-in X session. Must contain auth_token, ct0 and twid (your account ID, set by x.com at login); guest_id is used when present.
  - `proxy` string, required: Your proxy, format http://user:pass@host:port. The requests to X go out through it. Used only for this request.
  - `passcode` string, required: Your X Chat passcode, the one you set up for encrypted messages on X. Send it as a string (a number works too, but loses leading zeros), up to 64 characters. It unlocks your chat keys for this request and is not saved. The unlocked keys stay in the server's memory for up to 30 minutes, so follow-up calls need no new unlock.
  - `conversation_id` string: ID of the conversation, as returned by /client/dm/inbox. A one-to-one chat has the ID <smaller user ID>:<larger user ID>. Letters, digits, colon, hyphen and underscore, up to 128 characters. Either conversation_id or user_id is required.
  - `user_id` string: Numeric ID of the other person (up to 20 digits) for a one-to-one chat. Used when conversation_id is not set.
  - `cursor` string: cursor from the previous page (digits only, up to 20) to get older messages. Leave it out for the newest page.

## Response `200`

{conversation_id, messages, has_more, cursor, message_request_state, undecrypted, events}. messages are newest first; cursor is set when has_more is true. undecrypted lists events the API could not read ({id, sender_id, error}; for example data x.com keeps in your own conversation), events the other decrypted events (read receipts, settings and key changes). Each message: id (sequence ID, also the cursor unit), message_id, conversation_id, sender_id, created_at and expires_at (Unix milliseconds; expires_at only in chats with disappearing messages), type (text, reaction, reaction_removed, edit, deleted, ...), text, verified (the sender's signature was checked) and content (the decrypted message as X's chat library returns it: reactions, replies, attachments). Expired disappearing messages are left out.

With format=simple: Collection of Message objects {id, senderId, createdAt, expiresAt, type, text, emoji, targetId, replyTo {id, senderId, text}, attachments [{kind, type, filename, filesizeBytes, durationMs, width, height, url, mediaHashKey}], verified}; times are RFC 3339, unknown values null. nextCursor is the cursor for older messages, null on the last page.

- RawPassthrough: X's own JSON (raw format, the default), passed through unchanged. The shape is X's and can change; see https://www.twittrapi.com/docs/response-format.

## Other responses

- `400`: The body is not valid JSON.
- `401`: Missing or unknown API key. Not charged.
- `402`: No balance left, or the trial expired. Not charged. Top up in the dashboard.
- `403`: Key deactivated, or the endpoint is not in your plan (trial keys and account actions). Not charged.
- `407`: The proxy is unreachable or timed out.
- `422`: cookie or proxy is missing, the cookie has no twid or ct0, or the passcode is neither a string nor a whole number or longer than 64 characters. The passcode is missing, neither conversation_id nor user_id is set, user_id is not numeric, conversation_id or cursor has the wrong format. Also: the account is suspended, or X rejected the session ("Twitter temporarily rejected this session; refresh the login and try again"). Wrong passcode: "Wrong passcode (N attempts left before X deletes the chat keys)". X deletes the chat keys after too many wrong passcodes, so the API never sends the same wrong passcode to X twice (for 24 hours it answers 422 at once), and once only 3 attempts are left it does not try at all: "Only a few passcode attempts are left for this account; unlock Chat in the X app with the correct passcode first". The account has no chat passcode yet: open Chat on x.com once and set one up.
- `429`: X's rate limit for this account: "X rate limit reached for this account, try again later".
- `500`: Something failed on our side or between us and X. Refunded (400, 407, 408, 409 or a 5xx error are refunded). Retry with backoff.
- `501`: Encrypted messages are not available on this server (the passcode cannot be used). Refunded.
- `502`: The chat keys could not be unlocked ("Could not unlock the chat keys, try again") or the messages could not be decrypted. Refunded.
- `default`: X refused the request: answered with X's status and {"error": "X refused the request: <X's reason>"}. When X answers 200 with an error, the status is 422.

---

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