API documentation
The OTP Flow API has two core endpoints: POST /send-otp delivers a code by SMS and returns an otp_id; POST /verify-otp checks the code your user typed. Base URL: https://otpflow.in. A machine-readable OpenAPI 3.1 spec is available.
Authentication
Create an API key in your dashboard and send it as a Bearer token. Never expose keys in browsers or mobile apps — call the API from your server.
Authorization: Bearer otpf_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/jsonSend OTP
POST https://otpflow.in/send-otp
| Field | Type | Description | |
|---|---|---|---|
| number | string | required | 10 digit Indian mobile number starting with 6–9. No country code. |
| purpose | string | required | 3–40 characters describing why the OTP is sent (e.g. login, registration). Shown in the SMS template. |
| ttl | integer | optional | Time-to-live in minutes, 5–120. Default 10. |
| otp | string | optional | Your own numeric OTP, 3–40 digits. Send as a string to preserve leading zeros. If omitted we generate one. |
| otp_length | integer | optional | Length of the generated OTP (3–40). Used only when otp is not provided. Default 6. |
curl -X POST https://otpflow.in/send-otp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"number":"9876543210","purpose":"login","ttl":10,"otp_length":6}'Response 200
{
"success": true,
"message": "OTP sent successfully",
"otp_id": "otp_k3J9xQ2mP0aZ7Vb1",
"number": "9876543210",
"purpose": "login",
"ttl": 10,
"expires_at": "2026-01-01T10:10:00.000Z",
"cost": 0.4,
"balance": 99.6
}Store the otp_id — you need it to verify. The OTP value itself is never returned by the API.
Verify OTP
POST https://otpflow.in/verify-otp
| Field | Type | Description | |
|---|---|---|---|
| otp_id | string | required | The id returned by /send-otp. |
| otp | string | required | The code entered by your user. |
curl -X POST https://otpflow.in/verify-otp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"otp_id":"otp_k3J9xQ2mP0aZ7Vb1","otp":"123456"}'Success 200
{ "success": true, "message": "OTP verified", "otp_id": "otp_k3J9xQ2mP0aZ7Vb1", "verified": true }Wrong code 400
{ "success": false, "error": "invalid_otp", "message": "Incorrect OTP. 4 attempt(s) left." }Each OTP can be verified once and allows up to 5 attempts before it is locked.
Check balance
GET https://otpflow.in/balance
{ "success": true, "balance": 250.5, "currency": "INR", "rate_per_otp": 0.6 }Errors
Errors return a non-2xx status and a JSON body with success: false, a stable error code and a human readable message.
| HTTP | error | Meaning |
|---|---|---|
| 401 | unauthorized | Missing or invalid API key |
| 402 | insufficient_balance | Wallet balance is lower than your per-OTP rate |
| 403 | forbidden | Account suspended |
| 404 | otp_not_found | Unknown otp_id |
| 409 | otp_already_used | OTP was already verified |
| 410 | otp_expired | TTL elapsed; request a new OTP |
| 422 | validation_error | Invalid or missing fields |
| 429 | rate_limited / too_many_attempts | Slow down or request a new OTP |
| 502 | sms_failed | Carrier rejected the SMS. You were not charged |
Limits & security
- Max 5 OTPs per destination number per 10 minutes, 120 requests/min per account.
- OTPs are encrypted at rest (AES-256-GCM) and compared in constant time.
- Maximum 5 verification attempts per OTP; OTPs are single-use.
Billing
Your wallet is debited your per-OTP rate when an OTP is sent. If the SMS fails to be accepted by the carrier, the amount is refunded immediately. See pricing for volume tiers.
