For the complete documentation index, see llms.txt. This page is also available as Markdown.

KYC China

KYC verification for mainland China citizens — identity, mobile, risk, and bank checks against Chinese government, 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: Chinese government (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

post

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, 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. 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 weaker ones are skipped. Specifically, identity.four-factor (which verifies the ID's validity dates in addition to name + ID) supersedes identity.two-factor when both idIssueDate and idExpiryDate are supplied.

  • On the identity-mobile axis, the strongest available variant runs. identity.three-factor (name + ID + mobile) supersedes identity.name-mobile and identity.id-mobile when all three fields are present.

  • On the bank axis, the default fan-out runs only the strongest eligible variant: bank.four-factor (name + ID + card + mobile) supersedes bank.three-factor (name + ID + card), which supersedes bank.two-factor (name + card). When you select bank products explicitly via run, each one fires independently — run: ["8.1", "8.3"] runs both 2-factor and 4-factor, 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 positive identity signal. no match from three-factor typically means the mobile isn't tied to that person; pair it with the result from two-factor / four-factor (also returned) to disambiguate document mismatch from mobile mismatch.

  • Mobile — flat object with whatever the lookups returned. Fields are absent when their underlying call returned no data; check summary.notFound to tell "no data available" from "we never asked".

  • Riskrisk.fraud returns a fixed list of four risk types (fraud, gambling operator, gambling player, money mule) each with severity None / Low / Medium / High. risk.criminal returns a flat boolean map. isNormal: null (rather than a boolean) means the check could not complete and the records are not authoritative.

  • Bank — up to three independent fields, one per variant that ran: bank2f (name + card), bank3f (name + card + ID), and bank4f (name + card + ID + mobile). Each holds match / no match / not found and is present only when that variant ran. The default fan-out populates a single field (strongest 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 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.

Authorizations
x-client-idstringRequired

Client ID in x-client-id header.

x-client-secretstringRequired

Client Secret in x-client-secret header.

Body

All fields are optional. The endpoint runs every sub-check whose required fields are present (see the Sub-check eligibility table above). At least one sub-check must be eligible — otherwise a 400 is returned.

namestringOptional

Full legal name in Chinese characters (simplified). Must match the name on the resident ID card exactly. Latin pinyin transliterations are not accepted by the upstream.

Enables: identity-document checks, mobile-cross checks, bank checks, criminal records.

Example: 张三
idNumberstringOptional

18-digit Chinese Resident Identity Card number (居民身份证号码). The 18th character may be X for older IDs.

Enables: identity-document checks, mobile-cross checks, bank checks, fraud, criminal records.

Example: 110101199001011234Pattern: ^[0-9]{17}[0-9Xx]$
idIssueDatestringOptional

ID card issue date in YYYYMMDD format (no separators). Found on the back of the physical card under 签发日期.

Pair with idExpiryDate to upgrade identity.two-factor to the stronger identity.four-factor check that also validates the on-file dates.

Example: 20180501Pattern: ^[0-9]{8}$
idExpiryDatestringOptional

ID card expiry date in YYYYMMDD format, or the literal string 长期 when the card has no expiry (issued to citizens 46+ in some cases). Found on the back of the physical card under 有效期限.

Example: 20281231
facePhotostringOptional

Base64-encoded facial photo. Do not include the data:image/...;base64, prefix — only the raw base64 payload.

Recommended: clear front-facing photo, no occlusions, JPEG/PNG, ≤2 MB. Enables identity.image, which compares the photo against the MPS face on file for the supplied name + ID. Requires name and idNumber.

Example: iVBORw0KGgoAAAANSUhEUgAA...
mobilestringOptional

11-digit mainland China mobile number. No country code, no +86, no spaces. First two digits must be 13-19.

Enables: mobile lookups, mobile-cross identity checks, fraud screening, bank four-factor. Mobile alone is enough to run mobile/risk lookups without any identity fields.

Example: 13800138000Pattern: ^1[3-9][0-9]{9}$
bankCardNumberstringOptional

Bank card number — debit or credit. Digits only, no spaces. Enables bank.two-factor (with name), bank.three-factor (also with idNumber), or bank.four-factor (also with mobile).

Example: 6222021234567890123
runstring[]Optional

Restrict the request to a specific set of products from the KYC Mainland China catalogue. Product numbers are strings (e.g. "1", "8.3"). When omitted or empty, every eligible sub-check runs (smart-selection rules apply). When supplied, only the listed products fire — and each runs independently, bypassing the default supersession rules between products 1/3, 11/12, and the bank variants. Selecting multiple bank products runs each one and reports it in its own bank2f / bank3f / bank4f field.

Supported values: "1", "2", "3", "4", "8.1", "8.2.1", "8.3", "11", "12", "15", "17", "24", "25", "27". See the Selecting which products to run section above for the product-number → sub-check mapping.

Any other value returns a 400 with requestId and a message distinguishing unimplemented (catalogued but no upstream yet — 5, 6, 7, 8.2.2, 9.1, 9.2.1, 9.2.2, 9.3, 10, 13, 14, 16, 18–23, 26, 28–35) from unknown (outside the catalogue entirely).

Example: ["1","8.3","25"]
callbackUrlstring · uriOptional

HTTPS URL to receive a webhook POST when the report is ready. When provided, the completed report (same payload as a 200 response from /kyc/cn/poll) is delivered to this URL. Polling remains available as a fallback.

Pattern: ^https://.+
Responses
200

KYC verification request initiated. Use the returned token with /kyc/cn/poll to fetch the result.

application/json
tokenstringRequired

JWT token encoding the request — pass it to /kyc/cn/poll until a 200 response is returned (typically within 10-30 seconds for a full KYC fan-out).

Example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
requestIdstringRequired

Stable identifier for this request. Returned on both 200 and 400 responses for tracing/support. Prefixed KYC_ followed by 12 random characters.

Example: KYC_a1b2c3d4e5f6
post/kyc/cn/request

KYC China verification - Poll

post

Check the status of a KYC verification request initiated via POST /kyc/cn/request. 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 documentation for the response shape semantics (sub-check eligibility, summary buckets, category-by-category interpretation).

Authorizations
x-client-idstringRequired

Client ID in x-client-id header.

x-client-secretstringRequired

Client Secret in x-client-secret header.

Body
tokenstringRequired

JWT token

Example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8UPattern: ^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$
Responses
200

KYC China result. Categories are present only when at least one of their sub-checks ran.

application/json
pdfUrlstring · uriRequired

Presigned URL to download a PDF version of this report. Valid for 7 days. The underlying object is deleted after 30 days by bucket lifecycle rule.

Example: https://kyc-bucket-ap-east-1-staging-canary.s3.ap-east-1.amazonaws.com/kyc/abc123.pdf?X-Amz-...
pdfFileNamestringRequired

Suggested filename for the PDF download.

Example: kyc-cn-abc123.pdf
bank2fstring · enumOptional

Result of the 2-factor bank check (name + card, product 8.1). Present only when the 2FV variant ran.

Possible values:
bank3fstring · enumOptional

Result of the 3-factor bank check (name + card + ID, product 8.2.1). Present only when the 3FV variant ran.

Possible values:
bank4fstring · enumOptional

Result of the 4-factor bank check (name + card + ID + mobile, product 8.3). Present only when the 4FV variant ran.

Possible values:
post/kyc/cn/poll

Last updated