GS1 Digital Link

One code on the pack. Every answer behind it.

This is the working handbook: what to set up, in what order, and exactly how the resolver behaves when a pack is scanned. The conformance narrative · what we implement, what we do not, and the standard each claim traces to · lives on the GS1 overview. The resolver is conformant to the GS1-Conformant Resolver Standard 1.2.0, verified with GS1's official test suite, self-asserted · GS1 does not certify resolvers, ours or anyone's.

The model

A GS1 Digital Link puts a plain web URI on the pack instead of a bare barcode. The identifier your products already carry (a GTIN, most commonly) becomes the path of a URL on your domain, and scanning it reaches whatever you have registered for it · a product page, a recall notice, instructions, a per-unit story.

https://qr.yourbrand.com/01/09506000134352/10/LOT4471/21/0042?17=261231
your domain (or https://tap2u.link) primary key · GTIN, AI 01 lot qualifier · AI 10 serial qualifier · AI 21 data attributes · here expiry, AI 17

Five things exist in the system, and everything on this page is one of them:

Licenceyour GS1 Company Prefix Itemthe product master, one per key Linksetdestinations, by type and scope Carrierthe printable QR symbol Serialsper-unit codes, derived

The catalogue is a product master-data registry your workspace owns and enters or imports itself · nothing is fetched from a GS1-hosted database for display. Manage it in Dashboard → GS1, over the REST API, or through MCP.

Primary keys

All 16 primary identification keys the Digital Link syntax defines are supported, each with every qualifier the standard gives it · the standard permits supporting a subset of keys but not a subset of a key. Every value is validated before anything is stored or printed: check digits, issued lengths (a GTIN is only ever 8, 12, 13 or 14 digits), and qualifier grammar.

AIKeyNameQualifier AIsCheck
00SSCCSerial Shipping Container Code· mod-10
01GTINGlobal Trade Item Number 22, 10, 21 or 235 (exclusive)mod-10
253GDTIGlobal Document Type Identifier· mod-10
255GCNGlobal Coupon Number· mod-10
401GINCGlobal Identification Number for Consignment· none
402GSINGlobal Shipment Identification Number· mod-10
414GLNGlobal Location Number (physical location) 254 or 7040 (exclusive)mod-10
415GLN (pay to)GLN of the invoicing party 8020(required)mod-10
417Party GLNParty Global Location Number 7040 (exclusive)mod-10
8003GRAIGlobal Returnable Asset Identifier· mod-10
8004GIAIGlobal Individual Asset Identifier 7040 (exclusive)none
8006ITIPIndividual Trade Item Piece 22, 10, 21mod-10
8010CPIDComponent / Part Identifier 8011none
8013GMNGlobal Model Number· char pair
8017GSRN-PGlobal Service Relation Number (provider) 8019mod-10
8018GSRNGlobal Service Relation Number (recipient) 8019mod-10
  • An exclusive qualifier forms a distinct identifier with its key (GTIN + 235 is a UPUI, GLN + 7040 is an FID) and never combines with the key's other qualifiers.
  • AI 415's qualifier is mandatory: a bare /415/{gln} is not a legal Digital Link URI and is answered with 400, per URI Syntax 1.6 §8.5.
  • GTIN-8/12/13 are accepted and stored canonically at 14 digits, and the length you issued at is remembered · it decides where your company prefix sits inside the number.
  • Any of the GS1 Syntax Dictionary's application identifiers can be attached to an item as master data; each is validated against its published format and encoded into the printed URI's query string.

Licences: who may publish a key

A Digital Link asserts that the identifier inside it was licensed to the publisher by GS1. No resolver standard checks that, so this platform does: you register the GS1 Company Prefix you licensed in Dashboard → GS1 → Licences, and every publishing action, on every origin · the shared host and your own custom domain alike · checks the key against your prefixes first. Owning a domain is not permission to print someone else's GTIN on it.

  • Attested vs verified. A licence is attested: your declaration, stored with the user, IP and timestamp · which is what we would produce in a dispute. With a GS1 registry connection configured it can be verified against the registry instead. The status is always shown honestly next to every item.
  • Restricted ranges are refused outright: store-internal prefixes (02x/04x/2xx), coupons, refund receipts and Bookland ISBNs identify nothing outside one retailer and are the most common self-serve mistake.
  • Prefixes are checked for overlap, not equality. Two workspaces cannot hold prefixes where one would swallow the other's key space.
  • On the shared host (https://tap2u.link) a key also needs a claim: exactly one workspace can own a given identifier there, first come first served among licensed workspaces. On your own custom domain the hostname itself is the tenant signal and no claim is needed · that is the production path for GS1 anyway.
  • A draft claims nothing. Claiming on create would let anyone lock a competitor out of an identifier without ever publishing.
  • Adding a licence is a legal declaration and is dashboard-only: it is deliberately not exposed to API keys or MCP agents, because it must record a real human user.

The linkset: what a scan finds

Each item carries a linkset: destinations typed with the GS1 Web vocabulary (57 selectable link types · gs1:activityIdeas, gs1:allergenInfo, gs1:appDownload, gs1:backgroundInfo, gs1:careersInfo, gs1:certificationInfo, …) and scoped to a qualifier level. A recall notice can be pinned to one lot; a warranty page to one serial; the product page to the key itself.

When a scan carries a qualifier no link is registered for, resolution walks up · never into a dead end:

/01/…/10/LOT4471/21/0042 the exact unit · matched first if a serial-scoped link exists
/01/…/10/LOT4471 the lot · where a recall notice would sit
/01/09506000134352 the key · always has a default link, enforced on every write
  • Every item always has a key-level default link · a linkset without one is rejected at write time, so a plain scan always has somewhere to go.
  • Language and format negotiation run on every scan: register the same link type with different hreflang values and a French shopper's phone gets the French page, with no app involved.
  • Editing the linkset is free, forever. Re-pointing a printed code is the whole product.
  • Machine access: any client asking with Accept: application/linkset+json (or ?linkType=linkset) gets the linkset as RFC 9264 JSON instead of a redirect.

The carrier: a symbol that survives printing

The carrier generator produces the QR symbol for an item (or any qualifier scope of it) to GS1's print guidance, and reports physical facts · because the artwork goes to a press, not a screen.

4 modulesquiet zone, all four sides, measured by test
ECC Mdefault error correction · Q and H available, L never offered
0.495 mmtarget X-dimension (0.396–0.99 mm range) per retail-POS guidance
20.3 mmwhat a 33-module symbol prints at · every symbol reports its own size
  • No logo overlay, ever. The GS1 carrier generator has no image path at all: a logo spends the error-correction budget a symbol needs to survive a conveyor and a scuffed pack. (The ordinary QR studio is a separate path and allows logos.)
  • It is a plain QR Code (]Q1), not a GS1 QR Code (]Q3) · a Digital Link URI is a URI; the FNC1/element-string symbology is a different job we do not claim.
  • The print sheet sizes each symbol in millimetres, carries the human-readable key under the symbol, and says "print at 100%, do not scale or crop" · the same sheet prints the same physical code on A4 and Letter.
  • Compression (shorter URIs, denser symbols) is available and every compressed URI is decompressed again and compared with its input before it is returned; if the round trip does not hold you get the uncompressed URI · longer, and correct. A few keys (GMN, GRAI, and the AI 235/7040/417 forms) cannot be compressed by GS1's own toolkit and always fall back the same way.

Serials: per-unit codes without a database of units

A serialised run gives every unit its own scannable identity (AI 21). Serials are derived, not stored: no row exists per serial, so a batch of 100,000 costs no storage, a re-export reproduces the identical codes, and a lost CSV never means re-buying a print run.

7F2A00001AK3VP7QZM
batch prefix · 4 chars index · Crockford base32, no I/L/O/U, ~1.07 billion per job authenticator · 40-bit truncated HMAC under the batch seed
  • The HMAC is what makes a serial unguessable, not just reproducible: a counterfeiter who collects real serials still cannot mint one that verifies, and verification compares in constant time.
  • The job id is the batch. Keep the X-Job-Id header from an issuance: re-deriving that batch later is free and yields byte-identical serials.
  • GET /api/v1/gs1/verify?serial=… answers "did we issue this?" by recomputation. What it deliberately is not: an EPCIS event trail · we can say a serial was issued, not where it has been.
  • Caps: 100,000 serials per CSV call on the API, 10,000 through MCP, 500 on a server-rendered print sheet. CSV import of catalogue items caps at 5,000 rows per call.

The resolver: what a scan gets back

Every origin · the shared host and each custom domain · self-describes at /.well-known/gs1resolver and answers per the Resolver Standard:

302The normal scan: redirect to the negotiated destination, with the request's query passed through and a Link header pointing at the machine-readable linkset.
300Several links are equally good: a choice page rather than a coin flip.
400A syntactically invalid URI: bad check digit, wrong-key qualifier, non-issued GTIN length. Malformed is an error, never a silent 404.
404A valid identifier nothing is registered for · and a requested linkType that is not registered (the 1.2.0 rule: never fall back to the default).
JSON?linkType=linkset or Accept: application/linkset+json returns the linkset as RFC 9264 JSON; ?linkType=all from a browser renders it as HTML with JSON-LD embedded.
  • Content negotiation (Accept-Language / Accept) applies to the plain redirect too · a phone camera sends no linkType, and the multilingual story has to work for an actual shopper, not just API clients.
  • Fully and partly compressed Digital Link URIs are decompressed and resolved.
  • Only an active item resolves. Draft and archived items are actively removed from the edge, so status genuinely controls world-visibility.
  • GS1 scans are metered like every other scan (1 credit) and appear in the same analytics.

What it costs

200 creditsper catalogue item, once · the product master every serial inherits
1 creditper serial · the ordinary dynamic-code rate, so a million-unit run stays viable
1 creditper scan, like every scan on the platform
0 creditsediting, re-pointing, exporting, verifying, carriers, re-deriving a batch

The two prices are split deliberately: the item carries the price the incumbents meter on, the serial stays at the volume rate. An updated CSV import row and a jobId re-derive are free · both were paid for at creation, and charging again would bill you 200 credits to fix a typo. Every charge is taken before the write and refunded if the write fails; a short wallet gets 402, never work-now-invoice-later.

Not implemented

Every docs page on this site lists what its feature does not do. For GS1 the list matters more than most, so here it is rather than in a footnote:

Known gaps · the honest list
  • QR only, no Data Matrix. GS1 permits a Digital Link in Data Matrix and healthcare largely uses it.
  • No EPCIS event trail. Verify answers "was this issued", not "where has it been".
  • No ISO 15415 grading. You get the geometry and print facts, not a graded score for a printed sample.
  • Context-based link selection is not implemented. ?context= is consumed per the standard but does not influence which link is chosen.
  • Compression skips some keys. GMN, GRAI and the AI 235/7040/417 forms fall back to the uncompressed URI · a gap in GS1's own reference toolkit, not a wrong symbol.
  • No certification exists to claim. Conformance is self-declared via the description file plus GS1's public test suite, which we pass · the same is true of every resolver you have ever read about.

Start to finish

  1. Declare your GS1 Company Prefix in GS1 → Licences. One attestation covers every key under the prefix, and a licence added later lights up every existing item it covers.
  2. Create the item (200 credits, once) with its key, name and default URL · in the dashboard, by API, by CSV import, or by an MCP agent. Keep it a draft until it is real; drafts resolve nowhere and claim nothing.
  3. Build the linkset: typed destinations per qualifier scope, languages via hreflang. Free, editable forever.
  4. Print the carrier from the item's QR tab or carrier.svg: pick the domain it resolves on, check the millimetre size it reports, hand the SVG to the press at 100%.
  5. Serialise if units matter (1 credit per serial): a CSV stream of per-unit URIs plus element strings. Keep the job id · the batch re-derives free.
  6. Watch it live: scans land in analytics and the live feed like every other code, and /api/v1/gs1/verify answers authenticity questions from the field.