The assessment object
An assessment is the body of POST /v1/assessments, of GET /v1/assessments/{assessment_id}, of each item in a history page and of the assessment.updated webhook. The same object is served in two views: standard, which every key gets, and detailed, which adds signals and risk.details for keys with the detailed scope. Try the live shape in the interactive reference.
Top-level fields
Section titled “Top-level fields”| Field | Type | Always present | Meaning |
|---|---|---|---|
schema_version |
string | Yes | "1", the version of the body shape. |
assessment_id |
string | Yes | asm_ and 26 characters. Unique within your organisation. |
account_id |
string | Yes | The account_id you sent. |
request_id |
string | No | The request_id you sent, echoed back. |
status |
string | Yes | complete, partial or pending. See status. |
recommendation |
string | When status is complete or partial |
allow, step_up or review. Equal to risk.recommendation. Absent on pending. |
confidence |
string | Yes | low, medium, high or unavailable. unavailable only on pending. See confidence. |
risk |
object | When status is complete or partial |
The risk decision. Absent on pending. See risk. |
signals |
array | Detailed view only | One entry per source answer, at most 256. A standard answer omits it; a detailed answer always carries it, possibly empty. See signals. |
incomplete_sources |
array | Yes | The lanes that did not settle, each listed once with a reason. Empty when everything answered. See incomplete_sources. |
policy_version |
string | Yes | The version of the policy that produced the answer, for example risk-2026-10.3. Store it with your decision. |
policy_basis |
string | Yes | How the policy’s weights were set. Currently heuristic_unvalidated. |
assessment_version |
integer | Yes | 1 on the synchronous answer. Each later change of score, band, recommendation, confidence, coverage or status increments it, up to 1000. Expiry does not. |
created_at |
string | Yes | RFC 3339. When the assessment was created. The same on every revision. |
updated_at |
string | Yes | RFC 3339. When this revision was produced. |
expires_at |
string | Yes | RFC 3339. created_at plus your retention period at creation, 365 days by default. After it the assessment is gone: reads answer 404 and it leaves history. See Data retention. |
assessment_url |
string | On pending, and on partial while work continues |
/v1/assessments/{assessment_id}, the path to poll. Absent on complete. |
timing |
object | Yes | deadline_ms, the budget the server applied (50 to 500), and elapsed_ms, server time from ingress to this revision (0 to 600,000). |
status
Section titled “status”| Value | Meaning | What you get |
|---|---|---|
complete |
Final. Nothing is still running. A lane that failed or timed out stays listed in incomplete_sources and lowers confidence. |
recommendation and risk. No assessment_url. |
partial |
A recommendation exists and at least one lane is still running or did not settle. incomplete_sources has at least one entry. |
recommendation and risk. The assessment may be revised: a webhook or a poll brings a higher assessment_version. |
pending |
Nothing usable arrived before the deadline. | No recommendation and no risk. confidence is unavailable, incomplete_sources has at least one entry and assessment_url is present. Poll it or wait for the webhook. |
Only the terminal webhook event carries a fourth value, expired, with no assessment body; see Webhooks.
recommendation
Section titled “recommendation”| Value | Meaning |
|---|---|
allow |
The evidence is consistent with a real customer. |
step_up |
Ask for more proof before the account’s first request, such as email or SMS verification. |
review |
Hold the signup for your team. Every assessment whose confidence is unavailable recommends review. |
Your rules map each value to what happens in your platform; see Turn an assessment into a decision.
confidence
Section titled “confidence”The top-level confidence says how much of the planned evidence answered.
| Value | Meaning |
|---|---|
high |
Most of the planned sources gave a definite answer. |
medium |
Some sources failed, timed out or are still running. |
low |
Most of the planned evidence did not answer. |
unavailable |
Nothing answered in time. Only on pending. |
risk.confidence carries the same scale without unavailable, because risk is absent whenever confidence is unavailable.
| Field | Type | Always present | Meaning |
|---|---|---|---|
score |
integer | Yes | 0 (safe) to 100 (risky). The sum of the weights of the reasons that fired, clamped to 0 to 100. |
band |
string | Yes | low for 0 to 29, medium for 30 to 59, high for 60 to 84, critical for 85 to 100. |
recommendation |
string | Yes | allow, step_up or review. The same value as the top-level recommendation. |
confidence |
string | Yes | low, medium or high. |
policy_version |
string | Yes | The weights that produced the score, for example risk-2026-10.3. A threshold override agreed for your organisation adds a suffix such as +t3. |
policy_basis |
string | Yes | The same value as the top-level policy_basis. |
reasons |
array | Yes | Every reason that fired, highest weight first, at most 32. Each has a code, a weight from -100 to 100 and, when it concerns one input, an identifier of email, username, phone, name, ip or context. See Reason codes. |
summary |
object | No | Typed facts per identifier. See summary. |
coverage |
object | Yes | Source counts by state: answered gave a definite answer, failed could not, pending are still being asked. |
details |
object | Detailed view only | Merged observations. See details. |
summary
Section titled “summary”Every fact in summary is optional. An absent fact was not answered yet. false and 0 are definite answers.
| Field | Type | Meaning |
|---|---|---|
email.disposable |
boolean | The email’s domain is a disposable address service. |
email.accounts_found |
integer | Accounts found for the email. |
email.breach |
object | Breach facts for the email. See the breach summary below. |
username.accounts_found |
integer | Accounts found for the username. |
username.breach |
object | Breach facts for the username. |
phone.line_type |
string | mobile, voip, landline, toll_free or other. |
phone.country |
string | ISO 3166-1 alpha-2 country of the number. |
phone.report_count |
integer | The highest report count any source gave for the number. |
phone.carrier |
string | Carrier network metadata, 1 to 128 characters. |
ip.country |
string | ISO 3166-1 alpha-2 country of the address. |
ip.type |
string | residential, hosting, vpn, proxy, tor, mobile or unknown. |
ip.seen_in_tenant |
integer | Assessments in your organisation with the same IP in the last 24 hours, this one included. |
consistency.name |
string | The submitted name against names in breach records and caller-ID records: match, partial, mismatch or unknown. |
consistency.phone |
string | The submitted phone against numbers found next to the email in breach records. Same values. |
consistency.ip |
string | The submitted IP against addresses found next to the email or username in breach records. Same values. |
consistency.country |
string | IP country, phone country, breach countries and policy_context.region together: consistent, mismatch or unknown. |
For a consistency value, unknown means the comparison ran without a usable result, for example a truncated or too-common breach result. Absent means the comparison was not made.
The breach summary under email.breach and username.breach:
| Field | Type | Meaning |
|---|---|---|
found |
boolean | Required. false means the value is in no breach table. |
breach_count |
integer | Distinct breach tables the value appears in. |
first_year |
integer | Year of the oldest dated table, 1970 to 2100. |
last_year |
integer | Year of the newest dated table. |
exposed |
array of string | The kinds of data the matching tables held next to the value: email, username, name, password, password_hash, phone, address, dob, ip, payment or security_qa. |
in_credential_lists |
boolean | The value is in a credential list or a stealer log. |
recent_password_exposure |
boolean | A plaintext password sat next to the value in a table dated two years ago or less, or the value is in a stealer log. |
truncated |
boolean | The value matched more tables or records than the lookup keeps. Absence-based claims are then withheld. A truncated result is always found. |
details
Section titled “details”Only a detailed answer carries risk.details: the breach tables that matched, and the names seen for the phone number and in breach records.
| Field | Type | Meaning |
|---|---|---|
phone_names |
array | Names seen for the phone number, at most 32, each with a value (1 to 128 characters) and seen_by, the number of sources that reported it. |
breaches |
array | The matching breach tables, newest first, at most 50. See the breach object below. |
breach_names |
array | Names found next to the identifiers in breach records, merged, at most 32, each with a value and seen_in, the number of distinct tables it was seen in. |
A breach object:
| Field | Type | Meaning |
|---|---|---|
name |
string | Required. The display name of the breached site or list, 1 to 128 characters. |
domain |
string | The breached site’s domain, lower case. Absent for a table without one, such as a credential list. |
category |
string | The table’s category, lower case, such as social, gaming or combolist. |
kind |
string | Required. site_breach (a site’s own users), credential_list (a compilation), stealer_log (malware victims), scrape, hacking_forum, abuse_service (customers of DDoS-for-hire, account generators, card shops and similar) or darkweb_site. |
year |
integer | The table’s date year. |
month |
integer | 1 to 12. Absent when only the year is known. |
matched_on |
string | Required. The submitted identifier the table matched: email, username or ip. |
exposed |
array of string | The kinds of data this table held, same values as the breach summary. |
signals
Section titled “signals”Only a detailed answer carries signals. Each entry is one source’s answer about one identifier, with at most one entry per type, source_id and identifier.
| Field | Type | Always present | Meaning |
|---|---|---|---|
type |
string | Yes | The signal kind, for example account_presence or breach_exposure. Ignore kinds you do not know. |
identifier |
string | Yes | email, username, phone or ip. |
outcome |
string | Yes | found, not_found, inconclusive, blocked or unavailable. A source that failed reports blocked, unavailable or inconclusive with a reason, so not_found is always a real answer. |
source_id |
string | Yes | The id of the source, stable for your organisation. |
reason |
string | On inconclusive, blocked and unavailable |
timed_out, rate_limited, blocked, source_error, unavailable, deferred or insufficient_evidence. |
evidence_ref |
string | On found |
evr_ and 26 characters: a reference to the evidence NoResponse holds, for support conversations. |
observed_at |
string | On found |
RFC 3339. When the evidence was first observed. |
last_checked_at |
string | On found and not_found |
RFC 3339. When the source was last queried for this identifier. |
freshness |
string | Yes | fresh or stale on found; unknown when no evidence was obtained. |
enrichment |
object | On a found phone signal only |
Facts about the number under fields: strings such as carrier, line_type, country and national_format, arrays such as alternate_names, and counts such as report_count and spam_report_count. |
incomplete_sources
Section titled “incomplete_sources”Each entry names a lane that did not settle and why. A lane is listed once.
| Field | Values |
|---|---|
lane |
accounts (account presence for email and username), phone, breach or ip. |
reason |
timed_out (the lane missed its deadline; a continuation may still settle it), rate_limited, blocked, source_error, unavailable or deferred (the lane runs after the synchronous answer, which is every phone lookup on the first answer). |
A complete assessment lists only lanes that failed or timed out. A partial or pending assessment lists at least one lane.
Timestamps and versions
Section titled “Timestamps and versions”created_at is fixed for the life of the assessment. updated_at and assessment_version move together: a revision that changes none of score, band, recommendation, confidence, coverage or status is not served. When you hold several copies, keep the one with the highest assessment_version and discard the rest. expires_at is fixed at creation and is the latest moment the assessment is served. If you shorten your retention period, an assessment stops being served earlier than its expires_at, so treat a 404 as expiry whatever the field says. See Data retention.
Examples
Section titled “Examples”Both examples are the contract’s own, with the account and name changed to the shared example used across these docs.
Standard view
Section titled “Standard view”{ "schema_version": "1", "assessment_id": "asm_01JB3C4D5E6F7G8H9JKMNPQRST", "account_id": "usr_8f2k1", "request_id": "req_01", "status": "complete", "recommendation": "allow", "confidence": "high", "risk": { "score": 0, "band": "low", "recommendation": "allow", "confidence": "high", "policy_version": "risk-2026-10.3", "policy_basis": "heuristic_unvalidated", "reasons": [ { "code": "voip_number", "weight": 25, "identifier": "phone" }, { "code": "breach_cooccurrence", "weight": -10, "identifier": "phone" }, { "code": "breach_name_match", "weight": -15, "identifier": "name" }, { "code": "established_accounts", "weight": -15, "identifier": "email" }, { "code": "aged_identity", "weight": -25, "identifier": "email" } ], "summary": { "email": { "disposable": false, "accounts_found": 6, "breach": { "found": true, "breach_count": 7, "first_year": 2013, "last_year": 2021, "exposed": ["email", "name", "password", "phone", "ip"], "in_credential_lists": false, "recent_password_exposure": false, "truncated": false } }, "username": { "accounts_found": 3, "breach": { "found": false, "truncated": false } }, "phone": { "line_type": "voip", "country": "US", "report_count": 0 }, "ip": { "country": "US", "type": "residential", "seen_in_tenant": 1 }, "consistency": { "name": "match", "phone": "match", "ip": "unknown", "country": "consistent" } }, "coverage": { "answered": 15, "failed": 0, "pending": 0 } }, "incomplete_sources": [], "policy_version": "risk-2026-10.3", "policy_basis": "heuristic_unvalidated", "assessment_version": 3, "created_at": "2026-10-18T11:00:00.000Z", "updated_at": "2026-10-18T11:00:04.000Z", "expires_at": "2027-10-18T11:00:00.000Z", "timing": { "deadline_ms": 500, "elapsed_ms": 4000 }}The weights sum to -40, so the score clamps to 0. elapsed_ms is 4000 because this is the third revision: the first answer arrived inside the 500 ms budget and two continuations settled afterwards.
Detailed view
Section titled “Detailed view”{ "schema_version": "1", "assessment_id": "asm_01JB3C4D5E6F7G8H9JKMNPQRST", "account_id": "usr_8f2k1", "request_id": "req_01", "status": "complete", "recommendation": "allow", "confidence": "high", "risk": { "score": 10, "band": "low", "recommendation": "allow", "confidence": "high", "policy_version": "risk-2026-10.3", "policy_basis": "heuristic_unvalidated", "reasons": [ { "code": "voip_number", "weight": 25, "identifier": "phone" }, { "code": "breach_name_match", "weight": -15, "identifier": "name" } ], "summary": { "email": { "disposable": false, "accounts_found": 1, "breach": { "found": true, "breach_count": 2, "first_year": 2016, "last_year": 2019, "exposed": ["email", "password", "password_hash"], "in_credential_lists": true, "truncated": false } }, "phone": { "line_type": "voip", "country": "US" }, "consistency": { "name": "match" } }, "coverage": { "answered": 4, "failed": 0, "pending": 0 }, "details": { "phone_names": [{ "value": "Jordan Lee", "seen_by": 3 }], "breaches": [ { "name": "Collection #1", "category": "combolist", "kind": "credential_list", "year": 2019, "matched_on": "email", "exposed": ["email", "password"] }, { "name": "LinkedIn", "domain": "linkedin.com", "category": "social", "kind": "site_breach", "year": 2016, "month": 5, "matched_on": "email", "exposed": ["email", "password_hash"] } ], "breach_names": [{ "value": "Jordan Lee", "seen_in": 2 }] } }, "signals": [ { "type": "account_presence", "identifier": "email", "outcome": "found", "source_id": "src_3f9a1c", "evidence_ref": "evr_01JB3C4D5E6F7G8H9JKMNPQRSV", "observed_at": "2026-10-18T11:00:00.180Z", "last_checked_at": "2026-10-18T11:00:00.180Z", "freshness": "fresh" }, { "type": "breach_exposure", "identifier": "email", "outcome": "found", "source_id": "src_8d27e4", "evidence_ref": "evr_01JB3C4D5E6F7G8H9JKMNPQRSW", "observed_at": "2026-10-18T11:00:01.900Z", "last_checked_at": "2026-10-18T11:00:01.900Z", "freshness": "fresh" }, { "type": "account_presence", "identifier": "phone", "outcome": "found", "source_id": "src_51b0d7", "evidence_ref": "evr_01JB3C4D5E6F7G8H9JKMNPQRSX", "observed_at": "2026-10-18T11:00:03.700Z", "last_checked_at": "2026-10-18T11:00:03.700Z", "freshness": "fresh" } ], "incomplete_sources": [], "policy_version": "risk-2026-10.3", "policy_basis": "heuristic_unvalidated", "assessment_version": 2, "created_at": "2026-10-18T11:00:00.000Z", "updated_at": "2026-10-18T11:00:04.000Z", "expires_at": "2027-10-18T11:00:00.000Z", "timing": { "deadline_ms": 500, "elapsed_ms": 4000 }}