Physical identification, QR and NFC

Choose the physical carrier for your products, learn the exact form of the GS1 Digital Link, and know what each carrier requires before you go into production.

On this page

A physical product has to carry something a phone can read. This page gives you the two carriers available, the printed QR and the NFC seal. When you leave it, you will know which exact address is printed or written into the chip, what the server answers when you read it, what each carrier brings at scan time, and the work each one requires before you launch a production run.

The ESPR regulation allows the passport at three levels: the model, the batch and the item. The delegated act for your product group decides which one applies. The two address forms described below cover the model and the item. A model passport covers every item that shares the same product code.

Here is the full path, from the physical carrier to the page the person scanning sees. The rest of this page covers each step in detail.

The path of a scan, from the physical carrier to the public page

Two separate columns, one per carrier, joining at the bottom. On the left, the QR printed on the label carries a fixed address, always the same one, sealtrust.io/p/0ABCDEFGHJKM, or its GS1 form sealtrust.io/01/{gtin}/21/{serial}. The resolver normalizes the number, finds the unit, then answers with a single 302 redirect: to the product page by default, to the passport page with the linkType=dpp parameter. On the right, the NFC seal, an NTAG 424 DNA chip, fills in the sealtrust.io/verify address itself with the e and c parameters, and that address changes on every read. The /verify page then calls GET /sdm/verify-url, the chip signature and its counter are checked, then the page shows a verdict followed by the record: product, brand, history, read counter. Both columns end at a public web page on the site, with no app to install, which answers the questions a scan raises and points to the passport. The QR identifies the object, the NFC seal adds a signature computed by the chip, different on every read.

#The three possible combinations

Every product carries an authentication method value. The product stores one of three.

ValueWhat the product carries
qrA printed code alone.
nfcAn NFC seal alone.
nfc+qrBoth on the same product.

You choose this value when you create the product, in the console form or in its CSV import. Always fill it in. A missing value counts as nfc. The server then refuses the creation with a 403 if your plan only allows QR. For a QR-only product, write qr explicitly.

The server also accepts the spelling nfc,qr and uppercase. Write exactly one of the three values in the table, in one of these spellings. Any other value does not give you the carrier you are asking for.

Asking for a value that contains nfc requires your plan to allow NFC authentication. Otherwise the server answers a 403 carrying the code AUTH_METHOD_NOT_ALLOWED, with the method requested and the list of the ones your plan allows. A qr request alone does not require that permission.

#The printed QR

#What it encodes

The QR printed on a unit encodes a single address, in this form:

Texte
https://sealtrust.io/p/0ABCDEFGHJKM

The last segment is the unit's public serial number. This form /p/{serial} is the unique product identifier intended for the European registry, and it is the one the label carries. Code generation refuses to produce an image for a unit that has no serial number.

#The serial number

The number is twelve characters long. Its alphabet has thirty-two characters: the digits 0 to 9 and the letters A to Z minus I, L, O and U. We remove those four letters because they are confused with digits on a label.

Three practical consequences:

  • The server corrects a number copied by hand. It reads an I and an L as a 1, and an O as a 0.
  • Case does not matter. The server uppercases the number before any lookup.
  • A number that is not the right length, or that contains a character outside this alphabet, gets an immediate 404.

We draw the number at random. It does not follow production order and therefore reveals neither your volumes nor how long you have been operating.

#What happens on a scan

A phone camera opens the address. The server answers with a single 302 redirect to the product page, on the public site.

curl
curl -sS -i "https://sealtrust.io/p/0ABCDEFGHJKM"
HTTP
HTTP/1.1 302 Found
Location: https://sealtrust.io/fr/product/0ABCDEFGHJKM

The server picks the page language from the reader's Accept-Language header, between French and English, French by default. There is never more than one redirect hop.

The product page answers the questions a person asks when scanning: what the object is, whether it is genuine, who holds it, where the proof is. It points to the digital passport.

#Requesting the passport with the linkType parameter

A machine reader that wants the document itself adds a linkType parameter.

curl
curl -sS -i "https://sealtrust.io/p/0ABCDEFGHJKM?linkType=dpp"
HTTP
HTTP/1.1 302 Found
Location: https://sealtrust.io/fr/passport/<identifiant de l'unité>

The last segment is the unit's hash, failing that its token id, failing that its serial number.

Five values are recognized: dpp, passport, gs1:dpp, gs1:digitalproductpassport and digitalproductpassport. Any other value is treated as if the parameter were absent, and the reader lands on the product page.

A unit withdrawn from circulation always goes to its passport, even without this parameter. Its identifier keeps answering, which the standard requires.

#The exact form

The same product is addressable through a GS1 Digital Link. Its form is set by the path structure.

Texte
https://sealtrust.io/01/03701234567890/21/0ABCDEFGHJKM
  • 01 is the application identifier for the GTIN, the international article number.
  • 03701234567890 is the GTIN normalized to fourteen digits.
  • 21 is the application identifier for the serial number.
  • 0ABCDEFGHJKM is the same public serial number as in the short form.

Both forms designate the same unit and lead to the same page. The domain does not make the GS1 Digital Link, the path structure does. A resolver hosted on your own domain stays compliant.

Serving these resolvers on your own domain name is part of the SealTrust Inside plan. Without it, your links stay on sealtrust.io.

#What the server checks

curl
curl -sS -i "https://sealtrust.io/01/03701234567890/21/0ABCDEFGHJKM"
HTTP
HTTP/1.1 302 Found
Location: https://sealtrust.io/fr/product/0ABCDEFGHJKM

The GTIN in the path must match the GTIN of the model the unit belongs to. Otherwise the server answers a 404. A unit whose model carries no GTIN is therefore not addressable through this form, and stays addressable through the short form /p/{serial}.

A GTIN that cannot be normalized gets a 400 with the message Invalid GTIN. The server keeps the digits of the segment, refuses anything beyond fourteen, and left-pads with zeros up to fourteen. The GTIN-8, GTIN-12, GTIN-13 and GTIN-14 forms therefore pass, with or without separators.

It then checks the GS1 check digit, and answers 400 with the message Invalid GTIN: the check digit does not match. when the last digit does not match the ones before it. This check happens at resolution time, and no longer only when the model is recorded: a miscopied code is therefore named as such, instead of answering "unknown product" and sending the reader to look in the wrong place.

Every other dead end gets the same 404 with the same message. An unknown number and a number known under another GTIN are indistinguishable from the outside.

#The form without a serial number

A link without the 21 segment designates a commercial model. It designates no particular unit.

curl
curl -sS -i "https://sealtrust.io/01/03701234567890"
HTTP
HTTP/1.1 302 Found
Location: https://sealtrust.io/fr/passport/01/03701234567890

It resolves to the model-level passport, the one attached to a model and to no unit. The server never serves a passport attached to a unit here: the data of one item must not be shown for another item of the same model.

The server answers a 404 in four cases, without distinguishing them: the GTIN is unknown, no public model passport is published for that GTIN, several brands publish a model passport under the same GTIN, or the GTIN belongs to a brand other than the one that owns the domain the scan arrives on.

This is the form that suits anything sold by the meter, by the box or by the pallet. A single code, printed on every unit of the model, and nothing created per item.

The batch level has no address form of its own. A passport attaches either to a model or to an item. The printed code is therefore the model's or the item's.

#The fifty-character limit

The European registry caps the unique product identifier at fifty characters. The GS1 structure consumes twenty-two on its own, on top of the domain: /01/, fourteen GTIN digits, /21/. The domain you choose therefore decides how many characters are left for the serial number.

Resolver domainCharacters left for the number
https://id.gs1.org10
https://sealtrust.io8
https://api.sealtrust.io4

Take the shortest domain you have. An api. subdomain is a trap. The identifier intended for the registry is the short form /p/{serial}, which does not spend those twenty-two characters.

The published passport carries both values: the normalized GTIN in the gtin field, and the full link in the gs1_digital_link field.

#What QR alone gives you

  • It carries the identifier intended for the registry. The /p/{serial} form is the unique product identifier the European registry expects. The field that must serve the document carries the same address followed by ?linkType=dpp.
  • It works at model level. A single code for a whole model, with nothing created per item, using the /01/{gtin} form. That level serves the passport and can be scanned.
  • It requires no hardware. The code is an image you download and send to print.
  • It leads to an ordinary web page. No app is needed to read it.

What it does not do: a printed QR encodes a fixed address. A photograph of the label encodes the same address. The resolver looks up the serial number and redirects; it has nothing else to examine. The QR therefore identifies an object, and does not by itself prove that the label has not been copied.

#What the NFC seal adds

The NFC seal is an NTAG 424 DNA chip. On every read, it writes two parameters into the address itself: an encrypted piece of data and a verification seal. The address therefore changes on every read.

Texte
https://sealtrust.io/verify?e=<donnée chiffrée>&c=<sceau>

The e parameter is thirty-two hexadecimal characters long. The c parameter is sixteen. A phone brought near the chip opens that already-completed address. The /verify page then queries the API verification endpoint, on api.sealtrust.io.

On the SealTrust Inside plan, we burn your own domain name into the chip, in place of sealtrust.io. That choice freezes at the first encoding: once a chip is burned, the server refuses any domain change with a 409, because chips already applied would point nowhere.

Three things follow.

An address already used is refused. The chip increments a counter on every read. The server requires that counter to advance strictly. An address captured then replayed gets a 409. A signature that does not match gets a 403 with the code SDM_MAC_MISMATCH. Malformed parameters get a 422.

The opening seal, on chips that carry one. Some chips embed a wire that breaks when the package is opened. They then add a two-character t parameter, placed between e and c. The response exposes three fields:

FieldContent
tamper_statusThe two raw characters: CC never opened, OO opened, OC opened then closed again, II function never activated.
seal_intacttrue never opened, false opened, null nothing to say.
tamper_policyWhat your brand wants done with it.

The seal_intact field has three states. The value null does not mean broken: an ordinary chip and a chip whose function was never activated both give null. Showing it as an alert would put a counterfeit warning on genuine products.

A richer response than the QR redirect. Reading a chip returns a document directly, with the product name, the brand, the token id, the contract address, the history, the counter value, a declared_stolen flag if a theft report is open on the unit, and a scan area at city granularity when the location is provided.

#What each one costs in preparation

#The QR

You request the image when you need it, and the server returns it as PNG. You choose a size between 100 and 1000 pixels. Error correction is level M and the white margin is two modules. Keep that margin when printing, otherwise reading becomes unreliable.

You can download the code for a product, the code for a certificate, or the ZIP archive of every code in a batch at once.

For a model-level QR, the console refuses to generate the image as long as no public model passport is published for that GTIN. The message is explicit. Printing a code that leads nowhere costs more than waiting.

In short: no hardware, no per-item handling, no procurement lead time.

#The NFC seal

Preparation is of a different nature.

  1. Your plan must include NFC authentication. Otherwise the server refuses product creation with a 403.
  2. You source the chips. They are physical components, with a lead time and a cost per item.
  3. Every chip passes in front of a reader before the product is created. We read its hardware identifier, you associate it with a line of your batch, you create the products, we encode the chips. It is a four-step path, and it happens chip by chip.
  4. We write the address into the chip at encoding time. You cannot change it remotely afterwards. A badly encoded chip is a chip to replace physically.

On an NFC batch, you can produce a printed QR in parallel for the same units. The reverse does not work: a chip address printed into a QR does not work, because it is the chip that fills in the e and c parameters, and a printed image fills in nothing.

#Choosing

QuestionQR aloneNFC seal
/p/{serial} identifier intended for the European registryYesYes
Model level, one code for a whole modelYesNot applicable
Hardware to sourceNoYes
Per-item handlingNoYes
Reading without an appYesYes
A different address on every readNoYes
Replay of a captured address refusedNoYes
Package opening detectionNoOn chips that carry one
Correction possible after manufacturingReprint the labelReplace the chip

One last point about domains. If you serve these resolvers on your own domain name, a link stays on that domain from start to finish, and your customers never see ours. A brand domain resolves only that brand's products: another brand's code scanned on your domain gets a 404.

Your answer opens a pre-filled email in your mail app, addressed to contact@sealtrust.io. You read it over before sending it.

Suggest a correctionReport a problem