> ## Documentation Index
> Fetch the complete documentation index at: https://opencals.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> How authentication works in the Opencals Storefront API: Storefront API keys, customer JWT tokens, refresh flows, and error responses.

The Storefront API uses two layers of authentication: a **Storefront API key** that identifies your store on every request, and optional **customer tokens** (JWTs) for endpoints that act on behalf of a logged-in customer.

## Storefront API key

Every request must include your Storefront API key in the `X-Api-Key` header:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
curl "https://api.opencals.com/storefront/products" \
  -H "X-Api-Key: sfk_your_key_here"
```

Keys are created in the dashboard under **Settings → API Keys**, are scoped to a single store, and start with `sfk_`.

<Warning>
  Treat the key like a secret: keep it in server-side environment variables and proxy requests through your own backend rather than calling the API directly from the browser.
</Warning>

## Customer authentication

Endpoints that read or modify a specific customer's data — their appointments, orders, and profile — additionally require a customer access token as a Bearer token:

```text theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Authorization: Bearer <customer_access_token>
X-Api-Key: sfk_your_key_here
```

### Sign in

Exchange a customer's credentials for a token pair:

<CodeGroup>
  ```bash Request theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
  curl -X POST "https://api.opencals.com/storefront/auth/sign-in" \
    -H "X-Api-Key: sfk_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "email": "customer@example.com",
      "password": "securepassword"
    }'
  ```

  ```json Response theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
  {
    "accessToken": "eyJhbGciOi…",
    "refreshToken": "eyJhbGciOi…"
  }
  ```
</CodeGroup>

### Refresh tokens

Access tokens expire. Get a fresh pair by calling the refresh endpoint with the **refresh token** as the Bearer token:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
curl "https://api.opencals.com/storefront/auth/refresh" \
  -H "X-Api-Key: sfk_your_key_here" \
  -H "Authorization: Bearer <customer_refresh_token>"
```

### All auth endpoints

| Method | Endpoint                                      | Description                                          |
| ------ | --------------------------------------------- | ---------------------------------------------------- |
| `POST` | `/storefront/auth/sign-up`                    | Register a new customer (sends a verification email) |
| `POST` | `/storefront/auth/sign-in`                    | Sign in with email and password                      |
| `POST` | `/storefront/auth/oauth`                      | Sign in or register via an OAuth provider            |
| `GET`  | `/storefront/auth/refresh`                    | Refresh tokens (pass the refresh token as Bearer)    |
| `POST` | `/storefront/auth/request-email-verification` | Resend the verification email                        |
| `POST` | `/storefront/auth/verify-email`               | Verify an email with the emailed token               |
| `POST` | `/storefront/auth/request-password-reset`     | Send a password reset email                          |
| `POST` | `/storefront/auth/reset-password`             | Reset a password with the emailed token              |

See the [API reference](/docs/api-reference/introduction) for full request and response shapes.

### Which endpoints need a customer token?

Customer-scoped endpoints — appointments, orders, and self-service profile management — require a customer Bearer token. Catalog endpoints (products, availability, staff members, locations, collections, store settings) need only the API key.

<Note>
  Cart and checkout endpoints accept an *optional* customer token: guests can book without an account, and checkout can return auth tokens for new customers so they're signed in automatically after their first booking.
</Note>

## Error responses

Errors use conventional HTTP status codes with a JSON body:

```json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{
  "statusCode": 400,
  "message": "Validation failed",
  "errors": [
    { "property": "email", "messages": ["Email is required"] }
  ]
}
```

| Status | Meaning                                                           |
| ------ | ----------------------------------------------------------------- |
| `400`  | Validation error — check the `errors` array for per-field details |
| `401`  | Invalid or missing API key or customer token                      |
| `404`  | Resource not found                                                |
| `429`  | Rate limit exceeded — see [rate limits](/docs/quickstart#rate-limits)  |

## Security checklist

* Store `sfk_` keys in environment variables, never in client bundles or repositories.
* Call the Storefront API from your server (API routes, server components, edge functions) and keep the key out of browser network requests.
* Store customer refresh tokens in `httpOnly` cookies rather than `localStorage` where possible.
* Rotate API keys from the dashboard if a key is ever exposed.
