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
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- Call
POST /auth/magic-link/sendwith the user's email. - 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.
- Complete sign-in by clicking the link (
GET /auth/magic-link/verify) or submitting the code (POST /auth/magic-link/verify-code). - 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 optionalredirect_to). JSON clients receiveotp_required: true,otp_methods, andchallenge_token. Complete sign-in with the verify endpoints. No session cookie is issued until verification succeeds.
Send magic link
POST /auth/magic-link/send
Authentication: None.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Email 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_to | string | No | Path or URL appended to the verification link (for example /dashboard) |
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):
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:
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
| Code | HTTP | Description |
|---|---|---|
missing_email | 400 | email is empty |
disposable_email | 400 | Temporary 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_email | 400 | Malformed 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. |
Verify via link (browser)
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
| Field | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Token from the magic link URL |
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:
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:
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
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Same email used in send (whitespace and letter case are ignored when matching) |
code | string | Yes | 5-character code from the email (case-insensitive) |
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
| Code | HTTP | Description |
|---|---|---|
invalid_code | 400 | Code wrong, expired, or already used |
account_locked | 429 | Too many failed attempts for this email |
rate_limited | 429 | Too 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.
| Mode | When | Credential |
|---|---|---|
| Pre-session | Immediately after sign-in, before any session exists | challenge_token from the auth response or /auth/verify URL |
| Re-auth | A sensitive route returns 403 with code otp_required on an existing session | Session 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.
Pre-session (magic link JSON)
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.
Related
- Device authorization — CLI and IDE login (returns an API key instead of a session cookie)
- Authentication — API keys for programmatic access
- REST API overview —
/v1/path prefix removed