Send a sign-in code
Send a one-time sign-in code, and a link carrying it, to an address that has an account. Any
account can sign in this way, with or without a password. The code is six digits and is valid for 15
minutes. The call answers 202 with the same body for every address; an address without an account
receives no email and no account is created. The shopper completes sign-in with
POST /v1/auth/verify-otp.
Sign-in by code is on for every store unless the merchant switches it off; store.auth.passwordless
in the store information reports the setting. When it is off, the call answers
403 passwordless_disabled.
redirect_to names the storefront page that receives the shopper from the email, which appends
?link_token=<token> to it. It must be on one of the store’s allowed storefront origins; otherwise
it is ignored and the email carries the code only.
At most one email per address is sent every 60 seconds and five an hour; a call past that is
answered 202 and sends nothing.
Secret key required. This endpoint requires a secret key (tybrite_sk_*). Publishable keys
answer 403 forbidden. Account calls are made from a server, which then holds the shopper’s session.
Attempt limits: with a secret key, past 5 attempts for one email address from one store in
15 minutes, the call is slowed to one attempt every 60 seconds; other calls for that address
answer 429 rate_limited with a Retry-After header giving the seconds until the next attempt is
accepted. With a publishable key, attempts are limited per calling address: 20 per address and email,
and 60 per address, in 15 minutes. Across every store and address, past 10 attempts for one
email the call is slowed to one every 60 seconds. Every attempt counts, successful or not,
and an address with no account is treated identically.
curl --request POST \
--url https://api.tybritelabs.com/v1/auth/magic-link \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"email": "john.doe@example.com",
"redirect_to": "https://shop.example.com/account?link=signin"
}
'import requests
url = "https://api.tybritelabs.com/v1/auth/magic-link"
payload = {
"email": "john.doe@example.com",
"redirect_to": "https://shop.example.com/account?link=signin"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: 'john.doe@example.com',
redirect_to: 'https://shop.example.com/account?link=signin'
})
};
fetch('https://api.tybritelabs.com/v1/auth/magic-link', 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.tybritelabs.com/v1/auth/magic-link",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => 'john.doe@example.com',
'redirect_to' => 'https://shop.example.com/account?link=signin'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tybritelabs.com/v1/auth/magic-link"
payload := strings.NewReader("{\n \"email\": \"john.doe@example.com\",\n \"redirect_to\": \"https://shop.example.com/account?link=signin\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
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.tybritelabs.com/v1/auth/magic-link")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"john.doe@example.com\",\n \"redirect_to\": \"https://shop.example.com/account?link=signin\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tybritelabs.com/v1/auth/magic-link")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"john.doe@example.com\",\n \"redirect_to\": \"https://shop.example.com/account?link=signin\"\n}"
response = http.request(request)
puts response.read_body{
"status": "code_sent",
"expires_in": 900
}{
"error": {
"code": "invalid_request",
"message": "Invalid request parameters",
"details": "The 'email' field is required"
}
}{
"error": {
"code": "unauthorized",
"message": "Missing or invalid Authorization header"
}
}{
"error": {
"code": "passwordless_disabled",
"message": "This store does not offer sign-in by emailed code."
}
}{
"error": {
"code": "rate_limited",
"message": "Too many requests for this API key. Please slow down and try again shortly.",
"remaining": 0,
"reset": 1706198400
}
}{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}Authorizations
API Key Authentication
Use your API key in the Authorization header:
Authorization: Bearer tybrite_sk_live_YOUR_KEY
Key Types:
Secret Keys (Server-Side Only):
- Format:
tybrite_sk_live_*(production) ortybrite_sk_test_*(sandbox) - Full read/write access to all endpoints
- ⚠️ NEVER expose in client-side code or public repositories
- Required for: every write a shopper does not make for themselves, authentication, payment verification, AI recommendations
Publishable Keys (Client-Safe):
- Format:
tybrite_pk_live_*(production) ortybrite_pk_test_*(sandbox) - Catalogue reads, plus the actions a shopper takes for themselves
- ✅ Safe for client-side JavaScript, mobile apps, and public code
- Allowed for: browsing products, search, CMS content, pricing queries, cart and wishlist, reviews, storefront events, and the Agent API's quotes, cart drafts and checkout intents
Endpoint-Specific Requirements:
- Authentication endpoints (
/v1/auth/*): Secret key required - Payment verification (
POST /v1/payments/verify): Secret key required - AI Recommendations (
POST /v1/recommendations): Secret key required - Semantic Search (
POST /v1/search): Both key types allowed (read-only operation) - Shopper actions (cart, wishlist, reviews, storefront events, Agent API quotes, cart drafts and checkout intents): Both key types allowed
- All other write operations: Secret key required
- All read operations: Both key types allowed
Using a publishable key for restricted operations returns 403 Forbidden.
Body
Response
Accepted. The body is the same for every address.
The answer to a request for an emailed code. It is identical whether or not the address has an account; an address without an account receives no email.
curl --request POST \
--url https://api.tybritelabs.com/v1/auth/magic-link \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"email": "john.doe@example.com",
"redirect_to": "https://shop.example.com/account?link=signin"
}
'import requests
url = "https://api.tybritelabs.com/v1/auth/magic-link"
payload = {
"email": "john.doe@example.com",
"redirect_to": "https://shop.example.com/account?link=signin"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: 'john.doe@example.com',
redirect_to: 'https://shop.example.com/account?link=signin'
})
};
fetch('https://api.tybritelabs.com/v1/auth/magic-link', 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.tybritelabs.com/v1/auth/magic-link",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => 'john.doe@example.com',
'redirect_to' => 'https://shop.example.com/account?link=signin'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tybritelabs.com/v1/auth/magic-link"
payload := strings.NewReader("{\n \"email\": \"john.doe@example.com\",\n \"redirect_to\": \"https://shop.example.com/account?link=signin\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
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.tybritelabs.com/v1/auth/magic-link")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"email\": \"john.doe@example.com\",\n \"redirect_to\": \"https://shop.example.com/account?link=signin\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tybritelabs.com/v1/auth/magic-link")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"john.doe@example.com\",\n \"redirect_to\": \"https://shop.example.com/account?link=signin\"\n}"
response = http.request(request)
puts response.read_body{
"status": "code_sent",
"expires_in": 900
}{
"error": {
"code": "invalid_request",
"message": "Invalid request parameters",
"details": "The 'email' field is required"
}
}{
"error": {
"code": "unauthorized",
"message": "Missing or invalid Authorization header"
}
}{
"error": {
"code": "passwordless_disabled",
"message": "This store does not offer sign-in by emailed code."
}
}{
"error": {
"code": "rate_limited",
"message": "Too many requests for this API key. Please slow down and try again shortly.",
"remaining": 0,
"reset": 1706198400
}
}{
"error": {
"code": "server_error",
"message": "An unexpected error occurred"
}
}
