Landed Cost Route Contract
Status: Active
Version: v1
Last updated: 2026-04-16
This document is the normative contract for POST /api/trade/landed-cost.
Route-level request/response coverage now appears in the versioned public OpenAPI artifact (docs/api/openapi/rgl8r-public-api-v1.3.0.yaml) and in the public API contract pack (docs/api/public-api-contract-v1.md); route semantics are documented separately outside the versioned public API contract pack. This document remains the route-semantics companion because it captures quantity-aware behavior, provider-comparison rules, and configured-corridor gating that the machine spec summarizes but does not explain operationally.
Route and auth
- Route:
POST /api/trade/landed-cost - Auth: Clerk bearer token or integration auth (
Authorization: Bearer ...or compatibilityx-api-key+x-org-id) - In legacy mode, Customer-facing authority source is destination-aware:
OPENCLAWfor US/default,OPENCLAW_CBSAfor Canada,OPENCLAW_HMRCfor UK,OPENCLAW_TARICfor EU member states. - In configured quote mode, the route resolves tenant corridor policy before calling the landed-cost engine. Exact
(originCountry, destinationCountry)corridor match wins; wildcard-origin(*, destinationCountry)is the fallback. - Catalog refresh uses the same destination-aware source selector as the public landed-cost route.
Configured-corridor precheck
If the tenant has one or more quote-corridor rows, the route is in configured mode and fails closed on corridors that do not have an enabled exact-or-wildcard match.
When that happens, POST /api/trade/landed-cost returns a route-local business outcome:
{
"authority": "internal",
"status": "unconfigured_corridor",
"breakdown": null,
"reason": "no_enabled_matching_corridor",
"metadata": {
"policyMode": "configured",
"originCountry": "CN",
"destinationCountry": "US",
"corridorConfigured": false
}
}Important boundary:
unconfigured_corridoris a route/business-layer result, not a landed-cost engine status.- The shared engine contract remains
authoritative | rate_not_available. - The public route records warning-style observability for this precheck failure, but does not record billable landed-cost usage when it happens.
Request contract
Required fields:
productValuehsCodeoriginCountrydestinationCountry
Optional fields:
shippingCostinsurancecurrencyquantityquantityUnitproviderdescription
Quantity-aware rules:
quantityis a positive decimal representing the total customs quantity for the shipment line matched to the HS code.quantityUnitmust be one of:KGM,LTR,DZN,TNE.quantity and quantityUnit must be provided together- Quantity is preserved as provided in the first slice; no rounding or unit conversion occurs.
Currency rules:
currencyis an optional 3-letter ISO 4217 code (e.g.EUR,USD,GBP).- When provided, the engine evaluates de minimis thresholds for the destination country. Without
currency, de minimis evaluation is skipped. - If
currencydoes not match the threshold currency for the destination, the response includesdeMinimis.skipped: 'currency_mismatch'(no exemption applied, threshold still reported). - For EU destinations,
currencyalso drives the low-value trust gate: EUR values at or below the €150 IOSS boundary returntrust.level: 'indicative'; non-EUR currencies returntrust.level: 'undetermined'.
Provider comparison rules:
providerenables tenant-scoped external comparison for legacy value-only requests.provider comparison does not support quantity-aware landed-cost requests yet- Quantity-aware requests with
providerreturn400 INVALID_REQUEST. - Provider comparison only runs when
currencyis explicitly supplied and equals'USD'. Omittingcurrencyor supplying any non-USD value skips provider comparison (the response includes the internal result withproviderSkipReason: 'non_usd_corridor').
Legacy field-name aliases (soft deprecation):
- Unknown request fields are silently dropped by the schema. To catch common misnamings before they cause wrong responses, the route detects a known list of legacy aliases and emits a deprecation signal.
- Detected aliases (all → canonical):
shipping,freight,shipping_cost→shippingCost;insurance_cost→insurance;currencyCode,currency_code→currency;destination,destCountry,destinationCountryCode→destinationCountry;origin,originCountryCode→originCountry. - Behavior when any alias is detected:
- Response includes an
X-Deprecation-Hintheader with a semicolon-separated list ofuse 'canonical' not 'alias'entries. - A
warn-level structured log is emitted server-side with the request id, tenant id, and the detected aliases. - The request is not rejected. The schema still drops the aliased field, so the caller will get the canonical field’s default (e.g.
shippingCost: 0) — the deprecation hint is the signal that the input was misnamed.
- Response includes an
- An alias is not flagged when the canonical field is also present (the canonical wins during parsing; the alias is just noise).
- This is a warning, not a breaking change. A versioned strict request contract is tracked as a follow-up in
docs/BACKLOG.md.
Example requests
Value-only request:
{
"productValue": 100.0,
"hsCode": "6109.10.00",
"originCountry": "CN",
"destinationCountry": "US",
"shippingCost": 50.0,
"insurance": 10.0
}Quantity-aware request:
{
"productValue": 100.0,
"hsCode": "2207.20.00",
"originCountry": "CN",
"destinationCountry": "CA",
"shippingCost": 50.0,
"insurance": 10.0,
"quantity": 2.0,
"quantityUnit": "KGM"
}Provider comparison request:
{
"productValue": 100.0,
"hsCode": "6109.10.00",
"originCountry": "CN",
"destinationCountry": "US",
"provider": "zonos",
"description": "Cotton knit t-shirt"
}Response notes
- Authoritative and
rate_not_availableresponses report the customer-facing source selected for the request destination. unconfigured_corridormeans the tenant is in configured quote mode but the requested corridor has no enabled exact-or-wildcard match. It is configuration debt, not missing tariff data.- Canada requests (
destinationCountry=CA) reportmetadata.sourceCode = "OPENCLAW_CBSA"when the authoritative engine resolves through the CBSA source. - Quantity-aware duty-expression evaluation is supported for the first landed-cost CBSA formula slice only; unsupported expressions remain
status: "rate_not_available"withmetadata.unsupportedExpression = true. - Full quoteability in the advanced TRADE path requires both
POST /api/compliance/checkreturningquoteReadiness = "quoteable"andPOST /api/trade/landed-costreturningstatus = "authoritative". Compliance readiness and landed-cost authority are separate gates in Wave 1.
Contract boundaries
CLAUDE.mdis a route index, not the full normative contract.docs/postman/RGL8R-API.postman_collection.jsonis a first-party request example surface and must stay aligned with this document.packages/mcp-server/src/tools/landed-cost.tsis a first-party tool surface and must stay aligned with this document.apps/docs/content/public/...is generated fromdocs/and must not be edited directly.