OTP API
Deliver secure one-time passwords to any mobile number. Configurable length, expiry, and type — with automatic rate limiting and brute-force protection built in so you can focus on your product.
Multiple PIN Types
Choose NUMERIC (4–8 digits), ALPHANUMERIC (6–12 chars), or ALPHABETIC codes to match your security requirements.
Configurable Expiry
Set expiry from 1 minute to 24 hours with automatic invalidation — no manual cleanup needed.
Built-in Rate Limiting
Automatic cooldown periods and max-attempt enforcement prevent brute force and OTP spam.
Metadata Support
Attach custom context (userId, sessionId, IP) to each OTP request for audit trails.
Resend & Cooldown
Resend OTPs after a configurable cooldown without creating duplicate active codes.
PCI-DSS Compliant
Codes are encrypted at rest with automatic expiration and brute-force protection built in.
5 min
Configurable 1–1440 min
3
Default (max 10)
60 s
Between resends
4–12
Chars (type-dependent)
Authentication
Request OTP
Call /v1/otp/request — Sendexa generates a secure code and sends it by SMS.
curl -X POST 'https://api.sendexa.co/v1/otp/request' \-H 'Content-Type: application/json' \-H 'Authorization: Basic YOUR_DASHBOARD_BASE64_TOKEN' \-d '{"phone": "0244123456","from": "MyBrand","message": "Your code is {code}. Valid for {amount} {duration}.","pinLength": 6,"pinType": "NUMERIC","expiry": { "amount": 5, "duration": "minutes" }}'
{"success": true,"data": {"id": "otp_abc123xyz","expiresAt": "2024-01-15T10:35:00.000Z"}}
User enters the code
Display an input in your UI. Store the id from step 1 — you'll need it for verification.
Verify the code
Call /v1/otp/verify with the OTP ID and the code the user entered.
curl -X POST 'https://api.sendexa.co/v1/otp/verify' \-H 'Content-Type: application/json' \-H 'Authorization: Basic YOUR_DASHBOARD_BASE64_TOKEN' \-d '{"id": "otp_abc123xyz","pin": "482910"}'
{"success": true,"message": "OTP verified successfully","data": { "verified": true }}
API Endpoints
| Type | Length | Example | Best For |
|---|---|---|---|
NUMERIC | 4–8 digits | 482910 | Login, general verification (easiest to type) |
ALPHANUMERIC | 6–12 chars | A7B9X2K4 | Financial transactions, high-security flows |
ALPHABETIC | 5–10 letters | KPTMRB | Voice-read verification codes |
ACTIVE_OTP_EXISTSAn unexpired OTP is already active for this phone. Show the remaining countdown to the user.
RATE_LIMIT_EXCEEDEDMax 3 OTP requests per phone per hour. Use retryAfter from the response for the countdown.
OTP_EXPIREDThe OTP has passed its expiry time. Prompt the user to request a new code.
INVALID_PINWrong code entered. Check attemptsRemaining — lock the flow at 0 to prevent brute force.
OTP_NOT_FOUNDThe OTP ID does not exist or was already verified. Redirect to request a new OTP.
Use these placeholders in the message field — they are replaced at send time:
{code}The generated OTP code (required)
{amount}The expiry amount (e.g. 5)
{duration}The expiry unit (minutes / hours)
Example: "Your code is {code}. Valid for {amount} {duration}." → "Your code is 482910. Valid for 5 minutes."
*889*142#When SMS is delayed or unavailable, users can dial *889*142# from any mobile phone to retrieve a pending OTP via USSD — no internet or SMS delivery required.
SMS delayed
Carrier congestion or network issues holding up delivery.
No data needed
Works on feature phones and areas with no internet access.
Same session
The OTP ID and expiry are unchanged — verify as normal.
The USSD fallback is active automatically for all OTP requests — no extra configuration required. The code is available until the session expires or the OTP is verified.
Security Best Practices
- Set
maxAmountOfValidationRetriesto 3–5 to prevent brute force attacks - Use 5-minute expiry for most flows — shorter is more secure
- Lock the UI after the attempt limit is reached; do not allow silent retries
- Store
userIdandipAddressinmetadatafor audit logs - Never log or expose the OTP code in your application logs
- Verify server-side — never trust client-side OTP validation alone