> ## Documentation Index
> Fetch the complete documentation index at: https://docs.struct.to/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> API keys, JWT public keys, and how to authenticate every Struct request.

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](https://struct.to/dashboard). Secret keys are shown only once at creation.

## REST

Send the key in the `X-API-Key` header on every request:

```bash theme={null}
curl https://api.struct.to/v1/polymarket/market \
  -H "X-API-Key: sk_live_YOUR_KEY"
```

For JWT public-key auth, send both credentials:

```bash theme={null}
curl https://api.struct.to/v1/polymarket/market \
  -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_*`.

<CodeGroup>
  ```typescript SDK theme={null}
  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,
  });
  ```

  ```bash cURL theme={null}
  curl https://api.struct.to/v1/polymarket/market \
    -H "X-API-Key: sk_live_YOUR_KEY"
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      "https://api.struct.to/v1/polymarket/market",
      headers={"X-API-Key": "sk_live_YOUR_KEY"},
  )
  ```
</CodeGroup>

See [SDK Authentication](/sdk/authentication) for the JWT rotation pattern.

## Rotating keys

To rotate a secret key:

1. Generate a new `sk_live_*` key in the dashboard.
2. Roll the new key out to your servers.
3. 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)](/api-reference/jwt-auth) for the full flow, supported algorithms, and JWKS configuration. Manage keys at [Dashboard › JWT Keys](/dashboard/jwt-keys).

<Warning>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.</Warning>

## 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](/guides/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.
