Cross-border shipping

Updated May 26, 202610 min read

International shipments require richer data than domestic ones. A domestic shipment can be booked with little more than a pickup, dropoff, and basic parcel information. International shipments must also carry item-level customs data (HS codes, country of origin, declared values), accurate parcel weights and dimensions, and trade terms (incoterms). They also require information about the commercial parties involved (seller, buyer, importer, exporter, broker).

This page describes the model. For the API walkthrough, see Recipe → ship cross-border.

How Carriyo resolves cross-border data

Carriyo uses a configuration hierarchy so you don't have to pass every cross-border field on every shipment. Defaults configured on the carrier account fill in any field the shipment omits. A small set of "smart defaults" go further by deriving parties from the pickup and dropoff addresses.

Resolution order, highest priority first:

  1. Shipment-level data. Anything in the shipment's customs object always wins.
  2. Carrier-account properties. Defaults configured on the carrier account: incoterm, party details, registration numbers, carrier-specific flags.
  3. Smart defaults. Fallback rules like Seller defaults to pickup or Buyer defaults to dropoff. They derive parties from the shipment's pickup and dropoff addresses when nothing else is configured.

For merchants who always ship from the same entity with the same tax registrations, configure the seller, exporter, and incoterm once on the carrier account. Individual shipments then only need to carry the data that varies per order (items, parcels, buyer/dropoff).

The cross-border data checklist

1. Item-level customs data

Each entry in items[] must carry enough detail for customs declaration and commercial invoice generation.

FieldPathNotes
Descriptionitems[].descriptionSpecific human-readable description for customs forms. Vague descriptions ("Goods", "Clothing") cause customs delays. Required at confirm.
SKUitems[].skuUnique catalogue identifier. Required at confirm.
Quantityitems[].quantityInteger ≥ 1. Required at confirm.
Unit priceitems[].price.amount, items[].price.currencyPer-unit price; currency as ISO 4217. Required at confirm.
HS codeitems[].hs_codeHarmonized System commodity code (typically 6–10 digits). Required by most carriers and customs authorities.
Country of originitems[].origin_countryISO 3166-1 alpha-2 of the manufacturing country.
Item weightitems[].weight.value, items[].weight.unitNet weight per unit. Used for customs value calculations.

Additional item fields when relevant: cost (for zero-priced samples that still need a customs value), dangerous_goods, battery.material_type and battery.packing_type (for lithium batteries), taxes[], duties[], manufacturer_id, material_composition.

2. Parcel-level details

Parcels define the physical packages. Even with complete item data, carrier booking can fail if parcel weight and dimensions are missing.

FieldPathNotes
Parcel weightparcels[].weight.value, parcels[].weight.unitTotal packed weight including packaging. Required for rating, booking, and label generation.
Parcel dimensionsparcels[].dimension.{width,height,depth,unit}Used to compute volumetric weight. Carriers charge on the greater of actual vs volumetric.
Item-to-parcel mappingparcels[].parcel_items[].{sku,quantity}Which items (and how many) are in each parcel. Critical for multi-parcel shipments where customs documentation must reflect each package's contents.

The parcel weight should reflect the total packed weight (packaging + filler + box + items), not the sum of item weights. Carriers use parcel weight for rating; customs uses item weights for duty calculation.

3. Shipment-level customs context

The customs object on the shipment carries trade terms, declared value overrides, and customs-specific instructions.

FieldPathNotes
Incotermscustoms.incotermsInternational Commercial Terms. See Incoterms below.
Declared valuecustoms.declared_value.{amount,currency}Optional override of the total customs value. If omitted, derived from items[].price × items[].quantity.
Customs instructionscustoms.instructionsFree-text handling notes for customs processing.
Declaration statementcustoms.declaration_statementFormal legal text for the customs declaration.

4. Customs parties

For domestic shipments, pickup and dropoff are sufficient. For international shipments, customs authorities and carriers may require you to identify additional parties. These appear on the commercial invoice and customs declaration documents.

PartyPathRole
Sellercustoms.sellerThe entity selling the goods. Appears on the commercial invoice as the invoicing party.
Buyercustoms.buyerThe entity purchasing the goods (typically the end customer). The "sold to" party on the invoice.
Importercustoms.importerImporter of Record, responsible for clearing goods through customs at the destination. Typically carries destination-country tax/duty registration numbers (VAT, IOSS, EOR).
Exportercustoms.exporterExporter of Record, responsible for export compliance and documentation in the origin country.
Brokercustoms.brokerCustoms broker handling clearance formalities. Used by carriers like FedEx for broker-assisted clearance.

Practical guide to which parties you need:

  • Seller + Buyer are required by most carriers for any cross-border shipment.
  • Exporter is often the same as the seller, but may differ if a third-party logistics provider handles export. Required by carriers like DHL Express.
  • Importer is often the same as the buyer, but critical when a dedicated importer of record handles clearance.
  • Broker is used when a customs broker is involved, common with FedEx's broker-assisted import.

Party structure

Every party uses the standard address structure (contact_name, company_name, contact_phone, contact_email, address1, address2, city, state, postcode, country) plus two cross-border-specific fields:

  • personal_id.{id,type}: government-issued identification (passport number, national ID). Required for customs clearance in some countries (e.g. Brazil CPF, South Korea PCC).
  • registration_numbers[]: tax and regulatory identifiers.

Registration numbers

Each party can carry multiple registration numbers. For example, a seller might have both a VAT number and an IOSS number.

"registration_numbers": [
  {
    "type_code": "VAT",
    "value": "GB123456789",
    "issuer_country_code": "GB"
  },
  {
    "type_code": "IOSS",
    "value": "IM1234567890",
    "issuer_country_code": "IE"
  }
]

Supported type_code values:

CodeFull nameCommon use
VATValue Added TaxEU, GCC, UK, most international shipments
IOSSImport One-Stop ShopEU imports under EUR 150
EOREconomic Operator Registration (EORI)EU/UK customs clearance
EINEmployer Identification NumberUS-based entities
GSTGoods and Services TaxIndia, Australia, Singapore, etc.
PANPermanent Account NumberIndia
SSNSocial Security NumberUS individual ID
DUNDun & Bradstreet NumberBusiness identification
FEDFederal Tax IDFederal-level US
STAState Tax IDState-level US
SDTState Department of TaxationUS state
INN / KPP / OGR / OKP(Russia-specific)Russian customs
CNPCod Numeric PersonalRomania
IEInscrição EstadualBrazil state-level tax

Common scenarios:

  • EU under EUR 150. Supply seller's or importer's IOSS.
  • United Kingdom. Importer should carry EOR (EORI); seller may need a UK VAT number if registered.
  • India. PAN and GST on exporter and importer.
  • GCC. VAT registrations on the commercial parties.

Incoterms

customs.incoterms declares which International Commercial Terms apply: who's responsible for shipping costs, insurance, duties, and risk at each stage.

Supported values

CodeFull nameDuties & taxesShipping risk
EXWEx WorksBuyerBuyer from seller's premises
FCAFree CarrierBuyerBuyer after handover to carrier
FASFree Alongside ShipBuyerBuyer after goods placed alongside vessel
FOBFree on BoardBuyerBuyer after goods loaded on vessel
CFRCost and FreightBuyerBuyer after goods loaded on vessel
CIFCost, Insurance, and FreightBuyerBuyer after goods loaded on vessel
CPTCarriage Paid ToBuyerBuyer after handover to carrier
CIPCarriage and Insurance Paid ToBuyerBuyer after handover to carrier
DAPDelivered at PlaceBuyerSeller until arrival at destination
DPUDelivered at Place UnloadedBuyerSeller until unloaded at destination
DDPDelivered Duty PaidSellerSeller until delivery
DDUDelivered Duty UnpaidBuyerSeller until arrival at destination
DATDelivered at TerminalBuyerSeller until unloaded at terminal (replaced by DPU in Incoterms 2020)
DAF / DEQ / DES(legacy)BuyerVarious legacy delivered-at-* terms

Choosing for e-commerce

For most cross-border e-commerce, the practical choice is between two:

  • DAP (Delivered at Place). Seller ships to the destination; buyer pays import duties and taxes on delivery. Most common, but the buyer may be surprised by duties at the door, leading to refused deliveries.
  • DDP (Delivered Duty Paid). Seller covers all costs including duties, taxes, and clearance fees. Better customer experience, but the seller assumes full duty and tax liability and needs the ability to pay duties in the destination country.

If you're seeing high refusal rates on international shipments, switching from DAP to DDP and pricing duties into your checkout flow is the standard remedy.

Carrier-specific incoterm support

Not every carrier supports every incoterm. Carriyo validates the incoterm against the carrier's accepted values at booking time. Examples:

CarrierSupported incoterms
DHL ExpressCFR, CIF, CIP, CPT, DAF, DAP, DAT, DDP, DDU, DPU, DEQ, DES, EXW, FAS, FCA, FOB
FedExDAP, DAT, DDP, DDU, EXW
UPSCFR, CIF, CIP, CPT, DAF, DDP, DDU, DEQ, DES, EXW, FAS, FCA, FOB
DHL eCommerceDDP, DDU
AJEXDDP, DDU
GLSDDP, DDU
DB SchenkerCFR, CIF, CIP, CPT, DAF, DAP, DAT, DDP, DDU, DEQ, DES, DPU, EXW, FAS, FCA, FOB

If you pass an unsupported incoterm, booking fails with an error listing the accepted values.

Incoterm resolution

Incoterms follow the standard configuration hierarchy:

  1. If customs.incoterms is set on the shipment, that wins.
  2. Otherwise, the carrier account's configured default applies.
  3. If neither is set, the carrier booking proceeds without an incoterm (which may fail if the carrier requires one).

Pass per shipment when you have mixed requirements: DDP for some destinations, DAP for others. Configure on the carrier account when all shipments through that account use the same trade terms. This is typical for accounts dedicated to a specific lane (for example, a DHL account used exclusively for DDP shipments from UAE to EU).

Carrier account defaults

Carrier account configuration is managed in the Dashboard. Each carrier exposes a different set of supported settings, depending on the carrier's specification and what Carriyo's integration supports. Three categories show up most often for cross-border flows:

Incoterm default

A default incoterm (e.g. DDP, DAP) set on the carrier account applies to every shipment booked through it unless the shipment explicitly passes customs.incoterms.

Customs party defaults

For carriers that require customs parties (like DHL Express), the full details of each party (name, company, address, registration numbers) can be pre-configured on the carrier account. This is common when:

  • The seller / exporter is always the same entity (your company).
  • The importer is a fixed third-party agent handling customs clearance at the destination.

When defaults are configured, you don't pass the corresponding customs.seller, customs.exporter, or customs.importer objects on each shipment. Carriyo fills them in at booking time.

Smart party defaults

Some carriers (DHL Express among them) support fallback rules that derive parties from the shipment's pickup/dropoff:

  • Seller defaults to pickup. If no seller is configured on the shipment or the carrier account, the pickup address is used as the seller.
  • Buyer defaults to dropoff. If no buyer is configured, the dropoff address is used as the buyer.

Useful for standard e-commerce flows where the seller is always the warehouse and the buyer is always the end customer. With both enabled, you may not need to pass seller or buyer at all.

Other carrier-specific settings

Examples of additional cross-border settings exposed at the carrier account level:

  • DHL Express. Customs declarable flag, invoice type and template, invoice signatory details, shipper and receiver registration numbers.
  • FedEx. Customs broker details (name, address, account number), terms of sale.

The available fields vary by carrier. Work with your Carriyo support team to set up the carrier account for your cross-border requirements.

Common pitfalls

SymptomLikely causeFix
Booking fails with "incoterm not supported"Incoterm value not accepted by this carrierUse a value from the Carrier-specific support table.
Customs hold or clearance delayVague item description, missing hs_code, or wrong origin_countrySpecific descriptions, accurate HS codes, correct origin country.
"Missing registration number" errorCarrier requires a tax ID on a customs partyAdd registration_numbers to seller / buyer / importer / exporter as needed.
Carrier charge higher than expectedMissing parcel dimension causing carrier to apply default volumetric weightAlways provide accurate parcel dimensions.
Duties charged to the wrong partyWrong incoterm (DDP vs DAP)Check the incoterm matches your commercial agreement.
Shipment rejected at originMissing exporter registration numbers or export licenseAdd EOR or other required registrations to customs.exporter.
Refused delivery / customer complaints about duty chargesCustomer unaware of import duty under DAP/DDUSwitch to DDP and price duties into checkout.

How it fits with other modules

  • Shipping. Cross-border shipments are normal Shipments with the customs object populated.
  • Carrier configuration. Cross-border defaults (incoterm, parties, smart defaults) are carrier-account configuration.
  • Booking flow. Customs data is validated during the carrier booking call.
  • Tracking. Once booked, cross-border shipments track the same way as domestic ones.