Log in a user
- Production and sandbox. The token decides the environment.
https://api.beautyrouter.com/v1/login
using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Post, RequestUri = new Uri("https://api.beautyrouter.com/v1/login"), Headers = { { "Authorization", "Bearer <token>" }, }, Content = new StringContent("{ \"externalSalonLocationId\": \"location-2048\", \"externalUserId\": \"staff-7731\", \"firstName\": \"Clara\", \"lastName\": \"Whitfield\", \"email\": \"clara.whitfield@example.com\" }") { Headers = { ContentType = new MediaTypeHeaderValue("application/json") } }};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}package main
import ( "fmt" "strings" "net/http" "io")
func main() {
url := "https://api.beautyrouter.com/v1/login"
payload := strings.NewReader("{ \"externalSalonLocationId\": \"location-2048\", \"externalUserId\": \"staff-7731\", \"firstName\": \"Clara\", \"lastName\": \"Whitfield\", \"email\": \"clara.whitfield@example.com\" }")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>") req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(res) fmt.Println(string(body))
}OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");RequestBody body = RequestBody.create(mediaType, "{ \"externalSalonLocationId\": \"location-2048\", \"externalUserId\": \"staff-7731\", \"firstName\": \"Clara\", \"lastName\": \"Whitfield\", \"email\": \"clara.whitfield@example.com\" }");Request request = new Request.Builder() .url("https://api.beautyrouter.com/v1/login") .post(body) .addHeader("Authorization", "Bearer <token>") .addHeader("Content-Type", "application/json") .build();
Response response = client.newCall(request).execute();import axios from 'axios';
const options = { method: 'POST', url: 'https://api.beautyrouter.com/v1/login', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, data: { externalSalonLocationId: 'location-2048', externalUserId: 'staff-7731', firstName: 'Clara', lastName: 'Whitfield', email: 'clara.whitfield@example.com' }};
try { const { data } = await axios.request(options); console.log(data);} catch (error) { console.error(error);}const url = 'https://api.beautyrouter.com/v1/login';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"externalSalonLocationId":"location-2048","externalUserId":"staff-7731","firstName":"Clara","lastName":"Whitfield","email":"clara.whitfield@example.com"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}val client = OkHttpClient()
val mediaType = MediaType.parse("application/json")val body = RequestBody.create(mediaType, "{ \"externalSalonLocationId\": \"location-2048\", \"externalUserId\": \"staff-7731\", \"firstName\": \"Clara\", \"lastName\": \"Whitfield\", \"email\": \"clara.whitfield@example.com\" }")val request = Request.Builder() .url("https://api.beautyrouter.com/v1/login") .post(body) .addHeader("Authorization", "Bearer <token>") .addHeader("Content-Type", "application/json") .build()
val response = client.newCall(request).execute()<?php
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.beautyrouter.com/v1/login', [ 'body' => '{ "externalSalonLocationId": "location-2048", "externalUserId": "staff-7731", "firstName": "Clara", "lastName": "Whitfield", "email": "clara.whitfield@example.com" }', 'headers' => [ 'Authorization' => 'Bearer <token>', 'Content-Type' => 'application/json', ],]);
echo $response->getBody();import requests
url = "https://api.beautyrouter.com/v1/login"
payload = { "externalSalonLocationId": "location-2048", "externalUserId": "staff-7731", "firstName": "Clara", "lastName": "Whitfield", "email": "clara.whitfield@example.com"}headers = { "Authorization": "Bearer <token>", "Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.json())require 'uri'require 'net/http'
url = URI("https://api.beautyrouter.com/v1/login")
http = Net::HTTP.new(url.host, url.port)http.use_ssl = true
request = Net::HTTP::Post.new(url)request["Authorization"] = 'Bearer <token>'request["Content-Type"] = 'application/json'request.body = "{ \"externalSalonLocationId\": \"location-2048\", \"externalUserId\": \"staff-7731\", \"firstName\": \"Clara\", \"lastName\": \"Whitfield\", \"email\": \"clara.whitfield@example.com\" }"
response = http.request(request)puts response.read_bodycurl --request POST \ --url https://api.beautyrouter.com/v1/login \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "externalSalonLocationId": "location-2048", "externalUserId": "staff-7731", "firstName": "Clara", "lastName": "Whitfield", "email": "clara.whitfield@example.com" }'import Foundation
let headers = [ "Authorization": "Bearer <token>", "Content-Type": "application/json"]let parameters = [ "externalSalonLocationId": "location-2048", "externalUserId": "staff-7731", "firstName": "Clara", "lastName": "Whitfield", "email": "clara.whitfield@example.com"] as [String : Any]
let postData = JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "https://api.beautyrouter.com/v1/login")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)request.httpMethod = "POST"request.allHTTPHeaderFields = headersrequest.httpBody = postData as Data
let session = URLSession.sharedlet dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) }})
dataTask.resume()Registers a new company and user, or logs in existing ones, and returns a user token that lasts 24 hours. When
the user’s name or email changed, it updates them. Send the salon location’s ID as externalSalonLocationId: each
location is its own company. Each login returns a new token, and the user’s earlier tokens keep working.
Beauty Router checks that a new or changed email can receive mail. If it cannot, the login fails with an error
that names the problem. If the address may cause trouble, the login succeeds and warnings names the problem.
canOrder says whether the user can create orders or only view the marketplace. Each login sets it, so the
latest value wins. With canOrder: false, the user cannot place, change, or cancel an order, change the
payment card, or archive a supplier. canManageCompany works the same way: with false, the user cannot change
the company’s details, licenses, or exemption certificates, or submit it for review. Your salon app decides who
gets which value.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
Your ID for the salon location. Each location is its own company.
Your ID for the user. A user is the combination of the salon location and this ID.
The user’s first name.
The user’s last name.
The user’s verified email. Beauty Router checks that a new or changed email can receive mail.
Whether the user can create orders or only view the marketplace. Each login sets it, so the latest value
wins. A login that leaves it out sets it to true.
Whether the user can change company settings. Each login sets it, so the latest value wins. A login that
leaves it out sets it to true.
Examples
{ "externalSalonLocationId": "location-2048", "externalUserId": "staff-7731", "firstName": "Clara", "lastName": "Whitfield", "email": "clara.whitfield@example.com"}Responses
Section titled “Responses”The user is logged in.
object
The user token, such as br-usertoken-live-<token>. Send it as Authorization: Bearer <token>.
When the user token expires, 24 hours after login.
object
The company ID.
unverified: not submitted yet. in_review: Beauty Router is reviewing the company. prequalified: the initial
check passed, and the detailed verification is still pending. The company can order, its orders wait until that
verification ends, and no charge happens until then. verified: the company passed the review.
declined: the company did not pass the review.
object
The user ID.
Problems with the email that did not stop the login. email_mailbox_full: the mailbox is full right now.
email_temporary_address: the address belongs to a disposable email service. Present only when there is one.
Examples
{ "userToken": "br-usertoken-live-Zk3pV9qL2xTn8RbW4mYc6HdJ0sFa1GeQ7uKo5NiP", "expiresAt": "2026-10-30T14:05:00Z", "company": { "id": "loc-7Hk2pQx9Lm", "verificationStatus": "unverified" }, "user": { "id": "user-3Vb8nWq1Rt" }}validation_failed: a field is missing or has a wrong value. 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.
The body of every error response.
object
object
A stable error code.
A sentence for developers. It can change, so do not match on it.
The request field that caused the error. Only some errors have it.
Only on verification_data_missing and app_setup_incomplete. What still needs to be set.
Only on order_total_changed and order_has_problems. A fresh quote.
object
One group per supplier. Each group becomes one order.
object
object
An amount in dollars, with up to two decimal places.
The discount on the whole line, in US dollars.
An amount in dollars, with up to two decimal places.
The total of the lines, in US dollars.
The discount on the whole order, in US dollars.
object
The shipping charge in US dollars, or null when it cannot be worked out.
How much more to order for free shipping, or null when it does not apply.
The sales tax in US dollars, or null when it cannot be worked out.
object
The exemption certificate used, or null.
Why no certificate applies, or null when one does.
The amount the salon pays for this group in US dollars, or null when it cannot be worked out. Send it back as
expectedTotal. A group with problems has no expectedTotal.
now: Beauty Router charges the card when the order is created. after_verification: the company is
prequalified, so the charge waits until its verification ends.
object
What stops a group or the whole request. See Problems.
A sentence you can show. It can change, so do not match on it.
The product. Only on problems with one product.
The supplier. Only on problems with one supplier.
How many units can be ordered. Only on insufficient_stock.
The lines no supplier can take.
object
object
What stops a group or the whole request. See Problems.
A sentence you can show. It can change, so do not match on it.
The product. Only on problems with one product.
The supplier. Only on problems with one supplier.
How many units can be ordered. Only on insufficient_stock.
What stops every group.
object
What stops a group or the whole request. See Problems.
A sentence you can show. It can change, so do not match on it.
The product. Only on problems with one product.
The supplier. Only on problems with one supplier.
How many units can be ordered. Only on insufficient_stock.
The sum of all the groups in US dollars, or null when a group’s total is.
Always usd.
Examples
{ "error": { "code": "validation_failed", "message": "ein must have 9 digits.", "field": "ein" }}{ "error": { "code": "email_mailbox_not_found", "message": "The mail server rejected this email address.", "field": "email" }}invalid_app_token: the Authorization header has no valid app token, or the token expired or was revoked.
The body of every error response.
object
object
A stable error code.
A sentence for developers. It can change, so do not match on it.
The request field that caused the error. Only some errors have it.
Only on verification_data_missing and app_setup_incomplete. What still needs to be set.
Only on order_total_changed and order_has_problems. A fresh quote.
object
One group per supplier. Each group becomes one order.
object
object
An amount in dollars, with up to two decimal places.
The discount on the whole line, in US dollars.
An amount in dollars, with up to two decimal places.
The total of the lines, in US dollars.
The discount on the whole order, in US dollars.
object
The shipping charge in US dollars, or null when it cannot be worked out.
How much more to order for free shipping, or null when it does not apply.
The sales tax in US dollars, or null when it cannot be worked out.
object
The exemption certificate used, or null.
Why no certificate applies, or null when one does.
The amount the salon pays for this group in US dollars, or null when it cannot be worked out. Send it back as
expectedTotal. A group with problems has no expectedTotal.
now: Beauty Router charges the card when the order is created. after_verification: the company is
prequalified, so the charge waits until its verification ends.
object
What stops a group or the whole request. See Problems.
A sentence you can show. It can change, so do not match on it.
The product. Only on problems with one product.
The supplier. Only on problems with one supplier.
How many units can be ordered. Only on insufficient_stock.
The lines no supplier can take.
object
object
What stops a group or the whole request. See Problems.
A sentence you can show. It can change, so do not match on it.
The product. Only on problems with one product.
The supplier. Only on problems with one supplier.
How many units can be ordered. Only on insufficient_stock.
What stops every group.
object
What stops a group or the whole request. See Problems.
A sentence you can show. It can change, so do not match on it.
The product. Only on problems with one product.
The supplier. Only on problems with one supplier.
How many units can be ordered. Only on insufficient_stock.
The sum of all the groups in US dollars, or null when a group’s total is.
Always usd.
Examples
{ "error": { "code": "invalid_app_token", "message": "The app token is missing or not valid." }}app_setup_incomplete: a required setting of your salon app is not set. missing lists what to set in the salon
app admin area. company_blocked: Beauty Router blocked this company. ip_not_allowed: the app token was sent from
an IP address that is not in your allowed list.
The body of every error response.
object
object
A stable error code.
A sentence for developers. It can change, so do not match on it.
The request field that caused the error. Only some errors have it.
Only on verification_data_missing and app_setup_incomplete. What still needs to be set.
Only on order_total_changed and order_has_problems. A fresh quote.
object
One group per supplier. Each group becomes one order.
object
object
An amount in dollars, with up to two decimal places.
The discount on the whole line, in US dollars.
An amount in dollars, with up to two decimal places.
The total of the lines, in US dollars.
The discount on the whole order, in US dollars.
object
The shipping charge in US dollars, or null when it cannot be worked out.
How much more to order for free shipping, or null when it does not apply.
The sales tax in US dollars, or null when it cannot be worked out.
object
The exemption certificate used, or null.
Why no certificate applies, or null when one does.
The amount the salon pays for this group in US dollars, or null when it cannot be worked out. Send it back as
expectedTotal. A group with problems has no expectedTotal.
now: Beauty Router charges the card when the order is created. after_verification: the company is
prequalified, so the charge waits until its verification ends.
object
What stops a group or the whole request. See Problems.
A sentence you can show. It can change, so do not match on it.
The product. Only on problems with one product.
The supplier. Only on problems with one supplier.
How many units can be ordered. Only on insufficient_stock.
The lines no supplier can take.
object
object
What stops a group or the whole request. See Problems.
A sentence you can show. It can change, so do not match on it.
The product. Only on problems with one product.
The supplier. Only on problems with one supplier.
How many units can be ordered. Only on insufficient_stock.
What stops every group.
object
What stops a group or the whole request. See Problems.
A sentence you can show. It can change, so do not match on it.
The product. Only on problems with one product.
The supplier. Only on problems with one supplier.
How many units can be ordered. Only on insufficient_stock.
The sum of all the groups in US dollars, or null when a group’s total is.
Always usd.
Examples
{ "error": { "code": "app_setup_incomplete", "message": "Set the required settings in the salon app admin area.", "missing": [ "webhookUrl", "orderUrl" ] }}{ "error": { "code": "company_blocked", "message": "Beauty Router blocked this company." }}{ "error": { "code": "ip_not_allowed", "message": "This IP address is not in the allowed list of your salon app." }}internal_error: something failed on Beauty Router’s side. Try again later.
The body of every error response.
object
object
A stable error code.
A sentence for developers. It can change, so do not match on it.
The request field that caused the error. Only some errors have it.
Only on verification_data_missing and app_setup_incomplete. What still needs to be set.
Only on order_total_changed and order_has_problems. A fresh quote.
object
One group per supplier. Each group becomes one order.
object
object
An amount in dollars, with up to two decimal places.
The discount on the whole line, in US dollars.
An amount in dollars, with up to two decimal places.
The total of the lines, in US dollars.
The discount on the whole order, in US dollars.
object
The shipping charge in US dollars, or null when it cannot be worked out.
How much more to order for free shipping, or null when it does not apply.
The sales tax in US dollars, or null when it cannot be worked out.
object
The exemption certificate used, or null.
Why no certificate applies, or null when one does.
The amount the salon pays for this group in US dollars, or null when it cannot be worked out. Send it back as
expectedTotal. A group with problems has no expectedTotal.
now: Beauty Router charges the card when the order is created. after_verification: the company is
prequalified, so the charge waits until its verification ends.
object
What stops a group or the whole request. See Problems.
A sentence you can show. It can change, so do not match on it.
The product. Only on problems with one product.
The supplier. Only on problems with one supplier.
How many units can be ordered. Only on insufficient_stock.
The lines no supplier can take.
object
object
What stops a group or the whole request. See Problems.
A sentence you can show. It can change, so do not match on it.
The product. Only on problems with one product.
The supplier. Only on problems with one supplier.
How many units can be ordered. Only on insufficient_stock.
What stops every group.
object
What stops a group or the whole request. See Problems.
A sentence you can show. It can change, so do not match on it.
The product. Only on problems with one product.
The supplier. Only on problems with one supplier.
How many units can be ordered. Only on insufficient_stock.
The sum of all the groups in US dollars, or null when a group’s total is.
Always usd.
Examples
{ "error": { "code": "internal_error", "message": "Something went wrong. Try again later." }}