コンテンツにスキップ

識別子 API ガイド

識別子は、物理的なマーキングをスレッドに接続します。DUST タグ、QR コード、バーコード、Data Matrix シンボル、NFC チップ、印刷されたテキストコードなどが該当します。バインド後は、現場でのスキャンからデジタル記録を解決できます。API 名前空間は /api/v1/tags です。これはパスとスキーマに残っている従来の命名ですが、このドキュメントの本文では「識別子」と表記します。

完全なリクエスト/レスポンススキーマについては、API リファレンスを参照してください。HTTP エラーとして返されるものを含め、各操作で発生し得るすべての結果は、エラーとスキャン結果にまとめて表形式で掲載しています。

操作メソッドとパス意味
抽出POST /api/v1/tags/extractバインドせずに、DUST 撮影画像を正規化されたフィンガープリントへ解析
バインドPOST /api/v1/tags/bind識別子をスレッドに関連付け
識別POST /api/v1/tags/identify検索:このスキャンに一致するスレッドはどれか
検証POST /api/v1/tags/verifyスキャンを特定のスレッドの識別子と比較
バインド解除POST /api/v1/tags/unbind識別子をスレッドから切り離す
テキスト設定POST /api/v1/tags/textバインド済み識別子の名前/説明を変更
更新POST /api/v1/tags/updateライフサイクル:プライバシー、アーカイブ/復元(値と種類は変更不可)

識別と検証の違い: 識別は「これは何か?」に答えます。searchTeamIds で範囲を指定して閲覧可能なスレッドを検索し、一致するものがあれば返します。検証は「これは、そのアイテムであるという申告どおりのものか?」に答えます。threadId と、それにバインドされた候補識別子を指定すると、API が一致を肯定または否定します。真正性の判断には検証を、検索には識別を使用してください。

スキャンエンドポイントは multipart/form-data を受け付け、data の形式は識別子の種類によって異なります。

tagTypedata取得元
DUST画像 — バイナリファイルパートまたは base64 データ URL(data:image/jpeg;base64,…)スキャナーによる DUST 光学撮影画像
QR, BAR_CODE, DATA_MATRIX, NFCデコード済みの文字列内容(または NFC の 16 進 ID)任意のシンボルスキャナー
TEXT人が読むとおりの、印刷された可読コードキーボード入力またはラベル登録

DUST 撮影画像はデコード済みの値ではなく、タグの写真です。サーバーがフィンガープリントを抽出します。撮影画像は DUST スキャン用ハードウェアから取得します。モバイル撮影については DUST Go との統合、すべてのモードに対応する組み込み可能な Web コンポーネントについては React Scannerを参照してください。

multipart 本文では、構造化フィールド(options、tags、searchTeamIds)を JSON 文字列として渡します。

TEXT は、アイテムまたはラベルに印刷された人間が読めるコードです。たとえば AB00017 のようなシリアルです。デコードするシンボルはないため、値を手入力するか、ラベルの登録記録から取得し、入力されたとおりに保存します。読み取るのが人であるため、TEXT はプラットフォームが大文字と小文字を区別せずに照合する唯一の種類です。ab00017 で識別または検証すると、バインド済みの AB00017 が見つかります。その他の種類はすべてバイト単位で照合されます。

QR、バーコード、Data Matrix、NFC の値と同様に、テキストコードはコピー可能で、それ自体に一意性はありません。同じコードが複数のスレッドや、リール上のすべてのラベルに正当に存在する場合があります。値が重複しても拒否されず、リールが重複を警告として報告するだけです。

抽出では、撮影画像を正規化されたフィンガープリントへ解析し、その品質を返します。登録前に撮影画像を確認する場合や、バインドの準備に役立ちます。

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

レスポンスは { id, qualityScore, annotatedImage?, forensics?, scan? } です。id は、後で画像を再アップロードせずにバインドできるフィンガープリント ID です(後述)。options には、プラットフォームがスキャンとともに保存する撮影メタデータ(機器、光学系、位置情報)も含められます。

画像を送信するすべての操作(抽出、バインド、識別、検証、改ざん分析)は、保存された内容を示す scan オブジェクトを返します。

{ "scanId": "…", "fingerprintId": "…", "dustId": "…" }
  • scanId は、画像が保存されると必ず存在します。これは撮影画像の安定した識別情報であり、自身のシステムにスキャン操作を記録する場合に保持する値です。
  • fingerprintId は抽出が成功した場合に存在します(それ以外は null)。
  • dustId は、操作によって DUST が返された場合に存在します。具体的には、バインドで作成された識別子、検証で確認された識別子、または識別で解決された識別子です。不一致、一致なし、抽出、改ざん分析(ここでの識別子は画像から解決されたものではなく、指定したものです)、および複数の候補を返した識別では null になります(各候補がそれぞれの識別子を持ちます)。

検証の不一致と識別の一致なしでは、既存のエラーステータスとコードが維持され、同じ受領情報が detail.scan に含まれます。結果が否定的でもスキャンは保存されています。品質上の理由、すなわち使用可能なキーポイントが少なすぎる、または存在しないために拒否された撮影画像も同様です。その操作が報告するエラーコードが何であっても(/tags/extract は SCAN_EXTRACTION_FAILURE、識別は SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS、検証は通常の IDENTIFIER_VERIFY_FAILED を返します)、画像は保持されます。ただし、使用可能なものを抽出できなかったため、受領情報の fingerprintId は null です。プラットフォームがまったくデコードできなかった画像だけは何も保存されず、受領情報もありません。

識別子をスレッドにバインドする

Section titled “識別子をスレッドにバインドする”

POST /api/v1/tags/bind は、tagType とペイロードによって区別される 3 種類の形式を受け付けます。

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

DUST のバインドでは、options.enrollmentSessionId は任意です。バッチを処理する登録ステーションや、1 つのアイテムを複数の角度から撮影する場合など、複数の撮影画像がまとまりを成す場合は、クライアントが生成した UUID を指定し、1 回の実行全体で同じ値を使用してください。これにより、プラットフォームはそれらのスキャンを同じセッションにグループ化します。単発のバインドでは完全に省略してください。DUST 画像のバインドでは、options.returnAnnotatedImage: true を指定して注釈付きの撮影画像を返すこともできます。

スキャンした識別子がスレッドのチームによって所有されるラベルのメンバーである場合(ラベルを参照)、バインドによって単独の識別子が作成されることはありません。ラベル全体がバインドされます。すべてのアクティブなメンバー識別子が 1 回の操作でスレッドに取り付けられ、レスポンスでは通常の tag(スキャンしたメンバー)に加えて、label(ラベル、そのリールと位置)および boundTags(バインドされたすべてのメンバー)が返されます。バインドの一環としてラベルの DUST 識別子も識別可能にするには、activateLabel: true を渡します。これは任意です。別のスレッドにすでにバインドされているラベルの場合は、detail.compositeTagId を伴う IDENTIFIER_ALREADY_BOUND が返されます。

スキャンからスレッドを識別する

Section titled “スキャンからスレッドを識別する”
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"'"]'

識別では、値のペイロード(tagType が QR/BAR_CODE/DATA_MATRIX/NFC で、デコード済みの data を指定するもの、または TEXT で、印刷されたコードを指定し、大文字と小文字を区別せずに照合するもの)や、識別子 ID のみ(tagType: "ANY" と tagId)も受け付けます。

一致した場合は、一致した識別子とそのスレッドが 200 で返されます。一致しなかった場合は、空の結果を持つ 200 ではなく、エラーステータスが返されます。一致しないことが確定した場合は 404 IDENTIFIER_NOT_FOUND、検索を完了できなかった場合は 503 SCAN_SEARCH_INCOMPLETE です。後者は別の状況であり、オペレーターに「見つかりません」と表示してはいけません。どちらの場合も、detail.scan にスキャン受領情報が含まれます。

識別されたスレッド、複数の候補、未バインドのラベル、一致なし、検索未完了、曖昧な一致、撮影画像の拒否、未バインドの識別子という 8 種類すべての結果について、各ステータス、コード、適切なクライアント応答をまとめた正規表は、エラーとスキャン結果 → 識別結果の正規表にあります。code で分岐し、さらに細かく区別する必要がある場合は detail.outcome(no_match、search_incomplete、ambiguous、quality_reject)を読み取ってください。

返された識別子がラベルに属する場合は、必ず tag.label(そのラベル、リール、位置)が含まれます。スキャンがアクティブなチームの在庫にある未バインドのラベルのメンバーと一致した場合、結果は { type: "label", label: { label, tags } } になります。まだスレッドはありませんが、ラベルとそのメンバー識別子が返されるため、クライアントはバインドを提案できます(ラベルを参照)。

searchTeamIds はチーム UUID の JSON 配列です(multipart 本文では JSON 文字列)。省略すると、識別は Dust-Ctx-Team-Id で指定された 1 つのチームだけを検索します。このヘッダー自体を省略すると、組織のルートチームが既定値になります。

ID は任意のものを指定できるわけではありません。所属している同一組織のチームは常に範囲に含められます。パートナー組織のチームに到達できるのは、そのチームから自身へのデータの流れを許可するアクティブな接続がある場合だけです。それ以外はリクエストが失敗するのではなく、範囲から暗黙的に除外されるため、広く見える範囲でも実際の検索対象が狭い場合があります。ID をハードコードせず、有効な ID を取得してください。

  • GET /api/v1/teams — 認証情報が所属する組織内のチーム({ teams: [{ teamId, orgId, name, … }], total })。
  • GET /api/v1/teams/connected — 検索可能なパートナーチーム。接続された 2 つのチームを示す接続記録として返されます。

スレッドに対してスキャンを検証する

Section titled “スレッドに対してスキャンを検証する”

検証は真正性確認の基本操作です。新しいスキャン、threadId、およびそのスレッドにすでにバインドされている候補 tags を指定すると、いずれかの候補が一致した場合に成功します。

tags は必須であり、ID 文字列の配列ではなく、各要素が { "tagId": "…", "tagType": "…" } であるオブジェクトの配列です。multipart 本文では 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" }]));

tagId の値は、確認対象のスレッドにすでにバインドされている識別子です。スレッドから読み取ってください。GET /api/v1/threads/{thread_id} は thread.tags に識別子を返し、それぞれに tagId と tagType が含まれます。したがって、一般的な検証ではスレッドを取得し、その識別子を今回撮影した種類で絞り込み、その結果を候補リストとして送信します。

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

そのスレッドにバインドされていない識別子を送信した場合、別のものと一致するのではなく、検証が失敗します。

候補数によってレスポンス形式が変わります。これは、この統合で最もよくある間違いです。

tags の長さ一致一致なし
ちょうど 1 件{ tag, scan? } を伴う 200IDENTIFIER_VERIFY_FAILED(HTTP 500)、受領情報は detail.scan
2 件以上{ success: true, verifiedTag, attemptedCount, failedCount, scan? } を伴う 200{ success: false, attemptedCount, failedCount, error, scan? } を伴う 200

したがって、複数候補の検証で一致しなかった場合は、success: false を含む、HTTP としては成功した呼び出しになります。response.ok を真正性の証明として扱わないでください。複数の候補を送信した場合は、必ず success を読み取ってください。エラーとスキャン結果 → 検証結果を参照してください。

バインド済み識別子を管理する

Section titled “バインド済み識別子を管理する”

以下は通常の JSON エンドポイントです。すべてで、tagId と、その識別子がバインドされている threadId の両方が必要です。

  • POST /api/v1/tags/text — name および/または description を設定します。
  • POST /api/v1/tags/update — name、description、isPrivate、archivedAt を設定します(ISO タイムスタンプを指定すると識別子がアーカイブされ、null を指定すると復元されます)。識別子の値と種類は変更できません。代わりに再バインドしてください。
  • POST /api/v1/tags/unbind — 識別子をスレッドから切り離します。

/api/v1/tamper 以下の改ざん分析は、DUST 識別子の新しいスキャンを、その識別子がバインドされたときに撮影された基準画像と比較し、測定内容を記録します。API が返すのは測定結果と証拠だけです。概要を示す数値、帯域、しきい値、プラットフォームが作成した結果フィールドは、このインターフェースのどこにもありません。また、alignmentOutcome が示すのは、2 つのスキャンを比較できたかどうかだけです(比較できなかった場合、測定結果は比較不可ですが、これは識別子自体についての判断ではありません)。

操作メソッドとパス
分析を実行POST /api/v1/tamper/analyses — フォームエンコード:threadId、tagId、および data と queryFingerprintId のどちらか一方のみ
所見を記録POST /api/v1/tamper/observations — { analysisId, result }
スレッドの分析を一覧表示GET /api/v1/tamper/analyses?threadId=…(任意で tagId、limit)
1 件の分析を取得GET /api/v1/tamper/analyses/{analysis_id}
結果ビットマップを取得GET /api/v1/tamper/analyses/{analysis_id}/artifacts/{name}

分析の実行では、threadId、tagId、および次のうちちょうど一方を含む multipart/form-data または application/x-www-form-urlencoded 本文を使用します。

  • data — DUST スキャンそのもの。ファイルまたは base64 エンコードされた画像として指定します。サービスが抽出を行います。
  • queryFingerprintId — 別途抽出した場合に、前述の POST /api/v1/tags/extract から取得済みのフィンガープリント ID。

両方を送信した場合も、どちらも送信しなかった場合も拒否されます。どちらの方法でも、通常の DUST 撮影画像が有効な入力になります。改ざん分析専用の撮影経路はありません。送信したスキャンを読み取れない場合はリクエストが失敗し、分析は記録されません。

改ざん分析所見は、プラットフォームが保存する唯一の結論であり、人が作成します。result は consistent、expected、inconsistent、unknown のいずれかで、既定値はなく、必須です。expected は、その識別子のユースケースおよび基材において通常想定される摩耗や劣化を記録します。所見は変更不可で、作成者が記録されます。新しい所見が以前の所見を置き換えることはなく、読み取り時には単一の現在結果ではなく、シリーズ全体(observations、新しい順)が返されます。メトリクスから結果を導出したり、自身の UI でシリーズを 1 つの値にまとめたりしないでください。

分析には metrics(アルゴリズムのカバー率とマーカー数をそのまま格納したオブジェクト)、任意の markerPoints、および artifactNames が含まれます。各マーカー座標セットは、それぞれのスキャンのピクセル空間にあります。1 つの座標系に合成するには、クエリ側の点に metrics.transformation_matrix を適用してください。結果ビットマップは保護されたコンテンツです。アーティファクトエンドポイントから取得してください。このエンドポイントはリクエストごとに再認可し、キャッシュ不可のバイト列を返します。

ラベル(通信上の名前:composite tag、名前空間 /api/v1/composite-tags)は、任意の種類の識別子を 1 つ以上持つ 1 枚の物理的なラベルです。DUST は必須ではありません。ラベルはリール上の位置に配置されます(collection.kind = "reel" で、UUID により識別されます。name は印刷されたリール番号または任意のタイトルであり、一意ではありません)。リールは、出荷されることのないフォルダーであるラベルコレクション(kind = "reel_collection")に格納できます。リールの expectedIdentifiers は、そのリール上の完全なラベルが各種類の識別子をいくつ持つかを [{ "tagType", "count" }] で示します(既定値は TEXT、DUST、QR が各 1 つ。入力時の count が 0 の場合、その種類は予定されていません)。これは登録ステーション向けのヒントであり、制約ではありません。ラベルの complete フラグは、予定されている各種類について、少なくともその数のアクティブな識別子があることを意味します。

操作メソッドとパス
ラベルコレクションを一覧表示/作成GET, POST /api/v1/composite-tags/collections; PATCH …/collections/{collection_id}
リールを一覧表示GET /api/v1/composite-tags/reels?collectionId=…&unfiled=…&transferred=any|only|hide&q=…
リールを作成POST /api/v1/composite-tags/reels — { name, description?, collectionId?, expectedIdentifiers? }
リールを取得/更新GET, PATCH /api/v1/composite-tags/reels/{reel_collection_id}(名前変更、予定構成、移動先の collectionId。null でコレクション未所属にする)
ラベルを作成POST /api/v1/composite-tags/reels/{reel_collection_id}/labels(multipart)
メンバー識別子を追加/取り除くPOST /api/v1/composite-tags/{composite_tag_id}/identifiers(multipart); DELETE …/identifiers/{tag_id}
ラベルを一覧表示/取得GET /api/v1/composite-tags?reelCollectionId=…&bound=any|only|unbound&transferred=…&q=…; GET …/{composite_tag_id}
メンバーの値からラベルを解決POST /api/v1/composite-tags/resolve — { tagType, value, reelCollectionId? }(TEXT は大文字と小文字を区別せずに照合)。レスポンスには detail(最初の一致)と candidates[](すべての一致。リールが指定された場合は位置順)が含まれる
ラベルを移動または切り分けPOST /api/v1/composite-tags/move; POST /api/v1/composite-tags/move/preview で事前確認
ラベルをバインド/バインド解除POST /api/v1/composite-tags/{composite_tag_id}/bind — { threadId, options?: { indexing: "default" } }; POST …/unbind
リール範囲を一括バインドPOST /api/v1/composite-tags/reels/{reel_collection_id}/bulk-bind — { fromPosition, toPosition, threadIds, activate?, dryRun? }
ラベルをアーカイブ/復元POST …/{composite_tag_id}/archive, POST …/unarchive
アクティベートPOST …/{composite_tag_id}/activate; POST /api/v1/composite-tags/reels/{reel_collection_id}/activate(バックグラウンド)
単独の DUST 識別子をアクティベートPOST /api/v1/tags/activate — { tagIds[] }(最大 200 件)。識別子ごとに 1 件の結果を返し、元には戻せない

リールを作成してラベルを登録する

Section titled “リールを作成してラベルを登録する”
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 }] }'

レスポンスは { reel } で、各件数が 0 のリール概要です。reel.collectionId を保持してください。これがリールを識別します。リールの作成では常に新しいリールが作成され、名前による再利用はありません。

各ラベルは 1 件の multipart リクエストで登録します。data には DUST 画像を最大 1 つ指定します。その他のすべてのメンバーは、{ tagType, value } エントリーの JSON 配列である identifiers、または POST /api/v1/tags/extract ですでに抽出済みの追加 DUST に対する { tagType: "DUST", fingerprintId } として渡します。humanReadable と qrValue は、それぞれ TEXT メンバーと QR メンバーの短縮表記です。position の既定値は、リール上の次の空き位置です。

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

少なくとも 1 つの識別子が作成される必要があります。成功時のレスポンスには outcome: "created" が含まれます。同じ位置で同じ DUST マーキングを使用して再試行すると、outcome: "already_enrolled" として整合されます。別のラベルにすでに含まれている、またはすでにバインド済みの DUST には 409 COMPOSITE_TAG_CONFLICT が返されます。重複した TEXT または QR の値は拒否されません。一部のリールでは同じ値を正当に繰り返すため、代わりにリールの warnings で報告されます。

options.indexing は、data の画像に対する DUST インデックスモードを選択します。none(検証のみ)を指定しない限り、default(識別可能)です。無人登録ステーションでは通常、検証のみで登録して後からアクティベートします。アクティベーションでは各 DUST をインデックス化し、プラットフォームの重複確認を実行するため、インデックス済みの DUST と重複する DUST を持つラベルは報告され、スキップされます。

同等の型付きクライアントフローは次のとおりです。

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 は、source、target、任意の expectedCount を受け取ります。

source には 3 つの形式があります。

  • { compositeTagIds } — 手動で選択したラベル。指定順に移動します。
  • { reelCollectionId, fromPosition, toPosition? } — 型付きの位置範囲(切り分け)。toPosition の既定値はリールの最後の位置です。
  • { fromCompositeTagId, toCompositeTagId } — スキャンで境界を指定する切り分け。範囲の最初と最後のラベルを、どちらの順序でも指定できます。サーバーはロックを取得して両者の位置を読み取ります。両方とも同じリール上のアクティブなラベルでなければなりません。それ以外の場合、detail.reason は endpoints_on_different_reels、endpoint_archived、endpoint_not_on_reel のいずれかになります。スキャンした値から各ラベルを解決するには /resolve を使用します(リールを範囲として指定すると、印刷されたコードが重複している場合に複数の candidates が表示され、呼び出し元で区別できます)。アクティベート済み DUST の場合は、POST /api/v1/tags/identify から解決することもできます。

target は { reelCollectionId } または { newReel: { name, description?, collectionId?, expectedIdentifiers? } } です(構成が指定されていない場合、新しいリールは移動元リールの構成を継承します)。

範囲内のすべてのアクティブなラベルが移動します。アーカイブ済みまたは出荷済みのラベルがある位置や、ラベルがない位置は、移動元リールに残る空きです。移動先ですべての位置が空いている場合は位置が維持されます。それ以外の場合は、バッチ全体が移動元での順序を保ったまま、移動先の最後の位置の後ろに追加されます。レスポンスには moved[] が含まれます。また、範囲またはスキャンで境界を指定した移動元の場合は、cut: { sourceReel, fromPosition, toPosition, count, boundCount, boundPositions, gaps[] } も含まれます。

expectedCount を指定すると、その件数が契約になります。移動対象となるアクティブなラベルがちょうどその数でなければ、400 INVALID_REQUEST と detail.reason: "count_mismatch"(expected、actual、fromPosition、toPosition)で移動が拒否されます。

POST /api/v1/composite-tags/move/preview は同じ source、任意の target、expectedCount を受け取り、何も変更せず、span、count、boundCount、バッチの first と last のラベル、predictedOutcome(kept_positions / appended / null)、countMatches、suggestedLast を返します。suggestedLast は、範囲が短い場合に expectedCount を満たす、リールの先にあるラベルです。これはオペレーターにスキャンを促す提案にすぎず、サーバーによって適用されることはありません。事前確認にはメンバー権限が必要で、移動にはチームまたは組織の管理者権限が必要です。

リール範囲を一括バインドする

Section titled “リール範囲を一括バインドする”

POST /api/v1/composite-tags/reels/{reel_collection_id}/bulk-bind は、fromPosition..toPosition の位置(両端を含む)にあるラベルを、指定順に threadIds へバインドします。k 番目の位置が k 番目のスレッドに対応します。処理は全件成功または全件失敗で、厳密です。範囲には threadIds.length とちょうど同じ数の位置が必要です(toPosition は必須であり、導出されません。これにより、呼び出し元がスプール上で確認した範囲を明示します)。1 回の呼び出しは最大 500 組で、すべての位置にアクティブで未バインドのラベルが存在する必要があります。サーバーが位置をスキップすることはありません。位置をスキップすると、それ以降のすべての対応関係が暗黙的にずれるためです。

バインドせずに事前確認するには、dryRun: true を渡します。どちらの場合もレスポンス形式は同じです。

  • outcome — 実際にバインドした後は "bound"、ドライランでは "preflight"。
  • rows[] — 対応関係ごとに 1 件:index、position、compositeTagId(空の位置では null)、labelName、textValue(ラベルの TEXT メンバー、すなわち印刷されたコード)、threadId、threadName、threadDescription。
  • blockers[] と warnings[] — { kind, index, position, compositeTagId?, threadId?, tagType?, existing? }。
  • activation — "queued"、"not_requested"、"already_active"、"no_dust" のいずれか。
  • reel — 件数が更新されたリール概要。

ブロッカーの種類: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。警告の種類:label_incomplete(リールが予定する数より識別子が少ないラベル)および thread_has_label(スレッドがすでにラベルを持っており、existing[] にそのラベルが示されます)。警告によってバインドが停止することはありません。

ブロッカーが 1 件でもあるコミットは 409 COMPOSITE_TAG_CONFLICT で失敗します。detail にはドライランと同じ rows、blockers、warnings が含まれるため、クライアントが解析する形式は常に 1 つだけです。範囲の長さが threadIds.length と異なる場合は 400 INVALID_REQUEST です。

権限:リールのチームへの所属と、各スレッドの編集権限が必要です。呼び出し元が編集できないスレッドは、リクエスト全体が拒否されるのではなく、その行の thread_not_editable ブロッカーになります。また、すべてのスレッドをリールのチームが所有している必要があります。単にチームと共有されているスレッドは thread_not_owned になります。

activate: true を指定すると、まずバインドをコミットし、その後 1 件のバックグラウンドジョブがバインドされたラベルの DUST マーキングだけをアクティベートします。アクティベーションに失敗したラベルもバインド済みのまま残り、検証のみとなります。進行状況はリールの counts.identifiableCount をポーリングしてください。

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 }'

型付きクライアントでは、次のようになります。

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

このバインドによって、N 回の個別バインドとまったく同じイベントが残ります。メンバー識別子ごとに 1 件の bind イベントが作成され、それぞれがスレッド、識別子、ラベルをターゲットとして持ち、すべてが 1 つの操作 ID を共有します。

バインド、バインド解除、出荷

Section titled “バインド、バインド解除、出荷”

ラベルは全体としてバインドされます。POST …/{composite_tag_id}/bind は、ラベルとそのすべてのアクティブなメンバー識別子をスレッドに取り付けます。options.indexing: "default" を指定すると、同時にアクティベートも行います。通常の POST /api/v1/tags/bind を任意のメンバー識別子に対して呼び出した場合も同じ処理が行われます(ラベルをバインドするを参照)。これはスキャナーが行う処理です。バインドにはスレッドの編集権限と、ラベルに対するチーム所有権が必要であり、スレッドも同じチームが所有していなければなりません。別のチームから共有されたスレッドにメンバー識別子をスキャンすると、単独のコピーとしてバインドされるのではなく、拒否されます(409 COMPOSITE_TAG_CONFLICT、reason: "label_owned_by_other_team")。POST …/unbind は、ラベルとそのすべてのメンバーを切り離します。

チームと組織の所有権はコンテキストヘッダーのみから取得され、作成者は検証済みの bearer トークンから取得されます。ラベルの読み取りとアクティベートには、チームへの所属が必要です。リールの作成、登録、移動、アーカイブは、サインイン済みのチームまたは組織の管理者、あるいは選択したチームのメンバーである組織スコープのサービスアカウントが実行できます。サービスアカウントが組織管理者の権限を借用することはできません。

出荷では、リールは明細書アイテム({ kind: "reel", collectionId })であり、その上のすべてのラベルが未バインドの場合に限り、リール全体で出荷されます。バインド済みのラベルはそのスレッドとともに出荷され、リールが出荷に含まれることはありません。出荷済みのリールとラベルは、送信側でも transferredAt が設定された状態で引き続き読み取れます。transferred=only または transferred=hide で絞り込んでください。

DICE のワークフローについては、ラベルとリールを参照してください。