Guide de l’API des identifiants
Un identifiant relie un marquage physique à une fiche : un identifiant DUST, un code QR, un code-barres, un symbole Data Matrix, une puce NFC ou un code textuel imprimé. Une fois lié, un scan effectué sur le terrain est résolu vers la fiche numérique. L’espace de noms de l’API est /api/v1/tags — une dénomination historique conservée dans les chemins et les schémas ; cette documentation emploie le terme identifiant dans le texte.
Schémas complets des requêtes et réponses : référence de l’API. Tous les résultats que chaque opération peut produire — y compris ceux renvoyés sous forme d’erreurs HTTP — sont regroupés dans les tableaux de la page Erreurs et résultats de scan.
Vue d’ensemble des opérations
Section intitulée « Vue d’ensemble des opérations »| Opération | Méthode et chemin | Sémantique |
|---|---|---|
| Extraire | POST /api/v1/tags/extract | Analyser une capture DUST pour obtenir une empreinte canonique, sans la lier |
| Lier | POST /api/v1/tags/bind | Associer un identifiant à une fiche |
| Identifier | POST /api/v1/tags/identify | Rechercher : quelle fiche correspond à ce scan ? |
| Vérifier | POST /api/v1/tags/verify | Comparer un scan aux identifiants d’une fiche précise |
| Délier | POST /api/v1/tags/unbind | Détacher un identifiant de sa fiche |
| Définir le texte | POST /api/v1/tags/text | Renommer ou modifier la description d’un identifiant lié |
| Mettre à jour | POST /api/v1/tags/update | Cycle de vie : confidentialité, archivage/restauration (la valeur et le type sont immuables) |
Identifier ou vérifier : l’identification répond à la question « qu’est-ce que c’est ? » — elle recherche parmi les fiches qui vous sont visibles (dans le périmètre défini par searchTeamIds) et renvoie la correspondance éventuelle. La vérification répond à la question « s’agit-il bien de l’élément qu’il prétend être ? » — vous indiquez un threadId et les identifiants candidats qui lui sont liés, puis l’API confirme ou infirme la correspondance. Utilisez la vérification pour les décisions d’authentification et l’identification pour les recherches.
Deux familles de charges utiles
Section intitulée « Deux familles de charges utiles »Les points de terminaison de scan acceptent multipart/form-data, et la forme de data dépend du type d’identifiant :
tagType | data | Provenance |
|---|---|---|
DUST | Une image — une partie de fichier binaire ou une URL de données en base64 (data:image/jpeg;base64,…) | Une capture optique DUST provenant d’un scanner |
QR, BAR_CODE, DATA_MATRIX, NFC | Le contenu de la chaîne décodée (ou l’ID NFC hexadécimal) | N’importe quel scanner de symboles |
TEXT | Le code imprimé lisible par une personne, tel qu’elle le lit | Saisie au clavier ou enregistrement d’une étiquette |
Une capture DUST est une photographie de l’identifiant, et non une valeur décodée — le serveur en extrait l’empreinte. Les captures proviennent du matériel de scan DUST : consultez Intégrer DUST Go pour la capture sur mobile et React Scanner pour un composant web prêt à l’emploi qui prend en charge tous les modes.
Dans les corps multipart, les champs structurés (options, tags, searchTeamIds) sont transmis sous forme de chaînes JSON.
Identifiants textuels
Section intitulée « Identifiants textuels »TEXT est le code lisible par une personne imprimé sur un objet ou une étiquette — par exemple un numéro de série tel que AB00017. Aucun symbole n’est à décoder : la valeur est donc saisie (ou provient de la fiche d’enregistrement d’une étiquette) et stockée exactement telle qu’elle a été entrée. Comme la lecture est effectuée par une personne, TEXT est le seul type pour lequel la plateforme recherche les correspondances sans tenir compte de la casse : une identification ou une vérification avec ab00017 trouve un AB00017 lié. Tous les autres types sont comparés octet par octet.
Comme les valeurs QR, de code-barres, Data Matrix et NFC, un code textuel peut être copié et ne possède aucune unicité intrinsèque — le même code peut légitimement figurer sur plusieurs fiches ou sur chaque étiquette d’une bobine. Une valeur répétée n’est jamais rejetée ; la bobine signale simplement les répétitions sous forme d’avertissement.
Extraire une capture DUST
Section intitulée « Extraire une capture DUST »L’extraction analyse une capture pour produire une empreinte canonique et renvoie sa qualité — ce qui permet de contrôler une capture avant son enregistrement ou de préparer une liaison :
curl -fsS "$APID_URL/api/v1/tags/extract" \ -H "Authorization: Bearer $DUST_TOKEN" \ -H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \ -F "data=@scan.jpeg" \ -F 'options={"enrollmentSessionId":"3d5e…"}'La réponse est { id, qualityScore, annotatedImage?, forensics?, scan? } — id est un ID d’empreinte que vous pourrez lier ultérieurement sans téléverser de nouveau l’image (voir ci-dessous). options contient également les métadonnées de capture (appareil, optique, géolocalisation) que la plateforme stocke avec le scan.
Le reçu de scan
Section intitulée « Le reçu de scan »Toute opération qui envoie une image — extraction, liaison, identification, vérification et analyse d’altération — renvoie un objet scan indiquant ce qui a été stocké :
{ "scanId": "…", "fingerprintId": "…", "dustId": "…" }scanIdest toujours présent dès que l’image a été stockée. Il constitue l’identité stable de la capture et la valeur à conserver si vous consignez les opérations de scan de votre côté.fingerprintIdest présent lorsque l’extraction a réussi (nulldans le cas contraire).dustIdest présent lorsque l’opération vous a renvoyé un DUST : l’identifiant créé par une liaison, confirmé par une vérification ou résolu par une identification. Il vautnullen cas de non-correspondance, d’absence de correspondance, d’extraction, d’analyse d’altération (l’identifiant y est celui que vous avez fourni, et non un identifiant vers lequel l’image a été résolue), ainsi que pour une identification ayant renvoyé plusieurs candidats (chaque candidat contient son propre identifiant).
Une non-correspondance de vérification et une absence de correspondance d’identification conservent leur statut et leur code d’erreur existants, et incluent le même reçu sous detail.scan — le scan a été stocké même si le résultat était négatif. Il en va de même pour une capture rejetée en raison de sa qualité — trop peu de points clés exploitables ou aucun — quel que soit le code d’erreur renvoyé par l’opération (/tags/extract répond SCAN_EXTRACTION_FAILURE ; l’identification expose SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS ; la vérification répond par son code habituel IDENTIFIER_VERIFY_FAILED) : l’image est conservée et son reçu comporte fingerprintId: null, car aucun élément exploitable n’a pu en être extrait. Seule une image que la plateforme n’a pas du tout pu décoder n’est pas stockée et ne possède aucun reçu.
Lier un identifiant à une fiche
Section intitulée « Lier un identifiant à une fiche »POST /api/v1/tags/bind accepte trois formes, distinguées par tagType et la charge utile :
curl -fsS "$APID_URL/api/v1/tags/bind" \ -H "Authorization: Bearer $DUST_TOKEN" \ -H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \ -F "threadId=$THREAD_ID" \ -F "tagType=DUST" \ -F "tagDescription=Inbound receiving scan" \ -F "data=@scan.jpeg" \ -F 'options={"enrollmentSessionId":"3d5e…"}'# QR, BAR_CODE, DATA_MATRIX, NFC: the decoded contentscurl -fsS "$APID_URL/api/v1/tags/bind" \ -H "Authorization: Bearer $DUST_TOKEN" \ -H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \ -F "threadId=$THREAD_ID" \ -F "tagType=QR" \ -F "data=https://example.com/item/SZ3J-11-ZJ17"# TEXT: the printed code as a person reads itcurl -fsS "$APID_URL/api/v1/tags/bind" \ -H "Authorization: Bearer $DUST_TOKEN" \ -H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \ -F "threadId=$THREAD_ID" \ -F "tagType=TEXT" \ -F "data=AB00017"# Reuse a fingerprint from a prior /extract — no image re-uploadcurl -fsS "$APID_URL/api/v1/tags/bind" \ -H "Authorization: Bearer $DUST_TOKEN" \ -H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \ -F "threadId=$THREAD_ID" \ -F "tagType=DUST" \ -F "fingerprintId=$FINGERPRINT_ID"options.enrollmentSessionId est facultatif lors d’une liaison DUST. Fournissez un UUID généré par le client — le même pendant toute une exécution — lorsque plusieurs captures doivent être regroupées, par exemple lorsqu’une station d’enregistrement traite un lot ou réalise une capture d’un même objet sous plusieurs angles ; la plateforme regroupe alors ces scans dans cette session. Omettez-le entièrement pour une liaison ponctuelle. Les liaisons d’images DUST peuvent aussi renvoyer la capture annotée avec options.returnAnnotatedImage: true.
Lier une étiquette
Section intitulée « Lier une étiquette »Si l’identifiant scanné appartient à une étiquette détenue par l’équipe de la fiche (voir Étiquettes), la liaison ne crée pas d’identifiant indépendant. Elle lie l’étiquette entière : chaque identifiant membre actif est associé à la fiche en une seule opération, et la réponse contient label (l’étiquette, sa bobine et sa position) ainsi que boundTags (tous les membres liés), en plus du champ tag habituel, qui correspond au membre scanné. Transmettez activateLabel: true pour rendre également les identifiants DUST de l’étiquette identifiables dans le cadre de la liaison ; cette option est facultative. Une étiquette déjà liée à une autre fiche renvoie IDENTIFIER_ALREADY_BOUND avec detail.compositeTagId.
Identifier une fiche à partir d’un scan
Section intitulée « Identifier une fiche à partir d’un scan »curl -fsS "$APID_URL/api/v1/tags/identify" \ -H "Authorization: Bearer $DUST_TOKEN" \ -H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \ -F "tagType=DUST" \ -F "data=@scan.jpeg" \ -F 'searchTeamIds=["'"$TEAM_ID"'"]'const result = await client.tags.identify({ tagType: "DUST", data: scanBlob, searchTeamIds: [teamId],});L’identification accepte également des charges utiles contenant une valeur (tagType valant QR/BAR_CODE/DATA_MATRIX/NFC avec la valeur data décodée, ou TEXT avec le code imprimé, comparé sans tenir compte de la casse) ou un simple ID d’identifiant (tagType: "ANY" avec tagId).
Résultats possibles de l’identification
Section intitulée « Résultats possibles de l’identification »Une correspondance renvoie 200 avec l’identifiant correspondant et sa fiche. Une absence de correspondance produit un statut d’erreur, et non un 200 avec un résultat vide : une absence de correspondance certaine renvoie 404 IDENTIFIER_NOT_FOUND, tandis qu’une recherche qui n’a pas pu être menée à terme renvoie 503 SCAN_SEARCH_INCOMPLETE — il s’agit d’une situation différente, qui ne doit pas être présentée à un opérateur comme « introuvable ». Les deux réponses contiennent le reçu de scan dans detail.scan.
Le tableau canonique des huit résultats — fiche identifiée, plusieurs candidats, étiquette non liée, aucune correspondance, recherche incomplète, correspondance ambiguë, capture rejetée, identifiant non lié — avec le statut, le code et la réponse client appropriée pour chacun, se trouve dans Erreurs et résultats de scan → Résultats canoniques de l’identification. Effectuez les branchements selon code et consultez detail.outcome (no_match, search_incomplete, ambiguous, quality_reject) lorsque vous devez distinguer plus précisément les cas.
Chaque identifiant renvoyé qui appartient à une étiquette contient tag.label (son étiquette, sa bobine et sa position). Lorsque le scan correspond à un membre d’une étiquette non liée dans l’inventaire de l’équipe active, le résultat est { type: "label", label: { label, tags } } : il n’existe pas encore de fiche, mais l’étiquette et ses identifiants membres sont renvoyés afin qu’un client puisse proposer de la lier (voir Étiquettes).
Choisir le périmètre de recherche
Section intitulée « Choisir le périmètre de recherche »searchTeamIds est un tableau JSON d’UUID d’équipes (une chaîne JSON dans les corps multipart). Si vous l’omettez, l’identification recherche dans une seule équipe : celle indiquée par Dust-Ctx-Team-Id, qui correspond par défaut à l’équipe racine de l’organisation.
Ces ID ne sont pas arbitraires. Les équipes de la même organisation auxquelles vous appartenez sont toujours incluses dans le périmètre ; l’équipe d’une organisation partenaire n’est accessible que par une connexion active qui autorise le transfert de ses données vers vous. Toute autre équipe est supprimée silencieusement du périmètre au lieu de faire échouer la requête ; un périmètre apparemment large peut donc se traduire par une recherche restreinte. Découvrez les ID valides au lieu de les coder en dur :
GET /api/v1/teams— les équipes de votre organisation auxquelles vos informations d’identification donnent accès ({ teams: [{ teamId, orgId, name, … }], total }).GET /api/v1/teams/connected— les équipes partenaires dans lesquelles vous pouvez effectuer des recherches, sous forme de fiches de connexion indiquant les deux équipes liées.
Vérifier un scan par rapport à une fiche
Section intitulée « Vérifier un scan par rapport à une fiche »La vérification est la primitive d’authentification : à partir d’un nouveau scan, d’un threadId et des tags candidats déjà liés à cette fiche, elle réussit si un candidat correspond.
tags est obligatoire et constitue un tableau d’objets — chacun de la forme { "tagId": "…", "tagType": "…" } — et non un tableau de chaînes d’ID. Dans un corps multipart, il est envoyé sous forme de chaîne JSON :
curl -fsS "$APID_URL/api/v1/tags/verify" \ -H "Authorization: Bearer $DUST_TOKEN" \ -H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \ -F "threadId=$THREAD_ID" \ -F "tagType=DUST" \ -F "data=@scan.jpeg" \ -F 'tags=[{"tagId":"'"$TAG_ID"'","tagType":"DUST"}]'const form = new FormData();form.set("threadId", threadId);form.set("tagType", "DUST");form.set("data", scanBlob);form.set("tags", JSON.stringify([{ tagId, tagType: "DUST" }]));Provenance des ID candidats
Section intitulée « Provenance des ID candidats »Les valeurs tagId sont celles des identifiants déjà liés à la fiche que vous contrôlez. Lisez-les depuis la fiche : GET /api/v1/threads/{thread_id} les renvoie dans thread.tags, chacun avec son tagId et son tagType. Une vérification classique récupère donc la fiche, filtre ses identifiants selon le type que vous venez de capturer et les envoie comme liste de candidats :
const record = await getThread(threadId); // GET /api/v1/threads/{thread_id}const candidates = (record.thread.tags ?? []) .filter((tag) => tag.tagType === "DUST") .map((tag) => ({ tagId: tag.tagId, tagType: tag.tagType }));L’envoi d’un identifiant qui n’est pas lié à cette fiche fait échouer la vérification au lieu de rechercher une correspondance ailleurs.
Interpréter le résultat
Section intitulée « Interpréter le résultat »Le nombre de candidats modifie la forme de la réponse, ce qui constitue l’erreur d’intégration la plus fréquente ici :
Longueur de tags | Correspondance | Aucune correspondance |
|---|---|---|
| Exactement un | 200 avec { tag, scan? } | IDENTIFIER_VERIFY_FAILED (HTTP 500), reçu dans detail.scan |
| Deux ou plus | 200 avec { success: true, verifiedTag, attemptedCount, failedCount, scan? } | 200 avec { success: false, attemptedCount, failedCount, error, scan? } |
Ainsi, une vérification de plusieurs candidats sans correspondance est un appel HTTP réussi contenant success: false. Ne considérez jamais response.ok comme une preuve d’authenticité — consultez success chaque fois que vous envoyez plusieurs candidats. Voir Erreurs et résultats de scan → Résultats de vérification.
Gérer les identifiants liés
Section intitulée « Gérer les identifiants liés »Il s’agit de points de terminaison JSON ordinaires ; ils nécessitent tous le tagId et le threadId de la fiche à laquelle l’identifiant est lié :
POST /api/v1/tags/text— définitnameet/oudescription.POST /api/v1/tags/update— définitname,description,isPrivateetarchivedAt(un horodatage ISO archive l’identifiant ;nullle restaure). La valeur et le type de l’identifiant sont immuables — effectuez une nouvelle liaison à la place.POST /api/v1/tags/unbind— détache l’identifiant de la fiche.
Analyse d’altération
Section intitulée « Analyse d’altération »Sous /api/v1/tamper, une analyse d’altération compare un nouveau scan d’un identifiant DUST à la capture de référence réalisée lors de sa liaison et consigne les mesures obtenues. L’API ne renvoie que des mesures et des éléments probants — aucun nombre récapitulatif, aucune plage, aucun seuil ni champ de résultat rédigé par la plateforme n’existe dans cette surface, et alignmentOutcome indique uniquement si les deux scans ont pu être comparés (lorsqu’ils ne l’ont pas pu, les mesures ne sont pas comparables, ce qui ne constitue pas une affirmation au sujet de l’identifiant).
| Opération | Méthode et chemin |
|---|---|
| Exécuter une analyse | POST /api/v1/tamper/analyses — données de formulaire : threadId, tagId et exactement l’un des champs data ou queryFingerprintId |
| Consigner une observation | POST /api/v1/tamper/observations — { analysisId, result } |
| Répertorier les analyses d’une fiche | GET /api/v1/tamper/analyses?threadId=… (avec éventuellement tagId, limit) |
| Obtenir une analyse | GET /api/v1/tamper/analyses/{analysis_id} |
| Récupérer une image bitmap de résultat | GET /api/v1/tamper/analyses/{analysis_id}/artifacts/{name} |
L’exécution d’une analyse nécessite un corps multipart/form-data ou application/x-www-form-urlencoded contenant threadId, tagId et exactement l’un des champs suivants :
data— le scan DUST lui-même, sous forme de fichier ou d’image encodée en base64. Le service l’extrait pour vous.queryFingerprintId— un ID d’empreinte déjà obtenu avecPOST /api/v1/tags/extract(ci-dessus), si vous avez effectué l’extraction séparément.
L’envoi des deux champs, ou d’aucun des deux, est rejeté. Dans les deux cas, une capture DUST ordinaire constitue une entrée valide — il n’existe pas de chemin de capture distinct pour l’analyse d’altération. Si le scan envoyé est illisible, la requête échoue et aucune analyse n’est consignée.
Une observation d’altération est la seule conclusion stockée par la plateforme, et elle est rédigée par une personne : result vaut consistent, expected, inconsistent ou unknown, ne possède aucune valeur par défaut et est obligatoire. expected consigne l’usure normale correspondant au cas d’utilisation et au substrat de l’identifiant. Les observations sont immuables et attribuées ; une nouvelle observation ne remplace jamais une observation antérieure, et les lectures renvoient la série complète (observations, de la plus récente à la plus ancienne) plutôt qu’un résultat actuel unique. Ne déduisez aucun résultat des mesures et ne réduisez pas la série à une valeur unique dans votre propre interface utilisateur.
Une analyse contient metrics (un objet transmis tel quel qui regroupe les fractions de couverture et les nombres de marqueurs calculés par l’algorithme), des markerPoints facultatifs et artifactNames. Chaque ensemble de coordonnées des marqueurs se trouve dans l’espace de pixels de son propre scan — composez-les dans un même repère en appliquant metrics.transformation_matrix aux points de la requête. Les images bitmap de résultat sont du contenu protégé : récupérez-les au moyen du point de terminaison des artefacts, qui renouvelle l’autorisation à chaque requête et renvoie des octets non mis en cache.
Étiquettes
Section intitulée « Étiquettes »Une étiquette (nom dans le protocole : composite tag, espace de noms /api/v1/composite-tags) est une étiquette physique unique portant un ou plusieurs identifiants de n’importe quel type ; DUST n’est pas obligatoire. Les étiquettes occupent une position sur une bobine (collection.kind = "reel", identifiée par son UUID ; son name est le numéro de bobine imprimé ou n’importe quel titre et n’est jamais unique). Les bobines peuvent être classées dans une collection d’étiquettes (kind = "reel_collection"), un dossier qui n’est jamais expédié. Le champ expectedIdentifiers d’une bobine indique combien d’identifiants de chaque type comporte une étiquette complète de cette bobine, sous la forme [{ "tagType", "count" }] (par défaut, un TEXT, un DUST et un QR ; un count égal à 0 en entrée signifie que le type n’est pas attendu). Il s’agit d’une indication destinée aux stations d’enregistrement, et non d’une contrainte ; le signalement complete d’une étiquette signifie qu’elle possède au moins ce nombre d’identifiants actifs pour chaque type attendu.
| Opération | Méthode et chemin |
|---|---|
| Répertorier/créer des collections d’étiquettes | GET, POST /api/v1/composite-tags/collections; PATCH …/collections/{collection_id} |
| Répertorier les bobines | GET /api/v1/composite-tags/reels?collectionId=…&unfiled=…&transferred=any|only|hide&q=… |
| Créer une bobine | POST /api/v1/composite-tags/reels — { name, description?, collectionId?, expectedIdentifiers? } |
| Obtenir/mettre à jour une bobine | GET, PATCH /api/v1/composite-tags/reels/{reel_collection_id} (renommage, composition attendue, collectionId pour la déplacer ; null la retire de son classement) |
| Créer une étiquette | POST /api/v1/composite-tags/reels/{reel_collection_id}/labels (multipart) |
| Ajouter/supprimer un identifiant membre | POST /api/v1/composite-tags/{composite_tag_id}/identifiers (multipart); DELETE …/identifiers/{tag_id} |
| Répertorier/obtenir des étiquettes | GET /api/v1/composite-tags?reelCollectionId=…&bound=any|only|unbound&transferred=…&q=…; GET …/{composite_tag_id} |
| Résoudre une étiquette à partir de la valeur d’un membre | POST /api/v1/composite-tags/resolve — { tagType, value, reelCollectionId? } (TEXT est comparé sans tenir compte de la casse) ; la réponse contient detail (première correspondance) et candidates[] (toutes les correspondances, dans l’ordre des positions lorsqu’une bobine est indiquée) |
| Déplacer ou découper des étiquettes | POST /api/v1/composite-tags/move; vérification préalable avec POST /api/v1/composite-tags/move/preview |
| Lier/délier une étiquette | POST /api/v1/composite-tags/{composite_tag_id}/bind — { threadId, options?: { indexing: "default" } }; POST …/unbind |
| Lier en masse une plage de bobine | POST /api/v1/composite-tags/reels/{reel_collection_id}/bulk-bind — { fromPosition, toPosition, threadIds, activate?, dryRun? } |
| Archiver/restaurer une étiquette | POST …/{composite_tag_id}/archive, POST …/unarchive |
| Activer | POST …/{composite_tag_id}/activate; POST /api/v1/composite-tags/reels/{reel_collection_id}/activate (en arrière-plan) |
| Activer des identifiants DUST indépendants | POST /api/v1/tags/activate — { tagIds[] } (jusqu’à 200) ; un résultat par identifiant, opération irréversible |
Créer une bobine et enregistrer des étiquettes
Section intitulée « Créer une bobine et enregistrer des étiquettes »curl -fsS "$APID_URL/api/v1/composite-tags/reels" \ -H "Authorization: Bearer $DUST_TOKEN" \ -H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \ -H "Dust-Ctx-Team-Id: $DUST_TEAM_ID" \ -H "Content-Type: application/json" \ --data '{ "name": "0030", "expectedIdentifiers": [{ "tagType": "TEXT", "count": 1 }, { "tagType": "DUST", "count": 1 }, { "tagType": "QR", "count": 1 }] }'La réponse est { reel }, un résumé de la bobine dont tous les nombres sont à zéro. Conservez reel.collectionId ; il identifie la bobine. La création d’une bobine crée toujours une nouvelle bobine : aucune réutilisation par nom n’est effectuée.
Chaque étiquette correspond à une requête multipart. Fournissez au maximum une image DUST dans data ; tous les autres membres sont transmis dans identifiers, un tableau JSON d’entrées { tagType, value }, ou sous la forme { tagType: "DUST", fingerprintId } pour un autre DUST déjà extrait avec POST /api/v1/tags/extract. humanReadable et qrValue sont des raccourcis respectivement pour un membre TEXT et un membre QR. position prend par défaut la prochaine position libre de la bobine.
curl -fsS "$APID_URL/api/v1/composite-tags/reels/$REEL_COLLECTION_ID/labels" \ -H "Authorization: Bearer $DUST_TOKEN" \ -H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \ -H "Dust-Ctx-Team-Id: $DUST_TEAM_ID" \ -F "position=1" \ -F "data=@scan.jpeg" \ -F 'identifiers=[{"tagType":"TEXT","value":"AB00001"},{"tagType":"QR","value":"https://v.example/ab00001"}]' \ -F 'options={"indexing":"none"}'Au moins un identifiant doit en résulter. Une réponse réussie contient outcome: "created" ; une nouvelle tentative avec le même marquage DUST à la même position est rapprochée avec outcome: "already_enrolled". Un DUST déjà présent sur une autre étiquette ou déjà lié renvoie 409 COMPOSITE_TAG_CONFLICT. Les valeurs TEXT ou QR répétées ne sont jamais rejetées : la bobine les signale plutôt dans warnings, car certaines bobines répètent légitimement une valeur.
options.indexing sélectionne le mode d’indexation DUST de l’image contenue dans data : default (identifiable), sauf si vous demandez none (Vérification uniquement). Les stations d’enregistrement sans surveillance effectuent généralement un enregistrement avec Vérification uniquement, puis activent les identifiants ultérieurement ; l’activation indexe chaque DUST et exécute le contrôle des doublons de la plateforme. Une étiquette dont le DUST reproduit un identifiant déjà indexé est donc signalée et ignorée.
Le processus équivalent avec le client typé est le suivant :
const created = await client.compositeTags.createReel({ name: "0030" });
const form = new FormData();form.set("position", "1");form.set("data", scanBlob);form.set("identifiers", JSON.stringify([{ tagType: "TEXT", value: "AB00001" }]));await client.compositeTags.createLabel(created.reel.collectionId, form);
const state = await client.compositeTags.getReel(created.reel.collectionId);await client.compositeTags.activateReel(created.reel.collectionId);Déplacer et découper
Section intitulée « Déplacer et découper »POST /api/v1/composite-tags/move accepte une source, une target et un expectedCount facultatif.
Trois formes de source sont possibles :
{ compositeTagIds }— des étiquettes sélectionnées individuellement, déplacées dans l’ordre indiqué.{ reelCollectionId, fromPosition, toPosition? }— une plage de positions typée (une découpe) ;toPositioncorrespond par défaut à la dernière position de la bobine.{ fromCompositeTagId, toCompositeTagId }— une découpe délimitée par des scans : la première et la dernière étiquette de la plage, dans n’importe quel ordre. Le serveur lit leurs positions sous verrouillage ; les deux doivent être des étiquettes actives de la même bobine (sinondetail.reasoncontientendpoints_on_different_reels,endpoint_archivedouendpoint_not_on_reel). Résolvez chaque étiquette à partir d’une valeur scannée avec/resolve(en limitant le périmètre à la bobine afin qu’un code imprimé répété fasse apparaître plusieurscandidatesque l’appelant pourra distinguer) ou, pour un DUST activé, avecPOST /api/v1/tags/identify.
La target est { reelCollectionId } ou { newReel: { name, description?, collectionId?, expectedIdentifiers? } } (une nouvelle bobine hérite de la composition de la bobine source lorsqu’aucune composition n’est fournie).
Toutes les étiquettes actives d’une plage sont déplacées ; les positions contenant une étiquette archivée ou expédiée, ou ne contenant aucune étiquette, constituent des intervalles vides qui restent sur la bobine source. Les positions sont conservées lorsqu’elles sont toutes libres sur la cible ; sinon, le lot entier est ajouté après la dernière position de la cible, dans l’ordre de la source. La réponse répertorie moved[] et, pour une source définie par une plage ou délimitée par des scans, cut: { sourceReel, fromPosition, toPosition, count, boundCount, boundPositions, gaps[] }.
expectedCount fait du nombre un engagement contractuel : lorsqu’il est fourni, le déplacement est refusé avec 400 INVALID_REQUEST et detail.reason: "count_mismatch" (expected, actual, fromPosition, toPosition), sauf si exactement ce nombre d’étiquettes actives serait déplacé.
POST /api/v1/composite-tags/move/preview accepte la même source, ainsi qu’une target et un expectedCount facultatifs, ne modifie rien et renvoie span, count, boundCount, les première et dernière étiquettes du lot dans first et last, predictedOutcome (kept_positions / appended / null), countMatches et suggestedLast — l’étiquette située plus loin sur la bobine qui permettrait de satisfaire expectedCount lorsque la plage est trop courte. Il s’agit d’une suggestion que l’opérateur peut scanner ; elle n’est jamais appliquée par le serveur. La vérification préalable est accessible aux membres ; le déplacement nécessite un administrateur d’équipe ou d’organisation.
Lier en masse une plage de bobine
Section intitulée « Lier en masse une plage de bobine »POST /api/v1/composite-tags/reels/{reel_collection_id}/bulk-bind lie les étiquettes situées aux positions fromPosition..toPosition (incluses) aux threadIds, dans l’ordre : la k-ième position à la k-ième fiche. L’opération est atomique et stricte : la plage doit couvrir exactement threadIds.length positions (toPosition est obligatoire et non dérivé, de sorte que l’appelant indique la plage qu’il a vérifiée sur la bobine), avec au maximum 500 paires par appel, et chaque position doit contenir une étiquette active et non liée. Le serveur ne saute jamais une position, car cela décalerait silencieusement toutes les associations suivantes.
Transmettez dryRun: true pour effectuer une vérification préalable sans liaison. La forme de la réponse est identique dans les deux cas :
outcome—"bound"après une liaison réelle,"preflight"pour une simulation.rows[]— une entrée par association :index,position,compositeTagId(nullpour une position vide),labelName,textValue(le membreTEXTde l’étiquette, c’est-à-dire le code imprimé),threadId,threadName,threadDescription.blockers[]etwarnings[]—{ kind, index, position, compositeTagId?, threadId?, tagType?, existing? }.activation—"queued","not_requested","already_active"ou"no_dust".reel— le résumé de la bobine avec les nombres mis à jour.
Types de blocages : position_empty, label_archived, label_transferred, label_bound, label_no_identifiers, identifier_bound_elsewhere, identifier_in_other_team_label, thread_not_owned, thread_unavailable, thread_in_transfer, thread_not_editable, thread_repeated. Types d’avertissements : label_incomplete (une étiquette comportant moins d’identifiants que la bobine n’en attend) et thread_has_label (la fiche possède déjà une étiquette ; existing[] les désigne). Les avertissements n’empêchent jamais une liaison.
Une validation comportant un blocage échoue avec 409 COMPOSITE_TAG_CONFLICT ; detail contient les mêmes rows, blockers et warnings qu’une simulation, de sorte qu’un client n’a jamais à analyser qu’une seule forme. Une plage dont la longueur diffère de threadIds.length produit une erreur 400 INVALID_REQUEST.
Autorisations : appartenance à l’équipe de la bobine et autorisation de modification sur chaque fiche. Une fiche que l’appelant ne peut pas modifier produit un blocage thread_not_editable pour la ligne concernée plutôt qu’un rejet immédiat de toute la requête, et chaque fiche doit appartenir à l’équipe de la bobine — une fiche simplement partagée avec l’équipe produit thread_not_owned.
Avec activate: true, la liaison est d’abord validée, puis une tâche en arrière-plan active précisément les marquages DUST des étiquettes liées ; une étiquette dont l’activation échoue reste liée et en mode Vérification uniquement. Interrogez counts.identifiableCount sur la bobine pour suivre la progression.
# Preflightcurl -fsS "$APID_URL/api/v1/composite-tags/reels/$REEL_COLLECTION_ID/bulk-bind" \ -H "Authorization: Bearer $DUST_TOKEN" \ -H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \ -H "Dust-Ctx-Team-Id: $DUST_TEAM_ID" \ -H "Content-Type: application/json" \ --data '{ "fromPosition": 1, "toPosition": 3, "threadIds": ["'$THREAD_1'", "'$THREAD_2'", "'$THREAD_3'"], "dryRun": true }'
# Commit, activating the bound Labels afterwardscurl -fsS "$APID_URL/api/v1/composite-tags/reels/$REEL_COLLECTION_ID/bulk-bind" \ -H "Authorization: Bearer $DUST_TOKEN" \ -H "Dust-Ctx-Org-Id: $DUST_ORG_ID" \ -H "Dust-Ctx-Team-Id: $DUST_TEAM_ID" \ -H "Content-Type: application/json" \ --data '{ "fromPosition": 1, "toPosition": 3, "threadIds": ["'$THREAD_1'", "'$THREAD_2'", "'$THREAD_3'"], "activate": true }'Avec le client typé :
const preview = await client.compositeTags.bulkBind(reelCollectionId, { fromPosition: 1, toPosition: threadIds.length, threadIds, dryRun: true,});if (preview.blockers.length === 0) { await client.compositeTags.bulkBind(reelCollectionId, { fromPosition: 1, toPosition: threadIds.length, threadIds, activate: true, });}La liaison produit exactement les événements que produiraient N liaisons individuelles : un événement bind par identifiant membre, chacun ayant la fiche, l’identifiant et l’étiquette comme cibles, tous partageant un même ID d’opération.
Liaison, dissociation et expédition
Section intitulée « Liaison, dissociation et expédition »Une étiquette est liée comme un tout : POST …/{composite_tag_id}/bind associe l’étiquette et chaque identifiant membre actif à la fiche ; options.indexing: "default" l’active également. Il en va de même lorsque vous appelez le point de terminaison ordinaire POST /api/v1/tags/bind avec n’importe quel identifiant membre (voir Lier une étiquette), ce que font les scanners. La liaison exige une autorisation de modification sur la fiche et que l’étiquette appartienne à l’équipe ; la fiche doit également appartenir à la même équipe. Un identifiant membre scanné sur une fiche qu’une autre équipe a partagée avec vous est refusé (409 COMPOSITE_TAG_CONFLICT, reason: "label_owned_by_other_team") au lieu d’être lié comme une copie indépendante. POST …/unbind détache l’étiquette et tous ses membres.
La propriété par l’équipe et l’organisation provient uniquement des en-têtes de contexte, tandis que le créateur est déterminé à partir du jeton porteur vérifié. La lecture et l’activation des étiquettes nécessitent l’appartenance à l’équipe. La création de bobines, l’enregistrement, le déplacement et l’archivage sont accessibles à un administrateur d’équipe ou d’organisation connecté, ou à un compte de service limité à l’organisation et membre de l’équipe sélectionnée ; un compte de service ne peut pas emprunter les droits d’un administrateur d’organisation.
Dans les expéditions, une bobine est un élément du manifeste ({ kind: "reel", collectionId }) et est expédiée entière, uniquement tant que toutes ses étiquettes sont non liées. Une étiquette liée est expédiée avec sa fiche et n’inclut jamais sa bobine dans l’expédition. Les bobines et étiquettes expédiées restent lisibles du côté de l’expéditeur avec transferredAt défini ; filtrez-les avec transferred=only ou transferred=hide.
Consultez Étiquettes et bobines pour découvrir le processus DICE.
Pages connexes
Section intitulée « Pages connexes »- Erreurs et résultats de scan — les tableaux canoniques des résultats d’identification et de vérification, ainsi que la manière de conserver un reçu de scan en cas d’échec
- Intégrer DUST Go — capturer des scans DUST sur mobile
- React Scanner — un composant de capture prêt à copier
- Guide de l’API des fiches — les fiches auxquelles les identifiants sont liés
- Référence de l’API — les schémas complets, y compris les options de métadonnées de capture