openapi: 3.0.3 info: title: Click-to-Call API version: "1.0" description: | REST API for placing and managing click-to-call telephony calls. ## Placing a call There are two ways to start a call, depending on how your agents work: **Browser Softphone (recommended)** — your CRM's Dial button opens a launch URL we provide. The agent's softphone runs in the browser; no physical phone and no backend call are needed on your side. See the [Integration Guide](./integration-guide.html) for the URL format. **Server-to-Server** — your backend calls `POST /calls` directly with your API key. We ring the agent first, then the customer, and bridge the two. ## Call status values | Status | Meaning | |---|---| | `INITIATED` | Call accepted and queued | | `ANSWER` | Connected — conversation in progress or completed | | `NO_ANSWER` | Rang but was not picked up | | `BUSY` | Line busy / engaged | | `FAILED` | Could not be placed | | `CANCELED` | Canceled before connection | ## Call status callback When a call ends, we POST its final result to the callback URL you provide during onboarding: ```json { "call_id": "5deb67aa-d54d-4536-9a25-a29813b3bddd", "uuid": "your-own-call-id", "status": "ANSWER", "duration": 150, "customer_number": "9876543210", "agent_number": "9876543211", "direction": "outbound", "start_time": "2026-07-03T10:00:00Z", "end_time": "2026-07-03T10:02:30Z", "recording_url": "https://api.clouddialer.in/recordings/crm_....wav" } ``` The `uuid` is always the one you passed at initiation — use it to correlate the callback to your original request. Delivery is retried up to 3 times with exponential backoff; the same `uuid` is sent on every retry. contact: name: Platform Engineering servers: - url: https://api.clouddialer.in description: Production tags: - name: Calls description: Start a call, check its status, or add a participant. - name: Ops description: Health check and call recording retrieval. paths: /calls: post: tags: [Calls] summary: Initiate a call operationId: initiateCall description: | Places a two-leg call: the agent leg rings first, then the customer leg, bridged together. Identify the agent with **either** `agent_number` (a real phone number, rung via the network) **or** `crm_username` (your own ID for the agent, as registered during onboarding — routes to their browser softphone) — not both. security: [{ ApiKey: [] }] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/InitiateCallRequest' examples: byCrmUsername: summary: Agent identified by your own agent ID (browser softphone) value: from_number: "919800000000" to_number: "9876543210" crm_username: "agent.017" uuid: "550e8400-e29b-41d4-a716-446655440000" byPhoneNumber: summary: Agent identified by a real phone number value: from_number: "919800000000" to_number: "9876543210" agent_number: "9876543211" uuid: "550e8400-e29b-41d4-a716-446655440000" responses: '200': description: Call accepted and queued content: application/json: schema: { $ref: '#/components/schemas/InitiateCallSuccess' } '400': description: Validation error, unrecognised caller-ID number, or unknown agent content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } '401': description: Invalid or missing API key content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } '403': description: Request origin not permitted for this account content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } '502': description: The telephony platform could not queue the call content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } /calls/{call_id}: get: tags: [Calls] summary: Get call status operationId: getCallStatus description: | Poll a call's current status, duration, recording URL, and per-leg detail. Optional — the status callback covers the standard flow — but useful for a live status UI or debugging. security: [{ ApiKey: [] }] parameters: - $ref: '#/components/parameters/CallId' responses: '200': description: Current call state content: application/json: schema: { $ref: '#/components/schemas/CallStatusResponse' } '404': description: Unknown call_id content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } /calls/{call_id}/add-leg: post: tags: [Calls] summary: Add a participant (transfer / conference) operationId: addCallLeg description: | Dials one more number into an already-active call — a warm transfer or three-way conference. If the added participant does not answer, the existing call is unaffected. security: [{ ApiKey: [] }] parameters: - $ref: '#/components/parameters/CallId' requestBody: required: true content: application/json: schema: type: object required: [number] properties: number: type: string example: "9123456780" description: 10-digit or +91 number to add to the call max_ring_time: type: integer default: 30 responses: '200': description: Participant queued — they join the call when they answer content: application/json: schema: type: object properties: status: { type: string, example: success } message: { type: string, example: "Leg queued" } leg_id: { type: integer, example: 3 } '400': description: Invalid number, or the call has already ended content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } '404': description: Unknown call_id content: application/json: schema: { $ref: '#/components/schemas/ErrorResponse' } /health: get: tags: [Ops] summary: Health check operationId: getHealth description: Unauthenticated liveness check for your monitoring. security: [] responses: '200': description: Service healthy content: application/json: schema: type: object properties: status: { type: string, example: success } '503': description: Service degraded or unavailable /recordings/{filename}: get: tags: [Ops] summary: Download a call recording operationId: getRecording description: | Direct download of a call recording, using the exact URL provided in the status callback / call status response. The unguessable per-call filename is the access control — treat recording URLs as private. security: [] parameters: - name: filename in: path required: true schema: { type: string, example: "crm_5deb67aa-d54d-4536-9a25-a29813b3bddd.wav" } responses: '200': description: Audio file content: audio/wav: { schema: { type: string, format: binary } } '404': description: Recording not found components: securitySchemes: ApiKey: type: http scheme: bearer description: | Your API key, issued during onboarding. Send it as `Authorization: Bearer ` on every request. Keep it in your backend only — never in browser-side code. parameters: CallId: name: call_id in: path required: true schema: { type: string, format: uuid } example: "5deb67aa-d54d-4536-9a25-a29813b3bddd" schemas: InitiateCallRequest: type: object required: [from_number, to_number, uuid] properties: from_number: type: string description: The caller-ID number to present, from the set assigned to your account during onboarding. example: "919800000000" to_number: type: string description: Customer's phone number (10-digit or +91 mobile). example: "9876543210" crm_username: type: string description: Your own ID for the agent, as registered during onboarding. Routes the agent leg to their browser softphone. Mutually exclusive with agent_number. example: "agent.017" agent_number: type: string description: Agent's real phone number (rung first). Mutually exclusive with crm_username. uuid: type: string description: Your own call identifier — echoed back in the status callback for correlation. recording: type: boolean default: true max_ring_time: type: integer default: 30 description: Seconds to ring before giving up. InitiateCallSuccess: type: object properties: status: { type: string, example: success } call_id: { type: string, format: uuid } message: { type: string, example: "Call initiated" } CallStatusResponse: type: object properties: status: { type: string, example: success } call_id: { type: string, format: uuid } uuid: { type: string } call_status: { type: string, enum: [INITIATED, ANSWER, NO_ANSWER, BUSY, FAILED, CANCELED] } duration: { type: integer, description: Talk time in seconds } recording_url: { type: string, nullable: true } start_time: { type: string, format: date-time, nullable: true } end_time: { type: string, format: date-time, nullable: true } legs: type: array description: One entry per participant on the call. items: type: object properties: role: { type: string, enum: [agent, destination] } number: { type: string } status: { type: string, enum: [DIALING, JOINED, LEFT, FAILED] } ErrorResponse: type: object properties: status: { type: string, example: error } error_code: { type: string, example: "INVALID_NUMBER" } message: { type: string }