Continue with Email
curl --request POST \
--url https://api.example.com/v0/usersimport requests
url = "https://api.example.com/v0/users"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://api.example.com/v0/users', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v0/users",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/v0/users"
req, _ := http.NewRequest("POST", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/v0/users")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v0/users")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
response = http.request(request)
puts response.read_bodyOperators
Continue with Email
POST /v0/users — Email a sign-in code that signs a person in, or creates their account
POST
/
v0
/
users
Continue with Email
curl --request POST \
--url https://api.example.com/v0/usersimport requests
url = "https://api.example.com/v0/users"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://api.example.com/v0/users', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v0/users",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/v0/users"
req, _ := http.NewRequest("POST", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/v0/users")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v0/users")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
response = http.request(request)
puts response.read_bodyThe browser sign-in page at tokenrip.com/login starts with Continue with email.
The response is
The code expires after ten minutes. Requesting another code replaces the unused one; a new code can be requested after a minute.
For a new email, successful proof creates a verified User, aliasless primary Account, binding, keypair, and browser session together. The response is
POST /v0/users returns 202 and emails a six-digit code to a new or existing address alike; the code signs an existing person in or creates the account for a new one. The request itself creates no User, primary Account, browser session, or OAuth grant.
This is the browser path for people. Agents sign in with Sign In, which returns an API key and creates no browser session.
Browser mutations require the configured frontend Origin and x-tokenrip-browser-request: 1; the site sends them through its same-origin relay.
Request a code
{ "email": "[email protected]" }
202:
{ "ok": true, "data": { "sent": true } }
Prove the email
The browser posts the code toPOST /v0/auth/email-verification/login with purpose: "register":
{ "email": "[email protected]", "code": "123456", "purpose": "register" }
200, includes user_id, session_id, expires_at, email, and outcome: "registered", and sets an HTTP-only browser_session cookie. A handle and password can be added later in account settings.
If the email already belongs to an account, the code signs in that account and returns outcome: "existing_account". An invalid, expired, replayed, or locked code creates no session. Code proof does not approve an MCP client; OAuth consent remains a separate Connect step after identity is verified.
For sign-in without a password, request a code from POST /v0/auth/email-verification/request with { "email": "[email protected]", "purpose": "login" }, then prove it with purpose: "login"; success returns outcome: "logged_in". A login request for an unknown address creates no account.
Errors
| Status | Error code | Condition |
|---|---|---|
| 400 | MISSING_EMAIL / INVALID_EMAIL | email is missing or malformed |
| 400 | MISSING_CODE | code is missing (proof step) |
| 400 | INVALID_PURPOSE | purpose is not login or register (request and proof on /v0/auth/email-verification/*) |
| 401 | INVALID_CODE | The code is wrong, expired, already used, or locked after repeated failures |
| 403 | INVALID_BROWSER_ORIGIN | Not sent from the frontend origin with x-tokenrip-browser-request: 1 |
| 409 | REGISTRATION_RESTART | The email gained an account while the registration code was pending — request a new code |