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
| Setting | Value |
|---|---|
| Dravo platform | x |
| Developer console | https://console.x.com |
| OAuth flow | OAuth 2.0 Authorization Code with PKCE |
| App type | Web App, Automated App or Bot |
| App permissions | Read and write |
| Callback | https://api.dravo.dev/v1/oauth/x/callback |
| Credentials | OAuth 2.0 Client ID and Client Secret |
| Required scopes | tweet.write, tweet.read, users.read, offline.access, media.write |
| Analytics scopes | No extra Dravo scope; post and account metrics use tweet.read and users.read with user context |
| Billing | X 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 Console → Sign in.
- Sign in with the X account that should own the developer organization.
- Accept the Developer Agreement and platform policies.
- Complete the requested use-case description and organization details.
- 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.
- Add a payment method when required.
- Purchase or enable enough API credits for create-post and media operations.
- Set a spending limit and low-balance alert.
- 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.
- Choose New App.
- Enter a name that distinguishes development from production.
- Describe the user-authorized social publishing use case.
- Associate the app with the correct project/organization if the console asks.
- 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 label | Protocol | Used by Dravo |
|---|---|---|
| API Key and API Key Secret | OAuth 1.0a | No |
| Bearer Token | App-only OAuth 2.0 | No |
| Access Token and Access Token Secret | OAuth 1.0a user context | No |
| OAuth 2.0 Client ID and Client Secret | OAuth 2.0 user context | Yes |
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.
- Enable OAuth 2.0.
- Leave OAuth 1.0a disabled unless another integration explicitly needs it.
- 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.
- Do not leave the app on Read.
- Direct Message permission is not required by Dravo.
- 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:
| Scope | Why Dravo needs it |
|---|---|
tweet.write | Create posts. |
tweet.read | Read post resources required by user-context API operations. |
users.read | Resolve /2/users/me, username and avatar. |
offline.access | Receive a refresh token. |
media.write | Upload 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:
-
Under Callback URI / Redirect URL, add:
TEXThttps://api.dravo.dev/v1/oauth/x/callback -
Under Website URL, enter the public website for your product. This is not the OAuth callback.
-
Add Terms of Service and Privacy Policy URLs when the console displays them.
-
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.
- Find the Client ID heading and copy its value.
- Copy the Client Secret shown under it.
- If the secret is no longer visible, choose Regenerate and copy the new value immediately.
- 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:
- API Key.
- API Key Secret.
- Bearer Token.
- Access Token.
- 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.
- Select X (Twitter).
- Enter a recognizable name such as
Production X App. - Paste the OAuth 2.0 Client ID into Client ID.
- Paste the OAuth 2.0 Client Secret into Client Secret.
- Confirm the fixed callback and all five scopes displayed by Dravo.
- Save the OAuth app.
5.2 Save it through the API
Endpoint: POST /v1/oauth-apps.
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.
- Select X (Twitter).
- Choose the OAuth app saved in Step 5.
- Choose Connect with OAuth.
- Sign in to the X account that should publish.
- Review the requested permissions and authorize the app.
- Allow X to return the browser to Dravo.
6.2 Connect from your backend
Endpoint: GET /v1/oauth/x/start.
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.
- Confirm the username and X user id are correct.
- Confirm
connection_typeisoauth. - Confirm
oauth_app_ididentifies the intended X app. - Confirm the stored scopes include all five values, especially
offline.accessandmedia.write. - Confirm token health is active.
7.2 Publish a text-only post
Navigation: Dravo Dashboard → Playground.
- Select only the new X account.
- Publish a short text post.
- Confirm the post appears on the intended X profile.
- If it fails with payment/access errors, inspect credits before changing OAuth.
7.3 Publish an image post
- Use a small public image or upload/import one through Dravo media.
- Publish it with a short caption.
- 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
- Keep a dedicated production X app and Dravo OAuth-app record.
- Register the same fixed Dravo callback on that production app.
- Repeat Steps 3–7 with the production Client ID and Secret.
- Reconnect every account after moving it to a different X app.
8.2 Configure billing safeguards
Navigation: Developer Console → Billing/Credits and Usage.
- Confirm create-post and media usage is funded.
- Set a spending ceiling and low-credit alert.
- Monitor response headers and the console for rate limits.
- Test the expected daily volume before onboarding users.
8.3 Plan credential and consent changes
- Rotating the Client Secret requires updating the Dravo OAuth app.
- Adding a scope or changing Read/Write permissions requires reconnection.
- Revoking the X app invalidates every account connected through it.
- 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
| Content | Current support | Notes |
|---|---|---|
| Text | Yes | Created with X API v2. |
| Images | Yes | Up to 4 images (JPEG/PNG/WEBP/GIF) attached to one post. |
| Video | Yes | One MP4/MOV via chunked upload and processing-status polling. |
| Poll | Yes | 2 to 4 options with a duration (no media). |
| Reply / thread | Yes | Reply to an existing post via reply_to. |
| Account analytics | Yes | Uses users.read and X user public_metrics fields. |
| Post analytics | Yes | Uses 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. |
| Refresh | Yes | Requires 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)
{ "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 } }}| Option | Type | Meaning |
|---|---|---|
poll | object | { options: [2 to 4 strings], duration_minutes: 5 to 10080 }. Cannot be combined with media. |
reply_settings | string | Who can reply: following, mentionedUsers, subscribers or verified (default everyone). |
reply_to | string | Post id to reply to (for threads). |
place_id | string | Tag a location by X place id. |
for_super_followers_only | boolean | Restrict to Super Followers. |
made_with_ai | boolean | Label the media as AI-generated. |
first_comment | string | Auto-reply to the new post with this text (best effort). Any failure appears in the result's warnings. |
Troubleshooting
invalid_client during callback
- Confirm the Dravo Client ID came from OAuth 2.0, not API Key.
- Confirm the Client Secret belongs to the same app.
- Confirm the app type is Web App/Automated App/Bot.
- 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
- Confirm App permissions is Read and write.
- Confirm the token contains
tweet.write. - Confirm the X project has sufficient API credits/access.
- Reconnect after fixing permissions.
Text works but media fails
- Confirm
media.writeappears on the connected account. - Confirm the token was issued after that scope was added.
- Confirm media API usage is funded.
- Reconnect and retry with a small image.
X analytics are empty or partial
- Confirm the connected token includes
tweet.readandusers.read. - Confirm the post belongs to the authenticated account when requesting non-public or organic metrics.
- Remember that X documents a limited window for non-public, organic and promoted metrics.
- 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
- Get access to the X Developer Console
- OAuth 2.0 Authorization Code Flow with PKCE
- X API authentication overview
- X API metrics
- Chunked media upload
- X API pay-per-usage pricing
Next, read Configure OAuth and connect accounts for the shared browser flow and Posts for the unified post payload.