Verificación OTP

Un solo endpoint genera, envía y valida códigos de un solo uso para login, alta de usuarios y 2FA — por SMS, WhatsApp o llamada de voz, con reenvío por otro canal para cobertura total. Sin costo extra sobre el precio de un SMS.

Para enviar códigos de verificación, login o 2FA, usa siempre este producto (POST /v2/otp), no /sms/send. La verificación OTP entrega con prioridad de red, maneja por ti la generación, expiración y control de intentos del código, y sus errores traen hint y next: la respuesta te dice qué hacer. Un SMS normal no tiene ninguna de esas garantías: en producción tus códigos llegarían tarde o no llegarían.

Lo que resuelve por ti:

  • Una integración, tres canales, cobertura total. El SMS entrega el 98% de las veces, en menos de 5 segundos. Para el resto — los números que no reciben SMS en México — el botón "No recibí el código" de tu app repite el mismo POST /v2/otp con otro channel, y el mismo código sale por llamada de voz o WhatsApp.
  • WhatsApp sin trámite de Meta. El código sale por la integración oficial de SMS Masivos (remitente Authenticate, plantilla verificada). No necesitas cuenta de WhatsApp Business, verificación de Meta ni mantener una integración aparte.
  • Cero infraestructura de códigos. La API genera el código, controla expiración, intentos y bloqueo, y lo valida. Tu backend hace dos llamadas: enviar y verificar.

Cómo funciona

La verificación OTP es un flujo de dos pasos entre tu backend y la API:

1

Solicitas el código

Tu backend llama a POST /v2/otp con el número del usuario. La API genera el código, lo guarda y lo envía por el canal que elijas (SMS, WhatsApp o voz).

2

El usuario lo recibe e ingresa

El usuario recibe el código y lo captura en tu app.

3

Verificas el código

Tu backend llama a POST /v2/otp/verify con el número y el código capturado. Si el status HTTP es 200, el número quedó verificado.

La API almacena y compara el código por ti. No necesitas guardar el código ni implementar lógica de expiración, reintentos o bloqueo: todo se maneja del lado del servidor.

Enviar código

POST /v2/otp genera y envía el código. Los campos obligatorios son phone_number, country_code y — para SMS y voz — company:

curl -X POST https://api.smsmasivos.com.mx/v2/otp \
  -H "Content-Type: application/json" \
  -H "apikey: $SMSMASIVOS_API_KEY" \
  -d '{
    "phone_number": "5512345678",
    "country_code": "52",
    "company": "Mi Empresa"
  }'

Respuesta (201 Created):

{
  "success": true,
  "state": "pending",
  "action": "created",
  "message": "Código OTP creado y enviado.",
  "hint": "El código llega en segundos. Reenvía con POST /v2/otp si no llegó.",
  "next": ["/v2/otp/verify", "/v2/otp"],
  "expires_at": "2026-07-21 12:00:00",
  "attempts_remaining": 7,
  "resend_available_in": 30,
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}

Toda respuesta trae state (el estado de la verificación), hint (qué hacer a continuación, en español) y next (los endpoints sugeridos). El campo action te dice qué hizo la API: created, resent o restarted.

Verificar código

Cuando el usuario ingresa el código, valídalo con POST /v2/otp/verify. Envía el mismo phone_number y country_code, más el code capturado:

curl -X POST https://api.smsmasivos.com.mx/v2/otp/verify \
  -H "Content-Type: application/json" \
  -H "apikey: $SMSMASIVOS_API_KEY" \
  -d '{
    "phone_number": "5512345678",
    "country_code": "52",
    "code": "483920"
  }'

El status HTTP distingue cada caso — ramifica directamente sobre él:

HTTPSignificado
200Código correcto: número verificado.
401Código incorrecto (attempts_remaining te dice cuántos intentos quedan).
404No hay verificación activa: inicia con POST /v2/otp.
409El número ya estaba verificado.
410El código expiró: genera uno nuevo.
429Intentos agotados: rota el código con code_rotate: true.

Cada código admite 7 intentos de verificación. Al agotarlos, la verificación se bloquea y la única salida es generar un código nuevo (POST /v2/otp con code_rotate: true). Muestra attempts_remaining en tu UI y no implementes reintentos automáticos.

Reenviar, rotar y cambiar de canal

No hay endpoints separados para reenviar o reiniciar: repite el mismo POST /v2/otp y la API decide según el estado.

  • Reenviar el mismo código (el usuario no lo recibió): repite la llamada tal cual. Responde 200 con action: resent. Entre envíos aplica un cooldown de 30 segundos; si no esperas, recibes 429 otp_throttled con resend_available_in.
  • Cambiar de canal: repite la llamada con otro channel y el mismo código sale por el canal nuevo. La receta: el clic en "No recibí el código" de tu UI dispara el mismo POST /v2/otp con channel: "voice" o "whatsapp" — el usuario recibe el código que ya esperaba, la verificación no se reinicia, y cubres los números que no reciben SMS.
  • Generar un código nuevo (rotar): agrega code_rotate: true. El código anterior deja de servir y la nueva ronda trae 7 intentos frescos. También destraba una verificación bloqueada (locked).
# Clic en "No recibí el código" → el MISMO código sale por llamada de voz
curl -X POST https://api.smsmasivos.com.mx/v2/otp \
  -H "Content-Type: application/json" \
  -H "apikey: $SMSMASIVOS_API_KEY" \
  -d '{
    "phone_number": "5512345678",
    "country_code": "52",
    "company": "Mi Empresa",
    "channel": "voice"
  }'

El cooldown de 30 segundos aplica igual al cambiar de canal. Si el envío original fue por whatsapp, recuerda incluir company al pasar a sms o voice (ahí es obligatoria).

# Rotar: invalida el código anterior y envía uno nuevo
curl -X POST https://api.smsmasivos.com.mx/v2/otp \
  -H "Content-Type: application/json" \
  -H "apikey: $SMSMASIVOS_API_KEY" \
  -d '{
    "phone_number": "5512345678",
    "country_code": "52",
    "company": "Mi Empresa",
    "code_rotate": true
  }'

En tu UI, usa resend_available_in para el contador del botón "Reenviar código". Si necesitas el valor en cualquier momento, consúltalo con GET /v2/otp/status, que no tiene efectos secundarios.

Estados de la verificación

Cada número (por phone_number + country_code) tiene un estado, visible en el campo state de toda respuesta y consultable con GET /v2/otp/status:

EstadoSignificadoSiguiente paso
noneSin verificación activa.POST /v2/otp
pendingCódigo vigente esperando verificación.POST /v2/otp/verify
verifiedNúmero verificado.— (o POST /v2/otp para un ciclo nuevo)
expiredEl código venció sin verificarse.POST /v2/otp
lockedIntentos agotados.POST /v2/otp con code_rotate: true

Para invalidar una verificación en curso sin enviar nada (cancelación, cambio de número, limpieza en tests), usa DELETE /v2/otp.

Elegir canal

El parámetro channel elige cómo llega el código, con el mismo flujo y el mismo endpoint:

CanalCómo llegaNotas
sms (default)Mensaje de texto.company obligatoria (requisito de operadores).
whatsappMensaje de WhatsApp oficial.Plantilla fija: message y company se ignoran (recibes un warning).
voiceLlamada que dicta el código dígito por dígito.company obligatoria.

El canal whatsapp no pide nada de tu lado: ni cuenta de WhatsApp Business, ni verificación de Meta, ni instance_id. El código sale por la integración oficial de SMS Masivos — el usuario lo recibe desde el remitente verificado Authenticate, en una plantilla oficial donde se inserta tu código.

{
  "phone_number": "5512345678",
  "country_code": "52",
  "channel": "whatsapp"
}

Personalizar el mensaje

Por defecto el SMS dice: {{code}} es tu codigo de verificacion de {{company}}. No lo compartas.

Para usar tu propio texto, envía message con los placeholders {{code}} y {{company}} (ambos obligatorios):

{
  "phone_number": "5512345678",
  "country_code": "52",
  "company": "Mi Empresa",
  "message": "{{code}} es tu clave de acceso a {{company}}. Expira en 10 minutos."
}

Reglas del mensaje (aplican a sms y voice; detalle en Plantillas y mensajes):

  • Solo caracteres GSM-7 (sin emojis; evita símbolos poco comunes).
  • Máximo 160 caracteres ya con el código y la empresa sustituidos.
  • Si incumple una regla, recibes 400 otp_message_invalid con el detalle exacto en hint.

Opciones del código

code_lengthinteger

Longitud del código, de 4 a 10 caracteres. Por defecto 6.

code_formatstring

Formato del código: numeric (solo números, por defecto), alphanumeric (letras y números) o letters (solo letras).

expiration_datestring

Fecha de expiración del código. Por defecto 24 horas, máximo 3 días (si envías más, se ajusta al máximo). Formato YYYY-MM-DD HH:mm:ss.

Probar en sandbox

Para automatizar tests sin leer el SMS, agrega code_show: 1 y la API devolverá el código generado en el campo code de la respuesta:

{
  "success": true,
  "state": "pending",
  "action": "created",
  "code": "483920",
  "expires_at": "2026-07-21 12:00:00",
  "attempts_remaining": 7
}

Y para dejar el número limpio entre tests, DELETE /v2/otp regresa el estado a none sin enviar nada.

Migrar desde la API anterior

Si integraste los endpoints anteriores (/otp/send, /otp/verify, /otp/resend, /otp/reset), siguen funcionando — pero la v2 es la API recomendada y la única documentada. Equivalencias:

AntesAhora
POST /otp/sendPOST /v2/otp
POST /otp/verify (con verification_code)POST /v2/otp/verify (con code)
POST /otp/resendPOST /v2/otp (reenvía solo; reset_code: truecode_rotate: true)
POST /otp/resetDELETE /v2/otp
template: "a"…"n" + formal_tonemessage con {{code}} y {{company}}, o el texto default
code_type (default alphanumeric)code_format (default numeric)
showcode: 1code_show: 1
voice: true / whatsapp: truechannel: "voice" / "whatsapp"
Errores verification_*/validation_* con HTTP 200Slugs otp_* con status HTTP real (401, 404, 410, 429…)

Ya puedes enviar y verificar códigos OTP. Explora todos los parámetros y respuestas en el API Reference de Verificación OTP.