Skip to content

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 fieldOptions key
Client IDx-twitter_client-id
Client Secretx-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:

TierCostNotes
Pay-Per-Use (default for new accounts)~$0.01 per write, ~$0.005 per readOAuth 2.0 included; this is what you'll get if you sign up today.
Basic (legacy)$200/moClosed to new signups; existing subscribers only.
Pro (legacy)$5,000/moExisting only.
Enterprise$42,000+/moContact 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.

  1. Projects & Apps → Create Project. Give it a name, pick a use case ("Making a bot", "Building tools for businesses", etc.), and a short description.
  2. 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:
    https://app.sosyabot.com/api/v1/auth/twitter/callback
    Self-hosters: substitute your BASE_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)).

  1. Client ID: paste the OAuth 2.0 Client ID from the Keys and tokens tab.
  2. Client Secret: paste the OAuth 2.0 Client Secret. Sosyabot stores it under the Options key x-twitter_client-secret (encrypted at rest per your storage config).
  3. Status: Enable. Disable hides the Connect button on the user-facing channels page; existing connections keep working until their token expires.
  4. Click Save.
  5. 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

  1. Open /app/channels as a regular workspace user (or yourself).
  2. Click Connect → X (Twitter) → OAuth.
  3. X opens its scope-consent screen. Approve.
  4. You should bounce back to /app/channels with the X account listed as active.
  5. Open /app/publishing, draft a one-line test tweet targeting the new channel, hit Publish now.
  6. Confirm the tweet appears on the X account's public timeline within a few seconds.

If any step fails, check Troubleshooting below.

Troubleshooting

SymptomCauseFix
Twitter integration disabled: client id/secret not configured on TestAdmin panel empty AND ENV emptyComplete steps 3–4 above.
X (Twitter) integration disabled by admin on TestStatus toggle is set to DisableFlip status to Enable and save.
401 Unauthorized on callbackWrong Client ID/Secret OR app type set to Public ClientRe-copy from the Keys and tokens tab; confirm app type is Confidential (Web App, Automated App or Bot).
Callback URL mismatch on the consent screenPortal Callback URI doesn't byte-for-byte match the URL Sosyabot sendsCompare exact strings, including trailing slash.
429 Too Many Requests on publishX rate limitPOST /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_reconnectAccess token expiredSosyabot 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).

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.