Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.phoenix.trade/llms.txt

Use this file to discover all available pages before exploring further.

Phoenix auth issues short-lived access JWTs and longer-lived refresh tokens. The Rise SDK can manage the session, attach bearer tokens to authenticated requests, refresh before expiry, and persist the rotated token pair.

Lifecycle

  1. Request a challenge from Phoenix.
  2. Sign the challenge with the authority wallet.
  3. Exchange the signature for an auth response.
  4. Use Authorization: Bearer <access_token> on authenticated routes.
  5. Refresh with POST /v1/auth/refresh before the access token expires.
  6. Reauthenticate when refresh fails with a terminal auth error.
The auth response shape is:
{
  "token_type": "Bearer",
  "access_token": "eyJ...",
  "expires_in": 900,
  "refresh_token": "eyJ...",
  "refresh_expires_in": 2592000
}
Store the full response. Refresh rotates the access token and refresh token; the previous access token is accepted for a short grace window.

Endpoints

StepEndpointBody or queryNotes
Wallet nonceGET /v1/auth/nonce?wallet_pubkey=...query wallet_pubkeyReturns nonce_id, message, and expires_at.
Wallet loginPOST /v1/auth/login/walletwallet_pubkey, signature, nonce_idsignature signs the exact nonce message.
Wallet transaction challengePOST /v1/auth/wallet/transaction-challengewallet_pubkeyAlternative for wallets that cannot sign arbitrary messages.
Wallet transaction loginPOST /v1/auth/login/wallet/transactionwallet_pubkey, nonce_id, signed_transactionExchanges the signed memo transaction for JWTs.
RefreshPOST /v1/auth/refreshrefresh_tokenSDKs include the current bearer token when available and store the rotated session.
LogoutPOST /v1/auth/logoutnoneRequires Authorization: Bearer <access_token> and revokes the session.

Wallet login

This TypeScript example is written for a browser wallet that supports signMessage.
import {
  LocalStorageAuthSessionStorage,
  createPhoenixClient,
} from "@ellipsis-labs/rise";
import bs58 from "bs58";

const client = createPhoenixClient({
  apiUrl: "https://perp-api.phoenix.trade",
  auth: true,
  authConfig: {
    storage: new LocalStorageAuthSessionStorage(),
  },
  ws: false,
});

const auth = client.auth!;
const walletPubkey = wallet.publicKey.toString();

const nonce = await auth.getWalletNonce(walletPubkey);
const message = new TextEncoder().encode(nonce.message);
const signature = await wallet.signMessage(message);

const session = await auth.loginWithWalletSignature(
  walletPubkey,
  bs58.encode(signature),
  nonce.nonce_id
);

console.log(session.accessToken);
In Rust builds with the solana-keypair feature, auth.login_with_wallet_keypair(&keypair).await? wraps the nonce, signing, and login sequence.

Refresh

With a managed SDK session, refresh is normally automatic before authenticated HTTP and WebSocket requests. Manual refresh is still useful when you want to force rotation after loading a saved session.
const auth = client.auth!;
const manager = client.sessionManager!;
const current = await manager.getSession();

if (!current) {
  throw new Error("No Phoenix auth session");
}

const refreshed = await auth.refresh(current.refreshToken);
console.log(refreshed.accessToken);
Terminal refresh failures mean the user must sign in again:
  • invalid_refresh_token
  • refresh_expired
  • session_missing

Auth errors

Auth errors use the standard API error shape:
{
  "error": "invalid_access_token"
}
Common unauthenticated or reauthentication cases:
Error codeStatusMeaning
missing_access_token401The route requires an authenticated access token, but none was available to the route guard.
missing_bearer_token401The route expects an Authorization: Bearer ... header.
invalid_access_token401The access JWT is malformed, expired, signed by an unknown key, or otherwise failed verification.
access_token_expired401The access token is expired. Refresh and retry.
session_missing401 or 404The server-side session was revoked, expired, or missing. Reauthenticate.
access_jti_mismatch401The access token is no longer the current token for the session. Refresh or reauthenticate.
invalid_refresh_token401The refresh token is invalid, expired, or already consumed. Reauthenticate.
refresh_expiredSDK-sideThe SDK knows the stored refresh token is past its expiry. Reauthenticate.
no_auth_sessionSDK-sideAuth was enabled, but no session is loaded. Sign in first.
user_only403The route requires user auth.
admin_only403The route requires admin auth.
insufficient_role403The authenticated role cannot access the route.