Environment - Supabase Cloud project: qqjrnulwsrbshdoncojz (production) - Auth: Phone OTP with “Send SMS” Hook enabled - Client: supabase-js 2.x (SSR + Browser, Next.js 15 App Router) - Edge Function: relay-auth (Deno edge) sends SMS via AWS SNS (hook-only) - Hook secret configured (Standard Webhooks header supported) Context - We switched to the new Auth “Send SMS” hook. The dashboard calls supabase.auth.signInWithOtp() directly. Supabase invokes our hook, and we deliver the SMS successfully via AWS SNS. However, OTP verification fails because Supabase’s / otp endpoint errors before storing the OTP. Problem - /otp returns 500 with “relation auth.otp_attempts does not exist” (SQLSTATE 42P01) - As a result, verifyOtp() responds “token has expired or is invalid” even though the user received a code. Steps To Reproduce 1. Enable Phone provider and configure Auth → Hooks → Send SMS to our edge function URL (relay-auth). 2. Client calls: - signInWithOtp({ phone, options: { shouldCreateUser: true, channel: 'sms' } }) 3. Hook runs and returns 200; SMS delivered (confirmed by logs). 4. Supabase logs show /otp 500 error (missing auth.otp_attempts). 5. Client calls verifyOtp({ phone, token, type: 'sms' }) → “token has expired or is invalid”. Relevant Logs - /otp failure - msg: Unhandled server error: ERROR: relation "otp_attempts" does not exist (SQLSTATE 42P01) - path: /otp - referer: http://localhost:3000 - /verify failure - msg: token has expired or is invalid - path: /verify (We can provide full log lines with request IDs if helpful.) Expected Behavior - /otp persists OTP attempt and returns 2xx. - verifyOtp succeeds when a valid code is provided. Actual Behavior - /otp 500 due to missing auth.otp_attempts, so no OTP is stored. - verifyOtp fails with “token expired or invalid”. What We Tried - Attempted to create the table via SQL Editor: - CREATE TABLE auth.otp_attempts (…) → ERROR 42501: permission denied for schema auth. - Verified hook config/secret and that SMS is delivered from the hook. - Phone provider enabled; Twilio set with placeholders (hook is the only sender). Request / Questions - Please provision/enable the internal objects required by Phone OTP + “Send SMS” hook in the auth schema for our project. Specifically, the following table and indexes appear to be required by /otp rate limiting/attempt logging: - CREATE TABLE IF NOT EXISTS auth.otp_attempts ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, phone_number TEXT, email TEXT, attempt_type TEXT NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW(), ip_address INET, user_agent TEXT ); - CREATE INDEX IF NOT EXISTS idx_auth_otp_attempts_phone_created ON auth.otp_attempts(phone_number, created_at) WHERE phone_number IS NOT NULL; - CREATE INDEX IF NOT EXISTS idx_auth_otp_attempts_email_created ON auth.otp_attempts(email, created_at) WHERE email IS NOT NULL; - CREATE INDEX IF NOT EXISTS idx_auth_otp_attempts_ip_created ON auth.otp_attempts(ip_address, created_at) WHERE ip_address IS NOT NULL; - If this table is created by an internal migration/feature flag when enabling the Send SMS hook, could you run/enable it for project qqjrnulwsrbshdoncojz? - Is enabling a vendor (e.g., Twilio) still required when using the hook-only path, or should the hook work independently without creating vendor tables? Minimal Client Snippet - Sign-in: - await supabase.auth.signInWithOtp({ phone, options: { shouldCreateUser: true, channel: 'sms' } }) - Verify: - await supabase.auth.verifyOtp({ phone, token, type: 'sms' }) - Hook (relay-auth): - Validates hook signature, normalizes E.164, sends SMS via AWS SNS, returns 200. Impact - Users receive an SMS but cannot login (production-blocking). Thank you — happy to provide additional logs or run any diagnostics needed.