Avyukta Intellicall

Version 1.0

API Reference

Click-to-call telephony for your CRM. There are two integrations — pick the one that matches how your agents talk to customers. Base URL:

https://api.clouddialer.in

New here? Read the Integration Guide for the architecture and call flow first.

Choose your integration

The only question that matters: where does the agent talk?

Browser

Agent talks in the browser

A softphone runs in the agent's browser tab.
  • No phone, no software to install
  • Your CRM just opens a URL — no backend work at all
  • Conferences are a URL too — no call_id to track
  • Uses your Launch Key
Server

Agent talks on a real phone

We ring the agent's phone, then the customer.
  • Nothing runs in the browser
  • Your backend calls our REST API
  • Conferences by agent — still no call_id needed
  • Uses your API Key (different key!)
 BrowserServer
Agent deviceBrowser tab (softphone)Any real phone
Your CRM doesOpens a URLCalls the REST API from its backend
CredentialLaunch Key (in the URL)API Key (Authorization header)
Backend neededNoYes
Ring orderBoth dialed together (softphone auto-answers)Agent's phone first — customer is only dialed once the agent answers
Result webhookSame for both

You can run both — e.g. office agents in the browser, field agents on their mobiles.

Authentication — two keys

We issue you two different keys. They are not interchangeable, and each works with exactly one integration.

KeyUsed bySent asSafe in a browser?
Launch Key Browser integration api_key=… in the URL Yes — it is designed for this
API Key Server integration Authorization: Bearer … No — backend only, never in browser code
⚠ Most common integration error. Sending the Launch Key to a server endpoint (or vice-versa) returns 401 {"error_code":"UNAUTHORIZED","message":"Invalid or missing API key"}. If you get that, check you're using the key that matches the integration — not that the key is wrong.

Server integration header

Authorization: Bearer <your-api-key>
Browser integration

Browser integration

Your CRM never calls our API. It only opens URLs. Everything else — registering the softphone, placing the call, live status, hanging up — is handled by the page we serve.

Start a call

OPEN URL/CRM_API/webphone/launch

Wire this to your Dial button. Open it in a new tab/window.

https://api.clouddialer.in/CRM_API/webphone/launch
  ?crm_username=<your agent id>
  &api_key=<your launch key>
  &to_number=<customer number>
  &from_number=<your caller-ID>
ParameterDescription
crm_username requiredYour own agent identifier. Stable and unique per agent.
api_key requiredYour Launch Key.
to_number requiredCustomer's number (10-digit or +91).
from_number requiredCaller-ID to present, from the numbers assigned to your account.
uuid optionalYour own call id, echoed back in the result push. Must be unique per call.
💡  The agent must allow microphone access the first time. A brand-new agent takes a few extra seconds on their very first call while their softphone is provisioned.

Conference / add a participant

OPEN URL/CRM_API/webphone/launch?action=conference

Wire this to your Conference button. It adds the third party into the agent's existing call — everyone stays on together. It does not start a new call, and it does not open a second call window.

https://api.clouddialer.in/CRM_API/webphone/launch
  ?action=conference
  &to_number=<number to add>

No call_id, no API key, no backend call. The agent's open call tab picks it up and merges the party in.

Server integration

Server integration

Your backend calls these. All requests and responses are JSON. Every request needs Authorization: Bearer <your-api-key>.

Start a call

POST/calls

We ring the agent's phone first. Only once the agent answers do we dial the customer, and bridge them together. If the agent never picks up, the customer is never called.

Body

FieldTypeDescription
from_number requiredstringCaller-ID to present, from the numbers assigned to your account.
to_number requiredstringCustomer's number (10-digit or +91).
agent_number requiredstringThe agent's phone number — this is what rings first.
uuid requiredstringYour own call id, echoed back in the result push. Must be unique per call.
crm_usernamestringStrongly recommended. Your agent id. Does not affect routing — but it appears in the result push and lets you conference without tracking a call_id.
recording optionalbooleanDefault true.
max_ring_time optionalintegerSeconds to ring. Default 30.
💡  Sending crm_username instead of agent_number routes the call to that agent's browser softphone — that is the Browser integration, and requires their call window to be open.

Example

POST /calls
Authorization: Bearer <your-api-key>
Content-Type: application/json

{
  "from_number":   "<your caller-ID>",
  "to_number":     "9876543210",
  "agent_number":  "9812345678",
  "crm_username":  "agent.017",
  "uuid":          "550e8400-e29b-41d4-a716-446655440000",
  "recording":     true
}
200 OK
{ "status": "success", "call_id": "5deb67aa-…", "message": "Call initiated" }
StatusMeaning
200Call accepted and queued
400Validation error, unrecognised caller-ID, or unknown agent
400INVALID_JSON — malformed body (check for smart/curly quotes)
401Wrong or missing key — see Authentication
409DUPLICATE_UUID — that uuid was already used
502Telephony platform could not queue the call

Conference / add a participant

POST/calls/conference

Merge a third party into the agent's current call. Identify the agent exactly as you did when dialing — we resolve their live call ourselves, so you never need to store our call_id.

FieldTypeDescription
crm_username or agent_number requiredstringWhich agent's call to add the participant to.
number requiredstringThe third party to add (10-digit or +91).
max_ring_time optionalintegerDefault 30.
POST /calls/conference
Authorization: Bearer <your-api-key>

{ "crm_username": "agent.017", "number": "9811111111" }

200 OK
{ "status": "success", "message": "Conference leg queued",
  "call_id": "5deb67aa-…", "leg_id": 3 }
StatusMeaning
200Third party is being dialed; joins when they answer
404AGENT_NOT_ON_CALL — that agent has no call in progress
400INVALID_NUMBER / MISSING_FIELD

If the added party doesn't answer, the existing call is unaffected.

Live agents

GET/calls/live

Every call in progress for your account — who is on a call, with whom, and each party's state. Useful for a real-time supervisor view.

200 OK
{
  "status": "success",
  "count": 1,
  "calls": [{
    "call_id": "5deb67aa-…",
    "uuid": "your-own-call-id",
    "crm_username": "agent.017",
    "agent_number": "9812345678",
    "customer_number": "9876543210",
    "call_status": "ANSWER",
    "call_type": "server",
    "started_at": "2026-07-03T10:00:00Z",
    "legs": [
      { "role": "agent",       "number": "9812345678", "status": "JOINED" },
      { "role": "destination", "number": "9876543210", "status": "JOINED" }
    ]
  }]
}

Call status

GET/calls/{call_id}

Poll one call's status, duration, recording URL and per-party detail. Optional — the result push covers the normal flow.

call_status is one of INITIATED, ANSWER, NO_ANSWER, BUSY, FAILED, CANCELED. Each leg is DIALING, JOINED, LEFT, or FAILED.

Add a participant by call_id

POST/calls/{call_id}/add-leg

Same as /calls/conference, but identified by our call_id instead of the agent. Use this only if you already store the call_id; otherwise prefer /calls/conference.

{ "number": "9811111111" }

Both integrations

These behave identically whichever integration you use.

Call result push

When a call ends we POST the full result to the callback URL you give us during onboarding. You don't poll. Up to 3 retries; de-duplicate on uuid. Full field list in the Integration Guide.

POST <your callback URL>
Content-Type: application/json

{
  "call_id": "5deb67aa-…", "uuid": "your-own-call-id",
  "status": "ANSWER", "duration": 150,
  "customer_number": "9876543210", "crm_username": "agent.017",
  "call_type": "server", "disposition": "CRM_CALL",
  "recording_url": "https://api.clouddialer.in/recordings/crm_….wav",
  "start_time": "2026-07-03T10:00:00Z", "end_time": "2026-07-03T10:02:30Z"
}

call_type tells you which integration produced the call: browser or server.

This event waits until the recording has been verified on disk, so it lands roughly ten seconds after the call ends. recording_url is optional and absent when there is no verified recording — that means "no recording exists", not "retry later". For something that fires the instant the call ends, see below.

Instant hangup event

Enabled per account. Sent the moment either side hangs up — before any recording work, so nothing delays it. This is what a post-call form should open on.

POST <your callback URL>
Content-Type: application/json

{
  "event": "call_hangup",
  "call_id": "5deb67aa-…", "uuid": "your-own-call-id",
  "status": "ANSWER", "duration": 150,
  "customer_number": "9876543210", "agent_number": "9876543211",
  "from_number": "8047587340", "crm_username": "agent.017",
  "direction": "outbound",
  "start_time": "2026-07-03T10:00:00Z", "end_time": "2026-07-03T10:02:30Z"
}

It carries no recording_url — that is exactly why it is immediate. The verified recording still arrives in the call result push above, so a call you have both enabled for produces two events. Ordering between them is not guaranteed; de-duplicate on uuid and handle whichever lands first.

failure_reason is added when a call failed for a known reason — currently agent_unreachable, meaning the agent never answered and the customer was never dialled.

Live stream (server-sent events)

GET/calls/{call_id}/stream?key={launch_key}

Enabled per account. The alternative to a callback for browser-based CRMs: your page holds the connection open and we push the hangup straight to it, with no endpoint for you to host. The stream closes when the call ends.

const es = new EventSource(
  `https://api.clouddialer.in/calls/${call_id}/stream?key=${LAUNCH_KEY}`
);

// once, on connect — current status and legs
es.addEventListener('call_state',  e => console.log(JSON.parse(e.data)));

// same body as the hangup callback; server closes the stream after it
es.addEventListener('call_hangup', e => openPostCallForm(JSON.parse(e.data)));

No delivery guarantee. A closed tab, a sleeping laptop or a dropped connection means the event is missed, and unlike the callback there is nowhere to retry to. Anything that must not be lost belongs on the callback.

Authentication is by query parameter because the browser EventSource API cannot set an Authorization header. Pass your launch key — never the vendor API key, which is a backend credential and would end up in browser history and access logs.

If the call already ended before you connect, the call_hangup is replayed immediately with "replayed": true. A keepalive comment is sent every 20 seconds so proxies don't close an idle connection; EventSource never surfaces those. Returns 403 SSE_DISABLED if streaming is not enabled for your account.

Download a recording

GET/recordings/{filename}

Direct download using the exact URL from the result push. The unguessable per-call filename is the access control — treat recording URLs as private. Returns the audio file, or 404.

Health check

GET/health

Unauthenticated liveness check for your monitoring.

200 OK
{ "status": "success" }