API

OTP API

Core concepts behind SecureOTP — code format, expiry, auto-invalidation, and channel selection.

OTP API

This page covers the concepts shared by all OTP channels. For channel-specific request and response details, see the SMS and WhatsApp guides.

Code Format

Every OTP generated by SecureOTP is a 6-character alphanumeric code drawn from uppercase letters and digits (A–Z, 0–9). Examples: A3K9PZ, B72XQ1, 0WLMR5.

The format is intentionally case-insensitive and avoids easily confused characters, making it safe to dictate over the phone or read from a notification preview.

Message Format

The message sent to the recipient follows this template:

{Your Account Name}: {CODE}. Valid for {TTL} minutes.

For example, if your account name is Acme and the code is A3K9PZ:

Acme: A3K9PZ. Valid for 5 minutes.

The account name is taken from your registered profile in the Console.

Expiry

OTP codes expire after 5 minutes by default. The exact expiry timestamp is returned in every send response:

json
{
  "message": "OTP sent successfully",
  "expiresAt": "2026-06-09T10:05:00.000Z"
}

Use expiresAt to show a countdown timer or display a "resend" button in your UI.

Auto-Invalidation

When a new OTP is sent to a phone number on the same channel, any previous unused, unexpired codes for that number are automatically invalidated. This prevents confusion where a user tries an old code after requesting a new one.

Verification

Verification requires two fields — the phone number and the code submitted by the user:

json
{
  "phone": "+628123456789",
  "code": "A3K9PZ"
}

The API checks that:

  1. A record exists for that phone + code + channel combination
  2. The code has not been used before
  3. The code has not expired

If all three conditions pass, the code is marked as used and verification succeeds. A used or expired code always returns a failure — codes cannot be verified more than once.

Error Responses

| Status | Body | Meaning | | --- | --- | --- | | 400 | Phone number is required | phone field missing from send request | | 400 | Phone and code are required | phone or code missing from verify request | | 400 | Invalid or expired OTP | Code not found, already used, or past expiresAt | | 402 | Insufficient balance | Account balance is below the cost of the send | | 503 | No default WhatsApp account is configured | No WhatsApp account has been set as default by an admin |

Choosing a Channel

Both channels produce the same OTP experience for the end user. The main differences:

| | SMS | WhatsApp | | --- | --- | --- | | Cost | 499 credits | 299 credits | | Requires device | SIM card | WhatsApp installed | | Delivery endpoint | /api/v1/otp/sms/send | /api/v1/otp/whatsapp/send | | Verify endpoint | /api/v1/otp/sms/verify | /api/v1/otp/whatsapp/verify |

Use the channel that best matches your user base. OTPs sent via SMS are verified only against SMS records, and WhatsApp OTPs only against WhatsApp records.