Méthode POST/partner /sellout
Déclarer qu'un produit a été remis au client final, depuis le système d'un distributeur. Droit sellout:write.
Sur cette page
Vous déclarez qu'un produit a quitté le rayon et a été remis au client final, en nommant le point de vente qui l'a vendu.
L'adresse complète est https://api.sealtrust.io/v1/partner/sellout. 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.
Cet appel est rejouable. Un produit déjà activé renvoie l'activation existante
avec status à already_activated, et complète seulement les informations qui
manquaient : le rattachement au point de vente, le pays, la ville. Rien n'est
écrasé. L'en-tête Idempotency-Key n'est pas lu par ce point d'entrée.
#Autorisation
Clef d'API dans l'en-tête Authorization, au format Bearer, avec le droit
sellout:write.
Authorization: Bearer votre_clefUne clef sans ce droit reçoit un 403 dont le message nomme le droit manquant.
L'offre de votre marque doit par ailleurs comprendre l'accès API. Sinon vous
recevez un 403 dont le champ detail porte le code FEATURE_NOT_AVAILABLE.
#Plafond d'appels
Deux plafonds distincts s'appliquent.
| Plafond | Valeur | Ce qui le déclenche |
|---|---|---|
| Débit | 120 appels par fenêtre de 60 secondes par défaut, la valeur réelle dépend de votre offre | un compteur par clef et un compteur pour la somme des clefs de votre marque, avec le même plafond |
| Quota journalier | celui attaché à votre clef, vide signifie illimité | 1 unité par appel, prélevée une fois le point de vente validé, avant la résolution du produit |
Le compteur de débit repart de zéro à chaque nouvelle fenêtre de 60 secondes. Le quota journalier repart de zéro au passage de minuit en temps universel.
Toute réponse qui passe le plafond de débit porte les en-têtes
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset et
X-RateLimit-Scope. X-RateLimit-Reset donne l'instant de la remise à zéro,
en secondes depuis le 1er janvier 1970 en temps universel. X-RateLimit-Scope
vaut key ou brand, et désigne le compteur le plus contraignant des deux.
Créer des clefs supplémentaires n'augmente pas le débit total autorisé à votre
marque.
#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 vendu. De 1 à 200 caractères. Nous acceptons quatre formes, voir ci-dessous. |
retailer_code | string | oui | Le code du point de vente, de 1 à 64 caractères. C'est celui que vous avez défini dans votre console, section Distribution. |
country | string | non | Pays de la vente, code ISO à deux lettres. Nous mettons la valeur en majuscules. Une chaîne vide vaut absence. |
city | string | non | Ville de la vente, 100 caractères maximum. |
#Les quatre formes acceptées pour identifier
Nous les essayons dans cet ordre, et nous bornons toujours la recherche à votre marque.
| Forme | Exemple | Reconnue à |
|---|---|---|
| Empreinte d'étiquette | 0x0000000000000000000000000000000000000000000000000000000000000000 | 0x suivi de 64 caractères hexadécimaux, soit 66 caractères en tout. C'est la valeur que nos réponses rendent dans le champ uid_hash |
| Identifiant de jeton | 10000000000000000000000000000000000000000000000000000000000000000000000000000 | une suite de chiffres, souvent très longue. C'est la valeur que nos réponses rendent dans le champ token_id |
| Numéro de certificat | ST-CERT-000000000000 | commence par ST-CERT-, suivi de 12 caractères. C'est le numéro affiché sur le certificat |
| Numéro de série imprimé sur le produit | 000000000000 | 12 caractères de l'alphabet Crockford Base32 |
Le numéro de série tolère les confusions de lecture courantes. Nous lisons les
lettres I et L comme le chiffre 1, la lettre O comme le chiffre 0, et
nous ignorons la casse. Nous ne résolvons jamais un produit détruit ou retiré.
#Ce que valent country et city quand vous les omettez
Si vous n'envoyez pas country, le pays enregistré est celui du point de vente.
Même règle pour city. Renseignez ces deux champs quand la vente n'a pas eu
lieu à l'adresse habituelle du point de vente.
#Requête d'exemple
curl -i -X POST https://api.sealtrust.io/v1/partner/sellout \
-H "Authorization: Bearer st_test_0000000000000000000000000000000000000000000000" \
-H "Content-Type: application/json" \
-d '{
"identifier": "000000000000",
"retailer_code": "BTQ-EXEMPLE-01",
"country": "FR",
"city": "Lyon"
}'const response = await fetch(
"https://api.sealtrust.io/v1/partner/sellout",
{
method: "POST",
headers: {
Authorization:
"Bearer st_test_0000000000000000000000000000000000000000000000",
"Content-Type": "application/json",
},
body: JSON.stringify({
identifier: "000000000000",
retailer_code: "BTQ-EXEMPLE-01",
country: "FR",
city: "Lyon",
}),
},
);
console.log(response.status);
console.log(response.headers.get("X-RateLimit-Scope"));
console.log(await response.json());import requests
response = requests.post(
"https://api.sealtrust.io/v1/partner/sellout",
headers={
"Authorization": "Bearer st_test_0000000000000000000000000000000000000000000000",
"Content-Type": "application/json",
},
json={
"identifier": "000000000000",
"retailer_code": "BTQ-EXEMPLE-01",
"country": "FR",
"city": "Lyon",
},
timeout=30,
)
print(response.status_code)
print(response.headers["X-RateLimit-Scope"])
print(response.json())#Réponse d'exemple
Code HTTP 200OK
Code HTTP 200, première déclaration pour ce produit.
{
"status": "activated",
"activation_id": 1024,
"product_id": 4096,
"product_name": "Sac de voyage Exemple SAS",
"retailer_code": "BTQ-EXEMPLE-01",
"source": "sellout_declared",
"activated_at": "2026-08-20T14:32:07.512430+00:00"
}Code HTTP 200 également si le produit avait déjà été activé, par exemple par un scan du client final avant votre déclaration.
{
"status": "already_activated",
"activation_id": 1024,
"product_id": 4096,
"product_name": "Sac de voyage Exemple SAS",
"retailer_code": "BTQ-EXEMPLE-01",
"source": "qr",
"activated_at": "2026-08-19T09:14:55.201884+00:00"
}Les sept champs de la réponse.
| Champ | Type | Description |
|---|---|---|
status | string | activated si cet appel a créé l'activation, already_activated si elle existait déjà. |
activation_id | integer | Identifiant interne de l'activation. Servez-vous-en pour rapprocher vos enregistrements des nôtres. Ne l'affichez pas à un tiers. |
product_id | integer | Identifiant du produit résolu à partir de identifier. |
product_name | string ou null | Nom du produit tel qu'il est enregistré. |
retailer_code | string | Le code de point de vente que vous avez envoyé, après résolution. Sur une réponse already_activated, si l'activation portait déjà un autre point de vente, nous gardons ce rattachement d'origine et ce champ ne le reflète pas. |
source | string | Ce qui a créé l'activation : sellout_declared pour une déclaration par cette route, nfc ou qr pour un premier scan. |
activated_at | string ou null | Date et heure de l'activation, au format ISO 8601 avec fuseau. Pour une activation qui existait déjà, c'est la date d'origine, jamais celle de votre appel. |
#Erreurs
| Code | Condition | Que faire |
|---|---|---|
| 401 | En-tête Authorization absent. La réponse porte WWW-Authenticate: Bearer. | Ajoutez l'en-tête Authorization: Bearer <votre clef>. |
| 401 | En-tête présent mais qui ne commence pas par Bearer suivi d'un espace. | Corrigez le format de l'en-tête. |
| 401 | Clef inconnue, ou clef de moins de 40 caractères. | Vérifiez que vous envoyez la clef entière. Elle n'est lisible qu'une fois, à sa création ; si elle est perdue, créez-en une nouvelle depuis la console. |
| 403 | Clef révoquée, ou déjà marquée expirée. Le message nomme l'état. | Créez une nouvelle clef. Une clef révoquée est refusée à chaque appel suivant, et la console ne propose aucune remise en service. |
| 403 | Clef arrivée à sa date d'expiration. Message API key has expired. | Créez une nouvelle clef. L'expiration est constatée au premier appel qui suit l'échéance, et elle est définitive. |
| 403 | La clef n'a pas le droit sellout:write. Message Missing required scopes: sellout:write. | Créez une clef qui porte ce droit. Les droits d'une clef existante ne se modifient pas. |
| 403 | L'offre de votre marque ne comprend pas l'accès API. detail vaut {"code": "FEATURE_NOT_AVAILABLE", "feature": "api_access"}. | Contactez-nous pour changer d'offre. Réessayer ne changera rien. |
| 404 | Aucun point de vente actif ne porte ce retailer_code dans votre marque. Message Retailer '<code>' not found for this brand. | Vérifiez le code dans votre console, section Distribution, et vérifiez que le point de vente est actif. |
| 404 | Aucun produit de votre marque ne correspond à identifier. Message Product not found for this identifier. | Vérifiez l'identifiant et sa forme. Un produit d'une autre marque, détruit ou retiré, répond la même chose. |
| 422 | Corps invalide : champ obligatoire absent, champ inconnu, country qui n'est pas deux lettres, ou longueur dépassée. | Le corps de la réponse liste les champs fautifs et le motif de chaque refus. Corrigez et rappelez. |
| 429 | Plafond de débit atteint, par votre clef ou par la somme des clefs de votre marque. | Attendez le nombre de secondes indiqué par l'en-tête Retry-After. X-RateLimit-Scope vous dit lequel des deux compteurs a refusé. |
| 429 | Quota journalier de la clef épuisé. Message Quota exceeded. Remaining today: <restant>/<limite>. | Les en-têtes X-Quota-Limit et X-Quota-Remaining donnent l'état du compteur. X-Quota-Reset donne la date de la dernière remise à zéro, une date passée. La suivante a lieu au passage de minuit en temps universel. Attendez minuit en temps universel, ou utilisez une clef au quota plus large. |
| 500 | Erreur interne pendant le traitement de l'appel. | Réessayez. Si l'erreur persiste, contactez-nous en indiquant l'heure de l'appel et le retailer_code employé. |
| 503 | Le service qui compte les appels est momentanément indisponible. Message Rate limiting temporarily unavailable, please retry shortly. | Réessayez dans quelques instants. Aucune déclaration n'a été enregistrée. |
#L'ordre des contrôles, et ce qu'il change pour votre quota
Les contrôles s'enchaînent dans cet ordre : plafond de débit, offre de la marque, existence du point de vente, quota journalier de la clef, puis résolution du produit.
Une conséquence utile : un retailer_code inconnu ne consomme pas votre quota
journalier, alors qu'un identifier introuvable le consomme, parce que nous
résolvons le produit après le décompte. Si vous rapprochez des ventes par lots,
validez vos codes de points de vente une fois pour toutes, et attendez-vous à ce
que les identifiants inconnus coûtent une unité chacun.
#Voir aussi
GET /certificate/{identifier}, lire le certificat d'authenticité d'un article.- Prendre en main la console, le tour des écrans côté marque, dans l'ordre où vous les utilisez.
- API partenaire, vue d'ensemble, adresse de base, clefs, droits, plafonds d'appels et pagination.
- Erreurs de l'API, reconnaître un code d'erreur et décider s'il faut corriger ou rejouer.
Cette page vous a-t-elle été utile ?
Votre réponse ouvre un courriel pré-rempli dans votre messagerie, à destination de contact@sealtrust.io. Vous le relisez avant de l'envoyer.