エラーとスキャン結果
これは、失敗処理のための参照ページです。すべてのエンドポイントが返すエラー本文、分岐処理に使用すべきコード、そして何よりも重要な、スキャンの標準結果表を掲載しています。「一致なし」は転送エラーではなく、あくまで結果であり、それでも HTTP エラーステータスとともに返されます。2xx 以外をすべて不具合として扱うコードは、実際には発生していない障害を報告してしまいます。
エンドポイントごとのスキーマについては、API リファレンスを参照してください。フロー自体については、識別子およびクイックスタートを参照してください。
エラーエンベロープ
Section titled “エラーエンベロープ”失敗したすべてのリクエストは、同じ 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 } }}| フィールド | 型 | 意味 |
|---|---|---|
code | string | 安定した機械可読コードです。これを使用して分岐してください。 message を解析してはいけません。 |
message | string | 人が読めるテキストで、Dust-Ctx-Locale に応じてローカライズされます。文言は変わる可能性がありますが、コードは変わりません。 |
status | number | HTTP ステータスと同じ値です。 |
detail | object, optional | エラーごとのコンテキストです。検証の詳細、スキャンレシート、結果の分類、競合する ID などが含まれます。 |
status と HTTP ステータスは常に一致するため、どちらを使用して分岐しても構いません。また、成功か失敗かを問わず、すべてのレスポンスには x-request-id ヘッダーが含まれます。ログに記録してください。サポートは、この値を使って対象のリクエストを正確に特定します。
クラス別のコード
Section titled “クラス別のコード”リクエストと認可
Section titled “リクエストと認可”| コード | ステータス | 発生条件 |
|---|---|---|
INVALID_REQUEST | 400 | 本文、クエリ、またはヘッダーの形式が不正です。検証の詳細は detail に含まれます。 |
INVALID_DATA | 400 | リクエストは解析できましたが、値を使用できません(たとえば、スキャンサービスが読み取りを拒否した識別ペイロード)。 |
UNAUTHORIZED | 401 | Bearer トークンがない、期限切れ、または無効です。 |
FORBIDDEN | 403 | 認証済みですが、このコンテキストではその操作を実行できません。 |
ATTRIBUTION_REQUIRED | 403 | Service Account の属性付与ポリシーが required であるにもかかわらず、書き込みに Dust-Ctx-Declared-Actor がありません。 |
ORG_ID_REQUIRED / TEAM_ID_REQUIRED | 400 | 範囲が限定されたエンドポイントで、コンテキストヘッダーが不足しています。 |
NOT_FOUND / NO_DATA_FOUND | 404 | このコンテキストから参照できる該当レコードがありません。 |
RATE_LIMITED | 429 | バックオフして再試行してください。 |
THREAD_DATA_CONFLICT | 409 | 楽観的同時実行制御の競合です。指定した expectedUpdatedAt が古くなっています。再読み込みしてから変更を再適用してください。 |
COMPOSITE_TAG_CONFLICT | 409 | ラベルの競合です。DUST がすでに別のラベルに存在するか別の場所にバインドされている、リールの位置が使用済みである、またはラベルの最後の識別子を取り除こうとしています。該当する理由は detail.reason に示されます。 |
UNKNOWN_ERROR / SERVICE_ERROR | 500 | サーバー側の障害です。バックオフして再試行し、問題が解消しない場合はリクエスト ID を添えてください。 |
識別子とスキャン
Section titled “識別子とスキャン”| コード | ステータス | 意味 |
|---|---|---|
IDENTIFIER_NOT_FOUND | 404 | 確定的な不一致です。検索対象のすべてのパーティションが応答しましたが、何も一致しませんでした。 |
IDENTIFIER_NOT_BOUND | 404 | 識別子は存在しますが、どのスレッドにもバインドされていません(tagId による識別からのみ到達できます)。 |
IDENTIFIER_ALREADY_BOUND | 409 | バインドが拒否されました。その識別子(またはラベル)は、すでに別のスレッドに存在します。detail.compositeTagId に対象のラベルが示されます。 |
IDENTIFIER_VERIFY_FAILED | 500 | 単一識別子の検証で一致しなかったか、指定した識別子がそのスレッドにバインドされていません。 |
SCAN_AMBIGUOUS_MATCH | 409 | 登録済みの異なる DUST が 2 つ以上一致し、その両方が検索範囲内でバインドされています。同じ撮影画像では再試行できません。 |
SCAN_SEARCH_INCOMPLETE | 503 | 一部のパーティションは「一致なし」と応答しましたが、ほかのパーティションは検索できませんでした。**これは不一致ではありません。**再試行してください。 |
SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS | 400 | 撮影画像自体が拒否されました。使用可能な詳細が不足しています。再スキャンし、同じ画像を再試行しないでください。 |
SCAN_IDENTICAL_SCAN | 400 | 送信された画像が、以前の撮影画像とバイト単位で同一です。新たに撮影してください。 |
SCAN_EXTRACTION_FAILURE | 500 | それ以外の点では受け入れられた画像で、抽出に失敗しました。 |
SCAN_ROUTING_UNAVAILABLE | 503 | この組織では操作を利用できない(たとえば、モジュールが有効になっていない)か、その経路が停止しています。 |
SCAN_BACKEND_UNAVAILABLE | 503 | スキャンバックエンドが一時的に利用できません。撮影画像に問題はありません。再スキャンせず、再試行してください。 |
標準の識別結果
Section titled “標準の識別結果”POST /api/v1/tags/identify には 8 種類の結果があります。3 種類は 200 で返され、残りはエラーステータスとともに返されますが、それでも回答です。この表は、人による連携とエージェント連携の両方に対する唯一の情報源です。同じ表が、dice-api-integration および dust-go-connect-integration スキルにも掲載されています。
| 結果 | HTTP | 本文 | 意味 | 対応 |
|---|---|---|---|---|
| 識別されたスレッド | 200 | { type: "identified", identified: { tag, thread, … }, scan? } | バインド済みの識別子がちょうど 1 つ一致しました。 | スレッドを開きます。scan.dustId は解決された DUST です。 |
| 複数の候補 | 200 | { type: "matches", matches: [ … ], scan? } | バインド済みの識別子が複数一致したか、一致を絞り込む必要があります。 | 候補を表示し、tagId(tagType: "ANY")を使用して再度識別します。scan.dustId は null で、各候補にはそれぞれの値が含まれます。 |
| バインドされていないラベル | 200 | { type: "label", label: { label, tags }, scan? } | スキャンによって、チームの在庫にあるラベルのメンバーが解決されましたが、そのラベルはまだどのスレッドにもバインドされていません。 | ラベルのバインドを提案します。不一致ではありません。 |
| 一致なし | 404 | code: "IDENTIFIER_NOT_FOUND", detail.outcome: "no_match" | 検索対象のすべてのパーティションが応答しましたが、何も一致しませんでした。detail.teamsSearched に範囲が示されます。 | 「見つかりません」と表示します。サービス障害として報告してはいけません。レシートは detail.scan にあります。 |
| 検索未完了 | 503 | code: "SCAN_SEARCH_INCOMPLETE", detail.outcome: "search_incomplete" | 一部のパーティションは「一致なし」と応答しましたが、ほかのパーティションには到達できませんでした。detail.teamsSearched、detail.teamsUnreachable、detail.orgsUnreachable に詳細が示されます。 | 再試行します。これを「見つかりません」と表示してはいけません。アイテムは登録済みである可能性が十分にあります。 |
| 曖昧な一致 | 409 | code: "SCAN_AMBIGUOUS_MATCH", detail.outcome: "ambiguous", detail.candidates, detail.boundCandidates, detail.attempts | バインド済みの DUST が 2 つ以上、高い確度で一致しました。プラットフォームは、この結果を返す前に同じ撮影画像を 2 回検索しています。 | scan.scanId とともに問題を明示し、DUST Identity に問い合わせてください。同じアイテムを新たに撮影しても解決しません。 |
| 撮影画像の拒否 | 400 | code: "SCAN_LOW_KEYPOINTS" / "SCAN_NO_KEYPOINTS" / "SCAN_IDENTICAL_SCAN", detail.outcome: "quality_reject" | 画像を使用できませんでした。品質による拒否は、ほかのすべてのパーティションの結果より優先されます。 | オペレーターに再スキャンを依頼します。画像は拒否されたスキャンとして保持され、使用可能な情報が抽出されなかったため、detail.scan.fingerprintId は null です。 |
| バインドされていない識別子 | 404 | code: "IDENTIFIER_NOT_BOUND" | tagId(tagType: "ANY")による識別でのみ発生します。識別子は存在しますが、スレッドがありません。 | バインドを提案します。 |
detail.outcome は、サーバーが使用した安定した分類です。値は no_match、search_incomplete、ambiguous、quality_reject です。まず code で分岐し、より細かな区別が必要な場合に detail.outcome を読み取ってください。
識別結果による分岐
Section titled “識別結果による分岐”// 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 の動作は、tags で送信する候補識別子の数によって異なります。候補が 1 つの場合は「はい」か「いいえ」で答える質問になり、複数の場合は検索になるためです。
tags の要素数 | 一致 | 一致なし |
|---|---|---|
| ちょうど 1 つ | { tag, scan? } とともに 200 | IDENTIFIER_VERIFY_FAILED(500)、レシートは detail.scan |
| 2 つ以上 | { success: true, verifiedTag, attemptedCount, failedCount, scan? } とともに 200 | { success: false, attemptedCount, failedCount, error, scan? } とともに 200 |
したがって、一括検証が失敗した場合でも、success: false を含む正常な HTTP 呼び出しです。複数の候補を送信する場合は必ず success を読み取り、response.ok だけから真正性を判断してはいけません。
すべての検証で tags は必須です。これは ID の配列ではなく、オブジェクトの配列です。
[{ "tagId": "8f2b…", "tagType": "DUST" }]失敗しても保持されるスキャンレシート
Section titled “失敗しても保持されるスキャンレシート”画像を送信するすべての操作(抽出、バインド、識別、検証、改ざん分析)は、保存された内容を示すスキャンレシートを返します。
{ "scanId": "…", "fingerprintId": "…", "dustId": "…" }- 成功時には、トップレベルの
scanとして返されます。 - 保存はされたものの否定的な結果になった場合は、
detail.scanとして返されます。たとえば、検証の不一致、識別の一致なし、重複として拒否されたバインド、品質による拒否が該当します。品質による拒否では、使用可能な情報が抽出されなかったため、fingerprintIdはnullです。
どちらの経路でも scanId を取得してください。これは、アルゴリズムの移行後も変わらない撮影画像の安定した識別情報であり、異議が申し立てられた結果の元画像をサポートが確認するために必要なものです。プラットフォームがまったくデコードできなかった画像だけは何も保存されず、その場合はレシートもありません。
const receipt = response.ok ? body.scan : body.detail?.scan;if (receipt) await recordScan(receipt.scanId, receipt.fingerprintId, receipt.dustId);dustId は、常にプラットフォームが利用者に返した値であり、内部の生の一致結果ではありません。不一致、一致なし、抽出、改ざん分析、および複数の候補を返した識別では null になります。
トークンの有効期限と再取得
Section titled “トークンの有効期限と再取得”Bearer トークンの有効期間は短く、リフレッシュトークンはありません。認証情報を再交換します。期限切れのトークンは通常の 401 UNAUTHORIZED となり、失効したトークンと区別できないため、どちらも同じ方法で処理してください。
- 事前に再取得してください。 トークンに有効期限のクレームが含まれている場合、
GET /api/auth/tokenはexpiresIn(秒)とexpiresAt(ISO 8601)を返します。余裕を持って再交換してください(60 秒あれば十分です)。有効期間をハードコードしてはいけません。 401の場合は 1 回だけ再試行してください。 時計のずれと、有効期間中の失効はいずれもこのステータスになります。再取得して 1 回再試行するのが適切です。ループさせてはいけません。- プロセスの開始時に一度だけ発行するのではなく、キャッシュを利用してリクエストごとに発行してください。 これにより、1 つのトークンの有効期間を超えて実行されるジョブが途中で失敗することを防げます。
実装例については、認証 → トークンの有効期限と再取得を参照してください。
再試行の指針
Section titled “再試行の指針”| 状況 | 同じリクエストを再試行するか | 注記 |
|---|---|---|
401 UNAUTHORIZED | はい。認証情報を再交換した後に 1 回だけ | 2 回以上必要になる場合は、認証情報自体が誤っています。 |
429 RATE_LIMITED | はい。バックオフして再試行 | |
503 SCAN_SEARCH_INCOMPLETE / SCAN_BACKEND_UNAVAILABLE | はい。撮影画像に問題はありません | オペレーターに再スキャンを求めないでください。 |
503 SCAN_ROUTING_UNAVAILABLE | いいえ | この組織では操作を利用できません。DUST Identity に問い合わせてください。 |
400 SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS / SCAN_IDENTICAL_SCAN | いいえ。代わりに再スキャン | 同じバイト列は再び拒否されます。 |
404 IDENTIFIER_NOT_FOUND | いいえ | これは回答です。 |
409 SCAN_AMBIGUOUS_MATCH | いいえ | サーバー側ですでに再試行されています。detail.attempts にその情報が示されます。 |
409 THREAD_DATA_CONFLICT | 再読み込みし、変更を再適用してから書き込み | そのまま再試行してはいけません。ほかの人の変更を上書きしてしまいます。 |
5xx UNKNOWN_ERROR | べき等な読み取りであれば、はい。バックオフして再試行 | 書き込みの場合は、再試行する前に書き込みが反映されたか確認してください。 |
- リクエスト規約 — ヘッダー、ページネーション、ローカライズ。
- 識別子 — これらの結果が返されるスキャン操作。
- 認証と API キー — 認証情報とトークンの有効期間。
- API リファレンス — エンドポイントごとのレスポンススキーマ。