curl --request POST \
--url https://api.cloud.corbado.io/v1/observe/passkeyIntelligence \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"observeContext": "<string>",
"credentialIDs": [
"<string>"
],
"credentials": [
{
"credentialID": "<string>",
"transports": [
"<string>"
],
"backupEligible": true,
"aaguid": "<string>",
"registrationUserAgent": "<string>"
}
]
}
'import requests
url = "https://api.cloud.corbado.io/v1/observe/passkeyIntelligence"
payload = {
"observeContext": "<string>",
"credentialIDs": ["<string>"],
"credentials": [
{
"credentialID": "<string>",
"transports": ["<string>"],
"backupEligible": True,
"aaguid": "<string>",
"registrationUserAgent": "<string>"
}
]
}
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({
observeContext: '<string>',
credentialIDs: ['<string>'],
credentials: [
{
credentialID: '<string>',
transports: ['<string>'],
backupEligible: true,
aaguid: '<string>',
registrationUserAgent: '<string>'
}
]
})
};
fetch('https://api.cloud.corbado.io/v1/observe/passkeyIntelligence', 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.cloud.corbado.io/v1/observe/passkeyIntelligence",
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([
'observeContext' => '<string>',
'credentialIDs' => [
'<string>'
],
'credentials' => [
[
'credentialID' => '<string>',
'transports' => [
'<string>'
],
'backupEligible' => true,
'aaguid' => '<string>',
'registrationUserAgent' => '<string>'
]
]
]),
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.cloud.corbado.io/v1/observe/passkeyIntelligence"
payload := strings.NewReader("{\n \"observeContext\": \"<string>\",\n \"credentialIDs\": [\n \"<string>\"\n ],\n \"credentials\": [\n {\n \"credentialID\": \"<string>\",\n \"transports\": [\n \"<string>\"\n ],\n \"backupEligible\": true,\n \"aaguid\": \"<string>\",\n \"registrationUserAgent\": \"<string>\"\n }\n ]\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.cloud.corbado.io/v1/observe/passkeyIntelligence")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"observeContext\": \"<string>\",\n \"credentialIDs\": [\n \"<string>\"\n ],\n \"credentials\": [\n {\n \"credentialID\": \"<string>\",\n \"transports\": [\n \"<string>\"\n ],\n \"backupEligible\": true,\n \"aaguid\": \"<string>\",\n \"registrationUserAgent\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cloud.corbado.io/v1/observe/passkeyIntelligence")
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 \"observeContext\": \"<string>\",\n \"credentialIDs\": [\n \"<string>\"\n ],\n \"credentials\": [\n {\n \"credentialID\": \"<string>\",\n \"transports\": [\n \"<string>\"\n ],\n \"backupEligible\": true,\n \"aaguid\": \"<string>\",\n \"registrationUserAgent\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"verdict": "phone",
"reason": "phone-needed",
"retryPolicy": {
"maxTries": 3,
"switchAfterInstantFailure": true
},
"environment": {
"source": "session"
}
}{
"error": {
"message": "Validation failed",
"details": [
{
"field": "projectID",
"message": "required"
}
]
}
}Predict the passkey login outcome
Recommends how to route a passkey login before the browser opens the passkey prompt: start normally
(local), prepare the user to sign in with a phone (phone), try once (try-once) or use another
sign-in method (fallback). Call server to server from your identity provider or login backend
after identifying the account.
This feature is available as an add-on and requires activation for your project. To enable it, contact support@corbado.com.
Send the Observe context from the login browser unchanged, for example the value of its
cbo_observe_context cookie, and the account’s credential IDs. Registration details are optional.
At least one credential is required; each credential array accepts at most 50 entries.
When device information is unavailable, the response has verdict: local,
reason: session-not-ready and environment.source: none. In that case, or without an Observe
context, continue with your existing login behavior. Set a bounded HTTP timeout and continue with
that same behavior on errors or timeouts; do not wait for retries of this call during the login.
The verdict is a recommendation, not a guarantee of passkey availability. Apply retryPolicy to
passkey attempts, not HTTP requests. See Prevent failed passkey logins
for integration steps and the PingAM example.
Required API key permission: observe:passkeys:read.
curl --request POST \
--url https://api.cloud.corbado.io/v1/observe/passkeyIntelligence \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"observeContext": "<string>",
"credentialIDs": [
"<string>"
],
"credentials": [
{
"credentialID": "<string>",
"transports": [
"<string>"
],
"backupEligible": true,
"aaguid": "<string>",
"registrationUserAgent": "<string>"
}
]
}
'import requests
url = "https://api.cloud.corbado.io/v1/observe/passkeyIntelligence"
payload = {
"observeContext": "<string>",
"credentialIDs": ["<string>"],
"credentials": [
{
"credentialID": "<string>",
"transports": ["<string>"],
"backupEligible": True,
"aaguid": "<string>",
"registrationUserAgent": "<string>"
}
]
}
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({
observeContext: '<string>',
credentialIDs: ['<string>'],
credentials: [
{
credentialID: '<string>',
transports: ['<string>'],
backupEligible: true,
aaguid: '<string>',
registrationUserAgent: '<string>'
}
]
})
};
fetch('https://api.cloud.corbado.io/v1/observe/passkeyIntelligence', 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.cloud.corbado.io/v1/observe/passkeyIntelligence",
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([
'observeContext' => '<string>',
'credentialIDs' => [
'<string>'
],
'credentials' => [
[
'credentialID' => '<string>',
'transports' => [
'<string>'
],
'backupEligible' => true,
'aaguid' => '<string>',
'registrationUserAgent' => '<string>'
]
]
]),
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.cloud.corbado.io/v1/observe/passkeyIntelligence"
payload := strings.NewReader("{\n \"observeContext\": \"<string>\",\n \"credentialIDs\": [\n \"<string>\"\n ],\n \"credentials\": [\n {\n \"credentialID\": \"<string>\",\n \"transports\": [\n \"<string>\"\n ],\n \"backupEligible\": true,\n \"aaguid\": \"<string>\",\n \"registrationUserAgent\": \"<string>\"\n }\n ]\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.cloud.corbado.io/v1/observe/passkeyIntelligence")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"observeContext\": \"<string>\",\n \"credentialIDs\": [\n \"<string>\"\n ],\n \"credentials\": [\n {\n \"credentialID\": \"<string>\",\n \"transports\": [\n \"<string>\"\n ],\n \"backupEligible\": true,\n \"aaguid\": \"<string>\",\n \"registrationUserAgent\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cloud.corbado.io/v1/observe/passkeyIntelligence")
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 \"observeContext\": \"<string>\",\n \"credentialIDs\": [\n \"<string>\"\n ],\n \"credentials\": [\n {\n \"credentialID\": \"<string>\",\n \"transports\": [\n \"<string>\"\n ],\n \"backupEligible\": true,\n \"aaguid\": \"<string>\",\n \"registrationUserAgent\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"verdict": "phone",
"reason": "phone-needed",
"retryPolicy": {
"maxTries": 3,
"switchAfterInstantFailure": true
},
"environment": {
"source": "session"
}
}{
"error": {
"message": "Validation failed",
"details": [
{
"field": "projectID",
"message": "required"
}
]
}
}Authorizations
Use an Observe API key from the management console. The key selects the project and must grant the permission listed on the operation. Keep this key on your server.
Body
Send the user's passkeys either as plain IDs (credentialIDs) or with what the identity provider stored at
registration (credentials), or both; at least one credential is required. Every field beyond the credential
ID is optional and only fills what Observe does not know itself.
Opaque Observe context from the login browser, for example its cbo_observe_context cookie value.
Pass it unchanged; do not construct or decode it. Without it, skip this call and continue with your
existing login behavior. Maximum 1024 characters.
Base64url or base64 credential IDs of the account's passkeys. At most 50 entries, each up to 1400 characters.
The user's passkeys with the registration data the identity provider stored. Use instead of or together
with credentialIDs; for the same credential ID these details are used. At most 50 entries.
Show child attributes
Show child attributes
Response
Login recommendation, reason, passkey retry policy and device-information source.
Recommended route: local starts the passkey login normally; phone prepares the user for a phone
and QR code; try-once allows one attempt with the fallback visible; fallback starts another
sign-in method. For reason: session-not-ready, retain your existing login behavior.
local, phone, try-once, fallback Why this route is recommended. Route by verdict; use reason for wording and reporting. More
reasons may be added.
| Verdict | Reason | Meaning |
|---|---|---|
local | passkey-on-this-device | A passkey is expected on this device. |
local | phone-possible | A passkey may be here or on a phone; start normally, do not announce a phone. |
local | session-not-ready | No device information yet: continue with your existing login behavior. |
phone | phone-needed | The passkey is most likely on a phone: prepare the user for a QR code. |
try-once | uncertain | The passkey may not work here: allow one attempt with the fallback visible. |
fallback | passkey-not-on-this-device | None of the passkeys can be used on this device. |
fallback | bluetooth-off | The passkey is on a phone and this computer reports Bluetooth off; the user may turn it on. |
fallback | phone-sign-in-unavailable | The passkey is on a phone and this browser offers no sign-in with a phone. |
fallback | no-passkey-support | This device cannot use passkeys. |
fallback | in-app-browser | The login runs in a browser embedded in another app. |
fallback | failed-on-this-device | Recent passkey attempts of this account failed on this device. |
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Was this page helpful?