api.md 13 KB

API Reference

AgentPaaS exposes a RESTful API via FastAPI. All endpoints (except /health and /) require authentication via the Authorization header with a bearer API key.

Base URL: http://localhost:8000 (development) or your production domain.

Interactive docs: GET /docs (Swagger UI) | GET /redoc (ReDoc)


Authentication

All authenticated endpoints require:

Authorization: Bearer ap_xxxxxxxxxxxx

API keys are scoped. Common scopes: agents:read, agents:write, agents:execute, keys:*, billing:read, admin:*.

Rate limiting applies per API key (sliding window, 60 requests/minute default). Exceeding the limit returns 429 Too Many Requests with a Retry-After: 60 header.


Agent CRUD

POST /api/v1/agents

Create a new agent. The config is validated by compiling it through lambdagent.from_config().

Request:

{
  "name": "my-research-agent",
  "description": "A research agent for summarization",
  "config": {
    "agentId": "research-01",
    "name": "researcher",
    "type": "react",
    "systemPrompt": "You are a research assistant.",
    "model": { "provider": "anthropic", "name": "claude-sonnet-4-20250514" },
    "react": { "maxSteps": 10 }
  },
  "tags": ["research", "production"],
  "environment": "production"
}

Response (201):

{
  "agent_id": "ag_abc123",
  "version": 1,
  "created_at": "2026-04-05T12:00:00Z",
  "endpoint": "/api/v1/agents/ag_abc123"
}

Errors: 400 INVALID_CONFIG -- config failed compilation.


GET /api/v1/agents

List all active agents for the authenticated tenant.

Query Parameters:

Param Type Description
tag string Filter by tag (optional)

Response (200):

{
  "agents": [
    {
      "id": "ag_abc123",
      "name": "my-research-agent",
      "description": "...",
      "current_version": 3,
      "tags": ["research"],
      "environment": "production",
      "status": "active",
      "created_at": "...",
      "updated_at": "..."
    }
  ],
  "count": 1
}

GET /api/v1/agents/{agent_id}

Get agent details including current version config.

Response (200): Full agent record with config field populated from the current version.

Errors: 404 AGENT_NOT_FOUND


PUT /api/v1/agents/{agent_id}

Update agent config. Automatically increments the version number. If the config hash is unchanged, no new version is created.

Required scope: agents:write

Request:

{
  "config": { "...updated config..." },
  "changelog": "Increased maxSteps to 20"
}

Response (200):

{
  "agent_id": "ag_abc123",
  "version": 4,
  "updated_at": "2026-04-05T12:30:00Z"
}

Errors: 400 INVALID_CONFIG, 404 AGENT_NOT_FOUND


DELETE /api/v1/agents/{agent_id}

Soft delete an agent (sets status to deleted).

Required scope: agents:write

Response (200):

{ "message": "Agent deleted", "agent_id": "ag_abc123" }

Execution

POST /api/v1/agents/{agent_id}/run

Synchronous agent execution. Compiles the config, runs the agent, and returns the result.

Required scope: agents:execute

Headers:

Header Description
X-Idempotency-Key Optional. If provided, duplicate requests return the cached result.

Request:

{
  "input": "Summarize the latest advances in quantum computing",
  "parameters": {
    "model.temperature": 0.5,
    "react.maxSteps": 15
  },
  "context": {}
}

The parameters field supports dot-notation for deep overrides (e.g., model.temperature sets config.model.temperature).

Response (200):

{
  "run_id": "run_xyz789",
  "status": "completed",
  "output": "Quantum computing has seen...",
  "usage": {
    "input_tokens": 1234,
    "output_tokens": 567,
    "total_tokens": 1801,
    "steps": 5,
    "duration_ms": 8320
  }
}

Errors: 404 AGENT_NOT_FOUND, 429 RATE_LIMITED, 500 EXECUTION_ERROR (with run_id for debugging)


POST /api/v1/agents/{agent_id}/run/stream

Server-Sent Events (SSE) streaming execution. Emits real-time step events as the agent runs.

Response: text/event-stream

Event types:

Event Data Description
think { "step": 1, "content": "...", "duration_ms": 500 } Agent reasoning step
think_chunk { "content": "partial text" } Streaming LLM chunk (ClaudeLam)
tool_call { "step": 2, "tool": "search", "content": "query" } Tool invocation
tool_result { "step": 2, "content": "result..." } Tool output
error { "message": "..." } Tool or execution error
answer { "content": "final answer" } Final agent answer
done { "status": "completed", "output": "...", "steps": 5 } Execution complete

Example client (JavaScript):

const es = new EventSource("/api/v1/agents/ag_abc123/run/stream", {
  method: "POST",
  headers: { "Authorization": "Bearer ap_xxx" },
  body: JSON.stringify({ input: "Hello" }),
});
es.addEventListener("think", (e) => console.log("Think:", JSON.parse(e.data)));
es.addEventListener("done", (e) => { console.log("Done:", JSON.parse(e.data)); es.close(); });

POST /api/v1/agents/{agent_id}/runs/record

Record an externally-executed run (e.g., from Claude Code CLI). Does NOT trigger lambdagent execution.

Request:

{
  "input": "...",
  "output": "...",
  "status": "completed",
  "duration_ms": 5000,
  "steps": 3,
  "source": "claude-code-cli"
}

Response (201):

{ "run_id": "run_abc", "status": "completed", "source": "claude-code-cli" }

GET /api/v1/agents/{agent_id}/runs

List recent runs for an agent.

Query Parameters: limit (default 20)

Response (200):

{ "runs": [ { "id": "run_xyz", "status": "completed", "duration_ms": 8320, "...": "..." } ] }

Versions

GET /api/v1/agents/{agent_id}/versions

List all versions of an agent, ordered newest first.

Required scope: agents:read

Response (200):

{
  "versions": [
    {
      "agent_id": "ag_abc123",
      "version": 3,
      "config": "...",
      "config_hash": "sha256...",
      "changelog": "Increased maxSteps",
      "created_by": "usr_xxx",
      "created_at": "...",
      "is_current": true
    }
  ]
}

POST /api/v1/agents/{agent_id}/rollback

Rollback to a previous version.

Request:

{ "target_version": 2 }

Response (200):

{ "agent_id": "ag_abc123", "version": 2, "message": "Rolled back" }

Errors: 404 VERSION_NOT_FOUND


Analysis

Static analysis endpoints for agent configs. These do not require an agent to be registered -- they accept raw configs.

POST /api/v1/analyze/lint

Lint an agent config for structural defects (26 rules: L001-L026).

Request:

{
  "config": { "type": "react", "systemPrompt": "...", "react": { "maxSteps": 50 } },
  "framework": "auto"
}

Response (200):

{
  "framework": "native",
  "errors": [
    { "rule": "L004a", "level": "ERROR", "message": "No terminate tool found (Y combinator has no base case)" }
  ],
  "warnings": [
    { "rule": "L010", "level": "WARN", "message": "maxSteps > 10; recommend runtime.engine: cek" }
  ],
  "info": [
    { "rule": "L025", "level": "INFO", "message": "Detected framework: native lambdagent" }
  ]
}

POST /api/v1/analyze/type-check

Type-check an agent pipeline using the T-Compose rule.

Response (200):

{
  "type_safe": true,
  "errors": []
}

Or when type errors are found:

{
  "type_safe": false,
  "errors": [
    { "stage": 1, "output_type": "Int", "input_type": "Json({name: string})" }
  ]
}

POST /api/v1/analyze/cost

Estimate worst-case execution cost via graded types.

Response (200):

{
  "tokens_upper_bound": 45000,
  "latency_sec": 12.5,
  "cost_usd": 0.135,
  "success_probability": 0.92
}

POST /api/v1/analyze/parallel-safety

Check store independence for parallel agents (Paper II Proposition 30).

Response (200):

{
  "safe": true,
  "conflicts": []
}

POST /api/v1/analyze/full

Run all analysis passes (lint + type-check + cost + parallel-safety) in a single request.

Response (200):

{
  "lint": { "framework": "...", "errors": [], "warnings": [], "info": [] },
  "types": { "type_safe": true, "errors": [] },
  "cost": { "tokens_upper_bound": 45000, "latency_sec": 12.5, "cost_usd": 0.135, "success_probability": 0.92 },
  "parallel": { "safe": true, "conflicts": [] }
}

API Keys (Auth)

POST /api/v1/auth/keys

Create a new API key for the current tenant.

Request:

{
  "name": "production-key",
  "scopes": ["agents:read", "agents:execute"],
  "rate_limit": 120,
  "expires_at": "2027-01-01T00:00:00Z"
}

Response (201):

{
  "key_id": "key_abc123",
  "api_key": "ap_a1b2c3d4e5f6...",
  "warning": "Store this key securely. It will not be shown again."
}

GET /api/v1/auth/keys

List all API keys for the current tenant (key values are not returned, only prefixes).

Response (200):

{
  "keys": [
    {
      "id": "key_abc123",
      "key_prefix": "ap_a1b2",
      "name": "production-key",
      "scopes": ["agents:read", "agents:execute"],
      "rate_limit": 120,
      "status": "active",
      "last_used_at": "...",
      "created_at": "..."
    }
  ]
}

DELETE /api/v1/auth/keys/{key_id}

Revoke an API key (sets status to revoked).

Response (200):

{ "message": "Key revoked" }

Admin

All admin endpoints require the admin:* scope.

POST /api/v1/admin/tenants

Create a new tenant with an admin user and initial API key.

Request:

{
  "name": "Acme Corp",
  "plan": "pro",
  "admin_email": "admin@acme.com"
}

Response (201):

{
  "tenant_id": "tn_xyz",
  "user_id": "usr_abc",
  "api_key": "ap_...",
  "warning": "Store this API key securely."
}

GET /api/v1/admin/tenants/{tenant_id}

Get tenant details.

Errors: 403 FORBIDDEN (non-admin), 404 TENANT_NOT_FOUND


PUT /api/v1/admin/tenants/{tenant_id}/quota

Set tenant resource quotas.

Request:

{
  "tokens_monthly": 5000000,
  "concurrency": 20,
  "agents": 100
}

Response (200):

{ "message": "Quota updated" }

GET /api/v1/admin/tenants/{tenant_id}/usage

Get tenant usage statistics.

Response (200):

{
  "runs": 1234,
  "input_tokens": 5678900,
  "output_tokens": 2345600,
  "total_duration_ms": 98765432
}

Billing

GET /api/v1/billing/usage

Query usage data for the current tenant.

Query Parameters:

Param Type Description
group_by string agent (default), model, or day

Response (200):

{
  "usage": [
    {
      "agent_id": "ag_abc123",
      "runs": 42,
      "input_tokens": 123456,
      "output_tokens": 65432,
      "total_ms": 345000
    }
  ],
  "tenant_id": "tn_xyz"
}

Status

GET /api/v1/status

Platform-wide status overview: total agents, runs, success rate, average latency.

GET /api/v1/status/agents

All agents status summary.

GET /api/v1/status/agents/{agent_id}

Detailed status for a single agent.

GET /api/v1/status/agents/{agent_id}/health

Agent health score.


Metrics

GET /api/v1/metrics/overview

Platform metrics snapshot for monitoring dashboards.


Health

GET /health

Unauthenticated health check endpoint.

Response (200):

{ "status": "ok", "version": "0.1.0" }

GET /

Root endpoint with service information.

Response (200):

{
  "service": "AgentPaaS",
  "version": "0.1.0",
  "docs": "/docs",
  "description": "Agent Platform as a Service -- Every agent is a Lambda term."
}

Common Error Format

All errors follow a consistent structure:

{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description"
  }
}
HTTP Status Code Description
400 INVALID_CONFIG Agent config failed compilation
401 UNAUTHORIZED Missing or invalid API key
403 FORBIDDEN Insufficient scope for this operation
404 AGENT_NOT_FOUND Agent does not exist or is deleted
404 VERSION_NOT_FOUND Requested version does not exist
404 JOB_NOT_FOUND Async job not found
421 HTTPS_REQUIRED HTTPS required in production
429 RATE_LIMITED Rate limit exceeded
500 EXECUTION_ERROR Agent execution failed (includes run_id)

Security Headers

All responses include:

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • X-XSS-Protection: 1; mode=block
  • Referrer-Policy: strict-origin-when-cross-origin
  • X-Response-Time-Ms: <ms>
  • X-Request-ID: <uuid> (for log correlation)

Production-only:

  • Strict-Transport-Security: max-age=31536000; includeSubDomains
  • Content-Security-Policy: default-src 'self'

Jobs (Async Execution)

GET /api/v1/jobs/{job_id}

Get async job status and result.

Response (200):

{
  "job_id": "job_abc",
  "status": "completed",
  "result": "...",
  "created_at": "..."
}

Status values: pending, running, completed, failed, cancelled.


POST /api/v1/jobs/{job_id}/cancel

Cancel a running or pending async job.

Response (200):

{ "message": "Job cancelled", "job_id": "job_abc" }