# GET /p/{serial}

Traduire le numéro de série imprimé sur un article en adresse de sa page consommateur. Point d'entrée public, sans clef d'API, qui répond par une redirection.

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

---

Vous transformez le numéro de série d'un article en adresse de la page qui le
présente à un consommateur, par une redirection.

## Autorisation

Aucune, point d'entrée public. Nous n'attendons ni clef d'API, ni cookie de
session, ni en-tête `Authorization`. Vous pouvez appeler cette adresse depuis
un serveur.

## Plafond d'appels

Cette adresse n'a pas de plafond propre. Elle relève du compteur général de
l'API, compté par adresse IP appelante sur une fenêtre de 60 secondes, et
partagé avec toutes les autres adresses qui n'ont pas de plafond propre.

Prévoyez le code 429 dans votre client et respectez l'en-tête `Retry-After`
qu'il porte. La valeur de ce compteur peut changer sans préavis, donc
n'inscrivez aucun nombre en dur dans votre code.

La 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. Ne faites pas dépendre votre client de leur
présence : traitez une réponse qui ne les porte pas comme une réponse normale,
et fondez votre rythme d'appel sur les valeurs que vous recevez.

Cette adresse ne consomme aucun quota de votre offre.

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

| Nom | Type | Obligatoire | Description |
| --- | --- | --- | --- |
| `serial` | `string` | oui | Le numéro de série public de l'article, 12 caractères. Paramètre de chemin. |
| `linkType` | `string` | non | Demande le passeport plutôt que la page consommateur. Cinq valeurs le déclenchent : `dpp`, `passport`, `gs1:dpp`, `gs1:digitalproductpassport`, `digitalproductpassport`. La casse et les espaces de bord n'ont pas d'importance. Nous traitons toute autre valeur comme si le paramètre était absent. Nous acceptons aussi l'orthographe `linktype`, tout en minuscules. |

Ce numéro de série est l'identifiant unique de produit au sens de la norme
EN 18219, celui que le registre européen des passeports numériques attend, et
celui qu'encode le QR code imprimé sur vos articles. Le registre européen
n'accepte aujourd'hui aucun enregistrement, de la part de personne. Nous
soumettrons cet identifiant dès que son enregistrement sera ouvert.

Ce point d'entrée adresse un exemplaire physique. Un passeport n'a pas besoin
de porter sur un exemplaire : le règlement ESPR prévoit trois niveaux, le
modèle, le lot et l'article. Nous servons aujourd'hui le niveau modèle et le
niveau article. Pour un passeport de modèle, l'identifiant à utiliser est
[`GET /01/{gtin}`](/reference/get-gs1-gtin/), qui résout le passeport de
référence partagé par tous les exemplaires qui portent le même code produit.
Vous n'avez donc pas à numéroter chaque exemplaire pour publier un passeport.

Le numéro de série s'écrit dans un alphabet de 32 caractères : les chiffres de
`0` à `9` et les lettres de `A` à `Z`, sauf `I`, `L`, `O` et `U`. Ces quatre
lettres sont écartées parce qu'elles se confondent avec des chiffres sur une
étiquette.

Avant toute recherche, nous nettoyons la valeur reçue : nous retirons les
espaces de bord, nous remplaçons `I` et `L` par `1`, `O` par `0`, et nous
passons le tout en majuscules. Une personne qui recopie un numéro à la main
peut donc se tromper sur `I`, `L` et `O` sans conséquence. Nous comprenons
`ilo2345678ab` comme `1102345678AB`, et c'est cette forme corrigée qui apparaît
dans l'adresse de destination. La lettre `U` n'est pas corrigée : une valeur
qui en contient part en 404.

Après ce nettoyage, nous refusons en 404 toute valeur qui ne fait pas
exactement 12 caractères, ou qui contient un caractère hors de l'alphabet. Les
paramètres d'authentification que certaines puces NFC ajoutent à l'adresse au
moment du scan sont des paramètres de requête : nous ne les lisons pas ici et
ils ne changent rien à la réponse.

> [!ATTENTION] Le numéro de série n'est pas l'identifiant du jeton
> Le numéro imprimé sur l'étiquette et l'identifiant du jeton sur la chaîne
> sont deux valeurs différentes. Ce point d'entrée n'accepte que le numéro de
> série. L'identifiant du jeton fait 77 à 78 chiffres et ne rentre pas dans une
> adresse d'identifiant.

## Corps de la requête

Aucun. C'est une requête `GET` : tout passe par le chemin et les paramètres de
requête.

## Requête d'exemple

Ne suivez pas la redirection. Ce que vous voulez lire, c'est l'en-tête
`Location`.

### Deux adresses pour la même route

Pour un appel serveur, appelez `https://api.sealtrust.io/v1/p/{serial}`. Le
même point d'entrée répond aussi sans le préfixe `/v1`. Pour une intégration
serveur nouvelle, utilisez la forme `/v1`.

Le QR code que nous générons pour vos articles, lui, porte la forme sans
préfixe, sur le nom de domaine de vos pages consommateur, par exemple
`https://sealtrust.io/p/{serial}`. C'est cette forme-là qui est imprimée et qui
sert d'identifiant, parce qu'un identifiant imprimé sur une étiquette ne se
corrige plus.

:::onglets
```bash title="curl"
curl -i https://api.sealtrust.io/v1/p/000000000000 \
  -H "Accept-Language: fr"
```
```typescript
const response = await fetch("https://api.sealtrust.io/v1/p/000000000000", {
  headers: { "Accept-Language": "fr" },
  redirect: "manual",
});

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

response = requests.get(
    "https://api.sealtrust.io/v1/p/000000000000",
    headers={"Accept-Language": "fr"},
    allow_redirects=False,
    timeout=30,
)

print(response.status_code)
print(response.headers["Location"])
```
:::

> [!ATTENTION] L'exemple TypeScript est un exemple serveur
> Exécuté dans un navigateur, `redirect: "manual"` masque le code de statut et
> l'en-tête `Location`. Faites cet appel depuis votre serveur.

> [!INFO] Le SDK TypeScript ne couvre pas ce point d'entrée
> Le paquet `@sealtrust-io/sdk` ne comporte aucune méthode de résolution
> d'identifiant. Vous appelez donc cette adresse en HTTP direct, comme
> ci-dessus. Les trois onglets montrent la même opération avec les mêmes
> valeurs.

## Réponse d'exemple

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

```http
HTTP/1.1 302 Found
Location: https://sealtrust.io/fr/product/000000000000
```

Avec `Accept-Language: en-GB,en;q=0.9`, la même requête renvoie la version
anglaise de la page.

```http
HTTP/1.1 302 Found
Location: https://sealtrust.io/en/product/000000000000
```

Avec `?linkType=dpp`, la destination devient le passeport de l'article.

```http
HTTP/1.1 302 Found
Location: https://sealtrust.io/fr/passport/0x0000000000000000000000000000000000000000000000000000000000000000
```

### Comment nous choisissons la destination

Ce point d'entrée ne rend aucun verdict d'authenticité. Nous retrouvons
l'article qui porte ce numéro de série, puis nous renvoyons l'adresse de la
page à afficher. Pour un verdict, appelez
[`GET /qr/verify`](/reference/get-qr-verify/) avec les paramètres signés du QR
code.

| Cas | Destination |
| --- | --- |
| Article en catalogue, sans `linkType` reconnu | La page consommateur de l'article, adressée par son numéro de série corrigé. |
| `linkType` reconnu | Le passeport de l'article, adressé par son empreinte. Si l'article n'a pas d'empreinte, par son identifiant de jeton, et à défaut par son numéro de série. |
| Article retiré du catalogue | Le passeport, avec ou sans `linkType`. Un article remplacé par une frappe plus récente ou archivé reste donc résolvable, ce qu'exige la norme EN 18219 pour un identifiant retiré. |

> [!INFO] Exactement une redirection
> Nous renvoyons une destination déjà préfixée par la langue, `/fr/` ou `/en/`.
> Le registre européen récupère les adresses d'identifiants pour les valider et
> pénalise les chaînes de redirections, donc nous visons directement l'adresse
> finale.

La langue de la destination vient de l'en-tête `Accept-Language`. Nous prenons
la première langue déclarée qui est le français ou l'anglais. Sans en-tête, ou
avec une langue que nous ne servons pas, nous répondons une destination en
français.

Si vous servez vos pages consommateur sur votre propre nom de domaine, nous
construisons la destination sur ce nom de domaine, en `https`, et le visiteur
ne quitte donc pas votre domaine.

Quand plusieurs enregistrements portent le même numéro de série, parce que vous
avez refrappé un article, nous répondons pour l'enregistrement encore en
catalogue. S'il n'y en a aucun, nous répondons pour le plus récent.

## Erreurs

| Code | Condition | Que faire |
| --- | --- | --- |
| 400 | L'appel arrive sur un nom d'hôte que nous ne servons pas, par exemple un domaine de marque dont la vérification n'est plus valide. La réponse ne porte pas de corps JSON. | Appelez `api.sealtrust.io`, ou faites vérifier à nouveau votre nom de domaine dans la console. |
| 404 | Le numéro de série est mal formé : longueur différente de 12 après nettoyage, ou caractère hors de l'alphabet. Message `Unknown product identifier`. | Vérifiez la valeur recopiée. N'ajoutez rien au numéro dans le chemin, tout paramètre supplémentaire se met dans la requête. |
| 404 | Aucun article ne porte ce numéro de série. Message `Unknown product identifier`. | Rien à corriger dans l'appel. Le numéro n'a jamais été attribué, ou il appartient à un autre système. |
| 404 | L'appel arrive sur le nom de domaine d'une marque et l'article appartient à une autre marque. Message `Unknown product identifier`. | Appelez ce numéro de série sur le domaine de la marque à laquelle il appartient, ou sur `api.sealtrust.io`. |
| 429 | Plus de 60 appels en 60 secondes depuis la même adresse IP, tous chemins `/p/` confondus. Message `Rate limit exceeded: 60 requests per 60s`. La réponse porte l'en-tête `Retry-After`, en secondes. | Attendez le nombre de secondes indiqué par `Retry-After`. Répartissez 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. |

> [!INFO] Les trois 404 sont indiscernables
> Les trois conditions ci-dessus renvoient exactement le même code et le même
> message. C'est voulu : une réponse différente selon le cas dirait à un
> visiteur quels numéros de série existent, et lui permettrait de les
> énumérer. Un 404 ne vous dit donc jamais laquelle des trois situations vous
> avez rencontrée.

## Voir aussi

- [`GET /01/{gtin}/21/{serial}`](/reference/get-gs1-gtin-serial/),
  résoudre un lien GS1 qui porte un GTIN et un numéro de série.
- [`GET /01/{gtin}`](/reference/get-gs1-gtin/),
  résoudre un lien GS1 qui ne porte qu'un GTIN.
- [`GET /qr/verify`](/reference/get-qr-verify/),
  vérifier un article depuis une adresse de vérification de l'ancienne
  forme.
- [`GET /resolve/{identifier}`](/reference/get-resolve/),
  lire en un appel tout ce qu'une page produit affiche.
- [Identification physique, QR et NFC](/identification-physique/),
  choisir le porteur physique et la forme exacte du lien GS1 Digital Link.
