# Orders

Your users place orders in Beauty Router's interface, or on your own New order page through the
[API](/create-orders/). One checkout creates one order for each brand or distributor in the cart.

After an order is created, Beauty Router charges the card at once. If the charge succeeds, the order is `placed`, and the
brand or distributor starts preparing it. If it fails, the order is `payment_failed`. Beauty Router then tracks the
delivery.

A `prequalified` company's order is `pending_verification`, with no charge. When verification passes, the charge runs, and
the order becomes `placed` or `payment_failed`.

You learn about orders in two ways:

- **In the page.** The library calls `onOrderCreated` with the orders from the checkout. See
  [Embedded marketplace](/embedded-marketplace/). `POST /v1/orders/batch` returns the orders it created.
- **On your server.** Beauty Router sends an `order.created` webhook for each new order, and `order.updated` when the
  status, tracking, delivery address, or other order details change. See [Webhooks](/webhooks/).

To show orders in your salon app without building a screen, use the [embedded interface](/embedded-marketplace/#functions):
`showOrders(element)` shows all the company's orders, `showOrder(element, orderId)` shows one order, and
`showOrderTracking(element, orderId)` shows its tracking page.

## List orders

`GET /v1/orders` returns the orders of the logged-in user's company, newest first, with [paging](/conventions/#paging).
Each item has the same [fields](#order-fields) as one order.

```bash
curl "https://api.beautyrouter.com/v1/orders?limit=100" \
  -H "Authorization: Bearer $USER_TOKEN"
```

The list includes [drafts](/create-orders/#drafts). `status` is an optional filter that returns only the orders with that
status, such as `status=draft`.

## Get an order

`GET /v1/orders/{orderId}` returns one order, which can be a draft. The order must belong to the company of the logged-in
user. Otherwise the request fails with `not_found`.

```bash
curl https://api.beautyrouter.com/v1/orders/order-8Jd3kFs6Gh \
  -H "Authorization: Bearer $USER_TOKEN"
```

```json
{
  "id": "order-8Jd3kFs6Gh",
  "number": "4821-7730",
  "companyId": "loc-7Hk2pQx9Lm",
  "userId": "user-3Vb8nWq1Rt",
  "status": "in_transit",
  "supplier": {
    "id": "sup-1Ry6tUi3Op",
    "name": "Amberlin"
  },
  "lines": [
    {
      "productId": "prod-4Fz9QbX2kL",
      "name": "Amberlin Bond Repair Shampoo 33.8 fl oz",
      "quantity": 6,
      "unitPrice": 24.50,
      "discount": 0.00,
      "total": 147.00
    },
    {
      "productId": "prod-6Gh2jKl8Zx",
      "name": "Amberlin Bond Repair Conditioner 33.8 fl oz",
      "quantity": 4,
      "unitPrice": 26.50,
      "discount": 0.00,
      "total": 106.00
    }
  ],
  "productsTotal": 253.00,
  "discount": 0.00,
  "shipping": 0.00,
  "salesTax": 22.01,
  "total": 275.01,
  "currency": "usd",
  "deliveryAddress": {
    "line1": "1450 Larimer St",
    "line2": "Suite 200",
    "city": "Denver",
    "state": "CO",
    "postalCode": "80202",
    "country": "US",
    "phone": "+13035550142"
  },
  "trackingNumbers": [
    {
      "number": "1Z999AA10123456784",
      "url": "https://www.ups.com/track?tracknum=1Z999AA10123456784",
      "carrier": "UPS"
    }
  ],
  "createdAt": "2026-10-26T15:42:10Z",
  "updatedAt": "2026-10-28T19:03:55Z",
  "deliveredAt": null
}
```

## Order fields

Amounts are decimal numbers in dollars. See [Money](/conventions/#money).

| Field                 | Description                                                                         |
| --------------------- | ----------------------------------------------------------------------------------- |
| `id`                  | The order ID, such as `order-8Jd3kFs6Gh`.                                             |
| `number`              | The order number people see, such as `4821-7730`.                                   |
| `companyId`           | The company the order belongs to, such as `loc-7Hk2pQx9Lm`.                         |
| `userId`              | The user who placed the order, such as `user-3Vb8nWq1Rt`.                            |
| `status`              | The order status. See [Statuses](#statuses).                                        |
| `cancelReason`        | Why the order was canceled. Only on canceled orders.                                |
| `supplier`            | The brand or distributor that fulfills the order: `id` and `name`.                  |
| `lines`               | The products in the order. See the line fields below.                               |
| `productsTotal`       | The total of the lines.                                                             |
| `discount`            | The discount on the order.                                                          |
| `shipping`            | The shipping charge, or `null` on a draft with no address.                          |
| `salesTax`            | The sales tax, or `null` on a draft with no address.                                |
| `total`               | The amount the salon pays, or `null` on a draft with no address.                    |
| `currency`            | Always `"usd"`.                                                                     |
| `deliveryAddress`     | Where the order is delivered, in the same format as the company's delivery address. `null` on a draft with no address. |
| `trackingNumbers`     | The shipments of the order: `number`, `url`, and `carrier`.                         |
| `createdAt`           | When the order was placed.                                                          |
| `updatedAt`           | When the order last changed.                                                        |
| `deliveredAt`         | When the order was delivered, or `null`.                                            |

| Line field  | Description                                     |
| ----------- | ----------------------------------------------- |
| `productId` | The Beauty Router product ID.                   |
| `name`      | The product name.                               |
| `quantity`  | How many units the salon ordered.               |
| `unitPrice` | The wholesale price of one unit, before the discount. |
| `discount`  | The discount on the whole line.                 |
| `total`     | The line total.                                 |

A salon can pay a shipping charge: a flat amount or a carrier quote, free above the supplier's threshold. Shipping is
locked when the order is created. Until the order is `placed`, other amounts can still change, so keep the order up to
date from `order.updated`.

## Statuses

| Status                 | Meaning                                                                                      |
| ---------------------- | -------------------------------------------------------------------------------------------- |
| `draft`                | The order is saved for later. It holds no stock, and the supplier does not see it. See [Drafts](/create-orders/#drafts). |
| `pending_verification` | The company is `prequalified`, and the order waits until its verification ends. There is no charge yet. |
| `payment_failed`       | The card charge failed, or a payment is waiting for 3-D Secure. Beauty Router tries once more after 24 hours when the failure can be retried. If that fails too, or the failure cannot be retried, it cancels the order at 24 hours. |
| `placed`               | The card was charged, and the brand or distributor starts preparing the order right after.  |
| `ready_to_ship`        | The order is ready to ship.                                                                  |
| `in_transit`           | The order shipped. `trackingNumbers` has the shipments.                                      |
| `delivered`            | The order was delivered.                                                                     |
| `delivery_failed`      | The delivery failed.                                                                         |
| `canceled`             | The order was canceled. `cancelReason` says why.                                             |

Only a `placed` order takes stock from the supplier. A draft, a `pending_verification` order, and a `payment_failed` order
hold none, so stock can run out before they are placed.

### Cancel reasons

| Reason         | Meaning                                         |
| -------------- | ----------------------------------------------- |
| `salon`        | The salon canceled the order.                   |
| `supplier`     | The brand or distributor canceled the order.    |
| `payment`      | The card charge failed, or the payment was disputed or refunded. |
| `verification` | The company did not pass verification.          |

## Change an order

You can change an order through the API, or let the salon do it in `showOrder(element, orderId)`.

| Action                       | Endpoint                                    | Order statuses                                                                     |
| ---------------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------- |
| Cancel the order             | `POST /v1/orders/{orderId}/cancel`          | `pending_verification`, `placed`, `payment_failed`                                 |
| Change the delivery address  | `PUT /v1/orders/{orderId}/delivery-address` | `draft`, `pending_verification`                                                    |
| Delete a draft               | `DELETE /v1/orders/{orderId}`               | `draft`                                                                            |

An order with another status fails with `order_action_not_allowed`. A user logged in with
[`canOrder: false`](/authentication/#users-who-cannot-order) gets `user_cannot_order`. Each call returns the order, and your server also receives an `order.updated` [webhook](/webhooks/). A draft sends no webhook. Deleting a draft returns `204` with no body.

These have no API, so use `showOrder`:

- **Choose another payment card:** `draft`, `pending_verification`, and `payment_failed`.
- **Pay again:** `payment_failed`, when a retry is allowed.

### Cancel an order

```bash
curl -X POST https://api.beautyrouter.com/v1/orders/order-8Jd3kFs6Gh/cancel \
  -H "Authorization: Bearer $USER_TOKEN"
```

The order gets the status `canceled` with the `cancelReason` `salon`. The rest depends on the status:

| Status                 | What happens                                                                                  |
| ---------------------- | --------------------------------------------------------------------------------------------- |
| `pending_verification` | The order closes. There is no charge to refund, and no brand or distributor is involved.      |
| `placed`               | Beauty Router refunds the full amount, and tells the brand or distributor.                    |
| `payment_failed`       | There is no charge to refund.                                                                 |

The brand may have started shipping a `placed` order. When its system refuses the cancel, the request fails with `409`
`supplier_refused_cancel`, and the order stays `placed`.

### Change the delivery address

Send the full address, with the same fields as the [company's delivery address](/companies/#update-the-company). `phone` is
optional here. Without it, the order uses the company's phone. This changes the order only. The company's default address stays as it is.

```bash
curl -X PUT https://api.beautyrouter.com/v1/orders/order-8Jd3kFs6Gh/delivery-address \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "line1": "2210 Pearl St",
    "city": "Boulder",
    "state": "CO",
    "postalCode": "80302",
    "country": "US",
    "phone": "+13035550187"
  }'
```

A `placed` order cannot change its address, because the brand or distributor starts preparing it right away. The request
fails with `order_action_not_allowed`.

The address must pass these checks:

| Check                                              | Otherwise                       |
| -------------------------------------------------- | ------------------------------- |
| A carrier can deliver to it.                       | `validation_failed`             |
| The company has a license in that state.           | `address_state_without_license` |
| Every hazardous product in the order can go there. | `hazmat_destination`            |
| The brand or distributor ships there.              | `ship_to_restricted`            |

Beauty Router recalculates the sales tax, so `total` can change. A `pending_verification` order keeps its shipping. A
draft's shipping is worked out when it is placed.

### Delete a draft

```bash
curl -X DELETE https://api.beautyrouter.com/v1/orders/order-2Xc9vBn4Mq \
  -H "Authorization: Bearer $USER_TOKEN"
```

The response is `204` with no body. A later request for that ID fails with `not_found`.

## Order tracking

`showOrderTracking(element, orderId)` shows the tracking page of one order, with a tab for each package. Until the order
ships, it says that tracking is not available yet.

To build your own tracking screen, use `trackingNumbers` on the order. Each item has the `number`, the `url` of the
carrier's tracking page, and the `carrier`. The `url` and the `carrier` are left out when Beauty Router does not have
them.

## Order rating

The salon can rate an order once it is delivered. `showOrderRating(element, orderId)` shows the rating form, and
`showOrder(element, orderId)` offers it too. To report a problem with a `delivery_failed` or `canceled` order, the salon uses `showOrder`. The Rate your
order email opens your `orderUrl` page.

## Card statements

Beauty Router charges the card, so the card statement shows `BEAUTYRTR`, an asterisk, and a short form of your app's
name, such as `BEAUTYRTR* ROSELANE`. The whole line has at most 22 characters, which leaves 11 for your app's name.

You set the short form in the salon app admin area. It has at most 11 characters, with Latin letters, digits, and spaces,
and at least one letter.

When you save your app's name and leave the short form empty, the admin area fills it in: the name's Latin letters, digits,
and spaces, cut to 11 characters. You can change it, and a later name change leaves it alone. If the name has no Latin
letter, the field stays empty, and statements show `BEAUTYRTR` alone.

## Emails to salon users

Beauty Router emails your users when an order is placed, shipped, delivered, or canceled, in your app's name. See
[Emails](/emails/).