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.