Buenas prácticas de Verify
Verify incluye valores predeterminados adecuados para la mayoría de flujos de códigos de verificación de un solo uso. Esta guía cubre las decisiones que los valores predeterminados no toman por ti: cuánto tiempo debe vivir un código, cuántos dígitos necesita, qué canal es apropiado para lo que realmente estás verificando, y cómo distinguir un problema de entrega de un problema de verificación.
Cuando un estándar público tiene una posición, esta guía lo cita. El estándar para autenticación con códigos de verificación es NIST SP 800-63B, Digital Identity Guidelines: Authentication and Authenticator Management, consultado el 3 de septiembre de 2026. Las citas corresponden a la revisión vigente. La anterior Revision 3, que gran parte del material de OTP aún referencia, lleva el propio aviso de NIST: reemplazada por SP 800-63-4 a partir del 1 de agosto de 2025.
Usa un código de seis dígitos o más
Los códigos de Bird tienen por defecto 6 dígitos y options.code_length acepta de 4 a 8 por solicitud (Configuración de verificación). El valor predeterminado es la elección correcta y el extremo inferior del rango no lo es:
The verifier SHALL generate random authentication secrets that are at least six decimal digits (or equivalent) in length using an approved random bit generator
Un código de 4 o 5 dígitos está dentro de lo que API acepta y fuera de lo que el estándar permite. Acorta un código solo en un flujo en el que hayas decidido que la entropía del código de verificación no es la que aporta la seguridad.
Mantén la vida útil en diez minutos o menos
Duration tiene un valor predeterminado de 10 minutos y acepta de 1 a 999 (Configuración de verificación). El valor predeterminado coincide exactamente con el tope del estándar:
In all cases, the authentication SHALL be considered invalid unless completed within 10 minutes.
Así que el rango supera con creces el límite. Subir Duration para reducir las consultas de soporte por códigos expirados es el único cambio de configuración que saca silenciosamente un flujo de 800-63B. Si los códigos expiran antes de que los usuarios lleguen a ellos, la solución suele estar en la ruta de envío, no en la ventana de tiempo.
Espera dos capas de limitación de solicitudes
Un intento de comprobación está acotado dos veces, y los dos límites responden a amenazas distintas.
Tu configuración. Maximum Retries tiene un valor predeterminado de 5 y acepta de 1 a 10; superado ese límite, la verificación falla con attempts_exhausted. El estándar exige que este límite exista para un código tan corto, y añade una regla que conviene conocer:
Generating a new authentication secret SHALL NOT reset the failed authentication count.
El límite tiene un tope, y este es el único punto en el que todos los valores que ofrece Verify están cómodamente dentro del estándar:
the verifier SHALL limit consecutive failed authentication attempts using a specific authenticator on a single subscriber account to no more than 100 by disabling that authenticator
Maximum Retries llega como máximo a 10, así que ninguna configuración que puedas elegir alcanza 100. Es la imagen inversa de las dos secciones anteriores: la longitud del código y la duración tienen cada una un valor soportado que sale de 800-63B, y el límite de intentos no tiene ninguno.
Los topes de plataforma de Bird, que se aplican independientemente de tu configuración: 5 envíos por dirección por hora rodante entre inicios y reenvíos, y 10 comprobaciones por conjunto de dirección de destinatario por minuto. Ambos devuelven 429 con un encabezado Retry-After (Medidas contra abuso). Existen para evitar que el tráfico de OTP se use como arma contra tu presupuesto o el teléfono de un desconocido, así que trata un 429 aquí como el sistema funcionando correctamente, no como un límite que debas sortear.
Elige el canal según lo que estés verificando
Verify entrega por email, SMS, WhatsApp y Telegram (Canales). El estándar trata dos de ellos de forma muy distinta, y la distinción tiene que ver con para qué es el código, no con qué canal lo transporta.
El email no es un factor de autenticación. 800-63B es categórico:
Email SHALL NOT be used for out-of-band authentication because it may be vulnerable to:
- Access using only a password
- Interception in transit or at intermediate mail servers
- Rerouting attacks, such as those caused by Domain Name System (DNS) spoofing
El mismo pasaje contempla el caso para el que está construido el canal de email de Verify:
Confirmation codes that are sent to validate email addresses or are issued as recovery codes (see Sec. 4.2.1.2) are not authentication processes and not affected by the above prohibition.
Un código por email que demuestra que alguien controla una dirección entra dentro de la excepción del estándar y es válido. Un código por email usado como segundo factor en el inicio de sesión no lo es. Si tu flujo es el segundo caso, no recurras al canal de email.
SMS y voz están permitidos pero restringidos. La entrega por la red telefónica conlleva tres obligaciones, en dos oraciones consecutivas:
Accordingly, verifiers SHALL ensure that alternative authenticator types are available to all subscribers and SHOULD remind subscribers of this limitation of PSTN out-of-band authenticators before binding one or more devices controlled by the subscriber. Verifiers SHOULD consider risk indicators (e.g., device swap, SIM change, number porting, other abnormal behavior) before using the PSTN to deliver an out-of-band authentication secret.
El SHALL es un requisito de producto, no una configuración de Verify: si SMS es tu único factor, no lo cumples. El orden de canales por país de Verify ayuda con la entrega, porque WhatsApp se intenta primero en la mayoría de países con SMS como respaldo (Canales), pero SMS, WhatsApp y Telegram son todas rutas de entrega para un mismo tipo de autenticador, fuera de banda, así que alternar entre ellas no es ofrecer un tipo alternativo. Los dos SHOULD son los que implementa un equipo de producto: un aviso en el momento en que el usuario vincula un número de teléfono, y una revisión de señales de portabilidad y cambio de SIM antes de enviar a ese número.
Lee el fallo antes de reenviar
Un código que no llega y un código que llega y se teclea mal necesitan tratamientos distintos, y los eventos los diferencian (Eventos).
verify.attempt.undelivered incluye un reason, y lo que ocurre después depende del plan de canales del destinatario. En un destinatario con más de un canal disponible, no termina la verificación: Bird avanza al siguiente canal, que recibe su propio verify.attempt.sent, así que un único intento no entregado no es una verificación muerta y no necesita un reenvío de tu parte. En un destinatario con un solo canal no hay a dónde avanzar, y la verificación termina.
Ese final necesita su propio tratamiento, y un evento lo identifica. El estado failed es compartido: una verificación llega a él tanto cuando se enviaron demasiados códigos de verificación incorrectos, con reason attempts_exhausted, como cuando ningún canal previsto pudo entregar uno, con reason undeliverable. Solo el segundo emite verify.verification.failed. Así que en un destinatario con un solo canal es ese evento, no el estado, lo que te indica que nadie recibió un código.
Una propiedad para tener en cuenta desde el diseño en lugar de descubrirla después, y esta es propia de Bird y no del estándar: Eventos indica que los informes de entrega son orientativos, no garantizados, porque los operadores y proveedores de correo varían en lo que confirman y en la rapidez con que lo hacen. En algunos mercados un evento de intento llega minutos tarde o no distingue entre entrega y aceptación. Construye la ruta de "I didn't get a code" sobre POST /v1/verify/verifications/next-channel, que avanza la verificación, en lugar de esperar un evento de entrega que puede no llegar.
Reintenta la solicitud, no la verificación
Los tres endpoints de Verify aceptan Idempotency-Key (Reintentar de forma segura). Tras un timeout o una conexión interrumpida, reintentar con la misma clave reproduce la respuesta original: no se envía un segundo código ni se consume un intento de comprobación. Sin clave, un reintento tras una respuesta perdida envía otro código y gasta otro envío contra el tope por hora.
Conoce el coste de cada envío
La facturación se aplica a cada código enviado, a la tarifa del canal para el destino, y un reenvío o un respaldo añade un cargo por envío (Coste y facturación). Dos consecuencias condicionan una política de reintentos:
- Una verificación que pasa por varios canales cuesta varios envíos, así que un plan de canales amplio es tanto una decisión de coste como de entrega.
- Telegram factura en un punto distinto. Se consulta a Telegram si el número puede recibir un mensaje, y el cargo se aplica cuando la respuesta es sí, a una tarifa plana mundial. Un número que no puede ser alcanzado es gratuito y avanza al siguiente canal sin cargo. Así que un cargo significa aceptado para entrega, no entregado: un código que luego no se entrega sigue facturado, y la verificación paga de nuevo por el canal al que pase. Si no quieres ese segundo cargo, retira Telegram del orden de canales para esos países en la página de Countries (Coste y facturación).
Próximos pasos
- Envío de verificaciones: la configuración, las medidas de protección y la lista completa de estados
- Eventos: todos los eventos de sesión y entrega con su payload
- Countries: orden de canales por país y disponibilidad
- Remitentes y marca: el aspecto de cada mensaje
Recursos relacionados
Continúa con la documentación, guías y ejemplos de este tema.