> For the complete documentation index, see [llms.txt](https://docs.fill-easy.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fill-easy.com/kyc-china.md).

# KYC China

KYC verification for mainland China citizens — identity, mobile, risk, and bank checks against Ministry of Public Security, telecom carrier, and banking records.

### Single endpoint, smart fan-out

You send one request to `POST /kyc/cn/request` with whatever fields you have, then either receive the result via a webhook (pass `callbackUrl` in the request) or poll `POST /kyc/cn/poll` until the result is ready. The service inspects the request, picks the strongest verification variant on each axis (e.g. four-factor instead of two-factor when ID dates are present), and runs every eligible sub-check in parallel against the upstream sources.

The merged response tells you, per category, whether the person matched, plus an execution `summary` so you can tell at a glance which checks ran, which matched, which came back empty, and which errored.

### Products

Each check has a **product number**. Pass these in `run` on `POST /kyc/cn/request` to control exactly which checks fire — and what you pay for. Omit `run` to fan out to every product whose inputs are present.

| Product | Check                                  | Inputs it needs                                                |
| ------- | -------------------------------------- | -------------------------------------------------------------- |
| `1`     | Identity — name + ID                   | `name`, `idNumber`                                             |
| `2`     | Identity — face match                  | `name`, `idNumber`, `facePhoto`                                |
| `3`     | Identity — name + ID + validity dates  | `name`, `idNumber`, `idIssueDate`, `idExpiryDate`              |
| `4`     | Identity — validity dates + face match | `name`, `idNumber`, `idIssueDate`, `idExpiryDate`, `facePhoto` |
| `8.1`   | Bank — 2-factor                        | `name`, `bankCardNumber`                                       |
| `8.2.1` | Bank — 3-factor                        | `name`, `idNumber`, `bankCardNumber`                           |
| `8.3`   | Bank — 4-factor                        | `name`, `idNumber`, `bankCardNumber`, `mobile`                 |
| `11`    | Mobile registered to name / ID         | `mobile` + `name` and/or `idNumber`                            |
| `12`    | Mobile + name + ID (3-factor)          | `name`, `idNumber`, `mobile`                                   |
| `15`    | Mobile attribution + location          | `mobile`                                                       |
| `17`    | Address-to-mobile association          | `mobile`, `addressesToVerify[]`                                |
| `24`    | Risk — fraud / gambling blacklist      | `mobile` or `idNumber`                                         |
| `25`    | Litigation records                     | `name`, `idNumber`                                             |
| `27`    | Risk — criminal / police record        | `name`, `idNumber`                                             |

The detailed sub-check eligibility (which upstream call each product maps to, and the smart-selection rules) lives under `POST /kyc/cn/request`.

### Data sources & residency

All upstream calls go through a Beijing-based proxy and query official sources: Ministry of Public Security (identity & criminal), telecom carriers (mobile & location), UnionPay member banks (bank verification), and the national anti-fraud blacklist (risk). No personal data is stored by Fill Easy after the response is returned.

### Cost & rate limits

Each sub-check is a separate upstream call; see the `summary` bucket table under `POST /kyc/cn/request` for which outcomes are billed. A request with `name`, `idNumber`, `mobile`, and `bankCardNumber` fans out to **9 calls** in parallel (worst case without `addressesToVerify`). Each entry in `addressesToVerify` adds one further call. To narrow what runs, simply omit the inputs you don't want to verify — every sub-check whose required fields are present always fires.

## KYC China verification - Request

> Verify a Chinese citizen's identity, mobile, risk profile, and/or bank card in a\
> single request. You send whatever fields you have; the service decides which\
> sub-checks to run, fans them out in parallel against the upstream sources\
> (Ministry of Public Security, telecom carriers, banks, blacklists), and merges\
> the results into one response.\
> \
> \---\
> \
> \## Selecting which products to run\
> \
> By default the endpoint runs \*\*every\*\* sub-check whose required inputs are\
> present. To restrict the request to a specific set of products, supply \`run\`\
> with an array of product numbers from the\
> \[KYC Mainland China catalogue]\(#tag/KYC-China). For example, \`run: \["1", "8.3", "25"]\`\
> runs only ID & Name verification, bank account 4-factor verification, and litigation\
> records. Product numbers are strings — bank verification is addressed per factor\
> level (\`8.1\`, \`8.2.1\`, \`8.3\`).\
> \
> \| Product # | Name | Sub-checks |\
> \|---|---|---|\
> \| \`1\` | ID & Name Verify | \`identity.two-factor\` |\
> \| \`2\` | ID, Name & Face Verify | \`identity.image\` |\
> \| \`3\` | ID, Name & Date Verify | \`identity.four-factor\` |\
> \| \`4\` | ID, Name, Date & Face Verify | \`identity.four-factor\` + \`identity.image\` |\
> \| \`8.1\` | Bank Account 2FV (name + card) | \`bank.two-factor\` |\
> \| \`8.2.1\` | Bank Account 3FV (name + card + ID) | \`bank.three-factor\` |\
> \| \`8.3\` | Bank Account 4FV (name + card + ID + mobile) | \`bank.four-factor\` |\
> \| \`11\` | Telecom 2FV | \`identity.name-mobile\`, \`identity.id-mobile\` |\
> \| \`12\` | Telecom 3FV | \`identity.three-factor\` |\
> \| \`15\` | Telecom Operator Location | \`mobile.attribution\`, \`mobile.location-work\`, \`mobile.location-residential\` |\
> \| \`17\` | Phone-Address Association | \`mobile.address-verify\[N]\` (one per \`addressesToVerify\` entry) |\
> \| \`24\` | Risk Score for Adverse Records | \`risk.fraud\` |\
> \| \`25\` | Litigation Record Verification | \`litigation.records\` |\
> \| \`27\` | Credit Risk Validation | \`risk.criminal\` |\
> \
> When \`run\` is omitted or empty, the default fan-out applies and the smart-selection\
> rules below avoid paying for redundant calls. When \`run\` is supplied, each requested\
> product fires independently if its required inputs are present — so asking for both\
> product \`1\` (two-factor) and product \`3\` (four-factor) runs \*\*both\*\*, bypassing the\
> default upgrade rule.\
> \
> \---\
> \
> \## How the request fans out\
> \
> The endpoint runs every sub-check whose required inputs are present in your request,\
> with two \*\*smart-selection rules\*\* that avoid paying for redundant calls:\
> \
> \- On the \*\*identity-document axis\*\*, the strongest available variant runs and\
> &#x20; weaker ones are skipped. Specifically, \`identity.four-factor\` (which verifies the\
> &#x20; ID's validity dates in addition to name + ID) supersedes \`identity.two-factor\`\
> &#x20; when both \`idIssueDate\` and \`idExpiryDate\` are supplied.\
> \- On the \*\*identity-mobile axis\*\*, the strongest available variant runs.\
> &#x20; \`identity.three-factor\` (name + ID + mobile) supersedes \`identity.name-mobile\`\
> &#x20; and \`identity.id-mobile\` when all three fields are present.\
> \- On the \*\*bank axis\*\*, the default fan-out runs only the strongest eligible\
> &#x20; variant: \`bank.four-factor\` (name + ID + card + mobile) supersedes\
> &#x20; \`bank.three-factor\` (name + ID + card), which supersedes \`bank.two-factor\`\
> &#x20; (name + card). When you select bank products explicitly via \`run\`, each one\
> &#x20; fires independently — \`run: \["8.1", "8.3"]\` runs both 2-factor and 4-factor,\
> &#x20; and each is reported in its own \`bank2f\` / \`bank3f\` / \`bank4f\` field.\
> \
> Other sub-checks run independently — face match (\`identity.image\`) is on a\
> separate axis from document verification, mobile lookups, and risk checks.\
> \
> Every sub-check whose required fields are present \*\*always runs\*\*. There is no\
> opt-out — if you don't want a category to fire, simply don't supply its inputs.\
> \
> \### Sub-check eligibility\
> \
> \| Sub-check | Required fields | What it verifies |\
> \|---|---|---|\
> \| \`identity.two-factor\` | \`name\`, \`idNumber\` | Name + ID pair exists in MPS records |\
> \| \`identity.four-factor\` | \`+ idIssueDate\`, \`idExpiryDate\` | As above, plus dates match the on-file record |\
> \| \`identity.image\` | \`name\`, \`idNumber\`, \`facePhoto\` | Photo matches the MPS face on file |\
> \| \`identity.name-mobile\` | \`name\`, \`mobile\` | Mobile is registered to that name |\
> \| \`identity.id-mobile\` | \`idNumber\`, \`mobile\` | Mobile is registered to that ID |\
> \| \`identity.three-factor\` | \`name\`, \`idNumber\`, \`mobile\` | All three are tied to one person |\
> \| \`mobile.attribution\` | \`mobile\` | Carrier, registered province, city |\
> \| \`mobile.location-work\` | \`mobile\` | Most-active city during 07:00-19:00 weekdays (last 3 months) |\
> \| \`mobile.location-residential\` | \`mobile\` | Most-active city during 21:00-07:00 (last 3 months) |\
> \| \`mobile.address-verify\[N]\` | \`mobile\` + entry in \`addressesToVerify\` | Distance segment between the supplied address and the mobile's recorded \`common\` / \`work\` / \`residential\` area. One sub-check fires per entry, indexed \`\[0]\`, \`\[1]\`, … |\
> \| \`risk.fraud\` | \`mobile\` or \`idNumber\` | Fraud / gambling / money-mule blacklist hits |\
> \| \`risk.criminal\` | \`name\`, \`idNumber\` | Police record flags (criminal, drug, fugitive, etc.) |\
> \| \`bank.two-factor\` | \`name\`, \`bankCardNumber\` | Card is registered under that name |\
> \| \`bank.three-factor\` | \`+ idNumber\` | As above, plus the card belongs to that ID |\
> \| \`bank.four-factor\` | \`+ mobile\` | As above, plus mobile is the card's registered number |\
> \
> \---\
> \
> \## Reading the response\
> \
> The response always contains a \`summary\` object. Each sub-category (\`identity\`,\
> \`mobile\`, \`risk\`, and the \`bank2f\` / \`bank3f\` / \`bank4f\` fields) is \*\*present only\
> if its sub-check ran and returned data\*\*. Categories with only errored or\
> not-found outcomes are omitted.\
> \
> \### \`summary\` buckets\
> \
> Every attempted sub-check appears in \`summary.ran\`. From there it lands in \*\*exactly\
> one\*\* of these buckets:\
> \
> \| Bucket | Meaning |\
> \|---|---|\
> \| \`matched\` | An identity- or bank-axis check returned \`match\` |\
> \| \`unmatched\` | An identity- or bank-axis check returned \`no match\` (the call ran successfully; the answer was negative) |\
> \| \`notFound\` | The upstream had no record for the input. For risk, litigation, and bank checks this is the clean-record answer and is billed; identity and mobile checks returning \`not found\` are not billed |\
> \| \`errored\` | The check could not be completed. \`error\` carries a short human-readable explanation. Not billed on its own — a multi-check product still bills once if another of its checks delivered |\
> \
> A sub-check whose upstream was unreachable or returned an unusable response\
> appears only in \`summary.unavailable\` — not in \`ran\` or any bucket above —\
> and is not billed on its own.\
> \
> Mobile lookups (\`mobile.attribution\`, \`mobile.location-\*\`) and risk lookups\
> (\`risk.fraud\`, \`risk.criminal\`) have no match/no-match semantics. They appear in\
> \`ran\` and either \`notFound\` or \`errored\` — never in \`matched\`/\`unmatched\`.\
> \
> \### Result semantics by category\
> \
> \- \*\*Identity\*\* — array of \`{ type, result, verification }\`. Treat \`match\` as a\
> &#x20; positive identity signal. \`no match\` from \`three-factor\` typically means the\
> &#x20; mobile isn't tied to that person; pair it with the result from \`two-factor\` /\
> &#x20; \`four-factor\` (also returned) to disambiguate document mismatch from mobile mismatch.\
> \- \*\*Mobile\*\* — flat object with whatever the lookups returned. Fields are absent\
> &#x20; when their underlying call returned no data; check \`summary.notFound\` to tell\
> &#x20; "no data available" from "we never asked".\
> \- \*\*Risk\*\* — \`risk.fraud\` returns a fixed list of four risk types (fraud, gambling\
> &#x20; operator, gambling player, money mule) each with severity \`None\` / \`Low\` /\
> &#x20; \`Medium\` / \`High\`. \`risk.criminal\` returns a flat boolean map. \`isNormal: null\`\
> &#x20; (rather than a boolean) means the check could not complete and the records are\
> &#x20; not authoritative.\
> \- \*\*Bank\*\* — up to three independent fields, one per variant that ran:\
> &#x20; \`bank2f\` (name + card), \`bank3f\` (name + card + ID), and \`bank4f\` (name + card +\
> &#x20; ID + mobile). Each holds \`match\` / \`no match\` / \`not found\` and is present only\
> &#x20; when that variant ran. The default fan-out populates a single field (strongest\
> &#x20; eligible); selecting multiple bank products via \`run\` populates one field each.\
> \
> \---\
> \
> \## Errors\
> \
> \| Code | Meaning |\
> \|---|---|\
> \| \`200\` | Always returned when at least one sub-check was eligible. Per-check failures appear in \`summary.errored\` or \`summary.unavailable\`, never as a 4xx/5xx |\
> \| \`400\` | No sub-checks were eligible with the supplied fields (e.g. only \`facePhoto\` provided without \`name\` + \`idNumber\`) |\
> \| \`502\` | Internal failure invoking the upstream — only thrown when something fails outside the per-check fan-out |\
> \
> Partial failures are intentional: a single misbehaving upstream sub-call should not\
> prevent the rest of the response from being delivered.\
> \
> \---\
> \
> \## Common patterns\
> \
> \*\*Lightweight identity check.\*\* Send \`name\` + \`idNumber\` only — runs \`identity.two-factor\`\
> and \`risk.criminal\` for the cost of two upstream calls.\
> \
> \*\*Document verification with face match.\*\* Send \`name\`, \`idNumber\`, \`idIssueDate\`,\
> \`idExpiryDate\`, \`facePhoto\` — runs \`identity.four-factor\` + \`identity.image\` plus\
> \`risk.criminal\`. The strongest non-mobile identity confirmation available.\
> \
> \*\*Mobile-anchored screening.\*\* Send \`mobile\` only — runs \`mobile.attribution\`, both\
> location lookups, and \`risk.fraud\`. Useful when only a phone number is known.\
> \
> \*\*Full KYC.\*\* Send \`name\`, \`idNumber\`, \`mobile\`, \`bankCardNumber\` (and optionally\
> \`idIssueDate\` / \`idExpiryDate\` / \`facePhoto\`) — fans out to up to 9 calls in parallel,\
> plus one extra call per entry in \`addressesToVerify\`.\
> \
> \*\*Address verification.\*\* Pass an \`addressesToVerify\[]\` entry to ask "is this address\
> near the user's \`work\` / \`residential\` / \`common\` area?". Each entry returns a\
> distance bucket (\`≤3km\` → \`>50km\`). Useful for verifying a user's stated address\
> against where their phone is actually used — runs alongside the standard mobile\
> location lookups, which return the actual cities.\
> \
> \*\*Staged verification.\*\* To gate later steps on earlier ones (e.g. only screen risk\
> and bank after identity passes), call the endpoint twice: first with \`name\` +\
> \`idNumber\` only, then re-call with the additional fields after you've inspected\
> the identity result. Each call is independent.\
> \
> \*\*Webhook Delivery:\*\* Provide \`callbackUrl\` to receive a \`POST\` with the completed report\
> when it is ready. The webhook body is a \[\`WebhookPayload\`]\(#/components/schemas/WebhookPayload)\
> containing the same data as a \`200\` response from \`/kyc/cn/poll\`.\
> The request includes \`X-Webhook-Event: kyc.cn.report.completed\` and \`X-Request-Id\` headers,\
> plus any custom headers supplied in \`callbackHeaders\`.\
> Polling via \`/kyc/cn/poll\` remains available regardless of whether a \`callbackUrl\` is provided.<br>

```json
{"openapi":"3.1.0","info":{"title":"Fill Easy Services","version":"1.0.0"},"tags":[{"name":"KYC China","description":"KYC verification for mainland China citizens — identity, mobile, risk, and bank checks\nagainst Ministry of Public Security, telecom carrier, and banking records.\n\n## Single endpoint, smart fan-out\n\nYou send one request to [`POST /kyc/cn/request`](#tag/KYC-China/operation/kycCnRequest)\nwith whatever fields you have, then either receive the result via a webhook\n(pass `callbackUrl` in the request) or poll\n[`POST /kyc/cn/poll`](#tag/KYC-China/operation/kycCnPoll) until the result is ready. The service inspects the request, picks the strongest\nverification variant on each axis (e.g. four-factor instead of two-factor when ID dates\nare present), and runs every eligible sub-check in parallel against the upstream sources.\n\nThe merged response tells you, per category, whether the person matched, plus an\nexecution `summary` so you can tell at a glance which checks ran, which matched, which\ncame back empty, and which errored.\n\n## Products\n\nEach check has a **product number**. Pass these in `run` on\n[`POST /kyc/cn/request`](#tag/KYC-China/operation/kycCnRequest) to control exactly which\nchecks fire — and what you pay for. Omit `run` to fan out to every product whose inputs\nare present.\n\n| Product | Check | Inputs it needs |\n|---|---|---|\n| `1` | Identity — name + ID | `name`, `idNumber` |\n| `2` | Identity — face match | `name`, `idNumber`, `facePhoto` |\n| `3` | Identity — name + ID + validity dates | `name`, `idNumber`, `idIssueDate`, `idExpiryDate` |\n| `4` | Identity — validity dates + face match | `name`, `idNumber`, `idIssueDate`, `idExpiryDate`, `facePhoto` |\n| `8.1` | Bank — 2-factor | `name`, `bankCardNumber` |\n| `8.2.1` | Bank — 3-factor | `name`, `idNumber`, `bankCardNumber` |\n| `8.3` | Bank — 4-factor | `name`, `idNumber`, `bankCardNumber`, `mobile` |\n| `11` | Mobile registered to name / ID | `mobile` + `name` and/or `idNumber` |\n| `12` | Mobile + name + ID (3-factor) | `name`, `idNumber`, `mobile` |\n| `15` | Mobile attribution + location | `mobile` |\n| `17` | Address-to-mobile association | `mobile`, `addressesToVerify[]` |\n| `24` | Risk — fraud / gambling blacklist | `mobile` or `idNumber` |\n| `25` | Litigation records | `name`, `idNumber` |\n| `27` | Risk — criminal / police record | `name`, `idNumber` |\n\nThe detailed sub-check eligibility (which upstream call each product maps to, and the\nsmart-selection rules) lives under\n[`POST /kyc/cn/request`](#tag/KYC-China/operation/kycCnRequest).\n\n## Data sources & residency\n\nAll upstream calls go through a Beijing-based proxy and query official sources:\nMinistry of Public Security (identity & criminal), telecom carriers (mobile & location),\nUnionPay member banks (bank verification), and the national anti-fraud blacklist (risk).\nNo personal data is stored by Fill Easy after the response is returned.\n\n## Cost & rate limits\n\nEach sub-check is a separate upstream call; see the `summary` bucket table under\n`POST /kyc/cn/request` for which outcomes are billed. A request with `name`, `idNumber`,\n`mobile`, and `bankCardNumber` fans out to **9 calls** in parallel (worst case without\n`addressesToVerify`). Each entry in `addressesToVerify` adds one further call. To narrow\nwhat runs, simply omit the inputs you don't want to verify — every sub-check whose\nrequired fields are present always fires.\n"}],"servers":[{"url":"sandbox.staging-api.fill-easy.com"}],"security":[{"ClientID":[],"ClientSecret":[]}],"components":{"securitySchemes":{"ClientID":{"type":"apiKey","description":"Client ID in x-client-id header.","name":"x-client-id","in":"header"}}},"paths":{"/kyc/cn/request":{"post":{"tags":["KYC China"],"summary":"KYC China verification - Request","description":"Verify a Chinese citizen's identity, mobile, risk profile, and/or bank card in a\nsingle request. You send whatever fields you have; the service decides which\nsub-checks to run, fans them out in parallel against the upstream sources\n(Ministry of Public Security, telecom carriers, banks, blacklists), and merges\nthe results into one response.\n\n---\n\n## Selecting which products to run\n\nBy default the endpoint runs **every** sub-check whose required inputs are\npresent. To restrict the request to a specific set of products, supply `run`\nwith an array of product numbers from the\n[KYC Mainland China catalogue](#tag/KYC-China). For example, `run: [\"1\", \"8.3\", \"25\"]`\nruns only ID & Name verification, bank account 4-factor verification, and litigation\nrecords. Product numbers are strings — bank verification is addressed per factor\nlevel (`8.1`, `8.2.1`, `8.3`).\n\n| Product # | Name | Sub-checks |\n|---|---|---|\n| `1` | ID & Name Verify | `identity.two-factor` |\n| `2` | ID, Name & Face Verify | `identity.image` |\n| `3` | ID, Name & Date Verify | `identity.four-factor` |\n| `4` | ID, Name, Date & Face Verify | `identity.four-factor` + `identity.image` |\n| `8.1` | Bank Account 2FV (name + card) | `bank.two-factor` |\n| `8.2.1` | Bank Account 3FV (name + card + ID) | `bank.three-factor` |\n| `8.3` | Bank Account 4FV (name + card + ID + mobile) | `bank.four-factor` |\n| `11` | Telecom 2FV | `identity.name-mobile`, `identity.id-mobile` |\n| `12` | Telecom 3FV | `identity.three-factor` |\n| `15` | Telecom Operator Location | `mobile.attribution`, `mobile.location-work`, `mobile.location-residential` |\n| `17` | Phone-Address Association | `mobile.address-verify[N]` (one per `addressesToVerify` entry) |\n| `24` | Risk Score for Adverse Records | `risk.fraud` |\n| `25` | Litigation Record Verification | `litigation.records` |\n| `27` | Credit Risk Validation | `risk.criminal` |\n\nWhen `run` is omitted or empty, the default fan-out applies and the smart-selection\nrules below avoid paying for redundant calls. When `run` is supplied, each requested\nproduct fires independently if its required inputs are present — so asking for both\nproduct `1` (two-factor) and product `3` (four-factor) runs **both**, bypassing the\ndefault upgrade rule.\n\n---\n\n## How the request fans out\n\nThe endpoint runs every sub-check whose required inputs are present in your request,\nwith two **smart-selection rules** that avoid paying for redundant calls:\n\n- On the **identity-document axis**, the strongest available variant runs and\n  weaker ones are skipped. Specifically, `identity.four-factor` (which verifies the\n  ID's validity dates in addition to name + ID) supersedes `identity.two-factor`\n  when both `idIssueDate` and `idExpiryDate` are supplied.\n- On the **identity-mobile axis**, the strongest available variant runs.\n  `identity.three-factor` (name + ID + mobile) supersedes `identity.name-mobile`\n  and `identity.id-mobile` when all three fields are present.\n- On the **bank axis**, the default fan-out runs only the strongest eligible\n  variant: `bank.four-factor` (name + ID + card + mobile) supersedes\n  `bank.three-factor` (name + ID + card), which supersedes `bank.two-factor`\n  (name + card). When you select bank products explicitly via `run`, each one\n  fires independently — `run: [\"8.1\", \"8.3\"]` runs both 2-factor and 4-factor,\n  and each is reported in its own `bank2f` / `bank3f` / `bank4f` field.\n\nOther sub-checks run independently — face match (`identity.image`) is on a\nseparate axis from document verification, mobile lookups, and risk checks.\n\nEvery sub-check whose required fields are present **always runs**. There is no\nopt-out — if you don't want a category to fire, simply don't supply its inputs.\n\n### Sub-check eligibility\n\n| Sub-check | Required fields | What it verifies |\n|---|---|---|\n| `identity.two-factor` | `name`, `idNumber` | Name + ID pair exists in MPS records |\n| `identity.four-factor` | `+ idIssueDate`, `idExpiryDate` | As above, plus dates match the on-file record |\n| `identity.image` | `name`, `idNumber`, `facePhoto` | Photo matches the MPS face on file |\n| `identity.name-mobile` | `name`, `mobile` | Mobile is registered to that name |\n| `identity.id-mobile` | `idNumber`, `mobile` | Mobile is registered to that ID |\n| `identity.three-factor` | `name`, `idNumber`, `mobile` | All three are tied to one person |\n| `mobile.attribution` | `mobile` | Carrier, registered province, city |\n| `mobile.location-work` | `mobile` | Most-active city during 07:00-19:00 weekdays (last 3 months) |\n| `mobile.location-residential` | `mobile` | Most-active city during 21:00-07:00 (last 3 months) |\n| `mobile.address-verify[N]` | `mobile` + entry in `addressesToVerify` | Distance segment between the supplied address and the mobile's recorded `common` / `work` / `residential` area. One sub-check fires per entry, indexed `[0]`, `[1]`, … |\n| `risk.fraud` | `mobile` or `idNumber` | Fraud / gambling / money-mule blacklist hits |\n| `risk.criminal` | `name`, `idNumber` | Police record flags (criminal, drug, fugitive, etc.) |\n| `bank.two-factor` | `name`, `bankCardNumber` | Card is registered under that name |\n| `bank.three-factor` | `+ idNumber` | As above, plus the card belongs to that ID |\n| `bank.four-factor` | `+ mobile` | As above, plus mobile is the card's registered number |\n\n---\n\n## Reading the response\n\nThe response always contains a `summary` object. Each sub-category (`identity`,\n`mobile`, `risk`, and the `bank2f` / `bank3f` / `bank4f` fields) is **present only\nif its sub-check ran and returned data**. Categories with only errored or\nnot-found outcomes are omitted.\n\n### `summary` buckets\n\nEvery attempted sub-check appears in `summary.ran`. From there it lands in **exactly\none** of these buckets:\n\n| Bucket | Meaning |\n|---|---|\n| `matched` | An identity- or bank-axis check returned `match` |\n| `unmatched` | An identity- or bank-axis check returned `no match` (the call ran successfully; the answer was negative) |\n| `notFound` | The upstream had no record for the input. For risk, litigation, and bank checks this is the clean-record answer and is billed; identity and mobile checks returning `not found` are not billed |\n| `errored` | The check could not be completed. `error` carries a short human-readable explanation. Not billed on its own — a multi-check product still bills once if another of its checks delivered |\n\nA sub-check whose upstream was unreachable or returned an unusable response\nappears only in `summary.unavailable` — not in `ran` or any bucket above —\nand is not billed on its own.\n\nMobile lookups (`mobile.attribution`, `mobile.location-*`) and risk lookups\n(`risk.fraud`, `risk.criminal`) have no match/no-match semantics. They appear in\n`ran` and either `notFound` or `errored` — never in `matched`/`unmatched`.\n\n### Result semantics by category\n\n- **Identity** — array of `{ type, result, verification }`. Treat `match` as a\n  positive identity signal. `no match` from `three-factor` typically means the\n  mobile isn't tied to that person; pair it with the result from `two-factor` /\n  `four-factor` (also returned) to disambiguate document mismatch from mobile mismatch.\n- **Mobile** — flat object with whatever the lookups returned. Fields are absent\n  when their underlying call returned no data; check `summary.notFound` to tell\n  \"no data available\" from \"we never asked\".\n- **Risk** — `risk.fraud` returns a fixed list of four risk types (fraud, gambling\n  operator, gambling player, money mule) each with severity `None` / `Low` /\n  `Medium` / `High`. `risk.criminal` returns a flat boolean map. `isNormal: null`\n  (rather than a boolean) means the check could not complete and the records are\n  not authoritative.\n- **Bank** — up to three independent fields, one per variant that ran:\n  `bank2f` (name + card), `bank3f` (name + card + ID), and `bank4f` (name + card +\n  ID + mobile). Each holds `match` / `no match` / `not found` and is present only\n  when that variant ran. The default fan-out populates a single field (strongest\n  eligible); selecting multiple bank products via `run` populates one field each.\n\n---\n\n## Errors\n\n| Code | Meaning |\n|---|---|\n| `200` | Always returned when at least one sub-check was eligible. Per-check failures appear in `summary.errored` or `summary.unavailable`, never as a 4xx/5xx |\n| `400` | No sub-checks were eligible with the supplied fields (e.g. only `facePhoto` provided without `name` + `idNumber`) |\n| `502` | Internal failure invoking the upstream — only thrown when something fails outside the per-check fan-out |\n\nPartial failures are intentional: a single misbehaving upstream sub-call should not\nprevent the rest of the response from being delivered.\n\n---\n\n## Common patterns\n\n**Lightweight identity check.** Send `name` + `idNumber` only — runs `identity.two-factor`\nand `risk.criminal` for the cost of two upstream calls.\n\n**Document verification with face match.** Send `name`, `idNumber`, `idIssueDate`,\n`idExpiryDate`, `facePhoto` — runs `identity.four-factor` + `identity.image` plus\n`risk.criminal`. The strongest non-mobile identity confirmation available.\n\n**Mobile-anchored screening.** Send `mobile` only — runs `mobile.attribution`, both\nlocation lookups, and `risk.fraud`. Useful when only a phone number is known.\n\n**Full KYC.** Send `name`, `idNumber`, `mobile`, `bankCardNumber` (and optionally\n`idIssueDate` / `idExpiryDate` / `facePhoto`) — fans out to up to 9 calls in parallel,\nplus one extra call per entry in `addressesToVerify`.\n\n**Address verification.** Pass an `addressesToVerify[]` entry to ask \"is this address\nnear the user's `work` / `residential` / `common` area?\". Each entry returns a\ndistance bucket (`≤3km` → `>50km`). Useful for verifying a user's stated address\nagainst where their phone is actually used — runs alongside the standard mobile\nlocation lookups, which return the actual cities.\n\n**Staged verification.** To gate later steps on earlier ones (e.g. only screen risk\nand bank after identity passes), call the endpoint twice: first with `name` +\n`idNumber` only, then re-call with the additional fields after you've inspected\nthe identity result. Each call is independent.\n\n**Webhook Delivery:** Provide `callbackUrl` to receive a `POST` with the completed report\nwhen it is ready. The webhook body is a [`WebhookPayload`](#/components/schemas/WebhookPayload)\ncontaining the same data as a `200` response from `/kyc/cn/poll`.\nThe request includes `X-Webhook-Event: kyc.cn.report.completed` and `X-Request-Id` headers,\nplus any custom headers supplied in `callbackHeaders`.\nPolling via `/kyc/cn/poll` remains available regardless of whether a `callbackUrl` is provided.\n","operationId":"kycCnRequest","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"All fields are optional. The endpoint runs every sub-check whose required\nfields are present (see the *Sub-check eligibility* table above). At least\none sub-check must be eligible — otherwise a `400` is returned.\n","properties":{"name":{"type":"string","description":"Full legal name in **Chinese characters** (simplified). Must match the\nname on the resident ID card exactly. Latin pinyin transliterations are\nnot accepted by the upstream.\n\nEnables: identity-document checks, mobile-cross checks, bank checks, criminal records.\n"},"idNumber":{"type":"string","description":"18-digit Chinese Resident Identity Card number (居民身份证号码).\nThe 18th character may be `X` for older IDs.\n\nEnables: identity-document checks, mobile-cross checks, bank checks, fraud, criminal records.\n","pattern":"^[0-9]{17}[0-9Xx]$"},"idIssueDate":{"type":"string","description":"ID card issue date in `YYYYMMDD` format (no separators). Found on the\nback of the physical card under 签发日期.\n\nPair with `idExpiryDate` to upgrade `identity.two-factor` to the stronger\n`identity.four-factor` check that also validates the on-file dates.\n","pattern":"^[0-9]{8}$"},"idExpiryDate":{"type":"string","description":"ID card expiry date in `YYYYMMDD` format, or the literal string `长期`\nwhen the card has no expiry (issued to citizens 46+ in some cases).\nFound on the back of the physical card under 有效期限.\n"},"facePhoto":{"type":"string","description":"Base64-encoded facial photo. **Do not include the `data:image/...;base64,`\nprefix** — only the raw base64 payload.\n\nRecommended: clear front-facing photo, no occlusions, JPEG/PNG, ≤2 MB.\nEnables `identity.image`, which compares the photo against the MPS face\non file for the supplied name + ID. Requires `name` and `idNumber`.\n"},"mobile":{"type":"string","description":"11-digit mainland China mobile number. **No country code, no `+86`,\nno spaces.** First two digits must be `13`-`19`.\n\nEnables: mobile lookups, mobile-cross identity checks, fraud screening,\nbank `four-factor`. Mobile alone is enough to run mobile/risk lookups\nwithout any identity fields.\n","pattern":"^1[3-9][0-9]{9}$"},"bankCardNumber":{"type":"string","description":"Bank card number — debit or credit. Digits only, no spaces. Enables\n`bank.two-factor` (with `name`), `bank.three-factor` (also with `idNumber`),\nor `bank.four-factor` (also with `mobile`).\n"},"addressesToVerify":{"type":"array","description":"Addresses to verify against the mobile's recorded location patterns.\nEach entry asks the upstream to compute the distance segment between\nthe supplied `address` and the mobile's most-active area for the\nselected `type`. Requires `mobile`.\n\nEach entry produces one `mobile.address-verify[N]` sub-check, where\n`N` is the array index. Results appear in the response under\n`mobile.addressVerifications[]` in the same order.\n\nUp to ~10 entries per request — each is a separate upstream call.\n","items":{"type":"object","required":["type","city","address"],"properties":{"type":{"type":"string","enum":["common","work","residential"],"description":"Which of the mobile's recorded locations to compare against:\n- `common` — area with overall highest mobile activity\n- `work` — most active area during 07:00-19:00 weekdays\n- `residential` — most active area during 21:00-07:00\n"},"city":{"type":"string","description":"City the address is located in (Chinese characters)."},"address":{"type":"string","description":"Street-level address (Chinese characters)."}}}},"run":{"type":"array","description":"Restrict the request to a specific set of products from the KYC\nMainland China catalogue. Product numbers are strings (e.g. `\"1\"`,\n`\"8.3\"`). When omitted or empty, every eligible sub-check runs\n(smart-selection rules apply). When supplied, only the listed\nproducts fire — and each runs independently, bypassing the default\nsupersession rules between products `1`/`3`, `11`/`12`, and the\nbank variants. Selecting multiple bank products runs each one and\nreports it in its own `bank2f` / `bank3f` / `bank4f` field.\n\nSupported values: `\"1\", \"2\", \"3\", \"4\", \"8.1\", \"8.2.1\", \"8.3\", \"11\",\n\"12\", \"15\", \"17\", \"24\", \"25\", \"27\"`. See the *Selecting which\nproducts to run* section above for the product-number → sub-check\nmapping.\n\nAny other value returns a `400` with `requestId` and a message\ndistinguishing **unimplemented** (catalogued but no upstream yet —\n`5, 6, 7, 8.2.2, 9.1, 9.2.1, 9.2.2, 9.3, 10, 13, 14, 16, 18–23, 26,\n28–35`) from **unknown** (outside the catalogue entirely).\n","items":{"type":"string"}},"callbackUrl":{"type":"string","format":"uri","pattern":"^https://.+","description":"HTTPS URL to receive a webhook POST when the report is ready.\nWhen provided, the completed report (same payload as a `200` response from `/kyc/cn/poll`)\nis delivered to this URL.\nPolling remains available as a fallback.\n"},"callbackHeaders":{"type":"object","additionalProperties":{"type":"string"},"description":"Custom HTTP headers to include in the webhook request.\nUse this to pass authentication or any other headers your endpoint requires\n(e.g. `{\"Authorization\": \"Bearer <token>\", \"X-Api-Key\": \"...\"}`).\n"}}}}}},"responses":{"200":{"description":"KYC verification request initiated. Use the returned token with `/kyc/cn/poll` to fetch the result.","content":{"application/json":{"schema":{"type":"object","required":["token","requestId"],"properties":{"token":{"type":"string","description":"JWT token encoding the request — pass it to `/kyc/cn/poll` until a `200` response\nis returned (typically within 10-30 seconds for a full KYC fan-out).\n"},"requestId":{"type":"string","description":"Stable identifier for this request. Returned on both `200` and `400`\nresponses for tracing/support. Prefixed `KYC_` followed by 12\nrandom characters.\n"}}}}}},"400":{"description":"Returned in two cases:\n- No sub-checks were eligible with the provided fields (e.g. supplying only\n  `facePhoto` without `name` and `idNumber`, or an empty request body).\n- `run` contains a product number that is not in the supported set\n  (`1, 2, 3, 4, 8, 11, 12, 15, 17, 24, 25, 27`).\n\nThe body always contains `{ \"message\": \"...\", \"requestId\": \"KYC_...\" }` —\nthe `requestId` is logged in the tracker so support can correlate the\nrejection with the originating call.\n","content":{"application/json":{"schema":{"type":"object","required":["message","requestId"],"properties":{"message":{"type":"string"},"requestId":{"type":"string"}}}}}},"502":{"description":"Internal failure invoking the upstream verification service before any sub-check\ncould run. Per-check failures during fan-out do **not** produce a 502 — they\nappear in `summary.errored` or `summary.unavailable` inside a successful 200 response.\n"}}}}}}
```

## KYC China verification - Poll

> Check the status of a KYC verification request initiated via\
> \[\`POST /kyc/cn/request\`]\(#tag/KYC-China/operation/kycCnRequest).\
> Returns \`202\` while the verification is still running, and \`200\` with\
> the full result when complete.\
> \
> Verification typically completes within 10-30 seconds depending on the\
> number of sub-checks and PDF rendering. Poll every 2-3 seconds.\
> \
> See the \[\`/kyc/cn/request\`]\(#tag/KYC-China/operation/kycCnRequest) documentation\
> for the response shape semantics (sub-check eligibility, summary buckets,\
> category-by-category interpretation).<br>

```json
{"openapi":"3.1.0","info":{"title":"Fill Easy Services","version":"1.0.0"},"tags":[{"name":"KYC China","description":"KYC verification for mainland China citizens — identity, mobile, risk, and bank checks\nagainst Ministry of Public Security, telecom carrier, and banking records.\n\n## Single endpoint, smart fan-out\n\nYou send one request to [`POST /kyc/cn/request`](#tag/KYC-China/operation/kycCnRequest)\nwith whatever fields you have, then either receive the result via a webhook\n(pass `callbackUrl` in the request) or poll\n[`POST /kyc/cn/poll`](#tag/KYC-China/operation/kycCnPoll) until the result is ready. The service inspects the request, picks the strongest\nverification variant on each axis (e.g. four-factor instead of two-factor when ID dates\nare present), and runs every eligible sub-check in parallel against the upstream sources.\n\nThe merged response tells you, per category, whether the person matched, plus an\nexecution `summary` so you can tell at a glance which checks ran, which matched, which\ncame back empty, and which errored.\n\n## Products\n\nEach check has a **product number**. Pass these in `run` on\n[`POST /kyc/cn/request`](#tag/KYC-China/operation/kycCnRequest) to control exactly which\nchecks fire — and what you pay for. Omit `run` to fan out to every product whose inputs\nare present.\n\n| Product | Check | Inputs it needs |\n|---|---|---|\n| `1` | Identity — name + ID | `name`, `idNumber` |\n| `2` | Identity — face match | `name`, `idNumber`, `facePhoto` |\n| `3` | Identity — name + ID + validity dates | `name`, `idNumber`, `idIssueDate`, `idExpiryDate` |\n| `4` | Identity — validity dates + face match | `name`, `idNumber`, `idIssueDate`, `idExpiryDate`, `facePhoto` |\n| `8.1` | Bank — 2-factor | `name`, `bankCardNumber` |\n| `8.2.1` | Bank — 3-factor | `name`, `idNumber`, `bankCardNumber` |\n| `8.3` | Bank — 4-factor | `name`, `idNumber`, `bankCardNumber`, `mobile` |\n| `11` | Mobile registered to name / ID | `mobile` + `name` and/or `idNumber` |\n| `12` | Mobile + name + ID (3-factor) | `name`, `idNumber`, `mobile` |\n| `15` | Mobile attribution + location | `mobile` |\n| `17` | Address-to-mobile association | `mobile`, `addressesToVerify[]` |\n| `24` | Risk — fraud / gambling blacklist | `mobile` or `idNumber` |\n| `25` | Litigation records | `name`, `idNumber` |\n| `27` | Risk — criminal / police record | `name`, `idNumber` |\n\nThe detailed sub-check eligibility (which upstream call each product maps to, and the\nsmart-selection rules) lives under\n[`POST /kyc/cn/request`](#tag/KYC-China/operation/kycCnRequest).\n\n## Data sources & residency\n\nAll upstream calls go through a Beijing-based proxy and query official sources:\nMinistry of Public Security (identity & criminal), telecom carriers (mobile & location),\nUnionPay member banks (bank verification), and the national anti-fraud blacklist (risk).\nNo personal data is stored by Fill Easy after the response is returned.\n\n## Cost & rate limits\n\nEach sub-check is a separate upstream call; see the `summary` bucket table under\n`POST /kyc/cn/request` for which outcomes are billed. A request with `name`, `idNumber`,\n`mobile`, and `bankCardNumber` fans out to **9 calls** in parallel (worst case without\n`addressesToVerify`). Each entry in `addressesToVerify` adds one further call. To narrow\nwhat runs, simply omit the inputs you don't want to verify — every sub-check whose\nrequired fields are present always fires.\n"}],"servers":[{"url":"sandbox.staging-api.fill-easy.com"}],"security":[{"ClientID":[],"ClientSecret":[]}],"components":{"securitySchemes":{"ClientID":{"type":"apiKey","description":"Client ID in x-client-id header.","name":"x-client-id","in":"header"}},"schemas":{"Token":{"type":"string","pattern":"^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$","description":"JWT token"},"LitigationStats":{"type":"object","description":"Aggregated counts and amounts for a litigation category (or all categories,\nwhen used as the `overall` block). Counts are case counts; amounts are in CNY.\n","required":["total"],"properties":{"total":{"type":"integer","description":"Total cases."},"open":{"type":"integer","description":"Open (unresolved) cases."},"closed":{"type":"integer","description":"Closed cases."},"asPlaintiff":{"type":"integer","description":"Cases where the subject was the plaintiff."},"asDefendant":{"type":"integer","description":"Cases where the subject was the defendant."},"asThirdParty":{"type":"integer","description":"Cases where the subject was a third party."},"totalAmount":{"type":"number","description":"Total monetary amount involved (CNY)."},"openAmount":{"type":"number","description":"Amount tied to open cases (CNY)."},"closedAmount":{"type":"number","description":"Amount tied to closed cases (CNY)."},"causeDistribution":{"type":"string","description":"Distribution of case causes, formatted by upstream as `Cause(count), Cause(count)`."},"locationDistribution":{"type":"string","description":"Distribution by case location."},"yearDistribution":{"type":"string","description":"Distribution by filing year."},"closingMethodDistribution":{"type":"string","description":"Distribution by closing method (e.g. judgment, mediation)."}}},"LitigationCategory":{"type":"object","required":["cases","stats"],"properties":{"cases":{"type":"array","items":{"$ref":"#/components/schemas/LitigationCase"}},"stats":{"$ref":"#/components/schemas/LitigationStats"}}},"LitigationCase":{"type":"object","description":"A single litigation record. Always includes the structural fields (case number,\ncourt, stage, parties). Optional fields are present only when the upstream service\nreturns them — criminal-specific fields (`charges`, `crimeAmount`) are absent for\nnon-criminal cases.\n","required":["caseType","caseNumber","court","courtLevel","stage","procedure","location","cause","partyRole","parties"],"properties":{"caseType":{"type":"string","description":"Case type label (e.g. `Civil First Instance`, `Criminal First Instance`, `Property Preservation Execution`)."},"caseNumber":{"type":"string"},"court":{"type":"string"},"courtLevel":{"type":"string","description":"Court level (Basic Court / Intermediate / High / Supreme)."},"stage":{"type":"string","description":"Case progress stage as returned by upstream (e.g. `Open`, `Closed`)."},"procedure":{"type":"string","description":"Trial procedure (First Instance, Second Instance, Retrial, Execution)."},"location":{"type":"string"},"cause":{"type":"string"},"partyRole":{"type":"string","description":"Subject's role in this case."},"parties":{"type":"array","items":{"$ref":"#/components/schemas/LitigationParty"}},"filingDate":{"type":"string","format":"date"},"closingDate":{"type":"string","format":"date"},"causeDetail":{"type":"string","description":"Full cause taxonomy path (comma-delimited from broad to specific)."},"closingMethod":{"type":"string","description":"How the case was closed (Judgment, Mediation, Withdrawal, etc.)."},"amount":{"type":"number","description":"Closing subject amount (CNY)."},"victoryEstimate":{"type":"string","description":"Upstream estimate of the subject's outcome (Won / Lost / Partial / Unknown)."},"charges":{"type":"string","description":"Criminal charges (criminal cases only)."},"chargesDetail":{"type":"string","description":"Full charge taxonomy path (criminal cases only)."},"crimeAmount":{"type":"number","description":"Crime amount (criminal cases only, CNY)."}}},"LitigationParty":{"type":"object","required":["name","role","type"],"properties":{"name":{"type":"string"},"role":{"type":"string","description":"Litigation status (e.g. Plaintiff, Defendant, Person Subject to Application, Third Party)."},"type":{"type":"string","description":"Party type (e.g. Natural Person, Enterprise Organization)."}}}}},"paths":{"/kyc/cn/poll":{"post":{"tags":["KYC China"],"summary":"KYC China verification - Poll","description":"Check the status of a KYC verification request initiated via\n[`POST /kyc/cn/request`](#tag/KYC-China/operation/kycCnRequest).\nReturns `202` while the verification is still running, and `200` with\nthe full result when complete.\n\nVerification typically completes within 10-30 seconds depending on the\nnumber of sub-checks and PDF rendering. Poll every 2-3 seconds.\n\nSee the [`/kyc/cn/request`](#tag/KYC-China/operation/kycCnRequest) documentation\nfor the response shape semantics (sub-check eligibility, summary buckets,\ncategory-by-category interpretation).\n","operationId":"kycCnPoll","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"$ref":"#/components/schemas/Token"}}}}}},"responses":{"200":{"description":"KYC China result. Categories are present only when at least one of their sub-checks ran.","content":{"application/json":{"schema":{"type":"object","required":["reportMetadata","summary","submitted","pdfUrl","pdfFileName"],"properties":{"reportMetadata":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["completed"]}}},"submitted":{"type":"object","description":"Masked echo of the inputs that were submitted, for audit and report\nrendering. Each field is present only if it was supplied in the\nrequest. Sensitive values are masked — the first two characters are\nkept and the rest replaced with asterisks of the same length.\n","properties":{"name":{"type":"string"},"idNumber":{"type":"string"},"idIssueDate":{"type":"string"},"idExpiryDate":{"type":"string"},"facePhoto":{"type":"string","description":"Literal `provided` when a face photo was supplied; otherwise omitted."},"mobile":{"type":"string"},"bankCardNumber":{"type":"string"},"addressesToVerify":{"type":"array","items":{"type":"object","required":["type","city","address"],"properties":{"type":{"type":"string","enum":["common","work","residential"]},"city":{"type":"string"},"address":{"type":"string"}}}}}},"pdfUrl":{"type":"string","format":"uri","description":"Presigned URL to download a PDF version of this report. Valid for 7 days.\nThe underlying object is deleted after 30 days by bucket lifecycle rule.\n"},"pdfFileName":{"type":"string","description":"Suggested filename for the PDF download."},"summary":{"type":"object","required":["ran","matched","unmatched","notFound","errored"],"properties":{"ran":{"type":"array","description":"Every sub-check that was attempted.","items":{"type":"string"}},"matched":{"type":"array","description":"Identity and bank checks that returned `match`.","items":{"type":"string"}},"unmatched":{"type":"array","description":"Identity and bank checks that returned `no match`.","items":{"type":"string"}},"notFound":{"type":"array","description":"Sub-checks where the upstream had no data on file.","items":{"type":"string"}},"errored":{"type":"array","description":"Sub-checks that could not be completed. Not billed on their own.","items":{"type":"object","required":["check","error"],"properties":{"check":{"type":"string"},"error":{"type":"string","description":"Short human-readable explanation. Internal diagnostics stay in server logs."}}}},"unavailable":{"type":"array","description":"Sub-checks that could not be attempted because the upstream\nwas unreachable or returned an unusable response. Not billed\non their own and not listed in `ran`. Absent when every\neligible sub-check was attempted.\n","items":{"type":"string"}}}},"identity":{"type":"array","description":"Present when at least one identity check ran. Each entry corresponds to one sub-check.","items":{"type":"object","required":["type","result","verification"],"properties":{"type":{"type":"string","enum":["two-factor","four-factor","image","name-mobile","id-mobile","three-factor"]},"result":{"type":"string","enum":["match","no match","not found"]},"verification":{"type":"string","description":"Human-readable description of what was verified."}}}},"mobile":{"type":"object","description":"Present when at least one mobile lookup returned data.","properties":{"carrier":{"type":"string"},"province":{"type":"string"},"city":{"type":"string"},"workLocation":{"type":"string","description":"Most active city during weekday work hours (07:00-19:00)."},"residentialLocation":{"type":"string","description":"Most active city during night hours (21:00-07:00)."},"addressVerifications":{"type":"array","description":"Per-entry results for each address supplied in\n`addressesToVerify[]`, in the same order as the input. The\noriginal `type`, `city`, and `address` are echoed back so\ncallers can correlate results to inputs without tracking the\nindex themselves.\n\nThe `distance` field is **present only when the upstream\nreturned a match** — its absence signals \"no data on file\"\n(the entry is also listed in `summary.notFound`). Errored\nentries are omitted from this array entirely and listed in\n`summary.errored`.\n","items":{"type":"object","required":["type","city","address"],"properties":{"type":{"type":"string","enum":["common","work","residential"]},"city":{"type":"string"},"address":{"type":"string"},"distance":{"type":"string","enum":["≤3km","≤5km","≤10km","≤20km","≤30km","≤50km",">50km"],"description":"Distance segment between the supplied address and the\nmobile's recorded location for the chosen `type`.\n"}}}}}},"risk":{"type":"object","description":"Present when at least one risk check ran.","properties":{"fraud":{"type":"array","items":{"type":"object","required":["type","level"],"properties":{"type":{"type":"string"},"level":{"type":"string","enum":["None","Low","Medium","High"]}}}},"criminal":{"type":"object","required":["isNormal","records"],"properties":{"isNormal":{"type":"boolean","nullable":true,"description":"`true` when no risk events are recorded; `false` when records exist;\n`null` when verification could not complete.\n"},"records":{"type":"object","additionalProperties":{"type":"boolean"},"description":"Map of human-readable record category to whether it applies."}}}}},"bank2f":{"type":"string","enum":["match","no match","not found"],"description":"Result of the 2-factor bank check (name + card, product `8.1`).\nPresent only when the 2FV variant ran.\n"},"bank3f":{"type":"string","enum":["match","no match","not found"],"description":"Result of the 3-factor bank check (name + card + ID, product `8.2.1`).\nPresent only when the 3FV variant ran.\n"},"bank4f":{"type":"string","enum":["match","no match","not found"],"description":"Result of the 4-factor bank check (name + card + ID + mobile, product\n`8.3`). Present only when the 4FV variant ran.\n"},"litigation":{"type":"object","description":"Judicial litigation records aggregated across civil, criminal,\nadministrative, preservation, execution, and bankruptcy case types.\nEach category is present only when at least one case exists. `overall`\nis the cross-category statistics block returned by the upstream service.\n","properties":{"overall":{"$ref":"#/components/schemas/LitigationStats"},"civil":{"$ref":"#/components/schemas/LitigationCategory"},"criminal":{"$ref":"#/components/schemas/LitigationCategory"},"administrative":{"$ref":"#/components/schemas/LitigationCategory"},"preservation":{"$ref":"#/components/schemas/LitigationCategory"},"execution":{"$ref":"#/components/schemas/LitigationCategory"},"bankruptcy":{"$ref":"#/components/schemas/LitigationCategory"}}}}}}}},"202":{"description":"KYC verification still processing — keep polling every 2-3 seconds.","content":{"application/json":{"schema":{"type":"object","required":["reportMetadata"],"properties":{"reportMetadata":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["processing"]}}}}}}}},"400":{"description":"Invalid or expired token."}}}}}}
```
