Create a customer
POST /customers - Create a partner-scoped customer (identity) record, idempotently.
0Gate is the primary public integration path for hosted payment, ramp, and swap experiences. Keep secret-key operations on your server and hand only browser-safe values to the widget.
A customer is your own CRM-style reference for an end user: an email plus optional contact fields and free-form metadata. It is a partner-scoped identity record you create and manage, not the KYC-verified 0Bit user identity. Use it to keep a stable handle for a person across sessions, transactions, and support.
Endpoint
| Field | Value |
|---|---|
| Method | POST |
| Path | /v1/customers |
| Area | Customers |
| Operation id | createCustomer |
| Auth boundary | Secret key from your server. |
Use it for
Create a partner-scoped customer record for an end user and get back its 0Bit id to store against your own user. Send your own reference as external_id so you can look the customer up later without storing the 0Bit id.
This is a standard identity write (CRM-style), not a privacy-minimized KYC view. The fields you send are the contact details you choose to keep — there is no document, screening, or verification payload involved.
Production rules
- Keep secret keys on your server. This endpoint requires an
sk_*key. Never call it from a browser with a publishablepk_*key. - Send an
Idempotency-Keyfor the write and reuse the same key when you retry a timeout — a repeat collapses to the original response instead of creating a second customer. emailis unique per partner, andexternal_idis unique per partner when supplied. A duplicate of either is a409with codecustomer_already_exists— branch on it rather than retrying.- Validate
external_id,phone, andcountry_codeformats before the call to avoid a400. - Store the returned
id, yourexternal_id, the request id, and timestamps so support and reconciliation do not depend on browser callbacks alone. - Treat examples and placeholder ids as fake data only.
Headers
| Header | Required | Use it for |
|---|---|---|
Authorization | Yes | Bearer sk_test_… / Bearer sk_live_…. Secret key from your server. |
Idempotency-Key | Yes | UUID for this logical write. Reuse it on retries so a duplicate request returns the original customer. |
Content-Type | Yes | application/json. |
Request body
| Field | Required | Type | Use it for |
|---|---|---|---|
email | Yes | string | Contact email, max 320 chars. Unique per partner — a duplicate returns 409. |
external_id | No | string | Your own reference for this user. Max 256 chars, pattern ^[\w.@:+\-]{1,256}$ (letters, digits, and . _ - + @ :). Unique per partner — a duplicate returns 409. |
phone | No | string | Strict E.164, pattern ^\+[1-9]\d{6,14}$, for example +447700900000. |
first_name | No | string | Given name, max 128 chars. |
last_name | No | string | Family name, max 128 chars. |
country_code | No | string | ISO 3166-1 alpha-2, pattern ^[A-Za-z]{2}$, for example GB. |
metadata | No | object | Free-form key/value pairs you control. Echoed back on reads. |
Server-managed fields are rejected
kyc_status and mode are server-managed and id, partner_id, livemode, created_at, and updated_at are server-set. Sending any field not listed above is a 400 — the body is strictly whitelisted.
Response
Returns 201 with the created customer object.
Customer object
| Field | Type | Use it for |
|---|---|---|
id | string | 0Bit id for this customer. Store it against your user; use it in /v1/customers/{id} calls. |
object | string | Always customer. |
partner_id | string | The partner this record belongs to (your account). |
mode | test/live | Which key mode created the record. |
livemode | boolean | true for live-mode records, false for test. |
external_id | string | null | Your reference, or null when not set. |
email | string | Contact email. |
phone | string | null | E.164 phone, or null. |
first_name | string | null | Given name, or null. |
last_name | string | null | Family name, or null. |
country_code | string | null | ISO 3166-1 alpha-2 region, or null. |
kyc_status | string | unverified / processing / verified / rejected / expired. Informational, not partner-settable, and always unverified at launch. |
metadata | object | Your free-form key/value pairs. |
created_at | string | null | RFC 3339 creation time. |
updated_at | string | null | RFC 3339 last-modified time. |
Examples
curl -X POST https://gate-api.0bit.app/v1/customers \
-H "Authorization: Bearer sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: 7f9c2b1e-0000-4a00-8000-000000000123" \
-H "Content-Type: application/json" \
-d '{
"email": "jordan@example.com",
"external_id": "user_42",
"phone": "+447700900000",
"first_name": "Jordan",
"last_name": "Doe",
"country_code": "GB",
"metadata": { "plan": "pro" }
}'{
"id": "67a1f3b9e4b0c10001234567",
"object": "customer",
"partner_id": "ptnr_test_000000000001",
"mode": "test",
"livemode": false,
"external_id": "user_42",
"email": "jordan@example.com",
"phone": "+447700900000",
"first_name": "Jordan",
"last_name": "Doe",
"country_code": "GB",
"kyc_status": "unverified",
"metadata": { "plan": "pro" },
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}{
"type": "conflict",
"code": "customer_already_exists",
"message": "A customer with email \"jordan@example.com\" already exists",
"request_id": "req_test_000000000123",
"doc_url": null,
"statusCode": 409
}A 409 with code customer_already_exists means the email or external_id is already taken for your account. Look the existing customer up with GET /v1/customers rather than retrying.
Errors
All errors use the unified envelope and carry an X-Request-Id response header. Branch on code/type/statusCode, not on the free-form message.
{
"type": "invalid_request",
"code": "invalid_request",
"message": "Example validation error using fake data.",
"request_id": "req_test_000000000123",
"doc_url": null,
"statusCode": 400
}| Status | type | When it happens |
|---|---|---|
400 | invalid_request | Bad body — missing email, malformed external_id/phone/country_code, or an unknown/server-managed field. |
401 | unauthorized | Missing or invalid secret key. |
409 | conflict | Code customer_already_exists — the email or external_id is already in use for this partner. |
429 | rate_limited | Request throttled. Back off and retry with the same Idempotency-Key. |
5xx | server_error | Transient server or upstream failure. Retry with bounded backoff and the same Idempotency-Key. |
Public boundary
This reference covers partner-scoped endpoint behavior, authentication, idempotency, webhook verification, and support-safe records. Internal operations, administrative routes, and unsupported availability claims are outside the public API contract.