Send magic link / OTP
Send a passwordless authentication link or OTP code to the customer’s email. The link/code is valid for 15 minutes.
⚠️ SECRET KEY REQUIRED
This endpoint requires a secret key (tybrite_sk_*). Publishable keys will return 403 Forbidden.
Attempt limits: an account is never locked by attempts made on it. 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: 10 per address and email, and 30 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@tybritelabs.com"
}
'import requests
url = "https://api.tybritelabs.com/v1/auth/magic-link"
payload = { "email": "john.doe@tybritelabs.com" }
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@tybritelabs.com'})
};
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@tybritelabs.com'
]),
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@tybritelabs.com\"\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@tybritelabs.com\"\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@tybritelabs.com\"\n}"
response = http.request(request)
puts response.read_body{
"message": "Magic link sent",
"email": "john.doe@tybritelabs.com",
"hint": "Check your email for the login link or OTP code"
}{
"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": "forbidden",
"message": "This operation requires a secret key. Publishable keys cover catalogue reads and the actions a shopper takes for themselves."
}
}{
"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
Customer email address
"john.doe@tybritelabs.com"
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@tybritelabs.com"
}
'import requests
url = "https://api.tybritelabs.com/v1/auth/magic-link"
payload = { "email": "john.doe@tybritelabs.com" }
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@tybritelabs.com'})
};
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@tybritelabs.com'
]),
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@tybritelabs.com\"\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@tybritelabs.com\"\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@tybritelabs.com\"\n}"
response = http.request(request)
puts response.read_body{
"message": "Magic link sent",
"email": "john.doe@tybritelabs.com",
"hint": "Check your email for the login link or OTP code"
}{
"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": "forbidden",
"message": "This operation requires a secret key. Publishable keys cover catalogue reads and the actions a shopper takes for themselves."
}
}{
"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"
}
}
