All articles
Guidetechnical8 min read

How to Test a GS1 Digital Link Resolver for Conformance

A GS1 Digital Link resolver either behaves the way the standard says, or it doesn't. The gap between "our resolver works" and "our resolver is conformant" is where most self-built implementations quietly sit, and you usually find out when a retailer's system does something your redirect didn't expect.

This is a practical checklist. Every example below is a real request you can run against a live conformant resolver right now, so you can compare the output to your own.

Why conformance matters more than it sounds

A resolver that mostly works is fine until it isn't. The failure mode is specific: your QR codes are already printed, already on shelves, already in warehouses. They all point at one domain. If that domain answers requests incorrectly, you cannot fix it by reprinting.

The parties that care are not consumers. They're retailer systems, supply chain software, and standards bodies, all of which follow the specification rather than guessing. Consumers scanning with a phone camera get a redirect and never notice the difference. Automated systems notice immediately.

What a conformant resolver has to get right

1. The /.well-known/gs1resolver descriptor

Every conformant resolver publishes a machine-readable description of itself at a fixed path. This is how other systems discover what your resolver supports without trial and error.

curl https://id.sunriseqr.com/.well-known/gs1resolver

The response is JSON describing the resolver:

{
  "name": "SunriseQR Resolver",
  "operatorName": "SunriseQR",
  "resolverRoot": "https://id.sunriseqr.com",
  "supportedPrimaryKeys": ["01"],
  "supportsLinkset": true,
  "supportsLanguageNegotiation": true,
  "conformanceLevel": 1
}

Test yours: the endpoint must return HTTP 200 with Content-Type: application/json. If it 404s, nothing downstream can discover your capabilities.

2. URI parsing across all four layers

A Digital Link URI is not a flat string. It has structure, and a conformant parser has to handle each layer:

  /01/07501234567893/10/BATCH-A1?17=261231
   |        |         |     |      |
   AI   Primary     Qualifier   Data Attribute
        Value       (batch)    (expiry date)

   <-- Layer 2 -->  <Layer 3>  <-- Layer 4 -->

01 is the Application Identifier for GTIN. 10 is a batch or lot qualifier. 17 is an expiry date passed as a query parameter. A resolver that only handles /01/{gtin} and chokes on qualifiers will fail the moment a traceability use case shows up.

3. GTIN validation, including the check digit

GTINs must be 8, 12, 13, or 14 digits with a valid check digit. This is where the most important distinction in resolver behavior lives.

A malformed GTIN is a client error (400):

curl https://id.sunriseqr.com/01/12345678901234
{
  "error": "Invalid GTIN",
  "detail": "Check digit failed for \"12345678901234\" — expected 1, got 4"
}

A well-formed GTIN that isn't in the catalog is a 404:

curl https://id.sunriseqr.com/01/00000000000000
HTTP/2 404

Test yours: send both. If your resolver returns the same status for a mangled number and an unknown-but-valid product, it's collapsing two genuinely different conditions. Automated clients use that distinction to decide whether to retry, report a data error, or move on.

4. Unsupported identifiers must fail clearly

Not every resolver supports every Application Identifier. That's allowed. What isn't allowed is failing confusingly.

curl https://id.sunriseqr.com/414/1234567890123
{
  "error": "Unsupported Application Identifier",
  "detail": "AI \"414\" is recognized but not yet supported by this resolver"
}

Test yours: a recognized-but-unsupported AI should say so, not return a generic 500 or a redirect to your homepage. The descriptor's supportedPrimaryKeys should agree with what the resolver actually does.

5. Content negotiation

The same URI has to return different things depending on what the client asks for. This is the requirement most homegrown resolvers skip, because a phone camera never exercises it.

RequestExpected response
No special headersHTTP redirect to the default link
Accept: application/linkset+jsonFull linkset JSON, not a redirect
?linkType=allFull linkset JSON
?linkType=gs1:pipRedirect to the product information page link

Test yours: request the same GTIN four ways. A resolver that redirects regardless of the Accept header is not conformant, however well it works in a phone camera.

6. The Link header

Even on a plain redirect, the response should carry a Link header advertising the other available link types. This lets a client discover alternatives without a second request.

Test yours: curl -I a plain resolution and check whether Link is present and lists more than the destination you were sent to.

7. Language negotiation

Where multiple links of the same type exist in different languages, the resolver should honour Accept-Language and pick the best match rather than always serving the first row it finds.

The quickest self-assessment

Run these against your own resolver and compare:

  1. GET /.well-known/gs1resolver returns 200 JSON
  2. A bad check digit returns 400, not 404 or 200
  3. An unknown but valid GTIN returns 404, not 400 or 200
  4. An unsupported AI returns a clear 400, not a 500
  5. Accept: application/linkset+json returns JSON, not a redirect
  6. ?linkType=all returns the full linkset
  7. A plain redirect carries a Link header
  8. Accept-Language changes which link is chosen

If any of those surprise you, that's the list to work through.

Why this is an argument for buying rather than building

None of the above is difficult in isolation. The difficulty is that it's a permanent commitment.

GS1 publishes spec updates. Each one means re-reading the standard, adjusting behavior, and re-running conformance testing. Meanwhile the resolver has to stay up, because every printed code depends on it, and there is no rollback that involves reprinting packaging.

That's the real calculation in build versus buy: not whether your team can implement content negotiation, but whether you want to own conformance maintenance and uptime for as long as your codes are in circulation.

SunriseQR's resolver is validated against the official GS1 conformance test suite, the same one retailers and standards bodies use, and we track spec changes so the codes you printed last year keep behaving correctly this year.