Pular para o conteúdo

Erros e resultados de digitalização

Esta é a página de consulta para o tratamento de falhas: o corpo do erro devolvido por todos os endpoints, os códigos que justificam ramificações e — mais importante — as tabelas canónicas de resultados das digitalizações. Uma “ausência de correspondência” é um resultado, não uma falha de transporte, e ainda assim chega com um estado de erro HTTP. O código que trata todos os estados que não sejam 2xx como erros irá comunicar interrupções que nunca ocorreram.

Os esquemas específicos de cada endpoint encontram-se na referência da API. Os próprios fluxos encontram-se em Identificadores e no guia de início rápido.

Todos os pedidos que falham devolvem o mesmo 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 } }
}
CampoTipoSignificado
codestringCódigo estável e legível por máquina. Crie ramificações com base neste campo. Nunca analise message.
messagestringTexto legível por pessoas, localizado de acordo com Dust-Ctx-Locale. A redação muda; os códigos não.
statusnumberReflete o estado HTTP.
detailobject, opcionalContexto específico do erro: detalhes da validação, recibo da digitalização, classificação do resultado e identificadores de conflitos.

status e o estado HTTP coincidem sempre, pelo que pode criar ramificações com base em qualquer um deles. Todas as respostas — de sucesso ou falha — também incluem um cabeçalho x-request-id. Registe-o; o suporte utiliza-o para localizar o seu pedido exato.

CódigoEstadoQuando
INVALID_REQUEST400Corpo, consulta ou cabeçalho malformado. Os detalhes da validação encontram-se em detail.
INVALID_DATA400O pedido foi analisado, mas os valores não podem ser utilizados (por exemplo, um conteúdo de identificação que o serviço de digitalização se recusou a ler).
UNAUTHORIZED401Token bearer ausente, expirado ou inválido.
FORBIDDEN403O contexto está autenticado, mas não pode efetuar esta ação.
ATTRIBUTION_REQUIRED403A política de atribuição da Conta de serviço é required e a escrita não incluiu Dust-Ctx-Declared-Actor.
ORG_ID_REQUIRED / TEAM_ID_REQUIRED400Falta um cabeçalho de contexto num endpoint com âmbito definido.
NOT_FOUND / NO_DATA_FOUND404Nenhum registo desse tipo está visível neste contexto.
RATE_LIMITED429Aguarde progressivamente e tente novamente.
THREAD_DATA_CONFLICT409Conflito de concorrência otimista: o seu expectedUpdatedAt está desatualizado. Volte a ler e a aplicar.
COMPOSITE_TAG_CONFLICT409Conflito de Etiqueta — um DUST já se encontra noutra Etiqueta ou está vinculado noutro local, uma Posição do Rolo está ocupada ou está a ser removido o último Identificador de uma Etiqueta. detail.reason indica qual destas situações ocorreu.
UNKNOWN_ERROR / SERVICE_ERROR500Falha do lado do servidor. Tente novamente com espera progressiva; inclua o identificador do pedido se a falha persistir.
CódigoEstadoSignificado
IDENTIFIER_NOT_FOUND404Uma ausência de correspondência definitiva: todas as partições pesquisadas responderam e não foi encontrada qualquer correspondência.
IDENTIFIER_NOT_BOUND404O Identificador existe, mas não está vinculado a nenhum Registo (apenas acessível através de uma identificação por tagId).
IDENTIFIER_ALREADY_BOUND409Vinculação recusada — esse Identificador (ou a respetiva Etiqueta) já se encontra noutro Registo. detail.compositeTagId identifica a Etiqueta.
IDENTIFIER_VERIFY_FAILED500A verificação de um único Identificador não encontrou correspondência ou o Identificador indicado não está vinculado a esse Registo.
SCAN_AMBIGUOUS_MATCH409Dois ou mais DUSTs inscritos distintos corresponderam e ambos estão vinculados no âmbito pesquisado. Não é possível repetir a tentativa com a mesma captura.
SCAN_SEARCH_INCOMPLETE503Algumas partições responderam “sem correspondência”, mas não foi possível pesquisar outras. Isto não é uma ausência de correspondência — tente novamente.
SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS400A própria captura foi rejeitada: contém muito pouco detalhe utilizável. Volte a digitalizar; não tente novamente com a mesma imagem.
SCAN_IDENTICAL_SCAN400A imagem enviada contém exatamente os mesmos bytes de uma captura anterior. Efetue uma nova captura.
SCAN_EXTRACTION_FAILURE500A extração falhou numa imagem que, de outro modo, foi aceite.
SCAN_ROUTING_UNAVAILABLE503A operação não está disponível para esta organização (por exemplo, devido a um módulo que não está ativado) ou a respetiva rota está indisponível.
SCAN_BACKEND_UNAVAILABLE503O backend de digitalização está temporariamente indisponível. A captura está correta — tente novamente, não volte a digitalizar.

POST /api/v1/tags/identify tem oito resultados. Três são 200; os restantes chegam com estados de erro e continuam a ser respostas. Esta tabela é a fonte única tanto para integrações humanas como de agentes — a mesma tabela aparece nas skills dice-api-integration e dust-go-connect-integration.

ResultadoHTTPCorpoO que significaO que fazer
Registo identificado200{ type: "identified", identified: { tag, thread, … }, scan? }Foi encontrada correspondência com exatamente um Identificador vinculado.Abrir o Registo. scan.dustId é o DUST resolvido.
Vários candidatos200{ type: "matches", matches: [ … ], scan? }Foi encontrada correspondência com mais de um Identificador vinculado ou é necessário desambiguar a correspondência.Mostrar os candidatos e voltar a identificar por tagId (tagType: "ANY"). scan.dustId é null; cada candidato inclui o seu próprio valor.
Etiqueta não vinculada200{ type: "label", label: { label, tags }, scan? }A digitalização foi resolvida para um membro de uma Etiqueta no inventário da sua Equipa que ainda não está vinculada a nenhum Registo.Disponibilizar a vinculação da Etiqueta. Não é uma ausência de correspondência.
Sem correspondência404code: "IDENTIFIER_NOT_FOUND", detail.outcome: "no_match"Todas as partições pesquisadas responderam e não foi encontrada qualquer correspondência. detail.teamsSearched indica o âmbito.Mostrar “não encontrado”. Não comunicar uma falha do serviço. O recibo encontra-se em detail.scan.
Pesquisa incompleta503code: "SCAN_SEARCH_INCOMPLETE", detail.outcome: "search_incomplete"Algumas partições responderam “sem correspondência”; não foi possível aceder a outras. detail.teamsSearched, detail.teamsUnreachable, detail.orgsUnreachable.Tentar novamente. Nunca apresentar este resultado como “não encontrado” — o item pode muito bem estar inscrito.
Correspondência ambígua409code: "SCAN_AMBIGUOUS_MATCH", detail.outcome: "ambiguous", detail.candidates, detail.boundCandidates, detail.attemptsFoi encontrada uma correspondência fiável com dois ou mais DUSTs vinculados. A plataforma pesquisou a mesma captura duas vezes antes de devolver este resultado.Apresentar o resultado com o scan.scanId e contactar a DUST Identity. Uma nova captura do mesmo item não resolverá a situação.
Captura rejeitada400code: "SCAN_LOW_KEYPOINTS" / "SCAN_NO_KEYPOINTS" / "SCAN_IDENTICAL_SCAN", detail.outcome: "quality_reject"Não foi possível utilizar a imagem. A rejeição por qualidade prevalece sobre todos os outros resultados das partições.Pedir ao operador que volte a digitalizar. A imagem é conservada como Digitalização rejeitada; detail.scan.fingerprintId é null.
Identificador não vinculado404code: "IDENTIFIER_NOT_BOUND"Ocorre apenas numa identificação por tagId (tagType: "ANY"): o Identificador existe, mas não tem Registo.Disponibilizar a respetiva vinculação.

detail.outcome é a classificação utilizada pelo servidor e é estável: no_match, search_incomplete, ambiguous, quality_reject. Crie primeiro ramificações com base em code e consulte detail.outcome quando precisar de uma distinção mais específica.

Criar ramificações com base num resultado de identificação

Seção intitulada “Criar ramificações com base num resultado de identificação”
// 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}`);
}

POST /api/v1/tags/verify comporta-se de forma diferente consoante o número de Identificadores candidatos enviados em tags, porque um candidato constitui uma pergunta de sim/não e vários constituem uma pesquisa:

Comprimento de tagsCorrespondênciaSem correspondência
Exatamente um200 com { tag, scan? }IDENTIFIER_VERIFY_FAILED (500), recibo em detail.scan
Dois ou mais200 com { success: true, verifiedTag, attemptedCount, failedCount, scan? }200 com { success: false, attemptedCount, failedCount, error, scan? }

Assim, uma verificação em massa que falha é uma chamada HTTP bem-sucedida com success: false. Consulte sempre success quando enviar mais de um candidato e nunca deduza a autenticidade apenas a partir de response.ok.

tags é obrigatório em todas as verificações. É uma matriz de objetos, não de identificadores:

[{ "tagId": "8f2b…", "tagType": "DUST" }]

Os recibos de digitalização persistem após uma falha

Seção intitulada “Os recibos de digitalização persistem após uma falha”

Todas as operações que enviam uma imagem — extração, vinculação, identificação, verificação e análise de adulteração — devolvem um recibo de digitalização que identifica o que foi armazenado:

{ "scanId": "…", "fingerprintId": "…", "dustId": "…" }
  • Em caso de sucesso, encontra-se no nível superior como scan.
  • Num resultado negativo que tenha sido armazenado, encontra-se em detail.scan — uma falta de correspondência na verificação, uma ausência de correspondência na identificação, uma vinculação recusada por duplicação ou uma rejeição por qualidade (na qual fingerprintId é null, porque não foi extraído nada utilizável).

Capture scanId em ambos os casos. É a identidade estável da captura entre migrações de algoritmos e é o que o suporte necessita para consultar a imagem associada a um resultado contestado. Apenas uma imagem que a plataforma não tenha conseguido descodificar de todo não armazena nada e, nesse caso, não existe recibo.

const receipt = response.ok ? body.scan : body.detail?.scan;
if (receipt) await recordScan(receipt.scanId, receipt.fingerprintId, receipt.dustId);

dustId é o que a plataforma lhe devolveu, nunca uma correspondência interna não processada: é null numa falta de correspondência, numa ausência de correspondência, numa extração, numa análise de adulteração e numa identificação que devolveu vários candidatos.

Os tokens bearer têm uma duração curta e não existe qualquer token de renovação — é necessário voltar a trocar a credencial. Um token expirado resulta num 401 UNAUTHORIZED normal, indistinguível de um token revogado, pelo que deve tratar ambos da mesma forma:

  1. Renove proativamente. GET /api/auth/token devolve expiresIn (segundos) e expiresAt (ISO 8601) sempre que o token contém uma declaração de expiração. Volte a efetuar a troca com alguma margem (60 segundos é confortável); nunca codifique uma duração fixa.
  2. Tente novamente uma vez após um 401. Tanto o desvio do relógio como a revogação durante a validade resultam neste estado. Uma renovação seguida de uma nova tentativa é o adequado; um ciclo não é.
  3. Emita um token por pedido a partir de uma cache, não apenas uma vez no arranque do processo, para que um trabalho que dure mais do que um token não falhe a meio.

A implementação completa encontra-se em Autenticação → Expiração e renovação de tokens.

SituaçãoTentar novamente o mesmo pedido?Notas
401 UNAUTHORIZEDSim, uma vez, depois de voltar a trocar a credencialMais do que uma vez significa que a própria credencial está errada.
429 RATE_LIMITEDSim, com espera progressiva
503 SCAN_SEARCH_INCOMPLETE / SCAN_BACKEND_UNAVAILABLESim — a captura está corretaNão obrigue o operador a voltar a digitalizar.
503 SCAN_ROUTING_UNAVAILABLENãoA operação não está disponível para esta organização; contacte a DUST Identity.
400 SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS / SCAN_IDENTICAL_SCANNão — volte a digitalizarOs mesmos bytes voltarão a ser rejeitados.
404 IDENTIFIER_NOT_FOUNDNãoÉ uma resposta.
409 SCAN_AMBIGUOUS_MATCHNãoA tentativa já foi repetida no lado do servidor; detail.attempts indica-o.
409 THREAD_DATA_CONFLICTVoltar a ler, aplicar novamente e depois escreverNão repita o pedido sem verificar — iria substituir as alterações de outra pessoa.
5xx UNKNOWN_ERRORSim, com espera progressiva, para leituras idempotentesNas escritas, verifique se a escrita foi aplicada antes de tentar novamente.