Claim & routing
Three routes act on a signed-in X account: read it, queue a payout from it, and set where its new credits go. They take the account from the session cookie and nothing else.
The session
GET /api/auth/x/start?returnTo=/claim redirects to X’s consent screen (OAuth 2.0 with PKCE, scopes users.read and tweet.read, both read-only). The callback sets a signed, HttpOnly, SameSite=Lax session cookie valid for seven days, and POST /api/auth/logout clears it.
The session is a browser cookie, so these routes are meant to be called from this site. A claim or routing change whose Origin header names another site is refused with 403, and no route accepts an X user id or a handle in its body.
GET /api/me
{ "signedIn": false, "xConfigured": true, "devLogin": false }Signed out is a normal answer, not a 401. xConfigured says whether sign-in with X is set up on this deployment.
{
"signedIn": true,
"xUserId": "1234567890",
"handle": "alice",
"displayHandle": "Alice",
"name": "Alice",
"avatarUrl": "https://…",
"routing": "hold",
"payoutWallet": null,
"declined": false,
"creditedLamports": "612000000",
"paidLamports": "400000000",
"burnedLamports": "0",
"balanceLamports": "212000000",
"handles": ["alice", "alice_old"],
"tokens": [ … ],
"payouts": [ … ],
"minClaimLamports": "10000000",
"autopayMinLamports": "50000000",
"claimsPaused": false
}The money fields, tokens and payouts have the same meaning as on GET /api/x/:handle. handles is every handle pinned to this X user id, so a rename shows as two entries. minClaimLamports and autopayMinLamports are the thresholds in force now; claimsPaused is true while payouts are paused.
POST /api/claim
POST /api/claim
content-type: application/json
{ "wallet": "<base58>", "lamports": "150000000" }| Field | Rules |
|---|---|
wallet | Required. A Solana wallet: a public key on the ed25519 curve that is unused or a plain system account. Program-derived addresses, token and program accounts, the treasury, and the mints and fee addresses of tokens launched here are refused |
lamports | Optional, a decimal string of whole lamports. Omit it to claim the whole balance, which is what the claim page does. At least 0.01 SOL by default, and no more than the balance |
{ "payoutId": 8, "lamports": "150000000", "status": "queued" }The claim is queued, not sent. The amount leaves the balance immediately, and the payout job sends it on its next run. Follow it through payouts on GET /api/me, or on the ledger.
POST /api/routing
POST /api/routing
content-type: application/json
{ "routing": "autopay", "wallet": "<base58>" }
{ "routing": "burn", "declined": true }
{ "routing": "hold", "wallet": null }| Field | Rules |
|---|---|
routing | Required. hold, autopay or burn |
wallet | Omitted keeps the saved wallet, null clears it, an address replaces it. Autopay needs one |
declined | Optional boolean, default false. true is the public decline and requires burn |
{ "ok": true, "routing": "autopay", "payoutWallet": "<base58>", "declined": false }The new routing applies to credits from the next sweep on. Switching to Autopay with a balance already above the threshold pays it on the next payout run. See Routing.
Errors
| Status | Route | When |
|---|---|---|
| 400 | both | Not a wallet address, or an address that is not a wallet (a token or program account); no balance; an amount of zero, above the balance or below the minimum; an unknown routing; a decline without burn; Autopay without a wallet |
| 401 | both | Not signed in, or the session expired |
| 403 | both | The request came from another site |
| 409 | claim | A payout is already queued or in flight. Wait for it to land |
| 429 | both | Over 10 claims or 20 routing changes a minute from your IP |
| 503 | both | Claims are paused, or the wallet could not be checked on chain just now. The balance is untouched |
error field. Show it as it is.