Configure X (Twitter)

View .md

Complete BYOK setup for X: create the developer app, configure OAuth 2.0 with PKCE, copy the correct client credentials and test media publishing.


Dravo uses X API v2 with an OAuth 2.0 user access token. Authorization Code with PKCE identifies the user, while offline.access provides the refresh token needed for a persistent connection.

Platform configuration verified against X's official documentation on June 24, 2026.

Configuration summary

SettingValue
Dravo platformx
Developer consolehttps://console.x.com
OAuth flowOAuth 2.0 Authorization Code with PKCE
App typeWeb App, Automated App or Bot
App permissionsRead and write
Callbackhttps://api.dravo.dev/v1/oauth/x/callback
CredentialsOAuth 2.0 Client ID and Client Secret
Required scopestweet.write, tweet.read, users.read, offline.access, media.write
Analytics scopesNo extra Dravo scope; post and account metrics use tweet.read and users.read with user context
BillingX API pay-per-usage credits

The callback is fixed for every Dravo customer. Do not replace api.dravo.dev with your product domain.

Step 1: Create an X developer account

1.1 Enroll in the Developer Console

Navigation: X Developer ConsoleSign in.

  1. Sign in with the X account that should own the developer organization.
  2. Accept the Developer Agreement and platform policies.
  3. Complete the requested use-case description and organization details.
  4. Confirm that the Developer Console dashboard opens successfully.

Use an organizational account and retain a second administrator where X allows it. Losing access to the owner account can block credential rotation.

1.2 Configure API credits

Navigation: Developer Console → Billing or Credits/Usage in the current console navigation.

  1. Add a payment method when required.
  2. Purchase or enable enough API credits for create-post and media operations.
  3. Set a spending limit and low-balance alert.
  4. Remember that successful OAuth does not prove that paid API calls are funded.

X documents the self-serve API as pay-per-usage. Old Free/Basic/Pro tier tutorials do not describe the current credit model.

Step 2: Create the X app

2.1 Create an app

Navigation: Developer Console dashboard → New App.

  1. Choose New App.
  2. Enter a name that distinguishes development from production.
  3. Describe the user-authorized social publishing use case.
  4. Associate the app with the correct project/organization if the console asks.
  5. Finish creation and open the app overview.

2.2 Identify the two credential families

Navigation: App → Keys and tokens.

X can show several unrelated credentials:

Console labelProtocolUsed by Dravo
API Key and API Key SecretOAuth 1.0aNo
Bearer TokenApp-only OAuth 2.0No
Access Token and Access Token SecretOAuth 1.0a user contextNo
OAuth 2.0 Client ID and Client SecretOAuth 2.0 user contextYes

Do not copy anything yet. The OAuth 2.0 Client Secret is available only after the confidential client type is configured in Step 3.

Step 3: Configure OAuth 2.0 and the callback

3.1 Open User authentication settings

Navigation: Developer Console → App → Settings → User authentication settings → Set up. If authentication already exists, the button is labelled Edit.

  1. Enable OAuth 2.0.
  2. Leave OAuth 1.0a disabled unless another integration explicitly needs it.
  3. Continue to the app type and permissions section.

3.2 Select a confidential app type

Under Type of App, select Web App, Automated App or Bot. X classifies this as a confidential client and issues a Client Secret. Do not select Native App or Single Page App; those are public clients.

Dravo still performs PKCE even though it is a confidential server-side client.

3.3 Enable write access

Under App permissions, select Read and write.

  1. Do not leave the app on Read.
  2. Direct Message permission is not required by Dravo.
  3. Save the setting before copying credentials.

This console choice makes write access possible. Dravo then asks the user for the exact scopes below during OAuth:

ScopeWhy Dravo needs it
tweet.writeCreate posts.
tweet.readRead post resources required by user-context API operations.
users.readResolve /2/users/me, username and avatar.
offline.accessReceive a refresh token.
media.writeUpload images and videos.

There is no free-form scope box in Dravo. Existing tokens must reconnect after any permission or scope change.

3.4 Add the callback and website URL

In User authentication settings, locate App info:

  1. Under Callback URI / Redirect URL, add:

    TEXT
    https://api.dravo.dev/v1/oauth/x/callback
  2. Under Website URL, enter the public website for your product. This is not the OAuth callback.

  3. Add Terms of Service and Privacy Policy URLs when the console displays them.

  4. Choose Save and reopen the page to confirm the callback remains present.

X requires an exact callback match. Do not add a wildcard, query string, fragment or your frontend return_url.

Step 4: Copy the OAuth 2.0 client credentials

4.1 Open Keys and tokens

Navigation: Developer Console → App → Keys and tokens → OAuth 2.0 Client ID and Client Secret.

  1. Find the Client ID heading and copy its value.
  2. Copy the Client Secret shown under it.
  3. If the secret is no longer visible, choose Regenerate and copy the new value immediately.
  4. Keep the Client ID and Secret together; they must belong to the same app.

4.2 Reject the wrong credentials

Before leaving X, verify that you did not copy:

  1. API Key.
  2. API Key Secret.
  3. Bearer Token.
  4. Access Token.
  5. Access Token Secret.

Those values belong to different authentication modes and cause invalid_client in Dravo's OAuth 2.0 callback.

Step 5: Save the X app in Dravo

5.1 Save it from the dashboard

Navigation: Dravo Dashboard → Connections → OAuth apps → Add OAuth app.

  1. Select X (Twitter).
  2. Enter a recognizable name such as Production X App.
  3. Paste the OAuth 2.0 Client ID into Client ID.
  4. Paste the OAuth 2.0 Client Secret into Client Secret.
  5. Confirm the fixed callback and all five scopes displayed by Dravo.
  6. Save the OAuth app.

5.2 Save it through the API

Endpoint: POST /v1/oauth-apps.

Shell
curl -X POST https://api.dravo.dev/v1/oauth-apps \
  -H "Authorization: Bearer $DRAVO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "x",
    "name": "Production X App",
    "client_id": "OAUTH_2_CLIENT_ID",
    "client_secret": "OAUTH_2_CLIENT_SECRET",
    "allowed_return_urls": [
      "https://app.acme.com/settings/connections"
    ]
  }'

The response must show the callback from Step 3 and all five scopes. Dravo encrypts the secret and never returns it.

Step 6: Connect an X account

6.1 Connect from the dashboard

Navigation: Dravo Dashboard → Connections → Connected accounts → Connect account.

  1. Select X (Twitter).
  2. Choose the OAuth app saved in Step 5.
  3. Choose Connect with OAuth.
  4. Sign in to the X account that should publish.
  5. Review the requested permissions and authorize the app.
  6. Allow X to return the browser to Dravo.

6.2 Connect from your backend

Endpoint: GET /v1/oauth/x/start.

Shell
curl --get "https://api.dravo.dev/v1/oauth/x/start" \
  -H "Authorization: Bearer $DRAVO_API_KEY" \
  --data-urlencode "oauth_app_id=YOUR_OAUTH_APP_ID" \
  --data-urlencode "return_url=https://app.acme.com/settings/connections"

Redirect the browser to authorize_url. Dravo creates and stores the PKCE verifier automatically; your application never handles it.

Step 7: Verify text and media publishing

7.1 Verify the identity and token

Dashboard: Dravo Dashboard → Connections → Connected accounts. API: GET /v1/accounts?platform=x.

  1. Confirm the username and X user id are correct.
  2. Confirm connection_type is oauth.
  3. Confirm oauth_app_id identifies the intended X app.
  4. Confirm the stored scopes include all five values, especially offline.access and media.write.
  5. Confirm token health is active.

7.2 Publish a text-only post

Navigation: Dravo Dashboard → Playground.

  1. Select only the new X account.
  2. Publish a short text post.
  3. Confirm the post appears on the intended X profile.
  4. If it fails with payment/access errors, inspect credits before changing OAuth.

7.3 Publish an image post

  1. Use a small public image or upload/import one through Dravo media.
  2. Publish it with a short caption.
  3. Confirm the media upload and post creation both complete.

Text and media use different X API operations. A text success does not prove that media.write or media credits are ready.

Step 8: Prepare the app for production

8.1 Separate development and production apps

  1. Keep a dedicated production X app and Dravo OAuth-app record.
  2. Register the same fixed Dravo callback on that production app.
  3. Repeat Steps 3–7 with the production Client ID and Secret.
  4. Reconnect every account after moving it to a different X app.

8.2 Configure billing safeguards

Navigation: Developer Console → Billing/Credits and Usage.

  1. Confirm create-post and media usage is funded.
  2. Set a spending ceiling and low-credit alert.
  3. Monitor response headers and the console for rate limits.
  4. Test the expected daily volume before onboarding users.
  1. Rotating the Client Secret requires updating the Dravo OAuth app.
  2. Adding a scope or changing Read/Write permissions requires reconnection.
  3. Revoking the X app invalidates every account connected through it.
  4. Keep a visible reconnect path in your product.

Token and PKCE behavior

Dravo generates an S256 PKCE verifier/challenge for every authorization and validates the returned state. There is no PKCE setting to paste into X.

X documents a default OAuth 2.0 access-token lifetime of two hours. offline.access causes X to issue a refresh token; Dravo stores it and rotates it when X returns a replacement. Without that scope the account quickly needs reconnection.

Tokens already issued do not gain new scopes. Disconnect and reconnect after adding media.write, enabling write access or changing the app configuration.

Supported features

ContentCurrent supportNotes
TextYesCreated with X API v2.
ImagesYesUp to 4 images (JPEG/PNG/WEBP/GIF) attached to one post.
VideoYesOne MP4/MOV via chunked upload and processing-status polling.
PollYes2 to 4 options with a duration (no media).
Reply / threadYesReply to an existing post via reply_to.
Account analyticsYesUses users.read and X user public_metrics fields.
Post analyticsYesUses tweet.read; public metrics are returned when available, while non-public/organic metrics require user context for owned posts and may be limited by API access/window.
RefreshYesRequires offline.access.

For media, Dravo initializes the upload, appends chunks, finalizes it and polls processing when X responds asynchronously. A post carries either up to 4 images, or a single video or GIF (images and video cannot be mixed).

Post options (platform_options.x)

JSON
{  "account_ids": ["YOUR_X_ACCOUNT_ID"],  "text": "What should we build next?",  "platform_options": {    "x": {      "poll": { "options": ["Feature A", "Feature B"], "duration_minutes": 1440 },      "reply_settings": "following",      "made_with_ai": false    }  }}
OptionTypeMeaning
pollobject{ options: [2 to 4 strings], duration_minutes: 5 to 10080 }. Cannot be combined with media.
reply_settingsstringWho can reply: following, mentionedUsers, subscribers or verified (default everyone).
reply_tostringPost id to reply to (for threads).
place_idstringTag a location by X place id.
for_super_followers_onlybooleanRestrict to Super Followers.
made_with_aibooleanLabel the media as AI-generated.
first_commentstringAuto-reply to the new post with this text (best effort). Any failure appears in the result's warnings.

Troubleshooting

invalid_client during callback

  1. Confirm the Dravo Client ID came from OAuth 2.0, not API Key.
  2. Confirm the Client Secret belongs to the same app.
  3. Confirm the app type is Web App/Automated App/Bot.
  4. If the secret was regenerated, update it in Dravo and reconnect.

Callback or redirect mismatch

Open App → Settings → User authentication settings → Edit and compare the stored callback character by character with Dravo's fixed URI.

Creating a post returns 403

  1. Confirm App permissions is Read and write.
  2. Confirm the token contains tweet.write.
  3. Confirm the X project has sufficient API credits/access.
  4. Reconnect after fixing permissions.

Text works but media fails

  1. Confirm media.write appears on the connected account.
  2. Confirm the token was issued after that scope was added.
  3. Confirm media API usage is funded.
  4. Reconnect and retry with a small image.

X analytics are empty or partial

  1. Confirm the connected token includes tweet.read and users.read.
  2. Confirm the post belongs to the authenticated account when requesting non-public or organic metrics.
  3. Remember that X documents a limited window for non-public, organic and promoted metrics.
  4. Check API access/credits before changing OAuth; public metrics can work while private metric fields are unavailable.

The account expires after about two hours

The token lacks offline.access or X did not issue a refresh token. Confirm the scope appears in Dravo, revoke the old consent and reconnect.

Official documentation

Next, read Configure OAuth and connect accounts for the shared browser flow and Posts for the unified post payload.