Launch offerYour ₹1,499 setup fee is on us — use code WELCOMEFREE at checkout. Ends 10 Oct.Start free

NaCaller API Reference

Build with NaCaller using an AI coding tool

This page is for language models and for the developers working with them. It says which NaCaller integration fits which job, gives you prompts to paste into Cursor, Lovable, Bolt, Replit, v0 or Claude Code, and lists what the API does not do, so nothing gets invented.

Which integration for which job

The jobUseHow it is secured
Prove a user owns the Indian mobile number they typedMiss OTP (missed call), with Voice OTP as the fallbackAPI key, server side
Read a code to a user who cannot give a missed callVoice OTPSame API key
Get every finished call (outcome, captured fields, recording, transcript) into a CRM or databaseCall results webhookSigning secret, verify each request
Get every finished call into a spreadsheet with no codeGoogle Sheets connectorShare the sheet, no keys
Send a new lead (form, ad, Zapier) so an agent calls them within secondsInstant Leads hookA token in the URL
Learn that a number just verified without pollingVerification callbackSigning secret

Machine-readable files

Give an assistant one of these instead of asking it to browse:

  • llms.txt: The index: what NaCaller is, plans, every page, and every docs page, in the standard llmstxt.org format.
  • llms-full.txt: The whole developer API in plain text (endpoints, fields, limits, errors), plus pricing, comparisons and FAQs. Best single file to give an assistant.
  • This documentation: Human-readable reference with copy-paste curl, Node and Python samples.

Ground rules for an assistant

  1. The key is a server secret. Verification keys look like nck_live_… and live in NACALLER_KEY. Never place one in client code, a VITE_ or NEXT_PUBLIC_ variable, a mobile app or a public repository. Every call goes through your backend.
  2. One base URL, one header. https://api.nacaller.com/api/verify with Authorization: Bearer <key>. Errors are JSON, { "error": "…" }, with an ordinary HTTP status.
  3. Verify webhooks on the raw body with a constant-time compare. The two webhooks use different headers: X-MissOTP-Signature for verification and X-NaCaller-Signature for call results.
  4. De-duplicate. A delivery can be retried, so the same event can arrive twice. Key on requestId or call.id.
  5. Indian mobiles only for verification: ten digits starting 6 to 9.
  6. Do not invent endpoints or fields. If something is not in this documentation or in llms-full.txt, it does not exist.

What the API does not do

  • There is no public API to create or edit agents, campaigns or contact lists, or to start an outbound call. These are done in the dashboard. Contacts can also be imported from a CSV, and leads can be posted in with the Instant Leads hook.
  • The only API-key endpoints are the verification ones (Miss OTP and Voice OTP). The lead and WhatsApp hooks use a token in the URL.
  • NaCaller sends exactly two kinds of outbound event: the verification callback and the call results webhook.
  • There is no sandbox: the free credits run on the live line, so test with your own phone.
  • There is no SDK package and no OpenAPI file yet. Every endpoint is plain HTTPS with JSON.

Prompts to paste

Each prompt is complete: it names the endpoints, where the secret lives, what to build and what not to invent.

Add phone verification

Prompt: phone verification (Miss OTP and Voice OTP)
Add mobile-number verification to this app using the NaCaller API.
Miss OTP (missed call) is the main method; Voice OTP (we call the user and read a code) is the fallback.
Full reference: https://nacaller.com/docs — plain-text summary: https://nacaller.com/llms-full.txt

SECRETS — read these carefully
- The API key is in the environment variable NACALLER_KEY (it looks like nck_live_…).
- It is a SERVER secret. Never put it in client code, a VITE_/NEXT_PUBLIC_ variable, or the browser bundle.
  Every NaCaller call must go through my backend (an API route, server action, or edge/serverless function —
  on Supabase use an Edge Function and store the key with `supabase secrets set`).

BASE URL: https://api.nacaller.com/api/verify
AUTH HEADER on every call: Authorization: Bearer <NACALLER_KEY>
Errors are JSON: { "error": "human-readable message" } with a normal HTTP status.

BUILD THESE SERVER ENDPOINTS (names are yours to choose):
1. POST /verify/start { phone }
   - Validate phone: keep the last 10 digits; must match /^[6-9]\d{9}$/ (Indian mobile).
   - Call POST https://api.nacaller.com/api/verify/start with JSON { "phone": "<10 digits>", "reference": "<my user id>" }.
   - Response: { requestId, phone, callNumber, status: "pending", expiresAt, expiresIn, instruction }.
   - Save requestId against the signed-in user/session on the server. Return callNumber, instruction, expiresAt, requestId.
2. GET /verify/status/:requestId
   - Only for a requestId saved for this user. Call GET https://api.nacaller.com/api/verify/status/<requestId>.
   - Response: { requestId, phone, reference, status: "pending" | "verified" | "expired", verified, verifiedAt, expiresAt }.
   - When verified is true, mark this user's phone as verified IN MY DATABASE (server side) and return { verified: true }.
3. POST /verify/voice { phone }   (the fallback)
   - Call POST https://api.nacaller.com/api/verify/start with { "phone", "reference", "method": "voice", "language": "en" } ("hi" for Hindi, "te" for Telugu).
   - Response: { id, method: "voice", status: "calling", expiresAt }. Save id against the user.
   - We ring the number and read a 6-digit code twice. The verification is valid for 10 minutes from /start. Until the user answers, /check returns 400 "not answered yet" (no attempt used) and /status says "pending".
4. POST /verify/check { id, code }
   - Call POST https://api.nacaller.com/api/verify/check with { "id", "code" }.
   - 200 { verified: true, id, phone, reference } → mark the phone verified server side.
   - 400 { verified: false, error, attemptsLeft } → show error and attemptsLeft. 5 attempts max.
   - 410 → code expired; offer to call again.

UI (one screen, mobile first)
- Step 1: a phone input ("Your mobile number", +91 prefix shown, 10 digits) and a "Verify" button.
- Step 2 (missed call): show the instruction text, e.g. "Give a missed call to <callNumber> from <phone>. You can cut the call as soon as it rings."
  Show callNumber large, as a tel: link ("Tap to call") on phones. Show a countdown to expiresAt (5 minutes).
  Poll my status endpoint every 2–3 seconds; stop on verified, expired, or when the user leaves the screen.
  On verified: a success state and continue. On expired: "Time ran out" + a "Try again" button.
- After ~20 seconds on step 2, show a secondary button: "Can't give a missed call? Get a call instead".
  It calls my /verify/voice endpoint, then shows a 6-digit code input (inputmode="numeric",
  autocomplete="one-time-code") with the text "We're calling <phone> now. Enter the 6-digit code you hear."
- Show the API's error message as-is for 400, 402 (out of credits — also log it for me), 429 (too many attempts; ask them to wait).
- Disable buttons while a request is in flight. Never show or log the API key.

OPTIONAL: instead of polling, receive a webhook. NaCaller POSTs JSON
{ event: "verification.verified", requestId, phone, reference, verifiedAt, timestamp } (voice ones also carry method) to my HTTPS callback URL
with header X-MissOTP-Signature = hex HMAC-SHA256 of the RAW request body, keyed with the signing secret in NACALLER_WEBHOOK_SECRET.
Verify it with a constant-time compare against the raw bytes (not re-serialised JSON), reject if timestamp
(milliseconds) is more than 5 minutes old, then reply 2xx. Keep polling as a backup.

Do not invent other NaCaller endpoints or fields. Write the code, then list the env vars I need to set.

Receive finished-call results

Prompt: call results webhook
Add an endpoint to this app that receives finished-call results from NaCaller.
Full reference: https://nacaller.com/docs/call-results — plain-text summary: https://nacaller.com/llms-full.txt

WHAT NACALLER DOES
- When a call ends, NaCaller waits about 45 seconds (so the transcript and outcome are ready) and then POSTs JSON to my HTTPS URL.
- Body: { event: "call.completed", call: { id, direction, status, outcome, outcomeReason, startedAt, completedAt, durationSeconds, to, from, recordingUrl, transcript, captured, tags }, contact: { id, name, phone } | null, campaign: { id, name } | null, agent: { id, name } | null }.
- "captured" is an object of the fields my campaign asked the agent to find out (for example { course: "B.Tech CSE" }); it can be empty.
- Header X-NaCaller-Signature: "sha256=" + hex HMAC-SHA256 of the RAW request body, keyed with the signing secret from NaCaller → Settings. Store that secret in NACALLER_CALL_SECRET.

BUILD
1. A POST route at a public HTTPS URL that reads the RAW body (not parsed JSON).
2. Verify the signature with a constant-time compare. Reject with 401 if it does not match.
3. De-duplicate on call.id (a retry after a failure can deliver the same call twice).
4. Save what I need (outcome, captured, recordingUrl, transcript), then reply with any 2xx within 8 seconds. Anything else is retried 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours later, then given up.

RULES
- The URL must be https and a public host. Redirects are not followed. Localhost does not work: use a tunnel while developing.
- Never log the signing secret. Do not invent other NaCaller endpoints or fields.
Write the code, then list the env vars and the URL I must paste into NaCaller → Settings → "Send call results".

Related

Stuck on the NaCaller API? Ask us.

A developer reads every message and replies by email, usually within one business day.

Call meStart free