Documentation Index
Fetch the complete documentation index at: https://docs.agentbot.raveculture.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Agents API
Create, manage, and interact with agents.
All agent endpoints that require authentication are scoped to the authenticated user’s data through
row-level security. You can only access agents that belong to your account.
List agents
Returns all agents owned by the authenticated user. When no session is present, returns an empty list instead of a 401 error.
Managed runtime inclusion
When you have a managed OpenClaw runtime (identified by openclawInstanceId on your user record) but no corresponding agent row exists in the database, the endpoint automatically includes a synthetic agent entry in the response. This ensures that managed runtimes are always visible in the agents list, even before a skill installation or other action has materialized the database row.
The synthetic entry uses the following defaults:
| Field | Value |
|---|
name | Managed OpenClaw Runtime |
model | openclaw |
status | running |
createdAt | 1970-01-01T00:00:00.000Z (epoch) |
The synthetic agent is generated in-memory and is not persisted to the database. It appears only in the listing response. When a database row is later created for the same
openclawInstanceId (for example, during
skill installation), the synthetic entry is no longer needed and the persisted row is returned instead.
Response (backend)
The backend returns a flat array of agent objects:
[
{
"id": "agent_123",
"status": "active",
"created": "2026-03-01T00:00:00Z",
"subdomain": "agent_123.agents.localhost",
"url": "https://agent_123.agents.localhost"
}
]
| Field | Type | Description |
|---|
[].id | string | Agent identifier |
[].status | string | Current agent status |
[].created | string | ISO 8601 creation timestamp |
[].subdomain | string | Agent subdomain |
[].url | string | Agent URL |
Response (web proxy)
The web proxy wraps the response in an object:
{
"agents": [
{
"id": "agent_123",
"userId": "user_456",
"name": "My Agent",
"model": "claude-opus-4-6",
"status": "running",
"websocketUrl": "ws://openclaw-gateway:10000/agent/user_123",
"createdAt": "2026-03-01T00:00:00Z",
"updatedAt": "2026-03-01T12:00:00Z"
}
],
"count": 1,
"status": "ok"
}
| Field | Type | Description |
|---|
agents | array | List of agent objects owned by the authenticated user |
agents[].id | string | Agent identifier |
agents[].userId | string | Owner user identifier |
agents[].name | string | Agent name |
agents[].model | string | AI model assigned to the agent |
agents[].status | string | Current agent status |
agents[].websocketUrl | string | null | WebSocket URL for the agent gateway |
agents[].createdAt | string | ISO 8601 creation timestamp |
agents[].updatedAt | string | ISO 8601 last update timestamp |
count | number | Total number of agents returned |
status | string | Response status (ok) |
The backend and web proxy return different response shapes. The backend returns a flat array with created, subdomain, and url fields. The web proxy wraps the data in an agents key and includes name, model, websocketUrl, createdAt, and updatedAt fields.
Create agent
Creates a new agent with an auto-generated ID and stores its metadata on disk. Requires bearer token authentication (backend).
Request body
| Field | Type | Required | Description |
|---|
name | string | Yes | Agent name |
config | object | No | Agent configuration |
config.plan | string | No | Plan tier (for example, solo, collective, label, network). Defaults to free when omitted. Note that free is not a standard plan tier and may not be recognized by other endpoints. |
config.aiProvider | string | No | AI provider (for example, openrouter, anthropic) |
config.stripeSubscriptionId | string | Conditional | Stripe subscription ID. Required unless the caller is an admin. When missing for non-admin callers, the endpoint returns 402. |
Response (201 Created)
{
"id": "agent_123",
"name": "My Agent",
"agentId": "agent_123",
"status": "pending",
"subdomain": "agent_123.agents.localhost",
"url": "https://agent_123.agents.localhost",
"createdAt": "2026-03-19T00:00:00Z"
}
Errors
| Code | Description |
|---|
| 400 | Name required |
| 401 | Unauthorized |
| 402 | Active subscription required. Returned when config.stripeSubscriptionId is missing and the caller is not an admin. Response includes code: "PAYMENT_REQUIRED". |
| 500 | Failed to create agent |
Get agent
Requires authentication and ownership of the agent.
Response (backend)
The backend returns the agent object directly without a wrapper:
{
"id": "agent_123",
"status": "active",
"startedAt": "2026-03-01T00:00:00Z",
"plan": "solo",
"subdomain": "agent_123.agents.localhost",
"url": "https://agent_123.agents.localhost",
"openclawVersion": "2026.4.11",
"verified": false,
"verificationType": null,
"attestationUid": null,
"verifiedAt": null
}
Response (web proxy)
The web proxy wraps the agent in an object:
{
"agent": {
"id": "agent_123",
"status": "active",
"startedAt": "2026-03-01T00:00:00Z",
"plan": "solo",
"subdomain": "agent_123.agents.localhost",
"url": "https://agent_123.agents.localhost",
"openclawVersion": "2026.4.11",
"verified": false,
"verificationType": null,
"attestationUid": null,
"verifiedAt": null
}
}
The backend returns the agent object directly. The web proxy wraps it in an agent key and adds a top-level status field. Sensitive fields (config.authToken and top-level authToken) are stripped from the response before it is returned to the client.
Errors
| Code | Description |
|---|
| 401 | Unauthorized |
| 403 | Forbidden — you do not own this agent. Returned when the agent has an ownerEmail set and it does not match the authenticated user’s email. Admins bypass this check. |
| 404 | Agent not found — no container or metadata exists for this agent ID |
| 500 | Failed to fetch agent |
Update agent
Updates an agent’s metadata including plan, AI provider, and configuration. Requires bearer token authentication (backend).
Request body
| Field | Type | Required | Description |
|---|
plan | string | No | Plan tier |
aiProvider | string | No | AI provider |
config | object | No | Additional configuration |
Response
{
"id": "agent_123",
"plan": "collective",
"aiProvider": "anthropic",
"subdomain": "agent_123.agents.localhost",
"status": "active",
"message": "Agent updated"
}
Errors
| Code | Description |
|---|
| 401 | Unauthorized |
| 403 | Forbidden — you do not own this agent |
| 404 | Agent not found |
| 500 | Failed to update agent |
Rename agent (web proxy)
Renames an agent. Requires session authentication and ownership. The name change is persisted locally and forwarded to the backend on a best-effort basis (backend failures are non-fatal).
Request body
| Field | Type | Required | Description |
|---|
name | string | Yes | New agent name (trimmed, max 64 characters) |
Response
{
"success": true,
"agent": {
"id": "agent_123",
"name": "New Name",
"status": "running",
"updatedAt": "2026-04-12T00:00:00Z"
}
}
Errors
| Code | Description |
|---|
| 400 | Name is required |
| 400 | Name too long (max 64 chars) |
| 401 | Unauthorized |
| 404 | Agent not found |
| 500 | Failed to rename agent |
Delete agent
Stops and removes the agent’s local Docker container, deallocates its port, and removes its metadata file. Requires bearer token authentication (backend).
Response (backend)
{
"id": "agent_123",
"deleted": true
}
Response (web proxy)
The web proxy also accepts DELETE /api/agents/:id with session authentication. It performs best-effort cleanup of the associated managed runtime service, deletes the agent record from the database (cascading to memories, files, and related data), and clears the user’s openclawInstanceId and openclawUrl fields. The agent is resolved by ownership check or by matching the user’s openclawInstanceId.
{
"success": true,
"deleted": "agent_123"
}
Errors
| Code | Description |
|---|
| 401 | Unauthorized |
| 403 | Forbidden — you do not own this agent |
| 404 | Agent not found |
| 500 | Failed to delete agent |
The backend uses best-effort cleanup (service destruction and metadata removal). If the agent metadata does not exist, the endpoint returns 404 before attempting cleanup. Container stop and removal failures are silently ignored.
Provision agent
POST /api/agents/provision
Provisions a new agent. Requires an active subscription unless the caller is an admin.
The agent is created immediately with a provisioning status and transitions to running once the backend deployment endpoint confirms the deployment. If deployment fails, the status changes to error. After a successful deployment, the provisioning endpoint also syncs the agent’s skills, memories, and files to the OpenClaw gateway. If the gateway is unreachable, the agent is set to pending_gateway_sync instead of failing outright — the sync can be retried later.
The provisioning endpoint calls POST /api/deployments on the backend to deploy the agent as a Render service. The request includes a 15-second timeout. When the model is set to claude-opus-4-6, the AI provider is automatically set to anthropic; otherwise it falls back to the provider specified in the agent configuration (default: openrouter). The plan sent to the backend defaults to label when no tier is specified.
The provisioning endpoint connects to the OpenClaw gateway using the
OPENCLAW_GATEWAY_URL environment variable. When this variable is not set, the endpoint falls back to the default internal gateway address. You can configure this variable in your environment to point to a custom gateway deployment. See
environment variables for details.
Request body
| Field | Type | Required | Description |
|---|
name | string | Yes | Agent name |
model | string | No | AI model (default: claude-opus-4-6). Options: claude-opus-4-6, gpt-4, custom |
config | object | No | Agent configuration |
tier | string | No | Subscription tier hint. Options: starter, pro, enterprise. This value is forwarded to the backend as the plan field (mapped to solo, collective, label, network). When omitted, the backend deployment defaults to label. |
The web proxy enforces agent limits based on the subscription tier (starter: 1, pro: 3, enterprise: 100). The backend provisioning route (POST /api/provision) enforces its own limits (solo: 1, collective: 3, label: 10, network: 999999). The plan middleware enforces a separate set of agent limits for AI model access (solo: 1, collective: 3, label: 10, network: 100). The provisioning limits and middleware limits apply independently. The limit cannot be overridden in the request body.
The backend also accepts legacy plan aliases for resource allocation: underground (2 GB / 1 CPU), starter (2 GB / 1 CPU), pro (4 GB / 2 CPU), scale (8 GB / 4 CPU), enterprise (16 GB / 4 CPU), and white_glove (32 GB / 8 CPU). These are accepted in addition to the standard plan names (solo, collective, label, network) when determining container resource limits.
Admin bypass
Admin users (configured via ADMIN_EMAILS) are exempt from the following restrictions:
- Subscription requirement — admins can provision agents without an active subscription (the
402 error is not returned).
- Agent limit — admins receive an elevated agent slot limit instead of the plan-based cap.
Admin status is determined by checking the session email against ADMIN_EMAILS. This endpoint does not accept an email field in the request body — only the authenticated session email is used for the admin check. See admin check in the POST /api/provision section for the legacy endpoint’s resolution order, which also supports a body email fallback when no session is present.
Backend payment enforcement is active. All paid plans require a valid Stripe subscription ID unless the caller is an admin (configured via ADMIN_EMAILS).
Response (201 Created)
{
"success": true,
"agent": {
"id": "agent_789",
"name": "My Agent",
"status": "running",
"websocketUrl": "ws://openclaw-gateway:10000/agent/user_123",
"model": "claude-opus-4-6",
"createdAt": "2026-03-19T00:00:00Z"
}
}
| Field | Type | Description |
|---|
agent.status | string | Agent status after provisioning. Possible values: running (fully deployed and gateway-synced), pending_gateway_sync (deployed but the gateway was unreachable during provisioning — skills, memories, and files have not been synced yet), or error (deployment failed). |
Errors
| Code | Description |
|---|
| 400 | Agent name is required |
| 401 | Unauthorized |
| 402 | Active subscription required to provision agents |
| 429 | Agent limit reached for your plan (web proxy). Response includes current (agent count) and limit fields. Limits: starter 1, pro 3, enterprise 100. Users without a recognized plan default to a limit of 1. The backend returns 402 with code AGENT_LIMIT_REACHED for the same condition. |
| 500 | Failed to provision agent |
GET /api/agents/simulator
Calculates token economics for agent sponsorship. Supports two modes: forward (set liquidity tokens to calculate market cap) and reverse (set desired market cap to calculate required liquidity tokens). No authentication required.
Query parameters
| Parameter | Type | Required | Description |
|---|
totalSupply | number | Yes | Total token supply (must be positive) |
liquidityTokens | number | No | Number of tokens allocated to liquidity. Triggers forward mode when provided. |
desiredMarketCapUsd | number | No | Target market cap in USD. Triggers reverse mode when provided and liquidityTokens is not set. |
sponsorshipAmount | number | No | SELFCLAW sponsorship amount (default: 1000) |
Forward mode example
GET /api/agents/simulator?totalSupply=1000000&liquidityTokens=100000
Reverse mode example
GET /api/agents/simulator?totalSupply=1000000&desiredMarketCapUsd=5000
Response
{
"mode": "forward",
"input": {
"totalSupply": 1000000,
"liquidityTokens": 100000,
"liquidityPercent": "10.0%",
"sponsorshipAmount": 1000
},
"valuation": {
"initialPrice": 0.01,
"marketCap": 10000,
"interpretation": "By providing 100,000 tokens, you're valuing at $10,000 market cap"
},
"formula": {
"initialPrice": "sponsorshipAmount / liquidityTokens",
"marketCap": "initialPrice * totalSupply",
"reverse": "liquidityTokens = (sponsorshipAmount * totalSupply) / desiredMarketCap",
"keyInsight": "Fewer tokens in liquidity = higher price = higher market cap (but thinner trading)"
},
"alternativeScenarios": [
{
"label": "High valuation (10%)",
"liquidityTokens": 100000,
"initialPrice": 0.01,
"marketCap": 10000,
"liquidityPercent": 10
}
],
"guidance": {
"liquidityRange": "10-40% of supply is typical",
"supplyRange": "1M-100M tokens is common",
"tradeoff": "Higher market cap = thinner liquidity. Lower = deeper, more stable."
}
}
| Field | Type | Description |
|---|
mode | string | forward or reverse |
input | object | Echoed input parameters with computed liquidity percentage |
valuation.initialPrice | number | Calculated initial price per token |
valuation.marketCap | number | Calculated market capitalization in USD |
valuation.interpretation | string | Human-readable explanation of the result |
formula | object | Formulas used for each calculation |
alternativeScenarios | array | Pre-computed scenarios at 10%, 25%, and 50% liquidity |
guidance | object | Recommended ranges and trade-off explanations |
Errors
| Code | Description |
|---|
| 400 | totalSupply is required (positive number) — missing or non-positive totalSupply |
| 400 | Provide either liquidityTokens (forward) or desiredMarketCapUsd (reverse) — neither liquidity mode parameter was provided |
| 400 | liquidityTokens cannot exceed totalSupply |
| 500 | Internal server error |
Agent definitions
Manage agent definitions stored as markdown files with YAML frontmatter. These endpoints are backend-only and require bearer token authentication.
List definitions
GET /api/agents/definitions
Returns all available agent definitions loaded from system, user, and project directories.
Query parameters
| Parameter | Type | Required | Description |
|---|
projectDir | string | No | Override the project directory to scan for definitions |
Response
{
"agents": [
{
"name": "researcher",
"description": "Deep research agent for web analysis",
"model": "openrouter/anthropic/claude-3.5-sonnet",
"tools": ["bash", "read", "write", "web"],
"scope": "system",
"source": "/path/to/definitions/researcher.md"
}
],
"total": 1
}
| Field | Type | Description |
|---|
agents | array | List of agent definition metadata objects |
agents[].name | string | Unique agent name (derived from the filename) |
agents[].description | string | Human-readable description |
agents[].model | string | AI model identifier |
agents[].tools | string[] | Available tool names |
agents[].scope | string | Where the definition was loaded from: system, user, or project |
agents[].source | string | File path of the definition |
total | number | Total number of definitions |
Get definition
GET /api/agents/definitions/:name
Returns a single agent definition by name, including the full instruction body.
Path parameters
| Parameter | Type | Description |
|---|
name | string | Agent definition name |
Response
Returns the full AgentDefinition object:
{
"name": "researcher",
"description": "Deep research agent for web analysis",
"model": "openrouter/anthropic/claude-3.5-sonnet",
"tools": ["bash", "read", "write", "web"],
"permissions": {
"bash": "dangerous",
"read": "safe",
"write": "dangerous"
},
"instruction": "# Researcher Agent\n\nYou are a deep research agent...",
"source": "/path/to/definitions/researcher.md",
"scope": "system"
}
| Field | Type | Description |
|---|
permissions | object | Per-tool permission levels: safe (auto-approve), dangerous (require approval), or destructive |
instruction | string | The markdown body of the definition file (the agent’s system prompt) |
Errors
| Code | Description |
|---|
| 404 | Agent definition not found |
| 500 | Failed to load definition |
Validate definition
POST /api/agents/definitions
Validates and previews an agent definition without saving it. Accepts raw markdown with YAML frontmatter.
Request body
| Field | Type | Required | Description |
|---|
content | string | Yes | Markdown content with YAML frontmatter |
---
name: researcher
description: Deep research agent for web analysis
model: openrouter/anthropic/claude-3.5-sonnet
tools: [bash, read, write, web]
permissions:
bash: dangerous
read: safe
write: dangerous
---
# Researcher Agent
You are a deep research agent...
Response
{
"valid": true,
"definition": {
"name": "researcher",
"description": "Deep research agent for web analysis",
"model": "openrouter/anthropic/claude-3.5-sonnet",
"tools": ["bash", "read", "write", "web"],
"scope": "project",
"source": "/tmp/agent-def-1234567890.md"
},
"full": {
"name": "researcher",
"description": "Deep research agent for web analysis",
"model": "openrouter/anthropic/claude-3.5-sonnet",
"tools": ["bash", "read", "write", "web"],
"permissions": { "bash": "dangerous", "read": "safe", "write": "dangerous" },
"instruction": "# Researcher Agent\n\nYou are a deep research agent...",
"source": "/tmp/agent-def-1234567890.md",
"scope": "project"
}
}
| Field | Type | Description |
|---|
valid | boolean | Whether the definition parsed successfully |
definition | object | Lightweight metadata for the parsed definition |
full | object | Complete AgentDefinition object including instruction body and permissions |
Errors
| Code | Description |
|---|
| 400 | Missing content (markdown with YAML frontmatter) — the content field was not provided |
| 400 | Invalid agent definition format — the content could not be parsed as a valid agent definition |
| 500 | Validation failed |
Clone agent
Not yet available. Agent cloning is under development and this endpoint currently returns 501 Not Implemented. No payment is processed. The request body is ignored.
Response (501 Not Implemented)
{
"error": "Agent cloning is not yet available",
"message": "This feature is under development. No payment has been charged.",
"status": "unavailable"
}
All POST requests to this endpoint return 501 regardless of the request body. No payment flow is initiated.
Errors
| Code | Description |
|---|
| 501 | Agent cloning is not yet available |
Clone service health
Returns the clone service status and protocol configuration. No authentication required.
{
"service": "agentbot-clone",
"version": "0.1.0",
"protocol": "x402-tempo",
"clonePrice": "1.0 pathUSD",
"chainId": 4217
}
| Field | Type | Description |
|---|
service | string | Service identifier |
version | string | Clone service version |
protocol | string | Payment protocol used |
clonePrice | string | Current price to clone an agent |
chainId | number | Blockchain chain ID for payments |
List provisioned agents
GET /api/agents/provision
Requires session authentication.
Response
{
"success": true,
"agents": [
{
"id": "agent_789",
"name": "My Agent",
"model": "claude-opus-4-6",
"status": "running",
"websocketUrl": "ws://openclaw-gateway:10000/agent/user_123",
"createdAt": "2026-03-19T00:00:00Z",
"updatedAt": "2026-03-19T00:00:00Z"
}
],
"count": 1
}
Errors
| Code | Description |
|---|
| 401 | Unauthorized |
| 500 | Failed to list agents |
Get agent configuration
GET /api/agents/:id/config
Returns the current configuration for an agent. Requires authentication and ownership.
Response
{
"config": {},
"status": "ok"
}
Errors
| Code | Description |
|---|
| 401 | Unauthorized |
| 404 | Agent not found or agent configuration not found |
| 500 | Failed to fetch agent configuration |
Update agent configuration
PUT /api/agents/:id/config
Updates the configuration for an agent. Requires authentication and ownership. The request body is forwarded to the backend.
Response
{
"config": {},
"status": "updated"
}
Errors
| Code | Description |
|---|
| 401 | Unauthorized |
| 404 | Agent not found |
| 500 | Failed to update agent configuration |
Get agent logs
Returns logs for an agent. Requires authentication and ownership.
This endpoint currently returns mock data. Log entries are generated placeholders, not real agent logs. For real-time logs, use the
live log stream endpoint instead.
Query parameters
| Parameter | Type | Description |
|---|
limit | number | Maximum log entries to return (default: 50, max: 100) |
level | string | Filter by log level (for example, info, error, warn) |
Response
{
"logs": [
{
"id": "log_1",
"timestamp": "2026-03-19T00:00:00Z",
"level": "info",
"message": "Agent activity log entry 1",
"source": "agent",
"agentId": "agent_123"
}
],
"total": 50,
"limit": 50,
"status": "ok"
}
Errors
| Code | Description |
|---|
| 401 | Unauthorized |
| 404 | Agent not found |
| 500 | Failed to fetch logs |
Stream agent logs
The live log streaming endpoints (
/api/logs/:agentId/stream,
/api/logs/:agentId/history,
POST /api/logs/:agentId/stop, and
GET /api/logs/active) are planned for a future release. See the
live log tail page for the intended specification.
Get agent messages
GET /api/agents/:id/messages
Returns paginated messages for an agent. Requires authentication and ownership.
Query parameters
| Parameter | Type | Description |
|---|
limit | number | Maximum messages to return (default: 50, max: 100) |
offset | number | Offset for pagination (default: 0) |
Response
{
"messages": [
{
"id": "msg_1",
"agentId": "agent_123",
"sender": "user",
"content": "Hello",
"timestamp": "2026-03-19T00:00:00Z",
"platform": "telegram"
}
],
"total": 0,
"limit": 50,
"offset": 0,
"status": "ok"
}
This endpoint currently returns mock data. Message entries are generated placeholders. A future release will connect this endpoint to the backend message store.
Errors
| Code | Description |
|---|
| 401 | Unauthorized |
| 404 | Agent not found |
| 500 | Failed to fetch messages |
Get agent stats
GET /api/agents/:id/stats
Returns live service metrics when available. When the backend is unavailable, the endpoint returns a 502 error with null metric fields instead of fabricated data.
Response (live)
{
"stats": {
"agentId": "agent_123",
"cpu": "0.15%",
"memory": "128MiB / 2GiB",
"memoryPercent": "6.25%",
"network": "1.2kB / 3.4kB",
"uptime": 86400000,
"uptimeFormatted": "1d 0h",
"status": "running",
"pids": "12",
"messagesProcessed": "N/A",
"messagesPerHour": "N/A",
"averageResponseTime": "N/A",
"successRate": "N/A",
"errorRate": "N/A"
},
"status": "ok"
}
Response (degraded — 502)
When the backend is unavailable, the endpoint returns 502 with "status": "degraded" and all metric fields set to null:
{
"error": "Agent stats temporarily unavailable",
"stats": {
"agentId": "agent_123",
"cpu": null,
"memory": null,
"memoryPercent": null,
"network": null,
"uptime": null,
"uptimeFormatted": null,
"status": "degraded",
"pids": null,
"messagesProcessed": null,
"messagesPerHour": null,
"averageResponseTime": null,
"successRate": null,
"errorRate": null
},
"status": "degraded"
}
This endpoint no longer returns mock data when the backend is unavailable. Previous versions returned fabricated metrics with "status": "mock". The endpoint now returns 502 with null fields so callers can distinguish between real metrics and a backend outage.
## Agent status values
Agent status is reported across multiple endpoints. The following table lists all possible status values:
| Status | Source | Description |
|--------|--------|-------------|
| `running` | `GET /api/instance/:userId`, `GET .../stats` | Agent is fully operational (both health and readiness checks pass) |
| `starting` | `GET /api/instance/:userId`, `POST .../start` | Agent is booting (health check passes, readiness check not yet ready) |
| `stopped` | `POST .../stop` | Agent container is stopped |
| `restarting` | `POST .../restart` (web proxy) | Agent container is restarting |
| `reset` | `POST .../reset-memory` (web proxy) | Agent memory was wiped and container is restarting |
| `repaired` | `POST .../repair` (web proxy) | Agent environment was rebuilt and container was restarted |
| `updating` | `POST .../update` (web proxy) | Agent image update is in progress |
| `unknown` | `GET /api/instance/:userId` | Neither health nor readiness checks returned a successful response |
| `unreachable` | `GET .../stats` | Gateway health check failed |
| `active` | Backend lifecycle endpoints | Container is running (backend Docker status) |
| `provisioning` | Provisioning flow | Agent is being created |
| `error` | Various | Operation or deployment failed |
<Note>The web proxy and backend may return different status strings for the same action. For example, the start action returns `"starting"` from the web proxy but `"active"` from the backend. See each endpoint's documentation for the exact response shape.</Note>
## Agent lifecycle
Lifecycle operations are available at two endpoint patterns depending on which service you call:
- **Web proxy:** `/api/instance/:userId/{action}` — requires session authentication and proxies to the backend.
- **Backend direct:** `/api/agents/:id/{action}` — requires API key authentication.
Both patterns support the same actions. The examples below show both response shapes where they differ.
<Note>The backend agents route uses local Docker commands (`docker start`, `docker stop`, `docker restart`) for lifecycle operations, not the Render API. The Render API is used by the provisioning route (`POST /api/provision`) for creating new agent services. When Docker is unavailable on the backend host, lifecycle operations return `500` with an error message. You can check availability using the [backend health endpoint](/api-reference/health#backend-health-check) — when the `docker` field is `unavailable`, lifecycle operations will fail.</Note>
### Start agent
```http
POST /api/agents/:id/start
Starts a stopped agent container using docker start.
Backend direct:
{
"success": true,
"status": "active"
}
Web proxy (POST /api/instance/:userId/start):
{
"success": true,
"status": "starting"
}
Errors
| Code | Source | Description |
|---|
| 401 | Both | Unauthorized — missing or invalid authentication |
| 403 | Both | Forbidden — you do not own this agent |
| 404 | Backend | Agent not found |
| 500 | Both | Start failed |
| 503 | Web proxy | Managed runtime controls are disabled, or service/config resolution failed |
Stop agent
POST /api/agents/:id/stop
Stops the agent container using docker stop. The container retains its data and configuration and can be resumed with the start endpoint.
Backend direct:
{
"success": true,
"status": "stopped"
}
Web proxy (POST /api/instance/:userId/stop):
{
"success": true,
"status": "stopped"
}
Errors
| Code | Source | Description |
|---|
| 401 | Both | Unauthorized — missing or invalid authentication |
| 403 | Both | Forbidden — you do not own this agent |
| 404 | Backend | Agent not found |
| 500 | Both | Stop failed |
| 503 | Web proxy | Managed runtime controls are disabled, or service/config resolution failed |
Restart agent
POST /api/agents/:id/restart
Backend direct:
{
"success": true,
"status": "active",
"healedLegacyModel": false,
"healMessage": "skip",
"openclawVersion": "2026.4.11"
}
| Field | Type | Description |
|---|
healedLegacyModel | boolean | Whether a deprecated model was automatically migrated during restart |
healMessage | string | Description of the migration performed (skip when no migration was needed, or skip:container-not-running when the container is unavailable) |
openclawVersion | string | Current OpenClaw runtime version |
Web proxy (POST /api/instance/:userId/restart):
{
"success": true,
"status": "restarting"
}
Errors
| Code | Source | Description |
|---|
| 401 | Both | Unauthorized — missing or invalid authentication |
| 403 | Both | Forbidden — you do not own this agent |
| 404 | Backend | Agent not found |
| 500 | Both | Restart failed |
| 503 | Web proxy | Managed runtime controls are disabled, or service/config resolution failed |
Update agent image
POST /api/agents/:id/update
Triggers an image update on the backend. Before replacing the service, the endpoint creates a backup of the agent’s data. If the new image fails to start, the endpoint automatically rolls back to the previous image.
Request body
| Field | Type | Required | Description |
|---|
image | string | No | Custom image to deploy. When omitted, the platform default image is used. |
Response
Backend direct:
{
"success": true,
"status": "active",
"image": "ghcr.io/openclaw/openclaw:2026.4.11",
"previousImage": "ghcr.io/openclaw/openclaw:2026.3.24",
"backupPath": "/opt/agentbot/data/backups/openclaw-updates/agent_123/20260320-000000.tar.gz",
"openclawVersion": "2026.4.11"
}
| Field | Type | Description |
|---|
image | string | New service image. The platform default is ghcr.io/openclaw/openclaw:2026.4.11. |
previousImage | string | Previous service image before the update |
backupPath | string | null | Path to the pre-update backup archive |
openclawVersion | string | Current OpenClaw runtime version |
When the new service fails to start, the endpoint reverts to previousImage. The caller still receives a 500 error, but the agent is restored to its prior working state. The pre-update backup remains available at backupPath for manual recovery if needed.
Web proxy (POST /api/instance/:userId/update):
{
"success": true,
"status": "updating",
"image": "ghcr.io/openclaw/openclaw:2026.4.11",
"openclawVersion": "2026.4.11"
}
The web proxy update response does not include previousImage or backupPath. On failure, the web proxy returns { "success": false, "status": "error" } — this differs from other web proxy lifecycle endpoints which use { "success": false, "error": "..." }.
Errors
| Code | Source | Description |
|---|
| 400 | Backend | Invalid docker image value |
| 401 | Both | Unauthorized — missing or invalid authentication |
| 403 | Both | Forbidden — you do not own this agent |
| 404 | Backend | Agent not found |
| 500 | Both | Update failed. When automatic rollback succeeds on the backend, the agent continues running on the previous image. |
| 503 | Web proxy | Managed runtime controls are disabled, or service/config resolution failed |
Repair agent
POST /api/agents/:id/repair
Repairs an agent by reconfiguring its environment and restarting the service.
Backend direct — stops the container, heals legacy model configuration, removes the container, and recreates it with the same image.
{
"success": true,
"message": "Agent repaired successfully"
}
Web proxy (POST /api/instance/:userId/repair) — reconfigures the agent’s environment variables on the managed runtime and restarts the service. The endpoint injects the user’s unique gateway token retrieved from the agent_registrations table rather than a shared platform token. If no per-user token exists in the database, a new UUID is generated and used instead.
{
"success": true,
"status": "repaired"
}
The web proxy repair endpoint always uses the authenticated user’s own gateway token from the database. This ensures each agent authenticates with a token unique to its owner.
Errors
| Code | Source | Description |
|---|
| 401 | Both | Unauthorized — missing or invalid authentication |
| 403 | Both | Forbidden — you do not own this agent |
| 404 | Backend | Agent not found |
| 500 | Both | Repair failed |
| 503 | Web proxy | Managed runtime controls are disabled, or service/config resolution failed |
Reset agent memory
POST /api/agents/:id/reset-memory
Backend direct:
{
"success": true,
"message": "Memory reset successfully"
}
Web proxy (POST /api/instance/:userId/reset-memory) — deletes all stored agent memory rows and restarts the container:
{
"success": true,
"status": "reset"
}
Errors
| Code | Source | Description |
|---|
| 401 | Both | Unauthorized — missing or invalid authentication |
| 403 | Both | Forbidden — you do not own this agent |
| 404 | Backend | Agent not found |
| 500 | Both | Reset failed |
| 503 | Web proxy | Managed runtime controls are disabled, or service/config resolution failed |
Lifecycle error responses
Backend lifecycle endpoints return the following shape on failure:
{
"error": "Error message describing the failure"
}
Web proxy lifecycle endpoints return a different error shape. Most endpoints use:
{
"success": false,
"error": "Error message describing the failure"
}
The update endpoint is the exception and returns:
{
"success": false,
"status": "error"
}
All web proxy lifecycle endpoints return 503 when managed runtime controls are disabled or when the platform cannot resolve the agent’s service configuration:
{
"success": false,
"error": "Managed runtime controls are temporarily disabled until the Railway control path is fully verified."
}
When service or configuration resolution fails, the error message describes the specific issue:
{
"success": false,
"error": "RAILWAY_ENVIRONMENT_ID not configured"
}
| Code | Source | Description |
|---|
| 401 | Both | Unauthorized — missing or invalid authentication |
| 403 | Both | Forbidden — authenticated user does not own this agent. The backend returns { "error": "Forbidden — you do not own this agent" }. The web proxy returns { "success": false, "error": "..." }. |
| 404 | Backend | Agent not found — no metadata exists for this agent ID |
| 500 | Both | The lifecycle action failed. On the backend, this means the Docker command or Railway mutation failed. On the web proxy, this means the Railway API call succeeded in resolving the service but the mutation itself failed. |
| 502 | Web proxy | Backend service unavailable |
| 503 | Web proxy | Managed runtime controls are disabled, or the platform could not resolve the agent’s managed service. This applies to all lifecycle actions: start, stop, restart, update, repair, and reset-memory. See 503 error details for the full list of possible error messages. |
Get instance details
GET /api/instance/:userId
Returns the current status and metadata for an agent instance.
Response
{
"userId": "user_123",
"status": "running",
"startedAt": "2026-03-01T00:00:00Z",
"subdomain": "user_123.agents.localhost",
"url": "https://user_123.agents.localhost",
"plan": "solo",
"openclawVersion": "2026.4.11",
"ffmpegAvailable": true,
"ffmpegVersion": "6.1.1"
}
| Field | Type | Description |
|---|
ffmpegAvailable | boolean | Whether ffmpeg is installed in the agent’s managed runtime. Required for autonomous baseFM DJ broadcasting. |
ffmpegVersion | string | null | The ffmpeg version string, or null when ffmpeg is not available |
Get instance stats
GET /api/instance/:userId/stats
Returns resource usage statistics for an agent instance.
Response
{
"userId": "user_123",
"status": "running",
"health": "healthy",
"cpu": "0%",
"memory": "0MB",
"uptime": "active",
"messages": null,
"errors": null,
"openclawVersion": "2026.4.11"
}
| Field | Type | Description |
|---|
userId | string | Agent user identifier |
status | string | Instance status (running or unreachable) |
health | string | Health check result (healthy or unreachable) |
cpu | string | CPU usage (currently returns a placeholder value) |
memory | string | Memory usage (currently returns a placeholder value) |
uptime | string | Uptime status (active or unknown) |
messages | number | null | Message count (reserved for future use) |
errors | number | null | Error count (reserved for future use) |
openclawVersion | string | Current OpenClaw runtime version. Absent when the instance is unreachable. |
When the instance is unreachable, the response uses placeholder values:
{
"userId": "user_123",
"status": "unreachable",
"health": "unreachable",
"cpu": "0%",
"memory": "0MB",
"uptime": "unknown",
"messages": null,
"errors": null
}
The openclawVersion field is omitted when the instance is unreachable. The cpu and memory fields currently return placeholder values and will report actual resource metrics in a future release.
Sync agent to gateway
POST /api/agents/:id/sync
Syncs agent skills, memories, and files to the OpenClaw gateway. Requires session authentication and ownership of the agent. The endpoint verifies that the authenticated user owns the agent by checking the database before proceeding with the sync.
Use this endpoint to retry a failed deployment, or to bring the live OpenClaw runtime back in line with the saved install records when an earlier skill install or uninstall returned "deployed": false. A skill is only treated as active in the runtime once a sync succeeds — saved install records alone do not imply the runtime accepted the skill.
Response
When the gateway accepts the sync:
{
"success": true,
"agentId": "agent_456",
"gatewayId": "agent_456",
"deployedAt": "2026-04-29T12:34:56.000Z",
"details": {
"skillsDeployed": 3,
"memoriesDeployed": 0,
"filesDeployed": 0
}
}
| Field | Type | Description |
|---|
success | boolean | Always true on a successful sync. |
agentId | string | ID of the agent that was synced. |
gatewayId | string | undefined | Identifier returned by the gateway for the synced agent. Falls back to agentId when the gateway does not return one. |
deployedAt | string | undefined | ISO 8601 timestamp recorded when the gateway accepted the sync. |
details | object | undefined | Counts of resources sent to the gateway during this sync. |
details.skillsDeployed | number | Number of installed skills sent to the runtime. |
details.memoriesDeployed | number | Number of memories sent to the runtime. |
details.filesDeployed | number | Number of files sent to the runtime. |
The gateway client validates the sync response body. A 2xx status with "success": false (or a body the client otherwise rejects) is treated as a failed sync and surfaces as a 500 from this endpoint, rather than being reported as a successful deploy.
Errors
When the sync fails, the response includes the underlying gateway error in details:
{
"error": "Sync failed",
"details": "Gateway error: 503",
"agentId": "agent_456"
}
| Code | Description |
|---|
| 401 | Unauthorized — no valid session |
| 404 | Agent not found — the agent does not exist or does not belong to the authenticated user |
| 500 | Sync failed. The response body includes a details string with the gateway error and the agentId that failed to sync. |
This endpoint enforces strict ownership. The agent must belong to the authenticated user’s account. Requests for agents owned by other users return 404 to prevent information leakage about agent IDs.
Get agent gateway token
GET /api/agents/:id/token
Returns the gateway token for the agent. If no token exists, a new cryptographically random token is generated using 32 bytes of entropy (returned as a 64-character hex string). Tokens generated by the service entrypoint (when the OPENCLAW_GATEWAY_TOKEN environment variable is not set) also use 32 bytes (64 hex characters).
{
"token": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
}
Errors
| Code | Description |
|---|
| 403 | Forbidden — you do not own this agent |
| 404 | Agent not found |
| 500 | Failed to get token |
Agent verification
Agents can be verified using multiple verification types: eas (Ethereum Attestation Service), coinbase, ens, or webauthn.
Managed runtime fallback
All verification endpoints support a managed runtime fallback. When you have a managed OpenClaw runtime but no corresponding agent row exists in the database, the endpoint automatically resolves the agent by matching your openclawInstanceId and upserts a synthetic agent record. This ensures verification works for managed runtimes even before a full agent record has been provisioned.
When the backend verification service returns 404 (for example, because the backend does not yet have metadata for the agent), the web proxy falls back to reading and writing verification state in the agent’s local config.verification field in the database. This means verification state is persisted and readable locally for managed runtimes, even when the backend has no record of the agent.
Get verification status
GET /api/agents/:id/verification
The backend GET endpoint uses /api/agents/:id/verification while POST and DELETE use /api/agents/:id/verify. The web API proxies all three methods through /api/agents/:id/verify.
Response
When the backend is available, the response is proxied as-is:
{
"verified": false,
"verificationType": null,
"attestationUid": null,
"verifierAddress": null,
"verifiedAt": null,
"metadata": null
}
When the backend returns 404, the endpoint falls back to the locally persisted verification state:
{
"verified": false,
"verificationType": null,
"attestationUid": null,
"verifierAddress": null,
"verifiedAt": null
}
The fallback response does not include the metadata field. All fields default to null (or false for verified) when no local verification state has been written.
Errors
| Code | Description |
|---|
| 401 | Unauthorized — no valid session |
| 404 | Agent not found — the agent does not exist and no managed runtime matches the ID |
| 500 | Failed to fetch verification status |
Verify agent
POST /api/agents/:id/verify
Request body
Requires Content-Type: application/json header.
| Field | Type | Required | Description |
|---|
verificationType | string | Yes | One of: eas, coinbase, ens, webauthn |
attestationUid | string | Conditional | Required for eas verification |
walletAddress | string | Conditional | Required for ens verification. Optional for eas and coinbase. |
signature | string | Conditional | Required for coinbase, ens, and webauthn verification |
Each verification type has specific field requirements:
| Type | Required fields |
|---|
eas | attestationUid |
coinbase | signature |
ens | signature, walletAddress |
webauthn | signature (used as the attestation) |
The web API always sets verified: true on success. When calling the backend directly, you can pass verified, verifierAddress, and metadata explicitly.
Response
{
"success": true,
"verified": true,
"verificationType": "eas",
"attestationUid": "0x123...",
"verifiedAt": "2026-03-19T00:00:00Z"
}
When the backend returns 404, the verification state is persisted locally and the same success response shape is returned.
Errors
| Code | Description |
|---|
| 400 | Invalid verification type — the verificationType value is not one of the supported types |
| 400 | Missing required field for the specified verification type (for example, Attestation UID required for EAS verification, Signature required for Coinbase verification, Signature and wallet address required for ENS verification, Attestation required for WebAuthn verification) |
| 401 | Unauthorized — no valid session |
| 404 | Agent not found — the agent does not exist and no managed runtime matches the ID |
| 500 | Failed to process verification |
Remove verification
DELETE /api/agents/:id/verify
When the backend returns 404, the endpoint clears the local verification state instead.
Errors
| Code | Description |
|---|
| 401 | Unauthorized — no valid session |
| 404 | Agent not found — the agent does not exist and no managed runtime matches the ID |
| 500 | Failed to remove verification |
Provision with channel tokens
Provisions a new agent with messaging channel tokens. Requires session authentication. When a session is active, the user’s email is automatically resolved from it.
When autoProvision is true or agentType is business, channel tokens are not required and the agent is provisioned as an OpenClaw-only deployment. Otherwise, at least one channel token (Telegram, WhatsApp, or Discord) is required.
Admin check
Admin status is determined by resolving a single email and checking it against the configured ADMIN_EMAILS:
- Session email — if an authenticated session exists, the session email is used.
- Body email fallback — if no session email is available (for example, the session is missing or the session user has no email), the
email field in the request body is used instead.
The first available email is checked against ADMIN_EMAILS. If it matches, the caller is treated as an admin. When the session is missing entirely (for example, after a Stripe checkout redirect loses the session cookie) and the body email matches an admin, a synthetic session is created and the request proceeds without requiring a real session. Non-admin users without a valid session receive a 401 error.
Only one email is checked — the session email takes priority. If the session email exists but is not an admin, the body email is not checked as a secondary fallback. This differs from previous behavior where both emails were checked independently.
This endpoint is subject to the general rate limit of 120 requests per minute per IP.
The request is proxied to the backend provisioning service. When MUX_TOKEN_ID and MUX_TOKEN_SECRET are configured, the backend creates a real Mux live stream via the Mux API with public playback policy. When Mux credentials are not configured, placeholder streaming credentials are returned instead.
Request body
| Field | Type | Required | Description |
|---|
telegramToken | string | Conditional | Telegram bot token. At least one channel token is required unless autoProvision is true or agentType is business. |
telegramUserId | string | No | Telegram user ID for owner binding |
whatsappToken | string | Conditional | WhatsApp API token. At least one channel token is required unless autoProvision is true or agentType is business. |
discordBotToken | string | Conditional | Discord bot token. At least one channel token is required unless autoProvision is true or agentType is business. |
aiProvider | string | No | AI provider (default: openrouter). Options: openrouter, gemini, groq, anthropic, openai. Each provider maps to a default model — see AI provider defaults below. |
plan | string | No | Plan tier. Options: solo, collective, label, network. Defaults to free when omitted. Since free is not a valid plan, omitting this field returns a 400 validation error — you must explicitly specify a paid plan when calling the backend directly. |
model | string | No | AI model identifier. When omitted, the default model for the selected aiProvider is used (see AI provider defaults). |
skills | string[] | No | List of skill identifiers to enable on the agent. |
agentType | string | No | Agent type. When set to business, the agent is provisioned as an OpenClaw-only deployment and channel tokens are not required. |
autoProvision | boolean | No | When true, the agent is provisioned as an OpenClaw-only deployment and channel tokens are not required. The onboard flow sets this automatically in deploy mode. |
email | string | No | User email address. When omitted, the email is automatically populated from the authenticated session. When a session email is available, it takes priority and the body email is not used for the admin check. This field is only checked against ADMIN_EMAILS when no session email is present (see admin check above). The resolved email is forwarded to the backend in the X-User-Email header. |
stripeSubscriptionId | string | No | Stripe subscription ID from checkout. This field is accepted by the backend provisioning service directly. The web proxy does not forward this field — it performs its own subscription check against the database instead. |
When a session is active, the server resolves the user email from the session via getServerSession. The session email is used for the admin check. When no session email is available, the email field from the request body is checked against ADMIN_EMAILS — if it matches, a synthetic session is created and the request proceeds. The resolved email is sent to the backend provisioning service in the X-User-Email header.
The following request fields are deprecated and no longer accepted: whatsappPhoneNumberId, whatsappBusinessAccountId, discordGuildId, discordChannelId.
Response
The proxy returns a filtered subset of the backend response:
{
"success": true,
"userId": "a1b2c3d4e5",
"subdomain": "dj-a1b2c3d4e5.agentbot.raveculture.xyz",
"url": "https://dj-a1b2c3d4e5.agentbot.raveculture.xyz",
"streamKey": "sk-ab12-cd34-ef56",
"liveStreamId": "x7k9m2p4q1"
}
The /api/provision proxy returns only success, userId, subdomain, url, streamKey, and liveStreamId. The full response shape from the backend provisioning service is shown below.
When provisioning with
autoProvision: true or
agentType: "business" (OpenClaw-only deployment), the proxy also persists the
openclawUrl and
openclawInstanceId to the user record. You can retrieve these values later using
GET /api/user/openclaw.
Full backend response
When calling the backend provisioning service directly, the response includes additional fields. The backend returns 200 OK on success (not 201 Created).
Channel tokens (telegramToken, discordBotToken, whatsappToken) are no longer included in the provision response. Tokens are write-only secrets — they are stored server-side but never returned to the caller.
{
"success": true,
"userId": "a1b2c3d4e5",
"agentId": "a1b2c3d4e5",
"id": "a1b2c3d4e5",
"aiProvider": "openrouter",
"aiProviderConfig": {
"model": "openai/gpt-4o-mini",
"baseUrl": "https://openrouter.ai/api/v1",
"requiresKey": true
},
"plan": "solo",
"streamKey": "sk-ab12-cd34-ef56",
"liveStreamId": "x7k9m2p4q1",
"rtmpServer": "rtmps://live.mux.com/app",
"playbackUrl": "https://image.mux.com/x7k9m2p4q1/playlist.m3u8",
"subdomain": "dj-a1b2c3d4e5.agentbot.raveculture.xyz",
"url": "https://dj-a1b2c3d4e5.agentbot.raveculture.xyz",
"hls": {
"playlistUrl": "https://image.mux.com/x7k9m2p4q1/playlist.m3u8"
},
"rtmp": {
"server": "rtmps://live.mux.com/app",
"key": "sk-ab12-cd34-ef56"
},
"status": "active",
"createdAt": "2026-03-20T00:00:00Z",
"metadata": {
"channels": {
"telegram": "enabled",
"discord": "disabled",
"whatsapp": "disabled"
},
"streaming": {
"provider": "mux",
"lowLatency": true,
"resolution": "1920x1080",
"bitrate": "5000k"
}
},
"container": {
"name": "agentbot-agent-a1b2c3d4e5",
"status": "running",
"serviceId": "srv-abc123def456",
"renderUrl": "https://agentbot-agent-a1b2c3d4e5.up.railway.app",
"controlUiUrl": "https://openclaw-production.up.railway.app/chat?session=agent%3Amain%3Amain#token=abc123&gatewayUrl=wss%3A%2F%2Fagentbot-agent-a1b2c3d4e5.up.railway.app"
}
}
The
container object is included when the backend successfully creates a container for the agent. If container creation fails, provisioning still succeeds and the
container field is omitted. The agent can operate using API-side processing until the container becomes available. You can check backend availability using the
backend health endpoint.
| Field | Type | Description |
|---|
container.name | string | Container name (format: agentbot-agent-{userId}) |
container.status | string | Container status (for example, running, stopped) |
container.serviceId | string | Railway service ID for the agent container. |
container.renderUrl | string | Public runtime URL for the agent container (for example, https://agentbot-agent-{userId}.up.railway.app). The field name is legacy; the runtime is now provisioned on Railway. |
container.controlUiUrl | string | Auto-connect URL for the OpenClaw Control UI. Includes the gateway token in the URL fragment (never sent to the server) and the WebSocket gateway URL. When no gateway token is available, the token and gateway URL fragments are omitted. |
container.port | number | null | Deprecated. Previously held the container’s local listening port. Removed in favor of container.renderUrl, which points to the public runtime URL. |
container.gatewayUrl | string | Deprecated. Previously held the local gateway URL. Replaced by container.renderUrl, which points to the public runtime URL. |
Errors
| Code | Description |
|---|
| 400 | At least one channel token required (Telegram, WhatsApp, or Discord) when autoProvision is not true and agentType is not business, or invalid aiProvider value |
| 400 | Invalid plan value (backend only). The plan field must be one of solo, collective, label, or network. The web proxy defaults to label when forwarding to the backend, so this error is only returned when calling the backend directly with an unrecognized value. Since the plan field defaults to free when omitted, and free is not a valid plan, callers must always specify an explicit paid plan. The error message is: Invalid plan. Supported: solo, collective, label, network. |
| 401 | Authentication required. Returned when no session is present and the resolved email (session email or body email) does not match a configured admin email (or no email is available). |
| 403 | Active subscription required (web proxy). Returned when the authenticated user does not have an active subscription and is not an admin. The response body includes success: false and an error message: Active subscription required. Please purchase a plan to deploy. |
| 402 | No free tier available. Returned when plan is free (the default when omitted). The response body includes code: "PAYMENT_REQUIRED" and the message "No free tier. Choose a paid plan to get started." You must explicitly specify a paid plan (solo, collective, label, or network). |
| 402 | Active subscription required (backend). Returned when a valid paid plan is specified but no Stripe subscription ID is provided and the caller is not an admin. The response body includes a code field set to PAYMENT_REQUIRED and the message "Active subscription required. Subscribe at /pricing". |
| 402 | Agent limit reached for your plan. The response body includes a code field set to AGENT_LIMIT_REACHED, along with current (current agent count) and limit (maximum allowed) fields. Provisioning limits: solo 1, collective 3, label 10, network unlimited. |
| 500 | Internal server error |
| 502 | Provisioning service unavailable or returned an error. All backend URLs failed or returned non-success responses. |
| 503 | Provisioning is temporarily disabled (kill switch active) or provisioning service misconfigured. |
AI provider defaults
Each aiProvider value maps to a default model and base URL. There are two model configurations: the container config (used by the agent’s internal gateway) and the provision response metadata (aiProviderConfig field). These may differ.
Container config models
These models are configured inside the agent service at provisioning time and are used by the gateway’s model fallback chain:
| Provider | Primary model | Fallback model | Base URL |
|---|
openrouter | openrouter/xiaomi/mimo-v2-pro | openrouter/anthropic/claude-sonnet-4, openrouter/google/gemini-2.5-flash | https://openrouter.ai/api/v1 |
gemini (or google) | google/gemini-2.0-flash | openrouter/anthropic/claude-sonnet-4-5 | https://generativelanguage.googleapis.com/v1beta/models |
groq | groq/gemma2-9b-it | openai/gpt-4o-mini | https://api.groq.com/openai/v1 |
anthropic | anthropic/claude-sonnet-4-5 | openai/gpt-4o | https://api.anthropic.com/v1 |
openai | openai/gpt-4o | openai/gpt-4o-mini | https://api.openai.com/v1 |
minimax | MiniMax/MiniMax-Text-01 | openai/gpt-4o-mini | https://api.minimax.chat/v1 |
The aiProviderConfig object returned in the provision response uses different default models:
| Provider | Default model | Base URL |
|---|
openrouter | openai/gpt-4o-mini | https://openrouter.ai/api/v1 |
gemini | gemini-2.0-flash | https://generativelanguage.googleapis.com/v1beta/models |
groq | mixtral-8x7b-32768 | https://api.groq.com/openai/v1 |
anthropic | claude-3-sonnet-20240229 | https://api.anthropic.com/v1 |
openai | gpt-4o | https://api.openai.com/v1 |
minimax | MiniMax/MiniMax-Text-01 | https://api.minimax.chat/v1 |
Each provider includes a fallback model in the service config that is used automatically when the primary model is unavailable or returns an error.
minimax is available as a fallback in the provider configuration map but is not currently accepted as a value for the aiProvider request parameter. Passing minimax as aiProvider returns a 400 validation error. This provider may be enabled in a future release.
Channel configuration
When an agent is provisioned, its channel configuration is generated based on the tokens provided. All channels share a set of defaults and each channel type has specific settings.
Channel defaults
| Setting | Value | Description |
|---|
groupPolicy | allowlist | Only explicitly allowed users can interact with the agent in group contexts |
heartbeat.showOk | false | Suppress heartbeat OK messages |
heartbeat.showAlerts | true | Show heartbeat alert messages |
heartbeat.useIndicator | true | Display a status indicator |
Telegram channel settings
| Setting | Value | Description |
|---|
dmPolicy | allowlist or pairing | allowlist when owner IDs are provided, pairing otherwise |
groups.*.requireMention | true | Agent only responds in groups when mentioned |
historyLimit | 50 | Number of messages retained in context |
replyToMode | first | Reply threading mode |
streaming | partial | Enable partial message streaming |
retry.attempts | 3 | Maximum retry attempts |
retry.minDelayMs | 400 | Minimum delay between retries |
retry.maxDelayMs | 30000 | Maximum delay between retries |
retry.jitter | 0.1 | Jitter factor for retry delays |
Discord channel settings
| Setting | Value | Description |
|---|
dmPolicy | allowlist or pairing | allowlist when owner IDs are provided, pairing otherwise |
dm.enabled | true | Accept direct messages |
dm.groupEnabled | false | Group DMs are disabled |
historyLimit | 20 | Number of messages retained in context |
streaming | partial | Enable partial message streaming |
retry.attempts | 3 | Maximum retry attempts |
retry.minDelayMs | 500 | Minimum delay between retries |
retry.maxDelayMs | 30000 | Maximum delay between retries |
retry.jitter | 0.1 | Jitter factor for retry delays |
WhatsApp channel settings
| Setting | Value | Description |
|---|
dmPolicy | allowlist or pairing | allowlist when owner IDs are provided, pairing otherwise |
groups.*.requireMention | true | Agent only responds in groups when mentioned |
sendReadReceipts | true | Send read receipts for incoming messages |
Group chat mention patterns
All channels that support group chat use the following default mention patterns: @agent and agent. The agent only responds in group conversations when one of these patterns is detected in the message.
Each agent is assigned a tool profile at provisioning time based on its plan tier. The tool profile determines which built-in tools the agent can use.
| Plan | Tool profile | Description |
|---|
solo | messaging | Chat-only tools suitable for messaging workflows |
collective | coding | Full development tools including code execution |
label | coding | Full development tools including code execution |
network | coding | Full development tools including code execution |
The tool profile is set once at service creation and persists for the lifetime of the agent. Upgrading your plan does not automatically change the tool profile of existing agents — you need to reprovision the agent or use the
repair endpoint to apply the new profile.
All tool profiles deny browser and canvas tools inside agent services. The coding profile includes shell commands (ls, cat, grep, curl, git, node, python3, and others) while the messaging profile restricts the agent to chat-oriented capabilities.
Deploy agent (backend)
This is a backend-only endpoint. It deploys an agent as a Render web service and requires a Content-Type: application/json header. Requires bearer token authentication. Rate limited to 5 requests per minute per IP.
Request body
| Field | Type | Required | Description |
|---|
agentId | string | Yes | Unique agent identifier |
config | object | No | Deployment configuration |
config.telegramToken | string | Yes | Telegram bot token |
config.ownerIds | string[] | No | Telegram owner user IDs |
config.aiProvider | string | No | AI provider (default: openrouter) |
config.apiKey | string | No | API key for the AI provider |
config.plan | string | No | Plan tier. Options: label, solo, collective, network. When omitted, defaults to free which resolves to starter resource limits (2 GB memory, 1 CPU). |
Response (201 Created)
{
"id": "deploy-agent_123",
"agentId": "agent_123",
"subdomain": "agent_123.agents.localhost",
"url": "https://agent_123.agents.localhost",
"status": "active",
"openclawVersion": "2026.4.11"
}
Response (200 Already Active)
If the agent service is already running, returns the existing deployment details with the same shape as the 201 response.
Errors
| Code | Description |
|---|
| 400 | agentId is required, Invalid agentId, or telegramToken is required |
| 401 | Unauthorized |
| 500 | Deployment failed. When the Render API is not reachable, the error message indicates that provisioning is unavailable. Check the backend health endpoint to verify Render API availability before deploying. |
Provision on Railway (backend)
POST /api/railway/provision
Provisions a new agent service on Railway via the Railway GraphQL API. This endpoint creates a Railway service, configures environment variables, mounts a persistent volume, generates a public domain, and triggers a deployment. Requires bearer token authentication.
This endpoint exists because direct calls from Vercel serverless functions to the Railway GraphQL API return 403. The backend runs on Railway, so its outbound requests to the Railway API succeed. The endpoint requires RAILWAY_API_KEY, RAILWAY_PROJECT_ID, and RAILWAY_ENVIRONMENT_ID environment variables to be configured. Set RAILWAY_TOKEN_TYPE to control how the platform authenticates with the Railway API — project sends the key via the Project-Access-Token header, while account (default), workspace, and oauth send it as a Bearer token in the Authorization header.
Request body
| Field | Type | Required | Description |
|---|
agentId | string | Yes | Agent identifier. Must be a 16-character lowercase hex string. |
plan | string | No | Plan tier for resource allocation. Defaults to solo. Options: underground, solo, collective, label, network. |
Plan resource limits
Each plan tier maps to specific Railway service resource limits:
| Plan | Memory | CPU |
|---|
underground | 2048 MB | 1 |
solo | 2048 MB | 1 |
collective | 4096 MB | 2 |
label | 8192 MB | 4 |
network | 16384 MB | 4 |
Response
{
"success": true,
"agentId": "a1b2c3d4e5f6g7h8",
"url": "https://agentbot-agent-a1b2c3d4e5f6g7h8.up.railway.app",
"serviceId": "srv-abc123def456",
"status": "deploying"
}
| Field | Type | Description |
|---|
success | boolean | Whether provisioning succeeded |
agentId | string | Agent identifier |
url | string | Public URL for the provisioned agent service |
serviceId | string | Railway service identifier |
status | string | Always deploying on success. The service may take a few minutes to become available. |
The provisioned service is configured with a persistent volume mounted at /data, a health check on /health with a 60-second timeout, and an ON_FAILURE restart policy with up to 10 retries. Environment variable injection retries once on failure with a 2-second delay.
Errors
| Code | Description |
|---|
| 400 | agentId required — the agentId field is missing or not a string |
| 400 | Invalid agentId format — the agentId does not match the expected 16-character hex format |
| 401 | Unauthorized — missing or invalid bearer token |
| 502 | Railway provisioning failed. The error message contains details from the Railway API response. |
| 503 | Railway not configured on this backend — the RAILWAY_API_KEY environment variable is not set. Also ensure RAILWAY_TOKEN_TYPE is set correctly if you are using a project-scoped token. |
Idempotency
If a Railway service with the same name already exists (for example, after a partial failure), the endpoint looks up the existing service ID and continues with environment variable injection and deployment. This makes the endpoint safe to retry.
OpenClaw version (backend)
GET /api/openclaw/version
Returns the current OpenClaw runtime version. Requires bearer token authentication.
Response
{
"openclawVersion": "2026.4.11",
"image": "ghcr.io/openclaw/openclaw:2026.4.11",
"deployedAt": "2026-03-20T00:00:00Z"
}
deployedAt returns the current server time when the request is made, not the actual deployment time of the OpenClaw runtime.
List instances (backend)
GET /api/openclaw/instances
Returns all running agent services. Requires bearer token authentication.
Response
{
"instances": [
{
"agentId": "agent_123",
"name": "openclaw-agent_123",
"image": "ghcr.io/openclaw/openclaw:2026.4.11",
"status": "Up 2 hours",
"createdAt": "2026-03-20 00:00:00 +0000 UTC",
"version": "2026.4.11",
"metadata": {
"agentId": "agent_123",
"createdAt": "2026-03-20T00:00:00Z",
"plan": "solo"
}
}
],
"count": 1
}
The metadata object contains the full agent metadata from the on-disk JSON file and may include additional fields beyond those shown (for example, aiProvider, port, subdomain, url, status, and config).
Errors
| Code | Description |
|---|
| 401 | Unauthorized |
| 500 | Failed to list instances |
Get instance service stats (backend)
GET /api/openclaw/instances/:id/stats
Returns resource usage for a specific agent service. Requires bearer token authentication.
Path parameters
| Parameter | Type | Description |
|---|
id | string | Agent ID |
Response
{
"agentId": "agent_123",
"cpu": "12.5%",
"memory": "100MiB / 1GiB",
"memoryPercent": "10.0%",
"network": "1.2kB / 3.4kB",
"blockIO": "0B / 0B",
"pids": "12",
"status": "running",
"uptime": 86400000,
"uptimeFormatted": "1d 0h",
"timestamp": "2026-03-20T00:00:00Z"
}
Get user OpenClaw instance
Returns the authenticated user’s OpenClaw URL and instance ID. These values are set during provisioning when autoProvision is true or agentType is business. Requires session authentication.
Response
{
"openclawUrl": "https://dj-a1b2c3d4e5.agentbot.raveculture.xyz",
"openclawInstanceId": "inst_a1b2c3d4e5"
}
| Field | Type | Description |
|---|
openclawUrl | string | null | The OpenClaw dashboard URL for this user. null when no OpenClaw instance has been provisioned. |
openclawInstanceId | string | null | The OpenClaw instance identifier. null when no OpenClaw instance has been provisioned. |
Agent interaction
GET /api/agent
POST /api/agent
Unified endpoint for interacting with agents. All requests require session authentication. The userId is always bound to the authenticated session and cannot be overridden by the client.
GET actions
Pass the action query parameter to select the operation.
List endpoints
Returns available endpoints and version information when no action is specified.
{
"apiVersion": "1.0.0",
"agentbotVersion": "2026.3.1",
"endpoints": {
"GET /api/agent": "List endpoints",
"GET /api/agent?action=health": "Health status",
"GET /api/agent?action=sessions": "List sessions",
"GET /api/agent?action=session&sessionId=xxx": "Get session details",
"GET /api/agent?action=memory": "Get agent memory",
"GET /api/agent?action=skills": "List available skills",
"GET /api/agent?action=credentials": "List configured credentials",
"POST /api/agent": "Send message to agent",
"POST /api/agent?action=create-session": "Create new session",
"POST /api/agent?action=update-skill": "Enable/disable skill"
}
}
Health
GET /api/agent?action=health
{
"status": "running",
"version": "2026.3.1",
"apiVersion": "1.0.0",
"uptime": 86400,
"model": "claude-sonnet-4-20250514",
"channels": ["telegram"],
"skills": [],
"lastSeen": 1710806400000
}
List sessions
GET /api/agent?action=sessions
{
"sessions": [
{
"id": "sess_abc123",
"status": "active",
"messageCount": 5,
"createdAt": 1710806400000,
"lastActivity": 1710810000000
}
]
}
Get session
GET /api/agent?action=session&sessionId=sess_abc123
Returns the full session including messages.
Memory
GET /api/agent?action=memory
Returns the last 10 messages from the active session (truncated to 100 characters each).
{
"memory": [
{ "role": "user", "content": "Hello, can you help me with..." },
{ "role": "assistant", "content": "Of course! Let me..." }
]
}
Skills
GET /api/agent?action=skills
Returns skills available on the agent instance.
Credentials
GET /api/agent?action=credentials
Returns which credentials are configured for the agent.
{
"credentials": {
"anthropic": false,
"openai": false,
"openrouter": true,
"google": false,
"telegram": true,
"discord": false,
"whatsapp": false
}
}
POST actions
Pass the action field in the request body.
Chat
| Field | Type | Required | Description |
|---|
action | string | No | Set to chat or omit (default action) |
message | string | Yes | Message to send to the agent |
sessionId | string | No | Session ID to continue. A new session is created if omitted and no active session exists. |
{
"sessionId": "sess_abc123",
"reply": "Agent is processing your request...",
"timestamp": 1710810000000
}
Create session
| Field | Type | Required | Description |
|---|
action | string | Yes | create-session |
{
"sessionId": "sess_abc123",
"status": "active"
}
Update skill
| Field | Type | Required | Description |
|---|
action | string | Yes | update-skill |
skillId | string | Yes | Skill ID to enable or disable |
enabled | boolean | No | Whether to enable or disable the skill. Defaults to false (removes the skill) when omitted. |
{
"success": true,
"skillId": "browser",
"enabled": true
}
Set credential
| Field | Type | Required | Description |
|---|
action | string | Yes | set-credential |
key | string | Yes | Credential key (for example, anthropic, telegram) |
value | string | No | Credential value. When omitted, the credential is marked as unconfigured. |
{
"success": true,
"key": "anthropic",
"configured": true
}
Errors
| Code | Description |
|---|
| 400 | Invalid action or missing required fields |
| 401 | Unauthorized |
| 404 | Session not found |
| 500 | Internal error |
Send message
Sends a message to your deployed agent. The message is queued for processing via the platform job system and the response is returned asynchronously. Requires session authentication.
Request body
| Field | Type | Required | Description |
|---|
message | string | Yes | Message to send to the agent |
Response (202 Accepted)
The endpoint enqueues the message as a background job and returns immediately with a job reference. Poll the job status using the returned jobId.
{
"queued": true,
"jobId": "job_abc123",
"status": "queued"
}
| Field | Type | Description |
|---|
queued | boolean | Always true when the message was accepted |
jobId | string | Unique identifier for the queued chat job |
status | string | Current job status (typically queued on creation) |
Errors
| Code | Description |
|---|
| 400 | Message required |
| 401 | Unauthorized |
| 404 | User not found, or no agent deployed for the authenticated user |
| 429 | Rate limited — too many requests or no workload slot available. May include a retryAfterSeconds field in the response body. |
| 500 | Failed to send message |
| 502 | Failed to queue the chat job on the backend |
| 503 | Gateway not configured for the user’s agent |
List messages
Returns the message history. Requires session authentication.
Response
{
"messages": [],
"count": 0
}
Errors
| Code | Description |
|---|
| 401 | Unauthorized |