Skip to content

Create an assessment

POST /v1/assessments assesses one signup, login, recovery or profile change and answers within 500 ms. The answer is an assessment: a 0 to 100 score, a band, a recommendation of allow, step_up or review, and the reasons behind it.

The call needs the assess scope and an Idempotency-Key header. Try it live in the interactive reference.

import { NoResponse } from "@noresponse/node";
const nr = new NoResponse(); // reads NORESPONSE_API_KEY
const assessment = await nr.assessments.create(
{
accountId: "usr_8f2k1",
identifiers: { email: "[email protected]", name: "Jordan Lee", ip: "81.2.69.160" },
policyContext: { flow: "signup", region: "GB" },
},
{ idempotencyKey: "signup-usr_8f2k1-01JB3C4D" },
);
console.log(assessment.status, assessment.risk?.score, assessment.risk?.recommendation);

The SDKs take the same nested body in each language’s own casing: identifiers and policy_context become identifiers and policyContext in Node, identifiers and policy_context in Python, and Identifiers and PolicyContext in Go. The sections below use the wire names.

Header Required Value
Authorization Yes Bearer and your API key. The key needs the assess scope, plus detailed when response_mode is detailed.
Content-Type Yes application/json. Anything else answers 415.
Idempotency-Key Yes 16 to 128 characters of A-Z, a-z, 0-9, ., _, : and -. A UUID or ULID fits. The key is bound to the body for 24 hours from first use. The same key with the same body replays the latest state of the same assessment; the same key with a different body answers 409.

The body is a JSON object of at most 32,768 bytes. Unknown members are refused with 400, including any field that names your organisation: the API key identifies it.

Field Type Required Limits Example
account_id string Yes 1 to 128 characters: a letter or digit, then letters, digits, ., _, :, | and -. Your own opaque id for the account: your internal id, or a hex digest of it. A JWT is refused. The vertical bar is accepted, so an Auth0 id such as auth0|64abc passes unchanged. It travels in the body, not the URL. usr_8f2k1
identifiers object Yes At least one of email, username or phone. name and ip sharpen the match, so a body with only those two is refused.
identifiers.email string One of three 3 to 254 characters: a local part of 1 to 64 characters without whitespace, angle brackets or invisible format characters, then @ and a domain with a letter top-level label. Send an internationalised domain in its punycode form. Nothing is trimmed. The local part keeps its case; only the domain is lower-cased. No alias rewriting: +tag and dots are kept as sent. [email protected]
identifiers.username string One of three 1 to 64 characters with no whitespace, control or invisible format characters. Echoed as sent; compared case-insensitively, so JordanLee and jordanlee are one username. A value shaped like a password hash, crypt string or JWT is refused. jordan_lee
identifiers.phone string One of three Strict E.164: +, then 8 to 15 digits, the first not 0, with no separators. Phone sources run after the synchronous answer, so a phone appears in incomplete_sources with reason deferred on the first answer, and a phone-only request answers 202 pending. +447700900123
identifiers.name string No 1 to 128 characters as the person typed it, in one string, with no control or invisible format characters. Used only to match names in the evidence. Not a hash, crypt string or JWT. Jordan Lee
identifiers.ip string No The signup’s client address: an IPv4 dotted quad without leading zeros, or an IPv6 address in its canonical lower-case compressed form. No zone, port or prefix. Send an IPv4 client as a dotted quad, not IPv4-mapped. Private, loopback, link-local, multicast, documentation and other reserved addresses answer 400. 81.2.69.160
policy_context object No At least one member when present.
policy_context.flow string No signup, login, recovery or profile_change. Absent means signup. signup
policy_context.region string No ISO 3166-1 alpha-2, upper case, as you determine it. Used in the country consistency check. GB
request_id string No 1 to 128 characters: a letter or digit, then letters, digits, ., _, : and -. Your correlation id, echoed back in the assessment. It is not an idempotency key. A UUID is accepted; a bare 40, 64 or 128 character hex digest or a JWT is refused. req_01
deadline_ms integer No 50 to 500. The end-to-end budget in milliseconds, measured from the first request byte to the last response byte. Absent means 500. A value above 500 answers 400 and is never clamped. 300
response_mode string No standard or detailed. Default standard. detailed adds signals and risk.details and needs the detailed scope on the key. There is no raw mode. standard
client_context object No Hints observed at your edge. At least one member when present. A client can forge them, so they carry low weights and corroborate the identifier evidence.
auth_context object No Facts your backend verified from your auth provider’s session. See the auth_context table below.
Field Type Limits Example
timezone string At most 64 characters. An IANA zone name such as Europe/London, or UTC. Fixed numeric offsets are refused. Europe/London
locale string At most 35 characters, BCP 47. A presentation hint, not a nationality. en-GB
platform string web, ios, android or other. web
app_version string At most 32 characters: a letter or digit, then letters, digits, ., _, + and -. No build metadata that holds secrets. 4.12.0
user_agent string At most 512 characters, as captured at your edge. Parsed for automation signs. Mozilla/5.0 ...
device_id string At most 128 characters: a letter or digit, then letters, digits, ., _, : and -. An opaque id you generate and scope to your organisation. Rotate it when the account or device changes. dev_3k9x
session_id string Same shape as device_id. An opaque signup session id, used for velocity and replay checks within your organisation. ses_71qa
form_duration_ms integer 0 to 86,400,000. Time from form start to submit. Under three seconds is an automation hint. 18400
client_observed_at string RFC 3339 timestamp from the client, at most 64 characters. A large skew from server time is a low-confidence feature. 2026-10-14T09:00:00Z

Send auth_context only from your backend, after it has verified the session token’s signature, issuer, audience and expiry. Raw tokens and your auth provider’s secrets are refused. provider, issuer, subject and verified are required when the object is present. The SDK auth adapters build it from verified claims.

Field Type Required Limits
provider string Yes 2 to 32 lower-case characters: a letter or digit, then letters, digits, ., _ and -. The slug of your auth provider. workos, clerk and auth0 have first-class adapters; anything else uses your own OIDC slug. The slug is metadata and does not change risk by itself.
issuer string Yes At most 256 characters, an https:// URL. The verified token issuer.
subject string Yes At most 256 characters of letters, digits and . _ : @ / | + = ~ -. The verified sub, opaque to NoResponse.
verified boolean Yes Whether your backend completed signature, issuer, audience and expiry validation. false contexts are accepted only as weak hints.
audience string No At most 256 characters of letters, digits and . _ : / -. The verified audience your API uses.
audiences array of string No At most 8 values of the same shape as audience. All verified audiences when the token carries more than one.
session_id string No At most 256 characters, same shape as subject. The verified session id, sid or equivalent.
organization_id string No Same shape. The active organisation or workspace id, when your auth provider issued one.
tenant_id string No Same shape. Your auth provider’s tenant id, when distinct from organization_id.
token_id string No Same shape. The verified jti, used for replay and rotation correlation.
issued_at string No RFC 3339, at most 64 characters. The token’s iat.
expires_at string No RFC 3339. The token’s exp.
session_created_at string No RFC 3339. When the session was created, from your backend’s session record. Separate from token issuance and auth_time.
auth_time string No RFC 3339. When the person last authenticated, when present.
account_created_at string No RFC 3339. When the account was created, from your verified record.
email_verified boolean No Whether your auth provider asserts the email was verified.
phone_verified boolean No Whether your auth provider asserts the phone was verified.
assurance_level string No unknown, low, medium or high, when you have mapped one.
impersonated boolean No Whether the session acts on behalf of another subject.
transport string No backend (default) or browser. Browser values are treated as weak hints.
mfa object No At least one of present (boolean), methods (at most 8 strings of at most 32 characters) and age_seconds (0 to 315,360,000).
roles array of string No At most 32 values of at most 64 characters. Roles do not change risk by themselves.
scopes array of string No At most 32 values of at most 64 characters. Scopes do not change risk by themselves.

The API answers within deadline_ms with one of two success statuses. Sources that miss the deadline keep running and the assessment is revised later; see Get and query assessments and Webhooks.

Status Meaning Body
200 OK A complete or partial assessment with a recommendation. The assessment object.
202 Accepted A pending assessment: nothing usable arrived before the deadline, so there is no recommendation yet. Poll Location no faster than Retry-After says, or wait for the assessment.updated webhook. The assessment object with status: "pending", confidence: "unavailable" and no risk.

Apply your own fallback for pending, for a 429 or 503 and for a transport timeout. None of them is an allow.

Header When Value
X-Request-ID Every response The request’s correlation id: 32 lower-case hexadecimal characters. Quote it when you contact NoResponse. The SDKs expose it as meta.requestId in TypeScript, meta.request_id in Python and the X-Request-ID header on res.Headers in Go.
Cache-Control Every response no-store.
Location 202 The polling path of the assessment, /v1/assessments/{assessment_id}.
Retry-After 202, 429 and 503 Whole seconds to wait before polling or retrying, from 1 to 3600.
Idempotent-Replayed 200 and 202 when the answer replays an earlier request true. The body is the latest state of the assessment that earlier request created, and no new work was started. Absent otherwise.
WWW-Authenticate 401 Bearer realm="noresponse-assessments".
{
"schema_version": "1",
"assessment_id": "asm_01JB3C4D5E6F7G8H9JKMNPQRST",
"account_id": "usr_8f2k1",
"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": "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 } },
"ip": { "country": "GB", "type": "residential", "seen_in_tenant": 1 },
"consistency": { "name": "match", "country": "consistent" }
},
"coverage": { "answered": 15, "failed": 0, "pending": 0 }
},
"incomplete_sources": [],
"policy_version": "risk-2026-10.3",
"policy_basis": "heuristic_unvalidated",
"assessment_version": 1,
"created_at": "2026-10-18T11:00:00.000Z",
"updated_at": "2026-10-18T11:00:00.412Z",
"expires_at": "2027-10-18T11:00:00.000Z",
"timing": { "deadline_ms": 500, "elapsed_ms": 412 }
}

Every field is described on The assessment object.

HTTP/1.1 202 Accepted
Content-Type: application/json
Location: /v1/assessments/asm_01JA2Z8K6Q4V7W9XBCDEFGHJKM
Retry-After: 1
X-Request-ID: 0123456789abcdef0123456789abcdef
Cache-Control: no-store
{
"schema_version": "1",
"assessment_id": "asm_01JA2Z8K6Q4V7W9XBCDEFGHJKM",
"account_id": "usr_8f2k1",
"status": "pending",
"confidence": "unavailable",
"incomplete_sources": [
{ "lane": "accounts", "reason": "timed_out" },
{ "lane": "breach", "reason": "timed_out" }
],
"policy_version": "heuristic-2026-10.1",
"policy_basis": "heuristic_unvalidated",
"assessment_version": 1,
"created_at": "2026-10-14T09:00:00.000Z",
"updated_at": "2026-10-14T09:00:00.412Z",
"expires_at": "2027-10-14T09:00:00.000Z",
"assessment_url": "/v1/assessments/asm_01JA2Z8K6Q4V7W9XBCDEFGHJKM",
"timing": { "deadline_ms": 300, "elapsed_ms": 301 }
}

The SDKs return the same assessment model for 200 and 202. Read status to tell them apart, or call the wait helper (nr.assessments.wait, client.assessments.wait, client.Assessments.Wait) to poll until the assessment is no longer pending.

Status Code When
400 invalid_request The body breaks a rule above. invalid_params names the fields.
400 invalid_idempotency_key The header is missing or malformed.
401 unauthorized No key, or a malformed, unknown, revoked or expired key.
403 tenant_forbidden The key lacks assess, or detailed when asked for.
409 idempotency_key_reused The key was used with a different body within 24 hours.
413 payload_too_large The body is over 32,768 bytes.
415 unsupported_media_type Content-Type is not application/json.
429 rate_limited Your rate or concurrency limit is used up. Retry after Retry-After.
503 service_unavailable The assessment could not be started. Retry after Retry-After.

Every error is a problem document; see Errors and limits.