Méthode GET/timeline /{identifier}
Lire l'historique public d'un produit : ses vérifications et ses changements de propriétaire, à partir du numéro imprimé, de l'identifiant de jeton ou de l'empreinte de puce. Point d'entrée public.
Sur cette page
Vous lisez l'historique d'un seul produit. En quittant cette page, vous saurez récupérer la liste de ses vérifications et de ses changements de propriétaire, triée du plus récent au plus ancien, ainsi que la fiche du produit auquel cet historique appartient.
Adresse complète :
GET https://api.sealtrust.io/v1/timeline/{identifier}Le même point d'entrée répond aussi sans le préfixe /v1, à
https://api.sealtrust.io/timeline/{identifier}. Les deux adresses appellent le
même code. Utilisez la forme /v1 pour une nouvelle intégration.
#Autorisation
Aucune. Ce point d'entrée est public.
Vous n'avez pas besoin de clef d'API. Si vous en envoyez une dans l'en-tête
Authorization, nous la décodons comme un jeton de session de la console. Une
clef d'API n'est pas un jeton de session : la lecture échoue en silence, et
nous traitons l'appel comme un appel anonyme. Vous recevez donc la même réponse
avec ou sans clef d'API.
Un jeton de session de la console change en revanche la réponse. Nous le lisons
dans l'en-tête Authorization comme dans le cookie de session posé par la
console. L'administrateur de la marque et le propriétaire actuel du produit
reçoivent alors les adresses e-mail en clair. L'administrateur reçoit en plus
les adresses de portefeuille dans from_address et to_address.
Sur un appel anonyme, vous recevez une projection anonymisée :
- nous ne rendons pas les adresses de portefeuille,
from_addressetto_addressvalentnull; - nous masquons les adresses e-mail : la valeur rendue garde les deux premiers
caractères de la partie locale, puis
..., puis six caractères stables, par exemplema...3f9c1d.
#Plafond d'appels
30 appels par tranche de 60 secondes, comptés par adresse IP appelante. La fenêtre est fixe.
Les appels sur /v1/timeline/… et sur /timeline/… alimentent le même
compteur. Passer d'une forme à l'autre ne relève donc pas le plafond.
Un dépassement renvoie 429, avec un corps qui redit le plafond. Cette réponse
ne porte aucun Retry-After. Elle porte en revanche x-ratelimit-limit,
x-ratelimit-remaining et x-ratelimit-reset, dont les valeurs ne décrivent
pas le plafond de ce point d'entrée.
Les réponses 200 portent ces trois mêmes en-têtes, avec les mêmes valeurs. N'utilisez pas ces en-têtes ici pour régler votre cadence. Sur un 429, attendez la fin de la fenêtre en cours, soit au plus 60 secondes.
#Paramètres de chemin et de requête
| Nom | Type | Obligatoire | Description |
|---|---|---|---|
identifier | string | oui | L'identifiant du produit. Trois formes sont acceptées, voir ci-dessous. |
Ce point d'entrée n'a aucun paramètre de requête.
#Les trois formes d'identifiant acceptées
Ce paramètre accepte trois formes. Vous envoyez celle que vous avez sous la main.
| Forme | À quoi elle ressemble | Où vous la trouvez |
|---|---|---|
| Numéro de série | 12 caractères, chiffres et lettres majuscules, dans un alphabet qui exclut I, L, O et U | Imprimé sur le produit, c'est ce que porte son QR |
| Identifiant de jeton | Une suite de chiffres, souvent très longue | Rendu par nos réponses dans le champ token_id |
| Empreinte de puce | 0x suivi de 64 caractères hexadécimaux | Rendue par nos réponses dans le champ uid_hash |
Nous reconnaissons le numéro de série quelle que soit la casse. Nous lisons les
caractères I et L comme un 1, et le caractère O comme un 0, pour
accepter un numéro recopié à la main depuis une étiquette. Nous ne rattrapons
pas le U : il ne fait partie ni de l'alphabet des numéros ni des caractères
traduits, et un identifiant qui en contient ne désigne aucun produit.
#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
Lecture de l'historique du produit dont le numéro imprimé est EXEMP1E00001.
curl -i https://api.sealtrust.io/v1/timeline/EXEMP1E00001import { SealTrustClient } from "@sealtrust-io/sdk";
// Ce point d'entrée est public et ignore la clef, mais le client du SDK
// refuse de se construire sans elle.
const sealtrust = new SealTrustClient({
apiKey: "st_test_0000000000000000000000000000000000000000000000",
});
const historique = await sealtrust.verify.timeline("EXEMP1E00001");
console.log(historique.product_name, historique.timeline.length);import requests
response = requests.get(
"https://api.sealtrust.io/v1/timeline/EXEMP1E00001",
timeout=30,
)
print(response.status_code)
print(response.json())#Réponse d'exemple
Code HTTP 200OK
Code HTTP 200.
Ce produit a été frappé, il n'a pas encore été réclamé par un client, et il a été scanné une fois par QR.
{
"token_id": "7719472615821079694904732333912527190217998977709370935963838933860875309329",
"product_name": "Sac modèle 1",
"brand_id": 42,
"brand_name": "Exemple SAS",
"category_id": null,
"category_name": "Maroquinerie",
"metadata_uri": "ipfs://exemple-de-contenu-non-reel",
"image_url": "https://exemple-sas.test/images/sac-modele-1.jpg",
"current_owner_email": null,
"current_owner_is_vault": true,
"timeline": [
{
"type": "verify",
"token_id": "7719472615821079694904732333912527190217998977709370935963838933860875309329",
"uid_hash": "0x1111111111111111111111111111111111111111111111111111111111111111",
"contract_address": "0x2222222222222222222222222222222222222222",
"timestamp": "2026-08-18T14:02:11.482000Z",
"signature": "signature-exemple",
"is_valid": true,
"source": "qr",
"from_address": null,
"to_address": null,
"from_email": null,
"to_email": null,
"from_display": null,
"to_display": null,
"comment": "Scanned",
"event_id": null
},
{
"type": "transfer",
"token_id": "7719472615821079694904732333912527190217998977709370935963838933860875309329",
"uid_hash": "0x1111111111111111111111111111111111111111111111111111111111111111",
"contract_address": "0x2222222222222222222222222222222222222222",
"timestamp": "2026-08-12T09:30:00.000000Z",
"signature": null,
"is_valid": null,
"source": null,
"from_address": null,
"to_address": null,
"from_email": "Mint",
"to_email": null,
"from_display": "Mint",
"to_display": "Vault",
"comment": "Factory mint",
"event_id": null
}
],
"contract_address": "0x2222222222222222222222222222222222222222"
}La réponse compte douze champs et rien d'autre. Vous recevez null pour les
champs sans valeur, ils ne disparaissent pas de la réponse.
| Champ | Type | Description |
|---|---|---|
token_id | string | L'identifiant du jeton sur la chaîne, rendu sous forme de texte. Vaut la chaîne vide quand le produit n'a pas encore d'identifiant de jeton. |
product_name | string | Le nom du produit, ou null. |
brand_id | integer | Le numéro de la marque à laquelle le produit appartient, ou null si le produit n'est rattaché à aucune. |
brand_name | string | Le nom de la marque, ou null si le produit n'est rattaché à aucune. |
category_id | integer | Toujours null sur ce point d'entrée. Le champ est déclaré dans la forme de la réponse, et ce point d'entrée ne le remplit jamais. |
category_name | string | Le nom de la catégorie du produit, ou null. |
metadata_uri | string | L'adresse des métadonnées du produit, ou null. |
image_url | string | L'adresse de l'image du produit, ou null. |
current_owner_email | string | L'adresse e-mail du propriétaire actuel, masquée. null quand aucun propriétaire n'est connu. |
current_owner_is_vault | boolean | true quand le produit est encore détenu par le coffre de la marque, donc pas encore réclamé par un client. |
timeline | object[] | Les événements, du plus récent au plus ancien. Voir le tableau ci-dessous. |
contract_address | string | L'adresse du contrat qui porte ce produit sur la chaîne, ou null. |
Le tableau timeline n'est pas paginé et n'a pas de taille maximale. Il
contient toutes les vérifications et tous les mouvements enregistrés pour ce
produit. Un produit très scanné rend donc une réponse volumineuse.
Dimensionnez votre lecture en conséquence.
#Un événement de la chronologie
Chaque entrée compte seize champs. Les champs qui n'ont pas de sens pour le
type d'événement valent null.
| Champ | Type | Description |
|---|---|---|
type | string | verify pour une vérification, transfer pour un mouvement de propriété. Ce sont les deux seules valeurs. |
token_id | string | L'identifiant du jeton concerné. |
uid_hash | string | L'empreinte de puce concernée, 0x suivi de 64 caractères hexadécimaux. |
contract_address | string | L'adresse du contrat concerné. |
timestamp | string | Date et heure de l'événement, en temps universel, au format ISO 8601. |
signature | string | La signature enregistrée avec la vérification. null sur un mouvement de propriété. |
is_valid | boolean | Le résultat de la vérification. null sur un mouvement de propriété. |
source | string | D'où vient la vérification. Les valeurs écrites aujourd'hui sont qr, sdm-url et sdm-json. null sur un mouvement de propriété. |
from_address | string | Toujours null sur un appel public. |
to_address | string | Toujours null sur un appel public. |
from_email | string | L'adresse e-mail de la partie qui cède, masquée. Vaut Mint quand l'événement est la frappe du produit. |
to_email | string | L'adresse e-mail de la partie qui reçoit, masquée. |
from_display | string | Un libellé prêt à afficher pour la partie qui cède. Mint pour la frappe, Vault pour le coffre de la marque, sinon l'adresse e-mail masquée ou une adresse de portefeuille tronquée. |
to_display | string | Le même libellé, pour la partie qui reçoit. |
comment | string | Une phrase courte en anglais qui résume l'événement : Scanned, Scanned by <e-mail masqué>, Transfer, Transferred from <e-mail masqué> to <e-mail masqué> ou Factory mint. |
event_id | integer | Toujours null sur ce point d'entrée. Le champ est déclaré dans la forme de la réponse, et ce point d'entrée ne le remplit jamais. |
#Erreurs
Le corps d'une réponse d'erreur porte un champ detail.
| Code | Condition | Que faire |
|---|---|---|
| 400 | L'identifiant envoyé n'a la forme d'aucune des trois formes acceptées : il contient autre chose que des chiffres et des lettres, ou il dépasse 64 caractères. detail vaut Invalid UID hash format (must be 0x + 64 hex characters). | Envoyez un numéro de série, un identifiant de jeton ou une empreinte de puce. Vérifiez qu'aucun espace ni caractère de ponctuation ne traîne. |
| 400 | L'identifiant commence par 0x et fait 66 caractères, mais il contient un caractère qui n'est pas hexadécimal. Même valeur de detail. | Une empreinte de puce n'accepte que les chiffres 0 à 9 et les lettres a à f. |
| 404 | L'identifiant est bien formé, mais aucun produit ne lui correspond, ou aucun événement n'a été enregistré pour lui. detail vaut No events found for this UID. | Vérifiez le numéro recopié. Un produit retiré du catalogue répond avec l'autre message 404, décrit à la ligne suivante. Un produit détruit interrogé par son numéro imprimé ou par son identifiant de jeton répond ici. |
| 404 | Des événements existent pour cet identifiant, mais aucun produit encore en catalogue ne s'y rattache. detail vaut Product not found for this UID. | Le produit a été retiré du catalogue. Son historique n'est plus servi. |
| 429 | Vous avez fait plus de 30 appels depuis votre adresse IP dans la fenêtre de 60 secondes en cours. detail vaut Rate limit exceeded: 30 requests per 60s. | Attendez la fin de la fenêtre, au plus 60 secondes, puis réessayez. Les en-têtes x-ratelimit-* de cette réponse décrivent un autre compteur, ne vous en servez pas pour calculer votre attente. |
| 500 | Une erreur inattendue s'est produite pendant le traitement de votre appel. detail vaut Internal Server Error. | Réessayez. Si l'erreur persiste, contactez le support en indiquant l'heure de l'appel. |
#Voir aussi
GET /resolve/{identifier}, lire en un appel tout ce qu'une page produit affiche.GET /products/{uid}/public, lire les informations publiques d'un produit.GET /p/{serial}, traduire le numéro de série imprimé en adresse de page consommateur.- Notions de base, distinguer modèle, lot et article avant de commander la moindre étiquette.
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.