Channels

WhatsApp

Send and verify OTP codes via WhatsApp, including custom accounts for premium users.

WhatsApp

SecureOTP can deliver OTP codes over WhatsApp. The cost per send is lower than SMS (299 credits vs 499), and messages arrive as a standard WhatsApp chat message.

WhatsApp delivery requires a connected WhatsApp account configured by an admin. Premium users may connect their own dedicated WhatsApp number.

Send an OTP

POST /api/v1/otp/whatsapp/send

Sends a 6-character OTP to the specified phone number via WhatsApp.

Request

bash
curl -X POST https://otp.applicanity.com/api/v1/otp/whatsapp/send \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "phone": "+628123456789" }'

Body parameters

| Parameter | Type | Required | Description | | --- | --- | --- | --- | | phone | string | Yes | Recipient phone number in E.164 format (e.g. +628123456789) | | whatsappAccountId | number | No | ID of a specific WhatsApp account to send from. Requires PREMIUM or ADMIN role. Defaults to the system default account. |

Response

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

| Field | Type | Description | | --- | --- | --- | | message | string | Confirmation message | | expiresAt | string (ISO 8601) | When the code expires |

Error responses

| Status | Error | Description | | --- | --- | --- | | 400 | Phone number is required | phone field missing | | 402 | Insufficient balance | Balance below the WhatsApp cost (299 credits) | | 403 | Selecting a custom WhatsApp account requires a premium subscription | whatsappAccountId provided but account role is USER | | 403 | You do not have access to this WhatsApp account | Account exists but is assigned to a different user | | 404 | WhatsApp account not found | Provided whatsappAccountId does not exist | | 503 | No default WhatsApp account is configured | No account has been set as default by an admin |


Verify an OTP

POST /api/v1/otp/whatsapp/verify

Identical contract to SMS verify, but checks against WhatsApp-channel records.

Request

bash
curl -X POST https://otp.applicanity.com/api/v1/otp/whatsapp/verify \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "phone": "+628123456789", "code": "A3K9PZ" }'

Body parameters

| Parameter | Type | Required | Description | | --- | --- | --- | --- | | phone | string | Yes | The phone number that received the OTP | | code | string | Yes | The code entered by the user |

Response — success

json
{
  "success": true,
  "message": "OTP verified successfully",
  "phone": "+628123456789"
}

Response — failure

json
{
  "success": false,
  "message": "Invalid or expired OTP"
}

Custom WhatsApp Accounts (Premium)

By default, all sends use a system default account configured by the platform admin. If you are on the PREMIUM plan, you can connect your own WhatsApp number and use it exclusively for your sends.

How it works

  1. An admin creates a WhatsApp account entry and assigns it to your user account
  2. You connect the account by scanning a QR code from the Setup page in the Console
  3. Once connected, pass the account's id as whatsappAccountId in your send requests
bash
curl -X POST https://otp.applicanity.com/api/v1/otp/whatsapp/send \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "phone": "+628123456789", "whatsappAccountId": 3 }'
If you omit whatsappAccountId, the system default account is used regardless of your plan.

Account statuses

| Status | Meaning | | --- | --- | | idle | Not yet connected — needs QR scan | | initializing | Client is starting up | | qr_ready | QR code available — open WhatsApp to scan | | ready | Connected and ready to send | | auth_failure | Authentication failed — reconnect required | | disconnected | Session was disconnected — reconnect required |

Sending to an account that is not in the ready state will fail with a 500 error. Monitor your account status from the Setup page and reconnect if it drops.


JavaScript Example

javascript
// Send using the default system account
const res = await fetch('/api/v1/otp/whatsapp/send', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.SECUREOTP_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ phone: '+628123456789' }),
})

// Send using your own dedicated account (PREMIUM)
const res = await fetch('/api/v1/otp/whatsapp/send', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.SECUREOTP_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    phone: '+628123456789',
    whatsappAccountId: 3,
  }),
})

const { expiresAt } = await res.json()