API docs
Orders
Permission: orders (read / write).
An order is a sale on the POS. That covers sales rung up at a register, online orders brought to the counter, and orders created through this API.
The order object
{
"object": "order",
"id": 5521,
"number": "S18-000037",
"status": "completed",
"source": "api",
"fulfillment": "ship",
"location_id": 18,
"register_id": null,
"customer": { "object": "customer", "id": 1166, "first_name": "Jordan", "last_name": "Reyes", "email": "jordan.reyes@example.com", "...": "…" },
"external_reference": "ETSY-3318842210",
"subtotal_cents": 4800,
"discount_cents": 0,
"coupon_code": null,
"trade_in_allowance_cents": 0,
"tax_cents": 0,
"shipping_cents": 695,
"total_cents": 5495,
"paid_cents": 5495,
"refunded_cents": 0,
"tax_rate_bps": 725,
"shipping_address": { "name": "Jordan Reyes", "line1": "118 Maple Ave", "city": "Hudson", "state": "OH", "postal_code": "44236", "country": "US" },
"lines": [
{ "object": "order_line", "id": 9801, "line_type": "product", "product_id": 617, "sku": "CDL-SSD-08", "description": "Sea Salt & Driftwood Soy Candle 8oz", "qty": 2, "unit_price_cents": 2400, "discount_cents": 0, "tax_cents": 0, "line_total_cents": 4800, "taxable": true, "serialized_item_id": null, "serial_number": null, "lot_id": null, "lot_number": null, "refunded_qty": 0 }
],
"payments": [
{ "object": "payment", "id": 7710, "tender": "external", "kind": "charge", "amount_cents": 5495, "status": "captured", "card_brand": null, "card_last4": null, "reference": "ETSY-PAY-1", "created_at": "2026-09-25T01:49:10Z" }
],
"bigcommerce_order_id": null,
"completed_at": "2026-09-25T01:49:10Z",
"voided_at": null,
"created_at": "2026-09-25T01:49:10Z",
"updated_at": "2026-09-25T01:49:10Z"
}
| Field | Values / notes |
|---|---|
status |
open, held (parked, to be finished at the register), completed, voided, refunded, partially_refunded |
source |
pos (register), api, and other channels |
fulfillment |
in_store, ship, pickup |
customer |
The full customer when the key has customers:read (plus date_of_birth with customer_dob:read). Otherwise just { "object": "customer", "id": … }. null for walk-in sales. |
external_reference |
Your reference, on orders created through the API. null on every other order. |
lines[].line_type |
product, fee, service, custom, gift_certificate |
lines[].serial_number |
The unit sold, when the product is serialized |
lines[].lot_id, lines[].lot_number |
The lot sold from, when the product is lot-tracked |
payments[].tender |
cash, card, check, gift_card, store_credit, external, layaway |
payments[].kind |
charge or refund |
payments[].reference |
Only for external and check tenders |
List
GET /v1/orders, filtered to the locations the key can see.
| Filter | |
|---|---|
status |
Any status above |
source |
e.g. api |
customer_id |
|
external_reference |
Your reference |
number |
e.g. S18-000037 |
location_id, updated_since, limit, cursor |
Get
GET /v1/orders/{id}. An order at a location the key can't see is a 404.
Create an order
POST /v1/orders (write) puts a sale from an outside channel onto the POS, so the stock comes off the shelf Black Beach POS owns.
| Field | |
|---|---|
lines |
Required, 1–100 lines: { "product_id" or "sku", "qty": 1–9999, "unit_price_cents"?, "lot_id"?, "serial_number"? }. Without unit_price_cents the POS price applies (including sale and customer-group prices). |
location_id |
Defaults to the key's only location, else the shop's default |
external_reference |
Up to 36 characters. Makes the create idempotent: sending an order with a reference that already exists returns the existing order with 200 and creates nothing. A reference already taken by something other than an API order is 409 EXTERNAL_REFERENCE_TAKEN. |
channel |
Up to 40 characters, e.g. Etsy. Shown to staff and used as the payment processor label. |
customer_id or customer |
An existing customer, or { email, first_name, last_name, phone?, company?, address{}?, marketing_opt_in? }. With customer, the buyer is matched by email, or created if new. |
fulfillment |
ship, pickup or in_store. Default: ship when shipping_address is sent, otherwise pickup. |
shipping_address |
{ name, company, line1, line2, city, state, postal_code, country, phone, email } |
shipping_cents |
Shipping charged |
tax_exempt |
true when the channel collected and remits the sales tax (a marketplace facilitator), so Black Beach POS doesn't charge it again. Otherwise tax is computed at the location's rate. |
paid |
Default true: paid on the outside channel |
payment_reference |
The channel's payment or transaction id |
notes |
Up to 1,000 characters, shown to staff |
What happens next:
- Paid, no serialized lines. The order completes immediately. An
externalpayment for the full total is recorded and stock comes off the location. The response hasstatus: "completed"andnext_action: null. - Lot-tracked lines. Send
lot_idto sell from a particular lot (it must be that product's lot, at that location, with the stock). Without it, the earliest-expiring lot with enough stock is used, the same as at the register. The line reportslot_idandlot_number. - Any serialized line. The order is saved held, with
next_action: "complete_at_register". Staff open it at the register, pick the unit and complete it;sale.completedfires then. Aserial_numberon a line is passed to staff as a request; it doesn't reserve the unit. Serialized lines withqtyover 1 are split into one line per unit. paid: false. The order is held withnext_action: "collect_payment_at_register".
curl -X POST https://app.blackbeachpos.com/api/v1/orders \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{
"external_reference": "ETSY-3318842210",
"channel": "Etsy",
"lines": [ { "sku": "CDL-SSD-08", "qty": 2, "unit_price_cents": 2400 } ],
"customer": { "email": "jordan.reyes@example.com", "first_name": "Jordan", "last_name": "Reyes" },
"shipping_cents": 995,
"shipping_address": { "name": "Jordan Reyes", "line1": "118 Maple Ave", "city": "Hudson", "state": "OH", "postal_code": "44236", "country": "US" },
"tax_exempt": true,
"payment_reference": "GB-PAY-771"
}'
Returns 201 with the order plus next_action.
Possible errors:
400 VALIDATION_ERROR: an unknown product (lines.N.product), a bad qty or price, alot_idon a product that doesn't track lots, or an unknowncustomer_id.409 INSUFFICIENT_STOCK: the shop doesn't allow negative stock and the location is short. Nothing is saved.
See the marketplace orders guide for a full walkthrough.
Void an order
POST /v1/orders/{id}/void with { "reason": "Customer cancelled" } (write).
- Voiding restores stock (to the same lots) and records a refund of the payments.
- Refused:
ALREADY_VOIDED, andSALE_REFUNDEDonce any part has been refunded.
Returns the voided order.
