GS1 · VIN · ISBN · UDI · NDC · SSCC — and everything in your building that carries no barcode at all
Scan it. Name it.
Prove where the answer came from.
barcoding.dev answers the question every scanner asks and no neutral API answers — what is this, and how do you know? — and the harder one standing behind it: what do you scan when the thing has no barcode? Reading a mark and giving a mark to something that never had one are the same door, and this is it. Everything below is a real encoding of real bytes, drawn to scale and decoded in the build that published it.
UPC · GS1 DataMatrix · Digital Link · PDF417 — the same grid, drawn to scale
Twelve digits, then everything else.
A UPC holds a class of product and nothing more — thirteen digits, 95 modules, no room for the lot, the expiry or the serial. Everything to its right is a real encoding of real bytes, drawn at the same module pitch, decoded in the build that published it.
01 EAN-13 9521234567899 13 B · 95 modules 37.3 mm at X = 0.33 decoded byte-exact PASS A class of product, not an item. 95 modules wide, and the height carries nothing.
the complete payload, exactly as it was handed to the encoder
9521234567899
build gate · round-trip
encoded 13 B → 95 modules
decoded zxing-cpp (zxing-wasm 3.1.2) → 13 B
compare byte-exact PASS
02 GS1 DataMatrix (01)09521234567899(10)L26A0417…[+18 B elided]…212300047719 60 B · 22 × 22 modules 6.0 mm at X = 0.25 decoded byte-exact PASS Four fields — GTIN, lot, expiry, serial. The same four in GS1-128 is 431 modules: 223.2 mm at GS1's general-distribution minimum. Or a 6.0 mm square.
the same four fields, at the same module pitch
Capacity grows while the mark shrinks. The GS1-128 above is the site's own encoder, drawn at one pixel per module because it does not fit the ladder's pitch on any screen.
the complete payload, exactly as it was handed to the encoder
(01)09521234567899(10)L26A0417(17)271231(21)SN95212300047719
build gate · round-trip
encoded 60 B → 22 × 22 modules
decoded zxing-cpp (zxing-wasm 3.1.2) → 60 B
compare byte-exact PASS
03 QR · Digital Link https://barcoding.dev/01/09521…[+39 B elided]…19?17=271231 81 B · v5 · 37 × 37 modules 14.9 mm at X = 0.33 decoded byte-exact PASS The same identity as an address. It resolves; it also reads with nothing behind it.
the complete payload, exactly as it was handed to the encoder
https://barcoding.dev/01/09521234567899/10/L26A0417/21/SN95212300047719?17=271231
build gate · round-trip
encoded 81 B → 37 × 37 modules, QR v5, ECC M
decoded zxing-cpp (zxing-wasm 3.1.2) → 81 B
compare byte-exact PASS
04 PDF417 @ ANSI 000000100001DL0031025…[+248 B elided]…CUNONE DDAF 290 B · 239 × 57 modules 46.2 mm at X = 0.19 decoded byte-exact PASS Hundreds of millions of people carry one of these. The card's printed face is a rendering of exactly these fields — and at 290 bytes it is already 1.5× wider than the square beside it carrying 595.
the complete payload, exactly as it was handed to the encoder
@
ANSI 000000100001DL00310259DLDCAC
DCBNONE
DCDNONE
DBA20311231
DCSSPECIMEN
DACPATTERN
DADNONE
DBD20260804
DBB19900417
DBC1
DAYBRO
DAU070 IN
DAG1400 DEPOT RD
DAILA VERGNE
DAJTN
DAK370860000
DAQS952123000477
DCFSPECIMEN952123000477
DCGUSA
DCK952123000477190001
DDEN
DDFN
DDGN
DCUNONE
DDAF
build gate · round-trip
encoded 290 B → 239 × 57 modules
decoded zxing-cpp (zxing-wasm 3.1.2) → 290 B
compare byte-exact PASS
The payload is invented and shaped like an AAMVA record; it is not anyone's licence and this platform stores no licence data — parse, verify, never store.
05 QR · Digital Link on an SSCC https://barcoding.dev/00/00952…[+553 B elided]…1800.PREPAID 595 B · v17 · 85 × 85 modules 30.7 mm at X = 0.33 decoded byte-exact PASS The document's own fields, in the query string of its own address. No network, no resolver, no account.
the complete payload, exactly as it was handed to the encoder
https://barcoding.dev/00/009521230000000019?402=95212300000000093&401=GINC-95212-260804-0093&400=PO-4471-0093&403=LTL-DIRECT.DOCK-14&410=0952123000019&411=0952123000026&412=0952123000033&413=0952123000040&414=0952123000057&415=0952123000064&417=0952123000071&420=37086&422=840&30=18&37=1440&3302=009435&3922=1875500&8008=260804140000&7003=2608061800&90=SCAC.NRFC-CLASS.85-NMFC.156600&91=SHIPFROM.NORTHRIDGE-FOODS-CO.1400-DEPOT-RD.LA-VERGNE.TN.37086.US&92=SHIPTO.CENTRAL-GROCERS-DC-14.220-INTERMODAL-PKWY.LA-VERGNE.TN.37086.US&93=TRAILER.NRFC-48221.SEAL.0951447.APPT.2608041400-2608041800.PREPAID
build gate · round-trip
encoded 595 B → 85 × 85 modules, QR v17, ECC M
decoded zxing-cpp (zxing-wasm 3.1.2) → 595 B
compare byte-exact PASS
A valid GS1 Digital Link on an SSCC primary key: it resolves at https://barcoding.dev/00/009521230000000019 when there is a network, and it reads with nothing behind it when there is not.
ratified barcode typespiloted; not an approved barcode type
the climb — the same header, plus line items at a measured 32 B each
Three symbols, not one that animates: a symbol must arrive whole and be scannable whenever it is visible. Error correction is M on every symbol on this page. The same 595-byte header at the other three levels: L v15 (77) · M v17 (85) · Q v21 (101) · H v24 (113).
This is where it stops. 12 line items is 979 bytes. The build keeps adding line items until the encoder refuses, and prints the wall it hits: 66 line items — 2707 bytes, QR v40 at error correction M. The label programs that actually run in production stop far below it. The mark carries the document's fields; it was never going to carry the page. This site's own Archivo subset is 54,336 bytes — 20 times everything a QR code can hold at this error-correction level.
What this is, said plainly: ratified GS1 Application Identifiers, in a Digital Link URI's query string exactly where the URI syntax puts data attributes. A plain QR carrying that URI is not one of the barcode types the General Specifications approve for this job. We show it because it works, and we say what it is.
19 of these fields are ratified GS1 Application Identifiers with a public meaning any conformant reader already knows — the shipment number, the seven party GLNs, the weights, the counts, the dates. The 4 that carry the party names, the addresses and the trailer are AIs 90–94, mutually agreed information: real Application Identifiers, but their meaning is agreed between two parties, not defined by the standard. GS1 has ratified party-and-address AIs in the 4300 range; this property's pinned AI table is a GenSpecs 26.0 subset that does not yet include them, so we do not claim them.
The same 595-byte header in PDF417, in specification and decoding, is a stripe: 579 modules × 12 rows — 145.8 mm × 9.0 mm at X = 0.25 — or 60.8 mm × 26.3 mm at 10 columns. The comparison is shape, not failure.
Then: what is it, and how do you know?
resolve and verify run pure and local on every identifier family — no network, no account, no key. enrich is not built yet (P0-2); when it answers it will draw on license-clean rails only, and every attribute will ship inside a provenance envelope: source, license class, fetch time, digest. No bare value anywhere in the API is the rule the live verbs already keep.
barcoding resolve '(01)09506000134352(17)261231(10)ABC123(21)1234'
scheme gs1.element-string
gtin 09506000134352 check: mod10 pass
ai.17 2026-12-31 (expiry)
ai.10 ABC123 (lot)
ai.21 1234 (serial)
identity sgtin: urn:epc:id:sgtin:9506000.013435.1234
digital-link https://barcoding.dev/01/09506000134352/21/1234
One parser for both 2D label forms — GS1 element strings and Digital Link URIs — normalized to the same canonical identity and AI map. The same run works on a VIN, an ISBN, a UDI, an SSCC, a tracking number.
fork the scan
From December 2027 the register reads the whole square — and keeps only the part it already had.
GS1's Sunrise 2027 programme expects retail point of sale to read and process 2D barcodes by the end of December 2027, extracting the GTIN. That is the whole obligation. It is cited here as GS1's programme, on GS1's timeline, and never as our forecast — the six things it is routinely claimed to require, each against its primary source, are stated exactly on the blog.
Here is the part nobody budgets. When a brand encodes lot, expiry and serial, all of it arrives in the same square, on the same scan, at the same instant — and this is not hypothetical: every US prescription-drug package already carries exactly that under DSCSA, which is a rollout that already happened rather than a forecast. The register takes the GTIN for the price lookup, because that is the number it has always had, and drops the rest on the floor. That is not a bug in anybody's till: a sale record is item, price, basket. It has nowhere to put this crate, from this batch, received at this door, sold at this lane at 4:52pm.
So fork it. The GTIN goes to the register exactly as it does today, untouched, on the same path. Lot, expiry and serial go out the other side of the fork, as named fields, to something built to hold them.
Nothing at the lane changes. No new step for a cashier. No new hardware — an imager that clears the December 2027 date already reads the whole square; keeping the rest of what it read is a software decision, not a capital one. The scan is already happening, the data is already in the read, and the ask is not to throw it away.
Where the fork runs, and where it stops.
Said on the page rather than discovered in an invoice, because a fork that quietly grows into a platform is how these programmes die.
This door's half is the split, and it is deliberately small. Read the square, split what it said, hand the GTIN out one side unchanged, and hand lot, expiry and serial out the other as named fields with the check verdict computed from the digits alone. That is decode, resolve and verify — pure, local, no key, no account, no network call. It is a split, not a system.
The other side of the fork is not this door's, and this page will not pretend otherwise. What happened, where, and who did it becomes an event at epcis.dev. Rolling it up across lanes, stores, docks and suppliers — and getting it back out as tables you own — is visibility.cloud. barcoding.dev holds no history at all: no query, no trace, alone among the family's doors. Your own history is queried and traced there, never here.
The explainer arc runs on the blog: barcodes → Sunrise 2027 → what a 2D mark means for the record layer → what a scan can and cannot say on its own. Start the arc →
The closed verb set.
Eight verbs answer today. --json on every verb; typed errors on stderr; exit codes 0 ok / 1 negative domain verdict / 2 usage. The same engine behind npx barcoding.dev and the hosted doors on this origin — four of them are listed as MCP tools — resolve, verify, generate, pins — all readOnlyHint: true; the rest are reachable on the CLI door only.
That number is not typed into this page. It is counted, at build time, out of the shipped verb table the CLI and the MCP door are both generated from, and the build refuses to publish if this table and that table disagree.
They come in four movements, and the order is the argument: read a mark, draw a mark, create an identifier for a thing that has none, and say what it means. The third one is the half of this door that has nothing to do with retail, and it is the next section.
| verb | what it does | network | exit-1 verdict |
|---|---|---|---|
| decode | a frame or a photo → what the mark actually says. Runs on the hosted door and, unchanged, in the browser at /scan — frames are decoded in memory and discarded, never stored, never logged | pure · local | nothing decodable in the frame |
| resolve | any identifier-bearing string — what a scanner read, bare GTIN-8/12/13/14, EPC URN, GS1 element string, GS1 Digital Link URI, VIN, ISBN, SSCC, tracking number — to canonical identity: scheme, canonical form, parsed AIs and fields | pure · local | unrecognized / unparseable |
| verify | check digit + structure + prefix legitimacy (GS1 prefix ranges → issuing MO; WMI → manufacturer; ISBN group), from pinned tables | pure · local | fails validation |
| generate | the write-side twin of resolve: id + AI map → element string, Digital Link URI, rendered symbol (QR / DataMatrix / GS1-128 / ITF-14). resolve(generate(x)) === x is the round-trip law | pure · local | unencodable input |
| serialize | nothing → one serial, derived from the request with no stored counter, on a GS1 AI(21)-safe alphabet. Free and transparent: the response states, in the response, exactly what the serial discloses | pure · local | — |
| pins | print the pinned artifact and data-source-manifest digests | pure · local | — |
| version | package, engine-core and pin-set versions | pure · local | — |
| mcp | the agent door on stdio: the tarball's verbs as MCP tools, generated from the same table, readOnly-hinted | — | — |
And five that do not.
The rest of the closed set is designed and not built. It is listed here, in its own table, in the future tense, each row naming the blocker that has to close — because a design listed beside a working verb with nothing to tell them apart is the way this page was wrong until 2026-08-05.
Every row below returned 404 · no door at that path from a terminal on 2026-08-05, and none of them is in the tarball. The build asserts both.
| verb | what it will do | build state |
|---|---|---|
| label | a template + the fields it accepts → a print-ready artifact: PNG, SVG, PDF and ZPL. The meter will be the template, never the call — open templates keep the free loop completable; the curated library and the regulated packs are the thing that is bought | not built · P0-29 |
| enrich | canonical id → attributes, every attribute inside the provenance envelope: source, license class, fetched-at, source digest | not built · P0-2 |
| bridge | SKU / ASIN / vendor code / free text → GTIN candidates with confidence, plus the evidence for each candidate | not built · P0-3 |
| hierarchy | GTIN → packaging-level graph (each / inner / case / pallet); inference always labelled as inference | not built · P0-3 |
| party | GLN / GS1 company prefix → party: licensee name, issuing MO, address grain where licensed | not built · P0-3 |
The other half: the thing that has no barcode.
Every argument about barcodes assumes there is one. Walk the building and most of what moves has no identifier at all — totes, bins, returnable crates, a pallet built forty minutes ago, a fixed asset, a dock door, a rack position, a form that exists only as a PDF. They are counted from memory and reconciled by argument, and no amount of retail standards work touches them.
serialize gives one of them a number, and it needs nobody's permission — no licence to buy, no membership to join, no vendor to schedule, no key, no account. That is why this is the act that comes first for assets, locations and returnables, and it is why it is free. One curl, and the number is yours.
The identifier is derived from the request, not handed out by a counter, so there is no range to run out of and nothing to reserve. Reserved ranges and serials that reveal nothing are a different, paid class — and the difference is stated here rather than discovered later.
Getting that number onto a printed tag is not yet this door's. Templates and print-ready output — PNG, SVG, PDF and ZPL for the printers you already own — are designed, gated on P0-29, and will ship as the label verb in the table above. Until they do, nothing here renders a tag, and this section stands on the call that is live.
$ curl -sX POST https://api.barcoding.dev/serialize
{ "serial": "Gs21hefO3IX74Lfk",
"profile": "T1 compact",
"alphabet": "tsp-1",
"decodes_to": { "issued_at": "2026-08-05T11:12:52.000Z",
"entropy_bits": 48 },
"discloses": [ "issuance time (1 s precision)" ],
"honesty": "This serial decodes publicly to: issuance time
(1 s precision). A collection of our serials therefore
reveals issuance volume and cadence, business hours and
timezone. That is the disclosed cost of free + stateless
+ transparent. If serials must reveal nothing, that is
the opaque class." }
A real response from that address, run on 2026‑08‑05. The disclosure is a field in the response, not a line in a policy page — a free serial anyone can take apart is legible to anyone, including whoever is counting yours, and the honest place to say so is where the serial is handed over. No key, no account.
The provenance envelope.
Per attribute, not per record — because real records are mosaics: nutrition may be public-domain USDA while the category is ODbL Open Facts. The license class travels with the value, so a downstream agent can filter by what it is allowed to do with each field.
{
"attribute": "servingSize",
"value": "30 g",
"source": {
"id": "usda-fdc",
"record": "https://fdc.nal.usda.gov/…",
"dataset_version": "FDC 2026-04 full download",
"digest": "sha256:…"
},
"license": "public-domain",
"fetched_at": "2026-07-31T18:20:11Z",
"confidence": 1.0
}
The license enum is closed — public-domain · open-attribution · share-alike-odbl · gs1-licensed-display-only · commercial-restricted — and enforced at assembly time, not disclaimed in a footer.
The barcode registry. GS1 is the largest family in it, not the boundary.
One pinned registry file per identifier family: grammar, check-digit algorithm, canonicalization, barcode-type bindings, enrichment sources with license class, and the join rule into events and transactions where one exists. AccessGUDID proved the pattern at government scale — one endpoint, three issuing agencies' grammars; this registry generalizes it.
| scheme | validation rail | enrichment rail | license class |
|---|---|---|---|
| GTIN / GLN / SSCC | GS1 GenSpecs mod-10, prefix ranges | food: USDA FDC (CC0) + Open Facts (ODbL, segregated) | public-domain · share-alike-odbl |
| VIN | 49 CFR 565.15 — transliteration, weights, mod-11 | NHTSA vPIC (public domain); auto.dev as the owned rail | public-domain · first-party |
| UDI (GS1 / HIBCC / ICCBBA) | FDA UDI formats doc | AccessGUDID / openFDA (CC0; GMDN fields stripped) | public-domain |
| NDC | structural (no check digit) | openFDA drug/ndc, updated daily (CC0) | public-domain |
| ISBN (978/979 — a valid GTIN-13) | free ISBN Users' Manual | routed to book enrichment | open · confirm-before-redistribution |
| USPS IMpb | Pub 199 MOD10, worked example public | identity open; tracking status per-caller | identity: open · status: per-caller |
| DL/ID PDF417 (AAMVA) | parse and verify ONLY | none — statutory | no_retention, hard flag |
Seven laws, not seven features.
The parts that cannot be renegotiated later. The incumbents in this category were walled, dead, shallow, or unlicensed; every law below is the structural answer to one of those four failure modes.
-
01
No bare value anywhere.
The live verbs keep this already: a resolve answer states computed from the digits · nothing was looked up rather than handing back a bare string. It is also the law enrich is being built to (not built yet — P0-2): every attribute it returns will travel inside a provenance envelope — source, license class, fetched-at, source digest — the license class a closed enum, enforced rather than descriptive, so a response assembled for storage cannot contain display-only attributes.
-
02
Identity is free. It stays free.
resolve, verify and generate are pure and local — no network, no account, no key. The validation layer of the physical world is public regulation and royalty-free standards, and this door keeps it that way.
-
03
License-clean rails only.
The enrichment corpus is built exclusively on rails whose licenses permit it: USDA FoodData Central (CC0), openFDA (CC0), NHTSA vPIC (public domain), the Open Facts family (ODbL, as a segregated source layer). No scraped aggregator, ever — their upstream rights are unverifiable and their downstream rights are contractually zero.
-
04
Sources are pinned like schemas.
Each source has a manifest recording the license text URL, retrieval date and the sha256 of the license text itself — an upstream license change is a failed pin, not a silent drift. pins prints the digests.
-
05
Conformant to the standard; explicit about everything beyond it.
For GS1 keys the /01/* door serves the GS1-Conformant Resolver 1.2.0 surface — RFC 9264 linksets, the description file at /.well-known/gs1resolver — and it is a delegating door: resolution itself is id.org.ai's, served here. For every other identifier family it is the same contract extended, and labeled as such.
-
06
One door for people and for agents.
The same closed verb set over MCP; resolve and verify ship readOnlyHint: true with complete tool descriptions, so an agent's first value moment fires inside already-granted authority. Refusals are honest tool errors, never fabricated answers.
-
07
Joined, not adjacent.
A serialized 2D scan constructs a valid EPCIS 2.0 ObjectEvent, validated against epcis.dev's pinned schema before it leaves the function; its bizTransactionList carries the PO/ASN context via transactions.dev; the attested observer rides in id.org.ai terms. Identity → event → transaction, one scan wide.
What enrich will be, stated plainly.
It is not built — P0-2, and it returned 404 on 2026-08-05. The identity half beneath it is: resolve, verify and generate work on every GTIN on earth today, because the standards layer is royalty-free and the check-digit universe is public regulation. What is being designed on top is deep and clean in four verticals — food, medical devices, drugs, vehicles — and identity-grade everywhere.
It will not be a 500-million-row general-merchandise lookup. The general-merchandise long tail is licensed territory — display-scoped and membership-gated — and this API names it as such rather than papering over it with scraped data. That is the shape of the thing: serve what is free and clear by construction, mark what is gated as gated. The live verbs are already built that way, and this page's own gated table is that rule applied to itself.
The two flagship chains: UPC → nutrition facts on wholly public rails, and VIN → vehicle, with live listing chain on the family's own auto.dev. One per family; both worked examples on the blog.
Before you believe any of it, go check one thing.
The record layer the other side of the fork lands in is EPCIS 2.0 — GS1's, published, free to read, and worth ten minutes of anybody's afternoon. It defines what, when, where, why and how. It carries no dimension for the person or the agent who did it; a company appears, as a party running a step, and the individual does not.
Whether that gap matters to your week is entirely your call, and plenty of readers will decide it does not. The point is that you can settle it without asking us anything — and a claim you can close by reading a public specification is worth more than one we assert here.
Then close the other half the same way. translate, validate and hash answer at epcis.dev with no key and no account: XML in, conformant JSON out; a document validated against the pinned official 2.0.1 schema with its sha256 printed in the response; the CBV 2.0 §8.9 event hash. Three curls, no signup, and the answer is the artifact rather than our description of it.
Verified from a terminal on 2026‑08‑05: POST epcis.dev/hash returned a ni:///sha-256;…?ver=CBV2.0 hash; POST epcis.dev/validate returned valid: true against epcis-json-schema.json 2.0.1, sha256 0f46ff69…; POST epcis.dev/translate turned an EPCIS 2.0 XML document into its JSON-LD twin, and refused a JSON body with a typed 415 naming the two media types it takes.
Get an API key.
You do not need one to start. decode, resolve, verify, generate and serialize are pure and local — no key, no account, and the fork above runs entirely inside them. A key will open the network half — enrich, bridge, hierarchy and party — and that half is not built: it is the gated table above, and every one of those four returned 404 on 2026-08-05. Everything this door can do today, it does without a key.
Say what arrives at your door and we email you when your key is ready. Agents are first-class here: read the offer, take a key, run the free half, no human in the loop.
Access is provisioned from this list. We use your address to tell you when yours is ready and to follow up about it; you can tell us to stop at any time. We don’t sell it or hand it to advertisers. If we stop working on this, we say so rather than go quiet.