Códigos de verificación de Supabase por WhatsApp
Supabase sigue creando y verificando el código. whatsapi lo entrega por WhatsApp, desde tu propio número, en lugar de por SMS. Funciona en Supabase Cloud y en un Supabase que alojas tú mismo.
1 · Crea una clave y vincula un número
Si todavía no lo hiciste, la guía de inicio muestra las dos cosas. Los códigos salen de tu número conectado y se cuentan en la clave de API que guarde el hook.
2 · Crea el hook
En la página Webhooks del panel, o con una llamada. El campo template es opcional. Pon {code} donde va el código.
import { Whatsapi } from "@whatsapi.sh/sdk";
const wa = new Whatsapi({ apiKey: "wa_your_key" });
const data = await wa.setSupabaseHook({ template: "Your Acme code is {code}" });{
"data": {
"secret": "v1,whsec_…", // shown once: copy it now
"record": {
"url": "https://api.whatsapi.sh/webhooks/supabase/send-sms/7d3e…",
"statusUrl": "https://api.whatsapi.sh/webhooks/supabase/send-sms/7d3e…/status",
"numberId": null,
"template": "Your Acme code is {code}",
"apiKey": { "id": "…", "name": "…", "prefix": "…", "revoked": false },
"createdAt": "…",
"updatedAt": "…"
}
}
}secret solo viene en la respuesta la primera vez que guardas y cuando lo rotas. Cópialo ahora. Pasa numberId para elegir desde qué número salen los códigos. Si no lo pasas, salen desde tu número conectado, siempre que tengas uno solo. Si no hay ninguno conectado, o hay más de uno, todos los códigos fallan hasta que elijas. Quitar el número elegido cuenta como no elegir ninguno.
3 · Apunta Supabase al hook
En Supabase Cloud
- En tu proyecto, abre Authentication → Auth Hooks y agrega un Send SMS hook. Elige HTTPS como tipo.
- Pega la
urlde la respuesta en el campo de la URL y elsecreten el campo del secreto, tal cual. El secreto ya empieza conv1,whsec_. Haz clic en Create hook. - Con el hook guardado, abre Authentication → Sign In / Providers → Phone. Activa Enable Phone provider y deja Enable phone confirmations activado, para que cada registro se confirme con un código. Pon SMS OTP Expiry en
300y guarda. No necesitas un proveedor de SMS: el hook ocupa su lugar.
En un Supabase que alojas tú mismo
Agrega estas cuatro líneas al environment del servicio auth (GoTrue) en tu docker-compose.yml. Pega la url y el secret de la respuesta tal cual. El secreto ya empieza con v1,whsec_.
services:
auth:
environment:
GOTRUE_HOOK_SEND_SMS_ENABLED: "true"
GOTRUE_HOOK_SEND_SMS_URI: "https://api.whatsapi.sh/webhooks/supabase/send-sms/<hook id>"
GOTRUE_HOOK_SEND_SMS_SECRETS: "<secret from the answer>"
GOTRUE_SMS_OTP_EXP: "300"El docker-compose.yml de Supabase ya trae estas variables comentadas, como # GOTRUE_HOOK_SEND_SMS_URI. Si el tuyo también las tiene, reemplaza esas líneas en vez de agregar otras al lado. Compose rechaza un archivo que repite una variable.
Activa el inicio de sesión por teléfono en .env, que está junto al docker-compose.yml. El compose de Supabase ya le pasa ENABLE_PHONE_SIGNUP a GoTrue como GOTRUE_EXTERNAL_PHONE_ENABLED, y ENABLE_PHONE_AUTOCONFIRM como GOTRUE_SMS_AUTOCONFIRM. No repitas esas dos en el compose.
ENABLE_PHONE_SIGNUP=true
ENABLE_PHONE_AUTOCONFIRM=falseSin ENABLE_PHONE_SIGNUP=true, el registro y el inicio de sesión por teléfono quedan desactivados. El archivo de ejemplo de Supabase trae ENABLE_PHONE_AUTOCONFIRM=true, que confirma los registros sin código. Cámbialo a false. Con el hook activado, GoTrue ignora GOTRUE_SMS_PROVIDER y su propia plantilla de SMS. El mensaje es la plantilla que guardaste aquí.
Después vuelve a crear el servicio auth. docker compose restart conserva los valores anteriores, así que ejecuta esto en su lugar:
docker compose up -d auth4 · Pruébalo
const phone = "+5511988887777";
// GoTrue creates a code, and whatsapi sends it to that WhatsApp.
await supabase.auth.signInWithOtp({ phone });
// Then check the code the person typed.
const { data, error } = await supabase.auth.verifyOtp({ phone, token: "482913", type: "sms" });El mensaje sale de tu número, con tu plantilla. Deja type: "sms" tal cual: para Supabase sigue siendo un código por teléfono.
5 · Ofrece el inicio de sesión por teléfono solo cuando funciona
Supabase no puede apagar el inicio de sesión por teléfono por su cuenta. Si tu número se desconecta, cada código falla y la persona se queda atascada en el formulario. Así que, antes de mostrar la opción de teléfono, consulta el statusUrl del hook. No pide clave, así que puedes llamarlo desde el navegador.
// Your hook's statusUrl. It needs no key, so it's safe in the browser.
const statusUrl = "https://api.whatsapi.sh/webhooks/supabase/send-sms/7d3e…/status";
async function phoneSignInWorks() {
try {
const res = await fetch(statusUrl);
return res.ok && (await res.json()).data.ready === true;
} catch {
return false; // Can't tell, so offer email instead.
}
}{ "data": { "ready": true } }ready es true cuando tu cuenta y la clave de API del hook están activas y el número que envía está conectado. La respuesta puede tener hasta 30 segundos. Si la llamada falla o no responde 200, tómalo como no disponible y ofrece el correo en su lugar. Un hook que no existe responde 404.
No puede saber si al número le quedan conversaciones nuevas en el día, porque eso depende de quién pide el código. Ese rechazo le sigue llegando a la persona, con el motivo.
Tiempo de espera y vencimiento
GoTrue le da 5 segundos a toda la llamada del hook. Pasado ese tiempo descarta el código, y ese código ya no se puede verificar aunque el mensaje llegue después. Respondemos en 4 segundos como máximo.
GOTRUE_SMS_OTP_EXP viene en 60 segundos por defecto. Es poco para WhatsApp: la persona tiene que ver el mensaje, abrirlo y escribir el código. Ponlo en 300, que son cinco minutos. En Supabase Cloud es SMS OTP Expiry, en la configuración del proveedor Phone.
Cuando un código no se puede enviar
Le decimos a GoTrue el motivo, con un texto pensado para quien está en el formulario, como “We can't send more codes to new numbers today. Try again tomorrow.” (el mensaje está en inglés). GoTrue v2.189.0 o posterior muestra ese mensaje. Las versiones anteriores muestran un error genérico del servidor, así que usa v2.189.0 o posterior.
Los límites propios de GoTrue se aplican antes de que nos llame. GOTRUE_SMS_MAX_FREQUENCY permite un código por teléfono cada 60 segundos, por defecto. GOTRUE_RATE_LIMIT_SMS_SENT permite 30 códigos por hora para todo tu Supabase, por defecto. Puedes subirlo si lo necesitas. En Supabase Cloud, ese límite por hora es Rate limit for sending SMS messages, en Authentication → Rate Limits. El límite de conversaciones nuevas del número, que se explica abajo, sigue aplicando.
Mientras no haya un número conectado, cada código se rechaza así. Supabase no puede apagar el inicio de sesión por teléfono por su cuenta, así que oculta esa opción hasta que el hook vuelva a estar listo, como muestra el paso 5.
Cada código es un mensaje de WhatsApp
Cada código se descuenta de la cuota de tu cuenta, como cualquier mensaje. El primer código a alguien con quien tu número nunca habló también gasta una de las conversaciones nuevas del día de ese número. Un número puede iniciar de 5 a 20 conversaciones nuevas al día, según cuánto tiempo lleva en uso la línea, salvo que le fijes otro límite. El hook envía desde un solo número, así que, cuando ya no quedan conversaciones nuevas en el día, los códigos a gente nueva se rechazan hasta el día siguiente. La gente que ya habló con tu número no cuenta.
Los códigos nunca esperan en la cola de envío ni muestran “escribiendo…”, porque hay alguien en el formulario aguardando el código. Cómo evitar el baneo explica el límite de conversaciones nuevas.
Rotar el secreto
Guarda el hook con rotateSecret: true. La respuesta trae un secreto nuevo, y el anterior sigue funcionando durante 24 horas.
import { Whatsapi } from "@whatsapi.sh/sdk";
const wa = new Whatsapi({ apiKey: "wa_your_key" });
const data = await wa.setSupabaseHook({ rotateSecret: true });Dentro de esas 24 horas, pon el secreto nuevo en Supabase. En Supabase Cloud, abre el Send SMS hook en Authentication → Auth Hooks y pega el secreto nuevo en lugar del anterior. Si alojas Supabase tú mismo, ponlo en GOTRUE_HOOK_SEND_SMS_SECRETS y vuelve a crear el servicio auth. Durante el cambio, puedes dejar los dos ahí, separados por |. GoTrue firma con cada uno.
GOTRUE_HOOK_SEND_SMS_SECRETS: "v1,whsec_new…|v1,whsec_old…"Conviene saber
- La plantilla solo acepta
{code}. Quien decide cuándo vence el código es Supabase. Si quieres el plazo en el mensaje, escríbelo tú y mantenlo igual al vencimiento que pusiste en el paso 3. - Cuando alguien cambia su número de teléfono, el código va al número nuevo.
- GoTrue solo llama a direcciones
https, salvo localhost. La URI siempre empieza conhttps://api.whatsapi.sh, aunque Supabase corra en el mismo servidor que whatsapi.
Cuando algo sale mal
A la izquierda, el error que tu app recibe de Supabase.
| Hook requires authorization token | No pudimos verificar la firma. El secreto del hook en Supabase no coincide con el nuestro, o el reloj del servidor está desfasado más de 5 minutos. Pega el secreto otra vez, o rótalo, y revisa el reloj. |
| Unexpected status code returned from hook: 404 | No hay ningún hook en esa dirección. La URI tiene un error de tipeo, o el hook se borró. Un hook nuevo trae URL y secreto nuevos, así que actualiza los dos. |
| Failed to reach hook within maximum time of 5.000000 seconds | GoTrue no pudo conectarse con nosotros en 5 segundos, y ese código ya no se va a poder verificar. Si alojas Supabase tú mismo, revisa que el contenedor de auth pueda acceder a https://api.whatsapi.sh: DNS, firewall, proxy. Después pide un código nuevo. |
| Service currently unavailable due to hook / Invalid payload sent to hook | En versiones anteriores a v2.189.0, GoTrue muestra esto en lugar de nuestro motivo cuando un código no se pudo enviar. Actualiza para ver el motivo, o revisa el panel. |
Si un código no llega, abre el panel. Revisa que el número siga conectado, que tu cuota no se haya agotado y que al número le queden conversaciones nuevas en el día. Si revocaste la clave de API que guardó el hook, los códigos también se detienen. Guarda el hook otra vez con una clave activa.