X-Autopilot

X Chat API: send encrypted DMs from code

The X Chat API is the only route to DMs now that conversations are encrypted. The key setup, the 500-a-day ceiling, and the errors that eat a weekend.

X-Autopilot Team··9 min read
On this page · 9 sections

The short version

  • ▸The X Chat API is a different API from the old DM endpoints, not a new version of them. GET /2/dm_events is blind to encrypted conversations, there is no documented way to downgrade a conversation back to plaintext, and the DM lookup docs still do not say so.
  • ▸X routes ciphertext only. Your process has to generate keypairs, register the public ones, wrap a conversation key for every participant, and sign every state-changing call. The Chat XDK does the crypto in Python, TypeScript, Rust, Go, C# and Java; nothing on X's side will do it for you.
  • ▸The ceiling is lower than most people plan for: 25 message sends per 15 minutes and 500 per 24 hours per app and per user. Public key registration is capped at 5 calls per 24 hours, so a bad key-setup loop locks you out for a day.
  • ▸Inbound events arrive through the X Activity API as chat.received and chat.sent, not through the legacy DM webhook product. A webhook subscribed the old way never fires, which is the single most common setup failure in X's own forum.
  • ▸Rotating a conversation key protects future messages only. Anyone holding an older key version can still read everything encrypted under it, and a key-change event signed by a member who later left the group stops verifying for good.

Quick answer

The X Chat API is the /2/chat endpoint family that sends and receives end-to-end encrypted direct messages on X (formerly Twitter). It went publicly available on 30 July 2026 alongside a client library called the Chat XDK, and a second batch of about twenty endpoints landed on 30 September 2026. It is not a new version of the old DM endpoints. It is a separate API with a separate encryption model, and if your DM code stopped seeing messages at some point in 2026, this is why.

Honest summary: sending an encrypted DM from code is now possible, the crypto is entirely your problem, and the hard ceiling is 500 outbound messages per 24 hours.

Why your old DM code sees nothing

X encrypts chat conversations. The v2 DM lookup endpoints, GET /2/dm_events and the two /2/dm_conversations/.../dm_events variants, only read plaintext ones. An encrypted thread does not error, it just comes back empty, which is the worst possible failure mode because it looks like you have a pagination bug.

A developer filed this on 12 February 2026, noting that the behaviour "isn't in the docs." An X staff member confirmed the next day that encrypted messages were not supported through the X API and that support was being explored. In the months between, people tried to force conversations back to plaintext and could not: a thread from 4 June 2026 describes a chatbot account whose conversations upgraded to encrypted mid-conversation with no way to stop it.

That gap is now closed by the Chat API rather than by a downgrade switch. As of today the DM lookup documentation still does not warn you that encrypted conversations are invisible to it, so the old endpoints remain a live trap for anyone starting from a 2024 tutorial.

What your process has to do

X holds ciphertext and nothing else, so six things that used to be X's job are now yours:

  1. Generate an identity keypair and a signing keypair, and store the private halves.
  2. Register the public keys with POST /2/users/:id/public_keys.
  3. Generate a conversation key, encrypt one copy per participant against their identity public key, and post the wraps.
  4. Encrypt and sign each outbound message, then send only the ciphertext.
  5. Receive ciphertext from webhooks, the activity stream, or event history.
  6. Verify and decrypt locally.

The Chat XDK does the cryptographic parts. It installs as pip install chatxdk on Python 3.10 and up, or npm install @xdevplatform/chat-xdk on Node 18 and up, with Rust, Go, C# and Java bindings alongside. Auth is OAuth 2.0 user context with dm.read, dm.write, users.read and tweet.read, plus media.write for attachments. There is no app-only shortcut: chat activity for an account needs that account's authorization. If you want a dedicated account to own the conversation rather than a human one, the X Bots API mints a project-owned account from a single call, and that post covers the message-request inbox problem a brand new bot hits on its first inbound DM.

For the user-facing half of the same system, including the PIN that unlocks a private key on a new device, XChat explained covers what people actually see.

The endpoint surface

That 30 September 2026 batch, shipped in API version 2.169, is what turns this from a messaging primitive into something you could build a product on.

AreaWhat you can now do
ConversationsDelete a conversation for yourself only, update a group description (client-encrypted)
AdminsList, request, approve and reject admin requests; grant and remove admin rights; restrict group actions to admins
InvitesEnable an invite link with an expiry, preview a group from a token, disable the link, handle join requests
MembersAdd members, remove members, leave by passing your own user ID
ControlsDisappearing messages with a TTL in milliseconds, mute and unmute, pinned messages, screenshot blocking and detection
Account settingsGET and PUT /2/chat/settings for who can message you, read receipts, the message quality filter and call settings

Two of those are worth flagging for anyone automating. PUT /2/chat/conversations/{id}/screen_capture toggles screenshot blocking and screenshot detection one setting per request, and in a group only admins can change them. And /2/chat/settings exposes the message quality filter, which is the setting most likely to be quietly deciding whether your outbound messages reach a stranger's primary inbox at all.

Rate limits are the real constraint

Here are the published limits from launch day, per app and per user:

Endpoint15 minutes24 hours
POST /2/chat/conversations/:id/messages25500
POST .../messages/delete25500
POST .../typing and .../read602,000
GET /2/chat/conversations/:id/events301,000
GET /2/chat/conversations and /:id501,000
POST /2/chat/conversations/group/initialize and /group550
POST .../keys and .../members10100
POST /2/chat/media/upload/initialize and /finalize15200
POST /2/users/:id/public_keys-5

Do the arithmetic before you design anything. 500 sends per 24 hours means a support bot can hold roughly 250 two-turn conversations a day and no more. Group creation at 50 per day rules out spinning up a group per customer at any scale. And the one to respect is the last row: five public-key registrations per 24 hours. A retry loop around key setup will exhaust that in seconds and lock your onboarding out until the window rolls. For how these dev-side numbers compare to what a logged-in account can do by hand, X's daily limits has the human-side figures.

What it costs

At launch X said the Chat API would be "free for a limited amount of time," and no end date has been published. That is not the same as a free DM bot, because the delivery and reply paths bill normally under pay-per-use. X's pricing page currently lists chat.received webhook deliveries at $0.010 each, chat.sent and chat.conversation_join as not billed, DM Event reads at $0.010 per resource, and DM Interaction: Create at $0.015 per request.

So the inbound side of a bot handling 1,000 messages a month costs about $10 in event delivery before you reply to anything. Worth reading alongside X API pricing, which covers the credit system and the free starter credits that offset early experiments.

Five errors that cost people a weekend

Your webhook never fires. Chat events come through the X Activity API as chat.received, chat.sent and chat.conversation_join, not through the legacy DM webhook flow. You register a webhook, pass the CRC challenge, then create subscriptions against it with POST /2/activity/subscriptions. X staff gave this exact diagnosis on 15 June 2026 to a developer whose endpoint was simply never called. The X Activity API post covers the subscription and CRC mechanics in full, including a billing correction that makes older cost estimates too low.

Every state-changing call returns 400. Adding or rotating conversation keys, creating a group and adding members all require an action_signatures array in the body, validated at the API boundary. Each entry needs message_id, encoded_message_event_detail and a message_event_signature. Use the SDK's prepare_* methods and send everything they return: group create and member adds return two signatures, and sending one of them is a 400.

Your local public key never matches the registered one. Comparing the Chat XDK's get_public_keys output to the API's public_key field as strings always fails for the same key. The API returns the DER encoding, which carries a fixed algorithm-identifier prefix; the SDK returns the raw key without it. Base64-decode both and check that the server bytes end with the local bytes. While you are there, compare public_key_version values numerically, not lexicographically, because they are millisecond timestamps of varying string length.

Old events fail verification forever. Signatures are rebuilt from the event itself, so an event signed over different bytes will not verify on any future load. No retry or key refresh heals it. Treat those as tombstones. In long-lived groups there is a second version of this: a key-change event signed by a member who has since left stops verifying because the public-keys endpoint no longer serves their keys, and the documented fallback is extract_conversation_keys, which recovers the key without the signature check.

Only the first message in a burst arrives. The 15 June 2026 thread reports batched inbound messages firing one event and later ones arriving late or not at all. Deduplicate deliveries on event_uuid, deduplicate messages on the signed message_id rather than on backend sequence IDs, and reconcile against GET /2/chat/conversations/:id/events instead of trusting the live stream to be complete.

What the encryption does not do

X cannot read message bodies, and that is a real property. Three things it does not give you:

  • No backward protection on rotation. Rotating a conversation key protects future messages only. Anyone already holding an older key version can still read everything encrypted under it. X's own group documentation says this plainly.
  • No metadata privacy. Who messaged whom, and when, is not encrypted.
  • No published third-party audit. Independent researchers have flagged the absence of forward secrecy and the fact that key-storage realms sit on X-controlled infrastructure. Treat this as better than plaintext DMs rather than as an audited messenger, and read the security section of XChat explained before you promise a client otherwise.

There is also a policy layer that the API does not enforce for you. Automated messaging sits under X's automation rules, which prohibit unsolicited bulk or aggressive messaging regardless of which endpoint sends it. Having an API key is not the same thing as having permission to message people who did not ask, and nothing here should be read as a compliance opinion on your specific use case.

Should you build on it

Build on it if encrypted DMs are the product: a support desk, a concierge agent, a two-way notification channel for people who have opted in. The endpoint surface after 30 September 2026 is genuinely complete enough, and the Chat XDK removes the part you would otherwise get wrong.

Think twice if your plan needs volume. 500 sends a day is a product constraint, not a soft limit, and the key-management burden is permanent: private keys on your infrastructure, rotation handling, and a verification path that fails closed by design. If you are starting from zero, how to get an X API key is the prerequisite step, and if what you actually want is scheduled posting and replies from your own account rather than an encrypted inbox, X-Autopilot runs on your Mac in your own browser session, which keeps a lower detection surface than cloud tools that act from a shared IP pool.

One thing not to do: start from an old DM tutorial. Everything written before 30 July 2026 about building a DM bot on X describes an API that can no longer see the conversations it was built for.

Frequently asked

Answers indexed by Google + AI assistants.

What is the X Chat API?+

It is the X API v2 endpoint family under /2/chat that sends and receives end-to-end encrypted direct messages. Message bodies are encrypted and signed on your client, X stores and routes only ciphertext, and a companion library called the Chat XDK handles key generation, encryption, decryption and signature verification. It went publicly available on 30 July 2026.

Why does /2/dm_events return nothing for my conversation?+

Because the conversation is encrypted. The v2 DM lookup endpoints only see plaintext conversations, so an encrypted thread looks empty rather than returning an error. An X staff member confirmed on 13 February 2026 that encrypted XChat messages were not supported through the DM endpoints, and no downgrade path to plaintext has been documented since.

Can a bot account use the Chat API?+

Yes, and X ships a bots endpoint family for exactly that. The catch is auth: Chat endpoints use OAuth 2.0 user context with dm.read and dm.write, so you need the authorization of whichever account the messages belong to. A bot token is not interchangeable with a user token on every chat and activity route.

How many messages can I send through the X Chat API per day?+

500 per 24 hours on POST /2/chat/conversations/:id/messages, with a shorter 25 per 15 minutes window, applied per app and per user. Typing and read-receipt calls get 2,000 per 24 hours. Conversation reads get 1,000. Those were the published limits at launch on 30 July 2026.

What does the X Chat API cost?+

X said at launch that the Chat API would be free for a limited amount of time and has not published an end date. The surrounding costs are real now: the pricing page bills chat.received webhook deliveries at $0.010 each, lists chat.sent as not billed, and prices DM Interaction: Create at $0.015 per request.

Why does my webhook never receive encrypted chat messages?+

Chat events are delivered by the X Activity API, not by the older DM webhook flow. You subscribe to chat.received and chat.sent through /2/activity/subscriptions against your registered webhook, or poll GET /2/activity/stream. X staff gave exactly this answer to a developer on 15 June 2026 whose webhook was never called.

Sources
Related searches
x chat apix chat api encrypted dmtwitter dm api not workingchat xdk pythonx api send direct messagex chat api rate limitsdecrypt xchat webhook payloadx chat api conversation keyxchatx activity apix bots apidm automationend to end encryptionx api pay per useoauth 2.0 user context
DY
Deepak YadavBuilding X-Autopilot

Product designer and indie hacker. Runs the agent on his own X account every day and writes up what the data shows, including when it's inconvenient.

Follow on X →
Try X-Autopilot.
$199 once. No subscription, no monthly bill. Real Chrome on your Mac.
See pricing▶
Free PDF · the X Growth Playbook

The exact playbook we use to grow on X

The bio that converts, the daily reply loop, the posting cadence, and the tool stack, in one no-fluff PDF. Drop your email and it's yours.

No spam. Unsubscribe anytime.

Free tools

Try it yourself.

All free X tools →
Keep reading

Related posts.