X (Twitter) API Setup Support

The app permission panel says Read and Write. The OAuth 2.0 authorization screen was configured with dm.write. The call to send a Direct Message still comes back with an insufficient-scope error.

This is one of the most common stalls on the X API, and it is caused by a single line in X’s own documentation that almost nobody reads before requesting scopes: dm.write does not work alone.

The rule that trips most first integrations

X’s official scope table for Direct Messages lists dm.read as “required with dm.write.” A token authorized for dm.write alone can pass every other check — app approved, correct endpoint, correct body — and still fail, because the authorization screen the user consented to never included the read scope the write action depends on.

Fixing this after launch means re-authorizing every existing user, not just shipping a code change. It is far cheaper to get the scope list right before the first user connects their account.

This page covers how X structures Direct Message access — the endpoint set, the one-to-one vs. group conversation model, the full required-scope list, and the points where DM integrations commonly stall. It is written for teams building inbox, CRM, or social-support tools on the X API.

Two conversation types, not one generic thread

X's Direct Message model splits conversations into two distinct types with different rules, and the API expects you to know which one you are working with before you call it.

One-to-one

Always exactly two participants. Sent through a dedicated participant-based endpoint, or reused once you have its conversation ID.

Group

Two or more participants, and membership can change over time. Created explicitly with a conversation_type flag and an initial participant list.

dm_conversation_id

Identifies the thread itself. Every conversation — one-to-one or group — gets one, and it is what you save to add further messages later.

dm_event_id

Identifies one specific action inside a thread, such as a single sent message. This is the ID a delete call targets, not the conversation ID.

The endpoint set

Sending and managing messages is spread across a small number of endpoints, and picking the wrong one for the situation is a common early mistake.

Method & endpointWhat it does
POST /2/dm_conversationsCreates a new conversation — this is the group-conversation path, with the first message sent in the same call
POST /2/dm_conversations/with/:participant_id/messagesSends to a one-to-one conversation, starting one if it doesn't exist yet
POST /2/dm_conversations/:dm_conversation_id/messagesAdds a message to an existing conversation by ID — works for both one-to-one and group threads
DELETE /2/dm_events/:idDeletes a specific message event

Delete is restricted, and this catches moderation and support tools by surprise. X's documentation is explicit: you can only delete messages you sent, never a message from another participant in the thread. A support-inbox feature that assumes it can remove any message in a conversation will fail on every message it didn't send itself.

The full scope list

Getting a token authorized with the right scopes at the start avoids re-consent later. X's documentation lists four scopes as relevant to the Direct Message endpoints:

ScopeWhat it covers
dm.writeSend and delete messages
dm.readRead conversations — required together with dm.write
tweet.readNeeded for some response expansions
users.readNeeded for user-object expansions

The prerequisites go beyond scopes: an approved developer account with an approved App, a Project configured in the Developer Console, and a User Access Token obtained through OAuth 2.0 with PKCE. An app-only Bearer Token is not the credential path X documents for these endpoints — the flow is built around a token tied to a consenting user.

Where these builds stall

  • Requesting dm.write without dm.read. The token authorizes, the send call still fails, and the fix requires re-consent from every already-connected user — not just a redeploy.
  • Using the wrong endpoint for the conversation type. The participant-based endpoint assumes one-to-one; a group needs the explicit conversation_type and participant_ids path instead.
  • Treating dm_event_id and dm_conversation_id as interchangeable. A delete call needs the event ID of the specific message, not the ID of the thread it lives in.
  • Assuming a media attachment can be referenced before the upload finishes. The attachment field expects a completed media_id from a separate upload call, not a file sent inline with the message.
  • Building moderation tooling around deleting any message in a thread. Only messages sent by the authenticated app's own user can be deleted through this API.
  • Not budgeting for the DM-specific rate ceiling. Direct Message endpoints carry their own limits, separate from posting limits — see the per-user DM lookup figure in our X API 429 rate-limits breakdown.

What a correctly scoped DM integration looks like

One consent screenEvery scope the DM flow needs is requested up front, with no silent re-authorization later
Conversation-type awareOne-to-one and group threads are created and addressed through the correct endpoints from day one
ID disciplineConversation IDs and event IDs are never used interchangeably in send, fetch, or delete calls

Getting this right before users connect their accounts

The individual pieces here — four scopes, four endpoints, two conversation types — are not complicated on their own. What makes X Direct Message integrations expensive to fix later is timing: the scope list is decided at the OAuth consent screen, before a single user has connected an account, and every scope missed there has to be recovered through mass re-authorization rather than a code change.

I provide setup, review-preparation and configuration support for X (Twitter) API integrations, including scope planning, endpoint selection and OAuth flow review before your first users connect. This sits alongside the platform's broader OAuth 2.0 PKCE setup and the distinction between OAuth scopes and app permission levels covered elsewhere on this site.

Scopes, endpoints and rate limits on the X API change frequently and must be confirmed against the official documentation and your developer console at the time you build. This page is technical setup and configuration support — no specific outcome or timeline can be promised.