Codumentor logo Codumentor

API Routes

Plugins can expose HTTP endpoints via FastAPI routers. This guide covers creating routes, authentication, and accessing shared state.

See also: Backend Hooks, Security


Creating API Routes

Return a FastAPI APIRouter from the api_router() method on your plugin class:

def api_router(self):
    from fastapi import APIRouter, Request
    router = APIRouter(tags=["my_plugin"])

    @router.post("/action")
    async def do_action(request: Request):
        return {"result": "success"}

    return router

Routes are mounted at /ui/plugins/{plugin_id}/. For example, a plugin with plugin_id = "my_plugin" that defines a /action route will be accessible at:

POST /ui/plugins/my_plugin/action

Authentication (Automatic)

All plugin routes are automatically protected — no code required from plugin developers.

The system injects authentication middleware that:

  1. Requires authentication — Every request must have a valid session
  2. Verifies conversation ownership — If {conversation_id} is in the route path, the authenticated user must own that conversation

Route Pattern Behavior

Route PatternBehavior
/votes/{conversation_id}Auth required + ownership verified
/stats/allAuth required only

Accessing the User

The authenticated user is available via request.state, injected by the auth middleware:

@router.get("/data/{conversation_id}")
async def get_data(conversation_id: str, request: Request):
    # User is available via request.state (injected by auth middleware)
    user = request.state.user
    print(f"Request from user: {user.id}")

    # conversation_id ownership is already verified by middleware
    # No need to check manually
    return {"user_id": user.id, "conversation_id": conversation_id}

Security Notes


Accessing Shared State

Use request.app.state for lazy-initialized shared stores:

@router.get("/data")
async def get_data(request: Request):
    # Lazy-init stores via app state
    store = getattr(request.app.state, "my_store", None)
    if store is None:
        store = MyStore()
        request.app.state.my_store = store
    return store.get_all()

This pattern ensures your store is created once and shared across all requests to your plugin.