> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pendpost.com/llms.txt
> Use this file to discover all available pages before exploring further.

# X (Twitter)

> Set up X posting with OAuth 1.0a keys and tokens, or OAuth 2.0 PKCE.

The X lane publishes posts to X (Twitter). It publishes only once an X credential is present in your `.env`; until then the lane is live-but-unauthenticated and waits for you to connect it (set `PENDPOST_MODE=mock` to exercise it through the mock driver).

You own your X account and are responsible for X's terms of service. pendpost drafts and schedules, but a human approves before anything goes live.

## Choose an auth path

X supports two authentication paths. OAuth 1.0a is the recommended path for posting: it uses four long-lived keys and tokens and does not expire on a fixed schedule. OAuth 2.0 with PKCE is the alternative if you prefer a browser authorization flow.

First create a Project in the [X developer portal](https://developer.x.com/en/portal/dashboard), then add an App inside it. Standalone apps cannot post; the App must live in a Project. Open the App's "User authentication settings" and set the app permissions to **Read and Write before generating any tokens**, since generating tokens while the app is Read-only mints read-only tokens.

## Setup ceremony

For OAuth 1.0a (recommended), generate both the API Key/Secret (consumer keys) and the Access Token/Secret in the portal and put them in your `.env`. Order matters: if you regenerate the API Key/Secret, regenerate the Access Token/Secret **afterward**, or the old access token is invalidated.

After pasting the four values into your `.env`, validate the X lane in the **Setup** tab, or run a live probe with the `health_recheck` tool, to confirm the credential is live and not just configured. The lane is ready when it reports live.

For OAuth 2.0 with PKCE instead, set the callback URL to `http://localhost:8087/callback` and run the interactive auth ceremony, which authorizes in the browser and writes the tokens to your `.env`:

```bash theme={null}
node scripts/x-social.mjs auth
```

## Environment variables

Which group you fill depends on the path you chose. `X_ACCESS_TOKEN` appears in both: it is shared between the paths, so the same name holds either the OAuth 1.0a access token or the OAuth 2.0 access token, never both at once.

OAuth 1.0a:

| Variable | Purpose |
| - | - |
| `X_API_KEY` | Your app's API key (OAuth 1.0a consumer key). |
| `X_API_SECRET` | Your app's API secret (OAuth 1.0a consumer secret). |
| `X_ACCESS_TOKEN` | The OAuth 1.0a access token. |
| `X_ACCESS_TOKEN_SECRET` | The OAuth 1.0a access token secret. |

OAuth 2.0 PKCE (written by the auth ceremony):

| Variable | Purpose |
| - | - |
| `X_CLIENT_ID` | Your app's OAuth 2.0 client id. |
| `X_CLIENT_SECRET` | Your app's OAuth 2.0 client secret. |
| `X_ACCESS_TOKEN` | The OAuth 2.0 access token (short-lived, refreshed automatically). |
| `X_REFRESH_TOKEN` | The OAuth 2.0 refresh token (rotates on every use). |

> [!NOTE]
>
> A lane publishes only once its credential is present. If the X variables are blank, the X lane is live-but-unauthenticated and waits for you to connect it. Set `PENDPOST_MODE=mock` to exercise it through the mock driver.

## Validate the lane

A present credential is not the same as a working one. After setup, validate the X lane in the **Setup** tab, or run a live probe with the `health_recheck` tool, to confirm the credential is live and not just configured. The lane is ready when it reports live.

> [!NOTE]
>
> **Over MCP.** Setup and rotation are an interactive human ceremony; secrets are never written through MCP. An agent can still check status (`account_status`), run a live liveness probe (`health_recheck`), and read non-secret config (`config_get`) over MCP without touching credentials.

## Common failures

| Symptom | Cause | Fix |
| - | - | - |
| 401 "Could not authenticate you" (code 32). | A key or token was hand-typed and is subtly wrong, or the signature inputs are mismatched. | Copy each value directly from the portal (do not retype), confirm all four OAuth 1.0a values belong to the same app, and re-run setup. |
| Auth worked yesterday but now fails. | The API Key/Secret was regenerated, which invalidated the previously issued Access Token/Secret. | Regenerate the Access Token/Secret **after** any API Key/Secret change, then update all four values. |
| Reads work but posting is forbidden. | The app was left at Read-only permissions when the tokens were generated. | Set User authentication to "Read and Write", regenerate the Access Token/Secret, and re-run setup. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.