Sign in a user through OTP
Send a magic-link or SMS one-time password.
Trigger a passwordless sign-in by sending the user a one-time code (or magic link, for email). The user completes sign-in by calling verify_otp with the code, or by clicking the magic link in the email.
By default, GoTrue will create the user if no account exists. Pass options: { should_create_user: false } to require an existing account.
Signature
supabase.auth.sign_in_with_otp(credentials)credentials is a hash — call with a literal ({ email: "..." }) or Ruby's hash-literal shorthand (email: "...").
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
email | String | Optional | User email. Provide either email or phone. |
phone | String | Optional | User phone number in E.164 format. Provide either email or phone. |
options | Hash | Optional | Nested options: email_redirect_to (String, URL embedded in the magic link), should_create_user (Boolean, default true, set false to require an existing account), data (Hash, user_metadata applied if the user is created), channel (String, sms or whatsapp for phone OTP; defaults to sms), captcha_token (String). |
Returns
A Struct with :message_id, :user, and :session. For magic-link / OTP flows the user and session fields are nil until the code is verified — only message_id is populated.
Example — email magic link
response = supabase.auth.sign_in_with_otp(
email: "ada@example.com",
options: { email_redirect_to: "https://app.example.com/auth/callback" }
)
response.message_id # => "..."Example — SMS OTP
response = supabase.auth.sign_in_with_otp(
phone: "+15555550123",
options: { channel: "sms" }
)
# Later, after the user reads the SMS:
supabase.auth.verify_otp(
phone: "+15555550123",
token: "123456",
type: "sms"
)Example — require an existing account
response = supabase.auth.sign_in_with_otp(
email: "ada@example.com",
options: { should_create_user: false }
)Missing both email and phone raises Supabase::Auth::Errors::AuthInvalidCredentialsError. verify_otp is the second step.