# How Voice OTP Works: From API Call to Spoken Code (2026)

> How a voice OTP call works step by step: the API request, code generation, the spoken template, press-1 confirmation, retries, rate limits, the callback and what each call costs.

Source: https://edesy.in/blog/how-voice-otp-works-2026

**A voice OTP is a one-time code delivered by phone call instead of SMS.** Your server asks Edesy to call a
number, Edesy calls it, reads the code aloud from your template (with optional press-1 confirmation), then
posts the result to your callback. The user types the code into your app as usual. It costs Rs 1.50 per
answered minute, and unanswered calls are free.

> **Try the flow on your own phone:** [Open the Voice OTP portal](https://voice-otp.edesy.in/?utm_source=edesy.in&utm_medium=blog&utm_campaign=how-voice-otp-works-2026), create an API key and call yourself.

## The flow in seven steps

1. **The user asks for a code.** They enter a phone number on your login, sign-up or checkout screen.
2. **Your server calls the API.** `POST https://voice-api.edesy.in/v1/voice-otp/send` with the phone number
   and, optionally, your own code.
3. **Checks run.** Edesy checks your wallet balance and the per-number rate limit. If either fails, the API
   returns an error straight away (`402 insufficient_balance` or `429 rate_limit_exceeded`) and no call is made.
4. **The call goes out.** The response comes back with an ID, the code and the status `initiated`.
5. **The user answers and hears the code.** The template is spoken by text-to-speech, with `{{otp}}`
   replaced by the code. If press-1 confirmation is on, the user is asked to press a key.
6. **The call ends.** Edesy records the result: delivered, no-answer, busy or failed, plus the key pressed,
   duration and hangup cause.
7. **Your callback gets the result.** Your server can show "Call me again" or switch to another channel
   if the call wasn't answered.

Your app still owns verification: you store the code (or a hash of it) with an expiry, and compare it
with what the user types.

## What goes into the request

| Field | Required | What it does |
|---|---|---|
| `phone_number` | Yes | 10-digit Indian number; a leading `+91`, `91` or `0` is removed |
| `otp_code` | No | Your own 4 to 10 character code; leave it out for a generated 6-digit code |
| `message_template` | No | Spoken text with `{{otp}}`; falls back to your saved default |
| `language` | No | For example en-IN, en-US or Hindi |
| `gather_enabled` | No | Turns on press-1 confirmation; the key comes back as `dtmf_response` |
| `callback_url` | No | Where the result is posted; overrides your default |
| `external_id` | No | Your own reference, returned in the callback and searchable in logs |
| `metadata` | No | Any JSON you want echoed back |

Authentication is `Authorization: Bearer sk_...`. The full reference is in the
[Voice OTP docs](https://edesy.in/docs/voice-otp).

## The spoken message

The template is plain text. A good OTP template does three things: says who is calling, reads the code
slowly, and repeats it.

- "Your Acme login code is {{otp}}. I repeat, {{otp}}."
- Hindi: "Aapka Acme login code hai {{otp}}. Dobara sunein, {{otp}}."

Text-to-speech reads each digit separately, so the user can write it down. Keep the brand name short and
don't add offers or extra lines. Every sentence adds seconds to the call, and OTP calls should stay
transactional.

You set a default template, language and voice once in the portal, then override per request only when you
need to (for example, a different brand in a multi-brand app).

## Press-1 confirmation

With `gather_enabled` on, the call asks the user to press a key after hearing the code. The key comes back
as `dtmf_response` in the callback and log. This tells you a person heard the code, not just that the call
connected. It's useful for high-value actions such as changing bank details or approving a payout, where
you want a little more proof.

Leave it off for routine logins: it adds a few seconds and one more step.

## Retries, rate limits and expiry

| Control | What it does | Where you set it |
|---|---|---|
| Retries on no-answer | Calls again if the first attempt isn't answered | Settings (0 to 3) |
| Rate limit per number | Caps how many OTP calls one number can get per hour | Settings |
| Default callback URL | Where results go when the request doesn't name one | Settings |
| Code expiry | How long the code is valid | Your app |

The rate limit matters more than it looks. Without it, anyone can type a victim's number into your login
form and make your app call them repeatedly. That annoys the victim and costs you money. Keep the limit
low.

## The callback and logs

When the call finishes, Edesy posts JSON like this to your callback URL:

```json
{
  "id": "5b1c2d3e-0000-4000-8000-000000000000",
  "external_id": "login-8812",
  "phone_number": "9876543210",
  "status": "delivered",
  "dtmf_response": "1",
  "duration_sec": 18,
  "price": "1.5000",
  "completed_at": "2026-10-28T10:15:30Z"
}
```

Status is one of `delivered`, `no-answer`, `busy` or `failed`. In the portal, each call has a log with a
timeline (initiated, ringing, answered, completed), the key pressed and the hangup cause, so support can
answer "I never got the call" with facts.

## What it costs

| Item | Price |
|---|---|
| Answered OTP call | Rs 1.50 per minute, 1-minute minimum |
| Unanswered, busy or failed call | Not charged |
| Monthly fee | None |
| GST | 18% extra, with GST invoices |

Calls are paid from a prepaid INR wallet. The price of each call is in the callback and the log, so you can
reconcile per user or per `external_id`. See [Voice OTP pricing](https://edesy.in/voice-otp/pricing).

## Where voice OTP fits

Voice OTP is a strong fallback for SMS and a strong primary channel for landline and feature-phone users.
For the cost and DLT comparison, read [voice OTP vs SMS OTP](https://edesy.in/blog/voice-otp-vs-sms-otp-india-2026). For
the code to wire it into an existing login, read
[add voice OTP fallback to your login](https://edesy.in/blog/add-voice-otp-fallback-to-login-2026). If you're comparing
against missed-call verification, see [missed call verification vs OTP](https://edesy.in/blog/missed-call-verification-vs-otp).

More on the product: [Voice OTP](https://edesy.in/voice-otp).

[Create a Voice OTP account](https://voice-otp.edesy.in/?utm_source=edesy.in&utm_medium=blog&utm_campaign=how-voice-otp-works-2026)
and send a test code to your own phone.

## Frequently Asked Questions

### What is a voice OTP?

A voice OTP is a one-time password delivered by an automated phone call. The user answers, hears the code read aloud (usually twice), and types it into your app or website, the same as an SMS code.

### Who generates the code, my app or Edesy?

Either. Send your own 4 to 10 character code in otp_code, or leave it out and Edesy generates a 6-digit code and returns it in the response. Your app still checks the code the user types.

### What does the user hear?

Whatever template you set, with {{otp}} replaced by the code, for example: Your Acme code is 4 8 2 9 1 3. I repeat, 4 8 2 9 1 3. You choose the language, and you can add a press-1 confirmation step.

### What happens if the user doesn't answer?

Edesy can retry the call on no-answer (you set the retry count in settings). If it still isn't answered, the callback reports no-answer or busy, and the unanswered call is not charged.

### How much does one voice OTP cost?

Rs 1.50 per answered minute with a 1-minute minimum, plus 18% GST. A short OTP call finishes inside a minute, so most cost Rs 1.50. There is no monthly fee.

### How do I know the call worked?

Edesy posts the result to your callback URL with a status (delivered, no-answer, busy or failed), the key pressed if confirmation is on, the duration and the price. The same details appear in the call log with a timeline and hangup cause.
