Conventions
These rules hold for every endpoint in the API.
Requests
Section titled “Requests”Request and response bodies are JSON, so send Content-Type: application/json. The one exception is document uploads,
where the request body is the file itself. See Documents.
Every request sends a token in the Authorization header. POST /v1/login, GET /v1/catalog/files, and
GET /v1/catalog/search send the app token. Every other endpoint sends the user token. See
Authentication.
Every Beauty Router ID is a prefix, a hyphen, and a base62 string, for example prod-4Fz9QbX2kL. Treat IDs as opaque
strings. Do not parse them or assume their length.
| Prefix | Object |
|---|---|
loc |
Salon location, which is a company in Beauty Router |
user |
User |
lic |
License |
exc |
Exemption certificate |
order |
Order |
prod |
Product |
group |
Product group |
brand |
Brand |
sup |
Supplier, which is a brand or distributor |
cat |
Category |
match |
Product match |
svc |
Service |
event |
Webhook event |
card |
Payment card |
Your own IDs
Section titled “Your own IDs”You send your own IDs as externalSalonLocationId and externalUserId when you log in a user, and as
externalId when you send products for matching. Each one is any string up to 128 characters.
Timestamps
Section titled “Timestamps”Timestamps are ISO 8601 in UTC, for example 2026-10-28T19:03:55Z. Dates without a time, such as a license expiration
date, are ISO 8601 dates, for example 2027-06-30.
Always send timestamps in UTC, ending in Z. A timestamp with a time zone, such as 2026-10-28T12:03:55-07:00, fails
with validation_failed, because it can lead to errors.
Amounts are decimal numbers in dollars, with a currency field next to them. The currency is always "usd".
{ "total": 275.01, "currency": "usd"}Each currency has a fixed precision. For usd it is two decimal places. An amount with more decimal places than the
currency supports fails with validation_failed.
Partial updates
Section titled “Partial updates”The API does not use PATCH. An endpoint accepts a partial update only when its documentation says so, as
PUT /v1/company does. Send PUT with only the fields you change, and fields you leave out stay as they are.
curl -X PUT https://api.beautyrouter.com/v1/company \ -H "Authorization: Bearer $USER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "displayName": "Golden Hour Hair Studio" }'Every other PUT endpoint takes the full value.
Paging
Section titled “Paging”Only some endpoints support paging. Each endpoint’s documentation says whether it does. Those endpoints use cursor paging with two query parameters:
| Parameter | Description |
|---|---|
limit |
How many items to return. The default is 50, and the maximum is 200. |
startingAfter |
The ID of the last item on the previous page. Leave it out for page 1. |
A paged list returns data with the items and hasMore, which is true when another page follows.
curl "https://api.beautyrouter.com/v1/product-matches?limit=100" \ -H "Authorization: Bearer $USER_TOKEN"{ "data": [ { "id": "match-3Pa8sDf2Gh", "externalId": "inv-10482", "status": "matched", "productId": "prod-4Fz9QbX2kL", "createdAt": "2026-10-27T09:14:02Z", "updatedAt": "2026-10-27T09:14:02Z" }, { "id": "match-9Lk4jHg7Fd", "externalId": "inv-10517", "status": "needs_reply", "question": "Is this product from Jadewell or Amberlin?", "createdAt": "2026-10-27T09:14:02Z", "updatedAt": "2026-10-27T16:40:31Z" } ], "hasMore": true}To get the next page, send the ID of the last item as startingAfter:
curl "https://api.beautyrouter.com/v1/product-matches?limit=100&startingAfter=match-9Lk4jHg7Fd" \ -H "Authorization: Bearer $USER_TOKEN"Errors
Section titled “Errors”An error has an HTTP status and a body with an error object:
{ "error": { "code": "validation_failed", "message": "ein must have 9 digits.", "field": "ein" }}| Field | Description |
|---|---|
code |
A stable code. Use it in your code. |
message |
A sentence for developers. It can change, so do not match on it. |
field |
The request field that caused the error. Only some errors have it. |
missing |
Only on verification_data_missing and app_setup_incomplete: what still needs to be set. |
quote |
Only on order_total_changed and order_has_problems: a fresh quote. |
Errors lists every code, with what it means and how to fix it.
For a 401 with user_token_expired or invalid_user_token, log the user in again with POST /v1/login and repeat
the request. See Authentication.
