Skip to main content

Knowledge Base (RAG)

Syntropy's Knowledge Base API lets connected products store documents, run embedding-based ingestion, and serve retrieval-augmented answers — without managing any vector infrastructure directly.

Authentication

KB endpoints sit under /api/{accountId}/... and use project API keys (not session auth). Pass the key as a Bearer token or x-api-key header.

Required scopes:

ScopeEndpoints
kb:writeRegister documents
kb:readQuery, poll document status

Endpoints

POST   /api/{accountId}/kb/docs
GET /api/{accountId}/kb/docs/{documentId}
POST /api/{accountId}/kb/query

Register a document

POST /api/{accountId}/kb/docs

Registers a document for ingestion. The document must already be uploaded to Syntropy Blob Storage; pass its blob key. Ingestion is asynchronous — the job is enqueued immediately and you can poll status via GET /kb/docs/{documentId}.

Re-registering the same blobKey for a tenant resets the document to pending and re-triggers ingestion, making it safe to call on updates.

Request

POST /api/{accountId}/kb/docs
Authorization: Bearer syn_sk_xxx
Content-Type: application/json
{
"tenantId": "firm_abc123",
"kbId": "default",
"blobKey": "tenants/firm_abc123/docs/contract-2026.pdf",
"title": "Service Agreement 2026",
"metadata": {
"practiceArea": "corporate",
"jurisdiction": "NY"
}
}
FieldTypeRequiredDescription
tenantIdstringOpaque string identifying the sub-tenant (e.g. a law firm ID). Auto-registered on first use.
kbIdstringKnowledge base partition. Defaults to "default".
blobKeystringKey in Syntropy Blob Storage pointing to the document file.
titlestringHuman-readable document title.
metadataobjectArbitrary key-value metadata stored on the document.

Response 201

{
"documentId": "uuid",
"jobId": "uuid",
"status": "pending"
}

Use documentId to poll ingestion status.


Get document status

GET /api/{accountId}/kb/docs/{documentId}

Poll this endpoint after registering a document to check ingestion progress.

Response 200

{
"document": {
"id": "uuid",
"tenantId": "firm_abc123",
"kbId": "default",
"blobKey": "tenants/firm_abc123/docs/contract-2026.pdf",
"title": "Service Agreement 2026",
"metadata": { "practiceArea": "corporate" },
"sourceType": "upload",
"ingestionStatus": "complete",
"ingestionError": null,
"createdAt": "2026-03-20T10:00:00.000Z",
"updatedAt": "2026-03-20T10:01:23.000Z"
}
}

ingestionStatus values:

ValueMeaning
pendingQueued, not yet picked up
processingWorker is actively ingesting
completeChunks written, document is queryable
failedIngestion failed; see ingestionError

Query the knowledge base

POST /api/{accountId}/kb/query

Embeds the question, runs a vector similarity search over the tenant's documents, and returns an LLM-generated answer with source citations.

Request

POST /api/{accountId}/kb/query
Authorization: Bearer syn_sk_xxx
Content-Type: application/json
{
"tenantId": "firm_abc123",
"kbId": "default",
"question": "What are the termination clauses in the 2026 service agreement?",
"topK": 8
}
FieldTypeRequiredDescription
tenantIdstringTenant to search within.
kbIdstringKnowledge base partition. Defaults to "default".
questionstringThe question to answer.
topKintegerNumber of chunks to retrieve (1–50, default 8).

Response 200

{
"answer": "The agreement may be terminated by either party with 30 days written notice...",
"citations": [
{
"documentId": "uuid",
"blobKey": "tenants/firm_abc123/docs/contract-2026.pdf",
"title": "Service Agreement 2026",
"snippet": "Either party may terminate this Agreement upon 30 days written notice...",
"score": 0.9312
}
]
}

score is cosine similarity (0–1); higher is more relevant. Citations are deduplicated by document and ordered by relevance.


Blob key conventions

Documents should be uploaded to Syntropy Blob Storage using a path that scopes them to the tenant:

tenants/{tenantId}/docs/{uuid}           # documents for ingestion
tenants/{tenantId}/intake-attachments/… # intake session uploads
tenants/{tenantId}/assets/… # branding / static assets

Use createPresignedUploadUrl from @eclosion-tech/syntropy-blob-storage to issue short-lived upload URLs for direct browser uploads, then pass the resulting blob key to POST /kb/docs.


Ingestion pipeline

  1. Blob is fetched from storage
  2. Text is extracted (PDF, plain text)
  3. Text is chunked (~1000 chars, 200 char overlap)
  4. Chunks are embedded via text-embedding-3-small (1536 dims)
  5. Embeddings are written to kb_chunks with an HNSW index
  6. Document status updated to complete

On failure, status is set to failed and ingestionError contains the reason. Re-registering the document triggers a clean re-ingestion.


Multi-tenancy

All data is scoped to (accountId, tenantId). A query for tenantId: "firm_abc" can never return documents belonging to tenantId: "firm_xyz", even within the same Syntropy account. Isolation is enforced at both the application and database (RLS) layers.