Skip to content

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.

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).
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.

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.

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.

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.

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.

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.

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.

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.

Both examples are the contract’s own, with the account and name changed to the shared example used across these docs.

{
"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.

{
"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 }
}