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

# Verify and assess risk

> Required scope: `kyc:verify`.

The verification verdict plus the risk assessment built on it.

Guide: [Risk assessment](/guides/risk-assessment).



## OpenAPI

````yaml /openapi.json post /v1/kyc/assess
openapi: 3.1.0
info:
  title: Sahl Partner API
  version: 0.2.0
  description: >-
    Server-to-server API for lenders and banks: document reading, verification,
    risk assessment and eID checks. Authenticate with `Authorization: Bearer
    <API key>`. Paths are relative to the server URL. New workspaces start in
    sandbox.
servers:
  - url: https://app.sahlfinancial.com/api
    description: Single API host. Send environment=sandbox (the default) for test data.
security:
  - bearerAuth: []
tags:
  - name: KYC
    description: Read documents, verify a profile, assess risk, start an eID check.
  - name: Bank connections (Growth)
    description: >-
      Growth plan, switched on per workspace by Sahl. Sandbox simulation today;
      production answers 409 until a bank data provider is live.
paths:
  /v1/kyc/assess:
    post:
      tags:
        - KYC
      summary: Verify and assess risk
      description: |-
        Required scope: `kyc:verify`.

        The verification verdict plus the risk assessment built on it.

        Guide: [Risk assessment](/guides/risk-assessment).
      operationId: assess
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProfileRequest'
            examples:
              answers:
                summary: Profile with the suitability answers
                description: >-
                  Risk tolerance needs objective, horizon, investment_knowledge
                  and investment_experience. Capacity needs at least one of
                  annual_income, net_liquid_assets, total_net_worth.
                value:
                  reference: client-0001
                  environment: sandbox
                  subject: Test Client
                  kind: individual
                  require_documents: false
                  values:
                    first_name: Test
                    last_name: Client
                    country: MA
                    citizenship: MA
                    annual_income: '84000'
                    net_liquid_assets: '20000'
                    total_net_worth: '60000'
                    objective: Balanced
                    horizon: 5-10 years
                    investment_knowledge: Good
                    investment_experience: < 5 years
                  documents: []
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AssessResponse'
              example:
                verification:
                  passed: true
                  checks:
                    - id: legible:national_id
                      label: national_id — key fields readable
                      severity: warning
                      passed: true
                      detail: ''
                    - id: expiry:national_id
                      label: national_id — not expired
                      severity: critical
                      passed: true
                      detail: ''
                    - id: format:cin:national_id
                      label: national_id — CIN number is well-formed
                      severity: warning
                      passed: true
                      detail: ''
                    - id: adult:national_id
                      label: national_id — holder is 18+
                      severity: critical
                      passed: true
                      detail: ''
                    - id: required:photo_id
                      label: Document establishing the account holder provided
                      severity: critical
                      passed: true
                      detail: ''
                    - id: expiry:recorded:id_expiry
                      label: The identity document on file is not expired
                      severity: critical
                      passed: true
                      detail: ''
                    - id: screening
                      label: Sanctions screening — no matches; PEP not list-screened
                      severity: info
                      passed: true
                      detail: >-
                        screened against 23000 sanctions entries. The bundle
                        carries no PEP list, so politically-exposed status rests
                        on the client's declaration, not on a list check.
                    - id: completeness
                      label: KYC/KYB data completeness (61%)
                      severity: warning
                      passed: false
                      detail: >-
                        missing 11 required data point(s): street1, city,
                        province, postal_code, phone, email, source_of_funds,
                        account_type
                  critical_failures: []
                  flags:
                    - id: completeness
                      label: KYC/KYB data completeness (61%)
                      severity: warning
                      passed: false
                      detail: >-
                        missing 11 required data point(s): street1, city,
                        province, postal_code, phone, email, source_of_funds,
                        account_type
                  completeness:
                    required: 28
                    present: 17
                    missing:
                      - street1
                      - city
                      - province
                      - postal_code
                      - phone
                      - email
                      - source_of_funds
                      - account_type
                      - third_party
                      - sin/ssn
                      - pep_foreign/pep_domestic/pep_hio/pep
                    percent: 61
                  policy:
                    id: null
                    version: 0
                    source: legacy
                    regime: none
                    regulator: null
                    purpose: onboarding
                    overrides_refused: []
                assessment:
                  risk_profile:
                    score: 58
                    band: Balanced
                    missing: []
                  capacity:
                    score: 20
                    band: Low
                    missing: []
                  compliance_risk:
                    level: Low
                    score: 1
                    factors:
                      - >-
                        Flag: KYC/KYB data completeness (61%) (missing 11
                        required data point(s): street1, city, province,
                        postal_code, phone, email, source_of_funds,
                        account_type)
                  suitability: Suitable
                  risk_level: Balanced
                  verification:
                    passed: true
                    checks:
                      - id: legible:national_id
                        label: national_id — key fields readable
                        severity: warning
                        passed: true
                        detail: ''
                      - id: expiry:national_id
                        label: national_id — not expired
                        severity: critical
                        passed: true
                        detail: ''
                      - id: format:cin:national_id
                        label: national_id — CIN number is well-formed
                        severity: warning
                        passed: true
                        detail: ''
                      - id: adult:national_id
                        label: national_id — holder is 18+
                        severity: critical
                        passed: true
                        detail: ''
                      - id: required:photo_id
                        label: Document establishing the account holder provided
                        severity: critical
                        passed: true
                        detail: ''
                      - id: expiry:recorded:id_expiry
                        label: The identity document on file is not expired
                        severity: critical
                        passed: true
                        detail: ''
                      - id: screening
                        label: >-
                          Sanctions screening — no matches; PEP not
                          list-screened
                        severity: info
                        passed: true
                        detail: >-
                          screened against 23000 sanctions entries. The bundle
                          carries no PEP list, so politically-exposed status
                          rests on the client's declaration, not on a list
                          check.
                      - id: completeness
                        label: KYC/KYB data completeness (61%)
                        severity: warning
                        passed: false
                        detail: >-
                          missing 11 required data point(s): street1, city,
                          province, postal_code, phone, email, source_of_funds,
                          account_type
                    critical_failures: []
                    flags:
                      - id: completeness
                        label: KYC/KYB data completeness (61%)
                        severity: warning
                        passed: false
                        detail: >-
                          missing 11 required data point(s): street1, city,
                          province, postal_code, phone, email, source_of_funds,
                          account_type
                    completeness:
                      required: 28
                      present: 17
                      missing:
                        - street1
                        - city
                        - province
                        - postal_code
                        - phone
                        - email
                        - source_of_funds
                        - account_type
                        - third_party
                        - sin/ssn
                        - pep_foreign/pep_domestic/pep_hio/pep
                      percent: 61
                registry: null
                case_id: 00000000-0000-4000-8000-000000000001
        '401':
          description: Missing, invalid, revoked or expired API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail: Invalid or revoked API key
        '403':
          description: >-
            Key lacks the scope, or the workspace is not enabled for the partner
            KYC API.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                detail:
                  code: kyc_scope_not_allowed
                  message: This workspace is not enabled for the partner KYC API.
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
        '429':
          description: >-
            Per-IP rate limit: 100 requests a minute on these routes. Carries
            `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorFlat'
              example:
                code: rate_limit_exceeded
                message: Too many requests. Please slow down.
        '500':
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorFlat'
              example:
                code: internal_error
                message: An unexpected error occurred
                details: null
      security:
        - bearerAuth: []
components:
  schemas:
    ProfileRequest:
      properties:
        reference:
          anyOf:
            - type: string
              maxLength: 64
              minLength: 1
              pattern: ^[A-Za-z0-9_.:-]+$
            - type: 'null'
          title: Reference
          description: >-
            Your own id for the client (see `/extract`). With one, the verdict
            is filed on the case for (workspace, environment, reference) and the
            answer carries `case_id`. Documents whose `document_id` came from
            `/extract` are linked to it.
        environment:
          type: string
          enum:
            - sandbox
            - production
          title: Environment
          default: sandbox
          description: '`sandbox` (default) or `production`.'
        subject:
          anyOf:
            - type: string
              maxLength: 255
            - type: 'null'
          title: Subject
          description: >-
            Client name (a person) or legal name (an entity), for the case. Up
            to 255 characters.
        values:
          additionalProperties: true
          type: object
          title: Values
          description: >-
            The client profile, as an object of field keys to values: names,
            date_of_birth, address, id_type, id_number, id_expiry, occupation,
            income and the other data points. Unknown keys are ignored.
        documents:
          items:
            additionalProperties: true
            type: object
          type: array
          title: Documents
          description: >-
            The `documents` entries that `/extract` returned, sent back
            unchanged.
        kind:
          anyOf:
            - type: string
            - type: 'null'
          title: Kind
          description: >-
            Client kind: `individual`, `corporation`, `partnership`,
            `charitable_org`, `trust`, `estate` (aliases: entity, business, kyb,
            charity, fiducie, societe, succession). Decides which document
            establishes the account holder. Null falls back on `entity`.
        entity:
          type: boolean
          title: Entity
          default: false
          description: True for a business (KYB). Used when `kind` is null.
        require_documents:
          type: boolean
          title: Require Documents
          default: true
          description: >-
            Ask for the identity document. False is honoured only while the
            workspace policy does not lock identity verification, or on a
            periodic review. A refused switch is listed in
            `policy.overrides_refused`.
        screen:
          type: boolean
          title: Screen
          default: true
          description: >-
            Sanctions screening of every party on the file. False is honoured
            only while the policy does not lock the sanctions screen.
        canadian_screening:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Canadian Screening
          description: >-
            Extra Canadian AML and PEP screening for a Canadian client. Null
            means the workspace policy decides. It needs eID provider
            credentials on the workspace.
        extra_checks:
          items:
            $ref: '#/components/schemas/CheckIn'
          type: array
          title: Extra Checks
          description: >-
            Checks only you can run (a duplicate client, your own blocklist).
            They are added to the verdict and can block it. An id starting
            `eid:` or `policy:` comes back prefixed `partner:`.
        purpose:
          type: string
          enum:
            - onboarding
            - periodic_review
          title: Purpose
          default: onboarding
          description: >-
            `onboarding` (default) or `periodic_review`. A review does not
            re-verify identity; screening and every other lock still apply.
      type: object
      title: ProfileRequest
      description: A profile and the per-file extractions that back it.
    AssessResponse:
      type: object
      title: AssessResponse
      properties:
        verification:
          type: object
          properties:
            passed:
              type: boolean
              description: True when no critical check failed. Warnings do not change it.
            checks:
              type: array
              items:
                $ref: '#/components/schemas/Check'
            critical_failures:
              type: array
              items:
                $ref: '#/components/schemas/Check'
              description: 'Checks with `passed: false` and severity `critical`.'
            flags:
              type: array
              items:
                $ref: '#/components/schemas/Check'
              description: >-
                Everything that needs a person: failed critical and warning
                checks.
            completeness:
              $ref: '#/components/schemas/Completeness'
            policy:
              $ref: '#/components/schemas/PolicyBlock'
          required:
            - passed
            - checks
            - critical_failures
            - flags
            - completeness
            - policy
        assessment:
          $ref: '#/components/schemas/Assessment'
        registry:
          type:
            - object
            - 'null'
          additionalProperties: true
        case_id:
          type: string
          format: uuid
          description: Only with a `reference`.
      required:
        - verification
        - assessment
        - registry
    Error:
      type: object
      title: Error
      description: >-
        FastAPI error body. `detail` is a string, or an object with `code` and
        `message` (extra keys possible).
      properties:
        detail:
          oneOf:
            - type: string
            - type: object
              additionalProperties: true
              properties:
                code:
                  type: string
                message:
                  type: string
      required:
        - detail
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    ErrorFlat:
      type: object
      title: ErrorFlat
      description: >-
        Body of a rate-limit 429, an unknown route 404 and a 500: `code` and
        `message` at the top level, with no `detail`.
      properties:
        code:
          type: string
        message:
          type: string
        details:
          type:
            - string
            - 'null'
          description: Present on a 500 only; null unless the server runs in debug mode.
      required:
        - code
        - message
    CheckIn:
      properties:
        id:
          type: string
          title: Id
        label:
          type: string
          title: Label
        severity:
          type: string
          pattern: ^(critical|warning|info)$
          title: Severity
          description: '`critical`, `warning` or `info`.'
        passed:
          type: boolean
          title: Passed
        detail:
          type: string
          title: Detail
          default: ''
      type: object
      required:
        - id
        - label
        - severity
        - passed
      title: CheckIn
      description: A check you ran yourself, added to the verdict.
    Check:
      type: object
      title: Check
      description: >-
        One verification result. `passed: false` with severity `critical` blocks
        the file; `warning` is a flag for a person; `info` is kept for the audit
        trail.
      properties:
        id:
          type: string
          description: >-
            Stable id, for example `expiry:national_id`. Some ids end in a
            document label or a party name.
        label:
          type: string
          description: Human sentence.
        severity:
          type: string
          enum:
            - critical
            - warning
            - info
        passed:
          type: boolean
        detail:
          type: string
          description: Why it failed. Empty when it passed.
      required:
        - id
        - label
        - severity
        - passed
        - detail
    Completeness:
      type: object
      title: Completeness
      description: >-
        How many of the required data points for this client kind are present in
        `values`. The `completeness` check is a warning below 80 percent.
      properties:
        required:
          type: integer
        present:
          type: integer
        missing:
          type: array
          items:
            type: string
          description: Field keys. A group such as `sin/ssn` counts once.
        percent:
          type: integer
      required:
        - required
        - present
        - missing
        - percent
    PolicyBlock:
      type: object
      title: PolicyBlock
      description: >-
        Which workspace policy the call ran under, and which request switches it
        refused.
      properties:
        id:
          type:
            - string
            - 'null'
          format: uuid
          description: Saved policy id, or null when none is saved.
        version:
          type: integer
        source:
          type: string
          enum:
            - tenant
            - preset
            - legacy
            - fallback
          description: >-
            `tenant` a saved policy, `preset` a regime preset, `legacy` the
            default, `fallback` a saved policy that no longer validates.
        regime:
          type: string
        regulator:
          type:
            - string
            - 'null'
        purpose:
          type: string
          enum:
            - onboarding
            - periodic_review
        overrides_refused:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              requested:
                type: boolean
              enforced:
                type: boolean
              locked_item:
                type: string
      required:
        - id
        - version
        - source
        - regime
        - purpose
        - overrides_refused
    Assessment:
      type: object
      title: Assessment
      properties:
        risk_profile:
          type: object
          properties:
            score:
              type:
                - integer
                - 'null'
              description: 0 to 100. Null when the answers it needs are missing.
            band:
              type:
                - string
                - 'null'
              enum:
                - Conservative
                - Moderate
                - Balanced
                - Growth
                - Aggressive
                - null
            missing:
              type: array
              items:
                type: string
              description: Inputs that were not provided.
          description: >-
            Risk tolerance from objective, horizon, investment_knowledge and
            investment_experience. Withheld (null) unless all four are answered.
        capacity:
          type: object
          properties:
            score:
              type:
                - integer
                - 'null'
              description: 0 to 100. Null when the answers it needs are missing.
            band:
              type:
                - string
                - 'null'
              enum:
                - Low
                - Moderate
                - High
                - Very High
                - null
            missing:
              type: array
              items:
                type: string
              description: Inputs that were not provided.
          description: >-
            Financial capacity from annual_income, net_liquid_assets and
            total_net_worth. Withheld (null) when none of the three is present.
        compliance_risk:
          type: object
          properties:
            level:
              type: string
              enum:
                - Low
                - Medium
                - High
            score:
              type: integer
              description: Risk points.
            factors:
              type: array
              items:
                type: string
          required:
            - level
            - score
            - factors
        suitability:
          type: string
          description: >-
            One of: `Blocked — document verification failed`, `Review —
            objective exceeds capacity`, `Enhanced due diligence required`,
            `Incomplete — suitability answers missing`, `Incomplete — financial
            capacity answers missing`, `Suitable`.
        risk_level:
          type:
            - string
            - 'null'
          description: The `risk_profile` band.
        verification:
          type: object
          additionalProperties: true
          description: >-
            The verdict the assessment was built on (same as `verification` at
            the top level, without `policy`).
      required:
        - risk_profile
        - capacity
        - compliance_risk
        - suitability
        - risk_level
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        API key created in the console. Scopes: kyc:extract, kyc:verify,
        kyc:eid; bank:read, bank:write (Growth plan, enabled per workspace by
        Sahl).

````

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