Create a new customer
Honors Idempotency-Key header. See /docs/api/idempotency.
Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.
In: header
Header Parameters
Client-supplied key (a UUID v4 is the recommended form, max 255 characters) that makes this mutation safe to retry.
- Replay window: 24 hours. The first response for a key is cached; a retry inside the window with the same request body returns the original status and body without re-running the handler.
- Reusing a key with a different body returns
409 Conflictwithcode: "IDEMPOTENCY_KEY_REUSED". The request fingerprint (method + path + body hash) is compared against the stored one; a mismatch is refused rather than served the old response. - Keys are scoped to the calling credential and store — two API keys may safely use the same key value.
- Error responses are cached too, so a retry of a request that failed validation returns the same 400 immediately.
- Sending this header on a
GETreturns400withcode: "IDEMPOTENCY_KEY_NOT_SUPPORTED"— GETs are already idempotent. - Support is per-route and this parameter is the authoritative signal: a key sent to a route that does not declare it is accepted and silently ignored.
length <= 255Customer email, unique per store. Used for login (when password is set) and order lookup.
emaillength <= 320Phone number (E.164 format recommended for SMS notifications).
length <= 50First name.
length <= 100Last name.
length <= 100Plaintext password. When provided, the customer is created as a registered account (can log in). When omitted, a guest record is created (orders can still link by email).
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z0-9]).{8,}$8 <= lengthMarketing consent flag. Required for transactional vs marketing email split.
Birthday month (1-12) for the loyalty birthday gift. Month and day only, with no year. Send it together with birthDay or not at all; one without the other is rejected.
1 <= value <= 12Birthday day of month (1-31). Send it together with birthMonth or not at all. The day must exist in that month, so 30 February is rejected.
1 <= value <= 31Free-form tags for segmentation (VIP, B2B, beta-tester, …).
items <= 100Free-form customer segment/role set by the merchant (e.g. "wholesale", "vip", "ambassador"). Returned on /customers/me so storefront code can branch on it to build custom, role-gated features. Not settable by the customer themselves: admin dashboard/API only.
length <= 50FIRST-TOUCH sales channel for a customer being created here. Creating a customer through the dashboard or API is not itself a channel sighting, so this stays null unless you say otherwise. Set it when you know where the person actually came from (migrating from another system, a phone order taken for a specific storefront). Accepts the internal SalesChannel id or the public vc_* connectionId. Attach them to the channels they SHOP on separately, via the publish endpoint.
Arbitrary JSON metadata. Useful for storing integration-specific IDs (your ERP customer number, CRM contact id, …).
curl -X POST "https://api.brainerce.com/api/v1/customers" \ -H "Idempotency-Key: string" \ -H "Content-Type: application/json" \ -d '{ "email": "[email protected]" }'{
"id": "cust_abc123",
"email": "[email protected]",
"firstName": "Jane",
"lastName": "Doe",
"phone": "+14155552671",
"hasAccount": true,
"emailVerified": true,
"acceptsMarketing": true,
"birthMonth": 4,
"birthDay": 17,
"tags": [
"vip",
"newsletter"
],
"role": "wholesale",
"totalOrders": 12,
"totalSpent": "1284.50",
"lastOrderAt": "2026-05-10T14:32:11.000Z",
"rfm": {
"r": 5,
"f": 4,
"m": 5,
"score": "5-4-5",
"segment": "champion"
},
"createdAt": "2025-08-01T09:00:00.000Z",
"loyaltyPointsBalance": 240,
"isLoyaltyMember": true,
"acquisitionSalesChannel": {
"id": "clsc_abc123",
"name": "Main storefront",
"connectionId": "vc_3n8Xk2p9QwErTyUiOpAsD"
},
"channelPublishes": [
{
"salesChannel": {
"id": "clsc_abc123",
"name": "Main storefront",
"connectionId": "vc_3n8Xk2p9QwErTyUiOpAsD"
},
"firstSeenAt": "2026-03-04T08:12:00.000Z",
"lastSeenAt": "2026-08-11T19:40:22.000Z"
}
],
"metadata": {
"erpId": "CUST-00421"
},
"loyaltyMembershipId": "clmem_abc123",
"platformConnections": [
{
"platformCode": "shopify",
"externalId": "7654321098765"
}
],
"addresses": [
{
"id": "addr_abc123",
"label": "Home",
"firstName": "Jane",
"lastName": "Doe",
"company": "Acme Inc",
"line1": "123 Main St",
"line2": "Apt 4B",
"city": "San Francisco",
"region": "CA",
"postalCode": "94103",
"country": "US",
"phone": "+14155552671",
"isDefault": true,
"createdAt": "2025-08-01T09:00:00.000Z",
"updatedAt": "2026-05-12T11:24:08.000Z"
}
],
"updatedAt": "2026-05-12T11:24:08.000Z"
}{
"statusCode": 400,
"code": "VALIDATION_FAILED",
"message": "name should not be empty, price must be a positive number",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/customers"
}{
"statusCode": 401,
"code": "UNAUTHORIZED",
"message": "Invalid or expired credential",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/customers"
}{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"message": "API key does not have the required scope: products:write",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/customers"
}{
"statusCode": 409,
"code": "CONFLICT",
"message": "The request conflicts with the current state of the resource",
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/customers"
}{
"statusCode": 429,
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retryAfterSeconds": 12
},
"timestamp": "2026-08-23T10:15:00.000Z",
"path": "/api/v1/store"
}