// Endpoints

One-time passwords

Issue a login code over WhatsApp and verify it.

Two calls give you WhatsApp login codes without building any of the hard parts. We generate the code, store only a hash of it, expire it and limit the guesses. Your server never sees the code, so it can never leak it.

  • You need an approved authentication template. Create one under Settings → Templates.
  • Use separate keys if you can: sending needs messages:send, checking needs otp:verify. A marketing tool holding a send key cannot confirm a login.
  • Codes are not blocked by opt-out. Someone who replied STOP to promotions can still sign in.
POST/api/v1/otp/send

Issue a one-time password

Scope: messages:send

curl -X POST https://whatsapp.esendblue.com/api/v1/otp/send \
  -H "Authorization: Bearer wacrm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "to": "+919876543210", "template_name": "login_code", "language": "en", "purpose": "login" }'
FieldDefaultNotes
to—Required, E.164
template_name—Required. An approved authentication template
language—The template language, e.g. en
purpose—Keeps flows apart: a checkout code does not cancel a login code
code_length64 to 8 digits
expires_in_seconds30060 to 900
max_attempts51 to 10 wrong guesses before the code dies

Out-of-range values are clamped rather than rejected, so a bad setting never breaks a login. A new code replaces the previous one for the same number and purpose. Resends are limited to one per 30 seconds and five per hour per number (otp_resend_too_soon, otp_rate_limited).

POST/api/v1/otp/verify

Verify a one-time password

Scope: otp:verify

curl -X POST https://whatsapp.esendblue.com/api/v1/otp/verify \
  -H "Authorization: Bearer wacrm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "to": "+919876543210", "code": "483920", "purpose": "login" }'

There is no verified: false

Every failure is an error response. Code that only checks for a 200 cannot mistake a wrong code for a successful login.
codeStatusMeaning
otp_not_found400No live code for this number and purpose
otp_expired400The code timed out. Send a new one
otp_invalid400Wrong code. The attempt was counted
otp_already_used400Right code, but it was already used
otp_attempts_exceeded429Too many wrong guesses. Send a new code

Typical login#

const API = "https://whatsapp.esendblue.com/api/v1";
const headers = (key) => ({
  Authorization: `Bearer ${key}`,
  "Content-Type": "application/json",
});

// 1. User typed their number.
await fetch(`${API}/otp/send`, {
  method: "POST",
  headers: headers(process.env.WHATSAPP_SEND_KEY),
  body: JSON.stringify({ to: phone, template_name: "login_code", language: "en", purpose: "login" }),
});

// 2. User typed the code they received.
const res = await fetch(`${API}/otp/verify`, {
  method: "POST",
  headers: headers(process.env.WHATSAPP_VERIFY_KEY),
  body: JSON.stringify({ to: phone, code, purpose: "login" }),
});
if (!res.ok) throw new Error((await res.json()).error.code);
// Signed in.