Auth.
RelayStation accepts three credentials, plus one transitional path retiring post-Phase C. Pick whichever fits your agent or your human session; the leases they produce are identical.
The API identifies callers as either an account actor (authenticated via API key or account JWT) or a wallet actor (x402 payment, or the transitional wallet-signature JWT). A lease is owned by whichever kind of actor created it; see Leases → Ownership.
The three paths
1. rs_live_ API key
A prepaid-balance credential under an account. Opaque token, Authorization: Bearer rs_live_…. Best for long-running agents, CI pipelines, and server-side automation.
- Format.
rs_live_+ 64 hex characters. The first 12 characters (prefix, e.g.rs_live_a1b2) are displayable; the full key is returned exactly once at rotation. - New accounts start without a key. OAuth sign-in (Google or GitHub) no longer auto-generates an API key. Your $0.50 welcome bonus lands on the balance immediately, but no
rs_live_key exists until you create one. Open the dashboard's Settings → API Keys tab and click Generate API key; the raw key is displayed once — copy it then. - Scope. One key per account. Rotating generates a new key and invalidates the old one atomically. The key carries no scope subdivisions — it can do everything the account owner can do.
- Mint.
POST /v1/account/api-keys— returns{ key, prefix, rotated, lastRotatedAt }. First issuance is201; rotation is200. - List.
GET /v1/account/api-keys— returns{ keys: [{ prefix, lastRotatedAt }] }(never the raw key). - Revoke.
DELETE /v1/account/api-keys/:prefix— prefix-match safety:409if the prefix is stale.
2. Account JWT (OAuth-issued)
A session token minted at OAuth sign-in. 30-day TTL, type: 'account', issuer: 'relaystation'. Best for human sessions on the dashboard. Agents should prefer API keys — JWTs rotate and expire on a schedule you don't control.
- Obtain. Start an OAuth flow at
GET /v1/auth/google/startorGET /v1/auth/github/start; the callback sets the session token. - Carry. Same
Authorization: Bearer …header as API keys. The middleware discriminates by payload:type === 'account'selects the account path. - Lifetime. 30 days. After expiry, re-sign-in; there is no refresh token today.
3. x402 wallet signature
Pay USDC on Base per request. The agent attaches an X-Payment header produced by an x402 client; the on-chain payment is both identity and authorization. No account, no prior relationship to RelayStation. Best for ephemeral agents that don't outlive a single task.
- Discovery.
/.well-known/x402.jsonadvertises the facilitator URL, network (base), and currency (USDC). - Actor identity. The wallet address from the signed payment becomes the owning actor for any lease created by the request.
- No JWT. There is no session — each priced call re-settles. See x402 integration for the full flow.
Transitional: wallet-signature JWT
Retiring post-Phase C. The legacy wallet-signature JWT (payload
{ wallet }, notype, no issuer) is recognized for dashboard-adjacent endpoints during the migration window so that pre-OAuth wallet users don't lose access. New integrations should use anrs_live_API key or account JWT instead.
Grep marker in the codebase: [PHASE-C wallet-JWT transitional branch]. When that branch is removed, wallet-signature JWTs stop working.
Which middleware runs on which endpoint
Three gates, layered.
- requireAuth
- Strict. Accepts
rs_live_API keys and account JWTs. Populatesreq.account. Guards every/v1/account/*endpoint and (for the most part) the lease-creation endpoints. - requireDashboardAuth
- Lenient transitional. Everything
requireAuthaccepts, plus legacy wallet-signature JWTs. Populatesreq.accountorreq.walletdepending on the path taken. Guards dashboard-adjacent endpoints that need to cover both pre-OAuth and post-OAuth users during Phase C. - requireAccountOrWalletAuth
- The lease-creation gate. Accepts an API key / account JWT (billed to prepaid balance) OR an x402
X-Paymentheader (billed on-chain). Never falls through to wallet-signature JWT — unpaid wallet sessions cannot create leases.
Picking a path
- Ephemeral agent, no account. x402. Mint a wallet, pay per request, terminate.
- Long-running agent or fleet. Prepaid account +
rs_live_API key. One top-up, one key. - Human at the dashboard. OAuth (Google or GitHub) → account JWT. Rotating API keys from the UI is a separate flow.
- Pre-Phase-C wallet user. Keep using the wallet-signature JWT until the sunset, then migrate to OAuth or to a fresh API key minted from the dashboard.
Which credential each endpoint accepts
Authoritative matrix. Columns are the four credential kinds; cells mark acceptance per endpoint. ⚠ marks paths that accept the transitional wallet-signature JWT (retires post-Phase D); new integrations shouldn't rely on these. — marks endpoints that don't use the Authorization header at all (the URL or drop-token IS the credential, or the endpoint is a pre-auth entrypoint).
| Endpoint | wallet-JWT | account-JWT | rs_live_ |
x402 |
|---|---|---|---|---|
| Storage | ||||
POST /api/store | ✗ | ✓ | ✓ | ✓ |
GET /api/download/:lease_id | ⚠ | ✓ | ✓ | ✗ |
GET /api/leases | ⚠ | ✓ | ✓ | ✗ |
| Mailbox | ||||
POST /api/mailbox | ✗ | ✓ | ✓ | ✓ |
GET /api/mailboxes | ⚠ | ✓ | ✓ | ✗ |
POST /api/mailbox/:drop_token/drop | — drop token is the credential (no auth header) | |||
GET /api/mailbox/:id/messages | ⚠ | ✓ | ✓ | ✗ |
GET · DELETE /api/mailbox/:id/messages/:mid | ⚠ | ✓ | ✓ | ✗ |
DELETE /api/mailbox/:id | ⚠ | ✓ | ✓ | ✗ |
| Checkpoint | ||||
POST /api/checkpoint | ✗ | ✓ | ✓ | ✓ |
GET /api/checkpoints | ⚠ | ✓ | ✓ | ✗ |
GET · PUT · DELETE /api/checkpoint/:workflow_id | ⚠ | ✓ | ✓ | ✗ |
| Handoff (cross-variant) | ||||
POST /api/handoff/:lease_id | ⚠ | ✓ | ✓ | ✗ |
GET /api/handoffs/:lease_id | ⚠ | ✓ | ✓ | ✗ |
DELETE /api/handoffs/:id | ⚠ | ✓ | ✓ | ✗ |
GET /api/claim/:token | — claim token is the credential (no auth header) | |||
| Account & billing | ||||
GET /v1/account/me | ✗ | ✓ | ✓ | ✗ |
GET /v1/account/ledger | ✗ | ✓ | ✓ | ✗ |
GET · POST · DELETE /v1/account/api-keys | ✗ | ✓ | ✓ | ✗ |
POST /v1/billing/checkout | ✗ | ✓ | ✓ | ✗ |
| Auth entrypoints | ||||
GET /v1/auth/google/start · /v1/auth/github/start | — pre-auth (no credential yet; OAuth redirect flow issues the account JWT) | |||
Two things to note about the ⚠ column. First: every ⚠ also carries a ✓ in a non-transitional column — the wallet-JWT path is a bonus acceptance, not a required one. Second: lease creation (POST /api/store, /api/mailbox, /api/checkpoint) is the single group that never accepts wallet-JWT. Wallet actors pay for leases via x402 at the moment of creation; they don't have a prepaid balance to debit.
Where credentials go
Every authenticated endpoint reads a single Authorization header:
# API key Authorization: Bearer rs_live_a1b2c3d4e5f6… # account JWT (OAuth-issued) Authorization: Bearer eyJhbGciOiJIUzI1NiIs… # wallet-signature JWT (transitional) Authorization: Bearer eyJhbGciOiJIUzI1NiIs…
x402 payments ride in X-Payment instead — never in Authorization. A request carrying both is treated as x402-first at the lease-creation gate; other gates reject x402 outright because they don't price per call.