API Reference
Garak provides a REST API for all database operations. All requests require authentication via access tokens.
This documentation is provided primarily for the purpose of allowing developers to create their own clients. You really should use a client instead of directly calling the REST api.
Authentication
All API requests must include an Authorization header:
Authorization: Bearer {node_id}/{token}
The token format is {node_id}/{token} where:
node_id: The node ID of the access token recordtoken: The actual token string
Permission Levels
| Grant | Access |
|---|---|
read | Read all documents in all silos |
write | Read/write all documents in all silos |
read::{silo} | Read all documents in shards with specified silo prefix |
write::{silo} | Read/write all documents in shards with specified silo prefix |
Common Request Format
All POST requests require a JSON body with at minimum:
{
"silo": "your-silo-id",
"shard": "your-shard-id",
"store": "your-store-name"
}
Status Endpoint
GET /api/status
Health check endpoint.
Response:
200 OK
Master Endpoints
Using a master token, initilized via the MASTER_TOKEN envvar, new tokens can be created.
The header when using master token follows format: Authorization: master_token_here.
POST /tokens/create
Create a new access token (requires master token).
Authorization: Master token only
Request:
{
"grant": "read" | "write" | "read::silo" | "write::silo"
}
Response:
{
"token": "{node_id}/{token_string}"
}
Read Operations
POST /get
Retrieve a single node by ID.
Request:
{
"silo": "string",
"shard": "string",
"store": "string",
"node_id": number
}
Response:
{
// Your document data
}
Errors:
404- Node not found403- Insufficient permissions
POST /mget
Retrieve multiple nodes by IDs.
Request:
{
"silo": "string",
"shard": "string",
"store": "string",
"node_ids": [number, number, ...]
}
Response:
{
"nodes": [
{
"node_id": number,
"value": {...} | null
},
...
]
}
POST /secondary_key_lookup
Look up a node by secondary key index.
Request:
{
"silo": "string",
"shard": "string",
"store": "string",
"trigger_field": "fieldName",
"key": "value"
}
Response:
{
"node_id": number | null
}
POST /categorization_lookup
Retrieve all nodes in a category.
Request:
{
"silo": "string",
"shard": "string",
"store": "string",
"trigger_field": "fieldName",
"key": "categoryValue"
}
Response:
{
"nodes": [number, number, ...]
}
POST /slice_lookup
Query a slice index for nodes in a range.
Request:
{
"silo": "string",
"shard": "string",
"store": "string",
"slice_name": "string",
"group": "string",
"point": { "Number": number } | { "Absolute": "Top" | "Bottom" },
"select": { "Limit": number } | { "Until": number }
}
Select Options:
{ "Limit": n }- Return up to n entries- Positive n: entries after/at point
- Negative n: entries before/at point
{ "Until": n }- Return all entries until reaching key value n
Response:
{
"nodes": [
number, // If fetching just IDs
// OR
{ // If fetching with keys
"key": number,
"value": number // node_id
}
]
}
POST /slice_correction
Internal endpoint for slice index maintenance.
Request:
{
"silo": "string",
"shard": "string",
"store": "string",
"slice_name": "string",
"group": "string",
"key": number,
"node_id": number,
"delete": boolean
}
Write Operations
All write operations require write permissions.
POST /insert
Insert a new node.
Request:
{
"silo": "string",
"shard": "string",
"store": "string",
"data": {
// Your document data
}
}
Response:
{
"node_id": number
}
Errors:
400- Data validation error or schema violation403- Insufficient permissions (read-only token)
POST /set
Set the value of a field in a node.
Request:
{
"silo": "string",
"shard": "string",
"store": "string",
"node_id": number,
"field": "fieldName.nested.path", // Supports dot notation
"value": any
}
Response:
{
// Updated document
}
Errors:
404- Node not found400- Invalid field or value
POST /inc
Increment a numerical field.
Request:
{
"silo": "string",
"shard": "string",
"store": "string",
"node_id": number,
"field": "fieldName",
"value": number // Can be negative for decrement
}
Response:
{
// Updated document
}
Errors:
404- Node not found400- Field is not a number (ETARGETFIELDNOTINT)
POST /add_to_set
Add a value to an array field (if not already present).
Request:
{
"silo": "string",
"shard": "string",
"store": "string",
"node_id": number,
"field": "fieldName",
"value": any
}
Behavior:
- If field is nullish, initializes as empty array
- Only adds value if not already present
- Creates field if it doesn't exist
Response:
{
// Updated document
}
Errors:
404- Node not found400- Field is not an array (ETARGETFIELDNOTARR)
POST /pull
Remove a value from an array field.
Request:
{
"silo": "string",
"shard": "string",
"store": "string",
"node_id": number,
"field": "fieldName",
"value": any
}
Response:
{
// Updated document
}
Errors:
404- Node not found400- Field is not an array
POST /delete
Delete a node.
Request:
{
"silo": "string",
"shard": "string",
"store": "string",
"node_id": number
}
Response:
{
// Success confirmation
}
Note: Deletion removes node from all indexes. Index removal failures are ignored.
Index Management
POST /create_index
Create an index (idempotent).
Request:
{
"index": {
"silo": "string",
"store": "string",
"trigger_field": "fieldName",
"kind": "SecondaryKey" | "Categorization" | "Slice",
// For Slice indexes only:
"group_on": "fieldName" | "$",
"name": "uniqueSliceName",
// Optional for Categorization and Slice:
"includes": {
"field": { "$eq": value }
// JSON condition selector
}
}
}
Index Kinds:
SecondaryKey:
{
"kind": "SecondaryKey",
"trigger_field": "email"
}
Categorization:
{
"kind": "Categorization",
"trigger_field": "status",
"includes": {
"type": { "$eq": "active" }
}
}
Slice:
{
"kind": "Slice",
"trigger_field": "timestamp",
"group_on": "chatId",
"name": "messagesByChat",
"includes": {
"deleted": { "$eq": false }
}
}
Response:
{
// Success confirmation
}
POST /reindex
Rebuild an index by scanning all nodes.
Request:
{
"index": {
"silo": "string",
"store": "string",
"trigger_field": "fieldName",
"kind": "SecondaryKey" | "Categorization" | "Slice",
"group_on": "fieldName", // For Slice only
"name": "sliceName" // For Slice only
},
"shard": "string"
}
Warning: This operation scans all nodes in the store and can be slow for large datasets.
Replication Endpoints
These endpoints are restricted to master token access only.
POST /replicas/cold_sync
Initiate a cold sync (full database snapshot).
Authorization: Master token only
Response:
{
"job_id": "uuid",
"status": "accepted",
"message": "Cold sync job started. Poll /replicas/cold_sync_status/{job_id} for status."
}
Status: 202 Accepted
GET /replicas/cold_sync_status/{job_id}
Check the status of a cold sync job.
Authorization: Master token only
Response:
{
"job_id": "string",
"status": "InProgress" | "Completed" | { "Failed": "error message" },
"created_at": number,
"completed_at": number | null,
"tar_path": "string" | null,
"tar_size": number | null,
"snapshot_time": number | null
}
GET /replicas/cold_sync_download/{job_id}
Download the completed snapshot tar file.
Authorization: Master token only
Response:
200- Tar file stream with headers:Content-Type: application/x-tarContent-Length: {size}x-garak-locktime: {timestamp}
202- Job still in progress404- Job not found500- Job failed or file not accessible
GET /replicas/hot_sync
Retrieve oplog entries for incremental replication.
Authorization: Master token only
Headers:
x-garak-latest: {last_known_timestamp}
Response:
[
{
"Write": {
"silo": "string",
"shard": "string",
"store": "string",
"node_id": number,
"data": {...},
"is_new_node": boolean
}
},
{
"Delete": {
"silo": "string",
"shard": "string",
"store": "string",
"node_id": number
}
}
]
Response Headers:
x-garak-latest: {current_timestamp}
Error Response Format
All errors return JSON with the following structure:
{
"field": "fieldName", // Field that caused error (if applicable)
"code": "ERROR_CODE",
"message": "Human readable error message",
"kind": "KIND_CONSTANT"
}
Error Kinds
| Kind | Description |
|---|---|
KIND_OPERATION_INCOMPATIBLE_WITH_FIELD | Operation cannot be performed on field type |
KIND_NODE_NOT_FOUND | Node ID does not exist |
KIND_SCHEMA_VIOLATION | Data violates index constraints |
KIND_MALFORMED_INDEX | Index definition or data is corrupted |
KIND_OVERSIZE_NODE | Node data exceeds 25KB limit |
Common Error Codes
| Code | HTTP Status | Description |
|---|---|---|
ENOTFOUND | 404 | Node not found |
ETARGETFIELDNOTARR | 400 | Field is not an array |
ETARGETFIELDNOTINT | 400 | Field is not a number |
ERROR_NODE_TOO_LARGE | 400 | Node exceeds 25KB |
EDUPLICATE | 400 | Violates secondary key uniqueness |