> ## 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.

# Frontend Auth (JWT)

> Call the Struct API straight from your frontend, authenticated per end user, with no secret key in the browser and no proxy backend to build.

JWT public key auth lets your app call the Struct API **directly from the browser or a mobile client**, authenticated as the individual end user, without shipping a secret key and without building a backend of your own to sit in front of Struct.

## The problem it solves

Your `sk_*` secret key grants full access to your whole organisation, so it can never ship in a frontend bundle. The usual way around this is to build and operate a proxy backend: it holds the secret key, logs each of your users in, forwards their calls to Struct, and enforces a per-user rate limit so one user can't burn your whole quota. That proxy is real infrastructure to write, host, and pay for, and it adds a network hop to every request.

JWT public key auth removes that layer. You create a publishable `pk_jwt_*` key, point it at your auth provider's public keys, and your frontend talks to Struct directly. Struct verifies each end user's JWT, bills your organisation, and applies the per-user rate limit for you.

## What you get

* **No secret key in the browser.** The `pk_jwt_*` key is safe to hardcode in a frontend bundle. On its own it does nothing; every request also needs a valid JWT signed by your configured auth provider.
* **No proxy to build or run.** Skip the auth-and-forward backend and call Struct directly from the client. Less infrastructure to maintain and host, lower cost.
* **Per-user rate limits.** Set a `per_session_rpm` cap that Struct enforces per end user (keyed on the JWT `sub` claim), on top of your parent key's limits, so a single user can't exhaust your quota.
* **Any auth provider.** Works with Privy, Auth0, Clerk, Turnkey, Google, or your own issuer, as long as it signs JWTs with an asymmetric key.

See [JWT Public Keys](/dashboard/jwt-keys) for how to create and manage keys.

## Wire Format

Every request requires two credentials: your `pk_jwt_*` key (hardcoded in your app) and the user's JWT (obtained at login).

### REST

```bash theme={null}
curl https://api.struct.to/v1/polymarket/market \
  -H "X-Api-Key: pk_jwt_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" \
  -H "Authorization: Bearer <user_jwt>"
```

### WebSocket

```
wss://api.struct.to/ws?api-key=pk_jwt_a1b2c3d4e5f6a1b2c3d4&token=<user_jwt>
```

Both parameters are required on every request.

## Auth Flow

<Steps>
  <Step title="Request received">
    Request arrives with `X-Api-Key: pk_jwt_xxx` and `Authorization: Bearer <user_jwt>`.
  </Step>

  <Step title="Key lookup">
    The `pk_jwt_` prefix triggers the JWT public key auth flow. The key record is loaded from the database (cached 60 seconds). If the key is disabled or not found, the request returns `401`.
  </Step>

  <Step title="JWT verification">
    **JWKS URL keys:** The JWKS is fetched from the configured URL (cached 5 minutes). If the JWT's `kid` is not found in the cached set, the JWKS is re-fetched once to handle key rotation.

    **Inline public key:** The PEM or JWK JSON key is used directly to verify the JWT signature.

    Supported algorithms: RS256, RS384, RS512, ES256, ES384, EdDSA.
  </Step>

  <Step title="Claims validation">
    `aud` and `iss` claims are validated if configured on the key. If the JWT is expired or the signature is invalid, the request returns `401`.
  </Step>

  <Step title="Rate limiting and billing">
    The `sub` claim is extracted as the session identifier. The parent API key's rate limit and credit cap apply. If `per_session_rpm` is set, an additional per-user rate limit is checked (keyed on `pk_jwt_xxx:sub`).
  </Step>

  <Step title="Request proceeds">
    Credits are billed through the parent API key.
  </Step>
</Steps>

## Supported Algorithms

Only **asymmetric** JWT signing algorithms are supported:

| Family         | Algorithms          |
| -------------- | ------------------- |
| RSA            | RS256, RS384, RS512 |
| Elliptic Curve | ES256, ES384        |
| Edwards Curve  | EdDSA               |

<Warning>Symmetric algorithms (HS256, HS384, HS512) are **not supported**. JWTs signed with shared secrets will be rejected. Your auth provider must use asymmetric key pairs where the public key is exposed via a JWKS endpoint or provided inline.</Warning>

## Key Verification Methods

### JWKS URL

Provide a JWKS endpoint URL and Struct will fetch the public keys automatically. This is the standard approach for most auth providers. The JWKS is cached for 5 minutes, and if a JWT arrives with an unknown `kid`, the JWKS is re-fetched once to handle key rotation.

### Inline Public Key

Alternatively, provide the public key directly in PEM format (`-----BEGIN PUBLIC KEY-----`) or as JWK JSON (`{"kty":"RSA", ...}`). The key is used directly to verify JWT signatures without any external fetch.
