Magic Link Sign-in

Passwordless email sign-in for the inference shell workspace. The same email includes a clickable link and a short login code for users who read email on a different device than the one they sign in on.

The workspace app uses this flow on the login page. You can call the endpoints directly to build a custom sign-in experience.

No API key is required for send or verify endpoints. Successful sign-in sets a session cookie on the response (same cookie the workspace uses for browser sessions). For accounts that require a second factor, the session cookie is issued only after the second factor is verified. See Login second factor.

→ Authentication · Device authorization (CLI and IDE login)


Flow overview

mermaid
1sequenceDiagram2    participant Client as Your app or browser3    participant API as api.inference.sh4    participant Email as User email56    Client->>API: POST /auth/magic-link/send {email}7    API->>Email: Magic link + 5-char login code8    alt Click link (same device)9        Email->>API: GET /auth/magic-link/verify?token=…10        API-->>Client: Redirect (session cookie set, or 2FA prompt)11    else Enter code (different device)12        Client->>API: POST /auth/magic-link/verify-code {email, code}13        API-->>Client: AuthResponse (session cookie set, or challenge_token)14    end15    opt Login second factor required16        Client->>API: POST /auth/otp/verify {method, code, challenge_token}17        API-->>Client: AuthResponse + session cookie18    end
  1. Call POST /auth/magic-link/send with the user's email.
  2. The user receives an email with a verification link and a 5-character login code (valid for 15 minutes). Both redeem the same challenge — using one consumes the other.
  3. Complete sign-in by clicking the link (GET /auth/magic-link/verify) or submitting the code (POST /auth/magic-link/verify-code).
  4. Platform staff always require a second factor before a session is created. Members require one when two-step verification is turned on. After magic-link or code sign-in, browser clients are redirected to /auth/verify?challenge_token=… (and optional redirect_to). JSON clients receive otp_required: true, otp_methods, and challenge_token. Complete sign-in with the verify endpoints. No session cookie is issued until verification succeeds.

POST /auth/magic-link/send

Authentication: None.

Request body

FieldTypeRequiredDescription
emailstringYesEmail address to sign in with. Surrounding whitespace is ignored; matching is case-insensitive. New accounts store the trimmed, lowercased form (see REST overview: Signup email restrictions).
redirect_tostringNoPath or URL appended to the verification link (for example /dashboard)
bash
1curl -X POST https://api.inference.sh/auth/magic-link/send \2  -H "Content-Type: application/json" \3  -d '{"email": "you@example.com"}'

Response

The API always returns success to prevent email enumeration, even when the address is unknown or rate limited (except IP-level throttling):

json
1{2  "message": "If an account exists, a magic link has been sent"3}

When the same email requests another link within the burst window (about one per minute), the response includes:

json
1{2  "message": "Please wait before requesting another link",3  "rate_limited": true,4  "retry_after": 605}

IP-level rate limits return HTTP 429 with a Retry-After header (seconds). The header is exposed to browser clients via CORS.

Errors

CodeHTTPDescription
missing_email400email is empty
disposable_email400Temporary or disposable email domain. Applies to new signups only; existing accounts are not affected. The same restriction applies when an account is created through OAuth. See REST overview: Signup email restrictions.
invalid_email400Malformed address. When plus-alias signup blocking is enabled, also returned for a new sign-up whose local part contains + on providers that do not fold aliases into one mailbox (Gmail/Googlemail and existing accounts are exempt). See REST overview: Signup email restrictions.

GET /auth/magic-link/verify?token={token}

Authentication: None.

Used when the user clicks the link in the email. On success, the API sets the session cookie and redirects to the workspace (or to redirect_to when provided at send time).

Accounts that require a login second factor are redirected without a session cookie to /auth/verify with a challenge_token query parameter (and redirect_to when the sign-in carried a safe in-app destination).

For SPA or mobile clients that handle the token themselves, use the JSON endpoint below instead.


Verify via token (JSON)

POST /auth/magic-link/verify

Authentication: None.

Request body

FieldTypeRequiredDescription
tokenstringYesToken from the magic link URL
bash
1curl -X POST https://api.inference.sh/auth/magic-link/verify \2  -H "Content-Type: application/json" \3  -c cookies.txt \4  -d '{"token": "TOKEN_FROM_EMAIL_LINK"}'

Response

When sign-in completes without 2FA:

json
1{2  "user": { "id": "user_abc", "email": "you@example.com", "username": "you" },3  "session_id": "sess_xyz",4  "otp_required": false5}

When a login second factor is required, the response omits session_id and no session cookie is set:

json
1{2  "user": { "id": "user_abc", "email": "you@example.com", "username": "you" },3  "otp_required": true,4  "otp_methods": ["email"],5  "challenge_token": "CHALLENGE_TOKEN"6}

otp_methods lists the factors the account may use (email, totp, passkey, recovery), default order first. Complete login second factor with the challenge_token before calling authenticated routes. The session cookie is set on the successful POST /auth/otp/verify response.


Verify via login code

POST /auth/magic-link/verify-code

Authentication: None.

Device-independent sign-in: the user reads the 5-character code from email and enters it on the sign-in device.

Request body

FieldTypeRequiredDescription
emailstringYesSame email used in send (whitespace and letter case are ignored when matching)
codestringYes5-character code from the email (case-insensitive)
bash
1curl -X POST https://api.inference.sh/auth/magic-link/verify-code \2  -H "Content-Type: application/json" \3  -c cookies.txt \4  -d '{"email": "you@example.com", "code": "AB3K9"}'

Response

Same AuthResponse shape as verify via token. The session cookie is set when sign-in completes: immediately when no 2FA is required, after OTP verification otherwise.

Errors

CodeHTTPDescription
invalid_code400Code wrong, expired, or already used
account_locked429Too many failed attempts for this email
rate_limited429Too many verification attempts from this IP

Failed code attempts count toward per-email lockout. Codes use an alphanumeric charset without ambiguous characters (0, 1, O, I, L).


Login second factor

Passwordless sign-in (magic link, login code, OAuth, or SAML) may stop before a session exists when the account requires a second factor. This is separate from the 5-character login code in the email.

ModeWhenCredential
Pre-sessionImmediately after sign-in, before any session existschallenge_token from the auth response or /auth/verify URL
Re-authA sensitive route returns 403 with code otp_required on an existing sessionSession cookie

The verify routes accept method (email, totp, passkey, recovery), an emailed or authenticator code, or a passkey assertion from POST /auth/passkey/assert/begin. Login challenges expire after 10 minutes. After a successful verify, the session is marked re-authenticated for 4 hours.

Full request shapes, passkey flows, lockout rules, and re-auth behavior are documented in Two-step verification API: login second factor.

bash
1curl "https://api.inference.sh/auth/otp/status?challenge_token=CHALLENGE_TOKEN"23curl -X POST https://api.inference.sh/auth/otp/verify \4  -H "Content-Type: application/json" \5  -c cookies.txt \6  -d '{"challenge_token": "CHALLENGE_TOKEN", "method": "email", "code": "123456"}'

On success, POST /auth/otp/verify returns an AuthResponse with session_id and sets the session cookie. The same pre-session flow applies to OAuth and SAML when otp_required is true.

Re-auth (existing session)

When an OTP-gated route returns 403 with code otp_required, call GET /auth/otp/status and POST /auth/otp/verify with the session cookie (no challenge_token). Resend applies only when email is among the offered methods.


we use cookies

we use cookies to ensure you get the best experience on our website. for more information on how we use cookies, please see our cookie policy.

by clicking "accept", you agree to our use of cookies.
learn more.