# A scan is not a URI.

*31 July 2026 · the launch journal*

The person commissioning scan lanes learns this one the hard way, mid-deployment: two cases arrive carrying 2D codes with *identical data* — same GTIN, same lot, same expiry — and one scans as a string of digits with invisible separators while the other scans as a URL. The lane software that assumed "2D means URL" writes the first one into a free-text field, and the site's data model has now quietly forked at the scanner. Everything downstream — dedupe, receiving reconciliation, trace — inherits the fork.

There are two payload grammars on 2D media, both conformant, both in the field for the long haul. A capture pipeline must parse both into one canonical form, because the *barcode* is a transport decision made by whoever printed it, and you do not control who printed it.

## The same data, both wire forms

GTIN `09506000134352`, lot `ABC123`, expiry 31 March 2027.

**Form one — GS1 element strings** (typical on GS1 DataMatrix; also GS1-128 and GS1 QR):

```
]d2 01 09506000134352 17 270331 10 ABC123
```

Byte-annotated: `]d2` is the symbology identifier the decoder prepends — "GS1 DataMatrix, FNC1 in first position" — and it is your only reliable signal that AI syntax follows. `01` is an Application Identifier: *GTIN, fixed length 14*, so the next 14 digits are consumed with no separator. `17` likewise: *expiry, fixed 6, YYMMDD*. `10` is *batch/lot, variable length up to 20* — variable-length AIs terminate with an FNC1 character (invisible in most scanner output, transmitted as ASCII 29 `<GS>`) or by being last, which is why conformant printers order fixed-length AIs first. Paste this payload into a browser and you get a search-engine shrug: element strings are a data structure, not an address.

**Form two — GS1 Digital Link URI** (typical on consumer-facing QR):

```
https://id.gs1.org/01/09506000134352/10/ABC123?17=270331
```

The same AIs wearing URI syntax, and the ordering is normative, not stylistic: the primary key `01` leads the path, qualifiers follow in the standard's fixed hierarchy (CPV `22`, then lot `10`, then serial `21` — you may omit levels, never reorder them), and non-qualifier data attributes like expiry `17` ride the query string. This form *is* web-resolvable — a phone camera opens it — and it parses with the same rigor, per the GS1 Digital Link URI syntax standard (a free download at ref.gs1.org, like the General Specifications that define every AI above).

## The canonical AI map is the contract

Both parsers must land on the identical structure:

```json
{ "01": "09506000134352", "10": "ABC123", "17": "270331" }
```

That map — not the raw payload, not the URI — is what the rest of the pipeline consumes: check-digit verification on `01`, date validation on `17`, and construction of the event identifier (GTIN+serial → SGTIN, GTIN+lot → LGTIN) that [the scan-to-capture entry](/journal/scan-to-object-event/) turns into a validated EPCIS event. If the two grammars land anywhere different, you have two data models wearing one label, and [the hash-normalization seam](/journal/urn-digital-link-one-hash/) one layer up cannot save you — it normalizes identifier *syntaxes*, not a lot code that one lane stored in a free-text remarks field.

The parsing itself, both grammars to one map, runs at [barcoding.dev](https://barcoding.dev) — the family's capture-side site, same dark look and feel, same rule that the parse is checkable rather than approximate.

## The corrections table

Statements a GS1-literate reviewer will grade your deployment plan on — each one a mistake that ships weekly in vendor decks:

| Common claim | The checkable fact |
|---|---|
| "QR is for consumers, DataMatrix is retail POS" | Both are sanctioned 2D carriers. In practice GS1 DataMatrix is what regulated and healthcare lines print; QR carrying a Digital Link URI is the consumer-scannable choice. The *payload grammar*, not the symbology, decides what a phone can do with it. |
| "Sunrise 2027 requires lot and expiry in every barcode" | The Sunrise 2027 milestone (GS1 US) is retail POS being *able to scan 2D* — and it is explicitly a voluntary industry milestone, with GTIN extraction at POS as the baseline capability. Lot, expiry, and serial are the brand owner's optional AIs, chosen per use case. |
| "2D codes are URLs" | Element-string payloads resolve to nothing in a browser. Only the Digital Link URI form is web-resolvable. The encoding choice belongs to the GTIN owner — your lane parses both, or it forks. |
| "The scanner tells you what it read" | Only if you configure it to transmit symbology identifiers (`]d2`, `]Q3`, `]C1`, …) and the FNC1/`<GS>` separator faithfully. A lane that strips them turns variable-length AI parsing into guesswork. Commission the transmit format first. |
| "We'll detect AI syntax by sniffing digits" | `01` opening a payload is also how a phone number can start. The symbology identifier is the sanctioned signal; heuristics on the bare payload are how mis-parses reach the record with a straight face. |

## What to hand the site

Concretely, for the deployment checklist: configure lanes to transmit symbology identifiers and separators; route every payload through one shared parser to the canonical AI map; verify the `01` check digit at the lane (a transposed GTIN caught at the dock costs nothing; caught in reconciliation it costs a meeting); and treat "which AIs does this supplier actually print" as posted fact per supplier, not folklore. The map is also your test surface — a conformant lane is one where both wire forms of the same data produce byte-identical maps, and that is a fixture you can keep, not a vibe.

Where this leads: once the map is canonical, one tap or one function call turns a scan into a conformant EPCIS ObjectEvent with the five dimensions filled and validated — that pipeline, end to end with real transcripts, is [the next entry](/journal/scan-to-object-event/). The resolver that answers "what *is* this thing" from the same map — enrichment, linksets, the identity behind the code — is barcoding.dev's `resolve` surface; to put your lanes on it, [say what you will capture](/get-a-key/?segment=integration). The executive brief on why the 2D transition is a data-layer decision, not a printer purchase, reads at [visibility.cloud](https://visibility.cloud).

---

**Proof, not adjectives.** This build could not verify the gateway's test suite green, so no sentence on this page claims a passing run — the last verified run is recorded in the repository's own CI, not here. Live on this origin, no key: POST /translate, /validate, /hash. No conformance attestation has ever been issued. The dated ledger is /what-ships-today/.
