Inference Logoinference.sh

Magic Link Sign-in

Passwordless email sign-in for the inference.sh 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 verification sets a session cookie on the response (same cookie the workspace uses for browser sessions).

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 to workspace (session cookie set)11    else Enter code (different device)12        Client->>API: POST /auth/magic-link/verify-code {email, code}13        API-->>Client: AuthResponse + session cookie14    end15    opt Admin account16        Client->>API: POST /auth/otp/verify {code}17        API-->>Client: Session fully unlocked18    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. Admin accounts require a second factor: after magic-link or code sign-in, the API returns otp_required: true and emails a 6-digit OTP. Call POST /auth/otp/verify before accessing OTP-gated routes. If the OTP expires, the next gated request automatically sends a fresh code.

POST /auth/magic-link/send

Authentication: None.

Request body

FieldTypeRequiredDescription
emailstringYesEmail address to sign in with
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": "[email protected]"}'

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.


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). Admin users are redirected with otp_required=true in the query string.

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

json
1{2  "user": { "id": "user_abc", "email": "[email protected]", "username": "you" },3  "session_id": "sess_xyz",4  "otp_required": false5}

When otp_required is true, complete OTP verification before calling OTP-gated API routes. The session cookie is set on the response either way.


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
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": "[email protected]", "code": "AB3K9"}'

Response

Same AuthResponse shape as verify via token. The session cookie is set on success.

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).


Admin OTP (second factor)

Admin accounts require email OTP after magic-link or code sign-in. This is separate from the passwordless login code — it is session-bound 2FA.

EndpointMethodDescription
/auth/otp/statusGETWhether the current session needs OTP (required, verified)
/auth/otp/verifyPOSTBody: { "code": "123456" } — completes 2FA
/auth/otp/resendPOSTSends a new OTP to the admin's email

All OTP routes require the session cookie from magic-link verification.

When an OTP-gated route returns 403 with code otp_required, the platform automatically emails a fresh OTP if the previous one is missing or expired (so the prompt is never a dead end).

bash
1# Check whether OTP is still required2curl https://api.inference.sh/auth/otp/status -b cookies.txt34# Submit the 6-digit code from email5curl -X POST https://api.inference.sh/auth/otp/verify \6  -H "Content-Type: application/json" \7  -b cookies.txt \8  -d '{"code": "123456"}'

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.