The TechDial platform, programmable
Everything your team does inside TechDial — 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 TechDial.
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. |
| 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": "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"
}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 from your app and pull call records (CDRs) with duration, disposition and cost. Calls present your own DID as caller ID.
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"
}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
TechDial 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.