Skip to content

X (Twitter) setup

Registering an X developer application and wiring it into SocialRelay.

X is the quickest platform to get publishing for real: unlike Meta and TikTok, posting works as soon as your app exists, without waiting on a review. What the paid tiers buy you is volume, not permission.

Prerequisites

  • An X account that will do the posting
  • A developer account at developer.x.com — the free tier is enough to publish

1. Create the app

  1. Open the X developer portal and create a project, then an app inside it.
  2. Give the app a name users will recognise on the consent screen.
  3. Open the app’s User authentication settings and choose Set up.

2. Configure OAuth 2.0

SocialRelay uses OAuth 2.0 with PKCE, not OAuth 1.0a. Set the following:

SettingValue
App permissionsRead and write — read-only cannot post
Type of AppWeb App, Automated App or Bot (a confidential client)
Callback URIhttps://social.aitoolbucket.com/api/oauth/x/callback
Website URLhttps://social.aitoolbucket.com

Read and write must be set before you connect

If the app is left on read-only, the OAuth flow still succeeds — the failure only appears later, when publishing returns a permissions error. Change the permission and reconnect the account; changing it after a token is issued does not upgrade that token.

3. Scopes

SocialRelay requests exactly these:

text
tweet.read      read tweets, needed to confirm a post exists
tweet.write     create tweets
users.read      read the connected account's own profile
media.write     upload images
offline.access  receive a refresh token so the connection survives

offline.access matters more than it looks: without it X issues no refresh token, the access token expires in a couple of hours, and every scheduled post after that fails until someone reconnects by hand.

4. Copy the credentials

From the app’s Keys and tokens tab, take the OAuth 2.0 Client ID and Client Secret. These are not the same as the API Key/Secret or the Access Token/Secret, which belong to OAuth 1.0a.

bash
cd /var/www/creator

read -rsp "X_CLIENT_ID: " V && printf '\nX_CLIENT_ID="%s"\n' "$V" >> .env
read -rsp "X_CLIENT_SECRET: " V && printf '\nX_CLIENT_SECRET="%s"\n' "$V" >> .env

sudo systemctl restart creator-app creator-worker

X_ACCESS_TOKEN and X_ACCESS_SECRET are optional

Those two are OAuth 1.0a credentials. SocialRelay does not need them for the flow described here; they exist in .env.example only for deployments that want an app-level fallback for media upload.

5. Connect the account

  1. Open Social Accounts in the dashboard.
  2. Press Connect X (Twitter).
  3. Approve the request on X.
  4. You are returned to Social Accounts and the card shows Connected with your handle.
  5. Press Test Connection — it should report the username back.

What X will and will not accept

SocialRelay enforces these before it calls the API, so an invalid post is caught in the review screen rather than becoming a failed publish:

  • 280 characters, counted including hashtags, by code point so emoji count as one
  • Up to 4 images, or none — text-only posts are fine
  • 5 MB per image, JPEG, PNG, or WebP

Troubleshooting

401 or X_TOKEN_EXPIRED

The access token expired and could not be refreshed. Usually offline.access was missing when the account was connected, so no refresh token was ever stored. Reconnect the account after confirming the scope.

403 when publishing

Almost always app permissions still set to read-only. Change to Read and write, then reconnect — the existing token keeps the permissions it was granted.

Duplicate content

X rejects an identical tweet posted twice in quick succession. This surfaces as X_DUPLICATE_CONTENT. Change the text and retry.

X_RATE_LIMITED

You have hit the posting cap for your access tier. The job is retried automatically with exponential backoff, honouring the platform’s Retry-After where it sends one. No action is needed unless it keeps failing, which means the tier is too small for your volume.

X_MEDIA_TOO_LARGE

An image is over 5 MB. SocialRelay checks declared sizes before uploading and re-checks after downloading, so this can also mean the image at that URL is larger than the payload claimed.

Once X works, the same pattern applies to Meta and TikTok.