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
| Layer | Credential | Lifetime | Purpose |
|---|---|---|---|
| Long-lived | API key id + secret + passphrase | Until rotated | Identify 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 minutes | Authorization: Bearer on REST, including encrypted trading routes. |
| WebSocket login | REST access_token (JWT), or legacy opaque api_key | Session | Required 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..."
}
| Field | Description |
|---|---|
access_token | Short-lived JWT bearer. sub is the Solana account (base58) on current protocol. |
token_type | Always Bearer. |
expires_in | Token lifetime in seconds (default 900 = 15 min). |
scope | Exactly 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:
- REST: the next request returns
401 token_expired. CallPOST /auth/tokenagain. - WebSocket: re-mint via
POST /auth/tokenand re-login (or reconnect); SDKs manage this path when configured with the long-lived key triple.
Public (Unauthenticated) Endpoints
The live unauthenticated REST endpoints are:
POST /auth/tokenGET /time,GET /rate-limitsGET /instruments,GET /instruments/{symbol}GET /tiers/config,GET /tiers/config/{tier_level}GET /stats/platform
GET /tiers/status is authenticated. POST /auth/token/revoke also requires a bearer token.
Errors
| Status | Code | Meaning |
|---|---|---|
400 | 1400 | Malformed request, missing fields, or unsupported grant_type. |
401 | 1401 | Missing/invalid credentials, expired token, or revoked token. |
403 | 1403 | Authenticated but forbidden, including insufficient scope or disallowed source IP. |
See Error Codes for the full reference.