Skip to main content
bidiDocs Dashboard

Start

Getting started

Your key, the base URL and seven calls from listing your agents to reading this month's usage, then the rules every call follows.

Authentication

Create a key in Settings > Developers (the Team plan and up) and send it as a bearer token. A key belongs to your workspace, carries only the scopes you gave it and can be revoked at any moment. bidi keeps a hash of it, which is why the dashboard shows it once.

Base URL

URL
https://www.bidii.ai/api/v2

Header

HTTP
Authorization: Bearer bidi_sk_YOUR_KEY

Create a key in your dashboard

Quickstart

Seven calls, in the order you would make them. Replace the key and the ids with your own; every example is complete.

1. List your agents

curl
curl https://www.bidii.ai/api/v2/agents \
  -H "Authorization: Bearer bidi_sk_YOUR_KEY"
200
{
  "data": [
    {
      "id": "6f1c0b2e-6b0f-4b7e-9d5a-1b2c3d4e5f60",
      "name": "Al Noor Dental",
      "status": "live",
      "language": "bilingual",
      "public_key": "pk_live_ab12cd34",
      "allowed_origins": ["https://alnoordental.ae"],
      "conversations_per_month": 1500,
      "created_at": "2026-09-14T08:12:30+00:00",
      "updated_at": "2026-09-30T17:40:02+00:00"
    }
  ],
  "next_cursor": null
}

2. Teach an agent something

curl
curl -X POST https://www.bidii.ai/api/v2/agents/AGENT_ID/sources \
  -H "Authorization: Bearer bidi_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: opening-hours-2026-10" \
  -d '{
    "type": "text",
    "name": "Opening hours",
    "text": "Open Saturday to Thursday, 9am to 6pm. Closed on Fridays. Emergencies: +971 4 000 0000."
  }'

Indexing runs in the background: the answer is 202 with the source at status uploaded. Poll it until indexed. The Idempotency-Key makes a retry safe.

3. Ask it a question

curl
curl -X POST https://www.bidii.ai/api/v2/agents/AGENT_ID/chat \
  -H "Authorization: Bearer bidi_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages": [{"role": "user", "content": "Are you open on Friday?"}]}'
200
{
  "conversation_id": "0c9e7d2a-1f4b-4a63-8e2d-7b5f6a1c2d3e",
  "message": {"id": "f3a1...", "role": "assistant", "content": "We are closed on Fridays. Saturday to Thursday we are open 9am to 6pm."},
  "outcome": "answered",
  "refused": false,
  "sources": [{"file_id": "...", "filename": "Opening hours"}],
  "state": "ai_active",
  "lead_captured": false,
  "limit": null,
  "degraded": false
}

4. Stream the answer

curl
curl -N -X POST https://www.bidii.ai/api/v2/agents/AGENT_ID/chat \
  -H "Authorization: Bearer bidi_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "stream": true,
    "conversation_id": "0c9e7d2a-1f4b-4a63-8e2d-7b5f6a1c2d3e",
    "messages": [{"role": "user", "content": "And on Saturday?"}]
  }'
text/event-stream
event: delta
data: {"text": "On Saturday we are open "}

event: delta
data: {"text": "from 9am to 6pm."}

event: sources
data: {"sources": [{"file_id": "...", "filename": "Opening hours"}]}

event: outcome
data: {"outcome": "answered", "state": "ai_active", "refused": false, "limit": null}

event: done
data: {"conversation_id": "0c9e7d2a-...", "message": {"id": "...", "role": "assistant", "content": "On Saturday we are open from 9am to 6pm."}, "outcome": "answered", "refused": false, "sources": [...], "state": "ai_active", "lead_captured": false, "limit": null, "degraded": false}

Streaming sends the same events the website widget receives: delta pieces of text, then sources, outcome and done. Anything that can refuse the call answers before the stream starts, as a normal HTTP error.

5. Page through conversations

curl
curl "https://www.bidii.ai/api/v2/agents/AGENT_ID/conversations?limit=20" \
  -H "Authorization: Bearer bidi_sk_YOUR_KEY"

curl "https://www.bidii.ai/api/v2/agents/AGENT_ID/conversations?limit=20&cursor=NEXT_CURSOR" \
  -H "Authorization: Bearer bidi_sk_YOUR_KEY"

curl https://www.bidii.ai/api/v2/conversations/CONVERSATION_ID/messages \
  -H "Authorization: Bearer bidi_sk_YOUR_KEY"

6. Read your leads

curl
curl "https://www.bidii.ai/api/v2/leads?status=new" \
  -H "Authorization: Bearer bidi_sk_YOUR_KEY"

curl -X PATCH https://www.bidii.ai/api/v2/leads/LEAD_ID \
  -H "Authorization: Bearer bidi_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "qualified"}'

7. Check this month's usage

curl
curl https://www.bidii.ai/api/v2/usage \
  -H "Authorization: Bearer bidi_sk_YOUR_KEY"

The contract

Pagination

Lists take limit (1 to 100) and cursor and answer data plus next_cursor; null means you reached the end. Cursors are opaque: pass them back unchanged.

Errors

Every error is one shape: an error object with a code, a message and a request_id. Codes are stable snake_case strings to switch on; messages are for people, in English or, with Accept-Language: ar, in Arabic. Quote the request id when you write to us.

Rate limits

Sixty requests per key per minute, twenty of them chat. Over the limit you get 429 with Retry-After; X-RateLimit-Remaining says how many calls are left in the minute.

Idempotency

Send an Idempotency-Key header on a POST that creates something. The same key on the same call within 24 hours returns the first answer, marked Idempotent-Replayed, instead of creating a second thing.

Versioning

Changes inside v2 only add fields and endpoints; a breaking change becomes v3. Every response carries X-Bidi-Api-Version with the date of the contract you are on.

Conversations

A conversation started through the API counts against the same monthly allowance as the website widget, on its first answered message. When the month is full the agent answers with its capacity note and the response says limit: monthly.

429
HTTP/1.1 429 Too Many Requests
Retry-After: 41
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-Bidi-Api-Version: 2026-10-01

{"error": {"code": "rate_limited", "message": "Rate limit reached. Wait for Retry-After seconds and try again.", "request_id": "9f2c1a7d3b4e5f60"}}
Conversations
CodeStatusMeaning
missing_key401No Authorization header, or not a bearer token.
invalid_key401The key is unknown or was revoked.
expired_key401The key passed its expiry date.
plan_required402An action past the plan's limit per agent, or one that waits for approval on a plan without the helpdesk; the body carries the plan to move to, except on the plan that holds the most actions, where the way out is removing one.
plan_required403The workspace's plan has no API; the body carries the plan to move to.
insufficient_scope403The key lacks the scope; required_scope names it.
secret_key_required403A publishable key called something other than chat; use a secret key on your server.
not_found404No such thing in this workspace, including ids that belong to someone else.
conversation_expired410The conversation's 24 hours are over; start a new one.
validation_error422The body or query did not validate; details lists each field.
rate_limited429Too many calls this minute; wait Retry-After seconds.

Where next

Read API keys and scopes before you ship a key anywhere, the API reference for every endpoint, and Webhooks to hear about leads and conversations the moment they happen.