# GET /passport/{identifier}/proof

Rassembler en un appel toutes les preuves publiques d'un passeport : empreinte du contenu, copie IPFS, ancrage sur Base, sceau de version et état du justificatif signé. Point d'entrée public.

Source : https://docs.sealtrust.io/reference/get-passport-proof/

---

Vous rassemblez en un seul appel les preuves publiques attachées au passeport
d'un produit. En quittant cette page, vous saurez demander ces preuves à partir
d'un identifiant d'article, lire ce que chaque bloc établit réellement, et
distinguer une preuve absente d'une preuve en défaut.

Adresse complète :

```http
GET https://api.sealtrust.io/v1/passport/{identifier}/proof
```

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

Ce point d'entrée ne renvoie aucun contenu du passeport. Il renvoie les
éléments qui permettent de vérifier ce contenu. Pour lire le passeport
lui-même, appelez `GET /v1/passport/{identifier}`.

> [!ATTENTION] Le bloc `anchor` ne prouve pas toujours un ancrage
> Ce bloc porte soit la racine du lot inscrite sur la chaîne, soit la
> transaction de frappe de l'article. Une frappe ne constitue pas un ancrage.
> Le champ `anchored` tranche entre les deux. Branchez votre code sur
> `anchored` et sur `proves`. Le nom de la clef `anchor` ne dit rien de ce que
> la référence établit.

## Autorisation

Aucune, point d'entrée public. Vous n'envoyez ni clef d'API, ni session, ni
en-tête d'origine. La réponse est la même pour tout le monde.

Vous recevez ici uniquement les passeports dont la visibilité est publique. Un
passeport réservé au propriétaire ou réservé à la marque ne vous est jamais
rendu par ce point d'entrée.

## Plafond d'appels

60 appels par tranche de 60 secondes, comptés par adresse réseau appelante.

Ce compteur est commun à tous les chemins qui commencent par `/passport`. Les
appels que vous adressez à l'un d'eux entament donc le budget des autres. Le
préfixe `/v1` ne crée pas un second budget : `/v1/passport/1042/proof` et
`/passport/1042/proof` remplissent le même compteur.

Chaque réponse acceptée porte trois en-têtes.

| En-tête | Contenu |
| --- | --- |
| `X-RateLimit-Limit` | le plafond appliqué sur la fenêtre, ici `60` |
| `X-RateLimit-Remaining` | ce qu'il vous reste dans la fenêtre en cours |
| `X-RateLimit-Reset` | l'horodatage de fin de la fenêtre, en secondes |

Un refus renvoie 429, avec ces trois en-têtes et `Retry-After`. Sur ce point
d'entrée, `Retry-After` vaut la durée de la fenêtre, soit 60 secondes.

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

| Nom | Type | Obligatoire | Description |
| --- | --- | --- | --- |
| `identifier` | `string` | oui | L'article dont vous voulez les preuves. Trois formes sont acceptées, voir ci-dessous. |

Ce point d'entrée n'a aucun paramètre de requête.

`identifier` accepte trois formes, essayées dans cet ordre.

| Forme | Aspect | Provenance |
| --- | --- | --- |
| Empreinte d'identifiant | `0x` suivi de 64 caractères hexadécimaux | l'empreinte de l'identifiant unique de l'exemplaire. Sur un article NFC elle vient de l'identifiant de la puce. Sur un article QR seul, le serveur la tire au hasard à la frappe. |
| Identifiant de jeton | un nombre écrit en décimal | l'identifiant de l'article sur la chaîne |
| Numéro de série imprimé | 12 caractères | ce que porte le QR code sur le produit, dans l'adresse `/p/{serial}` |

Vous pouvez écrire le numéro de série en majuscules ou en minuscules. Nous
ramenons les caractères qui se ressemblent à une forme unique avant la
recherche, donc un `I` ou un `L` saisi à la main retrouve le `1`, et un `O`
retrouve le `0`.

Ce point d'entrée ne résout que les articles encore au catalogue de la marque.
Un article détruit sur la chaîne, remplacé par une version ultérieure ou
archivé répond 404.

### En-têtes

Aucun en-tête n'est requis.

## Corps de la requête

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

## Requête d'exemple

Preuves du passeport de l'article dont l'identifiant de jeton est `1042`.

> [!INFO] Le SDK TypeScript ne couvre pas ce point d'entrée
> Aucune méthode du paquet `@sealtrust-io/sdk` n'appelle cette adresse.
> L'exemple TypeScript ci-dessous utilise `fetch`, sans dépendance.

:::onglets
```bash title="curl"
curl -i https://api.sealtrust.io/v1/passport/1042/proof
```
```typescript
const reponse = await fetch(
  "https://api.sealtrust.io/v1/passport/1042/proof",
);

if (reponse.status === 404) {
  console.log("Aucun passeport public pour cet identifiant.");
} else if (reponse.ok) {
  const preuves = await reponse.json();

  console.log(preuves.passport_version, preuves.data_hash);

  if (preuves.anchor && preuves.anchor.anchored === true) {
    console.log("Lot ancré sur", preuves.anchor.chain);
    console.log(preuves.anchor.merkle_root);
  } else if (preuves.anchor) {
    console.log("Pas d'ancrage de lot. Référence :", preuves.anchor.proves);
  }

  if (preuves.passport_anchor) {
    console.log(
      "Document daté au plus tard le",
      preuves.passport_anchor.anchored_at,
    );
    console.log(
      "Contenu inchangé :",
      preuves.passport_anchor.data_hash_matches,
    );
  }

  console.log("Scellé :", preuves.seal.sealed);
  console.log("Justificatif signé :", preuves.vc.issued);
} else {
  console.log(reponse.status, await reponse.json());
}
```
```python
import requests

response = requests.get(
    "https://api.sealtrust.io/v1/passport/1042/proof",
    timeout=30,
)

if response.status_code == 404:
    print("Aucun passeport public pour cet identifiant.")
elif response.ok:
    preuves = response.json()

    print(preuves["passport_version"], preuves.get("data_hash"))

    ancrage = preuves.get("anchor")
    if ancrage and ancrage.get("anchored") is True:
        print("Lot ancré sur", ancrage["chain"])
        print(ancrage.get("merkle_root"))
    elif ancrage:
        print("Pas d'ancrage de lot. Référence :", ancrage.get("proves"))

    document = preuves.get("passport_anchor")
    if document:
        print("Document daté au plus tard le", document["anchored_at"])
        print("Contenu inchangé :", document["data_hash_matches"])

    print("Scellé :", preuves["seal"]["sealed"])
    print("Justificatif signé :", preuves["vc"]["issued"])
else:
    print(response.status_code, response.json())
```
:::

## Réponse d'exemple

Code HTTP `200`.

```json
{
  "passport_version": 3,
  "data_hash": "1111111111111111111111111111111111111111111111111111111111111111",
  "ipfs_uri": "ipfs://bafybeiexemple00000000000000000000000000000000000000000000",
  "ipfs_gateway_url": "https://ipfs.io/ipfs/bafybeiexemple00000000000000000000000000000000000000000000",
  "anchor": {
    "chain": "base",
    "chain_id": 8453,
    "type": "merkle_batch",
    "tx_hash": "0x2222222222222222222222222222222222222222222222222222222222222222",
    "basescan_url": "https://basescan.org/tx/0x2222222222222222222222222222222222222222222222222222222222222222",
    "merkle_root": "0x3333333333333333333333333333333333333333333333333333333333333333",
    "anchored": true,
    "proves": "batch_inclusion"
  },
  "passport_anchor": {
    "chain": "base",
    "chain_id": 8453,
    "tx_hash": "0x4444444444444444444444444444444444444444444444444444444444444444",
    "basescan_url": "https://basescan.org/tx/0x4444444444444444444444444444444444444444444444444444444444444444",
    "merkle_root": "0x5555555555555555555555555555555555555555555555555555555555555555",
    "leaf": "0x6666666666666666666666666666666666666666666666666666666666666666",
    "leaf_index": 2,
    "proof": [
      "0x7777777777777777777777777777777777777777777777777777777777777777",
      "0x8888888888888888888888888888888888888888888888888888888888888888"
    ],
    "anchored_at": "2026-08-14T02:15:09+00:00",
    "data_hash_matches": true,
    "proves": "content_existed_at_or_before_tx"
  },
  "seal": {
    "sealed": true,
    "sealed_at": "2026-08-13T09:41:22+00:00",
    "algorithm": "st-dpp-chain-v1",
    "version_hash": "9999999999999999999999999999999999999999999999999999999999999999",
    "prev_version_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "linked": true,
    "chain_link_match": true
  },
  "vc": {
    "issued": true,
    "vct": "https://schema.sealtrust.io/vct/digital-product-passport",
    "issued_at": "2026-08-13T09:41:25+00:00"
  },
  "verifications": {
    "count": 47,
    "last_verified_at": "2026-08-19T18:03:44+00:00"
  }
}
```

`passport_version`, `seal` et `vc` sont toujours présents. Nous omettons les
autres champs de premier niveau quand la preuve correspondante n'existe pas.
Une absence signifie que cette preuve n'est pas disponible. Traitez-la comme
une information, au même titre qu'une valeur.

| Champ | Type | Présence | Description |
| --- | --- | --- | --- |
| `passport_version` | `integer` | toujours | Le numéro de version du passeport publié auquel se rapportent toutes les preuves ci-dessous. |
| `data_hash` | `string` | si enregistrée | L'empreinte SHA-256 du contenu complet du passeport, 64 caractères hexadécimaux sans préfixe `0x`. |
| `ipfs_uri` | `string` | si copie vérifiée | L'adresse IPFS de la copie publique du passeport. |
| `ipfs_gateway_url` | `string` | si copie vérifiée | La même copie, sous forme d'adresse web ouvrable dans un navigateur. |
| `anchor` | `object` | si l'article a une référence sur la chaîne | La référence sur la chaîne qui concerne l'ARTICLE. Voir ci-dessous. |
| `passport_anchor` | `object` | si la version a été ancrée | L'ancrage qui date le DOCUMENT. Voir ci-dessous. |
| `seal` | `object` | toujours | Le sceau de la version et sa place dans la suite des versions. Voir ci-dessous. |
| `vc` | `object` | toujours | L'état du justificatif signé attaché au passeport. Voir ci-dessous. |
| `verifications` | `object` | si le compte est supérieur à zéro | Le nombre de vérifications réussies enregistrées pour cet exemplaire, par QR comme par NFC, et la date de la dernière. Absent aussi quand l'exemplaire ne porte pas d'empreinte d'identifiant. |

Le passeport servi ici porte soit sur cet exemplaire précis, soit sur son
modèle. Nous cherchons d'abord un passeport rattaché à l'exemplaire. À défaut,
nous servons le passeport publié pour son modèle, partagé par tous les
exemplaires qui portent le même code produit. Dans ce second cas, `data_hash`,
`ipfs_uri`, `ipfs_gateway_url`, `passport_anchor`, `seal` et `vc` décrivent le
document du modèle. `anchor` et `verifications` restent propres à l'exemplaire
que vous avez demandé.

La réponse porte aussi l'en-tête `Cache-Control: no-store, max-age=0`. Ne
mettez cette réponse dans aucun cache partagé. Pour réduire le nombre de vos
appels, gardez le résultat dans votre propre cache applicatif, avec la durée de
fraîcheur que votre usage tolère.

### Le bloc `anchor`, la référence de l'article

Ce bloc est présent quand l'article porte une référence sur la chaîne. Il
recouvre deux situations différentes, et c'est le champ `anchored` qui les
sépare.

| Champ | Type | Description |
| --- | --- | --- |
| `chain` | `string` | Le réseau, en forme lisible par une machine. `base` en production. |
| `chain_id` | `integer` | L'identifiant du réseau. `8453` pour Base en production. |
| `type` | `string` | `merkle_batch` ou `mint_transaction`. |
| `tx_hash` | `string` | La transaction concernée. |
| `basescan_url` | `string` | Le lien direct vers cette transaction sur l'explorateur public du réseau. |
| `merkle_root` | `string` | La racine du lot inscrite sur la chaîne. Présent uniquement quand `type` vaut `merkle_batch` et que la racine est enregistrée. |
| `anchored` | `boolean` | `true` uniquement quand le lot de l'article a été inscrit sur la chaîne. |
| `proves` | `string` | `batch_inclusion` quand le lot est ancré, `token_minted` sinon. |

`anchored: true` veut dire que le lot auquel appartient l'article a été inscrit
sur la chaîne. Vous obtenez la preuve d'appartenance elle-même par
`GET /v1/verify/merkle/{identifier}`, lorsque ce bloc porte aussi
`merkle_root`. Sans `merkle_root`, cet appel répond 404 et `detail` vaut
`No Merkle anchor for this product`.

`anchored: false` veut dire que le lot de l'article n'a jamais été inscrit. Le
bloc porte alors la transaction de frappe de l'article. Elle démontre que le
jeton existe sur la chaîne. Elle ne démontre rien sur un lot, et rien sur le
contenu du passeport.

Dans les deux cas, cette référence ne porte aucun engagement sur le contenu du
passeport. L'engagement sur le contenu se lit dans `passport_anchor` et dans
`seal`.

### Le bloc `passport_anchor`, la date du document

Ce bloc est présent quand cette version précise du passeport a été inscrite sur
la chaîne. Il établit une seule chose : le contenu existait au plus tard au
moment de cette transaction.

| Champ | Type | Description |
| --- | --- | --- |
| `type` | `string` | Toujours `passport_anchor`. Il distingue cet ancrage de celui du bloc `anchor`, qui porte un lot d'articles. |
| `anchored` | `boolean` | Toujours `true`. Le bloc n'existe que lorsque la version a été inscrite sur la chaîne. |
| `chain` | `string` | Le réseau, en forme lisible par une machine. |
| `chain_id` | `integer` ou `null` | L'identifiant du réseau. `null` sur un ancrage antérieur à l'enregistrement du réseau. `chain` indique alors le réseau de production. |
| `tx_hash` | `string` | La transaction qui porte la racine. |
| `basescan_url` | `string` | Le lien direct vers cette transaction sur l'explorateur public du réseau. |
| `merkle_root` | `string` | La racine inscrite sur la chaîne, `0x` suivi de 64 caractères hexadécimaux. |
| `leaf` | `string` | L'empreinte de cette version dans l'arbre, `0x` suivi de 64 caractères hexadécimaux. |
| `leaf_index` | `integer` | La position de cette empreinte dans la liste des empreintes inscrites. Le comptage démarre à 0. |
| `proof` | `string[]` | Les empreintes voisines à combiner avec `leaf`, de bas en haut, pour retrouver `merkle_root`. La liste est vide quand l'inscription ne couvrait qu'une version. |
| `anchored_at` | `string` ou `null` | La date de l'inscription, au format ISO 8601. |
| `data_hash_matches` | `boolean` | `true` quand le passeport enregistré aujourd'hui correspond encore à ce qui a été inscrit. |
| `proves` | `string` | Toujours `content_existed_at_or_before_tx`. |

Vous pouvez refaire la vérification d'appartenance sans nous faire confiance.
Vous partez de `leaf`. Pour chaque élément de `proof`, dans l'ordre, vous mettez
les deux valeurs de 32 octets côte à côte, la plus petite des deux d'abord, puis
vous appliquez keccak256 à la concaténation. Le résultat devient la nouvelle
valeur de travail. Après le dernier élément de `proof`, vous devez obtenir
exactement `merkle_root`. C'est la convention de vérification d'OpenZeppelin,
avec des voisins triés à chaque étage. Ouvrez ensuite `basescan_url` pour lire
la transaction qui a inscrit cette racine.

> [!DANGER] `data_hash_matches: false` est un signal d'altération
> Cette valeur veut dire que le passeport enregistré aujourd'hui ne correspond
> plus à ce qui a été inscrit sur la chaîne pour cette version. Nous la
> publions au lieu de la masquer. Traitez-la comme une alerte et adressez-vous
> à la marque.

L'absence de `passport_anchor` est courante et normale. Publier une version ne
l'inscrit pas sur la chaîne. L'inscription est une opération distincte, faite
par passages groupés, et beaucoup de passeports publiés ne portent jamais ce
bloc. Ne concluez donc rien de son absence sur la validité du passeport. Le
bloc `seal`, lui, est toujours présent et couvre les versions non inscrites.

### Le bloc `seal`, le sceau de la version

Ce bloc est toujours présent. Il tient sur les versions que `passport_anchor`
ne couvre pas.

| Champ | Type | Présence | Description |
| --- | --- | --- | --- |
| `sealed` | `boolean` | toujours | `false` quand la version n'a jamais été scellée. Le bloc s'arrête alors là. |
| `sealed_at` | `string` | si `sealed` | La date du scellement, au format ISO 8601. |
| `algorithm` | `string` | si `sealed` | La version de l'algorithme de chaînage. Vaut `st-dpp-chain-v1`. |
| `version_hash` | `string` ou `null` | si `sealed` | L'empreinte de ce maillon, 64 caractères hexadécimaux. |
| `prev_version_hash` | `string` ou `null` | si `sealed` | L'empreinte du maillon précédent. `null` pour la première version publiée. |
| `linked` | `boolean` | si `sealed` | `true` quand cette version porte une empreinte de maillon. |
| `reason` | `string` | si `linked` est `false` | Vaut `sealed_before_chain`. La version a été publiée avant l'existence du chaînage, donc aucun maillon n'est fabriqué après coup. Le bloc s'arrête là. |
| `chain_link_match` | `boolean` | si `linked` | `true` quand le passeport enregistré aujourd'hui produit encore exactement le maillon stocké. |

`chain_link_match` est recalculé à chaque appel à partir du contenu enregistré.
`false` veut dire que la version a été modifiée après sa publication.

### Le bloc `vc`, le justificatif signé

Ce bloc est toujours présent.

| Champ | Type | Présence | Description |
| --- | --- | --- | --- |
| `issued` | `boolean` | toujours | `true` quand un justificatif signé a été émis pour ce passeport. |
| `vct` | `string` | si enregistré | L'identifiant du modèle de justificatif. Vaut aujourd'hui `https://schema.sealtrust.io/vct/digital-product-passport`. |
| `issued_at` | `string` | si enregistrée | La date d'émission, au format ISO 8601. |

### La copie IPFS

`ipfs_uri` et `ipfs_gateway_url` désignent la copie publique du passeport. Le
contenu à cette adresse est la vue publique du passeport. Son empreinte diffère
donc de `data_hash`, qui porte sur le contenu complet.

Vous ne recevez ces deux champs que lorsque nous avons récupéré la copie et
vérifié qu'elle est exactement le passeport public que la marque publie
aujourd'hui. Leur absence peut donc signifier trois choses : aucune copie n'a
été déposée, la copie n'a pas pu être récupérée au moment de l'appel, ou son
contenu ne correspond plus à ce que la marque publie. Une marque qui change ses
règles d'accès change ce que montre son passeport public, donc une copie
déposée avant ce changement cesse d'être annoncée tant qu'une nouvelle n'a pas
été déposée. Ne concluez rien de leur seule absence.

L'hôte de `ipfs_gateway_url` est celui d'une passerelle publique et peut
changer. Ne le codez pas en dur. Utilisez l'adresse rendue telle quelle, ou
`ipfs_uri` avec la passerelle de votre choix.

## Erreurs

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

| Code | Condition | Que faire |
| --- | --- | --- |
| 404 | Aucun article au catalogue ne correspond à cet identifiant, sous aucune des trois formes acceptées. `detail` vaut `Product not found`. | Vérifiez l'identifiant. Un article détruit sur la chaîne, remplacé par une version ultérieure ou archivé ne se résout plus et donne cette même réponse. |
| 404 | L'article existe, mais aucun passeport publié en visibilité publique ne lui est rattaché, ni directement, ni par son modèle. `detail` vaut `No published passport found for this product`. | Ne traitez pas cette réponse comme un échec. Cet article n'a pas de passeport public. Un passeport réservé au propriétaire ou à la marque donne la même réponse. |
| 429 | Le plafond de 60 appels par 60 secondes est atteint pour votre adresse réseau, sur l'ensemble des chemins `/passport`. `detail` vaut `Rate limit exceeded: 60 requests per 60s`. | Attendez le nombre de secondes indiqué par `Retry-After`, puis réessayez. Mettez la réponse en cache de votre côté. |
| 500 | Une erreur inattendue s'est produite pendant le traitement de votre appel. `detail` vaut `Internal Server Error`. La réponse porte un en-tête `X-Request-Id`. | Réessayez. Si l'erreur persiste, contactez le support en indiquant la valeur de `X-Request-Id`. |

Une lecture d'ancrage ou de copie IPFS qui échoue ne produit pas d'erreur. Le
bloc concerné est absent, et les autres preuves sont servies normalement.

## Voir aussi

- [`GET /passport/{identifier}/verify`](/reference/get-passport-verify/),
  contrôler l'intégrité du passeport publié d'un article.
- [`GET /verify/merkle/{identifier}`](/reference/get-verify-merkle/),
  récupérer la preuve d'appartenance d'un article au lot ancré sur Base.
- [`GET /passport/{identifier}`](/reference/get-passport-identifier/),
  lire le passeport publié d'un article.
- [Confiance et preuves](/confiance-et-preuves/),
  ce que chaque preuve établit et comment un tiers refait la vérification.
