Méthode GET/passport/{identifier}

Lire le passeport numérique publié d'un produit à partir de son numéro imprimé, de son identifiant de jeton ou de son empreinte de puce, au niveau d'accès demandé. Point d'entrée public.

Sur cette page

Vous lisez le passeport numérique publié d'un seul produit. En quittant cette page, vous saurez récupérer ses données au niveau d'accès que vous demandez, lire sa garantie, savoir sur quelle base chaque section peut être crue, et reconnaître un produit retiré du catalogue.

Adresse complète :

HTTP
GET https://api.sealtrust.io/v1/passport/{identifier}

Le même point d'entrée répond aussi sans le préfixe /v1, à https://api.sealtrust.io/passport/{identifier}. Les deux adresses appellent le même code. Utilisez la forme /v1 pour une nouvelle intégration.

#Autorisation

Aucune pour les niveaux public et end_user. Ce point d'entrée est public.

Une clef d'API partenaire n'ouvre rien ici. Le jeton reçu dans l'en-tête Authorization est décodé comme un jeton de session de compte utilisateur, et une clef d'API n'en est pas un : la lecture échoue en silence et l'appel se poursuit comme un appel anonyme.

Quatre valeurs du paramètre access_tier exigent en revanche une session de compte, présentée par l'en-tête Authorization: Bearer <jeton de session> ou par le cookie de session posé à la connexion.

Niveau demandéCe qu'il faut
publicrien
end_userrien
repairerune session, et une accréditation de réparateur sur la marque du produit
recyclerune session, et une accréditation de recycleur sur la marque du produit
upstreamune session ayant accès à la marque du produit, ou le rôle d'autorité
authorityune session portant le rôle d'autorité de surveillance du marché

Une session ayant accès à la marque du produit ouvre les trois niveaux de métier sur ses propres produits. Une session portant le rôle d'autorité de surveillance du marché les ouvre également.

#Plafond d'appels

60 appels par tranche de 60 secondes, comptés par adresse IP appelante. La fenêtre est fixe. Ce plafond est partagé par toutes les adresses qui commencent par /passport, et les formes /passport/… et /v1/passport/… alimentent le même compteur.

Chaque réponse acceptée porte trois en-têtes qui décrivent ce compteur.

En-têteContenu
X-RateLimit-Limitle plafond appliqué sur la fenêtre, ici 60
X-RateLimit-Remainingce qu'il vous reste dans la fenêtre en cours
X-RateLimit-Resetl'horodatage de fin de la fenêtre, en secondes

Un dépassement renvoie 429, avec les mêmes trois en-têtes et un Retry-After en secondes.

#Paramètres de chemin et de requête

NomTypeObligatoireDescription
identifierstringouiL'identifiant du produit. Trois formes sont acceptées, voir ci-dessous.
access_tierstringnonLe niveau d'accès demandé. Vaut public par défaut. Six valeurs acceptées, listées plus bas.
verify_integritybooleannonVaut false par défaut. À true, le serveur récupère la copie IPFS du passeport, compare son empreinte, et ajoute un bloc integrity à la réponse.
formatstringnonAbsent par défaut, le serveur rend alors le JSON décrit plus bas. La valeur jsonld rend le même contenu filtré, exprimé en Schema.org et GS1. Le serveur ignore toute autre valeur et rend la réponse par défaut.

#Les trois formes d'identifiant acceptées

FormeÀ quoi elle ressembleOù vous la trouvez
Numéro de série12 caractères, chiffres et lettres majuscules. Les lettres I, L, O et U n'y figurent jamais.Imprimé sur le produit, c'est ce que porte son QR
Identifiant de jetonUne suite de chiffres, souvent très longueRendu par nos réponses dans le champ token_id
Empreinte de puce0x suivi de 64 caractères hexadécimauxRendue par nos réponses dans le champ uid_hash

Le serveur reconnaît l'empreinte de puce quelle que soit la casse. Il reconnaît le numéro de série de la même façon, et il le canonicalise comme le fait le résolveur du QR : il lit les lettres I et L comme un 1, et la lettre O comme un 0. Vous pouvez donc recopier à la main le numéro lu sur une étiquette, même si vous confondez ces caractères.

Le serveur reconnaît la forme à l'écriture. Il cherche une valeur qui commence par 0x et fait exactement 66 caractères comme une empreinte de puce. Il cherche toute autre valeur d'abord comme un identifiant de jeton, puis, si cette recherche ne donne rien, comme un numéro de série.

#Les six valeurs de access_tier

Ces niveaux sont des publics différents, sans hiérarchie entre eux. Un recycleur n'est pas au-dessus d'un réparateur. Chacun des trois niveaux de métier hérite du niveau public et du niveau utilisateur final, puis ajoute ce que son métier demande.

ValeurCe qu'elle ajoute aux champs de data
publicidentification du produit, conformité ESPR, REACH et marquage CE, pourcentage de recyclabilité, pourcentage de contenu recyclé, étiquettes, spécification générale de batterie
end_userimpact environnemental, circularité complète, matière principale, coton biologique certifié, durabilité, efficacité énergétique, empreinte carbone
repairernomenclature, notice de démontage, indice de réparabilité, état de santé de batterie
recyclercomposition matière, substances préoccupantes, notice de démontage, état de santé de batterie
upstreamcomposition matière, substances préoccupantes, fabrication, chaîne d'approvisionnement
authorityl'intégralité des données, sans filtrage

Une marque peut remplacer ces règles par les siennes, par catégorie de produit. Le tableau ci-dessus décrit ce qui s'applique à défaut de règles propres à la marque.

Le serveur refuse toute autre valeur en 422. Il renvoie le niveau réellement servi dans le champ access_tier et dans l'en-tête X-DPP-Access-Tier de la réponse JSON par défaut. Lisez l'un des deux plutôt que de le supposer. Avec format=jsonld, ni ce champ ni cet en-tête n'existent, voir plus bas.

#En-têtes de réponse à connaître

En-têteContenu
X-DPP-Access-Tierle niveau réellement servi
Cache-Controlno-store, max-age=0, quel que soit le niveau servi. Ne placez cette réponse derrière aucun cache partagé.

Le serveur ne pose X-DPP-Access-Tier que sur la réponse JSON par défaut. Cache-Control porte la même valeur sur les deux formats.

Aucun en-tête n'est obligatoire dans la requête.

#Corps de la requête

Aucun. Cette requête n'a pas de corps.

#Requête d'exemple

Lecture du passeport public du produit dont le numéro imprimé est EXEMP1E00001. Les trois exemples font le même appel, arrêtent le programme sur une réponse d'erreur, puis affichent les trois mêmes valeurs : passport_version, access_tier et data.product_identity.

curl --fail-with-body -s \
  "https://api.sealtrust.io/v1/passport/EXEMP1E00001?access_tier=public" \
  | jq '{passport_version, access_tier, product_identity: .data.product_identity}'

Dans l'exemple curl, --fail-with-body renvoie un code de sortie non nul quand le serveur répond une erreur, et affiche quand même le corps. L'outil jq ne sert qu'à lire le JSON dans le terminal, il ne participe pas à l'appel.

#Réponse d'exemple

Code HTTP 200OK

Code HTTP 200.

Ce produit est encore au catalogue, il n'a pas été réclamé par un client, et le passeport est demandé au niveau public.

JSON
{
  "id": 1,
  "product_id": 1,
  "brand_id": 42,
  "schema_version": "1.0",
  "passport_version": 3,
  "data": {
    "product_identity": {
      "gtin": "03701234567890",
      "model": "Cartable Exemple 32",
      "brand": "Exemple SAS",
      "made_in": "FR"
    },
    "compliance": {
      "eu_espr": true,
      "reach": true,
      "ce_marking": false
    },
    "circularity": {
      "recyclability_percentage": 62,
      "recycled_content_percentage": 0
    }
  },
  "data_hash": "0000000000000000000000000000000000000000000000000000000000000000",
  "ipfs_uri": null,
  "ipfs_gateway_url": null,
  "visibility": "public",
  "access_tier": "public",
  "is_owner": false,
  "published_at": "2026-08-01T09:00:00+00:00",
  "product_name": "Cartable Exemple 32",
  "brand_name": "Exemple SAS",
  "image_url": "https://exemple-sas.test/images/cartable-32.jpg",
  "gtin": "03701234567890",
  "gs1_digital_link": "https://id.gs1.org/01/03701234567890/21/EXEMP1E00001",
  "warranty": {
    "status": "active",
    "ends_at": "2028-08-01T09:00:00+00:00",
    "duration_months": 24,
    "transferable": true,
    "remaining_days": 730
  },
  "evidence": {
    "sections": {
      "identity": "verified",
      "integrity": "declared",
      "composition": "declared",
      "substances_of_concern": "declared"
    },
    "legend": {
      "declared": "Stated by the brand. Recorded, dated and attributable, but not independently checked.",
      "verified": "Checked mechanically against a public record; no declaration involved."
    },
    "derived": true,
    "note": "Statuses are computed by SealTrust from verifiable facts. A brand cannot set them."
  }
}

Le domaine du champ gs1_digital_link est celui du résolveur configuré pour votre intégration. https://id.gs1.org n'est que la valeur de repli, utilisée quand aucun résolveur n'est configuré. Ne codez pas ce domaine en dur, lisez la valeur renvoyée.

#Les champs de la réponse

ChampTypeDescription
idintegerL'identifiant de la version de passeport servie.
product_idintegerL'unité à laquelle ce passeport est attaché, ou null quand le passeport porte sur le modèle et vaut pour tous ses exemplaires.
brand_idintegerLe numéro de la marque à laquelle le passeport appartient.
schema_versionstringLa version du schéma de données du passeport.
passport_versionintegerLe numéro de version publiée. Il augmente à chaque nouvelle publication.
dataobjectLes données du passeport, filtrées selon le niveau servi. Sa forme dépend de la catégorie de produit.
data_hashstring ou nullL'empreinte des données complètes de cette version, 64 caractères hexadécimaux. null quand aucune empreinte n'a été enregistrée pour cette version.
ipfs_uristringL'adresse IPFS de la copie du passeport. Toujours null aux niveaux public et end_user.
ipfs_gateway_urlstringL'adresse HTTP par laquelle cette copie se lit. Toujours null aux niveaux public et end_user.
visibilitystringLa visibilité de la version servie : public, ou owner_only quand le propriétaire actuel est authentifié. La visibilité brand_only n'est jamais servie ici.
access_tierstringLe niveau réellement servi, qui peut différer du niveau demandé pour le propriétaire du produit.
is_ownerbooleantrue quand l'appel est authentifié et que le compte est le propriétaire actuel de l'unité.
published_atstringDate et heure de publication de cette version, au format ISO 8601, ou null.
product_namestring ou nullLe nom du produit. null quand aucun nom n'a été enregistré sur l'article.
brand_namestringLe nom de la marque, ou null si le produit n'est rattaché à aucune.
image_urlstringLa photographie du modèle, ou null.
gtinstringLe GTIN du modèle, ramené à 14 chiffres. null quand le modèle n'en porte pas, ou quand la valeur enregistrée n'est pas un GTIN valide.
gs1_digital_linkstringLe lien GS1 Digital Link qui identifie cet exemplaire, de la forme <domaine de résolution>/01/<gtin sur 14 chiffres>/21/<numéro de série>. null quand le GTIN ou le numéro de série manque.
warrantyobjectLe résumé de garantie, ou null quand le produit n'en a pas. Voir ci-dessous.
evidenceobjectSur quelle base chaque section peut être crue. Voir ci-dessous. Absent si son calcul échoue.
lifecycleobjectPrésent uniquement quand l'unité est détruite ou sortie du catalogue. Voir ci-dessous.
integrityobjectPrésent uniquement quand verify_integrity=true et que le lien IPFS est servi à votre niveau. Voir ci-dessous.

#Le bloc warranty

ChampTypeDescription
statusstringactive, expiring_soon, expired ou void. Recalculé à chaque lecture.
ends_atstringDate de fin, au format ISO 8601, ou null pour une garantie à vie.
duration_monthsintegerLa durée annoncée, en mois.
transferablebooleantrue quand la garantie suit le produit lors d'un changement de propriétaire.
remaining_daysintegerJours entiers restants. Négatif quand la garantie est passée. null pour une garantie à vie ou annulée.

#Le bloc evidence

Trois valeurs existent, et elles sont calculées par SealTrust. Une marque ne peut pas les choisir.

ValeurCe qu'elle dit
verifiedVérifié mécaniquement contre un registre public, sans déclaration de personne.
document_backedUn document tiers est joint et peut être récupéré. Son contenu n'a pas été audité par SealTrust.
declaredDéclaré par la marque. Enregistré, daté, attribuable, non vérifié de façon indépendante.

Quatre sections portent une de ces valeurs : identity, integrity, composition et substances_of_concern. La section identity passe à verified quand l'unité porte un identifiant de jeton sur la chaîne. La section integrity passe à verified quand l'empreinte de cette version a été ancrée et correspond toujours aux données enregistrées. Le bloc porte en plus legend, qui redit le sens des valeurs présentes, derived à true, et note.

#Le bloc lifecycle

Il n'apparaît que si l'unité est détruite ou sortie du catalogue. Son passeport reste servi pour que l'identifiant continue de résoudre.

JSON
{
  "lifecycle": {
    "status": "superseded",
    "is_burned": false,
    "superseded": true,
    "note": "This unit is superseded or withdrawn; its passport is retained so the identifier stays resolvable (EN 18219 §4.2.2 persistence)."
  }
}

Lisez is_burned avant status.

Le champ status vaut superseded quand l'unité a été remplacée par une autre, et archived quand elle a été retirée sans remplacement. Le champ superseded ne vaut true que pour le premier cas.

Le bloc apparaît aussi quand l'unité a été détruite, c'est-à-dire quand is_burned vaut true. Dans ce cas status porte l'état courant du produit, qui peut être null ou une valeur active. Ne déduisez donc jamais la destruction de la valeur de status.

#Le bloc integrity

JSON
{
  "integrity": {
    "ipfs_fetched": true,
    "ipfs_hash": "1111111111111111111111111111111111111111111111111111111111111111",
    "expected_hash": "1111111111111111111111111111111111111111111111111111111111111111",
    "match": true
  }
}
ChampTypeDescription
ipfs_fetchedbooleantrue quand le serveur a réussi à lire la copie IPFS.
ipfs_hashstringL'empreinte du contenu réellement lu sur IPFS.
expected_hashstringL'empreinte attendue pour ce contenu.
matchbooleanLe verdict de la comparaison.

expected_hash est l'empreinte de la projection PUBLIQUE du passeport, celle qui est déposée sur IPFS. Elle diffère de data_hash, qui couvre les données complètes, y compris les champs réservés aux niveaux professionnels. Les deux valeurs coïncident seulement quand le passeport ne porte aucun champ non public. Ne comparez donc jamais expected_hash et data_hash.

Le champ match vaut true quand la copie IPFS correspond, false quand elle diffère, et null quand la copie n'a pas pu être récupérée. Dans ce dernier cas ipfs_fetched vaut false, un champ error remplace les deux empreintes, et null signifie que rien n'a pu être conclu.

#La réponse en JSON-LD

Avec format=jsonld, la réponse porte le type de contenu application/ld+json. C'est un document Schema.org et GS1 dont les champs sont filtrés par le même niveau d'accès. Il ne contient ni passport_version, ni data_hash, ni les blocs warranty, evidence, lifecycle et integrity décrits ci-dessus : la garantie y est exprimée en WarrantyPromise, et les autres blocs n'y figurent pas.

Deux autres différences comptent pour votre intégration.

Le document ne porte pas de champ access_tier. Le serveur ne renvoie pas non plus l'en-tête X-DPP-Access-Tier. Pour connaître le niveau réellement servi, appelez sans format, ou tenez-vous-en au niveau que vous avez demandé.

Le serveur ignore verify_integrity dans ce format. Il rend le document JSON-LD avant de calculer le bloc integrity, donc ce paramètre ne change rien à la réponse et aucune erreur ne vous le signale.

#Erreurs

Le corps d'une réponse d'erreur porte un champ detail.

CodeConditionQue faire
401access_tier=authority est demandé sans session valide. detail vaut Authority-tier access requires authentication.Connectez-vous avec un compte portant le rôle d'autorité de surveillance du marché. Une clef d'API partenaire ne convient pas.
401access_tier vaut repairer, recycler ou upstream, et l'appel ne porte aucune session valide. detail vaut Professional-tier access requires authentication.Présentez un jeton de session de compte, ou demandez le niveau public ou end_user.
403access_tier=authority est demandé par un compte connecté qui ne porte pas ce rôle. detail vaut Authority-tier access is restricted to market surveillance authorities.Demandez le niveau qui correspond à votre habilitation.
403Un niveau professionnel est demandé par un compte connecté qui n'a ni accès à la marque du produit, ni l'accréditation correspondante sur cette marque. detail vaut This tier is restricted to the product's brand, partners holding the matching accreditation, and market-surveillance authorities.Demandez à la marque l'accréditation qui correspond à votre métier, puis demandez le niveau de ce métier.
404Aucun produit ne correspond à l'identifiant, sous aucune des trois formes acceptées. detail vaut Product not found.Vérifiez le numéro recopié. Un produit détruit ou retiré du catalogue reste résolu ici, donc cette réponse veut bien dire que l'identifiant est inconnu.
404Le produit existe, mais aucune version de passeport publiée ne lui correspond. detail vaut No published passport found for this product.La marque doit publier une version. Un brouillon non publié n'est jamais servi, et une version en visibilité brand_only non plus.
422Une valeur de paramètre est refusée : un access_tier qui n'est pas une des six valeurs, ou un verify_integrity qui n'est pas un booléen. detail est une liste, chaque entrée portant loc, type et msg.Lisez loc pour savoir quel paramètre est en cause, puis corrigez sa valeur.
429Plus de 60 appels ont été faits depuis votre adresse IP vers une adresse /passport dans la fenêtre de 60 secondes en cours. detail vaut Rate limit exceeded: 60 requests per 60s.Attendez le nombre de secondes indiqué par l'en-tête Retry-After, puis réessayez.
500Une erreur inattendue s'est produite pendant le traitement de votre appel. detail vaut Internal Server Error.Réessayez. Si l'erreur persiste, contactez le support en indiquant l'heure de l'appel et la valeur de l'en-tête X-Request-Id de la réponse.

#Voir aussi

Votre réponse ouvre un courriel pré-rempli dans votre messagerie, à destination de contact@sealtrust.io. Vous le relisez avant de l'envoyer.

Proposer une correctionSignaler un problème