POST /customers
Create an end-user customer. The body is polymorphic: a type discriminator selects an individual (KYC flow) or a business (KYB flow). The rest of the body carries the matching profile fields. On success the response carries the customer's UUID. Every subsequent call uses it. The customer is scoped to the org bound to your API key.
The body accepts partial drafts: every leaf is optional on the wire, the wizard can save what it has so far and the server stores whatever's there. Submit-time validation runs on POST /customers/:id/kyc/submit, required-field gaps surface there as missing[], not here.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
type | "individual" | "business" | yes | Discriminator. Picks the shape of profile. |
profile | KycProfile | KybProfile | yes | Empty object is fine: every leaf is optional at create time. Submit-time validation runs separately on POST /customers/:id/kyc/submit. |
sandbox_auto_approve_kyc | boolean | no | Sandbox only. Controls what happens when you later call POST /customers/:id/kyc/submit. Omit it or set true for immediate approval in the submit response and a corresponding status webhook. Set false for asynchronous status progression. Creating the customer never submits it. On production, explicit true is rejected with 400; explicit false is accepted as a no-op. |
type: "individual", embed a KycProfile
{
"type": "individual",
"profile": {
"identity": {
"first_name": "Alice",
"last_name": "Liddell",
"date_of_birth": "1990-04-12"
},
"contact": {
"email": "alice@example.com"
},
"addresses": {
"residential": {
"line1": "123 Main St",
"city": "Brooklyn",
"state": "NY",
"postal_code": "11201",
"country": "US"
}
}
}
}See KycProfile for the full sub-section list (identity, addresses, contact, employment, risk, volumes, jurisdictions, attestations, documents, supplemental).
type: "business", embed a KybProfile
{
"type": "business",
"profile": {
"business": {
"legal_name": "Acme Inc.",
"entity_type": "c_corporation",
"country_of_registration": "US"
},
"contact": {
"email": "ops@acme.com"
}
}
}See KybProfile for the full sub-section list (business, addresses, contact, operations, volumes, counterparties, attestations, associated_persons, supplemental, documents).
Document references
Document bytes are uploaded separately via POST /customers/:id/kyc/documents, but the upload endpoint takes a customer_id in the URL, so you need a created customer first. The common flow is:
POST /customerswith profile fields only, no document refs.POST /customers/:id/kyc/documentsfor each document. Capture the returneddocument_id; supporting documents attach automatically and appear in the read-onlyprofile.documents.supporting_documentslist.PUT /customers/:id/kyc/profileto write thedocument_idinto the profile (profile.documents.government_issued_identification.front_image_document_idorprofile.associated_persons[].gov_id_front_image_document_id).POST /customers/:id/kyc/submitonce required fields are in.
Request headers
| Header | Notes |
|---|---|
Idempotency-Key | Optional. Any unique string up to 256 characters, generally a UUID. Re-sending the same key returns the originally created customer instead of creating a duplicate. Re-sending it with a different type than the original create is rejected with 409 — a key never answers for the other kind of customer. Deleting the customer frees its key for reuse. Recommended for any caller doing automatic retries. |
Response 201
The id of the new customer, the type discriminator (so clients don't have to remember which kind they posted), and an auto-minted HostedKycLink: share this url with the end-customer to drive them through verification, no separate provisioning call needed.
Individual (type: "individual"): url carries the ind segment, fully qualified against the environment's APP_BASE_URL:
{
"type": "individual",
"id": "2f10…",
"kyc_link": {
"token": "klink_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w",
"url": "https://orchestration.seismic.systems/kyc-link/ind/klink_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w"
}
}Business (type: "business"): same field name kyc_link, url carries the biz segment:
{
"type": "business",
"id": "8e3b…",
"kyc_link": {
"token": "klink_b9c8d7e6f5g4h3i2j1k0l9m8n7o6p5q4r3s2t1u0v9w8x",
"url": "https://orchestration.seismic.systems/kyc-link/biz/klink_b9c8d7e6f5g4h3i2j1k0l9m8n7o6p5q4r3s2t1u0v9w8x"
}
}On sandbox, url resolves against https://orchestration.seismictest.net instead (same shape, different origin).
Sandbox approval timing
Creating a sandbox customer always stores a draft. You must still call POST /customers/:id/kyc/submit after the profile is complete.
By default, that submit returns the final approved state immediately and emits the corresponding status webhook. Set "sandbox_auto_approve_kyc": false when creating the customer to exercise the asynchronous lifecycle instead: submit returns an in-flight state, then signed status webhooks advance the customer through pending, under_review, and finally approved after randomized delays. If the first update lands quickly, the submit response may already show under_review.
Sandbox submission still enforces profile validation and jurisdiction restrictions. A restricted jurisdiction leaves the customer under_review instead of approving. The production API rejects explicit true with 400; explicit false is accepted as a no-op, so an opted-out payload can be shared across environments.
{
"type": "individual",
"profile": {
"identity": {
"first_name": "Alice",
"last_name": "Liddell",
"date_of_birth": "1990-04-12"
},
"contact": {
"email": "alice@example.com"
}
},
"sandbox_auto_approve_kyc": false
}Errors
400: body schema invalid (unknown enum value, malformed date, etc.). The message names the bad path.400:sandbox_auto_approve_kyc: trueset against the production API. Immediate sandbox approval is sandbox-only. Explicitfalseis accepted as a no-op.409(IdempotencyKeyConflict): theIdempotency-Keywas previously used to create a customer of a differenttype. Send a fresh key for the new customer.
Example: individual
curl -X POST "$SEISMIC_ORCHESTRATION_URL/v0/customers" \
-H "Api-Key: $SEISMIC_ORCHESTRATION_API_KEY" \
-H "content-type: application/json" \
-d '{
"type": "individual",
"profile": {
"identity": {
"first_name": "Alice",
"last_name": "Liddell",
"date_of_birth": "1990-04-12"
},
"contact": {
"email": "alice@example.com"
},
"addresses": {
"residential": {
"line1": "123 Main St",
"city": "Brooklyn",
"state": "NY",
"postal_code": "11201",
"country": "US"
}
}
}
}'Example: business
curl -X POST "$SEISMIC_ORCHESTRATION_URL/v0/customers" \
-H "Api-Key: $SEISMIC_ORCHESTRATION_API_KEY" \
-H "content-type: application/json" \
-d '{
"type": "business",
"profile": {
"business": {
"legal_name": "Acme Inc.",
"entity_type": "c_corporation",
"country_of_registration": "US"
},
"contact": {
"email": "ops@acme.com"
}
}
}'