REST API
MAIA public REST API. Base URL:
https://api.maia-ai.comOverview
Every request must be authenticated with one of these headers:
X-API-Key: maia_YOUR_KEYor
Authorization: Bearer maia_YOUR_KEYKeys are created in the dashboard at dashboard.maia-ai.com → “API and MCP” section → “API” tab → “Create key”. The key is shown only once. See Authentication for details.
Scopes:
read— read: projects, calls, contacts, campaigns, knowledge base, lead search, numbers.write— write and launch: outbound calls, contact and knowledge edits, calling campaigns, lead search, number assignment.
All data is strictly scoped to the key owner: only their projects and related entities are reachable. Business logic (subscription/balance gates, consent under Russian law, moderation, provisioning) is the same as in the dashboard.
Pagination
List endpoints accept query parameters and return a wrapper object:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | int | 50 (max 200) | How many records to return |
offset | int | 0 | Offset from the start of the result set |
{ "items": [ /* … */ ], "total": 128, "limit": 50, "offset": 0 }Projects
List projects
GET /v1/public/projects — scope read.
Returns the projects (agents) owned by the key.
curl https://api.maia-ai.com/v1/public/projects \
-H "X-API-Key: maia_YOUR_KEY"{
"projects": [
{ "id": "3f9a2b10-6c7d-4e88-9f12-0a1b2c3d4e5f", "name": "Sales team", "language": "ru" }
]
}Calls
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /v1/public/calls | read | Call log (filters project_id, direction, status; paginated) |
GET | /v1/public/calls/{call_id} | read | Call details: summary, extracted data, transcript |
POST | /v1/public/calls | write | Place an outbound call |
Place an outbound call
POST /v1/public/calls — scope write.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
project_id | string (uuid) | yes | Project the call is placed from |
phone | string | yes | Number in +79991234567 format |
task | string | no | Task instruction for the agent on this call |
vars | object | no | Variables for {{var}} substitution in the prompt |
Pre-flight checks: project ownership, active subscription, balance ≥ price per minute (otherwise 402), an assigned outbound line (otherwise 409).
curl -X POST https://api.maia-ai.com/v1/public/calls \
-H "X-API-Key: maia_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"project_id": "3f9a2b10-6c7d-4e88-9f12-0a1b2c3d4e5f",
"phone": "+79991234567",
"task": "Confirm tomorrow appointment at 3pm",
"vars": { "client_name": "John" }
}'Contacts
Unified contacts (the same data as the dashboard “Contacts”).
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /v1/public/contacts | read | Contact list (filters q, project_id, channel, lead; paginated) |
PATCH | /v1/public/contacts/{contact_id} | write | Edit a contact card |
Edit a contact
PATCH /v1/public/contacts/{contact_id} — scope write. Fields not sent are left unchanged.
| Field | Type | Description |
|---|---|---|
name | string | Contact name |
notes | string | Notes |
tags | array | Tags (replace the whole list) |
lead_status | string | new | in_progress | hot | won | lost; "" clears the status |
curl -X PATCH https://api.maia-ai.com/v1/public/contacts/a1b2c3d4-1111-2222-3333-444455556666 \
-H "X-API-Key: maia_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "lead_status": "hot", "tags": ["vip", "callback"] }'Knowledge base
Project knowledge sources. Indexing runs in the background — a source moves through
pending → indexing → ready | error; check its status in the list.
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /v1/public/projects/{project_id}/knowledge | read | List sources (id, kind, name, status, chunks) |
POST | /v1/public/projects/{project_id}/knowledge | write | Add a source: text or URL |
POST | /v1/public/projects/{project_id}/knowledge/file | write | Upload a file source (multipart) |
DELETE | /v1/public/projects/{project_id}/knowledge/{source_id} | write | Delete a source (with chunks and file) |
POST …/knowledge body: type (text | url), title, content (for text),
url, crawl (crawl the site), max_pages (1–50, default 15). Files —
txt/md/csv/html/json up to 5 MB, PDF/DOCX up to 20 MB — go through the multipart route …/knowledge/file.
curl -X POST https://api.maia-ai.com/v1/public/projects/3f9a2b10-6c7d-4e88-9f12-0a1b2c3d4e5f/knowledge \
-H "X-API-Key: maia_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "type": "url", "url": "https://example.com/faq", "crawl": true, "max_pages": 15 }'Calling campaigns
Mass outbound calling of a contact list. Flow: create → upload contacts → start (with consent) → track progress.
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /v1/public/campaigns | read | Campaign list (+counters, price per minute, spend) |
POST | /v1/public/campaigns | write | Create a campaign (draft) |
GET | /v1/public/campaigns/{id} | read | Campaign: status, params, counters, spend |
POST | /v1/public/campaigns/{id}/contacts | write | Upload contacts (JSON, before start) |
GET | /v1/public/campaigns/{id}/contacts | read | Campaign contacts (filters status, goal) |
POST | /v1/public/campaigns/{id}/start | write | Start (requires consent=true) |
POST | /v1/public/campaigns/{id}/pause | /resume | /stop | write | Pause / resume / stop |
POST | /v1/public/campaigns/{id}/requeue | write | Re-call the ones not reached (no_answer/busy/failed) |
params on creation (as in the dashboard): timezone, window_start, window_end,
concurrency, retries, retry_interval_min, scheduled_start_at. Contacts are loaded as a
JSON array [{phone, name?, vars?}] (E.164 normalization, dedup by number, mode =
append | replace) and only before start (otherwise 409).
Starting a campaign requires consent=true — an explicit confirmation that you have the
subscribers’ consent to be called (Russian laws 152-FZ/38-FZ). Without it — 400. Other gates:
active subscription, uploaded contacts, an assigned outbound line, sufficient balance.
# start a campaign with confirmed subscriber consent
curl -X POST https://api.maia-ai.com/v1/public/campaigns/CAMPAIGN_ID/start \
-H "X-API-Key: maia_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "consent": true }'Lead search
Find and enrich leads from a brief. Flow: parse the brief (free) → run the paid search → track progress → export leads or turn them straight into a calling campaign.
| Method | Path | Scope | Description |
|---|---|---|---|
POST | /v1/public/lead-search/parse-brief | write | Free brief parse: structure + estimate + providers |
POST | /v1/public/lead-search | write | Run a paid search |
GET | /v1/public/lead-search | read | Search jobs (last 50) |
GET | /v1/public/lead-search/{job_id} | read | Job status and progress (poll) |
GET | /v1/public/lead-search/{job_id}/leads | read | Job leads (filters status, has_phone, min_score, q) |
POST | /v1/public/lead-search/{job_id}/cancel | write | Soft-cancel the search |
POST | /v1/public/lead-search/{job_id}/to-campaign | write | Build a draft campaign from leads with phones |
POST /lead-search body: brief_raw, brief_parsed (structure from parse-brief:
niche, geo, signals, exclusions, target_leads, queries) and accept_terms
(a one-time legal checkbox on the first run). You are billed for enriched leads actually found.
Launch gates: active subscription, brief moderation (422), unaccepted legal terms
(400 without accept_terms on the first run), daily job limit (429), an already
active job (409), estimate vs balance (402).
# 1) free brief parse — returns the estimate and available providers
curl -X POST https://api.maia-ai.com/v1/public/lead-search/parse-brief \
-H "X-API-Key: maia_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "brief_raw": "beauty salons in Kazan, 50 leads" }'Numbers (telephony)
The key owner’s SIP-trunk numbers and their assignment to projects by direction
(in — inbound, out — outbound, both — both). For calls and campaigns a project needs
an outbound line (out/both).
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /v1/public/numbers | read | All trunk numbers with assignment status |
POST | /v1/public/projects/{project_id}/numbers | write | Assign a number to a project |
DELETE | /v1/public/projects/{project_id}/numbers/{number} | write | Unassign a number (query direction — only that direction) |
curl -X POST https://api.maia-ai.com/v1/public/projects/3f9a2b10-6c7d-4e88-9f12-0a1b2c3d4e5f/numbers \
-H "X-API-Key: maia_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "number": "+74951234567", "direction": "both" }'Real-time voice session
POST /v1/public/realtime/session — scope write.
Opens a live voice session with the project’s agent over WebRTC: a device, app, or SDK talks to the agent in real time. The response (LiveKit room access) and connection examples (JS, Python, Swift, Kotlin) are on the dedicated Real-time voice page.
Error codes
| Code | Meaning |
|---|---|
400 | Body validation error; missing consent (campaign start) or accept_terms (lead search) |
401 | Key missing or invalid |
402 | Insufficient balance (below price per minute or estimate) |
403 | Key lacks the required scope |
404 | Object not found (or not owned by the key) |
409 | No outbound SIP line; campaign contacts after start; a lead-search job already active |
422 | Brief or prompt rejected by moderation |
429 | Daily lead-search launch limit or key rate limit exceeded |
Full reference
Interactive OpenAPI specification with all fields: API Reference.