Skip to main content
This guide takes you end to end: create a knowledge base, ingest your documents through the parse-chunk-embed pipeline, and query the result with vector, hybrid, or RAG-context search — from the REST API and both SDKs. It documents every request field, response shape, error, and credit cost so you can wire retrieval into a workflow or an agent with no surprises. For the conceptual model behind retrieval (knowledge bases, managed vs BYOK, ingest and retrieval), read Knowledge & RAG. To manage knowledge bases in the app instead of by API, see the Knowledge overview.
A knowledge base has its own embedding and chunking configuration — the unit that retrieval searches over. A document is one uploaded file; ingest splits it into chunks, each carrying a vector embedding. The vocabulary on this page matches the glossary.

Before you start

Every endpoint on this page lives under the /knowledge-bases router and is org-scoped and admin-gated. You need:
  • A ModuleX API key (mx_live_…). Get one from the app or see Authentication.
  • The owner or admin role in the organization. Every knowledge route depends on organization_admin_required, so a non-admin caller is rejected with 403. The legacy member role is retired — see Roles & permissions.
  • The base URL https://api.modulex.dev. There is no /v1 path segment — paths are exactly /knowledge-bases/....
Authenticate every request with two headers (the backend also accepts X-API-KEY as an alternative to the bearer token):
The header is X-Organization-ID with a capital ID. It is required on every knowledge endpoint except the single unauthenticated info endpoint GET /knowledge-bases/info/supported-file-types. A missing or wrong org context resolves to a 403 (or 404 for cross-org resources). All request bodies and responses are snake_case JSON.

Step 1 — choose managed or BYOK

The single decision that drives cost is whether the knowledge base is managed or BYOK (bring your own key). It is set by embedding_config.integration_name:

Managed (modulexdb)

Set integration_name to modulexai. ModuleX provisions the embedding provider and stores vectors in its managed store. Ingest and retrieval are billed in credits. See modulexdb (managed).

BYOK

Point embedding_config at one of your own LLM-provider credentials (for example your own OpenAI or Cohere key). Ingest and retrieval are uncosted by ModuleX — you pay the provider directly. See Knowledge providers.
Only managed knowledge bases (those whose embedding_config.integration_name is modulexai) are metered. BYOK retrieval and ingest consume no ModuleX credits — they are analytics-only. The credit columns throughout this page apply to managed knowledge bases only.
If you omit embedding_config.credential_id entirely, ModuleX auto-discovers an org credential that exposes an embedding-capable model (it looks for an llm_provider credential whose integration has a model flagged is_embedding). If none exists, create fails — see the errors below.
Embedding-config key drift. Two key conventions coexist in the backend and both are read defensively. The model-level default uses provider + model_id; the service default uses integration_name + provider_id + model_id + credential_id. When you write embedding_config, the embed code accepts either provider or provider_id, either model or model_id, and either provider_credential_id or credential_id. Pick one convention and stay consistent. Managed is detected specifically on integration_name == "modulexai".

Step 2 — create the knowledge base

POST /knowledge-bases creates the base and returns 201 Created. There is no credit gate on create. On success ModuleX also auto-creates a linked internal credential (the “native KB = credential” link) so a knowledge node can reference the base, and sets status to active.

Request fields

string
required
Display name, 1–255 characters.
string
Optional free-text description.
object
Per-knowledge-base embedding settings. Omit it to auto-discover an embedding credential and use the defaults below.
object
Per-knowledge-base chunking settings used by the ingest pipeline.

Create a knowledge base

The JS SDK takes camelCase method arguments but returns snake_case fields (so the response id/created_at stay snake_case); the Python SDK is snake_case both ways. Nested config keys like integration_name and model_id are already snake_case on both sides.

Response (201 Created)

string
The knowledge-base UUID.
string
Owning organization.
string
Creator user id.
string
The auto-created internal credential that links this base to workflow nodes.
string
The display name.
string
The description, or null.
object
The resolved embedding settings.
object
The resolved chunking settings.
string
One of active, processing, error, archived. New bases are active.
integer
Document count (rolled up).
integer
Chunk count across documents.
integer
Token count across documents.
string
Creation timestamp.
string
Last-update timestamp.
Example response

Create errors

The knowledge-base count quota is a plan entitlement (max_knowledge_bases): unlimited for admin and Enterprise, Max 50, Pro 3, Free uses the config default. Quota counts non-archived bases — archiving a base frees a slot. Quota and storage denials use the plain 403 {"detail": ...} shape, not the billing DenialEnvelope.

Step 3 — ingest documents

Upload a file with POST /knowledge-bases/{knowledge_base_id}/documents as multipart/form-data. The upload returns 201 with the document in pending status; a background worker then runs the parse-chunk-embed pipeline asynchronously. You poll the document status until it reaches completed or failed.

Form parts

file
required
The document to ingest. Supported types: pdf, docx, doc, txt, md, html, csv, json, xlsx, pptx. The type is resolved from the extension first, then the MIME type.
string
Optional JSON string of arbitrary metadata. Invalid JSON returns 400 with {"detail": "Invalid metadata JSON"}.
Per-file size cap is a plan entitlement, not a fixed 50 MB. The static info endpoint advertises 50 MB, but the actually enforced per-file cap is your plan’s storage entitlement: Free 10 MB, Pro 50 MB, Max 100 MB, Enterprise unlimited. Plan-storage and per-base document-count breaches return 403 {"detail": ...} (not the billing envelope). The hard 50 MB constant is only a fallback when the plan does not meter file size. A base also accepts at most 500 documents.
Deduplication. ModuleX computes a content hash of the file. Uploading the same content to the same base again returns 400 (Duplicate file...). To re-ingest changed content, delete the existing document first.

Upload a document

Upload response (201 Created)

The upload endpoint returns the router DocumentResponse shape:
string
Document UUID.
string
Parent base.
string
Original filename.
string
Resolved type (for example pdf).
integer
File size in bytes.
string
Lifecycle status; pending immediately after upload.
integer
Chunks produced; 0 until ingest completes.
integer
Tokens counted; 0 until ingest completes.
string
Failure reason, or null.
string
Upload timestamp.
Example response

What the worker does

The background ingest_document task (retried up to 3 times) drives the document from pending to a terminal state:
1

Parse

Decodes text formats; uses pypdf for PDF, python-docx for Word, and the unstructured library for HTML and unknown types.
2

Chunk

Splits text per chunking_config, recording token_count, start_char, and end_char for each chunk.
3

Embed

Generates embeddings with OpenAI or Cohere. A managed base routes through the same underlying model used at query time, so ingest-time and search-time vectors match.
4

Persist

Writes chunk rows with their vectors, sets the document completed, and updates chunk_count and token_count. On any exception the document is set failed with an error_message and the task retries.
The document status lifecycle is pending → processing → completed | failed, and a failed document can be returned to pending with retry.

Poll document status

GET /knowledge-bases/{kb_id}/documents/{document_id}/status returns a status dict whose extra fields vary by state. Poll it until status is completed or failed.
Example response (completed)
  • pendingmessage: "Waiting to be processed".
  • processing — adds processing_started_at.
  • completed — adds processing_completed_at, chunk_count, token_count.
  • failed — adds error with the failure reason.

Retry a failed document

POST /knowledge-bases/{kb_id}/documents/{document_id}/retry re-enqueues ingest. It is valid only when the document status is failed (otherwise 400); it resets the document to pending and clears the error and timestamps. There is no credit gate on retry — the per-chunk embedding cost is keyed on {doc_id}:embedding:{token_count}, so an unchanged reprocess does not re-charge.

Other document operations

Ingest errors

Step 4 — query the knowledge base

ModuleX offers four retrieval modes. All reserve 1 retrieval credit on a managed base (see Costs) and accept the same auth headers. Pick the mode that fits:

Vector search

POST /knowledge-bases/{kb_id}/search — semantic (cosine) similarity over one base.

Multi-base search

POST /knowledge-bases/search — the same query across several bases, merged by score.

Hybrid search

POST /knowledge-bases/{kb_id}/hybrid-search — vector plus keyword full-text ranking.

Retrieve context

POST /knowledge-bases/{kb_id}/retrieve-context — a single token-budgeted RAG context string.
string
required
The search text (minimum length 1).
integer
default:"5"
Number of results, 1–50.
number
default:"0.0"
Cosine-similarity floor, 0.0–1.0. Computed as 1 - distance.
object
Optional filter; supports document_id or document_ids to scope to specific documents.
boolean
default:"true"
Return chunk text in each match.
boolean
default:"true"
Return chunk metadata in each match.
string
The query you sent.
string
The base searched.
integer
The requested result count.
integer
Number of matches returned.
array
Ranked matches, highest score first.
Example response
POST /knowledge-bases/search runs the query across several bases and merges results by score. It takes knowledge_base_ids (array of UUIDs, required), query (required), top_k (1–50, default 5), and min_score (0.0–1.0, default 0.0). Bases your org cannot access are silently skipped. The response is { "query", "knowledge_bases_searched", "top_k", "total_matches", "matches" }.
Billing reserves one retrieval credit if any queried base is managed (regardless of ordering). One known in-code limitation: the per-base query-embedding token cost is recorded for the last searched base only.
POST /knowledge-bases/{kb_id}/hybrid-search blends semantic similarity with keyword full-text ranking.
string
required
The search text.
integer
default:"5"
Number of results, 1–50.
number
default:"0.3"
Weight of the keyword score, 0.0–1.0.
number
default:"0.7"
Weight of the semantic score, 0.0–1.0.
number
default:"0.0"
Combined weighted-score floor, 0.0–1.0.
object
Optional document filter, as in vector search.
Each match adds semantic_score and keyword_score alongside the combined score, and the response echoes search_type: "hybrid" and the weights you used.
min_score is not the same scale across modes. In vector search it is a cosine- similarity floor; in hybrid search it is a floor on the combined weighted score. Tune it per mode.

Retrieve context (for RAG)

POST /knowledge-bases/{kb_id}/retrieve-context returns a single ready-to-prompt string instead of a match array — ideal for feeding an LLM.
string
required
The search text.
integer
default:"2000"
Token budget for the assembled context, 100–10000.
integer
default:"10"
Chunks to consider, 1–50.
number
default:"0.3"
Cosine-similarity floor, 0.0–1.0.
It runs the same vector search, then concatenates chunks within the token budget, each prefixed with a [Source: <filename>, Chunk <n>] header and joined by a separator. The response is { "context": "<string>", "query": "<query>" }.

Query from a workflow or agent

Inside a workflow, a knowledge node retrieves from a base by its linked credential and returns chunks, context, or both (set by the node’s output_format). The Assistant retrieves through its own search_knowledge tool. Both reuse the same managed-billing path as the REST endpoints, so the credit costs below apply identically.

Search errors

Step 5 — costs

Cost depends entirely on the managed-vs-BYOK choice from Step 1. For the full credit model, see Credits & metering; for how the gate denies calls, see Usage gating & limits. A credit is the managed-usage unit: 100 credits = $1.00 (1 credit = $0.01). The gate works reserve → record → release: it reserves the credit before doing the work and records it on success, so an exhausted balance is rejected before any document is written or any embedding is generated.
On a managed base, ingest and retrieval flow through the live billing gate. When the balance is exhausted (or rate-limited, or the plan quota is hit), the call is denied with the flat DenialEnvelope{code, layer, key, current, limit, reason} — at 402 (credit/wallet), 403 (quota), or 429 (rate). This is not the {"detail": ...} shape used by CRUD and access errors. A 429 also carries Retry-After and X-RateLimit-* headers. See Errors & status codes for all three error-envelope shapes.
Knowledge-base count quota vs storage cap are plan entitlements, enforced separately from credits and returned as 403 {"detail": ...} — not as the billing envelope. Counts and caps are described under Step 2 and Step 3.

Manage and clean up

Supported file types — no auth. GET /knowledge-bases/info/supported-file-types is the one knowledge endpoint with no authentication. It returns the 10 supported types and the fallback max_file_size_bytes (52428800) / max_file_size_mb (50.0). Remember that the enforced per-file cap is your plan entitlement, not this advertised 50 MB.

Next steps

Knowledge & RAG

The conceptual model: knowledge bases, managed vs BYOK, ingest and retrieval.

Knowledge overview

Manage knowledge bases, documents, and processing in the app.

Knowledge node

Retrieve from a base inside a workflow.

Credits & metering

Exactly what consumes credits, and the reserve-record-release lifecycle.