REST API Reference
Authenticate, make requests, and integrate Botonom into your existing stack using our REST API.
This guide begins with Authentication, continues through Conventions, Core endpoints, Webhooks, and finishes with Rate limits & errors.
Authentication
All API requests require a company API key (bk_live_...). Create one from your dashboard:
Dashboard → Developers → Credentials → Create Key
The full key is shown once at creation. Send it with every request:
curl "https://api.botonom.com/en/api/v1/agents/list/" \
-H "X-Botonom-Api-Key: bk_live_your_key_here"
Authorization: Bearer bk_live_... is also accepted. The key is scoped to your workspace; keys can be listed and revoked from the same page or via keys/list and keys/revoke.
Conventions
Base URL: https://api.botonom.com/en/api/v1
- Paths are
<resource>/<action>/- identifiers travel as query parameters or in the JSON body, never in the path. agent_idis always the agent's uuid (fromagents/list).- Every response uses the envelope
{status, code, title, data, msg}; failures add a machine-readableerror.codeand a real HTTP status. Idempotency-Keyheader (max 128 chars) is supported onagents/commandand every create endpoint: retrying the identical request replays the stored response instead of duplicating the action; the same key with a different payload returns409 IDEMPOTENCY_CONFLICT.
The full machine-readable contract is published as an OpenAPI 3.0 spec, and you can try every endpoint live in the API Playground.
Core endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | agents/list | List agents (public id = uuid) |
| GET | agents/get?agent_id= | Agent details |
| POST | agents/create | Hire an agent from a preset |
| POST | agents/command | Give an agent a natural-language command |
| GET | agents/command_status?run_id= | Poll a command run |
| GET | skills/list · POST skills/install | Skill catalog + install/remove |
| POST | contacts/create · calendar/event_create · tasks/create | Create records agents work with |
| GET | usage/tokens · billing/get | Usage and billing reads |
| POST | webhooks/create | Subscribe to signed events |
The flagship flow is agents/command: your system sends an instruction, the agent executes it asynchronously with its own persona, skills and permissions (with autonomy: full it acts - e.g. sends an email from its own address).
POST /v1/agents/command
{
"agent_id": "<agent-uuid>",
"instruction": "Send a short welcome email to ahmet@example.com and greet him by name.",
"data": { "name": "Ahmet" },
"autonomy": "full"
}
Response (202 Accepted):
{
"status": true,
"code": 202,
"data": {
"run": { "run_id": "cmd_654741d0...", "status": "queued", "autonomy": "full" }
},
"msg": "Command queued"
}
Poll agents/command_status?run_id=cmd_... until status is completed (the agent's reply arrives in result_text), or skip polling entirely by subscribing to the agent.command.completed webhook.
Getting the answer as data, not prose
result_text is written for a person. When your code is the reader, describe the shape you want with schema and the same run also returns output - an object with your field names in it.
POST /v1/agents/command
{
"agent_id": "<agent-uuid>",
"instruction": "Review this month's orders and summarise revenue.",
"schema": {
"type": "object",
"properties": {
"total_revenue": { "type": "number", "description": "Sum of quantity x price" },
"currency": { "type": "string", "enum": ["TRY", "USD"] },
"top_product": { "type": "string", "description": "Name of the highest-earning product" },
"products": {
"type": "array",
"description": "Revenue per product",
"items": { "type": "object", "properties": { "name": { "type": "string" }, "revenue": { "type": "number" } } }
}
},
"required": ["total_revenue", "top_product"]
}
}
command_status then returns both:
{
"run_id": "cmd_654741d0...",
"status": "completed",
"result_text": "This month's revenue is 27,400 TRY; Masa earned the most...",
"output": {
"total_revenue": 27400,
"currency": "TRY",
"top_product": "Masa",
"products": [{ "name": "Masa", "revenue": 10000 }]
},
"output_error": null
}
What you get without a schema
Leave schema out and output still arrives, in a standard envelope - so the endpoint is programmable by default:
| Field | Meaning |
|---|---|
result | ok / partial / needs_input / refused / failed |
summary | One sentence, in the language of your instruction |
follow_up | What the agent still needs from you, or null |
actions | Capabilities it actually used, e.g. ["orders.list", "code_interpreter.run_python"] |
files | Files it produced: [{ "name", "url", "type" }] |
actions and files are computed from the run itself, not written by the model, so they cannot drift from what really happened.
Rules worth knowing
- Supported types:
string,number,integer,boolean,array,object. Up to 50 properties, nested up to 5 levels. Anything else returns400 SCHEMA_INVALIDwith the exact path, e.g.properties.when: unsupported type "date". - Optional means nullable. A property you leave out of
requiredis always present inoutputand isnullwhen the run did not produce it, so you never have to tell an absent key from a null one. outputcan benull, and thenoutput_errorsays why. The prose answer is still delivered - a run is never failed because a shape could not be filled.- Describe your fields. The
descriptionon a property is what the agent reads. A field calledproduct_countwith no description may come back as units sold rather than distinct products; one sentence removes the ambiguity. datagoes in,outputcomes back. They are different halves of the same call:datais context you supply,outputis the answer in your shape.
Try it against your own agent in the API Playground: the request body ships with a schema you can edit, and the run comes back with output beside result_text.
Webhooks
Subscribe an https endpoint to events; the signing secret (whsec_...) is returned once:
POST /v1/webhooks/create
{
"url": "https://yourapp.com/botonom/webhook",
"events": ["agent.command.completed", "agent.command.failed"]
}
Deliveries carry X-Botonom-Event and X-Botonom-Signature: t=<unix>,v1=<hmac> headers. Failed deliveries retry automatically (2m / 10m / 30m / 2h, max 5 attempts); 20 consecutive failures auto-disable the subscription. See the Webhook Setup Guide for signature verification code.
Rate limits & errors
Common status codes and error codes:
| HTTP | error.code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | Missing or malformed input |
| 401 | API_KEY_REQUIRED / API_KEY_INVALID | Missing or bad key |
| 404 | AGENT_NOT_FOUND / RUN_NOT_FOUND / NOT_FOUND | Unknown resource |
| 405 | METHOD_NOT_ALLOWED | Wrong HTTP method |
| 409 | IDEMPOTENCY_CONFLICT | Same Idempotency-Key, different payload |
| 429 | RATE_LIMITED | Quota exceeded - honor Retry-After |
Business gates surface with their own codes, e.g. TOKEN_LIMIT_REACHED, PLAN_REQUIRED, SKILL_OAUTH_REQUIRED. Treat error.code as the stable contract; human-readable msg text may change.
See API Rate Limits for quota details.

