| last_validated | 2026-03-16 |
|---|
This document provides complete REST API documentation for the Agent Brain server.
The Agent Brain API is a RESTful JSON API built with FastAPI. It provides endpoints for:
- Health Monitoring: Server status, indexing progress, and provider health
- Document Querying: Semantic, keyword, hybrid, graph, and multi-mode search
- Document Indexing: Index documents and code from folders with job queue
- Folder Management: List and remove indexed folders
- Cache Management: View and clear embedding cache
- Job Queue: Monitor and cancel indexing jobs
Interactive documentation is available at:
- Swagger UI:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc - OpenAPI JSON:
http://localhost:8000/openapi.json
The Agent Brain API does not require authentication by default. It binds to 127.0.0.1 and is accessible only from localhost.
For network deployment, implement authentication via a reverse proxy (nginx, Traefik) or enable CORS restrictions.
http://127.0.0.1:8000
For per-project instances, read the port from .agent-brain/runtime.json:
{
"base_url": "http://127.0.0.1:49321",
"port": 49321
}Check server health status.
Response 200 OK:
{
"status": "healthy",
"message": "Server is running and ready for queries",
"timestamp": "2026-01-15T10:30:00Z",
"version": "9.0.0",
"mode": "project",
"instance_id": "abc123",
"project_id": "my-project",
"active_projects": null
}Status Values:
| Status | Description |
|---|---|
healthy |
Ready for queries |
indexing |
Indexing in progress |
degraded |
Running with issues (e.g., vector store not initialized) |
unhealthy |
Not operational |
Get detailed indexing status. This endpoint never blocks, even during active indexing.
Response 200 OK:
{
"total_documents": 150,
"total_chunks": 1200,
"total_doc_chunks": 800,
"total_code_chunks": 400,
"indexing_in_progress": false,
"current_job_id": null,
"progress_percent": 0.0,
"last_indexed_at": "2026-01-15T10:30:00Z",
"indexed_folders": ["/path/to/docs"],
"supported_languages": ["python", "typescript"],
"graph_index": {
"enabled": true,
"initialized": true,
"entity_count": 120,
"relationship_count": 250,
"store_type": "simple"
},
"queue_pending": 0,
"queue_running": 0,
"current_job_running_time_ms": null,
"file_watcher": {
"running": true,
"watched_folders": 2
},
"embedding_cache": {
"hits": 150,
"misses": 30,
"hit_rate": 0.833,
"mem_entries": 180,
"entry_count": 500,
"size_bytes": 2048000
},
"query_cache": {
"hits": 10,
"misses": 5,
"hit_rate": 0.667,
"cached_entries": 5,
"index_generation": 3
}
}Note: The embedding_cache field is omitted for fresh installs with an empty cache. The query_cache field is null when the cache service is not initialized.
Get detailed status of all configured providers with health checks.
Response 200 OK:
{
"config_source": "/path/to/.agent-brain/agent-brain.yml",
"strict_mode": false,
"validation_errors": [],
"providers": [
{
"provider_type": "embedding",
"provider_name": "openai",
"model": "text-embedding-3-large",
"status": "healthy",
"message": null,
"dimensions": 3072
},
{
"provider_type": "summarization",
"provider_name": "anthropic",
"model": "claude-haiku-4-5-20251001",
"status": "healthy",
"message": null,
"dimensions": null
}
],
"timestamp": "2026-01-15T10:30:00Z"
}A reranker provider entry is included when ENABLE_RERANKING=true.
Get PostgreSQL connection pool metrics and database info. Only available when storage backend is postgres.
Response 200 OK:
{
"status": "healthy",
"backend": "postgres",
"pool": {
"pool_size": 5,
"checked_in": 4,
"checked_out": 1,
"overflow": 0
},
"database": {
"version": "PostgreSQL 16.2 ...",
"host": "localhost",
"port": 5432,
"database": "agent_brain"
}
}Errors:
| Code | Description |
|---|---|
400 |
Storage backend is not postgres |
Execute a search query.
Request Body:
{
"query": "how does authentication work",
"top_k": 5,
"similarity_threshold": 0.3,
"mode": "hybrid",
"alpha": 0.5,
"source_types": ["doc", "code"],
"languages": ["python", "typescript"],
"file_paths": ["src/**/*.py"],
"entity_types": ["Class", "Function"],
"relationship_types": ["calls", "extends"]
}Parameters:
| Field | Type | Default | Description |
|---|---|---|---|
query |
string | (required) | Search query (1-1000 chars) |
top_k |
integer | 5 |
Results to return (1-50) |
similarity_threshold |
float | 0.3 |
Minimum similarity (0.0-1.0) |
mode |
string | "hybrid" |
Search mode |
alpha |
float | 0.5 |
Hybrid balance (0=BM25, 1=Vector) |
source_types |
array | null |
Filter by ["doc", "code", "test"] |
languages |
array | null |
Filter by programming languages |
file_paths |
array | null |
Filter by file patterns (supports wildcards) |
entity_types |
array | null |
Filter graph results by entity types (graph/multi modes only) |
relationship_types |
array | null |
Filter graph results by relationship types (graph/multi modes only) |
Mode Values:
| Mode | Description |
|---|---|
bm25 |
Keyword-only search |
vector |
Semantic-only search |
hybrid |
BM25 + Vector fusion |
graph |
Knowledge graph traversal |
multi |
All three with RRF |
Response 200 OK:
{
"results": [
{
"text": "Authentication is configured via...",
"source": "/path/to/docs/auth.md",
"score": 0.92,
"vector_score": 0.92,
"bm25_score": 0.85,
"chunk_id": "chunk_abc123",
"source_type": "doc",
"language": "markdown",
"graph_score": null,
"related_entities": null,
"relationship_path": null,
"rerank_score": null,
"original_rank": null,
"metadata": {
"chunk_index": 0,
"total_chunks": 5
}
}
],
"query_time_ms": 125.5,
"total_results": 1
}Response Fields:
| Field | Type | Description |
|---|---|---|
text |
string | Chunk content |
source |
string | Source file path |
score |
float | Combined/primary score |
vector_score |
float or null | Semantic similarity score |
bm25_score |
float or null | Keyword match score |
graph_score |
float or null | Graph traversal score |
chunk_id |
string | Unique chunk identifier |
source_type |
string | "doc", "code", or "test" |
language |
string or null | Programming language |
related_entities |
array or null | GraphRAG related entities |
relationship_path |
array or null | GraphRAG relationship paths |
rerank_score |
float or null | Score from reranking stage (if enabled) |
original_rank |
integer or null | Position before reranking (1-indexed) |
metadata |
object | Additional metadata |
Errors:
| Code | Description |
|---|---|
400 |
Invalid query (empty or too long) |
409 |
Embedding provider mismatch (re-index required) |
503 |
Index not ready (indexing in progress or not initialized) |
Get the number of indexed chunks.
Response 200 OK:
{
"total_chunks": 1200,
"ready": true
}Enqueue a job to index documents from a folder. Returns immediately with a job ID. The job is processed asynchronously by a background worker.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
force |
boolean | false |
Bypass deduplication and force a new job |
allow_external |
boolean | false |
Allow paths outside the project directory |
rebuild_graph |
boolean | false |
Rebuild only the graph index without re-indexing documents (requires ENABLE_GRAPH_INDEX=true) |
Request Body:
{
"folder_path": "/path/to/documents",
"chunk_size": 512,
"chunk_overlap": 50,
"recursive": true,
"include_code": true,
"supported_languages": ["python", "typescript"],
"code_chunk_strategy": "ast_aware",
"generate_summaries": false,
"force": false,
"include_patterns": ["docs/**/*.md", "src/**/*.py"],
"include_types": ["python", "docs"],
"exclude_patterns": ["node_modules/**", "__pycache__/**"],
"injector_script": null,
"folder_metadata_file": null,
"dry_run": false,
"watch_mode": null,
"watch_debounce_seconds": null
}Body Parameters:
| Field | Type | Default | Description |
|---|---|---|---|
folder_path |
string | (required) | Path to folder |
chunk_size |
integer | 512 |
Chunk size in tokens (128-2048) |
chunk_overlap |
integer | 50 |
Overlap in tokens (0-200) |
recursive |
boolean | true |
Scan subdirectories |
include_code |
boolean | false |
Include source code files |
supported_languages |
array | null |
Languages to index |
code_chunk_strategy |
string | "ast_aware" |
"ast_aware" or "text_based" |
generate_summaries |
boolean | false |
Generate LLM summaries for code chunks |
force |
boolean | false |
Force re-indexing even if embedding provider changed |
include_patterns |
array | null |
Glob patterns to include |
include_types |
array | null |
File type presets (e.g., ["python", "docs"]) |
exclude_patterns |
array | null |
Glob patterns to exclude |
injector_script |
string | null |
Path to Python script exporting process_chunk(chunk: dict) -> dict |
folder_metadata_file |
string | null |
Path to JSON file with static metadata to merge into all chunks |
dry_run |
boolean | false |
Validate injector against sample chunks without indexing |
watch_mode |
string | null |
Watch mode for auto-reindex: "auto" or "off" |
watch_debounce_seconds |
integer | null |
Per-folder debounce in seconds |
Response 202 Accepted:
{
"job_id": "job_abc123def456",
"status": "pending",
"message": "Job queued for /path/to/documents"
}Errors:
| Code | Description |
|---|---|
400 |
Invalid folder path, path outside project (without allow_external), invalid injector script, invalid include_types preset, or rebuild_graph=true with GraphRAG not enabled |
429 |
Queue is full (backpressure) |
Enqueue a job to add documents from another folder to the existing index. Same request body as POST /index (without generate_summaries, injector_script, folder_metadata_file, dry_run, watch_mode, watch_debounce_seconds).
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
force |
boolean | false |
Bypass deduplication and force a new job |
allow_external |
boolean | false |
Allow paths outside the project directory |
Response 202 Accepted:
{
"job_id": "job_abc123def456",
"status": "pending",
"message": "Job queued to add documents from /path/to/documents"
}Errors:
| Code | Description |
|---|---|
400 |
Invalid folder path, path outside project (without allow_external), or invalid include_types preset |
429 |
Queue is full (backpressure) |
Reset the index (delete all documents). Cannot be performed while jobs are running.
Response 200 OK:
{
"job_id": "reset",
"status": "completed",
"message": "Index has been reset successfully"
}Errors:
| Code | Description |
|---|---|
409 |
Cannot reset while indexing jobs are in progress |
List all folders that have been indexed with chunk counts and metadata.
Response 200 OK:
{
"folders": [
{
"folder_path": "/home/user/project/docs",
"chunk_count": 42,
"last_indexed": "2026-02-24T01:00:00+00:00",
"watch_mode": "off",
"watch_debounce_seconds": null
},
{
"folder_path": "/home/user/project/src",
"chunk_count": 128,
"last_indexed": "2026-02-24T00:30:00+00:00",
"watch_mode": "auto",
"watch_debounce_seconds": 10
}
],
"total": 2
}Remove a folder from the index, deleting all its chunks from the vector store.
Request Body:
{
"folder_path": "/home/user/project/docs"
}Body Parameters:
| Field | Type | Default | Description |
|---|---|---|---|
folder_path |
string | (required) | Path to the folder to remove from the index |
Response 200 OK:
{
"folder_path": "/home/user/project/docs",
"chunks_deleted": 42,
"message": "Successfully removed 42 chunks for /home/user/project/docs"
}Errors:
| Code | Description |
|---|---|
404 |
Folder not found in the index |
409 |
Active indexing job running for this folder |
500 |
Failed to delete chunks |
Get embedding cache hit/miss counters and disk statistics.
Response 200 OK:
{
"hits": 150,
"misses": 30,
"hit_rate": 0.833,
"mem_entries": 180,
"entry_count": 500,
"size_bytes": 2048000
}Errors:
| Code | Description |
|---|---|
503 |
Embedding cache service not initialized |
Clear all cached embeddings and reclaim disk space. Safe to call while indexing jobs are running (running jobs will regenerate embeddings at normal API cost).
Response 200 OK:
{
"count": 500,
"size_bytes": 2048000,
"size_mb": 1.953125
}Errors:
| Code | Description |
|---|---|
503 |
Embedding cache service not initialized |
Indexing operations are queued and processed asynchronously.
List all jobs in the queue.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 50 |
Maximum results (1-100) |
offset |
integer | 0 |
Skip first N results |
Response 200 OK:
{
"jobs": [
{
"id": "job_abc123def456",
"status": "running",
"folder_path": "/path/to/docs",
"operation": "index",
"include_code": true,
"source": "manual",
"enqueued_at": "2026-02-03T10:00:00Z",
"started_at": "2026-02-03T10:00:05Z",
"finished_at": null,
"progress_percent": 45.5,
"error": null
}
],
"total": 1,
"pending": 0,
"running": 1,
"completed": 0,
"failed": 0
}Get detailed information about a specific job.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
job_id |
string | Job identifier |
Response 200 OK:
{
"id": "job_abc123def456",
"status": "running",
"folder_path": "/path/to/docs",
"operation": "index",
"include_code": true,
"source": "manual",
"enqueued_at": "2026-02-03T10:00:00Z",
"started_at": "2026-02-03T10:00:05Z",
"finished_at": null,
"execution_time_ms": 15000,
"progress": {
"files_processed": 45,
"files_total": 100,
"chunks_created": 230,
"current_file": "src/services/auth.py",
"percent_complete": 45.0,
"updated_at": "2026-02-03T10:00:20Z"
},
"total_documents": 45,
"total_chunks": 230,
"error": null,
"retry_count": 0,
"cancel_requested": false,
"eviction_summary": null
}Errors:
| Code | Description |
|---|---|
404 |
Job not found |
Cancel a pending or running job.
- PENDING jobs are cancelled immediately.
- RUNNING jobs have
cancel_requestedflag set; the worker will stop at the next checkpoint. - Completed, failed, or already cancelled jobs return 409.
Response 200 OK:
{
"job_id": "job_abc123def456",
"status": "cancelled",
"message": "Job cancellation requested"
}Errors:
| Code | Description |
|---|---|
404 |
Job not found |
409 |
Cannot cancel completed/failed/cancelled job |
interface QueryRequest {
query: string; // 1-1000 characters
top_k?: number; // 1-50, default 5
similarity_threshold?: number; // 0.0-1.0, default 0.3
mode?: "bm25" | "vector" | "hybrid" | "graph" | "multi"; // default "hybrid"
alpha?: number; // 0.0-1.0, default 0.5
source_types?: string[]; // ["doc", "code", "test"]
languages?: string[]; // ["python", "typescript", ...]
file_paths?: string[]; // Glob patterns
entity_types?: string[]; // ["Class", "Function", ...] (graph/multi modes)
relationship_types?: string[]; // ["calls", "extends", ...] (graph/multi modes)
}interface QueryResponse {
results: QueryResult[];
query_time_ms: number;
total_results: number;
}
interface QueryResult {
text: string;
source: string;
score: number;
vector_score?: number;
bm25_score?: number;
graph_score?: number;
chunk_id: string;
source_type: string; // "doc", "code", or "test"
language?: string;
related_entities?: string[];
relationship_path?: string[];
rerank_score?: number; // if reranking enabled
original_rank?: number; // position before reranking (1-indexed)
metadata: Record<string, any>;
}interface IndexRequest {
folder_path: string;
chunk_size?: number; // 128-2048, default 512
chunk_overlap?: number; // 0-200, default 50
recursive?: boolean; // default true
include_code?: boolean; // default false
supported_languages?: string[];
code_chunk_strategy?: "ast_aware" | "text_based"; // default "ast_aware"
generate_summaries?: boolean; // default false
force?: boolean; // default false
include_patterns?: string[];
include_types?: string[]; // file type presets, e.g. ["python", "docs"]
exclude_patterns?: string[];
injector_script?: string; // path to Python injector script
folder_metadata_file?: string; // path to JSON metadata file
dry_run?: boolean; // default false
watch_mode?: string; // "auto" or "off"
watch_debounce_seconds?: number; // per-folder debounce in seconds
}interface IndexResponse {
job_id: string;
status: string;
message?: string;
}interface HealthStatus {
status: "healthy" | "indexing" | "degraded" | "unhealthy";
message?: string;
timestamp: string; // ISO 8601
version: string;
mode?: string; // "project" or "shared"
instance_id?: string;
project_id?: string;
active_projects?: number;
}interface IndexingStatus {
total_documents: number;
total_chunks: number;
total_doc_chunks: number;
total_code_chunks: number;
indexing_in_progress: boolean;
current_job_id?: string;
progress_percent: number;
last_indexed_at?: string; // ISO 8601
indexed_folders: string[];
supported_languages: string[];
graph_index?: GraphIndexStatus;
queue_pending: number;
queue_running: number;
current_job_running_time_ms?: number;
file_watcher?: FileWatcherStatus;
embedding_cache?: EmbeddingCacheStatus; // omitted when cache is empty
query_cache?: QueryCacheStatus;
}
interface GraphIndexStatus {
enabled: boolean;
initialized: boolean;
entity_count: number;
relationship_count: number;
store_type: string;
}
interface FileWatcherStatus {
running: boolean;
watched_folders: number;
}
interface EmbeddingCacheStatus {
hits: number;
misses: number;
hit_rate: number;
mem_entries: number;
entry_count: number;
size_bytes: number;
}
interface QueryCacheStatus {
hits: number;
misses: number;
hit_rate: number;
cached_entries: number;
index_generation: number;
}interface ProvidersStatus {
config_source?: string;
strict_mode: boolean;
validation_errors: string[];
providers: ProviderHealth[];
timestamp: string; // ISO 8601
}
interface ProviderHealth {
provider_type: string; // "embedding", "summarization", "reranker"
provider_name: string; // e.g. "openai", "anthropic", "ollama"
model: string;
status: string; // "healthy", "degraded", "unavailable"
message?: string;
dimensions?: number; // for embedding providers
}interface FolderListResponse {
folders: FolderInfo[];
total: number;
}
interface FolderInfo {
folder_path: string;
chunk_count: number;
last_indexed: string; // ISO 8601
watch_mode: string; // "off" or "auto"
watch_debounce_seconds?: number;
}interface FolderDeleteRequest {
folder_path: string;
}
interface FolderDeleteResponse {
folder_path: string;
chunks_deleted: number;
message: string;
}interface JobListResponse {
jobs: JobSummary[];
total: number;
pending: number;
running: number;
completed: number;
failed: number;
}
interface JobSummary {
id: string;
status: "pending" | "running" | "done" | "failed" | "cancelled";
folder_path: string;
operation: string; // "index" or "add"
include_code: boolean;
source: string; // "manual" or "auto"
enqueued_at: string;
started_at?: string;
finished_at?: string;
progress_percent: number;
error?: string;
}
interface JobDetailResponse {
id: string;
status: "pending" | "running" | "done" | "failed" | "cancelled";
folder_path: string;
operation: string;
include_code: boolean;
source: string;
enqueued_at: string;
started_at?: string;
finished_at?: string;
execution_time_ms?: number;
progress?: JobProgress;
total_documents: number;
total_chunks: number;
error?: string;
retry_count: number;
cancel_requested: boolean;
eviction_summary?: Record<string, any>;
}
interface JobProgress {
files_processed: number;
files_total: number;
chunks_created: number;
current_file: string;
percent_complete: number;
updated_at: string;
}{
"detail": "Error message describing what went wrong"
}| Code | Description |
|---|---|
200 |
Success |
202 |
Accepted (async operation started) |
400 |
Bad Request (invalid parameters) |
404 |
Not Found |
409 |
Conflict (e.g., embedding mismatch, jobs in progress) |
429 |
Too Many Requests (queue full) |
500 |
Internal Server Error |
503 |
Service Unavailable (index not ready or cache not initialized) |
Query Errors:
// Empty query
{
"detail": "Query cannot be empty"
}
// Index not ready
{
"detail": "Index not ready. Indexing is in progress."
}
// Embedding provider mismatch
{
"detail": "Embedding mismatch: ... Re-index with --force to resolve."
}Index Errors:
// Folder not found
{
"detail": "Folder not found: /path/to/nonexistent"
}
// Queue full
{
"detail": "Queue full (3 pending, 1 running). Try again later."
}
// Path outside project
{
"detail": "Path /external/path is outside project root /home/user/project"
}Health Check:
curl http://localhost:8000/healthProvider Status:
curl http://localhost:8000/health/providersSearch Query:
curl -X POST http://localhost:8000/query \
-H "Content-Type: application/json" \
-d '{
"query": "authentication implementation",
"mode": "hybrid",
"top_k": 10
}'Start Indexing (with force):
curl -X POST "http://localhost:8000/index?force=true" \
-H "Content-Type: application/json" \
-d '{
"folder_path": "/path/to/project",
"include_code": true,
"recursive": true
}'List Indexed Folders:
curl http://localhost:8000/index/foldersRemove a Folder from Index:
curl -X DELETE http://localhost:8000/index/folders \
-H "Content-Type: application/json" \
-d '{"folder_path": "/path/to/project/docs"}'Check Embedding Cache:
curl http://localhost:8000/index/cacheClear Embedding Cache:
curl -X DELETE http://localhost:8000/index/cacheList Jobs:
curl "http://localhost:8000/index/jobs?limit=10"Get Job Details:
curl http://localhost:8000/index/jobs/job_abc123def456Cancel a Job:
curl -X DELETE http://localhost:8000/index/jobs/job_abc123def456Reset Index:
curl -X DELETE http://localhost:8000/indexUsing httpx:
import httpx
BASE_URL = "http://localhost:8000"
async def search_documents(query: str, mode: str = "hybrid"):
async with httpx.AsyncClient() as client:
response = await client.post(
f"{BASE_URL}/query",
json={
"query": query,
"mode": mode,
"top_k": 10,
}
)
response.raise_for_status()
return response.json()
async def index_folder(folder_path: str, include_code: bool = False):
async with httpx.AsyncClient() as client:
response = await client.post(
f"{BASE_URL}/index",
params={"force": True},
json={
"folder_path": folder_path,
"include_code": include_code,
}
)
response.raise_for_status()
return response.json()
# Usage
results = await search_documents("authentication")
for result in results["results"]:
print(f"{result['source']}: {result['score']:.2f}")Polling for Indexing Completion:
import asyncio
import httpx
async def wait_for_indexing():
async with httpx.AsyncClient() as client:
while True:
response = await client.get(f"{BASE_URL}/health/status")
status = response.json()
if not status["indexing_in_progress"]:
print(f"Indexing complete: {status['total_chunks']} chunks")
break
print(f"Progress: {status['progress_percent']:.1f}%")
await asyncio.sleep(2)Using fetch:
const BASE_URL = 'http://localhost:8000';
async function searchDocuments(query, mode = 'hybrid') {
const response = await fetch(`${BASE_URL}/query`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query,
mode,
top_k: 10,
}),
});
if (!response.ok) {
throw new Error(`Query failed: ${response.statusText}`);
}
return response.json();
}
// Usage
const results = await searchDocuments('authentication');
results.results.forEach(result => {
console.log(`${result.source}: ${result.score.toFixed(2)}`);
});The Agent Brain API does not implement rate limiting by default. For production deployments, implement rate limiting at the reverse proxy level.
The API version is included in the health response:
{
"version": "9.0.0"
}Breaking changes will increment the major version number.
- Configuration Reference - Server and query settings
- Deployment Guide - Production deployment
- Plugin Guide - CLI and skill integration