Skip to content

Rate Card and Quotation System -- API Reference

**Platform:** iDrv5-MyFR8 Logistics Management **Version:** 2.0 (Post-Overhaul Schema) **Last Updated:** April 2026

Platform: iDrv5-MyFR8 Logistics Management
Version: 2.0 (Post-Overhaul Schema)
Last Updated: April 2026


  1. Overview
  2. Rate Cards API
  3. Rate Entries API
  4. Zones API
  5. Service Levels API
  6. Warehouse Rates API
  7. Surcharges API
  8. Addons API
  9. Customers API
  10. Quotes API
  11. Data Model Reference
  12. Rate Types Reference
  13. Integration Guide
  14. Appendix

The rate computation pipeline follows this hierarchy:

Rate Card
|-- Rate Entries (one per route: origin zone -> destination zone)
| |-- Rate Entry Tiers (range-based pricing: weight, pallet count)
| |-- Rate Entry Conditions (dimension/weight/transport config constraints)
| |-- Rate Overrides (custom pricing per service level)
| |-- Rate Entry Surcharges (per-entry addon overrides/exclusions)
| |-- Rate Card Variations (transport config-specific pricing)
|-- Addons (surcharges, discounts, taxes -- waterfall engine)
|-- Transit Times (from profiles or custom overrides)
|-- Warehouse Rates (activity-based pricing for warehouse-type cards)
|
Zones
|-- Zone Suburbs (postcode/suburb mappings for zone resolution)
|
Service Levels (Express, Standard, Economy)
|-- Multiplier-based pricing (base_cost_multiplier applied to base rates)
|-- Custom overrides (rate_overrides table per entry+service level)
|-- Cubic factor (for chargeable weight computation)

Most endpoints require Flask-Login session authentication. Endpoints marked with @login_required will return 401 Unauthorized if no valid session exists.

Development shortcuts for authentication:

  • GET /super-admin — auto-login as super admin
  • GET /direct-operations — auto-login as operations user

All API endpoints use the /api/ prefix:

  • Rate Cards: /api/rate-cards/
  • Rate Entries: /api/rate-entries/
  • Zones: /api/zones
  • Service Levels: /api/service-levels
  • Warehouse Rates: /api/warehouse-rates/
  • Addons: /api/addons/
  • Customers: /api/customers/
  • Quotes: /api/quotes/
  • Surcharges: /api/surcharges

All endpoints return JSON. The standard envelope is:

{
"success": true,
"data": { ... }
}

or on error:

{
"success": false,
"error": "Description of the error"
}

Some older endpoints use alternative keys (rate_cards, zones, rate_entries, message, etc.) instead of the generic data wrapper. The specific response shape for each endpoint is documented below.

  • API requests/responses: yyyy-mm-dd (ISO 8601 date)
  • Frontend display: dd/mm/yyyy (Australian format)
  • Always convert on the frontend before sending to API.

Blueprint prefix: /api/rate-cards
Source: api/rate_cards_api.py

GET /api/rate-cards/

Query Parameters:

ParameterTypeDefaultDescription
pageint1Page number
per_pageint20Items per page
statusstringallFilter by status (all, active, suspended)

Response:

{
"rate_cards": [
{
"id": 1,
"name": "JATT Per KG Rates",
"rate_type": "actual_weight",
"rate_calculation_method_id": 3,
"effective_date": "2025-01-01",
"expiry_date": null,
"description": "Standard per-KG rates",
"is_active": true,
"transit_time_mode": "inherit",
"transit_time_profile_id": null,
"created_at": "2025-01-01T00:00:00",
"updated_at": "2025-06-15T10:30:00",
"status": "active",
"customers": [
{ "id": 5, "name": "Acme Logistics" }
],
"cargo_types": [
{ "id": 1, "name": "General (Dry)", "code": "DRY" }
]
}
],
"total": 25,
"pages": 2,
"current_page": 1,
"statistics": {
"total_rate_entries": 3500,
"active_rate_entries": 3200,
"total_rate_cards": 25,
"active_rate_cards": 22,
"total_zones": 150
}
}
GET /api/rate-cards/:id

Response:

{
"success": true,
"rate_card": {
"id": 1,
"name": "JATT Per KG Rates",
"rate_type": "actual_weight",
"rate_calculation_method_id": 3,
"effective_date": "2025-01-01",
"expiry_date": null,
"description": "Standard per-KG rates",
"is_active": true,
"transit_time_mode": "inherit",
"transit_time_profile_id": null,
"transit_profile_name": null,
"created_at": "2025-01-01T00:00:00",
"updated_at": "2025-06-15T10:30:00",
"status": "active",
"customers": [
{ "id": 5, "name": "Acme Logistics" }
]
}
}

Error Codes: 404 — Rate card not found.

POST /api/rate-cards/

Requires: @login_required

Request Body:

{
"name": "New Rate Card",
"rate_type": "pallet",
"rate_calculation_method_id": 2,
"effective_date": "2025-07-01",
"expiry_date": "2026-06-30",
"notes": "Optional description",
"customers": ["Acme Logistics", "Beta Transport"],
"cargo_types": [1, 3]
}
FieldTypeRequiredDescription
namestringYesUnique name for the rate card
effective_datestringYesFormat: yyyy-mm-dd
rate_calculation_method_idintYesFK to rate_calculation_methods
rate_typestringNoOne of: load, pallet, actual_weight, time, warehouse, quantity, flat_rate, weight. Default: load
expiry_datestringNoMust be after effective_date
notesstringNoStored as description
customersstring[]NoCustomer names to associate
cargo_typesint[]NoCargo type IDs. Defaults to DRY if empty

Response: 201 Created

{
"success": true,
"message": "Rate card created successfully",
"rate_card": {
"id": 26,
"name": "New Rate Card",
"rate_type": "pallet",
"customers": [{ "id": 5, "name": "Acme Logistics" }],
"cargo_types": [{ "id": 1, "name": "General (Dry)", "code": "DRY" }]
}
}

Error Codes:

  • 400 — Missing required fields
  • 409 — Duplicate name or database constraint violation
PUT /api/rate-cards/:id

Requires: @login_required

Request Body (all fields optional):

{
"name": "Updated Name",
"rate_calculation_method_id": 3,
"effective_date": "2025-07-01",
"expiry_date": "2026-12-31",
"notes": "Updated description",
"transit_time_mode": "profile",
"transit_time_profile_id": 2,
"customers": ["Acme Logistics"],
"cargo_types": [1, 2]
}
FieldTypeDescription
transit_time_modestringOne of: inherit, profile, custom, none
transit_time_profile_idint/nullFK to transit_time_profiles (validated for existence)
customersstring[]Replaces all customer associations (names matched case-insensitively)
cargo_typesint[]Replaces all cargo type associations

Response:

{
"success": true,
"message": "Rate card updated successfully",
"rate_card": { ... }
}

Error Codes:

  • 400 — Expiry date before effective date, invalid transit_time_mode
  • 404 — Rate card or transit profile not found
  • 409 — Duplicate name
DELETE /api/rate-cards/:id

Requires: @login_required

Soft-deletes by setting is_active = false and status = 'suspended'.

POST /api/rate-cards/:id/suspend
POST /api/rate-cards/:id/activate

Requires: @login_required

Suspend Request Body (optional):

{
"reason": "Rate review in progress"
}

Both create audit entries.

GET /api/rate-cards/calculation-methods

Response:

{
"success": true,
"methods": [
{ "id": 1, "display_name": "Per Load (FTL)", "symbol": "$", "order_no": 1 },
{ "id": 2, "display_name": "Per Pallet", "symbol": "$/plt", "order_no": 2 },
{ "id": 3, "display_name": "Per KG (Actual Weight)", "symbol": "$/kg", "order_no": 3 },
{ "id": 4, "display_name": "Per Cubic Meter", "symbol": "$/m3", "order_no": 4 },
{ "id": 5, "display_name": "Per Hour", "symbol": "$/hr", "order_no": 5 },
{ "id": 10, "display_name": "Warehouse", "symbol": "$", "order_no": 10 }
]
}
GET /api/rate-cards/:id/transit-times

Requires: @login_required

Returns merged list from custom overrides (rate_card_transit_overrides) and profile entries (transit_time_entries). Each entry has a source field: "custom", "inherit", or "profile".

Response:

{
"success": true,
"transit_times": [
{
"id": 45,
"origin_id": "1",
"origin_code": "SYD",
"origin_name": "Sydney Metro",
"destination_id": "2",
"destination_code": "MEL",
"destination_name": "Melbourne Metro",
"transit_hours": 24,
"service_type": "Express",
"last_updated": "2025-06-15T10:00:00",
"source": "custom"
}
]
}
POST /api/rate-cards/:id/transit-times

Requires: @login_required

Upserts into rate_card_transit_overrides.

Request Body:

{
"origin_id": 1,
"destination_id": 2,
"transit_hours": 24,
"service_type": "Express"
}
DELETE /api/rate-cards/:id/transit-times/:origin_id/:destination_id?service_type=Express

Requires: @login_required

GET /api/rate-cards/:id/addons

Returns combined list: addons assigned via form_target=rate_card, global addons (apply_to_all_rate_cards=true), and directly assigned via addon_rate_cards.

Each addon includes override_value, is_global, and is_directly_assigned flags.

POST /api/rate-cards/:id/addons

Request Body:

{
"addon_ids": [1, 2, 3],
"override_values": { "1": "15.50", "3": "2.5" }
}
PUT /api/rate-cards/:id/addons/:addon_id

Request Body:

{
"override_value": "18.00"
}
DELETE /api/rate-cards/:id/addons/:addon_id

Cannot remove global addons (returns 400).

GET /api/rate-cards/:id/zones/export

Returns CSV file download.

POST /api/rate-cards/:id/zones/import

Multipart form upload with field zones_file (CSV file).

GET /api/rate-cards/:id/transit-times/export
POST /api/rate-cards/:id/transit-times/import

Multipart form upload with field transit_times_file (CSV or XLSX).


Blueprint prefix: /api/rate-entries
Source: api/rate_entries_api.py

GET /api/rate-entries/rate-card/:rate_card_id

Returns all rate entries for a specific rate card, one row per route (origin-destination pair).

Response:

{
"rate_entries": [
{
"id": 101,
"rate_card_id": 1,
"origin_zone_id": 5,
"destination_zone_id": 8,
"base_rate": 45.00,
"flat_rate": null,
"minimum_rate": 25.00,
"maximum_rate": 999999.00,
"charging_type": "flat_rate",
"display_name": null,
"transit_time_hours": null,
"vehicle_type_id": null,
"distance_km": null,
"rate_per_km": null,
"rate_per_tonne": null,
"minimum_hours": null,
"load_rate": null,
"drop_rate": null,
"rate_calculation_method_name": "actual_weight",
"origin_zone_code": "SYD",
"origin_zone_name": "Sydney Metro",
"destination_zone_code": "MEL",
"destination_zone_name": "Melbourne Metro",
"vehicle_type_name": null,
"vehicle_type_code": null,
"conditions_count": 1,
"tiers_count": 3,
"surcharges_count": 0,
"overrides_count": 2,
"variations": [
{
"id": 10,
"variation_name": "Pantech",
"flat_rate": 550.00,
"charging_type": "per_unit"
}
],
"base_rate_value": 45.00,
"minimum_rate_value": 25.00,
"maximum_rate_value": 999999.00
}
],
"total_count": 1071,
"variations_count": 15
}
GET /api/rate-entries/:id

Returns full detail including service levels (with multiplier-calculated effective rates and overrides), conditions, tiers, surcharges (merged inherited + per-entry), and variations.

Response (abbreviated):

{
"success": true,
"rate_entry": {
"id": 101,
"rate_card_id": 1,
"origin_zone_id": 5,
"destination_zone_id": 8,
"base_rate": 0.1234,
"flat_rate": null,
"minimum_rate": 25.00,
"maximum_rate": 999999.00,
"charging_type": "flat_rate",
"display_name": null,
"transit_time_hours": null,
"origin_zone_code": "SYD",
"origin_zone_name": "Sydney Metro",
"destination_zone_code": "MEL",
"destination_zone_name": "Melbourne Metro",
"origin_zone_type": "METRO",
"destination_zone_type": "REGIONAL",
"service_levels": [
{
"service_level_id": 1,
"service_level_name": "Express",
"multiplier": 1.50,
"effective_base_rate": 0.1851,
"effective_min_rate": 37.50,
"is_overridden": false,
"custom_base_charge": null,
"custom_min_charge": null
},
{
"service_level_id": 2,
"service_level_name": "Standard",
"multiplier": 1.00,
"effective_base_rate": 0.1234,
"effective_min_rate": 25.00,
"is_overridden": false
}
],
"conditions": [
{
"id": 50,
"applies_to": { "packaging_types": ["Pallet", "Skid"] },
"min_weight_kg": 0,
"max_weight_kg": 5000,
"min_length_cm": null,
"max_length_cm": null
}
],
"tiers": [
{
"id": 200,
"tier_name": "0-500kg",
"tier_range_start": 0,
"tier_range_end": 500,
"base_charge": 0.1500,
"minimum_charge": 35.00,
"tier_unit": "chargeable_weight"
},
{
"id": 201,
"tier_name": "501-750kg",
"tier_range_start": 501,
"tier_range_end": 750,
"base_charge": 0.1200,
"minimum_charge": 30.00,
"tier_unit": "chargeable_weight"
}
],
"surcharges": [
{
"id": null,
"name": "Fuel Levy",
"action": "add",
"rate_type": "percentage",
"charge_by": "Surcharge",
"amount": 22.5,
"addon_id": 3,
"is_linked": true,
"is_inherited": true,
"is_excluded": false,
"is_overridden": false,
"inherited_amount": 22.5,
"addon_label": "Fuel Levy",
"trigger_mode": "mandatory",
"is_global": true,
"calculation_order": 10
}
],
"variations": [
{
"id": 10,
"variation_name": "Pantech 12-pallet",
"variation_type": "custom",
"variation_data": {
"conditions": [{ "condition_type": "transport_configuration", "transport_config_ids": [5, 6] }],
"base_charge": 550.00,
"flat_rate": 0,
"tiers": []
},
"display_order": 1,
"is_active": true
}
]
}
}
POST /api/rate-entries/create

Creates a single rate entry for one route.

Request Body:

{
"rate_card_id": 1,
"origin_zone_id": 5,
"destination_zone_id": 8,
"rate_calculation_method_id": 3,
"base_rate": 0.1234,
"minimum_rate": 25.00,
"maximum_rate": 999999
}
FieldTypeRequiredDescription
rate_card_idintYesFK to rate_cards
origin_zone_idintYesFK to zones
destination_zone_idintYesFK to zones
rate_calculation_method_idintYesFK to rate_calculation_methods
base_ratedecimalYesBase rate per unit
minimum_ratedecimalNoMinimum charge. Default: 0
maximum_ratedecimalNoMaximum charge. Default: 999999

Response: 201 Created

{
"success": true,
"message": "Rate entry created successfully",
"rate_entry_id": 3501
}
POST /api/rate-entries/save-comprehensive

Creates or updates rate entries for multiple destination zones with all associated data (conditions, tiers, surcharges, variations, service level overrides) in a single transaction.

Request Body:

{
"rate_card_id": 1,
"rate_calculation_method_id": 3,
"origin_zone_id": 5,
"destination_zone_ids": [8, 9, 10],
"service_level_pricing": {
"2": { "service_level_id": 2, "base_charge": 0.1234, "minimum_charge": 25.00 },
"1": { "service_level_id": 1, "use_multiplier": false, "base_charge": 0.1800, "minimum_charge": 40.00 },
"3": { "service_level_id": 3, "use_multiplier": true }
},
"flat_rate": null,
"charging_type": "flat_rate",
"display_name": "SYD-MEL Standard",
"transit_time_hours": 24,
"conditions": [
{
"condition_type": "transport_configuration",
"transport_config_ids": [1, 2, 3]
},
{
"condition_type": "packaging_type",
"packaging_types": ["Pallet", "Skid"]
},
{
"min_weight_kg": 0,
"max_weight_kg": 5000
}
],
"tiers": [
{
"tier_name": "0-500kg",
"tier_range_start": 0,
"tier_range_end": 500,
"base_charge": 0.1500,
"minimum_charge": 35.00,
"tier_unit": "chargeable_weight"
}
],
"surcharges": [
{
"addon_id": 3,
"name": "Fuel Levy",
"action": "add",
"rate_type": "percentage",
"amount": 25.0,
"is_inherited": true,
"is_overridden": true
},
{
"addon_id": 7,
"is_inherited": true,
"is_excluded": true
}
],
"variations": [
{
"variation_name": "Pantech 12-pallet",
"base_charge": 550.00,
"flat_rate": 0,
"minimum_charge": 0,
"conditions": [
{ "condition_type": "transport_configuration", "transport_config_ids": [5] }
],
"tiers": [],
"addons": []
}
]
}

Key Fields:

FieldDescription
destination_zone_idsArray of zone IDs — creates one entry per destination
service_level_pricingKeyed by service level ID. ID 2 = Standard (stored in rate_entries.base_rate). Others go to rate_overrides unless use_multiplier: true
conditionsArray of condition objects (transport config, packaging type, dimensions, time windows)
tiersArray of tier objects for range-based pricing
surchargesOnly overrides and exclusions are stored. Unmodified inherited addons are skipped
variationsTransport-config-specific pricing alternatives stored in rate_card_variations

Surcharge Storage Rules:

  • is_inherited: true, is_excluded: false, is_overridden: false — NOT stored (inherits dynamically)
  • is_inherited: true, is_overridden: true — Stored with custom amount
  • is_inherited: true, is_excluded: true — Stored as exclusion
  • Custom surcharges (no addon_id) — Always stored

Response: 201 Created

{
"success": true,
"message": "Successfully created 3 rate entries with all associated data",
"created_entries_count": 3,
"created_entries": [
{
"rate_entry_id": 3501,
"destination_zone_id": 8,
"base_charge": 0.1234,
"minimum_charge": 25.00,
"conditions_count": 2,
"tiers_count": 3,
"surcharges_count": 2
}
],
"overrides_created": 3,
"overrides_deleted": 0,
"variations_saved": 3
}
PUT /api/rate-entries/:id

Request Body:

{
"rate_card_id": 1,
"origin_zone_id": 5,
"destination_zone_id": 8,
"rate_calculation_method_id": 3,
"base_rate": 0.1300,
"flat_rate": null,
"charging_type": "flat_rate",
"display_name": "SYD-MEL",
"transit_time_hours": 24,
"minimum_rate": 28.00,
"maximum_rate": 999999,
"service_level_pricing": {
"1": { "use_multiplier": false, "base_charge": 0.1900, "minimum_charge": 42.00 },
"3": { "use_multiplier": true }
},
"conditions": [],
"tiers": [],
"variations": []
}

Conditions, tiers, and variations are replaced entirely on each save (delete + re-insert).

Response:

{
"success": true,
"message": "Rate entry updated successfully",
"overrides_created": 1,
"overrides_deleted": 1,
"conditions_saved": 0,
"tiers_saved": 0,
"variations_saved": 0
}
DELETE /api/rate-entries/:id

Hard delete. Cascades to tiers, conditions, surcharges, and overrides.

Response:

{
"success": true,
"message": "Rate entry deleted successfully"
}
POST /api/rate-entries/compute-rate

This is the key endpoint for rate computation. It performs full server-side evaluation of tiers, conditions, service level multipliers, variations, chargeable weight, and addons.

Request Body:

{
"customer_id": 42,
"pickup_zone_id": 5,
"delivery_zone_id": 8,
"service_level_id": 2,
"transport_config_id": 3,
"job_type": "standard",
"charging_type": "actual_weight",
"chargeable_weight_kg": 850,
"items": [
{
"quantity": 2,
"packaging_type": "Pallet",
"length_cm": 120,
"width_cm": 120,
"height_cm": 150,
"weight_kg": 350
},
{
"quantity": 1,
"packaging_type": "Carton",
"length_cm": 60,
"width_cm": 40,
"height_cm": 40,
"weight_kg": 25
}
]
}
FieldTypeRequiredDescription
customer_idintYesCustomer for rate card lookup
pickup_zone_idintConditionalOrigin zone. Auto-resolved from pickup_postcode or pickup_suburb if not provided
delivery_zone_idintConditionalDestination zone. Auto-resolved similarly
pickup_postcodestringNoFallback for zone resolution
pickup_suburbstringNoFallback for zone resolution
delivery_postcodestringNoFallback for zone resolution
delivery_suburbstringNoFallback for zone resolution
service_level_idintNoService level for multiplier. Defaults to Standard
transport_config_idintNoTransport configuration for variation matching
job_typestringNo"standard" or "hourly_hire". Hourly hire uses a separate computation path
charging_typestringNoHint to prefer rate entries matching this method
chargeable_weight_kgfloatNoOverridden by server-side computation from items
itemsarrayNoLine items with dimensions and weight
hoursfloatNoHours for hourly hire jobs

Computation Pipeline:

  1. Rate entry lookup — Customer-specific rate cards first (via rate_card_customers), then global cards
  2. Variation matching — If transport_config_id provided, match against rate_card_variations conditions
  3. Condition evaluation — Check packaging types, transport configs, dimensions, time windows
  4. Chargeable weight computationmax(volumetric_weight, dead_weight) per item. Volumetric = (L x W x H / 1,000,000) x cubic_factor
  5. Tier matching — Find matching tier by chargeable weight or pallet count
  6. Service level pricing — Apply multiplier or use custom override from rate_overrides
  7. Item charge calculation — Per-item charges based on tier rate and calculation method
  8. Total calculation — base_charge + flat_rate, with minimum charge enforcement
  9. Addon waterfall — Automatically calculates applicable addons (fuel levy, GST, etc.)

Response:

{
"success": true,
"found": true,
"computation": {
"rate_card_name": "JATT Per KG Rates",
"rate_card_id": 1,
"rate_entry_id": 101,
"customer_specific": true,
"service_level": {
"name": "Standard",
"multiplier": 1.0,
"is_override": false
},
"chargeable_weight": {
"cubic_factor": 250.0,
"total_dead_weight": 725.00,
"total_volumetric_weight": 1080.00,
"total_chargeable_weight": 1080.00,
"method_used": "volumetric"
},
"conditions_evaluation": [
{
"type": "packaging_type",
"passed": true,
"detail": "Packaging type matches: booking has pallet, carton, allowed: pallet, carton, skid"
}
],
"all_conditions_pass": true,
"tier_matched": {
"name": "751+kg",
"range": "751-99999 chargeable_weight",
"rate": 0.0950,
"unit": "chargeable_weight",
"minimum_charge": 28.00
},
"raw_base_rate": 0.1234,
"base_rate": 0.0950,
"flat_rate": 0,
"initial_cost": 0.12,
"base_rate_source": "751+kg (751-99999 chargeable_weight @ $0.0950)",
"calculation_method": "actual_weight",
"items_breakdown": [
{
"description": "2x Pallet (120x120x150cm, 350kg)",
"normal_charge": 205.20,
"volumetric_weight": 540.00,
"dead_weight": 350.00,
"chargeable_weight": 540.00,
"weight_method": "volumetric",
"volume_m3": 2.16
}
],
"calculation_steps": [
"Rate Card: JATT Per KG Rates (ID: 1)",
"Rate Entry ID: 101",
"Customer-specific: Yes",
"Tier matched: 751+kg (751-99999 chargeable_weight @ $0.0950)",
"Service level: Standard (1.00x -- no multiplier)",
"--- TOTAL CALCULATION ---",
"TOTAL: $102.60"
],
"variation_matched": null,
"transport_config_id": 3,
"totals": {
"initial_cost": 0.12,
"base_charge": 102.60,
"flat_rate_charge": 0,
"subtotal": 102.60,
"minimum_rate": 28.00,
"minimum_applied": false,
"final_total": 102.60
},
"addons": {
"addons": [
{
"addon_id": 3,
"name": "Fuel Levy",
"label": "Fuel Levy",
"addon_type": "surcharge",
"value_type": "percentage",
"amount": 23.09,
"trigger_mode": "mandatory",
"applies_on": "subtotal"
}
],
"base_rate": 0.12,
"flat_rate": 0,
"subtotal": 0.12,
"addon_total": 23.09,
"grand_total": 23.21
}
}
}

No Match Response:

{
"success": true,
"found": false,
"message": "No matching rate entry found for the specified zones"
}

When job_type is "hourly_hire", the compute-rate endpoint uses a separate computation path:

Request Body:

{
"customer_id": 42,
"job_type": "hourly_hire",
"transport_config_id": 7,
"hours": 4,
"service_level_id": null
}

Computation:

  1. Resolves vehicle_type_id from transport_config_id via transport_configurations table
  2. Finds rate entries in time-type rate cards matching the customer and vehicle type
  3. Applies minimum_hours enforcement: effective_hours = max(hours, minimum_hours)
  4. Calculates: base_price = hourly_rate x effective_hours
  5. Applies minimum charge if base_price < minimum_rate
  6. Runs addon waterfall

Response has the same structure as standard compute-rate, with additional fields:

{
"computation": {
"job_type": "hourly_hire",
"hourly_rate": 85.00,
"hours": 4,
"minimum_hours": 4,
"effective_hours": 4,
"base_rate_source": "Rigid Truck @ $85.00/hr",
"calculation_method": "hourly"
}
}
POST /api/rate-entries/check-zone

Resolves a zone from a suburb/postcode.

Request Body:

{
"suburb": "Parramatta",
"postcode": "2150",
"state": "NSW"
}

Response:

{
"success": true,
"found": true,
"zone_id": 5,
"zone_name": "Sydney Metro",
"message": "Pickup zone found: Sydney Metro"
}
POST /api/rate-entries/check-entry

Checks if a rate entry exists for a customer and zone pair. Searches customer-specific rate cards first, then global.

Request Body:

{
"customer_id": 42,
"pickup_zone_id": 5,
"delivery_zone_id": 8
}

Response (customer-specific match):

{
"success": true,
"customer_specific_found": true,
"found": true,
"rate_card_count": 2,
"customer_name": "Acme Logistics",
"rate_entry_id": 1,
"rate_card_name": "JATT Per KG Rates",
"message": "Found matching rate entry in JATT Per KG Rates for Acme Logistics"
}
GET /api/rate-entries/get-company-rate-cards/:company_id

Returns all active rate cards for a company with their rate entries.

GET /api/rate-entries/:id/tiers

Response:

{
"success": true,
"data": [
{
"id": 200,
"rate_entry_id": 101,
"tier_name": "0-500kg",
"tier_range_start": 0,
"tier_range_end": 500,
"base_charge": 0.1500,
"minimum_charge": 35.00,
"tier_unit": "chargeable_weight",
"tier_type": "weight"
}
]
}
GET /api/rate-entries/rate-card/:rate_card_id/tiers

Returns tiers for all entries in a rate card, keyed by rate_entry_id.

GET /api/rate-entries/rate-calculation-methods

Returns the same data as GET /api/rate-cards/calculation-methods but from the rate entries blueprint.


Blueprint prefix: /api/zones
Source: api/zones_api.py

GET /api/zones

Query Parameters:

ParameterTypeDescription
include_suburbsstringSet to "true" to include aggregated suburb_names and postcodes per zone

Response:

{
"success": true,
"zones": [
{
"id": 1,
"name": "Sydney Metro",
"short_name": "SYD",
"is_metro": 1,
"is_no_service_flag": 0,
"is_limited_service": 0,
"zone_type": "Metro",
"service_status": "Full Service",
"is_active": true,
"created_at": "2025-01-01T00:00:00",
"country": "Australia",
"has_boundary": true,
"suburb_names": "Bankstown,Bondi,Burwood,...",
"postcodes": "2000,2010,2020,..."
}
]
}
POST /api/zones

Request Body:

{
"name": "Newcastle Regional",
"short_name": "NCL",
"is_metro": 0,
"is_no_service_flag": 0,
"is_limited_service": 0,
"country_id": 13
}
FieldTypeRequired
namestringYes
short_namestringYes
is_metroint (0/1)No (default 0)
is_no_service_flagint (0/1)No (default 0)
is_limited_serviceint (0/1)No (default 0)
country_idintNo

Response:

{
"success": true,
"message": "Zone created successfully",
"zone_id": 151
}
PUT /api/zones/:zone_id

Same fields as create. Also accepts zone_listing_id and is_active.

DELETE /api/zones/:zone_id

Hard delete.

GET /api/zones/:zone_id/suburbs

Response:

{
"success": true,
"suburbs": [
{ "id": 1, "name": "Bankstown", "postcode": "2200", "state": "NSW" },
{ "id": 2, "name": "Bondi", "postcode": "2026", "state": "NSW" }
],
"zone_id": 1,
"count": 45
}
GET /api/suburbs/all

Returns all suburbs across all zones.

PUT /api/zones/:zone_id/suburbs

Request Body:

{
"suburb_ids": [1, 2, 3, 4]
}

Replaces all suburb assignments for the zone.

PUT /api/zones/:zone_id/composition

Request Body:

{
"boundary_geometry": { "type": "Polygon", "coordinates": [...] },
"selected_suburbs": ["Bankstown", "Bondi"],
"coverage_type": "polygon"
}
GET /api/zones/:zone_id/geometry

Returns the GeoJSON boundary geometry for a zone.

GET /api/zone-listings

Returns all zone listing groups.


Blueprint prefix: /api/service-levels
Source: api/service_levels_api.py

GET /api/service-levels

Response:

{
"service_levels": [
{
"id": 1,
"name": "Express",
"description": "Next-day delivery",
"priority": 1,
"base_cost_multiplier": 1.50,
"transit_time_multiplier": 0.50,
"cubic_factor": 250.0,
"is_active": true
},
{
"id": 2,
"name": "Standard",
"description": "Standard delivery",
"priority": 2,
"base_cost_multiplier": 1.00,
"transit_time_multiplier": 1.00,
"cubic_factor": 250.0,
"is_active": true
},
{
"id": 3,
"name": "Economy",
"description": "Budget delivery",
"priority": 3,
"base_cost_multiplier": 0.85,
"transit_time_multiplier": 1.50,
"cubic_factor": 250.0,
"is_active": true
}
]
}
GET /api/service-levels/:id
POST /api/service-levels

Request Body:

{
"name": "Priority",
"description": "Same-day priority service",
"priority": 0,
"base_cost_multiplier": 2.00,
"transit_time_multiplier": 0.25,
"cubic_factor": 300.0,
"is_active": true
}
PUT /api/service-levels/:id

Same fields as create, all optional.

DELETE /api/service-levels/:id

Blueprint prefix: /api/warehouse-rates
Source: api/warehouse_rates_api.py

Warehouse rates provide activity-based pricing for warehouse type rate cards. Rates are grouped by category (e.g., INBOUND, STORAGE, OUTBOUND, VALUE_ADDED).

GET /api/warehouse-rates/rate-card/:rate_card_id

Response:

{
"success": true,
"data": {
"rate_card_id": 5,
"categories": {
"INBOUND": [
{
"id": 1,
"activity_name": "Container Unload (20ft)",
"activity_code": "IN-CU20",
"rate": 385.00,
"rate_unit": "per_container",
"minimum_charge": null,
"notes": "Standard unload, palletise and put away",
"sort_order": 1,
"is_active": true
}
],
"STORAGE": [
{
"id": 15,
"activity_name": "Pallet Storage",
"activity_code": "ST-PLT",
"rate": 8.50,
"rate_unit": "per_pallet_per_week",
"minimum_charge": 50.00,
"notes": "Weekly storage rate per pallet position",
"sort_order": 1,
"is_active": true
}
]
},
"total_items": 61
}
}
POST /api/warehouse-rates/

Request Body:

{
"rate_card_id": 5,
"category": "OUTBOUND",
"activity_name": "Pick & Pack (per order)",
"activity_code": "OUT-PP",
"rate": 12.50,
"rate_unit": "per_order",
"minimum_charge": 25.00,
"notes": "Standard pick and pack per order",
"sort_order": 1
}
FieldTypeRequired
rate_card_idintYes
categorystringYes
activity_namestringYes
ratedecimalYes
rate_unitstringYes
activity_codestringNo
minimum_chargedecimalNo
notesstringNo
sort_orderintNo (default 0)

Response: 201 Created

{
"success": true,
"data": { "id": 62 }
}
PUT /api/warehouse-rates/:rate_id

All fields optional, uses COALESCE to preserve existing values.

DELETE /api/warehouse-rates/:rate_id

Hard delete.


Blueprint prefix: /api/surcharges
Source: modules/freight_management/surcharges_routes.py

This is the legacy surcharges system. For the newer, more flexible surcharge/addon system, see Section 8 (Addons API). Both systems coexist; the Addons system is preferred for new integrations.

GET /api/surcharges

Requires: @login_required

Response:

{
"success": true,
"surcharges": [
{
"id": 1,
"name": "Fuel Levy",
"description": "Monthly fuel surcharge percentage",
"surcharge_type": "FUEL",
"apply_per": "CONSIGNMENT",
"value_type": "PERCENTAGE",
"value": 22.5
}
]
}
GET /api/surcharges/:id
POST /api/surcharges

Requires: @login_required

Request Body:

{
"name": "Dangerous Goods Surcharge",
"description": "Applied to DG classified freight",
"surcharge_type": "DG",
"apply_per": "CONSIGNMENT",
"value_type": "FIXED",
"value": 75.00
}
FieldTypeRequiredValues
namestringYes
surcharge_typestringYesSee Appendix for enum values
apply_perstringYesSee Appendix for enum values
value_typestringYesFIXED, PERCENTAGE
valuedecimalYesAmount or percentage
descriptionstringNo
PUT /api/surcharges/:id

All fields optional.

DELETE /api/surcharges/:id
POST /api/surcharges/initialize

Seeds the database with default surcharge records.


Blueprint prefix: /api/addons
Source: api/addons_api.py + controllers/addons_controller.py

The Addons system is the unified engine for surcharges, discounts, taxes, and document additions. It supports conditional logic, customer/rate-card scoping, waterfall calculation, per-unit pricing, and external resolver integration.

GET /api/addons/

Requires: @login_required

Query Parameters:

ParameterTypeDescription
addon_typestringsurcharge, discount, tax, document_addition
form_targetstringbooking, rate_card, pickup, delivery, etc.
is_activestringtrue or false
searchstringSearches name, label, description, alias
pageintDefault: 1
per_pageintDefault: 50

Response:

{
"success": true,
"data": [
{
"id": 1,
"name": "Fuel Levy",
"label": "Fuel Levy",
"alias": "fuel_levy",
"help_text": "Applied monthly based on fuel index",
"description": "Percentage-based fuel surcharge",
"addon_type": "surcharge",
"addon_type_id": 1,
"form_target": "Booking",
"form_target_id": 1,
"form_target_ids": [1, 2],
"value_type": "percentage",
"value_type_id": 2,
"default_value": "22.5",
"rate_calculation_method_id": null,
"is_active": true,
"apply_to_all_rate_cards": true,
"apply_to_all_customers": true,
"display_order": 1,
"trigger_mode": "mandatory",
"client_visible": true,
"ui_binding": null,
"calculation_order": 10,
"category": "system",
"applies_on": "subtotal",
"application_scope": "per_booking",
"unit_type": null,
"minimum_charge": null,
"maximum_charge": null,
"is_taxable": true,
"tax_category": "standard",
"tax_code": null,
"tax_inclusive": false,
"region": "AU",
"resolver_url": null,
"fallback_value": 0
}
],
"total": 15,
"page": 1,
"per_page": 50,
"total_pages": 1
}
GET /api/addons/:id

Returns full addon details with conditions, customer assignments, and rate card assignments.

POST /api/addons/

Requires: @login_required

Request Body:

{
"name": "Tailgate Pickup",
"label": "Tailgate Pickup",
"alias": "tailgate_pickup",
"help_text": "Hydraulic tailgate required at pickup",
"description": "Extra charge for tailgate service",
"addon_type_id": 1,
"form_target_ids": [1],
"value_type_id": 1,
"default_value": "45.00",
"trigger_mode": "automatic",
"ui_binding": "pickup_tailgate",
"client_visible": true,
"calculation_order": 50,
"category": "handling",
"applies_on": "subtotal",
"application_scope": "per_booking",
"is_active": true,
"apply_to_all_rate_cards": false,
"apply_to_all_customers": true,
"tax_category": "standard",
"region": "AU"
}
FieldTypeRequiredDescription
namestringYesUnique name
addon_type_idintYesFK to addon_types
form_target_idsint[]YesFK to form_targets (supports multiple)
value_type_idintYesFK to value_types
default_valuestringNoDefault amount/percentage
trigger_modestringNomandatory, automatic, manual
ui_bindingstringNoMatches UI checkbox name for automatic triggers
calculation_orderintNoLower = calculated first
applies_onstringNobase_rate, subtotal, running_total
application_scopestringNoper_booking, per_unit
unit_typestringNoFor per_unit: pallet, kg, km, cubic_meter, item
minimum_chargedecimalNoFloor for calculated amount
maximum_chargedecimalNoCeiling for calculated amount
tax_categorystringNostandard, gst_free, zero_rated, input_taxed
regionstringNoAU, US, DXB, PH, GLOBAL
resolver_urlstringNoExternal pricing API URL
resolver_secretstringNoSecret header for external API
fallback_valuedecimalNoFallback when resolver fails
PUT /api/addons/:id

Same fields as create, all optional.

DELETE /api/addons/:id

Hard delete with cascade.

Conditions provide conditional logic for when addons should apply.

GET /api/addons/:id/conditions
POST /api/addons/:id/conditions

Request Body:

{
"condition_type": "weight_range",
"condition_operator": "between",
"condition_value": [0, 5000],
"logic_operator": "AND"
}
FieldTypeValues
condition_typestringjob_type, service_level, weight_range, distance_range, zone_type, etc.
condition_operatorstringequals, not_equals, in, greater_than, less_than, between
condition_valueanySingle value or array
logic_operatorstringAND or OR (default: AND)
PUT /api/addons/:id/conditions/:condition_id
DELETE /api/addons/:id/conditions/:condition_id
GET /api/addons/:id/customers
POST /api/addons/:id/customers

Request Body:

{
"customer_ids": [1, 2, 3]
}
DELETE /api/addons/:id/customers/:customer_id
GET /api/addons/:id/rate-cards
POST /api/addons/:id/rate-cards

Request Body:

{
"rate_card_ids": [1, 2, 3]
}
DELETE /api/addons/:id/rate-cards/:rate_card_id
GET /api/addons/for-context?form_target=booking&customer_id=42&rate_card_id=1
POST /api/addons/for-context

Requires: @login_required

Returns only the addons applicable to the given context after evaluating:

  1. Form target matching
  2. Region filtering (tenant-locked)
  3. Customer assignment check (unless apply_to_all_customers)
  4. Rate card assignment check (unless apply_to_all_rate_cards)
  5. Conditional logic evaluation

Each addon in the response may include customer_override_value and rate_card_override_value if overrides exist.

POST /api/addons/calculate

Requires: @login_required

Request Body:

{
"addon_id": 3,
"context": {
"base_amount": 500.00,
"weight": 1200,
"distance": 450,
"time": 6,
"load_count": 4
}
}

8.11 Calculate Addons Batch (Waterfall Engine)

Section titled “8.11 Calculate Addons Batch (Waterfall Engine)”
POST /api/addons/calculate-batch

Requires: @login_required

This is the core calculation endpoint for the addon waterfall engine. It evaluates all applicable addons in order, building up a running total.

Request Body:

{
"form_target": "Booking",
"customer_id": 42,
"rate_card_id": 1,
"base_rate": 100.00,
"flat_rate": 50.00,
"ui_context": {
"pickup_tailgate": true,
"delivery_tailgate": false
},
"selected_addon_ids": [5, 8],
"quantity_context": {
"chargeable_weight": 1200,
"actual_weight": 850,
"distance": 450,
"load_count": 4,
"item_count": 6,
"cubic_meters": 3.5
}
}
FieldTypeRequiredDescription
form_targetstringYese.g., Booking, Rate Card
customer_idintNoFor customer-specific overrides
rate_card_idintNoFor rate-card-specific overrides
base_ratefloatNoBase rate for percentage calculations
flat_ratefloatNoFlat rate component
ui_contextobjectNoMaps ui_binding keys to booleans for automatic triggers
selected_addon_idsint[]NoManually selected addon IDs for manual triggers
quantity_contextobjectNoValues for per-unit calculations

Waterfall Calculation Logic:

  1. Fetch applicable addons via get_addons_for_context
  2. Filter by trigger mode:
    • mandatory — always included
    • automatic — included if ui_context[ui_binding] is true
    • manual — included if addon ID is in selected_addon_ids
  3. Two-pass engine: non-tax addons first (sorted by calculation_order), then tax addons
  4. For each addon, calculate amount based on value_type and application_scope:
    • fixed_amount + per_booking = flat fee
    • percentage + per_booking = percentage of base amount
    • fixed_amount + per_unit = rate x quantity (from unit_type)
    • percentage + per_unit = percentage x quantity
    • external_resolver = call external API
  5. Apply min/max charge guardrails
  6. Track taxable vs non-taxable running totals
  7. Tax addons apply to the taxable running total

Response:

{
"success": true,
"data": {
"addons": [
{
"addon_id": 3,
"alias": "fuel_levy",
"name": "Fuel Levy",
"label": "Fuel Levy",
"addon_type": "surcharge",
"value_type": "percentage",
"trigger_mode": "mandatory",
"calculation_order": 10,
"default_value": "22.5",
"raw_value": "22.5",
"amount": 33.75,
"applied_on_amount": 150.00,
"applies_on": "subtotal",
"application_scope": "per_booking",
"is_taxable": true,
"is_tax_addon": false,
"tax_category": "standard",
"region": "AU",
"client_visible": true
},
{
"addon_id": 10,
"alias": "gst",
"name": "GST",
"label": "GST (10%)",
"addon_type": "tax",
"value_type": "percentage",
"trigger_mode": "mandatory",
"calculation_order": 900,
"amount": 18.38,
"applied_on_amount": 183.75,
"applies_on": "running_total",
"is_taxable": false,
"is_tax_addon": true,
"tax_code": "GST",
"tax_inclusive": false
}
],
"base_rate": 100.00,
"flat_rate": 50.00,
"subtotal": 150.00,
"taxable_subtotal": 183.75,
"non_taxable_total": 0,
"addon_total": 52.13,
"grand_total": 202.13
}
}
POST /api/addons/:id/test-resolver

Requires: @login_required

Tests an external resolver endpoint with sample or provided data.

Request Body (optional):

{
"origin_lat": -33.86,
"origin_lng": 151.20,
"dest_lat": -37.81,
"dest_lng": 144.96,
"weight_kg": 500,
"vehicle_type": "rigid_truck",
"route_distance_km": 880
}
GET /api/addons/reference-data

Returns all lookup data for the addon management UI:

{
"success": true,
"data": {
"addon_types": [
{ "id": 1, "name": "surcharge" },
{ "id": 2, "name": "discount" },
{ "id": 3, "name": "tax" },
{ "id": 4, "name": "document_addition" }
],
"form_targets": [
{ "id": 1, "name": "Booking" },
{ "id": 2, "name": "Rate Card" },
{ "id": 3, "name": "Pickup" },
{ "id": 4, "name": "Delivery" }
],
"value_types": [
{ "id": 1, "name": "fixed_amount" },
{ "id": 2, "name": "percentage" },
{ "id": 3, "name": "per_unit" },
{ "id": 4, "name": "text_input" },
{ "id": 5, "name": "external_resolver" }
],
"rate_calculation_methods": [...],
"trigger_modes": [...],
"categories": [...],
"applies_on_options": [...],
"application_scope_options": [...],
"unit_type_options": [...],
"tax_code_options": [...],
"tax_category_options": [...],
"region_options": [...]
}
}

Blueprint prefix: /api/customers
Source: api/customers_api.py

GET /api/customers/

Returns all active customers from the customers table.

Response:

{
"success": true,
"customers": [
{
"id": 1,
"name": "Acme Logistics",
"email": "info@acme.com",
"phone": "0412345678",
"address": "123 Main St",
"city": "Sydney",
"state": "NSW",
"postcode": "2000",
"is_active": true
}
],
"total": 42
}
GET /api/customers/:customer_id
GET /api/customers/company/:company_id

Returns detailed customer information from the companies table with default site address.

PUT /api/customers/company/:company_id

Updates company details and default site address.

GET /api/customers/company/:company_id/sites
POST /api/customers/company/:company_id/sites

Request Body:

{
"site_name": "Melbourne Warehouse",
"line_1": "456 Industry Rd",
"suburb": "Dandenong",
"city": "Melbourne",
"state": "VIC",
"postcode": "3175",
"country": "Australia",
"is_default": false,
"is_billing": false,
"is_depot": true,
"is_head_office": false,
"site_contact_name": "John Smith",
"site_phone": "0398765432"
}
PUT /api/customers/company/:company_id/sites/:site_id
DELETE /api/customers/company/:company_id/sites/:site_id

Blueprint prefix: /api/quotes
Source: api/quotes_api.py

GET /api/quotes/

Query Parameters:

ParameterTypeDefaultDescription
statusstringnoneFilter by status label
customer_idintnoneFilter by customer
searchstringnoneSearch in quote_number or customer name
limitint100Records per page
offsetint0Skip records

Response:

{
"success": true,
"data": [
{
"id": 1,
"quote_number": "Q-001",
"customer_id": 42,
"customer_name": "Acme Logistics",
"pickup_date": "2025-07-15",
"delivery_date": "2025-07-17",
"freight_type": "STANDARD",
"weight": 1200,
"volume": 3.5,
"pallets": 4,
"pieces": 6,
"value": 350.00,
"status": "1",
"status_label": "Pending",
"origin_zone": "Sydney Metro",
"destination_zone": "Melbourne Metro",
"base_rate": 250.00
}
],
"total": 156,
"limit": 100,
"offset": 0
}
GET /api/quotes/statistics

Response:

{
"success": true,
"data": {
"Pending": 45,
"Rejected": 12,
"Processed": 99,
"total": 156
}
}
GET /api/quotes/:id

Returns all fields from the unified_quotes table.

POST /api/quotes/

Request Body:

{
"origin_location": "Sydney",
"destination_location": "Melbourne",
"customer_id": 42,
"customer_name": "Acme Logistics",
"customer_email": "info@acme.com",
"service_type": "Express",
"freight_type": "STANDARD",
"job_type": "LTL",
"weight_kg": 1200,
"volume_m3": 3.5,
"quantity": 4,
"packaging_type": "Pallet",
"length_cm": 120,
"width_cm": 120,
"height_cm": 150,
"pickup_date": "2025-07-15",
"delivery_date": "2025-07-17",
"dangerous_goods": false,
"tailgate_pickup": true,
"tailgate_delivery": false,
"express_delivery": true,
"source_portal": "OPERATIONS",
"base_rate": 250.00,
"fuel_surcharge": 56.25,
"gst_amount": 30.63,
"total_amount": 336.88
}
FieldTypeRequired
origin_locationstringYes
destination_locationstringYes

All other fields are optional. Auto-generates quote_reference in format {portal_initial}Q-{YYYYMMDD}-{NNNN}.

Response: 201 Created

{
"success": true,
"message": "Quote created successfully",
"data": {
"id": 157,
"quote_reference": "OQ-20250715-0001"
}
}
PUT /api/quotes/:id

Accepts any updatable field. Only provided fields are updated.

PUT /api/quotes/:id/status

Request Body:

{
"status": "APPROVED",
"status_notes": "Approved by operations manager"
}

Valid statuses: PENDING, QUOTED, APPROVED, REJECTED, EXPIRED, CONVERTED

DELETE /api/quotes/:id

Soft delete: sets status to REJECTED with notes “Deleted by user”.


ColumnTypeConstraintsDescription
idSERIALPK
nameVARCHAR(255)NOT NULL, UNIQUERate card name
rate_typeVARCHAR(50)NOT NULLload, pallet, actual_weight, time, warehouse
rate_calculation_method_idINTEGERNOT NULL, FKReference to rate_calculation_methods
effective_dateDATEStart date
expiry_dateDATEEnd date (must be after effective_date)
descriptionTEXTDescription/notes
is_activeBOOLEANDEFAULT TRUEActive flag
statusVARCHAR(20)DEFAULT ‘active’active, suspended
transit_time_modeVARCHAR(20)DEFAULT ‘inherit’inherit, profile, custom, none
transit_time_profile_idINTEGERFKReference to transit_time_profiles
created_atTIMESTAMP
updated_atTIMESTAMP
ColumnTypeConstraintsDescription
idSERIALPK
rate_card_idINTEGERNOT NULL, FKReference to rate_cards
origin_zone_idINTEGERFKReference to zones
destination_zone_idINTEGERFKReference to zones
rate_calculation_method_idINTEGERFKReference to rate_calculation_methods
base_rateDECIMAL(10,4)Base rate per unit
flat_rateDECIMAL(10,2)Flat rate per consignment
minimum_rateDECIMAL(10,2)Minimum charge
maximum_rateDECIMAL(10,2)Maximum charge
charging_typeVARCHAR(50)e.g., flat_rate, per_unit
display_nameVARCHAR(255)Human-readable name
transit_time_hoursINTEGERCustom transit time override (NULL = use profile)
vehicle_type_idINTEGERFKReference to vehicle_types (for FTL/hourly)
distance_kmDECIMAL(10,2)Route distance
rate_per_kmDECIMAL(10,4)Per-km rate
rate_per_tonneDECIMAL(10,4)Per-tonne rate
minimum_hoursDECIMAL(5,2)Minimum hours for hourly rates
load_rateDECIMAL(10,2)Load component for bulk rates
drop_rateDECIMAL(10,2)Drop component for bulk rates
is_activeBOOLEANDEFAULT TRUE
created_atTIMESTAMP
updated_atTIMESTAMP

Unique Index: (rate_card_id, COALESCE(origin_zone_id, 0), COALESCE(destination_zone_id, 0), COALESCE(vehicle_type_id, 0))

ColumnTypeDescription
idSERIALPK
rate_entry_idINTEGERFK to rate_entries (CASCADE)
tier_nameVARCHAR(100)e.g., “0-500kg”, “1-4 pallets”
tier_range_startDECIMAL(10,2)Start of range
tier_range_endDECIMAL(10,2)End of range
base_chargeDECIMAL(10,4)Rate per unit within tier
minimum_chargeDECIMAL(10,2)Minimum charge for this tier
tier_unitVARCHAR(50)chargeable_weight, pallets, quantity
tier_typeVARCHAR(30)weight, pallet, space, volume
created_atTIMESTAMP
ColumnTypeDescription
idSERIALPK
rate_entry_idINTEGERFK to rate_entries
applies_toJSONBCondition config: {"transport_config_ids": [...]}, {"packaging_types": [...]}, {"condition_type": "time_window_pickup"}
min_length_cmDECIMAL
max_length_cmDECIMAL
min_width_cmDECIMAL
max_width_cmDECIMAL
min_height_cmDECIMAL
max_height_cmDECIMAL
min_weight_kgDECIMAL
max_weight_kgDECIMAL
min_volume_m3DECIMAL
max_volume_m3DECIMAL
time_window_startTIMEFor time-window conditions
time_window_endTIME
time_window_daysJSONBe.g., ["Monday","Tuesday","Wednesday","Thursday","Friday"]
ColumnTypeDescription
idSERIALPK
rate_entry_idINTEGERFK to rate_entries
service_level_idINTEGERFK to service_levels
custom_base_chargeDECIMAL(10,4)Custom base rate for this service level
custom_min_chargeDECIMAL(10,2)Custom minimum for this service level

Unique Constraint: (rate_entry_id, service_level_id)

ColumnTypeDescription
idSERIALPK
rate_entry_idINTEGERFK to rate_entries
nameVARCHAR(255)Surcharge name
actionVARCHAR(20)add, subtract
rate_typeVARCHAR(20)fixed, percentage
charge_byVARCHAR(50)Category label
amountDECIMAL(10,2)Amount or percentage
addon_idINTEGERFK to addons (NULL for custom surcharges)
is_excludedBOOLEANTRUE = inherited addon excluded from this entry
ColumnTypeDescription
idSERIALPK
rate_card_idINTEGERFK to rate_cards
rate_entry_idINTEGERFK to rate_entries
variation_nameVARCHAR(255)e.g., “Pantech 12-pallet”
variation_typeVARCHAR(50)custom
rate_calculation_method_idINTEGERFK
variation_dataJSONBContains: base_charge, flat_rate, minimum_charge, conditions[], tiers[], addons[]
display_orderINTEGER
is_activeBOOLEAN
ColumnTypeDescription
idSERIALPK
nameVARCHAR(255)Full name (e.g., “Sydney Metro”)
short_nameVARCHAR(10)Code (e.g., “SYD”)
is_metroINTEGER0 or 1
is_no_service_flagINTEGER0 or 1
is_limited_serviceINTEGER0 or 1
country_idINTEGERFK to countries
zone_listing_idINTEGERFK to zone_listings
boundary_geometryJSONBGeoJSON boundary
is_activeINTEGER0 or 1
ColumnTypeDescription
idSERIALPK
zone_idINTEGERFK to zones
suburb_nameVARCHAR(255)
postcodeVARCHAR(10)
state_codeVARCHAR(10)
ColumnTypeDescription
idSERIALPK
nameVARCHAR(100)e.g., “Express”, “Standard”, “Economy”
descriptionTEXT
priorityINTEGERDisplay/sort order
base_cost_multiplierDECIMAL(5,2)Multiplier applied to base rates
transit_time_multiplierDECIMAL(5,2)Multiplier applied to transit times
cubic_factorDECIMAL(8,2)For chargeable weight computation (default 250)
is_activeBOOLEAN
ColumnTypeDescription
idSERIALPK
rate_card_idINTEGERFK to rate_cards (CASCADE)
categoryVARCHAR(30)INBOUND, STORAGE, OUTBOUND, VALUE_ADDED
activity_nameVARCHAR(255)
activity_codeVARCHAR(50)
rateDECIMAL(10,2)
rate_unitVARCHAR(50)per_container, per_pallet, per_week, per_hour, etc.
minimum_chargeDECIMAL(10,2)
notesTEXT
sort_orderINTEGER
is_activeBOOLEAN
ColumnTypeDescription
idSERIALPK
nameVARCHAR(100)Internal name: load, pallet, actual_weight, chargeable_weight, time, warehouse, etc.
display_nameVARCHAR(255)UI label
symbolVARCHAR(20)Unit symbol: $, $/kg, $/plt, $/hr
order_noINTEGERSort order
ColumnTypeDescription
idSERIALPK
nameVARCHAR(255)Unique name
labelVARCHAR(255)Display label
aliasVARCHAR(100)Short unique alias
addon_type_idINTEGERFK to addon_types
form_target_idINTEGERFK to form_targets (legacy)
value_type_idINTEGERFK to value_types
default_valueVARCHAR(100)Default amount/percentage
trigger_modeVARCHAR(20)mandatory, automatic, manual
ui_bindingVARCHAR(100)UI checkbox binding for automatic triggers
calculation_orderINTEGERLower = earlier in waterfall
applies_onVARCHAR(20)base_rate, subtotal, running_total
application_scopeVARCHAR(20)per_booking, per_unit
unit_typeVARCHAR(20)pallet, kg, km, cubic_meter, item
minimum_chargeDECIMAL
maximum_chargeDECIMAL
tax_categoryVARCHAR(20)standard, gst_free, zero_rated, input_taxed
regionVARCHAR(10)AU, US, DXB, PH, GLOBAL
resolver_urlVARCHAR(500)External pricing API URL
resolver_secretVARCHAR(255)Secret header for external API
fallback_valueDECIMALFallback when resolver fails
apply_to_all_rate_cardsBOOLEAN
apply_to_all_customersBOOLEAN
is_activeBOOLEAN
ColumnTypeDescription
idSERIALPK
nameVARCHAR(255)Profile name
is_defaultBOOLEANWhether this is the system default profile
is_activeBOOLEAN
ColumnTypeDescription
idSERIALPK
profile_idINTEGERFK to transit_time_profiles
origin_zone_idINTEGERFK to zones
destination_zone_idINTEGERFK to zones
base_transit_hoursINTEGERTransit time in hours
ColumnTypeDescription
idSERIALPK
nameVARCHAR(100)e.g., “A Trailer”, “B Double”, “Rigid Truck”
codeVARCHAR(20)e.g., “A_TRAILER”, “B_DOUBLE”

  • Dimension: vehicle_type_id differentiates rates per vehicle type
  • Storage: One rate_entry per route + vehicle type combination
  • Pricing: base_rate = flat per-load charge. No tiers.
  • Additional fields: distance_km, rate_per_km, rate_per_tonne
  • Computation: total = base_rate x service_level_multiplier
  • Storage: One rate_entry per route with base_rate = 0 (pricing via tiers)
  • Tiers: Typically 3 tiers: 1-4 pallets, 5-12 pallets, 13+ pallets
  • Tier unit: pallets or quantity
  • Computation: Match pallet count to tier, apply tier.base_charge x quantity x service_level_multiplier
  • Storage: One rate_entry per route with base_rate = basic per-kg rate, minimum_rate = minimum charge
  • Tiers: Typically 3 weight tiers: 0-500kg, 501-750kg, 751+kg
  • Tier unit: chargeable_weight
  • Computation:
    1. Compute chargeable weight per item: max(volumetric_weight, dead_weight) where volumetric_weight = (L x W x H / 1,000,000) x cubic_factor
    2. Match total chargeable weight to tier
    3. total = tier_rate x chargeable_weight x service_level_multiplier
    4. Enforce minimum charge
  • Dimension: vehicle_type_id differentiates rates per vehicle
  • Storage: One rate_entry per vehicle type (no origin/destination zones)
  • Fields: base_rate = hourly rate, minimum_hours
  • Computation:
    1. effective_hours = max(requested_hours, minimum_hours)
    2. total = hourly_rate x effective_hours
    3. Enforce minimum charge
  • Storage: Uses separate warehouse_rates table (not rate_entries)
  • Structure: Grouped by category: INBOUND, STORAGE, OUTBOUND, VALUE_ADDED
  • Each rate: activity_name, rate, rate_unit, minimum_charge
  • Not computed via the standard rate engine — used for warehouse service pricing
  • Storage: rate_entries with load_rate + drop_rate components
  • Fields: base_rate = total (load_rate + drop_rate)
  • Computation: Standard pipeline with load/drop breakdown

Step-by-Step: Compute a Rate from an External System

Section titled “Step-by-Step: Compute a Rate from an External System”
  1. Authenticate — Obtain a session cookie by logging in via /login/operations with valid credentials.

  2. Resolve zones (if you have addresses, not zone IDs):

    POST /api/rate-entries/check-zone
    Body: { "suburb": "Parramatta", "postcode": "2150" }

    Repeat for both pickup and delivery locations. Extract zone_id from responses.

  3. Identify customer — Look up the customer ID:

    GET /api/customers/

    Find the customer by name. Note the id.

  4. Compute the rate:

    POST /api/rate-entries/compute-rate
    Body: {
    "customer_id": 42,
    "pickup_zone_id": 5,
    "delivery_zone_id": 8,
    "service_level_id": 2,
    "items": [
    {
    "quantity": 4,
    "packaging_type": "Pallet",
    "length_cm": 120,
    "width_cm": 120,
    "height_cm": 150,
    "weight_kg": 350
    }
    ]
    }
  5. Read the response:

    • computation.totals.final_total — The freight charge (before addons)
    • computation.totals.initial_cost — Base rate (goes to BASE PRICE field)
    • computation.addons.grand_total — Total including all addons (fuel, GST, etc.)
    • computation.tier_matched — Which pricing tier was applied
    • computation.calculation_steps — Human-readable audit trail
  6. Create a quote (optional):

    POST /api/quotes/
    Body: {
    "origin_location": "Sydney",
    "destination_location": "Melbourne",
    "customer_id": 42,
    "weight_kg": 1400,
    "quantity": 4,
    "base_rate": 102.60,
    "fuel_surcharge": 23.09,
    "gst_amount": 12.57,
    "total_amount": 138.26,
    "source_portal": "API"
    }

Zone and transit time data can be imported via CSV:

POST /api/rate-cards/:id/zones/import
Content-Type: multipart/form-data
Field: zones_file (CSV file)
POST /api/rate-cards/:id/transit-times/import
Content-Type: multipart/form-data
Field: transit_times_file (CSV or XLSX)

External Resolver Pattern (Dynamic Pricing)

Section titled “External Resolver Pattern (Dynamic Pricing)”

For addons that need real-time pricing from external APIs:

  1. Create an addon with value_type = "external_resolver"
  2. Set resolver_url to your API endpoint
  3. Optionally set resolver_secret (sent as X-Resolver-Secret header)
  4. Set fallback_value for timeout/error cases

Expected resolver API contract:

Request:

POST {resolver_url}
{
"context": "pricing_calculation",
"addon_id": "fuel_levy",
"shipment_data": {
"origin": { "lat": -33.86, "lng": 151.20 },
"destination": { "lat": -37.81, "lng": 144.96 },
"weight_kg": 500,
"vehicle_type": "rigid_truck",
"route_distance_km": 880
},
"customer_id": 42
}

Expected response:

{
"status": "success",
"cost": 125.50,
"currency": "AUD",
"taxable": true,
"meta_data": { "source": "fuel_index_2025_07" }
}

Results are cached for 60 seconds per unique route+addon combination. Timeout is 2 seconds.


IDNameDisplay NameSymbol
1loadPer Load (FTL)$
2palletPer Pallet$/plt
3actual_weightPer KG (Actual Weight)$/kg
4chargeable_weightPer KG (Chargeable)$/kg
5timePer Hour$/hr
6distancePer KM$/km
10warehouseWarehouse$
11per_tonnePer Tonne$/t

Enum values for surcharge_type:

  • FUEL — Fuel surcharge
  • RESIDENTIAL — Residential delivery
  • REMOTE — Remote area
  • OVERSIZE — Oversize items
  • TAILGATE — Tailgate service
  • WEEKEND — Weekend delivery
  • DG — Dangerous goods
  • ACCESS — Access difficulty
  • HANDLING — Special handling
  • OTHER — Other

Enum values for apply_per:

  • CONSIGNMENT — Per consignment
  • ITEM — Per item
  • KG — Per kilogram
  • CUBIC_METRE — Per cubic metre
  • PALLET — Per pallet
  • HOUR — Per hour
  • MANIFEST — Per manifest

Derived from is_metro flag:

  • Metrois_metro = 1
  • Regionalis_metro = 0

Service status (derived):

  • Full Serviceis_no_service_flag = 0 AND is_limited_service = 0
  • Limited Serviceis_limited_service = 1
  • No Serviceis_no_service_flag = 1

E. Service Level Multipliers (Typical Values)

Section titled “E. Service Level Multipliers (Typical Values)”
Service Levelbase_cost_multipliertransit_time_multipliercubic_factor
Express1.500.50250
Standard1.001.00250
Economy0.851.50250

How multipliers work:

  • Rates stored in rate_entries are Standard rates (multiplier = 1.0)
  • Express: effective_rate = base_rate x 1.50
  • Economy: effective_rate = base_rate x 0.85
  • Custom overrides in rate_overrides bypass the multiplier entirely
ModeBehavior
mandatoryAlways applied — cannot be removed
automaticApplied when the corresponding UI checkbox (ui_binding) is checked
manualApplied only when explicitly selected by the user
CategoryIn Tax Base?Description
standardYesNormal taxable item (e.g., Fuel Levy, Tailgate)
gst_freeNoGST-free supply (e.g., International freight, medical)
zero_ratedNoZero-rated supply (e.g., Exports)
input_taxedNoInput-taxed (rare — mainly financial services)

The waterfall processes addons in two passes:

  1. Non-tax addons — Sorted by calculation_order (ascending). Each addon can reference base_rate, subtotal, or running_total for percentage calculations.
  2. Tax addons — Always processed after all non-tax addons. Tax amount is calculated on the taxable_running_total (subtotal + all taxable non-tax addons).

Typical ordering:

  • 10: Fuel Levy (mandatory, percentage on subtotal)
  • 50: Tailgate Pickup (automatic, fixed per booking)
  • 50: Tailgate Delivery (automatic, fixed per booking)
  • 100: DG Surcharge (manual, fixed per booking)
  • 900: GST (mandatory, percentage on taxable running total)
StatusDescription
PENDINGNewly created, awaiting pricing
QUOTEDPrice calculated and presented
APPROVEDCustomer approved the quote
REJECTEDCustomer or system rejected
EXPIREDPast expiry date
CONVERTEDConverted to a booking
CodeMeaning
200Success
201Created
400Bad request (validation error)
401Unauthorized (not logged in)
404Resource not found
409Conflict (duplicate name, constraint violation)
500Internal server error