Pular para o conteúdo

Guia da API de Identificadores

Um identificador liga uma marcação física a um Registo: uma etiqueta DUST, um código QR, um código de barras, um símbolo Data Matrix, um chip NFC ou um código de texto impresso. Depois de vinculado, uma digitalização no terreno é resolvida para o registo digital. O espaço de nomes da API é /api/v1/tags — uma designação legada que se mantém nos caminhos e esquemas; esta documentação utiliza identificador no texto.

Esquemas completos de pedidos/respostas: referência da API. Todos os resultados que cada operação pode produzir — incluindo os que chegam como erros HTTP — estão reunidos numa tabela em Erros e resultados de digitalização.

OperaçãoMétodo e caminhoSemântica
ExtrairPOST /api/v1/tags/extractAnalisar uma captura DUST para obter uma impressão digital canónica, sem vincular
VincularPOST /api/v1/tags/bindAssociar um identificador a um Registo
IdentificarPOST /api/v1/tags/identifyPesquisar: que Registo corresponde a esta digitalização?
VerificarPOST /api/v1/tags/verifyComparar uma digitalização com os identificadores de um Registo específico
DesvincularPOST /api/v1/tags/unbindSeparar um identificador do respetivo Registo
Definir textoPOST /api/v1/tags/textMudar o nome/a descrição de um identificador vinculado
AtualizarPOST /api/v1/tags/updateCiclo de vida: privacidade, arquivar/restaurar (o valor e o tipo são imutáveis)

Identificar ou verificar: identificar responde a «o que é isto?» — pesquisa os Registos visíveis para si (com âmbito definido por searchTeamIds) e devolve a correspondência, se existir. Verificar responde a «este é o item que afirma ser?» — indica um threadId e os identificadores candidatos vinculados ao mesmo, e a API confirma ou nega. Utilize a verificação para decisões de autenticação e a identificação para pesquisas.

Os endpoints de digitalização aceitam multipart/form-data, e a estrutura de data depende do tipo de identificador:

tagTypedataOrigem
DUSTUma imagem — uma parte de ficheiro binário ou um URL de dados em base64 (data:image/jpeg;base64,…)Uma captura ótica DUST proveniente de um scanner
QR, BAR_CODE, DATA_MATRIX, NFCO conteúdo descodificado da cadeia (ou o ID hexadecimal NFC)Qualquer leitor de símbolos
TEXTO código legível por pessoas impresso, tal como uma pessoa o lêIntrodução pelo teclado ou inscrição de uma Etiqueta

Uma captura DUST é uma fotografia da etiqueta, não um valor descodificado — o servidor extrai a impressão digital. As capturas provêm de hardware de digitalização DUST: consulte Integrar com o DUST Go para efetuar capturas em dispositivos móveis e o React Scanner para obter um componente Web pronto a integrar que processa todos os modos.

Em corpos multipart, os campos estruturados (options, tags, searchTeamIds) são transmitidos como cadeias JSON.

TEXT é o código legível por pessoas impresso num item ou numa Etiqueta — um número de série como AB00017. Não existe qualquer símbolo para descodificar, pelo que o valor é introduzido manualmente (ou provém do registo de inscrição de uma Etiqueta) e é guardado exatamente como foi introduzido. Uma vez que o leitor é uma pessoa, TEXT é o único tipo que a plataforma compara sem distinguir maiúsculas de minúsculas: identificar ou verificar com ab00017 encontra um AB00017 vinculado. Todos os outros tipos são comparados byte a byte.

Tal como os valores de QR, código de barras, Data Matrix e NFC, um código de texto pode ser copiado e não possui unicidade intrínseca — o mesmo código pode aparecer legitimamente em vários Registos ou em todas as Etiquetas de um Rolo. Um valor repetido nunca é rejeitado; o Rolo limita-se a comunicar as repetições como aviso.

A extração analisa uma captura para obter uma impressão digital canónica e devolve a respetiva qualidade — útil para conferir uma captura antes da inscrição ou para preparar uma vinculação:

Terminal window
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…"}'

A resposta é { id, qualityScore, annotatedImage?, forensics?, scan? } — id é o ID de uma impressão digital que pode vincular posteriormente sem voltar a carregar a imagem (ver abaixo). options também inclui metadados da captura (dispositivo, ótica, geolocalização) que a plataforma guarda com a digitalização.

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

{ "scanId": "…", "fingerprintId": "…", "dustId": "…" }
  • scanId está sempre presente depois de a imagem ser guardada. É a identidade estável da captura e o valor a conservar caso registe as operações de digitalização do seu lado.
  • fingerprintId está presente quando a extração foi bem-sucedida (caso contrário, é null).
  • dustId está presente quando a operação lhe devolveu um DUST: o identificador criado por uma vinculação, confirmado por uma verificação ou resolvido por uma identificação. É null numa falta de correspondência, num resultado sem correspondência, numa extração, numa análise de adulteração (nesse caso, o identificador foi fornecido por si e não resolvido pela imagem) e numa identificação que tenha devolvido vários candidatos (cada candidato inclui o seu próprio identificador).

Uma falta de correspondência numa verificação e a ausência de correspondência numa identificação mantêm o respetivo estado e código de erro e incluem o mesmo recibo em detail.scan — a digitalização foi guardada apesar de o resultado ser negativo. O mesmo acontece com uma captura rejeitada por motivos de qualidade — por ter poucos pontos-chave utilizáveis ou nenhum — independentemente do código de erro comunicado pela operação (/tags/extract responde com SCAN_EXTRACTION_FAILURE; a identificação expõe SCAN_LOW_KEYPOINTS/SCAN_NO_KEYPOINTS; a verificação responde com o habitual IDENTIFIER_VERIFY_FAILED): a imagem é conservada e o respetivo recibo tem fingerprintId: null, porque não foi possível extrair nada utilizável. Apenas uma imagem que a plataforma não conseguiu descodificar de todo não é guardada nem tem recibo.

POST /api/v1/tags/bind aceita três estruturas, diferenciadas por tagType e pelo conteúdo:

Terminal window
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…"}'

options.enrollmentSessionId é opcional numa vinculação DUST. Forneça um UUID gerado pelo cliente — o mesmo durante toda uma execução — quando várias capturas fizerem parte do mesmo conjunto, como uma estação de inscrição a processar um lote ou uma captura de um item a partir de vários ângulos; a plataforma agrupa essas digitalizações nessa sessão. Omita-o por completo para uma vinculação pontual. As vinculações de imagens DUST também podem devolver a captura anotada com options.returnAnnotatedImage: true.

Se o identificador digitalizado pertencer a uma Etiqueta propriedade da Equipa do Registo (consulte Etiquetas), a vinculação não cria um identificador independente. Vincula a Etiqueta completa: todos os identificadores membros ativos são associados ao Registo numa única operação, e a resposta inclui label (a Etiqueta, o respetivo Rolo e a posição) e boundTags (todos os membros vinculados), além do tag habitual, que corresponde ao membro digitalizado. Transmita activateLabel: true para tornar também identificáveis os identificadores DUST da Etiqueta durante a vinculação; esta opção é facultativa. Uma Etiqueta já vinculada a outro Registo devolve IDENTIFIER_ALREADY_BOUND com detail.compositeTagId.

Identificar um Registo a partir de uma digitalização

Seção intitulada “Identificar um Registo a partir de uma digitalização”
Terminal window
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"'"]'

A identificação também aceita conteúdos de valor (tagType de QR/BAR_CODE/DATA_MATRIX/NFC com os data descodificados ou TEXT com o código impresso, comparado sem distinguir maiúsculas de minúsculas) ou apenas um ID de identificador (tagType: "ANY" com tagId).

Uma correspondência devolve 200 com o identificador correspondente e o respetivo Registo. Uma ausência de correspondência é um estado de erro, não um 200 com um resultado vazio: uma ausência definitiva de correspondência é 404 IDENTIFIER_NOT_FOUND, enquanto uma pesquisa que não pôde ser concluída é 503 SCAN_SEARCH_INCOMPLETE — uma situação diferente que não pode ser apresentada a um operador como «não encontrado». Ambas incluem o recibo da digitalização em detail.scan.

A tabela canónica dos oito resultados — Registo identificado, vários candidatos, Etiqueta não vinculada, nenhuma correspondência, pesquisa incompleta, correspondência ambígua, captura rejeitada e identificador não vinculado — com o estado, código e resposta correta do cliente para cada um, encontra-se em Erros e resultados de digitalização → Resultados canónicos da identificação. Ramifique com base em code e leia detail.outcome (no_match, search_incomplete, ambiguous, quality_reject) quando precisar da distinção mais específica.

Todos os identificadores devolvidos que pertençam a uma Etiqueta incluem tag.label (a respetiva Etiqueta, Rolo e posição). Quando a digitalização corresponde a um membro de uma Etiqueta não vinculada no inventário da Equipa ativa, o resultado é { type: "label", label: { label, tags } }: ainda não existe um Registo, mas são devolvidos a Etiqueta e os respetivos identificadores membros, para que um cliente possa disponibilizar a respetiva vinculação (consulte Etiquetas).

searchTeamIds é uma matriz JSON de UUIDs de Equipas (uma cadeia JSON em corpos multipart). Se for omitido, a identificação pesquisa exatamente uma Equipa: a indicada por Dust-Ctx-Team-Id, que, por sua vez, tem como predefinição a Equipa raiz da organização.

Os IDs não são arbitrários. As Equipas da mesma organização às quais pertence estão sempre incluídas no âmbito; uma Equipa de uma organização parceira só fica acessível através de uma Ligação ativa que permita o fluxo dos respetivos dados para si. Tudo o resto é silenciosamente removido do âmbito, em vez de provocar a falha do pedido, pelo que um âmbito aparentemente amplo pode resultar numa pesquisa restrita. Obtenha os IDs válidos em vez de os codificar diretamente:

  • GET /api/v1/teams — as Equipas da sua organização às quais a sua credencial pertence ({ teams: [{ teamId, orgId, name, … }], total }).
  • GET /api/v1/teams/connected — as Equipas parceiras nas quais pode pesquisar, sob a forma de registos de Ligação que identificam as duas Equipas ligadas.

Verificar uma digitalização relativamente a um Registo

Seção intitulada “Verificar uma digitalização relativamente a um Registo”

A verificação é a primitiva de autenticação: perante uma digitalização recente, um threadId e os tags candidatos já vinculados a esse Registo, é bem-sucedida se qualquer candidato corresponder.

tags é obrigatório e é uma matriz de objetos — cada um com { "tagId": "…", "tagType": "…" } — e não uma matriz de cadeias de IDs. Num corpo multipart, é enviado como uma cadeia JSON:

Terminal window
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" }]));

Os valores de tagId são os identificadores já vinculados ao Registo que está a conferir. Leia-os a partir do Registo: GET /api/v1/threads/{thread_id} devolve-os em thread.tags, cada um com o respetivo tagId e tagType. Assim, uma verificação típica obtém o Registo, filtra os respetivos identificadores pelo tipo que acabou de capturar e envia-os como lista de candidatos:

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 }));

Enviar um identificador que não esteja vinculado a esse Registo faz com que a verificação falhe, em vez de permitir a correspondência com outra entidade.

O número de candidatos altera a estrutura da resposta, o que constitui o erro de integração mais frequente neste caso:

Comprimento de tagsCorrespondênciaSem correspondência
Exatamente um200 com { tag, scan? }IDENTIFIER_VERIFY_FAILED (HTTP 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 com vários candidatos que falhe é uma chamada HTTP bem-sucedida com success: false. Nunca trate response.ok como prova de autenticidade — leia success sempre que enviar mais de um candidato. Consulte Erros e resultados de digitalização → Resultados da verificação.

Estes são endpoints JSON simples; todos exigem o tagId e o threadId do Registo ao qual o identificador está vinculado:

  • POST /api/v1/tags/text — definir name e/ou description.
  • POST /api/v1/tags/update — definir name, description, isPrivate e archivedAt (um carimbo de data/hora ISO arquiva o identificador; null restaura-o). O valor e o tipo do identificador são imutáveis — volte a vinculá-lo.
  • POST /api/v1/tags/unbind — separar o identificador do Registo.

Em /api/v1/tamper, uma Análise de adulteração compara uma nova digitalização de um identificador DUST com a referência capturada quando este foi vinculado e regista o que foi medido. A API devolve apenas medições e evidências — não existe, em qualquer ponto da interface, nenhum número de resumo, intervalo, limiar ou campo de resultado atribuído pela plataforma, e alignmentOutcome indica exclusivamente se foi possível comparar as duas digitalizações (quando não foi possível, as medições não são comparáveis, o que não constitui uma afirmação sobre o identificador).

OperaçãoMétodo e caminho
Executar uma AnálisePOST /api/v1/tamper/analyses — codificação de formulário: threadId, tagId e exatamente um de data ou queryFingerprintId
Registar uma ObservaçãoPOST /api/v1/tamper/observations — { analysisId, result }
Listar as Análises de um RegistoGET /api/v1/tamper/analyses?threadId=… (opcionalmente tagId, limit)
Obter uma AnáliseGET /api/v1/tamper/analyses/{analysis_id}
Obter um mapa de bits do resultadoGET /api/v1/tamper/analyses/{analysis_id}/artifacts/{name}

A execução de uma Análise recebe um corpo multipart/form-data ou application/x-www-form-urlencoded com threadId, tagId e exatamente um dos seguintes:

  • data — a própria digitalização DUST, como ficheiro ou imagem codificada em base64. O serviço efetua a extração.
  • queryFingerprintId — o ID de uma impressão digital que já possua, proveniente de POST /api/v1/tags/extract (acima), caso tenha efetuado a extração separadamente.

O envio de ambos, ou de nenhum, é rejeitado. Em qualquer caso, uma captura DUST normal é uma entrada válida — não existe um caminho de captura separado para a análise de adulteração. Se não for possível ler a digitalização enviada, o pedido falha e não é registada nenhuma Análise.

Uma Observação de adulteração é a única conclusão que a plataforma guarda e é emitida por uma pessoa: result assume um dos valores consistent, expected, inconsistent ou unknown, não possui predefinição e é obrigatório. expected regista o desgaste normal do caso de utilização e do substrato do identificador. As Observações são imutáveis e têm autoria atribuída; uma nova nunca substitui uma anterior, e as leituras devolvem toda a série (observations, da mais recente para a mais antiga), em vez de um único resultado atual. Não derive um resultado das métricas nem reduza a série a um único valor na sua própria IU.

Uma Análise inclui metrics (um objeto transmitido sem alterações com as frações de cobertura e as contagens de marcadores do algoritmo), markerPoints opcionais e artifactNames. Cada conjunto de coordenadas de marcadores encontra-se no espaço de píxeis da respetiva digitalização — componha-os num único referencial aplicando metrics.transformation_matrix aos pontos da consulta. Os mapas de bits do resultado são conteúdo protegido: obtenha-os através do endpoint de artefactos, que volta a autorizar cada pedido e devolve bytes que não podem ser colocados em cache.

Uma Etiqueta (nome na interface: composite tag, espaço de nomes /api/v1/composite-tags) é uma etiqueta física que contém um ou mais identificadores de qualquer tipo; DUST não é obrigatório. As Etiquetas ocupam uma posição num Rolo (collection.kind = "reel", identificado pelo respetivo UUID; o seu name é o número impresso no rolo ou qualquer título e nunca é único). Os Rolos podem ser arquivados numa Coleção de etiquetas (kind = "reel_collection"), uma pasta que nunca é enviada. O expectedIdentifiers de um Rolo indica quantos Identificadores de cada tipo uma Etiqueta completa desse Rolo contém, no formato [{ "tagType", "count" }] (por predefinição, um TEXT, um DUST e um QR; um count igual a 0 na entrada significa que o tipo não é esperado). Trata-se de uma indicação para estações de inscrição, não de uma restrição, e o sinalizador complete de uma Etiqueta significa que contém pelo menos esse número de Identificadores ativos de cada tipo esperado.

OperaçãoMétodo e caminho
Listar/criar Coleções de etiquetasGET, POST /api/v1/composite-tags/collections; PATCH …/collections/{collection_id}
Listar RolosGET /api/v1/composite-tags/reels?collectionId=…&unfiled=…&transferred=any|only|hide&q=…
Criar um RoloPOST /api/v1/composite-tags/reels — { name, description?, collectionId?, expectedIdentifiers? }
Obter/atualizar um RoloGET, PATCH /api/v1/composite-tags/reels/{reel_collection_id} (mudar o nome, a composição esperada e collectionId para o mover; null deixa-o sem coleção)
Criar uma EtiquetaPOST /api/v1/composite-tags/reels/{reel_collection_id}/labels (multipart)
Adicionar/remover um identificador membroPOST /api/v1/composite-tags/{composite_tag_id}/identifiers (multipart); DELETE …/identifiers/{tag_id}
Listar/obter EtiquetasGET /api/v1/composite-tags?reelCollectionId=…&bound=any|only|unbound&transferred=…&q=…; GET …/{composite_tag_id}
Resolver uma Etiqueta por um valor de membroPOST /api/v1/composite-tags/resolve — { tagType, value, reelCollectionId? } (TEXT é comparado sem distinguir maiúsculas de minúsculas); a resposta inclui detail (primeira correspondência) e candidates[] (todas as correspondências, pela ordem das posições quando é fornecido um Rolo)
Mover ou cortar EtiquetasPOST /api/v1/composite-tags/move; efetuar uma verificação prévia com POST /api/v1/composite-tags/move/preview
Vincular/desvincular uma EtiquetaPOST /api/v1/composite-tags/{composite_tag_id}/bind — { threadId, options?: { indexing: "default" } }; POST …/unbind
Vincular em massa um Intervalo do roloPOST /api/v1/composite-tags/reels/{reel_collection_id}/bulk-bind — { fromPosition, toPosition, threadIds, activate?, dryRun? }
Arquivar/restaurar uma EtiquetaPOST …/{composite_tag_id}/archive, POST …/unarchive
AtivarPOST …/{composite_tag_id}/activate; POST /api/v1/composite-tags/reels/{reel_collection_id}/activate (em segundo plano)
Ativar identificadores DUST independentesPOST /api/v1/tags/activate — { tagIds[] } (até 200); um resultado por identificador, apenas progressivo
Terminal window
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 }] }'

A resposta é { reel }, um resumo do Rolo com contagens a zero. Conserve reel.collectionId; este identifica o Rolo. A criação de um Rolo cria sempre um novo: não existe reutilização pelo nome.

Cada Etiqueta corresponde a um pedido multipart. Forneça, no máximo, uma imagem DUST como data; todos os outros membros são transmitidos através de identifiers, uma matriz JSON de entradas { tagType, value }, ou { tagType: "DUST", fingerprintId } para um DUST adicional que já tenha sido extraído com POST /api/v1/tags/extract. humanReadable e qrValue são formas abreviadas de indicar um membro TEXT e um membro QR. Por predefinição, position corresponde à posição livre seguinte no Rolo.

Terminal window
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"}'

Tem de resultar pelo menos um identificador. Uma resposta bem-sucedida tem outcome: "created"; uma nova tentativa com a mesma marcação DUST na mesma posição é reconciliada como outcome: "already_enrolled". Um DUST que já esteja noutra Etiqueta ou já se encontre vinculado devolve 409 COMPOSITE_TAG_CONFLICT. Os valores TEXT ou QR repetidos nunca são rejeitados: o Rolo comunica-os em warnings, pois alguns rolos repetem legitimamente um valor.

options.indexing seleciona o modo de indexação DUST da imagem em data: default (identificável), a menos que solicite none (Apenas verificação). Normalmente, as estações de inscrição autónomas efetuam a inscrição em modo Apenas verificação e ativam posteriormente; a ativação indexa cada DUST e executa a verificação de duplicados da plataforma, pelo que uma Etiqueta cujo DUST duplique outro já indexado é comunicada e ignorada.

O fluxo equivalente com o cliente tipado é:

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);

POST /api/v1/composite-tags/move recebe uma source, um target e um expectedCount opcional.

Existem três estruturas de origem:

  • { compositeTagIds } — Etiquetas selecionadas manualmente, movidas pela ordem indicada.
  • { reelCollectionId, fromPosition, toPosition? } — um intervalo de posições tipado (um corte); por predefinição, toPosition corresponde à última posição do Rolo.
  • { fromCompositeTagId, toCompositeTagId } — um corte delimitado por digitalizações: a primeira e a última Etiqueta do intervalo, por qualquer ordem. O servidor lê as respetivas posições sob bloqueio; ambas têm de ser Etiquetas ativas no mesmo Rolo (caso contrário, endpoints_on_different_reels, endpoint_archived ou endpoint_not_on_reel em detail.reason). Resolva cada Etiqueta a partir de um valor digitalizado com /resolve (limitado ao Rolo, para que um código impresso repetido seja apresentado como vários candidates que o autor da chamada terá de desambiguar) ou, no caso de um DUST ativado, com POST /api/v1/tags/identify.

O target é { reelCollectionId } ou { newReel: { name, description?, collectionId?, expectedIdentifiers? } } (um Rolo novo herda a composição do Rolo de origem quando nenhuma é indicada).

Todas as Etiquetas ativas de um intervalo são movidas; as posições que contenham uma Etiqueta arquivada ou enviada, ou que não contenham qualquer Etiqueta, são lacunas que permanecem no Rolo de origem. As posições são mantidas quando todas estiverem livres no destino; caso contrário, todo o lote é acrescentado após a última posição do destino, pela ordem da origem. A resposta apresenta moved[] e, no caso de uma origem por intervalo ou delimitada por digitalizações, cut: { sourceReel, fromPosition, toPosition, count, boundCount, boundPositions, gaps[] }.

expectedCount torna a contagem contratual: quando é indicado, o movimento é recusado com 400 INVALID_REQUEST e detail.reason: "count_mismatch" (expected, actual, fromPosition, toPosition), a menos que sejam movidas exatamente esse número de Etiquetas ativas.

POST /api/v1/composite-tags/move/preview aceita a mesma source, um target opcional e expectedCount, não altera nada e devolve span, count, boundCount, a primeira (first) e a última (last) Etiquetas do lote, predictedOutcome (kept_positions/appended/null), countMatches e suggestedLast — a Etiqueta mais adiante no Rolo que permitiria cumprir expectedCount quando o intervalo é demasiado curto. É uma sugestão para o operador digitalizar, nunca aplicada pelo servidor. A verificação prévia está disponível para membros; o movimento exige um administrador da Equipa ou da Organização.

POST /api/v1/composite-tags/reels/{reel_collection_id}/bulk-bind vincula as Etiquetas nas posições fromPosition..toPosition (inclusive) aos threadIds, por ordem: a k-ésima posição ao k-ésimo Registo. A operação é estrita e executada na totalidade ou não é executada: o intervalo tem de abranger exatamente threadIds.length posições (toPosition é obrigatório e não é derivado, para que o autor da chamada indique o intervalo que verificou na bobina), com um máximo de 500 pares por chamada, e todas as posições têm de conter uma Etiqueta ativa e não vinculada. O servidor nunca ignora uma posição, pois tal deslocaria silenciosamente todos os emparelhamentos seguintes.

Transmita dryRun: true para efetuar a verificação prévia sem vincular. A estrutura da resposta é igual nos dois casos:

  • outcome — "bound" após uma vinculação real, "preflight" numa simulação.
  • rows[] — uma por emparelhamento: index, position, compositeTagId (null para uma posição vazia), labelName, textValue (o membro TEXT da Etiqueta, ou seja, o código impresso), threadId, threadName, threadDescription.
  • blockers[] e warnings[] — { kind, index, position, compositeTagId?, threadId?, tagType?, existing? }.
  • activation — "queued", "not_requested", "already_active" ou "no_dust".
  • reel — o resumo do Rolo com as contagens atualizadas.

Tipos de impedimento: 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. Tipos de aviso: label_incomplete (uma Etiqueta com menos Identificadores do que o Rolo espera) e thread_has_label (o Registo já contém uma Etiqueta; existing[] identifica-as). Os avisos nunca impedem uma vinculação.

Uma confirmação com qualquer impedimento falha com 409 COMPOSITE_TAG_CONFLICT; detail inclui os mesmos rows, blockers e warnings que uma simulação, pelo que um cliente só tem de analisar uma estrutura. Um intervalo cujo comprimento seja diferente de threadIds.length produz 400 INVALID_REQUEST.

Autoridade: associação à Equipa do Rolo e permissão de edição em cada Registo. Um Registo que o autor da chamada não possa editar constitui um impedimento thread_not_editable nessa linha, em vez de provocar a rejeição de todo o pedido, e todos os Registos têm de pertencer à Equipa do Rolo — um Registo apenas partilhado com a Equipa produz thread_not_owned.

Com activate: true, a vinculação é confirmada primeiro e, em seguida, uma tarefa em segundo plano ativa exatamente as marcações DUST das Etiquetas vinculadas; uma Etiqueta cuja ativação falhe permanece vinculada e no modo Apenas verificação. Consulte periodicamente counts.identifiableCount do Rolo para acompanhar o progresso.

Terminal window
# Preflight
curl -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 afterwards
curl -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 }'

Com o cliente tipado:

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,
});
}

A vinculação deixa exatamente os eventos que N vinculações individuais deixariam: um evento bind por identificador membro, cada um com o Registo, o identificador e a Etiqueta como alvos, todos com o mesmo ID de operação.

Uma Etiqueta é vinculada como um todo: POST …/{composite_tag_id}/bind associa a Etiqueta e todos os identificadores membros ativos ao Registo; options.indexing: "default" também a ativa. O mesmo acontece quando utiliza o POST /api/v1/tags/bind normal com qualquer identificador membro (consulte Vincular uma Etiqueta), que é o que fazem os scanners. A vinculação exige permissão de edição no Registo e que a Equipa seja proprietária da Etiqueta, e o Registo tem de pertencer à mesma Equipa: um identificador membro digitalizado para um Registo partilhado consigo por outra Equipa é recusado (409 COMPOSITE_TAG_CONFLICT, reason: "label_owned_by_other_team"), em vez de ser vinculado como uma cópia independente. POST …/unbind separa a Etiqueta e todos os respetivos membros.

A propriedade da Equipa e da Organização provém exclusivamente dos cabeçalhos de contexto, e o criador provém do token de portador verificado. A leitura e a ativação de Etiquetas exigem associação à Equipa. A criação de Rolos, inscrição, movimentação e arquivo são permitidos a um administrador autenticado da Equipa ou da Organização, ou a uma Conta de Serviço com âmbito de Organização que seja membro da Equipa selecionada; uma Conta de Serviço não pode beneficiar da autoridade de administrador da Organização.

Nos Envios, um Rolo é um item do manifesto ({ kind: "reel", collectionId }) e é enviado na totalidade, mas apenas quando todas as suas Etiquetas não estiverem vinculadas. Uma Etiqueta vinculada é enviada com o respetivo Registo e nunca inclui o respetivo Rolo no Envio. Os Rolos e as Etiquetas enviados continuam legíveis no lado do remetente, com transferredAt definido; filtre-os com transferred=only ou transferred=hide.

Consulte Etiquetas e Rolos para conhecer o fluxo de trabalho DICE.