REST API
The daemon exposes a REST API on the configured port (default: 31415). All endpoints are prefixed with /api/ and accept JSON request bodies.
Local only
The API is bound to 127.0.0.1 with CORS restricted to localhost origins. It is not accessible from the network.
Agents
GET /api/agents
List all active agents.
curl http://localhost:31415/api/agents[
{
"id": "backend-1",
"role": "backend",
"status": "active",
"provider": "claude-code",
"model": "claude-sonnet-4-6",
"scope": ["src/api/**"],
"contextUsage": 0.45,
"spawnedAt": "2026-04-05T10:00:00Z"
}
]POST /api/agents
Spawn a new agent. Optionally set workingDir to spawn the agent inside a subdirectory.
curl -X POST http://localhost:31415/api/agents \
-H "Content-Type: application/json" \
-d '{"role": "backend", "scope": ["src/api/**"], "model": "claude-sonnet-4-6", "workingDir": "packages/backend"}'DELETE /api/agents/:id
Kill an agent.
curl -X DELETE http://localhost:31415/api/agents/backend-1POST /api/agents/:id/rotate
Trigger context rotation for an agent. Generates a handoff brief, kills the session, and respawns with fresh context.
curl -X POST http://localhost:31415/api/agents/backend-1/rotate{
"old": "backend-1",
"new": "backend-2",
"handoffBrief": true,
"reason": "manual"
}POST /api/agents/:id/instruct
Send an instruction to a running agent. Uses rotation-based delivery -- the instruction is injected into the agent's context on its next rotation cycle.
curl -X POST http://localhost:31415/api/agents/backend-1/instruct \
-H "Content-Type: application/json" \
-d '{"message": "Refactor the auth middleware to use JWT instead of sessions"}'POST /api/agents/:id/query
Query an agent without disrupting its current work. Runs a headless call to gather information.
curl -X POST http://localhost:31415/api/agents/backend-1/query \
-H "Content-Type: application/json" \
-d '{"question": "What is the current state of the database migration?"}'{
"answer": "Migration 003 is complete. Working on 004 — adding the sessions table.",
"agentId": "backend-1"
}Health
GET /api/health
Health check.
curl http://localhost:31415/api/health{
"status": "ok",
"uptime": 3600,
"agents": 3,
"version": "0.3.0"
}Teams
GET /api/teams
List saved teams.
curl http://localhost:31415/api/teamsPOST /api/teams
Save current configuration as a team.
curl -X POST http://localhost:31415/api/teams \
-H "Content-Type: application/json" \
-d '{"name": "fullstack"}'POST /api/teams/:name/load
Load and spawn a saved team.
curl -X POST http://localhost:31415/api/teams/fullstack/loadDELETE /api/teams/:name
Delete a saved team.
curl -X DELETE http://localhost:31415/api/teams/fullstackCredentials
GET /api/credentials
List stored credentials (keys are masked).
curl http://localhost:31415/api/credentialsPOST /api/credentials
Store an API key for a provider.
curl -X POST http://localhost:31415/api/credentials \
-H "Content-Type: application/json" \
-d '{"provider": "codex", "key": "sk-your-key"}'DELETE /api/credentials/:provider
Delete a stored credential.
curl -X DELETE http://localhost:31415/api/credentials/codexProviders
GET /api/providers
List available providers and their installation status.
curl http://localhost:31415/api/providersConfiguration
GET /api/config
Get current configuration.
curl http://localhost:31415/api/configPATCH /api/config
Update configuration values.
curl -X PATCH http://localhost:31415/api/config \
-H "Content-Type: application/json" \
-d '{"rotationThreshold": 0.7, "maxAgents": 8}'Dashboard
GET /api/dashboard
Aggregated dashboard data -- agent summary, token usage, savings, and system status in a single call.
curl http://localhost:31415/api/dashboard{
"agents": { "active": 3, "total": 7, "rotations": 4 },
"tokens": { "used": 1250000, "saved": 380000, "budget": null },
"savings": { "rotation": 210000, "coldStart": 120000, "conflict": 50000 },
"uptime": 7200
}Project Manager
POST /api/pm/review
Trigger a PM review of a specific agent's recent changes.
curl -X POST http://localhost:31415/api/pm/review \
-H "Content-Type: application/json" \
-d '{"agentId": "backend-1"}'GET /api/pm/history
Get PM review history.
curl http://localhost:31415/api/pm/historyJournalist
GET /api/journalist
Get Journalist status and last cycle info.
curl http://localhost:31415/api/journalist{
"running": true,
"lastCycle": "2026-04-05T10:02:00Z",
"interval": 120,
"documentsGenerated": ["GROOVE_PROJECT_MAP.md", "GROOVE_DECISIONS.md"]
}POST /api/journalist/cycle
Manually trigger a Journalist synthesis cycle.
curl -X POST http://localhost:31415/api/journalist/cycleApprovals
GET /api/approvals
List pending approval requests.
curl http://localhost:31415/api/approvalsPOST /api/approvals
Submit an approval decision.
curl -X POST http://localhost:31415/api/approvals \
-H "Content-Type: application/json" \
-d '{"id": "approval-1", "decision": "approve"}'Tokens
GET /api/tokens/summary
Token usage summary with savings breakdown.
curl http://localhost:31415/api/tokens/summary{
"totalUsed": 1250000,
"totalSaved": 380000,
"breakdown": {
"rotation": 210000,
"coldStart": 120000,
"conflict": 50000
},
"perAgent": [
{ "id": "backend-1", "used": 450000, "model": "claude-sonnet-4-6" }
]
}Routing
GET /api/routing
Adaptive model routing status and cost tracking.
curl http://localhost:31415/api/routing{
"mode": "auto",
"decisions": 12,
"costSaved": 0.45,
"currentMappings": {
"backend-1": { "model": "claude-sonnet-4-6", "reason": "medium_complexity" }
}
}Rotation
GET /api/rotation
Rotation statistics and history.
curl http://localhost:31415/api/rotation{
"totalRotations": 4,
"history": [
{
"old": "backend-1",
"new": "backend-2",
"reason": "context_threshold",
"timestamp": "2026-04-05T10:30:00Z"
}
]
}Gateways
GET /api/gateways
List all configured gateways with connection status.
curl http://localhost:31415/api/gateways[
{
"id": "telegram-abc123",
"type": "telegram",
"displayName": "Telegram",
"connected": true,
"enabled": true,
"chatId": "123456789",
"notifications": { "preset": "critical" },
"commandPermission": "full",
"allowedUsers": 1,
"botUsername": "my_groove_bot"
}
]POST /api/gateways
Create a new gateway.
curl -X POST http://localhost:31415/api/gateways \
-H "Content-Type: application/json" \
-d '{"type": "telegram"}'Supported types: telegram, discord, slack.
GET /api/gateways/:id
Get a specific gateway's status and configuration.
curl http://localhost:31415/api/gateways/telegram-abc123PATCH /api/gateways/:id
Update gateway configuration.
curl -X PATCH http://localhost:31415/api/gateways/telegram-abc123 \
-H "Content-Type: application/json" \
-d '{"notifications": {"preset": "lifecycle"}, "commandPermission": "read-only"}'Updatable fields: enabled, chatId, allowedUsers, notifications, commandPermission.
DELETE /api/gateways/:id
Delete a gateway and disconnect it.
curl -X DELETE http://localhost:31415/api/gateways/telegram-abc123POST /api/gateways/:id/test
Send a test notification through the gateway.
curl -X POST http://localhost:31415/api/gateways/telegram-abc123/testPOST /api/gateways/:id/connect
Manually connect a gateway.
curl -X POST http://localhost:31415/api/gateways/telegram-abc123/connectPOST /api/gateways/:id/disconnect
Manually disconnect a gateway.
curl -X POST http://localhost:31415/api/gateways/telegram-abc123/disconnectPOST /api/gateways/:id/credentials
Set a credential for a gateway (e.g., bot token).
curl -X POST http://localhost:31415/api/gateways/telegram-abc123/credentials \
-H "Content-Type: application/json" \
-d '{"key": "bot_token", "value": "123456:ABC-DEF..."}'DELETE /api/gateways/:id/credentials/:key
Delete a gateway credential.
curl -X DELETE http://localhost:31415/api/gateways/telegram-abc123/credentials/bot_tokenCodebase Indexer
GET /api/indexer
Indexer status -- whether the scan has run and how many workspaces were detected.
curl http://localhost:31415/api/indexer{
"indexed": true,
"lastIndexTime": 1712300000000,
"workspaceCount": 3,
"stats": { "totalFiles": 847, "totalDirs": 42, "treeDepth": 4 }
}GET /api/indexer/workspaces
List detected workspaces with metadata.
curl http://localhost:31415/api/indexer/workspaces{
"workspaces": [
{ "path": "packages/frontend", "name": "@mysite/frontend", "type": "npm-workspaces", "files": 124, "dirs": 8 },
{ "path": "packages/backend", "name": "@mysite/backend", "type": "npm-workspaces", "files": 89, "dirs": 6 }
]
}POST /api/indexer/rescan
Re-scan the project structure. Useful after adding new workspaces or restructuring.
curl -X POST http://localhost:31415/api/indexer/rescanIntegrations
GET /api/integrations/:id/tools
List the tools available from an installed integration. Starts the MCP server if not already running.
curl http://localhost:31415/api/integrations/github/tools{
"tools": [
{
"name": "create_issue",
"description": "Create a new issue in a GitHub repository",
"inputSchema": {
"type": "object",
"properties": {
"owner": { "type": "string" },
"repo": { "type": "string" },
"title": { "type": "string" },
"body": { "type": "string" }
},
"required": ["owner", "repo", "title"]
}
}
]
}POST /api/integrations/:id/exec
Execute a tool on an installed integration. The daemon proxies the call to the MCP server over JSON-RPC and returns the result as plain JSON. Dangerous tools return 202 and require human approval before executing.
Standard call (non-gated tool):
curl -X POST http://localhost:31415/api/integrations/github/exec \
-H "Content-Type: application/json" \
-d '{"tool": "search_repositories", "params": {"query": "groove"}}'{
"result": {
"content": [
{ "type": "text", "text": "Found 3 repositories matching \"groove\"..." }
]
}
}Approval-gated call (dangerous tool):
curl -X POST http://localhost:31415/api/integrations/github/exec \
-H "Content-Type: application/json" \
-d '{"tool": "create_issue", "params": {"owner": "myorg", "repo": "myapp", "title": "Fix redirect"}}'{
"requiresApproval": true,
"approvalId": "approval-42",
"message": "Tool \"create_issue\" requires approval. Retry with this approvalId once approved."
}Retry after approval:
curl -X POST http://localhost:31415/api/integrations/github/exec \
-H "Content-Type: application/json" \
-d '{"tool": "create_issue", "params": {"owner": "myorg", "repo": "myapp", "title": "Fix redirect"}, "approvalId": "approval-42"}'Request body:
| Field | Type | Required | Description |
|---|---|---|---|
tool | string | Yes | Name of the MCP tool to execute |
params | object | No | Arguments to pass to the tool (defaults to {}) |
approvalId | string | No | Approval ID from a previous 202 response — required to execute gated tools |
agent | string | No | Agent ID making the call — recorded in audit log |
Responses:
| Status | Meaning |
|---|---|
| 200 | Tool executed successfully |
| 202 | Tool requires approval — approvalId returned, retry after approval |
| 400 | Validation error, integration not installed, or MCP server error |
| 403 | Approval was rejected |
| 404 | approvalId not found |
| 429 | Rate limit exceeded (30 calls/min per integration) |
Audit events: Every call is logged — integration.exec on success, integration.exec.blocked when approval-gated, integration.exec.rate_limited when throttled.
Adaptive
GET /api/adaptive
Adaptive threshold data -- per-provider, per-role rotation thresholds and session scores.
curl http://localhost:31415/api/adaptive{
"thresholds": {
"claude-code": { "backend": 0.73, "frontend": 0.78 },
"codex": { "backend": 0.75 }
},
"convergence": { "claude-code": true, "codex": false }
}Coordination
Agent coordination on shared resources (npm install, server restart, shared config). Returns 423 on conflict with the owner's identity. Locks auto-expire after 10 minutes to prevent deadlock.
POST /api/coordination/declare
Acquire an exclusive lock on a list of resources.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
agentId | string | Yes | Agent declaring the operation |
operation | string | Yes | Human-readable name (e.g., "npm install") |
resources | string[] | Yes | Resources to lock (arbitrary strings, exact-match) |
ttlMs | number | No | Lock TTL in ms. Default 600000 (10 min) |
Success response (200):
{ "declared": true, "operation": "npm install", "resources": ["package.json"] }Conflict response (423):
{
"conflict": true,
"owner": "bk-2",
"operation": "build",
"resource": "package.json",
"expiresAt": 1744500000000
}POST /api/coordination/complete
Release any locks held by the agent. Always call this after an operation — even on failure — to avoid deadlock.
Request body:
| Field | Type | Required |
|---|---|---|
agentId | string | Yes |
GET /api/coordination
List all currently-declared operations.
{
"operations": {
"bk-2": {
"name": "npm install",
"resources": ["package.json"],
"acquiredAt": 1744499700000,
"expiresAt": 1744500300000
}
}
}Memory (Layer 7)
Persistent agent memory that survives rotations, restarts, and new spawns. See Persistent Memory guide for conceptual overview.
GET /api/memory/constraints
List all project constraints.
{ "constraints": [{ "hash": "a1b2c3d4", "category": "hard", "text": "ESM only — never use require()" }] }POST /api/memory/constraints
Add a new constraint. Dedup'd by hash — identical text returns { added: false, reason: "duplicate" }.
Request body: { "text": "...", "category": "hard" } — text 3-500 chars, category optional (defaults to "general").
DELETE /api/memory/constraints/:hash
Remove a constraint by its hash.
GET /api/memory/handoff-chain
List roles that have an active handoff chain.
{ "roles": ["backend", "frontend", "planner"] }GET /api/memory/handoff-chain/:role
Full chain entries for a role (last 10 rotations, newest first).
{
"role": "backend",
"entries": [
{ "rotationN": 8, "body": "## Rotation 8 — ..." },
{ "rotationN": 7, "body": "## Rotation 7 — ..." }
]
}GET /api/memory/handoff-chain/:role/recent
Markdown-formatted recent briefs for injection into agent context. Query param ?count=3 (max 10).
GET /api/memory/discoveries
List error→fix discoveries. Query params: ?role=backend, ?limit=100 (max 500).
{
"discoveries": [
{
"ts": "2026-04-12T14:00:00Z",
"agentId": "bk-3",
"role": "backend",
"trigger": "Cannot find module gray-matter",
"fix": "npm install gray-matter",
"outcome": "success"
}
]
}POST /api/memory/discoveries
Record a new discovery.
Request body:
| Field | Type | Required |
|---|---|---|
agentId | string | No |
role | string | No (default: "unknown") |
trigger | string | Yes (max 300 chars) |
fix | string | Yes (max 500 chars) |
outcome | string | No (must be "success" to store, default "success") |
Failed attempts are not stored. Identical trigger+fix pairs are deduplicated.
GET /api/memory/specializations
Full per-agent and per-role quality profiles.
{
"perAgent": {
"bk-3": {
"role": "backend",
"sessionCount": 14,
"avgQualityScore": 78,
"fileTouches": { "packages/daemon/src/journalist.js": 23 },
"preferredThreshold": 0.82
}
},
"perProjectRole": {
"backend": { "sessionCount": 42, "avgQualityScore": 74, "topFileChurn": { "packages/daemon/src/api.js": 18 } }
}
}GET /api/memory/specializations/:agentId
Single agent's profile. Returns 404 if no data.
Tokens (additions)
GET /api/tokens/by-team
Ranked per-team token burn. Answers "which team used the most tokens?" in one call.
{
"teams": [
{
"teamId": "T1",
"teamName": "agents",
"isDefault": false,
"agentCount": 4,
"totalTokens": 48_000_000,
"totalCostUsd": 12.40,
"avgTokensPerAgent": 12_000_000
}
]
}Unassigned agents appear under teamId: "__unassigned__".
GET /api/tokens/summary additions
The existing summary response now includes internalOverhead — tokens and cost consumed by GROOVE's own coordination (Journalist, PM, planner, negotiator, gateway, agent Q&A). Use this to compute honest ROI against savings.total.
{
"totalTokens": 275_000_000,
"cacheHitRate": 0.887,
"savings": { "total": 32_000_000, "fromRotation": 29_800_000, "percentage": 10.4 },
"internalOverhead": {
"tokens": 1_200_000,
"costUsd": 0.36,
"components": {
"__journalist__": { "tokens": 800_000, "costUsd": 0.24, "sessions": 240 },
"__pm__": { "tokens": 300_000, "costUsd": 0.09, "sessions": 18 },
"__negotiator__": { "tokens": 100_000, "costUsd": 0.03, "sessions": 4 }
}
}
}Toys
GET /api/toys
List all available API toys. Optionally filter by category.
curl http://localhost:31415/api/toys[
{
"id": "nasa-eonet",
"name": "NASA EONET",
"category": "space",
"auth": "api-key",
"difficulty": "intermediate",
"description": "Earth Observatory natural events — wildfires, storms, volcanoes",
"docsUrl": "https://eonet.gsfc.nasa.gov/docs/v3",
"starters": [
"Build a live earthquake map",
"Track wildfire activity by region",
"Create a natural disaster alert dashboard"
]
}
]Query parameters:
| Param | Type | Description |
|---|---|---|
category | string | Filter by category — space, weather, finance, fun, maps, data |
GET /api/toys/:id
Get a single toy by ID.
curl http://localhost:31415/api/toys/nasa-eonetReturns the same shape as a single item from GET /api/toys. Returns 404 if the toy ID doesn't exist.
POST /api/toys/:id/launch
Launch a toy — creates a new team and spawns a planner agent that web-fetches the API docs and starts a conversation.
curl -X POST http://localhost:31415/api/toys/nasa-eonet/launch \
-H "Content-Type: application/json" \
-d '{"apiKey": "your-nasa-api-key", "starterPrompt": "Build a live earthquake map"}'{
"team": {
"id": "T12",
"name": "Toy: NASA EONET"
},
"agent": {
"id": "planner-25",
"role": "planner",
"status": "active"
},
"toyId": "nasa-eonet"
}Request body:
| Field | Type | Required | Description |
|---|---|---|---|
apiKey | string | No | API key for the toy — encrypted and stored locally. Omit for free APIs |
starterPrompt | string | No | Pre-selected starter idea — skips the "what do you want to build?" step |
Responses:
| Status | Meaning |
|---|---|
| 200 | Team created, planner spawned |
| 400 | Validation error (e.g., toy requires a key but none provided) |
| 404 | Toy ID not found |
Routing Suggestions
GET /api/agents/:id/routing/suggestion
When the classifier has 40+ events and detects that a lighter model would handle the agent's current task, returns a downshift suggestion. Never auto-applied — user must accept via UI click.
Response (200):
{
"agentId": "bk-3",
"currentModel": { "id": "claude-opus-4-6", "name": "Opus 4.6", "tier": "heavy" },
"suggestedModel": { "id": "claude-haiku-4-5", "name": "Haiku 4.5", "tier": "light" },
"classifiedTier": "light",
"eventCount": 65,
"reason": "Last 65 events classified as light. A lighter model would likely handle this task and reduce cost."
}Response (204): No suggestion — classifier confidence too low, tiers already aligned, or current model is already cheapest.
Never suggests upshift (moving to a heavier tier). Honors the heavy-defaults design principle.
