Skip to content

Knowledge Base & Embeddings

A Knowledge Base is a named container of chunked, embedded text that a workflow’s LLM node can retrieve against at runtime (RAG). This page is the exhaustive field-by-field reference for all fourteen knowledge-base and embedding operations. For a conceptual walkthrough of how retrieval fits into a workflow, see Knowledge Base; for using the raw embedding endpoint outside the platform’s own retrieval path, see Embeddings.

All operations on this page except embed require a valid JWT (Authorization: Bearer <token>) issued to a user belonging to the tenant that owns the knowledge base — see Authentication. embed uses a separate X-Api-Key header instead (see below). Every knowledge base, document, and chunk is scoped to the caller’s tenant.

Every request in this reference must include the x-huat-platform: customer header in addition to standard GraphQL headers.

FieldTypeNotes
idInt!
nameString!
descriptionString
createdAtDateTime!
updatedAtDateTime!
FieldTypeNotes
idInt!
fileNameString!
mimeTypeString!
fileSizeBytesInt!
parseStatusString!PENDING | PARSING | READY | FAILED. See the parse lifecycle below.
parseErrorStringSet only when parseStatus is FAILED.
rawTextString!The document’s full extracted/stored text. Only ever populated by the singular knowledgeDocument(id) query belowknowledgeDocuments (the list query) never selects it, so listing a knowledge base with many/large documents doesn’t pay for fetching all of their text. Empty while parseStatus is PENDING/PARSING.
createdAtDateTime!
updatedAtDateTime!

Returned only by retrieveChunks — a preview of a single retrieved chunk, not a persisted, independently-queryable resource.

FieldTypeNotes
chunkIdInt!
documentIdInt!The source document this chunk came from.
contentString!The chunk’s raw text.
scoreFloat!Cosine similarity to the query, higher is more relevant.

A document’s parseStatus moves through up to four states, depending on how it was created:

  • Inline text (via ingestDocument) — chunked, embedded, and persisted synchronously. parseStatus is READY immediately; there is nothing to poll.
  • Uploaded file (via requestKnowledgeDocumentUpload + confirmKnowledgeDocumentUpload) — parses asynchronously: PENDING (row created, job enqueued) → PARSING (job running) → READY (chunks embedded and available to retrieveChunks), or FAILED with parseError populated if parsing errors out.

There is no subscription for parse completion — poll knowledgeDocuments(knowledgeBaseId) and check the parseStatus field on the document you just uploaded, or simply refetch after a few seconds.


All knowledge bases owned by the current tenant.

Auth: JWT

knowledgeBases: [KnowledgeBaseDto!]!

Request

query ListKnowledgeBases {
knowledgeBases {
id
name
description
}
}
POST /graphql
Content-Type: application/json
Authorization: Bearer <jwt>
x-huat-platform: customer

Response

{
"data": {
"knowledgeBases": [
{
"id": 5,
"name": "Product Docs",
"description": "Internal product documentation"
},
{ "id": 6, "name": "Support FAQ", "description": null }
]
}
}

All documents in a knowledge base owned by this tenant, most recent first. Includes parseStatus — poll or refetch this after confirmKnowledgeDocumentUpload to see PENDINGPARSINGREADY (or FAILED, with parseError set) for an uploaded file. Inline-text documents (via ingestDocument) are READY immediately.

Auth: JWT

knowledgeDocuments(knowledgeBaseId: Int!): [KnowledgeDocumentDto!]!

Request

query ListKnowledgeDocuments($knowledgeBaseId: Int!) {
knowledgeDocuments(knowledgeBaseId: $knowledgeBaseId) {
id
fileName
mimeType
fileSizeBytes
parseStatus
parseError
}
}
{ "knowledgeBaseId": 5 }

Response

{
"data": {
"knowledgeDocuments": [
{
"id": 101,
"fileName": "product-manual.pdf",
"mimeType": "application/pdf",
"fileSizeBytes": 843201,
"parseStatus": "READY",
"parseError": null
},
{
"id": 102,
"fileName": "release-notes.pdf",
"mimeType": "application/pdf",
"fileSizeBytes": 120044,
"parseStatus": "PARSING",
"parseError": null
}
]
}
}

A single document owned by this tenant, including its full rawText — unlike knowledgeDocuments above, which never selects this field. Use this to fetch a document’s content for preview or editing (see updateKnowledgeDocumentText below).

Auth: JWT

knowledgeDocument(id: Int!): KnowledgeDocumentDto!

Request

query GetKnowledgeDocument($id: Int!) {
knowledgeDocument(id: $id) {
id
fileName
rawText
parseStatus
}
}
{ "id": 101 }

Response

{
"data": {
"knowledgeDocument": {
"id": 101,
"fileName": "product-manual.pdf",
"rawText": "Chapter 1: Getting Started\n\nTo reset your password, go to Settings > Security...",
"parseStatus": "READY"
}
}
}

Diagnostic/test query: embeds query and returns the topK (max 20) most semantically similar chunks from a knowledge base owned by this tenant, ordered by cosine similarity (most relevant first). This is the same retrieval path an LLM workflow node with knowledgeBaseId set uses internally — use this query to sanity-check what a workflow node would actually retrieve for a given user query, before wiring it into a live workflow.

Auth: JWT

retrieveChunks(knowledgeBaseId: Int!, query: String!, topK: Int): [KnowledgeChunkPreviewDto!]!

Arguments

ArgumentTypeRequiredNotes
knowledgeBaseIdInt!yes
queryString!yesThe text to embed and search against.
topKIntnoMax 20. Defaults to a small number if omitted — pass it explicitly if you need a specific count.

Request

query RetrieveChunks($knowledgeBaseId: Int!, $query: String!, $topK: Int) {
retrieveChunks(
knowledgeBaseId: $knowledgeBaseId
query: $query
topK: $topK
) {
chunkId
documentId
content
score
}
}
{
"knowledgeBaseId": 5,
"query": "How do I reset my password?",
"topK": 3
}

Response

{
"data": {
"retrieveChunks": [
{
"chunkId": 5501,
"documentId": 101,
"content": "To reset your password, go to Settings > Security and click 'Reset Password'...",
"score": 0.87
},
{
"chunkId": 5502,
"documentId": 101,
"content": "Password reset links expire after 24 hours...",
"score": 0.79
}
]
}
}

For the conceptual version of how retrieval feeds a workflow’s LLM node, see Knowledge Base.

API-key-tier: embeds each string in texts (same order in, same order out) using the same model/dimensions as this tenant’s knowledge base (gemini-embedding-001, 768 dims) — a drop-in source for a caller building their own vector index outside Wetel. Max 100 texts per call. usage.costUsd reflects the embedding model’s public paid-tier rate; inputTokens is a character-length estimate, not an exact tokenizer count, so costUsd is an estimate too.

Auth: API key (X-Api-Key header) — not a JWT. See Embeddings for how to generate a key.

embed(texts: [String!]!): EmbedResultDto!

EmbedResultDto fields

FieldTypeNotes
vectors[[Float!]!]!One embedding vector per input text, same order as texts. Always fully populated — never contains null entries.
usageEmbedUsageDto!{ inputTokens, outputTokens, costUsd }.

Request

query Embed($texts: [String!]!) {
embed(texts: $texts) {
vectors
usage {
inputTokens
outputTokens
costUsd
}
}
}
{ "texts": ["hello world", "a second string to embed"] }
POST /graphql
Content-Type: application/json
X-Api-Key: <api-key>
x-huat-platform: customer

Response

{
"data": {
"embed": {
"vectors": [
[0.0123, -0.0456, 0.0789],
[-0.0034, 0.0912, 0.0201]
],
"usage": {
"inputTokens": 6,
"outputTokens": 0,
"costUsd": 0.0000012
}
}
}
}

Note: the response above truncates each vector to 3 dimensions for readability — real vectors are 768-dimensional.


Creates a new named knowledge base container for this tenant. Subject to the plan’s max-knowledge-base quota.

Auth: JWT

createKnowledgeBase(input: CreateKnowledgeBaseInput!): KnowledgeBaseDto!

CreateKnowledgeBaseInput fields

FieldTypeRequired
nameString!yes
descriptionStringno

Request

mutation CreateKnowledgeBase($input: CreateKnowledgeBaseInput!) {
createKnowledgeBase(input: $input) {
id
name
description
}
}
{
"input": {
"name": "Product Docs",
"description": "Internal product documentation"
}
}
POST /graphql
Content-Type: application/json
Authorization: Bearer <jwt>
x-huat-platform: customer

Response

{
"data": {
"createKnowledgeBase": {
"id": 5,
"name": "Product Docs",
"description": "Internal product documentation"
}
}
}

Soft-deletes a knowledge base for this tenant. Does not cascade-delete its documents or chunks — this is documented, intentional behavior, not an oversight. After deleting a knowledge base, its documents and their chunks remain in storage and are simply no longer reachable through a live knowledge base row; plan for this if you need to reclaim storage or comply with a deletion request (delete documents individually beforehand, or handle cleanup out of band).

Auth: JWT

deleteKnowledgeBase(id: Int!): Boolean!

Request

mutation DeleteKnowledgeBase($id: Int!) {
deleteKnowledgeBase(id: $id)
}
{ "id": 6 }

Response

{ "data": { "deleteKnowledgeBase": true } }

Ingests inline pasted text into a knowledge base — chunks, embeds, and persists it synchronously, subject to plan quotas (chunk count and storage MB). Text only, max 500KB. For a real PDF/DOCX file, use requestKnowledgeDocumentUpload + confirmKnowledgeDocumentUpload instead — that path parses asynchronously. Returns the number of chunks created.

Auth: JWT

ingestDocument(input: IngestDocumentInput!): Int!

IngestDocumentInput fields

FieldTypeRequiredNotes
knowledgeBaseIdInt!yes
fileNameString!yesA display label — no real file is uploaded for this path.
textString!yesMax 500KB.

Request

mutation IngestDocument($input: IngestDocumentInput!) {
ingestDocument(input: $input)
}
{
"input": {
"knowledgeBaseId": 5,
"fileName": "faq-snippet.txt",
"text": "Q: How do I reset my password?\nA: Go to Settings > Security and click 'Reset Password'."
}
}

Response

{ "data": { "ingestDocument": 2 } }

Step 1 of file upload. Validates the knowledge base’s ownership and a soft (non-authoritative) quota pre-check, then returns a presigned PUT URL (15 minute expiry) plus the objectStorageKey to use when confirming. PDF, DOCX, Markdown, CSV, or XLSX, max 20MB (Markdown/CSV/XLSX added 2026-09-17). Rate-limited (10/min per client).

The browser (or your server) then PUTs the raw file bytes directly to uploadUrl — they never pass through the Wetel API server.

Auth: JWT

requestKnowledgeDocumentUpload(input: RequestKnowledgeDocumentUploadInput!): RequestKnowledgeDocumentUploadResult!

RequestKnowledgeDocumentUploadInput fields

FieldTypeRequiredNotes
knowledgeBaseIdInt!yes
fileNameString!yes
mimeTypeString!yesOne of: application/pdf, DOCX’s application/vnd.openxmlformats-officedocument.wordprocessingml.document, text/markdown, text/csv, or XLSX’s application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (last three added 2026-09-17).
declaredSizeBytesInt!yesMax 20MB. The real uploaded size is verified server-side in step 2 — this value is only used for the soft pre-check.

RequestKnowledgeDocumentUploadResult fields

FieldTypeNotes
uploadUrlString!Presigned PUT URL, expires in 15 minutes.
objectStorageKeyString!Pass this back unchanged to confirmKnowledgeDocumentUpload.

Request

mutation RequestUpload($input: RequestKnowledgeDocumentUploadInput!) {
requestKnowledgeDocumentUpload(input: $input) {
uploadUrl
objectStorageKey
}
}
{
"input": {
"knowledgeBaseId": 5,
"fileName": "product-manual.pdf",
"mimeType": "application/pdf",
"declaredSizeBytes": 843201
}
}

Response

{
"data": {
"requestKnowledgeDocumentUpload": {
"uploadUrl": "https://storage.example.com/knowledge-uploads/tenant-9/abc123?X-Signature=...",
"objectStorageKey": "knowledge-uploads/tenant-9/abc123"
}
}
}

Step 1.5 — the browser upload itself (not a Wetel API call):

PUT https://storage.example.com/knowledge-uploads/tenant-9/abc123?X-Signature=...
Content-Type: application/pdf
<raw file bytes>

Step 2 of file upload. Call this after the browser finishes PUTting the file to the URL from requestKnowledgeDocumentUpload. Verifies the upload actually completed, reserves storage quota against the real file size, creates the document row (parseStatus: PENDING), and enqueues async parsing. Poll the knowledgeDocuments query afterward to see parseStatus transition to READY (or FAILED with a parseError).

Auth: JWT

confirmKnowledgeDocumentUpload(input: ConfirmKnowledgeDocumentUploadInput!): ConfirmKnowledgeDocumentUploadResult!

ConfirmKnowledgeDocumentUploadInput fields

FieldTypeRequiredNotes
knowledgeBaseIdInt!yes
fileNameString!yes
mimeTypeString!yes
objectStorageKeyString!yesThe value returned by requestKnowledgeDocumentUpload.

ConfirmKnowledgeDocumentUploadResult fields

FieldTypeNotes
documentIdInt!
parseStatusString!Always PENDING on this response — the row was just created.

Request

mutation ConfirmUpload($input: ConfirmKnowledgeDocumentUploadInput!) {
confirmKnowledgeDocumentUpload(input: $input) {
documentId
parseStatus
}
}
{
"input": {
"knowledgeBaseId": 5,
"fileName": "product-manual.pdf",
"mimeType": "application/pdf",
"objectStorageKey": "knowledge-uploads/tenant-9/abc123"
}
}

Response

{
"data": {
"confirmKnowledgeDocumentUpload": {
"documentId": 101,
"parseStatus": "PENDING"
}
}
}

After this call, poll knowledgeDocuments(knowledgeBaseId: 5) and check parseStatus on document 101 until it reaches READY (or FAILED).

Soft-deletes a single document — and, unlike deleteKnowledgeBase above, its chunks too — releasing the tenant’s chunk-count and storage quota reservation. Does not delete the knowledge base itself.

Auth: JWT

deleteKnowledgeDocument(id: Int!): Boolean!

Request

mutation DeleteKnowledgeDocument($id: Int!) {
deleteKnowledgeDocument(id: $id)
}
{ "id": 101 }

Response

{ "data": { "deleteKnowledgeDocument": true } }

Re-runs ingestion for an existing document, using its current stored content — no new input required:

  • A FAILED file upload (one that still has its original uploaded bytes) is re-queued for a fresh extract+chunk+embed attempt — parseStatus goes back to PENDING, same as a first upload.
  • A READY document (of any origin) is re-chunked and re-embedded in place from its own rawText — useful after a chunking/embedding change, without re-uploading or re-pasting anything.
  • Rejected with a validation error if the document is currently PENDING or PARSING.

Auth: JWT

resyncKnowledgeDocument(id: Int!): Boolean!

Request

mutation ResyncKnowledgeDocument($id: Int!) {
resyncKnowledgeDocument(id: $id)
}
{ "id": 102 }

Response

{ "data": { "resyncKnowledgeDocument": true } }

Overwrites a document’s own text and re-chunks/re-embeds it in place, subject to plan quotas — the “edit” counterpart to resyncKnowledgeDocument above (which re-runs with the SAME text; this replaces it). Works for a document of any origin — file upload, pasted text, or URL ingest — once it’s been ingested at least once, since they all converge on the same rawText storage. A FAILED document can also be edited this way: fixing bad or missing content by hand is a valid recovery path distinct from resyncKnowledgeDocument’s “just retry the same bytes.” Rejected if the document is currently PENDING or PARSING.

Auth: JWT

updateKnowledgeDocumentText(id: Int!, text: String!): Boolean!

Request

mutation UpdateKnowledgeDocumentText($id: Int!, $text: String!) {
updateKnowledgeDocumentText(id: $id, text: $text)
}
{
"id": 101,
"text": "Chapter 1: Getting Started\n\nTo reset your password, go to Settings > Security and click 'Reset Password'. Links expire after 24 hours."
}

Response

{ "data": { "updateKnowledgeDocumentText": true } }

Ingests a single web page into a knowledge base: fetches url, extracts its text (HTML pages are stripped to plain text; text/plain pages are used as-is), then chunks/embeds/persists it synchronously, subject to plan quotas — same return shape as ingestDocument. Single page only — does not follow links. Rate-limited (10/min per client) since it triggers a real outbound fetch. Blocked for any URL that resolves to a private/internal/reserved IP address range (loopback, RFC 1918 private ranges, link-local/cloud-metadata addresses, etc.) — this is a deliberate SSRF protection, not a bug, and there is no way to override it.

Auth: JWT

ingestDocumentFromUrl(input: IngestDocumentFromUrlInput!): Int!

IngestDocumentFromUrlInput fields

FieldTypeRequiredNotes
knowledgeBaseIdInt!yes
urlString!yesMust be http:// or https://.
fileNameStringnoDisplay label. Defaults to the fetched page’s own <title>, or its hostname if no title is found.

Request

mutation IngestDocumentFromUrl($input: IngestDocumentFromUrlInput!) {
ingestDocumentFromUrl(input: $input)
}
{
"input": {
"knowledgeBaseId": 5,
"url": "https://example.com/help/password-reset"
}
}

Response

{ "data": { "ingestDocumentFromUrl": 3 } }