Błędy i wyniki skanowania
To strona referencyjna dotycząca obsługi błędów: treść błędu zwracana przez każdy punkt końcowy, kody, od których warto uzależniać logikę, oraz — co najważniejsze — kanoniczne tabele wyników skanowania. „Brak dopasowania” to wynik, a nie błąd transportu, mimo że przychodzi z kodem stanu błędu HTTP. Kod, który traktuje każdą odpowiedź spoza zakresu 2xx jako usterkę, będzie zgłaszać awarie, które nigdy nie wystąpiły.
Schematy poszczególnych punktów końcowych znajdują się w dokumentacji API. Same przepływy opisano w sekcjach Identyfikatory i Szybki start.
Koperta błędu
Dział zatytułowany „Koperta błędu”Każde nieudane żądanie zwraca ten sam obiekt 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 } }}| Pole | Typ | Znaczenie |
|---|---|---|
code | string | Stały kod czytelny dla maszyn. Od niego uzależniaj logikę. Nigdy nie analizuj message. |
message | string | Tekst czytelny dla człowieka, zlokalizowany zgodnie z Dust-Ctx-Locale. Sformułowania się zmieniają; kody nie. |
status | number | Odzwierciedla kod stanu HTTP. |
detail | object, opcjonalne | Kontekst konkretnego błędu: szczegóły walidacji, raport skanu, klasyfikacja wyniku, ID konfliktów. |
status i kod stanu HTTP są zawsze zgodne, więc logikę można uzależnić od dowolnego z nich. Każda odpowiedź — pomyślna lub nie — zawiera też nagłówek x-request-id. Zapisuj go w logach; zespół wsparcia używa go, aby odnaleźć dokładnie twoje żądanie.
Kody według klasy
Dział zatytułowany „Kody według klasy”Żądanie i autoryzacja
Dział zatytułowany „Żądanie i autoryzacja”| Kod | Status | Kiedy |
|---|---|---|
INVALID_REQUEST | 400 | Nieprawidłowa treść, zapytanie lub nagłówek. Szczegóły walidacji w detail. |
INVALID_DATA | 400 | Żądanie zostało przeanalizowane, ale wartości nie można użyć (na przykład ładunek identyfikacji, którego usługa skanowania odmówiła odczytania). |
UNAUTHORIZED | 401 | Brakujący, wygasły lub nieprawidłowy token Bearer. |
FORBIDDEN | 403 | Uwierzytelniono, ale ten kontekst nie może wykonać tej operacji. |
ATTRIBUTION_REQUIRED | 403 | Zasady przypisania konta usługi mają wartość required, a zapis nie zawierał nagłówka Dust-Ctx-Declared-Actor. |
ORG_ID_REQUIRED / TEAM_ID_REQUIRED | 400 | W punkcie końcowym o określonym zakresie brakuje nagłówka kontekstu. |
NOT_FOUND / NO_DATA_FOUND | 404 | Żaden taki obiekt nie jest widoczny w tym kontekście. |
RATE_LIMITED | 429 | Odczekaj i ponów próbę. |
THREAD_DATA_CONFLICT | 409 | Konflikt współbieżności optymistycznej: twoja wartość expectedUpdatedAt jest nieaktualna. Odczytaj dane ponownie i zastosuj zmiany jeszcze raz. |
COMPOSITE_TAG_CONFLICT | 409 | Konflikt etykiety — DUST znajduje się już na innej etykiecie lub jest powiązany gdzie indziej, pozycja na rolce jest zajęta albo usuwany jest ostatni identyfikator etykiety. detail.reason wskazuje, który przypadek zaszedł. |
UNKNOWN_ERROR / SERVICE_ERROR | 500 | Błąd po stronie serwera. Ponów próbę z wydłużającym się odstępem; jeśli problem się utrzymuje, podaj ID żądania. |
Identyfikatory i skanowanie
Dział zatytułowany „Identyfikatory i skanowanie”| Kod | Status | Znaczenie |
|---|---|---|
IDENTIFIER_NOT_FOUND | 404 | Ostateczny brak dopasowania: odpowiedziała każda przeszukiwana partycja i nic nie zostało dopasowane. |
IDENTIFIER_NOT_BOUND | 404 | Identyfikator istnieje, ale nie jest powiązany z żadnym rekordem (osiągalne tylko przez identyfikację według tagId). |
IDENTIFIER_ALREADY_BOUND | 409 | Odmowa powiązania — ten identyfikator (lub jego etykieta) znajduje się już w innym rekordzie. detail.compositeTagId wskazuje etykietę. |
IDENTIFIER_VERIFY_FAILED | 500 | Weryfikacja pojedynczego identyfikatora nie dała dopasowania albo wskazany identyfikator nie jest powiązany z tym rekordem. |
SCAN_AMBIGUOUS_MATCH | 409 | Dopasowano dwa lub więcej różnych zarejestrowanych DUST i wszystkie są powiązane w przeszukiwanym zakresie. Ponowienie z tym samym ujęciem nic nie da. |
SCAN_SEARCH_INCOMPLETE | 503 | Część partycji odpowiedziała „brak dopasowania”, ale innych nie udało się przeszukać. To nie jest brak dopasowania — ponów próbę. |
SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS | 400 | Samo ujęcie zostało odrzucone: zbyt mało użytecznych szczegółów. Zeskanuj ponownie; nie ponawiaj z tym samym obrazem. |
SCAN_IDENTICAL_SCAN | 400 | Przesłany obraz ma bajty identyczne z wcześniejszym ujęciem. Wykonaj nowe. |
SCAN_EXTRACTION_FAILURE | 500 | Ekstrakcja nie powiodła się dla obrazu, który poza tym został zaakceptowany. |
SCAN_ROUTING_UNAVAILABLE | 503 | Operacja nie jest dostępna dla tej organizacji (na przykład moduł nie jest włączony) albo jej trasa nie działa. |
SCAN_BACKEND_UNAVAILABLE | 503 | Zaplecze skanowania jest tymczasowo niedostępne. Ujęcie było w porządku — ponów próbę, nie skanuj ponownie. |
Kanoniczne wyniki identyfikacji
Dział zatytułowany „Kanoniczne wyniki identyfikacji”POST /api/v1/tags/identify ma osiem wyników. Trzy mają kod 200; pozostałe przychodzą z kodami stanu błędu i mimo to są odpowiedziami. Ta tabela jest jedynym źródłem zarówno dla integracji tworzonych przez ludzi, jak i przez agentów — ta sama tabela występuje w umiejętnościach dice-api-integration i dust-go-connect-integration.
| Wynik | HTTP | Treść | Co oznacza | Co zrobić |
|---|---|---|---|---|
| Zidentyfikowany rekord | 200 | { type: "identified", identified: { tag, thread, … }, scan? } | Dopasowano dokładnie jeden powiązany identyfikator. | Otwórz rekord. scan.dustId to rozpoznany DUST. |
| Kilku kandydatów | 200 | { type: "matches", matches: [ … ], scan? } | Dopasowano więcej niż jeden powiązany identyfikator albo dopasowanie wymaga rozstrzygnięcia. | Pokaż kandydatów i ponownie wykonaj identyfikację według tagId (tagType: "ANY"). scan.dustId ma wartość null; każdy kandydat zawiera własną wartość. |
| Niepowiązana etykieta | 200 | { type: "label", label: { label, tags }, scan? } | Skan wskazał identyfikator należący do etykiety z zasobów twojego zespołu, która nie jest jeszcze powiązana z żadnym rekordem. | Zaproponuj powiązanie etykiety. To nie jest brak dopasowania. |
| Brak dopasowania | 404 | code: "IDENTIFIER_NOT_FOUND", detail.outcome: "no_match" | Odpowiedziała każda przeszukiwana partycja i nic nie zostało dopasowane. detail.teamsSearched podaje zakres. | Pokaż „nie znaleziono”. Nie zgłaszaj awarii usługi. Raport skanu w detail.scan. |
| Niepełne wyszukiwanie | 503 | code: "SCAN_SEARCH_INCOMPLETE", detail.outcome: "search_incomplete" | Część partycji odpowiedziała „brak dopasowania”, a z innymi nie udało się połączyć. detail.teamsSearched, detail.teamsUnreachable, detail.orgsUnreachable. | Ponów próbę. Nigdy nie przedstawiaj tego jako „nie znaleziono” — przedmiot może być zarejestrowany. |
| Niejednoznaczne dopasowanie | 409 | code: "SCAN_AMBIGUOUS_MATCH", detail.outcome: "ambiguous", detail.candidates, detail.boundCandidates, detail.attempts | Z dużą pewnością dopasowano dwa lub więcej powiązanych DUST. Platforma dwukrotnie przeszukała to samo ujęcie, zanim zwróciła ten wynik. | Przekaż ten wynik wraz z scan.scanId i skontaktuj się z DUST Identity. Nowe ujęcie tego samego przedmiotu tego nie rozstrzygnie. |
| Odrzucone ujęcie | 400 | code: "SCAN_LOW_KEYPOINTS" / "SCAN_NO_KEYPOINTS" / "SCAN_IDENTICAL_SCAN", detail.outcome: "quality_reject" | Nie można było użyć obrazu. Odrzucenie ze względu na jakość ma pierwszeństwo przed wszystkimi innymi wynikami partycji. | Poproś operatora o ponowne zeskanowanie. Obraz jest zachowywany jako odrzucony skan; detail.scan.fingerprintId ma wartość null. |
| Identyfikator niepowiązany | 404 | code: "IDENTIFIER_NOT_BOUND" | Tylko przy identyfikacji według tagId (tagType: "ANY"): identyfikator istnieje, ale nie ma rekordu. | Zaproponuj jego powiązanie. |
detail.outcome to klasyfikacja użyta przez serwer; jej wartości są stałe: no_match, search_incomplete, ambiguous, quality_reject. Najpierw uzależniaj logikę od code, a detail.outcome odczytuj, gdy potrzebujesz dokładniejszego rozróżnienia.
Rozgałęzianie logiki na podstawie wyniku identyfikacji
Dział zatytułowany „Rozgałęzianie logiki na podstawie wyniku identyfikacji”// 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}`);}Wyniki weryfikacji
Dział zatytułowany „Wyniki weryfikacji”POST /api/v1/tags/verify działa różnie w zależności od tego, ilu identyfikatorów kandydujących przesyłasz w tags, ponieważ jeden kandydat to pytanie typu tak/nie, a kilku to wyszukiwanie:
Długość tags | Dopasowanie | Brak dopasowania |
|---|---|---|
| Dokładnie jeden | 200 z { tag, scan? } | IDENTIFIER_VERIFY_FAILED (500), raport skanu w detail.scan |
| Dwa lub więcej | 200 z { success: true, verifiedTag, attemptedCount, failedCount, scan? } | 200 z { success: false, attemptedCount, failedCount, error, scan? } |
Zbiorcza weryfikacja, która się nie powiedzie, jest więc pomyślnym wywołaniem HTTP z success: false. Zawsze odczytuj success, gdy przesyłasz więcej niż jednego kandydata, i nigdy nie wnioskuj o autentyczności na podstawie samego response.ok.
Pole tags jest wymagane przy każdej weryfikacji. Jest to tablica obiektów, a nie samych wartości ID:
[{ "tagId": "8f2b…", "tagType": "DUST" }]Raporty skanu przetrwają niepowodzenie
Dział zatytułowany „Raporty skanu przetrwają niepowodzenie”Każda operacja przesyłająca obraz — ekstrakcja, powiązanie, identyfikacja, weryfikacja, analiza manipulacji — zwraca raport skanu wskazujący, co zostało zapisane:
{ "scanId": "…", "fingerprintId": "…", "dustId": "…" }- W przypadku powodzenia znajduje się na najwyższym poziomie jako
scan. - W przypadku wyniku zapisanego, ale negatywnego, znajduje się w
detail.scan— dotyczy to niezgodności przy weryfikacji, braku dopasowania przy identyfikacji, odmowy powiązania z powodu duplikatu oraz odrzucenia ze względu na jakość (wtedyfingerprintIdma wartośćnull, ponieważ nie wyodrębniono nic użytecznego).
Zapisuj scanId w obu przypadkach. Jest to stała tożsamość ujęcia, niezmienna przy migracjach algorytmów, i właśnie jej potrzebuje zespół wsparcia, aby obejrzeć obraz stojący za spornym wynikiem. Tylko obraz, którego platforma w ogóle nie mogła zdekodować, nie jest zapisywany — wtedy raportu skanu nie ma.
const receipt = response.ok ? body.scan : body.detail?.scan;if (receipt) await recordScan(receipt.scanId, receipt.fingerprintId, receipt.dustId);dustId to wartość, którą platforma ci zwróciła, a nigdy surowe wewnętrzne dopasowanie: ma wartość null przy niezgodności, braku dopasowania, ekstrakcji, analizie manipulacji oraz przy identyfikacji, która zwróciła kilku kandydatów.
Wygasanie i odświeżanie tokenów
Dział zatytułowany „Wygasanie i odświeżanie tokenów”Tokeny Bearer są krótkotrwałe i nie ma tokenu odświeżania — poświadczenie wymienia się ponownie. Wygasły token powoduje zwykły błąd 401 UNAUTHORIZED, nieodróżnialny od tokenu unieważnionego, więc oba przypadki obsługuj tak samo:
- Odświeżaj z wyprzedzeniem.
GET /api/auth/tokenzwracaexpiresIn(w sekundach) iexpiresAt(ISO 8601), gdy token zawiera oświadczenie o wygaśnięciu. Wymieniaj poświadczenie ponownie z zapasem (60 sekund to wygodna wartość); nigdy nie koduj czasu życia na sztywno. - Po błędzie
401ponów próbę jeden raz. Trafiają tu zarówno rozbieżności zegarów, jak i unieważnienie w trakcie ważności tokenu. Jedno odświeżenie i ponowienie to właściwa liczba; pętla już nie. - Pobieraj token dla każdego żądania z pamięci podręcznej, a nie raz przy uruchomieniu procesu, aby zadanie trwające dłużej niż ważność jednego tokenu nie przerwało się w połowie.
Gotowa implementacja znajduje się w sekcji Uwierzytelnianie → Wygasanie i odświeżanie tokenów.
Wskazówki dotyczące ponawiania
Dział zatytułowany „Wskazówki dotyczące ponawiania”| Sytuacja | Ponowić to samo żądanie? | Uwagi |
|---|---|---|
401 UNAUTHORIZED | Tak, jeden raz, po ponownej wymianie poświadczenia | Kolejny błąd oznacza, że nieprawidłowe jest samo poświadczenie. |
429 RATE_LIMITED | Tak, z wydłużającym się odstępem | |
503 SCAN_SEARCH_INCOMPLETE / SCAN_BACKEND_UNAVAILABLE | Tak — ujęcie jest w porządku | Nie każ operatorowi skanować ponownie. |
503 SCAN_ROUTING_UNAVAILABLE | Nie | Operacja jest niedostępna dla tej organizacji; skontaktuj się z DUST Identity. |
400 SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS / SCAN_IDENTICAL_SCAN | Nie — zamiast tego zeskanuj ponownie | Te same bajty zostaną ponownie odrzucone. |
404 IDENTIFIER_NOT_FOUND | Nie | To jest odpowiedź. |
409 SCAN_AMBIGUOUS_MATCH | Nie | Ponowiono już po stronie serwera; informuje o tym detail.attempts. |
409 THREAD_DATA_CONFLICT | Odczytaj ponownie, zastosuj zmiany jeszcze raz, a następnie zapisz | Nie ponawiaj na ślepo — mogłoby to nadpisać czyjeś zmiany. |
5xx UNKNOWN_ERROR | Tak, z wydłużającym się odstępem, w przypadku idempotentnych odczytów | W przypadku zapisów przed ponowieniem sprawdź, czy zapis został wykonany. |
Zobacz także
Dział zatytułowany „Zobacz także”- Konwencje żądań — nagłówki, paginacja, lokalizacja.
- Identyfikatory — operacje skanowania, z których pochodzą te wyniki.
- Uwierzytelnianie i klucze API — poświadczenia i czas życia tokenów.
- Dokumentacja API — schematy odpowiedzi poszczególnych punktów końcowych.