# GET /passport/{identifier}/verify

Contrôler l'intégrité du passeport publié d'un article : empreinte enregistrée, copie publique immuable et sceau de version. Point d'entrée public.

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

---

Vous contrôlez qu'un passeport publié n'a pas été modifié depuis sa
publication. En quittant cette page, vous saurez demander ce contrôle à partir
de n'importe quel identifiant d'article, lire les trois verdicts que la réponse
rend, et distinguer un passeport altéré d'un contrôle qui n'a pas pu aboutir.

Adresse complète :

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

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

> [!INFO] Ce point d'entrée ne rend pas le contenu du passeport
> Il rend uniquement des verdicts d'intégrité et des empreintes. Pour lire les
> données du passeport, appelez
> [`GET /passport/{identifier}`](/reference/get-passport-identifier/). Pour
> obtenir les preuves publiques rassemblées, dont l'ancrage sur la chaîne Base,
> appelez [`GET /passport/{identifier}/proof`](/reference/get-passport-proof/).

## 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.

## 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/0ABCDEFGHJKM/verify` et `/passport/0ABCDEFGHJKM/verify`
remplissent le même compteur.

Prévoyez un délai d'attente d'au moins 30 secondes côté client. Pendant votre
appel, ce point d'entrée va chercher la copie publique immuable sur des
passerelles publiques. Il en interroge plusieurs l'une après l'autre, chacune
avec sa propre limite de temps, donc un premier appel sur une copie que le
serveur n'a encore jamais lue peut durer une vingtaine de secondes. Les appels
suivants sur la même copie répondent sans aller la rechercher. Quand aucune
passerelle ne répond, la réponse reste 200 et `ipfs_match` vaut `null`.

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 contrôler le passeport. 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'article | `0x` suivi de 64 caractères hexadécimaux | Pour un article NFC, l'empreinte de l'identifiant lu sur la puce. Pour un article QR, une empreinte que le serveur tire au hasard au moment de la frappe. Les deux ont la même forme et vous les utilisez de la même façon. |
| Identifiant de jeton | un entier de 256 bits écrit en décimal, 77 ou 78 chiffres | L'identifiant de l'article sur la chaîne. Traitez-le comme une chaîne de caractères : il dépasse un entier 64 bits. |
| Numéro de série imprimé | 12 caractères | Ce que porte le QR code sur le produit, dans l'adresse `/p/{serial}`. |

Le serveur reconnaît l'empreinte d'article sans distinction de casse. Il
reconnaît le numéro de série de la même façon, et il ramène les caractères qui
se ressemblent à une forme unique avant de chercher : un `I` ou un `L` que vous
saisissez à la main retrouve le `1`, un `O` retrouve le `0`.

Ce point d'entrée n'accepte pas le numéro de certificat d'authenticité. Vous
l'employez sur `GET /certificate/{identifier}`.

### 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

Contrôle d'intégrité du passeport de l'article dont le numéro de série imprimé
est `0ABCDEFGHJKM`. Les exemples utilisent cette forme parce qu'elle tient sur
12 caractères. Un identifiant de jeton s'écrit au même endroit dans l'adresse,
sur 77 ou 78 chiffres. Les trois exemples posent le même délai d'attente de 30
secondes, pour la raison donnée plus haut.

> [!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 --max-time 30 https://api.sealtrust.io/v1/passport/0ABCDEFGHJKM/verify
```
```typescript
const reponse = await fetch(
  "https://api.sealtrust.io/v1/passport/0ABCDEFGHJKM/verify",
  { signal: AbortSignal.timeout(30000) },
);

if (reponse.status === 404) {
  console.log("Aucun passeport public à contrôler pour cet article.");
} else if (reponse.ok) {
  const controle = await reponse.json();
  console.log(controle.db_hash_match);
  console.log(controle.ipfs_match, controle.ipfs_uri);
  console.log(controle.seal.sealed, controle.seal.chain_link_match);
} else {
  console.log(reponse.status, await reponse.json());
}
```
```python
import requests

response = requests.get(
    "https://api.sealtrust.io/v1/passport/0ABCDEFGHJKM/verify",
    timeout=30,
)

if response.status_code == 404:
    print("Aucun passeport public à contrôler pour cet article.")
elif response.ok:
    controle = response.json()
    print(controle["db_hash_match"])
    print(controle["ipfs_match"], controle["ipfs_uri"])
    print(controle["seal"]["sealed"], controle["seal"]["chain_link_match"])
else:
    print(response.status_code, response.json())
```
:::

## Réponse d'exemple

Code HTTP `200`.

```json
{
  "db_hash_match": true,
  "ipfs_match": true,
  "ipfs_uri": "ipfs://bafybeiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "ipfs_gateway_url": "https://passerelle.exemple.invalid/ipfs/bafybeiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "data_hash": "1111111111111111111111111111111111111111111111111111111111111111",
  "computed_hash": "1111111111111111111111111111111111111111111111111111111111111111",
  "passport_version": 3,
  "seal": {
    "sealed": true,
    "sealed_at": "2026-05-14T09:12:44+00:00",
    "algorithm": "st-dpp-chain-v1",
    "version_hash": "2222222222222222222222222222222222222222222222222222222222222222",
    "prev_version_hash": "3333333333333333333333333333333333333333333333333333333333333333",
    "linked": true,
    "chain_link_match": true
  }
}
```

La réponse compte huit champs et rien d'autre.

| Champ | Type | Description |
| --- | --- | --- |
| `db_hash_match` | `boolean` ou `null` | `true` quand les données enregistrées du passeport correspondent encore à l'empreinte enregistrée avec elles. `null` quand aucune empreinte n'a été enregistrée pour cette version. |
| `ipfs_match` | `boolean` ou `null` | `true` quand la copie publique immuable correspond à la version publique du passeport. `false` quand elle en diffère. `null` quand aucune copie n'existe, ou quand aucune passerelle n'a répondu. |
| `ipfs_uri` | `string` ou `null` | L'adresse `ipfs://` de la copie publique immuable. Renseignée uniquement quand cette copie est le passeport public que la marque publie aujourd'hui, voir ci-dessous. |
| `ipfs_gateway_url` | `string` ou `null` | La même copie, sous forme d'adresse HTTP ouvrable dans un navigateur. Renseignée aux mêmes conditions que `ipfs_uri`. |
| `data_hash` | `string` ou `null` | L'empreinte enregistrée avec cette version du passeport, 64 caractères hexadécimaux. `null` quand aucune empreinte n'a été enregistrée. |
| `computed_hash` | `string` | L'empreinte recalculée au moment de votre appel à partir des données enregistrées, 64 caractères hexadécimaux. Elle porte sur les données complètes du passeport, y compris les champs que ce point d'entrée ne rend pas et que la version publique du passeport ne rend pas non plus. Vous ne pouvez donc pas la reproduire vous-même à partir de données publiques. |
| `passport_version` | `integer` | Le numéro de version du passeport contrôlé. Il démarre à 1 et augmente d'une unité à chaque nouvelle version du passeport. |
| `seal` | `object` | Le sceau de cette version et sa place dans la chaîne des versions, voir ci-dessous. |

### Quel passeport le serveur contrôle

Le contrôle porte sur la dernière version publiée dont la visibilité est
publique. Le serveur cherche d'abord le passeport propre à l'article. À défaut,
il contrôle le passeport du modèle, partagé par tous les articles du modèle.

La réponse ne dit pas laquelle des deux portées a répondu. Les deux numérotent
leurs versions séparément, donc `passport_version` peut valoir 1 dans les deux
cas.

Le serveur ne contrôle jamais ici un passeport réservé au propriétaire ni un
passeport réservé à la marque. La réponse est alors 404, comme si aucun
passeport n'était publié.

### Quand le serveur vous rend l'adresse `ipfs_uri`

Vous recevez l'adresse de la copie publique lorsque son contenu est exactement
le passeport public que la marque publie aujourd'hui. Sinon, `ipfs_uri` et
`ipfs_gateway_url` valent `null`.

Une marque qui change ses règles d'accès change ce que montre son passeport
public. Une copie déposée avant ce changement n'est donc plus annoncée tant
que la marque n'en a pas déposé une nouvelle. Ne lisez jamais ces deux `null`
comme un verdict sur la copie.

`ipfs_match` à `null` ne dit rien sur l'intégrité de la copie. Réessayez plus
tard avant de conclure.

### Le bloc `seal`

| Champ | Type | Description |
| --- | --- | --- |
| `sealed` | `boolean` | `false` quand la version n'est pas scellée. Le bloc s'arrête alors là et ne porte aucun autre champ. |
| `sealed_at` | `string` | La date et l'heure du scellement, au format ISO 8601. |
| `algorithm` | `string` | La version de l'algorithme de chaînage. Vaut aujourd'hui `st-dpp-chain-v1`. |
| `version_hash` | `string` ou `null` | L'empreinte scellée de cette version, 64 caractères hexadécimaux. |
| `prev_version_hash` | `string` ou `null` | L'empreinte scellée de la version précédente du même passeport. `null` pour la toute première version. |
| `linked` | `boolean` | `true` quand cette version porte une empreinte scellée et prend donc sa place dans la chaîne. |
| `reason` | `string` | Présent uniquement quand `linked` vaut `false`. Vaut alors `sealed_before_chain` : vous avez publié cette version avant l'existence du chaînage, et le serveur n'en fabrique aucun après coup. |
| `chain_link_match` | `boolean` | Présent uniquement quand `linked` vaut `true`. `false` veut dire que les données enregistrées ne correspondent plus à ce qui a été scellé. |

Le bloc `seal` prend donc trois formes, et vous devez pouvoir les distinguer
sans rien déduire d'un champ absent.

```json
{ "sealed": false }
```

```json
{
  "sealed": true,
  "sealed_at": "2025-11-02T08:30:00+00:00",
  "algorithm": "st-dpp-chain-v1",
  "version_hash": null,
  "prev_version_hash": null,
  "linked": false,
  "reason": "sealed_before_chain"
}
```

```json
{
  "sealed": true,
  "sealed_at": "2026-05-14T09:12:44+00:00",
  "algorithm": "st-dpp-chain-v1",
  "version_hash": "2222222222222222222222222222222222222222222222222222222222222222",
  "prev_version_hash": "3333333333333333333333333333333333333333333333333333333333333333",
  "linked": true,
  "chain_link_match": true
}
```

### Quel verdict lire en premier

`chain_link_match` à `false` est le signal qu'une version scellée a été
modifiée après sa publication. C'est le verdict le plus fort de cette réponse.
Il vient d'un recalcul fait au moment de votre appel.

`ipfs_match` à `false` porte le même genre de signal sur la copie publique.

> [!ATTENTION] `db_hash_match` est le verdict le plus faible des trois
> Il compare les données enregistrées avec l'empreinte enregistrée à côté
> d'elles. Les deux valeurs sont chez nous. Ce verdict détecte une corruption,
> il ne prouve rien contre nous. `ipfs_match` et `chain_link_match` sont les
> deux verdicts qu'un tiers peut opposer à quelque chose que nous ne pouvons
> pas réécrire.

## Erreurs

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

| Code | Condition | Que faire |
| --- | --- | --- |
| 404 | Aucun article 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 public n'est publié pour lui ni pour son modèle. `detail` vaut `No published passport found for this product`. | Ne traitez pas cette réponse comme un échec du contrôle. Il n'y a rien à contrôler. Un passeport réservé au propriétaire ou à la marque donne aussi cette réponse. |
| 429 | Le plafond de 60 appels par 60 secondes est atteint pour votre adresse réseau, tous chemins commençant par `/passport` confondus. `detail` vaut `Rate limit exceeded: 60 requests per 60s`. | Attendez le nombre de secondes indiqué par `Retry-After`, puis réessayez. |
| 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 passerelle injoignable ne produit pas d'erreur HTTP. La réponse reste 200
et `ipfs_match` vaut `null`.

## Voir aussi

- [`GET /passport/{identifier}/proof`](/reference/get-passport-proof/),
  rassembler les preuves publiques du passeport d'un article.
- [`GET /passport/{identifier}`](/reference/get-passport-identifier/),
  lire le passeport publié d'un article.
- [`GET /certificate/{identifier}`](/reference/get-certificate/),
  lire le certificat d'authenticité d'un article.
- [Confiance et preuves](/confiance-et-preuves/),
  ce que chaque preuve établit et comment un tiers refait la vérification.
