Errores y resultados de escaneo
Esta es la página de consulta para gestionar fallos: el cuerpo de error que devuelve cada endpoint, los códigos para los que merece la pena crear ramas y, lo más importante, las tablas canónicas de resultados de los escaneos. «Sin coincidencia» es un resultado, no un fallo de transporte, y aun así llega con un estado de error HTTP. El código que trata cada respuesta que no sea 2xx como un error notificará interrupciones que nunca ocurrieron.
Los esquemas de cada endpoint están en la referencia de la API. Los propios flujos están en Identificadores y en la guía de inicio rápido.
La envoltura de errores
Sección titulada «La envoltura de errores»Cada solicitud fallida devuelve el mismo objeto 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 } }}| Campo | Tipo | Significado |
|---|---|---|
code | string | Código estable legible por máquina. Cree ramas basándose en este campo. Nunca analice message. |
message | string | Texto legible por personas, localizado según Dust-Ctx-Locale. La redacción cambia; los códigos no. |
status | number | Refleja el estado HTTP. |
detail | object, opcional | Contexto específico del error: detalles de validación, el recibo de escaneo, la clasificación del resultado e identificadores de conflictos. |
status y el estado HTTP siempre coinciden, por lo que puede crear ramas basándose en cualquiera de ellos. Cada respuesta, tanto correcta como fallida, también incluye una cabecera x-request-id. Regístrela; el equipo de soporte la utiliza para encontrar su solicitud exacta.
Códigos por clase
Sección titulada «Códigos por clase»Solicitud y autorización
Sección titulada «Solicitud y autorización»| Código | Estado | Cuándo |
|---|---|---|
INVALID_REQUEST | 400 | Cuerpo, consulta o cabecera con formato incorrecto. Los detalles de validación están en detail. |
INVALID_DATA | 400 | La solicitud se analizó, pero los valores no pueden utilizarse (por ejemplo, una carga útil de identificación que el servicio de escaneo se negó a leer). |
UNAUTHORIZED | 401 | Falta el token al portador, ha caducado o no es válido. |
FORBIDDEN | 403 | La autenticación es válida, pero este contexto no puede realizar esa acción. |
ATTRIBUTION_REQUIRED | 403 | La política de atribución de la cuenta de servicio es required y la escritura no incluía ningún Dust-Ctx-Declared-Actor. |
ORG_ID_REQUIRED / TEAM_ID_REQUIRED | 400 | Falta una cabecera de contexto en un endpoint con ámbito definido. |
NOT_FOUND / NO_DATA_FOUND | 404 | No hay ningún registro de ese tipo visible en este contexto. |
RATE_LIMITED | 429 | Espere un tiempo creciente y vuelva a intentarlo. |
THREAD_DATA_CONFLICT | 409 | Conflicto de concurrencia optimista: su expectedUpdatedAt está desactualizado. Vuelva a leer los datos y a aplicar los cambios. |
COMPOSITE_TAG_CONFLICT | 409 | Conflicto de Etiqueta: un DUST ya está en otra Etiqueta o vinculado en otro lugar, una posición de la bobina está ocupada o se está quitando el último Identificador de una Etiqueta. detail.reason indica cuál es el caso. |
UNKNOWN_ERROR / SERVICE_ERROR | 500 | Fallo del servidor. Vuelva a intentarlo con espera incremental; incluya el identificador de la solicitud si el problema persiste. |
Identificadores y escaneo
Sección titulada «Identificadores y escaneo»| Código | Estado | Significado |
|---|---|---|
IDENTIFIER_NOT_FOUND | 404 | Ausencia de coincidencia definitiva: todas las particiones consultadas respondieron y no hubo ninguna coincidencia. |
IDENTIFIER_NOT_BOUND | 404 | El Identificador existe, pero no está vinculado a ninguna Ficha (solo se puede llegar a este resultado mediante una identificación por tagId). |
IDENTIFIER_ALREADY_BOUND | 409 | Se rechazó la vinculación: ese Identificador (o su Etiqueta) ya está en otra Ficha. detail.compositeTagId identifica la Etiqueta. |
IDENTIFIER_VERIFY_FAILED | 500 | La verificación de un único Identificador no produjo una coincidencia, o el Identificador indicado no está vinculado a esa Ficha. |
SCAN_AMBIGUOUS_MATCH | 409 | Dos o más DUST inscritos distintos coincidieron y ambos están vinculados dentro del ámbito consultado. No se puede volver a intentar con la misma captura. |
SCAN_SEARCH_INCOMPLETE | 503 | Algunas particiones respondieron «sin coincidencia», pero no se pudieron consultar otras. Esto no es una ausencia de coincidencia: vuelva a intentarlo. |
SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS | 400 | Se rechazó la propia captura: no contiene suficientes detalles utilizables. Vuelva a escanear; no reintente con la misma imagen. |
SCAN_IDENTICAL_SCAN | 400 | La imagen enviada contiene exactamente los mismos bytes que una captura anterior. Tome una nueva. |
SCAN_EXTRACTION_FAILURE | 500 | La extracción falló en una imagen que, por lo demás, se había aceptado. |
SCAN_ROUTING_UNAVAILABLE | 503 | La operación no está disponible para esta organización (por ejemplo, un módulo que no está habilitado) o su ruta está fuera de servicio. |
SCAN_BACKEND_UNAVAILABLE | 503 | El backend de escaneo no está disponible temporalmente. La captura es válida: vuelva a intentarlo, no vuelva a escanear. |
Resultados canónicos de identificación
Sección titulada «Resultados canónicos de identificación»POST /api/v1/tags/identify tiene ocho resultados. Tres devuelven 200; el resto llegan con estados de error y aun así son respuestas. Esta tabla es la fuente única tanto para integraciones humanas como para integraciones de agentes: la misma tabla aparece en las skills dice-api-integration y dust-go-connect-integration.
| Resultado | HTTP | Cuerpo | Qué significa | Qué hacer |
|---|---|---|---|---|
| Ficha identificada | 200 | { type: "identified", identified: { tag, thread, … }, scan? } | Coincidió exactamente un Identificador vinculado. | Abra la Ficha. scan.dustId es el DUST resuelto. |
| Varios candidatos | 200 | { type: "matches", matches: [ … ], scan? } | Coincidió más de un Identificador vinculado o es necesario desambiguar la coincidencia. | Muestre los candidatos y vuelva a identificar por tagId (tagType: "ANY"). scan.dustId es null; cada candidato incluye el suyo. |
| Etiqueta sin vincular | 200 | { type: "label", label: { label, tags }, scan? } | El escaneo se resolvió como miembro de una Etiqueta del inventario de su Equipo que aún no está vinculada a ninguna Ficha. | Ofrezca vincular la Etiqueta. No es una ausencia de coincidencia. |
| Sin coincidencia | 404 | code: "IDENTIFIER_NOT_FOUND", detail.outcome: "no_match" | Todas las particiones consultadas respondieron y no hubo ninguna coincidencia. detail.teamsSearched indica el ámbito. | Muestre «no encontrado». No notifique un fallo del servicio. El recibo está en detail.scan. |
| Búsqueda incompleta | 503 | code: "SCAN_SEARCH_INCOMPLETE", detail.outcome: "search_incomplete" | Algunas particiones respondieron «sin coincidencia» y no se pudo acceder a otras. detail.teamsSearched, detail.teamsUnreachable, detail.orgsUnreachable. | Vuelva a intentarlo. Nunca muestre este resultado como «no encontrado»: es muy posible que el artículo esté inscrito. |
| Coincidencia ambigua | 409 | code: "SCAN_AMBIGUOUS_MATCH", detail.outcome: "ambiguous", detail.candidates, detail.boundCandidates, detail.attempts | Dos o más DUST vinculados coincidieron con un alto grado de confianza. La plataforma buscó dos veces con la misma captura antes de devolver este resultado. | Muestre el problema junto con scan.scanId y póngase en contacto con DUST Identity. Una nueva captura del mismo artículo no lo resolverá. |
| Captura rechazada | 400 | code: "SCAN_LOW_KEYPOINTS" / "SCAN_NO_KEYPOINTS" / "SCAN_IDENTICAL_SCAN", detail.outcome: "quality_reject" | No se pudo utilizar la imagen. El rechazo por calidad tiene prioridad sobre cualquier otro resultado de las particiones. | Pida al operador que vuelva a escanear. La imagen se conserva como Escaneo rechazado; detail.scan.fingerprintId es null. |
| Identificador sin vincular | 404 | code: "IDENTIFIER_NOT_BOUND" | Solo se produce en una identificación por tagId (tagType: "ANY"): el Identificador existe, pero no tiene ninguna Ficha. | Ofrezca vincularlo. |
detail.outcome es la clasificación utilizada por el servidor y es estable: no_match, search_incomplete, ambiguous, quality_reject. Cree ramas basándose primero en code y lea detail.outcome cuando necesite una distinción más precisa.
Creación de ramas según un resultado de identificación
Sección titulada «Creación de ramas según un resultado de identificación»// 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}`);}Resultados de verificación
Sección titulada «Resultados de verificación»POST /api/v1/tags/verify se comporta de forma diferente según cuántos Identificadores candidatos envíe en tags, porque un candidato plantea una pregunta de sí o no, mientras que varios candidatos constituyen una búsqueda:
Longitud de tags | Coincidencia | Sin coincidencia |
|---|---|---|
| Exactamente uno | 200 con { tag, scan? } | IDENTIFIER_VERIFY_FAILED (500), recibo en detail.scan |
| Dos o más | 200 con { success: true, verifiedTag, attemptedCount, failedCount, scan? } | 200 con { success: false, attemptedCount, failedCount, error, scan? } |
Por tanto, una verificación en bloque que falla es una llamada HTTP correcta con success: false. Lea siempre success cuando envíe más de un candidato y nunca deduzca la autenticidad únicamente a partir de response.ok.
tags es obligatorio en todas las verificaciones. Es un array de objetos, no de identificadores:
[{ "tagId": "8f2b…", "tagType": "DUST" }]Los recibos de escaneo sobreviven a los fallos
Sección titulada «Los recibos de escaneo sobreviven a los fallos»Cada operación que envía una imagen —extracción, vinculación, identificación, verificación o análisis de alteraciones— devuelve un recibo de escaneo que indica qué se almacenó:
{ "scanId": "…", "fingerprintId": "…", "dustId": "…" }- Cuando la operación se completa correctamente, aparece en el nivel superior como
scan. - Cuando el resultado se almacena, pero es negativo, aparece en
detail.scan: una verificación sin coincidencia, una identificación sin coincidencia, una vinculación rechazada por duplicidad o un rechazo por calidad (en cuyo casofingerprintIdesnull, porque no se extrajo nada utilizable).
Capture scanId en ambos casos. Es la identidad estable de la captura a través de las migraciones de algoritmos y es lo que el equipo de soporte necesita para consultar la imagen asociada a un resultado controvertido. Solo una imagen que la plataforma no pudo decodificar en absoluto no almacena nada; en ese caso no hay recibo.
const receipt = response.ok ? body.scan : body.detail?.scan;if (receipt) await recordScan(receipt.scanId, receipt.fingerprintId, receipt.dustId);dustId es lo que la plataforma le devolvió, nunca una coincidencia interna sin procesar: es null en una discrepancia, una ausencia de coincidencia, una extracción, un análisis de alteraciones y una identificación que devolvió varios candidatos.
Caducidad y renovación de tokens
Sección titulada «Caducidad y renovación de tokens»Los tokens al portador tienen una vida corta y no existe ningún token de actualización: debe volver a intercambiar la credencial. Un token caducado genera un 401 UNAUTHORIZED normal, indistinguible de uno revocado, por lo que debe gestionar ambos del mismo modo:
- Renueve el token de forma proactiva.
GET /api/auth/tokendevuelveexpiresIn(segundos) yexpiresAt(ISO 8601) siempre que el token incluya una declaración de caducidad. Vuelva a intercambiarlo con cierto margen (60 segundos resulta adecuado); nunca codifique de forma fija su duración. - Reintente una vez tras un
401. Tanto el desfase del reloj como una revocación durante la vigencia del token producen este resultado. Una renovación seguida de un único reintento es suficiente; un bucle no lo es. - Emita un token por solicitud desde una caché, no una sola vez al iniciar el proceso, para que un trabajo que dure más que un token no falle a mitad de la ejecución.
La implementación completa está en Autenticación → Caducidad y renovación de tokens.
Recomendaciones de reintento
Sección titulada «Recomendaciones de reintento»| Situación | ¿Reintentar la misma solicitud? | Notas |
|---|---|---|
401 UNAUTHORIZED | Sí, una vez, después de volver a intercambiar la credencial | Más de un intento significa que la propia credencial es incorrecta. |
429 RATE_LIMITED | Sí, con espera incremental | |
503 SCAN_SEARCH_INCOMPLETE / SCAN_BACKEND_UNAVAILABLE | Sí; la captura es válida | No haga que el operador vuelva a escanear. |
503 SCAN_ROUTING_UNAVAILABLE | No | La operación no está disponible para esta organización; consulte a DUST Identity. |
400 SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS / SCAN_IDENTICAL_SCAN | No; vuelva a escanear | Los mismos bytes volverán a rechazarse. |
404 IDENTIFIER_NOT_FOUND | No | Es una respuesta. |
409 SCAN_AMBIGUOUS_MATCH | No | Ya se volvió a intentar en el servidor; detail.attempts lo indica. |
409 THREAD_DATA_CONFLICT | Vuelva a leer, aplique de nuevo los cambios y, después, escriba | No reintente a ciegas: sobrescribiría los cambios de otra persona. |
5xx UNKNOWN_ERROR | Sí, con espera incremental, para lecturas idempotentes | Para escrituras, compruebe si la escritura se realizó antes de reintentarlo. |
Véase también
Sección titulada «Véase también»- Convenciones de las solicitudes — cabeceras, paginación y localización.
- Identificadores — las operaciones de escaneo de las que proceden estos resultados.
- Autenticación y claves de API — credenciales y duración de los tokens.
- Referencia de la API — esquemas de respuesta de cada endpoint.