Slice
API

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

Signed out, 200
{ "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.

Signed in, 200
{
  "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

Request
POST /api/claim
content-type: application/json

{ "wallet": "<base58>", "lamports": "150000000" }
FieldRules
walletRequired. 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
lamportsOptional, 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
200 application/json
{ "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

Request
POST /api/routing
content-type: application/json

{ "routing": "autopay", "wallet": "<base58>" }
{ "routing": "burn", "declined": true }
{ "routing": "hold", "wallet": null }
FieldRules
routingRequired. hold, autopay or burn
walletOmitted keeps the saved wallet, null clears it, an address replaces it. Autopay needs one
declinedOptional boolean, default false. true is the public decline and requires burn
200 application/json
{ "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

StatusRouteWhen
400bothNot 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
401bothNot signed in, or the session expired
403bothThe request came from another site
409claimA payout is already queued or in flight. Wait for it to land
429bothOver 10 claims or 20 routing changes a minute from your IP
503bothClaims are paused, or the wallet could not be checked on chain just now. The balance is untouched
Every error carries a sentence meant for the person claiming, such as the balance they tried to exceed or the minimum they fell under, in the error field. Show it as it is.