Enviar Código de Verificación (OTP)

Genera y envía un código de verificación por SMS, WhatsApp o llamada de voz. Este único endpoint maneja todo el ciclo: la primera llamada crea el código, y el botón "No recibí el código" de tu app se resuelve repitiendo la misma llamada, incluso con otro channel: el mismo código sale por el canal nuevo y cubres los números que no reciben SMS. La respuesta siempre te dice qué hizo (action) y qué sigue (next).
post/v2/otp

Para probar, basta con phone_number, country_code, company y message; el código llega por SMS en segundos. Con code_in_response: true la respuesta incluye el código y puedes automatizar la prueba sin leer el teléfono.

Autorización

apikeystringheaderrequerido

Cuerpo de la solicitud

application/json

phone_numberstringrequerido
Número de teléfono a verificar (hasta 10 dígitos, sin código de país). En México siempre son 10 dígitos.
country_codestringrequerido
Código de país, numérico. Para México envía 52.
channelenum

Canal de entrega del código:

  • sms (default): mensaje de texto.
  • whatsapp: mensaje de WhatsApp oficial con plantilla fija (ignora message y company). No requiere cuenta de WhatsApp Business ni verificación de Meta: sale por nuestra integración oficial (remitente Authenticate).
  • voice: llamada que dicta el código dígito por dígito.

En un reenvío puedes cambiar de canal: el mismo código pendiente sale por el canal nuevo (fallback).

enum:smswhatsappvoice
companystringrequerido

Nombre de tu empresa o app. Se inserta en message mediante el placeholder {{company}}.

Si channel es whatsapp, se ignora: la plantilla oficial de WhatsApp es fija (recibes un warning avisando).

messagestringrequerido

Texto que recibe el usuario. Debe incluir los placeholders {{code}} y {{company}}, usar solo caracteres GSM-7 (sin emojis ni acentos raros) y no exceder 160 caracteres ya con el código y la empresa sustituidos.

Un OTP nunca se envía partido en dos SMS: a partir de 161 caracteres la operadora fragmenta el mensaje y eso rompe el autocompletado de códigos del teléfono. Por eso el request falla en vez de enviarse. El límite se evalúa en cada envío con la longitud real del código que va en ese mensaje. En un reenvío se manda el código ya guardado, que puede ser más largo que el code_length de ese request: si creaste con code_length: 10 o con un code_custom largo, dimensiona el texto del reenvío contra esa longitud. El rechazo no altera la verificación en curso.

Ejemplo armado (con code: "483920" y company: "Mi Empresa"): 483920 es tu codigo de verificacion de Mi Empresa. No lo compartas.

Si channel es whatsapp, se ignora: el texto sale por la plantilla oficial fija de WhatsApp (recibes un warning avisando).

code_customstring

Tu propio código, si ya tienes un sistema que los genera y quieres seguir usándolo. Nosotros solo lo enviamos y lo verificamos.

Debe cumplir las mismas reglas que un código generado por nosotros: de 4 a 10 caracteres, solo letras y/o números, sin espacios ni signos.

  • Se guarda y se verifica tal cual lo envías: la comparación distingue mayúsculas de minúsculas, así que si mandas a3f9k2 tu usuario debe capturar exactamente a3f9k2.
  • No se combina con code_format ni code_length (el formato y la longitud los define tu código). Si envías cualquiera de los dos junto con code_custom, responde 400 otp_invalid_param.
  • Si ya hay una verificación pendiente y el código que envías es distinto al vigente, se inicia una ronda nueva (reemplaza el código y reinicia los 7 intentos). Si es el mismo, es un reenvío normal. En ambos casos aplica el cooldown de 30s.
  • Con code_custom, lo que decide si se rota es el código, no code_rotate. Mandar el mismo código no reinicia los intentos aunque agregues code_rotate: true: si la verificación está locked, responde 429 otp_max_attempts. Para obtener 7 intentos nuevos manda un código distinto.
  • Con sandbox: true no necesitas code_in_response: true: ya conoces el código.
  • Si el envío falla (402 otp_insufficient_credits o 502 otp_send_failed), el código queda registrado pero no se entregó nada: reintenta el envío, no verifiques.
code_formatenum

Formato del código generado:

  • numeric (default): solo números.
  • alphanumeric: letras y números.
  • letters: solo letras.

No aplica si envías code_custom (el formato lo define tu código).

enum:numericalphanumericletters
code_lengthinteger
Longitud del código (4 a 10 caracteres). Por defecto 6. No aplica si envías code_custom (la longitud la define tu código).
code_rotateboolean
true descarta el código vigente y genera uno nuevo (nueva ronda con 7 intentos). Sin él, un código vigente se reenvía tal cual. El cooldown de 30s aplica igual.
code_in_responseboolean

Devuelve el código generado en el campo code de la respuesta:

  • false (default): no devolver.
  • true: devolver (pruebas/automatización del lado del servidor).
sandboxboolean
Modo prueba. Con true el flujo se ejecuta completo (genera y registra el código) pero el SMS no se entrega y no se descuenta saldo. Como el usuario nunca recibe el mensaje, debes enviar también code_in_response: true para obtener el código en la respuesta; si falta, responde 400 otp_missing_param. La excepción es code_custom: si tú pusiste el código, ya lo conoces y code_in_response no hace falta.
expiration_datestring
Fecha de expiración del código, formato YYYY-MM-DD HH:mm:ss (futura). Por defecto 24 horas; máximo 3 días (si envías más, se ajusta al máximo).

Respuesta

200Código reenviado (action: resent) o rotado sobre una ronda en curso (action: restarted)
successbooleanrequerido
stateenumrequerido
Estado de la verificación después del envío. Siempre pending: hay un código activo esperando verificación.
enum:pending
actionenumrequerido

Qué hizo la API con tu solicitud:

  • created: primera verificación para el número (o la anterior ya había expirado). HTTP 201.
  • resent: reenvió el mismo código vigente. HTTP 200.
  • restarted: generó un código nuevo e inició otra ronda (con code_rotate: true, o tras una verificación completada). HTTP 200 o 201.
enum:createdresentrestarted
messagestring
Descripción del resultado en español.
hintstring
Sugerencia accionable sobre el siguiente paso.
nextarray<string>
Endpoints sugeridos como siguiente paso del flujo.
expires_atstring
Fecha de expiración del código (YYYY-MM-DD HH:mm:ss, hora del centro de México).
attempts_remaininginteger
Intentos de verificación disponibles para este código (máximo 7 por ronda).
resend_available_ininteger
Segundos que deben pasar antes de poder reenviar o rotar el código (cooldown de 30s).
warningstring
Solo cuando aplica: aviso no bloqueante, por ejemplo al enviar message/company con channel: whatsapp (se ignoran, la plantilla de WhatsApp es fija).
codestring
El código generado. Solo cuando envías code_in_response: true (pruebas/automatización).
request_idstring
UUID para seguimiento de la petición.
201Código creado y enviado (primera verificación, o la anterior ya había expirado o completado)
successbooleanrequerido
stateenumrequerido
Estado de la verificación después del envío. Siempre pending: hay un código activo esperando verificación.
enum:pending
actionenumrequerido

Qué hizo la API con tu solicitud:

  • created: primera verificación para el número (o la anterior ya había expirado). HTTP 201.
  • resent: reenvió el mismo código vigente. HTTP 200.
  • restarted: generó un código nuevo e inició otra ronda (con code_rotate: true, o tras una verificación completada). HTTP 200 o 201.
enum:createdresentrestarted
messagestring
Descripción del resultado en español.
hintstring
Sugerencia accionable sobre el siguiente paso.
nextarray<string>
Endpoints sugeridos como siguiente paso del flujo.
expires_atstring
Fecha de expiración del código (YYYY-MM-DD HH:mm:ss, hora del centro de México).
attempts_remaininginteger
Intentos de verificación disponibles para este código (máximo 7 por ronda).
resend_available_ininteger
Segundos que deben pasar antes de poder reenviar o rotar el código (cooldown de 30s).
warningstring
Solo cuando aplica: aviso no bloqueante, por ejemplo al enviar message/company con channel: whatsapp (se ignoran, la plantilla de WhatsApp es fija).
codestring
El código generado. Solo cuando envías code_in_response: true (pruebas/automatización).
request_idstring
UUID para seguimiento de la petición.
400Parámetro faltante o inválido (otp_missing_param, otp_invalid_param, otp_invalid_channel, otp_message_invalid)
successbooleanrequerido
stateenum
Estado actual de la verificación (presente cuando es relevante para el error).
enum:nonependingverifiedexpiredlocked
errorstringrequerido

Slug estable del error, para manejo programático:

Slug HTTP Significado
otp_missing_param 400 Falta un parámetro obligatorio.
otp_invalid_param 400 Un parámetro es inválido o no aplica a este endpoint.
otp_invalid_channel 400 channel debe ser sms, whatsapp o voice.
otp_message_invalid 400 El message personalizado no cumple las reglas (placeholders, GSM-7, 160).
otp_incorrect_code 401 El código no coincide.
otp_insufficient_credits 402 Saldo insuficiente para enviar.
otp_not_found 404 No hay verificación activa para el número.
otp_already_verified 409 El número ya está verificado.
otp_code_expired 410 El código expiró.
otp_throttled 429 Cooldown de reenvío activo (espera resend_available_in).
otp_max_attempts 429 Se agotaron los 7 intentos de verificación de la ronda.
otp_internal 500 Error inesperado.
otp_send_failed 502 El mensaje no pudo enviarse.
messagestringrequerido
Descripción del error en español.
hintstring
Sugerencia accionable para resolverlo.
nextarray<string>
Endpoints sugeridos como siguiente paso.
expires_atstring
Expiración del código vigente, cuando aplica.
attempts_remaininginteger
Intentos de verificación restantes, cuando aplica.
resend_available_ininteger
Segundos restantes del cooldown de reenvío, cuando aplica (errores otp_throttled).
codestring
Solo en otp_insufficient_credits: siempre sms_07, el código estándar de saldo insuficiente de toda la API.
request_idstring
UUID para seguimiento de la petición.
402Saldo insuficiente para enviar el código
successbooleanrequerido
stateenum
Estado actual de la verificación (presente cuando es relevante para el error).
enum:nonependingverifiedexpiredlocked
errorstringrequerido

Slug estable del error, para manejo programático:

Slug HTTP Significado
otp_missing_param 400 Falta un parámetro obligatorio.
otp_invalid_param 400 Un parámetro es inválido o no aplica a este endpoint.
otp_invalid_channel 400 channel debe ser sms, whatsapp o voice.
otp_message_invalid 400 El message personalizado no cumple las reglas (placeholders, GSM-7, 160).
otp_incorrect_code 401 El código no coincide.
otp_insufficient_credits 402 Saldo insuficiente para enviar.
otp_not_found 404 No hay verificación activa para el número.
otp_already_verified 409 El número ya está verificado.
otp_code_expired 410 El código expiró.
otp_throttled 429 Cooldown de reenvío activo (espera resend_available_in).
otp_max_attempts 429 Se agotaron los 7 intentos de verificación de la ronda.
otp_internal 500 Error inesperado.
otp_send_failed 502 El mensaje no pudo enviarse.
messagestringrequerido
Descripción del error en español.
hintstring
Sugerencia accionable para resolverlo.
nextarray<string>
Endpoints sugeridos como siguiente paso.
expires_atstring
Expiración del código vigente, cuando aplica.
attempts_remaininginteger
Intentos de verificación restantes, cuando aplica.
resend_available_ininteger
Segundos restantes del cooldown de reenvío, cuando aplica (errores otp_throttled).
codestring
Solo en otp_insufficient_credits: siempre sms_07, el código estándar de saldo insuficiente de toda la API.
request_idstring
UUID para seguimiento de la petición.
429Cooldown de reenvío activo (otp_throttled) o intentos de verificación agotados sin code_rotate (otp_max_attempts). Incluye header Retry-After.
successbooleanrequerido
stateenum
Estado actual de la verificación (presente cuando es relevante para el error).
enum:nonependingverifiedexpiredlocked
errorstringrequerido

Slug estable del error, para manejo programático:

Slug HTTP Significado
otp_missing_param 400 Falta un parámetro obligatorio.
otp_invalid_param 400 Un parámetro es inválido o no aplica a este endpoint.
otp_invalid_channel 400 channel debe ser sms, whatsapp o voice.
otp_message_invalid 400 El message personalizado no cumple las reglas (placeholders, GSM-7, 160).
otp_incorrect_code 401 El código no coincide.
otp_insufficient_credits 402 Saldo insuficiente para enviar.
otp_not_found 404 No hay verificación activa para el número.
otp_already_verified 409 El número ya está verificado.
otp_code_expired 410 El código expiró.
otp_throttled 429 Cooldown de reenvío activo (espera resend_available_in).
otp_max_attempts 429 Se agotaron los 7 intentos de verificación de la ronda.
otp_internal 500 Error inesperado.
otp_send_failed 502 El mensaje no pudo enviarse.
messagestringrequerido
Descripción del error en español.
hintstring
Sugerencia accionable para resolverlo.
nextarray<string>
Endpoints sugeridos como siguiente paso.
expires_atstring
Expiración del código vigente, cuando aplica.
attempts_remaininginteger
Intentos de verificación restantes, cuando aplica.
resend_available_ininteger
Segundos restantes del cooldown de reenvío, cuando aplica (errores otp_throttled).
codestring
Solo en otp_insufficient_credits: siempre sms_07, el código estándar de saldo insuficiente de toda la API.
request_idstring
UUID para seguimiento de la petición.
500Error interno inesperado
successbooleanrequerido
stateenum
Estado actual de la verificación (presente cuando es relevante para el error).
enum:nonependingverifiedexpiredlocked
errorstringrequerido

Slug estable del error, para manejo programático:

Slug HTTP Significado
otp_missing_param 400 Falta un parámetro obligatorio.
otp_invalid_param 400 Un parámetro es inválido o no aplica a este endpoint.
otp_invalid_channel 400 channel debe ser sms, whatsapp o voice.
otp_message_invalid 400 El message personalizado no cumple las reglas (placeholders, GSM-7, 160).
otp_incorrect_code 401 El código no coincide.
otp_insufficient_credits 402 Saldo insuficiente para enviar.
otp_not_found 404 No hay verificación activa para el número.
otp_already_verified 409 El número ya está verificado.
otp_code_expired 410 El código expiró.
otp_throttled 429 Cooldown de reenvío activo (espera resend_available_in).
otp_max_attempts 429 Se agotaron los 7 intentos de verificación de la ronda.
otp_internal 500 Error inesperado.
otp_send_failed 502 El mensaje no pudo enviarse.
messagestringrequerido
Descripción del error en español.
hintstring
Sugerencia accionable para resolverlo.
nextarray<string>
Endpoints sugeridos como siguiente paso.
expires_atstring
Expiración del código vigente, cuando aplica.
attempts_remaininginteger
Intentos de verificación restantes, cuando aplica.
resend_available_ininteger
Segundos restantes del cooldown de reenvío, cuando aplica (errores otp_throttled).
codestring
Solo en otp_insufficient_credits: siempre sms_07, el código estándar de saldo insuficiente de toda la API.
request_idstring
UUID para seguimiento de la petición.
502El mensaje no pudo enviarse (falla del canal)
successbooleanrequerido
stateenum
Estado actual de la verificación (presente cuando es relevante para el error).
enum:nonependingverifiedexpiredlocked
errorstringrequerido

Slug estable del error, para manejo programático:

Slug HTTP Significado
otp_missing_param 400 Falta un parámetro obligatorio.
otp_invalid_param 400 Un parámetro es inválido o no aplica a este endpoint.
otp_invalid_channel 400 channel debe ser sms, whatsapp o voice.
otp_message_invalid 400 El message personalizado no cumple las reglas (placeholders, GSM-7, 160).
otp_incorrect_code 401 El código no coincide.
otp_insufficient_credits 402 Saldo insuficiente para enviar.
otp_not_found 404 No hay verificación activa para el número.
otp_already_verified 409 El número ya está verificado.
otp_code_expired 410 El código expiró.
otp_throttled 429 Cooldown de reenvío activo (espera resend_available_in).
otp_max_attempts 429 Se agotaron los 7 intentos de verificación de la ronda.
otp_internal 500 Error inesperado.
otp_send_failed 502 El mensaje no pudo enviarse.
messagestringrequerido
Descripción del error en español.
hintstring
Sugerencia accionable para resolverlo.
nextarray<string>
Endpoints sugeridos como siguiente paso.
expires_atstring
Expiración del código vigente, cuando aplica.
attempts_remaininginteger
Intentos de verificación restantes, cuando aplica.
resend_available_ininteger
Segundos restantes del cooldown de reenvío, cuando aplica (errores otp_throttled).
codestring
Solo en otp_insufficient_credits: siempre sms_07, el código estándar de saldo insuficiente de toda la API.
request_idstring
UUID para seguimiento de la petición.

Qué hace cada llamada

El endpoint decide según el estado del número; tú solo repites el POST:

Estado del númeroQué haceHTTPaction
Sin verificación activa (o expirada)Crea y envía un código nuevo.201created
Código vigenteReenvía el mismo código.200resent
Código vigente + code_rotate: trueDescarta el anterior y genera uno nuevo (7 intentos frescos).200restarted
Ya verificadoInicia un ciclo nuevo.201restarted

Entre envíos aplica un cooldown de 30 segundos (con o sin code_rotate); si no esperas, recibes 429 otp_throttled con el tiempo restante.

Dependencias entre parámetros

  • company y message son obligatorios en toda petición, sin importar el canal — mándalos siempre igual y solo cambia channel. message debe incluir {{code}} y {{company}}.
  • channel: whatsapp reemplaza company y message por su plantilla oficial fija (recibes un warning avisando que se ignoraron); en sms y voice se usan tal cual los envías.
  • sandbox: true (modo prueba: no entrega el SMS ni descuenta saldo) requiere code_in_response: true; sin él, 400 otp_missing_param.

code_in_response: true está pensado para pruebas y automatización del lado del servidor. Nunca reenvíes esa respuesta al navegador o a la app del usuario: si el código regresa al mismo cliente que lo va a capturar, el flujo deja de probar la posesión del teléfono.

Cómo leer los errores

Cada error llega con su status HTTP real, un slug estable en error, una sugerencia accionable en hint y los endpoints sugeridos en next:

HTTPerrorCuándo ocurreQué hacer
400otp_missing_param / otp_invalid_paramFalta un parámetro, es inválido, o enviaste uno de la API anterior (voice, whatsapp, template).El hint indica el parámetro exacto.
400otp_invalid_channelchannel no es sms, whatsapp ni voice.Corrige el valor.
400otp_message_invalidmessage sin {{code}}/{{company}}, con caracteres fuera de GSM-7, o que excede 160 caracteres ya expandido.El hint detalla la regla incumplida.
402otp_insufficient_creditsSin saldo para enviar (incluye code: sms_07).Recarga saldo.
429otp_throttledCooldown de reenvío activo.Espera resend_available_in segundos (también en el header Retry-After).
429otp_max_attemptsLa ronda está bloqueada por intentos agotados y no enviaste code_rotate.Rota el código con code_rotate: true.
502otp_send_failedEl canal no pudo entregar el mensaje.Reintenta; si persiste, contacta a soporte con el request_id.

Los errores de autenticación (auth_*) vienen de la capa común de la API y llegan con HTTP 200 y el detalle en el body; consulta Errores y límites.