> ## Documentation Index
> Fetch the complete documentation index at: https://docs.noresponse.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Create an assessment

> Assesses one signup, login or purchase, named by `flow`. `depth` sets how long the answer waits: `quick` waits only for the sources that answer within 500 ms, `full` waits up to 3,000 ms. It defaults to `full` for `signup` and `purchase` and `quick` for `login`, and `deadline_ms` can lower either.

The answer leads with `account`, the account's one current risk, and acts on `account.risk.decision`. The assessment's own facts are under `detail`.

- **200** carries a `complete` or `partial` assessment with `account`.
- **202** carries a `pending` assessment, plus `Location` and `Retry-After`. `account` is present only when an earlier assessment of the account was decided. Poll the `Location` or wait for the `assessment.updated` webhook.

Apply your own fallback when `account` is absent, and for 429, 503 or a timeout. Never treat them as `allow`.

`Idempotency-Key` is required. A retry with the same key and body replays the latest state of the same assessment with `Idempotent-Replayed: true` and starts no new work. The same key with a different body answers 409 `idempotency_key_reused`. Keys are kept for 24 hours.

Requires the `assess` scope. `response_mode: "detailed"` also needs the `detailed` scope.



## OpenAPI

````yaml /openapi.json post /v1/assessments
openapi: 3.1.0
info:
  title: NoResponse API
  version: '1'
  description: >-
    The NoResponse API assesses a signup, login or purchase and answers with the
    account's one current risk: a 0 to 100 score, a decision for the flow
    (`allow`, `step_up`, `review` or `block`), the reasons behind it and the
    decision for each flow. The assessment's own facts, such as its status,
    confidence and incomplete sources, sit under `detail`.


    Authenticate every request with your API key as `Authorization: Bearer
    nrk_...`. Call the API only from your backend.


    Guides, SDKs and webhook verification are at
    [docs.noresponse.org](https://docs.noresponse.org/). Every error is an RFC
    9457 problem document; see
    [Errors](https://docs.noresponse.org/reference/errors).
servers:
  - url: https://api.noresponse.org
    description: Production
security:
  - tenantBearer: []
tags:
  - name: assess
    x-displayName: Assessments
    x-group: Assessments
    description: Create, read and list assessments, and report outcomes.
  - name: accounts
    x-displayName: Accounts
    x-group: Accounts
    description: >-
      Each of your accounts holds one current risk, and its assessments are its
      history.
  - name: usage
    x-displayName: Key usage
    x-group: Key usage
    description: >-
      Report usage of the API keys your signup gate issued, so a shared or
      stolen key raises its account's risk.
  - name: keys
    x-displayName: API keys
    x-group: API keys
    description: Rotate your own API key.
  - name: integrations
    x-displayName: Integrations
    x-group: Integrations
    description: >-
      Hosted endpoints that answer your WorkOS or Auth0 sign-up and sign-in
      hooks.
  - name: webhooks
    x-displayName: Webhooks
    x-group: Webhooks
    description: Your webhook endpoints, and the signed events NoResponse sends to them.
paths:
  /v1/assessments:
    post:
      tags:
        - assess
      summary: Create an assessment
      description: >-
        Assesses one signup, login or purchase, named by `flow`. `depth` sets
        how long the answer waits: `quick` waits only for the sources that
        answer within 500 ms, `full` waits up to 3,000 ms. It defaults to `full`
        for `signup` and `purchase` and `quick` for `login`, and `deadline_ms`
        can lower either.


        The answer leads with `account`, the account's one current risk, and
        acts on `account.risk.decision`. The assessment's own facts are under
        `detail`.


        - **200** carries a `complete` or `partial` assessment with `account`.

        - **202** carries a `pending` assessment, plus `Location` and
        `Retry-After`. `account` is present only when an earlier assessment of
        the account was decided. Poll the `Location` or wait for the
        `assessment.updated` webhook.


        Apply your own fallback when `account` is absent, and for 429, 503 or a
        timeout. Never treat them as `allow`.


        `Idempotency-Key` is required. A retry with the same key and body
        replays the latest state of the same assessment with
        `Idempotent-Replayed: true` and starts no new work. The same key with a
        different body answers 409 `idempotency_key_reused`. Keys are kept for
        24 hours.


        Requires the `assess` scope. `response_mode: "detailed"` also needs the
        `detailed` scope.
      operationId: createAssessment
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssessmentRequest'
            examples:
              minimal:
                value:
                  account_id: acct_7f3a9c
                  flow: account_creation
                  identifiers:
                    email: new.user@example.com
              full:
                value:
                  account_id: acct_7f3a9c
                  flow: account_creation
                  identifiers:
                    email: new.user@example.com
                    username: new_user_42
                    phone: '+447700900123'
                  policy_context:
                    region: GB
                  request_id: req-2026-10-14-0001
                  deadline_ms: 300
              name_and_ip:
                value:
                  account_id: acct_8412
                  flow: account_creation
                  identifiers:
                    email: jane.doe@example.com
                    username: janedoe
                    phone: '+14155550123'
                    name: Jane A. Doe
                    ip: 73.162.18.45
                  policy_context:
                    region: US
                  request_id: req_01
                  deadline_ms: 500
                  response_mode: standard
      responses:
        '200':
          $ref: '#/components/responses/AssessmentSettled'
        '202':
          $ref: '#/components/responses/AssessmentPending'
        '400':
          $ref: '#/components/responses/InvalidRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/WorkspaceForbidden'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
      security:
        - tenantBearer: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |
            curl --request POST \
              --url 'https://api.noresponse.org/v1/assessments' \
              --header "Authorization: Bearer $NORESPONSE_API_KEY" \
              --header 'Content-Type: application/json' \
              --header 'Idempotency-Key: account-acct_7f3a9c' \
              --data '{
              "account_id": "acct_7f3a9c",
              "flow": "account_creation",
              "identifiers": {
                "email": "new.user@example.com"
              }
            }'
        - lang: typescript
          label: TypeScript
          source: |
            import { NoResponse } from "@noresponse/node";

            const noResponse = new NoResponse({
              apiKey: process.env["NORESPONSE_API_KEY"] ?? "",
            });

            async function run() {
              const result = await noResponse.assessments.create({
                idempotencyKey: "account-acct_7f3a9c",
                body: {
                  accountId: "acct_7f3a9c",
                  flow: "account_creation",
                  identifiers: {
                    email: "new.user@example.com",
                  },
                },
              });

              console.log(result);
            }

            run();
        - lang: python
          label: Python
          source: |
            from noresponse import NoResponse
            import os


            with NoResponse(
                api_key=os.getenv("NORESPONSE_API_KEY", ""),
            ) as no_response:

                res = no_response.assessments.create(idempotency_key="account-acct_7f3a9c", account_id="acct_7f3a9c", flow="account_creation", identifiers={
                    "email": "new.user@example.com",
                }, policy_context={
                    "region": "AU",
                }, request_id="req-2026-10-14-0001", client_context={
                    "timezone": "Australia/Sydney",
                    "locale": "en-AU",
                    "app_version": "4.12.0",
                    "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0 Safari/537.36",
                    "device_id": "dev_5c1e8a",
                    "session_id": "sess_9b2d41",
                    "client_observed_at": "2026-10-14T08:59:58.000Z",
                }, auth_context={
                    "provider": "clerk",
                    "issuer": "https://example-app.clerk.accounts.dev",
                    "audience": "https://app.example.com",
                    "audiences": [
                        "https://app.example.com",
                    ],
                    "subject": "user_2EXAMPLE",
                    "session_id": "sess_2EXAMPLE",
                    "organization_id": "org_2EXAMPLE",
                    "tenant_id": "tenant_2EXAMPLE",
                    "token_id": "2EXAMPLEJTI",
                    "issued_at": "2026-10-14T08:59:40.000Z",
                    "expires_at": "2026-10-14T09:00:40.000Z",
                    "session_created_at": "2026-10-14T08:57:00.000Z",
                    "auth_time": "2026-10-14T08:55:00.000Z",
                    "account_created_at": "2026-10-14T08:56:00.000Z",
                    "verified": False,
                    "mfa": {
                        "methods": [
                            "totp",
                        ],
                    },
                    "roles": [
                        "admin",
                    ],
                    "scopes": [
                        "org:sys_profile:manage",
                    ],
                }, issued_credential={
                    "sha256": "2bdbf5c72f2a36df0ef40fb92203612274d4546f56dba946070a6a79c2401a1b",
                })

                # Handle response
                print(res)
        - lang: go
          label: Go
          source: "package main\n\nimport(\n\t\"context\"\n\t\"os\"\n\tnoresponse \"github.com/noresponsehq/go/v5\"\n\t\"github.com/noresponsehq/go/v5/models/components\"\n\t\"log\"\n)\n\nfunc main() {\n    ctx := context.Background()\n\n    s := noresponse.New(\n        noresponse.WithSecurity(os.Getenv(\"NORESPONSE_API_KEY\")),\n    )\n\n    res, err := s.Assessments.Create(ctx, \"account-acct_7f3a9c\", components.AssessmentRequest{\n        AccountID: \"acct_7f3a9c\",\n        Flow: components.FlowAccountCreation,\n        Identifiers: components.AssessmentRequestIdentifiers{\n            Email: noresponse.Pointer(\"new.user@example.com\"),\n        },\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n    if res != nil {\n        // handle response\n    }\n}\n"
        - lang: java
          label: Java
          source: |
            package hello.world;

            import java.lang.Exception;
            import org.noresponse.NoResponse;
            import org.noresponse.models.errors.AuthenticationException;
            import org.noresponse.models.errors.BadRequestException;
            import org.noresponse.models.errors.ConflictException;
            import org.noresponse.models.errors.NotFoundException;
            import org.noresponse.models.errors.PermissionDeniedException;
            import org.noresponse.models.errors.RateLimitException;
            import org.noresponse.models.operations.CreateAssessmentResponse;
            import org.noresponse.models.shared.AssessmentRequest;
            import org.noresponse.models.shared.AssessmentRequestIdentifiers;
            import org.noresponse.models.shared.Flow;

            public class Application {

                public static void main(String[] args) throws Exception {

                    NoResponse sdk = NoResponse.builder()
                            .apiKey(System.getenv().getOrDefault("NORESPONSE_API_KEY", ""))
                        .build();

                    CreateAssessmentResponse res = sdk.assessments().create()
                            .idempotencyKey("account-acct_7f3a9c")
                            .body(AssessmentRequest.builder()
                                .accountId("acct_7f3a9c")
                                .flow(Flow.ACCOUNT_CREATION)
                                .identifiers(AssessmentRequestIdentifiers.builder()
                                    .email("new.user@example.com")
                                    .build())
                                .build())
                            .call();

                    if (res.assessment().isPresent()) {
                        System.out.println(res.assessment().get());
                    }
                }
            }
        - lang: csharp
          label: C#
          source: >
            using NoResponse;

            using NoResponse.Models.Shared;

            using System;


            var sdk = new NoResponseClient(apiKey:
            Environment.GetEnvironmentVariable("NORESPONSE_API_KEY"));


            var res = await sdk.Assessments.CreateAsync(
                body: new AssessmentRequest() {
                    AccountId = "acct_7f3a9c",
                    Flow = Flow.AccountCreation,
                    Identifiers = new AssessmentRequestIdentifiers() {
                        Email = "new.user@example.com",
                    },
                },
                idempotencyKey: "account-acct_7f3a9c"
            );


            // handle response
        - lang: php
          label: PHP
          source: |
            <?php

            declare(strict_types=1);

            require 'vendor/autoload.php';

            use NoResponse;
            use NoResponse\Models\Shared;

            $sdk = NoResponse\NoResponseClient::builder()
                ->setSecurity(
                    getenv('NORESPONSE_API_KEY') ?: ''
                )
                ->build();

            $body = new Shared\AssessmentRequest(
                accountId: 'acct_7f3a9c',
                flow: Shared\Flow::AccountCreation,
                identifiers: new Shared\AssessmentRequestIdentifiers(
                    email: 'new.user@example.com',
                ),
            );

            $response = $sdk->assessments->create(
                body: $body,
                idempotencyKey: 'account-acct_7f3a9c'

            );

            if ($response->assessment !== null) {
                // handle response
            }
        - lang: ruby
          label: Ruby
          source: >
            require 'noresponse'


            Models = ::NoResponse::Models

            s = ::NoResponse::NoResponseClient.new(
              api_key: ENV.fetch('NORESPONSE_API_KEY', '')
            )

            res = s.assessments.create(body:
            Models::Shared::AssessmentRequest.new(
              account_id: 'acct_7f3a9c',
              flow: Models::Shared::Flow::ACCOUNT_CREATION,
              identifiers: Models::Shared::AssessmentRequestIdentifiers.new(
                email: 'new.user@example.com'
              )
            ), idempotency_key: 'account-acct_7f3a9c')


            unless res.nil?
              # handle response
            end
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: >-
        Opaque client key, 16 to 128 characters of [A-Za-z0-9._:-] (a UUID or
        ULID fits). Scoped to the workspace principal and bound to the canonical
        body for 24 hours.
      schema:
        type: string
        minLength: 16
        maxLength: 128
        pattern: ^[A-Za-z0-9._:-]{16,128}$
        not:
          pattern: \n
  schemas:
    AssessmentRequest:
      title: Signup assessment request
      description: >-
        POST /v1/assessments body. flow names the customer event
        (account_creation, login or purchase) and depth how long the answer
        waits for sources (quick or full, defaulting per flow). The workspace is
        resolved from the bearer credential only, so the body has no workspace
        field and refuses one like any other unknown member. At least one of
        email, username or phone is required; name and ip only refine an
        assessment. No field is defined for a password, token or breach value,
        and the only hash accepted is issued_credential.sha256. Unknown members
        are refused, and account_id, request_id and username refuse exact secret
        shapes; keeping secrets out of them beyond that is a provider
        obligation. Server-side normalisation: the email local part is kept
        exactly as received (case preserved, NFC only) and only the domain is
        lower-cased; no alias rewriting of any kind. The username is echoed as
        received and compared and digested in its match form: NFC, Unicode
        default case fold (str.casefold), NFC again, so JaneDoe and janedoe are
        one username. The whole body is capped at 32768 bytes, above the largest
        body these limits allow; a larger body answers 413 before parsing.
        Optional client_context accepts bounded timezone, locale, platform, app
        version, raw user-agent for parsing, workspace-scoped opaque
        device/session IDs, form duration and a client timestamp. These are
        untrusted, privacy-minimised hints; the server does not echo or expose
        raw user-agent/device/session values and does not treat a single
        mismatch as a denial. Optional auth_context carries normalized claims
        from a customer backend after it verifies its WorkOS, Clerk, Auth0 or
        other OIDC/session token; raw tokens and provider secrets are refused.
        account_id is required, except when auth_context comes from a provider
        hook at pre_registration (transport provider_hook, hook_point
        pre_registration), where no account exists yet; the assessment then
        carries a server-issued account_id, pre: followed by its assessment_id.
      type: object
      additionalProperties: false
      required:
        - flow
        - identifiers
      properties:
        account_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[A-Za-z0-9][A-Za-z0-9._:|-]{0,127}$
          description: >-
            The provider's own opaque identifier for the account being checked,
            scoped to the calling workspace. It must be provider-opaque and must
            not contain a direct identifier (email, phone, name, national id):
            that is a provider obligation the schema can only partly check. '|'
            is accepted so an Auth0 user id such as 'auth0|64abc' passes
            unchanged; '@', '+', '$' and whitespace are refused by the pattern,
            as is an exact-shape JWT; a hex digest such as a SHA-256 is allowed,
            because hashing an internal id is a common pseudonymous scheme. A
            value such as '07700900123' or 'jane.doe.gmail.com' cannot be told
            apart from an opaque id. It travels in bodies, and in a URL only as
            the path segment of GET /v1/accounts/{account_id} and its
            /assessments. An assessment requested by a provider hook at
            pre_registration, which has no account yet, carries the
            server-issued account_id pre: followed by its assessment_id.
          not:
            description: refused_text plus secret_encoded, for account_id.
            anyOf:
              - description: >-
                  A line feed anywhere, refused with `not` beside every anchored
                  pattern.
                pattern: \n
              - description: >-
                  Exact shapes only: a complete modular-crypt password hash
                  (bcrypt $2a$/$2b$/$2x$/$2y$ with a two-digit cost and 53
                  characters; $argon2i/d/id with its m, t, p parameters, salt
                  and hash; md5-crypt $1$, sha256-crypt $5$ or sha512-crypt $6$
                  with a 22, 43 or 86 character hash; passlib $scrypt$ with ln,
                  r, p, salt and hash) or a three-segment JWT whose header is
                  base64url JSON opening with an "alg", "typ", "kid" or "cty"
                  member (eyJhbGciOi, eyJ0eXAiOi, eyJraWQiOi or eyJjdHkiOi). A
                  value that merely starts with '$2' or 'eyJ', such as
                  eyJohnathan.doe.x, is not refused. Best effort, not a
                  guarantee; see the provider obligation in the README.
                anyOf:
                  - pattern: ^\$2[abxy]?\$[0-9]{2}\$[./A-Za-z0-9]{53}$
                  - pattern: >-
                      ^\$argon2(?:id|i|d)\$(?:v=[0-9]{1,4}\$)?m=[0-9]{1,10},t=[0-9]{1,10},p=[0-9]{1,3}\$[A-Za-z0-9+/]{8,}\$[A-Za-z0-9+/]{16,}$
                  - pattern: >-
                      ^\$[156]\$(?:rounds=[0-9]{1,9}\$)?[./A-Za-z0-9]{1,16}\$(?:[./A-Za-z0-9]{22}|[./A-Za-z0-9]{43}|[./A-Za-z0-9]{86})$
                  - pattern: >-
                      ^\$scrypt\$ln=[0-9]{1,2},r=[0-9]{1,3},p=[0-9]{1,3}\$[A-Za-z0-9+/.]{8,}\$[A-Za-z0-9+/.]{16,}$
                  - pattern: >-
                      ^eyJ(?:hbGciOi|0eXAiOi|raWQiOi|jdHkiOi)[A-Za-z0-9_-]*\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*$
        flow:
          enum:
            - account_creation
            - login
            - purchase
            - signup
          description: >-
            The customer event this assessment gates. Required. The answer's
            account.risk.decision is the decision for this flow, and the flow
            sets the default depth.
        depth:
          enum:
            - quick
            - full
          description: >-
            Optional. Absent means the flow's default depth in the workspace's
            settings, which is full for account_creation and purchase and quick
            for login until changed.
        identifiers:
          type: object
          additionalProperties: false
          properties:
            email:
              type: string
              minLength: 3
              maxLength: 254
              pattern: "^[^\\s@<>\\x00-\\x1f\\x7f-\\x9f­؜᠎​-‏‪-‮⁠-⁤⁦-⁯\uFEFF￹-￻]{1,64}@(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\\.)+[A-Za-z]{2,63}$"
              description: >-
                One address: a 1 to 64 character local part without whitespace,
                angle brackets, control characters or invisible format
                characters (soft hyphen, zero-width and bidi controls, BOM),
                '@', and an LDH domain with a letter TLD. An internationalised
                domain is sent in its A-label (punycode) form. Nothing is
                trimmed: a padded value or one with a trailing line feed is
                refused. Normalisation: the local part keeps its case
                ('Jane.Doe' and 'jane.doe' are different identifiers) and is
                only NFC-normalised; the domain is lower-cased. Aliases are
                explicit: no '+tag' stripping, no dot removal and no
                provider-specific heuristics (googlemail.com is not gmail.com),
                and no identifier is linked across providers. Other Unicode
                format characters (general category Cf) that the pattern does
                not list are removed or refused by server normalisation.
              not:
                description: >-
                  A line feed anywhere, refused with `not` beside every anchored
                  pattern.
                pattern: \n
            username:
              type: string
              minLength: 1
              maxLength: 64
              pattern: "^[^\\s\\x00-\\x1f\\x7f-\\x9f­؜᠎​-‏‪-‮⁠-⁤⁦-⁯\uFEFF￹-￻]{1,64}$"
              description: >-
                The handle the account is signing up with: 1 to 64 characters,
                no whitespace, control or invisible format characters, and not
                an exact-shape credential hash, crypt string or JWT. Echoed as
                received; compared and digested case-insensitively in its match
                form; remaining Cf characters are a server normalisation rule.
              not:
                description: >-
                  refused_text plus secret_hex_digest and secret_encoded, for
                  request_id and username.
                anyOf:
                  - description: >-
                      A line feed anywhere, refused with `not` beside every
                      anchored pattern.
                    pattern: \n
                  - description: >-
                      Exactly 40, 64 or 128 hex characters (SHA-1, SHA-256,
                      SHA-512) and nothing else. Refused in username and
                      request_id, where such a value is almost certainly a
                      credential hash; a 39 or 41 character hex string, a short
                      hex word such as 'deadbeef' or a UUID is accepted. Not
                      refused in account_id: a hex digest of an internal id is a
                      common pseudonymous account id and not a secret under.
                    pattern: ^(?:[0-9A-Fa-f]{40}|[0-9A-Fa-f]{64}|[0-9A-Fa-f]{128})$
                  - description: >-
                      Exact shapes only: a complete modular-crypt password hash
                      (bcrypt $2a$/$2b$/$2x$/$2y$ with a two-digit cost and 53
                      characters; $argon2i/d/id with its m, t, p parameters,
                      salt and hash; md5-crypt $1$, sha256-crypt $5$ or
                      sha512-crypt $6$ with a 22, 43 or 86 character hash;
                      passlib $scrypt$ with ln, r, p, salt and hash) or a
                      three-segment JWT whose header is base64url JSON opening
                      with an "alg", "typ", "kid" or "cty" member (eyJhbGciOi,
                      eyJ0eXAiOi, eyJraWQiOi or eyJjdHkiOi). A value that merely
                      starts with '$2' or 'eyJ', such as eyJohnathan.doe.x, is
                      not refused. Best effort, not a guarantee; see the
                      provider obligation in the README.
                    anyOf:
                      - pattern: ^\$2[abxy]?\$[0-9]{2}\$[./A-Za-z0-9]{53}$
                      - pattern: >-
                          ^\$argon2(?:id|i|d)\$(?:v=[0-9]{1,4}\$)?m=[0-9]{1,10},t=[0-9]{1,10},p=[0-9]{1,3}\$[A-Za-z0-9+/]{8,}\$[A-Za-z0-9+/]{16,}$
                      - pattern: >-
                          ^\$[156]\$(?:rounds=[0-9]{1,9}\$)?[./A-Za-z0-9]{1,16}\$(?:[./A-Za-z0-9]{22}|[./A-Za-z0-9]{43}|[./A-Za-z0-9]{86})$
                      - pattern: >-
                          ^\$scrypt\$ln=[0-9]{1,2},r=[0-9]{1,3},p=[0-9]{1,3}\$[A-Za-z0-9+/.]{8,}\$[A-Za-z0-9+/.]{16,}$
                      - pattern: >-
                          ^eyJ(?:hbGciOi|0eXAiOi|raWQiOi|jdHkiOi)[A-Za-z0-9_-]*\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*$
            phone:
              type: string
              minLength: 9
              maxLength: 16
              pattern: ^\+[1-9][0-9]{7,14}$
              description: >-
                Strict E.164: '+', then 8 to 15 digits, the first not 0, with no
                separators and no trailing line feed. A phone pair is never
                reported not_found because it was not attempted.
              not:
                description: >-
                  A line feed anywhere, refused with `not` beside every anchored
                  pattern.
                pattern: \n
            name:
              type: string
              minLength: 1
              maxLength: 128
              pattern: "^[^\\x00-\\x1f\\x7f-\\x9f­؜᠎​-‏‪-‮⁠-⁤⁦-⁯\uFEFF￹-￻]{1,128}$"
              description: >-
                The full name as the user typed it, in one string: 1 to 128
                characters, with no control or invisible format characters
                (spaces are kept). The server NFC-normalises it and refuses any
                remaining control, format (Cf), line or paragraph separator
                character. It is never sent to a lookup or a provider: it is
                used only to match against names in the evidence. Not an
                exact-shape hash, crypt string or JWT.
              not:
                description: >-
                  refused_text plus secret_hex_digest and secret_encoded, for
                  request_id and username.
                anyOf:
                  - description: >-
                      A line feed anywhere, refused with `not` beside every
                      anchored pattern.
                    pattern: \n
                  - description: >-
                      Exactly 40, 64 or 128 hex characters (SHA-1, SHA-256,
                      SHA-512) and nothing else. Refused in username and
                      request_id, where such a value is almost certainly a
                      credential hash; a 39 or 41 character hex string, a short
                      hex word such as 'deadbeef' or a UUID is accepted. Not
                      refused in account_id: a hex digest of an internal id is a
                      common pseudonymous account id and not a secret under.
                    pattern: ^(?:[0-9A-Fa-f]{40}|[0-9A-Fa-f]{64}|[0-9A-Fa-f]{128})$
                  - description: >-
                      Exact shapes only: a complete modular-crypt password hash
                      (bcrypt $2a$/$2b$/$2x$/$2y$ with a two-digit cost and 53
                      characters; $argon2i/d/id with its m, t, p parameters,
                      salt and hash; md5-crypt $1$, sha256-crypt $5$ or
                      sha512-crypt $6$ with a 22, 43 or 86 character hash;
                      passlib $scrypt$ with ln, r, p, salt and hash) or a
                      three-segment JWT whose header is base64url JSON opening
                      with an "alg", "typ", "kid" or "cty" member (eyJhbGciOi,
                      eyJ0eXAiOi, eyJraWQiOi or eyJjdHkiOi). A value that merely
                      starts with '$2' or 'eyJ', such as eyJohnathan.doe.x, is
                      not refused. Best effort, not a guarantee; see the
                      provider obligation in the README.
                    anyOf:
                      - pattern: ^\$2[abxy]?\$[0-9]{2}\$[./A-Za-z0-9]{53}$
                      - pattern: >-
                          ^\$argon2(?:id|i|d)\$(?:v=[0-9]{1,4}\$)?m=[0-9]{1,10},t=[0-9]{1,10},p=[0-9]{1,3}\$[A-Za-z0-9+/]{8,}\$[A-Za-z0-9+/]{16,}$
                      - pattern: >-
                          ^\$[156]\$(?:rounds=[0-9]{1,9}\$)?[./A-Za-z0-9]{1,16}\$(?:[./A-Za-z0-9]{22}|[./A-Za-z0-9]{43}|[./A-Za-z0-9]{86})$
                      - pattern: >-
                          ^\$scrypt\$ln=[0-9]{1,2},r=[0-9]{1,3},p=[0-9]{1,3}\$[A-Za-z0-9+/.]{8,}\$[A-Za-z0-9+/.]{16,}$
                      - pattern: >-
                          ^eyJ(?:hbGciOi|0eXAiOi|raWQiOi|jdHkiOi)[A-Za-z0-9_-]*\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*$
            ip:
              type: string
              minLength: 2
              maxLength: 39
              pattern: >-
                ^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|[0-9a-f]{0,4}(?::[0-9a-f]{0,4}){2,7})$
              description: >-
                The account_creation's client IP address: an IPv4 dotted quad
                without leading zeros, or an IPv6 address in its RFC 5952 text
                form (lower case, no leading zeros, the longest zero run
                compressed). Send an IPv4 client as a dotted quad, never
                IPv4-mapped. No zone, port or prefix. The server refuses a
                non-canonical IPv6 spelling, and every private, loopback,
                link-local, multicast, documentation or otherwise reserved
                address (the IANA special-purpose registries), with 400: only a
                public address says anything about a signup.
              not:
                description: >-
                  A line feed anywhere, refused with `not` beside every anchored
                  pattern.
                pattern: \n
          description: >-
            At least one of email, username or phone. name and ip are matching
            inputs only, never looked up on their own, so a body with only name
            or ip (or neither) is refused with 400.
          anyOf:
            - required:
                - email
            - required:
                - username
            - required:
                - phone
        policy_context:
          type: object
          additionalProperties: false
          minProperties: 1
          properties:
            region:
              type: string
              pattern: ^[A-Z]{2}$
              description: >-
                ISO 3166-1 alpha-2 region of the user, as the provider
                determines it.
              not:
                description: >-
                  A line feed anywhere, refused with `not` beside every anchored
                  pattern.
                pattern: \n
          description: 'Optional policy inputs: region.'
        request_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$
          description: >-
            An optional provider correlation id, echoed back. It is not an
            idempotency key. Refuses an exact-shape JWT or a bare 40, 64 or 128
            character hex digest; a UUID is accepted.
          not:
            description: >-
              refused_text plus secret_hex_digest and secret_encoded, for
              request_id and username.
            anyOf:
              - description: >-
                  A line feed anywhere, refused with `not` beside every anchored
                  pattern.
                pattern: \n
              - description: >-
                  Exactly 40, 64 or 128 hex characters (SHA-1, SHA-256, SHA-512)
                  and nothing else. Refused in username and request_id, where
                  such a value is almost certainly a credential hash; a 39 or 41
                  character hex string, a short hex word such as 'deadbeef' or a
                  UUID is accepted. Not refused in account_id: a hex digest of
                  an internal id is a common pseudonymous account id and not a
                  secret under.
                pattern: ^(?:[0-9A-Fa-f]{40}|[0-9A-Fa-f]{64}|[0-9A-Fa-f]{128})$
              - description: >-
                  Exact shapes only: a complete modular-crypt password hash
                  (bcrypt $2a$/$2b$/$2x$/$2y$ with a two-digit cost and 53
                  characters; $argon2i/d/id with its m, t, p parameters, salt
                  and hash; md5-crypt $1$, sha256-crypt $5$ or sha512-crypt $6$
                  with a 22, 43 or 86 character hash; passlib $scrypt$ with ln,
                  r, p, salt and hash) or a three-segment JWT whose header is
                  base64url JSON opening with an "alg", "typ", "kid" or "cty"
                  member (eyJhbGciOi, eyJ0eXAiOi, eyJraWQiOi or eyJjdHkiOi). A
                  value that merely starts with '$2' or 'eyJ', such as
                  eyJohnathan.doe.x, is not refused. Best effort, not a
                  guarantee; see the provider obligation in the README.
                anyOf:
                  - pattern: ^\$2[abxy]?\$[0-9]{2}\$[./A-Za-z0-9]{53}$
                  - pattern: >-
                      ^\$argon2(?:id|i|d)\$(?:v=[0-9]{1,4}\$)?m=[0-9]{1,10},t=[0-9]{1,10},p=[0-9]{1,3}\$[A-Za-z0-9+/]{8,}\$[A-Za-z0-9+/]{16,}$
                  - pattern: >-
                      ^\$[156]\$(?:rounds=[0-9]{1,9}\$)?[./A-Za-z0-9]{1,16}\$(?:[./A-Za-z0-9]{22}|[./A-Za-z0-9]{43}|[./A-Za-z0-9]{86})$
                  - pattern: >-
                      ^\$scrypt\$ln=[0-9]{1,2},r=[0-9]{1,3},p=[0-9]{1,3}\$[A-Za-z0-9+/.]{8,}\$[A-Za-z0-9+/.]{16,}$
                  - pattern: >-
                      ^eyJ(?:hbGciOi|0eXAiOi|raWQiOi|jdHkiOi)[A-Za-z0-9_-]*\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*$
        deadline_ms:
          type: integer
          minimum: 50
          maximum: 3000
          description: >-
            Optional tighter budget, 50 to 3000 ms, measured from HTTP ingress
            including authentication, decoding, queueing and serialisation.
            Absent means the 3000 ms server ceiling; a value above the ceiling
            is refused with 400 rather than clamped.
        response_mode:
          enum:
            - standard
            - detailed
          default: standard
          description: >-
            standard returns the consolidated risk response; detailed adds
            merged provider-neutral facts when the key has detailed scope. raw
            is never available to a workspace.
        client_context:
          type: object
          additionalProperties: false
          minProperties: 1
          description: >-
            Optional, customer-observed context from the client of the user.
            These values are untrusted hints: the server parses them into
            privacy-preserving features, never echoes raw user_agent or opaque
            IDs, and never treats one mismatch as a block decision.
          properties:
            timezone:
              type: string
              maxLength: 64
              pattern: ^(?:UTC|[A-Za-z_]+(?:/[A-Za-z0-9_+.-]+)+)$
              description: >-
                IANA time-zone name such as Europe/London or
                America/Los_Angeles. Fixed numeric offsets are not accepted.
            locale:
              type: string
              maxLength: 35
              pattern: ^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$
              description: >-
                BCP 47 locale, for example en-AU. Treat as a presentation hint,
                not a nationality signal.
            platform:
              enum:
                - web
                - ios
                - android
                - other
              description: Client platform. Closed vocabulary keeps integrations stable.
            app_version:
              type: string
              maxLength: 32
              pattern: ^[A-Za-z0-9][A-Za-z0-9._+-]{0,31}$
              description: >-
                Customer application version, without build metadata that
                contains secrets.
            user_agent:
              type: string
              maxLength: 512
              description: >-
                Raw User-Agent captured at the customer edge. It is parsed
                server-side, never returned or retained in risk output.
            device_id:
              type: string
              maxLength: 128
              pattern: ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$
              description: >-
                Workspace-scoped opaque identifier generated by the customer. Do
                not send a hardware fingerprint; rotate it when the account or
                device changes.
            session_id:
              type: string
              maxLength: 128
              pattern: ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$
              description: >-
                Opaque signup session identifier. It is used for
                workspace-scoped velocity and replay checks only.
            form_duration_ms:
              type: integer
              minimum: 0
              maximum: 86400000
              description: >-
                Elapsed time from form start to submit. Values below three
                seconds are a weak automation hint and are never a sole adverse
                decision.
            client_observed_at:
              type: string
              format: date-time
              maxLength: 64
              description: >-
                RFC 3339 timestamp observed by the client. Server ingress time
                remains authoritative; a large clock skew is only a
                low-confidence feature.
        auth_context:
          title: Verified provider authentication context
          description: >-
            Optional server-derived authentication context. The customer
            verifies its WorkOS, Clerk, Auth0 or other OIDC/session token before
            sending this object from its backend, or a provider hook (an Auth0
            Action or a WorkOS Action) describes the event it was called for
            with transport provider_hook. No raw token or unverified browser
            claim is accepted. Values are opaque correlation inputs and bounded
            authentication-strength signals.
          type: object
          additionalProperties: false
          minProperties: 1
          properties:
            provider:
              type: string
              pattern: ^[a-z0-9][a-z0-9._-]{1,31}$
              description: >-
                Lowercase provider slug. WorkOS, Clerk and Auth0 have
                first-class adapters; every other provider uses a
                customer-specific OIDC slug. Provider brand is metadata; it does
                not change risk by itself.
            issuer:
              type: string
              maxLength: 256
              pattern: ^https://[^\s]+$
              description: >-
                The verified token issuer, normally the provider's HTTPS issuer
                URL.
            audience:
              type: string
              maxLength: 256
              pattern: ^[A-Za-z0-9._:/-]{1,256}$
              description: >-
                The verified audience/resource identifier used by the customer's
                API. Use audiences when the provider emits multiple values.
            audiences:
              type: array
              maxItems: 8
              items:
                type: string
                maxLength: 256
                pattern: ^[A-Za-z0-9._:/-]{1,256}$
              description: >-
                All verified audience/resource identifiers from an OAuth or OIDC
                token. The first value may also be copied to audience for simple
                integrations.
            subject:
              type: string
              maxLength: 256
              pattern: ^[A-Za-z0-9._:@/|+=~-]{1,256}$
              description: >-
                The verified stable subject identifier (`sub`), opaque to
                NoResponse. Required, except from a provider hook at
                pre_registration, where no user exists yet.
            session_id:
              type: string
              maxLength: 256
              pattern: ^[A-Za-z0-9._:@/|+=~-]{1,256}$
              description: The verified session identifier (`sid` or provider equivalent).
            organization_id:
              type: string
              maxLength: 256
              pattern: ^[A-Za-z0-9._:@/|+=~-]{1,256}$
              description: >-
                The active organization or workspace identifier, when the
                provider issued one.
            tenant_id:
              type: string
              maxLength: 256
              pattern: ^[A-Za-z0-9._:@/|+=~-]{1,256}$
              description: >-
                The provider workspace identifier, when distinct from
                organization_id.
            token_id:
              type: string
              maxLength: 256
              pattern: ^[A-Za-z0-9._:@/|+=~-]{1,256}$
              description: >-
                The verified token identifier (`jti`), used for replay and
                rotation correlation.
            issued_at:
              type: string
              format: date-time
              maxLength: 64
              description: Verified token issue time (`iat`).
            expires_at:
              type: string
              format: date-time
              maxLength: 64
              description: Verified token expiry (`exp`).
            session_created_at:
              type: string
              format: date-time
              maxLength: 64
              description: >-
                Creation time of the provider session from the verified backend
                session record (for example Clerk Session.createdAt or WorkOS
                Session.created_at). This is separate from token issuance and
                auth_time.
            auth_time:
              type: string
              format: date-time
              maxLength: 64
              description: >-
                Verified end-user authentication time (`auth_time`), when
                present.
            account_created_at:
              type: string
              format: date-time
              maxLength: 64
              description: >-
                Account creation time from the customer's verified provider
                record, when available.
            email_verified:
              type: boolean
              description: >-
                Whether the customer's provider asserts that the email
                identifier was verified.
            phone_verified:
              type: boolean
              description: >-
                Whether the customer's provider asserts that the phone
                identifier was verified.
            assurance_level:
              type: string
              enum:
                - unknown
                - low
                - medium
                - high
              description: >-
                Provider or OIDC assurance level, when the customer has mapped
                one.
            impersonated:
              type: boolean
              description: >-
                Whether the verified session is acting on behalf of another
                subject.
            transport:
              type: string
              enum:
                - backend
                - browser
                - provider_hook
              default: backend
              description: >-
                Where this context was derived. Backend is the trusted
                integration path; browser values are treated as weak hints.
                provider_hook is a hosted provider hook (an Auth0 or WorkOS
                Action) calling at hook_point; it needs hook_point and verified
                false, and its authentication facts are not scored.
            hook_point:
              type: string
              enum:
                - pre_registration
                - post_registration
                - pre_login
                - post_login
                - token_issuance
              description: >-
                The provider event a provider_hook context was sent for.
                pre_registration runs before the user exists, so subject and the
                request's account_id may be absent there. Sent only with
                transport provider_hook.
            verified:
              type: boolean
              description: >-
                Whether the customer completed signature, issuer, audience and
                expiry validation before calling NoResponse. False contexts are
                accepted only as weak hints.
            mfa:
              type: object
              additionalProperties: false
              minProperties: 1
              properties:
                present:
                  type: boolean
                methods:
                  type: array
                  maxItems: 8
                  items:
                    type: string
                    maxLength: 32
                    pattern: ^[A-Za-z0-9._:-]{1,32}$
                age_seconds:
                  type: integer
                  minimum: 0
                  maximum: 315360000
              description: >-
                Normalized multi-factor evidence from a verified provider
                session.
            roles:
              type: array
              maxItems: 32
              items:
                type: string
                maxLength: 64
                pattern: ^[A-Za-z0-9._:@/-]{1,64}$
              description: >-
                Bounded role slugs from the verified provider; roles do not
                change risk by themselves.
            scopes:
              type: array
              maxItems: 32
              items:
                type: string
                maxLength: 64
                pattern: ^[A-Za-z0-9._:@/-]{1,64}$
              description: >-
                Bounded scopes from the verified access token; scopes do not
                change risk by themselves.
          required:
            - provider
            - issuer
            - verified
          allOf:
            - if:
                required:
                  - transport
                properties:
                  transport:
                    const: provider_hook
              then:
                required:
                  - hook_point
                properties:
                  verified:
                    const: false
            - if:
                required:
                  - hook_point
              then:
                required:
                  - transport
                properties:
                  transport:
                    const: provider_hook
            - if:
                required:
                  - transport
                  - hook_point
                properties:
                  transport:
                    const: provider_hook
                  hook_point:
                    const: pre_registration
              else:
                required:
                  - subject
        issued_credential:
          type: object
          additionalProperties: false
          required:
            - sha256
          description: >-
            Optional. When your platform issues an API key to the new account,
            send SHA-256 of that key, so later usage of the key, or a leaked
            copy, ties back to this assessment. Each SDK has a helper that
            computes it. Send only the hash: customer keys are high-entropy, so
            the hash reveals nothing about them. The assessment stores it, and
            no response, webhook or log carries it.
          properties:
            sha256:
              type: string
              pattern: ^[0-9a-f]{64}$
              description: >-
                SHA-256 of the issued key's UTF-8 bytes, as 64 lower-case hex
                characters.
      if:
        required:
          - auth_context
        properties:
          auth_context:
            required:
              - transport
              - hook_point
            properties:
              transport:
                const: provider_hook
              hook_point:
                const: pre_registration
      else:
        required:
          - account_id
    Assessment:
      title: Assessment
      description: >-
        The assessment resource: the body of POST /v1/assessments (200 complete
        or partial, 202 pending), GET /v1/assessments/{assessment_id} and the
        assessment.updated webhook. Its main object is account: the account's
        one current risk, as GET /v1/accounts/{account_id} serves it, with
        risk.decision the decision for this assessment's flow. detail holds this
        assessment's own facts and score. The account's current risk comes from
        its latest assessment that has a decision, which is this one once it has
        a decision and no newer one does, and an earlier one while this one is
        pending. Concurrent assessments of one account each answer the account's
        risk as committed when they are served.
      type: object
      additionalProperties: false
      required:
        - schema_version
        - id
        - status
        - flow
        - depth
        - detail
      properties:
        schema_version:
          const: '1'
          description: Version of the body shape.
        id:
          type: string
          pattern: ^asm_[0-9A-HJKMNP-TV-Z]{26}$
          description: This assessment's id, equal to detail.assessment_id.
          not:
            description: >-
              A line feed anywhere, refused with `not` beside every anchored
              pattern.
            pattern: \n
        status:
          enum:
            - complete
            - partial
            - pending
          description: >-
            This assessment's status, equal to detail.status. While it is
            pending, account carries the risk of the account's previous decided
            assessment, or is absent.
        flow:
          enum:
            - account_creation
            - login
            - purchase
          description: >-
            The flow the request named. account.risk.decision is the decision
            for it. An assessment made before flows existed reports
            account_creation.
        depth:
          enum:
            - quick
            - full
          description: >-
            The depth the assessment was served at: the request's, or its flow's
            default. An assessment made before depths existed reports full.
        account:
          title: Account
          description: >-
            The account's one current risk when this assessment was served,
            decided for this assessment's flow: the body GET
            /v1/accounts/{account_id} would answer, with risk.decision equal to
            decisions_by_flow for flow. Absent only while the account has no
            assessment with a decision (its first assessment is still pending);
            a webhook or GET of the decided assessment then carries it.
          type: object
          additionalProperties: false
          required:
            - schema_version
            - id
            - risk
            - decisions_by_flow
            - assessment_id
            - updated_at
          properties:
            schema_version:
              const: '1'
              description: Version of the body shape.
            id:
              type: string
              minLength: 1
              maxLength: 128
              pattern: ^[A-Za-z0-9][A-Za-z0-9._:|-]{0,127}$
              description: The account_id the workspace sends with its assessments.
              not:
                description: refused_text plus secret_encoded, for account_id.
                anyOf:
                  - description: >-
                      A line feed anywhere, refused with `not` beside every
                      anchored pattern.
                    pattern: \n
                  - description: >-
                      Exact shapes only: a complete modular-crypt password hash
                      (bcrypt $2a$/$2b$/$2x$/$2y$ with a two-digit cost and 53
                      characters; $argon2i/d/id with its m, t, p parameters,
                      salt and hash; md5-crypt $1$, sha256-crypt $5$ or
                      sha512-crypt $6$ with a 22, 43 or 86 character hash;
                      passlib $scrypt$ with ln, r, p, salt and hash) or a
                      three-segment JWT whose header is base64url JSON opening
                      with an "alg", "typ", "kid" or "cty" member (eyJhbGciOi,
                      eyJ0eXAiOi, eyJraWQiOi or eyJjdHkiOi). A value that merely
                      starts with '$2' or 'eyJ', such as eyJohnathan.doe.x, is
                      not refused. Best effort, not a guarantee; see the
                      provider obligation in the README.
                    anyOf:
                      - pattern: ^\$2[abxy]?\$[0-9]{2}\$[./A-Za-z0-9]{53}$
                      - pattern: >-
                          ^\$argon2(?:id|i|d)\$(?:v=[0-9]{1,4}\$)?m=[0-9]{1,10},t=[0-9]{1,10},p=[0-9]{1,3}\$[A-Za-z0-9+/]{8,}\$[A-Za-z0-9+/]{16,}$
                      - pattern: >-
                          ^\$[156]\$(?:rounds=[0-9]{1,9}\$)?[./A-Za-z0-9]{1,16}\$(?:[./A-Za-z0-9]{22}|[./A-Za-z0-9]{43}|[./A-Za-z0-9]{86})$
                      - pattern: >-
                          ^\$scrypt\$ln=[0-9]{1,2},r=[0-9]{1,3},p=[0-9]{1,3}\$[A-Za-z0-9+/.]{8,}\$[A-Za-z0-9+/.]{16,}$
                      - pattern: >-
                          ^eyJ(?:hbGciOi|0eXAiOi|raWQiOi|jdHkiOi)[A-Za-z0-9_-]*\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*$
            risk:
              type: object
              additionalProperties: false
              required:
                - score
                - decision
                - reasons
              properties:
                score:
                  type: integer
                  minimum: 0
                  maximum: 100
                  description: 0 (safe) to 100 (risky), as risk.score of the assessment.
                decision:
                  enum:
                    - allow
                    - step_up
                    - review
                    - block
                  description: >-
                    The decision for the flow of the assessment this account is
                    served with: on GET /v1/accounts/{account_id}, the flow of
                    the current assessment; inside an assessment, that
                    assessment's flow.
                reasons:
                  type: array
                  maxItems: 32
                  description: >-
                    Every reason that fired, highest weight first. The weights
                    sum to score before clamping.
                  items:
                    type: object
                    additionalProperties: false
                    required:
                      - code
                      - weight
                    properties:
                      code:
                        enum:
                          - no_digital_footprint
                          - voip_number
                          - disposable_email
                          - new_email
                          - spam_reports
                          - fraud_flag
                          - identity_mismatch
                          - country_mismatch
                          - proxy_ip
                          - seen_before
                          - shared_ip
                          - many_recent_accounts
                          - established_accounts
                          - aged_identity
                          - breach_name_match
                          - breach_cooccurrence
                          - breach_name_mismatch
                          - breach_phone_mismatch
                          - recent_password_exposure
                          - credential_list_exposure
                          - abuse_ecosystem_member
                          - no_breach_history
                          - automation_fast_submit
                          - client_clock_skew
                          - bot_or_automation
                          - auth_email_verified
                          - auth_phone_verified
                          - auth_mfa_recent
                          - auth_new_session
                          - auth_new_account
                          - auth_impersonated
                          - context_timezone_mismatch
                          - context_locale_mismatch
                          - context_line_country_mismatch
                          - prior_abuse_link
                          - test_scenario
                          - shared_api_key
                        description: >-
                          A risk reason from the reviewed list.
                          old_breach_history and consistent_identity were
                          replaced by the breach codes (aged_identity,
                          breach_name_match, breach_cooccurrence and the
                          mismatches) and are refused. test_scenario appears
                          only on an assessment made with a test key
                          (nrk_test_), which the deterministic simulator
                          answers: its weight is the scenario the magic email or
                          phone chose, and 0 for any other identifier. A live
                          assessment never carries it. shared_api_key is an
                          account reason, never an assessment one: it appears
                          only in the risk of GET /v1/accounts/{account_id} and
                          the account an assessment carries, when a key your
                          account creation gate issued to the account
                          (issued_credential.sha256) was used from many IP
                          addresses or countries within 24 hours (POST
                          /v1/key_usage/batches).
                      weight:
                        type: integer
                        minimum: -100
                        maximum: 100
                      identifier:
                        enum:
                          - email
                          - username
                          - phone
                          - name
                          - ip
                          - context
                        description: >-
                          The submitted identifier or bounded client context a
                          reason concerns; absent when it concerns the whole
                          assessment. Context is a claimed, untrusted hint and
                          never a device fingerprint.
            decisions_by_flow:
              type: object
              additionalProperties: false
              required:
                - account_creation
                - login
                - purchase
              description: >-
                The decision for each flow: the account's score under that
                flow's thresholds. A flow the workspace has not set uses its
                account_creation thresholds, so the flows differ only where it
                set them.
              properties:
                account_creation:
                  enum:
                    - allow
                    - step_up
                    - review
                    - block
                  description: >-
                    block when the score reached the workspace's block threshold
                    (risk.block of the assessment), otherwise the assessment's
                    recommendation. Advisory: the workspace decides and
                    enforces.
                login:
                  enum:
                    - allow
                    - step_up
                    - review
                    - block
                  description: >-
                    block when the score reached the workspace's block threshold
                    (risk.block of the assessment), otherwise the assessment's
                    recommendation. Advisory: the workspace decides and
                    enforces.
                purchase:
                  enum:
                    - allow
                    - step_up
                    - review
                    - block
                  description: >-
                    block when the score reached the workspace's block threshold
                    (risk.block of the assessment), otherwise the assessment's
                    recommendation. Advisory: the workspace decides and
                    enforces.
            assessment_id:
              type: string
              pattern: ^asm_[0-9A-HJKMNP-TV-Z]{26}$
              description: The assessment the current risk comes from.
              not:
                description: >-
                  A line feed anywhere, refused with `not` beside every anchored
                  pattern.
                pattern: \n
            updated_at:
              type: string
              format: date-time
              description: updated_at of that assessment's latest revision.
          $defs:
            account_decision:
              enum:
                - allow
                - step_up
                - review
                - block
              description: >-
                block when the score reached the workspace's block threshold
                (risk.block of the assessment), otherwise the assessment's
                recommendation. Advisory: the workspace decides and enforces.
        detail:
          title: Assessment detail
          description: >-
            This assessment's own facts: its score, recommendation, signals,
            coverage and timing.
          type: object
          additionalProperties: false
          required:
            - schema_version
            - assessment_id
            - account_id
            - status
            - confidence
            - incomplete_sources
            - policy_version
            - policy_basis
            - assessment_version
            - created_at
            - updated_at
            - expires_at
            - timing
          properties:
            schema_version:
              const: '1'
              description: Version of the body shape.
            assessment_id:
              type: string
              pattern: ^asm_[0-9A-HJKMNP-TV-Z]{26}$
              description: >-
                Opaque, server-assigned assessment id: 'asm_' and a 26-character
                Crockford base32 ULID. Unique within the workspace; another
                workspace's assessment id answers 404.
              not:
                description: >-
                  A line feed anywhere, refused with `not` beside every anchored
                  pattern.
                pattern: \n
            account_id:
              type: string
              minLength: 1
              maxLength: 128
              pattern: ^[A-Za-z0-9][A-Za-z0-9._:|-]{0,127}$
              description: >-
                The provider's own opaque identifier for the account being
                checked, scoped to the calling workspace. It must be
                provider-opaque and must not contain a direct identifier (email,
                phone, name, national id): that is a provider obligation the
                schema can only partly check. '|' is accepted so an Auth0 user
                id such as 'auth0|64abc' passes unchanged; '@', '+', '$' and
                whitespace are refused by the pattern, as is an exact-shape JWT;
                a hex digest such as a SHA-256 is allowed, because hashing an
                internal id is a common pseudonymous scheme. A value such as
                '07700900123' or 'jane.doe.gmail.com' cannot be told apart from
                an opaque id. It travels in bodies, and in a URL only as the
                path segment of GET /v1/accounts/{account_id} and its
                /assessments. An assessment requested by a provider hook at
                pre_registration, which has no account yet, carries the
                server-issued account_id pre: followed by its assessment_id.
              not:
                description: refused_text plus secret_encoded, for account_id.
                anyOf:
                  - description: >-
                      A line feed anywhere, refused with `not` beside every
                      anchored pattern.
                    pattern: \n
                  - description: >-
                      Exact shapes only: a complete modular-crypt password hash
                      (bcrypt $2a$/$2b$/$2x$/$2y$ with a two-digit cost and 53
                      characters; $argon2i/d/id with its m, t, p parameters,
                      salt and hash; md5-crypt $1$, sha256-crypt $5$ or
                      sha512-crypt $6$ with a 22, 43 or 86 character hash;
                      passlib $scrypt$ with ln, r, p, salt and hash) or a
                      three-segment JWT whose header is base64url JSON opening
                      with an "alg", "typ", "kid" or "cty" member (eyJhbGciOi,
                      eyJ0eXAiOi, eyJraWQiOi or eyJjdHkiOi). A value that merely
                      starts with '$2' or 'eyJ', such as eyJohnathan.doe.x, is
                      not refused. Best effort, not a guarantee; see the
                      provider obligation in the README.
                    anyOf:
                      - pattern: ^\$2[abxy]?\$[0-9]{2}\$[./A-Za-z0-9]{53}$
                      - pattern: >-
                          ^\$argon2(?:id|i|d)\$(?:v=[0-9]{1,4}\$)?m=[0-9]{1,10},t=[0-9]{1,10},p=[0-9]{1,3}\$[A-Za-z0-9+/]{8,}\$[A-Za-z0-9+/]{16,}$
                      - pattern: >-
                          ^\$[156]\$(?:rounds=[0-9]{1,9}\$)?[./A-Za-z0-9]{1,16}\$(?:[./A-Za-z0-9]{22}|[./A-Za-z0-9]{43}|[./A-Za-z0-9]{86})$
                      - pattern: >-
                          ^\$scrypt\$ln=[0-9]{1,2},r=[0-9]{1,3},p=[0-9]{1,3}\$[A-Za-z0-9+/.]{8,}\$[A-Za-z0-9+/.]{16,}$
                      - pattern: >-
                          ^eyJ(?:hbGciOi|0eXAiOi|raWQiOi|jdHkiOi)[A-Za-z0-9_-]*\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*$
            request_id:
              type: string
              minLength: 1
              maxLength: 128
              pattern: ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$
              description: >-
                An optional provider correlation id, echoed back. It is not an
                idempotency key. Refuses an exact-shape JWT or a bare 40, 64 or
                128 character hex digest; a UUID is accepted.
              not:
                description: >-
                  refused_text plus secret_hex_digest and secret_encoded, for
                  request_id and username.
                anyOf:
                  - description: >-
                      A line feed anywhere, refused with `not` beside every
                      anchored pattern.
                    pattern: \n
                  - description: >-
                      Exactly 40, 64 or 128 hex characters (SHA-1, SHA-256,
                      SHA-512) and nothing else. Refused in username and
                      request_id, where such a value is almost certainly a
                      credential hash; a 39 or 41 character hex string, a short
                      hex word such as 'deadbeef' or a UUID is accepted. Not
                      refused in account_id: a hex digest of an internal id is a
                      common pseudonymous account id and not a secret under.
                    pattern: ^(?:[0-9A-Fa-f]{40}|[0-9A-Fa-f]{64}|[0-9A-Fa-f]{128})$
                  - description: >-
                      Exact shapes only: a complete modular-crypt password hash
                      (bcrypt $2a$/$2b$/$2x$/$2y$ with a two-digit cost and 53
                      characters; $argon2i/d/id with its m, t, p parameters,
                      salt and hash; md5-crypt $1$, sha256-crypt $5$ or
                      sha512-crypt $6$ with a 22, 43 or 86 character hash;
                      passlib $scrypt$ with ln, r, p, salt and hash) or a
                      three-segment JWT whose header is base64url JSON opening
                      with an "alg", "typ", "kid" or "cty" member (eyJhbGciOi,
                      eyJ0eXAiOi, eyJraWQiOi or eyJjdHkiOi). A value that merely
                      starts with '$2' or 'eyJ', such as eyJohnathan.doe.x, is
                      not refused. Best effort, not a guarantee; see the
                      provider obligation in the README.
                    anyOf:
                      - pattern: ^\$2[abxy]?\$[0-9]{2}\$[./A-Za-z0-9]{53}$
                      - pattern: >-
                          ^\$argon2(?:id|i|d)\$(?:v=[0-9]{1,4}\$)?m=[0-9]{1,10},t=[0-9]{1,10},p=[0-9]{1,3}\$[A-Za-z0-9+/]{8,}\$[A-Za-z0-9+/]{16,}$
                      - pattern: >-
                          ^\$[156]\$(?:rounds=[0-9]{1,9}\$)?[./A-Za-z0-9]{1,16}\$(?:[./A-Za-z0-9]{22}|[./A-Za-z0-9]{43}|[./A-Za-z0-9]{86})$
                      - pattern: >-
                          ^\$scrypt\$ln=[0-9]{1,2},r=[0-9]{1,3},p=[0-9]{1,3}\$[A-Za-z0-9+/.]{8,}\$[A-Za-z0-9+/.]{16,}$
                      - pattern: >-
                          ^eyJ(?:hbGciOi|0eXAiOi|raWQiOi|jdHkiOi)[A-Za-z0-9_-]*\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]*$
            status:
              enum:
                - complete
                - partial
                - pending
              description: >-
                complete: final; nothing is still running. A lane that failed or
                missed its 10 s deadline stays listed in incomplete_sources, and
                confidence is lower. partial: a recommendation exists and at
                least one lane is still running or did not settle (see
                incomplete_sources). pending: nothing usable before the
                deadline; no recommendation yet, poll or wait for the webhook.
            recommendation:
              enum:
                - allow
                - step_up
                - review
              description: Advisory only. The provider decides and enforces.
            confidence:
              enum:
                - low
                - medium
                - high
                - unavailable
              description: >-
                Ordinal confidence in the recommendation under the current
                heuristic policy. It is not a probability.
            risk:
              type: object
              additionalProperties: false
              required:
                - score
                - band
                - recommendation
                - confidence
                - policy_version
                - policy_basis
                - reasons
                - coverage
              description: >-
                the one risk decision for this assessment. score is 0 (safe) to
                100 (risky): the sum of the weights of the reasons that fired,
                clamped to 0..100, from hand-set versioned weights. Every served
                answer with a recommendation carries it, and there the top-level
                recommendation equals risk.recommendation. block is true from
                the workspace's block threshold (block_threshold), off by
                default. It names no source or provider.
              properties:
                score:
                  type: integer
                  minimum: 0
                  maximum: 100
                band:
                  enum:
                    - low
                    - medium
                    - high
                    - critical
                  description: low 0-29, medium 30-59, high 60-84, critical 85-100.
                recommendation:
                  enum:
                    - allow
                    - step_up
                    - review
                  description: Advisory only. The provider decides and enforces.
                block:
                  type: boolean
                  description: >-
                    true when the workspace's block threshold is set and score
                    reached it. A provider hook can deny the account creation on
                    it; the provider decides and enforces. It does not change
                    recommendation. It is false while blocking is off, the
                    default, and a body from before the flag existed omits it:
                    read absent as false.
                block_threshold:
                  type: integer
                  minimum: 1
                  maximum: 100
                  description: >-
                    The block threshold this answer used: block is true exactly
                    when score is at least this value. Absent while blocking is
                    off. NoResponse sets it per workspace on request, at or
                    above the review threshold.
                confidence:
                  enum:
                    - low
                    - medium
                    - high
                  description: >-
                    How much of the planned evidence answered; it says nothing
                    about risk.
                policy_version:
                  type: string
                  minLength: 1
                  maxLength: 64
                  pattern: ^[a-z0-9][a-z0-9._-]{0,63}(\+t[1-9][0-9]{0,8})?$
                  description: >-
                    Version of the hand-authored heuristic weights that produced
                    the recommendation, for example 'risk-2026-10.7'. A
                    workspace threshold override adds a +t<N> suffix, e.g.
                    'risk-2026-10.7+t3'.
                  not:
                    description: >-
                      A line feed anywhere, refused with `not` beside every
                      anchored pattern.
                    pattern: \n
                policy_basis:
                  const: heuristic_unvalidated
                reasons:
                  type: array
                  maxItems: 32
                  description: >-
                    Every reason that fired, highest weight first. The weights
                    sum to score before clamping.
                  items:
                    type: object
                    additionalProperties: false
                    required:
                      - code
                      - weight
                    properties:
                      code:
                        enum:
                          - no_digital_footprint
                          - voip_number
                          - disposable_email
                          - new_email
                          - spam_reports
                          - fraud_flag
                          - identity_mismatch
                          - country_mismatch
                          - proxy_ip
                          - seen_before
                          - shared_ip
                          - many_recent_accounts
                          - established_accounts
                          - aged_identity
                          - breach_name_match
                          - breach_cooccurrence
                          - breach_name_mismatch
                          - breach_phone_mismatch
                          - recent_password_exposure
                          - credential_list_exposure
                          - abuse_ecosystem_member
                          - no_breach_history
                          - automation_fast_submit
                          - client_clock_skew
                          - bot_or_automation
                          - auth_email_verified
                          - auth_phone_verified
                          - auth_mfa_recent
                          - auth_new_session
                          - auth_new_account
                          - auth_impersonated
                          - context_timezone_mismatch
                          - context_locale_mismatch
                          - context_line_country_mismatch
                          - prior_abuse_link
                          - test_scenario
                          - shared_api_key
                        description: >-
                          A risk reason from the reviewed list.
                          old_breach_history and consistent_identity were
                          replaced by the breach codes (aged_identity,
                          breach_name_match, breach_cooccurrence and the
                          mismatches) and are refused. test_scenario appears
                          only on an assessment made with a test key
                          (nrk_test_), which the deterministic simulator
                          answers: its weight is the scenario the magic email or
                          phone chose, and 0 for any other identifier. A live
                          assessment never carries it. shared_api_key is an
                          account reason, never an assessment one: it appears
                          only in the risk of GET /v1/accounts/{account_id} and
                          the account an assessment carries, when a key your
                          account creation gate issued to the account
                          (issued_credential.sha256) was used from many IP
                          addresses or countries within 24 hours (POST
                          /v1/key_usage/batches).
                      weight:
                        type: integer
                        minimum: -100
                        maximum: 100
                      identifier:
                        enum:
                          - email
                          - username
                          - phone
                          - name
                          - ip
                          - context
                        description: >-
                          The submitted identifier or bounded client context a
                          reason concerns; absent when it concerns the whole
                          assessment. Context is a claimed, untrusted hint and
                          never a device fingerprint.
                summary:
                  type: object
                  additionalProperties: false
                  properties:
                    phone:
                      type: object
                      additionalProperties: false
                      properties:
                        line_type:
                          enum:
                            - mobile
                            - voip
                            - landline
                            - toll_free
                            - other
                        country:
                          type: string
                          pattern: ^[A-Z]{2}$
                          not:
                            pattern: \n
                        report_count:
                          type: integer
                          minimum: 0
                          description: Highest report count any source gave for the number.
                        carrier:
                          type: string
                          minLength: 1
                          maxLength: 128
                          description: >-
                            Carrier-network metadata from privately weighted
                            sources; not upstream provider attribution.
                    email:
                      type: object
                      additionalProperties: false
                      properties:
                        disposable:
                          type: boolean
                        accounts_found:
                          type: integer
                          minimum: 0
                          description: Accounts found for the email.
                        breach:
                          type: object
                          additionalProperties: false
                          required:
                            - found
                          description: >-
                            Breach facts for the email. Replaces the boolean
                            breached.
                          properties:
                            found:
                              type: boolean
                            breach_count:
                              type: integer
                              minimum: 0
                              description: Distinct breach tables the value appears in.
                            first_year:
                              type: integer
                              minimum: 1970
                              maximum: 2100
                              description: Year of the oldest dated table.
                            last_year:
                              type: integer
                              minimum: 1970
                              maximum: 2100
                              description: Year of the newest dated table.
                            exposed:
                              type: array
                              maxItems: 11
                              items:
                                enum:
                                  - email
                                  - username
                                  - name
                                  - password
                                  - password_hash
                                  - phone
                                  - address
                                  - dob
                                  - ip
                                  - payment
                                  - security_qa
                                description: >-
                                  A kind of data a breach table held next to the
                                  identifier. password means a plaintext
                                  password was present; NoResponse never stores
                                  it and never says what it was. password_hash
                                  means a hash, salt or token was present. A
                                  value, never: the schema has no member for
                                  one.
                              description: >-
                                The union of data kinds the matching tables
                                held, never the values.
                            in_credential_lists:
                              type: boolean
                              description: >-
                                The value is in a credential_list or stealer_log
                                table.
                            recent_password_exposure:
                              type: boolean
                              description: >-
                                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 table.
                            truncated:
                              type: boolean
                          allOf:
                            - if:
                                properties:
                                  truncated:
                                    const: true
                                required:
                                  - truncated
                              then:
                                properties:
                                  found:
                                    const: true
                            - if:
                                properties:
                                  found:
                                    const: false
                                required:
                                  - found
                              then:
                                not:
                                  anyOf:
                                    - required:
                                        - first_year
                                    - required:
                                        - last_year
                                    - required:
                                        - exposed
                    username:
                      type: object
                      additionalProperties: false
                      properties:
                        accounts_found:
                          type: integer
                          minimum: 0
                          description: Accounts found for the username.
                        breach:
                          type: object
                          additionalProperties: false
                          required:
                            - found
                          description: Breach facts for the username.
                          properties:
                            found:
                              type: boolean
                            breach_count:
                              type: integer
                              minimum: 0
                              description: Distinct breach tables the value appears in.
                            first_year:
                              type: integer
                              minimum: 1970
                              maximum: 2100
                              description: Year of the oldest dated table.
                            last_year:
                              type: integer
                              minimum: 1970
                              maximum: 2100
                              description: Year of the newest dated table.
                            exposed:
                              type: array
                              maxItems: 11
                              items:
                                enum:
                                  - email
                                  - username
                                  - name
                                  - password
                                  - password_hash
                                  - phone
                                  - address
                                  - dob
                                  - ip
                                  - payment
                                  - security_qa
                                description: >-
                                  A kind of data a breach table held next to the
                                  identifier. password means a plaintext
                                  password was present; NoResponse never stores
                                  it and never says what it was. password_hash
                                  means a hash, salt or token was present. A
                                  value, never: the schema has no member for
                                  one.
                              description: >-
                                The union of data kinds the matching tables
                                held, never the values.
                            in_credential_lists:
                              type: boolean
                              description: >-
                                The value is in a credential_list or stealer_log
                                table.
                            recent_password_exposure:
                              type: boolean
                              description: >-
                                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 table.
                            truncated:
                              type: boolean
                          allOf:
                            - if:
                                properties:
                                  truncated:
                                    const: true
                                required:
                                  - truncated
                              then:
                                properties:
                                  found:
                                    const: true
                            - if:
                                properties:
                                  found:
                                    const: false
                                required:
                                  - found
                              then:
                                not:
                                  anyOf:
                                    - required:
                                        - first_year
                                    - required:
                                        - last_year
                                    - required:
                                        - exposed
                    ip:
                      type: object
                      additionalProperties: false
                      description: >-
                        Facts about the submitted IP from the offline database
                        and this workspace's own recent assessments. Every
                        member is optional; absent makes no claim.
                      properties:
                        country:
                          type: string
                          pattern: ^[A-Z]{2}$
                          not:
                            description: >-
                              A line feed anywhere, refused with `not` beside
                              every anchored pattern.
                            pattern: \n
                          description: ISO 3166-1 alpha-2 country of the address.
                        type:
                          enum:
                            - residential
                            - hosting
                            - vpn
                            - proxy
                            - tor
                            - mobile
                            - unknown
                          description: The address's network type.
                        seen_in_workspace:
                          type: integer
                          minimum: 0
                          description: >-
                            Assessments in this workspace with the same IP in
                            the last 24 hours, this one included. A count only.
                    consistency:
                      type: object
                      additionalProperties: false
                      description: >-
                        Cross-identifier agreement, computed from facts only.
                        Every member is optional; absent makes no claim.
                      properties:
                        name:
                          enum:
                            - match
                            - partial
                            - mismatch
                            - unknown
                          description: >-
                            The submitted name against breach names and
                            caller-ID names.
                        phone:
                          enum:
                            - match
                            - partial
                            - mismatch
                            - unknown
                          description: >-
                            The submitted phone against numbers found next to
                            the email in breach records.
                        ip:
                          enum:
                            - match
                            - partial
                            - mismatch
                            - unknown
                          description: >-
                            The submitted IP against IPs found next to the email
                            or username in breach records.
                        country:
                          enum:
                            - consistent
                            - mismatch
                            - unknown
                          description: >-
                            IP country, phone country, breach countries and
                            policy_context.region together.
                  description: >-
                    A few typed facts per identifier. Every fact is optional:
                    absent means the evidence did not answer and makes no claim,
                    while false or 0 is a definite answer. Lanes that do not
                    exist yet leave their members absent.
                coverage:
                  type: object
                  additionalProperties: false
                  required:
                    - answered
                    - failed
                    - pending
                  description: Sources by state, never by name.
                  properties:
                    answered:
                      type: integer
                      minimum: 0
                      description: Sources that gave a definite answer.
                    failed:
                      type: integer
                      minimum: 0
                      description: Sources that could not answer.
                    pending:
                      type: integer
                      minimum: 0
                      description: Sources still being asked.
                details:
                  type: object
                  additionalProperties: false
                  description: >-
                    Detailed view only (response_mode=detailed, which needs the
                    detailed scope); a standard answer never carries it. Merged
                    observations: no provider, source, origin, raw breach
                    record, value or score.
                  properties:
                    phone_names:
                      type: array
                      maxItems: 32
                      items:
                        type: object
                        additionalProperties: false
                        required:
                          - value
                          - seen_by
                        properties:
                          value:
                            type: string
                            minLength: 1
                            maxLength: 128
                          seen_by:
                            type: integer
                            minimum: 1
                    breaches:
                      type: array
                      maxItems: 50
                      items:
                        type: object
                        additionalProperties: false
                        required:
                          - name
                          - kind
                          - matched_on
                        description: >-
                          One breach table that matched, detailed view only. No
                          member carries a record, value, password or hash.
                        properties:
                          name:
                            type: string
                            minLength: 1
                            maxLength: 128
                            not:
                              description: >-
                                A line feed anywhere, refused with `not` beside
                                every anchored pattern.
                              pattern: \n
                          domain:
                            type: string
                            minLength: 3
                            maxLength: 253
                            pattern: >-
                              ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$
                            not:
                              description: >-
                                A line feed anywhere, refused with `not` beside
                                every anchored pattern.
                              pattern: \n
                            description: >-
                              The breached site's domain, lower case. Absent
                              when the table has none (a credential list, for
                              example).
                          category:
                            type: string
                            pattern: ^[a-z0-9_]{1,64}$
                            not:
                              description: >-
                                A line feed anywhere, refused with `not` beside
                                every anchored pattern.
                              pattern: \n
                            description: >-
                              The table's category from its name, lower case
                              (social, gaming, combolist...).
                          kind:
                            enum:
                              - site_breach
                              - credential_list
                              - stealer_log
                              - scrape
                              - hacking_forum
                              - abuse_service
                              - darkweb_site
                            description: >-
                              Who is in the table: site_breach, a site's own
                              users; credential_list, a combolist or
                              compilation; stealer_log, malware or botnet
                              victims; scrape; hacking_forum, a criminal
                              hacking, cracking or carding community;
                              abuse_service, the customers of DDoS-for-hire,
                              account generators, dump or card shops, darknet
                              markets, stalkerware or breach-lookup services;
                              darkweb_site, an onion site of unknown purpose.
                          year:
                            type: integer
                            minimum: 1970
                            maximum: 2100
                            description: A breach table's date year.
                          month:
                            type: integer
                            minimum: 1
                            maximum: 12
                            description: Absent when only the year is known.
                          matched_on:
                            enum:
                              - email
                              - username
                              - ip
                            description: >-
                              The submitted identifier a breach table matched
                              on. The phone is never a breach selector, and the
                              name is never looked up.
                          exposed:
                            type: array
                            maxItems: 11
                            items:
                              enum:
                                - email
                                - username
                                - name
                                - password
                                - password_hash
                                - phone
                                - address
                                - dob
                                - ip
                                - payment
                                - security_qa
                              description: >-
                                A kind of data a breach table held next to the
                                identifier. password means a plaintext password
                                was present; NoResponse never stores it and
                                never says what it was. password_hash means a
                                hash, salt or token was present. A value, never:
                                the schema has no member for one.
                      description: The matching breach tables, newest first, at most 50.
                    breach_names:
                      type: array
                      maxItems: 32
                      description: >-
                        Names found next to the identifiers in breach records,
                        merged, with the number of distinct tables each was seen
                        in.
                      items:
                        type: object
                        additionalProperties: false
                        required:
                          - value
                          - seen_in
                        properties:
                          value:
                            type: string
                            minLength: 1
                            maxLength: 128
                          seen_in:
                            type: integer
                            minimum: 1
              allOf:
                - if:
                    properties:
                      block:
                        const: true
                    required:
                      - block
                  then:
                    required:
                      - block_threshold
            signals:
              type: array
              maxItems: 256
              items:
                type: object
                additionalProperties: false
                required:
                  - type
                  - identifier
                  - outcome
                  - source_id
                  - freshness
                properties:
                  type:
                    type: string
                    minLength: 1
                    maxLength: 64
                    pattern: ^[a-z][a-z0-9_]{0,63}$
                    description: >-
                      The producer emits only registered, active types with a
                      registered identifier kind and outcome.
                    not:
                      description: >-
                        A line feed anywhere, refused with `not` beside every
                        anchored pattern.
                      pattern: \n
                  identifier:
                    enum:
                      - email
                      - username
                      - phone
                      - ip
                    description: >-
                      Which submitted identifier a signal concerns. ip is a
                      selector of breach_exposure only.
                  outcome:
                    enum:
                      - found
                      - not_found
                      - inconclusive
                      - blocked
                      - unavailable
                    description: >-
                      Per-signal outcome. A failure is never not_found: a source
                      that timed out, was blocked or errored reports blocked,
                      unavailable or inconclusive with a reason, and is listed
                      in incomplete_sources.
                  source_id:
                    type: string
                    minLength: 1
                    maxLength: 64
                    pattern: ^[a-z0-9][a-z0-9_.-]{0,63}$
                    description: >-
                      Opaque, workspace-scoped projection of the source that
                      produced a signal. It remains stable for this workspace
                      while the projection key is unchanged, but never
                      identifies an upstream provider, feed, breach table or
                      source record.
                    not:
                      description: >-
                        A line feed anywhere, refused with `not` beside every
                        anchored pattern.
                      pattern: \n
                  reason:
                    enum:
                      - timed_out
                      - rate_limited
                      - blocked
                      - source_error
                      - unavailable
                      - deferred
                      - insufficient_evidence
                    description: >-
                      Why a signal or source did not produce a definite
                      found/not_found. timed_out means the source missed the
                      request deadline; a continuation may still settle it.
                      deferred means the (source, identifier) pair is async-only
                      and was not attempted on the synchronous path; a
                      continuation settles it.
                  evidence_ref:
                    type: string
                    pattern: ^evr_[0-9A-HJKMNP-TV-Z]{26}$
                    description: >-
                      Opaque pointer to evidence NoResponse holds for this
                      signal. It never encodes the evidence itself (no breach
                      value, credential or page body).
                    not:
                      description: >-
                        A line feed anywhere, refused with `not` beside every
                        anchored pattern.
                      pattern: \n
                  observed_at:
                    type: string
                    format: date-time
                    description: When the evidence was first observed.
                  last_checked_at:
                    type: string
                    format: date-time
                    description: When the source was last queried for this identifier.
                  freshness:
                    enum:
                      - fresh
                      - stale
                      - unknown
                    description: >-
                      Freshness class of the evidence behind a signal. unknown
                      whenever no evidence was obtained.
                  enrichment:
                    type: object
                    additionalProperties: false
                    required:
                      - fields
                    properties:
                      fields:
                        type: object
                        minProperties: 1
                        additionalProperties: false
                        properties:
                          phone:
                            type: string
                            maxLength: 8192
                          name:
                            type: string
                            maxLength: 8192
                          carrier:
                            type: string
                            maxLength: 8192
                          line_type:
                            type: string
                            maxLength: 8192
                          entity_type:
                            type: string
                            maxLength: 8192
                          country_code:
                            type: string
                            maxLength: 8192
                          country:
                            type: string
                            maxLength: 8192
                          state:
                            type: string
                            maxLength: 8192
                          city:
                            type: string
                            maxLength: 8192
                          location:
                            type: string
                            maxLength: 8192
                          national_format:
                            type: string
                            maxLength: 8192
                          job_title:
                            type: string
                            maxLength: 8192
                          company_name:
                            type: string
                            maxLength: 8192
                          email:
                            type: string
                            maxLength: 8192
                          website:
                            type: string
                            maxLength: 8192
                          first_call_at:
                            type: string
                            maxLength: 8192
                          last_call_at:
                            type: string
                            maxLength: 8192
                          matched_name:
                            type: string
                            maxLength: 8192
                          alternate_names:
                            type: array
                            maxItems: 1024
                            items:
                              type:
                                - string
                                - number
                                - boolean
                                - object
                                - array
                          name_candidates:
                            type: array
                            maxItems: 1024
                            items:
                              type:
                                - string
                                - number
                                - boolean
                                - object
                                - array
                          timezones:
                            type: array
                            maxItems: 1024
                            items:
                              type:
                                - string
                                - number
                                - boolean
                                - object
                                - array
                          emails:
                            type: array
                            maxItems: 1024
                            items:
                              type:
                                - string
                                - number
                                - boolean
                                - object
                                - array
                          websites:
                            type: array
                            maxItems: 1024
                            items:
                              type:
                                - string
                                - number
                                - boolean
                                - object
                                - array
                          locations:
                            type: array
                            maxItems: 1024
                            items:
                              type:
                                - string
                                - number
                                - boolean
                                - object
                                - array
                          social_profiles:
                            type: array
                            maxItems: 1024
                            items:
                              type:
                                - string
                                - number
                                - boolean
                                - object
                                - array
                          report_count:
                            type: integer
                            minimum: 0
                          tag_count:
                            type: integer
                            minimum: 0
                          total_calls:
                            type: integer
                            minimum: 0
                          calls_60_days:
                            type: integer
                            minimum: 0
                          reports_60_days:
                            type: integer
                            minimum: 0
                          searches_60_days:
                            type: integer
                            minimum: 0
                          comment_count:
                            type: integer
                            minimum: 0
                          names_count:
                            type: integer
                            minimum: 0
                          spam_report_count:
                            type: integer
                            minimum: 0
                    description: >-
                      Only present for a found phone signal. Curated facts about
                      the number, not raw upstream documents and never the
                      provider's identity, scores or verdicts; follows normal
                      record retention/deletion.
                allOf:
                  - if:
                      properties:
                        outcome:
                          const: found
                      required:
                        - outcome
                    then:
                      required:
                        - evidence_ref
                        - observed_at
                        - last_checked_at
                      properties:
                        freshness:
                          enum:
                            - fresh
                            - stale
                      not:
                        required:
                          - reason
                      description: >-
                        A definite presence: evidence exists, so its freshness
                        is known.
                  - if:
                      properties:
                        outcome:
                          const: not_found
                      required:
                        - outcome
                    then:
                      required:
                        - last_checked_at
                      not:
                        anyOf:
                          - required:
                              - reason
                          - required:
                              - evidence_ref
                      description: >-
                        A definite absence. Carries no failure reason: a
                        timeout, block or error is never encoded as not_found.
                  - if:
                      properties:
                        outcome:
                          enum:
                            - inconclusive
                            - blocked
                            - unavailable
                      required:
                        - outcome
                    then:
                      required:
                        - reason
                  - if:
                      properties:
                        outcome:
                          enum:
                            - blocked
                            - unavailable
                      required:
                        - outcome
                    then:
                      properties:
                        freshness:
                          const: unknown
                      not:
                        anyOf:
                          - required:
                              - evidence_ref
                          - required:
                              - observed_at
                      description: >-
                        No evidence was obtained, so there is nothing observed
                        and nothing to reference.
                  - if:
                      required:
                        - enrichment
                    then:
                      properties:
                        identifier:
                          const: phone
                        outcome:
                          const: found
              description: >-
                Detailed view only (response_mode=detailed, which needs the
                detailed scope): a standard answer omits signals[], and a
                detailed one always carries it, possibly empty. At most one
                signal per (type, source_id, identifier).
            incomplete_sources:
              type: array
              maxItems: 256
              items:
                type: object
                additionalProperties: false
                required:
                  - lane
                  - reason
                description: >-
                  One lane that did not settle, and why. It names a lane, never
                  a source: the (source, identifier) pairs behind it stay
                  internal. A lane is listed once. A lane that missed the 10 s
                  lane deadline is timed_out, never not_found.
                properties:
                  lane:
                    enum:
                      - accounts
                      - phone
                      - breach
                      - ip
                    description: >-
                      An evidence lane: accounts (account presence for email and
                      username), phone, breach and ip.
                  reason:
                    enum:
                      - timed_out
                      - rate_limited
                      - blocked
                      - source_error
                      - unavailable
                      - deferred
                    description: >-
                      Why a lane did not settle. insufficient_evidence is a
                      settled verdict (an inconclusive signal), never a reason
                      to list a lane as incomplete. deferred means the lane is
                      async-only and still running; a continuation settles it.
                      timed_out means the lane missed its deadline.
              description: >-
                The lanes that did not settle, each listed once with a reason;
                never a source. In a detailed view a lane is listed exactly when
                one of its signals did not settle; a standard view carries no
                signals to match.
            policy_version:
              type: string
              minLength: 1
              maxLength: 64
              pattern: ^[a-z0-9][a-z0-9._-]{0,63}(\+t[1-9][0-9]{0,8})?$
              description: >-
                Version of the hand-authored heuristic weights that produced the
                recommendation, for example 'risk-2026-10.7'. A workspace
                threshold override adds a +t<N> suffix, e.g.
                'risk-2026-10.7+t3'.
              not:
                description: >-
                  A line feed anywhere, refused with `not` beside every anchored
                  pattern.
                pattern: \n
            policy_basis:
              const: heuristic_unvalidated
            assessment_version:
              type: integer
              minimum: 1
              maximum: 1000
              description: >-
                Monotonic revision of this assessment. 1 on the synchronous
                answer; each material change while the assessment is served (an
                async continuation settling a source, a change of
                recommendation, confidence or status) increments it. Expiry does
                not increment it: an expired assessment is gone, not revised,
                and the expiry webhook carries the last served version.
            created_at:
              type: string
              format: date-time
              description: >-
                When the assessment was created. Fixed for the life of the
                assessment: every revision carries the same created_at.
            updated_at:
              type: string
              format: date-time
            expires_at:
              type: string
              format: date-time
              description: >-
                After this instant the assessment is no longer served: GET
                /v1/assessments/{assessment_id} answers 404 and it leaves the
                account history. It is created_at plus the workspace's retention
                period at creation (default 365 days, at most 365). Fixed per
                record: a later async revision never extends it, and a new
                assessment for the same account never renews an older one. If
                the workspace later shortens retention, the effective expiry
                becomes created_at + min(retention at creation, current
                retention).
            assessment_url:
              type: string
              maxLength: 64
              pattern: ^/v1/assessments/asm_[0-9A-HJKMNP-TV-Z]{26}$
              description: >-
                Server-relative path of the authoritative assessment, GET
                /v1/assessments/{assessment_id}.
              not:
                description: >-
                  A line feed anywhere, refused with `not` beside every anchored
                  pattern.
                pattern: \n
            timing:
              type: object
              additionalProperties: false
              required:
                - deadline_ms
                - elapsed_ms
              properties:
                deadline_ms:
                  type: integer
                  minimum: 50
                  maximum: 3000
                  description: The budget the server applied to the synchronous answer.
                elapsed_ms:
                  type: integer
                  minimum: 0
                  maximum: 600000
                  description: >-
                    Server time from HTTP ingress to this revision of the
                    assessment.
            auth_context:
              type: object
              additionalProperties: false
              required:
                - provider
                - issuer
                - verified
                - transport
              description: >-
                The identity fields of the auth context the assessment was made
                with, echoed for the workspace's own correlation: the request's
                auth_context, or the one a hosted provider hook (POST
                /hooks/{integration_id}) built, with transport provider_hook and
                its hook_point. Present exactly when the request carried an
                auth_context, and the same on every revision of the assessment.
                Values are as the request or the hook sent them; tokens,
                timestamps, roles and scopes are not echoed, and the echo does
                not change the score.
              properties:
                provider:
                  type: string
                  pattern: ^[a-z0-9][a-z0-9._-]{1,31}$
                  description: The provider slug the context named.
                issuer:
                  type: string
                  maxLength: 256
                  pattern: ^https://[^\s]+$
                  description: The issuer the context named.
                subject:
                  type: string
                  maxLength: 256
                  pattern: ^[A-Za-z0-9._:@/|+=~-]{1,256}$
                  description: >-
                    The subject the context named; absent when it named none (a
                    pre_registration hook).
                session_id:
                  type: string
                  maxLength: 256
                  pattern: ^[A-Za-z0-9._:@/|+=~-]{1,256}$
                  description: The session identifier the context named, when it named one.
                organization_id:
                  type: string
                  maxLength: 256
                  pattern: ^[A-Za-z0-9._:@/|+=~-]{1,256}$
                  description: >-
                    The organization identifier the context named, when it named
                    one.
                verified:
                  type: boolean
                  description: >-
                    The verified flag the context carried. A provider_hook
                    context is always false.
                transport:
                  type: string
                  enum:
                    - backend
                    - browser
                    - provider_hook
                  description: >-
                    Where the context was derived: backend (also when the
                    request named no transport), browser, or provider_hook for a
                    hosted provider hook.
                hook_point:
                  type: string
                  enum:
                    - pre_registration
                    - post_registration
                    - pre_login
                    - post_login
                    - token_issuance
                  description: >-
                    The provider event a provider_hook context was sent for;
                    absent for any other transport.
              if:
                properties:
                  transport:
                    const: provider_hook
              then:
                required:
                  - hook_point
                properties:
                  verified:
                    const: false
              else:
                not:
                  required:
                    - hook_point
          allOf:
            - if:
                properties:
                  status:
                    const: complete
                required:
                  - status
              then:
                required:
                  - recommendation
                properties:
                  incomplete_sources:
                    items:
                      properties:
                        reason:
                          not:
                            const: deferred
                    description: >-
                      A complete assessment may list lanes that failed or timed
                      out, never one still running.
                not:
                  required:
                    - assessment_url
            - if:
                properties:
                  status:
                    const: partial
                required:
                  - status
              then:
                required:
                  - recommendation
                properties:
                  incomplete_sources:
                    minItems: 1
            - if:
                properties:
                  status:
                    const: pending
                required:
                  - status
              then:
                required:
                  - assessment_url
                not:
                  anyOf:
                    - required:
                        - recommendation
                    - required:
                        - risk
                properties:
                  confidence:
                    const: unavailable
                  incomplete_sources:
                    minItems: 1
            - if:
                properties:
                  confidence:
                    const: unavailable
                required:
                  - confidence
              then:
                properties:
                  recommendation:
                    const: review
            - if:
                properties:
                  risk:
                    required:
                      - details
                required:
                  - risk
              then:
                required:
                  - signals
    Problem:
      title: Problem details error
      description: >-
        RFC 9457 problem-details error body returned by every non-2xx API
        response, extended with a stable machine code and request id.
      type: object
      additionalProperties: false
      required:
        - type
        - title
        - status
        - detail
        - code
        - request_id
      properties:
        type:
          type: string
          format: uri-reference
        title:
          type: string
        status:
          type: integer
          minimum: 400
          maximum: 599
        detail:
          type: string
        instance:
          type: string
          format: uri-reference
        code:
          type: string
        request_id:
          type: string
          minLength: 1
        invalid_params:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - field
              - reason
            properties:
              field:
                type: string
                minLength: 1
              reason:
                type: string
                minLength: 1
              example:
                type: string
                minLength: 1
  responses:
    AssessmentSettled:
      description: >-
        A complete or partial assessment. Retry-After is present while a source
        is still continuing.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        Idempotent-Replayed:
          description: >-
            Present and true when this answer replays an earlier request with
            the same Idempotency-Key and body: the body is the latest assessment
            of the assessment that request created.
          schema:
            type: string
            const: 'true'
          required: false
        Retry-After:
          description: >-
            Whole seconds to wait before polling again. Present on a partial
            answer while a source is still continuing.
          schema:
            type: string
            pattern: >-
              ^(?:[1-9]|[1-9][0-9]|[1-9][0-9]{2}|[12][0-9]{3}|3[0-5][0-9]{2}|3600)$
            description: Whole decimal seconds; wait at least this long.
          required: false
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Assessment'
              - properties:
                  status:
                    enum:
                      - complete
                      - partial
          examples:
            complete:
              value:
                schema_version: '1'
                id: asm_01JB3C4D5E6F7G8H9JKMNPQRST
                status: complete
                flow: account_creation
                depth: full
                account:
                  schema_version: '1'
                  id: acct_8412
                  risk:
                    score: 0
                    decision: allow
                    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
                  decisions_by_flow:
                    account_creation: allow
                    login: allow
                    purchase: allow
                  assessment_id: asm_01JB3C4D5E6F7G8H9JKMNPQRST
                  updated_at: '2026-10-18T11:00:04.000Z'
                detail:
                  schema_version: '1'
                  assessment_id: asm_01JB3C4D5E6F7G8H9JKMNPQRST
                  account_id: acct_8412
                  request_id: req_01
                  status: complete
                  recommendation: allow
                  confidence: high
                  risk:
                    score: 0
                    band: low
                    recommendation: allow
                    block: false
                    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_workspace: 1
                      consistency:
                        name: match
                        phone: match
                        ip: unknown
                        country: consistent
                    coverage:
                      answered: 14
                      failed: 1
                      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
            partial:
              value:
                schema_version: '1'
                id: asm_01JB3C4D5E6F7G8H9JKMNPQRST
                status: partial
                flow: account_creation
                depth: full
                account:
                  schema_version: '1'
                  id: acct_8412
                  risk:
                    score: 15
                    decision: allow
                    reasons:
                      - code: recent_password_exposure
                        weight: 15
                        identifier: email
                  decisions_by_flow:
                    account_creation: allow
                    login: allow
                    purchase: allow
                  assessment_id: asm_01JB3C4D5E6F7G8H9JKMNPQRST
                  updated_at: '2026-10-18T11:00:10.000Z'
                detail:
                  schema_version: '1'
                  assessment_id: asm_01JB3C4D5E6F7G8H9JKMNPQRST
                  account_id: acct_8412
                  request_id: req_01
                  status: partial
                  recommendation: allow
                  confidence: medium
                  risk:
                    score: 15
                    band: low
                    recommendation: allow
                    block: false
                    confidence: medium
                    policy_version: risk-2026-10.3
                    policy_basis: heuristic_unvalidated
                    reasons:
                      - code: recent_password_exposure
                        weight: 15
                        identifier: email
                    summary:
                      email:
                        disposable: false
                        accounts_found: 1
                        breach:
                          found: true
                          breach_count: 3
                          first_year: 2019
                          last_year: 2025
                          exposed:
                            - email
                            - password
                          recent_password_exposure: true
                          truncated: false
                      username:
                        breach:
                          found: true
                          breach_count: 25
                          truncated: true
                      consistency:
                        name: unknown
                    coverage:
                      answered: 5
                      failed: 1
                      pending: 0
                  incomplete_sources:
                    - lane: accounts
                      reason: timed_out
                  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:10.000Z'
                  expires_at: '2027-10-18T11:00:00.000Z'
                  timing:
                    deadline_ms: 500
                    elapsed_ms: 10000
                  assessment_url: /v1/assessments/asm_01JB3C4D5E6F7G8H9JKMNPQRST
            login:
              value:
                schema_version: '1'
                id: asm_01JB3C4D5E6F7G8H9JKMNPQRST
                status: complete
                flow: login
                depth: quick
                account:
                  schema_version: '1'
                  id: acct_8412
                  risk:
                    score: 0
                    decision: allow
                    reasons:
                      - code: credential_list_exposure
                        weight: 10
                        identifier: email
                      - code: established_accounts
                        weight: -15
                      - code: aged_identity
                        weight: -25
                        identifier: email
                  decisions_by_flow:
                    account_creation: allow
                    login: allow
                    purchase: allow
                  assessment_id: asm_01JB3C4D5E6F7G8H9JKMNPQRST
                  updated_at: '2026-10-18T11:00:00.212Z'
                detail:
                  schema_version: '1'
                  assessment_id: asm_01JB3C4D5E6F7G8H9JKMNPQRST
                  account_id: acct_8412
                  request_id: req_01
                  status: complete
                  recommendation: allow
                  confidence: high
                  risk:
                    score: 0
                    band: low
                    recommendation: allow
                    block: false
                    confidence: high
                    policy_version: risk-2026-10.3
                    policy_basis: heuristic_unvalidated
                    reasons:
                      - code: credential_list_exposure
                        weight: 10
                        identifier: email
                      - code: established_accounts
                        weight: -15
                      - code: aged_identity
                        weight: -25
                        identifier: email
                    summary:
                      email:
                        disposable: false
                        accounts_found: 4
                        breach:
                          found: true
                          breach_count: 5
                          first_year: 2014
                          last_year: 2022
                          exposed:
                            - email
                            - name
                            - password_hash
                          in_credential_lists: true
                          recent_password_exposure: false
                          truncated: false
                    coverage:
                      answered: 6
                      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.212Z'
                  expires_at: '2027-10-18T11:00:00.000Z'
                  timing:
                    deadline_ms: 500
                    elapsed_ms: 212
            detailed:
              value:
                schema_version: '1'
                id: asm_01JB3C4D5E6F7G8H9JKMNPQRST
                status: complete
                flow: account_creation
                depth: full
                account:
                  schema_version: '1'
                  id: acct_8412
                  risk:
                    score: 10
                    decision: allow
                    reasons:
                      - code: voip_number
                        weight: 25
                        identifier: phone
                      - code: breach_name_match
                        weight: -15
                        identifier: name
                  decisions_by_flow:
                    account_creation: allow
                    login: allow
                    purchase: allow
                  assessment_id: asm_01JB3C4D5E6F7G8H9JKMNPQRST
                  updated_at: '2026-10-18T11:00:04.000Z'
                detail:
                  schema_version: '1'
                  assessment_id: asm_01JB3C4D5E6F7G8H9JKMNPQRST
                  account_id: acct_8412
                  request_id: req_01
                  status: complete
                  recommendation: allow
                  confidence: high
                  risk:
                    score: 10
                    band: low
                    recommendation: allow
                    block: false
                    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: Jane Doe
                          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: Jane Doe
                          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
    AssessmentPending:
      description: >-
        Nothing usable before the deadline. No recommendation; poll Location or
        wait for assessment.updated.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        Idempotent-Replayed:
          description: >-
            Present and true when this answer replays an earlier request with
            the same Idempotency-Key and body: the body is the latest assessment
            of the assessment that request created.
          schema:
            type: string
            const: 'true'
          required: false
        Location:
          description: The polling URI of the assessment, /v1/assessments/{assessment_id}.
          schema:
            type: string
            maxLength: 64
            pattern: ^/v1/assessments/asm_[0-9A-HJKMNP-TV-Z]{26}$
            description: >-
              Server-relative path of the authoritative assessment, GET
              /v1/assessments/{assessment_id}.
            not:
              description: >-
                A line feed anywhere, refused with `not` beside every anchored
                pattern.
              pattern: \n
          required: true
        Retry-After:
          description: >-
            Whole seconds to wait before polling or retrying. Required on 202,
            429 and 503.
          schema:
            type: string
            pattern: >-
              ^(?:[1-9]|[1-9][0-9]|[1-9][0-9]{2}|[12][0-9]{3}|3[0-5][0-9]{2}|3600)$
            description: Whole decimal seconds; wait at least this long.
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Assessment'
              - properties:
                  status:
                    const: pending
          examples:
            pending:
              value:
                schema_version: '1'
                id: asm_01JA2Z8K6Q4V7W9XBCDEFGHJKM
                status: pending
                flow: account_creation
                depth: full
                detail:
                  schema_version: '1'
                  assessment_id: asm_01JA2Z8K6Q4V7W9XBCDEFGHJKM
                  account_id: acct_7f3a9c
                  request_id: req-2026-10-14-0001
                  policy_version: heuristic-2026-10.1
                  policy_basis: heuristic_unvalidated
                  assessment_version: 2
                  created_at: '2026-10-14T09:00:00.000Z'
                  updated_at: '2026-10-14T09:00:00.412Z'
                  expires_at: '2027-10-14T09:00:00.000Z'
                  timing:
                    deadline_ms: 300
                    elapsed_ms: 301
                  status: pending
                  confidence: unavailable
                  incomplete_sources:
                    - lane: accounts
                      reason: timed_out
                    - lane: breach
                      reason: timed_out
                  assessment_url: /v1/assessments/asm_01JA2Z8K6Q4V7W9XBCDEFGHJKM
            pending_known_account:
              value:
                schema_version: '1'
                id: asm_01JA2Z8K6Q4V7W9XBCDEFGHJKM
                status: pending
                flow: purchase
                depth: full
                account:
                  schema_version: '1'
                  id: acct_7f3a9c
                  risk:
                    score: 0
                    decision: allow
                    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
                  decisions_by_flow:
                    account_creation: allow
                    login: allow
                    purchase: allow
                  assessment_id: asm_01JA1Y7J5P3T6V8WABCDEFGHJK
                  updated_at: '2026-10-12T16:20:03.000Z'
                detail:
                  schema_version: '1'
                  assessment_id: asm_01JA2Z8K6Q4V7W9XBCDEFGHJKM
                  account_id: acct_7f3a9c
                  request_id: req-2026-10-14-0001
                  policy_version: heuristic-2026-10.1
                  policy_basis: heuristic_unvalidated
                  assessment_version: 2
                  created_at: '2026-10-14T09:00:00.000Z'
                  updated_at: '2026-10-14T09:00:00.412Z'
                  expires_at: '2027-10-14T09:00:00.000Z'
                  timing:
                    deadline_ms: 300
                    elapsed_ms: 301
                  status: pending
                  confidence: unavailable
                  incomplete_sources:
                    - lane: accounts
                      reason: timed_out
                    - lane: breach
                      reason: timed_out
                  assessment_url: /v1/assessments/asm_01JA2Z8K6Q4V7W9XBCDEFGHJKM
    InvalidRequest:
      description: >-
        The body is not JSON or does not match the assessment request schema
        (unknown member such as tenant_id, no identifier, malformed or oversized
        identifier, an exact secret shape, deadline_ms above 3000), or
        Idempotency-Key is missing or malformed. invalid_params names the
        offending fields, never their values.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 400
                  code:
                    enum:
                      - invalid_request
                      - invalid_idempotency_key
          examples:
            deadline:
              value:
                type: https://docs.noresponse.org/problems/invalid-request
                title: Invalid request
                status: 400
                detail: The body does not match the assessment request schema.
                code: invalid_request
                request_id: 0123456789abcdef0123456789abcdef
                invalid_params:
                  - field: deadline_ms
                    reason: must be at most 500
    Unauthorized:
      description: No API key, or one that is malformed, unknown, revoked or expired.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        WWW-Authenticate:
          schema:
            type: string
            const: Bearer realm="noresponse-assessments"
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 401
                  code:
                    enum:
                      - unauthorized
    WorkspaceForbidden:
      description: >-
        The API key is valid but may not use this operation: it lacks the
        operation's scope, or its workspace is not entitled or is suspended.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 403
                  code:
                    enum:
                      - workspace_forbidden
          examples:
            missingScope:
              value:
                type: https://docs.noresponse.org/problems/workspace-forbidden
                title: Forbidden
                status: 403
                detail: The API key does not carry a scope this operation accepts.
                code: workspace_forbidden
                request_id: 0123456789abcdef0123456789abcdef
    IdempotencyConflict:
      description: >-
        The Idempotency-Key was already used by this workspace, within 24 hours,
        with a different body. A reuse with the same body is never a conflict,
        even while the first request is in flight: it replays the latest
        assessment.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 409
                  code:
                    enum:
                      - idempotency_key_reused
          examples:
            reused:
              value:
                type: https://docs.noresponse.org/problems/idempotency-key-reused
                title: Idempotency key reused
                status: 409
                detail: This Idempotency-Key was used with a different request body.
                code: idempotency_key_reused
                request_id: 0123456789abcdef0123456789abcdef
    PayloadTooLarge:
      description: The body exceeds 32768 bytes. Refused before parsing.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 413
                  code:
                    enum:
                      - payload_too_large
    UnsupportedMediaType:
      description: Content-Type is not application/json.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 415
                  code:
                    enum:
                      - unsupported_media_type
    RateLimited:
      description: >-
        The workspace's rate or concurrency limit is exhausted. Retry-After says
        when to retry; the provider applies its own fallback meanwhile.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        Retry-After:
          description: >-
            Whole seconds to wait before polling or retrying. Required on 202,
            429 and 503.
          schema:
            type: string
            pattern: >-
              ^(?:[1-9]|[1-9][0-9]|[1-9][0-9]{2}|[12][0-9]{3}|3[0-5][0-9]{2}|3600)$
            description: Whole decimal seconds; wait at least this long.
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 429
                  code:
                    enum:
                      - rate_limited
          examples:
            limited:
              value:
                type: https://docs.noresponse.org/problems/rate-limited
                title: Rate limited
                status: 429
                detail: >-
                  The tenant's assessment rate is exhausted; retry after the
                  Retry-After interval.
                code: rate_limited
                request_id: 0123456789abcdef0123456789abcdef
    ServiceUnavailable:
      description: >-
        The assessment could not be admitted at all (overload, shutdown or a
        dependency down). An assessment that was admitted answers partial or
        pending instead of 503.
      headers:
        Cache-Control:
          schema:
            type: string
            const: no-store
          required: true
        Retry-After:
          description: >-
            Whole seconds to wait before polling or retrying. Required on 202,
            429 and 503.
          schema:
            type: string
            pattern: >-
              ^(?:[1-9]|[1-9][0-9]|[1-9][0-9]{2}|[12][0-9]{3}|3[0-5][0-9]{2}|3600)$
            description: Whole decimal seconds; wait at least this long.
          required: true
        X-Request-ID:
          description: The request correlation id, 32 lowercase hexadecimal characters.
          schema:
            type: string
            pattern: ^[0-9a-f]{32}$
            not:
              pattern: \n
          required: true
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - properties:
                  status:
                    const: 503
                  code:
                    enum:
                      - service_unavailable
  securitySchemes:
    tenantBearer:
      type: http
      scheme: bearer
      description: >-
        Your API key, sent as `Authorization: Bearer <key>`. A key is
        `nrk_live_` or `nrk_test_`, a 26-character key id, `_` and a
        43-character secret. A key sees only its own environment's data.


        Each key has one or more scopes: `assess` creates and reads assessments,
        records outcomes and reports key usage; `history` reads assessments, an
        account's current risk and an account's history, and erases an account;
        `detailed` adds the detailed view; `webhooks` registers, tests, rotates
        and deletes webhook endpoints; `integrations` registers and manages
        hosted provider integrations. A missing, unknown, revoked or expired key
        answers 401. A valid key without the scope an operation needs answers
        403.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.