Logistics & Shipping Validation
Four validators, one shipping workflow. Here's how to validate a container code, HS classification code, UN/LOCODE port, and EORI number before processing an international shipment — catching data errors before they reach customs.
In this guide
- 1. The shipping validation problem
- 2. The four identifiers
- 3. Step 1: Validate the container code
- 4. Step 2: Validate the HS code
- 5. Step 3: Validate the UN/LOCODE
- 6. Step 4: Validate the EORI number
- 7. Putting it all together — parallel validation
- 8. cURL examples
- 9. Handling edge cases
- 10. Summary checklist
1. The shipping validation problem
International shipments involve four distinct identifier types — each governed by a different standard, each validated by a different authority. A single invalid identifier can stall a shipment at customs, trigger a port rejection, or result in a customs fine.
The challenge is that these identifiers come from different parties: the carrier provides the container code, the shipper provides the HS classification and EORI, and the freight forwarder provides the LOCODE. By the time a booking reaches your platform, any of these could contain a typo, an outdated code, or a missing check digit.
The solution: validate all four identifiers in parallel before the booking is submitted to the carrier or customs system — catching errors at data entry, not at the port.
2. The four identifiers
Each identifier covers a different aspect of an international shipment:
| Validator | What it validates | When to use | API endpoint |
|---|---|---|---|
| Container Code | ISO 6346 owner code + serial + mod-11 check digit | Tracking intermodal containers | GET /v0/container-code |
| HS Code | WCO Harmonized System product classification | Customs declarations, tariff calculations | GET /v0/hs-code |
| UN/LOCODE | 5-char UN port/place code + location lookup | Port of loading/discharge, place of delivery | GET /v0/locode |
| EORI | EU Economic Operators Registration and Identification | Identifying the exporter/importer in EU customs | GET /v0/eori |
3. Step 1: Validate the container code
ISO 6346 container codes follow a strict format: a 3-letter owner code, a 1-letter category identifier, a 6-digit serial number, and a mod-11 check digit. The endpoint validates all parts:
- Owner code — 3-letter prefix registered with the Bureau International des Containers (BIC)
- Category identifier — U (freight container), J (detachable freight container equipment), Z (trailer/chassis), R (reefer)
- Serial number — 6-digit sequence number assigned by the owner
- Mod-11 check digit — weighted checksum that catches transpositions
Example response
{ "valid": true, "ownerCode": "MSC", "categoryCode": "U", "categoryName": "Freight container", "serialNumber": "305638", "checkDigit": "3" }
What to check in your code
valid === true — the owner code, serial, and mod-11 check digit are all valid
categoryCode — verify the container type matches the cargo (e.g., R = reefer for refrigerated goods)
ownerCode — the 3-letter prefix identifies the container owner/operator for tracking
4. Step 2: Validate the HS code
The Harmonized System (HS) is the WCO's international goods classification standard, used by 200+ countries. HS codes have hierarchical depth:
- Chapter (2 digits) — e.g., 84 = Machinery
- Heading (4 digits) — e.g., 8471 = Computers
- Subheading (6 digits) — e.g., 847130 = Portable computers
Example response
{ "valid": true, "code": "847130", "level": "subheading", "description": "Portable automatic data-processing machines", "formatted": "8471.30", "chapter": { "code": "84", "description": "Nuclear reactors, boilers, machinery and mechanical appliances; parts thereof" } }
What to check in your code
valid === true — the code exists in the WCO HS nomenclature
level === "subheading" — customs declarations require 6-digit codes; heading-level is insufficient
description — display to the user for confirmation that the classification matches the goods
level.5. Step 3: Validate the UN/LOCODE
UN/LOCODE is a 5-character code identifying ports, airports, and logistics places worldwide. The endpoint validates the code format and looks up the location:
- Country code — first 2 characters (ISO 3166-1 alpha-2)
- Location code — last 3 characters (alphanumeric)
- Functions — port, rail, road, airport, postal designations
- Coordinates — geographic position for routing calculations
Example response
{ "valid": true, "found": true, "locode": "DEHAM", "country": "DE", "location": "HAM", "name": "Hamburg", "nameAscii": "Hamburg", "subdivision": "HH", "functions": ["port", "rail", "road"], "coordinates": "5333N 00958E" }
What to check in your code
valid === true — the 5-character format is correct
found === true — the location exists in the UN/LOCODE database; a valid format but unknown port causes routing failures
functions — verify the location supports the required transport mode (e.g., "port" for sea shipments)
6. Step 4: Validate the EORI number
EORI (Economic Operators Registration and Identification) is a unique identifier required for any entity importing or exporting goods into/from the EU or UK. The endpoint validates:
- Country code prefix — 2-letter EU/UK country code
- Identifier format — country-specific format (e.g., GB = 12-15 alphanumeric)
- Structure — validates the total length and character set per country rules
Example response
{ "valid": true, "countryCode": "DE", "country": "Germany", "identifier": "123456789012345", "formatted": "DE123456789012345" }
What to check in your code
valid === true — the EORI passes format and length validation for the declared country
countryCode — the EORI country must match the exporter/importer's declared country of establishment
formatted — use the normalized form for customs filing to ensure consistent formatting
7. Putting it all together — parallel validation
The following Node.js example validates all four identifiers in parallel using Promise.all. Use the @isvalid-dev/sdk package or the native fetch API (Node 18+).
import { createClient } from '@isvalid-dev/sdk'; const iv = createClient({ apiKey: process.env.ISVALID_API_KEY }); async function validateShipment({ containerCode, hsCode, locode, eoriNumber }) { const [container, hs, loc, eori] = await Promise.all([ iv.containerCode(containerCode), iv.hsCode(hsCode), iv.locode(locode), iv.eori(eoriNumber), ]); return { container: { valid: container.valid, ownerCode: container.ownerCode ?? null, categoryCode: container.categoryCode ?? null, categoryName: container.categoryName ?? null, }, hs: { valid: hs.valid, level: hs.level ?? null, description: hs.description ?? null, formatted: hs.formatted ?? null, }, locode: { valid: loc.valid, found: loc.found ?? null, name: loc.name ?? null, functions: loc.functions ?? null, }, eori: { valid: eori.valid, countryCode: eori.countryCode ?? null, formatted: eori.formatted ?? null, }, }; } // ── Example: Hamburg port, electronics, MSC container, German EORI ───────── const result = await validateShipment({ containerCode: 'MSCU3056383', hsCode: '847130', locode: 'DEHAM', eoriNumber: 'DE123456789012345', }); console.log('Container:', result.container.valid ? `✓ ${result.container.ownerCode} / ${result.container.categoryName}` : '✗ invalid'); console.log('HS Code :', result.hs.valid ? `✓ ${result.hs.formatted} — ${result.hs.description}` : '✗ invalid'); console.log('LOCODE :', result.locode.found ? `✓ ${result.locode.name} [${result.locode.functions?.join(', ')}]` : '✗ not found'); console.log('EORI :', result.eori.valid ? `✓ ${result.eori.formatted}` : '✗ invalid'); const requiresFullHs = result.hs.valid && result.hs.level !== 'subheading'; if (requiresFullHs) console.warn('⚠ HS code must be 6-digit subheading for customs filing');
Promise.all. The total latency is the slowest single call, not the sum of all four.8. cURL examples
Validate a container code (MSC container):
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/container-code?value=MSCU3056383"
Validate an HS code (portable computers):
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/hs-code?value=847130"
Validate a UN/LOCODE (Hamburg):
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/locode?value=DEHAM"
Validate an EORI number (Germany):
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/eori?value=DE123456789012345"
9. Handling edge cases
Container code with spaces or dashes
Container codes are often formatted as "MSCU 305638-3" or "MSCU3056383". Normalize to the 11-character form before validation.
function normalizeContainerCode(raw) { // Remove spaces and dashes, uppercase return raw.replace(/[\s-]/g, '').toUpperCase(); } const normalized = normalizeContainerCode('MSCU 305638-3'); // → 'MSCU3056383' const result = await iv.containerCode(normalized);
HS code at heading level — not sufficient for customs
A 4-digit HS heading passes validation but is insufficient for customs declarations. Check the level field and require subheading depth.
const hs = await iv.hsCode('8471'); if (hs.valid && hs.level !== 'subheading') { return { status: 'insufficient', reason: `HS code is at ${hs.level} level — customs requires 6-digit subheading`, hint: `Provide a more specific code under chapter ${hs.chapter?.code}`, }; }
LOCODE valid format but not found in database
The 5-character format is correct but the location doesn't exist in UN/LOCODE. This often happens with obsolete codes or typos in the country prefix.
const loc = await iv.locode('DEABC'); if (loc.valid && !loc.found) { // Format is valid (DE + 3 chars) but not in UN/LOCODE registry return { status: 'unknown_locode', reason: 'Location code not found in UN/LOCODE database', suggestion: 'Check the country prefix and location code', }; }
EORI country mismatch
The EORI country code doesn't match the shipper's declared country of establishment. A German company should have a DE-prefixed EORI.
if (eoriResult.valid && eoriResult.countryCode !== shipperCountry) { logger.warn(`EORI country mismatch: EORI=${eoriResult.countryCode}, shipper=${shipperCountry}`); return { status: 'requires_review', reason: 'EORI country does not match declared country of establishment', }; }
10. Summary checklist
See also
Validate shipping documents before filing
Free tier includes 100 API calls per day. No credit card required. Container code, HS code, LOCODE, and EORI validation all included.