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": "S93-000037",
"status": "completed",
"source": "api",
"fulfillment": "ship",
"location_id": 93,
"register_id": null,
"customer": { "object": "customer", "id": 1166, "first_name": "Jordan", "last_name": "Reyes", "email": "jordan.reyes@example.com", "...": "…" },
"external_reference": "GB-1048812233",
"contains_firearms": false,
"subtotal_cents": 3198,
"discount_cents": 0,
"coupon_code": null,
"trade_in_allowance_cents": 0,
"tax_cents": 0,
"shipping_cents": 995,
"total_cents": 4193,
"paid_cents": 4193,
"refunded_cents": 0,
"tax_rate_bps": 725,
"shipping_address": { "name": "Jordan Reyes", "line1": "3400 Kent Rd", "city": "Stow", "state": "OH", "postal_code": "44224", "country": "US" },
"lines": [
{ "object": "order_line", "id": 9801, "line_type": "product", "product_id": 2062, "sku": "AMM-9-115-50", "description": "Blazer Brass 9mm 115gr FMJ (50)", "qty": 2, "unit_price_cents": 1599, "discount_cents": 0, "tax_cents": 0, "line_total_cents": 3198, "taxable": true, "is_firearm": false, "serialized_item_id": null, "serial_number": null, "refunded_qty": 0 }
],
"payments": [
{ "object": "payment", "id": 7710, "tender": "external", "kind": "charge", "amount_cents": 4193, "status": "captured", "card_brand": null, "card_last4": null, "reference": "GB-PAY-1", "entry": null, "processor": null, "approval_code": null, "refund_of_payment_id": null, "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), awaiting_transfer (paid; the firearm is waiting on its transfer paperwork), 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, transfer_fee, custom, gift_certificate, range, membership (a sign-up, initiation fee or membership dues) |
lines[].serial_number |
The unit sold, when the product is serialized |
payments[].tender |
cash, card, check, gift_card, store_credit, external, layaway, on_account |
payments[].kind |
charge or refund |
payments[].reference |
Only for external and check tenders |
payments[].entry |
For card payments when the shop has card processing on: terminal (charged on the shop's card terminal), token (a saved card, e.g. membership dues), manual (typed in from a separate terminal, approved by a manager). null otherwise. |
payments[].processor |
payroc when the card went through the shop's processor (simulator in a test shop). null for cards recorded by hand and every other tender. |
payments[].approval_code |
The processor's approval code on a processor card charge. null otherwise. |
payments[].refund_of_payment_id |
On a refund that went back to the card: the id of the charge it returned money to. |
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. S93-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 Iron Register owns.
| Field | |
|---|---|
lines |
Required, 1–100 lines: { "product_id" or "sku", "qty": 1–9999, "unit_price_cents"?, "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. GunBroker. 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, ffl_number } |
shipping_cents |
Shipping charged |
tax_exempt |
true when the channel collected and remits the sales tax (a marketplace facilitator), so Iron Register 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 or firearm 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. - Any serialized or firearm line. The order is saved held, with
next_action: "complete_at_register". Staff open it at the register, pick the serial number, and for a firearm run the Form 4473 or the FFL-to-FFL shipment. When they complete it,sale.completedfires. The API never completes a firearm disposition. Aserial_numberon a line is passed to staff as a request; it doesn't reserve the unit. Firearm 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.ironregister.com/api/v1/orders \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{
"external_reference": "GB-1048812233",
"channel": "GunBroker",
"lines": [ { "sku": "AMM-9-115-50", "qty": 2, "unit_price_cents": 1599 } ],
"customer": { "email": "jordan.reyes@example.com", "first_name": "Jordan", "last_name": "Reyes" },
"shipping_cents": 995,
"shipping_address": { "name": "Jordan Reyes", "line1": "3400 Kent Rd", "city": "Stow", "state": "OH", "postal_code": "44224", "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, 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 and records a refund of the payments.
- Orders with firearm lines are refused with
409 FIREARM_ORDER, because they're voided in Iron Register, where the 4473 is cancelled with them. - Also refused:
ALREADY_VOIDED, andSALE_REFUNDEDonce any part has been refunded.
Returns the voided order.