Skip to main content

The atomic-create principle

The Arcus Products API accepts the complete nested resource graph in a single POST. You express what you want; the API creates every child in one atomic transaction. If any child fails, the entire request rolls back — no orphan products, no partial state.

product_type discriminator

Every product has a product_type that controls which children are valid: Only title and product_type are required; a missing one returns 400 missing_required_field with the name in param. The key needs the products:write scope. A few rules apply to every type:
  • list_price, cost and last_purchase_price cannot be negative. A free item with a price of 0 is fine.
  • part_number must be unique among your active products; a repeat returns 409.
  • A service product never tracks inventory, whatever you send for track_inventory.

Full example: variant parent with kit variants

A “Riser Kit” with two kit variants — Dome and Flat — each with their own component assemblies, three qty-break pricing tiers, and a vendor assignment. One POST call.
The response includes the variants, the product-level pricing and vendors hydrated inline. For a kit or box, the components[] you send are created in the same transaction, but the create response does not echo them back; read them with GET /v1/products/{id}/components. Variant components are returned inside each variant, as below.

Qty-break pricing with pricing_level_id

Each pricing[] element defines one row in a tiered price book: An active tier is unique for its combination of product, variant, pricing level or account, and qty_break. Sending a second one returns 422 duplicate_pricing_policy_for_scope, with param: "qty_break" and the id of the existing tier in conflicting_policy_id; edit that tier or choose a different level, account or quantity. A pricing level or account that is not in your entity is refused. All money fields (list_price, sell_price, adjustment) are returned as JavaScript numbers (float), not strings.

Idempotency

Include Idempotency-Key: <your-unique-key> on every create call. Replaying the same key within 24 hours returns the original response without re-creating resources.

Standalone kit (no variants)

Error handling

Validation errors return the exact param path so you know which field in which child failed:
Transactional rollback: if any child write fails, no rows are written. The response always reflects the full success or full failure state. The param path is prefixed with the child it belongs to, such as variants[0].pricing[2] or vendors[0].

Adding pricing tiers to an existing product

Use POST /v1/products/{id}/pricing to add tiers incrementally after creation:
The call needs products:write and returns 201 with the new tier in data: its id, product_id, pricing_level_id, qty_break, pricing_type, list_price, sell_price, is_default, and demoted_default_policy_id (null unless the new tier took over as default). A product that is not in your entity returns 404. Send variant_id to price one variant of a variant parent rather than the parent.