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

# Authentication

> Choose the right auth pattern for server-side and browser integrations.

# Authentication

Kynasmith uses short-lived bearer access tokens for API calls. The SDKs handle token management for you.

## Server-side authentication

Use your project-bound API key to initialize the Python SDK. The SDK automatically exchanges your credentials for a short-lived access token and refreshes it as needed.

```python theme={null}
from kynasmith import KynasmithClient

client = KynasmithClient(
    api_key="ks_key_123:secret_123",
)
```

The `api_key` value combines your key ID and secret in the format `key_id:key_secret`. You can find both values in the portal under **Settings > API Keys**.

<Accordion title="Alternative credential formats">
  You can also bootstrap with separate key ID and secret fields, or with a service account:

  ```python theme={null}
  # Separate key fields
  client = KynasmithClient(
      api_key_id="key_123",
      api_key_secret="secret_123",
  )

  # Service account
  client = KynasmithClient(
      service_account_id="svc_123",
      service_account_secret="svc_secret_123",
  )
  ```
</Accordion>

## Browser authentication

Do not embed long-lived API keys or service-account secrets in the browser.

Instead, expose a backend endpoint that exchanges your API key for a short-lived Kynasmith access token, then pass it through a `tokenProvider`:

```ts theme={null}
import { createClient } from "@kynasmith/web-sdk";

const client = createClient({
  tokenProvider: async () => {
    const response = await fetch("/api/kynasmith/token", { method: "POST" });
    const { access_token } = await response.json();
    return access_token;
  },
});
```

### Backend token endpoint example

Your backend endpoint should call the Kynasmith token exchange API:

```python theme={null}
# Example backend endpoint (using any Python web framework)
import httpx

async def exchange_token():
    """Mint a short-lived Kynasmith access token for the browser client."""
    async with httpx.AsyncClient() as http:
        resp = await http.post(
            "https://api.kynasmith.dev/api/auth/access-tokens",
            json={
                "api_key_id": "key_123",
                "api_key_secret": "secret_123",
                "scopes": ["sessions:write"],
            },
        )
        resp.raise_for_status()
        return resp.json()
```

The response contains an `access_token` field with the short-lived bearer token your frontend needs.

<Accordion title="Advanced: static access token">
  If you already have a short-lived bearer token, you can pass it directly instead of a `tokenProvider`:

  ```ts theme={null}
  const client = createClient({
    accessToken: "ks_at_...",
  });
  ```

  When using a static `accessToken`, you are responsible for refresh and rotation.
</Accordion>

## Access token scopes

When minting an access token, you can restrict its capabilities with scopes. If no scopes are specified, the token inherits the full scope of the credential that minted it.

| Scope             | Description                                                       |
| ----------------- | ----------------------------------------------------------------- |
| `movespecs:read`  | Read MoveSpec resources, drafts, versions, and validation results |
| `movespecs:write` | Create, update, validate, version, release, and fork MoveSpecs    |
| `sessions:read`   | Read detection session status and results                         |
| `sessions:write`  | Create detection sessions and connect to realtime streams         |
| `tokens:read`     | List and inspect access tokens                                    |
| `tokens:write`    | Create and revoke access tokens                                   |

Use the narrowest scope set that your integration requires. Browser integrations typically need only `sessions:write` (and optionally `movespecs:read`).

## Token lifetime notes

* Browser integrations should treat `tokenProvider` as a refresh hook, not as one-time bootstrap. The SDK may call it before each authenticated request.
* The Python SDK automatically refreshes access tokens as they approach expiry.
* If you pass a bearer token directly through `accessToken` or `access_token`, you are responsible for refresh and rotation.

## Direct API integration

If you are integrating without an official SDK, use:

1. `POST /api/auth/access-tokens` to mint a bearer access token
2. `Authorization: Bearer <access_token>` header on all subsequent API calls
3. `POST /api/detection/sessions` to create a session

See the [API reference](/api-reference/overview) for the full endpoint documentation.

If token exchange, scopes, or bearer auth fail, see [Authentication troubleshooting](/troubleshooting/authentication).
