Knowledge Object API
Every Knowledge Object is backed by a REST API for managing its instances — the rows of its materialized table. This page covers the object endpoints (create, list, read) and the full instance CRUD API.
- Base path:
/knowledge-object
- Auth: standard logged-in session, same as every other app endpoint.
- Permissions: resource type
knowledge_object under the project hierarchy — read endpoints require read, instance writes require write.
Branching
Every endpoint takes an optional branch query parameter, defaulting to main. Objects are stored in your repository, so each branch has its own definition and its own ClickHouse table:
- The object id in the URL is stable across branches — only the
branch param changes when you switch.
- Object reads return
definition: null when the object is not deployed on the requested branch.
- Instance calls return
404 when the object is not deployed on the requested branch.
- Instance operations require the branch’s definition to be
READY — otherwise they return 400. Gate instance UIs on definition.status === "READY".
Object endpoints
Create object
Creates an object by writing its YAML file to the repository and deploying it — the object and its definition are then materialized by the loader, exactly as if you had committed the file yourself.
POST /knowledge-object/create
Content-Type: application/json
{
"metadata": {
"name": "Company",
"description": "A company.",
"fields": [
{ "name": "id", "type": "int", "primary_key": true },
{ "name": "name", "type": "str" },
{ "name": "owner", "type": "str", "optional": true, "relationship": [{ "object": "Person" }] }
],
"settings": { "label_column": "name", "icon": "building" }
},
"project": "6748d8b66a5a2ac9f0d39326",
"branch": "main"
}
| Field | Type | Description |
|---|
metadata | object | The object definition (YAML reference, as JSON). Fully validated. |
project | string | Id of the project to create the object in. |
branch | string | (Optional) Branch to deploy on. Defaults to main. |
metadata is validated exactly like a deployed YAML file (primary key required, relationship rules, label_column must name a field, …) — an invalid definition returns 400.
- The server writes
objects/<name>_<hash>.yaml, registers it in the project’s config.yaml, and deploys on branch. The definition starts at PENDING_MIGRATION and is materialized asynchronously.
- Response:
200 with no body. Poll Get object or List objects for the definition and its status.
400 if an object with the same name already exists in the project.
Update object
PATCH /knowledge-object/{id}
Content-Type: application/json
{ "metadata": { /* name, description, fields, settings */ }, "branch": "main" }
Updates the object’s metadata on one branch by rewriting its YAML file and redeploying.
- Send the full
metadata (same shape as create). branch is optional (defaults to main).
- Renaming is not supported —
metadata.name must equal the current name (400 otherwise).
- Changing the primary key (columns or types) is rejected (
400) — the instance key derives from it.
- A schema change re-runs migration (definition returns to
PENDING_MIGRATION → READY); metadata/settings-only changes apply in place.
- Response:
200, no body. Poll Get object for the updated definition and status.
Delete object
DELETE /knowledge-object/{id}?branch=main
Removes the object’s definition on the given branch only (other branches keep theirs; branch defaults to main).
- The object is removed from its YAML file (the file and its
config.yaml entry are dropped when it was the only object in the file) and redeployed. The branch definition is deleted, and the object identity is deleted once no branch has a definition.
- A background task then drops that branch’s ClickHouse table and view.
- Response:
204, no body.
List objects
GET /knowledge-object/list?branch=main
Standard list parameters — filters, page, page_size, sort_by — plus branch (default main). Filters apply to the object identity (e.g. name, project.$id); the definition for the selected branch is attached to each item.
# Objects of one project, with their feature-x definitions
GET /knowledge-object/list?filters=[project.$id][$eq]:6748d8b66a5a2ac9f0d39326&branch=feature-x
{ "total_count": 3, "items": [ { "id": "…", "name": "Employee", "branches": ["main"], "definition": { } } ] }
Only objects deployed on the selected branch are returned.
Get object
GET /knowledge-object/get-by-id/{id}?branch=main
Returns the object identity plus the definition for the requested branch. definition is null when the object is not deployed on that branch.
{
"id": "6863f1a2c9d4b2a7e1f00001",
"name": "Employee",
"project": { "id": "6748d8b66a5a2ac9f0d39326", "collection": "project" },
"branches": ["feature-x", "main"],
"definition": {
"id": "6863f1a2c9d4b2a7e1f00002",
"branch": "main",
"file_path": "objects/employee.yaml",
"status": "READY",
"error_message": null,
"table_name": "object_main_employee__raw",
"view_name": "object_main_employee",
"metadata": {
"name": "Employee",
"description": "Employee object.",
"settings": { "object_type": "STANDALONE", "store_versions": false, "icon": "users", "label_column": "name" },
"fields": [
{ "name": "id", "type": "int", "primary_key": true, "optional": false, "mutable": true, "default": null, "relationship": null },
{ "name": "name", "type": "str", "primary_key": false, "optional": false, "mutable": true },
{ "name": "owner", "type": "str", "optional": true, "relationship": [{ "object": "Person" }] }
],
"actions": []
}
}
}
Use definition.metadata.fields to build instance forms and table columns, and definition.view_name to query the object in the SQL editor. The view exposes the object fields plus __version, __timestamp, and __primary_key (the instance key as hex).
Instance endpoints
All instance endpoints are nested under an object id: /knowledge-object/{id}/instances. {id} is the object’s id (not its name). Each accepts branch (default main) and operates on that branch’s table.
List instances
GET /knowledge-object/{id}/instances?page=1&page_size=50&branch=main
| Param | Type | Default | Description |
|---|
page | int | 1 | Page number. |
page_size | int | 50 | Rows per page. |
branch | string | main | Branch to read from. |
fields | repeated string | (all) | Column selection. |
filters | repeated JSON | (none) | Row filtering. |
{
"total_count": 128,
"items": [
{ "_key": "8A3F9C2E1B4D6F7A8A3F9C2E1B4D6F7A", "id": 1, "name": "john", "last_login": "2026-07-01T09:30:00Z", "_version": 1 }
]
}
Each item is the selected object fields plus the meta fields _key and _version.
Column selection
Use the repeated fields query parameter to fetch only the columns you need — for example a dropdown that shows the label column and stores the primary key:
- omitted → all object fields (default).
?fields=id&fields=name → only those fields, plus _key and _version.
?fields= (present but empty) → only the meta fields _key and _version.
- An unknown field name →
400 (detail.unknown_fields).
The meta fields _key and _version are always included.
Row filtering
Use the repeated filters query parameter to filter rows. Each value is a JSON object { "column", "operator", "value" }, and multiple filters are combined with AND. Filtering affects both the returned rows and total_count.
GET /knowledge-object/{id}/instances
?filters={"column":"is_active","operator":"equal","value":true}
&filters={"column":"salary","operator":"greater_than","value":50000}
operator | Meaning |
|---|
equal | column = value |
not_equal | column ≠ value |
greater_than | column > value |
less_than | column < value |
contains | case-sensitive substring match |
not_contains | negated substring match |
column must be an object field name → an unknown column returns 400 (detail.unknown_filter_column).
value is not type-checked against the field; it is sent to the database as a safely-escaped literal.
- A filter that isn’t valid JSON returns
400 (detail.invalid_filter).
- Combine freely with
fields, page, and page_size.
Create instance
POST /knowledge-object/{id}/instances
Content-Type: application/json
{ "id": 1, "name": "john", "last_login": null }
- The payload is validated against the object’s field definitions (types, required, nullable).
- Optional fields, and fields with a
default, may be omitted — the database fills in defaults (including functional ones like now()).
Response: 201 Created with the created instance (its _key is in the body).
{ "_key": "8A3F9C2E1B4D6F7A8A3F9C2E1B4D6F7A", "id": 1, "name": "john", "last_login": null }
409 if an instance with the same primary key values already exists.
- Creating with the primary key of a previously deleted instance succeeds — the instance is revived under the same
_key.
Batch upsert instances
Insert or update many instances in a single request — useful for bulk imports and syncing.
POST /knowledge-object/{id}/instances/batch?branch=main
Content-Type: application/json
[
{ "id": 1, "name": "john" },
{ "id": 2, "name": "jane", "last_login": "2026-07-01T09:30:00Z" },
{ "id": 3, "name": "amir" }
]
The request body is a JSON array of instance payloads (each shaped exactly like a single create payload). Semantics are upsert:
- If an instance with the same primary key does not exist, it is inserted as a new instance (version
1).
- If it already exists, a new version is appended with an incremented version number (the previous value is kept in history). A previously deleted instance is revived the same way.
- Optional fields and fields with a
default may be omitted per item — the database fills them in.
Constraints:
- Maximum 1000 items per request — more returns
400 (detail.max_batch_size, detail.received).
- The whole batch is validated before any write. If any item is invalid, nothing is written and the response is
400 with the offending item’s index (detail.index, detail.errors).
- A primary key that appears more than once within the same batch is rejected with
400 (detail.index, detail.error).
Response: 200 with a summary of what was applied.
{ "created": 2, "updated": 1, "total": 3 }
To retrieve the resulting instances (with their _keys), read them back via List.
Get instance
GET /knowledge-object/{id}/instances/{key}?branch=main&add_relationships=false
Returns the instance, or 404 if it does not exist or was deleted.
Relationships — pass add_relationships=true to resolve the object’s relationship fields. Each relationship field keeps its raw _key value, and a parallel _relationships object is added, keyed by field name, holding the resolved related instance (or null when the key is empty, the target object is not deployed/ready on the branch, or the related instance was deleted). Resolution is one level deep. Default is false (no _relationships key, no extra queries).
{
"_key": "AB12...",
"id": 5,
"name": "Acme",
"owner": "9F3C...",
"_relationships": {
"owner": { "_key": "9F3C...", "id": 1, "name": "John", "email": "j@acme.com" }
}
}
Instance history
GET /knowledge-object/{id}/instances/{key}/history?branch=main
Returns all versions of an instance, newest first — including deletion rows, and even for instances that are currently deleted (so it works where a plain GET returns 404).
[
{ "_key": "8A3F…", "id": 1, "name": "John Smith", "_version": 2, "_timestamp": "2026-07-06T10:00:00", "_deleted": false, "_user_id": "6748d8…" },
{ "_key": "8A3F…", "id": 1, "name": "john", "_version": 1, "_timestamp": "2026-07-05T09:30:00", "_deleted": false, "_user_id": "6748d8…" }
]
Each item is the object fields plus _version, _timestamp, _deleted, and _user_id (empty string when not written by a user). 404 if no instance ever existed for the key.
For objects with store_versions: false, superseded versions may be compacted away over time — history is best-effort in that mode.
Update instance
PATCH /knowledge-object/{id}/instances/{key}
Content-Type: application/json
{ "name": "John Smith" }
- Send only the fields to change (partial update).
- Primary key fields in the payload →
400 (they are immutable; a different primary key is a different instance).
- Immutable fields (
mutable: false) cannot be changed after creation.
- Returns the full updated instance.
Delete instance
DELETE /knowledge-object/{id}/instances/{key}
204 No Content. A subsequent GET on the key returns 404. Deletion is logical — history is kept internally and the key can be revived by creating an instance with the same primary key.
Errors
| Status | When | Detail |
|---|
400 | Definition not READY on the branch | Instance operations require a completed migration. |
400 | Payload fails validation | detail.errors carries the validation message (detail.index on batch). |
400 | Batch larger than 1000 items | detail.max_batch_size, detail.received. |
400 | Duplicate primary key within a batch | detail.index, detail.error. |
400 | Primary key field in a PATCH payload | detail.fields lists the offending fields. |
400 | Value outside a field’s options | detail.invalid_options maps field → rejected value. |
400 | Unknown fields value | detail.unknown_fields lists the unknown fields. |
400 | Unknown filter column | detail.unknown_filter_column. |
400 | Malformed filters JSON | detail.invalid_filter. |
400 | Object with the same name already exists | On create object. |
404 | Object id not found / other project | Standard not-found. |
404 | Object not deployed on the branch | detail.branch. |
404 | Instance missing or deleted | — |
409 | Create with existing primary key | detail.key is the existing _key. |
Not available yet
- Executing object actions via the API (
POST /{id}/instances/{key}/actions/{action}).
- Backed objects (
object_type: BACKED).