Skip to main content

    Python FastAPI Quickstart

    Build a chat backend that mints per-user JWTs and proxies to Behest. Works for any Python frontend, mobile app, or server-to-server flow.

    Prerequisites: Python 3.10+, a Behest project + API key + slug.


    1. Configure CORS (only if a browser will call Behest directly)

    If your frontend calls Behest from the browser with the minted JWT — the recommended pattern below — add every browser origin to Project → Settings → Allowed Origins in the dashboard:

    http://localhost:3000
    http://localhost:5173
    https://your-app.com
    

    Click Save. Skip this step if FastAPI is the only caller (server-to-server).


    2. Install

    bash
    pip install fastapi uvicorn "behest-ai>=1.5" python-multipart

    behest>=1.5 bundles the v1.5 dual-mode SDK (API-key mode + local-sign mode) on top of the OpenAI SDK.


    3. Token endpoint

    Security: derive user_id and tier from a server-verified session (your session cookie, auth provider, or identity JWT — see integrations/supabase.md, clerk.md, auth0.md). Never accept them from the request body or a client-controlled header — any caller could mint a JWT for any user and spoof the entire tenant.

    python
    # app.py
    import os
    from fastapi import FastAPI, HTTPException, Depends
    from behest import Behest
     
    from .auth import require_user  # your app's verified-session dependency
     
    app = FastAPI()
     
    # Reads BEHEST_KEY and BEHEST_BASE_URL from env.
    # behest_sk_live_* → apiKey mode. behest_pk_* (tenant PEM) → local-sign mode.
    behest = Behest()
     
    @app.post("/api/behest/token")
    async def mint_token(user = Depends(require_user)):
        # user.id and user.plan come from YOUR verified session — never from the request body.
        try:
            result = await behest.auth.mint(user_id=user.id, tier=user.plan or 1, ttl=900)
        except Exception as e:
            raise HTTPException(500, str(e))
        return {
            "token": result.token,
            "ttl": result.ttl,
            "sessionId": result.session_id,
            "expiresAt": result.expires_at,
        }

    Local signing alternative

    If BEHEST_KEY starts with behest_pk_ (a tenant RSA PEM), the same code above switches to local-sign mode — no HTTP round-trip per mint. You also need BEHEST_KID, BEHEST_TENANT_ID, BEHEST_PROJECT_ID in env. See auth-modes.


    4. Server-to-server chat

    python
    # chat.py
    from behest import Behest
     
    behest = Behest()  # reads BEHEST_KEY + BEHEST_BASE_URL from env
     
    async def chat_for_user(user_id: str, messages: list):
        # Pass user_id on the chat call — the SDK auto-mints a per-user JWT for this request.
        stream = await behest.chat.completions.create(
            messages=messages,
            stream=True,
            user_id=user_id,
            # model is optional — Behest uses your project default when omitted
        )
        async for chunk in stream:
            yield chunk.choices[0].delta.content or ""

    Your frontend fetches a JWT from /api/behest/token, then calls Behest directly. Kong enforces the allowed origins you configured in step 1.

    ts
    // Browser — fetch token
    async function fetchBehestToken() {
      const r = await fetch("/api/behest/token", {
        method: "POST",
        credentials: "include",
      });
      return (await r.json()) as { token: string; ttl: number; sessionId: string };
    }
     
    // Browser — stream a chat completion
    const { token, sessionId } = await fetchBehestToken();
    const resp = await fetch(`${BEHEST_BASE_URL}/v1/chat/completions`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
        "X-Session-Id": sessionId,
      },
      body: JSON.stringify({ messages, stream: true }),
    });

    Only add a FastAPI streaming proxy if you need central logging or to inject server-only data into every prompt; most apps prefer browser-direct.


    6. Run

    bash
    BEHEST_KEY=behest_sk_live_... BEHEST_BASE_URL=https://amber-fox-042.behest.app uvicorn app:app --reload
    bash
    # /token requires a verified session — call it from your authenticated client, not curl.

    Next steps

    Enterprise Token FinOps: Enforce hard budgets and attribute costs per session.

    Learn more