The Omixo AI platform, programmable
Everything your team does inside Omixo AI — WhatsApp, phone calls, your CRM, tickets and AI assistants — is available over a clean REST API so you can automate it from your website, app, ERP or backend. Authenticate with one key, call an endpoint, get JSON back. No coding framework required; if it can make an HTTPS request, it can talk to Omixo AI.
Quickstart — from zero to your first call
Three steps. You can be authenticated and making live requests in a couple of minutes.
Create a free account
Sign up in under a minute — no card needed. You start on the free tier with 2 concurrent calls free, so you can build and test straight away. You only buy a plan / top up the wallet when you go live.
Start free →Copy your API key
Inside the panel open Settings → Developer. Copy your key (it looks like cpaas_xxxxxxxx…). This one key authenticates every request. Keep it secret; you can regenerate it any time.
Make your first call
Call GET /api/v1/me with your key — a safe, read-only request that returns your account & wallet. If you get {"ok":true}, you are connected. Then send a WhatsApp message, push a call, or sync a contact — all with the same key.
GET /api/v1/mecurl "https://api.omixo.ai/api/v1/me" \ -H "X-API-Key: cpaas_your_api_key"
const res = await fetch("https://api.omixo.ai/api/v1/me", {
headers: { "X-API-Key": "cpaas_your_api_key" }
});
console.log(await res.json());$ch = curl_init("https://api.omixo.ai/api/v1/me");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["X-API-Key: cpaas_your_api_key"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($ch);import requests
r = requests.get("https://api.omixo.ai/api/v1/me",
headers={"X-API-Key": "cpaas_your_api_key"})
print(r.json()){
"ok": true,
"account": {
"company_id": 42,
"name": "Your Business",
"status": "active",
"wallet_balance": 1840.5,
"currency": "INR"
}
}Authentication
Send your workspace API key in the X-API-Key header on every request. The key identifies your workspace (tenant) and every response is automatically scoped to your own data — you can never see another business's data, and they can never see yours.
X-API-Key: cpaas_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
{
"ok": false,
"error": "invalid_api_key",
"message": "Invalid X-API-Key."
}Conventions
- REST over HTTPS — one base URL, JSON in and JSON out (UTF-8).
- Every response has an "ok" boolean. On failure you also get "error" (a stable slug) and a human "message".
- Phone numbers: send 10-digit Indian numbers or full E.164 (with country code). Bare 10-digit numbers are auto-prefixed with 91.
- List endpoints are paginated / limited — pass ?limit= (most cap at 100).
- Every call (success or failure) is logged to your Developer page for a live audit trail.
👤 Account & wallet
Read your account, wallet balance and live per-minute / per-message rates. Perfect for a first test call.
Your workspace name, status, KYC state, timezone and wallet balance. The recommended first call to confirm your key works.
curl "https://api.omixo.ai/api/v1/me" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"account": {
"company_id": 42,
"name": "Grand Ride Motors",
"status": "active",
"kyc_status": "verified",
"timezone": "Asia/Kolkata",
"wallet_balance": 1840.5,
"currency": "INR"
}
}Live balance plus the per-minute voice / AI and per-reply WhatsApp rates that apply to your workspace.
curl "https://api.omixo.ai/api/v1/wallet" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"wallet": {
"balance": 1840.5,
"currency": "INR",
"status": "active",
"rates": {
"voice_per_min": 1,
"ai_per_min": 6,
"whatsapp_per_msg": 0.4,
"stt_per_min": 0.3
}
}
}Recent wallet debits & credits. Filter with ?limit= (max 100).
curl "https://api.omixo.ai/api/v1/transactions" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"transactions": [
{
"id": 90112,
"type": "debit",
"amount": 6,
"reason": "AI call 00:58",
"balance_after": 1834.5,
"created_at": "2026-08-15 14:22:00"
}
]
}💬 WhatsApp messaging
Send any WhatsApp type from your own system — text, approved templates (+PDF), media, quick-reply buttons and list menus — and discover your templates as a ready-to-use dropdown. Replies land in your Omixo Inbox and the AI can carry the chat on.
One endpoint for every send type via the "type" field. Free-form types (text/media/buttons/list/location) need the 24-hour customer-service window open (the customer messaged you in the last 24h). type=template works anytime.
| Field | Description | |
|---|---|---|
| to | required | 10-digit or full-country-code number. |
| type | required | text | template | image | document | video | buttons | list | location. |
| text | optional | Message body (text/buttons/list). |
| template | optional | Approved template name (type=template). |
| lang | optional | Template language, e.g. en, hi. Default en. |
| variables | optional | Array filling {{1}}..{{n}} in order. |
| code | optional | One-time code for an AUTHENTICATION (OTP) template. Fills both the message body and the copy-code / autofill button automatically — use this instead of variables for OTP templates. |
| media_url | optional | Public URL for media / PDF template header. |
| buttons | optional | Up to 3 quick-reply button labels. |
| items | optional | Up to 10 list options (type=list). |
curl -X POST "https://api.omixo.ai/api/v1/messages/send" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"to": "9565990444",
"type": "text",
"text": "Hello from the Omixo API! 👋"
}'{
"to": "9565990444",
"type": "template",
"template": "tally_invoice",
"lang": "en",
"variables": [
"Rahul",
"INV-101",
"₹12,500",
"14-07-2026"
],
"media_url": "https://example.com/invoice.pdf",
"filename": "INV-101.pdf"
}{
"to": "9565990444",
"type": "template",
"template": "login_otp",
"lang": "en",
"code": "472913"
}{
"to": "9565990444",
"type": "buttons",
"text": "Would you like a demo?",
"buttons": [
"Yes",
"No",
"Call me"
]
}{
"to": "9565990444",
"type": "list",
"text": "Our courses:",
"list_button": "View courses",
"items": [
"Bank PO",
"SSC",
"Railway",
"Teaching"
]
}{
"to": "9565990444",
"type": "document",
"media_url": "https://example.com/brochure.pdf",
"caption": "Our brochure 📄",
"filename": "brochure.pdf"
}{
"ok": true,
"message": "Text sent to 919565990444"
}Queue up to 1000 messages in ONE request for background, rate-limit-aware sending — the right way to push volume instead of hammering /messages/send per message. Put the shared fields (type/template/lang/media_url…) at the top level and per-row to (+ variables/text/client_ref) in messages[]. Returns 202 immediately; every send auto-retries transient Meta rate-limits, and you track each via /messages/status. Pass a unique client_ref per row to make retries idempotent (no double-send).
| Field | Description | |
|---|---|---|
| messages | required | Array of {to, variables?/text?, client_ref?} — up to 1000. |
| type / template / lang / media_url / caption / filename | optional | Shared fields applied to every row (e.g. a template blast). |
curl -X POST "https://api.omixo.ai/api/v1/messages/bulk" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"type": "template",
"template": "order_update_en",
"lang": "en",
"messages": [
{
"to": "9565990444",
"variables": [
"Rahul",
"#1042"
],
"client_ref": "ord-1042"
},
{
"to": "9811122233",
"variables": [
"Priya",
"#1043"
],
"client_ref": "ord-1043"
}
]
}'{
"ok": true,
"batch_id": "01JABCXYZ…",
"queued": 2,
"rejected": 0,
"errors": [],
"message": "2 message(s) queued — they are being sent in the background. Track via /messages/status."
}Look up ONE message by the wamid you got from send. status moves sent → delivered → read, or failed (with the reason). This is the pull version of the whatsapp.status webhook — poll it, or just subscribe to the CRM Events Webhook to get pushed every change.
| Field | Description | |
|---|---|---|
| wamid | required | The message id returned by /messages/send. |
curl "https://api.omixo.ai/api/v1/messages/status" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"wamid": "wamid.HBgLM…",
"to": "919565990444",
"type": "text",
"direction": "outbound",
"status": "read",
"status_at": "2026-10-08T09:21:04+05:30",
"sent_at": "2026-10-08T09:20:58+05:30",
"error": null,
"errors": []
}Recent OUTBOUND messages with their current delivery status — for a dashboard or a catch-up poll. Filter by recipient or status.
| Field | Description | |
|---|---|---|
| to | optional | Only this recipient number. |
| status | optional | sent | delivered | read | failed. |
| limit | optional | Rows to return (default 50, max 200). |
curl "https://api.omixo.ai/api/v1/messages/statuses" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"count": 2,
"messages": [
{
"wamid": "wamid.HBgLM…",
"to": "919565990444",
"type": "template",
"direction": "outbound",
"status": "delivered",
"status_at": "2026-10-08T09:21:00+05:30",
"sent_at": "2026-10-08T09:20:58+05:30",
"error": null,
"errors": []
},
{
"wamid": "wamid.HBgQZ…",
"to": "919811122233",
"type": "text",
"direction": "outbound",
"status": "failed",
"status_at": "2026-10-08T09:20:10+05:30",
"sent_at": "2026-10-08T09:20:08+05:30",
"error": "Re-engagement message",
"errors": [
{
"code": 131047,
"title": "Re-engagement message"
}
]
}
]
}Whether a WhatsApp sender is connected and which number your messages come from.
curl "https://api.omixo.ai/api/v1/whatsapp/status" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"connected": true,
"from": "919044266522",
"display_name": "Omixo AI"
}Tells you whether a free-form text will actually be delivered to a customer. Returns send_type = "text" (open) or "template" (closed) — use it to auto-pick the send type.
| Field | Description | |
|---|---|---|
| to | required | Full number with country code. |
curl "https://api.omixo.ai/api/v1/whatsapp/window" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"open": true,
"send_type": "text",
"expires_in_minutes": 742
}Best way to build a send screen in your own panel. Each template comes decoded: label, variable_count, variables[] (position + example), preview, media_required and a copy-paste send_example — no need to parse Meta JSON.
| Field | Description | |
|---|---|---|
| q | optional | Name search. |
| category | optional | MARKETING | UTILITY | AUTHENTICATION. |
| language | optional | Filter by language. |
curl "https://api.omixo.ai/api/v1/whatsapp/templates" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"templates": [
{
"name": "callback_confirm",
"language": "hi",
"category": "UTILITY",
"label": "Callback confirm (hi)",
"variable_count": 3,
"preview": "Hi {{1}}, we will call you at {{2}}. — {{3}}"
}
]
}Build a brand-new WhatsApp template from your own app — send the body text + example values and Omixo assembles the Meta component JSON and submits it for approval. Returns status=PENDING; poll GET templates until it turns APPROVED.
| Field | Description | |
|---|---|---|
| name | required | Unique template name. |
| language | required | en | hi | en_US … |
| category | optional | UTILITY (default) | MARKETING. |
| body | required | Text with {{1}},{{2}}… placeholders. |
| examples | optional | Example values in {{n}} order. |
curl -X POST "https://api.omixo.ai/api/v1/whatsapp/templates" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "order_update_en",
"language": "en",
"category": "UTILITY",
"body": "Hello {{1}}, your order {{2}} is ready for pickup.",
"examples": [
"Rahul",
"#1042"
],
"footer": "Team Omixo"
}'{
"ok": true,
"status": "PENDING",
"name": "order_update_en"
}📞 Voice calls
Originate click-to-call, place automated OBD (outbound) voice calls that run a flow / speak text / play audio, and pull call records (CDRs) with duration, disposition and cost. All calls present your own DID as caller ID and bill as normal voice usage.
Rings your agent first, then bridges to the customer, showing your DID as caller ID. Needs an active calling plan.
| Field | Description | |
|---|---|---|
| agent | required | Your agent number to ring first. |
| destination | required | The customer number to bridge to. |
| caller_id | optional | A DID of yours to present (defaults to your enabled DID). |
curl -X POST "https://api.omixo.ai/api/v1/calls/click-to-call" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"agent": "9044200377",
"destination": "9565990444",
"caller_id": "915226523606"
}'{
"ok": true,
"message": "Call originated — ringing your agent first, then bridging the customer.",
"call_id": "1723712345.678",
"channel": "1723712345.678",
"agent": "919044200377",
"destination": "919565990444",
"caller_id": "915226523606"
}Dial a number and, when it answers, do ONE of three things: run a published Flow (flow_id), speak text with TTS (say), or play an audio clip (audio_url). We present your DID as caller ID and bill the talk-time as normal voice. The call is queued and placed within seconds; track it with GET /calls/obd/{id} or receive a webhook. Needs an active calling plan + wallet balance.
| Field | Description | |
|---|---|---|
| to | required | Number to call (10-digit or +E.164). |
| flow_id | optional | Run this published flow on answer. (Exactly one of flow_id / say / audio_url.) |
| say | optional | Text to speak (TTS). Max 800 chars. Use {name} placeholders filled from vars ({{name}} also accepted). |
| audio_url | optional | https URL of an audio clip to play (no TTS cost). We fetch + transcode once and cache it. |
| voice | optional | say mode only. A Sarvam speaker (default, low cost) or an ElevenLabs voice id. |
| vars | optional | Key/value map filling {name} placeholders in say / flow text, e.g. {"name":"Rahul"}. |
| repeat | optional | say/audio: replay 1-5 times (default 1). |
| caller_id | optional | A DID of yours to present (defaults to your enabled DID). |
| webhook_url | optional | Public https URL we POST the final outcome to (signed if you set an events-webhook secret). |
| max_attempts | optional | How many times to dial before giving up (1-10, default 4). A genuine busy / no-answer / invalid number is final and never retried. |
| retry_minutes | optional | Keep retrying a call blocked by trunk CAPACITY/congestion for up to this many minutes, even past max_attempts (0-1440, default 0 = count only). Set it for guaranteed delivery when your lines are busy — a congested call is never dropped, it waits and redials. |
| retry_backoff | optional | Seconds to wait between retries (5-600, default 20). |
| concurrency | optional | Max calls dialled at the same time for your workspace (1-50, default 3). Match it to your number's provisioned trunk capacity — setting it higher than the carrier allows causes congestion and dropped calls. |
curl -X POST "https://api.omixo.ai/api/v1/calls/obd" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"to": "9565990444",
"say": "Namaste {name}, aapka order ready hai. Dhanyavaad!",
"vars": {
"name": "Rahul"
},
"voice": "priya"
}'{
"to": "9565990444",
"flow_id": 12,
"vars": {
"name": "Rahul"
}
}{
"to": "9565990444",
"audio_url": "https://example.com/promo.mp3",
"repeat": 2
}{
"to": "9565990444",
"say": "Reminder: your appointment is tomorrow at 11 AM.",
"webhook_url": "https://yourapp.com/hooks/obd"
}{
"ok": true,
"message": "Call queued — it will be placed shortly.",
"call": {
"id": 4821,
"to": "+919565990444",
"mode": "say",
"status": "queued"
}
}Queue up to 1000 OBD calls in one request. Put the shared template (flow_id / say / audio_url / voice / webhook_url) at the top level and a per-row to (+ optional vars) in calls[]. Dialling is paced automatically to your trunk capacity (set concurrency to match your carrier) so it never floods the line — a batch no longer sends a few calls and drops the rest. Capacity-blocked calls redial within retry_minutes. Returns a batch_id to track the whole run.
| Field | Description | |
|---|---|---|
| calls | required | Array of {to, vars?} — up to 1000. |
| say | flow_id | audio_url | required | The shared action for every call (same rules as a single OBD). |
| voice / repeat / caller_id / webhook_url | optional | Shared options applied to all rows. |
| max_attempts / retry_minutes / retry_backoff / concurrency | optional | Shared delivery controls for the whole batch (see the single OBD call for each). concurrency paces the batch to your trunk capacity and retry_minutes keeps capacity-blocked calls redialling, so every contact is dialled instead of a few going through and the rest dropping. |
curl -X POST "https://api.omixo.ai/api/v1/calls/obd/bulk" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"say": "Namaste {name}, kal humara free demo hai. Join zaroor kijiye!",
"voice": "priya",
"webhook_url": "https://yourapp.com/hooks/obd",
"calls": [
{
"to": "9565990444",
"vars": {
"name": "Rahul"
}
},
{
"to": "9811122233",
"vars": {
"name": "Priya"
}
}
]
}'{
"ok": true,
"batch_id": "01JABCXYZ…",
"queued": 2,
"rejected": 0,
"calls": [
{
"id": 4822,
"to": "+919565990444",
"mode": "say",
"status": "queued"
}
],
"errors": []
}Poll one OBD call. status is queued → ringing → answered → completed (or no_answer / failed). Includes disposition, any DTMF the callee pressed, and talk-time in seconds.
curl "https://api.omixo.ai/api/v1/calls/obd/{id}" \
-H "X-API-Key: cpaas_your_api_key"{
"ok": true,
"call": {
"id": 4821,
"to": "+919565990444",
"mode": "say",
"status": "completed",
"disposition": "ANSWERED",
"dtmf": "1",
"duration_sec": 18,
"answered_at": "2026-10-08T09:20:11+05:30",
"completed_at": "2026-10-08T09:20:29+05:30"
}
}Aggregate status of a bulk submit — counts by status plus the per-call list.
curl "https://api.omixo.ai/api/v1/calls/obd/batch/{batch_id}" \
-H "X-API-Key: cpaas_your_api_key"{
"ok": true,
"batch_id": "01JABCXYZ…",
"total": 2,
"by_status": {
"completed": 1,
"no_answer": 1
},
"calls": []
}Create a voice campaign from a calling list that runs a published Flow (flow_id) or an AI Assistant (assistant_id) on every answered call. The dialer auto-starts it in ~20s and paces the originates with an inbound-safe channel reserve, so bulk dialing never bounces your incoming calls. Opt-out / DND numbers are skipped automatically. Send contacts[] ({to, name?, vars?}) or a plain numbers[] — up to 5000 per request.
| Field | Description | |
|---|---|---|
| name | required | Campaign name (shown in the panel). |
| flow_id | assistant_id | required | What to run on answer. flow_id wins if both are sent (bills as plain voice); assistant_id runs the AI voice agent. |
| contacts | optional | Array of {to, name?, vars?} — vars fill {name}/placeholders in the flow/assistant text. Up to 5000. (Use this OR numbers.) |
| numbers | optional | Simple array of phone-number strings if you have no names/vars. |
| country_code | optional | Country code for 10-digit numbers (default 91). |
| cid | optional | Caller-ID DID to present (defaults to your enabled DID). |
| pacing | optional | Max simultaneous originates 1-100 (default 10); the governor still caps to your free channels so inbound never starves. |
curl -X POST "https://api.omixo.ai/api/v1/campaigns" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Diwali offer",
"flow_id": 12,
"country_code": "91",
"contacts": [
{
"to": "9565990444",
"name": "Rahul",
"vars": {
"city": "Lucknow"
}
},
{
"to": "9811122233",
"name": "Priya"
}
]
}'{
"name": "Feedback drive",
"assistant_id": 7,
"numbers": [
"9565990444",
"9811122233"
]
}{
"ok": true,
"message": "Campaign queued - dialing starts automatically in ~20 seconds, paced with an inbound-safe channel reserve.",
"campaign": {
"id": 240,
"name": "Diwali offer",
"status": "queued",
"runs": "flow",
"flow_id": 12,
"assistant_id": null,
"pacing": 10,
"imported": 2,
"invalid": 0,
"suppressed": 0
}
}Live progress of a campaign - overall status plus a count of contacts by state (pending / calling / done / failed / skipped).
curl "https://api.omixo.ai/api/v1/campaigns/{id}" \
-H "X-API-Key: cpaas_your_api_key"{
"ok": true,
"campaign": {
"id": 240,
"name": "Diwali offer",
"status": "running",
"pacing": 10,
"total": 2,
"by_status": {
"done": 1,
"pending": 1
}
}
}Stop a queued/running campaign immediately - no further numbers are dialed (still-pending contacts are skipped). Calls already in progress finish normally.
curl -X POST "https://api.omixo.ai/api/v1/campaigns/{id}/cancel" \
-H "X-API-Key: cpaas_your_api_key"{
"ok": true,
"campaign": {
"id": 240,
"status": "completed"
}
}Cut a call you originated (click-to-call) or a masked/PIN-Connect bridge that is in progress — e.g. the customer's pre-paid balance ran out. Pass the call_id you got from click-to-call or the call_connected webhook.
| Field | Description | |
|---|---|---|
| call_id | required | The call/channel id (from click-to-call or the call_connected webhook). |
curl -X POST "https://api.omixo.ai/api/v1/calls/hangup" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"call_id": "1723712345.678"
}'{
"ok": true,
"hungup": true,
"call_id": "1723712345.678"
}Change the remaining talk-time of a call in progress — pre-paid top-up mid-call, or an early cut. seconds = talk-time left FROM NOW; the auto-hangup timer resets to it. seconds:0 removes the cap (unlimited). Verified against the caller channel you own.
| Field | Description | |
|---|---|---|
| call_id | required | The call/channel id (from the call_connected webhook or click-to-call). |
| seconds | required | New talk-time remaining from now, in seconds (0 = no limit). |
curl -X POST "https://api.omixo.ai/api/v1/calls/extend" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"call_id": "1723712345.678",
"seconds": 600
}'{
"ok": true,
"call_id": "1723712345.678",
"seconds_left": 600,
"unlimited": false
}Recent call log — direction, duration, disposition and cost. Each row carries a cdr_id and recording reference.
| Field | Description | |
|---|---|---|
| limit | optional | Rows to return (default 50). |
curl "https://api.omixo.ai/api/v1/calls/records" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"count": 1,
"records": [
{
"cdr_id": 270,
"direction": "outbound",
"from": "915226523606",
"to": "919565990444",
"duration": 58,
"disposition": "ANSWERED",
"cost": 6,
"started_at": "2026-08-15 14:21:02"
}
]
}🔐 PIN Connect — number masking
Create masked-call sessions where two people talk without seeing each other's number — QR "call the owner", ride-hailing rider↔driver, delivery, classifieds. The caller dials your PIN-Connect DID, enters a PIN, and is bridged to the owner with your DID as caller ID. Numbers stay private; a webhook fires on connect and completion. For pre-paid talk-time products (astrologer / consult / helpline where the caller buys minutes), set max_call_seconds so the bridge auto-hangs up when the balance runs out — or cut it yourself any time with /api/v1/calls/hangup.
Create a session
POST /masked-sessions with the owner_number (plus optional max_call_seconds and webhook_url). You get back a PIN.
Share the PIN
Show the caller: dial your PIN-Connect DID and enter the PIN. You can embed it in a QR code or a button.
Caller enters the PIN
They call your DID and key in the PIN. Omixo validates it, guarded by a brute-force lock and a per-caller cooldown.
Bridged privately
Omixo rings the owner and connects both legs. Your DID is the caller ID; the real numbers never show.
call_connected fires
Your webhook receives the live call_id. Start your billing timer and keep the call_id for live control.
Control and close
Extend on top-up or cut instantly with /calls/extend and /calls/hangup. call_completed fires at the end with duration and a signed recording_url.
Returns a PIN. max_uses controls one-time vs multi-call; two_way lets either party call the other; per-caller cooldown + brute-force guard are built in. Set max_call_seconds for a pre-paid talk-time cap — the bridged call auto-hangs up at that many seconds (both legs), no polling needed. Top up or cut talk-time mid-call with /api/v1/calls/extend.
| Field | Description | |
|---|---|---|
| owner_number | required | Who the caller reaches. 💡 Why & when: The person the caller wants to reach, such as the astrologer or the driver. Their number stays hidden; the caller only ever dials your DID. |
| pin | optional | Omit for a random 4-digit PIN. 💡 Why & when: Let Omixo generate a random PIN unless you need a fixed one, for example a PIN printed on a card. Random is safer. |
| ttl_minutes | optional | Lifetime (default 30). 💡 Why & when: Auto-expires the PIN so an old session cannot be reused. Match it to how long the caller has to dial in. |
| max_uses | optional | 1 = one-time (default), N = N calls, 0 = unlimited till expiry. 💡 Why & when: Decides whether the PIN dies after the first call (a marketplace enquiry) or keeps working for a set number or until expiry (a support line). |
| two_way | optional | true = owner can also call caller_bind back. 💡 Why & when: Turn on only when the owner also needs to call the customer back on the same masked link, for example a driver calling a rider. |
| caller_bind | optional | Lock the session to one caller. 💡 Why & when: Locks the PIN to one phone number so nobody who overhears the PIN can use it. Recommended for one-to-one pairings. |
| max_call_seconds | optional | Hard cap per bridged call — the call auto-hangs up at this many seconds (pre-paid / talk-time cap). Omit for no cap. 💡 Why & when: Your pre-paid safety cap. If a caller has 15 paid minutes, set 900 so the bridge can never run over even if your own timer fails. |
| webhook_url | optional | POSTed call_connected + call_completed (with signed recording_url). 💡 Why & when: Where Omixo tells your app that the call connected and ended. This is how you start and stop billing and fetch the recording. Set it whenever you bill or log calls. |
curl -X POST "https://api.omixo.ai/api/v1/masked-sessions" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"owner_number": "919044266522",
"ttl_minutes": 30,
"max_uses": 1,
"two_way": false,
"caller_bind": "919999888877",
"max_call_seconds": 900,
"purpose": "Astrologer consult — 15 min pack",
"webhook_url": "https://your-app.com/hooks/omixo"
}'{
"ok": true,
"session": {
"id": 842,
"pin": "4821",
"status": "active",
"owner_number": "919044266522",
"max_uses": 1,
"max_call_seconds": 900,
"expires_at": "2026-08-15 18:30:00"
}
}Your PIN sessions (active by default). Filter with ?status=active|used|revoked|expired|all, ?pin=, ?limit=.
curl "https://api.omixo.ai/api/v1/masked-sessions" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"sessions": [
{
"id": 842,
"pin": "4821",
"status": "active",
"owner_number": "919044266522",
"uses": 0
}
]
}Kill a session early so its PIN can no longer connect. Replace {id} with the session id.
curl -X DELETE "https://api.omixo.ai/api/v1/masked-sessions/{id}" \
-H "X-API-Key: cpaas_your_api_key"{
"ok": true,
"revoked": true
}Create a capped, webhook-wired session, then control the live call from your webhook handler.
const BASE = "https://api.omixo.ai", KEY = "cpaas_your_api_key";
// 1) Create a masked session for a 15-minute pre-paid consult
const r = await fetch(BASE + "/api/v1/masked-sessions", {
method: "POST",
headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
body: JSON.stringify({
owner_number: "919044266522", // the astrologer (stays hidden)
caller_bind: "919999888877", // lock the PIN to this customer
max_call_seconds: 900, // safety cap = 15 minutes
webhook_url: "https://your-app.com/hooks/omixo"
})
});
const { session } = await r.json();
// Tell the customer: dial your PIN-Connect DID and enter PIN <session.pin>
// 2) In your webhook handler (Express):
app.post("/hooks/omixo", (req, res) => {
const e = req.body;
if (e.event === "call_connected") { saveCallId(e.session_id, e.call_id); startTimer(e.session_id); }
if (e.event === "call_completed") { chargeCustomer(e.session_id, e.duration); archive(e.recording_url); }
res.sendStatus(200);
});
// 3) Customer tops up mid-call -> push the cap out live
await fetch(BASE + "/api/v1/calls/extend", {
method: "POST",
headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
body: JSON.stringify({ call_id: callId, seconds: 600 }) // 10 more minutes
});
// 4) Balance ends -> cut the call instantly
await fetch(BASE + "/api/v1/calls/hangup", {
method: "POST",
headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
body: JSON.stringify({ call_id: callId })
});Set webhook_url on the session and Omixo calls your app in real time over HTTPS POST with a JSON body. Two events fire.
{
"event": "call_connected",
"call_id": "1723712345.678",
"session_id": 842,
"pin": "4821",
"caller": "919999888877",
"bridged_to": "919044266522",
"use": 1,
"at": "2026-08-15T18:22:04+05:30"
}| Field | What it's for |
|---|---|
| call_id | The live channel id. Pass it to /calls/extend or /calls/hangup to control this call while it is still in progress. |
| session_id | The masked session this call belongs to. |
| caller / bridged_to | The two connected parties, for your own records. |
| use | Which use of the PIN this is (1 for the first call). |
| at | Connect time — a useful billing start marker. |
{
"event": "call_completed",
"session_id": 842,
"caller": "919999888877",
"owner_number": "919044266522",
"duration": 372,
"disposition": "ANSWERED",
"recording_url": "https://api.omixo.ai/rec/842-signed",
"cdr_id": 90231,
"at": "2026-08-15T18:28:16+05:30"
}| Field | What it's for |
|---|---|
| duration | Talk-time in seconds — bill on this exact number. |
| disposition | ANSWERED, NO ANSWER, BUSY, and so on. |
| recording_url | A signed link to the recording that expires in 7 days. Download it promptly. |
| cdr_id | The call record id, for your reconciliation. |
📇 CRM contacts
Push leads from your website/app straight into the Omixo CRM, keep them in sync, and read them back. Contacts are matched (upserted) by phone; tags and attributes merge.
Paginated CRM contacts. Filter with ?q= (name/phone) or ?status=. ?limit= caps at 100.
curl "https://api.omixo.ai/api/v1/contacts" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"contacts": [
{
"id": 5501,
"name": "Rahul Sharma",
"phone": "919565990444",
"status": "new",
"tags": [
"website-lead"
]
}
],
"total": 128,
"current_page": 1,
"per_page": 30
}Add or update a contact (matched by phone). Great for capturing website leads.
| Field | Description | |
|---|---|---|
| phone | required | Contact phone (the match key). |
| name | optional | Full name. |
| optional | Email address. | |
| service_required | optional | What they want. |
| tags | optional | Array of tags (merged). |
| attrs | optional | Custom key/value object (merged). |
curl -X POST "https://api.omixo.ai/api/v1/contacts" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"phone": "9565990444",
"name": "Rahul Sharma",
"email": "rahul@example.com",
"tags": [
"website-lead"
],
"attrs": {
"city": "Lucknow"
}
}'{
"ok": true,
"contact": {
"id": 5501,
"name": "Rahul Sharma",
"phone": "919565990444",
"status": "new"
}
}Update a contact by id — status, service, tags, attrs. Replace {id}.
curl -X PUT "https://api.omixo.ai/api/v1/contacts/{id}" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"status": "qualified",
"attrs": {
"budget": "50k"
}
}'{
"ok": true,
"contact": {
"id": 5501,
"status": "qualified"
}
}🎫 Tickets & action items
Raise action items from your own system and resolve them — callbacks, complaints, tasks. They appear in the Omixo Action Center for your team.
Your action items. Filter ?status=open|acknowledged|resolved or ?level=.
curl "https://api.omixo.ai/api/v1/tickets" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"tickets": [
{
"id": 3310,
"subject": "Callback requested",
"level": "important",
"status": "open"
}
],
"total": 4
}Raise a ticket / action item from your app.
| Field | Description | |
|---|---|---|
| subject | required | Short title. |
| level | optional | normal | important | emergency. |
| phone | optional | Related contact number. |
| details | optional | Free text. |
curl -X POST "https://api.omixo.ai/api/v1/tickets" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"subject": "Callback requested",
"level": "important",
"phone": "9565990444",
"details": "Wants a demo tomorrow 4 PM"
}'{
"ok": true,
"ticket": {
"id": 3311,
"subject": "Callback requested",
"level": "important",
"status": "open"
}
}Acknowledge or resolve a ticket. Replace {id}.
curl -X PUT "https://api.omixo.ai/api/v1/tickets/{id}" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"status": "resolved",
"resolution": "Demo scheduled"
}'{
"ok": true,
"ticket": {
"id": 3311,
"status": "resolved"
}
}🤖 AI assistants
List the AI assistants configured on your workspace — the brains that answer your calls, WhatsApp and widget.
Your AI assistants with name, languages and status.
curl "https://api.omixo.ai/api/v1/assistants" \ -H "X-API-Key: cpaas_your_api_key"
{
"ok": true,
"assistants": [
{
"id": 7,
"name": "Maya — Sales",
"languages": [
"hi",
"en"
],
"status": "active"
}
]
}🧾 Tally ERP bridge
Send a formatted Tally ERP / Prime voucher (invoice, receipt, statement, reminder) to a customer over WhatsApp with the PDF attached. Needs the Tally add-on active.
Delivers a mapped template with the voucher PDF. Works from Tally TDL or any ERP.
| Field | Description | |
|---|---|---|
| phone | required | Customer number. |
| type | required | invoice | receipt | statement | reminder | payment | order. |
| party_name | optional | Ledger / party name. |
| amount | optional | Voucher amount. |
| items | optional | Line items array. |
curl -X POST "https://api.omixo.ai/api/v1/tally/send" \
-H "X-API-Key: cpaas_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"phone": "9565990444",
"type": "invoice",
"party_name": "Ashish Saxena",
"number": "INV-101",
"date": "07-07-2026",
"amount": 12500,
"due_date": "14-07-2026",
"items": [
{
"name": "Course fee",
"qty": 1,
"amount": 12500
}
]
}'{
"ok": true,
"message": "Voucher sent to 919565990444"
}Errors
Failures return an HTTP status plus a JSON body with a stable error slug and a human message.
Rate limits
- Tenant self-service API (contacts, tickets, wallet, me, assistants, WhatsApp discovery): 120 requests/minute.
- Message send, call, Tally, masked-session create: 60 requests/minute.
- Read endpoints for records/masked-session list: 120 requests/minute.
Exceeding a limit returns 429 — back off and retry.
Webhooks
Omixo AI can call your app back in real time.
Start building free
2 concurrent calls free. Pay-as-you-go when you go live. Your API key is waiting in Settings → Developer.
Create a free account → See pricingDeveloper FAQ
The questions SOHO, SME and enterprise teams ask before they build.