# GET /partner-portal/products/{identifier}

Retrouver un produit d'une marque qui vous a accrédité, à partir de ce qui est écrit sur l'objet, et lire son passeport filtré sur les seules accréditations que cette marque vous a reconnues.

Source : https://docs.sealtrust.io/reference/get-partner-portal-products/

---

Vous retrouvez un produit à partir de l'identifiant lu sur l'objet, et vous
recevez son passeport filtré sur les accréditations que cette marque vous a
reconnues, et sur elles seules. En
quittant cette page, vous saurez quels identifiants ce point d'entrée accepte,
ce que contient la réponse, et quelles erreurs il renvoie.

Ce point d'entrée appartient au portail partenaire. Il est réservé aux comptes
réparateur et recycleur, et il s'authentifie avec la session du compte. La clef
d'API de la marque n'a pas cours ici.

Adresse complète :

```http
GET https://api.sealtrust.io/v1/partner-portal/products/{identifier}
```

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

## Autorisation

Session d'un compte partenaire. Vous ouvrez cette session avec
`POST /v1/auth/login`, qui rend un jeton d'accès et pose aussi un cookie de
session. Vous présentez ensuite le jeton dans l'en-tête `Authorization`, au
format `Bearer`. Il vaut 60 minutes.

Vous devez remplir trois conditions, dans cet ordre.

1. Votre session est valide et votre compte est actif. Sinon la réponse est 401.
2. Votre compte est de type réparateur ou recycleur. Un compte d'un autre type
   reçoit 403.
3. Au moins une marque vous a accrédité, et cette accréditation est active. Sans
   cela, la réponse est 403 avant même toute recherche de produit.

L'API cherche uniquement parmi les marques qui vous ont accrédité. Elle ne vous
rend jamais un produit d'une autre marque.

> [!ATTENTION] Une clef d'API ne fonctionne pas sur ce chemin
> Les clefs d'API servent les points d'entrée `/v1/partner/*`, qui sont une
> surface différente. Si vous présentez une clef d'API ici, l'API répond 401.

Le portail est prévu pour l'application partenaire. L'API accepte aussi un appel
serveur à serveur qui porte le jeton dans l'en-tête `Authorization`.

L'API refuse en 403 un appel qui s'appuie sur le cookie de session et qui vient
d'une origine que l'API n'accepte pas.

## Plafond d'appels

Un plafond d'appels s'applique à ce point d'entrée. Il est réglé pour l'usage
normal du portail, où vous cherchez un produit puis enregistrez une
intervention.

Au-delà, l'API répond 429. Le refus porte un en-tête `Retry-After` qui donne le
nombre de secondes à attendre. Attendez ce délai, puis rappelez.

Le plafond couvre l'ensemble du portail partenaire. Alterner entre les points
d'entrée ne vous redonne donc pas de marge. Espacez vos appels au lieu de les
envoyer en rafale.

La valeur du plafond n'est pas un engagement et peut changer sans préavis.
N'inscrivez aucun seuil en dur dans votre code, appuyez-vous sur `Retry-After`.

> [!INFO] Cet appel ne décompte aucun quota
> Cet appel n'entame pas le quota mensuel de produits de la marque. Aucun quota
> de clef d'API n'entre en jeu non plus, puisque ce point d'entrée n'utilise pas
> de clef d'API.

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

| Nom | Type | Obligatoire | Description |
| --- | --- | --- | --- |
| `identifier` | `string` | oui | Ce qui identifie le produit. L'API accepte quatre formes, voir ci-dessous. |

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

L'API essaie les quatre formes dans cet ordre.

| Forme | À quoi elle ressemble | Remarque |
| --- | --- | --- |
| Empreinte d'identifiant physique | commence par `0x` | La casse est ignorée. |
| Numéro de jeton | uniquement des chiffres | Le numéro attribué au produit sur la chaîne. |
| Numéro de certificat | tel qu'il figure sur le certificat | Comparé à l'identique, sans tolérance de casse. |
| Numéro de série | 12 caractères, celui imprimé sur l'objet | Voir la tolérance de saisie ci-dessous. |

Le numéro de série est la seule forme qu'un humain a sous les yeux. Il est écrit
dans un alphabet qui exclut les caractères que l'oeil confond. À la lecture,
l'API met la saisie en majuscules, puis elle ramène `I` et `L` sur `1`, et `O`
sur `0`. Vous pouvez donc saisir la lettre `I`, l'API la lit comme le chiffre
`1`. Un opérateur qui recopie une étiquette n'est pas puni pour une confusion de
caractère. L'API ne traite comme numéro de série qu'une chaîne de 12 caractères
exactement, tous pris dans cet alphabet.

L'API ne résout pas un produit détruit, ni un produit que la marque a retiré du
catalogue. La réponse est alors 404.

### En-têtes

| Nom | Type | Obligatoire | Description |
| --- | --- | --- | --- |
| `Authorization` | `string` | non | `Bearer` suivi du jeton de session rendu par la connexion. Nécessaire si votre appel ne porte pas le cookie de session. |

## Corps de la requête

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

## Requête d'exemple

Recherche du produit dont le numéro de série imprimé est `EXEMPLE00001`.
Remplacez `VOTRE_JETON_DE_SESSION` par le jeton d'accès que la connexion vous a
rendu.

Le SDK TypeScript ne couvre pas cette surface, l'onglet TypeScript montre donc
un appel `fetch` direct.

:::onglets
```bash title="curl"
curl -i https://api.sealtrust.io/v1/partner-portal/products/EXEMPLE00001 \
  -H "Authorization: Bearer VOTRE_JETON_DE_SESSION"
```
```typescript
const reponse = await fetch(
  "https://api.sealtrust.io/v1/partner-portal/products/EXEMPLE00001",
  {
    method: "GET",
    headers: {
      Authorization: "Bearer VOTRE_JETON_DE_SESSION",
    },
  },
);

console.log(reponse.status);
console.log(await reponse.json());
```
```python
import requests

response = requests.get(
    "https://api.sealtrust.io/v1/partner-portal/products/EXEMPLE00001",
    headers={
        "Authorization": "Bearer VOTRE_JETON_DE_SESSION",
    },
    timeout=30,
)

print(response.status_code)
print(response.json())
```
:::

> [!INFO] Le SDK TypeScript ne couvre pas le portail partenaire
> `@sealtrust-io/sdk` s'authentifie avec une clef d'API et sert les points
> d'entrée `/v1/partner/*`. Appelez ce chemin avec `fetch`, comme ci-dessus.

## Réponse d'exemple

Code HTTP `200`.

```json
{
  "product": {
    "product_id": 4821,
    "token_id": "1029384756",
    "uid_hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "product_name": "Sac Modèle A",
    "brand_id": 12,
    "brand_name": "Exemple SAS",
    "category_name": "Maroquinerie",
    "status": "written"
  },
  "passport": {
    "available": true,
    "access_tier": "recycler",
    "passport_version": 3,
    "data": {
      "product_identity": {
        "gtin": "03701234567890",
        "model": "Modèle A",
        "brand": "Exemple SAS",
        "made_in": "FR"
      },
      "materials": {
        "primary": { "name": "Cuir pleine fleur", "percentage": 70, "origin": "IT" },
        "certified_organic": false
      },
      "circularity": {
        "repairability_index": 7.8,
        "expected_lifetime_years": 15,
        "disassembly_instructions_url": "https://exemple-sas.test/demontage/modele-a"
      },
      "compliance": {
        "eu_espr": true,
        "reach": true
      }
    }
  },
  "allowed_event_types": [
    "after_sale_service",
    "maintenance",
    "reconditioning",
    "repair"
  ]
}
```

La réponse compte trois champs de premier niveau.

| Champ | Type | Description |
| --- | --- | --- |
| `product` | `object` | Le produit retrouvé. Huit champs, voir ci-dessous. |
| `passport` | `object` | Le passeport, filtré pour votre niveau d'accès. Quatre champs, voir ci-dessous. |
| `allowed_event_types` | `string[]` | Les types d'intervention que vos accréditations sur cette marque vous permettent d'enregistrer. Liste triée par ordre alphabétique. |

### Le bloc `product`

| Champ | Type | Description |
| --- | --- | --- |
| `product_id` | `integer` | Le numéro du produit. C'est lui qui relie vos interventions à cet objet. |
| `token_id` | `string` ou `null` | Le numéro du jeton sur la chaîne. Vaut `null` tant que la frappe n'est pas confirmée. |
| `uid_hash` | `string` ou `null` | L'empreinte de l'identifiant physique. |
| `product_name` | `string` ou `null` | Le nom du produit. |
| `brand_id` | `integer` ou `null` | Le numéro de la marque propriétaire. |
| `brand_name` | `string` ou `null` | Le nom de la marque. Vaut `null` si la marque n'est plus lisible. |
| `category_name` | `string` ou `null` | Le nom de la catégorie. Vaut `null` si le produit n'a pas de catégorie. |
| `status` | `string` ou `null` | L'état du produit. Voir la liste ci-dessous. |

`status` prend l'une de ces valeurs : `draft`, `minting`, `mined`, `written`,
`burn_submitted`, `burned`, `superseded`, `archived`, `stolen`, `revoked`. Les
états `superseded` et `archived` sortent le produit du catalogue. Ce point
d'entrée ne rend jamais un produit dans l'un de ces deux états, ni un produit
détruit.

> [!ATTENTION] Vous ne voyez que votre métier
> Le passeport que ce portail vous rend est filtré sur les accréditations que
> la marque vous a effectivement reconnues, et sur elles seules. Un recycleur
> accrédité voit les matériaux et les substances préoccupantes ; il ne voit pas
> les données de fabrication ni la provenance amont, qui relèvent d'un autre
> métier. Détenir une accréditation n'en ouvre aucune autre.

### Le bloc `passport`

| Champ | Type | Description |
| --- | --- | --- |
| `available` | `boolean` | `true` quand ce produit a un passeport publié. |
| `access_tier` | `string` | Les métiers réellement servis, joints par un `+` et rangés par ordre alphabétique : `recycler`, ou `repairer+recycler` pour un partenaire qui porte les deux accréditations sur cette marque. Vaut `public` quand la marque ne vous en a reconnu aucune, et le passeport est alors filtré au niveau public. |
| `passport_version` | `integer` ou `null` | Le numéro de version du passeport rendu. Vaut `null` quand il n'y a pas de passeport. |
| `data` | `object` ou `null` | Le contenu du passeport, filtré. Vaut `null` quand il n'y a pas de passeport. |

Quand l'API ne trouve aucun passeport, le bloc vaut
`{"available": false, "access_tier": "public", "passport_version": null, "data": null}`.
L'API rend le reste de la réponse normalement. Vous pouvez enregistrer une
intervention sur un produit sans passeport.

Trois règles décident du passeport rendu.

- L'API ne rend qu'un passeport publié, en visibilité publique ou réservée au
  propriétaire. Elle ne rend pas un brouillon, ni un passeport que la marque
  garde pour son usage interne.
- L'API cherche d'abord le passeport propre à l'exemplaire que vous avez
  résolu. À défaut, et si le produit est rattaché à un modèle, elle rend le
  passeport de référence de ce modèle. Un exemplaire sans passeport propre est
  donc décrit par la référence de son modèle. Les données par unité que vous
  lisez portent toujours sur l'exemplaire que vous avez résolu.
- Quand plusieurs versions existent, l'API rend la plus élevée.

> [!ATTENTION] `data` ne contient pas tout le passeport
> L'API filtre le contenu sur vos accréditations, puis selon les règles que la
> marque a définies. Deux marques peuvent donc vous ouvrir des champs
> différents pour un produit comparable. L'exemple ci-dessus est un passeport
> de maroquinerie. La forme du contenu dépend du passeport publié.

### Le bloc `allowed_event_types`

Ces valeurs sont exactement celles que
`POST /v1/partner-portal/interventions` accepte pour ce produit. Ce point
d'entrée refuse en 422 tout type absent de cette liste.

| Type d'accréditation | Types d'intervention rendus |
| --- | --- |
| Réparateur | `after_sale_service`, `maintenance`, `reconditioning`, `repair` |
| Recycleur | `destruction`, `end_of_life`, `recycling`, `return` |

Un compte qui détient les deux accréditations actives sur la même marque reçoit
les huit valeurs. L'API calcule cette liste marque par marque. Un même compte
peut donc recevoir une liste différente pour un produit d'une autre marque.

## Erreurs

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

| Code | Condition | Que faire |
| --- | --- | --- |
| 401 | Vous ne présentez aucune session : ni en-tête `Authorization`, ni cookie de session. `detail` vaut `Not authenticated`. La réponse porte aussi `WWW-Authenticate: Bearer`. | Connectez-vous, puis présentez le jeton rendu. |
| 401 | Le jeton est illisible, mal signé ou expiré. `detail` vaut `Invalid JWT token`. | Renouvelez votre session. Un jeton d'accès vaut 60 minutes. |
| 401 | Le jeton que vous présentez n'est pas un jeton de session. `detail` vaut `Invalid token`. | Utilisez le jeton d'accès rendu par la connexion. |
| 401 | Le jeton que vous présentez est celui d'une connexion arrêtée à l'étape de vérification en deux temps. `detail` vaut `MFA verification required`. | Terminez la vérification en deux temps, puis utilisez le jeton rendu à la fin. |
| 401 | Une déconnexion ou un changement de mot de passe a révoqué ce jeton. `detail` vaut `Token has been revoked`. | Reconnectez-vous. |
| 401 | Le jeton ne porte pas d'adresse de compte. `detail` vaut `Invalid token: missing email`. | Reconnectez-vous. |
| 401 | Votre compte n'est plus actif. `detail` vaut `Account disabled`. | Contactez la marque qui vous a accrédité. |
| 403 | Votre appel porte le cookie de session, sans annoncer ni `Origin` ni `Referer`. `detail` vaut `Origin or Referer header required`. | Appelez depuis l'application partenaire, ou présentez le jeton dans l'en-tête `Authorization` au lieu du cookie. |
| 403 | Votre appel ne porte pas de jeton dans l'en-tête `Authorization`, et il annonce une origine que l'API n'accepte pas. `detail` vaut `Forbidden origin`. | Appelez depuis l'application partenaire, ou depuis votre serveur en présentant le jeton dans l'en-tête `Authorization`. |
| 403 | Votre compte n'est ni réparateur ni recycleur. `detail` vaut `Partner account required (repairer or recycler)`. | Ce chemin ne s'adresse pas à ce type de compte. |
| 403 | Aucune marque ne vous a accrédité, ou vos accréditations ne sont plus actives. `detail` vaut `Aucune accréditation active`. | Demandez à la marque de réactiver votre accréditation. |
| 403 | Vos accréditations ne vous permettent pas d'agir sur ce produit. | Demandez une accréditation à la marque concernée. |
| 404 | Cet identifiant ne désigne aucun produit que vos accréditations vous permettent d'atteindre. `detail` vaut `Produit introuvable`. | Vérifiez la saisie. Un produit détruit ou retiré du catalogue donne la même réponse. |
| 404 | Le compte lié au jeton n'existe plus. `detail` vaut `User not found`. | Reconnectez-vous. Si l'erreur persiste, contactez la marque qui vous a accrédité. |
| 429 | Vous avez dépassé le plafond d'appels. La réponse porte un en-tête `Retry-After`. | Attendez le nombre de secondes indiqué par `Retry-After`, puis réessayez. Répartissez vos appels au lieu de les envoyer en rafale. |
| 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`. |

## Voir aussi

- [`POST /partner-portal/interventions`](/reference/post-partner-portal-interventions/),
  enregistrer une intervention sur un produit.
- [`GET /partner-portal/interventions`](/reference/get-partner-portal-interventions/),
  lister les interventions que votre compte a enregistrées.
- [`GET /partner-portal/me`](/reference/get-partner-portal-me/),
  lire votre profil de partenaire et les marques qui vous ont accrédité.
