AgentHub exposes a REST API for managing agencies, agents, blueprints, schedules, and more.
All API endpoints require authentication when SECRET is set in your environment.
# Via header
curl -H "X-SECRET: your-secret" https://your-hub.workers.dev/agencies
# Via query parameter
curl https://your-hub.workers.dev/agencies?key=your-secretAll endpoints are relative to your deployment URL (e.g., https://your-hub.workers.dev).
Agencies provide multi-tenant isolation. Each agency has its own blueprints, agents, vars, and filesystem.
GET /agencies
Response:
{
"agencies": [
{ "id": "my-agency", "name": "my-agency", "createdAt": "2024-01-01T00:00:00Z" }
]
}POST /agencies
Content-Type: application/json
{ "name": "my-agency" }
Response: 201 Created
{ "id": "my-agency", "name": "my-agency", "createdAt": "2024-01-01T00:00:00Z" }Errors:
400- Name is required or invalid (must be alphanumeric with dashes/underscores)409- Agency already exists
DELETE /agency/:agencyId
Deletes the agency and all its data (agents, blueprints, vars, filesystem).
Blueprints define agent templates with prompts, capabilities, and configuration.
GET /agency/:agencyId/blueprints
Returns both static (code-defined) and dynamic (runtime-created) blueprints.
Response:
{
"blueprints": [
{
"name": "assistant",
"description": "A helpful assistant",
"prompt": "You are a helpful assistant.",
"capabilities": ["@default"],
"model": "gpt-4o"
}
]
}POST /agency/:agencyId/blueprints
Content-Type: application/json
{
"name": "my-agent",
"description": "Custom agent",
"prompt": "You are a custom agent.",
"capabilities": ["@default", "my-tool"],
"model": "gpt-4o",
"vars": { "MAX_ITERATIONS": 20 }
}
DELETE /agency/:agencyId/blueprints/:blueprintName
Only deletes dynamic blueprints (code-defined blueprints cannot be deleted via API).
Agents are running instances of blueprints with their own conversation state.
GET /agency/:agencyId/agents
Response:
{
"agents": [
{
"id": "abc123",
"agentType": "assistant",
"status": "completed",
"createdAt": "2024-01-01T00:00:00Z"
}
]
}Returns the hierarchical tree of agents (parent-child relationships for subagents).
GET /agency/:agencyId/agents/tree
POST /agency/:agencyId/agents
Content-Type: application/json
{
"agentType": "assistant",
"vars": { "CUSTOM_VAR": "value" }
}
Response: 201 Created
{
"id": "abc123",
"agentType": "assistant",
"agencyId": "my-agency",
"createdAt": "2024-01-01T00:00:00Z"
}DELETE /agency/:agencyId/agents/:agentId
GET /agency/:agencyId/agents/:agentId/tree
These endpoints interact with a specific agent instance.
GET /agency/:agencyId/agent/:agentId/state
Response:
{
"state": {
"messages": [...],
"tools": [...],
"thread": { "id": "...", "agentType": "...", ... },
"todos": [...]
},
"run": {
"status": "completed",
"step": 5
}
}Send messages to an agent and trigger a run.
POST /agency/:agencyId/agent/:agentId/invoke
Content-Type: application/json
{
"messages": [
{ "role": "user", "content": "Hello!" }
]
}
Request body:
| Field | Type | Description |
|---|---|---|
messages |
ChatMessage[] |
Messages to add to the conversation |
files |
Record<string, string> |
Files to upload (path -> content) |
idempotencyKey |
string |
Prevent duplicate processing |
vars |
Record<string, unknown> |
Override vars for this invocation |
POST /agency/:agencyId/agent/:agentId/action
Content-Type: application/json
{
"plugin": "hitl",
"action": "approve",
"payload": { "approved": true }
}
GET /agency/:agencyId/agent/:agentId/events
Accept: text/event-stream
Returns a Server-Sent Events stream of agent events.
GET /agency/:agencyId/agent/:agentId
Upgrade: websocket
Establishes a WebSocket connection directly to a specific agent for real-time events.
Events received: All agent events are broadcast to connected clients (see Agent Events below).
Subscribe to events from multiple agents through a single connection.
GET /agency/:agencyId/ws
Upgrade: websocket
Establishes a WebSocket connection to the agency for aggregated agent events.
Send a message to filter which agents' events you receive:
{ "type": "subscribe", "agentIds": ["agent-id-1", "agent-id-2"] }Remove the filter to receive events from all agents:
{ "type": "unsubscribe" }Events are relayed from agents with additional metadata:
{
"type": "assistant.message",
"agentId": "abc123",
"agentType": "assistant",
"content": "Hello!"
}All standard agent events include agentId and agentType fields when received through the agency WebSocket.
Events broadcast over WebSocket connections (both agent and agency):
| Event Type | Description |
|---|---|
thread.created |
New thread/conversation created |
request.accepted |
Request accepted (with idempotency key) |
run.started |
Agent run has started |
run.tick |
Run step completed |
run.paused |
Run paused (hitl, error, exhausted, subagent) |
run.resumed |
Run resumed after pause |
run.canceled |
Run was canceled |
agent.started |
Agent processing started |
agent.completed |
Agent completed successfully |
agent.error |
Agent encountered an error |
model.started |
LLM request started |
model.delta |
Streaming token received |
model.completed |
LLM request completed |
assistant.message |
Assistant message (content and/or tool calls) |
tool.started |
Tool execution started |
tool.output |
Tool returned output |
tool.error |
Tool execution failed |
Schedules allow agents to run automatically on a timer.
GET /agency/:agencyId/schedules
Response:
{
"schedules": [
{
"id": "sch_123",
"name": "daily-report",
"agentType": "reporter",
"type": "cron",
"cron": "0 9 * * *",
"enabled": true
}
]
}POST /agency/:agencyId/schedules
Content-Type: application/json
{
"name": "daily-report",
"agentType": "reporter",
"type": "cron",
"cron": "0 9 * * *",
"input": {
"messages": [{ "role": "user", "content": "Generate daily report" }]
}
}
Schedule types:
| Type | Fields | Description |
|---|---|---|
cron |
cron |
Standard cron expression |
interval |
intervalMs |
Milliseconds between runs |
once |
runAt |
ISO timestamp for one-time execution |
GET /agency/:agencyId/schedules/:scheduleId
PATCH /agency/:agencyId/schedules/:scheduleId
Content-Type: application/json
{ "cron": "0 10 * * *" }
DELETE /agency/:agencyId/schedules/:scheduleId
POST /agency/:agencyId/schedules/:scheduleId/pause
POST /agency/:agencyId/schedules/:scheduleId/resume
POST /agency/:agencyId/schedules/:scheduleId/trigger
GET /agency/:agencyId/schedules/:scheduleId/runs
Agency-level variables inherited by all agents.
GET /agency/:agencyId/vars
Response:
{
"vars": {
"LLM_API_KEY": "sk-...",
"MAX_ITERATIONS": 10
}
}PUT /agency/:agencyId/vars
Content-Type: application/json
{
"LLM_API_KEY": "sk-...",
"CUSTOM_VAR": "value"
}
GET /agency/:agencyId/vars/:varKey
PUT /agency/:agencyId/vars/:varKey
Content-Type: application/json
"value"
DELETE /agency/:agencyId/vars/:varKey
Connect to Model Context Protocol servers for additional tools.
GET /agency/:agencyId/mcp
Response:
{
"servers": [
{
"id": "mcp_123",
"name": "my-mcp",
"url": "https://mcp.example.com",
"status": "connected"
}
]
}POST /agency/:agencyId/mcp
Content-Type: application/json
{
"name": "my-mcp",
"url": "https://mcp.example.com",
"apiKey": "optional-key"
}
DELETE /agency/:agencyId/mcp/:serverId
POST /agency/:agencyId/mcp/:serverId/retry
GET /agency/:agencyId/mcp/tools
POST /agency/:agencyId/mcp/call
Content-Type: application/json
{
"serverId": "mcp_123",
"tool": "tool-name",
"args": { ... }
}
R2-backed filesystem for persistent file storage.
GET /agency/:agencyId/fs/:path
For directories, returns a listing. For files, returns the content.
PUT /agency/:agencyId/fs/:path
Content-Type: application/octet-stream
<file content>
DELETE /agency/:agencyId/fs/:path
| Prefix | Resolves To |
|---|---|
~/ |
Agent's private directory |
/shared/ |
Agency shared directory |
/agents/:id/ |
Specific agent's directory |
| (other) | Agency root |
GET /plugins
Response:
{
"plugins": [
{ "name": "vars", "tags": ["default"] },
{ "name": "hitl", "tags": ["hitl"], "varHints": [...] }
],
"tools": [
{ "name": "greet", "description": "Greet a user", "tags": ["default"] }
]
}All errors return JSON with an error message:
{ "error": "Description of the error" }Common status codes:
| Code | Description |
|---|---|
400 |
Bad request (invalid input) |
401 |
Unauthorized (missing/invalid secret) |
404 |
Resource not found |
409 |
Conflict (resource already exists) |
500 |
Internal server error |