{ "openapi": "3.0.3", "info": { "title": "pgEdge RAG Server API", "description": "REST API for querying RAG (Retrieval-Augmented Generation) pipelines", "version": "1.0.0" }, "servers": [ { "url": "/v1", "description": "API v1" } ], "paths": { "/health": { "get": { "summary": "Health check", "description": "Check if the server is running, and whether each pipeline's LLM providers are reachable", "operationId": "getHealth", "tags": [ "System" ], "responses": { "200": { "description": "Server is healthy", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthResponse" } } } } } } }, "/live": { "get": { "summary": "Liveness check", "description": "Cheap, dependency-free check that the server process is up and serving; does not ping providers. Suitable for a latency-sensitive liveness probe", "operationId": "getLive", "tags": [ "System" ], "responses": { "200": { "description": "Server process is up", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LiveResponse" } } } } } } }, "/pipelines": { "get": { "summary": "List pipelines", "description": "Get a list of all available RAG pipelines", "operationId": "listPipelines", "tags": [ "Pipelines" ], "responses": { "200": { "description": "List of pipelines", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PipelinesResponse" } } } } } } }, "/pipelines/{name}": { "post": { "summary": "Query pipeline", "description": "Execute a RAG query against a specific pipeline.\n\nWhen the server is configured with `identity.enabled`, this endpoint requires a caller identity, supplied by the trusted proxy in front of the server as a JSON claim set in the configured claims header (default `X-Forwarded-Claims`) or a bare subject in the configured subject header (default `X-Forwarded-User`). The server does NOT verify those headers: it checks no signature, issuer, audience or expiry, and trusts whatever the proxy asserts. Anything able to reach this server's port directly can therefore assert any identity, so the deployment must ensure only a trusted proxy can — see docs/identity.md. Retrieval then runs as that caller and PostgreSQL row-level security decides what it may see. A request carrying no identity is refused with 401 `IDENTITY_REQUIRED`; there is no fallback to the service's own database role.\n\nIdentity error codes: `IDENTITY_REQUIRED` (401, no identity was presented), `IDENTITY_MALFORMED` (400, the claims header was not a JSON object), `IDENTITY_UNTRUSTED_PEER` (403, the request came from an address not permitted to assert an identity) and `IDENTITY_ROLE_NOT_ALLOWED` (403, the claims named a database role that is not on the server's allowlist).", "operationId": "queryPipeline", "tags": [ "Pipelines" ], "parameters": [ { "name": "name", "in": "path", "description": "Pipeline name", "required": true, "schema": { "type": "string" } } ], "requestBody": { "description": "Query request", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QueryRequest" } } } }, "responses": { "200": { "description": "Query response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/QueryResponse" } }, "text/event-stream": { "schema": { "type": "string", "description": "Server-Sent Events stream" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "401": { "description": "No caller identity was presented and the server requires one (IDENTITY_REQUIRED)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "The identity presented was not acceptable: the request came from an address not permitted to assert one (IDENTITY_UNTRUSTED_PEER), or it named a database role that is not permitted (IDENTITY_ROLE_NOT_ALLOWED)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Pipeline not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "413": { "description": "Request body exceeds the 1 MiB limit", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Server error. error.code is RETRIEVAL_REFUSED when the document search could not be run because of a server-side configuration or permissions problem, RETRIEVAL_FAILED when the search failed for an unclassified reason, or EXECUTION_ERROR for any other failure. A retrieval failure is never reported as an empty result set", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "503": { "description": "The document store could not be reached, so no search ran (error.code RETRIEVAL_UNAVAILABLE). Unlike a 500 this is transient and the request may be retried", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "504": { "description": "The request took too long to process (error.code REQUEST_TIMEOUT)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/stats": { "get": { "summary": "Pipeline usage stats", "description": "Get cumulative LLM token usage for every pipeline", "operationId": "getStats", "tags": [ "System" ], "responses": { "200": { "description": "Pipeline usage statistics", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatsResponse" } } } } } } } }, "components": { "schemas": { "ErrorDetail": { "type": "object", "properties": { "code": { "type": "string", "description": "Error code" }, "message": { "type": "string", "description": "Human-readable description of the failure class. Deliberately coarse: upstream provider error text is never relayed here, because providers echo a truncated form of the submitted API key on an authentication failure. Full detail is written to the server log instead." } }, "required": [ "code", "message" ] }, "ErrorResponse": { "type": "object", "properties": { "error": { "$ref": "#/components/schemas/ErrorDetail" } }, "required": [ "error" ] }, "Filter": { "type": "object", "properties": { "conditions": { "type": "array", "description": "Filter conditions to apply", "items": { "$ref": "#/components/schemas/FilterCondition" }, "maxItems": 50 }, "logic": { "type": "string", "description": "Logical operator to combine conditions: AND or OR (default: AND)", "default": "AND", "enum": [ "AND", "OR" ] } }, "required": [ "conditions" ] }, "FilterCondition": { "type": "object", "properties": { "column": { "type": "string", "description": "Column name to filter on" }, "operator": { "type": "string", "description": "Comparison operator", "enum": [ "=", "!=", "\u003c", "\u003e", "\u003c=", "\u003e=", "LIKE", "ILIKE", "IN", "NOT IN", "IS NULL", "IS NOT NULL" ] }, "value": { "description": "Value to compare against (not required for IS NULL / IS NOT NULL)" } }, "required": [ "column", "operator" ] }, "HealthResponse": { "type": "object", "properties": { "pipelines": { "type": "array", "description": "Per-pipeline provider connectivity", "items": { "$ref": "#/components/schemas/PipelineHealth" } }, "status": { "type": "string", "description": "Overall health status: \"healthy\" or \"degraded\" (one or more providers unreachable). Always HTTP 200" } }, "required": [ "status" ] }, "LiveResponse": { "type": "object", "properties": { "status": { "type": "string", "description": "Liveness status; always \"ok\" when the process is serving" } }, "required": [ "status" ] }, "Message": { "type": "object", "properties": { "content": { "type": "string", "description": "Message content" }, "role": { "type": "string", "description": "Message role (user or assistant)" } }, "required": [ "role", "content" ] }, "PipelineHealth": { "type": "object", "properties": { "completion": { "description": "Completion provider connectivity", "$ref": "#/components/schemas/ProviderHealth" }, "embedding": { "description": "Embedding provider connectivity", "$ref": "#/components/schemas/ProviderHealth" }, "name": { "type": "string", "description": "Pipeline name" } }, "required": [ "name", "embedding", "completion" ] }, "PipelineInfo": { "type": "object", "properties": { "description": { "type": "string", "description": "Pipeline description" }, "name": { "type": "string", "description": "Pipeline name" } }, "required": [ "name" ] }, "PipelineUsage": { "type": "object", "properties": { "completion": { "description": "Cumulative completion token usage", "$ref": "#/components/schemas/TokenUsage" }, "description": { "type": "string", "description": "Pipeline description" }, "embedding": { "description": "Cumulative embedding token usage", "$ref": "#/components/schemas/TokenUsage" }, "name": { "type": "string", "description": "Pipeline name" } }, "required": [ "name", "embedding", "completion" ] }, "PipelinesResponse": { "type": "object", "properties": { "pipelines": { "type": "array", "description": "List of available pipelines", "items": { "$ref": "#/components/schemas/PipelineInfo" } } }, "required": [ "pipelines" ] }, "ProviderHealth": { "type": "object", "properties": { "error": { "type": "string", "description": "Failure class if unreachable, drawn from a fixed set. Never contains upstream provider error text, since that can include part of the configured API key; see the server log for detail." }, "reachable": { "type": "boolean", "description": "Whether the provider responded to a connectivity check" } }, "required": [ "reachable" ] }, "QueryRequest": { "type": "object", "properties": { "disable_hybrid": { "type": "boolean", "description": "Skip the keyword-search arm for this request, using vector search alone. Reduces latency and database work. This can only turn hybrid search off: a request cannot enable it where the pipeline configuration has disabled it.", "default": false }, "filter": { "description": "Structured filter to apply to search results", "$ref": "#/components/schemas/Filter" }, "include_sources": { "type": "boolean", "description": "Request the source documents used to produce the answer. Honoured only if the pipeline is configured with allow_include_sources: true; otherwise the answer is returned without sources and the request still succeeds.", "default": false }, "messages": { "type": "array", "description": "Previous conversation history for context", "items": { "$ref": "#/components/schemas/Message" } }, "query": { "type": "string", "description": "The question to answer" }, "stream": { "type": "boolean", "description": "Enable streaming response (SSE)", "default": false }, "top_n": { "type": "integer", "description": "Override default result limit" } }, "required": [ "query" ] }, "QueryResponse": { "type": "object", "properties": { "answer": { "type": "string", "description": "The generated answer" }, "sources": { "type": "array", "description": "Source documents. Present only when the request set include_sources=true and the pipeline permits it via allow_include_sources.", "items": { "$ref": "#/components/schemas/Source" } }, "tokens_used": { "type": "integer", "description": "Total tokens consumed" } }, "required": [ "answer", "tokens_used" ] }, "Source": { "type": "object", "properties": { "content": { "type": "string", "description": "Document content" }, "id": { "type": "string", "description": "Document identifier" }, "score": { "type": "number", "format": "double", "description": "Relevance score" } }, "required": [ "content", "score" ] }, "StatsResponse": { "type": "object", "properties": { "pipelines": { "type": "array", "description": "Per-pipeline cumulative token usage", "items": { "$ref": "#/components/schemas/PipelineUsage" } } }, "required": [ "pipelines" ] }, "TokenUsage": { "type": "object", "description": "Cumulative token usage since client creation or last reset", "properties": { "cache_creation_input_tokens": { "type": "integer", "description": "Cumulative tokens used to write to the prompt cache" }, "cache_read_input_tokens": { "type": "integer", "description": "Cumulative tokens read from the prompt cache" }, "completion_tokens": { "type": "integer", "description": "Cumulative completion/output tokens" }, "prompt_tokens": { "type": "integer", "description": "Cumulative prompt/input tokens" }, "total_tokens": { "type": "integer", "description": "Cumulative total tokens (prompt + completion)" } }, "required": [ "prompt_tokens", "completion_tokens", "total_tokens" ] } } } }