{"openapi":"3.1.0","info":{"title":"Seerah AI","description":"\nRAG-powered Islamic Q&A over the Quran, Hadith, scholarly fatwa and Seerah history.\n\n## Two ways in\n\n| Caller | Scheme | Header | Endpoints |\n|---|---|---|---|\n| **External applications** | API key | `X-API-Key: sk_live_…` | `/api/v1/**` |\n| **Web / mobile end users** | Supabase Auth JWT | `Authorization: Bearer <jwt>` | `/api/chat`, `/api/conversations/**`, `/api/auth/**` |\n\nThe two are independent. An external app integrates purely with an API key and\nnever touches Supabase Auth; the browser app keeps signing users in through\nSupabase exactly as before.\n\n## Getting an API key\n\n1. `POST /api/v1/auth/request-access` — public. Describe your integration; you\n   get back a `request_id`.\n2. An administrator reviews the queue (`GET /api/v1/auth/requests`) and either\n   approves it (`…/approve`, which mints your key) or rejects it (`…/reject`).\n3. **The key is shown exactly once**, in the approval response. It is stored\n   only as a peppered hash and cannot be recovered — if you lose it, rotate.\n\n## Roles\n\n* **`client`** — the normal integration role. Calls `/api/v1/chat` and the\n  conversation endpoints, and can rotate or revoke *its own* key.\n* **`admin`** — additionally reviews access requests and mints, rotates or\n  revokes *any* key. The last active admin key cannot be revoked.\n\n### Admin endpoints\n\n`GET /api/v1/auth/requests`, `…/approve`, `…/reject`, `POST /api/v1/auth/keys`,\n`GET /api/v1/auth/keys`, and passing a `key_id` to `…/keys/rotate` or\n`…/keys/revoke` all require an **admin API key**.\n\nThere is no separate admin credential or header. Send the admin key in the same\n`X-API-Key` header:\n\n```\nX-API-Key: <ADMIN_BOOTSTRAP_KEY>\n```\n\nThat value is the deployment's admin key — the `ADMIN_BOOTSTRAP_KEY` setting,\nor a key later minted with `\"role\": \"admin\"`. In Swagger UI, paste it into the\n**Authorize** dialog's `ApiKeyAuth` field before trying these operations.\n\nA client key gets `403 This endpoint requires an admin API key.` Check what you\nare holding with `GET /api/v1/auth/keys/me` — an admin key reports\n`\"role\": \"admin\"` and `\"scopes\": [\"*\"]`.\n\nIf `ADMIN_BOOTSTRAP_KEY` was left unset, the first admin key was generated and\nprinted to the server log exactly once at first start.\n\n## Using a key\n\n```bash\ncurl -X POST \"$BASE_URL/api/v1/chat\" \\\n  -H \"X-API-Key: $SEERAH_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"query\": \"What does the Quran say about patience?\", \"end_user_id\": \"user-8842\"}'\n```\n\nPass an `end_user_id` to keep each of your own users' threads separate; every\nconversation is namespaced to `(your key, end_user_id)`, so no integration can\never read another's data.\n\n## Errors and limits\n\n`401` invalid / revoked / expired key · `403` role or scope insufficient ·\n`404` unknown or not-yours resource · `409` state conflict (already reviewed,\nduplicate request) · `429` rate limited — the `Retry-After` and\n`X-RateLimit-*` response headers tell you when to try again.\n","contact":{"name":"Seerah AI support","email":"support@seerahai.karmat-hq.com"},"license":{"name":"Proprietary"},"version":"1.1.0"},"servers":[{"url":"https://api-seerahai.karmat-hq.com","description":"This deployment"}],"paths":{"/api/auth/config":{"get":{"tags":["auth","auth"],"summary":"Auth Config","description":"Public Supabase project config used by the frontend JS client.","operationId":"auth_config_api_auth_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthConfig"}}}}}}},"/api/auth/me":{"get":{"tags":["auth","auth"],"summary":"Me","operationId":"me_api_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPublic"}}}}},"security":[{"SupabaseBearer":[]}]}},"/api/conversations":{"get":{"tags":["conversations","conversations"],"summary":"List Conversations","operationId":"list_conversations_api_conversations_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ConversationSummary"},"type":"array","title":"Response List Conversations Api Conversations Get"}}}}},"security":[{"SupabaseBearer":[]}]}},"/api/conversations/{conversation_id}":{"get":{"tags":["conversations","conversations"],"summary":"Get Conversation","operationId":"get_conversation_api_conversations__conversation_id__get","security":[{"SupabaseBearer":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"integer","title":"Conversation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/routers__conversations__ConversationDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["conversations","conversations"],"summary":"Remove Conversation","operationId":"remove_conversation_api_conversations__conversation_id__delete","security":[{"SupabaseBearer":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"integer","title":"Conversation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/chat":{"post":{"tags":["chat"],"summary":"Chat","description":"Main chat endpoint which runs the full state machine pipeline.","operationId":"chat_api_chat_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"SupabaseBearer":[]}]}},"/api/":{"get":{"tags":["chat"],"summary":"Health","description":"System Health check","operationId":"health_api__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusResponse"}}}}}}},"/api/admin/logs":{"get":{"tags":["admin","Logging"],"summary":"Get System Logs","operationId":"get_system_logs_api_admin_logs_get","parameters":[{"name":"level","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Level"}},{"name":"module","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Module"}},{"name":"func_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Func Name"}},{"name":"extra_filter","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"title":"Extra Filter"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DBLog"},"title":"Response Get System Logs Api Admin Logs Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/request-access":{"post":{"tags":["API keys"],"summary":"Request Access","description":"Ask for an API key. **No authentication required.**\n\nCreates a pending access request an administrator then approves or rejects.\nKeep the returned `request_id` — it is how you track the outcome.","operationId":"request_access_api_v1_auth_request_access_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessRequestCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessRequestAck"}}}},"409":{"description":"A pending request already exists for this email."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/requests":{"get":{"tags":["API keys"],"summary":"List Access Requests","description":"Review the access-request queue. **Requires an admin API key.**\n\nSend the admin key as `X-API-Key: <ADMIN_BOOTSTRAP_KEY>`.","operationId":"list_access_requests_api_v1_auth_requests_get","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"Admin API key.","schema":{"type":"string","title":"Admin API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/AccessRequestStatus"},{"type":"null"}],"description":"Filter by review status.","title":"Status"},"description":"Filter by review status."},{"name":"contact_email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Contact Email"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AccessRequestOut"},"title":"Response List Access Requests Api V1 Auth Requests Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/requests/{request_id}/approve":{"post":{"tags":["API keys"],"summary":"Approve Access Request","description":"Approve a pending request and issue its API key. **Admin only.**\n\nSend the admin key as `X-API-Key: <ADMIN_BOOTSTRAP_KEY>`.\n\nThe response carries the full key — the only time it is ever returned.","operationId":"approve_access_request_api_v1_auth_requests__request_id__approve_post","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"Admin API key.","schema":{"type":"string","title":"Admin API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"},{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ApproveRequest"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyIssued"}}}},"404":{"description":"No such request."},"409":{"description":"Already reviewed."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/requests/{request_id}/reject":{"post":{"tags":["API keys"],"summary":"Reject Access Request","description":"Reject a pending request, recording why. **Admin only.**\n\nSend the admin key as `X-API-Key: <ADMIN_BOOTSTRAP_KEY>`.","operationId":"reject_access_request_api_v1_auth_requests__request_id__reject_post","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"Admin API key.","schema":{"type":"string","title":"Admin API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"},{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessRequestOut"}}}},"404":{"description":"No such request."},"409":{"description":"Already reviewed."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/keys":{"post":{"tags":["API keys"],"summary":"Create Key","description":"Mint a key directly, skipping the request queue. **Admin only.**\n\nSend the admin key as `X-API-Key: <ADMIN_BOOTSTRAP_KEY>`.\n\nUse this to provision a partner you already trust, or a second admin key.","operationId":"create_key_api_v1_auth_keys_post","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyIssued"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"Admin API key.","schema":{"type":"string","title":"Admin API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"}]},"get":{"tags":["API keys"],"summary":"List Keys","description":"List issued keys and their usage. Secrets are never included. **Admin only.**\n\nSend the admin key as `X-API-Key: <ADMIN_BOOTSTRAP_KEY>`.","operationId":"list_keys_api_v1_auth_keys_get","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"Admin API key.","schema":{"type":"string","title":"Admin API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/ApiKeyStatus"},{"type":"null"}],"title":"Status"}},{"name":"role","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/ApiRole"},{"type":"null"}],"title":"Role"}},{"name":"owner_email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Email"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyOut"},"title":"Response List Keys Api V1 Auth Keys Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/keys/rotate":{"post":{"tags":["API keys"],"summary":"Rotate Key","description":"Replace a key with a fresh secret, keeping its role, scopes and limits.\n\nA client key may rotate itself; an admin key may rotate any key by `key_id`\n— pass the admin key as `X-API-Key: <ADMIN_BOOTSTRAP_KEY>` to do that.\nSet `grace_minutes` to keep the old secret alive during a deployment.","operationId":"rotate_key_api_v1_auth_keys_rotate_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RotateRequest"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyIssued"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"API key.","schema":{"type":"string","title":"API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"}]}},"/api/v1/auth/keys/revoke":{"post":{"tags":["API keys"],"summary":"Revoke Key","description":"Permanently disable a key.\n\nA client key may revoke itself; an admin key may revoke any key by `key_id`\n— pass the admin key as `X-API-Key: <ADMIN_BOOTSTRAP_KEY>` to do that.\nRevoking is irreversible — issue a new key instead of un-revoking.","operationId":"revoke_key_api_v1_auth_keys_revoke_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RevokeRequest"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"API key.","schema":{"type":"string","title":"API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"}]}},"/api/v1/auth/keys/me":{"get":{"tags":["API keys"],"summary":"Whoami","description":"Introspect the key making this call — role, scopes, limits and usage.","operationId":"whoami_api_v1_auth_keys_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyOut"}}}}},"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"API key.","schema":{"type":"string","title":"API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"}]}},"/api/v1/chat":{"post":{"tags":["Integration"],"summary":"Ask a question","description":"Run the full retrieval + reasoning pipeline for one question.\n\nOmit `conversation_id` to start a thread; pass the one you got back to\ncontinue it with full multi-turn context.","operationId":"integration_chat_api_v1_chat_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationChatRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationChatResponse"}}}},"401":{"description":"Missing or invalid API key."},"429":{"description":"Rate limit exceeded for this key."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"API key.","schema":{"type":"string","title":"API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"}]}},"/api/v1/conversations":{"get":{"tags":["Integration"],"summary":"List conversations","description":"Threads belonging to one of your end users, most recently updated first.","operationId":"list_conversations_api_v1_conversations_get","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"API key.","schema":{"type":"string","title":"API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"},{"name":"end_user_id","in":"query","required":false,"schema":{"type":"string","maxLength":128,"default":"default","title":"End User Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ConversationSummary"},"title":"Response List Conversations Api V1 Conversations Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/conversations/{conversation_id}":{"get":{"tags":["Integration"],"summary":"Read a conversation","description":"Full message history for one thread.","operationId":"get_conversation_api_v1_conversations__conversation_id__get","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"API key.","schema":{"type":"string","title":"API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"integer","title":"Conversation Id"}},{"name":"end_user_id","in":"query","required":false,"schema":{"type":"string","maxLength":128,"default":"default","title":"End User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/routers__integration__ConversationDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Integration"],"summary":"Delete a conversation","description":"Delete a thread and every message in it.","operationId":"delete_conversation_api_v1_conversations__conversation_id__delete","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"API key.","schema":{"type":"string","title":"API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"integer","title":"Conversation Id"}},{"name":"end_user_id","in":"query","required":false,"schema":{"type":"string","maxLength":128,"default":"default","title":"End User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/status":{"get":{"tags":["Integration"],"summary":"Integration status","description":"Knowledge-base readiness plus the limits attached to your key.","operationId":"integration_status_api_v1_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationStatus"}}}}},"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-API-Key","in":"header","required":true,"description":"API key.","schema":{"type":"string","title":"API key","examples":["sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"]},"example":"sk_live_xxxxxxxxxxxxxxxx_EXAMPLEKEYNOTREALdoNOTuseTHISvalue00"}]}},"/health":{"get":{"tags":["health"],"summary":"Health Check","description":"Dedicated health check endpoint","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusResponse"}}}}}}}},"components":{"schemas":{"AccessRequestAck":{"properties":{"request_id":{"type":"string","title":"Request Id"},"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"}},"type":"object","required":["request_id","status","message"],"title":"AccessRequestAck","description":"What the applicant gets back — no key, just a tracking id."},"AccessRequestCreate":{"properties":{"organization":{"type":"string","maxLength":120,"minLength":2,"title":"Organization"},"contact_name":{"type":"string","maxLength":120,"minLength":2,"title":"Contact Name"},"contact_email":{"type":"string","format":"email","title":"Contact Email"},"use_case":{"type":"string","maxLength":2000,"minLength":10,"title":"Use Case","description":"What the integration does and roughly how much traffic it expects."},"website":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Website"},"requested_role":{"$ref":"#/components/schemas/ApiRole","description":"`client` for normal integrations. `admin` needs manual review.","default":"client"}},"type":"object","required":["organization","contact_name","contact_email","use_case"],"title":"AccessRequestCreate","example":{"contact_email":"aisha@noorlearning.com","contact_name":"Aisha Bello","organization":"Noor Learning","requested_role":"client","use_case":"Answering student questions inside our mobile study app.","website":"https://noorlearning.com"}},"AccessRequestOut":{"properties":{"request_id":{"type":"string","title":"Request Id"},"organization":{"type":"string","title":"Organization"},"contact_name":{"type":"string","title":"Contact Name"},"contact_email":{"type":"string","title":"Contact Email"},"use_case":{"type":"string","title":"Use Case"},"website":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Website"},"requested_role":{"type":"string","title":"Requested Role"},"status":{"type":"string","title":"Status"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"reviewed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Reviewed At"},"reviewed_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reviewed By"},"review_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Review Note"},"issued_key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Issued Key Id"}},"type":"object","required":["request_id","organization","contact_name","contact_email","use_case","requested_role","status","created_at"],"title":"AccessRequestOut"},"AccessRequestStatus":{"type":"string","enum":["pending","approved","rejected"],"title":"AccessRequestStatus"},"ApiKeyCreate":{"properties":{"name":{"type":"string","maxLength":120,"minLength":2,"title":"Name"},"owner_email":{"type":"string","format":"email","title":"Owner Email"},"role":{"$ref":"#/components/schemas/ApiRole","default":"client"},"organization":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Organization"},"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Scopes","description":"Defaults to the standard scope set for the role."},"expires_in_days":{"anyOf":[{"type":"integer","maximum":3650.0,"minimum":1.0},{"type":"null"}],"title":"Expires In Days","description":"Omit for a non-expiring key."},"rate_limit_per_minute":{"anyOf":[{"type":"integer","maximum":100000.0,"minimum":0.0},{"type":"null"}],"title":"Rate Limit Per Minute"}},"type":"object","required":["name","owner_email"],"title":"ApiKeyCreate"},"ApiKeyIssued":{"properties":{"api_key":{"type":"string","title":"Api Key","description":"The full secret. Store it now; shown once."},"key":{"$ref":"#/components/schemas/ApiKeyOut"},"warning":{"type":"string","title":"Warning","default":"Store this key securely — it will not be shown again."}},"type":"object","required":["api_key","key"],"title":"ApiKeyIssued","description":"Returned only at creation / approval / rotation.\n\n`api_key` is shown exactly once — it is stored hashed and cannot be\nrecovered afterwards. Lose it and you rotate."},"ApiKeyOut":{"properties":{"key_id":{"type":"string","title":"Key Id"},"prefix":{"type":"string","title":"Prefix"},"name":{"type":"string","title":"Name"},"role":{"type":"string","title":"Role"},"owner_email":{"type":"string","title":"Owner Email"},"organization":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Organization"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes","default":[]},"status":{"type":"string","title":"Status"},"rate_limit_per_minute":{"type":"integer","title":"Rate Limit Per Minute"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"usage_count":{"type":"integer","title":"Usage Count","default":0},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"revoked_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Revoked By"},"revoked_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Revoked Reason"},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id"},"rotated_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rotated From"},"rotated_to":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rotated To"}},"type":"object","required":["key_id","prefix","name","role","owner_email","status","rate_limit_per_minute","created_at"],"title":"ApiKeyOut","description":"Key metadata. Never contains the secret."},"ApiKeyStatus":{"type":"string","enum":["active","revoked","expired"],"title":"ApiKeyStatus"},"ApiRole":{"type":"string","enum":["admin","client"],"title":"ApiRole","description":"Roles an API key can hold."},"ApproveRequest":{"properties":{"role":{"anyOf":[{"$ref":"#/components/schemas/ApiRole"},{"type":"null"}],"description":"Overrides the role the applicant asked for."},"name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Name"},"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Scopes"},"expires_in_days":{"anyOf":[{"type":"integer","maximum":3650.0,"minimum":1.0},{"type":"null"}],"title":"Expires In Days"},"rate_limit_per_minute":{"anyOf":[{"type":"integer","maximum":100000.0,"minimum":0.0},{"type":"null"}],"title":"Rate Limit Per Minute"},"note":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Note"}},"type":"object","title":"ApproveRequest"},"AuthConfig":{"properties":{"supabase_url":{"type":"string","title":"Supabase Url"},"supabase_anon_key":{"type":"string","title":"Supabase Anon Key"}},"type":"object","required":["supabase_url","supabase_anon_key"],"title":"AuthConfig"},"ChatRequest":{"properties":{"query":{"type":"string","title":"Query"},"conversation_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Conversation Id"}},"type":"object","required":["query"],"title":"ChatRequest"},"ChatResponse":{"properties":{"answer":{"type":"string","title":"Answer"},"sources":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Sources"},"conversation_id":{"type":"integer","title":"Conversation Id"},"sources_found":{"type":"integer","title":"Sources Found"},"plan":{"additionalProperties":true,"type":"object","title":"Plan"},"validation":{"additionalProperties":true,"type":"object","title":"Validation"},"pipeline_trace":{"additionalProperties":true,"type":"object","title":"Pipeline Trace"},"run_id":{"type":"string","title":"Run Id"}},"type":"object","required":["answer","sources","conversation_id","sources_found","plan","validation","pipeline_trace","run_id"],"title":"ChatResponse"},"ConversationSummary":{"properties":{"id":{"type":"integer","title":"Id"},"title":{"type":"string","title":"Title"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","title","created_at","updated_at"],"title":"ConversationSummary"},"DBLog":{"properties":{"id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id"},"level":{"type":"string","title":"Level"},"message":{"type":"string","title":"Message"},"module":{"type":"string","title":"Module"},"func_name":{"type":"string","title":"Func Name"},"extra_data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extra Data"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"}},"type":"object","required":["level","message","module","func_name"],"title":"DBLog","description":"Database model for logging application events, errors, and other relevant information.\n    "},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"IntegrationChatRequest":{"properties":{"query":{"type":"string","maxLength":2000,"minLength":1,"title":"Query"},"conversation_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Conversation Id","description":"Continue a thread, or omit to start a new one."},"end_user_id":{"type":"string","maxLength":128,"title":"End User Id","description":"Your own identifier for the end user asking the question. Threads are isolated per (API key, end_user_id).","default":"default"}},"type":"object","required":["query"],"title":"IntegrationChatRequest","example":{"end_user_id":"user-8842","query":"What does the Quran say about patience?"}},"IntegrationChatResponse":{"properties":{"answer":{"type":"string","title":"Answer"},"sources":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Sources"},"conversation_id":{"type":"integer","title":"Conversation Id"},"sources_found":{"type":"integer","title":"Sources Found"},"plan":{"additionalProperties":true,"type":"object","title":"Plan"},"validation":{"additionalProperties":true,"type":"object","title":"Validation"},"run_id":{"type":"string","title":"Run Id"},"end_user_id":{"type":"string","title":"End User Id"}},"type":"object","required":["answer","sources","conversation_id","sources_found","plan","validation","run_id","end_user_id"],"title":"IntegrationChatResponse"},"IntegrationStatus":{"properties":{"status":{"type":"string","title":"Status"},"hadith_docs":{"type":"integer","title":"Hadith Docs"},"quran_docs":{"type":"integer","title":"Quran Docs"},"scholarqa_docs":{"type":"integer","title":"Scholarqa Docs"},"history_docs":{"type":"integer","title":"History Docs"},"store_ready":{"type":"boolean","title":"Store Ready"},"key_id":{"type":"string","title":"Key Id"},"role":{"type":"string","title":"Role"},"rate_limit_per_minute":{"type":"integer","title":"Rate Limit Per Minute"}},"type":"object","required":["status","hadith_docs","quran_docs","scholarqa_docs","history_docs","store_ready","key_id","role","rate_limit_per_minute"],"title":"IntegrationStatus"},"MessageOut":{"properties":{"id":{"type":"integer","title":"Id"},"role":{"type":"string","title":"Role"},"content":{"type":"string","title":"Content"},"extra_data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extra Data"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","role","content","created_at"],"title":"MessageOut"},"RejectRequest":{"properties":{"reason":{"type":"string","maxLength":1000,"minLength":3,"title":"Reason"}},"type":"object","required":["reason"],"title":"RejectRequest"},"RevokeRequest":{"properties":{"key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Id","description":"Admin only — the key to revoke. Omit to revoke your own key."},"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason"}},"type":"object","title":"RevokeRequest"},"RotateRequest":{"properties":{"key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Id","description":"Admin only — the key to rotate. Omit to rotate your own key."},"grace_minutes":{"type":"integer","maximum":1440.0,"minimum":0.0,"title":"Grace Minutes","description":"Keep the old key working this long so a live deployment can cut over.","default":0}},"type":"object","title":"RotateRequest"},"StatusResponse":{"properties":{"status":{"type":"string","title":"Status"},"hadith_docs":{"type":"integer","title":"Hadith Docs"},"quran_docs":{"type":"integer","title":"Quran Docs"},"scholarqa_docs":{"type":"integer","title":"Scholarqa Docs"},"history_docs":{"type":"integer","title":"History Docs"},"store_ready":{"type":"boolean","title":"Store Ready"}},"type":"object","required":["status","hadith_docs","quran_docs","scholarqa_docs","history_docs","store_ready"],"title":"StatusResponse"},"UserPublic":{"properties":{"id":{"type":"string","title":"Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"}},"type":"object","required":["id"],"title":"UserPublic"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"routers__conversations__ConversationDetail":{"properties":{"id":{"type":"integer","title":"Id"},"title":{"type":"string","title":"Title"},"messages":{"items":{"$ref":"#/components/schemas/MessageOut"},"type":"array","title":"Messages"}},"type":"object","required":["id","title","messages"],"title":"ConversationDetail"},"routers__integration__ConversationDetail":{"properties":{"id":{"type":"integer","title":"Id"},"title":{"type":"string","title":"Title"},"end_user_id":{"type":"string","title":"End User Id"},"messages":{"items":{"$ref":"#/components/schemas/MessageOut"},"type":"array","title":"Messages"}},"type":"object","required":["id","title","end_user_id","messages"],"title":"ConversationDetail"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key issued to an external application, in the form `sk_live_<key_id>_<secret>`. Obtain one through `POST /api/v1/auth/request-access`.\n\nAdmin-only endpoints use this same header, holding a key whose role is `admin` — `X-API-Key: <ADMIN_BOOTSTRAP_KEY>`. Paste that key here to try the review and key-management operations; a client key returns `403` on them."},"SupabaseBearer":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Supabase Auth access token for a signed-in web user. Used only by the browser app — external integrations use `ApiKeyAuth` instead."}}},"tags":[{"name":"API keys","description":"Request, issue, rotate and revoke the credentials external applications use. Everything except `request-access` needs an `X-API-Key` header; the review and key-management endpoints (`/requests*`, `POST /keys`, `GET /keys`, and rotating or revoking a key other than your own) need that key to be an **admin** key."},{"name":"Integration","description":"The endpoints an external application actually calls, authenticated with `X-API-Key`."},{"name":"auth","description":"Supabase Auth configuration and the current web user. **Unchanged.**"},{"name":"chat","description":"Browser chat endpoint (Supabase JWT)."},{"name":"conversations","description":"Web users' own chat history (Supabase JWT)."},{"name":"Logging","description":"System log inspection."},{"name":"health","description":"Unauthenticated liveness and knowledge-base status."}],"externalDocs":{"description":"Integration guide","url":"https://api-seerahai.karmat-hq.com/redoc"}}