X (Twitter) API Setup
A walkthrough for instance administrators: create an X (formerly Twitter) Developer App, paste its OAuth 2.0 credentials into Sosyabot's /admin/api-integration/x-twitter page, and verify a real account connect from start to finish.
This is the admin-side doc. For the user-side flow (an end-user clicking Connect → X) see Connections → X (Twitter).
Overview
Sosyabot resolves X credentials at runtime in this order: admin panel override (Options collection) → ENV variable. The admin form at /admin/api-integration/x-twitter writes:
| Form field | Options key |
|---|---|
| Client ID | x-twitter_client-id |
| Client Secret | x-twitter_client-secret |
| Status (Enable / Disable) | x-twitter_integration_status |
The ENV fallback (used when the admin panel is empty) is TWITTER_CLIENT_ID + TWITTER_CLIENT_SECRET. See ENV Reference.
You only need to do this once per deployment — the values are global to the instance, not per-workspace.
Pricing & tier
X moved to a paid-API model. As of 2026 the active tiers are:
| Tier | Cost | Notes |
|---|---|---|
| Pay-Per-Use (default for new accounts) | ~$0.01 per write, ~$0.005 per read | OAuth 2.0 included; this is what you'll get if you sign up today. |
| Basic (legacy) | $200/mo | Closed to new signups; existing subscribers only. |
| Pro (legacy) | $5,000/mo | Existing only. |
| Enterprise | $42,000+/mo | Contact X sales. |
Sosyabot uses the OAuth 2.0 user-authorization flow, which is available on every tier — there is no auth-flow restriction. The tier only controls request volume and per-call billing.
No Free tier
The Free tier was discontinued for new signups in February 2026. New developer accounts must enable billing on Pay-Per-Use before any tweet can be posted via the API.
Step-by-step on developer.x.com
1. Sign up for a developer account
Go to https://developer.x.com, sign in with the X account you want to own the app, and accept the Developer Agreement. (The legacy developer.twitter.com URL still redirects.)
2. Create a Project, then an App inside it
App management lives at https://console.x.com.
- Projects & Apps → Create Project. Give it a name, pick a use case ("Making a bot", "Building tools for businesses", etc.), and a short description.
- Inside the new Project click Create App. Pick a globally-unique app name.
3. Configure user authentication
Open the App and click User authentication settings → Set up. Fill in:
- Type of App: Web App, Automated App or Bot (this is the Confidential client option — required for Sosyabot's server-side OAuth flow). Do not pick Native App or Public Client — both will fail.
- App permissions: Read and write. (Add Direct messages later if you need DM features.)
- Callback URI / Redirect URL:Self-hosters: substitute your
https://app.sosyabot.com/api/v1/auth/twitter/callbackBASE_URL. The path (/api/v1/auth/twitter/callback) is fixed. - Website URL:
https://sosyabot.com(or your own marketing site).
Save the form. X will validate the callback URL on save — if you typo it, save fails immediately.
Confidential client only
The Sosyabot integration uses a confidential OAuth 2.0 client. Public Client / Native App options skip the client-secret exchange entirely and Sosyabot will return 401 Unauthorized on every callback.
4. Grab Client ID + Client Secret
After saving, open the Keys and tokens tab on the App. Under OAuth 2.0 Client ID and Client Secret, copy both values.
Client Secret is shown only once
X displays the secret a single time. If you close the modal without saving it, you must regenerate (which invalidates the old one and breaks any existing Sosyabot connection). Store both in a password manager before leaving the page.
The callback URL you set in step 3 is also visible here as confirmation.
Pasting into Sosyabot admin panel
Open /admin/api-integration/x-twitter (Admin Panel → OAuth Credentials → X (Twitter)).
- Client ID: paste the OAuth 2.0 Client ID from the Keys and tokens tab.
- Client Secret: paste the OAuth 2.0 Client Secret. Sosyabot stores it under the
Optionskeyx-twitter_client-secret(encrypted at rest per your storage config). - Status: Enable. Disable hides the Connect button on the user-facing channels page; existing connections keep working until their token expires.
- Click Save.
- Click Test credentials. A successful response shows the first 6 chars of your Client ID plus the full callback URL Sosyabot will send to X. A failure shows one of:
Twitter integration disabled: client id/secret not configured→ both admin panel and ENV are empty.X (Twitter) integration disabled by admin→ status toggle is set to Disable.
No restart needed
The Twitter passport strategy is wired to reload itself when credentials change. You don't need ./service.sh restart api after saving — the next OAuth callback uses the new values.
Verification — end-to-end smoke test
- Open
/app/channelsas a regular workspace user (or yourself). - Click Connect → X (Twitter) → OAuth.
- X opens its scope-consent screen. Approve.
- You should bounce back to
/app/channelswith the X account listed as active. - Open
/app/publishing, draft a one-line test tweet targeting the new channel, hit Publish now. - Confirm the tweet appears on the X account's public timeline within a few seconds.
If any step fails, check Troubleshooting below.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Twitter integration disabled: client id/secret not configured on Test | Admin panel empty AND ENV empty | Complete steps 3–4 above. |
X (Twitter) integration disabled by admin on Test | Status toggle is set to Disable | Flip status to Enable and save. |
401 Unauthorized on callback | Wrong Client ID/Secret OR app type set to Public Client | Re-copy from the Keys and tokens tab; confirm app type is Confidential (Web App, Automated App or Bot). |
Callback URL mismatch on the consent screen | Portal Callback URI doesn't byte-for-byte match the URL Sosyabot sends | Compare exact strings, including trailing slash. |
429 Too Many Requests on publish | X rate limit | POST /2/tweets caps: 100/15 min per user, 10,000/24 h per app. The x-rate-limit-reset response header gives the reset epoch. |
Connected channel suddenly shows needs_reconnect | Access token expired | Sosyabot does not request offline.access, so there is no automatic refresh — the end user must reconnect from /app/channels. |
The offline.access decision is intentional in this build — see the user-side Connections → X page for the rationale (cookie-connect mode is offered as the long-lived alternative).
ENV fallback (self-hosters)
If you'd rather configure secrets in .env than the admin panel:
TWITTER_CLIENT_ID=<your client id>
TWITTER_CLIENT_SECRET=<your client secret>These are read at boot from backend/src/config/env.ts and act as the fallback when the admin panel is empty. The admin panel always wins when both are set, so it's safe to configure both.
After editing .env you must ./service.sh restart api (ENV is read once at process start; admin-panel writes don't need a restart).
Cookie connect alternative
X also supports a cookie-based connect path (POST /api/v1/auth/twitter/unofficial) that bypasses the OAuth client entirely and uses a logged-in browser's auth_token + ct0 cookies. This is useful for personal handles where you don't want to register a developer app at all. See Connections → X for the user-facing instructions.
Cookie connect doesn't require any admin configuration — the credentials in this page only affect the OAuth path.