Register a storefront customer account
Takes an `email` and an optional `locale`, and NOTHING else. It ALWAYS returns an identical generic 200 `{ ok: true }`, mails an activation link to the address, and causes no other observable effect. It never installs a credential, never mutates an existing row, and never issues a session token: an anonymous caller proves nothing about the address they submit, so nothing they submit is allowed to cause an effect. The password, terms acceptance, profile and marketing consent are collected FRESH on the inbox-linked activation page (`activation/confirm`) from whoever proves they control the address, and are applied there atomically. The response is byte-identical on EVERY branch: a fresh address, an address backed by an unclaimed guest row, an address that already has an account, an address whose activation link is already live, a soft-deleted or GDPR-anonymized row, and a budget exhaustion. There is no field-scoped duplicate-email 422 any more: that was a live user-enumeration oracle, and it is DELETED rather than narrowed. Duplicate accounts remain impossible via the partial-unique `(site_id, email)` index, so only the disclosure went away. All email-dependent work (lookup, resolve-or-create, token issuance, mail) runs in a DETACHED worker AFTER the response, so response latency cannot leak whether the address exists either (no timing oracle), mirroring `password-reset/request`. Optional `locale` selects the activation-mail language and is never rejected: any value maps to `en`, `fr`, `es`, `de` or `pt`, a regional variant to its base language (`pt-BR` gives `pt`) and any other non-empty string to English. Omitted, empty, or not a string: the site's default locale is used and mapped the same way. When (and only when) the worker CREATES the customer row, it also records a pending, content-free welcome intent in the same transaction (commerce slice G1, ADR-0050). No welcome mail is rendered or sent at this point: the account does not exist yet and the activation link is already in flight, so the intent simply carries the resolved locale until `activation/confirm` (or an earlier guest booking) promotes it. Reusing an existing customer records nothing, and the whole side effect is inert while the `customer_welcome_email` kill-switch is off. None of it is observable here: the response, its latency, and the activation mail are all unchanged. Behind the publishable key, the `customer_accounts` flag, spam protection, and the customer-auth rate-limit bucket. Returns 200 with `{ ok: true }`. > **Note:** This endpoint requires the e-commerce feature to be enabled on your site.
/customers/registerhttps://api.lynkow.com/storefront/{siteId}Request bodyrequired
emailstringrequiredRequired. Valid email address
localestringBuyer-email language. Never rejected: an exact match on en, fr, es, de or pt is kept, a regional variant falls back to its base language (pt-BR gives pt), and any other non-empty string falls back to English. Omitted, empty, or not a string: the site default language is used and mapped the same way