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:
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 & path | Purpose |
|---|---|
GET /v1/orders | List 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).