API docs

Quickstart

By the end of this page you’ll have listed your calls, subscribed to webhooks, verified a signature, and placed a real AI call from one curl.

1. Get an API key

In the dashboard, open Integrations → API Keys → Create key. Choose Full access. The key is shown once — store it now.

export VCA_API_KEY="vca_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

2. Your first request

curl -H "Authorization: Bearer $VCA_API_KEY" \
  "https://api.voicecallingai.com/v1/calls?limit=2"
{
  "data": [
    {
      "id": "789f04a1-74f9-4e0d-9715-a55361ad5d63",
      "direction": "inbound",
      "from": "+91XXXXX22338",
      "to": "+912264230136",
      "status": "completed",
      "agent_id": "c071a030-8601-4e73-bfb2-872d49b42f38",
      "contact_id": "6fec7323-9c4e-4d2a-8c11-2f1b6f3f2a10",
      "outcome": "callback_requested",
      "talk_seconds": 52,
      "created_at": "2026-06-04T18:48:21.512Z",
      "external_ref": null
    }
  ],
  "has_more": true
}

3. Subscribe to webhooks

curl -X POST https://api.voicecallingai.com/v1/webhook-subscriptions \
  -H "Authorization: Bearer $VCA_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "name": "My CRM",
    "url": "https://example.com/hooks/voicecallingai",
    "events": ["call.answered", "call.completed", "webhook.test"]
  }'

The 201 response includes signing_secret (whsec_…) — shown once. Store it next to your key.

Send a test event and watch your endpoint:

curl -X POST https://api.voicecallingai.com/v1/webhook-subscriptions/SUB_ID/test \
  -H "Authorization: Bearer $VCA_API_KEY"

4. Verify the signature (always)

Every delivery carries X-VCA-Signature: t=<unix>,v1=<hex> where v1 = HMAC_SHA256(signing_secret, "{t}." + raw_body). Verify before trusting:

# pip install flask
import hmac, hashlib, time
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = b"whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

@app.post("/hooks/voicecallingai")
def hook():
    sig = dict(p.split("=", 1) for p in request.headers["X-VCA-Signature"].split(","))
    expected = hmac.new(SECRET, f"{sig['t']}.".encode() + request.get_data(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig["v1"]):
        abort(401)
    if abs(time.time() - int(sig["t"])) > 300:
        abort(401)  # replay protection
    event = request.get_json()
    print(event["type"], event["data"].get("outcome"))
    return "", 200  # respond 2xx fast; process async

5. Make a phone ring

curl -X POST https://api.voicecallingai.com/v1/calls \
  -H "Authorization: Bearer $VCA_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "agent_id": "YOUR_AGENT_ID",
    "to": "+91XXXXX22338",
    "variables": {"name": "Harsh"},
    "external_ref": "crm-lead-4821"
  }'

You get 202 with a call request (status: "queued"). The platform dials from your workspace number (e.g. +912264230136), the AI agent holds the conversation, and seconds after hangup your endpoint receives call.completed:

{
  "id": "6a4113f9-2e1c-4b7a-9f3e-8d2c5a1b0e44",
  "type": "call.completed",
  "created_at": "2026-06-05T14:34:52.118Z",
  "api_version": "2026-06-04",
  "data": {
    "call_id": "e9d30090-1f2b-4c3d-8a4e-5b6c7d8e9f00",
    "direction": "outbound",
    "from": "+912264230136",
    "to": "+91XXXXX22338",
    "agent_id": "c071a030-8601-4e73-bfb2-872d49b42f38",
    "contact_id": "8a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9",
    "campaign_id": null,
    "status": "completed",
    "hangup_cause": "normal_clearing",
    "timings": {
      "queued_at": "2026-06-05T14:32:55Z", "dialed_at": "2026-06-05T14:32:57Z",
      "answered_at": "2026-06-05T14:33:01Z", "ended_at": "2026-06-05T14:34:10Z",
      "ring_seconds": 4, "talk_seconds": 69, "total_seconds": 75
    },
    "credits_charged": 12.5,
    "outcome": "not_interested",
    "disposition": null,
    "summary": {
      "outcome": "not_interested",
      "confidence": 0.9,
      "next_action": "Do not follow up.",
      "customer_name": "Harsh",
      "key_interests": [],
      "key_objections": ["not relevant right now"],
      "scheduled_for": null
    },
    "external_ref": "crm-lead-4821"
  }
}

Use external_ref to join the result back to your own records: GET /v1/calls?external_ref=crm-lead-4821.

That’s the whole loop. Next: Concepts for the call-request lifecycle, or the reference.