Enviar Código de Verificación (OTP)
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)./v2/otpPara 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.
Cuerpo de la solicitud
application/json
52.Canal de entrega del código:
sms(default): mensaje de texto.whatsapp: mensaje de WhatsApp oficial con plantilla fija (ignoramessageycompany). No requiere cuenta de WhatsApp Business ni verificación de Meta: sale por nuestra integración oficial (remitenteAuthenticate).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).
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).
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).
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
a3f9k2tu usuario debe capturar exactamentea3f9k2. - No se combina con
code_formatnicode_length(el formato y la longitud los define tu código). Si envías cualquiera de los dos junto concode_custom, responde400 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, nocode_rotate. Mandar el mismo código no reinicia los intentos aunque agreguescode_rotate: true: si la verificación estálocked, responde429 otp_max_attempts. Para obtener 7 intentos nuevos manda un código distinto. - Con
sandbox: trueno necesitascode_in_response: true: ya conoces el código. - Si el envío falla (
402 otp_insufficient_creditso502 otp_send_failed), el código queda registrado pero no se entregó nada: reintenta el envío, no verifiques.
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).
code_custom (la longitud la define tu código).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.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).
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.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
action: resent) o rotado sobre una ronda en curso (action: restarted)pending: hay un código activo esperando verificación.Qué hizo la API con tu solicitud:
created: primera verificación para el número (o la anterior ya había expirado). HTTP201.resent: reenvió el mismo código vigente. HTTP200.restarted: generó un código nuevo e inició otra ronda (concode_rotate: true, o tras una verificación completada). HTTP200o201.
YYYY-MM-DD HH:mm:ss, hora del centro de México).message/company con channel: whatsapp (se ignoran, la plantilla de WhatsApp es fija).code_in_response: true (pruebas/automatización).pending: hay un código activo esperando verificación.Qué hizo la API con tu solicitud:
created: primera verificación para el número (o la anterior ya había expirado). HTTP201.resent: reenvió el mismo código vigente. HTTP200.restarted: generó un código nuevo e inició otra ronda (concode_rotate: true, o tras una verificación completada). HTTP200o201.
YYYY-MM-DD HH:mm:ss, hora del centro de México).message/company con channel: whatsapp (se ignoran, la plantilla de WhatsApp es fija).code_in_response: true (pruebas/automatización).otp_missing_param, otp_invalid_param, otp_invalid_channel, otp_message_invalid)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. |
otp_throttled).otp_insufficient_credits: siempre sms_07, el código estándar de saldo insuficiente de toda la API.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. |
otp_throttled).otp_insufficient_credits: siempre sms_07, el código estándar de saldo insuficiente de toda la API.otp_throttled) o intentos de verificación agotados sin code_rotate (otp_max_attempts). Incluye header Retry-After.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. |
otp_throttled).otp_insufficient_credits: siempre sms_07, el código estándar de saldo insuficiente de toda la API.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. |
otp_throttled).otp_insufficient_credits: siempre sms_07, el código estándar de saldo insuficiente de toda la API.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. |
otp_throttled).otp_insufficient_credits: siempre sms_07, el código estándar de saldo insuficiente de toda la API.Qué hace cada llamada
El endpoint decide según el estado del número; tú solo repites el POST:
| Estado del número | Qué hace | HTTP | action |
|---|---|---|---|
| Sin verificación activa (o expirada) | Crea y envía un código nuevo. | 201 | created |
| Código vigente | Reenvía el mismo código. | 200 | resent |
Código vigente + code_rotate: true | Descarta el anterior y genera uno nuevo (7 intentos frescos). | 200 | restarted |
| Ya verificado | Inicia un ciclo nuevo. | 201 | restarted |
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
companyymessageson obligatorios en toda petición, sin importar el canal — mándalos siempre igual y solo cambiachannel.messagedebe incluir{{code}}y{{company}}.channel: whatsappreemplazacompanyymessagepor su plantilla oficial fija (recibes unwarningavisando que se ignoraron); ensmsyvoicese usan tal cual los envías.sandbox: true(modo prueba: no entrega el SMS ni descuenta saldo) requierecode_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:
| HTTP | error | Cuándo ocurre | Qué hacer |
|---|---|---|---|
400 | otp_missing_param / otp_invalid_param | Falta un parámetro, es inválido, o enviaste uno de la API anterior (voice, whatsapp, template). | El hint indica el parámetro exacto. |
400 | otp_invalid_channel | channel no es sms, whatsapp ni voice. | Corrige el valor. |
400 | otp_message_invalid | message sin {{code}}/{{company}}, con caracteres fuera de GSM-7, o que excede 160 caracteres ya expandido. | El hint detalla la regla incumplida. |
402 | otp_insufficient_credits | Sin saldo para enviar (incluye code: sms_07). | Recarga saldo. |
429 | otp_throttled | Cooldown de reenvío activo. | Espera resend_available_in segundos (también en el header Retry-After). |
429 | otp_max_attempts | La ronda está bloqueada por intentos agotados y no enviaste code_rotate. | Rota el código con code_rotate: true. |
502 | otp_send_failed | El 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.