Overview
POST /v1/orders is the atomic create endpoint for all order document types (quotes, sales orders, invoices, purchase orders, and returns). A single request can create the order header plus all inline children:
- Line items with automatic pricing (qty-break levels, account defaults)
- Header-level discount (percentage or flat)
- Automatic tax via AvaTax or local rates
- Inventory allocation per line item
- Inline payment charged after commit
line_items, payments, and tax_lines expanded inline. You never need a follow-up GET to see what was created.
document_type is the one required field and account_id is optional (send it whenever the order is for a customer); the key needs the orders:write scope. If the account does not belong to your entity, or an address id you send does not, the call returns 404 account_not_found or 404 address_not_found, and nothing is created. An id from another entity and an id that does not exist give the same answer.
Canonical scenario: 3 line items + discount + tax + allocation + payment
Document types
Auto-populate from account defaults
When you provide anaccount_id, Arcus auto-resolves:
- Shipping and billing address from the account’s default shipping and default billing addresses
- Pricing level from the account’s default pricing level
- Payment terms from the account, then the sales channel, then the entity default
- Sales channel from the account’s default sales channel
- Tax exemption from the account’s tax-exempt flag and AvaTax entity code
{document_type, account_id}.
Line items
- Pricing engine runs automatically: qty-break pricing levels, account default pricing level.
Override with
sell_rate, or with its aliasunit_price. Sending both with different values returns400 price_field_conflict, and a zero or negative price returns422 invalid_price. When an override differs from the catalog list price, the order is still created and the response carries awarningsarray naming the line, the catalog price and the applied price; the override is also recorded in the order’s activity history. - Variant guard: if the product has variants,
variant_idis required. Otherwise the request returns422 validation_errorwithparam: "line_items[N].variant_id". - Kit expansion: kit products automatically expand their components as child line items (zero-dollar children). No additional calls needed.
- Any child failure rolls back all items and the order header. Nothing is persisted on validation error.
Discount
The discount is recorded as a discount adjustment on the order.
value must be 0 or more, and a percentage cannot exceed 100; either mistake returns 400 validation_failed with param: "discount.value".
Automatic tax (auto_compute_tax)
auto_compute_tax: true also runs it again after automatic coupons have been applied, so the final tax reflects them. Results appear in tax_lines[] and tax_total in the response, and the totals (subtotal, tax_total, shipping_total, order_total) are always numbers.
Requirements:
shipping_address_idmust resolve to a taxable US jurisdiction.- Tax-exempt accounts (with
avatax_entity_code) are auto-detected andtax_totalwill be 0.
tax_total defaults to 0. You can trigger a re-calculation later via POST /v1/orders/{id}/recalculate.
Inventory allocation (auto_allocate_inventory)
true, Arcus reserves stock for each line item. A reservation holds the units for this order; it does not ship them.
- Location: uses
location_idfrom the request, or entity default location. - Shortfall guard: if ANY line item has insufficient
availablestock, the entire request returns422 insufficient_inventorywith per-product shortfall details. The rollback is atomic — nothing is allocated if any line fails. - Release: inventory is automatically released on order cancel or void.
Example shortfall response
Inline payments (payments[])
payment_method_id and a positive amount; a missing one returns 400 validation_failed with the path in param. payment_type defaults to card, and save_card: true asks Arcus to keep the card on the account for later use. Inline payments are meant for a payment method already saved on the account. Take cash, check, credit-memo, terms and external payments with the separate call below.
In this inline form, payment_method_id is the Arcus internal UUID of a saved method (the id in the list from GET /v1/accounts/{id}/payment-methods), not the Stripe pm_xxx id.
Payment fires after the order is committed to the database. This matches industry behavior:
if the payment fails, the order still exists with payment_status: unpaid, and the create call still returns 201. A failed entry shows up in _inline_payment_results[] with its index, an error and a hint; for example payment_method_not_found means the id does not belong to your entity, and payment_failed means the charge did not go through. Retry it with the call below.
Inline payment results appear in _inline_payment_results[] alongside the main response.
Taking a payment later (POST /v1/orders/{id}/payments)
Use this to retry a failed inline payment, to collect a balance, or to record a payment that is not a saved card. The key needs both orders:write and payments:write.
Other fields depend on the type:
amount_tendered for cash, check_number for a check, credit_memo_id for a credit memo, and external_reference for an external payment.
A successful card payment returns 200 with data.amount_charged, data.processing_fee, data.new_payment_status, data.payment_intent_id and data.journal_entry_id. A payment larger than the balance due is not rejected: the excess becomes a credit memo on the account (overpayment_credit and credit_memo_id in the result).
Taking a payment posts its journal entry, updates the order’s
amount_paid, balance_due and payment_status, and fires the payment events described under Webhook events.
Recalculating totals (POST /v1/orders/{id}/recalculate)
Call this after a change that happened outside the create call, such as a manual line edit, a tax jurisdiction change or a pricing rule update. It takes no body, needs orders:write, and returns the order with its subtotal, tax, shipping, discount and order_total worked out again.
Idempotency
Include theIdempotency-Key header to safely retry without double-creating the order.
The same key returns the same response (including the same order number) without
re-running any DB writes or Stripe charges.
GL behavior
Creating a quote or a sales order posts no ledger entry. The general ledger is affected later:- Fulfillment: COGS debit, inventory credit
- Payment: each payment posts its own journal entry, and the card payment result returns its
journal_entry_id
Webhook events
Subscribers onorder.created receive this event immediately after the order is committed. A payment that goes through fires the events of the payment family (for example payment.created and payment.succeeded). See Webhooks
for event schemas and retry policy.
Side effects of creating an order
- A confirmation email goes to the account’s email address when the request includes line items. If the account has no email on file, nothing is sent and the skipped send is logged.
- Automatic coupons that apply to the order are evaluated and applied, and the totals are recalculated.
- Reserved stock (with
auto_allocate_inventory) is held until the order ships, is cancelled or is voided.

