# GET /01/{gtin}/21/{serial}

Résoudre un GS1 Digital Link (AI 01 + AI 21) vers la page publique de l'article. Point d'entrée public, sans clef d'API, qui répond par une redirection.

Source : https://docs.sealtrust.io/reference/get-gs1-gtin-serial/

---

Vous transformez un GS1 Digital Link en adresse de la page publique de
l'article. La réponse porte le code 302 et l'adresse de destination dans
l'en-tête `Location`. Le corps de la réponse est vide.

## Autorisation

Aucune, point d'entrée public. Il n'attend ni clef d'API, ni cookie de session,
ni en-tête `Authorization`. Un appel serveur à serveur est accepté.

Appelez ce point d'entrée sur `https://api.sealtrust.io`. La même route répond
aussi sous le préfixe `/v1`, à
`https://api.sealtrust.io/v1/01/{gtin}/21/{serial}`. Les deux adresses appellent
le même code. Pour une intégration serveur nouvelle, utilisez la forme `/v1`. La
forme sans préfixe existe parce que c'est la structure de chemin `/01/…/21/…`
qui fait d'une adresse un GS1 Digital Link, donc c'est elle que rencontre un
lecteur de code.

Le domaine que nous inscrivons dans les liens GS1 que nous produisons est
réglable, et vaut `https://id.gs1.org` par défaut. L'identifiant que nous
déclarons au registre européen des passeports numériques est l'adresse courte
`/p/{serial}`, parce qu'un lien GS1 complet dépasse la limite de 50 caractères
du registre. Le QR code que nous imprimons sur un article encode lui aussi
`/p/{serial}`.

> [!ATTENTION] Appelez ce point d'entrée depuis un serveur
> Un script lancé depuis une page web n'obtient rien d'exploitable ici. Notre
> politique d'origines n'autorise qu'une liste fermée de sites, et nous
> n'exposons aux scripts ni l'en-tête `Location`, ni les en-têtes
> `X-RateLimit-*`. Un scan de QR code ou un clic sur le lien ouvre la page
> normalement : cette politique ne concerne que les appels lancés par un script
> depuis une autre page.

> [!INFO] Les deux codes que porte l'adresse
> `AI 01` est le GTIN, le numéro d'article international à 14 chiffres qui
> désigne une référence commerciale. `AI 21` est le numéro de série, qui désigne
> un exemplaire précis de cette référence. Le couple des deux désigne un objet
> physique unique.
>
> `{serial}` est le numéro de série public à 12 caractères, le même que celui
> porté par l'adresse courte `/p/{serial}`. Ce n'est pas l'identifiant du jeton
> sur la chaîne. Les deux formes désignent le même exemplaire et aboutissent à
> la même page.

Un lien GS1 peut aussi ne porter que le GTIN, sans `AI 21`. Il désigne alors la
référence commerciale et résout vers le passeport du modèle, partagé par tous
les exemplaires qui portent ce même code produit. Voir `GET /01/{gtin}`. Le
règlement ESPR autorise un passeport au niveau du modèle, du lot ou de
l'exemplaire. Nous servons le niveau modèle et le niveau exemplaire. Rien ne
vous oblige à sérialiser chaque unité.

## Plafond d'appels

Ce chemin n'a pas de plafond qui lui soit propre. Il relève du compteur général
de l'API, compté par adresse IP appelante sur une fenêtre de 60 secondes. Ce
compteur est unique pour toutes les routes qui n'ont pas de plafond propre, et
il est unique pour toutes les valeurs de GTIN et de numéro de série. Parcourir
mille liens différents consomme mille appels du même budget.

Toute réponse porte les en-têtes `X-RateLimit-Limit`, `X-RateLimit-Remaining` et
`X-RateLimit-Reset`, ce dernier donnant l'heure de remise à zéro en secondes
depuis le 1er janvier 1970.

Ce point d'entrée ne consomme aucun quota de votre offre.

> [!ATTENTION] Prévoyez le code 429 dans votre client
> `X-RateLimit-Remaining` tombe à 0 dès que vous dépassez le compteur général.
> Ralentissez avant d'y arriver. Un appel au-delà du budget peut recevoir un
> code 429 accompagné d'un en-tête `Retry-After` en secondes : attendez cette
> durée, puis réessayez. La valeur du compteur général peut changer sans que ce
> chemin soit modifié, donc réglez votre client sur les en-têtes de vos réponses
> et n'inscrivez aucun nombre en dur dans votre code.

## Paramètres de chemin et de requête

| Nom | Type | Obligatoire | Description |
| --- | --- | --- | --- |
| `gtin` | `string` | oui | Le GTIN de la référence commerciale. Nous retirons tout caractère qui n'est pas un chiffre, tirets et espaces compris, puis nous complétons à gauche par des zéros jusqu'à 14 chiffres. Un GTIN sans aucun chiffre, ou de plus de 14 chiffres, donne 400. Nous vérifions aussi sa clef de contrôle : voir ci-dessous. |
| `serial` | `string` | oui | Le numéro de série public de l'exemplaire : 12 caractères de l'alphabet Crockford Base32, qui exclut les lettres I, L, O et U. Nous retirons les espaces de bord, et la casse n'a pas d'importance. Toute autre longueur, ou tout caractère hors de cet alphabet, donne 404. |
| `linkType` | `string` | non | Demande le passeport plutôt que la page produit. Cinq valeurs le déclenchent, listées ci-dessous. La casse et les espaces de bord n'ont pas d'importance. |

Nous ramenons les lettres I et L au chiffre 1, et la lettre O au chiffre 0, avant
de chercher l'exemplaire. Vous obtenez donc la bonne réponse même pour un numéro
ressaisi à la main avec un caractère sosie.

Nous ignorons tout autre paramètre de requête. Une adresse qui traîne les
paramètres d'un scan NFC, ou les marqueurs de campagne d'un lien partagé, résout
exactement comme l'adresse nue.

### Les cinq valeurs de `linkType`

| Valeur | Effet |
| --- | --- |
| `dpp` | Redirige vers le passeport de l'exemplaire. |
| `passport` | Idem. |
| `gs1:dpp` | Idem. |
| `gs1:digitalproductpassport` | Idem. |
| `digitalproductpassport` | Idem. |

Toute autre valeur donne la même réponse qu'un paramètre absent, donc la
redirection va vers la page produit. Vous pouvez écrire le nom du paramètre
`linkType` ou `linktype`.

### La clef de contrôle du GTIN

Le dernier chiffre d'un GTIN est sa clef de contrôle : la règle GS1 modulo 10 le
calcule à partir des chiffres qui le précèdent. Nous la recalculons et nous
comparons avant toute recherche.

Comptez donc 8, 12, 13 ou 14 chiffres, les quatre longueurs qu'un GTIN peut
avoir, et vérifiez que le dernier est bien la clef des précédents. Un GTIN qui
sort de cette règle reçoit un code 400, avec le corps
`{"detail": "Invalid GTIN: the check digit does not match."}`.

Recopiez le GTIN depuis le code-barres de la référence. Une faute de frappe sur
un seul chiffre se voit alors dès l'appel.

### Ce que le GTIN doit vérifier

Nous comparons le GTIN du chemin au GTIN du modèle de l'exemplaire. Les deux
doivent être identiques. Vous en tirez deux conséquences directes.

Un exemplaire dont le modèle ne porte aucun GTIN n'est pas adressable par cette
forme. Il reste adressable par son adresse courte `/p/{serial}`.

Un numéro de série réel associé à un GTIN qui n'est pas le sien répond 404, avec
exactement le même corps qu'un numéro de série inconnu. Vous ne pouvez pas
distinguer les deux cas, et c'est voulu : la réponse ne confirme jamais
l'existence d'un numéro de série.

### Un exemplaire retiré du catalogue continue de répondre

Un exemplaire remplacé par une frappe ultérieure et un exemplaire archivé restent
résolvables. Nous les redirigeons vers leur passeport plutôt que vers la page
produit, parce que la page produit ne les affiche plus. Un code imprimé sur un
objet qui est encore entre les mains de quelqu'un ne doit pas répondre
« inconnu ».

Un numéro de série désigne un seul exemplaire. Quand cet exemplaire a été
remplacé par une frappe ultérieure, c'est la version en cours qui répond.

## Corps de la requête

Aucun. C'est une requête `GET`, tout passe par le chemin.

## Requête d'exemple

Les trois exemples résolvent le même lien et ne suivent pas la redirection, pour
que vous voyiez l'en-tête `Location`. Ils appellent la forme `/v1`, celle que
nous recommandons pour une intégration serveur. Retirez `/v1` pour appeler
l'adresse telle qu'elle figure dans un lien GS1 : la réponse est identique.

L'exemple TypeScript s'exécute côté serveur, sous Node 18 ou plus récent.

:::onglets
```bash title="curl"
curl -i https://api.sealtrust.io/v1/01/03701234567891/21/0000000000AB
```
```typescript
const gtin = "03701234567891";
const serial = "0000000000AB";

const response = await fetch(
  `https://api.sealtrust.io/v1/01/${gtin}/21/${serial}`,
  { redirect: "manual" },
);

console.log(response.status);
console.log(response.headers.get("Location"));
console.log(response.headers.get("X-RateLimit-Remaining"));
```
```python
import requests

gtin = "03701234567891"
serial = "0000000000AB"

response = requests.get(
    f"https://api.sealtrust.io/v1/01/{gtin}/21/{serial}",
    allow_redirects=False,
    timeout=30,
)

print(response.status_code)
print(response.headers["Location"])
print(response.headers["X-RateLimit-Remaining"])
```
:::

> [!INFO] Le SDK TypeScript ne couvre pas ce point d'entrée
> Le paquet `@sealtrust-io/sdk` expose la frappe en lot, la vérification et les
> abonnements aux notifications. Vous appelez donc la résolution d'un GS1
> Digital Link en HTTP direct, comme ci-dessus.

## Réponse d'exemple

Code HTTP 302. Le corps est vide. Tout est dans l'en-tête `Location`.

```http
HTTP/1.1 302 Found
Location: https://sealtrust.io/fr/product/0000000000AB
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1786000020
Content-Length: 0
```

Avec `?linkType=dpp`, ou pour un exemplaire retiré du catalogue, la destination
est le passeport.

```http
HTTP/1.1 302 Found
Location: https://sealtrust.io/fr/passport/0x0000000000000000000000000000000000000000000000000000000000000000
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 598
X-RateLimit-Reset: 1786000020
Content-Length: 0
```

### Ce qui compose l'adresse de destination

| Élément | Comment nous le choisissons |
| --- | --- |
| L'hôte | Le site public SealTrust. Si votre requête est arrivée sur le nom de domaine vérifié de la marque propriétaire, nous gardons la redirection sur ce domaine, en `https`. |
| La langue | `fr` ou `en`. Nous lisons votre en-tête `Accept-Language` et nous retenons la première langue de votre liste qui est l'une des deux. Si aucune ne correspond, nous répondons `fr`. |
| Le chemin | `product/{serial}` par défaut. `passport/{identifier}` quand vous demandez le passeport, ou quand l'exemplaire est retiré du catalogue. |

`{identifier}` est l'empreinte publique de l'exemplaire, une chaîne `0x` suivie
de 64 caractères hexadécimaux. Pour un exemplaire qui n'en a pas, c'est son
identifiant de jeton, et à défaut son numéro de série. Ne le reconstruisez pas,
lisez l'en-tête `Location`.

Lisez toujours l'en-tête `Location` plutôt que de reconstruire l'adresse
vous-même. L'hôte et la langue dépendent de la marque et de votre requête.

> [!INFO] Le serveur répond toujours par une seule redirection
> Nous résolvons la langue ici, dans cette réponse. C'est ce qui évite un second
> saut. Le registre européen des passeports numériques va chercher les adresses
> d'identifiants pour les valider et pénalise les chaînes de redirections.

> [!ATTENTION] Un domaine de marque ne résout que ses propres articles
> Si la requête arrive sur le nom de domaine vérifié d'une marque et que
> l'exemplaire appartient à une autre marque, la réponse est 404. Le domaine
> d'une marque ne sert donc jamais le passeport d'un concurrent sous sa propre
> identité visuelle.
>
> Un domaine de marque se met en place avec nous, puis vous le faites vérifier
> depuis votre console. Tant qu'il n'est pas vérifié, appelez ce point d'entrée
> sur `api.sealtrust.io`.

## Erreurs

| Code | Condition | Que faire |
| --- | --- | --- |
| 400 | Le GTIN du chemin ne contient aucun chiffre, ou en contient plus de 14 après retrait des séparateurs. Message `Invalid GTIN`. | Corrigez le GTIN. Il doit tenir en 14 chiffres au maximum. |
| 400 | Le GTIN du chemin ne compte pas 8, 12, 13 ou 14 chiffres, ou son dernier chiffre n'est pas la clef de contrôle des précédents. Message `Invalid GTIN: the check digit does not match.` | Recopiez le GTIN depuis le code-barres de la référence, puis rappelez. |
| 400 | Votre requête est arrivée sur un nom de domaine que nous ne servons pas, ou sur un domaine de marque qui n'est pas encore vérifié. La réponse est du texte brut, aucun JSON. | Appelez `https://api.sealtrust.io`, ou faites vérifier le domaine de la marque avant de l'utiliser. |
| 404 | Le numéro de série n'a pas la forme attendue : longueur différente de 12, ou caractère hors de l'alphabet Crockford Base32. Message `Unknown GS1 Digital Link`. | Vérifiez la valeur relevée sur l'étiquette. |
| 404 | Aucun exemplaire ne porte ce numéro de série. Message `Unknown GS1 Digital Link`. | Vérifiez la valeur. Si l'article n'a jamais été enregistré chez nous, ce code est définitif. |
| 404 | L'exemplaire existe, son modèle ne porte aucun GTIN exploitable. Message `Unknown GS1 Digital Link`. | Renseignez le GTIN du modèle dans votre console. En attendant, utilisez l'adresse courte `/p/{serial}`. |
| 404 | L'exemplaire existe, le GTIN du chemin n'est pas celui de son modèle. Message `Unknown GS1 Digital Link`. | Reconstruisez le lien à partir du GTIN réel du modèle. Le couple GTIN et numéro de série doit désigner le même objet. |
| 404 | La requête est arrivée sur le nom de domaine vérifié d'une marque, et l'exemplaire appartient à une autre marque. Message `Unknown GS1 Digital Link`. | Appelez ce lien sur notre domaine, ou sur le domaine de la marque qui possède l'article. |
| 404 | Le chemin est incomplet, par exemple `/01/03701234567891/21` sans numéro de série. Message `Not Found`. | Complétez le chemin. Les quatre segments `01`, le GTIN, `21` et le numéro de série sont tous obligatoires. |
| 429 | Plus de 60 appels en 60 secondes depuis la même adresse IP, tous chemins `/01/` confondus. Message `Rate limit exceeded: 60 requests per 60s`. La réponse porte un en-tête `Retry-After` en secondes. | Attendez le nombre de secondes indiqué par `Retry-After`, puis réessayez. Étalez vos appels au lieu de les envoyer en rafale. |
| 500 | Erreur inattendue de notre côté. Corps figé `{"detail": "Internal Server Error"}`. | Réessayez. L'en-tête `X-Request-Id` identifie l'appel ; transmettez-le-nous s'il se répète. |

Les cinq conditions qui portent le message `Unknown GS1 Digital Link` rendent le
même code et le même corps. Vous ne pouvez pas les distinguer, et c'est
délibéré. Des messages distincts permettraient à un scanner de séparer « ce
numéro de série n'existe pas » de « ce numéro de série existe sous un autre
GTIN », donc de confirmer quels numéros sont réels.

Ce point d'entrée ne renvoie pas de 422. Nous prenons le GTIN et le numéro de
série comme des chaînes de caractères, puis nous les contrôlons nous-mêmes : un
GTIN inexploitable ou dont la clef de contrôle ne tombe pas juste ressort en
400, un numéro de série mal formé ressort en 404.

## Voir aussi

- [`GET /01/{gtin}`](/reference/get-gs1-gtin/),
  résoudre un lien GS1 qui ne porte qu'un GTIN.
- [`GET /p/{serial}`](/reference/get-p-serial/),
  traduire le numéro de série imprimé en adresse de page consommateur.
- [`GET /passport/{identifier}`](/reference/get-passport-identifier/),
  lire le passeport publié d'un article.
- [Identification physique, QR et NFC](/identification-physique/),
  choisir le porteur physique et la forme exacte du lien GS1 Digital Link.
