Cualify.
OutboundInboundBookingCollectionsPricingSecurityBlog
Sign inStart a pilot

Cualify Field Notes

Building India's AI calling stack — in public.

One short essay every other Friday on voice AI, Indian SMB GTM, and what we ship. No spam. Unsubscribe in one click.

Cualify.

AI calling agency for Indian SMBs. Multilingual voice agents, INR billing, DPDP-ready from day one.

Built in Mumbai · Stored in Mumbai

Product

  • Outbound sales
  • Inbound support
  • Appointment booking
  • Collections
  • Voice library
  • Docs

Company

  • Pricing
  • Cualify vs Bolna
  • Changelog
  • Security
  • Blog
  • About
  • Contact

Legal

  • Terms of service
  • Privacy policy
  • Data Processing
  • Acceptable Use
  • 15-day refund

Compliance

  • DPDP Act 2023
  • TRAI / DLT
  • 99.5% SLA
  • Sub-processors
  • Customer KYC
  • Contact DPO

© 2026 Cualify Technologies. All rights reserved.

[email protected]·[email protected]·+91 80 0000 0000

DocsIntegrations

REST API overview

JSON over HTTPS. Bearer-token auth. Designed so a CRM workflow can place a call when a lead arrives, then receive the outcome via webhook.

Base URL

https://app.cualify.in/api/v1

All endpoints are namespaced under /api/v1. We'll version-bump to v2 only on breaking changes; additive changes (new fields, new endpoints) ship without a version bump.

Authentication

Every request needs a Bearer token in the Authorization header. Generate a key in Integrations → API keys; you get a full key starting with cuk_live_. Save it immediately — we never echo the secret again, only the masked prefix.

Authorization: Bearer cuk_live_abc12345_<your-32-char-secret>

Keys are scoped to a single org. Revoke any time from the same page — the next request with that key returns 401 unauthorized within ~30 seconds.

Error envelope

Every non-2xx response uses the same JSON shape:

{
  "error": {
    "code": "invalid_request",
    "message": "to_e164 must be E.164 — e.g. +919812345678",
    "param": "to_e164",
    "details": { /* zod-flat for validation failures */ }
  }
}

Codes are stable strings (invalid_request, not_found, insufficient_credits, forbidden, upstream_error, internal_error). Switch on code in your client, not on the HTTP status — status can change between minor versions; the code won't.

Endpoints

POST /api/v1/calls — place an outbound call

POST /api/v1/calls
Authorization: Bearer cuk_live_...
Content-Type: application/json

{
  "agent_id": "0a8c5a4e-...",          // agent uuid from /playbooks
  "to_e164": "+919812345678",          // recipient
  "from_e164": "+918012345678",        // optional — defaults to your configured ExoPhone
  "language": "hi-IN",                 // optional — defaults to the agent's language
  "max_duration_sec": 300,             // optional — defaults to agent's setting
  "metadata": { "crm_lead_id": "L-7890" }
}

→ 201 Created
{
  "data": {
    "id": "e7b1d6c5-...",
    "status": "queued",
    "agent_id": "0a8c5a4e-...",
    "to_e164": "+919812345678",
    "provider": "bolna",
    "provider_call_id": "boln_...",
    "created_at": "2026-05-31T14:22:11Z"
  }
}

The call status starts as queued and transitions through dialing → in_progress → completed /failed / no_answer / busy. To track: subscribe to call.completed / call.failed webhooks (recommended), or poll GET /api/v1/calls/{id}.

Common pre-call errors: insufficient_credits (top up at /billing), forbidden with reason "No active plan" (renew), not_found on agent_id (verify it's in your org).

GET /api/v1/calls — list calls

GET /api/v1/calls?limit=50&offset=0&status=completed
Authorization: Bearer cuk_live_...

→ 200 OK
{
  "data": [
    {
      "id": "...",
      "status": "completed",
      "direction": "outbound",
      "from_e164": "+918012345678",
      "to_e164": "+919812345678",
      "language": "hi-IN",
      "duration_sec": 142,
      "credits_debited_paise": 1420,
      "recording_url": "https://...",
      "llm_extracted_intent": "demo_booked",
      "llm_sentiment": "positive",
      "llm_outcome": "demo_scheduled_2026_06_03",
      "started_at": "...",
      "ended_at": "...",
      "created_at": "..."
    }
  ],
  "pagination": { "limit": 50, "offset": 0, "total": 1247, "has_more": true }
}

Query params: limit (1–100, default 50), offset (default 0), status (one of the 9 status values). Ordered newest-first.

GET /api/v1/calls/{id} — full call detail

Same fields as the list endpoint plus the full transcript (when status=completed) and per-event timeline (disclosure-played, intent- detected, handoff-requested, etc.).

POST /api/v1/contacts — upsert a contact

Use this from your CRM to keep Cualify's contact book in sync. Idempotent on phone_e164 — repeat calls update the existing row.

POST /api/v1/contacts
Authorization: Bearer cuk_live_...
Content-Type: application/json

{
  "phone_e164": "+919812345678",
  "name": "Riya Sharma",
  "email": "[email protected]",                // optional
  "language_pref": "hi-IN",               // optional
  "metadata": { "lead_source": "google_ads" },
  "consent_capture": "website_form"       // pre_call | website_form | manual | imported
}

→ 200 OK
{
  "data": { "id": "...", "phone_e164": "+919812345678", "name": "Riya Sharma", "updated_at": "..." }
}

GET /api/v1/contacts — list contacts

Paginated. Same limit / offset shape as calls. Filter by consent_capture if you want only the contacts you can legally call.

POST /api/v1/webhooks — register an outbound webhook

Subscribe a HTTPS endpoint to receive event deliveries. Cualify signs every payload with HMAC-SHA256 so you can verify authenticity. Full details: Webhook events + payloads.

POST /api/v1/webhooks
Authorization: Bearer cuk_live_...
Content-Type: application/json

{
  "url": "https://your-app.com/cualify-webhook",
  "events": ["call.completed", "call.failed"]
}

→ 201 Created
{
  "data": { "id": "...", "url": "...", "events": ["call.completed", "call.failed"], "active": true, "created_at": "..." },
  "secret": "abcdef0123...64chars"   // returned ONCE — store it now
}

GET /api/v1/webhooks — list registered endpoints

Returns each registration with its last delivery timestamp + last HTTP status. Useful for monitoring delivery health.

DELETE /api/v1/webhooks/{id} — remove a webhook

Immediate. No 30-day soft-delete — once you DELETE, no further events ship to that URL.

Rate limits

  • 120 requests / minute / org across all v1 endpoints (sliding window).
  • Rate-limited responses: 429 too_many_requests with a Retry-After header in seconds.
  • Higher limits available on Scale / Enterprise — email [email protected].

cURL quickstart

# Place a call
curl -X POST https://app.cualify.in/api/v1/calls \
  -H "Authorization: Bearer cuk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "0a8c5a4e-...",
    "to_e164": "+919812345678"
  }'

# Poll until completed
curl https://app.cualify.in/api/v1/calls/<id> \
  -H "Authorization: Bearer cuk_live_..."

SDKs

We don't ship an official SDK yet — the API surface is small enough that customers wire it directly. If you build a Node / Python / Ruby wrapper and want it listed here, send a PR or email [email protected].

What's next?

  • Webhook events + payloads — sign verification + retry policy
  • Zoho + HubSpot setup — pre-built connectors

Got a question this page didn't answer? Ping us on WhatsApp or email support. We typically reply in under 2 hours.