Sonic runs two billing engines under one organisation: subscription and shipment. The choice is a workspace mode, not a per-invoice toggle. Everything downstream — catalogs, collections labels, bank rec payer matching, journal columns — follows that choice.
What changes when you flip modes
| Surface | Subscription workspace | Shipment workspace |
|---|---|---|
| Bill-to catalog | Customer | Shipper |
| Contracts nav | PDF ingest → schedule | Carrier contracts (rates + mapping) |
| Core billing object | Schedule + phases | Rated shipment |
| Invoice engine | subscription |
shipment |
| Collections account | Customer | Shipper |
| Bank rec payer match | Customer + contacts | Shipper + contacts |
| Journal product column | Product name | Shipment id |
| Credit notes | Supported | Freight path differs; focus on invoice corrections |
JSON and API keys often still say customer_id for historical reasons. UI labels change to shipper where operators work freight.
Subscription mode — what you are optimising for
- Contract PDF → review → approve → billing schedule
- Phases, ramps, one-time fees, payment-gated activation
- Seat snapshots, usage templates, minimum commitments
- Invoices on billing day; immutability after send
- Watchtower on sent unpaid customer invoices
This is the contract-to-cash path for SaaS, usage platforms, and seat-based B2B.
Shipment mode — what you are optimising for
- Carrier ShipmentContract with rated charges and column mapping
- Upload → shipper match → create haul → invoice preview
- Rate indexes with Default + branches
- Invoice grouping by shipper and period window
- Recognition on haul date; collections on shipper
This is the path for logistics operators billing movements, not MRR.
Why one org does not usually do both
You can reason about both engines in code — Invoice.billing_engine branches cleanly — but operators suffer when one workspace pretends to be both:
- Collections shows a Schedules tab that freight invoices will never use.
- Bank rec matches against the wrong contact catalog.
- Revenue journal filters search product names on rows that are actually rate lines under a shipment id.
Product and services companies with a freight experiment should treat freight as a deliberate workspace decision with its own catalogs, not as "customers with trucks."
Switching or standing up a new org
Greenfield shipment orgs: enable shipment billing in organisation settings, build carriers/shippers/contracts, then run a real carrier file before you trust AR metrics.
Subscription orgs adding freight later: plan a catalog migration story for bill-to parties. Shippers are not customers — aliasing names in a spreadsheet is not the same as linking catalog records.
Watch out for
/contractsis subscription PDF ingest. Carrier deals live under Contracts in shipment nav (/shipment-contracts).- Do not fork invoice tables per mode. Shared components branch on
billing_engine; keep one list with filters. - Freight invoices hide subscription-only display fields like PO/Reference defaults where configured.
- Payment links still work — Stripe/Razorpay mark subscription or shipment invoices paid the same way.
- Recognition methods differ. Do not expect deferred revenue on freight hauls.