curl --request POST \
--url https://api.sandbox.sardine.ai/v1/identity/kyh/widget \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/json' \
--data '
{
"sessionKey": "client-session-123",
"code": "482913"
}
'import requests
url = "https://api.sandbox.sardine.ai/v1/identity/kyh/widget"
payload = {
"sessionKey": "client-session-123",
"code": "482913"
}
headers = {
"Authorization": "Basic <encoded-value>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Basic <encoded-value>', 'Content-Type': 'application/json'},
body: JSON.stringify({sessionKey: 'client-session-123', code: '482913'})
};
fetch('https://api.sandbox.sardine.ai/v1/identity/kyh/widget', 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.sandbox.sardine.ai/v1/identity/kyh/widget",
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([
'sessionKey' => 'client-session-123',
'code' => '482913'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Basic <encoded-value>",
"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.sandbox.sardine.ai/v1/identity/kyh/widget"
payload := strings.NewReader("{\n \"sessionKey\": \"client-session-123\",\n \"code\": \"482913\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Basic <encoded-value>")
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.sandbox.sardine.ai/v1/identity/kyh/widget")
.header("Authorization", "Basic <encoded-value>")
.header("Content-Type", "application/json")
.body("{\n \"sessionKey\": \"client-session-123\",\n \"code\": \"482913\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sandbox.sardine.ai/v1/identity/kyh/widget")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Basic <encoded-value>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"sessionKey\": \"client-session-123\",\n \"code\": \"482913\"\n}"
response = http.request(request)
puts response.read_body{
"widgetUrl": "https://identity.sardine.ai/kyh?token=Zk3x9...",
"expiresAt": "2026-11-09T18:05:00Z"
}{
"error": "Customer not found for customer ID",
"status": 400
}Create KYH Tracking Link
Creates a short-lived tracking link for the human an AI agent is acting on behalf of. Send the link to the human (for example by SMS or email). When they open it on their own device, Sardine’s device SDK collects device and behavior intelligence (DI/BB) under the sessionKey you provided, and the page shows the human your code (an OTP) to give to the agent.
How it works
- Call this endpoint with a
sessionKeyand a one-timecodeof your own. - Deliver the returned
widgetUrlto the human. - The human opens the link on their device. Signals are collected under your
sessionKey. - Call the Sardine Customers or Devices API with the same
sessionKeyto retrieve DI/BB signals as you normally would. The link only collects signals; it does not return them.
Notes
-
The link expires 5 minutes after creation (see
expiresAt). -
The link can be opened a limited number of times (SMS link scanners may open it first); after that it returns
410 Gone. -
Only one link is active per
sessionKey. Creating a new link for the samesessionKeyreplaces the previous one, which then stops working. -
The
codeis never returned in the API response.
curl --request POST \
--url https://api.sandbox.sardine.ai/v1/identity/kyh/widget \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/json' \
--data '
{
"sessionKey": "client-session-123",
"code": "482913"
}
'import requests
url = "https://api.sandbox.sardine.ai/v1/identity/kyh/widget"
payload = {
"sessionKey": "client-session-123",
"code": "482913"
}
headers = {
"Authorization": "Basic <encoded-value>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Basic <encoded-value>', 'Content-Type': 'application/json'},
body: JSON.stringify({sessionKey: 'client-session-123', code: '482913'})
};
fetch('https://api.sandbox.sardine.ai/v1/identity/kyh/widget', 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.sandbox.sardine.ai/v1/identity/kyh/widget",
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([
'sessionKey' => 'client-session-123',
'code' => '482913'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Basic <encoded-value>",
"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.sandbox.sardine.ai/v1/identity/kyh/widget"
payload := strings.NewReader("{\n \"sessionKey\": \"client-session-123\",\n \"code\": \"482913\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Basic <encoded-value>")
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.sandbox.sardine.ai/v1/identity/kyh/widget")
.header("Authorization", "Basic <encoded-value>")
.header("Content-Type", "application/json")
.body("{\n \"sessionKey\": \"client-session-123\",\n \"code\": \"482913\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sandbox.sardine.ai/v1/identity/kyh/widget")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Basic <encoded-value>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"sessionKey\": \"client-session-123\",\n \"code\": \"482913\"\n}"
response = http.request(request)
puts response.read_body{
"widgetUrl": "https://identity.sardine.ai/kyh?token=Zk3x9...",
"expiresAt": "2026-11-09T18:05:00Z"
}{
"error": "Customer not found for customer ID",
"status": 400
}Authorizations
HTTP Basic Auth. Use your clientId as the username and clientSecret as the password.
Body
Your session key for this login or signup. Use the same value when you later call the Customers or Devices API to retrieve signals.
255"client-session-123"
The code (OTP) to show the human once they open the link. 4 to 12 letters or digits.
^[A-Za-z0-9]{4,12}$"482913"