The rate engine is the core of Ship Quote. It takes a single POST request carrying shipment details and returns a ranked list of quotes from every active agency in the system — static carriers whose tariffs live in MongoDB and live API carriers queried in real time. This page walks through each processing stage in order, then covers the internal mechanics of the calculators and carrier adapter pattern.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/plantasur-dev/ship-quote/llms.txt
Use this file to discover all available pages before exploring further.
Processing Pipeline
Input — User Sends Shipment Data
A client posts shipment data to
POST /api/v1/rates/compareByPostalCode or POST /api/v1/rates/compareByProvinceCode. The request body must include a destination postal code, an ISO-2 country code, and a non-empty items array.Validation — Middleware Guards the Request
Before the controller is invoked, the request passes through two chained middleware functions.
rateDestinationValidation checks that destinationPostalCode and countryCode are present strings. For Spanish shipments (when countryCode === DEFAULT_COUNTRY), it also enforces that the postal code matches the 5-digit format /^\d{5}$/.rateItemsValidation ensures items is a non-empty array and that every element carries a valid typeServices, a positive weight, and positive large, width, and height values. Any failure throws an HTTP 400 immediately.Scope Detection — National vs. International
The engine determines the shipment scope by comparing
The scope is used in the next step to filter agencies and later to label the zone on API-provider results.
countryCode against the DEFAULT_COUNTRY environment variable (typically ES).countryCode | Resolved Scope | Display Label |
|---|---|---|
ES (matches DEFAULT_COUNTRY) | national | NACIONAL |
| Any other code | international | INTERNACIONAL |
Agency Filtering — Match Coverage Rules
With the scope resolved, Agencies with
rates.service.js queries MongoDB for all active agencies whose rules.coverage array includes the current scope. The result set is then split into two buckets by agency.type.active: false are excluded entirely; they remain in the database but receive no traffic until re-activated.Parallelization — Static and API Providers Run Concurrently
Both provider calls are launched simultaneously with If an individual API carrier throws or times out, the error is caught inside the provider and converted to a structured error result — the other agencies are not affected.
Promise.all. Neither blocks the other — a slow external carrier API cannot delay a fast static lookup.Processing — Calculation per Provider Type
Static provider (
static.rate.provider.js): For each static agency, the provider reads data from the in-memory tariff store, resolves the destination zone, and delegates to either calculatePallet or calculateParcel based on zone.calculationMode.API provider (api.rate.provider.js): For each API agency, the provider instantiates the correct carrier class via carrierFactory(agency), calls carrier.getRates(input), and passes the raw response through buildRateComplete to normalize the shape.Both paths produce the same intermediate structure: an array of buildRateResult objects carrying service, transportType, concepts, incidents, and a pre-computed total.Normalization and Response
All raw results flow through The final response is a flat array of per-agency objects, each containing
presentRate() in rate.presenter.js. This function translates internal concept codes (BASE, EXTRA_DIMENSIONS, EXTRA_WEIGHT, ADDITIONAL_BLOCK, etc.) into human-readable labels using a labels map and shapes each entry into the public response format.agency, available, zone, and services.Scope System
TheSCOPE_TYPES constant is the single source of truth for shipment scope values across the entire codebase.
SCOPE_LABELS is used by the API provider to populate the zone field on the response object when there is no database zone name available (external carriers are not zone-mapped internally).
Pallet Rate Calculator
Static pallet calculation starts inpallet.rate.calculator.js. The function reads the zone’s pricingMode.type and delegates to one of two strategies.
weight Pricing Mode — Per Pallet Type
The calculateSinglePallet strategy groups inbound pallet items by matching each one to the smallest PalletType in the agency’s sortedPalletTypes list whose maxWeight, maxHeight, maxLength, and maxWidth all satisfy the item’s dimensions. Rejected items (no matching pallet type) are returned as incident records, not dropped silently.
For each group the engine looks up the rate document using the composite cache key "pallet|{zoneName}|{palletTypeId}", finds the matching priceBreak bracket for the group quantity, applies the fuel surcharge, and emits a buildConcept('BASE', total, { palletType, quantity, unitPrice, items }) record.
weight_volume Pricing Mode — Total Weight Across All Items
The calculateWeightVolume strategy sums effective weights across all pallet items (optionally applying volumetric conversion if zone.volumetric.enabled is true), then looks up the rate using the key "pallet|{zoneName}|none" and matches the total weight against the priceBreaks array.
Tonnage Pricing Rule
Some zones include a tonnage pricing rule that switches the unit from €/kg to €/ton when total weight exceeds a configured threshold.Fuel Surcharge
Both pricing modes apply the agency-level fuel surcharge configured inagency.supplements.fuelSurcharge. Depending on type, it adds either a percentage or a fixed amount on top of the base price before the tonnage rule is evaluated.
Parcel Rate Calculator
Parcel calculation uses a different flow because parcel items are not grouped by type — all items in the request are treated as a single shipment whose total weight determines the price.Item Validation
Each parcel item is checked against the service’s
limits (maxWeight, maxLength, maxSumDimensions). Items that exceed a limit generate an incident record and are excluded from the pricing calculation.Weight Resolution
Valid items are enriched with their volumetric weight (
length × width × height / volumetricFactor). The total weight used for pricing is determined by zone.pricingMode.type:weight→ use the sum of real weights.weight_volume→ usemax(realWeight, volumetricWeight).
Math.ceil to the next whole kilogram.Surcharge Calculation
Three surcharge types are evaluated per service:
| Surcharge | Trigger | Field |
|---|---|---|
extraKg | Total shipment weight exceeds the last priceBreaks max bracket | surcharges.extraKg.pricePerKg |
dimensionRanges | Item sum-of-dimensions (L + W + H) falls within a bracket | surcharges.dimensionRanges[] |
multiParcelExcess | Total weight exceeds thresholdKg; extra cost accrues in blocks of divisor kg | surcharges.multiParcelExcess |
Carrier Adapter Pattern (API Provider)
All external carrier integrations follow the same abstract base class pattern defined incarriers.service.interface.js.
fetchApi helper wraps the native fetch API with an AbortController timeout. A non-2xx response throws an HTTP error with the carrier’s error message; a network timeout throws HTTP 408.
New carriers are registered in carriers.map.js by adding a mapping from agency.code to the concrete class:
carrierFactory(agency) looks up agency.code in this map and returns a new instance, or null if no adapter has been registered yet — in which case the API provider returns a structured "Carrier not implemented" error result instead of throwing.
Dachser Adapter
The Dachser adapter (dascher.service.js) demonstrates how a concrete carrier implementation works. It overrides getRates to fan out requests across multiple transport products (e.g., targoflex, targospeed, targospeed 12) concurrently using Promise.allSettled — so a failure on one product does not cancel the others.
mapResponse converts the carrier’s raw JSON into buildRateResult objects using buildConcept for each line item in quotationDetails (freight base, fuel surcharge, product surcharge, etc.).
Error Handling and Incidents
Neither provider throws unhandled errors to the caller. All failures — zone not found, unsupported calculation mode, carrier timeout, API 4xx/5xx — are converted to a structured result usingbuildStaticErrorResult or buildApiErrorResult.
available flag on the top-level agency result object is derived automatically: it is true only when at least one service in the services array has an empty incidents array.