
An HS code rarely appears in just one place. The commercial invoice typically carries one per line item. The certificate of origin usually repeats them, since it identifies the same goods by the same classifications. The bill of lading often lists one per cargo line. Three documents, written by three parties at three different times, all describing the same goods.
Each of those documents can look perfectly fine on its own. The invoice’s code exists, matches the goods, reads cleanly off the page. So does the certificate’s. The problem only becomes visible when you put them side by side and notice they are different codes. A checker that evaluates documents one at a time will never see it.
This is why HS codes get checked at two levels in CargoLint, and why the two levels are engineered as separate systems that fail for different reasons. This post walks through both.
Two different questions
The first question is about a single document: is this declared code consistent with the goods it sits next to? That check has access to the line’s description, so it can score the code against what the document says the goods are.
The second question is about the shipment: do the papers tell the same story as each other? That check compares declared codes across documents, and deliberately does not re-litigate whether any of them fits the goods - that verdict already exists at the document level.
Keeping the two apart matters because the failure modes are different. A code can fit the goods and still contradict the certificate of origin. A code can agree with every other paper and still point at a subheading the description does not support. Merging those into one signal would leave a reviewer guessing which problem they are looking at.
What a declared code is checked against
A declared code has two parts, and they are checked separately. The first six digits are the international part - the subheading shared by every country that uses the Harmonized System. Everything after the sixth digit belongs to the destination country’s own tariff schedule.
For the international part, two checks run. The subheading has to exist in the current HS edition, and the line’s description has to independently route to it - the same analysis that recommends codes for a blank line, pointed at the declared one. When the description routes somewhere else entirely, the code is surfaced as doubtful, with the better-matching candidate shown for comparison. When the first pass lands in an uncertain band rather than a clear verdict, a second model pass reads the description against the declared subheading before anything is flagged: agreement clears the flag, disagreement attaches the reasons, and if that second pass is unavailable the original assessment simply stands - the system does not invent a verdict it could not compute.
Unrecognised codes get one extra courtesy. When the declared six digits are not a subheading in the current edition, every single-digit variation is tried against the description. If exactly one variant both exists and matches the goods, it is offered as the likely intended code - useful when a 6 was misread as an 8 somewhere between the shipper’s system and the scan. If two or more variants qualify, nothing is offered. A guess presented with one candidate is a suggestion; the same guess presented with three is noise.
Beyond the sixth digit, checking is only possible where the destination’s tariff schedule is on file - currently the US schedule, with other destinations evaluated to six digits and labelled accordingly. The full code is matched against the currently effective tariff lines under its subheading. Where several lines could apply, the description decides between them - or, when it genuinely cannot, the check records the single question that would settle it rather than picking a line and hoping.
Document type changes what runs. Invoice and certificate of origin lines describe the product, so they get the full treatment. A bill of lading cargo line describes freight - “said to contain: 36 cartons apparel” - so scoring a code against that wording would flag correct codes, and only the checks that read no description run there. Packing lists carry no HS codes at all.
The same verdict, everywhere
One property is enforced across all of this: the same code, the same description, and the same destination always produce the same verdict, wherever it renders. The verdict is computed once and cached against exactly those three inputs, so the pill you see on a line in Document Details is the same one you see in the review editor and on a shared document - same state, same colour, same explanation.
That sounds like an implementation detail. It is closer to a trust requirement. A reviewer who sees a code flagged on one screen and clean on another stops believing either screen. Determinism per input triple is what makes it safe to put the verdict in front of people at all.
It also draws a clean boundary with the shipment level: if two documents declare different codes for the same goods, that is two different inputs, two independent verdicts, and - by design - a disagreement that per-document checking will never report. Catching it is the cross-document rule’s job.
What counts as disagreement between documents
Before two declared codes can disagree, the comparison has to be fair. Codes are normalised to digits only, so 8501.10.20 and 85011020 are the same code - formatting differences between documents never count as a mismatch.
Agreement is then defined as a prefix match at six digits or longer. A code declared as 850110 agrees with 85011020: the invoice speaks at the international level, the other document adds the destination detail, and there is no contradiction between them. But 8501 against 850110 is treated as no agreement at all - four digits is a heading, and a heading is too coarse to confirm anything. Requiring six digits of shared specificity keeps “these agree” meaning something.
And absence is handled as absence. A line with no code on one document leaves nothing to reconcile - extraction is sparse on real paperwork, and an empty field is a gap, which is a different situation from two filled fields that contradict each other. Missing codes are surfaced by the per-document checks as their own state.
Invoice versus certificate of origin
These two documents mirror each other line by line, so the comparison is per line: each invoice line is paired with the certificate line at the same position, falling back to matching by product code when the line orders differ. Paired lines whose codes fail the agreement test become one finding that lists every mismatched pair.
The interesting design decision is what gets flagged. For this pair of documents, both ends of every mismatch - the invoice field and the certificate field. Neither document is authoritative for HS codes; the invoice is not the truth against which the certificate is judged, or the other way round. The system can see that they disagree, and it can show both values side by side, but deciding which one describes the goods is a judgement about the goods - exactly the thing the reviewer, with the per-document verdicts alongside, is best placed to settle.
Bill of lading versus invoice
The B/L comparison cannot work line by line, because B/L cargo lines aggregate freight rather than mirroring invoice lines - one cargo line might cover three invoice lines that happen to travel in the same containers. Pairing by position would manufacture false mismatches out of packing arrangements.
So the comparison is set membership: each B/L cargo line’s code is checked against all of the invoice’s codes, and only a code that agrees with none of them is flagged. And because a cargo line pairs with no single invoice line, there is no specific invoice field to point at - only the B/L side of the finding is highlighted.
What lands on the desk
Both cross-document findings surface as warnings on the shipment’s consistency report, alongside the quantity, value, and date checks, with the observed values from each contributing document shown together. The per-document verdicts stay on the lines themselves, as status pills in the Document Issues panel. The two layers link to each other but never overwrite each other.
None of it is a ruling. A flagged mismatch is the system saying these papers disagree, and here is exactly where - which document, which line, which values. Sometimes the certificate is stale, sometimes the invoice was corrected late, sometimes a freight forwarder copied last shipment’s code. Which paper needs fixing is a call that belongs to the person filing the entry; the engineering goal is that they make it with every disagreement already on the table, before anyone else finds it for them.
For the full list of per-line HS states, see what each HS code state means; for everything else the shipment-level checks compare, see shipment consistency checks.