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

# Screen a Subject

> Screen a person or business against global sanctions and denied-party watchlists.

### Act on `decision`, not on `topScore`

`decision` is the integration contract. Branch on it and nothing else:

| `decision` | Meaning | What you should do |
|---|---|---|
| `clear` | Nothing matched | Proceed |
| `review` | A possible match a human must adjudicate | Hold the subject |
| `blocked` | A confident match to an asset-freeze list | Hold and escalate |
| `unavailable` | Screening could not complete | **Not screened.** Retry or hold — never treat as clear |

### Why not the score

`topScore` is a **name-similarity score, not a risk score**, and it is returned for
diagnostics only — show it to your reviewers to help them triage, but do not build
thresholds on it. Four reasons, all measured against live watchlist data:

- **It does not separate real from innocent.** A true OFAC SDN entry scores `0.8122` on
  name alone; an innocent person with a common name scores `0.7513`. Six points apart —
  no threshold sits safely between them.
- **It is not comparable across subjects.** The score carries a coverage penalty — a
  name-only query is structurally capped however perfect the name, while a query with a
  date of birth is not. The same SDN entry scores `0.8122` on name alone and `0.9025` with
  a date of birth. Same person, same list entry, different number. A threshold applied
  across a mixed population applies different standards to different customers.
- **It is not stable over time.** That same entry scored `0.8762` on 2026-07-14 and
  `0.9025` on 2026-07-16 — same subject, same query, same software. The watchlists
  themselves refresh (OFAC gained 61 entries in those two days) and scores move with the
  data. Lists reload every 12 hours. A hard-coded `>= 0.90` can change behaviour twice a
  day, with no release on your side.
- **Legal effect follows the list, not the number.** A `0.95` match on a sectoral list is
  a transaction restriction; a `0.82` match on a freeze list is an asset freeze. Ranking
  those by score is backwards.

`decision` absorbs all four. It is the only part of this response we treat as a stable
contract.

`decision` already folds in the score, the list's legal effect, how much identity data was
available, and exact government-ID matches — so you do not have to.

### Send more than a name

A `dateOfBirth` (people) or `address` (businesses) is what makes the answer trustworthy.
`dataCompleteness` in the response tells you what you gave us: `full`, `partial`, or
`name_only`. A `name_only` check is deliberately never auto-blocked, because at that
coverage the score cannot carry the weight. If you see `name_only`, your integration is
leaving accuracy on the table.

### `confirmWith` — the one thing to send next

When a check comes back `review` or `blocked`, `confirmWith` asks for **exactly one field**:
the strongest piece of identity data you have not already sent. `key` is the request field
to send it in, so the ask is directly actionable — populate it, POST a new check, and if
that still hits you are asked for the next one. Anything already in hand is never asked for
twice, whatever order it arrived in.

For a **person** the ladder is `dateOfBirth` → `governmentIds` → `altNames` → `address`; for
a **business**, `address` → `governmentIds` → `altNames` → `incorporationDate`. So a check
carrying only a name is asked for a date of birth, while one that already carried a
government ID skips straight past it:

| Your check carries | `confirmWith` asks for |
|---|---|
| name | `dateOfBirth` |
| name + `governmentIds` | `dateOfBirth` |
| name + `governmentIds` + `dateOfBirth` | `altNames` |
| the whole ladder | *(empty — nothing left to send)* |

`label` is the same wording our compliance desk would use for that field, so it is safe to
show verbatim to whoever supplies the data.

Two things the ordering is honest about. A **government ID** is the strongest confirmation
available — an exact match to a listed party's identifier is not a coincidence — but the
effect is one-way: an ID that *doesn't* match does not clear anything by itself, it gives a
reviewer the grounds to. That is why a **date of birth** outranks it for a person: it changes
what the search returns rather than only how a returned match is judged.

`confirmWith` is empty on `clear`, and empty on `unavailable` — that check never ran, so more
identity data does not help; retry it instead. An empty array on a hit means the check is
already as strong as this API can make it and the remaining question needs a human.

Each successful check is billed once on your monthly invoice. `unavailable` checks are not
billed.



## OpenAPI

````yaml POST /v1/platform/screening/checks
openapi: 3.1.0
info:
  title: Finogate Platform API v1
  version: 0.1.0
servers:
  - url: https://api-sandbox.finogates.com
    description: Sandbox
  - url: https://api.finogates.com
    description: Production
security: []
tags:
  - name: auth
  - name: users
  - name: payees
  - name: verification
  - name: bank accounts
  - name: card processing
  - name: digital-assets
  - name: international
  - name: fees
  - name: onboarding
  - name: pending-applications
  - name: wallets
  - name: payments
  - name: document mailing
  - name: merchant onboarding
  - name: catalog
  - name: webhooks
  - name: screening
  - name: Platform - File Scanning
  - name: phone-numbers
  - name: sms
  - name: calls
  - name: listings
  - name: Card Transfers
  - name: embed-tokens
paths:
  /v1/platform/screening/checks:
    post:
      tags:
        - screening
      summary: Screen a subject
      description: >-
        Screen a person or business against global sanctions and denied-party
        watchlists.


        ### Act on `decision`, not on `topScore`


        `decision` is the integration contract. Branch on it and nothing else:


        | `decision` | Meaning | What you should do |

        |---|---|---|

        | `clear` | Nothing matched | Proceed |

        | `review` | A possible match a human must adjudicate | Hold the subject
        |

        | `blocked` | A confident match to an asset-freeze list | Hold and
        escalate |

        | `unavailable` | Screening could not complete | **Not screened.** Retry
        or hold — never treat as clear |


        ### Why not the score


        `topScore` is a **name-similarity score, not a risk score**, and it is
        returned for

        diagnostics only — show it to your reviewers to help them triage, but do
        not build

        thresholds on it. Four reasons, all measured against live watchlist
        data:


        - **It does not separate real from innocent.** A true OFAC SDN entry
        scores `0.8122` on
          name alone; an innocent person with a common name scores `0.7513`. Six points apart —
          no threshold sits safely between them.
        - **It is not comparable across subjects.** The score carries a coverage
        penalty — a
          name-only query is structurally capped however perfect the name, while a query with a
          date of birth is not. The same SDN entry scores `0.8122` on name alone and `0.9025` with
          a date of birth. Same person, same list entry, different number. A threshold applied
          across a mixed population applies different standards to different customers.
        - **It is not stable over time.** That same entry scored `0.8762` on
        2026-07-14 and
          `0.9025` on 2026-07-16 — same subject, same query, same software. The watchlists
          themselves refresh (OFAC gained 61 entries in those two days) and scores move with the
          data. Lists reload every 12 hours. A hard-coded `>= 0.90` can change behaviour twice a
          day, with no release on your side.
        - **Legal effect follows the list, not the number.** A `0.95` match on a
        sectoral list is
          a transaction restriction; a `0.82` match on a freeze list is an asset freeze. Ranking
          those by score is backwards.

        `decision` absorbs all four. It is the only part of this response we
        treat as a stable

        contract.


        `decision` already folds in the score, the list's legal effect, how much
        identity data was

        available, and exact government-ID matches — so you do not have to.


        ### Send more than a name


        A `dateOfBirth` (people) or `address` (businesses) is what makes the
        answer trustworthy.

        `dataCompleteness` in the response tells you what you gave us: `full`,
        `partial`, or

        `name_only`. A `name_only` check is deliberately never auto-blocked,
        because at that

        coverage the score cannot carry the weight. If you see `name_only`, your
        integration is

        leaving accuracy on the table.


        ### `confirmWith` — the one thing to send next


        When a check comes back `review` or `blocked`, `confirmWith` asks for
        **exactly one field**:

        the strongest piece of identity data you have not already sent. `key` is
        the request field

        to send it in, so the ask is directly actionable — populate it, POST a
        new check, and if

        that still hits you are asked for the next one. Anything already in hand
        is never asked for

        twice, whatever order it arrived in.


        For a **person** the ladder is `dateOfBirth` → `governmentIds` →
        `altNames` → `address`; for

        a **business**, `address` → `governmentIds` → `altNames` →
        `incorporationDate`. So a check

        carrying only a name is asked for a date of birth, while one that
        already carried a

        government ID skips straight past it:


        | Your check carries | `confirmWith` asks for |

        |---|---|

        | name | `dateOfBirth` |

        | name + `governmentIds` | `dateOfBirth` |

        | name + `governmentIds` + `dateOfBirth` | `altNames` |

        | the whole ladder | *(empty — nothing left to send)* |


        `label` is the same wording our compliance desk would use for that
        field, so it is safe to

        show verbatim to whoever supplies the data.


        Two things the ordering is honest about. A **government ID** is the
        strongest confirmation

        available — an exact match to a listed party's identifier is not a
        coincidence — but the

        effect is one-way: an ID that *doesn't* match does not clear anything by
        itself, it gives a

        reviewer the grounds to. That is why a **date of birth** outranks it for
        a person: it changes

        what the search returns rather than only how a returned match is judged.


        `confirmWith` is empty on `clear`, and empty on `unavailable` — that
        check never ran, so more

        identity data does not help; retry it instead. An empty array on a hit
        means the check is

        already as strong as this API can make it and the remaining question
        needs a human.


        Each successful check is billed once on your monthly invoice.
        `unavailable` checks are not

        billed.
      operationId: create_check_v1_platform_screening_checks_post
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ScreeningCheckCreate'
      responses:
        '200':
          description: >-
            The screening result. This example shows a `review` — a real match
            that a human must confirm, and the shape most genuine hits arrive
            in. The `topScore` shown is illustrative: do not assert on it, and
            do not infer a threshold from it (see the endpoint description).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CommonResponse_ScreeningCheckRead_'
              example:
                status_code: 200
                data:
                  id: 3f8a1c2e-9b47-4d51-8e06-1a2b3c4d5e6f
                  entityType: person
                  decision: review
                  topScore: 0.8762
                  dataCompleteness: full
                  hits:
                    - sourceList: us_ofac
                      sourceId: '22790'
                      entityName: Nicolas MADURO MOROS
                      matchScore: 0.8762
                      category: sanctions_blocking
                      programs:
                        - VENEZUELA
                  confirmWith:
                    - key: governmentIds
                      label: Government ID number
                  externalRef: order-1043
                  createdAt: '2026-07-16T10:12:04Z'
                query_generated_time: 1784282324000
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
        - _AuthenticatedBearer: []
components:
  schemas:
    ScreeningCheckCreate:
      properties:
        entityType:
          $ref: '#/components/schemas/ScreeningEntityType'
          description: Whether the subject is a person or a business.
        name:
          type: string
          maxLength: 500
          minLength: 1
          title: Name
          description: Full name / legal name.
        dateOfBirth:
          anyOf:
            - type: string
            - type: 'null'
          title: Dateofbirth
          description: >-
            Date of birth (any common format; normalised to YYYY-MM-DD).
            Supplying it dramatically reduces false positives for people.
        gender:
          anyOf:
            - type: string
            - type: 'null'
          title: Gender
        incorporationDate:
          anyOf:
            - type: string
            - type: 'null'
          title: Incorporationdate
        website:
          anyOf:
            - type: string
            - type: 'null'
          title: Website
        altNames:
          items:
            type: string
          type: array
          title: Altnames
        address:
          anyOf:
            - $ref: '#/components/schemas/ScreeningAddressInput'
            - type: 'null'
        email:
          anyOf:
            - type: string
            - type: 'null'
          title: Email
        phone:
          anyOf:
            - type: string
            - type: 'null'
          title: Phone
        governmentIds:
          items:
            type: string
          type: array
          title: Governmentids
        externalRef:
          anyOf:
            - type: string
              maxLength: 255
            - type: 'null'
          title: Externalref
      type: object
      required:
        - entityType
        - name
      title: ScreeningCheckCreate
      description: Screen one subject on demand. Each successful call is billed once.
    CommonResponse_ScreeningCheckRead_:
      properties:
        status_code:
          type: integer
          title: Status Code
          description: HTTP status code for the response.
        data:
          $ref: '#/components/schemas/ScreeningCheckRead'
          description: Payload or error details.
        query_generated_time:
          type: integer
          title: Query Generated Time
          description: >-
            UTC timestamp (milliseconds since epoch) when response was
            generated.
      type: object
      required:
        - status_code
        - data
        - query_generated_time
      title: CommonResponse[ScreeningCheckRead]
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    ScreeningEntityType:
      type: string
      enum:
        - person
        - business
      title: ScreeningEntityType
      description: >-
        Maps to the screening engine's ``type`` query parameter.


        Only these two. Do **not** add ``organization``: the engine scores the
        same

        business wildly differently under ``business`` (0.8122) vs
        ``organization``

        (0.3933), so the wrong value silently reports a sanctioned entity as
        clear.
    ScreeningAddressInput:
      properties:
        line1:
          anyOf:
            - type: string
            - type: 'null'
          title: Line1
        line2:
          anyOf:
            - type: string
            - type: 'null'
          title: Line2
        city:
          anyOf:
            - type: string
            - type: 'null'
          title: City
        state:
          anyOf:
            - type: string
            - type: 'null'
          title: State
        postalCode:
          anyOf:
            - type: string
            - type: 'null'
          title: Postalcode
        country:
          anyOf:
            - type: string
            - type: 'null'
          title: Country
      type: object
      title: ScreeningAddressInput
      description: A structured address; flattened to one line before screening.
    ScreeningCheckRead:
      properties:
        id:
          type: string
          format: uuid
          title: Id
        entityType:
          type: string
          title: Entitytype
        decision:
          type: string
          title: Decision
          description: >-
            The value to act on: clear, review, blocked, or unavailable. 'clear'
            = proceed; 'review'/'blocked' = hold for a human; 'unavailable'
            means the screening service could not complete — treat it as
            not-yet-screened, never clear.
        topScore:
          anyOf:
            - type: number
            - type: 'null'
          title: Topscore
          description: >-
            Highest match score among the hits (0.0–1.0), or null if none.
            DIAGNOSTIC ONLY — a name-similarity score, not a risk score. Do not
            build thresholds on it: a true watchlist entry scores 0.8122 on name
            alone while an innocent common name scores 0.7513; the score is not
            comparable between subjects screened with different amounts of
            identity data; and it moves as the watchlists refresh (the same
            entry scored 0.8762 and then 0.9025 two days later, unchanged
            software). Lists reload every 12h. Act on 'decision', which is a
            stable contract.
        dataCompleteness:
          type: string
          title: Datacompleteness
          description: >-
            full, partial, or name_only — how much identity data was screened. A
            name_only check is weaker; supply a date of birth or address to
            strengthen it.
        hits:
          items:
            $ref: '#/components/schemas/ScreeningHitRead'
          type: array
          title: Hits
        confirmWith:
          items:
            $ref: '#/components/schemas/ScreeningConfirmationField'
          type: array
          title: Confirmwith
          description: >-
            The ONE piece of identity data that would most improve this result —
            the strongest field you have not already sent. At most one entry, so
            send it and POST a new check; if that still hits, the next check
            asks for the next field, and nothing already in hand is ever asked
            for twice. Populated only when there is something to resolve
            ('review' or 'blocked'); empty on 'clear', and empty on
            'unavailable' because nothing was screened. Empty on a hit means the
            check is already as strong as this API can make it and the remaining
            question needs a human.
        subjectName:
          anyOf:
            - type: string
            - type: 'null'
          title: Subjectname
          description: The name that was screened, as submitted on the check.
        externalRef:
          anyOf:
            - type: string
            - type: 'null'
          title: Externalref
        createdAt:
          type: string
          format: date-time
          title: Createdat
      type: object
      required:
        - id
        - entityType
        - decision
        - dataCompleteness
        - createdAt
      title: ScreeningCheckRead
      description: The result of a screening check.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    ScreeningHitRead:
      properties:
        sourceList:
          type: string
          title: Sourcelist
        sourceId:
          type: string
          title: Sourceid
        entityName:
          type: string
          title: Entityname
        matchScore:
          type: number
          title: Matchscore
        category:
          type: string
          title: Category
          description: 'Legal effect: sanctions_blocking, sanctions_restricting, pep, other.'
        programs:
          items:
            type: string
          type: array
          title: Programs
      type: object
      required:
        - sourceList
        - sourceId
        - entityName
        - matchScore
        - category
      title: ScreeningHitRead
      description: One matched watchlist entity.
    ScreeningConfirmationField:
      properties:
        key:
          type: string
          title: Key
          description: >-
            The request-body field to send it in — always one this endpoint
            accepts, so the ask is directly actionable by re-POSTing a check
            with this field populated. E.g. 'dateOfBirth', 'governmentIds',
            'altNames', 'address'.
        label:
          type: string
          title: Label
          description: Human-readable name, for showing to whoever supplies it.
      type: object
      required:
        - key
        - label
      title: ScreeningConfirmationField
      description: One piece of identity data that would confirm or rule out a match.
  securitySchemes:
    _AuthenticatedBearer:
      type: oauth2
      flows:
        password:
          scopes: {}
          tokenUrl: /v1/platform/auth/token

````