Struct authenticates every REST, websocket, and webhook management request with an API key. Two key types are supported:
| Prefix | Use from | Description |
|---|
sk_live_ | Server-side code only | Secret key. Full access to your organisation. Inherits the org’s plan, rate limit, and credit cap. |
pk_jwt_ | Browsers, mobile apps, any untrusted client | Public key. Useless without a valid JWT from your configured auth provider. Safe to ship in frontend bundles. |
Generate keys from the Dashboard. Secret keys are shown only once at creation.
REST
Send the key in the X-API-Key header on every request:
curl https://api.struct.to/v1/markets \
-H "X-API-Key: sk_live_YOUR_KEY"
For JWT public-key auth, send both credentials:
curl https://api.struct.to/v1/markets \
-H "X-API-Key: pk_jwt_YOUR_KEY" \
-H "Authorization: Bearer USER_JWT"
WebSocket
Browsers can’t send custom headers on WebSocket connections, so credentials are passed as query parameters:
wss://api.struct.to/ws?api-key=sk_live_YOUR_KEY
wss://api.struct.to/ws/alerts?api-key=sk_live_YOUR_KEY
JWT public-key auth adds a token parameter:
wss://api.struct.to/ws?api-key=pk_jwt_YOUR_KEY&token=USER_JWT
SDK
The TypeScript SDK accepts the same credentials. The same apiKey field accepts secret and public keys; pass jwt (or a rotating getJwt) alongside pk_jwt_*.
import { StructClient } from "@structbuild/sdk";
const client = new StructClient({
apiKey: "sk_live_YOUR_KEY",
});
const userClient = new StructClient({
apiKey: "pk_jwt_YOUR_KEY",
jwt: session.accessToken,
});
See SDK Authentication for the JWT rotation pattern.
Rotating keys
To rotate a secret key:
- Generate a new
sk_live_* key in the dashboard.
- Roll the new key out to your servers.
- Disable the old key in the dashboard.
Disabled keys reject new requests immediately. Open websocket connections stay live until they disconnect normally; reconnect attempts will fail authentication.
JWT public keys rotate transparently when you point them at a new JWKS URL. Cached JWKS entries are refreshed every 5 minutes, or immediately when a JWT presents an unknown kid.
JWT public keys
pk_jwt_* keys let your end users authenticate directly from a browser or mobile app using a JWT from your auth provider (Privy, Auth0, Clerk, Turnkey, Google, custom). The Struct API verifies the JWT signature against your configured public key and applies per-user rate limits keyed on the JWT sub claim.
See Frontend Auth (JWT) for the full flow, supported algorithms, and JWKS configuration. Manage keys at Dashboard › JWT Keys.
Symmetric algorithms (HS256, HS384, HS512) are not supported. Only asymmetric families (RS*, ES*, EdDSA) are accepted, because your public key needs to be safe to publish.
Error responses
| Status | Meaning | Action |
|---|
401 Unauthorized | Missing or malformed credentials. | Check the X-API-Key header is present and not empty. |
401 Unauthorized | Invalid key, expired JWT, or signature mismatch. | Verify the key value and JWT signing algorithm. |
403 Forbidden | Key disabled or org over its credit cap. | Re-enable the key or top up credits in the dashboard. |
429 Too Many Requests | Rate limit exceeded. | Back off and retry. See Rate Limits. |
On WebSocket, auth failures arrive as the auth_failed event (SDK) or a close frame with code 1008. Reconnect attempts are not retried automatically on auth failures, since the credentials need to be fixed first.
Security checklist
- Use
sk_live_* only from servers you control. Never embed in frontend bundles, mobile apps, or public repos.
- Use
pk_jwt_* for every browser-side or mobile call. Pair with a short-lived JWT (15 minutes or less) signed by your auth provider.
- Scope work to multiple keys when you have distinct environments or services so you can rotate without coordinated downtime.
- Monitor key usage in the dashboard. A key that suddenly spikes is the first sign it’s been leaked.