Erreurs et résultats de scan
Cette page constitue la référence pour la gestion des échecs : le corps d’erreur renvoyé par chaque point de terminaison, les codes sur lesquels il est utile de créer des embranchements et, surtout, les tableaux canoniques des résultats de scan. Une « aucune correspondance » est un résultat, pas un échec de transport, même si elle est renvoyée avec un statut d’erreur HTTP. Un code qui traite chaque réponse autre que 2xx comme un bug signalera des interruptions qui n’ont jamais eu lieu.
Les schémas propres à chaque point de terminaison se trouvent dans la référence de l’API. Les processus eux-mêmes sont présentés dans Identifiants et dans le guide de démarrage rapide.
L’enveloppe d’erreur
Section intitulée « L’enveloppe d’erreur »Chaque requête ayant échoué renvoie le même objet JSON :
{ "code": "IDENTIFIER_NOT_FOUND", "message": "No match in the 3 searched teams", "status": 404, "detail": { "outcome": "no_match", "teamsSearched": 3, "scan": { "scanId": "…", "fingerprintId": "…", "dustId": null } }}| Champ | Type | Signification |
|---|---|---|
code | string | Code stable lisible par une machine. Créez vos embranchements à partir de ce champ. N’analysez jamais message. |
message | string | Texte lisible par un humain, localisé selon Dust-Ctx-Locale. La formulation change, contrairement aux codes. |
status | number | Reproduit le statut HTTP. |
detail | object, facultatif | Contexte propre à l’erreur : détails de validation, reçu de scan, classification du résultat, identifiants en conflit. |
status et le statut HTTP correspondent toujours ; vous pouvez donc créer vos embranchements à partir de l’un ou de l’autre. Chaque réponse, qu’elle indique une réussite ou un échec, comporte également un en-tête x-request-id. Consignez-le : l’assistance l’utilise pour retrouver votre requête exacte.
Codes par classe
Section intitulée « Codes par classe »Requête et autorisation
Section intitulée « Requête et autorisation »| Code | Statut | Quand |
|---|---|---|
INVALID_REQUEST | 400 | Corps, requête ou en-tête mal formé. Les détails de validation figurent dans detail. |
INVALID_DATA | 400 | La requête a été analysée, mais les valeurs ne peuvent pas être utilisées (par exemple, une charge utile d’identification que le service de scan a refusé de lire). |
UNAUTHORIZED | 401 | Jeton porteur manquant, expiré ou non valide. |
FORBIDDEN | 403 | L’authentification a réussi, mais ce contexte n’autorise pas cette action. |
ATTRIBUTION_REQUIRED | 403 | La politique d’attribution du compte de service est définie sur required et l’écriture ne comportait aucun Dust-Ctx-Declared-Actor. |
ORG_ID_REQUIRED / TEAM_ID_REQUIRED | 400 | Un en-tête de contexte manque sur un point de terminaison délimité. |
NOT_FOUND / NO_DATA_FOUND | 404 | Aucune fiche de ce type n’est visible dans ce contexte. |
RATE_LIMITED | 429 | Attendez en appliquant un délai progressif, puis réessayez. |
THREAD_DATA_CONFLICT | 409 | Conflit de concurrence optimiste : votre expectedUpdatedAt est obsolète. Relisez les données et réappliquez vos modifications. |
COMPOSITE_TAG_CONFLICT | 409 | Conflit d’étiquette : un DUST se trouve déjà sur une autre étiquette ou est lié ailleurs, une position de bobine est occupée, ou l’opération supprimerait le dernier identifiant d’une étiquette. detail.reason indique lequel de ces cas s’applique. |
UNKNOWN_ERROR / SERVICE_ERROR | 500 | Échec côté serveur. Réessayez avec un délai progressif ; si le problème persiste, joignez l’identifiant de la requête. |
Identifiants et scan
Section intitulée « Identifiants et scan »| Code | Statut | Signification |
|---|---|---|
IDENTIFIER_NOT_FOUND | 404 | Absence définitive de correspondance : chaque partition interrogée a répondu et aucune correspondance n’a été trouvée. |
IDENTIFIER_NOT_BOUND | 404 | L’identifiant existe, mais n’est lié à aucune fiche (accessible uniquement par une identification au moyen de tagId). |
IDENTIFIER_ALREADY_BOUND | 409 | Liaison refusée : cet identifiant, ou son étiquette, se trouve déjà sur une autre fiche. detail.compositeTagId désigne l’étiquette. |
IDENTIFIER_VERIFY_FAILED | 500 | La vérification d’un identifiant unique n’a trouvé aucune correspondance, ou l’identifiant indiqué n’est pas lié à cette fiche. |
SCAN_AMBIGUOUS_MATCH | 409 | Au moins deux DUST enregistrés distincts correspondent et sont tous deux liés dans le périmètre interrogé. Une nouvelle tentative avec la même capture ne peut pas résoudre le problème. |
SCAN_SEARCH_INCOMPLETE | 503 | Certaines partitions ont répondu « aucune correspondance », mais d’autres n’ont pas pu être interrogées. Il ne s’agit pas d’une absence de correspondance : réessayez. |
SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS | 400 | La capture elle-même a été rejetée : elle contient trop peu de détails exploitables. Effectuez un nouveau scan ; ne réessayez pas avec la même image. |
SCAN_IDENTICAL_SCAN | 400 | L’image envoyée contient exactement les mêmes octets qu’une capture précédente. Prenez-en une nouvelle. |
SCAN_EXTRACTION_FAILURE | 500 | L’extraction a échoué sur une image qui avait par ailleurs été acceptée. |
SCAN_ROUTING_UNAVAILABLE | 503 | L’opération n’est pas disponible pour cette organisation, par exemple parce qu’un module n’est pas activé, ou sa route est indisponible. |
SCAN_BACKEND_UNAVAILABLE | 503 | Le moteur de scan est temporairement indisponible. La capture ne présente aucun problème : réessayez sans effectuer de nouveau scan. |
Résultats canoniques d’identification
Section intitulée « Résultats canoniques d’identification »POST /api/v1/tags/identify présente huit résultats possibles. Trois renvoient un statut 200 ; les autres sont renvoyés avec des statuts d’erreur, mais constituent néanmoins des réponses. Ce tableau est la source de référence unique pour les intégrations humaines comme pour celles des agents ; le même tableau figure dans les skills dice-api-integration et dust-go-connect-integration.
| Résultat | HTTP | Corps | Signification | Action à effectuer |
|---|---|---|---|---|
| Fiche identifiée | 200 | { type: "identified", identified: { tag, thread, … }, scan? } | Exactement un identifiant lié correspond. | Ouvrez la fiche. scan.dustId est le DUST résolu. |
| Plusieurs candidats | 200 | { type: "matches", matches: [ … ], scan? } | Plusieurs identifiants liés correspondent, ou la correspondance doit être désambiguïsée. | Affichez les candidats et recommencez l’identification au moyen de tagId (tagType: "ANY"). scan.dustId vaut null ; chaque candidat comporte son propre identifiant. |
| Étiquette non liée | 200 | { type: "label", label: { label, tags }, scan? } | Le scan a été résolu comme un membre d’une étiquette figurant dans l’inventaire de votre équipe et qui n’est encore liée à aucune fiche. | Proposez de lier l’étiquette. Il ne s’agit pas d’une absence de correspondance. |
| Aucune correspondance | 404 | code: "IDENTIFIER_NOT_FOUND", detail.outcome: "no_match" | Chaque partition interrogée a répondu et aucune correspondance n’a été trouvée. detail.teamsSearched indique le périmètre. | Affichez « introuvable ». Ne signalez pas un échec du service. Le reçu se trouve dans detail.scan. |
| Recherche incomplète | 503 | code: "SCAN_SEARCH_INCOMPLETE", detail.outcome: "search_incomplete" | Certaines partitions ont répondu « aucune correspondance », mais d’autres n’ont pas pu être jointes. detail.teamsSearched, detail.teamsUnreachable, detail.orgsUnreachable. | Réessayez. Ne présentez jamais ce résultat comme « introuvable » : l’élément peut très bien être enregistré. |
| Correspondance ambiguë | 409 | code: "SCAN_AMBIGUOUS_MATCH", detail.outcome: "ambiguous", detail.candidates, detail.boundCandidates, detail.attempts | Au moins deux DUST liés correspondent avec un degré de confiance élevé. La plateforme a interrogé deux fois la même capture avant de renvoyer ce résultat. | Signalez le résultat en indiquant scan.scanId et contactez DUST Identity. Une nouvelle capture du même élément ne résoudra pas le problème. |
| Capture rejetée | 400 | code: "SCAN_LOW_KEYPOINTS" / "SCAN_NO_KEYPOINTS" / "SCAN_IDENTICAL_SCAN", detail.outcome: "quality_reject" | L’image n’a pas pu être utilisée. Le rejet pour des raisons de qualité prévaut sur tous les autres résultats des partitions. | Demandez à l’opérateur d’effectuer un nouveau scan. L’image est conservée en tant que scan rejeté ; detail.scan.fingerprintId vaut null. |
| Identifiant non lié | 404 | code: "IDENTIFIER_NOT_BOUND" | Uniquement lors d’une identification au moyen de tagId (tagType: "ANY") : l’identifiant existe, mais n’est associé à aucune fiche. | Proposez de le lier. |
detail.outcome correspond à la classification utilisée par le serveur et reste stable : no_match, search_incomplete, ambiguous, quality_reject. Créez d’abord vos embranchements à partir de code, puis consultez detail.outcome lorsque vous avez besoin d’une distinction plus précise.
Créer des embranchements à partir d’un résultat d’identification
Section intitulée « Créer des embranchements à partir d’un résultat d’identification »// Runs on your SERVER (it holds the bearer token).const response = await fetch(`${apidUrl}/api/v1/tags/identify`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Dust-Ctx-Org-Id": organizationId }, body: form,});const body = await response.json();
if (response.ok) { switch (body.type) { case "identified": return { kind: "thread", thread: body.identified.thread }; case "matches": return { kind: "candidates", candidates: body.matches }; case "label": return { kind: "unbound-label", label: body.label }; default: throw new Error(`Unknown identify result type: ${body.type}`); }}
switch (body.code) { case "IDENTIFIER_NOT_FOUND": // An answer, not an outage. return { kind: "no-match", scanId: body.detail?.scan?.scanId ?? null }; case "SCAN_SEARCH_INCOMPLETE": case "SCAN_BACKEND_UNAVAILABLE": return { kind: "retry", scanId: body.detail?.scan?.scanId ?? null }; case "SCAN_LOW_KEYPOINTS": case "SCAN_NO_KEYPOINTS": case "SCAN_IDENTICAL_SCAN": return { kind: "rescan", scanId: body.detail?.scan?.scanId ?? null }; case "SCAN_AMBIGUOUS_MATCH": return { kind: "ambiguous", scanId: body.detail?.scan?.scanId ?? null }; default: throw new Error(`${body.code}: ${body.message}`);}Résultats de vérification
Section intitulée « Résultats de vérification »Le comportement de POST /api/v1/tags/verify dépend du nombre d’identifiants candidats que vous envoyez dans tags, car un seul candidat constitue une question binaire, tandis que plusieurs candidats constituent une recherche :
Longueur de tags | Correspondance | Aucune correspondance |
|---|---|---|
| Exactement un | 200 avec { tag, scan? } | IDENTIFIER_VERIFY_FAILED (500), reçu dans detail.scan |
| Au moins deux | 200 avec { success: true, verifiedTag, attemptedCount, failedCount, scan? } | 200 avec { success: false, attemptedCount, failedCount, error, scan? } |
Ainsi, une vérification en masse qui échoue est un appel HTTP réussi avec success: false. Lisez toujours success lorsque vous envoyez plusieurs candidats et ne déduisez jamais l’authenticité à partir de la seule valeur de response.ok.
tags est obligatoire pour chaque vérification. Il s’agit d’un tableau d’objets, et non d’identifiants :
[{ "tagId": "8f2b…", "tagType": "DUST" }]Les reçus de scan subsistent après un échec
Section intitulée « Les reçus de scan subsistent après un échec »Chaque opération qui envoie une image — extraction, liaison, identification, vérification, analyse d’altération — renvoie un reçu de scan indiquant ce qui a été stocké :
{ "scanId": "…", "fingerprintId": "…", "dustId": "…" }- En cas de réussite, il se trouve au niveau supérieur sous la forme
scan. - Lorsqu’un résultat négatif a néanmoins été stocké, il se trouve dans
detail.scan: non-correspondance lors d’une vérification, aucune correspondance lors d’une identification, liaison refusée en raison d’un doublon ou rejet pour des raisons de qualité (auquel casfingerprintIdvautnull, car aucun élément exploitable n’a été extrait).
Récupérez scanId dans les deux cas. Il constitue l’identité stable de la capture au fil des migrations d’algorithmes, et l’assistance en a besoin pour examiner l’image à l’origine d’un résultat contesté. Seule une image que la plateforme n’a pas du tout pu décoder n’entraîne aucun stockage ; dans ce cas, il n’existe aucun reçu.
const receipt = response.ok ? body.scan : body.detail?.scan;if (receipt) await recordScan(receipt.scanId, receipt.fingerprintId, receipt.dustId);dustId correspond à ce que la plateforme vous a renvoyé, jamais à une correspondance interne brute : il vaut null en cas de non-correspondance, d’absence de correspondance, d’extraction, d’analyse d’altération, ainsi que lors d’une identification ayant renvoyé plusieurs candidats.
Expiration et renouvellement des jetons
Section intitulée « Expiration et renouvellement des jetons »Les jetons porteurs ont une durée de vie courte et il n’existe aucun jeton de renouvellement : vous devez échanger de nouveau l’identifiant d’accès. Un jeton expiré produit un simple statut 401 UNAUTHORIZED, impossible à distinguer de celui d’un jeton révoqué ; traitez donc ces deux cas de la même manière :
- Renouvelez de manière proactive.
GET /api/auth/tokenrenvoieexpiresIn(en secondes) etexpiresAt(au format ISO 8601) chaque fois que le jeton comporte une déclaration d’expiration. Procédez à un nouvel échange en prévoyant une marge (60 secondes constituent une marge confortable) ; ne codez jamais une durée de vie en dur. - Réessayez une seule fois après un statut
401. Une dérive d’horloge comme une révocation en cours de validité produisent ce statut. Un renouvellement suivi d’une nouvelle tentative suffit ; n’utilisez pas de boucle. - Créez un jeton pour chaque requête à partir d’un cache, et non une seule fois au démarrage du processus, afin qu’une tâche dont la durée dépasse celle d’un jeton n’échoue pas à mi-parcours.
L’implémentation complète se trouve dans Authentification → Expiration et renouvellement des jetons.
Recommandations concernant les nouvelles tentatives
Section intitulée « Recommandations concernant les nouvelles tentatives »| Situation | Réessayer la même requête ? | Remarques |
|---|---|---|
401 UNAUTHORIZED | Oui, une fois, après un nouvel échange de l’identifiant d’accès | Au-delà d’une tentative, cela signifie que l’identifiant d’accès lui-même est incorrect. |
429 RATE_LIMITED | Oui, avec un délai progressif | |
503 SCAN_SEARCH_INCOMPLETE / SCAN_BACKEND_UNAVAILABLE | Oui, la capture ne présente aucun problème | Ne demandez pas à l’opérateur d’effectuer un nouveau scan. |
503 SCAN_ROUTING_UNAVAILABLE | Non | L’opération n’est pas disponible pour cette organisation ; contactez DUST Identity. |
400 SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS / SCAN_IDENTICAL_SCAN | Non, effectuez plutôt un nouveau scan | Les mêmes octets seront de nouveau rejetés. |
404 IDENTIFIER_NOT_FOUND | Non | Il s’agit d’une réponse. |
409 SCAN_AMBIGUOUS_MATCH | Non | Le serveur a déjà effectué une nouvelle tentative ; detail.attempts l’indique. |
409 THREAD_DATA_CONFLICT | Relisez, réappliquez, puis écrivez | Ne réessayez pas aveuglément : vous écraseriez les modifications de quelqu’un d’autre. |
5xx UNKNOWN_ERROR | Oui, avec un délai progressif, pour les lectures idempotentes | Pour les écritures, vérifiez si l’écriture a abouti avant de réessayer. |
Voir aussi
Section intitulée « Voir aussi »- Conventions relatives aux requêtes — en-têtes, pagination et localisation.
- Identifiants — opérations de scan à l’origine de ces résultats.
- Authentification et clés d’API — identifiants d’accès et durée de vie des jetons.
- Référence de l’API — schémas de réponse propres à chaque point de terminaison.