---
title: Customers
description: How Spree models customer accounts — registration, authentication, addresses, order history, store credits, and guest checkout behavior.
---

## Overview

Customers interact with your store through the Store API. They can register, log in, manage their profile, and view order history.

```mermaid
erDiagram
    Customer ||--o{ Order : "places"
    Customer ||--o{ Address : "address book"
    Customer ||--o| Address : "default ship/bill"
    Customer ||--o{ Wishlist : "has many"
    Customer ||--o{ StoreCredit : "has many"
    Customer ||--o{ GiftCard : "holds"
    Customer ||--o{ CompanyMembership : "buys for"
    CompanyMembership }o--|| Company : "belongs to"
    Customer }o--o{ CustomerGroup : "segmented by"

    Customer {
        string email
        string first_name
        string last_name
        string phone
    }

    Order {
        string number
        string payment_status
        string fulfillment_status
    }

    Address {
        string label
        string first_name
        string last_name
        string company
        string address1
        string address2
        string city
        string postal_code
        string state_code
        string country_code
        string phone
    }

    Wishlist {
        string name
        boolean is_default
    }
```

## Registration


```typescript Store SDK
const { token, user } = await client.customers.create({
  email: 'john@example.com',
  password: 'password123',
  password_confirmation: 'password123',
  first_name: 'John',
  last_name: 'Doe',
})
// token => JWT token for subsequent authenticated requests
// user => { id: "cus_abc123", email: "john@example.com", first_name: "John", ... }
```

```bash cURL
curl -X POST 'https://api.mystore.com/api/v3/store/customers' \
  -H 'X-Spree-Api-Key: pk_xxx' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "john@example.com",
    "password": "password123",
    "password_confirmation": "password123",
    "first_name": "John",
    "last_name": "Doe"
  }'
```


> **NOTE:** The Store API flow above is **customer self-registration** — the customer sets a password and gets a JWT back. To [create a customer record from the back office](../sdk/admin/resources.md) (importers, CRM sync, phone orders), use the [Admin SDK](../sdk/admin/quickstart.md) instead. No password is set, so it's a managed record rather than a sign-up:
> 
> ```typescript Admin SDK
import { createAdminClient } from '@spree/admin-sdk'

const client = createAdminClient({
  baseUrl: 'https://store.example.com',
  secretKey: 'sk_xxx',
})

const customer = await client.customers.create({
  email: 'john@example.com',
  first_name: 'John',
  last_name: 'Doe',
  tags: ['wholesale'],
})
```

## Login


```typescript Store SDK
const { token, user } = await client.auth.login({
  email: 'john@example.com',
  password: 'password123',
})
// Use the token for authenticated requests
```

```bash cURL
curl -X POST 'https://api.mystore.com/api/v3/store/auth/login' \
  -H 'X-Spree-API-Key: pk_xxx' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "john@example.com",
    "password": "password123"
  }'
```


The response includes a JWT `token` and a `user` object. Pass the token in subsequent requests via the [`Authorization: Bearer <token>`](../../api-reference/store-api/authentication.md) header.

## Token Refresh

Refresh an expiring token to keep the session alive. For how `client.auth.login` and `client.auth.refresh` fit into configuring the SDK for guest vs customer (JWT) auth, see the [SDK authentication guide](../sdk/authentication.md):


```typescript Store SDK
const { token, refresh_token } = await client.auth.refresh({
  refresh_token: existingRefreshToken,
})
// Persist the rotated refresh_token for the next refresh
```

```bash cURL
curl -X POST 'https://api.mystore.com/api/v3/store/auth/refresh' \
  -H 'X-Spree-Api-Key: pk_xxx' \
  -H 'Content-Type: application/json' \
  -d '{ "refresh_token": "rt_xxx" }'
```


## Password Reset

Password reset is a two-step flow. First, request a reset email. Then, use the token from the email to set a new password.

### Step 1: Request Reset


```typescript Store SDK
await client.passwordResets.create({
  email: 'john@example.com',
  redirect_url: 'https://myshop.com/reset-password',
})
// Always returns { message: "..." } — even if the email doesn't exist
// This prevents email enumeration
```

```bash cURL
curl -X POST 'https://api.mystore.com/api/v3/store/password_resets' \
  -H 'X-Spree-Api-Key: pk_xxx' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "john@example.com",
    "redirect_url": "https://myshop.com/reset-password"
  }'
```


The optional `redirect_url` parameter specifies where the password reset link in the email should point to. The token will be appended as a query parameter (e.g., `https://myshop.com/reset-password?token=...`). If the store has Allowed Origins configured, the `redirect_url` must match one of them.

This fires a `customer.password_reset_requested` event with the reset token in the payload. If you're using the `spree_emails` package, the email is sent automatically. Otherwise, subscribe to this event to send the reset email yourself (see [Events](events.md)).

### Step 2: Reset Password


```typescript Store SDK
const { token, user } = await client.passwordResets.update(
  'reset-token-from-email',
  {
    password: 'newsecurepassword',
    password_confirmation: 'newsecurepassword',
  }
)
// Returns JWT token + user (auto-login)
```

```bash cURL
curl -X PATCH 'https://api.mystore.com/api/v3/store/password_resets/RESET_TOKEN' \
  -H 'X-Spree-Api-Key: pk_xxx' \
  -H 'Content-Type: application/json' \
  -d '{
    "password": "newsecurepassword",
    "password_confirmation": "newsecurepassword"
  }'
```


On success, the user is automatically logged in and a JWT token is returned. The reset token expires after 15 minutes (configurable via `Spree::Config.customer_password_reset_expires_in`) and is single-use (changing the password invalidates it).

## Newsletter Subscriptions

Headless storefronts often need to collect newsletter signups before account creation (footer forms, popup overlays). The Store API exposes a double opt-in subscription flow that mirrors the password reset webhook pattern.

### Step 1: Subscribe


```typescript Store SDK
await client.newsletterSubscribers.create({
  email: 'guest@example.com',
  redirect_url: 'https://myshop.com/newsletter/confirm',
})
// Returns the NewsletterSubscriber (verified: false for guests)
```

```bash cURL
curl -X POST 'https://api.mystore.com/api/v3/store/newsletter_subscribers' \
  -H 'X-Spree-Api-Key: pk_xxx' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "guest@example.com",
    "redirect_url": "https://myshop.com/newsletter/confirm"
  }'
```


The optional `redirect_url` points at the storefront page that will receive the verification token. It is validated against the store's Allowed Origins — URLs that do not match are silently dropped from the webhook payload (secure-by-default).

This fires a `newsletter_subscriber.subscription_requested` event whose payload includes `email`, `verification_token`, and the validated `redirect_url`. Subscribe to this event from your storefront's webhook handler to send the confirmation email — the link in the email should point to `<redirect_url>?token=<verification_token>`. The bundled `spree_emails` package also listens to this event and sends a default confirmation email if you're not running a headless storefront.

If the request is authenticated via a customer JWT and the JWT's email matches the subscribed email, the subscription is **auto-verified** and no event is fired (the user has already proven email ownership).

### Step 2: Verify


```typescript Store SDK
await client.newsletterSubscribers.verify({
  token: 'token-from-email',
})
// Returns the verified NewsletterSubscriber
```

```bash cURL
curl -X POST 'https://api.mystore.com/api/v3/store/newsletter_subscribers/verify' \
  -H 'X-Spree-Api-Key: pk_xxx' \
  -H 'Content-Type: application/json' \
  -d '{ "token": "token-from-email" }'
```


On success, the subscriber is marked verified. If the subscription is linked to a customer record, that customer's `accepts_email_marketing` flag is also set to `true`. Consent is preserved across registration: if a guest subscribes and later registers with the same email, the existing subscriber is reused — registration won't accidentally reset their opt-in state.

## Customer Profile


```typescript Store SDK
// Get current customer
const customer = await client.customer.get()
// {
//   id: "cus_xxx",
//   email: "john@example.com",
//   first_name: "John",
//   last_name: "Doe",
//   default_shipping_address: { ... },
//   default_billing_address: { ... },
//   addresses: [{ ... }, { ... }],
// }

// Update profile
const updated = await client.customer.update({
  first_name: 'Jonathan',
  accepts_email_marketing: true,
})
```

```bash cURL
# Get current customer
curl 'https://api.mystore.com/api/v3/store/customers/me' \
  -H 'X-Spree-API-Key: pk_xxx' \
  -H 'Authorization: Bearer <jwt_token>'

# Update profile
curl -X PATCH 'https://api.mystore.com/api/v3/store/customers/me' \
  -H 'X-Spree-API-Key: pk_xxx' \
  -H 'Authorization: Bearer <jwt_token>' \
  -H 'Content-Type: application/json' \
  -d '{ "first_name": "Jonathan", "accepts_email_marketing": true }'
```


## Customer Resources

Authenticated customers have access to these resources:

| Resource | Description |
|----------|-------------|
| [**Addresses**](addresses.md#customer-address-book) | Billing and shipping addresses with default selection |
| [**Orders**](orders.md#order-history) | Past order history |
| **Credit Cards** | Saved credit cards for checkout |
| **Payment Sources** | Other saved payment methods (PayPal, Klarna, etc.) |
| **Store Credits** | Balance assigned by the store, usable at checkout |
| **Gift Cards** | Gift cards owned by or assigned to the customer |
| **Wishlists** | Saved product lists |

## Guest Checkout

Customers don't need to register to purchase. Guest checkout uses an order token (`X-Spree-Token`) to identify the cart. See [Orders — Cart](orders.md#cart) for details.

## Related Documentation

- [Account (Store SDK)](../sdk/store/account.md) — Task-oriented walkthrough of registration, login, profile, addresses, and order history
- [Addresses](addresses.md) — Customer address management
- [Orders](orders.md) — Order history and checkout
- [Authentication](../providers/sso.md) — Custom authentication setup
- [Staff & Roles](staff-roles.md) — Admin users and permissions
