REST API Reference
Base URL: http://localhost:8100
OpenAPI schema: GET /openapi.json
Interactive UI: GET /docs
All business endpoints are versioned under /v1/. Unversioned paths that match known prefixes (/nodes, /edges, /search, etc.) receive a 301 redirect to the /v1/ equivalent.
Authentication
All business endpoints require Authorization: Bearer <token>.
Login
http
POST /auth/token
Content-Type: application/json
{ "email": "admin@example.com", "password": "changeme" }json
{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"token_type": "bearer",
"expires_in": 3600
}Refresh token
http
POST /auth/refresh
Content-Type: application/json
{ "refresh_token": "eyJ..." }Logout
http
POST /auth/logout
Authorization: Bearer <token>Current user
http
GET /auth/me
Authorization: Bearer <token>Change password
http
POST /auth/change-password
Authorization: Bearer <token>
Content-Type: application/json
{ "current_password": "old", "new_password": "new" }Nodes
Create node
http
POST /v1/nodes
Content-Type: application/json
{
"node_id": "person_001",
"labels": ["Person"],
"properties": { "name": "Alice", "age": 30 },
"embedding": [0.1, 0.2, ...],
"tenant_id": "acme"
}Response 201
json
{
"node_id": "person_001",
"labels": ["Person"],
"properties": { "name": "Alice", "age": 30 },
"created_at": "2026-03-05T10:00:00Z"
}Get node
http
GET /v1/nodes/{node_id}?tenant_id=acmeUpdate node
http
PATCH /v1/nodes/{node_id}
Content-Type: application/json
{
"properties": { "age": 31 },
"labels": ["Person", "Employee"]
}Delete node
http
DELETE /v1/nodes/{node_id}?tenant_id=acmeList nodes
http
GET /v1/nodes?label=Person&limit=100&offset=0&tenant_id=acmeResponse 200
json
{ "nodes": [...], "total": 42 }Bulk create nodes
http
POST /v1/nodes/bulk
Content-Type: application/json
{
"nodes": [
{ "node_id": "p1", "labels": ["Person"], "properties": { "name": "Bob" } },
{ "node_id": "p2", "labels": ["Person"], "properties": { "name": "Carol" } }
],
"tenant_id": "acme"
}Response 200
json
{ "created": 2, "failed": 0 }Edges
Create edge
http
POST /v1/edges
Content-Type: application/json
{
"edge_id": "knows_001",
"edge_type": "KNOWS",
"source_id": "person_001",
"target_id": "person_002",
"properties": { "since": "2024-01-01" }
}Get edge
http
GET /v1/edges/{edge_id}Update edge
http
PATCH /v1/edges/{edge_id}
Content-Type: application/json
{ "properties": { "weight": 0.9 } }Delete edge
http
DELETE /v1/edges/{edge_id}Batch existence check
http
POST /v1/edges/exists
Content-Type: application/json
{
"pairs": [
{ "source_id": "p1", "edge_type": "KNOWS", "target_id": "p2" }
]
}Search
Vector similarity search
http
POST /v1/search/vector
Content-Type: application/json
{
"query_embedding": [0.1, 0.2, ...],
"top_k": 10,
"label_filter": "Person",
"tenant_id": "acme"
}Response 200
json
{
"results": [
{ "node": { "node_id": "p1", ... }, "score": 0.98, "distance": 0.02 }
]
}Hybrid search (vector + graph)
http
POST /v1/search/hybrid
Content-Type: application/json
{
"query_embedding": [0.1, 0.2, ...],
"edge_type": "KNOWS",
"depth": 2,
"top_k": 10
}Graph traversal
http
POST /v1/traverse
Content-Type: application/json
{
"start_node_id": "person_001",
"edge_type": "KNOWS",
"depth": 3,
"direction": "outbound"
}Response 200
json
{
"nodes": [...],
"edges": [...],
"paths": [{ "nodes": [...], "edges": [...] }]
}Cypher query
http
POST /v1/query
Content-Type: application/json
{
"query": "MATCH (n:Person)-[:KNOWS]->(m) WHERE n.node_id = $id RETURN m",
"parameters": { "id": "person_001" }
}Response 200
json
{ "results": [...], "columns": ["m"], "stats": { "rows": 3 } }Journeys
Start a journey
http
POST /v1/journeys
Content-Type: application/json
{
"journey_type": "onboarding",
"entity_id": "user_42",
"metadata": { "referrer": "signup-page" }
}Get journey instance
http
GET /v1/journeys/{instance_id}Advance journey stage
http
POST /v1/journeys/{instance_id}/advance
Content-Type: application/json
{ "to_stage": "email_verified", "actor": "system", "notes": "OTP confirmed" }Cancel journey
http
POST /v1/journeys/{instance_id}/cancel
Content-Type: application/json
{ "actor": "admin", "reason": "user request" }Journey audit trail
http
GET /v1/journeys/{instance_id}/auditList journeys
http
GET /v1/journeys?journey_type=onboarding&status=active&limit=50&offset=0RAG Studio
Added in v0.27.2. Requires an LLM provider API key to be configured.
Get RAG configuration
http
GET /v1/rag/config
Authorization: Bearer <token>Response 200
json
{
"provider": "openai",
"model": "gpt-5.4-mini",
"embedding_provider": "openai",
"embedding_model": "text-embedding-3-small",
"chunk_strategy": "recursive",
"chunk_size": 512,
"chunk_overlap_percent": 10,
"reranker": null,
"top_k": 10,
"temperature": 0.1
}Update RAG configuration
http
PUT /v1/rag/config
Authorization: Bearer <token>
Content-Type: application/json
{
"provider": "anthropic",
"model": "claude-sonnet-4-20250514",
"chunk_strategy": "semantic",
"chunk_size": 1024,
"chunk_overlap_percent": 15
}Only the fields you send are updated — omitted fields keep their current values.
RAG query
http
POST /v1/rag/query
Authorization: Bearer <token>
Content-Type: application/json
{
"query": "What are the fire safety requirements for Building 7?",
"top_k": 5,
"filters": { "label": "Document", "category": "safety" }
}Response 200
json
{
"answer": "Building 7 requires ...",
"sources": [
{ "node_id": "doc_42", "chunk": "...", "score": 0.95 }
],
"model": "claude-sonnet-4-20250514",
"tokens_used": { "prompt": 1842, "completion": 312 }
}Evaluate RAG pipeline
http
POST /v1/rag/evaluate
Authorization: Bearer <token>
Content-Type: application/json
{
"test_queries": [
{ "query": "What is the load capacity?", "expected_answer": "500 kN per column" }
]
}Response 200
json
{
"results": [
{
"query": "What is the load capacity?",
"answer": "The load capacity is 500 kN per column ...",
"scores": { "relevance": 0.94, "faithfulness": 0.91, "answer_similarity": 0.88 }
}
],
"aggregate": { "avg_relevance": 0.94, "avg_faithfulness": 0.91 }
}List available models
http
GET /v1/rag/models
Authorization: Bearer <token>Response 200
json
{
"language_models": [
{ "provider": "openai", "model": "gpt-5.4-mini", "context_window": 400000, "max_output": 128000 },
{ "provider": "anthropic", "model": "claude-sonnet-4-20250514", "context_window": 200000, "max_output": 64000 }
],
"embedding_models": [
{ "provider": "openai", "model": "text-embedding-3-small", "dimensions": 1536 },
{ "provider": "cohere", "model": "embed-v4.0", "dimensions": 1536 }
],
"reranker_models": [
{ "provider": "cohere", "model": "rerank-v4-pro" }
],
"chunking_strategies": ["fixed", "recursive", "semantic", "sentence_window", "parent_child", "markdown_header"]
}KMS admin
Requires
ADMINpermission.
| Method | Path | Description |
|---|---|---|
GET | /admin/kms/status | Active provider, sealed/initialized, key count |
GET | /admin/kms/keys | List all key IDs |
GET | /admin/kms/keys/{key_id} | Describe a key |
POST | /admin/kms/keys/{key_id}/rotate | Trigger key rotation |
POST | /admin/kms/keys/{key_id}/disable | Disable key |
POST | /admin/kms/keys/{key_id}/enable | Re-enable key |
Health & metrics
http
GET /health/live # liveness probe (always 200 if process is up)
GET /health/ready # readiness probe (200 after DB init completes)
GET /metrics # Prometheus text/plain metricsError responses
All errors return:
json
{ "detail": "Human-readable error message" }| Status | Meaning |
|---|---|
400 | Bad request / validation error |
401 | Missing or invalid token |
403 | Insufficient permissions |
404 | Resource not found |
409 | Conflict (e.g. duplicate node ID) |
429 | Rate limit exceeded — Retry-After header included |
500 | Internal server error |