Z Zise Developers 简体中文

Create a member

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

Two idempotency layers: external_member_id naturally deduplicates; resubmitting the same external ID returns the existing row with 200 rather than 201. x-idempotency-key provides an additional layer. Already exists is therefore not an error; branch on the status code.

⚠ If the email already belongs to another merchant's member on the platform, this returns 400 invalid_request and does not create a member. Identity separation, moving email uniqueness from users to identities, has not yet been implemented. The global unique index remains, so the same individual cannot currently create separate accounts under two merchants.

This is not an Already registered response: it is identical to the generic Invalid request response, with nothing indicating that the email exists elsewhere. Distinguishing the cases would enable cross-merchant member probing. Consequently, after 400 you cannot determine yourself whether the cause is bad format or this restriction; ask the user to use another email or contact us.

If the email already belongs to your own merchant, we recognize the existing member, attach your external_member_id, and return 200.

The account receives a random placeholder password and does not use our password login. Members enter through your App or our hosted screens.

Emits member.created.

Prerequisites

  • The email does not belong to another merchant's member on the platform.
FieldTypeRequiredDescription
x-idempotency-key string Required UUID v4

Request Body

FieldTypeRequiredDescription
external_member_id string Required Your own member identifier, maximum 120 characters. It is the natural idempotency key for this business line and one of the accepted values for all subsequent x-on-behalf-of headers.
email string Required Member email. The server stores it in lowercase and validates only that it contains @. It is also the identity layer's merging key, as described above.

Response

200This external_member_id already exists; returns the existing row without creating a new member.
201Created successfully
{
  "id": "mem_9f1c0c8e-6f2a-4c1d-9d0b-2a7e5b3f8c41",
  "external_member_id": "u-10086",
  "uid": "80031427",
  "email": "alice@example.com",
  "nickname": "",
  "status": "normal",
  "kyc_level": 0,
  "created_at": "2026-08-12T09:30:00.000Z"
}
400invalid_request: the same response for three cases: body is not JSON; external_member_id is empty or exceeds 120 characters, or email is empty or lacks @; the email already belongs to another merchant's member, as described above.
403insufficient_scope: missing members:write.
409idempotency_key_reused · idempotency_in_progress

Emitted Events

Green = successful terminal state · Red = terminal state requiring action · Purple = intermediate state. Open an event for its payload and signature verification details.

Request
curl -X POST 'https://api.zinfra.vip/v1/members' \
  -H 'x-auth-token: Bearer $TOKEN' \
  -H 'x-idempotency-key: $IDEMPOTENCY_KEY' \
  -H 'content-type: application/json' \
  -d '{
    "external_member_id": "u-10086",
    "email": "alice@example.com"
  }'
const res = await fetch("https://api.zinfra.vip/v1/members", {
  method: "POST",
  headers: {
    "x-auth-token": "Bearer $TOKEN",
    "x-idempotency-key": "$IDEMPOTENCY_KEY",
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "external_member_id": "u-10086",
    "email": "alice@example.com"
  }),
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();
import requests

res = requests.post(
    "https://api.zinfra.vip/v1/members",
    headers={
        "x-auth-token": "Bearer $TOKEN",
        "x-idempotency-key": "$IDEMPOTENCY_KEY",
        "content-type": "application/json",
    },
    json={
      "external_member_id": "u-10086",
      "email": "alice@example.com"
    },
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()
req, _ := http.NewRequest("POST", "https://api.zinfra.vip/v1/members",
    strings.NewReader(`{
  "external_member_id": "u-10086",
  "email": "alice@example.com"
}`))
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/members"))
    .header("x-auth-token", "Bearer $TOKEN")
    .header("x-idempotency-key", "$IDEMPOTENCY_KEY")
    .header("content-type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString("""
{
  "external_member_id": "u-10086",
  "email": "alice@example.com"
}
"""))
    .build();
// Use String / BigDecimal for amounts, not double.
$ch = curl_init('https://api.zinfra.vip/v1/members');
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'
{
  "external_member_id": "u-10086",
  "email": "alice@example.com"
}
JSON,
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
200
// No response example is declared in the specification for this operation.