Service for the storefront buyer-authentication surface: register, log in, read the
current buyer and their bookings, manage their address book, reset a forgotten
password, and manage marketing consent. Accessible via lynkow.customers.
Requires a publishable key: pass publishableKey to createClient(...) so every
request carries it (customer routes reject calls without a valid key). The customer
session token is a SEPARATE identity track: activation.confirm and login return
it and write it to shared in-memory client state. Authenticated customer methods
attach it as Authorization: Bearer <token> ALONGSIDE the publishable key, and
signed-in reservation creation reads the same state so a new booking appears in
me() right away. logout clears the shared value. Persist the token in your own
storage (for example a cookie) and seed it back via createClient({ customerToken })
to keep the buyer signed in across a reload or SSR. The SDK never persists or logs it.
Signing up is TWO steps and no session exists between them: register only asks for
an activation link to be sent, and activation.confirm (called from the page that
link lands on) sets the password and returns the session.
Access via: lynkow.customers
Methods
6 methods
login
login(input: CustomerLoginInput): Promise<CustomerAuthResult>Authenticates a buyer by email plus password. On success the returned token is
STORED and attached on later authenticated calls, and { customer, token } is
returned. Any failure (wrong password, unknown email) surfaces as one uniform
LynkowError with no field that reveals whether the email exists. Login is not
spam-gated (it is rate-limited server-side), so no honeypot fields are sent.
Parameter | Type | Description |
|---|---|---|
|
| CustomerLoginInput: |
Returns: Promise<CustomerAuthResult>
const { customer, token } = await lynkow.customers.login({
email: '[email protected]',
password: 'a-strong-passphrase',
})
saveToken(token)logout
logout(options?: CustomerLogoutOptions): Promise<void>Logs the buyer out. By default this is a local token discard with no network
round-trip: the shared token is cleared so later customer and reservation calls go
out without a Bearer header. With { everywhere: true } it requires a stored token,
first revokes EVERY session token issued to this buyer server-side, then clears the
local token. Resolves to void.
Parameter | Type | Description |
|---|---|---|
|
| Optional CustomerLogoutOptions: |
Returns: Promise<void>
await lynkow.customers.logout() // discard the token on this client only
await lynkow.customers.logout({ everywhere: true }) // also revoke all sessions server-sideme
me(): Promise<CustomerSession>Returns the current buyer plus at most their 100 most recent bookings.
Retro-linked guest bookings are withheld until customer.emailVerified is
true. Not cached: the booking list changes as the buyer books. Requires a
stored session token.
Returns: Promise<CustomerSession>
const { customer, bookings } = await lynkow.customers.me()
for (const booking of bookings) {
console.log(`${booking.reference}: ${booking.status} at ${booking.startsAt}`)
}register
register(input: CustomerRegisterInput, options?: CustomerSpamOptions): Promise<void>Step 1 of signing up: asks for an activation link to be sent to email. It takes
the address and nothing else, creates no session, and does NOT sign the buyer in.
Step 2 is CustomersService.activation.confirm, called from the page the
emailed link lands on, where the password is set and the session is issued.
ALWAYS resolves the same way whether or not the address already has an account, so it reveals nothing about who is registered. Do NOT branch your UI on the outcome: show one message either way, or you re-create client-side the account-enumeration signal the API removed.
Honeypot and timestamp anti-spam fields are injected automatically; on a
reCAPTCHA-protected storefront pass options.recaptchaToken. Passing a password,
acceptsTerms, a profile or marketingOptIn here has NO effect: the server SILENTLY
STRIPS them and still returns the same success, so do not write a catch expecting a
rejection. They are simply discarded, never stored and never replayed. Collect them at
activation.confirm instead.
Parameter | Type | Description |
|---|---|---|
|
| CustomerRegisterInput: |
|
| Optional CustomerSpamOptions: |
Returns: Promise<void>
await lynkow.customers.register({ email: '[email protected]' })
// Same message on every outcome: never branch on whether the email is known.
show('Check your inbox. If this address can be registered, a link is on its way.')resendVerification
resendVerification(options?: BaseRequestOptions): Promise<void>Re-sends the verification mail for the signed-in buyer. The API returns no content both when it dispatches a fresh link and when the email is already verified, so this method does not expose verification state. Requires a stored session token.
Parameter | Type | Description |
|---|---|---|
|
| Optional request options. |
Returns: Promise<void>
await lynkow.customers.resendVerification({ locale: 'fr' })verifyEmail
verifyEmail(token: string): Promise<CustomerEmailVerificationResult>Confirms a buyer's email with the single-use token from the verification mail,
flipping emailVerified (and, for a claimed guest row, hasAccount) to true.
This is what makes retro-linked guest bookings, placed with the same email before
registration, appear in me().bookings. An invalid, expired, or already-used token
surfaces as one uniform LynkowError (no oracle). No session token is required, and
confirming does NOT sign the buyer in (they log in normally afterward). Not spam-gated
(the token is unguessable; the endpoint is rate-limited server-side).
Parameter | Type | Description |
|---|---|---|
|
| The single-use, short-lived email-verification token from the emailed link. |
Returns: Promise<CustomerEmailVerificationResult>
await lynkow.customers.verifyEmail(tokenFromEmailLink)
const { bookings } = await lynkow.customers.me() // retro-linked guest bookings now visible