Authentication

GoDark uses long-lived API credentials (api_key id + api_secret + passphrase). How those credentials reach the edge differs slightly by transport.

Credential Model

LayerCredentialLifetimePurpose
Long-livedAPI key id + secret + passphraseUntil rotatedIdentify your trading principal. The passphrase is set at key-creation time and must be supplied with every token exchange or SDK login — it is not recoverable if lost.
Short-lived (REST)access_token (JWT Bearer)~15 minutesAuthorization: Bearer on REST, including encrypted trading routes.
WebSocket loginREST access_token (JWT), or legacy opaque api_keySessionRequired before private channels or encrypted trading on /ws/v1; public channels may be subscribed without login. Official SDKs mint the JWT via POST /auth/token and never put id:secret:passphrase on the socket.

Generate and manage key triples from the app under Settings → API Keys. The passphrase is shown once at creation; if you lose it, rotate the key.

Official SDKs take apiKeyId / apiSecret / passphrase (env: GODARK_API_KEY_ID, GODARK_API_SECRET, GODARK_PASSPHRASE).

Account identity (protocol)

Login and HPKE bind a 32-byte Solana account (L2 wallet pubkey, base58 on the wire and in JWT sub). Order headers and HPKE info use those 32 bytes — not a 16-byte UUID. The WebSocket login response also carries string-shaped account_id (acct_...) and session_id (sess_...) identifiers; neither replaces the 32-byte account used by HPKE.

REST — POST /auth/token

Exchange credentials for a bearer token. Public docs use an OAuth 2.0-style client_credentials JSON body.

POST /api/v1/auth/token

Request

Content-Type: application/json
{
  "grant_type": "client_credentials",
  "client_id": "<api_key_id>",
  "client_secret": "<api_secret>",
  "passphrase": "<passphrase>"
}

Response

{
  "code": 0,
  "data": {
    "access_token": "eyJhbGciOi...",
    "token_type": "Bearer",
    "expires_in": 900,
    "scope": "trade"
  },
  "timestamp": "2026-09-27T12:00:00Z",
  "request_id": "req_01HXYZ..."
}
FieldDescription
access_tokenShort-lived JWT bearer. sub is the Solana account (base58) on current protocol.
token_typeAlways Bearer.
expires_inToken lifetime in seconds (default 900 = 15 min).
scopeExactly one scope: trade for keys with trading permission, otherwise read.

The authenticated account is the JWT sub; the token response does not add a separate account field.

cURL

curl -X POST the REST origin/api/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "YOUR_API_KEY_ID",
    "client_secret": "YOUR_API_SECRET",
    "passphrase": "YOUR_PASSPHRASE"
  }'

POST /auth/token/revoke

Revoke the authenticated user's refresh sessions and outstanding access tokens.

POST /api/v1/auth/token/revoke
Authorization: Bearer <access_token>

Send no token body. Success is HTTP 200 with the normal envelope:

{
  "code": 0,
  "data": {
    "success": true,
    "user_id": "<account-identity>",
    "revoked": "refresh_sessions_and_access_tokens",
    "access_tokens_revoked": 1
  },
  "timestamp": "2026-09-27T12:00:00Z",
  "request_id": "req_01HXYZ..."
}

Using credentials

REST

Every authenticated REST request includes:

Authorization: Bearer <access_token>

Trading routes that place, cancel, or modify orders additionally send one-shot HPKE bodies — see Encryption & Transport. GodarkRestClient obtains the bearer via /auth/token, then seals each order body.

WebSocket

Login is required before private-channel subscriptions or encrypted trading; public WebSocket channels can be subscribed without it. Official SDKs mint a REST bearer via POST /api/v1/auth/token (client_credentials), then send that JWT as the WebSocket login token — they do not send id:secret:passphrase on the socket. A legacy opaque api_key (for example a custom host test-key-*) may still be sent as-is.

{
  "id": "c-1",
  "op": "login",
  "args": {
    "token": "<access_token>"
  }
}

A successful login returns account (32-byte identity), conn_id (required for HPKE), and session metadata. SDKs then run HPKE setup before encrypted trading. See WebSocket Trading.

Token Expiry (REST bearer)

Access tokens expire after about 15 minutes. On expiry:

Public (Unauthenticated) Endpoints

The live unauthenticated REST endpoints are:

GET /tiers/status is authenticated. POST /auth/token/revoke also requires a bearer token.

Errors

StatusCodeMeaning
4001400Malformed request, missing fields, or unsupported grant_type.
4011401Missing/invalid credentials, expired token, or revoked token.
4031403Authenticated but forbidden, including insufficient scope or disallowed source IP.

See Error Codes for the full reference.