{"openapi":"3.1.0","info":{"title":"ValorBrain Engine API","description":"REST interface for ValorBrain's memory engine — search, retrieval, document lifecycle, RAG chat, knowledge graph, and multi-tenant management.\n","version":"0.2.0","contact":{"name":"Valor Digital","url":"https://valor.digital"},"license":{"name":"Proprietary"}},"servers":[{"url":"https://valorbrain-api.valor.digital","description":"This process (VALORBRAIN_PUBLIC_URL)"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Health","description":"Liveness, readiness, and system diagnostics"},{"name":"Memory","description":"Memory briefing and typed memories (REST mirrors of the MCP memory tools)"},{"name":"Discovery","description":"Machine-readable discovery documents (credential matrix)"},{"name":"Documents","description":"Document CRUD, ingest, and mutations"},{"name":"Search","description":"Hybrid search, temporal, hierarchical, and retrieve"},{"name":"Agents","description":"Agent signup, identify, claim, and verify"},{"name":"OAuth","description":"OAuth 2.1 authorization server and protected resource"},{"name":"MCP Tokens","description":"MCP API token CRUD"},{"name":"Observability","description":"Metrics, traces, ROI, and feature flags"}],"paths":{"/healthz":{"get":{"operationId":"healthz","summary":"Public liveness probe","description":"Returns 200 if the process is up. No auth, no store dependency.","tags":["Health"],"security":[],"responses":{"200":{"description":"Process is alive","content":{"application/json":{"schema":{"type":"object","required":["status","service","timestamp"],"properties":{"status":{"type":"string","enum":["ok"]},"service":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}}}}}}}},"/health":{"get":{"operationId":"health","summary":"Health check with database status","description":"Public callers (through the tunnel, no credential) get only status, version and uptime_seconds. Local operators and authenticated callers also get git_hash, database, db_health, tenant corpus counts and request context.","tags":["Health"],"security":[],"responses":{"200":{"description":"Service health","content":{"application/json":{"schema":{"type":"object","required":["status","service"],"properties":{"status":{"type":"string"},"service":{"type":"string"},"version":{"type":"string"},"database":{"type":"string"},"documents":{"type":"integer"},"needsEmbedding":{"type":"integer"},"hasVectors":{"type":"boolean"},"request":{"$ref":"#/components/schemas/RequestContext"}}}}}}}}},"/documents":{"post":{"operationId":"ingestDocument","summary":"Ingest a document","description":"Create or update a document in a collection. Supports quality gate evaluation, schema packs, auto-linking, and temporal metadata. The tenant comes from the credential (X-Tenant-ID only with the platform token).\n","tags":["Documents"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["collection","path","content"],"properties":{"collection":{"type":"string","description":"Collection name"},"path":{"type":"string","description":"Unique document path within the collection"},"title":{"type":"string"},"content":{"type":"string","maxLength":1000000,"description":"Document body (max 1MB)"},"content_type":{"type":"string","default":"note"},"confidence":{"type":"number","minimum":0,"maximum":1,"default":0.5},"quality_score":{"type":"number","minimum":0,"maximum":1,"default":0.5},"metadata":{"type":"object","additionalProperties":true},"user_id":{"type":"string"},"run_id":{"type":"string"},"event_at":{"type":"string","format":"date-time"},"event_type":{"type":"string"}}}}}},"responses":{"200":{"description":"Document ingested","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"action":{"type":"string","enum":["created","updated","deduplicated","skipped","unchanged"],"description":"\"unchanged\" (VAL-172): posted content hash equals the stored hash for the same path — the engine performs no write and records no audit row.\n"},"id":{"type":"integer"},"path":{"type":"string"},"hash":{"type":"string"},"embed_state":{"type":"string"},"gate_verdict":{"type":"object","properties":{"shouldIndex":{"type":"boolean"},"reason":{"type":"string"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}},"get":{"operationId":"listDocuments","summary":"Multi-get documents by glob pattern","description":"Fetch multiple documents at once by path glob. The `pattern` query parameter is mandatory; `max_bytes` bounds the total body payload and `collection` narrows the glob to one collection. The same blocked-collection predicate applied to search applies here.\n","tags":["Documents"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"},{"name":"pattern","in":"query","required":true,"schema":{"type":"string"},"description":"Glob pattern over document paths"},{"name":"collection","in":"query","schema":{"type":"string"}},{"name":"max_bytes","in":"query","schema":{"type":"integer","default":10240},"description":"Bound on total body payload returned"}],"responses":{"200":{"description":"Multi-get result","content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{"type":"string"},"collection":{"anyOf":[{"type":"string"},{"type":"null"}]},"count":{"type":"integer"},"documents":{"type":"array","items":{"type":"object"}},"errors":{"type":"array","items":{"type":"object"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/documents/{id}":{"get":{"operationId":"getDocument","summary":"Get a document by ID or hash","tags":["Documents"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"},{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Document ID or hash"}],"responses":{"200":{"description":"Document details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Document"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/documents/{id}/visibility":{"patch":{"operationId":"patchDocumentVisibility","summary":"Update document visibility","tags":["Documents"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["visibility"],"properties":{"visibility":{"type":"string","enum":["private","tenant"]}}}}}},"responses":{"200":{"description":"Visibility updated","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/documents/{id}/pin":{"post":{"operationId":"pinDocument","summary":"Pin a document for permanent prioritization","tags":["Documents"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Document pinned","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/documents/{id}/snooze":{"post":{"operationId":"snoozeDocument","summary":"Temporarily hide a document from context","tags":["Documents"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"until":{"type":"string","format":"date-time"}}}}}},"responses":{"200":{"description":"Document snoozed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/documents/{id}/forget":{"post":{"operationId":"forgetDocument","summary":"Permanently deactivate a document","tags":["Documents"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Document forgotten","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/documents/{id}/feedback":{"post":{"operationId":"documentFeedback","summary":"Submit relevance feedback for a document","tags":["Documents"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["feedback"],"properties":{"feedback":{"type":"string","enum":["POSITIVE","NEGATIVE","VERY_NEGATIVE"]},"reason":{"type":"string"},"user_id":{"type":"string","description":"Voter, honored only for the platform credential and the tenant's SaaS service key (they act for the session they name). Any other credential votes as its own user; one with no user votes once, as itself."}}}}}},"responses":{"200":{"description":"Feedback recorded","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"No active document the voter may read"},"409":{"description":"Ambiguous docid prefix; lists only candidates the voter may read"}}}},"/search":{"post":{"operationId":"search","summary":"Hybrid search (BM25 + vector + rerank)","description":"Full hybrid search: BM25 and dense vectors fused by RRF, then a cross-encoder rerank and MMR diversity. An LLM only expands the query (optional HyDE); it never ranks. Optional self-correct retrieval. Personalized PageRank exists behind a flag and is off in production. Searches DOCUMENTS (prose): keyed facts (upsert_keyed_fact / keyed_facts) are NOT part of the text index — query them via keyed_facts_as_of, or receive the current values with declared precedence via memory_prepare / working_context.\n","tags":["Search"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["query"],"properties":{"query":{"type":"string","description":"Search query"},"collection":{"type":"string","description":"Filter to collection (comma-separated list is split)"},"limit":{"type":"integer","default":10},"order":{"type":"string","enum":["relevance","event_time"],"description":"Delivery order of the final set"},"intent":{"type":"string"},"expand":{"type":"boolean","description":"Force HyDE expansion on/off"},"self_correct":{"type":"boolean","description":"Self-correct retrieval"},"mode":{"type":"string","enum":["auto","keyword","semantic","hybrid"]},"compact":{"type":"boolean","default":true},"profile":{"type":"string","enum":["speed","balanced","deep"]},"explain":{"type":"boolean","description":"Attach retrieval channels per hit"}}}}}},"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchResult"}},"query":{"type":"string"},"mode":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/search/temporal":{"post":{"operationId":"temporalSearch","summary":"Temporal search with time-aware filtering","tags":["Search"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["query"],"properties":{"query":{"type":"string"},"reference_date":{"type":"string","format":"date-time","description":"Anchor for temporal intent detection (default now)"},"limit":{"type":"integer","default":10,"maximum":50},"user_id":{"type":"string"},"run_id":{"type":"string"}}}}}},"responses":{"200":{"description":"Temporally filtered results","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchResult"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/search/hierarchical":{"post":{"operationId":"hierarchicalSearch","summary":"Hierarchical search across memory layers","tags":["Search"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["query"],"properties":{"query":{"type":"string"},"max_depth":{"type":"integer","default":3},"top_k":{"type":"integer","default":20},"score_alpha":{"type":"number","default":0.7}}}}}},"responses":{"200":{"description":"Hierarchical results","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/retrieve":{"post":{"operationId":"retrieve","summary":"Unified retrieval with auto-routing","description":"Auto-classifies query intent and routes to optimal search backend.","tags":["Search"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["query"],"properties":{"query":{"type":"string","maxLength":2000},"mode":{"type":"string","enum":["auto","keyword","semantic","causal","timeline","discovery","complex","hybrid"],"default":"auto"},"limit":{"type":"integer"},"collection":{"type":"string","description":"Comma-separated list is split into multiple collections"},"compact":{"type":"boolean","default":true},"profile":{"type":"string","enum":["speed","balanced","deep"]},"include_structured":{"type":"boolean"},"explain":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Retrieved results","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchResult"}},"mode":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"413":{"description":"Query longer than 2000 chars"}}}},"/api/v1/agents/signup":{"post":{"operationId":"agentSignup","summary":"Register a new agent (public)","description":"Public endpoint — no auth required. Creates a shadow tenant and returns an API key.\n","tags":["Agents"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agent_caller"],"properties":{"agent_caller":{"type":"string","maxLength":100},"user_agent":{"type":"string"}}}}}},"responses":{"200":{"description":"Agent registered with API key","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"tenant_id":{"type":"string"},"api_key":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"501":{"description":"Postgres backend required"}}}},"/api/v1/agents/mcp-token":{"post":{"operationId":"agentMcpTokenReissue","summary":"Reissue the signup MCP token with the agent key","description":"Authenticated by the workspace's own `vb_agent_` key only (any other credential gets 401 `agent_key_required`). Mints a new `vbm_` with the signup ceiling — scopes `read write`, toolset `agent`, the key's own expiry — and revokes the previous signup MCP token in the same transaction. Rate-limited per tenant (3 per minute by default).\n","tags":["Agents"],"security":[],"responses":{"201":{"description":"New MCP token (raw value shown once)","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"tenant_id":{"type":"string"},"mcp_token":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"toolset":{"type":"string"},"expires_at":{"type":"string","nullable":true},"revoked_previous":{"type":"integer"}}}}}},"401":{"description":"Not the workspace's `vb_agent_` key (`agent_key_required`), or an invalid key"},"429":{"description":"Too many reissues for this workspace"}}}},"/api/v1/agents/identify":{"post":{"operationId":"agentIdentify","summary":"Identify an agent by API key","description":"Requires a Bearer API key (401 when absent).","tags":["Agents"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agent_caller"],"properties":{"agent_caller":{"type":"string"}}}}}},"responses":{"200":{"description":"Agent identity","content":{"application/json":{"schema":{"type":"object","properties":{"agent_name":{"type":"string"},"tenant_id":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/agents/claim":{"post":{"operationId":"agentClaim","summary":"Request ownership claim for an agent","description":"Requires a Bearer API key (401 when absent).","tags":["Agents"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}}}}},"responses":{"200":{"description":"Claim request initiated","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/agents/claim/verify":{"post":{"operationId":"agentClaimVerify","summary":"Verify an agent ownership claim","description":"Requires a Bearer API key (401 when absent). The SaaS calls it instead with the internal secret and the `claim_token` from the signup's `claim_url`. On success the workspace is named for its human and keeps its plan (it changes only when the operator sets `VALORBRAIN_AGENT_PLAN_AFTER_CLAIM`); `login_url` is where the human creates the app login (or adds the workspace to the account that e-mail already has).","tags":["Agents"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","otp"],"properties":{"email":{"type":"string","format":"email"},"otp":{"type":"string"}}}}}},"responses":{"200":{"description":"Claim verified","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"tenant_id":{"type":"string"},"email":{"type":"string"},"tenant_name":{"type":"string"},"login_url":{"type":"string","nullable":true,"description":"Null on an engine without a SaaS configured."},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/.well-known/oauth-authorization-server":{"get":{"operationId":"oauthAsMetadata","summary":"OAuth authorization server metadata","description":"Public discovery endpoint per RFC 9414.","tags":["OAuth"],"security":[],"responses":{"200":{"description":"Authorization server metadata","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"operationId":"oauthRsMetadata","summary":"OAuth protected resource metadata","description":"Public discovery endpoint per RFC 9728.","tags":["OAuth"],"security":[],"responses":{"200":{"description":"Protected resource metadata","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/oauth/register":{"post":{"operationId":"oauthRegister","summary":"Dynamic client registration","tags":["OAuth"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"scope":{"type":"string","description":"Space-separated subset of \"read write\"; default \"read write\""}}}}}},"responses":{"201":{"description":"Client registered","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"token_endpoint_auth_method":{"type":"string","enum":["none"]},"scope":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/oauth/authorize":{"get":{"operationId":"oauthAuthorize","summary":"OAuth authorization endpoint","description":"Redirects to consent page.","tags":["OAuth"],"security":[],"parameters":[{"name":"response_type","in":"query","required":true,"schema":{"type":"string","enum":["code"]},"description":"Only \"code\" is accepted"},{"name":"client_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"redirect_uri","in":"query","required":true,"schema":{"type":"string"}},{"name":"code_challenge","in":"query","required":true,"schema":{"type":"string"},"description":"PKCE challenge (S256)"},{"name":"code_challenge_method","in":"query","schema":{"type":"string","default":"S256","enum":["S256"]}},{"name":"scope","in":"query","schema":{"type":"string","default":"read"}},{"name":"state","in":"query","schema":{"type":"string"}},{"name":"resource","in":"query","schema":{"type":"string"},"description":"RFC 8707 resource indicator; must be served by this AS"}],"responses":{"302":{"description":"Redirect to consent page"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/oauth/token":{"post":{"operationId":"oauthToken","summary":"OAuth token exchange","tags":["OAuth"],"security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["grant_type"],"properties":{"grant_type":{"type":"string"},"code":{"type":"string"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"redirect_uri":{"type":"string"}}}}}},"responses":{"200":{"description":"Token response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string"},"expires_in":{"type":"integer"},"refresh_token":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/oauth/revoke":{"post":{"operationId":"oauthRevoke","summary":"Revoke an OAuth token","tags":["OAuth"],"security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string"}}}}}},"responses":{"200":{"description":"Token revoked"}}}},"/api/v1/mcp/tokens":{"get":{"operationId":"listMcpTokens","summary":"List MCP API tokens","tags":["MCP Tokens"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"}],"responses":{"200":{"description":"Token list","content":{"application/json":{"schema":{"type":"object","properties":{"tokens":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"is_active":{"type":"boolean"},"credential_kind":{"type":"string","enum":["personal","gateway","integration"]},"acts_for_users":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"createMcpToken","summary":"Create an MCP API token","tags":["MCP Tokens"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"expires_at":{"type":"string","format":"date-time"},"credential_kind":{"type":"string","enum":["personal","gateway"],"default":"personal","description":"personal = bound to a person (user_id, else the creator) and acts as that person. gateway = no person, admin only; reads the company view (private documents included), narrowed to the X-User-ID a request names — always send X-User-ID for a person's request. integration credentials are created at POST /api/v1/authority/integrations."},"acts_for_users":{"type":"boolean","default":false,"description":"Gateway only (so admin only) — writes act as the tenant member it names, over that user's own documents only."}}}}}},"responses":{"200":{"description":"Token created (full key returned only once)","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"key":{"type":"string","description":"Full API key (shown only at creation)"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/mcp/tokens/{tokenId}":{"get":{"operationId":"getMcpToken","summary":"Get MCP token details","tags":["MCP Tokens"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"},{"name":"tokenId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Token details","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"put":{"operationId":"updateMcpToken","summary":"Update MCP token","tags":["MCP Tokens"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"},{"name":"tokenId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Token updated","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"deleteMcpToken","summary":"Delete MCP token","tags":["MCP Tokens"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"},{"name":"tokenId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Token deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/memory/prepare":{"post":{"operationId":"memoryPrepare","summary":"Session briefing plus memory relevant to a message","description":"REST mirror of the MCP `memory_prepare` tool: identity, task state, what changed, notifications and the memory relevant to `message`, in one call. The tenant comes from the credential.\n","tags":["Memory"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"message":{"type":"string","description":"The task or question (alias `query`)."},"collection":{"type":"string"},"episode_id":{"type":"string"},"recall_budget":{"type":"integer"},"include_recall":{"type":"boolean","default":true},"fast_mode":{"type":"boolean","default":false}}}}}},"responses":{"200":{"description":"Briefing and relevant memory","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"`message` missing"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/memory/store":{"post":{"operationId":"memoryStore","summary":"Persist a typed memory (decision, observation, lesson…)","description":"REST mirror of the MCP `memory_store` tool.","tags":["Memory"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","content"],"properties":{"type":{"type":"string","default":"note","description":"decision, observation, problem, milestone, handoff, lesson or note."},"title":{"type":"string","minLength":5},"content":{"type":"string","minLength":20},"collection":{"type":"string","description":"Defaults to the caller agent's collection."},"tags":{"type":"array","items":{"type":"string"}},"confidence":{"type":"number"}}}}}},"responses":{"200":{"description":"Stored memory (path and docid)","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Title or content too short"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/conversations":{"post":{"operationId":"ingestConversation","summary":"Ingest a whole conversation (turn windows and dated sketch)","description":"Indexes a conversation the way the transcript watcher does: turn windows plus derived documents. `POST /documents` stays the path for loose text.\n","tags":["Documents"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["collection","slug","messages"],"properties":{"collection":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"messages":{"type":"array","items":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string"},"content":{"type":"string"},"date":{"type":"string"}}}},"userName":{"type":"string"},"assistantName":{"type":"string"},"sessionId":{"type":"string"},"windowSize":{"type":"integer"},"overlap":{"type":"integer"}}}}}},"responses":{"200":{"description":"Documents written for the conversation","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Missing collection, slug or messages"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/.well-known/valorbrain-credentials.json":{"get":{"operationId":"credentialMatrix","summary":"Canonical credential matrix","description":"Every credential the platform accepts — prefix, issuer, surfaces that accept and refuse it, identity, lifecycle and rate limits. Generated from src/credential-matrix.ts; `sha256` changes whenever the matrix does.\n","tags":["Discovery"],"security":[],"responses":{"200":{"description":"The matrix","content":{"application/json":{"schema":{"type":"object","required":["version","sha256","credentials"],"properties":{"version":{"type":"integer"},"sha256":{"type":"string"},"credentials":{"type":"array","items":{"type":"object"}}}}}}}}}},"/api/v1/memory/recall":{"post":{"operationId":"memoryRecall","summary":"Token-budgeted memory recall","description":"Pull category-ranked memory in a fixed token budget (mem0-style).","tags":["Observability"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string"},"budget":{"type":"integer","default":1500},"format":{"type":"string","enum":["json","markdown"],"default":"markdown"}}}}}},"responses":{"200":{"description":"Budgeted memory recall","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/v1/memory/used":{"post":{"operationId":"memoryUsed","summary":"Declare which memories an answer actually used (quality loop)","description":"REST mirror of the MCP `memory_used` tool. The agent declares the docids ('#ab12cd' hex prefixes) or paths of the memories it relied on; optionally a verdict (\"confirmed\"/\"corrected\") when the user reacted to what the memory said. Feeds usage coverage, ranking signals and the trust loop. Infra agents excluded downstream.\n","tags":["Observability"],"parameters":[{"$ref":"#/components/parameters/TenantIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["docids"],"properties":{"docids":{"type":"array","items":{"type":"string"},"description":"Docids ('#ab12cd') or document paths; [] declares an empty working set (no usage event)"},"verdict":{"type":"string","enum":["confirmed","corrected"],"description":"User confirmed or corrected what the memory said (V5)"},"note":{"type":"string","description":"Why the memories were useful"}}}}}},"responses":{"200":{"description":"Declaration recorded","content":{"application/json":{"schema":{"type":"object","properties":{"declared":{"type":"integer"},"marked":{"type":"integer"},"unresolved":{"type":"array","items":{"type":"string"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"vbm_ | vb_agent_ | vb_","description":"MCP token `vbm_<65 base62 chars> (69 chars)`: Token scopes (`read`, `write`, `admin`, granular ones from src/mcp-scopes.ts; `[]` grants nothing), additive toolsets (`agent,graph`), allowed collections and audience (RFC 8707). An OAuth token asked for MCP or for the app is valid at both (sibling audience, ADR-085); ingest requires `write`. Agent account key `vb_agent_<random>`: Implicit scopes `read write mcp read_log:read secrets` at the REST guard (no billing, pricing, admin or connector credentials); capped at `read write` on the OAuth consent. Service key `vb_<tenant-slug>_<random>`: Implicit scopes `read write mcp read_log:read secrets` at the REST guard (no billing, pricing, admin or connector credentials); capped at `read write` on the OAuth consent. The tenant comes from the credential. Matrix: https://valorbrain-api.valor.digital/.well-known/valorbrain-credentials.json"}},"parameters":{"TenantIdHeader":{"name":"X-Tenant-ID","in":"header","required":false,"description":"Tenant UUID, meaningful only for the platform credential, which spans tenants. A tenant credential carries its own tenant; a header that disagrees with it is refused (403).\n","schema":{"type":"string","format":"uuid"}},"MoneyTenantIdHeader":{"name":"X-Tenant-ID","in":"header","required":true,"description":"Explicit target tenant UUID for a money operation. The tenant is never accepted from the JSON body.\n","schema":{"type":"string","format":"uuid"}}},"responses":{"BadRequest":{"description":"Invalid request parameters or body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"Missing or invalid authentication","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Forbidden":{"description":"Insufficient permissions (admin required)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"BillingConflict":{"description":"Source idempotency, settlement proof, provider identity, or subscription period/state conflict. Cross-tenant ownership is never disclosed.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingEventErrorResponse"}}}},"BillingUnprocessable":{"description":"Recognized billing event that cannot be applied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingEventErrorResponse"}}}}},"schemas":{"Commitment":{"type":"object","description":"A promise made to someone outside the company (ADR-087)","properties":{"id":{"type":"string","format":"uuid"},"key":{"type":"string"},"promise":{"type":"string"},"counterpart":{"type":"string"},"owner":{"type":"string"},"due_on":{"type":"string","format":"date"},"first_due_on":{"type":"string","format":"date","description":"Due date when first recorded; never moves"},"made_on":{"type":"string","format":"date"},"evidence":{"type":"string"},"progress":{"type":"string","nullable":true},"status":{"type":"string","enum":["prometido","em_andamento","em_risco","cumprido","perdido","liberado"]},"closing_reason":{"type":"string","nullable":true},"closed_at":{"type":"string","format":"date-time","nullable":true},"closed_by":{"type":"string","nullable":true},"refs":{"type":"array","items":{"type":"string"}},"authority":{"type":"string","enum":["human","designated","import","agent"]},"source_agent":{"type":"string","nullable":true},"user_id":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"days_left":{"type":"integer","description":"Days from today to due_on; negative when overdue"}}},"CommitmentRecordResult":{"type":"object","properties":{"commitment":{"$ref":"#/components/schemas/Commitment"},"created":{"type":"boolean"},"reopened":{"type":"boolean"},"same_counterpart":{"type":"array","description":"On create, other open commitments to the same counterpart (possible duplicates)","items":{"$ref":"#/components/schemas/Commitment"}}}},"MeetAliasResult":{"type":"object","properties":{"recorded":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}}}},"skipped":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"reason":{"type":"string"}}}}}},"Document":{"type":"object","properties":{"id":{"type":"integer","description":"Internal document ID"},"hash":{"type":"string","description":"Content hash (SHA-256)"},"collection":{"type":"string"},"path":{"type":"string"},"title":{"type":"string"},"content_type":{"type":"string"},"page_type":{"type":"string"},"visibility":{"type":"string","enum":["private","tenant","public"]},"embed_state":{"type":"string","enum":["pending","indexed","skipped","error"]},"is_pinned":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"SearchResult":{"type":"object","properties":{"docid":{"type":"string"},"filepath":{"type":"string"},"title":{"type":"string"},"score":{"type":"number"},"snippet":{"type":"string"},"body":{"type":"string"},"mirror_paths":{"type":"array","items":{"type":"string"},"description":"Other paths with the same content hash, collapsed into this result (the same file indexed under several paths). Lists only copies the caller can read; omitted when there are none. Returned by /search and /retrieve.\n"}}},"ErrorResponse":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}},"CreditBalance":{"type":"object","properties":{"balance":{"type":"number"},"currency":{"type":"string"}}},"RequestContext":{"type":"object","properties":{"tenantId":{"type":"string","nullable":true},"sourceAgent":{"type":"string","nullable":true},"sourceSystem":{"type":"string","nullable":true},"sessionId":{"type":"string","nullable":true}}},"BillingEventKind":{"type":"string","enum":["subscription_activation","subscription_renewal","subscription_payment_failed","subscription_cancelled","credit_grant","refund","reversal"]},"BillingEventApplyRequest":{"type":"object","additionalProperties":false,"required":["source","source_id","event_kind"],"description":"All four subscription events require provider_subscription_id, period_start, and period_end. Activation and renewal additionally require plan_id and the complete settlement bundle. Their paid period is compared by PostgreSQL without JavaScript timestamp rounding. credit_amount must be zero for every event except credit_grant, where it must be positive. Refund and reversal are recognized but return unsupported_billing_event (422).\n","allOf":[{"oneOf":[{"properties":{"period_start":{"type":"null"},"period_end":{"type":"null"}}},{"required":["period_start","period_end"],"properties":{"period_start":{"type":"string","format":"date-time"},"period_end":{"type":"string","format":"date-time"}}}]},{"oneOf":[{"properties":{"settlement_asset":{"type":"null"},"settlement_network":{"type":"null"},"settlement_amount_atomic":{"type":"null"},"settlement_decimals":{"type":"null"},"settlement_reference":{"type":"null"}}},{"required":["settlement_asset","settlement_network","settlement_amount_atomic","settlement_decimals","settlement_reference"],"properties":{"settlement_asset":{"type":"string","minLength":1},"settlement_network":{"type":"string","minLength":1},"settlement_amount_atomic":{"oneOf":[{"type":"string","pattern":"^[0-9]{1,38}$"},{"type":"integer","minimum":0,"maximum":9007199254740991}]},"settlement_decimals":{"type":"integer","minimum":0,"maximum":18},"settlement_reference":{"type":"string","minLength":1}}}]},{"if":{"properties":{"event_kind":{"enum":["subscription_activation","subscription_renewal","subscription_payment_failed","subscription_cancelled"]}},"required":["event_kind"]},"then":{"required":["provider_subscription_id","period_start","period_end"],"properties":{"provider_subscription_id":{"type":"string","minLength":1},"period_start":{"type":"string","format":"date-time"},"period_end":{"type":"string","format":"date-time"}}}},{"if":{"properties":{"event_kind":{"enum":["subscription_activation","subscription_renewal"]}},"required":["event_kind"]},"then":{"required":["plan_id","settlement_asset","settlement_network","settlement_amount_atomic","settlement_decimals","settlement_reference"],"properties":{"plan_id":{"type":"string","minLength":1},"settlement_asset":{"type":"string","minLength":1},"settlement_network":{"type":"string","minLength":1},"settlement_amount_atomic":{"oneOf":[{"type":"string","pattern":"^[0-9]{1,38}$"},{"type":"integer","minimum":0,"maximum":9007199254740991}]},"settlement_decimals":{"type":"integer","minimum":0,"maximum":18},"settlement_reference":{"type":"string","minLength":1}}}},{"if":{"properties":{"event_kind":{"const":"credit_grant"}},"required":["event_kind"]},"then":{"required":["credit_amount"],"properties":{"credit_amount":{"type":"number","exclusiveMinimum":0}}},"else":{"properties":{"credit_amount":{"type":"number","maximum":0}}}}],"properties":{"source":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9:_-]+$","description":"Provider-qualified source. Subscription events use stripe:*, solana:mpp:*, or manual_contract:*.\n"},"source_id":{"type":"string","minLength":1,"maxLength":512,"description":"Globally stable provider event identity."},"event_kind":{"$ref":"#/components/schemas/BillingEventKind"},"plan_id":{"type":["string","null"],"maxLength":64},"provider_subscription_id":{"type":["string","null"],"maxLength":512},"period_start":{"type":["string","null"],"format":"date-time","description":"RFC3339 with an explicit timezone. Original text is preserved in canonical hashing and final event results.\n"},"period_end":{"type":["string","null"],"format":"date-time","description":"RFC3339 with an explicit timezone and later than period_start.\n"},"settlement_asset":{"type":["string","null"],"maxLength":16,"description":"Normalized to uppercase."},"settlement_network":{"type":["string","null"],"maxLength":128,"description":"Normalized to lowercase before hashing and proof uniqueness checks."},"settlement_amount_atomic":{"oneOf":[{"type":"string","pattern":"^[0-9]{1,38}$"},{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"settlement_decimals":{"type":["integer","null"],"minimum":0,"maximum":18},"settlement_reference":{"type":["string","null"],"maxLength":512,"description":"Globally unique together with lowercase settlement_network when present, regardless of source or tenant.\n"},"credit_amount":{"type":"number","minimum":0,"maximum":1000000000,"multipleOf":1e-8,"default":0},"provider_metadata":{"type":"object","additionalProperties":true,"description":"Small public provider references only. Secret, private, credential, token, mandate, authorization, and signature keys are rejected.\n"},"metadata":{"type":"object","additionalProperties":true,"description":"Non-sensitive metadata copied to the credit ledger when applicable. The same secret/private/token/mandate/signature key filter applies.\n"}}},"BillingSubscriptionSnapshot":{"type":"object","required":["plan_id","status","billing_provider","provider_subscription_id","current_period_start","current_period_end","grace_until"],"properties":{"plan_id":{"type":"string"},"status":{"type":"string","enum":["free","incomplete","active","past_due","cancelled","expired"]},"billing_provider":{"type":["string","null"],"enum":["stripe","solana_mpp","manual_contract",null]},"provider_subscription_id":{"type":["string","null"]},"current_period_start":{"type":["string","null"],"format":"date-time"},"current_period_end":{"type":["string","null"],"format":"date-time"},"grace_until":{"type":["string","null"],"format":"date-time"}}},"BillingCreditResult":{"type":"object","required":["granted","balance_after"],"properties":{"granted":{"type":"number"},"balance_after":{"type":"number"}}},"BillingEventApplyResult":{"type":"object","required":["event_id","applied","replay","subscription"],"description":"Subscription is null for pure credit_grant events. Ignored events keep applied false and include a stable ignored_reason. An identical source replay sets replay true and applied false while preserving the stored subscription, credit, ignored_reason, and event id.\n","properties":{"event_id":{"type":"string","format":"uuid"},"applied":{"type":"boolean"},"replay":{"type":"boolean"},"subscription":{"oneOf":[{"$ref":"#/components/schemas/BillingSubscriptionSnapshot"},{"type":"null"}]},"ignored_reason":{"type":"string","enum":["stale_period","subscription_cancelled","grace_elapsed"]},"credit":{"$ref":"#/components/schemas/BillingCreditResult"}}},"BillingEventRecord":{"type":"object","required":["id","tenant_id","source","source_id","event_kind","request_hash","credit_amount","result","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"tenant_id":{"type":"string","format":"uuid"},"source":{"type":"string"},"source_id":{"type":"string"},"event_kind":{"$ref":"#/components/schemas/BillingEventKind"},"request_hash":{"type":"string","pattern":"^[0-9a-f]{64}$"},"plan_id":{"type":["string","null"]},"provider_subscription_id":{"type":["string","null"]},"period_start":{"type":["string","null"],"format":"date-time"},"period_end":{"type":["string","null"],"format":"date-time"},"settlement_asset":{"type":["string","null"]},"settlement_network":{"type":["string","null"],"description":"Lowercase canonical network identifier."},"settlement_amount_atomic":{"type":["string","null"],"pattern":"^[0-9]+$"},"settlement_decimals":{"type":["integer","null"]},"settlement_reference":{"type":["string","null"]},"credit_amount":{"type":"string","description":"PostgreSQL numeric serialized without precision loss."},"result":{"$ref":"#/components/schemas/BillingEventApplyResult"},"created_at":{"type":"string","format":"date-time"}}},"BillingEventsResponse":{"type":"object","required":["events"],"properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/BillingEventRecord"}}}},"SubscriptionCreditGrantRequest":{"type":"object","additionalProperties":false,"required":["invoice_id","subscription_id","plan_id","amount"],"description":"Legacy Stripe invoice credit. It is journaled as credit_grant and never creates, reads, or mutates tenant_subscriptions.\n","properties":{"invoice_id":{"type":"string","minLength":1,"maxLength":512},"subscription_id":{"type":"string","minLength":1,"maxLength":512},"plan_id":{"type":"string","minLength":1,"maxLength":64},"amount":{"type":"number","exclusiveMinimum":0,"maximum":1000000000,"multipleOf":1e-8},"period_start":{"type":["string","null"],"format":"date-time"},"period_end":{"type":["string","null"],"format":"date-time"}}},"SubscriptionCreditGrantResponse":{"type":"object","required":["ok","balance_after","granted"],"properties":{"ok":{"type":"boolean","enum":[true]},"balance_after":{"type":"number","description":"Zero on an identical legacy invoice replay."},"granted":{"type":"number","description":"Zero on an identical legacy invoice replay."},"idempotent_skip":{"type":"boolean"}}},"BillingEventErrorResponse":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Includes idempotency_conflict, settlement_proof_conflict, subscription_provider_conflict, subscription_period_conflict, invalid_subscription_transition, and unsupported_billing_event.\n"},"message":{"type":"string","description":"Present for validation errors; omitted for conflicts and higher statuses."}}}}}}