Skip to content

Content

The Content API reads L0/L1/L2 content, writes text, and maintains semantic and vector indexes for stored content.

API Reference

abstract()

Read L0 abstract (~100 tokens summary).

Parameters

ParameterTypeRequiredDefaultDescription
uristrYes-Viking URI (must be a directory)

Python SDK

python
abstract = client.abstract("viking://resources/docs/")
print(f"Abstract: {abstract}")
# Output: "Documentation for the project API, covering authentication, endpoints..."

TypeScript SDK

typescript
const abstract = await client.abstract("viking://resources/docs/");
console.log(abstract);

Go SDK

go
abstract, err := client.Abstract(ctx, "viking://resources/docs/")
if err != nil {
    return err
}
fmt.Println(abstract)

HTTP API

GET /api/v1/content/abstract?uri={uri}
bash
curl -X GET "http://localhost:1933/api/v1/content/abstract?uri=viking://resources/docs/" \
  -H "X-API-Key: your-key"

CLI

bash
openviking abstract viking://resources/docs/

Response

json
{
  "status": "ok",
  "result": "Documentation for the project API, covering authentication, endpoints...",
  "time": 0.1
}

overview()

Read L1 overview, applies to directories.

Parameters

ParameterTypeRequiredDefaultDescription
uristrYes-Viking URI (must be a directory)

Python SDK

python
overview = client.overview("viking://resources/docs/")
print(f"Overview:\n{overview}")

TypeScript SDK

typescript
const overview = await client.overview("viking://resources/docs/");
console.log(overview);

Go SDK

go
overview, err := client.Overview(ctx, "viking://resources/docs/")
if err != nil {
    return err
}
fmt.Println(overview)

HTTP API

GET /api/v1/content/overview?uri={uri}
bash
curl -X GET "http://localhost:1933/api/v1/content/overview?uri=viking://resources/docs/" \
  -H "X-API-Key: your-key"

CLI

bash
openviking overview viking://resources/docs/

Response

json
{
  "status": "ok",
  "result": "## docs/\n\nContains API documentation and guides...",
  "time": 0.1
}

read()

Read L2 full content.

Parameters

ParameterTypeRequiredDefaultDescription
uristrYes-Viking URI
offsetintNo0Starting line number (0-indexed)
limitintNo-1Number of lines to read, -1 means read to end
rawboolNofalseReturn raw stored content without memory-field cleanup. HTTP API only (Python SDK does not expose it yet).

Notes

  • read() accepts file URIs only. Passing an existing directory URI returns INVALID_ARGUMENT (400), not NOT_FOUND. This error carries a structured details payload — details.expected is "file", details.actual is "directory", and details.resource is the offending URI (present on the HTTP path) — so clients can detect a file-vs-directory mismatch programmatically (for example, fall back to list) instead of string-matching the message.
  • Public URI parameters accept resources and user scopes. For session files, use viking://user/{user_id}/sessions/{session_id} or the backward-compatible viking://session/{session_id} alias. Internal scopes such as temp and queue return INVALID_URI.

Python SDK

python
content = client.read("viking://resources/docs/api.md")
print(f"Content:\n{content}")

TypeScript SDK

typescript
const content = await client.read("viking://resources/docs/api.md", 0, -1);
console.log(content);

Go SDK

go
content, err := client.Read(ctx, "viking://resources/docs/api.md", 0, -1)
if err != nil {
    return err
}
fmt.Println(content)

HTTP API

GET /api/v1/content/read?uri={uri}
bash
curl -X GET "http://localhost:1933/api/v1/content/read?uri=viking://resources/docs/api.md" \
  -H "X-API-Key: your-key"

CLI

bash
openviking read viking://resources/docs/api.md

Response

json
{
  "status": "ok",
  "result": "# API Documentation\n\nFull content of the file...",
  "time": 0.1
}

write()

Update an existing file, or create a new one when mode="create", and automatically refresh related semantics and vectors.

Parameters

ParameterTypeRequiredDefaultDescription
uristrYes-File URI to write. For mode="create", the file must not already exist
contentstrYes-New content to write
modestrNoreplacereplace, append, or create
waitboolNofalseWait for background semantic/vector refresh
timeoutfloatNonullTimeout in seconds when wait=true

Notes

  • replace and append require the file to exist; create targets a new file and returns 409 Conflict when the path already exists. Directories are always rejected.
  • create only accepts text-writable extensions: .md, .txt, .json, .yaml, .yml, .toml, .py, .js, .ts. Parent directories are created automatically.
  • Derived semantic files cannot be written directly: .abstract.md, .overview.md, .relations.json.
  • File content is updated before the API returns. wait only controls whether the call waits for semantic/vector refresh to finish.
  • The public API no longer accepts regenerate_semantics or revectorize; write always refreshes related semantics and vectors.

Python SDK

python
result = client.write(
    "viking://resources/docs/api.md",
    "# Updated API\n\nFresh content.",
    mode="replace",
    wait=True,
)
print(result["root_uri"])

TypeScript SDK

typescript
await client.write("viking://resources/docs/new.md", "# New document\n", { wait: true });

Go SDK

go
result, err := client.Write(
    ctx,
    "viking://resources/docs/api.md",
    "# Updated API\n\nFresh content.",
    &openviking.WriteOptions{
        Mode: "replace",
        Wait: true,
    },
)
if err != nil {
    return err
}
fmt.Println(result["root_uri"])

HTTP API

POST /api/v1/content/write
bash
curl -X POST "http://localhost:1933/api/v1/content/write" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{
    "uri": "viking://resources/docs/api.md",
    "content": "# Updated API\n\nFresh content.",
    "mode": "replace",
    "wait": true
  }'

CLI

bash
openviking write viking://resources/docs/api.md \
  --content "# Updated API\n\nFresh content." \
  --wait

Response

json
{
  "status": "ok",
  "result": {
    "uri": "viking://resources/docs/api.md",
    "root_uri": "viking://resources/docs",
    "context_type": "resource",
    "mode": "replace",
    "written_bytes": 29,
    "content_updated": true,
    "semantic_status": "complete",
    "vector_status": "complete",
    "queue_status": {
      "Semantic": {
        "processed": 1,
        "error_count": 0,
        "errors": []
      },
      "Embedding": {
        "processed": 2,
        "error_count": 0,
        "errors": []
      }
    }
  }
}

batch_write()

Apply a preconditioned set of file writes below one Resource or Memory directory, then refresh the affected semantic and vector indexes as one request.

Parameters

ParameterTypeRequiredDefaultDescription
root_uristringYes-Existing Resource or Memory directory containing every target
operationsarrayYes-File writes to validate and apply
waitbooleanNotrueWait for semantic/vector refresh
timeoutnumberNonullRefresh timeout in seconds when wait=true
telemetryboolean/objectNofalseInclude operation telemetry

Each operation contains:

FieldTypeRequiredDescription
uristringYesTarget file URI below root_uri
contentstringConditionalUTF-8 text; exactly one of content and content_base64 is required
content_base64stringConditionalBase64-encoded bytes; not supported for Memory targets
precondition.kindstringYescreate_if_absent or replace_if_hash
precondition.base_hashstringConditionalRequired for replace_if_hash, formatted as sha256:<lowercase-hex>

Notes

  • A request supports at most 128 operations, 8 MiB per file, and 16 MiB total.
  • All targets must be files below root_uri, use the same context type, and have unique canonical URIs.
  • Resource targets may use any safe file extension; Memory targets retain the text extension allowlist and do not accept binary content.
  • Every non-idempotent precondition is checked under the target tree lock before the first new write. A mismatch returns 409 Conflict.
  • If a target already contains the requested bytes, it is reported as unchanged; retrying the same request can therefore repeat a failed refresh without rewriting matching content.
  • The API prevents precondition conflicts from causing new writes, but an underlying I/O failure can still leave writes completed earlier in the batch visible.

Python SDK

python
result = client.batch_write(
    root_uri="viking://resources/wiki",
    operations=[
        {
            "uri": "viking://resources/wiki/new.md",
            "content": "# New page\n",
            "precondition": {"kind": "create_if_absent"},
        },
        {
            "uri": "viking://resources/wiki/existing.md",
            "content": "# Updated page\n",
            "precondition": {
                "kind": "replace_if_hash",
                "base_hash": "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
            },
        },
    ],
    wait=True,
)

HTTP API

POST /api/v1/content/batch-write
bash
curl -X POST http://localhost:1933/api/v1/content/batch-write \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{
    "root_uri": "viking://resources/wiki",
    "operations": [
      {
        "uri": "viking://resources/wiki/new.md",
        "content": "# New page\n",
        "precondition": {"kind": "create_if_absent"}
      }
    ],
    "wait": true
  }'

Response

json
{
  "status": "ok",
  "result": {
    "root_uri": "viking://resources/wiki",
    "created": ["viking://resources/wiki/new.md"],
    "updated": [],
    "unchanged": [],
    "queue_status": {
      "Semantic": {
        "processed": 1,
        "error_count": 0,
        "errors": []
      }
    }
  }
}

The TypeScript and Go SDKs and the CLI do not currently expose batch write directly.


download()

Download a file as raw bytes. This is intended for images, PDFs, and other non-text content. The response uses application/octet-stream and returns the filename through Content-Disposition.

ParameterTypeRequiredDescription
uristringYesFile URI to download

HTTP API

http
GET /api/v1/content/download?uri={uri}
bash
curl --get http://localhost:1933/api/v1/content/download \
  -H "X-API-Key: your-key" \
  --data-urlencode "uri=viking://resources/images/logo.png" \
  --output logo.png

Response

On success, the endpoint returns HTTP 200 with the raw file bytes instead of the standard JSON envelope:

http
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Disposition: attachment; filename*=UTF-8''logo.png

<binary body>

The public SDKs and CLI do not currently expose a dedicated raw-byte download method, so this section shows only the HTTP tab.


set_tags()

Set explicit k=v tags used by retrieval filters. replace replaces existing tags, while append adds tags. When the target is a directory, recursive=true applies the update to files below it.

Python SDK

python
result = client.set_tags(
    "viking://resources/project/",
    ["team=search", "env=prod"],
    mode="replace",
    recursive=True,
)

TypeScript SDK

typescript
const result = await client.setTags(
  "viking://resources/project/",
  ["team=search", "env=prod"],
  { mode: "replace", recursive: true },
);

Go SDK

go
result, err := client.SetTags(
    ctx,
    "viking://resources/project/",
    []string{"team=search", "env=prod"},
    &openviking.SetTagsOptions{Mode: "replace", Recursive: true},
)

HTTP API

http
POST /api/v1/content/set_tags
Content-Type: application/json
bash
curl -X POST http://localhost:1933/api/v1/content/set_tags \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{
    "uri":"viking://resources/project/",
    "tags":["team=search","env=prod"],
    "mode":"replace",
    "recursive":true
  }'

POST /api/v1/fs/attrs/set_tags is an equivalent compatibility path currently used by the Python, TypeScript, and Go SDKs and the CLI.

CLI

bash
ov set-tags viking://resources/project/ \
  --tags team=search,env=prod \
  --mode replace \
  --recursive

Response

json
{
  "status": "ok",
  "result": {
    "uri": "viking://resources/project/",
    "updated_uris": [
      "viking://resources/project/guide.md"
    ],
    "root_uri": "viking://resources/project/",
    "context_type": "resource",
    "tags": [
      "team=search",
      "env=prod"
    ],
    "mode": "replace",
    "success_count": 1,
    "skipped_count": 0,
    "failed_count": 0,
    "tags_updated": true
  }
}

updated_uris contains the semantic record URIs actually updated. For recursive directory updates, success_count, skipped_count, and failed_count summarize all targets.


reindex()

Reindex semantic and/or vector artifacts for existing content already stored in OpenViking. This is an operational maintenance API intended for scenarios such as embedding model changes, VLM changes, vector store rebuild, or post-upgrade repair of existing indexes.

This API operates on existing viking://... content. It does not import new files. For normal ingestion, use Resources.

Authentication

  • HTTP endpoint: requires admin/root role when authentication is enabled. In api_key mode, use an admin key for tenant content; a raw root key cannot access tenant-scoped data.
  • Python embedded mode: uses the current service context
  • Python HTTP client / CLI: sends the current authenticated identity

Parameters

ParameterTypeRequiredDefaultDescription
uristrYes-Viking URI to reindex
modestrNovectors_onlyReindex mode: vectors_only, semantic_and_vectors, or prune_orphans
waitboolNotrueWhether to wait for completion
dry_runboolNofalseOnly valid with mode="prune_orphans"; report orphan vector records without deleting them

The HTTP request body rejects unknown fields. uri may use OpenViking path variables accepted by other content APIs; it is resolved before validation.

Supported URI scopes

  • viking://
  • viking://user
  • viking://user/<user_id>
  • viking://resources
  • viking://resources/...
  • viking://user/<user_id>/memories/...
  • viking://user/<user_id>/skills
  • viking://user/<user_id>/skills/<skill_name>

Session namespaces are not supported by reindex(). Requests for viking://session/... or viking://user/<user_id>/sessions/... are rejected; when reindexing a broader user namespace, session subtrees are skipped.

Modes

  • vectors_only: rebuilds vector-store records from currently recoverable source data without rewriting .abstract.md or .overview.md
  • semantic_and_vectors: regenerates semantic artifacts first, then rebuilds vectors from the refreshed semantic outputs
  • prune_orphans: deletes vector-store records under the requested URI whose source files no longer exist in the filesystem. With dry_run=true, it only reports how many records would be deleted.

For resource and skill, semantic_and_vectors refreshes directory/file semantic artifacts, including .abstract.md and .overview.md. For memory, it rebuilds the current persisted memory subtree semantics and vectors, but it does not replay historical extraction order.

For semantic_and_vectors, semantic generation and vector rebuilding are sequenced by the reindex executor. The semantic refresh step does not enqueue its own background vectorization work; vectors are rebuilt by the reindex step so wait=true reflects the reindex operation itself.

For prune_orphans, source existence is checked against the filesystem. If an entire directory is missing, vector records for files and semantic sidecars below that directory, such as .abstract.md and .overview.md, are pruned together. dry_run is rejected for other modes.

Python SDK

python
result = client.reindex(
    uri="viking://resources",
    mode="vectors_only",
    wait=True,
)
print(result)
python
result = client.reindex(
    uri="viking://user/default/skills",
    mode="semantic_and_vectors",
    wait=False,
)
print(result["status"])
python
result = client.reindex(
    uri="viking://resources",
    mode="prune_orphans",
    dry_run=True,
)
print(result["would_delete_records"])

TypeScript SDK

typescript
console.log(await client.reindex("viking://resources/docs/"));

Go SDK

go
result, err := client.Reindex(ctx, "viking://resources", &openviking.ReindexOptions{
    Mode: "vectors_only",
    Wait: true,
})
if err != nil {
    return err
}
fmt.Println(result["status"])
go
result, err := client.Reindex(ctx, "viking://resources", &openviking.ReindexOptions{
    Mode: "prune_orphans",
    Wait: true,
    DryRun: true,
})
if err != nil {
    return err
}
fmt.Println(result["would_delete_records"])

HTTP API

POST /api/v1/content/reindex

There is no /api/v1/maintenance/reindex endpoint. Use /api/v1/content/reindex.

bash
curl -X POST http://localhost:1933/api/v1/content/reindex \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -H "X-OpenViking-Account: default" \
  -d '{
    "uri": "viking://resources",
    "mode": "prune_orphans",
    "wait": true,
    "dry_run": true
  }'

CLI

bash
openviking reindex viking://resources --mode vectors_only
bash
openviking reindex viking://user/default/skills --mode semantic_and_vectors --wait false
bash
openviking reindex viking://resources --mode prune_orphans --dry-run

Synchronous response (wait=true)

json
{
  "status": "ok",
  "result": {
    "uri": "viking://resources",
    "mode": "vectors_only",
    "status": "completed",
    "object_type": "resource",
    "scanned_records": 120,
    "rebuilt_records": 118,
    "deleted_records": 0,
    "would_delete_records": 0,
    "unsupported_records": 2,
    "failed_records": 0,
    "duration_ms": 1284,
    "warnings": []
  },
  "time": 0.1
}

Asynchronous response (wait=false)

json
{
  "status": "ok",
  "result": {
    "uri": "viking://resources",
    "mode": "vectors_only",
    "object_type": "resource",
    "status": "accepted",
    "task_id": "task_xxx"
  },
  "time": 0.1
}

Poll the returned task through the task API:

bash
curl -X GET http://localhost:1933/api/v1/tasks/task_xxx \
  -H "X-API-Key: your-key" \
  -H "X-OpenViking-Account: default"

Reindex background tasks use task_type="admin_reindex" and resource_id equal to the requested uri, so they can also be listed with:

text
GET /api/v1/tasks?task_type=admin_reindex&resource_id=viking://resources

Task records are persisted under /local/{account_id}/_system/tasks/{user_id}/{task_id}.json and can be queried after restart.

Result fields

FieldDescription
statuscompleted for synchronous completion, accepted for background execution
uriRequested URI after path-variable resolution
object_typeInferred target type, such as resource, skill, memory, user_namespace, skill_namespace, or global_namespace
modeEffective reindex mode
scanned_recordsNumber of records or semantic sources considered
rebuilt_recordsNumber of vector records successfully rebuilt
deleted_recordsNumber of vector records deleted by prune_orphans; 0 for dry_run=true
would_delete_recordsNumber of vector records that would be deleted by prune_orphans in dry-run mode
unsupported_recordsNumber of records skipped because no usable vector source was available
failed_recordsNumber of records that failed while rebuilding
duration_msSynchronous run duration in milliseconds
warningsRecoverable per-record warnings
task_idBackground task ID, present only when wait=false

Behavior notes

  • vectors_only and semantic_and_vectors are non-destructive. They use rebuild/upsert behavior and do not require dropping the vector collection first.
  • prune_orphans is destructive unless dry_run=true: it removes vector records whose source files no longer exist.
  • viking:// reindex fans out to supported top-level namespaces and excludes session.
  • Namespace reindex operations such as viking://user propagate to supported child content types.
  • vectors_only is the right mode when only the embedding model or vector index needs to be refreshed.
  • semantic_and_vectors is the right mode when semantic artifacts themselves must be regenerated before re-vectorization.
  • prune_orphans is the right mode when the filesystem has been changed outside normal APIs and the vector store may still contain records for deleted paths.
  • Only one reindex task can run for the same URI and owner at a time. A concurrent request for the same target returns a conflict.
  • For resource files, text files can use file content when no summary is available. Non-text files require a generated summary or existing vector record fallback; otherwise they are counted as unsupported.

Current limitations

  • Reindex uses the best currently recoverable source inputs. It is not guaranteed to replay the exact historical embedding input byte-for-byte in every case.
  • Memory semantic reindex is based on the currently persisted memory tree. It does not reconstruct the original chronological memory-extraction pipeline.

Released under the Apache-2.0 License.