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.
Five things exist in the system, and everything on this page is one of them:
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.
| AI | Key | Name | Qualifier AIs | Check |
|---|---|---|---|---|
| 00 | SSCC | Serial Shipping Container Code | · | mod-10 |
| 01 | GTIN | Global Trade Item Number | 22, 10, 21 or 235 (exclusive) | mod-10 |
| 253 | GDTI | Global Document Type Identifier | · | mod-10 |
| 255 | GCN | Global Coupon Number | · | mod-10 |
| 401 | GINC | Global Identification Number for Consignment | · | none |
| 402 | GSIN | Global Shipment Identification Number | · | mod-10 |
| 414 | GLN | Global Location Number (physical location) | 254 or 7040 (exclusive) | mod-10 |
| 415 | GLN (pay to) | GLN of the invoicing party | 8020(required) | mod-10 |
| 417 | Party GLN | Party Global Location Number | 7040 (exclusive) | mod-10 |
| 8003 | GRAI | Global Returnable Asset Identifier | · | mod-10 |
| 8004 | GIAI | Global Individual Asset Identifier | 7040 (exclusive) | none |
| 8006 | ITIP | Individual Trade Item Piece | 22, 10, 21 | mod-10 |
| 8010 | CPID | Component / Part Identifier | 8011 | none |
| 8013 | GMN | Global Model Number | · | char pair |
| 8017 | GSRN-P | Global Service Relation Number (provider) | 8019 | mod-10 |
| 8018 | GSRN | Global Service Relation Number (recipient) | 8019 | mod-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:
- 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
hreflangvalues 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.
- 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.
- 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-Idheader 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:
| 302 | The normal scan: redirect to the negotiated destination, with the request's query passed through and a Link header pointing at the machine-readable linkset. |
| 300 | Several links are equally good: a choice page rather than a coin flip. |
| 400 | A syntactically invalid URI: bad check digit, wrong-key qualifier, non-issued GTIN length. Malformed is an error, never a silent 404. |
| 404 | A 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
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:
- 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
- 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.
- 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.
- Build the linkset: typed destinations per qualifier scope,
languages via
hreflang. Free, editable forever. - 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%. - 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.
- Watch it live: scans land in analytics and the live feed like every other code, and
/api/v1/gs1/verifyanswers authenticity questions from the field.