Z Zise Developers 简体中文

Submit member L1 KYC directly from your server

POST /v1/kyc/applications scope: kyc:write
Merchant account Requires x-idempotency-key

Submits a person-level L1 KYC profile for this member. Review is per person, not per card BIN: a person may have only one non-rejected profile at a time. card_product_id is optional; when supplied, it associates the submission with a product visible in your catalog by creating a snapshot. Omit it to submit only the person-level profile. Do not supply an issuer or BIN yourself.

After L1 approval, apply for a second or third card product directly through POST /v1/cards/applications, changing product_id. Do not call this endpoint again. The issuance gate checks only whether this person's L1 is approved.

For the same merchant + member + product, another submission when one is nonterminal or approved returns the existing application with 200. Rejected applications may be resubmitted. Submitting for another product while the first is pending returns state_invalid. Wait for GET /v1/kyc to report the result; do not submit two profiles in parallel.

If L1 is already approved, calling this endpoint for a different product only adds an approved snapshot; profile may be an empty object. It does not trigger another review, and card issuance does not depend on it.

Poll GET /v1/kyc for the result. Submission itself does not emit a webhook; approval / rejection / document-supplement requests emit kyc.result.updated.

FieldTypeRequiredDescription
x-idempotency-key string Required
x-on-behalf-of string Required The member's external_member_id or mem_<uuid>.

Request Body

FieldTypeRequiredDescription
card_product_id string Optional Optional card product identifier, with an optional cpd_ prefix, from GET /v1/cards/products. When supplied, creates only a product snapshot, which issuance does not read; omitted means person-level profile only. After approval, use product_id on the card application endpoint to apply for other products.
profile object Required The member's L1 identity profile. The first submission must be complete. After L1 approval, subsequent calls may use {}.

Response

201Person-level L1 accepted (status=pending). Without card_product_id, id is the person-level kyc_<id> and card_product_id is an empty string. With a product, an approved snapshot may instead be added for that product. For other card products, use the person-level result from GET /v1/kyc.
{
  "id": "cpkyc_visa_virtual_usd_42",
  "card_product_id": "visa_virtual_usd",
  "kyc_id": "42",
  "status": "pending",
  "level": 0
}
400invalid_request · product_not_available · state_invalid
404member_not_found · card_product_not_found
409idempotency_key_required · idempotency_key_reused
Request
curl -X POST 'https://api.zinfra.vip/v1/kyc/applications' \
  -H 'x-auth-token: Bearer $TOKEN' \
  -H 'x-idempotency-key: $IDEMPOTENCY_KEY' \
  -H 'content-type: application/json' \
  -d '{
    "profile": {
      "first_name": "Jane",
      "last_name": "Doe",
      "nationality": "US"
    }
  }'
const res = await fetch("https://api.zinfra.vip/v1/kyc/applications", {
  method: "POST",
  headers: {
    "x-auth-token": "Bearer $TOKEN",
    "x-idempotency-key": "$IDEMPOTENCY_KEY",
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "profile": {
      "first_name": "Jane",
      "last_name": "Doe",
      "nationality": "US"
    }
  }),
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();
import requests

res = requests.post(
    "https://api.zinfra.vip/v1/kyc/applications",
    headers={
        "x-auth-token": "Bearer $TOKEN",
        "x-idempotency-key": "$IDEMPOTENCY_KEY",
        "content-type": "application/json",
    },
    json={
      "profile": {
        "first_name": "Jane",
        "last_name": "Doe",
        "nationality": "US"
      }
    },
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()
req, _ := http.NewRequest("POST", "https://api.zinfra.vip/v1/kyc/applications",
    strings.NewReader(`{
  "profile": {
    "first_name": "Jane",
    "last_name": "Doe",
    "nationality": "US"
  }
}`))
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-idempotency-key", "$IDEMPOTENCY_KEY")
req.Header.Set("content-type", "application/json")
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/applications"))
    .header("x-auth-token", "Bearer $TOKEN")
    .header("x-idempotency-key", "$IDEMPOTENCY_KEY")
    .header("content-type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString("""
{
  "profile": {
    "first_name": "Jane",
    "last_name": "Doe",
    "nationality": "US"
  }
}
"""))
    .build();
// Use String / BigDecimal for amounts, not double.
$ch = curl_init('https://api.zinfra.vip/v1/kyc/applications');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'x-auth-token: Bearer $TOKEN',
    'x-idempotency-key: $IDEMPOTENCY_KEY',
    'content-type: application/json',
  ],
  CURLOPT_POSTFIELDS => <<<'JSON'
{
  "profile": {
    "first_name": "Jane",
    "last_name": "Doe",
    "nationality": "US"
  }
}
JSON,
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
201
{
  "id": "cpkyc_visa_virtual_usd_42",
  "card_product_id": "visa_virtual_usd",
  "kyc_id": "42",
  "status": "pending",
  "level": 0
}