{"record":{"id":"be07a60aaa15a510","repo":"odysseus-dev/odysseus","slug":"this-endpoint-requires-an-api-token","errorCode":null,"errorMessage":"This endpoint requires an API token","messagePattern":"This endpoint requires an API token","errorType":"http","errorClass":"HTTPException","httpStatus":403,"severity":"error","filePath":"routes/webhook/webhook_routes.py","lineNumber":240,"sourceCode":"        if model:\n            model_lower = model.lower()\n            for prefix, prov in MODEL_PROVIDER_MAP.items():\n                if model_lower.startswith(prefix):\n                    return KNOWN_PROVIDERS[prov]\n        return None\n\n    class SyncChatRequest(BaseModel):\n        message: str = Field(..., max_length=MAX_MESSAGE_LEN)\n        model: Optional[str] = Field(None, max_length=200)\n        session: Optional[str] = Field(None, max_length=100)\n        api_key: Optional[str] = Field(None, max_length=256)\n        base_url: Optional[str] = Field(None, max_length=MAX_URL_LEN)\n        provider: Optional[str] = Field(None, max_length=50)\n\n    @router.post(\"/v1/chat\")\n    async def sync_chat(request: Request, body: SyncChatRequest):\n        if not getattr(request.state, \"api_token\", False):\n            raise HTTPException(403, \"This endpoint requires an API token\")\n        scopes = set(getattr(request.state, \"api_token_scopes\", []) or [])\n        if \"chat\" not in scopes:\n            raise HTTPException(403, \"API token is not scoped for chat\")\n        token_owner = getattr(request.state, \"api_token_owner\", None)\n\n        from core.models import ChatMessage\n        from src.llm_core import llm_call_async\n        from src.endpoint_resolver import build_chat_url, build_headers, build_models_url, normalize_base\n\n        message = body.message.strip()\n        if not message:\n            raise HTTPException(400, \"Message is required\")\n\n        session_id = body.session\n        sess = None\n\n        # --- Case 1: Resume an existing session ---\n        if session_id and session_manager:","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/webhook/webhook_routes.py#L222-L258","documentation":"HTTP 403 from POST /v1/chat: the request carried no API token. The route checks request.state.api_token, which is set by API-token middleware; falsy means the Authorization header did not present a valid token (absent, malformed, or rejected upstream).","triggerScenarios":"Calling /v1/chat with no Authorization header; sending a session cookie but no API token (admin browser login does not satisfy this check); sending a malformed header the middleware drops without setting state.","commonSituations":"n8n/Make/Activepieces integration where the HTTP Request node was configured without the API-key auth preset; curl examples missing the header; assuming the admin UI session works for the sync endpoint.","solutions":["Send the API token in the Authorization header exactly as the middleware expects (check the API-token auth middleware for scheme, e.g. 'Authorization: Bearer <token>')","Create a token in the admin API-keys UI if none exists","In n8n/Make, switch the node's auth type to 'Generic Credential / Header Auth' or Bearer and paste the token"],"exampleFix":"# before\ncurl -X POST http://host/api/webhooks/v1/chat -d '{\"message\":\"hi\"}'\n\n# after\ncurl -X POST http://host/api/webhooks/v1/chat \\\n  -H 'Authorization: Bearer sk-...' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"message\":\"hi\"}'","handlingStrategy":"validation","validationCode":"# before the call: ensure a token is configured\nif not API_TOKEN:\n    raise ConfigError(\"Create an API token in Admin and set API_TOKEN\")","typeGuard":null,"tryCatchPattern":"if resp.status_code == 403 and 'requires an API token' in detail:\n    raise ConfigError('missing/invalid Authorization header for /v1/chat')","preventionTips":["Always send 'Authorization: Bearer <token>' on /v1/chat; browser sessions do not count","Smoke-test the token with a trivial request when setting up n8n/Make integrations"],"tags":["auth","api-token","fastapi","http-403","n8n"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}