VocalDocscontracts 1.65.0

Quick start: knowledge for an Agent

A Knowledge base holds the documents an Agent may cite. In this quick start you create a Knowledge base, upload a document to it, publish and activate a version, bind the Knowledge base to an Agent version, and ask a retrieval. It uses curl and an API key; no SDK is involved. It needs an Agent: use the one from Your first Agent.

On the Phase 0 host. Served. The eleven operations below were first released in 1.53.0 or earlier, and the api on the Phase 0 host serves every one of them. On 2026-10-24 UTC the runner passed the 28 steps of the four quick starts against the Phase 0 host, which has pinned contracts 1.60.0 since deploy #70, with a live key: 27 steps were answered and one was skipped, as its page allows. No run with a test key is recorded.

Before you start

You need an API key whose scopes include knowledge:read, knowledge:write, agents:read and agents:write. The Knowledge base needs an embedding Provider that the Organization's Compliance profile allows. Use a test key and a document of your own that holds no personal data.

export VOCAL_API=https://api.example.test
export VOCAL_API_KEY=vk_test_...

A short synthetic document is enough. Save it as opening-hours.md:

cat > opening-hours.md <<'TEXT'
# Opening hours

The front desk of the furniture shop is open from 9 in the morning to 5 in the afternoon, Monday to Friday.
It is closed on weekends.
TEXT

1. Choose an embedding model

GET /v1/providers lists the Provider descriptors. A Knowledge base names an embedding Provider and model from the descriptors of the stage embeddings.

curl "$VOCAL_API/v1/providers?limit=100" \
  -H "Authorization: Bearer $VOCAL_API_KEY"

Look for data, an array of descriptors with a provider, a stage, the models an Agent may name and enabled, as in Your first Agent. The answer also carries object and has_more. Choose a descriptor whose stage is embeddings, whose lifecycle is not reserved, and one of its models. The runner takes the mock descriptor when the run prefers mock Providers, and otherwise a real one, falling back to the mock one when the catalog holds no usable real one.

2. Create the Knowledge base

POST /v1/knowledge-bases creates the draft. Its name is kebab-case, 1 to 64 characters, and unique among the Knowledge bases of the Organization that are not archived.

curl -X POST "$VOCAL_API/v1/knowledge-bases" \
  -H "Authorization: Bearer $VOCAL_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "quick-start-handbook",
    "embedding": { "provider": "<embedding_provider>", "model": "<embedding_model>" }
  }'

The answer is 201. Look for object, which is "knowledge_base", id beginning kb_, revision, which is 1, and active_version and latest_version, both null until you publish. Keep the id.

Measured on 2026-10-24 UTC with a live key, the catalog of the Phase 0 host held two embeddings descriptors, a mock one and a reserved real one. The host answered 422 capability_not_available to the model of the reserved descriptor and 201 to the mock model, although the descriptor says a mock Provider runs for test keys only, so the run used the mock model.

3. Add an upload Source

A Source says where Documents come from. POST /v1/knowledge-bases/{knowledge_base_id}/sources adds one; an upload Source waits for files.

curl -X POST "$VOCAL_API/v1/knowledge-bases/kb_.../sources" \
  -H "Authorization: Bearer $VOCAL_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "upload", "name": "handbook" }'

The answer is 201. Look for object, which is "knowledge_source", id beginning ksrc_, kind, which is "upload", and knowledge_base_id. Keep the id.

4. Upload a Document

POST /v1/knowledge-bases/{knowledge_base_id}/documents uploads a file to the Source as multipart/form-data. The file is encrypted under the Organization's data key, the Document is created pending and an Ingestion run starts. The file part needs a file name and a media type among PDF, DOCX, HTML, Markdown and plain text.

curl -X POST "$VOCAL_API/v1/knowledge-bases/kb_.../documents" \
  -H "Authorization: Bearer $VOCAL_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "source_id=ksrc_..." \
  -F "title=Opening hours" \
  -F "file=@opening-hours.md;type=text/markdown"

A new Document answers 201, and a revision of one the Source holds answers 200. Look for object, which is "knowledge_document", id beginning kdoc_, source_id and status, which is pending. Keep the id.

5. Read the Document until it is indexed

GET /v1/knowledge-bases/{knowledge_base_id}/documents/{document_id} answers the Document. Read it until status is indexed; failed, quarantined and removed end the wait.

curl "$VOCAL_API/v1/knowledge-bases/kb_.../documents/kdoc_..." \
  -H "Authorization: Bearer $VOCAL_API_KEY"

Look for object, id, status, which becomes indexed, and chunk_count, which is at least 1 once the Document is searchable. Wait a second between reads. In the run of 2026-10-24 UTC the Document was indexed on the third read.

6. Read the Knowledge base

The next write names the draft's revision in If-Match, so read it first. GET /v1/knowledge-bases/{knowledge_base_id} answers the draft.

curl "$VOCAL_API/v1/knowledge-bases/kb_..." \
  -H "Authorization: Bearer $VOCAL_API_KEY"

Look for object, id and revision. Keep the revision.

7. Publish and activate a version

POST /v1/knowledge-bases/{knowledge_base_id}/versions freezes the configuration and the latest indexed revision of every Document as the next version. "activate": true makes it the active version. The publish is refused with 422 validation_failed while a Document is still pending, and when no Document is indexed.

curl -X POST "$VOCAL_API/v1/knowledge-bases/kb_.../versions" \
  -H "Authorization: Bearer $VOCAL_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{ "activate": true, "notes": "The first version, from the quick start." }'

The answer is 201. Look for object, which is "knowledge_base_version", knowledge_base_id, version, which is 1, active, which is true, and document_count. Keep the version.

8. Read the Agent for its revision

Binding a Knowledge base changes the Agent draft, and a change names the draft's revision. GET /v1/agents/{agent_id} answers it.

curl "$VOCAL_API/v1/agents/agt_..." \
  -H "Authorization: Bearer $VOCAL_API_KEY"

Look for object, id and revision, and latest_version and active_version, which name the version you published in the first quick start. Keep the revision.

9. Bind the Knowledge base to the Agent draft

PATCH /v1/agents/{agent_id} replaces the whole draft configuration, validated as a create is, so the body repeats the configuration of the first quick start and adds knowledge. A binding names the Knowledge base and how the Agent uses it: context retrieves before each model request. "track": "pinned" freezes the version that is active when the Agent is published.

curl -X PATCH "$VOCAL_API/v1/agents/agt_..." \
  -H "Authorization: Bearer $VOCAL_API_KEY" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{
    "config": {
      "engine": "cascaded",
      "language": "en",
      "instructions": "You are a friendly assistant for a furniture shop. Answer briefly and ask one question at a time.",
      "transcriber": { "provider": "<stt_provider>", "model": "<stt_model>" },
      "model": { "provider": "<llm_provider>", "model": "<llm_model>" },
      "voice": { "provider": "<tts_provider>", "model": "<tts_model>", "voice_id": "<voice_id>" },
      "knowledge": [
        {
          "name": "handbook",
          "knowledge_base_id": "kb_...",
          "use": "context",
          "track": "pinned"
        }
      ]
    }
  }'

The answer is 200 and the revision advances by one. Look for object, id, revision and, in config, knowledge, which holds the binding. A Knowledge base with no active version is refused at publish with 422 validation_failed and the issue knowledge_base_no_active_version. Keep the new revision.

10. Publish an Agent version that reads the Knowledge base

Publishing freezes the binding into a new Agent version, as in the first quick start.

curl -X POST "$VOCAL_API/v1/agents/agt_.../versions" \
  -H "Authorization: Bearer $VOCAL_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'If-Match: "2"' \
  -H "Content-Type: application/json" \
  -d '{ "activate": true, "notes": "Reads the handbook, from the quick start." }'

The answer is 201. Look for object, agent_id, version, active, status and config_sha256, and knowledge_bindings, which holds the binding the version froze. Calls on this version read the Knowledge base.

11. Ask a retrieval

POST /v1/knowledge-bases/{knowledge_base_id}/query runs the retrieval a Call uses over the active version. Without include, the passages carry identifiers, ranks, scores and locators but no text, and nothing is audited.

curl -X POST "$VOCAL_API/v1/knowledge-bases/kb_.../query" \
  -H "Authorization: Bearer $VOCAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "When is the front desk open?", "top_k": 3 }'

Look for object, which is "knowledge_query_result", knowledge_base_id, version, which is the version searched, passages, reranked, elapsed_ms and top_score. An empty passages is an answer, not a failure; a question the Document answers should return at least one passage. Add include=text to the query string to read the passage text, which makes the query an audited content read.

Operations and releases

Every operation here is served by the Phase 0 host. The release is the one that first served the operation.

Run it

The repository that builds these pages holds a runner that sends these requests in order, with the Agent of the first quick start, checks each answer's status and the fields named above, and prints one line per step. See HTTP quick starts for the command and for the record of its run.