# POST /partner-portal/interventions

Enregistrer une intervention sur un produit depuis un compte partenaire réparateur ou recycleur. Session partenaire, aucune clef d'API.

Source : https://docs.sealtrust.io/reference/post-partner-portal-interventions/

---

Vous enregistrez une intervention que vous venez de réaliser sur un produit :
une réparation, un entretien, un reconditionnement, un recyclage. L'intervention
s'ajoute à l'historique du produit, porte votre nom et indique ce que vous avez
prouvé au moment de l'enregistrer.

L'adresse complète est
`https://api.sealtrust.io/v1/partner-portal/interventions`. La même route existe
sans le préfixe `/v1`, et c'est la forme `/v1` qui est recommandée pour une
nouvelle intégration.

Le portail partenaire est une surface différente de l'API à clef. Il
s'authentifie avec la session d'un compte partenaire, et une clef d'API n'y
donne aucun accès.

Cet appel n'est pas rejouable. Deux appels identiques créent deux interventions.
Aucun en-tête d'idempotence n'est lu par ce point d'entrée.

## Autorisation

Session d'un compte partenaire, de type réparateur ou recycleur. Le jeton de
session est celui que renvoie `POST /v1/auth/login`, dans le champ
`access_token`.

```http
Authorization: Bearer <votre jeton de session>
```

Le cookie de session `access_token` est accepté lui aussi. C'est la forme
qu'utilise le navigateur. Elle déclenche les contrôles d'origine et de jeton
anti-falsification décrits plus bas.

Appelez ce point d'entrée depuis votre serveur, avec l'en-tête `Authorization`.
Un appel émis par une page de navigateur passe par des contrôles supplémentaires
d'origine et de falsification de requête, qui renvoient un 403 quand ils ne sont
pas satisfaits.

Quatre conditions doivent être réunies pour qu'une intervention soit
enregistrée.

1. Le compte est de type réparateur ou recycleur. Sinon la réponse est un 403.
2. Le compte a au moins une accréditation active. Sinon la réponse est un 403
   portant le message `Aucune accréditation active`.
3. Le produit appartient à une marque qui a accrédité ce compte. Sinon la
   réponse est un 403 ou un 404.
4. Le type d'intervention est couvert par vos accréditations sur la marque de ce
   produit. Sinon la réponse est un 422 qui liste les types autorisés.

Les accréditations sont accordées par la marque, marque par marque. Un même
compte peut détenir les deux types sur une même marque, et les types
d'intervention autorisés sont alors la réunion des deux ensembles.

| Accréditation | Types d'intervention autorisés |
| --- | --- |
| Réparateur | `repair`, `maintenance`, `reconditioning`, `after_sale_service` |
| Recycleur | `recycling`, `end_of_life`, `destruction`, `return` |

Le point d'entrée `GET /v1/partner-portal/products/{identifier}` rend la liste
`allowed_event_types` calculée pour le produit visé, ce qui évite de deviner.

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

Il n'y a ici ni quota journalier ni compteur par marque : ces deux mécanismes
sont attachés aux clefs d'API, et le portail partenaire n'en utilise pas.

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

Ce point d'entrée n'a aucun paramètre de chemin ni de requête. Tout passe par le
corps de la requête.

## Corps de la requête

Format `application/json`. Tout champ absent de ce tableau fait refuser la
requête en 422.

| Nom | Type | Obligatoire | Description |
| --- | --- | --- | --- |
| `identifier` | `string` | oui | Le produit sur lequel vous êtes intervenu. Les espaces de bord sont retirés. Une valeur vide est refusée. Quatre formes sont acceptées, voir ci-dessous. |
| `event_type` | `string` | oui | Le type d'intervention. La valeur est débarrassée de ses espaces de bord et mise en minuscules avant contrôle. Elle doit figurer parmi les types autorisés par vos accréditations sur la marque du produit. |
| `title` | `string` | oui | Titre court de l'intervention. 255 caractères au maximum. Les espaces de bord sont retirés. Une valeur vide est refusée. |
| `description` | `string` | non | Texte libre. Aucune longueur maximale. Enregistré tel quel. |
| `metadata` | `object` | non | Vos propres informations sur l'intervention. Enregistrées telles quelles. La clef `partner_type` y est ajoutée si vous ne la fournissez pas. |
| `proof_code` | `string` | non | Le code de remise lu par le client, ou le bon de travail émis par la marque. Les espaces et les tirets sont retirés, la valeur est mise en majuscules. Une chaîne vide vaut absence de code. |

### Les quatre formes acceptées pour `identifier`

Elles sont essayées dans cet ordre, et la recherche est bornée aux marques qui
vous ont accrédité.

| Forme | Exemple | Reconnue à |
| --- | --- | --- |
| Empreinte d'étiquette | `0x0000000000000000000000000000000000000000000000000000000000000000` | commence par `0x` |
| Identifiant de jeton | `10000000000000000000000000000000000000000000000000000000000000000000000000000` | ne contient que des chiffres |
| Numéro de certificat | `CERT-EXEMPLE-0001` | correspond à un certificat de la marque |
| Numéro de série imprimé sur le produit | `000000000000` | 12 caractères de l'alphabet Crockford Base32 |

L'identifiant de jeton compte 77 à 78 chiffres. Il est stocké et rendu comme une
chaîne de caractères. Déclarez-le comme une chaîne dans votre intégration, et
dimensionnez le champ en conséquence.

Le numéro de série tolère les confusions de lecture courantes : les lettres `I`
et `L` sont lues comme le chiffre `1`, la lettre `O` comme le chiffre `0`, et la
casse n'a pas d'importance. C'est la forme à privilégier quand vous avez l'objet
en main, parce que c'est la seule qui soit imprimée dessus. Un produit détruit ou
retiré n'est jamais résolu.

### Le niveau de preuve, et comment le relever

Une accréditation dit que vous avez le droit de travailler sur les produits
d'une marque. Elle ne dit pas que ce produit précis est passé entre vos mains,
et l'identifiant est imprimé sur l'objet. Le champ `proof_level` de la réponse
enregistre donc ce que vous avez réellement prouvé.

| `proof_level` | Ce que vous avez envoyé | Ce que cela vaut |
| --- | --- | --- |
| `declared` | aucun `proof_code` | vous avez déclaré l'intervention et vous connaissiez l'identifiant |
| `customer_code` | un code de remise généré par le client final | le détenteur du produit vous l'a remis |
| `work_order` | un bon de travail émis par la marque | la marque vous a confié ce produit précis |

Le client final génère son code de remise depuis son compte, par
`POST /v1/custody/repair-codes`. Le code fait 8 caractères et n'est affiché
qu'une fois. Il vaut pour un seul produit, ne sert qu'une fois, et expire au
bout de 30 jours par défaut, une durée que l'émetteur peut fixer entre 1 et 365
jours. Le bon de travail est émis par la marque depuis sa console, et il nomme à
la fois le produit et votre compte.

Un code refusé annule tout l'appel, et rien n'est enregistré. Si le code ne
passe pas, vérifiez-le auprès du client, ou enregistrez l'intervention sans
`proof_code`, au niveau `declared`.

> [!INFO] Un niveau de preuve ne se relève jamais après coup
> Il est écrit à la création de l'intervention et n'est plus modifiable. Si le
> client vous tend son code, envoyez-le dans ce même appel.

## Requête d'exemple

:::onglets
```bash title="curl"
curl -i -X POST https://api.sealtrust.io/v1/partner-portal/interventions \
  -H "Authorization: Bearer VOTRE_JETON_DE_SESSION" \
  -H "Content-Type: application/json" \
  -d '{
    "identifier": "000000000000",
    "event_type": "repair",
    "title": "Remplacement de la fermeture éclair",
    "description": "Ancienne fermeture remplacée par une pièce du fabricant.",
    "metadata": {
      "duree_minutes": 45,
      "pieces": ["fermeture éclair"]
    },
    "proof_code": "23456789"
  }'
```
```typescript
const response = await fetch(
  "https://api.sealtrust.io/v1/partner-portal/interventions",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer VOTRE_JETON_DE_SESSION",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      identifier: "000000000000",
      event_type: "repair",
      title: "Remplacement de la fermeture éclair",
      description: "Ancienne fermeture remplacée par une pièce du fabricant.",
      metadata: {
        duree_minutes: 45,
        pieces: ["fermeture éclair"],
      },
      proof_code: "23456789",
    }),
  },
);

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

response = requests.post(
    "https://api.sealtrust.io/v1/partner-portal/interventions",
    headers={
        "Authorization": "Bearer VOTRE_JETON_DE_SESSION",
        "Content-Type": "application/json",
    },
    json={
        "identifier": "000000000000",
        "event_type": "repair",
        "title": "Remplacement de la fermeture éclair",
        "description": "Ancienne fermeture remplacée par une pièce du fabricant.",
        "metadata": {
            "duree_minutes": 45,
            "pieces": ["fermeture éclair"],
        },
        "proof_code": "23456789",
    },
    timeout=30,
)

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

> [!INFO] Le SDK TypeScript ne couvre pas le portail partenaire
> Le paquet `@sealtrust-io/sdk` expose la frappe en lot, la vérification et les
> abonnements aux notifications, toutes des opérations à clef d'API. Le portail
> partenaire s'appelle donc en HTTP direct, comme ci-dessus.

## Réponse d'exemple

Code HTTP 201.

```json
{
  "id": 8123,
  "product_id": 4096,
  "brand_id": 12,
  "event_type": "repair",
  "proof_level": "customer_code",
  "title": "Remplacement de la fermeture éclair",
  "description": "Ancienne fermeture remplacée par une pièce du fabricant.",
  "event_metadata": {
    "duree_minutes": 45,
    "pieces": ["fermeture éclair"],
    "partner_type": "repairer"
  },
  "performed_by": "Atelier Exemple (réparateur accrédité)",
  "product_name": "Sac de voyage Exemple SAS",
  "occurred_at": "2026-08-20T14:32:07.512430+00:00",
  "created_at": "2026-08-20T14:32:07.512430+00:00"
}
```

> [!ATTENTION] Le champ change de nom entre la requête et la réponse
> Vous envoyez `metadata`. La réponse le rend sous le nom `event_metadata`. Le
> contenu est identique. La clef change de nom. Une intégration qui relit
> `metadata` dans la réponse lit `undefined`.

Les douze champs de la réponse.

| Champ | Type | Description |
| --- | --- | --- |
| `id` | `integer` | Identifiant de l'intervention, attribué par ordre de création. Ne l'exposez pas publiquement. |
| `product_id` | `integer` | Identifiant du produit résolu à partir de `identifier`. |
| `brand_id` | `integer` | Identifiant de la marque du produit. |
| `event_type` | `string` | Le type d'intervention enregistré, en minuscules. |
| `proof_level` | `string` ou `null` | `declared`, `customer_code` ou `work_order`. Voir le tableau plus haut. |
| `title` | `string` | Le titre, débarrassé de ses espaces de bord. |
| `description` | `string` ou `null` | La description, telle que vous l'avez envoyée. |
| `event_metadata` | `object` ou `null` | Ce que vous avez envoyé dans `metadata`, augmenté de la clef `partner_type`. |
| `performed_by` | `string` ou `null` | Votre identité, telle qu'elle apparaîtra dans l'historique du produit. |
| `product_name` | `string` ou `null` | Nom du produit tel qu'il est enregistré. |
| `occurred_at` | `string` | Date et heure de l'intervention, au format ISO 8601 avec fuseau. |
| `created_at` | `string` | Date et heure de l'enregistrement, au format ISO 8601 avec fuseau. |

### Ce que contient `performed_by`

Ce champ est construit à partir de votre compte, suivi de la qualité dans
laquelle vous êtes intervenu, par exemple `réparateur accrédité`. La valeur
retenue est votre prénom et votre nom, à défaut le nom de votre société, à
défaut l'adresse e-mail du compte. Renseignez votre nom ou votre société dans
votre profil si vous ne voulez pas que votre adresse e-mail figure dans
l'historique des produits.

### Ce que contient `partner_type`

La clef `partner_type` est ajoutée à `metadata` quand vous ne la fournissez pas.
Elle vaut `repairer` pour les quatre types d'intervention de réparation, et
`recycler` pour les quatre types de fin de vie. Si vous envoyez vous-même cette
clef, votre valeur est conservée.

## Erreurs

| Code | Condition | Que faire |
| --- | --- | --- |
| 401 | Ni en-tête `Authorization`, ni cookie de session `access_token`. Message `Not authenticated`, avec l'en-tête `WWW-Authenticate: Bearer`. | Ajoutez l'en-tête `Authorization: Bearer <votre jeton de session>`. |
| 401 | Jeton illisible, expiré ou mal signé. Message `Invalid JWT token`. | Reconnectez-vous par `POST /v1/auth/login` et reprenez le jeton renvoyé. |
| 401 | Jeton révoqué par une déconnexion ou un changement de mot de passe. Message `Token has been revoked`. | Reconnectez-vous. |
| 401 | Jeton émis pour autre chose qu'une session, par exemple une confirmation d'adresse. Message `Invalid token`. Un jeton en attente de second facteur donne `MFA verification required`. | Terminez la connexion et utilisez le jeton de session qu'elle renvoie. |
| 401 | Le jeton est signé mais ne désigne aucun compte. Message `Invalid token: missing email`. | Reconnectez-vous par `POST /v1/auth/login` et reprenez le jeton renvoyé. |
| 401 | Compte désactivé. Message `Account disabled`. | Contactez la marque qui vous a accrédité. Réessayer ne changera rien. |
| 403 | Le compte n'est ni réparateur ni recycleur. Message `Partner account required (repairer or recycler)`. | Utilisez le compte partenaire que la marque a créé pour vous. |
| 403 | Le compte n'a aucune accréditation active. Message `Aucune accréditation active`. | Demandez à la marque de vous accréditer, ou de réactiver votre accréditation. |
| 403 | Le produit n'est pas dans votre périmètre d'accréditation. Message `Ce produit appartient à une marque qui ne vous a pas accrédité`. | Ce produit n'est pas dans votre périmètre. Demandez une accréditation à cette marque. |
| 403 | L'appel vient d'un navigateur, depuis une origine que nous n'autorisons pas, ou sans indiquer son origine. Messages `Forbidden origin` et `Origin or Referer header required`. | Appelez ce point d'entrée depuis votre serveur, avec l'en-tête `Authorization`. |
| 403 | L'appel vient d'un navigateur et le jeton anti-falsification manque ou ne correspond pas. Message `bad_csrf`. | Appelez ce point d'entrée depuis votre serveur, avec l'en-tête `Authorization`. |
| 404 | Aucun produit accessible ne correspond à `identifier`. Message `Produit introuvable`. | Vérifiez l'identifiant et sa forme. Un produit détruit ou retiré répond la même chose. |
| 404 | Le compte que désigne le jeton n'existe plus. Message `User not found`. | Le compte a été supprimé. Contactez la marque qui vous a accrédité. |
| 422 | Corps invalide : champ obligatoire absent, champ inconnu, `identifier`, `event_type` ou `title` vide. | Le corps de la réponse liste les champs fautifs et le motif de chaque refus. Corrigez et rappelez. |
| 422 | Le `title` dépasse 255 caractères. Le corps de la réponse ne nomme pas le champ fautif. | Raccourcissez le titre et mettez le détail dans `description`, qui n'a aucune longueur maximale. |
| 422 | Le type d'intervention n'est couvert par aucune de vos accréditations sur cette marque. Le message liste les types autorisés. | Choisissez un type de la liste rendue. Si aucun ne convient, demandez à la marque l'accréditation correspondante. |
| 422 | Le `proof_code` ne correspond à aucun code émis pour ce produit. Message `Code inconnu pour ce produit.` | Vérifiez que le code a bien été émis pour ce produit précis. Rappelez sans `proof_code` pour enregistrer l'intervention au niveau `declared`. |
| 422 | Le code a déjà été utilisé. Message `Ce code a déjà servi.` | Un code ne sert qu'une fois. Demandez-en un nouveau, ou rappelez sans `proof_code`. |
| 422 | Le code a été annulé par son émetteur. Message `Ce code a été annulé par son émetteur.` | Demandez au client ou à la marque d'en émettre un nouveau. |
| 422 | Le code a dépassé sa date de validité. Message `Ce code a expiré.` | Demandez-en un nouveau. |
| 422 | Le bon de travail nomme un autre partenaire que vous. Message `Ce bon de travail a été émis pour un autre partenaire.` | Ce bon ne vous est pas destiné. Demandez à la marque d'en émettre un à votre nom. |
| 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 rappelez. Le plafond couvre tout le portail partenaire, espacez donc l'ensemble de vos appels. |

## Voir aussi

- [`GET /partner-portal/interventions`](/reference/get-partner-portal-interventions/),
  lister les interventions que votre compte a enregistrées.
- [`GET /partner-portal/products/{identifier}`](/reference/get-partner-portal-products/),
  retrouver un produit d'une marque qui vous a accrédité.
- [`GET /partner-portal/me`](/reference/get-partner-portal-me/),
  lire votre profil de partenaire et les marques qui vous ont accrédité.
