GS1 Digital Link and resolver
The link on every QR code, and how the resolver sends browsers to the public page and machines to the public JSON.
Every passport has one web address that identifies the battery: a GS1 Digital Link. It is what the QR code on the battery carries. The same address serves people and machines: the resolver behind it decides what to return from the request.
The link
https://batterypass.example.com/01/04012345000023/21/SV2027-000184| Part | Meaning |
|---|---|
https://batterypass.example.com | The resolver's origin: your BatteryPass address, or a separate resolver hostname your operator configured |
/01/04012345000023 | GS1 application identifier 01: the model's GTIN, always with 14 digits |
/21/SV2027-000184 | GS1 application identifier 21: the unit's serial number; a / in a serial number is sent percent-encoded (%2F) |
The link is in gs1_digital_link of getPassport and is fixed
once the identifiers are locked. getPassportQr renders
exactly this link as PNG or SVG (error correction level M, a quiet zone of four modules). Always take the link
from the API rather than building it yourself.
What the resolver returns
The resolver lives at the root of the origin (/01/…/21/…), not under /api/v1, and needs no key.
| Request | Answer |
|---|---|
A browser (Accept: text/html, or no JSON preference) | 307 to the public passport page /pass/{passportId} |
?linkType=describedby (or gs1:describedby) | 307 to the public JSON /api/v1/public/passports/{passportId} |
?linkType=all, or Accept: application/json, application/linkset+json or application/ld+json without linkType | 200 with a linkset (application/linkset+json) |
| An unknown GTIN and serial number | 404: a short page for browsers, a Problem not_found for JSON clients |
| Too many requests from one address | 429 rate_limited with Retry-After |
Every answer for a known passport also carries the links in a Link header, with the relations
https://gs1.org/voc/pip (the page), describedby (the public JSON) and linkset. Answers are cacheable for
five minutes and vary by Accept.
A passport whose identifiers are locked but that is not published yet, or one that was withdrawn, still resolves: its page explains the state, and the public JSON returns the status without content.
Following the link from code
# The public JSON of the passport behind a QR code
curl -sL "https://batterypass.example.com/01/04012345000023/21/SV2027-000184?linkType=describedby"
# The linkset
curl -s -H "Accept: application/linkset+json" \
"https://batterypass.example.com/01/04012345000023/21/SV2027-000184"The linkset names the page and the public JSON:
{
"linkset": [
{
"anchor": "https://batterypass.example.com/01/04012345000023/21/SV2027-000184",
"https://gs1.org/voc/pip": [
{ "href": "https://batterypass.example.com/pass/0192f0c4-7b1e-7c3a-9d2e-5a8b6c4d3e21", "type": "text/html", "title": "Battery passport" }
],
"describedby": [
{ "href": "https://batterypass.example.com/api/v1/public/passports/0192f0c4-7b1e-7c3a-9d2e-5a8b6c4d3e21", "type": "application/json", "title": "Public passport (JSON)" }
]
}
]
}If you already know GTIN and serial number, you can skip the resolver and call
getPublicPassportByProductId with
{gtin}:{serial}, for example 04012345000023:SV2027-000184. The GTIN may have 8, 12, 13 or 14 digits there.
The resolver, the public passport reads and the public documents share one limit of 300 requests per minute per client address. A scanner app that resolves many codes should respect the cache headers; public passport reads may be cached for five minutes.
Related pages
Condition and lifecycle
How measured condition data reaches a passport without a review step, how the re-publication policy works, and what lifecycle events record.
Signatures
What BatteryPass signs in each passport version, how the hash and the Ed25519 signature are computed, and how to check them with the published keys.