Overview
The Context Engine is hosted by promptev. The SDK reaches it with a project API key: ingestion with uploadDocument and retrieval with searchKnowledgeBase. Both return promises.
| Method | What it does | Returns |
|---|
| uploadDocument(files, options?) | Put one file, or up to 20, into the key’s project. | Promise<{ results: UploadResult[] }> |
| searchKnowledgeBase(action, fields?, options?) | Read the key’s project knowledge base. | Promise<Record<string, any>> |
Install
Package @promptev/client, version 1.3.0. It runs anywhere fetch, FormData and Blob exist.
npm install @promptev/client
PromptevClient
The key belongs to one project, so no call names a project. Create it under the project’s API keys in the promptev console. It is sent as Authorization: Bearer, never in a URL.
import { PromptevClient } from "@promptev/client";
const client = new PromptevClient({
apiKey: process.env.PROMPTEV_API_KEY!, // fk_...
timeout: 120_000, // ms; uploads wait until the files are ready
});
| Option | Type | Default | Meaning |
|---|
| apiKey | string | required | The project API key (fk_...), from the project’s API keys in the console. Keep it on your server. |
| baseUrl | string | https://api.promptev.ai | Where the API is. |
| headers | Record<string, string> | none | Extra HTTP headers on every request. |
| timeout | number | 30000 | Milliseconds to wait for the response. Raise it for large uploads. |
| maxRetries | number | 2 | Retries on a 502, 503 or 504. |
uploadDocument(files, options?)
Put files into the key’s project knowledge base, in the same Uploads source the console writes to. The project’s agents can use them on their next turn.
import { readFile } from "node:fs/promises";
// Node: name the bytes
const { results } = await client.uploadDocument(
{ name: "handbook.pdf", content: await readFile("handbook.pdf") },
);
// Several files, and build the Context Graph too
const batch = await client.uploadDocument(
[
{ name: "refunds.pdf", content: await readFile("refunds.pdf") },
{ name: "notes.md", content: "# Release notes\n..." },
],
{ mode: "graph" },
);
for (const r of batch.results) console.log(r.filename, r.status, r.error ?? "");
// Browser or edge: a File or Blob from an <input type="file"> works too
await client.uploadDocument(fileInput.files![0]);
- A file is a File or Blob, or { name, content, type? } where content is a Blob, ArrayBuffer, typed array (a Node Buffer) or string. The name’s extension decides how the file is read.
- Limits: up to 20 files per call, 25 MB each (MAX_FILES_PER_REQUEST, MAX_UPLOAD_BYTES). Larger sets belong in a connector source.
- options.mode: "hybrid" or "graph". Graph also extracts entities and relationships into the Context Graph. Leave it out for the workspace’s default. options.signal aborts the upload.
- One result per file, in order. A file that fails is reported in its own row and never fails the batch.
- Same bytes, same document: uploading an identical file again updates it instead of adding a copy, so a retry is safe.
UploadResult
| Field | Type | Meaning |
|---|
| filename | string | The file’s name. |
| status | string | What became of it. "failed" when it did not land. |
| document_id | string | null | The document’s id, or null when it did not land. |
| chunks | number | How many passages it was split into. |
| units | number | The ingestion units counted for this file. |
| error | string | null | Why this file failed, or null. |
searchKnowledgeBase(action, fields?, options?)
Read the key’s project knowledge base. It is the same tool, with the same actions, as search_knowledge_base on the project’s MCP server. Field names are the API’s own (top_k, document_id). Fields that are undefined or null are not sent. options.signal aborts the call. Start with discover.
import { KNOWLEDGE_ACTIONS } from "@promptev/client";
console.log(KNOWLEDGE_ACTIONS);
const overview = await client.searchKnowledgeBase("discover");
const found = await client.searchKnowledgeBase("search", { query: "how many days do customers have to ask for a refund", top_k: 5 });
for (const hit of found.hits) console.log(hit.document_name, hit.score, hit.chunk_text);
const doc = await client.searchKnowledgeBase("get_doc", { document_id: found.hits[0].document_id });
const total = await client.searchKnowledgeBase("compute", { query: "total revenue by region for Q3" });
const near = await client.searchKnowledgeBase("get_neighbors", { entity: "Acme GmbH" });
KNOWLEDGE_ACTIONS and the KnowledgeAction type list the actions for reference. The server decides, so an action added there works without a new SDK.
Actions
The graph actions (get_neighbors, traverse, find_related, community_summary) need documents uploaded in graph mode. discover says which actions the project can run.
| Action | What you get | Fields |
|---|
| discover | Each document and what is inside it (a sheet’s columns, a document’s sections), the extracted fields, and which actions the project can run. Call it first. | source_ids, document_ids, limit, cursor |
| search | Passages by meaning or keywords, as hits. For an ID, code or invoice number, search the bare identifier. | query, top_k, mode, source_ids, document_ids |
| get_doc | One whole document by id. | document_id |
| get_docs | Several whole documents at once. | document_ids, max_chars |
| get_chunks | One long document in order, up to 25 passages at a time. | document_id, start, end |
| list | Browse the documents without searching. | source_ids, document_ids, limit, cursor |
| query_meta | The structured fields a question names (dates, amounts, parties) and each document’s value. Compare the values yourself. | query, source_ids, limit |
| compute | A figure from the spreadsheets (total, average, count, ranking, margin) or the exact row for one value, over every row. Ask in plain words. | query, source_ids, document_ids |
| map_reduce | The same question asked of every document in scope, one answer per document. | query, source_ids, document_ids |
| get_neighbors | What is one step from a named thing in the Context Graph. | entity, limit |
| traverse | Everything within a few steps of a named thing. For how two things connect, traverse from one. | entity, depth, category, limit |
| find_related | Connections of one kind across the knowledge base, with no starting thing. | category, label, entity_type, limit |
| community_summary | The main themes and groups the knowledge base falls into. | query, limit |
Fields
| Field | Type | Meaning |
|---|
| query | string | What you are looking for, in words, or a bare identifier you are looking up. |
| top_k | number | How many passages a search returns. Default 10. |
| mode | "hybrid" | "graph" | How a search ranks. Leave it out and the right one is chosen. "graph" also uses how things connect. |
| document_id | string | One document’s id, from discover, list or a search hit. Not a number written inside a document. |
| document_ids | string[] | Narrow to these documents. |
| source_ids | string[] | Narrow to these sources (an upload folder, a connector). |
| limit | number | How many rows: documents for discover and list (default 50), candidates for query_meta (20), links for the graph actions (50), groups for community_summary (3). |
| cursor | string | The next_cursor a previous page returned. |
| entity | string | Where a graph action starts: an entity id from an earlier graph answer (best), or a name as written in the documents. |
| depth | number | How many steps traverse walks, 1 to 5. Default 2. |
| category | string | The kind of connection to follow or find. discover lists the ones in your knowledge base. |
| label | string | The exact relationship wording, for example reports_to. Narrower than category. |
| entity_type | string | Keep only connections with this type of entity on one end. |
| start | number | First passage to read, from 0. A search hit’s chunk_idx reads around that passage. |
| end | number | Last passage to read, inclusive. |
| max_chars | number | How much text get_docs returns before it stops and hands back the ids it did not reach. |
What comes back
An object, the knowledge base’s answer as sent. A search returns hits, best first:
{
"hits": [
{
"document_id": "…",
"document_name": "refund-policy.pdf",
"chunk_text": "Refunds are accepted within 30 days …",
"score": 0.82,
"source_id": "…",
"chunk_idx": 4
}
]
}
discover and list return documents (each with id, name, source_id and kind), count and has_more. When there is more, send next_cursor back as cursor:
let page = await client.searchKnowledgeBase("list", { limit: 50 });
for (;;) {
for (const d of page.documents) console.log(d.name);
if (!page.has_more) break;
page = await client.searchKnowledgeBase("list", { limit: 50, cursor: page.next_cursor });
}
Follow the answer. An answer may carry a next_action or next_page: the next call, already filled in. Cite document_name when you show a passage.
Errors
Every error extends PromptevError with message, statusCode, responseText and, when known, field. A 502, 503 or 504 is retried (maxRetries) before ServerError is thrown.
| Error | Status | When |
|---|
| ValidationError | 422 (or 400) | An unknown action or a field it does not take; no files, more than 20, a file over 25 MB, a type that is not accepted, or a mode other than "hybrid" or "graph". Some are caught before the request is sent. e.field names the field. |
| AuthenticationError | 401 (or 403) | The key is missing, malformed, revoked or expired. |
| InsufficientCreditsError | 402 | The workspace has no credits left. A top-up fixes it, a retry does not. |
| NotFoundError | 404 | The key reaches no project. |
| RateLimitError | 429 | Too many requests. e.retryAfter says how many seconds to wait. |
| ServerError | 5xx | The server failed, after the retries. |
| NetworkError | none | No answer: DNS, connection, abort or the client timeout. |
import {
ValidationError, AuthenticationError, InsufficientCreditsError,
NotFoundError, RateLimitError,
} from "@promptev/client";
try {
await client.searchKnowledgeBase("search", { query: "how many days do customers have to ask for a refund" });
} catch (e) {
if (e instanceof ValidationError) console.error("Fix the request:", e.message, e.field);
else if (e instanceof InsufficientCreditsError) console.error("Out of credits: top up");
else if (e instanceof RateLimitError) console.error("Retry in", e.retryAfter, "s");
else throw e;
}
Credits and costs are on the guide.