curl --request POST \
--url https://api.cloud.corbado.io/v1/observe/userDiagnosis \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"userID": "tus-123456",
"onlyFailed": true
}
'import requests
url = "https://api.cloud.corbado.io/v1/observe/userDiagnosis"
payload = {
"userID": "tus-123456",
"onlyFailed": True
}
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({userID: 'tus-123456', onlyFailed: true})
};
fetch('https://api.cloud.corbado.io/v1/observe/userDiagnosis', 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/userDiagnosis",
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([
'userID' => 'tus-123456',
'onlyFailed' => true
]),
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/userDiagnosis"
payload := strings.NewReader("{\n \"userID\": \"tus-123456\",\n \"onlyFailed\": true\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/userDiagnosis")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"userID\": \"tus-123456\",\n \"onlyFailed\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cloud.corbado.io/v1/observe/userDiagnosis")
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 \"userID\": \"tus-123456\",\n \"onlyFailed\": true\n}"
response = http.request(request)
puts response.read_body{
"diagnosis": {
"status": "worked",
"focus": {
"id": "123456",
"operation": "passkey-login",
"start": "2026-10-01T10:00:00Z",
"completed": true
},
"answer": {
"whatHappened": "The passkey sign-in completed successfully.",
"nextStep": "No action is needed.",
"bucket": "reassure"
},
"outcome": {
"status": "finished",
"where": "same-flow"
}
}
}{
"error": {
"message": "Validation failed",
"details": [
{
"field": "projectID",
"message": "required"
}
]
}
}Explain what went wrong for a user
Explains a user’s passkey sign-in or passkey creation attempt for support. Returns what happened, the next step, the supporting signals and whether the user subsequently completed the journey.
This feature is available as an add-on and requires activation for your project. To enable it, contact support@corbado.com.
Send the Observe userID. By default, the latest supported attempt is selected, excluding silent
background checks. Set onlyFailed to true to select the latest unsuccessful attempt, or send
actionID to explain a specific attempt. The recovery window defaults to 60 minutes and can be
between 1 and 10080 minutes.
Check diagnosis.status before displaying an answer: explained and worked include an answer;
no-case-matched, no-supported-action and no-actions indicate that no explanation is available.
An unknown user returns HTTP 404. See Support diagnosis
for the support workflow.
Required API key permission: observe:user:read.
curl --request POST \
--url https://api.cloud.corbado.io/v1/observe/userDiagnosis \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"userID": "tus-123456",
"onlyFailed": true
}
'import requests
url = "https://api.cloud.corbado.io/v1/observe/userDiagnosis"
payload = {
"userID": "tus-123456",
"onlyFailed": True
}
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({userID: 'tus-123456', onlyFailed: true})
};
fetch('https://api.cloud.corbado.io/v1/observe/userDiagnosis', 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/userDiagnosis",
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([
'userID' => 'tus-123456',
'onlyFailed' => true
]),
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/userDiagnosis"
payload := strings.NewReader("{\n \"userID\": \"tus-123456\",\n \"onlyFailed\": true\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/userDiagnosis")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"userID\": \"tus-123456\",\n \"onlyFailed\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cloud.corbado.io/v1/observe/userDiagnosis")
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 \"userID\": \"tus-123456\",\n \"onlyFailed\": true\n}"
response = http.request(request)
puts response.read_body{
"diagnosis": {
"status": "worked",
"focus": {
"id": "123456",
"operation": "passkey-login",
"start": "2026-10-01T10:00:00Z",
"completed": true
},
"answer": {
"whatHappened": "The passkey sign-in completed successfully.",
"nextStep": "No action is needed.",
"bucket": "reassure"
},
"outcome": {
"status": "finished",
"where": "same-flow"
}
}
}{
"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
Observe user ID (format tus-<number>), returned by user search.
Explain this attempt (format sfl-<number>) instead of selecting the latest supported attempt.
Takes precedence over onlyFailed. The attempt must belong to the requested user's activity.
Select the latest supported attempt that did not complete. Ignored when actionID is supplied.
Time window after the selected attempt's start in which a later successful journey of the same type counts as recovery. Defaults to 60 minutes; accepts 1 to 10080. The attempt's own journey always counts.
1 <= x <= 10080Response
Explanation and support context for the selected attempt.
Support explanation and the context needed to investigate the selected attempt.
Show child attributes
Show child attributes
{
"status": "worked",
"focus": {
"id": "123456",
"operation": "passkey-login",
"start": "2026-10-01T10:00:00Z",
"completed": true
},
"answer": {
"whatHappened": "The passkey sign-in completed successfully.",
"nextStep": "No action is needed.",
"bucket": "reassure"
},
"outcome": {
"status": "finished",
"where": "same-flow"
}
}
Was this page helpful?