Activate a storefront customer account
Consumes a single-use activation token (hashed at rest, constant-time compared, TTL, `used_at` and bound-address checked in the service) and, in ONE transaction on the locked row, sets the scrypt password hash, stamps `terms_accepted_at`, writes the profile, fans out the marketing consent, flips `email_verified` and `has_account` to true, and bumps `customers.token_invalidated_at` so any leaked prior session dies. The account genuinely comes into existence here, so `customer.registered` is audited here. EVERY value written comes from THIS request's payload. Nothing the anonymous `register` call carried is persisted or replayed: inbox control proves the address, never agreement, and replaying a caller-supplied password, terms tick or profile at this point would be the identical forgery displaced one layer later. `acceptsTerms` MUST be true (a missing or false value is a 422 and nothing is activated); `marketingOptIn` is optional and NEVER blocks activation. The activation also clears the per-`(site, email)` login-failure backoff, so an attacker cannot pre-seed failures against an address to lock its real owner out of an account they never controlled. Only a row WITHOUT a password may be activated: a token presented for a row that already has one is rejected with the same uniform 400 as an invalid link. An invalid, expired, already-consumed, or address-mismatched token returns that same uniform 400 (no oracle). A successful activation also queues the buyer's welcome email in the SAME transaction (commerce slice G1, ADR-0050): the pending intent that `register` recorded is promoted, its localized content frozen, and one delivery job enqueued, so a rolled-back activation takes the welcome with it and no mail can describe an account that never activated. The mail itself leaves asynchronously, at most once per customer for the lifetime of the relationship: an activation that finds no intent, finds one a guest booking already promoted, or runs while the `customer_welcome_email` kill-switch is off sends nothing. Nothing about that outcome reaches this response, and the send itself happens out of band afterwards, so a transport failure cannot affect the activation. Behind the publishable key, the `customer_accounts` flag, and the customer-auth rate-limit bucket. Returns 200 with `{ customer, token }`: the session is issued HERE, to an actor who has now proven the inbox. The customer payload carries no `password_hash` and no `site_id`. > **Note:** This endpoint requires the e-commerce feature to be enabled on your site.
/customers/activation/confirmhttps://api.lynkow.com/storefront/{siteId}Request bodyrequired
phonestring3-40 characters
tokenstringrequiredRequired. Min 1 character
lastNamestring1-100 characters
passwordstringrequiredRequired. 8-100 characters
firstNamestring1-100 characters
acceptsTermsenumrequiredRequired. Terms-of-service acceptance, collected on the activation page. REQUIRED to be true: a missing or false value rejects the activation (422) and no account is created.. One of: true. Boolean
One of:
truemarketingOptInbooleanOptional marketing opt-in. When true, opts the customer in on every supported marketing channel.. Boolean