Aberon HTTP API Reference
Base URL: http://<host>:8080
All API endpoints: http://<host>:8080/api/
Auth: Authorization: Bearer <token> (JWT or API key)
Content-Type: application/json
Response envelope:
{"success": true, "data": ..., "error": null, "meta": null}
Authentication
Login (get JWT token)
POST /api/auth/login
{"email": "admin@aberon.internal", "password": "your-password"}
Returns: {"token": "eyJ...", "user": {"id": "...", "email": "...", "role": "admin"}}
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@aberon.internal","password":"Admin123!"}'
Create API key
POST /api/auth/keys
Authorization: Bearer <jwt>
{
"name": "my-agent-key",
"scopes": ["agent:write", "trace:write", "guardrail:check"],
"data_access": "full"
}
Scopes:
| Scope | Description |
|---|---|
agent:write |
Register agents, send heartbeats |
trace:write |
Create traces and spans |
guardrail:check |
Run guardrail checks |
data_access:
| Value | What the key can read |
|---|---|
full |
All span content (default) |
redacted |
Content replaced with [REDACTED] |
metadata_only |
Only metrics, no content |
Returns: {"key": "aberon_sk_...", "key_id": "...", "prefix": "aberon_sk_xxxx"} — save the key, shown only once
curl -X POST http://localhost:8080/api/auth/keys \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"name":"agent-key","scopes":["agent:write","trace:write","guardrail:check"]}'
List API keys
GET /api/auth/keys
Authorization: Bearer <jwt>
Revoke API key
DELETE /api/auth/keys/{key_id}
Authorization: Bearer <jwt>
Agents
Register agent
POST /api/agents
Authorization: Bearer <api_key>
{
"name": "my-llm-agent",
"model": "qwen-2.5-72b",
"framework": "custom",
"project": "my-project",
"environment": "production",
"capture_mode": "full",
"external_ref": "my-agent-v1",
"parent_agent_id": null
}
capture_mode:
| Value | Stored in DB |
|---|---|
full |
All prompts and outputs |
redacted |
Text replaced with [REDACTED] |
metadata_only |
Only token counts, no content |
Returns: {"id": "uuid", "name": "...", "capture_mode": "full", ...}
curl -X POST http://localhost:8080/api/agents \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"my-agent","model":"qwen-2.5-72b","capture_mode":"full"}'
Heartbeat (keep agent online)
POST /api/agents/{agent_id}/heartbeat
Authorization: Bearer <api_key>
Body: {} (empty)
Call every 30 seconds to keep agent status = active. Missing heartbeats for 5+ minutes → agent moves to offline.
curl -X POST http://localhost:8080/api/agents/$AGENT_ID/heartbeat \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
List agents
GET /api/agents
Authorization: Bearer <jwt_or_api_key>
Get agent
GET /api/agents/{agent_id}
Authorization: Bearer <jwt_or_api_key>
Traces
Create trace
POST /api/traces
Authorization: Bearer <api_key>
{
"trace_id": "unique-trace-id-hex-or-uuid",
"agent_id": "uuid-of-agent",
"parent_trace_id": null,
"metadata": {"task": "contract-review"}
}
trace_id — client-generated, any unique string (e.g. uuid4().hex)
TRACE_ID=$(python3 -c "import uuid; print(uuid.uuid4().hex)")
curl -X POST http://localhost:8080/api/traces \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"trace_id\":\"$TRACE_ID\",\"agent_id\":\"$AGENT_ID\"}"
Add span to trace
POST /api/traces/{trace_id}/spans
Authorization: Bearer <api_key>
{
"span_id": "unique-span-id",
"parent_span_id": null,
"name": "llm_call",
"kind": "llm",
"input_data": {"prompt": "..."},
"output_data": {"response": "..."},
"tokens_in": 1500,
"tokens_out": 800,
"latency_ms": 4200,
"status": "ok",
"started_at": "2026-04-07T10:00:00.000000+00:00",
"completed_at": "2026-04-07T10:00:04.200000+00:00"
}
kind values: llm, tool, guardrail, reasoning, other
status values: ok, error
Complete / update trace
PATCH /api/traces/{trace_id}
Authorization: Bearer <api_key>
{
"status": "completed",
"total_tokens": 2300,
"input_tokens": 1500,
"output_tokens": 800,
"cost_usd": 0.0023,
"latency_ms": 4200
}
status values: running, completed, error
List traces
GET /api/traces?agent_id=uuid&status=completed&page=1&per_page=20
Authorization: Bearer <jwt_or_api_key>
Query params: agent_id, status (running/completed/error), from, to, root_only, has_error, page, per_page
Get trace detail (with span tree)
GET /api/traces/{trace_id}
Authorization: Bearer <jwt_or_api_key>
Returns full trace with nested spans. Content filtered by API key data_access level.
Guardrails
Check guardrails (MUST call before risky actions)
POST /api/guardrails/check
Authorization: Bearer <api_key>
{
"agent_id": "uuid",
"trace_id": "current-trace-id",
"input_text": "text to check",
"tool_name": "deploy_config",
"current_cost_usd": 0.15,
"context": {}
}
Returns:
{
"allowed": true,
"blocked_by": [],
"requires_approval": null,
"sanitized_text": null,
"latency_ms": 12.5
}
Result handling:
if result["allowed"]:
proceed() # all policies passed
elif result["blocked_by"]:
stop() # blocked by policy
elif result["requires_approval"]:
wait_for_approval(result["requires_approval"]["approval_id"])
If sanitized_text is not null — use it instead of original (PII was redacted).
curl -X POST http://localhost:8080/api/guardrails/check \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"agent_id\":\"$AGENT_ID\",\"trace_id\":\"$TRACE_ID\",\"input_text\":\"some text\",\"tool_name\":\"deploy_config\"}"
Approvals
Poll approval status
GET /api/approvals/{approval_id}
Authorization: Bearer <jwt_or_api_key>
Returns:
{
"id": "uuid",
"status": "pending",
"tool_name": "deploy_config",
"timeout_at": "2026-04-07T10:05:00+00:00"
}
status values: pending → approved / denied / expired
Poll every 3-5 seconds until status is not pending.
# Poll until decided
while true; do
STATUS=$(curl -s http://localhost:8080/api/approvals/$APPROVAL_ID \
-H "Authorization: Bearer $JWT" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['status'])")
echo "Status: $STATUS"
[ "$STATUS" != "pending" ] && break
sleep 3
done
Decide (approve or deny)
POST /api/approvals/{approval_id}/decide
Authorization: Bearer <jwt> (admin or agent_owner only)
{"decision": "approve", "reason": "Reviewed and approved"}
decision values: approve | deny
List approvals
GET /api/approvals?status=pending&page=1&per_page=20
Authorization: Bearer <jwt_or_api_key>
Query params: status, agent_id, page, per_page
Policies
Create policy
POST /api/policies
Authorization: Bearer <jwt> (admin only)
PII detection policy:
{
"name": "block-pii",
"type": "pii",
"config": {
"action": "block",
"entities": ["EMAIL_ADDRESS", "PHONE_NUMBER", "PERSON"]
},
"is_active": true,
"priority": 0
}
Cost limit policy:
{
"name": "cost-limit-50usd",
"type": "cost_limit",
"config": {
"max_cost_usd": 50.0,
"period": "run"
},
"is_active": true,
"priority": 0
}
Tool restriction (require human approval):
{
"name": "approve-deployments",
"type": "tool_restriction",
"config": {
"tools": ["deploy_config", "write_to_switch", "execute_sql"],
"action": "require_approval",
"approval_timeout_seconds": 300
},
"target_agent_id": "uuid-of-agent",
"apply_to_children": true,
"is_active": true,
"priority": 10
}
Leave target_agent_id as null for a global policy (applies to all agents).
List policies
GET /api/policies
Authorization: Bearer <jwt_or_api_key>
Audit Log
Search audit entries
GET /api/audit?event_type=guardrail.blocked&page=1&per_page=50
Authorization: Bearer <jwt>
Query params: event_type, agent_id, from_time, to_time, page, per_page
Event types: agent.created, agent.heartbeat_missed, trace.created, guardrail.blocked, guardrail.approval_requested, approval.created, approval.decided, approval.expired, policy.created, policy.updated, api_key.created, user.login
Verify audit chain integrity
GET /api/audit/verify
Authorization: Bearer <jwt>
Returns: {"valid": true, "entries_checked": 245, "message": "All 245 entries verified"}
Export audit log
GET /api/audit/export?from_time=2026-04-01T00:00:00Z&to_time=2026-04-08T00:00:00Z
Authorization: Bearer <jwt>
Returns JSON array of all audit entries in chronological order.
Health
GET /health → {"status": "ok"}
GET /ready → {"status": "ready", "db": true}
No authentication required.
What JWT vs API Key Can Do
| Action | API key | JWT (login) |
|---|---|---|
| Register agents | ✅ (agent:write) |
✅ |
| Send heartbeats | ✅ (agent:write) |
✅ |
| Create traces / spans | ✅ (trace:write) |
✅ |
| Check guardrails | ✅ (guardrail:check) |
✅ |
| Read traces | ✅ (filtered by data_access) |
✅ (always full) |
| Create / edit policies | ❌ | ✅ admin only |
| Create API keys | ❌ | ✅ admin only |
| Approve / deny approvals | ❌ | ✅ admin / agent_owner |
| Manage users | ❌ | ✅ admin only |
For an AI orchestrator that needs to do everything — login with email+password to get a JWT:
# Get JWT (valid 24 hours)
JWT=$(curl -s -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@aberon.internal","password":"Admin123!"}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['data']['token'])")
# Use JWT for everything: policies, approvals, keys, agents
curl -X POST http://localhost:8080/api/policies \
-H "Authorization: Bearer $JWT" ...
JWT expires in 24 hours — re-login to get a new one.
Full Agent Flow Example
Complete example: register agent, check guardrails, create trace, add span, complete trace.
BASE=http://localhost:8080
# 1. Login
JWT=$(curl -s -X POST $BASE/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@aberon.internal","password":"Admin123!"}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['data']['token'])")
# 2. Create API key
API_KEY=$(curl -s -X POST $BASE/api/auth/keys \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"name":"my-key","scopes":["agent:write","trace:write","guardrail:check"]}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['data']['key'])")
# 3. Register agent
AGENT_ID=$(curl -s -X POST $BASE/api/agents \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"my-agent","model":"qwen-2.5-72b","capture_mode":"full"}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['data']['id'])")
# 4. Check guardrails before action
RESULT=$(curl -s -X POST $BASE/api/guardrails/check \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"agent_id\":\"$AGENT_ID\",\"trace_id\":\"trace-001\",\"input_text\":\"some input\",\"tool_name\":\"deploy_config\"}")
ALLOWED=$(echo $RESULT | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['allowed'])")
echo "Allowed: $ALLOWED"
# 5. Create trace
TRACE_ID=$(python3 -c "import uuid; print(uuid.uuid4().hex)")
curl -s -X POST $BASE/api/traces \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"trace_id\":\"$TRACE_ID\",\"agent_id\":\"$AGENT_ID\"}"
# 6. Add span
NOW=$(python3 -c "from datetime import datetime,timezone; print(datetime.now(timezone.utc).isoformat())")
curl -s -X POST $BASE/api/traces/$TRACE_ID/spans \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"span_id\":\"span-001\",\"name\":\"llm_call\",\"kind\":\"llm\",\"tokens_in\":1500,\"tokens_out\":800,\"status\":\"ok\",\"started_at\":\"$NOW\",\"completed_at\":\"$NOW\"}"
# 7. Complete trace
curl -s -X PATCH $BASE/api/traces/$TRACE_ID \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"status":"completed","total_tokens":2300,"cost_usd":0.0023,"latency_ms":4200}'
# 8. Heartbeat (call every 30 seconds in background)
curl -s -X POST $BASE/api/agents/$AGENT_ID/heartbeat \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
echo "Done! View trace at: $BASE (login and go to Traces)"
Error Responses
| HTTP Code | Meaning |
|---|---|
400 |
Bad request — invalid input |
401 |
Missing or invalid auth token |
403 |
Insufficient permissions or missing scope |
404 |
Resource not found |
409 |
Conflict — e.g. deciding an already-decided approval |
422 |
Validation error — check request body |
429 |
Rate limit exceeded (default: 600 req/min) |
500 |
Internal server error — check backend logs |
Error format:
{"success": false, "data": null, "error": {"code": "...", "message": "..."}, "meta": null}
Guardrail results (200, not errors):
Blocked / approval required are business results, not errors — always HTTP 200 with success: true.
Aberon API v0.1 — SDK: aberon.ai/docs/sdk