Worked examples · Logistics
EDI invoices and the check digit.
A freight invoice sent as an X12 210 (Motor Carrier Freight Details and Invoice) is read against the demurrage and detention checklist: its structure, its totals, and the check digit of the container it bills for.
The problem
The Federal Maritime Commission's rule on demurrage and detention billing (46 CFR part 541, effective May 28, 2024) lists the information an invoice must contain. Under § 541.5, an invoice that omits required information eliminates the billed party's obligation to pay the charge.
A container number carries its own check digit, which validates the transmission of the owner code and serial number (ISO 6346). A valid digit shows the number is internally consistent. It does not show that the container exists, or that it is the right one.
Invoices travel as EDI as well as on paper, and the same questions apply to both.
Three invoices
Well formed
16 segments read under the public X12 layer, version 004010.
One digit of the container number
The container number, against its own check digitfindingequipment_check_digit_disagrees
16 segments read under the public X12 layer, version 004010.
A total that does not add up
The grand total, against its own line chargesfindingtotals_disagree
16 segments read under the public X12 layer, version 004010.
A finding is a statement about the document, set in neutral ink under its own name. Each certificate still verifies: the document is wrong, the certificate is not.
What the checklist can answer from EDI
The same checklist reads paper and EDI. From a 210, it locates 2 of its 18 data points, each at its byte position. 2 are refused as ambiguous, because the set carries more than one candidate. 14 are absent, with the code no_fact_answers_this_point: the set does not carry them.
That is stated, not hidden. On EDI the checklist's value is the structure, the totals and the check digit. The paper invoice is read in full on the demonstration page.
the bill of lading numberresolvedPBLU2026034771
the total amount dueresolved162500
the invoice daterefusedambiguous_data_point
the daily raterefusedambiguous_data_point
Boundary
CERTIFIED: this report states which of the fields this checklist asks for were LOCATED on the invoice submitted, and names each field that was not. A field reported as not located is reported as missing from this invoice, without a hedge: where 46 CFR part 541 requires a field, it requires it on the invoice itself, and 46 CFR § 541.5 makes the omission of a required field dispositive.
NOT ATTESTED That every field this checklist asks for is required of this particular invoice is not attested; some apply only to an export, or only to an import. What an omission decides about the charges billed is not attested, and whether they are owed is not determined here. That a located value is correct is not attested; it is certified as the invoice states it.
Check it without Factum
The certificate is published as it was returned. Download it with the key and the Factum Verifier, and run the command. It needs Node 18 or later and nothing else.
Verifiedexit 0
The files: wellformed.json, public-key.txt, edi-210-01-wellformed-pblu.edi, edi-210-09-mustfail-check-digit-pblu.edi, edi-210-08-mustfail-totals-disagree-pblu.edi, SHA256SUMS.
The key is a development key minted for this recording. A valid signature says the holder of this key signed these bytes.
node factum-verify.mjs wellformed.json public-key.txt
VERIFIED keyId vd-2b223ab58835e187 canonical bytes 75344 barcode … (the signed barcode, 478 characters, printed in full when you run it) surface FULL - the body carries no surface block, so every claim the run captured is on it CERTIFIED BY THIS CHECK: this document was signed by the holder of the private half of the key you supplied, over the canonical form named in its signature block, and has not been altered by one byte since. NOT CERTIFIED BY THIS CHECK: that the key you supplied is the operator's. That comes from WHERE YOU OBTAINED THE KEY and from nothing in this file - a valid signature under the wrong key is exactly what a forgery looks like.
The certificate is published as it was returned. Download it with the key and the Factum Verifier, and run the command. It needs Node 18 or later and nothing else.
Verifiedexit 0
The files: check-digit.json, public-key.txt.
node factum-verify.mjs check-digit.json public-key.txt
VERIFIED keyId vd-2b223ab58835e187 canonical bytes 79026 barcode … (the signed barcode, 488 characters, printed in full when you run it) surface FULL - the body carries no surface block, so every claim the run captured is on it CERTIFIED BY THIS CHECK: this document was signed by the holder of the private half of the key you supplied, over the canonical form named in its signature block, and has not been altered by one byte since. NOT CERTIFIED BY THIS CHECK: that the key you supplied is the operator's. That comes from WHERE YOU OBTAINED THE KEY and from nothing in this file - a valid signature under the wrong key is exactly what a forgery looks like.
The certificate is published as it was returned. Download it with the key and the Factum Verifier, and run the command. It needs Node 18 or later and nothing else.
Verifiedexit 0
The files: totals.json, public-key.txt.
node factum-verify.mjs totals.json public-key.txt
VERIFIED keyId vd-2b223ab58835e187 canonical bytes 78223 barcode … (the signed barcode, 492 characters, printed in full when you run it) surface FULL - the body carries no surface block, so every claim the run captured is on it CERTIFIED BY THIS CHECK: this document was signed by the holder of the private half of the key you supplied, over the canonical form named in its signature block, and has not been altered by one byte since. NOT CERTIFIED BY THIS CHECK: that the key you supplied is the operator's. That comes from WHERE YOU OBTAINED THE KEY and from nothing in this file - a valid signature under the wrong key is exactly what a forgery looks like.