X (Twitter) paid
Run an X account as a DM bot. People direct-message the account, your agent replies in the same thread, behind the same on_message handler as every other channel. Reactive only: it answers DMs it receives and never cold-DMs anyone.
X is a paid channel: DM reads and sends run on Caspian's network and are pay-per-use, so the account needs a small credit balance. Sign in once to unlock paid channels and get $100 in free credit. See Authentication.
How it works
- Reactive DM bot. Someone DMs the connected X account; that arrives at your
on_messagehandler like any other channel.message.reply()answers in the same DM thread. - Polling, not webhooks. The gateway polls X for inbound DMs, so there is nothing to host or expose. Expect a few seconds of latency, not instant delivery.
- Never cold-DMs. The agent only replies to DMs it receives. This keeps the account within X's automation rules.
Mark the account as Automated in X settings (Settings → Your account → Automation) and label it under the account that owns the app. Running an unlabeled automated account risks suspension under X's terms.
Two ways to connect
Pick one:
| Option | Method | When to use |
|---|---|---|
| One-click | install_x() | Fastest. Authorize with "Sign in with X" using Caspian's shared X app: nothing to paste. |
| Your own tokens | connect_x() | Branded. Use your own X app's account tokens from developer.x.com. |
Option A: one-click "Sign in with X"
install_x() uses the gateway's shared X app (OAuth 1.0a, 3-legged), so there is no X app to create. It returns a connection with an authorize_url. Open it or hand it to the developer, authorize on X, and that account becomes the bot.
from caspian_sdk import CommClient
client = CommClient()
result = client.install_x()
print("Authorize here:", result["authorize_url"]) # "Sign in with X"
@client.on_message
def handle(message):
message.reply(f"You said: {message.text}")
client.listen()import { CommClient } from "caspian-sdk";
const client = new CommClient();
const result = await client.installX();
console.log("Authorize here:", result.authorize_url); // "Sign in with X"
client.onMessage(async (message) => {
await message.reply(`You said: ${message.text}`);
});
await client.listen();Whatever X account you are logged into when you click Authorize becomes the bot. That handle is what your users will DM. If you don't want your personal account to be the bot, create a dedicated account first (e.g. @yourbrandbot), log into it, then open the authorize link.
Once approved, the account goes active. Just run the same on_message handler and listen(). If install_x() returns a 400, the shared X app isn't configured on this gateway; use Option B.
Option B: bring your own account tokens
Create an app at developer.x.com with app permissions "Read and write and Direct message", then grab the Access Token and Access Token Secret from the Keys & Tokens tab. The numeric user_id is the part before the dash in the access token.
x = client.connect_x(
access_token="<Access Token>",
access_secret="<Access Token Secret>",
user_id="<numeric user id>",
)const x = await client.connectX({
accessToken: "<Access Token>",
accessSecret: "<Access Token Secret>",
userId: "<numeric user id>",
});Either way, the same handler answers X DMs and message.reply() replies in the same DM thread. A 409 means that X account is already connected to another agent.
Verify it
DM the connected account from any other X account. The running integration prints the inbound DM and replies within about ten seconds.
Limits
| Surface | Max length |
|---|---|
| Direct messages | ~10,000 characters |
| Public replies (posts) | 280 characters |
Inbound arrives by polling, so there is a few seconds of latency and no webhook to host. Because reads and sends are pay-per-use, keep a small credit balance on the account.