# Registration and sessions

These three endpoints manage customer accounts and session tokens. `register` and `login` return a `token` alongside the customer; send it as `X-Customer-Token` on customer-authenticated endpoints. All three require the `customers:write` scope on your API key.

## POST /v1/api/customers/register

Register a storefront customer account. Returns the created customer plus a session token. If the email already belongs to a guest row (created by cart email capture in the Commerce module), the guest is upgraded in place: same `id`, order history preserved, name and password set from this request. Only an email that belongs to a real (non-guest) account is rejected. Extra rate limit: 10 requests per minute per IP.

### Body parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Yes | Full name. Maximum 150 characters. |
| `email` | string | Yes | Valid email address, maximum 255 characters. Lowercased server-side. Must not belong to an existing non-guest account. |
| `password` | string | Yes | Minimum 8 characters. |

```bash title="Request"
curl -X POST https://api.borkol.com/v1/api/customers/register \
  -H "Authorization: Bearer {api_key}" \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane Doe","email":"jane@example.com","password":"s3cret-pass"}'
```

```javascript title="Request (JS)"
const res = await fetch("https://api.borkol.com/v1/api/customers/register", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Jane Doe",
    email: "jane@example.com",
    password: "s3cret-pass",
  }),
});
const { data: customer, token } = await res.json();
```

Status: `201 Created`.

```json title="Response"
{
  "data": {
    "id": "9d4e2f1a-6b3c-4f8e-9a1d-2c5b7e8f0a3d",
    "name": "Jane Doe",
    "email": "jane@example.com",
    "phone": null,
    "created_at": "2026-08-14T10:32:11+00:00"
  },
  "token": "12|Fq8sT3vY6wZx9aBcDeFgHiJkLmNoPqRsTuVwXyZ0"
}
```

### Errors

- `422` with `errors.email` `["Email already registered."]` when a non-guest account already uses the email.
- `422` for field validation failures (missing name, invalid email, password shorter than 8 characters).
- `429` when the 10 requests per minute per IP limit is exceeded.

## POST /v1/api/customers/login

Authenticate a customer with email and password. Returns the customer and a fresh session token, and updates the account's `last_login_at`. The check is timing-safe and never reveals whether the email exists: a wrong password, an unknown email, and a guest account (which has no password) all fail with the same 422. Extra rate limit: 10 requests per minute per IP.

### Body parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `email` | string | Yes | Valid email address. Lowercased server-side before lookup. |
| `password` | string | Yes | The account password. |

```bash title="Request"
curl -X POST https://api.borkol.com/v1/api/customers/login \
  -H "Authorization: Bearer {api_key}" \
  -H "Content-Type: application/json" \
  -d '{"email":"jane@example.com","password":"s3cret-pass"}'
```

Status: `200 OK`.

```json title="Response"
{
  "data": {
    "id": "9d4e2f1a-6b3c-4f8e-9a1d-2c5b7e8f0a3d",
    "name": "Jane Doe",
    "email": "jane@example.com",
    "phone": "+31612345678",
    "created_at": "2026-08-14T10:32:11+00:00"
  },
  "token": "13|Gh7rS2uX5vWy8zAbCdEfGhIjKlMnOpQrStUvWxYz"
}
```

### Errors

- `422` with `errors.email` `["Invalid credentials."]` for a wrong email or a wrong password. The error is always on the `email` key.
- `429` when the 10 requests per minute per IP limit is exceeded.

## POST /v1/api/customers/logout

Revoke the customer session token given in `X-Customer-Token`. The header must be present and valid to pass customer authentication: a currently valid token is revoked and the request returns 204, while an already-revoked or unknown token fails authentication with 401 `{"message": "Invalid customer token."}`.

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `X-Customer-Token` | string | Yes | The session token to revoke. |

No body.

```bash title="Request"
curl -X POST https://api.borkol.com/v1/api/customers/logout \
  -H "Authorization: Bearer {api_key}" \
  -H "X-Customer-Token: 13|Gh7rS2uX5vWy8zAbCdEfGhIjKlMnOpQrStUvWxYz"
```

Status: `204 No Content`. The response has no body.

### Errors

- `401` `{"message":"Invalid customer token."}` when `X-Customer-Token` is missing or invalid.
