Tawa docs
Cookbook

FastAPI

Wire a scan into a FastAPI endpoint with the async client.

A minimal pattern using AsyncTawaGuard inside a FastAPI app.

main.py
from fastapi import FastAPI, HTTPException
from tawaguard import AsyncTawaGuard

app = FastAPI()

# Constructed once at import time, shared across requests. Lazy by default
# (no verification round trip on import) — see Configuration if you want to
# verify eagerly during startup instead.
tawa = AsyncTawaGuard("YOUR_API_KEY")

_BLOCKED_ACTIONS = {"block", "hard_block", "soft_block"}


@app.post("/chat")
async def chat(message: str):
    scan = await tawa.analyze(message)

    if scan.action in _BLOCKED_ACTIONS:
        raise HTTPException(
            status_code=403,
            detail={"error": "Message blocked by content policy.", "rationale": scan.rationale},
        )

    # ... forward `message` to your model of choice ...
    return {"ok": True}


@app.on_event("shutdown")
async def shutdown():
    await tawa.aclose()

Verifying eagerly on startup

Constructing AsyncTawaGuard directly defers key verification to the first request. To catch a bad key at startup instead, verify explicitly in a lifespan handler:

from contextlib import asynccontextmanager

from fastapi import FastAPI
from tawaguard import AsyncTawaGuard

tawa: AsyncTawaGuard


@asynccontextmanager
async def lifespan(app: FastAPI):
    global tawa
    tawa = await AsyncTawaGuard.create("YOUR_API_KEY")
    yield
    await tawa.aclose()


app = FastAPI(lifespan=lifespan)

See Configuration for the full breakdown of eager vs. lazy verification.

On this page