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 external payment for the full total is recorded and stock comes off the location. The response has status: "completed" and next_action: null.
  • Lot-tracked lines. Send lot_id to 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 reports lot_id and lot_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.completed fires then. A serial_number on a line is passed to staff as a request; it doesn't reserve the unit. Serialized lines with qty over 1 are split into one line per unit.
  • paid: false. The order is held with next_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, a lot_id on a product that doesn't track lots, or an unknown customer_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, and SALE_REFUNDED once any part has been refunded.

Returns the voided order.