Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.arcuserp.com/llms.txt

Use this file to discover all available pages before exploring further.

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
The response is the fully hydrated order with line_items, payments, and tax_lines expanded inline. You never need a follow-up GET to see what was created.

Canonical scenario: 3 line items + discount + tax + allocation + payment

curl -s -X POST "https://api.arcuserp.com/v1/orders" \
  -H "Authorization: Bearer ark_live_ent_wss_abc123xyz" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "document_type": "sales_order",
    "account_id": "c3f2a7b1-0e44-4f89-9d12-aabb0c123456",
    "location_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "shipping_address_id": "d4e5f6a7-b8c9-0123-def4-567890abcdef",
    "billing_address_id": "e5f6a7b8-c9d0-1234-ef56-7890abcdef01",
    "line_items": [
      {"product_id": "11111111-2222-3333-4444-555555555555", "quantity": 5},
      {"product_id": "22222222-3333-4444-5555-666666666666", "quantity": 3},
      {
        "product_id": "33333333-4444-5555-6666-777777777777",
        "variant_id": "44444444-5555-6666-7777-888888888888",
        "quantity": 10
      }
    ],
    "discount": {"type": "percentage", "value": 5},
    "auto_compute_tax": true,
    "auto_allocate_inventory": true,
    "payments": [
      {
        "payment_method_id": "55555555-6666-7777-8888-999999999999",
        "amount": 750.00,
        "payment_type": "card",
        "auto_capture": true
      }
    ]
  }'

Document types

document_typeUse forAccount type
quoteNon-binding estimateCustomer
sales_orderStandard order (confirm to allocate)Customer
invoiceBillable invoice (auto-posts to AR)Customer
returnReturn merchandise authorizationCustomer
purchase_orderPurchase from a vendorVendor

Auto-populate from account defaults

When you provide an account_id, Arcus auto-resolves:
  • Shipping and billing address from is_default_shipping / is_default_billing flags
  • Pricing level from accounts.default_pricing_level_id
  • Payment terms from account, then channel, then entity-level default
  • Sales channel from accounts.default_sales_channel_id
  • Tax exemption from accounts.tax_exempt and accounts.avatax_entity_code
You only need to provide overrides. A minimal valid request is {document_type, account_id}.

Line items

{
  "line_items": [
    {"product_id": "...", "quantity": 5},
    {"product_id": "...", "variant_id": "...", "quantity": 3},
    {"product_id": "...", "quantity": 1, "sell_rate": 49.99, "notes": "Special price"}
  ]
}
  • Pricing engine runs automatically: qty-break pricing levels, account default pricing level. Override with sell_rate.
  • Variant guard: if the product has variants, variant_id is required. Otherwise the request returns 422 validation_error with param: "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

{
  "discount": {"type": "percentage", "value": 5}
}
Supported types:
TypeBehavior
percentageApplied to line item subtotal sum (before tax). value = 0-100.
flatFixed dollar amount deducted from subtotal.
The discount is stored as an order_adjustment row with adjustment_type: 'discount'.

Automatic tax (auto_compute_tax)

{"auto_compute_tax": true}
When true, Arcus calls AvaTax (or local tax rates if AvaTax is not configured) after all items are inserted. Results appear in tax_lines[] and tax_total in the response. Requirements:
  • shipping_address_id must resolve to a taxable US jurisdiction.
  • Tax-exempt accounts (with avatax_entity_code) are auto-detected and tax_total will be 0.
Tax calculation is non-blocking: if AvaTax is unavailable, the order is still created and tax_total defaults to 0. You can trigger a re-calculation later via POST /v1/orders/{id}/recalculate.

Inventory allocation (auto_allocate_inventory)

{"auto_allocate_inventory": true}
When true, Arcus reserves stock per line item via ORDER_RESERVATION inventory transactions.
  • Location: uses location_id from the request, or entity default location.
  • Shortfall guard: if ANY line item has insufficient available stock, the entire request returns 422 insufficient_inventory with 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

{
  "error": "insufficient_inventory",
  "code": "insufficient_inventory",
  "type": "validation_error",
  "param": "line_items[2].quantity",
  "hint": "Insufficient inventory for product 33333333-.... Requested: 10, available: 4.",
  "product_id": "33333333-...",
  "requested": 10,
  "available": 4
}

Inline payments (payments[])

{
  "payments": [
    {
      "payment_method_id": "55555555-...",
      "amount": 750.00,
      "payment_type": "card",
      "auto_capture": true
    }
  ]
}
payment_method_id is the Arcus internal UUID from account_payment_methods (not the Stripe pm_xxx ID). Use GET /v1/accounts/{id}/payment-methods to list available methods. 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. Retry the payment via POST /v1/orders/{id}/payments. Inline payment results appear in _inline_payment_results[] alongside the main response.

Idempotency

Include the Idempotency-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.
-H "Idempotency-Key: order-session-2026-05-12-001"
Use a stable key tied to your create session (e.g. session ID + timestamp). Keys expire after 24 hours.

GL behavior

No GL entries fire at order create. The general ledger is only affected at:
  • Fulfillment: COGS debit, inventory credit
  • Payment: AR debit, cash/revenue credit
This matches NetSuite and Acumatica behavior. Quotes and sales orders are subledger-only until fulfilled and invoiced. See GL Fundamentals for the full posting map.

Webhook events

Subscribers on order.created receive this event immediately after the order is committed. If inline payments succeed, payment.received fires as well. See Webhooks for event schemas and retry policy.

Error reference

CodeHTTPMeaning
missing_field400Required top-level field missing (document_type, account_id)
validation_failed400Inline child validation failure (see param for exact path)
line_item_failed400A specific line item was rejected (inactive product, box type, etc.)
insufficient_inventory422auto_allocate_inventory shortfall — see product_id, requested, available
account_role_mismatch422Sales order against a vendor account (or PO against a customer account)
tax_exempt_requires_exemption_code422tax_exempt: true but no avatax_entity_code on order or account
payment_method_not_found404Inline payment_method_id UUID does not exist for this entity
insufficient_scope403API key lacks orders:write scope