Skip to content

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=acme

Update 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=acme

List nodes

http
GET /v1/nodes?label=Person&limit=100&offset=0&tenant_id=acme

Response 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" }
  ]
}

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}/audit

List journeys

http
GET /v1/journeys?journey_type=onboarding&status=active&limit=50&offset=0

RAG 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 ADMIN permission.

MethodPathDescription
GET/admin/kms/statusActive provider, sealed/initialized, key count
GET/admin/kms/keysList all key IDs
GET/admin/kms/keys/{key_id}Describe a key
POST/admin/kms/keys/{key_id}/rotateTrigger key rotation
POST/admin/kms/keys/{key_id}/disableDisable key
POST/admin/kms/keys/{key_id}/enableRe-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 metrics

Error responses

All errors return:

json
{ "detail": "Human-readable error message" }
StatusMeaning
400Bad request / validation error
401Missing or invalid token
403Insufficient permissions
404Resource not found
409Conflict (e.g. duplicate node ID)
429Rate limit exceeded — Retry-After header included
500Internal server error

Purple8 Graph is proprietary software. All rights reserved.