X
Reply to public mentions and hold direct message conversations on X.
Install
pnpm add @chat-adapter/xQuick start
The adapter auto-detects X_CONSUMER_SECRET, X_USER_ACCESS_TOKEN, X_USER_ID, and X_USERNAME from the environment.
import { Chat } from "chat";
import { createXAdapter } from "@chat-adapter/x";
const bot = new Chat({
userName: "mybot",
adapters: {
x: createXAdapter(),
},
});
bot.onNewMention(async (thread, message) => {
await thread.post(`Hi @${message.author.userName}!`);
});
bot.onDirectMessage(async (thread, message) => {
await thread.post("Hello from X!");
});import { bot } from "@/lib/bot";
export async function GET(request: Request) {
return bot.webhooks.x(request);
}
export async function POST(request: Request) {
return bot.webhooks.x(request);
}Configuration
Prop
Type
Authentication
1. Create an X app
- Go to the X developer portal and create a Project and App.
- Under Keys and tokens, copy the API Key Secret to
X_CONSUMER_SECRET. - Enable OAuth 2.0 user authentication with the scopes
tweet.read,tweet.write,users.read,dm.read,dm.write,like.write, andoffline.access. - Complete the OAuth 2.0 flow for the bot account. Either store the access token as
X_USER_ACCESS_TOKEN, or storeX_CLIENT_IDplusX_REFRESH_TOKENto let the adapter manage token refresh.
X OAuth 2.0 user tokens expire after about two hours. For long-running bots, configure managed refresh (clientId + refreshToken): the adapter refreshes tokens before expiry and persists the rotated refresh token in the state adapter, encrypted when encryptionKey is set.
2. Register a webhook and subscriptions
X delivers events through the X Activity API. Set this up once, in the X developer console, which handles the auth for you:
- Register your webhook URL (
https://your-domain.com/api/webhooks/x). It must be publicly reachable over HTTPS and cannot include a port. X sends a CRC challenge immediately and revalidates hourly; the adapter answers both automatically. - Create subscriptions for the events the adapter consumes:
post.mention.create,dm.received, anddm.sent. These are private events, so the bot user must have authorized your app first.
Subscription and webhook management is one-time setup, not something the adapter does at runtime. If you script it instead of using the console, note the Activity API endpoints are auth-picky and operation-specific (creating a private-event subscription needed OAuth 1.0a user context in testing, while list and delete used the app-only bearer token), and do not fully match the published spec. The console avoids all of this.
Advanced
Webhook flow
- CRC challenge (GET): X sends a
crc_token, answered withsha256=<base64 HMAC>keyed by the consumer secret. - Event delivery (POST): activity events signed via
x-twitter-webhooks-signature, verified against the raw request body with timing-safe comparison.
Streaming
X has no native streaming and public post edits are eligibility-gated, so the adapter buffers the entire stream and posts once on completion instead of using post+edit updates.
Reply threading
Replies target the most recent mention received in the conversation, and consecutive replies chain under the bot's previous reply. Top-level posts go through channel.post on the x:public channel.
Reactions
Likes are the only reaction X exposes. addReaction accepts emoji.heart or "like" and maps to POST /2/users/:id/likes; anything else throws a ValidationError.
Thread ID format
x:post:{conversationId} # public post threads (channel: x:public)
x:dm:{participantUserId} # direct message with a single userExamples: x:post:1943467279943467279, x:dm:783214. X DM webhooks carry no conversation id, only participant ids, so DMs are threaded by the other participant's user id: openDM("783214") returns x:dm:783214, and both replies and inbound events for that conversation share the same thread. Sends route to the by-participant endpoint (POST /2/dm_conversations/with/{id}/messages).
Automation policy
X enforces automation rules: get explicit consent before automated replies or DMs, honor opt-outs immediately, disclose the bot identity in the account profile, and never send bulk or duplicate automated content.
Feature support
Messaging
| Feature | Supported |
|---|---|
| Post message | |
| Edit message | Posts only |
| Delete message | |
| File uploads | |
| Streaming | Buffered |
| Scheduled messages |
Rich content
| Feature | Supported |
|---|---|
| Card format | Plain text |
| Buttons | |
| Link buttons | Rendered as text |
| Select menus | |
| Tables | ASCII |
| Fields | Plain text |
| Images in cards | |
| Modals |
Conversations
| Feature | Supported |
|---|---|
| Slash commands | |
| Mentions | |
| Add reactions | Likes only |
| Remove reactions | Likes only |
| Typing indicator | |
| DMs | |
| Ephemeral messages | |
| User lookup | |
| Parent subject | |
| Native client | |
| Custom API endpoint |
Message history
| Feature | Supported |
|---|---|
| Fetch messages | DMs via API, posts cached |
| Fetch single message | Posts via API, DMs cached |
| Fetch thread info | |
| Fetch channel messages | |
| List threads | |
| Fetch channel info | |
| Post channel message |