> ## Documentation Index
> Fetch the complete documentation index at: https://www.bolna.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# AGENTS

# AGENTS.md — Bolna API guidance for AI coding assistants

This file encodes the non-obvious facts and gotchas that AI assistants need to generate correct Bolna API code. The Bolna OpenAPI spec is the authoritative reference; this file surfaces the things most likely to produce broken code if missed.

## Base URL and authentication

```
Base URL:  https://api.bolna.ai
Auth:      Authorization: Bearer <api_key>
```

All requests require the `Authorization` header. A missing or invalid key returns `401 Access denied`.

## Critical API facts (verified against the live API)

### 1. POST /v2/agent returns 201, not 200

```json theme={"system"}
// HTTP 201 Created
{ "agent_id": "123e4567-...", "state": "created" }
```

The response field is `state`, not `status`. The v1 `/agent` endpoint returns 200 — only v2 returns 201.

### 2. POST /batches returns 201, not 200

```json theme={"system"}
// HTTP 201 Created
{ "batch_id": "3c90c3cc0d444b5088888dd25736052a", "state": "created" }
```

Batch create uses `multipart/form-data` with fields `agent_id` (text) and `file` (CSV binary).

### 3. scheduled\_at MUST use +00:00, NOT Z

The `Z` UTC suffix is **rejected with a 500 error**. Always use a numeric offset:

```python theme={"system"}
# WRONG — returns 500
scheduled_at = "2026-06-23T18:30:00Z"

# CORRECT
from datetime import datetime, timezone, timedelta
when = (datetime.now(timezone.utc) + timedelta(minutes=3)).replace(microsecond=0).isoformat()
# produces: "2026-06-23T15:30:00+00:00"
```

Additional scheduling rules:

* Must be **at least 2 minutes in the future** — earlier returns 400
* Bolna **rounds up to the next 10-minute mark** (a 12:03 schedule runs at 12:10)

### 4. Wait for `completed`, not `call-disconnected`

`call-disconnected` fires the instant the line drops. At that moment `conversation_duration`, `total_cost`, `recording_url`, and `extracted_data` are still `null` or `0`. The `completed` event arrives a few seconds later with all fields populated.

```python theme={"system"}
TERMINAL = {"completed","no-answer","busy","failed","canceled","stopped","error","balance-low"}

while True:
    data = get_execution(exec_id)
    if data["status"] in TERMINAL:
        # safe to read duration, cost, recording, transcript
        break
    time.sleep(5)
```

### 5. toolchain.pipelines is array-of-arrays

```json theme={"system"}
// WRONG — array of strings
"pipelines": ["transcriber", "llm", "synthesizer"]

// CORRECT — array of arrays
"pipelines": [["transcriber", "llm", "synthesizer"]]
```

### 6. GET /batches/{batch_id}/executions returns a bare array

```json theme={"system"}
// NOT wrapped: { "data": [...] }
// Returns directly:
[
  { "id": "...", "status": "completed", ... },
  { "id": "...", "status": "no-answer", ... }
]
```

### 7. from\_phone\_number is optional on POST /call

Omitting `from_phone_number` uses the account's default number. The call succeeds without it.

### 8. Webhook source IP

Bolna sends webhooks from `13.203.39.153`. Whitelist this IP on your server/firewall or events will be dropped.

## Execution status lifecycle

```
queued → initiated → ringing → in-progress → call-disconnected → completed
                                                                 → no-answer
                                                                 → busy
                                                                 → failed
```

Terminal statuses (safe to stop polling): `completed`, `no-answer`, `busy`, `failed`, `canceled`, `stopped`, `error`, `balance-low`

## Batch status lifecycle

```
created → scheduled → running → completed
                              → stopped (manual)
                              → failed
```

## Minimal working agent body

```json theme={"system"}
{
  "agent_config": {
    "agent_name": "My Agent",
    "agent_welcome_message": "Hi, how can I help?",
    "tasks": [{
      "task_type": "conversation",
      "toolchain": {
        "execution": "sequential",
        "pipelines": [["transcriber", "llm", "synthesizer"]]
      },
      "tools_config": {
        "llm_agent": {
          "agent_type": "simple_llm_agent",
          "agent_flow_type": "streaming",
          "llm_config": { "provider": "openai", "model": "gpt-4.1-mini", "max_tokens": 150, "temperature": 0.2 }
        },
        "synthesizer": {
          "provider": "elevenlabs",
          "provider_config": { "voice": "Nila", "voice_id": "V9LCAAi4tTlqe9JadbCo", "model": "eleven_turbo_v2_5" },
          "stream": true, "buffer_size": 250, "audio_format": "wav"
        },
        "transcriber": { "provider": "deepgram", "model": "nova-3", "language": "en", "stream": true, "encoding": "linear16", "sampling_rate": 16000, "endpointing": 250 },
        "input": { "provider": "plivo", "format": "wav" },
        "output": { "provider": "plivo", "format": "wav" }
      },
      "task_config": { "call_terminate": 90, "hangup_after_silence": 10 }
    }]
  },
  "agent_prompts": {
    "task_1": { "system_prompt": "You are a helpful assistant. Keep replies short." }
  }
}
```

## Key endpoint summary

| Operation           | Method | Path                       | Returns                                                 |
| ------------------- | ------ | -------------------------- | ------------------------------------------------------- |
| Create agent        | POST   | `/v2/agent`                | 201 `{ agent_id, state: "created" }`                    |
| Make call           | POST   | `/call`                    | 200 `{ execution_id, status: "queued" }`                |
| Get execution       | GET    | `/executions/{id}`         | 200 execution object                                    |
| Create batch        | POST   | `/batches`                 | 201 `{ batch_id, state: "created" }`                    |
| Schedule batch      | POST   | `/batches/{id}/schedule`   | 200 `{ message: "success", state: "scheduled at ..." }` |
| Batch executions    | GET    | `/batches/{id}/executions` | 200 bare array                                          |
| List phone numbers  | GET    | `/phone-numbers/all`       | 200 array                                               |
| Link inbound number | POST   | `/inbound/setup`           | 200 `{ url, phone_number, id }`                         |
| User info           | GET    | `/user/me`                 | 200 `{ id, name, email, wallet, concurrency }`          |

## Further reading

* [API Quickstart](/quickstarts/api) — end-to-end outbound call in 4 steps
* [Batch Quickstart](/quickstarts/batch) — CSV campaign
* [Errors & Status Codes](/api-reference/errors) — full enum tables
* [Webhooks](/guides/post-call/polling-call-status-webhooks) — receive results without polling
