KYC result (level and minimal fields; no images or profile)
x-on-behalf-of required
Called on behalf of a member; x-on-behalf-of is required. Omitting it returns member_context_required (400), with no fallback to a default member.
Returns the result only. Original profiles and images never leave the platform, and we never disclose which merchant performed KYC, which would leak cross-merchant information.
⚠ nationality and updated_at have values only after L1 approval; otherwise they are empty strings, not null.
⚠ status has three values: approved (level ≥ 1), pending (a profile exists that is neither approved nor rejected), and none (all other cases). REJECTED appears as none, indistinguishable from never applied. You cannot distinguish that case, and rejection reasons are not returned.
⚠ Do not build a two-state onboarding flow. An earlier description incorrectly stated that, contrary to the implementation. Sending another KYC link to a user whose submission is pending review causes POST /v1/kyc/sessions to return 400 state_invalid, leaving your flow at a dead end. Use pending to identify this case.
For required KYC levels by business line, see GET /v1/kyc/requirements.
Also returns quick_kyc: { bindings, remaining }. Each binding's identity_issue_country is the quick profile's document-issuing country, as a two-letter country code or an empty string if unset. A quick binding does not mean the member is KYC-verified: level still reflects only the member's own L1, and quick KYC cannot be used for remittances. See Quick KYC.
Request Headers
| Field | Type | Required | Description |
|---|---|---|---|
x-on-behalf-of |
string | Required | The member on whose behalf to call. Accepts your external_member_id or our mem_<uuid>.
Nonexistent, belonging to another merchant, and suspended members all return the same 404. |
Response
{
"level": 1,
"status": "approved",
"nationality": "CN",
"updated_at": "2026-08-10T04:12:33.100Z",
"quick_kyc": {
"bindings": [],
"remaining": 5
}
}member_context_required: missing x-on-behalf-of.member_not_found, including members belonging to another merchant or suspended members.curl -X GET 'https://api.zinfra.vip/v1/kyc' \
-H 'x-auth-token: Bearer $TOKEN' \
-H 'x-on-behalf-of: $MEMBER_ID'const res = await fetch("https://api.zinfra.vip/v1/kyc", {
method: "GET",
headers: {
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
},
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();import requests
res = requests.get(
"https://api.zinfra.vip/v1/kyc",
headers={
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
},
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()req, _ := http.NewRequest("GET", "https://api.zinfra.vip/v1/kyc",
nil)
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-on-behalf-of", "$MEMBER_ID")
res, err := http.DefaultClient.Do(req)
// Decode amount fields as string, not float64.HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.zinfra.vip/v1/kyc"))
.header("x-auth-token", "Bearer $TOKEN")
.header("x-on-behalf-of", "$MEMBER_ID")
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
// Use String / BigDecimal for amounts, not double.$ch = curl_init('https://api.zinfra.vip/v1/kyc');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'x-auth-token: Bearer $TOKEN',
'x-on-behalf-of: $MEMBER_ID',
],
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
{
"level": 1,
"status": "approved",
"nationality": "CN",
"updated_at": "2026-08-10T04:12:33.100Z",
"quick_kyc": {
"bindings": [],
"remaining": 5
}
}