x-twitter · git:20260815.99fb2fb · 2026-08-15 · sha256 01cb4b302a493a55
x-twitter git:20260815.99fb2fbA
Immutable. This exact content never changes and is served at /api/v1/blob/01cb4b302a493a55.
---
name: x-twitter
description: >-
Drive the user's authenticated X/Twitter via the browser skill: read
timelines/profiles, who-liked/retweeted (Favoriters/Retweeters), search, and
gated posts/likes/DMs through X's internal GraphQL API. Use for any 'do X on
Twitter/X as me' or build-the-X-graph task.
metadata:
tags: browser, x-twitter, internal-api, recon, social-graph
---
# X / Twitter — recon plan
> ✅ **SHIPPED on the Camofox stealth session** (X flags CDP-Chromium → use
> Camofox, not the local-browser chain). Helpers:
>
> - `scripts/lib/camofox.ts` (`createCamofox`) — Camofox-service REST client
> (tabs/navigate/click/type/evaluate + `fetchInPage`). baseUrl via
> `CAMOFOX_URL` (⚠️ use `127.0.0.1`, not `localhost` — node/undici picks IPv6
> ::1 and fails).
> - **X adapter** — `SOCIAL_PLATFORMS.x` in `@agstudio/browser-social`
> (`sites/x/adapter.ts`): profile · authored · engagement-given/received ·
> connections, as a `SocialSourcePort`. queryIds read **live from X's JS
> bundle** (drift-proof); `features` flags **self-heal** on 400s. The capture
> recipe lives in the registry, not a script-local helper.
> - **Graph ingest:**
> `scripts/profile.ts x <handle> --graph-only [--depth quick]` → registry
> capture → `landFootprint` graph sink (platform `"x"`): profile→SocialPerson,
> posts→AUTHORED, connections→FOLLOWS. Validated live: @romanbuildsaas → 73
> records · 74 graph ops.
> - **Login** done via Camofox (cookie-banner dismissed via `/evaluate` JS, then
> username→password→Continue). ⚠️ Followers list often **404s** for
> large/protected accounts; Favoriters/Retweeters load lazily →
> `captureMore()` after opening the likes/retweets modal. Gecko: no CDP, but
> `/evaluate` works (used for the API calls).
>
> Legacy note: the local-browser cloned profile is NOT logged into X and X flags
> CDP anyway — the Camofox path above supersedes it.
**Internal API:** GraphQL at
`https://x.com/i/api/graphql/<queryId>/<OperationName>` (+ some REST
`https://x.com/i/api/1.1/` and `/2/`). Same shape as Voyager: the web app calls
its own GraphQL; we drive that in-page.
## Auth (capture from any /i/api/graphql request header)
- `authorization: Bearer <web-app bearer>` — a **constant** embedded in X's JS
(same for all web users; long `AAAA…`). Capture once.
- `x-csrf-token: <ct0 cookie>` — per-session; read from `document.cookie` (ct0).
- cookies (`auth_token`, `ct0`) ride automatically (same-origin).
- also: `x-twitter-active-user: yes`, `x-twitter-auth-type: OAuth2Session`,
`x-twitter-client-language: en`.
In-page fetch:
```js
;async () => {
const ct0 = (document.cookie.match(/ct0=([^;]+)/) || [])[1]
const r = await fetch(URL, {
headers: {
authorization: BEARER,
"x-csrf-token": ct0,
"x-twitter-active-user": "yes",
"x-twitter-auth-type": "OAuth2Session",
"content-type": "application/json",
},
})
return await r.json()
}
```
## Operations to capture + test (queryId + `features`/`variables` DRIFT)
| Purpose | Operation |
| --------------------- | -------------------------------------------------------- |
| Feed | `HomeTimeline` / `HomeLatestTimeline` |
| Profile → rest_id | `UserByScreenName(screen_name)` |
| A user's tweets | `UserTweets` / `UserTweetsAndReplies(userId)` |
| Tweet + replies | `TweetDetail(focalTweetId)` |
| **Who liked** | `Favoriters(tweetId)` |
| **Who retweeted** | `Retweeters(tweetId)` |
| Following / Followers | `Following` / `Followers(userId)` |
| Search | `SearchTimeline(rawQuery, product)` |
| WRITE: tweet | `CreateTweet` |
| WRITE: like | `FavoriteTweet` |
| WRITE: retweet | `CreateRetweet` |
| WRITE: DM | REST `/1.1/dm/new2.json` or `/i/api/1.1/dm/conversation` |
## Entity model
`User{rest_id, screen_name, name, followers_count, …}` ·
`Tweet{id, full_text, author:User, favorite_count, retweet_count, reply_count, bookmark_count}`.
Responses are `instructions[].entries[]` timeline trees → walk to
`tweet_results.result`.
## Capture method
Open x.com → `list_network_requests` → find `/i/api/graphql/` → read queryId +
OperationName + grab `authorization` (bearer) + the `variables`/`features`
objects from the request. Reuse for the session.
## Actions to test (read-first; writes gated)
1. UserByScreenName → rest_id for a target.
2. UserTweets → their recent tweets + counts.
3. Favoriters/Retweeters on one tweet → **who engaged** (→ persona/graph).
4. SearchTimeline → discover accounts → score → dossier.
5. (gated) FavoriteTweet / CreateTweet / DM — explicit confirm, rate-limited.
## Gotchas
- GraphQL needs a `features` object (toggles) that DRIFTS — capture live or the
request 400s.
- queryIds rotate per deploy. ct0 must match the cookie exactly.
- `x.com` vs `twitter.com` origin — use whichever the tab is on (cookies are
per-domain).