What it does
- Rate groups. A named pricing scheme you attach to a customer, a contract, or a class of work. Most accounts end up with a handful: a standard schedule, a couple of negotiated ones, and an internal cost schedule.
- Typed rates. Each charge is one of three shapes — per unit (per kilometre, per kilogram, per item, per minute of wait), a percentage of another line (a fuel levy, a surcharge), or a commission split.
- Rate conditions. The rules that decide whether a rate applies at all: vehicle type, service level, day of week, time of day, a distance band, a weight band.
- Rate zones. Pricing by geography — a metro zone, a regional zone, an airport surcharge area — defined over a named boundary shape.
- Calculation. Send a job and get back the customer charge and the driver cost, broken down line by line, with the rate that produced each line identified. Optionally persist those lines against the stop.
Rate definitions and the calculation engine are available now. Invoice batch generation — rolling priced jobs into a periodic invoice document per customer — is planned and not yet callable. Charge and cost lines can be persisted against stops today; assembling them into an invoice is currently yours to do.
How it works
Pricing is data, not code
Your commercial terms live on the platform as definitions you can read, edit and audit. Changing a fuel levy is an edit, not a deployment. Because every quote and every completed job runs through the same definitions, the price you quoted on Monday and the price you invoice at month end are produced by the same rules.
Deterministic by design
Conditions are evaluated in a defined order, so identical inputs always produce an identical price. This is what makes the numbers defensible: when a customer queries a charge, you can re-run the same job and get the same answer, with each line traced back to the rate that generated it.
Both sides of the job
Most pricing systems model revenue. This one models revenue and cost together — the charge to the customer and the pay to the driver or subcontractor — so margin is visible per job rather than emerging at the end of a month. A rate group can be a customer schedule or a driver pay schedule; the machinery is the same.
Zones share the boundary library
A rate zone is defined over a named boundary shape from the platform’s shared shape library, not a private copy. The same suburb outline can be a rate zone, a service area and a geofence at once. When a boundary genuinely moves, you edit it once and everything linked to it follows — no risk of pricing a delivery against last year’s zone because only two of three copies were updated.
The reverse protection matters too: changing the shape from inside a single geofence creates a private copy for that geofence rather than altering the shared boundary. A pricing zone never shifts because somebody adjusted an alerting boundary.
Permissions
| Permission | What it allows | Needed |
|---|---|---|
| rates.read | Read rate definitions — groups, rates, conditions and zones. | Required |
| rates.write | Create, update and delete rate definitions. | Required to configure |
| rates.execute | Run the pricing engine — price a job into charge and cost lines, and optionally persist them. | Required to price |
| polygons.read | Read the boundary shapes a rate zone is defined over, and link an existing shape to a zone. | Required for zones |
| polygons.write | Create and edit boundary shapes for rate zones. Editing a shape that other modules also use additionally requires write permission on those modules, because moving a shared boundary can change prices. | Optional |
| stops.read | Read the stops being priced. | Required to price |
| orders.read | Read the order a priced job belongs to. | Optional |
| documents.read | Read metadata for documents attached to a job, such as proof of delivery supporting a charge. | Optional |
API reference
Rate definitions are callable today. The quote engine and invoicing batches are not yet published.
| Service | What it covers | Reference | Contract |
|---|---|---|---|
| rates-api | Rate groups, rates, conditions and zones. | OpenAPI description | openapi.json |
| polygons-api | The shared boundary library that rate zones link to. | OpenAPI description | openapi.json |
Each openapi.json is the generated OpenAPI 3.1 contract, served by the service itself — it is never hand-edited, so it cannot drift from the running API. The OpenAPI description link opens the same contract as a browsable reference. Both resolve to the environment these docs are served from, so a page read on the test site links to the test contract.
Events
This module does not raise events of its own today. The events it is expected to raise are listed below so you can plan a subscription around them, but they are not yet published on the event stream.
Pricing is a request-and-response operation: you ask for a price and get one. There is no pricing event to wait for. What you do subscribe to are the delivery events that tell you a job is ready to be priced.
| Event | Raised when | Tells you |
|---|---|---|
| stop.completed | A stop reaches its terminal completed state. | The job is finished and its actuals are settled — the usual trigger to price it. |
| package.delivered | A package is delivered. | Per-package confirmation, for schedules that charge by item rather than by stop. |
| order.completed | Every stop on an order is done. | The order is ready to be rolled into an invoice. |
| stop.failed | A delivery or collection attempt fails. | The reason — most schedules price a failed attempt differently from a successful one. |
Invoice lifecycle events arrive with invoice batch generation.
The envelope these arrive in, the delivery and retry guarantees, and every other event type are in Events & webhooks — or jump straight to the event catalog.