# Authentication

Beauty Router uses two tokens. The app token identifies your salon app and stays on your server. The user token
identifies one user of your salon app and can go to your frontend.

Both tokens go in the `Authorization` header:

```http
Authorization: Bearer <token>
```

Each token starts with its type and its environment, so the two types have different formats:

| Token      | Production                  | Sandbox                        |
| ---------- | --------------------------- | ------------------------------ |
| App token  | `br-apptoken-live-<token>`  | `br-apptoken-sandbox-<token>`  |
| User token | `br-usertoken-live-<token>` | `br-usertoken-sandbox-<token>` |

A token works only in its own environment. Each endpoint takes one type:

| Endpoint                 | Token      |
| ------------------------ | ---------- |
| `POST /v1/login`         | App token  |
| `GET /v1/catalog/files`  | App token  |
| `GET /v1/catalog/search` | App token  |
| Every other endpoint     | User token |

`GET /v1/catalog/products/{productId}` is a catalog endpoint that takes the user token, so your page can call it.

## App token

You generate app tokens in the salon app admin area, which you can open once Beauty Router has set up your salon app. The
admin area shows each token once, so store it on your server right away.

You can have several app tokens at the same time, so you can rotate them safely. Each one can have a lifetime, so it
expires on a date you choose.

Keep app tokens on your server. Never put one in browser or mobile code, and never let it leave your security perimeter.
Anyone who has one can log in as any user of your salon app and place orders for any company in your client base that
has already added a payment card.

An expired or revoked token returns `invalid_app_token`.

### Allowed IP addresses

You can limit where your app tokens work. In the salon app admin area, add IP addresses or ranges, such as `203.0.113.10`
or `203.0.113.0/24`. A request with an app token from any other address fails with `ip_not_allowed`. The token stays
active, and the attempt appears in your [integration errors](/support/#integration-errors).

The list is empty by default, which allows every address. Each environment has its own list. Your servers need fixed
outgoing addresses for this. On AWS, Google Cloud, or Azure, that usually means a NAT gateway with a static IP.

### New networks

Beauty Router notes the networks your app tokens are used from, such as a cloud provider. When a token is used from a new
network, you get a [notification](/support/#integration-errors). The request still works. This needs no setup, and it
works when your cloud addresses change.

### Rotate a token

Two tokens can work side by side, so you can replace a token with no downtime:

1. Generate a new token in the admin area, with a lifetime that fits your rotation, such as one year.
2. Put the new token on your server.
3. Limit the lifetime of the old token, so that it expires once all your servers use the new one.

User tokens stay valid when the app token that created them expires. Each one ends after its usual 24 hours.

Beauty Router sends a reminder 30 days and 7 days before a token expires, unless a newer token that you already use
outlasts it. The
reminder goes to your [developer email and Slack webhook](/support/#integration-errors), or to your team in the salon app
admin area if you set neither.

### Revoke a token

If a token leaks, revoke it in the admin area. It stops working at once, and so does every user token it created. Your
users get new user tokens at their next login.

## User token

The user token can go to your frontend. It acts for one user only and lasts 24 hours. Your page can use it to call the
API directly, and the [JavaScript library](/embedded-marketplace/) runs on it. A browser request must come from an
[origin](/support/#get-access) you added in the salon app admin area. Otherwise it fails with `origin_not_allowed`, which
means the origin is not registered in your salon app admin area. A browser may report this as a CORS error instead.

A user can have several user tokens at the same time, for example when they open your salon app on a phone and on a
computer. Each login returns a new token, and the earlier ones keep working.

## Log in a user

Your server calls `POST /v1/login` with the app token and the user's details. It gets back a user token. Take the user and
the salon location from your own session, never from what the browser sends, because the app token can log in any user.

| Field                     | Description                                                                                |
| ------------------------- | ------------------------------------------------------------------------------------------ |
| `externalSalonLocationId` | Your ID for the salon location, up to 128 characters.                                      |
| `externalUserId`          | Your ID for the user, up to 128 characters.                                                |
| `firstName`               | The user's first name, up to 80 characters.                                               |
| `lastName`                | The user's last name, up to 80 characters.                                                |
| `email`                   | The user's verified email.                                                                 |
| `canOrder`                | Whether the user can create orders or only view the marketplace. Optional, default `true`. |
| `canManageCompany`        | Whether the user can change company settings. Optional, default `true`.                    |

```bash
curl https://api.beautyrouter.com/v1/login \
  -H "Authorization: Bearer $BEAUTY_ROUTER_APP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "externalSalonLocationId": "location-2048",
    "externalUserId": "staff-7731",
    "firstName": "Clara",
    "lastName": "Whitfield",
    "email": "clara.whitfield@example.com"
  }'
```

The response has the user token, when it expires, and the Beauty Router IDs of the company and the user:

```json
{
  "userToken": "br-usertoken-live-Zk3pV9qL2xTn8RbW4mYc6HdJ0sFa1GeQ7uKo5NiP",
  "expiresAt": "2026-10-30T14:05:00Z",
  "company": {
    "id": "loc-7Hk2pQx9Lm",
    "verificationStatus": "unverified"
  },
  "user": {
    "id": "user-3Vb8nWq1Rt"
  }
}
```

Use `company.verificationStatus` to decide what to show the user. See [Companies and verification](/companies/).

### New and returning users

The same call registers a new company and a new user, and logs in existing ones. There is no separate registration step:
whenever a user needs Beauty Router, your server makes this call. When the user's name or email changed in your salon
app, the call updates them in Beauty Router.

There is no password and no email confirmation. Your salon app vouches for its user, so these checks stay on your side:

- **Names.** You manage them. Beauty Router stores what you send.
- **Email verification.** Send only an email the user has verified.
- **Email uniqueness.** If emails must be unique inside a company, check that yourself. Two users of one company can have
  the same email.

### Email deliverability

Beauty Router checks that a new or changed email can receive mail.

When the address cannot receive mail, the login fails with status 400, `field` set to `email`, and a code that names the
problem. Your salon app can then ask the user to check the email or to give another one.

| Code                      | Problem                                            |
| ------------------------- | -------------------------------------------------- |
| `email_invalid_format`    | The address is not a valid email address.          |
| `email_domain_not_found`  | The domain does not exist or cannot receive email. |
| `email_mailbox_not_found` | The mail server rejected the address.              |

When the address may cause trouble, the login succeeds, Beauty Router keeps the address, and the response has a `warnings`
list:

```json
{
  "userToken": "br-usertoken-live-Zk3pV9qL2xTn8RbW4mYc6HdJ0sFa1GeQ7uKo5NiP",
  "warnings": ["email_mailbox_full"]
}
```

| Warning                   | What it means                                                         |
| ------------------------- | --------------------------------------------------------------------- |
| `email_mailbox_full`      | The mailbox is full right now, so emails bounce until the user frees space. |
| `email_temporary_address` | The address belongs to a disposable email service.                    |

If your salon app accepts such an address, it must handle users who do not receive the emails they expect. A warning comes
only with the login that checked the email.

An email passes with no warning in every other case, such as a domain that accepts all mail, or an address that could not
be checked. In the sandbox, addresses at `example.com` skip the check, so you can test with made-up users.

### Salon locations

Each salon location is its own company in Beauty Router. Send the location's ID as `externalSalonLocationId`. A salon with
three locations is three companies, and each one is verified and orders on its own.

### Who counts as one user

A user is the combination of the salon location and `externalUserId`. The same `externalUserId` at another location is a
different user, so a person who works at two locations is two users in Beauty Router.

This rarely matters in practice. Beauty Router keeps no settings that follow a person between locations, and the name,
email, and rights come with each login.

### Users who cannot order

`canOrder` says whether the user can create orders or only view the marketplace. With `canOrder: false`, the user can
view the marketplace, keep favorites, and fill a cart, but cannot place, change, or cancel an order, change the
payment card, or archive a supplier. Those requests fail with `user_cannot_order`.

Each login sets `canOrder`, so the latest value wins. A login that leaves it out sets it to `true`, so send `false`
every time you log in that user, including when the token expires.

Your salon app decides who gets which value. Beauty Router has no roles.

### Users who cannot change company settings

`canManageCompany` says whether the user can change the company's settings. With `canManageCompany: false`, the user can
read the company, but cannot change its details, licenses, or exemption certificates, or submit it for review. Those
requests fail with `user_cannot_manage_company`.

It works like `canOrder`. Each login sets it, so the latest value wins. A login that leaves it out sets it to `true`,
so send `false` every time you log in that user. It does not affect ordering or the payment card, and a user who can
order can still choose a delivery address at checkout.

Only users with `canManageCompany` get the [company emails](/emails/), except the two card emails, so keep it `true` for at
least one user of each company.

### Incomplete setup

Login fails with `app_setup_incomplete` until your salon app's [required settings](/support/#get-access) are set in the
salon app admin area of that environment. `missing` lists what to set:

```json
{
  "error": {
    "code": "app_setup_incomplete",
    "message": "Set the required settings in the salon app admin area.",
    "missing": ["webhookUrl", "orderUrl"]
  }
}
```

The values in `missing` are `name`, `origins`, `webhookUrl`, `webhookSecret`, `orderUrl`, `productUrl`, `verificationUrl`,
and `marketplaceUrl`.

### Blocked companies

Beauty Router can block a company, for example for fraud. Its user tokens stop working at once, and a login fails with
`company_blocked`.

## Log out a user

`POST /v1/logout` ends the user token it is called with. Call it from your frontend or your server when the user logs out
of your salon app. The user's other tokens keep working, for example on another device.

```bash
curl -X POST https://api.beautyrouter.com/v1/logout \
  -H "Authorization: Bearer $USER_TOKEN"
```

The response is `204` with no body. A later request with that token fails with `invalid_user_token`.

## Token expiry

The user token lasts 24 hours. The `expiresAt` field says when it ends.

There is no refresh token. Call `POST /v1/login` again when the token expires, or when a request returns `401` with
`user_token_expired` or `invalid_user_token`. In the embedded marketplace, the library asks your code for a new token
through `getUserToken`. See [Embedded marketplace](/embedded-marketplace/).

## Environments

The sandbox and production use the same API base URL, `https://api.beautyrouter.com/v1`. The token decides the
environment: a token that starts with `br-apptoken-sandbox-` or `br-usertoken-sandbox-` works in the sandbox, and a
`-live-` token works in production.

The sandbox runs on the same deployment as production, so it always has the same version. All systems in the sandbox
run in test mode.

Each environment has its own salon app admin area, with its own app tokens, origins, and settings. Build and test against the sandbox. When you go live, set up your salon app in the production admin area, and switch the app token and the webhook secret to production. The library loads from `https://js.beautyrouter.com/v1/beauty-router.js` in both environments, and it takes the environment from the user token.