| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697 |
- {
- "openapi": "3.1.0",
- "info": {
- "title": "smartbotic-vectorapi",
- "version": "0.3.1",
- "description": "General-purpose REST API fronting smartbotic-database. Supports multi-project namespacing and multi-key authorization with per-project grants."
- },
- "components": {
- "securitySchemes": {
- "bearerAuth": {
- "type": "http",
- "scheme": "bearer",
- "description": "API key issued by the /api/v1/keys endpoint. Pass as Authorization: Bearer <KEY>."
- }
- },
- "schemas": {
- "ProjectEmbeddingSettings": {
- "type": "object",
- "description": "Three views of a project's embedding configuration. In overrides, an empty string means the field is inherited.",
- "properties": {
- "effective": { "$ref": "#/components/schemas/EmbeddingTier" },
- "inherited": { "$ref": "#/components/schemas/EmbeddingTier" },
- "overrides": { "$ref": "#/components/schemas/EmbeddingTier" }
- }
- },
- "EmbeddingTier": {
- "type": "object",
- "properties": {
- "openai_api_base": { "type": "string" },
- "default_embedding_model": { "type": "string" },
- "openai_api_key_set": { "type": "boolean" }
- }
- },
- "Error": {
- "type": "object",
- "properties": {
- "error": { "type": "string" },
- "message": { "type": "string" }
- },
- "required": ["error", "message"]
- },
- "CollectionMeta": {
- "type": "object",
- "properties": {
- "name": { "type": "string" },
- "kind": { "type": "string", "enum": ["json", "vector"] },
- "vector_dimension": { "type": "integer", "minimum": 1 },
- "embedding_model": { "type": "string" },
- "created_at": { "type": "integer" }
- },
- "required": ["name", "kind"]
- },
- "KeyScopeRule": {
- "type": "object",
- "properties": {
- "collection": { "type": "string", "description": "Collection name the rule applies to" },
- "ops": {
- "type": "array",
- "items": { "type": "string", "enum": ["read","list","search","insert","update","delete"] },
- "description": "Permitted operations on this collection"
- },
- "require_human_token": { "type": "boolean", "description": "Phase 2: require a CAPTCHA token (reserved)" }
- },
- "required": ["collection", "ops"]
- },
- "KeyScope": {
- "type": "object",
- "description": "Capability scope that constrains a non-admin key to specific collections and operations. A scoped key is safe to embed in untrusted clients (e.g. read-only, insert-only, origin-pinned, rate-limited, expiring). Scoped keys cannot manage collections or projects.",
- "properties": {
- "rules": {
- "type": "array",
- "items": { "$ref": "#/components/schemas/KeyScopeRule" },
- "minItems": 1,
- "description": "At least one rule is required. A request is allowed only if a matching rule exists for the (collection, op) pair."
- },
- "origins": {
- "type": "array",
- "items": { "type": "string" },
- "description": "Allowed Origin header values. Empty array means any origin is permitted. When non-empty, requests without a matching Origin header are rejected (fails closed)."
- },
- "rate_limit_per_min": {
- "type": "integer",
- "minimum": 0,
- "description": "Maximum requests per minute for this key (keyed by key id). 0 means unlimited. Exceeding the limit returns 429 with a Retry-After: 60 header."
- },
- "expires_at": {
- "type": "integer",
- "description": "Unix epoch seconds after which the key is treated as expired (auth returns 401). 0 means the key never expires."
- }
- },
- "required": ["rules"]
- },
- "ApiKeyPublic": {
- "type": "object",
- "properties": {
- "id": { "type": "string", "description": "Stable non-secret identifier used to reference the key in PATCH/DELETE" },
- "key_prefix": { "type": "string" },
- "label": { "type": "string" },
- "projects": { "type": "array", "items": { "type": "string" } },
- "admin": { "type": "boolean" },
- "created_at": { "type": "integer" },
- "scope": { "$ref": "#/components/schemas/KeyScope", "description": "Present only when the key carries a capability scope" }
- }
- },
- "RelationDefinition": {
- "type": "object",
- "description": "Relation names are per-project and unqualified: the API always takes and returns bare names. The internal `project:` prefix used to store the relation is never exposed to clients.",
- "properties": {
- "name": { "type": "string" },
- "child": { "type": "string", "description": "Child collection name - holds the foreign reference" },
- "child_field": { "type": "string", "description": "Field on the child document that stores the parent's id" },
- "parent": { "type": "string", "description": "Parent collection name" },
- "on_delete": { "type": "string", "enum": ["restrict", "cascade", "set_null", "no_action"] },
- "validate_on_write": { "type": "boolean" },
- "created_at": { "type": "integer" },
- "updated_at": { "type": "integer" }
- },
- "required": ["name", "child", "child_field", "parent", "on_delete"]
- }
- },
- "responses": {
- "Conflict": {
- "description": "Conflict. `error.code` distinguishes the two cases vectorapi returns 409 for: `duplicate_values` (a unique index was refused because the field already holds colliding values) and `relation_restricted` (a document delete was blocked by a restrict relation).",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "error": {
- "type": "object",
- "properties": {
- "code": { "type": "string", "enum": ["duplicate_values", "relation_restricted"] },
- "message": { "type": "string" },
- "details": {
- "type": "object",
- "description": "Machine-readable payload. For `duplicate_values`: `duplicate_examples` (array of up to five document ids, one per colliding value). For `relation_restricted`: `impacts` (array of the same shape returned by GET .../documents/{id}/delete-impact).",
- "properties": {
- "duplicate_examples": { "type": "array", "items": { "type": "string" } },
- "impacts": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "relation": { "type": "string" },
- "child_collection": { "type": "string" },
- "child_field": { "type": "string" },
- "on_delete": { "type": "string" },
- "child_count": { "type": "integer" },
- "sample_child_ids": { "type": "array", "items": { "type": "string" } },
- "blocks": { "type": "boolean" }
- }
- }
- }
- }
- }
- },
- "required": ["code", "message"]
- }
- }
- }
- }
- }
- }
- }
- },
- "security": [{ "bearerAuth": [] }],
- "paths": {
- "/healthz": {
- "get": {
- "summary": "Liveness check - always 200 if the process is running",
- "operationId": "healthz",
- "security": [],
- "responses": {
- "200": { "description": "OK" }
- }
- }
- },
- "/readyz": {
- "get": {
- "summary": "Readiness check - 200 when the database is reachable",
- "operationId": "readyz",
- "security": [],
- "responses": {
- "200": { "description": "Ready" },
- "503": { "description": "Database not reachable" }
- }
- }
- },
- "/api/v1/projects": {
- "get": {
- "summary": "List projects the key may access (admin sees all)",
- "operationId": "listProjects",
- "responses": {
- "200": {
- "description": "Project list",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "projects": { "type": "array", "items": { "type": "string" } }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" }
- }
- },
- "post": {
- "summary": "Create a new project (admin only)",
- "operationId": "createProject",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "name": { "type": "string" }
- },
- "required": ["name"]
- }
- }
- }
- },
- "responses": {
- "201": { "description": "Project created" },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" },
- "422": { "description": "Validation error" }
- }
- }
- },
- "/api/v1/projects/{project}": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- }
- ],
- "get": {
- "summary": "Get project info (collection and document counts)",
- "operationId": "getProject",
- "responses": {
- "200": {
- "description": "Project info",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "name": { "type": "string" },
- "collections": { "type": "integer" },
- "documents": { "type": "integer" }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Project not found" }
- }
- },
- "delete": {
- "summary": "Drop a project (admin only; not 'default')",
- "operationId": "deleteProject",
- "responses": {
- "200": { "description": "Project deleted" },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required or cannot drop default" },
- "404": { "description": "Project not found" }
- }
- }
- },
- "/api/v1/keys": {
- "get": {
- "summary": "List API keys (admin only; secret masked; returns id and key_prefix)",
- "operationId": "listKeys",
- "responses": {
- "200": {
- "description": "Key list",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "keys": {
- "type": "array",
- "items": { "$ref": "#/components/schemas/ApiKeyPublic" }
- }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" }
- }
- },
- "post": {
- "summary": "Generate a new API key (admin only); returns the secret key value once",
- "operationId": "createKey",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "label": { "type": "string" },
- "projects": {
- "type": "array",
- "items": { "type": "string" },
- "description": "Project names the key may access; use [\"*\"] for all projects"
- },
- "admin": { "type": "boolean", "default": false },
- "scope": { "$ref": "#/components/schemas/KeyScope", "description": "Optional capability scope. Mutually exclusive with admin:true." }
- },
- "required": ["label", "projects"]
- }
- }
- }
- },
- "responses": {
- "201": {
- "description": "Key created; contains the secret key value (shown once) and the stable id",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "id": { "type": "string", "description": "Stable non-secret identifier for this key" },
- "key": { "type": "string", "description": "Secret key value - shown only once" },
- "label": { "type": "string" },
- "projects": { "type": "array", "items": { "type": "string" } },
- "admin": { "type": "boolean" },
- "scope": { "$ref": "#/components/schemas/KeyScope" }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" },
- "422": { "description": "Validation error" }
- }
- }
- },
- "/api/v1/keys/{id}": {
- "parameters": [
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Stable non-secret key id (from the id field in list/create responses)"
- }
- ],
- "patch": {
- "summary": "Update key grants (admin only)",
- "operationId": "updateKey",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "label": { "type": "string" },
- "projects": { "type": "array", "items": { "type": "string" } },
- "admin": { "type": "boolean" },
- "scope": { "$ref": "#/components/schemas/KeyScope", "description": "Set or replace the capability scope. Omit to leave unchanged. Pass null to clear the scope." }
- }
- }
- }
- }
- },
- "responses": {
- "200": { "description": "Key updated" },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" },
- "404": { "description": "Key not found" }
- }
- },
- "delete": {
- "summary": "Revoke an API key (admin only)",
- "operationId": "deleteKey",
- "responses": {
- "200": { "description": "Key revoked" },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" },
- "404": { "description": "Key not found" }
- }
- }
- },
- "/api/v1/projects/{project}/collections": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- }
- ],
- "get": {
- "summary": "List collections in a project",
- "operationId": "listCollections",
- "parameters": [
- {
- "name": "q",
- "in": "query",
- "schema": { "type": "string" },
- "description": "Case-insensitive substring filter on collection name (applied server-side)."
- },
- {
- "name": "sort",
- "in": "query",
- "schema": { "type": "string", "enum": ["name", "kind", "documents", "size", "created_at"], "default": "name" },
- "description": "Field to sort by (applied server-side). Ties break by name."
- },
- {
- "name": "desc",
- "in": "query",
- "schema": { "type": "boolean", "default": false }
- }
- ],
- "responses": {
- "200": {
- "description": "Collection list",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "collections": {
- "type": "array",
- "items": { "$ref": "#/components/schemas/CollectionMeta" }
- }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" }
- }
- },
- "post": {
- "summary": "Create a collection in a project",
- "operationId": "createCollection",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "name": { "type": "string" },
- "kind": { "type": "string", "enum": ["json", "vector"] },
- "vector_dimension": {
- "type": "integer",
- "minimum": 1,
- "description": "Required when kind=vector"
- },
- "embedding_model": {
- "type": "string",
- "description": "Optional; defaults to global default_embedding_model"
- }
- },
- "required": ["name", "kind"]
- }
- }
- }
- },
- "responses": {
- "201": { "description": "Collection created" },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "422": { "description": "Validation error (e.g. missing vector_dimension for vector kind)" }
- }
- }
- },
- "/api/v1/projects/{project}/collections/{name}": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- },
- {
- "name": "name",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Collection name"
- }
- ],
- "get": {
- "summary": "Get collection metadata",
- "operationId": "getCollection",
- "responses": {
- "200": {
- "description": "Collection metadata",
- "content": {
- "application/json": {
- "schema": { "$ref": "#/components/schemas/CollectionMeta" }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Collection not found" }
- }
- },
- "delete": {
- "summary": "Drop a collection and all its documents",
- "operationId": "deleteCollection",
- "responses": {
- "200": { "description": "Collection deleted" },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Collection not found" }
- }
- }
- },
- "/api/v1/projects/{project}/collections/{name}/documents": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- },
- {
- "name": "name",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Collection name"
- }
- ],
- "get": {
- "summary": "Find documents with optional filter, pagination, and sort",
- "operationId": "findDocuments",
- "parameters": [
- {
- "name": "filter",
- "in": "query",
- "schema": { "type": "string" },
- "description": "Filter expression in field:op:value grammar (e.g. age:gte:30, name:eq:Alice). Multiple filters are ANDed."
- },
- {
- "name": "limit",
- "in": "query",
- "schema": { "type": "integer", "default": 20 }
- },
- {
- "name": "offset",
- "in": "query",
- "schema": { "type": "integer", "default": 0 }
- },
- {
- "name": "sort",
- "in": "query",
- "schema": { "type": "string" },
- "description": "Field to sort by"
- },
- {
- "name": "desc",
- "in": "query",
- "schema": { "type": "boolean", "default": false }
- }
- ],
- "responses": {
- "200": {
- "description": "Document list",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "documents": { "type": "array", "items": { "type": "object" } },
- "count": { "type": "integer" }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Collection not found" },
- "429": { "description": "Too Many Requests (rate-limited scoped key)" }
- }
- },
- "post": {
- "summary": "Insert a new JSON document",
- "operationId": "insertDocument",
- "parameters": [
- {
- "name": "X-Captcha-Token",
- "in": "header",
- "required": false,
- "schema": { "type": "string" },
- "description": "CAPTCHA response token. Required when the scoped key rule has require_human_token=true; verified server-side via the configured captcha provider."
- },
- {
- "name": "ttl_seconds",
- "in": "query",
- "required": false,
- "schema": { "type": "integer", "minimum": 0 },
- "description": "Optional expiry, in seconds from now. Absent means the document has no expiry."
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "data": {
- "type": "object",
- "description": "Arbitrary JSON document payload"
- }
- }
- }
- }
- }
- },
- "responses": {
- "201": {
- "description": "Document inserted",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "id": { "type": "string" }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Collection not found" },
- "422": { "description": "Validation error" },
- "429": { "description": "Too Many Requests (rate-limited scoped key)" }
- }
- }
- },
- "/api/v1/projects/{project}/collections/{name}/documents/{id}": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- },
- {
- "name": "name",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Collection name"
- },
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Document ID"
- }
- ],
- "get": {
- "summary": "Get a document by ID",
- "operationId": "getDocument",
- "responses": {
- "200": {
- "description": "Document",
- "content": {
- "application/json": {
- "schema": { "type": "object" }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Document not found" },
- "429": { "description": "Too Many Requests (rate-limited scoped key)" }
- }
- },
- "put": {
- "summary": "Replace a document (full upsert by ID)",
- "operationId": "replaceDocument",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": { "type": "object" }
- }
- }
- },
- "responses": {
- "200": { "description": "Document replaced" },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Collection not found" },
- "422": { "description": "Validation error" },
- "429": { "description": "Too Many Requests (rate-limited scoped key)" }
- }
- },
- "patch": {
- "summary": "Partially update a document (merge fields)",
- "operationId": "patchDocument",
- "parameters": [
- {
- "name": "ttl_seconds",
- "in": "query",
- "required": false,
- "schema": { "type": "integer", "minimum": 0 },
- "description": "Sets the document's expiry, in seconds from now. This parameter is asymmetric by design: **omitting it leaves the document's current expiry untouched**; sending `ttl_seconds=0` **clears** the expiry, making the document permanent. Do not assume 0 means \"no TTL requested\": it actively wipes an existing one."
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": { "type": "object" }
- }
- }
- },
- "responses": {
- "200": { "description": "Document updated" },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Document not found" },
- "422": { "description": "Validation error" },
- "429": { "description": "Too Many Requests (rate-limited scoped key)" }
- }
- },
- "delete": {
- "summary": "Delete a document by ID",
- "description": "Fails with 409 relation_restricted when a restrict relation has children referencing this document (see GET .../delete-impact to preview this beforehand without deleting).",
- "operationId": "deleteDocument",
- "responses": {
- "200": { "description": "Document deleted" },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Document not found" },
- "409": { "$ref": "#/components/responses/Conflict" },
- "429": { "description": "Too Many Requests (rate-limited scoped key)" }
- }
- }
- },
- "/api/v1/projects/{project}/collections/{name}/documents/{id}/delete-impact": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- },
- {
- "name": "name",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Collection name"
- },
- {
- "name": "id",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Document ID"
- }
- ],
- "get": {
- "summary": "Preview what deleting this document would affect, without deleting it",
- "description": "Read-only: changes nothing. Reports whether a delete would be blocked and, for every relation where this collection is the parent, the child collection, the count of referencing child documents, up to a handful of sample child ids, and whether that particular relation blocks the delete. Follows the collection's read grant, so a scoped key with a read rule on this collection may call it (index/relation management routes are admin/full-key only, but this one is not).",
- "operationId": "describeDelete",
- "responses": {
- "200": {
- "description": "Delete impact preview",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "would_be_blocked": { "type": "boolean" },
- "impacts": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "relation": { "type": "string" },
- "child_collection": { "type": "string" },
- "child_field": { "type": "string" },
- "on_delete": { "type": "string", "enum": ["restrict", "cascade", "set_null", "no_action"] },
- "child_count": { "type": "integer" },
- "sample_child_ids": { "type": "array", "items": { "type": "string" } },
- "blocks": { "type": "boolean" }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Document not found" },
- "429": { "description": "Too Many Requests (rate-limited scoped key)" }
- }
- }
- },
- "/api/v1/projects/{project}/collections/{name}/indexes": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- },
- {
- "name": "name",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Collection name"
- }
- ],
- "post": {
- "summary": "Declare a field index, optionally unique; backfills existing rows",
- "description": "Backfills existing rows as part of the call, so the index is usable immediately once this returns. Idempotent: re-declaring an index that already exists on `field` reports already_existed:true instead of erroring. Requires a full (non-scoped) key with project access; scoped keys cannot manage indexes.",
- "operationId": "createIndex",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "field": { "type": "string" },
- "unique": {
- "type": "boolean",
- "default": false,
- "description": "When true, enforces a uniqueness constraint. If the field already holds duplicate values across existing documents, the call fails with 409 duplicate_values instead of creating the index."
- }
- },
- "required": ["field"]
- }
- }
- }
- },
- "responses": {
- "201": {
- "description": "Index declared",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "field": { "type": "string" },
- "unique": { "type": "boolean" },
- "rows_indexed": { "type": "integer" },
- "already_existed": { "type": "boolean" }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - project access required; scoped keys cannot manage indexes" },
- "404": { "description": "Collection not found" },
- "409": { "$ref": "#/components/responses/Conflict" },
- "422": { "description": "Validation error (missing field)" }
- }
- },
- "get": {
- "summary": "List indexes declared on a collection",
- "operationId": "listIndexes",
- "responses": {
- "200": {
- "description": "Index list",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "indexes": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "field": { "type": "string" },
- "distinct_values": { "type": "integer" },
- "entries": { "type": "integer" },
- "unique": { "type": "boolean" }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Collection not found" }
- }
- }
- },
- "/api/v1/projects/{project}/collections/{name}/indexes/{field}": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- },
- {
- "name": "name",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Collection name"
- },
- {
- "name": "field",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Indexed field name"
- }
- ],
- "delete": {
- "summary": "Drop an index",
- "operationId": "dropIndex",
- "description": "Requires a full (non-scoped) key with project access; scoped keys cannot manage indexes.",
- "responses": {
- "200": {
- "description": "Index dropped",
- "content": {
- "application/json": {
- "schema": { "type": "object", "properties": { "dropped": { "type": "string" } } }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - project access required; scoped keys cannot manage indexes" },
- "404": { "description": "Index not found" }
- }
- }
- },
- "/api/v1/projects/{project}/collections/{name}/indexes/{field}/values": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- },
- {
- "name": "name",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Collection name"
- },
- {
- "name": "field",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Indexed field name"
- }
- ],
- "get": {
- "summary": "Distinct values of an indexed field, with per-value counts",
- "description": "Index-dependent by design, not an error: returns an empty values list with HTTP 200 when `field` has no index, and also for array-valued fields, whose index keys are the individual array elements rather than the field's own value. Follows the collection's read grant, so a scoped key with a read rule on this collection may call it. Use order=desc&limit=1 to get the maximum value.",
- "operationId": "indexFieldValues",
- "parameters": [
- {
- "name": "limit",
- "in": "query",
- "schema": { "type": "integer", "minimum": 1, "maximum": 1000, "default": 100 },
- "description": "Maximum number of distinct values to return, 1-1000."
- },
- {
- "name": "order",
- "in": "query",
- "schema": { "type": "string", "enum": ["asc", "desc"], "default": "asc" },
- "description": "Sort order of the returned values."
- }
- ],
- "responses": {
- "200": {
- "description": "Distinct values with counts (possibly empty; see description)",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "values": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "value": {},
- "count": { "type": "integer" }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "422": { "description": "Validation error (limit out of 1..1000 range or non-numeric)" },
- "429": { "description": "Too Many Requests (rate-limited scoped key)" }
- }
- }
- },
- "/api/v1/projects/{project}/collections/{name}/relations-enforced": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- },
- {
- "name": "name",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Collection name"
- }
- ],
- "put": {
- "summary": "Enable or disable relation enforcement on a collection (admin only)",
- "description": "IMPORTANT: relations_enforced is read on the collection you are deleting FROM, i.e. the PARENT side of the relation, not on the child collection that holds the foreign reference. Disabling it on the child has no effect on deletes issued against the parent. To allow deletes on a parent collection to bypass restrict relations, set this flag on the parent, not the child.",
- "operationId": "setRelationsEnforced",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": { "enforced": { "type": "boolean" } },
- "required": ["enforced"]
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "Enforcement flag updated",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": { "collection": { "type": "string" }, "enforced": { "type": "boolean" } }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" },
- "422": { "description": "Validation error (enforced missing or not a boolean)" }
- }
- }
- },
- "/api/v1/projects/{project}/collections/{name}/vectors": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- },
- {
- "name": "name",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Collection name (must be kind=vector)"
- }
- ],
- "post": {
- "summary": "Store a vector; supply either text (auto-embedded) or an explicit vector array",
- "description": "Store a vector. Supply either `text` (auto-embedded) or a pre-computed `vector` array. When supplying `text`, the server-side embedding result is cached by default. Send `Cache-Control: no-store` to bypass the cache: the text will be re-embedded fresh and the result will not be stored.",
- "operationId": "upsertVector",
- "parameters": [
- {
- "name": "X-Captcha-Token",
- "in": "header",
- "required": false,
- "schema": { "type": "string" },
- "description": "CAPTCHA response token. Required when the scoped key rule has require_human_token=true; verified server-side via the configured captcha provider."
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "description": "Optional stable ID; auto-generated if omitted"
- },
- "text": {
- "type": "string",
- "description": "Source text; will be embedded using the collection's embedding_model"
- },
- "vector": {
- "type": "array",
- "items": { "type": "number" },
- "description": "Pre-computed embedding; dimension must match the collection's vector_dimension"
- },
- "metadata": {
- "type": "object",
- "description": "Arbitrary JSON payload stored alongside the vector"
- }
- }
- }
- }
- }
- },
- "responses": {
- "201": {
- "description": "Vector stored",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "id": { "type": "string" }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Collection not found" },
- "422": { "description": "Validation error (wrong dimension, not a vector collection, etc.)" },
- "429": { "description": "Too Many Requests (rate-limited scoped key)" }
- }
- }
- },
- "/api/v1/projects/{project}/collections/{name}/search": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- },
- {
- "name": "name",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Collection name (must be kind=vector)"
- }
- ],
- "post": {
- "summary": "Cosine-similarity search; supply either query_text (auto-embedded) or query_vector",
- "description": "Supply either `query_text` or `query_vector`. **For low latency, prefer `query_vector`.** `query_text` requires a synchronous server-side embedding round-trip to the configured provider, which typically dominates request latency (seconds for large models). Clients issuing many searches (e.g. an LLM/n8n agent) should embed once on their side and pass `query_vector` to skip that step entirely; cosine search itself is sub-millisecond. Send `Cache-Control: no-store` to bypass the server-side embedding cache: the query text will be re-embedded fresh and the result will not be stored.",
- "operationId": "searchVectors",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "query_text": {
- "type": "string",
- "description": "Query text; embedded with the collection's model. Slow path: incurs a server-side embedding round-trip. Prefer query_vector when you can pre-embed."
- },
- "query_vector": {
- "type": "array",
- "items": { "type": "number" },
- "description": "Pre-computed query embedding (length must equal the collection's vector_dimension). Fast path: skips server-side embedding."
- },
- "top_k": {
- "type": "integer",
- "minimum": 1,
- "default": 5,
- "description": "Maximum number of results to return (default 5)"
- },
- "min_score": {
- "type": "number",
- "default": 0,
- "description": "Minimum cosine similarity score (0-1), default 0"
- },
- "filters": {
- "type": "array",
- "items": { "type": "string" },
- "description": "Metadata filters in field:op:value grammar, ANDed (e.g. src:eq:n8n). Same operators as the documents endpoint (eq, ne, lt, lte, gt, gte, in, contains, exists, regex, search) and evaluated by the database, so a document scores the same with or without a filter. Metadata is stored at the document top level: metadata {\"colour\":\"red\"} is filtered as colour:eq:red. Note that contains is array membership - use search for substrings. A malformed spec or unknown operator returns 400 bad_filter. The filtered scan is bounded at 10000 candidates."
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "Search results",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "results": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": { "type": "string" },
- "score": { "type": "number" },
- "metadata": { "type": "object" }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" },
- "404": { "description": "Collection not found" },
- "422": { "description": "Validation error" },
- "429": { "description": "Too Many Requests (rate-limited scoped key)" }
- }
- }
- },
- "/api/v1/projects/{project}/stats": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- }
- ],
- "get": {
- "summary": "Per-project stats (accessible by any key granted the project)",
- "operationId": "projectStats",
- "responses": {
- "200": {
- "description": "Project stats",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "project": { "type": "string" },
- "collections": { "type": "integer" },
- "documents": { "type": "integer" }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden" }
- }
- }
- },
- "/api/v1/projects/{project}/relations": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- }
- ],
- "post": {
- "summary": "Create a relation between two collections in a project (admin only)",
- "description": "Relation names are per-project and unqualified: the API takes and returns bare names - the internal project: prefix used to store the relation is never exposed. name may not start with _ or the reserved vectorapi_ prefix.",
- "operationId": "createRelation",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "name": { "type": "string", "description": "Unqualified relation name" },
- "child": { "type": "string", "description": "Child collection name - holds the foreign reference" },
- "child_field": { "type": "string", "description": "Field on the child document that stores the parent's id" },
- "parent": { "type": "string", "description": "Parent collection name" },
- "on_delete": {
- "type": "string",
- "enum": ["restrict", "cascade", "set_null", "no_action"],
- "default": "restrict",
- "description": "Any value outside this set is rejected with 422."
- },
- "validate_on_write": {
- "type": "boolean",
- "default": false,
- "description": "When true, writes to the child are validated against an existing parent."
- }
- },
- "required": ["name", "child", "child_field", "parent"]
- }
- }
- }
- },
- "responses": {
- "201": {
- "description": "Relation created",
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RelationDefinition" } } }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" },
- "422": { "description": "Validation error (missing field, or on_delete not one of restrict/cascade/set_null/no_action)" }
- }
- },
- "get": {
- "summary": "List relations defined in a project (admin only)",
- "operationId": "listRelations",
- "responses": {
- "200": {
- "description": "Relation list",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "relations": { "type": "array", "items": { "$ref": "#/components/schemas/RelationDefinition" } }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" }
- }
- }
- },
- "/api/v1/projects/{project}/relations/{name}": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- },
- {
- "name": "name",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Unqualified relation name"
- }
- ],
- "get": {
- "summary": "Get a relation by name (admin only)",
- "operationId": "getRelation",
- "responses": {
- "200": {
- "description": "Relation",
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RelationDefinition" } } }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" },
- "404": { "description": "Relation not found" }
- }
- },
- "delete": {
- "summary": "Drop a relation by name (admin only)",
- "operationId": "dropRelation",
- "responses": {
- "200": {
- "description": "Relation dropped",
- "content": {
- "application/json": {
- "schema": { "type": "object", "properties": { "dropped": { "type": "string" } } }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" },
- "404": { "description": "Relation not found" }
- }
- }
- },
- "/api/v1/projects/{project}/settings/embeddings": {
- "parameters": [
- {
- "name": "project",
- "in": "path",
- "required": true,
- "schema": { "type": "string" },
- "description": "Project name"
- }
- ],
- "get": {
- "summary": "Get this project's embedding settings: effective, inherited and overridden values. The API key is never returned, only openai_api_key_set.",
- "operationId": "getProjectEmbeddingSettings",
- "responses": {
- "200": {
- "description": "Project embedding settings",
- "content": {
- "application/json": {
- "schema": { "$ref": "#/components/schemas/ProjectEmbeddingSettings" }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - requires a key granted this project; capability-scoped keys are refused" }
- }
- },
- "put": {
- "summary": "Set or clear this project's embedding overrides. Omit a field to leave it unchanged; send null to clear it and inherit the global value.",
- "operationId": "updateProjectEmbeddingSettings",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "openai_api_base": {
- "type": ["string", "null"],
- "description": "Must start with http:// or https://. Empty string is rejected; send null to inherit the global value"
- },
- "openai_api_key": {
- "type": ["string", "null"],
- "description": "Write-only, never returned. Empty string is rejected; send null to inherit the global key"
- },
- "default_embedding_model": {
- "type": ["string", "null"],
- "description": "Empty string is rejected; send null to inherit the global default"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "Updated project embedding settings",
- "content": {
- "application/json": {
- "schema": { "$ref": "#/components/schemas/ProjectEmbeddingSettings" }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - requires a key granted this project; capability-scoped keys are refused" },
- "422": {
- "description": "Validation failed: empty value, malformed base URL, or openai_api_base without openai_api_key for the same project (code embedding_base_requires_key)",
- "content": {
- "application/json": {
- "schema": { "$ref": "#/components/schemas/Error" }
- }
- }
- }
- }
- },
- "delete": {
- "summary": "Drop every override so the project inherits the global embedding settings again",
- "operationId": "deleteProjectEmbeddingSettings",
- "responses": {
- "200": {
- "description": "Overrides removed",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "reverted": { "type": "string" },
- "had_overrides": { "type": "boolean" }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - requires a key granted this project; capability-scoped keys are refused" }
- }
- }
- },
- "/api/v1/settings": {
- "get": {
- "summary": "Get current settings (admin only); openai_api_key is masked to a boolean openai_api_key_set",
- "operationId": "getSettings",
- "responses": {
- "200": {
- "description": "Settings (with secret masked)",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "openai_api_base": { "type": "string" },
- "openai_api_key_set": { "type": "boolean" },
- "default_embedding_model": { "type": "string" },
- "cors_origins": { "type": "array", "items": { "type": "string" } },
- "session_ttl_minutes": { "type": "integer" },
- "webui_enabled": { "type": "boolean" },
- "default_project": { "type": "string" },
- "embedding_connect_timeout_sec": { "type": "integer" },
- "embedding_read_timeout_sec": { "type": "integer" },
- "embedding_cache_size": { "type": "integer" },
- "embedding_cache_ttl_sec": { "type": "integer" },
- "embedding_cache_max_bytes": { "type": "integer" },
- "embedding_cache_normalize": { "type": "boolean" },
- "embedding_client_pool_size": { "type": "integer" },
- "captcha_provider": { "type": "string" },
- "captcha_secret_set": { "type": "boolean", "description": "True when a captcha_secret is configured (the secret itself is never returned)" },
- "captcha_verify_url": { "type": "string" }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" }
- }
- },
- "put": {
- "summary": "Update settings (admin only); omit openai_api_key to keep the existing secret",
- "operationId": "updateSettings",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "openai_api_base": { "type": "string" },
- "openai_api_key": { "type": "string", "description": "Omit or pass empty string to preserve the existing key" },
- "default_embedding_model": { "type": "string" },
- "cors_origins": { "type": "array", "items": { "type": "string" } },
- "session_ttl_minutes": { "type": "integer", "minimum": 1 },
- "webui_enabled": { "type": "boolean" },
- "default_project": { "type": "string" },
- "embedding_connect_timeout_sec": { "type": "integer", "minimum": 1, "description": "Embedding HTTP connect timeout in seconds (default 10)" },
- "embedding_read_timeout_sec": { "type": "integer", "minimum": 1, "description": "Embedding HTTP read timeout in seconds (default 60)" },
- "embedding_cache_size": { "type": "integer", "minimum": 0, "description": "Maximum number of cached embedding entries (default 4096)" },
- "embedding_cache_ttl_sec": { "type": "integer", "minimum": 0, "description": "Cache entry TTL in seconds; 0 = no expiry (default 86400)" },
- "embedding_cache_max_bytes": { "type": "integer", "minimum": 0, "description": "Maximum total bytes of cached vectors (default 268435456 = 256 MB)" },
- "embedding_cache_normalize": { "type": "boolean", "description": "When true, text is trimmed and lower-cased before cache lookup (default false)" },
- "embedding_client_pool_size": { "type": "integer", "minimum": 1, "description": "Number of keep-alive HTTP clients pooled per upstream origin (default 4)" },
- "captcha_provider": { "type": "string", "description": "CAPTCHA provider: empty string to disable, 'turnstile', or 'hcaptcha'" },
- "captcha_secret": { "type": "string", "description": "CAPTCHA server-side secret. Omit or pass empty string to preserve the existing secret." },
- "captcha_verify_url": { "type": "string", "description": "Full URL of the CAPTCHA verify endpoint (e.g. https://challenges.cloudflare.com/turnstile/v0/siteverify)" }
- }
- }
- }
- }
- },
- "responses": {
- "200": { "description": "Settings updated", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" } } } } } },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" }
- }
- }
- },
- "/api/v1/stats": {
- "get": {
- "summary": "Server-wide stats (admin only)",
- "operationId": "globalStats",
- "responses": {
- "200": {
- "description": "Global stats",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "projects": { "type": "integer" },
- "memory_pressure_level": { "type": "integer" },
- "collections": { "type": "integer" },
- "documents": { "type": "integer" },
- "embedding": { "type": "object", "properties": { "cache_size": { "type": "integer" }, "cache_capacity": { "type": "integer" }, "cache_hits": { "type": "integer" }, "cache_misses": { "type": "integer" }, "cache_hit_ratio": { "type": "number" }, "cache_bytes": { "type": "integer" } } },
- "db_client_version": {
- "type": "string",
- "description": "Version of the smartbotic-database client library actually bound by the dynamic linker at process startup - not necessarily the version this binary was compiled against. 2.4.x and 2.11.x share a SONAME, so a mismatched shared library can load silently at runtime and only surface as a crash on a later restart; this field lets an operator catch that before it does."
- },
- "db_client_commit": {
- "type": "string",
- "description": "Commit hash of the loaded smartbotic-database client library, for the same reason as db_client_version."
- }
- }
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" }
- }
- }
- },
- "/api/v1/admin/readonly": {
- "get": {
- "summary": "Get the server-wide read-only lock status (admin only)",
- "description": "Server-global, not per project: a single lock state applies to the entire database backing every project. Reflects the database's own authoritative state, obtained live from it on every call - the database can also enter read-only by itself after a degraded or failed recovery, not only via an operator's PUT to this endpoint, so readonly:true does not necessarily mean an operator locked it. While locked, the database refuses writes (observed error: \"Insert rejected: database is in read-only mode. manually locked via SetReadOnly RPC\") and vectorapi surfaces that as HTTP 503.",
- "operationId": "getReadOnly",
- "responses": {
- "200": {
- "description": "Read-only status",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "readonly": { "type": "boolean" },
- "scope": { "type": "string", "enum": ["server"], "description": "Always \"server\" - the lock is not scoped to a project." },
- "reason": { "type": "string", "description": "DB-defined free text explaining why the database is read-only. Present only when non-empty; its absence does not imply the state was operator-set." }
- },
- "required": ["readonly", "scope"]
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" }
- }
- },
- "put": {
- "summary": "Set the server-wide read-only lock (admin only)",
- "description": "Server-global, not per project. Locking blocks writes across every project on this server.",
- "operationId": "setReadOnly",
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": { "readonly": { "type": "boolean" } },
- "required": ["readonly"]
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "Read-only status after the change",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "readonly": { "type": "boolean" },
- "scope": { "type": "string", "enum": ["server"] },
- "reason": { "type": "string", "description": "Present only when non-empty" }
- },
- "required": ["readonly", "scope"]
- }
- }
- }
- },
- "401": { "description": "Unauthorized" },
- "403": { "description": "Forbidden - admin required" },
- "422": { "description": "Validation error (readonly missing or not a boolean)" },
- "503": { "description": "Database rejected the request (e.g. failed to change read-only mode)" }
- }
- }
- }
- }
- }
|