Files

905 lines
37 KiB
YAML

openapi: 3.1.0
info:
title: RAGCore API
version: 1.2.1
description: Central secure local-first retrieval platform contract.
servers:
- url: https://ragcore.example.test
security:
- bearerAuth: []
tags:
- name: health
- name: control
- name: ingestion
- name: retrieval
- name: operations
paths:
/health/live:
get:
security: []
tags: [health]
operationId: getLiveness
responses:
'200':
description: Process is alive.
content:
application/json:
schema: {$ref: '#/components/schemas/Health'}
/health/ready:
get:
security: []
tags: [health]
operationId: getReadiness
responses:
'200':
description: Required dependencies are ready.
content:
application/json:
schema: {$ref: '#/components/schemas/Readiness'}
'503': {$ref: '#/components/responses/Problem'}
/v1/applications:
get:
tags: [control]
operationId: listApplications
parameters:
- {$ref: '#/components/parameters/Cursor'}
- {$ref: '#/components/parameters/Limit'}
responses:
'200':
description: Applications visible to the caller.
content:
application/json:
schema:
$ref: '#/components/schemas/ApplicationPage'
'401': {$ref: '#/components/responses/Problem'}
'403': {$ref: '#/components/responses/Problem'}
post:
tags: [control]
operationId: createApplication
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/ApplicationCreate'}
responses:
'201':
description: Application created.
content:
application/json:
schema: {$ref: '#/components/schemas/Application'}
'400': {$ref: '#/components/responses/Problem'}
'403': {$ref: '#/components/responses/Problem'}
'409': {$ref: '#/components/responses/Problem'}
/v1/applications/{application_id}/credentials:
post:
tags: [control]
operationId: issueApplicationCredential
parameters:
- {$ref: '#/components/parameters/ApplicationId'}
- {$ref: '#/components/parameters/IdempotencyKey'}
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/CredentialIssueRequest'}
responses:
'201':
description: Credential issued; plaintext token is returned only here.
content:
application/json:
schema: {$ref: '#/components/schemas/CredentialIssued'}
'403': {$ref: '#/components/responses/Problem'}
'404': {$ref: '#/components/responses/Problem'}
/v1/knowledge-spaces:
get:
tags: [control]
operationId: listKnowledgeSpaces
parameters:
- {$ref: '#/components/parameters/Cursor'}
- {$ref: '#/components/parameters/Limit'}
responses:
'200':
description: Authorized spaces.
content:
application/json:
schema: {$ref: '#/components/schemas/KnowledgeSpacePage'}
post:
tags: [control]
operationId: createKnowledgeSpace
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/KnowledgeSpaceCreate'}
responses:
'201':
description: Knowledge space created.
content:
application/json:
schema: {$ref: '#/components/schemas/KnowledgeSpace'}
'403': {$ref: '#/components/responses/Problem'}
'409': {$ref: '#/components/responses/Problem'}
/v1/knowledge-spaces/{space_id}/grants:
post:
tags: [control]
operationId: createSpaceGrant
parameters:
- {$ref: '#/components/parameters/SpaceId'}
- {$ref: '#/components/parameters/IdempotencyKey'}
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/SpaceGrantCreate'}
responses:
'201':
description: Grant created.
content:
application/json:
schema: {$ref: '#/components/schemas/SpaceGrant'}
'403': {$ref: '#/components/responses/Problem'}
'404': {$ref: '#/components/responses/Problem'}
/v1/policies:
get:
tags: [control]
operationId: listPolicies
parameters:
- {$ref: '#/components/parameters/Cursor'}
- {$ref: '#/components/parameters/Limit'}
responses:
'200':
description: Workspace policies visible to the caller.
content:
application/json:
schema: {$ref: '#/components/schemas/PolicyPage'}
'403': {$ref: '#/components/responses/Problem'}
post:
tags: [control]
operationId: createPolicy
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/PolicyCreate'}
responses:
'201':
description: Policy created.
content:
application/json:
schema: {$ref: '#/components/schemas/Policy'}
'400': {$ref: '#/components/responses/Problem'}
'403': {$ref: '#/components/responses/Problem'}
'404': {$ref: '#/components/responses/Problem'}
'409': {$ref: '#/components/responses/Problem'}
/v1/uploads:
post:
tags: [ingestion]
operationId: createUpload
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [space_id, file]
properties:
space_id: {type: string, format: uuid}
source_id: {type: [string, 'null'], format: uuid}
external_id: {type: [string, 'null'], maxLength: 512, description: 'The stable id of this item at its source (e.g. a wiki page id). Requires source_id -- without one there is nothing stable to deduplicate against. Ingesting the same (source_id, external_id) pair again creates a new version of the same document instead of a duplicate.'}
locale: {type: [string, 'null'], maxLength: 35, description: 'BCP-47-ish language tag for the content; defaults to en.'}
file: {type: string, format: binary}
responses:
'202':
description: Upload accepted and ingestion job created.
content:
application/json:
schema: {$ref: '#/components/schemas/AcceptedJob'}
'400': {$ref: '#/components/responses/Problem'}
'403': {$ref: '#/components/responses/Problem'}
'413': {$ref: '#/components/responses/Problem'}
'415': {$ref: '#/components/responses/Problem'}
/v1/sources:
get:
tags: [ingestion]
operationId: listSources
parameters:
- {$ref: '#/components/parameters/Cursor'}
- {$ref: '#/components/parameters/Limit'}
- name: space_id
in: query
schema: {type: string, format: uuid}
responses:
'200':
description: Authorized sources.
content:
application/json:
schema: {$ref: '#/components/schemas/SourcePage'}
post:
tags: [ingestion]
operationId: createSource
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/SourceCreate'}
responses:
'201':
description: Source created.
content:
application/json:
schema: {$ref: '#/components/schemas/Source'}
'403': {$ref: '#/components/responses/Problem'}
/v1/sources/{source_id}/syncs:
post:
tags: [ingestion]
operationId: startSourceSync
parameters:
- {$ref: '#/components/parameters/SourceId'}
- {$ref: '#/components/parameters/IdempotencyKey'}
responses:
'202':
description: Synchronization scheduled.
content:
application/json:
schema: {$ref: '#/components/schemas/AcceptedJob'}
'403': {$ref: '#/components/responses/Problem'}
'404': {$ref: '#/components/responses/Problem'}
/v1/documents:
get:
tags: [ingestion]
operationId: listDocuments
description: >-
Looks a document up by the sync client's own identity -- the primitive
a client uses to decide skip-vs-update by comparing content_sha256 /
source_fingerprint before ever uploading. With both filters: zero or
one item; an unmatched filter is an empty page, not a 404. With only
source_id: EVERY document that sync identity owns in the workspace
(active and retired, each with its searchable version when it has
one) -- the authoritative current set a sync client diffs its expected
set against instead of trusting its own memory. Bounded (a sync source
is a bounded set by construction), not paginated; no space_id/status
browse mode.
parameters:
- name: source_id
in: query
required: true
schema: {type: string, format: uuid}
- name: external_id
in: query
required: false
schema: {type: string, maxLength: 512}
responses:
'200':
description: Zero or one matching document (with external_id), or every document of the source identity (without).
content:
application/json:
schema: {$ref: '#/components/schemas/DocumentPage'}
'400': {$ref: '#/components/responses/Problem'}
'401': {$ref: '#/components/responses/Problem'}
'403': {$ref: '#/components/responses/Problem'}
/v1/documents/{document_id}:
get:
tags: [ingestion]
operationId: getDocument
parameters: [{$ref: '#/components/parameters/DocumentId'}]
responses:
'200':
description: Document with its active (published) version, if any.
content:
application/json:
schema: {$ref: '#/components/schemas/Document'}
'401': {$ref: '#/components/responses/Problem'}
'403': {$ref: '#/components/responses/Problem'}
'404': {$ref: '#/components/responses/Problem'}
/v1/documents/{document_id}/versions:
get:
tags: [ingestion]
operationId: listDocumentVersions
description: Every version of the document regardless of state, newest first. Not paginated.
parameters: [{$ref: '#/components/parameters/DocumentId'}]
responses:
'200':
description: Every version of this document.
content:
application/json:
schema: {$ref: '#/components/schemas/DocumentVersionPage'}
'401': {$ref: '#/components/responses/Problem'}
'403': {$ref: '#/components/responses/Problem'}
'404': {$ref: '#/components/responses/Problem'}
/v1/documents/{document_id}/retire:
post:
tags: [ingestion]
operationId: retireDocument
description: >-
Soft-retires a document (source_deleted=true) via the existing
mark_source_deleted; never a hard delete. Idempotent. Callers should
only call this after a complete, successful source scan confirms the
item is genuinely gone -- a partial or failed scan must not retire
anything, per the reconcile rule in
docs/04_INGESTION/CONNECTORS.md. Not enforced server-side: there is
no concept of "a scan" on this side of the API.
parameters: [{$ref: '#/components/parameters/DocumentId'}]
responses:
'200':
description: The now-retired document.
content:
application/json:
schema: {$ref: '#/components/schemas/Document'}
'401': {$ref: '#/components/responses/Problem'}
'403': {$ref: '#/components/responses/Problem'}
'404': {$ref: '#/components/responses/Problem'}
/v1/spaces/{space_id}/index/reconcile:
post:
tags: [ingestion]
operationId: reconcileSpaceIndex
description: >-
Brings the searchable index of ONE knowledge space in line with its
published, non-retired document versions: points that belong to a
superseded version or to a retired (source_deleted) document are
removed; nothing is re-embedded or inserted. Idempotent; scoped to
the given space only (other spaces in the same collection are never
read or written). The natural last step of a sync client's run after
it has uploaded new versions and retired vanished items.
parameters:
- name: space_id
in: path
required: true
schema: {type: string, format: uuid}
responses:
'200':
description: Reconciliation outcome (counts).
content:
application/json:
schema: {$ref: '#/components/schemas/SpaceIndexReconciliation'}
'401': {$ref: '#/components/responses/Problem'}
'403': {$ref: '#/components/responses/Problem'}
'404': {$ref: '#/components/responses/Problem'}
'503': {$ref: '#/components/responses/Problem'}
/v1/jobs/{job_id}:
get:
tags: [operations]
operationId: getJob
parameters: [{$ref: '#/components/parameters/JobId'}]
responses:
'200':
description: Job status.
content:
application/json:
schema: {$ref: '#/components/schemas/Job'}
'401': {$ref: '#/components/responses/Problem'}
'404': {$ref: '#/components/responses/Problem'}
/v1/search:
post:
tags: [retrieval]
operationId: searchKnowledge
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/SearchRequest'}
responses:
'200':
description: Authorized retrieval results.
content:
application/json:
schema: {$ref: '#/components/schemas/SearchResponse'}
'400': {$ref: '#/components/responses/Problem'}
'403': {$ref: '#/components/responses/Problem'}
'503': {$ref: '#/components/responses/Problem'}
'422': {$ref: '#/components/responses/Problem'}
/v1/context:
post:
tags: [retrieval]
operationId: buildContext
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/ContextRequest'}
responses:
'200':
description: Token-bounded context package.
content:
application/json:
schema: {$ref: '#/components/schemas/ContextResponse'}
'400': {$ref: '#/components/responses/Problem'}
'403': {$ref: '#/components/responses/Problem'}
'422': {$ref: '#/components/responses/Problem'}
'503': {$ref: '#/components/responses/Problem'}
/v1/answers:
post:
tags: [retrieval]
operationId: createAnswer
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/AnswerRequest'}
responses:
'200':
description: Grounded answer or explicit non-answer.
content:
application/json:
schema: {$ref: '#/components/schemas/AnswerResponse'}
text/event-stream:
schema: {type: string}
'400': {$ref: '#/components/responses/Problem'}
'403': {$ref: '#/components/responses/Problem'}
'422': {$ref: '#/components/responses/Problem'}
'429': {$ref: '#/components/responses/Problem'}
'503': {$ref: '#/components/responses/Problem'}
/v1/citations/{citation_id}:
get:
tags: [retrieval]
operationId: getCitation
parameters: [{$ref: '#/components/parameters/CitationId'}]
responses:
'200':
description: Authorized immutable citation.
content:
application/json:
schema: {$ref: '#/components/schemas/Citation'}
'404': {$ref: '#/components/responses/Problem'}
/v1/feedback:
post:
tags: [retrieval]
operationId: createFeedback
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
content:
application/json:
schema: {$ref: '#/components/schemas/FeedbackCreate'}
responses:
'201':
description: Feedback recorded.
content:
application/json:
schema: {$ref: '#/components/schemas/Feedback'}
'404': {$ref: '#/components/responses/Problem'}
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: RAGCore service token or OIDC session token
parameters:
Cursor:
name: cursor
in: query
schema: {type: string, maxLength: 2048}
Limit:
name: limit
in: query
schema: {type: integer, minimum: 1, maximum: 100, default: 50}
IdempotencyKey:
name: Idempotency-Key
in: header
required: true
schema: {type: string, minLength: 16, maxLength: 128}
ApplicationId: {name: application_id, in: path, required: true, schema: {type: string, format: uuid}}
SpaceId: {name: space_id, in: path, required: true, schema: {type: string, format: uuid}}
SourceId: {name: source_id, in: path, required: true, schema: {type: string, format: uuid}}
DocumentId: {name: document_id, in: path, required: true, schema: {type: string, format: uuid}}
JobId: {name: job_id, in: path, required: true, schema: {type: string, format: uuid}}
CitationId: {name: citation_id, in: path, required: true, schema: {type: string, format: uuid}}
responses:
Problem:
description: Stable problem-details response.
content:
application/problem+json:
schema: {$ref: '#/components/schemas/Problem'}
schemas:
SpaceIndexReconciliation:
type: object
required: [space_id, documents_total, documents_retired, searchable_versions, points_before, deleted_points, orphaned_documents, points_after]
properties:
space_id: {type: string, format: uuid}
documents_total: {type: integer}
documents_retired: {type: integer}
searchable_versions: {type: integer}
points_before: {type: integer}
deleted_points: {type: integer}
orphaned_documents: {type: integer}
points_after: {type: integer}
Health:
type: object
required: [status, service]
properties:
status: {const: ok}
service: {const: ragcore}
Readiness:
type: object
required: [status, components]
properties:
status: {enum: [ok, degraded, unavailable]}
components:
type: object
additionalProperties:
type: object
required: [status]
properties:
status: {enum: [ok, unavailable]}
latency_ms: {type: [number, 'null'], minimum: 0}
code: {type: [string, 'null']}
Problem:
type: object
required: [type, title, status, code, trace_id]
properties:
type: {type: string, format: uri}
title: {type: string}
status: {type: integer, minimum: 400, maximum: 599}
code: {type: string, pattern: '^[A-Z0-9_]+$'}
detail: {type: [string, 'null']}
trace_id: {type: string}
retry_after_seconds: {type: [integer, 'null'], minimum: 0}
errors: {type: array, items: {type: object}}
ApplicationCreate:
type: object
additionalProperties: false
required: [slug, display_name]
properties:
slug: {type: string, pattern: '^[a-z0-9][a-z0-9.-]{1,62}$'}
display_name: {type: string, minLength: 1, maxLength: 120}
description: {type: [string, 'null'], maxLength: 1000}
scopes: {type: array, uniqueItems: true, items: {$ref: '#/components/schemas/ApplicationScope'}}
default_space_ids: {type: array, uniqueItems: true, items: {type: string, format: uuid}}
Application:
allOf:
- {$ref: '#/components/schemas/ApplicationCreate'}
- type: object
required: [id, workspace_id, status, created_at]
properties:
id: {type: string, format: uuid}
workspace_id: {type: string, format: uuid}
status: {enum: [active, disabled]}
created_at: {type: string, format: date-time}
ApplicationPage:
type: object
required: [items]
properties:
items: {type: array, items: {$ref: '#/components/schemas/Application'}}
next_cursor: {type: [string, 'null']}
CredentialIssueRequest:
type: object
additionalProperties: false
required: [name, scopes]
properties:
name: {type: string, minLength: 1, maxLength: 120}
scopes: {type: array, uniqueItems: true, items: {type: string}}
expires_at: {type: [string, 'null'], format: date-time}
CredentialIssued:
type: object
required: [id, token, token_prefix, scopes, created_at]
properties:
id: {type: string, format: uuid}
token: {type: string, writeOnly: true}
token_prefix: {type: string}
scopes: {type: array, items: {type: string}}
created_at: {type: string, format: date-time}
expires_at: {type: [string, 'null'], format: date-time}
ApplicationScope:
enum: [search, context, answer, documents:read, citations:read, feedback:write, sources:sync]
PolicyCreate:
type: object
additionalProperties: false
required: [operation]
properties:
operation: {type: string, minLength: 1, maxLength: 120}
knowledge_space_id: {type: [string, 'null'], format: uuid}
required_scopes: {type: array, uniqueItems: true, items: {$ref: '#/components/schemas/ApplicationScope'}}
Policy:
allOf:
- {$ref: '#/components/schemas/PolicyCreate'}
- type: object
required: [id, workspace_id, active, created_at]
properties:
id: {type: string, format: uuid}
workspace_id: {type: string, format: uuid}
active: {type: boolean}
created_at: {type: string, format: date-time}
PolicyPage:
type: object
required: [items]
properties:
items: {type: array, items: {$ref: '#/components/schemas/Policy'}}
next_cursor: {type: [string, 'null']}
DataPolicy:
enum: [local_only, local_preferred, cloud_allowed, retrieval_only, no_generation]
KnowledgeSpaceCreate:
type: object
additionalProperties: false
required: [slug, display_name, data_policy]
properties:
slug: {type: string, pattern: '^[a-z0-9][a-z0-9._-]{1,100}$'}
display_name: {type: string, minLength: 1, maxLength: 120}
description: {type: [string, 'null'], maxLength: 2000}
data_policy: {$ref: '#/components/schemas/DataPolicy'}
minimum_trust: {enum: [trusted_internal, trusted_external, unverified, user_uploaded, internet]}
retention_days: {type: integer, minimum: 1, maximum: 3650, default: 90}
KnowledgeSpace:
allOf:
- {$ref: '#/components/schemas/KnowledgeSpaceCreate'}
- type: object
required: [id, workspace_id, status, created_at]
properties:
id: {type: string, format: uuid}
workspace_id: {type: string, format: uuid}
status: {enum: [active, disabled]}
created_at: {type: string, format: date-time}
KnowledgeSpacePage:
type: object
required: [items]
properties:
items: {type: array, items: {$ref: '#/components/schemas/KnowledgeSpace'}}
next_cursor: {type: [string, 'null']}
SpaceGrantCreate:
type: object
additionalProperties: false
required: [principal_type, principal_id, permissions]
properties:
principal_type: {enum: [application, user, role]}
principal_id: {type: string, format: uuid}
permissions: {type: array, uniqueItems: true, items: {enum: [read, search, context, answer, manage_sources, manage_space]}}
SpaceGrant:
allOf:
- {$ref: '#/components/schemas/SpaceGrantCreate'}
- type: object
required: [id, space_id, created_at]
properties:
id: {type: string, format: uuid}
space_id: {type: string, format: uuid}
created_at: {type: string, format: date-time}
SourceType: {enum: [upload, folder, git, website]}
SourceCreate:
type: object
additionalProperties: false
required: [space_id, type, name, config]
properties:
space_id: {type: string, format: uuid}
type: {$ref: '#/components/schemas/SourceType'}
name: {type: string, minLength: 1, maxLength: 160}
config: {type: object, description: Typed and redacted per connector; secrets use references.}
enabled: {type: boolean, default: true}
Source:
allOf:
- {$ref: '#/components/schemas/SourceCreate'}
- type: object
required: [id, status, created_at]
properties:
id: {type: string, format: uuid}
status: {enum: [active, disabled, degraded]}
last_sync_at: {type: [string, 'null'], format: date-time}
created_at: {type: string, format: date-time}
SourcePage:
type: object
required: [items]
properties:
items: {type: array, items: {$ref: '#/components/schemas/Source'}}
next_cursor: {type: [string, 'null']}
DocumentVersionStatus:
enum: [acquired, security_checked, parsed, normalized, chunked, embedded, indexed, validated, published, superseded, retired, failed]
DocumentVersion:
type: object
required: [id, document_id, status, source_fingerprint, content_sha256]
properties:
id: {type: string, format: uuid}
document_id: {type: string, format: uuid}
status: {$ref: '#/components/schemas/DocumentVersionStatus'}
source_fingerprint: {type: string}
content_sha256: {type: string, pattern: '^[a-f0-9]{64}$'}
created_at: {type: [string, 'null'], format: date-time}
DocumentVersionPage:
type: object
required: [items]
properties:
items: {type: array, items: {$ref: '#/components/schemas/DocumentVersion'}}
next_cursor: {type: [string, 'null']}
Document:
type: object
required: [id, space_id, external_id, title, status]
properties:
id: {type: string, format: uuid}
space_id: {type: string, format: uuid}
source_id: {type: [string, 'null'], format: uuid, description: 'Null for a document ingested without a source_id (e.g. an ad hoc upload).'}
external_id: {type: string}
title: {type: string}
status: {enum: [active, source_deleted]}
active_version: {oneOf: [{$ref: '#/components/schemas/DocumentVersion'}, {type: 'null'}]}
DocumentPage:
type: object
required: [items]
properties:
items: {type: array, items: {$ref: '#/components/schemas/Document'}}
next_cursor: {type: [string, 'null']}
JobStatus: {enum: [ready, leased, succeeded, retry_wait, terminal]}
Job:
type: object
required: [id, operation, status, attempt_count, max_attempts]
properties:
id: {type: string, format: uuid}
operation: {type: string}
status: {$ref: '#/components/schemas/JobStatus'}
attempt_count: {type: integer, minimum: 0}
max_attempts: {type: integer, minimum: 1}
last_error_code: {type: [string, 'null']}
AcceptedJob:
type: object
required: [job_id, status_url]
properties:
job_id: {type: string, format: uuid}
status_url: {type: string, format: uri-reference}
SearchFilter:
type: object
additionalProperties: false
properties:
content_types: {type: array, uniqueItems: true, maxItems: 50, items: {type: string}}
languages: {type: array, uniqueItems: true, maxItems: 50, items: {type: string, minLength: 2, maxLength: 16}}
source_ids: {type: array, uniqueItems: true, maxItems: 50, items: {type: string, format: uuid}}
document_ids: {type: array, uniqueItems: true, maxItems: 50, items: {type: string, format: uuid}}
trust_levels: {type: array, uniqueItems: true, maxItems: 50, items: {enum: [trusted_internal, trusted_external, unverified, user_uploaded, internet]}}
SearchRequest:
type: object
additionalProperties: false
required: [query]
properties:
query: {type: string, minLength: 1, maxLength: 8000, pattern: '.*\S.*'}
requested_space_ids: {type: array, uniqueItems: true, maxItems: 50, items: {type: string, format: uuid}}
profile: {type: string, pattern: '^[a-z0-9][a-z0-9_-]{0,63}$', default: hybrid-v1}
max_results: {type: integer, minimum: 1, maximum: 50, default: 10}
filters: {$ref: '#/components/schemas/SearchFilter'}
include_debug: {type: boolean, default: false, description: Authorized Query Lab use only.}
Citation:
type: object
additionalProperties: false
required: [id, document_id, document_version_id, title, excerpt, excerpt_sha256, source_uri]
properties:
id: {type: string, format: uuid}
document_id: {type: string, format: uuid}
document_version_id: {type: string, format: uuid}
title: {type: string}
section: {type: [string, 'null']}
page_start: {type: [integer, 'null'], minimum: 1}
page_end: {type: [integer, 'null'], minimum: 1}
line_start: {type: [integer, 'null'], minimum: 1}
line_end: {type: [integer, 'null'], minimum: 1}
excerpt: {type: string}
excerpt_sha256: {type: string, pattern: '^[a-f0-9]{64}$'}
source_uri: {type: string, description: 'Stable internal locator ragcore://source/<source_id>.'}
source_id: {type: [string, 'null'], format: uuid, description: 'Canonical source the cited document belongs to.'}
source_name: {type: [string, 'null'], description: 'Registry name of the source (e.g. Uploads, Infra runbooks).'}
source_kind: {type: [string, 'null'], enum: [upload, folder, git, website, null]}
locator: {type: [string, 'null'], description: 'The item within the source (external id / path).'}
revision: {type: [string, 'null'], description: 'Connector revision of the cited version (e.g. a Git commit).'}
display: {type: string, description: 'Human-readable one-line provenance, e.g. "Network Design.pdf · p. 18 · Firewall Segmentation".'}
CitationResponse:
type: object
additionalProperties: false
required: [id, document_id, document_version_id, title, excerpt, excerpt_sha256, source_uri]
properties:
id: {type: string, format: uuid}
document_id: {type: string, format: uuid}
document_version_id: {type: string, format: uuid}
title: {type: string}
section: {type: [string, 'null']}
page_start: {type: [integer, 'null'], minimum: 1}
page_end: {type: [integer, 'null'], minimum: 1}
line_start: {type: [integer, 'null'], minimum: 1}
line_end: {type: [integer, 'null'], minimum: 1}
excerpt: {type: string}
excerpt_sha256: {type: string, pattern: '^[a-f0-9]{64}$'}
source_uri: {type: string, description: 'Stable internal locator ragcore://source/<source_id>.'}
source_id: {type: [string, 'null'], format: uuid, description: 'Canonical source the cited document belongs to.'}
source_name: {type: [string, 'null'], description: 'Registry name of the source (e.g. Uploads, Infra runbooks).'}
source_kind: {type: [string, 'null'], enum: [upload, folder, git, website, null]}
locator: {type: [string, 'null'], description: 'The item within the source (external id / path).'}
revision: {type: [string, 'null'], description: 'Connector revision of the cited version (e.g. a Git commit).'}
display: {type: string, description: 'Human-readable one-line provenance, e.g. "Network Design.pdf · p. 18 · Firewall Segmentation".'}
SearchResult:
type: object
required: [chunk_id, text, rank, citation]
properties:
chunk_id: {type: string, format: uuid}
parent_chunk_id: {type: [string, 'null'], format: uuid}
text: {type: string}
rank: {type: integer, minimum: 1}
scores:
type: object
properties:
dense: {type: [number, 'null']}
sparse: {type: [number, 'null']}
fused: {type: [number, 'null']}
rerank: {type: [number, 'null']}
citation: {$ref: '#/components/schemas/Citation'}
SearchResponse:
type: object
required: [retrieval_run_id, effective_space_ids, results, degraded]
properties:
retrieval_run_id: {type: string, format: uuid}
effective_space_ids: {type: array, items: {type: string, format: uuid}}
results: {type: array, items: {$ref: '#/components/schemas/SearchResult'}}
degraded: {type: boolean}
degradation_codes: {type: array, items: {type: string}}
timings_ms: {type: object, additionalProperties: {type: number, minimum: 0}}
ContextRequest:
type: object
additionalProperties: false
required: [query]
properties:
query: {type: string, minLength: 1, maxLength: 8000, pattern: '.*\S.*'}
requested_space_ids: {type: array, uniqueItems: true, maxItems: 50, items: {type: string, format: uuid}}
profile: {type: string, pattern: '^[a-z0-9][a-z0-9_-]{0,63}$', default: hybrid-v1}
max_results: {type: integer, minimum: 1, maximum: 50, default: 10}
filters: {$ref: '#/components/schemas/SearchFilter'}
include_debug: {type: boolean, default: false, description: Authorized Query Lab use only.}
max_context_tokens: {type: integer, minimum: 256, maximum: 64000, default: 8000}
ContextBlockResponse:
type: object
additionalProperties: false
required: [text, citation_ids, token_count]
properties:
text: {type: string}
citation_ids: {type: array, minItems: 1, items: {type: string, format: uuid}}
token_count: {type: integer, minimum: 1}
AnswerabilityStatus: {enum: [answerable, partially_answerable, not_answerable, conflicting_evidence]}
ContextResponse:
type: object
additionalProperties: false
required: [retrieval_run_id, answerability, token_count]
properties:
retrieval_run_id: {type: string, format: uuid}
answerability: {$ref: '#/components/schemas/AnswerabilityStatus'}
answerability_reasons: {type: array, items: {type: string}}
blocks: {type: array, items: {$ref: '#/components/schemas/ContextBlockResponse'}}
citations: {type: array, items: {$ref: '#/components/schemas/CitationResponse'}}
token_count: {type: integer, minimum: 0}
degraded: {type: boolean, default: false, description: 'True when retrieval fell back from its normal path (e.g. an unreachable reranker) but still produced these citations from a safe fallback ranking.'}
degradation_reasons: {type: array, items: {type: string}}
AnswerRequest:
allOf:
- {$ref: '#/components/schemas/ContextRequest'}
- type: object
properties:
generation_profile: {type: string, default: answer-local-balanced-v1}
stream: {type: boolean, default: false}
AnswerClaim:
type: object
required: [text, citation_ids]
properties:
text: {type: string}
citation_ids: {type: array, minItems: 1, items: {type: string, format: uuid}}
AnswerResponse:
type: object
required: [answer_id, retrieval_run_id, answerability, answer, citations, claims]
properties:
answer_id: {type: string, format: uuid}
retrieval_run_id: {type: string, format: uuid}
answerability: {$ref: '#/components/schemas/AnswerabilityStatus'}
answer: {type: string}
claims: {type: array, items: {$ref: '#/components/schemas/AnswerClaim'}}
citations: {type: array, items: {$ref: '#/components/schemas/Citation'}}
degraded: {type: boolean, default: false, description: 'True on a generation retry or when retrieval degraded (e.g. an unreachable reranker fell back to fused ranking) -- the answer and citations are still real, not fabricated.'}
degradation_reasons: {type: array, items: {type: string}}
FeedbackCreate:
type: object
additionalProperties: false
required: [target_type, target_id, rating]
properties:
target_type: {enum: [retrieval_run, answer]}
target_id: {type: string, format: uuid}
rating: {enum: [helpful, not_helpful, incorrect, incomplete, unsafe]}
comment: {type: [string, 'null'], maxLength: 4000}
Feedback:
allOf:
- {$ref: '#/components/schemas/FeedbackCreate'}
- type: object
required: [id, created_at]
properties:
id: {type: string, format: uuid}
created_at: {type: string, format: date-time}