# Calculate duties, taxes and landed cost (POST /v1/calculate)

> POST /v1/calculate: Calculate duties, taxes and landed cost. border.bot API reference (Landed cost).

Source: https://staging.border.bot/docs/api/calculate-landed-cost

> Documentation index: https://staging.border.bot/llms.txt. Every page is Markdown at its URL + `.md`.

`POST https://staging-api.border.bot/v1/calculate`

Requires an API key (`Authorization: Bearer bb_live_…`).

Duties, taxes, fees and the landed cost for one HS code / origin / destination (the destinations `GET /countries` marks `landedCost`), or for a catalogue product by `sku` or `productId` (its approved code for the destination, origin, value, weight and content). Costs 1 credit. Unsupported destinations return 422 `unsupported_country` without charging.

API key scope: `calculate`.

## Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `idempotency-key` | string | no | Make retries safe: a repeat with the same key returns the original result without charging again (keys are scoped to your workspace). |

## Request body (JSON)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `destinationCountry` | string | yes | Destination: one `GET /countries` marks `landedCost`. ISO 3166-1 alpha-2, case-insensitive. |
| `currency` | string | no | ISO 4217 currency of `value`, `shippingCost` and `insuranceCost`. |
| `shippingCost` | number | no | Shipping cost (affects CIF-based duty/VAT bases). |
| `insuranceCost` | number | no | Insurance cost. |
| `transportMode` | "air" \| "sea" \| "road" \| "rail" | no | Transport mode (fees such as HMF apply to sea freight). |
| `shippingTerms` | "EXW" \| "FCA" \| "FAS" \| "FOB" \| "CFR" \| "CIF" \| "CPT" \| "CIP" \| "DAP" \| "DPU" \| "DDP" | no | Incoterm of the price (Incoterms 2020). Under C and D terms (CFR, CIF, CPT, CIP, DAP, DPU, DDP) the price already carries the goods to the destination, so `shippingCost` isn’t added to the customs value again. |
| `shipmentChannel` | "courier" \| "postal" \| "freight" | no | How the goods travel and clear: `courier` (an express consignment: DHL, UPS, FedEx…), `postal` (national post) or `freight` (cargo on its own entry). Omitted: from `transportMode` — air is `courier`, sea, road and rail are `freight` (sea cargo is never an express consignment, so `courier` by sea is priced as freight). Affects de minimis and fees (US: the informal-entry MPF, 19 CFR 24.23(b)(2), is charged on freight; express consignments pay the carrier’s per-waybill fee, 24.23(b)(4)). The result’s `shipmentChannel` says which was used. |
| `entryDate` | string | no | Calculate with the rates in force on this date (YYYY-MM-DD). Default: today. |
| `tradeAgreement` | string | no | Ignored. Preferential rates are applied automatically from the origin and destination; the agreement used is returned as `tradeAgreement`. |
| `region` | string | no | State, province or territory, where import taxes differ inside the country. Canada needs one (`ON`, `QC`, `BC`…). |
| `purpose` | "sale" \| "gift" \| "sample" \| "return" | no | Why the goods are sent: some thresholds differ for gifts, samples and returns. Default: `sale`. A `gift` is a personal gift, not something bought: a free gift with a purchase is `sale` (or set `giftInConjunctionWithPurchase`). |
| `sender` | "person" \| "business" | no | Who sends the goods: a private `person` or a `business`. Gift exemptions are usually for gifts from a person (US: up to $100 per recipient per day, 19 CFR 10.152); a gift from a business qualifies only on the declaration the destination accepts, claimed in `claims` (US: `gift_statement`, the parcel clearly marked as a gift with a statement enclosed, 19 CFR 10.153(c)). Left out, a gift exemption limited to some senders is applied and the result is `estimated`. |
| `giftInConjunctionWithPurchase` | boolean | no | The gift is given in conjunction with a purchase (a bonus or free article with an order). It isn’t a gift for customs (US 19 CFR 10.153(a)): gift exemptions don’t apply and the goods are treated as sold. |
| `businessBuyer` | boolean | no | The buyer is a business: some taxes are reverse-charged or apply differently. |
| `sellerRegistrations` | string[] | no | Tax schemes the seller is registered for (`EU_IOSS`, `GB_VAT`, `AU_GST`, `NZ_GST`, `NO_VOEC`…). Taxes the seller collects at checkout are returned with `collectedBy: "seller"`. |
| `preference` | string | no | `best` (default): the lowest preferential rate the origin qualifies for. `none`: the general rate only. Or a programme code (`S` for USMCA into the US). Every option is returned in `options`. |
| `claims` | string[] | no | Exemption, relief and quota codes being claimed (US Chapter 99 exclusions such as `9903.88.69`; an end-use authorisation such as TARIC document `N990`; a tariff quota order number such as `050331`). Codes you could claim are returned in `claims.available`. |
| `enforceValidation` | boolean | no | Refuse a shipment that matches one of the destination’s `reject` validation rules (400 `invalid_input`, `details.reason: "validation_failed"`, refunded) instead of reporting it in `validation`. |
| `declarationType` | "full" \| "simplified" | no | The declaration the goods are entered on, when known: `full` (US formal entry) or `simplified` (US informal entry). Default: what the destination’s value rules require. A full declaration may always be chosen; a simplified one the value rules out is ignored (see `notes`). Fees differ by declaration (US MPF, the cotton assessment). |
| `filing` | object | no | How the declaration is filed, when known: `method` `electronic` or `paper`, `preparedBy` `declarant` or `authority` (a customs officer). Default: the destination’s usual filing (electronic, by the declarant). The US informal-entry MPF differs by both. |
| `hsCode` | string | no | HS/HTS code (4–10 digits). With `sku` or `productId`, leave it out to use the product’s approved code for the destination. |
| `originCountry` | string | no | Country of origin. With `sku` or `productId`, the catalogue’s origin is used when left out. |
| `value` | number | no | Total customs value of the line, in `currency`. With `sku` or `productId`: the item’s unit value × `quantity` when left out (it must be in the request’s currency). |
| `quantity` | integer | no | Number of units. |
| `weight` | number | no | Shipment weight (needed for weight-based duties). Requires `weightUnit`. |
| `weightUnit` | "kg" \| "lb" | no | Unit of `weight`. |
| `volumeLiters` | number | no | Volume of the goods in litres, for duties charged per litre (wine, spirits, fuel). |
| `alcoholPercent` | number | no | Alcohol by volume (%), for duties charged per litre of pure alcohol. |
| `components` | object[] | no | Metal content by value (line totals in `currency`), for duties charged on it: US Section 232 steel, aluminum and copper derivatives. Without it, those duties come back as `regulatory` (conditional). |
| `metalWeightPercent` | number | no | Share of the product’s weight that is metal (0–100), for content-based exemptions. |
| `materials` | object[] | no | What the goods are made of (line totals): harmonised `material` codes (`steel`, `aluminium`, `copper`, `wood`, `component`…; `GET /catalog/terms`) with their weight or share of the weight, value, and where each was produced by stage (`melt_pour`, `smelt`, `cast`, `harvest`, `production`). Measures reserved for declared content apply from them on their own (US Section 232: at least 85% of the aluminium, steel and copper by weight smelted and cast / melted and poured in the United States, Proclamation 11032), and duties charged on content read their value (the non-U.S. content of USMCA goods: the line’s value less the parts produced in the United States). Without them the full rate is charged, never less, and `contentHints` says what would lower it. |
| `conditions` | string[] | no | Rate conditions the goods meet, when the destination reserves a rate for them: a harmonised kind (`pharmaceutical`, `end_use`, `certificate`, `company`, `quality`, `route`) or the publisher’s own code (EU TARIC additional code `2500`). Without it the default rate is charged (the unconditional one, or the highest when every rate has a condition); the rates you could claim are returned in `options` with their `condition`. The importer must hold what justifies a claimed condition. |
| `declaredPrice` | object | no | The price of the goods per unit, in `currency` (`per`: `100 kg`, `t`, `hl`, `kg`…), for duties set by the price: the EU’s entry prices for fruit and vegetables and minimum import prices (TARIC price conditions). Without it the price is the line’s value over its weight or volume; when that isn’t known either, the highest duty the condition can charge is used and `needs` lists `price`. |
| `salePrice` | number | no | The price the importer (or domestic seller) sells the line for, line total in `currency`, for taxes charged on the sale price rather than the customs value (US large cigars, 27 CFR 41.39). Without it the customs value stands in, the result is `estimated` and `needs` lists `sale_price`. |
| `entryProvision` | object | no | The special provision the line is entered under, when not its classification alone (US chapter 98): `kind` is `repair_abroad` (9802.00.40/.50), `processing_abroad` (9802.00.60), `assembled_abroad` (9802.00.80), `agreement_provision` (USMCA subchapter XXIII), `returned` (9801.00.10) or `other`, with the provision’s `code`. Duties the provision is outside of are left out (US Section 301, 338, forced-labour, Brazil, Nicaragua and Russia duties, except for 9802 repairs, processing and assembly); repairs and processing pay on `dutiableValue` (the value of the repairs or processing, line total in `currency`), assembly abroad on the value less the U.S. components (`dutiableValue`, or worked out from `materials`). Section 232 stays charged, on the full value under 9802.00.60. |
| `sku` | string | no | A SKU in your catalogue: its approved code for the destination, origin, value, weight and content components are used (what the line gives wins). A product without an approved code for the destination is refused without charging (`invalid_input`, `details.reason: "missing_classification"`). |
| `productId` | string (uuid) | no | A catalogue product’s id (its first active item), instead of `sku`. |

## Responses

- `200`: The landed-cost breakdown and the credits charged. (`CalculateResponse`)
- `400`: Invalid input (`invalid_input`). (`Error`)
- `401`: Missing, invalid, revoked or expired API key (`unauthorized`). (`Error`)
- `402`: Not enough credits (`insufficient_credits`). Nothing was charged. (`Error`)
- `403`: Workspace suspended, action disabled, or the key lacks the scope (`org_suspended`, `action_disabled`, `forbidden`). (`Error`)
- `409`: A request with this Idempotency-Key is still in progress (`conflict`). (`Error`)
- `422`: Destination not supported (`unsupported_country`). (`Error`)
- `429`: Rate limited (`rate_limited`). Retry after `Retry-After` seconds; `RateLimit` says which limit was hit (`r=0`, `t` seconds until its window ends) and `RateLimit-Policy` its quota. (`Error`)
- `500`: Unexpected error (`internal`). (`Error`)
- `502`: The engine failed (`upstream_error`). The credits are refunded automatically. (`Error`)
- `504`: The engine timed out (`upstream_timeout`). The credits are refunded automatically. (`Error`)

## Example

```bash
curl -X POST 'https://staging-api.border.bot/v1/calculate' \
  -H 'Authorization: Bearer bb_live_...' \
  -H 'Content-Type: application/json' \
  -d '{"hsCode":"6109.10.00.12","originCountry":"CN","destinationCountry":"US","value":120,"currency":"USD","shippingCost":15}'
```
