API v1 · reviewed 8 September 2026
From your systems
to the next conversation.
Capture leads, read your workspace wallet and receive signed lead events. These examples describe the endpoints implemented in this build.
Base URL: https://callvani.com/api/v1. Use your workspace API key with this production endpoint. There is no public call-creation API in v1.
01 / Send your first lead
- Sign in and configure the lead source in your workspace.
- Open Integrations & API → API keys. Create a server-side key with
leads:write. - Save the secret when shown, then run this request on your server.
# Set these on your server. Never expose API keys in browser code.
export CALL_VANI_ORIGIN="https://callvani.com"
export CALL_VANI_API_KEY="vaani_live_YOUR_KEY"
curl --fail-with-body "$CALL_VANI_ORIGIN/api/v1/leads" \
-H "Authorization: Bearer $CALL_VANI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"sourceType":"manual","externalLeadId":"crm-1042",
"name":"Aarav Mehta","phone":"+919876543210",
"productInterest":"Product demo","notes":"Requested a callback"}'Success is HTTP 201 with { "data": { …lead } }. Scoring is currently rule-based. A queued call job is not proof that a telephone call was placed; provider setup, consent, balance and execution gates still apply.
02 / Authentication & scopes
Send Authorization: Bearer vaani_live_…. Keys are shown once, hashed at rest, customer-scoped and revocable. Create separate keys per server and give each only the required scope. Never put a private key in a website, mobile bundle, URL or log.
leads:readleads:writecredits:readcalls:write is reserved; no v1 route consumes it. /api/app/* and /api/admin/* are session-authenticated interfaces, not public bearer-key APIs. Revoke and replace a leaked key immediately.
03 / Lead API
/api/v1/leadsleads:write| Field | Contract |
|---|---|
| sourceType | Required: meta_ads, google_ads, website_form or manual |
| name | Required string; trimmed length at least 2 |
| phone | Required valid phone number; stored in E.164 format. Local numbers default to India; other countries need an explicit country code. |
| externalLeadId | Optional stable source ID for deduplicating the lead row |
| email / campaignName / productInterest / notes | Optional strings |
| estimatedValue | Optional nonnegative number, rounded to an integer |
POST returns camelCase fields including capturedAt, summary, callJobId, opportunityId; source is the source display name. A repeated external ID returns the existing lead ID, but is not a general idempotency guarantee: webhook events may repeat. Deduplicate in your receiver too.
/api/v1/leadsleads:readReturns { data: [...] } with the newest 100 leads. No pagination or query filters are implemented. List fields include id, name, phone, email, status, score, intent, ai_summary, captured_at, source. Here source is the source type. Note the casing difference from POST.
curl "$CALL_VANI_ORIGIN/api/v1/leads" -H "Authorization: Bearer $CALL_VANI_API_KEY"04 / SMS send
/api/app/smsWorkspace session · campaigns.manageSMS is available inside the authenticated app for workspaces that connect a BYO sms_gateway integration. This endpoint holds wallet credits before sending, finalizes when the provider accepts the message and releases the hold for sandbox/no-provider or provider rejection.
# Session-authenticated app endpoint, for workspace users.
# Public bearer-key SMS v1 is intentionally not exposed yet.
curl --fail-with-body "$CALL_VANI_ORIGIN/api/app/sms" \
-H "Content-Type: application/json" \
-b "YOUR_APP_SESSION_COOKIE" \
-d '{"to":"+919876543210","message":"Hi Aarav, your Call Vani demo is confirmed."}'First version charges one sms_message event per submitted message. Provider segment-count reconciliation belongs in the delivery webhook and is not exposed as a public API yet. If no SMS gateway is connected, the API returns sandbox delivery and releases the wallet hold.
05 / Wallet balance
/api/v1/creditscredits:readcurl "$CALL_VANI_ORIGIN/api/v1/credits" -H "Authorization: Bearer $CALL_VANI_API_KEY"Example response:
{"data":{"balance":100,"low_balance_threshold":20,"updated_at":"2026-09-08 10:00:00"}}When no wallet exists, balance and threshold are zero and updated_at is omitted. A credit is a usage unit, not one rupee or one minute. Do not derive a charge from balance alone.
In-app usage charges use a hold → finalize → release model. Current billable ids include inbound call minute, outbound call minute, AI voice minute, test call, WhatsApp marketing template, WhatsApp utility/authentication template, WhatsApp service reply, SMS message, email send and recording storage. Workspace admins can view the exact live rate card in Billing.
06 / Website forms & widgets
Create an active form under Lead Capture. Configure its allowed origins, fields, logo, theme and display trigger. The generated embed uses a public form key, never an API key.
<script src="https://callvani.com/api/widget/YOUR_PUBLIC_FORM_KEY" defer></script>/api/forms/{publicKey}/leadsPublic form key + configured origin policySubmit JSON matching the form fields; name and phone are required. Success: {"accepted":true,"leadId":"…","score":0}. Active form required; 64 KiB payload cap and 20 requests/hour per form/fingerprint. Allowed origins are not authentication: a request without Origin is currently permitted. Add edge bot protection for public campaigns.
Widget JavaScript returns 404 for a missing/unpublished form and 422 for invalid configuration. Saving an active form updates its live embed; a separate draft/publish snapshot is not yet supported. Voice widgets have a separate setup flow and are not the lead-form endpoint.
07 / Signed lead webhooks
Add an HTTPS receiver under Integrations & API → Webhooks. The currently emitted events are lead.created and lead.qualified (score ≥75), from v1 lead POST. Call, appointment and low-credit event names may appear in settings but are not emitted in this build. Forms and provider ingestion do not yet emit these outbound events.
Content-Type: application/json
X-Vaani-Event: lead.created
X-Vaani-Signature: t=UNIX_SECONDS,v1=HEX_DIGEST
{"id":"event_…","type":"lead.created","createdAt":"2026-09-08T04:30:00Z","data":{"lead":{"id":"lead_…"}}}import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyWebhook(rawBody, header, secret) {
if (!secret || typeof header !== 'string') return false;
const match = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(header);
if (!match) return false;
const timestamp = Number(match[1]);
if (!Number.isSafeInteger(timestamp) ||
Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
const expected = createHmac('sha256', secret)
.update(match[1] + '.').update(rawBody).digest();
const received = Buffer.from(match[2], 'hex');
return received.length === expected.length &&
timingSafeEqual(received, expected);
}
// Pass the exact raw request bytes BEFORE parsing JSON.
// Verify X-Vaani-Signature, then deduplicate payload.id durably.
// Return 2xx only after your queue has durably accepted the event.Failed initial deliveries enqueue up to eight worker attempts with exponential backoff and a dead-letter state. A running job processor is required. Retries keep the event ID and body but receive a fresh signature timestamp. Acknowledge quickly, process asynchronously, and use durable event-ID deduplication. Once a lead is saved, webhook setup or dispatch errors do not turn the accepted lead into a failed API request. HTTP 201 confirms lead acceptance, not webhook delivery; inspect delivery status separately instead of resubmitting the lead.
08 / Errors, retries & limits
- 400: invalid lead JSON, fields, source or ingestion failure. Response:
{"error":"message"}. - 401: missing, invalid, revoked or insufficient-scope API key.
- 408 / 413: lead API request body read timed out or exceeded 64 KiB. These requests are rejected before ingestion.
- 404 / 413 / 429: public form missing, body too large or rate limited. Form preflight refusal can return 403.
- 5xx / network timeout: acceptance may be uncertain. Check the result before retrying a write; use stable external IDs and receiver deduplication.
Public v1 currently has no per-key rate limit, pagination, key expiry or stable machine error-code contract. Lead v1 validates phone numbers with the calling parser and stores them in E.164 format. Local numbers default to India; numbers elsewhere need an explicit country code. A valid format does not establish reachability or permission to call. Its JSON request body is limited to 64 KiB with a five-second total read deadline, including chunked requests without Content-Length. Keep keys server-side and apply a gateway rate limit before public production exposure.
Own-voice profiles · record, review and preview
Open Voice Profiles → Create my voice in the workspace. Choose Hindi, English or Hinglish and read the on-screen script, or upload your own WAV/MP3. Keep a copy with Download original, then optionally compare the original and noise-cleaned sample. Cleanup uses ElevenLabs Audio Isolation and applies to the uploaded sample; it is separate from live telephone noise cancellation.
Creating a voice requires explicit own-voice and provider processing consent. Any required provider verification and platform consent review happen before the profile can be used. After approval, listen to the actual provider previews, link the profile to your agent and test before publishing. A voice profile does not translate your recording or create a video avatar.
Recording and local playback are not billed. Voice creation, optional sample cleanup and generated speech are separate operations; review your configured credit quote before processing. An unconfigured price blocks processing rather than assuming it is free. CallVani credits are not ElevenLabs credits. Provider subscriptions, quotas and charges are managed by the platform administrator.
These actions use authenticated workspace routes, not public v1 API keys. Samples remain private and permission-checked. A failed request keeps the original sample available; check the reported upload, processing or creation error before retrying.
See the own-voice workflow and pricing09 / Billing units & payment state
New monthly plans: Launch ₹2,999; Growth ₹9,999; Scale ₹24,999. Usage is separate. New credit packs cost ₹1.90/credit; existing subscriptions and prepaid balances remain unchanged. Taxes, carrier charges, numbers and premium provider costs are separate.
- Text playground: 10 credits per turn.
- Realtime browser playground:
realtime_session_v1, 10 credits per session reservation—not per minute. Known rejection releases the reservation; uncertain acceptance requires review and never triggers automatic paid fallback. - Current Exotel completed-call handler: 10 credits per started minute, minimum one minute. Other voice paths are not uniformly metered; production usage settlement is still under validation.
Payment acceptance is not fulfillment. Credits are granted after a verified paid event; delayed methods wait for payment success. Do not add credits based on a browser redirect. The ₹19 standard AI-minute target and provider-cost scenarios are planning figures until each voice route has validated duration metering.
10 / Connect the right account
Customers connect their own accounts from Integrations; platform operators configure provider credentials privately. Never share platform secrets in client embeds or public documentation.
- Meta leads: provider callback under
/api/integrations/meta/leads?workspace=…; verification challenge and raw-body signature validation. Requires the workspace’s configured assets. - Google lead forms: workspace-scoped provider callback with the configured lead-form key.
- WhatsApp: verify customer business/phone assets and opt-in. Outside the rolling customer-service window, approved templates are required. Marketing is not generally free.
- Voice: configure the provider, agent, language, consent and carrier. Browser microphone permission and secure HTTPS are required outside localhost.
Need a key or connection? Open your workspace. Review the policies below before processing customer data.