// otp
OTP verification
Two calls handle phone verification. POST /v1/otp generates a 6-digit code and sends it; POST /v1/otp/verify checks what the user typed. You never see or store the code.
Send a code
curl -X POST https://sms-api.ekrasunya.com/v1/otp \
-H "X-API-Key: eksms_..." \
-H "Content-Type: application/json" \
-d '{
"to": "9818000000",
"channels": ["device", "sms"],
"brand": "Acme"
}'
The response names the first channel tried. Keep to for the verify call.
{
"status": "success",
"statusCode": 201,
"message": "OK",
"data": {
"id": "6f1c2a8e-3b4d-4e5f-9a10-2b3c4d5e6f70",
"to": "9818000000",
"channel": "device",
"expiresInMinutes": 10
}
}
The recipient receives:
Acme: Your verification code is 481920. It expires in 10 minutes. Do not share it with anyone.
Parameters
| Field | Required | Description |
|---|---|---|
| to | Yes | Nepali mobile number, 10 digits starting with 9. A +977 or 977 prefix is accepted. |
| channels | No | Channels to try, in order: sms (the A2P route) and device (your enrolled phones running the EK SMS Gateway app). Default ["sms"]. |
| senderId | No | Sender ID for the sms channel, same rule as Send a message. |
| brand | No | Your app or company name, up to 30 characters, shown before the code. |
Channel fallback
The code goes out on the first channel in channels. It moves to the next channel when the current one reports the message failed, or when no phone has picked up a device send within 60 seconds.
Each fallback sends a new code, and the earlier one stops working. A user holding an older code sees OTP_INVALID and should use the latest message.
Cheapest first
["device", "sms"] tries your own SIM first and only pays the A2P rate when the phone is offline or the send fails.Check a code
curl -X POST https://sms-api.ekrasunya.com/v1/otp/verify \
-H "X-API-Key: eksms_..." \
-H "Content-Type: application/json" \
-d '{ "to": "9818000000", "code": "481920" }'
A correct code returns verified: true once. Every failure returns 400 with code OTP_INVALID and a message you can show the user: wrong code, expired, already used, or too many attempts.
Limits
| Rule | Value |
|---|---|
| Code length | 6 digits |
| Valid for | 10 minutes |
| Wrong attempts per code | 5, then request a new code |
| Codes per number | 5 per rolling 24 hours (OTP_RATE_LIMITED, 429) |
| Codes that count | Only the newest code for a number. A new request replaces the old one. |
Billing
Each channel attempt is billed as one ordinary message at that route's rate. A code that falls back from device to sms is charged for both sends; a send that fails is refunded. Verify calls are free.
Sends pass the same checks as POST /v1/messages: API key scope messages:send, KYC, sender ID, balance, daily spend limit and the new-account limits. A request rejected by one of these does not count toward the daily limit.
Next: Error codes · Delivery & webhooks