API ReferenceEndpointsCustomers

Customers

Customers represent shoppers who have created an account in your store. A customer carries email, addresses, OAuth provider connections, saved payment methods, and order history. Use these endpoints to manage customer profiles, look up orders, and run the customer authentication flow (login, register, password reset, OAuth).

Register customer

POST
/vc/{connectionId}/customers/register
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
emailstring

Customer email, unique per store. Becomes the login identifier.

passwordstring

Password (≥ 8 chars, must match PASSWORD_REGEX: at least one uppercase, lowercase, number, and special char).

Length8 <= length
firstName?string

First name.

lastName?string

Last name.

phone?string

Phone number (E.164 format recommended).

acceptsMarketing?boolean

Marketing consent flag (gates promo emails / SMS).

birthMonth?number

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. Optional on most channels; a channel with requireBirthday turned on rejects a registration that omits it.

Range1 <= value <= 12
birthDay?number

Birthday 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.

Range1 <= value <= 31
privacyPolicyAccepted?boolean

Privacy-policy acceptance flag, required by some stores before account creation. The frontend should require this checkbox when the store enables it.

referralCode?string

Loyalty referral share code (REF-XXXXXXXX) from a referrer's link. Validated asynchronously after registration, so an invalid code never fails the registration itself.

curl -X POST "https://api.brainerce.com/api/vc/string/customers/register" \  -H "origin: string" \  -H "Content-Type: application/json" \  -d '{    "email": "[email protected]",    "password": "CorrectHorse-Battery-Staple9"  }'
{
  "customer": {
    "id": "clcus_abc123",
    "email": "[email protected]",
    "firstName": "Jane",
    "lastName": "Doe",
    "phone": "string",
    "emailVerified": true
  },
  "token": "string",
  "expiresAt": "2026-10-07T09:00:00.000Z",
  "requiresVerification": false
}

Sign in customer

POST
/vc/{connectionId}/customers/login
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
emailstring

Customer email address used at registration.

passwordstring

Customer password (plaintext over HTTPS, and never logged).

curl -X POST "https://api.brainerce.com/api/vc/string/customers/login" \  -H "origin: string" \  -H "Content-Type: application/json" \  -d '{    "email": "[email protected]",    "password": "CorrectHorse-Battery-Staple9"  }'
{
  "customer": {
    "id": "clcus_abc123",
    "email": "[email protected]",
    "firstName": "Jane",
    "lastName": "Doe",
    "phone": "string",
    "emailVerified": true
  },
  "token": "string",
  "expiresAt": "2026-10-07T09:00:00.000Z",
  "requiresVerification": false
}

Reset password

POST
/vc/{connectionId}/customers/forgot-password
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
emailstring

Email address to send the password-reset link to. Endpoint always responds 200 (no enumeration); the email is only sent if a matching customer exists.

curl -X POST "https://api.brainerce.com/api/vc/string/customers/forgot-password" \  -H "origin: string" \  -H "Content-Type: application/json" \  -d '{    "email": "[email protected]"  }'
{
  "message": "If an account exists, a password reset email has been sent"
}

Reset password

POST
/vc/{connectionId}/customers/reset-password
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
tokenstring

Single-use reset token from the password-reset email. Tokens expire after 1 hour and are invalidated on use.

newPasswordstring

New password (≥ 8 chars, must match PASSWORD_REGEX: at least one uppercase, lowercase, number, and special char).

Length8 <= length
curl -X POST "https://api.brainerce.com/api/vc/string/customers/reset-password" \  -H "origin: string" \  -H "Content-Type: application/json" \  -d '{    "token": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",    "newPassword": "CorrectHorse-Battery-Staple9"  }'
{
  "message": "If an account exists, a password reset email has been sent"
}

Get customer profile

GET
/vc/{connectionId}/customers/me
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
authorizationstring
curl -X GET "https://api.brainerce.com/api/vc/string/customers/me" \  -H "origin: string" \  -H "authorization: string"
{
  "id": "clcus_abc123",
  "email": "[email protected]",
  "firstName": "Jane",
  "lastName": "Doe",
  "phone": "+14155552671",
  "emailVerified": true,
  "birthMonth": 4,
  "birthDay": 17
}

Update customer profile

PATCH
/vc/{connectionId}/customers/me
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
authorizationstring
firstName?string

First name.

lastName?string

Last name.

phone?string

Phone number (E.164 format recommended).

acceptsMarketing?boolean

Marketing consent flag, set by the customer themselves.

birthMonth?number

Birthday month (1-12). Month and day only, with no year: the birthday exists for the loyalty gift, and a year would be date-of-birth data we have no reason to hold. Send it together with birthDay or not at all, and send null for both to remove a stored birthday.

Range1 <= value <= 12
birthDay?number

Birthday day of month (1-31). Send it together with birthMonth or not at all, and send null for both to remove a stored birthday. The day must exist in that month, so 30 February is rejected.

Range1 <= value <= 31
curl -X PATCH "https://api.brainerce.com/api/vc/string/customers/me" \  -H "origin: string" \  -H "authorization: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "clcus_abc123",
  "email": "[email protected]",
  "firstName": "Jane",
  "lastName": "Doe",
  "phone": "+14155552671",
  "hasAccount": true,
  "emailVerified": true,
  "acceptsMarketing": false,
  "birthMonth": 4,
  "birthDay": 17,
  "role": "wholesale",
  "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"
    }
  ],
  "createdAt": "string",
  "updatedAt": "string"
}

Get checkout prefill data

GET
/vc/{connectionId}/customers/me/checkout-prefill
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
authorizationstring
curl -X GET "https://api.brainerce.com/api/vc/string/customers/me/checkout-prefill" \  -H "origin: string" \  -H "authorization: string"
{
  "customer": {
    "id": "clcus_abc123",
    "email": "[email protected]",
    "firstName": "Jane",
    "lastName": "Doe",
    "phone": "+14155552671",
    "emailVerified": true,
    "birthMonth": 4,
    "birthDay": 17
  },
  "defaultAddress": {
    "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"
  },
  "shippingAddress": {
    "email": "string",
    "firstName": "string",
    "lastName": "string",
    "company": "string",
    "line1": "string",
    "line2": "string",
    "city": "string",
    "region": "string",
    "postalCode": "string",
    "country": "string",
    "phone": "string"
  }
}

Get customer cart

Returns the logged-in customer's active cart, creating one if none exists. Requires a customer Bearer token in the Authorization header.

GET
/vc/{connectionId}/customers/me/cart
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Sales-channel connection ID (vc_*)

Header Parameters

origin?string
authorizationstring

Customer Bearer token (Bearer <token>).

curl -X GET "https://api.brainerce.com/api/vc/string/customers/me/cart" \  -H "origin: string" \  -H "authorization: string"
{
  "id": "clcrt_abc123",
  "sessionToken": "string",
  "customerId": "string",
  "status": "ACTIVE",
  "currency": "USD",
  "notes": "string",
  "analyticsClientId": "string",
  "analyticsSessionId": "string",
  "subtotal": "39.80",
  "discountAmount": "5.00",
  "ruleDiscountAmount": "0",
  "promoDiscountTotal": "0",
  "couponCode": "WELCOME10",
  "appliedDiscounts": [
    {
      "ruleId": "cldr_abc123",
      "ruleName": "string",
      "type": "PERCENTAGE_OFF",
      "discountAmount": "5.00",
      "description": "string"
    }
  ],
  "nudges": [
    {
      "ruleId": "cldr_abc123",
      "text": "string",
      "type": "AMOUNT_NEEDED",
      "amountNeeded": "12.00",
      "quantityNeeded": 1
    }
  ],
  "items": [
    {
      "productId": "clx1234567890",
      "variantId": "string",
      "quantity": 2
    }
  ],
  "itemCount": 3,
  "hasPriceChanges": false,
  "hasUnavailableItems": false,
  "unavailableItemIds": [],
  "expiresAt": "string",
  "createdAt": "string",
  "updatedAt": "string",
  "reservation": {
    "hasReservation": true,
    "expiresAt": "string",
    "remainingSeconds": 540,
    "strategy": "ON_CART",
    "countdownMessage": "string"
  },
  "recommendations": {},
  "upgrades": {},
  "bundles": {}
}

List customer addresses

GET
/vc/{connectionId}/customers/me/addresses
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
authorizationstring
curl -X GET "https://api.brainerce.com/api/vc/string/customers/me/addresses" \  -H "origin: string" \  -H "authorization: string"
[
  {
    "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"
  }
]

Add customer address

POST
/vc/{connectionId}/customers/me/addresses
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
authorizationstring
label?string

Friendly label shown in the address picker UI.

firstNamestring

First name on the address.

lastNamestring

Last name on the address.

company?string

Company name (B2B).

line1string

Street address line 1.

line2?string

Street address line 2 (apt/suite).

citystring

City.

region?string

State / Province.

postalCodestring

Postal / ZIP code.

countrystring

ISO 3166-1 alpha-2 country code.

phone?string

Phone (E.164 recommended).

isDefault?boolean

Mark this address as the customer's default. Promoting a new default automatically demotes the previous one.

curl -X POST "https://api.brainerce.com/api/vc/string/customers/me/addresses" \  -H "origin: string" \  -H "authorization: string" \  -H "Content-Type: application/json" \  -d '{    "firstName": "Jane",    "lastName": "Doe",    "line1": "123 Main St",    "city": "San Francisco",    "postalCode": "94103",    "country": "US"  }'
{
  "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"
}

Update customer address

PATCH
/vc/{connectionId}/customers/me/addresses/{addressId}
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring
addressIdstring

Header Parameters

originstring
authorizationstring
label?string

Friendly label.

firstName?string

First name.

lastName?string

Last name.

company?string

Company.

line1?string

Street line 1.

line2?string

Street line 2.

city?string

City.

region?string

State / Province.

postalCode?string

Postal / ZIP.

country?string

ISO country code.

phone?string

Phone.

isDefault?boolean

Promote this address to be the customer's default.

curl -X PATCH "https://api.brainerce.com/api/vc/string/customers/me/addresses/string" \  -H "origin: string" \  -H "authorization: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "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"
}

Delete customer address

DELETE
/vc/{connectionId}/customers/me/addresses/{addressId}
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring
addressIdstring

Header Parameters

originstring
authorizationstring
curl -X DELETE "https://api.brainerce.com/api/vc/string/customers/me/addresses/string" \  -H "origin: string" \  -H "authorization: string"
{
  "success": true
}

List customer orders

GET
/vc/{connectionId}/customers/me/orders
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Query Parameters

pagestring
limitstring

Header Parameters

originstring
authorizationstring
curl -X GET "https://api.brainerce.com/api/vc/string/customers/me/orders?page=string&limit=string" \  -H "origin: string" \  -H "authorization: string"
{
  "data": [
    {
      "id": "clord_abc123",
      "orderNumber": "ORD-20260907-0012",
      "status": "processing",
      "totalAmount": "46.96",
      "subtotal": "39.80",
      "discountAmount": "0",
      "couponCode": "WELCOME10",
      "couponDiscount": "string",
      "ruleDiscountAmount": "string",
      "appliedDiscounts": [
        {}
      ],
      "shippingAmount": "5.00",
      "taxAmount": "0",
      "currency": "ILS",
      "createdAt": "string",
      "itemCount": 2,
      "items": [
        {
          "productId": "clprd_abc123",
          "variantId": "string",
          "sku": "string",
          "name": "string",
          "quantity": 2,
          "price": "19.90",
          "unitPrice": "19.90",
          "totalPrice": "39.80",
          "image": "string",
          "customizations": {},
          "modifiers": [
            {}
          ]
        }
      ],
      "hasDownloads": false,
      "notes": "string",
      "tenders": [
        {
          "id": "cltnd_abc123",
          "type": "GIFT_CARD",
          "amountBase": "25.00",
          "currencyBase": "ILS",
          "giftCard": {}
        }
      ]
    }
  ],
  "meta": {
    "page": 1,
    "limit": 10,
    "total": 137,
    "totalPages": 14
  }
}

List order downloads

GET
/vc/{connectionId}/customers/me/orders/{orderId}/downloads
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring
orderIdstring

Query Parameters

checkout_idstring

Header Parameters

originstring
authorizationstring
curl -X GET "https://api.brainerce.com/api/vc/string/customers/me/orders/string/downloads?checkout_id=string" \  -H "origin: string" \  -H "authorization: string"
[
  {
    "productName": "Wallpaper pack",
    "fileName": "wallpapers.zip",
    "downloadUrl": "https://api.brainerce.com/api/stores/clst_abc/downloads/eyJ...",
    "downloadsUsed": 0,
    "downloadLimit": 5,
    "expiresAt": "string"
  }
]

Verify email

POST
/vc/{connectionId}/customers/verify-email
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
authorizationstring
codestring

6-digit verification code from the email-verification message. Codes expire after 15 minutes.

Length6 <= length
curl -X POST "https://api.brainerce.com/api/vc/string/customers/verify-email" \  -H "origin: string" \  -H "authorization: string" \  -H "Content-Type: application/json" \  -d '{    "code": "482917"  }'
{
  "verified": true,
  "message": "Email verified",
  "customer": {
    "id": "clcus_abc123",
    "email": "[email protected]",
    "firstName": "Jane",
    "lastName": "Doe",
    "phone": "string",
    "emailVerified": true
  },
  "token": "string",
  "expiresAt": "string",
  "requiresVerification": true
}

Resend verification email

POST
/vc/{connectionId}/customers/resend-verification
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
authorizationstring
curl -X POST "https://api.brainerce.com/api/vc/string/customers/resend-verification" \  -H "origin: string" \  -H "authorization: string"
{
  "message": "Verification email sent",
  "token": "string"
}

List o auth providers

GET
/vc/{connectionId}/oauth/providers
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
curl -X GET "https://api.brainerce.com/api/vc/string/oauth/providers" \  -H "origin: string"
{
  "providers": [
    "GOOGLE",
    "FACEBOOK"
  ]
}

Get o auth authorize url

GET
/vc/{connectionId}/oauth/{provider}/authorize
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring
providerstring

Query Parameters

redirectUrlstring

Header Parameters

originstring
curl -X GET "https://api.brainerce.com/api/vc/string/oauth/string/authorize?redirectUrl=string" \  -H "origin: string"
{
  "authorizationUrl": "string",
  "state": "string",
  "provider": "GOOGLE"
}

Handle o auth callback

GET
/vc/{connectionId}/oauth/{provider}/callback
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring
providerstring

Query Parameters

codestring
statestring
errorstring
error_descriptionstring

Header Parameters

originstring
curl -X GET "https://api.brainerce.com/api/vc/string/oauth/string/callback?code=string&state=string&error=string&error_description=string" \  -H "origin: string"
{
  "error": "access_denied",
  "errorDescription": "string",
  "provider": "GOOGLE",
  "customer": {
    "id": "clcus_abc123",
    "email": "[email protected]",
    "firstName": "Jane",
    "lastName": "Doe",
    "phone": "string",
    "emailVerified": true
  },
  "token": "string",
  "expiresAt": "string",
  "isNewCustomer": false,
  "linkedToExisting": true,
  "redirectUrl": "/account"
}

List customer o auth connections

GET
/vc/{connectionId}/customers/me/oauth-connections
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Header Parameters

originstring
authorizationstring
curl -X GET "https://api.brainerce.com/api/vc/string/customers/me/oauth-connections" \  -H "origin: string" \  -H "authorization: string"
{
  "connections": [
    {
      "id": "cloac_abc123",
      "provider": "GOOGLE",
      "email": "[email protected]",
      "createdAt": "string"
    }
  ]
}
POST
/vc/{connectionId}/oauth/{provider}/link
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring
providerstring

Query Parameters

redirectUrlstring

Header Parameters

originstring
authorizationstring
curl -X POST "https://api.brainerce.com/api/vc/string/oauth/string/link?redirectUrl=string" \  -H "origin: string" \  -H "authorization: string"
{
  "authorizationUrl": "string",
  "state": "string",
  "provider": "GOOGLE"
}
DELETE
/vc/{connectionId}/oauth/{provider}/link
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring
providerstring

Header Parameters

originstring
authorizationstring
curl -X DELETE "https://api.brainerce.com/api/vc/string/oauth/string/link" \  -H "origin: string" \  -H "authorization: string"
{
  "success": true
}

List contact forms

Returns the contact forms the merchant has published to this sales channel — key, name and description only, not the field schema. Fetch the schema for one form with GET /contact-forms/{formKey}, then submit it to POST /inquiries. Every store has a form keyed main; a merchant can add more (a support form, a wholesale enquiry form) and this list is how you discover them. Inactive forms are omitted. Rate limited to 60 requests per minute.

GET
/vc/{connectionId}/contact-forms
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Sales-channel connection ID (vc_*)

Header Parameters

origin?string
curl -X GET "https://api.brainerce.com/api/vc/string/contact-forms" \  -H "origin: string"
[
  {
    "key": "main",
    "name": "Contact us",
    "isDefault": true
  }
]

Get contact form schema

Returns the full field schema for one contact form: every field's key, type, label, placeholder, whether it is required, and its validation bounds. Render one control per field in the order given, then submit the collected values as the fields object of POST /inquiries — the server re-validates against this same schema and strips unknown keys, so a form built from a stale copy of the schema will have values silently dropped. Labels and placeholders come back translated for locale, falling back to the store language. Rate limited to 60 requests per minute.

GET
/vc/{connectionId}/contact-forms/{formKey}
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Sales-channel connection ID (vc_*)

formKeystring

Form to fetch, as listed by GET /contact-forms. An empty segment resolves to the default form, main.

Query Parameters

locale?string

Locale for translated labels and placeholders (e.g. "he"). Falls back to the store language.

Header Parameters

origin?string
curl -X GET "https://api.brainerce.com/api/vc/string/contact-forms/main?locale=string" \  -H "origin: string"
{
  "id": "clcf_abc123",
  "key": "main",
  "name": "string",
  "description": "string",
  "submitButton": "Send",
  "successMessage": "Thanks, we will be in touch.",
  "fields": [
    {
      "key": "message",
      "type": "TEXTAREA",
      "label": "Your message",
      "placeholder": "string",
      "helpText": "string",
      "isRequired": true,
      "enumValues": [
        {
          "value": "red",
          "label": "Red"
        }
      ],
      "validation": {
        "minLength": 2,
        "maxLength": 500,
        "min": 1,
        "max": 99,
        "pattern": "string",
        "patternMessage": "string"
      },
      "defaultValue": "string",
      "width": "FULL"
    }
  ]
}

Create inquiry

Submits a contact-form message to the merchant. Two payload shapes are accepted. The modern one is formKey plus a fields object keyed by the field keys returned by GET /contact-forms/{formKey} — every value is re-validated against that schema server-side and unknown keys are stripped. The legacy flat shape (name, email, subject, message, phone) is still accepted for older storefronts. A request may send both; fields wins and the flat fields fill in whatever it omits.

No authentication. Include the honeypot field in your markup as a hidden, empty input — any non-empty value rejects the request. Rate limited to 3 submissions per minute, which is deliberately tight: this route mails the merchant.

POST
/vc/{connectionId}/inquiries
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Sales-channel connection ID (vc_*)

Header Parameters

origin?string
name?string

Sender name. Legacy flat shape — prefer fields keyed by the form schema. Mirrored onto the same column fields.name writes, so sending both is redundant, not additive.

Lengthlength <= 120
email?string

Sender email, so the merchant can reply. Legacy flat shape — prefer fields.

Lengthlength <= 254
subject?string

Subject line. Legacy flat shape — prefer fields.

Lengthlength <= 200
message?string

Message body. Legacy flat shape — prefer fields.

Lengthlength <= 10000
phone?string

Sender phone number, when the form collects one. Legacy flat shape.

Lengthlength <= 40
formKey?string

Which contact form this submission belongs to, as listed by GET /contact-forms. Defaults to main. Lowercase letters, digits, _ and -; must start with a letter.

Match^[a-z][a-z0-9_-]{0,39}$
fields?object

The submitted values, keyed by the field keys from GET /contact-forms/{formKey}. Every value is re-validated against that form's schema server-side and any key the schema does not declare is STRIPPED SILENTLY — a form built from a stale schema loses those values without an error. This is the preferred shape; the flat fields above exist only for older storefronts.

Empty Object

locale?string

Storefront locale at submission time (e.g. "he"). Sets the language of the confirmation email and how the inquiry is displayed to the merchant. Falls back to the store language.

Lengthlength <= 10
sourceMetadata?object

Arbitrary provenance: referrer, UTM parameters, the page the form sat on. Stored as JSON for your own reporting, never interpreted.

Empty Object

customerId?string

Logged-in customer this inquiry belongs to, when the storefront knows one. Optional — contact forms are open to guests.

Lengthlength <= 120
metadata?object

Arbitrary key/value data stored alongside the inquiry.

Empty Object

honeypot?string

Anti-bot honeypot. Render it hidden and leave it empty; a filled value rejects the request. Bots complete every text input, humans never see this one.

curl -X POST "https://api.brainerce.com/api/vc/string/inquiries" \  -H "origin: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "clinq_abc123",
  "status": "NEW",
  "createdAt": "string"
}

{
  "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/vc/{connectionId}/inquiries"
}

Subscribe to marketing email

Signs an address up for the store's marketing email — the newsletter popup, the footer capture bar, an exit-intent modal.

This does NOT grant consent. It creates the contact and mails a confirmation link, and the address stays unmailable until the recipient clicks it, so a forged or mistyped submission costs one email and nothing more. Do not tell the shopper they are subscribed; tell them to check their inbox.

The response is identical whatever the address's history — already subscribed, already a customer, entirely unknown — so the endpoint cannot be used to probe who shops at this store. A per-address cooldown suppresses repeat confirmation mail to the same victim.

No authentication. Include the honeypot field as a hidden, empty input. Rate limited to 3 requests per minute.

POST
/vc/{connectionId}/marketing/subscribe
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Sales-channel connection ID (vc_*)

Header Parameters

origin?string
emailstring

Address to subscribe. Lowercased and trimmed server-side. A confirmation email is sent to it; consent is granted only when that link is clicked.

Lengthlength <= 254
firstName?string

Given name, when the form collects one. Greets the recipient in the confirmation email and is stored on the customer record.

Lengthlength <= 100
lastName?string

Family name, when the form collects one.

Lengthlength <= 100
locale?string

Storefront locale at submission time (e.g. "he"). Picks the language of the confirmation email and is saved as the preferred locale on a newly created customer. Falls back to the store language.

Lengthlength <= 10
source?string

Where on the storefront the signup came from, such as "popup", "footer", or "exit-intent". Free-form; stored for your own reporting, never interpreted.

Lengthlength <= 60
sourceMetadata?object

Arbitrary provenance: referrer, UTM parameters, the page the popup fired on. Stored as JSON on a newly created customer record.

Empty Object

honeypot?string

Anti-bot honeypot. Render it hidden and leave it empty; a filled value rejects the request. Bots complete every text input, humans never see this one.

curl -X POST "https://api.brainerce.com/api/vc/string/marketing/subscribe" \  -H "origin: string" \  -H "Content-Type: application/json" \  -d '{    "email": "[email protected]"  }'
{
  "ok": true
}

Read the newsletter signup benefit

The offer to show beside a newsletter signup field: the discount, how long the coupon lasts, any minimum order, whether it is limited to a first order, and the merchant-written headline and terms.

Returns null when the store offers no benefit — render the plain signup form in that case, and do not promise anything.

Show these terms on the form, then post to /marketing/subscribe. The coupon does not exist yet at that point: it is created only when the shopper clicks the link in the confirmation email, and it is then emailed to them. Tell them to check their inbox rather than telling them they have a coupon.

Carries no information about any individual and accepts no email address, so it cannot be used to check whether someone has already subscribed or already claimed a benefit.

GET
/vc/{connectionId}/newsletter-benefit
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Sales-channel connection ID (vc_*)

Query Parameters

localestring

Header Parameters

origin?string
curl -X GET "https://api.brainerce.com/api/vc/string/newsletter-benefit?locale=string" \  -H "origin: string"
Empty

Read the active newsletter signup benefit

The offer to show beside a newsletter signup field: discount, validity, minimum order, whether it is first-order only, and the merchant-written headline and terms.

Returns null when no benefit is active — render the plain signup form in that case.

Carries no information about any individual and accepts no email address, so it cannot be used to test whether someone has already subscribed or claimed. Tell the shopper the terms shown here, then post to /marketing/subscribe; the coupon only exists after they click the link in the confirmation email.

GET
/stores/{storeId}/newsletter-benefit

Path Parameters

storeIdstring

Store ID

Query Parameters

locale?string

Preferred language for the headline and terms. Falls back to the store language.

curl -X GET "https://api.brainerce.com/api/stores/string/newsletter-benefit?locale=string"
Empty

Create a new customer

Honors Idempotency-Key header. See /docs/api/idempotency.

POST
/v1/customers
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
emailstring

Customer email, unique per store. Used for login (when password is set) and order lookup.

phone?string

Phone number (E.164 format recommended for SMS notifications).

firstName?string

First name.

lastName?string

Last name.

password?string

Plaintext 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).

Length8 <= length
acceptsMarketing?boolean

Marketing consent flag. Required for transactional vs marketing email split.

birthMonth?number

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.

Range1 <= value <= 12
birthDay?number

Birthday 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.

Range1 <= value <= 31
tags?array<string>

Free-form tags for segmentation (VIP, B2B, beta-tester, …).

role?string

Free-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.

Lengthlength <= 50
acquisitionSalesChannelId?string

FIRST-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.

metadata?object

Arbitrary JSON metadata. Useful for storing integration-specific IDs (your ERP customer number, CRM contact id, …).

Empty Object

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"
}

Get a customer by ID

GET
/v1/customers/{id}
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Path Parameters

idstring

Customer ID

curl -X GET "https://api.brainerce.com/api/v1/customers/string"
{
  "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",
  "marketingStatus": "SUBSCRIBED"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}"
}

{
  "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/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}"
}

Update a customer

Supports Idempotency-Key header for safe retries.

PATCH
/v1/customers/{id}
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Path Parameters

idstring

Customer ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
email?string

Customer email. Changing email re-triggers verification (the new address must be verified before sensitive operations).

phone?string

Phone number.

firstName?string

First name.

lastName?string

Last name.

acceptsMarketing?boolean

Marketing consent flag.

birthMonth?number | null

Birthday month (1-12) for the loyalty birthday gift. Month and day only, no year. Send null for BOTH fields to remove a stored birthday; sending neither leaves it untouched, so an edit form that simply omits an emptied field can correct a birthday but never clear one.

birthDay?number | null

Birthday day of month (1-31). Send null for BOTH fields to remove a stored birthday; sending neither leaves it untouched, so an edit form that simply omits an emptied field can correct a birthday but never clear one.

tags?array<string>

Replace the customer's tag list. Pass [] to clear all tags.

role?string

Free-form customer segment/role set by the merchant (e.g. "wholesale", "vip", "ambassador"). Pass an empty string to clear it. Not settable by the customer themselves: admin dashboard/API only.

Lengthlength <= 50
acquisitionSalesChannelId?string

Override the FIRST-TOUCH sales channel, the channel this customer came in through. Normally stamped automatically at registration / OAuth / checkout and then never changed; this is the merchant correction for customers created from the dashboard or imported from a file, where no channel was ever known. Accepts the internal SalesChannel id or the public vc_* connectionId. Pass an empty string to clear it back to "unknown". Does NOT change which channels the customer is active in: that is the publish/unpublish endpoints.

metadata?object

Replace metadata JSON (full replacement, not merge).

Empty Object

curl -X PATCH "https://api.brainerce.com/api/v1/customers/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "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/{id}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}"
}

{
  "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/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}"
}

Get a customer by email

GET
/v1/customers/by-email
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Query Parameters

emailstring
curl -X GET "https://api.brainerce.com/api/v1/customers/by-email?email=string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/by-email"
}

{
  "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/by-email"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/by-email"
}

Customer login

Supports Idempotency-Key header for safe retries.

POST
/v1/customers/login
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
emailstring

Customer email address used at registration.

passwordstring

Customer password (plaintext over HTTPS, and never logged).

curl -X POST "https://api.brainerce.com/api/v1/customers/login" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "email": "[email protected]",    "password": "CorrectHorse-Battery-Staple9"  }'
{
  "customer": {
    "id": "string",
    "email": "[email protected]",
    "firstName": "string",
    "lastName": "string",
    "phone": "string",
    "emailVerified": true
  },
  "token": "string",
  "expiresAt": "2019-08-24T14:15:22Z",
  "requiresVerification": true
}

{
  "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/login"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/login"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/login"
}

Customer registration

Supports Idempotency-Key header for safe retries.

POST
/v1/customers/register
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
emailstring

Customer email, unique per store. Becomes the login identifier.

passwordstring

Password (≥ 8 chars, must match PASSWORD_REGEX: at least one uppercase, lowercase, number, and special char).

Length8 <= length
firstName?string

First name.

lastName?string

Last name.

phone?string

Phone number (E.164 format recommended).

acceptsMarketing?boolean

Marketing consent flag (gates promo emails / SMS).

birthMonth?number

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. Optional on most channels; a channel with requireBirthday turned on rejects a registration that omits it.

Range1 <= value <= 12
birthDay?number

Birthday 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.

Range1 <= value <= 31
privacyPolicyAccepted?boolean

Privacy-policy acceptance flag, required by some stores before account creation. The frontend should require this checkbox when the store enables it.

referralCode?string

Loyalty referral share code (REF-XXXXXXXX) from a referrer's link. Validated asynchronously after registration, so an invalid code never fails the registration itself.

curl -X POST "https://api.brainerce.com/api/v1/customers/register" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "email": "[email protected]",    "password": "CorrectHorse-Battery-Staple9"  }'
{
  "customer": {
    "id": "string",
    "email": "[email protected]",
    "firstName": "string",
    "lastName": "string",
    "phone": "string",
    "emailVerified": true
  },
  "token": "string",
  "expiresAt": "2019-08-24T14:15:22Z",
  "requiresVerification": true
}

{
  "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/register"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/register"
}

Request password reset

Supports Idempotency-Key header for safe retries.

POST
/v1/customers/forgot-password
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
emailstring

Email address to send the password-reset link to. Endpoint always responds 200 (no enumeration); the email is only sent if a matching customer exists.

curl -X POST "https://api.brainerce.com/api/v1/customers/forgot-password" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "email": "[email protected]"  }'
{
  "message": "If an account exists with that email, a reset link has been sent"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/forgot-password"
}

Reset password with token

Supports Idempotency-Key header for safe retries.

POST
/v1/customers/reset-password
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
tokenstring

Single-use reset token from the password-reset email. Tokens expire after 1 hour and are invalidated on use.

newPasswordstring

New password (≥ 8 chars, must match PASSWORD_REGEX: at least one uppercase, lowercase, number, and special char).

Length8 <= length
curl -X POST "https://api.brainerce.com/api/v1/customers/reset-password" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "token": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",    "newPassword": "CorrectHorse-Battery-Staple9"  }'
{
  "message": "If an account exists with that email, a reset link has been sent"
}

{
  "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/reset-password"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/reset-password"
}

Get customer addresses

GET
/v1/customers/{id}/addresses
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Path Parameters

idstring

Customer ID

curl -X GET "https://api.brainerce.com/api/v1/customers/string/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"
  }
]

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses"
}

{
  "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/{id}/addresses"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses"
}

Add a customer address

Supports Idempotency-Key header for safe retries.

POST
/v1/customers/{id}/addresses
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Path Parameters

idstring

Customer ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
label?string

Friendly label shown in the address picker UI.

firstNamestring

First name on the address.

lastNamestring

Last name on the address.

company?string

Company name (B2B).

line1string

Street address line 1.

line2?string

Street address line 2 (apt/suite).

citystring

City.

region?string

State / Province.

postalCodestring

Postal / ZIP code.

countrystring

ISO 3166-1 alpha-2 country code.

phone?string

Phone (E.164 recommended).

isDefault?boolean

Mark this address as the customer's default. Promoting a new default automatically demotes the previous one.

curl -X POST "https://api.brainerce.com/api/v1/customers/string/addresses" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "firstName": "Jane",    "lastName": "Doe",    "line1": "123 Main St",    "city": "San Francisco",    "postalCode": "94103",    "country": "US"  }'
{
  "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"
}

{
  "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/{id}/addresses"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses"
}

{
  "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/{id}/addresses"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses"
}

Update a customer address

Supports Idempotency-Key header for safe retries.

PATCH
/v1/customers/{id}/addresses/{addressId}
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Path Parameters

idstring

Customer ID

addressIdstring

Address ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
label?string

Friendly label.

firstName?string

First name.

lastName?string

Last name.

company?string

Company.

line1?string

Street line 1.

line2?string

Street line 2.

city?string

City.

region?string

State / Province.

postalCode?string

Postal / ZIP.

country?string

ISO country code.

phone?string

Phone.

isDefault?boolean

Promote this address to be the customer's default.

curl -X PATCH "https://api.brainerce.com/api/v1/customers/string/addresses/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "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"
}

{
  "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/{id}/addresses/{addressId}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses/{addressId}"
}

{
  "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/{id}/addresses/{addressId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses/{addressId}"
}

Delete a customer address

Supports Idempotency-Key header for safe retries.

DELETE
/v1/customers/{id}/addresses/{addressId}
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → Authentication → API Keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Path Parameters

idstring

Customer ID

addressIdstring

Address ID

Header Parameters

Idempotency-Key?string

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 Conflict with code: "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 GET returns 400 with code: "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.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/customers/string/addresses/string" \  -H "Idempotency-Key: string"
Empty

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses/{addressId}"
}

{
  "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/{id}/addresses/{addressId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/customers/{id}/addresses/{addressId}"
}

On this page

No Headings