> For the complete Lynkow documentation index in agent-friendly format, see [llms.txt](/llms.txt).

# CustomersService

**Publié le** : 2026-08-20
**Catégorie** : Services

# CustomersService

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`

```typescript
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 |
| --- | --- | --- |
| `input` | `CustomerLoginInput` | CustomerLoginInput: `email` and `password`. |


Returns: `Promise<CustomerAuthResult>`

```typescript
const { customer, token } = await lynkow.customers.login({
  email: 'alice@example.com',
  password: 'a-strong-passphrase',
})
saveToken(token)
```

---

### `logout`

```typescript
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 |
| --- | --- | --- |
| `options` | `CustomerLogoutOptions` | Optional CustomerLogoutOptions: `everywhere` to also revoke all server-side sessions. |


Returns: `Promise<void>`

```typescript
await lynkow.customers.logout() // discard the token on this client only
await lynkow.customers.logout({ everywhere: true }) // also revoke all sessions server-side
```

---

### `me`

```typescript
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>`

```typescript
const { customer, bookings } = await lynkow.customers.me()
for (const booking of bookings) {
  console.log(`${booking.reference}: ${booking.status} at ${booking.startsAt}`)
}
```

---

### `register`

```typescript
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 |
| --- | --- | --- |
| `input` | `CustomerRegisterInput` | CustomerRegisterInput: `email`, and nothing else.<br>The anti-spam `_ts` uses `options.formStartedAt` when supplied. Otherwise it uses<br>client creation time clamped to at most 55 minutes before this request: a freshly<br>created server client can still hit the API's minimum fill-time rejection, while a<br>long-lived browser client does not become permanently expired. |
| `options` | `CustomerSpamOptions` | Optional CustomerSpamOptions: `formStartedAt`, `recaptchaToken` (required on reCAPTCHA-protected storefronts), a `locale` override selecting the activation-mail language, and `fetchOptions` (e.g. an `AbortSignal`). |


Returns: `Promise<void>`

```typescript
await lynkow.customers.register({ email: 'alice@example.com' })
// 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`

```typescript
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 |
| --- | --- | --- |
| `options` | `BaseRequestOptions` | Optional request options. `locale` selects the buyer-mail language; `fetchOptions` can carry an `AbortSignal`. |


Returns: `Promise<void>`

```typescript
await lynkow.customers.resendVerification({ locale: 'fr' })
```

---

### `verifyEmail`

```typescript
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 |
| --- | --- | --- |
| `token` | `string` | The single-use, short-lived email-verification token from the emailed link. |


Returns: `Promise<CustomerEmailVerificationResult>`

```typescript
await lynkow.customers.verifyEmail(tokenFromEmailLink)
const { bookings } = await lynkow.customers.me() // retro-linked guest bookings now visible
```