PPactDocs
Sales

Orders

Read and manage the order rows created when a quote is accepted, with independent financial and fulfillment lifecycles and a per-order event ledger.

Orders

An order is created automatically when a quote is accepted — you don't hand-key orders. The operator API under /v1/orders is a mostly-read surface with a small set of operator overrides; the heavy lifting (invoicing, payment status) is driven by Stripe webhooks and the CPQ order lifecycle. Gated by the sales module.

Live

Orders are backed by the orders and order_events tables and the core.cpq.order_lifecycle state machine. Rows are tenant-scoped and carry a back-pointer to the source quote_id.

Two independent lifecycles

An order tracks money and fulfillment separately, because a service can be delivered before it's paid (or vice versa).

Financial status (status column): pending, paid, cancelled. Stripe drives the real transitions through the webhook handler; the order also carries stripe_invoice_id, stripe_customer_id, hosted_invoice_url, invoice_status (Stripe's draft/open/paid/uncollectible/void), and payment_terms_days (0/15/30/60/90) which seeds the invoice due date and the revenue-recognition start date.

Fulfillment status (fulfillment_status column), a linear pipeline enforced in core.cpq.order_lifecycle:

code
unfulfilled → provisioning → fulfilling → fulfilled → closed
                    ↓             ↓            ↓
                cancelled (reachable from any non-terminal stage)

Forward progression may skip stages (a one-motion delivery can jump unfulfilled → fulfilled); closed is only reachable from fulfilled. The stages provisioning, fulfilling, and fulfilled each fire a workflow trigger (topic order.<stage>) so downstream handoff automation can attach.

Endpoints

Method & pathPurpose
GET /v1/ordersList with status, account_id filters
GET /v1/orders/{id}Detail + back-pointer to the source quote
PATCH /v1/orders/{id}Operator overrides: mark paid offline, cancel an order created in error, adjust billing contact / payment terms

Stripe-driven, not PATCH-driven

PATCH is for out-of-band operator corrections only. Normal payment-state changes arrive through the Stripe webhook handler, not this endpoint. Marking an order paid via PATCH records an offline payment; it does not reconcile against Stripe.

The order event ledger

Every financial and fulfillment transition appends an order_events row — event_type, from_status, to_status, note, actor_user_id — which renders as the order's own timeline on the detail page. This is distinct from the cross-aggregate domain_events audit log; both are written on each transition.

Renewals

Orders carry term_months, renews_at, and a renewal_quote_id back-pointer, so a term order can spawn a renewal quote as its renews_at date approaches (handled in core.cpq.renewals).