Sign in

Intellismi API

Everything the dashboard does is an HTTP call, so your own systems can place calls, learn what happened on them, keep the contact list in sync and pull usage. This page covers the stable v1 surface; the interactive OpenAPI listing of every route is at /api/docs on your installation.

The same reference, rendered, lives at https://<your-intellismi>/developers.

Base URL and authentication

https://<your-intellismi>/api/v1/...
Authorization: Bearer aicc_xxxxxxxxxxxxxxxxx

Create keys under Settings → API access. A key belongs to one organisation and carries scopes:

Scope Allows
calls:write place and hang up calls
calls:read list calls, read transcripts, download recordings, read usage
contacts:write create/update contacts
contacts:read look contacts up

Errors are JSON: {"detail": "human-readable reason"} with a conventional status (400 bad input, 401 no/invalid key, 403 missing scope, 404 not found, 409 the line cannot call right now, 429 concurrent-call limit of the plan reached).

API keys created under Settings → API access can also manage campaigns and inbox requests (see below). Everything is plain JSON over HTTPS; there is no SDK to install. Copy the examples, replace the key and the base URL.

Quick start

# 1. Which agents and lines do I have?
curl https://<your-intellismi>/api/v1/agents -H "Authorization: Bearer aicc_…"
curl https://<your-intellismi>/api/v1/lines  -H "Authorization: Bearer aicc_…"

# 2. Have the agent call someone about an appointment
curl -X POST https://<your-intellismi>/api/v1/calls \
  -H "Authorization: Bearer aicc_…" -H "Content-Type: application/json" \
  -d '{"number": "+97339000000", "caller_name": "Ali Hassan", "profile_id": 7,
       "purpose": "Remind Ali about his appointment tomorrow at 10:00 and ask him to confirm or pick another time.",
       "reference": "APT-1042", "metadata": {"clinic": "Seef"}}'

# 3. Get the result when the call ends (webhook), or poll:
curl https://<your-intellismi>/api/v1/calls/<id> -H "Authorization: Bearer aicc_…"

Agents

GET /api/v1/agents — the organisation's hired agents. Use id as profile_id on calls and agent_profile_id on campaigns; the agent introduces itself by its name and speaks with its own voice.

{"agents": [{"id": 7, "name": "Ahmed", "role": "Customer service · Arabic & English", "languages": ["ar", "en"], "is_default": true}]}

How an outbound call works

  1. POST /api/v1/calls — Intellismi picks a registered line and sends the INVITE. The response is 201 immediately with status: "dialing". The assistant is not connected yet.
  2. The far end rings. If they do not answer, the call ends with status: "failed" and detail carrying the SIP reason (busy, no answer, number not found…).
  3. The moment they answer, the audio path is attached and the assistant speaks its greeting (or the profile's default), then works toward purpose using the organisation's knowledge base, tools (take a message, save the contact, transfer to a person) and the language you fixed or the one the person speaks.
  4. When either side hangs up, the call is summarised, billed for the whole seconds the assistant was on the line, and — if you set one — your webhook receives the full result. You can also poll GET /api/v1/calls/{id}.

Place a call

POST /api/v1/calls (scope calls:write)

Field Type Notes
number string required — +97317000000 (E.164, recommended), local digits, an extension, or a sip: URI
account_id string line to call from (see /api/v1/lines). Default: the first registered line
caller_name string who you are calling; the assistant greets them by name
purpose string what to achieve, e.g. "Confirm tomorrow's 10:00 appointment or offer to reschedule"
greeting string exact first sentence; default comes from the agent profile
language en ar hi multi fix the call language; multi follows the person
profile_id int AI agent profile to use instead of the line's default
reference string ≤120 your own id (order, ticket, appointment) — returned everywhere and filterable
metadata object any JSON, echoed back unchanged in the call record and webhook
webhook_url string per-call override of the organisation webhook
mode ai (default) tone tone places a test call that plays a tone instead of the assistant
curl -X POST https://<your-intellismi>/api/v1/calls \
  -H "Authorization: Bearer aicc_…" -H "Content-Type: application/json" \
  -d '{
    "number": "+97339000000",
    "caller_name": "Ali Hassan",
    "purpose": "Confirm tomorrow'\''s 10:00 dental appointment. If it does not suit, offer 11:00 or Thursday 9:00.",
    "language": "ar",
    "reference": "APT-1042",
    "metadata": {"clinic": "Seef", "doctor": "Dr. Noor"}
  }'
{
  "id": "acc-1-7", "direction": "outbound", "status": "dialing", "outcome": "",
  "line": "acc-1", "number": "sip:+97339000000@ucaas.example.com",
  "caller_name": "Ali Hassan", "reference": "APT-1042", "metadata": {"clinic": "Seef", "doctor": "Dr. Noor"},
  "purpose": "Confirm tomorrow's 10:00 dental appointment…",
  "started_at": 1789300000.1, "answered_at": null, "ended_at": null,
  "duration_seconds": 0, "billed_seconds": 0, "summary": "", "is_lead": false, "recording_url": null
}

Python:

import requests
r = requests.post("https://<your-intellismi>/api/v1/calls",
                  headers={"Authorization": "Bearer aicc_…"},
                  json={"number": "+97339000000", "caller_name": "Ali Hassan",
                        "purpose": "Confirm tomorrow's 10:00 appointment", "reference": "APT-1042"})
r.raise_for_status()
call_id = r.json()["id"]

Node:

const r = await fetch("https://<your-intellismi>/api/v1/calls", {
  method: "POST",
  headers: { Authorization: "Bearer aicc_…", "Content-Type": "application/json" },
  body: JSON.stringify({ number: "+97339000000", caller_name: "Ali Hassan",
                         purpose: "Confirm tomorrow's 10:00 appointment", reference: "APT-1042" }),
});
const { id } = await r.json();

Read a call

GET /api/v1/calls/{id} (scope calls:read) — the record above plus transcript ([{role, text, at}]) and messages taken during the call.

status Meaning
dialing INVITE sent, waiting for the far end
ringing inbound call not yet answered
answered in progress
ended finished normally
failed not connected — see detail
outcome Meaning
ai the assistant handled it
human a person on your team took it
forwarded transferred to a number/extension
ivr the caller only used the menu
missed never answered

Webhooks

Set the URL (and an optional signing secret) under Settings → API access, or PUT /api/v1/webhook {"url": "...", "secret": "..."}. POST /api/v1/webhook/test sends a sample.

When a call ends Intellismi POSTs JSON with header X-Intellismi-Event: call.completed:

{
  "event": "call.completed",
  "id": "acc-1-7", "direction": "outbound", "status": "ended", "outcome": "ai", "detail": "",
  "line": "acc-1", "number": "sip:+97339000000@…", "caller_name": "Ali Hassan",
  "reference": "APT-1042", "metadata": {"clinic": "Seef"}, "purpose": "Confirm…",
  "started_at": 1789300000.1, "answered_at": 1789300006.4, "ended_at": 1789300071.9,
  "duration_seconds": 65.5, "billed_seconds": 66,
  "summary": "Ali confirmed the 10:00 appointment and asked for an SMS reminder.",
  "is_lead": false, "sentiment": "positive", "forwarded_to": null,
  "transcript": [{"role": "assistant", "text": "…", "at": 1789300007.0}, {"role": "user", "text": "…", "at": 1789300011.2}],
  "messages": [{"from": "Ali Hassan", "subject": "Reminder", "text": "Send an SMS the evening before", "callback_number": "+97339000000", "priority": "normal"}],
  "recording_url": "https://<your-intellismi>/api/v1/calls/acc-1-7/recording"
}

If a secret is set, X-Intellismi-Signature: sha256=<hex> is the HMAC-SHA256 of the raw body. Verify it:

import hmac, hashlib
expected = "sha256=" + hmac.new(SECRET.encode(), raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, request.headers["X-Intellismi-Signature"])

Respond with any 2xx within 10 s. Non-2xx and timeouts are retried twice (2 s, then 4 s later); failures are recorded in the audit log.

Contacts

Usage and billing

GET /api/v1/usage?since=&until= — defaults to the current calendar month in the organisation's timezone.

{
  "plan": "Business", "included_seconds": 72000,
  "billed_seconds": 4212, "overage_seconds": 0, "price": 0.0, "currency": "BHD",
  "by_direction": [{"direction": "inbound", "seconds": 3900, "price": 0, "calls": 61}, {"direction": "outbound", "seconds": 312, "price": 0, "calls": 9}],
  "by_day": [{"day": "2026-09-01", "seconds": 640, "price": 0, "calls": 11}, …]
}

How a call is charged:

  1. Time is measured only while the assistant is on the line. Ringing, welcome messages, IVR menus and time after a transfer are free.
  2. That time is rounded up to whole seconds (billed_seconds).
  3. Each second is priced at the rate for the model that handled the call and the direction (inbound/outbound). Seconds within the plan's included_seconds for the month are credited on the statement; the rest is the usage charge.

Reports (CSV)

Signed-in users can download from the dashboard (Home → Reports, Settings → Plan & billing) or directly:

GET /api/reports/calls.csv?from=2026-09-01&to=2026-09-30
GET /api/reports/usage.csv        # one row per billed call
GET /api/reports/transcripts.csv
GET /api/reports/messages.csv
GET /api/reports/contacts.csv

Dates are inclusive and interpreted in the organisation's timezone. Files are UTF-8 with BOM, so Arabic opens correctly in Excel.

Real-time events

The dashboard's WebSocket at /ws streams call_state, incoming, transcript and reg_state events for the signed-in organisation; API keys can connect with the same Authorization header.

Free AI (self-hosted) {#free-ai}

Intellismi can run on free, open models on a server you control, with no per-second usage charge. Speech-to-text, replies and the voice each run as an OpenAI-compatible service; docker-compose.local-ai.yml in the repository starts all three on one NVIDIA machine:

docker compose -f docker-compose.local-ai.yml up -d
docker exec -it $(docker ps -qf name=ollama) ollama pull qwen2.5:7b   # or jais / allam for Arabic

Then in the dashboard: Settings → AI agents → profile → "Use the free AI stack", save, and assign the profile to a line. Calls handled by this profile are metered at the local:* rate, which is zero by default. Expect replies to start about a second after the caller stops speaking; the cloud provider is a little faster and supports tools (messages, transfers, lookups) - the free stack does not yet.

Inbox requests

Everything the agent writes down for your team - messages, callbacks, complaints, orders, bookings, sales leads - is a request in the Inbox. Your ticketing or CRM system can read them, create them and comment on them.

Method Path What
GET /api/v1/requests?status=open&department=Sales&kind=lead&q= list; status = new, read, open (new+read), done
POST /api/v1/requests create: {caller_name, callback_number, subject, message, kind, department, priority}
PUT /api/v1/requests/{id} change status, department, assigned_to, priority, kind
POST /api/v1/requests/{id}/comments {"text": "Called back, booked Monday 10:00"} - an internal reply

A request looks like:

{"id": 41, "created_at": 1789300071.9, "call_id": "acc-1-7", "source": "agent",
 "caller_name": "Ali Hassan", "callback_number": "+97339000000",
 "kind": "lead", "department": "Sales", "priority": "high", "status": "new",
 "subject": "Quote for 20 chairs", "message": "Wants a quote for 20 office chairs by Thursday",
 "assigned_to": null, "comments": []}

Requests taken during a call are also inside that call's webhook under messages, so a CRM can create its own ticket the moment the call ends.

Outbound campaigns

Call a list of people with a script and get the answers back as a spreadsheet: consent calls, surveys and interviews, sales follow-ups, appointment confirmations for a whole day. Manage them in the dashboard under Call Center → Outbound campaigns, or over the API.

Method Path What
GET /api/campaigns/presets ready-made scripts: permission, interview, sales, custom
POST /api/v1/campaigns create - see the example
GET /api/v1/campaigns list with progress counts
GET /api/v1/campaigns/{id} progress plus every contact's status, outcome and answers
PUT /api/v1/campaigns/{id} change the script, agent, line, window, attempts
POST /api/v1/campaigns/{id}/contacts {"rows": [{"phone": "+973…", "name": "Ali", "fields": {"order_no": "A-17"}}]}
POST /api/campaigns/{id}/upload multipart CSV / Excel: phone, name, … (template: /api/campaigns/template.csv)
POST /api/v1/campaigns/{id}/status {"action": "start" \| "pause" \| "reset"}
GET /api/v1/campaigns/{id}/results.xlsx or .csv one row per person, one column per question
# Create a consent campaign, add people, start it
curl -X POST https://<your-intellismi>/api/v1/campaigns -H "Authorization: Bearer aicc_…" -H "Content-Type: application/json" -d '{
  "name": "WhatsApp consent - March", "kind": "permission", "agent_profile_id": 7, "language": "ar",
  "intro": "Hello {name}, this is {agent} from {company}. Do you have a minute?",
  "questions": [
    {"key": "consent", "text": "May we send you our monthly offers on WhatsApp?", "type": "yes_no"},
    {"key": "channel", "text": "Would you prefer WhatsApp, SMS or email?", "type": "choice", "options": ["WhatsApp", "SMS", "Email"]}
  ],
  "closing": "Thank you {name}, have a good day.",
  "window_start": "09:00", "window_end": "20:00", "max_attempts": 2, "retry_minutes": 60}'

curl -X POST https://<your-intellismi>/api/v1/campaigns/3/contacts -H "Authorization: Bearer aicc_…" -H "Content-Type: application/json" \
  -d '{"rows": [{"phone": "+97339000001", "name": "Ali"}, {"phone": "+97339000002", "name": "Sara", "fields": {"order_no": "A-17"}}]}'

curl -X POST https://<your-intellismi>/api/v1/campaigns/3/status -H "Authorization: Bearer aicc_…" -H "Content-Type: application/json" -d '{"action": "start"}'

Question types: text, yes_no, number, choice (with options), date. {name}, {company}, {agent} and any fields column (e.g. {order_no}) are filled in per person. Calls go out one at a time per campaign inside the window, retried max_attempts times retry_minutes apart. Outcomes: completed, partial, declined, callback, wrong_number, no_answer. Each campaign call is a normal call: it appears in GET /api/v1/calls with reference = campaign:<campaign id>:<contact id> and fires the webhook.

Integration recipes

Appointment reminders from your booking system. The evening before, for each appointment POST /api/v1/calls with caller_name, purpose ("Remind … tomorrow at 10:00; confirm, or offer Wednesday 11:00 or Thursday 09:00"), reference = your appointment id. On the webhook, read summary and messages; if the transcript contains a new time, update the booking. The agent can also book directly if you connect your API under Settings → Integrations.

Order or delivery confirmation. POST /api/v1/calls with the order in metadata and a purpose like "Confirm the delivery address and a time slot today between 2 and 6". Both come back untouched in the webhook.

Lead follow-up. When a lead is created in your CRM, add the person to a running sales campaign with POST /api/v1/campaigns/{id}/contacts. Read the answers from GET /api/v1/campaigns/{id} or the results file, or catch each call's webhook (reference starts with campaign:).

Two-way ticketing. Poll GET /api/v1/requests?status=new (or use the webhook's messages) to create tickets; when your agent resolves one, PUT /api/v1/requests/{id} with {"status": "done"} and post the resolution as a comment so the call-center team sees it.

Live dashboard in your own app. Connect to /ws with the same Authorization header to receive call_state, incoming and transcript events as they happen.