> ## Documentation Index
> Fetch the complete documentation index at: https://metacognition-fdc534de-master.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication & API keys

> How your API key becomes a JWT, how the SDK refreshes it, and where to store secrets in dev and prod.

export const AuthSequence = ({phases}) => <div className="not-prose my-6">
    <figure className="rounded-xl border border-zinc-950/15 bg-zinc-950/[0.02] p-4 dark:border-white/15 dark:bg-white/[0.04]">
      <div className="flex flex-col gap-3">
        {phases.map((p, i) => <div key={p.id} className="grid grid-cols-[28px_1fr] items-start gap-3">
            <div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg border border-[#F32C05]/45 bg-[#F32C05]/15 text-xs font-bold text-zinc-900 dark:border-[#FF5530]/50 dark:bg-[#F32C05]/20 dark:text-zinc-50">
              {i + 1}
            </div>
            <div className="min-w-0">
              <div className="text-sm font-semibold text-zinc-900 dark:text-zinc-50">{p.title}</div>
              <div className="mt-1.5 text-sm leading-relaxed text-zinc-600 dark:text-zinc-400">
                {p.detail}
              </div>
            </div>
          </div>)}
      </div>
    </figure>
  </div>;

<Info>
  If you only need a working client, start with the [Quickstart](/quickstart). Come back here when you are wiring secrets, using your own JWT, or rotating keys.
</Info>

The SDK starts with your API key. On the first real call, it exchanges that key for short-lived access and refresh tokens.

After that, the client keeps the tokens in memory, refreshes them when needed, and retries the original call. Most apps never need to handle raw token strings.

## Flow

The diagram shows what happens when your app calls the SDK.

```mermaid theme={null}
sequenceDiagram
  autonumber
  participant App as Your app
  participant SDK as Tex SDK
  participant API as Tex API

  App->>SDK: Tex(api_key=...)
  note over SDK: Lazy — no network until a real method runs
  App->>SDK: tex.recall(...) / remember / usage
  SDK->>API: POST /auth/token-exchange
  API-->>SDK: access_token (24h) + refresh_token (7d)
  SDK->>API: Product call (Authorization: Bearer access_token)
  API-->>SDK: 200 + payload
  SDK-->>App: return value

  note over App,API: Later: access token expired
  App->>SDK: tex.recall(...)
  SDK->>API: Product call (expired Bearer)
  API-->>SDK: 401
  SDK->>API: POST /auth/refresh
  API-->>SDK: new access_token
  SDK->>API: Retry product call
  API-->>SDK: 200 + payload
  SDK-->>App: return value
```

### Steps in the SDK

<AuthSequence
  phases={[
{
  id: "construct",
  title: "You build the client",
  detail: "Tex(api_key=...) does not call the network until you run a real method.",
},
{
  id: "first",
  title: "You call recall/remember/usage",
  detail: "The SDK calls POST /auth/token-exchange and receives access (24h) and refresh (7d) JWTs.",
},
{
  id: "steady",
  title: "SDK attaches Bearer access token",
  detail: "Your call runs with Authorization: Bearer <access_token>.",
},
{
  id: "refresh",
  title: "When access expires",
  detail: "The SDK calls POST /auth/refresh, gets a new access token, and retries once.",
},
]}
/>

## Auth mode

Most apps use an API key. Use one of the other modes only when you already manage auth somewhere else.

<Tabs>
  <Tab title="API key (recommended)">
    ```python theme={null}
    tex = Tex(
        api_key="tex_live_…",
        base_url="https://api.getmetacognition.com",
    )
    ```

    This is the default for most apps. The SDK handles token exchange and refresh.
  </Tab>

  <Tab title="Bring your own JWT">
    ```python theme={null}
    tex = Tex(
        access_token=jwt_from_my_auth_service,
        refresh_token=refresh_jwt,           # optional
        org_id="org_abc",                    # required with BYO-JWT
        user_id="u_xyz",                     # required with BYO-JWT
        base_url="https://api.getmetacognition.com",
    )
    ```

    Use this when another service already creates the JWTs.

    <Warning>
      BYO-JWT does **not** auto-fill `org_id` / `user_id` from `/auth/verify`. Pass them explicitly. Every `remember` and `recall` needs them in the request scope.
    </Warning>
  </Tab>

  <Tab title="Org + user login (debug only)">
    ```python theme={null}
    tex = Tex(
        org_id="org_abc",
        user_id="u_xyz",
        base_url="http://localhost:8000",
    )
    ```

    <Warning>
      `/auth/login` is **disabled** in production. It returns 403 unless the integration backend runs with `DEBUG=true`. Use this only for local development against a self-hosted backend. In production, use an API key.
    </Warning>
  </Tab>
</Tabs>

## Key storage

<CardGroup cols={2}>
  <Card title="Local dev" icon="laptop">
    `.env` file. Add it to `.gitignore`. Load with `python-dotenv`.
  </Card>

  <Card title="Docker / Kubernetes" icon="boxes-stacked">
    Secret manager mounted as `TEX_API_KEY` env var.
  </Card>

  <Card title="Vercel / Netlify" icon="cloud">
    Project environment variable named `TEX_API_KEY`.
  </Card>

  <Card title="GitHub Actions" icon="github">
    Repository secret exposed as `${{ secrets.TEX_API_KEY }}`.
  </Card>
</CardGroup>

The SDK reads `TEX_API_KEY` from the environment automatically when `api_key=` is omitted.

## Rotation

<Steps>
  <Step title="Mint key B">
    [Dashboard → API Keys → New key](https://app.getmetacognition.com/dashboard/keys).
  </Step>

  <Step title="Roll out">
    Deploy with `TEX_API_KEY=<key B>`.
  </Step>

  <Step title="Verify">
    Check the dashboard's `last_used_at` value or your own logs.
  </Step>

  <Step title="Revoke key A">
    Click **Revoke** on the old key. JWTs created from key A can keep working for up to 24h, so customers do not see a sudden failure.
  </Step>
</Steps>

## Bad key

<CodeGroup>
  ```python Python theme={null}
  from tex import Tex, AuthenticationError

  try:
      tex = Tex(api_key="tex_live_BOGUS", base_url="https://api.getmetacognition.com")
      tex.usage.today()
  except AuthenticationError as e:
      print(e.status_code)   # 401
      print(e.message)       # "Invalid API key" or similar
      print(e.request_id)    # Quote this when filing tickets
  ```

  ```bash cURL theme={null}
  $ curl -X POST https://api.getmetacognition.com/auth/token-exchange \
      -H 'content-type: application/json' \
      -d '{"api_key": "tex_live_BOGUS"}'

  {"error":"HTTP 401","message":"Invalid API key","request_id":"…"}
  ```
</CodeGroup>

<Note>
  **Token lifetimes.** Access JWTs last 24h. Refresh JWTs last 7d. After that, the SDK exchanges your API key again. To invalidate tokens, revoke the API key they came from.
</Note>

<Card title="Next: multi-user memory" icon="layer-group" href="/concepts/scopes" horizontal>
  How `org_id` / `user_id` / `session_id` partition memory.
</Card>
