# アカウントプロトコル Source: https://adcp-docs-ja.pier1.co.jp/docs/accounts/overview AdCP アカウントプロトコルは広告取引の商取引レイヤーを定義します — 請求、オペレーター認可、バイヤー・ブランド・ベンダーエージェント間の利用報告。 アカウントプロトコルは、すべての AdCP ベンダープロトコルの基盤となる商取引レイヤーを定義します。メディアバイ、データシグナル、コンテンツ標準チェックなど、あらゆる取引は商取引関係を持つ当事者間で行われます。アカウントプロトコルはその関係を確立し、ベンダーがサービスの利用状況を追跡できるよう消費報告を提供します。 ## 商取引モデル すべての AdCP 取引には7つの根本的な問いがあります。 | 問い | 回答主体 | 仕組み | | ---------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 広告主は誰か? | ブランドレジストリ | `brand.domain` が `brand.json` に解決される | | ブランドの代理で誰が動くか? | ブランドレジストリ | `brand.json` の `authorized_operators` がブランド代理購入者を宣言 | | オペレーターはどう認証するか? | セラーケイパビリティ | `require_operator_auth` が誰が認証しなければならないか、どのアカウント参照形状が期待されるかを決定。 | | このアカウントで何をしてよいか? | 呼び出し元スコープ | [`sync_accounts`](/docs/accounts/tasks/sync_accounts) と [`list_accounts`](/docs/accounts/tasks/list_accounts) のレスポンスの各アカウントごとエントリの `authorization` オブジェクトが、呼び出しエージェントの `allowed_tasks`、`field_scopes`、`scope_name`、`read_only` を記述。下記の [Caller authorization](#caller-authorization) を参照。 | | 誰が請求を受けるか? | バイヤー宣言 | バイヤーが `sync_accounts` で `billing` を渡す — `operator`、`agent`、`advertiser`。セラーが承認または拒否。 | | 何が消費されたか? | 利用報告 | `report_usage` がベンダーエージェントに配信後のサービス利用状況を通知 | セラーは [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) の `require_operator_auth` でアカウントモデルを宣言します。そのフィールドは誰が認証しなければならないかを宣言します。それ自体は OAuth が使われるか、`list_accounts` が公開されるか、どの `sync_accounts` モードがサポートされるかを宣言しません。 `require_operator_auth` が `true`(**アカウント ID 名前空間**)の場合、オペレーターは独立して認証し、セラーまたは上流プラットフォームが正準のアカウント名前空間を所有するため、バイヤーはセラー割り当ての `account_id` 値を渡します。クレデンシャルが複数のアカウントにアクセスし得る場合、セラーは [`list_accounts`](/docs/accounts/tasks/list_accounts) を公開しなければならず(MUST)、バイヤーは最初のアカウントスコープリクエストの前に明示的な `account_id` を解決しなければなりません(MUST)。クレデンシャルが正確に 1 つのアカウントに束縛される場合、セラーはそのシングルトンを返す `list_accounts` を公開すべきで(SHOULD)、同じ明示的な `account_id` が別の宣言されたパスまたはアウトオブバンドのオンボーディングを通じて供給される場合のみ省略してもよい(MAY)。 `require_operator_auth` が `false`(**バイヤー宣言アカウント**)の場合、エージェントは信頼され、バイヤーは [`sync_accounts`](/docs/accounts/tasks/sync_accounts) でブランド/オペレーターのペアを宣言してアカウントをプロビジョニングします。 **広告ネットワーク**は両方のモデルを同時に利用できます — バイヤー向けにはバイヤー宣言アカウント(ネットワークはエージェント信頼)、各基盤プラットフォームとはアカウント ID 名前空間(ネットワークはオペレーターとして認証)。完全なアカウントチェーン(`バイヤーエージェント → ネットワーク(バイヤー宣言)→ AI プラットフォーム(アカウント ID 名前空間)`)については [Sponsored Intelligence ガイド — ネットワークのアカウントモデル](/docs/sponsored-intelligence/networks#account-model-for-networks) を参照してください。 配信後、オーケストレーターは [`report_usage`](/docs/accounts/tasks/report_usage) を呼び出し、ベンダーエージェント(シグナル、ガバナンス、クリエイティブ)にサービスの消費状況を通知します。これは精算ではなく、ベンダーが獲得収益を追跡し請求を検証するための消費報告です。 ## スコープ アカウントプロトコルはすべてのベンダープロトコルに適用されます。オーケストレーターはブランド/オペレーターのペアごとにベンダーエージェントとのアカウントを一度確立し、そのエージェントとのすべてのやり取りで同じアカウント参照を再利用します。 | ベンダープロトコル | アカウント参照の用途 | | --------- | ---------------------------- | | メディアバイ | レートカード、請求書、キャンペーン帰属 | | シグナル | アカウント別料金オプション、アクティベーション、利用報告 | | ガバナンス | コンテンツ標準の請求 | | クリエイティブ | クリエイティブサービスの請求 | アカウント参照はセラーが割り当てた `account_id`(セラー所有の名前空間、通常 `require_operator_auth: true`)または自然キー — `brand` + `operator`(バイヤー宣言アカウント、`require_operator_auth: false`)のいずれかになります。バイヤー宣言アカウントでは、セラーが内部 `account_id` もエコーしても、自然キーの `AccountRef` は後続の呼び出しで有効なままでなければなりません(MUST)。サンドボックスの場合、アカウント ID 名前空間は `list_accounts` で探索するかアウトオブバンドで供給される既存のテストアカウントを使い、バイヤー宣言アカウントは `sandbox: true` を付けた `sync_accounts` でサンドボックスを宣言します。詳細は [アカウント参照](/docs/building/by-layer/L2/accounts-and-agents#account-references) を参照してください。 ## アカウントステータスライフサイクル アカウントは定義された状態のセットを進みます。終端状態(`rejected`、`closed`)はそれ以上の遷移を許しません。 ``` sync_accounts ──▶ pending_approval ──▶ active │ │ │ (seller declines) ├── (credit limit / funds depleted) ▼ │ ▼ rejected (terminal) │ payment_required │ │ (buyer resolves billing) │ ▼ │ active │ ├── (seller suspends) ──▶ suspended │ │ │ (seller reactivates) ◀─┤ │ │ │ └──▶ closed (terminal) │ └── (seller or buyer closes) ──▶ closed (terminal) ``` **遷移ルール:** * `pending_approval` → `active`: セラーが与信/契約/アイデンティティレビュー後に承認 * `pending_approval` → `rejected`: セラーが拒否。終端 — バイヤーは新しいアカウントリクエストを提出しなければならない。 * `active` → `payment_required`: 与信限度に達したか資金が枯渇したとき自動 * `payment_required` → `active`: バイヤーが未払い残高を解決したとき。セラーは自動遷移してもよい(MAY)し、手動再アクティベーションを要求してもよい(MAY)。 * `active` → `suspended`: セラー起因(ポリシー違反、請求紛争、不正レビュー)。セラーは Webhook でオーケストレーターに通知しなければならない(MUST)。 * `suspended` → `active`: セラー起因の再アクティベーション * `suspended` → `closed`: セラー起因の恒久クローズ * `active` → `closed`: セラーまたはバイヤー起因の恒久クローズ。終端。 * セラーは終端状態のアカウントへのオペレーションを `ACCOUNT_NOT_FOUND` または適切なエラーで拒否しなければならない(MUST) ### アカウントステータス別のオペレーション アカウントステータスは、どのタスクが許可されるかのゲートとして機能します。読み取り専用オペレーションは常に利用可能。変更オペレーションはステータスに基づいて制限されます。 | Task | `active` | `pending_approval` | `payment_required` | `suspended` | `rejected` / `closed` | | ------------------------ | -------- | ------------------ | ------------------ | ----------- | --------------------- | | `list_accounts` | Yes | Yes | Yes | Yes | Yes | | `get_account_financials` | Yes | Yes | Yes | Yes | No | | `get_products` | Yes | No | Yes | No | No | | `create_media_buy` | Yes | No | No | No | No | | `update_media_buy` | Yes | No | Yes | No | No | | `get_media_buys` | Yes | No | Yes | Yes | No | | `sync_creatives` | Yes | No | Yes | No | No | | `sync_catalogs` | Yes | No | Yes | No | No | | `sync_event_sources` | Yes | No | Yes | No | No | | `report_usage` | Yes | No | Yes | Yes | No | * `payment_required` は新規支出(`create_media_buy`)をブロックするが、既存バイの管理とセットアップの解決を許可。パッケージ追加は機能的に新規支出と同等なので、セラーはアカウントが `payment_required` のとき `update_media_buy` 内の `new_packages` も拒否すべき(SHOULD)。 * `suspended` は既存データへの読み取り専用アクセスを許可するが、すべての変更をブロック * セラーは、停止アカウントのブロックされたオペレーションには `ACCOUNT_SUSPENDED` を、payment-required アカウントのブロックされたオペレーションには `ACCOUNT_PAYMENT_REQUIRED` を返さなければならない(MUST) ## Caller authorization アカウントにアクセスできるすべての呼び出し元が同じ付与を持つわけではありません。ベンダーエージェントは、ある呼び出しエージェントに完全なスコープを、別のエージェントに狭い読み取り+更新スコープを発行できます。認証は呼び出し元が主張どおりの者であることを確認します。認可は「この呼び出し元はこのアカウントで何をしてよいか?」に答えます。 **すべてのベンダープロトコルに適用。** ここで説明する認可メカニズムは共有 Accounts Protocol の一部です — `sync_accounts` / `list_accounts` を実装するすべてのエージェント(media-buy sales agent、signals agent、governance agent、creative agent、brand agent)に適用されます。signals エージェントはアクティベーション vs カタログアクセスをスコープし、governance エージェントは監査読み取り vs プラン管理をスコープし、creative エージェントはライブラリ読み取り vs アップロードをスコープします。標準の名前付きスコープ `attestation_verifier` のみが Media Buy Protocol 固有です(**AAO Verified (Live)** 修飾子にバインド)。残りの仕組み — `allowed_tasks`、`field_scopes`、`read_only`、`custom:` プレフィックスのスコープ — はプロトコル中立です。 **スキーマ**: [`/schemas/v3/core/account-authorization.json`](https://adcontextprotocol.org/schemas/v3/core/account-authorization.json) スコープイントロスペクションをサポートするベンダーエージェントは、[`sync_accounts`](/docs/accounts/tasks/sync_accounts) と [`list_accounts`](/docs/accounts/tasks/list_accounts) のレスポンスの各アカウントごとエントリに `authorization` オブジェクトを付加します: ```json theme={null} { "account_id": "acc_acme_compliance", "name": "Acme c/o AAO Compliance", "status": "active", "billing": "operator", "authorization": { "allowed_tasks": [ "get_adcp_capabilities", "get_products", "get_media_buys", "get_media_buy_delivery", "list_creatives", "update_media_buy" ], "field_scopes": { "update_media_buy": ["reporting_webhook"] }, "scope_name": "attestation_verifier", "read_only": false } } ``` ### フィールド | Field | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `allowed_tasks` | 呼び出し元がこのアカウントに対して呼び出してよい正準の snake\_case タスク名。タスクの不在は「不許可」と読まなければならない(MUST)— 不在のタスクを呼び出すと `SCOPE_INSUFFICIENT` を返す。 | | `field_scopes` | 呼び出し元が設定してよいリクエストフィールドのタスクごとのオプション許可リスト。キーはタスク名、値はフィールドパス。タスクがここに現れるとき、許可リスト外の任意のフィールドは `FIELD_NOT_PERMITTED` を返す。暗黙的なフレーミングフィールド — 型付きエンティティ参照(`account`、`media_buy_id`、`package_id`、`creative_id`、`signal_id`、`format_id`、`proposal_id`、`plan_id`、`session_id`)、並行性/冪等性(`revision`、`idempotency_key`)、バイヤー側相関(`buyer_ref`、`po_number`)、モードフラグ(`dry_run`)、ページネーション(`pagination`、`cursor`、`max_results`)、エンベロープフィールド(`context`、`ext`、`adcp_major_version`、`push_notification_config`)— は常に許可され、許可リストに現れる必要はない。リストは非網羅的: 読み取りタスクの他の任意の型付きエンティティ ID パラメーターやクエリ形成フィールドはフレーミングとして扱うべき(SHOULD)。 | | `scope_name` | オプションの名前付きスコープ識別子。`attestation_verifier` のみが標準化(media-buy 固有、**AAO Verified (Live)** 修飾子にバインド)。エージェント定義の名前は `custom:` プレフィックスを使わなければならず(MUST)、標準値のタイポがスルーせずスキーマ検証に失敗するように。 | | `read_only` | 便宜フラグ。true のとき、タスクが `allowed_tasks` にあるかに関わらず変更は `READ_ONLY_SCOPE` を返す。省略は `false` と等価。呼び出し元は `allowed_tasks` だけから read-only を推論してはならない(MUST NOT)。 | ### 存在と不在のセマンティクス * **存在**: ベンダーエージェントは、形状がこの瞬間のこのアカウントに対するこの呼び出し元への強制を反映すると表明します。数秒古いのは問題ないが、体系的に発散するのは非コンフォーマント。 * **単一アカウントで不在**: ベンダーエージェントはそのアカウントについて呼び出し元にスコープを伝えていません。呼び出し元はエラー駆動ディスカバリー(タスクを試し、RBAC エラーコードを処理)にフォールバックします。 * **すべてのアカウントで不在**: ベンダーエージェントはスコープイントロスペクションを実装していません。呼び出し元は不在からアクセスを推論してはならない(MUST NOT)— ベンダーエージェントは依然ローカルでスコープを強制し、任意のタスク呼び出しで `SCOPE_INSUFFICIENT` / `READ_ONLY_SCOPE` / `FIELD_NOT_PERMITTED` を返し得ます。 * **`authorization` は 3.x でオプション**。破壊的変更を避けたいベンダーエージェントは設定を延期できます。設定は厳密に追加的です — 呼び出し元が試行で発見するはずのエラーを先取りできるようにします。 `attestation_verifier` 標準スコープ(Media Buy Protocol 固有)を主張するベンダーエージェントは `authorization` を設定しなければなりません(**MUST**)— **AAO Verified (Live)** 証明フローは、宣伝されたスコープが強制に一致することの検証に依存します。 ### アイデンティティバインディング、リフレッシュケイデンス、一貫性 `authorization` オブジェクトは読み取り時に **`(呼び出し元アイデンティティ, account_id)` タプルに暗黙的にスコープ**されます。異なる認証済み呼び出し元に返される同じアカウントは異なる `authorization` オブジェクトを返してもよい(MAY)— それが RBAC モデルの要点です。ベンダーエージェントは呼び出し元アイデンティティを認証済みリクエストから解決しなければならず(MUST、クライアント供給フィールドからではない)、返す `authorization` をその解決されたアイデンティティにバインドしなければなりません(MUST)。 **リフレッシュケイデンス。** * 呼び出し元は、アカウントに対する能動的使用の少なくとも **300 秒**ごとに、`sync_accounts` または `list_accounts`(保持する `account` でフィルタ)を介して `authorization` を再読み取りすべき(SHOULD)。 * ベンダーエージェントは、オペレーター起因のスコープ変更を、変更がなされてから **300 秒以内**に `sync_accounts` / `list_accounts` レスポンスに反映しなければならない(MUST)。300 秒ごとにポーリングするコンフォーマントな呼び出し元は、最大 1 リフレッシュサイクル以内にオペレーター変更を見ます。 * ベンダーエージェントは `authorization` オブジェクトを短期間キャッシュしてもよい(MAY)が、再検証なしに 300 秒を超えてキャッシュしてはならない(MUST NOT)。 * 300 秒の数字はターゲットではなくフロアです — スコープがより頻繁に変わるベンダーエージェントはより速く表面化すべきで(SHOULD)、**AAO Verified (Live)** 証明を実行する呼び出し元は check-7 ケイデンス(ローリングウィンドウごとに少なくとも 1 回、加えて観測されたすべてのスコープ変更時)で探るべき(SHOULD)。 **一貫性。** ある `(呼び出し元アイデンティティ, account_id)` タプルについて、リフレッシュウィンドウ内の逐次読み取りは、オペレーター起因のスコープ変更を除き、同一の `authorization` オブジェクトを返さなければならない(MUST)。ロードバランスされたまたは結果整合性のバックエンドからのちらつき — 異なるレプリカにヒットして 10 秒離れた 2 つの読み取りが異なる `allowed_tasks` を返す — は非コンフォーマント。コンプライアンスエンジンとコーディングエージェントは状態追跡にスコープの安定性に依存します。それを保証できないベンダーエージェントは、一貫性なく設定するのではなく `authorization` を省略しなければなりません(MUST)。 これは上記の存在セマンティクスの「体系的に発散するのは非コンフォーマント」保証の具体形です — 検証可能です: コンフォーマンスチェックはリフレッシュウィンドウ内で同じアイデンティティから同じアカウントを 2 回読み、結果を diff できます。 ### リフレッシュウィンドウ内での SCOPE\_INSUFFICIENT へのバイヤー応答 単一の `SCOPE_INSUFFICIENT` レスポンスは、2 つの原因間で観測的に区別不能です: 有効な付与をまだ伝播していないセラーレプリカ(一時的インフラアーティファクト — 解決する)と、オペレーターによる正当なスコープ削減(永続的 — 表面化しなければならない)。エラーをオペレーター介入が必要な確定的な `correctable` シグナルとして分類する前に、バイヤーは 2 つを区別するため小さな制限されたリトライバジェットを使い果たしてもよい(MAY): * **リトライバジェット:** 3 回以下、各回 1〜5 秒のジッター付きバックオフで分離。これは曖昧性解消ロジック — `correctable` 分類を信頼する前にスコープが本当に不十分かを確立 — であり、correctable エラーの復旧アクションではありません。 * **300 秒ウィンドウではない:** リトライバジェットはセラーの伝播 SLA ではありません。バイヤーはすべての偽陰性で 300 秒ウィンドウ全体を待つべきではありません(SHOULD NOT)。累積 15 秒までのバックオフ遅延が典型的なレプリカラグを吸収するのに十分です。 * **リトライ尽きた後:** バイヤーはエラーを表面化しなければなりません(MUST)。「表面化」とは: 呼び出し層に構造化エラー(アカウント ID、失敗したタスク、試行回数を持つ `error.details.retry_count` を含む)を返す AND 失敗をオペレーターがアクセス可能なチャネル — 構造化ログ、ダッシュボードアラート、通知 — で可視にすることを意味します。エスカレーションメカニズムは実装依存。要件はエラーが黙って飲み込まれないことです。 `READ_ONLY_SCOPE` は、バイヤーが付与伝播ラグ(書き込み付与更新に遅れているレプリカ)を疑うとき、同じ制限リトライロジックに従います。注意: 書き込みアクセスが最近*失効*した場合、古いレプリカは、失効したスコープが拒否すべきだった変更を受け入れるかもしれません。制限リトライが成功した場合、バイヤーは結果を信頼できるとして扱う前に `authorization` を再読み取りしなければならず(MUST)、再読み取りで書き込みアクセスが失効したことが確認された場合、バイヤーは変更を潜在的に未認可としてフラグするアラートをオペレーターに表面化しなければならず(MUST)、正しいものとして黙って受け入れてはなりません(MUST NOT)。 `FIELD_NOT_PERMITTED` はこのパターンに従いません。エージェント自律の復旧パス — 許可されないフィールドを削除して再送 — がリトライの考慮に優先します。バイヤーは `FIELD_NOT_PERMITTED` について同一の失敗リクエストをリトライしてはならず(MUST NOT)、即座に修正して再送すべき(SHOULD)。 これらのコードの規範的なリトライ例外条項は [Authorization (RBAC)](/docs/building/by-layer/L3/error-handling#authorization-rbac) を参照。 ### 標準の名前付きスコープ: `attestation_verifier` **AAO Verified (Live)** 準備([#2965](https://github.com/adcontextprotocol/adcp/issues/2965) で追跡)を宣伝する media-buy sales agent は、次の最小形状で `scope_name: "attestation_verifier"` で識別される名前付きスコープをサポートしなければなりません(MUST): * `allowed_tasks`(最小限 — ベンダーエージェントは追加の読み取り専用タスクを含めてもよい): * `get_adcp_capabilities` * `get_products` * `get_media_buys` * `get_media_buy_delivery` * `list_creatives` * `update_media_buy` * `field_scopes.update_media_buy`: `["reporting_webhook"]` * `read_only`: `false` このスコープは `create_media_buy`、`sync_creatives`、`update_media_buy` のすべての支出コミットまたはターゲティング変更フィールドを意図的に省略します。継続的な可観測性検証のために狭く設計されています — コンプライアンスエンジンはライブキャンペーンを発見し、インベントリとクリエイティブの状態を読み、検証レポート Webhook を接続し、デリバリーを読めますが、インベントリを予約したり、予算を変更したり、フライト日を変えたり、クリエイティブをアップロードしたり、何かをキャンセルしたりはできません。 `get_products` と `list_creatives` が含まれるのは、**AAO Verified (Live)** の可観測性が、セラーの宣言されたインベントリとアクティブなバイのクリエイティブパイプライン状態のサニティ読み取りを必要とするためです。 `attestation_verifier` は Media Buy Protocol 固有です — **AAO Verified (Live)** 修飾子にバインドし、それはメディアバイフロー(ライブ観測は実際の広告配信を必要とする)です。signals、governance、creative、brand エージェント向けの同等の可観測性スコープはまだ標準化されていません。標準化されるまで、それらのエージェントは `custom:` スコープを使います。 **レポートのみ — ライフサイクル実行は将来のスコープ。** `attestation_verifier` は (Live) の*レポート*半分です: エンジンはセラーがトラフィックしたキャンペーンを観測し、デリバリーを読み、検証 Webhook を接続します。意図的にキャンペーンを作成したり、クリエイティブを接続したり、予算を変更したりはできません。補完的な*ライフサイクル実行*の役割 — エンジンが正準の PSA をセラーのライブエージェントを通じてエンドツーエンドでトラフィックする、[#3046](https://github.com/adcontextprotocol/adcp/issues/3046) で検討される AAO 運用の正準キャンペーンランナー向け — はより広い書き込みスコープ(`attestation_runner`、[#3561](https://github.com/adcontextprotocol/adcp/issues/3561) で追跡)を必要とします。今日のブラウンフィールド登録(Path B)は `attestation_verifier` のみを必要とします。ランナー側のスコープは正準キャンペーンランナー自体とともに点灯します。 ### 他のベンダープロトコル向けのカスタムスコープ 任意のベンダーエージェントは `custom:` プレフィックスを使ってカスタムスコープを定義してもよい(MAY)。バイヤーは custom-prefixed スコープ名からいかなるセマンティクスも仮定してはなりません(MUST NOT)— 名前はエージェント定義で、帯域外(ドキュメント、オンボーディング)で学ばれます。 例示的な例(標準化されていない — 各エージェントが独自に命名): * **Signals agent**: `custom:activation_only` — `allowed_tasks: [get_signals, activate_signal, get_adcp_capabilities]`、カタログ管理なし、クロスアカウントメタデータなし。 * **Governance agent**: `custom:audit_viewer` — `allowed_tasks: [get_plan_audit_logs, get_adcp_capabilities]`、`read_only: true`。ガバナンストレイルへの読み取りアクセスを付与された規制当局や外部監査人に有用。 * **Creative agent**: `custom:library_reader` — `allowed_tasks: [list_creatives, list_creative_formats]`、`read_only: true`。アップロードしないバイヤー(例: 測定パートナー)が変更せずにライブラリの中身を発見できる。 * **Brand agent(権利)**: `custom:rights_viewer` — `allowed_tasks: [get_rights, get_brand_identity]`、`read_only: true`。クリアランス権限なしのディスカバリー。 上記の存在/不在セマンティクス、アイデンティティバインディング、リフレッシュケイデンス、一貫性要件は、すべてのベンダープロトコルにわたって一律に適用されます — `custom:activation_only` を設定する signals エージェントは、`attestation_verifier` を設定する media-buy セラーと同じ 300 秒リフレッシュ義務を負います。 ### 先行技術 イントロスペクションモデル — 「呼び出し元が認可を強制する当事者に付与が何かを尋ねる」— は、AdCP のタスク・フィールド認可モデルに特化した [RFC 7662 OAuth 2.0 Token Introspection](https://www.rfc-editor.org/rfc/rfc7662) と構造的に類似しています。レスポンスを別個のタスクに分割するのではなく sync/list に埋め込むことは、アカウントディスカバリーとスコープイントロスペクションが同じ自然な問い(「私のアカウントは何で、それで何ができるか?」)であることを反映します — 2 つは一緒に返されます。 ## トランザクションライフサイクル ``` 1. セラーケイパビリティを探索 get_adcp_capabilities → require_operator_auth, supported_billing 2. ブランドアイデンティティを解決 brand.domain/.well-known/brand.json を取得 → 正規ブランド (domain, brand_id) 3. オペレーターアイデンティティを検証 brand.json の authorized_operators を確認 → このブランドのオペレーターが許可されているか確認 4. 認証(必要な場合) require_operator_auth が true の場合 → authorization_endpoint またはアウトオブバンドでオペレーター資格情報を取得 5. アカウント参照を確立 アカウント ID 名前空間 (require_operator_auth: true): list_accounts() → このブランド/オペレーターの既存 account_id を検索(上流管理) または account_id をアウトオブバンドで受領(セラー定義) バイヤー宣言 (require_operator_auth: false): sync_accounts({ accounts: [{ brand, operator, billing }] }) → status, billing terms 6. 実行 プロトコルタスクがアカウント参照を使用して正しいレートと条件を適用 例: get_products(account: {...}), create_media_buy(account: {...}) 7. 利用を報告 report_usage(usage: [{ account: {...}, operator_id, kind, vendor_cost, ... }]) 配信後にベンダーエージェントへサービス消費状況を通知 ``` ## プリンシパル アカウントプロトコルは4種類のプリンシパルで動作します。請求階層、信頼モデル、認可オペレーターの詳細は [アカウントとエージェント](/docs/building/by-layer/L2/accounts-and-agents) を参照してください。 | プリンシパル | 役割 | 識別子 | | ---------- | ---------------- | ----------------------------------------------------- | | ブランド | 誰の製品を広告するか | `brand.domain` + brand.json 経由のオプション `brand.brand_id` | | オペレーター | 誰が購入を行うか | ドメイン (例: `pinnacle-media.com`) | | エージェント | どのソフトウェアが購入するか | 認証済みセッション | | ベンダーエージェント | セラーの AdCP エージェント | `agent_url` | ## タスク | タスク | 目的 | | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------- | | [`sync_accounts`](/docs/accounts/tasks/sync_accounts) | ブランド/オペレーターのペアと請求を宣言してアカウントをプロビジョニング(バイヤー宣言アカウント、`require_operator_auth: false`) | | [`list_accounts`](/docs/accounts/tasks/list_accounts) | 既存アカウントを探索(アカウント ID 名前空間、`require_operator_auth: true`); 保留中アカウントのステータスをポーリング | | [`sync_governance`](/docs/accounts/tasks/sync_governance) | セラー側検証のためガバナンスエージェントエンドポイントをアカウントに同期 | | [`get_account_financials`](/docs/accounts/tasks/get_account_financials) | オペレーター請求アカウントの支出、クレジット、請求書ステータスを照会 | | [`report_usage`](/docs/accounts/tasks/report_usage) | 配信後にベンダーエージェントへサービス消費状況を通知 | ## ブランドレジストリとの接続 アカウント参照の `brand.domain` は任意の識別子ではありません — ブランドのドメインであり、ブランドの正規アイデンティティ、サブブランド、認可オペレーター、プロパティを宣言する `brand.json` ファイルに解決可能です。 ベンダーエージェントはブランドレジストリに対してバイヤーの主張を検証できます: オーケストレーターが `acme-corp.com` を代表すると主張した場合、ベンダーは `acme-corp.com/.well-known/brand.json` を取得して認可オペレーターとブランド階層を確認できます。これによりアカウントプロトコルは改ざん耐性を持ちます — アカウント関係は公開検証可能なブランドアイデンティティに基づきます。 ブランドアイデンティティの解決方法については [ブランドプロトコル](/docs/brand-protocol/index) を参照してください。 ## 取引相手の検証 広告のすべての商取引関係は、実際に取引している相手が誰であるかを知ることに依存します。アカウントプロトコルはブランドレジストリを通じてプロトコルレベルでこれに対応します。 オーケストレーターがアカウントを参照する際、`brand.domain` が広告主を識別します。ベンダーエージェントは `brand.domain/.well-known/brand.json` を取得して以下を検証できます。 * **ブランドアイデンティティ**: このブランドは主張通りか? * **オペレーター認可**: リクエストのオペレーターはこのブランドの代理購入が許可されているか? * **ブランド階層**: このハウスポートフォリオにはどのサブブランドが含まれるか? この検証は公開アクセス可能な DNS ホスト型アイデンティティに基づきます — バイヤーエージェントが主張する内容ではなく、ブランド自身が宣言した内容によります。 `pending_approval` アカウント状態では人間によるレビューが行われます: 与信審査、法的合意、本人確認。これらのステップが必要なベンダーエージェントは人間がプロセスを完了するための `setup.url` を返します。アカウントがアクティブになる前に完了が必要です。 ### ブランドレジストリとコントリビュートバックパターン [AgenticAdvertising.org ブランドレジストリ](https://agenticadvertising.org) は、独自の `brand.json` を公開していないブランドに対してコミュニティが維持するブランドアイデンティティレイヤーを提供します。アカウント設定前にブランドを解決するバイヤーエージェントは、通常のワークフローの副産物としてレジストリにデータをコントリビュートできます — 追加作業なしにエコシステムのアイデンティティカバレッジを向上させます。 バイヤーエージェントに推奨されるパターンは3つのビルディングブロックを使用します([#1166](https://github.com/adcontextprotocol/adcp/issues/1166) 参照)。 | ツール | 目的 | | ---------------- | ------------------------------------------------ | | `resolve_brand` | レジストリを確認し brand.json を取得 — 利用可能な場合は正規アイデンティティを返す | | `research_brand` | Brandfetch 経由でエンリッチし `enriched` としてレジストリに自動保存 | | `save_brand` | ブランドを `community` としてレジストリに手動コントリビュート | ```javascript theme={null} async function ensureBrand(domain) { // 1. レジストリを確認(brand.json または以前に解決済み) const resolved = await resolveBrand(domain); if (resolved.errors) { // 解決失敗 — ブランド不明、エンリッチへ進む } else if (resolved.source === 'brand_json' || resolved.source === 'enriched') { // 権威ある、またはエンリッチされたデータが利用可能 — ユーザーに確認してから使用 return await confirmWithUser(resolved); } // source === 'community': レジストリにプレースホルダーあり、より豊富なデータのためエンリッチ // 2. Brandfetch 経由でエンリッチ — 'enriched' としてレジストリに自動保存 const enriched = await researchBrand(domain); if (enriched.errors) { // エンリッチメント利用不可 — コミュニティエントリにフォールバック、またはユーザーに修正を促す return resolved ? await confirmWithUser(resolved) : null; } // 3. エンリッチデータを使用する前にユーザーに確認 // エンリッチメントはサードパーティ — ユーザー確認でエラーを検知し、レジストリの品質を向上 return await confirmWithUser(enriched); } ``` `confirmWithUser` はUXに合った確認メカニズムのプレースホルダーです — 明示的なプロンプト、ワークフローUIのレビューステップ、または人間のレビューをトリガーする低信頼フラグ。確認ステップが改善ループを機能させます: エンリッチメントデータはサードパーティから来るため正確さは保証されません。本番キャンペーンで使用される前のユーザー検証がレジストリの精度を保ちます。 #### ソース権威 レジストリはブランドデータがどこから来たかを追跡します。権威の降順でソースを示します。 | ソース | 意味 | 上書き可能? | | ------------ | --------------------------------------- | ------------ | | `brand_json` | ブランドが `/.well-known/brand.json` 経由で自己宣言 | 不可 — 409 を返す | | `enriched` | サードパーティエンリッチメント(Brandfetch) | より高い権威のみ | | `community` | レジストリメンバーが手動コントリビュート | 可 | エージェントが `save_brand` または `research_brand` を呼び出すと、レジストリはマージロジックを適用します: より高い権威ソースの既存フィールドは保持され、欠落フィールドのみが補完されます。ブランドが宣言した内容を尊重しながらギャップを埋めます。 `research_brand` は最近の `enriched` データがドメインのレジストリに既に存在する場合、再エンリッチをスキップして冗長な API 呼び出しを避けます。 ブランドの完全な編集履歴(誰が、いつ、どのような概要でコントリビュートしたか)は [`GET /api/brands/history`](/docs/registry/index#activity-history) で照会できます。 #### プロパティコントリビュートバック 同じパターンがパブリッシャープロパティにも適用されます。バイヤーエージェントがセールスエージェントとのやり取りを通じて新しいパブリッシャーを発見した場合、`POST /api/properties/save` 経由でそのプロパティをレジストリにコントリビュートできます。これはブランドコントリビュートバックがブランドカバレッジを向上させるのと同様に、プロパティカバレッジを向上させます。詳細は [レジストリ API — プロパティを保存](/docs/registry/index#save-property) を参照してください。 ## 利用報告 ベンダーエージェント(シグナル、ガバナンス、クリエイティブ)はキャンペーン実行の直接参加者ではなく、オーケストレーターがメディアバイへの入力としてサービスを使用します。配信後、`report_usage` はこれらのベンダーに消費状況を伝え、獲得収益を追跡し請求を検証できるようにします。 `report_usage` はバイヤー報告です: オーケストレーターが消費を計算して報告します。各レコードは独自の `account`、`operator_id`、`kind`(`"signal"`、`"content_standards"`、`"creative"`)を持ちます。ベンダーエージェントは報告された `pricing_option_id` を使用して正しいレートが適用されたことを検証します。 部分的な受け入れは有効です — 単一のリクエストが複数のアカウント、オペレーター、キャンペーンにまたがることができます。レスポンスは受け入れられたレコード数と(もしあれば)失敗したレコードを確認します。 # get_account_financials Source: https://adcp-docs-ja.pier1.co.jp/docs/accounts/tasks/get_account_financials get_account_financials はオペレーター請求 AdCP アカウントの支出サマリー、クレジット残高、支払いステータス、請求書履歴を返します。account_financials ケイパビリティが必要。 オペレーター請求アカウントの財務ステータスを照会する — 支出サマリー、クレジットまたはプリペイ残高、支払いステータス、請求書履歴。予算を意識したエージェントがプロトコルを離れることなく支出判断に必要なコンテキストを得られます。 `get_account_financials` はセラーが [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) で `account_financials: true` を宣言している場合のみ利用可能。**オペレーター請求アカウント**にのみ適用されます。エージェント請求アカウントの場合、エージェント自身の課金システムが信頼できる情報源となります。 **応答時間**: 約1秒。 **リクエストスキーマ**: [`/schemas/latest/account/get-account-financials-request.json`](https://adcontextprotocol.org/schemas/latest/account/get-account-financials-request.json) **レスポンススキーマ**: [`/schemas/latest/account/get-account-financials-response.json`](https://adcontextprotocol.org/schemas/latest/account/get-account-financials-response.json) ## クイックスタート キャンペーン開始前に残りのクレジットを確認します。 ```javascript JavaScript theme={null} import { testAgent } from "@adcp/client/testing"; const result = await testAgent.getAccountFinancials({ account: { account_id: "acc_acme_001" }, }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } if ("errors" in result.data && result.data.errors) { throw new Error(`Operation failed: ${JSON.stringify(result.data.errors)}`); } const { spend, credit, payment_status } = result.data; console.log(`Spent: $${spend?.total_spend} this period`); if (credit) { console.log(`Available credit: $${credit.available_credit} of $${credit.credit_limit}`); } if (payment_status === "past_due") { console.log("Warning: payment is past due — campaigns may be paused"); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.get_account_financials( account={"account_id": "acc_acme_001"}, ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") print(f"Spent: ${result.spend.total_spend} this period") if hasattr(result, 'credit') and result.credit: print(f"Available credit: ${result.credit.available_credit} of ${result.credit.credit_limit}") if getattr(result, 'payment_status', None) == 'past_due': print("Warning: payment is past due — campaigns may be paused") asyncio.run(main()) ``` ## リクエストパラメーター | パラメーター | 型 | 必須 | 説明 | | --------- | ------ | --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `account` | object | Yes | [アカウント参照](/docs/building/by-layer/L2/accounts-and-agents#account-references) — `account_id` または自然キー(`brand` + `operator` + オプションの `sandbox`)。オペレーター請求アカウントである必要があります。 | | `period` | object | No | 支出サマリーの日付範囲: ISO 8601 日付形式の `start` と `end`。省略時は現在の請求サイクルがデフォルト。 | ## レスポンス **成功レスポンス:** アカウントの財務データを返します。保証されているのは `account`、`currency`、`period`、`timezone` のみ — その他はセラーが公開する内容による。 | フィールド | 説明 | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------- | | `account` | リクエストからエコーされるアカウント参照。 | | `currency` | すべての金額の ISO 4217 通貨コード。 | | `period` | 実際にカバーされる期間(`start`、`end`)。請求サイクルの境界に合わせて調整される場合があります。 | | `timezone` | セラーの請求日境界の IANA タイムゾーン(例: `America/New_York`)。レスポンスのすべての日付はこのタイムゾーンのカレンダー日付。 | | `spend` | 支出サマリー: `total_spend`(`currency` 単位の金額)とオプションの `media_buy_count`。 | | `credit` | クレジットベースアカウントに存在。`credit_limit`、`available_credit`、オプションの `utilization_percent`(0-100)を含みます。 | | `balance` | プリペイアカウントに存在。`available` 残高とオプションの `last_top_up`(`amount`、`date`)を含みます。 | | `payment_status` | `current`、`past_due`、`suspended`。 | | `payment_terms` | 有効な支払い条件: `net_15`、`net_30`、`net_45`、`net_60`、`net_90`、`prepay`。 | | `invoices` | 最近の請求書の配列: `invoice_id`、`amount`、`status`(`draft`、`issued`、`paid`、`past_due`、`void`)、オプションの `period`、`due_date`、`paid_date`。 | **エラーレスポンス:** * `errors` -- 操作レベルのエラーの配列。財務データは含まれない。 **注:** レスポンスは判別共用体を使用 -- 財務データまたは `errors` のいずれか一方のみ、両方は含まれない。 ## 一般的なシナリオ ### キャンペーン開始前の予算確認 ```javascript JavaScript theme={null} import { testAgent } from "@adcp/client/testing"; const financials = await testAgent.getAccountFinancials({ account: { account_id: "acc_acme_001" }, }); if (!financials.success || "errors" in financials.data) { throw new Error("Could not check financials"); } const campaignBudget = 15000; const { credit } = financials.data; if (credit && credit.available_credit < campaignBudget) { console.log( `Insufficient credit: $${credit.available_credit} available, ` + `$${campaignBudget} needed. ` + `Credit utilization: ${credit.utilization_percent}%` ); } else { console.log("Budget check passed — proceeding with campaign"); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): financials = await test_agent.simple.get_account_financials( account={"account_id": "acc_acme_001"}, ) if hasattr(financials, 'errors') and financials.errors: raise Exception("Could not check financials") campaign_budget = 15000 credit = getattr(financials, 'credit', None) if credit and credit.available_credit < campaign_budget: print( f"Insufficient credit: ${credit.available_credit} available, " f"${campaign_budget} needed. " f"Credit utilization: {credit.utilization_percent}%" ) else: print("Budget check passed — proceeding with campaign") asyncio.run(main()) ``` ### プリペイ残高モニタリング ```javascript JavaScript theme={null} import { testAgent } from "@adcp/client/testing"; const financials = await testAgent.getAccountFinancials({ account: { brand: { domain: "acme-corp.com" }, operator: "acme-corp.com", }, }); if (!financials.success || "errors" in financials.data) { throw new Error("Could not check financials"); } const { balance } = financials.data; if (balance && balance.available < 2000) { console.log( `Low balance warning: $${balance.available} remaining. ` + `Consider topping up before launching new campaigns.` ); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): financials = await test_agent.simple.get_account_financials( account={ "brand": {"domain": "acme-corp.com"}, "operator": "acme-corp.com", }, ) if hasattr(financials, 'errors') and financials.errors: raise Exception("Could not check financials") balance = getattr(financials, 'balance', None) if balance and balance.available < 2000: print( f"Low balance warning: ${balance.available} remaining. " f"Consider topping up before launching new campaigns." ) asyncio.run(main()) ``` ## エラーハンドリング | エラーコード | 説明 | 解決策 | | --------------------- | ----------------------------------------- | --------------------------------------------- | | `UNSUPPORTED_FEATURE` | アカウントがエージェント請求を使用している — セラーから財務データは取得できない | エージェント請求アカウントには自身の課金システムを照会 | | `UNSUPPORTED_FEATURE` | セラーがこのアカウントまたは期間の財務データを持っていない | `account_financials` ケイパビリティが `true` であることを確認 | | `ACCOUNT_NOT_FOUND` | アカウントが存在しないか、アクセスできない | アカウント参照を確認するか、再同期する | ## 次のステップ * [list\_accounts](/docs/accounts/tasks/list_accounts) -- アカウントを探索してステータスを確認します * [get\_adcp\_capabilities](/docs/protocol/get_adcp_capabilities) -- このタスクを呼び出す前に `account_financials` を確認します * [アカウントとエージェント](/docs/building/by-layer/L2/accounts-and-agents) -- 請求モデルとアカウント参照 # list_accounts Source: https://adcp-docs-ja.pier1.co.jp/docs/accounts/tasks/list_accounts list_accounts は認証済みエージェントが操作できるすべての広告主アカウントを AdCP ベンダーエージェントから返します。メディアバイ、シグナル、ガバナンス、クリエイティブの各プロトコルで機能します。 認証済みエージェントがこのベンダーエージェントで操作できるすべてのアカウントを返します。既存アカウントの探索、保留中アカウントのステータス変更確認、プロトコル操作で使用する `account_id` 値の取得に使用します。 上流管理のアカウント名前空間では、`list_accounts` は任意のディスカバリーの飾りではなく、名前空間ディスカバリー契約です。上流プラットフォームがアクセス可能なアカウントセットを所有するため、バイヤーは最初のアカウントスコープリクエストの前に明示的な `account_id` を解決しなければなりません(MUST)。認証済みクレデンシャルが複数のアカウントにアクセスできる場合、セラーは `list_accounts` を公開しなければなりません(MUST)。正確に 1 つのアカウントにアクセスできる場合、SDK が自動選択して必須アカウント呼び出しで `{ "account_id": "..." }` を送れるよう、セラーはそのシングルトンを返す `list_accounts` を公開すべきです(SHOULD)。`sync_accounts` プロビジョニングは、将来の明示的なケイパビリティがそのモードを宣言しない限り、3.0.x でアカウント ID アカウントを作成しません。今日これらのセラーで `sync_accounts` が公開されている場合、それは `account_id` ですでに識別されたアカウントに対する設定更新にのみ使用します。 `list_accounts` はすべてのベンダープロトコルで機能する — メディアバイエージェント、シグナルエージェント、ガバナンスエージェント、クリエイティブエージェントはすべてこの同じタスクを通じてアカウントを返します。 **応答時間**: 約1秒。 **リクエストスキーマ**: [`static/schemas/source/account/list-accounts-request.json`](https://github.com/adcontextprotocol/adcp/blob/main/static/schemas/source/account/list-accounts-request.json) **レスポンススキーマ**: [`/schemas/v3/account/list-accounts-response.json`](https://adcontextprotocol.org/schemas/v3/account/list-accounts-response.json) ## クイックスタート このエージェントが操作できるすべてのアカウントを一覧表示します。 ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { ListAccountsResponseSchema } from "@adcp/sdk"; const result = await testAgent.listAccounts({}); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = ListAccountsResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } for (const account of validated.accounts) { console.log(`${account.account_id}: ${account.name} (${account.status})`); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.list_accounts() if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for account in result.accounts: print(f"{account.account_id}: {account.name} ({account.status})") asyncio.run(main()) ``` ## リクエストパラメーター すべてのパラメーターはオプション。空のリクエストは、認証済み呼び出し元に見えるすべてのアカウントを返します。既知の 1 アカウントを `account_id` または自然キー(`brand` + `operator`、オプションで `sandbox`)で再読み取りするときは `account` を使います。 | パラメーター | 型 | 必須 | 説明 | | ------------ | ------- | -- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | `account` | object | No | 正確なアカウントフィルタ。ディスカバリー後に `{ "account_id": "..." }` を渡すか、バイヤー宣言アカウントセラーでは `brand`、`operator`、オプションの `sandbox` を持つ自然キーを渡します。セラーは認証済み呼び出し元に見える一致するアカウントのみを返します。 | | `status` | string | No | アカウントステータスでフィルタ: `active`、`pending_approval`、`rejected`、`payment_required`、`suspended`、`closed`。 | | `sandbox` | boolean | No | true の場合、サンドボックスアカウントのみ返します。false の場合、本番アカウントのみ返します。両方を返すには省略します。主にアカウント ID 名前空間で使用し、サンドボックスアカウントはプラットフォーム上の既存テストアカウントです。 | | `pagination` | object | No | 大規模アカウントセット用のページネーションカーソル。 | ## レスポンス | フィールド | 説明 | | ------------ | ---------------------------- | | `accounts` | アカウントオブジェクトの配列(下記参照) | | `errors` | リクエストが失敗した場合のエラー配列 | | `pagination` | さらに結果がある場合の次ページのページネーションカーソル | **各アカウントには以下が含まれます:** | フィールド | 説明 | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `account_id` | ベンダーエージェントの識別子。プロトコルタスクに渡す: `create_media_buy`、`get_signals`、`activate_signal`、`report_usage` などの操作。`status: "rejected"` の場合は省略されることがあります。 | | `name` | アカウントのベンダーエージェント表示名 | | `brand` | ブランド参照オブジェクト: `domain`([ブランドレジストリ](/docs/brand-protocol/brand-json)のハウスドメイン)とオプションの `brand_id`(ハウス内のサブブランド) | | `operator` | オペレータードメイン。常に存在 — ブランドが直接運営する場合、`operator` はブランドのドメインと同一。 | | `status` | 現在のアカウント状態: `active`、`pending_approval`、`rejected`、`payment_required`、`suspended`、`closed` | | `billing` | 有効な請求モデル: `operator` または `agent` | | `account_scope` | セラーがアカウントをスコープした方法: `operator`、`brand`、`operator_brand`、`agent`。[アカウントスコープ](/docs/building/by-layer/L2/accounts-and-agents#account-scope) を参照。 | | `payment_terms` | このアカウントで合意した支払い条件: `net_15`、`net_30`、`net_45`、`net_60`、`net_90`、`prepay`。アカウントがアクティブな場合、すべての請求書に対して拘束力を持ちます。 | | `governance_agents` | このアカウントに登録されたガバナンスエージェントのエンドポイント。[`sync_governance`](/docs/accounts/tasks/sync_governance) でガバナンスエージェントが設定されている場合に存在。 | | `setup` | `status: "pending_approval"` の場合に存在。セットアップ完了の `url` と必要な内容を説明する `message` を含みます。 | | `authorization` | オプション。このアカウントに対する呼び出しエージェントのスコープ付与 — `allowed_tasks`、`field_scopes`、`scope_name`、`read_only`。すべてのベンダーエージェントタイプ(media-buy、signals、governance、creative、brand)に適用 — Accounts Protocol の面は共有。スコープイントロスペクションをサポートするベンダーエージェントはこれを設定すべき(SHOULD)。`attestation_verifier` 標準スコープを主張する media-buy sales agent は設定しなければならない(MUST)。不在は、ベンダーエージェントがこのアカウントについてイントロスペクション可能なスコープを宣伝しないことを意味する。呼び出し元は不在からアクセスを推論してはならず(MUST NOT)、RBAC エラーコードによるエラー駆動ディスカバリーにフォールバックする。完全な形状とセマンティクスは [Caller authorization](/docs/accounts/overview#caller-authorization) を参照。 | | `notification_configs` | [`sync_accounts`](/docs/accounts/tasks/sync_accounts#account-level-webhook-subscriptions) で登録されたアカウントレベルの Webhook サブスクライバー。各エントリは `subscriber_id`、`url`、`event_types[]`、`active` を運ぶ。アカウントに永続化されたサブスクライバーがある場合に存在。`subscriber_id` はアカウントスコープの論理キー。同じサブスクライバーを再登録するとそのサブスクライバーの設定を置き換える。`authentication.credentials` はすべてのエントリで省略(書き込み専用)。この面を使って、sync 後に何がアクティブかを検証し、複数サブスクライバーにわたるファンアウトを監査し、バイヤー側の期待とセラー側の永続状態のドリフトを検出する。これはアカウントライフサイクルフィードではない。アカウントステータスの変更はアカウントの `status` フィールドまたはワンショットの `sync_accounts.push_notification_config` 非同期結果チャネルから読む。 | ### 単一パブリッシャーのカーディナリティ 正確に 1 つのパブリッシャーエンティティを提供するセラーは、呼び出しプリンシパルに関わらず、そのエンティティを `list_accounts` レスポンスの唯一のアカウントとして返してもよい(MAY)。[`list-accounts-response.json`](https://github.com/adcontextprotocol/adcp/blob/main/static/schemas/source/account/list-accounts-response.json) の *"Direct advertiser with single account"* の例がこのケースの正準です — `pagination` エンベロープを全く持たない単一要素の `accounts[]`。 `pagination.has_more: true` を要求するページネーションコンフォーマンスは、次の場合には適用されません: * `pagination` が完全に不在(正準の単一アカウント形状)、または * `pagination.total_count` が存在し ≤ 1 ランナーはどちらの場合もページネーションウォークフェーズを `not_applicable` として採点すべきです(SHOULD)。このパターンはコンフォーマントです。スペックは `accounts[]` に `minItems` 制約を持たず、単一アカウントの例は規範的です。 ## 一般的なシナリオ ### アカウントがアクティブになるまでポーリング `sync_accounts` が `pending_approval` を返した後、アカウントが準備できるまでポーリングします。 ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { ListAccountsResponseSchema } from "@adcp/sdk"; async function waitForAccount(targetAccountId, maxAttempts = 20) { for (let i = 0; i < maxAttempts; i++) { const result = await testAgent.listAccounts({ status: "active" }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = ListAccountsResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("accounts" in validated) { const account = validated.accounts.find(a => a.account_id === targetAccountId); if (account) { console.log(`Account active: ${account.account_id}`); return account; } } // 再ポーリングまで30秒待機 await new Promise(resolve => setTimeout(resolve, 30_000)); } throw new Error(`Account ${targetAccountId} did not become active`); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def wait_for_account(target_account_id: str, max_attempts: int = 20): for _ in range(max_attempts): result = await test_agent.simple.list_accounts(status='active') if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") account = next( (a for a in result.accounts if a.account_id == target_account_id), None ) if account: print(f"Account active: {account.account_id}") return account await asyncio.sleep(30) raise Exception(f"Account {target_account_id} did not become active") ``` ### アクティブなアカウントのみフィルタ ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { ListAccountsResponseSchema } from "@adcp/sdk"; const result = await testAgent.listAccounts({ status: "active" }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = ListAccountsResponseSchema.parse(result.data); if ("accounts" in validated) { for (const account of validated.accounts) { console.log(`${account.account_id}: ${account.name} — billing: ${account.billing}`); } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.list_accounts(status='active') if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for account in result.accounts: print(f"{account.account_id}: {account.name} — billing: {account.billing}") asyncio.run(main()) ``` ## エラーハンドリング | エラーコード | 説明 | 解決策 | | ------------------- | --------------------- | --------------------------------- | | `ACCOUNT_NOT_FOUND` | このエージェントのアカウントが見つからない | まず `sync_accounts` を実行して購買関係を確立する | ## 次のステップ * [sync\_accounts](/docs/accounts/tasks/sync_accounts) — セラーと広告主アカウントを同期します * [sync\_governance](/docs/accounts/tasks/sync_governance) — ガバナンスエージェントをアカウントに同期します * [アカウントとエージェント](/docs/building/by-layer/L2/accounts-and-agents) — 請求モデル、信頼モデル、認可オペレーター * [ブランドプロトコル](/docs/brand-protocol/brand-json) — ベンダーエージェントがブランドの `domain` からブランドアイデンティティを解決する方法 # report_usage Source: https://adcp-docs-ja.pier1.co.jp/docs/accounts/tasks/report_usage report_usage はキャンペーン配信後に消費データを AdCP ベンダーエージェントに送信する — 配信されたインプレッション、照会されたシグナル、実行されたガバナンスチェック — ベンダーが収益を追跡し請求を検証できるよう。 キャンペーン配信後のベンダーサービスの消費状況を報告します。オーケストレーターがベンダーエージェント(シグナル、ガバナンス、クリエイティブ)に使用状況を通知し、ベンダーが獲得収益を追跡して請求を検証できるようにするために呼び出す。 各利用レコードは自己完結型 — 独自の `account` と `media_buy_id` を持ちます。単一のリクエストが複数のアカウントとキャンペーンにまたがることができます。 **応答時間**: 約1秒。 **リクエストスキーマ**: [`/schemas/v3/account/report-usage-request.json`](https://adcontextprotocol.org/schemas/v3/account/report-usage-request.json) **レスポンススキーマ**: [`/schemas/v3/account/report-usage-response.json`](https://adcontextprotocol.org/schemas/v3/account/report-usage-response.json) ## リクエストパラメーター | パラメーター | 型 | 必須 | 説明 | | ------------------ | -------------- | --- | ---------------------------------------------------------------------------------------------- | | `idempotency_key` | string | 推奨 | このリクエストのクライアント生成一意キー(UUID 推奨)。同じキーを持つリクエストが既に受け入れられている場合、サーバーは再処理せずに元のレスポンスを返します。再試行時の二重請求を防ぐ。 | | `reporting_period` | object | Yes | UTC の ISO 8601 日時形式の `start` と `end`。リクエスト内のすべてのレコードに適用されます。 | | `usage` | UsageRecord\[] | Yes | 1つ以上の利用レコード。 | ### 利用レコードのフィールド 各レコードには `account`、`vendor_cost`、`currency` が必要。追加フィールドはベンダータイプによる。 | フィールド | 型 | 必須 | 説明 | | ------------------------- | ------------------------------------------------------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `account` | [AccountRef](/docs/building/by-layer/L2/accounts-and-agents#account-references) | Yes | このレコードのアカウント — `account_id` または `{ brand, operator }` で指定。 | | `vendor_cost` | number | Yes | このレコードでベンダーに支払う金額(`currency` 単位) | | `currency` | string | Yes | ISO 4217 通貨コード | | `pricing_option_id` | string | ベンダー: Yes | ベンダーのディスカバリーレスポンス(`get_signals`、`list_creatives`、`list_content_standards`、`list_property_lists`)または実行レスポンス(`build_creative`)からの料金オプション。ベンダーはこれを使用して正しいレートが適用されたことを検証します。 | | `impressions` | number | シグナル: Yes | 配信されたインプレッション数 | | `media_spend` | number | percent\_of\_media: Yes | パーセント・オブ・メディアコスト検証用のメディア支出 | | `signal_agent_segment_id` | string | シグナル: Yes | `get_signals` からのシグナル識別子 | | `creative_id` | string | クリエイティブ: Yes | `build_creative` または `list_creatives` からのクリエイティブ識別子。請求検証のため利用を特定のクリエイティブにリンクします。`build_creative` のバリアントリーフは、トラフィックされた/ライブラリに追加されたときのみ `creative_id` を得ます — 破棄された best-of-N やファンアウトバリアントはここで報告されません。それらの課金は `build_creative` レスポンス上のインラインのリーフごと `vendor_cost` です(トラフィックされないリーフの権威的レコード)。 | | `build_variant_id` | string | No | 報告される `creative_id` が特定の `build_creative` バリアントリーフから昇格したがそのソース ID と異なる場合、リコンシリエーションのためソースの `build_variant_id` を運びます。`creative_id` が保持された `build_variant_id` である正準パスでは、このフィールドを省略します。 | | `property_list_id` | string | プロパティリスト: Yes | `list_property_lists` からのプロパティリスト識別子。請求検証のため利用を特定のプロパティリストにリンクします。 | ## レスポンス | フィールド | 説明 | | ---------- | ------------------------------------------------------------ | | `accepted` | 正常に保存された利用レコード数 | | `errors` | 個々のレコードのバリデーションエラー。部分的な受け入れは有効 — 一部が失敗しても受け入れられたレコードは保存されます。 | ## 例 ### シグナル利用 — 単一キャンペーン ```json リクエスト theme={null} { "idempotency_key": "550e8400-e29b-41d4-a716-446655440000", "reporting_period": { "start": "2025-03-01T00:00:00Z", "end": "2025-03-31T23:59:59Z" }, "usage": [ { "account": { "account_id": "acct_pinnacle_signals" }, "signal_agent_segment_id": "luxury_auto_intenders", "pricing_option_id": "po_lux_auto_cpm", "impressions": 4200000, "media_spend": 21000.00, "vendor_cost": 2100.00, "currency": "USD" } ] } ``` ```json レスポンス theme={null} { "accepted": 1 } ``` ### クリエイティブ利用 — CPM 課金のアドサーバー ```json リクエスト theme={null} { "idempotency_key": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "reporting_period": { "start": "2026-03-01T00:00:00Z", "end": "2026-03-31T23:59:59Z" }, "usage": [ { "account": { "account_id": "acct_acme_creative" }, "creative_id": "cr_88201", "pricing_option_id": "po_video_cpm", "impressions": 2400000, "vendor_cost": 1200.00, "currency": "USD" } ] } ``` ```json レスポンス theme={null} { "accepted": 1 } ``` ### マルチアカウントバッチ 2つのアカウントにまたがる2つのキャンペーンの単一リクエスト: ```json theme={null} { "idempotency_key": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "reporting_period": { "start": "2025-03-01T00:00:00Z", "end": "2025-03-31T23:59:59Z" }, "usage": [ { "account": { "account_id": "acct_pinnacle_signals" }, "signal_agent_segment_id": "luxury_auto_intenders", "pricing_option_id": "po_lux_auto_cpm", "impressions": 2100000, "vendor_cost": 1050.00, "currency": "USD" }, { "account": { "account_id": "acct_nova" }, "signal_agent_segment_id": "eco_conscious_shoppers", "pricing_option_id": "po_eco_cpm", "impressions": 800000, "vendor_cost": 400.00, "currency": "USD" } ] } ``` ### 部分的な受け入れ 一部のレコードがバリデーションに失敗した場合、レスポンスは受け入れられた数を示します: ```json theme={null} { "accepted": 1, "errors": [ { "code": "INVALID_PRICING_OPTION", "message": "pricing_option_id 'po_unknown' does not exist on this account", "field": "usage[1].pricing_option_id" } ] } ``` ## 再試行の安全性 本番利用では必ず `idempotency_key` を含めます。リクエストがタイムアウトまたはネットワークエラーを返した場合、同じキーで再試行する — サーバーは二重計上せずに元の結果を返します。 リクエストごとに新しい UUID を生成する(利用レコードごとではない)。同じ期間に追加レコードを報告する必要がある場合は、新しいキーで新しいリクエストを送信します。 ## 報告ケイデンス 定期的に報告する — 最低でも月次。支出が大きいキャンペーンには週次報告により、ベンダーエージェントに獲得収益のタイムリーな可視性を提供します。 最終期間をクローズするため、キャンペーン完了時に報告します。 ## エラーハンドリング | エラーコード | 説明 | 解決策 | | ------------------------ | ----------------------------------------- | ------------------------------------------------ | | `ACCOUNT_NOT_FOUND` | 利用レコードのアカウント参照が見つからないか、アクセスできない | `list_accounts` で確認; 必要に応じて `sync_accounts` を再実行 | | `INVALID_USAGE_DATA` | 利用レコードに欠落または無効なフィールドがある | ベンダータイプの必須フィールドを確認 | | `INVALID_PRICING_OPTION` | `pricing_option_id` がこのアカウントに見つからない | ベンダーの探索レスポンスから `pricing_option_id` を確認 | | `DUPLICATE_REQUEST` | この `idempotency_key` を持つリクエストが既に受け入れられている | 無視して安全 — 元のレスポンスが変更なく返される | ## 次のステップ * [sync\_accounts](/docs/accounts/tasks/sync_accounts) — 報告前にセラーと広告主アカウントを同期します * [アカウントプロトコル](/docs/accounts/overview) — アカウント確立と精算の全体像 * [アカウントとエージェント](/docs/building/by-layer/L2/accounts-and-agents) — 請求階層とオペレーターモデル # sync_accounts Source: https://adcp-docs-ja.pier1.co.jp/docs/accounts/tasks/sync_accounts sync_accounts は、バイヤー宣言アカウントをプロビジョニングするか、AdCP セラーエージェントで既存アカウントの設定を更新します。 1つ以上のブランド/オペレーターのペアに対してセラーと広告主アカウントを同期するか、セラーがそのモードを公開する場合は既存アカウントの設定を更新します。ブランドは `domain` + オプションの `brand_id` を含む `brand` オブジェクトで識別され、`/.well-known/brand.json` 経由で解決されます。 `sync_accounts` はすべてのセラープロトコルで使用されます: メディアバイエージェント、シグナルエージェント、ガバナンスエージェント、クリエイティブエージェント。プロビジョニングモードでは、バイヤーの意図を宣言し、セラーが内部でアカウントをプロビジョニングまたはリンクします。バイヤー宣言アカウント(`require_operator_auth: false`)にはプロビジョニングモードを使い、後続のリクエストには自然キー(`brand` + `operator`)を使用します。セラーは内部ハンドルとして `account_id` をエコーしてもよい(MAY)が、この方法でプロビジョニングされたアカウントについて自然キーの `AccountRef` を受け入れ続けなければなりません(MUST)。アカウント ID 名前空間では、セラーが割り当てたアカウント ID を [`list_accounts`](/docs/accounts/tasks/list_accounts) またはアウトオブバンドのオンボーディングで探索します。アカウント ID 名前空間の `sync_accounts` プロビジョニングは、将来の明示的なケイパビリティがそのモードを宣言しない限りスコープ外です。そのようなセラーが今日 `sync_accounts` を公開する場合、`account_id` でキーされる設定更新モードにのみ使用します。 **応答時間**: 約1秒。アカウントプロビジョニングは同期的; 与信審査や法的レビューには人間の対応が必要な場合がある(`setup.url` 付きの `status: "pending_approval"` で示されます)。 **リクエストスキーマ**: [`/schemas/v3/account/sync-accounts-request.json`](https://adcontextprotocol.org/schemas/v3/account/sync-accounts-request.json) **レスポンススキーマ**: [`/schemas/v3/account/sync-accounts-response.json`](https://adcontextprotocol.org/schemas/v3/account/sync-accounts-response.json) ## クイックスタート 単一の広告主アカウントを同期して結果のステータスを確認します。 ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncAccountsResponseSchema } from "@adcp/sdk"; const result = await testAgent.syncAccounts({ accounts: [ { brand: { domain: "acme-corp.com" }, operator: "acme-corp.com", billing: "operator", }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncAccountsResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } for (const account of validated.accounts) { console.log(`${account.brand.domain}: ${account.status}`); if (account.status === "pending_approval" && account.setup?.url) { console.log(` Complete setup at: ${account.setup.url}`); } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.sync_accounts( accounts=[ { "brand": {"domain": "acme-corp.com"}, "operator": "acme-corp.com", "billing": "operator", }, ], ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for account in result.accounts: print(f"{account.brand['domain']}: {account.status}") if account.status == 'pending_approval' and hasattr(account, 'setup') and account.setup: print(f" Complete setup at: {account.setup.url}") asyncio.run(main()) ``` ## リクエストパラメーター | パラメーター | 型 | 必須 | 説明 | | -------------------------- | ------- | --- | ------------------------------------------------------------------------------------------- | | `accounts` | array | Yes | 同期するアカウントエントリの配列(下記参照)。 | | `delete_missing` | boolean | No | true の場合、このエージェントが以前に同期したがこのリクエストに含まれないアカウントを非アクティブ化します。認証済みエージェントにスコープされます。デフォルト: `false`。 | | `dry_run` | boolean | No | true の場合、変更を適用せずにプレビューします。デフォルト: `false`。 | | `push_notification_config` | object | No | アカウントステータス変更時の非同期通知用 Webhook(例: `pending_approval` から `active` への遷移)。 | **アカウントエントリのフィールド:** | フィールド | 型 | 必須 | 説明 | | ---------------------- | ------- | --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `brand` | object | Yes | 広告主を識別するブランド参照。`domain`(brand.json がホストされているハウスドメイン)とオプションの `brand_id`(マルチブランドハウス用)を含みます。[brand-ref](/docs/brand-protocol/brand-json) を参照。 | | `operator` | string | Yes | ブランドの代理で活動するエンティティのドメイン(例: `pinnacle-media.com`)。ブランドが直接運営する場合はブランドのドメインに設定。brand.json の `authorized_operators` に対して検証されます。 | | `billing` | string | Yes | 請求先: `operator`、`agent`、`advertiser`。セラーがケイパビリティレベルで受け入れる内容を確認するには `get_adcp_capabilities` の `supported_billing` を確認します。セラーはこの請求モデルを受け入れるかリクエストを拒否しなければなりません。セラーは、セラー全体のケイパビリティが受け入れる値でも、呼び出しバイヤーエージェントの商業関係が許可しない場合は追加で拒否してもよい(MAY)— 例: パススルー専用としてオンボードされたバイヤーエージェント(支払い関係なし — オペレーターのみが請求され得る)。2 つのゲートは別個のエラーコードを使います — セラー全体のケイパビリティゲートには `BILLING_NOT_SUPPORTED`、バイヤーエージェントごとのゲートには `BILLING_NOT_PERMITTED_FOR_AGENT` — ためエージェントはプロースを解析せずに自律リトライ vs 人間オンボーディングにディスパッチできます。[バイヤーエージェントのアイデンティティ](/docs/building/by-layer/L2/accounts-and-agents#buyer-agent-identity)と [Billing and Account Setup](/docs/building/by-layer/L3/error-handling#billing-and-account-setup) を参照。 | | `billing_entity` | object | No | 支払責任を負う当事者の構造化されたビジネスエンティティ詳細。`legal_name`(必須)に加え、オプションで `vat_id`、`tax_id`、`registration_number`、`address`、`contacts`、`bank`。銀行詳細は書き込み専用 — リクエストに含めるがレスポンスでエコーされない。[請求エンティティとインボイス受取人](/docs/building/by-layer/L2/accounts-and-agents#billing-entity-and-invoice-recipient)を参照。 | | `payment_terms` | string | No | このアカウントの支払い条件: `net_15`、`net_30`、`net_45`、`net_60`、`net_90`、`prepay`。セラーはこれらの条件を受け入れるかアカウントを拒否しなければなりません — 条件は暗黙的に変更されない。省略時、セラーはデフォルト条件を適用します。 | | `sandbox` | boolean | No | true の場合、実際のプラットフォーム呼び出しや請求なしでサンドボックスアカウントを設定します。バイヤー宣言アカウント(`require_operator_auth: false`)にのみ適用。アカウント ID 名前空間の場合、サンドボックスアカウントは `list_accounts` で探索するかアウトオブバンドで供給される既存のテストアカウント。 | | `notification_configs` | array | No | 単一のメディアバイより長く続くイベント向けのアカウントレベル Webhook サブスクライバー: クリエイティブライフサイクル通知とホールセールフィード変更 Webhook。省略すると既存のサブスクライバーを変更しない。`[]` を送るとすべてのサブスクライバーを削除。完全な配列を送ると置き換え。エントリはアカウントスコープの `subscriber_id` でキーされる。既存の `subscriber_id` はアップサートされ、送信配列に不在の永続化 ID は削除される。 | **自然キー**: タプル `(brand, operator, sandbox)` がアカウント関係を一意に識別します。`{brand: {domain: "acme-corp.com"}, operator: "acme-corp.com"}`(直接)は `{brand: {domain: "acme-corp.com"}, operator: "pinnacle-media.com"}`(代理店経由)とは異なるアカウント。`sandbox: true` を追加すると、同じブランド/オペレーターのペアに対してサンドボックスアカウントがプロビジョニングされる — 実際のプラットフォーム呼び出しや請求なし。 ## レスポンス **成功レスポンス:** アカウントごとの結果を含む `accounts` 配列を返します。操作が成功しても、個々のアカウントが保留中、拒否、または失敗することがあります。 **エラーレスポンス:** * `errors` -- 操作レベルのエラーの配列(認証失敗、サービス利用不可)。`accounts` 配列は含まれない。 **注:** レスポンスは判別共用体を使用 -- `accounts` または `errors` のいずれか一方のみ、両方は含まれない。 **アカウントごとのフィールド:** | フィールド | 説明 | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `brand` | リクエストからエコーされます。`domain` とオプションの `brand_id` を含むオブジェクト。 | | `operator` | リクエストからエコーされます。 | | `name` | アカウントのセラー表示名。 | | `action` | 実行された内容: `created`、`updated`、`unchanged`、`failed`。 | | `status` | アカウントの現在の状態([アカウントステータス](#account-status) を参照)。 | | `billing` | 適用された請求モデル。リクエストの値と一致します。 | | `billing_entity` | 請求される当事者のビジネスエンティティ詳細、リクエストからエコー。セラーはエージェントが省略したフィールド(例: 与信チェックからの `registration_number`)を追加してもよいが、異なるエンティティのデータを返してはならない。銀行詳細は省略(書き込み専用)。 | | `account_scope` | セラーがアカウントをスコープした方法: `operator`(このオペレーターのブランド間で共有)、`brand`(このブランドのオペレーター間で共有)、`operator_brand`(このオペレーター+ブランドのペア専用)、`agent`(宣言されたブランド/オペレーターのペア間で共有されるエージェントスコープのアカウント)。[アカウントスコープ](/docs/building/by-layer/L2/accounts-and-agents#account-scope) を参照。 | | `setup` | `status: "pending_approval"` の場合に存在。与信または法的セットアップ完了の `url`、必要な内容を説明する `message`、オプションの `expires_at` を含みます。 | | `rate_card` | セラーが割り当てたレートカード識別子(該当する場合)。 | | `payment_terms` | このアカウントで合意した支払い条件: `net_15`、`net_30`、`net_45`、`net_60`、`net_90`、`prepay`。アカウントがアクティブな場合、すべての請求書の拘束力を持つ条件。 | | `credit_limit` | 最大未払い残高(`{amount, currency}`)。 | | `errors` | アカウントごとのエラー(`action: "failed"` の場合のみ存在)。 | | `warnings` | 非致命的な通知。 | | `sandbox` | これがサンドボックスアカウントかどうか、リクエストからエコーされます。バイヤー宣言アカウントにのみ存在。 | | `notification_configs` | オプション。リクエスト適用後の現在の永続化されたアカウントレベル Webhook サブスクライバー。リクエストが `notification_configs` を含んだか、アカウントがすでに永続化されたサブスクライバーを持つ場合、`created`、`updated`、`unchanged` の結果で存在。各エントリは `subscriber_id`、`url`、`event_types[]`、`active` を運ぶ。`authentication.credentials` は省略(書き込み専用)。 | | `authorization` | オプション。このアカウントに対する呼び出しエージェントのスコープ付与 — `allowed_tasks`、`field_scopes`、`scope_name`、`read_only`。すべてのベンダーエージェントタイプ(media-buy、signals、governance、creative、brand)に適用 — Accounts Protocol の面は共有。`created`、`updated`、`unchanged` の結果で存在。`failed` の結果では省略。スコープイントロスペクションをサポートするベンダーエージェントはこれを設定すべき(SHOULD)。`attestation_verifier` 標準スコープを主張する media-buy sales agent は設定しなければならない(MUST)。不在は、ベンダーエージェントがイントロスペクション可能なスコープを宣伝しないことを意味する。呼び出し元は不在からアクセスを推論してはならない(MUST NOT)。完全な形状は [Caller authorization](/docs/accounts/overview#caller-authorization) を参照。 |

アカウントステータス

| ステータス | 意味 | 次のステップ | | ------------------ | -------------- | ----------------------------------------------------------------------------------------- | | `active` | 使用準備完了 | プロトコル操作で [アカウント参照](/docs/building/by-layer/L2/accounts-and-agents#account-references) を使用 | | `pending_approval` | セラーがレビュー中 | 与信または法的プロセスを完了するために人間が `setup.url` にアクセスする必要がある場合があります。更新確認のため `list_accounts` をポーリング。 | | `rejected` | セラーがリクエストを拒否 | `warnings` の拒否理由を確認し、調整して再試行するかセラーに連絡 | | `payment_required` | 与信限度額超過または残高不足 | 資金追加または与信限度額引き上げ。他のアカウントに支出を振り分ける。 | | `suspended` | アクティブだったが現在停止中 | セラーに連絡して解決 | | `closed` | アクティブだったが現在終了 | -- | ### 非同期通知 `push_notification_config` が提供され、セラーが `pending_approval` を返した場合、アカウントステータスが変更されると(例: 承認 → `active`、拒否 → `rejected`)、セラーはWebhook通知を送信します。 プロビジョニングリクエストでは、通知ペイロードに `(brand, operator)` の自然キーが含まれるため、バイヤーは元の同期リクエストと関連付けられます。セラーがセラー割り当ての `account_id` も返す場合、通知は便宜ハンドルとしてそれを含みます。バイヤーは後続の呼び出しについて依然セラーの宣言されたアカウント参照モデルに従います。 ```json theme={null} { "brand": { "domain": "nova-brands.com", "brand_id": "glow" }, "operator": "pinnacle-media.com", "status": "active", "account_id": "acc_glow_001" } ``` バイヤーが `push_notification_config` を提供しなかった場合、ステータス変更を確認するために [`list_accounts`](/docs/accounts/tasks/list_accounts) をポーリングします。 ## 2つのモード: プロビジョニング vs. 設定更新 各アカウントごとのエントリは 2 つのキー形状の 1 つを使い、両方を使うことはありません: * **プロビジョニングモード** — エントリルートにフラットな `brand` + `operator` + `billing`。セラーはアカウントをプロビジョニングまたはアップサートします。バイヤー宣言アカウント(`require_operator_auth: false`)に使用。これは AdCP 3.0 が出荷した形状です。セラーは `account_id` をエコーしてもよい(MAY)が、自然キーの `AccountRef` は後続の呼び出しで有効なままです。 * **設定更新モード** — エントリルートに `account`([AccountRef](/docs/building/by-layer/L2/accounts-and-agents#account-references))、`brand`/`operator`/`billing` は不在。セラーはアカウントの設定可能な状態を更新します — プロビジョニングの副作用なし。セラーがこのタスクを通じて設定更新を公開する場合のアカウント ID 名前空間にのみ使用。上流管理のアカウントは `list_accounts` で探索し、セラー定義のアカウント ID はアウトオブバンドで供給されます。バイヤー宣言アカウントセラーも、以前にプロビジョニングしたアカウントに対する設定更新にこのモードを受け入れてもよい(MAY)。 スキーマは `oneOf` で排他性を強制します — 同じエントリに両方の形状を送ることは検証エラーです。設定更新モードを実装しないセラーは `account` でキーされたエントリを `UNSUPPORTED_PROVISIONING` で拒否します。`sync_accounts` を通じてプロビジョニングしないセラー(アカウント ID 名前空間を含む)は、同じコードで自然キープロビジョニングエントリを拒否します。 ## アカウントレベルの Webhook サブスクリプション `notification_configs[]` は、ライフサイクルが単一のメディアバイより長く続く通知向けのアカウントレベル Webhook サブスクライバーを運びます — `creative.status_changed`、`creative.purged`、ホールセールフィード変更 Webhook(`product.*`、`signal.*`、`wholesale_feed.bulk_change`)、および `notification-type.json` にそれらのイベントタイプが追加された後の将来のアカウントアンカーリソースイベント。 これはアカウントオブジェクトのライフサイクルイベントストリームではありません。今日 `account.created`、`account.updated`、`account.status_changed`、`account.closed` の通知タイプはありません。アカウントステータスの変更は [`list_accounts`](/docs/accounts/tasks/list_accounts) をポーリングするか、`sync_accounts` プロビジョニングリクエストの非同期結果についてはこのタスクの `push_notification_config` を通じて観測します。 これらのイベントタイプでは、「ホールセールフィード」はセラーの購入可能なホールセールプロダクトと `get_products` または `get_signals` が返すシグナルフィードを意味します。`sync_catalogs` が管理するバイヤー提供のフィードではありません。 **両方**のプロビジョニングと設定更新モードで許可されます。宣言的セマンティクス: * `notification_configs` を省略すると、アカウントの既存のサブスクライバーを変更しません。 * `notification_configs: []` を送ると、そのアカウントのすべてのサブスクライバーを削除します。 * 非空の配列を送ると、アカウントの現在のセットを提出されたセットで置き換えます。 1 つのアカウント内で、`subscriber_id` は安定した論理キーです。既存の `(account_id, subscriber_id)` を異なる `url`、`event_types`、`authentication`、`active` 値で再送すると、重複を作るのではなくそのサブスクライバーのアクティブ設定を置き換えます。セラーは提出された配列を永続状態とマージしてはなりません(MUST NOT): `subscriber_id` が送信配列に現れない永続化されたサブスクライバーは削除されます。一時停止されたエントリ(`active: false`)は同じ置き換えセマンティクスの対象です。一時停止されたサブスクリプションを保持するには、送信配列に `active: false` で再度含めます。同じ提出配列内の重複した `subscriber_id` 値は無効です。置き換えはアカウントスコープです。同じ `subscriber_id` は別のアカウントで再利用してもよい(MAY)。 提出された置き換えセットの任意のエントリが検証またはアクティベーション証明に失敗した場合、セラーはそのアカウントエントリを `action: "failed"` で拒否し、アカウントの以前の `notification_configs[]` セットを変更しないままにします。セラーは置き換えセットを部分適用して失敗したサブスクライバーのみを黙って落としてはなりません(MUST NOT)。 各エントリは次を持ちます: * `subscriber_id` — バイヤー供給の識別子、アカウント内で一意。マルチサブスクライバーアカウントがエンドポイントでルーティングできるよう、すべての発火でエコーされる * `url` — HTTPS エンドポイント URL。セラーは、新規または変更されたアクティブサブスクライバーをアクティブとして扱う前に、エンドポイントアクティベーションチャレンジまたは同等の制御証明を完了しなければならない(MUST)。 * `event_types[]` — サブスクライバーが望むタイプ。アカウントアンカータイプのみ許可(今日: `creative.status_changed`、`creative.purged`、`product.created`、`product.updated`、`product.priced`、`product.removed`、`signal.created`、`signal.updated`、`signal.priced`、`signal.removed`、`wholesale_feed.bulk_change`)。セラーは任意のメディアバイアンカータイプ(`scheduled`、`final`、`delayed`、`adjusted`、`impairment`)と `account.status_changed` のような未定義のアカウントライフサイクル名を、`accounts[].errors[]` の `INVALID_REQUEST` または `VALIDATION_ERROR` でアカウントごとの検証失敗として拒否しなければならず(MUST)、`error.field` は無効な `event_types` エントリを指さなければならない(MUST)。 * `authentication`(オプション)— レガシー Bearer または HMAC-SHA256。デフォルトの RFC 9421 Webhook プロファイルを使うには省略。存在する場合、`push_notification_config.authentication` と同じ署名付き登録のダウングレード耐性ルールが適用されます。クレデンシャルは書き込み専用 — セラーは読み取り時に省略します。 * `active`(デフォルト `true`)— 登録を削除せずにサブスクライバーを一時停止するには `false` を設定。セラーは `active: false` の間アウトバウンド証明チャレンジのみスキップしてもよい(MAY)。書き込み時に HTTPS パース、ホスト名正規化、予約範囲拒否を依然強制しなければならない(MUST)。一時停止されたサブスクライバーは再アクティベートされるまで発火を受け取ってはならない(MUST NOT)。再アクティベーションは、現在の有効な証明がない任意のタプルについて、接続ピン留め付きの完全な SSRF 検証と制御証明を繰り返さなければならない(MUST)。 ### エンドポイントの制御証明 エントリを `active: true` として永続化またはエコーする前に、セラーは URL を検証し、[Webhook URL validation](/docs/building/by-layer/L1/security#webhook-url-validation-ssrf) の SSRF ルールを適用し、レシーバーがエンドポイントを制御することを証明しなければなりません(MUST)。 証明は、タプル `(account_id, subscriber_id, normalized url, authentication mode/credential binding, normalized event_types)` について現在の有効な証明がないときに必要です。サブスクライバー ID、正規化 URL、認証モード/クレデンシャルバインディング、`event_types[]` を変更すると、新しいセットがアクティブになる前に新しい証明が必要です。チャレンジ POST 自体は、候補設定がレガシー配信認証を選択しても、セラーの RFC 9421 Webhook プロファイル鍵で署名されなければなりません(MUST)。新しい署名者は `adcp_use: "request-signing"` を使用。非推奨の `webhook-signing` 鍵は互換期間中も受け入れられます。レシーバーは RFC 9421 署名を検証しなければならず(MUST)、`seller_agent_url`、`delivery_auth`、`event_types` が保留中の登録に一致しない限りチャレンジを拒否しなければなりません(MUST)。セラーは独自の証明有効期限ポリシーで再チャレンジしてもよい(MAY)。 標準チャレンジは、`type`、`challenge`、`account_id`、`subscriber_id`、`seller_agent_url`、`delivery_auth`、`event_types` を含む JSON ボディを持つ候補 `url` への HTTPS POST です。正準スキーマは [`webhook-challenge.json`](https://adcontextprotocol.org/schemas/v3/core/webhook-challenge.json) と [`webhook-challenge-response.json`](https://adcontextprotocol.org/schemas/v3/core/webhook-challenge-response.json) です。 ```json theme={null} { "type": "webhook.challenge", "challenge": "example-challenge-token-000000000000", "account_id": "acct_123", "subscriber_id": "buyer-primary", "seller_agent_url": "https://seller.example/adcp", "delivery_auth": { "mode": "rfc9421" }, "event_types": ["creative.status_changed"] } ``` レシーバーは、正確に 1 つのエコーフィールドを含む JSON ボディで HTTP `2xx` を返して制御を証明します: ```json theme={null} { "challenge": "example-challenge-token-000000000000" } ``` セラーは後方互換エイリアスも受け入れなければなりません(MUST): ```json theme={null} { "token": "example-challenge-token-000000000000" } ``` チャレンジ値は暗号学的にランダムで、単一使用で、登録タプルにスコープされなければなりません(MUST)。失敗、非 `2xx`、不正、不一致、タイムアウトのチャレンジは証明失敗を意味します。セラーは SSRF 検証と同じアウトバウンドフェッチ上限(10 秒接続、10 秒読み取り)を使うべきで(SHOULD)、`sync_accounts` のクリティカルパスで最大 1 回の初回チャレンジ POST を行うべきです(SHOULD)。セラーは、同じチャレンジ値を使いリクエスト予算内で完了する場合、返す前に 1 回の一時的ネットワーク失敗をリトライしてもよい(MAY)。そうでなければバイヤーは `sync_accounts` を再送してリトライします。 証明失敗時、セラーは `action: "failed"`、`errors[].code: "VALIDATION_ERROR"`(不正 URL には `INVALID_REQUEST`)、`accounts[i].notification_configs[j].url` を指す `error.field` を持つアカウントごとの失敗を返します。以前の永続化されたサブスクライバーセットは変更されません。`dry_run: true` はネットワークチャレンジを送ってはなりません(MUST NOT)。構造検証と何が証明を必要とするかのみを報告できます。 例 — アカウント ID 名前空間アカウントにバイヤー側エンドポイントと監査バスを登録: ```json theme={null} { "idempotency_key": "f2c4b7d9-6789-49bc-defa-2345678901bc", "accounts": [ { "account": { "account_id": "acc_acme_pinnacle" }, "notification_configs": [ { "subscriber_id": "buyer-primary", "url": "https://buyer.example/webhooks/adcp/creative", "event_types": ["creative.status_changed", "creative.purged"], "active": true }, { "subscriber_id": "audit-bus", "url": "https://audit.buyer.example/adcp/ingest", "event_types": ["creative.status_changed", "creative.purged"], "active": true } ] } ] } ``` 例 — ホールセールプロダクトとシグナル変更のためのホールセールフィードミラーサブスクライバーを登録: ```json theme={null} { "idempotency_key": "a8af8cf1-89bd-41f3-b27d-7ee7e9f8d2e4", "accounts": [ { "account": { "account_id": "acc_acme_pinnacle" }, "notification_configs": [ { "subscriber_id": "wholesale-feed-sync", "url": "https://buyer.example/webhooks/adcp/wholesale-feed", "event_types": [ "product.created", "product.updated", "product.priced", "product.removed", "signal.created", "signal.updated", "signal.priced", "signal.removed", "wholesale_feed.bulk_change" ], "active": true } ] } ] } ``` [`sync_governance`](/docs/accounts/tasks/sync_governance) で登録されたガバナンスエージェントは、これらの Webhook に暗黙的にサブスクライブ**されません**。ガバナンスエージェントもクリエイティブライフサイクルの発火を受け取るべき場合、その URL を別個の `notification_configs[]` エントリとして登録します — 明示的、監査可能、独自の `event_types[]` フィルター付き。 適用された状態は [`list_accounts`](/docs/accounts/tasks/list_accounts) で検証します — レスポンスはクレデンシャルを秘匿した現在の永続化された `notification_configs[]` をアカウントごとに運びます。`sync_accounts` も、リクエストが `notification_configs` を含んだか、任意の永続化されたサブスクライバーがすでに存在する場合、`created`、`updated`、`unchanged` の結果で現在のサニタイズされたセットをエコーします。 ホールセールフィード通知は、別個のサブスクリプションタスクではなくここで登録されます。Webhook ボディは [`wholesale-feed-webhook.json`](https://adcontextprotocol.org/schemas/v3/core/wholesale-feed-webhook.json) です: 変更されたプロダクト、シグナル、またはバルク変更サマリーに加え、変更後の `wholesale_feed_version` を運びます。セラーは各 Webhook を発行する前に、対応するホールセール読み取りが使うのと同じサブスクライバーごとの認可とスコープ述語を適用しなければなりません(MUST)。レシーバーはペイロードをローカルミラーに適用してもよい(MAY)。逃した/信頼できないプッシュの修復と、支出や権限をバインドする前には `if_wholesale_feed_version` 付きの `get_products` / `get_signals` を使います。ケイパビリティ宣言とイベントセマンティクスは [wholesale\_feed\_webhooks](/docs/protocol/get_adcp_capabilities#wholesale_feed_webhooks) を参照。 ## 一般的なシナリオ ### 複数のブランドを同期する代理店 ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncAccountsResponseSchema } from "@adcp/sdk"; const result = await testAgent.syncAccounts({ accounts: [ { brand: { domain: "nova-brands.com", brand_id: "spark" }, operator: "pinnacle-media.com", billing: "operator", }, { brand: { domain: "nova-brands.com", brand_id: "glow" }, operator: "pinnacle-media.com", billing: "operator", }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncAccountsResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } for (const account of validated.accounts) { if (account.status === "active") { console.log(`Ready: ${account.brand.domain}/${account.brand.brand_id} → ${account.status}`); } else if (account.status === "pending_approval") { console.log(`Setup required for ${account.brand.brand_id}: ${account.setup?.url}`); // アクティブになるまで list_accounts をポーリング } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.sync_accounts( accounts=[ { "brand": {"domain": "nova-brands.com", "brand_id": "spark"}, "operator": "pinnacle-media.com", "billing": "operator", }, { "brand": {"domain": "nova-brands.com", "brand_id": "glow"}, "operator": "pinnacle-media.com", "billing": "operator", }, ], ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for account in result.accounts: if account.status == 'active': print(f"Ready: {account.brand['domain']}/{account.brand.get('brand_id')} → {account.status}") elif account.status == 'pending_approval': print(f"Setup required for {account.brand.get('brand_id')}: {account.setup.url}") # アクティブになるまで list_accounts をポーリング asyncio.run(main()) ``` ### ブランドによる直接購入 ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncAccountsResponseSchema } from "@adcp/sdk"; const result = await testAgent.syncAccounts({ accounts: [ { brand: { domain: "acme-corp.com" }, operator: "acme-corp.com", billing: "operator", }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncAccountsResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } const account = validated.accounts[0]; if (account.status === "active") { console.log(`Ready: ${account.brand.domain} — ${account.status}`); } else if (account.status === "pending_approval") { console.log(`Setup required: ${account.setup?.url}`); // アクティブになるまで list_accounts をポーリング } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.sync_accounts( accounts=[ { "brand": {"domain": "acme-corp.com"}, "operator": "acme-corp.com", "billing": "operator", }, ], ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") account = result.accounts[0] if account.status == 'active': print(f"Ready: {account.brand['domain']} — {account.status}") elif account.status == 'pending_approval': print(f"Setup required: {account.setup.url}") # アクティブになるまで list_accounts をポーリング asyncio.run(main()) ``` ### 拒否の処理 セラーがリクエストを拒否した場合、アカウントエントリは `status: "rejected"` を持ちます。 ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncAccountsResponseSchema } from "@adcp/sdk"; const result = await testAgent.syncAccounts({ accounts: [ { brand: { domain: "acme-corp.com", brand_id: "clearance" }, operator: "acme-corp.com", }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncAccountsResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } for (const account of validated.accounts) { if (account.status === "rejected") { console.log("Account request was rejected"); if (account.warnings?.length) { console.log(`Reason: ${account.warnings.join(", ")}`); } } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.sync_accounts( accounts=[ { "brand": {"domain": "acme-corp.com", "brand_id": "clearance"}, "operator": "acme-corp.com", }, ], ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for account in result.accounts: if account.status == 'rejected': print("Account request was rejected") warnings = getattr(account, 'warnings', None) if warnings: print(f"Reason: {', '.join(warnings)}") asyncio.run(main()) ``` ## エラーハンドリング | エラーコード | 説明 | 解決策 | | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | `ACCOUNT_NOT_FOUND` | 参照されたアカウントが存在しないか、アクセスできない | `account_id` を確認するか、再同期する | | `BILLING_NOT_SUPPORTED` | セラー全体のケイパビリティゲート(`supported_billing` が値を含まない)またはアカウント関係ごとのゲート。[Billing and Account Setup](/docs/building/by-layer/L3/error-handling#billing-and-account-setup)を参照 | `supported_billing` の `get_adcp_capabilities` を確認し、調整または `billing` を省略。ケイパビリティ vs アカウントスコープを区別するには `error.details.scope` を検査 | | `BILLING_NOT_PERMITTED_FOR_AGENT` | セラー全体のケイパビリティは値を受け入れるが、呼び出しバイヤーエージェントの商業関係が許可しない(例: パススルー専用 — 支払い関係なし)。[バイヤーエージェントのアイデンティティ](/docs/building/by-layer/L2/accounts-and-agents#buyer-agent-identity)を参照 | 存在する場合は `error.details.suggested_billing`(通常 `operator`)でリトライ。不在の場合は人間に表面化 — エージェントは自身の商業関係を拡張できない | | `PAYMENT_TERMS_NOT_SUPPORTED` | セラーがリクエストされた支払い条件を受け入れない | セラーのデフォルトを受け入れるため `payment_terms` を省略するか、オフラインで交渉 | | `ACCOUNT_PAYMENT_REQUIRED` | アカウントに支払いが必要な未払い残高がある | 未払い残高を解決するか、別のアカウントに振り分ける | | `ACCOUNT_SUSPENDED` | アカウントが停止されている | セラーに連絡して解決 | | `BRAND_REQUIRED` | ブランド参照なしで請求可能な操作が試みられた | リクエストに `brand` を含める | ## 次のステップ * [list\_accounts](/docs/accounts/tasks/list_accounts) -- 保留中のアカウントのステータス変更をポーリング * [sync\_governance](/docs/accounts/tasks/sync_governance) -- ガバナンスエージェントをアカウントに同期 * [アカウントとエージェント](/docs/building/by-layer/L2/accounts-and-agents) -- 請求モデル、信頼モデル、認可オペレーター * [ブランドプロトコル](/docs/brand-protocol/brand-json) -- セラーエージェントが `brand.domain` からブランドアイデンティティを解決する方法 * [get\_adcp\_capabilities](/docs/protocol/get_adcp_capabilities) -- アカウントを同期する前に `supported_billing` と `require_operator_auth` を確認 # sync_governance Source: https://adcp-docs-ja.pier1.co.jp/docs/accounts/tasks/sync_governance sync_governance は特定のアカウントにガバナンスエージェントエンドポイントを同期する。セラーはこれらのエージェントを永続化し、メディアバイライフサイクルイベント中に check_governance 経由でそれらを呼ぶ。 特定のアカウントのガバナンスエージェントエンドポイントを同期します。セラーはエージェントを永続化し、メディアバイライフサイクルイベント中に `check_governance` 経由でそれを呼びます。各アカウントエントリーは [アカウント参照](/docs/building/by-layer/L2/accounts-and-agents#account-references) をちょうど 1 つのガバナンスエージェントとペアにし、アカウント id 名前空間(`account_id`)とバイヤー宣言アカウント(`brand` + `operator`)の両方をサポートします。 アカウントは、完全なライフサイクルを所有する 1 つのガバナンスエージェントにバインドします。認可、配信監視、コンプライアンスは、別々の権威が保持する専門分野ではなく、1 つのプランに対する同じ評価のフェーズです。専門レビュー(法務、ブランドセーフティ、カテゴリー)は、複数の登録全体ではなくガバナンスエージェント内で合成します。`governance_agents` は `maxItems: 1` の配列です、なぜなら配列形状は 3.0 が出荷した形状だから — 制約は荷重を担い、緩和に向けたステージングポストではありません。エンベロープの `governance_context` はこの層の下では単数です。上限を緩和するには、計画されていない協調ワイヤー形状変更が必要でしょう。[One governance agent per account](/docs/governance/campaign/specification#one-governance-agent-per-account) を参照。 これは **置換セマンティクス** を使います — 各呼び出しが指定されたアカウントの以前登録されたエージェントを置き換えます。リクエストに含まれないアカウントは既存の構成を保ちます。 **Response Time**: 約 1s。 **Request Schema**: [`/schemas/v3/account/sync-governance-request.json`](https://adcontextprotocol.org/schemas/v3/account/sync-governance-request.json) **Response Schema**: [`/schemas/v3/account/sync-governance-response.json`](https://adcontextprotocol.org/schemas/v3/account/sync-governance-response.json) ## クイックスタート アカウント id 名前空間アカウントのガバナンスエージェントを同期: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncGovernanceResponseSchema } from "@adcp/sdk"; const result = await testAgent.syncGovernance({ accounts: [ { account: { account_id: "acct-social-001" }, governance_agents: [ { url: "https://governance.pinnacle-media.com", authentication: { schemes: ["Bearer"], credentials: "gov-token-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } } ] } ] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncGovernanceResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } for (const entry of validated.accounts) { if (entry.status === "synced") { console.log(`${JSON.stringify(entry.account)}: ${entry.governance_agents.length} agent registered`); } else { console.log(`${JSON.stringify(entry.account)}: failed — ${JSON.stringify(entry.errors)}`); } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.sync_governance( accounts=[ { "account": {"account_id": "acct-social-001"}, "governance_agents": [ { "url": "https://governance.pinnacle-media.com", "authentication": { "schemes": ["Bearer"], "credentials": "gov-token-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } } ] } ] ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for entry in result.accounts: if entry.status == "synced": print(f"{entry.account}: {len(entry.governance_agents)} agent registered") else: print(f"{entry.account}: failed — {entry.errors}") asyncio.run(main()) ``` ## リクエストパラメーター | Parameter | Type | Required | Description | | ---------- | ----- | -------- | ---------------------------------------------------------- | | `accounts` | array | Yes | アカウントごとのガバナンスエージェントエントリー。各がアカウント参照をそのアカウントのガバナンスエージェントとペア。 | **各アカウントエントリー:** | Field | Type | Required | Description | | ------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `account` | object | Yes | [アカウント参照](/docs/building/by-layer/L2/accounts-and-agents#account-references): アカウント id 名前空間には `{account_id}`、バイヤー宣言アカウントには `{brand, operator}`。 | | `governance_agents` | array | Yes | このアカウントのガバナンスエージェントエンドポイント。ちょうど 1 エントリーの配列(`minItems: 1`、`maxItems: 1`)。 | **ガバナンスエージェント:** | Field | Type | Required | Description | | ---------------- | ------ | -------- | ------------------------------------------------------------------------------------ | | `url` | string | Yes | ガバナンスエージェントの HTTPS エンドポイント URL。 | | `authentication` | object | Yes | このエージェントを呼ぶときセラーが提示する認証情報。`schemes`(1 つの認証スキームの配列)と `credentials`(トークン、最小 32 文字)を含む。 | ## レスポンス **成功レスポンス:** アカウントごとの結果を伴う `accounts` 配列を返します。操作が成功しても個別のエントリーは失敗しうる。 | Field | Description | | ------------------- | ------------------------------------------------------------------ | | `account` | アカウント参照、リクエストからエコー。 | | `status` | `"synced"` または `"failed"`。 | | `governance_agents` | このアカウントで今アクティブなガバナンスエージェント。永続化された状態を反映。`status: "synced"` のときのみ存在。 | | `errors` | アカウントごとのエラー。`status: "failed"` のときのみ存在。 | **エラーレスポンス:** 操作レベルエラー(認証失敗、サービス利用不可)を伴う `errors` 配列。`accounts` 配列は存在しない。 ## 認可 セラーは、ガバナンスエージェントを永続化する前に、認証されたエージェントが各参照アカウントに対する権限を持つことを検証しなければなりません(MUST)。エージェントが所有しないアカウントを参照するリクエストは、それらのエントリーにエラーを伴う `failed` ステータスを返さなければなりません(MUST)。 ## 一般的なシナリオ ### アカウントごとに異なるガバナンスエージェント 単一の `sync_governance` 呼び出しは、アカウントごとに別個のエージェントを登録できます — 各アカウントは依然としてちょうど 1 つのエージェントにバインドしますが、同じ呼び出しのアカウントはそれを共有する必要はありません。 ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncGovernanceResponseSchema } from "@adcp/sdk"; const result = await testAgent.syncGovernance({ accounts: [ { account: { account_id: "acct-social-001" }, governance_agents: [ { url: "https://governance.pinnacle-media.com", authentication: { schemes: ["Bearer"], credentials: "gov-token-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } } ] }, { account: { account_id: "acct-social-002" }, governance_agents: [ { url: "https://governance.acme-buyer.com", authentication: { schemes: ["Bearer"], credentials: "gov-token-yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" } } ] } ] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncGovernanceResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } for (const entry of validated.accounts) { console.log(`${JSON.stringify(entry.account)}: ${entry.status}`); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.sync_governance( accounts=[ { "account": {"account_id": "acct-social-001"}, "governance_agents": [ { "url": "https://governance.pinnacle-media.com", "authentication": { "schemes": ["Bearer"], "credentials": "gov-token-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } } ] }, { "account": {"account_id": "acct-social-002"}, "governance_agents": [ { "url": "https://governance.acme-buyer.com", "authentication": { "schemes": ["Bearer"], "credentials": "gov-token-yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" } } ] } ] ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for entry in result.accounts: print(f"{entry.account}: {entry.status}") asyncio.run(main()) ``` ### バイヤー宣言アカウント(brand + operator) ```json Request theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/account/sync-governance-request.json", "idempotency_key": "e5b9f2c3-1234-48a0-1234-56789012345e", "accounts": [ { "account": { "brand": { "domain": "nova-brands.com", "brand_id": "spark" }, "operator": "pinnacle-media.com" }, "governance_agents": [ { "url": "https://governance.pinnacle-media.com", "authentication": { "schemes": ["Bearer"], "credentials": "gov-token-yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" } } ] } ] } ``` ### ガバナンスエージェント認証情報のローテーション 更新された `authentication` で `sync_governance` を再度呼びます。置換セマンティクスは、新しい認証情報が以前の構成を上書きすることを意味します。 ### 3.1 以前のマルチエージェント登録からの移行 3.0 の以前のドラフトは、エージェントごとの `categories` を伴うアカウントごと最大 10 のガバナンスエージェントを許可しました。3.1 は `governance_agents` をちょうど 1 エントリーに制約し `categories` を削除します。以前の形状に対して 1 つ以上のエージェントを登録したバイヤーは、次の `sync_governance` 呼び出しで単一エージェントに崩さなければなりません(MUST)。セラーの永続化された状態は置き換えられます。新しいリクエストスキーマは 1 つ以上のエージェントを直ちに拒否するので、「混合モード」ウィンドウは存在しません。 **バイヤー側崩壊決定。** 以前登録されたエージェントのどれが単一エージェントになるかはバイヤー内部の決定です — プロトコルはランク付けや推奨をしません。典型的なパス: (a) 最も広いポリシーカバレッジを持つエージェント(通常は予算/支出権限エージェント)を保ち、専門ロジック(法務、ブランドセーフティ、規制レビュー)を内部ワークフローとしてそれに折りたたむ。(b) 以前の専門家に内部でファンアウトする新しい「フロントドア」ガバナンスエージェントをデプロイし、そのエージェントのみを登録。(c) 常に事実上のガバナンス表面だったエージェントを保ち、他の専門レビューを再登録せずに内部ワークフローとしてそれに折りたたむ。監査証跡が各内部レビュアーが貢献したものを保持するよう、チェックレスポンスの `categories_evaluated` と `findings[].details` 経由で内部分解を監査人に表示します。 **セラー側。** セラーは、新しいスキーマの下での初回ブートで、以前永続化されたマルチエージェント状態を最初のエントリー(元の同期位置で順序付け)に崩し、移行を監査証跡にログしてもよい(MAY)。セラーは、次の `sync_governance` 呼び出しが複数のエージェントを再登録しようとするバイヤーに、この移行ガイダンスを指す明確なエラーを表示すべきです(SHOULD)。 ## エラー処理 | Error Code | Description | Resolution | | ------------------- | ------------------------- | ------------------------------------------------- | | `ACCOUNT_NOT_FOUND` | 参照アカウントが存在しないかアクセス不可 | `list_accounts` または `sync_accounts` 経由でアカウント参照を検証 | | `UNAUTHORIZED` | エージェントが参照アカウントに対する権限を持たない | このアカウントへのアクセスを持つエージェントとして認証されているか確認 | ## 次のステップ * [list\_accounts](/docs/accounts/tasks/list_accounts) — アカウントとその現在のガバナンスエージェントを発見 * [sync\_accounts](/docs/accounts/tasks/sync_accounts) — アドバタイザーアカウントをプロビジョンまたはリンク * [check\_governance](/docs/governance/campaign/tasks/check_governance) — セラーがメディアバイイベント中にガバナンスエージェントをどう呼ぶか * [アカウントとエージェント](/docs/building/by-layer/L2/accounts-and-agents) — アカウントモデル、課金、トラスト # A2A ガイド Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L0/a2a-guide AdCP A2A 連携ガイド: クライアントセットアップ、エージェントカード確認、非同期タスク向け SSE ストリーミング、アーティファクト処理、Agent-to-Agent Protocol のレスポンス形式。 Agent-to-Agent Protocol を使って AdCP を統合するためのトランスポート別ガイドです。タスク処理、ステータス管理、ワークフローパターンは [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照してください。 ## A2A プロトコルバージョン AdCP は Linux Foundation ガバナンス下の [A2A 仕様](https://a2a-protocol.org/latest/)を追跡します。**1.0** ワイヤーフォーマットがターゲットです。**v0.3** は依然広くデプロイされており、互換期間中サポートされます。 ### 1.0 で変わったこと | 領域 | v0.3 | 1.0 | | ----------------- | ---------------------------------- | ------------------------------------------------------------------------------------- | | エージェントカードのトランスポート | ルートの `url` + `protocolVersion` | 各インターフェースごとの `url`、`protocolBinding`、`protocolVersion` を持つ `supportedInterfaces[]` 配列 | | `Part` 判別子 | `kind: "text" \| "data" \| "file"` | `kind` なし — コンテンツはどのフィールドが設定されているか(`text`、`data`、`url`、`raw`)で決まる | | File フィールド | `uri`、`name`、`mimeType` | `url`(参照)または `raw`(base64 バイト)、`filename`、`mediaType` | | メッセージロール | `"user"` / `"agent"` | `"ROLE_USER"` / `"ROLE_AGENT"`(ProtoJSON 正準形) | | タスク状態 | `"completed"`、`"working"`、… | `"TASK_STATE_COMPLETED"`、`"TASK_STATE_WORKING"`、… | | タイムスタンプ | ISO-8601 | ミリ秒精度の ISO-8601 UTC(`YYYY-MM-DDTHH:mm:ss.sssZ`) | AdCP 自身の統合トップレベル `status` フィールド(`@adcp/sdk` が返す)は、引き続き小文字の短縮形(`"completed"`、`"working"`、…)を使用します — これは生の A2A `status.state` に対する AdCP の抽象化であり、A2A ワイヤー値ではありません。 ### デュアルバージョン互換性 v0.3 と 1.0 の両方のクライアントに提供する必要のあるサーバーは、エージェントカードで両方のインターフェースを宣伝し、トランスポート層で明示的な互換性を有効にします(例: Python SDK の `enable_v0_3_compat=True`)。後方互換性はデフォルトでは有効に**なりません**。 1.0 を話すクライアントは、SDK が下位変換を提供する場合に v0.3 サーバーと通信できます。逆(v0.3 クライアント → 1.0 専用サーバー)はサーバーが互換を有効にする必要があります。 ### 本ガイドの例 以下の例は **1.0 ワイヤーフォーマット**(`kind` フィールドなし、ProtoJSON enum)を使用します。v0.3 サーバーの場合、同じ Part は `{ kind: "text", text: "…" }` になり、状態は小文字になります。AdCP 抽出クライアント([A2A レスポンス抽出](/docs/building/by-layer/L0/a2a-response-extraction)を参照)は互換期間中に両方の形状を受け入れます。 ## A2A クライアントのセットアップ ### 1. A2A クライアントを初期化 ```javascript theme={null} const a2a = new A2AClient({ endpoint: 'https://adcp.example.com/a2a', auth: { type: 'bearer', token: process.env.ADCP_API_KEY }, agent: { name: "AdCP Media Buyer", version: "1.0.0" } }); ``` ### 2. エージェントカードを確認 ```javascript theme={null} // Check available skills const agentCard = await a2a.getAgentCard(); console.log(agentCard.skills.map(s => s.name)); // ["get_products", "create_media_buy", "sync_creatives", ...] ``` ### 3. 最初のタスクを送る ```javascript theme={null} const response = await a2a.send({ message: { role: "ROLE_USER", parts: [{ text: "Find video products for pet food campaign" }] } }); // すべてのレスポンスに統一ステータスフィールドが含まれる(AdCP 1.6.0+) console.log(response.status); // "completed" | "input-required" | "working" | etc. console.log(response.message); // Human-readable summary ``` ## メッセージ構造(A2A 固有) ### マルチパートメッセージ A2A の強みは、テキスト・データ・ファイルを組み合わせたマルチパートメッセージです: ```javascript theme={null} // Text + structured data + file const response = await a2a.send({ message: { role: "ROLE_USER", parts: [ { text: "Create campaign with these assets" }, { data: { skill: "create_media_buy", parameters: { packages: ["pkg_001"], total_budget: 100000 } } }, { url: "https://cdn.example.com/hero-video.mp4", filename: "hero_video_30s.mp4", mediaType: "video/mp4" } ] } }); ``` ### スキル呼び出し方法 #### 自然言語(柔軟) ```javascript theme={null} // Agent interprets intent const task = await a2a.send({ message: { role: "ROLE_USER", parts: [{ text: "Find premium CTV inventory under $50 CPM" }] } }); ``` #### 明示的スキル(決定的) ```javascript theme={null} // Explicit skill with exact parameters const task = await a2a.send({ message: { role: "ROLE_USER", parts: [{ data: { skill: "get_products", parameters: { max_cpm: 50, channels: ["ctv"], tier: "premium" } } }] } }); ``` #### ハイブリッド(推奨) ```javascript theme={null} // Context + explicit execution for best results const task = await a2a.send({ message: { role: "ROLE_USER", parts: [ { text: "Looking for inventory for spring campaign targeting millennials" }, { data: { skill: "get_products", parameters: { audience: "millennials", season: "Q2_2024", max_cpm: 45 } } } ] } }); ``` **ステータス処理**: 完全なパターンは [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照してください。 ## A2A レスポンス形式 **AdCP 1.6.0 の新機能**: すべてのレスポンスに統一ステータスフィールドが含まれます。 ### 標準レスポンス構造 A2A 上の AdCP レスポンスは、タスクレスポンスを含む DataPart(`data` フィールドを運ぶ Part)を少なくとも 1 つ含める **必要があります**。人間向けメッセージの TextPart(`text` フィールドを運ぶ Part)は **推奨** ですが任意です。 ```json theme={null} { "status": "completed", // AdCP unified status (see Core Concepts) "taskId": "task-123", // A2A task identifier "contextId": "ctx-456", // Automatic context management "artifacts": [{ // A2A-specific artifact structure "artifactId": "artifact-product-catalog-abc", "name": "product_catalog", "parts": [ { "text": "Found 12 video products perfect for pet food campaigns" }, { "data": { "products": [...], "total": 12 } } ] }] } ``` A2A 1.0 ワイヤーフォーマットは `kind` 判別子を運びません — Part のコンテンツタイプはどのフィールドが設定されているか(`text`、`data`、`url`、`raw`)で暗黙的に決まります。v0.3 のサーバー/クライアントでは、同等の Part に `"kind": "text"` / `"kind": "data"` / `"kind": "file"` が含まれます。 **完全な標準仕様は [A2A Response Format](/docs/building/by-layer/L0/a2a-response-format) を参照してください。** ### A2A 固有フィールド * **taskId**: ストリーミング更新のための A2A タスク ID * **contextId**: A2A プロトコルが自動管理 * **artifacts**: テキスト・データを含むマルチパート成果物 * **status**: A2A の `status.state` からマップされる AdCP の統合小文字短縮形([A2A レスポンス抽出](/docs/building/by-layer/L0/a2a-response-extraction#wire-format-compatibility)を参照) ### アーティファクトの処理 `DataPart` が複数ある場合(ストリーミングなど)は **最後の `DataPart` を正とします**: ```javascript theme={null} // アーティファクトを抽出(現状 AdCP は 1 レスポンス 1 アーティファクト) const artifact = response.artifacts?.[0]; if (artifact) { // Detect Part type by presence of field (1.0) with kind fallback (v0.3) const isText = (p) => typeof p.text === 'string' || p.kind === 'text'; const isData = (p) => p.data != null || p.kind === 'data'; const message = artifact.parts?.find(isText)?.text; const data = artifact.parts?.find(isData)?.data; return { artifactId: artifact.artifactId, message, data, status: response.status }; } return { status: response.status }; ``` **レスポンス構造の要件、エラーハンドリング、実装パターンの詳細は [A2A Response Format](/docs/building/by-layer/L0/a2a-response-format) を参照してください。** ## プッシュ通知(A2A 固有) A2A では `PushNotificationConfig` によりプッシュ通知が標準で定義されています。Webhook URL を設定すると、ポーリング不要でサーバーがタスク更新を直接 POST します。 ### 相関: URL ではなくペイロードフィールド 受信する通知は、ペイロードボディの `operation_id`(および `task_type`)を使って相関します — `pushNotificationConfig.url` を解析することは**決して**しません。URL はサーバーにとって不透明で、相関のワイヤーレベルの真実の源はペイロードフィールドです。完全な規範的ワイヤー契約は [Webhooks — Operation IDs and URL templates](/docs/building/by-layer/L3/webhooks#operation-ids-and-url-templates) を参照してください(MCP と A2A の両方に適用されます — アドテックのすべての比較可能な非同期通知プロトコルは URL を発火エンティティにとって不透明にします)。 バイヤーは自身の HTTP サーバーのルーティング補助として `operation_id` を URL パスやクエリにエンコードしてもよい(MAY)— 多くの Web フレームワークはボディを解析する前にパスセグメントでディスパッチします — が、それはバイヤー側のサーバー設計の選択であり、ワイヤー契約の一部ではありません。バイヤーのサーバールーティングテンプレートはセラーには見えません。セラーはバイヤーが供給した `pushNotificationConfig.operation_id` フィールドからのみ `operation_id` を読み、ペイロードでそのままエコーします。 **URL テンプレート(バイヤー側のサーバールーティングのみ):** ```javascript theme={null} // Path parameters url: `https://buyer.com/webhooks/a2a/${taskType}/${operationId}` // Query parameters url: `https://buyer.com/webhooks/a2a?task=${taskType}&op=${operationId}` // Or fully opaque — the seller doesn't care about URL shape url: `https://buyer.com/webhooks/${randomToken}` ``` **設定例:** ```javascript theme={null} const operationId = "op_nike_q1_2025"; const taskType = "create_media_buy"; await a2a.send({ message: { role: "ROLE_USER", parts: [{ data: { skill: "create_media_buy", parameters: { /* task params */ } } }] }, pushNotificationConfig: { url: `https://buyer.com/webhooks/a2a/${taskType}/${operationId}`, operation_id: operationId, // canonical correlation channel — seller echoes verbatim token: "client-validation-token", // Optional: for client-side validation authentication: { schemes: ["bearer"], credentials: "shared_secret_32_chars" } } }); ``` Webhook のペイロード形式、プロトコル比較、詳細な処理例は [Webhooks](/docs/building/by-layer/L3/webhooks) を参照してください。 ## SSE ストリーミング(A2A 固有) A2A の強みは Server-Sent Events によるリアルタイム更新です: A2A 上でもアプリケーション層のタスクライフサイクルは依然として AdCP が所有します。A2A `Task`、`taskId`、SSE、プッシュ通知フレームはトランスポート配信の仕組みです。耐久性のあるビジネスオペレーションは AdCP `task_id` でキーされる AdCP ペイロードのままです。完了した A2A タスクでも、ペイロードが `status: 'submitted'` と言う AdCP レスポンスを運ぶことがあります。 ### タスク監視 ```javascript theme={null} class A2aTaskMonitor { constructor(taskId) { this.taskId = taskId; this.events = new EventSource(`/a2a/tasks/${taskId}/events`); this.events.addEventListener('status', (e) => { const update = JSON.parse(e.data); this.handleStatusUpdate(update); }); this.events.addEventListener('progress', (e) => { const data = JSON.parse(e.data); console.log(`${data.percentage}% - ${data.message}`); }); } handleStatusUpdate(update) { switch (update.status) { case 'input-required': // 追加情報・承認が必要 this.emit('input-required', update); break; case 'completed': this.events.close(); this.emit('completed', update); break; case 'failed': this.events.close(); this.emit('failed', update); break; } } } ``` ### リアルタイム更新の例 ```javascript theme={null} // 長時間オペレーションを開始 const response = await a2a.send({ message: { role: "ROLE_USER", parts: [{ data: { skill: "create_media_buy", parameters: { packages: ["pkg_001"], total_budget: 100000 } } }] } }); // Monitor A2A transport progress in real time via SSE if (response.status === 'working' || response.status === 'submitted') { const monitor = new A2aTaskMonitor(response.taskId); monitor.on('progress', (data) => { updateUI(`${data.percentage}%: ${data.message}`); }); monitor.on('completed', (final) => { // Extract last DataPart from the artifact — don't assume a positional index. const parts = final.artifacts[0].parts; const dataParts = parts.filter(p => p.data != null || p.kind === 'data'); const payload = dataParts[dataParts.length - 1]?.data; if (payload?.status === 'submitted') { // A2A delivery completed, but the AdCP operation is still queued. return pollAdcpTask(payload.task_id); } console.log('Created:', payload?.media_buy_id); }); } ``` ### A2A Webhook ペイロード例 **例 1: 完了オペレーションの `Task` ペイロード** タスク完了時、サーバーは A2A 1.0 の `StreamResponse` エンベロープでラップされた完全な `Task` オブジェクトを送信します。タスク結果は `.artifacts` に存在します: ```json theme={null} { "task": { "id": "task_456", "contextId": "ctx_123", "status": { "state": "TASK_STATE_COMPLETED", "timestamp": "2026-01-22T10:30:00.000Z" }, "artifacts": [{ "name": "task_result", "parts": [ { "text": "Media buy created successfully" }, { "data": { "media_buy_id": "mb_12345", "creative_deadline": "2026-01-30T23:59:59.000Z", "packages": [ { "package_id": "pkg_001", "context": { "line_item": "li_ctv_sports" } } ] } } ] }] } } ``` **重要**: **`completed`、`failed`、`rejected`** ステータスでは、AdCP タスク結果は **`.artifacts[0].parts[]` に必ず入れる必要があります**。サーバーがフリーテキストの致命的メッセージのみ(構造化ペイロードなし)を持つ場合、`status.message.parts[]` にフォールバックしてもよい(MAY)— クライアントは両方を扱います。 A2A 1.0 の `StreamResponse` oneof は、すべての SSE フレームとプッシュ通知ペイロードを、`{ task }`、`{ statusUpdate }`、`{ artifactUpdate }`、`{ message }` のちょうど 1 つでラップします(A2A 1.0 §3.2.3、§4.3.3)。`tasks/get` と v0.3 サーバーからの非ストリーミングレスポンスは素のオブジェクトを配信します。クライアントはフィールドを読む前にアンラップします。 **例 2: 進捗更新用 `TaskStatusUpdateEvent`** 実行中の中間ステータス更新では、`status.message.parts[]` に任意データを含められます。SSE/プッシュフレームはイベントを `{ "statusUpdate": { … } }` としてラップします: ```json theme={null} { "statusUpdate": { "taskId": "task_456", "contextId": "ctx_123", "status": { "state": "TASK_STATE_INPUT_REQUIRED", "message": { "role": "ROLE_AGENT", "parts": [ { "text": "Campaign budget $150K requires VP approval" }, { "data": { "reason": "BUDGET_EXCEEDS_LIMIT" } } ] }, "timestamp": "2026-01-22T10:15:00.000Z" } } } ``` **すべてのステータスペイロードは AdCP スキーマを使用します**: 最終ステータス(completed/failed)も中間ステータス(working, input-required, submitted)も [`async-response-data.json`](https://adcontextprotocol.org/schemas/v3/core/async-response-data.json) に参照がある対応スキーマを持ちます。中間ステータスのスキーマは策定中で将来変更される可能性があるため、実装者は緩めに扱う選択も可能です。 ### A2A Webhook のペイロード種別 [A2A 1.0 仕様](https://a2a-protocol.org/latest/specification/#433-push-notification-payload)に従い、サーバーは `StreamResponse` oneof でラップした異なるペイロードタイプを送信します: | エンベロープキー | 内側ペイロード | いつ使うか | 何を含むか | | ---------------- | ------------------------- | ---------------------------------------------------------------------- | ----------------------------------------- | | `task` | `Task` | 最終状態(`completed`, `failed`, `canceled`, `rejected`)や完全なコンテキストが必要な場合 | 履歴とアーティファクトデータを含む完全なタスクオブジェクト | | `statusUpdate` | `TaskStatusUpdateEvent` | 実行中のステータス遷移(`working`, `input-required`, `auth-required`, `submitted`) | メッセージパートを含む軽量ステータス更新 | | `artifactUpdate` | `TaskArtifactUpdateEvent` | ストリーミングによるアーティファクト更新 | `append` / `lastChunk` フラグ付きのアーティファクトチャンク | | `message` | `Message` | 帯域外のエージェントメッセージ | タスクステータス遷移に紐づかないメッセージ | AdCP では主に次の 2 つが多くなります: * `{ task }`: 最終結果(`completed`, `failed`, `rejected`) * `{ statusUpdate }`: 進捗更新(`working`, `input-required`, `auth-required`) クライアントはフィールドを読む前に単一キーのエンベロープをアンラップします。非ストリーミングレスポンス(例: `tasks/get`)は素のペイロードを配信します — そこでは単一キーエンベロープのアンラップは no-op です。 **エンベロープのセマンティクス:** * **`{ artifactUpdate }`** フレームは、ブール値フラグ `append`(名前付きアーティファクトにパートを連結)と `lastChunk`(最終チャンクを示す)付きの増分アーティファクトチャンクを運びます。ストリームを消費する AdCP クライアントは、これらをターゲットアーティファクトに蓄積し、終端状態の `{ task }` フレームが到着したときに抽出アルゴリズムを適用すべきです(SHOULD)。プッシュ通知を消費するクライアントは通常、すでにマージされた `Task` オブジェクトを受け取り、個々の `artifactUpdate` フレームを無視できます。A2A 1.0 §7.3 を参照。 * **`{ message }`** フレームは、タスクステータス遷移に紐づかない帯域外のエージェントメッセージです。AdCP はタスク指向です — タスク向けクライアントは素の `message` エンベロープをログして無視すべきです(SHOULD)。 ### Webhook が送信される条件 Webhooks are sent when **all** of these conditions are met: 1. **Task type supports async** (e.g., `create_media_buy`, `sync_creatives`, `get_products`) 2. **`pushNotificationConfig` is provided** in the request 3. **Task runs asynchronously** — initial response is `working` or `submitted` 初回レスポンスがすでに終端(`completed`, `failed`, `rejected`)なら Webhook は送信されません。結果はその場で得られます。 **Webhook を送るステータス変化:** * `working` → 進捗更新(処理中) * `input-required` → 人による入力が必要 * `auth-required`(1.0) → 実行中の再認証チャレンジ * `completed` → 最終結果 * `failed` → エラー詳細 * `rejected`(1.0) → `adcp_error` 付きのポリシー/検証拒否 * `canceled` → キャンセル確定 ### データスキーマのバリデーション A2A Webhook の DataPart `data` フィールドはステータス別スキーマを使用します: | Status | Schema | Contents | | -------------------- | ------------------------------------------- | ------------------------------------ | | `completed` | `[task]-response.json` | Full task response (success branch) | | `failed` | `[task]-response.json` | Full task response (error branch) | | `rejected`(1.0) | `[task]-response.json`(error branch) | `adcp_error` 付きのポリシー/検証拒否 | | `working` | `[task]-async-response-working.json` | Progress info (`percentage`, `step`) | | `input-required` | `[task]-async-response-input-required.json` | Requirements, approval data | | `auth-required`(1.0) | `[task]-async-response-auth-required.json` | Auth challenge (scheme, URL, scopes) | | `submitted` | `[task]-async-response-submitted.json` | Acknowledgment (usually minimal) | スキーマ参照: [`async-response-data.json`](https://adcontextprotocol.org/schemas/v3/core/async-response-data.json) ### Webhook ハンドラーの例 ```javascript theme={null} const express = require('express'); const app = express(); app.post('/webhooks/a2a/:taskType/:operationId', async (req, res) => { const { taskType, operationId } = req.params; const rawBody = req.body; // Webhook の正当性検証(Bearer トークン例) const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: 'Missing Authorization header' }); } const token = authHeader.substring(7); if (token !== process.env.A2A_WEBHOOK_TOKEN) { return res.status(401).json({ error: 'Invalid token' }); } // Unwrap A2A 1.0 StreamResponse envelope: { task } | { statusUpdate } | { artifactUpdate } | { message } const envelopeKeys = ['task', 'message', 'statusUpdate', 'artifactUpdate']; const bodyKeys = Object.keys(rawBody || {}); const webhook = (bodyKeys.length === 1 && envelopeKeys.includes(bodyKeys[0])) ? rawBody[bodyKeys[0]] : rawBody; // Extract basic fields from A2A webhook payload const taskId = webhook.id || webhook.taskId; const contextId = webhook.contextId; const status = webhook.status?.state || webhook.status; // Normalize 1.0 / v0.3 state values const normalizeState = (s) => s?.replace(/^TASK_STATE_/, '').toLowerCase().replace(/_/g, '-'); const normalizedStatus = normalizeState(status); // Detect Part type by field presence (1.0) with kind fallback (v0.3) const isDataPart = (p) => p.data != null || p.kind === 'data'; const isTextPart = (p) => typeof p.text === 'string' || p.kind === 'text'; // Extract AdCP data based on status let adcpData, textMessage; const FINAL = ['completed', 'failed', 'canceled', 'rejected']; if (FINAL.includes(normalizedStatus)) { // FINAL STATES: Extract from .artifacts (fallback to status.message.parts) const artifactParts = webhook.artifacts?.[0]?.parts; const dataPart = artifactParts?.find(isDataPart) ?? webhook.status?.message?.parts?.find(isDataPart); const textPart = artifactParts?.find(isTextPart) ?? webhook.status?.message?.parts?.find(isTextPart); adcpData = dataPart?.data; textMessage = textPart?.text; } else { // INTERIM STATES: Extract from status.message.parts (optional) const dataPart = webhook.status?.message?.parts?.find(isDataPart); const textPart = webhook.status?.message?.parts?.find(isTextPart); adcpData = dataPart?.data; textMessage = textPart?.text; } // Handle status changes (normalized works for both 1.0 and v0.3 wire values) switch (normalizedStatus) { case 'input-required': // 人に入力が必要であることを通知 await notifyHuman({ task_id: taskId, context_id: contextId, message: textMessage, data: adcpData }); break; case 'auth-required': // A2A 1.0: re-authenticate and resume the task // SECURITY: validate challenge_url against the agent's registered origin // before opening/fetching. See A2A Response Extraction §Auth Challenge URL Validation. if (!isValidChallengeUrl(adcpData?.challenge_url, agentAuthOrigin(taskId))) { return res.status(400).json({ error: 'Invalid challenge_url for agent' }); } await startAuthChallenge({ task_id: taskId, auth_scheme: adcpData?.auth_scheme, challenge_url: adcpData.challenge_url, scopes: adcpData?.scopes // show to user for fresh consent, do not auto-grant }); break; case 'completed': // 完了したオペレーションを処理 if (adcpData?.media_buy_id) { await handleMediaBuyCreated({ media_buy_id: adcpData.media_buy_id, packages: adcpData.packages }); } break; case 'failed': // 失敗を処理 await handleOperationFailed({ task_id: taskId, error: adcpData?.adcp_error ?? adcpData?.errors, message: textMessage }); break; case 'rejected': // A2A 1.0: policy/validation rejection with structured adcp_error await handleOperationRejected({ task_id: taskId, error: adcpData?.adcp_error, message: textMessage }); break; case 'working': // 進捗 UI を更新 await updateProgress({ task_id: taskId, percentage: adcpData?.percentage, message: textMessage }); break; case 'canceled': await handleOperationCanceled(taskId); break; } // 正常処理時は必ず 200 を返す res.status(200).json({ status: 'processed' }); }); ``` ## コンテキスト管理(A2A 固有) **主要な利点**: A2A はコンテキストを自動管理するため、`context_id` を手動で扱う必要はありません。 ### 自動コンテキスト ```javascript theme={null} // 最初のリクエスト - A2A が自動でコンテキストを作成 const response1 = await a2a.send({ message: { role: "ROLE_USER", parts: [{ text: "Find premium video products" }] } }); // 後続リクエスト - A2A が自動でコンテキストを保持 const response2 = await a2a.send({ message: { role: "ROLE_USER", parts: [{ text: "Filter for sports content" }] } }); // システムが自動で前回のリクエストに紐づける ``` ### 明示的コンテキスト(任意) ```javascript theme={null} // 明示的に制御したい場合 const response2 = await a2a.send({ contextId: response1.contextId, // 任意 - A2A が追跡済み message: { role: "ROLE_USER", parts: [{ text: "Refine those results" }] } }); ``` **MCP との違い**: MCP の手動 context\_id 管理と異なり、A2A はプロトコルレベルでセッション継続を扱います。 ## マルチモーダルメッセージ(A2A 固有) A2A の特徴は、1 つのメッセージ内にテキスト・データ・ファイルを組み合わせられることです: ### コンテキスト付きクリエイティブアップロード ```javascript theme={null} // キャンペーンコンテキスト付きでクリエイティブを送信 const response = await a2a.send({ message: { role: "ROLE_USER", parts: [ { text: "Add this hero video to the premium sports campaign" }, { data: { skill: "sync_creatives", parameters: { media_buy_id: "mb_12345", action: "upload_and_assign" } } }, { url: "https://cdn.example.com/hero-30s.mp4", filename: "sports_hero_30s.mp4", mediaType: "video/mp4" } ] } }); ``` ### キャンペーンブリーフ + アセット ```javascript theme={null} // 完全なキャンペーンブリーフを送信 await a2a.send({ message: { role: "ROLE_USER", parts: [ { text: "Campaign brief and assets for Q1 launch" }, { url: "https://docs.google.com/campaign-brief.pdf", filename: "Q1_campaign_brief.pdf", mediaType: "application/pdf" }, { data: { budget: 250000, kpis: ["reach", "awareness", "conversions"], target_launch: "2024-01-15" } } ] } }); ``` ## 利用可能なスキル すべての AdCP タスクは A2A スキルとして利用できます。確実な実行には明示的な呼び出しを使用してください: **タスク管理**: 全ドメインにわたる非同期追跡、ポーリングパターン、Webhook 連携の詳細は [Webhooks](/docs/building/by-layer/L3/webhooks) を参照。 ### スキルの構造 ```javascript theme={null} // Standard pattern for explicit skill invocation await a2a.send({ message: { role: "ROLE_USER", parts: [{ data: { skill: "skill_name", // Exact name from Agent Card parameters: { // Task-specific parameters // See task documentation for parameters } } }] } }); ``` ### 利用可能なスキル * **Protocol**: `get_adcp_capabilities` (start here to discover agent capabilities) * **Media Buy**: `get_products`, `list_creative_formats`, `create_media_buy`, `update_media_buy`, `sync_creatives`, `get_media_buy_delivery`, `provide_performance_feedback` * **Signals**: `get_signals`, `activate_signal` **タスクパラメータ**: 詳細なパラメータ仕様は [Media Buy](/docs/media-buy) と [Signals](/docs/signals/overview) を参照してください。 ## エージェントカード A2A エージェントは `.well-known/agent.json` の Agent Card で機能を公開します。 ### Agent Card の取得 ```javascript theme={null} // エージェントの機能を取得 const agentCard = await a2a.getAgentCard(); // 利用可能なスキルを列挙 const skillNames = agentCard.skills.map(skill => skill.name); console.log('Available skills:', skillNames); // スキル詳細を取得 const getProductsSkill = agentCard.skills.find(s => s.name === 'get_products'); console.log('Examples:', getProductsSkill.examples); // Pick a transport interface (1.0) const jsonrpc = agentCard.supportedInterfaces?.find( i => i.protocolBinding === 'JSONRPC' && i.protocolVersion === '1.0' ); console.log('Endpoint:', jsonrpc?.url); ``` ### Agent Card 構造の例(A2A 1.0) 1.0 では、v0.3 のトップレベル `url` と `protocolVersion` フィールドが `supportedInterfaces` 配列に置き換えられます。各エントリは 1 つのトランスポートバインディングとプロトコルバージョンを宣伝します。`supportsAuthenticatedExtendedCard` は `capabilities.extendedAgentCard` に移動しました。 ```json theme={null} { "name": "AdCP Media Buy Agent", "description": "AI-powered media buying agent", "version": "1.0.0", "securitySchemes": { "bearerAuth": { "type": "http", "scheme": "bearer" } }, "security": [{"bearerAuth": []}], "supportedInterfaces": [ { "url": "https://sales.example.com/a2a/jsonrpc", "protocolBinding": "JSONRPC", "protocolVersion": "1.0" } ], "defaultInputModes": ["text/plain", "application/json"], "defaultOutputModes": ["application/json"], "capabilities": { "streaming": true, "pushNotifications": true, "extendedAgentCard": false }, "skills": [ { "name": "get_products", "description": "Discover available advertising products", "examples": [ "Find premium CTV inventory for sports fans", "Show me video products under $50 CPM" ] } ], "extensions": [ { "uri": "https://adcontextprotocol.org/extensions/adcp", "description": "AdCP media buying protocol support", "required": false, "params": { "adcp_version": "2.6.0", "protocols_supported": ["media_buy"], "extensions_supported": ["sustainability"] } } ] } ``` ### v0.3 互換のためのデュアル宣伝 v0.3 から移行するサーバーは両方のインターフェースを宣伝します。クライアントは理解できるバージョンを選びます: ```json theme={null} { "supportedInterfaces": [ { "url": "https://sales.example.com/a2a/jsonrpc", "protocolBinding": "JSONRPC", "protocolVersion": "1.0" }, { "url": "https://sales.example.com/", "protocolBinding": "JSONRPC", "protocolVersion": "0.3" } ] } ``` Python SDK サーバーはルート構築時に `enable_v0_3_compat=True` も渡す必要があります — 後方互換性はデフォルトでは有効になりません。[A2A Python SDK 1.0 migration guide](https://github.com/a2aproject/a2a-python/blob/v1.0.0/docs/migrations/v1_0/README.md) を参照してください。 ### AdCP 拡張 **推奨**: 実行時の機能発見には [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) を使用してください。エージェントカードの拡張は、レジストリやディスカバリーサービス向けの静的メタデータを提供します。 `extensions` 配列に AdCP 拡張を含めることで、プログラム的に AdCP 対応を宣言できます。 A2A プロトコルでは `extensions` 配列に以下を持つ拡張を列挙します: * **`uri`**: 拡張の識別子(`https://adcontextprotocol.org/extensions/adcp` を使用) * **`description`**: AdCP をどう使うかの説明 * **`required`**: クライアントがこの拡張を必須とするか(AdCP は通常 `false`) * **`params`**: AdCP 固有の設定(下記スキーマ参照) ```javascript theme={null} // エージェントが AdCP に対応しているか確認 const agentCard = await fetch('https://sales.example.com/.well-known/agent.json') .then(r => r.json()); // extensions 配列から AdCP 拡張を取得 const adcpExt = agentCard.extensions?.find( ext => ext.uri === 'https://adcontextprotocol.org/extensions/adcp' ); if (adcpExt) { console.log('AdCP Version:', adcpExt.params.adcp_version); console.log('Supported domains:', adcpExt.params.protocols_supported); // ["media_buy", "creative", "signals"] console.log('Typed extensions:', adcpExt.params.extensions_supported); // ["sustainability"] } ``` **Extension Params**: v2 では `adcp-extension.json` スキーマが使われていましたが、v3 で廃止されました。v3 以降のエージェントでは `get_adcp_capabilities` タスクで実行時に機能を発見してください。上記の `params` オブジェクトは典型的な構造です。 :::note エージェントカードメタデータの `adcp_version` フィールドは v2 の慣習であり、v3 スペックの一部ではありません。v3 のバージョンネゴシエーションでは、バイヤーがすべてのリクエストでリリース精度の `adcp_version`(例: `"3.1"`)を送り、セラーが [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) の `adcp.supported_versions` でサポートするリリースを宣伝し、すべてのレスポンスでエンベロープルートに `adcp_version` をエコーします。レガシーの整数のみの `adcp_major_version` フィールドも後方互換性のため依然受け入れられます。完全な契約は [versioning.mdx § Version negotiation](/docs/reference/versioning#version-negotiation) を参照してください。 ::: **メリット**: * テストコールなしで AdCP 対応状況を発見できます * 実装しているプロトコルドメイン(media\_buy, creative, signals)を宣言できます * バージョンに基づく互換性チェックが可能 ## 統合の例 ```javascript theme={null} // A2A クライアントを初期化 const a2a = new A2AClient({ /* config */ }); // 統一ステータスで処理(Core Concepts を参照) async function handleA2aResponse(response) { switch (response.status) { case 'input-required': // 追加情報要求を処理(パターンは Core Concepts 参照) const input = await promptUser(response.message); return a2a.send({ contextId: response.contextId, message: { role: "ROLE_USER", parts: [{ text: input }] } }); case 'working': // SSE ストリーミングで監視 return streamUpdates(response.taskId); case 'completed': // Extract last DataPart — presence of .data field identifies it in 1.0 const parts = response.artifacts[0].parts; const dataParts = parts.filter(p => p.data != null || p.kind === 'data'); return dataParts[dataParts.length - 1].data; case 'failed': throw new Error(response.message); } } // マルチモーダルメッセージによる使用例 const result = await a2a.send({ message: { role: "ROLE_USER", parts: [ { text: "Find luxury car inventory" }, { data: { skill: "get_products", parameters: { audience: "luxury car intenders" } } } ] } }); const finalResult = await handleA2aResponse(result); ``` ## A2A 固有の考慮点 ### エラーハンドリング 失敗したタスクは、アーティファクトの `DataPart` の `adcp_error` キーに構造化された AdCP エラーを格納します。完全な抽出ロジックと復旧動作は [Transport Error Mapping](/docs/building/operating/transport-errors) を参照してください。 ```javascript theme={null} try { const response = await a2a.send(message); if (response.status === 'failed') { // Check for structured AdCP error in artifacts // Detect DataPart by field presence (1.0) or kind (v0.3) const dataPart = response.artifacts?.[0]?.parts?.find( p => p.data != null || p.kind === 'data' ); const adcpError = dataPart?.data?.adcp_error; if (adcpError) { // code, recovery, retry_after などを含む構造化エラー console.log('AdCP error:', adcpError.code, adcpError.recovery); if (adcpError.recovery === 'transient') { // 遅延後にリトライ await sleep((adcpError.retry_after || 5) * 1000); return retry(); } } throw new Error(response.message); } } catch (a2aError) { // A2A トランスポートエラー(接続、認証など) console.error('A2A Error:', a2aError); } ``` ### クリエイティブアップロードのエラーハンドリング For uploading creative assets and handling validation errors, use the `sync_creatives` task. See [sync\_creatives Task Reference](/docs/creative/task-reference/sync_creatives) for complete testable examples. `@adcp/sdk` ライブラリは A2A アーティファクトの抽出を自動で処理するため、レスポンス構造を手動で解析する必要はありません。 ## ベストプラクティス 1. **ハイブリッドメッセージ**(テキスト + データ + 必要に応じてファイル)を活用 2. アーティファクト処理前に **status フィールド** を確認 3. 長時間処理には **SSE ストリーミング** でリアルタイム更新 4. ステータス処理パターンは **Core Concepts** を参照 5. 利用可能なスキルと例は **エージェントカード** で確認 ## 次のステップ * **Core Concepts**: ステータス処理とワークフローは [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照 * **Task Reference**: [Media Buy Tasks](/docs/media-buy) と [Signals](/docs/signals/overview) * **Protocol Comparison**: [MCP integration](/docs/building/by-layer/L0/mcp-guide) と比較 * **Examples**: 完全なワークフロー例は Core Concepts に掲載 **ステータス処理、非同期オペレーション、確認フローについては [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照してください。このガイドは A2A トランスポート固有の内容に絞っています。** # A2A レスポンス抽出 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L0/a2a-response-extraction A2A Task オブジェクトから AdCP レスポンスデータを抽出する方法: ステータスベースの分岐、last-DataPart 権威、ラッパー拒否、クライアント実装要件。 このページは、A2A Task オブジェクトと TaskStatusUpdateEvents から AdCP レスポンスデータを抽出する規範的アルゴリズムを定義します。セラーが生成しなければならない正準レスポンス構造については [A2A Response Format](/docs/building/by-layer/L0/a2a-response-format) を参照。エラー固有の抽出については [Transport Error Mapping](/docs/building/operating/transport-errors) を参照。 ## A2A の上の AdCP 慣例 このページのルールは AdCP 固有のセマンティクスを A2A に重ねます。非 AdCP の A2A エージェントはそれらを強制せず、準拠する出力を生成することを期待されるべきではありません。 * **単一アーティファクト不変条件。** AdCP タスクはすべての出力パーツを含む 1 つのアーティファクトを生成する。クライアントは `artifacts[0]` から読む。セラーが複数の distinct な成果物を必要とする場合、複数のアーティファクトではなく別々のタスクとしてモデル化すべき。 * **Last-DataPart 権威。** 1 つのアーティファクトに複数の DataPart が現れるとき(ストリーミング中に典型的)、最後のものが権威的。以前の DataPart は置き換えられた進捗スナップショット。 * **First-DataPart は interim 用。** `status.message.parts` に複数の DataPart が現れるとき、最初のものが使われる — interim 更新は累積ではなく単一イベントのスナップショット。 * **ラッパー拒否。** `.data` が `{ response: {...} }`(`response` という単一キー)の DataPart は、有効なペイロードではなくフレームワークラッパーのバグとして扱われる。 ## ワイヤー形式の互換性 このアルゴリズムは **A2A 1.0** と **v0.3** の両レスポンスを扱います。抽出は 1 つのワイヤー形式を仮定してはなりません — 同じ AdCP クライアントが v0.3 互換性期間中に両方と話す可能性があります。 **State 値。** `status.state` フィールドは、1.0 では ProtoJSON 形式(`"TASK_STATE_COMPLETED"`、`"TASK_STATE_WORKING"` …)、v0.3 では小文字形式(`"completed"`、`"working"` …)で到着します。クライアントは比較前に正規化します。 **Part 形状。** 1.0 の DataPart は非 null の `data` フィールドを持ち `kind` を持ちません。v0.3 の DataPart は `kind: "data"` と `data` フィールドを持ちます。両方が「`data` フィールドが非 null オブジェクト」を満たします。同じことが TextParts(`text` フィールド存在)と FileParts(1.0 の `url`/`raw`、または v0.3 の `kind: "file"`)にも当てはまります。A2A 1.0 §4.1.6 に従い、Part は厳格な `oneof` です — `text`、`raw`、`url`、`data` の正確に 1 つが設定されます。複数のコンテンツフィールドを持つ Part を受け取るクライアントは、それを不正な形式として扱うべきです(SHOULD)。 **ストリーミングエンベロープ。** A2A 1.0 は、ストリーミングレスポンスとプッシュ通知ペイロードを、`task`、`message`、`statusUpdate`、`artifactUpdate` の正確に 1 つのキーを持つ `StreamResponse` oneof でラップします(A2A 1.0 §3.2.3、§4.3.3)。非ストリーミングレスポンス(例: `tasks/get`、または HTTP 上の v0.3)は素のオブジェクトを配信します。抽出は下のアルゴリズムを適用する前に単一キーエンベロープをアンラップします。 ## ステータスベースの抽出 抽出場所はタスクのステータスに依存します。この表の State 名は正規化された小文字形式で示されます — 生のワイヤー値ではなく正規化された state に対してマッチしてください。 | Status | Type | Data Location | DataPart Selection | | ---------------- | ------------ | ---------------------------------------------------------- | --------------------------------------------------- | | `completed` | Final | `.artifacts[0].parts[]`(フォールバック: `status.message.parts[]`) | 最後の DataPart | | `failed` | Final | `.artifacts[0].parts[]`(フォールバック: `status.message.parts[]`) | 最後の DataPart | | `canceled` | Final | `.artifacts[0].parts[]` | 最後の DataPart(通常なし) | | `rejected` | Final(1.0) | `.artifacts[0].parts[]` | 最後の DataPart(ポリシー/検証拒否の `adcp_error` を運ぶ) | | `working` | Interim | `status.message.parts[]` | 最初の DataPart | | `submitted` | Interim | `status.message.parts[]` | 最初の DataPart | | `input-required` | Interim | `status.message.parts[]` | 最初の DataPart | | `auth-required` | Interim(1.0) | `status.message.parts[]` | 最初の DataPart(auth チャレンジデータ — scheme、URL、scopes を運ぶ) | Final 状態は、`.artifacts` が欠如または空のとき `status.message.parts[]` にフォールバックします — これは、別のアーティファクトではなくステータスメッセージに最終ペイロードを置くサーバーをカバーします。 Canceled タスクはめったにデータを運びません — DataPart が存在しないとき抽出は null を返し、それが期待されるケースです。Rejected タスクは、リクエストがなぜ拒否されたか(tier/policy/validation)を記述する `adcp_error` DataPart を運ぶことが期待されます。 ## 抽出アルゴリズム クライアントは、これらのステップを使って A2A レスポンスから AdCP データを抽出しなければなりません(MUST): 0. **ストリームエンベロープをアンラップ。** 入力が `task`、`message`、`statusUpdate`、`artifactUpdate` という正確に 1 つのトップレベルキーを持つオブジェクトで、そのキーの値が非 null・非配列オブジェクトなら、入力をその値で置き換える(A2A 1.0 `StreamResponse` oneof)。素の `Task` / `TaskStatusUpdateEvent` オブジェクト — 非ストリーミングレスポンスまたは v0.3 — は変更なく通過。`artifactUpdate` はタスクステータスを運ばない。アンラップされると `status.state` は欠如しステップ 1 は null を返す。 **正確に一度** アンラップする。クライアントは再帰してはならない(MUST NOT)。アンラップされた内部オブジェクト自体が単一キーエンベロープ形状(`{ task: { task: {...} } }` または任意の組み合わせ)を持つ場合、不正な形式として扱い null を返す — これはネストされたエンベロープの密輸試行。内部値のトップレベルキーが `task` / `message` / `statusUpdate` / `artifactUpdate` のいずれかを含むエンベロープは拒否されなければならない(MUST)。 素の `{ message }` エンベロープ(帯域外エージェントメッセージ)はタスク指向の抽出器によって無視されなければならない(MUST) — アンラップされたオブジェクトが `status.state` を持たないときステップ 1 は null を返す。Webhook/SSE ハンドラーは、認識されない `{ message }` エンベロープに `200 OK` 承認を返してはならない(MUST NOT)。エンドポイントをプローブする攻撃者への存在オラクルとして動作するのを避けるため、`400 Bad Request` を返すかトランスポート層で黙って破棄する。 1. **`status.state` を読む。** 欠如なら null を返す。比較前に小文字形式に正規化(`TASK_STATE_COMPLETED` → `completed`)。正規化後、state は **正確な ASCII 文字列等価** で既知の final/interim トークンの 1 つに一致しなければならない(MUST)。クライアントは、繰り返しのセパレーターを折り畳んだり、空白をトリムしたり、ASCII 小文字を超えた Unicode case-folding を適用したりしてはならない(MUST NOT)。他の任意の値 — クライアントが認識しない新しい `TASK_STATE_*` 入力を含む — は「unknown」で、抽出は null を返す(ステップ 4)。 2. **Final 状態**(`completed`、`failed`、`canceled`、`rejected`): a. `artifacts[0].parts[]` で DataPart(`data` フィールドが非 null オブジェクトの Part — `kind` の存在にかかわらず)を探す。 b. **最後の** DataPart を権威的として使う([Last-DataPart Authority](#last-datapart-authority) を参照)。 c. **ラッパーを拒否**: DataPart の `.data` がオブジェクトを含む単一キー `response` を持つ場合、これはフレームワークラッパーのバグ。throw またはエラーをログ。 d. `.data` を返す。 e. **フォールバック**: アーティファクトがない、またはアーティファクトに DataPart がない場合、ステップ 3 を使って `status.message.parts[]` を確認。 3. **Interim 状態**(`working`、`submitted`、`input-required`、`auth-required`): a. `status.message.parts[]` で DataPart を探す。 b. **最初の** DataPart を使う。 c. `.data` を返す、または DataPart が見つからなければ null。 4. **Unknown 状態**: null を返す。前方互換のクライアントは認識されないステータス値で throw すべきではない(SHOULD NOT)。 State 正規化: `TASK_STATE_` プレフィックスを除去、小文字化、アンダースコアをハイフンに置換。これは A2A 1.0(`"TASK_STATE_INPUT_REQUIRED"`)と v0.3(`"input-required"`)の両方を同じ値にマップします。 DataPart 検出はフィールド存在を使います — 1.0 Part `{ "data": {...} }` と v0.3 Part `{ "kind": "data", "data": {...} }` は両方とも「非 null オブジェクト `data` フィールド」テストを満たします。 ```javascript A2A Client theme={null} function normalizeState(state) { if (typeof state !== 'string') return null; return state.replace(/^TASK_STATE_/, '').toLowerCase().replace(/_/g, '-'); } function isDataPart(p) { return p != null && p.data != null && typeof p.data === 'object' && !Array.isArray(p.data); } // A2A 1.0 StreamResponse oneof: { task } | { message } | { statusUpdate } | { artifactUpdate } function unwrapStreamEnvelope(input) { if (input == null || typeof input !== 'object' || Array.isArray(input)) return input; const keys = Object.keys(input); if (keys.length !== 1) return input; const envelopeKeys = ['task', 'message', 'statusUpdate', 'artifactUpdate']; if (envelopeKeys.includes(keys[0]) && typeof input[keys[0]] === 'object' && input[keys[0]] !== null) { return input[keys[0]]; } return input; } function extractAdcpResponseFromA2A(input) { const task = unwrapStreamEnvelope(input); const state = normalizeState(task?.status?.state); if (!state) return null; const FINAL = ['completed', 'failed', 'canceled', 'rejected']; const INTERIM = ['working', 'submitted', 'input-required', 'auth-required']; if (FINAL.includes(state)) { // Final: last DataPart from artifacts[0] const artifact = task.artifacts?.[0]; if (artifact?.parts) { const dataParts = artifact.parts.filter(isDataPart); if (dataParts.length > 0) { const last = dataParts[dataParts.length - 1]; // Reject framework wrappers const keys = Object.keys(last.data); if (keys.length === 1 && keys[0] === 'response' && typeof last.data.response === 'object') { throw new Error( 'Invalid response format: DataPart contains wrapper object {response: {...}}. ' + 'This is a server-side bug.' ); } return last.data; } } // Fallback to status.message.parts return extractFromMessage(task); } if (INTERIM.includes(state)) { return extractFromMessage(task); } return null; // Unknown state } function extractFromMessage(task) { const parts = task.status?.message?.parts; if (!Array.isArray(parts)) return null; const dataPart = parts.find(isDataPart); return dataPart?.data ?? null; } ``` ## Last-DataPart Authority Final 状態については、`artifacts[0].parts[]` の **最後の** DataPart が権威的です。ストリーミング中、中間の DataPart は最終結果に置き換えられる古い進捗データを含みうる: ```json theme={null} { "status": {"state": "TASK_STATE_COMPLETED"}, "artifacts": [{ "parts": [ {"text": "Found products"}, {"data": {"progress": 25}}, {"data": {"products": [...], "total": 12}} ] }] } ``` 抽出されるデータは `{"progress": 25}` ではなく `{"products": [...], "total": 12}` です。 Interim 状態については、interim 更新が累積ではなく単一イベントのスナップショットなので、**最初の** DataPart が使われます。 ## ラッパー拒否 クライアントは、`.data` がフレームワーク固有のオブジェクトでラップされた DataPart を拒否しなければなりません(MUST): ```json theme={null} // REJECTED: wrapper detected {"data": {"response": {"products": [...]}}} // ACCEPTED: direct payload {"data": {"products": [...]}} ``` 検出ルール: `.data` が値がオブジェクトの `response` という正確に 1 つのキーを持つ場合、それはラッパーです。これはサーバー側のバグです — クライアントは黙ってアンラップするのではなく throw またはエラーをログすべきです。 ラッパー検出は **Final 状態のみ**(アーティファクト)に適用されます。Interim ステータスメッセージは軽量な進捗スナップショットです — `status.message.parts` にラッパー検出は不要です。 **例外**: 他のキーと並んで `response` を持つ `.data` オブジェクトはラッパーでは **ありません**: ```json theme={null} // NOT a wrapper — response is one of several keys {"data": {"response": {...}, "status": "completed", "errors": []}} ``` ## エラー抽出との関係 このアルゴリズムは、エラーペイロード(`adcp_error`)を含む A2A レスポンスから *任意の* AdCP データを抽出します。エラー固有の抽出([Transport Error Mapping](/docs/building/operating/transport-errors))は、抽出されたデータで `adcp_error` キーを確認する特殊化です。 transport-errors 仕様は、すべてのアーティファクトを `adcp_error` についてスキャンする独自の `extractAdcpErrorFromA2A` 関数を提供します。その関数はエラー検出(すべてのパーツをエラーキーについてスキャン)に最適化されています。この関数は汎用の抽出器(最初のアーティファクトからの最後の DataPart)です。単一の `adcp_error` DataPart を持つ failed タスクについては、両方が等価な結果を生成します。 典型的なクライアントフロー: ```javascript theme={null} function handleA2aResponse(task) { const data = extractAdcpResponseFromA2A(task); // Check if the extracted data is an error if (data?.adcp_error) { return handleError(data.adcp_error); } return handleSuccess(data); } ``` ## セキュリティ考慮事項 ### セラー制御データ `.artifacts[].parts[].data` と `status.message.parts[].data` のすべてのデータはセラー制御です。[Transport Error Mapping](/docs/building/operating/transport-errors#security-considerations) のプロンプトインジェクション、データ境界、サイズ制限の要件が適用されます。 ### プロトタイプ汚染 クライアントは、キーをフィルターせずに抽出された DataPart ペイロードを `Object.assign` やスプレッド経由でアプリケーション状態にマージしてはなりません(MUST NOT)。マージ前に期待されるタスクレスポンススキーマに対して検証してください。 ### FilePart URI 検証 A2A レスポンスは FilePart を含みうる。1.0 ではこれらは `url` フィールド(参照によるファイル)または `raw` フィールド(base64 バイト)を運ぶ Part。v0.3 では `uri` フィールドを伴う `kind: "file"` を運ぶ。クライアントは、URL が `https` スキームを使い、userinfo コンポーネントを含まず、期待されるドメイン許可リストに一致することを検証しなければならない(MUST)。`javascript:`、`data:`、`file:`、`http:` URI を拒否。`raw` パーツについては、受け入れる前に最大デコードサイズを強制する。 ### Auth チャレンジ URL 検証 `auth-required` を扱うとき、セラーは `status.message.parts` に auth チャレンジ — 通常 `auth_scheme`、`challenge_url`、`scopes` のようなフィールドを持つ DataPart — を送る。クライアントが開くまたはフェッチするセラー制御の URL は OAuth フィッシングと SSRF ベクター。任意のユーザー向けまたはプログラム的な auth フローを開始する前に、クライアントは `challenge_url` を検証しなければならない(MUST): * スキームは `https` でなければならない(MUST)。`http:`、`javascript:`、`data:`、`file:` を拒否。 * URL は userinfo コンポーネント(`user:pass@host` 形式)を含んではならない(MUST NOT)。 * ホストは、このエージェントカードの認証されたセラーの登録された auth オリジンに一致しなければならない(MUST)。クライアントは、タスクペイロードから導出されるのではなく、Agent Card の `supportedInterfaces[].url` オリジンまたは宣言された `authOrigin` 拡張フィールドからシードされたエージェントごとの許可リストを維持すべき(SHOULD)。 * 任意の `redirect_uri`、`return_url`、または類似のクエリパラメーターは、ナビゲーション前にクライアントによって落とされるか上書きされなければならない(MUST)。セラー供給のリダイレクトを決して転送しない。 * `scopes` は付与ではなくリクエストとして扱われなければならない(MUST)。scopes をユーザーに示し、各チャレンジで新鮮な同意を得る。 クライアントがチャレンジ URL をサーバー側でフェッチする場合、レスポンスサイズとタイムアウトの境界が適用される(例: 256 KB レスポンス上限、10 秒タイムアウト、リダイレクト制限 3)。 ### セラー制御文字列の衛生 すべての `adcp_error.message`、`adcp_error.details.*`、ステータス TextPart コンテンツはセラー制御です。これらを UI にレンダリングするクライアントは、ターゲットコンテキスト(HTML、Slack、CLI)用にエスケープしなければならない(MUST)。それらをログするクライアントは、ログ注入を防ぐため CRLF を除去しなければならない(MUST)。これは `adcp_error` を運ぶすべての状態(`failed`、`rejected`、システム開始の `canceled`)と自由テキストの `status.message` に適用されます。 ### サイズ制限 クライアントはスキーマ検証前に最大 DataPart サイズ(例: 1MB)を強制すべきです(SHOULD)。エラーペイロード(4096 バイトに上限)とは異なり、成功ペイロードはより大きくなりうるが依然として境界が必要です。 ### 仲介者注入 last-DataPart 慣例は、アーティファクトが単一の信頼された送信者から無傷で受け取られることを仮定します。マルチホップシナリオ(buyer → orchestrator → seller)では、仲介者が追加のパーツを注入できます。仲介者を通じて動作するクライアントは、アーティファクトのパーツ数が期待に一致することを検証すべきです(SHOULD)。 ## クライアントライブラリ要件 この仕様を実装するクライアントライブラリは次をしなければなりません(MUST): 1. **A2A 1.0 ストリームエンベロープをアンラップ。** `task`、`message`、`statusUpdate`、`artifactUpdate` のキーを持つ単一キーオブジェクトは `StreamResponse` ラッパー — アルゴリズムの残りを適用する前に内部オブジェクトにアンラップ。素のオブジェクトは変更なく通過。 2. **A2A 1.0 と v0.3 の両ワイヤー形状を受け入れる。** 比較前に `status.state` を正規化(`TASK_STATE_` プレフィックス除去、小文字化、アンダースコアをハイフンに)。`kind` ではなくフィールド存在(`data` が非 null オブジェクト)で DataPart を検出。 3. **正規化された state で分岐。** Final 状態(`completed`、`failed`、`canceled`、`rejected`)はアーティファクトを使い、interim 状態(`working`、`submitted`、`input-required`、`auth-required`)は `status.message.parts` を使う。 4. **Final 状態には最後の DataPart を使う。** null、非オブジェクト、配列 `.data` の DataPart をスキップ。 5. **Interim 状態には最初の DataPart を使う。** 6. **ラッパーを検出し拒否。** 単一キー `{response: {...}}` ペイロードはバグ。 7. **優雅にフォールバック。** Final 状態でアーティファクトが空なら、`status.message.parts` を確認。 8. **Unknown 状態を扱う。** null を返し、throw しない。 ## テストベクター 機械可読なテストベクターは [`/static/test-vectors/a2a-response-extraction.json`](https://adcontextprotocol.org/test-vectors/a2a-response-extraction.json) で利用可能です。各ベクターは次を含みます: * `status`: A2A タスクステータス * `path`: 抽出パス(`artifact`、`status_message`、または `none`) * `response`: A2A Task または TaskStatusUpdateEvent * `expected_data`: 抽出されるべき AdCP データ(または `null`) * `expected_error_type`: 存在する場合、抽出は throw すべき(例: `wrapper_detected`) クライアントライブラリはこれらのベクターに対して抽出ロジックを検証すべきです(SHOULD)。 ## 関連項目 * [A2A Response Format](/docs/building/by-layer/L0/a2a-response-format) — セラーの正準レスポンス構造 * [Transport Error Mapping](/docs/building/operating/transport-errors) — MCP と A2A からのエラー抽出 * [MCP Response Extraction](/docs/building/by-layer/L0/mcp-response-extraction) — MCP の同等仕様 * [A2A Guide](/docs/building/by-layer/L0/a2a-guide) — A2A トランスポート統合 # A2A レスポンス形式 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L0/a2a-response-format A2A プロトコルで送信される AdCP レスポンスに必要な DataPart 構造、完了タスクおよび非同期タスクのアーティファクトレイアウト、Agent-to-Agent Protocol におけるステータス別レスポンスパターン。 このドキュメントは、A2A プロトコルで送信される AdCP レスポンスの **標準構造** を定義します。 ## A2A ワイヤーフォーマット 以下の例は **A2A 1.0** ワイヤーフォーマットを使用します。Part は `kind` 判別子を持たず(コンテンツタイプはどのフィールドが設定されているか — `text`、`data`、`url`、`raw` — で暗黙的に決まる)、ロールは `ROLE_USER` / `ROLE_AGENT`、タスク状態は `TASK_STATE_*`(ProtoJSON 正準形)です。v0.3 との対比は [A2A ガイド](/docs/building/by-layer/L0/a2a-guide#a2a-protocol-versions)を参照してください。 AdCP のトップレベル統合 `status` フィールド(`@adcp/sdk` が返す)は、引き続き小文字の短縮形(`"completed"`、`"failed"`、`"working"`、`"input-required"`、`"submitted"`)を使用します。これは `status.state` 上の AdCP の抽象化であり、A2A ワイヤー値ではありません。 v0.3 サーバーの場合、同じ DataPart は `{ "kind": "data", "data": {...} }` になり、状態は小文字になります。抽出クライアントは互換期間中に両方の形状を受け入れます。 ## 必須構造 ### 最終レスポンス(status: "completed") **A2A 上の AdCP レスポンスは必ず以下を満たす必要があります:** * タスクのペイロードを含む DataPart(非 null の `data` フィールドを持つ Part)を少なくとも 1 つ含めます * 複数アーティファクトではなく、1 つのアーティファクトに複数パートを入れる * DataPart が複数ある場合は最後のものを正とします * AdCP ペイロードをフレームワーク固有オブジェクトでラップしない(`{ response: {...} }` など禁止) **Recommended pattern:** ```json theme={null} { "status": "completed", "taskId": "task_123", "contextId": "ctx_456", "artifacts": [{ "name": "task_result", "parts": [ { "text": "Found 12 video products perfect for pet food campaigns" }, { "data": { "products": [...], "total": 12 } } ] }] } ``` * **TextPart**(`text` フィールドを持つ Part): 人間向けサマリー — **推奨**(任意) * **DataPart**(`data` フィールドを持つ Part): 構造化された AdCP レスポンスペイロード — **必須** * **FilePart**(`url` または `raw` フィールドを持つ Part): 任意のファイル参照(プレビュー、レポート) **複数アーティファクト:** 本質的に異なる成果物(例: クリエイティブと別個のトラフィッキングレポート)がある場合のみ。AdCP では稀であり、基本は 1 アーティファクト内に複数パートを推奨。 ### 中間レスポンス(working, submitted, input-required, auth-required) 中間ステータス更新は `TaskStatusUpdateEvent` として配信され、任意の進捗/チャレンジデータは(`artifacts` ではなく)`status.message.parts[]` に含まれます。アーティファクトはタスクライフサイクル中に蓄積され、タスクが終端状態に達すると最終成果物として読まれます。 ```json theme={null} { "taskId": "task_123", "contextId": "ctx_456", "status": { "state": "TASK_STATE_WORKING", "timestamp": "2026-01-22T10:15:00.000Z", "message": { "role": "ROLE_AGENT", "parts": [ { "text": "Processing your request. Analyzing 50,000 inventory records..." }, { "data": { "percentage": 45, "current_step": "analyzing_inventory" } } ] } } } ``` SSE 経由またはプッシュ通知として配信される場合、このイベントは A2A 1.0 の `StreamResponse` oneof でラップされます: `{ "statusUpdate": { … } }`。非ストリーミングレスポンス(例: `tasks/get`)は素のオブジェクトを配信します。クライアントは `status.state` を読む前にアンラップします — [A2A レスポンス抽出](/docs/building/by-layer/L0/a2a-response-extraction#extraction-algorithm)を参照してください。 **中間レスポンスの特徴:** * **TextPart** はステータス表示のため推奨 * **DataPart** は任意だが、提供する場合は AdCP スキーマに準拠 * 中間ステータス用スキーマ(`*-async-response-working.json`、`*-async-response-input-required.json` など)は策定中で変わる可能性あり * スキーマ進化を踏まえ、中間データの扱いを緩やかにする選択も可能 **最終ステータスになった場合**(`completed`、`failed`、`canceled`、`rejected`)、完全な AdCP タスクレスポンスが `Task` オブジェクトで配信され、DataPart は `.artifacts[0].parts[]` に入ります。 ### フレームワークのラッパー(禁止) **重要**: DataPart の内容はフレームワーク固有オブジェクトでラップせず、AdCP レスポンスペイロードを直接含める必要があります。 ```json theme={null} // ❌ WRONG - Wrapped in custom object { "data": { "response": { // ← Framework wrapper "products": [...] } } } // ✅ CORRECT - Direct AdCP payload { "data": { "products": [...] // ← Direct schema-compliant response } } ``` **理由:** * スキーマ検証が破綻する(クライアントは `products` がルートにあると期待) * 不要なネストが増える * プロトコル非依存設計に反する(ラッパーがフレームワーク依存) * クライアントでのデータ抽出が複雑化 **実装がラッパーを追加している場合**、クライアントサイドで回避するのではなく、フレームワーク層のバグとして修正すべきです。 ## クライアントの標準的な扱い このセクションでは、クライアントが A2A プロトコルレスポンスから AdCP レスポンスを抽出する方法を正確に定義します。 ### クイックリファレンス | Status | Webhook Type | Data Location | Schema Required? | Returns | | --------------------- | ----------------------- | --------------------------------------------------- | ------------------------- | ------------------------------------ | | `working` | `TaskStatusUpdateEvent` | `status.message.parts[]` | ✅ Yes (if present) | `{ status, taskId, message, data? }` | | `submitted` | `TaskStatusUpdateEvent` | `status.message.parts[]` | ✅ Yes (if present) | `{ status, taskId, message, data? }` | | `input-required` | `TaskStatusUpdateEvent` | `status.message.parts[]` | ✅ Yes (if present) | `{ status, taskId, message, data? }` | | `auth-required` (1.0) | `TaskStatusUpdateEvent` | `status.message.parts[]` | ✅ Yes (auth challenge) | `{ status, taskId, message, data }` | | `completed` | `Task` | `.artifacts[]` (fallback: `status.message.parts[]`) | ✅ Required | `{ status, taskId, message, data }` | | `failed` | `Task` | `.artifacts[]` (fallback: `status.message.parts[]`) | ✅ Required | `{ status, taskId, message, data }` | | `rejected` (1.0) | `Task` | `.artifacts[]` | ✅ Required (`adcp_error`) | `{ status, taskId, message, data }` | **ポイント**: * **最終ステータス** は `Task` オブジェクトを用い、データは `.artifacts` に格納。サーバーに構造化ペイロードがない場合(例: JSON-RPC パースエラー、タスク前の認証失敗)、`status.message.parts` にテキストメッセージのみを置くことがある — クライアントはその場所にフォールバックする。 * **中間ステータス** は `TaskStatusUpdateEvent` を用い、`status.message.parts[]` に任意データ。 * **ストリーム/Webhook 配信** はペイロードを A2A 1.0 の `StreamResponse` oneof(`{ task }`、`{ statusUpdate }`、`{ artifactUpdate }`、`{ message }`)でラップする。クライアントはフィールドを読む前にアンラップする。 * いずれのステータスもデータがある場合は AdCP スキーマを使用。 * 中間ステータスのスキーマは策定中で変わる可能性あり。 ### ルール1: ステータスに応じた処理 クライアントは、正しいデータ抽出場所を決定するため、正規化されたステータスで分岐しなければなりません。ここで参照する `status` は AdCP の統合小文字値(例: `"completed"`)です。`status.state` の生の A2A ワイヤー値は 1.0 では `TASK_STATE_COMPLETED`、v0.3 では `completed` です。比較する前に正規化してください — [A2A レスポンス抽出](/docs/building/by-layer/L0/a2a-response-extraction#extraction-algorithm)を参照してください。 ```javascript theme={null} const INTERIM = ['working', 'submitted', 'input-required', 'auth-required']; const FINAL = ['completed', 'failed', 'canceled', 'rejected']; function handleA2aResponse(response) { const status = response.status; // AdCP unified status // 中間ステータス - status.message.parts から抽出(TaskStatusUpdateEvent) if (INTERIM.includes(status)) { return { status: status, taskId: response.taskId, contextId: response.contextId, message: extractTextPartFromMessage(response), data: extractDataPartFromMessage(response), // Optional AdCP data (required for auth-required) }; } // 最終ステータス - .artifacts から抽出(Task オブジェクト)、status.message にフォールバック if (FINAL.includes(status)) { return { status: status, taskId: response.taskId, contextId: response.contextId, message: extractTextPartFromArtifacts(response) ?? extractTextPartFromMessage(response), data: extractDataPartFromArtifacts(response) ?? extractDataPartFromMessage(response), }; } // 前方互換: 未知の将来状態は null を返し、throw しない return { status, taskId: response.taskId, contextId: response.contextId, message: null, data: null }; } ``` **重要**: * **中間ステータス**: `TaskStatusUpdateEvent` → `status.message.parts[]` から抽出 * **最終ステータス**: `Task` オブジェクト → `.artifacts[0].parts[]` から抽出。アーティファクトが空の場合は `status.message.parts[]` にフォールバック ### Rule 2: Data Extraction Helpers Extract data from the appropriate location based on webhook type: ```javascript theme={null} // Part-type detectors: field presence (A2A 1.0) with kind fallback (v0.3) const isDataPart = (p) => p.data != null && typeof p.data === 'object' && !Array.isArray(p.data); const isTextPart = (p) => typeof p.text === 'string'; // For FINAL statuses (Task object) - extract from .artifacts, return null if absent function extractDataPartFromArtifacts(response) { const dataParts = response.artifacts?.[0]?.parts?.filter(isDataPart) || []; if (dataParts.length === 0) return null; // caller falls back to status.message.parts // Use LAST data part as authoritative const lastDataPart = dataParts[dataParts.length - 1]; const payload = lastDataPart.data; // CRITICAL: Payload MUST be direct AdCP response, not a framework wrapper. // A wrapper is a single-key object { response: {...} } — reject it. // Objects that have 'response' alongside other keys are NOT wrappers. const keys = Object.keys(payload); if (keys.length === 1 && keys[0] === 'response' && typeof payload.response === 'object') { throw new Error( 'Invalid response format: DataPart contains wrapper object. ' + 'Expected direct AdCP payload (e.g., {products: [...]}) ' + 'but received {response: {products: [...]}}. ' + 'This is a server-side bug that must be fixed.' ); } return payload; } function extractTextPartFromArtifacts(response) { const textPart = response.artifacts?.[0]?.parts?.find(isTextPart); return textPart?.text || null; } // For INTERIM statuses (TaskStatusUpdateEvent) - extract from status.message.parts function extractDataPartFromMessage(response) { const dataPart = response.status?.message?.parts?.find(isDataPart); return dataPart?.data || null; } function extractTextPartFromMessage(response) { const textPart = response.status?.message?.parts?.find(isTextPart); return textPart?.text || null; } ``` これらの検出器は両方のワイヤーフォーマットで動作します。1.0 の DataPart は `data` が設定されている(`kind` なし)、v0.3 の DataPart は `kind: "data"` と `data` が設定されている — どちらも `p.data != null` を満たします。 ### ルール3: スキーマ検証 すべての AdCP レスポンスはスキーマを用いますが、検証方法はステータスによって異なります: ```javascript theme={null} function validateResponse(response, taskName) { const status = response.status; let data, schemaName; // ステータスに応じてデータを抽出しスキーマを決定 if (INTERIM.includes(status)) { // 中間: status.message.parts の任意データ data = extractDataPartFromMessage(response); if (data) { // 中間ステータス専用スキーマ(策定中) schemaName = `${taskName}-async-response-${status}.json`; // 任意: スキーマが変わる可能性があるため中間検証を省略してもよい if (STRICT_VALIDATION_MODE) { validateAgainstSchema(data, loadSchema(schemaName)); } } } else if (FINAL.includes(status)) { // 最終: .artifacts から必須データ(status.message.parts にフォールバック) data = extractDataPartFromArtifacts(response) ?? extractDataPartFromMessage(response); schemaName = `${taskName}-response.json`; // 最終レスポンスは必ず検証 if (!validateAgainstSchema(data, loadSchema(schemaName))) { throw new Error( `Response payload does not match ${taskName} schema. ` + `Ensure DataPart contains direct AdCP response structure.` ); } } } ``` **スキーマ進化の注意**: 中間ステータスのスキーマ(`*-async-response-working.json` など)は策定中です。安定するまでは緩やかな扱いにする選択も可能です。 ### 完全な例 Task と TaskStatusUpdateEvent の両方を正しく扱う統合例: ```javascript theme={null} async function executeTask(taskName, params) { const response = await a2aClient.send({ task: taskName, params: params }); // 1. ステータスに基づいて正しい場所から抽出 const result = handleA2aResponse(response); // 2. スキーマ検証 validateResponse(response, taskName); return result; } // 使い方 const result = await executeTask('get_products', { brief: 'CTV inventory in California' }); // ステータス別の処理 if (result.status === 'working') { // TaskStatusUpdateEvent - data は status.message.parts console.log('Processing:', result.message); if (result.data) { console.log('Progress:', result.data.percentage + '%'); } } else if (result.status === 'input-required') { // TaskStatusUpdateEvent - data from status.message.parts console.log('Input needed:', result.message); console.log('Reason:', result.data?.reason); } else if (result.status === 'completed') { // Task オブジェクト - data は .artifacts console.log('Success:', result.message); console.log('Products:', result.data.products); // Full AdCP response } ``` ## Last Data Part Authority パターン
このパターンの理由 ストリーミング処理では、中間レスポンスに古い進捗データが含まれることがあります: ```json theme={null} // Working status with progress { "status": "working", "artifacts": [{ "parts": [ {"text": "Searching inventory..."}, {"data": {"progress": 25}} ] }] } // Completed - last data part is authoritative { "status": "completed", "artifacts": [{ "parts": [ {"text": "Found 12 products"}, {"data": {"progress": 25}}, // Old {"data": {"products": [...], "total": 12}} // ← Authoritative ] }] } ``` **Note:** This is an AdCP-specific convention, not required by A2A protocol. Document this in your Agent Card when serving non-AdCP clients.
## Test Cases ### ✅ Correct Behavior ```javascript theme={null} // Test 1: Working status (TaskStatusUpdateEvent) - extract from status.message.parts const workingResponse = { taskId: 'task_123', contextId: 'ctx_456', status: { state: 'TASK_STATE_WORKING', message: { role: 'ROLE_AGENT', parts: [ { text: 'Processing inventory...' }, { data: { percentage: 50, current_step: 'analyzing' } } ] } } }; const result1 = handleA2aResponse(workingResponse); assert(result1.data.percentage === 50, 'Should extract data from status.message.parts'); assert(result1.message === 'Processing inventory...', 'Should extract text from status.message.parts'); // Test 2: Completed status (Task) - extract from .artifacts const completedResponse = { taskId: 'task_123', contextId: 'ctx_456', status: { state: 'TASK_STATE_COMPLETED', timestamp: '2026-01-22T10:30:00.000Z' }, artifacts: [{ parts: [ { text: 'Found 3 products' }, { data: { products: [...], total: 3 } } ] }] }; const result2 = handleA2aResponse(completedResponse); assert(result2.data !== undefined, 'Completed status must have data'); assert(Array.isArray(result2.data.products), 'Data should be direct AdCP payload'); // Test 3: Wrapper detection (should reject) const wrappedResponse = { taskId: 'task_123', status: { state: 'TASK_STATE_COMPLETED' }, artifacts: [{ parts: [ { data: { response: { products: [...] } } } ] }] }; assert.throws(() => { extractDataPartFromArtifacts(wrappedResponse); }, /Invalid response format.*wrapper/); ``` ### ❌ Incorrect Behavior (Common Mistakes) ```javascript theme={null} // 誤り: 中間ステータスで抽出元を間違える function badHandleWorking(response) { // ❌ TaskStatusUpdateEvent doesn't have .artifacts - data is in status.message.parts const data = response.artifacts?.[0]?.parts?.find(isDataPart)?.data; return { status: 'working', data }; // Will be null/undefined! } // 誤り: completed で抽出元を間違える function badHandleCompleted(response) { // ❌ Task object has data in .artifacts, not in status.message.parts const data = response.status?.message?.parts?.find(p => p.data)?.data; return { status: 'completed', data }; // Will be null/undefined! } // 誤り: ラッパーを確認しない function badExtraction(response) { const payload = response.artifacts[0].parts[0].data; // ❌ Returns { response: { products: [...] } } instead of { products: [...] } return payload; // Client receives wrong structure! } // 誤り: ネストされた response を参照 function badClientUsage(result) { // ❌ クライアントコードがこうする必要はない const products = result.data.response.products; // 正しくは: result.data.products } ``` ## エラーハンドリング ### タスクレベルのエラー(部分失敗) タスクは実行されたが完全には完了しなかった場合。`status: "completed"` の DataPart に `errors` 配列を入れます: ```json theme={null} { "status": "completed", "taskId": "task_123", "artifacts": [{ "parts": [ { "text": "Signal discovery completed with partial results" }, { "data": { "signals": [...], "errors": [{ "code": "NO_DATA_IN_REGION", "message": "No signal data available for Australia", "field": "deliver_to.countries[1]", "details": { "requested_country": "AU", "available_countries": ["US", "CA", "GB"] } }] } } ] }] } ``` **errors 配列を使う場面:** * プラットフォーム認可の問題(`PLATFORM_UNAUTHORIZED`) * データが部分的にしかない場合 * データの一部でバリデーション問題がある場合 ### プロトコルレベルのエラー(致命的) タスクが実行できなかった場合。`status: "failed"` とメッセージを返します: ```json theme={null} { "taskId": "task_456", "status": "failed", "message": { "role": "ROLE_AGENT", "parts": [{ "text": "Authentication failed: Invalid or expired API token" }] } } ``` **`status: failed` を使う場面:** * 認証失敗(無効/期限切れトークン) * リクエスト不正(JSON 破損、必須フィールド欠落) * リソース不在(未知の taskId、期限切れ context) * システムエラー(DB 不調、内部サービス障害) ### エラーの所在: 決定ルール 配置は、サーバーが何を持っているか、どの状態にあるかで選択されます: | 状況 | 状態 | 場所 | ペイロード | | ---------------------------- | ----------- | --------------------------------- | ------------------------------------- | | タスク実行、一部失敗 | `completed` | `artifacts[0].parts[]` DataPart | `{ , errors: [...] }` | | 構造化エラーで失敗 | `failed` | `artifacts[0].parts[]` DataPart | `{ adcp_error: {...} }` | | ポリシー/検証による拒否(1.0) | `rejected` | `artifacts[0].parts[]` DataPart | `{ adcp_error: {...} }` | | システム起因のキャンセル(タイムアウト、上流障害) | `canceled` | `artifacts[0].parts[]` DataPart | `{ adcp_error: {...} }` | | ユーザー起因のキャンセル(`tasks/cancel`) | `canceled` | `status.message.parts[]` TextPart | 人間可読テキストのみ | | プロトコル/トランスポート障害、アーティファクト未生成 | `failed` | `status.message.parts[]` TextPart | 人間可読テキストのみ | **目安:** サーバーが構造化エラーデータを持つ場合、それを DataPart としてアーティファクトに入れる。`status.message` は、タスクアーティファクトが一度も生成されなかったケース(JSON-RPC パースエラー、認証ハンドシェイク失敗、不正リクエスト、詳細のないユーザー起因キャンセル)向けのフリーテキストフォールバックだ。A2A 1.0 §3.7 もこれを補強する: *「メッセージはタスク出力の配信に使うべきではない。結果はアーティファクトで返すべきである。」* **`rejected` vs `failed`。** サーバーがタスクの試行を拒否する場合(作業開始前のポリシー/ティア/検証チェック)は `rejected` を使う。作業が開始されて致命的なエラーに遭遇した場合は `failed` を使う。どちらもアーティファクトに `adcp_error` を運ぶ — 状態は障害が*いつ*発生したかを区別し、それが呼び出し元側で異なるリトライと UX 挙動を駆動する。 **キャンセル起源はセラー帰属ではなくクライアントで照合される。** `status.state: "canceled"`(または `TASK_STATE_CANCELED`)は、キャンセルがユーザー起因かシステム起因かを呼び出し元に伝えない — セラーは、実際にはユーザー起因だったキャンセルについて、バイヤーの帳簿やリトライロジックを誤らせるために `adcp_error` をアーティファクトに置くこともできる。クライアントはキャンセル起源をローカルで照合しなければなりません(MUST): この `taskId` について未処理の `tasks/cancel` リクエストがある場合、ペイロードに関わらずキャンセルをユーザー起因として扱い、セラーが付加した `adcp_error` を無視します。クライアントは、セラーが送った `adcp_error.recovery` ヒントを根拠にユーザー起因のキャンセルをリトライしてはなりません(MUST NOT)。 ## ステータスマッピング AdCP は A2A の TaskState enum をそのまま使用します: | A2A Status | Payload Type | Data Location | AdCP Usage | | --------------------- | ----------------------- | ------------------------------------------------ | -------------------------------------------------------------------- | | `completed` | `Task` | `.artifacts` | Task finished successfully, data in DataPart, optional errors array | | `failed` | `Task` | `.artifacts` (or `status.message` for text-only) | Fatal error preventing completion, `adcp_error` when structured | | `rejected` (1.0) | `Task` | `.artifacts` | Policy/validation rejection, `adcp_error` with rejection reason | | `canceled` | `Task` | `.artifacts` (typically none) | Task canceled by user or system | | `input-required` | `TaskStatusUpdateEvent` | `status.message.parts` | Need user input/approval, data + text explaining what's needed | | `auth-required` (1.0) | `TaskStatusUpdateEvent` | `status.message.parts` | Authentication challenge during task execution (scheme, URL, scopes) | | `working` | `TaskStatusUpdateEvent` | `status.message.parts` | Processing (\< 120s), optional progress data | | `submitted` | `TaskStatusUpdateEvent` | `status.message.parts` | Long-running (hours/days), minimal data, use webhooks/polling | ## Webhook ペイロード 非同期処理(`status: "submitted"`)では Webhook でも同じアーティファクト構造を返します: ```json theme={null} POST /webhook-endpoint { "taskId": "task_123", "status": "completed", "timestamp": "2026-01-22T10:30:00.000Z", "artifacts": [{ "parts": [ {"text": "Media buy approved and live"}, {"data": { "media_buy_id": "mb_456", "packages": [...], "creative_deadline": "2026-01-30T23:59:59.000Z" }} ] }] } ``` AdCP データは同じ Last DataPart パターンで抽出します。**Webhook 認証、リトライパターン、セキュリティ** は [Webhooks](/docs/building/by-layer/L3/webhooks) を参照してください。 ## レスポンス内の File Part クリエイティブ系の操作ではファイル参照を含む場合があります: ```json theme={null} { "status": "completed", "artifacts": [{ "parts": [ {"text": "Creative uploaded and preview generated"}, {"data": { "creative_id": "cr_789", "format_id": { "agent_url": "https://creatives.adcontextprotocol.org", "id": "video_standard_30s" }, "status": "ready" }}, {"url": "https://cdn.example.com/cr_789/preview.mp4", "filename": "preview.mp4", "mediaType": "video/mp4"} ] }] } ``` **File Part の用途:** プレビュー URL、生成済みアセット、トラフィッキングレポート。**AdCP レスポンスの生データには使わず**、必ず DataPart を使用。 ## リトライと冪等性 ### TaskId による重複排除 A2A の `taskId` はリトライ検出に使えます。エージェントは次を行うべきです: * `taskId` が完了済みオペレーションと一致する場合(TTL 内)、キャッシュレスポンスを返す * 進行中のオペレーションに対する重複 `taskId` 送信は拒否します ```json theme={null} // Duplicate taskId during active operation { "taskId": "task_123", "status": "failed", "message": { "role": "ROLE_AGENT", "parts": [{ "text": "Task 'task_123' is already in progress. Use tasks/get to check status." }] } } ``` ## 例
Product Discovery 成功 ```json theme={null} { "status": "completed", "taskId": "task_001", "contextId": "ctx_abc", "artifacts": [{ "name": "product_catalog", "parts": [ { "text": "Found 8 CTV products targeting sports fans under $50 CPM" }, { "data": { "products": [ { "product_id": "ctv_sports_premium", "name": "Premium Sports CTV" } // ... 7 more products ] } } ] }] } ```
承認が必要な Media Buy ```json theme={null} { "status": "input-required", "taskId": "task_002", "contextId": "ctx_def", "artifacts": [{ "name": "approval_request", "parts": [ { "text": "Media buy exceeds auto-approval limit ($100K). Please approve to proceed." }, { "data": { "media_buy_id": "mb_pending_456", "packages": [ { "package_id": "pkg_pending_001", "status": "pending_approval" }, { "package_id": "pkg_pending_002", "status": "pending_approval" } ], "creative_deadline": "2025-02-01T23:59:59Z" } } ] }] } ```
部分的失敗を含む Signal Discovery ```json theme={null} { "status": "completed", "taskId": "task_003", "contextId": "ctx_ghi", "artifacts": [{ "name": "signal_results", "parts": [ { "text": "Found 3 signals for luxury automotive. Note: No data available for Australia region." }, { "data": { "signals": [ { "signal_id": "lux_auto_us", "name": "Luxury Auto Intenders - US", "reach": 2500000 } ], "total": 3, "errors": [{ "code": "NO_DATA_IN_REGION", "message": "No signal data available for requested region: Australia", "field": "deliver_to.countries[1]", "details": { "requested_country": "AU", "available_countries": ["US", "CA", "GB"] } }] } } ] }] } ```
プラットフォーム認可の問題(タスクレベルエラー) プラットフォームや操作固有の認可失敗はタスクレベルのエラーです: ```json theme={null} { "status": "completed", "taskId": "task_004", "contextId": "ctx_jkl", "artifacts": [{ "name": "signal_activation_result", "parts": [ { "text": "Signal activation failed: Account not authorized for Peer39 data on PubMatic" }, { "data": { "errors": [{ "code": "PLATFORM_UNAUTHORIZED", "message": "Account 'brand-456-pm' not authorized for Peer39 data on PubMatic. Contact your PubMatic account manager to enable access.", "details": { "platform": "pubmatic", "account_id": "brand-456-pm", "data_provider": "peer39" } }] } } ] }] } ```
プロトコルレベルの失敗(致命的) 認証失敗はプロトコルレベルのエラーです: ```json theme={null} { "taskId": "task_005", "status": "failed", "message": { "parts": [{ "text": "Authentication failed: Invalid or expired API token. Please refresh your credentials and retry." }] } } ```
## Implementation Checklist When implementing A2A responses for AdCP: **Final Responses (status: "completed" or "failed") - Use `Task` object:** * [ ] **Always include status field** from TaskState enum * [ ] **Use `.artifacts` array with at least one DataPart** containing AdCP response payload * [ ] **Include TextPart** with human-readable message (recommended for UX) * [ ] **Use single artifact with multiple parts** (not multiple artifacts) * [ ] **Use last DataPart as authoritative** if multiple exist * [ ] **Never nest AdCP data in custom wrappers** (no `{ response: {...} }` objects) * [ ] **DataPart content MUST match AdCP schemas** (validate against `[task]-response.json`) **Interim Responses (status: "working", "submitted", "input-required") - Use `TaskStatusUpdateEvent`:** * [ ] **Use `status.message.parts[]` for optional data** (not `.artifacts`) * [ ] **TextPart** is recommended for human-readable status updates * [ ] **DataPart** is optional but follows AdCP schemas when provided (`[task]-async-response-[status].json`) * [ ] **Interim schemas are work-in-progress** - clients may handle more loosely * [ ] **Include progress indicators** when applicable (percentage, current\_step, ETA) **Error Handling:** * [ ] **Use `status: "failed"` for protocol errors only** (auth, invalid params, system errors) * [ ] **Use `errors` array for task failures** (platform auth, partial data) with `status: "completed"` **General:** * [ ] **Include taskId and contextId** for tracking * [ ] **Follow discriminated union patterns** for task responses (check schemas) * [ ] **Use correct payload type**: `Task` for final states, `TaskStatusUpdateEvent` for interim * [ ] **Support taskId-based deduplication** for retry detection ## See Also * [A2A Guide](/docs/building/by-layer/L0/a2a-guide) - Complete A2A integration guide * [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) - Status handling patterns * [Error Handling](/docs/building/by-layer/L3/error-handling) - Fatal vs non-fatal errors * [Protocol Comparison](/docs/building/concepts/protocol-comparison) - MCP vs A2A differences # L0 — ワイヤーとトランスポート Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L0/index AdCP スタックのワイヤーとトランスポート層。JSON-over-HTTP フレーミング、MCP メッセージエンベロープ、A2A SSE ストリーム、スキーマ検証、言語ネイティブ型生成。 L0 はプロトコルバイトをワイヤーから取り出して型付きのインメモリ値に変え、またはアウトバウンドリクエストを公開されたスキーマに対してシリアライズします。両側で対称 — 同じプリミティブ、鏡映された方向。 ## L0 の SDK が提供しなければならないもの SDK を選ぶか新しい言語に移植する場合、これが L0 のビルドターゲットです: * 公開された JSON スキーマからの **生成された言語ネイティブ型**(リクエスト/レスポンスペアごとに 1 型、加えて共有リソース型)。 * バンドルされたスキーマに対して配線された **スキーマ検証器** — そのため採用者はスキーマロードのダンスを手書きせずにインバウンドとアウトバウンドのペイロードを検証できる。 * \{MCP, A2A} の少なくとも一方の **トランスポートアダプター**。理想的には両方。これらは通常、上流プロトコル SDK を再実装するのではなくラップする。 * 採用者にパスをハードコードさせずにアクティブな AdCP バージョンの正しいスキーマファイルを見つける **スキーマバンドルアクセサー**。 累積的なクロス層のストーリー(L0+L1+L2+L3 が何をもたらすか)については、[SDK スタックリファレンス](/docs/building/cross-cutting/sdk-stack#l0--wire--transport) を参照。 ## この層のページ * **[Schemas](/docs/building/by-layer/L0/schemas)** — スキーマバンドル、サプライチェーン検証、型生成、バージョンピン留め。 * **[MCP guide](/docs/building/by-layer/L0/mcp-guide)** — `tools/call` エンベロープ、JSON-RPC 2.0、トランスポートアダプター形状。 * **[A2A guide](/docs/building/by-layer/L0/a2a-guide)** — SSE イベントストリーム、タスクフレーミング、アーティファクト抽出。 * **[A2A response format](/docs/building/by-layer/L0/a2a-response-format)** — A2A ワイヤー形式リファレンス。 * **[MCP response extraction](/docs/building/by-layer/L0/mcp-response-extraction)** — `tools/call` レスポンスを型付き値にパース。 * **[A2A response extraction](/docs/building/by-layer/L0/a2a-response-extraction)** — A2A ストリームとアーティファクトをパース。 # MCP ガイド Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L0/mcp-guide AdCP MCP 統合ガイド: Model Context Protocol 実装のためのツールコールパターン、context_id 管理、レスポンス解析、ワイヤフォーマット。 Model Context Protocol を使って AdCP を統合するためのトランスポート別ガイドです。タスク処理、ステータス管理、ワークフローパターンは [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照してください。 ## MCP 経由で AdCP をテスト [CLI ツール](/docs/building/schemas-and-sdks#cli-tools) を使うか、AgenticAdvertising.org のアシスタント [Addie](https://agenticadvertising.org) とチャットして AdCP タスクをテストできます。 ## ツールコールパターン ### 基本のツール呼び出し ```javascript theme={null} // Standard MCP tool call const response = await mcp.call('get_products', { brand: { domain: "premiumpetfoods.com" }, brief: "Video campaign for pet owners" }); // All responses include status field (AdCP 1.6.0+) console.log(response.status); // "completed" | "input-required" | "working" | etc. console.log(response.message); // Human-readable summary ``` ### フィルター付きツール呼び出し ```javascript theme={null} // Structured parameters const response = await mcp.call('get_products', { brand: { domain: "betnow.com" }, brief: "Sports betting app for March Madness", filters: { channels: ["ctv"], delivery_type: "guaranteed", max_cpm: 50 } }); ``` ### アプリケーションレベルのコンテキスト付き呼び出し ```javascript theme={null} // Pass opaque application-level context; agents must carry it back const response = await mcp.call('build_creative', { target_format_id: { agent_url: 'https://creative.agent', id: 'premium_bespoke_display' }, creative_manifest: { /* ... */ }, context: { ui: 'buyer_dashboard', session: '123' } }); // Response includes the same context at the top level console.log(response.context); // { ui: 'buyer_dashboard', session: '123' } ``` ## MCP レスポンス形式 **規範的:** AdCP MCP レスポンスは**フラット構造**を使用します — エンベロープフィールド(`status`、`context_id`、`context`、`task_id`、`timestamp`、`replayed`、`adcp_error`、`governance_context`)とタスクボディフィールドが、ツールレスポンスのルートに兄弟として現れます。[`core/protocol-envelope.json`](https://adcontextprotocol.org/schemas/v3/core/protocol-envelope.json) で定義される `payload` オブジェクトは文書上のグルーピング構造であり、シリアライズされるワイヤーキーでは**ありません**: ボディフィールドは MCP 上で `payload:` キーの下にネストされ**ません**。これは MCP のネイティブな `structuredContent` の慣習に一致します。 ```json theme={null} { "status": "completed", // envelope: unified task status "message": "Found 5 products", // envelope: human-readable summary "context_id": "ctx-abc123", // envelope: session identifier (server-managed) "context": { "ui": "buyer_dashboard" }, // envelope: per-request opaque echo (caller-owned) "timestamp": "2026-05-19T14:25:30Z", // envelope: response generation time "products": [...], // body: task-specific data, sibling of envelope fields "errors": [...] // body: per-record / payload-level errors (warning severity allowed) } ``` **プロデューサールール。** MCP ツール実装は、エンベロープフィールドとボディフィールドをルートにフラットな兄弟として発行しなければなりません(MUST)。ボディフィールドを `payload:` キーの下にネストするのは非コンフォーマントです — レシーバーはフラットなルートから解析し、ネストされた表現はすべての出荷済み SDK を壊します。 **レシーバールール。** MCP ツールコンシューマーは、ツールレスポンスのフラットなルートからエンベロープとボディのフィールドを解析しなければなりません(MUST)。レシーバーはネストされた `payload:` キーを要求してはなりません(MUST NOT)。スキーマの `payload` はドキュメントであり、ワイヤー要件ではありません。レスポンスに `status` が不在の場合(レガシーまたはトランスポートネイティブの状態キャリア)、レシーバーは非エラーレスポンスについて `completed` をデフォルトとし、エラーエンベロープについては `adcp_error` を検査しなければなりません(MUST)。 **`context_id` vs `context` — 意味的に直交。** * `context_id` は、複数のツール呼び出しにわたって関連オペレーションを追跡するための**サーバー管理のセッション識別子**です。サーバーがそれを発行し、呼び出し元はセッションをつなぐため後続の呼び出しでエコーしてもよい(MAY)。MCP のトランスポートレベルセッションとは別物です。 * `context` は、**呼び出し元が供給する不透明なエコーオブジェクト**([`core/context.json`](https://adcontextprotocol.org/schemas/v3/core/context.json))です — エージェントは解析せずにバイト単位で保持します。バイヤー側の相関(UI セッション ID、トレース ID、カスタムメタデータ)に使われます。 * 両方が同じレスポンスに現れてもよい(MAY)。これらはエイリアスでは**ありません**。 **ステータス処理**: 完全なステータス処理パターンは [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照してください。 ## 利用可能なツール すべての AdCP タスクは MCP ツールとして利用できます: ### プロトコルツール ```javascript theme={null} await mcp.call('get_adcp_capabilities', {...}); // Discover agent capabilities (start here) ``` ### Media Buy ツール ```javascript theme={null} await mcp.call('get_products', {...}); // Discover inventory await mcp.call('list_creative_formats', {...}); // Get format specs await mcp.call('create_media_buy', {...}); // Create campaigns await mcp.call('update_media_buy', {...}); // Modify campaigns await mcp.call('sync_creatives', {...}); // Manage creative assets await mcp.call('get_media_buy_delivery', {...}); // Performance metrics await mcp.call('provide_performance_feedback', {...}); // Share outcomes ``` ### Signals ツール ```javascript theme={null} await mcp.call('get_signals', {...}); // Discover audience signals await mcp.call('activate_signal', {...}); // Deploy signals to platforms ``` **タスクパラメータ**: [Media Buy](/docs/media-buy) および [Signals](/docs/signals/overview) セクションの各タスクドキュメントを参照。 ## トランスポートラッパーとしての MCP Tasks AdCP のタスクライフサイクル状態はアプリケーション層の状態です。MCP Tasks は `tools/call` リクエストをラップして、LLM ではなく MCP クライアントが `CallToolResult` を待てるようにします。これらは AdCP の `task_id`、ステータスペイロード、Webhook、ポーリング/リコンシリエーション面を置き換えません。 タスク拡張された MCP 呼び出しは、`status` がまだ `submitted` である AdCP ペイロードを配信した後に正常に完了できます。その時点から、メディアバイ、クリエイティブ、シグナル、またはガバナンスのワークフローは AdCP 層で開いたままであり、Webhook または AdCP ポーリングで観測すべきです。 :::warning クライアントサポートは限定的 ほとんどのチャットベース MCP クライアント(Claude Desktop、Cursor)はまだ MCP Tasks をサポートしていません。クライアントがタスク拡張ツール呼び出しをサポートしない場合、代わりに標準の `tools/call` に **Webhook** または **AdCP ポーリング**を加えて使用してください — これらは任意の MCP クライアントで動作します。トランスポート非依存のパターンは [Async Operations](/docs/building/by-layer/L3/async-operations) と [Push Notifications](/docs/building/by-layer/L3/webhooks) を参照してください。 MCP Tasks は、MCP クライアントを自分で制御する場合(例: `@modelcontextprotocol/sdk` で独自のオーケストレーターを構築)に、初回の `tools/call` 結果のプロトコルレベルの待機が欲しいときに有用です。これらは任意のトランスポート配管であり、正準の AdCP タスクストアではありません。 ::: ### SDK 実装 `@modelcontextprotocol/sdk` パッケージを使う場合、MCP Tasks のサポートは最小限のコードで済みます。`InMemoryTaskStore`(または独自の `TaskStore` 実装)を Server コンストラクターに渡します — SDK が `tasks/get`、`tasks/result`、`tasks/list`、`tasks/cancel` のハンドラーを自動登録します: ```typescript theme={null} import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { InMemoryTaskStore } from '@modelcontextprotocol/sdk/experimental/tasks'; const taskStore = new InMemoryTaskStore(); const server = new Server( { name: 'my-adcp-agent', version: '1.0.0' }, { capabilities: { tools: {}, tasks: { list: {}, cancel: {}, requests: { tools: { call: {} } }, }, }, taskStore, }, ); ``` `tools/call` ハンドラーで、`task` フィールドを確認してストアを使います: ```typescript theme={null} server.setRequestHandler(CallToolRequestSchema, async (request, extra) => { const taskField = request.params.task; const result = await executeMyTool(request.params); if (!taskField) return result; // Synchronous path // Task-augmented: extra.taskStore handles requestId, sessionId, // and sends notifications/tasks/status on completion const task = await extra.taskStore.createTask({ ttl: taskField.ttl }); await extra.taskStore.storeTaskResult( task.taskId, result.isError ? 'failed' : 'completed', result, ); return { task: await extra.taskStore.getTask(task.taskId) }; }); ``` SDK はポーリング、キャンセル、TTL クリーンアップ、`tasks/result` レスポンスの `_meta` 注入を処理します。`InMemoryTaskStore` は非永続です — 本番では、データベースでバックアップされた `TaskStore` を実装してください。 `Server` の代わりに `McpServer` を使う場合、`server.experimental.tasks.registerToolTask()` でタスク対応ツールを登録します — 高レベル API は `taskSupport` を宣言するツールについてこれを強制します。 :::warning 本番のタスク分離 `InMemoryTaskStore` はタスクをセッションでスコープしません — タスク ID を知る任意のクライアントがそれを読み取り、キャンセル、リストできます。本番では、すべてのオペレーションで `sessionId` によりフィルタリングする `TaskStore` を実装してください。また、クライアント提供の TTL 値をサーバー側でクランプし、タスク作成にレート制限を強制してください。 ::: ### サーバーケイパビリティ AdCP MCP サーバーはケイパビリティで `tasks` を宣言します: ```json theme={null} { "capabilities": { "tools": {}, "tasks": { "list": {}, "cancel": {}, "requests": { "tools": { "call": {} } } } } } ``` ### ツールレベルのタスクサポート 各ツールは、`execution.taskSupport` を通じてタスク拡張実行をサポートするかどうかを宣言します: | ツール | `taskSupport` | 根拠 | | ------------------------ | ------------- | ------------------------- | | `get_products` | `optional` | 複雑な検索、HITL の明確化 | | `create_media_buy` | `optional` | 外部システム、承認ワークフロー | | `update_media_buy` | `optional` | 外部システム更新 | | `build_creative` | `optional` | 人間のクリエイティブレビュー、長時間の制作レンダー | | `sync_creatives` | `optional` | アセット処理とトランスコード | | `get_signals` | `optional` | 複雑なオーディエンス発見 | | `activate_signal` | `optional` | プラットフォームデプロイ | | `sync_plans` | `optional` | ガバナンスプラン処理 | | `check_governance` | `optional` | 外部ポリシー評価 | | `report_plan_outcome` | `optional` | 外部システム更新 | | `acquire_rights` | `optional` | 承認ワークフロー | | `update_rights` | `optional` | 外部更新 | | `get_rights` | `optional` | 外部ルックアップ | | `get_adcp_capabilities` | `forbidden` | 即時、静的 | | `list_creative_formats` | `forbidden` | 即時カタログルックアップ | | `preview_creative` | `forbidden` | 既存マニフェストをレンダー | | `list_creatives` | `forbidden` | セッション状態ルックアップ | | `get_media_buys` | `forbidden` | セッション状態ルックアップ | | `get_media_buy_delivery` | `forbidden` | セッション状態ルックアップ | | `get_creative_delivery` | `forbidden` | セッション状態ルックアップ | | `get_plan_audit_logs` | `forbidden` | セッション状態ルックアップ | | `get_brand_identity` | `forbidden` | 即時ルックアップ | `taskSupport: "optional"` のツールはどちらの方法でも呼び出せます: * **`task` フィールドなし**: 同期 — 結果を直接返す * **`task` フィールドあり**: 即座に `CreateTaskResult` を返す。トランスポートネイティブの `tasks/get` で MCP タスクをポーリングし、トランスポートネイティブの `tasks/result` で `CallToolResult` を取得し、その結果内の AdCP ペイロードを検査する。 ### ツールをタスクとして呼び出す `tools/call` リクエストに `task` フィールドを含めます: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_products", "arguments": { "buying_mode": "brief", "brief": "Premium CTV inventory for luxury auto" }, "task": { "ttl": 3600000 } } } ``` サーバーは即座にタスクハンドルを返します: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "task": { "taskId": "786512e2-9e0d-44bd-8f29-789f320fe840", "status": "working", "statusMessage": "Searching inventory for luxury auto CTV placements", "createdAt": "2025-11-25T10:30:00Z", "lastUpdatedAt": "2025-11-25T10:30:00Z", "ttl": 3600000, "pollInterval": 5000 } } } ``` クライアントは、タスクが終端状態(`completed`、`failed`、`cancelled`)に達するまで `tasks/get` で MCP トランスポートタスクをポーリングし(`pollInterval` を尊重)、その後 `tasks/result` で `CallToolResult` を取得します。トランスポートタスクを中止するには、MCP `taskId` を付けて `tasks/cancel` を送ります。 `CallToolResult` を取得した後、AdCP レスポンスペイロードを検査します。それが `status: "submitted"` と AdCP `task_id` を含む場合、トランスポートタスクはキューイングされた AdCP レスポンスを配信しましたが、アプリケーションワークフローはまだ開いています。Webhook または AdCP ポーリング(`get_task_status`、または 3.x のレガシー `tasks/get`)で続行します。 ### MCP タスクステータス vs. AdCP ステータス AdCP は MCP Tasks より豊富なステータスセットを使用します。実装が AdCP の進捗をトランスポートネイティブの MCP タスクにミラーする場合、このマッピングは MCP ラッパーにのみ使用してください。AdCP ペイロードがドメインワークフロー状態の真実の源のままです: | AdCP ステータス | MCP タスクステータス | 備考 | | ---------------- | ---------------- | ---------------------------------------------------------------- | | `working` | `working` | 直接マッピング | | `submitted` | `working` | キュー状態を示すため `statusMessage` を使う | | `input-required` | `input_required` | サーバーがタスクを `input_required` に移動し、`tasks/result` で elicitation を送る | | `completed` | `completed` | 直接マッピング | | `failed` | `failed` | 直接マッピング | | `rejected` | `failed` | 拒否理由には `statusMessage` を使う | | `canceled` | `cancelled` | スペルの違い(AdCP は米式、MCP は英式) | | `auth-required` | `input_required` | Elicitation がクレデンシャルを要求 | ### 長寿命オペレーションのための Webhook MCP Tasks は MCP セッション内での待機を処理しますが、多くの AdCP オペレーションは単一のセッションより長く続きます(例: パブリッシャー承認に 24 時間かかるメディアバイ)。これらについては、AdCP 呼び出しに `push_notification_config` を登録します: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "create_media_buy", "arguments": { "buyer_ref": "nike_q1_2025", "packages": [], "push_notification_config": { "url": "https://buyer.com/webhooks/adcp/create_media_buy/op_abc123", "authentication": { "schemes": ["HMAC-SHA256"], "credentials": "shared_secret_32_chars" } } }, "task": { "ttl": 86400000 } } } ``` MCP タスクはセッション内のトランスポートラッパーを追跡します。Webhook は AdCP アプリケーションタスクを独立して追跡し、MCP セッション終了後も有効なままです。Webhook のペイロード形式と認証は [Push Notifications](/docs/building/by-layer/L3/webhooks) を参照してください。 ## コンテキスト管理(MCP 固有) **重要**: MCP はコンテキストを手動管理する必要があります。会話状態を保つには `context_id` を渡してください。 ### コンテキストセッションパターン ```javascript theme={null} class McpAdcpSession { constructor(mcpClient) { this.mcp = mcpClient; this.contextId = null; } async call(tool, params, options = {}) { // Build request with protocol-level fields const request = { tool: tool, arguments: params }; // Include context from previous calls if (this.contextId) { request.context_id = this.contextId; } // Include webhook configuration (protocol-level, A2A-compatible) if (options.push_notification_config) { request.push_notification_config = options.push_notification_config; } // Optionally augment with an MCP Task wrapper if (options.task) { request.task = options.task; } const response = await this.mcp.callTool(request); // Save context for next call if (response.context_id) { this.contextId = response.context_id; } return response; } reset() { this.contextId = null; } } ``` ### 使用例 #### 基本的なコンテキスト付きセッション ```javascript theme={null} const session = new McpAdcpSession(mcp); // First call - no context needed const products = await session.call('get_products', { brief: "Sports campaign" }); // Follow-up - context automatically included const refined = await session.call('get_products', { brief: "Focus on premium CTV" }); // Session remembers previous interaction ``` #### MCP Tasks を用いた非同期処理 `taskSupport: "optional"` のツールでは、`task` オプションを渡して MCP Tasks を使います: ```javascript theme={null} const session = new McpAdcpSession(mcp); // Synchronous call (no task augmentation) const products = await session.call('get_products', { buying_mode: 'brief', brief: "Sports campaign" }); // Task-augmented call for a long-running operation const result = await session.call('create_media_buy', { packages: [...], }, { task: { ttl: 86400000 }, // 24-hour TTL push_notification_config: { // Webhook backup for session-outliving ops url: "https://buyer.com/webhooks/adcp/create_media_buy/op_abc123", authentication: { schemes: ["HMAC-SHA256"], credentials: "shared_secret_32_chars" } } } ); // result is a CreateTaskResult for the MCP wrapper. // After tasks/result, inspect the AdCP payload; if it is still submitted, // continue via webhook or AdCP get_task_status / legacy tasks/get. ``` **Webhook POST format:** ```json theme={null} { "task_id": "task_456", "status": "completed", "timestamp": "2025-01-22T10:30:00Z", "result": { "media_buy_id": "mb_12345", "packages": [...] } } ``` **Note:** レシーバーは、Webhook URL を解析するのではなく、ペイロードボディの `operation_id`(および `task_type`)を使って Webhook を相関しなければなりません(MUST)。バイヤーは自身のサーバー側ルーティングの便宜のため `operation_id` を URL パスやクエリに埋め込んでもよい(MAY、URL 構造はセラーにとって不透明で完全にバイヤー定義)が、セラーはその URL を決して解析しません — セラーは登録時に渡されたバイヤー供給の `operation_id` をエコーし、相関のワイヤーレベルの真実の源はペイロードフィールドです。[`mcp-webhook-payload.json`](https://adcontextprotocol.org/schemas/v3/core/mcp-webhook-payload.json) と [Webhooks — Operation IDs](/docs/building/by-layer/L3/webhooks#operation-ids-and-url-templates) を参照してください。 `result` フィールドには AdCP のデータペイロードが入ります。`completed`/`failed` ではタスクレスポンス全体(例: `create-media-buy-response.json`)、それ以外のステータスではステータス別スキーマ(例: `create-media-buy-async-response-working.json`)を使用します。 #### MCP Webhook のエンベロープフィールド [`mcp-webhook-payload.json`](https://adcontextprotocol.org/schemas/v3/core/mcp-webhook-payload.json) には以下が含まれます: **必須フィールド:** * `idempotency_key` — 発火ごとのトランスポート重複排除キー(完全なセマンティクスはスキーマを参照) * `operation_id` — バイヤー供給の相関識別子で、セラーがそのままエコーする。レシーバーは URL パスでは**なく**これを使って通知を発信元タスクにルーティングする。セラーは URL を解析してこれを導出してはならない(MUST NOT)。URL 構造はセラーの視点からは実装依存である。 * `task_id` — 相関用の一意なタスク ID * `task_type` — タスクごとのハンドラーにルーティングするためのタスク名(例: `create_media_buy`, `sync_creatives`) * `status` — 現在のタスクステータス(completed, failed, working, input-required など) * `timestamp` — Webhook 生成時の ISO 8601 タイムスタンプ **任意フィールド:** * `notification_id` — 再発行追跡のためのイベント層の安定 ID(スキーマを参照) * `protocol` — AdCP プロトコルファミリー(`media-buy` または `signals`) * `context_id` — 会話/セッション ID * `message` — ステータス変更に関する人間向けコンテキスト **Data フィールド:** * `result` — タスク固有の AdCP ペイロード(下記のデータスキーマ検証を参照) #### Webhook が送信される条件 Webhook は次の **すべて** を満たす場合に送信されます: 1. **タスクが非同期をサポート**(例: `create_media_buy`, `sync_creatives`, `get_products`) 2. リクエストに **`pushNotificationConfig` が指定** されています 3. **タスクが非同期実行** — 初回レスポンスが `working` または `submitted` 初回レスポンスがすでに終端(`completed`, `failed`, `rejected`)なら、結果が手元にあるため Webhook は送信されません。 **Webhook を送るステータス変化:** * `working` → 進捗更新(処理中) * `input-required` → 人による入力が必要 * `completed` → 最終結果 * `failed` → エラー詳細 #### データスキーマの検証 MCP Webhook の `result` フィールドはステータス別スキーマを使用します: | Status | Schema | Contents | | ---------------- | ------------------------------------------- | -------------------------- | | `completed` | `[task]-response.json` | 成功ブランチの完全なタスクレスポンス | | `failed` | `[task]-response.json` | エラーブランチの完全なタスクレスポンス | | `working` | `[task]-async-response-working.json` | 進捗情報(`percentage`, `step`) | | `input-required` | `[task]-async-response-input-required.json` | 必要事項、承認情報 | | `submitted` | `[task]-async-response-submitted.json` | 受領通知(通常は最小限) | スキーマ参照: [`async-response-data.json`](https://adcontextprotocol.org/schemas/v3/core/async-response-data.json) #### Webhook Handler Example ```javascript theme={null} const express = require('express'); const app = express(); app.post('/webhooks/adcp/:task_type/:agent_id/:operation_id', async (req, res) => { const { task_type, agent_id, operation_id } = req.params; const webhook = req.body; // Verify webhook authenticity (HMAC-SHA256 example) const signature = req.headers['x-adcp-signature']; const timestamp = req.headers['x-adcp-timestamp']; if (!verifySignature(webhook, signature, timestamp)) { return res.status(401).json({ error: 'Invalid signature' }); } // Handle status changes switch (webhook.status) { case 'input-required': // Alert human that input is needed await notifyHuman({ operation_id, message: webhook.message, context_id: webhook.context_id, data: webhook.result }); break; case 'completed': // Process the completed operation if (task_type === 'create_media_buy') { await handleMediaBuyCreated({ media_buy_id: webhook.result.media_buy_id, packages: webhook.result.packages }); } break; case 'failed': // Handle failure await handleOperationFailed({ operation_id, error: webhook.result?.errors, message: webhook.message }); break; case 'working': // Update progress UI await updateProgress({ operation_id, percentage: webhook.result?.percentage, message: webhook.message }); break; case 'canceled': await handleOperationCanceled(operation_id, webhook.message); break; } // Always return 200 for successful processing res.status(200).json({ status: 'processed' }); }); function verifySignature(payload, signature, timestamp) { const crypto = require('crypto'); const expectedSig = crypto .createHmac('sha256', process.env.WEBHOOK_SECRET) .update(timestamp + JSON.stringify(payload)) .digest('hex'); return signature === `sha256=${expectedSig}`; } ``` #### タスク管理とポーリング ```javascript theme={null} // Check status of a specific AdCP task const taskStatus = await session.call('get_task_status', { task_id: 'task_456', include_result: true }); if (taskStatus.status === 'completed') { console.log('Result:', taskStatus.result); } // State reconciliation const reconciliation = await session.call('list_tasks', { filters: { statuses: ['submitted', 'working', 'input-required'] } }); if (reconciliation.tasks.length > 0) { console.log('Found open AdCP tasks:', reconciliation.tasks); // Start tracking these tasks } ``` ### コンテキスト期限切れの扱い ```javascript theme={null} async function handleContextExpiration(session, tool, params) { try { return await session.call(tool, params); } catch (error) { if (error.message?.includes('context not found')) { // Context expired - start fresh session.reset(); return session.call(tool, params); } throw error; } } ``` **主な違い**: コンテキストを自動管理する A2A と異なり、MCP は `context_id` を明示的に扱う必要があります。 ## 非同期処理の扱い AdCP レスポンスが `working` または `submitted` を返す場合、結果を受け取る方法が必要です。これは MCP クライアントが MCP Tasks をサポートするかどうかに関わらず適用されます — 以下のパターンは任意のクライアントで動作します。 | アプローチ | 最適な用途 | トレードオフ | | ------------- | --------------------- | -------------------------------------------------------- | | **Webhooks** | 本番システム、任意のタスク時間 | 数時間/数日を扱えるが、公開エンドポイントが必要 | | **Polling** | シンプルな統合、短時間タスク | 実装が簡単だが、長時間待機に非効率 | | **MCP Tasks** | MCP SDK を使うカスタムクライアント | 初回 `tools/call` のためのプロトコルネイティブなラッパーだが、AdCP のタスク追跡を置き換えない | ### オプション 1: Webhook(推奨) Webhook URL を設定すると、オペレーション完了時にサーバーが結果を POST します。これは外部依存(パブリッシャー承認、人間のレビュー)でブロックされる `submitted` オペレーションに適したアプローチです。 ```javascript theme={null} const response = await session.call('create_media_buy', { packages: [...], budget: { total: 150000, currency: "USD" } }, { push_notification_config: { url: "https://buyer.com/webhooks/adcp/create_media_buy/op_abc123", authentication: { schemes: ["HMAC-SHA256"], credentials: "shared_secret_32_chars" } } } ); // If status is 'submitted', the server will POST the result to your webhook // No polling needed — just handle the webhook when it arrives ``` ペイロード形式と認証は [Push Notifications](/docs/building/by-layer/L3/webhooks) を参照してください。 ### オプション 2: ポーリング(バックアップ) `submitted` オペレーションのバックアップとして、または Webhook エンドポイントを公開できない場合に AdCP ポーリングを使います。3.x では、セラーが宣伝する場合は `get_task_status` を優先し、そうでなければレガシー `tasks/get` を使います: ```javascript theme={null} async function pollForResult(session, taskId, pollInterval = 30000) { while (true) { const response = await session.call('get_task_status', { task_id: taskId, include_result: true }); if (['completed', 'failed', 'canceled'].includes(response.status)) { return response; } if (response.status === 'input-required') { const input = await promptUser(response.message); return session.call('create_media_buy', { context_id: response.context_id, additional_info: input }); } await new Promise(resolve => setTimeout(resolve, pollInterval)); } } ``` ### ステータス別の扱い ```javascript theme={null} const initial = await session.call('create_media_buy', { packages: [...], budget: { total: 100000, currency: "USD" } }); switch (initial.status) { case 'completed': // Done — result is inline console.log('Created:', initial.media_buy_id); break; case 'working': // Server is actively processing (>30s) — just wait, result will arrive // No polling needed; 'working' is a progress signal, not a polling trigger console.log('Processing:', initial.message); break; case 'submitted': // Blocked on external dependency — use webhook or poll console.log(`Task ${initial.task_id} queued for approval`); break; case 'input-required': // Blocked on user input console.log('Need more info:', initial.message); break; } ``` ## 統合の例 ```javascript theme={null} // コンテキスト管理付きで MCP セッションを初期化 const session = new McpAdcpSession(mcp); // 統一ステータスで処理(Core Concepts を参照) async function handleAdcpCall(tool, params, options = {}) { const response = await session.call(tool, params, options); switch (response.status) { case 'input-required': // 追加情報を処理(パターンは Core Concepts 参照) const input = await promptUser(response.message); return session.call(tool, { ...params, additional_info: input }); case 'working': // Server is actively processing — just wait, result will arrive console.log('Processing:', response.message); return response; case 'submitted': // Blocked on external dependency — webhook or poll console.log(`Task ${response.task_id} submitted, webhook will notify`); return { pending: true, task_id: response.task_id }; case 'completed': return response; // タスク固有フィールドはトップレベル case 'failed': throw new Error(response.message); } } // Example usage const products = await handleAdcpCall('get_products', { brief: "CTV campaign for luxury cars" }); ``` ## MCP 固有の考慮点 ### サーバー側のツールラッパーはエンベロープフィールドを許容しなければならない バイヤー SDK は、エンベロープレベルのフィールド(`idempotency_key`、`context_id`、`context`、`governance_context`、`push_notification_config`)を、それらを消費しない読み取り専用ツールを含め、すべての AdCP ツール呼び出しで一様に送信します。MCP ツール実装はこれらのフィールドを受け入れ、使わないものを無視しなければなりません(MUST)。エンベロープフィールドが存在するという理由で呼び出しを拒否してはなりません(MUST NOT)。よくある罠: * **FastMCP / Pydantic の厳格なシグネチャ** — `idempotency_key: str | None = None`(および他のエンベロープフィールド)を受け入れて無視するオプショナルとして宣言するか、`**kwargs` で未知のものを飲み込みます。入力モデルを制御できる場合は `model_config = ConfigDict(extra='allow')`。 * **Zod / valibot の入力スキーマの `.strict()`** — `.strict()` を外すか、passthrough バリアントを使います。 * **入力モデルに `additionalProperties: false` を注入する OpenAPI codegen** — ジェネレーター設定を修正します。スペックのリクエストスキーマは `additionalProperties: true` を宣言しています。 `idempotency_key` に対して `unexpected_keyword_argument` を送出するラッパーは、エンベロープ契約に従う任意のバイヤー SDK に対してコンプライアンスに失敗します。規範ルールは [security.mdx > Server-side tool wrapper conformance](/docs/building/by-layer/L1/security#server-side-tool-wrapper-conformance) を参照してください。 ### ツールディスカバリー ```javascript theme={null} // List available tools — use get_adcp_capabilities for runtime feature detection const tools = await mcp.listTools(); // Check which tools support async execution const asyncTools = tools.filter(t => t.execution?.taskSupport === 'optional'); ``` ### MCP サーバーカードによる AdCP 拡張 **推奨**: 実行時の機能発見には [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) を使用してください。サーバーカード拡張はツールカタログやレジストリ向けの静的メタデータを提供します。 MCP サーバーは `/.well-known/mcp.json`(または `/.well-known/server.json`)のサーバーカードで AdCP 対応を宣言できます。AdCP 固有メタデータは `adcontextprotocol.org` 名前空間の `_meta` フィールドに記載します。 ```json theme={null} { "name": "io.adcontextprotocol/media-buy-agent", "version": "1.0.0", "title": "AdCP Media Buy Agent", "description": "AI-powered media buying agent implementing AdCP", "tools": [ { "name": "get_products" }, { "name": "create_media_buy" }, { "name": "list_creative_formats" } ], "_meta": { "adcontextprotocol.org": { "adcp_version": "2.6.0", "protocols_supported": ["media_buy"], "extensions_supported": ["sustainability"] } } } ``` **AdCP 対応の検出:** ```javascript theme={null} // Check both possible locations for MCP server card const serverCard = await fetch('https://sales.example.com/.well-known/mcp.json') .then(r => r.ok ? r.json() : null) .catch(() => null) || await fetch('https://sales.example.com/.well-known/server.json') .then(r => r.json()); // Check for AdCP metadata const adcpMeta = serverCard?._meta?.['adcontextprotocol.org']; if (adcpMeta) { console.log('AdCP Version:', adcpMeta.adcp_version); console.log('Supported domains:', adcpMeta.protocols_supported); // ["media_buy", "creative", "signals"] console.log('Typed extensions:', adcpMeta.extensions_supported); // ["sustainability"] } ``` **メリット:** * テストコールなしで AdCP の対応状況を把握できます * 実装しているプロトコルドメイン(media\_buy, creative, signals)を宣言できます * サポートする拡張を宣言できる([Context & Sessions](/docs/building/by-layer/L2/context-sessions#extension-fields-ext) 参照) * バージョンに基づく互換性チェックが可能 **Note:** `_meta` フィールドは [MCP server.json spec](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/server-json/generic-server-json.md) に従い逆 DNS の名前空間を使用します。`/.well-known/mcp.json` と `/.well-known/server.json` の両方をサポートしてください。 ### パラメータバリデーション ```javascript theme={null} // MCP provides tool schemas for validation const toolSchema = await mcp.getToolSchema('get_products'); // 呼び出し前にスキーマでバリデーション ``` ### エラーハンドリング AdCP エラーは `isError: true` のツールレベルレスポンスとして `structuredContent.adcp_error` にエラーが格納されて返されます。完全な抽出ロジックと JSON-RPC トランスポートコードは [Transport Error Mapping](/docs/building/operating/transport-errors) を参照してください。 ```javascript theme={null} try { const response = await session.call('get_products', params); // AdCP アプリケーションエラーを確認(isError: true かつ構造化データあり) if (response.isError) { const adcpError = response.structuredContent?.adcp_error; if (adcpError) { // code, recovery, retry_after などを含む構造化エラー console.log('AdCP error:', adcpError.code, adcpError.recovery); } } } catch (mcpError) { // MCP トランスポートエラー(接続、認証など) // AdCP 構造化トランスポートエラーを確認 const adcpError = mcpError.data?.adcp_error; if (adcpError) { console.log('Transport error:', adcpError.code); } else { console.error('MCP Error:', mcpError); } } ``` ## ベストプラクティス 1. **セッションラッパーを利用** してコンテキストを自動管理 2. レスポンス処理前に **status フィールド** を確認 3. **コンテキスト期限切れ** はリトライで丁寧に処理 4. ステータス処理パターンは **Core Concepts** を参照 5. 利用可能なら MCP ツールスキーマで **パラメータ検証** ## 次のステップ * **Core Concepts**: ステータス処理とワークフローは [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照 * **Task Reference**: [Media Buy Tasks](/docs/media-buy) と [Signals](/docs/signals/overview) * **Protocol Comparison**: [A2A integration](/docs/building/by-layer/L0/a2a-guide) と比較 * **Examples**: 完全なワークフロー例は Core Concepts に掲載 **ステータス処理、非同期オペレーション、確認フローについては [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照してください。このガイドは MCP トランスポート固有の内容に絞っています。** # MCP レスポンス抽出 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L0/mcp-response-extraction MCP ツール結果から AdCP 成功レスポンスデータを抽出する方法: structuredContent、text フォールバック、クライアント実装要件。 このページは、MCP ツール結果から AdCP 成功レスポンスデータを抽出する規範的アルゴリズムを定義します。エラー抽出については [Transport Error Mapping](/docs/building/operating/transport-errors) を参照。 ## 層の分離 | Path | When | Data Source | | -------------------------------------------------------------------- | ------------------------ | ----------------------------------------------- | | 成功抽出(このページ) | `isError` が欠如または `false` | `structuredContent` または `content[].text` | | エラー抽出([transport-errors](/docs/building/operating/transport-errors)) | `isError: true` | `structuredContent.adcp_error` または text フォールバック | クライアントは、どの抽出パスを使うかを決める前に `isError` を確認しなければなりません(MUST)。`isError: true` のレスポンスは、非エラーデータを持つ `structuredContent` を含んでいても、成功レスポンスとして処理してはなりません(MUST NOT)。 ## 抽出アルゴリズム クライアントは、この順序で MCP ツール結果から AdCP データを抽出しなければなりません(MUST): 1. **ガード: エラーレスポンスを拒否。** `isError` が truthy なら null を返す。エラー抽出は別のパス。 2. **`structuredContent`** — 存在し非配列オブジェクトなら、それを返す。唯一のキーが `adcp_error` なら null を返す(これは `isError` フラグを欠くエラーレスポンス)。 3. **Text フォールバック** — `content[]` アイテムを配列順で反復。`type === 'text'` の各アイテムについて、1MB サイズ制限を強制し、次に `JSON.parse` を試みる。結果が非配列オブジェクトなら、それを返す。パースに失敗する、非オブジェクトとしてパースされる、または `adcp_error` キーのみを含むアイテムはスキップ。 4. **構造化データが見つからない** — null を返す。レスポンスは機械可読な AdCP データのないプレーンテキスト。 ```javascript MCP Client theme={null} function extractAdcpResponseFromMcp(response) { // 1. Error responses go through transport-errors extraction if (response.isError) return null; // 2. structuredContent (preferred — MCP 2025-03-26+) if (response.structuredContent != null && typeof response.structuredContent === 'object' && !Array.isArray(response.structuredContent)) { const sc = response.structuredContent; // adcp_error-only structuredContent is an error missing isError flag const keys = Object.keys(sc); if (keys.length === 1 && keys[0] === 'adcp_error') return null; return sc; } // 3. Text fallback — JSON.parse content[].text if (response.content && Array.isArray(response.content)) { for (const item of response.content) { if (item.type === 'text' && item.text) { if (item.text.length > 1_048_576) continue; // 1MB size limit try { const parsed = JSON.parse(item.text); if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { // Skip adcp_error-only payloads (error missing isError flag) const keys = Object.keys(parsed); if (keys.length === 1 && keys[0] === 'adcp_error') continue; return parsed; } } catch { /* not JSON */ } } } } return null; } ``` ## 抽出パス ### structuredContent(推奨) MCP 2025-03-26 は型付きツール結果のため `structuredContent` を導入しました。AdCP サーバーは完全なレスポンスペイロードをここに返します: ```json theme={null} { "content": [{"type": "text", "text": "Found 3 products matching your brief."}], "structuredContent": { "status": "completed", "message": "Found 3 products", "products": [ {"product_id": "ctv_sports_premium", "name": "Premium Sports CTV"}, {"product_id": "ctv_news_standard", "name": "Standard News CTV"} ] } } ``` `structuredContent` オブジェクトが AdCP レスポンスそのものです — タスク固有のフィールド(`products`、`media_buy_id`、`status` など)はネストではなくトップレベルにあります。 ### Text フォールバック 古い MCP サーバー(2025-03-26 以前)はレスポンスを `content[].text` に JSON としてシリアライズします: ```json theme={null} { "content": [ {"type": "text", "text": "{\"status\":\"completed\",\"products\":[{\"product_id\":\"ctv_premium\"}]}"} ] } ``` クライアントは JSON オブジェクトを生成する最初のテキストアイテムをパースします。`structuredContent` とテキスト JSON の両方が存在するとき、`structuredContent` が優先します。 ## エラー抽出との関係 成功とエラーの抽出は補完的です: ```javascript theme={null} function handleMcpResponse(response) { // Try error extraction first (only runs if isError is true) const error = extractAdcpErrorFromMcp(response); if (error) return handleError(error); // Then try success extraction const data = extractAdcpResponseFromMcp(response); if (data) return handleSuccess(data); // Plain text response — no structured data return handlePlainText(response.content); } ``` ## セキュリティ考慮事項 ### セラー制御データ `structuredContent` と `content[].text` のすべてのデータはセラー制御です。[Transport Error Mapping](/docs/building/operating/transport-errors#security-considerations) の同じプロンプトインジェクションとデータ境界要件が適用されます。 ### サイズ制限 クライアントは処理前に最大ペイロードサイズを強制すべきです(SHOULD)。推奨制限は `structuredContent` に 1MB。text フォールバックについては、過大なペイロードからのメモリ枯渇を防ぐため `JSON.parse` の前に制限を適用します。 ### プロトタイプ汚染 クライアントは、キーをフィルターせずに抽出されたレスポンスオブジェクトを `Object.assign` やスプレッド経由でアプリケーション状態にマージしてはなりません(MUST NOT)。`__proto__` や `constructor` のようなセラー制御のキーはプロトタイプ汚染をトリガーできます。マージ前に期待されるタスクレスポンススキーマに対して検証してください。 ### 型混乱 クライアントは成功抽出の前に `isError` を確認しなければなりません(MUST)。このガードなしでは、クライアントはエラーレスポンスを成功データとして処理し、誤ったビジネスロジック(例: `RATE_LIMITED` エラーをプロダクトデータとして扱う)につながる可能性があります。 ## クライアントライブラリ要件 この仕様を実装するクライアントライブラリは次をしなければなりません(MUST): 1. **抽出前に `isError` を確認。** エラーレスポンスに null を返す。 2. **`structuredContent` を優先。** `structuredContent` が欠如するときのみテキストパースにフォールバック。 3. **パースされたテキストを検証。** `JSON.parse` から非配列オブジェクトのみを受け入れる。配列、文字列、数値、boolean、null を拒否。 4. **`adcp_error` のみの `structuredContent` を扱う。** `structuredContent` が `adcp_error` キーのみを含むとき、null を返す — これは `isError` フラグを欠くかもしれないエラーレスポンス。 ## テストベクター 機械可読なテストベクターは [`/static/test-vectors/mcp-response-extraction.json`](https://adcontextprotocol.org/test-vectors/mcp-response-extraction.json) で利用可能です。各ベクターは次を含みます: * `path`: 抽出パス(`structuredContent` または `text_fallback`) * `response`: MCP ツール結果エンベロープ * `expected_data`: 抽出されるべき AdCP データ(または `null`) クライアントライブラリはこれらのベクターに対して抽出ロジックを検証すべきです(SHOULD)。 ## 関連項目 * [Transport Error Mapping](/docs/building/operating/transport-errors) — MCP と A2A からのエラー抽出 * [A2A Response Extraction](/docs/building/by-layer/L0/a2a-response-extraction) — A2A の同等仕様 * [MCP Guide](/docs/building/by-layer/L0/mcp-guide) — MCP トランスポート統合 # Schemas Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L0/schemas AdCP JSON スキーマ: どこで取得するか、プロトコル tarball、スキーマバージョニング、bundled 対 $ref 解決バリアント、Sigstore 経由でのサプライチェーン来歴の検証方法。 L0 ワイヤー層は、公開された JSON Schema でフレーム化された JSON-over-HTTP です。このページはスキーマを取得するためのリファレンスです — それらがどこに存在するか、バージョンをピン留めする方法、サプライチェーン来歴を検証する方法、リリース内のディレクトリ形状。スキーマ自体ではなく SDK を選んでいるなら、[Choose your SDK](/docs/building/by-layer/L4/choose-your-sdk) を参照。 ## Schema access AdCP スキーマは 2 つのソースから利用可能です: | Source | URL | Best For | | ------- | ------------------------------------------------------------------ | ---------------------- | | Website | `https://adcontextprotocol.org/schemas/v3/` | ランタイムフェッチ、バージョンエイリアス | | GitHub | `https://github.com/adcontextprotocol/adcp/tree/main/dist/schemas` | オフラインアクセス、CI/CD パイプライン | 両ソースは同一のスキーマを含みます。GitHub リポジトリは、バンドルされたスキーマがコードベースに直接コミットされた、すべてのリリースされたバージョンを含みます。 ## One-shot protocol bundle 数百の個別スキーマファイルを同期するのは積み重なります。すべての AdCP リリースは、完全なプロトコル — スキーマ、コンプライアンスストーリーボード、OpenAPI レジストリ — を含む単一の gzip 圧縮された tarball も公開するため、クライアントはツリーをクロールする代わりに 1 つのアーティファクトを引けます。 | Path | Contents | Notes | | ------------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------- | | `https://adcontextprotocol.org/protocol/latest.tgz` | 現在の開発バンドル | すべてのマージで変わる | | `https://adcontextprotocol.org/protocol/{version}.tgz` | ピン留めされたリリースバンドル | 公開されたら不変 | | `https://adcontextprotocol.org/protocol/{version}.tgz.sha256` | SHA-256 チェックサム | ダウンロード完全性の検証に使う | | `https://adcontextprotocol.org/protocol/{version}.tgz.sig` | Sigstore 分離署名 | 発行者アイデンティティの検証に使う。リリースが `release.yml` ワークフロー経由でカットされたときのみ存在 — 帯域外の再公開では欠如。 | | `https://adcontextprotocol.org/protocol/{version}.tgz.crt` | Fulcio 発行の署名証明書 | `cosign verify-blob` のため `.sig` とペア。リリースが `release.yml` ワークフロー経由でカットされたときのみ存在 — 帯域外の再公開では欠如。 | すべての tarball は単一の `adcp-{version}/` ディレクトリに展開されます(安全な展開、tarbomb なし)。内部: ``` adcp-{version}/ README.md # quickstart + links CHANGELOG.md # release notes manifest.json # version, generated_at, contents summary schemas/ # full JSON schema tree (same as /schemas/{version}/) compliance/ # protocols/, specialisms/, universal/, test-kits/, index.json openapi/registry.yaml # OpenAPI description ``` 展開前にチェックサムを検証: ```bash theme={null} curl -OL https://adcontextprotocol.org/protocol/3.1.0.tgz curl -OL https://adcontextprotocol.org/protocol/3.1.0.tgz.sha256 shasum -a 256 -c 3.1.0.tgz.sha256 tar xzf 3.1.0.tgz cd adcp-3.1.0 ``` バージョンごとに一度引き、SHA でキャッシュすれば、リクエストを検証し、ストーリーボードを実行し、ドキュメントをオフラインでレンダリングするのに必要なすべてを持ちます。`@adcp/sdk` の `sync-schemas` コマンドはこれを内部で使います。 利用可能な tarball は [`/protocol/`](https://adcontextprotocol.org/protocol/) にもリストされています。 ### Verifying protocol bundle signatures SHA-256 サイドカーは tarball と同じオリジンに存在するため、転送中の改ざんからのみ保護します。サプライチェーン保護 — バンドルが AdCP リリースワークフローから来たこと、ホストが侵害されても悪意あるものとすり替えられなかったことを証明 — のため、すべてのリリースされた `{version}.tgz` は Sigstore 分離署名とともに公開されます。 署名は、keyless OIDC を使う GitHub Actions リリースワークフローによって生成されます: 漏洩する長寿命の AdCP 署名鍵はありません。証明書は署名をそれを発行したワークフローアイデンティティにバインドします。 ```bash theme={null} # Pull the tarball and the two signature sidecars curl -OL https://adcontextprotocol.org/protocol/3.1.0.tgz curl -OL https://adcontextprotocol.org/protocol/3.1.0.tgz.sig curl -OL https://adcontextprotocol.org/protocol/3.1.0.tgz.crt # Verify (requires cosign 2.x — `brew install cosign`) cosign verify-blob \ --signature 3.1.0.tgz.sig \ --certificate 3.1.0.tgz.crt \ --certificate-identity-regexp '^https://github\.com/adcontextprotocol/adcp/\.github/workflows/release\.yml@refs/(heads|tags)/.*$' \ --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ 3.1.0.tgz ``` `cosign verify-blob` は、SHA が一致し TLS が有効でも、署名が AdCP リリースワークフロー以外のものによって作られた場合、非ゼロで終了します。プロトコルバンドルを取り込む任意のパイプラインで、強制ソースとしてこれを使ってください。`@adcp/sdk`、`adcp-client-python`、`adcp-go` SDK は、サイドカーが存在するときこの検証を自動的に実行します。 `refs/(heads|tags)/.*` ワイルドカードは意図的です — リリースは push トリガーのワークフロー実行中に署名するため、証明書サブジェクトはリリースブランチ(例: v3.0.1+ の `refs/heads/3.0.x`、v3.0.0 の `refs/heads/main`)を名指しします。信頼ゲートはコンシューマーの正規表現ではなく上流の `release.yml` の `on.push.branches` 許可リストです。リテラル許可リスト正規表現(`(main|2\.6\.x)` スタイル)は、新しい保守ブランチが追加されるたびに黙って壊れます — 完全な信頼モデルとリリースごとの証明書サブジェクトルックアップについては [プロトコル tarball の検証](/docs/reference/verifying-protocol-tarballs) を参照。 署名に先行する古いリリース、および帯域外で再公開されたバージョン(署名ワークフローをバイパス)は、チェックサムのみのままです — クライアントは欠けているサイドカーを検証失敗ではなく「チェックサムのみ」の信頼レベルとして扱うべきです。 ## Compliance storyboards ストーリーボードは `/compliance/{version}/` でスキーマと並んで存在します。それらは、AAO がエージェントのケイパビリティクレームを検証するために実行するテストシナリオを定義します。 | Path | Purpose | | --------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | `/compliance/{version}/universal/` | すべてのエージェントに必須(ケイパビリティディスカバリー、エラー処理、スキーマ検証) | | `/compliance/{version}/protocols/{protocol}/` | プロトコル(`media-buy`、`creative`、`signals`、`governance`、`brand`、`sponsored-intelligence`)を主張するのに必要なベースライン | | `/compliance/{version}/specialisms/{id}/` | 任意の専門化クレーム(例: `sales-guaranteed`、`sales-broadcast-tv`) | | `/compliance/{version}/index.json` | 利用可能なプロトコル、専門分野、universal ストーリーボードを列挙 | `supported_protocols`(プロトコルベースライン用)と `specialisms`(狭いケイパビリティクレーム用)を `get_adcp_capabilities` に宣言します — コンプライアンスランナーが一致するバンドルを実行して検証します。エージェントが主張できるすべてのプロトコルと専門分野については完全な [Compliance Catalog](/docs/building/verification/compliance-catalog) を参照。 ## Common schemas | Schema | URL | | --------------- | -------------------------------------------------------------- | | Product | `https://adcontextprotocol.org/schemas/v3/core/product.json` | | Media Buy | `https://adcontextprotocol.org/schemas/v3/core/media-buy.json` | | Creative Format | `https://adcontextprotocol.org/schemas/v3/core/format.json` | | Schema Registry | `https://adcontextprotocol.org/schemas/v3/index.json` | **AI コーディングエージェント向け:** MCP 統合ドキュメントについては、コーディングエージェントを **[https://docs.adcontextprotocol.org/mcp](https://docs.adcontextprotocol.org/mcp)** に向けてください。 ## Schema versioning AdCP はセマンティックバージョニングを使います。ユースケースに正しいパスを選んでください: | Path | Example | Best For | | --------- | ------------------------------------------------------------ | --------------- | | 正確なバージョン | `/schemas/3.0.0/`、`/compliance/3.0.0/`、`/protocol/3.0.0.tgz` | 本番、SDK 生成 | | メジャーバージョン | `/schemas/v3/`、`/compliance/v3/` | 開発、ドキュメント | | マイナーバージョン | `/schemas/v3.0/`、`/compliance/v3.0/` | 安定した開発(パッチ更新のみ) | 同じバージョンセマンティクスが `/schemas`、`/compliance`、`/protocol/{version}.tgz` に適用されます — 1 つのリリースが 3 つすべてをカットします。 ### Production (recommended) 安定性のため正確なバージョンにピン留め: ```javascript theme={null} const SCHEMA_VERSION = '3.0.0'; const schema = await fetch( `https://adcontextprotocol.org/schemas/${SCHEMA_VERSION}/core/product.json` ); ``` ### Development 後方互換の更新に追随するためメジャーバージョンエイリアスを使う: ```javascript theme={null} const schema = await fetch( 'https://adcontextprotocol.org/schemas/v3/core/product.json' ); ``` ### SDK type generation ```bash theme={null} # TypeScript npx json-schema-to-typescript \ https://adcontextprotocol.org/schemas/3.0.0/core/product.json \ --output types/product.d.ts # Python datamodel-codegen \ --url https://adcontextprotocol.org/schemas/3.0.0/core/product.json \ --output models/product.py ``` ## Bundled schemas `$ref` 解決をサポートしないツールには、すべての参照がインラインで解決されたバンドルスキーマを使います。バンドルスキーマは website と GitHub の両方から利用可能です: ### Website access ``` https://adcontextprotocol.org/schemas/3.0.0/bundled/media-buy/create-media-buy-request.json ``` ### GitHub access バンドルスキーマは `dist/schemas/{VERSION}/bundled/` でリポジトリにコミットされています: ```bash theme={null} # Clone and access locally git clone https://github.com/adcontextprotocol/adcp.git ls adcp/dist/schemas/3.0.0/bundled/media-buy/ # Or fetch directly via GitHub raw curl https://raw.githubusercontent.com/adcontextprotocol/adcp/main/dist/schemas/3.0.0/bundled/media-buy/get-products-request.json ``` ### Directory structure ``` dist/schemas/{VERSION}/ ├── bundled/ # Fully dereferenced schemas │ ├── media-buy/ # Media buying tasks │ ├── creative/ # Creative tasks │ ├── signals/ # Signal protocol tasks │ ├── property/ # Property/governance tasks │ ├── content-standards/ # Content standards tasks │ ├── sponsored-intelligence/ # Sponsored intelligence tasks │ ├── protocol/ # Protocol tasks │ └── core/ # Core shared schemas and legacy task lifecycle schemas ├── core/ # Modular schemas with $ref ├── trusted-match/ # Serve-time Context Match and Identity Match schemas ├── media-buy/ └── index.json # Schema registry ``` `index.json` がディレクトリ権威です。それはバンドルの `published_version`、安定性メタデータ、`protocol_layers` を宣言します: ネゴシエーション層(`media-buy`、`creative`、`signals`、`account`、`governance`、`brand`、`sponsored-intelligence`)と決定/配信層(`trusted-match`)。 ### Bundled schema categories すべてのリクエスト/レスポンスタスクスキーマがバンドルされます: | Category | Tasks | | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `bundled/media-buy/` | get-products, create-media-buy, update-media-buy, list-creative-formats, sync-creatives, build-creative, list-creatives, get-media-buy-delivery, list-authorized-properties, provide-performance-feedback | | `bundled/creative/` | list-creative-formats, preview-creative | | `bundled/signals/` | get-signals, activate-signal | | `bundled/property/` | create-property-list, get-property-list, list-property-lists, update-property-list, delete-property-list, validate-property-delivery | | `bundled/content-standards/` | create-content-standards, get-content-standards, list-content-standards, update-content-standards, calibrate-content, validate-content-delivery, get-media-buy-artifacts | | `bundled/sponsored-intelligence/` | si-get-offering, si-initiate-session, si-send-message, si-terminate-session | | `bundled/protocol/` | get-adcp-capabilities, get-task-status, list-tasks | | `bundled/core/` | tasks-get, tasks-list | 利用可能なすべてのスキーマについては [スキーマレジストリ](https://adcontextprotocol.org/schemas/v3/index.json) を参照。 ## Version discovery ```bash theme={null} # Get the canonical stable schema bundle and registry URL. curl https://adcontextprotocol.org/schemas/latest.json | jq '{version: .latest_stable, index}' # Or read the full file-based discovery index. curl https://adcontextprotocol.org/schemas/index.json | jq '.aliases' # The versioned registry also carries the full semver of its bundle. # (Note: `published_version` carries full semver including patch. # It's distinct from the per-request/response wire `adcp_version` # field defined in core/version-envelope.json, which uses # release-precision — never send `published_version` on the wire.) curl https://adcontextprotocol.org/schemas/v3/index.json | jq '.published_version' ``` 正準バージョンをディレクトリ順や `versions[0]` から推論しないでください。プレリリースアーティファクトはピン留めされた履歴ビルドのため発見可能なままです。正準の安定選択には `latest_stable` または `aliases` マップを使ってください。 バージョン履歴と移行ガイドについては [リリースノート](/docs/reference/release-notes) を確認してください。 ## Registry API AgenticAdvertising.org レジストリは、ブランド解決、プロパティ解決、エージェントディスカバリー、認可検証のためのパブリックな REST API を提供します。認証不要。 REST 経由でブランドを解決、エージェントを発見、認可を検証。 # L1 — アイデンティティと署名 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L1/index AdCP スタックのアイデンティティと署名層。RFC 9421 HTTP メッセージ署名、公開鍵解決、リプレイウィンドウ強制、鍵ローテーション。 L1 は、リクエストがヘッダーが主張する者から来たこと、およびボディが転送中に変更されなかったことを暗号学的に検証します。リプレイウィンドウ強制と鍵ローテーションを伴う RFC 9421 HTTP メッセージ署名。両側で対称 — エージェントはインバウンドを検証しアウトバウンド webhook に署名する。呼び出し元はアウトバウンドに署名しインバウンド webhook を検証する。 ## L1 の SDK が提供しなければならないもの SDK を選ぶか新しい言語に移植する場合、これが L1 のビルドターゲットです: * アウトバウンドリクエストのための **RFC 9421 メッセージ署名の署名**。 * `created` / `expires` のリプレイウィンドウ強制と `keyid` ベースの鍵ルックアップを含む、インバウンドリクエストの **RFC 9421 検証**。 * **プラグイン可能な署名プロバイダー抽象** — 開発用のプロセス内鍵、本番用の KMS / HSM プロバイダー。 * 採用者が完全なエージェントを起動せずに署名配線が正しいことをアサートできる **テストフィクスチャまたは検証者テストハーネス**。 累積的なクロス層のストーリー(L0+L1 が何をもたらすか)については、[SDK スタックリファレンス](/docs/building/cross-cutting/sdk-stack#l1--identity--signing) を参照。 ## この層のページ * **[Security implementation profile](/docs/building/by-layer/L1/security)** — RFC 9421 ワイヤー詳細、KMS 統合、リプレイウィンドウ調整。 * **[Webhook verifier tuning](/docs/building/by-layer/L1/webhook-verifier-tuning)** — クロックスキュー処理、鍵ローテーション遷移、署名失敗診断。 # リクエスト署名ガイド Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L1/request-signing AdCP における RFC 9421 リクエスト署名のステップバイステップガイド: 鍵生成、JWKS 公開、brand.json セットアップ、クライアント側署名、サーバー側検証、webhook 署名、鍵ローテーション、適合性テスト。 AdCP 3.0 は暗号的リクエスト認証のため [HTTP Message Signatures (RFC 9421)](https://www.rfc-editor.org/rfc/rfc9421) をサポートします。バイヤーはアウトバウンドリクエストに署名し、セラーが誰が送ったかとペイロードが改ざんされていないことを検証できます。セラーはアウトバウンド webhook に署名し、バイヤーが真正性を検証できます。 署名は **AdCP 3.0 ではオプション** で、すべての支出コミット操作について **AdCP 4.0 で必須** になります。まだ署名しないエージェントも、インバウンドリクエストの署名ヘッダー(`Signature`、`Signature-Input`、`Content-Digest`)を壊れることなく許容しなければなりません。 これは実践的実装ガイドです。規範的仕様 — カバードコンポーネント、正準化ルール、完全な検証者チェックリスト、リプレイ dedup サイジング、完全なエラー分類 — については [Security: Signed Requests](/docs/building/by-layer/L1/security#署名付きリクエストトランスポート層) を参照してください。 下のコード例はタブで **JavaScript/TypeScript**、**Python**、**Go** SDK ヘルパーを使います。3 つの SDK すべてが同じ適合性ベクターに対して同じ RFC 9421 プロファイルを実装します — API 表面は異なりますがワイヤー出力は同一です。あなたの言語がリストされていない場合、[適合性ベクター](#testing) と [規範的仕様](/docs/building/by-layer/L1/security#署名付きリクエストトランスポート層) は言語非依存です。 ## これが必要になるとき | あなたは… | 必要なこと… | なぜ | | ----------------------- | ------------------------------------ | -------------------------- | | **バイヤー**(セラーツールを呼ぶ) | アウトバウンドリクエストに署名 | セラーはリクエストがあなたから来た証明を要求するかも | | **バイヤー**(webhook を受信) | インバウンド webhook 署名を検証 | webhook がセラーから来たことを確認 | | **セラー**(ツール呼び出しを受信) | インバウンドリクエスト署名を検証 | バイヤーが主張する者であることを確認 | | **セラー**(webhook を送信) | アウトバウンド webhook に署名 | バイヤーが webhook 真正性を検証できるように | | **オーケストレーター**(セラーにプロキシ) | アウトバウンドリクエストに署名 + インバウンド webhook を検証 | セラーの視点ではあなたがバイヤー | ## 主要概念 ### 署名カバレッジ AdCP 署名プロファイルは以下のリクエストコンポーネントをカバーします: * `@method` — HTTP メソッド * `@target-uri` — 完全な正準化リクエスト URL * `@authority` — 小文字化されたホストヘッダー * `content-type` — メディアタイプ * `content-digest` — リクエストボディの SHA-256 または SHA-512 ハッシュ(`covers_content_digest` ケイパビリティを参照) 任意のカバードコンポーネントが署名後に変わると、検証が失敗します。 ### 鍵分離 すべてのエージェントは、明確な `kid` と `adcp_use` タグを持つ署名鍵を公開します: * `adcp_use: "request-signing"` — アウトバウンドツール呼び出しとアウトバウンド webhook の署名用 * `adcp_use: "webhook-signing"` — 非推奨。後方互換性のため依然として webhook パスで受理される 爆発半径分離のため、ツール呼び出しと webhook に同じ鍵素材を再利用するのではなく、webhook 配信用に別の `kid` の下で 2 つ目の `request-signing` 鍵を公開してください。 ### ディスカバリーチェーン 検証者は 3 ステップのチェーンを通じてあなたの公開鍵を見つけます: ``` Your domain (e.g., agent.example.com) -> /.well-known/brand.json # brand manifest with agent declarations -> agents[].jwks_uri # pointer to your key store -> /.well-known/jwks.json # JSON Web Key Set with public keys ``` `@adcp/client` SDK は、キャッシングとリフレッシュでこのチェーンを自動的に処理する `BrandJsonJwksResolver` を提供します。 ## ステップ 1: 署名鍵を生成する ### CLI ```bash theme={null} adcp signing generate-key --alg ed25519 --kid my-agent-2026 \ --private-out ./private.jwk --public-out ./public-jwks.json ``` これは Ed25519 キーペアを生成し、以下を書き込みます: * `private.jwk` — 秘密鍵(`d` フィールドを持つ JWK)。これを秘密に保つ。 * `public-jwks.json` — JWKS 形式の公開鍵。これを公開する。 ### プログラマティック ```typescript theme={null} import { generateKeyPair, exportJWK } from 'jose'; const { publicKey, privateKey } = await generateKeyPair('EdDSA', { crv: 'Ed25519' }); const publicJwk = await exportJWK(publicKey); const privateJwk = await exportJWK(privateKey); const kid = 'my-agent-2026'; publicJwk.kid = kid; publicJwk.use = 'sig'; publicJwk.key_ops = ['verify']; publicJwk.adcp_use = 'request-signing'; ``` ```python theme={null} from adcp.signing import generate_signing_keypair # CLI equivalence: adcp-keygen --alg ed25519 --purpose request-signing --kid my-agent-2026 pem_bytes, public_jwk = generate_signing_keypair( alg="ed25519", purpose="request-signing", kid="my-agent-2026", ) # pem_bytes — write to disk with mode 0600 and O_EXCL, or pass to a secret manager. # public_jwk — publish in your JWKS endpoint. Already includes kid, use, key_ops, and adcp_use. ``` ```go theme={null} import "github.com/adcontextprotocol/adcp-go/adcp/signing" res, err := signing.GenerateKeyForProfile( signing.AlgEd25519, "my-agent-2026", signing.ProfileRequestSigning, ) if err != nil { /* handle */ } // res.PrivateKeyPEM — write to disk with mode 0600, or pass to a secret manager. // res.PublicJWK — serialize and publish in your JWKS endpoint. AdCP-required // fields (kid, kty, crv, alg, use, key_ops, adcp_use) are set. ``` webhook RFC 9421 プロファイルには `signing.ProfileWebhookSigning` を使います。対応する公開鍵を `adcp_use: "request-signing"` で公開してください。webhook 専用の鍵素材が欲しい場合、別の `kid` を使ってください。 ### サポートされるアルゴリズム | Algorithm | `alg` value | Key type | Notes | | ----------- | ---------------------------------------------- | ----------------- | ----------------------------------------------- | | Ed25519 | `ed25519` (RFC 9421) / `EdDSA` (JWK) | `OKP` / `Ed25519` | 推奨。高速、小さい署名。 | | ECDSA P-256 | `ecdsa-p256-sha256` (RFC 9421) / `ES256` (JWK) | `EC` / `P-256` | エッジランタイムフレンドリー(Cloudflare Workers、Vercel Edge)。 | アルゴリズム名は JWK エントリー(`"alg": "EdDSA"`)と RFC 9421 `Signature-Input` パラメーター(`alg="ed25519"`)で異なります。仕様の [アルゴリズム命名テーブル](/docs/building/by-layer/L1/security#adcp-rfc-9421-profile) を参照してください。 ### 秘密鍵の保存 あなたのランタイムがサポートする最も強いオプションを選んでください。最も安全なものから最も安全でないものへ: * **クラウド KMS**(GCP Cloud KMS、AWS KMS、Azure Key Vault): 秘密鍵は HSM 内で生成され、決してそれを離れません。署名は KMS API を呼ぶことで実行されます。あなたは鍵バイトではなく JWK 参照のみを保持します。TypeScript SDK は GCP Cloud KMS 用に `createKmsSigner` を公開します — [`@adcp/client/signing/kms`](https://github.com/adcontextprotocol/adcp-client/tree/main/src/lib/signing/kms) を参照。支出コミット操作を処理する任意のエージェントに推奨。 * **シークレットマネージャー**(GCP Secret Manager、AWS Secrets Manager、HashiCorp Vault): ブート時にロードし、プロセスライフタイムの間メモリに保つ。KMS より簡単だが鍵素材がプロセス内に存在する — メモリダンプ、ロギング、または侵害された依存関係を通じてリークする。 * **環境変数**: `ADCP_SIGNING_PRIVATE_KEY='{"kid":"...","kty":"OKP",...}'`。開発と小規模デプロイに許容可能。シークレットマネージャーと同じメモリ常駐リスク。 * **ファイル**: 開発のみ。決してバージョン管理にコミットしない。既存ファイルが決して上書きされないようモード `0600` と `O_EXCL` を使う — `Path.write_bytes` はプロセス umask(しばしば `0644`、world-readable)を継承し、秘密鍵素材には安全でない。 KMS を選ぶと、署名レイテンシーが上がります(リクエストごとに HSM への 1 ラウンドトリップ)。コミットする前に負荷下でプロファイルしてください — TypeScript と Python SDK は JWK メタデータを積極的にキャッシュし、内部テストで GCP KMS に対して毎秒数百の署名を維持できますが、あなたの数字はリージョンと並行性に依存します。 ## ステップ 2: 公開鍵を公開する ### JWKS エンドポイント 安定した HTTPS URL(デフォルトは `/.well-known/jwks.json`)で JSON Web Key Set をサーブします: ```json theme={null} { "keys": [ { "kid": "my-agent-2026", "kty": "OKP", "crv": "Ed25519", "x": "", "use": "sig", "key_ops": ["verify"], "adcp_use": "request-signing" } ] } ``` ここには公開鍵のみ — `d` フィールドなし。`Cache-Control: max-age=3600` または同様を設定してください。webhook 配信に別の鍵素材を使う場合、明確な `kid` を持つ 2 つ目の `request-signing` JWK を公開してください。非推奨の `webhook-signing` JWK は後方互換性のため webhook パスで受理されたままです。 ### brand.json あなたのブランドドメインの `/.well-known/brand.json` でサーブします。`jwks_uri` は検証者があなたの鍵を見つける方法です: ```json theme={null} { "name": "My Company", "domain": "example.com", "agents": [ { "url": "https://agent.example.com", "jwks_uri": "https://agent.example.com/.well-known/jwks.json", "capabilities": ["media-buy"], "adcp_use": ["request-signing"] } ] } ``` ## ステップ 3: アウトバウンドリクエストに署名する(バイヤー / オーケストレーター) ### fetch / HTTP クライアントのラッピング `createSigningFetch` は任意の `fetch` 互換関数をラップしてアウトバウンドリクエストに自動的に署名します: ```typescript theme={null} import { createSigningFetch } from '@adcp/client/signing'; const privateJwk = JSON.parse(process.env.ADCP_SIGNING_PRIVATE_KEY); const signingFetch = createSigningFetch(fetch, { keyid: 'my-agent-2026', alg: 'ed25519', privateKey: privateJwk, }); // Use signingFetch anywhere you'd use fetch. // Signature, Signature-Input, and Content-Digest headers are added automatically. await signingFetch('https://seller.example.com/mcp', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); ``` Python SDK は、`ADCPClient` で `signing` が設定されているときすべてのアウトバウンドリクエストに自動署名します: ```python theme={null} from adcp import ADCPClient from adcp.signing import SigningConfig, load_private_key_pem private_key = load_private_key_pem(open("private-key.pem", "rb").read()) client = ADCPClient( base_url="https://seller.example.com/mcp", signing=SigningConfig( key_id="my-agent-2026", alg="ed25519", private_key=private_key, cover_content_digest=True, ), ) # Every request the client sends carries Signature, Signature-Input, and # Content-Digest headers. ``` より低レベルの制御(例: クライアント外で任意の `httpx.Request` に署名)には、`sign_request` を直接呼びます: ```python theme={null} from adcp.signing import sign_request signed = sign_request( method="POST", url="https://seller.example.com/mcp", headers={"Content-Type": "application/json"}, body=request_body_bytes, private_key=private_key, key_id="my-agent-2026", alg="ed25519", cover_content_digest=True, ) # signed.as_dict() returns the headers to attach to the outgoing request. ``` Go SDK は、`http.Client` トランスポートをラップする `Signer` を公開します: ```go theme={null} import ( "net/http" "github.com/adcontextprotocol/adcp-go/adcp/signing" ) priv, _, _ := signing.LoadPrivateKey(pemBytes) signer, _ := signing.NewSigner(signing.SignerOptions{ KeyID: "my-agent-2026", Algorithm: signing.AlgEd25519, PrivateKey: priv, }) client := &http.Client{ Transport: signer.RoundTripper(http.DefaultTransport, true /* cover content-digest */), } req, _ := http.NewRequest("POST", "https://seller.example.com/mcp", body) req.Header.Set("Content-Type", "application/json") resp, _ := client.Do(req) // Signature, Signature-Input, and Content-Digest are added by the transport. ``` トランスポートなしの単発署名には、リクエスト上で `signer.SignRequest(req, signing.SignOptions{CoverContentDigest: true})` を直接呼びます。 ### ケイパビリティ対応署名 `buildAgentSigningFetch` はターゲットセラーが `signed-requests` をサポートするかをチェックし、サポートされるときのみ署名します。これは本番の推奨アプローチです: ```typescript theme={null} import { buildAgentSigningFetch, CapabilityCache } from '@adcp/client/signing/client'; const capabilityCache = new CapabilityCache(); const signingFetch = buildAgentSigningFetch({ upstream: fetch, signing: { kid: 'my-agent-2026', alg: 'ed25519', private_key: privateJwk, agent_url: 'https://agent.example.com', sign_supported: true, }, getCapability: () => capabilityCache.get('https://seller.example.com'), }); ``` これは署名を期待しないエージェントへの署名送信を避け、ケイパビリティルックアップをキャッシュします。 ## ステップ 4: インバウンド署名を検証する(セラー) ### フレームワークミドルウェア 生の Express ルートには、raw-body ミドルウェアの後に `createExpressVerifier` をマウントします。`resolveOperation` コールバックには `mcpToolNameResolver` を使います — JSON-RPC エンベロープを解析し MCP ツール名を返します: ```typescript theme={null} import { createExpressVerifier, StaticJwksResolver, InMemoryReplayStore, InMemoryRevocationStore, } from '@adcp/client/signing'; import { mcpToolNameResolver } from '@adcp/client/server'; app.post( '/mcp', rawBodyMiddleware(), // req.rawBody must hold the byte-exact body createExpressVerifier({ capability: { supported: true, covers_content_digest: 'required', required_for: ['create_media_buy', 'update_media_buy'], }, jwks: new StaticJwksResolver(buyerPublicKeys), replayStore: new InMemoryReplayStore(), revocationStore: new InMemoryRevocationStore(), resolveOperation: mcpToolNameResolver, }), handler ); // On verify: req.verifiedSigner = { keyid, agent_url?, verified_at }. // On reject: 401 with WWW-Authenticate: Signature error="". ``` Python SDK は Flask と Starlette/FastAPI 用のフレームワークラッパーを出荷します。両方とも同じ `verify_request_signature` を呼び、拒否時に `SignatureVerificationError` を発生させます — それを `unauthorized_response_headers` を持つ 401 にマップします: ```python theme={null} from fastapi import FastAPI, Request, HTTPException from adcp.signing import ( VerifyOptions, VerifierCapability, CachingJwksResolver, InMemoryReplayStore, StaticRevocationChecker, ) from adcp.signing.middleware import ( verify_starlette_request, unauthorized_response_headers, ) from adcp.signing.errors import SignatureVerificationError app = FastAPI() verify_options = VerifyOptions( capability=VerifierCapability( supported=True, covers_content_digest="required", required_for={"create_media_buy", "update_media_buy"}, ), jwks_resolver=CachingJwksResolver(), replay_store=InMemoryReplayStore(), revocation_checker=StaticRevocationChecker(set()), ) @app.post("/mcp") async def mcp(request: Request): try: signer = await verify_starlette_request(request, options=verify_options) except SignatureVerificationError as exc: raise HTTPException( status_code=401, detail=exc.code, headers=unauthorized_response_headers(exc), ) # signer.key_id, signer.agent_url, signer.verified_at available for audit. body = await request.body() return await handle_mcp(body, signer) ``` Flask には: `verify_starlette_request` を `verify_flask_request`(同期)に置き換えます。非 Starlette ASGI フレームワークには、`verify_request_signature` を直接呼びます。 `http.Handler` チェーンに `signing.Middleware` をマウントします。ミドルウェアはインバウンド署名を検証し、成功時にリクエストコンテキストに `VerifiedSigner` を投入し、失敗時に `WWW-Authenticate: Signature error=""` を伴う `401` を書き込みます: ```go theme={null} import ( "net/http" "github.com/adcontextprotocol/adcp-go/adcp/signing" ) resolver := signing.NewCachingJWKSResolver() replay := signing.NewMemoryReplayStore(0 /* default cap */) revocation := signing.NewStaticRevocationSource(nil) mw := signing.Middleware(signing.MiddlewareOptions{ Resolver: resolver, Replay: replay, Revocation: revocation, OperationResolver: signing.DefaultOperationResolver, // /adcp/ ContentDigestPolicy: signing.DigestRequired, RequiredFor: []string{"create_media_buy", "update_media_buy"}, }) http.Handle("/mcp", mw(handler)) // Inside handler: func handler(w http.ResponseWriter, r *http.Request) { v := signing.VerifiedSignerFromContext(r.Context()) if v == nil { // Operation not in RequiredFor and request was unsigned — proceed // with bearer auth or whatever fallback you've configured. } // v.KeyID, v.AgentURL, v.VerifiedAt, v.Algorithm — available for audit. } ``` MCP サーバーには: `signing.DefaultOperationResolver` を、JSON-RPC エンベロープを解析し MCP ツール名を返すカスタムリゾルバー(TS SDK の `mcpToolNameResolver` の同等物)に置き換えます。 ### `requireAuthenticatedOrSigned` で署名 + bearer 認証を合成する `requireAuthenticatedOrSigned` は完全な合成をバンドルします: presence ゲートルーティング(ヘッダー存在時は署名認証、それ以外はフォールバック)と `requiredFor` 強制 — 署名必須操作の未認証リクエストは、認証情報がまったく供給されなくても `401 request_signature_required` を得ます。 ```typescript theme={null} import { serve, verifyApiKey, verifySignatureAsAuthenticator, requireAuthenticatedOrSigned, mcpToolNameResolver, MUTATING_TASKS, } from '@adcp/client/server'; import { BrandJsonJwksResolver, InMemoryReplayStore, InMemoryRevocationStore } from '@adcp/client/signing/server'; serve(createAgent, { authenticate: requireAuthenticatedOrSigned({ signature: verifySignatureAsAuthenticator({ capability: { supported: true, required_for: ['create_media_buy'], covers_content_digest: 'either' }, jwks: new BrandJsonJwksResolver(), replayStore: new InMemoryReplayStore(), revocationStore: new InMemoryRevocationStore(), resolveOperation: mcpToolNameResolver, }), fallback: verifyApiKey({ keys: { 'sk_live_abc': { principal: 'acct_42' } } }), requiredFor: [...MUTATING_TASKS], resolveOperation: mcpToolNameResolver, }), }); ``` `MUTATING_TASKS` は `@adcp/client/server` からエクスポートされる支出コミットと状態変更操作の完全なリストです — 自身のリストを保守するのではなくそれを使ってください。 ### JWKS リゾルバーオプション | Resolver | Use case | | ----------------------- | ---------------------------------------------------- | | `StaticJwksResolver` | 既知のバイヤー鍵の固定セット。開発/テストに良い。 | | `HttpsJwksResolver` | キャッシングとリフレッシュで URL から JWKS を取得。 | | `BrandJsonJwksResolver` | 完全なディスカバリーチェーン: brand.json → jwks\_uri → JWKS。本番に推奨。 | ## ステップ 5: インバウンド webhook を検証する(バイヤー / オーケストレーター) セラーが webhook を送るとき、真正性を確認するため署名を検証してください。webhook プロファイルはリクエスト署名と同じ RFC 9421 メカニクスを使いますが、`tag="adcp/webhook-signing/v1"` と `Content-Digest` が常にカバーされます(オプトアウトなし)。 ```typescript theme={null} import { verifyWebhookSignature, BrandJsonJwksResolver, InMemoryReplayStore, } from '@adcp/client/signing/server'; const jwks = new BrandJsonJwksResolver(); const replayStore = new InMemoryReplayStore(); app.post('/webhook', async (req, res) => { try { await verifyWebhookSignature(req, { jwks, replayStore }); } catch { return res.status(401).json({ error: 'invalid webhook signature' }); } // Process the verified webhook... }); ``` ```python theme={null} from adcp.signing import ( WebhookVerifyOptions, BrandJsonJwksResolver, InMemoryReplayStore, verify_webhook_signature, ) from adcp.signing.errors import SignatureVerificationError webhook_options = WebhookVerifyOptions( jwks_resolver=BrandJsonJwksResolver(), replay_store=InMemoryReplayStore(), ) @app.post("/webhook") async def webhook(request: Request): body = await request.body() try: sender = verify_webhook_signature( method=request.method, url=str(request.url), headers=dict(request.headers), body=body, options=webhook_options, ) except SignatureVerificationError: raise HTTPException(status_code=401, detail="invalid webhook signature") # sender.key_id, sender.agent_url available for audit; process the webhook. ``` ```go theme={null} import ( "github.com/adcontextprotocol/adcp-go/adcp/signing" ) // Mount the same Middleware on your webhook receiver, but configure it for // the webhook profile — adcp_use="request-signing" (deprecated // "webhook-signing" also accepted), Content-Digest required, no required_for // gating (webhooks always carry signatures). webhookMW := signing.Middleware(signing.MiddlewareOptions{ Resolver: signing.NewBrandJSONJWKSResolver(), Replay: signing.NewMemoryReplayStore(0), Revocation: signing.NewStaticRevocationSource(nil), Profile: signing.ProfileWebhookSigning, ContentDigestPolicy: signing.DigestRequired, }) http.Handle("/webhook", webhookMW(webhookHandler)) func webhookHandler(w http.ResponseWriter, r *http.Request) { sender := signing.VerifiedSignerFromContext(r.Context()) // sender.KeyID, sender.AgentURL — process the verified webhook. } ``` ## ステップ 6: アウトバウンド webhook に署名する(セラー) `createAdcpServer` に `signerKey` を渡すと、フレームワークがすべてのアウトバウンド webhook に自動署名します: ```typescript theme={null} serve(() => createAdcpServer({ name: 'My Seller', version: '1.0.0', webhooks: { signerKey: { keyid: 'my-seller-webhook-2026', alg: 'ed25519', privateKey: webhookPrivateJwk, }, }, mediaBuy: { /* ... */ }, })); ``` 各アウトバウンド webhook を `sign_webhook` で署名し、送信前に返されたヘッダーを付けます: ```python theme={null} from adcp.signing import sign_webhook, load_private_key_pem import httpx, json private_key = load_private_key_pem(open("webhook-private-key.pem", "rb").read()) async def post_webhook(url: str, payload: dict) -> None: body = json.dumps(payload).encode("utf-8") headers = {"Content-Type": "application/json"} signed = sign_webhook( method="POST", url=url, headers=headers, body=body, private_key=private_key, key_id="my-seller-webhook-2026", alg="ed25519", ) headers.update(signed.as_dict()) # adds Signature, Signature-Input, Content-Digest async with httpx.AsyncClient() as client: await client.post(url, content=body, headers=headers) ``` `ProfileWebhookSigning` で `Signer` を構成し、`SignRequest` またはその `RoundTripper` 経由で使います: ```go theme={null} priv, _, _ := signing.LoadPrivateKey(webhookPemBytes) webhookSigner, _ := signing.NewSigner(signing.SignerOptions{ KeyID: "my-seller-webhook-2026", Algorithm: signing.AlgEd25519, PrivateKey: priv, Profile: signing.ProfileWebhookSigning, }) webhookClient := &http.Client{ Transport: webhookSigner.RoundTripper(http.DefaultTransport, true /* always cover content-digest for webhooks */), } // Use webhookClient.Post / .Do to deliver webhooks; signatures are added automatically. ``` webhook 署名公開鍵を `"adcp_use": "request-signing"` を持つ JWK として公開してください。webhook 検証者は後方互換性のため非推奨の `"webhook-signing"` 鍵を依然として受理しますが、新しい署名者は `request-signing` を使うべきです。独立した webhook ローテーションまたは爆発半径分離が欲しい場合、webhook 固有の `kid` を持つ別の `request-signing` JWK を公開してください。 ## ステップ 7: ケイパビリティを宣言する セラーがインバウンド署名を検証する場合、バイヤーが署名すべきと分かるよう `get_adcp_capabilities` レスポンスで `signed_requests`(オンワイヤースキーマでのエイリアス `request_signing`)を宣言してください: ```typescript theme={null} createAdcpServer({ capabilities: { overrides: { signed_requests: { supported: true, required_for: ['create_media_buy', 'update_media_buy'], supported_for: ['sync_creatives', 'sync_audiences'], covers_content_digest: 'either', }, }, }, mediaBuy: { /* ... */ }, }); ``` ```python theme={null} from adcp.server.responses import capabilities_response class MySeller(ADCPHandler): async def get_adcp_capabilities(self, params, context=None): return capabilities_response( ["media_buy"], request_signing={ "supported": True, "required_for": ["create_media_buy", "update_media_buy"], "supported_for": ["sync_creatives", "sync_audiences"], "covers_content_digest": "either", }, ) ``` ```go theme={null} // In your get_adcp_capabilities handler, set the request_signing block on // the response builder: return adcp.CapabilitiesResponse(adcp.CapabilitiesData{ SupportedProtocols: []string{"media-buy"}, RequestSigning: &adcp.RequestSigningCapability{ Supported: true, RequiredFor: []string{"create_media_buy", "update_media_buy"}, SupportedFor: []string{"sync_creatives", "sync_audiences"}, CoversContentDigest: "either", }, }), nil ``` バイヤーは `get_adcp_capabilities` を呼び、`request_signing.required_for` と `supported_for` を読んで、あなたがどの操作に署名を期待するかを知ります。 ## 鍵ローテーション JWKS エンドポイントはゼロダウンタイムローテーションのため複数の鍵を同時にサポートします: 1. 新しい `kid` を持つ新しいキーペアを生成 2. 新しい公開鍵を JWKS に追加(古いものと新しいもの両方が公開される) 3. 新しい秘密鍵を使うよう署名設定を更新 4. 24〜48 時間後、古い公開鍵を JWKS から削除 緊急ローテーション(鍵侵害)には、古い `kid` を失効リストの `revoked_kids` に追加し、即座に新しい鍵にローテートしてください。失効リスト形式については [Revocation](/docs/building/by-layer/L1/security#revocation) を参照してください。 ## Testing ### 適合性ベクター 仕様は `compliance/cache/3.0.0/test-vectors/request-signing/`(ソースは `static/compliance/source/test-vectors/request-signing/`)で **39 個のテストベクター** を出荷します: * **12 個の正ベクター**: 検証者が受理しなければならない有効な署名付きリクエスト(非 4xx) * **27 個の負ベクター**: 検証者が `401` と正しいエラーコードで拒否しなければならない無効なリクエスト ```bash theme={null} # Debug a single vector adcp signing verify-vector \ --vector compliance/cache/3.0.0/test-vectors/request-signing/positive/001-basic-post.json ``` ### 検証者をグレードする ```bash theme={null} adcp grade request-signing https://agent.example.com/mcp --auth-token $TOKEN ``` ### エラーコード 検証が失敗するとき、`WWW-Authenticate: Signature error=""` を伴う `401` を返します: | Code | Meaning | | ----------------------- | --------------------- | | `missing_signature` | 必要なときに署名ヘッダーが存在しない | | `invalid_signature` | 署名が公開鍵に対して検証されない | | `expired_signature` | 署名タイムスタンプが古すぎる | | `replayed_nonce` | nonce が既に使われた | | `revoked_key` | 鍵が失効された | | `unknown_key` | 鍵 ID が JWKS に見つからない | | `unsupported_algorithm` | アルゴリズムが allowlist にない | 完全なエラーコード分類については [Transport error taxonomy](/docs/building/by-layer/L1/security#transport-error-taxonomy) を参照してください。 ## 関連 * [Security: Signed Requests](/docs/building/by-layer/L1/security#署名付きリクエストトランスポート層) — 検証者チェックリスト、正準化ルール、リプレイ dedup サイジングを伴う規範的仕様 * [Push Notifications](/docs/building/by-layer/L3/webhooks) — 署名検証を含む webhook セットアップ * [エージェントを検証する](/docs/building/verification/validate-your-agent) — 署名適合性を含む完全なコンプライアンス検証 * [エージェントをビルドする](/docs/building/by-layer/L4/build-an-agent) — SDK セットアップとストーリーボード検証 * [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421) — HTTP Message Signatures 仕様 # セキュリティ Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L1/security AdCP セキュリティガイド: 金融オペレーションのリスク分類、Webhook HMAC 検証、リプレイ防止、アクセス制御、本番デプロイの認証情報管理。 **本番利用で重要** AdCP は金銭的なコミットメントと機微なキャンペーンデータを扱う可能性があります。実際の広告予算を管理する実装は、本書に概説するセキュリティ対策を実装しなければなりません。 ***なぜ*を探していますか?** このページは規範的な実装リファレンス — コンフォーマントなエージェントが従うルールです。脅威モデル、層状防御の物語、ブランド IT と CISO 向けのチェックリストは、[Security Model](/docs/building/concepts/security-model) を参照してください。 ## 概要 AdCP は次のような高リスク環境で動作します: * **金銭取引**: 実際の広告費が動く * **複数主体の信頼**: 認証済みエージェント、パブリッシャー、オーケストレーター間の連携が必要 * **機微なデータ**: 1P シグナル、未公開クリエイティブ、競合ターゲティング戦略を含む * **非同期オペレーション**: 複数のシステムとプロトコルにまたがる ## リスク分類 ### 高リスクオペレーション(金融) これらのオペレーションは実際の広告予算をコミットします: | Operation | Risk | Primary Threat | | ------------------ | ---------------------------------------- | ------------------------------ | | `create_media_buy` | Creates financial commitments | Budget fraud, credential theft | | `update_media_buy` | Modifies budgets and campaign parameters | Unauthorized modifications | **要件:** * 短命な認証情報 — 漏洩したトークンの影響範囲に見合ったサイズにする。支出をコミットできるトークンには 1 時間以内が妥当なデフォルト。相当な閾値を超える支出をコミットできる、または組織境界をまたぐトークンには 15 分以内が適切。最小の数字をデフォルトにするのではなく、選択したウィンドウを文書化して正当化する。 * トランザクション整合性のためのリクエスト署名 * 大規模予算向けの多要素認証または承認ワークフロー * 改ざん不可能なログによる完全な監査証跡 ### 中リスクオペレーション(データアクセス) これらのオペレーションは機微なビジネスデータにアクセスします: | Operation | Risk | | ------------------------ | ---------------------------------------------- | | `get_media_buy_delivery` | Exposes performance metrics and spend data | | `list_creatives` | Access to creative assets | | `sync_creatives` | Uploads potentially sensitive creative content | ### 低リスクオペレーション(ディスカバリー) これらのオペレーションは公開アクセス可能です: | Operation | Risk | | ----------------------- | -------------------------- | | `get_adcp_capabilities` | Agent capability discovery | | `get_products` | Public inventory discovery | | `list_creative_formats` | Public format catalog | ## Webhook セキュリティ AdCP 3.0 は Webhook 署名を [AdCP RFC 9421 プロファイル](#webhook-callbacks)に統一します — セラーはオペレーターの `brand.json` の `agents[].jwks_uri` を通じて公開した鍵でアウトバウンド Webhook に署名し、バイヤーはその JWKS に対して検証します。パブリッシャーの `adagents.json` がそのセラーに `signing_keys[]` をピン留めする場合、そのピンが権威的です。秘密はワイヤーを渡らず、アイデンティティはインバウンドリクエストと同じ方法で暗号学的に確立されます。 **9421 Webhook 署名は 3.0 でベースライン必須です。** Webhook を発行するセラーは、バイヤーが `push_notification_config.authentication` または `accounts[].notification_configs[].authentication` を設定して下記のレガシースキームに明示的にオプトインしない限り、[Webhook callbacks](#webhook-callbacks) プロファイルに従って署名しなければなりません(MUST)。 ### レガシー HMAC-SHA256 フォールバック(非推奨、4.0 で削除) 9421 プロファイルをまだ採用していないレシーバーと相互運用する必要のあるバイヤーは、`push_notification_config.authentication.credentials` または `accounts[].notification_configs[].authentication.credentials` を設定してオプトインしてもよい(MAY)。バイヤーのリクエストに `authentication` が存在する場合、セラーは [Push Notifications](/docs/building/by-layer/L3/webhooks#legacy-hmac-sha256-fallback) で定義されたセマンティクスを使って HMAC-SHA256 で署名します。レガシースキームは 3.x 専用の互換性の便宜です。セラーはサポートを断ってもよく(MAY)、AdCP 4.0 で削除されます。 セラーがサポートを選択した場合のレガシースキームの規範ルール: * **アルゴリズム**: HMAC-SHA256 のみ * **署名メッセージ**: `{unix_timestamp}.{raw_http_body_bytes}` — JSON を決して再シリアライズしない * **バイト等価性の不変条件**: HMAC は、パースされた JSON 値ではなく生のバイト上で計算されます。署名者と検証者はワイヤー上のバイトを直接比較しなければなりません(MUST)。ペイロードを再パース・再シリアライズすると — ライブラリが一致しコンパクトセパレーターを使っていても — 署名されたバイトを再現する保証はありません。キー順序、ユニコードエスケープポリシー、数値表現がシリアライザー間で発散するためです(具体例は下記「正準化されない側面」を参照)。このスキームは正準的な JSON 形式を定義しません。下記の「正準的なワイヤー形式」と「検証者の入力」ルールは、署名者側と検証者側でそれぞれ最も一般的なバイトドリフトの失敗を狭めますが、バイトレベルの発散を排除しません。 * **正準的なワイヤー形式**: `{raw_http_body_bytes}` は、署名者が HTTP ボディとしてワイヤーに載せるバイトとバイト単位で同一でなければなりません(MUST)。署名者が JSON 値をシリアライズしてボディを構築する場合、JSON のコンパクトセパレーター `","`(項目セパレーター)と `":"`(キーセパレーター)を使わなければなりません(MUST)— トークン間に空白なし。言語レベルのシリアライザー JavaScript `JSON.stringify`、Go `encoding/json` `json.Marshal`、Ruby `JSON.generate`、Java Jackson `writeValueAsString` はデフォルトでコンパクト出力を生成します。それらをラップする HTTP クライアント(axios、`json.Marshal` されたボディを持つ Go `net/http`、`JSON.generate` を持つ Ruby `Net::HTTP`、Jackson を持つ Java OkHttp)はそのデフォルトを継承します。Python では `httpx` はコンパクトセパレーターでシリアライズしますが、stdlib `json.dumps` はデフォルトで `", "` / `": "` になり、`separators` kwarg なしでペイロードを `json.dumps` に渡す HTTP クライアント(`requests(json=...)`、`aiohttp`)は空白入りのボディを発行します — それらのパスの署名者は `separators=(",", ":")` を明示的に渡さなければなりません(MUST)。この列挙は網羅的ではありません。署名者はこのリストに頼るのではなく、HTTP クライアントの実際のワイヤー上のシリアライズを検証しなければなりません(MUST、例: プロキシやフックでリクエストボディをキャプチャ)。署名は、署名者がシリアライズしたオブジェクトではなく、レシーバーが見るバイトをカバーします。 * **正準化されない側面**: キー順序、ユニコードエスケープポリシー、数値表現はこのスキームで正準化されません。特に数値については言語デフォルトが発散し(`JSON.stringify(1.0)` → `1`、Python `json.dumps(1.0)` → `1.0`、Go `json.Marshal(1.0)` → `1`。`0.1` のような浮動小数点や科学記法も同様の崖に当たる)、あるライブラリでシリアライズして送信前に別のライブラリで再パース・再シリアライズする署名者は、コンパクトセパレーターでも署名者-検証者ドリフトを生じさせ得ます — 上記のバイト等価性の不変条件が、このスキームを成立させる唯一のものです。 * **重複オブジェクトキー**: 署名者は重複オブジェクトキーを発行してはならず(MUST NOT)、シリアライズ前に上流呼び出し元からの重複キー入力を拒否しなければなりません(MUST)。署名者側の MUST は要となります。この失敗モードを捕捉できる唯一の場所だからです: 重複キーペイロードを黙って畳み込む署名者は、呼び出し元の意図と異なるセマンティクスを持つ暗号学的にクリーンな署名済みフレームを発行し、検証者はワイヤーから上流の発散を検出できません — 署名されたバイトは正常に見えます。署名者側のコンフォーマンスはワイヤー上で検証不能で、ランタイム検出ではなく帯域外の監査 / 相互運用テストで強制されることが期待されます(この形状は署名仕様では日常的で、COSE と JOSE は同じパターンを使います)。検証者は、HMAC 検証が成功した後、重複オブジェクトキーを含むボディを拒否しなければならず(MUST)、構造化された不正ボディエラー(署名不一致エラーとは別 — 署名は有効。ボディが不正)を返します。RFC 8259 §4 に従い、JSON オブジェクト内の名前は「一意であるべき(SHOULD)」であり、非一意の名前を持つオブジェクトを受け取るソフトウェアの動作は予測不能です — したがって同じ HMAC 有効バイトをパースする 2 つの検証者は、パースされた値について一致しないことがあります。これはパーサー差分攻撃クラスです(cf. CVE-2017-12635。ある CouchDB パーサーが同じ署名済みボディから `roles=[]` を読み、別のパーサーが `roles=["_admin"]` を読んだ)。レガシー HMAC Webhook スキームで運ばれるすべてのボディは状態変更通知(クリエイティブステータス、メディアバイステータス、ガバナンス遷移)なので、MUST はこのスキームに無条件で適用されます。検出は重複キーを露出するパーサーを使わなければなりません(MUST)— それらを黙って破棄する last-wins/first-wins のデフォルトはこの要件を満たしません。署名者入力検証と検証者ボディチェックの両方についての言語ごとの strict-parse エスケープハッチ: 正準の非網羅的列挙(デフォルトで strict に*見える*だけでデータキー重複を黙って畳み込むライブラリを含む)は [Webhook 検証者チェックリストのステップ 14](#webhook-callbacks)を参照してください。検証者側のコンフォーマンスフィクスチャは `static/test-vectors/webhook-hmac-sha256.json` の `duplicate-keys-conflicting-values`(`expected_verifier_action: "reject-malformed"`)です。署名者側のコンフォーマンスフィクスチャは同じファイルの `signer_side.rejection_vectors` にあります: `signer-upstream-duplicate-key-rejection`(トップレベル)、`signer-upstream-duplicate-key-deep-nested`(署名者のチェックがトップレベルキーだけでなくネストされたオブジェクトに再帰することを検証)、`signer-upstream-duplicate-key-array-contained`(署名者のチェックが配列内のオブジェクトに降りることを検証 — オブジェクトには再帰するが配列メンバーには再帰しない手書きバリデーターの盲点)、`signer-upstream-duplicate-key-three-deep`(ウォーカーが浅い固定深度で止まらないことを検証)。正例フィクスチャ `signer-upstream-clean-input` が `signer_side.positive_vectors` にあり、すべてを拒否する署名者が負例フィクスチャを些細にパスしないようにします — 相互運用ハーネスは、重複キー入力の拒否とクリーン入力の受け入れの両方をアサートしなければなりません(MUST)。上流入力の拒否をログやエラーレスポンスで表面化する署名者は、[Webhook 検証者チェックリストのステップ 14b](#webhook-callbacks)で定義された同じキー名サニタイズルール(最初の非印字文字で `` に切り詰め、32 バイト以下の最後の UTF-8 コードポイントに切り詰め、数を 4 で上限)を適用しなければなりません(MUST)— 署名者側のチャネルは検証者側のチャネルと同じ攻撃者制御バイト形状を持ち、信頼の方向が逆になっているだけです。**エラー識別子は規範的、エラーオブジェクトの内部は非規範的。** 署名者がエラーで拒否を表面化する場合、エラー識別子(判別ユニオンのエラーコード文字列、型付き throw イディオムの例外クラス名、直和型のタグ)は正確に `duplicate_key_input` でなければなりません(MUST、大文字小文字を区別、接頭辞・接尾辞なし)— マルチ SDK 統合が `if (error.code === 'duplicate_key_input') { ... }` を書き、どの SDK がフレームに署名したかに関わらずディスパッチが機能するように。エラーキャリアの内部形状(サニタイズされたキーリストのフィールド名、オーバーフローマーカー文字列、型付き例外コンストラクター引数)は実装依存です。クラッシュ / フェイルクローズする検証者はコンフォーマントだが最適でない(リクエストは黙って受け入れられないが、送信者は actionable なエラーコードを受け取らない)。検証者は代わりに構造化された不正ボディエラーを返すべきです(SHOULD)。非コンフォーマントな失敗モード — 署名検証者のパースがダウンストリームのビジネスロジックのパースと発散する黙った受け入れ — は現在禁止されています。ペイロードをビジネスロジックに渡す前に重複キーを検出しない検証者はこのスキームに準拠しません。 * **検証者の入力**: 検証者は、いかなる JSON パースや再シリアライズの前にキャプチャした、ワイヤー上で受信した生の HTTP ボディバイトを使わなければなりません(MUST)。すべての現代的な HTTP フレームワークはパース前の生ボディフックを公開します(Express `express.raw()`、FastAPI `Request.body()`、aiohttp `Request.read()`、`json.Unmarshal` 前の Go `io.ReadAll(r.Body)`)。生キャプチャフックは同じルート上のいかなる JSON パースミドルウェアの前に実行しなければなりません(MUST)。検証者が実行される前にリクエストボディを消費するグローバルにマウントされた `express.json()` または FastAPI `BaseModel` ボディバインディングは、署名されたバイトではなく再文字列化されたペイロード上で検証者を動作させます — これは一般的なデプロイミスです。検証者はパースされたペイロードを再シリアライズして署名されたバイトを再構築すべきではありません(SHOULD NOT): 再シリアライズは、キー順序・ユニコードエスケープ・数値フォーマットが異なる署名者に対して黙って失敗し、検証者が表面化すべき署名者のバグを隠します。生バイトを本当にキャプチャできない検証者は、再シリアライズされた近似を受け入れるのではなく、フェイルクローズしてインフラのギャップを表面化しなければなりません(MUST)。 * **タイムスタンプソース**: 署名メッセージ内の `{unix_timestamp}` は、`X-ADCP-Timestamp` ヘッダーで送られた正確な ASCII 整数でなければなりません(MUST)。署名者と検証者はいかなるボディフィールドからもそれを導出してはなりません(MUST NOT)。 * **タイミングセーフ比較**: 定数時間比較を使わなければなりません(MUST、例: `timingSafeEqual`) * **リプレイウィンドウ**: `|current_time - timestamp| > 300` 秒のリクエストを拒否 * **最小シークレット長**: 32 バイト * **ヘッダー形式**: `X-ADCP-Signature: sha256=` と `X-ADCP-Timestamp: `。ボディレベルの `signature` フィールドは便宜的なコピーであり、ヘッダーより信頼してはなりません(MUST NOT)。 **検証順序**(レガシースキーム): 1. `X-ADCP-Signature` または `X-ADCP-Timestamp` ヘッダーが欠落していれば拒否 2. タイムスタンプが非数値なら拒否 3. タイムスタンプが 5 分ウィンドウ外なら拒否 4. HMAC を計算して比較 **シークレットローテーション**(レガシースキーム): * レシーバーはローテーション中、現在と以前の両方のシークレットからの署名を受け入れなければなりません(MUST) * ローテーションウィンドウはリプレイウィンドウ(5 分)を超えるべきではありません(SHOULD NOT) * パブリッシャーはローテーション時に即座に新しいシークレットで署名を開始します ### Webhook URL 検証(SSRF) バイヤー、セラー、またはガバナンスエージェントが他者にフェッチさせるために提供する任意の URL は SSRF ベクトルです。これには `push_notification_config.url`、`accounts[].notification_configs[].url`、`accounts[].governance_agents[].url`(セラーが `check_governance` を呼ぶときにフェッチ)、コレクションリストの `webhook_url`、TMP プロバイダーの `endpoint`、`adagents.json` の `authoritative_location`、`reporting_bucket.setup_instructions` が含まれます。 `sync_accounts.accounts[].notification_configs[]` を通じて登録されるアカウントレベルの Webhook サブスクライバーも、アクティベーション前にエンドポイント所有権の証明を必要とします。SSRF 検証はセラーが内部ネットワークアドレスを呼んでいないことを証明します。バイヤーがパブリック HTTPS エンドポイントを制御することは証明しません。セラーは、新規または変更されたアクティブなサブスクライバーをアクティブとして扱う前に、RFC 9421 署名付きのアクティベーションチャレンジまたは同等の制御証明を完了しなければならず(MUST)、レシーバーはチャレンジをエコーする前にセラーアイデンティティ、配信認証メタデータ、イベントタイプセットを検証しなければなりません(MUST)。一時停止されたサブスクライバー(`active: false`)は非アクティブ中のアウトバウンド証明チャレンジのみスキップできます。セラーは書き込み時に URL パース、HTTPS、ホスト名正規化、予約範囲拒否を依然として強制しなければならず(MUST)、一時停止されたサブスクライバーは再アクティベートされるまで発火を受け取ってはなりません(MUST NOT)。標準チャレンジペイロードとレスポンス形状は [sync\_accounts endpoint proof of control](/docs/accounts/tasks/sync_accounts#endpoint-proof-of-control) で定義されています。 相手方が制御する URL へのアウトバウンドフェッチの前に、フェッチャーは次を行わなければなりません(MUST): 1. **本番で非 HTTPS URL を拒否する。** 2. **ホスト名を解決し**、解決された IP がいずれかの予約範囲に入る場合フェッチを拒否する: * IPv4: RFC 1918(`10.0.0.0/8`、`172.16.0.0/12`、`192.168.0.0/16`)、RFC 6598 CGNAT(`100.64.0.0/10`)、ループバック(`127.0.0.0/8`)、リンクローカル(`169.254.0.0/16` — AWS/GCP/Azure/Alibaba のインスタンスメタデータで使われる `169.254.169.254` を明示的に含む)、ブロードキャスト(`255.255.255.255`)、`0.0.0.0/8`、マルチキャスト(`224.0.0.0/4`)。 * IPv6: ループバック(`::1`)、ユニークローカル(`fc00::/7`)、リンクローカル(`fe80::/10`)、IPv4 マップ(`::ffff:0:0/96` — 予約 IPv4 を IPv6 にマップする最も一般的なバイパス)、マルチキャスト(`ff00::/8`)、AWS IMDSv2 の fd00:ec2::254 アドレス。 3. **接続を検証済み IP にピン留めする。** DNS ベースのフィルタリングだけでは DNS リバインディングに脆弱です: 攻撃者は検証時にパブリック IP を、接続時にプライベート IP を提供します。フェッチャーは接続をピン留めしなければなりません(MUST)。**推奨**: (a) 検証済み IP を TCP connect 呼び出しに直接渡し、`Host:` ヘッダーを URL から設定する。**フォールバック**(HTTP クライアントが事前解決 IP を受け付けられない場合のみ): (b) いかなるリクエストボディを送る前に、ソケットのハンドシェイク後ピアアドレスを予約範囲リストに対して検証する。注: (b) は最初のボディバイトが出荷される前に発火するピアアドレスフックをクライアントライブラリが公開することに依存します。多くの一般的なライブラリはそうしないため、(b) を選ぶ実装はテストでフックを検証しなければなりません(MUST)。ピン留めなしの DNS 再解決は不十分です。 4. **相手方制御の URL をフェッチする際、リダイレクトの追従を拒否する**(30x レスポンスは、オリジンが最初のチェックをバイパスした予約アドレスにリダイレクトすることを許す)。2 つの制限された例外があり、各ホップでステップ 1-3 を再検証しチェーンに上限を設ける: [brand.json 解決](#buyer-identity-resolution)(1 リダイレクト、チェーンなし)と、初回の `/.well-known/adagents.json` フェッチ(標準の apex→www ホスティングが解決するよう、**同一登録可能ドメイン**リダイレクトのみ追従 — `apex ↔ www`、HTTPS 保持、3 ホップ以下、最初に要求されたドメインに固定 — [managed networks](/docs/governance/property/managed-networks#why-not-http-redirects) を参照)。`adagents.json` の `authoritative_location` 参照は例外を取りません: その 2 番目のホップでのリダイレクトは拒否しなければなりません(MUST)。 5. **レスポンスサイズとタイムアウトに上限を設ける。** 推奨: 5 MB ボディ上限、10 秒接続、10 秒読み取り。唯一の例外は、マネージドネットワーク間接パターンでの参照解決された権威的ファイル — ポインターファイルの `authoritative_location` がネットワークオリジンにリダイレクトした後の 2 番目のホップのみ — で、パブリッシャーネットワーク横断でファンアウトするため推奨 20 MB 上限を使います。ポインターファイル自体は 5 MB のままです。[managed networks security](/docs/governance/property/managed-networks#security-considerations) を参照。 6. **URL を提供したエージェントにフェッチエラーをエコーしない。** 詳細なエラーメッセージ(接続拒否 vs タイムアウト vs TLS 失敗)は、内部ネットワークトポロジーを探るサイドチャネルです。 #### 宛先ポート: デフォルトで寛容 パブリッシャーは、相手方が供給する URL(`push_notification_config.url`、コレクションリスト `webhook_url`、TMP プロバイダー `endpoint` など)に対して、デフォルトで宛先ポート許可リストを強制すべきではありません(SHOULD NOT)。URL 契約は `format: "uri"` のみで、プロトコルはポートを制約しません。バイヤーは正当に非標準 TLS ポートで Webhook レシーバーをホストします — Tomcat デフォルト `:9443`、Spring Boot デフォルト `:4443`、パスルーティングのマルチテナントゲートウェイ、テナントごとのポート付きサブドメイン切り出し — そしてデフォルトのポート許可リストは、パブリッシャーオペレーターにリストの拡張を頼む以外の手段なく、それらを黙って拒否します。 プロトコルが依拠する SSRF ガードは、上記ステップ 2-3 の **IP 範囲チェック + DNS リバインディング耐性のある接続ピン**であり、ポートフィルタリングではありません。予約範囲チェックは現実的な SSRF 脅威(`10.0.0.0/8`、`127.0.0.0/8`、`169.254.169.254` などの内部サービスへのトラフィック密輸)をカバーします。ルーティング可能なパブリック IP の上でのポートフィルタリングは、コスト(コンフォーマントなバイヤーの拒否)が通常その利益を上回る限界的な防御です。 多層防御として宛先ポート許可リストを望むオペレーター — 例えば、パブリッシャーの egress ファイアウォールがすでにアウトバウンドポートを制限するロックダウンされたエンタープライズ環境 — は、`{443, 8443}` を妥当なハードモードの出発点として、SDK またはデプロイ設定で明示的にオプトインすべきです(SHOULD)。`DEFAULT_ALLOWED_PORTS` 定数を出荷する SDK は、それを「制限なし」にデフォルトしなければならず(MUST)、`{443, 8443}` をデフォルトとしてではなくオプトインプロファイルとして表面化します。ハードモードをアクティブにするセラーは、バイヤーが最初の Webhook 配信時に制約を発見する前に統合をサイズできるよう、オペレーター向けドキュメントに許可ポートセットを文書化しなければなりません(MUST)。 ワイヤーレベルの URL 契約は **`format: "uri"` を超えて制約されません**。ハードモードのポートフィルタリングはオペレーター側のポリシー選択であり、プロトコル側の要件ではありません。 機能固有のセキュリティセクションは、これらのルールを独自のライフサイクルとコンテンツ処理要件で拡張します: * [オフラインレポートバケット](/docs/media-buy/media-buys/optimization-reporting#security-considerations-for-offline-delivery) — IAM 層のプレフィックススコープ、アカウントステータス変更時の認証情報失効。 * [コレクションリスト](/docs/governance/collection/tasks/collection_lists#security-considerations) — `auth_token` スコープと失効、配信 ID 検証、Webhook 署名の規範ルール。 * [マネージドネットワークの `authoritative_location`](/docs/governance/property/managed-networks#security-considerations) — バリデーターのフェッチセマンティクス、変更検出、関係終了。 * [TMP プロバイダー登録](/docs/trusted-match/specification#provider-registration-security) — 動的登録認証、ルーター-プロバイダー間認証、`/health` の情報漏洩ルール。 ## 認証のベストプラクティス ### 認証情報の保管 ```javascript theme={null} // Use secure key management systems // Never commit credentials to version control // Use environment variables or secret managers // Example: Secure credential retrieval async function getCredentials(agentId) { // Retrieve from secure storage (AWS KMS, Vault, etc.) const encrypted = await secretManager.get(`agent/${agentId}/apiKey`); return decrypt(encrypted); } ``` ### トークンの有効期限 高リスクオペレーションには短命なトークンを使います: ```javascript theme={null} const TOKEN_LIFETIMES = { discovery: 3600, // 1 hour for read operations financial: 900, // 15 minutes for financial operations refresh: 86400 // 24 hours for refresh tokens }; function validateToken(token, operationType) { const decoded = jwt.verify(token, secret); const maxAge = TOKEN_LIFETIMES[operationType] || TOKEN_LIFETIMES.discovery; if (Date.now() - decoded.iat > maxAge * 1000) { throw new Error('Token expired for this operation type'); } return decoded; } ``` ## エージェントとアカウントの分離 あらゆる状態 — メディアバイ、クリエイティブ、冪等性キャッシュエントリ、セッション ID、ガバナンストークン — は、それを所有する[アカウント](/docs/reference/glossary#a)にスコープされます。クロスアカウント読み取りは、存在を漏らすのではなく汎用の「not found」を返さなければなりません(MUST)。認証済み[エージェント](/docs/reference/glossary#a)は、セラーが*誰が呼んでいるか*を知る手段です。リクエストの `account` は*その呼び出しが作用している請求関係*です。分離には両方のチェックが必要です。 セールスエージェントは次を行わなければなりません(MUST): 1. **作成時にバインド** — 各オブジェクト(メディアバイ、クリエイティブ、セッションなど)を、それを作成したリクエストで使われたアカウントに恒久的に関連付ける。 2. **アクセス時に検証** — 後続の各読み取りまたは変更で、認証済みエージェントがオブジェクトのバインドされたアカウントへのアクセス権を持つことを検証する。 3. **フェイルクローズ** — 検証が失敗した場合、汎用エラーを返す(ステータス 403 または 404 が許容されるが、ボディは「未認可」を「not found」と区別したりアカウントを名指ししたりしてはならない)。決してリソースクエリにフォールスルーしない。 これらのルールが強制する請求関係モデルは [Accounts & Security — Data Isolation](/docs/media-buy/advanced-topics/accounts-and-security#data-isolation) を、[Account](/docs/reference/glossary#a) と [Agent](/docs/reference/glossary#a) の正式な定義はグロッサリーを参照してください。 ### 二段階パターン スキーマが `account` を要求するすべてのアカウントスコープリクエストは、明示的な `AccountRef`(アカウント ID 名前空間では `account_id`、バイヤー宣言アカウントでは `{brand, operator}` 自然キー)を運びます。セラーは、欠落した必須 `account` を認証情報が示すデフォルトで黙って置き換えてはなりません(MUST NOT)。`account` が任意のタスクでは、省略セマンティクスはタスクローカルで、そのタスクが文書化しなければなりません。正しい分離は、順に実行される 2 つのチェックです: 1. **認可プリチェック** — リクエストの `account` は認証済みエージェントの認可セット内になければなりません(MUST)。403 または汎用の「not found」でフェイルクローズ(決して「あなたはそのアカウントに認可されていません」ではない — それは存在の漏洩です)。 2. **リソースクエリ** — リクエストの `account_id` を主キー制約としてフィルタリング。認可セット全体ではなく、このリクエストが作用している特定のアカウントのみで。 ```javascript theme={null} // Two-step: precheck request account is authorized, then scope the query to it. // authorizedAccountIds is a Set populated once at auth-time, not an Array. // Set.has() is O(1); Array.includes() is O(n) and scans element-by-element, which // on large authorized-account sets introduces a timing difference between early // and late matches that a caller can probe across requests. async function getMediaBuy(mediaBuyId, requestAccountId, authAgent) { // Step 1: auth precheck if (!authAgent.authorizedAccountIds.has(requestAccountId)) { // Generic error - don't reveal whether the account exists throw new NotFoundError("Media buy not found"); } // Step 2: resource query scoped to the specific account const mediaBuy = await db.mediaBuys.findOne({ id: mediaBuyId, account_id: requestAccountId // Primary filter }); if (!mediaBuy) { // Generic error - same shape as the precheck failure throw new NotFoundError("Media buy not found"); } return mediaBuy; } ``` by-ID ルックアップで*全体の*認可セットでフィルタリングするのは退行です: アカウント A の下で発行された `get_media_buy(X)` は、両方がエージェントの認可セット内にあれば、アカウント B が所有するバイに対して成功してしまいます。リクエストが供給する `account_id` が、ルックアップを呼び出し元の*表明された*意図に結び付けるものです。 ### 行レベルセキュリティ 最も一般的な分離の失敗は、**結合またはネストされた関係を介した IDOR** です: クエリが主テーブルを `account_id` でスコープするが、同じプリンシパルでフィルタリングされなかった関連テーブル(ラインアイテム、クリエイティブ、配信行)から結合または返す。1 つのハンドラーのバグが壁を突き破れないよう、ハンドラーコードだけでなくデータ層でプリンシパルごとに防御します: ```sql theme={null} -- PostgreSQL example -- app.current_account is set by the auth layer AFTER the precheck above succeeds CREATE POLICY account_isolation ON media_buys USING (account_id = current_setting('app.current_account')::uuid); ALTER TABLE media_buys ENABLE ROW LEVEL SECURITY; ``` **リストエンドポイント**(明示的なアカウントフィルターなしの `get_media_buys`)では、RLS は認証時に設定されるセッション変数を介してエージェントの認可セットにスコープします: ```sql theme={null} CREATE POLICY account_isolation_list ON media_buys FOR SELECT USING (account_id = ANY(current_setting('app.authorized_accounts')::uuid[])); ``` ### クライアント側の分離: クロスプリンシパルのツールコール混同 上記のルールはサーバー側の強制です。正当だが侵害されたエージェントが呼び出し元であっても、セラーのデータを保護します。**クライアント側の相棒**は、プリンシパル X が供給したテキストにプリンシパル Y の権限を使うツールコールを駆動させないというバイヤーエージェントの義務です。 LLM 駆動のバイヤーエージェントは通常、複数のプリンシパルの認証情報を同時に保持します: 複数のセラー(セラーごとに 1 つの認証情報セット)と、エージェンシーエージェント内では複数のブランドアカウント。エージェントが処理する任意の信頼できない文字列 — セラーが返すプロダクト説明、ブリーフから継承されたキャンペーン名、エラーエンベロープの拒否理由、Webhook イベントボディ — は、それらのプリンシパルの*1 つ*から供給されたテキストです。エージェントのプランニングループが単一の LLM コンテキストからそれらすべてにわたってツールを呼べる場合、セラー X のテキストに注入されたプロンプトが、エージェントにセラー Y のエンドポイントで `create_media_buy` を呼ばせたり、ブランド A の予算をブランド B のインベントリに使わせたりできます。これはツールコール粒度での[混乱した代理人](https://en.wikipedia.org/wiki/Confused_deputy_problem)問題です: 攻撃者はサンドボックスを脱出する必要がありません — エージェント自身の正当な権限が損害を与えます。 LLM 駆動の AdCP エージェントを運用するオペレーターは、少なくとも次の制御を適用しなければなりません(MUST): 1. **テキストにその起源プリンシパルをタグ付けする。** LLM コンテキストがネットワークから取り込むすべての文字列(ツール結果、Webhook ボディ、レジストリドキュメント、クリエイティブメタデータ)は、それを生成した `{principal_domain, tool_name, response_field}` トリプルで内部的に注釈されなければなりません(MUST)。取り込み時に注釈を落とすことが、この防御が死ぬ場所です。 2. **ツールコールのターゲットを呼び出しプリンシパルに制限する。** ターゲットプリンシパルが、決定を駆動する文字列を供給したプリンシパルと同じでないツールコールは、(a) 拒否されるか、(b) 人間の承認ステップを通るか、(c) オペレーターが事前に宣言した明示的なプリンシパルごとのポリシーで仲介されなければなりません(MUST)。デフォルトは allow ではなく refuse でなければなりません(MUST)。 3. **認証情報スコープを LLM コンテキストごとに分離する。** 単一の LLM プランニングループは、利害が衝突し得るプリンシパル(例: 同じインベントリを競う 2 つのブランド。1 つのコンテキスト内のバイヤー認証情報とガバナンスエージェントの署名鍵)のライブ認証情報を保持してはなりません(MUST NOT)。スコープ分離は、LLM に指示するのではなく、プロセス / ツール登録層で強制されます — LLM は誤用のアフォーダンスを持ってはなりません(MUST NOT)。 4. **成功だけでなく、すべてのクロスプリンシパルの*試み*をログする。** ルール 2 の下での拒否は、オペレーターが監視しなければならないシグナルです(MUST)— あるプリンシパルからの拒否率の上昇は、あなたのエージェントを標的とする注入キャンペーンの最も早く検出可能な兆候です。 この脅威は通常のプロンプト注入とは異なります: 通常の注入は*1 つの*プリンシパルの権限内でデータを流出させたり未認可のツールコールをトリガーしたりします。クロスプリンシパル混同は、攻撃者が Y の認証情報を一度も保持せずに、プリンシパル X の信頼できないテキストを使ってプリンシパル Y の権限に到達します。上記のサーバー側 Layer 2 制御は、プリンシパル Y のアカウントがバイヤーエージェントの認可セットにまだない場合にのみ試みを検出します — ある場合(エージェンシーとマルチセラーエージェントの要点そのもの)、サーバーは正当に見える呼び出しを見ます。 プロトコルはこの規律をクライアントエージェントに強制できません。そのテストは運用的です: すべての LLM 駆動 AdCP バイヤーは、どのプリンシパルが同じプランニングコンテキストに一緒に現れられるか、クロスプリンシパルのツールコールを何がゲートするかを、書面で説明できなければなりません(MUST)。 ## 時間セマンティクス AdCP は管轄区域、アドサーバー、デイパートカレンダーをまたいで動作します。実装は時間について正確でなければならず(MUST)、さもなくばバイヤーとセラーは「午後 5 時までに配信」が何を意味したかで意見が食い違います。 ### タイムスタンプ形式 AdCP のリクエスト、レスポンス、Webhook ペイロードのすべてのタイムスタンプフィールドは、明示的なタイムゾーンオフセット付きの [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) でなければなりません(MUST)。 ``` ✅ 2026-04-19T10:00:00Z // UTC, recommended ✅ 2026-04-19T10:00:00-04:00 // explicit offset ❌ 2026-04-19T10:00:00 // no offset — ambiguous ❌ 2026-04-19 10:00:00 // not ISO 8601 ``` 実装は曖昧な(「ナイーブな」)タイムスタンプを `INVALID_REQUEST` で拒否しなければなりません(MUST)。実装はワイヤー上で UTC(`Z` サフィックス)を使い、プレゼンテーション層でローカル時刻に変換すべきです(SHOULD)。 ### 区間 AdCP のあらゆる時間ウィンドウ — フライト日、レポートウィンドウ、デイパートターゲティング、冪等性リプレイ TTL — は**半開区間** `[start, end)` を使います。開始タイムスタンプは含み、終了タイムスタンプは含みません。`start_time: 2026-04-01T00:00:00Z` と `end_time: 2026-05-01T00:00:00Z` のキャンペーンは 4 月中実行され、5 月の最初のティックで停止します。 ### デイパートターゲティング デイパート定義は**タイムゾーンセマンティクス**を宣言しなければなりません(MUST)— 時刻値が持つ 3 つの意味のどれか: * **バイヤー宣言ゾーン** — デイパートと並ぶ IANA ゾーン名(例: `timezone: "America/New_York"`)。デイパートは、視聴者やパブリッシャーの場所に関わらずそのゾーンに対して評価されます。バイヤーが「ニューヨーク時間の午後 9〜11 時」をグローバルに強制したいときに使います。 * **パブリッシャーローカル** — デイパートはパブリッシャーが宣言したローカルゾーンで評価されます。バイヤーが「パブリッシャーのスケジュール上のプライムタイム」を望み、それが何を意味するかをパブリッシャーに決めさせてよいときに使います。 * **視聴者ローカル** — デイパートは各視聴者のタイムゾーンに対して評価され、配信時に視聴者の場所シグナルから解決されます。バイヤーがグローバルオーディエンス横断で「ローカル午後 8 時に配信」を望むときに使います。 宣言されたセマンティクスのないデイパートは曖昧で、`INVALID_REQUEST` で拒否しなければなりません(MUST)。セラーは宣言されたセマンティクスを守らなければなりません(MUST)。セラーが要求されたモードをサポートできない場合(例: 単一ゾーンで動作するパブリッシャーは視聴者ローカルデイパートを配信できない)、セラーは黙って変換するのではなく `INVALID_REQUEST` で拒否しなければなりません(MUST)。エージェントごとのデフォルトは非規範的で、依拠してはなりません(MUST NOT)。 ## Request Safety ### 冪等性 `idempotency_key` は**すべての AdCP タスクリクエストで必須**です — 読み取りも変更系も同様。キーは `(認証済みエージェント, アカウント)` ごとにスコープされます — 同じセラー上の別エージェント、同じエージェント下の別アカウント、別セラーをまたいでは意味を持ちません。両次元でスコープすることで、1 つのエージェント(例: エージェンシー)が複数アカウントに作用するときのクロスアカウントキャッシュ衝突を防ぎます: アカウント A とアカウント B の下での同一に見える `create_media_buy` は 2 つの別個のバイであり、2 つにまたがってリプレイされる 1 つのキャッシュレスポンスにはなりません。 **強制カーブ。** セラーは 3.0 以降、`idempotency_key` を省略する**変更系**リクエストを `INVALID_REQUEST` で拒否しなければなりません(MUST、変更なし)。**読み取り**リクエストについては、ルールは 2 つのマイナーにわたって段階的に導入されます: * **3.1.0** — セラーは `idempotency_key` を運ぶ読み取りを受け入れ、ルール 2-9 に従って処理しなければなりません(MUST、未宣言のエンベロープフィールドで拒否しない)。セラーはそれを省略する読み取りを `INVALID_REQUEST` で拒否すべきです(SHOULD)。セラーは 3.1.x メンテナンスウィンドウの間は省略を受け入れてもよい(MAY)。 * **3.2.0** — セラーは `idempotency_key` を省略する読み取りを `INVALID_REQUEST` で拒否しなければなりません(MUST)。猶予ウィンドウは 3.2 のカットで閉じます。 この段階的強制により、手書きのバイヤー統合 — curl、薄い MCP クライアント、またはフィールドを一律に含めない OpenAPI codegen で構築 — が 3.1 のカットではなくリリースウィンドウにわたって移行できます。バイヤー SDK(`@adcp/client`、`adcp-py`)は今日すでに `idempotency_key` を一律に送っているため、SDK 利用の統合者はカット日の影響を受けません。 **なぜユニバーサルか — 読み取りツールを含む。** いくつかの AdCP タスクは多相です。`get_products` が正準のケースです: `buying_mode: 'brief'` / `'wholesale'` は同期的に完了する(純粋な読み取り)ことがありますが、キュレーションが上流クエリや HITL を必要とするとき同じツールが `Submitted` エンベロープを返してもよく(MAY)、`action: 'finalize'` 付きの `buying_mode: 'refine'` はプロポーザルを `expires_at` ホールドウィンドウ付きでコミット済みに遷移させるコミットです([refinement guide § Finalize is exclusive](/docs/media-buy/product-discovery/refinement) を参照)。バイヤーは呼び出し時に、ある呼び出しが純粋な読み取り、非同期タスク作成、コミットのどれになるかを予測できません — したがってワイヤー契約はすべての呼び出しで一律に `idempotency_key` を要求します。純粋な読み取りとして解決する呼び出しでは、キャッシュは TTL 内でバイト安定なリトライ時リプレイを提供し、これは無害でバイヤーに一律のリトライセーフな契約を与えます。非同期タスク作成またはコミットとして解決する呼び出しでは、キャッシュは変更系タスクと同じ at-most-once 保証を提供します。代替案 — バイヤーの SDK で呼び出しごとに読み取り vs 変更系を分類 — は、同じタスク名が読み取りと書き込みの両モードを持つとき実現不可能です。セラーが返す未知の `error.code` 値のデコード(猶予ウィンドウ中の `INVALID_REQUEST` でも、後のマイナーで追加されたコードでも)は [Forward-compatible decoding](/docs/building/by-layer/L3/error-handling#forward-compatible-decoding-normative) ルールに従います。 このセクションは AdCP タスクリクエストにのみ適用されます。OpenRTB 入札ストリームは独自のセマンティクス(`BidRequest.id` は冪等性キーではなくトランザクション ID)を持ち、スコープ外です。 #### 規範的なセラーの動作 1. **スキーマ検証が最初に実行される。** セラーは、冪等性キャッシュを参照する前に、リクエストをそのスキーマ(`idempotency_key` の存在と形式を含む)に対して検証しなければなりません(MUST)。不正なリクエストはキャッシュに一切触れずに `INVALID_REQUEST` を返します — さもなくばキャッシュミスがタイミングサイドチャネルになり、スキーマ検証がキー形式を受け入れたかを漏らします。検証エラーは決してキャッシュされません(ルール 2)。 2. **最初の呼び出しが正準。** **タスク成功**時(`status: completed`、または非同期オペレーションの `status: submitted`)、セラーは内側のレスポンスペイロード(プロトコルエンベロープではない)を `(authenticated_agent, account_id, idempotency_key)` でキーし、正準リクエストペイロードのハッシュと共に保存します。**キャッシュエントリは不変です** — TTL 内のリプレイは元々キャッシュされたペイロードを(`replayed: true` 付きで)返さなければならず(MUST)、そのペイロード内の状態追跡フィールドはリソースの現在の状態を反映するようリフレッシュされてはなりません(MUST NOT)。このルールは両方の成功ブランチにわたって適用されます: * **非同期タスク** — キャッシュされたレスポンスは `task_id` を含む `submitted` 結果です。非同期タスクがその後完了・失敗・キャンセルされても、リプレイは現在の終端状態ではなく元々キャッシュされた `submitted` レスポンスを返さなければなりません(MUST)。バイヤーは返された `task_id` を使い、最初の呼び出しと全く同じように `tasks/get` または Webhook で現在の状態を観測します。 * **同期成功タスク** — 初回レスポンスが状態追跡フィールド(例: `create_media_buy` の `status`, `packages`, `affected_packages`。`sync_creatives` / `sync_accounts` のレコードごとの `status` 配列。`acquire_rights` / `activate_signal` のリソーススナップショット)を運ぶ場合、リプレイはリソースへの介在する変更に関わらず元々キャッシュされたペイロードを返さなければなりません(MUST)。`status: pending_creatives` で作成され、その後 `update_media_buy` で `canceled` に変更されたメディアバイは、`status: pending_creatives` としてリプレイされます — キャッシュされたバイトは作成時レスポンスの履歴スナップショットであり、現在状態の読み取りではありません。バイヤーは現在の状態についてリソースの読み取りエンドポイント(`get_media_buys`, `list_accounts`, `list_creatives` など)を参照しなければなりません(MUST)。下記「バイヤーの義務」を参照。 これはバイト安定なキャッシュ特性を一律に保ち、冪等性層をリソースライフサイクルから分離します — セラーはタスクやリソース状態が変わってもキャッシュエントリを更新する必要がありません。代替案(「リプレイ時に状態フィールドをリフレッシュ」)は、すべてのセラーにリソース状態機械を冪等性キャッシュに通させ、あるキーの有効なキャッシュ内容の数を増やし(単一キーのリプレイが呼び出し間で決定論的でなくなる)、残りのルールが依拠する正準リプレイの不変条件を壊します。セラーは、一部の状態追跡フィールドがリプレイ時にリフレッシュされ、他がされないハイブリッドを実装してはなりません(MUST NOT)— 部分リフレッシュは両方の選択肢の最悪で、非コンフォーマントです。 3. **成功レスポンスのみがキャッシュされる。** いかなるエラー — 検証、ガバナンス拒否、トランスポート失敗、内部エラー — でもキーは**保存されません**。リトライは再実行します。これはバイヤーの意図に一致します: 5xx 後のリトライは失敗をリプレイするのではなく再試行すべきです。また、バイヤーの不正リクエストがキーに TTL 全体ロックされるのを防ぎます。 4. **リプレイはキャッシュされたレスポンスを返す。** 同じ `idempotency_key` かつ等価な正準形ペイロード(下記「ペイロード等価性」を参照)を持つ後続リクエストは、副作用を再実行せずに保存された内側レスポンスを返さなければなりません(MUST)。セラーはレスポンス時に送信プロトコルエンベロープに `replayed: true` を注入します — `replayed` は冪等性層が生成するエンベロープレベルのフィールドであり、キャッシュされた内側レスポンスの一部では**ありません**。リプレイ時の注入により、エンベロープ変更(新しい `timestamp`、ローテーションされた `governance_context` など)に関わらずキャッシュされたペイロードがリプレイ間でバイト安定に保たれます。MCP のトランスポート固有の注記: MCP ツールレスポンスは別個のエンベロープスロットを持ちません。サーバーは `replayed` をツール結果オブジェクト自体の中(例: 構造化リターンの先頭)またはレスポンスメタデータフィールドで公開してもよい(MAY)。REST と A2A レスポンスはエンベロープフィールドを直接使います。 5. **異なる正準ペイロードでのキー再利用は競合。** 同じキー、リプレイウィンドウ内で異なる正準ハッシュは `IDEMPOTENCY_CONFLICT` で拒否しなければなりません(MUST)。セラーは 2 番目のリクエストを黙って適用してはなりません(MUST NOT)。 6. **期限切れキーは明示的に拒否される。** `replay_ttl_seconds` が経過した後、セラーはキャッシュエントリを退避してもよい(MAY)。セラーが見たことのあるキーで退避後に到着するリクエストは、黙って新規として扱うのではなく `IDEMPOTENCY_EXPIRED` で拒否すべきです(SHOULD)— 黙った再実行は、まさにキーが防ぐはずのダブルブッキングのフットガンです。セラーは TTL 境界で ±60 秒のクロックスキューウィンドウ(本書の他所で JWS `exp` に適用される許容範囲と同じ)を許可すべきです(SHOULD)。名目上の期限切れの数秒後に到着するリトライが、新規として扱われるのではなく依然キャッシュからリプレイされるように。 **耐久性は規範的。** 宣言された `replay_ttl_seconds` はベストエフォートのキャッシュヒントではなく耐久性契約です。セラーは、宣言された TTL の間、プロセス再起動、ポッド置換、リージョンフェイルオーバー、オペレーター起因のキャッシュフラッシュを生き延びるストレージで冪等性キャッシュをバックアップしなければなりません(MUST)。インメモリのみのストア(プレーンな `Map`、バッキング層なしの単一プロセス LRU)は、`replay_ttl_seconds` がプロセス寿命を超えるときは常に非コンフォーマントです — 3600 秒の下限では常に真です。宣言された TTL 未満での黙った退避の帰結は**変位リプレイウィンドウ**です: 送信者は新しい署名ノンスの下で同じ `idempotency_key` で正当にリトライし(署名済みリトライが機能すべき方法 — ノンスは送信ごとであってイベントごとではない)、署名リプレイチェックを通過し、レシーバーのインメモリ状態が落とされたためアプリ層キャッシュが空であることを見つけます。副作用が 2 回実行されます。セラーは、キャッシュ層が耐久的に守れる以上の `replay_ttl_seconds` を宣言してはならず(MUST NOT)、「見たことがない」を「宣言 TTL 下で退避された」と区別できないとき、フェイルオープン(黙った再実行)ではなくフェイルクローズ(`IDEMPOTENCY_EXPIRED`)しなければなりません(MUST)。運用上の現実が「メモリのみ、ポッド再起動で消失」のセラーは、`replay_ttl_seconds` を保証される最短ポッド寿命以下に宣言することが求められます — 実際上、これは耐久層を強制します。 7. **リプレイウィンドウは推論ではなく宣言される。** セラーは `get_adcp_capabilities` で `capabilities.idempotency.replay_ttl_seconds` を宣言しなければなりません(MUST、最小 3600 秒 / 1 時間、推奨 86400 秒 / 24 時間、最大 604800 秒 / 7 日)。クライアントは想定デフォルトにフォールバックしてはなりません(MUST NOT)— 宣言のないセラーは非準拠で、リトライ機微なオペレーションには安全でないものとして扱わなければなりません(MUST)。 8. **キャッシュ増大防御。** セラーは、リクエストレート制限とは別に、`(authenticated_agent, account)` ごとの冪等性キャッシュ挿入レート制限を適用しなければならず(MUST)、エージェントごとの挿入レートが設定上限を超えたときはキャッシュを無制限に増大させるのではなく `RATE_LIMITED`([error taxonomy](/docs/building/by-layer/L3/error-handling#rate-limit-handling) を参照)を返さなければなりません(MUST)。安価な成功パスオペレーション(例: `log_event`)で毎秒 N 個の新しいキーを送るバイヤーは、さもなくば無制限のストレージを強制し、3600 秒の下限で `replay_ttl_seconds` に比例した増幅を伴います。自然な境界は `inserts_per_hour × replay_ttl_hours ≤ max_cache_rows_per_agent` です。 **推奨上限(3.1+):** 元の 60/秒持続 / 300/秒バーストの単一予算上限は、書き込み重視のローンチパターン(10 メディアバイ/分以下 × 10 パッケージ × 10 クリエイティブ、3-5 倍の余裕)に対してサイズされました。ユニバーサル冪等性の下では、読み取りトラフィックも挿入レートに寄与します — 5 アカウント横断で `get_products(brief)` + `list_creatives` + `list_accounts` を 1Hz でポーリングする単一のエージェンティックダッシュボードは、いかなる書き込み活動の前に読み取りだけで約 15 挿入/秒です。オペレーターは `(authenticated_agent, account)` ごとの**分割予算**を採用すべきです(SHOULD): * **読み取り: 300 挿入/秒持続、ローリング 10 秒ウィンドウで 1,500/秒バースト。** [Polling / state re-read](#agent-retry-vs-polling-vs-re-plan) ルール下でのダッシュボードポーリングとエージェンティック状態再読み取りが支配的。読み取りトラフィックは通常、ユーザー駆動の UI 操作中はバースト的、エージェント実行中は低レートで安定。 * **書き込み: 60 挿入/秒持続、300/秒バースト。** 元の書き込み重視サイジングから変更なし — バイヤーのダッシュボードポーリングが、`create_media_buy` / `sync_creatives` / `activate_signal` をダブル実行レースから守る書き込み容量を枯渇させられないよう、別個の予算として保持。 * **合算上限(多層防御):** 総挿入はエージェントごとに 350/秒持続 / 1,700/秒バーストを超えるべきではありません(SHOULD NOT)— 小さなクッション付きの 2 予算の合計。読み取り予算を飽和させる攻撃者が書き込み容量を飢えさせられないように。 安定した低ボリュームトラフィックのオペレーターはこれらの開始値未満に締めてもよい(MAY)。この上限より大きいバーストオンボーディングやトラフィッキングパターンのオペレーターは、正当なトラフィックの黙った拒否を受け入れるのではなく引き上げなければなりません(MUST)。分割予算の形状(別個の読み取りと書き込みカウンター)は、オペレーターが大きさを締めても 3.1 以降実装しなければなりません(MUST)— 共有単一予算上限がこのルールが防ぐ失敗モードです。持続境界はローリング 60 秒ウィンドウです — 10 秒ウィンドウを空にするバーストは 60 秒ローリング境界の次の 50 秒にカウントされます。異なるウィンドウ形状(固定分バケット、EWMA)を採用するセラーは、リトライロジックを持つバイヤーが `RATE_LIMITED` がいつ発火するか予測できるよう文書化しなければなりません(MUST)。セラー間のウィンドウ形状の黙った発散は、同一のバイヤートラフィックがあるセラーを通過し、コンフォーマントな実装で別のセラーに拒否されることを意味します。3600 秒 TTL 下限で合算上限レートはエージェントごとの常駐を約 126 万エントリに制限します — 書き込みのみサイジングの元の 21.6 万から一桁上で、読み取りトラフィックの追加を反映します。エージェントごとのストレージ予算はこれを考慮すべきです。数値推奨は SHOULD レベルです。レート制限して `RATE_LIMITED` で拒否する動作自体は MUST です。セラーは上限を調整可能な設定パラメーターとして公開しなければなりません(MUST)— 300/60 の読み取り/書き込み分割数はエージェンティックバイヤーダッシュボードパターンの初回デプロイ開始点であり、凍結されたデフォルトではありません。セラーは正確な設定上限数値をケイパビリティレスポンスで公開すべきではありません(SHOULD NOT)— そうすると上限がエコシステム全体の攻撃ターゲットになります。バイヤーは、ケイパビリティイントロスペクションではなく `RATE_LIMITED` + `retry_after` レスポンスを通じて実効上限を発見します。 上限は `(authenticated_agent, account)` ごとです — 冪等性キー自体(項目 1)と同じスコープ — なので、マルチアカウントエージェンシーはアカウントごとの予算が単一の共有クォータに畳み込まれません。`RATE_LIMITED` 拒否は [error handling taxonomy](/docs/building/by-layer/L3/error-handling#rate-limit-handling) に従い `retry_after`(秒)を設定しなければならず(MUST)、冪等性レスポンスとしてキャッシュされてはなりません(MUST NOT、ルール 3: 成功レスポンスのみキャッシュ)。セラーは `retry_after` を安価な拒否フロアとして強制すべきです(SHOULD)— `retry_after` 経過前にリトライするバイヤーは、リトライごとにフルのスキーマ検証・キャッシュチェックパイプラインに再入するのではなく、事前認証トークンバケット(例: リバースプロキシ層)にヒットすべきです(SHOULD)。この規律なしでは、誤動作するバイヤーがレートリミッター自体の負荷を増幅できます。 9. **並行リトライ — 最初の挿入が勝つ。** 同じ `(authenticated_agent, account_id, idempotency_key)` を運ぶ 2 番目のリクエストが、最初のリクエストがまだ実行中に到着してもよい(MAY)— 最も一般的には、セラーのダウンストリーム呼び出しが返る前にバイヤーのトランスポートタイムアウトが発火し、バイヤーがリトライするとき。セラーはレースを決定論的に解決しなければならず(MUST)、副作用を 2 回実行してはならず(MUST NOT)、2 番目のリクエストを黙って落としてはなりません(MUST NOT)。解決はスコープタプル上の `(unique constraint, INSERT … ON CONFLICT DO NOTHING)` パターンです: 最初に着地する行が実行を所有し、正準ペイロードハッシュを実行中の行に保存する(センチネルではない)。後続リクエストは、レスポンススロットがまだ設定されていないがペイロードハッシュは設定されている既存行を観測します。 セラーは 2 番目のリクエストを 2 つのポリシーの 1 つで処理しなければならず(MUST)、呼び出し間で一貫して動作しなければなりません(MUST)— クライアントはセッション内の最初のレスポンスからポリシーを推論し、後続のリトライに適用します: * **Wait-and-replay**(高速オペレーション向け推奨、通常 5 秒未満): セラーは最初が完了するまで 2 番目のリクエストをブロックし、その後 `replayed: true` 付きでキャッシュされたレスポンスを返します。2 番目の呼び出しの総壁時間はセラーのリクエストタイムアウト予算で制限されます。 * **Reject-and-redirect**(長時間実行のダウンストリーム呼び出しを伴う低速オペレーション向け推奨): セラーは即座に `IDEMPOTENCY_IN_FLIGHT` を返し、最初のリクエストの経過時間と予想完了に基づいて `error.details.retry_after`(秒、整数)を設定します。バイヤーはヒント経過後、同じ `idempotency_key` でリトライしなければなりません(MUST)— `IDEMPOTENCY_IN_FLIGHT` で新しいキーを生成するバイヤーは、安全なリトライを、まさにこのルールが防ぐダブル実行レースに変えます。 同じキーかつ*異なる*正準ペイロードを持つ 2 番目のリクエストが実行中ウィンドウ中に来た場合、`IDEMPOTENCY_IN_FLIGHT` ではなく `IDEMPOTENCY_CONFLICT`(ルール 5)を返さなければなりません(MUST)— 正準形の不一致は行の保存済みハッシュに対して INSERT 時に計算可能なので、最初のリクエストのレスポンスを待たずに競合を検出できます。バッキングストアがハンドラー完了まで実際の正準ハッシュを永続化できないセラー(例: プレースホルダーセンチネルパターン)は、ルール 9 のコンフォーマンスを宣言する前に INSERT 時にハッシュを永続化するようストアをアップグレードしなければなりません(MUST)— 代替案(同一キー・異ペイロードレースで `IDEMPOTENCY_IN_FLIGHT` を返し、最初のリクエスト完了後にのみ競合を表面化)は、実際のクライアントバグの検出を黙って遅らせます。 ルール 3 に従い、最初のリクエストが最終的に失敗する(検証エラー、ダウンストリームタイムアウト、内部エラー)場合、`(in_flight)` 行は解放されます — キーは「見たことがない」状態に戻り、後続のリトライは最初から再実行します。セラーは実行中の行の寿命を宣言されたタスクごとハンドラータイムアウトに制限しなければならず(MUST)、そのタイムアウトが発火したとき — ダウンストリームがまだ応答していなくても — 行を解放しなければなりません(MUST、ルール 3 に従い失敗として扱う)。この境界なしでは、ハングしたハンドラーが同じキーに対して無期限に `IDEMPOTENCY_IN_FLIGHT` を返し、バイヤーをいかなる安全なリトライパスからもロックアウトします。 reject-and-redirect を使うセラーは、`error.details.retry_after` を `replay_ttl_seconds`(`capabilities.idempotency` で宣言)以下の値に設定しなければなりません(MUST)。セラー自身のリプレイウィンドウを過ぎて待つよう指示されたバイヤーは、レスポンスがもはやリプレイできなくなるまで待つよう言われています — 待機は無意味で、バイヤーは新しいキーを生成する(このルールが防ぐ失敗モード)か、リトライで `IDEMPOTENCY_EXPIRED` にヒットします。セラーは `capabilities.idempotency.in_flight_max_seconds` — 実行中の行の最大寿命、セラーのタスクごとハンドラータイムアウトにスコープ — も宣言すべきです(SHOULD)。バイヤーは存在する場合その宣言値を主要なリトライ予算境界として使うべきです(SHOULD)。不在の場合、桁数ヒューリスティクス(セラーの典型的なハンドラーレイテンシーから導出され、リプレイ TTL の一桁下、決して TTL 上限自体ではない値)にフォールバックします。 セラーはスコープ境界をまたいで実行中の状態を漏らしてはなりません(MUST NOT): 候補キーを探る攻撃者は、行が存在するか、実行中か、一度も存在しなかったかに関わらず、同じレスポンス形状とタイミングを受け取らなければなりません(MUST)。 10. **サービス境界をまたぐ — ダウンストリームリコンシリエーション。** セラーはリクエスト処理中にダウンストリームシステムを呼び出すのが一般的です — `create_media_buy` での SSP/アドサーバー呼び出し、請求オペレーションでの決済プロバイダー呼び出し、`check_governance` でのガバナンスエージェント呼び出し。これらの呼び出しは、セラーを「ダウンストリーム不明」状態に残し得る独自の失敗モードを持ちます: ダウンストリームがリクエストを受け入れた後、そのレスポンス到着前にネットワーク接続が切れた。セラープロセスが呼び出し中にクラッシュした。リージョンフェイルオーバーがレスポンス永続化前にワーカーをスワップした。ルール 3(成功レスポンスのみキャッシュ)は必要だが不十分です: 単にキャッシュせずリトライで再実行するセラーは、ダウンストリームを二重呼び出しし、そこで重複した副作用を作ります。 **コンフォーマンスの採点。** このルールはコンプライアンスストーリーボードスイートによるプログラム的採点ではなく、レビュアー採点です。ブラックボックス観察は「セラーがクレーム行を持つ」を「セラーがテスト実行で運が良かった」と区別できません。`parallel_dispatch_runner` テストキットはルール 10 のコンフォーマンスを `reviewer_checks` の下にリストします — ルール 10 のコンフォーマンスを表明するセラーは、どのパターンがどのダウンストリームに適用されるかを記述する運用ランブックを表面化しなければならず(MUST)、レビュアーはそのランブックに対して実装を検証します。他の規範ルール(1-9)はプログラム的に採点されます。 セラーは、二重呼び出しがビジネス上の帰結(リソース作成、決済移動、不可逆な状態変更)を持つすべてのダウンストリーム呼び出しについて、2 つのリコンシリエーションパターンの 1 つを採用しなければなりません(MUST)。読み取り専用のダウンストリーム呼び出し(キャッシュルックアップ、書き込まない適格性チェック)は免除されます — が、ダウンストリーム監査ログにも書く不正スコアリングルックアップのような境界ケースはこのルールでは書き込みとしてカウントされます(監査ログエントリが副作用)。 * **Write-claim-before-invoke(推奨デフォルト)。** ダウンストリームを呼び出す前に、セラーは冪等性キャッシュ行と同じトランザクションで「クレーム」行を永続化します — 通常 `{idempotency_key, downstream_provider, downstream_request_id, status: 'invoked', invoked_at}` — セラー生成の `downstream_request_id`(ダウンストリーム自身の相関/冪等性識別子としてダウンストリームに渡す)を使って。リトライ時、ダウンストリームを再度呼び出す前に、セラーは `(idempotency_key, downstream_provider)` でクレーム行をルックアップしてリコンサイルしなければなりません(MUST): `downstream_request_id` でダウンストリームをクエリして真の結果を判定し、そこからキャッシュ投入を再開します。セラーは、ローカルレコードの欠落を「ダウンストリーム呼び出しは起きなかった」と扱ってはなりません(MUST NOT)— ダウンストリーム受け入れとローカル永続化の間のクラッシュは、まさに起きてローカルレコードが欠落しているケースです。ダウンストリームが `downstream_request_id` のレコードなしを報告する場合(クレーム行は永続化されたが、セラーが呼び出し前にクラッシュ)、セラーは呼び出しを未実行として扱い、呼び出しを進めなければなりません(MUST)。クレーム行はすでに `downstream_request_id` を予約しているので、ダウンストリーム自身の冪等性が後続のリトライを重複排除します。ダウンストリームルックアップからの曖昧なレスポンス(一時的 5xx、ネットワークエラー、不正レスポンス)では、セラーはフェイルクローズしなければなりません(MUST)— 未認証の「レコードなし」シグナルで呼び出しを進めるのではなく、バイヤーに一時的エラーを返します(バイヤーがルール 9 に従い同じ `idempotency_key` でリトライするように)。 * **Thread-buyer-key(ダウンストリームプロトコルがサポートする場合に許容)。** セラーはバイヤーの `idempotency_key` のダウンストリームプロバイダーごとの派生をダウンストリーム自身の冪等性キーとして渡します — 通常 `HMAC(K_provider, idempotency_key)`。ここで `K_provider` はプロバイダーアイデンティティでキーされたセラーの KMS 管理ルートから導出されます(ダウンストリームごとに 1 鍵、すべてのダウンストリームで共有する 1 つのセラーシークレットではない)。プロバイダーごとの導出は、単一のダウンストリームが侵害された場合のクロスプロバイダーリプレイを防ぎます。すべてのダウンストリームで共有するセラーシークレットは、すべてのプロバイダーを単一の鍵露出影響範囲に畳み込みます。ダウンストリームの at-most-once 保証が、セラーのローカル永続化が見逃したケースをカバーします。セラーは、キャッシュされたレスポンスが正しく投入されるよう成功パスで依然クレーム行を書かなければなりません(MUST)が、ダウンストリーム自体がリトライ時の真実の源になります。セラーは、異なる信頼プリンシパルが運用する任意のダウンストリームにバイヤーの生の `idempotency_key` を渡してはなりません(MUST NOT)— バイヤーのキーは TTL 内のケイパビリティトークン(下記「キーはセキュリティ機微」を参照)であり、信頼境界をまたいで転送するとケイパビリティ面が広がります。「異なる信頼プリンシパル」とは、セラーが同じセキュリティ境界の下で運用しない任意のシステムを意味します。セラーがエンドツーエンドで所有する純粋にテナント内のマイクロサービス(同じ KMS、同じ監査ログ、同じオペレーター)に生のキーを渡すことは信頼境界をまたがず、許可されます(ただしプロバイダーごとの導出が依然としてより良いデフォルト)。 セラーは、どのパターンがどのダウンストリームに適用されるかを運用ランブックに文書化しなければなりません(MUST)。セラーは「ダウンストリームレスポンス検査でのベストエフォート重複排除」という 3 番目のパターン — ダウンストリームのレスポンスペイロードをキャッシュされた指紋と比較して呼び出しがすでに起きたか判定 — を使ってはなりません(MUST NOT)。ダウンストリームのレスポンス形状はバージョン間で変わり、指紋は同期バグの温床だからです。クレーム行 OR スレッド化されたキー。レスポンスへのパターンマッチではありません。 セラーは、ダウンストリーム起因のエラーをバイヤーに返す際、バイヤーの `idempotency_key`(またはその可逆な派生)をエラーエンベロープに含めてはなりません(MUST NOT)。セラーのダウンストリームプロバイダーごとのキー(またはセラーが誤って生でスレッド化した場合はバイヤーのキー)に言及するダウンストリームエラーは、バイヤーに伝播する前に再キーまたは除去されなければなりません(MUST)— さもなくばダウンストリームエラーメッセージが信頼境界をまたぐキー開示面になります。 このルールのバイヤー可視の帰結: セラーが低速ダウンストリームを呼び出し、バイヤーがウィンドウ中にリトライするとき、2 番目のリクエストでのセラーのレスポンスは、ダウンストリームの動作ではなく、ルール 9 の下でのセラーのポリシー(`IDEMPOTENCY_IN_FLIGHT` または wait-and-replay)で決まります。バイヤーはどのダウンストリームがパスにあるか知る必要はありません — セラーは関わらず一律のリトライ面を提示しなければなりません(MUST)。 #### ペイロード等価性 「等価」とは、フィールドごとのセマンティック比較ではなく、**同一の正準 JSON 形式**を意味します。セラーは正準形をハッシュしてハッシュを比較することで等価性を判定しなければなりません(MUST)。正準形は [RFC 8785 JSON Canonicalization Scheme (JCS)](https://www.rfc-editor.org/rfc/rfc8785) です — 数値シリアライズ、キー順序、エスケープはすべて JCS §3 に規範的に従います。 **ハッシュから除外されるフィールド**(閉じたリスト — セラーは拡張してはならない、MUST NOT): * `idempotency_key` — キー自体 * `context` — バイヤー不透明なエコーデータ(トレース ID、相関 ID)は設計上リトライで変わる * `governance_context` — エンベロープ上。リトライでリフレッシュされた署名トークンかもしれない * `push_notification_config.authentication.credentials` — ローテーションされた bearer トークンかもしれない。URL とスキームはハッシュに残る。クレデンシャル値のみ除外。 リクエストボディの他のすべて — `ext` を含む — は含まれ、「欠落した任意フィールド」は「明示的に null に設定されたフィールド」と等価では**ありません**(JCS は区別を保持し、ハッシュも同様)。**バイヤーはローテーションするトークンやリトライ不安定な値を `ext` 内に置いてはなりません(MUST NOT)。** `ext` は正準ペイロードの一部です。リトライ間で変わる値は、バイヤーの意図が変わっていなくても `IDEMPOTENCY_CONFLICT` をトリガーします。ローテーションする認証情報は上記の除外リストフィールドに、バイヤー側のトレースデータは `context` に属します。セラーはケイパビリティ、設定、拡張を介して除外リストを拡張してはなりません(MUST NOT)— リストは本スペックで固定され、そこでのドリフトはエコシステム全体でリトライセーフティ保証を黙って弱めます。**除外リストへの将来の追加はペイロード等価性への破壊的変更です**(`ext` に今除外される値を入れたバイヤーは、以前は別個だったリトライが互いに重複排除し始めるのを見る)ので、リストはマイグレーションノート付きのメジャーバージョンバンプでのみ成長します。追加を提案する新しい PR は、特定のバイヤーがたまたまローテーションしたというだけでなく、なぜそのフィールドがセマンティックにリトライ契約の外にあるかを示さなければなりません(MUST)。 **リファレンス実装**: `SHA-256(JCS(payload - excluded_fields))`。 * TypeScript / JavaScript: [`@truestamp/canonify`](https://www.npmjs.com/package/@truestamp/canonify) または [`canonicalize`](https://www.npmjs.com/package/canonicalize) * Python: [`pyjcs`](https://pypi.org/project/pyjcs/) または [RFC 8785 appendix](https://www.rfc-editor.org/rfc/rfc8785) のリファレンス実装 * Go: [`gowebpki/jcs`](https://github.com/gowebpki/jcs) * Rust: [`serde_jcs`](https://crates.io/crates/serde_jcs) AdCP SDK ミドルウェアは JCS 正準化を出荷するので、セラーは独自実装する必要がありません。独自の正準形を作ることは「私のマシンでは動く」冪等性バグの一般的な原因です — JCS はそれを避けるよう精密に規定されています。 #### サーバー側ツールラッパーのコンフォーマンス バイヤー SDK はエンベロープレベルのフィールド(`idempotency_key`, `context_id`, `context`, `governance_context`, `push_notification_config`)を**すべての AdCP ツール呼び出しで一律に**送ります — バイヤーはツールごとに、セラーのラッパーがどのエンベロープフィールドをたまたま宣言するかを知り得ません。サーバーは、ツールパラメーターに到着するがツールのパラメータースキーマで宣言されていないエンベロープレベルフィールドを許容しなければなりません(MUST)。具体的には: * **`idempotency_key`** はすべての AdCP タスクリクエストで必須です(上記ルール 1 を参照 — 読み取りも変更系も)。ツールラッパーはそれを受け入れなければなりません(MUST)。冪等性層がルール 2-9 に従ってルーティングします。フィールドを `unexpected_keyword_argument`(FastMCP/Pydantic の厳格なシグネチャ)で拒否するラッパーは非コンフォーマントです。 * **`context_id`, `context`, `push_notification_config`, `governance_context`** は読み取りを含むすべてのツールで受け入れられなければなりません(MUST)。あるフィールドを消費しないツールはそれを無視しなければならず(MUST)、エンベロープフィールドが存在するという理由で呼び出しを拒否してはなりません(MUST NOT)。 これは、すべての公開 AdCP リクエストスキーマが宣言する `additionalProperties: true` デフォルトのサーバー側対応物です。スキーマ自身の `additionalProperties` 宣言と矛盾する形でサーバー側バリデーターを設定することはコンフォーマンス違反です。一般的なサーバー実装の罠: * **厳格なシグネチャの FastMCP / Pydantic** — `def get_products(brief: str)` と宣言されたツールラッパーは、バイヤーが同じ params オブジェクト内に `idempotency_key` を送ると `unexpected_keyword_argument` を送出します。修正: `idempotency_key: str | None = None`(および他のエンベロープフィールド)を受け入れて無視する任意パラメーターとして宣言するか、`**kwargs` catch-all を使って未知のキーを破棄します。Pydantic-on-input は `Extra.allow` または `model_config = ConfigDict(extra='allow')` を使います。 * **`.strict()` 付きの Zod / valibot** はインバウンドリクエストスキーマで同じ理由で未知のキーを拒否します。入力スキーマで `.strict()` を外すか、passthrough バリアントで合成します。 * **codegen ツールが `additionalProperties: false` を注入した OpenAPI 生成サーバースタブ** — 生成された入力スキーマがスペックの `additionalProperties: true` デフォルトをミラーすることを検証します。一部のジェネレーターはモデル発行時にデフォルトを反転させます。 ワイヤーレベルの不変条件は: バイヤー SDK は同じエンベロープフィールドセットをすべてのセラーのすべての AdCP ツールに送れなければならず(MUST)、エンベロープフィールドで拒否するセラーはプロトコルが約束するクロスセラーの可搬性を壊します。このルールは 3.1+ で規範的です。エンベロープフィールドを拒否する既存のラッパーは次のメンテナンスバンプで非コンフォーマントです。 参照: このルールは、[`runner-output-contract.yaml` > `response_schema_validator_semantics`](https://github.com/adcontextprotocol/adcp/blob/main/static/compliance/source/universal/runner-output-contract.yaml) でレスポンス側バリデーター向けにすでに確立されたバリデーターごとのパターンを一般化します — 両ルールは同じ原則(「バリデーター設定はスキーマ自身の `additionalProperties` 宣言と矛盾してはならない」)をワイヤーの両端で表現します。 #### レスポンスレベルのリプレイインジケーター プロトコルエンベロープは、冪等性キャッシュを介して解決された任意のリクエストへのレスポンスにトップレベルの `replayed` ブール値を運びます: ```json theme={null} { "status": "completed", "replayed": true, "timestamp": "2026-04-18T14:35:00Z", "payload": { "media_buy_id": "mb_01HW7J8K9P0Q1R2S3T4U5V6W7X" } } ``` `replayed` はセラーの冪等性層がレスポンス時に生成し、キャッシュには保存されません。新規実行では `false`(または省略 — バイヤーは省略を `false` として扱わなければならない、MUST)。キャッシュされたリプレイでは `true`。内側の `payload` は元の成功実行で保存されたものとバイト単位で同じです。エンベロープフィールド(`timestamp`, `context_id` など)は異なることがあります — それらはキャッシュされたものではなく現在のレスポンスを記述します。 バイヤーは `replayed` を次に使います: * **エージェントの副作用抑制** — 人間が見る前にレスポンスデータに作用するエージェント(通知、ダウンストリームツール呼び出し、メモリ書き込み)は、リトライで再発行しないよう `replayed` を確認しなければなりません(MUST)。「キャンペーン作成!」通知、LLM メモリ挿入、ダウンストリームエージェント呼び出しは、まさに黙ったリプレイが壊すものです。 * **副作用の不変条件** — exactly-once イベントセマンティクスを期待するダウンストリームシステムは、レスポンスを新しいイベントとして扱う前に `replayed` を読みます。 * **請求リコンシリエーション** — 「今月 N 個のバイを処理」は `replayed: false` のみをカウントします。 * **ロギング** — 「キャッシュを返してリトライが成功」を「リトライが新しい実行をトリガー」から区別(後者は通常リプレイウィンドウやキー管理のバグを示します)。 * **状態機械ルーティング** — キャッシュされた `payload` の状態追跡フィールド(例: リプレイされた `create_media_buy` の `status: pending_creatives`)は、現在状態の読み取りではなく履歴スナップショットです(セラールール 2 とバイヤーの義務下の「リプレイレスポンスは履歴スナップショット」を参照)。バイヤーはいかなる状態依存アクションの前にリソースの読み取りエンドポイントを介して再読み取りしなければなりません(MUST)。 #### IDEMPOTENCY\_CONFLICT レスポンス形状 標準の AdCP エラーエンベロープ。エラーボディ: * `code: "IDEMPOTENCY_CONFLICT"` と人間可読な `message` を含めなければなりません(MUST) * キャッシュされたレスポンス、元のペイロード、正準形の diff、それらから導出された指紋を含めてはなりません(MUST NOT)。`field` json-pointer ヒントは無害に見えますがスキーマ形状を明かします(例: `/packages/0/budget` は攻撃者に、被害者のペイロードが最初のパッケージに予算を持っていたと伝えます)。セラーは発行してはなりません(MUST NOT)。リトライをデバッグする正当なバイヤーは自身の 2 つのペイロードを diff できます — 両方を持っています。 ```json theme={null} { "errors": [ { "code": "IDEMPOTENCY_CONFLICT", "message": "idempotency_key was used with a different payload within the replay window. Either resend the exact original payload (to return the cached response) or generate a fresh UUID v4 to submit this new payload.", "recovery": "correctable" } ], "context": { "correlation_id": "..." } } ``` キャッシュされた状態を漏らすことは、キー再利用を読み取りオラクルに変えます。被害者のキーを推測または盗んだ攻撃者は、さもなくばそれを探ってペイロード構造を推論できます。エラーボディはコードのみを露出します。 #### SI send\_message の冪等性モデル `si_send_message` は、会話ターンがセッション状態を進めるため、他の変更よりも狭いスコープを必要とします。キーは `(authenticated_agent, account_id, session_id, idempotency_key)` にスコープされます。 * **TTL 内のターン N のリトライはターン N のキャッシュされたレスポンスを返します**。ターン N+1 がその後受け入れられていても。冪等性はあなたがしたことを返し、セッションが何であるかを巻き戻しません。バイヤーのリトライは「私のメッセージは通ったか」を尋ねています — 答えは依然「はい、これが返ってきたものです」です。 * **新しい `idempotency_key` を持つ新しい `si_send_message` は新しいターン**で、現在のセッション状態に対して処理されます。バイヤーは HTTP 試行ごとではなく論理ターンごとに新しいキーを生成しなければなりません(MUST)。 * **セラーがセッション状態をターン N を超えて進め、キャッシュされたレスポンスをバイト単位で再現できない場合**(例: セッションがストレージのためにプルーニングされた)、セラーは再構築するのではなく `SESSION_NOT_FOUND` または `IDEMPOTENCY_EXPIRED` を返してもよい(MAY)。セッションタイムアウトをはるかに過ぎてリトライするバイヤーはこれを予期すべきです。 #### バイヤーの義務 バイヤーは `(seller, request)` ペアごとに一意の `idempotency_key` を生成しなければなりません(MUST)。同じキーをセラーをまたいで再利用すると、共謀するセラーが同じバイヤーからのリクエストを相関できます。各リクエストに新しい UUID v4 を使います。ネットワークエラー後のリトライでは、バイヤーは全く同じペイロードを同じキーで再送しなければなりません(MUST)— どちらかを変えると at-most-once セマンティクスが壊れます。特に、バイヤーは同じキーでのリトライ間で `push_notification_config.url` を変えてはなりません(MUST NOT)。URL は正準ハッシュの一部で、それをローテーションすると `IDEMPOTENCY_CONFLICT` をトリガーします。Webhook 設定を変えるときはキーをローテーションします。 **ネットワークリトライ vs エージェント再計画 vs ポーリング / 状態再読み取り。** 似ているが異なる処理が必要な 3 つのケース: * **ネットワークリトライ** — ソケットタイムアウト、5xx、一時的失敗。バイヤーは*同じ意図*を持ち*同じバイト*を送った — そしてそれらを*同じキー*で再送しなければなりません(MUST)。これが idempotency\_key の存在理由です。 * **エージェント再計画** — バイヤーは、プランナーが再実行され(プロンプト再実行、ツール出力変化、ポリシー再評価)*異なるペイロード*を生成したエージェントです。意図が変わりました。エージェントは*新しいキー*を生成し、以前のリクエストを放棄されたものとして扱わなければなりません(MUST)。以前のキーを異なる正準ペイロードで再利用すると `IDEMPOTENCY_CONFLICT` を返し、これはセラーが正しくエージェントに「あなたはリトライしていない、新しいことをしている」と伝えるものです。 * **ポーリング / 状態再読み取り** — `get_products(brief)`, `list_creatives`, `list_accounts` を間隔でポーリングするダッシュボード。変更後に新しい状態をフェッチするため `get_media_buys` を読むバイヤーエージェント。任意の「時刻 T の現在状態をください」呼び出し。バイヤーは呼び出しごとに新しい `idempotency_key` を生成しなければなりません(MUST)。以前のポーリングのキーを再利用すると、キャッシュされたスナップショットを(`replay_ttl_seconds` まで)リプレイし、黙って古いデータを返します — まさにキャッシュが変更系で防ぐ失敗モードです。このルールは下記の [リプレイレスポンスは履歴スナップショット](#replay-responses-are-historical-snapshots) パターンの再読み取りステップも規定します: 「現在状態の再読み取り」呼び出しは新しいキーを運ばなければならず(MUST)、決して状態を読んでいる変更のキーを使いません。 疑わしいときは、バイヤーの意図が\*\*「以前と同じ答えをください」**(ネットワークリトライ — キーを再利用)か**「現在の答えをください」**(ポーリング / 状態再読み取り — 新しいキーを生成)か**「この新しいことをして」\*\*(エージェント再計画 — 新しいキーを生成)かを尋ねます。リクエストを構築するために LLM をループするエージェンティッククライアントは、ネットワークリトライケースのために最初の送信時にシリアライズされたバイトをキーと共に凍結・キャッシュすべきです(SHOULD)。プランナーが再実行で少し違うものを生成しても、リトライが同一のペイロードを送るように。 **ブートストラップ切り出し — `get_adcp_capabilities`。** ディスカバリー呼び出し自体はこのセクションのルール 1-9 から免除されます。`get_adcp_capabilities` は、バイヤーがセラーが `adcp.idempotency.replay_ttl_seconds` を宣言するかを学ぶ方法なので、ディスカバリー呼び出しに対するフェイルクローズルールはブートストラップをデッドロックさせます。バイヤーは `get_adcp_capabilities` で `idempotency_key` を省略してもよく(MAY)、セラーはそれなしで呼び出しを受け入れなければなりません(MUST)。`get_adcp_capabilities` で `idempotency_key` を送るバイヤー(例: フィールドを一律に含める SDK)は標準のキャッシュ動作を得ます — が、ディスカバリー呼び出しは状態を運ばず、リプレイは無害です。他のすべての AdCP タスクリクエストはルール 1-9 の対象のままです。下記のフェイルクローズ義務はケイパビリティフェッチが完了すると適用されます。 **セラーのケイパビリティ宣言が欠落している場合。** `get_adcp_capabilities` レスポンスが `adcp.idempotency.replay_ttl_seconds` を省略するセラーは非準拠です。ケイパビリティフェッチが成功した後、クライアント SDK はそのセラーに対する後続のすべての AdCP タスクリクエストでフェイルクローズしなければなりません(MUST)— エラーを発生させ、デフォルトを仮定しない — バイヤーが黙ったダブルブッキングの後ではなく即座に非準拠を学ぶように。フェイルクローズルールは、`idempotency_key` が一律に必須になった今、すべての AdCP タスクリクエスト(`get_adcp_capabilities` 自体を除く)に適用されます — 純粋な読み取りとして解決する呼び出しを含みます。バイヤーは呼び出し時に多相タスク(`get_products` brief vs refine+finalize vs 非同期 Submitted)が読み取りか変更のどちらに解決するか予測できず、TTL 宣言の欠落はセラーがどのモードでもリトライに安全でないことを意味するからです。 **セラー発行のエラーコードのデコード。** セラーは、バイヤーのピン留め語彙が認識しないかもしれないエラーコード(`IDEMPOTENCY_CONFLICT`, `IDEMPOTENCY_EXPIRED`, `IDEMPOTENCY_IN_FLIGHT`, `INVALID_REQUEST`、または後のマイナーバージョンで追加されたコード)を返してもよい(MAY)。受信者は [Forward-compatible decoding(規範的)](/docs/building/by-layer/L3/error-handling#forward-compatible-decoding-normative) に従ってこれらをデコードしなければなりません(MUST)— 復旧分類のため `error.recovery` を読み、`recovery` が不在のとき `transient` をデフォルトとし、コード値が馴染みないという理由でレスポンスを決して拒否しない。`transient` 分類エラーのリトライセマンティクスは [§ Retry Logic](/docs/building/by-layer/L3/error-handling#retry-logic)(`maxRetries` とジッター付き指数バックオフ)で制限されます — バイヤーは `transient` デフォルトで無限にループしてはなりません(MUST NOT)。 **リプレイレスポンスは履歴スナップショット。** `replayed: true` を運ぶレスポンスは元の初回呼び出しレスポンスとバイト等価です(セラールール 2)— その中の状態追跡フィールドは初回呼び出し時のリソース状態を反映し、リソースの現在状態では**ありません**。リプレイされた `create_media_buy` レスポンスから `status: pending_creatives` を読み、実際には何時間も `canceled` にあるリソースに `update_media_buy(canceled: true)` を呼ぶバイヤーは、`NOT_CANCELLABLE` エラーと状態機械バグを表面化します。現在状態を必要とするバイヤーはリソースの読み取りエンドポイント — メディアバイには `get_media_buys`、アカウントには `list_accounts`、クリエイティブには `list_creatives`、シグナルには `get_signals`、他のリソースには同等物 — を参照しなければなりません(MUST)。`replayed: true` は、いかなる状態依存の決定の前に新しい読み取りが必要という明示的なシグナルです。SDK はフラグを透過的にアンラップするのではなく呼び出し元コードに表面化すべきです(SHOULD)。エージェンティックバイヤーは、次のアクションがリソース状態に依存するいかなるプランニングステップについても `replayed: true` を停止シグナルとして扱わなければならず(MUST)、続行前に再読み取りしなければなりません(MUST)。 **再読み取りは新しい `idempotency_key` を運ばなければなりません(MUST)。** 状態を再読み取りしている変更のキーを再利用すると、`IDEMPOTENCY_CONFLICT` を返す(読み取りペイロードが変更ペイロードと異なる場合 — ほぼ常に真)か、さらに悪いことにキャッシュされた変更レスポンス自体を返します(ペイロードがたまたま一致する場合)。*以前の読み取り*のキーを再利用すると、その以前の読み取りのキャッシュされたスナップショットを返します — まさにこのルールが防ぐ古い状態の失敗モードです。状態再読み取りは上記のポーリング / 状態再読み取りケースに該当します。呼び出しごとに新しいキーを生成します。 **永続化されたキーの TTL 境界。** 一部のバイヤーは、プロセス再起動や夜間リコンサイル後のリトライも重複排除するよう、`idempotency_key` を自身のオブジェクト(例: バイヤーの DB の `campaign.pending_idempotency_key`)と共に永続化します。これは**セラーの宣言された `replay_ttl_seconds` 内でのみ**機能します。TTL を超えると、セラーはリトライを `IDEMPOTENCY_EXPIRED` で拒否する(良い)か、キャッシュが退避されていれば新しいリクエストとして扱います(黙ったダブルブッキング — このフィールドが防ぐ失敗モード)。TTL を過ぎてリトライするバイヤーは、再送前に自然キーチェック(例: `context.internal_campaign_id` で `get_media_buys` をクエリ)にフォールバックしなければなりません(MUST)。`idempotency_key` はリプレイウィンドウ内での at-most-once 実行を保証し、永遠にではありません。セラーの TTL より長いリトライホライズンを持つキューベースのリトライシステムとワークフローエンジンはこれを中心に設計されなければなりません(MUST)— 自然キー再チェックなしに数日後にリプレイするデッドレターキューにキーを入れないでください。 **キーはセキュリティ機微。** `idempotency_key` は TTL 内の秘密のケイパビリティトークンです — それを保持し元のペイロードを知る者は誰でもそれをリプレイしてキャッシュされたレスポンスを読めます。キーをセッショントークンのように扱います: 完全な形でログしない、URL に埋め込まない、エージェント間で共有しない。相関が必要なら prefix のみ(UUID の最初の 8 文字)でログします。`pending_idempotency_key` を保存時に永続化するバイヤー(例: バイヤーの DB のキャンペーン行と共に)は、bearer トークンに使うのと同じ制御でそれを暗号化しなければならず(MUST)、露出ウィンドウを最小化するため成功確認後にキーをパージすべきです(SHOULD)。 **セラーはキャッシュ層を保存時に暗号化しなければなりません(MUST)。** ユニバーサル冪等性(3.1+)の下では、キャッシュは 3.0.x で保持した書き込みレシートに加えて読み取りツールレスポンス(`get_products`, `list_accounts`, `list_creatives`, `get_signals` など)を保持します。それらの読み取りレスポンスは、セラーの基盤リソースストアと同じ機微度でアカウントスコープのデータ — ブランドドメイン、アカウント名、プロダクト配分、シグナル参照 — を運びます。セラーは、キャッシュされたデータが読まれた元のリソースストアに使うのと同じ制御で冪等性キャッシュに保存時暗号化を適用しなければならず(MUST)、キャッシュをデータ保存時制御から免除される一時的なリトライレシートストアとして扱ってはならず(MUST NOT)、設定ミスのクエリが兄弟テナントのキャッシュされた読み取りレスポンスを引き出せないよう、ストレージ層で(アプリ層だけでなく)キャッシュ読み取りを `(authenticated_agent, account_id)` でスコープしなければなりません(MUST)。 **キーは推測不能でなければなりません(MUST)。** スキーマは `^[A-Za-z0-9_.:-]{16,255}$` を強制し、バイヤーは UUID v4(約 122 ビットのエントロピー)または同等の CSPRNG 生成値を使わなければなりません(MUST)。`retry-001` や単調カウンターのような低エントロピーキーはキャッシュを列挙可能な面に変えます: 攻撃者はキー空間を歩き、それぞれをターゲットエージェントに対してテストできます。セラーは、認証済みエージェントが個別に信頼されないとき、基本的なエントロピーチェックに失敗するキー(例: すべてゼロ、繰り返し文字、短い ASCII 単語)を `INVALID_REQUEST` で拒否すべきです(SHOULD)。 **3 状態レスポンス(`success` / `IDEMPOTENCY_CONFLICT` / `IDEMPOTENCY_EXPIRED`)は冪等性キーの存在オラクルです。** 候補キーを保持する攻撃者はそれを探れます: `success` は見たことがない、`IDEMPOTENCY_CONFLICT` は異なるペイロードでライブ、`IDEMPOTENCY_EXPIRED` は以前使われた、を意味します。上記の `(agent, account)` ごとのスコープが主要な防御です — エージェント A として認証された攻撃者はエージェント B のキーを探れず、アカウント A にスコープされた呼び出し元は共有エージェント認証情報の下でもアカウント B のキーを探れません。推測不能なキーが 2 次防御です — 被害者のキーを推測できない攻撃者はオラクルを有用に探れません。セラーはスコープ境界をまたいで、または未認証の呼び出し元に `IDEMPOTENCY_EXPIRED` を表面化してはなりません(MUST NOT)。セラーは冪等性層で「キーが存在する」と「キーが存在しない」ルックアップの間の区別可能なタイミングも避けるべきです(SHOULD)。負のパスでの定数時間フロアは、エラーコードオラクルなしでも持続するサイドチャネルを閉じます。 **SI セッションスコープ。** `si_send_message` ではキーは `(authenticated_agent, account_id, session_id, idempotency_key)` にスコープされます。したがって `session_id` はオラクル面の一部です: セッション ID が推測可能なら、1 つのキーを盗んだ攻撃者は多くのセッションに対してそれを探れます。SI セラーは 122 ビット以上のエントロピーを持つ CSPRNG(UUID v4 または同等)を使ってサーバー側で `session_id` を生成しなければならず(MUST)、別のエージェントに観測可能な何か(リクエストシーケンス番号、ユーザーハンドル、タイムスタンプ)から導出してはなりません(MUST NOT)。異なる `session_id` で送られた同じ idempotency\_key は異なるスコープタプルです — 常に新しいリクエストで、決して競合ではありません。 **キャッシュスコープ安全性のための `account_id` エントロピー。** `account_id` はすべての冪等性スコープタプルの一部なので、オラクル面の一部でもあります: 盗んだ冪等性キーを持つエージェント A として認証された攻撃者は、それを候補アカウント ID に対して探って A の認可セット内のアカウントを列挙したり、A がこれまで作用したアカウントを学んだりできます。アカウント ID が短い連番またはセマンティック値(`acct_123`, `nike-us`)のとき、これは実際の列挙チャネルです。サーバー割り当てのアカウント ID を発行するセラーは、冪等性キャッシュスコープに参加する任意のアカウント ID に推測不能な値(UUID v4 / ULID、122 ビット以上のエントロピー)を使わなければなりません(MUST)。バイヤー宣言アカウントモデル(自然キー `{brand, operator}`)の下で運用するセラーは、それをキャッシュスコープコンポーネントとして使う前に自然キーをセラーローカルソルトでハッシュしなければなりません(MUST)— 自然キーは設計上公開であり、オラクル防御として直接使えません。 ```javascript theme={null} import { canonicalize } from "@truestamp/canonify"; // RFC 8785 JCS import { createHash } from "node:crypto"; const EXCLUDED_FROM_HASH = new Set([ "idempotency_key", "context", "governance_context", ]); function payloadHash(request) { const filtered = Object.fromEntries( Object.entries(request).filter(([k]) => !EXCLUDED_FROM_HASH.has(k)), ); // If push_notification_config.authentication.credentials rotates, exclude it too if (filtered.push_notification_config?.authentication) { const { credentials, ...auth } = filtered.push_notification_config.authentication; filtered.push_notification_config = { ...filtered.push_notification_config, authentication: auth, }; } return createHash("sha256").update(canonicalize(filtered)).digest("hex"); } async function createMediaBuy(request, envelope) { if (!request.idempotency_key) { throw new InvalidRequestError("idempotency_key is required"); } const requestHash = payloadHash(request); const existing = await db.findByIdempotencyKey({ agent_id: currentAgent.id, account_id: request.account.account_id, idempotency_key: request.idempotency_key, }); if (existing) { if (existing.expires_at < new Date()) { throw new IdempotencyExpiredError("idempotency_key is past replay window"); } if (existing.request_hash !== requestHash) { throw new IdempotencyConflictError("idempotency_key reused with a different payload"); } // Return the stored INNER payload; replayed: true is injected by the envelope layer envelope.replayed = true; return existing.response; } return db.transaction(async (tx) => { const response = await processMediaBuy(tx, request); // Cache ONLY on success, and cache only the inner response payload await tx.idempotencyKeys.insert({ agent_id: currentAgent.id, account_id: request.account.account_id, key: request.idempotency_key, request_hash: requestHash, response, expires_at: new Date(Date.now() + TTL_SECONDS * 1000), }); envelope.replayed = false; return response; }); } ``` #### 自然キー冪等性は代替にならない アップサート型タスク(`sync_accounts`, `sync_audiences`, `sync_catalogs`, `sync_event_sources`, `sync_governance`, `sync_plans`)はすでにリソースレベルで重複排除します — 同じ `account_id` または `audience_id` を持つ 2 つの呼び出しは 2 つではなく 1 つの行を生成します。それが**リソース冪等性**です。 `idempotency_key` はより厳格なものを保証します: **エンベロープ冪等性**。リクエスト全体 — その副作用を含む — が最大 1 回実行されます。キーなしで同じ sync エンベロープをリトライすると、リソース行が同一になっても、オンボーディング Webhook を 2 回発火したり、重複した監査ログエントリを発行したり、ピクセルエンドポイントを二重プロビジョニングしたりし得ます。キーがリトライを本当に安全にするものです。 スペックでの唯一の例外は `si_terminate_session` です: `session_id` と「terminate」動詞は完全に冪等 — すでに終了したセッションへの 2 番目の呼び出しは新しい副作用なしに同じ終端状態を返す — なので、そのスキーマは `idempotency_key` を要求しません。 ### 署名付きガバナンスコンテキスト `governance_context` は信頼境界をまたぎます — ガバナンスエージェントからバイヤー、セラー、そして戻り、最終的には元のトランザクションが閉じてからずっと後に承認を検証する必要があるかもしれない監査人や規制当局へ。AdCP 3.0 は、いかなる当事者も発行者に召喚状を出さずに真正性、バインディング、リプレイを検証できるよう、値の形式をガバナンスエージェントが署名したコンパクトな JWS に厳格化します。 **役割:** * **ガバナンスエージェント**がトークンに署名します。署名する唯一の当事者です。 * **バイヤー**はガバナンスエージェントから受け取ったトークンをプロトコルエンベロープに添付し、セラーに転送します。バイヤーはトークンを構築・変更・再署名してはなりません(MUST NOT)。バイヤーは自身の監査記録のため `jti` と `check_id` を保持すべきです(SHOULD)。 * **セラー**はトークンを受け取ったまま永続化し、後続のすべてのガバナンス呼び出しにそのまま含めます。検証を実装するセラーは、トークンに基づいて行動する前に下記のチェックリストに従って検証しなければなりません(MUST)。検証をまだ実装していないセラーも、ダウンストリームの検証可能な当事者(監査人、規制当局)が後でそれに基づいて行動できるよう、トークンを変更せずに永続化・転送しなければなりません(MUST)。 * **監査人と規制当局**はガバナンスエージェントの公開鍵を使って独立に検証します — これが署名形式が提供するために存在するアカウンタビリティ特性です。 同じ文字列はガバナンスライフサイクルの主要な相関キーでもあります。ガバナンスエージェントは自身のトークンをデコードして内部状態(バイヤー相関 ID、ポリシー決定ログなど)をルックアップします — セラーとバイヤーはペイロードを解析する必要は決してありません。 #### スコープと依存関係 * **スコープ内(3.0)**: バイサイドガバナンス。`governance_context` トークンは、AdCP タスク(`create_media_buy`, `acquire_rights`, `activate_signal`, `creative_services`)を介してなされる支出コミットメントを認可します。独自のコンプライアンスポリシーを実行するセラー(例: CTV 政治広告ルール、パブリッシャーブランドセーフティゲート)は、それらを独自のガバナンスワークフロー上の `conditions` レスポンスで表現します。このプロファイルの下では署名付きトークンを発行しません。 * **スコープ外(3.0)**: セラーサイドのガバナンス当局。将来の RFC が `adagents.json` を介して宣言されるセラーサイドの署名付き決定をカバーするようこのプロファイルを拡張するかもしれません。 * **スコープ外(永久)**: OpenRTB 入札ストリーム。ガバナンスの証明は AdCP メディアバイ境界で終わります。署名付き証明をインプレッションごとの入札リクエストに通すことは運用上実現不可能(1 トークン、多数の受信者、ブロードキャストファンアウト)で不要(支出認可はインプレッションごとではなくメディアバイ時に発生)です。 **トランスポート署名(#2307)への依存**: このプロファイルのアンチスプーフ特性は、セラーがトークンの `iss` クレームとは独立にバイヤードメインを確立できることに依存します — 下記の [Buyer identity resolution](#buyer-identity-resolution) を参照。#2307 なしの 3.0 では、セラーはバイヤーアイデンティティを確立するため mTLS または事前プロビジョニングされたバイヤー API キーのいずれかを使わなければなりません(MUST)。リクエストの bearer トークンだけを brand.json 解決へのアイデンティティ入力として扱うことは循環的で、スプーフィングを防ぎません。3.1 は規範的に #2307 スタイルの署名付きリクエストを要求します。 #### AdCP JWS プロファイル このプロファイルは `governance_context`(#2306)と、スタンドアロントークンとして署名される将来の任意の AdCP アーティファクトに適用されます。トランスポート層リクエスト署名(#2307)は RFC 9421 HTTP Signatures を使いますが、ここで説明する JWKS ディスカバリーを共有します。ガバナンス署名鍵を #2307 トランスポート署名鍵として使ってはなりません(MUST NOT)— JWKS エンドポイントは共有ですが、各鍵エントリは `"key_ops": ["verify"]` と `"use": "sig"` を宣言し、別個の `kid` を占めなければなりません(MUST)。検証者は、目的をまたぐ鍵再利用を防ぐため key-ops 分離を強制しなければなりません(MUST)。 **ヘッダー** * `alg`: サーバー側ランタイムでは `EdDSA`(Ed25519)を RECOMMENDED。Ed25519 が明示的なランタイム設定を要するエッジランタイム(Cloudflare Workers、Vercel Edge、Deno Deploy)では `ES256`(ECDSA P-256)を RECOMMENDED。検証者は `none`、`HS*`、2048 ビット未満の任意の `RS*` バリアントを拒否しなければなりません(MUST)。検証者はトークンヘッダー上で許可リストを強制しなければならず(MUST)、ライブラリのデフォルトのみに頼ってはなりません(MUST NOT)。 * `kid`: REQUIRED。発行者の JWKS 内の署名鍵を識別します。 * `typ`: REQUIRED。正確に `adcp-gov+jws` でなければなりません(MUST、バイト単位一致。検証者は RFC 6838 §4.2.8 に従い `+jws` 構造化サフィックスを正規化・除去してはならない、MUST NOT)。型付きヘッダーは、ガバナンス署名鍵が別目的の汎用 JWT を検証するよう騙されるのを防ぎます。 * `crit`: `crit` リストのクレームが存在する場合 REQUIRED。RFC 7515 §4.1.11 に従い、`crit` は検証者が理解しなければならないヘッダー/クレーム名の配列です。検証者は `crit` の名前が認識されない場合トークンを拒否しなければなりません(MUST)。ガバナンスエージェントは、省略または誤解釈が認可セマンティクスを変える任意のクレーム(例: 将来の `budget_cap` クレーム)を `crit` にリストしなければなりません(MUST)。これはプロファイルが後のバージョンでクレームを追加するときの黙ったダウングレード攻撃を防ぎます。 **クレーム** | Claim | Required | Description | | ---------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `iss` | Yes | ガバナンスエージェント識別子。バイヤーの brand.json のガバナンス型エントリの `url` とバイト単位(パスコンポーネントを含む)で一致する HTTPS URL でなければならない。マルチテナント SaaS ガバナンスエージェント(例: `https://gov.vendor.com/tenant/acme`)が同じオリジンを共有する兄弟テナントにスプーフされないよう、パスレベル一致が必要。 | | `sub` | Yes | トークンが認可する `plan_id`。注: ここで `sub` はユーザーや認証済みエージェントではなくリソース識別子として使われる。`sub` をユーザー ID としてログする実装はこれに注意すべき。 | | `plan_hash` | Yes | 証明を評価されたプラン状態に監査層でバインド。セラー検証チェックリストの一部ではない — セラーは不透明なカーゴとして扱う。セマンティクス、正準化、検証パスは [Plan binding and audit](/docs/governance/campaign/specification#plan-binding-and-audit) で定義。 | | `aud` | Yes | ターゲットセラー識別子。購入されるプロパティについてこのセラーを認可したセラーの `adagents.json` エントリの正確な URL 文字列(スキーム、ホスト、ポート、パスを含むバイト単位)でなければならない。大文字小文字を区別、パスプレフィックス一致なし。バイヤーが複数セラーを評価するインテントトークンでは、バイヤーはターゲットセラーごとに 1 トークンを要求しなければならない(プライバシートレードオフは [Intent-phase disclosure](#intent-phase-disclosure) を参照)。 | | `iat` | Yes | 発行時タイムスタンプ(エポックからの秒)。 | | `nbf` | No | Not-before タイムスタンプ。存在する場合、検証者は now \< nbf(±60 秒スキュー)なら拒否しなければならない。 | | `exp` | Yes | 有効期限タイムスタンプ。インテントトークンは 15 分以内に期限切れになるべき。実行フェーズトークン(`purchase`, `modification`, `delivery`)は 30 日以内に期限切れになければならない。ガバナンスエージェントは各ライフサイクルチェックで新しいトークンを発行して長いライフサイクルをリフレッシュする。 | | `jti` | Yes | 一意のトークン識別子。セラーのリプレイ検出と監査人の相関に使われる。RECOMMENDED 形式: 時間順序付けのため UUID v7 または ULID。 | | `phase` | Yes | `intent`(セラー前)、`purchase`、`modification`、`delivery`。このトークンが認可するガバナンスチェックフェーズに一致。セラーが実行するオペレーションが必要なフェーズを決定: `create_media_buy` → `purchase`、`update_media_buy` → `modification`、デリバリーレポートコールバック → `delivery`。 | | `caller` | Yes | このトークンを生成したガバナンスチェックを要求した当事者の URL。インテントフェーズではオーケストレーター/バイヤー、実行フェーズでは通常セラー自身(コールバックはセラーを caller として到着)。 | | `check_id` | Yes | この決定に対するガバナンスエージェントの `check_id`。`report_plan_outcome` と `get_plan_audit_logs` に相関。 | | `media_buy_id` | Conditional | セラー割り当てのメディアバイ ID。`purchase`、`modification`、`delivery` フェーズトークンで存在しなければならない。`intent` フェーズトークンでは null または不在でなければならない。 | | `policy_decisions` | No | `{ policy_id, outcome }` エントリのコンパクトな配列(`confidence` を含み得る)。セラーに可視。ガバナンスエージェントはプライバシー機微なデプロイでこれを省略し(SHOULD、[Privacy considerations](#privacy-considerations) を参照)、代わりに `policy_decision_hash` を使うべき。 | | `policy_decision_hash` | No | 正準化された決定ログの SHA-256 ハッシュ、hex エンコード。存在する場合、セラーは不透明な完全性アンカーとして扱う。完全なログは `audit_log_pointer` を介して監査人が取得可能。ガバナンスエージェントは `policy_decisions` または `policy_decision_hash` のいずれかを含めなければならない(両方も許可)。 | | `audit_log_pointer` | No | 完全な決定証拠のため `get_plan_audit_logs` が消費可能な HTTPS URL。存在する場合、監査人はポインターを使って完全なログをフェッチできる。アクセス制御はガバナンスエージェントが管理。 | | `status` | No | 任意の前方互換フック。存在する場合、将来の IETF JWT Status List メカニズム(draft-ietf-oauth-status-list)に準拠する JSON オブジェクトでなければならない。`status` を理解しない検証者は、それが `crit` に現れない限り、存在だけで拒否してはならない。 | **未知クレームの扱い**: 検証者は、認識しない名前のクレームを無視しなければなりません(MUST)*ただし*それらのクレーム名がトークンの `crit` ヘッダーに現れる場合を除き、その場合トークンを拒否しなければなりません(MUST)。この非対称ルール — 未知は無視、未知かつクリティカルは拒否 — が、プロファイルの将来バージョンが、まだ更新していない検証者の後方互換性を壊さずにセマンティックに意味のあるクレームを追加する方法です。 **サイズ**: `policy_decision_hash` を持つ典型的なトークンは 4096 文字のエンベロープ上限に余裕を持って収まります。実装は大きな証拠ペイロードをトークンに入れてはなりません(MUST NOT)。代わりに `audit_log_pointer` を使います。 **`plan_hash` は監査層でありワイヤー層ではない**: `plan_hash` クレームは、ガバナンスエージェント、監査人、バイヤー側コンプライアンスによるオフワイヤー検証のためにトークンが運ぶ暗号学的カーゴです。このプロファイルのセラー検証契約の一部ではなく、決して `crit` にリストされません。正準化、除外フィールド、保持ルール、テストベクターは [Plan binding and audit](/docs/governance/campaign/specification#plan-binding-and-audit)(ガバナンススペック)で規定されます。セラーは `governance_context` をそのまま永続化・転送し、`plan_hash` を検査せずに下記の 15 ステップ検証チェックリスト — 真正性、認可スコープ、鮮度 — を実行します。 #### バイヤーアイデンティティ解決 brand.json クロスチェック(検証チェックリストのステップ 13)がアンチスプーフィング制御です。それはセラーが*どのバイヤーの brand.json を参照するか*を知ることを要求します — 認証済みエージェントが誰が呼んでいるかを証明し、解決チェーンがそのエージェントを、セラーが brand.json をフェッチすべきバイヤードメインにマップします。3.0 でセラーは次のいずれかを介してバイヤードメインを確立しなければなりません(MUST): 1. **mTLS**: バイヤーがクライアント証明書を提示。証明書の Subject/SAN がバイヤーの登録済みドメインに解決。セラーが `https://{domain}/.well-known/brand.json` をフェッチ。 2. **事前プロビジョニングされたバイヤーアイデンティティ**: オンボーディング時にセラーが発行し、セラーの記録でバイヤーのドメインにマップされた API キーまたは OAuth クライアント識別子。 3. **#2307 に従う署名付きリクエスト**(3.1 規範): `keyid` がバイヤーの adagents スタイルエージェントレジストリのバイヤー宣言公開鍵に解決する RFC 9421 HTTP Signatures。 セラーは、リクエストの未認証フィールド(トークンの `iss`、`caller`、任意のクライアント供給ヘッダーを含む)からバイヤーアイデンティティを導出してはなりません(MUST NOT)。そうすると循環的な信頼チェーンが生じます: 攻撃者は、攻撃者制御の brand.json で宣言された攻撃者制御のガバナンスエージェントが署名したトークンを提示して「私はバイヤーです」を証明します。特に、**トークンの `iss` は、検証チェックリストのステップ 13 がそれが*認証済み*バイヤーの brand.json にガバナンス型エントリとして現れることを確認するまで未信頼の入力です** — 認証メカニズム(mTLS、API キー、署名付きリクエスト)が最初にバイヤードメインを確立し、*その*ドメインからフェッチされた brand.json のみが、どのガバナンスエージェント(`iss`)がこのバイヤーのために署名してよいかを証明すると信頼されます。 brand.json 解決は 1 リダイレクト(`authoritative_location` または `house` リダイレクトバリアント)に従って停止します。セラーはリダイレクトチェーンに従ってはなりません(MUST NOT)。 #### 鍵ディスカバリー(JWKS) セラーと監査人は JWKS(RFC 7517)を介してガバナンスエージェントの公開鍵を解決します: 1. [Buyer identity resolution](#buyer-identity-resolution) のルールを介してバイヤードメインを確立する。 2. バイヤーの brand.json をフェッチする。`type` が `governance` で `url` がトークンの `iss` とバイト単位で等しい `agents[]` エントリを見つける。一致するエントリがなければ拒否する。 3. 宣言されていればエントリの `jwks_uri` を使う。不在の場合、`{origin of iss}/.well-known/jwks.json`(origin = RFC 6454 に従う scheme+host+port)をデフォルトとする。共有オリジンから複数バイヤーを提供するマルチテナントガバナンスエージェントは、テナント鍵素材がオリジン横断でプールされないよう、明示的なテナントごとの `jwks_uri` を宣言しなければならない(MUST)。シャーディングは分離要件だけでなくサイズ要件でもある: 各 `jwks_uri` は `MAX_JWKS_BYTES` 予算(64 KiB — 下記の検証者疑似コードを参照)の下でフェッチされ、これは JWKS 固有の上限で、汎用の 5 MB SSRF ボディ上限より意図的に厳しい。数百のテナントごとの鍵をプールする単一の JWKS は 64 KiB を超えて拒否されるので、テナントごとの `jwks_uri`(それぞれ小さな鍵セットを提供)— 1 つの集約ドキュメントではない — がスケール時のコンフォーマントなパス。 4. JWKS を HTTPS でフェッチする。 5. JWKS 内で `kid` がトークンヘッダーに一致する鍵を見つける。`kid` のキャッシュミスでは、拒否する前に JWKS を 1 回再フェッチする(無制限の再フェッチを防ぐため最小 30 秒のクールダウンを尊重)。 **JWKS キャッシュ TTL** は失効リストポーリング間隔([Revocation](#revocation) を参照)で上限が制限されなければなりません(MUST)。長いキャッシュ TTL は失効を無効にします: 侵害された `kid` が `revoked_kids` に追加されても、セラーの JWKS キャッシュが検証のために失効した鍵をまだ提供する場合、失効チェック(ステップ 14 で独立に実行)のみが不正を捕捉します。 **SSRF 保護**: `jwks_uri` と失効リスト URL は相手方が供給します。これらの URL へのすべてのアウトバウンドフェッチは [Webhook URL validation](#webhook-url-validation-ssrf) で定義された SSRF 制御に従わなければなりません(MUST): 非 HTTPS を拒否、予約範囲(クラウドメタデータアドレスを含む)の解決 IP を拒否、接続を検証済み IP にピン留め、リダイレクトを拒否、レスポンスサイズとタイムアウトに上限、相手方への詳細なエラーメッセージを抑制。鍵ディスカバリーで SSRF 規律のない JWS プロファイルはメタデータ流出ベクトルです。 #### セラー検証チェックリスト リクエストをガバナンス承認済みとして扱う前に、セラーはこれらのチェックを順に実行し、最初の失敗でショートサーキットしなければなりません(MUST): 1. コンパクト JWS をパースする。不正なら拒否。 2. ヘッダー `alg` が `none` または許可リスト(EdDSA、ES256)にない場合拒否。ライブラリのデフォルトに頼ってはならない(MUST NOT)。 3. ヘッダー `typ` が正確に `adcp-gov+jws` でない場合拒否(正規化なし)。 4. ヘッダーが `crit` 配列を含み、リストされた名前が検証者に認識されない場合拒否。 5. 上記のディスカバリールールで `iss` を JWKS に解決する。JWKS がフェッチできない(SSRF 検証後)か 1 回の再フェッチ後に `kid` が存在しない場合拒否。 6. JWKS エントリの `use` が `"sig"` で `key_ops` が `"verify"` を含むことを検証。他の用途にマークされた鍵は拒否。 7. 署名を暗号学的に検証する。 8. `aud` が関連する `adagents.json` エントリで宣言されたセラー自身の正準 URL とバイト単位で等しくない場合拒否。 9. `exp` が過去、または `iat` が 60 秒より先の未来の場合拒否(±60 秒クロックスキュー許容、両境界で対称)。`nbf` が存在する場合、`now < nbf − 60 s` なら拒否。 10. `sub` がこのトークンが添付されているガバナンス呼び出しの `plan_id` と等しくない場合拒否(プランスワップを防ぐ)。 11. `phase` がオペレーションに一致しない場合拒否: `create_media_buy` には `purchase`、`update_media_buy` には `modification`、デリバリーレポートコールバックには `delivery`、`intent` はセラー前のバイヤー側評価のみ。 12. 非インテントトークンでは、`media_buy_id` がリクエストのメディアバイ ID と等しくない場合拒否。 13. クロスチェック: トークンの `iss` はバイヤーの現在の brand.json([Buyer identity resolution](#buyer-identity-resolution) を介して確立)にガバナンス型エージェントとして現れなければならない(MUST)。セラーは妥当な TTL(1 時間推奨)で brand.json をキャッシュし、検証失敗時にリフレッシュすべき(SHOULD)。 14. 失効リスト([Revocation](#revocation) を参照)を確認する。`jti` ∈ `revoked_jtis` またはトークンヘッダーの `kid` ∈ `revoked_kids` なら拒否。このチェックはキャッシュミス時だけでなくすべての検証で実行される。 15. `jti` がこの `(iss, aud)` タプルで以前に見られている場合拒否。ストレージガイダンスは [Replay dedup](#replay-dedup) を参照。 15 のチェックすべてが通過した後にのみ、セラーはリクエストをガバナンス承認済みとして扱います。セラーは `plan_hash` を検証しないことに注意 — そのクレームはガバナンスエージェント / 監査人層でバインドされます([Plan-state binding](#plan-state-binding) を参照)。 #### リプレイ重複排除 ステップ 15 はリプレイを防ぐため `jti` 値の追跡を要求します。素朴な実装 — 無制限のセット — はメモリリスクであり DoS ベクトル(攻撃者がストレージを枯渇させるため一意のトークンでセラーをフラッド)でもあります。 **スケーリング推奨**: * 実行トークンの `exp` を 30 日で上限(ガバナンスエージェントが強制。セラーはそれより長いものを拒否)。これは重複排除ウィンドウを制限します。 * 高速パスチェックとして小さな偽陽性率(約 100 万分の 1)の `(iss, aud, jti)` でキーされたブルームフィルターを使い、ブルームフィルターヒット時のみ制限されたストア(Redis `SET jti NX EX `、TTL クリーンアップ付き Postgres 一意インデックス)で権威的ルックアップ。 * ガバナンスエージェントは、セラーが重複排除ストアを時間ウィンドウでパーティション化して期限切れパーティションを安価にドロップできるよう、`jti` 値を時間順序付け可能な形式(UUID v7 または ULID)で発行すべき(SHOULD)。 #### 失効 exp ベースの期限切れだけでは、メディアバイのライフサイクルの間生きる実行フェーズトークンをカバーしません。ガバナンスエージェントは `{origin of iss}/.well-known/governance-revocations.json` に失効リストを公開しなければならず(MUST)、同じ JWKS の鍵を使ってリスト自体に署名しなければなりません(MUST): ```json theme={null} { "payload": "", "signatures": [ { "protected": "", "signature": "" } ] } ``` ペイロード(JWS フラット化 JSON シリアライズ。コンパクト形式も許容): ```json theme={null} { "version": 1, "issuer": "https://gov.example.com", "updated": "2026-04-18T14:00:00Z", "next_update": "2026-04-18T14:15:00Z", "revoked_jtis": ["01HWZX..."], "revoked_kids": ["gov-2026-03"] } ``` * `revoked_jtis` は個別の決定を無効にします(例: プランが撤回された)。失効は署名鍵に関わらずその `jti` を持つ任意のトークンに適用されます。 * `revoked_kids` はその `kid` の下で署名されたすべてのトークン(失効タイムスタンプの前後)を無効にします。発行後のトークンだけではありません。 * `issuer` はこのリストが規定するトークンの `iss` オリジンと一致しなければなりません(MUST)。共有 CDN による発行者をまたぐキャッシュ置換を防ぎます。 * リストは署名されているので、侵害された CDN や DNS オリジンが、侵害された鍵の失効を解除するために古いまたは改ざんされたリストを提供できません。 **ポーリングケイデンス**: * セラーは `next_update` で宣言されたケイデンスでリストをポーリングしなければなりません(MUST)。 * フロア: 1 分。上限: 実行フェーズトークンを受け入れる任意のセラーで 30 分。ガバナンスエージェントは、実行フェーズトラフィックがカバーする発行者について `next_update` を 30 分より先の未来に宣言してはなりません(MUST NOT)。`next_update` 値は HTTP キャッシュヘッダーではなく JSON タイムスタンプです — 標準の HTTP キャッシュはそれを尊重しません。セラーは自分でそれをパースして守らなければなりません(MUST)。DoS 耐性より高速な鍵侵害伝播を優先するセラーはフロア付近でポーリングすべき(SHOULD)。上限は、より長い失効エンドポイント停止に耐えることと引き換えに遅い `revoked_kids` 伝播を受け入れるセラーのために存在します。 * ポーリングは、15 分以下の `exp` を持つインテントフェーズトークン(上記 JWT クレーム表からのインテントトークン `exp` 上限 — ポーリング上限とは別、数値が以前は一致していたが)では任意です。 * 不要なボディ転送を避けるため HTTP 条件付きリクエスト(`If-Modified-Since` / `ETag`)を使います。 **フェッチ失敗の安全デフォルト**: セラーが `next_update + grace`(grace = 以前のポーリング間隔の 4 倍を推奨)以内に失効リストを正常にリフレッシュしていない場合、セラーはリストがリフレッシュされるまで新しい `purchase`、`modification`、`delivery` フェーズトークンを拒否しなければなりません(MUST)。これは失効エンドポイントを DoS する攻撃者が侵害された鍵の不正ウィンドウを延ばすのを防ぎます。ポーリング上限で運用するセラーは約 2.5 時間のエンドポイント停止耐性を得ます。フロアのセラーは約 5 分を得ます。リスク許容度に合わせて grace 定数ではなくポーリングケイデンスを調整します。 * ガバナンスエージェントは、現在のローテーション後に監査人が履歴トークンを検証できるよう、失効した公開鍵を監査保持期間(7 年推奨)の間発見可能に保持しなければなりません(MUST)。失効した鍵は `{origin}/.well-known/jwks-archive.json`(アクティブ JWKS とは別)で提供すべきです(SHOULD)。 #### 鍵ローテーション * ガバナンスエージェントは、新しい `kid` を持つ新しい鍵を JWKS に追加し、新しい `kid` で新しいトークンに署名し、最も長命な未処理トークンが期限切れになるまで古い鍵を公開したままにしてローテーションします。 * セラー JWKS キャッシュは、拒否する前に missing-`kid` 失敗で無効化・再フェッチしなければなりません(MUST、無制限の再フェッチを防ぐため 30 秒クールダウン付き)。 * 緊急ローテーション(鍵侵害)は、古い `kid` を署名付き `revoked_kids` リストに追加し、即座に新しい鍵にローテーションして進みます。インテントトークンの短い exp、実行トークンの上限付き exp、失効リストポーリングが共に不正ウィンドウを制限します。 #### 検証エラータクソノミー セラーとクライアントライブラリは、リトライ vs 拒否のセマンティクスがエコシステム全体で一貫するよう、これらのコードで検証失敗を表面化すべきです(SHOULD)。AdCP クライアントライブラリ(`@adcp/sdk` など)はこのタクソノミーにマップする型付きエラーを公開すべきです(SHOULD)。 | Failure | Retry? | Code | Notes | | ------------------------------------------------------------------------- | ----------------- | ----------------------------------------------------------- | ---------------------------------------------------------------- | | JWKS fetch timeout or 5xx | Yes, with backoff | `governance_jwks_unavailable` | 一時的。指数バックオフでリトライ。N 試行後に中止。 | | JWKS fetch fails SSRF validation | No | `governance_jwks_untrusted` | 恒久的。設定ミスの `jwks_uri` または攻撃を示す。 | | `kid` not in JWKS after refetch | No | `governance_key_unknown` | 拒否。ローテーションラグまたは鍵失効を示す可能性。 | | Signature invalid, `typ` mismatch, `alg` not allowed, `crit` unknown | No | `governance_token_invalid` | 拒否。改ざんまたは実装バグを示す。 | | `exp` in past, `jti` replayed, `nbf` in future | No | `governance_token_expired` / `_replayed` / `_not_yet_valid` | 拒否。トークンはリトライで治せない。 | | `jti` ∈ `revoked_jtis` or `kid` ∈ `revoked_kids` | No | `governance_token_revoked` | 拒否。 | | `iss` not in buyer brand.json | No | `governance_issuer_not_authorized` | 拒否。スプーフィングの試みを示す可能性。 | | Revocation list not refreshed within grace | No (block new) | `governance_revocation_stale` | 失効リストがリフレッシュされるまで新しいトークンを拒否。既存の完全検証済みトークンは既存の grace 内で信頼され続けてよい。 | | `aud` mismatch, `sub` mismatch, `phase` mismatch, `media_buy_id` mismatch | No | `governance_token_not_applicable` | 拒否。トークンは有効だがこのオペレーション向けではない。 | サーバーは内部検証詳細(例: どの特定クレームが不一致だったか)を相手方にエコーしてはなりません(MUST NOT)。上記の安定コードを返し、詳細はサーバー側でログします。 #### プライバシー考慮事項 **`policy_decisions` の可視性**: トークンは JWS(公開鍵を持つ誰でも読める)であり JWE(暗号化)ではありません。`policy_decisions` がガバナンスエージェントが評価したポリシー ID の完全なリストを含む場合、トークンを受け取るすべてのセラーは、バイヤーのガバナンス姿勢が考慮するポリシーを学びます — 競合インテリジェンス、場合によっては機微なオーディエンス特性についてのシグナリング(例: `minors_compliance` ポリシー ID は 18 歳未満オーディエンスのターゲティングを示唆)。ガバナンスエージェントは、バイヤーのコンプライアンス姿勢が機微なとき `policy_decisions` の代わりに `policy_decision_hash` を使うべきです(SHOULD)。完全なログはガバナンスエージェント制御のアクセスで `audit_log_pointer` を介して監査人に利用可能なままです。 **インテントフェーズのセラー開示(GA へ)**: `aud` バインディングは、競合オークションで N セラーを評価するバイヤーが、各々 1 セラーに `aud` バインドされた N 個の別個のインテントトークンを要求しなければならないことを意味します。したがってガバナンスエージェントはバイヤーが考慮したセラーの完全なリストを見ます — セラーがインテント時に GA に未知だった不透明文字列モデルに対するプライバシー退行。これは明示的なトレードオフです: クロスセラーリプレイ耐性はセラーごとのバインディングを要します。将来の `aud_hash` メカニズム(トークンがトークンスコープのソルトでセラー URL のハッシュをバインドし、各セラーが検証のため自身の URL でハッシュを計算)は、リプレイ耐性を犠牲にせずに GA に対するインテント時のセラープライバシーを回復できます。3.0 では定義されていません。フォローアップとして追跡されています。 **`caller` URL**: オーケストレーターの識別子を含みます。トークンを長期保持するセラーと監査人は、これが示唆する保持ポリシーに注意すべきです。 #### リファレンス実装 **デコードされた例トークン(インテントフェーズ)**: ヘッダー: ```json theme={null} { "alg": "EdDSA", "kid": "gov-2026-04", "typ": "adcp-gov+jws" } ``` ペイロード: ```json theme={null} { "iss": "https://gov.scope3.com", "sub": "plan_q1_2026_launch", "plan_hash": "EiCW8FkxgZ2wKqGv3Z9XuT4n2LwcJm1fK7vRaTpQ0sU", "aud": "https://seller.example.com/adcp", "iat": 1744934400, "exp": 1744935300, "jti": "01HWZXABCDEFG1234567890", "phase": "intent", "caller": "https://orchestrator.example.com", "check_id": "chk_001", "policy_decision_hash": "9b2a...f41c", "audit_log_pointer": "https://gov.scope3.com/plans/plan_q1_2026_launch/logs/01HWZXABCDEFG1234567890" } ``` **セラー検証者(TypeScript、`jose` で約 30 行)**: ```ts theme={null} import { createRemoteJWKSet, decodeProtectedHeader, decodeJwt, jwtVerify } from "jose"; class GovTokenError extends Error { constructor(public code: string) { super(code); } } const jwksCache = new Map>(); function jwksFor(jwksUri: string) { let jwks = jwksCache.get(jwksUri); if (!jwks) { // ssrfValidatedFetch enforces the Webhook URL validation rules on the JWKS URL jwks = createRemoteJWKSet(new URL(jwksUri), { cacheMaxAge: 15 * 60 * 1000, cooldownDuration: 30 * 1000, [Symbol.for("fetch")]: ssrfValidatedFetch }); jwksCache.set(jwksUri, jwks); } return jwks; } export async function verifyGovernanceContext(token: string, ctx: { sellerId: string; planId: string; mediaBuyId?: string; phase: "intent" | "purchase" | "modification" | "delivery"; resolveBrandJsonGovernanceAgent: (iss: string) => Promise<{ jwks_uri: string } | null>; seenJti: (iss: string, aud: string, jti: string) => Promise; isRevoked: (iss: string, jti: string, kid: string) => Promise; revocationFresh: (iss: string) => Promise; }) { const header = decodeProtectedHeader(token); if (header.typ !== "adcp-gov+jws") throw new GovTokenError("governance_token_invalid"); if (!["EdDSA", "ES256"].includes(header.alg ?? "")) throw new GovTokenError("governance_token_invalid"); const { iss } = decodeJwt(token); const agent = await ctx.resolveBrandJsonGovernanceAgent(iss as string); if (!agent) throw new GovTokenError("governance_issuer_not_authorized"); const { payload } = await jwtVerify(token, jwksFor(agent.jwks_uri), { issuer: iss as string, audience: ctx.sellerId, typ: "adcp-gov+jws", algorithms: ["EdDSA", "ES256"], clockTolerance: 60, }).catch(() => { throw new GovTokenError("governance_token_invalid"); }); if (payload.sub !== ctx.planId) throw new GovTokenError("governance_token_not_applicable"); if (payload.phase !== ctx.phase) throw new GovTokenError("governance_token_not_applicable"); if (ctx.phase !== "intent" && payload.media_buy_id !== ctx.mediaBuyId) throw new GovTokenError("governance_token_not_applicable"); if (!(await ctx.revocationFresh(iss as string))) throw new GovTokenError("governance_revocation_stale"); if (await ctx.isRevoked(iss as string, payload.jti as string, header.kid as string)) throw new GovTokenError("governance_token_revoked"); if (await ctx.seenJti(iss as string, ctx.sellerId, payload.jti as string)) throw new GovTokenError("governance_token_replayed"); return payload; } ``` **移行デュアルパス(3.0 中のセラー)**: ```ts theme={null} const JWS_COMPACT = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/; function handleGovernanceContext(value: string, ctx) { persistOpaque(value); // always persist and forward for auditor use if (!JWS_COMPACT.test(value)) return; // pre-3.0 opaque value, nothing to verify return verifyGovernanceContext(value, ctx); // throws on any failure } ``` #### 移行(3.0 → 3.1) * **3.0**: ガバナンスエージェントは、必須の `plan_hash` 監査層クレーム(セマンティクスは [Plan binding and audit](/docs/governance/campaign/specification#plan-binding-and-audit) を参照)を含め、このプロファイルに従ってコンパクト JWS を発行しなければなりません(MUST)。セラーは 15 ステップチェックリストを検証してもよい(MAY)。検証しないセラーはトークンを変更せずに永続化・転送しなければなりません(MUST)。JWS でない値は非推奨で、遷移中の pre-3.0 ガバナンスエージェントからのみ現れるべきです(SHOULD)。3.0 で非 JWS 値を発行するガバナンスエージェントは、セラーが検証不能なデプロイを検出できるよう、それをケイパビリティで宣言しなければなりません(MUST)。 * **3.1**: すべてのセラーは 15 ステップチェックリストに従って検証しなければなりません(MUST)。ガバナンスエージェントは JWS を発行しなければなりません(MUST)。非 JWS 値はエンドツーエンドで拒否されます。`plan_hash` は監査層のまま(ガバナンスエージェント / 監査人 / バイヤーコンプライアンス検証のみ — セラー検証ではない)。 フィールド名とスキーマ形状(単一文字列、4096 文字以下)はバージョン間で変わりません。文字列の内部形式のみが厳格化されます。これは以前のプロトコルバージョンからの相関キーセマンティクスを保持します — すでに値を不透明として扱うセラーは転送を続けるのに変更不要です。アカウンタビリティ特性を望むセラーは検証チェックリストを実装してオプトインします。 ### 署名付きリクエスト(トランスポート層) [署名付きガバナンスコンテキスト](#signed-governance-context)は認可アーティファクトに署名します。リクエスト署名はリクエスト自体 — メソッド、ターゲット URI、ヘッダー、(デフォルトで)ボディバイト — に署名し、特定のエージェントがリクエストを発行したことを、リプレイと改ざん保護付きで暗号学的に確立します。有効な署名は 1 つのことだけを証明します: **リクエストは、その鍵が署名したエージェントから来た。** そのエージェントがリクエストボディで名指しされたブランドのために行動する*認可*を持つかは別の関心事で、ターゲットハウスの brand.json の `authorized_operator[]` が規定します。このセクションは認証のみを定義します。認可ルックアップは brand.json スキーマが規定し、リクエストが署名されているかに関わらず発生します。 AdCP 3.0 はこのプロファイルを、`get_adcp_capabilities` の `request_signing` を介して**オプションかつケイパビリティ宣伝**として定義します。AdCP 4.0 — 次の破壊的変更蓄積ウィンドウ — は支出コミットオペレーションでそれを要求します。基盤は 3.0 で出荷され、早期採用者が強制前に正準化とプロキシ相互運用のバグを表面化できます。[Transport migration timeline](#transport-migration-timeline) を参照。 **役割:** * **エージェント**は、オペレーターの brand.json の `agents[]` エントリの自身の `jwks_uri` で公開した鍵でリクエストに署名します。オペレーター(brand.json をホストするドメイン)は直接購入するハウスでも認可されたサードパーティでもよい — このプロファイルは区別しません。署名者は常にエージェントです。 * **セラー**は署名を署名エージェントの公開鍵に対して検証し、エージェントアイデンティティを確立します。次にセラーは別個のブランド-オペレーター認可チェック(このプロファイルのスコープ外)を実行します。 * **エージェント側 AdCP エンドポイントを呼ぶセラー**(例: それ自体が AdCP プロトコル呼び出しであるバイヤーホストの変更コールバック)は、アウトゴーイングリクエストに対称的に署名します。受信エージェントはセラーオペレーターの brand.json の `agents[]` エントリで公開されたセラーの鍵に対して検証します。プッシュ通知 Webhook コールバック(`push_notification_config.url` や類似の非同期一方向通知)は、このプロファイルの対称 [Webhook callbacks](#webhook-callbacks) バリアントでカバーされます — セラーは `adcp_use: "request-signing"` 鍵でアウトバウンド署名し(非推奨の `"webhook-signing"` 値も受け入れられる)、バイヤーが検証します。 **依存関係:** * JWKS ディスカバリー、SSRF ルール、alg 許可リスト、失効セマンティクス、鍵ローテーションを上記の [AdCP JWS profile](#adcp-jws-profile) と共有します。リクエスト検証は決して別の鍵目的を受け入れません: リクエスト署名 JWK は `"adcp_use": "request-signing"`、`"use": "sig"`、`"key_ops": ["verify"]`、および異なる `adcp_use` を持つ他の JWKS エントリに現れない `kid` を宣言しなければなりません(MUST)。検証者は 4 つすべてを強制します。[Agent key publication](#agent-key-publication) を参照。Webhook パスは、Webhook `tag` がドメイン分離を提供するため独自の明示的な緩和を持ちます。 * ガバナンスの [Buyer identity resolution](#buyer-identity-resolution) のアイデンティティブートストラップ依存を解決します: リクエスト署名を検証するセラーは暗号学的に確立された署名エージェントアイデンティティを持ち、署名エージェントのオペレータードメインをガバナンス検証ステップの brand.json 解決入力として使ってもよい(MAY)。 **コンフォーマンス。** 検証者の動作は、`request_signing.supported: true` を宣伝する任意のエージェントで実行される [`/compliance/latest/universal/signed-requests`](https://adcontextprotocol.org/compliance/latest/universal/signed-requests) のユニバーサルなケイパビリティゲートストーリーボードで採点されます。ストーリーボードは下記の [verifier checklist](#verifier-checklist-requests) のすべてのステップとこのプロファイルのすべての正準化エッジルールを、[`/compliance/latest/test-vectors/request-signing/`](https://adcontextprotocol.org/compliance/latest/test-vectors/request-signing/) のテストベクターに対して行使します。自身のエージェントに対して CLI グレーダーを実行するには [Auth Graders](/docs/building/verification/grading) を参照。 **汎用の RFC 9421 レスポンス署名プロファイルはない。** このプロファイルは*リクエスト*に署名します。AdCP 3.x は同期*レスポンストランスポート*に署名する汎用のペアプロファイルを定義しません。セラーは同期 AdCP レスポンス(MCP `tools/call` でも、ストリーミング `artifactUpdate` フレームを含む A2A 非ストリーミングレスポンスでも)に RFC 9421 §2.2.9 レスポンス署名を適用してはならず(MUST NOT)、バイヤーは同期返信の RFC 9421 レスポンス署名に依拠してはなりません(MUST NOT)。即時レスポンストランスポートの完全性は、リクエストを運んだ認証済みセッション内の TLS に依拠します。ボディを変更する CDN でのリクエスト側ボディ完全性を規定する標準のエッジ終端の注意事項を除きます。セッションを超えて存続する必要のあるアーティファクトの耐久的な保存時証明 — 専門分野スコープのペイロード(ブランド権利、AAO Verified コンプライアンス、セールスインテリジェンスリレー、ガバナンスレシート、`plan_receipt` のような双方向否認防止レシート)を含む — は [signed webhooks](#webhook-callbacks)(`adcp_use: "request-signing"` 鍵で署名)の役割です。この分割は意図的です — 完全な根拠と、正準アーティファクトが証明可能である必要のあるツールの request-the-webhook パターンは [Security Model: What gets signed](/docs/building/concepts/security-model#what-gets-signed--and-what-doesnt) を参照。 **指定タスクのペイロードエンベロープレスポンス署名。** 閉じたタスクのリストが、そのレスポンス*ペイロード*を `adcp_use: "response-signing"` の下で暗号学的に署名されるものとして指定します。このプリミティブは RFC 9421 §2.2.9 トランスポートレスポンス署名と、要となる 2 つの軸で異なります: * **署名の場所:** HTTP レスポンスヘッダーではなく、レスポンスボディの中。 * **検証パス:** レスポンスボディをパースし、次に JWS をエージェントの `jwks_uri` で公開された応答エージェントの `response-signing` JWK に対して検証 — トランスポートヘッダー上の RFC 9421 ベース再構築ではない。 タスクは、そのレスポンスペイロードが正準の証明可能アーティファクトであり、かつ Webhook 発行の再構築が実現可能でない場合にのみ指定リストに認められます(デフォルトパスは [request-the-webhook パターン](/docs/building/concepts/security-model#the-request-the-webhook-pattern) を参照)。3.x のリストは次で閉じられています: * **`verify_brand_claim`** とそのバルクバリアント **`verify_brand_claims`**(Brand Protocol)。応答するブランドエージェントは、ブランドの `adcp_use: "response-signing"` 鍵の下で JWS エンベロープとしてレスポンスペイロードに署名します。署名は方向非対称の信頼モデルの要です — [`verify_brand_claim` trust model](/docs/brand-protocol/tasks/verify_brand_claim#trust-model) と [Building a brand agent — Signing setup](/docs/brand-protocol/building-a-brand-agent#signing-setup) を参照。 このリストにないタスクは、いかなる署名プリミティブの下でもレスポンスに署名してはなりません(MUST NOT)。任意のツールに RFC 9421 §2.2.9 を適用する汎用レスポンス署名ヘルパー(どの `tag` や `adcp_use` 文字列を作っても)はこのプロファイルの外で動作し、3.x 非コンフォーマントです。スペックが 3.x で認可する唯一のレスポンス署名プリミティブは、指定タスクリストのペイロードエンベロープ JWS です。 したがって `adcp_use: "response-signing"` 値は JWK 層でペイロードエンベロープのプリミティブに予約されます。**`adcp_use: "response-signing"` で公開された鍵は、このセクションで定義されたペイロードエンベロープ JWS のみに署名しなければなりません(MUST)。そのような鍵を使って RFC 9421 §2.2.9 トランスポート署名を生成することは、署名されるタスクに関わらずプロファイル違反です。** 将来のメジャーバージョンが任意のタスクに RFC 9421 トランスポートレスポンス署名をスコープする場合、検証者が JWK だけからプリミティブを区別できるよう、別個の `adcp_use` 値(例: `"response-transport-signing"`)を使わなければなりません(MUST)— ブランドプロトコル値は両方をカバーするよう後付けできません。リスト成長と追加のプリミティブは将来のスペックバージョンに延期された規範的決定です。 指定タスクの成功レスポンスは [`response-payload-jws-envelope.json`](https://adcontextprotocol.org/schemas/v3/core/response-payload-jws-envelope.json) に一致する `signed_response` メンバーを運ばなければなりません(MUST)。エンベロープペイロードは正準の署名済みタスクボディオブジェクトで、`typ: "adcp-response-payload+jws"`、`task`、`brand_domain`、`agent_url`、`request_hash`、`iat`、`exp`、`response` を含まなければなりません(MUST)。外側のタスクボディフィールドは通常のタスクコンシューマー向けの便宜フィールドです。署名に依拠する検証者は、いずれかの未署名タスクボディフィールドが `signed_response.payload.response` と食い違う場合エンベロープを拒否しなければなりません(MUST)。プロトコル/バージョンエンベロープフィールドはこの比較から除外され、`status`、`context_id`、`task_id`、`message`、`timestamp`、`replayed`、`adcp_version`、`adcp_major_version` を含みます。 このプロファイルは RFC 7797 の非エンコードペイロードではなく通常の JWS 署名を使います。JWS 署名入力は `BASE64URL(UTF8(protected)) || "." || BASE64URL(UTF8(JCS(payload)))` で、`payload` は `signed_response.payload`、`protected` は `{ "alg": "EdDSA" | "ES256", "kid": "...", "typ": "adcp-response-payload+jws" }` にデコードされます。protected ヘッダーは `b64` を含んではなりません(MUST NOT)。レスポンス検証者は [AdCP JWS profile](#adcp-jws-profile) の共有 JWS ディスカバリーとハードニングルールを強制しなければなりません(MUST): 許可アルゴリズム、`use: "sig"`、`"verify"` を含む `key_ops`、正確な `adcp_use: "response-signing"`、missing-`kid` 再フェッチ、失効チェック、SSRF セーフな JWKS フェッチ、正準化前の重複キー拒否。 `request_hash` は `sha256:` に JCS 正準リクエストバインディングオブジェクト `{ task, brand_domain, agent_url, caller_identity, request }` の非パディング base64url SHA-256 を加えたものです。`caller_identity` は、認証済みトランスポートまたはクレデンシャルマッピングから導出された型付き正準文字列でなければなりません(MUST)。例: `signed-agent-url:`、`api-client-id:`、`mtls-san:`。認証済み呼び出し元アイデンティティが存在しない場合、`caller_identity` は `null` で、検証者はレスポンスを呼び出し元にバインドされない弱い証拠として扱わなければなりません(MUST)。 `brand_domain` はエコーではなくテナントバインディングフィールドです。マルチブランドエージェントは、それをサーバー側のテナント解決と、答えを生成したポリシーストアの brand.json エントリから設定しなければならず(MUST)、リクエストボディからコピーしてはなりません(MUST NOT)。`agent_url` は `response-signing` JWK がエンベロープを検証する応答 `agents[]` エントリの正準 URL です。オンライン検証者は、小さなクロックスキュー許容のみを適用した後、`exp` 以降のエンベロープを拒否しなければなりません(MUST)。監査検証者は `exp` 後に検証してもよい(MAY)が、ブランドエージェントが記載された `iat`/`exp` ウィンドウ中にそのペイロードに署名したという履歴証拠としてのみです。 レスポンス署名鍵は目的だけでなくブランドテナントでもスコープされます。共有マルチブランドフリートは、同じソフトウェアと `agent_url` が複数ブランドを提供しても、提供する各 `brand_domain` に別個のレスポンス署名鍵素材と別個の `kid` 値を公開しなければなりません(MUST)。レスポンス署名 JWK のクロスブランド再利用は、テナントバインドのリプレイ分析を無効にするためプロファイル違反です。このルールは通常のクロス目的分離より厳しく、`adcp_use: "response-signing"` 鍵にのみ適用されます。 #### トランスポートスコープ | Class | 3.0 | 4.0 | | ----------------------------------------------------------------------------------------- | --------------------------------------- | ------------ | | Spend-committing (`create_media_buy`, `update_media_buy`, `acquire_*`, `activate_signal`) | Optional, capability-advertised | Required | | Reversible state changes (`sync_creatives`, `update_creative_status`) | Optional | Recommended | | Read / discovery (`get_products`, `get_media_buy_delivery`, `list_*`) | Not in scope | Not in scope | | TMP `provider_endpoint_url` requests | Out of scope (TMP has its own envelope) | Out of scope | 読み取り呼び出しは bearer 認証のままです。読み取りトラフィックへの署名は、比例した利益なしに検証コストを追加します。署名の目的は状態変更オペレーションの完全性です。 #### クイックスタート: 3.0 でリクエスト署名にオプトイン 4.0 のフリップ前に 3.0 で署名をパイロットしたい実装者向け: **リクエストに署名するエージェントとして:** 0. ターゲットセラーで `get_adcp_capabilities` を呼び出す。`request_signing.supported_for` と `required_for` を読んで、セラーがあなたに署名を期待する AdCP オペレーションを確認し、`request_signing.protocol_methods_supported_for` / `protocol_methods_required_for` を読んで、セラーの検証者がカバーする JSON-RPC プロトコルメソッド(例: `tasks/cancel`)を確認する。`covers_content_digest`(`"required"` / `"forbidden"` / `"either"`)を読んで、`content-digest` をカバーしなければならない、してはならない、してもよいかを確認する。 1. Ed25519 鍵ペアを生成: `openssl genpkey -algorithm ed25519 -out signing-key.pem`。 2. 公開鍵を JWK としてエクスポート。`"kid"`、`"use": "sig"`、`"key_ops": ["verify"]`、`"adcp_use": "request-signing"`、`"alg": "EdDSA"` を追加。 3. JWK をエージェントの `jwks_uri`(brand.json の `agents[]` エントリで宣言された URL。エージェント URL のオリジンの `/.well-known/jwks.json` にデフォルト)で公開。 4. AdCP クライアントを秘密鍵とエージェント URL で設定。SDK は、セラーの `supported_for` または `required_for` ケイパビリティにリストされた任意のオペレーションと、`protocol_methods_supported_for` または `protocol_methods_required_for` にリストされた任意の JSON-RPC メソッドについて、セラーの `covers_content_digest` ポリシーを守って自動的にリクエストに署名する。SDK は、秘密鍵がプロセスメモリではなくマネージド鍵ストア(KMS / HSM / Vault)に存在できるよう、プラガブルな署名者をサポートすべき(SHOULD)— 下記の [Production key storage](#production-key-storage) を参照。 5. [`/compliance/latest/test-vectors/request-signing/`](https://adcontextprotocol.org/compliance/latest/test-vectors/request-signing/) のコンフォーマンスベクター(AdCP バージョンごとに公開。ソースは `static/compliance/source/test-vectors/request-signing/`)でエンドツーエンド検証 — クライアントが正例ベクターの `expected_signature_base` に一致する署名を生成すれば完了。 **検証者(セラー)として:** 1. `get_adcp_capabilities` で `request_signing.supported: true` を宣伝。パイロット中は `required_for: []` のまま。相手方ごとに段階的にオペレーションを追加。 2. 変更系ルートで署名検証ミドルウェアを有効化。[verifier checklist](#verifier-checklist-requests) を実装 — 14 のチェックすべて(13 の番号付きステップとサブステップ 9a)、最初の失敗でショートサーキット。 3. `required_for` を設定する前に、パイロット相手方についてシャドウモード(検証してログ。失敗で拒否しない)で開始。最初の数週間は検証失敗をオペレーションではなくモニタリングで表面化。 4. 検証者に対してコンフォーマンス負例ベクターを実行 — 各拒否はベクターの記載された `error_code` を生成しなければならない(MUST)。ベクターの `failed_step` は情報的です。正しいエラーコードで拒否する実装は、内部ステップ番号が異なってもコンフォーマントです。 **最小実行可能検証者(3.0 シャドウモード):** チェックリストのステップ 1-9、9a、10、インメモリリプレイキャッシュ、軽量な `kid` メンバーシップチェック付き 1 分失効ポーリング(完全な grace セマンティクスは延期)。リプレイやダイジェスト失敗で拒否されるリクエストがないので、これは log-and-observe シャドウモードに許容されます。**`required_for` に任意のオペレーションを追加する前に、ステップ 11-13 を実装** — ダイジェスト再計算(ステップ 11)、成功後のリプレイ挿入(ステップ 13)、完全な失効 stale grace ウィンドウ(ステップ 9 の一部)。不完全な検証者で強制に切り替えると、シャドウログではなくライブ本番トラフィックでリプレイとボディ完全性のギャップが表面化します。ステップ 1 を飛び越さないでください — 不正な署名は常に拒否し、決してフォールバックしません。 #### 本番の鍵保管 署名者の秘密鍵がどこに存在するかは実装依存です — スペックはワイヤー上のバイトのみに関心があります — が、オペレーターは本番でプロセスメモリに秘密署名鍵を保持することを避けるべきです(SHOULD)。プロセス侵害は署名鍵を漏らし、唯一の救済は、公開鍵をキャッシュしたすべての相手方をまたぐ(それらのキャッシュ TTL 内での)ローテーションです。 推奨パターン: SDK がプラガブルな署名者インターフェース(例: `sign(payload: Uint8Array): Promise`)を公開し、オペレーターのアダプターがオペレーションをマネージド鍵ストア — AWS KMS、GCP KMS、Azure Key Vault、HashiCorp Vault Transit、または HSM — に委任します。鍵はマネージドストアを決して離れません。SDK は正準署名ベースを構築し、ストアがそれに署名し、SDK は返されたバイトから `Signature` と `Signature-Input` ヘッダーを組み立てます。ワイヤー形式はインプロセス署名と同一です。 アダプター作成者向けの 2 つの実装注記: * ほとんどの KMS API が返す ECDSA-P256 署名は DER エンコードです。このプロファイルと RFC 9421 §3.3.1 は IEEE P1363(`r‖s`、P-256 では 64 バイト)を要求します。アダプター境界で変換します。 * KMS 鍵を単一目的として扱います。このプロファイルの `tag` パラメーターは署名者ではなく検証者を保護します — 同じ KMS 鍵を AdCP リクエスト署名と他の任意の署名プロトコルに再利用するオペレーターは、クロスプロトコルオラクルを作ります。AdCP 署名パスのみが鍵を呼び出せるよう KMS アクセスポリシーをバインドします(GCP `roles/cloudkms.signer` を特定の cryptoKey にスコープ、AWS `kms:Sign` を鍵 ARN で条件付け)。 リファレンス実装: `@adcp/sdk`(TypeScript)は sync/async パリティを持つ `SigningProvider` インターフェース、テスト用のインメモリプロバイダー、[`examples/gcp-kms-signing-provider.ts`](https://github.com/adcontextprotocol/adcp-client/blob/main/examples/gcp-kms-signing-provider.ts) の GCP KMS リファレンスアダプターを出荷します。完全なウォークスルーは [SDK signing guide](https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/SIGNING-GUIDE.md#step-35-production-key-storage--kms--hsm--vault) を参照。 **トリップワイヤーパターン — init 時に公開鍵をアサート。** マネージド鍵ストアは黙ってローテーションできます(IAM ポリシースワップ、バージョン無効化、敵対的置換)。公開 JWKS を更新せずにローテーションが起きると、変わらない `kid` をフェッチする検証者は、明確なエラーシグナルなしにすべての署名を拒否します — オペレーターは KMS ミスマッチではなく相手方の失敗を見ます。防御: 期待される公開鍵(SPKI バイト、base64 エンコード)をコードと共にコミットし、署名者 init 時にストアが返す鍵とバイト比較(`getPublicKey()` など)します。ミスマッチは、すべての署名済み呼び出しで黙ってではなく、起動時に大きく失敗します。ローテーションはその後、意図的な二段階になります: ピン留めされた定数を更新し、新しい鍵バージョンパスを設定し、デプロイ。 **ライフサイクル: eager ではなく lazy init。** プロセスがリスナーをバインドする前に `getPublicKey`(または任意の KMS ウォームアップ呼び出し)を呼ぶことはレビューでクリーンに見えますが、危険な失敗モードがあります: KMS 認証が誤設定されていると、KMS クライアント内の gRPC / TLS リトライが無期限にブロックし、プロセスはポートを開かず、インフラのヘルスチェックがタイムアウトします — 根本的な KMS エラーではなく「サービス到達不能」アラームを表面化します。正しいライフサイクルは最初の署名時の lazy init です: リクエストが署名を必要とする最初のときにストアを呼び、成功時のみ結果をキャッシュし(エラーを決してキャッシュしない)、並行する初回呼び出しリクエストを in-flight promise で重複排除します。Fail-fast の誤設定検出は、プロセス起動時ではなく、切り替え前にデプロイターゲットの認証情報で KMS パスを行使する CI/CD プレデプロイプローブに属します。 **`adcp_use` ごとに 1 JWK — 公開形状。** 単一目的ルールは鍵素材**と** JWKS 公開に適用されます。Webhook は独自の目的を必要としないことに注意: それらは `"request-signing"` 鍵で署名されるので([Webhook callbacks](#webhook-callbacks) のステップ 8 を参照)、リクエストと Webhook の両方を同じ鍵で署名するオペレーターは単一の `"request-signing"` エントリを公開します。Webhook に別個の鍵素材(影響範囲分離)を望むオペレーターは、**別個の `kid` を持つ 2 つ目の `"request-signing"` 鍵**を公開します — 分離は別個の `adcp_use` ではなく `kid` から来ます。`adcp_use` 値は常に**文字列**であり配列ではありません — 単一エントリに `"adcp_use": ["request-signing","webhook-signing"]` を公開することは受信者が拒否するスキーマエラーです: ```json theme={null} { "keys": [ { "kty": "OKP", "crv": "Ed25519", "x": "SRYr8eSvjkZF6dAUquI1sKuU4YGZkoGH-2jwkz4dRJg", "kid": "acme-signing-2026-04", "alg": "EdDSA", "use": "sig", "adcp_use": "request-signing", "key_ops": ["verify"] }, { "kty": "OKP", "crv": "Ed25519", "x": "lHJI-IvBwCE36heDNOyBmCk5UMKRIs4b4BAWJRgao-M", "kid": "acme-webhook-2026-04", "alg": "EdDSA", "use": "sig", "adcp_use": "request-signing", "key_ops": ["verify"] } ] } ``` この 2 つ目のエントリは [Key publication](#webhook-callbacks) セクションのオプションの Webhook 分離鍵です: 同じ `adcp_use: "request-signing"`、別個の `kid`、Webhook 鍵の侵害がリクエスト署名に及ばないよう Webhook 署名に使用。別個の `kid` 値はまた、相手方が 2 つの鍵を独立にキャッシュ・ローテーションできることを意味します。 #### AdCP RFC 9421 プロファイル このプロファイルは、クロス実装の相互運用が扱いやすくなるよう、RFC 9421 を単一の正準形状に制約します。 **カバーされるコンポーネント(すべての署名済みリクエストで REQUIRED):** | Component | Notes | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `@method` | 大文字。 | | `@target-uri` | 下記のアルゴリズムで正準化。署名者は署名ベースを計算する前に正準化を適用しなければならず(MUST)、検証者は検証前に受信したリクエストに同じ正準化を適用しなければならない(MUST)。 | | `@authority` | 小文字の `host[:port]`、デフォルトポート(https は `443`、http は `80`)を除去。 | | `content-type` | ボディのあるリクエストで必須。 | | `content-digest` | 検証者の `request_signing.covers_content_digest` ケイパビリティが規定 — [Content-digest and proxy compatibility](#content-digest-and-proxy-compatibility) を参照。 | **`@target-uri` 正準化**は [AdCP URL canonicalization rules](/docs/reference/url-canonicalization) に従います — RFC 3986 §6.2.2(構文ベース正規化)と §6.2.3(スキームベース正規化)、UTS-46 Nontransitional IDN 処理、IPv6 ゾーン識別子拒否を適用する 8 ステップ。署名者と検証者は同じアルゴリズムを適用します。そこで拒否された不正なオーソリティは、署名パスで `request_target_uri_malformed` にマップされます。権威的なアルゴリズム、コンフォーマンスベクター、落とし穴リストはそのページに存在します — このプロファイルの扱いを薄く保つことで、署名固有のコピーと汎用コピーの間の発散を防ぎます。 **`@authority` 正準化**は、正準化アルゴリズムのホストとポートステップ後の URL のオーソリティから `host[:port]` を生成します(小文字ホスト / IDN → ACE / IPv6 ブラケット保持。userinfo 除去。デフォルトポート除去)。IPv6 ホストは `@authority` でブラケットを保持します(`[::1]:8443`)。検証者は、存在する場合 HTTP/2+ の `:authority` 疑似ヘッダーから、そうでなければ受信した HTTP/1.1 `Host` ヘッダーから `@authority` を導出しなければなりません(MUST)— リバースプロキシルーティング状態、ロードバランサーメタデータ、フォワードプロキシが転送中に書き換えた任意の `Host` 値からではありません。**受信リクエストに `:authority` と `Host` の両方が存在する場合**(HTTP/2→HTTP/1.1 変換中間者は RFC 7540 §8.1.2.3 により両方を残すことが許可され、これは等価性を要求するがソースの除去は要求しない)、検証者は正準化後にバイト等価でなければ `request_target_uri_malformed` で拒否しなければなりません(MUST)。pick-one 動作は黙ったダウングレード面です。ソースヘッダーに関わらず、正準化された値は正準 `@target-uri` のオーソリティコンポーネントとバイト単位で一致しなければなりません(MUST)— 署名済み `@target-uri` に対するバイト一致が要となる安全ゲートです。`Host` は転送中に書き換えられ得るからです。ミスマッチは `request_target_uri_malformed` で拒否します。これはクロス vhost リプレイベクトルを閉じます: TLS 終端されたリクエストを傍受し、同じ検証者プール上の 2 つ目の vhost(同じ証明書 SAN、異なる `Host`)にリプレイする攻撃者は、署名が `@authority` をカバーしていてもオーソリティ一致チェックに失敗します。 正準化する署名者と正準化する検証者は、同じ論理リクエストに対して同一のバイトを生成しなければなりません(MUST)。あなたの 9421 ライブラリが異なるルールを適用する場合、このプロファイルに一致するよう設定するか、URL をライブラリに渡す前に正規化します。 [`canonicalization.json`](https://adcontextprotocol.org/compliance/latest/test-vectors/request-signing/canonicalization.json) コンフォーマンスセットは、固定入力と期待出力、加えて不正オーソリティ拒否ケースで、アルゴリズムのすべてのルールを行使します。SDK はこのセットをすべてのコミットで実行すべきです(SHOULD)— 署名者間の正準化発散は、そうでなくなるまで黙っており、その後診断が痛い本番相互運用バグになります。 検証者は、カバーされるコンポーネントリストがリクエストタイプに必要なコンポーネントを省略する署名を拒否しなければなりません(MUST)。署名者は調整なしに追加ヘッダーをカバーしてはなりません(MUST NOT)— 余分なコンポーネントは、それらを含めない実装をまたいで署名を黙って無効にします。 **署名パラメーター(`Signature-Input` パラメーター、すべて REQUIRED):** | Parameter | Notes | | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `created` | Unix 秒。60 秒より先の未来なら拒否。 | | `expires` | Unix 秒。`expires > created` かつ `expires − created ≤ 300`(5 分最大有効性)を満たさなければならない。過去なら ±60 秒スキュー許容で拒否。 | | `nonce` | Base64url エンコード、非パディング(末尾 `=` なし)。検証者は、デコードされたバイト長が 16 バイト未満、または値がパディングを含む場合拒否しなければならない(MUST)。これが「128 ビット以上のエントロピー」要件が実際に強制される方法。 | | `keyid` | 署名者の公開 JWKS の `kid` に一致。 | | `alg` | `ed25519` または `ecdsa-p256-sha256` でなければならない。検証者はライブラリのデフォルトとは独立に許可リストを強制しなければならない(MUST)。 | | `tag` | 正確に `adcp/request-signing/v1` でなければならない — バイト単位一致、プレフィックス一致なし、ケースフォールディングなし。`tag` sig-param は `Signature-Input` にちょうど 1 回現れなければならない。検証者は重複を拒否しなければならない(MUST)。tag 名前空間がプロファイルのバージョン管理方法。将来のバージョンはパラメーターセマンティクスを変えるのではなく tag をバンプし、`adcp/request-signing/v2` 検証者は `v1` 署名を拒否し逆も同様。 | 6 つのパラメーターすべてが REQUIRED です。検証者はいずれかが不在なら拒否しなければなりません(MUST、`request_signature_params_incomplete`)。 **アルゴリズム命名 — JWK vs RFC 9421。** 各アルゴリズムの 2 つの名前はソーススペックで異なります。実装はこれらを十分頻繁に混同するので表が必要です: | Algorithm | JWK `alg` (in JWKS) | RFC 9421 `alg` (in `Signature-Input`) | | ------------------------ | ------------------- | ------------------------------------- | | Ed25519 | `EdDSA` | `ed25519` | | ECDSA P-256 with SHA-256 | `ES256` | `ecdsa-p256-sha256` | 検証者が `keyid` を解決して JWK に `"alg": "EdDSA"` を見つけたとき、一致する sig-param 値は `ed25519` です。実装は、それぞれ独立に許可リストを検証することに加えて、2 つが一致すること(JWK alg がマッピング表で sig-param alg に一致)を検証すべきです。ガバナンスプロファイルからのエッジランタイム根拠が適用されます — `ES256` は `EdDSA` がランタイム設定を要するエッジ向けの代替です。 **リクエストごとに 1 署名。** 検証者はちょうど 1 つの `Signature-Input` ラベル(慣習的に `sig1`)を処理しなければならず(MUST)、リクエストに存在する追加のラベルを無視しなければなりません(MUST)。リレーされたリクエストに再署名する必要のある中間者は、上流ラベルに追加するのではなく置換しなければなりません(MUST)。完全なリレーチェーンセマンティクス(リレーが発信者の署名を保持したい場合)は [#2324](https://github.com/adcontextprotocol/adcp/issues/2324) で追跡され、3.0 のスコープ外です。 **バイナリ値エンコード(`Signature`, `Content-Digest`)。** RFC 9421 §3.1 と §2.1.3 はバイナリ値を RFC 8941 Structured Field `sf-binary` トークン(`::`)として発行し、RFC 8941 §3.3.5 は `+`/`/` と `=` パディングを持つ標準 base64 アルファベット(RFC 4648 §4)を規定します。AdCP プロファイルはこれを上書きします: `Signature` と `Content-Digest` の sf-binary 値は**パディングなし base64url**(RFC 4648 §5)でエンコードしなければならず(MUST)、内側バイトが `[A-Za-z0-9_-]` から引かれ末尾 `=` のないトークンを生成します。 根拠: URL セーフ、パディングなし、すでに base64url 非パディングと規定された `nonce` sig-param と対称。HTTP ヘッダー値の標準 base64 の 2 つの相互運用ハザード — 一部のプロキシが書き換える `/` と一部のヘッダーパーサーが構造化フィールドパラメーター区切りとして扱う `=` — を避けます。 検証者の要件: 1. 署名者はパディングなし base64url のみを発行しなければなりません(MUST)。`+`、`/`、`=` を含む `Signature` または `Content-Digest` 値を発行する署名者は非コンフォーマントです。 2. 検証者はパディングなし base64url を受け入れなければなりません(MUST)。検証者はこの明確化より前の相手方との相互運用のため、純粋な標準 base64 トークンも寛容にデコードすべきです(SHOULD、`+`→`-` 次に `/`→`_` に変換、次に末尾 `=` を除去、次に base64url デコード)。この寛容は **AdCP 3.2** で削除予定の互換性の便宜です — それに依拠する署名者はそれまでにパディングなし base64url に移行しなければなりません(MUST)。 3. 検証者は、アルファベットを混在させる任意のトークン(同じトークン値内の `[+/=]` 内の任意の文字 AND `[-_]` 内の任意の文字)を `request_signature_header_malformed` で拒否しなければなりません(MUST)。混在アルファベットトークンは曖昧です: `A+B-` は「標準 base64 文字を変換」と「base64url デコード」ステップの順序によって異なるバイトにデコードされ得、検証者間で異なる `Content-Digest` バイトは、攻撃者があるバリデーターが受け入れ別のが拒否するダイジェストミスマッチを仕込むことを許します。 4. コンフォーマンスベクターの `expected_signature_base` フィールドはバイナリ値エンコードから独立です — 正準署名ベースバイトを含み、ヘッダーフィールドエンコードではありません。発行される `Signature` トークン自体のみがエンコードされます。 **非 AdCP 上流からの `Content-Digest` についての注記。** RFC 9530 §2 は `Content-Digest` を定義し、sf-binary を RFC 8941(標準 base64)に委ねるので、別のエコシステムからのコンフォーマントな 9530 発行者(CDN、非 AdCP フレームワーク)は、RFC 8941 デフォルトを使ってインバウンドリクエストに `Content-Digest` を設定するかもしれません。上記の AdCP 上書きは**署名済み AdCP リクエスト**に適用されます。そのようなリクエストを処理する検証者は上書きルールを使わなければなりません(MUST)。未署名トラフィックや非 AdCP 上流からの `Content-Digest` を扱う検証者はどちらのエンコードを受け入れてもよい(MAY)— これは署名プロファイルのスコープ外です。 **`required_for` / `supported_for` のオペレーション名は AdCP プロトコルオペレーション名**(`create_media_buy`, `update_media_buy`, `acquire_rights` など)です — MCP ツール名、A2A スキル名、任意のトランスポート固有の改名ではありません。検証者は AdCP プロトコルスペックが定義しないオペレーション名を受け入れてはなりません(MUST NOT)。これがクロストランスポート検証者が「`create_media_buy` に署名された」の意味に合意する方法です。 **プロトコルメソッドカバレッジ(`protocol_methods_*`)。** AdCP オペレーションは相手方が呼ぶ唯一の変更系面ではありません: A2A 0.3.0 §7.x は同じ認証済みチャネルを通るタスクライフサイクルメソッド(`tasks/cancel`, `tasks/get`, `tasks/resubscribe`)を定義し、MCP トランスポートは SDK タスクストアが接続されると同じ `tasks/*` JSON-RPC メソッドを自動登録します。セラーはこれらのメソッドの検証者カバレッジを AdCP オペレーションリストとは別の名前空間で宣言します: | Field | Contents | Match semantics | | ------------------------------------------------ | ------------------------------------------------ | ----------------------------------------------------------- | | `request_signing.protocol_methods_supported_for` | JSON-RPC method strings (e.g., `"tasks/cancel"`) | インバウンドリクエストの JSON-RPC `method` フィールドが一致するとき、検証者は署名を受け入れて検証。 | | `request_signing.protocol_methods_warn_for` | Same | `warn_for` のシャドウモードミラー: 失敗をログ、拒否しない。 | | `request_signing.protocol_methods_required_for` | Same | 未署名の一致を `request_signature_required` で拒否。 | 一致する値は JSON-RPC エンベロープの `method` フィールド(`tasks/cancel`, `tasks/get`, …)であり、MCP `tools/call` の `params.name` では**ありません**。AdCP ツール名(`/` なし)は任意の `protocol_methods_*` 配列に現れてはならず(MUST NOT)、JSON-RPC メソッド名(`/` を含む)は `supported_for` / `warn_for` / `required_for` に現れてはなりません(MUST NOT)。検証者は名前空間分割に違反するケイパビリティブロックを、2 つの間で文字列を黙って強制するのではなく、設定時エラーで拒否しなければなりません(MUST)。**検証者はクロス名前空間一致してはなりません: `protocol_methods_required_for` メンバーシップは JSON-RPC `method` が `tools/call` であるボディ(`params.name` がリストされたメソッド文字列に等しくても)で満たされてはならず(MUST NOT)、`required_for` メンバーシップは JSON-RPC `method` が `tools/call` 以外の任意のものであるボディで満たされてはなりません(MUST NOT)。** 2 つのバケットは互いに素なエンベロープフィールドに対して一致されます。 署名ベース構築は両方の名前空間で同一です: 同じ RFC 9421 カバーコンポーネント(`@target-uri`, `@method`, セラーの `covers_content_digest` ポリシーに従う `content-digest`, 存在する場合 `authorization`)が適用され、`@target-uri` と `@method` は JSON-RPC メソッド文字列ではなく実際の HTTP リクエストを反映します。`tasks/cancel` POST に署名するバイヤーは、他の変更系呼び出しと全く同じように署名します。新しいフィールドが変えるのは、どの JSON-RPC メソッドが検証のスコープ内かのセラーの宣言のみです。 **共有トランスポート上のクロス名前空間リプレイリスク。** 単一の `@target-uri` が `tools/call` エンベロープと JSON-RPC プロトコルメソッドの両方を受け入れる場合(正準の MCP レイアウト — 両方が `/mcp` に POST)、`@target-uri` と `@method` だけではボディがどの JSON-RPC メソッドを呼ぶかをバインドしません。`method` フィールドはボディに存在します。`content-digest` カバレッジなしでは、署名済み `tools/call` リクエストをキャプチャする経路上攻撃者は、署名ウィンドウ内でボディを `{"method":"tasks/cancel",...}`(または逆)にスワップでき、検証者はそれを受け入れます。`tools/call` と共有されるトランスポート上で `protocol_methods_required_for`(または任意の `protocol_methods_*`)を設定するセラーは、ボディ — そしてそれを通じて JSON-RPC メソッド — が署名にバインドされるよう `covers_content_digest: 'required'` を設定すべきです(SHOULD)。`'required'` を採用できないセラーは、`@target-uri` 自体が名前空間を分割するよう、AdCP とプロトコルメソッドトラフィックを別個の `@target-uri` にマウントしなければなりません(MUST)。 3.x でケイパビリティブロックを読むバイヤーは、`supported_for` / `required_for` からプロトコルメソッドカバレッジを仮定してはなりません(MUST NOT): `create_media_buy` を `required_for` にリストし `protocol_methods_*` について沈黙するセラーは、`tasks/cancel` カバレッジを宣言していません。`tasks/cancel` に日和見的に署名するバイヤー SDK(セラーが沈黙するときの唯一の擁護可能なデフォルト)はスペックに違反せずにそうしてもよい(MAY)が、相互運用可能な強制は、セラーが `protocol_methods_supported_for` または `protocol_methods_required_for` を設定して初めて生じます。 #### エージェント鍵の公開 リクエスト署名鍵は、署名エージェント自身のオペレーターの brand.json の `agents[]` エントリの `jwks_uri` に存在し、アウトバウンド Webhook は `"request-signing"` 鍵(オプションで別個の `kid` の下の Webhook 専用鍵素材)で署名されます。署名するすべてのエージェント — 任意の `type` の — は同じ公開パターンを使います。パブリッシャー `adagents.json` は追加で `authorized_agents[].signing_keys[]` を通じて許可されたセラー鍵をピン留めしてもよい。存在する場合、そのピンはスコープされたセルサイド認可に権威的です。 **パブリッシャーピンの優先順位。** パブリッシャーの `adagents.json` の認可エージェントエントリが `signing_keys` ピン([`adagents.json` §`signing_keys`](/docs/governance/property/adagents#signing_keys) を参照)を運ぶ場合、そのピンは権威的です: 検証者は、`jwks_uri` の内容に関わらず、`keyid` がピン留めセットにない任意の署名を拒否しなければなりません(MUST)。エージェントホストの JWKS は、パブリッシャーピンが存在するときは常に助言的です。これはエージェントドメイン侵害ウィンドウを閉じます — エージェントのドメインを乗っ取る攻撃者は、パブリッシャーのピンが依然受け入れを規定するため、エンドポイントとその宣伝された鍵の両方を黙ってスワップできません。パブリッシャーは、委任スコープに変更系オペレーションを含む任意のエージェントについてピン留めすることが要求されます。ローテーションとキャッシュセマンティクスは adagents.json ルールを参照。 各リクエスト署名 JWK エントリは宣言しなければなりません(MUST): | Member | Value | Notes | | ---------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `use` | `"sig"` | 標準 JWK 署名用途。 | | `key_ops` | `["verify"]` | 検証者可視の JWKS は verify のみを宣言。署名オペレーターは対応する秘密鍵を JWK スペックに従い `["sign"]` でローカルに保持。 | | `adcp_use` | `"request-signing"` | AdCP 固有の目的判別子。`"governance-signing"`(JWS プロファイル)と将来の任意の AdCP 署名目的と区別。検証者はリクエスト署名を検証するとき `adcp_use` が不在または異なる任意の JWK を拒否しなければならない(MUST)。同じ `"request-signing"` 鍵(または別個の `kid` の下の 2 つ目)がアウトバウンド Webhook にも署名 — [Webhook callbacks](#webhook-callbacks) を参照。(`"webhook-signing"` は削除保留の非推奨目的 — [#5555](https://github.com/adcontextprotocol/adcp/issues/5555) を参照。後方互換性のため Webhook パスで依然受け入れられる。) | | `kid` | distinct | JWKS 内で一意。`adcp_use` に関わらず他のエントリの `kid` と衝突してはならない(MUST NOT)。 | | `alg` | `"EdDSA"` or `"ES256"` | 署名の `alg` パラメーターに一致しなければならない(JWK `alg` は JWS 名を使い、`Signature-Input` の `alg` は RFC 9421 名を使う)。 | クロス目的鍵再利用は禁止され、上記の明示的な Webhook 緩和を除き `adcp_use` を介して**ローカルに強制可能**です: `"request-signing"` 鍵は、RFC 9421 `tag` がそれらのプロファイルを分離するため、リクエストまたは Webhook のどちらにも署名できます。単一の JWK エントリは 1 つの `adcp_use` 値のみを宣言できるので、パブリッシャーはガバナンス署名鍵を有効なリクエスト署名鍵として偶然(または意図的に)提示できません。検証者はフェッチした JWK 上の `adcp_use` を確認し、他の JWKS エンドポイントをまたいでは確認しません — クロスエンドポイントルックアップは要求も許可もされません。 **オリジン分離(ガバナンスは MUST、他は SHOULD)。** `adcp_use` は帯域内判別子です — クロス目的検証を防ぎますが、公開オリジンを防御しません。共有 JWKS エンドポイントのオリジン侵害は、それが公開するすべての署名目的を同時に侵害します。ガバナンス署名鍵はシステムで最も影響範囲の大きい鍵(その侵害はマルチテナント侵害)なので、ガバナンス署名鍵はトランスポート/Webhook RFC 9421 鍵とは別のオリジンから提供されなければなりません(MUST)。正準パターンは: * `governance-keys.{org}.example/.well-known/jwks.json` — ガバナンス署名 JWK のみ * `keys.{org}.example/.well-known/jwks.json` — リクエスト署名鍵(Webhook 専用 `kid` を含む)、後方互換性ウィンドウ中の非推奨 Webhook 署名鍵、TMP 鍵 オペレーターはさらに進んで各署名面を別個のサブドメインから提供すべきです(SHOULD)。多層防御: ガバナンス鍵はオフラインローテーション(手動ローテーションと人間承認付き HSM/KMS)にすべきで(SHOULD)、トランスポートと Webhook 鍵は自動ローテーションを使ってもよい(MAY)。オペレーターは `get_adcp_capabilities` に `identity.key_origins` マップを公開して分離スキームを宣伝します。スキーマは `governance_signing`、`request_signing`、`webhook_signing`、`tmp_signing` オリジン URI を定義します。`webhook_signing` は Webhook 配信面を名指し、必要なライブ `adcp_use: "webhook-signing"` 目的ではありません。Webhook がリクエスト署名鍵を使うとき `request_signing` と同じオリジンを指してもよい。実装者は、相手方がオンボーディングでオリジン分離を検証できるよう、フィールドを設定すべきです(SHOULD)。**フィールドが存在する場合、検証者はオンボーディングで宣言されたガバナンス署名オリジンが宣言されたリクエスト/Webhook 署名オリジンと異なることを確認し、共存の場合ユーザーが対処可能なエラーでオンボーディングを拒否しなければなりません(MUST)。** オリジン分離の MUST はそうでなければワイヤー上で検証不能です — 宣伝を公開する要点は、相手方がそれをプログラム的に強制できるようにすることです。規範ルールに違反する宣言を受け入れることは制御を無効にします。検証者は追加で各宣言された JWKS をフェッチしてその `jwks_uri` オリジンが宣伝値に一致することを確認してもよい(MAY)。 **実装者注記:** `adcp_use` はカスタム JWK メンバーです。主要な JOSE ライブラリ(`jose`, `node-jose`, `python-jose`, `go-jose`)はパース時に未知のメンバーを保持します。厳格な JWK バリデーター(`PyJWT` の一部モード、Web Crypto API の `SubtleCrypto.importKey`)は未知のメンバーを拒否するかもしれません。JWK を `SubtleCrypto.importKey` または同等の厳格なコンシューマーに渡すとき、JWK オブジェクトから `adcp_use` を除去しますが、ステップ 8 のポリシーチェックのために保持します。フィールドは暗号ライブラリではなく AdCP 検証者ポリシー向けです。 **署名済みリクエストの JWKS ディスカバリー** — インカミング署名の `keyid` が与えられたとき: 1. 検証者は署名エージェントの URL をその brand.json の `agents[]` エントリに解決します。ディスカバリーは以前のオンボーディングから来てもよく(MAY)、レジストリキャッシュから来てもよい(MAY)が、正準のワイヤー上ブートストラップはエージェントの `get_adcp_capabilities` レスポンスの `identity.brand_json_url` フィールドです — [Discovering an agent's signing keys via `brand_json_url`](#discovering-an-agents-signing-keys-via-brand_json_url) を参照。 2. エージェントの `jwks_uri`(またはエージェントの `url` のオリジンの `/.well-known/jwks.json` にデフォルト)を [Webhook URL validation](#webhook-url-validation-ssrf) に従う SSRF 検証付きでフェッチ。JWKS キャッシュ TTL は失効リストポーリング間隔で上限が制限される。 3. `kid` がキャッシュされた JWKS に不在の場合、JWKS を**即座に**再フェッチ(ステップ 2 の最初のフェッチはキャッシュされていたかもしれない)。同じ `jwks_uri` について過去 30 秒に再フェッチがすでに実行された場合、クールダウンが適用される: 検証者は再度再フェッチしてはならず(MUST NOT)、`request_signature_key_unknown` で拒否しなければならない(MUST)。クールダウンは再フェッチ間であり、最初のフェッチ前ではない。 検証者は、特定の `agents[]` エントリに解決できない `keyid` からの署名を受け入れてはなりません(MUST NOT)— 匿名署名はアカウンタビリティを提供しません。 #### `brand_json_url` を介したエージェントの署名鍵の発見 `get_adcp_capabilities` の `identity.brand_json_url` フィールド(3.x で追加、スキーマ `static/schemas/source/protocol/get-adcp-capabilities-response.json` を参照)は、エージェント → オペレーター → 鍵チェーンのワイヤー上ブートストラップです。フィールド名は、オペレーター構造が単一ブランド、サブブランドを持つハウス、エージェンシー、純粋なオペレーターレコードのいずれかに関わらず、それが指すアーティファクト(オペレーターの `brand.json` ファイル)を反映します。エージェント URL `A` だけが与えられたとき、検証者はエージェントの署名鍵を次で解決します: 1. `A` の `get_adcp_capabilities` レスポンスを [Webhook URL validation](#webhook-url-validation-ssrf) に従う SSRF 検証付きでフェッチ(HTTPS のみ — URL `A` は呼び出し元が供給し、Webhook コールバックに使う同じアドレスファミリー + プライベート IP フィルタリングを通らなければならない)。到達不能/タイムアウトでは `request_signature_capabilities_unreachable` で拒否。 2. `identity.brand_json_url` を読む。不在でリクエストが署名されている場合、`request_signature_brand_json_url_missing` で拒否。値が非 HTTPS の場合も同じコードで拒否(スキーマは `^https://` を強制するが、検証者はチェックを再表明しなければならない。不正な値を許容する 3.x パーサーは続行してはならない、MUST NOT)。required-when ルール: `identity.brand_json_url` は、エージェントが `request_signing.supported_for`/`required_for` を非空、`webhook_signing.supported === true`、または `identity.key_origins` の下の任意のフィールドを宣言するとき存在しなければならない(MUST)。これは 3.x でストーリーボード強制。4.0 ではレスポンスが 4.x リリースを含む `supported_versions` を宣言するときスキーマ必須になる。クロスバージョン検証者(4.x サポートを宣伝しない 3.x エージェントと通信する 4.0)は不在の `identity.brand_json_url` を受け入れ続けなければならない(MUST)。 3. **オリジンバインディング。** エージェント URL `A` のホスト eTLD+1 は `brand_json_url` のホスト eTLD+1 と等しくなければならない(MUST)。eTLD+1 計算はピン留めされた日付付き [Public Suffix List](https://publicsuffix.org/list/public_suffix_list.dat) スナップショットを使わなければならない(MUST、`vercel.app`, `pages.dev`, `github.io` のようなプラットフォームがサフィックスとして扱われるよう ICANN+PRIVATE セクション両方をスコープ)。異なる PSL バージョンを実行する 2 つの検証者は互いに非コンフォーマント。eTLD+1 が不一致の場合、brand.json をフェッチして `authorized_operators[]` が `A` の eTLD+1 をリストすることを確認。どちらも成立しなければ `request_signature_brand_origin_mismatch` で拒否。これは、攻撃者が `attacker.example/mcp` にエージェントを立て、その `brand_json_url` を、たまたま正当に `attacker.example/mcp` をリストする無関係なオペレーターの brand.json(例: SaaS マルチテナントデプロイ)に向ける共有テナントスプーフィングベクトルを閉じる。 4. `brand_json_url` の brand.json を [Webhook URL validation](#webhook-url-validation-ssrf) に従う SSRF 検証付きでフェッチ。検証者はこのフェッチでリダイレクトに従ってはならない(MUST NOT、このプロファイルの他所で文書化された `authoritative_location` の単一リダイレクト切り出しはそのフィールドにスコープされ、brand.json ブートストラップに継承されてはならない)。推奨予算: 接続 5 秒、総デッドライン 10 秒、ボディ上限 256 KiB。成功フェッチのキャッシュ TTL は JWKS 失効ポーリング間隔で上限が制限されなければならない(MUST、鍵ローテーションが古い brand.json でマスクされないように)。負のレスポンス(404、ネットワーク失敗)は 60 秒以上キャッシュされてはならない(MUST NOT)— 誤設定を修正するオペレーターが完全な失効サイクルの間ロックアウトされてはならない。 5. `url` が `A` と**バイト等価**な `agents[]` エントリを見つける(このステップで正準化なし — ガバナンス JWS の `iss`-to-brand.json 一致と同じルール、[Buyer identity resolution](#buyer-identity-resolution) を参照。最も一般的な失敗モードは末尾スラッシュまたはスキーム不一致、例: `https://x.com/mcp` ≠ `https://x.com/mcp/`)。一致しなければ `request_signature_agent_not_in_brand_json` で拒否。複数一致する場合(オペレーター誤設定 — brand.json スキーマは現在 `agents[]` を URL 一意に制約しない)、`request_signature_brand_json_ambiguous` で拒否。 6. JWKS ソースを**署名面 AND 役割**(`adcp_use` だけでなく送信者 vs 受信者の位置)で解決: * **セルサイド Webhook 配信のみ** — すなわち、セラーがメディアバイ配信についてバイヤーへのアウトバウンド Webhook に署名: パブリッシャーの `adagents.json signing_keys` ピン(存在する場合)は上記のパブリッシャーピン優先順位ルールに従って権威的で、下記のすべてを上書きする。ピンは(エージェント、Webhook 配信面、セルサイド役割)にスコープされる — オペレーター側 Webhook 配信(例: オペレーターステータスコールバックを受け取るバイヤーホストの Webhook)を上書きせず、別個の `adcp_use: "webhook-signing"` 鍵目的を示唆しない。 * **他のすべての(面、役割)タプル** — リクエスト署名(任意の方向)、オペレーター側 Webhook 配信、ガバナンス署名、TMP 署名: 一致した `agents[]` エントリの `jwks_uri` を使い、不在時は `A` のオリジンの `/.well-known/jwks.json` にデフォルト。 7. **`identity.key_origins` 一貫性チェック(署名時は必須)。** ケイパビリティレスポンスの `identity.key_origins` の下で宣言され、**ステップ 6 での JWKS ソースがオペレーター brand.json だった**(すなわち、パブリッシャー `adagents.json signing_keys` ピンでない)すべての面/目的について、解決された `jwks_uri` のホストはその面/目的について宣言されたオリジンと等しくなければならない(MUST)。任意の面/目的での不一致 → `{ purpose, expected_origin, actual_origin }` を運ぶ `request_signature_key_origin_mismatch` で拒否。ソースがパブリッシャーピンだった特定の(エージェント、面/役割)タプルについて**のみ**チェックをスキップ — 同じ面のオペレーター側使用は依然チェック。エージェントが対応する `identity.key_origins.{purpose}` エントリなしに署名を宣言する場合、`{ purpose, posture }` を運ぶ `request_signature_key_origin_missing` で拒否。 8. JWKS をフェッチ、`kid` を見つけ、既存の RFC 9421 プロファイル([verifier checklist](#verifier-checklist-requests) のステップ 7 以降)に従って検証。 **トラストルート。** brand.json はオペレーター証明(「このエージェントは私のもの、これがその鍵」)。`adagents.json` はパブリッシャー証明(「このエージェントは私のインベントリを販売してよい。オプションで、これがそのピン留め `signing_keys`」)。セルサイド Webhook 署名では、パブリッシャーピンが権威的(パブリッシャー > オペレーター)。リクエスト署名とオペレーター側 Webhook 署名では、オペレーター brand.json の `jwks_uri` が権威的。エージェントは自身の鍵を決して自己証明しない — `jwks_uri` フィールドは意図的にケイパビリティレスポンスに運ばれない。オペレーターは brand.json 経由で帯域外に鍵を公開する。 **`sponsored_intelligence.brand_url` は別物。** SI エージェントはレンダリング目的(色、フォント、ロゴ、トーン)で `sponsored_intelligence` の下に `brand_url` フィールドを運んでもよい — フィールドが `brand_url` と名付けられているのは、SI コンテキストではそれが本当に「広告されているブランド」だからだ。そのフィールドはレンダリングポインターであり、トラストルートポインターではない。SI エージェントは `sponsored_intelligence.brand_url` を `identity.brand_json_url` と異なる URL に設定してもよい(MAY、例: レンダリング用のサブブランド brand.json、鍵についてはオペレーターの brand.json を依然信頼)。**検証者は鍵ディスカバリーに `identity.brand_json_url` を使わなければならず(MUST)、`identity.brand_json_url` が不在でも `sponsored_intelligence.brand_url` をトラストルートポインターとして使ってはならない(MUST NOT)。** SI レンダリングメタデータを消費する検証者は `sponsored_intelligence.brand_url` を読んでもよい(MAY)。同じ検証者は任意の署名検証フローで `identity.brand_json_url` に切り替えなければならない(MUST)。命名の区別は意図的: 「広告されているブランド」コンテキストには `brand_url`、「オペレーターマスターレコード」コンテキストには `brand_json_url`。 **このディスカバリーチェーンの拒否コード(3.x)。** 相手方ドキュメント(`brand_json_url`, `matched_entries[]`)由来の詳細フィールドは、検証者エラーを表示する管理 UI でレンダリングする前に HTML エスケープしなければならない(MUST)— 構造化形状は検証者制御でも、攻撃者が影響を与えられる文字列。 | Code | When | Detail fields | Remediation | | -------------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `request_signature_brand_json_url_missing` | ケイパビリティが `identity.brand_json_url` を運ばず署名済みリクエストを受信、または非 HTTPS 値を運んだ | `agent_url` | オペレーター: `identity.brand_json_url` をオペレーター brand.json の HTTPS URL(通常 `https://{your-domain}/.well-known/brand.json`)に設定。検証者: オペレーションに表面化。リトライしない。 | | `request_signature_capabilities_unreachable` | ケイパビリティフェッチ失敗(DNS、TCP、TLS、タイムアウト、非 2xx) | `agent_url`, `http_status`, `dns_error`, `last_attempt_at` | 検証者は 1-5 秒のジッター付きバックオフ後 1 回リトライしてよい、その後諦める。60 秒以上ネガティブキャッシュしない。一時的として表面化。 | | `request_signature_brand_json_unreachable` | brand.json フェッチ失敗(同条件) | `brand_json_url`, `http_status`, `dns_error`, `last_attempt_at` | `_capabilities_unreachable` と同じリトライ/キャッシュ規律。 | | `request_signature_brand_json_malformed` | brand.json が strict-parse 失敗(重複キー、ボディ上限超過、非 JSON コンテンツ) | `brand_json_url`, `parse_error` | オペレーター: 重複オブジェクトキーなし、256 KiB ボディ上限内の strict-JSON brand.json を提供。検証者: リトライしない。オペレーションに表面化。 | | `request_signature_brand_origin_mismatch` | エージェント eTLD+1 ≠ `brand_json_url` eTLD+1 かつ `authorized_operators[]` が委任しない | `agent_url`, `agent_etld1`, `brand_json_url_etld1` | オペレーター: エージェントをブランド eTLD+1 に移すか、エージェント eTLD+1 を brand.json `authorized_operators[]` に追加。リトライ不可。 | | `request_signature_agent_not_in_brand_json` | エージェント URL が解決された brand.json の任意の `agents[].url` とバイト等価でない | `agent_url`, `brand_json_url` | オペレーター: エージェント URL を `agents[].url` にバイト等価で追加。一般的原因: 末尾スラッシュ、スキーム不一致、IDN/punycode 正規化。リトライ不可。 | | `request_signature_brand_json_ambiguous` | 複数の `agents[]` エントリがエージェント URL に一致 | `agent_url`, `brand_json_url`, `matched_count`, `matched_entries[]` | オペレーター: URL で `agents[]` エントリを重複排除。リトライ不可。 | | `request_signature_key_origin_mismatch` | 解決された `jwks_uri` ホスト ≠ 宣言された `identity.key_origins.{purpose}` | `purpose`, `expected_origin`, `actual_origin` | オペレーター: `identity.key_origins.{purpose}` を解決された `jwks_uri` のホストに合わせる。リトライ不可。 | | `request_signature_key_origin_missing` | 署名姿勢を宣言したが `identity.key_origins.{purpose}` が不在 | `purpose`, `posture` | オペレーター: `identity.key_origins.{purpose}` 宣言をケイパビリティに追加。リトライ不可。 | **AdCP 3.0 にピン留めしたまま `brand_json_url` を採用。** フィールドは 3.x の次のマイナーに厳密に追加的なスキーマ変更として着地します。AdCP はパッチリリース(3.0.x)で新しいフィールドを出荷しないので、正式なバックポートは検討対象外です。しかしバージョンバンプを待たずに使い始められます。ワイヤー形状は前方互換です: * 3.0 コンフォーマントな**セラー**は今日 `get_adcp_capabilities` レスポンスに `identity.brand_json_url` を設定してもよい(MAY)。フィールドを無視する 3.0 検証者は動き続け、3.x 検証者は自動的にそれを拾う。調整もバージョンバンプも不要。 * 3.0 コンフォーマントな**検証者**は、フィールドを日和見的に読み(`caps.identity?.brand_json_url` 経由)、存在するとき 8 ステップチェーンを実行し、不在時は既存の帯域外エージェント → オペレーターマッピングにフォールバックしてもよい(MAY)。チェーン自体は HTTPS フェッチと JSON パースだけ — その中に 3.x SDK を要するものはない。 これは今日署名検証を構築する [Scope3](https://github.com/scope3) のようなセラーの推奨パスです: ケイパビリティレスポンスにフィールドを出荷し、相手方にチェーンを文書化し、3.x ロールアウトを受動的に起こさせます。 ##### クイックスタート: `brand_json_url` ベースの検証者を実装 上記の [request-signing quickstart](#quickstart-opt-into-request-signing-in-30) をミラーします。エージェントごとに一度実行 — 結果の `agents[]` エントリ、`jwks_uri`、JWKS はステップ 4 の TTL ルールに従ってキャッシュされます。 1. 署名エージェントの URL `A` の**ケイパビリティをフェッチ**。これは**プロトコルレベル**呼び出し — `A` に対する生の HTTP `GET` ではなく、エージェントの宣言されたトランスポート(MCP `tools/call` または A2A スキル呼び出し)を介して `get_adcp_capabilities` を呼び出す。エージェント URL は JSON ケイパビリティドキュメントではなくプロトコルエンドポイント。[Webhook URL validation](#webhook-url-validation-ssrf) に従う SSRF セーフトランスポートを使う: HTTPS のみ、アドレスファミリー + プライベート IP フィルタリング、リダイレクトなし、予算 `{ connect: 5000, total: 10000, body: MAX_CAPABILITIES_BYTES, maxRedirects: 0 }`。 2. **`identity.brand_json_url` を読む。** 不在(かつリクエストが署名済み)または非 HTTPS なら `request_signature_brand_json_url_missing` で拒否。 3. **eTLD+1 オリジンバインディング。** ピン留め PSL スナップショットを使って `eTLD+1(A)` と `eTLD+1(brand_json_url)` を計算。ベンダー済みの日付付きスナップショットで [`tldts`](https://www.npmjs.com/package/tldts)(TS)、[`publicsuffixlist`](https://pypi.org/project/publicsuffixlist/)(Python)、または [`golang.org/x/net/publicsuffix`](https://pkg.go.dev/golang.org/x/net/publicsuffix)(Go)を使う。ランタイムで PSL をフェッチしない — ランタイムフェッチは DoS オラクルとデプロイ間で非決定論的な eTLD+1 を作る。一致すれば続行。そうでなければ `brand.json` をフェッチして `authorized_operators[]` を確認 — `eTLD+1(A)` が委任されていれば続行。そうでなければ `request_signature_brand_origin_mismatch` で拒否。このアルゴリズム全体のオリジン比較は両側を正準化しなければならない(MUST): ホストを ASCII 小文字化し、バイト等価前に IDNA-2008 A-label 形式(Punycode)に変換。非正準比較(例: 生の `Example.COM` vs `example.com`、または U-label vs A-label)は正当なトラフィックを黙って拒否する。 4. 同じ SSRF ルール + リダイレクトなし、ボディ上限 `MAX_BRAND_JSON_BYTES`、接続 5 秒、総 10 秒で **`brand.json` をフェッチ**。重複キーを拒否する strict JSON パーサー(例: TS の [`secure-json-parse`](https://www.npmjs.com/package/secure-json-parse)、重複で raise する `object_pairs_hook` 付きの Python stdlib `json.JSONDecoder`、Go の重複キーチェックと組み合わせた [`encoding/json`](https://pkg.go.dev/encoding/json) `Decoder.DisallowUnknownFields`)でパース — 重複キーはステップ 14 がリクエスト面で閉じるパーサー差分ベクトルで、同じトラストルートドキュメントが検証者間で 2 つの異なる形状にパースされてはならない(MUST NOT)。重複キー検出で `request_signature_brand_json_malformed` で拒否。成功レスポンスを JWKS 失効ポーリング間隔まで(それより長くない)キャッシュ。失敗は最大 60 秒キャッシュ。 5. `url` が `A` とバイト等価な **`agents[]` エントリを見つける**(正準化なし)。ミスで `request_signature_agent_not_in_brand_json`、複数一致で `request_signature_brand_json_ambiguous` を拒否。 6. 一致したエントリから **`jwks_uri` を解決** — セルサイド Webhook 配信のみ、オペレーターの `jwks_uri` よりパブリッシャーの `adagents.json signing_keys` ピン(存在する場合)を優先。他のすべての(面、役割)タプルは、一致したエントリの `jwks_uri`(デフォルト: `A` のオリジンの `/.well-known/jwks.json`)を使う。 7. **一貫性チェック。** ケイパビリティ `identity.key_origins` の下で宣言されたすべての面/目的について、解決された `jwks_uri` ホストと宣言オリジンの両方に `canonicalizeOrigin()`(ASCII 小文字 + IDNA-2008 A-label)を適用し、バイト比較(パブリッシャーピン由来の特定の(エージェント、面/役割)タプルのみスキップ)。適宜 `request_signature_key_origin_mismatch` / `_missing` を拒否。 8. **[verifier checklist](#verifier-checklist-requests) のステップ 8 以降にハンドオフ** — JWKS をフェッチ(同じバイト予算 `MAX_JWKS_BYTES` と 5/10 秒の接続/総デッドライン)、`kid` を見つけ(ここでステップ 7 のプリアンブルですでに解決済み — 検証者チェックリストのステップ 7 はディスカバリープリアンブル自体)、RFC 9421 に従って検証。 疑似コード(TypeScript 風。下記の SDK ヘルパーはこれを単一呼び出しに折りたたむ): ```ts theme={null} const MAX_CAPABILITIES_BYTES = 65_536; const MAX_BRAND_JSON_BYTES = 262_144; const MAX_JWKS_BYTES = 65_536; const FETCH_BUDGETS = { connect: 5_000, total: 10_000, maxRedirects: 0 }; function canonicalizeOrigin(hostOrUrl: string): string { const host = hostOrUrl.includes('://') ? new URL(hostOrUrl).hostname : hostOrUrl; return toAsciiIdna2008(host.toLowerCase()); // A-label form } async function resolveAgent(agentUrl: string): Promise { const caps = await getAdcpCapabilities(agentUrl, { // step 1: protocol-level call ...FETCH_BUDGETS, body: MAX_CAPABILITIES_BYTES, ssrf: true, }); const brandJsonUrl = caps.identity?.brand_json_url; if (!brandJsonUrl?.startsWith('https://')) throw new Err('brand_json_url_missing'); // step 2 const agentEtld1 = etldPlusOne(new URL(agentUrl).hostname, PINNED_PSL_SNAPSHOT); // step 3 const brandEtld1 = etldPlusOne(new URL(brandJsonUrl).hostname, PINNED_PSL_SNAPSHOT); const brandJson = await safeFetch(brandJsonUrl, { // step 4 ...FETCH_BUDGETS, body: MAX_BRAND_JSON_BYTES, ssrf: true, parse: 'strict-json', }); if (agentEtld1 !== brandEtld1 && !brandJson.authorized_operators?.some(o => o.domain === agentEtld1)) { throw new Err('brand_origin_mismatch'); } const entries = brandJson.agents.filter(e => e.url === agentUrl); // step 5 (byte-equal) if (entries.length === 0) throw new Err('agent_not_in_brand_json'); if (entries.length > 1) throw new Err('brand_json_ambiguous'); const entry = entries[0]; const jwksUri = entry.jwks_uri ?? `${origin(agentUrl)}/.well-known/jwks.json`; // step 6 for (const [purpose, declared] of Object.entries(caps.identity?.key_origins ?? {})) { // step 7 if (canonicalizeOrigin(jwksUri) !== canonicalizeOrigin(declared)) { throw new Err('key_origin_mismatch', { purpose }); } } const jwks = await safeFetch(jwksUri, { // step 8 setup ...FETCH_BUDGETS, body: MAX_JWKS_BYTES, ssrf: true, parse: 'strict-json', }); return { agentUrl, brandJsonUrl, agentEntry: entry, jwksUri, jwks, /* trace, freshness */ }; } ``` 公開されたら [`/compliance/latest/test-vectors/brand-discovery/`](https://adcontextprotocol.org/compliance/latest/test-vectors/brand-discovery/) の brand-discovery テストベクターに対してエンドツーエンド検証。それまで、`/compliance/latest/universal/capabilities-brand-url-discovery/` のストーリーボードがフィクスチャ brand.json + JWKS に対して検証者アルゴリズムを行使し、各エラーパスに正しい `request_signature_*` コードをアサートします。 ##### リファレンス実装 8 ステップアルゴリズムは 3 つの SDK で出荷されます — ランタイムに合うものを選びます。3 つとも同じ論理レコードを返します: エージェント URL、解決された brand.json URL、一致した `agents[]` エントリ、JWKS URI、JWKS 自体、ケイパビリティレスポンスの `identity_posture` ブロック、ステップ 7 の `key_origins` チェックからの `consistency` フラグ、`freshness` タイムスタンプセット、ステップごとの `trace`。 * **TypeScript**([`@adcp/sdk`](https://github.com/adcontextprotocol/adcp-client)): `resolveAgent(url)` は `{ agentUrl, brandJsonUrl, agentEntry, jwksUri, jwks, identityPosture, consistency, freshness, trace }` を返す。`getAgentJwks(url)` は JWKS のみの高速パス。`createAgentJwksSet(url, opts)` は `jose` の `jwtVerify` に渡す `JWTVerifyGetKey` を返す。 * **Python**([`adcp`](https://github.com/adcontextprotocol/adcp-client-python)): `resolve_agent(url)` は `agent_url`, `brand_json_url`, `agent_entry`, `jwks_uri`, `jwks`, `identity_posture`, `consistency`, `freshness`, `trace` フィールドを持つ `AgentResolution` データクラスを返す。`verify_request_signature(request, *, agent_url, allowed_algs)` はディスカバリーチェーンと [verifier checklist](#verifier-checklist-requests) を 1 呼び出しで実行するワンショットヘルパー。 * **Go**([`adcp-go`](https://github.com/adcontextprotocol/adcp-go)): `ResolveAgent(ctx, agentURL) (*AgentResolution, error)` は `AgentURL`, `BrandJSONURL`, `AgentEntry`, `JWKSUri`, `JWKS`, `IdentityPosture`, `Consistency`, `Freshness`, `Trace` フィールドを持つ構造体を返す。`VerifyRequestSignature(ctx, req, opts) (*VerifiedIdentity, error)` は TS/Python のワンショットをミラー。 各 SDK は開発ループデバッグ用の CLI を出荷します — `npx @adcp/sdk@latest resolve `、`adcp resolve `(`python -m adcp resolve ` も)、`adcp resolve `(Go バイナリ、Python と同名 — `$PATH` またはベンダーで区別)— ステップごとの `fetched_at`/`age_seconds`/`ok` 付きのトレースを表示し、`request_signature_brand_*` 失敗をトリアージするオペレーターがどのステップが拒否したかとその理由を正確に見られます。Python(`[project.scripts]` console\_scripts エントリ)と Go(バイナリ `adcp`、Go モジュールパス `github.com/adcontextprotocol/adcp-go` とは別)のツールチェーンは両方ともトップレベル `adcp` コマンドをインストールするので、単一のマッスルメモリ呼び出しがランタイムをまたいで機能します。 #### エージェントアイデンティティ 有効な署名はちょうど 1 つの事実を確立します: **リクエストは `jwks_uri` が `keyid` を含むエージェントが発行した。** 検証者は、どのオペレーターかだけでなく、どの特定のエージェントが署名したかを学びます。エージェントを含む brand.json(検証者の既存のエージェントマッピングを介して発見)が、どのオペレーターがそのエージェントを運用するかを検証者に伝えます。 **`agent_url` の導出。** 検証者のリクエストコンテキスト上の正準バイヤーエージェント識別子は、[verifier checklist](#verifier-checklist-requests) のステップ 7 で `keyid` を解決した `jwks_uri` を持つ `agents[]` エントリの `url` フィールドです。`agent_url` は JWK クレーム、JWS クレーム、署名済みエンベロープフィールドでは**ありません** — 検証者が JWKS をフェッチするためにすでに使った公開座標です。これは、検証者が完全に制御した入力(オンボーディングで確立されたエージェントマッピング、加えて今フェッチした JWKS)から導出を決定論的にし、署名者がリクエストに署名した鍵のものとは異なる `agent_url` を主張するワイヤーアフォーダンスを取り除きます。解決済み署名者オブジェクトをアダプターに表面化する SDK は、`agent_url` をこの導出からソースしなければならず(MUST)、エンベロープ上のバイヤー主張 `agent_url` フィールドを受け入れて暗号学的に確立されたものとして扱ってはなりません(MUST NOT)。(`creative.verify_agent.agent_url` や `governance.accepted_verifiers[].agent_url` のようなバイヤー主張の*検証者*参照は別の構造 — それらは公開された許可リストの下でセラーが呼び出すエージェントを名指し、インバウンドリクエストの署名者ではなく、許可されたまま。) 認可 — このオペレーターがリクエストボディで名指しされたブランドのために行動を許可されるか — は、ターゲットハウスの brand.json の `authorized_operator[]` エントリが規定する別個のプロトコルレベルチェックです。リクエストが署名されているかに関わらず発生し、このプロファイルのスコープ外です。検証者は両方のチェックを実行しなければなりません(MUST)。このセクションは最初のもののみを規定します。 検証者はリクエストボディフィールドから署名者アイデンティティを導出してはなりません(MUST NOT)。署名 → JWKS → エージェントエントリチェーンが署名済みトランスポート上の唯一の権威的アイデンティティパスです。bearer / API キー / OAuth トランスポートでは、エージェントアイデンティティはセラーのオンボーディングレコードのクレデンシャル-トゥ-エージェントマッピングから来ます — そのマッピングが唯一の正当なアイデンティティソースです。セラーはアイデンティティ解決への代替入力としてエンベロープ側の `buyer_agent_url`(または同等の自己主張呼び出し元アイデンティティフィールド)を導入してはなりません(MUST NOT): ワイヤーアフォーダンスは、相殺チェックなしに、呼び出し元がクレデンシャルマップが主張しないアイデンティティを主張することを許します。 brand.json ディスカバリーは 1 リダイレクト(`authoritative_location`)に従って停止します。 #### 検証者チェックリスト(リクエスト) **チェックリストを適用する前に、検証者はオペレーションが署名を要求するかを判定しなければなりません(MUST):** * オペレーションが検証者の `required_for` ケイパビリティにあり、AND `Signature-Input` ヘッダーが存在せず、AND 呼び出し元がこのオペレーションについて検証者が受け入れる他のクレデンシャル(bearer、API キー、mTLS)を提示しない場合、`request_signature_required` で拒否。このブランチに入る未署名リクエストは決してチェックリストに入りません。未署名だが他の方法で認証された呼び出し元を規定するルールは [Composition with fallback authenticators](#composition-with-fallback-authenticators) を参照。 * `Signature` または `Signature-Input` のどちらかが他方なしに存在する場合、`request_signature_header_malformed` で拒否。2 つのヘッダーはバインドされたペアです。一方が他方なしは不正で、「推測できる欠けた部分で署名された」ではありません。このルールは、プロキシが `Signature-Input` を除去し `Signature` を残すダウングレードベクトルを閉じます。 * `Signature-Input` ヘッダーが存在するが不正な場合、`request_signature_header_malformed` で拒否。検証者は、不正な署名が存在するとき、**`required_for` にないオペレーションでも**、bearer のみの認証にフォールバックしてはなりません(MUST NOT)— 存在するが壊れた署名は署名者の意図を示します。黙ったフォールバックはダウングレード攻撃を可能にします。 そうでなければ、検証者はこれら 15 のチェック(14 の番号付きステップとサブステップ 9a)を順に適用し、最初の失敗でショートサーキットしなければなりません(MUST)。ステップ 14 は 14a(strict-parse 要件)と 14b(ロギング規律)に分解されます — 両方ともステップ 14 が実行されるときに適用され、1 つのチェックの詳述であり、カウント上別個のチェックではありません。このチェックリストはエージェントアイデンティティのみを確立します — ブランド-オペレーター認可はターゲットハウスの brand.json が規定する別個の後続チェックです。 1. `Signature-Input` と `Signature` ヘッダーを RFC 9421 §4 に従ってパース。不正なら拒否。 2. `created`, `expires`, `nonce`, `keyid`, `alg`, `tag` のいずれかが `Signature-Input` パラメーターから不在なら拒否(`request_signature_params_incomplete`)。 3. `tag` が正確に `adcp/request-signing/v1` でないなら拒否(`request_signature_tag_invalid`)。 4. `alg` が許可リスト(`ed25519`, `ecdsa-p256-sha256`)にないなら拒否。ライブラリのデフォルトに頼ってはならない(`request_signature_alg_not_allowed`)。 5. `expires ≤ created`、`created > now + 60 s`、`expires < now − 60 s`、または `expires − created > 300 s` なら拒否(`request_signature_window_invalid`)。 6. カバーされるコンポーネントが `@method`, `@target-uri`, `@authority` のすべてを含まないなら拒否(`request_signature_components_incomplete`)。ボディが存在する場合、`content-type` がカバーされていないなら拒否。検証者の `covers_content_digest` ケイパビリティが `"required"` なら、`content-digest` がカバーされていないなら拒否。検証者の `covers_content_digest` ケイパビリティが `"forbidden"` かつ `content-digest` がカバーされて*いる*なら、`request_signature_components_unexpected` で拒否。 7. `keyid` を [Agent key publication](#agent-key-publication) を介して JWK に解決。検証者が署名エージェントのキャッシュされたエージェント → JWKS マッピングを持たない場合、このステップの前に [Discovering an agent's signing keys via `brand_json_url`](#discovering-an-agents-signing-keys-via-brand_json_url) を実行 — その 8 ステッププリアンブル(ケイパビリティ → `identity.brand_json_url` → brand.json → agents\[] → jwks\_uri)は `keyid` 解決の前提条件で、そのセクションの `request_signature_brand_*` と `request_signature_key_origin_*` コードでショートサーキット。確立されたマッピング内の `kid` ミスでは、`request_signature_key_unknown` で拒否する前に 1 回再フェッチ(再フェッチ間の 30 秒クールダウンに従う)。`keyid` が特定の `agents[]` エントリに解決できないなら拒否。 8. JWK の `use` が `"sig"`、`key_ops` が `"verify"` を含み、`adcp_use` が `"request-signing"` に等しいことを検証。任意の不一致(不在の `adcp_use` を含む。非コンフォーマントとして扱わなければならない)で拒否(`request_signature_key_purpose_invalid`)。 9. [Transport revocation](#transport-revocation) リストを確認。`keyid` ∈ `revoked_kids` なら拒否(`request_signature_key_revoked`)。検証者が grace 内に失効リストをリフレッシュしていないなら `request_signature_revocation_stale` で拒否。 **9a. keyid ごとの上限チェック。** [keyid ごとのリプレイキャッシュ上限](#transport-replay-dedup)を確認。この `keyid` について上限に達しているなら `request_signature_rate_abuse` で拒否。暗号検証(ステップ 10)の前に実行 — ステップ 9 と同じ根拠: 上限を枯渇させる侵害されたまたは誤設定された署名者が、増幅された Ed25519/ECDSA 作業を検証者に強制してはならない(MUST NOT)。`keyid` 解決(ステップ 7)の*後*に実行し、上限状態オラクルが検証者がすでに認識をコミットした鍵についてのみ応答するように — 9a を早く実行すると、攻撃者が JWKS に公開されていない keyid を含む全 keyid 空間で検証者内部のレート制限状態を探れる。 10. [上記のプロファイル](#adcp-rfc-9421-profile)に従い `@target-uri` 正準化 AND `@authority` 導出を適用した後、カバーされるコンポーネントを使って RFC 9421 §2.5 に従い正準署名ベースを計算。**`@authority` ルールは要:** 検証者は、存在する場合 HTTP/2+ の `:authority` 疑似ヘッダーから、そうでなければ受信した HTTP/1.1 `Host` ヘッダーから `@authority` を導出しなければならない(MUST)— リバースプロキシルーティング状態、ロードバランサーメタデータ、フォワードプロキシが転送中に書き換えた任意の `Host` 値からではない。受信リクエストに `:authority` と `Host` の両方が存在する場合、正準化後にバイト等価でなければならない(RFC 7540 §8.1.2.3 等価性)。発散は `request_target_uri_malformed` で拒否。正準化された `@authority` は正準 `@target-uri` のオーソリティコンポーネントとバイト単位で一致しなければならない(MUST)。ミスマッチは `request_target_uri_malformed` で拒否。署名済み `@target-uri` に対するそのバイト一致 — ソースヘッダーの選択ではなく — が唯一の安全ゲートです。`Host` 自体が転送中に書き換えられ得るからです。このチェックリストだけから — プロファイルの正準化セクションを相互参照せずに — 構築する実装者はこのルールを適用しなければならない(MUST)。それをスキップするとクロス vhost リプレイベクトル(攻撃者が TLS 終端されたリクエストを傍受し、同じ検証者プール上の 2 つ目の vhost にリプレイ: 同じ証明書 SAN、異なる `Host`)を黙って受け入れる。正準化完了後、JWK に対して署名を検証(失敗で `request_signature_invalid`)。 11. `content-digest` がカバーされている場合、受信したボディバイトからダイジェストを再計算して比較(ミスマッチで `request_signature_digest_mismatch`)。 12. リプレイキャッシュに対してノンスを確認([Transport replay dedup](#transport-replay-dedup) を参照)。`(keyid, nonce)` がリプレイキャッシュ TTL 内で見られている場合拒否(`request_signature_replayed`)。 13. **ステップ 1-9、9a、10-12 がすべて通過した後にのみ**、`(keyid, nonce)` を TTL = `(expires − now) + 60 s`(+60 秒はステップ 5 で適用したスキュー許容に一致)でリプレイキャッシュに挿入。この挿入はステップ 14 のボディ整形式チェックの前に発生しなければならない(MUST)。不正なボディ上に有効な署名を運ぶキャプチャされたフレームが、各リトライで暗号検証 CPU を燃やすためにリプレイできないように — ノンスは、ボディ形状に関わらず、暗号学的に有効なフレームの最初の目撃で燃やされる。 14. **ボディ整形式性。** 検証者は重複オブジェクトキーを含むボディを拒否しなければならない(MUST、`request_body_malformed`)。RFC 8259 §4 に従い、重複キーパース動作は予測不能 — 署名はワイヤー上のバイトに対して有効だが、2 つのパーサーがパースされた値について一致しないことがあり、これはパーサー差分攻撃クラス(cf. CVE-2017-12635)。このチェックは、署名検証者のペイロードのビューとダウンストリームコンシューマーのビューの間のギャップを閉じる。リクエストボディは、パーサー差分の影響範囲が Webhook のステータスフリップの影響範囲より大きい状態変更・支出コミットペイロード(`create_media_buy`, `update_media_buy_delivery` など)を運び、このチェックを少なくとも Webhook 面と同じくらい要にする。`request_body_malformed` は `request_signature_digest_mismatch` とは別: 署名は有効。ボディが曖昧な状態にパースされる。構造化 `request_body_malformed` エラーを返すのではなくクラッシュする検証者はコンフォーマントだが最適でない — 送信者は actionable なエラーコードを受け取らない。**Idempotency\_key カバレッジはこのチェックから従う**: ステップ 14 はスキーマ検証と冪等性キャッシュルックアップ([idempotency](#idempotency) を参照)の前に実行されるので、`idempotency_key` 自体が重複する(異なるパーサーが異なるキーを見る)リクエストボディはここで拒否され、決してキャッシュに到達しない。別個の冪等性層監査は不要。 **14a. Strict-parse 要件。** チェックは重複キーを露出するパーサーを使わなければならない(MUST)— それらを黙って破棄する last-wins/first-wins のデフォルトはこの要件を満たさない。[Webhook 検証者チェックリストのステップ 14a](#webhook-callbacks)の言語ごとの strict-parse エスケープハッチ列挙がここに同一に適用される。 **14b. ロギング規律。** 検証者は `request_body_malformed` 拒否で完全なリクエストボディバイトをログすべきではない(SHOULD NOT)。`keyid`、ノンス、バイト長、特定の重複キー名のみをログ。[Webhook 検証者チェックリストのステップ 14b](#webhook-callbacks)のキー名サニタイズルール(最初の非印字文字で `` に切り詰め、32 バイト以下の最後の UTF-8 コードポイントに切り詰め、数を 4 で上限)がここに同一に適用される — 攻撃者制御バイトチャネルはリクエスト面で同じ形状を持つ。 14 のチェックすべてが通過した後にのみ、検証者はリクエストを暗号学的に認証されたものとして扱います。検証者はリクエストコンテキストに `verified_signer: { keyid, agent_url, verified_at }` を記録すべきです(SHOULD)。ダウンストリームコード — 後続のブランド-オペレーター認可チェックを含む — が署名済みエージェントアイデンティティでログ・監査できるように。 **暗号検証前の安価な拒否(ステップ 10 の前のステップ 9 と 9a)は意図的です。** 検証者が最初に暗号をチェックすると、失効鍵署名をリプレイする攻撃者 — または keyid ごとの上限が満杯の検証者をハンマーする署名者 — が、各拒否で Ed25519 または ECDSA 検証を強制し、安価な増幅になります。失効と keyid ごとの上限を前に移すことがその O(verify) → O(1) ギャップを閉じます。ステップ 9 の失効状態はすでに署名者のオリジンで外部公開されています。ステップ 9a の上限状態は検証者内部ですが、持続的な攻撃者によるトラフィックパターン分析で観測可能です。スペックは、上限観測が黙ったオラクルではなくインシデントシグナルとして表面化するよう、別個の `request_signature_rate_abuse` エラーコードを `SHOULD alert operators` 要件([Transport replay dedup](#transport-replay-dedup) を参照)と意図的にペアにします — 侵害鍵イベントは、それを引き起こした攻撃者にも読めても、オペレーターにとって大きくあるべきです。 **上限の要となる不変条件。** 秘密鍵なしの外部トラフィックは上限を増やせません: リプレイキャッシュ挿入はステップ 13、暗号検証(ステップ 10)の*後*、ボディ整形式性(ステップ 14)の*前*に発生するので、ステップ 10 で失敗する任意のリクエストは決して上限エントリを消費せず、ステップ 14 で失敗する任意のリクエストはすでにノンスを燃やしています — 不正なボディ上に有効な署名を運ぶキャプチャされたフレームは、増幅された暗号検証作業を強制するためにリプレイできません。これが 9a が上限状態の*リーダー*であって*ライター*でない理由です — 正当な鍵保持者(または鍵を侵害した者、上限が検出するために存在するケース)のみがセットを増やせます。チェックリストへの将来の編集は両方の順序を保持しなければならない(MUST): 挿入を早く移す(ステップ 10 の前)と、任意の外部当事者が偽造された構造的に有効な署名で上限をフラッドできる。挿入を遅く移す(ステップ 14 の後)と、不正ボディリプレイベクトルを再び開く。 ステップ 12 の `(keyid, nonce)` 重複排除は対照的に暗号検証の*後*に実行され、リプレイキャッシュが無効な署名で消費されないようにします。 #### フォールバック認証器との合成 `required_for` は署名要件を**呼び出し元のクレデンシャルパスに対して相対的に**規定し、絶対的ではありません。検証者は通常 1 つ以上の認証器(bearer、API キー、mTLS、9421)を受け入れ、`required_for` はその認証チェーン内の 1 つのレバーで、他を覆すオーバーライドではありません。 **下記ルールの用語:** *未認証*とは、呼び出し元が有効な署名も、このオペレーションについて検証者が受け入れる他のクレデンシャルも提示しないことを意味します。認識されない bearer トークンまたは API キー(検証者が受け入れないもの)は有効なクレデンシャルでは*ありません* — 呼び出し元は未認証で最初のルールに該当します。 規範ルール: * `required_for` オペレーションへの**未認証**リクエストは `request_signature_required` で拒否しなければなりません(MUST)。 * `required_for` オペレーションへの**未署名だが他の方法で認証された**リクエスト(有効な bearer、API キー、mTLS アイデンティティ。`Signature-Input` なし)は、署名欠落で拒否してはなりません(MUST NOT)。フォールバッククレデンシャルは検証者がその呼び出し元に十分と宣伝したもので、`required_for` は検証者自身の認証器設定を遡及的に無効化しません。 * **署名済み**リクエストは [verifier checklist](#verifier-checklist-requests) に入り、オペレーションが `required_for` にあるかに関わらず暗号学的メリットで評価されます。 * **不正な署名**は、チェックリストプリアンブルの不正署名ルールに従い、フォールバックをとにかくブロックします。壊れた署名は署名者の意図を示し、bearer に黙ってダウングレードしてはなりません(MUST NOT)。 `warn_for` はこのルールで変わりません: 未署名リクエストについてすでに非拒否で、ロールアウト中の署名済みだが無効な署名をモニタリングシグナルとして表面化し続けます。 **セラーの強制 — ケイパビリティ宣言に合う姿勢を選ぶ。** 3 つの強制姿勢が有効です。セラーは 1 つを選び、フォールバック認証器をそれに応じて設定しなければなりません(MUST)。`required_for` を宣伝しながらリストされたオペレーションで bearer 認証を開いたままにすることはセキュリティシアターです — 検証者が bearer を有効と宣伝し、呼び出し元はそれを使う権利があります。 * **Strict(このオペレーションで署名は無条件)。** セラーは、オペレーションで bearer/API キー/mTLS を完全に受け入れるのを止めるか、*または*、9421 オンボーディングを完了した相手方からの非署名リクエストを拒否するフォールバック認証器を呼び出し元ごとのフラグでゲートしなければなりません(MUST)。これは `required_for` がすべての未署名を拒否する姿勢。 * **署名を優先、フォールバックを受け入れ(ロールアウト中推奨)。** オペレーションに `required_for` を宣伝するが bearer を開いたまま。合成ルールが適用: 未署名-未認証呼び出し元は拒否、未署名-bearer 認証呼び出し元は通過。バイヤーが自身のペースで 9421 にオンボードする数四半期にわたる移行に適する。 * **助言のみ。** オペレーションを `required_for` ではなく `warn_for`(または `supported_for`)に移す。検証者は存在するとき署名を検証して失敗をログするが、署名欠落で決して拒否しない。 *呼び出し元ごとのフラグの例(strict 姿勢):* 9421 対応の相手方の `agents[]` エントリに `signing_onboarded: true` フラグを運ぶセラーは、`required_for` のオペレーションについて解決済みエージェントが `signing_onboarded: true` を持つ bearer クレデンシャルを拒否するよう bearer 認証器を設定します。他のエージェントはフラグが切り替わるまで bearer で認証し続けます。`required_for` への昇格は運用上安全なまま — 既存の bearer トラフィックが続く一方、オンボード済み相手方はより厳格なバーに保たれます。 相手方のケイパビリティ面で `required_for` を読むバイヤーは、\*\*「クレデンシャルを一切提示しない呼び出し元はこのオペレーションで拒否される。検証者が受け入れる bearer、API キー、mTLS クレデンシャルを提示する呼び出し元は署名欠落で拒否されない」\*\*を学びます。それは「すべての未署名呼び出し元が拒否される」ではありません。自身の未署名 bearer 呼び出しを `required_for` オペレーションでフェイルクローズさせたいバイヤーは、ケイパビリティブロックから動作を推論するのではなく、そのオペレーションについて bearer クレデンシャルを失効させるようセラーと交渉しなければなりません(MUST)。 **なぜこの合成で strict 解釈でないか。** strict 解釈(「`required_for` はフォールバッククレデンシャルに関わらずすべての未署名リクエストを拒否」)には 2 つの実用的問題があります。第一に、3.0 ロールアウトパターンと衝突します: セラーはオペレーションを数四半期にわたって `supported_for → warn_for → required_for` に昇格し、ほとんどが移行中に同じオペレーションでライブ bearer トラフィックを持ちます。strict 解釈は、すべての相手方をセラーの `required_for` フリップと歩調を合わせて署名に移行させるか、壊れるよう強制します。第二に、遠隔作用バグを作ります: 運用モニタリング目的で `required_for` を有効化するセラーは、警告なしにそのオペレーションのすべての bearer 認証バイヤーを不注意に 401 し、ケイパビリティを削除する以外の救済パスがありません。合成ルールは `required_for` を段階的に有効化して安全にします — その効果は検証者が実際に所有する未認証ブランチにスコープされます。 #### Content-digest とプロキシ互換性 `content-digest` をカバーすることはリクエストボディバイトを署名にバインドします。支出コミットオペレーションでは、これが要点です: ボディが金銭を指定し、ボディにコミットしない署名は重要な攻撃面を保護しません。サーバー間 AdCP デプロイ — そのほとんど — では、ボディを変更する中間者は稀で、通常特定の意図的な設定の結果です。デフォルト姿勢: **支出コミットオペレーションで `content-digest` をカバー。ボディ保持を妨げるトランスポートを、対応する制約ではなく修正すべきバグとして扱う。** **既知のボディ変更トランスポートパターン。** これらの設定はボディバインディング署名を壊し、本番での 9421 相互運用バグの単独最大の原因です: * POST ボディを再圧縮またはバッファ変更する CDN 設定(稀だが、特定の Cloudflare Workers、Fastly VCL、CloudFront Lambda\@Edge セットアップはバイト変更を導入し得る)。 * JSON リクエストボディを「サニタイズ」する WAF(空白正規化、キー並べ替え、未知フィールド除去)。ほとんどの WAF は変更せずに検査するが、一部は変更する。 * ロギング、検証、変換のためにクライアントとオリジンの間で JSON を再シリアライズするリバースプロキシまたは API ゲートウェイ。 * チャンクエンコードフレーミングの仮定が異なる HTTP/2 → HTTP/1.1 ブリッジ。 * **署名者側シリアライズミスマッチ。** ある JSON シリアライズ(例: デフォルトの空白セパレーター付き `json.dumps(payload)`)上で `content-digest` を計算する一方、HTTP クライアントがワイヤー上に異なるシリアライズ(例: コンパクトセパレーター)を書く署名者は、レシーバーが決して見ないバイト上のダイジェストを生成します。すべての検証者がその後 `webhook_signature_digest_mismatch` または `request_signature_digest_mismatch` で拒否します。**ボディを一度シリアライズし、それらの正確なバイトをダイジェスト入力と HTTP ボディの両方に使う** — 事前シリアライズされたオブジェクトからダイジェストを計算してクライアントが同じバイトを再現すると信頼しない。これは[レガシー HMAC スキームがコンパクトセパレーターでピン留めする](#legacy-hmac-sha256-fallback-deprecated-removed-in-40)同じ罠です。9421 は黙ってではなく大きく失敗する(ダイジェストミスマッチはハード拒否)が、署名者側の修正は同一です。 **トランスポートを制御する場合**、ボディをバイト単位でエンドツーエンド保持し `content-digest` をカバー。**トランスポートを制御しない場合**、セキュリティ保証を劣化させるのではなくそれを修正。実際のトラフィックを送る前にテストエンドポイントに対する `POST` エコーテストでエンドツーエンド検証。 レガシーインフラのため本当にボディバイトを保持できない検証者は `covers_content_digest: "forbidden"` を宣伝してもよい(MAY)。これはインフラを修正できない狭いケースのオプトアウトです。`"required"` はすべての支出コミットオペレーションに推奨。`"either"` がデフォルト — 署名者がリクエストごとに選択し、検証者はカバー済みとカバーなしの両形式を受け入れます。 **`"required"` は厳格。** 検証者が `covers_content_digest: "required"` を宣伝するとき、`content-digest` をカバーしないボディを持つ署名済みリクエストは `request_signature_components_incomplete` でハード拒否です。検証者はそれを「ソフト」な署名済みだがボディ非バインドリクエストとして受け入れてはなりません(MUST NOT)。ソフトモードはありません。ある呼び出しで `content-digest` をカバーしたくない署名者は、ポリシーが `"either"` または `"forbidden"` の検証者にルーティングするか、その呼び出しに全く署名しないかしなければなりません(MUST)。 #### トランスポートリプレイ重複排除 [verifier checklist](#verifier-checklist-requests) のステップ 12 は `(keyid, nonce)` ごとの重複排除を要求します。無制限のセットはメモリと DoS リスクです。 * 各エントリの TTL = ウィンドウ検証で適用した対称クロックスキュー許容に一致する `(expires − now) + 60 s`。典型的な TTL ≤ 360 秒(5 分 + 60 秒スキュー)。 * TTL 退避付きの `(keyid, nonce)` でキーされたインメモリ LRU、期待リクエストレート × 最大署名有効性でサイズ。 * 署名者ごとに約 10K req/秒を超える場合: `EX = remaining_validity_seconds + 60` の Redis `SETNX`。 * 分散検証者(マルチリージョン): リージョンごとのリプレイキャッシュは許容。これが可能にする唯一の攻撃はリージョンをまたぐ `(expires − now + 60 s)` 内の単一リプレイで、約 6 分に制限され、攻撃者が中間ルーティングを制御する場合のみ有効。 検証者はリクエスト bearer トークン、IP、任意の非 `(keyid, nonce)` 値をリプレイキーとして使ってはなりません(MUST NOT)— それらは正当なエージェントトラフィックを拒否する偽陽性を生みます。 **keyid ごとの上限。** 濫用的または侵害された署名者が一意のノンスで検証者メモリを枯渇させるのを防ぐため、検証者はリプレイキャッシュに keyid ごとのエントリ上限を強制しなければなりません(MUST)。推奨上限: `keyid` ごとに 1,000,000 エントリ。上限超過で、検証者はその `keyid` からの新しい署名を `request_signature_rate_abuse` で拒否しなければならず(MUST)— 黙って退避してはならず — オペレーターにアラートすべきです(SHOULD)。上限に達することは侵害された鍵または著しく誤設定された署名者を示すからです。黙った退避が危険なモードです: まさに検証者が攻撃下にあるときにリプレイウィンドウを作ります。keyid ごとの上限は総キャッシュ上限とは別: 検証者は多くの行儀の良い署名者を介して正当に総上限に達し得ますが、keyid ごとの枯渇は明白に攻撃シグナルです。上限チェックは [verifier checklist](#verifier-checklist-requests) のステップ 9a — 暗号検証の**前**に評価され、濫用的署名者が増幅された Ed25519/ECDSA 作業を検証者に強制できないように。 **単一プロセス vs 分散強制。** 単一プロセス検証者では、ステップ 9a(読み取り)とステップ 13(挿入)は 1 つの実行で逐次的で上限は正確です。Redis バックのリプレイキャッシュを共有する分散検証者では、ステップ 9a は安価な高速パス増幅ガードだが権威的ではありません: 2 つの検証者が両方 `size == cap − 1` を観測し、両方 9a を通過し、両方ステップ 10-12 を通過し、両方ステップ 13 で挿入し得ます。上限ドリフトを避けるため、ステップ 13 の挿入は上限チェックとアトミックであるべきです(SHOULD、例: over-cap センチネルを返す Lua スクリプトまたは `SETNX` パターン)— ステップ 9a は安価な増幅ガードのまま、ステップ 13 が権威的な強制ポイント。アトミック挿入が over-cap を返す検証者は、成功させるのではなく `request_signature_rate_abuse` でリクエストを拒否しなければなりません(MUST)。ステップ 13 で助言的な上限は上限ではありません。 #### トランスポート失効 オペレーターは、`agents[]` エントリの下で公開されたガバナンス、リクエスト署名、その他のエージェント署名鍵をカバーする単一の結合失効リストを brand.json オリジンで提供すべきです(SHOULD)。形式と署名セマンティクスはガバナンス失効リストに一致(上記の [Revocation](#revocation) を参照)。リクエスト署名鍵について: * `revoked_kids` はその `kid` の下で署名されたすべてのリクエスト(失効タイムスタンプの前後)を無効にします。 * `revoked_jtis` は使われません(リクエスト署名は `jti` を持たず、ノンスの一意性は鍵ごと)。 リクエスト署名済み変更を受け入れる検証者は、`next_update` で宣言されたケイデンス(フロア 1 分、上限 30 分)で失効リストをポーリングしなければなりません(MUST)。フェッチ失敗の安全デフォルトが grace = 以前のポーリング間隔の 4 倍で適用: `next_update + grace` 内にリフレッシュしていない検証者は、リストがリフレッシュされるまで新しいリクエスト署名済み変更を `request_signature_revocation_stale` で拒否しなければなりません(MUST)。 #### トランスポートケイパビリティ宣伝 検証者は `get_adcp_capabilities` の `request_signing` ブロックを介して署名サポートと呼び出しごとの要件を宣伝します: ```json theme={null} { "request_signing": { "supported": true, "covers_content_digest": "either", "required_for": [], "warn_for": ["create_media_buy"], "supported_for": [ "create_media_buy", "update_media_buy", "sync_creatives", "activate_signal" ] } } ``` * `supported`: true のとき、検証者は存在するとき署名を検証。false または不在のとき、署名は無視される。 * `covers_content_digest`: `"required"`, `"forbidden"`, `"either"`(デフォルト)のいずれか。`"required"`: 署名者は `content-digest` をカバーしなければならない。ボディ非署名の署名は拒否。`"forbidden"`: 署名者は `content-digest` をカバーしてはならない。ボディバインド署名は拒否。`"either"`: 署名者が選択。検証者は両方を受け入れ。 * `required_for`: **他の有効なクレデンシャルを提示しない未署名リクエスト**が `request_signature_required` で拒否される AdCP プロトコルオペレーション名(トランスポート固有でない)。3.0 ではデフォルトで空。署名者はリストされた任意のオペレーションに署名しなければならない(MUST)。bearer、API キー、mTLS フォールバックとの合成は [Composition with fallback authenticators](#composition-with-fallback-authenticators) が規定 — 特に、有効なフォールバッククレデンシャルを提示する未署名リクエストは受け入れられ、署名を無条件にしたいセラーはそのオペレーションで他のクレデンシャルタイプを拒否するようフォールバック認証器を設定しなければならない(MUST)。 * `warn_for`: 検証者が存在するとき署名を検証し、失敗をモニタリングでログするが、**拒否しない**オペレーション。`supported_for` から `required_for` へのシャドウモードブリッジとして使用。セラーが強制前に実トラフィック失敗率を見る相手方ごとのパイロットを可能にする。優先順位: `required_for > warn_for > supported_for`。署名者は `warn_for` のオペレーションに署名すべき(SHOULD)。検証者はこれらのオペレーションへの未署名または検証失敗リクエストを拒否してはならない(MUST NOT)。 * `supported_for`: 署名が存在するとき検証されるが必須でないオペレーション。署名者はこれらに署名すべき(SHOULD)。通常 `required_for` と `warn_for` のスーパーセット。 **ロールアウトパターン:** 1. 署名準備を発表: オペレーションを `supported_for` に追加。相手方は署名を開始できるが、しなくても何も変わらない。 2. シャドウモードに昇格: オペレーションを `warn_for` に移す。検証者は検証失敗をログ。トラフィックは影響なし。オペレーターは失敗率を監視してデバッグ。 3. 強制: 失敗率がオペレーターの閾値を下回ったら `required_for` に移す。そのオペレーションへの未署名または無効署名リクエストは今拒否される。 3.0 では、検証者は `required_for: []` で出荷し、選択的に設定します。`warn_for` は強制に切り替える前の推奨プレプロダクション停止です。4.0 ではプロトコルが規範的に `required_for` が検証者がサポートするすべての支出コミットオペレーションを含むことを要求し、それらのオペレーションに `covers_content_digest: "required"` が推奨されます。 #### トランスポートエラータクソノミー 401 で `WWW-Authenticate: Signature error=""` に返され、SDK 検証者が型付きエラーとして表面化する安定コード。命名パターンは [governance taxonomy](#verification-error-taxonomy) に一致し、SDK エラー処理が対称になります。 | Failure | Retry? | Code | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----------------------------------------- | | 署名が要求される未署名リクエスト — (a) オペレーションが `required_for` にある、または (b) リクエストペイロードが `required_for` メンバーシップに関わらず署名をトリガーするフィールドを運ぶ(例: 署名対応セラーの `push_notification_config.authentication` または `accounts[].notification_configs[].authentication` — [Webhook callbacks](#webhook-callbacks) を参照) | No | `request_signature_required` | | リクエスト `@target-uri` が構文的に不正(例: 空オーソリティ、裸の IPv6、IPv6 ゾーン識別子、生の非 ASCII ホスト)、OR 正準化された `@authority` が正準 `@target-uri` のオーソリティコンポーネントとバイト一致しない(クロス vhost リプレイ) | No | `request_target_uri_malformed` | | `Signature` または `Signature-Input` ヘッダーが存在するが不正 | No | `request_signature_header_malformed` | | 必須 sig-param が不在(`created`, `expires`, `nonce`, `keyid`, `alg`, `tag`) | No | `request_signature_params_incomplete` | | `tag` が `adcp/request-signing/v1` でない | No | `request_signature_tag_invalid` | | `alg` が許可リストにない | No | `request_signature_alg_not_allowed` | | 署名ウィンドウ無効(`expires ≤ created`、スキュー、期限切れ、> 5 分有効性) | No | `request_signature_window_invalid` | | 必須カバーコンポーネント欠落 | No | `request_signature_components_incomplete` | | ケイパビリティが `"forbidden"` のときカバーコンポーネントが `content-digest` を含む | No | `request_signature_components_unexpected` | | 1 回の再フェッチ後 `keyid` が署名者 JWKS にない | No | `request_signature_key_unknown` | | JWK `key_ops` が `verify` を欠く、`use` ≠ `sig`、または `adcp_use` ≠ `request-signing` | No | `request_signature_key_purpose_invalid` | | `keyid` ∈ `revoked_kids` | No | `request_signature_key_revoked` | | 失効リストが grace 内にリフレッシュされていない | No (block new) | `request_signature_revocation_stale` | | 暗号検証失敗 | No | `request_signature_invalid` | | 再計算されたダイジェストとの `content-digest` ミスマッチ | No | `request_signature_digest_mismatch` | | ボディが重複オブジェクトキーを含む(パーサー差分ベクトル) | No | `request_body_malformed` | | ノンスがウィンドウ内ですでに見られている | No | `request_signature_replayed` | | keyid ごとのリプレイキャッシュがエントリ上限を超過 | No (block new) | `request_signature_rate_abuse` | | JWKS フェッチ一時的失敗 | Yes (with backoff) | `request_signature_jwks_unavailable` | | JWKS フェッチが SSRF 検証失敗 | No | `request_signature_jwks_untrusted` | サーバーは安定コードを超えて内部検証詳細をエコーしてはなりません(MUST NOT)。詳細はサーバー側でログします。 **`WWW-Authenticate` 形式。** AdCP はリクエスト署名チャレンジの realm 値を定義しません。検証者は `realm` パラメーターなし、他のパラメーターなしで `WWW-Authenticate: Signature error=""` を発行しなければなりません(MUST)。ヘッダーをパースするクライアントは他のパラメーターを許容しなければならず(MUST、RFC 7235 は実装が追加を含めることを許可)、それらに依存すべきではありません(SHOULD NOT)。 #### Webhook コールバック プッシュ通知 Webhook(バイヤーが登録する `push_notification_config.url` への POST)、アカウントレベル Webhook(`accounts[].notification_configs[].url` への POST)、類似の非同期セラー起動コールバックは、このプロファイルの対称バリアントの下で署名されます。役割方向はリクエスト署名に対して反転します: **セラーがアウトバウンド署名**、**バイヤーが検証**。9421 Webhook 署名は Webhook を発行する任意の 3.0 セラーでベースライン必須で、[Webhook Security](#webhook-security) で説明された非推奨 HMAC フォールバック付きです。 **プログラム的宣伝付きベースライン。** 9421 Webhook 署名は Webhook を発行する任意のセラーでベースライン必須です — デフォルトは署名で、交渉されるオプションではありません。`get_adcp_capabilities` の `webhook_signing` ケイパビリティブロックは、バイヤーが非署名セラーを、トラフィック検査(このブロックが復元される前に `request_signing` との非対称性が現れた方法)で発見するのではなく*オンボーディングで*検出できるように存在します。ケイパビリティ面が変更系 Webhook 発行を他所で宣伝するセラー(例: `media_buy.reporting_delivery_methods` が `webhook` を含む、`media_buy.content_standards.supports_webhook_delivery: true`、または `wholesale_feed_webhooks.supported: true`)は、このブロックを `supported: true` で含めなければなりません(MUST)。Webhook を発行しないセラーはブロックを完全に省略してもよい(MAY)。`supported: false` は未署名 Webhook を発行する安全でない姿勢に予約され、Webhook 不在を示すために使ってはなりません(MUST NOT)。面が変更系 Webhook 発行を宣伝する一方 `webhook_signing` ブロックが `supported: false` を宣伝するか省略されるセラーと統合するバイヤーは、ユーザーが対処可能なエラーでオンボーディングを失敗させなければなりません(MUST)— 発行するが Webhook に署名しないセラーは、任意の変更系 Webhook ユースケースで統合するのに安全でありません。 ```json theme={null} { "webhook_signing": { "supported": true, "profile": "adcp/webhook-signing/v1", "algorithms": ["ed25519", "ecdsa-p256-sha256"], "legacy_hmac_fallback": false } } ``` * `supported`: セラーがケイパビリティ面の他所で変更系 Webhook 発行を宣伝するとき `true` でなければならない(MUST)。バイヤーは `supported: false` またはブロック欠落でセラー面が Webhook 発行を宣伝するときオンボーディングを拒否。Webhook を発行しないセラーはブロック全体を省略すべき(SHOULD)。 * `profile`: このプロファイルバージョンでは正確に `adcp/webhook-signing/v1` でなければならない(MUST)。将来のプロファイルバージョンは文字列をバンプ。 * `algorithms`: `["ed25519", "ecdsa-p256-sha256"]` のサブセット — このセラーが署名するアルゴリズムセット。Webhook 署名検証者許可リストに一致。宣伝された `algorithms` 配列がこのセット外の任意の値を含む場合、バイヤーはユーザーが対処可能なエラーでオンボーディングを拒否しなければならない(MUST)。セット外アルゴリズムは誤設定または非コンフォーマントなセラーを示し、黙った受け入れは許可リストを無効にする。 * `legacy_hmac_fallback`: バイヤーが `push_notification_config.authentication.credentials` または `accounts[].notification_configs[].authentication.credentials` を設定するときセラーがレガシー HMAC-SHA256 スキームをサポートする場合に限り `true`。`false` が 3.x の推奨姿勢。 バイヤーは `push_notification_config.authentication.credentials` または `accounts[].notification_configs[].authentication.credentials` を設定してレガシー HMAC-SHA256 スキームにオプトインします。そうでなければセラーは 9421 Webhook プロファイルで署名します。セラーはレガシースキームのサポートを断ってもよい(MAY)— 上記の `legacy_hmac_fallback` フラグを参照。 **モード選択はスイッチであり両方ではない。** `push_notification_config.authentication` または `accounts[].notification_configs[].authentication` の存在は、その URL に配信されるすべての Webhook についてちょうど 1 つの署名モードを選択します: `authentication` あり → レガシー HMAC-SHA256(または Bearer)、`authentication` なし → 9421。セラーは同じ Webhook を両方の方法で署名してはなりません(MUST NOT)。バイヤーは「まず 9421 を試し、HMAC にフォールバック」検証を試みてはなりません(MUST NOT)— そのパターンはダウングレードオラクル動作を作り、バイヤーが求めていない署名を受け入れます。検証者は、レシーバーが Webhook 登録について設定された HMAC シークレットを持つかで検証パスを厳密にキーします。 **鍵公開。** 署名鍵は、署名エージェントのオペレータードメインのセラー**自身の brand.json** の `agents[]` エントリの、そのエントリの `jwks_uri` メンバーでセラーが公開します — 他の任意の AdCP エージェント鍵と同じ公開パターン。Webhook はエージェントの **`adcp_use: "request-signing"`** 鍵で署名されます。別個の Webhook 鍵目的はありません。リクエストと Webhook のドメイン分離は、鍵目的ではなく署名 `tag`(`adcp/request-signing/v1` vs `adcp/webhook-signing/v1`)が運びます。各署名 JWK は宣言しなければなりません(MUST): | Member | Value | | ---------- | --------------------------------------------- | | `use` | `"sig"` | | `key_ops` | `["verify"]` | | `adcp_use` | `"request-signing"` | | `kid` | JWKS 内で別個。`adcp_use` に関わらず他の `kid` と衝突してはならない | | `alg` | `"EdDSA"` or `"ES256"` | **鍵分離は別個の `kid` を介してオプション — 別個の目的ではない。** Webhook トラフィックを別個の鍵素材で署名させたい(Webhook 鍵侵害がリクエスト署名に及ばないように、または 2 つを独立にローテーションするように)オペレーターは、**別個の `kid` を持つ 2 つ目の `adcp_use: "request-signing"` 鍵**を公開し、それで Webhook に署名します。両方の鍵は同じ `adcp_use` を運びます。検証者は `Signature-Input` の `kid` で正しいものを解決します。分離を達成するのに専用の Webhook 鍵目的は不要です。 > **非推奨:** `adcp_use: "webhook-signing"` は非推奨で将来のメジャーバージョンで削除予定([#5555](https://github.com/adcontextprotocol/adcp/issues/5555) で追跡。正確なウィンドウは WG/RFC 決定)。検証者は後方互換性のためそれを依然受け入れなければならない(MUST、`"webhook-signing"` 鍵の下で署名された Webhook はクリーンに検証される)が、新しい署名者は `"request-signing"` 鍵のみで公開・署名すべき(SHOULD)。 Webhook を検証するバイヤーは、`adcp_use` が `"request-signing"`(または非推奨の `"webhook-signing"`)である JWK を受け入れなければならず(MUST)、他の鍵目的失敗 — 他の任意の `adcp_use` 値、不在の `adcp_use`、欠落した `verify` key\_op — を `webhook_signature_key_purpose_invalid` で拒否しなければなりません(MUST)。逆は依然禁止: リクエスト検証は `adcp_use == "request-signing"` を正確に要求し(鍵が他の目的を宣言するとリクエスト署名は拒否)、`"response-signing"` も `"governance-signing"` 鍵も Webhook 配信で決して有効ではありません。Webhook パスは鍵目的について寛容です。すでに `tag`(`adcp/webhook-signing/v1`)と必須の `content-digest` カバレッジでドメイン分離を運ぶので、鍵目的チェックはそこに混同耐性を追加しないからです。 **トラストアンカーと影響範囲。** Webhook 真正性のトラストアンカーは**署名者の brand.json オリジン** — 署名エージェントの `agents[]` エントリを宣言する brand.json をホストする HTTPS オリジンです。そのオリジンの侵害(サブパス乗っ取り、DNS ハイジャック、`/.well-known/brand.json` または `jwks_uri` の CDN キャッシュポイズニング)は、オペレーターが `revoked_kids` エントリを公開しバイヤー検証者が失効リストをリフレッシュするまで、バイヤーがその署名者から受け入れるすべての Webhook を侵害します。バイヤーは統合オンボーディングで学んだエージェントの `jwks_uri` URL をピン留めし、URL 自体の変更(安定 URL 内の `kid` ローテーションだけでなく)にアラームすべきです(SHOULD)— URL の変更は再アンカーを強制し、黙った採用ではなくオペレーターの注意を要求すべきです(SHOULD)。同じ JWKS 内の `kid` 衝突は各 `kid` がちょうど 1 つの鍵に解決するよう禁止されます。Webhook はエージェントの `request-signing` 鍵で署名されるので、デフォルトでリクエスト署名鍵侵害は Webhook に及びます。影響範囲分離を必要とするオペレーターは、Webhook 配信専用の別個の `kid` を持つ 2 つ目の `request-signing` 鍵を公開してそれで Webhook に署名します — 分離は別個の `adcp_use` ではなく別個の `kid` の下の別個の鍵素材から来ます。 **カバーされるコンポーネント**はリクエスト署名と同一: `@method`, `@target-uri`, `@authority`, `content-type`, `content-digest`。`content-digest` は Webhook コールバックで REQUIRED — ボディがイベントを運び、Webhook レシーバーはボディ保持がバイヤー自身のインフラ問題であるバイヤー制御のエンドポイントです。Webhook に `covers_content_digest: "forbidden"` オプトアウトはありません。Webhook ボディバイトを保持できないトランスポートは修正されなければなりません(MUST)。 **署名パラメーター**は 1 つの上書き付きでリクエスト署名と同一: | Parameter | Notes | | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `created`, `expires`, `nonce`, `keyid`, `alg` | [リクエスト署名パラメーター](#adcp-rfc-9421-profile)と同じセマンティクス。 | | `tag` | 正確に `adcp/webhook-signing/v1` でなければならない。検証者は Webhook ルート上の `adcp/request-signing/v1` を `webhook_signature_tag_invalid` で拒否しなければならない。別個の tag は、リクエスト署名が Webhook 署名としてリプレイされること、およびその逆を防ぐ。 | **JWKS ディスカバリー。** バイヤーはすでに使っている AdCP 統合からセラーのエージェント URL を知っています。バイヤーは解決します: 1. セラーエージェント URL `A` → `A` のオペレータードメインの `/.well-known/brand.json` を [Webhook URL validation](#webhook-url-validation-ssrf) に従う SSRF 検証付きでフェッチ。brand.json 解決は 1 リダイレクト(`authoritative_location` または `house` リダイレクトバリアント)に従って停止。 2. フェッチした brand.json で、`url` が `A` とバイト単位で一致する `agents[]` エントリを見つける。 3. そのエントリの `jwks_uri`(または `A` のオリジンの `/.well-known/jwks.json` にデフォルト)を SSRF 検証付きでフェッチ。JWKS キャッシュ TTL は失効リストポーリング間隔(フロア 1 分、上限 30 分)で上限が制限される。長時間実行のタスクフローは JWKS ローテーションをまたぐ。検証者はタスクの寿命の間単一の JWKS スナップショットをピン留めしてはならない(MUST NOT)。 4. インカミング `Signature-Input` の `keyid` をフェッチしたセットの JWK に解決。`kid` ミスでは、`webhook_signature_key_unknown` で拒否する前に 1 回再フェッチ(再フェッチ間の 30 秒クールダウンに従う)。ミス時再フェッチパスはタスク中の鍵ローテーションを扱う要のメカニズム — それをスキップするクライアントは正当なローテーション後配信を拒否する。 バイヤーは Webhook ペイロードフィールド(`task_id`, `operation_id` など)または `adagents.json` エントリから署名者アイデンティティを導出してはなりません(MUST NOT)— それらはパブリッシャー認可であり署名者アイデンティティではありません。アイデンティティは署名 → JWKS → セラー `agents[]` エントリチェーンのみを介して確立されます。 **ダウングレードと注入への耐性。** バイヤーの Webhook 署名の好みは、Webhook を登録するインバウンドリクエストの `push_notification_config.authentication` または `accounts[].notification_configs[].authentication` の存在または不在で伝えられます。3.0 では、そのインバウンドリクエストは 9421 署名ではなく頻繁に bearer 認証されるので、経路上の変更者(誤設定プロキシ、侵害された中間者)が `authentication` ブロックを黙って除去または注入できます。次のルールが影響範囲を封じ込めます: * **セラーは**非空 `authentication` ブロックで到着するすべてのリクエストを**ログしなければなりません(MUST)。** 予期しない HMAC 選択へのオペレーションアラームは、バイヤーが 9421 を得ていると思ったときにバイヤー側を保護します。 * **リクエスト署名をサポートするセラーは**、`push_notification_config.authentication` または任意の `accounts[].notification_configs[].authentication` に `authentication` が存在するとき、インバウンドリクエストが([request verifier checklist](#verifier-checklist-requests) に従って)9421 署名されることを**要求しなければならず(MUST)**、`request_signature_required`(`required_for` オペレーションに使うのと同じコード — [Transport error taxonomy](#transport-error-taxonomy) を参照)で拒否します。署名済みリクエストがボディに暗号学的にコミットするとき、`authentication` ブロックは署名も無効化せずに注入または除去できません。リクエスト署名を全くサポートしないセラーはこのルールを強制する方法がなく、前の項目の log-and-alarm 姿勢にフォールバックします — 3.0 移行注記であり免除ではない: [request-signing migration timeline](#transport-migration-timeline) は 4.0 で支出コミットオペレーションにリクエスト署名を必須にし、その時点で未署名のみのセラーはなくなります。 * **バイヤーは**、`authentication.credentials` で登録した後に 9421 署名済み Webhook を受け取ったとき、または `authentication` なしで登録した後に HMAC 署名済み Webhook を受け取ったとき、黙ってダウングレードするのではなく **`webhook_mode_mismatch` で拒否してアラームしなければなりません(MUST)。** 拒否が安全特性です。アラームはテレメトリ — アラームするがペイロードを受け入れるバイヤーは、すでにミスマッチした署名スキームに権限を渡しています。拒否は安定エラーコード付きの HTTP `401` として表面化し、送信者側のリトライロジックが同一にリプレイするのではなくインシデントレスポンスにルーティングできます。 * **バイヤーは**、9421 をまだ実装していないセラーと相互運用するとき、オンボーディングで **HMAC モードを帯域外で交渉すべきです(SHOULD)。** オペレーターレコードでの耐久性のある相手方ごとのモード選択は、リクエストごとのフィールドのように MITM 変更可能ではありません。 **Webhook の検証者チェックリスト。** これら 15 のチェック(14 の番号付きステップとサブステップ 9a)を順に適用し、最初の失敗でショートサーキットします。ステップ 14 は 14a(strict-parse 要件)と 14b(ロギング規律)に分解 — 両方ともステップ 14 実行時に適用され、1 つのチェックの詳述。下記のステップは [request verifier checklist](#verifier-checklist-requests) に**2 つのパラメーター置換** — `tag` 値(`adcp/request-signing/v1` の代わりに `adcp/webhook-signing/v1`)と信頼方向解決(バイヤーの代わりにセラーの brand.json `agents[]` エントリ)— を加えたものです。ステップ 14(ボディ整形式性)は 2 つのプロファイルで同一。エラーコードプレフィックスのみ異なる(`webhook_body_malformed` vs `request_body_malformed`)。実装は 2 つのプロファイル間で検証者コードを共有し、2 つのパラメーター置換で分岐し、プロファイル固有のエラーコードを設定すべきで(SHOULD)、実装をフォークすべきではありません。エラーコードは `webhook_*` プレフィックス — ほとんどが `webhook_signature_*` 中置を運び、加えてそれなしの構造コード(現在 `webhook_target_uri_malformed`, `webhook_mode_mismatch`, `webhook_body_malformed`)— なので呼び出し元側のエラー処理が 2 つのプロファイルを区別します。 1. `Signature-Input` と `Signature` ヘッダーを RFC 9421 §4 に従ってパース。不正なら拒否(`webhook_signature_header_malformed`)。`Signature` または `Signature-Input` が他方なしに存在する場合、同じコードで拒否 — 推測可能でなくバインドされたペア。 2. `created`, `expires`, `nonce`, `keyid`, `alg`, `tag` のいずれかが `Signature-Input` パラメーターから不在なら拒否(`webhook_signature_params_incomplete`)。 3. `tag` が正確に `adcp/webhook-signing/v1` でないなら拒否(`webhook_signature_tag_invalid`)。バイト単位一致、ケースフォールディングなし。 4. `alg` が許可リスト(`ed25519`, `ecdsa-p256-sha256`)にないなら拒否。ライブラリのデフォルトに頼ってはならない(`webhook_signature_alg_not_allowed`)。 5. `expires ≤ created`、`created > now + 60 s`、`expires < now − 60 s`、または `expires − created > 300 s` なら拒否(`webhook_signature_window_invalid`)。 6. カバーされるコンポーネントが `@method`, `@target-uri`, `@authority`, `content-type`, `content-digest` のすべてを含まないなら拒否(`webhook_signature_components_incomplete`)。`content-digest` は REQUIRED。ポリシーブランチはない。 7. 上記の JWKS ディスカバリーステップで `keyid` を JWK に解決。`kid` ミスでは、拒否(`webhook_signature_key_unknown`)前に 1 回再フェッチ(再フェッチ間 30 秒クールダウン)。`keyid` が署名者の brand.json の特定の `agents[]` エントリに解決できないなら拒否。 8. JWK の `use` が `"sig"`、`key_ops` が `"verify"` を含み、`adcp_use` が `"request-signing"` であることを検証 — Webhook はエージェントのリクエスト署名鍵で署名される([Key publication](#webhook-callbacks) を参照)。非推奨の `"webhook-signing"` 値も後方互換性のため受け入れなければならない(MUST)。他の任意の結果で `webhook_signature_key_purpose_invalid` で拒否: 不在の `adcp_use`、欠落した `verify` key\_op、他の任意の `adcp_use` 値(例: `"response-signing"`, `"governance-signing"`)。ここで `"request-signing"` を受け入れるのは安全です。クロスプロトコル混同が鍵目的判別子ではなく `tag`(ステップ 3)と必須の `content-digest` カバレッジ(ステップ 6)で防がれるからです: キャプチャされたリクエスト署名は `tag=adcp/request-signing/v1` を運びステップ 3 で拒否されます。(`webhook_mode_mismatch` は HMAC-vs-9421 認証モードセレクターミスマッチに予約 — [Downgrade and injection resistance](#webhook-callbacks) を参照 — で鍵目的失敗には使われません。) 9. [Transport revocation](#transport-revocation) リスト(署名目的をまたいで再利用)を確認。`keyid ∈ revoked_kids` なら拒否(`webhook_signature_key_revoked`)。検証者が grace 内にリフレッシュしていないなら `webhook_signature_revocation_stale` で拒否。 **9a. keyid ごとの上限チェック。** [Webhook リプレイキャッシュ上限](#webhook-replay-dedup-sizing)を確認。超過なら `webhook_signature_rate_abuse` で拒否。リクエスト署名と同じ安価な拒否の根拠で、暗号検証(ステップ 10)の前に実行。 10. [リクエスト署名プロファイル](#adcp-rfc-9421-profile)に従い `@target-uri` 正準化 AND `@authority` 導出を適用した後、カバーされるコンポーネントを使って RFC 9421 §2.5 に従い正準署名ベースを計算。**`@authority` ルールは Webhook セキュリティの要:** 検証者は、存在する場合 HTTP/2+ の `:authority` 疑似ヘッダーから、そうでなければ受信した HTTP/1.1 `Host` ヘッダーから `@authority` を導出しなければならない(MUST)— リバースプロキシルーティング状態、ロードバランサーメタデータ、フォワードプロキシが転送中に書き換えた任意の `Host` 値からではない。受信リクエストに `:authority` と `Host` の両方が存在する場合、正準化後にバイト等価でなければならない(RFC 7540 §8.1.2.3 等価性)。発散は `webhook_target_uri_malformed` で拒否。正準化された `@authority` は正準 `@target-uri` のオーソリティコンポーネントとバイト単位で一致しなければならない(MUST)。ミスマッチは `webhook_target_uri_malformed` で拒否。署名済み `@target-uri` に対するそのバイト一致 — ソースヘッダーの選択ではなく — が唯一の安全ゲート。`Host` 自体が転送中に書き換えられ得るから。このチェックリストだけから — プロファイルを相互参照せずに — 構築する実装者はこのルールを適用しなければならない(MUST)。それをスキップするとクロス vhost リプレイベクトル(攻撃者が TLS 終端された Webhook を傍受し、同じ検証者プール上の 2 つ目の vhost にリプレイ: 同じ証明書 SAN、異なる `Host`)を黙って受け入れる。正準化完了後、JWK に対して署名を検証(失敗で `webhook_signature_invalid`)。 11. 受信したボディバイトから `content-digest` を再計算して比較(ミスマッチで `webhook_signature_digest_mismatch`)。REQUIRED — ポリシーブランチなし。 12. リプレイキャッシュに対してノンスを確認。`(keyid, nonce)` がリプレイキャッシュ TTL 内で見られている場合拒否(`webhook_signature_replayed`)。 13. **ステップ 1-12 がすべて通過した後にのみ**、`(keyid, nonce)` を TTL = `(expires − now) + 60 s` でリプレイキャッシュに挿入。この挿入はステップ 14 のボディ整形式チェックの前に発生しなければならない(MUST)。不正なボディ上に有効な署名を運ぶキャプチャされたフレームが各リトライで暗号検証 CPU を燃やすためにリプレイできないように — ノンスはボディ形状に関わらず暗号学的に有効なフレームの最初の目撃で燃やされる。この順序が保持する要となる上限不変条件はステップ 14b の後に文書化。 14. **ボディ整形式性。** 検証者は重複オブジェクトキーを含むボディを拒否しなければならない(MUST、`webhook_body_malformed`)。RFC 8259 §4 に従い、重複キーパース動作は予測不能 — 署名はワイヤー上のバイトに対して有効だが、2 つのパーサーがパースされた値について一致しないことがあり、これはパーサー差分攻撃クラス(cf. CVE-2017-12635)。このチェックは、署名検証者のペイロードのビューとダウンストリームコンシューマーのビューの間のギャップを閉じる。構造化 `webhook_body_malformed` エラーを返すのではなくクラッシュする検証者はコンフォーマントだが最適でない。このチェックのコンフォーマンスフィクスチャは `static/test-vectors/webhook-hmac-sha256.json` の `duplicate-keys-conflicting-values` ベクター — 9421 プロファイルは署名検証成功後に同じボディ整形式ルールを適用しなければならない(MUST)。`webhook_body_malformed` は `webhook_signature_digest_mismatch` とは別: 署名は有効。ボディが曖昧な状態にパースされる。 **14a. Strict-parse 要件。** チェックは重複キーを露出するパーサーを使わなければならない(MUST)— それらを黙って破棄する last-wins/first-wins のデフォルトはこの要件を満たさない。「安全」または「厳格」とマーケティングされても、重複キー入力で衝突を表面化せずに値を返すクエリライブラリもこの要件を満たさない(cf. Go の `tidwall/gjson` — バリデーターではなくクエリライブラリ)。言語ごとの strict-parse エスケープハッチ、正準の非網羅リスト: * **Python**: stdlib `json.loads(..., object_pairs_hook=...)` — フック内で重複を検出して raise。チェックを満たす。 * **Node**: `JSON.parse` に strict モードなし。重複キーイベントハンドラー付きのストリーミングパーサー(`stream-json`, `jsonparse`)を使う。`secure-json-parse` はデフォルトで不十分: その保護はプロトタイプ汚染キー(`__proto__`, `constructor`)を標的とし、データキー重複ではない(依然 last-wins で畳み込む)。データキー重複を明示的に拒否するよう設定するか、下にストリーミングパーサーを重ねる。 * **Go**: `encoding/json` に strict モードなし、重複を検出しない。オブジェクトスコープごとの明示的な `map[string]struct{}` 一意キーガード付きの `json.Decoder` トークンウォーク、OR 明示的に有効化した `decoder.DisallowDuplicateKey()` 付きの `goccy/go-json`(デフォルトではない)を使う。このチェックに `tidwall/gjson` を使ってはならない — 衝突をシグナルせずに重複キー入力で最後の値を返すクエリライブラリ。 * **Java**: Jackson `DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY`(デフォルト無効、明示的に有効化)。 * **Ruby**: stdlib `JSON.parse` に検出フックなし。`allow_nan: false` / 重複拒否オプションを明示的に設定した `Oj.load(..., mode: :strict)` を使う。 **14b. ロギング規律。** 検証者は `webhook_body_malformed` 拒否で完全なリクエストボディバイトをログすべきではない(SHOULD NOT)。`keyid`、ノンス、バイト長、特定の重複キー名のみをログ。侵害された署名者鍵を保持する攻撃者は、さもなくば攻撃者が選んだバイトを大規模に防御者のログに強制でき、フレームごとにリプレイキャッシュスロットを燃やしつつ、SIEM ポイズニングや認証情報流出のフォローオン攻撃のための攻撃者制御のログトレイルを残せます。重複キー名をログするとき、検証者は各名前を順に適用される次のルールでサニタイズしなければなりません(MUST): * **(a) 最初の非印字コードポイントで切り詰め**、N が切り詰めプレフィックスのバイト長である `` を発行。これは位置情報を省く(キー名内の非印字文字の配置は、さもなくばそれ自体がビット位置としてエンコード可能な攻撃者チャネル)一方、「ここで何かが間違っていた」という診断シグナルを保持。非印字セットは少なくとも次を含まなければならない(MUST): **C0 制御**(U+0000–U+001F)、**DEL**(U+007F)、**C1 制御**(U+0080–U+009F、マルチバイト形式でのターミナル制御セマンティクス)、**bidi 制御と分離子**(U+200E, U+200F, U+202A–U+202E, U+2066–U+2069 — ターミナルと SIEM UI での逆レンダリング)、**行と段落の分離子**(U+2028, U+2029 — 多くのログビューアで改行としてレンダリングされ行注入を可能にする)、**ゼロ幅文字**(U+200B–U+200D — 不可視難読化)、**バイトオーダーマーク**(U+FEFF — パーサー破損)。実装はセットをより広い Unicode 非印字分類に拡張してもよい(MAY)が、狭めてはならない(MUST NOT)— ASCII のみのチェックは、このルールが閉じるログ注入チャネルをまさに再び開く bidi オーバーライドと行分離子攻撃を見逃す。 * **(b) 最後の完全な UTF-8 コードポイント境界で最大 32 バイトに切り詰め**。現実的な AdCP フィールド名はおよそ 24 文字(`signed_authorized_agents`)が上限なので、32 は寛大な上限でありつつ攻撃者制御バイト面を制限。切り詰めは 32 バイト以下の最後の完全な UTF-8 コードポイント境界で発生しなければならない(MUST)。マルチバイトシーケンスがコードポイント中間で分割されず、無効な UTF-8 がログに落ちないように(同じ入力を異なる無効 UTF-8 末尾に切り詰める異なる検証者もログ集約を壊す)。 * **(c) 拒否ごとにログされる重複キー名の数を 4 で上限**、超過なら `<...N more>` を発行。4 vs 8 vs 16 の衝突キーを知る診断価値はほぼゼロ。 これらの制約なしでは、キー名チャネルは攻撃者制御バイトサイドチャネルのまま — 完全ボディロギングより小さいが非ゼロで、ログ注入ベクトルとしてよく前例がある。上流入力の拒否をログする署名者([重複オブジェクトキー署名者側ルール](#legacy-hmac-sha256-fallback-deprecated-removed-in-40)を参照)は、署名者側エラー出力で表面化する任意のキー名に同じ (a)/(b)/(c) サニタイズルールを適用しなければならない(MUST)。ワイヤー方向が逆でもチャネル形状は同一。 **Webhook キャッシュの要となる不変条件。** 署名者の秘密鍵なしの外部トラフィックはこのキャッシュを増やせません: ステップ 13 で認められる各エントリはすでにステップ 10 の暗号検証を通過しているので、キャッシュ増大を駆動する当事者は正当な鍵保持者か、鍵を侵害した者 — keyid ごとの上限(ステップ 9a)と新規 keyid 認可プレッシャーアラーム([Webhook replay dedup sizing](#webhook-replay-dedup-sizing) を参照)が検出するよう設計されたケース — です。不変条件は[類似のリクエスト署名ルール](#verifier-checklist-requests)(そこのステップ 13 直後の「上限の要となる不変条件」段落を参照)をミラーします。Webhook チェックリストへの将来の編集はこの順序を保持しなければならない(MUST): ステップ 13 の挿入をステップ 10 の署名検証の前に移すと、任意の外部当事者が偽造された構造的に有効な署名でキャッシュをフラッドできる。 Webhook パスに後続のブランド-オペレーター認可ステップはありません — 署名がセラーのアイデンティティを確立し、そのアイデンティティが Webhook を受け入れるのに十分です。`idempotency_key` のアプリケーション層重複排除は、重複した副作用から保護するため署名検証(ステップ 13)の後に実行されます。 **Webhook ごとに 1 署名。** 検証者はちょうど 1 つの `Signature-Input` ラベルを処理し、追加のラベルを無視しなければなりません(MUST)。 ##### Webhook リプレイ重複排除のサイジング Webhook のリプレイ重複排除は [Transport replay dedup](#transport-replay-dedup) の `(keyid, nonce)` キー形状と TTL セマンティクスを再利用しますが、バイヤー側キャッシュはバイヤーが統合するすべてのセラーからの署名を見ます — リクエスト側ケースとは根本的に異なるファンイン。 * **keyid ごとのエントリ上限**: 推奨 100,000 エントリ(リクエスト側 1,000,000 上限の 10 分の 1)。6 分ウィンドウで 100K の一意 Webhook を発行するセラーは単一署名者から 275/秒持続 — 通常オペレーションに十分な余裕でありつつ、誤設定または鍵侵害の強いシグナル。 * **集約キャッシュ上限**: すべての署名者にわたって推奨 `min(aggregate_memory_budget, 10,000,000)` エントリ。集約上限超過で、検証者は新しい署名を `webhook_signature_rate_abuse` で拒否しなければならず(MUST)、オペレーターにアラートすべき(SHOULD)— 黙った退避はまさに検証者が攻撃下にあるときにリプレイウィンドウを作る。 * **セラーごとの予算**: オペレーターは、すべてのセラーを各 100K で等重み付けするのではなく、統合の重要度でセラーごとに予算すべき(SHOULD)。支出コミットセラーの Webhook ファンインはディスカバリーのみのセラーのそれとは異なる。 * **新規 keyid 認可プレッシャー**(MUST 追跡、SHOULD アラート)。検証者は単位時間あたりに以前に見たことのない `keyid` から認められるキャッシュエントリのレート(例: 最初のエントリを挿入する別個の `keyid` の 5 分ローリングカウント)を追跡しなければならない(MUST)。新規 keyid 認可レートの急なスパイクは**分散侵害攻撃**のシグネチャです: N 個の侵害された署名者鍵を保持する攻撃者は、各鍵が keyid ごとの上限(ステップ 9a)内に十分収まりつつ、集合的に集約キャッシュを飽和させ、TTL ウィンドウごとに各鍵 N エントリを駆動できます。各鍵のトラフィックは個別には低ボリュームの正当な署名者に見えます。集約形状がシグナルです。 検証者は、新規 keyid 認可が 4 つの閾値の**いずれか**(最初にトリガーするもの)を超えたときアラートすべきで(SHOULD)、各々が別個の攻撃者パターンを閉じます: * **(a)** 現在の認可レートを短期地平の移動平均ベースラインと比較する**短ウィンドウ比率閾値** — 安定ベースラインに対する急なスパイクを捕捉。 * **(b)** 中期地平パーセンタイルベースラインに対する**中ウィンドウ比率閾値** — その地平でトラフィックがベースライン末尾に支配される数週間のランプアップ攻撃を捕捉。 * **(c)** 長期地平パーセンタイルベースラインに対する**長ウィンドウ比率閾値** — 中期地平アンカーを自らとともにドリフトさせる数ヶ月のランプアップ攻撃を捕捉。 * **(d)** 絶対フロアと文書化されたウィンドウにわたる一意 keyid カウントの一部を組み合わせた**比例上限** — 比率ベースラインがゼロ近くのスパーストラフィック検証者を捕捉し、AND 任意のサイズのオペレーターに自動スケール(小さな検証者は低い比例フロアを得、エンタープライズ検証者は比例的に大きいものを得る)。 **4 つのカテゴリは規範的。具体的な閾値はそうではない。** オペレーターは任意の公開された例値を出発点として扱い、自身のトラフィックをベースライン化し、それに応じて調整しなければなりません(MUST)— 公開された規範閾値数は攻撃者に検出姿勢へのオラクルを渡します。具体的な開始値、ベースライン化方法論、攻撃シナリオウォークスルーは非規範的な [Webhook Verifier Tuning Guide](/docs/building/by-layer/L1/webhook-verifier-tuning) で公開されています。実装はガイドの開始値を初回デプロイデフォルトとして出荷してもよい(MAY)が、各閾値を調整可能な設定パラメーター(例: 環境変数、設定ファイル)として公開しなければなりません(MUST)— ハードコードされた開始値は事実上オペレーター可視のデフォルトになり攻撃者オラクルを再導入します。実装は、任意の閾値が検証者の最初の認可より 30 日を超えて出荷開始値のままであるとき `threshold_tuning_overdue` イベントをログまたはアラームすべきです(SHOULD)。これはオペレーター調整義務に、オペレーターの勤勉さだけに頼るのではなくテスト可能・監査可能なフックを与えます。 アラームペイロードは、オペレーターのトリアージが正しい脅威形状に応答できるよう、どの節(a、b、c、d)がトリップしたかを名指さなければなりません(MUST)。ここでのアラームは、集約上限がトリガーする*前*にスローバーン分散侵害パターンを捕捉します — 集約上限で `webhook_signature_rate_abuse` が発火すると、キャッシュはすでに満杯で、すべての正当な署名者が拒否されています。アラームは自動失効ではなくインシデントレスポンスにルーティングすべきです(SHOULD): 「攻撃」と「新しいセラーのバッチをオンボーディング」の区別シグナルはオペレーターコンテキストで、マシン導出可能ではなく、アラームでの自動失効は DoS ベクトルを作ります(正当な新規署名者オンボーディングを駆動する任意の当事者がアラームをトリップして大量失効を引き起こせる)。 **クロスエンドポイントスコープ(MUST)。** 複数の Webhook エンドポイント(統合ごと、環境ごと、テナントごと、または水平スケールされたフリートのポッドごと)を公開するバイヤーは、次のいずれかをしなければなりません(MUST): 1. ある署名者が到達できるすべてのエンドポイントにわたって**単一の論理リプレイキャッシュを共有**(Redis / 共有重複排除サービス — プロセスごとインメモリではない)。エンドポイント A が挿入した `(keyid, nonce)` がステップ 12 実行前にエンドポイント B に見えるように。または 2. **正準宛先 URL をリプレイキーに含める**、重複排除を `(keyid, canonical destination URL, nonce)` にスコープ。正準形は [リクエスト署名プロファイル](#adcp-rfc-9421-profile)に従う正規化後の `@target-uri`(スキーム小文字、ホスト IDNA 正規化、デフォルトポート省略、フラグメント除去)。 オプション 1 がより強い — ±360 秒ウィンドウ内でクロスエンドポイントリプレイをきっぱり拒否。オプション 2 はより弱い — 同じ `(keyid, nonce)` が各別個のエンドポイント URL でリプレイ可能だが、署名済み `@target-uri` が署名でカバーされるので、エンドポイント B の検証者はエンドポイント A 向けに署名された `@target-uri` を持つ任意のペイロードを `webhook_signature_digest_mismatch`(正準署名ベースが失敗)または `webhook_signature_invalid` で拒否。オプション 2 は署名者の正準 `@target-uri` がエンドポイントごとのときのみ許容。複数エンドポイントに同じペイロードを署名する署名者はオプション 2 を無効にし、オプション 1 を使わなければならない(MUST)。 共有層なしのポッドごとまたはリージョンごとの*インメモリ*リプレイキャッシュは、複数エンドポイントを実行するバイヤーには非コンフォーマント: ±360 秒と攻撃者が別のポッドにルーティングする能力のみに制限されるクロスエンドポイントリプレイウィンドウを残します。オペレーターは Webhook フリートを共有重複排除層でフロントするか、上記のエンドポイントごと URL スコープを文書化・強制するかしなければなりません(MUST)。 [Transport replay dedup](#transport-replay-dedup) の他のすべてのルールがそのまま適用されます: 単一プロセス検証者のインメモリ LRU、高ボリュームでの Redis `SETNX`、分散デプロイのステップ 13 でのアトミック挿入-上限チェック。 ##### Webhook の失効とローテーション 署名者はリクエスト署名に使うのと同じ結合失効リストを介して失効を公開しなければなりません(MUST)— [Transport revocation](#transport-revocation) を参照。オペレーターオリジンごとの単一リストがガバナンス署名、リクエスト署名、Webhook 署名鍵をカバーします。 **HMAC→9421 移行。** HMAC から 9421 に移行するバイヤーは、セラーが切り替えを確認したら HMAC 検証者を無効化しなければなりません(MUST)。両検証者を同時に実行することは、HMAC パスを元の 5 分リプレイウィンドウ + バイヤーが切るのを忘れた時間の分、悪用可能なままにします。「念のため」の運用姿勢は非推奨パスを意図された非推奨を過ぎてライブに保ちます。セラーは以前に 9421 に移行された相手方からの `authentication` ブロックを拒否し、拒否をログすべきです(SHOULD)。切り替えウィンドウ中、バイヤーは両検証者を実行してもよい(MAY)が、どちらのスキームの下でも同じ論理イベントが同じ `(sender identity, idempotency_key)` タプルにマップされるよう単一の重複排除キースペースを維持すべきです(SHOULD)— 混在モード配信下の重複排除スコープは [Reliability](/docs/building/by-layer/L3/webhooks#reliability) セクションを参照。 ##### Webhook エラータクソノミー コードは [request-signing error taxonomy](#transport-error-taxonomy) と並行し、SDK エラー処理が 2 つのプロファイルを区別するよう `webhook_` プレフィックス付き。バイヤーはこれらのいずれでもセラーに `401` を返してもよい(MAY)。セラーのリトライループは同じ署名バイトでリプレイするので、この表のすべてのコードは送信者にリトライ不可 — 署名失敗、オーソリティミスマッチ、モードミスマッチはすべてリトライで同一の出力を生む — HTTP セマンティクスがリトライを許可しても。 | Failure | Code | | -------------------------------------------------------------------- | ----------------------------------------- | | `Signature` または `Signature-Input` ヘッダーが不正、または一方が他方なし | `webhook_signature_header_malformed` | | 必須 sig-param 不在 | `webhook_signature_params_incomplete` | | `tag` が `adcp/webhook-signing/v1` でない | `webhook_signature_tag_invalid` | | `alg` が許可リストにない | `webhook_signature_alg_not_allowed` | | 署名ウィンドウ無効 | `webhook_signature_window_invalid` | | 必須カバーコンポーネント欠落(`content-digest` を含む) | `webhook_signature_components_incomplete` | | 1 回の再フェッチ後 `keyid` がセラー JWKS にない | `webhook_signature_key_unknown` | | JWK `adcp_use` ∉ 、不在、または `key_ops` が `verify` を欠く | `webhook_signature_key_purpose_invalid` | | `keyid` ∈ `revoked_kids` | `webhook_signature_key_revoked` | | 失効リストが grace 内にリフレッシュされていない | `webhook_signature_revocation_stale` | | 暗号検証失敗 | `webhook_signature_invalid` | | `content-digest` ミスマッチ | `webhook_signature_digest_mismatch` | | ボディが重複オブジェクトキーを含む(パーサー差分攻撃クラス) | `webhook_body_malformed` | | `@authority` が署名済み `@target-uri` オーソリティコンポーネントと一致しない(クロス vhost リプレイ) | `webhook_target_uri_malformed` | | ノンスがウィンドウ内ですでに見られている | `webhook_signature_replayed` | | keyid ごとのリプレイキャッシュが上限超過 | `webhook_signature_rate_abuse` | | 登録された認証モードが受信 Webhook の署名モードと一致しない | `webhook_mode_mismatch` | **検証失敗のリトライセマンティクス。** 少なくとも 1 回の配信は送信者に任意の非 2xx レスポンスでリトライするよう伝えますが、検証失敗は一時的エラーではありません — 署名バイトとリクエストコンテキストは各リトライで同一に到着するので、各リトライは同一に失敗します。送信者は `WWW-Authenticate: Signature error="webhook_*"`(上記タクソノミーで定義された任意のコード、`webhook_signature_*`, `webhook_target_uri_malformed`, `webhook_mode_mismatch` を含む)を運ぶ `401` レスポンスを、その特定の配信試行の終端失敗として扱わなければなりません(MUST): 現在のイベントのリトライを停止し、オペレーターの注意のためエラーコードで失敗をログし、後続イベントの通常のリトライキューを続ける。送信者は、オペレーター定義の閾値を超える持続的な `webhook_*` エラーレートを、発行し続けるのではなくインシデントレスポンスにルーティングすべきです(SHOULD)— 持続的な署名、オーソリティ、モード失敗は鍵ローテーション調整問題、誤設定検証者、または侵害を示し、すべて人間のアクションが必要。レシーバーはこれらの失敗を黙って破棄してはならず(MUST NOT)、オペレーターログでの表面化がセキュリティ姿勢の一部。 **将来の追加に関する編集者注記。** 上記のワイルドカード `webhook_*` 終端失敗分類は eager sweep です: タクソノミーに追加される任意の新コードは、個別レビューなしに配信ごと終端セマンティクスを継承します。リトライ可能であるべき新しい `webhook_*` コード(例: 将来の一時的インフラシグナル)を追加する編集者は、追加の時点で例外を切り出すようこの段落を更新しなければなりません(MUST)— まだ定義されていないコードについてパターンマッチが安全なままであることに頼らない。 ##### Webhook 移行タイムライン | Phase | Behavior | | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 3.0 GA | 9421 Webhook 署名は Webhook を発行する任意のセラーのベースライン。バイヤーが `push_notification_config.authentication.credentials` または `accounts[].notification_configs[].authentication.credentials` を設定するときレガシー HMAC-SHA256 フォールバック利用可能。セラーはサポートを断ってもよい。 | | 3.x | HMAC フォールバックは非推奨。セラーは選択時に警告をログすべき(SHOULD)。SDK は依然 `authentication` を設定するバイヤーに非推奨通知を表面化すべき(SHOULD)。 | | 4.0 | `push_notification_config` と `accounts[].notification_configs[]` の `authentication` がスキーマから削除。9421 Webhook 署名が唯一のサポートパス。 | #### TMP クロスリファレンス **TMP 鍵は別個の `adcp_use` 値を宣言しなければならない(MUST)**(または完全に省略)。検証者がステップ 8 を介してリクエスト署名でそれらを拒否するように。TMP 鍵をリクエスト署名と Webhook 署名鍵と同じ `jwks_uri` で公開することは許可され推奨されます — 1 つの公開パターン、5 つの署名システム、各々 `kid` スコープ: * ガバナンス JWS — `adcp_use: "governance-signing"` * リクエスト署名(RFC 9421)— `adcp_use: "request-signing"`(Webhook にも署名。[Webhook callbacks](#webhook-callbacks) を参照) * Webhook 署名(RFC 9421)— `request-signing` 鍵を使用。レガシー `adcp_use: "webhook-signing"` 値は**非推奨**(依然受け入れ、削除保留 — 非推奨注記のフォローアップイシューを参照) * 指定タスクレスポンスペイロード JWS — `adcp_use: "response-signing"`(上記の [Designated-task payload-envelope response signing](#designated-task-response-signing) を参照) * TMP エンベロープ — TMP 独自の将来の `adcp_use` 値 すべての検証者が自身のプロファイルで正確な `adcp_use` 一致を強制するので、クロス目的再利用は自動的に防がれます。 Trusted Match Protocol はマッチ時リクエストに独自の Ed25519 エンベロープで署名します。TMP のリクエストごと予算(約 5% でサンプル検証)は、すべての呼び出しでの完全な RFC 9421 検証には厳しすぎます。**TMP 署名はこのセクションのスコープ外**です。このプロファイルは TMP 鍵が同じ JWKS でリクエスト署名鍵と並んで公開される方法のみを制約します。 #### トランスポート移行タイムライン AdCP 4.0 は次の破壊的変更蓄積ウィンドウです。支出コミットオペレーションの必須リクエスト署名はそのフロア要件の 1 つ — AdCP 4.0 支出トラフィックの最小セキュリティバー — であり、唯一の目玉機能ではありません。他の v4.0 変更は[ロードマップ](/docs/reference/roadmap#v40-planned)に蓄積されます。 | Phase | Status | Behavior | | ------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 3.0 GA | Optional, capability-advertised | 検証者は検証してもよい。デフォルトで `required_for: []`。署名者は署名してもよい。リファレンスベクター出荷。リファレンス SDK パイロット開始。 | | 3.x | Reference SDKs ship; pilots surface bugs | コンフォーマンステストベクターがクロス SDK 相互運用を駆動。早期採用者が名指しの相手方で段階的に `required_for` を有効化。 | | 4.0 | Required for spend-committing operations | `required_for` は `create_media_buy`, `acquire_*`, 検証者がサポートする任意の支出コミットオペレーションを含まなければならない。署名者は署名しなければならない。それらのオペレーションに `covers_content_digest: "required"` 推奨。 | 3.x で署名を出荷する実装は、実トラフィックに対してエンドツーエンドパスを検証するため、4.0 の前に検証者側 `required_for` を選択的に(相手方ごとパイロット、その後より広いロールアウト)有効化すべきです(SHOULD)— これがエコシステム全体の破壊なしに 4.0 移行を実現可能にするものです。 #### リクエスト検証者リファレンス(TypeScript) 説明目的のみ。`verify9421` と `parseSignatureInput` コールバックはプロトコル固有の正準化と署名検証をカプセル化します。実装は [`/compliance/latest/test-vectors/request-signing/`](https://adcontextprotocol.org/compliance/latest/test-vectors/request-signing/) の AdCP コンフォーマンステストベクターに対して検証された特定の RFC 9421 ライブラリをピン留めすべきです。 ```ts theme={null} import { createRemoteJWKSet } from "jose"; class RequestSignatureError extends Error { constructor(public code: string) { super(code); } } const ALLOWED_ALGS = new Set(["ed25519", "ecdsa-p256-sha256"]); const REQUIRED_TAG = "adcp/request-signing/v1"; const REQUIRED_COMPONENTS = new Set(["@method", "@target-uri", "@authority"]); const REQUIRED_PARAMS = ["created", "expires", "nonce", "keyid", "alg", "tag"] as const; export async function verifyAdcpRequestSignature(req: Request, ctx: { operationName: string; requiredFor: Set; contentDigestPolicy: "required" | "forbidden" | "either"; resolveJwk: (keyid: string) => Promise<{ jwk: unknown; agentUrl: string }>; // throws _key_unknown after refetch isKeyRevoked: (keyid: string) => Promise; isRevocationStale: () => Promise; isKeyidAtCapacity: (keyid: string) => Promise; isReplayed: (keyid: string, nonce: string) => Promise; recordNonce: (keyid: string, nonce: string, ttlSeconds: number) => Promise; verify9421: (req: Request, jwk: unknown, covered: string[]) => Promise; // throws on signature or digest failure parseSignatureInput: (header: string) => { keyid?: string; alg?: string; created?: number; expires?: number; nonce?: string; tag?: string; components: string[]; }; }) { const sigInput = req.headers.get("signature-input"); // Pre-check: required_for / downgrade protection. if (!sigInput) { if (ctx.requiredFor.has(ctx.operationName)) throw new RequestSignatureError("request_signature_required"); return; // operation doesn't require a signature; verify nothing. } let parsed; try { parsed = ctx.parseSignatureInput(sigInput); } catch { throw new RequestSignatureError("request_signature_header_malformed"); } // 2: presence for (const p of REQUIRED_PARAMS) { if ((parsed as any)[p] == null) throw new RequestSignatureError("request_signature_params_incomplete"); } // 3: tag if (parsed.tag !== REQUIRED_TAG) throw new RequestSignatureError("request_signature_tag_invalid"); // 4: alg if (!ALLOWED_ALGS.has(parsed.alg!)) throw new RequestSignatureError("request_signature_alg_not_allowed"); // 5: window (including expires > created) const now = Math.floor(Date.now() / 1000); if (parsed.expires! <= parsed.created! || parsed.created! > now + 60 || parsed.expires! < now - 60 || parsed.expires! - parsed.created! > 300) { throw new RequestSignatureError("request_signature_window_invalid"); } // 6: components for (const c of REQUIRED_COMPONENTS) { if (!parsed.components.includes(c)) throw new RequestSignatureError("request_signature_components_incomplete"); } const coversCd = parsed.components.includes("content-digest"); if (ctx.contentDigestPolicy === "required" && !coversCd) { throw new RequestSignatureError("request_signature_components_incomplete"); } if (ctx.contentDigestPolicy === "forbidden" && coversCd) { throw new RequestSignatureError("request_signature_components_unexpected"); } // 7: JWK resolution const { jwk } = await ctx.resolveJwk(parsed.keyid!); // throws _key_unknown // 8: key purpose const j = jwk as any; if (j.use !== "sig" || !Array.isArray(j.key_ops) || !j.key_ops.includes("verify") || j.example_use !== "request-signing") { throw new RequestSignatureError("request_signature_key_purpose_invalid"); } // 9: revocation (BEFORE crypto verify) if (await ctx.isRevocationStale()) throw new RequestSignatureError("request_signature_revocation_stale"); if (await ctx.isKeyRevoked(parsed.keyid!)) throw new RequestSignatureError("request_signature_key_revoked"); // 9a: per-keyid cap (BEFORE crypto verify) — prevents amplified crypto work by abusive/misconfigured signer. if (await ctx.isKeyidAtCapacity(parsed.keyid!)) { throw new RequestSignatureError("request_signature_rate_abuse"); } // 10 + 11: crypto verify, content-digest recompute — both inside verify9421. try { await ctx.verify9421(req, jwk, parsed.components); } catch (e: any) { if (e?.code === "digest_mismatch") throw new RequestSignatureError("request_signature_digest_mismatch"); throw new RequestSignatureError("request_signature_invalid"); } // 12: replay check if (await ctx.isReplayed(parsed.keyid!, parsed.nonce!)) { throw new RequestSignatureError("request_signature_replayed"); } // 13: replay insert (only after all checks pass) await ctx.recordNonce(parsed.keyid!, parsed.nonce!, (parsed.expires! - now) + 60); } ``` ### 予算検証 コミット前に予算を検証します: ```javascript theme={null} async function validateBudget(request, account) { const { budget } = request; // Check positive amount if (budget.amount <= 0) { throw new ValidationError('Budget must be positive'); } // Check against account limits const limits = await getAccountLimits(account.account_id); if (budget.amount > limits.daily_spend_limit) { throw new BudgetError('Exceeds daily spend limit'); } // Check available balance const balance = await getAvailableBalance(account.account_id); if (budget.amount > balance) { throw new BudgetError('Insufficient balance'); } } ``` ## トランスポートセキュリティ AdCP のアプリケーション層セキュリティプリミティブ(9421 署名、JWS ガバナンス、冪等性)は、トランスポートが攻撃者を助けないことを前提とします。誤設定された TLS スタックはその前提を壊します — アクティブな経路上の敵対者に耐えるよう設計されたプロトコルを、すべての中間者を信頼するものに格下げします。 このセクションはすべての AdCP エンドポイント — インバウンド(セラーとバイヤーの API 面)とアウトバウンド(JWKS フェッチ、brand.json フェッチ、失効リストフェッチ、Webhook 配信)— で規範的です。オペレーターが午前 3 時に暗号スイートについて第一原理から推論しなくてよいよう、意図的に規定的です。 ### TLS バージョンポリシー * **TLS 1.3 がすべての AdCP エンドポイントで RECOMMENDED。** * **TLS 1.2 が最小。** エンドポイントはハンドシェイクで TLS 1.1 以下を拒否しなければなりません(MUST)。 * **クライアント側検証者**(例: 相手方の JWKS、brand.json、失効リストをフェッチする AdCP サーバー)は TLS 1.2 未満をネゴシエートすることを拒否しなければなりません(MUST)。「互換性」のため依然 TLS 1.0 をデフォルトとするライブラリは明示的に設定されなければなりません(MUST)。 * SSL 2.0、SSL 3.0、TLS 1.0、TLS 1.1 は有効化してはなりません(MUST NOT)— どのエンドポイントでも、どのレガシーパートナーでも、別のポートでも。 ### 暗号スイートとアルゴリズム * TLS 1.3: IETF 定義スイート(`TLS_AES_128_GCM_SHA256`, `TLS_AES_256_GCM_SHA384`, `TLS_CHACHA20_POLY1305_SHA256`)を使います。3 つとも AEAD。他の TLS 1.3 スイートは存在しません。それらを恣意的に無効化しないでください — 「速度」を理由に ChaCha20 を無効化するオペレーターは、1 つのクライアントの癖でモバイルクライアントを壊す寸前です。 * TLS 1.2: **AEAD のみ**の ECDHE スイートに制限。許可セットは `ECDHE-ECDSA-AES128-GCM-SHA256`, `ECDHE-ECDSA-AES256-GCM-SHA384`, `ECDHE-ECDSA-CHACHA20-POLY1305`, `ECDHE-RSA-AES128-GCM-SHA256`, `ECDHE-RSA-AES256-GCM-SHA384`, `ECDHE-RSA-CHACHA20-POLY1305`。 * CBC-MAC、RC4、3DES、DES、NULL、EXPORT、匿名 DH、静的 RSA 鍵交換スイートは TLS 1.2 で無効化されなければなりません(MUST)— その存在はハンドシェイクの上に構築されたすべてのセキュリティ特性を黙って格下げします。 * サーバー証明書は ECDSA(P-256 または P-384)または RSA ≥ 2048 ビットを使わなければなりません(MUST)。RSA \< 2048 は使ってはなりません(MUST NOT)。 * エンドポイントはサーバー側暗号順序(OpenSSL `SSL_OP_CIPHER_SERVER_PREFERENCE`、nginx `ssl_prefer_server_ciphers on`)を優先しなければなりません(MUST)。強いスイートが相互に利用可能なとき、弱いクライアントが弱いスイートを強制できないように。 ### 証明書検証(アウトバウンドフェッチ) AdCP が行うすべてのアウトバウンド HTTPS リクエスト — JWKS、brand.json、失効リスト、Webhook コールバック、アグリゲータープロキシ — は完全な PKIX 検証を実行しなければなりません(MUST)。具体的なチェック: * **トラストチェーン**はオペレーターが意図的に含めたパブリックルートで終端しなければなりません(MUST)。本番コードパスのどこにも `--insecure`、`verify=False`、`rejectUnauthorized: false` なし。これは単独で最も一般的な本番侵害です — エンジニアがステージングの証明書問題を回避するため検証を切り、そのフラグが出荷される。 * **SAN 一致**が権威的なアイデンティティチェックです。証明書は URL ホストに一致する Subject Alternative Name エントリを持たなければなりません(MUST)。CN のみのフォールバックは受け入れてはなりません(MUST NOT)。主要な HTTP クライアントはレガシーの理由で依然それをサポートしますが、AdCP 検証者は SAN を要求しなければなりません(MUST)。 * **有効期限**は現在のクロックに対してチェックされなければなりません(MUST)。TLS 証明書が先週期限切れになったドメインから JWKS をフェッチすることは、互換性問題ではなくガバナンスのレッドフラグです。 * **ホスト名検証**はライブラリ設定で有効化されなければなりません(MUST)。いくつかの人気の HTTP クライアントライブラリはホスト名検証をデフォルトでオンにして出荷しますが、驚くほど多くがそれを無効にするフラグを持ちます。AdCP 実装はホスト名検証がオンであることを仮定するのではなくアサートしなければなりません(MUST)。 * **OCSP ステープリング**は提供されたとき受け入れるべきです(SHOULD)。オペレーター制御の証明書での OCSP must-staple は RECOMMENDED。Must-staple は欠落したステープルをハード失敗に変え、OCSP でのソフト失敗の抜け穴を閉じます。 * **Certificate Transparency(CT)** SCT は規制された支出を提供するエンドポイントでチェックされるべきです(SHOULD)。ブラウザはすでに CT を強制します。規制カテゴリのワークフローでガバナンス JWKS をフェッチする AdCP SDK もそうすべきで(SHOULD)、隠された誤発行証明書が検出可能に。 * **ピン留め**はプロトコル層で必須ではなく、正当なオペレーター証明書ローテーションと衝突するため相手方が供給する URL(brand.json、JWKS)では避けるべきです(SHOULD)。パブリック CA チェーンへのピン留め(中間ピン)は許容。特定のリーフ証明書へのピン留めは非推奨。 ### インバウンドサーバー側ヘッダー ```javascript theme={null} app.use((req, res, next) => { // HSTS: 1 year, include subdomains, preload-eligible. MUST be on every HTTPS response. res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload'); // No framing of AdCP API responses — even though they're JSON, frame isolation // protects any error or debug HTML that could leak through. res.setHeader('X-Frame-Options', 'DENY'); // MIME sniffing off: responses declare their type, clients MUST respect it. res.setHeader('X-Content-Type-Options', 'nosniff'); // Prevent referrers leaking to external URLs supplied by counterparties. res.setHeader('Referrer-Policy', 'no-referrer'); // AdCP endpoints serve no browser-facing HTML — block script-source loading outright. // If your operator reuses the same origin for a dashboard, adjust this per-path. res.setHeader('Content-Security-Policy', "default-src 'none'; frame-ancestors 'none'"); next(); }); ``` **HSTS max-age は AdCP エンドポイントを提供する任意のドメインで ≥ 31536000(1 年)でなければなりません(MUST)。** オペレーターに文書化された理由がない限り `includeSubDomains` を設定しなければなりません(MUST)。支出コミット AdCP エンドポイントを提供するドメインは HSTS プリロードリストに提出すべきです(SHOULD)。 ### クライアント / アウトバウンド TLS ハードニング アウトバウンドフェッチのコードパス(ガバナンス JWKS、brand.json、失効リスト、Webhook 配信、アグリゲータープロキシ)は次をしなければなりません(MUST): * ホストごとの固定上限と全体の固定上限を持つ接続プールを使う。無制限のプールはリソース枯渇面。 * TLS ハンドシェイク時間をデフォルトで 10 秒、総リクエスト時間を 30 秒で上限 — 相手方が供給する URL はさもなくばタールピット DoS ベクトル。 * 接続を [SSRF 制御](#webhook-url-validation-ssrf)を通過した IP アドレスにピン留め — SSRF チェックと実際の接続の間の DNS 再解決が TOCTOU バイパスが着地する方法。 * セキュリティ機微なフェッチでリダイレクトを拒否。JWKS、brand.json、失効リスト、Webhook コールバックのフェッチはリダイレクトに従ってはならず(MUST NOT)、[brand.json 解決ルール](#buyer-identity-resolution)はすでに「1 リダイレクト(`authoritative_location` または `house` バリアント)、チェーンなし」と述べ、初回の `/.well-known/adagents.json` フェッチは同一登録可能ドメインリダイレクトのみに従う(apex↔www、HTTPS 保持、3 ホップ以下、最初に要求されたドメインに固定)— 他のすべての場所ではゼロ。`adagents.json` の `authoritative_location` 参照は「他のすべての場所」: ゼロリダイレクト。 * 信頼境界をまたぐセッション再開を無効化。攻撃者制御の相手方との TLS セッションを後の検証済み相手方(DNS リバインド経由の同じ IP)に再開することはよく知られた混同のクラス。ライブラリのデフォルトは通常問題ないが、オペレーターは監査しなければなりません(MUST)。 ### TLS 再ネゴシエーションとダウングレード * TLS 1.2 の**セキュア再ネゴシエーション**(RFC 5746)は、再ネゴシエーションがサポートされる場合有効化されなければなりません(MUST)。非セキュア再ネゴシエーション許容スタックは MUST-disable。 * **TLS 圧縮**(CRIME)はオフでなければなりません(MUST)。 * **Heartbeat 拡張**は TLS 1.2 エンドポイントでオフでなければなりません(MUST、Heartbleed 系統)。 * TLS 1.3 の **0-RTT / early-data** は、変更系 AdCP オペレーションを受け入れる任意のエンドポイントで有効化してはなりません(MUST NOT)。0-RTT は設計上リプレイ可能です。冪等性と署名ノンス重複排除は、リクエストがアプリケーションロジックに到達した後は無料の救済ではありません。読み取り専用ディスカバリーエンドポイント(`get_adcp_capabilities`, `list_creative_formats`)は 0-RTT を使ってもよい(MAY)。他のすべては使ってはなりません(MUST NOT)。 ### mTLS トランスポート [mTLS](/docs/building/by-layer/L2/authentication#mtls) が認証メカニズムのとき: * クライアント証明書 SAN / Subject は、`adagents.json` または `brand.json` で宣言されたバイヤーの登録済みドメインに一致しなければなりません(MUST)。任意のヘッダーフィールド(`X-Forwarded-Client-Cert`, `X-Client-DN` など)に頼ることは[明示的に禁止](#buyer-identity-resolution)されています — ヘッダーフィールドは誤設定プロキシをまたいで注入され得ます。 * 終端エッジ(ロードバランサー、メッシュサイドカー)は、検証済み証明書アイデンティティを、サーバーが認証できるクラスタ内チャネルで AdCP サーバーに転送しなければなりません(MUST)。未認証のサイドカーヘッダーはバイパス — mTLS をエンドツーエンドでデプロイするか、クラスタ内チャネルをピン留めします。 * クライアント証明書はオペレーターが運用する CRL または OCSP レスポンダーに対してチェックされなければなりません(MUST)。「私たちが発行した」は「まだ有効」と同じではありません。 ### プライベートネットワークとメタデータ保護 このセクションのトランスポート制御は、相手方が供給する URL の [SSRF 制御](#webhook-url-validation-ssrf)を代替しません。相手方 URL へのすべてのアウトバウンドフェッチは SSRF ルールを適用しなければなりません(MUST)— 非 HTTPS を拒否、予約範囲(クラウドメタデータアドレスを含む)の IP を拒否、リダイレクトを拒否、サイズと時間に上限。URL が `169.254.169.254` を指すなら TLS は無用です。 ### このセクションが置き換えないもの トランスポートセキュリティは天井ではなくフロアです。完璧な TLS スタックでも次を置き換えません: * **アプリケーション層のボディ完全性**([リクエスト署名](#request-signing)と [Webhook コールバック](#webhook-callbacks))— TLS はワイヤーを保護し、侵害された中間者後のペイロードは保護しません。 * **ガバナンス証明**([署名付きガバナンスコンテキスト](#signed-governance-context))— TLS は、バイヤーのガバナンスエージェントがこの支出を認可したかをセラーに伝えません。 * **冪等性**([Request Safety](#request-safety))— TLS は、送信者がネットワークタイムアウト後にリトライするのを防ぎません。 「私たちは現代的な TLS 設定を持つ」を「私たちの AdCP デプロイは安全」と混同するオペレーターは、まさにボディバインド署名プロファイルが防御するために存在するオペレーターです。 ## 入力検証 ### リクエスト検証 すべてのユーザー提供入力を検証します: ```javascript theme={null} const INPUT_LIMITS = { targeting_brief_max_length: 5000, creative_upload_max_size: 100 * 1024 * 1024, // 100MB max_formats_per_request: 50, max_products_per_query: 100 }; function validateRequest(request) { // Check string lengths if (request.brief?.length > INPUT_LIMITS.targeting_brief_max_length) { throw new ValidationError('Brief exceeds maximum length'); } // Validate IDs are proper UUIDs if (request.product_id && !isValidUUID(request.product_id)) { throw new ValidationError('Invalid product_id format'); } // Reject unexpected fields const allowedFields = ['brief', 'product_id', 'budget', 'context_id']; for (const field of Object.keys(request)) { if (!allowedFields.includes(field)) { throw new ValidationError(`Unexpected field: ${field}`); } } } ``` ### SQL インジェクション防止 常にパラメーター化クエリを使います: ```javascript theme={null} // GOOD: Parameterized query (request-supplied account_id after auth precheck) const result = await db.query( 'SELECT * FROM media_buys WHERE id = $1 AND account_id = $2', [mediaBuyId, request.account.account_id] ); // BAD: String concatenation (NEVER do this) // const result = await db.query( // `SELECT * FROM media_buys WHERE id = '${mediaBuyId}'` // ); ``` ## 監査ログ ### 必須ログイベント すべてのセキュリティ関連イベントをログします: ```javascript theme={null} const LOG_EVENTS = { AUTH_SUCCESS: 'auth_success', AUTH_FAILURE: 'auth_failure', BUDGET_COMMIT: 'budget_commit', BUDGET_MODIFY: 'budget_modify', ACCESS_DENIED: 'access_denied', WEBHOOK_VERIFIED: 'webhook_verified', WEBHOOK_REJECTED: 'webhook_rejected' }; function logSecurityEvent(eventType, details) { console.log(JSON.stringify({ event: eventType, timestamp: new Date().toISOString(), agent_id: details.agentId, account_id: details.accountId, ip_address: details.ipAddress, resource: details.resource, outcome: details.outcome, // NEVER log: credentials, PII, targeting briefs })); } ``` ### ログ保持 * セキュリティログ: 最低 90 日(365 日推奨) * 金融ログ: 7 年(コンプライアンス要件) * アクセスログ: 最低 30 日 ## セキュリティチェックリスト ### パブリッシャー(AdCP サーバー)向け * [ ] 強力な認証を実装(OAuth 2.0、API キー、または mTLS) * [ ] すべてのデータベースクエリでエージェントとアカウントの分離を強制 * [ ] 金融オペレーションに冪等性を実装 * [ ] 厳格なスキーマ検証ですべての入力を検証 * [ ] すべての通信に TLS 1.3+ を使用 * [ ] Webhook 署名を暗号学的に検証 * [ ] すべてのセキュリティイベントを改ざん不可能にログ ### バイヤーエージェント(AdCP クライアント)向け * [ ] 認証情報をセキュアな鍵管理システムに保管 * [ ] 認証情報を 90 日ごとにローテーション * [ ] すべての AdCP 通信に HTTPS を使用 * [ ] パブリッシャーからのレスポンスを検証 * [ ] 異常な支出パターンのアラートを実装 ### オーケストレーター(マルチエージェント、マルチアカウント)向け * [ ] 各エージェントの認証情報を別々に保管(暗号化) * [ ] すべてのクエリでエージェントとアカウントのフィルタリングを強制 * [ ] データベースで行レベルセキュリティを使用 * [ ] すべてのオペレーションをエージェントとアカウントのアイデンティティ付きでログ * [ ] エージェントごとのレート制限を実装 ## 次のステップ * **Security Model**: このリファレンスが実装する脅威モデルと 5 層防御の物語は [Security Model](/docs/building/concepts/security-model) を参照 * **Webhooks**: Webhook セキュリティパターンは [Webhooks](/docs/building/by-layer/L3/webhooks) を参照 * **Error Handling**: 認証エラーは [Error Handling](/docs/building/by-layer/L3/error-handling) を参照 * **Orchestrator Design**: マルチテナントセキュリティは [Orchestrator Design](/docs/building/operating/orchestrator-design) を参照 # Webhook 検証者チューニングガイド Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L1/webhook-verifier-tuning webhook 検証者しきい値の非規範的チューニングレシピ — 開始値、ベースライン化方法論、攻撃シナリオのウォークスルー。 この文書は非規範的です。[Webhook Security](/docs/building/by-layer/L1/security#webhook-security) で **構造的形状** が仕様化されている webhook 検証者しきい値の **開始値** とチューニング方法論を提供します。規範的仕様はカテゴリー(短ウィンドウ比、中ウィンドウ比、長ウィンドウ比、比例上限)としきい値がオペレーター構成可能であるべきという要件のみを仕様化します。このガイドはどこから始めどうチューニングするかを伝えます。 **最初の 30 日のオラクルリスク。** 下の開始値は公開されており、したがって攻撃者に既知です。出荷されたデフォルトで動く検証者は、オペレーターがしきい値を自身のトラフィックにチューニングするまでオラクルに対して動いています。**オペレーターは初回デプロイから 30 日以内に各しきい値をチューニングしなければなりません(MUST)**。公開された開始値で 30 日を超えて動く検証者は、既知の攻撃者チューニングターゲットに対して動いています。実装は初回デプロイで各開始しきい値をランダム化すべきで(SHOULD)、開始値の \[0.5×, 2×] にわたる log-uniform 分布から引きます(同等に: フリート全体で最も狭いデフォルトと最も広いデフォルトの間に 4× のスプレッドを持つ比率一様ジッター)。より狭い分布(例: ±30%、わずか 1.86× のスプレッドを与える)は、規律ある攻撃者が公開値の 0.7× にチューニングしフリートのすべてのジッターされたデプロイの下に留まることを許します。\[0.5×, 2×] にわたる log-uniform は攻撃者に 4× の範囲をカバーすることを強い、それは攻撃ボリュームで意味あるコストを課し始めます。**実装は、任意のしきい値が検証者の初回受理から 30 日を超えて出荷開始値のままであるとき `threshold_tuning_overdue` イベントをログまたはアラームすべきです(SHOULD)** — これは 30 日チューニングルールにテスト可能で監査可能なフックを与えます(それなしでは、ルールはオペレーターの勤勉さのみに依存し、勤勉さが失われると黙って失敗します)。 **なぜこのガイドが仕様と別か。** 具体的なしきい値を規範的デフォルトとして公開することは攻撃者にオラクルを渡します — 規律ある攻撃者は仕様を読み、公開値のちょうど下に留まるよう攻撃をチューニングします。規範的仕様は意図的に *ルールがどんな形状を持つか* を述べます。このガイドは *どんな数字から始めるか* を述べます。オペレーターはこれらを開始値として扱い、自身のトラフィックを観測し、調整しなければなりません(MUST)。 ## チューニングしているルール 検証者は新 keyid 受理圧力を追跡しなければならず(MUST)、レートが 4 つのしきい値の **いずれか**(最初にトリガーするもの)を超えるときアラートすべきです(SHOULD)。規範的仕様はこれら 4 つのしきい値をカテゴリーで名指します。このガイドは各カテゴリーの開始値を与えます。 ## 開始値 | # | Category | 開始式 | 捕捉するもの | | ----- | -------- | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | **a** | 短ウィンドウ比 | 新 keyid 受理レートの `24 時間移動平均の 3×` | 安定したベースラインに対する突然のスパイク — 古典的な「異常なトラフィックボリューム」シグナル。 | | **b** | 中ウィンドウ比 | `30 日 P95 の 2×` | 複数週のランプアップ攻撃。30 日 P95 はベースライントラフィックのテールに支配されるため、2〜3 週のランプは参照を攻撃にドリフトできない。 | | **c** | 長ウィンドウ比 | `90 日 P99 の 1.5×` | 複数月のランプアップ攻撃。30 日 P95 をドリフトさせる 60〜90 日の段階的侵害は、P99 テールがはるかにゆっくり動くため依然として 90 日 P99 をトリップする。 | | **d** | 比例上限 | `max(20 個の異なる新 keyid、10% × 30 日ユニーク keyid 数) per 5 分ウィンドウ` | 移動平均と P95/P99 値がゼロ近い(小規模オペレーター)疎トラフィック検証者、かつ任意のサイズのオペレーターの自動スケーリング。 | **これらは開始値であり、規範的デフォルトではありません。** 新規デプロイは初日にそれらを使えます。トラフィックベースラインが安定するにつれ、観測された偽陽性と偽陰性のレートに基づいて締めるか緩めてください。 ## ベースライン化方法論 しきい値をチューニングする前に、検証者のトラフィックのベースライン形状を確立してください: 1. **アラームなしで 30 日の新 keyid 受理を収集する。** レートをインストルメントするがオペレーターをページしない。 2. **デプロイの P50、P95、P99** を 5 分ウィンドウあたりの新 keyid 受理で計算する。 3. **30 日スライディングウィンドウあたりのユニーク keyid 数を追跡する。** これは条項 (d) の分母です。 4. **中央値とピークの正当なオンボーディングバッチを文書化する。** 日常的に 1 日 50 の新署名者をオンボードする(週 2 回 10 分ウィンドウにバッチ化)場合、条項 (d) の 20/5 分の固定フロアは厳しすぎます。最大の正当なバッチに合わせて上げてください。 ベースラインが分かれば、各条項 (a)/(b)/(c)/(d) はデプロイ内の具体的なしきい値になります。仕様の 4 つの OR 形状は、任意の 1 つの条項のトリップでアラートに十分であることを意味します — したがってしきい値は形状で一致する必要はなく、それぞれ異なる攻撃者パターンを閉じる必要があります。 ## 攻撃シナリオのウォークスルー ### シナリオ 1: 突然の大量侵害 攻撃者が週末に 100 の署名者鍵を侵害し、月曜朝から 100 すべてから同時に webhook を送り始める。 * **トリップするもの**: 条項 (a)。新 keyid 受理の 24 時間移動平均は約 0(安定した検証者上)。1 つの 5 分ウィンドウでの 100 の新 keyid はその `3×` より桁違いに上。 * **オペレーターが必要とするアラーム詳細**: どの条項 (a) か。トリアージチームが単一鍵スパイクではなく大量侵害パターンを探すべきと分かるように。 ### シナリオ 2: 忍耐強い複数週ランプ 攻撃者が週 1 に 5 鍵、週 2 に 10、週 3 に 20、週 4 に 40 を侵害 — 週ごとに倍増し、今日のレートが昨日の 2× を超えないため任意の「昨日の 3×」ルールの下に留まる。 * **トリップするもの**: 条項 (b)。30 日 P95 は最初の 3 週のベースライントラフィックに支配されるため、その `2×` はほぼ通常のピーク。週 4 までに 40 keyid/日は週次ベースラインの 8× で、P95 アンカーを十分に超える。 * **条項 (a) だけならミス**: はい。2× 日次ランプは 3× 短ウィンドウ MA の下に永続的に留まる。 ### シナリオ 3: 複数四半期の段階的侵害 攻撃者が 90 日間 1 日 1 鍵を侵害 — 今日のレートが昨日とほぼ等しいため任意の日次または週次比をトリガーしない。 * **トリップするもの**: 条項 (c)。90 日 P99 は攻撃よりはるかに古いベースライントラフィックにアンカーされる。ランプの最後の 2 週(76〜90 日)でさえ P99 の `1.5× ベースライン` を超えて登録される。 * **条項 (a) と (b) だけならミス**: はい。単調な遅いランプは 24 時間 MA と 30 日 P95 の両方をそれとともにドリフトさせる。 ### シナリオ 4: 疎トラフィック検証者、バースト攻撃 合計 20 のアクティブ署名者とゼロ近い新 keyid トラフィックを持つ検証者が、突然 5 分ウィンドウで 15 の新 keyid を見る。 * **トリップするもの**: 何も。比率ルール (a)/(b)/(c) はゼロ近いベースライン(`3× 0.01 = 0.03`)に対して比較し、正当な単一セラーオンボーディングを含む任意の正の受理でトリップする — したがって疎トラフィック検証者でアラームするにはノイズが多すぎる。条項 (d) の `max(20, 10%×20) = max(20, 2) = 20` 固定フロアは、発火前に 5 分ウィンドウあたり 20 を超える新 keyid を要求する。15 はフロアの下。 * **オペレーターが見るもの**: 何も。疎トラフィック検証者での 15 の新 keyid は通常範囲内。疎トラフィック検証者を運用するオペレーターは、日常オンボーディングが定期的にそれを超えるなら固定フロアを上げるべきで(SHOULD)、または日常オンボーディングが下に留まるならフロアを 20 のままにする(攻撃者の上限が ≤20/ウィンドウになり、合理的なウィンドウでの集約圧力を鋭く制限する)。 ### シナリオ 5: 大規模検証者の上限スケーリング 10,000 のアクティブ署名者を持つ検証者が 5 分ウィンドウで 500 の新 keyid を見る。 * **トリップするもの**: 条項 (d) からは何も。10% × 10,000 = 1,000。500 は比例フロアを超えない。検証者のベースラインに応じて、500/5 分が 24 時間移動平均または 30 日 P95 より実質的に上なら条項 (a) または (b) がトリップするかもしれない。 * **スケールで変わるもの**: 小規模検証者(100 署名者)では、500 の新 keyid は署名者ベース全体の 5× — 明らかに攻撃。条項 (d) の `max(20, 10%×100) = 20` フロアは 500 が 25× 超で即座に発火することを意味する。比例形状は自動スケールする。 ### シナリオ 6: オンボーディングバースト偽陽性 計画された火曜バッチで 200 の新セラーをオンボードする検証者が、バッチ中に条項 (a) または (d) をトリップする。 * **オペレーターがすること**: 条項 (d) の固定フロアを一時的に上げる(変更管理で文書化)、または既知のオンボーディングウィンドウでアラートをサイレンスする。バッチ後、フロアはベースラインに戻る。監査でき戻せるよう引き上げを文書化する。引き上げフロアウィンドウはできるだけ短く内部スコープに保つべき(SHOULD) — 公に発表されたオンボーディングウィンドウは攻撃者の計画シグナル(シナリオ 10 を参照)。 * **なぜ自動失効がここで間違いか**: 仕様の `Alarms SHOULD route to incident response, not automatic revocation` ルールはまさにこのケースのために存在する。機械導出可能な「攻撃対オンボーディング」は信頼できない。オペレーターコンテキストが区別シグナル。 ### シナリオ 7: 正当な鍵ローテーションストーム ピアセラーのルート CA が失効し、その 500 の署名エージェントすべてが 10 分ウィンドウ内で新しい `keyid` にローテートする。検証者は 1 つの 5 分ウィンドウで 500 の新 keyid を、次で 0 を見る。 * **トリップするもの**: 条項 (a) とおそらく (d)。形状はレートのみのレベルでシナリオ 1(突然の大量侵害)と区別できない。 * **オペレーターがすること**: アラームをトリアージし、ピアセラーの通知からイベント形状を認識し(CA 侵害インシデントは通常ピアに事前発表される)、インシデントレコードで正当とマークし、自動失効しない。ピアが事前発表しなかった場合、ピア連絡が確認するまでシナリオ 1 とまったく同様に扱う。**ピアの発表だけに基づいてアラームを先制的にサイレンスしない** — 侵害されたピア事前発表チャネル自体が攻撃者の戦術。アラームが発火しトリアージされることが多層防御レイヤー。 ### シナリオ 8: 薄い履歴ウィンドウ攻撃(デプロイ後 1〜90 日) 昨日デプロイされた検証者は 30 日 P95 データも 90 日 P99 データも持たない。条項 (b) と (c) はパーセンタイルウィンドウが成熟するまで条項 (d) フロアに優雅に劣化する。検証者が新しいと知る攻撃者は、最初の 90 日間条項 (d) の `max(20, 10%×count)` フロアの下に留まるランプを段階化し、その間条項 (a) のみが意味あるカバレッジを提供する。 * **トリップするもの**: 条項 (a) のみ — そして十分に大きい短ウィンドウスパイクでのみ。条項 (b)、(c)、(d) はすべてフロア支配ケースに劣化する。 * **オペレーターがすること**: 新しい検証者では、P95/P99 が成熟する間の最初の 90 日間、条項 (d) の絶対フロアを公開開始値の下に締めるべき(SHOULD)(例: 20 の代わりに 10)。これを永続的チューニングではなく文書化された初回デプロイ姿勢として扱う — パーセンタイルウィンドウが実データを持ったら成熟検証者フロアに戻す。 * **なぜウォームアップ中に条項 (b)/(c)/(d) が独立でないか**: 条項 (c) は明示的に `1.5× max(observed_P99, clause_d_floor)` に劣化するため、1〜90 日の間、条項 (c) と (d) は冗長。これはルール形状の既知の制限。締めたフロア姿勢が緩和策。 ### シナリオ 9: 断続的低ボリューム攻撃(ルール形状の制限) 攻撃者が 500 鍵を侵害し、フリート全体で 30 分ごとに 1 つの新 keyid を発行 — 約 48/日。`max(20, 10% × 200 署名者数) = 20`/5 分の条項 (d) フロアに対して、各 5 分ウィンドウは 0 か多くて 1〜2 の新 keyid を見る。30 日で攻撃は 1,440 の新 keyid を受理する — それが条項 (b) が比較する 30 日ユニーク keyid 数の一部になる。攻撃はベースラインに事前に焼き込まれている。 * **トリップするもの**: 何も。 * **オペレーターが見るもの**: 30 日にわたる上昇したユニーク keyid 数だが、単一ウィンドウアラームは発火しない。 * **なぜこれが既知の制限か**: 受理圧力ルールはボリュームスパイク攻撃を閉じ、長ウィンドウにわたって平滑化された低レート長期間攻撃を閉じない。**keyid ごとの上限(ステップ 9a)と集約キャッシュ上限はこのギャップを閉じない** — それらはキャッシュサイズを制限し、鍵集団の成長ではない。1,440 の新 keyid/月は 10M 集約上限の約 0.014%。レートウィンドウレベルでは、各条項 (a/b/c/d) はゼロでトリップし集約上限アラームは決して発火しない。脅威モデルに緩やかに滴る鍵集団の成長を持つオペレーターは **アプリケーションレベル検出を重ねなければならない(MUST)**(署名者評判スコアリング、「請求期間あたり配信されるシグナル」のようなビジネス上意味あるウィンドウにわたるセラーごとのトラフィック異常検出、宣言されたフリートサイズ期待に対して追跡される新 keyid 受理)。受理圧力ルールと上限だけに依存することは、攻撃クラスが仕様で認められているが実際の検出がない検証者を出荷することになる。 ### シナリオ 10: オンボーディングウィンドウタイミング攻撃 攻撃者が検証者オペレーターの公開発表(製品ローンチ、会計年度境界、プラットフォームパートナーシップ)を監視する。オペレーターはシナリオ 6 に従い予定された火曜オンボーディングウィンドウのため条項 (d) のフロアを `200` に上げる。攻撃者は大量侵害をその火曜にタイミングし、一時的に上げられたフロアに乗る。 * **トリップするもの**: 上げられたフロアウィンドウ中は何も。 * **オペレーターがすること**: 上げられたフロアウィンドウ中、条項 (d) が意図的に緩くても、条項 (a)/(b)/(c) のアラームは **自動抑制ではなく必須の人間レビュー** にエスカレートすべき(SHOULD)。上げられたフロアウィンドウをできるだけ短く内部スコープに保つ — 攻撃者がスケジュールできる形で「新セラーオンボーディングが日付 X に起こる」と公に発表するのを避ける。公開発表が避けられない場合(規制開示、顧客向けローンチ)、ウィンドウ中に帯域外検出を増やすべき(SHOULD)(トラフィックパターン分析、セラークレームのクロス検証、リクエストボディサンプリング)。 ### シナリオ 11: 成熟検証者でのベースラインリセット(フェイルオーバー、キャッシュ再構築、設定変更) 90 日の安定した P95/P99 データを持つ成熟検証者が、ベースライン計算キャッシュが空のスタンバイプールにフェイルオーバーする。条項 (b)/(c) は再構築の間、条項 (d) フロア支配ケースに劣化する — シナリオ 8(薄い履歴ウィンドウ)を反映するが、成熟しているはずの検証者で。フェイルオーバーイベントが起こることを知る攻撃者(公開ステータスページインシデント、予定メンテナンスウィンドウ、観測可能な応答時間変化)は、再構築ウィンドウ中に着地するよう攻撃をタイミングできる。 * **トリップするもの**: 条項 (a) のみ(シナリオ 8 と同じ)。条項 (b)/(c) はベースラインデータを持たない。 * **オペレーターがすること**: *一時的な* 薄い履歴姿勢として扱う。空のキャッシュから再構築するのではなく、フェイルオーバー全体でベースライン統計状態を永続化する(Redis / 共有 dedup サービス) — 仕様がクロスエンドポイントスコーピング下のリプレイキャッシュに既に要求するのと同じインフラ選択がこれも修正する。永続化が不可能なら、再構築ウィンドウ中に条項 (d) の絶対フロアを締め、シナリオ 10 に従い (a)/(b)/(c) アラームを人間レビューにエスカレートする。 * **なぜこれがシナリオ 8 と仕様上異なるか**: シナリオ 8 は 90 日で安定すると期待される初回デプロイ姿勢。シナリオ 11 は、オペレーターがフェイルオーバー全体でベースラインを永続化しなければ無期限に再発しうる成熟検証者の運用イベント姿勢。仕様は永続化選択を義務化できない(デプロイ内部)。チューニングガイドは、オペレーターが緩和責任を負う既知の攻撃タイミング機会としてそれを呼び出せる。 ## 考慮すべきチューニング調整 | Observation | Adjustment | | -------------------------------------- | ---------------------------------------------------------------------------------------------------- | | 正当なバースト中に条項 (a) から偽陽性が多すぎる | 条項 (a) の比率を `3×` から `4×` または `5×` に上げる。補償のため条項 (b)/(c)/(d) のしきい値を下げない — それらは異なる攻撃者形状を捕捉する。 | | 条項 (d) が日常オンボーディングで発火する | 条項 (d) の固定フロア成分を最大の正当なバッチサイズに合わせて上げる。`10%×30d-unique-count` 比例部分は変えないままにする。 | | 条項 (c) が 60 日未満実行するレッドチーム演習中に決して発火しない | 期待通り — 条項 (c) は複数月アンカー。レッドチーム演習は、条項 (c) が 90 日 P99 に正しく配線されていることを検証するため 60 日の遅いランプシナリオを含むべき(SHOULD)。 | | アラームが同じイベントに条項 (a) と (d) の両方が発火したことを示す | アラームペイロードで最初にトリップした条項をレポートする(仕様に従い)。両方の条項が表面化することは情報的であり、バグではない。 | | 検証者が意味ある P99 データを持つには小さすぎる | 条項 (c) は `1.5× max(observed_P99, clause_d_floor)` に優雅に劣化する — 決して比例上限より低くならない。90 日追跡し、その後 P99 が意味を持つ。 | ## してはいけないこと * **チューニングしたしきい値を外部に公開しない。** しきい値はデプロイ内部の運用パラメーター。このルールは 3 つのオーディエンスを区別する: * **公開開示**(ブログ投稿、マーケティングコピー、公開設定リポジトリ、オープンソースデフォルト、カンファレンストーク): **禁止**。これはこのガイドが閉じるために存在する攻撃者オラクル。 * 資格ある**セキュリティ監査人、規制当局、契約レッドチームへの NDA 下での証明済み開示**: **許可**。検出姿勢評価自体が多層防御慣行で、SOC 2 / ISO 27001 監査がそれを要求するかもしれない。NDA スコープは再配布を制限しエンゲージメント終了時の削除を義務付けるべき(SHOULD)。 * **内部オペレーターランブック、インシデントレスポンスランブック、バージョン管理されたオペレーター設定**: **必須**。検出チームは効果的にトリアージするため値を必要とし、インシデント後フォレンジックはイベント時のしきい値を知ることを要求する。 * **4 つのしきい値すべてを同じ値にチューニングしない。** 各条項は異なる攻撃者パターンを捕捉する。それらを崩すと検出カバレッジを失う。 * **アラームで自動失効しない。** アラームはインシデントレスポンスのシグナルであり、修復アクションではない。受理圧力アラームでの署名者鍵の自動失効はサービス拒否ベクターを作る: 正当な新署名者オンボーディングを駆動する任意の当事者がアラームをトリップし大量失効を引き起こせる。 * **開始値をデプロイ設定にハードコードしない。** 各しきい値をチューナブルパラメーター(例: 環境変数、設定ファイル)にし、オペレーターがコード変更なしに調整できるようにする。ハードコードされた開始値は事実上のオペレーター可視デフォルトになり、攻撃者オラクルを再導入する。 ## 関連 * [Webhook Security → Webhook replay dedup sizing](/docs/building/by-layer/L1/security#webhook-replay-dedup-sizing) — このガイドがチューニングするルールの規範的仕様。15 チェック検証者フローの直下の §Webhook replay dedup sizing 見出しまでスクロール。「New-keyid admission pressure」箇条書きが、チューニングガイドが開始値で埋める 4 つのカテゴリーを持つルール。 * [Webhook 検証者チェックリスト](/docs/building/by-layer/L1/security#webhook-callbacks) — 完全な 15 チェックフロー。ステップ 14b(ロギング規律)はステップ 14(ボディの整形式性)下のサブステップ。そのサニタイズルール(非印字分類、32 バイト UTF-8 コードポイント安全トランケーション、4 でのカウント上限)は、このガイドがアラームが運ぶと仮定する診断情報に適用される。 # アカウント状態 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L2/account-state AdCP アカウント状態モデル: アカウントがカタログ、クリエイティブ、オーディエンス、イベントソース、キャンペーンを保持する方法。同期タスク、アップサートセマンティクス、非同期承認ワークフロー。 # アカウント状態 AdCP のアカウントはステートフルなコンテナです。バイヤーがセラーのプラットフォームでキャンペーンを実行する前に、アカウントに状態を構築します: プロダクトカタログ、クリエイティブアセット、オーディエンスリスト、コンバージョントラッキング。各状態には独自の同期タスク、独自の承認ワークフロー、独自のライフサイクルがあります。 これは AdCP の以前のバージョンとは異なります。以前はアカウントが請求の参照であり、ほとんどの操作がステートレスでしました。AdCP 3.0 では、アカウントがすべてを結びつける中心的なオブジェクトです。 ## 状態ドメイン アカウントは6つのカテゴリの状態を保持し、それぞれが専用のタスクで管理されます: | ドメイン | 同期タスク | 管理対象 | ライフサイクル | | ------------ | -------------------- | ----------------------------------- | ---------------------------------- | | **アイデンティティ** | `sync_accounts` | バイヤーが誰か、どのブランド、請求条件 | 一度セットアップ、まれに更新 | | **カタログ** | `sync_catalogs` | プロダクトフィード、インベントリ、ストア、プロモーション、オファリング | 継続的 — フィードは毎時/毎日更新 | | **クリエイティブ** | `sync_creatives` | フォーマット固有のマニフェストを持つクリエイティブアセット | キャンペーンごと、必要に応じて更新 | | **オーディエンス** | `sync_audiences` | ファーストパーティ CRM オーディエンスリスト | 増分 — メンバーを時間とともに追加/削除 | | **イベントソース** | `sync_event_sources` | コンバージョントラッキング設定(ピクセル、S2S、アプリイベント) | ソースごとに一度セットアップ、まれに変更 | | **ガバナンス** | `sync_governance` | このアカウントのガバナンスエージェント設定 | アカウントごとに一度セットアップ、ガバナンスエージェント変更時に更新 | | **キャンペーン** | `create_media_buy` | パッケージとターゲティングを持つアクティブキャンペーン | 準備できたら作成、フライト中に更新 | 各同期タスクは同じパターンに従います: * **アップサートセマンティクス** — アイテムは ID でマッチされ、新しければ作成、存在すれば更新 * **ディスカバリーモード** — アイテム配列を省略してアカウントに既存のものを確認 * **非同期承認** — プラットフォームはアクティベート前にアイテムをレビューすることがあります * **アイテムごとのステータス** — 個別アイテムは独立して成功または失敗できます ## セットアップシーケンス 典型的なバイイングワークフローは依存関係の順でアカウント状態を構築します。各ステップは前のステップが完了していることが必要です: ```mermaid theme={null} flowchart LR A[sync_accounts] --> B[sync_catalogs] A --> C[sync_event_sources] B --> D[sync_creatives] C --> D A --> E[sync_audiences] A --> G[sync_governance] D --> F[create_media_buy] E --> F G --> F ``` ### 1. アカウントを確立します `sync_accounts` はバイヤーが誰で、どのように支払うかを宣言します。セラーは関係を認め、ステータスと請求条件を返します。 ```json theme={null} { "accounts": [{ "brand": { "domain": "acme-corp.com" }, "operator": "pinnacle-media.com", "billing": "operator" }] } ``` ### 2. カタログを同期します `sync_catalogs` はプロダクトデータをアカウントで利用可能にします。フォーマットは `assets` 配列の `catalog` アセットタイプを通じて必要なカタログタイプを宣言するため、バイヤーはクリエイティブを送信する前に適切なフィードを同期します。 ```json theme={null} { "account": { "account_id": "acct_001" }, "catalogs": [ { "catalog_id": "product-feed", "type": "product", "url": "https://feeds.acme.com/products.xml", "feed_format": "google_merchant_center", "update_frequency": "daily" }, { "catalog_id": "store-locations", "type": "store", "url": "https://feeds.acme.com/stores.json", "feed_format": "custom", "update_frequency": "weekly" } ] } ``` プラットフォームは各フィードを取得して検証します。アイテムは承認、拒否、または警告付きでフラグされることがあります — Google Merchant Center がプロダクトリスティングをレビューするのに似ています。 ### 3. イベントソースを設定します `sync_event_sources` はコンバージョントラッキングを設定して、プラットフォームが広告露出に結果を帰属させられるようにします。 ```json theme={null} { "account": { "account_id": "acct_001" }, "event_sources": [{ "event_source_id": "web-pixel", "name": "Website Conversions", "type": "pixel", "events": ["purchase", "add_to_cart", "lead"] }] } ``` ### 4. ガバナンスを設定します [`sync_governance`](/docs/accounts/tasks/sync_governance) はアカウントにガバナンスエージェントを登録します。設定されると、ガバナンスをサポートするセラーはメディアバイを確定する前に [`check_governance`](/docs/governance/campaign/tasks/check_governance) を呼び出します。 ```json theme={null} { "account": { "account_id": "acct_001" }, "governance_agents": [{ "agent_url": "https://governance.acme-corp.com/adcp", "domains": ["campaign", "creative", "content_standards"] }] } ``` 稼働中のアカウントでガバナンスエージェントを変更すると、すべてのアクティブキャンペーンに影響します。ガバナンスエージェントが削除されると、セラーはそのドメインについて `check_governance` の呼び出しを停止します。新しいエージェントが追加されても、既存のキャンペーンは遡及的に検証されません。更新されたガバナンス設定を通るのは新しいトランザクションのみです。 ### 5. クリエイティブを同期します `sync_creatives` は、ステップ 2 で同期されたカタログを参照するクリエイティブアセットを送信します。カタログ駆動フォーマットの場合、クリエイティブの `catalogs` フィールドはアイテムをインラインで埋め込む代わりに、`catalog_id` で同期されたカタログを参照します。 ```json theme={null} { "account": { "account_id": "acct_001" }, "creatives": [{ "creative_id": "product-carousel", "format_id": { "agent_url": "https://creative.retailer.com/adcp", "id": "product_carousel_with_inventory" }, "catalogs": [{ "catalog_id": "product-feed", "type": "product", "tags": ["summer"] }], "assets": { "banner_image": { "url": "https://cdn.acmecorp.com/carousel-hero.jpg", "width": 1200, "height": 628 } } }] } ``` ### 6. オーディエンスをアップロードします `sync_audiences` はターゲティング用のファーストパーティオーディエンスリストをアップロードします。送信前にメンバーはハッシュ化されます。 ```json theme={null} { "account": { "account_id": "acct_001" }, "audiences": [{ "audience_id": "high-value-customers", "name": "High Value Customers", "add": [ { "hashed_email": "a1b2c3..." }, { "hashed_email": "d4e5f6..." } ] }] } ``` ### 7. キャンペーンを作成します すべての状態が整ったら、`create_media_buy` が同期された状態を参照するキャンペーンを活性化します: ```json theme={null} { "account": { "account_id": "acct_001" }, "name": "Summer Product Launch", "packages": [{ "product_id": "sponsored-products", "creative_ids": ["product-carousel"], "targeting_overlay": { "audiences": { "include": ["high-value-customers"] } } }] } ``` ## ディスカバリー すべての同期タスクは**ディスカバリーモード**をサポートします: アイテム配列なしでタスクを呼び出して、アカウントに既存の状態を確認します。これはバイイングエージェントがセラーがブランドについて既に知っていることを学ぶ方法です。 ```json theme={null} // このアカウントにはどんなカタログがあるか? { "account": { "account_id": "acct_001" } } // レスポンス: アカウントに既存のカタログ { "catalogs": [ { "catalog_id": "product-feed", "action": "unchanged", "item_count": 1250 }, { "catalog_id": "store-locations", "action": "unchanged", "item_count": 45 } ] } ``` これが重要な理由: セラーはすでに他のソースからブランドデータを持っている可能性があります — 小売業者はコマースプラットフォームからブランドのプロダクトカタログを持っているかもしれないし、パブリッシャーは以前のキャンペーンからクリエイティブを持っているかもしれません。ディスカバリーにより、バイヤーはすべてを再アップロードするのではなく、既存の状態の上に構築できます。 ## 承認ワークフロー 同期タスクは多くの場合非同期です。プラットフォームはアイテムをアクティブにする前にレビューする必要がある場合があります: * **カタログ**: プロダクトリスティングはコンテンツポリシーチェックを経ます。アイテムは承認、拒否、または警告付きでフラグされることがあります。 * **クリエイティブ**: 生成クリエイティブは人間の承認が必要です。従来のクリエイティブはポリシーレビューが必要な場合があります。 * **オーディエンス**: プラットフォームはハッシュ化された識別子をユーザーベースと照合する時間が必要です。 * **イベントソース**: コンバージョントラッキングはピクセル検証が必要な場合があります。 すべての同期タスクは処理完了時のウェブフックコールバック用に `push_notification_config` をサポートします。長時間実行する操作の場合、プラットフォームは非同期ステータス更新(working、input-required、submitted)を返し、バイヤーがポーリングするかウェブフックで受け取ります。 ## 状態の依存関係 一部の状態は他の状態に依存します。プラットフォームはこれらの依存関係を強制します: * **クリエイティブはカタログを参照する** — `catalog_id: "product-feed"` を使用するクリエイティブは、そのカタログが最初に同期されていることが必要 * **キャンペーンはクリエイティブとオーディエンスを参照する** — `create_media_buy` は参照された `creative_ids` とオーディエンス ID がアカウントに存在することが必要 * **イベントソースは最適化を可能にする** — パッケージの最適化ゴールはアトリビューション用にイベントソースを参照します 依存関係が欠けている場合、プラットフォームは最初に何を同期する必要があるかを説明するエラーを返します。 ## ステートレス vs ステートフル操作 すべてのものがアカウント状態を必要とするわけではありません。一部のタスクはステートレスクエリです: | ステートレス(アカウント不要) | ステートフル(アカウント必要) | | ----------------------------------- | --------------------------------- | | `get_products` — インベントリを発見 | `create_media_buy` — インベントリを購入 | | `list_creative_formats` — フォーマットを発見 | `sync_creatives` — クリエイティブをアップロード | | `get_signals` — シグナルを発見 | `activate_signal` — シグナルを活性化 | | `get_adcp_capabilities` — 機能を発見 | `sync_catalogs` — カタログをアップロード | パターン: **発見はステートレス、実行はステートフル**。アカウントなしでセラーのインベントリを閲覧できます。購入するにはアカウントが必要です。 ## 関連ドキュメント * **[アカウントとエージェント](/docs/building/by-layer/L2/accounts-and-agents)** — アカウントアイデンティティ、請求モデル、`sync_accounts` の詳細 * **[非同期操作](/docs/building/by-layer/L3/async-operations)** — 非同期承認ワークフローの仕組み * **[ウェブフック](/docs/building/by-layer/L3/webhooks)** — 非同期操作完了時の通知受け取り * **[カタログ](/docs/creative/catalogs)** — パブリッシャーが広告でレンダリングするアイテムを提供する型付きデータフィード # アカウントとエージェント Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L2/accounts-and-agents AdCP アカウントとエージェント: すべてのトランザクションにおける4つのエンティティ(ブランド、アカウント、オペレーター、エージェント)、アカウント ID 名前空間、バイヤー宣言アカウント、請求設定。 AdCP はすべての請求可能な操作において4つのエンティティを区別する: | エンティティ | 問い | 識別方法 | | ---------- | ----------------------- | ----------------------------------------------------------------------------------- | | **ブランド** | 誰のプロダクトが広告されるか? | ブランド参照: `domain` + オプションの `brand_id`([brand.json](/docs/brand-protocol/brand-json)) | | **アカウント** | 誰が請求されるか? どのレートが適用されるか? | [アカウント参照](#account-references) | | **オペレーター** | 誰がブランドのために操作するか? | ドメイン(例: `pinnacle-media.com`) | | **エージェント** | どのソフトウェアが購入を配置するか? | 認証済みセッション | **ブランド** — プロダクトまたはサービスが宣伝される広告主。`brand` 参照(`domain` + オプションの `brand_id`)で識別され、`/.well-known/brand.json` を通じて解決されます。シングルブランドの企業はドメインのみを使用する(`brand_id` なし)。 **アカウント** — バイヤーとセラーの間の請求関係。レートカード、支払条件、信用限度、請求書を受け取る人を決定します。すべての請求可能な操作にはアカウント参照が必要だ — セラーまたは上流プラットフォームが正規のアカウント名前空間を所有する場合はセラーが割り当てた `account_id`、そのタプルがバイヤー宣言アカウントの耐久性のあるプロトコルキーである場合は自然キー(`brand`、`operator`)。サンドボックスアカウントは同じモデルに従う — アカウント ID 名前空間は `list_accounts` またはアウトオブバンドのセットアップからの既存のサンドボックス ID を使用し、バイヤー宣言サンドボックスは `sandbox: true` を含む自然キーを使用します。 **オペレーター** — 購入を主導するエンティティ — エージェンシートレーディングデスク、ブランドの内部チーム、または広告主の代わりに行動する別のエンティティ。ドメインで識別され、`brand.json` の[認可オペレーター](#authorized-operators)を通じて検証可能です。 **エージェント** — 購入を配置してキャンペーンを管理するソフトウェア。セラーで認証し、複数のオペレーターとブランドのために操作することがあります。 完全な商業モデルについては[アカウントプロトコルの概要](/docs/accounts/overview)を、タスクリファレンスについては[sync\_accounts](/docs/accounts/tasks/sync_accounts)を参照。 ## セラーが宣言するもの セラーは [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities#account) の `account` セクションを設定します: **1. どの請求モデルをサポートするか?**(`supported_billing`) バイヤーはすべての `sync_accounts` エントリで `billing` としてこれらの値の1つを渡す必要があります。セラーは受け入れるか拒否するかを決める。 | 請求 | 請求される対象 | ユースケース | | ------------ | --------------------------- | --------------------------------------------------------- | | `operator` | オペレーター(エージェンシーまたはブランドが直接購入) | オペレーターが自分の条件で購入 | | `agent` | エージェント | エージェントがブランド間で請求を統合 | | `advertiser` | 広告主が直接 | オペレーターが発注するが広告主が支払う(ソーシャルプラットフォームや DACH の B2B ワークフローで一般的) | **2. オペレーターレベルの認証を必要とするか?**(`require_operator_auth`) このフィールドが認証モデルとアカウント参照の形状を決定する: `false`(デフォルト)の場合 — **バイヤー宣言アカウント**: セラーはエージェントを信頼します。エージェントは一度認証して `sync_accounts` を通じてアカウントを宣言します。後続のリクエストでは、バイヤーは自然キー(`brand` + `operator`)を渡し、セラーが内部で解決します。 `true` の場合 — **アカウント ID 名前空間**: 各オペレーターはセラーと直接認証する必要があります。エージェントはオペレーターごとにクレデンシャルを取得する — セラーの `authorization_endpoint` を使った OAuth、またはアウトオブバンドの API キーで。後続のリクエストはセラーが割り当てた `account_id` を渡します。クレデンシャルが複数のアカウントにアクセスし得る場合、セラーは `list_accounts` を公開しなければならず(MUST)、バイヤーは最初のアカウントスコープリクエストの前に明示的なアカウントを解決しなければなりません(MUST)。クレデンシャルが正確に 1 つのアカウントに束縛される場合、セラーはそのシングルトンを返す `list_accounts` を公開すべきです(SHOULD)。セラーが `list_accounts` を省略してもよい(MAY)のは、別の宣言されたパスまたはアウトオブバンドのオンボーディングを通じて同じ明示的なアカウント ID を供給する場合のみです。 サンドボックスの場合、パスはアカウント名前空間に従う: アカウント ID 名前空間は `list_accounts` またはアウトオブバンドのセットアップからの既存のテストアカウントを使用し、バイヤー宣言アカウントは `sandbox: true` を含む `sync_accounts` を使用して自然キーで参照します。 セラーは `account_financials: true` を宣言して [`get_account_financials`](/docs/accounts/tasks/get_account_financials) を通じてアカウントレベルの財務データ(支出、信用、請求書)を公開することもできます。これはオペレーター請求アカウントにのみ適用されます。 **ケイパビリティの例:** ```json theme={null} { "account": { "require_operator_auth": false, "supported_billing": ["operator", "agent"] } } ``` `advertiser` 請求をサポートするセラーはそれを明示的に宣言します: ```json theme={null} { "account": { "require_operator_auth": false, "supported_billing": ["operator", "agent", "advertiser"] } } ``` これらのフィールドは一般的なパターンに組み合わさる。 ## セラーパターン どのようなプラットフォームから購入するか? それがアカウントセットアップパターンを決定します。 | プラットフォームタイプ | アカウントパターン | `require_operator_auth` | `supported_billing` | | ------------------------------------- | ------------------ | ----------------------- | ------------------------------------------ | | [ソーシャル / ウォールドガーデン](#social-platform) | 上流管理のアカウント ID 名前空間 | `true` | `["operator"]` | | [ダイレクトパブリッシャー](#direct-publisher) | バイヤー宣言アカウント | `false` | `["operator"]` または `["operator", "agent"]` | | [DSP / プログラマティック](#dsp--programmatic) | バイヤー宣言アカウント | `false` | `["agent"]` | ### ソーシャルプラットフォーム オペレーターはすでにプラットフォーム上にアカウントを持っている — 広告アカウント、ビジネスマネージャー、セルフサービスダッシュボード。上流プラットフォームがそのクレデンシャルでアクセス可能な正規のアカウント名前空間を所有します。エージェントはオペレーターのクレデンシャルを取得(OAuth または API キーで)し、オペレーターごとのセッションを開き、`list_accounts` を通じて明示的なアカウントを解決し、返された `account_id` 値を使用します。プラットフォームはオペレーターに直接請求します。 **ケイパビリティ:** ```json theme={null} { "account": { "require_operator_auth": true, "supported_billing": ["operator"], "authorization_endpoint": "https://seller.example.com/oauth/authorize" } } ``` **バイヤーワークフロー:** 1. `get_adcp_capabilities` を呼び出す — `require_operator_auth: true` と `authorization_endpoint` を確認 2. 各オペレーターについて: a. オペレーターのクレデンシャルを取得(`authorization_endpoint` を使った OAuth、またはアウトオブバンドの API キー) b. オペレーターのクレデンシャルで新しいセッションを開く c. `list_accounts` を呼び出してそのクレデンシャルで見えるアカウントを発見する 3. 人間またはポリシーがリストから正しいアカウントを選択する 4. オペレーターのセッションと `{ "account_id": "..." }` を使って `get_products` / `create_media_buy` を呼び出す **list\_accounts レスポンス:** ```json theme={null} { "accounts": [{ "account_id": "acc_spark_social_001", "name": "Spark paid social", "brand": { "domain": "nova-brands.com", "brand_id": "spark" }, "operator": "pinnacle-media.com", "status": "active", "billing": "operator", "account_scope": "operator_brand" }] } ``` 後続の呼び出しは発見された ID を使用する: ```json theme={null} { "account": { "account_id": "acc_spark_social_001" } } ``` **重要ポイント:** エージェントのクレデンシャルではなく、オペレーターのクレデンシャルがそのセッションのすべての呼び出しを認可します。バイヤーは 3.0.x のアカウント ID モデルでは AdCP を通じてアカウントを宣言または作成しません。`list_accounts` は上流の名前空間をミラーします。セラーが `sync_accounts` を公開する場合、それは既存の `account_id` に対する設定更新のためだけであり、将来の明示的なケイパビリティがアカウント ID プロビジョニングを宣言しない限り、自然キーのプロビジョニングではありません。 ### ダイレクトパブリッシャー パブリッシャーはエージェントを信頼するが、オペレーターに直接請求します。エージェントは `sync_accounts` を通じてアカウントをセットアップする — オペレーターごとのログインは不要。アカウントはアクティブになる前に人間の承認(信用調査、法的合意)が必要なことがあります。 多くのパブリッシャーはエージェント請求も受け入れる(`supported_billing: ["operator", "agent"]`)。バイヤーはアカウントごとに選択する — ダイレクト関係のあるオペレーターは `billing: "operator"` を使用し、それ以外は `billing: "agent"` を使用します。セラーが特定のアカウントに対して要求された請求をサポートしない場合、リクエストを拒否し、エージェントは別のモデルで再送信します。 **ケイパビリティ:** ```json theme={null} { "account": { "supported_billing": ["operator", "agent"] } } ``` **バイヤーワークフロー:** 1. `get_adcp_capabilities` を呼び出す — `require_operator_auth` が欠如(デフォルトは `false`)を確認 2. 各ブランド/オペレーターペアに対して `sync_accounts` を呼び出す 3. アカウントステータスが `active` になるのを待つ — 人間が `setup.url` で信用/法的手続きを完了する必要がある場合があります 4. `account` 参照を使って `get_products` を呼び出す 5. `account` 参照を使って `create_media_buy` を呼び出す **sync\_accounts リクエスト — ブランドが直接購入:** ```json theme={null} { "accounts": [{ "brand": { "domain": "acme-corp.com" }, "operator": "acme-corp.com", "billing": "operator" }] } ``` セラーはリクエストを認め、プロビジョニング前にセットアップが必要: ```json theme={null} { "accounts": [{ "brand": { "domain": "acme-corp.com" }, "operator": "acme-corp.com", "action": "created", "status": "pending_approval", "billing": "operator", "account_scope": "brand", "setup": { "url": "https://seller.example.com/advertiser-onboard", "message": "Complete advertiser registration and credit application" } }] } ``` セラーは関係 `(brand: "acme-corp.com", operator: "acme-corp.com", billing: "operator")` を認めたが、アカウントはアクティブになる前にレビューが保留中です。Acme Corp の担当者が URL でセットアップを完了します。進捗を確認するため、エージェントは次のいずれかを行う: * 同じ自然キーで `sync_accounts` を再呼び出す — セラーが更新されたステータスを返す * リクエストに `push_notification_config` が提供されていた場合はウェブフック通知を受け取ります **重要ポイント:** `pending_approval` は通常のパスです。すべてのバイヤーはセラーとダイレクト関係が必要です。 **請求拒否 — オペレーター請求が利用不可:** セラーは一般的にオペレーター請求をサポートするが、すべてのオペレーターに対してサポートしない場合があります。ここで、エージェントはダイレクト関係のないオペレーターに対してオペレーター請求をリクエストする: ```json theme={null} { "accounts": [{ "brand": { "domain": "acme-corp.com" }, "operator": "acme-corp.com", "billing": "operator" }] } ``` セラーはこのオペレーターにダイレクト請求関係がないためリクエストを拒否: ```json theme={null} { "accounts": [{ "brand": { "domain": "acme-corp.com" }, "operator": "acme-corp.com", "action": "failed", "status": "rejected", "errors": [{ "code": "BILLING_NOT_SUPPORTED", "message": "Operator billing is not available for this account. Re-submit with billing: \"agent\"." }] }] } ``` エージェントは `billing: "agent"` で再送信するか、このセラーではオペレーター請求が利用できないことをバイヤーに伝える。請求はサイレントに再マッピングされることはない。 ### DSP / プログラマティック すべての請求はエージェントを通じて流れる。エージェントはプラットフォームとの継続的な関係を持ち、すべてのブランドとオペレーターにわたって請求を統合します。アカウントは即座に作成される — 人間の承認は不要です。 **ケイパビリティ:** ```json theme={null} { "account": { "supported_billing": ["agent"] } } ``` **バイヤーワークフロー:** 1. `get_adcp_capabilities` を呼び出す — `supported_billing: ["agent"]` を確認 2. `billing: "agent"` で各ブランド/オペレーターペアに `sync_accounts` を呼び出す 3. アカウントは即座にアクティブ — 人間の承認は不要 4. `account` 参照を使って `get_products` / `create_media_buy` を呼び出す **sync\_accounts リクエスト:** ```json theme={null} { "accounts": [{ "brand": { "domain": "nova-brands.com", "brand_id": "spark" }, "operator": "pinnacle-media.com", "billing": "agent" }] } ``` アカウントは即座にアクティブ: ```json theme={null} { "accounts": [{ "brand": { "domain": "nova-brands.com", "brand_id": "spark" }, "operator": "pinnacle-media.com", "action": "created", "status": "active", "billing": "agent", "account_scope": "operator_brand" }] } ``` **重要ポイント:** エージェントは統合された単一の請求書を受け取ります。ブランドごとのアカウントはレポートの粒度を提供するが、請求は一元化されます。 ## 認可オペレーター ブランドは `/.well-known/brand.json` の `authorized_operators` フィールドを通じて誰が代表できるかを宣言します。セラーは `sync_accounts` を処理する際にこれに対してオペレーターを検証すべきです。 ```json theme={null} { "house": { "domain": "nova-brands.com", "name": "Nova Brands" }, "brands": [ { "id": "spark", "names": [{"en": "Spark"}] }, { "id": "glow", "names": [{"en": "Glow"}] } ], "authorized_operators": [ { "domain": "pinnacle-media.com", "brands": ["spark", "glow"], "countries": ["US", "GB", "DE"] }, { "domain": "summit-agency.jp", "brands": ["spark"], "countries": ["JP"] }, { "domain": "nova-brands.com", "brands": ["*"] } ] } ``` | フィールド | 必須 | 説明 | | ----------- | --- | ---------------------------------------------- | | `domain` | はい | オペレーターのドメイン | | `brands` | はい | このオペレーターが代表できるブランド ID。`["*"]` はすべてのブランドを意味します。 | | `countries` | いいえ | ISO 3166-1 alpha-2 国コード。グローバル認可の場合は省略します。 | ### 検証フロー 1. `{brand.domain}/.well-known/brand.json` を解決します 2. `authorized_operators` でマッチする `domain` と `brands` 内のブランドを確認 3. 見つかった場合 → 進む(アカウントはまだ信用/法的承認が必要なことがあります) 4. 見つからない場合 → アカウントを拒否(`action: "failed"`)または手動レビュー用に `pending_approval` を返す 検証は信頼シグナルであり、ゲートではありません。セラーは `brand.json` でオペレーターを見つけることでプロビジョニングを迅速化できます。オペレーターがリストにない場合でも、セラーは独自のレビュープロセスを通じて承認できます。 **自己認可は暗黙的です。** `operator` ドメインがブランドのドメインと一致する場合、ブランドが直接操作している — `authorized_operators` へのリストは不要です。 `authorized_operators` はブランドとその代わりに操作する人との間のインターフェースをモデル化します。内部のエージェンシー階層はモデル化しません。 ## バイヤーエージェントのアイデンティティ `authorized_operators` は、オペレーターがブランドを代表することを許可されているかどうかをセラーに伝えます。しかし、呼び出しを行う*エージェント*が誰か、そのエージェントとどんな商業関係が記録されているかは伝えません。それらは別の問いであり、セラーはプロビジョニング前に両方を確認します。 すべての `sync_accounts` リクエストで 2 つのレイヤーが動作します: | レイヤー | 問い | どこに存在するか | | ------------------ | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **エージェントアイデンティティ** | どのバイヤーエージェントがこのリクエストを発行したか? | セラーのオンボーディングレコード。署名付きリクエストの `agent_url`([リクエスト署名](/docs/building/by-layer/L1/security#signed-requests-transport-layer)使用時)またはエージェントが提示した bearer / API キー / OAuth クレデンシャルで照会される。署名またはクレデンシャルがアイデンティティを確立する。認可は別のチェック。 | | **ブランド-オペレーター認可** | リクエストで名指しされたオペレーターはブランドのために行動する認可を持つか? | `brand.json` の `authorized_operators`(上記)。リクエストが署名されているかどうかに関わらず検証される。 | 両方のレイヤーが通過しなければなりません(MUST)。オンボード済みエージェントからの署名付きリクエストでも、認可されていないオペレーター向けならブランド-オペレーターチェックで拒否されます。認可されたオペレーター向けでも、認識されていないエージェントからのリクエストはアイデンティティチェックで拒否されます。`sync_accounts` で [`request_signing.required_for`](/docs/building/by-layer/L1/security#transport-scope) を宣伝するセラーは、アイデンティティレイヤーで未署名トラフィックを拒否します。それを宣伝しないセラーも、エージェント請求可能な値を受け入れる前に確立されたクレデンシャルマッピングを要求してもよい(MAY)。 ブランド-オペレーターチェックは、[オペレーターの取り消しとキャッシング](#オペレーターの取り消しとキャッシング)に従いセラーがキャッシュした `brand.json` に対して実行されます — 取り消しは最終的です。高価値または初回のブランドプロビジョニングを行うセラーは、TOCTOU ウィンドウを閉じるためキャッシュをバイパスすべきです(SHOULD)。 **ブランド-オペレーター認可 Protocol の SDK 命名。** ブランド-オペレーターチェック向けの型付き Protocol を(アダプターが独自のリゾルバーを差し込めるよう)公開する SDK は、参照するファイルにちなんで名前を付けるべきです(SHOULD): `BrandAuthorizationResolver`(または各言語の慣用的なケーシングでの同等物)。ファイルは `brand.json/authorized_operators` — ブランドを代表してよい人のブランド側の宣言です。SDK はこの Protocol を `adagents.json` にちなんで命名すべきではありません(SHOULD NOT)。`adagents.json` はパブリッシャー側 / データプロバイダー側であり、別の関係(どの sales agent がそのパブリッシャーのインベントリを販売してよいか)をモデル化します。バイヤー側のリゾルバーを `AdagentsResolver` と命名すると 2 つの面が混同され、アダプターが誤ったメンタルモデルに固定されます。これはスペック側の推奨です。SDK の慣習は上流に追随します。 **エージェントの商業状態はオフラインです。** バイヤーエージェントが*パススルー専用*(支払い関係なし — オペレーターのみが請求され得る)か*エージェント請求可能*(エージェントが直接請求され得る)かは、オペレーターアカウント作成と同じように、セラーのオンボーディングシステムに記録されます。そのレコードのプロビジョニング — 契約、KYC、支払条件、請求エンティティのキャプチャ — は AdCP のスコープ外です。スコープ内なのはワイヤー上の 2 つの帰結です: 1. **ランタイム請求ゲート。** `billing: "agent"` または `billing: "advertiser"` を送信するパススルー専用エージェントは、`BILLING_NOT_PERMITTED_FOR_AGENT` と `operator` の `error.details.suggested_billing` で拒否されます。リカバリー契約は [Billing and Account Setup](/docs/building/by-layer/L3/error-handling#billing-and-account-setup) を参照してください。 2. **エージェントごとのデフォルト。** セラーは、そのエージェントの下で新しいアカウントをプロビジョニングする際、バイヤーエージェントのオンボーディングレコードから `payment_terms`、`billing_entity`、レートカードの紐付け、信用限度を事前入力してもよい(MAY)。`sync_accounts` リクエストのアカウントごとの値は常にエージェントごとのデフォルトより優先されます — バイヤーは行ごとにオーバーライドできます。エージェントごとのレイヤーは推奨される実装パターンです(SSP が OpenRTB DSP 向けに `buyer_id` / `seat_id` 行を維持する方法をミラーします)。小規模パブリッシャーは、エージェントごとの条件を区別する債権業務を持つまで、セラー全体のデフォルトに折りたたんでもよい(MAY)。 ## アカウント参照 すべてのアカウントスコープの操作は、フラットな `account_id` 文字列の代わりに `account` オブジェクトを受け入れる。セラーの `require_operator_auth` ケイパビリティが認証モデルと参照の形状を決定します。ツールの公開が、上流管理の `account_id` 名前空間と、アウトオブバンドで供給されるセラー定義の ID を区別します。 呼び出し元スコープのイントロスペクションをサポートするセラーは、[`sync_accounts`](/docs/accounts/tasks/sync_accounts) と [`list_accounts`](/docs/accounts/tasks/list_accounts) のレスポンスの各アカウントエントリに、任意の `authorization` オブジェクトを付加します — この呼び出し元がそのアカウントで使用を許可されているタスクとリクエストフィールド、および標準的な名前付きスコープ(例: `attestation_verifier`)をリストします。完全な形状とセマンティクスは [Caller authorization](/docs/accounts/overview#caller-authorization) を参照してください。 ### アカウント ID 名前空間(`require_operator_auth: true`) アカウントは AdCP の外部で管理されます。広告主はセラーのプラットフォーム上でアカウントを作成し、オペレーターにそれを管理する権限を付与し、バイヤーはセラーが割り当てた `account_id` を渡します。エージェントはアカウント作成や請求セットアップには関与しない — それらは広告主、オペレーター、セラーの間で直接処理されます。 **典型的なセラー:** ソーシャルプラットフォーム、セルフサービス広告プラットフォーム — 広告主がすでにアカウントを持っているどこでも。 **上流管理のワークフロー:** 1. 広告主がセラーのプラットフォームにアカウントを作成(アウトオブバンド) 2. 広告主がオペレーターにアカウントを管理する権限を付与(アウトオブバンド) 3. エージェントが `list_accounts` を呼び出して利用可能なアカウントを発見 4. 人間がリストから正しいアカウントを選択 5. エージェントがすべてのリクエスト(`get_products`、`create_media_buy` など)で `{ "account_id": "acc_acme_001" }` を渡します `list_accounts` は、上流が名前空間を所有するため、認証済みクレデンシャルが複数のアカウントにアクセスし得る場合は必須です。クレデンシャルが正確に 1 つのアカウントに束縛される場合でも、SDK が自動選択して必須アカウント呼び出しで明示的な `{ "account_id": "..." }` を送れるよう、セラーはそのシングルトンを返す `list_accounts` を公開すべきです(SHOULD)。`sync_accounts` プロビジョニングは、将来の明示的なケイパビリティが宣言しない限り、3.0.x のアカウント ID 名前空間ではスコープ外です。今日 `sync_accounts` が公開されている場合、それは既存の `account_id` に対する設定更新モードです。 **セラー定義のワークフロー:** 一部のセラーは、アカウント発見面を公開せずに `account_id` を使用します。そのパターンでは、セラーはオンボーディングまたは設定時にバイヤーにアカウント ID を渡し、バイヤーはアカウントスコープ呼び出しでその ID を渡します。`list_accounts` の不在は、発見すべきプロトコル名前空間がないことを意味します。バイヤーが自然キーでプロビジョニングを試みるべきことを意味しません。 ### バイヤー宣言アカウント(`require_operator_auth: false`) エージェントが購入関係を管理します。`sync_accounts` を呼び出して誰が広告するか、誰がブランドの代わりに操作するか、誰が支払うかをセラーに伝える。セラーはアカウントをプロビジョニングしてステータスで応答する — アカウント ID は宣言の副産物であり、バイヤーが事前に知る必要があるものではありません。 **典型的なセラー:** 従来のパブリッシャー、リテールメディアネットワーク、DSP — 購入関係がプログラム的に確立されるどこでも。 `sync_accounts` は宣言ツールです。各エントリはセラーにバイヤーが必要とするものを伝えるフラグのセットだ: | フラグ | セラーに伝えること | | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `brand`(`domain` + オプションの `brand_id`) | どのブランドが広告するか | | `operator` | 誰がブランドの代わりに操作するか(エージェンシー、トレーディングデスク、またはブランド自身) | | `billing` | 誰が請求書を受け取るか — `operator`、`agent`、または `advertiser` | | `billing_entity` | 支払責任を負う当事者の構造化されたビジネスエンティティ詳細 — 法人名、VAT ID、税務 ID、住所、連絡先、銀行詳細。正式な B2B インボイスに使用。銀行詳細は書き込み専用(レスポンスでエコーされない)。 | | `payment_terms` | このアカウントの支払条件(`net_15`、`net_30`、`net_45`、`net_60`、`net_90`、`prepay`)。セラーはこれらの条件を受け入れるかアカウントを拒否しなければなりません — 条件はサイレントに再マッピングされることはない。 | | `sandbox` | これがサンドボックス(テスト)アカウントかどうか — 実際の支出なし。バイヤー宣言アカウントのみで使用。アカウント ID 名前空間のサンドボックスは既存のもので、`list_accounts` を通じて発見されるかアウトオブバンドで供給されます。 | セラーに異なることをさせる可能性があるフラグのすべての組み合わせ — 異なるエンティティへの請求、異なるレートカードのセットアップ、サンドボックスの作成 — は別々の宣言です。 ### 請求エンティティとインボイス受取人 構造化されたインボイスデータを必要とする市場(例: VAT ID を要求する EU B2B トランザクション)では、アカウントの `billing_entity` が、`billing` が指す相手のデフォルトのビジネスエンティティ詳細を提供します。これには法人名、税務識別子、郵送先住所、請求連絡先、銀行詳細が含まれます。 個々のメディアバイでは、`invoice_recipient` がアカウントのデフォルトをオーバーライドできます — 特定のキャンペーンを別の当事者に請求すべき場合に便利です。`invoice_recipient` がアカウントのデフォルトと異なり、かつアカウントに `governance_agents` がある場合、セラーはガバナンスエージェントが請求リダイレクトを承認または拒否できるよう、それを `check_governance` リクエストに含めなければなりません(MUST)。 **ワークフロー:** 1. エージェントが1つ以上の宣言で `sync_accounts` を呼び出す 2. セラーがそれぞれのアカウントをプロビジョニングまたはリンクし、ステータスで応答: * `active` — 使用準備完了 * `pending_approval` — セラーがレビュー中(人間が `setup.url` を訪れる必要があるかもしれない) * `rejected` — セラーがリクエストを拒否 3. 後続リクエストでアカウント参照を渡す: * **バイヤー宣言アカウント**(`require_operator_auth: false`): 自然キー `{ "brand": { "domain": "acme-corp.com" }, "operator": "pinnacle-media.com" }` を渡します * **アカウント ID 名前空間**(`require_operator_auth: true`): `{ "account_id": "acc_acme_001" }` を渡す(上流管理の名前空間では `list_accounts` を通じて発見、セラー定義の名前空間ではアウトオブバンドで受領) * **サンドボックス(バイヤー宣言)**: `sandbox: true` を含む自然キーを渡す(`sync_accounts` を通じて宣言) * **サンドボックス(アカウント ID 名前空間)**: `{ "account_id": "test_acc_001" }` を渡す(既存のテストアカウント、`list_accounts` を通じて発見またはアウトオブバンドで供給) 4. 何かが変わった場合(請求モデル、新しいブランド、新しいオペレーター)、再度 `sync_accounts` を呼び出す `billing` が `"agent"` の場合、エージェントは請求に直接責任を負うことがある。`billing` が `"operator"` または `"advertiser"` の場合、エージェントは仲介するが請求される当事者ではありません。セラーはアカウントをアクティブにする前に人間の承認を必要とすることがあります。 ### 自然キーセマンティクス タプル `(brand, operator, sandbox)` はアカウント関係を一意に識別します。`brand` は `domain` とオプションの `brand_id` を持つネストされたオブジェクトです。`operator` は常に必要 — ブランドが直接操作する場合は `operator` をブランドのドメインに設定します。`sandbox` は省略時のデフォルトは `false`。例えば、`{brand: {domain: "acme-corp.com"}, operator: "acme-corp.com"}`(ブランドが直接購入)は `{brand: {domain: "acme-corp.com"}, operator: "pinnacle-media.com"}`(エージェンシー経由のブランド)とは異なるアカウントです。`sandbox: true` を追加すると同じペアのサンドボックスアカウントを参照します。 完全なリクエスト/レスポンススキーマについては[sync\_accounts タスクリファレンス](/docs/accounts/tasks/sync_accounts)を参照。 ### アカウントステータス | ステータス | 意味 | 次のステップ | | ------------------ | ---------------- | ------------------------------------------------------------- | | `active` | 使用準備完了 | このアカウントで購入を配置 | | `pending_approval` | セラーがレビュー中 | 人間が `setup.url` を訪れる必要があるかもしれない。更新のため `list_accounts` をポーリング。 | | `rejected` | セラーがリクエストを拒否 | 拒否理由を確認し、調整して再同期するか、セラーに連絡 | | `payment_required` | 信用限度に達した | 資金を追加するか他のアカウントに支出をルーティング | | `suspended` | アクティブだったが現在は一時停止 | セラーに連絡 | | `closed` | アクティブだったが現在は終了 | — | ### アカウントスコープ エージェントは自然キー — `(brand, operator)` でアカウントをリクエストします。セラーが割り当てる粒度を決定します。レスポンスの `account_scope` フィールドはセラーがリクエストをどのように解決したかをエージェントに伝える: | スコープ | 意味 | 例 | | ---------------- | ----------------------------------- | -------------------------------------------------------------------------------------------- | | `operator` | このオペレーター下のすべてのブランドに対して1つのアカウント | エージェントが(Pinnacle Media, Acme)と(Pinnacle Media, Nova)を送信 — セラーは両方を Pinnacle Media アカウントにマッピング | | `brand` | オペレーターに関わらずこのブランドに対して1つのアカウント | エージェントが(Acme, Pinnacle Media)と(Acme, Summit Agency)を送信 — セラーは両方を Acme アカウントにマッピング | | `operator_brand` | このオペレーター+ブランドペアに専用アカウント | エージェントが(Pinnacle Media, Acme)を送信 — セラーが特定の Acme-via-Pinnacle アカウントを作成 | | `agent` | ブランド別・オペレーター別の分割がないエージェントスコープのアカウント | エージェントがどんなブランドを送信しても — セラーがリクエストを継続的なエージェントスコープのアカウントにマップ | エージェントはスコープを選択しない — セラーが独自のアカウントポリシーに基づいて割り当てる。`(brand: {domain: "acme-corp.com"}, operator: "pinnacle-media.com")` をリクエストするエージェントは、セラーに応じてオペレータースコープ、ブランドスコープ、または専用の operator\_brand アカウントを受け取ることがあります。 複数の自然キーが同じスコープに解決する場合、`account_scope` がその理由を説明します。 バイヤー宣言アカウント(`require_operator_auth: false`)では、後続リクエストで自然キー(`brand` + `operator`)を使用する — サンドボックスアカウントには `sandbox: true` を追加します。セラーは内部ハンドルとして `sync_accounts` から `account_id` を返してもよいが、この方法でプロビジョニングされたすべてのアカウントについて自然キーの `AccountRef` を受け入れ続けなければなりません(MUST)。アカウント ID 名前空間(`require_operator_auth: true`)では、上流管理の名前空間では `list_accounts` を通じて、セラー定義の名前空間ではアウトオブバンドで、サンドボックスのテストアカウントを含めアカウント ID を取得します。 ## エラーコード | コード | 返されるタイミング | 解決策 | | ------------------------ | ------------------------------------- | -------------------------------- | | `ACCOUNT_REQUIRED` | 複数のアカウント; セラーがどれか判断できない | アカウント参照に `account_id` を渡す | | `ACCOUNT_NOT_FOUND` | `account_id` が存在しないかエージェントがアクセス権を持たない | アカウント参照を確認し、`sync_accounts` を再実行 | | `ACCOUNT_SETUP_REQUIRED` | 自然キーが解決されたがアカウントのセットアップが必要 | URL/メッセージの `details.setup` を確認 | | `ACCOUNT_AMBIGUOUS` | 自然キーが複数のアカウントに解決する | `account_id` またはより具体的な自然キーを渡す | | `PAYMENT_REQUIRED` | 信用限度に達したか資金が枯渇した | 資金を追加するか別のアカウントにルーティング | | `ACCOUNT_SUSPENDED` | アカウントが正常な状態でない | セラーに連絡 | | `BRAND_REQUIRED` | ブランド参照なしの請求可能な操作 | リクエストに `brand` を含める | セラーが `ACCOUNT_REQUIRED` を返す場合、利用可能なアカウントを含める: ```json theme={null} { "errors": [{ "code": "ACCOUNT_REQUIRED", "message": "Multiple accounts available. Please specify account_id in the account reference.", "details": { "available_accounts": [ { "account_id": "acc_acme_001", "name": "Acme Corp" }, { "account_id": "acc_pinnacle", "name": "Pinnacle Media" } ] } }] } ``` ## 設計ノート ### sync\_accounts とセラーのレコードシステム エージェントが `(brand: {domain: "acme-corp.com"}, operator: "pinnacle-media.com")` を宣言すると、セラーは独自のシステム — CRM、OMS、広告サーバー、または請求プラットフォーム — でレコードを検索または作成します。 `sync_accounts` はセラーのレコードシステムへのバイヤーサイドインターフェースです。セラーは: * 自然キーを既存のアカウントにマッピングして `status: "active"` を返すことがあります * 新しいレコードを作成して即座に返すことがある(`status: "active"`) * 人間のレビューを保留するプレースホルダーを作成することがある(`status: "pending_approval"`) * リクエストを完全に拒否することがある(`status: "rejected"`) `list_accounts` はセラーがこのエージェントにマッピングしたすべてのレコードを返す — 保留中と拒否されたエントリを含みます。エージェントは `list_accounts` を使用して、アクティブなアカウントだけでなく、このセラーとのポートフォリオの完全な状態を確認します。 ### アカウントとインサーションオーダー アカウントは継続的な関係を表す — 誰が請求されるか、どのレートが適用されるか、どのくらいの信用が利用可能か。キャンペーンやインサーションオーダーではありません。 インサーションオーダーとキャンペーンフライトは `create_media_buy` を通じたメディアバイとしてモデル化されます。アカウントは*請求条件*を決定し、メディアバイは*何がいつ実行されるか*を決定します。単一のアカウントはそのライフタイムにわたって多くのメディアバイを持つことができます。 ### オペレーターの取り消しとキャッシング ブランドが `authorized_operators` からオペレーターを削除した場合、既存のアクティブなアカウントは自動的に非アクティブ化されない。取り消しは即時ではなく最終的なものだ — `ads.txt` の変更がサプライサイドで伝播する方法に似ています。 セラーは `brand.json` の標準 HTTP キャッシングヘッダーを尊重して定期的に再検証すべきです。合理的なキャッシュ TTL は 24 時間です。 ### SMB のブランドアイデンティティ `/.well-known/brand.json` を通じたドメインベースのアイデンティティはあらゆる規模の組織に機能する — どのウェブサーバーにでもホストできる静的な JSON ファイルです。 ドメインにファイルをホストできない組織の場合、`brand.json` の `authoritative_location` フィールドにより、ハウスドメインがホストされた場所にリダイレクトできる: ```json theme={null} { "house": { "domain": "local-bakery.com" }, "authoritative_location": "https://registry.agenticadvertising.org/brands/local-bakery.com" } ``` # 認証 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L2/authentication AdCP 認証ガイド: 公開操作と認証必須操作の使い分け、静的クレデンシャルの実装、バイヤーおよびセラーエージェント向けの認証情報管理。 AdCP では、公開操作と認証必須の操作を使い分ける段階的な認証モデルを採用しています。 ## 認証が必要な場面 ### 公開操作(認証不要) 探索や評価のため、以下は認証なしで利用できます: * **`get_adcp_capabilities`** - エージェントの機能、ポートフォリオ、対応機能の取得 * **`list_creative_formats`** - 利用可能なクリエイティブ形式の閲覧 * **`get_products`** - 在庫の探索(認証なしでは結果が限定) **理由**: パブリッシャーは、ビジネス関係を結ぶ前に購入者に自社の提供内容を知ってもらいたいため。 **重要**: 未認証の `get_products` は以下のように制限される場合があります: * 一部のカタログ(標準商品)のみ * 価格情報や CPM の非表示 * カスタム商品なし * 汎用的なフォーマット対応のみ ### 認証が必要な操作 以下の操作には有効な認証情報が必要です: * **`get_products`** (full access) - Complete catalog with pricing and custom products * **`create_media_buy`** - Create advertising campaigns * **`update_media_buy`** - Modify existing campaigns * **`sync_creatives`** - Upload creative assets * **`list_creatives`** - View your creative library * **`get_media_buy_delivery`** - Monitor campaign performance and metrics * **`provide_performance_feedback`** - Submit optimization signals **理由**: 金銭が絡む取引、機密データへのアクセス、稼働中キャンペーンの変更が含まれるため。 ## 認証方式 AdCP は認証必須操作向けに 3 つの認証メカニズムをサポートします。選択は操作のリスククラスと使用する AdCP バージョンによります: | メカニズム | 3.0(現行) | 3.1+ | 備考 | | ---------------------------------------------- | ----------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | **RFC 9421 リクエスト署名** | すべての認証必須操作で RECOMMENDED | 変更系 / 金融操作で **REQUIRED** | 非対称、ボディ束縛、リプレイ耐性。[RFC 9421 リクエスト署名](/docs/building/by-layer/L1/security#request-signing)を参照。 | | **相互 TLS(mTLS)** | 任意の操作で許可 | 9421 の代替として許可 | トランスポート層のアイデンティティ。デプロイがすでにエッジで mTLS を終端している場合に推奨。 | | **静的 Authorization クレデンシャル(Bearer または Basic)** | 許可。3.0 の実効的なベースライン | 変更系 / 金融操作で **PROHIBITED**、読み取り / ディスカバリーのみ許可 | 共有シークレットのトランスポート。Bearer が正規の例。HTTP Basic も TLS 上で送信され保護された各リクエストで検証される場合は許容される。変更系操作については廃止予定が文書化されている — [既知の制限](/docs/reference/known-limitations)を参照。 | **3.0 の変更系操作の下限。** 3.1 が到着するまで、TLS 上の静的 Authorization ヘッダークレデンシャルが変更系操作の実効的な下限です。支出コミットメントを扱う運用者は、強制的な切り替えを避けるため、3.1 の廃止日より前に RFC 9421 リクエスト署名を出荷すべきです(SHOULD)。 ### 静的 Authorization クレデンシャル(3.0 ベースライン) ``` Authorization: Bearer Authorization: Basic ``` Bearer トークンの種類: * **Opaque tokens**: サーバーで検証されるエージェント紐づけ文字列 * **JWT tokens**: クレームを埋め込んだ自己完結型トークン HTTP Basic クレデンシャルも静的な共有シークレットメカニズムです。TLS 上で `Authorization` ヘッダーに載せて送信しなければならず(MUST)、サーバーは保護された各リクエストでクレデンシャルを検証しなければなりません(MUST)。Basic クレデンシャルは Bearer トークンより強力ではありません。両者ともトランスポート上に事前プロビジョニングされた共有シークレットを載せるため、同じコンフォーマンスクラスとして受け入れられます。 実装はすべての静的クレデンシャル認証エンドポイントで TLS 1.2+ を強制しなければなりません(MUST)。トランスポート要件は[実装セキュリティリファレンス](/docs/building/by-layer/L1/security)を参照してください。 クレデンシャルは `Authorization` リクエストヘッダーで運ばなければなりません(MUST)。セラーは非正規のエイリアス(例: 一部の初期 MCP 専用デプロイに現れた `x-adcp-auth`)を要求してはならず(MUST NOT)、エージェントカード・機能レスポンス・ドキュメントでサポート対象ヘッダーとして宣伝してもなりません(MUST NOT)。セラーは既存アダプターの統合を段階的に廃止する間、そのようなエイリアスを移行的な入力として受け入れてもよい(MAY)が、同じエンドポイントで `Authorization: Bearer` または `Authorization: Basic` も受け入れなければなりません(MUST)。バイヤーエージェントと SDK は、セラーが明示的に Basic クレデンシャルをプロビジョニングしない限り `Authorization: Bearer` を発行すべきです(SHOULD)。SDK の例やドキュメント文字列で、エイリアスヘッダーを正規の形として示してはなりません(MUST NOT)。 ### RFC 9421 リクエスト署名(推奨。3.1+ の変更系操作で必須) 署名付きリクエストは、`@method`、`@target-uri`、`@authority`、`content-type`、`content-digest` を、±60 秒のタイムスタンプウィンドウと 128 ビット以上のノンス付きで `Ed25519`、`ecdsa-p256-sha256`、または `rsa-pss-sha512` 署名の下に束縛します。完全な検証器チェックリスト、鍵ディスカバリールール(`brand.json` → `agents[]` → `jwks_uri`)、ローテーションセマンティクスは[実装セキュリティリファレンス](/docs/building/by-layer/L1/security#request-signing)で定義されています。`get_adcp_capabilities.request_signing.supported` による機能ディスカバリーにより、クライアントは変更系呼び出しを送信する前にセラーが署名を強制するかどうかを検出できます。 ### mTLS エッジで mTLS を終端する運用者は、AdCP 操作の主要なアイデンティティメカニズムとしてピア証明書を使用してもよい(MAY)。mTLS を使用する場合、運用者はいかなるヘッダーフィールドでもなく証明書のサブジェクト / SAN にアイデンティティをピン留めしなければなりません(MUST)。 ### JWT のクレーム When using JWT tokens, include these standard claims: ```json theme={null} { "sub": "agent_123", "exp": 1706745600, "iat": 1706742000 } ``` 認可のために追加クレームを求める sales agent もあります。 ## エージェントとアカウント AdCP は **エージェント**(リクエストを実行する主体)と **アカウント**(課金対象)を区別します: * **エージェント**: API 呼び出しを行う認証済みエンティティ(トークンで識別) * **アカウント**: 料率と請求を決定する課金関係 エージェントは複数のアカウントにアクセスできる場合があります(例: 複数クライアントを管理する代理店)。アカウントの選択と課金の帰属については [Accounts and Agents](/docs/building/by-layer/L2/accounts-and-agents) を参照してください。 スキーマ定義は [`account.json`](https://adcontextprotocol.org/schemas/v3/core/account.json) を参照してください。 ## テナント解決 AdCP はテナントをリクエストペイロードからではなく、認証済みプリンシパルから解決します。セラーエージェントは、認証済みアイデンティティ(bearer トークン、Basic クレデンシャル、mTLS クライアント証明書、または RFC 9421 鍵)を、自身の認可コンテキストを介して発信元バイヤーのアカウントにマッピングします。タスクペイロードが認証の代替としてテナントアイデンティティを運ぶことは決してありません。スキーマが `account` エンベロープではなくグローバルに一意なリソース ID(`plan_id`、`rights_id`、`standards_id`、`event_source_id`、`list_id`)を要求する場合、セラーは同じ認可コンテキストを介して ID → テナントを解決します。認証済みプリンシパルは参照されたリソースへのアクセス権を持たなければならず、リソース自体はそれがプロビジョニングされたブランドを保持します。それらの呼び出しでのエンベロープアイデンティティは冗長であり、認証済みプリンシパルと食い違えばスプーフィングのベクトルになります。 トレーニングエージェントのコンプライアンスストーリーボードは、これらの呼び出しにサンドボックスのルーティング規約としてエンベロープアイデンティティを注入します。トレーニングエージェントは自身の認証済みプリンシパル層を持たないためです — [ストーリーボード作成](/docs/contributing/storyboard-authoring)を参照してください。本番のセラーはそれを必要としません。 ## クレデンシャルの配置 **バイヤープリンシパル**を認証するクレデンシャルは、トランスポートの認証チャネルに到着しなければならず(MUST)、タスクペイロード — トップレベル、`context` 内、`ext` 内、その他あらゆるネストされた場所 — に置いてはなりません(MUST NOT)。トランスポートチャネルは次のとおりです: * **HTTP 上の静的クレデンシャル** — [RFC 6750 §2](https://www.rfc-editor.org/rfc/rfc6750#section-2) に従う `Authorization: Bearer `、またはセラーが HTTP Basic クレデンシャルをプロビジョニングする場合は `Authorization: Basic `。 * **RFC 9421 署名付きリクエスト** — [RFC 9421 §2](https://www.rfc-editor.org/rfc/rfc9421#section-2) に従う `Signature` および `Signature-Input` ヘッダー。署名自体がクレデンシャルであり、ペイロード内には署名者を認証するものは何もありません。 * **MCP および A2A の認証フレーミング** — トランスポートの認証記述子(例: MCP の `authInfo`、A2A の `authentication.schemes`)。認証要件のディスカバリーは、該当する場合 [RFC 9728 §3](https://www.rfc-editor.org/rfc/rfc9728#section-3) の保護リソースメタデータに従います。 * **相互 TLS** — 上表の mTLS 行に従うピア証明書。 このルールはトランスポート非依存です。セラーがどのメカニズムを受け入れるかに関わらず適用されます。バイヤープリンシパルがペイロードフィールドを介して認証する AdCP バージョン・機能・セラーポリシーは存在しません。同じ配置ルールは、リクエストがダウンストリームの評価器呼び出しのために渡そうとするクレデンシャルや呼び出し元提供の信頼素材にも適用されます。それらはエージェント間 - 評価器間トランスポート、またはアカウントプロビジョニングに属し、タスクペイロードには属しません。ペイロード内でクレデンシャルまたは信頼素材のキー(例: 任意のネスト深度の `_access_token`、`api_key`、`client_secret`、`bearer`、`authorization`、`jwk`、`jwks`、`jwks_uri`)を検出したセラーは、AdCP 3.1 の下でリクエストを [`CREDENTIAL_IN_ARGS`](/docs/building/by-layer/L3/error-handling#authentication-and-access) で拒否すべきです(SHOULD)。この要件は 3.1 公開日の 90 日後に MUST に格上げされます。このコードのリカバリー分類は `terminal` です。エージェントは自動リトライしてはなりません(MUST NOT)。自動リトライは試行ごとにクレデンシャルを再ログし、それ自体がこのルールが塞ぐプロンプトインジェクションの流出面だからです([エージェント広告に固有の脅威](/docs/building/concepts/security-model#threats-specific-to-agentic-advertising)を参照)。 ### カーブアウト 以下のクレデンシャル面は**バイヤープリンシパル**のクレデンシャルでは**なく**、上記のルールは適用されません: * **`push_notification_config.authentication.credentials`**([スキーマ](https://adcontextprotocol.org/schemas/v3/core/push-notification-config.json))。これは、**セラー**がバイヤーの Webhook エンドポイントに**折り返し**呼び出す際に使用するレガシー Bearer / HMAC-SHA256 クレデンシャルです。セラーを呼び出し元として、バイヤーを受信側として認証します — インバウンドの AdCP リクエストを認証するバイヤープリンシパルのクレデンシャルとは直交します。デフォルトの 9421 Webhook プロファイルは `brand.json` で発見された鍵を使用し、共有シークレットを一切交換しません。レガシーブロックは AdCP 4.0 で削除される非推奨の互換スキームです。 * **帯域外で交換されるオンボーディング時のシークレット** — 初回トークン発行、OAuth 動的登録レスポンス、ダッシュボード発行の API キー。これらは AdCP タスクペイロードとしてではなく、AAO 認可サーバーまたはセラーのオンボーディングフローを通過します。 ### リレーエージェント 代理店 / A2A リレートポロジー(ブランド → リレー → セラー)は、**リレー自身のプリンシパルの下で**認証します。リレーはブランドエージェントの RFC 9421 署名をそのまま保持する(パススルーモデル)か、自身の鍵の下で再署名する(再署名モデル)かのいずれかです — どちらのオプションも [#2324](https://github.com/adcontextprotocol/adcp/issues/2324) で説明されています。いずれのモデルも、ブランドのトランスポートクレデンシャルをリレー側のペイロードフィールドとして転送することを許可しません。リレーが記録上のプリンシパルである場合のブランドエージェントのアイデンティティは、リクエストボディにアイデンティティコンテキスト(例: `adagents.json` / `authorized_operator[]` に対してセラーが検証可能なバイヤー側のアイデンティティアサーション)として運ばなければならず(MUST)、転送されたトランスポートクレデンシャルとしては決して運びません。リレーは、アウトバウンドのセラー宛てリクエストの任意の args フィールドでバイヤークレデンシャルをエコーまたは再添付してはなりません(MUST NOT)。 ## プロトコル設定 ほとんどの MCP および A2A 統合は、認証ヘッダーとして `Authorization: Bearer `([RFC 6750 §2](https://www.rfc-editor.org/rfc/rfc6750#section-2))を使用します。クライアントを次のように設定します: ```json theme={null} { "auth": { "type": "bearer", "token": "" } } ``` クライアントライブラリが `Authorization: Bearer ` ヘッダーの付与を処理します。 セラーが明示的に HTTP Basic クレデンシャルをプロビジョニングする場合、同じトランスポートルールが適用されます。HTTPS 上の認証必須な各リクエストで `Authorization: Basic ` を送信します。 **レッグごとのヘッダーエイリアスポリシー。** 2 つのプロトコルレッグは、標準の `Authorization` ヘッダーを超えてどのエイリアスを受け入れるかが異なります: * **A2A** — `Authorization` のみ。Bearer クレデンシャルの場合、`Authorization: Bearer ` を送信し、セラーのエージェントカードで `bearerAuth` `HTTPAuthSecurityScheme` を宣言します([A2A ガイド — エージェントカード](/docs/building/by-layer/L0/a2a-guide#agent-cards)を参照)。Basic クレデンシャルの場合、`Authorization: Basic ` を送信し、HTTP Basic を宣言します。`x-adcp-auth` カスタムヘッダーは A2A 面では認識されません。 * **MCP** — `Authorization` が Bearer および Basic クレデンシャルの主要ヘッダーです。`x-adcp-auth` は adcp 4.5.0 より前の統合向けの後方互換 Bearer エイリアスとして受け入れられます。新しい実装は両方のレッグで標準ヘッダーを使用すべきです。 **adcp 4.5.0 に移行するセラーへ。** 以前 `a2a_header_name` ノブで A2A レッグヘッダーとして `x-adcp-auth` を設定していた場合、`bearerAuth` を宣言するようエージェントカードを更新する前に、そのノブが RFC 6750 のデフォルトに設定されていることを確認してください — レガシーヘッダーからまだ移行していないバイヤーは、さもなくば HTTP 401 を受け取ります。 ## MCP クライアントの設定 MCP プロトコルでは、認証は HTTP ヘッダーの手動付与ではなくトランスポート層で処理されます。 ### MCP クライアントライブラリの利用 The recommended approach is to use an MCP client library: ```typescript TypeScript theme={null} import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; const transport = new StreamableHTTPClientTransport( new URL('https://agenticadvertising.org/api/training-agent/mcp'), { requestInit: { headers: { 'Authorization': 'Bearer YOUR_TOKEN_HERE' } } } ); const client = new Client({ name: 'my-client', version: '1.0.0' }); await client.connect(transport); ``` ```python Python theme={null} from mcp import ClientSession from mcp.client.streamable_http import streamablehttp_client async with streamablehttp_client( "https://agenticadvertising.org/api/training-agent/mcp", headers={"Authorization": "Bearer YOUR_TOKEN_HERE"} ) as (read, write): async with ClientSession(read, write) as session: await session.initialize() ``` ### よくある誤り: 生の HTTP ヘッダー追加 生の HTTP リクエストに認証ヘッダーを付けようとするのは誤りです: ```http theme={null} # This won't work for MCP endpoints GET /mcp HTTP/1.1 Authorization: Bearer YOUR_TOKEN ``` MCP は HTTP 上のストリーミングプロトコルです。認証は、プロトコル交渉とメッセージフレーミングを担う MCP クライアントのトランスポート層で設定する必要があります。 ### 認証トラブルシュート "authentication required" が出る場合: 1. **MCP クライアントライブラリを使っているか確認** - 生の HTTP 呼び出しをしていないか 2. **トークンの渡し方を確認** - トランスポート設定に渡しているか 3. **公開テストエージェントで試験** - カスタムエージェント前に動作確認 4. **プロトコルバージョンを確認** - クライアントとサーバーの互換性を確保 OAuth ハンドシェイクの失敗や RFC 9421 署名の問題には、[CLI 認証グレーダー](/docs/building/verification/grading)を使用してください — `diagnose-auth` は RFC 9728 + RFC 8414 のディスカバリーを探索して仮説をランク付けし、`grade request-signing` はすべての署名ベクトルをベクトルごとの診断付きで実行します。 ## 認証情報の取得 ### アカウント開設フロー 認証が必要な操作を行うには、各 sales agent とのアカウント開設が必要です: 1. **Sales agent を特定**: パブリッシャーの `adagents.json` から発見 2. **営業窓口に連絡**: エージェントの営業/提携チームに問い合わせ 3. **オンボーディング**: 企業情報の提供、契約締結、課金設定 4. **認証情報を受領**: API キーまたは OAuth クライアント資格情報を取得 **Note**: 各 sales agent は独立してアカウントを管理します。エージェントごとに別の認証情報が必要です。 ### 動的クライアント登録(オプション) Some sales agents support OAuth 2.0 dynamic client registration: ```http theme={null} POST /oauth/register Content-Type: application/json { "client_name": "Your Company Name", "redirect_uris": ["https://yourapp.com/callback"], "grant_types": ["authorization_code", "refresh_token"], "scope": "adcp:products adcp:media_buys adcp:creatives" } ``` 動的登録に対応しているかは、sales agent のドキュメントや `adagents.json` を確認してください。 ### アグリゲーションプラットフォーム 複数の sales agent との認証情報や関係を一括管理するアグリゲーションプラットフォーム(例: Scope3)の利用を検討してください。これにより次が簡素化されます: * 認証情報管理 * 金銭的なやり取り * 契約手続き * コンプライアンス監視 ## AAO プラットフォームサービスへの認証 上記のメカニズムは**エージェント間**認証(バイヤー ↔ sales agent)を規定します。**AAO ホスト型サービス** — レジストリ書き込み API、AAO MCP エンドポイント、メンバーダッシュボード — への認証は別の面です。 AAO は OAuth 2.1 + OIDC 認可サーバーを運用します。クライアントは標準の well-known を介してそれを発見します: * **認可サーバーメタデータ(RFC 8414):** `https://agenticadvertising.org/.well-known/oauth-authorization-server` * **保護リソースメタデータ(RFC 9728):** `/.well-known/oauth-protected-resource/api`(REST API)および `/.well-known/oauth-protected-resource/mcp`(MCP)。どちらも `https://agenticadvertising.org` を認可サーバーとして列挙します。 * **フロー:** PKCE(S256)付き認可コード。ユーザーアイデンティティは WorkOS AuthKit 経由。トークンは署名付き JWT です。 * **動的クライアント登録(RFC 7591):** `POST /register`。 * **サーバー間:** `client_credentials` グラントはありません。バックエンドサービスは OAuth `/token` エンドポイントではなく、[AAO ダッシュボード](https://agenticadvertising.org/dashboard/api-keys)の WorkOS 組織 API キーを使用すべきです。 すべての AAO エンドポイントは HTTPS 専用です。プレーン HTTP で提供されるディスカバリードキュメントは拒否してください。 AAO から取得したユーザー JWT は AdCP クレデンシャルでは**ありません**。sales agent への呼び出しは、上表に従い依然としてそのエージェントの bearer / 9421 / mTLS クレデンシャルを使用します。完全なリファレンス: [AAO レジストリ — 認証](/docs/registry#authentication)。 **sales agent の RFC 9728 保護リソースメタデータで `authorization_endpoint` を発見した場合**(例: オペレーターアカウントの OAuth フロー向け)、発見された `authorization_servers` の発行者を、そのセラーについて `adagents.json` — または帯域外のオンボーディング — が認可したものに対してピン留めしてください。リソース自体が返した AS URL を盲目的に信頼しないでください。さもなくば、悪意のあるまたは侵害されたセラーがオペレーターのクレデンシャルを攻撃者制御のエンドポイントにルーティングできます。 ## エラーレスポンス ### 保護された操作への未認証リクエスト ```json theme={null} { "error": { "code": "AUTH_REQUIRED", "message": "Authentication required for this operation" } } ``` ### 無効または期限切れの認証情報 ```json theme={null} { "error": { "code": "AUTH_INVALID", "message": "Invalid or expired credentials" } } ``` ### 権限不足 ```json theme={null} { "error": { "code": "INSUFFICIENT_PERMISSIONS", "message": "Agent does not have required permissions for this operation" } } ``` ## ベストプラクティス 1. **安全な保管**: 環境変数やシークレットマネージャーで保護 2. **ローテーション**: 認証情報のローテーションポリシーを実装 3. **スコープ最小化**: 必要最小限の権限のみ要求 4. **トークン更新**: JWT の自動リフレッシュを実装 5. **エラーハンドリング**: 認証エラーをリトライロジックで適切に処理 ## 認証テスト 公開テストエージェントは共有トークンを受け入れます — サインアップは不要です: ```bash theme={null} export ADCP_AUTH_TOKEN="1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ" export AGENT_URL="https://agenticadvertising.org/api/training-agent/mcp" ``` このトークンでクライアントを設定します: ```json theme={null} { "agent_uri": "https://agenticadvertising.org/api/training-agent/mcp", "protocol": "mcp", "auth": { "type": "bearer", "token": "1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ" } } ``` 組織スコープの利用状況トラッキングには、公開トークンを [AAO ダッシュボード](https://agenticadvertising.org/dashboard/api-keys)の自身の API キーに置き換えてください。 サンドボックスモードを含むテスト機能の詳細は [Sandbox Mode](/docs/media-buy/advanced-topics/sandbox) を参照してください。 # コンテキストとセッション Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L2/context-sessions AdCP における context_id と task_id の使い分け、MCP および A2A プロトコルリクエストをまたいだ会話状態・セッション継続・拡張フィールドの管理方法。 AdCP はリクエスト間で状態を維持するために識別子とデータフィールドを利用します。これらを理解することは、効果的な統合を行う上で不可欠です。 ## 主要な識別子 AdCP では用途の異なる 2 種類の識別子を使用します: ### context\_id と task\_id | Identifier | Purpose | Lifespan | Scope | | --------------- | ------------ | ------------ | -------------- | | **context\_id** | 会話・セッションの継続 | 約 1 時間 | 複数のタスク呼び出しをまたぐ | | **task\_id** | 特定オペレーションの追跡 | 完了まで(数時間〜数日) | 個別オペレーション | **context\_id**: * プロトコル層から付与(A2A は自動、MCP は手動) * 会話履歴とセッション継続を提供 * 複数タスク呼び出し間の状態維持に使用 * タイムアウト後に失効(一般的に 1 時間) **task\_id**: * 非同期になり得る個別リクエストに固有 * 会話をまたいで存続 * オペレーションの進行状況を長期にわたり追跡 * タスク完了まで保持(複雑なメディアバイでは数日かかることも) * 別の会話やセッションから参照可能 ### 使用例 ```javascript theme={null} // First call - establishes context and creates task const result = await call('create_media_buy', { brief: "Launch summer campaign" }); const contextId = result.context_id; // 会話継続に使用 const taskId = result.task_id; // このメディアバイを追跡 // 同じ会話内の後続呼び出し - context_id を使用 const update1 = await call('update_media_buy', { context_id: contextId, // Maintains conversation state task_id: taskId, // References the specific media buy updates: {...} }); // 数日後の別会話 - task_id のみで十分 const delivery = await call('get_media_buy_delivery', { task_id: taskId // No context_id - this is a new conversation }); ``` ## プロトコルの違い * **A2A**: コンテキストはプロトコルが自動で管理 * **MCP**: `context_id` を手動で管理する必要あり ### A2A のコンテキスト(自動) A2A はセッションをネイティブに扱うため、コンテキスト管理は不要です: ```javascript theme={null} // A2A はコンテキストを自動管理 const task = await a2a.send({ message: {...} }); // contextId is managed by A2A protocol // Follow-ups automatically use the same context const followUp = await a2a.send({ contextId: task.contextId, // 任意 - A2A が追跡 message: {...} }); ``` ### MCP のコンテキスト(手動) MCP では状態を維持するために明示的なコンテキスト管理が必要です: ```javascript theme={null} // First call - no context const result1 = await mcp.call('get_products', { brief: "Video ads" }); const contextId = result1.context_id; // 保存しておく! // Follow-up - must include context_id const result2 = await mcp.call('get_products', { context_id: contextId, // 継続に必須 brief: "Focus on premium inventory" }); ``` ### MCP におけるコンテキスト管理パターン ```javascript theme={null} class MCPSession { constructor(mcp) { this.mcp = mcp; this.contextId = null; } async call(method, params) { const result = await this.mcp.call(method, { ...params, context_id: this.contextId }); this.contextId = result.context_id; // 次の呼び出し用に更新 return result; } } ``` ### MCP エージェント側: セッション ID フォールバック 多くの MCP クライアント(ChatGPT、Claude など)は `context_id` を渡しません。エージェントはトランスポートのセッション ID をフォールバックとして使用することで、自動的なセッション永続化を実現できます: ```typescript theme={null} server.tool('get_products', schema, async (args, extra) => { // 明示的な context_id があればそれを使用し、なければ MCP sessionId にフォールバック const contextId = args.context_id ?? extra?.sessionId; const products = await generateProducts(args.brief, contextId); await productStore.save(contextId, products); return products; }); ``` これにより、シンプルなクライアントでも自動セッション永続化を利用しながら、再開可能なセッションを必要とする高度なバイヤーには明示的な制御を残せます。実装例は [Snap AdCP Agent](https://github.com/scope3data/snap-adcp) を参照してください。 ## コンテキストが保持するもの `context_id` はプロトコルに関わらず会話状態を保持します: * 現在議論中のメディアバイや商品 * 検索結果と適用済みフィルター * 会話履歴とユーザー意図 * セッション内で示されたユーザーの嗜好 * ワークフロー状態と一時的な判断 Note: メディアバイのステータスやクリエイティブアセット、パフォーマンスデータなど長期的なタスク状態は `context_id` ではなく `task_id` で追跡します。 ## 拡張フィールド (`ext`) 拡張フィールドはプロトコル互換性を保ちつつ、プラットフォーム固有の機能を実現します。 ### スキーマパターン Extensions appear consistently across requests, responses, and domain objects: ```json theme={null} { "product_id": "ctv_premium", "name": "Connected TV Premium Inventory", "ext": { "gam": { "order_id": "1234567890", "dashboard_url": "https://..." }, "roku": { "content_genres": ["comedy", "drama"] } } } ``` `ext` オブジェクトの特徴: * 常に **任意**(必須にしない) * 任意の有効な JSON 構造を許容 * 実装側は未知のフィールドでも必ず保持 * AdCP スキーマでバリデートしない(実装側での検証は可) ### 名前空間(重要) 拡張は必ずベンダー/プラットフォームごとの名前空間を用います: ```json theme={null} // ✅ Correct - Namespaced { "ext": { "gam": { "test_mode": true }, "roku": { "app_ids": ["123"] } } } // ❌ Incorrect - Not namespaced { "ext": { "test_mode": true, // Missing namespace! "app_ids": ["123"] // Which platform? } } ``` ## アプリケーションコンテキスト (`context`) コンテキストは、レスポンスや Webhook でそのまま返される不透明な相関データを提供します。 ### 主な特性 * エージェントはコンテキストを解析せず、動作に利用しません * 呼び出し元の内部トラッキング用途のみに存在 * レスポンスや Webhook で内容を変えずに返されます ### 規範的なエコー契約 エージェントは以下のルールに従わなければなりません。コンプライアンスランナーはこれらを文字どおり検証し、バイヤーは相関のためにこれらに依存します。 1. **成功時のエコー。** 呼び出し元がリクエストにトップレベルの `context` オブジェクトを含めた場合、エージェントはレスポンスに同じオブジェクトをバイト単位で等価な形で含めなければなりません。これはレスポンスのステータスが `completed`、`submitted`、`working`、`input-required`、その他いかなる終端または中間状態であっても適用されます。 2. **エラー時のエコー。** 失敗レスポンスも `context` をそのままエコーしなければなりません。エラーパスでコンテキストを落とすと、バイヤーが最も必要とするまさにそのときに相関が壊れます。`adcp_error`、`errors[]`、その他いかなるエラーエンベロープを返すエージェントも、呼び出し元の `context` を引き継がなければなりません。 3. **非同期更新時のエコー。** プッシュ通知、Webhook ペイロード、および同じオペレーションに対してエージェントが発行するその後のメッセージは、元の `context` を引き継がなければなりません。エージェントは初回レスポンスと後続のステータス更新の間でコンテキストを落としてはなりません。`context.trace_id` で相関したバイヤーは、そのオペレーションのすべてのメッセージに同じトレースが現れることを期待します。 4. **合成の禁止。** 呼び出し元が `context` オブジェクトを提供しない場合、エージェントはそれを捏造してはなりません。コンテキストなしのリクエストへのレスポンスは `context` フィールドを省略しなければなりません(またはトランスポートの通常のシリアライズに従い null / 不在として発行します)。エージェント側からの合成コンテキストはコンフォーマンス違反です。コンテキストの要点は、それが呼び出し元によって所有されることにあります。 5. **改変の禁止。** エージェントはエコーするコンテキスト内のフィールドを追加・削除・改名・並べ替え・型変更してはなりません。JSON 等価性が適用されます。`{"a":1,"b":2}` と `{"b":2,"a":1}` は異なるシリアライズになり得ますが、キー集合と値が一致していればエコールール上は等価とみなされます。バイトリテラルの等価性に依存する検証器(例: 生の JSON をハッシュする MCP クライアント)は、エージェント側で安定したキー順序でシリアライズすべきです。 6. **アクションの禁止。** エージェントは `context` 内のいかなる値も解析・検証・ログ記録・分岐に利用してはなりません。コンテキストはエージェントにとって不透明です。構造化された識別子のように見える値も、それを解釈してよいという合図ではありません。 ### Schema Pattern ```json theme={null} { "tool": "create_media_buy", "arguments": { "packages": [...], "context": { "ui_session_id": "sess_abc123", "trace_id": "trace_xyz789", "internal_campaign_id": "camp_456" } } } ``` レスポンスでも同じコンテキストが返されます: ```json theme={null} { "status": "input-required", "message": "Media buy requires manual approval before activation.", "context_id": "ctx_ghi789", "context": { "ui_session_id": "sess_abc123", "trace_id": "trace_xyz789", "internal_campaign_id": "camp_456" } } ``` ### コンテキストの主な用途 1. **UI/セッショントラッキング** - 非同期処理間の状態維持 2. **リクエスト相関** - 分散システムでのリクエスト追跡 3. **内部 ID** - 内部データ構造へのマッピング 4. **組織コンテキスト** - マルチテナントの追跡 ## 使い分けの指針 | Field | Purpose | Agent Reads? | Agent Modifies? | | ------------ | ------------ | ------------ | --------------------- | | `context_id` | セッション継続 | Yes | Yes (creates/updates) | | `task_id` | オペレーション追跡 | Yes | Yes (creates) | | `ext` | プラットフォーム固有設定 | MAY | MAY add response data | | `context` | 不透明な相関情報 | NEVER | NEVER | ### `ext` を使うとき: * プラットフォーム側でデータを解釈する必要があります * データがオペレーション動作に影響し得ます * プラットフォーム固有の設定を表現します * 複数オペレーションにわたりデータを残す必要があります ### `context` を使うとき: * 呼び出し元の内部用途に限定されます * エージェントの動作に決して影響させない * 相関/トラッキングのみの目的です * そのままの内容で返してほしい ## ベストプラクティス ### A2A の場合 * プロトコルにコンテキスト管理を任せる * 必要に応じて contextId で会話を明示的に紐づける * セッション管理を信頼します ### MCP の場合 * 呼び出し間で context\_id を必ず保持 * セッションラッパーを実装(上記パターン参照) * コンテキストの有効期限(1 時間)に対応 * 新しいワークフローでは新規コンテキストを開始 * **Agents**: `context_id` が提供されない場合はトランスポートのセッション ID をフォールバックとして使用([セッション ID フォールバック](#mcp-エージェント側-セッション-id-フォールバック)参照) ### 拡張フィールドについて * 必ずベンダーキー配下で名前空間を分ける * 拡張仕様を十分にドキュメント化します * 共通パターンは標準化を提案することを検討 ### アプリケーションコンテキストについて * 不透明のままにし、エージェントが解釈する前提で構造化しません * 大きなペイロードは避ける(コンテキストは全レスポンスで返されます) * 相関用途のみに使い、運用データには使わない # L2 — 認証とレジストリ Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L2/index AdCP スタックの認証とレジストリ層。検証されたアイデンティティをスコープされたプリンシパルに変える — どのバイヤー、どのブランド、どの広告主アカウント、どの sandbox 対 live ティア。 L2 は検証されたアイデンティティをスコープされたプリンシパルに変えます。エージェント側: マルチテナントプリンシパル解決、sandbox/live 境界、ブランド解決、権限スコーピング。呼び出し元側: 自身のアイデンティティを公開し、呼んでいるエージェントをルックアップする — はるかに小さいサーフェス。 ## L2 の SDK が提供しなければならないもの SDK を選ぶか新しい言語に移植する場合、これが L2 のビルドターゲットです: * マルチテナントルーティングのフック付きで、認証されたプリンシパルをスコープされたアカウントに解決する **アカウントストア抽象**。 * 少なくとも API キーと bearer トークンの形状のための **認証プリミティブ**、加えてそれらを合成する方法。 * **ブランド解決 / エージェントレジストリルックアップ** — または SDK がネイティブに出荷しない場合は文書化された拡張ポイント。 * 適合性テストサーフェスが本番アカウントでのディスパッチを拒否するよう SDK 境界で強制される **sandbox 対 live アカウントフラグ**。 累積的なクロス層のストーリー(L0+L1+L2 が何をもたらすか)については、[SDK スタックリファレンス](/docs/building/cross-cutting/sdk-stack#l2--auth--registry) を参照。 ## この層のページ * **[Authentication](/docs/building/by-layer/L2/authentication)** — 認証情報と権限。 * **[Account state](/docs/building/by-layer/L2/account-state)** — マルチテナントアカウント解決。 * **[Accounts and agents](/docs/building/by-layer/L2/accounts-and-agents)** — アカウントスコーピングとエージェントアイデンティティの関係。 * **[Context & sessions](/docs/building/by-layer/L2/context-sessions)** — リクエストをまたいだプリンシパル状態の管理。 # 非同期オペレーション Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L3/async-operations AdCP の非同期オペレーションガイド: 同期・非同期・対話的(入力待ち)タスク種別のポーリング、SSE ストリーミング、タイムアウト戦略を含む処理方法。 AdCP のオペレーションは秒から日までかかることがあります。サーバーは、オペレーションにどれだけ時間がかかるか、何がブロックしているかに基づいて応答方法を決定します。 ## 30 秒ルール 任意の AdCP タスクは次のいずれかのステータスを返せます。サーバーは、関与する作業について知っていることに基づいて選択します: | 想定所要時間 | ステータス | 呼び出し元がすること | | ------------------ | ---------------------- | ---------------------------------------------------------------------------------------- | | 30 秒未満 | `completed` / `failed` | 結果はインライン — 完了 | | 30 秒超、サーバーが能動的に処理中 | `working` | 帯域外の進捗シグナル。接続は開いたままで、準備できたら結果が届く。呼び出し元は待つだけ | | 外部依存でブロック | `submitted` | 真に非同期 — `task_id` でポーリング。設定されていれば `push_notification_config` で通知も配信できる。結果は数時間〜数日かかることがある | | 人の入力でブロック | `input-required` | 呼び出し元は要求された入力を提供して継続する | **`working` は非同期ではありません。** これはサーバーが処理を続けながら帯域外(MCP ステータス通知または SSE 経由)で送る進捗シグナルです。呼び出し元は接続を保持し、準備できたら結果を受け取ります — ポーリングも Webhook もありません。「少し時間がかかっていますが、対応中です」と考えてください。 **`submitted` は非同期です。** オペレーションはサーバーの制御外の何か — パブリッシャー承認、人間のレビュー、サードパーティ処理 — でブロックされています。呼び出し元は常に `task_id` で AdCP タスクステータス面をポーリングできます。設定された Webhook は、バックグラウンドワークフロー向けの追加の通知チャネルであり、ポーリングの置き換えではありません。 :::tip `submitted` オペレーションの Webhook **Webhook** はバックグラウンドの `submitted` オペレーションに推奨されます — 任意のトランスポート(MCP、A2A、REST)で動作し、単一セッションより長く続くオペレーションを扱えます。MCP/REST では、Webhook チャネルはタスクリクエストの `push_notification_config` で要求します。A2A では、snake\_case のスキルパラメーターではなく `configuration.pushNotificationConfig` のトランスポート設定のままです。リクエストが Webhook チャネルを含み、サーバーが `submitted` を返してタスクを受け入れた場合、サーバーは少なくとも終端の完了または失敗の通知をそのチャネルに配信しなければなりません(MUST)。中間の進捗通知は、別のオペレーション固有の契約が要求しない限り任意です。サーバーが要求された Webhook チャネルを尊重できない場合、配信を黙って格下げするのではなく、構造化エラーでリクエストを拒否しなければなりません(MUST)。[Push Notifications](/docs/building/by-layer/L3/webhooks) を参照してください。 **ポーリング**は AdCP タスクポーリング面を介して `submitted` タスクに常に有効です。3.x では、その面はレガシーの `tasks/get` で、セラーがエイリアスを宣伝する場合は任意で `get_task_status` を使用します。どちらの名前も同じペイロード形状を受け入れます。マルチアカウントの呼び出し元は、セラーがタスクの可視性を認証済みアカウント + プリンシパルのペアにスコープできるよう `account` を含めるべきです(SHOULD)。下記の[ポーリングパターン](#submitted-オペレーションのポーリング)を参照してください。 **トランスポートネイティブなタスクは AdCP ライフサイクルではありません。** MCP Tasks や A2A タスク更新は AdCP レスポンスを運んだりストリーミングしたりできますが、耐久性のあるタスク状態は AdCP ペイロード(`task_id`、`status`、Webhook ペイロード、AdCP ポーリング/リコンシリエーション)です。[MCP ガイド](/docs/building/by-layer/L0/mcp-guide#mcp-tasks-as-a-transport-wrapper)を参照してください。 ::: ## オペレーションの例 ### 同期(即時) | Operation | Description | | ------------------------------------ | ----------------------------------- | | `get_adcp_capabilities` | Agent capability discovery | | `list_creative_formats` | Format catalog | | `build_creative` (library retrieval) | Resolving an existing `creative_id` | ### 人の入力が必要な場合がある | Operation | Description | | ----------------------------- | ---------------------------------------------------- | | `get_products` | When brief is vague or needs clarification | | `create_media_buy` | When approval is required | | `build_creative` (generation) | When creative direction or asset selection is needed | ### 非同期(`submitted`)になる場合がある | Operation | Description | | ----------------------------------- | -------------------------------------------------------------------------- | | `create_media_buy` | Publisher approval workflows | | `update_media_buy` | Manual seller review for budget, targeting, or creative changes | | `get_products` (`brief` / `refine`) | Bespoke curation that depends on upstream inventory queries or HITL review | | `get_signals` (`brief`) | Semantic signal discovery that depends on slow provider queries or review | | `sync_creatives` | Asset review and transcoding pipelines | | `build_creative` (with review) | Human creative review before finalizing | | `sync_catalogs` | Large feeds or feeds requiring content policy review | | `activate_signal` | Platform deployment pipelines | これらのオペレーションは外部システムと連携するか人の承認を必要とします。ホールセールのフィード読み取りは例外です。`get_products buying_mode: "wholesale"` と `get_signals discovery_mode: "wholesale"` は同期的な修復/リコンシリエーション読み取りのままで、`submitted` ではなく `incomplete[]` で部分完了を報告します。 ## タイムアウト設定 ステータスに応じて妥当なタイムアウトを設定します: ```javascript theme={null} const TIMEOUTS = { sync: 30_000, // 30 seconds — most operations complete here working: 300_000, // 5 minutes — server is actively processing interactive: 300_000, // 5 minutes for human input submitted: 86_400_000 // 24 hours for external dependencies }; function getTimeout(status) { if (status === 'submitted') return TIMEOUTS.submitted; if (status === 'working') return TIMEOUTS.working; if (status === 'input-required') return TIMEOUTS.interactive; return TIMEOUTS.sync; } ``` `working` はポーリング間隔ではなく接続タイムアウト(どれだけ開いたまま保持するか)を使います。サーバーは進捗を帯域外で送り、同じ接続で結果を配信します。`submitted` はポーリングまたは Webhook 配信のウィンドウを使います。Webhook を主要な通知パスとして設定していても、30 秒のポーリング間隔は妥当なデフォルトです。 ## Human-in-the-Loop ワークフロー ### 設計原則 1. **デフォルトは任意** - 承認は実装ごとに設定 2. **明確なメッセージ** - 何を承認するかを明示 3. **適切なタイムアウト** - 人の入力で無期限にブロックしません 4. **監査証跡** - 誰が何をいつ承認したか記録 非同期オペレーションにおける Human-in-the-Loop パターンは [Embedded Human Judgment](/docs/governance/embedded-human-judgment) フレームワークを体現しています — 人間の判断は後付けではなく、システム設計に組み込まれます。 ### 承認パターン ```javascript theme={null} async function handleApprovalWorkflow(response) { if (response.status === 'input-required' && needsApproval(response)) { // Show approval UI with context const approval = await showApprovalUI({ title: "Campaign Approval Required", message: response.message, details: response, // Task fields are at top level approver: getCurrentUser() }); // Send approval decision const decision = { approved: approval.approved, notes: approval.notes, approver_id: approval.approver_id, timestamp: new Date().toISOString() }; return sendFollowUp(response.context_id, decision); } } ``` ### よくある承認トリガー * **予算閾値**: \$100K 超のキャンペーン * **新規広告主**: 初回の購入者 * **センシティブコンテンツ**: 特定業界や話題 * **手動インベントリ**: パブリッシャー承認が必要なプレミアム枠 ## 進捗トラッキング ### 進捗更新 長時間処理では進捗情報が提供されることがあります: ```json theme={null} { "status": "working", "message": "Processing creative assets...", "task_id": "task-456", "progress": 45, "step": "transcoding_video", "steps_completed": ["upload", "validation"], "steps_remaining": ["transcoding_video", "thumbnail_generation", "cdn_distribution"] } ``` ### 進捗表示 ```javascript theme={null} function displayProgress(response) { if (response.progress !== undefined) { updateProgressBar(response.progress); } if (response.step) { updateStatusText(`Step: ${response.step}`); } if (response.steps_completed) { updateStepsList(response.steps_completed, response.steps_remaining); } // Always show the message updateMessage(response.message); } ``` ## プロトコル非依存パターン これらのパターンは MCP/A2A どちらでも機能します。 ### 確認フローを含む商品探索 ```javascript theme={null} async function discoverProducts(brief) { let response = await adcp.send({ task: 'get_products', brief: brief }); // 確認ループを処理 while (response.status === 'input-required') { const moreInfo = await promptUser(response.message); response = await adcp.send({ context_id: response.context_id, additional_info: moreInfo }); } if (response.status === 'completed') { return response.products; // Task fields are at top level } else if (response.status === 'failed') { throw new Error(response.message); } } ``` ### 承認フローを含むキャンペーン作成 ```javascript theme={null} async function createCampaign(packages, budget) { let response = await adcp.send({ task: 'create_media_buy', packages: packages, total_budget: budget }); // Handle approval if needed if (response.status === 'input-required') { const approved = await getApproval(response.message); if (!approved) { throw new Error('Campaign creation not approved'); } response = await adcp.send({ context_id: response.context_id, approved: true }); } // 'working' はサーバーが能動的に処理中 — 結果が届く // 'submitted' は外部依存でブロック — task_id でポーリング。 // 設定されていれば Webhook も完了を配信し得る。 if (response.status === 'submitted') { response = await pollForResult(response.task_id); } if (response.status === 'completed') { return response.media_buy_id; // Task fields are at top level } else { throw new Error(response.message); } } ``` ### `submitted` オペレーションのポーリング ポーリングは `submitted` オペレーションに常に有効です。設定されていれば Webhook も完了を配信し得ますが、呼び出し元はタスクポーリング面を通じてリコンサイルできます。`working` はポーリングしないでください — サーバーは開いた接続で結果を配信します。 ```javascript theme={null} async function pollForResult(taskId, options = {}) { const { maxWait = 86_400_000, pollInterval = 30_000 } = options; const startTime = Date.now(); while (true) { if (Date.now() - startTime > maxWait) { throw new Error('Operation timed out'); } await sleep(pollInterval); const response = await adcp.call('get_task_status', { task_id: taskId, include_result: true }); if (['completed', 'failed', 'canceled'].includes(response.status)) { return response; } } } ``` ## 非同期前提の設計 ### 状態を永続化します 非同期処理でメモリ状態に依存しない: ```javascript theme={null} class AsyncOperationTracker { constructor(db) { this.db = db; } async startOperation(taskId, operationType, request) { await this.db.operations.insert({ task_id: taskId, type: operationType, status: 'submitted', request: request, created_at: new Date(), updated_at: new Date() }); } async updateStatus(taskId, status, result = null) { await this.db.operations.update( { task_id: taskId }, { status: status, result: result, updated_at: new Date() } ); } async getPendingOperations() { return this.db.operations.find({ status: { $in: ['submitted', 'working', 'input-required'] } }); } } ``` ### 再起動に耐える オーケストレーター再起動後に追跡を再開: ```javascript theme={null} async function onStartup() { const tracker = new AsyncOperationTracker(db); const pending = await tracker.getPendingOperations(); for (const operation of pending) { // Check current status on server const response = await adcp.call('get_task_status', { task_id: operation.task_id, include_result: true }); // Update local state await tracker.updateStatus(operation.task_id, response.status, response); // Resume polling if still pending if (['submitted', 'working'].includes(response.status)) { startPolling(operation.task_id); } } } ``` ## ベストプラクティス 1. **非同期前提で設計** - どの操作も時間がかかる前提 2. **状態を永続化** - メモリだけに依存しません 3. **再起動を考慮** - 起動時に追跡を再開 4. **タイムアウトを実装** - 無限に待たない 5. **進捗を表示** - ユーザーに状況を伝える 6. **キャンセル対応** - 長時間処理をキャンセル可能に 7. **監査証跡** - ステータス遷移をログ ## 次のステップ * **Webhooks**: ポーリングの代わりにプッシュ通知を使う場合は [Webhooks](/docs/building/by-layer/L3/webhooks) * **Task Lifecycle**: ステータス処理の詳細は [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) * **Orchestrator Design**: 本番パターンは [Orchestrator Design](/docs/building/operating/orchestrator-design) # コンプライアンステストコントローラー Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L3/comply-test-controller セラー側の遷移を決定的にトリガーすることで、ストーリーボードランナーが完全なライフサイクルステートマシンを歩けるようにするオプションのサンドボックスツール。 # コンプライアンステストコントローラー **コンプライアンステストコントローラーは開発/ステージング専用のアフォーダンスであり、本番時の概念ではありません。** AAO グレーディングはそれを要求も使用もしません。AAO コンプライアンスハートビートは、すべてのリクエストに `account.sandbox: true` を付けてセラーの登録された本番 URL に対してストーリーボードを駆動し、セラーの本番スタックがフラグを尊重する責任を負います — コントローラーエンドポイントは不要です。 セラーは、自身の統合テストをサポートするため開発またはステージング環境でコントローラーを実装してもよい(MAY) — ライフサイクルステートマシンを決定的に歩く、フィクスチャをシードする、そうでなければ実時間を待つ必要がある遷移を強制する。それがその目的です。本番デプロイで公開してはなりません(MUST NOT)(下の [Sandbox gating](#sandbox-gating) を参照)。 コントローラーが AAO Verified (Sandbox) にどう関係するか混乱していますか? フレーミング決定については [#4379](https://github.com/adcontextprotocol/adcp/issues/4379) を参照してください: (Sandbox) は「実本番エンドポイントが完全なストーリーボードスイート全体でサンドボックスフラグ付きトラフィックを正しく処理する」ことを証明します。コントローラーは *あなたの* テストのための開発者側のアフォーダンスであり、AAO 側のグレーディングメカニズムではありません。 AdCP は、アカウント、クリエイティブ、メディアバイ、SI セッション、配信レポートのライフサイクルステートマシンを定義します。これらのステートマシンの多くの遷移はセラー開始です — クリエイティブ承認、アカウント停止、予算枯渇、配信計上。ストーリーボードランナーはバイヤー開始フローのみを行使でき、セラー開始遷移を未テストのままにします。 **コンプライアンステストコントローラー** は、決定的ローカルテストをサポートするためセラーが開発/ステージング環境で公開するオプションのツールです。ランナーがセラー側の状態遷移をオンデマンドでトリガーでき、開発中にエンドツーエンドのライフサイクル検証を可能にします。 ## 動機 テストコントローラーなしでは、コンプライアンステストは観測的です: アクションを発火し、存在する状態を読み返し、進む。これはスキーマ違反を捕捉しますが動作違反は捕捉しません。 | Track | Observational (today) | Deterministic (with controller) | | --------------- | -------------------------------------------- | --------------------------------------------------------------- | | **Creative** | Sync → 初期ステータスを観測 | `processing` → `approved` → `archived` を歩く。理由付きで `rejected` を強制 | | **Account** | 既存ステータスを読む | `suspended` を強制 → 操作ゲートを検証 → 再アクティブ化 | | **SI sessions** | Initiate → message → terminate | タイムアウト理由で `terminated` を強制 → 次の呼び出しで `SESSION_NOT_FOUND` を検証 | | **Reporting** | `get_media_buy_delivery` を呼ぶ → データが存在することを望む | 配信をシミュレート → ロールアップを検証 | | **Budgeting** | 予算付きでバイを作成 → 読み返す | しきい値まで支出をシミュレート → アラートと `payment_required` を検証 | | **Media buy** | Create → pause → resume | セラー開始 `rejected` を強制 → 終端状態を検証 | ## Sandbox gating セラーは本番デプロイで `comply_test_controller` を公開してはなりません(MUST NOT) — 誰にも、どの表面でも。ツールは `tools/list`(MCP)とエージェントカードの `skills[]`(A2A)から不在でなければならず(MUST)、`compliance_testing` ブロックは `get_adcp_capabilities` から不在でなければならず(MUST)、ディスパッチはトランスポートの標準未知ツールエラー(例: MCP の JSON-RPC `-32601 Method not found`、A2A の未知スキル拒否)を返さなければなりません(MUST) — ツールを実装しないセラーの同一トランスポートレスポンスと区別できない。これらの表面のいずれかでツールを公開する本番デプロイは、ディスパッチがゲートされているかどうかにかかわらず非適合です。 正準パターンは 2 つのデプロイです: 1 つは本番(コントローラー未配線)、1 つはサンドボックス/ステージング(すべての来訪者向けにコントローラー配線)。セラーはサンドボックス/ステージングデプロイでのみ `comply_test_controller` を公開します。そのようなデプロイに認証できる任意のプリンシパルがそれを呼べます。 セラーは代わりに、混合サンドボックス/ライブプリンシパルを持つ単一デプロイを実行し、解決されたアカウントのモードでゲートしてプリンシパルごとにツールを投影してもよい(MAY)。これは実装パターンであり、正準モデルではありません。このパターンを選ぶセラーは 3 つの表面すべてを一貫してゲートしなければなりません(MUST): `tools/list`(または `skills[]`)、`compliance_testing` ケイパビリティブロック、ディスパッチ。部分的な投影 — 例: `tools/list` をゲートするが `compliance_testing` ブロックをライブプリンシパルに可視のまま残す、または名前でプローブするライブプリンシパルに(未知ツールではなく)`FORBIDDEN` を返す — は非適合です。それはデプロイスコーピングが閉じるディスカバリーサイドチャネルを再開きます。 `FORBIDDEN` は、呼び出し元がコントローラーを呼ぶ権限があるが `params` が非サンドボックスアカウントを参照するサンドボックス内ケースのために予約されています。サンドボックスゲートは、ツール登録時だけでなく、アカウント参照についてリクエストごとに強制されます。 サンドボックス認証情報のプロビジョニングと、本番をサンドボックス/ステージングデプロイから分離するメカニズムはセラー固有で、この仕様の範囲外です。セラーは、ストーリーボードランナーが適切に接続できるよう、サンドボックスアクセスメカニズムを文書化しなければなりません(MUST)。 ストーリーボードランナーは、本番と信じる接続上で `tools/list`(または `skills[]`)内の `comply_test_controller` の存在、または `get_adcp_capabilities` 内の `compliance_testing` ブロックの存在を、ハードな適合性失敗として扱わなければなりません(MUST)。 ## ツール定義 **Schemas**: [`comply-test-controller-request.json`](https://adcontextprotocol.org/schemas/v3/compliance/comply-test-controller-request.json) | [`comply-test-controller-response.json`](https://adcontextprotocol.org/schemas/v3/compliance/comply-test-controller-response.json) コンプライアンステストコントローラーを実装するセラーは以下をしなければなりません(MUST): * サンドボックスモードでのみツールを公開(上のサンドボックスゲートを参照) * 本番と同じ状態遷移ルールを強制 — 無効な遷移はエラーを返さなければならない(MUST) * 強制された状態変更を後続の読み取り(`list_creatives`、`get_media_buys` など)に反映 ```json theme={null} { "name": "comply_test_controller", "description": "Triggers seller-side state transitions for compliance testing. Sandbox only.", "inputSchema": { "type": "object", "properties": { "scenario": { "type": "string", "enum": [ "list_scenarios", "force_creative_status", "force_creative_purge", "force_account_status", "force_media_buy_status", "force_create_media_buy_arm", "force_get_products_arm", "force_get_signals_arm", "force_task_completion", "force_session_status", "simulate_delivery", "simulate_budget_spend", "seed_account", "seed_product", "seed_pricing_option", "seed_creative", "seed_plan", "seed_media_buy", "seed_creative_format", "seed_measurement_catalog", "query_upstream_traffic", "query_provenance_audit_observations", "force_upstream_unavailable" ], "description": "The seller-side transition or fixture-seed to trigger." }, "params": { "type": "object", "description": "Scenario-specific parameters. Omit for list_scenarios. force_creative_status: {creative_id, status, rejection_reason?}. force_creative_purge: {creative_id, purge_kind?, reason_code?, reason_detail?}. force_account_status: {account_id, status}. force_media_buy_status: {media_buy_id, status, rejection_reason?}. force_create_media_buy_arm: {arm, task_id?, message?} - task_id required when arm = submitted. force_get_products_arm: {arm, task_id?, message?} - task_id required when arm = submitted. force_get_signals_arm: {arm, task_id?, message?} - task_id required when arm = submitted. force_task_completion: {task_id, result}. force_session_status: {session_id, status, termination_reason?}. simulate_delivery: {media_buy_id, impressions?, clicks?, reported_spend?, conversions?, reach?, frequency?, reach_window?, viewability?}. simulate_budget_spend: {account_id|media_buy_id, spend_percentage}. seed_account: {account_id, fixture?}. seed_product: {product_id, fixture?}. seed_pricing_option: {product_id, pricing_option_id, fixture?}. seed_creative: {creative_id, fixture?}. seed_plan: {plan_id, fixture?}. seed_media_buy: {media_buy_id, fixture?}. seed_creative_format: {format_id, fixture?}. seed_measurement_catalog: {vendor, metrics[]}. query_upstream_traffic: {since_timestamp?, endpoint_pattern?, limit?, attestation_mode?, identifier_value_digests?}. query_provenance_audit_observations: {creative_id}. force_upstream_unavailable: {tool, upstream_name?}." } }, "required": ["scenario"] } } ``` `params` の description は、MCP クライアント(LLM を含む)が条件付きスキーマ分岐ではなく description を読むため、各シナリオの param 形状をインラインします。SDK コード生成に適した形式的検証スキーマについては、下のシナリオごとの定義を参照してください。 ## Scenarios ### `force_creative_status` クリエイティブを指定されたステータスに遷移させます。セラーは [クリエイティブライフサイクルステートマシン](/docs/creative/specification#creative-status-lifecycle) に従い有効な遷移を強制しなければなりません(MUST)。 **Params:** | Field | Type | Required | Description | | ------------------ | ----------------------------------------------------------------------------------------- | ------------------------- | ----------------- | | `creative_id` | string | Yes | 遷移するクリエイティブ | | `status` | `processing` \| `pending_review` \| `approved` \| `suspended` \| `rejected` \| `archived` | Yes | ターゲットステータス | | `rejection_reason` | string | `status` = `rejected` のとき | 拒否の理由 | | `reason_code` | CreativeEventReasonCode | No | ライフサイクル遷移の理由コード | | `reason_detail` | string | No | ライフサイクル遷移の人間可読な詳細 | **Example:** ```json theme={null} { "scenario": "force_creative_status", "params": { "creative_id": "cr-123", "status": "rejected", "reason_code": "policy_revocation", "rejection_reason": "Brand safety policy violation" } } ``` ### `force_account_status` アカウントを指定されたステータスに遷移させます。セラーは [アカウントライフサイクルルール](/docs/accounts/overview#account-status-lifecycle) を強制しなければなりません(MUST) — 終端状態(`rejected`、`closed`)は退出できません。 **Params:** | Field | Type | Required | Description | | ------------ | --------------------------------------------------------------------------------------------- | -------- | ----------- | | `account_id` | string | Yes | 遷移するアカウント | | `status` | `active` \| `pending_approval` \| `rejected` \| `payment_required` \| `suspended` \| `closed` | Yes | ターゲットステータス | **Example:** ```json theme={null} { "scenario": "force_account_status", "params": { "account_id": "acct-456", "status": "payment_required" } } ``` ### `force_media_buy_status` メディアバイを指定されたステータスに遷移させます。セラーはメディアバイライフサイクルを強制しなければなりません(MUST) — `rejected` は `pending_creatives` または `pending_start` からのみ有効です。 **Params:** | Field | Type | Required | Description | | ------------------ | --------------------------------------------------------------------------------------------------------- | ------------------------- | ----------- | | `media_buy_id` | string | Yes | 遷移するメディアバイ | | `status` | `pending_creatives` \| `pending_start` \| `active` \| `paused` \| `completed` \| `rejected` \| `canceled` | Yes | ターゲットステータス | | `rejection_reason` | string | `status` = `rejected` のとき | 拒否の理由 | **Example:** ```json theme={null} { "scenario": "force_media_buy_status", "params": { "media_buy_id": "mb-789", "status": "rejected", "rejection_reason": "Policy violation" } } ``` ### `force_create_media_buy_arm` 呼び出し元の認証済みサンドボックスアカウントからの次の [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) 呼び出しを特定のレスポンスアームに形作ります。v1 は 2 つのアームをサポートします: `submitted`(非同期タスクエンベロープ、まだ `media_buy_id` なし)と `input-required`(errors 分岐)。`force_media_buy_status` と異なり、エンティティは遷移しません — まだメディアバイがありません — したがってレスポンスは `previous_state`/`current_state` ではなく `forced.arm` を運びます。 submitted アームのワイヤー形状はそれ以外は実装依存です: ほとんどのセラーはほとんどのバイを同期的にルーティングし、どのバイヤー側リクエスト形状も確実に非同期をトリガーしません。このシナリオはストーリーボードがアームをピン留めできるようにし、退行したセラー(例: `status: submitted` の下で `media_buy_id` を発行)が黙って適合性を通過できないようにします。 **Params:** | Field | Type | Required | Description | | --------- | ------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `arm` | `submitted` \| `input-required` | Yes | 次の `create_media_buy` 呼び出しのターゲットレスポンスアーム | | `task_id` | string | `arm` = `submitted` のとき | セラーが submitted エンベロープ上でそのまま発行しなければならず(MUST)、後続の `tasks/get` ポーリングで受理しなければならない(MUST)決定的タスクハンドル(最大 128 文字)。サンドボックス task\_id は呼び出し元不透明な文字列。本番 task-id 形式ルールは適用されない。 | | `message` | string | No | セラーの `create_media_buy` レスポンス上にそのまま表示される人間可読な説明。プレーンテキスト、最大 2000 文字。結果のレスポンスを消費するバイヤーは、[submitted エンベロープの `message`](https://adcontextprotocol.org/schemas/v3/media-buy/create-media-buy-response.json) について文書化されたプロンプトインジェクションサニタイズを適用しなければならない(MUST) — このシナリオは、ランナーがバイヤー側サニタイズをテストするため敵対的文字列を注入する自然な場所。 | **Example:** ```json theme={null} { "scenario": "force_create_media_buy_arm", "params": { "arm": "submitted", "task_id": "task_async_signed_io_q2", "message": "Awaiting IO signature from sales team; typical turnaround 2–4 hours" } } ``` **Response.** 登録されたディレクティブを運ぶ `ForcedDirectiveSuccess` 形状: ```json theme={null} { "success": true, "forced": { "arm": "submitted", "task_id": "task_async_signed_io_q2" }, "message": "Next create_media_buy call will return the submitted arm with task_id task_async_signed_io_q2" } ``` `forced.task_id` は `arm: submitted` のときのみ存在します。 **Consumption and idempotency.** ディレクティブは呼び出し元の認証済みサンドボックスアカウント(アカウント + プリンシパルペア)にキーされ、そのアカウントからの次の `create_media_buy` 呼び出しで消費されます。新しいディレクティブなしの後続呼び出しはセラーのデフォルトアームを返します。バイヤー側 `idempotency_key` セマンティクスは変わりません: 呼び出し元が既にディレクティブを消費した `create_media_buy` リクエストをリプレイする場合、セラーはキャッシュされたレスポンスをリプレイしなければならず(MUST)(リクエスト冪等性キャッシュが勝つ)、今や空のディレクティブスロットに対して再評価してはなりません(MUST NOT)。セラーは、同じトランスポート接続内でも、異なるアカウントまたはプリンシパルからの `create_media_buy` 呼び出しに対してディレクティブをマッチしてはなりません(MUST NOT)。ディレクティブが消費される前の 2 つ目の `force_create_media_buy_arm` 呼び出しは前のものを上書きします。 ### `force_get_products_arm` / `force_get_signals_arm` 呼び出し元の認証済みサンドボックスアカウント(アカウント + プリンシパルペア)からの次のキュレートディスカバリー呼び出しを submitted タスクエンベロープに形作ります。`force_get_products_arm` は `buying_mode: "brief"` または `"refine"` の `get_products` にのみ適用されます。`force_get_signals_arm` は `discovery_mode: "brief"`(または省略、brief がデフォルト)の `get_signals` にのみ適用されます。ホールセールフィード読み取りは同期フィードアクセスであり、これらのディレクティブを消費してはならず(MUST NOT)、ディレクティブが存在するというだけで Submitted アームを返してはなりません(MUST NOT)。 ディレクティブは `force_create_media_buy_arm` と同じ理由で存在します: バイヤーはリクエスト形状だけから確実に非同期ディスカバリーをトリガーできませんが、適合性はクライアントとセラーがタスク結果パスを尊重することを証明する決定的な方法を必要とします。submitted エンベロープは `status` と `task_id`(プラス `message` のようなオプションの助言フィールド)のみを運びます。終端の `products[]`、`proposals[]`、または `signals[]` は、`get_task_status`(レガシー `tasks/get`)と任意の登録されたプッシュ通知を通じてタスク完了時に着地します。 **Params:** | Field | Type | Required | Description | | --------- | ----------- | ----------------------- | -------------------------------------------------------------------------------------------- | | `arm` | `submitted` | Yes | 次の一致するディスカバリー呼び出しのターゲットレスポンスアーム。 | | `task_id` | string | `arm` = `submitted` のとき | セラーが submitted エンベロープ上でそのまま発行しなければならず(MUST)、後続のポーリングで受理しなければならない(MUST)決定的タスクハンドル(最大 128 文字)。 | | `message` | string | No | submitted ディスカバリーレスポンス上にそのまま表示される人間可読な説明。プレーンテキスト、最大 2000 文字。 | **Examples:** ```json theme={null} { "scenario": "force_get_products_arm", "account": { "brand": { "domain": "acmeoutdoor.example" }, "operator": "pinnacle-agency.example", "sandbox": true }, "params": { "arm": "submitted", "task_id": "task_async_products_acme_q3", "message": "Custom product curation queued; typical turnaround 10 minutes" } } ``` ```json theme={null} { "scenario": "force_get_signals_arm", "account": { "brand": { "domain": "novamotors.example" }, "operator": "pinnacle-agency.example", "sandbox": true }, "params": { "arm": "submitted", "task_id": "task_async_signals_nova_ev", "message": "Signal discovery queued; typical turnaround 10 minutes" } } ``` **Response.** 両シナリオとも `force_create_media_buy_arm` と同じ `ForcedDirectiveSuccess` 形状を返し、`forced.arm` と `forced.task_id` を運びます。 **Consumption and idempotency.** ディレクティブは呼び出し元の認証済みサンドボックスアカウント(アカウント + プリンシパルペア)にキーされ、その同じアカウントからの次の一致するディスカバリー呼び出しで消費されます。セラーは、製品ディレクティブを `get_signals` に、シグナルディレクティブを `get_products` に、brief/refine ディレクティブをホールセールモードに、または任意のディレクティブを異なるアカウントまたはプリンシパルにマッチしてはなりません(MUST NOT)。消費前の同じ操作に対する 2 つ目のディレクティブは前のディレクティブを上書きします。リクエスト冪等性リプレイセマンティクスは変わりません: ディレクティブを消費したディスカバリーリクエストがリプレイされる場合、セラーはキャッシュされた submitted エンベロープを返し、新しいディレクティブを消費しません。 ### `force_task_completion` 以前に submitted された非同期タスクを、バイヤー供給の結果ペイロードで `completed` に解決します。`force_*_arm` シナリオの相棒: それらのシナリオはセラーを submitted エンベロープに駆動します。これはタスクストアエントリーを `completed` に遷移させ登録された結果をスタンプすることでループを閉じます。バイヤーは、`push_notification_config.url` へのセラーのプッシュ通知と、`status: "completed"` をレポートする後続の `get_task_status` 呼び出しを通じて完了を観測します。呼び出し元が `include_result: true` を要求するとき、`get_task_status` は元の非同期操作に一致する型付き終端結果ペイロードを返します。 submitted → completed ライフサイクルはそれ以外は非決定的です — 実タスク完了は帯域外シグナル(IO 副署名、バッチプロセッサー cron、ガバナンス人間レビュー)に乗ります。ストーリーボードは待てません。このシナリオは、ランナーがディレクティブ登録直後に完了を決定的にピン留めできるようにし、バイヤー側ポーリングアサーションがバイヤーが本番で観測するのと同じワイヤー形状で発火するようにします。 **Params:** | Field | Type | Required | Description | | --------- | ----------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `task_id` | string | Yes | 解決するタスク。呼び出し元の認証済みサンドボックスアカウント内で解決しなければならない(MUST)。セラーは他のアカウントに属する `task_id` に対して `NOT_FOUND` を返さなければならない(MUST)(上のマルチテナント規約に従い `FORBIDDEN` ではない)。通常は先の `create_media_buy` submitted エンベロープレスポンスから捕捉(または `force_create_media_buy_arm` 経由で登録)。 | | `result` | [`async-response-data`](https://adcontextprotocol.org/schemas/v3/core/async-response-data.json) | Yes | 記録する完了ペイロード。プッシュ通知 webhook と `tasks/get` ポーリングレスポンスが使う同じ `anyOf` union に対して検証される。`create_media_buy` については、これは `media_buy_id` と `packages` を持つ `CreateMediaBuyResponse`。`result` がタスクの元のメソッドのレスポンス分岐に対して検証されない場合、セラーは `INVALID_PARAMS` を発行しなければならない(MUST)。セラーは 256 KB を超える `result` ペイロードを `INVALID_PARAMS` で拒否してもよい(MAY)。ストーリーボードはこの下に留まらなければならない(MUST)。 | **Example:** ```json theme={null} { "scenario": "force_task_completion", "params": { "task_id": "task_async_signed_io_q2", "result": { "media_buy_id": "mb_async_signed_io_q2", "status": "active", "packages": [ { "package_id": "pkg-0", "product_id": "async_signed_io_q2", "budget": 30000 } ] } } } ``` **Response.** 状態遷移成功形状を返します: ```json theme={null} { "success": true, "previous_state": "submitted", "current_state": "completed", "message": "Task task_async_signed_io_q2 transitioned from submitted to completed" } ``` ソース状態は `submitted`、`working`、または `input-required` でなければならない(MUST)。他のソースは `INVALID_TRANSITION` を返します。`task_id` が呼び出し元のアカウントに未知なら、セラーは `NOT_FOUND` を発行しなければならず(MUST)、タスクが既に終端(`completed` / `failed` / `canceled`)なら `INVALID_TRANSITION` を発行しなければなりません(MUST)。タスクを `failed` に強制することはこのシナリオの範囲外です。`force_create_media_buy_arm` の input-required アームがバイヤー入力必要失敗パスをカバーします。 **Replay semantics.** タスクが終端になる前の同一 params でのリプレイは冪等な no-op です。タスクが終端になる前の分岐する params でのリプレイは登録された結果を上書きしなければなりません(MUST)(last-write-wins) — `force_create_media_buy_arm` の「2 つ目の呼び出しが上書き」と同じ前例。タスクが終端になった後、すべてのリプレイは params にかかわらず `INVALID_TRANSITION` を返します。 **Cross-protocol obligations.** * **プッシュ通知。** バイヤーが元の `create_media_buy` で `push_notification_config.url` を登録した場合、完了強制は登録された `result` ペイロードで webhook を発火しなければなりません(MUST)(完了データの正準 3.0 配信パス)。そうでなければストーリーボードは終端ステータスのポーリングのみをテストでき、結果のプッシュ配信はテストできません。 * **`simulate_delivery` / `simulate_budget_spend`。** `media_buy_id` を運ぶ有効な `CreateMediaBuyResponse` で completed に強制されると、結果のメディアバイはそれらのシナリオでアドレス可能でなければなりません(MUST)。`force_task_completion` を通じたラウンドトリップは、同期フローを通らずにメディアバイを必要とするストーリーボードのサポートされたパスです。 **Buyer-side observation.** このシナリオが実行された後、登録された `result` はすべての呼び出し元供給フィールドを保持してバイヤーの `push_notification_config.url`(3.0 正準パス)に配信されます。セラーはセラー制御フィールド(例: `created_at`、`dsp_*` ID、正規化された通貨ケーシング)で拡張してもよい(MAY)が、呼び出し元供給値を上書きしてはなりません(MUST NOT)。後続の `tasks/get(task_id)` は `status: "completed"` を返さなければなりません(MUST)。`result` ペイロードはサンドボックスでバイヤー制御でセラーのストアを通じてラウンドトリップします — webhook 経由でそれを受け取るバイヤーは、バイト自体を起源としたという事実にかかわらず、ペイロードを信頼できないセラー出力として扱わなければなりません(MUST)(AdCP 規約に従い)。これは `force_task_completion` を、webhook 配信パスでバイヤー側サニタイズをテストするときランナーが敵対的ペイロードを注入する自然な場所にします。 ### `force_session_status` SI セッションを終端ステータスに遷移させます。そうでなければ実タイムアウトを待つ必要があるタイムアウトと終了シナリオのテストを可能にします。`termination_reason` param は原因をシミュレートし、ストーリーボードランナーがセラーが後続レスポンスで正しい理由をレポートすることを検証できます。 **Params:** | Field | Type | Required | Description | | -------------------- | -------------------------- | --------------------------- | ---------------------------------------------------------------- | | `session_id` | string | Yes | 遷移するセッション | | `status` | `complete` \| `terminated` | Yes | ターゲット終端ステータス | | `termination_reason` | string | `status` = `terminated` のとき | 終了の理由(例: `session_timeout`、`host_terminated`、`policy_violation`) | **Example:** ```json theme={null} { "scenario": "force_session_status", "params": { "session_id": "sess-abc", "status": "terminated", "termination_reason": "session_timeout" } } ``` ### `simulate_delivery` メディアバイの合成配信データを注入します。`get_media_buy_delivery` への後続呼び出しはこのデータを反映しなければなりません(MUST)。配信シミュレーションは加算的です — 各呼び出しが既存の配信合計に加算します。 **配信と予算は独立したシステムです。** `simulate_delivery` は広告サーバーがレポートするものを記録します。`simulate_budget_spend` は課金システムが追跡するものを記録します。セラーの本番システムはこれらを結合してもしなくてもよい — テストコントローラーは結合を仮定しません。 **Params:** | Field | Type | Required | Description | | ---------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `media_buy_id` | string | Yes | 配信を加えるメディアバイ | | `impressions` | integer | No | シミュレートするインプレッション | | `clicks` | integer | No | シミュレートするクリック | | `reported_spend` | object | No | `{ amount: number, currency: string }` — 配信データでレポートされる支出、予算に影響しない | | `conversions` | integer | No | シミュレートするコンバージョン | | `reach` | number | No | `totals.reach` に表示するユニークリーチ数 | | `frequency` | number | No | `totals.frequency` に表示するリーチ単位あたり平均フリークエンシー | | `reach_window` | object | No | シミュレートされたリーチ/フリークエンシーの測定ウィンドウ。形状: `{ kind: "cumulative" }`、`{ kind: "period", period: Duration }`、または `{ kind: "rolling", period: Duration }` | | `viewability` | object | No | `totals.viewability` に表示するビューアビリティブロック。`measurable_impressions`、`viewable_impressions`、`viewable_rate`、`viewed_seconds`、`standard` を含む。測定されたビューアビリティ値が存在するときは常に `standard` を供給すべき(SHOULD) | **Example:** ```json theme={null} { "scenario": "simulate_delivery", "params": { "media_buy_id": "mb-789", "impressions": 10000, "clicks": 150, "reach": 4000, "frequency": 2.5, "reach_window": { "kind": "rolling", "period": { "interval": 7, "unit": "days" } }, "viewability": { "measurable_impressions": 9000, "viewable_impressions": 7200, "viewable_rate": 0.8, "viewed_seconds": 4.3, "standard": "mrc" }, "reported_spend": { "amount": 150.00, "currency": "USD" } } } ``` ### `simulate_budget_spend` 指定されたパーセンテージまでの予算消費をシミュレートします。実支出を待たずに予算しきい値アラートと `payment_required` 遷移のテストを可能にします。これはアカウントレベルの財務状態に影響する唯一のシナリオです。 `simulate_budget_spend` を呼んだ後、セラーはシミュレートされた消費を `get_account_financials` に反映しなければなりません(MUST)。具体的には: * `total_spend`(または同等)はシミュレートされた金額を反映しなければならない(MUST) * `remaining_budget`(または同等)はそれに応じて減らされなければならない(MUST) * 予算利用率パーセンテージは `spend_percentage` に一致しなければならない(MUST) **Params:** | Field | Type | Required | Description | | ------------------ | ------ | -------- | ------------------- | | `account_id` | string | No | アカウント(アカウントレベル予算用) | | `media_buy_id` | string | No | メディアバイ(バイレベル予算用) | | `spend_percentage` | number | Yes | 予算のこの % まで支出(0–100) | `account_id` または `media_buy_id` の少なくとも 1 つが必要です。ターゲットエンティティは非ゼロ予算が構成されていなければならず(MUST)、そうでない場合コントローラーは `INVALID_PARAMS` を返すべきです(SHOULD)。 **Example:** ```json theme={null} { "scenario": "simulate_budget_spend", "params": { "media_buy_id": "mb-789", "spend_percentage": 95 } } ``` ### `seed_product` 後続のストーリーボードステップが安定した ID で製品を参照できるよう、呼び出し元供給の `product_id` を持つ製品フィクスチャを作成(またはアップサート)します。フィクスチャが明示的に hidden とマークしない限り、コントローラーはシードされた製品を認証済みアカウントの下で `get_products` 経由で発見可能にしなければなりません(MUST)。 **なぜこのシナリオが存在するか。** ストーリーボードは `"test-product"` のようなフィクスチャ ID をハードコードし、セラーが一致する製品を持つことを期待します。シードシナリオなしでは、すべての実装者が適合性スイートがどの ID を期待するかを再発見し手動でエイリアスしなければなりません。`seed_product` はその発見を明示的でストーリーボード作成のコントラクトに置き換えます。 **Params:** | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------------------------------------------------------------------------------------------------------------- | | `product_id` | string | Yes | ストーリーボードが参照する安定した識別子 | | `fixture` | object | No | 製品形状。最小限有用なフィールド: `delivery_type`、`channels`、`pricing_options[]`、`format_ids[]`。セラーは省略されたフィールドのデフォルトを埋めてもよい(MAY)。 | ベンダーメトリック前提条件テストには、外部ベンダーカタログには `seed_measurement_catalog` を優先してください。製品フィクスチャは、製品コントラクトと参照される測定スナップショットを 1 つのフィクスチャで必要とするローカルハーネスの互換性フォールバックとして、`{ vendor, metrics[] }` として形作られた `measurement_catalogs[]` エントリーも運べます。同じベンダーに両方が供給される場合、明示的な `seed_measurement_catalog` スナップショットがそのコンプライアンスセッションで優先されます。 **Example:** ```json theme={null} { "scenario": "seed_product", "params": { "product_id": "test-product", "fixture": { "delivery_type": "non_guaranteed", "channels": ["display"], "pricing_options": [ { "pricing_option_id": "test-pricing", "pricing_model": "cpm", "currency": "USD", "floor_price": 1.0 } ], "format_ids": [{ "id": "display_300x250" }] } } } ``` ### `seed_pricing_option` 既存のシードされた製品に価格オプションを追加(またはアップサート)します。ストーリーボードが最初の `seed_product` 呼び出しに含まれなかった特定の価格オプションを必要とするとき、またはオプションの属性がセラーのデフォルトから分岐する必要があるときに使います。 **Params:** | Field | Type | Required | Description | | ------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `product_id` | string | Yes | 親製品(既に存在していなければならない — 先にシードする) | | `pricing_option_id` | string | Yes | 価格オプションの安定した識別子 | | `fixture` | object | No | [`PricingOption`](https://adcontextprotocol.org/schemas/v3/core/pricing-option.json) スキーマに従う価格オプション形状(`pricing_model`、`currency`、オークションベースの `floor_price`、固定の `fixed_price` など) | **Example:** ```json theme={null} { "scenario": "seed_pricing_option", "params": { "product_id": "test-product", "pricing_option_id": "default", "fixture": { "pricing_model": "cpm", "floor_price": 5.0, "currency": "USD" } } } ``` ### `seed_creative` 特定のライフサイクルステータスでクリエイティブフィクスチャを作成します。ガバナンスと配信ストーリーボードが最初に `sync_creatives` をラウンドトリップせずに事前承認されたクリエイティブを参照できるようにします。 **Params:** | Field | Type | Required | Description | | ------------- | ------ | -------- | ----------------------------------------------------------------------- | | `creative_id` | string | Yes | 安定した識別子 | | `fixture` | object | No | クリエイティブ形状。典型的なフィールド: `status`、`format_id`、`assets`、`click_through_url`。 | **Example:** ```json theme={null} { "scenario": "seed_creative", "params": { "creative_id": "campaign_hero_video", "fixture": { "status": "approved", "format_id": { "id": "video_30s" }, "assets": [{ "type": "video", "url": "https://example.com/hero.mp4" }] } } } ``` ### `seed_plan` メディアプランフィクスチャを作成します。最初に完全なブリーフィング + プロポーザルフローを実行せずに特定のプランに対してアサートするガバナンスストーリーボードで使われます。 **Params:** | Field | Type | Required | Description | | --------- | ------ | -------- | ---------------------------------------------------------- | | `plan_id` | string | Yes | 安定した識別子 | | `fixture` | object | No | プラン形状。典型的なフィールド: `budget`、`brand`、`flight`、`line_items[]`。 | **Example:** ```json theme={null} { "scenario": "seed_plan", "params": { "plan_id": "gov_acme_q2_2027", "fixture": { "budget": { "total": 30000, "currency": "USD" }, "brand": { "domain": "acmeoutdoor.example" }, "flight": { "start": "2027-04-01", "end": "2027-06-30" } } } } ``` ### `seed_media_buy` `create_media_buy` フローをバイパスして、指定されたライフサイクル状態でメディアバイフィクスチャを作成します。既存のバイに対してガバナンスまたは配信動作をアサートする必要があるストーリーボードで使われます。 **Params:** | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------------------------------ | | `media_buy_id` | string | Yes | 安定した識別子 | | `fixture` | object | No | メディアバイ形状。典型的なフィールド: `status`、`packages[]`、`budget`、`flight`。 | **Example:** ```json theme={null} { "scenario": "seed_media_buy", "params": { "media_buy_id": "mb_acme_q2_2026_auction", "fixture": { "status": "active", "packages": [{ "package_id": "pkg_001", "product_id": "test-product" }] } } } ``` ### `seed_measurement_catalog` セラーのコントローラーがこのシナリオをアドバタイズするとき、コンプライアンスセッションのため測定ベンダーの `get_adcp_capabilities.measurement.metrics[]` スナップショットをシードします。メディアバイストーリーボードは、製品レベルのベンダーメトリックケイパビリティを外部ベンダーカタログディスカバリー前提条件から区別するため、このシナリオを伴う明示的な `comply_test_controller` ステップを使います。ストーリーボードは、SDK アダプターセットがまだこのシナリオを採用していないコントローラーの互換性フォールバックとして、このスナップショットを `seed_product.fixture.measurement_catalogs[]` に運ぶこともできます。同じベンダーに両方のソースが存在するとき、明示的な `seed_measurement_catalog` シードが権威的です。 **Params:** | Field | Type | Required | Description | | --------- | --------- | -------- | ----------------------------------------------------------------------------------- | | `vendor` | BrandRef | Yes | カタログがシードされる測定ベンダー | | `metrics` | object\[] | Yes | カタログエントリー。各エントリーは `metric_id` を含まなければならず、`measurement.metrics[]` のオプションフィールドを含んでもよい | **Example:** ```json theme={null} { "scenario": "seed_measurement_catalog", "params": { "vendor": { "domain": "attentionvendor.example" }, "metrics": [ { "metric_id": "attention_catalog_baseline", "unit": "score", "description": "Baseline attention metric present in this vendor catalog." } ] } } ``` ### シードのセマンティクスと順序 * **フィクスチャ形状。** `fixture` は許容的(`additionalProperties: true`)に保たれ、ストーリーボード作成者が各テストが必要とする最小限の形状を宣言できます。フィクスチャは対応するドメインスキーマ(`seed_product` には `core/product.json`、`seed_pricing_option` には `core/pricing-option.json`、`seed_creative` には `media-buy/sync-creatives-request.json` の creative-item 形状、`seed_media_buy` には `core/media-buy.json`、`seed_plan` にはプランスキーマ)に適合すべきです(SHOULD)。`seed_measurement_catalog.metrics[]` は `get_adcp_capabilities.measurement.metrics[]` をミラーします。セラーは明らかに不正な形式のフィクスチャを `INVALID_PARAMS` で拒否してもよい(MAY)。 * **再シード時の冪等性。** 同じ主 ID と最初と等価な `fixture` を持つ 2 つ目の呼び出しは成功し `previous_state: "existing"` で `success: true` を返すべきです(SHOULD)。**分岐する** フィクスチャを持つ 2 つ目の呼び出しは、どのフィールドが分岐したかを説明する `error_detail` を伴う `INVALID_PARAMS` を返さなければなりません(MUST) — セラーは黙ってマージまたは更新してはなりません(MUST NOT)。実行中にフィクスチャ状態を変える必要があるストーリーボードは、再シードではなく `force_*` シナリオを使わなければなりません(MUST)。これは同じストーリーボードをセラー全体で決定的に保ちます。 * **外部キー順序。** ランナーは、セラーが子の前に参照される親を受け取るよう、依存関係順にフィクスチャをシードします。依存関係 DAG: ``` product ──┬─→ pricing_option ├─→ plan └─→ media_buy creative ────→ media_buy plan ────────→ media_buy ``` 具体的には: `seed_pricing_option` の前に `seed_product`。フィクスチャがそれらを参照するとき `seed_media_buy` の前に `seed_product`、`seed_creative`、`seed_plan` すべて。`fixtures:` ブロックを宣言するストーリーボードは、ランナーがトポロジカルソートできる順序でエントリーをリストしなければならない(MUST) — 存在しない製品の `seed_pricing_option`、または最初にシードされなかったクリエイティブ/製品/プランを参照する `seed_media_buy` を受け取るセラーは、親を自動作成するのではなく `INVALID_PARAMS` を返さなければならない(MUST)。 * **サンドボックススコープ。** シードされたフィクスチャは認証済みサンドボックスアカウントにのみ存在します。`NOT_FOUND` は `force_*` と同じように適用されます — 呼び出し元のアカウントの親製品を見られないセラーは、黙って別のテナントにフォールバックするのではなく `NOT_FOUND` を返さなければなりません(MUST)。 * **ケイパビリティアドバタイズ。** 特定のシードシナリオを実装しないセラーは、そのシナリオ名に対して `UNKNOWN_SCENARIO` を返さなければなりません(MUST)。ランナーは、`prerequisites.controller_seeding` がそのシナリオを要求するストーリーボードの `seed_*` 上の `UNKNOWN_SCENARIO` をカバレッジギャップとして扱います — それらのストーリーボードは failed ではなく `not_applicable` としてグレードされます。これは **馴染みのない** `seed_*` 名にも適用されます: enum は拡張のためオープン(下記参照)なので、ランナーはセラーが決して見たことのないシナリオを発行するかもしれません。セラーとランナーは、認識されないシナリオ値をスキーマ拒否するのではなく `UNKNOWN_SCENARIO` で応答しなければなりません(MUST)。 * **拡張のためオープンな enum。** `scenario` enum は時間とともに新しい値を追加します(専門分野が要求するにつれ新しいシードシナリオが着地)。ランナーとセラーは、認識しないシナリオ文字列を受け入れ、ハードにスキーマ検証を失敗させるのではなく `UNKNOWN_SCENARIO` で応答しなければなりません(MUST) — そうでなければすべての新しい enum 値が古い実装の破壊的変更になります。 ## レスポンス形状 ### 状態遷移レスポンス(`force_*`) **Success:** ```json theme={null} { "success": true, "previous_state": "processing", "current_state": "approved", "message": "Creative cr-123 transitioned from processing to approved" } ``` **Failure (invalid transition):** ```json theme={null} { "success": false, "error": "INVALID_TRANSITION", "error_detail": "Cannot transition from archived to processing — archived is terminal", "current_state": "archived" } ``` **Failure (unknown entity):** ```json theme={null} { "success": false, "error": "NOT_FOUND", "error_detail": "Creative cr-unknown not found", "current_state": null } ``` ### シミュレーションレスポンス(`simulate_*`) **`simulate_delivery` response:** ```json theme={null} { "success": true, "simulated": { "impressions": 10000, "clicks": 150, "reach": 4000, "frequency": 2.5, "reach_window": { "kind": "rolling", "period": { "interval": 7, "unit": "days" } }, "viewability": { "measurable_impressions": 9000, "viewable_impressions": 7200, "viewable_rate": 0.8, "viewed_seconds": 4.3, "standard": "mrc" }, "reported_spend": { "amount": 150.00, "currency": "USD" } }, "cumulative": { "impressions": 25000, "clicks": 380, "reach": 4000, "frequency": 2.5, "reach_window": { "kind": "rolling", "period": { "interval": 7, "unit": "days" } }, "viewability": { "measurable_impressions": 9000, "viewable_impressions": 7200, "viewable_rate": 0.8, "viewed_seconds": 4.3, "standard": "mrc" }, "reported_spend": { "amount": 375.00, "currency": "USD" } }, "message": "Delivery simulated for mb-789: 10000 impressions, 150 clicks, $150.00 spend" } ``` `simulated` フィールドはこの呼び出しで注入された値をエコーバックします。`cumulative` フィールドは、このメディアバイの加算カウンターと支出の実行合計、プラス最新の非加算リーチウィンドウとビューアビリティ状態を返し、呼び出し元が `get_media_buy_delivery` をチェックする前に期待される状態を検証できます。 **`simulate_budget_spend` response:** ```json theme={null} { "success": true, "simulated": { "spend_percentage": 95, "computed_spend": { "amount": 950.00, "currency": "USD" }, "budget": { "amount": 1000.00, "currency": "USD" } }, "message": "Budget for mb-789 set to 95% consumed ($950.00 of $1000.00)" } ``` ### エラーコード コントローラーは、ストーリーボードランナーが特定の失敗モードをアサートできるよう構造化エラーコードを使わなければなりません(MUST): | Error code | When | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `INVALID_TRANSITION` | 要求されたステートマシン遷移が有効でない(例: `archived → processing`、`canceled → paused`) | | `INVALID_STATE` | 操作がリソースの現在ステータスに許可されていない(例: 分岐する形状で既に存在するフィクスチャを再シード) | | `NOT_FOUND` | エンティティが存在しないか呼び出し元がアクセス権を持たない(マルチテナントサンドボックスは「あなたのものでない」を「見つからない」として扱うべき(SHOULD)) | | `UNKNOWN_SCENARIO` | このセラーが実装しないシナリオ | | `INVALID_PARAMS` | 欠けているまたは不正な形式の params、または前提条件が満たされない(例: 予算未構成のエンティティでの `simulate_budget_spend`) | | `FORBIDDEN` | サンドボックス接続から参照された本番アカウント | | `JCS_NON_FINITE_NUMBER` | Digest モード `query_upstream_traffic` が `NaN`、`+Infinity`、`-Infinity` を含む解析済み JSON 様値ツリーを正準化できない。コントローラーはこれらの値を強制変換してはならず、ランナーは影響を受けた検証を `not_applicable` としてグレードする | | `INTERNAL_ERROR` | 一時的なセラー側の失敗(例: サンドボックスデータベース利用不可)。ランナーは失敗として扱う前に一度リトライすべき(SHOULD)。 | **コントローラー固有 enum。** コントローラーレスポンスの `error` フィールドは、[`comply-test-controller-response.json`](https://adcontextprotocol.org/schemas/v3/compliance/comply-test-controller-response.json) で定義されたコントローラー固有の語彙を使い、タスクレベルエラーを統制する正準セラーレスポンス [`error-code.json`](https://adcontextprotocol.org/schemas/v3/enums/error-code.json) enum とは別です。`INVALID_TRANSITION` はコントローラー固有です(ステートマシンプリミティブは、セラーレベルエラーコードが `INVALID_STATE` に折りたたむ遷移対状態の区別を公開する)。コントローラーレスポンスのストーリーボードアサーションは、`check: error_code` ではなく `path: "error"` または直接 `field_value` チェックを使います — 形状非依存の `error_code` チェックは、コントローラー自身のレスポンススキーマではなく、タスクレスポンスエラー(`adcp_error` / ペイロード `errors[]`)用です。 ### 冪等性 状態遷移シナリオ(`force_*`)は冪等です: 現在状態に一致するステータスを強制すると、`previous_state` が `current_state` に等しい成功を返します。これは、ランナーが一時的失敗後にリトライするときのフレーキーなテストを避けます。 シミュレーションシナリオ(`simulate_*`)は冪等では **ありません** — `simulate_delivery` は既存合計に加算し、`simulate_budget_spend` は現在の支出レベルを置き換えます。 ## テスト表面 セラーの状態の記録がどこに存在するかが、ストーリーボードテストループがどう閉じるかを決定します。状態ローカルセラー(典型的には SSP、クリエイティブエージェント)は上の `seed_*` シナリオ経由でセラーの DB に書き込みます。セラーの読み取りハンドラーは同じストアを消費し、seed→read ループが自然に閉じます。アップストリームプロキシセラー(プラットフォームにプロキシする DSP、リテーラーカタログを読むリテールメディアネットワーク、シグナルブローカー)は、読み取りハンドラーがセラーの制御しないシステムに到達するためその方法でループを閉じられません。TypeScript SDK は、まず実アダプター呼び出しを実行し、次にシードされたフィクスチャをレスポンスにマージする `TestControllerBridge` を出荷します。どちらのパスも `AAO Verified (Spec)` が証明するワイヤー形式通過を獲得します。どちらのパスも `(Sandbox)` が証明するものではありません — それはセラーの本番スタックが実世界の副作用なしに `account.sandbox: true` を尊重するかどうかをカバーする別の軸です。 このパターンの両実装のクロスページフレーミング、SDK の `_bridge` 助言マーカー、ランタイムシグナル曖昧性解消テーブルはすべて、適合性仕様 → [Test surfaces and the storyboard loop](/docs/building/verification/conformance#test-surfaces-and-the-storyboard-loop) に存在します。 ## コンプライアンステストモード セラーのツールリストに `comply_test_controller` が存在するかどうかが、コンプライアンステスターがどのモードを使うかを決定します: ### ケイパビリティディスカバリー セラーはすべてのシナリオをサポートせずにテストコントローラーを実装してもよい。ストーリーボードランナーは、最初のインタラクションとして `scenario: "list_scenarios"` で `comply_test_controller` を呼ぶべきです(SHOULD)。これをサポートするセラーは実装されたシナリオのリストを返します: ```json theme={null} { "success": true, "scenarios": [ "force_creative_status", "force_account_status", "force_media_buy_status" ] } ``` `list_scenarios` を実装するセラーは、[`comply-test-controller-request.json`](https://adcontextprotocol.org/schemas/v3/compliance/comply-test-controller-request.json) の `scenario` enum にそのまま現れるシナリオ名で応答しなければなりません(MUST)。カスタムセラー固有シナリオ名はコンプライアンスコントラクトの一部ではありません。ストーリーボードランナーは正準 enum 外のシナリオにディスパッチしないため、それらをリストしても目的はありません。`seed_product` をサポートするセラーは文字列 `"seed_product"` で応答しなければなりません(MUST) — `"create_test_product"` や他のバリアントではなく。 `list_scenarios` を実装しないセラーは `UNKNOWN_SCENARIO` を伴うエラーを返すべきです(SHOULD)。これが起こると、ランナーは各シナリオを個別に試み、`UNKNOWN_SCENARIO` レスポンスをカバレッジギャップ(失敗ではない)として扱います。これは、`list_scenarios` をスキップする早期実装者がペナルティを受けないことを意味します — ランナーは試行を通じてサポートされたシナリオを発見します。 ### 観測モード(デフォルト) `comply_test_controller` が利用できないとき: * ランナーはバイヤー開始フローを実行しレスポンススキーマを検証 * セラーアクションを要求するステートマシン遷移はスキップ * 助言観測が何をテストできなかったかを記録 ### 決定的モード `comply_test_controller` が利用可能なとき: * ランナーは各ライフサイクルのすべての到達可能な状態を歩く * エッジケースを強制: 終端状態、無効な遷移、エラーコード * 強制された状態変更が後続の読み取りに反映されることを検証 * 操作ゲートをテスト(例: アカウントが `suspended` のとき `create_media_buy` がブロックされる) ランナーは決定的モードで 3 つの結果カテゴリーを区別します: * **Scenario not supported** — `list_scenarios` または `UNKNOWN_SCENARIO` エラーで返される。失敗ではなくカバレッジギャップとしてレポート。 * **Transition correctly rejected** — コントローラーが無効な状態変更に `INVALID_TRANSITION` を返した。これは pass。 * **Unexpected failure** — コントローラーが有効であるべき遷移にエラーを返した、または失敗すべき遷移に成功した。これはコンプライアンス失敗。 ### 例: 決定的モードでのクリエイティブライフサイクル ``` 1. sync_creatives(creative) 2. list_creatives() → verify status = "processing" 3. force_creative_status(creative_id, "pending_review") 4. force_creative_status(creative_id, "approved") 5. list_creatives() → verify status = "approved" 6. force_creative_status(creative_id, "archived") 7. list_creatives() → verify status = "archived" 8. sync_creatives(same creative) → verify unarchive (→ approved or pending_review) 9. force_creative_status(creative_id, "rejected", reason) 10. list_creatives() → verify rejection_reason persisted 11. sync_creatives(same creative) → verify resubmission (rejected → processing) 12. force_creative_status(creative_id, "approved") → expect INVALID_TRANSITION (must go through pending_review) ``` ### 例: 決定的モードでのアカウント操作ゲート ``` 1. sync_accounts(account) → active 2. force_account_status(account_id, "suspended") 3. create_media_buy() → expect ACCOUNT_SUSPENDED 4. get_media_buys() → expect existing buys still readable 5. force_account_status(account_id, "active") 6. create_media_buy() → expect success 7. force_account_status(account_id, "payment_required") 8. update_media_buy(add packages) → expect ACCOUNT_PAYMENT_REQUIRED 9. get_media_buys() → existing buys still readable ``` ### 例: 決定的モードでのメディアバイライフサイクル ``` 1. create_media_buy() → status = "pending_creatives" 2. force_media_buy_status(media_buy_id, "rejected", reason) → expect success 3. get_media_buys() → verify status = "rejected", rejection_reason persisted 4. force_media_buy_status(media_buy_id, "active") → expect INVALID_TRANSITION (rejected is terminal) 5. create_media_buy() → new buy, status = "pending_creatives" 6. force_media_buy_status(media_buy_id, "pending_start") 7. force_media_buy_status(media_buy_id, "active") 8. force_media_buy_status(media_buy_id, "rejected") → expect INVALID_TRANSITION (rejected only valid from pending_creatives or pending_start) ``` ### 例: 配信と予算の検証 ``` 1. create_media_buy(budget: $1000) 2. simulate_delivery(impressions: 10000, reported_spend: $500) 3. get_media_buy_delivery() → verify delivery reflects simulated data (reported_spend is delivery-only; does not affect account budget) 4. simulate_budget_spend(spend_percentage: 95) 5. get_account_financials() → verify total_spend reflects 95% ($950, not $500 from delivery) 6. simulate_budget_spend(spend_percentage: 100) 7. force_account_status("payment_required") 8. create_media_buy() → expect ACCOUNT_PAYMENT_REQUIRED ``` ## 認定階層 | Tier | Requirement | What it proves | | ------------------------- | ---------------------- | ----------------------------------------------- | | **Functional compliance** | 観測モードですべてのストーリーボードを通過 | ツールが存在し、正しく応答し、バイヤー開始フローを完了する | | **Stateful compliance** | 決定的モードですべてのストーリーボードを通過 | ステートマシンが正しい遷移を強制し、エラーコードが仕様に一致し、操作ゲートが正しくブロックする | **専門分野スコープのシード要件。** Stateful compliance はまた、セラーが認定する専門分野をカバーする `seed_*` シナリオを実装することを要求します。`UNKNOWN_SCENARIO` → `not_applicable` グレーディングは、欠けている表面積の正直なカバレッジレポート用であり、適合性からの一括オプトアウトではありません — `sales-non-guaranteed` を認定するセラーは少なくとも `seed_product` と `seed_pricing_option` を実装しなければならず(MUST)、`creative-ad-server` を認定するセラーは `seed_creative` を実装しなければならず(MUST)、`governance-delivery-monitor` を認定するセラーは `seed_plan`(とストーリーボードが要求する場合 `seed_media_buy`)を実装しなければなりません(MUST)。`static/compliance/source/specialisms/` のストーリーボード作成者はストーリーボードが必要とするフィクスチャを宣言します。セラーはそのリストを認定上の専門分野に一致させます。 ## 実装ガイダンス ### セラー向け 1. `comply_test_controller` をデプロイレベルでゲートする — `tools/list`(または A2A `skills[]`)に現れてはならず(MUST NOT)、`compliance_testing` ケイパビリティブロック経由でアドバタイズされてはならず(MUST NOT)、本番デプロイで未知ツールにディスパッチしなければならない(MUST)。完全なルールについては [Sandbox gating](#sandbox-gating) を参照。 2. 本番ステートマシンロジックを再利用する — コントローラーは同じ内部遷移関数を呼ぶべきで、バイパスしない 3. 遷移ルールを強制する — `rejected` が本番で終端なら、`force_media_buy_status(rejected → active)` はコントローラー経由でも失敗しなければならない 4. 変更を即座に反映する — 強制された遷移の後、次の `list_*` または `get_*` 呼び出しは更新された状態を返さなければならない ### コンプライアンステスター向け 1. `tools/list` 経由のプロファイルディスカバリー中にツールを検出 2. `list_scenarios` を呼びどのシナリオがサポートされるかを発見 3. ベースラインとして観測モードを実行 — どこでも動く 4. コントローラーが利用可能なとき決定的シナリオを上に重ねる 5. どのモードが使われたかをレポートしカバレッジギャップを失敗から区別 6. コントローラーの遷移検証自体をテスト — 無効な遷移は黙って成功するのではなく `INVALID_TRANSITION` を返すべき ## 設計決定 1. **セラーは遷移順序を検証する。** コントローラーは本番と同じステートマシンルールを強制する。決して `processing` でなかったクリエイティブに `force_creative_status(approved)` を呼ぶことはエラー — コントローラーは本番と同様にそれを拒否する。ここで参照されるライフサイクルステートマシンはそれぞれのプロトコル仕様で定義される([クリエイティブライフサイクル](/docs/creative/specification#creative-status-lifecycle)、[アカウントライフサイクル](/docs/accounts/overview#account-status-lifecycle)、[メディアバイライフサイクル](/docs/media-buy/specification)、[SI セッションライフサイクル](/docs/sponsored-intelligence/specification#session-states) を参照)。 2. **テストは自己完結的。** 各テストは既存のものを再利用するのではなく専用エンティティ(メディアバイ、クリエイティブ、アカウント)を作成すべき(SHOULD)。これは加算シミュレーション呼び出し(`simulate_delivery`)がリセットメカニズムを必要とせずに既知のゼロ状態から始まることを保証する。`reset` シナリオは不要。コンプライアンステスターは、複数のストーリーボードランナーインスタンスが同じサンドボックスに対して並行実行するときの衝突を避けるため、テストエンティティに一意の識別子(例: UUID)を使うべき(SHOULD)。サンドボックスエンティティのクリーンアップ(例: TTL ベースの期限切れ)はセラーの責任。 3. **配信シミュレーションは合成マーカーを使う。** `simulate_delivery` レコードは、セラーが内部的に簿記に使える `synthetic: true` フィールドを含んでもよい(MAY)。ランナーはこのマーカーを無視する — にかかわらず同じスキーマに対して `get_media_buy_delivery` レスポンスを検証する。これはテストの正しさに影響せずにセラーの実装ハードルを下げる。 4. **1 ツール、多シナリオ。** 単一ツール設計は、7 つの別々のツールの約 1,400 トークンに対しコンテキストウィンドウコストを約 500 トークンに保つ。セラーは 1 つのサンドボックスゲートを実装する。ランナーは 1 つのツールを検出する。`list_scenarios` イントロスペクションは、ツールごとの存在検出を要求せずに部分実装を処理する。 # エラーハンドリング Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L3/error-handling AdCP のエラーハンドリング: プロトコルエラー、タスク失敗、バリデーションエラーの標準コード、復旧戦略、指数バックオフによるリトライロジック。 AdCP は全オペレーションで一貫したエラーハンドリングを行います。エラー分類を理解し、適切な復旧戦略を実装することが堅牢な統合には不可欠です。 ## 準拠レベル セラーはエラーハンドリングを段階的に採用できます。各レベルは前のレベルの上に構築されます: | Level | 実装する内容 | エージェントができること | | ----------- | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | | **Level 1** | すべてのエラーに `code` と `message` を返す | エラーコードで失敗を分類できる | | **Level 2** | `recovery`、`retry_after`、`field`、`suggestion` を追加する | 一時的エラーの自動リトライと修正可能なエラーの自己訂正ができる | | **Level 3** | [トランスポートバインディング](/docs/building/operating/transport-errors) で MCP の `structuredContent` または A2A のアーティファクト `DataPart` にエラーを入れる | プログラム的クライアントがテキスト解析なしに型付きエラーオブジェクトを取得できる | **Level 1** は準拠実装の最低要件です。**Level 2** でエージェント主導の復旧が可能になります — `recovery` がなければエージェントはエラーコードから推測するしかありません。**Level 3** で `@adcp/client` のようなクライアントライブラリが完全な型付きエラーオブジェクトを提供できます。 ## エラーの分類 ### 1. プロトコルエラー AdCP ビジネスロジック外の通信・接続問題: * ネットワークタイムアウト * 接続拒否 * TLS/SSL エラー * JSON パースエラー **対応:** 指数バックオフでリトライします。 ### 2. タスクエラー `status: "failed"` で返るビジネスロジックの失敗: * 在庫不足 * 無効なターゲティング * 予算バリデーション失敗 * リソース未検出 **対応:** `recovery` フィールドを確認して、リトライするか、リクエストを修正するか、エスカレートするかを判断します。 ### 3. バリデーションエラー スキーマ検証に失敗する不正リクエスト: * 必須項目の欠落 * 無効な型 * 範囲外の値 **対応:** リクエスト形式を修正して再送します(多くは開発時の問題です)。 ## エラーレスポンス形式 失敗した処理はステータス `failed` とエラー詳細を返します。エラーオブジェクトは [`error.json`](https://adcontextprotocol.org/schemas/v3/core/error.json) スキーマに従います: ```json theme={null} { "status": "failed", "message": "Budget is below the seller's minimum for this product", "errors": [ { "code": "BUDGET_TOO_LOW", "message": "Budget is below the seller's minimum for this product", "recovery": "correctable", "field": "budget.total", "suggestion": "Increase budget to at least 500 USD", "details": { "minimum_budget": 500, "currency": "USD" } } ] } ``` ### エンベロープ vs. ペイロードエラー — 二層モデル AdCP はエラーを 2 つの異なる場所で公開し、実装者は状況に応じて正しい層を設定する必要があります。これはエージェントとストーリーボードの間でエラー形状のドリフトが起きる最も一般的な原因です。 | 層 | キー | いつ設定するか | 形状 | | ----------------- | --------------------------------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------- | | **タスクペイロード** | `payload.errors[]`(またはトランスポートに応じてトップレベル `errors[]`) | タスクが実行され、ペイロードが 1 つ以上の問題(致命的または非致命的)を報告 | [`error.json`](https://adcontextprotocol.org/schemas/v3/core/error.json) に従うエラーオブジェクトの配列 | | **トランスポートエンベロープ** | `adcp_error` | タスクが失敗し、トランスポートに型付き・抽出可能なシグナルが必要 | [`error.json`](https://adcontextprotocol.org/schemas/v3/core/error.json) に従う単一のエラーオブジェクト | **致命的なタスク失敗は両方の層を設定すべきです(SHOULD)。** ペイロードは任意のプロトコルがそのまま読める構造化された `errors[]` 配列を運び、トランスポートエンベロープは MCP/A2A クライアントがペイロードを再解析せずに型付きエラーを抽出できるよう `adcp_error` を運びます。2 つのうち片方だけを設定するのが、ほとんどの相互運用バグの原因です — トランスポートエンベロープを読むランナーはエラーを見ず、ペイロードを読むランナーはトランスポート上にエラーシグナルを見ません: ```json theme={null} // MCP — structuredContent AND payload both carry the error { "content": [{"type": "text", "text": "{\"adcp_error\":{\"code\":\"BUDGET_TOO_LOW\", ...}}"}], "isError": true, "structuredContent": { "adcp_error": { "code": "BUDGET_TOO_LOW", "message": "...", "recovery": "correctable" }, "payload": { "errors": [ { "code": "BUDGET_TOO_LOW", "message": "...", "recovery": "correctable", "field": "budget.total" } ] } } } ``` ```json theme={null} // A2A — artifact DataPart carries adcp_error; if the agent also surfaces payload via a sibling DataPart, errors[] lives there { "status": { "state": "failed" }, "artifacts": [{ "artifactId": "error-result", "parts": [ { "kind": "data", "data": { "adcp_error": { "code": "BUDGET_TOO_LOW", ... } } }, { "kind": "data", "data": { "errors": [{ "code": "BUDGET_TOO_LOW", "field": "budget.total", ... }] } } ] }] } ``` **非致命的なエラーはペイロードのみを設定します。** 警告を報告する `status: "submitted"` または `status: "input-required"` タスク(例: 「このメディアバイは手動承認が必要です — \[警告詳細]」)は、ペイロードの `errors[]` に `severity: "warning"` を設定しますが、`adcp_error` を設定してはなりません(MUST NOT)。トランスポートエンベロープは「タスクが失敗した」を示すものであり、警告チャネルではありません。 **ストーリーボードバリデーター。** 失敗したタスクのエラーコードをアサートする際は、`check: field_present, path: "errors"` ではなく `check: error_code` を優先してください。`error_code` は形状非依存です — ランナーは `adcp_error.code`(トランスポート)または `errors[0].code`(ペイロード)のいずれかから解決します。直接の `path: "errors"` チェックはアサーションをペイロード形状に固定し、エージェントがコンフォーマントであってもトランスポートエンベロープ経由でのみエラーを表面化するエージェントに対して失敗します。[Storyboard authoring — Asserting on errors](/docs/contributing/storyboard-authoring#asserting-on-errors)を参照してください。 **判別付き拒否アーム。** タスクレスポンスが構造化された拒否アーム(例: `AcquireRightsRejected`、`CreativeRejected` — ルールは [`GOVERNANCE_DENIED`](https://adcontextprotocol.org/schemas/v3/enums/error-code.json) のワイヤー配置ガイダンスを参照)を定義する場合、スペック的に正しい拒否レスポンスはワイヤー上にエラーコードを運びません — 拒否アームはスキーマ層で `not: { required: [errors] }` を強制します。`check: error_code` のアサートはコンフォーマントなエージェントに対して失敗します。代わりに判別子でアサートしてください: `check: field_value, path: "status", value: "rejected"`。これは `acquire_rights` のガバナンス拒否と `creative_approval` のポリシー拒否のパターンです。2 つのパスを混在させるアサーション(拒否アームを持つタスクに `error_code`、持たないタスクに `field_value`)は、非スペックの見解をストーリーボードに焼き込みます。 ### エラーオブジェクトのフィールド これらのフィールドは [`error.json`](https://adcontextprotocol.org/schemas/v3/core/error.json) スキーマで定義されています: | Field | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `code` | string | Yes | [標準語彙](#標準エラーコード)またはセラー固有の機械判読用エラーコード | | `message` | string | Yes | 人向けのエラー説明 | | `recovery` | string | No | エージェントの復旧分類: `transient`、`correctable`、`terminal` | | `retry_after` | number | No | リトライまでの待機秒数(一時的エラー) | | `field` | string | No | JSONPath-lite 形式のフィールドパス(例: `packages[0].targeting`)。`issues` が存在する場合、セラーはこれを RFC 6901 から JSONPath-lite に変換した `issues[0].pointer` に設定しなければなりません(MUST、例: `/packages/0/targeting` → `packages[0].targeting`)。将来のメジャーバージョンで非推奨になります。 | | `issues` | array | No | バリデーション失敗の構造化リスト。各エントリは `pointer`(RFC 6901)、`message`、`keyword`(拒否した JSON Schema キーワード — `required` / `type` / `format` など)、および任意で `schema_id`、`schemaPath`、`discriminator` を運びます。`schema_id` / `schemaPath` / `discriminator` のセマンティクス、本番発行ルール、`schema_id` の解決パスは [Validator-internals フィールド](#validator-internals-フィールドissues)を参照してください。 | | `suggestion` | string | No | エラーの修正提案 | | `details` | object | No | 追加のコンテキスト固有情報。セラーは pre-3.1 コンシューマーとの後方互換性のため `issues[]` をここに `details.issues` としてミラーしてもよい(MAY)。新しいコンシューマーはトップレベルの `issues` フィールドを優先すべきです(SHOULD)。 | ### Validator-internals フィールド(`issues`) 各 `issues[]` エントリの 3 つの任意フィールドは、ペイロードを拒否したスキーマ要素を名指しするため、エージェントはバリエーションを探る代わりに 1 回のイテレーションでバリデーションエラーから復旧できます: | Field | Shape | Purpose | | --------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | `schema_id` | string — 公開された `$id`(例: `/schemas/3.1.0/core/activation-key.json`) | 拒否した(サブ)スキーマの正準名。3.1+ コンシューマーの主要ハンドル。 | | `schemaPath` | string — JSON Schema ツリーパス(例: `#/properties/packages/items/oneOf/1`) | バリデーター内部のトラバーサル。3.0.x 後方互換のため保持。3.1+ コンシューマーは `schema_id` を優先すべき(SHOULD)。(将来のメジャーで `schema_path` にリネーム。) | | `discriminator` | array of `{property_name, value}` | const 判別の `oneOf` / `anyOf` についてバリデーターが選択したバリアント。ペイロード内に存在する値がソース。OpenAPI 3.x の `discriminator.propertyName` と整合。 | **`schema_id` の解決。** 文字列はスキーマの `$id` です。スキーマを読み込むには: * **HTTPS 正準:** `https://adcontextprotocol.org` を前置します(例: `https://adcontextprotocol.org/schemas/3.1.0/core/activation-key.json`)。キャッシュ可能。バージョンごとに不変。 * **SDK バンドル:** `@adcp/sdk` と `adcp-client-python` はオフライン解決のためスキーマバンドルを同梱します — バンドルツリーに対して `$id` で検索します。 * **バンドルツリーの注意点。** 事前解決されたバンドルツリー(`/schemas/{version}/bundled/...`)から提供されるツールは、`$id` を保持したままサブスキーマをインライン化します(3.1+、#3868 を参照)— ただしバンドル内の各サブスキーマの**最初の出現**でのみです。同じソーススキーマが複数の同居場所から参照される場合、`$id` は最初のインラインにのみ付与され、後続の出現は、SDK のエラーレポートがスキーマツリーを遡る際に最も近い `$id` を持つ祖先(通常はレスポンスルート)にフォールバックします。サブツリーに巻き上げられた `$defs` 参照を含むサブスキーマも、`$id` を保持するとローカルフラグメント解決が壊れるため、バンドル時に `$id` が剥がされます。#3868 より前に生成されたバンドルを読むコンシューマーはレスポンスルートの `$id` のみを見ます。`$id` が `/bundled/-response.json` で終わるかを確認して pre-#3868 のケースを検出し、その場合はバンドルスキーマを `pointer` で辿ってフォールバックします。 **判別子のセマンティクス。** セラーは、(a) 拒否したスキーマが const 判別の `oneOf` / `anyOf` であり、(b) 判別子プロパティがペイロードに存在する場合にのみ `discriminator` を設定します。ワイヤーフィールドは呼び出し元が送った値を報告します — 部分一致ヒューリスティクスに基づくバリデーターの推論ではありません — ため、Ajv、Python `jsonschema`、`gojsonschema` にわたって決定論的です。生き残るバリアントがゼロの場合、セラーは `discriminator` を省略しなければなりません(MUST、省略が「バリデーターがターゲットバリアントを局所化できなかった」というエージェントへのシグナル)。複合判別子(例: `audience-selector` の `(type, value_type)`)の場合、エントリは拒否したスキーマの `properties` ブロックでの宣言順に並びます。 判別子プロパティがペイロードから*欠落*している場合(バリデーターがブランチ選択すら開始できない)、セラーは `discriminator` を省略します — 復旧シグナルは、欠落した判別子プロパティを名指す `keyword: "required"` を持つ兄弟の `issues[]` エントリから来ます。「必須の判別子フィールドが欠落」+「この issue に `discriminator` なし」を読むエージェントは、名指されたプロパティを設定して復旧します。バリアントに一致しなかった値を持つ `discriminator` 配列を見るエージェントは、別の値に切り替えて復旧します。 **本番発行ルール(公開スペックのスタンス)。** 3 つのフィールドはすべて、**拒否した要素がセラーが `get_adcp_capabilities` で宣伝するバージョンの公開スペックに存在する場合**、本番で発行しても安全です。根拠は replay-locally です: スキーマは adcontextprotocol.org で公開され、すべての SDK にバンドルされているため、同じペイロードに対して同じバリデーターを実行する敵対者は同じブランチ選択を導出します — ワイヤーフィールドは彼らが計算できない情報を運びません。 replay-locally の議論には、セラーが順守しなければならないカーブアウトがあります: * **プライベート拡張。** カスタム `oneOf` ブランチ、サーバー専用のサブスキーマ、`additionalProperties: true` で重ねた enum サブセットを実行するセラーは、拒否した要素が公開スペックに存在しない場合、`schema_id`、`schemaPath`、`discriminator` を発行してはなりません(MUST NOT)。発行は、敵対者が再現できないセラー内部のバリデーション状態を漏らします。*実装:* 公開 + プライベートの混在バリデーションツリーを実行するセラーは通常、(a) 公開専用とプライベート専用のバリデーターを別々にコンパイルし公開ランからのみ `schema_id` を発行するか、(b) 各 `$id` をソースバンドルにマッピングするサイドテーブルでコンパイル済みスキーマを計装します。 * **バージョンスキュー。** プレリリースまたはポストリリースのスキーマに対して検証するセラーは、`get_adcp_capabilities` で名指したバージョンの公開バンドルに存在しない `$id` を持つ `schema_id` を発行してはなりません(MUST NOT)。 * **サーバー狭窄化された公開要素。** セラーがサーバー側で公開 enum、パターン、数値範囲をテナント固有のサブセットに絞る場合(例: 公開スキーマでは `["a","b","c"]` を受け入れるが、この呼び出し元には `["a"]` 以外をすべて拒否)、セラーは公開 `schema_id` に対して `keyword: "enum"`(または `pattern` / `minimum` / `maximum`)で `VALIDATION_ERROR` を返してはなりません(MUST NOT)。公開スペックの再現は値を受け入れますが、セラーはプライベート状態で拒否します。拒否が公開スキーマ要素に誤帰属されないよう、代わりに `POLICY_VIOLATION` または `UNSUPPORTED_FEATURE` を使用します。「公開再現は受け入れ、セラーは拒否」の構造的差分自体が、セラーのプライベートサブセットの指紋です。 * **カスタムキーワード。** `keyword` は JSON Schema Draft 7 / 2020-12 の語彙から取らなければなりません(MUST)— バリデーター固有のカスタムキーワード(Ajv `addKeyword`、`instanceof`)を使うセラーはそれらをワイヤーに発行してはなりません(MUST NOT)。 * **プローブの簡潔性。** セラーは、上記のカーブアウトが適用されない場合でも、本番エンベロープを簡潔に保つため、これら 3 つのフィールドをレート制限されたエンドポイントの dev/sandbox レスポンスにスコープしてもよい(MAY)。フィールドの省略は常にコンフォーマントです。 ## 標準エラーコード 標準エラーコードは [`error-code.json`](https://adcontextprotocol.org/schemas/v3/enums/error-code.json) で定義されています。語彙は**オープン**です: `error.code` はワイヤー上 `string` として型付けされ、標準コードは文書的で、送信者は標準セット外のコードを発行してもよい(MAY)。 ### Forward-compatible decoding(規範的) **エラーコード語彙はオープンです。** `error.code` は [`core/error.json`](https://adcontextprotocol.org/schemas/v3/core/error.json) で `string` として型付けされています — 閉じた enum ではありません — ため、厳格な JSON Schema バリデーターは任意の文字列値を受け入れなければなりません(MUST)。`error-code.json` の標準語彙は文書的であり、ワイヤーレベルで送信者も受信者も制約しません。 **受信者は未知のコードをデコードしなければなりません(MUST)。** AdCP バージョン X にピン留めされた受信者が、バージョン X+1 で導入された `error.code`(または標準語彙外のプラットフォーム固有コード)を運ぶレスポンスをデコードする場合: 1. レスポンスを整形式として扱う — エンベロープを拒否したり、デシリアライズ例外を投げたり、汎用プロトコルエラーに格下げしたりしてはなりません(MUST NOT)。 2. 存在する場合、`error.recovery`(エラーエンベロープのトップレベルフィールド)から復旧分類を回復します。`error.recovery` が規範的なキャリアであり、`error-code.json` の `enumMetadata.recovery` は文書的なミラーです。 3. `error.recovery` が不在の場合(レガシー送信者)、保守的なデフォルトを適用します。`transient` が未知のコードの安全なデフォルトです — リトライ・ウィズ・バックオフは terminal 分類より悪くなり得ず、マニフェストの [`error_code_policy.default_unknown_recovery`](https://adcontextprotocol.org/schemas/v3/manifest.schema.json) がこれを正準のフォールバックとして文書化しています。`transient` デフォルトは [§ リトライロジック](#リトライロジック)のリトライルールで**制限されます** — 受信者は `maxRetries` とジッター付き指数バックオフスケジュールを適用しなければならず(MUST)、`transient` デフォルトで無限にループしてはなりません(MUST NOT)。敵対的またはバグのある送信者は、コンフォーマントなクライアントに対して無制限のリトライを誘発できません。オープン enum のデコードルールは受信者をリトライバジェットから免除しません。 **送信者は受信者のピン留め語彙外のコードを発行してもよい(MAY)。** 3.1 時代のコード(例: `PROPOSAL_NOT_FOUND`)を 3.0 ピン留めの受信者に発行する送信者はスペックに違反しません — 受信者は上記ルールによりそれを処理する必要があります。送信者が受信者のピン留めバージョンを知る場合(`adcp_version` エンベロープエコーまたはケイパビリティディスカバリー経由)、同等が存在するときはそのバージョンの語彙からコードを優先すべきです(SHOULD)。同等がないときは、より新しいまたはプラットフォーム固有のコードを発行してもよい(MAY)。 **送信者はすべてのエラーで `error.recovery` を設定しなければなりません(MUST)。** これはバージョンスキューにわたる復旧セマンティクスの規範的なキャリアです — 受信者は知らないコードを確実に分類できませんが、常に `error.recovery` を読めます。省略は forward-compat ルールを無効にします。 **なぜ重要か。** Forward-compatible デコードは、将来のメンテナンスライン(3.1.x、4.0.x、…)が古い受信者を壊さずに新しいエラーコードを追加的に出荷できるようにするワイヤーレベルの不変条件です。それがなければ、すべての新コードは次のマイナーまで保留されるワイヤー変更です — これが 3.0.x の現在の drift-lint ポリシーです。3.1+ でこれを備えると、新コードはメンテナンスラインの寿命中に登録でき、これはアダプターの実世界の拒否パスが新コードを表面化する際に重要です。 **3.0.x ポリシーは変更なし。** 3.0.x 受信者はこの規範ルールより前なので、3.0.x はサポート期間の残りの間ワイヤー安定を保ちます — 新コードは依然として次のマイナーで着地します。このルールは 3.1 以降の受信者契約を定めます。 **Not-found の優先順位。** 参照された識別子が解決しない場合、解決される型がリクエストから既知であれば、セラーはリソース固有のコードを返すべきです(SHOULD): `product_id` には `PRODUCT_NOT_FOUND`、`package_id` には `PACKAGE_NOT_FOUND`、`media_buy_id` には `MEDIA_BUY_NOT_FOUND`、`creative_id` には `CREATIVE_NOT_FOUND`、`signal_id` には `SIGNAL_NOT_FOUND`、SI `session_id` には `SESSION_NOT_FOUND`、`account_id` には `ACCOUNT_NOT_FOUND`、ガバナンス `plan_id` には `PLAN_NOT_FOUND`。専用コードのないリソース型(例: プロパティリスト、コンテンツ基準、権利付与、SI オファリング、プロポーザル、カタログ、イベントソース、コレクションリスト、ブランド、個別プロパティ)には `REFERENCE_NOT_FOUND` にフォールバックします。専用の標準コードを欠く型付きパラメーターは、カスタムの `*_NOT_FOUND` コードを作るのではなく `REFERENCE_NOT_FOUND` を使わなければなりません(MUST)— 語彙は上流のスペック変更で成長し、セラーごとのインフレでは成長しません。クライアントは最初に `error.code` で分岐すべきです(SHOULD)。リソース固有のコードにより、クライアントは `error.field` を解析せずにディスパッチできます。 **多相パラメーター。** 未解決の識別子が多相または型なしパラメーター(複数のリソース型を受け入れるフィールド)経由で供給された場合、解決される型にリソース固有のコードが存在しても、セラーは `REFERENCE_NOT_FOUND` を使わなければなりません(MUST)。多相パラメーターで型固有のコードを使うと、解決される型が未認可の呼び出し元に漏れます。多相性はツールスキーマのパラメーターの宣言された形状に対して — **いかなるルックアップの前にも** — 評価されるため、汎用の `reference_id` パラメーターは id が何に解決されるかに関わらず `REFERENCE_NOT_FOUND` にディスパッチします。ディスパッチ後に解決される型で評価すると漏洩が再導入されます。 ツールの宣言されたパラメーター形状は、あるツールバージョンについてすべての呼び出し元にわたって同一でなければならず(MUST)、ディスパッチルールは呼び出し元のアイデンティティに条件付けられてはなりません(MUST NOT)。テナント B には `property_list_id` を、テナント A には `reference_id` のみを公開するスキーマは、ケイパビリティディスカバリー自体を列挙オラクルに変えます(2 つのアイデンティティでスキーマを読み、diff を取る)。 **アクセス不能な参照への統一レスポンス。** 統一レスポンス要件はこの語彙の**すべての** not-found コード(`REFERENCE_NOT_FOUND`、`SIGNAL_NOT_FOUND`、`CREATIVE_NOT_FOUND`、`MEDIA_BUY_NOT_FOUND`、`PACKAGE_NOT_FOUND`、`SESSION_NOT_FOUND`、`ACCOUNT_NOT_FOUND`、`PLAN_NOT_FOUND`)に適用されます: セラーは「存在するが呼び出し元にアクセス権がない」に対して「存在しない」と同じレスポンスを返さなければなりません(MUST)。2 つを決して区別しないでください — これがクロステナント列挙が着地する方法です。 この MUST は `error.code` だけでなく、すべての観測可能なチャネルをカバーします: * **エラーオブジェクト。** `error.code`、`error.message`、`error.field`、`error.details` は 2 つのケース間でバイト等価でなければなりません(MUST)。`REFERENCE_NOT_FOUND` にフォールバックする型付きパラメーターでは、`error.field` は true-miss と resolve-then-deny にわたって同一でなければならず(MUST)— 両方で省略されるか、両方で型中立な名前に置換されます。`error.field` は、パラメーター名が型中立な場合(例: `reference_id`)に入力パラメーターを名指してもよい(MAY)。元のパラメーター名が型を明かす場合(例: `property_list_id`)、`error.field` は省略されるか中立な名前に置換されなければなりません(MUST)。`error.message` は汎用でなければなりません(MUST、`"Property list not found"` のようなリソース修飾テキストなし)。`REFERENCE_NOT_FOUND` については特に、セラーは `error.field`、`error.details`、リソース修飾された `error.message` を通じて解決される型を漏らしてはなりません(MUST NOT)。パラメーターが配列(例: `catalog_ids`、`format_ids`)の場合、`error.field` は配列パラメーター自体を名指さなければなりません(MUST)。セラーは `error.details` で特定の解決不能な要素を列挙してもよい(MAY)— ただし要素が呼び出し元によってそのまま供給された場合のみです。セラーは要素レベルで「供給された要素は解決したが呼び出し元が未認可」を「供給された要素は存在しない」と区別してはなりません(MUST NOT)。それは配列エントリの粒度で列挙オラクルを再導入します。 * **トランスポートステータス。** HTTP ステータスコード、A2A `task.status.state`、MCP `isError` は 2 つのケース間で同一でなければなりません(MUST)。 * **レスポンスヘッダー。** `ETag`、`Cache-Control`、リソース型ごとのレート制限バケット、CDN タグ、および値または存在がリソース型で異なる任意のヘッダーは同一でなければなりません(MUST)。 * **副作用。** Webhook ディスパッチと監査ログ書き込みは同一でなければなりません(MUST)— resolve-then-deny パスは、true-miss がしないような形で、テナント監査行を書いたり、バックグラウンド作業(検索インデクサー更新、キャッシュウォーマー、アクセスログアグリゲーター)をキューイングしたり、リソース型ごとのクォータ/レート制限カウンターをインクリメントしたり、任意のサブスクライバー(リソース所有者を含む)に Webhook を発火したりしてはなりません(MUST NOT)。resolve-then-deny パスがテナントごとの DB シャードまたはキャッシュに触れる場合、共同テナントの観測者がストレージ層メトリクスで区別できないよう、true-miss パスも同じ形状のストレージに触れなければなりません(MUST、例: 両方をテナント非依存のリゾルバー経由でルーティング)。 * **可観測性。** ダウンストリームのログ、APM スパン、サードパーティのエラーレポートテレメトリ(Sentry、Datadog、Rollbar など)は、呼び出し元にアクセス権がないとき解決されるリソース型でタグ付けされてはなりません(MUST NOT)。true-miss が発するトレースは resolve-then-deny が発するトレースと構造的に区別不能でなければなりません(MUST)。 レイテンシーの等価性を別個の要件ではなく帰結にするため、セラーは両方のパスで同じ形状の解決・認可作業を実行しなければなりません(MUST)— **resolve-then-authorize**、「未知の id」でショートサーキットしてはなりません。true-miss でも、セラーは同等の形状の認可判断を実行しなければなりません(MUST、例: 空のプリンシパルセットに対して、または呼び出し元自身のテナントをデコイとして)。これにより ACL グラフのサイズと形状で変わる認可者レイテンシーがサイドチャネルにならないようにします。ルックアップ前の入力検証(UUID 形式、長さ、正規表現)は、リクエスト内容のみで決定論的である場合に限り許可されます(同じ入力 → 同じ判定、呼び出し元や存在に関わらず)。 非規範的な実装ノート: `SELECT ... WHERE id = ? AND tenant = ?` のような単一クエリパターンは*見た目は*統一的ですが、行が別のテナントに存在するかどうかで実行計画、バッファプールのタッチ、認可者の呼び出しが異なります。id で解決し、次に読み込んだ行(true-miss では空の行)に対して認可する二段階パターンを、観測的統一性を自然に生む唯一のパターンとして優先してください。 **キャッシュの温かさ**は別個のオラクルです: テナント B の id に対する温かいキャッシュは、最近誰かがそれにアクセスしたことを示します。セラーはキャッシュ投入を認可でゲートしてはなりません(MUST NOT)— true-miss の id は resolve-then-deny と同じ TTL でキャッシュ・アズ・ミスされなければならず(MUST)、または not-found レスポンスではキャッシュ読み取りがバイパスされなければなりません(MUST)。 **自分で検証する。** ペア化プローブの `adcp fuzz` 不変条件は、ツールごとに 2 つのレスポンスを比較して統一レスポンス準拠を確認します。テナントセットアップ要件と CLI 呼び出しは [Validate Your Agent — Preparing to test uniform error responses](/docs/building/verification/validate-your-agent#preparing-to-test-uniform-error-responses)を参照してください。フルストレングステストには 2 つの分離されたテナントが必要です。単一テナントの実行は「存在しない」レッグのみをカバーします。 ### 認証とアクセス | Code | Recovery | Description | Resolution | | -------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `AUTH_REQUIRED` | correctable\* | 認証が必要、または提示された認証情報が拒否された | 欠落時は認証情報を提供する。拒否時はオペレーターにエスカレートする — 下記の警告を参照 | | `CREDENTIAL_IN_ARGS` | terminal | 認証素材または呼び出し元提供の信頼素材が、該当するトランスポート認証/信頼チャネルではなくリクエスト args(トップレベル、`context`、`ext`、その他ネスト位置)に置かれた。インバウンドのバイヤープリンシパル認証情報と、評価器ペイロードフィールドに紛れ込んだ評価器呼び出しの認証情報や JWK/JWKS/JWKS-URI 素材を含む。 | 自動リトライしない — 自動リトライは認証情報を再ログする。認証情報を該当するトランスポート認証/信頼チャネルに移し([クレデンシャルの配置](/docs/building/by-layer/L2/authentication#credential-placement))、漏洩した認証情報をローテーションし、再送する | | `ACCOUNT_NOT_FOUND` | terminal | アカウント参照を解決できない | `list_accounts` で確認するか、セラーに連絡する | | `ACCOUNT_SETUP_REQUIRED` | correctable | 使用前にアカウントのセットアップが必要 | `details.setup` の URL または手順を確認する | | `ACCOUNT_AMBIGUOUS` | correctable | 自然キーが複数のアカウントに解決する | 明示的な `account_id` またはより具体的な自然キーを渡す | | `ACCOUNT_PAYMENT_REQUIRED` | terminal | 未払い残高の支払いが必要 | バイヤーが請求を解決しなければなりません | | `ACCOUNT_SUSPENDED` | terminal | アカウントが停止されている | セラーに連絡して解決する | **`AUTH_REQUIRED` のサブケース — 拒否された認証情報を自動リトライしない。** ワイヤーコードはエージェントが異なる扱いをしなければならない 2 つの運用上異なるケースを運びます: * **認証情報が欠落** → 認証情報を提供して 1 回リトライ。エージェントループ内で修正可能。 * **認証情報が提示されたが拒否された**(期限切れ、失効、または署名不正) → 自動リトライ**しない**。認証情報ローテーションのためオペレーターにエスカレート。拒否された認証情報を SSO エンドポイントに再提示すると、ブルートフォースプローブと区別不能なリトライ嵐パターンが生じます — セラーの不正検知が呼び出し元エージェントをレート制限、停止、またはアラートするかもしれません。 `CREDENTIAL_IN_ARGS` は関連するが別個のケースで、認証素材または呼び出し元提供の信頼素材が該当するトランスポート認証/信頼チャネルではなく**タスクペイロード**に置かれたものです。そのコードは `terminal`(自動リトライは認証情報を再ログする)で、ルール+カーブアウト(プッシュ通知の Webhook 認証、リレートポロジー)は [クレデンシャルの配置](/docs/building/by-layer/L2/authentication#credential-placement)にあります。 将来のマイナーリリースはこのコードを `AUTH_MISSING`(correctable)と `AUTH_INVALID`(terminal)に分割します。それまでは、エージェントは認証情報が失敗したリクエストに添付されていたかで分岐します: ```javascript theme={null} case 'AUTH_REQUIRED': { // The caller's request builder records whether an auth header was attached. // The error-handling SDK surfaces this on `error.request_had_credentials` (or you // pass it in from your own request wrapper). const requestHadCredentials = Boolean(error.request_had_credentials); if (!requestHadCredentials) { // Sub-case (a) — provide credentials and retry. await refreshCredentials(); return retry(); } // Sub-case (b) — credentials were presented and rejected. // Treat as terminal at the application layer; surface to operator. console.error('Credential rejected — needs human rotation:', error.message); throw error; } ``` ### 請求とアカウントセットアップ リクエストの請求またはアカウント形状の値がセラーに受け入れられない場合に [`sync_accounts`](/docs/accounts/tasks/sync_accounts) が返します。2 つの請求拒否コードは*どのゲート*が発火したかを区別するため、エージェントはプロースを解析せずに正しい復旧(自律リトライ vs 人へのエスカレーション)にディスパッチできます。これらのコードが乗る二層アイデンティティモデルは [バイヤーエージェントのアイデンティティ](/docs/building/by-layer/L2/accounts-and-agents#buyer-agent-identity)を参照してください。 | Code | Recovery | Description | Resolution | | --------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BILLING_NOT_SUPPORTED` | correctable | セラーが要求された `billing` 値を、セラー全体のケイパビリティレベル(`supported_billing` が値を含まない)またはアカウント関係ごとのレベル(例: セラーは一般に `operator` 請求を受け入れるが、この特定アカウントのオペレーターと直接関係がない)で拒否 | `get_adcp_capabilities` の `supported_billing` を確認し、サポートされる値で再送するか `billing` を省略する。存在する場合は [`billing-not-supported.json`](https://adcontextprotocol.org/schemas/v3/error-details/billing-not-supported.json) に従い `error.details.scope`(`"capability"` または `"account"`)で分岐する | | `BILLING_NOT_PERMITTED_FOR_AGENT` | correctable | セラー全体のケイパビリティは要求値を受け入れるが、呼び出し元バイヤーエージェントの商業関係が受け入れない(例: パススルー専用としてオンボード — 支払い関係なし — のため `operator` 請求のみ許可) | 存在する場合は `error.details.suggested_billing`(通常 `operator`)でリトライ。不在の場合、拒否は terminal-pending-onboarding — エージェントは自動リトライしてはならず(MUST NOT)、バイヤー側の人間に表面化してセラーとの支払い関係のオンボーディングをオフラインで完了させなければならない(MUST) | | `PAYMENT_TERMS_NOT_SUPPORTED` | correctable | セラーが要求された `payment_terms` 値を受け入れない | `payment_terms` を省略してデフォルトを受け入れるか、別のサポート値でリトライするか、オフラインで交渉する | | `BRAND_REQUIRED` | correctable | ブランド参照なしで請求可能なオペレーションが試みられた | リクエストに `brand`(`domain` と任意の `brand_id`)を含める | 規範的要件: * **確立されたエージェントアイデンティティなしの統一レスポンス。** `BILLING_NOT_PERMITTED_FOR_AGENT` は呼び出し元のセラーとのオンボード済み商業状態に基づいて `BILLING_NOT_SUPPORTED` と異なります。アイデンティティ確立なしに per-agent コードを返すと、未認証プローブがコード選択を「このエージェントは agent-billable としてオンボードされているか?」のオラクルとして使えます — [`*_NOT_FOUND` 統一レスポンスルール](#標準エラーコード)と同じ形状です。境界線: セラーは、[Agent identity](/docs/building/by-layer/L1/security#agent-identity) に従う署名付きリクエスト導出、またはセラーのオンボーディングレコードのクレデンシャル・トゥ・エージェントマッピングを通じてエージェントアイデンティティが確立された場合にのみ `BILLING_NOT_PERMITTED_FOR_AGENT` を発行しなければなりません(MUST)。それ以外のすべてのケース — 特定のエージェントレコードにマップされていない bearer 認証情報を含む — では、セラーは `BILLING_NOT_SUPPORTED` を返さなければならず(MUST)、`"account"` スコープヒント自体がアカウント関係ごとのオラクルとして機能しないよう、このパスでは `error.details.scope` を省略しなければなりません(MUST)。 * **`BILLING_NOT_PERMITTED_FOR_AGENT` の details 形状はクランプされる。** `error.details` は [`error-details/billing-not-permitted-for-agent.json`](https://adcontextprotocol.org/schemas/v3/error-details/billing-not-permitted-for-agent.json) に準拠しなければなりません(MUST): `rejected_billing`(エコー)と任意の単一の `suggested_billing` リトライ値。スキーマは `additionalProperties: false` を設定します。形状はエージェントの完全な許可請求サブセット、レートカード、支払条件、信用限度、請求エンティティ、その他の per-agent 商業状態を運んではなりません(MUST NOT)— 単一プローブでの完全サブセット開示は、まさにクランプが防ぐオラクルです。 * **ワンショットリトライ。** `error.details.suggested_billing` でリトライして 2 つ目の `BILLING_NOT_PERMITTED_FOR_AGENT` を受け取ったバイヤーエージェントは、再度リトライするのではなく人間に表面化しなければなりません(MUST)。復旧はセラーが提案する単一のフォールバックに制限されます。さらなるイテレーションはセラーの設定ミスまたはエージェントが自律的に解決できないオンボーディング状態を示します。 **復旧ディスパッチ — 例。** 2 つの請求コードは異なる復旧をします。実装者は単一のリトライパスに折りたたむのではなく、明示的に分岐すべきです(SHOULD)。以下のスニペットは、他のタスク例で使う `@adcp/sdk/testing` ラッパーではなく、セラーが直接返すレスポンス形状(`sync_accounts` レスポンスの `accounts[].errors[]` 配列 — [task reference](/docs/accounts/tasks/sync_accounts) を参照)を使います。 ```javascript theme={null} async function syncAccountsWithRecovery(client, account) { const result = await client.syncAccounts({ accounts: [account] }); const error = result.accounts[0]?.errors?.[0]; if (!error) return result; switch (error.code) { case 'BILLING_NOT_SUPPORTED': { // Seller-wide or per-account gate. Check capabilities, dispatch on scope. const scope = error.details?.scope; // "capability" | "account" if (scope === 'capability') { // The seller never accepts this value. Pick from supported_billing. const supported = error.details?.supported_billing ?? []; if (supported.length === 0) return surfaceToHuman(error); return client.syncAccounts({ accounts: [{ ...account, billing: supported[0] }], }); } // Per-account-relationship reject — the operator-on-this-account isn't // billable directly. Try the next-most-permissive value the seller's // capability allows. return tryNextBillingValue(client, account, error); } case 'BILLING_NOT_PERMITTED_FOR_AGENT': { // Per-buyer-agent commercial gate. Autonomous retry only when the seller // suggests a fallback; otherwise surface — the agent cannot extend its // own commercial relationship. const suggested = error.details?.suggested_billing; if (!suggested) return surfaceToHuman(error); return client.syncAccounts({ accounts: [{ ...account, billing: suggested }], }); } default: throw error; } } ``` **例エンベロープ** — `BILLING_NOT_PERMITTED_FOR_AGENT`、パススルー専用バイヤーエージェントが `operator` へのフォールバックを受け取る: ```json theme={null} { "accounts": [{ "brand": { "domain": "nova-brands.com", "brand_id": "spark" }, "operator": "pinnacle-media.com", "action": "failed", "status": "rejected", "errors": [{ "code": "BILLING_NOT_PERMITTED_FOR_AGENT", "message": "This buyer agent is onboarded as passthrough-only; only operator billing is permitted.", "recovery": "correctable", "details": { "rejected_billing": "agent", "suggested_billing": "operator" } }] }] } ``` ### 認可(RBAC) 呼び出し元が認証されているがリクエストの特定スコープを欠く場合に返されます。強制はセラーローカルです。発見可能性は [`sync_accounts`](/docs/accounts/tasks/sync_accounts) と [`list_accounts`](/docs/accounts/tasks/list_accounts) のレスポンスのアカウントごとエントリの `authorization` オブジェクト経由です。完全な形状は [Caller authorization](/docs/accounts/overview#caller-authorization) を参照してください。 | Code | Recovery | Description | Resolution | | --------------------- | ----------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | | `PERMISSION_DENIED` | correctable | 汎用の認可失敗、または必要な署名付き認証情報(例: `governance_context`)が欠落・検証失敗・別のプラン/セラー/フェーズ向けに発行された | `check_governance` を呼んで有効なトークンを発行するか、根本的な権限を解決するためセラーに連絡する | | `SCOPE_INSUFFICIENT` | correctable | 呼び出されたタスクがこのアカウントの呼び出し元の `allowed_tasks` にない | `sync_accounts` または `list_accounts` でアカウントの `authorization` を再読み込みして呼び出し元の実際の `allowed_tasks` を発見し、許可されたタスクを使うか、より広いスコープを要求する | | `READ_ONLY_SCOPE` | correctable | 呼び出し元のスコープが `read_only: true`。呼び出されたタスクは状態を変更する | 非変更の代替を使うか、変更を許可するスコープを要求する | | `FIELD_NOT_PERMITTED` | correctable | リクエストフィールドがこのタスクの呼び出し元の `field_scopes` 許可リストにない | 許可されないフィールドを削除するか、より広いフィールドスコープを要求する | | `AGENT_SUSPENDED` | terminal | 呼び出し元バイヤーエージェントのこのセラーとの商業関係が一時停止されている | バイヤー側の人間に表面化する。セラーとのオフライン再オンボーディングで解決するかもしれない。エージェントは一方的に停止を解除できない。 | | `AGENT_BLOCKED` | terminal | 呼び出し元バイヤーエージェントのこのセラーとの商業関係が恒久的に拒否されている | バイヤー側の人間に表面化する。関係はセラーとのオフラインのオペレーターアクションを通じてのみ復活する。 | 規範的要件: * **`FIELD_NOT_PERMITTED` は `error.field` を設定しなければなりません(MUST)** — 正確な問題フィールドパス(例: `packages[0].budget`、`end_time`)を。それがなければ、エージェントはフィールドを削除してリトライすることで確実に自動復旧できません。複数フィールドが許可されない場合、各 `error.field` が明確になるようセラーは問題フィールドごとに 1 つのエラーを返すべきです(SHOULD)。単一エラーを返す場合、`error.details.fields` が完全なリストを含んでもよい(MAY)。 * **`SCOPE_INSUFFICIENT` は `error.details.introspection_hint` を含むべきです(SHOULD)** — セラーが sync/list で `authorization` オブジェクトをサポートする場合。ストローマン形状: `{ "task": "sync_accounts", "account": { ... } }`、呼び出し元にスコープを再発見する場所を指し示します。これはコーディングエージェントの「エラーが出た。どうすればいい?」ループを閉じます。 * **4 つのコードすべては `adcp_error` エンベロープフィールドとペイロード `errors[]` 配列の両方を設定しなければなりません(MUST)** — 上記の二層モデルに従い。スコープエラーはスキーマエラーと同じ方法で型付きクライアントライブラリに引き継がれます。 `SCOPE_INSUFFICIENT` vs. `PERMISSION_DENIED`: タスク自体がこのアカウントのこの呼び出し元に付与されていない場合は `SCOPE_INSUFFICIENT` を使います。`PERMISSION_DENIED` は認証情報形状の失敗(署名付きコンテキストの欠落、署名検証の失敗)と、スコープが正しい抽象化ではない汎用のセラーポリシー拒否に予約します。 `SCOPE_INSUFFICIENT` vs. `UNSUPPORTED_FEATURE`: 両方が該当する場合 — セラーがタスクを実装しておらず、かつ呼び出し元がいずれにせよスコープされない — セラーは `SCOPE_INSUFFICIENT` を返すべきです(SHOULD)。`UNSUPPORTED_FEATURE`(呼び出し元はセラーを切り替えなければならない)より actionable(呼び出し元はより広いスコープを要求できる)だからです。実装*する*が呼び出し元に公開されていないタスクに `UNSUPPORTED_FEATURE` を返すセラーは、呼び出し元が対応できないケイパビリティ情報を漏らしています。 `FIELD_NOT_PERMITTED` vs. `VALIDATION_ERROR`: フィールドはタスクスキーマ上有効で、異なるスコープの呼び出し元からは受け入れられます。この呼び出し元の `field_scopes` に含まれないことを理由に拒否されます。 **認可エラーの `recovery: correctable` について。** 4 つの authz コードすべては `correctable` に分類され、*リクエストを修正して再送できる*ことを意味します。これはエージェントが自律的にリクエストを修正できることを意味**しません** — `SCOPE_INSUFFICIENT` と `READ_ONLY_SCOPE` の「修正」はオペレーターからの帯域外のスコープ付与であり、エージェントは自分で実行できません。エージェントは同じ認証情報に対して自動リトライするのではなく、authz エラーをオペレーターに表面化すべきです(SHOULD)。固定スコープに対するリトライループは防御姿勢ではなくバグです — 下記の制限された曖昧性解消リトライという狭い例外を除いて。`FIELD_NOT_PERMITTED` のみがエージェント自律の復旧パス(許可されないフィールドを削除して再送)を持ちます。 **`SCOPE_INSUFFICIENT` と `READ_ONLY_SCOPE` のリトライ曖昧性解消。** セラーの 300 秒の認可リフレッシュウィンドウ内でのいずれかのコードの単一レスポンスは、クロスレプリカのちらつき — スペックがセラーに生成を禁じるが、バイヤーが実際には遭遇する一時的なインフラアーティファクト — と観測的に区別不能です。エラーをオペレーター介入が必要な確定的な `correctable` シグナルとして分類する前に、バイヤーはスコープが本当に不十分かを確立するため、制限されたリトライバジェット(3 回以下、各回 1〜5 秒のジッター付きバックオフで分離)を使い果たしてもよい(MAY)。これは曖昧性解消ロジックであり、correctable エラーからの復旧ではありません。制限されたリトライが成功なく尽きた後、バイヤーはエラーを表面化しなければならず(MUST)、自律的にリトライを続けてはなりません(MUST NOT)。このリトライ例外は `FIELD_NOT_PERMITTED` には適用されません — エージェント自律の strip-and-resubmit パスがそれに優先します。`READ_ONLY_SCOPE` の失効シナリオに関する注意を含む完全なガイダンスは [Buyer response to SCOPE\_INSUFFICIENT within the refresh window](/docs/accounts/overview#buyer-response-to-scope_insufficient-within-the-refresh-window)を参照してください。 **`FIELD_NOT_PERMITTED` — 両層を設定するエンベロープとペイロードの例:** ```json theme={null} { "adcp_error": { "code": "FIELD_NOT_PERMITTED", "message": "Caller's field_scopes for update_media_buy does not include this field", "recovery": "correctable", "field": "packages[0].budget" }, "payload": { "errors": [ { "code": "FIELD_NOT_PERMITTED", "message": "Caller's field_scopes for update_media_buy does not include this field", "recovery": "correctable", "field": "packages[0].budget", "details": { "task": "update_media_buy", "permitted_fields": ["reporting_webhook"] } } ] } } ``` `field` 値は削除するものを正確に特定します。`details.permitted_fields`(任意、助言的)は問題のタスクの許可リストを列挙し、エージェントがリトライ前に検証できます。`SCOPE_INSUFFICIENT` については、呼び出し元にスコープを再発見する場所を指すため `details.introspection_hint: { "task": "list_accounts", "account": { ... } }` を設定します。 #### エージェントごとの認可ゲート per-buyer-agent ゲートは 3 つの異なる拒否パスにわたって発火し、それぞれ独自の判別子を持つため、呼び出し元はプロースを解析せずにディスパッチできます: | Code | `details.scope` | `details.reason` | Meaning | | ------------------- | ----------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `AGENT_SUSPENDED` | —(`details.scope` なし。コードが判別子) | — | エージェントのセラーとの商業関係が一時停止。再オンボーディングで解決するかも。`recovery: "terminal"`。 | | `AGENT_BLOCKED` | —(`details.scope` なし。コードが判別子) | — | エージェントの商業関係が恒久的に拒否。自律復旧なし。`recovery: "terminal"`。 | | `PERMISSION_DENIED` | `"agent"` | `"sandbox_only"` | エージェントがサンドボックストラフィック専用にプロビジョニングされ、リクエストが非サンドボックスアカウントに対するもの。[`error-details/agent-permission-denied.json`](https://adcontextprotocol.org/schemas/v3/error-details/agent-permission-denied.json) に従い `additionalProperties: false`。 | per-agent 商業ステータス拒否(`AGENT_SUSPENDED`、`AGENT_BLOCKED`)は [`BILLING_NOT_PERMITTED_FOR_AGENT`](#請求とアカウントセットアップ) の先例に従います — コード自体が判別子で、レスポンスは明示的な `error.details.scope` フィールドを運びません。`PERMISSION_DENIED + scope:"agent"` パスは、`reason` が `error-details/agent-permission-denied.json` の閉じた enum に登録された非ステータスのプロビジョニングゲートに予約されます。`"agent"` 値は [`enums/error-scope.json`](https://adcontextprotocol.org/schemas/v3/enums/error-scope.json) の共有判別子語彙の登録済みサブセットです。 **新コードを作るか `reason` enum を拡張するか。** ライフサイクル終端の per-agent 状態(suspended、blocked、将来の deny-list スタイルの状態)は専用コードを得ます — 異なる `recovery` 分類を運び、一級判別子に値します。非ステータスのプロビジョニングゲートと一時的拒否は、代わりに `error-details/agent-permission-denied.json` の `reason` enum(例: `sandbox_only`)を拡張します。スロットリング形状の拒否は新しい per-agent コードではなく `RATE_LIMITED` を再利用します。dedicated-code-per-state パターンは、ライフサイクル語彙が有限だから成立するのであり、すべての新ゲートのためのオープンレジストリとしてではありません。 **3.0.5 プレースホルダーからの移行ノート。** 3.0.5 は per-agent ライフサイクルのプレースホルダーとして `details.status: ["suspended", "blocked"]` 軸を持つ `agent-permission-denied.json` を出荷しました。3.1 はそのプレースホルダーを専用の `AGENT_SUSPENDED` / `AGENT_BLOCKED` コードに統合します — `status` 軸は `agent-permission-denied.json` から削除され、スキーマは `scope:"agent" + reason:"sandbox_only"` のみを受け入れます。3.0.5 プレースホルダーに対して統合したセラーは専用コードに移行しなければなりません(MUST)。プレースホルダー形状は保持されません。 規範的要件(3 つのコードすべてに一律適用): * **確立されたエージェントアイデンティティなしの統一レスポンス。** per-agent 拒否は、[Agent identity](/docs/building/by-layer/L1/security#agent-identity) に従う署名付きリクエスト導出、またはセラーのオンボーディングレコードのクレデンシャル・トゥ・エージェントマッピングを通じてバイヤーエージェントアイデンティティが確立された場合にのみ意味があります。セラーはそのパスでのみ `AGENT_SUSPENDED` / `AGENT_BLOCKED` または `details.scope: "agent"` 付き `PERMISSION_DENIED` を発行しなければなりません(MUST)。それ以外のすべてのケース — 特定エージェントレコードにマップされていない bearer 認証情報を含む — では、汎用の `PERMISSION_DENIED` を返し、`error.details.scope` を省略しなければなりません(MUST)。アイデンティティ確立なしに per-agent コード(または per-agent スコープ)を返すと、未認証プローブがコード選択をクロステナントのオンボーディングオラクルとして使え、[`*_NOT_FOUND` 統一レスポンスルール](#標準エラーコード)と [`BILLING_NOT_PERMITTED_FOR_AGENT`](#請求とアカウントセットアップ) が閉じるのと同じ形状です。 * **未確立アイデンティティで省略ルールがカバーするチャネル。** MUST はすべての観測可能なチャネルに適用されます — `error.code` / `error.message` / `error.field` / `error.details`(未確立アイデンティティパスで message は汎用でなければならない)、HTTP ステータス、A2A `task.status.state`、MCP `isError`、レスポンスヘッダー(ETag、Cache-Control、per-agent レート制限バケット、CDN タグ)、副作用(監査ログ書き込み、Webhook ディスパッチ、バックグラウンドジョブのキューイング、per-agent クォータカウンター、DB シャードルーティング、オンボーディングレコードのキャッシュ投入)、可観測性(ログ、APM スパン、Sentry/Datadog/Rollbar タグは未確立アイデンティティパスでエージェントアイデンティティ、エージェントレコード参照、per-agent ゲート分類を運んではならない)。多相性はツールスキーマの宣言されたパラメーター形状に対して*いかなるオンボーディングレコードのルックアップの前にも*評価されます — ツールの宣言された形状は、アイデンティティが確立されたかに関わらずすべての呼び出し元にわたって同一でなければなりません(MUST)。 * **レイテンシー等価性。** セラーは両方のパスで同じ形状のオンボーディングレコードルックアップを実行しなければなりません(MUST、例: unmapped-credential パスで空のエージェントレコードに対して resolve-then-authorize)。ルックアップレイテンシーが「認証情報がエージェントにマップされていない」を「認証情報が suspended/blocked エージェントにマップされる」と区別しないように。resolve-then-authorize、decision-of-equivalent-shape、`*_NOT_FOUND` ルールが要求するのと同じ姿勢。キャッシュ投入はアイデンティティ確立でゲートされてはなりません(MUST NOT)— さもなくばキャッシュ自体がヒット/ミスタイミングを通じてオラクルになります。 * **リトライカウンターのサイドチャネル。** 「自律リトライなし」ルール(下記)自体がサイドチャネルになってはなりません。セラーは、未確立アイデンティティパスで、true-unauthenticated パスと異なる per-agent リトライ/バックオフカウンターをインクリメントしたり、異なる `retry_after` を発行したり、異なるレート制限ヘッダーを表面化したりしてはなりません(MUST NOT)。no-retry ルールに準拠するバイヤーエージェントは、その準拠が別のテナントが読めるセラー側の per-agent 可観測性に反映されてはなりません(MUST NOT)。 * **3 つのパスのいずれにも per-agent 商業状態なし。** `AGENT_SUSPENDED`、`AGENT_BLOCKED`、`PERMISSION_DENIED + scope:"agent"` のいずれも、エージェントの完全な許可請求サブセット、レートカード、支払条件、信用限度、請求エンティティ、連絡チャネル、カスタム reason 文字列、その他の per-agent 商業状態を運びません — 単一プローブでの完全サブセット開示は、まさにクランプが防ぐオラクルです。`AGENT_SUSPENDED` / `AGENT_BLOCKED` は `error.details` ペイロードを運びません。`PERMISSION_DENIED + scope:"agent"` は `error-details/agent-permission-denied.json`(`scope` + 登録済み `reason`、`additionalProperties: false`)に準拠しなければなりません(MUST)。新しい `reason` 値は、クロス言語 SDK がプロース解析なしにディスパッチできるようスキーマの enum に追加されなければなりません(MUST)。将来の per-agent 状態面(エスカレーションチャネル、lift-policy URL)は、この形状を緩めるのではなく、独自のクランプされた details 形状を持つ新しい専用コードに属します。スキーマの `additionalProperties: false` はスキーマ検証ガードです。セラーはセラー側コンポーザーから受け取った認識されないまたは拡張キーを欠陥として扱い、送信前に削除しなければなりません(MUST)。 * **per-agent ゲートで自律リトライなし。** 3 つのコードすべては実際上 terminal です: `AGENT_SUSPENDED` と `AGENT_BLOCKED` は `enumMetadata` の `recovery: "terminal"` で直接宣言します。`PERMISSION_DENIED + scope:"agent"` パスはワイヤーレベルで `correctable`(登録済み `PERMISSION_DENIED` 分類に一致)ですが実際上は terminal-pending-onboarding です — エージェントはサンドボックス専用プロビジョニングを一方的に解除できません。バイヤーエージェントは 3 つのいずれでも自動リトライするのではなくバイヤー側の人間に表面化しなければなりません(MUST)。再試行はゲートを強化するだけです。 **復旧ディスパッチ — 例。** 最初に `error.code` で分岐し、`PERMISSION_DENIED` の per-agent ゲートのみ `details.scope` にフォールスルーします: ```javascript theme={null} async function dispatchAuthzError(error) { // Per-agent commercial status — code is the discriminator, no details payload. if (error.code === 'AGENT_SUSPENDED' || error.code === 'AGENT_BLOCKED') { return surfaceToHuman({ code: error.code }); } if (error.code !== 'PERMISSION_DENIED') throw error; // Generic credential-shaped failure — no scope on details. if (!error.details?.scope) { return refreshGovernanceContextAndRetry(error); } // Per-agent provisioning gate — terminal-pending-onboarding. if (error.details?.scope === 'agent') { const { reason } = error.details; // reason: 'sandbox_only' return surfaceToHuman({ code: error.code, reason }); } throw error; // Unknown scope — surface rather than guess. } ``` **例エンベロープ** — `AGENT_SUSPENDED`: ```json theme={null} { "adcp_error": { "code": "AGENT_SUSPENDED", "message": "Buyer agent's commercial relationship with this seller is suspended.", "recovery": "terminal" } } ``` **例エンベロープ** — サンドボックス専用プロビジョニングゲート付き `PERMISSION_DENIED`: ```json theme={null} { "adcp_error": { "code": "PERMISSION_DENIED", "message": "This buyer agent is provisioned for sandbox traffic only.", "recovery": "correctable", "details": { "scope": "agent", "reason": "sandbox_only" } } } ``` サンドボックス専用パスのワイヤーレベル `recovery: "correctable"` は、[`error-code.json`](https://adcontextprotocol.org/schemas/v3/enums/error-code.json) の `enumMetadata` に従う `PERMISSION_DENIED` の登録済み分類です — SDK は `details.scope` に基づいて登録値を切り替えてはなりません(MUST NOT)。バイヤーエージェントはワイヤーレベルの `recovery` フィールドに関わらず拒否を terminal-pending-onboarding として扱い、人間に表面化し、自動リトライしないようにしなければなりません(MUST)。(suspended/blocked パスでは、コード自体が `recovery: "terminal"` を直接運ぶため、この注意は適用されません。) ### リクエストバリデーション | Code | Recovery | Description | Resolution | | ------------------------ | ----------- | --------------------------- | ------------------------------------------------- | | `INVALID_REQUEST` | correctable | リクエストが不正またはスキーマ制約に違反している | リクエストパラメーターを確認して修正する | | `UNSUPPORTED_FEATURE` | correctable | このセラーがサポートしていない機能を要求している | `get_adcp_capabilities` を確認してサポートされていないフィールドを削除する | | `POLICY_VIOLATION` | correctable | リクエストがコンテンツまたは広告ポリシーに違反している | エラー詳細のポリシー要件を確認する | | `COMPLIANCE_UNSATISFIED` | correctable | 必要な開示事項をターゲットフォーマットで満たせない | 必要な開示機能をサポートするフォーマットを選択する | ### インベントリと商品 | Code | Recovery | Description | Resolution | | ---------------------------- | ----------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | `PRODUCT_NOT_FOUND` | correctable | 参照した商品 ID が不明または期限切れ | 無効な ID を削除するか、`get_products` で再探索する | | `PRODUCT_UNAVAILABLE` | correctable | 商品が売り切れまたは利用不可 | 別の商品を選択する | | `PROPOSAL_EXPIRED` | correctable | 参照したプロポーザルの `expires_at` が過ぎている | `get_products` を実行して新しいプロポーザルを取得する | | `PROPOSAL_NOT_FOUND` | correctable | `proposal_id` がセラーに不明(未確定、誤テナント、またはキャッシュから退避) | `get_products` を `buying_mode: "refine"` + `action: "finalize"` で再発行して現在の proposal\_id を取得する | | `MULTI_FINALIZE_UNSUPPORTED` | correctable | `refine[]` が複数の `action: "finalize"` エントリを運んだ。セラーがアトミックな複数プロポーザルコミットを保証できない | 単一プロポーザルの finalize 呼び出しを順次実行する(`get_products` 呼び出しごとに 1 つの finalize) | | `REQUOTE_REQUIRED` | correctable | 要求された更新が、元の見積もりが価格付けされたエンベロープ(予算、日付、ボリューム、ターゲティング)の外にある。`pricing_option` はロックされたまま | 更新を現在の見積もりに合わせるか、商品/条件を再発見するか、利用可能ならパッケージを追加するか、別のメディアバイを作成する。3.1 は `update_media_buy` の修正見積もりアーティファクトを定義していない。 | | `SIGNAL_NOT_FOUND` | correctable | 参照されたシグナルがカタログに存在しない | `get_signals` で `signal_id` を検証するか、このエージェントからの利用可能性を確認する | | `AUDIENCE_TOO_SMALL` | correctable | オーディエンスセグメントが最小サイズを下回っている | ターゲティングを広げるか、より多くのオーディエンスメンバーをアップロードする | ### 予算とクリエイティブ | Code | Recovery | Description | Resolution | | -------------------- | ----------- | ------------------------- | -------------------------------------------------------------- | | `BUDGET_TOO_LOW` | correctable | 予算がセラーの最小値を下回っている | 予算を増やすか `capabilities.media_buy.limits` を確認する | | `BUDGET_EXHAUSTED` | terminal | アカウントまたはキャンペーン予算を使い切った | バイヤーが資金を追加するか予算上限を増やさなければなりません | | `CREATIVE_NOT_FOUND` | correctable | 参照されたクリエイティブがライブラリに存在しない | `list_creatives` で `creative_id` を検証するか、`sync_creatives` で登録する | | `CREATIVE_REJECTED` | correctable | クリエイティブがコンテンツポリシーレビューに不合格 | セラーの `advertising_policies` に従って修正する | ### システム | Code | Recovery | Description | Resolution | | --------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | `RATE_LIMITED` | transient | リクエストレートを超過した | `retry_after` 秒待ってからリトライする | | `SERVICE_UNAVAILABLE` | transient | セラーサービスが一時的に利用不可 | 指数バックオフでリトライする | | `STALE_RESPONSE` | transient(助言的) | 非致命的: 上流/サブエージェントが到達不能だったため、セラーがフレッシュネス目標を過ぎたキャッシュから設定済みペイロードを提供した。`SERVICE_UNAVAILABLE`(空ペイロード + 致命的)とは異なる。`error.details` は [`error-details/stale-response.json`](pathname:///schemas/v3/error-details/stale-response.json) に従う | キャッシュされたペイロードを受け入れるか、フレッシュなデータのため後でリトライする — `error.details.cache_age_seconds` を検査して判断する | | `CONFIGURATION_ERROR` | terminal | セラー側のデプロイ設定ミス(例: `mock_upstream_url` の欠落、未宣言の `upstream_url`、未設定の環境変数)。`SERVICE_UNAVAILABLE`(一時的)と `INVALID_REQUEST`(バイヤー修正可能)とは異なる。セラーはトランスポート失敗マーカー(HTTP 5xx、MCP `isError: true`、A2A `failed`)を立てなければならない(MUST)。`error.message` はオペレーターが対処可能な詳細を運び、認証情報・接続文字列・スタックトレースを含んではならない(MUST NOT) | セラーのオペレーターに表面化する。自動リトライしない — リトライは設定ミスのデプロイを解決しない | | `CONFLICT` | transient | 同時変更が検出された | リソースを再読み込みして最新状態でリトライする | | `REFERENCE_NOT_FOUND` | correctable | 専用の not-found コードを持たない参照リソースの汎用フォールバック。[Not-found の優先順位](#標準エラーコード)を参照 | 適切なディスカバリータスクで識別子を検証する。存在する場合はリソース固有のコードを優先する | ## 復旧分類 `recovery` フィールドを使ってエラーの処理方法を決定します: | Recovery | 意味 | アクション | | ------------- | --------------------------------------- | -------------------------------- | | `transient` | 一時的な失敗(レート制限、サービス停止、競合) | `retry_after` 後または指数バックオフでリトライする | | `correctable` | リクエストを修正して再送可能(無効フィールド、予算不足、クリエイティブ不合格) | リクエストを変更してリトライする | | `terminal` | 人の対応が必要(アカウント停止、支払い必要) | 人のオペレーターにエスカレートする | 未知の `recovery` 値(前方互換性)は `terminal` として扱います。 ```javascript theme={null} function isRetryable(error) { // Use recovery field when available if (error.recovery) { return error.recovery === 'transient'; } // Network errors are retryable if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT') { return true; } // Fall back to error code matching return ['RATE_LIMITED', 'SERVICE_UNAVAILABLE', 'CONFLICT'].includes(error.code); } ``` ## リトライロジック このセクションのルールは、呼び出し元がリトライしてよいすべての `transient` 分類エラーを制限します。これには [§ Forward-compatible decoding](#forward-compatible-decoding規範的) の下で未知のエラーコードに適用される `transient` デフォルトを含みます。未知のコードをデコードして `transient` にフォールバックする受信者は、下記の `maxRetries` とジッター付き指数バックオフスケジュールを適用しなければなりません(MUST)。オープン enum のデコードルールは受信者をリトライバジェットから免除しません。`code=GO_FOREVER, recovery=transient` を発行する敵対的またはバグのある送信者は、コンフォーマントなクライアントに対して無制限のリトライを誘発できません。 ### Normative throttling behavior これらのルールは、呼び出し元がスロットリングカテゴリのエラー(`RATE_LIMITED`、または `recovery` が `transient` で `details` が [`rate-limited`](https://adcontextprotocol.org/schemas/v3/error-details/rate-limited.json) の detail 形状に準拠する任意のエラー)を受け取ったときに適用されます: * 呼び出し元は存在する場合 `retry_after` を尊重しなければならず(**MUST**)、示された秒数より早く同じリクエストをリトライしてはなりません(**MUST NOT**)。 * 呼び出し元は `retry_after` が不在の場合、ジッター付き指数バックオフを使うべきです(**SHOULD**)。ベース 2 秒、上限 60 秒、±25% ジッターが安全なデフォルトです。 * 呼び出し元は非スロットリングエラー(例: `INVALID_REQUEST`、`CREATIVE_REJECTED`)をスロットリングされたかのように扱ってはなりません(**MUST NOT**)。他の理由で拒否されたレスポンスをバックオフのケイデンスでリトライするのは防御姿勢ではなくバグです。 * 呼び出し元は繰り返されるスロットリングを無限にリトライするのではなくオペレーターに表面化すべきです(**SHOULD**)。持続する `RATE_LIMITED` レスポンスは一時的な瞬きではなく、キャパシティまたはポリシーのシグナルです。 * セラーは、行儀の良い呼び出し元が意図的にバックオフできるよう、リクエストを黙ってキューイングまたはドロップするのではなく、`retry_after` を設定した `RATE_LIMITED` を返すべきです(**SHOULD**)。 * セラーは、呼び出し元が 429 ごとに反応するのではなく先を計画できるよう、[`rate-limited`](https://adcontextprotocol.org/schemas/v3/error-details/rate-limited.json) の detail 形状(`limit`、`remaining`、`window_seconds`、`scope`)を設定してもよい(**MAY**)。 ### 指数バックオフ リトライ可能なエラーには指数バックオフを実装します: ```javascript theme={null} async function retryWithBackoff(fn, options = {}) { const { maxRetries = 3, baseDelay = 1000, maxDelay = 60000 } = options; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await fn(); } catch (error) { if (!isRetryable(error) || attempt === maxRetries) { throw error; } // Use retry_after when available, otherwise exponential backoff const retryAfter = error.retry_after || Math.min(baseDelay * Math.pow(2, attempt), maxDelay); // Add jitter to prevent thundering herd const jitter = retryAfter * (0.75 + Math.random() * 0.5); await sleep(jitter); } } } ``` ### レート制限の処理 ```javascript theme={null} async function handleRateLimit(error, retryFn) { if (error.recovery !== 'transient' && error.code !== 'RATE_LIMITED') { throw error; } const retryAfter = error.retry_after || 60; console.log(`Rate limited. Waiting ${retryAfter} seconds...`); await sleep(retryAfter * 1000); return retryFn(); } ``` ## エラーハンドリングパターン ### 基本的なエラーハンドラー ```javascript theme={null} async function handleAdcpError(error) { // Use recovery classification when available switch (error.recovery) { case 'transient': const delay = error.retry_after ? error.retry_after * 1000 : 5000; await sleep(delay); return retry(); case 'correctable': // Surface suggestion so the request can be fixed if (error.suggestion) { console.log('Suggestion:', error.suggestion); } if (error.field) { console.log('Problem field:', error.field); } throw error; case 'terminal': console.error('Terminal error:', error.message); throw error; } // Fall back to error code matching switch (error.code) { case 'AUTH_REQUIRED': await refreshCredentials(); return retry(); case 'INVALID_REQUEST': console.error('Validation error:', error); throw error; default: console.error('AdCP error:', error); throw error; } } ``` ### ユーザーフレンドリーなメッセージ 技術的なエラーをユーザー向けメッセージに変換します: ```javascript theme={null} const USER_MESSAGES = { 'RATE_LIMITED': 'Too many requests. Please wait a moment and try again.', 'BUDGET_TOO_LOW': 'This is below the seller\'s minimum budget. Increase your budget.', 'PRODUCT_NOT_FOUND': 'One or more products could not be found. Try searching again.', 'ACCOUNT_SUSPENDED': 'Your account has been suspended. Contact the seller to resolve.', 'SERVICE_UNAVAILABLE': 'The service is temporarily unavailable. Please try again in a few minutes.', 'CREATIVE_REJECTED': 'Your creative did not pass policy review. Check the suggestion for details.', 'AUDIENCE_TOO_SMALL': 'Your target audience is too small. Try broadening your targeting.' }; function getUserMessage(code, fallbackMessage) { return USER_MESSAGES[code] || fallbackMessage || 'An unexpected error occurred. Please try again.'; } ``` ### 構造化されたエラーログ デバッグのためにコンテキスト付きでエラーを記録します: ```javascript theme={null} function logError(error, context = {}) { console.error('AdCP Error:', { code: error.code, recovery: error.recovery, message: error.message, field: error.field, timestamp: new Date().toISOString(), ...context, // Don't log sensitive data // NO: credentials, briefs, PII }); } ``` ## Webhook のエラーハンドリング ### Webhook 配信失敗 Webhook 配信に失敗した場合、ポーリングにフォールバックします: ```javascript theme={null} class WebhookErrorHandler { async onDeliveryFailure(taskId, error) { console.warn(`Webhook delivery failed for ${taskId}:`, error); // Start polling as fallback this.startPolling(taskId); // Track failure for monitoring this.metrics.incrementCounter('webhook_failures'); } async startPolling(taskId) { const response = await adcp.call('tasks/get', { task_id: taskId, include_result: true }); if (['completed', 'failed', 'canceled'].includes(response.status)) { await this.processResult(taskId, response); } else { // Schedule next poll setTimeout(() => this.startPolling(taskId), 30000); } } } ``` ### Webhook ハンドラーのエラー Webhook エンドポイント内のエラーを丁寧に扱います: ```javascript theme={null} app.post('/webhooks/adcp', async (req, res) => { try { // Always respond quickly res.status(200).json({ status: 'received' }); // Process asynchronously await processWebhookAsync(req.body); } catch (error) { // Log error but don't fail the response console.error('Webhook processing error:', error); // Move to dead letter queue for investigation await deadLetterQueue.add(req.body, error); } }); ``` ## 復旧戦略 ### コンテキストの復旧 コンテキストが期限切れの場合は新しい会話を開始します: ```javascript theme={null} async function callWithContextRecovery(request) { try { return await adcp.call(request); } catch (error) { if (error.code === 'INVALID_REQUEST' && error.message?.includes('context not found')) { // Clear stale context and retry delete request.context_id; return await adcp.call(request); } throw error; } } ``` ### 部分的成功の扱い 一部のオペレーションは部分的に成功する場合があります: ```json theme={null} { "status": "completed", "message": "Created media buy with warnings", "media_buy_id": "mb_123", "errors": [ { "code": "COMPLIANCE_UNSATISFIED", "message": "Required disclosure position not supported by one placement", "field": "packages[0].placements[2]", "suggestion": "Choose a format that supports the required disclosure positions" } ] } ``` 部分成功を処理します: ```javascript theme={null} function handlePartialSuccess(response) { if (response.status === 'completed' && response.errors?.length) { // Show warnings to user for (const warning of response.errors) { showWarning(warning.message, warning.suggestion); } } // Continue with successful result return response; } ``` ## ガバナンスエラーパターン [`check_governance`](/docs/governance/campaign/tasks/check_governance) はエラーオブジェクトではなく `status` フィールドを返します。ガバナンス結果はプロトコル的な意味でのエラーではありません — それらは判断です。AdCP タスクエラーとは別に扱ってください。 | ガバナンスステータス | 意味 | アクション | | ------------ | ------------ | ------------ | | `approved` | プランがガバナンスを通過 | 進む | | `conditions` | 制約付きで承認 | 条件を適用し、再チェック | | `denied` | プランがガバナンスに違反 | オペレーションをブロック | ガバナンスエージェントが内部的に人間のレビューを必要とする場合(例: アクションがエージェントの権限を超える)、`check_governance` は任意の非同期タスクのように振る舞います — `submitted`/`working` ステータスを返し、最終的に `approved` または `denied` に解決します。これは特別なロジックではなく標準の[非同期タスクライフサイクル](/docs/building/by-layer/L3/task-lifecycle)で扱ってください。 プロトコル層からのガバナンスエラー(ガバナンス判断とは対照的に)は標準のエラー形式を使います。最も一般的なもの: | Code | Recovery | 発生するとき | | ----------------- | ----------- | -------------------------------------------- | | `PLAN_NOT_FOUND` | correctable | `check_governance` の前に `sync_plans` が呼ばれなかった | | `INVALID_REQUEST` | correctable | 必須フィールドの欠落(例: `plan_id`、`caller`) | | `AUTH_REQUIRED` | correctable | ガバナンスエージェントが認証を要求 | ## 設定エラーパターン `CONFIGURATION_ERROR` はセラー側のデプロイ欠陥を示します — `mode: 'mock'` で宣言されたが `mock_upstream_url` のないアカウント、`upstream_url` のない `mode: 'live'` または `mode: 'sandbox'` のプラットフォーム、セラープロセスで未設定の必須環境変数。バイヤーは修正できず、リトライは解決できず、セラー側のオペレーターが対処しなければなりません。カタログには設計上汎用の `INTERNAL_ERROR` コードがなく、`CONFIGURATION_ERROR` は意図的により狭い — 修復が「これをセラーのオペレーターに報告する」である actionable なスライスをカバーします。そのプロファイルに合わない不透明なクラッシュはカタログ非コード化のままです。セラーはプラットフォーム固有のコードを返してもよく(MAY)、バイヤーは[前方互換ルール](#復旧分類)に従って `recovery` 分類にフォールバックします。 ### 集約シグナル: リクエストごとに terminal、セラーごとに障害 単一の `CONFIGURATION_ERROR` はそれを受け取ったリクエストについて `terminal` です — バイヤーはセラー側の人間に表面化しなければならず(MUST)、自動リトライしてはなりません(MUST NOT)。短いウィンドウ内での同じセラーからの繰り返しの `CONFIGURATION_ERROR` は別種の運用シグナルです: セラー側の障害。バイヤー側のダッシュボードとアラートは、リクエストごと terminal の扱いとは別に、セラーごとの集約 `CONFIGURATION_ERROR` レートを障害インジケーターとして扱うべきです(SHOULD、例: 単一セラーから M 分に N 回発生でページング)。この収束が重要なのは、集約 `CONFIGURATION_ERROR` を汎用の terminal エラーとバケット化するバイヤーは、コードの存在意義であるセラー分離された障害シグナルを失うからです。 ### error.message: オペレーターが対処可能、デプロイ内部ではない コード自体が判別子です — `CONFIGURATION_ERROR` は `error.details` 形状を運びません(`AGENT_SUSPENDED` / `AGENT_BLOCKED` の[最小開示の先例](#エージェントごとの認可ゲート)が適用)。`error.message` が診断を運び、セラーはデプロイ内部をバイヤーに漏らさずにセラー側のオペレーターに有用なレベルに調整すべきです(SHOULD)。message はワイヤー可視です — 認証情報、接続文字列、完全なファイルパス、スタックトレースを含んではなりません(MUST NOT)。 有用(オペレーターは対処でき、バイヤーは悪用可能なことを何も学ばない): ```json theme={null} { "code": "CONFIGURATION_ERROR", "message": "account is mode='mock' but no mock_upstream_url declared in metadata; populate it in the AccountStore", "recovery": "terminal" } ``` 有用でない(オペレーターは既に問題があると知っていた。バイヤーはセラーのファイルシステムの場所を学ぶ): ```json theme={null} { "code": "CONFIGURATION_ERROR", "message": "configuration error", "recovery": "terminal" } ``` 漏洩(やってはいけない): ```json theme={null} { "code": "CONFIGURATION_ERROR", "message": "ECONNREFUSED postgres://admin:hunter2@10.0.1.42:5432/prod (at /opt/seller/src/db/pool.ts:127)", "recovery": "terminal" } ``` ## ベストプラクティス 1. **まず `recovery` を確認する** — エラーの処理方法として最も信頼できるシグナルです 2. **リトライを実装する** — 一時的エラーは指数バックオフを使用します 3. **レート制限を尊重する** — `retry_after` の値を順守します 4. **未知のコードを適切に扱う** — `recovery` 分類にフォールバックします 5. **コンテキスト付きログ** — デバッグ用に `code`、`recovery`、`field` を含めます 6. **フォールバックを用意する** — 常に代替策を持ちます(例: Webhook 失敗時のポーリング) 7. **terminal エラーはリトライしない** — 人のオペレーターにエスカレートします 8. **部分成功に対応する** — 成功レスポンスの警告も処理します ## 次のステップ * **Transport Bindings**: エラーが MCP と A2A でどう伝達されるかは [Transport Errors](/docs/building/operating/transport-errors) * **Task Lifecycle**: ステータス処理は [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) * **Webhooks**: Webhook エラー処理は [Webhooks](/docs/building/by-layer/L3/webhooks) * **Security**: 認証エラーは [Security](/docs/building/by-layer/L1/security) # L3 — プロトコルセマンティクス Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L3/index AdCP スタックのプロトコルセマンティクス層。ライフサイクルステートマシン、冪等性、エラーカタログ、非同期タスクコントラクト、適合性テストサーフェス、webhook 発出。SDK の価値のほとんどが存在する場所。 L3 はエージェント側で AdCP が *何を意味するか* を強制します。ワイヤー形状は整形式(L0)、呼び出し元は本物(L1)で認可済み(L2)。今: リクエストは世界の現在の状態を考慮して合法か? エージェントにとって、L3 はプロトコルサーフェスの大部分です — [一から 3〜4 人月のビルド](/docs/building/cross-cutting/sdk-stack#why-sdks-matter-more-in-adcp-than-in-eg-http) はほぼ完全にここに存在します。呼び出し元にとって、L3 はコンシューマー側: 状態遷移を強制するのではなく、エラーコードを分類し状態遷移を扱う、数週間のハンドラーグルー。 ## L3 の SDK が提供しなければならないもの SDK を選ぶか新しい言語に移植する場合、これが L3 のビルドターゲットです: * すべての仕様定義リソースの **ライフサイクルステートマシングラフ**、仕様正しいエラーコード(`NOT_CANCELLABLE` / `INVALID_STATE` など)を発する遷移アサーションプリミティブ付き。 * クロスペイロード衝突検出と `IDEMPOTENCY_CONFLICT` エンベロープの no-payload-echo 不変条件を持つ **冪等性キャッシュ**。 * **非同期タスクストア + ディスパッチャー** — ツールは非同期にオプトインする。SDK は `task_id` を返し、ポーリングを受け入れ、終端アーティファクトを発する。 * **Webhook エミッター** — 署名済み、リトライ済み、冪等。 * 解決されたアカウントが sandbox または mock モードのとき状態を決定的に駆動するよう配線された(そうでなければ拒否される)**適合性テストサーフェス**(`comply_test_controller`)。 * 仕様のエコーコントラクトを扱う **リソースごとの永続性プリミティブ**。 * 上のすべてを賢明なデフォルトで結びつける **サーバー構築エントリポイント**。 累積的なクロス層のストーリー(L0+L1+L2+L3 が何をもたらすか)については、[SDK スタックリファレンス](/docs/building/cross-cutting/sdk-stack#l3--protocol-semantics) を参照。2.5 と 3.0 の間で L3 で何が変わったかについては、[What changed at L3 in 3.0](/docs/building/cross-cutting/version-adaptation#what-changed-at-l3-in-3-0) を参照。 ## この層のページ * **[Task lifecycle](/docs/building/by-layer/L3/task-lifecycle)** — ステータス値、遷移、ポーリング。 * **[Async operations](/docs/building/by-layer/L3/async-operations)** — 同期、非同期、インタラクティブなタスク処理。 * **[Webhooks](/docs/building/by-layer/L3/webhooks)** — プッシュ通知、署名、リトライ、冪等性。 * **[Error handling](/docs/building/by-layer/L3/error-handling)** — エラーカテゴリー、コード、リカバリー分類。 * **[`comply_test_controller`](/docs/building/by-layer/L3/comply-test-controller)** — サンドボックス専用の適合性テストサーフェス。 # タスクライフサイクル Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L3/task-lifecycle AdCP タスクライフサイクル: ステータス値(submitted、working、input-required、completed、failed)、状態遷移、レスポンス構造、すべてのオペレーションのポーリングパターン。 すべての AdCP レスポンスには `status` フィールドが含まれ、現在の状態と次に取るべき行動を示します。これはすべての AdCP 処理の基盤です。 ## ステータス値 AdCP は [A2A プロトコルの TaskState enum](https://a2a-protocol.org/dev/specification/#63-taskstate-enum) と同じステータス値を使用します: | Status | Meaning | Your Action | | ---------------- | -------------------------- | ------------------------------------------------- | | `submitted` | Task queued for execution | Show "queued" indicator, wait for updates | | `working` | Agent actively processing | Show progress, poll frequently for updates | | `input-required` | Needs information from you | Read `message` field, prompt user, send follow-up | | `completed` | Successfully finished | Process `data`, show success message | | `canceled` | User/system canceled task | Show cancellation notice, clean up | | `failed` | Error occurred | Show error from `message`, handle gracefully | | `rejected` | Agent rejected the request | Show rejection reason, don't retry | | `auth-required` | Authentication needed | Prompt for auth, retry with credentials | | `unknown` | Indeterminate state | Log for debugging, may need manual intervention | ## レスポンス構造 AdCP レスポンスはタスク固有フィールドがトップレベルにある **フラット構造** です: ```json theme={null} { "status": "completed", // Always present: what state we're in "message": "Found 5 products", // Always present: human explanation "context_id": "ctx-123", // Session continuity "context": { // Application-level context echoed back "ui": "buyer_dashboard" }, "products": [...] // Task-specific fields at top level } ``` ## ステータス処理 ### 基本パターン ```javascript theme={null} function handleAdcpResponse(response) { switch (response.status) { case 'completed': // 成功 - データ処理(タスクフィールドはトップレベル) showSuccess(response.message); return processData(response); case 'input-required': // 追加情報が必要 - ユーザーに確認 const userInput = await promptUser(response.message); return sendFollowUp(response.context_id, userInput); case 'working': // 進行中 - 進捗表示して待機 showProgress(response.message); return pollForUpdates(response.context_id); case 'failed': // エラー - メッセージを表示し丁寧に処理 showError(response.message); return handleError(response.errors); case 'auth-required': // 認証が必要 const credentials = await getAuth(); return retryWithAuth(credentials); default: // 想定外のステータス console.warn('Unknown status:', response.status); showMessage(response.message); } } ``` ### 確認フロー ステータスが `input-required` のとき、必要な情報が message で示されます: ```json theme={null} { "status": "input-required", "message": "I need more information about your campaign. What's your budget and target audience?", "context_id": "ctx-123", "products": [], "suggestions": ["budget", "audience", "timing"] } ``` **クライアントサイドの処理:** ```javascript theme={null} if (response.status === 'input-required') { // message から必要項目を抽出 const missingInfo = extractRequirements(response.message); // 必要項目に応じて質問 const answers = await promptForInfo(missingInfo); // 同じ context_id で追送 return sendMessage(response.context_id, answers); } ``` ### 承認フロー 人による承認は `input-required` の特殊ケースです: ```json theme={null} { "status": "input-required", "message": "Media buy exceeds auto-approval limit ($100K). Please approve to proceed with campaign creation.", "context_id": "ctx-123", "approval_required": true, "amount": 150000, "reason": "exceeds_limit" } ``` **クライアントサイドの処理:** ```javascript theme={null} if (response.status === 'input-required' && response.approval_required) { // 承認 UI を表示 const approved = await showApprovalDialog(response.message, response); // 承認結果を送信 const decision = approved ? "Approved" : "Rejected"; return sendMessage(response.context_id, decision); } ``` ### 長時間オペレーション 非同期オペレーションは `working` または `submitted` で開始し、進捗を返します: ```json theme={null} { "status": "working", "message": "Creating media buy. Validating inventory availability...", "context_id": "ctx-123", "task_id": "task-456", "progress": 25, "step": "inventory_validation" } ``` **プロトコル別のポーリング:** * **MCP**: context\_id を使ってポーリング * **A2A**: SSE ストリームでリアルタイム更新を購読 ## ステータスの流れ タスクは予測可能な状態を経由して進行する: ``` submitted → working → completed ↓ ↓ ↑ input-required → → → → → ↓ failed ``` * **`submitted`**: 実行待ち。Webhook を設定するかポーリング * **`working`**: 処理中。高頻度でポーリング * **`input-required`**: ユーザー入力が必要。会話を継続 * **`completed`**: 成功。結果を処理 * **`failed`**: エラー。適切に処理 ## ポーリングパターン ### ステータス別ポーリング間隔 ステータスによってポーリング頻度を変えます: ```javascript theme={null} const POLLING_INTERVALS = { working: 5000, // 5 seconds - should complete within 120s submitted: 60000, // 1 minute - long-running operations 'input-required': null // Don't poll - wait for user input }; async function pollForUpdates(taskId, currentStatus) { const interval = POLLING_INTERVALS[currentStatus]; if (!interval) return; await sleep(interval); const response = await adcp.call('tasks/get', { task_id: taskId, include_result: true }); if (['completed', 'failed', 'canceled'].includes(response.status)) { return response; } return pollForUpdates(taskId, response.status); } ``` ### タイムアウト処理 オペレーション種別に応じて妥当なタイムアウトを設定します: ```javascript theme={null} const TIMEOUTS = { sync: 30_000, // 30 seconds for immediate operations interactive: 300_000, // 5 minutes for human input working: 120_000, // 2 minutes for working tasks submitted: 86_400_000 // 24 hours for submitted tasks }; function setTimeoutForStatus(status) { switch (status) { case 'working': return TIMEOUTS.working; case 'submitted': return TIMEOUTS.submitted; case 'input-required': return TIMEOUTS.interactive; default: return TIMEOUTS.sync; } } ``` ## タスク再同期 `tasks/list` で失われた状態を復元します: ```javascript theme={null} // 保留中のオペレーションを取得 const pending = await session.call('tasks/list', { filters: { statuses: ["submitted", "working", "input-required"] } }); // ローカル状態と突き合わせ const missingTasks = pending.tasks.filter(task => !localState.hasTask(task.task_id) ); // 未追跡タスクの監視を再開 for (const task of missingTasks) { startPolling(task.task_id); } ``` ## ベストプラクティス 1. **まず status を確認** - 成功前提にしません 2. **すべてのステータスを処理** - 未知の状態も default でカバー 3. **context\_id を保持** - 会話継続に必須 4. **task\_id で追跡** - 特に長時間オペレーションで重要 5. **タイムアウトを実装** - 無限に待たない 6. **ステータス遷移をログ** - デバッグと監査に有用 ## 次のステップ * **Async Operations**: 異なるオペレーション種別の扱いは [Async Operations](/docs/building/by-layer/L3/async-operations) * **Webhooks**: プッシュ通知パターンは [Webhooks](/docs/building/by-layer/L3/webhooks) * **Error Handling**: エラー分類と復旧は [Error Handling](/docs/building/by-layer/L3/error-handling) # Push Notifications Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L3/webhooks AdCP プッシュ通知: セラーが RFC 9421 署名付き POST リクエスト(レガシー HMAC フォールバック付き)を通じて、非同期タスクのステータス更新を Webhook エンドポイントに配信する方法。セットアップ、URL テンプレート、冪等性。 プッシュ通知により、セラーはポーリングを要求する代わりに、タスクステータスの更新をあなたに直接配信できます。タスクリクエストで Webhook URL を提供すると、タスクの進行に伴ってセラーがその URL にステータス変更を POST します。 ## 仕組み 1. Webhook 相関のため、タスク呼び出しごとに一意のオペレーション ID が生成される 2. あなたのレシーバー向けに Webhook URL が構築される。URL は自身のルーティングトークンを含んでよいが、セラーにとっては不透明である 3. `push_notification_config` が URL と明示的な `operation_id` を伴ってタスクリクエストボディに注入される — 共有シークレットは不要 4. タスクステータスが変わるとセラーがあなたの URL に Webhook 通知を POST する。各 POST は、自身の brand.json の `agents[]` エントリで公開した `adcp_use: "request-signing"` 鍵で署名される。非推奨の `webhook-signing` 鍵は互換期間中も受け入れられる 5. あなたはセラーが公開する JWKS に対して署名を検証し、`idempotency_key` で重複排除する 6. 各通知はペイロードで明示的な `operation_id` をエコーバックするため、URL を解析せずに相関できる ``` create_media_buy request └── push_notification_config └── url: "https://you.com/adcp/webhook/create_media_buy/agent_123/route_abc123" └── operation_id: "op_456" // No shared secret — the seller signs with its own key, you verify against // its published JWKS. See "Signature verification" below. ↓ seller processes task ↓ POST https://you.com/adcp/webhook/create_media_buy/agent_123/route_abc123 Signature-Input: sig1=("@method" "@target-uri" "@authority" "content-type" "content-digest"); created=1706097600;expires=1706097900;nonce="...";keyid="seller-webhook-2025"; alg="ed25519";tag="adcp/webhook-signing/v1" Signature: sig1=:: Content-Digest: sha-256=:: Content-Type: application/json { "idempotency_key": "whk_01HW9D3H8FZP2N6R8T0V4X6Z9B", ← dedup by this "task_id": "task_456", "operation_id": "op_456", ← echoed from push_notification_config.operation_id "status": "completed", "result": { ... } } ``` `@adcp/sdk` ライブラリを使用している場合、このフロー全体が自動的に処理されます。**バイヤー**としては、クライアントに `webhookUrlTemplate` と自身のエージェント URL を設定します。`push_notification_config` がすべての送信タスク呼び出しに注入され、受信 Webhook はセラーの JWKS に対して自動的に検証されます。**Webhook を発行するセラー**としては、brand.json の `agents[]` エントリに署名 JWK を公開します。新しい署名者は `adcp_use: "request-signing"` を使用します。Webhook 専用の鍵素材が欲しい場合は、別個の `kid` を持つ 2 つ目の `request-signing` JWK を公開します。 :::warning レガシー HMAC フォールバック(非推奨) RFC 9421 Webhook プロファイルをまだ採用していないレシーバーと統合するバイヤーは、`push_notification_config.authentication.credentials` を設定することでレガシー HMAC-SHA256 スキームにオプトインしてもよい(MAY)。そのパスは非推奨であり AdCP 4.0 で削除されます — 下記の [レガシー HMAC-SHA256 フォールバック](#legacy-hmac-sha256-fallback-deprecated) を参照してください。Webhook を登録するインバウンドリクエストは 3.0 では通常 9421 署名されないため、`authentication` ブロックは経路上の除去/注入を受けやすい — 運用上の緩和策は [ダウングレードと注入への耐性](/docs/building/by-layer/L1/security#webhook-callbacks)を参照してください。 ::: ## 命名: snake\_case vs camelCase これは人を混乱させます。2 つの命名規則が関係しています: | コンテキスト | フィールド名 | 例 | | ---------------------------- | -------------------------- | ---------------------------------------------------------------------------- | | **MCP タスク引数**(AdCP JSON) | `push_notification_config` | `{ push_notification_config: { url: ..., operation_id: ... } }` | | **A2A configuration オブジェクト** | `pushNotificationConfig` | `configuration: { pushNotificationConfig: { url: ..., operation_id: ... } }` | AdCP のフィールド名は常に **`push_notification_config`**(snake\_case)です。他のタスクパラメーターと並んでタスクリクエストボディに入ります。 A2A では、A2A プロトコルが camelCase を使う `configuration` エンベロープでこれをラップします — が、オブジェクトの中身は同一です。 ## リクエストへの push\_notification\_config の追加 ### MCP `push_notification_config` をタスク引数として、他のタスクパラメーターとマージして含めます: ```json theme={null} { "brand": { "brand_id": "acme" }, "start_time": { "type": "date", "date": "2025-03-01" }, "end_time": "2025-06-30T23:59:59Z", "packages": [...], "push_notification_config": { "url": "https://you.com/webhooks/adcp/create_media_buy/route_abc123", "operation_id": "op_abc123" } } ``` `authentication` はデフォルトケースでは省略されます — セラーは自身の `adcp_use: "request-signing"` 鍵で署名します。非推奨の `webhook-signing` 鍵は互換期間中も受け入れられます。レガシー HMAC-SHA256 フォールバックが必要な場合のみ `authentication.credentials` を含めます。 ### A2A A2A では、スキルパラメーターは `message.parts[].data.parameters` に残ります。プッシュ通知設定はトップレベルの `configuration` オブジェクトに入ります: ```json theme={null} { "message": { "parts": [{ "kind": "data", "data": { "skill": "create_media_buy", "parameters": { "packages": [...] } } }] }, "configuration": { "pushNotificationConfig": { "url": "https://you.com/webhooks/adcp/create_media_buy/route_abc123", "operation_id": "op_abc123" } } } ``` ## オペレーション ID と URL テンプレート オペレーション ID により、受信 Webhook を正しいタスク呼び出しに相関できます。パターン: 1. バイヤーがタスク呼び出しごとに一意のオペレーション ID を生成する 2. バイヤーがそれを `push_notification_config.operation_id` としてセラーに引き渡す。Webhook URL の構造はバイヤーの選択であり、**セラーにとって不透明**である 3. セラーがすべての Webhook ペイロードで `operation_id` をそのままエコーする — URL 解析は不要 **規範的なワイヤー契約:** * **バイヤーは(SHOULD)** すべての Webhook 登録についてセラーに `operation_id` を供給し、タスク呼び出しごとに一意の値を生成すべきです(UUID 推奨)。セラーは `operation_id` を省略した Webhook 登録を `INVALID_REQUEST` で拒否してもよい(MAY)。 * **セラーは(MUST)** すべての Webhook ペイロードで、バイヤーが供給した `operation_id` 値を受け取ったとおり正確にエコーしなければなりません。ペイロードフィールドが相関の**唯一の**真実の源です。 * **セラーは(MUST NOT)** `push_notification_config.url` を解析して `operation_id` を導出してはなりません — URL 構造(パステンプレート、クエリパラメーター、不透明トークンなど)はセラーの視点からは実装依存であり、実装をまたいで確実に逆算できません。バイヤーの URL 規約はプロトコルの一部ではありません。 * **レシーバーは(MAY)** URL パスまたはクエリ文字列で HTTP エンドポイントをディスパッチしてもよいが、URL 由来の値をオペレーション相関キーとして使用してはなりません(MUST NOT)。ワイヤーレベルの相関識別子はペイロードフィールドです。 これは、アドテックにおけるすべての比較可能な非同期通知プロトコル(OpenRTB の `nurl`/`burl`、VAST トラッキングピクセル、A2A の `PushNotificationConfig`)が示す先例と一致します: HTTP 呼び出しを発火するエンティティは、相関データのためにレシーバーの URL を決して解析しません。 **URL テンプレートパターン(バイヤー側の規約のみ):** ``` https://you.com/webhooks/{task_type}/{agent_id}/{route_token} ``` 上記のテンプレートは、有用な**バイヤー向けのサーバー側ルーティング補助**です — バイヤーの HTTP サーバーがボディを先に解析せずにパスセグメントでディスパッチできるようにします — が、規範的ではなく、セラーはそれに依存できません。`?route=…`、フラットなパス、または完全に不透明なトークンを好むバイヤーも、セラー側の `operation_id` が SDK の送信側 API を通じて供給される限り、完全にコンフォーマントです。 **例(クライアントライブラリが自動処理):** ```typescript theme={null} import { randomUUID } from 'crypto'; const operationId = randomUUID(); // e.g. "cd51e063-2b79-4a6d-afac-ed7789c3a443" const routeId = randomUUID(); // receiver-local routing token; opaque to the seller const webhookUrl = `https://you.com/adcp/webhook/create_media_buy/${agentId}/${routeId}`; // pass both webhookUrl and operationId in push_notification_config ``` セラーの Webhook ペイロードには `"operation_id": "cd51e063-2b79-4a6d-afac-ed7789c3a443"` が含まれるため、ハンドラーはペイロードフィールドを直接読むことで正しい保留中のオペレーションに相関できます。URL パスは HTTP ハンドラーを選択できますが、プロトコルのオペレーション ID は選択できません。 **セラー SDK 実装**は、送信側 Webhook API の明示的なパラメーターとして `operation_id` を公開します(例: Python `WebhookSender.send_mcp(url=…, operation_id=…)`)。セラーのアプリケーションコードが元のタスクリクエストから Webhook 発火へ値を引き通します。SDK は URL からそれを回復しようとは決してしません。 ### 呼び出し元の `context` オブジェクトのエコー 発信元のリクエストがトップレベルの `context` オブジェクトを運んだ場合、セラーは同じオペレーションのすべての Webhook ペイロードで、`operation_id` と並んでその同じオブジェクトをそのままエコーしなければなりません(MUST)。これは同期および非同期ステータスレスポンスに適用されるのと同じ契約です — [コンテキストとセッション — 規範的なエコー契約](/docs/building/by-layer/L2/context-sessions#normative-echo-contract)を参照してください。エコーは `working`、`input-required`、`completed`、`failed`、`canceled` の配信を通じて引き継がれなければなりません(MUST)。初回レスポンスと後の Webhook の間で `context` を落とすと、まさに最も必要とされる箇所でバイヤー側の相関が壊れます。`context.trace_id` または `context.internal_campaign_id` でルーティングするバイヤーは、すべての配信でのそのままのエコーに依存します。 ## Webhook が発火するとき Webhook は、`push_notification_config` がリクエストにある限り、初回レスポンス後の各ステータス変更ごとに送信されます。 タスクが同期的に完了する場合(初回レスポンスがすでに `completed`、`failed`、`rejected` などの終端状態)、Webhook は送信されません — すでに結果を持っているからです。 初回レスポンスが非終端(`working` または `submitted`)であるオペレーションのみが、後で AdCP タスク Webhook を発行できます。インラインの終端レスポンスに対して、セラーは `task_id` を捏造したりインライン結果を `push_notification_config.url` にリプレイしたりしてタスク Webhook を合成してはなりません(MUST NOT)。同期専用オペレーション、および `push_notification_config` が意味を持たない同期専用オペレーションモードは、代わりにそのフィールドを整形式のランタイムエラーとして拒否してもよい(MAY)。これはセラー対バイヤーのワイヤールールです: バイヤー SDK は同期レスポンスをローカルのコールバック、Promise、ハンドラー呼び出しに正規化してもよい(MAY)が、それらのローカル SDK の便宜は AdCP Webhook ではありません。将来の AdCP バージョンがバイヤー通知の同期完了通知モードを追加する場合、それは明示的でケイパビリティ宣伝されます。3.x のタスク Webhook 契約はそれを定義しません。 **Webhook をトリガーするステータス変更:** | ステータス | 意味 | | ---------------- | --------------------- | | `working` | タスク処理中 — 進捗情報を含むことがある | | `input-required` | 人間の承認または明確化を待機中 | | `completed` | 最終結果が利用可能 | | `failed` | タスクがエラー詳細付きで失敗 | | `canceled` | タスクがキャンセルされた | ## Webhook ペイロード形式 ### MCP ```json theme={null} { "idempotency_key": "whk_01HW9D3H8FZP2N6R8T0V4X6Z9B", "task_id": "task_456", "operation_id": "cd51e063-2b79-4a6d-afac-ed7789c3a443", "task_type": "create_media_buy", "domain": "media-buy", "status": "completed", "timestamp": "2025-01-22T10:30:00Z", "message": "Media buy created successfully", "result": { "media_buy_id": "mb_12345", "packages": [ { "package_id": "pkg_001", "context": { "line_item": "li_ctv_sports" } } ] } } ``` すべての Webhook ペイロードは必須の `idempotency_key` を運びます — 同じイベントのリトライをまたいで安定する、送信者生成の鍵です。これは正準の重複排除フィールドです。下記の [信頼性](#reliability) を参照してください。 ### ペイロードの構造: エンベロープ vs. result Webhook レシーバーは、**ワイヤーエンベロープ**とタスク固有の **result** を区別しなければなりません。完全な MCP Webhook エンベロープは、HTTP POST ボディとして送信される JSON オブジェクトです。デリバリーレポートの内容は `result` の下に存在します。それ自体はトップレベルの POST ボディとしては有効ではありません。 デリバリーレポートの発火では、完全なワイヤーペイロードは次のようになります: ```json theme={null} { "idempotency_key": "whk_20260526_example_000031", "operation_id": "delivery_report_67_2026_04", "task_id": "delivery_report_67_2026_04_000031", "task_type": "media_buy_delivery", "status": "completed", "timestamp": "2026-05-26T09:00:44.582Z", "message": "Scheduled media buy delivery report available", "result": { "notification_type": "scheduled", "sequence_number": 31, "reporting_period": { "start": "2026-05-25T00:00:00Z", "end": "2026-05-25T23:59:00Z" }, "currency": "USD", "media_buy_deliveries": [ { "media_buy_id": "mb_001", "status": "active", "totals": { "impressions": 125000, "spend": 5625.0, "clicks": 250 }, "by_package": [] } ] } } ``` この内側の result オブジェクトは有効なデリバリーレポートの内容ですが、**トップレベルの Webhook POST ボディとしては有効ではありません**: ```json theme={null} { "notification_type": "scheduled", "sequence_number": 31, "reporting_period": { "start": "2026-05-25T00:00:00Z", "end": "2026-05-25T23:59:00Z" }, "currency": "USD", "media_buy_deliveries": [] } ``` トップレベルの `status` は非同期 Webhook ステータス(`completed`、`failed`、`working` など)です。ネストされた `media_buy_deliveries[].status` はメディアバイのライフサイクルまたはレポート状態(`active`、`paused`、`reporting_delayed` など)です。2 つのフィールドを混同しないでください。 `scheduled`、`final`、`delayed`、`adjusted` などのデリバリーレポートデータイベントでは、`notification_id` は設計上不在です。トランスポートイベントは `idempotency_key` で重複排除してください。`aggregated_totals` フィールドは `get_media_buy_delivery` レスポンス専用の API 専用であり、レポート Webhook の result ペイロードで発行してはなりません。 署名とコンテンツダイジェストは、この完全なエンベロープとして送信される正確な生の JSON バイト上で計算されます。検証前に `result` オブジェクトだけを再シリアライズしたり、空白を追加したり、フィールド順を変えたりすると、バイトが変わり署名検証が壊れます。 ### A2A A2A は `Task` オブジェクト(最終状態向け)または `TaskStatusUpdateEvent`(進捗向け)を送信します。最終状態(`completed`、`failed`)では、AdCP result データは `.artifacts[0].parts[]` にあります。中間状態(`working`、`input-required`)では、データは `status.message.parts[]` にあります。 ```json theme={null} { "id": "task_456", "contextId": "ctx_123", "status": { "state": "completed", "timestamp": "2025-01-22T10:30:00Z" }, "artifacts": [{ "artifactId": "result", "parts": [ { "kind": "text", "text": "Media buy created successfully" }, { "kind": "data", "data": { "media_buy_id": "mb_12345", "packages": [ { "package_id": "pkg_001", "context": { "line_item": "li_ctv_sports" } } ] } } ] }] } ``` ### プロトコル比較 | | MCP | A2A | | ---------------- | ---------------------------------- | ------------------------------------------------------------------- | | **Config フィールド** | `push_notification_config`(タスク引数内) | `configuration.pushNotificationConfig`(スキルパラメーターと別) | | **エンベロープ** | `mcp-webhook-payload.json` | ネイティブ `Task` / `TaskStatusUpdateEvent` | | **Result の場所** | `result` フィールド | `.artifacts[0].parts[].data`(最終)/ `status.message.parts[].data`(中間) | | **データスキーマ** | 同一の AdCP スキーマ | 同一の AdCP スキーマ | ### 登録チャネルがエンベロープ形状を決定する Webhook エンベロープの形状は、同期リクエストがどのトランスポートで送られたかではなく、**バイヤーがどの登録メカニズムを使ったか**で決まります: | 登録経路 | 配信されるエンベロープ | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------- | | AdCP `push_notification_config`(タスク引数、MCP/A2A/REST) | [`mcp-webhook-payload.json`](#mcp) | | A2A `TaskPushNotificationConfig`([`CreateTaskPushNotificationConfig`](https://a2a-protocol.org/latest/specification/) RPC、または `SendMessage` 上のインライン `task_push_notification_config`) | A2A 1.0 §4.3.3 に従う A2A ネイティブ `Task` / `TaskStatusUpdateEvent` | 2 つのチャネルは独立しています。バイヤーは同じタスクについて両方を登録し、ステータス変更ごとに両方の Webhook を受け取ってもよい(MAY)。 **なぜこれがモデルであり「インバウンドトランスポートに合わせる」ではないのか。** 各チャネルはそのエンベロープ専用に作られています: AdCP `push_notification_config` は AdCP `mcp-webhook-payload` 形状のための AdCP 層の登録、A2A `TaskPushNotificationConfig` は A2A 自身の `StreamResponse` ラップ配信のための A2A 層の登録です。バイヤーはレシーバーに合うチャネルを選びます — 判別子フィールドは不要で、オーバーライドすべき曖昧さもありません。 **典型例: A2A で同期、AdCP 形状の Webhook。** MCP ネイティブのランタイムからオーケストレーションし、1 つの特定の高スループット同期オペレーションに A2A を使うバイヤーは、その `SendMessage` ボディ内の AdCP タスク引数に `push_notification_config` を入れます。セラーは、同期トランスポートが A2A であることに関わらず、それを AdCP 形状の登録として尊重します。バイヤーのレシーバーは、パイプライン内の他のすべての AdCP Webhook と同じ `mcp-webhook-payload` 形状を受け取ります。 **A2A 形状の Webhook を望む A2A バイヤー**は、A2A のネイティブプッシュ通知メカニズムを通じて登録します。AdCP はそのケースのために何も追加する必要はありません。 ### ステータス別の result データ | ステータス | `result` / `data` の内容 | | ---------------------- | --------------------------------------------- | | `completed` / `failed` | 完全なタスクレスポンス | | `working` | 進捗: `percentage`、`current_step`、`total_steps` | | `input-required` | 理由と検証エラー | | `submitted` | 最小限の確認 | ## 署名検証 すべての AdCP 3.0 Webhook は [RFC 9421 Webhook プロファイル](/docs/building/by-layer/L1/security#webhook-callbacks)の下で署名されます。セラーは、自身の brand.json の `agents[]` エントリで公開した `adcp_use: "request-signing"` 鍵で署名します。非推奨の `webhook-signing` 鍵は互換期間中も受け入れられます。あなたはセラーが公開する JWKS に対して検証します。共有シークレットはワイヤーを渡りません。 **パブリッシャーは 3 つのヘッダーを送ります**(`Content-Type` に加えて): ``` Signature-Input: sig1=("@method" "@target-uri" "@authority" "content-type" "content-digest"); created=;expires=;nonce=; keyid=;alg="ed25519";tag="adcp/webhook-signing/v1" Signature: sig1=:: Content-Digest: sha-256=:: ``` カバーされるコンポーネントは固定です: `@method`、`@target-uri`、`@authority`、`content-type`、`content-digest`。`content-digest` は REQUIRED です — ボディがイベントそのものであり、それをカバーしない署名は重要な攻撃面を保護していません。 **検証**は、14 ステップの[リクエスト検証器チェックリスト](/docs/building/by-layer/L1/security#verifier-checklist-requests)に、3 つの Webhook 置換を加えたものに従います: * エラーコードは `webhook_signature_*` プレフィックスを使う([Webhook エラータクソノミー](/docs/building/by-layer/L1/security#webhook-error-taxonomy)を参照)。 * `tag` は `adcp/webhook-signing/v1` でなければならない(MUST)。 * `keyid` はセラーオペレーターの `brand.json` の `agents[].jwks_uri` を介して解決し、存在する場合はパブリッシャーの `adagents.json` の `signing_keys[]` ピンを適用する(統合からセラーのエージェント URL はすでに持っている)。 **レシーバー実装スケッチ:** ```typescript theme={null} import { createRemoteJWKSet, jwtVerify } from 'jose'; // Use a validated RFC 9421 library (e.g., `http-message-signatures`) pinned to the AdCP profile. app.post('/webhooks/adcp/*', async (req, res) => { try { // 1. Parse Signature-Input / Signature headers and reject on malformed. // 2. Resolve keyid against the seller operator's brand.json JWKS. // 3. Run the AdCP webhook verifier checklist (14 steps). await verifyAdcpWebhookSignature(req, { sellerAgentUrl: req.sellerContext.agentUrl, // known from your integration requiredTag: 'adcp/webhook-signing/v1', allowedAlgs: ['ed25519', 'ecdsa-p256-sha256'], }); } catch (err) { return res.status(401) .setHeader('WWW-Authenticate', `Signature error="${err.code}"`) .end(); } // 4. Dedup by idempotency_key before applying side effects (see Reliability below). processWebhook(req.body); res.status(200).end(); }); ``` :::caution 生のボディと content-digest `Content-Digest` の検証(チェックリストのステップ 11)には、生の HTTP ボディバイトが必要です。JSON パースの前にそれらをキャプチャしてください — いかなる再シリアライズもダイジェストの一致を壊します。 Express では: ```typescript theme={null} app.use(express.json({ verify: (req, _res, buf) => { (req as any).rawBody = buf.toString('utf-8'); }, })); ``` ::: :::note リプレイ保護 `created`/`expires`/`nonce` の sig-params は、5 分の最大有効ウィンドウと `(keyid, nonce)` のリプレイ重複排除を強制します。keyid ごとの上限とメモリ制限ルールは [トランスポートリプレイ重複排除](/docs/building/by-layer/L1/security#transport-replay-dedup)を参照してください。 ::: ### レガシー HMAC-SHA256 フォールバック(非推奨) :::warning 非推奨 — AdCP 4.0 で削除 下記の HMAC-SHA256 スキームは 3.x のみの互換性のための便宜です。新しい統合は `push_notification_config.authentication` を省略し、上記の [9421 Webhook プロファイル](#signature-verification)を使用すべきです(SHOULD)。セラーはレガシースキームのサポートを断ってもよい(MAY)。 ::: バイヤーは `push_notification_config.authentication.credentials` を設定することで HMAC-SHA256 にオプトインできます。設定されている場合、セラーは共有シークレットを使って HMAC-SHA256 で署名し、リプレイ保護のためにタイムスタンプを含めます。 **設定(レガシー):** ```json theme={null} { "authentication": { "schemes": ["HMAC-SHA256"], "credentials": "your_shared_secret_min_32_chars" } } ``` **パブリッシャーは 2 つのヘッダーを送ります(レガシー):** ``` X-ADCP-Signature: sha256= X-ADCP-Timestamp: ``` **署名アルゴリズム(レガシー):** 署名されるメッセージは `{unix_timestamp}.{raw_json_body}` — Unix タイムスタンプ(秒)、ドット、次に HTTP ボディで送信される正確な JSON バイトです。 ``` Signature = sha256= + hex( HMAC-SHA256( secret, "{timestamp}.{rawBody}" ) ) ``` `rawBody` はワイヤー上で送信される正確なバイトで**なければなりません**。JSON ペイロードをシリアライズしてボディを生成する際は、**コンパクトなセパレーター**(`","` と `":"`、周囲の空白なし)を使用してください — これは JavaScript の `JSON.stringify` とほとんどの HTTP クライアントのデフォルトに一致し、レシーバーが `raw_body` として見るものです。ここでのよくあるクロス SDK の失敗は、署名者が空白を挿入する言語デフォルト(例: Python `json.dumps(payload)`)を呼ぶ一方、HTTP クライアントがコンパクトなバイトをワイヤーに書き込むケースです — 署名者はレシーバーが決して見ないバイト上で署名します。バイト等価性のために `json.dumps(payload, separators=(",", ":"))`(または同等物)を使用してください。正準のワイヤー形式と検証器入力の扱いに関する完全なルールは [Webhook セキュリティ — レガシー規範ルール](/docs/building/by-layer/L1/security#legacy-hmac-sha256-fallback-deprecated-removed-in-40)を参照してください。 **パブリッシャー実装(レガシー):** ```typescript theme={null} import { createHmac } from 'crypto'; function signWebhook(rawBody: string, secret: string): { signature: string; timestamp: string } { const timestamp = Math.floor(Date.now() / 1000).toString(); const message = `${timestamp}.${rawBody}`; const hex = createHmac('sha256', secret).update(message).digest('hex'); return { signature: `sha256=${hex}`, timestamp }; } ``` **レシーバー実装(レガシー):** ```typescript theme={null} import { createHmac, timingSafeEqual } from 'crypto'; function verifyWebhook( rawBody: string, signature: string, timestamp: string, secret: string, ): boolean { const ts = parseInt(timestamp, 10); if (isNaN(ts)) return false; const now = Math.floor(Date.now() / 1000); if (Math.abs(now - ts) > 300) return false; const message = `${ts}.${rawBody}`; const expected = `sha256=${createHmac('sha256', secret).update(message).digest('hex')}`; if (signature.length !== expected.length) return false; return timingSafeEqual(Buffer.from(signature), Buffer.from(expected)); } ``` レガシースキームの規範ルールは [Webhook セキュリティ](/docs/building/by-layer/L1/security#legacy-hmac-sha256-fallback-deprecated-removed-in-40)にあります。 ### レガシー Bearer トークン(非推奨) A2A の `authentication.schemes: ["Bearer"]` スキームも互換性のためにサポートされ、AdCP 4.0 で削除されます。Bearer はボディに対する改ざん保護を提供しません。9421 プロファイルは署名者アイデンティティ(JWKS アンカー、ローテーション可能、失効可能)と鍵管理(ワイヤー上に共有シークレットなし)でより強力です。ボディ完全性の保護は、両者ともボディバイトをカバーするためレガシー HMAC スキームと同等です。セラーは変更系コールバックについて Bearer を拒否すべきです(SHOULD)。 ```json theme={null} { "authentication": { "schemes": ["Bearer"], "credentials": "your_bearer_token_min_32_chars" } } ``` ```javascript theme={null} app.post('/webhooks/adcp', (req, res) => { const token = req.headers.authorization?.replace('Bearer ', ''); if (token !== process.env.ADCP_WEBHOOK_TOKEN) return res.status(401).end(); processWebhook(req.body); res.status(200).end(); }); ``` ## 信頼性 Webhook は**少なくとも 1 回の配信**を使用します — 同じイベントを複数回受け取ることがあり、イベントは順不同で到着することがあります。 ### `idempotency_key` による重複排除 すべての Webhook ペイロード — MCP タスクエンベロープ、ガバナンスのリスト変更 Webhook(`collection_list_changed`、`property_list_changed`)、アーティファクトプッシュ Webhook、権利の `revocation-notification` — は必須の `idempotency_key` を運びます。パブリッシャーはこの鍵を個別のイベントごとに 1 回生成し、すべてのリトライで再利用します。レシーバーはそれで重複排除しなければなりません(MUST)。 **送信者の要件:** * 鍵は暗号学的にランダムでなければなりません(MUST、UUID v4 推奨)。連番、タイムスタンプのみ、その他の予測可能な値は非コンフォーマントです: レシーバーは生の値で重複排除するため、予測可能な鍵は攻撃者がレシーバーのキャッシュに事前投入して後の正当なイベントを抑制することを可能にします。 * 鍵は同じイベントのリトライをまたいで安定でなければならず(MUST)、個別のイベントに再利用してはなりません(MUST NOT)。 **レシーバーの要件:** * 重複排除のスコープは `(認証された送信者アイデンティティ, idempotency_key)` です。「認証された送信者アイデンティティ」とは、署名検証によって確立された送信者の暗号学的アイデンティティを意味します — 9421 デフォルトでは、解決された `keyid` → 署名者の `agents[]` エントリ URL、レガシーフォールバックでは、検証された HMAC シークレットまたは Bearer トークンからのクレデンシャルバインディング。アイデンティティをペイロードフィールドから導出してはなりません。異なる送信者からの鍵は独立したキースペースに保たなければならず(MUST)、複数のセラーと統合するレシーバーはそれらを統合してはなりません(MUST NOT)。HMAC→9421 移行中は、レシーバーは同じ論理セラーの両方の送信者アイデンティティ形式を 1 つのキースペースにマップし、スキームをまたぐ重複も重複排除されるようにすべきです(SHOULD)。 * **クロスエンドポイント重複排除(MUST)。** 複数の Webhook エンドポイント(統合ごと、環境ごと、テナントごと、または水平スケールされたフリートのポッドごと)を公開するレシーバーは、ある送信者が到達できるすべてのエンドポイントにわたって `(送信者アイデンティティ, idempotency_key)` キースペースを共有しなければなりません(MUST) — ポッドごとのインメモリキャッシュは非コンフォーマントです。共有ティアがなければ、同じ署名済みイベントが兄弟エンドポイントにリプレイされると 2 回実行されます。`(keyid, nonce)` スコープに関するトランスポート層の対応ルールは [Webhook リプレイ重複排除のサイジング](/docs/building/by-layer/L1/security#webhook-replay-dedup-sizing)を参照してください。 * 重複排除の状態は、プロセス再起動、ポッド置換、リージョンフェイルオーバーを生き延びる耐久ストレージに少なくとも 24 時間永続化しなければなりません(MUST)。パブリッシャーはそのウィンドウを超えてリトライすべきではありません(SHOULD NOT)。レシーバーの TTL 後に到着するリトライは新しいイベントとして再処理されます。インメモリのみのキャッシュ(バッキングティアなしのポッドごとの `Map` または LRU)は非コンフォーマントです — 約 360 秒の署名ノンスウィンドウと 24 時間の冪等性ウィンドウの非対称性が、**変位リプレイウィンドウ**を作ります。そこでは、正当な署名済みリトライ(新しいノンス、同じ `idempotency_key`)が署名検証を通過し、レシーバーがインメモリ状態を落としたためキャッシュエントリを見つけられません。副作用が 2 回実行されます。キャッシュティアが 24 時間を耐久的に守れないレシーバーは、統合するすべての送信者に、より短い実効ウィンドウを文書化しなければなりません(MUST) — 黙って短縮するのが危険なモードです。 * レシーバーは送信者ごとに重複排除キャッシュサイズを制限し、無制限に成長させるのではなく `429 Too Many Requests` を返す(または接続を切る)べきです(SHOULD) — 高ボリュームの新しい鍵を発行する誤動作または敵対的なセラーは、さもなくばストレージ増幅のベクトルになります。 * **重複は `2xx`(通常 `200 OK`)で応答しなければならず(MUST)**、`409 Conflict` ではありません。少なくとも 1 回の送信者は、2xx 以外のレスポンスを「配信失敗」と解釈し、指数バックオフでリトライします。正常に重複排除されたイベントに `4xx` を返すと、正しいレシーバーの挙動がリトライ嵐に変わります。重複はエラーではなく no-op です。 * Webhook レシーバーは、鍵再利用をまたいだペイロード等価性を検証**しません**。送信者が変更されたペイロードで鍵を再利用した場合(送信者のバグ)、レシーバーのキャッシュされた最初のコピーが勝ち、2 つ目は黙って重複排除されます。これはリクエスト側の `IDEMPOTENCY_CONFLICT` の挙動とは異なります — 送信者は個別のイベントごとに新しい鍵を生成することについて単独で責任を負います。 ```javascript theme={null} app.post('/webhooks/adcp', async (req, res) => { const payload = req.body; const { idempotency_key, task_id, status, timestamp, result } = payload; // Scope dedup to the authenticated sender — never trust a payload field for identity. const sender = req.verifiedSenderId; // set by 9421 verifier (keyid → agent URL) or legacy HMAC/Bearer middleware // Dedup: same (sender, idempotency_key) within the replay window → already processed. // Return 200 (not 409) so the sender stops retrying. if (await db.webhookAlreadyProcessed(sender, idempotency_key)) { return res.status(200).end(); } await db.markWebhookProcessed(sender, idempotency_key); // before side effects — fail-closed on crash // Ordering: separately, don't apply a stale status on top of a newer one. // Ordering state is keyed on task_id, not idempotency_key — two distinct events // (different keys) can still arrive out of order. Still a 200: we received it cleanly. const task = await db.getTask(task_id); if (task?.updated_at >= timestamp) { return res.status(200).end(); } await db.updateTask(task_id, { status, updated_at: timestamp, result }); await triggerBusinessLogic(task_id, status); res.status(200).end(); }); ``` **必ずバックアップとしてポーリングを実装してください。** Webhook はネットワークの問題やサーバーダウンで失敗することがあります。Webhook が設定されている場合はより遅いポーリング間隔(例: 30 秒ではなく 2 分ごと)を使い、Webhook で終端ステータスを受け取ったらポーリングを停止します。 ### 発火漏れの診断 バイヤーが Webhook がエンドポイントに届いていないと疑う場合 — ゲートウェイの 5xx、古いシーケンスの重複排除、ドリフトした Webhook URL、作動したサーキットブレーカー下での発火抑制 — [`get_media_buys`](/docs/media-buy/task-reference/get_media_buys#webhook-activity) を `include_webhook_activity: true` で呼び出します。返される各メディアバイは、呼び出し元プリンシパルの最近の発火の `webhook_activity` 配列を運びます。これには `idempotency_key`(ペイロードの重複排除キーと一致 — 自身のエンドポイントログと照合)、`status`(`success` / `failed` / `timeout` / `connection_error` / `pending`)、`http_status_code`、`attempt`、`error_message` が含まれます。スコープは呼び出し元プリンシパル自身の発火です。オペレーターチケットは不要です。 ## ベストプラクティス 1. **必ずバックアップとしてポーリングを実装する** — Webhook は失敗し得る。Webhook が設定されている場合は間隔を減らして(例: 2 分ごと)ポーリングし、終端ステータスを受け取ったら停止する 2. **`idempotency_key` で重複排除する** — すべてのペイロードはリトライをまたいで安定する必須の鍵を運ぶ。処理済みの鍵を少なくとも 24 時間追跡する 3. **重複には 2xx を返す** — 正常に重複排除されたイベントはエラーではなく no-op。2xx 以外を返すと送信者のリトライバックオフをトリガーしリトライ嵐を作る 4. **処理前に署名を検証する** — いかなる副作用の前にも 9421 Webhook 検証器チェックリスト(またはオプトインした場合はレガシー HMAC チェック)を実行する 5. **即座に確認応答する** — セラーのタイムアウトと不要なリトライを避けるため、重い処理の前に `200` を返す 6. **URL 構造に依存しない** — ビジネス相関にはペイロードの `operation_id` を使う。URL パスはエンドポイントの多重分離のみに使ってよい 7. **4.0 での HMAC 削除に備える** — 現在レガシー HMAC フォールバックを使っている場合、3.x の間に 9421 Webhook プロファイルへ移行する ## ペイロード抽出 Webhook レシーバーは形式を検出し AdCP データを抽出する必要があります。バイヤーはトランスポートを設定したため通常は形式を知っていますが、防御的な検出はマルチフォーマットレシーバーに有用です。 ### 形式検出 | シグナル | 形式 | | ----------------------------- | --- | | `status` が文字列、`task_id` が存在 | MCP | | `status` が `.state` を持つオブジェクト | A2A | ### 抽出 **MCP Webhook:** `result` フィールドから直接データを抽出します。 **A2A Webhook:** [A2A レスポンス抽出](/docs/building/by-layer/L0/a2a-response-extraction)アルゴリズムを使用します — 最終状態は `.artifacts[0].parts[]`(最後の DataPart)から、中間状態は `status.message.parts[]`(最初の DataPart)から抽出します。 ```javascript theme={null} function extractAdcpResponseFromWebhook(payload, knownFormat) { const format = knownFormat || detectFormat(payload); if (format === 'mcp') return payload.result ?? null; if (format === 'a2a') return extractAdcpResponseFromA2A(payload); return null; } function detectFormat(payload) { if (payload.status && typeof payload.status === 'object' && !Array.isArray(payload.status) && payload.status.state) return 'a2a'; if (typeof payload.status === 'string' && payload.task_id) return 'mcp'; return null; } ``` ### セキュリティ要件 * **Content-Type 検証**: 送信者は `application/json` を送らなければなりません(MUST)。レシーバーは署名検証の前に他のタイプを拒否しなければなりません(MUST)。 * **ペイロードサイズ制限**: レシーバーは 1MB 制限を強制すべきです(SHOULD)。署名検証の前に拒否します — 大きなペイロード上でダイジェストや HMAC を計算するのは DoS ベクトルです。`413 Payload Too Large` を返します。 * **重複排除**: `idempotency_key` が正準の重複排除フィールドです。署名検証(9421 またはレガシー HMAC)とリプレイ重複排除がトランスポートを保護し、`idempotency_key` がアプリケーション層で重複する副作用から保護します。 * **形式検出**: 自動検出は防御的なフォールバックです。レシーバーはペイロード検査のみに頼るのではなく、トランスポート設定からの既知の形式(`knownFormat` パラメーター)を使用すべきです(SHOULD)。侵害された中間者が、抽出を誤ったパスにルーティングする曖昧なペイロードを作る可能性があります。 ### テストベクター 機械可読のテストベクターは [`/static/test-vectors/webhook-payload-extraction.json`](https://adcontextprotocol.org/test-vectors/webhook-payload-extraction.json) で利用できます。クライアントライブラリは、形式検出と抽出のロジックをこれらのベクターに対して検証すべきです(SHOULD)。 ## レポート Webhook レポート Webhook はタスクステータス Webhook とは別です。アクティブなメディアバイの定期的なパフォーマンスデータを配信し、`push_notification_config` ではなく `create_media_buy` の `reporting_webhook` を通じて設定されます。 `reporting_webhook` の詳細は [Task Reference](/docs/media-buy/task-reference) を参照してください。 ## 永続チャネル契約 タスク Webhook は論理タスクごとに 1 回発火し、タスクが確定すると停止します。**永続 Webhook** — メディアバイ上の `reporting_webhook` と `push_notification_config` — は単一のオペレーションより長く続き、リソースの寿命の間繰り返し発火します。以下の契約は永続チャネルに適用されます。 このセクションは [スナップショットとログ契約](/docs/protocol/snapshot-and-log)のトランスポート側の半分です。読み取り側のルール(スナップショットが権威、リプレイ = 再読み取り)は、そのページを参照してください。 ### 配信セマンティクス * **少なくとも 1 回の配信。** セラーはリトライ下で同じ論理イベントを再発火してもよい(MAY)。レシーバーは `idempotency_key` でトランスポートのリトライを重複排除しなければなりません(MUST)。型付き `notification_id` も運ぶ状態形状イベント([`mcp-webhook-payload.json`](https://adcontextprotocol.org/schemas/v3/core/mcp-webhook-payload.json) と [snapshot-and-log Rule 1](/docs/protocol/snapshot-and-log#1-two-distinct-ids-per-fire-and-per-state) を参照)については、レシーバーは発火を現在のスナップショット状態に相関させるため `notification_id` も追跡しなければなりません(MUST) — 同じ `notification_id` を 2 つの異なる `idempotency_key` 値の下で見ることは、トランスポートのリトライではなく再発行のシグナルです。 * **順序保証なし。** 同じリソース上の 2 つのイベントが数秒以内に順不同で到着してもよい(MAY)。レシーバーは Webhook の順序を正準として扱うのではなく、リソーススナップショットを通じてリコンサイルしなければなりません(MUST)。 * **冪等な適用。** 同じペイロードを 2 回適用しても、結果のレシーバー状態は同一でなければなりません(MUST)。 ### 合体(Coalescence) 状態形状のイベントタイプについて、セラーは同じリソース上の複数のほぼ同時の変更を単一のプッシュに合体させるべきです(SHOULD)。**合体ウィンドウはイベントタイプごとであり、一律の上限ではありません** — レイテンシーに敏感なイベント(不正、ブランドセーフティ)は、アドバイザリと同じウィンドウを待てません。 | イベントタイプ | デフォルト合体ウィンドウ | 備考 | | ---------------------- | ------------- | -------------------------------------------------------------------------- | | `impairment`(一般) | 5 分(超えるべきでない) | リソース状態の障害のデフォルト — オーディエンス停止、クリエイティブ失効など | | `impairment`(レイテンシー敏感) | サブ分 / 合体なし | 不正駆動、ブランドセーフティ駆動、その他バイヤーの応答ウィンドウが短いクラス。セラーはこれらに一般デフォルトを適用してはならない(MUST NOT) | | 将来のアドバイザリイベント | 数時間〜日次 | ノイズ許容度が高い。より大きなウィンドウが適切 | | 将来のディフェクトイベント | 数分〜数時間 | 緊急度は impairment とアドバイザリの間 | セラーは、デフォルト未満のレイテンシーを必要とするレシーバー向けに、`get_agent_capabilities` を通じてより短い合体ウィンドウを宣言してもよい(MAY)。セラーは、レシーバー側で宣言された明示的なバイヤーのオプトインなしに、タイプごとのデフォルトを超えてはなりません(MUST NOT)。デリバリーレポートの発火(`scheduled`、`final`)は独自のケイデンスに従い、この合体ルールの対象外です。 ### リプレイと回復 バイヤーのレシーバーがオフラインで発火を逃した場合、回復は**スナップショットを読む**ことです。すべての永続チャネルに 2 つのパスが存在し、内容は同等です: * `impairment` イベントを逃した → `get_media_buys` を呼んで `impairments[]` を読む(完全な状態回復)。 * デリバリーレポートの発火を逃した → 該当ウィンドウについて、セラーが `reporting_capabilities.windowed_pull_granularities`(#4590)で宣言した粒度に `time_granularity` を設定して `get_media_buy_delivery` を呼ぶ。プルは Webhook が配信したのと同じウィンドウごとのスライスを返す。ウィンドウ粒度をまだ宣言していないセラーは、日付範囲の集計と日次内訳のみを返し、サブ日次の発火を再構築できない。 * その他の状態形状イベントを逃した → 対応する `get_*` タスクを呼ぶ。 AdCP はトランスポート層でイベントリプレイのプリミティブにコミットしません。Webhook 配信可視化面(`get_media_buys` 上の `webhook_activity[]`、[#4278](https://github.com/adcontextprotocol/adcp/issues/4278) で提案)は、**デバッグ**のために保持ウィンドウ内の最近の発火を公開します — バイヤーはこれを使って、セラーが発火したこととレシーバーが返した HTTP ステータスを検証します。それはデータ回復チャネルではありません。それはスナップショットのウィンドウごとのプル(#4590)の役割です。 ### 可変性とローテーション メディアバイ上の `push_notification_config` と `reporting_webhook` は、バイを再作成せずに `update_media_buy` を通じて更新してもよい(MAY)。よくある理由: レシーバー URL のローテーション、期限切れ bearer トークンの置換、署名鍵のスワップ。 セラーは、更新が確認応答された後の次の発火で更新された設定を尊重しなければなりません(MUST)。正式なハンドオフウィンドウはありません — バイヤーは伝播ウィンドウ中に以前の URL に対して少数の発火を受け取ることがあり(MAY)、以前の URL が合体ウィンドウの間静かになるまで両方の URL をライブとして扱うべきです(SHOULD)。 ### 認証更新 永続 Webhook は bearer トークンより長く続きます。bearer 認証(レガシー HMAC プロファイルまたはトークンベース mTLS)を使うレシーバーは、期限切れ前に `update_media_buy` を通じてトークンをローテーションすべきです(SHOULD)。9421 署名プロファイルを使うレシーバーはトークンローテーションを必要としません — 検証はセラーが公開する JWKS に対して行われ、セラーはそれを独立してローテーションします。 セラーの発火がレシーバーから 401 を受け取った場合、セラーはこれを一時的なレシーバー側の設定エラーとして扱うべきです(SHOULD): 標準スケジュールでリトライし、デバッグのため `webhook_activity[]` に失敗を表面化し、Webhook を自動無効化しない。 ### 終了 永続 Webhook はバイの終端ライフサイクル遷移を通じて発火します: * `final` デリバリーレポートは、バイが `completed`、`canceled`、`rejected` に達した後に発火する。 * 保留中の `impairment` イベントは、セラーがキューに持っている場合、終了前に発火する(または合体されて発火する)。 * 最終発火の後、設定された URL に対してそれ以上のイベントは発火しない。セラーは、バイヤーが終了シーケンスを監査できるよう、終了後の保持ウィンドウの間 `webhook_activity[]` を保持してもよい(MAY)。 ## 次のステップ * [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) — ステータス値と遷移 * [Async Operations](/docs/building/by-layer/L3/async-operations) — 長時間実行タスクの処理 * [Error Handling](/docs/building/by-layer/L3/error-handling) — Webhook エラーパターン # 呼び出し元を構築する Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L4/build-a-caller AdCP バイヤーとデマンドサイドアプリケーションのためのクライアント側ガイド。SDK をインストール、エージェントのケイパビリティを発見、呼び出し、非同期レスポンスとエラーを処理、レポートを取り込む。数か月ではなく数週間のハンドラーグルー。 **バイ側** — DSP、プランニングツール、エージェンティッククライアント、またはプラン、購入、レポートのために AdCP エージェントを呼ぶ任意のアプリケーション — を構築しているなら、ここから始めてください。呼び出し元側の L0–L3 は、エージェント側が要求する [3〜4 人月のビルド](/docs/building/cross-cutting/sdk-stack#why-sdks-matter-more-in-adcp-than-in-eg-http) ではなく、数週間のハンドラーグルーです。あなたの言語のフルスタック SDK が L0–L3 を運びます。あなたは呼び出しロジック、レスポンス処理、そしてアプリケーションがデータで何をするかを書きます。 **仕様レベルのリファレンス対ビルド形式ガイド。** このページはビルドを順に案内します。ワイヤーレベルの不変条件 — すべての変更呼び出しに適用されるすべてのルール — は [AdCP エージェントの呼び出し](/docs/protocol/calling-an-agent) にあります。本番に行く前に一度読んでください。ワイヤー形状エラーをデバッグするときはいつでも参照してください。 **ライブエージェントに対して試す。** AAO は `https://test-agent.adcontextprotocol.org` でパブリックテストエージェントを、ドメインごとのエンドポイント — `/sales/mcp`、`/creative/mcp`、`/signals/mcp`、`/governance/mcp` — で実行します。クライアントを一致するエンドポイントに向けると `getAdcpCapabilities()` が認証なしで機能します。実際のセラーに向ける前にインストールを検証するために使ってください。 ## SDK をインストール サーバープリミティブを出荷する同じ SDK が呼び出しクライアントも出荷します。1 つをインストールすれば両方を持ちます。 ```bash theme={null} npm install @adcp/sdk ``` ```typescript theme={null} import { createSingleAgentClient } from '@adcp/sdk'; const client = createSingleAgentClient({ id: 'sales', name: 'Sales agent', agent_uri: 'https://sales.example.com/mcp', protocol: 'mcp', }); ``` マルチエージェントファンアウト(1 つのクライアントが並列で多くのセラーを駆動)には、代わりに `ADCPMultiAgentClient` を使います — 同じ呼び出しサーフェス、エージェント id でインデックス化。 * [NPM Package](https://www.npmjs.com/package/@adcp/sdk) * [GitHub Repository](https://github.com/adcontextprotocol/adcp-client) ```bash theme={null} pip install adcp ``` ```python theme={null} from adcp import ADCPClient, AgentConfig, Protocol client = ADCPClient(AgentConfig( id="sales", agent_uri="https://sales.example.com/mcp", protocol=Protocol.MCP, )) ``` * [PyPI Package](https://pypi.org/project/adcp/) * [GitHub Repository](https://github.com/adcontextprotocol/adcp-client-python) ```bash theme={null} go get github.com/adcontextprotocol/adcp-go/adcp ``` Go 呼び出しサーフェスは活発に開発中です。現在のカバレッジについては [adcp-go README](https://github.com/adcontextprotocol/adcp-go) を参照。 ## 認証する ほとんどのエージェントは、`get_adcp_capabilities` を超えた何かに応答する前に認証情報を要求します。SDK は構築時に認証を受け入れます: ```typescript theme={null} const client = createSingleAgentClient({ id: 'sales', name: 'Sales agent', agent_uri: 'https://sales.example.com/mcp', protocol: 'mcp', auth_token: process.env.ADCP_API_KEY, }); ``` ```typescript theme={null} const client = createSingleAgentClient({ id: 'sales', name: 'Sales agent', agent_uri: 'https://sales.example.com/mcp', protocol: 'mcp', signing: { keyId: 'your-key-id', privateKey: /* PEM or KMS handle */ }, }); ``` 最初の呼び出しが 401 / `AUTH_REQUIRED` を返す場合、認証情報がエージェントに到達していません — リクエストペイロードではなくコンストラクターオプションを確認してください。完全な認証情報モデルについては [L1 セキュリティ実装プロファイル](/docs/building/by-layer/L1/security) を参照。 ## 最初の呼び出し: エージェントを発見する 手動でツールを呼ぶ前に、エージェントに何をサポートするか尋ねます。 ```typescript theme={null} const capabilities = await client.getAdcpCapabilities(); // → { supported_protocols: [...], adcp_versions: [...], features: {...} } ``` `get_adcp_capabilities` はエージェントのプロトコルカバレッジ、AdCP バージョン範囲、機能フラグを返します。呼び出しをゲートするのに使ってください — `media_buy` が `supported_protocols` にないなら、このエージェントに対して `create_media_buy` を呼ばないでください。完全なレスポンス形状については [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) リファレンスを参照。 ディスカバリーチェーンの残り(エージェントカード、`tools/list`、`get_schema`)については、[エージェントの呼び出しのディスカバリーチェーンセクション](/docs/protocol/calling-an-agent#discovery-chain) を参照。 ## 呼び出しをする クライアントの型付きメソッドは AdCP ツールに対応します。SDK は送信前にリクエストをバンドルされたスキーマに対して検証し、レスポンスを型付き値にパースします。 ```typescript theme={null} const products = await client.getProducts({ brief: 'Video campaign for pet owners, 18–34, US, $50K monthly', }); const buy = await client.createMediaBuy({ idempotency_key: crypto.randomUUID(), account: { brand: { domain: 'acme.com' }, operator: 'sales.example' }, packages: [/* … */], }); ``` 最初の呼び出しで知っておく価値のある 2 つのこと: * **論理操作ごとに新しい `idempotency_key` を生成。** リトライで同じキー → サーバーは同じレスポンスをリプレイ。失敗後の新しいキーは重複したバイを作る。[冪等性ルール](/docs/protocol/calling-an-agent#idempotency-replay-vs-new-operation) を参照。 * **`account` は判別された `oneOf`。** 1 つのバリアント(`sync_accounts` / `list_accounts` からの `{account_id}`、または自然キーとしての `{brand, operator}` — `brand.domain` はバイヤーのブランドドメイン、`operator` はセラーエージェントのデプロイホスト名または brand.json 識別子)を選び、その必須フィールドのみを送る。それらをマージすると両方で失敗。[`account` は `oneOf`](/docs/protocol/calling-an-agent#account-is-oneof--pick-exactly-one-variant) を参照。 ## 3 つのレスポンス形状を扱う すべての変更ツールは 3 つの形状の 1 つを返します。それらを明示的に扱ってください。 ```typescript theme={null} const response = await client.createMediaBuy({/* … */}); if ('errors' in response) { // Error: don't retry without fixing — read response.adcp_error.issues[] // for correctable failures (validation, oneOf, etc.) } else if (response.status === 'submitted') { // Async: the work is queued, NOT done. The completion payload arrives // later — either via webhook (preferred) or by polling the AdCP task. } else { // Sync success: response carries the completion payload directly. // (e.g., response.media_buy_id, response.packages) } ``` SDK は非同期完了のため webhook に導きます。構築時に `webhookUrlTemplate` とステータス変更ハンドラーを設定します。SDK はインバウンド webhook をセラーの JWKS に対して検証し、同期レスポンスが運ぶのと同じ `result` 形状であなたのハンドラーを発火します — 下の [Webhook を受信する](#receive-webhooks) を参照。 webhook を使う代わりにポーリングしなければならない場合(例: ワンショットスクリプト内)、AdCP ポーリングサーフェスを呼びます: セラーがアドバタイズするとき `get_task_status`、そうでなければ 3.x のレガシー AdCP `tasks/get`。[エージェントの呼び出しの非同期レスポンスセクション](/docs/protocol/calling-an-agent#async-responses-status-submitted-means-queued) にワイヤーコントラクトがあります。 ## エラーから回復する レスポンスに `adcp_error` を見たら、`issues[]` を読み `recovery` に基づいて行動します: ```typescript theme={null} const { code, recovery, issues } = response.adcp_error; switch (recovery) { case 'correctable': // Buyer-side fix. Patch the JSON pointers from issues[], resend with // the SAME idempotency_key (fresh key = new operation). break; case 'transient': // Retry with the SAME idempotency_key. Same key on retry replays the // cached response if the work landed. break; case 'terminal': // Human action required. Don't retry. break; } ``` `issues[]` は実行可能な部分です: 各エントリは JSON Pointer(`pointer`)、Ajv キーワード(`required`、`oneOf`、`enum` など)、そして — `oneOf` 失敗については — 各バリアントの必須フィールドをリストする `variants[]` 配列を持ちます。完全なエンベロープとリカバリーセマンティクスについては [エラーリカバリーセクション](/docs/protocol/calling-an-agent#error-recovery--read-issues) を参照。 ## Webhook を受信する 非同期タスクについては、ポーリングまたは webhook 登録のいずれかができます。webhook は `include_result: true` を伴う AdCP タスクポーリングと同じ `result` ペイロードを配信します。SDK は、セラーの brand.json 経由で鍵を解決し、リプレイウィンドウを強制し、[webhook エラータクソノミー](/docs/building/by-layer/L1/security#webhook-error-taxonomy) の構造化エラーをサーフェスする RFC 9421 webhook 検証者(`@adcp/sdk/signing/server` の `createWebhookVerifier`)を出荷します。 マルチエージェントクライアントを `webhookUrlTemplate` とステータス変更ハンドラーで配線する([@adcp/sdk README](https://github.com/adcontextprotocol/adcp-client#readme) 準拠)と、インバウンド webhook が検証され自動的にハンドラーにディスパッチされます — あなたの HTTP ルートはリクエストをクライアントに渡す 1 行です。 配線方法にかかわらずエンドポイントが満たさなければならないワイヤーレベルの要件(カバードコンポーネント、`content-digest` 強制、重複排除の規律)については、[L3 — Webhooks](/docs/building/by-layer/L3/webhooks#signature-verification) を参照。 ## レポートを取り込む レポートは読み取り専用で、他のすべてと同じ呼び出し/レスポンス形状に従います。所有するバイの配信を引き、ウィンドウ付きケイデンスで反復します: ```typescript theme={null} const delivery = await client.getMediaBuyDelivery({ media_buy_ids: ['mb_123', 'mb_456'], window: { start: '2026-05-01T00:00:00Z', end: '2026-05-02T00:00:00Z' }, }); ``` パフォーマンス webhook をサポートするセラーについては、上に示した同じ webhook レシーバー経由でデルタを受け取ります。そうでなければレポートニーズに応じた任意のケイデンスでポーリングします。呼び出し元 L4 — 最適化、ペーシングアラート、アトリビューション結合、ダッシュボード — は型付き `delivery` オブジェクトの上のあなたのアプリケーションコードです。 ## 書かずに済んだもの 呼び出し元側の L0–L3 が数週間のハンドラーグルーなのは、SDK が既に次を出荷したからです: * **L0** — 型付きリクエストビルダー、レスポンスパーサー、バンドルされたスキーマに対するスキーマ検証。 * **L1** — アウトバウンド RFC 9421 署名(呼び出しごと)、インバウンド webhook 検証、鍵ローテーション。 * **L2** — エージェントレジストリルックアップ、エージェントカード公開、認証情報合成。 * **L3** — 非同期タスクポーリング、webhook レシーバー、冪等性キー生成ヘルパー、エラーリカバリー分類。 [SDK スタックリファレンス](/docs/building/cross-cutting/sdk-stack) が各層を分解します。[サーバー対クライアント比較表](/docs/building/cross-cutting/sdk-stack#server-vs-client-at-each-layer) がコスト非対称性の並列ビューです。 ## 次は * **[エージェントの呼び出し](/docs/protocol/calling-an-agent)** — 正準ワイヤーコントラクトリファレンス。本番に行く前に一度読む。 * **[Schemas](/docs/building/by-layer/L0/schemas)** — スキーマバンドル、型生成、バージョンピン留め。 * **[Webhooks](/docs/building/by-layer/L3/webhooks)** — プッシュ通知、署名、リトライ、信頼性パターン。 * **[Error handling](/docs/building/by-layer/L3/error-handling)** — エラーカテゴリー、コード、リカバリー分類。 * **[エージェントを検証する](/docs/building/verification/validate-your-agent)** — 呼び出し元側のワイヤー適合性のストーリーボードも存在する。呼び出しが機能したら実行する。 プロトコルごとのタスクリファレンス: * [メディアバイタスクリファレンス](/docs/media-buy/task-reference/index) * [クリエイティブタスクリファレンス](/docs/creative/task-reference) * [シグナルタスクリファレンス](/docs/signals/tasks/get_signals) * [ブランドプロトコルタスク](/docs/brand-protocol) # エージェントを構築する Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L4/build-an-agent AdCP SDK スキルファイルを使って、コーディングエージェントで数分でストーリーボード準拠のエージェントを生成する。 AdCP エージェントを構築する最速の方法は、コーディングエージェント(Claude Code、Codex、Cursor、Windsurf)を AdCP SDK のスキルファイルに向けることです。各スキルは 2〜8 分でプロトコル準拠、ストーリーボード検証済みのエージェントを生成します。 **エンジニアリングチームのないパブリッシャー?** プロトコルコンプライアンスはローンチの一部です — プロダクト管理、アドサーバーへのアクティベーション、ホスティングは別の労力です。3 つのパス(マネージドプラットフォームと提携、事前構築されたエージェントをセルフホスト、独自に構築)については **[エージェントの運用](/docs/building/operating/operating-an-agent)** を参照。 ## SDK をインストール 各 SDK はプロトコルコンプライアンス — スキーマ検証、エラー形式、バージョンネゴシエーション、レスポンスビルダー — を扱うため、あなたはプロトコル配管ではなくビジネスロジックを書きます。 ```bash theme={null} npm install @adcp/sdk ``` JS/TS SDK は、型付きツール登録、レスポンスビルダー、組み込みのストーリーボードランナーを提供します。本番のほとんどのエージェントがこの SDK を使います。 * [NPM Package](https://www.npmjs.com/package/@adcp/sdk) * [GitHub Repository](https://github.com/adcontextprotocol/adcp-client) ```bash theme={null} pip install adcp ``` Python SDK は同じケイパビリティを提供します — `ADCPHandler` をサブクラス化し、ツールを実装し、すべての返り値にレスポンスビルダーを使います: ```python theme={null} from adcp.server import ADCPHandler, serve from adcp.server.responses import capabilities_response class MySeller(ADCPHandler): async def get_adcp_capabilities(self, params, context=None): return capabilities_response(["media_buy"]) # ... implement tools, use response builders for every return serve(MySeller(), name="my-seller") ``` レスポンスビルダー(`adcp.server.responses`)はスキーマコンプライアンスを扱うため、生の JSON を構築しません。すべてのツールの返りにそれらを使ってください。 * [PyPI Package](https://pypi.org/project/adcp/) * [GitHub Repository](https://github.com/adcontextprotocol/adcp-client-python) ```bash theme={null} go get github.com/adcontextprotocol/adcp-go/adcp ``` Go SDK は、型付きツール登録、レスポンスビルダー、コンプライアンステストコントローラーを提供します。型は正準 AdCP スキーマから生成されます。 | Component | Import | | ---------- | ----------------------------------------------------------- | | ツール登録 | `adcp.AddTool(server, name, desc, handler)` | | HTTP サーバー | `adcp.Serve(createAgent)` | | レスポンスビルダー | `adcp.ProductsResponse(data)`、`adcp.MediaBuyResponse(data)` | | テストコントローラー | `adcp.RegisterTestController(server, store)` | 完全な例については [Go SDK README](https://github.com/adcontextprotocol/adcp-go) を参照。 レスポンスビルダー(`adcp.ProductsResponse()`、`adcp.MediaBuyResponse()` など)はスキーマコンプライアンスを扱うため、生の JSON ではなく型付き struct を返します。 * [GitHub Repository](https://github.com/adcontextprotocol/adcp-go) **あなたの言語の SDK を使ってください。** 3 つすべての SDK — JS/TS、Python、Go — がスキーマ検証、エラー形式、プロトコルネゴシエーションを扱います。プロトコルコンプライアンスのために異なる言語を使う必要はありません。 ## スキルを選ぶ 各 SDK は、コーディングエージェントに特定のエージェントタイプの構築を案内するスキルを出荷します。SDK 横断の一般的なスキル: * `build-seller-agent` — インベントリを販売するパブリッシャー、SSP、メディアネットワーク * `build-signals-agent` — オーディエンスセグメントを提供する CDP またはデータプロバイダー * `build-creative-agent` — クリエイティブをレンダリングするアドサーバーまたは CMP * `build-generative-seller-agent` — ブリーフから広告を生成する AI アドネットワーク * `build-retail-media-agent` — カタログ駆動のクリエイティブを持つリテールメディアネットワーク 例えば、JS/TS セラースキルは [`adcp-client/skills/build-seller-agent/SKILL.md`](https://github.com/adcontextprotocol/adcp-client/tree/main/skills/build-seller-agent) にあります。各 SDK がそのスタック固有の実装ガイダンスを含むため、スキルカバレッジと命名は言語ごとに異なります。あなたの言語のディレクトリを参照: * **JS/TS** — [adcp-client/skills](https://github.com/adcontextprotocol/adcp-client/tree/main/skills) * **Python** — [adcp-client-python/skills](https://github.com/adcontextprotocol/adcp-client-python/tree/main/skills) * **Go** — [adcp-go/skills](https://github.com/adcontextprotocol/adcp-go/tree/main/skills) ### どのドメインと専門分野を主張するか? 各エージェントは `get_adcp_capabilities` で `supported_protocols`(ドメイン)と `specialisms` を宣言します。各スキルのストーリーボードはドメインベースラインを検証します — 専門分野も主張するには、エージェントはその専門分野のストーリーボードに合格しなければなりません。スキルから専門分野へのマッピング: | Skill | Typical `supported_protocols` | Typical `specialisms` (pick one or combine) | | ------------------------------- | ----------------------------- | ---------------------------------------------- | | `build-seller-agent` | `["media_buy", "creative"]` | `sales-guaranteed`、`sales-non-guaranteed` | | `build-generative-seller-agent` | `["media_buy", "creative"]` | `creative-generative` + `sales-non-guaranteed` | | `build-retail-media-agent` | `["media_buy", "creative"]` | `sales-catalog-driven` | | `build-signals-agent` | `["signals"]` | `signal-owned`、`signal-marketplace` | | `build-creative-agent` | `["creative"]` | `creative-ad-server`、`creative-template` | **セールス専門分野の選択:** 完全な決定木については Compliance Catalog の [セールス専門分野の選択](/docs/building/verification/compliance-catalog#choosing-a-sales-specialism) を参照。クイックリファレンス: * **`sales-guaranteed`** — IO 承認、固定価格。RFP/プロポーザルフローをサポートするなら `media_buy.supports_proposals: true` を設定。直接購入のみなら `false`(または省略)。 * **`sales-non-guaranteed`** — オークション / PMP。 * **`sales-broadcast-tv`**、**`sales-catalog-driven`**、**`sales-social`** — チャネル固有。決定木を参照。 複数主張できます。完全なタクソノミーと専門分野ごとのストーリーボードについては [Compliance Catalog](/docs/building/verification/compliance-catalog) を参照。 **ブランド権利** エージェント(タレント、音楽、ストックメディアのライセンス)を構築している? 今日スキルはありません — [Brand Protocol ドキュメント](/docs/brand-protocol) を参照し、`brand` ドメインの下で `brand-rights` を主張してください。 ストーリーボードとステータス(stable、preview、deprecated)を持つすべてのドメインと専門分野については [Compliance Catalog](/docs/building/verification/compliance-catalog) を参照。 ストーリーボード合格は **[AAO Verified (Spec)](/docs/building/verification/aao-verified)** 修飾子を獲得します — テストモードエンドポイントでシードされたテストデータに対して検証。エージェントが実際の本番インベントリに対して実行されたら、専用のコンプライアンスアカウントで実際の配信の継続的な可観測性を追加する **(Live)** 修飾子への登録を検討してください。エージェントは (Spec)、(Live)、または両方を保持できます。AdCP を本番インフラとして扱うエンタープライズバイヤーは (Live) でフィルターします。 ## エージェントを構築する コーディングエージェントをあなたのエージェントタイプのスキルファイルに向けます。Claude Code で: ``` Fetch https://raw.githubusercontent.com/adcontextprotocol/adcp-client/main/skills/build-seller-agent/SKILL.md, then build a seller agent for a premium sports news publisher with guaranteed CTV and OLV inventory. ``` ``` Fetch https://raw.githubusercontent.com/adcontextprotocol/adcp-client-python/main/skills/build-seller-agent/SKILL.md, then build a seller agent for a premium sports news publisher with guaranteed CTV and OLV inventory. ``` あなたのエージェントタイプの `adcp-client-python` スキルに向けてください。正確なスキルがまだない場合、最も近いマッチについて [adcp-client-python/skills](https://github.com/adcontextprotocol/adcp-client-python/tree/main/skills) を参照。 ``` Fetch https://raw.githubusercontent.com/adcontextprotocol/adcp-go/main/skills/build-seller-agent/SKILL.md, then build a seller agent for a premium sports publisher. ``` Cursor または Windsurf では、スキルファイルをダウンロードしプロンプトのコンテキストとして含めます。各スキルはコーディングエージェントを次を通じて案内します: 1. ビジネスモデルの決定(何を販売、どう価格設定、承認ワークフロー) 2. 正しいスキーマでのツール登録 3. ストーリーボード検証を通過するレスポンス形状 4. エラー処理とエッジケース ## ストーリーボードで検証する ストーリーボードランナーは、エージェントがどの言語で書かれているかにかかわらず Node.js を必要とします。 エージェントが実行されたら、一致するストーリーボードに対して検証します: ```bash theme={null} # JS/TS agent npx tsx agent.ts & npx @adcp/sdk@latest storyboard run http://localhost:3001/mcp media_buy_seller --json # Python agent python agent.py & npx @adcp/sdk@latest storyboard run http://localhost:3001/mcp media_buy_seller --json # Go agent go run main.go & npx @adcp/sdk@latest storyboard run http://localhost:3001/mcp media_buy_seller --json ``` ストーリーボードはすべての必須ツール呼び出しを実行しレスポンス形状を検証します。ストーリーボードランナーはデフォルトでサンドボックスモードを使います — あなたのエージェントはすべてのアカウント参照で `sandbox: true` を受け取り、実際のプラットフォーム呼び出しなしにシミュレートされたデータを返すべきです。合格する実行はエージェントがプロトコル準拠であることを意味します。 ``` media_buy_seller (9 steps) ✓ get_adcp_capabilities ✓ sync_accounts ✓ get_products ✓ create_media_buy ✓ list_creative_formats ✓ sync_creatives ✓ list_creatives ✓ get_media_buy_delivery ✓ provide_performance_feedback 9/9 passed ``` **プロトコル準拠 ≠ 本番準備完了。** 合格する実行はエージェントが AdCP を正しく話すことを意味します。ローンチには各ツール呼び出しの背後にビジネスインフラが必要です — プロダクトと価格、アドサーバーへのアクティベーション、オーダー管理、ホスティング、`adagents.json` 経由のディスカバリー登録。完全なリストと提携・セルフホスト・構築のいずれかについては **[エージェントの運用](/docs/building/operating/operating-an-agent)** を参照。 各スキルは異なるビジネスモデル用のバリアントストーリーボードを含みます — non-guaranteed、承認付き guaranteed、proposal モードなど。すべての利用可能なストーリーボードを見るには `npx @adcp/sdk@latest storyboard list` を実行。 完全なテストワークフロー — 失敗ステップのデバッグ、コンプライアンスチェックの実行、Addie を通じたインタラクティブな検証 — については **[エージェントを検証する](/docs/building/verification/validate-your-agent)** を参照。エージェントが **上流プラットフォームをラップ** する(DSP、SSP、リテールデータ、クリエイティブサーバー、シグナルマーケットプレイス)場合、ストーリーボードだけでは捕まえないファサードバグをサーフェスする事前ステージングゲートについては **[モック上流フィクスチャでアダプターエージェントを検証する](/docs/building/verification/validate-with-mock-fixtures)** を参照。 ## 追加リソース JS/TS SDK は、人間とコーディングエージェントの両方のために設計されたドキュメントを含みます: | Resource | JS/TS location | Purpose | | -------- | ------------------------------------------------------ | -------------------------------- | | プロトコル仕様 | `node_modules/@adcp/sdk/docs/llms.txt` | 1 ファイルの完全なプロトコル — ツール、型、エラーコード、例 | | サーバーガイド | `node_modules/@adcp/sdk/docs/guides/BUILD-AN-AGENT.md` | サーバー側実装パターン | Python と Go の同等物は各 SDK の GitHub リポジトリにあります。[adcp-client-python](https://github.com/adcontextprotocol/adcp-client-python) と [adcp-go](https://github.com/adcontextprotocol/adcp-go) を参照。 ## 次は * **[エージェントを検証する](/docs/building/verification/validate-your-agent)** — ストーリーボード、コンプライアンスチェック、build-validate-fix ループ * **[エージェントの運用](/docs/building/operating/operating-an-agent)** — プロトコル層の背後にあるもの、提携・セルフホスト・構築のいずれか * **[SDK を選ぶ](/docs/building/by-layer/L4/choose-your-sdk)** — スキーマアクセス、CLI ツール、SDK パッケージエクスポート * **[MCP 統合ガイド](/docs/building/by-layer/L0/mcp-guide)** — トランスポート、セッション、認証の詳細 * **[タスクライフサイクル](/docs/building/by-layer/L3/task-lifecycle)** — ステータス値、遷移、ポーリング * **[エラー処理](/docs/building/by-layer/L3/error-handling)** — エラーカテゴリー、コード、リカバリー # SDK を選ぶ Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L4/choose-your-sdk あなたの言語の AdCP SDK を選ぶ。カバレッジマトリクス、インストールコマンド、各 SDK が L0–L3 で出荷するもの — そのため L4 ビジネスロジックに集中できる。 AdCP SDK は L0–L3(ワイヤー、署名、認証、プロトコルセマンティクス)を吸収するため、あなたは L4 ビジネスロジックを書きます。既にいる言語の SDK を選んでください — 3 つの SDK すべてが同じワイヤー適合性のラインをターゲットにします。 この背後にある層モデル — 各層が何を含み、各層の SDK が何を提供すべきか — については [SDK スタックリファレンス](/docs/building/cross-cutting/sdk-stack) を参照。 ## カバレッジマトリクス 各層で「出荷済み」が何を意味するかは [L0–L3 チェックリスト](/docs/building/cross-cutting/sdk-stack#what-an-sdk-at-each-layer-should-provide) です。 *最終更新: 2026-05-03。* | SDK | Production GA | Beta / dev | L0 | L1 | L2 | L3 | | -------------------- | ------------- | ---------- | :-: | :-: | :-: | :-: | | **`@adcp/sdk`** (TS) | `6.9.0` | — | ✅ | ✅ | ✅ | ✅ | | **`adcp`** (Python) | `3.x` | `4.x` | ✅ | ⚠️ | ⚠️ | ⚠️ | | **`adcp-go`** | — | `v1.x` | ⚠️ | ❌ | ❌ | ❌ | 凡例: ✅ 出荷済み · ⚠️ 部分的 / 進行中 · ❌ まだ未カバー。**Production GA** は今日ピン留めすべきラインです。**Beta / dev** は次のメジャーで進行中のものです。 **`@adcp/sdk` 6.x は完全な L0–L3 を出荷します**(AdCP 3.0)— 採用者は L4 のみを書きます。5.x ラインはセキュリティのみのサポートで新しいビルドに推奨されません。6.x に切り替えてください。**`adcp`(Python)3.x は本番ライン** で、完全な L0 型カバレッジ付き。4.x 書き直し(PyPI でベータ)が L1–L3 のギャップを閉じます — アウトバウンド RFC 9421 署名、ブランド解決ヘルパー、webhook 発出。4.0 GA カットについては [adcp-client-python リリース](https://github.com/adcontextprotocol/adcp-client-python/releases) を追跡してください。**`adcp-go`** は活発に開発中で、型 + トランスポートが最初に着地します。リリースごとに何がスコープ内かは [adcp-go README](https://github.com/adcontextprotocol/adcp-go) を参照。 3.0 以前の呼び出し元は、アップグレード前に [3.0 移行ガイド](/docs/reference/migration/index) を進めるべきです。公開されたすべてのプロトコルバージョンの一目でのステータスについては [バージョンと互換性](/docs/reference/versions) を参照。サポートウィンドウの調達グレードのコンテキストについては、[v2 サンセットタイムライン](/docs/reference/v2-sunset) と [バージョニングとガバナンス](/docs/reference/versioning) を参照。 **Python と TypeScript は、ファーストクラスのサポート言語として完全な L0–L4 カバレッジを運びます。** **Go** は同じ方向に動いています。**他の言語** は公式ロードマップにありません。コミュニティ保守のポートは歓迎されます — [Builders Working Group](/docs/community/working-group) と [Slack コミュニティ](https://join.slack.com/t/agenticads/shared_invite/zt-3c5sxvdjk-x0rVmLB3OFHVUp~WutVWZg) を参照。 ## JavaScript / TypeScript [![npm version](https://img.shields.io/npm/v/@adcp/sdk)](https://www.npmjs.com/package/@adcp/sdk) ```bash theme={null} npm install @adcp/sdk ``` ```javascript theme={null} import { createSingleAgentClient } from '@adcp/sdk'; const client = createSingleAgentClient({ id: 'sales', name: 'Sales agent', agent_uri: 'https://sales.example.com/mcp', protocol: 'mcp', }); const products = await client.getProducts({ brief: 'Video campaign for pet owners', }); ``` **リソース:** * [NPM Package](https://www.npmjs.com/package/@adcp/sdk) * [GitHub Repository](https://github.com/adcontextprotocol/adcp-client) **パッケージエクスポート:** * `@adcp/sdk` — メインエントリ: 呼び出し元(`createSingleAgentClient`、`ADCPMultiAgentClient`)と共有型 * `@adcp/sdk/server` — エージェント側サーバープリミティブ(`createAdcpServerFromPlatform`、`createAdcpServer`、決定プラットフォームインターフェース) * `@adcp/sdk/server/legacy/v5` — レガシー v5 ハンドラーバッグエントリ、移行中盤のコードベース向けにまだサポート * `@adcp/sdk/signing` — RFC 9421 署名プリミティブ * `@adcp/sdk/signing/server` — webhook + リクエスト検証者(`createWebhookVerifier`、`verifyRequestSignature`、`createExpressVerifier`) * `@adcp/sdk/signing/client` — アウトバウンド署名(`signRequest`、`signWebhook`、`createSigningFetch`) * `@adcp/sdk/testing` — バイヤー側ストーリーボードランナー(`runStoryboard`、`comply`、`testAgent`)とセラー側コントローラースキャフォールド(`createComplyController` — [Get Test-Ready](/docs/building/verification/get-test-ready) を参照) * `@adcp/sdk/conformance` — 適合性ハーネスのためのアサーション + ストーリーボードヘルパー * `@adcp/sdk/schemas` — バンドルされた AdCP JSON Schema * `@adcp/sdk/types` — TypeScript 型定義 * `@adcp/sdk/types/v2-5` — クロスバージョン呼び出し元のための v2.5 型共存インポート ## Python [![PyPI version](https://img.shields.io/pypi/v/adcp)](https://pypi.org/project/adcp/) ```bash theme={null} pip install adcp ``` ```python theme={null} from adcp import ADCPClient, AgentConfig, Protocol, GetProductsRequest client = ADCPClient(AgentConfig( id='sales', agent_uri='https://sales.example.com/mcp', protocol=Protocol.MCP, )) result = await client.get_products( GetProductsRequest(brief='Video campaign for pet owners'), ) ``` **リソース:** * [PyPI Package](https://pypi.org/project/adcp/) * [GitHub Repository](https://github.com/adcontextprotocol/adcp-client-python) ## Go ```bash theme={null} go get github.com/adcontextprotocol/adcp-go/adcp ``` Go SDK は、型付きツール登録、レスポンスビルダー、コンプライアンステストコントローラーを提供します。型は正準 AdCP スキーマから生成されます。 | Component | Import | | ---------- | ------------------------------------------------------------------------------------------------------------ | | ツール登録 | `adcp.AddTool(server, name, desc, handler)` | | HTTP サーバー | `adcp.Serve(createAgent)` | | レスポンスビルダー | `adcp.ProductsResponse(data)`、`adcp.MediaBuyResponse(data)` など | | テストコントローラー | `adcp.RegisterTestController(server, store)` | | Skills | [github.com/adcontextprotocol/adcp-go/skills](https://github.com/adcontextprotocol/adcp-go/tree/main/skills) | 完全な API リファレンスについては [Go SDK README](https://github.com/adcontextprotocol/adcp-go) を参照。 **リソース:** * [GitHub Repository](https://github.com/adcontextprotocol/adcp-go) ## CLI ツール JavaScript と Python の SDK は、テストと開発のためのコマンドラインツールを含みます。 両 SDK は同じ位置形状を共有します: `adcp [tool] [payload]`。最初の位置引数はエイリアス、ビルトイン(`test-mcp`、`test-a2a`)、または URL — プロトコルは自動検出されます。再入力を避けるため `--save-auth` でエイリアスを保存します。 ### JavaScript CLI ```bash theme={null} npx @adcp/sdk@latest --help npx @adcp/sdk@latest --save-auth my-agent https://sales.example.com/mcp npx @adcp/sdk@latest my-agent get_products '{"brief":"CTV campaign"}' # or against the built-in public test agent: npx @adcp/sdk@latest test-mcp get_products '{"brief":"CTV campaign"}' ``` CLI はストーリーボード(`adcp storyboard run`)、適合性グレーディング(`adcp grade`)、レジストリ診断も駆動します。完全なサーフェスについては `--help` を参照。 ### Python CLI ```bash theme={null} uvx adcp --help uvx adcp --save-auth my-agent https://sales.example.com/mcp uvx adcp my-agent get_products '{"brief":"CTV campaign"}' ``` ## 次は * **[Build an agent](/docs/building/by-layer/L4/build-an-agent)** — サーバー側 L4 パス。スキルファイル + コーディングエージェント。 * **[Build a caller](/docs/building/by-layer/L4/build-a-caller)** — クライアント側 L4 パス。インストール、呼び出し、レスポンス処理、レポート取り込み。 * **[Schemas](/docs/building/by-layer/L0/schemas)** — スキーマバンドル、型生成、バージョンピン留め。 * **[Migrate from hand-rolled](/docs/building/by-layer/L4/migrate-from-hand-rolled)** — SDK が多くをカバーする前に構築された AdCP エージェントを既に実行している? 一度に 1 層ずつスワップする。 # L4 — ビジネスロジック Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L4/index AdCP スタックのビジネスロジック層。SDK があなたに委ねるもの: エージェント側のインベントリと価格、呼び出し元側のプランニングと購買。採用者の約 95% のデフォルトの出発層。 L4 は、あなたのエージェントや呼び出し元をあなたのものにするものです。AdCP SDK は L0–L3(ワイヤー、署名、認証、プロトコルセマンティクス)を出荷し、L4 をあなたに委ねます。ほとんどの採用者にとってこれが正しい出発層です — あなたのチームの付加価値はここに存在し、下のプロトコルの再実装にはありません。 各下位層が何を含み、各層の SDK が何を提供しなければならないかについては [SDK スタックリファレンス](/docs/building/cross-cutting/sdk-stack) を参照。 ## この層のページ * **[Choose your SDK](/docs/building/by-layer/L4/choose-your-sdk)** — 言語カバレッジマトリクス、インストールコマンド、パッケージエクスポート、CLI ツール。SDK を選んでいないならここから始める。 * **[Build an agent](/docs/building/by-layer/L4/build-an-agent)** — サーバー側。コーディングエージェントをスキルファイルに向ける。ストーリーボード準拠の AdCP エージェントを得る。 * **[Build a caller](/docs/building/by-layer/L4/build-a-caller)** — クライアント側。SDK をインストール、エージェントを発見、呼び出し、レスポンス処理、レポート取り込み。 * **[Migrate from hand-rolled](/docs/building/by-layer/L4/migrate-from-hand-rolled)** — SDK が成熟する前に構築された AdCP エージェントを既に実行している? 一度に 1 層ずつスワップする。 # 手書きエージェントから移行する Source: https://adcp-docs-ja.pier1.co.jp/docs/building/by-layer/L4/migrate-from-hand-rolled 本番で動作する AdCP エージェントを持つ採用者のための段階的移行パス。棚卸しステップ、最低リスク優先のスワップ順、衝突モード(冪等性、アカウントモード、webhook 署名、ステートマシンドリフト)、ステップごとのロールバックプレイブック、適合性を通過する中間状態、移行しない場合。 このガイドは、フラグデイの書き直しなしに公式 SDK に移行したい **本番で動作する AdCP エージェント** を持つ採用者向けです。あなたのエージェントは実トラフィックを提供します。現在のスタックを構築しそれを守るエンジニアがいます。数週間の凍結を許容できません。パスは段階的です — 一度に 1 層をスワップし、各ステップ後に出荷し、進むにつれ再認証します。 グリーンフィールドなら、間違ったドキュメントにいます — [エージェントを構築する](/docs/building/by-layer/L4/build-an-agent) を参照。そもそも移行するか決めている段階なら、building 概要の [手書き再評価チェック](/docs/building#two-checks-before-you-start) を参照。 ## 0. 今日所有するものを棚卸しする 何かをスワップする前に、手書きスタックが [AdCP スタック](/docs/building/cross-cutting/sdk-stack) の各層で何を提供するかを書き留めます。そのドキュメントの L0–L3 チェックリストをルーブリックとして使ってください。各行をマーク: *出荷済み* / *部分的* / *まだ*。 これが操作の順序を決めます。最低リスクのスワップは通常、今日 **最も少ない** カバレッジを持つ層です。なぜなら、調整する既存の動作が最も少ないからです。 ## 1. コードを変える前に、まず仕様適合性に到達する 最も重要な単一のステップ: 1. エージェントに **mock-mode アカウント** を立てる。(live/sandbox/mock の区別がまだない場合、下の [Account-mode mismatch](#account-mode-mismatch) を参照 — まず境界にフラグを追加する。) 2. mock-mode トラフィックをリファレンスモックサーバーにルーティングする。 3. エージェントに対して AdCP ストーリーボードを実行する([Conformance](/docs/building/verification/conformance) と [エージェントを検証する](/docs/building/verification/validate-your-agent) を参照)。 4. pass/fail レポートを読む。 失敗リストがあなたの移行バックログで、順序付けられています。「SDK が価値を追加すると思う」を「これが失敗するストーリーボードで、それぞれがどの L3 コンポーネントを指すか」に変換します。このステップなしでは、測定されたギャップの代わりにセールストークに基づいて SDK を買っています。 思ったより準拠していることを発見するかもしれません(その場合移行は予想より小さい)か、より少ない(その場合 SDK 採用の論拠が強まる)。どちらの結果も有用です。 ## 2. 操作の順序(最低リスク優先) 推奨スワップ順: 1. **適合性テストサーフェス**([`comply_test_controller`](/docs/building/by-layer/L3/comply-test-controller))。純粋に加算的 — mock-mode トラフィックが今や SDK を通る。ライブトラフィックは触れられない。その場で仕様適合性認証を獲得。 2. **エラーコードカタログ**。エラーエンベロープ構築を SDK のエラービルダーで置き換える。リカバリー分類とコード優先度(例: `INVALID_STATE` より `NOT_CANCELLABLE`)が無料で来る。[Error handling](/docs/building/by-layer/L3/error-handling) を参照。 3. **冪等性キャッシュ**。最もリスクの高いスワップ — [Two idempotency caches in series](#two-idempotency-caches-in-series) を参照。仕様コントラクト: [Idempotency](/docs/building/by-layer/L1/security#idempotency)。 4. **非同期タスクストア + ディスパッチャー**。SDK の `task_id` + 終端アーティファクトコントラクトを採用。しばしばワーカーキューに触れる。[Task lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照。 5. **ステートマシン**、一度に 1 リソース。最も保守時間を費やす場所なら MediaBuy を最初に([ライフサイクルリファレンス](/docs/media-buy/media-buys/lifecycle))。各後にライフサイクルストーリーボードを再実行。 6. **Webhook 発出**(署名済み、リトライ済み、冪等)。1–5 と独立。並列化できる。[Webhooks](/docs/building/by-layer/L3/webhooks) を参照。 7. **RFC 9421 署名 + 検証**。上のすべてと独立。並列化できる。[Security implementation](/docs/building/by-layer/L1/security) を参照。 8. **認証 / アカウントストア**。最後。手書きの L2 はおそらく簡単には動かないビジネス決定をエンコードしている。 任意のステップで止められます。L2 なしで L3(ステップ 1–6)を採用するのは完全に有効なエンドポイントです — あなたの認証層はそれがすることを続け、SDK がプロトコルセマンティクスを引き継ぎます。[What you can leave hand-rolled](#5-what-you-can-leave-hand-rolled) を参照。 ## 3. 注意すべき衝突モード これらは、来るのが見えないと段階的移行を痛みにする「2 つのスタックが互いに戦う」失敗モードです。 ### Two idempotency caches in series 既存のキャッシュは境界でリクエストをフィールドし、SDK のキャッシュはプロトコル境界でリクエストをフィールドします。症状: どのキャッシュが最初にヒットしたかに応じて同じ `idempotency_key` が異なるエンベロープを返す。クロスペイロード再利用が一方で検出され他方で検出されない。 **解決。** 1 つを選び、他を退役させる。通常あなたのを退役 — SDK のは `IDEMPOTENCY_CONFLICT` の *no-payload-echo* 不変条件(盗まれた鍵の read-oracle 脅威)と、仕様が義務付けるクロスペイロード衝突検出を強制します。ストレージバックエンド(Redis、Postgres)を保持する必要があるなら、SDK をフォークする代わりにカスタムバックエンドとして SDK のキャッシュコントラクトをそれに向けます。 ### Account-mode mismatch SDK は `live` / `sandbox` / `mock` アカウントを区別します([Sandbox](/docs/media-buy/advanced-topics/sandbox) を参照)。手書きスタックが区別を欠く場合、mock-mode ストーリーボードがライブハンドラーにディスパッチする可能性があります。症状: ストーリーボードが本番状態を変異させる。適合性認証がディスパッチを拒否する。 **解決。** SDK の適合性コントローラーを採用する前に、境界にアカウントモードフラグを追加します。`comply_test_controller` は sandbox または mock でない任意のアカウントに対して実行するのを拒否します — その拒否はバグではなく機能です。 ### Webhook signature ownership 両スタックがアウトバウンド webhook に署名しようとすると、受信者は 2 つの `Signature` ヘッダーを見ます(または一方が勝ち他方がプロキシによって黙って上書きされる)。どちらにせよ、署名は検証されません。 **解決。** 境界で 1 つの署名者を選ぶ。通常 SDK の、なぜなら公開鍵レジストリに対して鍵ローテーションを追跡し RFC 9421 正準化を正しく扱うからです。KMS 裏付けの鍵素材を保持し、それを使うよう SDK の署名プロバイダー抽象を設定します。 ### State machine drift 手書きのステートマシンはおそらく SDK が拒否するエッジ(例: `active` をスキップする直接 `pending_creatives → completed`、または `NOT_CANCELLABLE` 対 `INVALID_STATE` 優先度を区別しない `active → canceled`)を持ちます。症状: 成功を期待した場所でライフサイクルストーリーボードが `INVALID_STATE` で失敗する。 **解決。** ステートマシンをスワップする *前に* エージェントに対してライフサイクルストーリーボードを実行します。エッジセットを仕様に調整 — 明白なバグを修正し、曖昧さについて仕様 issue を提出。次に SDK のステートマシンをスワップイン。それはあなたが手で収束させたものを強制します。 ### Webhook delivery transport キュー/ワーカースタックが今日 webhook を配信する場合、あなたのを退役させずに SDK のエミッターを配線すると二重配信します。症状: 受信者がわずかに異なる時刻に同じペイロードで重複する冪等性キーを見る。 **解決。** SDK はエンベロープを構築します。どう出荷するかはあなた次第です。組み込みの HTTP 配信を実行する代わりに既存のトランスポートに引き渡すよう SDK を設定 — それがシーム。 ### Schema validation collisions インバウンドペイロードを独自のスキーマバンドルに対して検証し、SDK がその境界で再び検証すると、重複作業(安価)か矛盾する判定(実際のバグ — あなたのバンドルが公開スキーマからドリフトした)のいずれかを得ます。 **解決。** SDK が入った後、ローカル検証器を退役させます。両方が実行される間、任意の不一致を SDK が間違っているのではなくあなたのバンドルが古いものとして扱います。 ## 4. 適合性を通過する中間状態 各ステップ後、mock-mode ストーリーボードを再実行し再認証できます。適合性を主張するために移行を終える必要はありません — SDK の適合性スイートが強制する任意の切り取りラインでストーリーボードを通過するだけです。 | After step | What you have | Conformance status | | ---------- | ------------------------- | ------------------------------------------------ | | 1 | 適合性コントローラー配線済み。エージェント変更なし | **仕様適合性**(mock-mode ストーリーボードが変更されていない L3 に対して実行) | | 2 | + SDK エラーエンベロープ | 同じ。より良いリカバリーセマンティクス | | 3 | + SDK 冪等性 | 同じ。クロスペイロード再利用でより厳格なセキュリティ | | 4 | + SDK 非同期タスクコントラクト | 同じ。一様なタスクライフサイクル | | 5 | + SDK ステートマシン | 同じ。遷移検証はもはやあなたの問題でない | | 6 | + SDK webhook エンベロープ | 同じ。署名済み、リトライ済み、重複排除キー付き | | 7 | + SDK 署名 | **ライブ適合性**(そのストーリーボードセットが出荷されたとき) | | 8 | + SDK アカウントストア | 完全な L4-on-SDK | 各ステップ後に出荷します。本番トラフィックは維持されます。 ### ステップごとのロールバック スワップ順の各ステップは約 5 分以内で可逆であるべきです。一般的なパターン: 各スワップは、削除ではなく **手書きコンポーネントと SDK のものの間のフィーチャーフラグ付きスイッチ** です。アカウントごとのフラグの背後でスワップを出荷し、観測し、次にデフォルトを反転します。ロールバックは反対方向の同じフラグです。 スワップごとに計画すべきこと: | Step | Revert mechanism | What state may have leaked | First thing to verify on rollback | | ----------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------- | | 1. 適合性コントローラー | mock-mode アカウントで SDK ルートを無効化 | なし — mock-mode は設計上 sandbox のみ | モックストーリーボードが手書きの L3 に対してまだ通過 | | 2. エラーエンベロープ | エラービルダーに戻す | スワップウィンドウのアウトバウンドエンベロープが SDK 形状のエラーコードを運ぶかも。古いコードでキーする下流コンシューマーがそれらをログしたかも | エラー監視ダッシュボードが再び正しいエンベロープ形状を読んでいる | | 3. 冪等性キャッシュ | あなたのキャッシュに戻す | **スワップウィンドウ中に両キャッシュがトラフィックを見た。** 同じ `idempotency_key` が今や両ストアに異なるエンベロープで存在するかも。ロールバック後、あなたのキャッシュが権威的。revert で SDK のキャッシュをフラッシュ | dual-cache ウィンドウからの `IDEMPOTENCY_CONFLICT` ストームなし | | 4. 非同期タスクコントラクト | あなたのタスクストアに戻す | SDK のコントラクト下で始まった未完了タスクが、ワーカーが期待するのと異なる終端アーティファクト形状を持つかも | revert が着地する前にスワップウィンドウ中に作成されたタスクをドレインまたはアボート | | 5. ステートマシン | あなたのステートマシンに戻す | SDK が、あなたのマシンが受け入れた遷移を拒否したかも(またはその逆)。影響を受けるリソースは、あなたのコードが進める方法を知らない状態にある | ワンショット再照合クエリを実行: 「両マシンで合法な状態のリソースはあるか? 一方でのみ合法な状態のものは?」 | | 6. Webhook エンベロープ | あなたのエミッターに戻す | 受信者がスワップウィンドウ中に SDK 形状の webhook を得た。既に ack したかも。ロールバックで再発出しない | 重複排除テーブルが古い/新しい `idempotency_key` 形状の両方を受け入れる(移行的) | | 7. RFC 9421 署名 | あなたの署名者に戻す | 受信者がウィンドウ中に SDK 署名の webhook/レスポンスを得た。その鍵レジストリはあなたの古い `keyid` をまだ解決しなければならない | あなたの古い `keyid` がまだ JWKS に公開されている | | 8. アカウントストア | あなたのストアに戻す | SDK のストア下でなされたアカウント解決決定が、あなたのストアと異なるテナントにトラフィックをルーティングしたかも | 完全に revert する前にスワップウィンドウ中に解決されたアカウントで再照合を実行 | ### 午前 2 時の本番障害 スワップ N が午前 2 時に本番で失敗した場合、オンコールレシピ: 1. 影響を受けるアカウント(または影響範囲を分離できないならグローバルに)について、**アカウントごとのフラグを手書きコンポーネントに戻す**。これが出血を止めるのに必要な唯一のステップ。 2. そのステップの上の **漏洩行を確認する**。ほとんどのステップはどこかに残余状態を残す — 朝のデバッグがコールドスタートしないようそれが何かをメモする。 3. **インシデント中に適合性スイートを再実行しない。** それは mock-mode に対して実行される。本番障害は異なるシグナル。 4. **一般的な SDK ではなくスワップステップに対してインシデントを提出する。** 移行ガイドのスワップ順が調査の単位。SDK のカバレッジマトリクスはその下流。 **不可逆なスワップを計画しない。** ステップが flag-and-flip パターンに適合しない(例: 破壊的スキーマ移行)場合、スワップ順の一部としてではなく、別個の名前付きプロジェクトとして行います。 ## 5. What you can leave hand-rolled SDK は仕様が意見を持つ場所で意見を持ち、そうでない場所でプラグイン可能です。既存のインフラを諦める必要はありません: * **署名プロバイダー。** KMS 統合を保持。SDK はカスタム署名者を受け入れる。 * **アカウントストア。** マルチテナントルーティングを保持。SDK のアカウントストアインターフェースがシーム。 * **冪等性バックエンド。** Redis / Postgres を保持。SDK のキャッシュコントラクトはプラグイン可能。 * **Webhook 配信トランスポート。** キューを保持。SDK はエンベロープを構築する。どう出荷するかはあなた次第。 * **スキーマ検証ライブラリ。** 望むなら検証器を保持。SDK はその境界で独自のを使い、あなたのではない。 手書きスタックがこれらに良い答えを持つなら、フォークではなく **設定** としてスワップインします。 ## 6. 移行中のバージョニング ジャグリングする 2 つのバージョン軸: * **バイヤーの仕様バージョン。** 移行は、バイヤーバージョンでハンドラーをフォークするのをやめるため呼び出しごとの `adcpVersion` ピン留めを追加する絶好の瞬間。[Version Adaptation](/docs/building/cross-cutting/version-adaptation) を参照。 * **SDK バージョン。** 最終状態としてレガシーインポートパスに移行しない — それを *通じて* 移行する。レガシーサブパスは、残りが古いものに留まる間、新しいエントリポイントで一度に 1 つの専門分野を採用できるよう存在する。同じプロジェクトのグリーンフィールドコードは新しいフレームワークを直接使う。 ### 実践例: 2 バイヤー、スワップ中盤 ステップ 3(冪等性)にいて、バイヤー A は AdCP 2.5、バイヤー B は AdCP 3.0。切り替えたアカウントについて SDK のコントローラー、エラーエンベロープ、冪等性キャッシュを採用済み。バイヤー A は SDK への飛行中、バイヤー B は SDK でグリーンフィールド。 インバウンド側: 各ピアの仕様バージョンを事前に識別(エージェントレジストリ、エージェントカード、または存在すれば `adcp_version` フィールドから)、エージェント / 呼び出しごとに `adcpVersion` をピン留め、SDK にワイヤー形状を適応させます。`@adcp/sdk` の例: ```ts theme={null} import { ADCPMultiAgentClient } from '@adcp/sdk'; const buyerA = new ADCPMultiAgentClient([{ id: 'buyer-a', agent_uri: 'https://buyer-a.example.com/mcp/', protocol: 'mcp', auth_token: process.env.BUYER_A_TOKEN, adcpVersion: 'v2.5', // ← per-agent pin, no fork in handlers }]); const buyerB = new ADCPMultiAgentClient([{ id: 'buyer-b', agent_uri: 'https://buyer-b.example.com/a2a', protocol: 'a2a', auth_token: process.env.BUYER_B_TOKEN, adcpVersion: '3.0', // ← canonical / current }]); ``` アウトバウンド(サーバー)側、*あなたが* 何を受け入れるかを宣言: ```ts theme={null} import { createAdcpServer } from '@adcp/sdk/server'; createAdcpServer({ capabilities: { major_versions: [3], supported_versions: ['3.0'], }, // …handlers, all on the canonical 3.0 shape; // 2.5 callers are translated by client-side adapters before they reach you. }); ``` ステップ 3 中盤で各バイヤーからの 1 呼び出しがどう見えるか: | | Buyer A (2.5 wire) | Buyer B (3.0 wire) | | -------------- | -------------------------- | ---------------------- | | **インバウンド形状** | 2.5 `create_media_buy` | 3.0 `create_media_buy` | | **アダプターがすること** | 2.5 → 3.0 形状を変換 | パススルー | | **ハンドラーが見るもの** | 3.0 型付きオブジェクト | 3.0 型付きオブジェクト(同じ) | | **冪等性キャッシュ** | SDK の(ステップ 3 にいる) | SDK の | | **エラーエンベロープ** | SDK の、アウトバウンドで 2.5 に変換して戻す | SDK の | | **アウトバウンド形状** | 2.5(出る途中でアダプターが変換) | 3.0 | 1 つのハンドラーコードベース。2 つのワイヤーバージョン。両バイヤーが期待するエンベロープ形状を見る。ステップ 3 で `adcpVersion` ピン留めを採用するのは安価 — バージョン作業のほとんどはアダプターモジュールにあり、SDK が既に出荷しています。 フォールバック(ステップ 3 を手書きキャッシュにロールバック)しても、バイヤー A はまだ SDK の変換アダプターを通ります — バージョン機構と冪等性機構は独立です。ロールバックでハンドラーコードを再フォークしません。 ## 7. 移行 *しない* とき エージェントが少数の名前付きバイヤーに凍結されたワイヤーサーフェスを提供し、エンジニアがプロトコル保守にほぼゼロの時間を費やすなら、移行 ROI は低いです。妥当な保留: * AdCP 2.5 にいて、どのバイヤーも 3.x を望まず、彼らが望んだら非推奨化する意志がある。 * (ステップ 1 からの)適合性ギャップが、SDK を採用せずにその場で修正するのに十分小さい。 * すべての層をエンドツーエンドで所有するハードな規制または運用上の理由がある。 それらのケースでは、とにかくステップ 1 を行う — 仕様適合性認証のため mock-mode をリファレンスモックサーバー経由でルーティング — し、AdCP 4.0 でまたはバイヤーミックスが動くとき移行問題を再訪します。 移行は、**保守負荷が実際で成長している** 採用者向けです。コストクレーム([一から L0–L3 に約 3〜4 人月](/docs/building/cross-cutting/sdk-stack#why-sdks-matter-more-in-adcp-than-in-eg-http))は、段階的に採用することで *買い戻している* ものです — しかしその保守負荷が実際に存在する場合のみ。 ## 関連項目 * [AdCP スタック](/docs/building/cross-cutting/sdk-stack) — 層アーキテクチャリファレンス * [どこから始めるか](/docs/building) — 決定ページ * [Version Adaptation](/docs/building/cross-cutting/version-adaptation) — 3 メカニズムリファレンス * [Conformance](/docs/building/verification/conformance) — ストーリーボードスイートがエージェントをどうグレードするか * [エージェントを構築する](/docs/building/by-layer/L4/build-an-agent) — グリーンフィールドパス。L4-on-SDK の最終状態がどう見えるかのリファレンスとして有用 # AdCP と OpenRTB Source: https://adcp-docs-ja.pier1.co.jp/docs/building/concepts/adcp-vs-openrtb AdCP vs OpenRTB: 相違点と協調方法。AdCP はエージェントのワークフローを、OpenRTB はインプレッション時の判断を処理し、Trusted Match Protocol がクロスパブリッシャーのフリークエンシーキャップなどのユースケースで両者を接続します。 AdCP と OpenRTB は広告スタックの異なる層で動作する補完的な標準です。競合するものではありません — プラットフォームは両方を実装できます(多くの場合そうします)。 最も重要な接続点の 1 つが**クロスパブリッシャーのフリークエンシーキャップ**です。AdCP は、OpenRTB がプログラマティックバイイングを可能にするのと同じ方法でこれを可能にします。すなわち、広告サーバーが配信する前に、各インプレッションをバイヤーが制御するリアルタイムの判断層に公開することによってです。AdCP では、その統合ポイントが **[Trusted Match Protocol (TMP)](/docs/trusted-match)** です。 ## 各標準が行うこと | | OpenRTB | AdCP | | ----------- | -------------------- | ---------------------------- | | **層** | インプレッションレベルのトランザクション | エージェントレベルのワークフロー | | **中核操作** | リアルタイム入札リクエスト/レスポンス | タスクベースのキャンペーン管理 | | **参加者** | DSP と SSP | AI エージェントと広告プラットフォーム | | **タイミング** | リアルタイム(ミリ秒) | 非同期(秒から日) | | **スコープ** | 単一インプレッションオークション | エンドツーエンドのキャンペーンライフサイクル | | **管理者** | IAB Tech Lab | AgenticAdvertising.org | | **成熟度** | 本番(v2.6) | 本番(v3.0) | | **トランスポート** | HTTP POST | MCP(ツール呼び出し)または A2A(エージェント間) | ## 重複する部分 両標準はメディアバイイングに関わりますが、粒度が異なります: * **OpenRTB** は個々のインプレッション決定を処理します: 「このインプレッションに入札すべきか、いくらで?」 * **AdCP** はキャンペーンレベルの決定を処理します: 「どんなインベントリが利用可能か? この予算とターゲティングでこのキャンペーンを実行します。」 1つの AdCP `create_media_buy` タスクはキャンペーンのライフタイムにわたって何千もの OpenRTB 入札リクエストにつながることがあります。 ## 異なる部分 **スコープ。** OpenRTB はオークションに焦点を当てています — 入札リクエスト、入札レスポンス、勝利通知、請求イベント。AdCP はキャンペーンの完全なライフサイクルをカバーします: プロダクト発見、クリエイティブ管理、オーディエンス活性化、キャンペーン実行、デリバリーレポート。AdCP はリアルタイム入札とは独立しても動作します。直販パブリッシャー、スポンサードコンテンツ事業、放送セラー、コマースメディアネットワークは、OpenRTB 統合なしに AdCP を実装できます。 **コミュニケーションモデル。** OpenRTB は同期 HTTP を使用します: 入札リクエストが到着し、入札者は数百ミリ秒以内に応答しなければなりません。AdCP は非同期です: エージェントが `create_media_buy` タスクを送信し、プラットフォームが独自のタイムラインで処理して、ステータス更新を返します。 **参加者。** OpenRTB はデマンドサイドプラットフォーム(DSP)をサプライサイドプラットフォーム(SSP)に自動オークションで接続します。AdCP は AI エージェントをあらゆる広告プラットフォームに接続します — DSP と SSP を含みますがそれに限りません。 **データモデル。** OpenRTB はインプレッションオブジェクト、入札オブジェクト、ディールオブジェクトを定義します。AdCP はメディアプロダクト、メディアバイ、クリエイティブフォーマット、オーディエンスシグナル、ブランドガバナンスルールを定義します。 ## 協調する方法 典型的な統合では、異なる層で両標準を使用します: 1. **バイヤーエージェント**が AdCP を使用してパブリッシャーのプラットフォームで利用可能なプロダクトを発見します(`get_products`) 2. エージェントが AdCP を通じて予算、ターゲティング、スケジュールを含むキャンペーンを作成します(`create_media_buy`) 3. インプレッション時に、パブリッシャーは **TMP Context Match**(ページコンテンツ、利用可能なパッケージ)と **Identity Match**(不透明なユーザートークン、パッケージ ID)を TMP Router に送信します 4. TMP はクロスパブリッシャーの露出を評価し、オファーと適格性の判断を返します — パブリッシャーはそれらをローカルで結合します 5. バイヤーエージェントが AdCP を通じてデリバリーを確認してキャンペーン全体のパフォーマンスを監視します(`get_media_buy_delivery`) このモデルでは、AdCP が戦略層(何を購入するか、いくら使うか、誰をターゲットにするか)を処理し、TMP がリアルタイムの実行層(どのパッケージをどのインプレッションで活性化するか)を処理し、OpenRTB が該当する場合の戦術的なオークション層(どの特定のインプレッションを勝ち取るか)を処理します。 ## TMP: リアルタイムの橋渡し [Trusted Match Protocol (TMP)](/docs/trusted-match) は、AdCP がインプレッション時の判断に到達する方法です。AdCP 自体をオークションプロトコルに変えることなく、クロスパブリッシャーのデータが配信判断に影響を与えられるよう、各インプレッション機会をバイヤーにリアルタイムで見せます。 TMP は構造的に分離された 2 つのオペレーションを定義します: ```mermaid theme={null} flowchart LR buyer["**Buyer Agent**
Creates media buy in AdCP"] pub["**Publisher**
Impression opportunity"] ctx["**Context Match**
Page content + packages
(no user identity)"] id["**Identity Match**
User token + package IDs
(no page context)"] join["**Publisher Join**
Intersect offers × eligibility"] decision["**Activate or suppress**"] buyer --> pub pub --> ctx pub --> id ctx --> join id --> join join --> decision ``` クロスパブリッシャーのフリークエンシーキャップの場合、これは次を意味します: 1. AdCP がキャンペーン、予算、パッケージを定義します 2. インプレッション時に、パブリッシャーは Context Match リクエスト(このコンテンツにどのパッケージがマッチするか?)と Identity Match リクエスト(このユーザーはこれらのパッケージに適格か?)を送信します 3. バイヤーの Identity Match エージェントは、TMP Router に接続されたすべてのパブリッシャーにわたる露出履歴を確認します — フリークエンシーキャップ、オーディエンスメンバーシップ、購入履歴がここで評価されます 4. パブリッシャーは 2 つのレスポンスをローカルで結合します。コンテキストにマッチし*かつ*アイデンティティ適格性を通過したパッケージが活性化され、その他はすべて抑制されます バイヤーがコンテキストとアイデンティティを同時に見ることはありません。クロスパブリッシャーのフリークエンシーキャップは、バイヤーがパブリッシャー横断で共有露出ストアを維持する Identity Match パスを通じて強制されます。 ## エコシステムの他の標準 AdCP と OpenRTB は他のいくつかの標準と共存します: | 標準 | 目的 | 管理者 | | -------------------------------- | ----------------------------------- | ------------ | | **MCP**(Model Context Protocol) | AI ツール呼び出し — AI モデルが外部ツールを呼び出す方法 | Anthropic | | **A2A**(Agent-to-Agent Protocol) | マルチエージェントコラボレーション — 自律エージェントが通信する方法 | Google | | **VAST** / **VPAID** | ビデオ広告サービングとインタラクティブビデオ | IAB Tech Lab | | **ads.txt** / **sellers.json** | サプライチェーン透明性と認可セラー検証 | IAB Tech Lab | | **Open Measurement SDK** | ビューアビリティとアテンション測定 | IAB Tech Lab | AdCP は MCP と A2A をトランスポート層として使用します。IAB コンテンツタクソノミーとオーディエンスセグメント標準は該当する場合に参照されます。 ## よくある質問 いいえ。それぞれ異なる目的を持っています。OpenRTB はリアルタイムインプレッションオークションを処理します。AdCP はキャンペーンレベルのエージェントワークフローを処理します。プラットフォームは両方を実装できます。 いいえ。AdCP は独立して機能します。リアルタイム入札を使用しないプラットフォーム(例えば、ダイレクトセールドパブリッシャーやコマースメディアネットワーク)は、OpenRTB 統合なしで AdCP を実装できます。 はい。バイヤーエージェントが AdCP を通じてメディアバイを作成すると、セルサイドプラットフォームはオーダーを履行するために任意の内部メカニズムを使用できます — OpenRTB オークション、ダイレクトインサーションオーダー、プライベートマーケットプレイスディール、またはクロスパブリッシャーのフリークエンシーキャップのような用途向けの TMP を介したリアルタイム活性化を含みます。 いいえ。AgenticAdvertising.org は独立したメンバー組織です。IAB Tech Lab の子会社、ワーキンググループ、または関連組織ではありません。しかし AdCP は IAB 標準との互換性を持つよう設計されています。 # AI エージェントが広告スペックをプラットフォーム間でやりとりする方法 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/concepts/how-agents-communicate AI 広告エージェントが AdCP の標準化されたタスクスキーマと MCP トランスポートを使用して、プラットフォーム間でインベントリを発見し、キャンペーンデータを交換し、購入を実行する方法。 AI エージェントが複数のプラットフォームにわたるキャンペーンを管理するとき、それぞれで同じことをする必要があります: 利用可能なインベントリを見つけ、購入を送信し、クリエイティブを提供し、デリバリーを確認します。課題は、すべてのプラットフォームがこれらの操作を異なる方法で説明していることです。 AdCP はこれを解決するために標準タスクのセットを定義します — それぞれが固定されたリクエストスキーマとレスポンススキーマを持つ — エージェントはどのプラットフォームと話しているかに関わらずこれを使用します。 ## コミュニケーションの問題 3つのプラットフォームにわたるキャンペーンを管理するバイヤーエージェントを考えてみます: | 操作 | プラットフォーム A | プラットフォーム B | プラットフォーム C | | ----------- | --------------------- | ------------------------ | ------------------ | | インベントリを見つける | `GET /api/products` | `POST /inventory/search` | `GET /catalogue` | | 購入を実行する | `POST /api/campaigns` | `PUT /orders/new` | `POST /media-buys` | | デリバリーを確認する | `GET /api/reports` | `POST /analytics/query` | `GET /stats/{id}` | 標準なしでは、エージェントは各プラットフォームにカスタムコードが必要です — 異なるエンドポイント、異なるフィールド名、異なるレスポンスフォーマット。これにより、エージェントが扱えるプラットフォームの数が制限されます。 AdCP では、エージェントはどこでも同じタスクを使用します: | 操作 | AdCP タスク | | ----------- | ------------------------ | | インベントリを見つける | `get_products` | | 購入を実行する | `create_media_buy` | | デリバリーを確認する | `get_media_buy_delivery` | スキーマはプラットフォーム間で同一です。異なるのはトランスポート接続のみです。 ## 2つのトランスポートプロトコル AdCP タスクは統合タイプに応じて2つのプロトコルで転送されます: ### MCP(Model Context Protocol) MCP は AI アシスタントが外部ツールを呼び出す方法です。AdCP MCP サーバーは Claude、Cursor、または任意の MCP 互換クライアントが呼び出せるツールとしてタスクを公開します。 ``` AI アシスタント → MCP クライアント → AdCP MCP サーバー → プラットフォーム ``` エージェントはツールとして `get_products` を呼び出します。MCP サーバーはリクエストをプラットフォームの内部 API に変換して標準化されたレスポンスを返します。 **最適な用途:** AI アシスタントがメディアバイヤーのプラットフォーム操作を支援するヒューマンインザループワークフロー。 ### A2A(Agent-to-Agent Protocol) A2A は自律エージェントが互いに通信する方法です。バイヤーエージェントがセラーエージェントに構造化されたメッセージを送信し、セラーエージェントがそれを処理して結果を返します — 長時間実行する操作にわたって。 ``` バイヤーエージェント → A2A クライアント → セラーエージェント → プラットフォーム ``` バイヤーエージェントは `create_media_buy` タスクをメッセージとして送信します。セラーエージェントがそれを処理し(秒または時間かかることがあります)、ステータス更新をストリーミングバックします。 **最適な用途:** エージェントが独立して動作し、主要なチェックポイントで人間が承認する自動化ワークフロー。 ### 同じタスク、異なるトランスポート 重要なポイント: AdCP タスク定義はトランスポート非依存です。`get_products` リクエストは MCP または A2A どちらで転送されても同じフィールドを持ちます。プラットフォームはドメインロジックを一度実装して両方のトランスポートで提供します。 ## エージェントが交換するもの AdCP は複数のドメインにわたるタスクを定義します。典型的なキャンペーンでエージェントが実際に送受信するものを示します: ### プロダクト発見 バイヤーエージェントは「何を購入できるか?」と尋ね、メディアプロダクトの構造化されたカタログを受け取ります — それぞれが価格設定、フォーマット、ターゲティングオプション、デリバリータイプを持ちます。 ```json theme={null} { "buying_mode": "brief", "brief": "Premium video inventory on sports content for Q2" } ``` レスポンスにはプロダクト ID、価格モデル(CPM、CPC、フラットレート)、利用可能なクリエイティブフォーマット、オーディエンスリーチ推定が含まれます。スキーマはどこでも同じなので、エージェントはプラットフォーム間でプロダクトを比較できます。 ### クリエイティブスペック クリエイティブを送信する前に、エージェントは `list_creative_formats` を呼び出してプラットフォームが受け入れるフォーマットを確認します。レスポンスには寸法、受け入れられるファイルタイプ、レンダリングロールを含む構造化されたフォーマットオブジェクトが含まれます: ```json theme={null} { "formats": [ { "format_id": { "agent_url": "https://ads.publisher.example.com", "id": "video_preroll_16x9" }, "name": "Pre-roll video (16:9)", "renders": [{ "role": "primary", "dimensions": { "width": 1920, "height": 1080, "unit": "px" } }] } ] } ``` 次にエージェントは `build_creative` を通じてマッチするクリエイティブを送信し、プラットフォームの要件に合わせて広告を生成または適応させます。 ### オーディエンスデータ エージェントはオーディエンスシグナル — ターゲティングセグメント、コンテキストデータ、ファーストパーティデータ — を `get_signals` を通じて交換します。バイヤーは必要なものを説明し、データプロバイダーはリーチ推定と価格を含むマッチするセグメントを返します。エージェントは `activate_signal` を通じて活性化する前にこれを評価できます。 ### キャンペーン実行 エージェントは予算、スケジュール、ブランドアイデンティティを含むメディアバイを作成します: ```json theme={null} { "account": { "account_id": "acct-12345" }, "brand": { "brand_id": "nova-electronics" }, "proposal_id": "prop-sports-video", "total_budget": { "amount": 25000, "currency": "USD" }, "start_time": "2026-04-01T00:00:00Z", "end_time": "2026-06-30T23:59:59Z" } ``` プラットフォームはこれを即座にまたは非同期で処理できます。AdCP のステータスシステム(`completed`、`working`、`submitted`、`input-required`)は進捗をエージェントに伝えます。 ### デリバリーレポート エージェントは `get_media_buy_delivery` を呼び出してパフォーマンスデータ — インプレッション、クリック、支出、コンバージョンイベント — を標準化された形式で取得します。デリバリースキーマはプラットフォーム間で同じなので、エージェントは異なるメトリクス名や計算方法を調整することなくパフォーマンスを集計して比較できます。 ## 実例 架空のエージェンシー Pinnacle Media が3つの AdCP 対応プラットフォームにわたって消費者電子機器ブランドのキャンペーンを実行します。 ### エージェントを発見します バイヤーエージェントは各パブリッシャーのドメインの `adagents.json` を確認して、セールスエージェントとサポートされるプロトコルを見つけます。 ### インベントリを比較します エージェントはすべての3つのプラットフォームで `get_products` を並行して呼び出します。構造化されたプロダクトカタログを受け取り、すべて同じスキーマで価格、フォーマット、リーチを比較します。 ### クリエイティブ要件を確認します エージェントは各プラットフォームで `list_creative_formats` を呼び出し、3つすべてに共通するフォーマットを特定します。クリエイティブ制作を共有セットに削減します。 ### 購入を実行します エージェントは比較分析に基づいた予算配分で各プラットフォームで `create_media_buy` を呼び出します。一部のプラットフォームは即座に確認し、他は `working` ステータスを返して後で確認します。 ### デリバリーを監視します エージェントは毎日3つのプラットフォームすべてで `get_media_buy_delivery` をポーリングし、エージェンシーの計画チーム向けに統合されたパフォーマンスビューに結果を集計します。 エージェンシーのチームは戦略を設定して結果をレビューします。エージェントはクロスプラットフォームの実行、フォーマット交渉、統合レポートを処理します。 ## はじめる AdCP トランスポートとしての MCP と A2A の詳細な技術比較。 実装ガイド、SDK、統合パターン。 バイヤーサイドの視点: エージェントがプラットフォーム間でメディアバイイングを自動化する方法。 プラットフォームが AI バイヤーエージェントにインベントリを公開する方法。 # AdCP が必要な理由 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/concepts/index AdCP が存在する理由: RTB、プラットフォーム API、ダイレクト IO にわたる断片化の問題と、広告向けユニバーサルエージェントプロトコルがそれを解決する方法。 ## デイトレではなく、配分 RTB は広告をデイトレのように扱います: *「このインプレッションの価値はいくらか?」*。代替可能な在庫では機能しますが、すべてをコモディティ化します。 AdCP はポートフォリオ単位の配分を可能にします: *「広告予算をどう配分すべきか?」*。これは広告主の実際の思考に合致します — 彼らが買うのはインプレッションではなく成果です。 RTB の思考モデルが広告主の実際の意思決定に合わない理由。 ## 構造的な問題 プログラマティック広告は 1 つの問い、*「このインプレッションは今いくらの価値があるか?」* をデフォルトとします。そのフレーミングは、CPM ベースでオークション中心の前提をトランザクション層に埋め込みます — それは世界の広告費の大部分には当てはまらない前提です。ダイレクトディール、スポンサーシップ、放送、屋外広告、リテールメディアはいずれも、異なる価格モデル、異なるタイムライン、異なる成功指標で動作します。これらはエッジケースではなく、多数派です。 AdCP はオークションの上位、**キャンペーンワークフロー層**に位置します。OpenRTB を置き換えたり、配信時の判断と競合したりするものではありません — オークションが発火する前後に発生する、計画・交渉・提案・ディール作成のステップを調整します。[プロトコル比較](/docs/building/concepts/adcp-vs-openrtb)で、AdCP と OpenRTB がどのように共存するかを説明しています。 これらのワークフローステップをプロトコルレベルのタスクとして構造化することで、AdCP はエージェントが[ブランドアイデンティティ](/docs/brand-protocol/brand-json)、[コンテンツ基準](/docs/governance/content-standards/)、ガバナンスチェックを、後付けではなく判断時に適用できるようにします。これらのタスクを使うエージェントは、実行後に取り付けるのではなく、購買プロセスの一部としてブランド適合性、クリエイティブの適合、コンプライアンスを推論できます。プロトコルがこれを可能にし、個々のエージェントがそれをどこまで使うかを決めます。 ## 分断の問題 現在、広告主はまったく異なる 3 つの購買システムに直面しています: | Paradigm | Era | How It Works | | ---------------- | ----------- | ------------------------------ | | **RTB/Biddable** | レガシー Web | OpenRTB によるリアルタイム入札 | | **API-based** | モダンソーシャル・AI | プラットフォーム固有の API (Meta, TikTok) | | **Direct IO** | レガシー | インサーションオーダー、手動取引 | それぞれ異なる統合、ツール、ワークフロー、専門知識が必要です。広告費の 90% は RTB に乗らず、ウォールドガーデンやダイレクトディール、プレミアム在庫で取引されています。 AdCP はこれら 3 つをひとまとめにする **ユニバーサルな API 標準** を提供します。 ## 設計時点でオムニチャネル 屋外広告を買うことと、ソーシャルリンクを買うことは根本的に異なります: * **価格モデルが異なる**: 定額 vs CPM vs エンゲージメントベース * **クリエイティブが異なる**: 静止画 vs 動画 vs 会話型 AI * **計測が異なる**: インプレッション vs エンゲージメント vs 来店数 AdCP は各チャネルの特性を保持しつつ差分を抽象化する概念レイヤーを作ります。1 つのプロトコルで、あらゆるチャネルを扱います。 ## なぜエージェントか? インテリジェントエージェントは、複雑で交渉を伴う取引のコストを削減します: * **細かなニュアンスに適応**: すべてをコードで過剰に指定せず対応 * **プラットフォームやチャネル間の差異を吸収** * **自然言語**: バイヤーが意図を伝えるだけで、パラメータ設定を不要に * **関係拡大**: 3〜5 のプラットフォームから 20 以上へ、チームを増やさずに拡張 ## AI のためのプロトコルレイヤー AdCP はレガシーシステムを統一するだけでなく、新しい AI エクスペリエンスのためのプロトコルレイヤーでもあります。 ### Sponsored Intelligence VAST が動画広告配信を定義したように、SI は AI アシスタントにおける会話型ブランド体験を定義します。AI が *「デルタ航空にボストン行きがあります。アシスタントにつなぎましょうか?」* と話すとき、その後の流れを決めるのが SI です。 会話型 AI が広告の経済をどう変えるか。 ### Brand identity AI によるクリエイティブ生成のための標準化されたブランドアイデンティティ。色やトーン、アセットなど、ブランドが自らを AI が扱える形式で表現します。 これらにより、手動設定ではなく構造化されたブランド入力を必要とする Performance Max のようなフル AI 化されたシステムを支えます。 AI エクスペリエンスの収益化 — 反転したデータフロー、プロダクトスペクトラム、SI チャットプロトコル。 AI クリエイティブ生成のための標準化されたブランドアイデンティティ。 ## 設計への示唆 これらの目的が AdCP の技術設計を形作っています: * **非同期**: 取引には時間がかかります。これはリアルタイムプロトコルではなく、処理は数分から数日かかる場合があります。 * **Human-in-the-loop**: 一部の決定には人の承認が必要です。パブリッシャーは手動承認を要求できます。 * **複数のトランスポート**: MCP と A2A が異なるプロトコルで同じタスクを提供します。 ## プロトコルファミリー | Protocol | Purpose | Key Tasks | | -------------------------- | ------------------ | ---------------------------------- | | **Media Buy** | キャンペーン実行 | `get_products`, `create_media_buy` | | **Signals** | オーディエンスターゲティング | `get_signals`, `activate_signal` | | **Creative** | 広告クリエイティブ管理 | `build_creative`, `sync_creatives` | | **Governance** | ブランドセーフティ、コンプライアンス | Property lists, content standards | | **Sponsored Intelligence** | 会話型ブランド体験 | `si_initiate_session` | | **Curation** | 在庫パッケージング | Coming soon | ## 次のステップ MCP と A2A の使い分けと共通点。 AdCP の JavaScript および Python ライブラリ。 # AI 広告標準のランドスケープ Source: https://adcp-docs-ja.pier1.co.jp/docs/building/concepts/industry-landscape AI 広告標準のランドスケープ: AdCP、OpenRTB、MCP、A2A の関係。エージェンティック広告におけるプロトコル、標準化団体、それぞれの役割の比較。 AI 駆動の広告を形作っている主要な標準は、AdCP(エージェントワークフロー調整)、OpenRTB(インプレッションオークション)、MCP(AI ツール呼び出し)、A2A(エージェント間通信)です。広告はプログラマティック(機械実行のオークション)からエージェンティック(AI エージェントがキャンペーンライフサイクル全体を管理)へと移行しており、これらのプロトコルが各部分の接続方法を定義します。 このページはランドスケープをマッピングします: 何が存在するか、誰が管理するか、どのように組み合わさるか。 ## アクティブなプロトコル ### Ad Context Protocol(AdCP) AdCP は AI エージェントが広告プラットフォームと対話する方法を定義します。キャンペーンの完全なライフサイクルをカバーします: プロダクト発見、メディアバイイング、クリエイティブ生成、オーディエンス活性化、ブランドガバナンス、デリバリーレポート。 | | | | ------------ | -------------------------------------------------------------------- | | **スコープ** | エージェントレベルの広告ワークフロー | | **トランスポート** | MCP(ツール呼び出し)または A2A(エージェント間) | | **管理者** | [AgenticAdvertising.org](https://agenticadvertising.org) | | **ライセンス** | Apache 2.0(オープンソース) | | **現在のバージョン** | 3.0 リリース候補 | | **主要タスク** | `get_products`、`create_media_buy`、`build_creative`、`activate_signal` | AdCP はトランスポート非依存です: 同じタスク定義が MCP と A2A の両方で機能します。プラットフォームは一度実装し、エージェントはどちらのトランスポートでも接続できます。 ### OpenRTB OpenRTB はリアルタイムインプレッションオークションを処理します — ほとんどのプログラマティックディスプレイとビデオ広告を動かす入札リクエスト/入札レスポンスサイクルです。 | | | | ------------ | -------------------------------------- | | **スコープ** | インプレッションレベルのトランザクション | | **トランスポート** | HTTP POST | | **管理者** | [IAB Tech Lab](https://iabtechlab.com) | | **現在のバージョン** | 2.6 / 3.0 | | **主要オブジェクト** | 入札リクエスト、入札レスポンス、勝利通知、請求通知 | OpenRTB と AdCP は補完的です。OpenRTB は「このインプレッションに入札すべきか?」を処理し、AdCP は「この予算とターゲティングでキャンペーンを作成する」を処理します。詳細は [AdCP と OpenRTB](/docs/building/concepts/adcp-vs-openrtb) を参照してください。 ### Model Context Protocol(MCP) MCP は AI モデルが外部ツールを呼び出す方法を定義します。Anthropic が開発し、AI アシスタントを API、データベース、サービスに接続するための標準です。 | | | | ----------- | -------------------------------------------- | | **スコープ** | AI ツール呼び出し | | **トランスポート** | stdio または SSE 上の JSON-RPC | | **管理者** | [Anthropic](https://modelcontextprotocol.io) | | **使用者** | Claude、Cursor、Windsurf、その他の AI アシスタント | AdCP は MCP をトランスポート層の一つとして使用します。AdCP MCP サーバーは広告タスク(`get_products` や `create_media_buy` など)を、任意の MCP 互換 AI アシスタントが呼び出せるツールとして公開します。 ### Agent-to-Agent Protocol(A2A) A2A は自律エージェントが互いに通信する方法を定義します。Google が開発し、特化エージェントが協調するマルチエージェントワークフローを可能にします。 | | | | ----------- | --------------------------------------- | | **スコープ** | エージェント間コラボレーション | | **トランスポート** | SSE ストリーミングを含む HTTP + JSON-RPC | | **管理者** | [Google](https://google.github.io/A2A/) | | **使用者** | マルチエージェントオーケストレーションフレームワーク | AdCP は A2A をもう一つのトランスポート層として使用します。A2A セットアップでは、バイヤーエージェントがセラーエージェントに構造化されたメッセージとして AdCP タスクを送信し、ストリーミングを通じた長時間実行操作をサポートします。 ## 標準化団体 | 組織 | 焦点 | 主要な成果 | | ------------------------------------------------------------ | ------------- | --------------------------------------------- | | **[AgenticAdvertising.org](https://agenticadvertising.org)** | AI エージェント広告標準 | AdCP 仕様、JSON スキーマ、リファレンス実装 | | **[IAB Tech Lab](https://iabtechlab.com)** | デジタル広告標準 | OpenRTB、VAST、ads.txt、sellers.json、コンテンツタクソノミー | | **[Anthropic](https://anthropic.com)** | AI 安全性と研究 | MCP 仕様 | | **[Google](https://google.github.io/A2A/)** | AI とクラウド | A2A 仕様 | | **[W3C](https://www.w3.org)** | Web 標準 | Privacy Sandbox API、Topics API | AgenticAdvertising.org は独立したメンバー組織です。IAB Tech Lab、Anthropic、Google、またはその他の会社の子会社やワーキンググループではありません。メンバーにはプラットフォームプロバイダー、広告主、エージェンシー、デベロッパーが含まれます。 ## エコシステムの他の標準 | 標準 | 目的 | 管理者 | | ------------------------------ | ---------------------- | ----------------------- | | **VAST** / **VPAID** | ビデオ広告サービングとインタラクティブビデオ | IAB Tech Lab | | **ads.txt** / **sellers.json** | サプライチェーン透明性 | IAB Tech Lab | | **Open Measurement SDK** | ビューアビリティとアテンション測定 | IAB Tech Lab | | **Unified ID 2.0** | プライバシー保護アイデンティティ | The Trade Desk / Prebid | | **Privacy Sandbox** | クッキーレスターゲティング API | Google / W3C | ## 層がどのように組み合わさるか これらのプロトコルはスタックの異なる層で動作します: ``` ┌──────────────────────────────────────────────┐ │ 戦略層 (AdCP) │ │ キャンペーン計画、予算配分、 │ │ クロスプラットフォーム調整 │ ├──────────────────────────────────────────────┤ │ トランスポート層 (MCP / A2A) │ │ エージェントがツールを呼び出しデータを交換する方法 │ ├──────────────────────────────────────────────┤ │ 実行層 (OpenRTB / プラットフォーム API) │ │ インプレッションレベルのオークション、広告サービング、│ │ クリエイティブレンダリング │ ├──────────────────────────────────────────────┤ │ 測定層 (OMSDK / UID2 / Privacy) │ │ ビューアビリティ、アトリビューション、アイデンティティ │ └──────────────────────────────────────────────┘ ``` 典型的なワークフロー: AI エージェントは **MCP** を使用してパブリッシャーのプラットフォームで **AdCP** タスクを呼び出してキャンペーンを作成します。パブリッシャーの広告サーバーは **OpenRTB** を使用してインプレッションレベルのデリバリーを実行します。**OMSDK** がビューアビリティを測定します。**ads.txt** がサプライチェーンを検証します。 ## 変わりつつあること 3つのトレンドがこれらの標準の相互作用を再形成しています: **エージェント仲介バイイング。** 人間がダッシュボードを操作する代わりに、AI エージェントがプラットフォーム全体のキャンペーンを管理するようになります。これにより標準化されたエージェントインターフェースへの需要が生まれます — それが AdCP が提供するものです。 **プロトコルの収束。** MCP と A2A がエージェント通信のトランスポート層を確立しています。AdCP のようなドメイン固有のプロトコルはその上に構築されます。これは HTTP がトランスポート層になり、ドメイン固有の API がその上に構築されたパターンと同じです。 **垂直特化。** 汎用エージェントプロトコル(MCP、A2A)はコミュニケーションを処理します。垂直プロトコルはドメインロジックを処理します。AdCP は広告を処理します。エージェントの採用が成長するにつれて、同じトランスポート+ドメインパターンが他の垂直に現れるかもしれません。 ## 参加します プロトコルアーキテクチャとコアコンセプトを理解します。 プロトコルの方向性を形成するワーキンググループに参加します。 AdCP と OpenRTB の詳細な比較 — どのように補完し合うか。 AdCP トランスポート層としての MCP と A2A の技術比較。 # レスポンスサイズの管理 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/concepts/managing-response-size AdCP レスポンスを軽量に保つ方法: フィールド選択、ページネーション、buying mode、切り詰めフラグ、そして最も重要なワイヤー対コンテキストの区別。 バイヤーエージェントが AdCP セラーを呼ぶとき、レスポンスは一握りのキュレーションされたプロダクトから、詳細なカード、シグナルメタデータ、プレースメント仕様を伴う完全なホールセールカタログまで及びます。このページは、AdCP が既に提供するレスポンスを適切なサイズに保つ制御 — そしてほとんどのトークン予算の懸念をプロトコル問題ではなくクライアント側の問題にするアーキテクチャの洞察 — をカバーします。 ## ワイヤーレスポンス ≠ モデルコンテキスト 最も重要な単一の点: **ワイヤー上のバイトはあなたのモデルが消費しなければならないものではありません。** AdCP レスポンスは構造化データです。MCP レスポンスは `structuredContent` として到着します — 任意のモデルが見る前にクライアントがパースする型付き JSON。A2A レスポンスは人間可読な `TextPart` と権威ある `DataPart` をペアにします。両方のケースで、よく構築されたクライアントは完全なレスポンスを保存し、次のモデルターンをプロンプトする前にそれを投影または要約します。 ``` Seller agent → structured response (full data) ↓ Client stores raw response ↓ Client projects / summarizes → model context (lean) ``` エージェントが生のツール結果を変更せずにモデルコンテキストに転送している場合、修正はプロトコルではなくクライアントにあります。レスポンスを保存し、モデルが次の決定に必要なものを抽出し、後で詳細が必要なとき保存されたデータを ID で参照してください。 **素朴なホストの注意点。** 一部の MCP ホストは `structuredContent` blob 全体をそのままモデルコンテキストに渡します。ホストを制御しているなら、プロンプト前に投影してください。していないなら、下の制御が過大なコンテキストに対するあなたの主要な防御になります。 ## セラーにキュレーションを頼む — フィードを引かない キュレーションされたおすすめを得るには、厳しい `pagination.max_results` を伴う `buying_mode: "brief"` を使います: ```json theme={null} { "buying_mode": "brief", "brief": "Premium video placements for a CPG brand targeting US adults 25-54", "pagination": { "max_results": 5 } } ``` セラーはそのベストマッチを返します — ランク付けされ、価格付けされ、実行準備完了。これは軽量なパスです。 **カタログミラーを構築していない限り `buying_mode: "wholesale"` を避けてください。** ホールセールはセラーの全プロダクトフィードをページ分割して列挙します。ストアフロントとフィード同期のために存在し、ディスカバリーのためではありません。エージェントが数個の良いものを見つけるために数百のホールセール結果をページ分割している場合、`brief` モードに切り替えてセラーにフィルタリングをさせてください。 前の `brief` レスポンスを反復するとき — 予算の調整、地理の絞り込み、プロダクトの交換 — は `buying_mode: "refine"` を使います。Refine は、ディスカバリーを一から再実行するのではなく、セラーの既存のキュレーションに作用します。 ## 軽量ディスカバリーのための `fields` セレクター プロダクトデータのサブセットのみが必要なとき、レスポンスを制限するため `fields` を渡します: ```json theme={null} { "buying_mode": "brief", "brief": "Sports streaming inventory for Q4", "fields": ["product_id", "name", "pricing_options"] } ``` これは次のとき有用です: * エージェントが複数のセラーをスキャンしていて初期比較のため ID と価格のみが必要 * `product_card`、`product_card_detailed`、`placements`、シグナルメタデータのような重いフィールドをスキップしたい * 特定のプロダクトに掘り下げる前にサマリービューを構築している `fields` なしでは、セラーは完全なプロダクトオブジェクト — ビジュアルカード定義、プレースメント仕様、任意のバンドルされたシグナルメタデータを含む — を返します。複数のセラーにわたる第一段階のディスカバリーには、それは必要以上のデータです。 ## カーディナリティ制御のためのページネーション すべての `get_products` モードはカーソルベースのページネーションをサポートします: | Parameter | Description | | ------------------------ | ----------------------------- | | `pagination.max_results` | ページごとの最大プロダクト(1–100、デフォルト 50) | | `pagination.cursor` | 前のレスポンスからの不透明なカーソル | レスポンス: | Field | Description | | ------------------------ | ---------------- | | `pagination.has_more` | 追加のページが存在するか | | `pagination.cursor` | 次のページのカーソル | | `pagination.total_count` | 結果セットの総プロダクト(任意) | **brief と refine モードでは**、エージェントが実際に推論できるオプションの数に `max_results` を設定します。モデルがどのみち比較するなら、5 つのキュレーションされたプロダクトは 50 より有用です。 **wholesale モードでは**、ページネーションがフィードをたどります。各レスポンスから `cursor` を渡して次のページを得ます。ミラーを維持しているなら、変更されていないフィードを完全にスキップするため [`wholesale_feed_versioning`](/docs/media-buy/task-reference/get_products#wholesale-feed-versioning) を確認してください。 ## 配信の切り詰めフラグ [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) は、大きく成長しうる内訳配列(geo 別、クリエイティブ別、日別など)を返します。各内訳配列は、返された行が完全なセットか top-N だけかを教える兄弟の boolean フラグ — `by_geo_truncated`、`by_creative_truncated` など — を持ちます: | Flag value | Meaning | | ---------- | ----------------- | | `false` | すべての行が存在 | | `true` | 返されたものを超えて追加の行が存在 | フラグが `true` のとき、返された行は要求されたメトリクスで降順ソートされます — 最も重要な内訳を持ち、裾野は省略されます。これは設計上: 配信内訳はアーカイブレポートではなく最適化決定のためです。完全なデータセットが必要なら、セラーのネイティブレポート API を使ってください。 ## まとめ トークン効率の良い AdCP 統合はこのパターンに従います: 1. **`brief` + `fields` + 厳しい `max_results` でディスカバリー。** 初期比較に必要なフィールドのみを持つキュレーションされたプロダクトを得る。 2. **`refine` モードで絞り込み。** 再クエリするのではなくセラーのキュレーションを反復する。 3. **生のレスポンスをクライアント側で保存。** 完全なプロダクトオブジェクトをモデルコンテキストに供給しない。重要なものを抽出し、残りを要約する。 4. **配信をページ分割する前に切り詰めフラグを読む。** `by_geo_truncated: false` なら、既にすべてを持っている — フォローアップ呼び出し不要。 5. **wholesale はフィード同期にのみ使う。** ユースケースがカタログミラーリングなら、wholesale + 条件付きバージョニングが正しいツール。他のすべてには `brief` が安い。 `fields`、`buying_mode`、ページネーションを含む完全なリクエスト/レスポンススキーマ。 内訳配列、切り詰めフラグ、ソートセマンティクス。 MCP 対 A2A トランスポートとレスポンスの構造。 プロダクト構造、`product_card` 対 `product_card_detailed`、レンダリング。 # プロトコル比較 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/concepts/protocol-comparison AdCP における MCP と A2A の比較: トランスポート形式、非同期処理、ステータスシステムのサイドバイサイド比較、および広告エージェント統合での使い分け。 MCP と A2A は、統一ステータスシステムを用いて同じ AdCP 機能を提供します。異なるのはトランスポート形式と非同期処理だけです。 ## クイック比較 | Aspect | MCP | A2A | | ------------------- | --------------------------- | ----------------------------- | | **Request Style** | Tool calls | Task messages | | **Response Style** | Direct JSON | Artifacts | | **Status System** | Unified status field | Unified status field | | **Async Handling** | Polling with tasks/get | SSE streaming | | **Webhooks** | Protocol wrapper extension | Native PushNotificationConfig | | **Task Management** | tasks/list, tasks/get tools | Native tasks/list, tasks/get | | **Context** | Manual (pass context\_id) | Automatic (protocol-managed) | | **Best For** | Claude, AI assistants | Agent workflows | ## 統一ステータスシステム 両プロトコルは同じステータスフィールドを同じ値で使用します。 ### ステータス処理(両プロトコル) すべてのレスポンスに、次に取るべき行動を示すステータスフィールドが含まれます: ```json theme={null} { "status": "input-required", // Same values for both protocols "message": "Need your budget", // Same human explanation // ... protocol-specific formatting below } ``` | Status | What It Means | Your Action | | ---------------- | ---------------------------- | --------------------------------------- | | `completed` | Task finished | Process data, show success | | `input-required` | Need user input | Read message, prompt user, follow up | | `working` | Processing (\< 120s) | Poll frequently, show progress | | `submitted` | Long-running (hours to days) | Provide webhook or poll less frequently | | `failed` | Error occurred | Show error, handle gracefully | | `auth-required` | Need auth | Prompt for credentials | [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) でステータス処理の完全ガイドを確認してください。 ## トランスポート形式の違い ステータスとデータは同じで、パッケージングが異なります: ### MCP レスポンス形式 ```json theme={null} { "status": "input-required", "message": "I need your budget and target audience", "context_id": "ctx-123", "products": [], "suggestions": ["budget", "audience"] } ``` ### A2A レスポンス形式 ```json theme={null} { "status": "input-required", "contextId": "ctx-123", "artifacts": [{ "artifactId": "artifact-product-discovery-xyz", "name": "product_discovery", "parts": [ { "kind": "text", "text": "I need your budget and target audience" }, { "kind": "data", "data": { "products": [], "suggestions": ["budget", "audience"] } } ] }] } ``` ## 非同期処理の違い 両プロトコルは同じステータス遷移で非同期処理を扱います: `submitted` → `working` → `completed`/`failed` ### MCP の非同期パターン ```javascript theme={null} // Initial response with task_id { "status": "submitted", "message": "Creating media buy, requires manual approval", "context_id": "ctx-123", "task_id": "task-456", } // Poll using tasks/get const updates = await session.call('tasks/get', { task_id: "task-456", include_result: true }); // Optional: Configure webhook const response = await session.call('create_media_buy', params, { push_notification_config: { url: "https://buyer.com/webhooks", authentication: { schemes: ["HMAC-SHA256"], credentials: "shared_secret_32_chars" } } }); ``` ### A2A の非同期パターン ```javascript theme={null} // Initial response with native task tracking { "status": "submitted", "taskId": "task-456", "contextId": "ctx-123", "estimatedCompletionTime": "2025-01-23T10:00:00Z" } // Real-time updates via SSE const events = new EventSource(`/tasks/${response.taskId}/events`); events.onmessage = (event) => { const update = JSON.parse(event.data); console.log(`Status: ${update.status}, Message: ${update.message}`); }; // Native webhook support await a2a.send({ message: { /* skill invocation */ }, push_notification_config: { webhook_url: "https://buyer.com/webhooks", authentication: { schemes: ["Bearer"], credentials: "secret_token_min_32_chars" } } }); ``` ## コンテキスト管理 ### MCP: 手動コンテキスト ```javascript theme={null} let contextId = null; async function callAdcp(request) { if (contextId) { request.context_id = contextId; } const response = await mcp.call('get_products', request); contextId = response.context_id; // Save for next call return response; } ``` ### A2A: 自動コンテキスト ```javascript theme={null} // A2A manages context automatically const response1 = await a2a.send({ message: "Find video products" }); const response2 = await a2a.send({ contextId: response1.contextId, // Optional - A2A tracks this message: "Focus on premium inventory" }); ``` ## 確認・追加情報の扱い 両プロトコルは同じ `status: "input-required"` パターンを使用します: ```javascript theme={null} // Works for both MCP and A2A function handleResponse(response) { if (response.status === 'input-required') { const info = promptUser(response.message); return sendFollowUp(response.context_id, info); } if (response.status === 'completed') { return processResults(response); } } ``` ## エラーハンドリング 両者とも `status: "failed"` と同じエラー構造を使用します: ```json theme={null} { "status": "failed", "message": "Insufficient inventory for your targeting criteria", "context_id": "ctx-123", "error_code": "insufficient_inventory", "suggestions": ["Expand targeting", "Increase CPM"] } ``` ## プロトコルを選ぶには ### MCP を選ぶ場合: * Claude Desktop または Claude Code を使用 * MCP 対応の AI アシスタントを利用 * シンプルなツールベース統合が必要 * 直接 JSON のレスポンスが欲しい ### A2A を選ぶ場合: * Google AI agents または Agent Engine を利用 * マルチモーダル(テキスト + ファイル)なワークフロー * リアルタイムなストリーミング更新 * アーティファクトベースのデータ処理 ### 両プロトコルが提供するもの: * 同じ AdCP タスクと機能 * クライアントロジックを明確にする統一ステータスシステム * 会話のためのコンテキスト管理 * 非同期処理のサポート * Human-in-the-loop ワークフロー * エラーハンドリングと復旧 ## 次のステップ * **MCP Guide**: ツールコールとコンテキスト管理は [MCP Guide](/docs/building/by-layer/L0/mcp-guide) を参照 * **A2A Guide**: アーティファクトとストリーミングは [A2A Guide](/docs/building/by-layer/L0/a2a-guide) を参照 * **Both protocols**: 統一ステータスで同じ機能を提供 # セキュリティモデル Source: https://adcp-docs-ja.pier1.co.jp/docs/building/concepts/security-model エージェンティック広告がなぜセキュリティのリスクを高めるか、AdCP が防御するよう設計された脅威、AdCP デプロイを評価するセキュリティ/IT リーダーのためのチェックリスト。 AdCP デプロイを評価する CISO、セキュリティアーキテクト、サードパーティリスクレビュアー向け — 取引のどちら側でも(ブランド、代理店、パブリッシャー、プラットフォーム、データプロバイダー)。[実装リファレンス](/docs/building/by-layer/L1/security) が規範的ルールを持ちます。このページはその背後にあるモデルを説明します。 ## なぜセキュリティは付加物ではなく基盤なのか 従来の広告では、お金が動く前に人間がインサーションオーダーをレビューします。エージェンティック広告では、エージェントが人間です。それはブリーフを評価し、条件を交渉し、バイを置き、レポートを扱い、失敗した取引をリトライするかを決めます — しばしば何かが既に起こるまで人がループに入りません。 そのシフトは 3 つの方法でリスクを集中させます: * **権限は可搬。** 年間 1,000 万ドルを支出できる認証情報はトークンに収まります。正しいスコープを持つ盗まれたトークンは、実際の予算に対して実際のメディアバイを作成でき、そのバイはすべての下流システムに正当に見えます。なぜなら — プロトコルの観点からは — それは*正当*だからです。 * **決定は速い。** エージェントは完全な plan-to-purchase ループを数秒で実行できます。侵害されたループは 1 日の予算を数分で燃やせます。ラインアイテムが投入されるのを見ている広告運用チームはいません。 * **攻撃者はあなたと同じツールを使う。** AI は API を使うのと同じ速さでそれをレッドチーム化できます。あなたのエージェントに文書化されたサーフェスがあれば(そしてあるべきです — それが他のエージェントがそれを発見する方法です)、敵対者のエージェントはそれを列挙し、プローブし、マシンスピードでファジングできます。security-by-obscurity は制御ではありません。 これらが AdCP が耐えるよう構築された条件です。このページの残りはその方法です。 エージェンティック ad tech における侵害サーフェスは「データ露出」ではありません。それは **認可されていない金銭的コミットメント**、**バイパスされたガバナンス**、**同じプラットフォーム上の広告主間のクロステナントデータ漏洩**、**規制当局が後で見ることを求める監査証跡の改ざん** です。それらのそれぞれは実装リファレンスに名前付きの脅威モデルを持ちます。このページは一歩下がってなぜかを説明します。 ## 脅威モデルで何が変わるか **従来の API セキュリティのすべてが依然適用されます** — 認証、認可、レート制限、入力検証、トランスポートセキュリティ、静止時データ暗号化、エンドポイント堅牢化、ロギング。エージェントはパブリックインターネット上の HTTP サービスです。REST API に適用するすべての制御をここでも適用します。AdCP はそのベースラインを置き換えず、このページはそれを再教育しません。 エージェンティック広告が *追加する* のは、従来のものの上の第 2 層の関心事です: | 従来の API セキュリティが既にカバーするもの… | エージェンティック広告がさらに要求するもの… | | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | 人間ユーザーの認証 | ブランドまたは代理店 *に代わって* [エージェント](/docs/reference/glossary#a)を認証し、そのブランドがこの特定の支出を認可したことを証明する | | データ流出の防止 | *認可されていない状態変更* の防止 — エージェントはリトライし、ループし、ファンアウトする。単一の成功した注入が多数回実行されうる | | 悪用的な呼び出し元のレート制限 | **リプレイ攻撃** の防止: ネットワークタイムアウトで 100 万ドルのメディアバイをリトライするエージェントは決して 2 つを作ってはならない | | 入力検証 | **当事者 URL 検証**: エージェントは他のエージェントが供給する URL(webhook、レジストリ、JWKS、レポートバケット)からフェッチする — それぞれが内部ネットワークへの [SSRF](/docs/reference/glossary#s) ベクター | | 監査ロギング | **暗号学的に署名されたガバナンスアテステーション** で、取引を生き残り、どちらの当事者も信頼せずに何年も後に規制当局が検証可能 | | シングルテナント分離 | **共有インフラ上のマルチエージェント、マルチアカウント分離** — 1 つの侵害されたエージェントが別のエージェントのバイ、クリエイティブ、ターゲティングを見てはならない | これらのどれも新規の暗号技術ではありません。新しいのは組み合わせ — よく理解されたプリミティブが自律的に、マシンスピードで、当事者境界を越えて動作する — で、左側の制御が今や人間がしていた決定をバックストップします。 ### Threats specific to agentic advertising 3 つの攻撃クラスは従来の API 脅威モデルには現れませんが、これには属します: * **1 つのエージェントの下のアカウントをまたいだ認証情報の再利用。** 代理店エージェントは通常、その認可アカウントセットのすべてのブランドにわたって機能する認証情報を保持します。したがって盗まれたエージェントトークンは、単一ブランドではなくマルチブランドの侵害です。AdCP の `(agent, account)` ごとのキャッシュスコーピング([Agent and Account Isolation](/docs/building/by-layer/L1/security#エージェントとアカウントの分離) を参照)と署名付きガバナンストークン(特定のプランとセラーにバインド)は、盗まれた認証情報で *何が* できるかを制限しますが、盗難を防ぎません。エージェンティックシステムにおける認証情報の衛生は、シングルテナント API より比例的により重要です。 * **共有ガバナンスエージェントのサプライチェーン。** ガバナンスエージェントはしばしば単一のオリジンから多くのブランドのために署名します。その侵害はマルチテナントの侵害です。[ガバナンスプロファイル](/docs/building/by-layer/L1/security#署名付きガバナンスコンテキスト)の JWKS / 失効リスト要件は影響範囲を制限しローテーションを観測可能にしますが、バイヤーのそのガバナンスエージェントへのデューデリジェンス姿勢は実世界のセキュリティ依存です — ガバナンスエージェントをマルチカスタマーの影響範囲を持つプロセッサーとして扱い、それに応じて評価してください。 * **マルチテナントオペレーターでのクロスプリンシパル鍵再利用。** 複数のプリンシパルに代わってエージェントをホストする任意のオペレーター — 複数のブランドに提供するガバナンスエージェント、複数の広告主に提供するバイヤーエージェント、複数のパブリッシャーに提供するセールスエージェント — は、フリート全体で 1 つの鍵を再利用するのではなく、プリンシパルごとに署名鍵をスコープしなければなりません(MUST)。具体的には、各 `keyid` は単一のプリンシパルにバインドしなければならず(MUST)、そうすれば単一の侵害された鍵が単一プリンシパルの侵害に縮小し失効が細かくなります。`{operator}:{principal}:{key_version}` のような慣例は有用なオペレーター側の帳簿付けの補助ですが、`kid` 値自体は RFC 7517 に従い検証者にとって不透明です — 検証者は `kid` 構造をパースしてプリンシパルアイデンティティを導出したり認可決定を下したりしてはならず(MUST NOT)、認証された署名 → JWKS → エージェントエントリチェーンを介して所有プリンシパルを解決し、`kid` を JWKS へのインデックスとしてのみ使わなければなりません(MUST)。したがって構造化された慣例を発明するオペレーターは、オンワイヤーの認可入力ではなく内部の帳簿付けツールを作ります。オペレーターは、当事者が手で JWKS を読み出さずにプロパティを検証できるよう、ケイパビリティサーフェスに分離プロパティを `identity.per_principal_key_isolation: true` としてアドバタイズすべきです(SHOULD)。 * **エージェント側認証情報を流出させるプロンプトインジェクション。** プランナー、クリエイティブレビューエージェント、ブリーフ解釈パイプラインはすべて、認証情報を保持しながら信頼できないテキスト(ブリーフ、クリエイティブメタデータ、プロダクト説明、キャンペーン名)を処理します。成功した注入は、エージェントに認可されていないツール呼び出しを発行させるか、トークンをログ、外部 URL、下流エージェントメッセージに漏らさせる可能性があります。AdCP はプロトコル層でこれを防げませんが、LLM 駆動エージェントを実行するすべてのオペレーターは、入力サンドボックス化、ツール呼び出しのエグレス制御(特定のプロンプトコンテキスト内からエージェントがどの URL / どのツールに到達できるか)、異常な認証情報使用の監視を必要とします。対処可能なプロトコル層のスライス — バイヤープリンシパル認証情報を LLM 可視のタスクペイロードから完全に外す — は [Credential placement](/docs/building/by-layer/L2/authentication#credential-placement) ルールです: 認証情報はトランスポートの認証チャネルで到着しなければならず、リクエスト引数の内側では決して到着しない(MUST)。これはこの空間で最も可能性の高い近期の侵害ベクターであり、プロトコルコンプライアンスだけでは解決されません。 * **クロスプリンシパルのツール呼び出し混乱。** バイヤーエージェントは通常、一度に *複数の* プリンシパルのアクティブな認証情報を保持します — いくつかのセラー(セラーごとに 1 セットの認証情報)といくつかのブランドアカウント(単一の代理店エージェントの権限セット内)。LLM 駆動エージェントはしばしばそれらのツールサーフェスのすべてを同じプランニングループに公開します。セラー X から返されたテキスト(プロダクト説明、キャンペーン名、拒否理由)経由で注入されたプロンプトは、エージェントにセラー Y のエンドポイントのツールを呼ばせるか、ブランド B に認可された予算を使ってブランド A の `create_media_buy` を呼ばせる可能性があります。これは LLM ツール呼び出し粒度での古典的な [confused deputy](https://en.wikipedia.org/wiki/Confused_deputy_problem) 問題です。プロトコル層の防御は Layer 2(すべてのツール呼び出しでのアカウントスコーピング、呼び出し元が権限を持たない任意のクロスアカウントアクションの拒否)にあります。オペレーター層の防御は、各インバウンド文字列を発信元プリンシパルでタグ付けし、ターゲットプリンシパルが文字列を供給したプリンシパルと異なるツール呼び出しを人間が承認しない限り拒否し、単一の LLM コンテキストが利害が衝突しうるプリンシパルの認証情報を保持することを禁じることです。この脅威は通常のプロンプトインジェクションとは別です: 攻撃者は *被害者プリンシパルの* 認証情報を使うためにサンドボックスをエスケープする必要がありません — 被害者自身のエージェントが彼らのためにそれをします。 ### Structural privacy separation AdCP は、当事者が行動するのに必要なものだけを学ぶよう設計されています。これはポリシーだけでなくプロトコル構造によって強制されます。例: * **[Trusted Match Protocol](/docs/trusted-match)** はインプレッション時の決定を 2 つの独立した呼び出しに分割します: *Context Match* はユーザーアイデンティティなしにコンテンツシグナル(トピック、センチメント、embedding)を運び、*Identity Match* はページコンテキストなしに不透明なユーザートークンを運びます。どちらの呼び出しも単独では、どのユーザーがどのページを訪れたかを明かしません — 分解がプライバシープロパティです。 * **Signals Protocol** はデプロイアクセスのみを持つ認証された呼び出し元に `activation_key` 値を返し、マーケットプレイスカタログアクセス(公開)をプライベートシグナル開示(アカウントスコープ)から構造的に分離します。 * **ガバナンストークン** は、バイヤーのコンプライアンス姿勢が敏感なとき、インライン `policy_decisions` の代わりに `policy_decision_hash` を使います — 完全な決定ログは、ガバナンスエージェントのアクセス制御の下、署名された `audit_log_pointer` 経由で監査者に利用可能なままです。 * **`sync_audiences` のオーディエンスメンバー** は、スキーマがバイヤー側での SHA-256 ハッシュ化を要求し平文を構造的に拒否する `hashed_email` と `hashed_phone` フィールドを使います。email または phone のソルトなし SHA-256 は匿名ではなく仮名 PII であることに注意 — 事前計算された辞書経由で回復可能なので、オペレーターは保持と同意についてハッシュ化識別子を PII として扱わなければなりません(MUST)。[プライバシー考慮事項](/docs/reference/privacy-considerations#unsalted-hashed-identifiers-are-pseudonymous-not-anonymous) を参照。 ここで「構造的」とは、分割されたワークフローの 1 つのレッグを侵害する攻撃者が、他方のレッグにのみ存在するよう設計された情報を得ないことを意味します。これは暗号学的機密性より弱い保証ですが、ポリシーだけより強いものです。 ## AdCP の多層防御モデル AdCP はこれらの脅威を 5 つの層で防御します。それぞれが別個の制御で、1 つの失敗が他を崩壊させません。これは支払いシステムで使われるのと同じ多層防御パターンです — 下の 5 つの層は、すべての準拠実装が *何を* 正しくしなければならないかを記述します。それらを *どう* 構築するかはあなた次第です。 ```mermaid theme={null} flowchart TB A[Request arrives] --> B["Layer 1: Identity
mTLS / signed requests / API key"] B --> C["Layer 2: Isolation
Per-agent, per-account scope"] C --> D["Layer 3: Idempotency
At-most-once execution"] D --> E["Layer 4: Signed Governance
JWS from governance agent"] E --> F["Layer 5: Auditability
Replay-proof audit trail"] F --> G[Execute side effects] style B fill:#e0f2fe,stroke:#0369a1 style C fill:#e0f2fe,stroke:#0369a1 style D fill:#e0f2fe,stroke:#0369a1 style E fill:#e0f2fe,stroke:#0369a1 style F fill:#e0f2fe,stroke:#0369a1 ``` ### Layer 1: Identity — who is actually calling? 他のどのチェックの前にも、セラーは *どの認証されたエージェント* がリクエストをしているかを確立しなければなりません。AdCP は 3 つのメカニズムを定義します。バージョンゲーティングがどの操作クラスにどれが許可されるかを決めます: * **RFC 9421 署名付き HTTP リクエスト** — バイヤーは各リクエストに、その公開エージェントレジストリで宣言された鍵で署名する。*3.0 ですべての認証操作に推奨。3.1+ で変更 / 金融操作に必須。* * **mTLS** — バイヤーは登録されたドメインに解決するクライアント証明書を提示する。*3.0 と 3.1+ で任意の操作に許可。* * **Bearer トークン**(事前プロビジョニングされた API キーまたは JWT) — オンボーディング時にセラーが発行、バイヤーにマップ。*3.0 では実効的なベースラインとして許可。**3.1+ で変更 / 金融操作に禁止**、以降読み取り専用。* 規範的マトリクスと検証者ルールは [Authentication](/docs/building/by-layer/L2/authentication#authentication-method) にあります。変更操作での bearer の 3.0 → 3.1 サンセットは [既知の制限](/docs/reference/known-limitations#authentication-and-identity) の下にログされています。 **これが防御するもの。** 攻撃者は `iss` フィールドや `caller` ヘッダーを設定して Acme だと主張できません。アイデンティティは、攻撃者が偽造できないもの(秘密鍵、証明書、事前共有シークレット)にバインドされます。後続のすべての層は認証されたエージェントをそのスコープとして使います — これを誤ると残りのスタックは装飾的です。 ### Layer 2: Isolation — one agent cannot see another すべての状態 — メディアバイ、クリエイティブ、冪等性キャッシュエントリ、セッション ID、ガバナンストークン — は、それを作成したエージェントと作業を認可した[アカウント](/docs/reference/glossary#a)にスコープされます。スコープを忘れるクエリはテナントをまたいでデータを漏らします。AdCP はセラーに、認証されたエージェントとその認可されたアカウントですべての読み取りをスコープし、境界をまたいで存在を漏らすのではなく汎用の「not found」を返すよう要求します。 実装は通常これをデータベース層で強制するため(Postgres row-level security が正準パターン)、1 つのハンドラーのバグが壁を突き抜けられません。 テナント境界内で、すべての呼び出し元が同じ付与を得るわけではありません。セラーはあるエージェントに完全なメディアバイスコープを、別のエージェントに狭い read + webhook-attach スコープ(例: AAO Verified コンプライアンスエンジンの [`attestation_verifier`](/docs/accounts/overview#standard-named-scope-attestation_verifier) スコープ)を発行できます。呼び出し元は [`sync_accounts`](/docs/accounts/tasks/sync_accounts) と [`list_accounts`](/docs/accounts/tasks/list_accounts) レスポンスのアカウントごとのエントリに添付された `authorization` オブジェクト経由で自身の付与を発見します。セラーはローカルで強制し、スコープ外リクエストを `SCOPE_INSUFFICIENT`、`READ_ONLY_SCOPE`、または `FIELD_NOT_PERMITTED` で拒否します。 **これが防御するもの。** 競争情報の漏洩。Agent A として認証された攻撃者は、Agent B のメディアバイ、クリエイティブ、冪等性キーをプローブできません — ID 推測でも、タイミングサイドチャネルでも、エラーメッセージの差分でもなく。狭いスコープで正当に認証されたエージェントは、付与されなかったタスクやフィールドにエスカレートできません。 ### Layer 3: Idempotency — at-most-once execution すべての変更 AdCP リクエストは必須の [`idempotency_key`](/docs/reference/glossary#i) を運びます。セラーは、宣言されたリプレイ TTL(最小 1h、推奨 24h、最大 7d)で、認証されたエージェントにスコープされたそのキーの下に最初の成功したレスポンスを保存します。同じキーと同じペイロードのリトライは、キャッシュされたレスポンスを返し `replayed: true` とマークします。同じキーの下の *異なる* ペイロードのリトライは `IDEMPOTENCY_CONFLICT` で拒否されます。 これがリトライを安全にする制御です。それなしでは、`create_media_buy` のネットワークタイムアウトが、バイヤーに二重予約(リトライ)と正当なバイの放棄(リトライしない)の選択を強います。それがあれば、同じバイトは常に同じ結果を生みます — 正確に一度。 **これが防御するもの。** リトライからの二重予約。盗まれて再利用されたリクエストからのリプレイ攻撃。エージェントの副作用からの重複 webhook(「キャンペーン作成!」通知、下流ツール呼び出し、LLM メモリ書き込み)。`replayed: true` は、レスポンスが新しいイベントを表すかキャッシュされたものかをすべての下流システムに知らせます。 ペイロード正準化とエラータクソノミーのオラクル耐性プロパティを含む完全な規範的ルールは [Request Safety](/docs/building/by-layer/L1/security#冪等性) にあります。 ### Layer 4: Signed governance — cryptographic proof of approval プランが支出に承認されるとき、ガバナンスエージェントは署名付き JWS トークンを発行します — 共有シークレットでも、不透明な cookie でもなく、次にバインドされた公開鍵検証可能なアテステーション: * **`sub`** — 認可される特定のプラン * **`aud`** — それに作用することを許された特定のセラー * **`phase`** — これが intent、purchase、modification、delivery のいずれか * **`exp`** — 認可が期限切れになるとき(intent には 15 分、execution には 30 日以下) * **`jti`** — リプレイ重複排除に使われる一意のトークン ID セラーは JWKS 経由でガバナンスエージェントの公開鍵をフェッチし、署名を検証し、15 ステップの検証チェックリストを実行し、その後にのみリクエストを承認済みとして扱います。監査者と規制当局は、同じ公開鍵を使って何年も後に同じトークンを検証できます — バイヤーもセラーも遡及的に承認を偽造できません。 **これが防御するもの。** 認可されていない支出。侵害されたバイヤー認証情報だけではメディアバイを作成できません — 攻撃者はまた、バイヤーのガバナンスエージェントによって署名され、この特定のセラーに、この特定のプランに、この特定の操作に、その有効ウィンドウ内で(`iat`/`nbf`/`exp` に ±60s のクロックスキュー許容。正確な境界は [実装リファレンス](/docs/building/by-layer/L1/security#署名付きガバナンスコンテキスト) を参照)バインドされた、`jti` が以前に見られていない有効で失効していないガバナンストークンを必要とします。 ### Layer 5: Auditability — the trail survives the transaction すべてのプロトコルイベントは、構造化され相関したレコードを生成します: 署名付きガバナンストークン、`idempotency_key` とその `replayed` フラグ、リクエスト ID チェーン、そして — ガバナンス制御イベントについては — 失効可能な監査ログポインター。これらは `get_plan_audit_logs` 経由で *監査者がクエリ可能* で、バイヤーやセラーにプライベートではありません。 主要なプロパティ: * **失効。** ガバナンスエージェントは well-known パスに署名付き失効リストを公開します。侵害された鍵と撤回されたプランは、リストを提供する CDN を信頼せずに無効化できます。 * **保持。** 失効した公開鍵は 7 年以上発見可能なままなので、履歴トークンはローテーション後も検証可能なままです。 * **承認来歴。** ガバナンスアテステーションは署名され公開鍵検証可能なので、アーティファクトを保持する任意の当事者は、それが述べられた時刻に署名鍵の保持者によって承認されたことを検証できます。これは否認防止に近づきます — しかし条件付きでのみ。バイヤーは後でプランが *決して承認されなかった* と主張できません、次の限り: (a) 署名鍵が署名時に侵害されていなかった(失効リストがこれを束縛する — 「署名したとき鍵は既に盗まれていた」の事後的主張は失効タイムラインに対して反証可能)、かつ (b) 署名者がその署名鍵の通常の管理を保持する。セラー側は *より弱い*: アテステーションはプランが存在したことを証明し、配信されたか承認されたことではありません。完全な双方向否認防止のため、セラーは `adcp_use: "request-signing"` 鍵を使って `{plan_id, received_at, plan_sha256}` をバインドする署名付き `plan_receipt` を署名付き webhook として発すべきです — 永続的なセラー側の承認は、他のすべての webhook アーティファクトと同じ静止時 webhook パスを流れます(下の [What gets signed](#what-gets-signed--and-what-doesnt) を参照)。署名付き領収書がなければ、「決して受け取っていない」は否認可能なままです。 **これが防御するもの。** 事後の改ざん。紛争における当事者間のクレームドリフト。認証情報がローテートした後ずっと到着する規制照会。 ## What gets signed — and what doesn't 3.x には 5 つのアプリケーション層署名サーフェスが存在します — 4 つが JWKS 公開パターンを共有し、加えて TMP 独自のエンベロープ: * **インバウンドリクエスト署名。** バイヤー(およびバイヤー側クライアントとして動作するセラー)は、[RFC 9421](/docs/building/by-layer/L1/security#request-signing) でアウトバウンドツール呼び出しに署名する。鍵目的 `adcp_use: "request-signing"`。 * **アウトバウンド webhook 署名。** セラーは非同期の [webhook 配信](/docs/building/by-layer/L1/security#webhook-callbacks) — タスク完了、ステータス変更、下流イベント、任意の専門分野スコープの永続アーティファクト(ブランド権利、AAO Verified コンプライアンス、セールスインテリジェンスリレー、ガバナンス領収書) — に署名する。RFC 9421。鍵目的 `adcp_use: "request-signing"`。非推奨の `adcp_use: "webhook-signing"` キーは後方互換性のため webhook パスで受け入れられたまま。 * **ガバナンスアテステーション署名。** ガバナンスエージェントは支出を認可する JWS トークン(上の Layer 4)に署名する。RFC 9421 とは別のプロファイル、独自の鍵目的(`adcp_use: "governance-signing"`)、JWKS、失効リストを持つ。 * **指定タスクレスポンスペイロード署名。** タスクの閉じたリスト — 現在は `verify_brand_claim` とそのバルクバリアント `verify_brand_claims` — は、レスポンスボディ内に運ばれる [JWS エンベロープ](/docs/building/by-layer/L1/security#request-signing) としてそのレスポンス *ペイロード* に署名する。鍵目的 `adcp_use: "response-signing"`。署名はブランドプロトコルの方向非対称信頼モデルにとって負荷を担う。受信者はレスポンスボディをパースし、応答エージェントの公開鍵に対して JWS を検証する。これはペイロードエンベロープ JWS で、RFC 9421 §2.2.9 トランスポートレスポンス署名ではない — 後者は指定されたものを含め 3.x のどのタスクにも定義されていない。[Brand Protocol: trust model](/docs/brand-protocol/tasks/verify_brand_claim#trust-model) を参照。 * **Trusted Match Protocol エンベロープ。** TMP は独自の Ed25519 エンベロープで、TMP のリクエストごとの予算にスケールされて(約 5% でサンプル検証)マッチ時リクエストに署名する。上の 4 つのサーフェスと JWKS 公開を共有するが独自のプロファイル。[TMP signing model](/docs/trusted-match/specification#signing-model) を参照。 **同期 AdCP レスポンスはトランスポート層で署名され *ない*。** 指定タスクリスト(`verify_brand_claim` ファミリー — ペイロードエンベロープ JWS、RFC 9421 トランスポートでない)の外では、バイヤーはレスポンス署名に依存してはならない(MUST NOT)。同期応答の完全性保証は認証されたセッション内の TLS によって配信される。静止時アテステーションを必要とするアーティファクトは署名付き webhook 経由で配信されなければならない(MUST)。これは MCP `tools/call` 応答と A2A 非ストリーミングレスポンス(およびそのストリーミング `artifactUpdate` フレーム)に対称的に適用される。A2A プッシュ通知配信は既に上の 2 番目のサーフェスがカバーする署名付き webhook パスに乗る。 ### Why the split is deliberate これは 1 つの RFC 9421 署名目的を持つ 2 つの配信サーフェスであり、同期応答のカバレッジギャップではありません。 * **TLS スコープの同期 + 署名付き webhook 非同期が設計。** 同期応答は、リクエストを運んだ認証されたセッション内で消費される — バイヤーは、body-modifying CDN でリクエスト側のボディ完全性を統治する同じエッジ終端の注意点とともに、セラーの認証されたエッジが応答したという TLS バインドされた証明を保持する([Transport security: edge-termination](/docs/building/by-layer/L1/security#what-this-section-does-not-replace) を参照)。ボディの署名はエッジ後の改ざんから保護するが、認証された TLS セッションが当事者の認証されたエッジ間で既に確立したものを超えては何も保護しない。対照的に静止時完全性は webhook が目的とするもの: アーティファクトは元のトランスポートを超えて生き残り、TCP 接続よりずっと長生きする鍵に対して、セッションが閉じた後ずっと検証する。 * **Webhook のみはセラーへの強制関数。** 「このアーティファクトは静止時完全性を必要とする」を、すべての応答へのフリーライダーではなく *明示的なモデリング決定* — webhook を発する — にすることは、オペレーターを意図的な設計に押しやる。さもなければ、*どの* アーティファクトが実際にアテステーションに値するかを問わずに「完全性のため」汎用のレスポンス署名プリミティブに手を伸ばすセラーは、事前に判断を下すよう押される。 * **署名サーフェスを倍にするのは運用上脆い。** 追加の署名目的やプロファイルはすべて、JWKS の別の鍵宣言、別のローテーションサイクル、別の検証者コードパス、別の適合性グレーダー、監視する別の失効エントリです。コストはすべての採用者がすべてのデプロイで負担し、便益は webhook を通じてよりクリーンなパスを持つ狭い監査と転送のフローに帰属します。エコシステム全体でネットマイナス。 ### Cases that look like response signing but aren't * **ツール呼び出し応答の監査とフォレンジック。** 「セラーが時刻 T に X と言った」をアテストする必要があるバイヤーは、同期応答経由ではなく webhook を発するツールパス経由でアーティファクトをリクエストします。非対称性が正しい設計問題を強います: どの応答が実際に静止時完全性を必要とするか? 実際には本能が示唆するより少ない。 * **クロスエージェント転送**(セールスインテリジェンスリレー、ブランド権利ハンドオフ、AAO Verified コンプライアンスアテステーション)。これらのフローのそれぞれの永続的アーティファクトは、`adcp_use: "request-signing"` を使う標準の署名付き webhook パスに乗る — 3.x には専門分野ごとの `adcp_use` 値はなく、汎用のレスポンス署名プリミティブもない(上の閉じた指定タスクリストが唯一のレスポンスペイロード署名サーフェスで、これらのフローはそれにない)。専門分野はアテステーション可能なペイロードを自身のエージェントからの署名付き webhook として配信する。それが既に答え。 * **双方向否認防止領収書**(例: `{plan_id, received_at, plan_sha256}` をバインドするセラーの署名付き `plan_receipt`)。仕様がセラーにそのような領収書を発することを推奨する場所で、それは同じ `adcp_use: "request-signing"` 鍵目的を使う署名付き webhook として配信される — インバウンドガバナンスアテステーションを承認した同期応答ではない。 ### The request-the-webhook pattern 今日同期的にアーティファクトを返すツールからアテステーション可能なアーティファクトが本当に必要な場合、仕様がサポートするパスは、正準バージョンを運ぶ署名付き webhook を発するようツールを構造化し、同期応答をトランスポートのみの承認として扱うことです。バイヤーはリクエストに webhook を登録します。セラーは `adcp_use: "request-signing"` でその webhook 経由で永続的アーティファクトを配信します(非推奨の `webhook-signing` キーは互換性ウィンドウ中まだ受け入れられる)。検証は他のすべての静止時セラー対バイヤーメッセージと一様で、新しい専門分野なし、新しいグレーダーなし。 今日永続的アーティファクトを同期的に返す一部の 3.x ツール(例: 同期応答で `rights_constraint` と `generation_credentials` を返す `acquire_rights`、またはセラー側の領収書が現在インラインで配信される任意のツール)は、このパターンの下で再構造化するか、その永続的完全性パスが webhook バリアント — 将来の同期応答署名ではない — であることを受け入れるかの候補です。 この決定は [#3737](https://github.com/adcontextprotocol/adcp/issues/3737) の解決です。脅威モデルが進化すれば 4.0 で再検討可能(例: 同期応答が webhook も流れない永続状態を運ぶトランスポートパターンが現れる)。 ## What to verify before going live AdCP デプロイを承認している場合 — ブランド CISO、パブリッシャーのセキュリティアーキテクト、代理店の IT リードとして — これらはチーム(またはベンダー)に尋ねる質問です。それぞれが上の層の 1 つにマップします。 ### Identity * [ ] 呼び出しエージェントはどう認証されるか?(RFC 9421 署名付きリクエスト、mTLS、または Bearer/API キー — ヘッダーフィールドでも `iss` でもない。変更 / 金融操作については、3.1 サンセット前に Bearer からの移行を計画 — [Authentication](/docs/building/by-layer/L2/authentication#authentication-method) を参照。) * [ ] トークンはどこに保存されるか?(KMS / シークレットマネージャー — ファイルでも静止時の env vars でもない) * [ ] ローテーションケイデンスは影響範囲に適切にサイズされ文書化されているか?(書き込み可能トークンには 24h 以下が妥当なデフォルト。スケールで支出をコミットするか組織境界を越えるトークンにはより厳しいウィンドウが適切。) * [ ] 失効パスは何か、誰が 1 時間未満でそれを実行できるか? ### Isolation * [ ] エージェント/アカウント分離はアプリケーションコードだけでなくデータベース層(row-level security)で強制されているか? * [ ] エラーメッセージはエージェントやアカウントをまたいで存在を漏らすか?(「存在しない」と「存在するがあなたのものでない」の両方に「Not found」) * [ ] 冪等性キー、セッション ID、ガバナンストークンは認証されたエージェントごとにスコープされ、テナント境界を越えて決して共有されないか? ### Idempotency * [ ] `capabilities.idempotency.replay_ttl_seconds` が宣言され、宣言された値が実装の実際のキャッシュ保持に一致するか? * [ ] 実装はビジネスロジックに触れる前に欠けているまたは不正な形式のキーを `INVALID_REQUEST` で拒否するか? * [ ] 冪等性キャッシュはインスタンス間で共有されているか(そのため再起動が黙った二重実行を許さない)? * [ ] 成功したレスポンスはキャッシュされるか?(エラーはキャッシュされてはならない、さもなければシステムがバイヤーを TTL の間ロックアウトする。) ### SSRF discipline * [ ] 当事者 URL(webhook、JWKS、adagents.json、レポートバケット)へのすべてのアウトバウンドフェッチは完全な 6 点チェックを実行するか: HTTPS のみ、予約 IP 拒否リスト、IP ピン留め、リダイレクトなし、サイズとタイムアウト上限、抑制されたエラー詳細? * [ ] 予約 IP 拒否リストは権威あるソース(IANA、クラウドプロバイダードキュメント)から列挙され、新しいクラウドプロバイダーや地域を追加するたびにレビューされるか? 現在の列挙については [実装リファレンス](/docs/building/by-layer/L1/security#webhook-url-validation-ssrf) を参照。 ### Governance verification * [ ] このエージェントが `governance_context` を受け入れる場合、15 の検証ステップすべてを実行するか拒否するか? * [ ] 失効リストは、文書化されたフェッチ失敗時の安全デフォルトで、宣言されたケイデンスでポーリングされるか? * [ ] JWKS キャッシュは失効ポーリング間隔で上に束縛されているか? ### Auditability * [ ] 完全なガバナンストークンは保持期間中逐語的に(到着したエンベロープを含め)永続化されるか? * [ ] 監査者は `jti`、`plan_id`、または認証されたエージェント識別子でクエリし、完全な管理チェーンを再構成できるか? * [ ] ログは追記専用で改ざん証拠を持つか(例: 変更可能なテーブルではなくリーガルホールド付きのオブジェクトストレージ)? ### Operational readiness * [ ] 次のランブックがあるか: 侵害された認証情報の失効、webhook シークレットのローテーション、ガバナンス鍵のローテーション、当事者へのインシデント通信? * [ ] 次の監視があるか: `IDEMPOTENCY_CONFLICT` レートスパイク(プロービング攻撃)、失敗したガバナンス検証(なりすまし試行)、単一の当事者からの SSRF 拒否、異常なクロスエージェントまたはクロスアカウントアクセスパターン、単一のピアからの 401/403 スパイク? * [ ] チームは次の少なくとも 1 つを卓上演習したか: 認証情報の盗難、ガバナンス鍵の侵害、クロステナントデータ漏洩、プロンプトインジェクション駆動の認証情報流出? * [ ] 冪等性キャッシュに特化した文書化された DR/RPO ターゲットがあるか(アプリケーションデータベースだけでなく)? キャッシュはパフォーマンスクリティカルだけでなく正しさクリティカル。 * [ ] ペネトレーションテストのケイデンスは何か、スコープは MCP と A2A サーフェス(REST だけでなく)を含むか? ### Data handling and subprocessors * [ ] エージェントのデータフローの文書化されたサブプロセッサーリストがあり、エージェントが使う LLM プロバイダーを含むか? * [ ] 各 LLM プロバイダーとの DPA は、プロンプト、ブランドアセット、ファーストパーティシグナル、クリエイティブメタデータが保持されるかモデル訓練に使われるかについて明示的か? * [ ] データレジデンシーは EU / UK / その他の地域要件を満たすよう設定可能か、設定はエージェントのケイパビリティや契約で可視か? * [ ] ログ保持はフォレンジックニーズ(セキュリティログに最低 90 日)とプライバシー義務(PII 保持の制限)の両方に整合しているか? 2 つは衝突しうる。ランブックは決定を名指しすべき。 * [ ] エージェントが LLM 駆動プランナーの場合、信頼できないテキスト(ブリーフ、ユーザーチャット、クリエイティブメタデータ)から作られたプロンプトから生じるツール呼び出しのサンドボックスモデルがあるか? 特定のプロンプトコンテキスト内からエージェントがどの URL / どのツールに到達できるかを、どのエグレス制御が制限するか? **このチェックリストの使用について。** 内部使用または NDA の下は問題ありません。完全に回答されたコピーを外部に公開すること — 特に具体的な「no」の回答を持つもの — は、敵対者にベンダーがどの制御に投資していないかのマップを与えます。完成したチェックリストを偵察に敏感なものとして扱ってください。 ## Where humans stay in the loop エージェンティック広告におけるセキュリティは、人間を除去する議論ではありません — 彼らを最も影響力があり最もレイテンシーコストの少ない場所に置く議論です。AdCP の [Embedded Human Judgment](/docs/governance/embedded-human-judgment) 原則は 5 つの負荷を担う場所を指定します: 1. **意図設定** — 人間が任意のエージェントが行動する前にキャンペーン目標、オーディエンス、予算エンベロープを定義する。 2. **境界設定** — 人間がエージェントが動作しなければならないポリシー、制約、しきい値を定義する。プランレベルの `audience_constraints` とガバナンスポリシーは、人間の判断の機械強制可能な表現。 3. **例外処理** — ガバナンスが `conditions` または `denied` を返すとき、または `TERMS_REJECTED` が着地するとき、決定は設計上人間にエスカレートする。 4. **オーバーライド権限** — 人間はいつでもアクティブなバイを一時停止、キャンセル、変更できる。プロトコルのライフサイクルタスク(`pause`、`resume`、`cancel`、`update_media_buy`)はどの状態がどの介入を受け入れるかについて明示的。 5. **監査とアカウンタビリティ** — すべての支出コミットメントは、人間が事後に検査できる署名されたリプレイ耐性のある証跡を生成する。 有用な読み方: このページのセキュリティ制御は、人間が設定した *境界* を防御します。それらは人間を置き換えません。 ## What AdCP does not do in 3.0 プロトコルが何を *しない* かを知ることは、それを評価することの一部です。正準で保守されるリストは [**既知の制限**](/docs/reference/known-limitations) にあり、セキュリティ、プライバシー、コマース、認証、ガバナンス、適合性にわたります。それがカバーするセキュリティ関連項目には次が含まれます: エンドユーザー認証なし、プロトコルレベルの侵害通知 SLA や CVD ポリシーなし、プロトコルレベルの PII トランスポートなし、LLM プロンプトインジェクション保証なし、プロトコル層のデータレジデンシーメカニズムなし、OAuth 2.1 の規範的要件なし、汎用の同期 RPC レスポンス署名なし — 指定タスクペイロードエンベロープ(`verify_brand_claim` ファミリー)を除く(上の [What gets signed](#what-gets-signed--and-what-doesnt) を参照)、クロス通貨バイサポートなし、プロトコルレベルの配信紛争フローなし、プロトコル内の支払いや決済なし。 これらのどれも隠されていません。それぞれは仕様の可視な端であり、将来の作業の候補です。 ## Trust anchors and the key-discovery gap 上のアイデンティティ、ガバナンス、ポインターファイルの層はすべて、同じ隠れた前提に依存します: 署名を検証する公開鍵が正直に発見できること。3.0 では、その発見パスはすべてのケースで当事者に根ざしています: * **RFC 9421 バイヤー鍵** — バイヤーエージェント自身のドメインまたは `.well-known` パスからフェッチされる JWKS。 * **ガバナンス JWS 鍵** — ガバナンスエージェント自身のドメインからフェッチされる JWKS。 * **エージェント署名鍵** — `brand.json` `agents[].jwks_uri` を通じてオペレーターアテストされ、変更セラー認可にはパブリッシャー `adagents.json` `authorized_agents[].signing_keys[]` ピン付き。 * **`adagents.json` 権威ポインター** — パブリッシャー自身の `/.well-known` からフェッチされ、ポインタースワップ脅威は [managed-networks security](/docs/governance/property/managed-networks#security-considerations) で文書化。 それらのステップのすべてが、当事者自身のインフラを信頼のルートとして信頼します。TLS はこれを閉じません — 証明書は攻撃者が侵害したホスト名に発行されるので、クリーンに検証します。したがって、当事者の CDN、DNS、`/.well-known` パスを制御する攻撃者は攻撃者制御の鍵を提供でき、それらの鍵で作られたすべての署名はそれらに対して検証します。 3.0 が実際に配信するのは **継続性を伴う trust-on-first-use** です: 検証者は最初に見た鍵をキャッシュし、以前の鍵セットに対してローテーションをピンし、予期しない変更でアラートします。これはハードルを上げます — 攻撃者はルーチンに見えるほど長く当事者オリジンを制御するか、被害者が何かをキャッシュする前にオンボーディング時に鍵をスワップするかしなければならない — が、ギャップを閉じません。これは 3.x 姿勢の正直な記述であり、主張された暗号学的信頼のルートではありません。 ### What raises the bar in 3.x 実装者は、任意の単一のオリジンに依存するのではなく独立したアテステーションソースを重ねるべきです(SHOULD)。下の各制御は、黙った鍵スワップを有界なウィンドウ内の検出可能なイベントに変換します: * **マルチソースクロスチェック。** 署名鍵が `brand.json` に現れるとき、署名されたエージェント応答で使われた鍵 *と* DNS ベースのアテステーション(鍵素材とロックステップでローテートされる、鍵フィンガープリントをドメインにバインドするパブリッシャーの apex の TXT レコード)に一致することを検証する。HTTPS オリジンだけの侵害は DNS も偽造しない。攻撃者は両方のサーフェスを同時に破らなければならない。 * **公開遅延 / 継続性ウィンドウ。** 一度も見たことのない鍵を、宣言された期間(24–72 h)暫定的として扱い、その間高価値操作は以前キャッシュされた鍵に対して検証され続け、ローテーションでアラートが発火する。正当なローテーションはオペレーターの承認とともにこれを生き残る。攻撃者注入の鍵は任意の支出が動く前にサーフェスする。 * **帯域外の鍵変更シグナリング。** パブリッシャー、ガバナンスエージェント、バイヤーエージェントは、当事者オリジンが偽造できないチャネル — ベンダーステータスページ、ads.txt クロス参照、パートナーアナウンスリスト、直接オペレーター通知 — を通じて鍵ローテーションをアナウンスすべき(SHOULD)。プロトコルはチャネルを規定しない。要件はチャネルが存在し検証者がそれを監視すること。 * **ローテーション有効性の規律。** 宣言されたローテーションウィンドウを過ぎた鍵は、好みのシグナルではなく攻撃サーフェスです。検証者は、古いキャッシュされた素材に黙ってフォールバックするのではなく、宣言された有効性を過ぎた鍵で作られた署名を拒否すべきで(SHOULD)、`not_after` を過去に設定するローテーションを正当なロールオーバーとして受け入れるのを拒否すべき(SHOULD)。 これらの制御は信頼のルートの代替にはなりません。それらは鍵スワップ攻撃を、黙って安価ではなく検出可能で高価にします — それが 3.x が正直に配信できるセキュリティ姿勢です。 ### What AdCP 4.0 needs: a centralized publisher-key registry 恒久的な修正は、TLS の Certificate Transparency や ad-tech アイデンティティ層の `sellers.json` に精神的に類似した中央集権レジストリです。最小のプロトコル関連プロパティ: 1. **パブリッシャー登録。** 各パブリッシャー、ガバナンスエージェント、セールスエージェントドメインが、そのドメインアイデンティティの下にルート検証鍵を登録する。レジストリは `{domain, root_key_fingerprint, enrolled_at}` をバインドし、文書化されたチャレンジ(DNS、HTTPS、または同等)を通じてドメイン制御をアテストする。 2. **追記専用ローテーションログ。** ローテーションは上書きではなく追記される。レジストリは透明性ログを公開するので、鍵ローテーションはバックデート、撤回、または異なる検証者に選択的に提供できない。 3. **公開クエリ可能性。** バイヤー、セラー、バリデーターはドメインでレジストリをクエリし、現在のルート鍵セットとローテーション履歴を受け取る。レジストリはディスカバリーインデックスで署名権威ではない — 決して秘密鍵を保持せず、任意の当事者に代わって署名を発行できない。 4. **ガバナンス中立の運用。** レジストリは、公開されたガバナンス、レジストリ自身の署名鍵の文書化された鍵儀式の透明性、任意の単一ベンダーから独立した継承計画を持つ業界団体によって運用される。 5. **後方互換のワイヤー形式。** レジストリの鍵は、検証者が既に消費するのと同じ JWKS 形式でサーフェスする。3.x 検証者のレジストリ根ざし信頼への切り替えは設定変更(JWKS ディスカバリーをレジストリインデックス URL に向ける)で、新しいプロトコルサーフェスではない。 これは **明示的に 3.x の要件ではありません。** それは 4.0 トラックとしてログされ、今日プロトコル内アテステーションサーフェス — `brand.json` `agents[].jwks_uri`、パブリッシャー `adagents.json` `authorized_agents[].signing_keys[]`、`authoritative_location`、署名付きガバナンス JWS — を構築する実装者が、後のレジストリルックアップがプロトコル破壊なしにそれをアンカーできるようデータを形作れるようにします。具体的には、実装者は鍵宣言を安定した単一目的の URI に保つべきで(SHOULD)、完全な鍵素材と並んで鍵フィンガープリントを運ぶべきで(SHOULD)(レジストリは曖昧なく識別できるものしかアンカーできない)、署名鍵とトランスポート鍵を混同すべきではありません(SHOULD NOT)。 レジストリが存在するまで、上のマルチソース制御が 3.x 規範的ベースラインです。それらは「1 つの当事者オリジンを侵害する攻撃者が黙った権限を得る」と「侵害が有界なウィンドウ内の検出可能なシグナルを生成する」の違いです。3.x は後者を約束し、前者を約束しません。 ## What is outside the protocol AdCP はワイヤーを仕様化します。次のいずれも仕様化せず — また代替もできません: * **シークレットストレージ。** KMS、Vault、Secrets Manager、または同等物を使う。プロトコルコンプライアンスは、コミットされた `.env` ファイルに座るトークンを魔法のように保護しない。 * **エンドポイント堅牢化。** あなたのエージェントはパブリックインターネット上のサービス。WAF、レート制限、DDoS 保護、TLS 設定、OS パッチ、依存関係スキャン — すべてあなた次第。 * **監視とインシデント対応。** プロトコルは監視する価値のあるシグナル(冪等性衝突、ガバナンス失敗、SSRF 拒否)を発する。それらを検出し対応するのはあなたの運用チームの仕事。 * **人間の制御。** 承認しきい値、支出上限、一時停止権限 — これらはプロトコルではなく、あなたのエージェントやガバナンスプラットフォーム内のポリシー設定。 * **物理的および人的セキュリティ。** 誰が本番に触れられるか、誰がブレークグラス認証情報を保持するか、誰が main にプッシュできるかについての通常の制御。 AdCP をドアの錠を仕様化するものと考えてください。あなたは依然として建物を所有します。 ## Further reading * **[Security(実装リファレンス)](/docs/building/by-layer/L1/security)** — HMAC、冪等性、SSRF、エージェント/アカウント分離、ガバナンス検証の規範的ルール * **[Embedded Human Judgment](/docs/governance/embedded-human-judgment)** — 実際の帰結を持つ決定で人間をループに保つ 5 つの原則 * **[Trusted Match Protocol](/docs/trusted-match)** — 配信時に構造的プライバシー分離を配信する 2 呼び出し分解(Context Match / Identity Match) * **[Webhooks](/docs/building/by-layer/L3/webhooks)** — 署名形式、リプレイウィンドウ、ローテーション * **[署名付きガバナンスコンテキスト](/docs/building/by-layer/L1/security#署名付きガバナンスコンテキスト)** — 15 ステップの検証チェックリスト * **[エージェントの運用](/docs/building/operating/operating-an-agent)** — 運用上の関心事としての認証情報管理、監視、インシデント対応 * **[エージェントの通信方法](/docs/building/concepts/how-agents-communicate)** — `adagents.json`、`brand.json`、ディスカバリー信頼チェーン # 既知の仕様の曖昧さ Source: https://adcp-docs-ja.pier1.co.jp/docs/building/cross-cutting/known-ambiguities コンプライアンステストに影響する未解決の AdCP 仕様ギャップ — 回避策と issue リンク付き。基底の issue がクローズされるとエントリは削除される。 このページは、現在ストーリーボード適合性に影響する仕様ギャップを列挙します。各エントリは 3 つのパターンの 1 つをカバーします: ベクターが 1 つの結果をアサートする仕様の `MAY` ブランチ、スキーマがまだ要求しないがストーリーボードがアサートするレスポンスフィールド、またはベクターがリファレンス SDK を通じてプローブするのを妨げるテストインフラの癖。 **エントリは、タグ付けされた `@adcp/sdk` または仕様リリースで修正が出荷されるまで持続します** — GitHub issue のクローズは削除トリガーではありません。なぜなら、修正に先行する SDK バージョンの実装者は依然として症状に遭遇するからです。各エントリの回避策は、関連するときリリースゲートを名指しします。ここのエントリがあなたが見ているものと一致しない場合、最新の状態について GitHub issue を確認してください — 修正がまだ引いていないリリースに着地したかもしれません。 ## このページの使い方 ストーリーボードが仕様準拠と信じる動作で失敗する場合、ストーリーボード名またはアサーションテキストをこのページで検索してください。各エントリは、ギャップ、ブロッカーを越えさせる回避策、修正を追跡する issue を記述します。エントリは、issue がクローズされたときではなくタグ付けされたリリースで修正が出荷されたときに削除されます — 権威ある状態については、このページをリンクされた issue と SDK / 仕様リリースノートとペアにしてください。 反対方向 — このリストに **ない** クリーンな修正のある失敗 — については、[ストーリーボードトラブルシューティングガイド](/docs/building/operating/storyboard-troubleshooting) を参照。 ## 現在の曖昧さ ### `check_governance` `conditions` フィールド形状 * **スキーマ**: `check-governance-response.json` は `conditions[]` アイテムを `{ field, required_value?, reason }` と定義し、`field` と `reason` を必須とします。`status: conditions` ステータスは今や `minItems: 1` の `conditions` を要求します。 * **解決**: [#2603](https://github.com/adcontextprotocol/adcp/issues/2603)。スキーマ厳格化は、このエントリの削除に続くプロトコルパッチリリースに着地します。 * **回避策(修正を引くまで)**: すべての `status: conditions` レスポンスで正準の `{ field, reason }` 形状の `conditions[]` を発します。散文の説明に従うエージェントは既にこれをします。スキーマ厳格化は強制を機械的にするだけです。 ### 非 OAuth エージェントに必要な PRM * **ストーリーボード**: `universal/security.yaml` フェーズ `oauth_discovery` + `mechanism_required`。 * **ギャップ**: RFC 9728 / RFC 8414 プローブがデフォルトですべてのエージェントに対して実行されます。API キーのみのサンドボックスは、プローブを「通過」するために偽の発行者 URL を立てていて、それはスキップするより悪いです。 * **解決**: [#2606](https://github.com/adcontextprotocol/adcp/issues/2606) と [#5042](https://github.com/adcontextprotocol/adcp/issues/5042) — ストーリーボードの物語は今や、静的認証情報のみのエージェントに、テストキットで `auth.api_key` または `auth.basic` を宣言し PRM を完全に省略するよう明示的に指示します。任意フェーズのセマンティクスが `oauth_discovery` 失敗を致命的でなくします。`mechanism_required` は一致する静的認証情報パス経由で通過します。 * **回避策**: エージェントに OAuth 発行者がない場合、`/.well-known/oauth-protected-resource/...` を提供しないでください。テストキットのプローブ認証情報を有効として受け入れるようエージェントを設定します。Bearer エージェントについては、デフォルトテストキット(`acme-outdoor`)が `demo-acme-outdoor-v1` でプローブし、エージェントは本番鍵と並んでその値を受け入れなければなりません。Basic エージェントについては、`auth.basic.username`/`auth.basic.password` または `auth.basic.credentials` を持つテストキットを提供します。これは `test_kit.auth.api_key` または `test_kit.auth.basic` のいずれかを満たし一致する静的認証情報フェーズを実行させます。それなしではフェーズがスキップされ `assert_mechanism` が `actual: []` で失敗します。具体的な修正については [ストーリーボードトラブルシューティングガイド — 静的認証情報エージェント: assert\_mechanism](/docs/building/operating/storyboard-troubleshooting#static-credential-agent-no-auth-mechanism-contributed-assert_mechanism) を参照。 ### SDK 経由の冪等性 missing-key プローブ * **ストーリーボード**: `universal/idempotency.yaml` ステップ `missing_key/create_media_buy_missing_key`。 * **ギャップ**: リファレンス `@adcp/sdk` SDK が変更タスクで `idempotency_key` を自動注入するため、「missing key rejection」をプローブしようとするベクターが missing key でエージェントに決して到達しません — ランナーがディスパッチ前に 1 つを注入します。 * **解決**: [#2607](https://github.com/adcontextprotocol/adcp/issues/2607) — ステップが `omit_idempotency_key: true` を宣言し、ランナーに自身の `applyIdempotencyInvariant` と SDK の自動注入の両方をスキップするようシグナルします。リクエストが鍵なしでエージェントに到着し、ベクターが拒否パスを正直にプローブできます。 * **回避策**: 必要なものなし — 既存の仕様要件を尊重する(変更タスクで欠けている `idempotency_key` を `INVALID_REQUEST` または `VALIDATION_ERROR` で拒否)。 ### ストーリーボードがアサートするレスポンススキーマフィールド * **ストーリーボード**: `sales_catalog_driven`(カタログ数)、`creative_ad_server`(pricing\_options)、`media_buy_seller/inventory_list_targeting`(property\_list エコー)、`creative_ad_server`(vendor\_cost 必須)。 * **ギャップ**: ストーリーボードベクターがアサートするものとレスポンススキーマが要求するものの間の歴史的ドリフト。 * **解決**: [#2604](https://github.com/adcontextprotocol/adcp/issues/2604)。監査完了: * `sync-catalogs-response.json` は今や `action` が `created`/`updated`/`unchanged` のときカタログエントリに `item_count` を要求。 * `property_list` / `collection_list` エコー: `packages[].targeting_overlay` 経由で既に正準。 * `list-creatives-response.json` `pricing_options`: 既に正準(配列、`minItems: 1`、アイテムは `pricing_option_id` を要求)。 * `report-usage-request.json` `vendor_cost`: 既に必須。 * **回避策**: すべての非失敗/非削除のカタログエントリに `item_count` を発します。準拠エージェントは既にこれをします。スキーマ厳格化が `response_schema` 検証でギャップを捕まえます。 ### ブランドプロトコルの権利保持者対広告主 `brand_id` * **ストーリーボード**: `specialisms/brand-rights/index.yaml` フェーズ `identity_discovery` + `rights_search`。 * **ギャップ**: `get_brand_identity.brand_id` は広告主(例: `acme_outdoor`)を識別します。`get_rights.brand_id` は検索を特定の権利保持者ブランド(例: `daan_janssen` のようなタレント)にスコープします。同じフィールド名、異なるエンティティ — #2627 修正の前はストーリーボードが広告主 id を権利保持者フィルターに通し、準拠エージェントは空の権利を返す(失敗)か「一致なしのとき全返し」フォールバック(バグをマスク)を追加しました。 * **解決**: [#2627](https://github.com/adcontextprotocol/adcp/issues/2627) — ストーリーボードは今や `buyer_brand`(互換性フィルタリング用の広告主)を送り、エージェントが完全なカタログを返すよう権利保持者 `brand_id` フィルターを省略します。 * **回避策**: `get_rights.brand_id` を権利保持者フィルターとしてのみ扱います。バイヤーの `brand.json` に対する互換性フィルタリングのため `buyer_brand` を投入します。 ### 再キャンセルエラーコード — `NOT_CANCELLABLE` 対 `INVALID_STATE` * **ストーリーボード**: `protocols/media-buy/state-machine.yaml > recancel_buy` と `scenarios/invalid_transitions.yaml > double_cancel/second_cancel`。 * **ギャップ**: `specification.mdx` §128(MAY `NOT_CANCELLABLE`)と §129(terminal-state 更新で MUST `INVALID_STATE`)の両方が `canceled` バイの再キャンセルに適用されました。state-machine-first の実装は §129 に従い `INVALID_STATE` を返し、cancellation-first の実装は `NOT_CANCELLABLE` を返しました。ベクターは歴史的に 1 つをピンしました。 * **解決**: [#2617](https://github.com/adcontextprotocol/adcp/issues/2617) / [#2619](https://github.com/adcontextprotocol/adcp/pull/2619) + [#2628](https://github.com/adcontextprotocol/adcp/issues/2628) — §129 は今やキャンセルケースを切り出します: terminal-state 更新がキャンセル試行のとき、エージェントは `NOT_CANCELLABLE` を返さなければなりません(MUST)。他の不正な遷移(canceled での pause/resume)は依然 `INVALID_STATE` を返します。両ストーリーボードは今や再キャンセルで `NOT_CANCELLABLE` をアサートします。 * **回避策**: 既に `canceled` のバイへの `canceled: true` 更新で `NOT_CANCELLABLE` を返します。terminal-state バイの pause/resume で `INVALID_STATE` を返します。キャンセル固有のコードが再キャンセルで勝ち、汎用コードが他のすべてで勝ちます。 ### ブランチセットステップグレーディング(`peer_branch_taken`) * **ストーリーボード**: `contributes_to:` フラグを共有する並列 `optional: true` フェーズを持つ任意のもの。 * **ギャップ**: 準拠エージェントは 1 つのブランチ(例: 即時成功)を選びます。他のブランチのアサーション(例: `status: pending_review`)は、エージェントが反対の動作を取ったため失敗します。#2629 修正の前のランナーは、`any_of` 集計が通過してもこれをサマリーで `× (unknown step)` としてサーフェスしました — 実装者はいないブランチをデバッグしました。 * **解決**: [#2629](https://github.com/adcontextprotocol/adcp/issues/2629) — ランナーは今や、選ばれなかったブランチステップをスキップ理由 `peer_branch_taken`(`not_applicable` とは別、後者はプロトコル/専門分野カバレッジギャップ用に予約)でグレードします。オーサリングルールについては `storyboard-schema.yaml` § "Per-step grading in any\_of branch patterns" を、正準の `detail` 形状については `runner-output-contract.yaml > skip_result.reasons.peer_branch_taken` を参照。 * **回避策**: ランナーが予期しないブランチ失敗をレポートする場合、ピア optional フェーズが同じ `contributes_to` フラグに寄与したかを確認してください。そうなら、選んだブランチで準拠しています — ランナーは #2629 更新が必要です。 ### 仕様準拠の `sample_request` を上書きする SDK リクエストビルダー * **ストーリーボード**: `sales_catalog_driven` `optimization_loop/provide_feedback`、`@adcp/sdk` コンプライアンスランナー経由で公開。 * **ギャップ**: ストーリーボードの `sample_request` は `provide-performance-feedback-request.json` スキーマに従い `performance_index`、`metric_type`、`feedback_source` を正しく宣言します。しかし `@adcp/sdk` の内部 `request-builder.js` に、ペイロードを非仕様の `feedback: { satisfaction, notes }` 形状で置き換える `provide_performance_feedback` のハードコードされた上書きがあったため、準拠エージェントは `INVALID_REQUEST` で拒否しベクターに失敗しました。 * **解決**: 上流 [adcontextprotocol/adcp-client#689](https://github.com/adcontextprotocol/adcp-client/issues/689) + [#2626](https://github.com/adcontextprotocol/adcp/issues/2626) — 上書きを削除しストーリーボードの `sample_request` にペイロードを駆動させます。 * **回避策**: adcp-client#689 修正を含む `@adcp/sdk` リリースに上げます。それまで、`provide_performance_feedback` ベクターは、リクエストを仕様スキーマに対して検証する任意のエージェントで失敗します。 ## 曖昧さがリストにないとき 仕様が曖昧に残すと信じる動作でブロックされているがこのリストにない場合、[adcontextprotocol/adcp](https://github.com/adcontextprotocol/adcp/issues/new) で issue を開いてください。ストーリーボード、ベクターのアサーションテキスト、選んだ準拠ブランチ、なぜ仕様が許すと信じるかを含めてください。最速の解決は、特定の仕様段落と特定のベクターアサーションを引用する issue から来ます — それはメンテナーが既存の修正を指すか、ギャップを確認してスケジュールするのに十分です。 # AdCP スタック Source: https://adcp-docs-ja.pier1.co.jp/docs/building/cross-cutting/sdk-stack AdCP 実装者のための層リファレンス。5 つの層(L0 ワイヤー、L1 署名、L2 認証、L3 プロトコルセマンティクス、L4 ビジネスロジック)、各層が何を含むか、各層の SDK が何を提供すべきか、SDK がどうバージョンドリフトを吸収するか、「一から」が実際に何にサインアップするか。 **AdCP はトランザクション/制御プレーンと独自の配信時実行層にまたがります。** プランニング、ディール作成、クリエイティブ提出、レポートは AdCP の MCP/A2A タスクサーフェスを使います。事前交渉されたパッケージのインプレッション時アクティベーションは [Trusted Match Protocol](/docs/trusted-match) を使い、オークションとレンダリングは依然として RTB や VAST のような隣接プロトコルに位置します。ほとんどの AdCP タスクレイテンシー予算は秒でしばしば設計上非同期です。TMP はミリ秒の配信時決定のために構築された AdCP サーフェスです。 AdCP エージェントを構築するために座るときの最初の問いは、**エンジニアリング時間をどこに費やしたいか** です。バイヤーの `create_media_buy` があなたのビジネスロジックに到達するまでに、それは 5 つの distinct な層 — ワイヤー形式、署名、認証、プロトコルセマンティクス、そして最後にあなたが実際に構築したいもの — を越えています。低く始めるほど、スタックのより多くを所有します。 このページはそれらの層、各層で SDK が提供するもの、どちらにせよあなたが書くために残されるものを説明します。チームに合う入口を選ぶために使ってください — エージェントを差別化する L4 ロジックに集中できるよう SDK にプロトコルサーフェスを吸収させるか、特定の理由があってより低く行くか。下のコスト分解([コンポーネントごとの L3 内訳](#why-sdks-matter-more-in-adcp-than-in-eg-http)、[バージョン適応](#version-adaptation))は、どちらの選択も意図的にするためにあります。 層の前にフレーミングについて 2 つのノート: * **プロトコルサーフェスは成長した。** AdCP 3.0 は [実質的な L3 の底](/docs/building/cross-cutting/version-adaptation#what-changed-at-l3-in-3-0) を追加しました — 必須の冪等性、公開されたライフサイクルステートマシン、適合性テストサーフェス、ベースラインとしての RFC 9421 署名、拡張エラーカタログ。以前のバージョンに対して最後に SDK を評価したなら、「SDK がすること」と「自分で書くもの」の間の線は動きました。 * **AdCP は外からは薄いプロトコルに見える。** 内側からは、実装者が初読で予期するより多くの L3(ステートマシン、冪等性、非同期タスクコントラクト、エラーセマンティクス、適合性)を持ちます。このページの分解は、L3 の見積もりが事前に可視になるよう存在します。 対象読者: 任意の言語の AdCP 実装者 — エージェントを構築、SDK を作成、または評価しているか。 ## The five layers 同じ 5 つの層が AdCP 会話の両側 — **エージェント(サーバー)** と **呼び出し元(クライアント)** — に存在します。しかし作業は非対称です: エージェントはプロトコルを **強制** し(ステートマシン、冪等性、エラーセマンティクス、適合性サーフェス、webhook 発出)、呼び出し元はそれを **消費** します(状態を読む、冪等性キーを供給、エラーを扱う、webhook を受け取る)。L0(ワイヤー)と L1(署名)はほぼ対称。L2(認証)と特に L3(プロトコルセマンティクス)がサーフェスが分岐する場所です。L4 は両側に存在しますが、異なる形状です — エージェントの L4 はそのインベントリと決定、呼び出し元の L4 はそのプランニングと購買ロジック。 このページがエージェント形状の用語で層を記述するとき、末尾の *Client side* ノートを探してください — それが(通常より小さい)呼び出し元側のサーフェスを名指しします。ページごとのコストコメンタリー、L3 人月見積もり、適合性の議論のほとんどはサーバー側を記述します。呼び出し元の構築は L2–L3 で意味あるほど軽い。なぜなら作業のほとんどはプロトコルを消費することで、強制することではないからです。 **呼び出し元のみ?** 下の各層の *Client side* ノートをざっと読み、次にコスト比較のため [Server vs client at each layer](#server-vs-client-at-each-layer) にジャンプ。 ```mermaid theme={null} %%{init: {"flowchart": {"htmlLabels": true, "wrappingWidth": 9999}, "themeVariables": {"fontSize": "14px"}}}%% flowchart TB subgraph yours["yours, always"] L4["L4 — Business logic
Inventory forecasting · pricing · creative review · upstream ad-server calls
(GAM / FreeWheel / Kevel / your decisioning engine)
The agent's competitive surface — what makes your agent yours"] end subgraph sdk["what an AdCP SDK provides"] direction TB L3["L3 — Protocol semantics
Lifecycle state machines · idempotency · error catalog · transition validation
Async-task contract · webhook emission · conformance surface · response envelope"] L2["L2 — Auth & registry
Agent identity verification · brand resolution · AAO bridge
Multi-tenant account resolution · principal scoping · sandbox-vs-live routing"] L1["L1 — Identity & signing
RFC 9421 HTTP message signatures · public-key registries
Signature verification · replay-window enforcement · key rotation"] L0["L0 — Wire & transport
JSON-over-HTTP framing · MCP message envelopes · A2A SSE streams
JSON schema validation · language-native type generation"] L3 ~~~ L2 L2 ~~~ L1 L1 ~~~ L0 end L4 ~~~ L3 ``` ### L0 — Wire & transport それがすること: プロトコルバイトをワイヤーから取り出し型付きのインメモリ値に変える。スキーマ検証が不正なペイロードを入口で捕まえる。 その中にあるもの: * HTTP ルーティング(または MCP-over-stdio の stdio)。 * MCP メッセージフレーミング(`tools/call` エンベロープ、JSON-RPC 2.0)。 * A2A SSE イベントストリーム。 * 仕様の `*.json` ファイルに対する JSON スキーマ検証([Schemas](/docs/building/by-layer/L0/schemas) を参照)。 * 型生成: 仕様のスキーマから言語ネイティブ型を生成し、アプリケーションコードが静的にチェックされる。 L0 のみを持つなら、パーサーを持ちます。バイヤーの `create_media_buy` はあなたのスタック上の型付きオブジェクト — そして他のすべてを自分でしなければなりません。 *Client side:* 同じプリミティブ、鏡映方向。クライアントはアウトバウンドリクエストを同じスキーマに対してシリアライズし、同じ型生成パイプラインを通じてレスポンスを消費する。L0 は本質的に対称。 ### L1 — Identity & signing それがすること: リクエストがヘッダーが主張する者から来たこと、ボディが転送中に変更されなかったことを暗号学的に検証する。[Security model](/docs/building/concepts/security-model) と [実装プロファイル](/docs/building/by-layer/L1/security) を参照。 その中にあるもの: * RFC 9421 HTTP メッセージ署名(`Signature-Input`、`Signature` ヘッダー)。 * エージェントレジストリからの公開鍵解決(またはオペレーター公開の JWKS)。 * 正準化されたリクエストに対する署名検証。 * リプレイウィンドウ強制(`created` / `expires` パラメーター)。 * 鍵ローテーション: 飛行中のリクエストを落とさずに `keyid` 変更を扱う。 L0+L1 を持つなら、誰があなたを呼んでいるかを知ります。まだ彼らが *何を* することを許されているかを知りません。 *Client side:* 自身の鍵でアウトバウンドリクエストに署名。エージェントからの webhook コールバックを検証。同じ RFC 9421 + リプレイウィンドウ + 鍵ローテーションのプリミティブ、すべてのリクエストの代わりに 1 つのインバウンドパス(webhook)だけ。 ### L2 — Auth & registry それがすること: 検証されたアイデンティティをスコープされたプリンシパルに変える — どのバイヤー、どのブランド、どの広告主アカウント、どの sandbox 対 live ティア。[Accounts](/docs/accounts/overview) と [Calling an agent](/docs/protocol/calling-an-agent) を参照。 その中にあるもの: * エージェントレジストリルックアップ(公開された [エージェントカード](/docs/protocol/calling-an-agent) からエージェントメタデータを解決)。 * ブランド解決: [Brand Protocol](/docs/brand-protocol) 経由でリクエストエージェントをバイヤーブランド / 広告主アイデンティティにマッピング。 * AAO([AgenticAdvertising.org](https://agenticadvertising.org))ブリッジ: エージェントのメンバー組織、AAO Verified バッジ、レジストリ可視性を解決 — [Registering an agent](/docs/registry/registering-an-agent) と [AAO Verified](/docs/building/verification/aao-verified) を参照。 * マルチテナントアカウント解決: 同じワイヤーリクエストがプリンシパルに応じて異なるアカウントにマップする。 * Sandbox 対 live アカウントフラグ付け — [Sandbox](/docs/media-buy/advanced-topics/sandbox) を参照。 * 権限スコーピング: このプリンシパルがどの AdCP ツールを呼ぶことを許されるか。 L0+L1+L2 を持つなら、何かをしようとする検証されスコープされたプリンシパルを持ちます。まだ *その何か* が現在の状態で合法かを知りません。 *Client side:* 小さなサブセット。クライアントは自身のアイデンティティ(エージェントカード、ブランドドメイン)を公開し、レジストリ経由で呼んでいるエージェントをルックアップし、認証情報を提示する。マルチテナントルーティングなし、プリンシパルスコーピングなし、強制する sandbox/live 境界なし — クライアント *が* プリンシパルで、どのエージェントと話すかを選ぶ。 ### L3 — Protocol semantics それがすること: AdCP が *何を意味するか* を強制する。ワイヤー形状は整形式(L0)、呼び出し元は本物(L1)で認可済み(L2)。今: リクエストは世界の現在の状態を考慮して合法か? その中にあるもの: * **ライフサイクルステートマシン** — `MediaBuy`([リファレンス](/docs/media-buy/media-buys/lifecycle))、`Creative`、`Account`、`SISession`、`CatalogItem`、`Proposal`、`Audience`。それぞれが仕様で定義された合法エッジを持つ。 * **遷移検証** — リソースごとに合法エッジを強制。それを禁じる状態へのキャンセル試行に `NOT_CANCELLABLE`、他の不正な移動に `INVALID_STATE` を発する。試みられたアクションがキャンセルのとき、キャンセル固有のコードが汎用のものより優先する。 * **冪等性** — すべての変更ツールで `idempotency_key` 必須。同じキーが TTL 内でキャッシュされたレスポンスをリプレイ。クロスペイロード再利用が `IDEMPOTENCY_CONFLICT` で失敗(盗まれた鍵の read-oracle 脅威モデルに従いペイロードエコーなし)。[冪等性プロファイル](/docs/building/by-layer/L1/security#idempotency) を参照。 * **エラーコードカタログ** — リカバリーセマンティクス(`transient` / `correctable` / `terminal`)を持つコード。正しいコードの選択は仕様コントラクトの一部。[Error handling](/docs/building/by-layer/L3/error-handling) を参照。 * **非同期タスクコントラクト** — 同期的に完了しないツールは `task_id` を返す。クライアントはポーリングまたは webhook コールバックを受け取る。タスクの終端アーティファクトが元のツールのレスポンス形状を運ぶ。[Task lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照。 * **Webhook 発出** — 状態変更がサブスクライブしたバイヤーに通知、リトライ、冪等性、署名付き。[Webhooks](/docs/building/by-layer/L3/webhooks) を参照。 * **適合性テストサーフェス** — `comply_test_controller`(サンドボックス専用)が `seed_*` / `force_*` / `simulate_*` を公開し、ストーリーボードが状態を決定的に駆動できる。[comply\_test\_controller](/docs/building/by-layer/L3/comply-test-controller) と [Conformance](/docs/building/verification/conformance) を参照。 * **レスポンスエンベロープ** — `context`、`task_id`、`status` フィールド、エラーエンベロープ形状、`adcp_version` エコー、ケイパビリティアドバタイズ。 L0+L1+L2+L3 を持つなら、完全な AdCP プロトコル実装を持ちます。まだビジネスロジックを何もしていません。 *Client side:* コンシューマー側のミラー、はるかに小さい。クライアントは遷移を強制するのではなくステートマシンを *読む*(各終端ステータスを正しく扱う)。キャッシュを維持するのではなくリトライで `idempotency_key` を *供給* する。発する正しいものを選ぶのではなくエラーコードをリカバリーセマンティクスで *分類* する(`transient` → リトライ、`correctable` → 修正して再送信、`terminal` → リトライしない)。発するのではなく非同期タスク結果と webhook コールバックを *ポーリングまたは受け取る*。公開する `comply_test_controller` サーフェスはなく、コンシューマー側で認証する適合性のラインもない。このページの後の L3 人月見積もりはサーバー側。クライアント L3 は数か月ではなく数週間のハンドラーグルー。 ### L4 — Business logic これがあなたのエージェントをあなたのものにするものです。 その中にあるもの: * 実際のアドサーバーに対するインベントリ予測。 * 価格ロジック、ディール条件、契約セマンティクス。 * クリエイティブレビューポリシー(ブランドセーフティ、フォーマットコンプライアンス)。 * GAM / FreeWheel / Kevel / Yahoo / 社内決定エンジンへの上流呼び出し。 * 最適化、ペーシング、詐欺検出 — インベントリを競合のものと差別化する何でも。 これは AdCP SDK があなたに委ねる層、**そしてこの層のみ** です。 *Client side:* L4 もあなたのもの、ただ異なる形状。呼び出し元の L4 はメディアプランニング、予算割り当て、ターゲットオーディエンス選択、ディール評価、レポート取り込み — 呼ぶエージェントであなたのバイ側アプリケーションがする何でも。仕様側リファレンスについては [Calling an agent](/docs/protocol/calling-an-agent) を参照。非対称性はスタック全体に及ぶ: エージェント L4 は *インベントリ* を、呼び出し元 L4 は *需要* を差別化する。 ## Server vs client at each layer 同じ 5 つの層、非常に異なるコスト。呼び出し元のみのビルド対エージェントビルドの作業をサイズするときこれを使います。 | Layer | Agent (server) | Caller (client) | | ------ | --------------------------------------------------------------- | -------------------------------------------------------------------------- | | **L4** | インベントリ、価格、クリエイティブレビュー、アドサーバー統合。セラーとしてあなたを差別化するもの。 | プランニング、予算、エージェント選択、レポート消費。バイヤーとしてあなたを差別化するもの。 | | **L3** | ステートマシン、冪等性、エラーセマンティクス、適合性テストサーフェス、webhook 発出を **強制**。約 3〜4 人月。 | 同じものを **消費**。状態を読む、冪等性キーを供給、エラーを分類、非同期 + webhook をポーリング/受け取る。数週間のハンドラーグルー。 | | **L2** | マルチテナントプリンシパル解決、sandbox/live 境界、ブランド解決、権限スコーピング。 | 自身のアイデンティティを公開。呼んでいるエージェントをルックアップ。はるかに小さいサーフェス。 | | **L1** | すべてのリクエストでインバウンドを検証。アウトバウンド webhook に署名。 | すべてのリクエストでアウトバウンドに署名。インバウンド webhook を検証。同じ暗号、鏡映パス。 | | **L0** | 受信 + パース + スキーマに対して検証。 | シリアライズ + 送信 + スキーマに対して検証。対称。 | 一から作る呼び出し元は L0–L3 にわたる数週間の仕事です — ハンドラーグルー、署名、レジストリルックアップ、レスポンスパース — エージェント側が要求する [3〜4 人月の L3 ビルド](#why-sdks-matter-more-in-adcp-than-in-eg-http) ではありません。このページの残りはコストが存在するためエージェント側に集中しますが、層モデルと SDK カバレッジマトリクスは呼び出し元のみのビルドにも等しく適用されます。 ## What an SDK at each layer should provide 実装者向けチェックリスト。層 L*n* のカバレッジを主張する SDK は、最低限、下のプリミティブを公開すべきです。採用者は SDK を選ぶときこれを自己評価ツールとして使い、SDK 作者はビルドターゲットとして使います。 チェックリストは **サーバー側カバレッジ** を記述します — エージェントサーフェスが SDK の価値の大部分が存在する場所です。各層での **クライアント側カバレッジ** はサブセットです: 型付きリクエストビルダー + レスポンスパーサー(L0)、アウトバウンド署名 + webhook 検証(L1)、エージェントカード公開 + レジストリルックアップ(L2)、ステートマシン *ハンドラー* + 冪等性キー生成 + エラーリカバリー分類 + 非同期結果ポーリング(L3)。フルスタック SDK は両方を出荷。 ### L0 coverage * 公開された JSON スキーマからの生成された言語ネイティブ型(リクエスト/レスポンスペアごとに 1 型、加えて共有リソース型)。 * バンドルされたスキーマに対して配線されたスキーマ検証器 — そのため採用者はスキーマロードのダンスを手書きせずにインバウンドとアウトバウンドのペイロードを検証できる。 * \{MCP, A2A} の少なくとも一方のトランスポートアダプター。理想的には両方。これらは通常、上流プロトコル SDK を再実装するのではなくラップする。 * 採用者にパスをハードコードさせずにアクティブな AdCP バージョンの正しいスキーマファイルを見つけるスキーマバンドルアクセサー。 ### L1 coverage * アウトバウンドリクエストのための RFC 9421 メッセージ署名の署名。 * `created` / `expires` のリプレイウィンドウ強制と `keyid` ベースの鍵ルックアップを含む、インバウンドリクエストの RFC 9421 検証。 * プラグイン可能な署名プロバイダー抽象: 開発用のプロセス内鍵、本番用の KMS / HSM プロバイダー。 * 採用者が完全なエージェントを起動せずに署名配線が正しいことをアサートできるテストフィクスチャまたは検証者テストハーネス。 ### L2 coverage * マルチテナントルーティングのフック付きで、認証されたプリンシパルをスコープされたアカウントに解決するアカウントストア抽象。 * 少なくとも API キーと bearer トークンの形状のための認証プリミティブ、加えてそれらを合成する方法。 * ブランド解決 / エージェントレジストリルックアップ(または SDK がネイティブに出荷しない場合は文書化された拡張ポイント)。 * 適合性テストサーフェスが本番アカウントでのディスパッチを拒否するよう SDK 境界で強制される sandbox 対 live アカウントフラグ。 ### L3 coverage * すべての仕様定義リソースのライフサイクルステートマシングラフ、仕様正しいエラーコード(`NOT_CANCELLABLE` / `INVALID_STATE` など)を発する遷移アサーションプリミティブ付き。 * クロスペイロード衝突検出と `IDEMPOTENCY_CONFLICT` エンベロープの no-payload-echo 不変条件を持つ冪等性キャッシュ。 * 非同期タスクストア + ディスパッチャー: ツールは非同期にオプトイン。SDK は `task_id` を返し、ポーリングを受け入れ、終端アーティファクトを発する。 * Webhook エミッター: 署名済み、リトライ済み、冪等。 * 解決されたアカウントが sandbox または mock モードのとき状態を決定的に駆動するよう配線された(そうでなければ拒否される)適合性テストサーフェス(`comply_test_controller`)。 * 仕様のエコーコントラクトを扱うリソースごとの永続性プリミティブ。 * 上のすべてを賢明なデフォルトで結びつけるサーバー構築エントリポイント。 ### L4 coverage 任意の SDK のスコープ外。採用者がこれを書きます。 ## SDK coverage varies 異なる言語 SDK は L0–L3 の異なるサブセットをカバーします。すべての実装者が使わなければならない単一の SDK はありません。重要なのは、実装が L3 で [適合性のライン](/docs/building/verification/conformance) に到達することで、そこに到達するのにどれだけ手書きが必要だったかにかかわらずです。 特定の言語内では、フルスタック SDK がデフォルトの出発点です。このドキュメントの層モデルは、より低く行った(特殊目的プロキシ、カスタムスタック統合)または SDK を新しい言語に移植した場合に何を再実装するかを説明するために存在し — 典型的なエージェントビルドでより低く始めることに意味ある勝利があると示唆するためではありません。 ### Current SDK coverage **Python と TypeScript がファーストクラスの言語です。** 両方が完全な L0–L4 カバレッジにコミット — TypeScript は今日 L0–L3 全体で GA、Python は 4.x サイクルを同じラインに向けて仕上げ中。**Go** は同じ方向に動き、L0 と部分的 L1 が活発に開発中。**他の言語** は今日公式ロードマップにありませんが、コミュニティ保守のポートに開かれています — 手伝いたいなら [Builders Working Group](/docs/community/working-group) と [Slack コミュニティ](https://join.slack.com/t/agenticads/shared_invite/zt-3c5sxvdjk-x0rVmLB3OFHVUp~WutVWZg) を参照。 各公式 SDK が今日出荷するもののスナップショット。この表を SDK メジャーと AdCP 仕様改訂でリフレッシュしてください。 *最終更新: 2026-05-03。* | SDK | Production GA | Beta / dev | L0 | L1 | L2 | L3 | | -------------------- | ------------- | ---------- | :-: | :-: | :-: | :-: | | **`@adcp/sdk`** (TS) | `6.9.0` | — | ✅ | ✅ | ✅ | ✅ | | **`adcp`** (Python) | `3.x` | `4.x` | ✅ | ⚠️ | ⚠️ | ⚠️ | | **`adcp-go`** | — | `v1.x` | ⚠️ | ❌ | ❌ | ❌ | 凡例: ✅ 出荷済み · ⚠️ 部分的 / 進行中 · ❌ まだ未カバー。**Production GA** は今日ピン留めすべきライン。**Beta / dev** は次のメジャーで進行中のもの。`@adcp/sdk` 6.x は完全な L0–L3 を運ぶ — 採用者は L4 のみを書く。Python 3.x は完全な L0 を持つ本番ライン。4.x 書き直し(ベータ)が L1–L3 を閉じる。Go は開発で型 + トランスポートを出荷。L1–L3 はスコープ内。インストールコマンド、パッケージエクスポート、言語ごとのギャップ詳細については [Choose your SDK](/docs/building/by-layer/L4/choose-your-sdk) を参照。 各層で「出荷済み」が何を意味するかは上の L0–L3 チェックリスト — これらの行は、公開された SDK ビルドですべてのチェックリスト項目が満たされるまで ✅ を主張すべきではありません。このスナップショットを超えたカバレッジ詳細については、各 SDK のリポジトリを参照。 形状比較の目的で、SDK が言語にかかわらず着地できる 3 つのカバレッジアーキタイプ: | Archetype | L0 | L1 | L2 | L3 | Adopter writes | | --------------- | -- | -- | -- | -- | ----------------- | | フルスタック SDK | ✅ | ✅ | ✅ | ✅ | L4 のみ | | トランスポート + 署名のみ | ✅ | ✅ | ⚠️ | ❌ | L2 + L3 + L4 | | 型のみ / 生成バインディング | ✅ | ❌ | ❌ | ❌ | L1 + L2 + L3 + L4 | ### Hosted implementations SDK とは異なる形状: インポートするライブラリではなく実行する **デプロイ可能なエージェント**。採用者はコードではなく設定する。ハンドラーコードを自分で書かずに既存システムの前に AdCP サーフェスが欲しいとき有用。 | Implementation | Maintainer | Stack | Notes | | --------------------- | ----------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------- | | **AdCP mock-server** | 仕様メンテナー | Reference | ストーリーボードが対して実行するブラックボックス AdCP エージェント。すべての言語 SDK が mock-mode トラフィックをそれに転送。[仕様適合性](/docs/building/verification/conformance) の共有インフラ。 | | **Prebid SalesAgent** | [Prebid コミュニティ](https://github.com/prebid/salesagent) | Python | オープンソースのセラー側 AdCP エージェント。パブリッシャーが AdCP 向け実装として実行。今日 L0–L3 で手書き、公式 SDK と並んで進化。 | ホストされた実装は、SDK がするのと同じ L3 適合性のラインを満たします — 仕様は実装非依存です。違いは運用形状: ホストされた実装はデプロイして設定するサービス、SDK は自身のサービスにコンパイルするコードです。 選択はレバレッジと制御の間のトレードオフです。フルスタック SDK は最も多くのコードを無料で出荷しますが、その選択にあなたを結びつけます。トランスポートのみの SDK は最大の制御を与えますが、認証できる前に数か月の L1–L3 作業にサインアップさせます。ほとんどの本番採用者は、個々の層をスワップするオプション(カスタム署名プロバイダー、カスタムアカウントストア、カスタム冪等性バックエンド)を持つフルスタックを望みます — よく設計されたフルスタック SDK はそれをフォークではなく設定として公開します。 ## Where can you start? 任意の層で実装できます。低く始めるほど、より多くを構築します。 | Starting layer | What you write | What's done for you | | ---------------------------- | -------------- | ------------------- | | L0(一から) | 5 つすべての層 | なし | | L1(JSON-over-HTTP ツールキットを持つ) | L1+L2+L3+L4 | L0(パーサー、スキーマ検証) | | L2(ライブラリ経由で HTTP 署名を持つ) | L2+L3+L4 | L0+L1 | | L3(認証/レジストリライブラリを持つ) | L3+L4 | L0+L1+L2 | | L4(フルスタック AdCP SDK を使う) | L4 のみ | L0+L1+L2+L3 | フルスタック AdCP SDK はあなたを L4 に持ち上げます。あなたは上流呼び出しを実装します。SDK はそれらの周りにプロトコルエンベロープを通します。チームの付加価値が L4 差別化なら 1 つを選び、特定の理由があるならより低く構築する — そして L1–L3 のスコープを正直に予算化してください。 構築しているものに基づいて入口を選ぶ短い決定ページについては [Where to start](/docs/building) を参照。 ## Why SDKs matter more in AdCP than in (e.g.) HTTP 一般的な比較: *「HTTP はプロトコルだ。人々は常に HTTP サーバーを一から構築する。なぜ AdCP は違うのか?」* 答えは層 L3 です。HTTP のプロトコルセマンティクスは最小 — メソッド、ステータスコード、ヘッダー。一から作る HTTP サーバーは既製のパーサーで週末に出荷できます。 AdCP の L3 は大きい: * **ステートマシン** — 公開されたライフサイクルグラフを持つ 7 リソースタイプ。 * **非同期タスク** — すべての変更ツールが同期または非同期になりうる。どの終端アーティファクトがタスクを閉じるかのコントラクトは非自明。 * **冪等性** — キャッシュ、リプレイ、衝突、TTL — すべて正しく配線。 * **エラーカタログ** — リカバリー分類を持つコード。誤ったものを選ぶと [適合性](/docs/building/verification/conformance) に失敗。 * **適合性テストサーフェス** — ストーリーボードが [`comply_test_controller`](/docs/building/by-layer/L3/comply-test-controller) ツール経由で状態を駆動。非自明なコントローラーサーフェスを出荷する。 * **Webhook 発出** — 署名済み、リトライ済み、冪等。 一から作る AdCP エージェントは、任意の L4 差別化の前に **L3 作業だけで約 3〜4 人月** です。1 人のシニアエンジニアが mock-mode 適合性のラインまでの内訳は、おおよそ: | L3 component | Honest estimate | | -------------------------------------------------------------------------------- | -------------------- | | 7 つのライフサイクルステートマシン(エッジを定義、遷移を検証、正しい `NOT_CANCELLABLE` / `INVALID_STATE` コードを発する) | 各約 1 週間 = **6〜7 週間** | | 冪等性キャッシュ(クロスペイロード衝突検出 + no-payload-echo 不変条件) | **1 週間** | | 非同期タスクストア + ディスパッチャー(ツールごとの正しい終端アーティファクトコントラクト) | **1〜2 週間** | | エラーコードカタログ配線(リカバリー分類、コード優先度) | **1〜2 週間** | | `comply_test_controller` 適合性サーフェス(`seed_*` / `force_*` / `simulate_*`) | **1〜2 週間** | | Webhook 発出(署名済み、リトライ済み、冪等、重複排除キー付き) | **1 週間** | | RFC 9421 署名 + 検証 + リプレイウィンドウ + 鍵ローテーション(L1 として別途カウントされるが、通常同じスコープにバンドル) | **2〜3 週間** | | 統合、適合性デバッグ、仕様の再読 | **2〜3 週間** | それは **約 14〜18 週間** で、チームの HTTP メッセージ署名とライフサイクルモデリングへの習熟に依存します。見積もりは **バージョン適応作業を除外** します — ツール、エッジ、エラーコードを追加するすべての仕様改訂が、あなたが永遠に運ぶ変換マトリクスに行を追加します。SDK 採用者はそれらを無料で得ます。一から作る実装者はすべてのリリースでそれらを払います。 これは 1 エンジニアから mock 適合性までの見積もりです。**パブリッシャー / 大規模プラットフォームスケールでは、SRE、セキュリティレビュー、既存の鍵インフラとの KMS / HSM 統合、負荷テスト、オンコール負担のため約 2 倍〜3 倍を掛けてください** — そのどれも L3 仕様作業ではなく、すべてがサーフェスが本番グレードになる前の実際のコストです。 「一から」は L0(ワイヤー形状)が視野の唯一の層のとき安く読めます。L3 が実際のスコープが隠れる場所です — 上の表は、チームがどちらにせよコミットする前に指すものです。 ## Version adaptation 3 つの「バージョン」軸が同時に動き、SDK の仕事はそれらがあなたのビジネスロジック内で衝突するのを防ぐことです: | Axis | Example | What changes when it moves | | ------------------- | ------------------------ | ------------------------------ | | **仕様バージョン** | AdCP `2.5 → 3.0.5 → 3.1` | ワイヤー形状、エラーコード、ライフサイクル状態、新しいツール | | **SDK バージョン** | SDK `5.x → 6.x` | API サーフェス、人間工学、コンパイル時保証 | | **ピアバージョン(呼び出しごと)** | v3.0 のバイヤー、v2.5 のセラー | 単一の会話がバージョンを越える。ペイロードが変換を要する | 一から作るエージェントは 3 つすべてを手で扱わなければなりません。SDK は 3 つの具体的なメカニズムを出荷し、採用者がそうしないようにします: 1. **呼び出しごとの仕様バージョンピン留め。** エージェントに `adcpVersion`(または言語同等物)を設定。SDK はリクエストとレスポンスをアダプターモジュールを通し、ハンドラーコードがピアが何を話すかにかかわらず正準(現在)形状に留まる。 2. **共存インポート経由の SDK メジャー移行。** SDK メジャーを上げても同日の書き直しを強制しない — 前メジャーのサーフェスが新しいエントリポイントと並んで利用可能なまま。一度に 1 つの専門分野を移行。 3. **ワイヤーレベルネゴシエーション。** すべてのリクエストが `adcp_major_version` を運ぶ。サーバーはサポートするものを宣言し、呼び出し元が範囲外なら `VERSION_UNSUPPORTED`(リカバリー分類されたエラー)を返す。 メカニズムごとのコードレベルレシピは [Version Adaptation](/docs/building/cross-cutting/version-adaptation) にあります。仕様側のルールについては [Versioning](/docs/reference/versioning) を参照。 ### Why this matters AdCP のバージョニングは **エピソード的ではなく連続的** です。3.1 が出荷されたら、3.0 と 3.1 の呼び出し元と同時に、無期限に話します。変換アダプターなしではこれはコードベースのフォークです。それらがあればコンストラクターフラグです。 仕様自体が既にこれらの交差の 1 つをしました。**2.5 → 3.0** は実質的な L3 の底を追加しました — 正準リストについては [What changed at L3 in 3.0](/docs/building/cross-cutting/version-adaptation#what-changed-at-l3-in-3-0) を参照。一から作る 2.5 エージェントは扱いやすかった。一から作る 3.0 エージェントは上で分解された [約 3〜4 人月の L3 ビルド](#why-sdks-matter-more-in-adcp-than-in-eg-http) です。 2.5 で機能した一から作るパスは 3.0 にスケールせず、3.0 が仕様の止まる場所ではありません。SDK が存在するのは L3 が実装者が手書きできるより速く成長したからで、バージョン適応サーフェスはリリースごとに成長し続けます。 ## Where the work actually lives L3 コストが集中する 5 つの場所、おおよその桁順。一から作るビルドをスコープしているか手書きのものを再評価しているかの自己チェックとして有用: 1. **L3 が作業のほとんど。** ステートマシン、冪等性、エラーカタログ、非同期タスク — 任意の L4 差別化の前に約 3〜4 人月。コンポーネントごとの週については [分解](#why-sdks-matter-more-in-adcp-than-in-eg-http) を参照。 2. **適合性は L3 駆動。** ストーリーボードは状態遷移とエラー形状をプローブする([Conformance](/docs/building/verification/conformance) を参照)。遷移検証器なしでは、仕様がテスト失敗から再導出される。 3. **バージョニングが複合する。** ツール、ライフサイクルエッジ、エラーコードを追加する各仕様改訂は、アダプター層が運ぶ新しい変換行。アダプター層の所有は、すべてのリリースでそのマトリクスを所有することを意味する。 4. **RFC 9421 + 鍵ローテーションは独自のプロジェクト。** 署名プロバイダー、KMS 統合、リプレイウィンドウ — 実際のエンジニアリング、そのどれも L4 差別化でない。 5. **mock-server は共有インフラ。** SDK は mock-mode ディスパッチをそれに無料で配線する。手書き実装は mock-mode をスキップする(そして仕様適合性認証を失う)か再構築するか。 SDK が多くをカバーする前に構築したなら、このリストは再評価への入力 — どちらにせよの判定ではありません。[移行ガイド](/docs/building/by-layer/L4/migrate-from-hand-rolled) が、部分的スワップが価値があると決めるチームのため swap-one-layer-at-a-time パスを案内します: どの層を最初にスワップするか、注意すべき衝突モード、どの中間状態がまだ適合性を通過するか。 ## What this means for compliance 2 種類のコンプライアンス、両方が層モデルによって形作られる: * **仕様適合性(L3 プロトコルテスト)** — 実装は AdCP ワイヤーコントラクトを満たすか? ストーリーボードがステートマシンを歩き、エラーコードを実行し、非同期タスクコントラクトをテストする。採用者の上流は無関係。**mock-mode** アカウントに対して実行: エージェントがすべてのツール呼び出しをリファレンスモックサーバーに転送。これが SDK の(または手書き実装の)L3 層を認証する。 * **ライブ適合性(フルスタックテスト、計画中)** — 実際にデプロイされたエージェント(テストインフラに対する採用者の L4 コード)がストーリーボードの下でエンドツーエンドで正しく振る舞うか? **sandbox-mode** アカウントに対して実行: mock ではなく採用者のコードパスが実行される。これが L0–L3 + 採用者の上流が結合して正しいワイヤー動作を生成することを認証する。 リファレンスモックサーバーは **仕様適合性オラクル** です — ストーリーボードが対して実行するブラックボックス AdCP エージェント。すべての言語 SDK が mock-mode トラフィックを HTTP 上でそれに転送するため、リファレンスパスはエコシステム全体で共有される。mock-server は SDK 非依存: 手書きの L0–L3 実装は、自身の mock 風味のアカウントを同じ mock-server にルーティングし自身の L3 ワイヤー動作に対してストーリーボードの pass/fail を検証することで仕様適合性を通過できる。失敗が SDK または mock 自体を巻き込むときの権威チェーンとトリアージ順については、[Mock-server authority and failure triage](/docs/building/verification/conformance#mock-server-authority-and-failure-triage) を参照。 ## TL;DR * AdCP は 5 つの層を持つ。仕様は L0–L3 に存在し、あなたのエージェントは L4 に存在する。 * 「一から」は L0–L3 を自分で実装することを意味する。それは多い — [コンポーネントごとの内訳](#why-sdks-matter-more-in-adcp-than-in-eg-http) を参照。 * フルスタック AdCP SDK はあなたを L4 に持ち上げる。あなたはビジネスロジックを書き、SDK がプロトコルを扱う。異なる言語 SDK は L0–L3 の異なるサブセットをカバーする。継承したいプロトコルの量に合うものを選ぶ — [カバレッジマトリクス](#current-sdk-coverage) を参照。 * **バージョン適応は SDK 機能で、採用者プロジェクトではない。** 呼び出しごとの仕様バージョンアダプター、SDK メジャーをまたいだ共存インポート、ワイヤー上の `adcp_major_version` ネゴシエーションが、ハンドラーをフォークせずに任意のサポートバージョンのピアと話させる。手書きエージェントは変換マトリクス全体を永遠に継承する。 * コンプライアンスは 2 つの風味で来る: **仕様適合性**(mock-mode、プロトコルのみ、L3 リファレンステスト)と **ライブ適合性**(sandbox-mode、フルスタック、L0–L4 エンドツーエンド、計画中)。 * 3.0 の前に最後に SDK を評価したなら、比較は動いた — [3.0 が追加した L3 の底](/docs/building/cross-cutting/version-adaptation#what-changed-at-l3-in-3-0) を参照。覚えているものではなく今日のカバレッジに対して再評価してください。 # バージョン適応 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/cross-cutting/version-adaptation AdCP エージェントを出荷するとき 3 つのバージョン軸が同時に動く — 仕様バージョン、SDK バージョン、ピアごとのバージョン。SDK は 3 つの具体的なメカニズム(呼び出しごとのピン留め、共存インポート、ワイヤーネゴシエーション)を出荷し、採用者がハンドラーコードに変換マトリクスを運ばないようにする。 AdCP エージェントまたはクライアントを出荷するとき、3 つのバージョンが同時に動きます: | Axis | Example | What changes | | ------------------- | ---------------------- | ------------------------------ | | **仕様バージョン** | AdCP `2.5 → 3.0 → 3.1` | ワイヤー形状、エラーコード、ライフサイクル状態、新しいツール | | **SDK バージョン** | SDK `5.x → 6.x` | API サーフェス、人間工学、コンパイル時保証 | | **ピアバージョン**(呼び出しごと) | v3.0 のバイヤー、v2.5 のセラー | 単一の会話がバージョンを越える | 公式 SDK は 3 つの具体的なメカニズムを出荷し、採用者がハンドラーコードに変換マトリクスを運ばないようにします。このページはメカニズムごとのレシピです。概念的背景については [SDK スタック — バージョン適応セクション](/docs/building/cross-cutting/sdk-stack#version-adaptation) を参照。仕様側のルールについては [Versioning](/docs/reference/versioning) を参照。 ## Mechanism 1 — 呼び出しごとに仕様バージョンをピン留め 古い(またはより新しいベータ)仕様バージョンにピン留めされたピアと話す **クライアント** のときこれを使います。SDK はあなたのリクエストとピアのレスポンスをアダプターモジュールを通し、あなたのハンドラーコードが正準(現在)形状に留まるようにします。 ### 単一エージェントでバージョンをピン留め JavaScript / TypeScript(`@adcp/sdk`): ```ts theme={null} import { ADCPMultiAgentClient } from '@adcp/sdk'; const client = ADCPMultiAgentClient.simple( 'https://legacy-agent.example.com/mcp/', { auth_token: process.env.AGENT_TOKEN, adcpVersion: 'v2.5', // ← pin here }, ); const agent = client.agent('default-agent'); const result = await agent.getProducts({ brief: 'CTV inventory' }); ``` Python と Go の SDK は同じメカニズムをそのイディオム的な呼び出しサイトの下で公開します — 各 SDK のリポジトリを参照。形状は一貫しています: エージェントごとまたは呼び出しごとのバージョンピン、構築時に検証、アダプターモジュールが正準形状に/から透過的に変換。 ### 事前にバージョンを検証 `adcpVersion`(または言語同等物)は構築時に検証されます。SDK は、**スキーマバンドルがビルドとともに出荷される** バージョンのみを受け入れます — バンドルが存在しない(例: インストールされた SDK に同期されていないベータチャネルをピン留めした)場合、構築はスキーマ同期ツールへのポインター付きの型付き設定エラーを throw します。 インストールされた SDK が実際に何をバンドルしているかを見るには、SDK の互換性リストエクスポートをクエリします — すべての公式 SDK が 1 つを公開します。各 AdCP バージョンがワイヤー上で何を意味するかの仕様側の権威あるリストについては、[`schemas/`](https://github.com/adcontextprotocol/adcp/tree/main/dist/schemas) を参照。 ### アダプターが実際にすること 各 SDK はツールごとのアダプターモジュール — 純粋な形状変換(フィールドリネーム、デフォルト投入、構造的再形成) — を出荷します。SDK はバージョンピンが設定されているときそれらを透過的に適用します。あなたのハンドラーは、ピアがどのバージョンを話すかにかかわらず現在の形状を見ます。 AdCP 3.1 が出荷され SDK を上げると、今やレガシーの 3.0 用の新しいアダプターフォルダーが現れます。あなたのハンドラーは動きません。 ## Mechanism 2 — 共存経由で SDK メジャーを移行 SDK をあるメジャーから次に上げ、アップグレードした日にすべてのハンドラーを書き直したくないときこれを使います。各 SDK は前メジャーのサーフェスを新しいエントリポイントと並んで利用可能に保ちます。 ### 例: `@adcp/sdk` 5.x → 6.x v6.0 では、v5 エントリポイントがトップレベルエクスポートからハードに削除されました。既存の v5 コードは 1 つのインポートパスをスワップすることで機能し続けます: ```ts theme={null} // v5 code — change only the import path import { createAdcpServer } from '@adcp/sdk/server/legacy/v5'; serve(() => createAdcpServer({ name: 'My Agent', version: '1.0.0', // …existing v5 handler bag — unchanged })); ``` 同じプロジェクトのグリーンフィールドコードは v6 エントリポイントを並べて使います: ```ts theme={null} import { createAdcpServerFromPlatform } from '@adcp/sdk/server'; const platform = new MyPlatform(); // implements DecisioningPlatform const server = createAdcpServerFromPlatform(platform, { name: 'my-agent', version: '1.0.0', }); ``` 両方がコンパイルし、両方が実行し、両方が適合性を通過します。一度に 1 つのハンドラー — または 1 つの専門分野 — を移行します。レガシーサブパスは、非推奨警告ではなく文書化された共存パスです。 他言語の SDK は同じパターンに従います: 前メジャーのサーフェスが現在のエントリポイントと並んでバージョン管理されたサブパスからインポート可能なままです。特定のインポートパスについては各 SDK のリリースノートを確認してください。 ### いつ実際に移行するか コンパイルし [適合性](/docs/building/verification/conformance) を通過し続ける限りレガシーサーフェスに留まります。新機能(コンパイル時専門分野強制、ケイパビリティ投影、グリーンフィールドコードでの冪等性 / 署名 / 非同期タスク / ステータス正規化の事前配線)が欲しいとき専門分野を移行します。急ぐ必要はありません。 ## Mechanism 3 — ワイヤーレベルネゴシエーション **サーバー** で、どの仕様バージョンを受け入れるかについて明示的にしたいときこれを使います。 ### サポートするものを宣言 `supported_versions`(リリース精度文字列)および/または `major_versions` がエージェントのケイパビリティ宣言に載ります。リリース精度文字列 — `'3.0'`、`'3.1'` — を使い、クライアント側ピン留めに使われるレガシーエイリアス(`'v2.5'`、`'v3'`)は使いません。v2.5 ハンドラーロジックのない 3.x サーバーはここに `'v2.5'` を宣言すべきではありません — その 2.5 呼び出し元は、サーバーの受け入れバージョンセットではなく、バイヤー側の *クライアント側* アダプターを通ります。インシデントトリアージのためにデプロイのパッチビルドをサーフェスしたい場合、`supported_versions` ではなく `build_version` ケイパビリティフィールドを使います。 `@adcp/sdk` の例(より低レベルの handler-bag API): ```ts theme={null} import { createAdcpServer } from '@adcp/sdk/server'; const server = createAdcpServer({ name: 'My Agent', version: '1.0.0', capabilities: { major_versions: [3], supported_versions: ['3.0', '3.1'], // …other capability fields }, // …handlers }); ``` `supported_versions`(メジャーにパース)と `major_versions` の union が、インバウンド `adcp_major_version` / `adcp_version` クレームに対するセラーの受け入れセットを定義します。仕様ルールと 3.1 で導入された双方向ネゴシエーションフローについては [Versioning — version negotiation](/docs/reference/versioning#version-negotiation) を参照。 ### 不一致で何が起こるか バイヤーのリクエストが受け入れセットにない `adcp_major_version`(または `adcp_version`)を運ぶ場合、SDK は `VERSION_UNSUPPORTED` エラーエンベロープを返します。エンベロープはセラーの `supported_versions` をエコーするため、バイヤーは帯域外ルックアップなしにピンをダウングレードできます。エンベロープ形状については [VERSION\_UNSUPPORTED error data](/docs/reference/versioning#version-unsupported-error-data) を参照。 ### バイヤー側: 2 つのサーフェス バージョン不一致がクライアントでサーフェスしうる場所は 2 つあり、異なる条件で発火します: **1. プリフライトの型付き例外。** クライアントがピアのケイパビリティを既にキャッシュしていて、呼び出しが通らないと事前に知っているとき、SDK はリクエストを送る *前に* 型付き `VersionUnsupportedError`(または言語同等物)を throw します。呼び出しサイトからキャッチ: ```ts theme={null} import { VersionUnsupportedError } from '@adcp/sdk'; try { const result = await agent.getProducts({ brief: '…' }); } catch (err) { if (err instanceof VersionUnsupportedError) { // peer doesn't support this call at the pinned version — // re-pin adcpVersion or switch agents } throw err; } ``` **2. ワイヤーからの `VERSION_UNSUPPORTED` エンベロープ。** 不一致がサーバー側でのみ検出される(例: バイヤーの `adcp_major_version` がバイヤーの `adcp_version` 文字列と異なってパースされる)とき、レスポンスはセラーの `supported_versions` をエコーする型付き `VERSION_UNSUPPORTED` エラーエンベロープを運びます: ```ts theme={null} const result = await agent.getProducts({ brief: '…' }); if (!result.success && result.adcpError?.code === 'VERSION_UNSUPPORTED') { const supported = result.adcpError.details?.supported_versions ?? []; // pick a version you also support, then re-issue with adcpVersion pinned } ``` `VERSION_UNSUPPORTED` はリカバリー分類 `correctable` です — プログラム的に扱うクライアントはサポートされるバージョンに対してリトライします。 これは最初のものへのフォールバックではなく 3 番目のメカニズムです: ネゴシエーションは *何が可能か* を教え、呼び出しごとのピン留めは SDK に *どれを使うか* を伝えます。 ## まとめ 典型的なマルチバージョン本番セットアップ: 1. **サーバー**: ケイパビリティに `supported_versions: ['3.0', '3.1']` を宣言。SDK はワイヤー上で両方を受け入れ、セット外の誰にでも `VERSION_UNSUPPORTED` を返す。(ハンドラーが実際に満たすバージョンのみを宣言。) 2. **クライアント(ピアごと)**: レジストリまたはピアのケイパビリティがアドバタイズするものに基づいて `adcpVersion`(例: `'v2.5'`)をピン留め。クライアント側アダプターがワイヤー形状を変換し、あなたのアプリケーションコードが現在の仕様に留まる。 3. **SDK アップグレード**: 自分のスケジュールで SDK を上げる。時間をかけて専門分野ごとに新しいエントリポイントに切り替える。準備できるまで残りをレガシーインポートに保つ。 結合効果: **1 つのハンドラーコードベース、3 つのバージョン軸、フォークなし。** ## 構築せずに済むもの 一から作るエージェントは次をしなければなりません: * サポートを主張するすべての仕様バージョン間の変換マトリクスを維持し、リリースが出荷されるたびに更新する。 * 自身の内部リファクタリングにわたって API 安定性を手書きする。 * ネゴシエーションハンドシェイクを実装する(`adcp_major_version` パース、`adcp_version` クロスチェック、supported-versions エコーを伴う `VERSION_UNSUPPORTED` エンベロープ形成)。 * 新しいバージョンが出荷されるにつれ適合性テストサーフェスを同期に保つ。 これらのそれぞれがすべての仕様改訂で複合します。SDK はそれらを吸収するため、チームの労力はバージョニング配管ではなく L4 差別化に行きます。 ## What changed at L3 in 3.0 今日の仕様に対して手書きエージェントをスコープしているなら、AdCP 3.0 で追加された L3 サーフェスが 2.5 からの最大の差分です。SDK が L3 ですることのほとんどは 3.0 の前に公開されたプリミティブとして存在しませんでした: * **必須の冪等性** — すべての変更ツールで `idempotency_key` 必須、`replayed: true` / `IDEMPOTENCY_CONFLICT` / `IDEMPOTENCY_EXPIRED` セマンティクスが `get_adcp_capabilities` で宣言。[Calling an agent の冪等性](/docs/protocol/calling-an-agent#idempotency-replay-vs-new-operation) を参照。 * **公開されたライフサイクルステートマシン** — 合法エッジ強制と `NOT_CANCELLABLE` / `INVALID_STATE` 優先度を持つ 7 リソースタイプ(`MediaBuy`、`Creative`、`Account`、`SISession`、`CatalogItem`、`Proposal`、`Audience`)。 * **適合性テストサーフェス** — ストーリーボードが状態を決定的に駆動する [`comply_test_controller`](/docs/building/by-layer/L3/comply-test-controller)(サンドボックス専用)。場当たり的なセラーごとのテストエンドポイントを置き換え。 * **ベースラインとしての RFC 9421 署名** — 3.0 では任意、AAO Verified の下で必須。2.5 の緩い bearer トークン姿勢を置き換え。 * **リカバリー分類を伴う拡張エラーカタログ** — `transient` / `correctable` / `terminal` リカバリーセマンティクスを持つ 18 の標準エラーコード。手書き 2.5 エージェントは通常非構造化エラー文字列を返した。 * **非同期タスクコントラクト** — すべての変更ツールが同期または非同期になりうる。どの終端アーティファクトがタスクを閉じるかのコントラクトが指定される。 * **Webhook 署名** — アウトバウンドリクエストと同じ RFC 9421 プロファイルで署名されたプッシュ通知。リプレイウィンドウ + リトライセマンティクスが指定される。 完全な 3.0 チェンジログ(L3 だけでなくプロトコル全体)については [v3 の新機能](/docs/reference/whats-new-in-v3) を参照。移行パスについては [手書きエージェントから移行する](/docs/building/by-layer/L4/migrate-from-hand-rolled) を参照。 一から作る 2.5 エージェントは扱いやすかった。一から作る 3.0 エージェントは、SDK スタックリファレンスで分解された [3〜4 人月の L3 ビルド](/docs/building/cross-cutting/sdk-stack#why-sdks-matter-more-in-adcp-than-in-eg-http) です。SDK が存在するのは、L3 が実装者が手書きできるより速く成長したからです。 ## 関連項目 * [AdCP スタック](/docs/building/cross-cutting/sdk-stack) — 層アーキテクチャリファレンス * [どこから始めるか](/docs/building) — 決定ページ * [Versioning](/docs/reference/versioning) — 仕様側のバージョンルール * [v3 の新機能](/docs/reference/whats-new-in-v3) — プロトコル全体の 3.0 チェンジログ * [手書きエージェントから移行する](/docs/building/by-layer/L4/migrate-from-hand-rolled) — 異なる仕様バージョンの飛行中バイヤーを持つスタックを採用するとき # 概要 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/index Ad Context Protocol の統合と構築のための完全ガイド このセクションでは、AdCP を理解し、統合し、堅牢なシステムを構築するために必要な情報を提供します。 ## 学習パス AdCP が存在する理由、解決する課題、プロトコル比較。初めての方はここから。 技術的基盤: MCP/A2A プロトコル、機能発見、認証、データモデル。 非同期処理、Webhook、エラーハンドリング、オーケストレータ設計で本番対応システムを構築。 ## クイックスタート **利用するプロトコルが決まっている場合:** * [MCP Integration Guide](/docs/building/by-layer/L0/mcp-guide) - Claude、AI アシスタント、MCP 対応ツール向け * [A2A Integration Guide](/docs/building/by-layer/L0/a2a-guide) - Google AI エージェントと A2A ワークフロー向け **プロトコル選択で迷っている場合:** [Protocol Comparison](/docs/building/concepts/protocol-comparison) で詳細比較を確認してください。 ## セクション概要 ### Understanding AdCP AdCP に関わるすべての人のための概念的基盤: * **[Why AdCP](/docs/building/concepts)** - 購買パラダイムの統一と AI エクスペリエンス実現という戦略的ビジョン * **[Protocol Comparison](/docs/building/concepts/protocol-comparison)** - MCP と A2A の比較 ### Foundations すべての AdCP 実装に必要な技術的基盤: * **[MCP Guide](/docs/building/by-layer/L0/mcp-guide)** - ツールコール、コンテキスト、例 * **[A2A Guide](/docs/building/by-layer/L0/a2a-guide)** - タスク、ストリーミング、アーティファクト * **[Capability Discovery](/docs/protocol/get_adcp_capabilities)** - エージェントの対応機能を調査 * **[Authentication](/docs/building/by-layer/L2/authentication)** - 認証情報と権限 * **[Context & Sessions](/docs/building/by-layer/L2/context-sessions)** - リクエスト間の状態管理 * **[Schemas and SDKs](/docs/building/schemas-and-sdks)** - スキーマと公式クライアントライブラリ ### Implementation Patterns 本番対応の堅牢なシステムを構築するために: * **[Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle)** - ステータス値、遷移、ポーリング * **[Async Operations](/docs/building/by-layer/L3/async-operations)** - 同期・非同期・対話的タスクの扱い * **[Webhooks](/docs/building/by-layer/L3/webhooks)** - プッシュ通知と信頼性パターン * **[Error Handling](/docs/building/by-layer/L3/error-handling)** - エラー分類、コード、復旧 * **[Security](/docs/building/by-layer/L1/security)** - セキュリティ考慮とベストプラクティス * **[Orchestrator Design](/docs/building/operating/orchestrator-design)** - ステートマシンとシステムアーキテクチャ # エージェントの運用 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/operating/operating-an-agent プロトコル準拠エージェントの背後にあるもの — プロダクト、アクティベーション、ホスティング、提携・セルフホスト・構築のいずれか。 2〜8 分の [エージェントを構築する](/docs/building/by-layer/L4/build-an-agent) パスはプロトコル準拠エージェントを得ます。このページはその後に来るもの — 各ツール呼び出しの背後にあるビジネスインフラ、そして誰がそれを実行するかを決める方法です。 ここの実践例は **セールスエージェント** です。それが最もインフラ重いケースだからです。短いコールアウトが、シグナル、クリエイティブ、リテールメディアのエージェントで運用上の関心事が分岐する場所をマークします。 ## SDK が既に扱うもの 欠けているものをリストする前に、ストーリーボード検証済みのエージェントが既にあなたに何を与えるかを明示するのが役立ちます: * AdCP ツールスキーマと型付き登録 * コンプライアンスを通過するリクエスト/レスポンス形状 * エラー形式とバージョンネゴシエーション * 実際のものにスワップできる例プロダクトを持つ出発点 下のすべては、それらのツールハンドラーの背後にあるものです。 ## 提携、セルフホスト、または構築 ライブエージェントへの 3 つのパスがあります。それらは、そもそも何かを所有するかではなく、どれだけ所有するかで異なります — どのケースでも、あなたは依然としてプロダクト、価格、アドサーバーへのアクティベーションを所有します。 **マネージドセールスエージェントプラットフォームと提携。** プラットフォームがエージェントエンドポイントを実行し、状態を保持し、広告運用チームがプロダクト、価格、承認を管理する管理 UI を公開します。あなたはアドサーバーを接続し、プラットフォームがプロトコルとその周りの運用を扱います。ライブへ最速、最小の制御。 **事前構築されたエージェントをセルフホスト。** 既存のオープンソースエージェント — 今日これは通常 [Prebid Sales Agent](https://github.com/prebid/salesagent)、GAM 統合を持つコミュニティフルスタックセラーエージェント — を自身のインフラにデプロイし、システムに接続します。プロトコル層と管理 UI の記述をスキップしますが、ホスティング、アップグレード、データベース、アドサーバー配線を所有します。中間: 提携より制御が多く、構築より作業が少ない。 **独自に構築。** SDK とスキルファイルを使ってカスタムエージェントを書きます。ビジネスロジック、価格モデル、アクティベーションパスの完全な制御を得ますが、他のすべてと並んでコードを所有するコストがあります。事前構築されたエージェントがあなたのスタックや価格モデルに合わないとき正しい答え。 SDK とストーリーボードは 3 つすべてのパスでプロトコルコンプライアンスをカバーします。下の表のすべては、どのパスを取るかにかかわらず、その線の外にあるものです。 ## まだ構築またはプロビジョニングが必要なもの | Component | What it does | Example approaches | | -------------------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **プロダクトと価格の管理** | 広告運用がコード変更なしにプロダクト、レートカード、パッケージング、可用性を定義できる。スキルファイルは例プロダクトをハードコードする。実際のエージェントはこれらを動的で編集可能にする必要がある。 | パートナープラットフォームのプロダクトカタログ、事前構築されたエージェントの管理 UI(例: Prebid Sales Agent)、またはデータベース裏付けのカスタム管理 UI。 | | **永続ストレージ** | リクエストをまたいでプロダクト、価格、メディアバイ、クリエイティブ割り当て、配信状態を保存。 | PostgreSQL、MySQL、またはチームが運用に快適な任意のデータストア。 | | **クリエイティブレビューとポリシー** | クリエイティブがライブになる前にブランドセーフティ、法的、フォーマットチェックを適用。プロトコルはクリエイティブを運び、あなたのポリシーがそれを受け入れるかを決める。 | 人間レビューキュー、自動ポリシーエンジン(IAB カテゴリー、ブランドリスト)、またはハイブリッド。広告運用チームが最初に過小評価するもの。 | | **トラフィッキングと履行** | 販売されたメディアバイをアドサーバーのライブキャンペーンに変える。これは完全に AdCP の外にあり、通常最も難しい部品。 | *手動:* 広告運用に GAM でキャンペーンを設定するよう促すメールまたは Slack アラート。*半自動:* ラインアイテムを作成しクリエイティブを割り当てる Google Ad Manager API。*完全自動:* バイ確認からライブ配信までのエンドツーエンドパイプライン。 | | **配信レポートとパフォーマンス** | `get_media_buy_delivery` と `provide_performance_feedback` を実際の数字で駆動。プロトコルツールは存在するが、その背後のログ取り込み、集約、アトリビューションパイプラインはあなたが構築する。 | アドサーバーレポート API プル、ウェアハウスへのログレベル取り込み、またはエージェントに供給するマネージド分析ツール。 | | **オーダー管理** | プロトコルのタスクライフサイクルがカバーするものを超えて、バイステータス、承認ワークフロー、クリエイティブ締め切り、ペーシングを追跡。 | 開始時はデータベースのステータスフィールド。ボリュームが増えるとダッシュボード。 | | **ホスティング** | オペレーター `brand.json` とパブリッシャー `adagents.json` で宣言する URL でエージェントをライブに保つ。ダウンタイムや URL ドリフトはバイヤー検証を壊す。 | クラウド VM、コンテナサービス(Cloud Run、ECS、Fly.io)、またはマネージドホスティングプラットフォーム。 | | **ディスカバリーと認可レコード** | エンドポイントを誰が運用するか、その署名鍵がどこに存在するか、どのパブリッシャーがどのインベントリにそれを認可するかをバイヤーエージェントに伝える。プロトコルサポートは `get_adcp_capabilities` で宣言。 | オペレーター `brand.json` とパブリッシャー `adagents.json` を公開。[Seller setup](/docs/brand-protocol/seller-setup) と [Authorized Properties](/docs/governance/property/authorized-properties) を参照。 | ## プロトコルが終わりあなたのビジネスが始まる場所 AdCP はエージェント間の会話の形状を定義します。次を定義しません: * **価格戦略** — インベントリをどう価格設定、レートカードがどう柔軟、いつ割引が適用されるか * **承認ポリシー** — どのキャンペーンを受け入れまたは拒否、どの根拠で * **課金と請求** — 仕様レベルの課金なし。バイヤーと帯域外で再照合 * **アイデンティティと同意** — ユーザーレベルのアイデンティティ、同意取得、データ主体の権利は規制され実装固有 * **SLA 監視** — エージェントエンドポイントのアップタイム、レイテンシー、エラー予算 * **広告運用ワークフロー** — チームがペーシング、makegood、エスカレーションをどう監視するか パートナープラットフォームはこれらのほとんどにデフォルト選択をします。セルフホストの事前構築されたエージェントは上書きするデフォルトを与えます。自己構築のエージェントはすべての判断をあなたにさせます。 ## エージェントタイプでどう異なるか 上のコンポーネント表はセールスエージェントを仮定します。他のエージェントタイプはそのほとんどを共有しますが特定の追加を持ちます: * **シグナルエージェント** — 同意と来歴が負荷を担う。防御可能なデータリネージ(セグメントがどこから来たか、どの同意がそれをカバーするか)とオプトアウトを尊重する能力が必要。アクティベーションはアドサーバートラフィッキングより、既にセグメントを取り込むプラットフォームへのセグメント配信について。 * **クリエイティブエージェント** — アセットストレージ、トランスコーディング、レンダリング SLA がアドサーバートラフィッキングを置き換える。クリエイティブレビューは、トラフィックするものではなくレンダリングするものについてのポリシーになる。 * **リテールメディアエージェント** — カタログ鮮度が運用上の制約。SKU が変わるにつれプロダクトが変わる。アクティベーションはしばしば GAM ではなくリテール固有の広告プラットフォームを通る。 ## 本番でのエージェントの運用 プロトコル準拠エージェントは、うまく運用されたエージェントと同じではありません。それは 2 つの異なる関心事です: **継続的な広告運用の健全性**(プロトコルのものが実際に仕事をしている)と **セキュリティ**(プロトコルのものがそうするとき信頼できるままでいる)。両方を、アドサーバー統合に与えるのと同じ真剣さで扱ってください。 ### 広告運用の健全性監視 — 実際に何を見るか これは日々の仕事です。そのほとんどは、任意の広告運用チームが既に実行する監視のプロトコル認識の拡張です。ポイントは「学ぶ新しい概念」ではなく「これがダッシュボードが必要な特定のプロトコルシグナル」です。Addie がこれらのセットアップを助けられます。 | What to watch | Why it matters | Signals to alert on | | -------------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | | **オープンタスクキュー深度** | 非同期タスク(`create_media_buy` プロポーザル、`sync_creatives` 承認、`si_initiate_session`)は承認者が遅れると積み重なる。深いキューはバイヤーが待っていることを意味する。 | SLA を超えたオープンタスク(例: クリエイティブ承認 > 4h)、最古タスクの経過時間上昇、キューが排出より速く成長。 | | **クリエイティブ承認スループット** | クリエイティブは実際に承認または拒否される必要がある。黙って動かなくなるキューは「すべて順調」と同一に見える — バイが起動していないことを除いて。 | 承認/拒否率対提出率、期待レビューウィンドウより古いまだ `pending` のクリエイティブ、拒否理由分布。 | | **ライフサイクル遷移が時間通り発火** | セラーはフライト日が来たとき `pending_start` → `active` を遷移しなければならない(MUST)。逃した遷移は、「正常に作成」されてもキャンペーンを never-delivered に残す。 | フライト開始を過ぎてまだ `pending_start` のバイ、クリエイティブ到着を過ぎて `pending_creatives` のバイ、自動再開すべきだった `paused` バイ。 | | **Webhook 配信の健全性** | Webhook はバイヤーが非同期状態変更を学ぶ方法。黙った配信失敗はバイヤーが代わりにポーリングする — またはしないでイベントを完全に逃すことを意味する。 | 失敗配信率、リトライバックログ、デッドレターキューサイズ、状態変更から成功プッシュまでの時間。 | | **起動不能なバイのステータス正しさ** | ライブに *なれない* キャンペーン(クリエイティブ拒否、アカウント停止、ポリシー拒否)は、黙って詰まるのではなくステータスに反映されなければならない(MUST)。ガバナンスがこれに依存する。 | 配信しておらず終端/ブロックステータスも示していないバイ、アドサーバー状態と AdCP 状態の不一致。 | | **配信レポートの鮮度と精度** | `get_media_buy_delivery` は現在の数字を返すべき。古いレポートはバイヤーが嘘に最適化することを意味する。 | last-updated タイムスタンプの遅れ、アドサーバーログと再照合しない支出デルタ。 | | **冪等性キャッシュ動作** | リトライされた `create_media_buy` は重複を作るのではなくキャッシュされたレスポンスを返さなければならない。コンプライアンスがこれをサンドボックスでテストする — 本番ドリフトは独自のシグナル。 | `IDEMPOTENCY_CONFLICT` 率(バイヤーのバグまたは攻撃者のプロービング)、進行中の作業の `IDEMPOTENCY_EXPIRED`(TTL が短すぎ)、アドサーバーで作成された重複メディアバイ(キャッシュ失敗)。 | | **エラーコード分布** | 特定のバイヤーからの特定のエラーコードのスパイクは通常、実際の問題の最初のシグナル。 | バイヤーごと時間ごとの上位 N エラーコード、見たことのない新しいエラーコード。 | これは、誰がコードを書いたかにかかわらずセールスエージェントが必要とする監視です。パートナープラットフォームはこのほとんどのダッシュボードを公開すべき。セルフホストの事前構築されたエージェントはそれらを配線することを要求する。自己構築のエージェントはすべての行をあなたのチームに置く。 ### セキュリティ監視 — コンプライアンスがカバーするもの対あなたに残るもの **ほとんどの AdCP セキュリティメカニクスは [コンプライアンススイート](/docs/building/verification/validate-your-agent) によって強制されます — 手で検証する必要はありません。** ストーリーボードランナーは、認証、冪等性、スキーマ適合性、エラー処理、ガバナンス動作をエージェントに対して検証します。スイートが通過すれば、ワイヤーレベルの動作は正しいです。エージェント/アカウントスコーピングの内部、JWS 検証ステップ、正準 JSON を学ぶ必要はありません — テストが学びます。 コンプライアンススイートが確認するもの(そのためあなたが教える必要がない): | Storyboard | What it verifies | | -------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `security_baseline`(universal) | 保護された操作で認証が必要。無効な鍵が拒否される。API キーまたは OAuth 2.0 の少なくとも一方が正しくアドバタイズされる。 | | `idempotency`(universal) | 変更リクエストが `idempotency_key` を尊重: リプレイがキャッシュを返し、衝突が `IDEMPOTENCY_CONFLICT` を返し、欠けている鍵が `INVALID_REQUEST` を返す。 | | `schema-validation`、`error-compliance` | レスポンスがスキーマに一致。エラーが標準タクソノミーを使う。 | | プロトコル + 専門分野ストーリーボード | プロトコルごとの動作(メディアバイライフサイクル、クリエイティブワークフロー、シグナルアクティベーション)がエンドツーエンドで正しい。 | | リクエスト署名テストベクター | RFC 9421 署名付きリクエストが 25 以上の肯定的・否定的ケースにわたって正しく検証される。 | **あなたが依然として所有するもの**、誰がコードを書いたかにかかわらず: | Concern | What you decide and run | | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **認証情報のストレージとローテーション** | トークンを KMS/シークレットマネージャーに保存。短命のトークンを使う(書き込み可能に 24h 以下、支出をコミットできるトークンに 1h 以下を検討)。完全にログしない、決してコミットしない。正しいローテーションケイデンスは漏洩トークンの影響範囲に依存 — あなたのを正当化する、数字をコピーしない。 | | **鍵儀式とブレークグラス** | 高価値の鍵(webhook シークレット、ガバナンス署名鍵)を生成、輸送、破棄する文書化されたプロセスを持つ。インシデント回復のため封印されたブレークグラス認証情報を維持 — そして監査証跡を残すそれらの使用手順。 | | **冪等性リプレイ TTL** | `capabilities.idempotency.replay_ttl_seconds` を宣言 — 下限 1h、推奨 24h、最大 7d — し、宣言された値が実際のキャッシュ保持に一致することを検証。事前構築されたエージェントはこれを設定として公開。自己構築は焼き込む。 | | **クロスインスタンスとマルチリージョンフェイルオーバー** | 冪等性キャッシュ、セッションストア、webhook 重複排除状態はインスタンス再起動を生き残り、水平スケールされたインスタンス間で共有されなければならない。マルチリージョンデプロイでは、リージョン B にルーティングされたリトライがリージョン A で元々処理されたバイをリプレイするのに十分な一貫性がキャッシュに必要。メモリのみの状態は最初の pod 再起動で at-most-once 保証を壊す。 | | **セキュリティシグナル監視** | `IDEMPOTENCY_CONFLICT` スパイク(プロービング)、失敗したガバナンス検証(なりすまし)、単一の当事者からの SSRF 拒否、1 つのピアからの 401/403 スパイクを監視。運用監視と同じダッシュボード — アラートが異なるだけ。 | | **当事者のベンダーリスク** | あなたのガバナンスエージェント、シグナルプロバイダー、クリエイティブベンダーはすべて、キャンペーンに影響する認証情報を保持するか署名付きトークンを発行する。任意のプロセッサーを評価するように評価: 開示ポリシー、侵害通知コミットメント、コンプライアンスアテステーション、アップタイム SLA。 | | **インシデント対応ランブック** | 1 時間未満で侵害された認証情報を失効、webhook シークレットをローテーション、当事者に通知、ガバナンストークンを発行するなら `revoked_kids` エントリを公開する方法を知る。必要になる前に卓上演習する。 | **提携** しているなら、ベンダーに尋ねてください: 「すべてのリリースで AdCP コンプライアンススイートを通過しますか? どのバージョン? 最新の実行を見られますか?」その単一の質問がコード制御サーフェスのほとんどをカバーします。また、SOC 2 Type II、ISO 27001、またはあなたの業界の同等のアテステーションを保持するか — そして侵害通知コミットメントが何かを尋ねてください。**事前構築されたエージェントをセルフホスト** しているなら、あなたのデプロイに対して自分でコンプライアンススイートを実行してください。**自己構築** しているなら、コンプライアンススイートがあなたの回帰ハーネスです。 **セキュリティと IT リーダー向け:** [Security Model](/docs/building/concepts/security-model) ページはあなたのために書かれています — ブランド、代理店、パブリッシャー、プラットフォームの CISO、セキュリティアーキテクト、サードパーティリスクレビュアー。脅威の風景と AdCP が設計上何を防御するかを説明し、ローンチ前にエンジニアリングチーム(またはベンダー)に尋ねる質問のチェックリストを含みます。 ## 次は * **[エージェントを検証する](/docs/building/verification/validate-your-agent)** — 上で参照されたコンプライアンススイート * **[Security](/docs/building/by-layer/L1/security)** — HMAC、冪等性、SSRF、ガバナンス検証の規範的リファレンス * **[エージェントの通信方法](/docs/building/concepts/how-agents-communicate)** — `adagents.json` と `brand.json` 経由のディスカバリー * **[セラー統合](/docs/building/operating/seller-integration)** — エージェントをアドサーバーに接続するパターン * **[Authorized Properties](/docs/governance/property/authorized-properties)** — 誰が何を販売できるか、それがどう宣言されるか # オーケストレーター設計 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/operating/orchestrator-design AdCP オーケストレーター設計: マルチベンダーキャンペーンワークフロー向けのステートマシンパターン、永続的オペレーション追跡、非同期ファーストアーキテクチャ、再同期。 このガイドは、非同期オペレーションや保留状態、Human-in-the-Loop を扱う AdCP オーケストレーターのベストプラクティスをまとめています。 ## 基本設計原則 ### 1. 非同期を前提に AdCP プロトコルは本質的に非同期です。処理は秒〜日単位でかかることがあります。 **DO:** * すべてのオペレーションを async/await で設計 * オペレーション状態を永続化 * オーケストレーター再起動に耐える * 適切なタイムアウトを実装 **DON'T:** * 即時完了を前提にしません * 同期ブロッキング呼び出しを使わない * 状態をメモリだけに置かない * バックオフなしに無限リトライしません ### 2. ステータス駆動ロジック オペレーションは標準化されたステータス値を通じて進行する: ```python theme={null} TASK_STATUSES = { "submitted", # Long-running (hours to days) - provide webhook or poll "working", # Processing (< 120 seconds) - poll frequently "input-required", # Need user input/approval - continue conversation "completed", # Success - process results "failed", # Error - handle appropriately "canceled", # User canceled "auth-required" # Need authentication } ``` ### 3. ステートマシン設計 AdCP タスクステータスに合わせた適切なステートマシンを実装します: ```python theme={null} class OperationState(Enum): # Local orchestrator states REQUESTED = "requested" CALLING_ADCP = "calling_adcp" # AdCP task states (match server responses) SUBMITTED = "submitted" WORKING = "working" INPUT_REQUIRED = "input_required" COMPLETED = "completed" FAILED = "failed" CANCELED = "canceled" # Valid state transitions VALID_TRANSITIONS = { "requested": ["calling_adcp"], "calling_adcp": ["submitted", "working", "input_required", "completed", "failed"], "submitted": ["working", "completed", "failed", "canceled"], "working": ["completed", "failed", "input_required"], "input_required": ["submitted", "working", "completed", "failed"] } ``` ## オペレーションの追跡 ### 永続ストレージ すべてのオペレーションを詳細に追跡しながら保存します: ```python theme={null} class OperationTracker: def __init__(self, db): self.db = db async def create_operation(self, operation_type, request_data, webhook_config=None): operation = { "id": str(uuid.uuid4()), "type": operation_type, "status": "requested", "request": request_data, "webhook_config": webhook_config, "created_at": datetime.now(), "updated_at": datetime.now(), "task_id": None, "context_id": None, "result": None, "error": None } await self.db.operations.insert_one(operation) return operation["id"] async def update_status(self, operation_id, status, **kwargs): update = { "status": status, "updated_at": datetime.now() } update.update(kwargs) await self.db.operations.update_one( {"id": operation_id}, {"$set": update} ) async def get_pending_operations(self): """Get all operations that need monitoring""" return await self.db.operations.find({ "status": {"$in": ["submitted", "working", "input_required"]} }).to_list(length=None) ``` ### 状態の再同期 起動時にローカル状態をサーバーと同期します: ```python theme={null} async def reconcile_with_server(self, adcp_client): """Sync local state with server using AdCP task reconciliation""" server_tasks = await adcp_client.call('list_tasks', { 'filters': {'statuses': ['submitted', 'working', 'input_required']} }) server_task_ids = {task['task_id'] for task in server_tasks['tasks']} local_operations = await self.get_pending_operations() local_task_ids = {op['task_id'] for op in local_operations if op['task_id']} return { 'orphaned_on_server': server_task_ids - local_task_ids, 'missing_from_server': local_task_ids - server_task_ids, 'total_pending_server': len(server_tasks['tasks']), 'total_pending_local': len(local_operations) } ``` ## 非同期オペレーションハンドラー ### レスポンスの振り分け ステータスに応じてレスポンスを処理します: ```python theme={null} class AsyncOperationHandler: def __init__(self, adcp_client, tracker, notifier): self.adcp = adcp_client self.tracker = tracker self.notifier = notifier self.polling_tasks = {} async def handle_operation_response(self, operation_id, response): """Handle any AdCP response with proper status routing""" status = response.get("status") # レスポンスの詳細でオペレーションを更新 await self.tracker.update_status( operation_id, status, task_id=response.get("task_id"), context_id=response.get("context_id"), result=response.get("result") if status == "completed" else None, error=response.get("error") if status == "failed" else None ) # ステータスに応じて処理を振り分け if status == "completed": await self._handle_completed(operation_id, response) elif status == "failed": await self._handle_failed(operation_id, response) elif status == "submitted": await self._handle_submitted(operation_id, response) elif status == "working": await self._handle_working(operation_id, response) elif status == "input_required": await self._handle_input_required(operation_id, response) ``` ### Submitted(長時間)オペレーション 長時間の処理を扱います: ```python theme={null} async def _handle_submitted(self, operation_id, response): """Handle long-running operations""" task_id = response["task_id"] # Check if webhook is configured operation = await self.tracker.get_operation(operation_id) webhook_config = operation.get("webhook_config") if webhook_config: # Webhook will handle completion notification await self.notifier.notify_submitted_with_webhook(operation_id, task_id) else: # Start polling for completion polling_task = asyncio.create_task( self._poll_for_completion(operation_id, task_id, interval=60) ) self.polling_tasks[task_id] = polling_task ``` ### バックオフ付きポーリング 効率的なポーリングを実装します: ```python theme={null} async def _poll_for_completion(self, operation_id, task_id, interval=60): """Poll task status until completion""" max_polls = 1440 if interval == 60 else 24 # 24 hours or 2 minutes poll_count = 0 while poll_count < max_polls: try: await asyncio.sleep(interval) poll_count += 1 task_response = await self.adcp.call('get_task_status', { 'task_id': task_id, 'include_result': True }) await self.handle_operation_response(operation_id, task_response) if task_response["status"] in ["completed", "failed", "canceled"]: break except Exception as e: await self.tracker.update_status( operation_id, "failed", error=f"Polling error: {str(e)}" ) break self.polling_tasks.pop(task_id, None) if poll_count >= max_polls: await self.tracker.update_status( operation_id, "failed", error="Task polling timeout" ) ``` ## Webhook サポート ### 信頼性の高い Webhook ハンドラー 信頼性パターンを使って Webhook を実装します: ```python theme={null} class WebhookHandler: def __init__(self, tracker, notifier, secret_key): self.tracker = tracker self.notifier = notifier self.secret_key = secret_key self.processed_events = {} def verify_webhook_signature(self, payload: bytes, signature: str) -> bool: """Verify webhook authenticity""" expected_signature = hmac.new( self.secret_key.encode(), payload, hashlib.sha256 ).hexdigest() return signature == f"sha256={expected_signature}" async def is_replay_attack(self, timestamp: str, event_id: str) -> bool: """Prevent replay attacks using timestamp and event ID""" event_time = datetime.fromisoformat(timestamp.replace('Z', '+00:00')) now = datetime.now() if now - event_time > timedelta(minutes=5): return True return event_id in self.processed_events ``` ### Webhook とポーリングの併用(バックアップ) Webhook のみに依存しないようにします: ```python theme={null} class ReliableWebhookOrchestrator: def __init__(self): self.webhook_timeout = timedelta(minutes=10) self.backup_polling_delay = timedelta(minutes=2) async def _handle_submitted_with_webhook(self, operation_id, task_id): """Handle submitted task with webhook + backup polling""" async def backup_polling(): await asyncio.sleep(self.backup_polling_delay.total_seconds()) operation = await tracker.get_operation(operation_id) if operation["status"] not in ["completed", "failed", "canceled"]: logger.info(f"Starting backup polling for task {task_id}") await self._poll_for_completion(operation_id, task_id, interval=60) asyncio.create_task(backup_polling()) ``` ## オーケストレーターの例 オーケストレーターの実装例: ```python theme={null} class AdCPOrchestrator: def __init__(self): self.adcp = AdCPClient() self.tracker = OperationTracker(db) self.handler = AsyncOperationHandler(self.adcp, self.tracker, UserNotifier()) self.webhook_base_url = "https://orchestrator.com/webhooks" async def create_campaign(self, user_id, request, enable_webhook=True): """Create a campaign with governance validation and full async handling. Plans must already be synced via sync_plans before calling this method. Plan creation happens during the planning phase, not at campaign creation time. """ # 1. Run intent check (plan must already exist) if request.get("governance_context"): gov_check = await self.adcp.call("check_governance", { "plan_id": request["governance_context"]["plan_id"], "caller": request["governance_context"]["caller"], "tool": "create_media_buy", "payload": request }) if gov_check["status"] == "denied": raise GovernanceDeniedError(gov_check["explanation"]) if gov_check["status"] == "conditions": raise GovernanceConditionsError(gov_check["conditions"]) # If check_governance needs human review internally, it returns # async task status (submitted/working) and resolves to # approved or denied — standard task lifecycle. # 2. Create the media buy await self._create_media_buy(user_id, request, enable_webhook) async def _create_media_buy(self, user_id, request, enable_webhook=True): """Create a media buy with full async handling.""" # 1. Prepare webhook configuration webhook_config = None if enable_webhook: webhook_config = { "webhook_url": f"{self.webhook_base_url}/adcp/{user_id}", "webhook_auth": { "type": "bearer", "credentials": await self.get_webhook_token(user_id) } } # 2. Create operation record operation_id = await self.tracker.create_operation( "create_media_buy", request, webhook_config=webhook_config ) try: # 3. Call AdCP response = await self.adcp.call("create_media_buy", request, webhook_config) # 4. Handle response await self.handler.handle_operation_response(operation_id, response) # 5. Return appropriate response to user return self._format_user_response(operation_id, response) except Exception as e: await self.tracker.update_status(operation_id, "failed", error=str(e)) raise async def reconcile_state_on_startup(self): """Recover from orchestrator restart""" reconciliation = await self.tracker.reconcile_with_server(self.adcp) logger.info(f"State reconciliation: {reconciliation}") for task_id in reconciliation["orphaned_on_server"]: # Resume monitoring orphaned tasks operation_id = await self.tracker.create_operation( "unknown", {}, status="submitted" ) await self.tracker.update_status(operation_id, "submitted", task_id=task_id) asyncio.create_task( self.handler._poll_for_completion(operation_id, task_id) ) ``` ## キャンペーンライフサイクルにおけるガバナンス プラン作成([`sync_plans`](/docs/governance/campaign/tasks/sync_plans))は計画フェーズ中 — キャンペーンが存在する前に発生します。ガバナンスチェックはキャンペーン実行中に発生します。これらは別々の関心事です。 **計画フェーズ**(メディアプランごとに 1 回): ``` sync_plans — オーケストレーターがプランをガバナンスエージェントにプッシュする ``` **キャンペーン実行**(メディアバイごと): ``` check_governance(tool + payload) → create_media_buy → check_governance(media_buy_id + planned_delivery) → delivery → report_plan_outcome ``` | フェーズ | 呼び出し元 | タスク | 失敗時に起こること | | --------- | --------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | インテントチェック | オーケストレーター | [`check_governance`](/docs/governance/campaign/tasks/check_governance)(`tool` + `payload`) | キャンペーンがバイヤーのプランに違反 — いかなる支出も発生する前に拒否または条件付き。ガバナンスエージェントが人間のレビューを必要とする場合、タスクは非同期になり承認または拒否に解決される。 | | 実行チェック | セラー | [`check_governance`](/docs/governance/campaign/tasks/check_governance)(`media_buy_id` + `planned_delivery`) | セラーのデリバリープランがバイヤーの期待と一致しない — 購入がブロックされる | | デリバリーチェック | セラー | [`check_governance`](/docs/governance/campaign/tasks/check_governance)(`phase: delivery` + `delivery_metrics`) | ドリフトを検出 — ペーシング、地域、またはチャネル分布がプランから逸脱 | | プラン成果 | オーケストレーター | [`report_plan_outcome`](/docs/governance/campaign/tasks/report_plan_outcome) | フィードバックループなし — ガバナンスエージェントが将来の推奨を改善できない | コード例付きの完全なシーケンスは[メディアバイのガバナンスワークフロー](/docs/media-buy/index#governance)を、セラーの実行チェック義務は[セラー統合ガイド](/docs/building/operating/seller-integration#execution-checks)を参照してください。 ## ベストプラクティス ### 1. 永続ストレージ オペレーションの状態には常に永続ストレージを使用します: * データベース(PostgreSQL、MongoDB) * メッセージキュー(Redis、RabbitMQ) * 分散キャッシュ(Redis Cluster) ### 2. 冪等性 すべてのオペレーションを冪等にする: ```python theme={null} async def create_media_buy_idempotent(self, request): existing = await self.db.operations.find_one({ "type": "create_media_buy", "request.po_number": request["po_number"], "status": {"$in": ["created", "active"]} }) if existing: return existing["result"] return await self.create_media_buy(request) ``` ### 3. タイムアウト処理 合理的なタイムアウトを実装します: ```python theme={null} OPERATION_TIMEOUTS = { "create_media_buy": timedelta(hours=24), "update_media_buy": timedelta(hours=12), "creative_approval": timedelta(hours=48) } ``` ### 4. エラーリカバリー サーキットブレーカー付きのリトライロジックを実装します: ```python theme={null} @retry( stop=stop_after_attempt(3), wait=wait_exponential(min=1, max=60), retry=retry_if_exception_type(TransientError) ) async def call_adcp_api(self, tool, params): try: return await self.adcp.call(tool, params) except RateLimitError: raise TransientError("Rate limited") except NetworkError: raise TransientError("Network error") ``` ### 5. モニタリングとアラート 主要メトリクスを追跡する: * タイプ別の保留オペレーション数 * 平均承認時間 * 拒否率 * タスクタイムアウト率 * API エラー率 ## ユーザーへの通知 保留中のオペレーションについてユーザーに知らせ続ける: ```python theme={null} class UserNotifier: async def notify_pending_approval(self, user_id, operation): message = { "type": "pending_approval", "operation_id": operation["id"], "message": "Your media buy requires publisher approval", "estimated_time": "2-4 hours" } await self.send_notification(user_id, message) async def notify_approval(self, user_id, operation): message = { "type": "operation_approved", "operation_id": operation["id"], "message": "Your media buy has been approved", "media_buy_id": operation["result"]["media_buy_id"] } await self.send_notification(user_id, message) ``` ## まとめ 堅牢な AdCP オーケストレーターを構築するには次のことが必要だ: 1. 全体を通じた非同期設計 2. 永続化を伴う適切な状態管理 3. 保留状態の適切な処理 4. 長時間オペレーションでのユーザーへの通知 5. モニタリングと観測性 保留状態はエラーではなく、広告ワークフローの正常な一部です。 ## 次のステップ * **Task Lifecycle**: ステータス処理については [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照 * **Webhooks**: プッシュ通知については [Webhooks](/docs/building/by-layer/L3/webhooks) を参照 * **Security**: マルチテナントセキュリティについては [Security](/docs/building/by-layer/L1/security) を参照 # AI エージェントへのインベントリ提供 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/operating/seller-integration AdCP セラー統合ガイド。パブリッシャー、SSP、広告プラットフォームが標準化されたプロダクト発見とメディアバイタスクを通じて AI バイヤーエージェントにインベントリを公開する方法。 AI エージェントがメディアを購入し始めています。エージェンシーの AI アシスタントが複数のプラットフォームにわたって広告インベントリを評価するとき、何を販売しているかを発見し、価格設定とターゲティングオプションを理解し、購入を実行する方法が必要だ — すべて標準インターフェースを通じて。 AdCP(Ad Context Protocol)はそのインターフェースを提供します。パブリッシャー、SSP、または広告プラットフォームであれば、AdCP を実装することで、各バイヤーエージェントのカスタム統合を必要とせずに、任意の準拠バイヤーエージェントがインベントリにアクセスできるようになります。 ## なぜ重要か 今日、すべてのプラットフォームはバイヤーが独自の API を学ぶことを必要とします。人間が購入を行う場合はそれで機能します。しかし AI エージェントは多くのプラットフォームを同時に横断して動作し、一般的な操作のための共通言語が必要です。 AdCP を実装したプラットフォームは、バイヤーエージェントによって即座に発見可能です。実装していないプラットフォームは各バイヤーがカスタム統合を構築する必要があり、インベントリにアクセスできるエージェントのプールが制限されます。 ## 実装する必要があるもの AdCP セルサイド統合には3つのパートがある: ### パブリッシャー認可を公開します 各パブリッシャードメインに `adagents.json` ファイルを公開します。このファイルはパブリッシャーのプロパティと、そのインベントリの販売またはエンリッチを認可されたエージェントを宣言する — サプライチェーンの透明性において `ads.txt` が機能する方法に似ています。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "contact": { "name": "Example Publisher", "email": "adops@publisher.example.com", "domain": "publisher.example.com" }, "properties": [ { "property_id": "publisher_home", "property_type": "website", "name": "Example Publisher", "publisher_domain": "publisher.example.com", "identifiers": [{ "type": "domain", "value": "publisher.example.com" }] } ], "authorized_agents": [ { "authorization_type": "property_ids", "url": "https://ads.publisher.example.com/mcp", "authorized_for": "Example Publisher direct inventory", "property_ids": ["publisher_home"], "signing_keys": [ { "kid": "publisher-sales-prod-2026", "kty": "OKP", "alg": "EdDSA", "crv": "Ed25519", "x": "w8zcY1LZqV4n1oKbfyq3n2q3sL2uV3z7kEw1m9Qjv4A", "use": "sig" } ] } ] } ``` バイヤーエージェントは `adagents.json` を確認してパブリッシャー認可を検証し、変更系のセラー認可についてはパブリッシャーがピン留めした署名鍵を検証します。エージェントのプロトコルカバレッジは `adagents.json` からではなく `get_adcp_capabilities` から発見します。 ### オペレーターアイデンティティを公開します セールスエージェントを運用する組織のために、`https://{your-domain}/.well-known/brand.json` に `brand.json` を公開します。`adagents.json` はどのパブリッシャーがセールスエージェントを認可するかをバイヤーに伝えます。`brand.json` はどのオペレーターが販売パスを主張しているか、どのプロパティを代表するか、署名鍵をどこで見つけるかをバイヤーに伝えます。 パブリッシャー所有のセールスチームの場合、所有または直接のプロパティをリストします。ネットワークや SSP の場合、`relationship: "delegated"` または `relationship: "ad_network"` で代表するプロパティをリストし、その値がパブリッシャーの `adagents.json` の `authorized_agents[].delegation_type` と一致することを確認します。2 つのファイルは双方向の検証チェーンを形成します。オペレーターは「私はこのプロパティを販売する」と宣言し、パブリッシャーは「このオペレーターはそれを販売する認可を持つ」と確認します。販売を委任するパブリッシャーも `adagents.json` を公開すべきです。自身の `brand.json` はパブリッシャーアイデンティティとポートフォリオコンテキストのために推奨されますが、委任されたオペレーターの `brand.json` が、バイヤーが呼び出すエージェントの必須のアイデンティティレコードです。 直販パブリッシャーと委任ネットワークの例を含む完全なセルサイドセットアップパターンは、[seller setup](/docs/brand-protocol/seller-setup) を参照してください。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "id": "example_publisher", "url": "https://publisher.example.com", "names": [{ "en_US": "Example Publisher" }], "properties": [ { "type": "website", "identifier": "publisher.example.com", "relationship": "owned" } ], "agents": [ { "type": "sales", "url": "https://ads.publisher.example.com", "id": "publisher_sales", "jwks_uri": "https://ads.publisher.example.com/.well-known/jwks.json" } ] } ``` `agents[].jwks_uri` によりバイヤーはあなたの公開署名鍵を解決できます。プロトコルは検証可能な署名と発見可能な公開鍵を要求します。特定の KMS ベンダーは要求しません。本番エージェントは秘密署名鍵を KMS/HSM またはマネージドシークレットシステムでバックアップすべきです。AdCP Webhook を送るセラーは、バイヤーがアウトバウンド Webhook 署名を検証できるよう、その JWKS に `adcp_use: "request-signing"` JWK を公開すべきです。非推奨の `adcp_use: "webhook-signing"` 鍵は後方互換性のため Webhook パスで受け入れられ続けます。鍵公開と Webhook 署名プロファイルは [request signing](/docs/building/by-layer/L1/request-signing) を参照してください。 ### インベントリを公開します `get_products` を実装して販売内容を記述します。各プロダクトは購入可能なユニットを表す — ディスプレイプレースメント、ビデオスロット、スポンサードリスティング、ニュースレタースポンサーシップなど。バイヤーエージェントは `buying_mode` とオプションの `brief` でこれを呼び出す: ```json theme={null} { "buying_mode": "brief", "brief": "Premium display placements for consumer electronics brand" } ``` レスポンスには価格、フォーマット、デリバリータイプを含む構造化されたプロダクトオブジェクトが含まれます: ```json theme={null} { "products": [ { "product_id": "homepage_leaderboard", "name": "Homepage leaderboard", "channels": ["display"], "format_ids": [ { "agent_url": "https://ads.publisher.example.com", "id": "display_728x90" } ], "pricing_options": [ { "pricing_option_id": "cpm_standard", "pricing_model": "cpm", "floor_price": 8.00, "currency": "USD" } ] } ] } ``` プロダクトメタデータが充実しているほど、バイヤーエージェントはキャンペーン要件にインベントリをより適切にマッチさせることができます。 ポッドキャスト、CTV、ライブイベントなどのコンテンツ中心のインベントリでは、プロダクトはショーを参照し、独占性を提供できる: ```json theme={null} { "products": [ { "product_id": "signal_noise_sponsorship", "name": "Signal & Noise — Category Sponsorship", "description": "Category-exclusive sponsorship of the Signal & Noise podcast, including pre-roll and mid-roll host read placements.", "shows": [{ "publisher_domain": "crestnetwork.example.com", "show_ids": ["signal_noise"] }], "publisher_properties": ["crestnetwork_podcast"], "channels": ["podcast"], "placements": [ { "placement_id": "pre_roll", "name": "Pre-roll (30s)" }, { "placement_id": "host_read", "name": "Mid-roll host read (60s)" } ], "delivery_type": "guaranteed", "exclusivity": "category", "format_ids": [ { "agent_url": "https://ads.publisher.example.com", "id": "audio_30s" } ], "pricing_options": [ { "pricing_option_id": "flat_monthly", "pricing_model": "flat_rate", "fixed_price": 15000, "currency": "USD" } ] } ] } ``` 完全なコンテンツモデルについては[ショーとエピソード](/docs/media-buy/product-discovery/collections-and-installments)を、独占性パターンについては[メディアプロダクト](/docs/media-buy/product-discovery/media-products#exclusivity)を参照。 ### バイを受け入れて実行します `create_media_buy` を実装してバイヤーエージェントからのキャンペーン指示を受け入れる。メディアバイにはプロダクト、予算、スケジュール、ターゲティングパラメーターが含まれます。 ```json theme={null} { "account": { "account_id": "acct-56789" }, "brand": { "brand_id": "nova-electronics" }, "proposal_id": "prop-homepage-leaderboard", "total_budget": { "amount": 10000, "currency": "USD" }, "start_time": "2026-04-01T00:00:00Z", "end_time": "2026-04-30T23:59:59Z" } ``` プラットフォームは通常のワークフローに従ってバイを処理する — 即時活性化、内部レビュー、または承認キューのいずれであっても。AdCP の非同期ステータスシステム(`completed`、`working`、`submitted`、`input-required`)により、あらゆるワークフローをモデル化できます。 ## 業種別ガイダンス 上記のコア統合ステップはすべてのセラーに適用されます。**複数のプラットフォームにわたって集約する広告ネットワーク**の場合は、プロダクトモデリング、アカウントチェーン、カタログフォワーディング、ネットワーク向け `adagents.json` について[広告ネットワークの詳細ガイド](/docs/sponsored-intelligence/networks)を参照。 垂直固有のプロダクトモデリング、価格設定パターン、測定については: * **AI プラットフォームと AI 広告ネットワーク**: スポンサードレスポンス、AI 検索プロダクト、カタログからの生成クリエイティブ、SI チャットプロトコルハンドオフについては[スポンサードインテリジェンスガイド](/docs/sponsored-intelligence/overview)を参照。 * **リテールメディアネットワーク**: スポンサードプロダクトリスティング、クローズドループアトリビューション、店内測定については[コマースメディアガイド](/docs/media-buy/commerce-media)を参照。 ## アカウントとサンドボックス 本番セールスエージェントはアカウントプロトコルを実装すべきです。[`sync_accounts`](/docs/accounts/tasks/sync_accounts) と [`list_accounts`](/docs/accounts/tasks/list_accounts) により、バイヤーが請求関係を確立し、広告主ごとの支出を追跡し、単一エージェントを通じて異なるブランドのために購入する複数のオペレーターを管理できます。 アカウントモデルはプラットフォームによって異なる: * **ウォールドガーデン**(ソーシャルプラットフォーム、AI プラットフォーム、リテールメディアネットワーク)は通常、明示的なアカウントを使用する — 各オペレーターが独立して認証するように [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) で `require_operator_auth: true` を設定します。 * **オープンプラットフォーム**(パブリッシャー、SSP)は暗黙的なアカウントを使用できる — エージェントが信頼され、`sync_accounts` を通じてアカウントを宣言します。 完全なワークフローについては[アカウントとエージェント](/docs/building/by-layer/L2/accounts-and-agents)を参照。 **サンドボックスサポートを強く推奨します。** 実際の支出を確定する前に、バイヤーがテストアカウントをプロビジョニングして完全な統合を検証できるよう、ケイパビリティで `account.sandbox: true` を宣言する — プロダクト発見、メディアバイ作成、デリバリーレポート。サンドボックスなしでは、バイヤーはライブインベントリに対してテストする必要があり、採用が遅くなりオンボーディングの摩擦が増加します。実装の詳細については[サンドボックスモード](/docs/media-buy/advanced-topics/sandbox)を参照。 ## オプション: デリバリーレポート `get_media_buy_delivery` を実装して、バイヤーエージェントが標準化された形式でパフォーマンスデータ — インプレッション、クリック、支出、コンバージョン — を取得できるようにします。これはエージェントが各ダッシュボードに個別にログインすることなく複数のプラットフォームにわたってキャンペーンを監視する方法です。 ## プロダクトデザインパターン インベントリタイプによって異なる AdCP 機能を使用します: | インベントリタイプ | 主要機能 | | ---------------- | ------------------------------------------------------------------------- | | 標準ディスプレイ/ビデオ | `format_ids`、`delivery_type: "non_guaranteed"`、オークション価格設定 | | ポッドキャストスポンサーシップ | `shows`、`placements`(ホストリード)、`delivery_type: "guaranteed"`、flat\_rate | | CTV シリーズスポンサーシップ | `shows`、`exclusivity`、`delivery_type: "guaranteed"` | | ライブイベント | `shows`(cadence: event)、`episodes`(flexible\_end、tentative)、`exclusivity` | | リテールメディア | `catalog_types`、`catalog_match`、メトリクス最適化 | コンテンツ中心のインベントリについては[ショーとエピソード](/docs/media-buy/product-discovery/collections-and-installments)を参照。独占性とスポンサーシップパターンについては[メディアプロダクト](/docs/media-buy/product-discovery/media-products#exclusivity)を参照。 ## ガバナンス適用 バイヤーエージェントはますます支出を確定する前にガバナンスコンプライアンスを要求するようになっています。ガバナンスを実装することで、インベントリがブランドセーフキャンペーンの対象となり、キャンペーン後の紛争が減り、プラットフォームがブランドの適合性を真剣に考えていることをバイヤーエージェントに示せる。セラーに関連する3つのガバナンスドメインがあります。 ### adagents.json によるプロパティガバナンス `adagents.json` ファイルはプロパティガバナンスの基盤です。販売するプロパティ、販売を認可されたエージェント、インベントリに関するデータを持つガバナンスエージェントを宣言します。バイヤーエージェントはこれを使用してサプライパスの認可を検証し、プロパティインテリジェンスを発見する — `adagents.json` が欠如または不完全な場合、バイヤーエージェントは主張するものを販売する権限があるかを検証できません。 バイヤーが品質、持続可能性、またはブランドセーフティのためにプロパティをスコアするガバナンスエージェントを発見できるよう、`property_features` エントリを宣言します。完全なスキーマについては[プロパティガバナンス仕様](/docs/governance/property/specification)を、パブリッシャーサイドのセットアップについては[adagents.json テクスペック](/docs/governance/property/adagents)を参照。 ### コンテンツスタンダードの適用 バイヤーが `get_products` または `create_media_buy` リクエストに `content_standards_ref` を含める場合、デリバリー中にブランド適合性ルールを適用するよう依頼しています。責任: 参照されたガバナンスエージェントからスタンダードを取得し、適用できるかどうかを評価し、できない場合はバイを拒否し、`calibrate_content` を通じてガバナンスエージェントに対してローカル評価モデルをキャリブレーションします。デリバリー後、バイヤーがコンプライアンスを独立して検証できるようにコンテンツアーティファクトをバイヤーに返します。 バイヤーのコンテンツスタンダードを意味のある形で適用できない場合は、受け入れてサイレントに失敗するのではなく、バイを拒否します。完全なセールスエージェントワークフローについては[コンテンツスタンダード実装ガイド](/docs/governance/content-standards/implementation-guide)を参照。 ### 実行チェック バイヤーのアカウントにガバナンスエージェントが設定されている場合([`sync_governance`](/docs/accounts/tasks/sync_governance) 経由)、セラーはメディアバイを確定する前に、`media_buy_id` と `planned_delivery` を付けて [`check_governance`](/docs/governance/campaign/tasks/check_governance) を呼び出さなければなりません(MUST)。これは拘束力のある検証です — ガバナンスエージェントがセラーの計画されたデリバリーをバイヤーのキャンペーンプランに照らして検証します。 ```javascript theme={null} // create_media_buy を確定する前に const check = await governanceAgent.checkGovernance({ plan_id: mediaBuy.plan_id, // from the create_media_buy request caller: "https://seller.example.com", governance_context: mediaBuy.governance_context, // opaque — pass through, do not parse media_buy_id: mediaBuy.media_buy_id, phase: "purchase", planned_delivery: { geo: { countries: ["US"] }, channels: ["olv"], start_time: mediaBuy.start_time, end_time: mediaBuy.end_time, total_budget: mediaBuy.total_budget.amount, currency: mediaBuy.total_budget.currency } }); if (check.status === "denied") { // Committed checks are always binding — do not confirm the media buy return { error: "GOVERNANCE_DENIED", detail: check.explanation }; } if (check.status === "conditions" && retries < 3) { // Conditions restrict what the seller can deliver — e.g., narrower geo, // blocked channels, reduced frequency. The seller adjusts their own // delivery parameters (not the buyer's budget) and re-calls check_governance. // If the seller cannot satisfy the conditions, reject the media buy. const adjusted = applyConditions(plannedDelivery, check.conditions); if (!adjusted) { return { error: "GOVERNANCE_CONDITIONS_UNSATISFIABLE", detail: check.conditions }; } // Re-check with adjusted delivery (governance agents SHOULD deny after 3 re-calls) return await checkGovernanceWithRetry(request, adjusted, retries + 1); } if (check.status === "conditions") { return { error: "GOVERNANCE_CONDITIONS_RETRY_LIMIT", detail: check.conditions }; } // check.status === "approved" — proceed with confirmation ``` 実行チェックはメディアバイライフサイクルの 3 つのフェーズをカバーします: | フェーズ | いつ呼ぶか | 何が検証されるか | | -------------- | ---------------------------------------------------------------------------- | ----------------------- | | `purchase` | `create_media_buy` を確定する前 | 予算、地域、チャネル、フライト日、ポリシー | | `modification` | [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) を確定する前 | 変更の大きさ、再配分、新しいパラメーター | | `delivery` | デリバリー中に定期的に | ペーシング、支出率、地域ドリフト、チャネル分布 | セラーは実行チェックを段階的に導入できます — purchase のみ(`create_media_buy` ごとに 1 回の呼び出し)から始め、その後 modification と delivery チェックを追加します。完全な仕様は [`check_governance`](/docs/governance/campaign/tasks/check_governance) を参照してください。 ### キャンペーンガバナンスコンテキスト バイヤーが `create_media_buy` リクエストのプロトコルエンベロープに `governance_context` を含める場合、メディアバイと一緒に保存します。これは不透明な値だ — 解釈せず、ただ永続化して転送します。 そのメディアバイのすべてのライフサイクルイベントでバイヤーのガバナンスエージェントに `governance_context` を渡す: | ライフサイクルイベント | governance\_context の流れ | | --------------- | ------------------------------------------------------------------------------------ | | **作成** | `create_media_buy` エンベロープで受信します。保存します。`check_governance` を呼び出す場合は含めます。 | | **活性化** | セラーの `planned_delivery` で `check_governance` を呼び出す際に保存した `governance_context` を含めます。 | | **更新** | 変更に対する `check_governance` に含めます。ガバナンスエージェントはそれを使用して累積的な変更を追跡します。 | | **一時停止 / 再開** | `check_governance` を呼び出す場合に含めます。ガバナンスエージェントはペーシング状態を更新します。 | | **キャンセル / 完了** | ガバナンスエージェントが予算追跡を終了して最終監査を生成できるよう含めます。 | | **デリバリーウェブフック** | ガバナンスエージェントがデリバリーデータと関連付けられるようウェブフックペイロードに含めます。 | ガバナンスエージェントは `governance_context` を使用して各イベントを元のプラン、キャンペーン、予算状態に再接続します。これなしに、ガバナンスエージェントはライフサイクルを通じてメディアバイを追跡する方法がない。 元のリクエストに `governance_context` が存在しない場合は、ガバナンス呼び出しをスキップする — バイヤーはこのメディアバイにキャンペーンガバナンスを使用していません。 ### クリエイティブガバナンス バイヤーエージェントはデリバリー前にクリエイティブ評価を要求することがある — セキュリティスキャン、コンテンツ分類、品質スコアリング。セラーとして、`get_creative_features` を通じてガバナンスエージェントにクリエイティブマニフェストを送信し、バイヤーが設定した機能要件を尊重する(例: `auto_redirect` や `credential_harvest` にフラグされたクリエイティブのブロック)。評価を自分で実装する必要はない。専門のガバナンスエージェントがそれを処理します。 フィーチャーベースの評価モデルとマルチエージェント協調パターンについては[クリエイティブガバナンスの概要](/docs/governance/creative/index)を参照。 ### スポンサードインテリジェンスセラー向け: 生成時の適用 従来のセラーはガバナンスをポストデリバリーフィルターとして適用する — コンテンツを分類し、失敗したものをブロックします。スポンサードインテリジェンスプラットフォームはサーブ時にクリエイティブを生成するため、ガバナンスルールを事後ではなく生成中に適用できます。バイヤーがコンテンツスタンダードをプッシュする際は、それらを生成パイプラインの制約として適用することで、不適切なコンテンツが生成されないようにします。これによりブランドは根本的に強力な保証を得る: 適合性はチェックとして後付けされるのではなく、出力に組み込まれる。コンテンツスタンダードがカタログ駆動のクリエイティブ生成とどのように統合されるかについては[スポンサードインテリジェンスガイド](/docs/sponsored-intelligence/overview)を参照。 ## 既存スタックとの接続 AdCP は既存の API やダッシュボードと並列して動作します。セルフサービスプラットフォームや内部のキャンペーン管理システムを置き換えるものではありません。AI エージェントが使用できる標準インターフェースを追加します。 | 既存システム | AdCP との関係 | | ---------------- | ------------------------------------------------- | | セルフサービスダッシュボード | AdCP は異なるオーディエンス(AI エージェント、人間ではない)に対応 | | 管理 API | AdCP は標準的なサブセットを提供し、API は完全な機能セットを提供 | | 広告サーバー(GAM、カスタム) | AdCP はキャンペーン指示を送信し、広告サーバーはデリバリーを処理 | | OpenRTB 統合 | AdCP はキャンペーン設定を処理し、OpenRTB はインプレッションレベルのオークションを処理 | ## はじめる インタラクティブビルダーを使って adagents.json ファイルを作成・検証します。 セルサイドタスク実装の完全なリファレンス: プロダクト、メディアバイ、デリバリーレポート。 実装ガイド、SDK、統合パターン。 プラットフォーム向けの AdCP 実装について質問する — コードは不要。 AI プラットフォームと広告ネットワーク向けのプロダクトモデリングとワークフロー。 リテールメディアネットワーク向けのプロダクトモデリングとワークフロー。 # ストーリーボードトラブルシューティング Source: https://adcp-docs-ja.pier1.co.jp/docs/building/operating/storyboard-troubleshooting AdCP コンプライアンスストーリーボードを実行するときの一般的な失敗パターン — 欠けているフィクスチャ、署名チャレンジ、エンベロープドリフト、コンテキストエコー、ケイパビリティ不一致、ステートマシンエラーコード。 コンプライアンスストーリーボードがエージェントに対して失敗すると、ランナーはステップ名とエラーテキストをレポートします。このページは、最も一般的なエラーパターンをその根本原因と修正にマップし、SDK ソースやランナー内部を探検せずに各失敗クラスを解決できるようにします。 各セクションは、あなたが見るエラー、その意味、エージェントで何を変えるかを示します。 ## Unknown fixture エラー ``` × (unknown step): PRODUCT_NOT_FOUND: Package 0: Product not found: test-product ``` ストーリーボードの `sample_request` はハードコードされた ID(`test-product`、`test-pricing`、`campaign_hero_video`、`gov_acme_q2_2027` など)を参照します。ランナーは、変更ステップが実行される前にエージェントがその ID をカタログに持つことを期待します。 **修正:** `comply_test_controller` を実装し、ストーリーボードの `fixtures:` ブロックで宣言されたシードシナリオを尊重します。`prerequisites.controller_seeding: true` が設定されると、ランナーは、メインフェーズが実行される前に外部キー順で `seed_product`、`seed_pricing_option`、`seed_creative`、`seed_plan`、`seed_media_buy` を呼ぶフィクスチャフェーズを自動注入します。 完全なシードコントラクトについては [コンプライアンステストコントローラー — シナリオ](/docs/building/by-layer/L3/comply-test-controller#scenarios) を参照。シード呼び出しで `UNKNOWN_SCENARIO` を返すエージェントは、ストーリーボードを `not_applicable` とグレードします — 欠けているサンドボックスサーフェスでペナルティを受けませんが、事前シードされた状態に依存するストーリーボードを通過できません。 ## 401 で署名チャレンジが欠けている ``` × (unknown step): expected error="request_signature_required", got error="(none)" ``` ストーリーボードは `get_adcp_capabilities.request_signing.required_for` で宣言された操作に未署名リクエストを送りました。エージェントは 401 で拒否しましたが `WWW-Authenticate: Signature ...` チャレンジヘッダーを含めなかったため、ランナーはトランスポートバインディングからエラーコードを解決できませんでした。 **修正:** 欠けているまたは無効な署名によって引き起こされるすべての 401 で RFC 9421 チャレンジヘッダーを発します。ランナーはトランスポートバインディング順序経由でエラーコードを解決します — `WWW-Authenticate` ヘッダーが欠けている場合、JSON ボディが有用なメッセージを運んでもエラー分類は「(none)」にフォールバックします。 リファレンス SDK は、これらのエラーを `@adcp/sdk/signing` の `RequestSignatureError` 経由で `.code: RequestSignatureErrorCode` で構築します。完全なタクソノミー(`request_signature_required`、`request_signature_header_malformed`、`request_signature_tag_invalid`、`request_signature_window_invalid`、`request_signature_key_unknown` など)はそのモジュールで列挙されます。エージェントは、SDK を話す呼び出し元が自動的に回復できるよう、チャレンジで同じコードをサーフェスすべきです(SHOULD)。 チャレンジヘッダー形式については [署名付きリクエスト(トランスポート層)](/docs/building/by-layer/L1/security#署名付きリクエストトランスポート層) と [transport-error バインディング順序](/docs/building/operating/transport-errors) を参照。 ## レスポンスエンベロープドリフト ``` × (unknown step): Response contains errors array ``` ベクターは `check: error_code` を使いましたが、あなたのレスポンスはランナーのクライアント検出順序が期待しなかった形状でエラーをサーフェスします。実際には、これはトランスポート層が既に `adcp_error` を運んでいたときエージェントが `errors[]` を返した(またはその逆)ことを意味します — ストーリーボードは単一のエラーコードをアサートし、ランナーはあなたが発したのと異なる層からそれを解決しました。 **修正:** [エンベロープ対ペイロードの 2 層モデル](/docs/building/by-layer/L3/error-handling#envelope-vs-payload-errors-the-two-layer-model) に従い、レスポンスごとに 1 つのエラーサーフェスを選び、それに固執します。MCP: 構造化コンテンツには `adcp_error`、タスクペイロードエラーには `errors[]`。A2A: 同じ層が適用 — エンベロープのトランスポートエラー、タスクアーティファクトの DataPart のアプリケーションエラー。 ランナーの `check: error_code` は形状非依存 — どちらの層からも解決する — が、エージェントが両方を同時に発すると不一致になりえ、ランナーは解決されたコードをベクターの期待に対してグレードします。1 つのサーフェスを選び一貫していれば分岐を避けます。 ## コンテキストエコー失敗 ``` × (unknown step): expected field "context.correlation_id" = "xyz", got (missing) ``` エージェントはリクエストの `context:` オブジェクトを含まないレスポンスを返しました。`context: { correlation_id: ... }` を送るすべてのストーリーボードステップは、`context.correlation_id` がレスポンスで変更なくエコーされることをアサートします。 **修正:** エラーを含むすべてのレスポンスで完全な `context:` オブジェクトを逐語的に保持します。エコーコントラクトは規範的 — バイヤーは `correlation_id` を使ってマルチエージェントフローをつなぎ、ランナーはすべてのコンテキストを運ぶステップをそれでグレードします。[Context and sessions — 規範的エコーコントラクト](/docs/building/by-layer/L2/context-sessions#normative-echo-contract) を参照。 キャプチャは同じコントラクトを逆に使います: `context_outputs:` を通じて `"$context."` を渡すストーリーボードは、プロデューサーステップの検証が通過した後にキャプチャが投入されることに依存します。プロデューサーが失敗または `context:` を省略したとき `$context.foo` を読む下流ステップは `unresolved_substitution` とグレードされます。 ## ケイパビリティベクター不一致(ランナーが宣言、エージェントがサポートしない) ``` × (unknown step): capability X asserted but not declared in get_adcp_capabilities ``` ストーリーボードは、エージェントがその `get_adcp_capabilities` レスポンスでアドバタイズしないケイパビリティを要求するステップをディスパッチしました。ランナーはこれらのステップを自動スキップすべきです。代わりに失敗としてグレードされているのを見ている場合、ケイパビリティが誤ったキーで宣言されているか、ランナーが自動スキップパスを欠いています。 **修正:** `get_adcp_capabilities.tools` リストと任意の required-for フィールド(`request_signing.required_for`、`idempotency.supported_tools` など)を再確認します。専門化エージェントにのみ適用されるベクターについては、ストーリーボード作者が `skipVectors` を使ってオプトアウトを明示的にフラグできます。実装者として、修正はほぼ常にベクターではなくケイパビリティ宣言にあります。 ## required-for 合成 ``` × (unknown step): missing auth — step requires authenticated or signed ``` ランナーは、認証された認証情報または署名付きリクエストのいずれかを期待する変更ステップに遭遇し、トランスポートがどちらも運びませんでした。通常これは、テストキットが `auth.api_key` または `auth.basic` を宣言せず **かつ** エージェントがリクエスト署名サポートをアドバタイズしないことを意味します — ランナーに呼び出しを認証する方法を残しません。 **修正:** (a) ランナーが静的な Authorization ヘッダー認証情報を使うようテストキットに `auth.api_key` または `auth.basic` を宣言するか、(b) ランナーが代わりにリクエストに署名するよう `get_adcp_capabilities.request_signing` 経由でリクエスト署名をアドバタイズします。ランナーの `requireAuthenticatedOrSigned` ゲートはどちらのパスも受け入れます — 両方が欠けているときのみ失敗します。 ## Static-credential agent: no auth mechanism contributed (assert\_mechanism) ```json theme={null} { "check": "assertion", "passed": false, "description": "Probe validations failed.", "expected": ["auth_mechanism_verified"], "actual": [] } ``` `mechanism_required` フェーズが、任意の optional auth フェーズから `auth_mechanism_verified` への寄与を見つけませんでした。これは静的認証情報のみのエージェント(Bearer API キーまたは HTTP Basic)の最も一般的な失敗で、実際の auth 問題ではありません — テストキット設定のギャップです。 **何が起こったか:** `api_key_path` フェーズは `skip_if: "!test_kit.auth.api_key"` を持ち、`basic_path` フェーズは `skip_if: "!test_kit.auth.basic"` を持ちます — それぞれ、テストキットが一致する認証情報を宣言しない限りスキップされます。`oauth_discovery` フェーズは `/.well-known/oauth-protected-resource/...` で 404 になります(静的認証情報のみのエージェントに期待される。それらの失敗はランナーによって黙って無視される)。すべての optional auth フェーズが何も寄与しないと、`assert_mechanism` は `actual: []` を見ます。 **`--auth TOKEN` の区別:** ランナーに渡す `--auth TOKEN` フラグはランナー自身のセッション認証情報です — あなたのエージェントへのランナー自身のリクエストを認可します。それは、静的認証情報フェーズが肯定的および無効認証情報プローブ中に送る特定の認証情報である `test_kit.auth.api_key` や `test_kit.auth.basic` とは完全に別です。これらは同じトークンではなく交換可能でありません。 **Bearer API キーエージェントの修正:** すべてのデフォルト AdCP ブランドテストキットは、`demo--v1` 命名規則を使って `auth.api_key` の下にそのプローブ API キーを宣言します。デフォルトテストキット(`acme-outdoor`)は `demo-acme-outdoor-v1` を使います。キットのプローブキーを本番鍵と並んで有効なコンプライアンステスト認証情報として受け入れるようエージェントを設定します。`demo--` プレフィックスが AdCP 適合性ハンドルです — 対して実行するキットのプレフィックスに一致する任意の Bearer トークンを受け入れます(サフィックスは仕様バージョンをまたいでローテートでき、プレフィックスは安定のまま): ```typescript theme={null} serve({ authenticate: verifyApiKey({ keys: { [PRODUCTION_TOKEN]: { principal: 'my-principal' }, // Accept the default compliance kit's probe key (and any future suffix rotation) 'demo-acme-outdoor-v1': { principal: 'compliance-runner' }, } }) }) ``` **HTTP Basic エージェントの修正:** `username`/`password` またはエンコードされていない `username:password` ペアを含む単一の `credentials` 値のいずれかで `auth.basic` を宣言するテストキットを使います。その Basic 認証情報を本番 Basic 認証情報と並んで有効なコンプライアンステスト認証情報として受け入れるようエージェントを設定します。`basic_path` フェーズは次に有効な Basic 認証情報とランダム無効な Basic 認証情報を送り、エージェントが有効な認証情報を受け入れ無効なものを拒否するときのみ `auth_mechanism_verified` を寄与します。 自身の本番認証情報のみを受け入れるエージェントは、一致する静的パスをスキップし(テストキット認証情報が一致しない)、`oauth_discovery` に失敗し(PRM なし)、`assert_mechanism` で `actual: []` に着地します。テストキット認証情報を許可された認証情報セットに追加すれば十分です — PRM エンドポイントや OAuth 発行者は不要です。 OAuth フェーズを「通過」するために存在しない発行者を指す偽の `/.well-known/oauth-protected-resource/...` を提供しないでください。それは、ストーリーボードが捕まえるよう設計された advertised-but-unserved 失敗モードをトリガーします。 carve-out がなぜ存在するか、静的認証情報 / `oauth_discovery` フェーズセマンティクスがどう設計されたかの背景については、[既知の仕様の曖昧さ — 非 OAuth エージェントに必要な PRM](/docs/building/cross-cutting/known-ambiguities#prm-required-for-non-oauth-agents) を参照。 ## `INVALID_STATE` 対 `INVALID_TRANSITION` 混同しやすい 2 つのコード: * **`INVALID_STATE`** — 「リソースがこのアクションを許さない状態にある」の正準 AdCP メディアバイエラーコード。要求されたように遷移できないメディアバイに対する `create_media_buy`/`update_media_buy`/`pause`/`resume`/`cancel` で使う。権威ある使用については `media-buy/specification.mdx` と `media-buy/media-buys/index.mdx` を参照。 * **`INVALID_TRANSITION`** — `comply_test_controller` サンドボックスプリミティブに固有。ランナーがセラーが拒否するステートマシン遷移を要求するとき(例: `active` を通らずに `approved` → `archived` を強制)に発せられる。[コンプライアンステストコントローラー — シナリオ](/docs/building/by-layer/L3/comply-test-controller#scenarios) を参照。 本番タスクで `INVALID_STATE` をアサートするストーリーボードベクターに対してエージェントが `INVALID_TRANSITION` を返すのはエラーコード語彙の不一致です — `INVALID_TRANSITION` は `static/schemas/source/enums/error-code.json` の正準 enum になく、コンプライアンステストコントローラーの外に現れるべきではありません。 ## 上記のいずれも一致しないとき ここの何にもマップしない失敗に遭遇した場合、[既知の仕様の曖昧さ](/docs/building/cross-cutting/known-ambiguities) ページを確認してください — 一部のストーリーボードは解決済みだが未リリースの仕様ギャップでブロックされ、回避策はそこで追跡されます。 まだ詰まっている? 完全なランナー出力とストーリーボード名とともに [adcontextprotocol/adcp](https://github.com/adcontextprotocol/adcp/issues) で issue を提出してください。メンテナーは通常、エラーシグネチャからパターンを絞れます。 # サポートリカバリーランブック Source: https://adcp-docs-ja.pier1.co.jp/docs/building/operating/support-recovery-runbooks エスカレーション SLA フォローアップ、認証完了リカバリー、ドメイン検証、レジストリクロールリカバリーの運用ランブック。 # サポートリカバリーランブック これらのランブックは、Addie、認証、レジストリのキューに黙って座るべきでないサポートケースをカバーします。 ## エスカレーション SLA フォローアップ Addie サポートリクエストは、管理エスカレーションダッシュボードとリクエスターのダッシュボードで可視です。SLA 強制ジョブは毎時実行されます。 管理フォローアップルール: * 緊急のオープンリクエストは 4 時間後に設定されたプライベートエスカレーション Slack チャネルに再サーフェスされます。 * 他のオープンリクエストは 24 時間後に再サーフェスされます。 * 承認済みまたは進行中のリクエストは、更新なしで 24 時間後に再サーフェスされます。 * リクエスターは 24 時間後に可視のダッシュボード更新を受け取り、詳細を追加するか自分でリクエストをクローズできます。 オペレーターチェックリスト: 1. `/admin/escalations` を開く。 2. アクティブなリクエストにフィルターし「Needs pickup」または「Needs update」を探す。 3. 人間が所有するときリクエストを承認または進行中に移す。 4. リクエスターがステータスを必要とするときリクエスター可視のノートを追加する。 5. 修正されたときリクエストを解決し、適切なときユーザーに通知する。 Slack 通知が欠けている場合、`/admin/settings` でエスカレーションチャネルを設定してください。 ## 認証完了リカバリー 認証リカバリージョブは 6 時間ごとに実行されます。モジュールまたは認証情報の再照合を終えなかった合格した試行を探します。 自動動作: * 合格した試行を `learner_progress` に再照合。 * 認証情報の適格性と授与を再実行。 * 自動修復が認証情報を授与できない場合、重複排除されたエスカレーションを提出。 手動修復: 1. `/admin/certification` を開く。 2. 「Attempts needing attention」で、合格した試行に「Reconcile credentials」をクリック。 3. 試行にまだ警告がある場合、learner 詳細パネルを開く。 4. learner、モジュール、Addie スレッドの教育チェックポイントがあるときのみ「Admin completion repair」を使う。 5. ローカル認証情報が存在するが Certifier バッジフィールドが欠けているとき「Issue missing badges」を実行。 6. ノートフィールドにエスカレーション ID または理由を記録。 learner レコードが既にモジュール完了を証明しない限り、重複した手動認証情報を作らないでください。まず learner レコードを修復し、次に認証情報を発行またはバックフィルします。 ## ドメイン検証リカバリー メンバーが WorkOS DNS TXT レコードを公開したがセルフサービス検証パスがブロック、レート制限、または webhook がローカル状態を更新しなかったとき、管理ドメイン検証リカバリーを使います。 これは内部サポートワークフローです。認証された管理ドメインツールまたはプライベート運用ランブックを使ってください。公開ドキュメントは、管理エンドポイントパス、bearer トークン例、WorkOS チャレンジトークン処理を公開すべきではありません。 レジストリ/ドメインエスカレーションを解決済みとしてクローズする前に、AAO UI だけでなくレジストリ状態を確認してください: * ターゲットドメインが意図された組織のローカル `organization_domains` 行を持つ。 * その行が `verified=true`。 * サポートリクエストが会社組織用のとき、行がメンバーの個人ワークスペースに添付されていない。 * レジストリが該当ドメインについて `member:null` をもはや返さない。 * 要求されたエージェントホスト名が登録組織の検証済みドメイン行でカバーされる。 WorkOS が検証済みドメインを示すがローカル行が欠けている、古い、または別の組織に添付されている場合、意図された組織の WorkOS-domain 再照合アクションを実行します。これは WorkOS を真実の源泉としてリプレイし、ドメインを `organization_domains` にミラーし、検証済みドメインのブランドレジストリ同期を再実行します。DNS 検証をバイパスしません。WorkOS はまずドメイン/チャレンジを知る必要があります。 個人ワークスペース/会社組織分割インシデントについては、まず読み取り専用の管理プレビューを実行します: * 内部運用ランブックまたは管理 UI からプライベートなドメイン分割プレビューを使う。 * プレビューは WorkOS 状態、ローカル `organization_domains`、メンバープロファイル存在、組織メンバーシップ、次の安全なアクションをレポートする。 * プレビューが WorkOS が既に会社組織のドメインを検証すると言うときのみ再照合アクションを実行。 * 課金が個人ワークスペースに添付されているように見える場合、人間レビュー後に別の Stripe 顧客プレビュー/確認ワークフローを使う。ドメイン修復パスで課金を動かさない。 結果: * `success: true`: WorkOS が DNS を確認しローカル `organization_domains` が再照合された。 * `still_pending`: WorkOS がまだ TXT レコードを見られない。メンバーにレコード名とトークンを確認するよう依頼。 * `no_challenge`: メンバードメイン設定または管理ドメインツールから新しいドメインチャレンジを発行。 DNS 伝播は通常数分かかりますが、古いプロバイダーキャッシュはより長く続きうる。数秒ごとにリトライし続けないでください。DNS レコードを修正し伝播後にリトライしてください。 リクエストが無効または疑わしい場合、ノート付きで `wont_do` としてクローズします。ローカルドメイン行が欠けている、未検証、または誤った組織に添付されている間、レジストリ/ドメインエスカレーションを `resolved` とマークしないでください。 ## レジストリとハートビートリカバリー メンバーが `adagents.json`、`brand.json`、認可、またはエンドポイントヘルスを修正した後、これらのトリガーを使います。 パブリッシャーとブランドのクロールリクエストは、認証されたメンバーまたは管理リクエストと JSON `domain` ボディを要求します: ```bash theme={null} curl -X POST "https://agenticadvertising.org/api/registry/crawl-request" \ -H "Content-Type: application/json" \ -b "$SESSION_COOKIE" \ --data '{"domain":"example.publisher"}' ``` 期待される受理レスポンス: ```json theme={null} { "message": "Crawl request accepted", "domain": "example.publisher" } ``` ブランドマニフェストについては、同じボディ形状を使います: ```bash theme={null} curl -X POST "https://agenticadvertising.org/api/registry/brand-crawl-request" \ -H "Content-Type: application/json" \ -b "$SESSION_COOKIE" \ --data '{"domain":"example.brand"}' ``` 期待される受理レスポンス: ```json theme={null} { "message": "Brand crawl request accepted", "domain": "example.brand" } ``` エージェントリフレッシュとハートビート再キューは、エンコードされたエージェント URL を使います。呼び出し元はエージェントを所有するか AAO 管理者でなければなりません。 ```bash theme={null} AGENT_URL="https://seller.example/adcp" ENCODED_URL="$(node -e 'process.stdout.write(encodeURIComponent(process.argv[1]))' "$AGENT_URL")" curl -X POST "https://agenticadvertising.org/api/registry/agents/$ENCODED_URL/refresh" \ -b "$SESSION_COOKIE" ``` リフレッシュは最新のプローブ結果、または監視が一時停止、レート制限、プローブ失敗の場合 `409`、`429`、`502` エラーを返します。 エージェントが通常のモニターによって確認されるべきとき、同期プロービングなしでハートビートをキューイング: ```bash theme={null} curl -X POST "https://agenticadvertising.org/api/registry/agents/$ENCODED_URL/monitoring/requeue" \ -b "$SESSION_COOKIE" ``` 期待されるレスポンス: ```json theme={null} { "requeued": true } ``` エンドポイントまたはケイパビリティ修正後の特定エージェントにはリフレッシュを使います。パブリッシャー認可変更にはクロールリクエストを使います。 # トランスポートエラーマッピング Source: https://adcp-docs-ja.pier1.co.jp/docs/building/operating/transport-errors AdCP の構造化エラーが MCP と A2A トランスポートを通じて伝達される方法: 抽出パス、JSON-RPC コード、リカバリー動作、クライアント実装要件。 AdCP エラーは**アプリケーション層**のエラーです。トランスポートエラーチャネルではなく、ツール/タスクレスポンスに属します。このページでは [`error.json`](https://adcontextprotocol.org/schemas/latest/core/error.json) スキーマが MCP と A2A レスポンスエンベロープにどのようにマッピングされるかを定義します。 エラースキーマ自体、標準コード、リカバリー戦略については[エラーハンドリング](/docs/building/by-layer/L3/error-handling)を参照。 ## 層の分離 | 層 | 例 | チャネル | | -------- | --------------------------------------------------- | ------------------------------- | | トランスポート | 接続拒否、不正な JSON-RPC、内部クラッシュ | JSON-RPC `error` / A2A プロトコルエラー | | アプリケーション | `RATE_LIMITED`、`BUDGET_TOO_LOW`、`CREATIVE_REJECTED` | ツール/タスクレスポンスボディ | トランスポートエラーはプロトコルライブラリが処理します。アプリケーションエラーはビジネスロジックが処理します。混在させると、AdCP エラーを有用にする構造化リカバリーデータが失われる。 ## MCP バインディング ### ツールレベルエラー すべての AdCP エラーコードの標準パス。ツールが実行し、リクエストを理解して、構造化エラーを返します。 **現在の実用的なパス:** ほとんどの MCP ホスト(Claude Desktop、Cursor、Windsurf)はエラーレスポンスの `content` テキストを読み取り、`structuredContent` を LLM やプログラム的なコンシューマーに公開しません。`structuredContent` の採用が広まるまで、テキストフォールバックパスがほとんどのエラーが抽出される方法です。サーバーは両方のパスをサポートすべきだ: ```json theme={null} { "content": [{"type": "text", "text": "{\"adcp_error\":{\"code\":\"RATE_LIMITED\",\"message\":\"Request rate exceeded\",\"retry_after\":5,\"recovery\":\"transient\"}}"}], "isError": true, "structuredContent": { "adcp_error": { "code": "RATE_LIMITED", "message": "Request rate exceeded", "retry_after": 5, "recovery": "transient" } } } ``` **`content` テキスト**は AdCP エラーをテキストベース抽出用の JSON 文字列として含みます。**`structuredContent.adcp_error`** はプログラム的クライアントが同じエラーをサポートする場合に含みます。人間が読めるテキストを含めるサーバーは2番目のコンテンツアイテムとして追加すべきで、簡潔に保つ(1文): ```json theme={null} { "content": [ {"type": "text", "text": "{\"adcp_error\":{\"code\":\"RATE_LIMITED\",\"message\":\"Request rate exceeded\",\"retry_after\":5,\"recovery\":\"transient\"}}"}, {"type": "text", "text": "Rate limited — retry in 5s."} ], "isError": true, "structuredContent": { "adcp_error": { "code": "RATE_LIMITED", "message": "Request rate exceeded", "retry_after": 5, "recovery": "transient" } } } ``` **`structuredContent` 存在時の簡潔なテキスト。** `structuredContent` が完全なエラーを含む場合、人間が読めるテキストコンテンツアイテムは1つの簡潔な文(例: "Rate limited — retry in 5s.")にすべきです。エラーの詳細はすでに `structuredContent` と JSON テキストフォールバックにある。散文でエラー全体を繰り返すと、特にリトライ中に蓄積する一時的なエラーの場合、LLM コンテキストトークンが無駄になります。 **`adcp_error` キー**: 名前空間化により `structuredContent` にも現れる可能性のある成功データ(例: `products`)との衝突を避ける。単一のキーにより検出が簡単になります。 **`structuredContent`** には MCP 2025-03-26 以降が必要です。古い MCP バージョンのサーバーは `structuredContent` を省略する — `content[0].text` の JSON 文字列で十分です。クライアントはテキストフォールバックパスを通じてこれを解析する([クライアント検出順序](#client-detection-order)を参照)。 ### トランスポートレベルエラー ツールディスパッチの前にインフラ(API ゲートウェイ、レートリミットミドルウェア)がリクエストを拒否した場合、ツールは実行されない。`data` に AdCP エラーを含む予約済み JSON-RPC エラーコードを使用します: ```json theme={null} { "jsonrpc": "2.0", "id": "req-123", "error": { "code": -32029, "message": "Rate limit exceeded", "data": { "adcp_error": { "code": "RATE_LIMITED", "retry_after": 5, "recovery": "transient" } } } } ``` ### 予約済み JSON-RPC コード | コード | AdCP エラーコード | タイミング | | -------- | --------------------- | ------------------------- | | `-32029` | `RATE_LIMITED` | ツールディスパッチ前のインフラレートリミット | | `-32028` | `AUTH_REQUIRED` | ツールディスパッチ前にミドルウェアが認証を拒否 | | `-32027` | `SERVICE_UNAVAILABLE` | インフラヘルスチェック失敗、アップストリームダウン | これらのコードは JSON-RPC サーバー定義範囲(`-32000` から `-32099`)にある。他のすべての AdCP エラーコードはツールレベルパスのみを使用します。 **MCP サーバー SDK の注意:** ツールハンドラー内から `McpError` をスローすると JSON-RPC エラーレスポンスが生成される — SDK はそれを `isError: true` ツール結果に変換**しない**。つまり `-32029` はミドルウェアからスローされても、ツールハンドラーからスローされても同じように機能します。しかしアプリケーション層エラー(ツールがリクエストを理解して構造化された失敗を返す場合)は、JSON-RPC エラーコードではなく上記の `isError: true` ツールレベルパスを使うべきです。`-32029`/`-32028`/`-32027` はツールディスパッチ前にリクエストを拒否するインフラのために予約します。 ### MCP サーバー実装 ```javascript theme={null} function adcpErrorResponse(error) { const adcpError = { code: error.code, message: error.message, recovery: error.recovery, ...(error.retry_after != null && { retry_after: error.retry_after }), ...(error.field != null && { field: error.field }), ...(error.suggestion != null && { suggestion: error.suggestion }), ...(error.details != null && { details: error.details }), }; return { content: [{ type: "text", text: JSON.stringify({ adcp_error: adcpError }) }], isError: true, structuredContent: { adcp_error: adcpError }, }; } server.tool( "get_products", "Search product catalog", { query: z.string() }, async ({ query }) => { try { const products = await searchProducts(query); return { content: [{ type: "text", text: `Found ${products.length} products` }], structuredContent: { products }, }; } catch (err) { if (err.code && err.recovery) { return adcpErrorResponse(err); } throw err; } } ); ``` ## A2A バインディング ### 失敗したタスク `DataPart` の AdCP エラーと人間/LLM 用の `TextPart` を含む `status: "failed"` を使用します: ```json theme={null} { "id": "task_456", "status": { "state": "failed", "timestamp": "2025-01-22T10:30:00Z" }, "artifacts": [{ "artifactId": "error-result", "parts": [ { "kind": "text", "text": "Rate limit exceeded. Retry in 5 seconds." }, { "kind": "data", "data": { "adcp_error": { "code": "RATE_LIMITED", "message": "Request rate exceeded", "retry_after": 5, "recovery": "transient" } } } ] }] } ``` これは[A2A レスポンスフォーマット](/docs/building/by-layer/L0/a2a-response-format)の規則に従う: 最終状態はデータに `.artifacts` を使用します。 **「ラッパーなし」ルールとの関係。** `adcp_error` キーは失敗したタスクの意図的な例外です。成功レスポンスの `DataPart` がタスク固有のデータ(例: `products`)を含むのとは異なり、失敗したタスクの `DataPart` はエラーのみを含みます。このキーは型の識別子として機能し、クライアントがステータスだけに頼ることなくエラーと成功ペイロードを区別できるようにします。 ### エラー MIME タイプ(オプション) A2A エージェントはエラー `DataPart` の `metadata.mimeType` を設定してもよい: ```json theme={null} { "kind": "data", "data": { "adcp_error": { "code": "RATE_LIMITED", "recovery": "transient" } }, "metadata": { "mimeType": "application/vnd.adcp.error+json" } } ``` クライアントは MIME タイプを必要としてはなりません。`adcp_error` キーが権威あるシグナルです。 ## エンベロープ vs. ペイロードエラー AdCP はエラーを 2 つの異なる場所で公開します。本ページは**トランスポートエンベロープ**(`adcp_error`)を扱います。**ペイロードエラー配列**(`errors[]`)は [Error Handling — Envelope vs. payload errors](/docs/building/by-layer/L3/error-handling#envelope-vs-payload-errors-the-two-layer-model) で扱います。 | 層 | キー | いつ設定するか | 読み手 | | ----------------------- | -------------------------------------------------------------------------- | ------------------------------------------- | ------------------------------ | | **トランスポートエンベロープ**(本ページ) | `adcp_error`(MCP `structuredContent`、A2A `DataPart`、JSON-RPC `error.data`) | タスクが失敗した。トランスポートに型付き・抽出可能なエラーシグナルが必要 | MCP ホスト、A2A クライアント、`@adcp/sdk` | | **タスクペイロード** | `payload.errors[]`(またはトップレベル `errors[]`) | タスクが実行された。ペイロードが 1 つ以上の問題(致命的または非致命的な警告)を報告 | ビジネスロジックのコンシューマー | 致命的なタスク失敗は**両方**の層を設定すべきです(SHOULD) — 正規の `protocol-envelope.json` の例と、規範的な SHOULD については `error-handling.mdx` リファレンスを参照してください。 ## クライアント検出順序 クライアントはこの順序で AdCP エラーを確認しなければなりません: 1. **`structuredContent.adcp_error`**(`isError: true` 付き)— MCP ツールレベルエラー 2. **`artifacts[].parts[].data.adcp_error`** — A2A タスクレベルエラー(アーティファクト) 3. **`status.message.parts[].data.adcp_error`** — A2A タスクレベルエラー(ステータスメッセージ) 4. **`error.data.adcp_error`** — JSON-RPC トランスポートレベルエラー 5. **`adcp_error` キーを含む JSON パースされた `content[].text`** — 古い MCP サーバー用のテキストフォールバック(`isError` レスポンスのみ) 6. **`payload.errors[0]`**(またはトップレベル `errors[0]`)— ペイロード層フォールバック。トランスポートエンベロープが `adcp_error` を表面化しないがペイロードが `errors[]` 配列を運ぶ場合に使用。ペイロード層のみが設定される非致命的なケース(例: 警告を報告する `input-required` タスク)ではペイロードからの読み取りは正当だが、ペイロード経由でのみエラーを表面化する致命的なタスクはエージェント側のコンフォーマンスギャップです。 7. **構造化エラーが見つからない** — 汎用エラー処理にフォールバック クライアントは抽出されたエラーに `string` 型の `code` フィールドがあることを検証しなければなりません。検証が失敗した場合は、構造化エラーが見つからないとして扱います。 ### Storyboard `check: error_code` 契約 Storyboard バリデーターは、エラーがどちらの層にも表面化し得るため、パス固有のアサーションではなく `check: error_code` を使用します。ランナー契約: * `check: error_code` は、上記の[クライアント検出順序](#client-detection-order)を実行してエラーコードを解決します — 優先順位: `adcp_error.code`(トランスポート)→ `errors[0].code`(ペイロード)。 * どちらの層も `code` を運ばない場合、検証は `error_code_not_resolvable` で失敗します。 * Storyboard 作成者はアサーションを特定のパス(例: `check: field_present, path: "errors"`)にピン留めすべきではありません(SHOULD NOT) — それはテストを 1 つの層に結合し、もう一方の層にエラーを表面化するエージェントに対して失敗します。[Storyboard authoring — Asserting on errors](/docs/contributing/storyboard-authoring#asserting-on-errors) を参照してください。 **抽出 vs アクション。** 上記の検出順序は*抽出*層だ — フィールド値をそのまま保持した生の `adcp_error` オブジェクトを返す(範囲外の `retry_after` を含む)。クランプ、リトライロジック、その他の動作要件は*アクション*層で適用されます([リカバリー動作](#recovery-behavior)を参照)。 実際には、実装はまずトランスポートタイプで分岐し、関連するパスのみを確認します: ```javascript MCP Client theme={null} function extractAdcpErrorFromMcp(response) { if (!response.isError) return null; // 1. structuredContent (preferred) if (response.structuredContent?.adcp_error) { return validate(response.structuredContent.adcp_error); } // 2. Text fallback if (response.content) { for (const item of response.content) { if (item.type === 'text' && item.text) { try { const parsed = JSON.parse(item.text); if (parsed && typeof parsed === 'object' && !Array.isArray(parsed) && parsed.adcp_error) { return validate(parsed.adcp_error); } } catch { /* not JSON */ } } } } return null; } // Reject malformed or oversized payloads function validate(error) { if (!error || typeof error !== 'object' || Array.isArray(error)) return null; if (typeof error.code !== 'string') return null; if (error.code.length === 0 || error.code.length > 64) return null; if (JSON.stringify(error).length > 4096) return null; return error; } // For JSON-RPC errors (caught as McpError) function extractAdcpErrorFromMcpError(error) { return validate(error.data?.adcp_error); } ``` ```javascript A2A Client theme={null} function extractAdcpErrorFromA2a(task) { // 1. Artifacts (preferred — final state data) if (task.artifacts) { for (const artifact of task.artifacts) { const dataParts = (artifact.parts || []).filter(p => p.kind === 'data'); for (const part of dataParts) { if (part.data?.adcp_error) { return validate(part.data.adcp_error); } } } } // 2. status.message.parts (some A2A implementations) const parts = task.status?.message?.parts; if (Array.isArray(parts)) { for (const part of parts) { if (part.kind === 'data' && part.data?.adcp_error) { return validate(part.data.adcp_error); } } } return null; } ``` ## リカバリー動作 抽出後、`recovery` フィールドに基づいてリカバリーを適用する: | リカバリー | クライアント動作 | | ------------- | ---------------------------------------------------------------------------------------- | | `transient` | `retry_after` 秒後にリトライします。`retry_after` が欠如または非有限の場合は、クライアントの設定された初期遅延から始まる指数バックオフを使用します。 | | `correctable` | `suggestion` と `field` を呼び出し元に示し、自動リトライはしない | | `terminal` | 人間のオペレーターにエラーを示し、リトライしない | **`retry_after` の境界:** セラーは 1 から 3600 秒の `retry_after` 値を返さなければなりません。クライアントはこの範囲外の値をクランプしなければなりません: 1 未満は 1 に、3600 超は 3600 になります。非有限値(`NaN`、`Infinity`)は欠如として扱わなければなりません。これにより、設定が間違ったサーバーからの積極的なリトライループと病的に長いストールの両方を防ぐ。 **リトライ上限:** バイヤーエージェントはオペレーションごとに最大リトライ回数(例: 3 回)と最大累積リトライ時間(例: 300 秒)を強制すべきです。リトライバジェットを超えて持続する一時的エラーはターミナルとしてエスカレーションすべきです。上限なしでは、すべてのリクエストで `retry_after: 3600` を返す悪意のある、または設定が間違ったセラーがエージェントを無期限に停滞させる可能性があります。 **`recovery` が欠如している場合:** 標準エラーコードテーブルを使用してコードベースの分類にフォールバックします。これにより、[レベル 1](/docs/building/by-layer/L3/error-handling#compliance-levels) サーバー(`code` と `message` のみを返す)でも、対応するクライアントから正しいリカバリー動作を得られます。コードも不明の場合は、`terminal` として扱います。 未知の `recovery` 値(前方互換性)については、`terminal` として扱います。 ```javascript theme={null} // Standard code → recovery mapping for when recovery field is absent const CODE_RECOVERY = { RATE_LIMITED: 'transient', SERVICE_UNAVAILABLE: 'transient', CONFLICT: 'transient', INVALID_REQUEST: 'correctable', AUTH_REQUIRED: 'correctable', POLICY_VIOLATION: 'correctable', PRODUCT_NOT_FOUND: 'correctable', PRODUCT_UNAVAILABLE: 'correctable', PROPOSAL_EXPIRED: 'correctable', BUDGET_TOO_LOW: 'correctable', CREATIVE_REJECTED: 'correctable', UNSUPPORTED_FEATURE: 'correctable', AUDIENCE_TOO_SMALL: 'correctable', ACCOUNT_SETUP_REQUIRED: 'correctable', ACCOUNT_AMBIGUOUS: 'correctable', COMPLIANCE_UNSATISFIED: 'correctable', ACCOUNT_NOT_FOUND: 'terminal', ACCOUNT_PAYMENT_REQUIRED: 'terminal', ACCOUNT_SUSPENDED: 'terminal', BUDGET_EXHAUSTED: 'terminal', }; function getRecovery(adcpError) { if (adcpError.recovery) return adcpError.recovery; return CODE_RECOVERY[adcpError.code] || 'terminal'; } function handleAdcpError(adcpError) { switch (getRecovery(adcpError)) { case 'transient': const raw = adcpError.retry_after; const delay = Number.isFinite(raw) ? Math.max(1, Math.min(3600, raw)) : null; return { action: 'retry', delaySeconds: delay }; case 'correctable': return { action: 'fix_request', field: adcpError.field, suggestion: adcpError.suggestion, }; case 'terminal': return { action: 'escalate', message: adcpError.message }; default: // Unknown recovery value: treat as terminal return { action: 'escalate', message: adcpError.message }; } } ``` ## 推奨 `details` 形式 `details` フィールドはオープンオブジェクトです。相互運用性の発散を防ぐため、セラーは一般的なエラーコードの `details` を設定する際にこれらの標準キーを使用すべきだ: ### `RATE_LIMITED` ```json theme={null} { "code": "RATE_LIMITED", "retry_after": 5, "recovery": "transient", "details": { "limit": 100, "remaining": 0, "window_seconds": 60, "scope": "account" } } ``` | キー | 型 | 説明 | | ---------------- | ------ | ----------------------------------------- | | `limit` | number | ウィンドウ内で許可される最大リクエスト数 | | `remaining` | number | 現在のウィンドウで残っているリクエスト数 | | `window_seconds` | number | レートリミットウィンドウの長さ | | `scope` | string | 制限が適用される対象: `account`、`tool`、または `global` | ### `BUDGET_TOO_LOW` ```json theme={null} { "code": "BUDGET_TOO_LOW", "recovery": "correctable", "details": { "minimum_budget": 500, "currency": "USD" } } ``` | キー | 型 | 説明 | | ---------------- | ------ | --------------- | | `minimum_budget` | number | このプロダクトのセラー最小予算 | | `currency` | string | ISO 4217 通貨コード | ### `AUDIENCE_TOO_SMALL` ```json theme={null} { "code": "AUDIENCE_TOO_SMALL", "recovery": "correctable", "details": { "minimum_size": 10000, "current_size": 2500 } } ``` | キー | 型 | 説明 | | -------------- | ------ | --------------- | | `minimum_size` | number | 必要な最小オーディエンスサイズ | | `current_size` | number | 現在のオーディエンスサイズ | ### `ACCOUNT_SETUP_REQUIRED` ```json theme={null} { "code": "ACCOUNT_SETUP_REQUIRED", "recovery": "correctable", "details": { "setup_url": "https://seller.example.com/setup/acct_123", "setup_steps": ["Accept terms of service", "Add payment method"] } } ``` | キー | 型 | 説明 | | ------------- | --------- | --------------------- | | `setup_url` | string | アカウントセットアップを完了できる URL | | `setup_steps` | string\[] | アカウントが準備できるまでの残りのステップ | ### `CREATIVE_REJECTED` ```json theme={null} { "code": "CREATIVE_REJECTED", "recovery": "correctable", "suggestion": "Revise creative to comply with alcohol advertising policy", "details": { "policy_id": "alcohol-advertising-v2", "policy_url": "https://seller.example.com/policies/alcohol-advertising", "reasons": ["Contains health claims not permitted for alcohol products"] } } ``` | キー | 型 | 説明 | | ------------ | --------- | ------------------- | | `policy_id` | string | 違反したポリシーの識別子 | | `policy_url` | string | 完全なポリシーを確認できる URL | | `reasons` | string\[] | クリエイティブが拒否された具体的な理由 | ### `POLICY_VIOLATION` ```json theme={null} { "code": "POLICY_VIOLATION", "recovery": "correctable", "details": { "policy_id": "targeting-restrictions-v3", "policy_url": "https://seller.example.com/policies/targeting", "violated_rules": ["No age-based targeting for financial products"] } } ``` | キー | 型 | 説明 | | ---------------- | --------- | ----------------- | | `policy_id` | string | 違反したポリシーの識別子 | | `policy_url` | string | 完全なポリシーを確認できる URL | | `violated_rules` | string\[] | 違反した具体的なルール | ### `CONFLICT` ```json theme={null} { "code": "CONFLICT", "recovery": "transient", "message": "Resource was modified since last read", "details": { "resource_id": "mb_12345", "expected_version": 3, "current_version": 5 } } ``` | キー | 型 | 説明 | | ------------------ | ---------------- | -------------------------- | | `resource_id` | string | 競合するリソースの識別子 | | `expected_version` | number \| string | クライアントが操作していたバージョンまたは ETag | | `current_version` | number \| string | サーバー上の現在のバージョンまたは ETag | ### サイズガイダンス セラーは `details` をコンパクトに保つべきです。エラーレスポンスは LLM コンテキストウィンドウを通じて流れ、すべてのトークンにコストがかかる — リトライをトリガーする一時的なエラーは1つの会話内で複数のエラーレスポンスを蓄積することがあります。ガイドラインとして、`details` を 500 シリアル化 JSON バイト未満に保つ(UTF-8 で `JSON.stringify(details).length` を使用 — 非 ASCII コンテンツには重要だ)。 ### `details` スキーマ 推奨される `details` 形式のすべての JSON スキーマはエラーコード列挙と一緒に公開されています: * [`/schemas/latest/error-details/rate-limited.json`](https://adcontextprotocol.org/schemas/latest/error-details/rate-limited.json) * [`/schemas/latest/error-details/budget-too-low.json`](https://adcontextprotocol.org/schemas/latest/error-details/budget-too-low.json) * [`/schemas/latest/error-details/audience-too-small.json`](https://adcontextprotocol.org/schemas/latest/error-details/audience-too-small.json) * [`/schemas/latest/error-details/account-setup-required.json`](https://adcontextprotocol.org/schemas/latest/error-details/account-setup-required.json) * [`/schemas/latest/error-details/creative-rejected.json`](https://adcontextprotocol.org/schemas/latest/error-details/creative-rejected.json) * [`/schemas/latest/error-details/policy-violation.json`](https://adcontextprotocol.org/schemas/latest/error-details/policy-violation.json) * [`/schemas/latest/error-details/conflict.json`](https://adcontextprotocol.org/schemas/latest/error-details/conflict.json) これらのスキーマは推奨であり、必須ではありません。`details` を完全に省略するセラーも適合しています。エージェントは特定の `details` キーを要求してはなりません — `details` が欠如または予期しない形状の場合は `code`、`message`、`recovery` にフォールバックします。 ## セラー固有のエラーコード セラーは[標準語彙](https://adcontextprotocol.org/schemas/latest/enums/error-code.json)にないエラーコードを使用してもよい。セラー固有のコードを標準コードと区別し、セラー間の衝突を避けるために: * セラー固有のコードは `X_{VENDOR}_{CODE}` 形式を使用しなければなりません(例: `X_STREAMHAUS_FLOOR_NOT_MET`) * `{VENDOR}` はベンダーエラーコードレジストリに登録された大文字英数字識別子でなければなりません(`/^[A-Z][A-Z0-9]{1,19}$/` にマッチ) * `{CODE}` は大文字英数字とアンダースコアでなければなりません(`/^[A-Z][A-Z0-9_]{1,39}$/` にマッチ) * エージェントは不明なコードを `recovery` 分類にフォールバックして処理しなければなりません * 不明なコードで `recovery` が欠如している場合は `terminal` として扱います * セラーは PR を提出することで[ベンダーエラーコードレジストリ](https://adcontextprotocol.org/schemas/latest/error-details/vendor-error-codes.json)にベンダープレフィックスとコードを登録すべきです ```javascript theme={null} function handleError(error) { if (isStandardErrorCode(error.code)) { // Handle per standard code semantics return handleStandardError(error); } // Unknown/vendor code: fall back to recovery classification return handleByRecovery(error); } ``` ## クライアントライブラリ要件 この仕様を実装するクライアントライブラリ(`@adcp/client` など)は以下を満たさなければなりません: 1. **構造化エラーを自動的に抽出します。** コンシューマーは、メッセージ文字列の汎用エラーではなく、`code`、`recovery`、`retryAfter`、`field`、`suggestion`、`details` を持つ型付きエラーオブジェクトを受け取るべきです。 2. **検出順序を実装します。** すべてのパスを順番に確認します: `structuredContent`、アーティファクト、`status.message.parts`、`error.data`、テキストフォールバック。 3. **抽出されたエラーを検証します。** `code` が空でない文字列(最大 64 文字)であり、シリアル化されたペイロードの合計が 4096 バイトを超えないことを確認します。検証に失敗したペイロードは破棄します。 4. **テキストフォールバックを `isError` でガードします。** `isError` が `true` の MCP レスポンスでのみ JSON ベースのテキスト抽出を試みる。JSON コンテンツを含む成功レスポンスをエラーとして解釈してはなりません。 5. **リカバリーメタデータを保持します。** 抽出されたエラーには `recovery` と `retry_after` を含め、呼び出し元が再解析なしにリトライロジックを実装できるようにします。 6. **未知のリカバリー値を処理します。** 未知の `recovery` 値は `terminal` として扱います。 7. **`retry_after` をクランプします。** 1 未満は 1 に、3600 超は 3600 になります。非有限値(`NaN`、`Infinity`)は欠如として扱わなければなりません。 8. **テキストフォールバックをサポートします。** `structuredContent` なしの MCP `isError` レスポンスの `content[].text` に対して `JSON.parse` を試みる。`structuredContent` の採用が広まるまで、これが主要な抽出パスになります。 クライアントライブラリは追加で: * `retry_after` が存在する場合に指数バックオフで `transient` エラーを自動リトライしてもよい * コンシューマーがリトライ動作を設定するための `retryPolicy` オプションを公開してもよい * `STANDARD_ERROR_CODES` テーブルを使用して標準エラーコードを型付きエラーサブクラスにマッピングしてもよい ## テストベクター 機械可読のテストベクターは [`/static/test-vectors/transport-error-mapping.json`](https://adcontextprotocol.org/test-vectors/transport-error-mapping.json) で入手可能です。各ベクターには以下が含まれます: * `transport`: `mcp` または `a2a` * `path`: 抽出パス(`structuredContent`、`jsonrpc_error`、`text_fallback`、`artifact`) * `response`: トランスポート固有のレスポンスエンベロープ * `expected_error`: 抽出されるべき AdCP エラー(またはレガシーサーバーの場合は `null`) * `expected_action`: `retry`、`surface_to_caller`、`escalate_to_human`、または `generic_error` クライアントライブラリはこれらのベクターに対して抽出ロジックを検証すべきです。 ## エージェントチェーンでのエラー変換 セラーエージェントがアップストリームサービス(API、データベース、他のエージェント)を呼び出す場合、アップストリームの失敗は呼び出し元に返す前に変換しなければなりません。 **ルール 1: アップストリームエラーを AdCP エラーコードに変換します。** 生のアップストリームエラーをそのまま渡してはなりません。セラーの内部 API からの HTTP 429 は `RATE_LIMITED` になります。データベース接続タイムアウトは `SERVICE_UNAVAILABLE` になります。バイヤーは関係のないシステムのエラーフォーマットを見るべきではありません。 **ルール 2: 呼び出し元の視点からリカバリーを分類します。** セラーがバイヤーのアクションなしにアップストリームの問題を修正できる場合、エラーは `transient` または `terminal` だ — `correctable` ではありません。`correctable` エラーは*バイヤー*が何かを変更する必要があることを意味します。例えば: セラーのアップストリームクリエイティブレビュー API が広告を拒否した場合、それは `correctable`(バイヤーはクリエイティブを修正できます)。しかしセラーの内部課金システムがダウンしている場合、アップストリームエラーが 500 であっても、それは `transient`(バイヤーはリトライすべき)だ。 **ルール 3: 中間者は保持するか変換するが、決して削除しません。** バイヤーとセラーの間に座る中間者(例: 複数のセラーにルーティングするエージェンシーエージェント)は以下を行わなければなりません: * アップストリームがすでに AdCP 準拠の場合は AdCP エラーを**変換せずに渡す**、または * アップストリームが異なるフォーマットを使用している場合はエラーを有効な AdCP エラーに**変換する** 中間者は渡すエラーから `recovery`、`retry_after`、または `details` を削除してはなりません。中間者は複数のアップストリームセラーからのエラーを `errors` 配列に集約してもよく、各エラーは元の `code` と `recovery` を保持します。 ```javascript theme={null} // Seller-side: translate upstream errors for the buyer function translateUpstreamError(upstreamError) { if (upstreamError.status === 429) { return { code: 'RATE_LIMITED', message: 'Request rate exceeded', recovery: 'transient', retry_after: upstreamError.headers?.['retry-after'] || 10, }; } if (upstreamError.status >= 500) { return { code: 'SERVICE_UNAVAILABLE', message: 'Service temporarily unavailable', recovery: 'transient', }; } // Never expose upstream details to the buyer return { code: 'SERVICE_UNAVAILABLE', message: 'An internal error occurred', recovery: 'transient', }; } ``` ## セキュリティ上の考慮事項 エラーレスポンスは LLM コンテキストを通じて流れる。すべてのフィールドはクライアント向けです。 ### セラーの要件 **実装は以下を含めてはなりません:** * 内部サービス名、ホスト名、または IP アドレス * データベースエラーテキスト、SQL フラグメント、またはクエリプラン * スタックトレースまたはファイルパス * 内部サービスからのアップストリーム API レスポンス * 認証情報、トークン、またはセッション識別子 **`suggestion` の境界:** 特定の閾値、有効な識別子、リソースの存在を明かすのではなく、一般的な修正ガイダンス(例: "Increase budget to meet minimum")を提供します。 **`retry_after` の一貫性:** タイミングサイドチャネルを避けるため、ターゲットリソースのプロパティではなく、呼び出し元のレートリミット状態を反映した一貫した値を返します。 **トランスポートレベルコードの粒度:** 予約済み JSON-RPC コード(`-32029`、`-32028`、`-32027`)はインフラエラーの分類を可能にします。エンドポイントのフィンガープリントを最小化したい実装は、これらを単一のコードに統合してもよい。 ### バイヤーエージェントの要件 **エラーフィールドを通じたプロンプトインジェクション。** `message`、`suggestion`、`field`、`details`、およびそれらの中のすべての文字列値は、バイヤーエージェントの LLM コンテキストに入るセラー制御コンテンツです。悪意のある、または侵害されたセラーは、バイヤーエージェントを操作することを目的とした指示を含む値を作成できます。 バイヤーエージェントは以下を行わなければなりません: * **すべてのリカバリー決定を `code` と `recovery` のみを通じてルーティングします。** アクション可能な指示のために `message`、`suggestion`、または `details` 値を解析してはなりません。上記の `handleAdcpError` 関数はこのパターンを示している — メッセージコンテンツではなく `recovery` で切り替える。 * **セラー提供の文字列にデータ境界を使用します。** エラーフィールド値を LLM コンテキストに含める場合、システムプロンプトが信頼できないセラーデータとして指定する明示的なデータデリミター(例: 構造化されたツールレスポンスフィールド、XML スタイルタグ)の内側に置く。セラー提供の文字列を散文や指示に補間しません。 * **セラー文字列を LLM コンテキストに含める前に長さ制限を適用する:** `message`(256 バイト)、`suggestion`(512 バイト)。サイレントにトランケートします。 * **すべての文字列フィールドから非印刷可能文字を除去する:** 制御文字(U+0000–U+001F)、ゼロ幅文字(U+200B–U+200F)、双方向オーバーライド文字(U+202A–U+202E)。 * **最大ペイロードサイズを強制します。** クライアントは `JSON.stringify(error).length` が 4096 バイトを超える抽出された `adcp_error` オブジェクトを破棄しなければなりません。これにより過大な `details` オブジェクトによるコンテキストウィンドウの消耗を防ぐ。 * **オブジェクトミューテーション操作でダイナミックプロパティパスとして `field` を使用しない**(例: `lodash.set`、ブラケット表記チェーン)。`field` 値は表示とフィールドレベルの UI ハイライトのみ用です。 * **キーをフィルタリングせずに `Object.assign`、スプレッド演算子、またはシャローコピーを通じて抽出されたエラーオブジェクトをアプリケーション状態にマージしません。** `__proto__` や `constructor` などのセラー制御キーは一部のランタイムでプロトタイプ汚染を引き起こす可能性があります。 * **生の `details` オブジェクトをシステムプロンプトやツールの説明に含めない。** **URL 検証。** `details.setup_url`(`ACCOUNT_SETUP_REQUIRED` エラー内)は、アカウントセットアップを完了するためにユーザーまたはエージェントが辿ることができるセラー提供の URL だ。クライアントは `setup_url` が `https` スキームを使用し、ユーザー情報コンポーネントを含まず(例: `https://user:pass@evil.com`)、ドメインがセラーの既知のドメインと一致することを検証しなければなりません。これらの確認に失敗した URL は拒否しなければなりません。 `details.policy_url`(`CREATIVE_REJECTED` および `POLICY_VIOLATION` エラー内)は情報提供のみです。クライアントは同じ検証を適用すべきです。すべてのセラー提供 URL は `https` 以外のスキーム(`http`、`javascript`、`data`、`file`)を使用している場合は拒否しなければなりません。 ## 関連情報 * [エラーハンドリング](/docs/building/by-layer/L3/error-handling) — エラースキーマ、標準コード、リカバリー戦略 * [MCP ガイド](/docs/building/by-layer/L0/mcp-guide) — MCP トランスポート統合 * [A2A ガイド](/docs/building/by-layer/L0/a2a-guide) — A2A トランスポート統合 * [A2A レスポンスフォーマット](/docs/building/by-layer/L0/a2a-response-format) — 標準 A2A レスポンス構造 # スキーマ & SDK Source: https://adcp-docs-ja.pier1.co.jp/docs/building/schemas-and-sdks AdCP スキーマとクライアント SDK のクイックスタートリファレンス: インストール、スキーマ URL、バージョンディスカバリー、次のステップ。 このページは、AdCP で構築するための 2 つの基礎的なリソース — すべてのリクエストとレスポンスを定義する JSON スキーマと、それらのスキーマを型付きコードに変える SDK — への道案内をします。 ## SDK AdCP は TypeScript、Python、Go 向けの公式 SDK を提供します。各 SDK はプロトコルレイヤー(ワイヤーフォーマット、署名、認証、ライフサイクルセマンティクス)を吸収するため、あなたはビジネスロジックだけを書けばよいのです。 `npm install @adcp/sdk` `pip install adcp` `go get github.com/adcontextprotocol/adcp-go` 完全なカバレッジマトリクス(各 SDK が現在提供するレイヤー)、バージョンピン留めのガイダンス、パッケージエクスポートの詳細は [Choose your SDK](/docs/building/by-layer/L4/choose-your-sdk) を参照してください。 SDK の背後にあるレイヤーモデル — L0 から L4 がそれぞれ何を含むか、そしてなぜ SDK が単純なプロトコルよりも AdCP で重要なのか — は、[SDK stack リファレンス](/docs/building/cross-cutting/sdk-stack) を参照してください。 ## スキーマ URL AdCP スキーマは、現在のメジャーバージョンエイリアスの下、`https://adcontextprotocol.org/schemas/` で公開されています: | パターン | 例 | 用途 | | ----------- | ---------------------------------------------------------------------------------- | --------------------------------- | | **現在のメジャー** | `https://adcontextprotocol.org/schemas/v3/media-buy/create-media-buy-request.json` | バリデーターを AdCP 3.x スキーマファミリーにピン留めする | スキーマは、すべてのタスクのリクエストとレスポンス、共有 enum、ガバナンスオブジェクト、クリエイティブフォーマット、シグナル定義をカバーします。本番と CI では現在のメジャーバージョン URL を使用し、メジャーリリースをまたいでリンクが安定するようにします。 ワイヤーレベルの `adcp_protocol_version` がスキーマバンドルバージョンとどう関係するか、そしてなぜそれらが別物なのかは、[Versioning & governance](/docs/reference/versioning) を参照してください。 ## adagents.json の認可 パブリッシャーは、エージェントエンドポイントと認可スコープを `adagents.json` で宣言します。スキーマは 6 つの認可タイプ — `property_ids`、`property_tags`、`inline_properties`、`publisher_properties`、`signal_ids`、`signal_tags` — をサポートし、それぞれがエージェントが行動を認可されるインベントリをスコープします。 認可タイプの完全なリファレンスと実例は、ガバナンスドキュメントの [Authorization Patterns](/docs/governance/property/adagents#authorization-patterns) を参照してください。 ## 次のステップ 各言語のカバレッジマトリクス、インストールコマンド、バージョンピン留め。 セラーエージェント構築のためのスキルファイルとステップバイステップガイド。 ケイパビリティの発見とレスポンス処理のためのバイヤー側リファレンス。 コンプライアンススイートを実行して実装を検証します。 # AAO Verified Source: https://adcp-docs-ja.pier1.co.jp/docs/building/verification/aao-verified AdCP エージェントの公開信頼マーク。2 つの修飾子 — ワイヤー形式適合性の Verified (Spec)、本番表面のサンドボックス許容の Verified (Sandbox)。どちらか、または両方を獲得する。 **ステータス**: Request for Comments **最終更新**: May 11, 2026 **AAO Verified** は AdCP エージェントの公開信頼マークです。括弧内に 2 つの修飾子のいずれか — **(Spec)** または **(Sandbox)** — を持ち、両方を持つこともあります。修飾子は、エージェントが *どの軸* の検証を獲得したかを名指します。 これは 2 つの階層ではなく 2 つの軸です。修飾子は異なる質問に答えます: * **Verified (Spec)** — あなたの AdCP プロトコル実装が仕様に一致する。ストーリーボードがどこかで通過する — テストデプロイかもしれず、ローカル開発かもしれない。ワイヤー形式、タスク形状、エラーセマンティクス、ステートマシン遷移がすべてチェックアウトする。本番許容ではなく *ワイヤー形式適合性* を証明する。 * **Verified (Sandbox)** — あなたの **実本番エンドポイント** が `account.sandbox: true` を正しく尊重する。AAO は登録された `agent_url` に対してサンドボックスフラグ付きトラフィックで完全なストーリーボードスイートを実行する。あなたの本番スタックは、スキーマ有効なレスポンス、正しいライフサイクル遷移、適切なエラーエンベロープ、**実世界の副作用なし**(実支出なし、実永続化なし、実プラットフォーム呼び出しなし)でそれを処理する。*本番コードパスがテストトラフィックを正しく許容する* ことを証明する。 エージェントはどちらかの軸、または両方を獲得できます。スタブ広告サーバーの周りの純粋なプロトコルラッパーは正直に **Verified (Spec)** です — それがテストエージェントと開発環境そのものです。本番 URL が完全なストーリーボードスイート全体でサンドボックストラフィックを処理する実本番セラーは、利用可能な最強のクレーム **Verified (Spec + Sandbox)** を獲得します。 2 つの軸は **直交** しています — どちらも他方の前提条件ではありません。別のテストデプロイを持たないセラー(テストモード表面を持たない本番専用プラットフォーム)は、本番 URL をサンドボックスフラグ付きで AAO のランナーに公開することで **(Sandbox)** を直接獲得できます — 別のテストエンドポイントは不要です。逆に、実インプレッションを決してサーブできないテストエージェントは、完全なクレームとして **(Spec)** を獲得します。 バッジは獲得された修飾子を表示します。 **セラー向け TL;DR。** 両方の修飾子は同じ AAO コンプライアンスハートビートを通じて同じストーリーボードを実行します。違いは、ランナーが *どこを* ターゲットするかと、セラーのスタックがサンドボックスフラグ付きトラフィックで *何をする* かです: * **(Spec)** はテストデプロイ / ローカル開発 / サンドボックスエンドポイントに対してストーリーボードを実行します。エージェントの本番表面は行使されません。 * **(Sandbox)** はセラーの登録された本番 `agent_url` に対して、すべてのリクエストに `account.sandbox: true` を付けてストーリーボードを実行します。セラーの本番スタックはフラグを尊重しなければなりません(MUST) — スキーマ有効なレスポンスを返し、状態を正しく遷移させ、エラーを適切に表面化し、**実世界の副作用がゼロ**(課金なし、サンドボックスアカウントを超えた永続化なし、第三者プラットフォーム呼び出しなし)でなければなりません。 セラー側のサンドボックスゲートは規範的です: すべてのサンドボックスフラグ付き本番リクエストは、`sandbox: true` クレームをまとったライブアカウントではなく、永続化されたサンドボックスアカウントに解決しなければなりません。開発側の決定的テストアフォーダンスについては [comply\_test\_controller](/docs/building/by-layer/L3/comply-test-controller) を参照してください。Sandbox グレーディング自体はコントローラーを要求も使用もしません。 ## 各軸が認証するもの ### Verified (Spec) | | | | ---------- | --------------------------------------------------------------------------------------------------------------------------- | | **テスト対象** | エージェント所有者が登録する任意のエンドポイント — テストデプロイ、ローカル開発、サンドボックス専用スタック。ランナーは区別しない。 | | **証明するもの** | AdCP ワイヤー形式、タスク形状、エラーセマンティクス、ステートマシン遷移、宣言された専門分野が動作するツールにマップされること、スキーマ適合性、フィルター動作、冪等性セマンティクス — 実世界の本番状態から分離して行使される。 | | **方法** | [コンプライアンスカタログ](/docs/building/verification/compliance-catalog) からのストーリーボードが、AAO のコンプライアンスハートビート上で登録されたエージェント URL に対して実行される。 | | **頻度** | 約 1 時間のハートビート | | **適格性** | 宣言された専門分野のストーリーボードを通過し、API アクセス階層のアクティブな AAO メンバーシップを保持する任意のエージェント | | **ステータス** | **現在利用可能** | ### Verified (Sandbox) | | | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **テスト対象** | あなたの登録された本番 `agent_url`、すべてのリクエストに `account.sandbox: true` を付けて。 | | **証明するもの** | あなたの本番コードパスがサンドボックスフラグを正しく尊重すること — (Spec) と同じストーリーボードだが、バイヤーが実際にヒットする実本番スタックに対して行使される。スキーマ有効なレスポンス、正しいライフサイクル遷移、適切なエラーエンベロープ、**実世界の副作用ゼロ**。 | | **方法** | (Spec) と同じストーリーボードスイートを、サンドボックスフラグ付きトラフィックでセラーの登録された URL に対して駆動。別の正準キャンペーンインフラなし。 | | **頻度** | (Spec) と同じ約 1 時間のハートビート | | **適格性** | (Spec) と同じ、加えてセラーの本番表面が `account.sandbox: true` リクエストを受け入れ、実状態を永続化せず、第三者プラットフォームを呼ばず、課金せずに処理すること | | **ステータス** | **基盤出荷中** — [#4382](https://github.com/adcontextprotocol/adcp/pull/4382)(account.sandbox スキーマゲート)、[#4384](https://github.com/adcontextprotocol/adcp/pull/4384)(ライブモード拒否ストーリーボード)。完全なグレーディングフレームワークが続く。 | (Sandbox) 修飾子は、以前のドラフトの `Verified (Live)` フレーミングを置き換えます。変更点: 「あなたの実マネー本番コードパスがインプレッションを正しく配信する」(スタックを通る正準キャンペーン)を証明する代わりに、(Sandbox) は「あなたの実本番コードパスが完全なストーリーボードスイート全体でサンドボックスフラグ付きトラフィックを正しく処理する」を証明します。両方とも実本番表面のクレームです。違いは何がテストされるかです。(Sandbox) は新しい AAO 運用インフラなしに専門分野全体で普遍的に達成可能です。再フレーミングの判定については [#4379](https://github.com/adcontextprotocol/adcp/issues/4379) を参照してください。 **`comply_test_controller` について**: コントローラーは採用者自身の統合テストのための **開発/ステージング専用** アフォーダンスです。AAO の (Sandbox) グレーディングはそれを要求も使用もしません。セラーは決定的ローカルテストをサポートするため開発環境でコントローラーエンドポイントを実装してもよい(MAY)が、本番スタックは (Sandbox) を獲得するために `comply_test_controller` を公開する必要はありません。セラー側のサンドボックスゲートが (Sandbox) が証明するものです — 実本番上での、フラグ付きトラフィックの下でのスキーマとライフサイクルの正しさ。開発時のテスト表面自体がどう立ち上げられるか — 状態ローカルセラー用の DB バックの `seed_*` 対 アップストリームプロキシセラー用の SDK の `TestControllerBridge` — は [Test surfaces and the storyboard loop](/docs/building/verification/conformance#test-surfaces-and-the-storyboard-loop) でカバーされます。 ## 命名の歴史 以前のドラフト(#3001)は「AdCP Conformant」と「AAO Verified」を 2 つの別個のマーク名 — 軸ごとに 1 つ — として提案しました。このページは代わりに **括弧内の軸修飾子を伴う単一のブランドマーク** を使います。同じ形状、異なる命名規約: | 以前のドラフト | 現在 | | ------------------------------ | -------------------------- | | AdCP Conformant | AAO Verified (Spec) | | AAO Verified | AAO Verified (Live) | | AdCP Conformant + AAO Verified | AAO Verified (Spec + Live) | リネームの背後にある理由: 合成可能な修飾子を伴う単一のブランドワード(「Verified」)は、バイヤーメッセージングにとってよりクリーンです。バイヤーは 2 つの別個のマークを学ぶ必要がなく、修飾子をインラインで読みます。**Verified (Spec)** を獲得するテストエージェントは、「ジュニア」な Conformant 階層ではなく、完全で尊厳あるクレームです — それらはテストエージェントで、それが全ポイントです。ワイヤー形式はこれを反映します: JWT とレジストリ API 内の単一の `verification_modes: string[]` 配列で、エージェントは `["spec"]` または `["spec", "sandbox"]` を持ちうる。エージェント + ロールごとに 1 つのバッジ URL。軸が獲得されるにつれ修飾子は進化し、埋め込まれたバッジは自動的に現在の状態を反映します。 以前のドラフトの「Tier 1 / Tier 2」の拒否は依然として正しい: 同じワード — *verified* — を 2 つの異なる種類のクレームにわたって階層化することはメッセージを濁します。2 軸修飾子フレーミングはその拒否を継承しつつ、ブランドワードを単数に保ちます。 ## カバレッジギャップは明示的 (Sandbox) フレーミングの下では、適用可能なすべてのストーリーボードがサンドボックスフラグ付きトラフィックでセラーの本番エンドポイントに対して試みられます。可観測性の切り出しはありません — universal ストーリーボード(`signed_requests`、`pagination_integrity` など)は標準スイートの一部として実行されます。レジストリは未選択項目を選択済みだがスキップされた項目と別に保ちます: 実行モード除外(「これはサンドボックス専用実行だったので、ライブ専用プローブは選択されなかった」)、辞退されたオプションケイパビリティ(「セラーはこの機能を主張しなかった」)、決定的テスト表面ギャップ(「本番エンドポイントは正しく `comply_test_controller` を省略している」)。それらはセラーへの異なる要求であり、1 つの一般的な「スキップされたオプションのもの」バケットに折りたたんではなりません(MUST NOT)。(Sandbox) 修飾子はその証拠に対する検証プロファイルです: どの未選択とスキップのクラスが Sandbox バッジに許容可能で、どれがブロッカーのままかを定義します。以前の (Live) 可観測性モデルを置き換えたフレーミング決定については [#4379](https://github.com/adcontextprotocol/adcp/issues/4379) を参照してください。 ## バッジを読む バッジは括弧内に修飾子を伴う単一の shields.io スタイル画像としてレンダリングされます: | 表示 | 意味 | | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `AAO Verified Sales Agent (Spec)` | 宣言されたメディアバイ専門分野のストーリーボードが、テストデプロイ / 開発 / サンドボックス専用エンドポイントに対して通過。ワイヤー形式とプロトコルセマンティクスは正しい。本番スタックのサンドボックス許容はまだ証明されていない。テストエージェントと本番前ロールアウトによくある。 | | `AAO Verified Sales Agent (Spec + Sandbox)` | 両方の軸を獲得。最強のクレーム。エージェントの登録された本番 URL が、実世界の副作用なしにサンドボックスフラグ付きトラフィックの下で完全なストーリーボードスイートを処理する。 | | `AAO Verified Sales Agent (Sandbox)` | `account.sandbox: true` の下でセラーの登録された本番エンドポイントに対してストーリーボードが通過。セラーの本番スタックがサンドボックスゲートを正しく尊重する。別のテストデプロイを持たない本番専用セラーによくある。 | | `AAO Verified — Not Verified` | このエージェント + ロールに対してバッジが発行されていない、またはバッジが取り消された。 | バッジ URL はエージェント + ロールごとに安定です。エージェントが軸を獲得または喪失するにつれ、SVG コンテンツは URL を変えずに更新されます — 埋め込まれたバッジは自動的に現在の状態を反映します。 ## 専門分野を宣言する ```json theme={null} // エージェントは get_adcp_capabilities でそのクレームを宣言する { "supported_protocols": ["media_buy", "creative"], "specialisms": [ "sales-broadcast-tv", "sales-guaranteed", "creative-ad-server" ] } ``` `specialisms` はバッジルーティングフィールドです。それを省略すると、エージェントは依然として `supported_protocols` が含意する universal とプロトコルベースラインストーリーボードを実行しますが、検証エンジンが評価する狭いクレームを持たないため専門分野バッジは発行できません。 ## エージェントが各軸を獲得する方法 軸はストーリーボード証拠に対する検証プロファイルであり、別々のランナー実装ではありません。ランナーは何が通過、失敗、スキップしたかとその理由を記録します。Spec と Sandbox プロファイルは、その証拠が公開修飾子に十分かどうかを決定します。 バイヤー固有の要件は別です。バイヤーは公開修飾子、宣言されたプロトコルまたは専門分野、1 つ以上のオプションケイパビリティ、スキップに対するより厳格な立場を要求できます。例えば、プロポーザルワークフローを必要とするバイヤーは、`media_buy.supports_proposals: false` がプロポーザルストーリーボードをスキップさせた、それ以外はクリーンな実行を拒否できます。決定的統合テストを行うバイヤーは、本番 Sandbox 修飾子がそのコントローラーの公開を要求しなくても、`comply_test_controller` を伴う開発/ステージングエンドポイントを要求できます。 ### Verified (Spec) を獲得するには: 1. 宣言された専門分野のために AdCP を実装する。 2. テストモードエンドポイントでストーリーボードを通過する。(何が失敗しているかを見るため、まず `@adcp/sdk/testing` 経由でローカルに実行する。) 3. API アクセス階層でアクティブな AAO メンバーシップを保持する。 コンプライアンスハートビートが自動的にそれを拾います — [エージェントを登録する](/docs/building/index) を超えた手動登録は不要です。 ### Verified (Sandbox) を獲得するには: セラーは以下によって (Sandbox) を獲得します: 1. **本番 `agent_url`** を AAO に登録する。これは (Spec) を獲得するのと同じ登録です — 別の「コンプライアンスアカウント」や「テストデプロイ」は不要。 2. 本番スタックに **サンドボックスアカウントゲート** を実装する: `account.sandbox: true` を伴うリクエストが到着したとき、セラーは(フィールドを信頼せずに)ターゲットされたアカウントが永続化されたレコード内のサンドボックスアカウントであることを検証し、完全なスキーマ/ライフサイクルの正しさでリクエストを処理しつつ **実世界の副作用ゼロ** を生成する — 実支出なし、実広告サーバーオーダーなし、第三者プラットフォーム呼び出しなし、サンドボックスアカウントの境界された状態を超えた本番永続化なし。 3. API アクセス階層でアクティブな AAO メンバーシップを保持する。 それだけです。コンプライアンスハートビートは (Spec) と同じストーリーボードを実行しますが、すべてのリクエストに `account.sandbox: true` を付けて登録された本番 URL をターゲットします。通過 → (Sandbox) 修飾子が発行されます。 **キー要件: サンドボックスアカウント分離。** セラーはアカウントレベルで明確なサンドボックス/ライブ区別を永続化しなければなりません(MUST)。ライブアカウントに対して `sandbox: true` を主張するリクエストは構造化エラーで拒否されなければなりません(MUST) — 正準拒否チェックについては [#4028](https://github.com/adcontextprotocol/adcp/issues/4028) と `comply-controller-mode-gate` ストーリーボードを参照してください。クロスモードリークが (Sandbox) が対して証明する失敗モードです。 ## 分散型検証 各バッジは署名付き JWT(EdDSA / Ed25519)に裏付けられています。AAO は `/.well-known/jwks.json` で公開鍵セットを公開するため、任意の第三者が AAO の API を呼ぶことなくバッジの真正性を検証できます。 トークンクレーム: ```json theme={null} { "iss": "https://aao.org", "sub": "https://your-agent.example.com/mcp", "aud": "aao-verification", "jti": "", "iat": 1745510400, "exp": 1748102400, "role": "media-buy", "adcp_version": "3.0", "verified_specialisms": ["sales-broadcast-tv", "sales-guaranteed"], "verification_modes": ["spec"], "protocol_version": "3.0.0" } ``` `adcp_version` はこのバッジが対して発行された AdCP リリース(`MAJOR.MINOR`)です。バッジ URL ルートで使われる `(agent_url, role, adcp_version)` アイデンティティとペアになります。**検証者は、関心のある AdCP バージョンに対して `adcp_version` をチェックしなければなりません(MUST)** — 3.1 適合性の証明として提示された 3.0 トークンは権威的ではありません。署名付きクレームは署名時に形状検証されます(`^[1-9][0-9]*\.[0-9]+$`)。検証者は同じ正規表現を防御的に適用すべきです(SHOULD)。 `verification_modes` は獲得された軸の配列です。テストデプロイのストーリーボード通過のみは `["spec"]`。本番エンドポイントもサンドボックスフラグ付きトラフィックの下で通過するエージェントは `["spec", "sandbox"]`。`protocol_version` はバッジがテストされた仕様ビルドの完全な semver です — サポートと監査のための情報的メタデータ。 ランナーカバレッジはバッジモードとは別にレポートされます。本番パスのサンドボックス実行は、本番エンドポイントが正しく `comply_test_controller` を省略するために選択されたコントローラー依存シナリオがスキップするとき、失敗アサーションがゼロでも `partial` になりうる。それは有用な証拠ですが、バッジ発行はレジストリのストーリーボードごとのステータス、未選択理由、スキップ理由を使い、失敗ステップ数だけではありません。期待されるサンドボックスモード除外、オプションケイパビリティスキップ、欠けている必須表面スキップは別々のシグナルとして可視のままで、バイヤーは「この実行の一部でない」を「選択されたが実行されなかった」を「このセラーが実装していない」から区別できます。 レジストリ API はリアルタイムステータスに対して権威的です。JWT は 30 日間キャッシュ可能な証明です。 ## ライフサイクル 検証は 1 回限りの証明書ではなく、継続的に再評価されます。 ### (Spec) * **発行** — すべての宣言された専門分野ストーリーボードが通過し + アクティブなメンバーシップを伴う最初のハートビート。 * **アクティブ** — 各ハートビートで再チェック。JWT が自動更新される。 * **劣化** — 最初のストーリーボード退行が 48 時間の猶予を開始する。オペレーターが調査する間、バッジは (Spec) をレンダリングし続ける。 * **取り消し** — 48 時間連続失敗 → `(Spec)` 修飾子がバッジから落ちる。保持されていれば (Sandbox) は影響を受けない — 軸は独立している。 * **回復** — 通過するストーリーボードが自動的に (Spec) を再発行する。 ### (Sandbox) * **発行** — Sandbox 検証プロファイルが `account.sandbox: true` の下で登録された本番 URL に対して通過し + アクティブなメンバーシップを伴う最初のハートビート。プロファイルは、すべてのバイヤー可視サンドボックスパスアサーションが通過することを要求し、本番禁止のコントローラーフェーズを未選択実行モード除外として扱い、オプションケイパビリティスキップを可視に保つ。 * **アクティブ** — 各ハートビートで再チェック。JWT が自動更新される。 * **劣化** — 最初の Sandbox プロファイル退行が 48 時間の猶予を開始する。オペレーターが調査する間、バッジは (Sandbox) をレンダリングし続ける。クロスモードリーク(実世界の副作用を生成するサンドボックスリクエスト、またはサンドボックスフラグ付きトラフィックを受け入れるライブアカウント)は猶予期間をスキップし即座に取り消してもよい(MAY) — それが (Sandbox) 証明の全ポイントである。 * **取り消し** — 48 時間連続失敗 → `(Sandbox)` 修飾子が落ちる。保持されていれば (Spec) は影響を受けない。 * **回復** — Sandbox プロファイルを通過すると (Sandbox) が再発行される。 メンバーシップの失効は、テスト結果にかかわらずバッジ全体を取り消します — 公開信頼マークはアクティブなメンバーシップを要求します。 ## マークセマンティクス セラーは以下を保持してもよい(MAY): * **(Spec) のみ** — テストモードエンドポイントでストーリーボードが通過。(Sandbox) は登録されていない、または本番エンドポイントがまだサンドボックスフラグ付きトラフィックの下で通過していない。テストエージェント、サンドボックス、本番前ロールアウトによくある。 * **(Sandbox) のみ** — `account.sandbox: true` の下で登録された本番エンドポイントに対してストーリーボードが通過。別のテストモード表面を持たない本番専用プラットフォームによくある。 * **(Spec + Sandbox)** — 最強のクレーム。両方の軸が独立に検証された。 * **どちらもなし** 2 つの軸は独立に評価されます。テストエンドポイントでのストーリーボード退行は (Sandbox) に影響せずに (Spec) を取り消します。本番エンドポイントでのサンドボックスパス退行は (Spec) に影響せずに (Sandbox) を取り消します。セラーはどちらの順序でどちらかを獲得できます。 ## バージョンごとのバッジ 各バッジは **(agent, role, AdCP version)** で識別されます — (Spec) と (Sandbox) の上の 3 つ目の軸。エージェントは AdCP リリース全体で並列バッジを保持できます。例えば、AdCP 3.1 向けのアップグレードを出荷するメディアバイエージェントは以下の両方を保持するかもしれません: * `AAO Verified Media Buy Agent 3.0 (Spec)` — 以前に獲得、まだ有効 * `AAO Verified Media Buy Agent 3.1 (Spec + Sandbox)` — アップグレード後に獲得 各バージョンは独立に評価されます。3.0 ストーリーボード退行は 3.1 に触れずに 3.0 バッジを取り消し、逆もまた然り。メンバーシップの失効は、エージェントのバッジのすべてのバージョンをアトミックに取り消します(信頼マークはバージョンレベルではなくエージェントレベル)。 バッジラベルは AdCP バージョンをロールと修飾子の間にインラインで埋め込みます: `Media Buy Agent 3.1 (Spec + Sandbox)`。 ## 表示 ### SVG バッジ 2 つの URL 形状: ``` # レガシー: 最高のアクティブバージョンに自動アップグレード https://agenticadvertising.org/api/registry/agents/{url-encoded-agent-url}/badge/{role}.svg # バージョンピン留め: 特定の AdCP リリースで凍結 https://agenticadvertising.org/api/registry/agents/{url-encoded-agent-url}/badge/{role}/{adcp-version}.svg ``` 自動アップグレード動作(エージェントが 3.1 を獲得したとき、埋め込み画像が `Media Buy Agent 3.0 (Spec)` から `Media Buy Agent 3.1 (Spec + Sandbox)` に自動的に切り替わる)を望むバイヤーはレガシー URL を埋め込みます。「AdCP 3.0 で検証済み」を具体的に呼び出したいバイヤーはバージョンピン留め URL を埋め込みます。 両方とも `Content-Security-Policy: script-src 'none'` と 5 分キャッシングを伴う shields.io スタイル SVG を返します。検証済みのときティール、そうでないときグレーでレンダリングされます。未知のエージェント、未知のロール、取り消されたバッジはすべてグレーの「Not Verified」バリアントを返します — URL は決して 404 せず、埋め込みを安全にします。エージェントが決して獲得しなかったバージョンのバージョンピン留め URL も「Not Verified」を返します(レガシー URL は現在の最良のマークを表示するのと対照的)。 ### 埋め込みスニペット ```bash theme={null} # レガシー(自動アップグレード) curl https://agenticadvertising.org/api/registry/agents/{url-encoded-agent-url}/badge/media-buy/embed # バージョンピン留め curl https://agenticadvertising.org/api/registry/agents/{url-encoded-agent-url}/badge/media-buy/3.0/embed ``` SVG をエージェントの AgenticAdvertising.org レジストリリスティングへのリンクでラップする HTML と Markdown スニペットを返します。README、ドキュメント、ランディングページ、ソーシャルプロフィールに安全。エージェントの検証軸または AdCP バージョンが変わるにつれ、レガシー埋め込みは自動的に現在の状態を反映します — (Sandbox) が点灯したときやエージェントが新しい AdCP バージョンを出荷したとき、埋め込みの交換は不要です。 ### レジストリフィルター エージェントレジストリはどちらの軸のフィルターも独立に表示します: * **「AdCP を正しく実装するエージェントを見せて」** → `verification_modes contains 'spec'` でフィルター * **「サンドボックスフラグ付きトラフィックの下で通過する本番エンドポイントを見せて」** → `verification_modes contains 'sandbox'` でフィルター * **「両方を持つエージェントを見せて」** → 両方でフィルター 両方のクエリが有効です。オプションを比較するバイヤーは (Sandbox) を使います。新しいエージェントを統合するオーケストレーター開発者は (Spec) を使います。 ### brand.json 拡充 AgenticAdvertising.org が登録されたブランドの brand.json データをサーブするとき、エージェントエントリーは完全なバージョンごとの詳細を伴う `aao_verification` ブロックを得ます: ```json theme={null} "aao_verification": { "verified": true, "verified_at": "2026-04-29T12:34:56.000Z", "badges": [ { "role": "media-buy", "adcp_version": "3.1", "verification_modes": ["spec", "sandbox"], "verified_at": "..." }, { "role": "media-buy", "adcp_version": "3.0", "verification_modes": ["spec"], "verified_at": "..." } ], "roles": ["media-buy"], "modes_by_role": { "media-buy": ["spec", "sandbox"] }, "deprecation_notice": "roles[] and modes_by_role reflect the highest-version badge per role only. A buyer pinned to a specific AdCP version SHOULD read badges[] and filter by adcp_version. Both fields will be removed in AdCP 4.0." } ``` `badges[]` は正準形状です — `(role, adcp_version)` ごとに 1 エントリー、バージョン降順。特定の AdCP バージョンにピン留めされたバイヤーは、`modes_by_role`(ロールごとに最高バージョンエントリーに平坦化され、3.1 バッジのみが Sandbox を持つとき 3.0 バイヤーにエージェントが Sandbox を持つと誤解させうる)を読むのではなく、`adcp_version` でフィルターしなければなりません(MUST)。 `roles[]` と `modes_by_role` は 1 リリースの間 **非推奨エイリアス** として保たれます。**削除ターゲット: AdCP 4.0。** ## 各修飾子を主張する方法 ### **(Spec)** を主張するには 1. API アクセス階層でアクティブな AAO メンバーシップを保持する。 2. `get_adcp_capabilities` で `supported_protocols` と `specialisms` を宣言する。 3. 宣言が義務付けるストーリーボード(universal + protocol ベースライン + specialism ベースライン)を特定の AdCP メジャーバージョンで通過する。 4. AAO コンプライアンスハートビートが **AAO Verified (Spec)** を自動的に発行し、各ハートビートサイクルで再検証する。 ### **(Sandbox)** を主張するには (Sandbox) は **(Spec) から独立** しています — 別のテストデプロイを持たないセラーは、本番エンドポイントをサンドボックスフラグ付きで AgenticAdvertising.org のランナーに公開することで (Sandbox) を直接獲得できます。 1. API アクセス階層でアクティブな AAO メンバーシップを保持する。 2. `get_adcp_capabilities` で `supported_protocols` と `specialisms` を宣言する((Spec) と同じ)。 3. **本番 `agent_url`** を AgenticAdvertising.org に登録する。コンプライアンスハートビートが、すべてのストーリーボードリクエストに `account.sandbox: true` を付けてそれをターゲットする。 4. 本番スタックにサンドボックスアカウントゲートを実装する: (フィールドを信頼するのではなく)永続化されたレコード内でターゲットされたアカウントがサンドボックスであることを検証し、完全なスキーマ/ライフサイクルの正しさでリクエストを処理しつつ **実世界の副作用ゼロ** を生成する — 実支出なし、実広告サーバーオーダーなし、第三者プラットフォーム呼び出しなし、境界されたサンドボックスアカウント状態を超えた本番永続化なし。 5. Sandbox 検証プロファイルを通過する: バイヤー可視サンドボックスパスストーリーボードが通過しなければならず、未選択実行モード除外は許可され、オプションケイパビリティスキップはスコープ選択として可視のまま。本番エンドポイントは `comply_test_controller` を公開することを期待されない。選択されたコントローラー依存決定的フェーズは、要求通りコントローラーが本番から欠けているとき Sandbox バッジのブロッカーではない。 6. `comply_test_controller` をサンドボックスプリンシパルに公開する共有本番表面を運用する場合、その表面について [`comply-controller-mode-gate`](https://adcontextprotocol.org/compliance/latest/universal/comply-controller-mode-gate) チェックを通過する。正準の 2 デプロイセラーは、本番でコントローラーを一切アドバタイズしないことで同じ分離要件を満たす。 7. AgenticAdvertising.org コンプライアンスハートビートは、Sandbox プロファイルがサンドボックスフラグ付きトラフィックで登録された URL に対して通過するとき **AAO Verified (Sandbox)** を発行する。 (Spec) と同じストーリーボード。同じハートビート頻度。異なる証明表面: 任意の登録エンドポイントではなく、サンドボックスフラグ付きの本番。 ## AAO Verified でないもの * **規制または財務の証明ではない。** SOC 2、ISO 27001、ISAE 3402 と類似のフレームワークは運用と財務統制の姿勢に対処します — 独自の監査パスを持つ別個の質問。AAO Verified は AdCP のワイヤーと配信の正しさです。 * **ハードなグラウンドトゥルース照合ではない。** (Sandbox) は本番コードパスがプロトコル表面全体でサンドボックスフラグ付きトラフィックを正しく処理することを証明します。ライブトラフィックの下でセラーの内部広告サーバーダッシュボードに対して実マネー AdCP レポート数値を照合しません。ハード照合は (Sandbox) 階層の外で追跡される別種の証明です。 * **AAO メンバーシップを超えた認定ではない。** [AgenticAdvertising.org 認定プログラム](/docs/learning/overview) は AAO Verified と合成します — 検証は認定への必要な入力ですが、検証自体は認定ではありません。 * **SLA ではない。** AAO Verified はアップタイム、レイテンシー、商業的成果を保証しません。セラーの AdCP 表面が実配信を継続的に反映することを証明します。商業的信頼性はバイヤーとセラーの間です。 * **デューデリジェンスの代替ではない。** バイヤーは依然としてセラーの契約条件、課金姿勢、ガバナンス慣行、インシデントレスポンス姿勢を独立に精査すべきです(SHOULD)。AAO Verified は 1 つの入力であり、全体像ではありません。 ## サポートする仕様との関係 AAO Verified (Sandbox) は小さな規範的 AdCP 仕様要素のセットに乗っています: * **[`account.sandbox` スキーマゲート (#3755 / #4382)](https://github.com/adcontextprotocol/adcp/issues/3755)** — アカウント参照にサンドボックス意図をピン留めし、セラーに永続化されたアカウントモードに対して検証するプロトコルレベルフラグを与える。セラー側ゲートが荷重を担う制御。リクエストフラグはそれ自体では信頼されない。 * **[`comply-controller-mode-gate` ストーリーボード (#4028 / #4384)](https://github.com/adcontextprotocol/adcp/issues/4028)** — コントローラーをサンドボックスプリンシパルに公開する共有表面を選ぶとき、セラーがライブモードアカウントに対するコントローラーディスパッチを正しく拒否することを検証する。正準本番デプロイはコントローラーを一切公開しないことでこれを満たす。 * **[UNKNOWN\_SCENARIO グレーディング (#4226 / #4228)](https://github.com/adcontextprotocol/adcp/issues/4226)** — セラーは開発/ステージングでコントローラーシナリオを選択的に実装してもよい(MAY)。ランナーは欠けている操作を失敗ではなくカバレッジギャップとしてグレードする。コントローラーは (Sandbox) フレーミングに従い開発専用。 以前の (Live) フレーミングのサポート issue(#2963、#2964、#2902 — `attestation_verifier` スコープ、`get_media_buys` 所有権、実データでの動作フィルターアサーション)は延期されました。AAO が正準キャンペーンモデルに戻るなら関連したままですが、(Sandbox) の下では荷重を担いません。 ## 他の表面との関係 * [適合性仕様](/docs/building/verification/conformance) — ストーリーボードを介して *conformant* が何を意味するかを定義。(Spec) 軸はあなたのエージェントがその仕様に一致することを検証。 * [コンプライアンスカタログ](/docs/building/verification/compliance-catalog) — エージェントが主張できるプロトコルと専門分野をインデックス。各宣言された専門分野が、適格な軸で検証エンジンがテストするもの。 * [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) — エージェントが `supported_protocols` と `specialisms` を宣言する場所。宣言が検証への入力。 * AAO メンバーシップ — バッジ発行に必要。メンバーシップの失効はバッジを取り消す。 # Addie とペアプログラミング(Socket Mode) Source: https://adcp-docs-ja.pier1.co.jp/docs/building/verification/addie-socket-mode アウトバウンド WebSocket 経由で開発/ステージング AdCP エージェントを Addie に接続し、会話的にストーリーボードを実行させる。公開 DNS なし、ngrok なし、インバウンド公開なし。 **ステータス**: `CONFORMANCE_SOCKET_ENABLED` の背後でプレビュー利用可能 **最終更新**: May 4, 2026 AdCP エージェントを構築しているとき、最も有用なループは: 進行中のサーバーに対してストーリーボードを実行し、何が失敗するかを見て、修正し、再実行する。今までそれは TLS、DNS、認証、ファイアウォール設定を伴う公開サンドボックスエンドポイントを立ち上げることを意味しました — 意味あるコードを書く前の実際の重労働で、大規模組織では非自明なセキュリティレビュー。 **Socket Mode 経由で Addie とペアプログラミング** はそれを次に折りたたみます: 1 つのライブラリをインストールし、トークンを貼り付け、アウトバウンドで接続する。Addie はあなたの開発エージェントを他の AdCP サーバーと同様に見ます — インバウンド公開なし、公開表面なし — そしてチャットでそれに対して任意のコンプライアンスストーリーボードを実行できます。 ## Socket Mode を使うとき(と使わないとき) | Socket Mode を使うとき… | 公開エンドポイントパスを使うとき… | | ------------------------------------------------- | --------------------------------------------------------------------------------------- | | エージェントを構築またはリファクタリングしていて高速フィードバックが欲しい | 安定したテストエンドポイントで AAO の [(Spec) ハートビート](/docs/building/verification/aao-verified) の準備ができた | | 開発エージェントが `localhost`、Codespace、またはファイアウォールの背後で動く | あなたのプラットフォームはどのみち公開テストエンドポイントを公開する | | Addie にチャットで複数のストーリーボードをインタラクティブに実行させたい | 無人の予定されたコンプライアンス実行が欲しい | | サンドボックスエンドポイントを公開するインフラを持たない小さなチーム | 規模で運用しバッチ CI を好む | Socket Mode は AAO Verified の代替では **ありません**。エージェントが安定したら、実テストエンドポイントを公開し AAO ハートビートを継続的に実行させてください — それが公開の **AAO Verified (Spec)** バッジを獲得するものです。Socket Mode はそこに到達する前(そして到達後、変更を反復するとき)の開発ループチャネルです。 **設計上、開発/ステージング専用。** Socket Mode は非本番デプロイにゲートされます。[adcp#3986](https://github.com/adcontextprotocol/adcp/issues/3986) に従い `comply_test_controller` と同じ制約です。本番エージェントはこのチャネルを公開せず、AAO は Socket Mode 経由で本番デプロイを決して登録しません。 ## 必要なもの 1. **活発に開発している AdCP MCP サーバー。** 不完全でもよい — それがポイントです。最もシンプルなケースは、MCP SDK がインストールされたラップトップ上で動く JS/TS プロセスです。 2. **AAO アカウント。** コンプライアンスチャネルはあなたの WorkOS 組織にバインドされるため、メンバーまたはトライアル組織にサインインしている必要があります。Addie との匿名チャットは Socket Mode を使えません。 3. 開発プロジェクトの **`@adcp/sdk` ≥ 6.9**。`ConformanceClient` プリミティブは `@adcp/sdk/server` から出荷されます。 4. **WebSockets(ポート 443)経由での `addie.agenticadvertising.org` へのネットワーク egress。** インバウンドルールは不要。 それだけです。公開 DNS なし、ファイアウォール変更なし、ngrok なし、証明書プロビジョニングなし。 ## 5 分のセットアップ ### ステップ 1 — Addie にトークンを頼む Addie チャットセッションで、尋ねます: > 新しいコンフォーマンストークンをください Addie はシェル export とコピーペースト統合スニペットを返します: ``` **Conformance token issued.** Bound to your organization, expires in 1h. Paste these into your dev environment and start the conformance client: export ADCP_CONFORMANCE_URL=wss://addie.agenticadvertising.org/conformance/connect export ADCP_CONFORMANCE_TOKEN=eyJ… Three-line integration with @adcp/sdk ≥ 6.9: … ``` トークンは 1 時間で期限切れになります。あなたのものが切れたら、Addie に新しいものを頼むだけです — 設計上リフレッシュエンドポイントはありません。 ### ステップ 2 — `ConformanceClient` を開発サーバーに配線する 既存の AdCP サーバーブートストラップに 3 行追加: ```ts theme={null} import { ConformanceClient } from '@adcp/sdk/server'; import { mcpServer } from './my-mcp-server'; const conformance = new ConformanceClient({ url: process.env.ADCP_CONFORMANCE_URL!, token: process.env.ADCP_CONFORMANCE_TOKEN!, server: mcpServer, }); await conformance.start(); ``` `mcpServer` は通常のトラフィックのため `StreamableHTTPServerTransport` に接続するのと同じ `Server` インスタンスです — 別のセットアップなし、並列サーバーなし。`ConformanceClient` はそれをアウトバウンド WebSocket 上で双方向に公開します。Addie は反対側で通常の MCP サーバーを見ます。 まだ AdCP サーバーがない場合、[`hello_seller_adapter_social` の例](https://github.com/adcontextprotocol/adcp-client/blob/main/examples/hello_seller_adapter_social.ts) をフォークしてください — SDK の `createAdcpServerFromPlatform` ヘルパーを使った作業済みの出発点です。 ### ステップ 3 — 接続を確認する トークンと URL を export した状態で開発サーバーを実行します。ステータスラインが見えるはずです: ``` [conformance] status=connecting [conformance] status=connected ``` `status=connected` が着地すると、Addie はあなたの開発サーバーを指すライブ MCP クライアントを持ちます。セッションは、プロセスを停止するかトークンが期限切れになるまで開いたままです。 ### ステップ 4 — チャットからストーリーボードを実行する Addie チャットに戻って: > 私のエージェントに対して `media_buy_state_machine` を実行して Addie は開いたソケットを通じてストーリーボードをディスパッチし、結果をチャットで markdown レポートとしてレンダリングします: ``` ### Conformance result — Media buy state machine lifecycle (media_buy_state_machine) **Overall:** ✅ PASSED **Steps passed/failed/skipped:** 8 / 0 / 1 **Duration:** 1240 ms #### ✓ Capability discovery - ✓ passed — Check agent capabilities #### ✓ Create a media buy - ✓ passed — Discover products for media buy - ✓ passed — Create the test media buy #### ✓ Valid state transitions - ✓ passed — Pause the media buy - ✓ passed — Resume the media buy - ✓ passed — Cancel the media buy … ``` 失敗するステップにはトリミングされたエラーテキストが含まれるため、チャットを離れずにその場で修正し再実行できます。グリーンになるまで反復してください。 [コンプライアンスカタログ](/docs/building/verification/compliance-catalog) の任意のストーリーボードをこの方法で実行できます — セールス、クリエイティブ、シグナル、ガバナンス、署名付きリクエストなど。分からない場合は Addie に何が利用可能か尋ねてください: *「セールスエージェントにどのコンフォーマンスストーリーボードが適用されますか?」* ## 接続後に Addie ができること ストーリーボード実行を超えて、ライブ MCP チャネルは Addie に次を可能にします: * **失敗するステップをインタラクティブに診断** — ステップが失敗したとき「なぜ?」と尋ねると、Addie は根本原因を絞り込むため異なる入力で同じツールを再呼び出しできる * **ケイパビリティ宣言を検証** — *「私の `get_adcp_capabilities` は実際に実装するものを主張していますか?」* * **ライフサイクル状態を歩く** — `comply_test_controller` を配線していれば、Addie は決定的状態遷移を駆動し結果を観測できる * **実ワイヤー出力に対して修正を提案** — *「この拒否で `error_code` フィールドが欠けています — 修正はこれです」* — 一般的な助言ではなく、彼女がちょうど見たバイトに基づく ## プライバシーと安全性 Socket Mode チャネルは表面を狭く保つよう構築されています: * **開発/ステージング専用。** 本番デプロイはこのチャネルを公開してはならない — `comply_test_controller` と同じデプロイスコープルール([adcp#3986](https://github.com/adcontextprotocol/adcp/issues/3986))。 * **あなたからのアウトバウンド。** あなたの開発ボックスが Addie への接続を開きます。Addie にはあなたのネットワークに手を伸ばす方法がありません。 * **セッションスコープ。** あなたがクライアントを起動する。プロセスを停止するまで実行される。永続的トンネルなし、デーモンなし。 * **組織スコープ。** トークンの WorkOS 組織クレームが唯一のテナント境界です。他の組織はチャネル上であなたのエージェントに到達できません。 * **いつでも切断。** クライアントプロセスをキルするとソケットが閉じます。あなたの組織の Addie のセッションは即座に退避されます。 * **Addie が見るものはあなたの Addie コンテキストに留まる。** チャットで彼女に伝える他のものと同じデータ処理姿勢。 チャネルを自分で検査したい場合、ワイヤー形式は `wss://` 上のプレーンな JSON-RPC 2.0 フレーム(MCP が既に使うのと同じ形状)です。トークンで URL に対して `wscat` を実行すると、Addie が見るものを正確に見られます。 ## トラブルシューティング ### Addie が「組織にマップされていません」と言う Addie と匿名でチャットしているか、あなたのアカウントがまだ WorkOS 組織にバインドされていません。メンバーまたはトライアル組織にサインインして再試行してください。 ### 接続時に `status=error`。サーバーが `401 Unauthorized` をログ トークンが期限切れ(1h TTL)か誤ったトークン。Addie に新しいものを頼んでください。新しいトークンも 401 する場合、あなたの AAO メンバーシップがコンフォーマンスエンタイトルメントを有効にしていないかもしれません — 組織のプランを確認してください。 ### ストーリーボードレポートがステップ 1 を `unknown tool get_adcp_capabilities` で失敗と表示 あなたの開発 MCP サーバーはまだ `get_adcp_capabilities` を実装していません。それはすべての AdCP エージェントが公開しなければならないディスカバリーツールです。任意のストーリーボードを実行する前にそれを実装してください — レスポンス形状については [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) を参照。 ### Addie が「あなたの組織にライブなコンフォーマンス接続がありません」と言う ソケットが開いていません。まだクライアントを起動していないか、切断されました。`ConformanceClient` を再起動し、Addie に何かを実行するよう頼む前に `status=connected` を確認してください。 ### ソケットは接続するがすべてのストーリーボードステップがスキップ あなたの `get_adcp_capabilities` レスポンスが、実装していない専門分野を宣言しています。ランナーは宣言された表面に一致しないステップをスキップします。ツールを実装するか宣言をトリムしてください。 ### Addie がチャネル上で何をしているかどう分かるか `onStatus` コールバックがすべての状態遷移(`connecting`、`connected`、`disconnected`、`error`)を公開します。開発ログにパイプしてください: ```ts theme={null} new ConformanceClient({ url, token, server: mcpServer, onStatus: (status, detail) => { console.log(`[conformance] status=${status}`, detail?.attempt ? `attempt=${detail.attempt}` : '', detail?.error ? `error=${detail.error.message}` : ''); }, }); ``` ツールレベルの可視性には、`setRequestHandler` コールバック内でログしてください — Addie の呼び出しは通常の MCP トラフィックとまったく同様にそこに着地します。 ## リファレンス * [`@adcp/sdk/server` `ConformanceClient`](https://github.com/adcontextprotocol/adcp-client/blob/main/src/lib/server/socket-mode/conformance-client.ts) — 採用者側プリミティブ * [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) — すべてのストーリーボードが始まるディスカバリーツール * [コンプライアンスカタログ](/docs/building/verification/compliance-catalog) — 利用可能なストーリーボードの完全なリスト * [Get Test-Ready](/docs/building/verification/get-test-ready) — 任意のストーリーボードが通過する前にエージェントに必要なもの * [AAO Verified](/docs/building/verification/aao-verified) — エージェントが安定したら卒業する公開信頼マーク * チャネル設計: [adcp#3991](https://github.com/adcontextprotocol/adcp/issues/3991) * デプロイスコープコントローラールール: [adcp#3986](https://github.com/adcontextprotocol/adcp/issues/3986) # コンプライアンスカタログ Source: https://adcp-docs-ja.pier1.co.jp/docs/building/verification/compliance-catalog エージェントが主張できる AdCP プロトコルと専門分野の完全なインデックス — それぞれが何を意味するか、どのコンプライアンスストーリーボードが実行されるか、ソース YAML をどこで見つけるか。 すべての AdCP エージェントは `get_adcp_capabilities` で `supported_protocols` と `specialisms` を宣言します。各宣言は、クレームを検証するためにストーリーボードランナーが実行する `/compliance/{version}/` のコンプライアンスバンドルにマップされます。 **`supported_protocols` は網羅的ではありません。** `accounts` 表面(`sync_accounts`、`list_accounts`、`sync_governance`)は、すべての `media_buy`、`creative`、`signals` エージェントに暗黙の基盤であり、意図的に `supported_protocols` 値ではありません。完全なアカウント表面については [Accounts tasks](/docs/accounts/tasks/sync_accounts) を参照してください。 このページはその分類の人間可読なインデックスです。機械可読な同等物は `/compliance/{version}/index.json` です。 ## Universal ストーリーボード すべてのエージェントは、どのプロトコルや専門分野を主張するかにかかわらず `/compliance/{version}/universal/` のすべてのストーリーボードを実行します。いくつかは *ケイパビリティゲート* — 該当ケイパビリティをアドバタイズするときのみ実行される — ですが、ストーリーボードはスコープにおいて依然として universal です: そのケイパビリティを主張する任意のエージェントがそれによってグレードされます。universal ストーリーボードを失敗すると全体のコンプライアンスが失敗します。 | Storyboard | 目的 | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `capability-discovery` | `get_adcp_capabilities` 形状、protocol/specialism 宣言、バージョンアドバタイズ | | `comply-controller-mode-gate` | 共有エンドポイント上で `comply_test_controller` を公開するセラーのサンドボックス/ライブ分離 — ライブモードのプリンシパルはシナリオディスパッチの前に拒否されなければならない | | `schema-validation` | リクエストとレスポンスのスキーマ適合性、ISO 8601 タイムスタンプ、時間的不変条件 | | `schema-validation-signals` | シグナルのレスポンススキーマ適合性 — すべてのシグナルの必須フィールド。`get_signals` にゲートされる | | `version-negotiation` | リリース精度の `adcp.supported_versions` アドバタイズとレスポンスエンベロープの `adcp_version` エコー。3.1 では助言的、後のカットで昇格 | | `v3-envelope-integrity` | v3 プロトコルエンベロープは v2 レガシー `task_status` または `response_status` フィールドを持ってはならない(MUST NOT) — v3 では `status` が唯一の正準ライフサイクルフィールド。 | | `error-compliance` | 構造化エラー形状、公開エラーコード、トランスポートバインディング、テナント間の存在リークなし | | `error-compliance-signals` | シグナルプロトコルのエラー処理 — 存在しないシグナル ID、欠けているフィールド、VERSION\_UNSUPPORTED、トランスポートバインディング。ディスカバリーフェーズは `get_signals` にゲートされる。アクティベーションフェーズは `signal-marketplace` のようなアクティベーションサポートを主張するエージェントのみ実行 | | `stale-response-advisory` | `STALE_RESPONSE` ワイヤー配置 — 助言は、トランスポート成功を保持した投入済み成功レスポンス上の `errors[]` に乗る。stale-cache 強制ステップは `force_upstream_unavailable` を伴う `comply_test_controller` にゲートされる | | `idempotency` | `idempotency_key` スコーピング、リプレイセマンティクス、`IDEMPOTENCY_CONFLICT`、`replayed: true`、宣言された TTL | | `read-tool-idempotency` | 読み取り専用タスクラッパーは、厳格なリクエストラッパー拒否なしに 3.1 の全リクエスト `idempotency_key` エンベロープを受け入れる。3.1 の省略キー猶予プローブを含む。 | | `canonical-format-validate-input` | 正準形式 `validate_input` 結果セマンティクス — 必須スロット全体の構造的 pass/fail と、シードされた製品宣言の `unvalidatable_nondeterministic`。`validate_input` をアドバタイズするエージェントにゲートされる。シード製品分岐は `comply_test_controller` シードサポートも要求。 | | `security` | **認証ベースライン — 未認証拒否、静的認証情報強制(Bearer API キーまたは HTTP Basic)、OAuth ディスカバリー + RFC 9728 オーディエンスバインディング。** [Authentication](/docs/building/by-layer/L2/authentication) を参照。 | | `webhook-emission` | アウトバウンド webhook 適合性 — リトライ間で安定した `idempotency_key`、RFC 9421 webhook 署名(またはバイヤーがオプトインした場合 HMAC フォールバック)。任意の操作で `push_notification_config` を受け入れる任意のエージェントで実行。 | | `webhook-receiver-envelope` | バイヤーレシーバーのリプレイ適合性: 完全な MCP webhook エンベロープを受け入れ、素の配信結果ペイロードを拒否し、生ボディバイト上で署名を検証し、`idempotency_key` でリトライを重複排除。 | | `notification-config-event-scope` | `sync_accounts.accounts[].notification_configs[]` セマンティック検証 — アカウントレベルのサブスクライバーは、共有通知タイプ enum で有効な値であってもメディアバイアンカーの通知タイプ(`scheduled`、`final`、`delayed`、`adjusted`、`impairment`)を拒否。 | | `notification-config-lifecycle` | `sync_accounts` 上のアカウントレベル `notification_configs[]` ライフサイクル: 一時停止された登録、耐久性のある `list_accounts` エコー、サブスクライバーキーの置換、clear-all セマンティクス。 | | `notification-config-rejections` | 重複する `subscriber_id` 値のためのセマンティック `notification_configs[]` リクエスト拒否パス。 | | `wholesale-feed-products` | 製品ホールセールフィードバージョニング — ブートストラップレスポンスは `wholesale_feed_version`/`cache_scope` を持ち、一致する `if_wholesale_feed_version` プローブは製品行なしで `unchanged` を返す。 | | `wholesale-feed-signals` | シグナルホールセールフィードバージョニング — ブートストラップレスポンスは `wholesale_feed_version`/`cache_scope` を持ち、一致する `if_wholesale_feed_version` プローブはシグナル行なしで `unchanged` を返す。 | | `wholesale-feed-product-webhooks` | 製品ホールセールフィード webhook イベントをアドバタイズするエージェントのアカウントレベル `notification_configs[]` 登録。 | | `wholesale-feed-signal-webhooks` | シグナルホールセールフィード webhook イベントをアドバタイズするエージェントのアカウントレベル `notification_configs[]` 登録。 | | `wholesale-feed-bulk-webhooks` | `wholesale_feed.bulk_change` をアドバタイズするエージェントのアカウントレベル `notification_configs[]` 登録。 | | `pagination-integrity` | ページ化された `list_creatives` レスポンスを継続ページから終端まで歩くことで検証される `cursor` ↔ `has_more` 不変条件。 | | `get-products-pagination-integrity` | `get_products` ホールセールページネーションセマンティクス — シードされた製品フィードを継続から終端まで歩き、brief/refine をフィード列挙ではなく上限付きキュレート/リファイン回答として保持。 | | `get-signals-pagination-integrity` | 広範なクエリの下でページ化された `get_signals` レスポンスを歩くことで検証される `cursor` ↔ `has_more` 不変条件 — ページ 1 は任意の非自明なシグナルセットに対して非終端でなければならず、ページ 2 はカーソルをたどる。 | | `pagination-integrity-list-accounts` | ページ化された `list_accounts` レスポンスを歩くことで検証される `cursor` ↔ `has_more` 不変条件 — ストーリーボードは `sync_accounts` 経由で 3 つのアカウントをブートストラップし、ページ 1 が非終端でページ 2 が古いカーソルなしで終端であることをアサート。 | | `pagination-integrity-creative-formats` | ページ化された `list_creative_formats` レスポンスを歩くことで検証される `cursor` ↔ `has_more` 不変条件 — ストーリーボードは `seed_creative_format` 経由で 2 つのクリエイティブフォーマットをシードし、ページ 1 が非終端でページ 2 が古いカーソルなしで終端であることをアサート。 | | `get-media-buys-pagination-integrity` | ページ化された `get_media_buys` レスポンスを歩くことで検証される `cursor` ↔ `has_more` 不変条件 — ストーリーボードは `seed_media_buy` 経由で 3 つのメディアバイをシードし、ページ 1 が非終端でページ 2 が古いカーソルなしで終端であることをアサート。 | | `content-standards-pagination-integrity` | ページ化された `list_content_standards` レスポンスを歩くことで検証される `cursor` ↔ `has_more` 不変条件 — ストーリーボードは `create_content_standards` 経由で 3 つのコンテンツ標準構成をブートストラップし、ページ 1 が非終端でページ 2 が古いカーソルなしで終端であることをアサート。 | | `collection-lists-pagination-integrity` | ページ化された `list_collection_lists` レスポンスを歩くことで検証される `cursor` ↔ `has_more` 不変条件 — ストーリーボードは `create_collection_list` 経由で 3 つのコレクションリストをブートストラップし、ページ 1 が非終端でページ 2 が古いカーソルなしで終端であることをアサート。 | | `property-lists-pagination-integrity` | ページ化された `list_property_lists` レスポンスを歩くことで検証される `cursor` ↔ `has_more` 不変条件 — ストーリーボードは `create_property_list` 経由で 3 つのプロパティリストをブートストラップし、ページ 1 が非終端でページ 2 が古いカーソルなしで終端であることをアサート。 | | `deterministic-testing` | `comply_test_controller` ステートマシン検証 — `capabilities.compliance_testing.supported: false` ならスキップ。 | | `signed-requests` | RFC 9421 トランスポート層リクエスト署名検証 — `request_signing.supported: false` ならスキップ。 | | `billing-gate-dispatch` | `sync_accounts.billing` 拒否ディスパッチ — `BILLING_NOT_SUPPORTED`(ケイパビリティゲート、`error.details.scope` 付き)対 `BILLING_NOT_PERMITTED_FOR_AGENT`(バイヤーエージェントごとの商業関係ゲート、クランプされた `rejected_billing` + `suggested_billing` 形状付き)。セラーが 3 つの `billing` 値すべてをサポートするときケイパビリティフェーズはスキップ。テストキットが `commercial_relationship: passthrough_only` を宣言しないときエージェントごとのフェーズはスキップ。 | ケイパビリティゲートの行(`deterministic-testing`、`signed-requests`)は、エージェントがケイパビリティを `false` としてアドバタイズするときのみスキップされます。それらを主張して部分的に実装することはできません。`supported: true` を宣言してストーリーボードを失敗することは非適合です — 部分的な実装を出荷するより `false` を宣言してください。`billing-gate-dispatch` と `comply-controller-mode-gate` の行は、通常のケイパビリティゲート行ではなく前提条件ゲートです: 各フェーズは前提条件が満たされないとき `not_applicable` をグレードします。エージェントごとの課金ゲートの完全なカバレッジを望むセラーは、エージェントごとのフェーズが実行されるよう `commercial_relationship: passthrough_only` を宣言したテストキットを出荷すべきです(SHOULD)。 ## プロトコル トップレベルのエージェントケイパビリティクレーム。エージェントは `supported_protocols` にリストすることでプロトコルを主張し、プロトコルのベースラインストーリーボードとすべての [universal](/docs/building/verification/validate-your-agent#storyboard-taxonomy) ストーリーボードを通過しなければなりません。 `supported_protocols` は snake\_case を使います。コンプライアンスパスと専門分野 ID は kebab-case を使います。完全なマッピングについては下の [Naming conventions](#naming-conventions) を参照してください。 | `supported_protocols` value | Compliance path | 目的 | | --------------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------- | | `media_buy` | `protocols/media-buy/` | キャンペーン作成、パッケージ管理、配信最適化、コンバージョントラッキング | | `creative` | `protocols/creative/` | クリエイティブアセット管理、フォーマットディスカバリー、レンダリング | | `signals` | `protocols/signals/` | オーディエンスシグナルディスカバリーとアクティベーション | | `governance` | `protocols/governance/` | プロパティガバナンス、ブランド標準、コンプライアンス | | `brand` | `protocols/brand/` | ブランドアイデンティティ、権利ディスカバリー、権利取得 *— 今日は小さなプロトコルだが、権利ライセンシング作業とともに成長中。`brand-rights` 専門分野を参照。* | | `sponsored_intelligence` | `protocols/sponsored-intelligence/` | AI 仲介コマースと会話型スポンサードエクスペリエンス | | `measurement` | Preview / 安定ベースラインなし | 実験的 3.1 メトリックカタログディスカバリー。それを実装するエージェントは `experimental_features` に `measurement.core` をリストしなければならない。 | [コンプライアンステストコントローラー](/docs/building/by-layer/L3/comply-test-controller) のサポートは、`supported_protocols` ではなく `get_adcp_capabilities` の `capabilities.compliance_testing` ブロックで宣言されます。コンプライアンステストはテストハーネス用の RPC 表面であり、機能的プロトコルではありません。 エージェントは複数のプロトコルを主張できます — フルスタックのメディアバイプラットフォームは `media_buy`、`creative`、`signals` をリストするかもしれません。ランナーはすべての一致するベースラインを実行します。 ## 専門分野 具体的なケイパビリティクレーム。各専門分野はちょうど 1 つのプロトコルの下に存在します。専門分野を主張するエージェントは、親プロトコルのベースラインに加えて専門分野のストーリーボードを通過しなければなりません — 例えば `sales-guaranteed` を主張するには `supported_protocols` に `media_buy` が必要です。 専門分野は `status` を持ちます: * **`stable`** — 完全に仕様化されたストーリーボード。コンプライアンスランナーはすべてのフェーズを実行する。`AAO Verified` はエージェントが実証可能に通過したことを意味する。 * **`preview`** — ID とスコープは予約済み。基盤プロトコル表面が安定するまでストーリーボードはプレースホルダー。エージェントはこれらを主張してもよい。ランナーは検証済み pass/fail の代わりに `{ status: "preview", passed: null, reason: "storyboard not yet defined" }` の結果を発行する。AAO バッジは preview 専門分野を明確なインジケーターでレンダリングする。 * **`deprecated`** — 後方互換性のため保持されるが、将来のメジャーで削除予定。ランナーは `{ status: "deprecated", passed: , reason: "..." }` を発行する — 存在すれば依然としてストーリーボードを実行するが、クレームを移行すべきと警告する。 ステータスは YAML フロントマターで専門分野ごとに宣言され、`/compliance/{version}/index.json` に表示されます。 専門分野は下で親プロトコルごとにグループ化されています。 **3.0 での変更点。** `sponsored_intelligence` は専門分野からフルプロトコルに昇格しました(`specialisms` ではなく `supported_protocols` で宣言)。`audience-sync` はそのツールファミリーに合わせて `governance` から `media-buy` に移動しました。`broadcast-platform` は `sales-broadcast-tv` に、`social-platform` は `sales-social` にリネームされました。`property-governance` と `collection-governance` は兄弟の `property-lists` と `collection-lists` 専門分野に分割されました。 ### media-buy | Specialism | Status | 目的 | | ------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sales-guaranteed` | stable | 人間 IO 承認を伴う保証メディアバイ | | `sales-non-guaranteed` | stable | 非保証オークションベースメディアバイ | | `sales-proposal-mode` | deprecated | **3.1 で非推奨。** このクレームを落とし `sales-guaranteed` + `media_buy.supports_proposals: true` で置き換えてください。[#3823](https://github.com/adcontextprotocol/adcp/issues/3823) を参照。 | | `sales-catalog-driven` | stable | コンバージョントラッキングを伴うカタログ駆動コマース | | `sales-broadcast-tv` | stable | 保証在庫と FCC キャンセルルールを伴う放送リニア TV | | `sales-social` | stable | セルフサービスフローを伴うソーシャルメディア広告プラットフォーム | | `governance-aware-seller` | stable | ベースライン登録後にバイヤーのキャンペーンガバナンスエージェントと合成するセラー — `check_governance` を呼び、承認、条件、拒否を変えずに伝播する。完全なガバナンスチェックループのオプションクレーム。 | | `audience-sync` | stable | バイヤー提供のオーディエンスセグメントをアクティベーションのためプラットフォームに同期(`sync_audiences`、`list_accounts` を使う) | **3.1 で登場。** `sales-streaming-tv`(CTV / ストリーミング)、`sales-exchange`(プログラマティック SSP / エクスチェンジ)、`sales-retail-media`(リテールメディアネットワーク)は 3.1 に予定されています。それらのカテゴリーのセラーは 3.0 GA では `sales-guaranteed` または `sales-non-guaranteed` を主張すべきです。 `audience-sync` はそのツールファミリーに合わせて `governance` プロトコルから `media-buy` に移動しました。エージェントが `audience-sync` を主張するが `supported_protocols` に `governance` のみ宣言する場合、`supported_protocols` に `media_buy` を追加してください — ランナーは今や audience-sync ストーリーボードと並んでメディアバイベースラインが実行されることを期待します。 ### creative | Specialism | Status | 目的 | | --------------------- | ------ | ------------------------------- | | `creative-ad-server` | stable | タグベース配信を伴うクリエイティブ広告サーバー | | `creative-generative` | stable | オンデマンドでアセットを生成する生成クリエイティブエージェント | | `creative-template` | stable | クリエイティブテンプレートと変換エージェント | ### signals | Specialism | Status | 目的 | | -------------------- | ------ | ------------------------------------------------------------------------ | | `signal-owned` | stable | `get_signals` を通じてファーストパーティセグメントを公開する所有シグナルエージェント。このクレームにアクティベーションは不要 | | `signal-marketplace` | stable | サードパーティデータを再販するマーケットプレイスシグナルエージェント。`get_signals` と `activate_signal` が必要 | ### governance | Specialism | Status | 目的 | | ----------------------------- | ------ | ----------------------------------------------------------------------------------- | | `content-standards` | stable | コンテンツ標準強制(ブランドセーフティ、ポリシーコンプライアンス) | | `property-lists` | stable | プロパティリストガバナンス — ターゲティングと配信コンプライアンスのためのキュレートされた包含/除外リスト | | `collection-lists` | stable | コレクションリストガバナンス — プログラムレベルのブランドセーフティのためのコンテンツプログラム(番組、シリーズ、ポッドキャスト)のキュレートされた包含/除外リスト | | `governance-delivery-monitor` | stable | ドリフト検出を伴うキャンペーン配信監視 | | `governance-spend-authority` | stable | 条件付き支出承認と human-in-the-loop ガバナンス | **3.1 で実験的。** メジャメントメトリックカタログディスカバリーは実験的 `measurement` ケイパビリティブロックと `measurement.core` 実験的機能を通じて利用可能です。安定した `measurement-verification` 専門分野とベースラインストーリーボードは、メジャメントタスク表面が凍結されるまで延期されます。 ### brand | Specialism | Status | 目的 | | -------------- | ------ | ---------------------------------------- | | `brand-rights` | stable | ブランドアイデンティティと権利ライセンシング(タレント、音楽、ストックメディア) | ## Choosing a sales specialism `sales-*` 専門分野は相互排他的ではありません — 保証ダイレクトデスクとオークションフロアの両方を持つハイブリッドプラットフォームは `sales-guaranteed` と `sales-non-guaranteed` の両方を主張すべきです。クレームを解決するには下のステップに従ってください。 **`sales-proposal-mode` は 3.1 で非推奨です。** 新しいエージェントでそれを主張しないでください。それを宣言する既存のエージェントはそれを完全に落とし、`get_adcp_capabilities` で `sales-guaranteed` + `media_buy.supports_proposals: true` で置き換えなければなりません。[#3823](https://github.com/adcontextprotocol/adcp/issues/3823) を参照。 3 つの専門分野は特定の配信チャネルに適用され、独自のストーリーボードを持ちます。これらのチャネルタイプの 1 つだけを販売する場合、一致する専門分野のみを主張してください。これらのチャネル外の一般的なディスプレイやビデオ在庫も販売する場合、ステップ 2 に進んでください。 | 運用するもの… | Claim | | ----------------------------------------------------- | ---------------------- | | FCC キャンセルルールを伴う放送リニア TV | `sales-broadcast-tv` | | カタログ駆動ダイナミック広告(製品リスティング、レストランメニュー、ホテルリスティング、ローカルコマース) | `sales-catalog-driven` | | プラットフォーム管理クリエイティブを伴うソーシャルプラットフォーム | `sales-social` | | 販売するもの… | Claim | | ------------------ | ------------------------------------------- | | 保証メディア(IO 承認、固定価格) | `sales-guaranteed` → ステップ 3 を参照 | | オークション / PMP 非保証 | `sales-non-guaranteed` | | 保証と非保証の両方 | `sales-guaranteed` + `sales-non-guaranteed` | `media_buy.supports_proposals` は `get_adcp_capabilities` レスポンスの `media_buy` ケイパビリティブロックのブール値です。`proposal_finalize` コンプライアンスシナリオが実行されるかをゲートします。これは適合性宣言であり、バイヤーのプロポーザルごとのルーティングシグナルではありません: バイヤーは `proposal_status` から返されたプロポーザルが購入できるかを決定します。 | もし… | Set | | --------------------------------------------------------- | ------------------------------------------------------------- | | RFP を受け入れ、プロポーザルを生成し、作成前にドラフトプロポーザルをコミット済みステータスにファイナライズする | `media_buy.supports_proposals: true` | | ダイレクトバイ保証のみを販売(オークション PG、リテール SKU、見積レート — RFP フローなし) | `media_buy.supports_proposals: false`(または省略 — デフォルトは `false`) | ```jsonc theme={null} // フルサービス保証セラー — プロポーザルライフサイクルがグレードされる { "supported_protocols": ["media_buy"], "specialisms": ["sales-guaranteed"], "media_buy": { "supports_proposals": true } } ``` ```jsonc theme={null} // ダイレクトバイ保証セラー — プロポーザルシナリオは capability_unsupported としてスキップ { "supported_protocols": ["media_buy"], "specialisms": ["sales-guaranteed"], "media_buy": { "supports_proposals": false } } ``` ### creative | Specialism | Status | 目的 | | --------------------- | ------ | ------------------------------- | | `creative-ad-server` | stable | タグベース配信を伴うクリエイティブ広告サーバー | | `creative-generative` | stable | オンデマンドでアセットを生成する生成クリエイティブエージェント | | `creative-template` | stable | クリエイティブテンプレートと変換エージェント | ### signals | Specialism | Status | 目的 | | -------------------- | ------ | ------------------------------------------------------------------------ | | `signal-owned` | stable | `get_signals` を通じてファーストパーティセグメントを公開する所有シグナルエージェント。このクレームにアクティベーションは不要 | | `signal-marketplace` | stable | サードパーティデータを再販するマーケットプレイスシグナルエージェント。`get_signals` と `activate_signal` が必要 | ### governance | Specialism | Status | 目的 | | ----------------------------- | ------ | ----------------------------------------------------------------------------------- | | `content-standards` | stable | コンテンツ標準強制(ブランドセーフティ、ポリシーコンプライアンス) | | `property-lists` | stable | プロパティリストガバナンス — ターゲティングと配信コンプライアンスのためのキュレートされた包含/除外リスト | | `collection-lists` | stable | コレクションリストガバナンス — プログラムレベルのブランドセーフティのためのコンテンツプログラム(番組、シリーズ、ポッドキャスト)のキュレートされた包含/除外リスト | | `governance-delivery-monitor` | stable | ドリフト検出を伴うキャンペーン配信監視 | | `governance-spend-authority` | stable | 条件付き支出承認と human-in-the-loop ガバナンス | **3.1 で実験的。** メジャメントメトリックカタログディスカバリーは実験的 `measurement` ケイパビリティブロックと `measurement.core` 実験的機能を通じて利用可能です。安定した `measurement-verification` 専門分野とベースラインストーリーボードは、メジャメントタスク表面が凍結されるまで延期されます。 ### brand | Specialism | Status | 目的 | | -------------- | ------ | ---------------------------------------- | | `brand-rights` | stable | ブランドアイデンティティと権利ライセンシング(タレント、音楽、ストックメディア) | ### sponsored-intelligence | Specialism | Status | 目的 | | ------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sponsored-intelligence` | preview | 専門分野 ID でディスパッチする SDK 用のエージェントクレーム。グレードされるストーリーボードは `sponsored-intelligence` プロトコルベースライン。この専門分野はワイヤー ID を予約し、SI ツールが `x-status: experimental` から卒業したとき `stable` に昇格する。 | ## クロスリソース不変条件 ステップごとの検証に加えて、専門分野はランナーが完全なストーリーボード実行全体で観測するクロスステップとクロスリソースの **不変条件** を宣言します。これらは単一のレスポンス形状では表面化しない状態不整合を捕捉します。 | Invariant | Scope | Specialisms | | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `status.monotonic` | 単一リソース — 仕様ライフサイクルグラフ上にないステップ間で観測されたステータス遷移を拒否。 | ステートフルなリソースライフサイクルを持つすべての専門分野。 | | `impairment.coherence` | クロスリソース — `media_buy.impairments[]` が参照されるリソースと同期を保つことを検証。**前方**: すべてのエントリーが現在オフラインのリソースを参照する。**逆**: 非終端バイが参照する任意のオフラインリソースが `impairments[]` に現れる。**Health-iff**: 非終端バイ上で、`impairments[]` が非空のとき、かつそのときに限り `health == "impaired"`(厳格な iff — 古いドリフトは失敗)。スコープ外: 3 つのルールすべてが終端ステータスバイで緩和される(セラーは終端遷移で保持されていた状態のまま `impairments[]` と `health` を残してもよい(MAY))。マテリアリティは `package_ids: minItems: 1` 経由でスキーマ強制される。 | `audience-sync`、`creative-ad-server`、`creative-template`、`creative-generative`、`sales-catalog-driven`。`media_buy_seller/dependency_impairment` シナリオ(`force_creative_status` 経由の creative-track)で駆動される。audience-track と catalog-track は、コンプライアンステストコントローラーが `force_audience_status` / `force_catalog_item_status` を追加すると続く。リソース遷移とメディアバイスナップショット読み取りの両方を観測しないストーリーボードでは `not_applicable` をグレード。 | 不変条件は専門分野 YAML の `invariants:` 配列で宣言され、それらが強制するルールとともにインラインで文書化されます。完全な `impairment.coherence` コントラクトについては [media-buy lifecycle § Compliance](/docs/media-buy/media-buys/lifecycle#compliance) を参照してください。 ## 主張する方法 `get_adcp_capabilities` でプロトコルと専門分野を宣言します: ```json theme={null} { "supported_protocols": ["media_buy", "creative"], "specialisms": ["sales-guaranteed", "creative-template"] } ``` ストーリーボードランナーは: 1. `/compliance/{version}/universal/` のすべてのストーリーボードを実行 2. `supported_protocols` の各プロトコルについて、`/compliance/{version}/protocols/{protocol}/` のベースラインを実行(snake\_case → kebab-case) 3. 各主張された専門分野のストーリーボードを `/compliance/{version}/specialisms/{id}/` で実行 4. `preview` 専門分野については、pass/fail 判定の代わりに警告を発行 — AAO Verified バッジは preview 専門分野を明確なインジケーターでレンダリング **ツールを実装し、かつ専門分野を主張してください。** 専門分野の必須ツールをすべて配線するが `capabilities.specialisms[]` から kebab-case ID を省略するエージェントは、ランナーによって **"No applicable tracks found"** としてグレードされます — `tracks_passed = 0, tracks_failed = 0, tracks_skipped = 1`。これはステップレベルでの黙った通過であり、トラックレベルでの黙った失敗です。修正は `get_adcp_capabilities` レスポンスに専門分野 ID(例: `"creative-generative"`)を追加することです。 任意の `stable` ストーリーボードが失敗すると、あなたのエージェントはそのクレームに対して非適合です。スイートをローカルで実行する方法については [エージェントを検証する](/docs/building/verification/validate-your-agent) を参照してください。ランナーが専門分野マニフェストをどうグレードされるシナリオに解決するか — `media_buy.supports_proposals` のようなケイパビリティフラグがどう個々のシナリオをゲートするかを含む — の詳細なウォークスルーについては [グレーディングの仕組み](/docs/building/verification/how-grading-works) を参照してください。 ## Naming conventions 分類には 4 つのケーシングが共存します。どれが適用されるかは、識別子がどこで読まれるかに依存します: | Casing | Layer | Example | 現れる場所 | | ------------ | ---------------------------------------------------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------- | | `snake_case` | ワイヤー enum(`supported_protocols`、`delivery_type`、チャネル ID、`signal_type`) | `media_buy`、`non_guaranteed`、`ctv`、`custom` | `get_adcp_capabilities` レスポンス、JSON ペイロード、生成されたスキーマ | | `kebab-case` | 専門分野 ID とコンプライアンス URL | `sales-broadcast-tv`、`property-lists`、`audience-sync` | `get_adcp_capabilities.specialisms`、`/compliance/.../specialisms/{id}/` パス | | `snake_case` | ストーリーボード `id:` と `category:` フィールド | `sales_broadcast_tv`、`audience_sync` | コンプライアンス YAML フロントマター、ランナー出力、テストレポート | | 散文 / ハイフン付き | タイトルとナラティブ | "Streaming TV"、"non-guaranteed" | カタログページ、ナラティブコピー | ワイヤー専門分野 ID とストーリーボードカテゴリー間の kebab↔snake スワップは機械的アイデンティティです — ハイフンがアンダースコアになるだけ。専門分野内のバリアントシナリオは `{category}/{variant}` パス形式を使います。 | Specialism ID (wire) | Channel / tool family | Storyboard category | Variant scenarios | | ---------------------------- | -------------------------------------- | ---------------------------- | ----------------------------------- | | `sales-broadcast-tv` | `channels: ['linear_tv']` | `sales_broadcast_tv` | — | | `sales-social` | `channels: ['social']` | `sales_social` | — | | `audience-sync` | `sync_audiences` tool | `audience_sync` | — | | `property-lists` | `property_list` tools | `property_lists` | — | | `collection-lists` | `collection_list` tools | `collection_lists` | — | | `governance-spend-authority` | `check_governance`, `sync_plans` | `governance_spend_authority` | `governance_spend_authority/denied` | | `creative-generative` | `build_creative` | `creative_generative` | `creative_generative/seller` | | `brand-rights` | `get_brand_identity`, `acquire_rights` | `brand_rights` | `brand_rights/governance_denied` | ケース分割は意図的です: `supported_protocols` は既に本番エージェントに出荷された既存の 3.0 フィールドである一方、専門分野 ID は新しく URL ファーストです(それぞれが `/compliance/.../specialisms/{id}/` 下のディレクトリ名)。ランナーはマッピングを透過的に処理します。 ### 専門分野 ↔ ツールファミリーマッピング エージェントが主張するプロトコルは、専門分野が使うツールファミリー名と常に一致するわけではありません: * `audience-sync` は `media-buy` プロトコルの下に存在します。なぜなら `sync_audiences` はメディアバイツールだからです。 * `property-lists`(専門分野 ID、kebab-case)は `property_list` ツールファミリー(`create_property_list`、`validate_property_delivery`)とストーリーボードカテゴリー `property_lists` にマップされます。 * `sales-broadcast-tv` は `channels: ['linear_tv']` を宣言します — "Broadcast TV" は散文名。`linear_tv` はワイヤー値です。 `/compliance/{version}/index.json` は各専門分野の `required_tools` を表示するため、エージェントは完全なストーリーボード YAML を読まずにツールファミリーを発見できます。 ### ワイヤー enum 対 散文 ワイヤー enum 値は常に `snake_case`(`non_guaranteed`、`pmax_platform`、`ctv`)です。散文は同じ概念をハイフンやスペースでレンダリングします("non-guaranteed auction inventory"、"Connected TV")。ペイロードを投入するときは常にワイヤー形式を使ってください — ハイフン付きまたはスペース付きの綴りは編集上のものだけで、スキーマ検証に失敗します。 ### `signal_type` 値 シグナルレスポンスの `signal_type` enum は 3 つの値を持ちます: * `marketplace` — シグナルエージェントはサードパーティデータプロバイダー(Experian、Peer39 など)が公開するセグメントを再販している。バイヤーはプロバイダーの `/.well-known/adagents.json` 経由で認可を検証できる。 * `owned` — シグナルエージェントは直接所有するデータ(リテーラー購入データ、パブリッシャー行動データ、通信位置データ)から派生した自身のファーストパーティセグメントを公開する。 * `custom` — シグナルソースはモデル、コンポジット、またはバイヤー提供の入力からオンデマンドでセグメントを構築する。`adagents.json` 認可チェーンが適用されないときこれを使う — セグメントはソースネイティブで、常設アップストリームプロバイダーに帰属しない。 ## 真実の源泉 機械インデックスはスキーマと並んで公開されます: | Path | Contents | | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | `/compliance/{version}/index.json` | 列挙されたプロトコル + 専門分野 + universal ストーリーボード + 専門分野ごとの `status` | | `/schemas/{version}/enums/specialism.json` | `get_adcp_capabilities.specialisms` が使う専門分野 enum | | `/schemas/{version}/enums/adcp-protocol.json` | `tasks-list-request` と webhook ペイロードが参照するタスク分類 enum。`supported_protocols` と同じ軸(ここでは kebab-case、ワイヤー上では snake\_case)。 | ビルドパイプラインは専門分野ファイルシステム ↔ enum パリティと、すべての専門分野の親プロトコルがコンプライアンスツリーに存在することを検証します。ドリフトはビルドを失敗させます。 このページのカタログは人間のコンテキストを与えるため手動で保守されています。権威的な列挙は常に `/compliance/{version}/index.json` です。 **アップストリームプラットフォームをラップするエージェントを構築していますか?** このカタログのストーリーボードは AdCP ワイヤーコントラクトをグレードします。アップストリームと統合せずに形状有効なレスポンスを返すアダプターは検出できません。補完的なプレステージングゲートについては **[モックアップストリームフィクスチャでアダプターエージェントを検証する](/docs/building/verification/validate-with-mock-fixtures)** を参照してください。 # 適合性仕様 Source: https://adcp-docs-ja.pier1.co.jp/docs/building/verification/conformance 「AdCP 適合」が何を意味するか、それを検証するストーリーボードで定義される。適合性は仕様が要求するもの、verified はスイートが証明するもの。 **ステータス**: Request for Comments **最終更新**: April 19, 2026 ## 3 つではなく 2 つの言葉 AdCP 適合性には 2 つの荷重を担う用語があります。3 つ目(実世界で耳にするもの)は罠です。 * **Conformant(適合)** — エージェントが規範的ルールを満たす。この文書がインデックスするストーリーボードで定義される。 * **Verified** — AAO がエージェントを最近テストし署名付き証明を発行した。アクティブなメンバーシップとライブなハートビートにゲートされる。[AAO Verified バッジ](/docs/building/verification/aao-verified) は 2 つの修飾子のいずれかを持ちます: テストデプロイまたは開発エンドポイントに対するストーリーボード適合性の **(Spec)**、`account.sandbox: true` フラグの下でセラーの実本番エンドポイントに対するストーリーボード適合性の **(Sandbox)**。エージェントはどちらか、または両方を獲得できます。 * **"Compliant"** — 自己証明、未検証、外部チェックなし。それを主張しない。それに向けて設計しない。この文書は *conformant* と *verified* を排他的に使います。 言い換えれば: * 適合性はエージェントのワイヤー動作の性質です。 * 検証は時間で区切られた第三者証明です。**(Spec)** は任意の登録エンドポイントに対するワイヤー形式適合性を証明します。**(Sandbox)** は同じストーリーボードスイートがサンドボックスフラグ付きトラフィックの下でセラーの実本番エンドポイントに対して通過することを証明します。同じストーリーボード、異なる証明表面。 * 2 つの軸は独立しています: 別のテストデプロイを持たないセラーは本番上で直接 **(Sandbox)** を獲得できます。実インプレッションを決してサーブできないテストエージェントは完全なクレームとして **(Spec)** を獲得します。 ## ストーリーボード適合性 対 AAO Verified このページは **ストーリーボード適合性** をインデックスします — シードされたテストデータに対して実行されるストーリーボードで検証される、エージェントのワイヤー動作が仕様に一致するときに持つ性質。ストーリーボードの通過は、ランナーがどこをターゲットしたかに応じて、エージェントのバッジ上で **AAO Verified (Spec)** または **AAO Verified (Sandbox)** の修飾子(または両方)を獲得します。 2 つ目の軸 — **AAO Verified (Sandbox)** — は、セラーの実本番エンドポイントが `account.sandbox: true` フラグの下で完全なストーリーボードスイートを正しく処理することを検証します。(Sandbox) はより強いクレームです: セラーはテストデプロイで (Spec) を通過しながら、本番スタックには壊れたサンドボックスゲート(フラグ付きトラフィックの下での実世界の副作用、欠けているアカウントモード検証など)があるかもしれません — (Sandbox) はそのギャップを閉じます。 2 つの修飾子は 1 つのブランドマーク — **AAO Verified** — を共有し、エージェントはどちらか、または両方を獲得できます。**(Spec) と (Sandbox) は独立しています**: それぞれが異なる証拠を通じて独立に適合性を実証します。(Spec) は任意の登録エンドポイントに対するワイヤー形式適合性を証明します。(Sandbox) は本番コードパスがサンドボックスフラグ付きトラフィックを正しく許容することを証明します。修飾子モデルと [Sandbox framing の判定](https://github.com/adcontextprotocol/adcp/issues/4379) については [AAO Verified](/docs/building/verification/aao-verified) を参照してください。このページの残りは両方の修飾子を裏付けるストーリーボードをインデックスします。 ## テスト表面とストーリーボードループ すべてのセラーは *テスト表面* を公開します — ストーリーボードランナーが実世界の副作用を引き起こすことなくセラーのツールを決定的に行使できるようにするメカニズム。テスト表面は (Spec) がグレードされる対象です。セラーがその表面をどう立ち上げるかは、状態の記録がどこに存在するかに依存します。実装は異なり、目標は異なりません: | 状態の記録がどこに存在するか | テストループがどう閉じるか | | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | ローカル DB のみ(典型的には SSP、クリエイティブエージェント) | ストーリーボードランナーは `comply_test_controller.seed_*` 経由でフィクスチャを書き込む。セラーの読み取りハンドラーは同じストアを消費する。seed → read ループが自然に閉じる。 | | セラーが制御しないアップストリームシステム(プラットフォームにプロキシする DSP、リテーラーカタログを読むリテールメディアネットワーク、シグナルブローカー) | シードされた書き込みは読み取りハンドラーにとって無効。TypeScript SDK は、まず実アダプター呼び出しを実行し(壊れたアップストリーム呼び出しは依然としてゲートを失敗させる)、次にシードされたフィクスチャをレスポンスにマージする `TestControllerBridge` を出荷する。 | | 混合(一部のツールはローカル、一部はアップストリーム) | ツールごとに両方。 | 両方のパスが `(Spec)` を獲得します — 両方ともセラーのワイヤー形式がストーリーボードに一致することを証明します。ブリッジはテスト表面パターンの **1 つの実装** であり、別のセラーカテゴリーではありません。配線されたシードのない状態ローカルセラーと、配線されたブリッジのないアップストリームプロキシセラーは同じ立場にあります: ストーリーボードはそれらに対してエンドツーエンドで実行できません。どちらのカテゴリーも `(Sandbox)` が証明するものではありません。`(Sandbox)` は、セラーの本番スタックが実世界の副作用なしに `account.sandbox: true` を尊重するかどうかをカバーする別の軸です。 ### フィクスチャマージ済みとアップストリーム由来のレスポンスを区別する レスポンスが SDK の `TestControllerBridge` を通過するとき、SDK はレスポンスに `_bridge: { callback, tool, merged_count }` マーカーをスタンプします。ステップ上のマーカーの存在は、レスポンス内容がセラーのハンドラーが返した後にシードされたフィクスチャからマージされたことを意味します。マーカーの不在は、レスポンスがセラーのアダプターからエンドツーエンドで来た(またはランナーが直接シードしたローカル DB から来た)ことを意味します。マーカーはランナーと下流リーダーボード用の助言的メタデータであり、ワイヤーコントラクトの一部では **ありません**。セラーはそれを発行してはならず(MUST NOT)、適合性チェックはそれを無視します。先頭のアンダースコアはフィールドをテストツール用に予約された SDK/ランナースタンプメタデータとしてマークします。同じプレフィックスを持つ将来のフィールドは同じルールに従います。 マーカー設計: [`adcp-client#1775`](https://github.com/adcontextprotocol/adcp-client/issues/1775)。出荷済み: [`adcp-client#1786`](https://github.com/adcontextprotocol/adcp-client/pull/1786)。マーカーを消費するリーダーボードポリシー: [`adcp-client#1782`](https://github.com/adcontextprotocol/adcp-client/issues/1782)。 ### 3 つのシグナル — 混同しないこと 採用者はしばしばこれら 3 つの制御を同じものとして読みます。それらは異なる質問に答えます: | Signal | 答える質問 | | --------------------------------------------------------- | -------------------------------------------------------- | | テストコントローラーの利用可能性(`tools/list` の `comply_test_controller`) | 「セラーは決定的モードの force を公開したか?」 | | サンドボックスフラグ(リクエスト上の `account.sandbox`) | 「ターゲットされたアカウントはサンドボックスアカウントで、実世界の副作用がないか?」 | | ブリッジ参加(レスポンス上の `_bridge` マーカー) | 「このレスポンスはアダプターのアップストリーム呼び出しから来たか、SDK がマージしたフィクスチャから来たか?」 | これらは個々のストーリーボードステップ上の **ランタイム制御** です — ストーリーボードの通過が時間をかけて何を *証明する* かを記述する `(Spec)` と `(Sandbox)` の検証修飾子とは別。ストーリーボードの通過は 3 つのシグナルの任意の組み合わせを持ちうる。 ## ストーリーボードが真実 すべての MUST を散文で再述する — それは避けがたく実行可能なスイートからドリフトする — のではなく、**ストーリーボードが適合性仕様そのものです。** この文書はそれらへのナビゲーショナルインデックスで、ストーリーボードの実行を義務付ける宣言でグループ化されています。 スイート内のすべての規範的ルールはちょうど 1 つの居場所を持ちます: [`/compliance/latest/`](https://adcontextprotocol.org/compliance/latest/) のストーリーボード YAML。「適合」が何を意味するかの変更はそこで、バージョン管理されたリリースで、実エージェントに対してテストされて起こります。ルールがストーリーボードにないなら、それは適合性の一部ではありません。 これは意図的です。ストーリーボードルールを再述する別の散文仕様は 2 つの真実の源泉を作ります。2 つの真実の源泉はドリフトします。私たちは 1 つを選びます: スイート。 `@adcp/sdk` パッケージは、ストーリーボード駆動の `comply()` に先行する `testing/scenarios/` 下の TypeScript ファイルも出荷します。それらは適合性仕様では **ありません** — どちらがどれかについては [Storyboards 対 scenarios](/docs/building/verification/storyboards-vs-scenarios) を参照してください。 以下で参照されるストーリーボードと散文セクションのキーワード「MUST」「MUST NOT」「REQUIRED」「SHALL」「SHALL NOT」「SHOULD」「SHOULD NOT」「RECOMMENDED」「MAY」「OPTIONAL」は [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) で説明される通りに解釈されます。 ## Conformance is layered すべてのエージェントは universal 層を満たします。各 `supported_protocols` クレームはプロトコルベースラインを追加します。各 `specialisms` クレームは専門分野ベースラインを追加します。 | Layer | Obligation | Path | | -------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------- | | **Universal** | すべての AdCP エージェント | [`/compliance/latest/universal/`](https://adcontextprotocol.org/compliance/latest/universal/) | | **Protocol** | `supported_protocols` 値を主張するエージェント | [`/compliance/latest/protocols/{protocol}/`](https://adcontextprotocol.org/compliance/latest/protocols/) | | **Specialism** | `specialisms` 値を主張するエージェント | [`/compliance/latest/specialisms/{id}/`](https://adcontextprotocol.org/compliance/latest/specialisms/) | エージェントは、そのストーリーボードを通過しないケイパビリティを宣言してはなりません(MUST NOT)。完全な分類については [コンプライアンスカタログ](/docs/building/verification/compliance-catalog) を、スイートをローカルで実行する方法については [エージェントを検証する](/docs/building/verification/validate-your-agent) を参照してください。 ## Universal 適合性 すべてのエージェントは以下のすべてのストーリーボードを通過しなければなりません(MUST)。 | Storyboard | 検証するもの | | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [`capability_discovery`](https://adcontextprotocol.org/compliance/latest/universal/capability-discovery) | `get_adcp_capabilities` 形状、protocol/specialism 宣言、バージョンアドバタイズ | | [`comply_controller_mode_gate`](https://adcontextprotocol.org/compliance/latest/universal/comply-controller-mode-gate) | 共有エンドポイント上で `comply_test_controller` を公開するセラーのサンドボックス/ライブ分離 — ライブモードのプリンシパルはシナリオディスパッチの前に拒否されなければならない | | [`schema_validation`](https://adcontextprotocol.org/compliance/latest/universal/schema-validation) | リクエストとレスポンスのスキーマ適合性、ISO 8601 タイムスタンプ、時間的不変条件 | | [`schema_validation_signals`](https://adcontextprotocol.org/compliance/latest/universal/schema-validation-signals) | シグナルのレスポンススキーマ適合性 — すべてのシグナルの必須フィールド。`get_signals` にゲートされる | | [`version_negotiation`](https://adcontextprotocol.org/compliance/latest/universal/version-negotiation) | リリース精度の `adcp.supported_versions` アドバタイズとレスポンスエンベロープの `adcp_version` エコー。3.1 では助言的、後のカットで昇格 | | [`v3_envelope_integrity`](https://adcontextprotocol.org/compliance/latest/universal/v3-envelope-integrity) | v3 プロトコルエンベロープは v2 レガシー `task_status` または `response_status` フィールドを持ってはならない(MUST NOT) — v3 では `status` が唯一の正準ライフサイクルフィールド | | [`error_compliance`](https://adcontextprotocol.org/compliance/latest/universal/error-compliance) | 構造化エラー形状、公開エラーコード、トランスポートバインディング、テナント間の存在リークなし | | [`error_compliance_signals`](https://adcontextprotocol.org/compliance/latest/universal/error-compliance-signals) | シグナルプロトコルのエラー処理 — 存在しないシグナル ID、欠けているフィールド、VERSION\_UNSUPPORTED、トランスポートバインディング。ディスカバリーフェーズは `get_signals` にゲートされる。アクティベーションフェーズは `signal-marketplace` のようなアクティベーションサポートを主張するエージェントのみ実行 | | [`stale_response_advisory`](https://adcontextprotocol.org/compliance/latest/universal/stale-response-advisory) | `STALE_RESPONSE` ワイヤー配置 — 助言は、トランスポート成功を保持した投入済み成功レスポンス上の `errors[]` に乗る。stale-cache 強制ステップは `force_upstream_unavailable` を伴う `comply_test_controller` にゲートされる | | [`idempotency`](https://adcontextprotocol.org/compliance/latest/universal/idempotency) | `idempotency_key` スコーピング、リプレイセマンティクス、`IDEMPOTENCY_CONFLICT`、`replayed: true`、宣言された TTL | | [`read_tool_idempotency`](https://adcontextprotocol.org/compliance/latest/universal/read-tool-idempotency) | 読み取り専用タスクラッパーは、厳格なリクエストラッパー拒否なしに 3.1 の全リクエスト `idempotency_key` エンベロープを受け入れる。3.1 の省略キー猶予プローブを含む | | [`canonical_format_validate_input`](https://adcontextprotocol.org/compliance/latest/universal/canonical-format-validate-input) | 正準形式 `validate_input` 結果セマンティクス: 必須スロット全体の構造的 pass/fail と、シードされた製品宣言の `unvalidatable_nondeterministic`。`validate_input` をアドバタイズするエージェントにゲートされる。シード製品分岐は `comply_test_controller` シードサポートも要求 | | [`security_baseline`](https://adcontextprotocol.org/compliance/latest/universal/security) | 未認証拒否、静的認証情報強制(Bearer API キーまたは HTTP Basic)、OAuth ディスカバリー + RFC 9728 オーディエンスバインディング | | [`webhook_emission`](https://adcontextprotocol.org/compliance/latest/universal/webhook-emission) | アウトバウンド webhook 適合性 — リトライ間で安定した `idempotency_key`、すべての配信での RFC 9421 webhook 署名(またはオプトイン HMAC フォールバック)。`push_notification_config` を受け入れる任意のエージェントで実行 | | [`webhook_receiver_envelope`](https://adcontextprotocol.org/compliance/latest/universal/webhook-receiver-envelope) | バイヤーレシーバーのリプレイ適合性: 完全な MCP webhook エンベロープを受け入れ、素の配信結果ペイロードを拒否し、生ボディバイト上で署名を検証し、`idempotency_key` でリトライを重複排除 | | [`notification_config_event_scope`](https://adcontextprotocol.org/compliance/latest/universal/notification-config-event-scope) | `sync_accounts.accounts[].notification_configs[]` セマンティック検証 — アカウントレベルのサブスクライバーは、それらの値が共有 enum で有効であってもメディアバイアンカーの通知タイプを拒否 | | [`notification_config_lifecycle`](https://adcontextprotocol.org/compliance/latest/universal/notification-config-lifecycle) | `sync_accounts` 上のアカウントレベル `notification_configs[]` ライフサイクル: 一時停止された登録、耐久性のある `list_accounts` エコー、サブスクライバーキーの置換、clear-all セマンティクス | | [`notification_config_rejections`](https://adcontextprotocol.org/compliance/latest/universal/notification-config-rejections) | 重複する `subscriber_id` 値のためのセマンティック `notification_configs[]` リクエスト拒否パス | | [`wholesale_feed_products`](https://adcontextprotocol.org/compliance/latest/universal/wholesale-feed-products) | 製品ホールセールフィードバージョニング: ブートストラップレスポンスは `wholesale_feed_version`/`cache_scope` を持ち、一致する `if_wholesale_feed_version` プローブは製品行なしで `unchanged` を返す | | [`wholesale_feed_signals`](https://adcontextprotocol.org/compliance/latest/universal/wholesale-feed-signals) | シグナルホールセールフィードバージョニング: ブートストラップレスポンスは `wholesale_feed_version`/`cache_scope` を持ち、一致する `if_wholesale_feed_version` プローブはシグナル行なしで `unchanged` を返す | | [`wholesale_feed_product_webhooks`](https://adcontextprotocol.org/compliance/latest/universal/wholesale-feed-product-webhooks) | 製品ホールセールフィード webhook イベントをアドバタイズするエージェントのアカウントレベル `notification_configs[]` 登録 | | [`wholesale_feed_signal_webhooks`](https://adcontextprotocol.org/compliance/latest/universal/wholesale-feed-signal-webhooks) | シグナルホールセールフィード webhook イベントをアドバタイズするエージェントのアカウントレベル `notification_configs[]` 登録 | | [`wholesale_feed_bulk_webhooks`](https://adcontextprotocol.org/compliance/latest/universal/wholesale-feed-bulk-webhooks) | `wholesale_feed.bulk_change` をアドバタイズするエージェントのアカウントレベル `notification_configs[]` 登録 | | [`pagination_integrity`](https://adcontextprotocol.org/compliance/latest/universal/pagination-integrity) | ページ化された `list_creatives` レスポンス上の `cursor` ↔ `has_more` 不変条件、継続ページから終端ページまで歩く | | [`get_products_pagination_integrity`](https://adcontextprotocol.org/compliance/latest/universal/get-products-pagination-integrity) | `get_products` ホールセールページネーションセマンティクス: シードされた製品フィードを継続から終端まで歩き、brief/refine をフィード列挙ではなく上限付きキュレート/リファイン回答として保持 | | [`get_signals_pagination_integrity`](https://adcontextprotocol.org/compliance/latest/universal/get-signals-pagination-integrity) | 広範なクエリの下でページ化された `get_signals` レスポンス上の `cursor` ↔ `has_more` 不変条件、任意の非自明なシグナルセットに対する最初のページ非終端アサーション付き | | [`pagination_integrity_list_accounts`](https://adcontextprotocol.org/compliance/latest/universal/pagination-integrity-list-accounts) | `list_accounts` レスポンス上の継続側ページネーション整合性。ストーリーボードは `sync_accounts` 経由で 3 つのサンドボックスアカウントをブートストラップし、最初のページで使用可能なカーソルを伴う `has_more=true` を要求し、そのカーソルを一度たどる | | [`pagination_integrity_creative_formats`](https://adcontextprotocol.org/compliance/latest/universal/pagination-integrity-creative-formats) | ページ化された `list_creative_formats` レスポンス上の `cursor` ↔ `has_more` 不変条件。ストーリーボードは `seed_creative_format` 経由で 2 つのクリエイティブフォーマットをシードし継続ページから終端ページまで歩く | | [`get_media_buys_pagination_integrity`](https://adcontextprotocol.org/compliance/latest/universal/get-media-buys-pagination-integrity) | ページ化された `get_media_buys` レスポンス上の `cursor` ↔ `has_more` 不変条件。ストーリーボードは `seed_media_buy` 経由で 3 つのメディアバイをシードし継続ページから終端ページまで歩く | | [`pagination_integrity_content_standards`](https://adcontextprotocol.org/compliance/latest/universal/content-standards-pagination-integrity) | ページ化された `list_content_standards` レスポンス上の `cursor` ↔ `has_more` 不変条件。ストーリーボードは `create_content_standards` 経由で 3 つのコンテンツ標準構成をブートストラップし継続ページから終端ページまで歩く | | [`pagination_integrity_collection_lists`](https://adcontextprotocol.org/compliance/latest/universal/collection-lists-pagination-integrity) | ページ化された `list_collection_lists` レスポンス上の `cursor` ↔ `has_more` 不変条件。ストーリーボードは `create_collection_list` 経由で 3 つのコレクションリストをブートストラップし継続ページから終端ページまで歩く | | [`pagination_integrity_property_lists`](https://adcontextprotocol.org/compliance/latest/universal/property-lists-pagination-integrity) | ページ化された `list_property_lists` レスポンス上の `cursor` ↔ `has_more` 不変条件。ストーリーボードは `create_property_list` 経由で 3 つのプロパティリストをブートストラップし継続ページから終端ページまで歩く | | [`deterministic_testing`](https://adcontextprotocol.org/compliance/latest/universal/deterministic-testing) | `comply_test_controller` ステートマシン — `capabilities.compliance_testing.supported: false` ならスキップ | | [`signed_requests`](https://adcontextprotocol.org/compliance/latest/universal/signed-requests) | RFC 9421 トランスポート層リクエスト署名検証 — `request_signing.supported: false` ならスキップ | | [`billing_gate_dispatch`](https://adcontextprotocol.org/compliance/latest/universal/billing-gate-dispatch) | `sync_accounts.billing` 拒否時の 2 ゲートディスパッチ: セラー全体のケイパビリティゲート(`error.details.scope` 付き `BILLING_NOT_SUPPORTED`)対 バイヤーエージェントごとの商業関係ゲート(クランプされた `rejected_billing` + オプションの `suggested_billing` 形状付き `BILLING_NOT_PERMITTED_FOR_AGENT`)。セラーが 3 つの `billing` 値すべてをサポートするときケイパビリティフェーズはスキップ。テストキットが `commercial_relationship: passthrough_only` を宣言しないときエージェントごとのフェーズはスキップ | `capabilities.compliance_testing.supported: true` を宣言するエージェントは、完全な [テストコントローラー](/docs/building/by-layer/L3/comply-test-controller) を実装しなければなりません(MUST)。部分的なコントローラーは非適合なので、出荷するより `false` を宣言してください。 `request_signing.supported: true` を宣言するエージェントは、[リクエスト署名プロファイル](/docs/building/by-layer/L1/security#署名付きリクエストトランスポート層) に従って完全な RFC 9421 検証者を実装しなければなりません(MUST)。部分的な検証者は非適合なので、出荷するより `false` を宣言してください。 ## Protocol 適合性 `supported_protocols` クレームはプロトコルのベースラインストーリーボードを義務付けます。 | `supported_protocols` | Storyboard | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `media_buy` | [`media_buy_seller`](https://adcontextprotocol.org/compliance/latest/protocols/media-buy/) + [`media_buy_state_machine`](https://adcontextprotocol.org/compliance/latest/protocols/media-buy/state-machine) | | `creative` | [`creative_lifecycle`](https://adcontextprotocol.org/compliance/latest/protocols/creative/) | | `signals` | [`signals_baseline`](https://adcontextprotocol.org/compliance/latest/protocols/signals/) | | `governance` | [`media_buy_governance_escalation`](https://adcontextprotocol.org/compliance/latest/protocols/governance/) | | `brand` | [`brand_baseline`](https://adcontextprotocol.org/compliance/latest/protocols/brand/) | | `sponsored_intelligence` | [`si_baseline`](https://adcontextprotocol.org/compliance/latest/protocols/sponsored-intelligence/) | ## Specialism 適合性 `specialisms` クレームは、親プロトコルベースラインに加えて専門分野のストーリーボードを義務付けます。カタログは [`/compliance/latest/index.json`](https://adcontextprotocol.org/compliance/latest/index.json) に存在します。人間可読なインデックスは [コンプライアンスカタログ](/docs/building/verification/compliance-catalog) です。 専門分野は `status` を持ちます — `stable`(検証済み pass/fail)、`preview`(ストーリーボード未定義。ランナーは `passed: null` を発行)、`deprecated`(削除予定)。エージェントは preview 専門分野を主張してもよい(MAY)が、preview クレームは pass/fail 判定を生みません。 ## ワイヤーの外側 一部の要件はストーリーボードで検証できません。なぜならワイヤーレベルではなくオペレーターレベルだからです。それらは適合エージェントを運用することの一部のままですが、スイートはそれらを証明できません。オペレーターはこれらに対して自己評価しなければなりません(MUST)。第三者フレームワーク(SOC 2、ISO 27001)が通常の証明パスです。 * **シークレットストレージ** — 認証情報は KMS または同等物に存在すべきです(SHOULD)。ワイヤーは認証が成功するかどうかだけを示し、鍵がどこに保存されたかは示しません。 * **認証情報のローテーションと失効** — オペレーターは、侵害された認証情報を 1 時間未満で失効させる文書化されたパスを持たなければなりません(MUST)。ワイヤーはランブックを観測できません。 * **人員と物理的セキュリティ** — 誰が本番に触れられるか、ブレークグラス管理、従業員のオフボーディング。完全にプロトコルの外側。 * **ガバナンスエージェントのデューデリジェンス** — オペレーターが第三者ガバナンスエージェントに依存するとき、バイヤーはそれをマルチカスタマーの爆発半径を持つプロセッサーとして扱い、その姿勢を評価すべきです(SHOULD)。ストーリーボードはセラーによる正しい JWS 処理を検証しますが、ガバナンスエージェント自体を保証できません。 * **LLM サブプロセッサーの姿勢** — エージェントが LLM プロバイダーを使う場合、そのプロバイダーとの DPA が、プロンプト、ブランドアセット、クリエイティブメタデータが保持されうるかを統制します。プロトコルはアップストリームの DPA 条件を見られません。 * **インシデントレスポンス** — AdCP は監視する価値のあるシグナル(`IDEMPOTENCY_CONFLICT` スパイク、失敗したガバナンス検証、SSRF 拒否)を発行します。検出、アラートルーティング、レスポンスはオペレーターの関心事です。 * **データレジデンシー構成** — EU / UK データがリージョン内に保たれるかどうかとその方法は、通常エージェントのケイパビリティまたはコントラクトで宣言されます。ワイヤーは宣言を記録し、基盤インフラは記録しません。 完全なオペレーターチェックリスト: [セキュリティモデル § 本番稼働前に検証すべきもの](/docs/building/concepts/security-model#what-to-verify-before-going-live)。 ## 適合性 対 外部保証 適合性はワイヤーレベルの正しさです。SOC 2、ISO 27001、NIST CSF は運用保証です。それらは異なる質問に答え、どちらも他方の代替にはなりません。 | 外部制御領域 | ストーリーボード証拠 | 外部保証へのギャップ | | ----------------------------------- | --------------------------------------------------- | -------------------------------- | | アクセス制御(SOC 2 CC6、ISO 27001 A.5.15) | `security_baseline`(アイデンティティ)+ プロトコルストーリーボードの分離チェック | 人員アクセスレビュー、最小権限管理、オフボーディング | | 変更管理(SOC 2 CC8) | `idempotency` はワイヤー上で重複状態変更が防がれることを証明 | デプロイ承認、リリースゲート、ロールバック手順 | | システム監視(SOC 2 CC7、ISO 27001 A.8.16) | エラー分類が監視可能な表面を生成 | 検出エンジニアリング、アラートルーティング、オンコールランブック | | 暗号(ISO 27001 A.8.24) | TLS、RFC 9421 署名、JWS ガバナンストークン | KMS 選択、ローテーション頻度、証明書ライフサイクル | | 監査ログ(SOC 2 CC7) | ガバナンスストーリーボードは署名付きレコード発行を検証 | ログ保持、リーガルホールド、整合性監視 | | データ処理(SOC 2 Privacy、GDPR、ISO 27701) | TMP 2 コール分離、オーディエンスハッシュ、シグナルアクセス制御 | データ主体の権利、DPA 管理、越境転送 | | ベンダーとサブプロセッサーリスク(SOC 2 CC9) | `adagents.json` / brand.json ディスカバリー、JWKS 公開 | 第三者リスク評価、LLM プロバイダーレビュー | | インシデントレスポンス(SOC 2 CC7、NIST CSF RS) | シグナルは観測可能。レスポンスは義務化されない | ランブック、机上演習、侵害通知 | | 事業継続(ISO 27001 A.5.30) | クロスインスタンス状態ストーリーボードチェック | RPO/RTO 目標、DR テスト | 2 つの実践的帰結: 1. ストーリーボード通過証拠は特定の外部制御目標をサポートしてもよい(MAY)。監査の代替にはなりません。 2. 外部認証は AdCP 適合性を含意しません。SOC 2 Type II は `create_media_buy` レスポンスが検証されるかどうかについて何も言いません。 ## 適合性を主張する方法 1. `get_adcp_capabilities` で `supported_protocols` と `specialisms` を宣言する。 2. 宣言が義務付けるすべてのストーリーボード — universal + protocol ベースライン + specialism ベースライン — を特定の AdCP メジャーバージョンで通過する。 3. 宣言と動作を同期に保つ。スイートがたまたまテストする未宣言のケイパビリティは、失敗する宣言済みケイパビリティとは別です。両方とも非適合です。 適合性はバージョンごとです。スイートはバージョンごとです。3.0 適合のエージェントはそれによって 3.1 適合にはなりません。 **第三者証明のために**、AAO に対してハートビートを実行し [AAO Verified バッジ](/docs/building/verification/compliance-catalog) を獲得してください。バッジは、AAO がエージェントを最近テストし通過がまだ保持されているという署名付きクレームです。*verified* でフィルターするバイヤーは *conformant* より小さいセットを得ます — より少ないエージェント、より新鮮な証明、責任を負う名指しされた当事者。 ## この文書がしないこと * **個々の MUST を定義する。** ストーリーボードがします。ルールがストーリーボードにないなら、それは適合性の一部ではありません。 * **認定を付与または取り消す。** [AgenticAdvertising.org 認定プログラム](/docs/learning/overview) がこの上に走ります。適合性は必要ですが十分ではありません。 * **既にスイートにあるもの以外のリファレンステストベクターを公開する。** [リファレンステストベクターインデックス](/docs/reference/test-vectors) は今日出荷されるベクターセットをカタログ化します。より広範なタスクレベルコーパスは 3.0 GA と 3.1 の間で漸増的に到達し、[#2383](https://github.com/adcontextprotocol/adcp/issues/2383) でスコープされます。 ## ストーリーボードが失敗するとき 失敗が仕様、モック、SDK 間の不一致を表面化するとき、以下のセクションはトリアージ順を与えます。症状から原因への検索については、このセクション末尾のリンクを参照してください。 ### Mock-server authority and failure triage `adcp mock-server` は安定表面のリファレンスワイヤー実装です。ストーリーボードの失敗がモックまたは SDK を巻き込むとき、このトリアージ順を使ってください: **トリアージ順: spec → mock → SDK。** ストーリーボード(とそれらが参照するスキーマ)は正準です。モックはストーリーボードを解釈します。SDK はモックを通じてプロトコルを消費します。 | Condition | Default verdict | Next step | | ---------------------------------------------- | --------------- | ---------------------------------------------------- | | SDK ワイヤー形状がモックのものと **異なる** | SDK が間違い | SDK に対してバグを提出 | | SDK ワイヤー形状がモックのものと **一致** するが、ストーリーボードが依然として失敗 | モックが間違い | モックを修正するため `adcontextprotocol/adcp` に issue を提出 | | ストーリーボードアサーションが、それ以外は通過するワイヤー形状と衝突 | ストーリーボードが間違い | ストーリーボードを修正するため `adcontextprotocol/adcp` に issue を提出 | | 仕様テキスト(ストーリーボード散文またはスキーマ)がモックと明示的に矛盾 | 仕様が勝つ。モックがバグ | モックを修正するため `adcontextprotocol/adcp` に issue を提出 | **スコープ。** このトリアージ順は安定表面のみに適用されます。実験的表面([実験的ステータス](/docs/reference/experimental-status) を参照)は活発な改訂中です。そこでのモック動作はまだ権威的ではありません。 **仕様の曖昧性 対 仕様の沈黙。** 仕様テキストが存在するが曖昧なとき、モックの動作が権威的解釈をピン留めします — そのピン留めは散文がまだ引き締められていなくても規範的です。仕様がある点について完全に沈黙しモックがそれに対する動作を持たないとき、チェーンは切れます。モックを権威的として扱うのではなく [known-ambiguities issue](/docs/building/cross-cutting/known-ambiguities) を開いてください。 * **[ストーリーボードのトラブルシューティング](/docs/building/operating/storyboard-troubleshooting)** — 最も一般的なストーリーボード失敗のエラーパターン → 根本原因 → 修正 * **[既知の仕様曖昧性](/docs/building/cross-cutting/known-ambiguities)** — 回避策と issue リンク付きのオープンな仕様ギャップ。基盤 issue がクローズすると項目は削除される ## さらに読む * **[AAO Verified](/docs/building/verification/aao-verified)** — セラーのライブ広告サーバー統合の継続的可観測性検証 * **[コンプライアンスカタログ](/docs/building/verification/compliance-catalog)** — ストーリーボード ID 付きプロトコルと専門分野の完全な分類 * **[エージェントを検証する](/docs/building/verification/validate-your-agent)** — スイートを実行する方法 * **[セキュリティモデル](/docs/building/concepts/security-model)** — セキュリティストーリーボードが強制する 5 つの防御層の戦略的フレーミング * **[Security(実装リファレンス)](/docs/building/by-layer/L1/security)** — ストーリーボードが引用する規範的ルール * **[バージョニング](/docs/reference/versioning)** — メジャーバージョンサポートウィンドウ * **[既知の制限](/docs/reference/known-limitations)** — 仕様の可視な縁 # テスト準備を整える Source: https://adcp-docs-ja.pier1.co.jp/docs/building/verification/get-test-ready セールスエージェントオペレーターがストーリーボードを実行する前に整えておく必要があるもの — ケイパビリティ、サンドボックスアカウント、コンプライアンステストコントローラー。 ストーリーボードは、エージェントが **conformant** として公開されるかを決めるバージョン管理されたバイヤーシミュレーションスイートです。バイヤーエージェントはそのステータスでフィルターします — 過剰主張やストーリーボード失敗は、CI 警告ではなく公開で永続的なシグナルです。このページは「エージェントを構築した」と「`npx @adcp/sdk@latest storyboard run` を実行できる」の間のチェックリストです。 ## ランナーが必要とする 3 つのサーフェス ランナーは、バイヤーが呼ぶのと同じ公開ツール、加えてフィクスチャセットアップ用の 1 つのサンドボックス専用ツールを通じてエージェントを駆動します。3 つのサーフェスが整っていなければなりません: | Surface | What it tells the runner | Where it lives | | ------------------------------------------------------------------------------- | ----------------------------------- | ------------------- | | [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) | どのプロトコルと専門分野を主張するか、サンドボックスをサポートすること | エージェントのケイパビリティレスポンス | | [`sync_accounts`](/docs/media-buy/advanced-topics/sandbox)(または `list_accounts`) | テストを実行するサンドボックスアカウントを取得する方法 | エージェントのアカウントツール | | [`comply_test_controller`](/docs/building/by-layer/L3/comply-test-controller) | フィクスチャをシードしセラー側の遷移を決定的に強制する方法 | エージェント、サンドボックス専用 | あなたはこれら 3 つのサーフェスを出荷します。ランナーはストーリーボード選択、フィクスチャ順序、レスポンス比較を所有します。 ## Step 1 — ケイパビリティを正直に宣言 `get_adcp_capabilities` は、ランナーがどのストーリーボードがあなたに適用されるかを選ぶ方法です。それは [適合性](/docs/building/verification/conformance) コントラクトでもあります: あなたは宣言したものに一致するすべてのストーリーボードを通過することを約束しています。 下の例はフルサービスの保証セラー(プロポーザルライフサイクル有効)用です。直接購入の保証セラーは `media_buy.supports_proposals: false` を設定(または省略)します。放送 TV セラーは `sales-broadcast-tv` を主張します。クリエイティブのみのエージェントは `creative-ad-server` または `creative-generative` 専門分野で `creative` プロトコルを主張します。シグナルプロバイダーは `signals` を主張します。パターンは同じ: 実際に実装するもののみを宣言。 ```json theme={null} { "supported_protocols": ["media_buy", "creative"], "specialisms": ["sales-guaranteed"], "media_buy": { "supports_proposals": true }, "account": { "sandbox": true, "require_operator_auth": false } } ``` * **`supported_protocols`** — `/compliance/{version}/protocols/` から一致するプロトコルストーリーボードを引き込む。 * **`specialisms`** — オプトイン専門分野ストーリーボードを引き込む(完全な列挙については [Compliance Catalog](/docs/building/verification/compliance-catalog) を参照)。 * **`account.sandbox: true`** — サンドボックスセマンティクス(実際の支出なし、本番副作用なし)を尊重することをシグナル。 * **`account.require_operator_auth`** — サンドボックスブートストラップパス(ステップ 2)を決定。 RTB のみを実行するのに `sales-guaranteed` を主張すると、記録に残るかたちで失敗するストーリーボードに送り込まれます。適合性ステータスは、バイヤーエージェントがセラーをフィルターするのに使う [Verified](/docs/building/verification/conformance) バッジの一部です — 一度過剰主張すると、どこでも包含を失います。 ## Step 2 — サンドボックスブートストラップパスを選ぶ ランナーは何かをする前にサンドボックスアカウントを取得しなければなりません。`require_operator_auth` フラグがパスを選びます: **バイヤー宣言アカウント(`require_operator_auth: false`)。** エージェントは任意の認証されたバイヤーからの `sync_accounts` を受け入れます。ランナーは `sandbox: true` で `sync_accounts` を呼びオンデマンドでテストアカウントを鋳造します。ほとんどの新しいセールスエージェントはここから始めます。 **アカウント ID 名前空間(`require_operator_auth: true`)。** アカウントはあなた側の人間または上流プラットフォームによって事前プロビジョニングされなければなりません。ランナーはサンドボックスフィルターで `list_accounts` を呼び既存のテストアカウントを発見します。認証情報が正確に 1 つのサンドボックスアカウントにバインドされている場合、そのシングルトンを返します。オペレーターに 1 つを要求する方法を伝える短いノートを公開します — 連絡先、期待されるターンアラウンド、受け取る認証情報を含めます。 完全な詳細と例: [サンドボックスモード](/docs/media-buy/advanced-topics/sandbox)。 ## Step 3 — コンプライアンステストコントローラーを実装 コンプライアンステストコントローラーなしでは、ランナーはバイヤー開始のフロー(**観察モード**)のみをテストします — スキーマ適合性、auth 拒否、ハッピーパスバイヤー呼び出し。それは初期統合作業と本番パスのサンドボックススモークテストに十分ですが、コントローラーシードまたはコントローラー強制のシナリオがスコープ内のときはいつでも **部分カバレッジ** を生成します。[適合性](/docs/building/verification/conformance) は、コントローラーによって可能になる完全なライフサイクルウォークである **決定的モード** を、完全な専門分野カバレッジのラインとして扱います。 実際には、セラーは通常両方を実行します: | Run | Endpoint | Expected result | | ----------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | | 観察サンドボックス実行 | サンドボックスアカウントを持つ本番エンドポイント、コントローラー未公開 | `steps_failed = 0`。live-only プローブは未選択として現れ、選択されたコントローラー依存シナリオは `missing_test_controller` スキップでサマリーを `partial` にしうる | | 決定的実行 | `comply_test_controller` を持つ dev/staging またはサンドボックス専用エンドポイント | `steps_failed = 0` でコントローラー依存カバレッジスキップなし | 最初の実行は「本番バイヤー可視パスはサンドボックストラフィックを許容するか?」に答えます。2 番目は「ランナーは宣言された専門分野のすべてのライフサイクルパスを証明できるか?」に答えます。それらを 1 つのシグナルに折り畳まないでください。ゼロ失敗の部分実行は有用ですが、バイヤーはどのライフサイクルアサーションがグレードされなかったかを正確に知るためスキップリストが必要です。 `comply_test_controller` は、3 つのファミリーをカバーする `scenario` パラメーターを持つ単一のサンドボックス専用ツールです: | Scenario family | What it does | When you need it | | --------------- | -------------------------------------------------------- | --------------------------------------------- | | `seed_*` | 呼び出し元供給の ID でフィクスチャ(プロダクト、価格オプション、クリエイティブ、プラン、メディアバイ)を作成 | ほぼすべてのストーリーボード — これがハードコード ID ディスカバリーを置き換える | | `force_*` | 通常セラー開始のエンティティを状態遷移を通じて駆動 | ステートマシン(クリエイティブ承認、アカウント停止など)をテストする任意のストーリーボード | | `simulate_*` | 配信データまたは予算支出を注入 | レポートと予算のストーリーボード | シナリオごとのパラメーターとレスポンス形状については [コンプライアンステストコントローラーリファレンス](/docs/building/by-layer/L3/comply-test-controller) を、各専門分野がどのシナリオを要求するかについては [Compliance Catalog](/docs/building/verification/compliance-catalog) を参照。 ### SDK スキャフォールドの配線 `@adcp/sdk`(6.x が AdCP 3.0 の本番 GA)は `createComplyController` を出荷し、ツール登録、パラメーター検証、エラーエンベロープ、再シード冪等性を再実装せずにデータ層をコントローラーに配線できます。 ```bash theme={null} npm install @adcp/sdk ``` スキャフォールドは TypeScript/JavaScript です。Python、Go、Java のセラーは [スキーマ](https://adcontextprotocol.org/schemas/v3/compliance/comply-test-controller-request.json) に対して直接ツールを実装します — 下のコントラクト(アダプター、エラーコード、冪等性セマンティクス)は同じように適用されます。他言語の SDK は [Choose your SDK](/docs/building/by-layer/L4/choose-your-sdk) で追跡されます。 ```ts theme={null} import { createComplyController, TestControllerError } from '@adcp/sdk/testing'; // `server` is your AdcpServer or MCP server instance — see `createAdcpServer` in // `@adcp/sdk/server` if you need a reference setup. const controller = createComplyController({ seed: { product: async ({ product_id, fixture }) => { await productRepo.upsert(product_id, fixture); }, creative: async ({ creative_id, fixture }) => { await creativeRepo.upsert(creative_id, fixture); }, // Add pricing_option, plan, media_buy as your claimed storyboards require. }, force: { creative_status: async ({ creative_id, status, rejection_reason }) => { const previous = await creativeRepo.getStatus(creative_id); if (previous == null) { throw new TestControllerError('NOT_FOUND', `creative ${creative_id} not found`); } const result = await creativeRepo.transition(creative_id, status, rejection_reason); if (result.kind === 'invalid_transition') { throw new TestControllerError('INVALID_TRANSITION', result.message, previous); } return { success: true, previous_state: previous, current_state: result.status }; }, // Add account_status, media_buy_status, session_status as needed. }, // simulate: { delivery, budget_spend } — add if you claim reporting/budget specialisms. }); // Primary gate: register the tool only in sandbox deployments, so it never // appears in production `tools/list`. if (process.env.ADCP_SANDBOX === '1') { controller.register(server); } ``` スキャフォールドがあなたのために扱うもの: * **ツール登録とスキーマ。** `controller.toolDefinition` が公開された仕様バージョンと同期のまま。 * **ディスパッチと `UNKNOWN_SCENARIO`。** 登録しないシナリオは自動的に `UNKNOWN_SCENARIO` を返す — スキーマエラーは決してない。 * **パラメーター検証。** 無効なパラメーターは、アダプターに到達せずに読める `error_detail` を伴う `INVALID_PARAMS` を生成。 * **シード冪等性。** 同じ `product_id` と等価な `fixture` で `seed_product` を 2 回呼ぶと `previous_state: "existing"` を返す。分岐した `fixture` は `INVALID_PARAMS` を返す。アダプターは最初のシードでのみ呼ばれる。 * **型付きエラーエンベロープ。** コントローラーエラーコード表の `code` で `TestControllerError(code, message, currentState?)` を throw。一般的なアダプターコードは `'INVALID_TRANSITION' | 'NOT_FOUND' | 'FORBIDDEN' | 'INVALID_PARAMS'`。ダイジェストモードの `query_upstream_traffic` 実装は、RFC 8785/JCS 正準化が非有限数値に遭遇したとき `JCS_NON_FINITE_NUMBER` も返しうる。 スキャフォールドはステートマシンを **所有しません**。遷移ルールはあなたのアダプターに存在するため、コンプライアンステストと本番が 1 つの真実の源泉を共有します — [teach-to-test 回避セクション](#avoiding-the-teach-to-test-trap) が依存するメカニクス。 ### サンドボックスゲーティングの 2 層 スキャフォールドは 2 つのゲートをサポートします。同じプロセスからサンドボックスと本番の両トラフィックを提供する任意のデプロイで両方を出荷してください: 1. **登録ゲート(主要)。** `controller.register(server)` を環境チェックでラップ。これが `comply_test_controller` を本番 `tools/list` から完全に外すものです。それなしでは、本番エンドポイントの漏洩したサンドボックス認証情報がセラー側の状態強制を露出します。 2. **リクエストごとのゲート(多層防御)。** `createComplyController` に `sandboxGate: (input) => boolean` を渡す。スキャフォールドはすべてのリクエストでそれを呼び、`false` を返すとき `FORBIDDEN` を返す。ツールが登録されているが一部のリクエストが依然本番アカウントを参照しうる共有プロセスデプロイでこれを使う。 `sandboxGate` は生のツール入力(`Record`)を受け取ります。SDK は auth コンテキストをそれに配管しません — あなたが何を検査するかを決めます。典型的なパターンは、参照されたエンティティ ID を `params` から引き出し、それが自身のデータ層でサンドボックスアカウントに属することを検証することです: ```ts theme={null} sandboxGate: async (input) => { const params = input.params as { account_id?: string; media_buy_id?: string } | undefined; const accountRef = params?.account_id ?? (params?.media_buy_id && await mediaBuyRepo.getAccountId(params.media_buy_id)); return typeof accountRef === 'string' && await accountRepo.isSandbox(accountRef); } ``` カスタム MCP ラッパー — リクエストごとの auth の AsyncLocalStorage、トランスポートレベルのサンドボックスゲーティング、セッション裏付けストア — については、`@adcp/sdk/server` から低レベルの `handleTestControllerRequest`、`toMcpResponse`、`TOOL_INPUT_SHAPE` を直接合成してください。 ## Step 4 — ストーリーボードランナーを実行 3 つのサーフェスが整ったら、ランナーが引き継ぎます: ```bash theme={null} npx @adcp/sdk@latest --save-auth my-agent http://localhost:3001/mcp npx @adcp/sdk@latest storyboard run my-agent ``` ランナーはケイパビリティを発見し、サンドボックスアカウントを取得し、コントローラー経由でフィクスチャをシードし、各一致するストーリーボードを歩きます。完全な CLI、デバッグフラグ、Addie ワークフローについては [エージェントを検証する](/docs/building/verification/validate-your-agent) を参照。 ## Avoiding the teach-to-test trap ストーリーボードはフィクスチャ ID をハードコードします — `"test-product"`、`"campaign_hero_video"`、`"acmeoutdoor.example"`。それらの文字列を特別扱いするコントローラーは、すべての実際のバイヤーで黙って失敗しながらスイートを通過します。それはまさに適合性が防ごうとしている業界コストです: すべての適合性後の統合失敗がセラーの評判を焼き、バイヤーエージェントの懐疑を膨らませ、プロトコル採用を遅らせます。 SDK スキャフォールドは既に正しい方向を指しています: アダプターは `product_id`、`creative_id` などを条件ではなく値として受け取ります。アダプターに `product_id === "test-product"` のスイッチが含まれていたら、後退しています。 2 つの経験則: 1. **シードシナリオを汎用に実装。** `seed_product` は任意の `product_id` を受け入れ、その ID でプロダクトをサンドボックスデータ層に永続化する。アダプターはサンドボックスストアに対する実際の upsert の薄いラッパー。 2. **`fixture` オブジェクトがコントラクトで、ID はそうでない。** ストーリーボード作者は `fixture` をテストが必要とする最小の形状に設定する。それを超えたすべて — ディスカバリー、フィルタリング、認可 — は、本番データで実行するのと同じ方法でフィクスチャシードされたデータで実行される、あなたの通常のコードパス。 確認するには: ストーリーボードのフィクスチャ ID をランダム UUID にスワップし再実行します。実行がまだ通過すれば、コントローラーは正しいです。壊れれば、修正するハードコードされた動作があります。 ## 準備チェックリスト 最初の完全なストーリーボードスイープの前に: * [ ] `get_adcp_capabilities` が実際に実装するプロトコルと専門分野のみを返す * [ ] `account.sandbox: true` が宣言され尊重される — サンドボックスリクエストが実際の支出、本番プラットフォーム呼び出し、永続化された本番状態を生成しない * [ ] `sync_accounts`(暗黙)または `list_accounts`(明示)がステップ 2 に従いサンドボックスリクエストを扱う * [ ] `comply_test_controller` が任意の本番エンドポイントの `tools/list` から欠如している * [ ] 非サンドボックスアカウントを参照するリクエストが `FORBIDDEN` で拒否される * [ ] 主張するストーリーボードが依存するすべてのシードシナリオが、ID 特別扱いなしにフィクスチャを汎用に永続化する * [ ] すべての force シナリオが本番と同じ状態遷移ルールを使い、無効な遷移で型付きエラーを返す * [ ] フィクスチャ ID がランダム UUID にスワップされても完全なストーリーボードスイープが通過する ## 次は * **[エージェントを検証する](/docs/building/verification/validate-your-agent)** — CLI、Addie ワークフロー、マルチインスタンス検証 * **[コンプライアンステストコントローラーリファレンス](/docs/building/by-layer/L3/comply-test-controller)** — 完全なシナリオごとの仕様 * **[サンドボックスモード](/docs/media-buy/advanced-topics/sandbox)** — 2 つのアカウントモデルパスを詳しく * **[Conformance](/docs/building/verification/conformance)** — 実行が通過したら「conformant」と「verified」が何を意味するか # Auth グレーダー Source: https://adcp-docs-ja.pier1.co.jp/docs/building/verification/grading RFC 9421 リクエスト署名適合性、OAuth ハンドシェイク診断、Ed25519/P-256 署名鍵生成と検証のための AdCP CLI グレーダー。 `@adcp/sdk` 5.21+ は認証適合性のための CLI グレーダーを出荷します。それらは [コンプライアンスストーリーボード](/docs/building/verification/validate-your-agent) とは別です — ストーリーボードはプロトコル動作をエンドツーエンドでテストし、これらのグレーダーは認証と署名の層を特にテストし、ベクターごとの診断と仮説ランク付けされた失敗分析を与えます。 下のすべてのコマンドは `npx @adcp/sdk@latest` を使います。`@adcp/sdk` をグローバルにインストール(`npm install -g @adcp/sdk`)している場合、`npx @adcp/sdk@latest` プレフィックスを落として `adcp` を直接使えます。 ## リクエスト署名グレーダー RFC 9421 適合性をエージェントに対してエンドツーエンドで検証します。すべての署名ベクターを実行しベクターごとの結果をレポートするため、どの正準化ルールやヘッダーカバレッジチェックが失敗しているかを正確に追跡できます。 ```bash theme={null} npx @adcp/sdk@latest grade request-signing ``` **確認するもの:** * 署名ベース正準化(method、target-uri、authority、content-type、content-digest) * カバードコンポーネントの完全性と順序 * `alg` と `kid` フィールドの存在と有効性 * タイムスタンプウィンドウ(±60 s)と nonce の一意性 * リプレイ検出(エージェントがアドバタイズする場合) * 否定ベクター拒否 — 各不正な形式のリクエストは期待されるエラーコードを生成しなければならない(MUST) **いつ使うか:** `get_adcp_capabilities` で任意の操作を `required_for` に切り替える前。当事者が署名検証失敗をレポートするとき。鍵アルゴリズムをアップグレードするとき(Ed25519 → P-256 またはその逆)。 ## OAuth ハンドシェイク診断 エージェントの OAuth ディスカバリードキュメント(RFC 9728 protected-resource メタデータ、RFC 8414 authorization-server メタデータ)をプローブし、authorization code + PKCE フローを実行し、結果の JWT をデコードし、何が間違っているかについて仮説をランク付けします。 ```bash theme={null} npx @adcp/sdk@latest diagnose-auth ``` `` 形式は `~/.adcp/config.json` の保存されたエイリアス(`npx @adcp/sdk@latest --save-auth ` 経由で設定)を使います。 **プローブするもの:** * `/.well-known/oauth-protected-resource` — 存在、`authorization_servers` リスト、HTTPS 強制 * `/.well-known/oauth-authorization-server` — issuer 一致、`token_endpoint`、`code_challenge_methods_supported` * トークンエンドポイントレスポンス — トークンタイプ、有効期限、scope カバレッジ * JWT クレーム — `iss`、`sub`、`aud`、`exp`、`iat` の存在と有効性 * クロスオリジン `authorization_servers` issuer ピン留め(リソースメタデータの AS URL が帯域外設定に一致しない場合フラグ) **出力:** ランク付けされた仮説リスト、例: `1. token_endpoint not reachable (connection refused) — likely cause`、`2. issuer mismatch — AS URL returned by protected-resource does not match adagents.json`。各仮説は該当する仕様セクションにリンクします。 **いつ使うか:** bearer トークン設定の後 `AUTH_REQUIRED` エラーが持続するとき。動的クライアント登録が予期しないレスポンスを返すとき。新しいセラーの OAuth セットアップが黙って失敗するとき。 ## 鍵生成 エージェントの `jwks_uri` での公開用にフォーマットされた Ed25519 または P-256 キーペアを生成します。 ```bash theme={null} npx @adcp/sdk@latest signing generate-key ``` 出力: * 秘密鍵ファイル(PEM、エージェントの署名設定用) * `"kid"`、`"use": "sig"`、`"key_ops": ["verify"]`、`"adcp_use": "request-signing"`、`"alg": "EdDSA"`(または P-256 の `"ES256"`)を持つ、JWKS エンドポイントに貼り付け可能な JWK **いつ使うか:** 初期署名セットアップ。鍵ローテーション(新しいものを生成、古いものと並べて公開、飛行中のリクエストをドレイン、古いものを退役)。 ## ベクター検証者 完全なグレーダーを実行せずに単一の署名ベクターを検証します。実装中に特定の正準化ケースをデバッグするのに有用。 ```bash theme={null} npx @adcp/sdk@latest signing verify-vector ``` stdin からベクター([`/compliance/latest/test-vectors/request-signing/`](https://adcontextprotocol.org/compliance/latest/test-vectors/request-signing/) のテストベクタースキーマに一致する JSON)を読み、クライアントの署名ベースが期待される出力に一致するかをレポートします。 **いつ使うか:** 署名クライアントを実装しながら、エンドツーエンドでテストする前に各コンポーネントルールを分離して確認するとき。 ## 関連 * [エージェントを検証する](/docs/building/verification/validate-your-agent) — ストーリーボードベースのプロトコルコンプライアンステスト * [Authentication](/docs/building/by-layer/L2/authentication) — 認証モデル概要、bearer トークン、RFC 9421 導入 * [セキュリティ実装リファレンス](/docs/building/by-layer/L1/security#署名付きリクエストトランスポート層) — 完全な RFC 9421 プロファイル、検証者チェックリスト、鍵公開ルール # グレーディングの仕組み Source: https://adcp-docs-ja.pier1.co.jp/docs/building/verification/how-grading-works AdCP コンプライアンスランナーが専門分野宣言を具体的なグレードされたストーリーボードのセットにどう変換するか — そしてケイパビリティフラグがどうそのセットを変えるか。 [適合性仕様](/docs/building/verification/conformance#conformance-is-layered) は 3 つの義務層を定義します: Universal、Protocol、Specialism。このページは Specialism 層の内側で何が起こるかを説明します: 専門分野マニフェストがどうグレードされるシナリオのセットに解決するか、シナリオごとのケイパビリティゲートがどうそのセットを狭めるか広げるか。 ## 宣言からグレードされるシナリオへ エージェントが `get_adcp_capabilities` で専門分野を宣言すると、ランナーは: 1. `/compliance/{version}/specialisms/{id}/` の専門分野マニフェストをフェッチ。 2. マニフェストの `requires_scenarios` リスト — ランナーがグレードしなければならないシナリオ ID の順序付きセット — を読む。 3. 各シナリオについて、シナリオが `requires_capability` ゲートを宣言するかを確認。 4. ゲートが存在する場合、シナリオを実行するかスキップするかを決めるため、`get_adcp_capabilities` レスポンスから名前付きパスを読む。 マニフェストが完全なシナリオリストを駆動します。ケイパビリティゲートはその上にシナリオごとに適用されます。 ## ランナー証拠対検証ポリシー ストーリーボードランナーは、エージェントがバッジを獲得するか特定のバイヤーを満たすかを決めません。証拠を生成します: 実行されたアサーション、失敗、選択されたがスキップされたステップ、選択されなかったステップ、スキップ理由、使われたエンドポイント/実行モード。検証ポリシーがその証拠を消費し、どのギャップが名前付きの結果に許容可能かを決めます。 | Question | Decided by | Evidence used | | ------------------------------------------ | ----------------------------- | ---------------------------------------------------------------- | | 「このステップはストーリーボードに一致したか?」 | ストーリーボードランナー | ステップ検証とレスポンススキーマ | | 「このエージェントは **Verified (Spec)** を獲得するか?」 | AgenticAdvertising.org 検証ポリシー | 登録された spec/test エンドポイントに対するランナー証拠、加えてメンバーシップと宣言チェック | | 「このエージェントは **Verified (Sandbox)** を獲得するか?」 | AgenticAdvertising.org 検証ポリシー | `account.sandbox: true` の下の本番エンドポイントに対するランナー証拠、加えてサンドボックス分離チェック | | 「これは特定のバイヤーに十分か?」 | そのバイヤーの要件プロファイル | Verified モード、宣言されたプロトコル/専門分野、要求する任意ケイパビリティ、AdCP 外の任意のビジネスまたは統合要件 | これがランナー分類が重要な理由です。「これはサンドボックス専用実行なので選択されなかった」はスイート選択の事実で、セラーケイパビリティギャップではありません。「セラーが任意機能を主張しなかったのでスキップ」はバッジに許容可能でも、その機能を要求するバイヤーには許容不可能かもしれません。「宣言された必須ツールが欠けているのでスキップ」はセラー実装問題です。 ## 要件プロファイル 「何を通過する必要があるか?」に答えるには、満たそうとしているプロファイルから始めます: | Target | Minimum question the profile answers | | ---------------------- | ------------------------------------------------------------------------------------------------------------ | | **Verified (Spec)** | 宣言された AdCP サーフェスは、ブロックする欠けたツールや失敗したアサーションなしに、登録された spec/test エンドポイントで必須ストーリーボードを通過するか? | | **Verified (Sandbox)** | 登録された本番エンドポイントは、サンドボックス分離が強制され Sandbox 許容可能なスキップのみが存在する状態で、`account.sandbox: true` の下でサンドボックス検証プロファイルを通過するか? | | **特定バイヤー要件** | このバイヤーはどの verified モード、プロトコル、専門分野、任意ケイパビリティ、運用動作、スキップクラスを要求するか? | バイヤー要件プロファイルは公開バッジより厳格でありえます。例えば、公開 Sandbox プロファイルは `media_buy.supports_proposals: false` のときケイパビリティゲートのプロポーザルストーリーボードがスキップされるのを受け入れられます。プロポーザルワークフローを要求するバイヤーは同じスキップをブロッカーとして扱えます。逆に、`comply_test_controller` を省略する本番エンドポイントは Sandbox バッジに許容可能でも、決定的統合テストをするバイヤーはそれを公開する別の dev/staging エンドポイントを求めるかもしれません。 ストーリーボードランナーはそれらのバイヤー決定をエンコードすべきではありません。検証ポリシーやバイヤープロファイルが一貫して評価できる型付き証拠を発すべきです。 ## 専門分野マニフェスト 各専門分野の `requires_scenarios` フィールドは、ランナーがグレードするシナリオをリストします。例 — `sales-guaranteed` マニフェストは 8 つの必須シナリオを宣言します: ```yaml theme={null} # /compliance/{version}/specialisms/sales-guaranteed/ (source: static/compliance/source/specialisms/sales-guaranteed/index.yaml) id: sales_guaranteed requires_scenarios: - media_buy_seller/refine_products - media_buy_seller/delivery_reporting - media_buy_seller/measurement_terms_rejected - media_buy_seller/pending_creatives_to_start - media_buy_seller/inventory_list_targeting - media_buy_seller/inventory_list_no_match - media_buy_seller/invalid_transitions - media_buy_seller/proposal_finalize # ← capability-gated ``` これらの 7 つは任意の `sales-guaranteed` エージェントに無条件で実行されます。8 つ目 — `proposal_finalize` — はケイパビリティゲートを運びます。 ## ケイパビリティゲート シナリオまたはフェーズは `requires_capability` ブロックを宣言できます。ランナーは `get_adcp_capabilities` レスポンスから名前付きパスを読み、期待される値に対して確認します。チェックが失敗(ケイパビリティが欠如または false)すると、シナリオまたはフェーズはスキップされ — `skip` ブロックが `reason: not_applicable` でランナー出力に現れ — `steps_failed` に寄与しません。 ```yaml theme={null} # /compliance/{version}/protocols/media-buy/scenarios/proposal_finalize/ (source: static/compliance/source/protocols/media-buy/scenarios/proposal_finalize.yaml) id: media_buy_seller/proposal_finalize requires_capability: path: media_buy.supports_proposals equals: true ``` ゲートは、実行時にエージェントのライブ `get_adcp_capabilities` レスポンスに対して評価されます — ランナーが universal `capability_discovery` ストーリーボード中に行うのと同じ呼び出し。 フェーズレベルのゲートは同じ形状を使い、ブロックを宣言するフェーズのみをスコープします。Universal ストーリーボードはこれをプロトコル固有のフェーズファミリーに使います。例えば、決定的テストは、エージェントが `sponsored_intelligence` を宣言しないとき SI セッションフェーズをスキップし、エージェントが宣言するメディアバイまたはクリエイティブフェーズは依然実行します。 ## 実践例 **シナリオ:** Priya の StreamHaus プラットフォームが `sales-guaranteed` を主張し `media_buy.supports_proposals: true` を宣言。 ```json theme={null} { "supported_protocols": ["media_buy"], "specialisms": ["sales-guaranteed"], "media_buy": { "supports_proposals": true } } ``` **ランナー動作:** `proposal_finalize` を含む 8 つすべての `requires_scenarios` が実行される。Priya のプラットフォームは完全なプロポーザルライフサイクル — プロポーザル付きブリーフ、refine、finalize、`create_media_buy` 経由の実行 — でグレードされる。 *** **シナリオ:** StreamHaus Direct はオークションベースの PG プラットフォーム — プロポーザル抽象なし。`sales-guaranteed` を主張し `media_buy.supports_proposals: false` を宣言。 ```json theme={null} { "supported_protocols": ["media_buy"], "specialisms": ["sales-guaranteed"], "media_buy": { "supports_proposals": false } } ``` **ランナー動作:** 7 つのシナリオが実行され、`proposal_finalize` はスキップされる。ランナー出力の `skip` ブロックが権威あるシグナル: ```json theme={null} { "storyboard_id": "media_buy_seller/proposal_finalize", "skip": { "reason": "not_applicable", "detail": "requires_capability check: media_buy.supports_proposals must equal true — agent declared false" } } ``` `skip` ブロックが存在するとき、ステップはグレードされず `steps_failed` にカウントされません。`skip.detail` 文字列が特定の原因(ケイパビリティゲート、欠けている専門分野宣言、欠けているツール)を識別します。 **欠如 = false。** `supports_proposals` フィールドはケイパビリティスキーマで `"default": false` を持ちます。レスポンスから省略することは `false` を宣言するのと等価です — ランナーはケイパビリティゲートのプロポーザルシナリオをスキップします。グレーディングにオプトインするには `true` を明示的に宣言してください。 このフラグはグレーディングゲートのみです。バイヤーエージェントはそれを特定のプロポーザルが実行可能かを決めるのに使うべきではありません。セラーがプロポーザルを返す場合、`proposal_status` が真実の源泉です: `draft` は create の前に finalize を要求、`committed` は `expires_at` の前に実行可能、欠如したステータスはレガシーの ready-to-buy。 ## 一目でのグレーディング判定 | Outcome | Output field | Meaning | | -------- | -------------------------------------------------------------- | --------------------------------------------------------------- | | シナリオ合格 | `skip` なしのステップ結果、`passed: true` | すべての検証が通過 | | シナリオ失敗 | `skip` なしのステップ結果、`passed: false` | 1 つ以上の必須検証が失敗。失敗フィールドと `json_pointer` については `validations[]` を参照 | | シナリオ未選択 | `run_summary.not_selected[].reason`、`steps_not_selected` にカウント | 呼び出し元の選択スイート、実行モード、バージョン、検証プロファイルが実行前にこのシナリオを除外 | | シナリオスキップ | `skip.reason: not_applicable` | シナリオは選択されたが、適用性ゲートが false と評価、例えばセラーが主張しなかった任意ケイパビリティ | | 必須ツール欠如 | `skip.reason: missing_tool` | シナリオは選択されエージェントは専門分野を宣言したが、`required_tools` にリストされたツールを公開しなかった | 実行の全体的なコンプライアンス判定は `steps_failed` によって決定されます。スキップされたステップ(`skip` ブロック存在)と未選択アイテム(`run_summary.not_selected[]` エントリ)はそのカウンターに寄与しませんが、異なることを意味します。`steps_not_selected` は、ランナーがこの実行からそれらのシナリオを意図的に除外したことを言います。`steps_skipped` は、ランナーがそれらのシナリオを選択したが実行できなかったことを言います。任意ケイパビリティ選択を欠けている必須サーフェスから区別するには `skipped_by_reason` と `skip.detail` を使います。 したがってサンドボックス専用実行は、除外された作業のみが選択モード外のとき、こう見えるべきです: ```json theme={null} { "summary": { "steps_passed": 84, "steps_failed": 0, "steps_skipped": 0, "steps_not_selected": 80, "not_selected_by_reason": { "run_mode_excluded": 80 } } } ``` それらの 80 アイテムが代わりに `steps_skipped` の下に現れる場合、それらは選択されてからスキップされた。それは異なるシグナルで `skipped_by_reason` が必要です。 ## 合格対部分カバレッジ ランナーサマリーは **失敗** を **カバレッジ** から区別します: | Run shape | What it means | Seller action | | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------- | | `steps_failed > 0` | 実行したステップでストーリーボードアサーションが失敗 | 対応するプロトコルや専門分野を主張する前にエージェントを修正 | | `steps_failed = 0`、カバレッジギャップスキップなし | 宣言されたスコープの完全合格 | 宣言されたプロトコル、専門分野、ケイパビリティフラグがクリーンにグレードされた | | `steps_failed = 0`、`steps_not_selected > 0` のみ | 要求されたスイートまたは実行モードが一部のプローブを意図的に除外、例えばサンドボックス専用実行中の live-only チェック | 実装依頼なし。実行モードを明確にラベル | | `steps_failed = 0`、ケイパビリティゲート `not_applicable` スキップのみ | より狭い宣言スコープ、失敗でない | エージェントが任意ケイパビリティを正直に辞退、例えば `media_buy.supports_proposals: false` | | `steps_failed = 0`、`missing_test_controller` スキップ | 決定的テストサーフェスのカバレッジギャップ | dev/staging の決定的パスを実行するかスキップされたライフサイクルカバレッジを明示的に公開 | | 宣言されたプロトコルや専門分野の任意の `missing_tool`、`requirement_unmet`、`unsatisfied_contract` スキップ | セラーがランナーが完全にテストできないクレームを宣言 | 欠けているサーフェスを修正するか宣言を狭める | この区別は本番パスのサンドボックス実行に重要です。セラーは、サンドボックスフラグ付きトラフィックの下で実際の本番エンドポイントに対してストーリーボードスイートを実行し **ゼロ失敗** を得ながら、本番エンドポイントが正しく `comply_test_controller` を公開しないため依然として `partial` サマリーを見られます。その結果はこう言います: 「バイヤー可視のサンドボックスパスはランナーがグレードできたすべてのアサーションを通過したが、コントローラーシードのライフサイクルシナリオはスキップされた」。それはサンドボックス準備の有用な証拠ですが、完全な決定的専門分野カバレッジと同じではありません。 完全なカバレッジには、コントローラーを公開する dev または staging エンドポイントに対して同じ宣言スコープを実行するか、必要な状態を事前シードしランナーがシードされた状態のカバレッジをアサートするよう設定します。本番のみのセラーには、バイヤーが何がグレードされ何がされなかったかを正確に見られるよう、スキップされたカバレッジリストをゼロ失敗結果と並んで公開します。 ## 各部品がどこに存在するか | Artifact | URL path | Source | | ------------------ | -------------------------------------------------------------- | --------------------------------------------------------------------- | | 専門分野マニフェスト | `/compliance/{version}/specialisms/{id}/` | `static/compliance/source/specialisms/{id}/index.yaml` | | シナリオ YAML | `/compliance/{version}/protocols/{protocol}/scenarios/{name}/` | `static/compliance/source/protocols/{protocol}/scenarios/{name}.yaml` | | Universal ストーリーボード | `/compliance/{version}/universal/` | `static/compliance/source/universal/` | | ケイパビリティスキーマ | `/schemas/v3/protocol/get-adcp-capabilities-response.json` | `static/schemas/source/protocol/get-adcp-capabilities-response.json` | 完全な専門分野からシナリオへのインデックスは [Compliance Catalog](/docs/building/verification/compliance-catalog) にあります。すべてのスキップ理由と判定形状を定義するランナー出力コントラクトは `static/compliance/source/universal/runner-output-contract.yaml` にあります。 ## 関連 * [適合性仕様](/docs/building/verification/conformance) — 3 層義務モデルと規範的ストーリーボードインデックス * [Compliance Catalog](/docs/building/verification/compliance-catalog) — プロトコル、専門分野、universal ストーリーボードの完全タクソノミー * [エージェントを検証する](/docs/building/verification/validate-your-agent) — `@adcp/sdk` でスイートをローカルで実行 # Storyboards 対 scenarios — どれがどれか Source: https://adcp-docs-ja.pier1.co.jp/docs/building/verification/storyboards-vs-scenarios AdCP の 3 つのものが「scenarios」という言葉を共有し、それらは同じではない。区別する方法。 AdCP エージェントを *どう* テストするかを理解しようとしているなら、このエコシステムの 3 つのものが重なる語彙を共有します。それらは同じではありません。それらを混同すると誤った結論を生みます — 実際にはギャップでないものがプロトコルギャップとしてレポートされる類を含みます。 ## TL;DR | | What it is | Where it lives | Normative? | | -------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------- | | **Storyboards** | ワークフローをエンドツーエンドで定義する YAML ファイル + すべてのワイヤー形状アサーション。**適合性仕様。** | `/compliance/{version}/universal/*.yaml`、`protocols/*/*.yaml`、`specialisms/*/index.yaml` | ✅ Yes | | **`comply_test_controller` scenarios** | ストーリーボードが決定的な状態を駆動できるよう、セラーが公開するプロトコルレベルのツール操作(`force_*`、`simulate_*`、`seed_*`)。 | セラーの `comply_test_controller` MCP ツールの `scenario` パラメーター。 | ✅ Yes | | **`@adcp/sdk/testing/scenarios/*.ts`** | ストーリーボード駆動の `comply()` に先行するレガシー TypeScript テストランナー。 | `node_modules/@adcp/sdk/dist/lib/testing/scenarios/*.js` | ❌ **No** | `node_modules/@adcp/sdk/dist/lib/testing/scenarios/media-buy.js` のファイルを読んで AdCP が何を要求するかを理解しようとしている場合 — **止まってください**。それは仕様ではありません。代わりに [ストーリーボード](/docs/building/verification/conformance) を読んでください。 ## Storyboards(規範的) ストーリーボードは適合性仕様です。それぞれが定義します: * ワークフロー(例: *sales-guaranteed proposal/refine/finalize ライフサイクル*) * バイヤーエージェントが行う正確なツール呼び出し * セラーのレスポンスが満たさなければならないすべてのワイヤー形状アサーション * ランナーが決定的な状態をシードするのに使う `comply_test_controller` 操作 [`conformance.mdx`](/docs/building/verification/conformance) はこれらを *「真実」* と呼び、それを意味します。これらの YAML ファイルは **AgenticAdvertising.org Verified (Spec)** の基礎です。AgenticAdvertising.org は、そのコンプライアンスハートビートが該当するストーリーボードを実行しすべてのアサーションが通過するときその修飾子を発行します。 ストーリーボードは次で見られます: * `/compliance/{version}/universal/*.yaml` — すべての AdCP エージェントが通過しなければならない * `/compliance/{version}/protocols/{protocol}/index.yaml` — プロトコルを主張する者 * `/compliance/{version}/specialisms/{id}/index.yaml` — オプトインクレーム `npx @adcp/sdk@latest storyboard run ` 経由で、または [Addie](https://agenticadvertising.org) を通じてインタラクティブに実行します。 ### ソース権威とロールアウト ストーリーボードの作られた真実の源泉は、`adcontextprotocol/adcp` リポジトリの `static/compliance/source/` です。`scripts/build-compliance.cjs` は開発中に `dist/compliance/latest/` を構築し、リリース時に `dist/compliance/{version}/` をスタンプし、`index.json` やレガシー `domains/` エイリアスのような生成された配布アーティファクトのみを追加します。SDK は、ランナーがオフラインで動作しバージョンピン留めされたままになるようスナップショットをバンドルしてもよいが、そのスナップショットは配布コピーで権威ではありません。 現在の移行中、`@adcp/sdk` はデフォルトで依然としてバンドルされたキャッシュをロードします。仕様リポジトリ CI は、プルリクエストが同じステップでランタイム権威を切り替えずに候補ソースに対してグレードされるよう、training-agent ストーリーボードを実行する前に `static/compliance/source/` をそのキャッシュにオーバーレイします。SDK リリース作業は、バンドルされたキャッシュが出荷を主張する仕様バンドルに一致することを別途証明すべきです。 破壊的または実質的により厳格なストーリーボード変更は、この順序でロールアウトしなければなりません: ```text theme={null} spec storyboard change -> reference implementations update -> @adcp/sdk runner release -> downstream consumers update ``` その順序は、リポジトリ所有のリファレンス実装がそれらを通過できるタグ付けされた実装を持つ前に新しいランナーがより厳格なシナリオを期待するリリースデッドロックを防ぎます。 ## `comply_test_controller` scenarios(規範的 — だが異なる) 「scenario」という言葉は、決定的テストをサポートするためにセラーが公開する `comply_test_controller` MCP ツールにも現れます。ストーリーボードは、実時間が経過するのを待たずにセラー状態を駆動するため、`scenario: ` でこのツールを呼びます: 例えば保留中の非同期メディアバイを終える `force_task_completion`、インプレッションを注入する `simulate_delivery`、フィクスチャをインストールする `seed_product`。 これらは **プロトコル操作** で、テストランナーではありません。[comply\_test\_controller](/docs/building/by-layer/L3/comply-test-controller) で文書化されています。セラーはテスト可能であるためそれらを実装しなければならず(MUST)、ストーリーボードは決定的なままであるためそれらを使わなければなりません(MUST)。 したがって「セラーがそのシナリオをサポートしない」と聞くとき、通常は「セラーの `comply_test_controller` がまだ `force_X` を公開していない」を意味します。「どのストーリーボードも X をテストしない」ではありません。 ## `@adcp/sdk/testing/scenarios/*.ts`(非規範的レガシー) SDK は `testing/scenarios/` の下に TypeScript ファイル — `media-buy.ts`、`signals.ts`、`creative.ts`、その他十数個の兄弟 — を出荷します。これらはストーリーボード駆動の `comply()` エンジンに先行します。それらは: * **適合性仕様ではない。** AdCP が何を要求するかを学ぶためにそれらを読むと誤った答えを生みます。 * **ストーリーボードとロックステップで保守されていない。** シナリオファイルは、ストーリーボード同等物がとうに訂正したパラメーターをハードコードしているかも。*例: `scenarios/media-buy.ts` は 4 つの呼び出しサイトで `buying_mode: 'brief'` をハードコードします。`sales-guaranteed` の YAML ストーリーボードは `proposal_finalize` ストーリーボード経由で `buying_mode: 'refine'` + `action: 'finalize'` を実行します。両方とも有効な `buying_mode` 値です。シナリオファイルはライフサイクルをカバーしていないだけです。* * **呼び出し可能だが、異なる目的で。** SDK の内部スモークテスト、下流ツールの統合テストフィクスチャ、一部のレガシーパスは依然としてこれらを使います。それらは AgenticAdvertising.org Verified (Spec) が対して実行するものではありません。 AdCP を理解するために `testing/scenarios/*.ts` を grep している自分に気づいたら、間違った角を曲がりました。正しいパス: 1. 宣言した専門分野に一致するストーリーボードを見つける: `npx @adcp/sdk@latest storyboard list` 2. YAML を読む: `npx @adcp/sdk@latest storyboard show ` 3. 実行する: `npx @adcp/sdk@latest storyboard run --agent-url ` ## 3 行デコーダー | 次を見たら… | …見ているものは | 仕様として信頼? | | ----------------------------------------------------------- | ---------------- | -------------------------------------------------- | | `/compliance/{version}/...` の下の `.yaml` | ストーリーボード | ✅ Yes | | `scenario` パラメーターを伴う `comply_test_controller` に応答するセラー | プロトコルレベルのテスト制御操作 | ✅ Yes(コントラクトは規範的。セラーは `UNKNOWN_SCENARIO` で拒否してもよい) | | `@adcp/sdk/dist/lib/testing/scenarios/` の下の `.ts` または `.js` | レガシー SDK テストランナー | ❌ No | ## クロスリポジトリ SDK 自体の曖昧性解消作業 — レガシーシナリオを `@deprecated` とマーク、ストーリーボード CLI 動詞をミラー、またはエクスポートを完全に削除 — は [`adcp-client`](https://github.com/adcontextprotocol/adcp-client) にあります。`testing/scenarios/*` が public-but-deprecated のままか internal-only になるかの決定は [#4035](https://github.com/adcontextprotocol/adcp/issues/4035) で追跡されます。 今のところ: AdCP エージェントをテストしたいなら、答えはストーリーボードです。「scenario」と名付けられた他の 2 つのものには場所がありますが、それではありません。 # モックアップストリームフィクスチャでアダプターエージェントを検証する Source: https://adcp-docs-ja.pier1.co.jp/docs/building/verification/validate-with-mock-fixtures AdCP アダプターエージェントのプレステージングゲート — 公開されたモックアップストリームとトラフィックカウンターが、ステージングテスト前に統合ギャップを表面化する。 **非規範的。** このページは補完的なハーネスパターンを説明するもので、コンプライアンス階層ではありません。このレシピはストーリーボードランナー([エージェントを検証する](/docs/building/verification/validate-your-agent))の上に構築されます — ストーリーボードやステージング統合テストを置き換えるものではなく、認定ゲートでもありません。ここで説明するモックフィクスチャの規約(`/_lookup/`、`/_debug/traffic`)は `@adcp/sdk` のリファレンス実装です。代替 SDK 実装は分岐しうる。プロトコルコントラクトではなくハーネス規約として扱ってください。 外部プラットフォーム(DSP、SSP、リテールデータウェアハウス、クリエイティブサーバー、シグナルマーケットプレイス)をラップするほとんどの AdCP エージェントは、ストーリーボードだけでは検出できないファサードバグを出荷します: ハンドラーは、実際にアップストリームと統合することなく形状的に有効な AdCP レスポンスを返します。このページのレシピはそのギャップを **プレステージングゲート** として閉じます — 安価、高速、CI で実行され、コードがステージングテナントに到達する前にファサードとコントラクトドリフトを表面化します。 エージェントがアップストリームをラップしない場合 — 例えば自身のデータを所有する純粋な意思決定サービス — ストーリーボードランナー単独で十分です。[エージェントを検証する](/docs/building/verification/validate-your-agent) を参照してください。 ## 4 ステップのレシピ ```bash theme={null} # 1. あなたの専門分野用のモックアップストリームをブートする npx @adcp/sdk@latest mock-server sales-social --port 4250 & # 2. http://localhost:4250 をアップストリームとして使うよう設定した AdCP エージェントを実行 ./your-agent.sh # Python / Go / Rust / TS — 言語非依存 # 3. 該当ストーリーボードに対してエージェントをグレードする npx @adcp/sdk@latest storyboard run http://localhost:3001/mcp sales_social \ --auth $TOKEN --json > grader.json # 4. エージェントが実際にアップストリームを呼んだことをアサート curl -s http://localhost:4250/_debug/traffic # {"traffic": {"POST /oauth/token": 1, "POST /event/track": 6, ...}} ``` ステップ 3 は AdCP ワイヤーバグ(レスポンス形状、エラーコード、冪等性、欠けている必須フィールド)を捕捉します。ステップ 4 は **統合ギャップ** を捕捉します — アップストリームの主要エンドポイントを行使することなく形状的に有効な AdCP レスポンスを返すハンドラー。両方のシグナルが重要で、片方だけでは不完全です。 上に示した mock-server CLI は `@adcp/sdk`(TypeScript)で出荷されます。CI が別の言語で動く場合、最もシンプルなパターンは TS CLI をサイドカーとして実行することです(例: GitHub Actions の Docker サービスコンテナ、または別の Node プロセス) — テスト対象のエージェントはネイティブ言語のままで、モック + ストーリーボードランナーだけが TS です。自身の mock-server を出荷する Python や Go SDK は下記の規約を再実装することになります。それが到達するまでは、TS CLI がリファレンスです。 ## なぜトラフィックカウンターか ストーリーボードは AdCP ワイヤーコントラクトをチェックします: レスポンスはスキーマに一致したか、エージェントは正しいツールをアドバタイズしたか、コンテキストはエコーしたか。それらはワイヤーの *背後* で何が起きたかをアサートしません。このリポジトリの CLAUDE.md ガイダンスは直截に述べています: **ストーリーボードはアサーションであり、真実ではない。** 形状のみの検証の下では正しく見えるがアップストリームを完全にスキップするアダプターは、これまでに一度ならずステージングに出荷されています。よくある形: * 入力が非仕様分岐に一致しないとき、ハンドラーがアップストリーム呼び出しの前に短絡する(例: `sync_audiences` の空 `members[]` → 形状的に有効な空レスポンスを返し、決して POST しない)。 * OAuth クライアントは配線されているがどのハンドラーからも呼ばれない。ツリーシェイキングは `void fetchUpstreamToken;` リテラルで無効化されている。 * アップストリームの必須フィールドスキーマを満たすため合成プレースホルダーデータが注入され、実データの形状不一致を隠す。 アップストリーム上のトラフィックカウンターは、最初の 2 ケースを無条件に捕捉し、ストーリーボードが該当するペイロードの多様性を行使する場合に 3 番目のケースを捕捉します。これを CI に **プレステージングゲート** として配置してください: 安価、決定的、既存のステージングテストを置き換えるのではなく補完します。 ## 利用可能なもの リファレンス TS 実装(`@adcp/sdk`)は、異なるアップストリーム表面形状をカバーする 4 つの mock-server 専門分野を出荷します。これらは網羅的ではありません — 代表的な認証/テナンシー/ペイロードパターンを行使するために存在し、他の専門分野は最も近い一致を再利用します。 | Specialism | Mimics | Auth | Multi-tenant scope | | -------------------- | ---------------------------------- | ------------------------------------------ | ------------------------------------------- | | `signal-marketplace` | LiveRamp / Lotame / データマーケットプレイス | Static Bearer | Header (`X-Operator-Id`) | | `creative-template` | Celtra / Innovid クリエイティブ管理プラットフォーム | Static Bearer | Path (`/v3/workspaces/{ws}/…`) | | `sales-social` | TikTok / Meta 型ソーシャル広告プラットフォーム | OAuth 2.0 client\_credentials with refresh | Path (`/v1.3/advertiser/{advertiser_id}/…`) | | `sales-guaranteed` | GAM / FreeWheel 保証型セールスプラットフォーム | Static Bearer | Header (`X-Network-Code`) | 認証の形とテナンシーパターンは現実的です。アダプターが受け取る具体的なアカウントフィールド名(例: `account.advertiser`、`account.operator`)は、AdCP リクエストをアップストリームテナントにバインドするための SDK 規約であり、規範的な AdCP 用語ではありません。正準マッピングについては `@adcp/sdk` ソースを参照してください。 各モックは以下を公開します: * **アップストリームのドメインエンドポイント** — 実プラットフォームの公開コントラクトに一致するよう形作られている。 * **`GET /_lookup/?=`** — AdCP 側の識別子からアップストリームテナント ID へのランタイム解決。*ハーネス規約。* * **`GET /_debug/traffic`** — ` ` でキーされたヒットカウンター、認証なし、ハーネス専用。*ハーネス規約。* ストーリーボード実行後に読み、各主要ルートが少なくとも 1 回ヒットしたことをアサートする。 各モックの OpenAPI 仕様は SDK パッケージの一部として出荷されます。アダプターを特定のシードデータではなく仕様に対して参照してください — シードは変動し、コントラクトの一部ではありません。 ## CI 統合 GitHub Actions ジョブのリファレンス形状、言語非依存: ```yaml theme={null} jobs: validate-adapter: runs-on: ubuntu-latest services: mock-upstream: image: node:20 ports: ['4250:4250'] # `npx @adcp/sdk@latest mock-server ` を実行するブートストラップスクリプトを使う。 steps: - uses: actions/checkout@v4 - run: ./scripts/start-agent.sh & # あなたの言語のエージェント - run: ./scripts/wait-for-port.sh 3001 - run: | npx @adcp/sdk@latest storyboard run http://localhost:3001/mcp \ --auth $TOKEN --json > grader.json - run: | # 期待される各アップストリームルートが少なくとも 1 回ヒットしたことをアサート curl -s http://localhost:4250/_debug/traffic | \ jq -e '.traffic["POST /oauth/token"] >= 1 and .traffic["POST /event/track"] >= 1' ``` **しきい値ガイダンス。** 最小限有用なアサーションは主要ルートごとに `≥ 1` です — それはハンドラーがアップストリームに到達したことを証明します。より強いアサーション(ルートごとのカウント、個別ペイロード検証)はストーリーボードのペイロード期待をエンコードする必要があり、プレステージングゲートでは保守負担に値しません。ストーリーボードが 3 つのオーディエンスアップロードを行使する場合、`custom_audience/upload` へ 3 ヒットを期待してください。そうでない場合、引くべきレバーはストーリーボードのペイロードカバレッジであって、ゲートのしきい値ではありません。 複数の専門分野を主張するエージェントについては、専門分野ごとに CI ジョブを並列で 1 つ実行してください。各ジョブは自身の mock-server ポートペア(エージェント + アップストリーム)を得ます。ジョブは独立しています。 ## 反復ループ 現実的には最初の実行では両方のゲートを通過しないでしょう。よくある形とデバッグ方法: * **ストーリーボードが `passing` だが N エンドポイントでトラフィックゲートが失敗** — 典型的なファサード。それらのルートのハンドラーは短絡したか、ストーリーボードの入力によって行使されなかった。 * **カスケードスキップを伴うストーリーボード `partial`** — 早期ステップ(`get_products`、`get_signals`)が、ランナーが状態を抽出するフィールドを欠いた形状的に有効なレスポンスを返した。下流ステップは `unresolved context variables` でスキップする。早期ステップのレスポンス形状を修正すればほとんどのカスケードスキップはクリアされる。 * **単一ステップでストーリーボード `failing` + トラフィックゲートはクリーン** — 通常は 1 行の形状バグ(誤ったフィールド名、欠けている必須フィールド、ステータス不一致)。ステップごとの `details` が JSON ポインターでフィールドを名指す。 * **トラフィックゲートが空(どこでも 0 ヒット)+ エージェントが起動しているように見える** — エージェントのブートがポートでリッスンした後に回復可能なエラーをスローした。エージェントの stderr を確認する。 最速のデバッグループ: `npx @adcp/sdk@latest storyboard step ` を使って失敗ステップを分離する。カスケードをスキップし、単一のツール呼び出しを実行し、サブ秒のフィードバック。分離したステップが通過するまで完全なストーリーボードを実行しないでください — 反復ごとに数分節約できます。 ## 制限 これらのゲートが何を捕捉し何を捕捉しないかについて、チームに正直であってください: ### ストーリーボードの制限 * **ストーリーボードはペイロードの多様性をアンダーカバーする。** ストーリーボードステップは、実際のアダプターが決して行使されない空入力で形状を通過するかもしれない — 重要なバリアントで。[adcontextprotocol/adcp#3785](https://github.com/adcontextprotocol/adcp/issues/3785) で追跡。 ### ランナー / ツーリングの注意点 * **ストーリーボードは黙ってカスケードスキップする** — 早期ステップのレスポンスが形状的に有効だがランナーが状態を抽出するフィールドを欠くとき。表示されるエラーは *下流* ステップにあり、早期ステップではありません — "failed" の前に "skipped" ステップを調査してください。[adcontextprotocol/adcp#3796](https://github.com/adcontextprotocol/adcp/issues/3796)(ランナー側)で追跡。 * **モックシードデータがストーリーボードフィクスチャ入力に一致しないかもしれない。** `_lookup/` で 404 が見えたら、ストーリーボードのペイロードはモックがシードしない ID を参照しているかもしれません。モックのシードを広げるか、[コンプライアンステストコントローラー](/docs/building/by-layer/L3/comply-test-controller) 経由でランタイムにシナリオ状態をシードしてください。 ### トラフィックゲートの制限 * **必要だが十分ではない。** ハンドラーは合成プレースホルダーデータでアップストリームを呼び、なおヒットカウントアサーションを満たしうる。規制されたチャネル(オーディエンスアップロード、コンバージョントラッキング、署名付きリクエスト)のエージェントについては、本番前に実アップストリームのペイロード検証に対する追加の統合テストが依然として必要です。 * **冪等性リプレイはトラフィックカウンターで行使されない。** `idempotency_key` を無視しアップストリーム書き込みを二重化するファサードはヒットカウントゲートを通過します。プラットフォームが at-most-once セマンティクスを持つ場合、ストーリーボードの冪等性リプレイシナリオ + 別のカウンターチェック(同じ `idempotency_key` → 同じヒットカウント)を使ってください。 * **アウトバウンド webhook 配信はアップストリームトラフィックカウンターで行使されない。** トラフィックカウンターはエージェントが呼び *込む* アップストリーム上に存在します。エージェント → バイヤー webhook の署名/配信は、ストーリーボードランナーの `--webhook-receiver` フラグで別途グレードされます。webhook を発行するアダプターには両方のゲートが適用されます。 ## 次は何か * **[エージェントを検証する](/docs/building/verification/validate-your-agent)** — より広範なストーリーボードランナー駆動の検証チェックリスト(ファズ、マルチインスタンス、リクエスト署名、webhook 適合性)。 * **[コンプライアンステストコントローラー](/docs/building/by-layer/L3/comply-test-controller)** — ストーリーボードがモックの提供しないフィクスチャを必要とするとき、ランタイムにシナリオ状態をシードする。 * **[エージェントをビルドする](/docs/building/by-layer/L4/build-an-agent)** — AdCP エージェントをビルドするための言語非依存ガイド。 * **[コンプライアンスカタログ](/docs/building/verification/compliance-catalog)** — universal / protocol / specialism ストーリーボードの完全な分類。 # ストーリーボードを使ってエージェントを検証する Source: https://adcp-docs-ja.pier1.co.jp/docs/building/verification/validate-your-agent AdCP エージェントをストーリーボードでテストする — CLI から、または Addie を通じて。 エージェントが実行されたら、本番稼働前に検証してください。ストーリーボードは特定のワークフローをエンドツーエンドで行使します — メディアバイ作成、クリエイティブ同期、シグナルディスカバリー。各ストーリーボードは、バイヤーエージェントが行う正確なツール呼び出しシーケンスを定義し、すべてのレスポンス形状を検証します。 ストーリーボードはコマンドラインから、また [Addie](https://agenticadvertising.org) を通じてインタラクティブに利用できます。それらはスキーマと並んで `/compliance/{version}/` でも公開され、`/protocol/{version}.tgz` のバージョンごとのプロトコル tarball にバンドルされています — オフラインで取得する方法については [スキーマと SDK](/docs/building/by-layer/L0/schemas#one-shot-protocol-bundle) を参照してください。 `@adcp/sdk` パッケージは、`testing/scenarios/*`(例: `media-buy.ts`、`signals.ts`)下のレガシー TypeScript テストランナーもエクスポートします。これらは `comply()` に先行し、適合性仕様では **ありません**。AdCP が何を要求するかを学ぶためにそれらのファイルを grep している自分に気づいたら、どの表面が規範的かについて [Storyboards 対 scenarios](/docs/building/verification/storyboards-vs-scenarios) を参照してください。 **アップストリームプラットフォームをラップ**(DSP、SSP、リテールデータウェアハウス、クリエイティブサーバー、シグナルマーケットプレイス)していますか? ストーリーボードは AdCP ワイヤーコントラクトをチェックします。ワイヤーの背後のアダプターが実際にアップストリームと統合するか、合成データで形状有効なレスポンスを返すかを判別できません。[モックアップストリームフィクスチャでアダプターエージェントを検証する](/docs/building/verification/validate-with-mock-fixtures) を参照してください — 公開されたモックフィクスチャとトラフィックカウンターが、任意の言語のアダプターにファサード耐性のあるコンプライアンスを与えます。 ## Storyboard taxonomy ストーリーボードは 3 つの層に編成され、エージェントは実際にサポートするものだけを宣言します: | Layer | Path | 通過しなければならない者 | | -------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | **Universal** | `/compliance/{version}/universal/` | すべての AdCP エージェント(ケイパビリティディスカバリー、エラー処理、スキーマ検証) | | **Protocol** | `/compliance/{version}/protocols/{protocol}/` | プロトコルを主張する任意のエージェント(`media-buy`、`creative`、`signals`、`governance`、`brand`) | | **Specialism** | `/compliance/{version}/specialisms/{id}/` | オプトインクレーム(例: `sales-guaranteed`、`sales-broadcast-tv`、`creative-generative`) — [コンプライアンスカタログ](/docs/building/verification/compliance-catalog) を参照 | `get_adcp_capabilities` で `supported_protocols` と `specialisms` を宣言してください — ランナーは一致するストーリーボードを自動的に選びます。完全な分類については [コンプライアンスカタログ](/docs/building/verification/compliance-catalog) を参照してください。 ## セットアップ 名前でエージェントを参照できるよう、名前付きエイリアスとして保存します: ```bash theme={null} npx @adcp/sdk@latest --save-auth my-agent http://localhost:3001/mcp ``` これはエイリアスを `~/.adcp/config.json` に保存します。これは一度だけ行えば十分です。組み込みエイリアス `test-mcp` と `test-a2a` は公開テストエージェントを指します — セットアップ不要です。 エイリアスの代わりに URL を直接渡すこともできます: `npx @adcp/sdk@latest storyboard run http://localhost:3001/mcp media_buy_seller` ## ストーリーボードを実行する ### 1. 利用可能なストーリーボードをリストする ```bash theme={null} npx @adcp/sdk@latest storyboard list ``` 各ストーリーボードは特定のエージェントタイプをターゲットします。[エージェントをビルドする](/docs/building/by-layer/L4/build-an-agent) ページはスキルを一致するストーリーボードにマップします。 ### 2. ストーリーボードが何をテストするかをプレビューする ```bash theme={null} npx @adcp/sdk@latest storyboard show media_buy_seller ``` これは何も実行せずにフェーズ、ステップ、検証を表示します。 ### 3. ストーリーボードを実行する ```bash theme={null} npx @adcp/sdk@latest storyboard run my-agent media_buy_seller ``` 出力は各ステップを pass/fail で表示します: ``` media_buy_seller (9 steps) ✓ get_adcp_capabilities ✓ sync_accounts ✓ get_products ✓ create_media_buy ✓ list_creative_formats ✓ sync_creatives ✓ list_creatives ✓ get_media_buy_delivery ✓ provide_performance_feedback 9/9 passed ``` 機械可読な結果には `--json` を渡します。各ステップの完全なリクエスト/レスポンスペイロードを見るには `--debug` を渡します。 ### 4. 失敗するステップをデバッグする ステップが失敗する場合、それを個別に実行します: ```bash theme={null} npx @adcp/sdk@latest storyboard step my-agent media_buy_seller create_media_buy --json --debug ``` 早期ステップからの状態(アカウント ID、製品 ID)を提供するには `--context` を渡します: ```bash theme={null} npx @adcp/sdk@latest storyboard step my-agent media_buy_seller get_products \ --context '{"account_id":"acct-123"}' --json ``` ### 5. すべてのストーリーボードを実行する すべてをテストするにはストーリーボード ID なしで実行します。CLI は `tools/list` 経由でエージェントのツールを発見し、一致するストーリーボードを自動的に選びます: ```bash theme={null} npx @adcp/sdk@latest storyboard run my-agent ``` 構造化出力には `--json` を追加します。 ストーリーボードランナーは、エージェントがオプションの [コンプライアンステストコントローラー](/docs/building/by-layer/L3/comply-test-controller) を実装するかどうかに応じて 2 つのモードで動作します: | Mode | When | テストするもの | | ----------------- | ------------ | ------------------------------ | | **Observational** | テストコントローラーなし | レスポンススキーマとバイヤー開始フロー | | **Deterministic** | テストコントローラー存在 | 完全なライフサイクルステートマシン、エラーコード、操作ゲート | ### `partial` を読む `partial` はカバレッジ結果であり、自動的に失敗ではありません。ランナーは、実行されたアサーションが失敗しなかったにもかかわらず 1 つ以上の選択されたシナリオがグレードできなかったときにそれを使います。最も一般的な原因は意図的です: 本番エンドポイントは `comply_test_controller` を公開してはならない(MUST NOT)ため、コントローラーがシードまたは強制するフェーズは `missing_test_controller` でスキップします。 `steps_not_selected` を `steps_skipped` と別に読んでください。未選択のシナリオは、あなたが要求したスイートまたは実行モードの外側にありました。スキップされたシナリオは選択されたスイートの内側にありましたが、適用性ゲート、欠けているテスト表面、欠けているツール、前提条件のためランナーがそれらを実行できませんでした。 結果が何を意味するかを決めるには、サマリーカウンターとスキップ理由を使ってください: | Summary | 解釈 | | ------------------------------------------------------- | --------------------------------------------- | | `0 failed`、`steps_not_selected > 0`、`steps_skipped = 0` | 選択された実行モードに期待される。ライブ専用プローブを除外するサンドボックス専用テストなど | | `0 failed`、ケイパビリティゲートの `not_applicable` スキップのみ | セラーが宣言したケイパビリティスコープのクリーンな通過 | | `0 failed`、`missing_test_controller` スキップ | バイヤー可視パスは通過したが、決定的ライフサイクルカバレッジはグレードされなかった | | 宣言されたプロトコルまたは専門分野の `missing_tool` スキップ | セラーが過剰主張したか、必須ツールの公開を忘れた | | 任意の failed ステップ | 修正されるまで宣言されたスコープに非適合 | 本番パスのサンドボックス検証では、`84 passed, 0 failed, 0 skipped, 80 not selected` のような結果はクリーンなサンドボックス専用選択結果です: 除外されたプローブはその実行の一部ではありませんでした。`84 passed, 0 failed, 80 skipped` のような結果は、何かを意味する前にスキップ内訳が必要です。オプションケイパビリティが主張されなかったためのスキップは選択スコープスキップです。`missing_test_controller` のスキップは決定的カバレッジギャップです: スイートは公開サンドボックスパスをテストし、コントローラーがシードするライフサイクルシナリオがグレードされなかったとレポートしました。それらを完全にグリーンにするには、コントローラーを公開する開発/ステージングエンドポイントに対して実行するか、必須状態を事前シードしランナーにシード状態カバレッジをアサートするよう伝えるか、スキップされたカバレッジリストを明示的な制限として受け入れ公開してください。 ## Addie を通じて検証する [Addie](https://agenticadvertising.org) は、CLI セットアップなしにインタラクティブなテストを提供します。任意の会話にエージェント URL を貼り付けて始めてください。 ### 接続性チェック Addie にエージェントをチェックするよう頼んでください。オンラインであることを検証し、アドバタイズされたツールをリストし、トランスポートプロトコル(MCP または A2A)を確認します。これは任意のテストを実行する前にエージェントが到達可能であることを確認する最速の方法です。 ### ストーリーボードコーチング Addie は CLI と同じストーリーボードを実行しますが、各ステップをインタラクティブに案内します。ステップが失敗すると、何が間違ったかを説明し、期待対実際のレスポンスを表示し、具体的なコード変更を提案します。これは構築中に反復する最速の方法です。 ### RFP テスト 実際の RFP またはキャンペーンブリーフを Addie と共有してください。それを解析し、バイヤーの実際の要件でエージェントの `get_products` を呼び、結果をあなたのセールスチームが通常提案するものと比較します。これは、エージェントが実際のバイヤー需要を処理できるか — あなた自身の在庫記述から派生した合成ブリーフだけでなく — をテストします。 ### IO 実行テスト インサーションオーダーを Addie と共有してください。ラインアイテムを抽出し、エージェントの製品カタログに対して照合し、`create_media_buy` がディールを実行できるかをテストします。出力はライン単位の照合品質(exact、close、weak、unmapped)とレート比較を表示するため、実行がどこで破綻するかを正確に見られます。 ### 推奨テストシーケンス 1. **接続性** — エージェントはオンラインか? 2. **ストーリーボード** — プロトコルコンプライアンスを通過するか? 3. **RFP テスト** — 実際のバイヤー需要に応答できるか? 4. **IO 実行** — 実際のディールをクローズできるか? 各ステップが信頼を構築します。ストーリーボードはプロトコルコンプライアンスを証明します。RFP と IO テストはビジネス準備性を証明します。 ## サンドボックスモード すべてのストーリーボード実行はデフォルトでサンドボックスモードを使います。ストーリーボードランナーはすべてのアカウント参照に `sandbox: true` を設定するため、エージェントは実プラットフォーム呼び出しや支出なしにリクエストを処理します。 エージェントは `get_adcp_capabilities` でサンドボックスサポートを宣言すべきです: ```json theme={null} { "account": { "sandbox": true } } ``` リクエストがサンドボックスアカウントを参照するとき、エージェントは本番状態を永続化したり実世界の副作用を引き起こしてはなりません(MUST NOT) — 実オーダーなし、実課金なし、実広告プラットフォーム API 呼び出しなし。シミュレートされたデータで現実的なレスポンス形状を返し、成功レスポンスに `sandbox: true` を含めてください。 完全な実装詳細と 2 つのアカウントモデルパス(暗黙対明示)については [サンドボックスモード](/docs/media-buy/advanced-topics/sandbox) を参照してください。 ## Verifying cross-instance state プロトコルは、`(brand, account)` スコープの状態が [エージェントプロセスインスタンス間で生き残る](/docs/protocol/architecture#state-persistence-and-horizontal-scaling) こと — あるレプリカで作成されたメディアバイが他の任意のレプリカから読めること — を要求します。単一インスタンスのストーリーボード成功はそれ自体でその不変条件を証明しません。デプロイに合った検証アプローチを選んでください。 **アーキテクチャで検証する。** 共有データストアを持つマネージドサーバーレスプラットフォーム — Lambda + DynamoDB、Cloudflare Workers + D1、Cloud Run + Firestore、Vercel + Neon — で実行する場合、不変条件は構造上成り立ちます。デプロイされたエンドポイントに対して通過するストーリーボードで十分です。発見可能なようストレージパターンを文書化してください。 **マルチインスタンステストで検証する。** 長期実行プロセス(コンテナ、VM、ロードバランサーの背後の古典的アプリサーバー)をデプロイする場合、ラウンドロビンルーティングの背後に 2 つ以上のレプリカを置き、共有エンドポイントに対してストーリーボードを実行します: ```bash theme={null} npx @adcp/sdk@latest --save-auth my-agent https://my-agent.example/mcp npx @adcp/sdk@latest storyboard run my-agent ``` コンプライアンスランナーは、`stateful: true` とマークされたステップを含む任意のストーリーボード — インプロセス状態を捕捉する可能性が最も高い write→read シーケンス — についてレプリカ間でリクエストをローテートします。ステートレスプローブ(ケイパビリティディスカバリー、認証拒否、スキーマ検証)は影響を受けません。 典型的な失敗は次のようになります: ``` ✗ get_media_buy MEDIA_BUY_NOT_FOUND create_media_buy on replica A returned media_buy_id=mb_abc123 (status: active) get_media_buy on replica B returned MEDIA_BUY_NOT_FOUND for the same id → Brand-scoped state is not shared across replicas. ``` **独自のテストで検証する。** 実データストアに対するプロパティベーステスト、レプリカ間のカオス障害注入、またはインスタンス間で書き込みと読み取りを相関させる本番可観測性はすべて有効です。プロトコルは方法論ではなく不変条件を気にします。 インサーションオーダー承認レコード、ガバナンストークン、シグナルアクティベーション、スポンサードインテリジェンスセッションはすべて同じルールの下にあります。後の呼び出しが読み返せる任意の書き込み状態は、プロセスごとの `Map` やモジュールレベル変数ではなく、共有ストアに存在しなければなりません。 ## Preparing to test uniform error responses [統一レスポンス MUST](/docs/building/by-layer/L3/error-handling#standard-error-codes) は、「id は存在するが呼び出し元がアクセス権を欠く」と「id が存在しない」について、すべての観測可能チャネル — エラーボディ、トランスポートステータス、ヘッダー、副作用、テレメトリー — 全体でバイト等価なレスポンスを要求します。これを検証するには、ツールごとに 2 つのレスポンスを比較するペア化プローブランナー(`adcp fuzz`)が必要です。ランナーは 2 つのモードを持ち、強いモードを行使する前にテナントセットアップを計画する必要があります。 **ベースラインモード — 単一テナント。** 1 つの認証トークン、ツールごとにプローブされる 2 つの新しい UUID。エラーボディの id エコー、allowlist 外のヘッダー分岐、MCP `isError` / A2A `task.status.state` 分岐、大まかなレイテンシーデルタを捕捉。どちらのプローブも実リソースに解決しないため、クロステナント存在リークは捕捉できません。 **クロステナントモード — 2 テナント。** テナント A がリソース(例: プロパティリスト、コンテンツ標準、メディアバイ、クリエイティブ)をシード。テナント B がシードされた id と新しい UUID に対してプローブ。ベースラインが構築できない `(exists, unauthorized)` 対 `(does not exist)` ペアを行使するため、完全な MUST を捕捉します。 両方のモードが仕様 MUST を行使します。クロステナントパスのみが不変条件全体を検証します。 ### 最小テナントセットアップ エージェントに対して 2 つの分離されたテストアカウントをプロビジョンします: * **テナント A** — 不変条件がシードするリソース(プロパティリスト、コンテンツ標準、メディアバイ、クリエイティブ)を作成できる。サンドボックスモードアカウントで問題ない。 * **テナント B** — 共有ディスカバリー表面に対して読み取り専用。プラットフォームがグローバルに可視にするもの(例: 公開された製品カタログ)を超えて A とテナントごとの状態を共有してはならない(MUST NOT)。 2 つのテナントが共有する他のもの — 監査シャード、リソースタイプでキーされたレート制限バケット、キャッシュタグ — は不変条件が捕捉するよう設計された潜在的サイドチャネルです。本番で共有するものだけを共有してください。 ### ランナー呼び出し ```bash theme={null} # クロステナント(完全な MUST) npx @adcp/sdk@latest fuzz my-agent \ --auth-token $TENANT_A_TOKEN \ --auth-token-cross-tenant $TENANT_B_TOKEN # ベースライン(部分カバレッジ) npx @adcp/sdk@latest fuzz my-agent --auth-token $TOKEN ``` トークンは `ADCP_AUTH_TOKEN` と `ADCP_AUTH_TOKEN_CROSS_TENANT` 経由でも供給できます。完全なフラグリスト、ヘッダー allowlist、現在プローブされるツールのリストについては [`@adcp/sdk` 統一エラーレスポンス不変条件ガイド](https://github.com/adcontextprotocol/adcp-client/blob/main/docs/guides/VALIDATE-YOUR-AGENT.md#uniform-error-response-invariant-paired-probe) を参照してください。 ### 1 テナントだけでテストする 2 つ目のテナントをまだプロビジョンしていない場合、とにかくベースラインを実行してください — 依然として意味のあるクラスのリークを捕捉し、CLI は実行をベースライン専用としてフラグするためオペレーターはカバレッジが部分的であることを見られます。単一テナントの fuzz を適合性シグナルではなく事前チェックとして扱ってください: クリーンなベースライン実行は MUST が成り立つことを証明しません。統一レスポンス適合性を主張する前にクロステナントレッグを追加してください。 ## build-validate-fix ループ 典型的な開発ワークフロー: 1. **Build** — コーディングエージェントを [スキルファイル](/docs/building/by-layer/L4/build-an-agent) に向けてエージェントを生成 2. **Run** — エージェントをローカルで起動(`npx tsx agent.ts`) 3. **Validate** — 一致するストーリーボードを実行(`npx @adcp/sdk@latest storyboard run my-agent media_buy_seller`) 4. **Fix** — 任意の失敗に対処(欠けているフィールド、誤ったステータス値、無効な遷移) 5. **Repeat** — すべてのステップが通過するまでストーリーボードを再実行 6. **Full check** — 本番稼働前に完全な評価のため `npx @adcp/sdk@latest storyboard run my-agent`(ストーリーボード ID なし)を実行 [Practitioner 認定](https://agenticadvertising.org/certification) では、ストーリーボード検証の通過が集大成です — それはエージェントが選択したロールトラックの完全なプロトコルワークフローを処理することを証明します。 ## CLI リファレンス | Command | Description | | ---------------------------------------------------------- | ------------------------------- | | `npx @adcp/sdk@latest storyboard list` | 利用可能なすべてのストーリーボードをリスト | | `npx @adcp/sdk@latest storyboard show ` | ストーリーボード構造をプレビュー | | `npx @adcp/sdk@latest storyboard run [id]` | 1 つのストーリーボードを実行、ID がなければ一致するすべて | | `npx @adcp/sdk@latest storyboard step ` | 単一ステップを実行 | | `npx @adcp/sdk@latest [tool] [payload]` | 任意のツールを直接呼び出す | | `npx @adcp/sdk@latest --save-auth ` | エージェントエイリアスを保存 | | `npx @adcp/sdk@latest --list-agents` | 保存されたエイリアスをリスト | すべてのコマンドは `--json`、`--debug`、`--auth TOKEN`、`--protocol mcp|a2a` をサポートします。 ## ストーリーボードが失敗するとき * **[ストーリーボードのトラブルシューティング](/docs/building/operating/storyboard-troubleshooting)** — 根本原因と修正にマップされたエラーパターン(欠けているフィクスチャ、署名チャレンジ、エンベロープドリフト、コンテキストエコー、ケイパビリティ不一致) * **[既知の仕様曖昧性](/docs/building/cross-cutting/known-ambiguities)** — 適合性に影響するオープンな仕様ギャップ、回避策と issue リンク付き ## 次は何か * **[コンプライアンステストコントローラー](/docs/building/by-layer/L3/comply-test-controller)** — 完全なライフサイクルカバレッジのため決定的テストを実装 * **[タスクライフサイクル](/docs/building/by-layer/L3/task-lifecycle)** — ステータス値、遷移、ポーリング * **[エラー処理](/docs/building/by-layer/L3/error-handling)** — エラーカテゴリー、コード、リカバリー # AdCP 入門 Source: https://adcp-docs-ja.pier1.co.jp/docs/intro AdCP はオープンなエージェント広告標準です。Alexのチームが断片化した状態から、プロトコルのすべてのドメインにまたがる統合ワークフローへと移行する様子を追います。 Alexが乱雑なスクリーンと絡まるケーブルが並ぶ作戦室で腕を組んで立ち、混乱した状況を見渡しています。後ろではチームメンバーがラップトップに向かっている Alexは Pinnacle Agency でメディアオペレーションを担当しています。彼女のチームは CTV、ディスプレイ、オーディオ、ソーシャル、リテールメディア、デジタル屋外広告という六つのチャネルで購入を行っています。チャネルごとに独自の購入方法、独自の用語、クリエイティブ・ターゲティング・レポートの独自の処理方式があります。一部は IO、他は API、プログラマティックは DSP、すべてにダッシュボードがあります。 今、クライアントは AI 生成クリエイティブ、インフルエンサーキャンペーン、ローカルラジオを試したがっています。新しいチャネルが増えるたびに、新しいツール、新しい統合、新しいワークフローの習得が必要になります。クライアントが新しいことを試したいたびにチームを拡大し続けることはできません。 問題はチームの能力ではありません。すべてのチャネルが異なる言語で話していて、エージェントがどのようにして在庫を発見し、購入を実行し、クリエイティブを配布し、データを活性化し、結果を報告するかという共通標準が業界に存在しないことです。 AdCP がその標準です。一つのプロトコル。あらゆるプラットフォーム。キャンペーンのすべてのステップ。 ## アクティベーションにとどまらない AdCP はアクティベーションだけでなく、ブリーフの受け取りから測定まで、キャンペーンのライフサイクル全体を調整します。プログラマティック取引所だけでなく、直接取引、スポンサーシップ、放送、屋外広告など、あらゆる取引モデルにまたがって機能します。世界の広告費の大半は非プログラマティックチャネルを流れており、AdCP はそのすべてに対応するように設計されています。 このページでは Alex のチームが新しいパートナーを探すところから結果を測定するまでの全ワークフローを追います。各セクションでは、人間側の課題、プロトコルによる解決策、それを実現するタスクを示します。最後には、AdCP がカバーするすべてのドメインとそれらのつながりが理解できます。 *** ## 新しいパートナーを探す Alexが壁のディスプレイに表示されたネットワークマップに手を伸ばし、接続されたパートナーの星座から新しいパブリッシャーノードを選ぼうとしている Alexはこれまで接触したことのないパブリッシャーと取引したいと考えています。従来のやり方では、営業電話、契約、そして何が利用可能かを確認するだけで何週間もの統合作業が必要になります。 AdCP では、ディスカバリーがプロトコルに組み込まれています。AdCP 対応のパブリッシャーはすべて `adagents.json` ファイルをホストしています。これは、プロパティと認可されたエージェントを機械可読な形式で宣言したものです。セラーオペレーターは、企業アイデンティティ、セラーエージェントのエンドポイント、署名鍵のディスカバリーのために `brand.json` を公開します。Alexのバイヤーエージェントは、ブラウザが `robots.txt` を読むのと同じように、両方のファイルを解決できます。 ``` https://streamhaus.tv/.well-known/adagents.json ``` ```json theme={null} { "version": "1.0", "publisher": { "name": "StreamHaus", "domain": "streamhaus.tv" }, "agents": [ { "url": "https://ads.streamhaus.tv/mcp", "protocol": "mcp", "capabilities": ["get_products", "create_media_buy", "sync_creatives"] } ] } ``` より広範なディスカバリー ——「スポーツ在庫を持つ CTV パブリッシャーを探して」—— には、[AgenticAdvertising.org レジストリ](/docs/registry) がエンティティ解決とエージェント検索を提供します。Alexのエージェントはカテゴリ、地域、または機能でレジストリを照会し、接続すべきパブリッシャーのリストを取得できます。 レジストリ API はブランドを AdCP エージェントに解決します: ``` GET /api/registry/agents?capability=get_products&channel=ctv ``` ```json theme={null} { "agents": [ { "domain": "streamhaus.tv", "agent_url": "https://ads.streamhaus.tv/mcp", "capabilities": ["get_products", "create_media_buy"], "channels": ["ctv", "olv"] } ] } ``` パブリッシャーがプロパティと認可されたエージェントを宣言する方法。 *** ## アカウントを設定します Samが、二人の間にティール色のチェックマークが表示されたラップトップを挟んでデスク越しに握手しています。新しい商業関係を構築している場面 Alexのチームがメディアを購入するには、商業的な関係が必要です。従来は、プラットフォームごとにオンボーディングが異なります——ポータル、フォーム、営業担当者、何週間もの往復作業。 AdCP はアカウントプロトコルでこれを標準化します。Alexのメディアバイヤーである Sam は、Pinnacle と StreamHaus の関係を一回の呼び出しで設定します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/account/sync-accounts-request.json", "accounts": [ { "brand": { "domain": "acmeoutdoor.com" }, "operator": "pinnacle-agency.com", "billing": "operator" } ] } ``` セラーはアカウントのステータス——アクティブ、審査中、または必要な追加情報——で応答します。アクティブになれば、Sam はメディアを購入できます。 `list_accounts` はすべてのプラットフォームにわたるアクティブな関係を表示するため、Alexはチームがどのパブリッシャーと設定済みかを一目で確認できます。 商業的アイデンティティ、請求モデル、マルチアドバタイザー管理。 *** ## 利用可能な在庫を探す Samが壁のスクリーンに表示された三つのプロダクトカードを指差しています。一つのキャンペーンブリーフから生成された CTV、ディスプレイ、オーディオの在庫オプション ここから本領が発揮されます。Sam は Acme Outdoor の Q2 キャンペーン向けにプレミアムスポーツ在庫を探したいと考えています。従来のやり方なら、四つのダッシュボードにログインして比較にならないものを比べることになります。 AdCP では、`get_products` が接続されたすべてのセラーに同じブリーフを送ります。Sam は欲しいものを自然言語で記述します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/media-buy/get-products-request.json", "buying_mode": "brief", "brief": "Premium sports video inventory, Q2 2026, targeting 25-45 males interested in outdoor recreation. Budget $50K across CTV and display.", "brand": { "domain": "acmeoutdoor.com" } } ``` すべてのセラーが同じフォーマット——価格、予測、ターゲティングオプション、クリエイティブ要件を含むプロダクト——で応答します。Sam は四つではなく一つの画面で提案を並べて比較できます。 ```json theme={null} { "products": [ { "product_id": "streamhaus_sports_ctv_q2", "name": "StreamHaus Sports Premium", "channels": ["ctv"], "pricing_options": [ { "model": "cpm", "price": 28.50, "currency": "USD" } ], "forecast": { "impressions": { "min": 500000, "max": 750000 } }, "format_ids": [ { "agent_url": "https://ads.streamhaus.tv", "id": "video_16x9_30s" } ] } ] } ``` しかし Sam はまだ終わっていません。StreamHaus のスポーツパッケージは気に入っていますが、予算を CTV に寄せてディスプレイの割り当てを削りたいと考えています。最初からやり直す代わりに、**refine モード**——セラーとの反復的な会話——を使います: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/media-buy/get-products-request.json", "buying_mode": "refine", "refine": [ { "scope": "product", "product_id": "streamhaus_sports_ctv_q2", "action": "more_like_this", "ask": "More CTV inventory like this, willing to go up to $35 CPM" }, { "scope": "request", "ask": "Drop display entirely, reallocate budget to CTV and OLV" } ] } ``` セラーは調整し、絞り込まれたオプションで応答します。新しい RFP も、最初からやり直しも不要です。Sam は欲しいものが得られるまで反復します。 ブリーフからデリバリーまで、三社のセラーにまたがる完全なメディアバイのウォークスルー。 *** ## クリエイティブを制作します Mayaがクリエイティブスタジオで iPad を持って座り、一つのブリーフから生成されたさまざまなサイズの広告フォーマットが壁のスクリーンに表示されている Pinnacle のクリエイティブストラテジストである Maya は、Sam のキャンペーン向けに広告を制作する必要があります。一つのキャンペーン、三社のセラー、六つのフォーマット——CTV 動画、OLV プレロール、ディスプレイバナー、コンパニオン広告。従来のやり方なら、六つの独立した制作ワークフローになります。 まず Maya は各セラーが受け付けるフォーマットを確認します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/creative/list-creative-formats-request.json", "type": "video" } ``` 各セラーは正確な仕様——寸法、コーデック、ファイルサイズ、尺の制限——とともにサポートするフォーマットを返します。推測は不要です。 次に Maya はクリエイティブエージェントにブリーフを渡します。一つのブリーフですべてのフォーマットが制作されます: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/media-buy/build-creative-request.json", "message": "Adventurous, aspirational summer campaign — gear for people who live outside", "brand": { "domain": "acmeoutdoor.com" }, "target_format_ids": [ { "agent_url": "https://ads.streamhaus.tv", "id": "video_16x9_30s" }, { "agent_url": "https://ads.streamhaus.tv", "id": "display_300x250" } ] } ``` クリエイティブエージェントは Acme Outdoor のブランドアイデンティティ——カラー、ロゴ、トーンガイドライン——をブランドの `brand.json` から直接取得します(詳細は後述)。ブランドガイドの PDF も手動のアセット受け渡しも不要です。 Maya が最初の草稿を気に入らなければ、自然言語で修正できます: *「冒頭のショットをよりダイナミックにして、プロダクトショットをハイキングブーツに変えて。」* `build_creative` タスクは反復的な改良をサポートしています——同じタスク、会話形式のガイダンス。 承認後、`sync_creatives` が完成したアセットをすべてのセラーに同時に配布します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/creative/sync-creatives-request.json", "account": { "brand": { "domain": "acmeoutdoor.com" }, "operator": "pinnacle-agency.com" }, "creatives": [ { "creative_id": "acme_summer_ctv_30s", "name": "Acme Summer CTV 30s", "format_id": { "agent_url": "https://ads.streamhaus.tv", "id": "video_16x9_30s" }, "assets": { "video": { "url": "https://cdn.pinnacle.com/acme_summer_30s.mp4", "width": 1920, "height": 1080, "duration_ms": 30000 } } } ] } ``` クリエイティブ生成、フォーマットディスカバリー、マルチセラー配布。 *** ## 購入を実行します Samがラップトップの起動ボタンを押し、画面からティール色のパルスが放射されています。後ろには腕を組んで満足そうな表情の Alexが立っている Sam はプロダクト、クリエイティブ、アカウントを揃えました。いよいよ購入です。`create_media_buy` への一回の呼び出しで、すべてのセラーにまたがるキャンペーンが実行されます: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/media-buy/create-media-buy-request.json", "account": { "brand": { "domain": "acmeoutdoor.com" }, "operator": "pinnacle-agency.com" }, "brand": { "domain": "acmeoutdoor.com" }, "start_time": "2026-04-01T00:00:00Z", "end_time": "2026-06-30T23:59:59Z", "packages": [ { "product_id": "streamhaus_sports_ctv_q2", "budget": 35000, "pricing_option_id": "cpm_standard" } ] } ``` クリエイティブを生成するセラー——AI アシスタント、会話型広告プラットフォーム——向けには、メディアバイに事前制作アセットの代わりにブリーフを含めることができます。セラーのクリエイティブエージェントがブランドアイデンティティとキャンペーンコンテキストを参照してリアルタイムに生成します。提供クリエイティブと生成クリエイティブの両モデルとも、同じ `create_media_buy` タスクを使用します。 `update_media_buy` はフライト中の変更に対応します: パッケージ間での予算のシフト、フライト日の調整、クリエイティブ割り当ての変更。キャンセルして再作成する必要はありません。 Sam のリクエストにある `idempotency_key` は飾りではありません。Pinnacle のバイヤーエージェントは、POST がネットワークを離れる前に RFC 9421 HTTP Message Signatures で署名します。StreamHaus は Pinnacle のオペレーターが公開した JWKS に対して Pinnacle の署名を検証してから、購入を受け付けます。キャンペーンが `pending_start` から `active` に移行すると、StreamHaus は Pinnacle のオーケストレーターへ署名付き Webhook を返します——同じ署名プロファイルで、StreamHaus のリクエスト署名鍵は `brand.json` の `agents[]` エントリを通じて公開され、任意でパブリッシャーの `adagents.json` によってピン留めされます。Sam のラップトップがレスポンスを取りこぼしてエージェントがリトライしても、`idempotency_key` によって二度目の呼び出しは安全です——StreamHaus は二重課金する代わりに `replayed: true` を付けて元の購入を返します。ガバナンス承認は `check_governance` の署名付き JWS トークンとして一緒に流れるため、チェーン内のどのエージェントも Jordan の承認を偽造できません。[セキュリティガイド](/docs/building/by-layer/L1/security)を参照してください。 *** ## 配信時にマッチングする キャンペーンが稼働しています。ユーザーが StreamHaus のページを読み込む、OutdoorNet のモバイルアプリを開く、あるいは AI アシスタントにハイキング用品について尋ねるとき、パブリッシャーは Sam のどのパッケージをアクティベートすべきか——今この瞬間、このコンテンツに、このユーザーに対して——を知る必要があります。 [Trusted Match Protocol(TMP)](/docs/trusted-match)は、これを構造的に分離された二つの操作で処理します。**Context Match** は利用可能なパッケージに対してコンテンツシグナルを評価します——この境界をユーザーアイデンティティが越えることはありません。**Identity Match** は不透明なトークンを使ってユーザーの適格性を確認します——この境界をページコンテキストが越えることはありません。パブリッシャーは両方のレスポンスをローカルで結合します。バイヤーがアイデンティティとコンテンツを同時に見ることはありません。 一つのプロトコルで、あらゆるサーフェスに対応します: ウェブ、モバイル、CTV、AI アシスタント、リテールメディア。 *** ## データを追加します Sam と Kai が並んでラップトップに向かい、二人の画面の間を半透明のティール色のデータストリームが弧を描いて流れています。キャンペーンデータとシグナルデータを組み合わせている場面 Sam のキャンペーンには、セラーが提供するターゲティング以上のものが必要です。クライアントには CRM データ(除外すべき既存顧客)があり、Pinnacle の DMP にはオーディエンスセグメントがあり、さらに Kai のデータ会社 Meridian Geo からのサードパーティシグナルを重ねたいと考えています。 **オーディエンス**は `sync_audiences` でキャンペーンに紐付けて送る: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/media-buy/sync-audiences-request.json", "account": { "brand": { "domain": "acmeoutdoor.com" }, "operator": "pinnacle-agency.com" }, "audiences": [ { "audience_id": "acme_existing_customers", "name": "Acme Outdoor — existing customers", "audience_type": "suppression" } ] } ``` **シグナル**——サードパーティのターゲティングデータ——は、シグナルプロトコルを通じてディスカバリーと活性化を行います。Sam は必要なものを検索します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/signals/get-signals-request.json", "signal_spec": "Outdoor recreation enthusiasts near sporting goods retailers, 25-45" } ``` Kai の Meridian Geo は、価格、カバレッジ、活性化オプションとともにマッチするシグナルセグメントを返します。Sam は必要なものを活性化します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/signals/activate-signal-request.json", "signal_agent_segment_id": "meridian_outdoor_rec_25_45", "destinations": [ { "type": "platform", "platform": "streamhaus" } ] } ``` シグナルは StreamHaus のプラットフォームで活性化されます。どちらの側もカスタム統合を構築することなく、Kai のデータが Sam のキャンペーンに届きます。 Sam が Kai のターゲティングデータをプラットフォームをまたいでどのようにディスカバリーし活性化するか。 *** ## ガバナンスを管理します Jordan がタブレットでガバナンス承認チェーンを確認しています。シルバーのフープイヤリングがランプの光に輝き、その表情は集中して慎重だ Sam のキャンペーンがどれも稼働する前に、ガバナンスを通過します。Pinnacle のキャンペーンオペレーションマネージャーである Jordan は、Alexがエージェントに費用を使わせる前にガバナンスフレームワークを設定しました。 `check_governance` は実行前に自動的に実行されます——予算制限、ブランドセーフティ、ターゲティングコンプライアンス: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/governance/check-governance-request.json", "plan_id": "acme_outdoor_q2_plan", "caller": "https://buyer.pinnacle-agency.com/a2a", "tool": "create_media_buy", "payload": { "brand": { "domain": "acmeoutdoor.com" }, "start_time": "2026-04-01T00:00:00Z", "end_time": "2026-06-30T23:59:59Z" } } ``` すべてがパスすれば、キャンペーンは進みます。エージェントの権限を超えるもの——例えば、予算が Jordan の 2万ドルの自動承認閾値を超えている場合——はガバナンスエージェントが人間にエスカレーションします。Jordan が確認し、必要であれば条件を追加して承認します。エージェントはこのステップをスキップできません。これはアーキテクチャ上の制約であり、手続き上のものではありません。 キャンペーン終了後、`get_plan_audit_logs` が完全な意思決定の記録を提供します——誰が何を提案し、誰が承認し、どんな条件が付けられ、実際に何が実行されたか。すべての決定が記録され、帰属が明確です。 ```json theme={null} { "entries": [ { "timestamp": "2026-03-15T14:30:00Z", "actor": "buyer_agent", "action": "submit_plan", "details": { "budget": 50000, "channels": ["ctv", "olv"] } }, { "timestamp": "2026-03-15T14:30:01Z", "actor": "governance_agent", "action": "escalate", "reason": "Budget exceeds auto-approval threshold ($20,000)" }, { "timestamp": "2026-03-15T15:12:00Z", "actor": "jordan@pinnacleagency.com", "action": "approve_with_conditions", "conditions": ["Weekly spend cap of $15,000", "CTV only — no OLV until brand safety review"] } ] } ``` ガバナンスのウォークスルー——混乱から監査証跡へ。 *** ## パフォーマンスを追跡します Sam がコーヒーを手に持ち、トレンドチャートが表示された大きなパフォーマンスダッシュボードの前に立ち、自信に満ちた表情で振り返っています。すべてが順調だ Sam のキャンペーンが稼働しています。従来のやり方なら四つのダッシュボードを確認することになります。今は `get_media_buy_delivery` がすべてのセラーからパフォーマンスデータを一つのレスポンスに集約します: ```json theme={null} { "impressions": 1250000, "clicks": 18750, "spend": { "amount": 34200, "currency": "USD" }, "by_package": [ { "product_id": "streamhaus_sports_ctv_q2", "impressions": 750000, "completion_rate": 0.87 } ] } ``` より深いパフォーマンス追跡のために、AdCP はさらに二つのツールを提供します: **`log_event`** はマーケティングイベント——購入、リード、サインアップ——をアトリビューションと最適化のためにセラーに送り返します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/media-buy/log-event-request.json", "event_source_id": "acme_website_pixel", "events": [ { "event_id": "evt_abc123", "event_type": "purchase", "event_time": "2026-05-15T10:30:00Z", "action_source": "website", "custom_data": { "value": 149.99, "currency": "USD" } } ] } ``` **`provide_performance_feedback`** は最適化ループを閉じます——何が機能していて何が機能していないかをセラーに伝え、アルゴリズムが調整できるようにします: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/media-buy/provide-performance-feedback-request.json", "media_buy_id": "mb_acme_q2_001", "measurement_period": { "start": "2026-04-01T00:00:00Z", "end": "2026-04-30T23:59:59Z" }, "performance_index": 1.35 } ``` *** ## ストアを接続します スマートフォンの商品カタログとノートパソコンのキャンペーンインターフェースが光るティール色の線で繋がれている俯瞰図。カタログデータが両者の間を流れている Acme Outdoor は 200 点の商品が入った Shopify ストアを持っています。彼らはカタログを AI プラットフォーム——商品を推薦する AI アシスタント、商品を表示する AI 検索エンジン、フィードデータを必要とするリテールメディアネットワーク——で利用可能にしたいと考えています。 `sync_catalogs` は商品フィードをすべての接続されたプラットフォームに送信します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/media-buy/sync-catalogs-request.json", "account": { "brand": { "domain": "acmeoutdoor.com" }, "operator": "pinnacle-agency.com" }, "catalogs": [ { "catalog_id": "acme_outdoor_products", "name": "Acme Outdoor Product Feed", "type": "product", "url": "https://acmeoutdoor.com/feeds/products.json", "feed_format": "shopify", "update_frequency": "daily" } ] } ``` セラーはカタログを取り込み、プロダクトレベルのターゲティング、ダイナミッククリエイティブ、会話型レコメンデーションに利用できるようにします。商品が在庫切れになったり価格が変わったりすると、フィードが更新され、セラーは自動的に同期します。 *** ## ブランドを守る Tomoko が企業ロビーに立ち、ブランドアイデンティティ要素が表示されたすりガラスのディスプレイの前に落ち着いた様子で立っています。何が世に出るかをコントロールしている Tomoko は Acme Outdoor の親会社 Nova Motors でブランドオペレーションを担当しています。彼女は Nova の `brand.json`——AI エージェントが直接参照する機械可読なブランドアイデンティティ——を公開しました: ``` https://novamotors.com/.well-known/brand.json ``` ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "house": { "domain": "novamotors.com", "name": "Nova Motors" }, "brands": [ { "id": "acme_outdoor", "names": [{ "en": "Acme Outdoor" }], "identity_agent": { "url": "https://brand.novamotors.com/mcp", "id": "nova_brand_agent" } } ] } ``` Maya のクリエイティブエージェントが広告を生成するとき、`brand.json` と `get_brand_identity` タスクから直接ブランドガイドライン——カラー、ロゴ、トーン、ビジュアルガイドライン——を取得します。ブランドガイドの PDF も手動のアセット受け渡しも不要です。ブランドは AI エージェントが見るものをコントロールし、プロトコルがそれを強制します。 ライセンスを受けたタレントやサードパーティ IP を使用するキャンペーンには、ブランドプロトコルが[権利ライセンス](/docs/brand-protocol/walkthrough-rights-licensing)を処理します——ディスカバリー、取得、クリエイティブ承認、ライフサイクル管理、すべて同じプロトコルを通じて。 ブランドアイデンティティ、権利ライセンス、ブランドが AI に対してアセットをどのようにコントロールするか。 *** ## 全体像 Alex は十二のプラットフォーム、十二の統合、プラットフォームの操作に溺れるチームから始まりました。今、チームは一つのプロトコルを通じて作業しています: | 必要なこと | AdCP の対処方法 | 主要タスク | | --------------- | ---------------------- | --------------------------------------------------------- | | 新しいパートナーを探す | パブリッシャーディスカバリー + レジストリ | `adagents.json`、Registry API | | 関係を構築する | 標準化されたオンボーディング | `sync_accounts`、`list_accounts` | | 在庫を発見する | 一つのブリーフ、すべてのセラーへ | `get_products`(ブリーフ + リファインモード) | | クリエイティブを制作する | 一つのブリーフ、すべてのフォーマットへ | `build_creative`、`list_creative_formats`、`sync_creatives` | | キャンペーンを実行する | 一回の購入、複数のセラー | `create_media_buy`、`update_media_buy` | | ターゲティングデータを追加する | オーディエンス + サードパーティシグナル | `sync_audiences`、`get_signals`、`activate_signal` | | すべてをガバナンスする | 人間による監督、アーキテクチャに組み込み | `check_governance`、`get_plan_audit_logs` | | パフォーマンスを追跡する | 統合レポーティング + イベント | `get_media_buy_delivery`、`log_event` | | コマースを接続する | 商品カタログ同期 | `sync_catalogs` | | ブランドを守る | 機械可読なアイデンティティ | `brand.json`、`get_brand_identity` | Sam はメディアを購入します。Maya はクリエイティブを制作します。Jordan はガバナンスを行います。Kai はデータを提供します。Tomoko はブランドを守ります。全員が同じプロトコルで話します。 *** ## 内部の仕組み AdCP は一つの AI がすべてを処理することを前提としません。専門化されたエージェントが得意なことを担当します: * **メディアバイイングエージェント**——在庫を発見しキャンペーンを実行します * **クリエイティブエージェント**——フォーマットをまたいで広告を生成・適応させる * **シグナルエージェント**——オーディエンスを見つけて活性化します * **ガバナンスエージェント**——ブランドセーフティとコンプライアンスを強制します * **オーケストレーター**——ワークフローを調整し、重要なことを人間が承認するようにします これらのエージェントは二つのトランスポートプロトコルで通信します: **MCP**(AI アシスタントがツールを呼び出す場合)と **A2A**(エージェント間の協調)。同じタスク、同じスキーマ、異なるトランスポートです。 **エージェントが主張どおりに動作することをどう確認するか:** すべてのエージェントは、自分が扱う大まかな領域と、サポートする具体的なフローをネットワークに伝えます——そしてそれらの主張はテスト可能です。プロトコルにはコンプライアンス・ストーリーボードが同梱されており、ランナーがエージェントに対してそれを実行します。パスすれば、主張は検証可能です。[コンプライアンスカタログ](/docs/building/verification/compliance-catalog)を参照してください。 ## ブリーフからライブ広告まで Alex のチームが今行っていること: 1. **ブリーフを書く**: 「Q2 に向けてスポーツパブリッシャーのプレミアム動画在庫を 5万ドルの予算で探して」 2. **エージェントが選択肢を発見する**: `get_products` がすべての接続されたセラーに同時に送られます 3. **提案を比較する**: プロダクトが標準フォーマット——価格、予測、ターゲティング——で返ってきます。すべて比較可能です 4. **エージェントがクリエイティブを制作する**: `build_creative` が各セラーのフォーマットにアセットを適応させる 5. **承認して起動する**: `create_media_buy` が一回の呼び出しでプラットフォームをまたいで実行します 6. **配信時にマッチングする**: TMP の Context Match + Identity Match が、構造的なプライバシー分離を保ちながら各インプレッションでパッケージをアクティベートします 7. **デリバリーを監視する**: `get_media_buy_delivery` がすべてのセラーからのパフォーマンスを一つのビューに集約します 各ステップは JSON Schema で定義されたリクエストとレスポンスを持つ標準 AdCP タスクを使用します。プラットフォーム固有のコードも、システム間の手動翻訳も不要です。 ## ガバナンスによる信頼 AI エージェントが自律的にお金を使う場合、信頼には構造が必要です。AdCP のガバナンスレイヤーがそれを提供します: * **キャンペーン開始前**: `check_governance` が予算制限、ブランドセーフティ、規制コンプライアンスを検証します * **権限を超える場合**: ガバナンスエージェントが人間にエスカレーションします——AI ではなく、あなたのチームが承認します * **キャンペーン実行中**: ガバナンスエージェントが承認されたパラメータに対するデリバリーを監視します * **デリバリー後**: `get_plan_audit_logs` が完全な意思決定の記録を提供します——誰が何を提案し、誰が承認し、実際に何が実行されたか ガバナンスは物事を遅らせるゲートではありません。時間とともにエージェントにより多くの自律性を与えられるようにするセーフティネットです。 ## どこから始めたいか? AI サーフェスに広告を出したいブランド、エージェンシー、企業向け プロトコルを実装するプラットフォーム、パブリッシャー、開発者向け ## はじめる AdCP について質問し、プロトコルを探索し、タスクをテストする——コード不要 テスト用 CLI ツールを備えた JavaScript と Python ライブラリ ブランドの brand.json ファイルを作成・検証します パブリッシャーの adagents.json ファイルを検証・作成します 登録済みのエージェント、ブランド、パブリッシャーを閲覧します MCP と A2A の選択、実装パターンの習得 ## 実際に見てみます Sam のブリーフからデリバリーまでの完全なキャンペーンを追う 構造的なプライバシー分離を保ったリアルタイムのパッケージアクティベーション Maya のクリエイティブ生成と配布を追う 支出を守るトラストモデルで Jordan の軌跡を追う ## プラットフォームプロバイダー向け AI が広告を購入しています。あなたのプラットフォームでも購入できるようにしましょう。 DSP、SSP、パブリッシャー、データプラットフォーム、クリエイティブプラットフォーム、ガバナンスサービス、またはその他の広告テクノロジーソリューションを運営している場合、AdCP によって AI エージェントがあなたのプラットフォームを発見してトランザクションを行えるようになります。始めるには: 1. **AdCP エージェントを実装する**——プラットフォームの機能を MCP または A2A 上の AdCP タスクとして公開します。`get_adcp_capabilities` から始めてください。 2. **ディスカバリーと認可のレコードを公開する**——オペレーターのアイデンティティ、エージェントのエンドポイント、署名鍵のディスカバリーには `brand.json` を、プロパティと認可されたエージェントにはパブリッシャーの `adagents.json` を使用します。 3. **実装をテストする**——[Addie](https://adcontextprotocol.org/chat) または[クライアント SDK](/docs/building/schemas-and-sdks) で検証します。 ビジネスに関連するプロトコルドメインを実装します: * **パブリッシャーと SSP**: [メディアバイ](/docs/media-buy) と [adagents.json](/docs/governance/property) * **データプロバイダー**: [シグナル](/docs/signals/overview) と[データプロバイダーガイド](/docs/signals/data-providers) * **クリエイティブプラットフォーム**: [クリエイティブ](/docs/creative) * **ガバナンスベンダー**: [ガバナンスプロトコル](/docs/governance/overview) * **ブランド**: [ブランドプロトコル](/docs/brand-protocol) と [brand.json](/docs/brand-protocol/brand-json) ## 広告主とエージェンシー向け チームを拡大することなく、より多くのプラットフォームでキャンペーンを実行します。 AdCP 対応エージェントは、単一のインターフェースを通じてすべてのメディアパートナーで動作します——同じタスクが、作業しているプラットフォームに関わらず、CTV 在庫の購入、オーディエンスデータの活性化、クリエイティブの管理を行います。 1. **バイヤーズガイドを読む**——[AI マネタイズガイド](/docs/sponsored-intelligence/monetizing-ai)でブランド、エージェンシー、中小企業向けにこれがどのように機能するかを説明しています。 2. **プラットフォームサポートを確認する**——メディアパートナーのどれが AdCP をサポートしているか確認するか、[レジストリ](/docs/registry)を閲覧します。 3. **Addie で試す**——[Addie に聞く](https://adcontextprotocol.org/chat)——コード不要でプロトコルをウォークスルーしてもらえます。 4. **自分のエージェントを構築する**——エンジニアリングチームは不要です。[認定プログラム](/docs/learning/overview)では、バイブコーディングを通じて誰でも動作する広告エージェントを構築できることを教えています——やりたいことを説明すれば、AI コーディングアシスタントがコードを書いてくれます。 5. **チームと共有する**——技術チームに[構築ガイド](/docs/building)と[クライアント SDK](/docs/building/schemas-and-sdks) を共有して統合を始めます。 ### クライアントライブラリ ```bash JavaScript/TypeScript theme={null} npm install @adcp/sdk ``` ```bash Python theme={null} pip install adcp ``` ```bash Go theme={null} go get github.com/adcontextprotocol/adcp-go/adcp ``` * **NPM**: [@adcp/sdk](https://www.npmjs.com/package/@adcp/sdk) | [GitHub](https://github.com/adcontextprotocol/adcp-client) * **PyPI**: [adcp](https://pypi.org/project/adcp/) | [GitHub](https://github.com/adcontextprotocol/adcp-client-python) * **Go**: [adcp-go](https://github.com/adcontextprotocol/adcp-go) ## オープンソースの例 [Prebid Sales Agent](https://github.com/prebid/salesagent) は、GAM 統合を備えたフルスタックのセラーエージェント(Python バックエンド、TypeScript プロトコルレイヤー)で、Prebid ワーキンググループによって構築されました。これは維持管理されるリファレンス実装ではなく、コミュニティの一例です。自分のエージェントを構築するには、[公式 SDK](/docs/building/schemas-and-sdks) と [skill ファイル](/docs/building/by-layer/L4/build-an-agent)から始めてください。 ## 組織について AdCP は [AgenticAdvertising.org](https://agenticadvertising.org) のプロジェクトです。AgenticAdvertising.org は、AI を活用した広告のオープン標準を推進するパブリッシャー、プラットフォーム、エージェンシー、テクノロジープロバイダーの業界組織です。メンバーは AgenticAdvertising.org に参加し、プロトコルの開発と採用に取り組みます。 ファウンデーションのガバナンス——構造、投票クラス、理事会構成、仕様のライフサイクル、行動規範——は、リポジトリの [CHARTER](https://github.com/adcontextprotocol/adcp/blob/main/CHARTER.md) にまとめられ、[agenticadvertising.org/governance](https://agenticadvertising.org/governance) で公開されています。 ## ヘルプが必要な場合 * ドキュメントを閲覧します * [Slack コミュニティ](https://join.slack.com/t/agenticads/shared_invite/zt-3c5sxvdjk-x0rVmLB3OFHVUp~WutVWZg)で質問します * メール: [support@adcontextprotocol.org](mailto:support@adcontextprotocol.org) # コマースメディア Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/commerce-media AdCPにおけるコマースメディア:小売メディアネットワークがセルフサービスプラットフォームを構築する代わりにエージェントストアフロントを立ち上げるべき理由、およびスポンサードプロダクト、店舗内デジタル、クローズドループアトリビューションのモデリング方法。 セルフサービス広告プラットフォームを構築するすべての小売メディアネットワークは、最終的に同じ場所に行き着く。最大規模の食料品店、薬局、総合小売業者と並んでエージェンシーの注目を競い合い、ブランドはまた別のインターフェースを覚え、もう一つのログインを管理し、もう一組のレポートを照合しなければなりません。 別の方法があります。エージェントストアフロントを立ち上げることです。バイヤーエージェントが他のセラーと同じプロトコルを使って探索し、取引し、測定できるよう、インベントリを公開します。エージェントが探索を担当すれば、優位性はUIの品質からデータの品質に移る。そしてデータこそ、多くの小売業者がすでに競争上の堀を持っている領域です。 ## ストアフロント、プラットフォームではなく Daniel reviews a cluttered platform roadmap on a wall screen — dashboard mockups, audience builders, and reporting UIs pile up while he looks skeptical Danielはマーケットプレイス ShopGrid のリテールメディアを担当しています。このマーケットプレイスには月間2億人のショッパーがおり、ロイヤルティプログラムから決定論的な購買データが得られます。ShopGridの広告ビジネスを構築するよう命じられたとき、ロードマップは見慣れたものでしました。セルフサービスのキャンペーンツール、独自のオーディエンスビルダー、カスタムレポーティングダッシュボード、そして最終的にはDSP。 構築の途中で問題に気づいた。ShopGridが獲得したかったブランド——CPG企業、ヘルス&ビューティーブランド、家電メーカー——はすでに十数のリテールメディアプラットフォームでキャンペーンを管理していました。各プラットフォームには独自のAPI、独自のオーディエンスタクソノミー、独自のレポーティングフォーマットがあった。ブランドにまた別のプラットフォームの統合を求めることは、エンジニアリング予算が100倍の企業とUIの品質で競争することを意味しました。 Daniel walks a retail floor as data streams flow from his tablet to glowing digital screens on end-caps and checkout lanes — the store as a data asset ShopGridの堀はダッシュボードにあるのではなかった。堀はデータにある。数百万人のロイヤルティ会員による決定論的な購買アトリビューション、数千の店舗にわたるリアルタイム在庫、そして購買ポイントに設置された店舗内デジタルスクリーン。問題は、そのデータとインベントリをできるだけ多くのバイヤーにアクセスしやすくする方法でしました。 Daniel at his desk as five retail media product cards radiate outward from his monitor — sponsored products, display, video, in-store, and premium placements being published DanielはAdCPセールスエージェントを立ち上げた。ShopGridのリテールメディアカタログ全体——スポンサードプロダクト、オンサイトディスプレイ、店舗内スクリーン——が `get_products` 経由でバイヤーエージェントが探索できるプロダクトとして公開されています。ロイヤルティデータは標準的な配信メトリクスを通じてレポートされるクローズドループアトリビューションを動かす。店舗ロケーションは、近接ターゲティングのためのキャッチメントエリアを持つカタログとしてモデル化されています。 Split scene — Sam runs a brief from his agency desk as search beams connect to Daniel's retail media products floating on the right, discovery in action 結果として、AdCPに対応しているバイヤーエージェントであれば、カスタム統合なしにShopGridのインベントリで取引できます。Pinnacle AgencyのSamは、Summit Foodsのクロスリテーラーキャンペーンを実行中に、他の小売業者と同じプロトコルとブリーフを使ってShopGridのスポンサードプロダクトを発見しました。 リテールメディアチームが実際に求めているのは、セルフサービスのコントロール、マネージドサービスの簡便さ、そして自社の内部ツールにあるすべてのデータです。セルフサービスプラットフォームはコントロールを与えるが、大規模なエンジニアリング投資が必要です。マネージドサービスはバイヤーにとって簡単だがスケールしません。エージェントストアフロントはこのジレンマを解決します。小売業者はプロダクト、価格設定、ルールを定義し(コントロール)、バイヤーエージェントが営業担当なしに探索と実行を処理し(簡便さ)、小売業者がMCPサーバーを運営しているため、すべてのトランザクションが自社インフラを通じて流れる(データは社内に留まる)。 これはShopGridが他のすべてを放棄したことを意味するわけではありません。バイヤーエージェントを使っていないブランドには依然として基本的なセルフサービスアクセスが必要です。しかしエージェントストアフロントは成長レイヤーです。中規模小売業者の独自プラットフォームを統合しなかったバイヤーにリーチする方法です。 以下の表は、馴染みのあるリテールメディアのコンセプトとAdCP上の対応物をマッピングしています。 ## コンセプトマッピング | リテールメディアのコンセプト | AdCPの対応物 | 参照 | | ----------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------- | | 小売業者 / リテールメディアネットワーク | セールスエージェント(MCPサーバー) | [`adagents.json`](/docs/media-buy/advanced-topics/accounts-and-security) | | 小売業者とのアドバタイザーアカウント | `account_id` + `list_accounts` | [Accounts & Agents](/docs/building/by-layer/L2/accounts-and-agents) | | ブランドのプロダクトカタログ | `brand.product_catalog` | [Brand identity](/docs/brand-protocol/brand-json) | | プロモーション用のGTIN / SKU選択 | `Catalog` 上のカタログセレクター(`ids`、`gtins`、`tags`) | [Catalogs](/docs/creative/catalogs) | | スポンサードプロダクトリスティング | カタログレンダリングクリエイティブを持つプロダクト | [Creative Formats](/docs/creative/formats) | | オンサイトディスプレイ / ビデオ | 標準 `format_ids` を持つプロダクト | [Media Products](/docs/media-buy/product-discovery/media-products) | | 小売業者のファーストパーティオーディエンス | ブリーフベース;`data_provider_signals` 経由の名前付きセグメント | [Targeting](/docs/media-buy/advanced-topics/targeting) | | ROASターゲット / コンバージョン最大化 | パッケージ上の `optimization_goals` | [Optimization & Reporting](/docs/media-buy/media-buys/optimization-reporting) | | クローズドループ測定 | プロダクト上の `outcome_measurement` + `conversion_tracking` | [Delivery Reporting](/docs/media-buy/task-reference/get_media_buy_delivery) | | アトリビューションウィンドウ(クリック14日、ビュー1日) | 配信レスポンスの `attribution_window` | [Delivery Reporting](/docs/media-buy/task-reference/get_media_buy_delivery) | | ROAS / アトリビューテッド収益 | 配信メトリクスの `roas`、`conversion_value` | [Delivery Reporting](/docs/media-buy/task-reference/get_media_buy_delivery) | | 店舗内アトリビューション | `in_store` を使った `by_action_source` | [Delivery Reporting](/docs/media-buy/task-reference/get_media_buy_delivery) | | 店舗ロケーション / 店舗ロケーター | 店舗カタログ(セラー提供またはバイヤー同期) | [Catalogs — Stores](/docs/creative/catalogs#stores) | | リアルタイム在庫 / 在庫あり | `sync_catalogs` 経由のインベントリカタログ | [Catalogs](/docs/creative/catalogs) | | 近接 / キャッチメントターゲティング | 店舗キャッチメントエリア(等時線、半径、GeoJSON) | [Catalogs — Catchment areas](/docs/creative/catalogs#catchment-areas) | 各小売業者は個別のセールスエージェントです。彼らのメディアオファリングはプロダクトとしてモデル化されています。バイヤーのブランドアイデンティティはSKUレベルのクリエイティブレンダリング用のプロダクトカタログを持ちます。ブランドと小売業者間のアカウント関係はメディアバイ上の `list_accounts` と `account` を通じて管理される——[Accounts & Agents](/docs/building/by-layer/L2/accounts-and-agents) を参照。 ## プロダクトスペクトラム リテールメディアネットワークは多様なプロダクトタイプを提供する——スポンサードリスティングだけではありません。すべては `retail_media` チャネルを共有するが、フォーマット、価格設定、クリエイティブ要件が異なります。Danielは ShopGrid の全ポートフォリオをファネル全体でバイヤーエージェントが組み合わせられるよう、個別のプロダクトとしてモデル化しました。 ### スポンサードプロダクトリスティング 最もシンプルなコマースメディアプロダクト。小売業者はバイヤーのプロダクトカタログからクリエイティブをレンダリングする——カスタムクリエイティブのアップロードは不要。価格設定は通常CPC。 ```json theme={null} { "product_id": "shopgrid_sponsored_products", "name": "Sponsored Products - Search & Browse", "description": "Sponsored product listings in search results and category pages. Products are rendered from retailer catalog data.", "channels": ["retail_media"], "publisher_properties": [ { "publisher_domain": "shopgrid.example", "selection_type": "all" } ], "format_ids": [ { "agent_url": "https://ads.shopgrid.example", "id": "sponsored_product_listing" } ], "delivery_type": "non_guaranteed", "pricing_options": [ { "pricing_option_id": "sp_cpc", "pricing_model": "cpc", "floor_price": 0.25, "price_guidance": { "p50": 0.85, "p75": 1.20 }, "currency": "USD", "min_spend_per_package": 50 } ], "delivery_measurement": { "provider": "ShopGrid deterministic purchase attribution", "notes": "Deterministic purchase attribution from first-party shopper data. 14-day lookback." }, "outcome_measurement": { "type": "attributed_sales", "attribution": "deterministic_purchase", "window": { "interval": 14, "unit": "days" }, "reporting": "daily_api" }, "conversion_tracking": { "action_sources": ["website", "app", "in_store"], "supported_targets": ["cost_per", "per_ad_spend"], "platform_managed": true }, "creative_policy": { "co_branding": "none", "landing_page": "any", "templates_available": true }, "catalog_types": ["product"], "catalog_match": { "matched_gtins": ["00013000006408", "00013000006415", "00013000006422"], "matched_count": 3, "submitted_count": 1200 } } ``` `catalog_types` フィールドはこのプロダクトがサポートするカタログタイプを宣言します。`catalog_match` フィールドはバイヤーに対して、このプロダクトでどのカタログアイテムが対象かを伝える。バイヤーはメディアバイを作成する際に、これらの値をカタログセレクター(`gtins`、`ids`)として使用する——`publisher_properties` が絞り込みに使える利用可能なプロパティをリストするのと同様です。セラーは `matched_gtins`、`matched_ids`、またはその両方を含めることができます。 スポンサードプロダクトリスティングはカタログレンダリング式です。小売業者はGTIN照合によって自社カタログデータからタイトル、価格、画像、評価を取得します。拡張プロダクトコンテンツ(比較チャート、ライフスタイルギャラリー、ブランドストーリーモジュール)とブランドストアは、広告購入ではなく小売業者のコンテンツシステムを通じて管理される補完的なコンテンツ戦略です。 主な特徴: * **CPC価格設定**:オークションベースの入札 * **`platform_managed: true`**:小売業者が常時購買アトリビューションを提供 * **`supported_targets`**:パッケージに `optimization_goals` を設定する際にどのターゲット種別が利用可能かをバイヤーに伝える * **`templates_available: true`**:小売業者がカタログデータからクリエイティブをレンダリング * **`action_sources`** に `in_store` が含まれ、オムニチャネルアトリビューションを実現 ### オンサイトディスプレイとビデオ 小売業者のプロパティ上のディスプレイおよびビデオ広告で、小売業者のファーストパーティショッパーデータを使ってターゲティングします。バイヤーは標準クリエイティブを提供します。高い最低出稿額とCPMは、小売業者のオーディエンスデータとギャランティードプレースメントの価値を反映しています。 ```json theme={null} { "product_id": "shopgrid_onsite_display", "name": "Category Shoppers - On-Site Display & Video", "description": "Display and video ads on ShopGrid marketplace and app, targeting shoppers with relevant purchase history.", "channels": ["retail_media"], "publisher_properties": [ { "publisher_domain": "shopgrid.example", "selection_type": "all" } ], "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_728x90" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_15s" } ], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "onsite_cpm", "pricing_model": "cpm", "fixed_price": 14.00, "currency": "USD", "min_spend_per_package": 10000 } ], "delivery_measurement": { "provider": "Retailer ad server with IAS viewability", "notes": "Impressions per IAB guidelines. MRC-accredited viewability." }, "outcome_measurement": { "type": "incremental_sales_lift", "attribution": "deterministic_purchase", "window": { "interval": 30, "unit": "days" }, "reporting": "weekly_dashboard" }, "conversion_tracking": { "action_sources": ["website", "app"], "supported_targets": ["cost_per", "per_ad_spend"], "platform_managed": true }, "creative_policy": { "co_branding": "required", "landing_page": "retailer_site_only", "templates_available": true } } ``` クリエイティブポリシーに注目:小売業者はコブランディングを必須とし、ランディングページを自社サイトに制限することが多い。 ### オフサイトオーディエンス拡張 小売業者はファーストパーティ購買データを使って、サードパーティのインベントリでオーディエンスにターゲティングする——小売業者自身のサイトを超えてリーチを拡大します。バイイングコンテキストが小売業者のデータアセットであるため、このプロダクトは依然として `retail_media` チャネルに属します。 ```json theme={null} { "product_id": "shopgrid_offsite_extension", "name": "Shopper Audiences - Off-Site Display & Video", "description": "Reach ShopGrid shoppers across premium display and video inventory using first-party purchase data.", "channels": ["retail_media"], "publisher_properties": [ { "publisher_domain": "shopgrid.example", "selection_type": "all" } ], "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_15s" } ], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "offsite_cpm", "pricing_model": "cpm", "fixed_price": 13.50, "currency": "USD", "min_spend_per_package": 10000 } ], "delivery_measurement": { "provider": "Self-reported impressions from proprietary ad server", "notes": "Impressions counted per IAB guidelines. Viewability via IAS." }, "outcome_measurement": { "type": "incremental_sales_lift", "attribution": "deterministic_purchase", "window": { "interval": 30, "unit": "days" }, "reporting": "weekly_dashboard" }, "conversion_tracking": { "action_sources": ["website", "app", "in_store"], "supported_targets": ["cost_per", "per_ad_spend"], "platform_managed": true } } ``` ### プレミアムプレースメント ホームページテイクオーバー、カテゴリースポンサーシップ、季節イベントプレースメント。これらの高視認性ポジションは固定レートでギャランティード配信として販売される——多くの場合、かなり前から予約されます。 ```json theme={null} { "product_id": "shopgrid_homepage_takeover", "name": "Homepage Takeover - 24 Hours", "description": "Exclusive homepage banner and hero placement for 24 hours.", "channels": ["retail_media"], "publisher_properties": [ { "publisher_domain": "shopgrid.example", "selection_type": "all" } ], "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_970x250" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" } ], "placements": [ { "placement_id": "homepage_hero", "name": "Homepage Hero Banner", "format_ids": [{ "agent_url": "https://creative.adcontextprotocol.org", "id": "display_970x250" }] }, { "placement_id": "homepage_sidebar", "name": "Homepage Sidebar", "format_ids": [{ "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }] } ], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "takeover_flat", "pricing_model": "flat_rate", "fixed_price": 50000.00, "currency": "USD" } ], "delivery_measurement": { "provider": "Retailer ad server", "notes": "Guaranteed impressions based on homepage traffic projections." }, "outcome_measurement": { "type": "attributed_sales", "attribution": "deterministic_purchase", "window": { "interval": 14, "unit": "days" }, "reporting": "daily_api" }, "creative_policy": { "co_branding": "required", "landing_page": "retailer_site_only", "templates_available": false } } ``` プレミアムプレースメントは `placements` 配列を使うため、バイヤーは同じプロダクト内の異なるポジションに異なるクリエイティブを割り当てられます。 ### 店舗内デジタル 物理的な小売ロケーション内のデジタルスクリーン——レジレーン、エンドキャップ、入口ディスプレイ、待合エリア。これらはデジタルと物理の橋渡しをし、測定は店舗内での購買に紐づく。同期された店舗カタログと組み合わせると、店舗内デジタルプロダクトは特定のロケーションをターゲットにし、在庫を考慮したクリエイティブを表示できます。 店舗内こそ、リテールメディアネットワークが他の誰も複製できないインベントリを持っている場所です。数千の物理ロケーションを持つ小売業者には、購買ポイントにスクリーンがある——ショッパーが通路を歩き回り、棚で商品を比較し、列に並んで待っている間にリーチできます。これらのショッパーの多くはオンライン広告をまったく見ない。店舗内でのみ買い物するため、オンラインチャネルでは到達不可能です。 ```json theme={null} { "product_id": "shopgrid_instore_screens", "name": "In-Store Digital Screens", "description": "Digital screens at checkout lanes, end-cap displays, and pharmacy waiting areas across 2,000+ locations.", "channels": ["retail_media"], "publisher_properties": [ { "publisher_domain": "shopgrid.example", "selection_type": "all" } ], "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_1080x1920" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_15s" } ], "placements": [ { "placement_id": "checkout_screen", "name": "Checkout Lane Screens", "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_1080x1920" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_15s" } ] }, { "placement_id": "endcap_screen", "name": "Aisle End-Cap Displays", "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_1080x1920" } ] }, { "placement_id": "pharmacy_waiting", "name": "Pharmacy Waiting Area", "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_1080x1920" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_15s" } ] } ], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "instore_cpm", "pricing_model": "cpm", "fixed_price": 12.00, "currency": "USD", "min_spend_per_package": 5000 } ], "delivery_measurement": { "provider": "Venue traffic sensors", "notes": "Impressions estimated from foot traffic data. Updated weekly." }, "conversion_tracking": { "action_sources": ["in_store"], "platform_managed": true }, "creative_policy": { "co_branding": "none", "landing_page": "any", "templates_available": true } } ``` 異なるスクリーンプレースメントには異なるクリエイティブ要件があります。レジスクリーンは静止したショッパーが近距離で30〜90秒の滞留時間を提供します。エンドキャップディスプレイは通りすがりのショッパーを遠めから2〜3秒の一瞥でキャッチします。薬局の待合エリアは滞留時間が長く、着席したオーディエンスがいます。ほとんどの店舗内ネットワークは無音だ——プレースメントが特に指定しない限り、クリエイティブはサウンドオフで設計すべきです。 店舗内プロダクトは小売業者の店舗カタログと連携します。このカタログはキャッチメントエリア(半径、等時線、またはGeoJSONポリゴン)を持つ店舗ロケーションを公開し、バイヤーエージェントが特定のエリアや店舗クラスターをターゲットにできるようにします。ブランドのプロダクトカタログと組み合わせると、店舗内スクリーンはコンテキストに関連したクリエイティブを表示できる——適切な商品を、適切な店舗で、適切な棚の近くに。 ### これらの例を超えて リテールメディアポートフォリオは上記の5つのプロダクトタイプを超えることが多い。スポンサードブランド広告——ヘッドライン、ブランドロゴ、2〜3つのフィーチャードプロダクト——は、バイヤーからのカスタムヘッドラインとカタログレンダリングのプロダクトタイルを持つ、スポンサードプロダクトリスティングとディスプレイの中間に位置します。デジタルクーポンとキャッシュバックオファーは購買に直接結びつき、インプレッションベースモデルにはうまく収まらない——CPAプライシングと `conversion_tracking` を持つプロダクトとしてモデル化できます。小売業者のメールニュースレターやアプリのプッシュ通知のスポンサードプレースメントは、高パフォーマンスなロウファネルプロダクトで、異なる `format_ids` を持つオンサイトディスプレイと同様にモデル化されます。ショッパブルビデオ——ビデオフレーム内でプロダクトがタグ付けされ、視聴者がカートに追加できる——はビデオフォーマットとカタログレンダリングのプロダクトオーバーレイを組み合わせます。キーワードレベルの検索スポンサーシップ、サンプリングプログラム、レシピ統合はそれぞれ異なる経済性を持つが、同じパターンに従う。適切な価格モデル、フォーマット、測定宣言を持つプロダクトです。 ## エンドツーエンドのワークフロー ### プロダクトカタログを持つブランド バイヤーはプロダクトカタログフィードを含むブランドリファレンスを提供します。これによりSKUレベルのターゲティングとカタログレンダリングのクリエイティブが可能になります: ```json theme={null} { "name": "Summit Foods", "url": "https://summitfoods.example.com", "product_catalog": { "feed_url": "https://summitfoods.example.com/products.xml", "feed_format": "google_merchant_center", "categories": ["food/sauces", "food/condiments", "beverages"], "update_frequency": "daily" } } ``` フィードにはGTIN、タイトル、価格、画像が含まれており、スポンサードプロダクトリスティングのレンダリングと小売業者間でのプロダクトマッチングに必要なすべてが揃っています。 ### アカウントへのカタログ同期 メディアバイを作成する前に、`sync_catalogs` 経由でブランドのデータフィードを小売業者のアカウントに同期します。これにより配信時にクリエイティブとターゲティングが参照するアカウント状態が構築されます。これが全体的なセットアップシーケンスにどう合うかは [Account state](/docs/building/by-layer/L2/account-state) を参照。 ```json theme={null} { "account": { "account_id": "acct_summitfoods_shopgrid_001" }, "catalogs": [ { "catalog_id": "product-feed", "name": "Summit Foods Product Catalog", "type": "product", "url": "https://summitfoods.example.com/products.xml", "feed_format": "google_merchant_center", "update_frequency": "daily" }, { "catalog_id": "inventory-feed", "name": "Store-Level Inventory", "type": "inventory", "url": "https://feeds.summitfoods.example.com/inventory.json", "feed_format": "custom", "update_frequency": "hourly" } ] } ``` プラットフォームは各フィードを取り込む: * **プロダクトカタログ** — 小売業者自身のカタログに対してGTINを検証し、アイテムレベルの承認ステータスをレポート * **インベントリフィード** — クリエイティブが「近くで在庫あり」を表示できるよう、在庫データを毎時更新 **店舗ロケーションはセラー提供であり、バイヤーが同期するものではありません。** リテールメディアでは、小売業者は自社の店舗ロケーションを所有している——CPGブランドは小売業者に小売業者の店舗を同期しません。小売業者の店舗カタログ(近接ターゲティングのためのキャッチメントエリアを持つ)はプラットフォームサイドのデータであり、バイヤーはターゲティングで参照できます。自社の物理ロケーションを運営するバイヤー(例:実店舗を持つDTCブランド、レストランチェーン)は、`sync_catalogs` を通じて自社の店舗カタログを他のプラットフォームに同期することになります。 同期されたカタログが必要なフォーマットは、`assets` 配列に `catalog` アセットタイプを宣言する——バイヤーエージェントはクリエイティブを提出する前にこれをチェックします。スポンサードプロダクトカルーセルには `product` カタログと `inventory` カタログの両方が必要な場合があります。 ### プロダクトの検索 `channels: ["retail_media"]` を指定して `get_products` をクエリし、コマースメディアプロダクトを見つける。ブランドアイデンティティにより、セラーはカタログ適格性でフィルタリングできます: ```json theme={null} { "brief": "Promote our organic ketchup line across grocery retailers. Focus on high-intent shoppers.", "brand": { "domain": "summitfoods.example.com" }, "filters": { "channels": ["retail_media"] } } ``` セラーは、バイヤーのGTINが小売業者のカタログにマッチするプロダクトを返します。マルチプロダクト小売業者は、スポンサードプロダクト、オンサイトディスプレイ、オフサイト拡張を個別のプロダクトとして返すことがあります。 スポンサードプロダクトリスティングについて、セラーは各プロダクトに `catalog_match` を含め、バイヤーのカタログアイテムのどれが対象かを示す: ```json theme={null} { "product_id": "shopgrid_sponsored_products", "catalog_match": { "matched_gtins": ["00013000006408", "00013000006415", "..."], "matched_count": 3, "submitted_count": 1200 } } ``` これはバイヤーに対して、この小売業者でどのGTINが対象で、全カタログアイテムの何件が評価されたかを伝える。バイヤーはメディアバイを作成する際に `matched_gtins` の値をカタログの `gtins` セレクターとして使用するか、カタログカバレッジを考慮して `tags` や `category` のような広いセレクターを使用します。 セラーはまた、プロダクトタイプ間での推奨予算配分を持つ[プロポーザル](/docs/media-buy/product-discovery/media-products#proposals)を `get_products` レスポンスとともに返すことができる——これは従来、人間の営業担当が必要だったメディアプランニングの専門知識をエンコードするものです。 ### メディアバイの作成 単一のメディアバイが複数のプロダクトタイプと小売業者にまたがることができます。各パッケージは異なるプロダクトをターゲットにします: ```json theme={null} { "account": { "account_id": "acct_summitfoods_shopgrid_001" }, "brand": { "domain": "summitfoods.example.com" }, "start_time": "2026-04-01T00:00:00Z", "end_time": "2026-04-30T23:59:59Z", "packages": [ { "product_id": "shopgrid_sponsored_products", "pricing_option_id": "sp_cpc", "budget": 15000, "bid_price": 1.10, "pacing": "even", "optimization_goals": [{ "kind": "event", "event_sources": [ { "event_source_id": "shopgrid_purchases", "event_type": "purchase", "value_field": "value" } ], "target": { "kind": "per_ad_spend", "value": 3.0 }, "priority": 1 }] }, { "product_id": "shopgrid_onsite_display", "pricing_option_id": "onsite_cpm", "budget": 25000, "pacing": "even" }, { "product_id": "shopgrid_homepage_takeover", "pricing_option_id": "takeover_flat", "budget": 50000 } ] } ``` `account` はこの小売業者とのブランドの請求関係を識別します。スポンサードプロダクトパッケージは、どのアイテムをプロモーションするかを指定するためにカタログセレクター(`gtins`、`tags`)を使用し、`optimization_goals` に `per_ad_spend` ターゲットを設定して、小売業者に購買額の3倍のリターンに向けて配信を最適化するよう伝える。ディスプレイとプレミアムパッケージは標準的なクリエイティブワークフローを使用します。 ### 配信レポーティング コマースメディアの配信レポートには、従来のメディアにはないアトリビューションメトリクスが含まれます。`attribution_window` はクロスプラットフォーム比較のための測定方法論を透明にします: ```json theme={null} { "reporting_period": { "start": "2026-04-01T00:00:00Z", "end": "2026-04-14T23:59:59Z" }, "currency": "USD", "attribution_window": { "post_click": { "interval": 14, "unit": "days" }, "post_view": { "interval": 1, "unit": "days" }, "model": "last_touch" }, "media_buy_deliveries": [ { "media_buy_id": "mb_shopgrid_001", "status": "active", "totals": { "impressions": 850000, "spend": 10350 }, "by_package": [ { "package_id": "pkg_sp_001", "pricing_model": "cpc", "rate": 0.92, "currency": "USD", "impressions": 850000, "spend": 10350, "clicks": 11250, "conversions": 2100, "conversion_value": 28500, "roas": 2.75, "cost_per_acquisition": 4.93, "by_action_source": [ { "action_source": "website", "count": 1500, "value": 20000 }, { "action_source": "app", "count": 350, "value": 4800 }, { "action_source": "in_store", "count": 250, "value": 3700 } ], "delivery_status": "delivering" } ] } ] } ``` `by_action_source` の内訳は、コンバージョンがどこで発生したかを示す——ウェブサイト、アプリ、実店舗。このオムニチャネルビューはコマースメディア固有のものです。 ## クローズドループアトリビューション Daniel and Sam view a closed-loop attribution dashboard — impressions flow through clicks, store visits, and purchases in a circular diagram with website, app, and in-store sources コマースメディアの決定的な優位性は、決定論的な購買アトリビューションです。小売業者はロイヤルティカードデータ、ログイン状態、POSレコードを使って広告露出とトランザクションをマッチングします。これこそ、リテールメディアを他のすべてのメディアチャネルと根本的に異なるものにするアセットだ——そして、エージェントストアフロントが標準の配信レポーティングを通じてバイヤーエージェントに公開するものです。 ### プロダクトによる宣言方法 プロダクトは2つのフィールドを通じてクローズドループ機能を示す: **`outcome_measurement`** はアトリビューション方法論を説明します: ```json theme={null} { "outcome_measurement": { "type": "attributed_sales", "attribution": "deterministic_purchase", "window": { "interval": 14, "unit": "days" }, "reporting": "daily_api" } } ``` **`conversion_tracking`** はアクションソースと小売業者が測定を管理するかどうかを宣言します: ```json theme={null} { "conversion_tracking": { "action_sources": ["website", "app", "in_store"], "supported_targets": ["cost_per", "per_ad_spend"], "platform_managed": true } } ``` `platform_managed` が `true` の場合、小売業者が常時測定を提供します。バイヤーサイドのピクセルやイベントソースの設定は不要です。 小売業者によってアトリビューションウィンドウが異なる(例:クリック14日 vs クリック7日)。配信レスポンスの `attribution_window` はこれを透明にするため、バイヤーは小売業者間でROASの比較を正規化できます。 ### 主要な配信メトリクス | メトリクス | フィールド | 説明 | | ----------- | ---------------------- | -------------------------------- | | 広告費用対効果 | `roas` | `conversion_value / spend` | | アトリビューテッド収益 | `conversion_value` | 広告にアトリビュートされた総購買額 | | コンバージョン | `conversions` | 広告にアトリビュートされた購買イベント数 | | 獲得単価 | `cost_per_acquisition` | `spend / conversions` | | ニューカスタマー率 | `new_to_brand_rate` | 初回購入者からのコンバージョンの割合 | | 店舗内売上 | `by_action_source` | `website`、`app`、`in_store` 別の内訳 | | イベントタイプ別 | `by_event_type` | `purchase`、`add_to_cart` などによる内訳 | Daniel by a retail storefront and Priya by a streaming TV shape flank Sam in the center — different sell-side verticals connecting to one buyer through the same protocol ## 従来のメディアバイとの違い | 側面 | 従来のメディア | コマースメディア | | --------------- | ----------------- | ------------------------------------ | | アトリビューション | 確率論的、モデルベース | 決定論的、購買ベース | | ターゲティングデータ | サードパーティ、コンテクスチュアル | ファーストパーティの購買/ロイヤルティデータ | | 主要KPI | インプレッション、クリック、CTR | ROAS、コンバージョン、アトリビューテッド収益 | | クリエイティブ(スポンサード) | バイヤー提供 | 小売業者がカタログからレンダリング | | 測定の所有者 | サードパーティ(IAS、DV) | 小売業者プラットフォーム(`platform_managed`) | | コンバージョンソース | ウェブサイトのみ | ウェブサイト + アプリ + 店舗内 | | ランディングページ | 任意の目的地 | 多くの場合、小売業者サイトのみ | | 価格モデル | CPM、CPC、CPCV | CPC(スポンサード)、CPM(ディスプレイ)、固定レート(プレミアム) | ## ベストプラクティス ### 小売業者向け:エージェントストアフロントの立ち上げ デフォルトのリテールメディアのプレイブック——セルフサービスプラットフォームを構築し、営業チームを採用し、マネージドサービス収益を伸ばし、最終的にDSPを構築する——は最大の小売業者には機能します。それ以外の場合、桁違いのリソースを持つプラットフォームと競いながら、何年ものエンジニアリング投資が必要になります。 エージェントストアフロントは経済性を変える。ブランドが学ばなければなりませんプラットフォームを構築する代わりに、バイヤーエージェントがすでに使いこなしている標準プロトコルでインベントリを公開します。エンジニアリング投資は実際の差別化要因——データ品質、測定精度、インベントリの幅——に向けられ、ダッシュボードのUXには向けられない。 1. **プロダクトタイプを個別にモデル化する** — スポンサードプロダクト、オンサイトディスプレイ、オフサイト、プレミアムプレースメント、店舗内デジタルは、適切な価格設定とフォーマットを持つ個別のプロダクトであるべきです。これにより、バイヤーエージェントが小売業者間で比較検討できます。 2. **データを前面に出す** — `deterministic_purchase` アトリビューションと `platform_managed: true` の `conversion_tracking` を持つ `outcome_measurement` を宣言します。これがバイヤーエージェントが最適化する対象です。測定が弱い小売業者は、UIの品質に関わらず、測定が強い小売業者に負ける。 3. **物理的なフットプリントを公開する** — バイヤーエージェントが地理でターゲティングできるよう、キャッチメントエリアを持つ店舗カタログを提供します。店舗内デジタルインベントリは、オンラインのみのプラットフォームが提供できないものです。発見可能にすること。 4. **スポンサードプロダクトリスティングに `catalog_match` を返す** — バイヤーがどのGTIN/SKUが対象かを確認できるようにします。これは営業担当が「あなたの1,200商品のうち3点を取り扱っています」と言うのと同等だが——クエリ時に自動的に発生します。 5. **配信レスポンスに `attribution_window` を含める** — 小売業者間で比較するバイヤーは、ルックバックウィンドウとモデルを知る必要があります。 6. **`by_action_source` をレポートする** — ウェブサイト、アプリ、店舗内コンバージョンを示すオムニチャネルの影響を報告します。クロスチャネルビューはコマースメディア固有のものであり、予算配分を促進します。 7. **プロポーザルを使用する** — `get_products` レスポンスとともに、プロダクトタイプ間での推奨予算配分を返します。これにより、営業担当との会話を、バイヤーエージェントが評価できる構造化データに置き換え、メディアプランニングの専門知識をプロトコルにエンコードします。 8. **すべてのコマースメディアプロダクトに `channels: ["retail_media"]` を設定する** — バイヤーがチャネルでフィルタリングできるようにします。 ### ブランド向け 1. **アカウントを早期にセットアップする** — バイを行う前に `list_accounts` を使って各小売業者との請求関係を確認します 2. **プロダクトとインベントリフィードを早期に同期する** — プロダクトとインベントリのカタログはバイを作成する前にアカウントに同期しておくべきです。インベントリフィードは毎時更新、プロダクトフィードは毎日更新されます。 3. **ターゲティングで小売業者の店舗カタログを参照する** — 小売業者の店舗ロケーションとキャッチメントエリアはプラットフォームサイドのデータです。自社の店舗カタログを同期せずに近接ターゲティングに使用します。 4. **`optimization_goals` を使用する** — パッケージに `cost_per` または `per_ad_spend` ターゲットを持つイベントゴールを設定し、小売業者が購買データに対して配信を最適化できるようにします 5. **カタログセレクターを戦略的に使用する** — 特定の商品には `gtins`、広範なプロモーションには `tags` や `category` 6. **ファネル全体に予算配分する** — コンバージョンにはスポンサードプロダクト、認知度向上にはオンサイトディスプレイ、リーチ拡大にはオフサイト 7. **アトリビューションウィンドウを比較する** — 異なるルックバックウィンドウを持つ小売業者間でROASを正規化するために、配信レポートの `attribution_window` を使用します ## 関連ドキュメント * [Account state](/docs/building/by-layer/L2/account-state) — カタログ、イベントソース、キャンペーンがアカウント上にどう構築されるか * [Accounts & Agents](/docs/building/by-layer/L2/accounts-and-agents) — アカウントのセットアップと請求関係 * [Media Channel Taxonomy](/docs/reference/media-channel-taxonomy) — `retail_media` チャネルの定義 * [Media Products](/docs/media-buy/product-discovery/media-products) — プロダクトモデルのリファレンス * [Catalogs](/docs/creative/catalogs) — プロダクト、インベントリ、店舗、プロモーションのフィード * [Brand identity](/docs/brand-protocol/brand-json) — プロダクトカタログとブランドアイデンティティ * [Pricing Models](/docs/media-buy/advanced-topics/pricing-models) — CPC、CPM、固定レートの詳細 * [Optimization & Reporting](/docs/media-buy/media-buys/optimization-reporting) — `optimization_goals` とコンバージョン最適化 * [Delivery Reporting](/docs/media-buy/task-reference/get_media_buy_delivery) — コマースアトリビューションメトリクス * [Targeting](/docs/media-buy/advanced-topics/targeting) — ブリーフベースのターゲティングアプローチ # Media buy プロトコル Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/index AdCP media buy プロトコルのウォークスルー ― CTV、ディスプレイ、オーディオを横断するキャンペーンを、ひとつの統一ワークフローでブリーフから配信まで追う。 メディアバイヤーが机に座り、それぞれ異なるデータ形式とインターフェースを表示する4つのプラットフォームダッシュボードに囲まれている Sam は Pinnacle Agency のメディアバイヤーです。クライアントが Acme Outdoor の Trail Pro 3000 向け Q2 キャンペーン ― スポーツとアウトドアライフスタイル系パブリッシャーへのプレミアム動画・ディスプレイ広告、予算 \$50,000 ― をようやくGoしました。評価すべきセラーが3社。合わせるべきクリエイティブあり。配信前にはガバナンスレビューも必要です。 前回これほどの規模のキャンペーンを回したときは2週間かかりました。4つのダッシュボード。4つのログイン。決して同じフォーマットでは届かないプロポーザルを比較するためのスプレッドシート。フライト日程を4つの別々のシステムに入力しました。 このウォークスルーでは、Sam が AdCP を使って同じキャンペーンを進める様子を追います ― プラットフォームごとに異なる4つのワークフローを、ひとつのプロトコルで置き換えます。 ## ステップ 1: ブリーフを書く Sam は自分が把握していることから始めます。キャンペーンの目標です。 Sam のブリーフが画面上で光り、清潔なティール色の光線として3体のセラーエージェントロボットへと放射されています。各ロボットは自分のポディウムに立ち、届いたリクエストを検討している AdCP では、ブリーフは `get_products` の中の自然言語です。Sam は各パブリッシャーのターゲティング分類や在庫カテゴリを覚える必要はありません ― 欲しいものを説明すれば、各セールスエージェントが自社在庫に照らして解釈してくれます。 ```javascript theme={null} const products = await Promise.all( sellers.map(seller => seller.getProducts({ buying_mode: "brief", brief: "Premium video inventory on sports and outdoor lifestyle publishers. Q2 flight, $50K budget. Adults 25-54, US and Canada.", brand: { domain: "acmeoutdoor.com" }, account: { brand: { domain: "acmeoutdoor.com" }, operator: "pinnacle-agency.com" } })) ); ``` ブリーフひとつ。セラー3社。返ってくるのは同じ JSON 構造です。 | Sam の言い方 | プロトコルの呼び方 | | ------------------------ | -------------------------------------------------------------------------------------------------------- | | キャンペーンブリーフ | `get_products` の `brief` フィールド | | メディアプラン | `get_products` から返されるプロダクト | | IO / 挿入指示書 | `create_media_buy` | | クリエイティブのトラフィッキング | ライブラリを持つセラーには `sync_creatives`、インライン専用のセラーにはインラインの `packages[].creatives` | | キャンペーンレポート | エージェント横断での `get_media_buy_delivery` | | フライト期間 | メディアバイまたはパッケージの `start_time` / `end_time` | | ビューアビリティ / IVT / 完了の SLA | パッケージの `performance_standards` — [アカウンタビリティ](/docs/media-buy/advanced-topics/accountability)を参照 | | メイクグッド | `measurement_terms` の `makegood_policy` — [アカウンタビリティ](/docs/media-buy/advanced-topics/accountability)を参照 | ## ステップ 2: プロポーザルを比較します 3体のセラーロボットがそれぞれの提案を披露している ― 動画スレート、ディスプレイバナー、ポッドキャストの波形。Sam はひとつの清潔な画面でそれらを並べてレビューし、満足そうな表情を浮かべている プロダクトは標準フォーマットで返ってきます。Sam は初めて、価格・配信予測・ターゲティングオプション・クリエイティブ要件を ― すべて並べて ― 見ることができます。 | セラー | プロダクト | CPM | 予測 | フォーマット | | ---------- | --------------------- | ---- | ------------ | ------------------ | | StreamHaus | CTV スポーツ プレロール | \$28 | 89万インプレッション | SSAI 30s 動画 | | OutdoorNet | アドベンチャーライフスタイル ディスプレイ | \$12 | 210万インプレッション | 300x250, 728x90 | | PodTrail | アウトドア ポッドキャスト ミッドロール | \$22 | 34万インプレッション | オーディオ 30s + コンパニオン | CSV なし。スプレッドシートなし。手入力なし。すべてのセラーが同じスキーマを返すため、プロダクトは比較可能です。 Sam は絞り込みたいと考えています。`refine` モードに切り替え、各エージェントに調整の指示を出します。 ```javascript theme={null} const refined = await seller.getProducts({ buying_mode: "refine", refine: [ { scope: "request", ask: "Only guaranteed packages. Must include completion rate SLA above 80%." } ], brand: { domain: "acmeoutdoor.com" }, account: { brand: { domain: "acmeoutdoor.com" }, operator: "pinnacle-agency.com" } }); ``` `refine` 配列を使えば、最初からやり直すことなく制約を重ねられます。各リファインメントは前の結果セットをさらに絞り込みます。 ## ステップ 3: クリエイティブを合わせる クリエイティブフォーマットのテンプレート ― 16:9 の動画フレーム、300x250 のバナー、コンパニオン付きのオーディオ波形 ― が小さなロボットに助けられながら実際の広告素材とパズルのピースのようにはまり合っている 各プロダクトにはクリエイティブ要件があります。Sam のプラットフォームは各セラーに `list_creative_formats` を呼び出し、必要なものを正確に把握します。 * **StreamHaus** は SSAI 対応の 30 秒動画(MP4、特定コーデック)が必要です * **OutdoorNet** はディスプレイバナー(300x250 と 728x90)が必要です * **PodTrail** は 30 秒オーディオと 300x250 コンパニオンバナーが必要です Sam のクリエイティブチームはすでにライブラリに素材を持っています。プラットフォームは既存のマニフェストを各セラーのフォーマット要件に照合し、ギャップを検出します ― PodTrail にはまだ存在しないオーディオカットが必要です。 ```javascript theme={null} const result = await seller.syncCreatives({ account: { brand: { domain: "acmeoutdoor.com" }, operator: "pinnacle-agency.com" }, creatives: [ { creative_id: "video_30s_trail_pro", name: "Trail Pro 3000 - 30s CTV Spot", format_id: { agent_url: "https://streamhaus.example", id: "ssai_30s" }, assets: { video: { url: "https://cdn.pinnacle-agency.example/trail-pro-30s.mp4", mime_type: "video/mp4" } } }, { creative_id: "display_trail_pro_300x250", name: "Trail Pro 3000 - Display 300x250", format_id: { agent_url: "https://outdoornet.example", id: "display_300x250" }, assets: { image: { url: "https://cdn.pinnacle-agency.example/trail-pro-300x250.png", mime_type: "image/png" } } } ] }); if (result.errors) { console.error('Sync failed:', result.errors); } else { console.log(`Synced ${result.creatives.length} creatives`); } ``` ## ステップ 4: キャンペーンを開始します Sam が光るランチボタンを押すと、ホログラフィックのキャンペーン設計図が机の上に現れ、CTV・ディスプレイ・オーディオの3つのブランチが広がり、それぞれに予算額が流れている Sam はメディアバイを作成します。セラーごとに1回の呼び出し、どこでも同じ構造です。 ```javascript theme={null} const buy = await seller.createMediaBuy({ account: { brand: { domain: "acmeoutdoor.com" }, operator: "pinnacle-agency.com" }, brand: { domain: "acmeoutdoor.com" }, start_time: "2026-04-01T00:00:00Z", end_time: "2026-06-30T23:59:59Z", packages: [{ product_id: "streamhaus_sports_preroll_q2", budget: 25000, pricing_option_id: "cpm_standard", creative_assignments: [{ creative_id: "video_30s_trail_pro" }] }] }); ``` セラーはクリエイティブを検証し、バイを承認するかレビューに回します。Sam はどのダッシュボードにもログインしません ― ステータス更新はプロトコルが処理します。 ## ステップ 5: ガバナンスチェック キャンペーン設計図がガバナンスロボットの配置するセキュリティチェックポイントを通過し、予算・ブランドセーフティ・ターゲティングコンプライアンスの3つのブランチそれぞれに緑のチェックマークが押されている 資金が動く前に、Sam のガバナンスエージェントがバイを検証します。 * **予算**: \$25K は Sam の承認済み支出限度額の範囲内です * **ブランドセーフティ**: StreamHaus は Acme Outdoor の承認済みパブリッシャーリストに掲載されています * **コンプライアンス**: ターゲティングパラメーターは米国・カナダの規制要件を満たしています * **クリエイティブ**: すべてのクリエイティブに必要な来歴メタデータが付与されています バイが Sam の権限を超える場合 ― たとえばセラー全体の合計が \$75K に達した場合 ― ガバナンスエージェントはマネージャーにエスカレーションします。`create_media_buy` タスクは、人間が承認するまでタスクレイヤーで `submitted`(または `input-required`)にとどまります。承認されて初めてタスクが完了し、メディアバイが `media_buy_id` を得ます。 キャンペーンガバナンスは、いかなるガバナンスチェックの前にも、オーケストレーターが [`sync_plans`](/docs/governance/campaign/tasks/sync_plans) を通じてキャンペーンプランを登録することを要求します。プランは、認可されたパラメータ ― 予算限度、チャネル、フライト日、コンプライアンスポリシー ― を定義し、後続のすべてのアクションがそれに対して検証されます。ガバナンスの完全なシーケンスは `sync_plans` → `check_governance`(提案)→ `create_media_buy` → `check_governance`(セラーによるコミット)です。完全な仕様については[キャンペーンガバナンス](/docs/governance/campaign/index)を参照してください。 ```javascript theme={null} // Step 1: Register the campaign plan with the governance agent const plan = await governance.syncPlans({ plans: [{ plan_id: "acme-q2-trail-pro", brand: { domain: "acmeoutdoor.com" }, objectives: "Q2 Trail Pro 3000 launch across sports and outdoor lifestyle publishers", budget: { total: 50000, currency: "USD", reallocation_threshold: 5000 }, flight: { start: "2026-04-01T00:00:00Z", end: "2026-06-30T23:59:59Z" }, countries: ["US", "CA"] }] }); // Step 2: Check governance before sending to seller (intent check) const check = await governance.checkGovernance({ plan_id: "acme-q2-trail-pro", caller: "https://orchestrator.pinnacle-agency.example", tool: "create_media_buy", payload: buy }); if (check.status === "denied") { // Don't proceed — governance rejected the plan } // Step 3: Send the buy to the seller with governance_context attached const governanceContext = check.governance_context; const mediaBuy = await seller.createMediaBuy({ ...buy, governance_context: governanceContext }); // Step 4: The seller independently calls check_governance with media_buy_id + // planned_delivery before confirming — validating against the same plan ``` ## ステップ 6: 配信時にマッチングします キャンペーンは承認され、稼働しています。ユーザーが StreamHaus のページを読み込む、OutdoorNet のアプリを開く、または AI アシスタントに質問すると、パブリッシャーの TMP ルーターが、Sam のどのパッケージを有効化すべきかを評価します。 二つの操作が別々に実行されます ― コンテキストマッチが「このコンテンツはパッケージのターゲティングに合うか?」を問い、アイデンティティマッチが「このユーザーは適格か?」を問います。パブリッシャーは両方のレスポンスをローカルで結合します。Sam のバイヤーエージェントは、ユーザーのアイデンティティとコンテンツのコンテキストを同時に見ることは決してありません ― この構造的な分離はプロトコルに組み込まれています。 同じフローがすべてのサーフェスで機能します。Sam は CTV、ウェブ、AI のそれぞれに対して、サーフェス固有の有効化コードを書きませんでした。TMP がそれらすべてを扱います。 ユーザーがハイキングギアに関する StreamHaus の記事を訪れると: 1. StreamHaus は、記事のコンテンツシグナルと Sam の利用可能なパッケージを添えてコンテキストマッチのリクエストを送ります 2. Sam のバイヤーエージェントが応答します: 「pkg-outdoor-display を有効化 ― このハイキングコンテンツはターゲティングに一致する」 3. StreamHaus は、ユーザートークンと Sam のすべてのアクティブなパッケージを添えて、別のアイデンティティマッチのリクエストを送ります 4. Sam のバイヤーエージェントが応答します: 「このユーザーは pkg-outdoor-display に適格(intent\_score: 0.82)」 5. StreamHaus は結果をローカルで結合し、ラインアイテムを有効化します 完全な仕様については[トラステッドマッチプロトコル](/docs/trusted-match)を参照してください。 ## ステップ 7: 配信を監視します Sam が机でリラックスして背もたれにもたれ、3社のセラーからの統合パフォーマンスチャートを表示する1つの清潔なダッシュボードを見ている ― 棒グラフが上昇し、折れ線グラフが収束し、すべてティール色で描かれている キャンペーンが動き始めました。Sam はひとつのビューで監視します ― プラットフォームは各セラーに `get_media_buy_delivery` を呼び出して結果をマージします。 ```javascript theme={null} const delivery = await seller.getMediaBuyDelivery({ account: { brand: { domain: "acmeoutdoor.com" }, operator: "pinnacle-agency.com" }, media_buy_ids: [buy.media_buy_id], include_package_daily_breakdown: true }); ``` すべてのセラーが同じフォーマットでレポートします: インプレッション、クリック、消化金額、完了率。Sam は4つではなく1つのダッシュボードを見ます。StreamHaus の CTV パッケージが未配信になったとき、彼は OutdoorNet に予算を再配分します ― 2つのプラットフォームにログインする代わりに、1回の `update_media_buy` 呼び出しで済みます。 ## 全体像 メディアバイの5つのステージを示す横並びのパイプライン: Discovery(虫眼鏡)、Planning(設計図)、Execution(ロケット打ち上げ)、Optimization(ダイヤル調整)、Reporting(清潔なダッシュボード) Sam は4つのダッシュボードから1つのプロトコルへと移行しました。CTV 在庫を購入したのと同じタスクが、ディスプレイとオーディオも購入しました ― プラットフォーム固有のコードなし、手動のデータ変換なし、スプレッドシートの突合せなしです。 | AdCP 導入前 | AdCP 導入後 | | --------------------- | -------------------------------- | | 4つのダッシュボード、4つのログイン | 1つのプロトコル、1つのビュー | | 手動 CSV 比較 | 標準化されたプロダクトプロポーザル | | プラットフォーム固有のクリエイティブ仕様 | 任意のセラーへの `list_creative_formats` | | 4つのキャンペーン設定ワークフロー | どこでも `create_media_buy` | | 手動レポート突合せ | `get_media_buy_delivery` で集約 | | サーフェスごとの有効化のアドオペレーション | TMP が任意のサーフェスでパッケージを自動的にマッチング | ## さらに深く学ぶ * **プロダクト探索**: [`get_products` の仕組み](/docs/media-buy/product-discovery/media-products) ― ブリーフ、ホールセールモード、プロポーザル、リファインメント * **キャンペーンライフサイクル**: [メディアバイの管理](/docs/media-buy/media-buys/index) ― ステータス遷移、更新、承認 * **最適化**: [配信とレポーティング](/docs/media-buy/media-buys/optimization-reporting) ― 指標、ディメンション別内訳、フィードバックループ * **ガバナンス**: [キャンペーンガバナンス](/docs/governance/overview) ― 三者間信頼モデルが Sam の支出を保護する仕組み * **クリエイティブ**: [クリエイティブウォークスルー](/docs/creative/index) ― Maya が Sam の使うクリエイティブをどのように作るか * **リアルタイムマッチング**: [トラステッドマッチプロトコル](/docs/trusted-match) ― コンテキストマッチとアイデンティティマッチを通じて、配信時にパッケージがどのように有効化されるか * **認定を取得する**: [バイヤートラック](/docs/learning/tracks/buyer)では、インタラクティブなモジュールを通じてメディアバイの完全なワークフローを学べます # Media Buy Lifecycle Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/media-buys/index AdCP のメディアバイライフサイクル — create_media_buy、update_media_buy、get_media_buys タスクを使って、セラーをまたいでキャンペーンを作成、更新、モニタリング、最適化します。 メディアバイは、AdCP における広告キャンペーンの完全なライフサイクルを表します。AdCP:Buy プロトコルは、最初のキャンペーン作成から継続的な最適化と更新まで、複数の広告プラットフォームにまたがってメディアバイを管理する統一されたインターフェースを提供します。 ## 概要 AdCP のメディアバイ管理は、次のための統一されたインターフェースを提供します: * 発見されたプロダクトとパッケージからの**キャンペーン作成** * すべてのキャンペーン状態を通じた**ライフサイクル管理** * 継続的な最適化のための**予算とターゲティングの更新** * 一貫した操作による**クロスプラットフォームのオーケストレーション** * 人間参加型のサポートを伴う**非同期オペレーション** ## メディアバイのライフサイクルフェーズ ### 1. 作成フェーズ [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) を使って、発見されたプロダクトをアクティブな広告キャンペーンに変換します: * **パッケージ設定**: プロダクトをフォーマット、ターゲティング、予算と組み合わせる * **キャンペーンのセットアップ**: タイミング、全体予算、ブランドコンテキストを定義する * **検証と承認**: 任意の人による承認を伴う自動チェック * **プラットフォームへのデプロイ**: 広告プラットフォームにまたがるキャンペーン作成 このフェーズには次が含まれる場合があります: * `active` ステータスでの即時作成(即時有効化) * バイヤーがトップレベルの `paused: true` を渡し、有効化の前提条件がそれ以外は満たされている場合の、`paused` ステータスでの保留作成 * `pending_creatives` ステータス(クリエイティブの割り当て待ち)または `pending_start` ステータス(配信準備完了、フライト日待ち)での遅延作成 * `pending_manual` タスクステータスによる人間の承認ワークフロー([非同期オペレーション](#非同期オペレーションと人間参加型)を参照) * `pending_permission` タスクステータスによる権限要件([非同期オペレーション](#非同期オペレーションと人間参加型)を参照) `pending_manual` と `pending_permission` は、人間参加型のキューに由来する**タスクレベル**のステータスです——これらは*操作*が承認を必要とするかどうかを記述するものであり、メディアバイのライフサイクル状態ではありません。メディアバイ自体は、操作が完了すると `pending_creatives`、`pending_start`、`active`、または `paused` に入ります。 **プラットフォームのマッピング:** * **Google Ad Manager**: LineItem を持つ Order を作成 * **Kevel**: Flight を持つ Campaign を作成 * **Triton Digital**: Flight を持つ Campaign を作成 ### 2. クリエイティブ供給フェーズ 作成されると、メディアバイは、セラーが表明している経路を通じてクリエイティブアセットを必要とします: ライブラリを持つセラーには [`sync_creatives`](/docs/creative/task-reference/sync_creatives)、インライン専用のセラーには [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) と [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) のインライン `packages[].creatives` です。 * **プラットフォーム固有のフォーマットサポート**(動画、音声、ディスプレイ、カスタム) * クリエイティブのコンプライアンスのための**検証とポリシーレビュー** * ターゲット配信のための**特定パッケージへの割り当て** ### 3. 有効化・配信フェーズ アクティブなキャンペーンをモニタリングし管理します: * **ステータストラッキング**: キャンペーンが `pending_creatives` から `pending_start`、そして `active` へ遷移する、または配信を保留して作成された場合は `paused` へ * **クリエイティブの割り当て**: クリエイティブライブラリからアセットを添付 * **配信モニタリング**: [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) でペーシングとパフォーマンス指標を追跡 * **問題の解決**: 承認の遅延やプラットフォームの問題に対応 ### 4. 最適化とレポートフェーズ AdCP の包括的なレポートツールを使った、継続的なパフォーマンスモニタリングとデータ駆動のキャンペーン最適化。 主な活動には次が含まれます: * リアルタイムおよび履歴分析による**パフォーマンスモニタリング** * 予算の再配分とターゲティングの絞り込みによる**キャンペーン最適化** * 一貫した分析のために同じターゲティング次元を使う**次元別レポート** * パフォーマンスフィードバックループを通じた**AI 主導のインサイト** 最適化戦略、パフォーマンスモニタリング、標準メトリクス、ベストプラクティスの完全な詳細については、\*\*[最適化とレポート](/docs/media-buy/media-buys/optimization-reporting)\*\*を参照してください。 ## キーコンセプト ### メディアバイの構造 メディアバイには次が含まれます: * **キャンペーンメタデータ**(バイヤー参照、ブランド、タイミング) * 通貨とペーシング設定を持つ**全体予算** * 異なるターゲティング/クリエイティブの組み合わせを表す**複数のパッケージ** * 作成、承認、実行の各フェーズを通じた**ステータストラッキング** ### パッケージの種類 三つの異なる種類が、ライフサイクルの異なる段階でパッケージを表します: | 種類 | スキーマ | 使用場所 | 目的 | | ---------------- | ------------------------------------------------ | -------------------------- | ------------------------------------- | | `PackageRequest` | `media-buy/package-request.json` | `create_media_buy` リクエスト | パッケージを作成するためにバイヤーが送るもの | | `Package` | `core/package.json` | `create_media_buy` 成功レスポンス | 作成後にセラーが返すもの(確定した状態) | | `PackageStatus` | `media-buy/get-media-buys-response.json` 内にインライン | `get_media_buys` レスポンス | 配信/レポートのビュー——クリエイティブ承認と任意のスナップショットを含む | `create_media_buy` を実装するときは `PackageRequest` を送ります。レスポンスは `Package` オブジェクトを返します。`get_media_buys` を呼んでステータスや配信を確認するとき、レスポンスには配信固有のフィールドを持つ `PackageStatus` アイテムが含まれます。 ### パッケージモデル パッケージはメディアバイの構成要素です: * 発見結果からの**単一プロダクト**の選択 - プロダクトを買うときは、プロダクト全体を買います(プロパティターゲティングを使う場合を除く) * このパッケージ向けに提供される**クリエイティブフォーマット** * ジオ制限、フリークエンシーキャップ、プロパティターゲティングを含む絞り込みのための**ターゲティングオーバーレイ** * 全体のメディアバイ予算の一部としての**予算配分** * プロダクトの利用可能な価格モデルからの**価格オプション**の選択 * 予算配信のための**ペーシング戦略**(even、asap、または front\_loaded) * オークションベースの価格モデルのための**入札価格**(該当する場合) * パッケージごとの任意の `start_time` と `end_time` を伴う**フライトスケジューリング** * 保証付きバイのための\*\*[アカウンタビリティ条件](/docs/media-buy/advanced-topics/accountability)\*\* — `performance_standards`、`measurement_terms`、`cancellation_policy` ### フライトスケジューリング パッケージは、メディアバイ内で独立したフライト日を持てます。これにより、同じプロダクトが異なる日付ウィンドウと予算で複数のパッケージとして現れる、週次(または任意のケイデンスの)フライトパターンが可能になります。 * **継承**: パッケージで `start_time` または `end_time` が省略された場合、パッケージはメディアバイの日付を継承します。各フィールドは独立して継承されます——パッケージはメディアバイの `end_time` を継承しつつ `start_time` を指定する、またはその逆も可能です。 * **検証**: パッケージの日付は親メディアバイの日付範囲内に収まらなければなりません。セラーは `start_time` が `end_time` と等しいか、それ以降であるパッケージを拒否すべきです(SHOULD)。 * **重複するフライト**: 同じプロダクトの複数のパッケージは、重複する日付範囲を持つことができます。各パッケージは独立した予算を維持します。 * **形式**: 素の ISO 8601 の日時——パッケージは `"asap"` をサポートしません **週次フライトの例:** 3月1〜31日に配信されるディスプレイキャンペーンを、リフト計測のためのダーク期間を挟んで週次の \$2,000 フライトに分割したもの(省略形——完全なリクエストの形は [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) を参照): ```json theme={null} { "start_time": "2026-03-01T00:00:00Z", "end_time": "2026-03-31T23:59:59Z", "packages": [ { "product_id": "prod_premium_display", "pricing_option_id": "cpm_usd_fixed", "budget": 2000, "start_time": "2026-03-01T00:00:00Z", "end_time": "2026-03-07T23:59:59Z" }, { "product_id": "prod_premium_display", "pricing_option_id": "cpm_usd_fixed", "budget": 2000, "start_time": "2026-03-08T00:00:00Z", "end_time": "2026-03-14T23:59:59Z" }, { "product_id": "prod_premium_display", "pricing_option_id": "cpm_usd_fixed", "budget": 2000, "start_time": "2026-03-22T00:00:00Z", "end_time": "2026-03-28T23:59:59Z" } ] } ``` 第 3 週は意図的に省略されています——リフト計測のためのダーク期間です。各フライトは独自の予算を持つため、ペーシングと支出は週ごとに制御されます。キャンペーン途中で調整するには、他のフライトに影響を与えずに個別のパッケージ予算を更新します。 ### クリエイティブの割り当てとプレースメントルーティング プロダクトが複数のバイヤーがターゲティング可能なプレースメントを定義する場合、バイヤーはプロダクトをパッケージとして購入しつつ、それらのプレースメントに異なるクリエイティブを割り当てられます。クリエイティブのプレースメント参照は、パッケージのインベントリ内でクリエイティブをルーティングします。パッケージが購入するインベントリを狭めることはありません。 **主なポイント:** * **パッケージはプロダクトを買う** - プロダクトレベルの `placements[].mode` が、どのパブリッシャースコープのプレースメントがバイヤーにターゲティング可能かを示します * **プレースメントのアイデンティティはパブリッシャースコープ** - パブリッシャー参照のプレースメントは、パブリッシャーの `adagents.json` に対して `{publisher_domain, placement_id}` として解決されます * **インラインプレースメントも引き続き許可** - 公開のパブリッシャープレースメント宣言がない場合、セールスエージェントは `name`、フォーマット、公開のバイヤー向け詳細を持つインラインプレースメントを定義できます。その `placement_id` は、名前付きのパブリッシャー名前空間で、または単一パブリッシャーのレガシー文脈で `publisher_domain` が省略される場合はセラーエージェント自身のパブリッシャー名前空間で解釈されます * パッケージのインベントリスコープは、選択したプロダクト、プロダクトの絞り込み、またはセラーがサポートするパッケージターゲティングサーフェスに由来します。`creative_assignments[].placement_refs` は、すでにスコープ内にあるプレースメントの間でクリエイティブをルーティングするだけです * `placement_refs` も `placement_ids` も持たないクリエイティブは、パッケージ内のすべてのバイヤーがターゲティング可能なプレースメントで配信されます * `placement_refs` とレガシーの `placement_ids` の両方が存在する場合、`placement_refs` が優先され、セラーは `placement_ids` を無視します * `mode: "included"` のプレースメントはプロダクトの公開構成の一部であり、`creative_assignments[].placement_refs` で参照できません * マルチパブリッシャープロダクトには `placement_refs` を使います。`placement_ids` は、プレースメント名前空間が曖昧でない場合にのみ、レガシーの略記として残ります * パブリッシャーは、`adagents.json` の `authorized_agents[].placement_ids` またはガバナンス下の `placement_tags` を使って、特定のプレースメントについてエージェントを認可できます。セラーは販売を認可されたパブリッシャー参照のプレースメントのみを返すべきです **ワークフロー例:** 1. **プロダクトがターゲティング可能なプレースメントを返す:** ```json theme={null} { "product_id": "network_premium", "placements": [ { "kind": "publisher_ref", "publisher_domain": "daily-pulse.example", "placement_id": "homepage_banner", "name": "Homepage Banner", "mode": "targetable", "format_ids": [{"agent_url": "...", "id": "display_728x90"}] }, { "kind": "publisher_ref", "publisher_domain": "metro-report.example", "placement_id": "homepage_banner", "name": "Homepage Banner", "mode": "targetable", "format_ids": [{"agent_url": "...", "id": "display_728x90"}] }, { "kind": "seller_inline", "publisher_domain": "daily-pulse.example", "placement_id": "article_sidebar", "name": "Article Sidebar", "mode": "targetable", "format_ids": [{"agent_url": "...", "id": "display_300x250"}] }, { "kind": "seller_inline", "publisher_domain": "daily-pulse.example", "placement_id": "sponsorship_lockup", "name": "Sponsorship lockup", "mode": "included" } ] } ``` 最初の二つのプレースメントはどちらも `placement_id: "homepage_banner"` を使いますが、それぞれが異なる `publisher_domain` でスコープされているため別個です。三つ目のプレースメントはカタログ参照を持たないためインラインです。このプロダクトが複数のパブリッシャー名前空間にまたがるため、それでも `publisher_domain` を持ちます。 2. **バイヤーがパッケージを作成(プロダクト全体を買う)し、各プレースメントに異なるクリエイティブを割り当てる:** ```json theme={null} { "product_id": "network_premium", "creative_assignments": [ { "creative_id": "creative_daily_pulse", "placement_refs": [ { "publisher_domain": "daily-pulse.example", "placement_id": "homepage_banner" } ] }, { "creative_id": "creative_metro_report", "placement_refs": [ { "publisher_domain": "metro-report.example", "placement_id": "homepage_banner" } ] } ] } ``` 3. **または、一つのクリエイティブをすべてのターゲティング可能なプレースメントに割り当てる(placement\_refs と placement\_ids を省略):** ```json theme={null} { "product_id": "network_premium", "creative_assignments": [ { "creative_id": "creative_universal" } ] } ``` `placement_refs` とレガシーの `placement_ids` の両方を省略すると、そのクリエイティブはパッケージ内のすべてのバイヤーがターゲティング可能なプレースメントで配信されます。 **ユースケース:** * **フォーマット固有のプレースメント**: ホームページは 728x90、サイドバーは 300x250 * **A/B テスト**: 異なるプレースメントで異なるクリエイティブをテスト * **ジオターゲティング**: 異なる DOOH スクリーンのロケーションに異なるクリエイティブ * **デイパーティング**: 朝と夜のプレースメントに異なるクリエイティブ 完全なプレースメントのドキュメントについては、[メディアプロダクト - プレースメント](/docs/media-buy/product-discovery/media-products.mdx#プレースメント)を参照してください。 ### プロパティターゲティング `property_targeting_allowed: true` のプロダクトについて、バイヤーは `targeting_overlay` の `property_list` を使って、どのプロパティをターゲットするかを指定できます: ```json theme={null} { "product_id": "flexible_news_network", "targeting_overlay": { "property_list": { "agent_url": "https://governance.example.com", "list_id": "pl_brand_safe_2024" } }, "budget": 50000 } ``` **主なポイント:** * `property_targeting_allowed: true` のプロダクトについてのみ有効 * パッケージは、プロダクトの `publisher_properties` と `property_list` の交差で配信されます * 省略した場合、パッケージはプロダクトのすべてのプロパティで配信されます * `property_targeting_allowed: false` のプロダクトに対して提供された場合、セラーはバリデーションエラーを返すべきです(SHOULD) プロダクトがどのようにターゲティングの柔軟性を宣言するかについての詳細は、[メディアプロダクト - プロパティターゲティング](/docs/media-buy/product-discovery/media-products#プロパティターゲティング)を参照してください。 ### ライフサイクル状態 メディアバイは、明示的な遷移ルールを持つ定義された状態を進みます: ``` create_media_buy ──┬──▶ pending_creatives ──▶ pending_start ──▶ active ├──▶ active ──(pause)──▶ paused └──▶ paused (when created with paused: true) paused ──(resume)──▶ active active ─────────────▶ completed (terminal) paused ─────────────▶ completed (terminal) pending_creatives ──▶ rejected (terminal) — seller rejects during setup pending_start ──────▶ rejected (terminal) — seller rejects during setup Any non-terminal ──── update(canceled: true) ──▶ canceled (terminal) ``` * **`pending_creatives`**: 承認済みだがクリエイティブが未割り当て——**バイヤー側のアクションが必要**(ライブラリを持つセラーには `sync_creatives`、インライン専用のセラーにはインラインの `packages[].creatives` を使用)。パブリッシャー側やガバナンス側の承認キューではありません: セラーはすでにバイを受理しており、欠けているのはバイヤーのクリエイティブ送信だけです。 * **`pending_start`**: 配信準備完了、フライト日待ち `pending_X` の命名規約は、次に必要となるライフサイクルフェーズを名付けるものであり、セラー/オペレーターの承認を待つ状態では**ありません**——`pending_creatives` は「クリエイティブが次のフェーズ」を、`pending_start` は「フライト日の開始が次のフェーズ」を意味します。どちらもセラー受理後の状態です。 * **`active`**: 実行中でインプレッションを配信している * **`paused`**: バイヤーまたはセラーによって一時的に停止された。有効化の前提条件をそれ以外は満たす場合、トップレベルの `paused: true` でバイを直接 `paused` として作成することもできます。クリエイティブの欠如や将来のフライト日のようなセットアップのブロッカーは、解消されるまで依然として `pending_creatives` または `pending_start` として表面化し、その後、作成時の保留が `paused` として可視になります。 * **`completed`**: 終了——フライトが終了、ゴールが達成、または予算が消化された * **`rejected`**: セラーによって断られた(終端) * **`canceled`**: 自然な完了の前に終了した。バイヤーとセラーのどちらが起点かを判断するには `cancellation.canceled_by` を確認します。 **表示の折りたたみ。** `pending_creatives` と `pending_start` は、下流のゲーティング——条件付き UI、タスクルーティング、レディネスチェック——をサポートするために細粒度です。バイヤーアプリケーションは、エンドユーザーに対して両方を単一の `pending` ラベルとして描画してもかまいませんが(MAY)、区別に依存するロジックが機能し続けるよう、ワイヤー上(API レスポンス、ウェブフック、永続化されたレコード、ログ)では生のステータス値を保持しなければなりません(MUST)。生の列挙を信頼できる情報源として扱い、そこから表示ラベルを導出してください。可能な限り、UI のアフォーダンスをステータス値から直接ではなく `valid_actions` から駆動してください。 **クリエイティブへの影響**: メディアバイが `rejected`、`canceled`、または `completed` に到達すると、そのクリエイティブの割り当ては解放されますが、クリエイティブ自体は変更されません。割り当てられていたクリエイティブは既存のレビューステータスのままライブラリに残り、他のメディアバイへの割り当てに利用できます。[クリエイティブの状態と割り当ての状態](/docs/creative/creative-libraries#creative-state-and-assignment-state-are-separate)を参照してください。 **オーダーの確定**: コミットされた `create_media_buy` レスポンスはオーダーの確定を構成します。レスポンスには、セラーのコミットのタイムスタンプを持つ `confirmed_at` が含まれます。遅延/手動承認のフローでは、セラーがコミットするまで `confirmed_at: null` を公開することがあります。一度値が入ると、そのタイムスタンプは後続のライフサイクルの変更を通じて安定したままです。[リビジョンと確定のセマンティクス](/docs/media-buy/specification#revision-and-confirmation-semantics)を参照してください。 **終端状態**: `completed`、`rejected`、`canceled` は終端です——そこから外への遷移はありません。セラーは終端状態のメディアバイへの更新をエラーコード `INVALID_STATE` で拒否しなければなりません(MUST)。 **作成時の保留。** `create_media_buy` のトップレベルの `paused: true` は、セットアップのブロッカーが存在する場合の潜在的な配信保留です。バイヤーは依然として最初にブロッカーの状態(`pending_creatives` または `pending_start`)を見ます。それが次に必要なフェーズだからです。クリエイティブが揃いフライトが開始できるようになると、そのバイは `active` ではなく `paused` に入ります。バイヤーは `update_media_buy` と `paused: false` でブロッカーの解消前に保留を解除できます。可視ステータスはブロッカーが解消するまで `pending_creatives` または `pending_start` のままで、その後 `active` へ進みます。 **セラーの実装要件——ステータスを永続化し、日付から再計算しない**: `status` は明示的なフィールドとして保存され、プロトコルイベントによってのみ変更されなければなりません(MUST)。フライト日の計算は `paused`、`canceled`、`rejected` を表現できません——それらは時計ではなく明示的なコマンドによって駆動されます。リクエスト時に `start_time`/`end_time` から `status` を再計算するセラーは、これらの状態を黙って落とし、そのメディアバイを読むすべてのバイヤーの `valid_actions` を壊します。正しいアプローチは: 日付の比較が `create_media_buy` 時に初期ステータス(`pending_creatives`、`pending_start`、`active`、または `paused`)を設定し、その後は状態機械がそのフィールドを所有する、というものです。 **有効なアクションの発見**: `get_media_buys` レスポンスには、各メディアバイの `valid_actions`——現在の状態でバイヤーが実行できるアクションのリスト——が含まれます。エージェントは状態機械をハードコードする代わりにこれを使うべきです(SHOULD): ```json theme={null} { "media_buys": [{ "media_buy_id": "mb_12345", "status": "active", "revision": 3, "valid_actions": ["pause", "cancel", "update_budget", "update_dates", "update_packages", "add_packages", "sync_creatives"], "packages": [...] }] } ``` **リビジョントラッキング**: 各メディアバイは、状態を変更するあらゆる変更のたびに増加する `revision` 番号を持ちます。楽観的並行性制御のために `update_media_buy` で `revision` を渡します——最後に読んでからリビジョンが変わっていれば、セラーは `CONFLICT` で拒否します。セラーはこのチェックを書き込みとアトミックに強制しなければなりません。アプリケーションレベルの read/compare/write のロジックは、並行する更新と競合する可能性があります。 ## コアオペレーション ### メディアバイの作成 作成プロセスは次を扱います: * 発見されたプロダクトがまだ利用可能であることを保証する**プロダクトの検証** * パッケージをまたいでクリエイティブ要件を確認する**フォーマット互換性** * 複数のパッケージにまたがって支出を配分する**予算の分配** * 複数のアドサーバーにまたがってキャンペーンを作成する**プラットフォームの調整** ### メディアバイの更新 各パッケージの操作の種類は構造的に明示的です——リクエストのどこに現れるかで決まります: | 操作 | リクエストフィールド | 例 | | --------- | --------------------------------- | ------------------------ | | **新規** | `new_packages[]` | フライト途中でラインアイテムを追加 | | **変更** | `packages[]` | 予算、ターゲティング、日付、クリエイティブを調整 | | **キャンセル** | `canceled: true` を伴う `packages[]` | ラインアイテムをキャンセル(取り消し不可) | キャンペーンレベルの変更には次が含まれます: * 支出の増減のための**予算調整** * オーディエンスパラメータを絞り込む**ターゲティングの更新** * キャンペーンのタイミングを延長または短縮する**スケジュールの変更** * キャンペーンレベルの配信制御のための**一時停止/再開** ### メディアバイのキャンセル [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) と `canceled: true` を使って、メディアバイまたは個別のパッケージをキャンセルします: ```json theme={null} { "media_buy_id": "mb_12345", "canceled": true, "cancellation_reason": "Campaign strategy changed" } ``` アクティブなメディアバイ内の単一のパッケージをキャンセルする: ```json theme={null} { "media_buy_id": "mb_12345", "packages": [ { "package_id": "pkg_67890", "canceled": true, "cancellation_reason": "Underperforming — reallocating budget" } ] } ``` * キャンセルは**取り消し不可**です——キャンセルされたメディアバイとパッケージは再有効化できません * セラーはキャンセルをエラーコード `NOT_CANCELLABLE` で拒否してもかまいません(MAY)(例: 契約上の義務、印刷生産中のオーダー) * キャンセルされたパッケージは、同じメディアバイ内の他のパッケージに影響を与えません。すべてのパッケージがキャンセルされた場合、`add_packages` をサポートするセラーは、バイヤーが `update_media_buy` の `new_packages` を通じて新しいパッケージを追加することを許可します。そうでない場合、バイヤーはメディアバイを明示的にキャンセルすべきです(SHOULD)。 * セラーはメディアバイまたはパッケージをキャンセルしてもかまいません(MAY)(例: ポリシー違反、インベントリの引き上げ)。セラー起点のキャンセルは `cancellation.canceled_by: "seller"` を設定し、オーケストレーターへのウェブフック通知をトリガーしなければなりません(MUST)。 ### パッケージのライフサイクル パッケージはメディアバイと同じ一時停止/キャンセルのパターンに従い、加えてクリエイティブ期限の強制があります: * **`paused`**: 一時的に停止——`paused: false` で再開可能 * **`canceled`**: 恒久的に停止——取り消し不可 * **`creative_deadline`**: クリエイティブのアップロードや変更のためのパッケージごとの期限。この期限の後、クリエイティブの変更は `CREATIVE_REJECTED` で拒否されます。 パッケージに `creative_deadline` が不在の場合、メディアバイの `creative_deadline` が適用されます。これはチャネル混在のオーダーで重要です——同じメディアバイ内で、印刷パッケージがデジタルパッケージより早い素材期限を持つことがあります。 ### ステータス管理 キャンペーンの状態遷移: * 保留中のキャンペーンを開始する**有効化リクエスト** * キャンペーン制御のための**一時停止/再開の操作** * バイヤー起点の終了のための**キャンセル** * 成功したキャンペーンのクローズのための**完了処理** * 失敗した操作のための**エラーリカバリ** ## レスポンスタイム メディアバイの操作は、予測可能なタイミングを持つ統一されたステータスシステムを使います: * **[`create_media_buy`](/docs/media-buy/task-reference/create_media_buy)**: 即時から数日 * `completed`: 即座に作成される単純なキャンペーン * `working`: 120 秒以内の処理(検証、セットアップ) * `submitted`: 数時間から数日を要する複雑なキャンペーン(人による承認) * **[`update_media_buy`](/docs/media-buy/task-reference/update_media_buy)**: 即時から数日 * `completed`: 即座に適用される予算変更 * `working`: 120 秒以内のターゲティング更新 * `submitted`: 承認を要するパッケージ変更(数時間から数日) * **[`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery)**: 約60秒(データ集計) * **パフォーマンス分析**: 約1秒(キャッシュされたメトリクス) **ステータスの意味:** * **`completed`**: 操作が完了、結果を即座に処理 * **`working`**: 処理中、120 秒以内の完了を期待 * **`submitted`**: 長時間実行の操作、ウェブフックを提供するか `tasks/get` でポーリング ## ベストプラクティス ### キャンペーンプランニング * プロダクトディスカバリーのブリーフで定義した**明確な目標から始める** * 異なるオーディエンス/クリエイティブの組み合わせを中心に**パッケージ構造をプランニングする** * プロダクトの価格ガイダンスに基づいて**現実的な予算を設定する** * パブリッシャーのワークフローで**承認の時間を確保する** ### 継続的な管理 * ターゲットに対する配信を保証するために**日次のペーシングをモニタリングする** * 最適化の機会のために**週次でパフォーマンスをレビューする** * 配信を乱さないために**ターゲティングを段階的に更新する** * オーディエンスの疲労を防ぐために**クリエイティブを定期的に刷新する** ### 予算管理 * 最初は**保守的に配分し**、その後パフォーマンスに基づいて増やす * 高パフォーマンスのパッケージのために**予算を確保する** * オーディエンスの可用性と価格の**季節性を見越す** * 異なるターゲティングアプローチにまたがって**支出効率をモニタリングする** * **予算管理**: 予算が更新されると、システムは CPM に基づいてインプレッションを自動的に再計算します ### 技術的実装 * **一時停止/再開の戦略**: メンテナンスにはキャンペーンレベルの制御を、最適化にはパッケージレベルを使う * **パフォーマンスモニタリング**: 定期的なステータスチェックと配信レポートがキャンペーンを軌道に乗せ続けます * **非同期設計**: 長時間実行の操作を適切に扱うようにオーケストレーターを設計する * **タスクトラッキング**: 保留中のタスク ID のために永続的なストレージを維持する * **ウェブフック統合**: リアルタイムの更新のためにウェブフックを実装する * **ユーザーへの伝達**: 保留状態をエンドユーザーに明確に伝える ## エラーハンドリング 保留状態とエラー状態、レスポンスパターン、リカバリ戦略を含む包括的なエラーハンドリングのガイダンスについては、[エラーハンドリング](/docs/building/by-layer/L3/error-handling)を参照してください。 メディアバイ固有のエラーコードは、各タスク仕様と[エラーハンドリングリファレンス](/docs/building/by-layer/L3/error-handling)に記載されています。 ## 非同期オペレーションと人間参加型 AdCP:Buy プロトコルは、コア原則として非同期オペレーションのために設計されています。オーケストレーターは保留状態を適切に扱わなければなりません(MUST)。 ### 人間参加型(HITL)オペレーション 多くのパブリッシャーは、自動化された操作に手動承認を要求します。プロトコルは HITL タスクキューを通じてこれをサポートします: 1. **操作リクエスト**: オーケストレーターが任意の変更タスクを呼ぶ 2. **保留レスポンス**: サーバーがタスク ID とともに `pending_manual` ステータスを返す 3. **タスクのモニタリング**: オーケストレーターがポーリングするか、ウェブフックを受け取る 4. **人によるレビュー**: パブリッシャーがレビューして承認/拒否する 5. **完了**: 承認時に元の操作が実行される ### HITL タスクの状態 ``` pending → assigned → in_progress → completed/failed ↓ escalated ``` ### オーケストレーターの要件 オーケストレーターは次を満たさなければなりません(MUST): 1. `pending_manual` と `pending_permission` を通常の状態として扱う 2. 保留中の操作を追跡するためにタスク ID を保存する 3. 指数バックオフを伴うリトライロジックを実装する 4. 操作の最終的な拒否を適切に扱う 5. リアルタイムの更新のためにウェブフックコールバックをサポートする(推奨) ## 標準メトリクス すべてのプラットフォームはこれらのコアメトリクスをサポートしなければなりません: * **impressions**: 広告閲覧回数 * **spend**: 通貨で使われた金額 * **clicks**: クリック数(該当する場合) * **ctr**: クリック率(clicks/impressions) 任意の標準メトリクス: * **conversions**: ポストクリック/ビューのコンバージョン * **viewability**: ビューアブルインプレッションの割合 * **completion\_rate**: 動画/音声の完了率 * **engagement\_rate**: プラットフォーム固有のエンゲージメントメトリクス ## プラットフォーム固有の考慮事項 異なるプラットフォームは、さまざまなレポートと最適化の機能を提供します: ### Google Ad Manager * Order は複数の LineItem を含められます * LineItem はパッケージと 1:1 でマップします * 高度なターゲティングとフリークエンシーキャップ * クリエイティブ承認プロセスが必要 * **レポート**: 包括的な次元別レポート、リアルタイムおよび履歴データ、高度なビューアビリティメトリクス ### Kevel * Campaign は Flight を含みます * Flight はパッケージと 1:1 でマップします * リアルタイム判断エンジン * カスタムクリエイティブテンプレートをサポート * **レポート**: リアルタイムレポート API、カスタムメトリクスのサポート、柔軟な集計オプション ### Triton Digital * 音声広告に最適化 * Campaign は異なるデイパートのための Flight を含みます * 強力なステーション/ストリームのターゲティング機能 * 音声のみのクリエイティブサポート * **レポート**: 音声固有のメトリクス(完了率、スキップ率)、ステーションレベルのパフォーマンスデータ、デイパート分析 ## 高度な分析 ### クロスキャンペーン分析 * 複数のキャンペーンにまたがる**ポートフォリオのパフォーマンス** * **オーディエンスの重複**とフリークエンシー管理 * キャンペーン横断の**予算配分**の最適化 ### 予測インサイト * 履歴データに基づく**パフォーマンス予測** * AI 分析からの**最適化の推奨** * 先を見越した調整のための**トレンド予測** ## 統合パターン ### 発見からメディアバイまで プロダクトディスカバリーからキャンペーン作成までのシームレスなフロー: 1. [`get_products`](/docs/media-buy/task-reference/get_products) を使ってインベントリを見つける 2. キャンペーン目標に合致するプロダクトを選ぶ 3. 適切なターゲティングとフォーマットでパッケージを設定する 4. [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) でメディアバイを作成する ### クリエイティブ統合 クリエイティブ管理との連携: 1. 選択したプロダクトからフォーマット要件を理解する 2. [クリエイティブ管理](/docs/media-buy/creatives/)を使ってアセットを準備する 3. キャンペーン作成中または更新を通じてクリエイティブを割り当てる 4. クリエイティブのパフォーマンスをモニタリングし、必要に応じて刷新する ### パフォーマンス最適化 包括的な分析を活用したデータ駆動のキャンペーン改善: 1. [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) で**配信を追跡する** * リアルタイムの配信メトリクスとペーシング分析をモニタリングする * 最適化の機会のためにパッケージレベルのパフォーマンス内訳を得る * 異なるターゲティングアプローチにまたがってパフォーマンスを追跡する 2. パッケージとターゲティングにまたがって**パフォーマンスを分析する** * 詳細なインサイトのために次元別レポートを使う * AI 主導の最適化のためにパフォーマンスインデックススコアをモニタリングする * 高パフォーマンスと低パフォーマンスのセグメントを特定する 3. [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) で**キャンペーンを更新する** * 高パフォーマンスと低パフォーマンスのパッケージ間で予算を再配分する * パフォーマンスデータに基づいてターゲティングを調整する * 低パフォーマンスのパッケージを一時停止し、成功したものをスケールする 4. パフォーマンスデータとビジネス成果に基づいて**反復する** * パフォーマンスデータを最適化アルゴリズムにフィードバックする * ターゲティングとクリエイティブの割り当てを継続的に絞り込む * 成功した戦略を類似のキャンペーンにまたがってスケールする #### 最適化のベストプラクティス 1. **頻繁にレポートする**: 定期的なレポートが最適化の機会を高めます 2. **ペーシングを追跡する**: 過少/過剰配信を避けるためにターゲットに対する配信をモニタリングする 3. **パターンを分析する**: 次元にまたがるパフォーマンスのトレンドを探す 4. **レイテンシを考慮する**: 一部のメトリクスはアトリビューションの遅延を持つことがあります 5. **メトリクスを正規化する**: パフォーマンス比較のために一貫したベースラインを使う ## 関連ドキュメント * **[プロダクトディスカバリー](/docs/media-buy/product-discovery/)** - メディアバイのためのインベントリの発見 * **[タスクリファレンス](/docs/media-buy/task-reference/)** - 完全な API ドキュメント * **[クリエイティブ](/docs/media-buy/creatives/)** - クリエイティブアセットの管理 * **[オーケストレーター設計ガイド](/docs/building/operating/orchestrator-design)** - 実装のベストプラクティス # メディアバイライフサイクルフロー Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/media-buys/lifecycle 製品ディスカバリーから配信までのステップバイステップシーケンス、保証ディール IO 受諾パスとクリエイティブ同期タイミングを含む。 このページはメディアバイライフサイクルの正準シーケンスリファレンスです。完全なライフサイクルの概念的背景 — キャンペーン構造、パッケージモデル、プロパティターゲティング、非同期操作 — については [メディアバイライフサイクル](/docs/media-buy/media-buys/) を参照してください。 ## 標準フロー すべてのメディアバイは 4 つのステップに従います: ```mermaid theme={null} flowchart TD A[get_products] --> B[create_media_buy] B --> C{initial state} C -->|creatives missing| D[pending_creatives] C -->|creatives present,\nflight not yet started| E[pending_start] C -->|creatives present,\nflight started| F[active] C -->|paused: true,\notherwise active| J[paused] D --> G[creative supply] G --> E E --> F F --> H{delivery} H -->|budget exhausted /\ngoal met / flight ended| I[completed] H -->|buyer or seller action| J[paused] J --> F J -->|flight ended| I ``` 1. **`get_products`** — ブリーフに一致する利用可能な在庫を発見。 2. **`create_media_buy`** — パッケージを提出。セラーが検証し確認。 3. **クリエイティブ供給** — それを必要とするパッケージにクリエイティブアセットを割り当てる、`sync_creatives` と `creative_assignments` を通じて、またはセラーがインラインクリエイティブ管理をアドバタイズするときインラインパッケージ `creatives` を通じて。 4. **配信** — バイが `active` に入りインプレッションを計上、または配信保留で作成されたとき `paused` に入る。最終的に終端状態に到達。 ## ステートマシン ### メディアバイ状態 | State | Meaning | Terminal? | | ------------------- | --------------------- | --------- | | `pending_creatives` | 承認済み。まだクリエイティブ未割り当て | No | | `pending_start` | クリエイティブ割り当て済み。フライト日待ち | No | | `active` | インプレッション配信中 | No | | `paused` | 一時停止 | No | | `completed` | フライト終了、目標達成、または予算枯渇 | Yes | | `rejected` | セラーがバイを辞退 | Yes | | `canceled` | バイヤーまたはセラーが完了前に終了 | Yes | `pending_manual` と `pending_permission` は **タスクレベル** ステータスです — それらは *操作*(例: `create_media_buy`)が人間レビューのためキューされているかを記述し、メディアバイ自身の状態ではありません。メディアバイは操作が完了すると `pending_creatives`、`pending_start`、`active`、または `paused` に入ります。[非同期操作](/docs/media-buy/media-buys/#asynchronous-operations-and-human-in-the-loop) を参照してください。 ### 遷移 ```mermaid theme={null} stateDiagram-v2 [*] --> pending_creatives : create_media_buy\n(no creatives) [*] --> pending_start : create_media_buy\n(creatives present,\nflight future) [*] --> active : create_media_buy\n(creatives present,\nflight started) [*] --> paused : create_media_buy\n(paused: true,\notherwise active) pending_creatives --> pending_start : sync_creatives\nor update_media_buy creatives pending_creatives --> paused : sync_creatives\nor update_media_buy creatives\n(create held) pending_start --> active : flight date reached pending_start --> paused : flight date reached\n(create held) active --> paused : update_media_buy\n(paused: true) paused --> active : update_media_buy\n(paused: false) active --> completed : flight ended /\ngoal met / budget exhausted paused --> completed : flight ended /\ngoal met / budget exhausted pending_creatives --> rejected : seller declines pending_start --> rejected : seller declines pending_creatives --> canceled : update_media_buy\n(canceled: true) pending_start --> canceled : update_media_buy\n(canceled: true) active --> canceled : update_media_buy\n(canceled: true) paused --> canceled : update_media_buy\n(canceled: true) completed --> [*] rejected --> [*] canceled --> [*] ``` ### ランタイムでの有効なアクションの発見 ステートマシンをハードコードするのではなく、`get_media_buys` から `valid_actions` を読みます。セラーは現在の状態でバイヤーができることを正確に返します: ```json theme={null} { "media_buy_id": "mb_12345", "status": "active", "revision": 3, "valid_actions": ["pause", "cancel", "update_budget", "update_dates", "update_packages", "add_packages", "sync_creatives"] } ``` バイヤーは状態を変えることを意図したすべての `update_media_buy` 呼び出しで最新の `revision` を渡すべきです(SHOULD)。フィールドは後方互換性のためオプションですが、存在するとき、リビジョンが最後の読み取り以来変わっていればセラーは `CONFLICT` で拒否し、チェックは並行更新が互いを上書きできないよう書き込みとアトミックに起こらなければなりません。[楽観的並行性](/docs/media-buy/task-reference/update_media_buy#optimistic-concurrency) を参照してください。 クリエイティブ変更には、`valid_actions` の `sync_creatives` はレガシーアクションラベルです。セラーがアドバタイズするクリエイティブパスを使います: ライブラリバックのセラーには `sync_creatives` と `creative_assignments`、インラインのみのセラーには `update_media_buy` の `packages[].creatives`。 ## 保証 / PG ディールバリエーション `delivery_type: "guaranteed"` の製品は、配信開始前に契約上のコミットメントを要求します。フローは `create_media_buy` の後に分岐します: ```mermaid theme={null} flowchart TD A[get_products\ndelivery_type: guaranteed] --> B[create_media_buy\nwith accountability_terms] B --> C{task status} C -->|IO signing needed| D[submitted\ntask_id returned] C -->|IO pre-signed| E[pending_creatives\nor pending_start] D --> F[IO signed\nout-of-band] F --> E E --> G[creative supply\nif needed] G --> H[active — guaranteed delivery] H --> I{performance} I -->|standards met| J[completed] I -->|under-delivery| K[makegood / remediation] K --> J ``` ### 保証バイを異なるものにするもの **`accountability_terms` は必須** です、保証製品を持つ各パッケージ上で。3 つのフィールドが必須: * `performance_standards` — ビューアビリティ、IVT、完了レート、測定ベンダーを伴う他のしきい値 * `measurement_terms` — 誰が課金メトリックを数えるか、許容分散、メイクグッド救済 * `cancellation_policy` — 早期終了の通知期間とキャンセル料 保証パッケージでこれらのいずれかを省略すると、セラーは `TERMS_REJECTED` を返します。 **IO 署名** — 保証製品の `create_media_buy` は同期的に完了するのではなく `task_id` を伴うタスクステータス `submitted` を返すかもしれません。これはセラーのシステムがインサーションオーダー(IO)受諾を待っていることを意味します。`tasks/get` でポーリングするか webhook を構成します。IO が署名されると、完了アーティファクトが `media_buy_id` を運びメディアバイは `pending_creatives` または `pending_start` に入ります。 **メイクグッド** — セラーが合意された `performance_standards` に対して過小配信する場合、`makegood_policy` から救済を提案します: `additional_delivery`、`credit`、または `invoice_adjustment`。バイヤーは受諾または異議を唱えます。 過小配信せずに受諾するセラーは好ましいアカウンタビリティシグナルを獲得します。バイヤーが `create_media_buy` 時に非デフォルト条件を提案できる方法を含む完全な交渉フローについては [アカウンタビリティ](/docs/media-buy/advanced-topics/accountability) を参照してください。 ## クリエイティブ同期タイミング ### クリエイティブがいつ必要か `create_media_buy` はパッケージごとのインライン `creative_assignments` または `creatives` を受け入れます。作成時にそれらを供給しフライト日が過ぎている場合、バイは直接 `active` に入る、またはリクエストがトップレベル `paused: true` を運ぶとき `paused` に入る。フライト日が未来の場合、`pending_start` に入る。トップレベル `paused: true` では、保留は潜在的でフライト日が到着するとバイは `paused` に入る。 作成時にクリエイティブが割り当てられない場合、バイは `pending_creatives` に入る。トップレベル `paused: true` では、保留は潜在的で、必要なクリエイティブが供給され任意の未来の開始日が到着した後バイは `paused` に入る。配信は、バイヤーがセラーがアドバタイズするパスを通じてパッケージごとに少なくとも 1 つのクリエイティブを供給するまで開始できません。`creative.has_creative_library: true` のセラーは `sync_creatives` と `creative_assignments` を使います。`creative.has_creative_library: true` と `inline_creative_management: true` の両方をアドバタイズするセラーは、`create_media_buy` と `update_media_buy` でインライン `packages[].creatives` も受け入れます。インラインのみのセラーは `update_media_buy` の `packages[].creatives` のみを使います。 作成時保留は、配信がそうでなければ準備できる前にクリアされるかも。`paused: false` の `update_media_buy` は保存された保留をクリアします。クリエイティブがまだ欠けているかフライト日がまだ未来の場合、そのブロッカーがクリアするまで可視ステータスは `pending_creatives` または `pending_start` のままです。 ### `creative_deadline` `create_media_buy` はメディアバイレスポンスで `creative_deadline` タイムスタンプを返します。個別のパッケージは自身の `creative_deadline` を運ぶかもしれません。**パッケージレベルデッドラインはメディアバイデッドラインより優先します。** これは混合チャネルオーダーに重要です — プリントパッケージは同じバイのデジタルパッケージより数日前の素材デッドラインを持つかもしれません。 デッドライン後、そのパッケージのクリエイティブ提出は、`sync_creatives` または `update_media_buy` のインライン `packages[].creatives` のどちらを通じて到着しても `CREATIVE_REJECTED` を返します。クリエイティブ変更はブロックされます。配信は現在割り当てられているクリエイティブで続きます(またはクリエイティブが決して割り当てられなかった場合パッケージは `pending_creatives` のまま)。 ``` Deadline hierarchy: package.creative_deadline (if present — wins) ↓ else media_buy.creative_deadline ``` ### バイが終わるときのクリエイティブへの影響 メディアバイが `rejected`、`canceled`、`completed` に到達するとき、クリエイティブ割り当ては解放されます。ライブラリバックのセラーには、クリエイティブ自体は削除されません — 既存のレビューステータスでライブラリに残り、他のメディアバイへの割り当てに利用可能です。インラインのみのセラーは、再利用可能なライブラリエントリーを露出せずに監査とレポートのためパッケージスコープのクリエイティブレコードを保持するかもしれません。 ## Health and dependency impairment `status` は運用状態を記述します — バイは配信中、一時停止、または終端か? **`health`** は、アップストリーム依存関係が無傷かを記述する別の直交フィールドです: | Field | Tracks | Values | | -------- | ------ | --------------------------------------------------------------------------------------------- | | `status` | 運用状態 | `pending_creatives`, `pending_start`, `active`, `paused`, `completed`, `rejected`, `canceled` | | `health` | 依存関係状態 | `ok`, `impaired` | 2 つは直交です。バイは `paused` かつ impaired、`pending_creatives` かつ impaired、または `active` かつ impaired になりえます。Health は `status` を変えません。`valid_actions` は影響を受けません。 ### `health` が `impaired` のとき `health` は、バイが参照するアップストリーム依存関係が少なくとも 1 つのパッケージの配信に影響するオフライン状態に入るとき `impaired` に遷移します: * バイがターゲットするオーディエンスが `suspended` に遷移(同意期限切れ、TTL、ポリシー強制)。 * バイが使うクリエイティブが `approved` から `suspended`(回復可能な依存関係/認可喪失)、`suspended` から `rejected`(終端依存関係/認可喪失)、または `approved` から `rejected`(承認後失効)に遷移。 * バイがターゲットするカタログアイテムが `withdrawn` に遷移(セラー開始の削除)。 * バイが依存するイベントソースが `insufficient` に入る(ゼロイベント受信)。 * バイがターゲットするプロパティが brand.json / adagents.json 経由で depublish される。 バイの `impairments[]` 配列は影響を受ける依存関係ごとに 1 エントリーを運びます: ```json theme={null} { "media_buy_id": "mb_456", "status": "active", "health": "impaired", "impairments": [ { "impairment_id": "imp_01HZX9...", "resource_type": "audience", "resource_id": "aud_123", "package_ids": ["pkg_a"], "transition": { "from": "ready", "to": "suspended" }, "reason_code": "consent_expired", "reason": "Hashed identifier consent basis expired on 2026-06-01.", "observed_at": "2026-06-02T14:11:00Z", "remediation": "Re-sync audience after refreshing consent upstream." } ] } ``` ### マテリアリティ `impairments[]` の各エントリーは、配信能力が劣化した少なくとも 1 つのパッケージをリストしなければなりません(MUST)。表面的な影響(依然としてサービス可能な仲間を持つパッケージの 1 つの拒否されたクリエイティブ)は機能低下としてレポートされてはなりません(MUST NOT) — それらはバイのではなくリソース自身のステータス経由で表示されます。 ### 逆方向 基盤リソースがサービス可能な状態に戻るとき(オーディエンス再同期、クリエイティブ再承認)、セラーは `impairments[]` から対応するエントリーを削除しなければならず(MUST)、他の機能低下が残らなければ `health` を `ok` に反転しなければなりません。バイヤーは次のスナップショット読み取りまたは次の `impairment` プッシュ(クロージャーを運ぶ)で回復した状態を見ます。 ### `impairment` webhook 経由でプッシュ バイの `health` が遷移するか機能低下が追加/削除されるとき、セラーはバイの `push_notification_config` に対して `impairment` 通知を発火します。ペイロードは `impairment` オブジェクト形状プラスバイの更新された `health` を再利用します。配信セマンティクス(at-least-once、順序なし、合体、スナップショット経由リプレイ)については [persistent webhook contract](/docs/building/by-layer/L3/webhooks#persistent-channel-contract) を参照してください。 機能低下 webhook は設計上 `package_ids[]` で影響を受けるパッケージを識別します。`context.buyer_ref` のようなパッケージレベル相関コンテキストを必要とするバイヤーは、機能低下発火を受け取った後 `get_media_buys` を呼びパッケージスナップショットを読むべきです。 ### マテリアリティカバレッジ MUST 強度のマテリアリティルールは、リソース → バイ結合が安価で 1:N のリソースタイプ — audience、event\_source、property — に適用されます。creative と catalog\_item には、マテリアリティは SHOULD 強度です: 大きなプールのクリエイティブは削除されても配信を劣化させないかもしれず、結合はセラーが計算するのにより高価です。実装者は不確かなとき保守的にレポートすべきで(SHOULD)、配信が証明可能に影響を受けないときレポートしてはなりません(MUST NOT)。 ### `reason_code` による修復 各 `reason_code` は典型的なバイヤー修復パスを持ちます。セラーは機能低下ごとにこれを埋めません — バイヤーエージェントは `reason_code` から直接修復をキーします。下のテーブルはプロトコルレベルガイダンスです。機能低下ごとのフリーテキスト `remediation` フィールドは、典型的なパスに合わないセラー固有コンテキストを運びます(例: 「このオーディエンスを昨日復元した。今同期してリフレッシュを拾って」)。 | `reason_code` | Typical buyer remediation | | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `consent_expired` | アップストリーム同意をリフレッシュ(例: クリーンルームフローでのハッシュ id 同意更新)、次に `sync_audiences` 経由でオーディエンスを再同期。 | | `ttl_expired` | 更新のため `sync_audiences` 経由でオーディエンスを再同期。 | | `pii_audit_failed` | アップストリームで監査発見に対処(ハッシュ、識別子衛生)。セラーが監査クリアをシグナルした **後** にのみ再同期 — クリアされていない監査に対して `sync_audiences` をループするバイヤーエージェントは収束しない。 | | `content_rejected` | 初期クリエイティブ承認と同じパス — 問題を修正し、ライブラリバックのセラーには `sync_creatives` 経由で、インラインのみのセラーには `update_media_buy` の `packages[].creatives` 経由で再提出。キャンペーンレベル決定(置き換え対再提出待ち)はクリエイティブプール構成に依存しバイヤーに委ねられる。 | | `identity_authorization_revoked` | セラーの接続フローを通じて下流アイデンティティ/投稿認可を復元、または認可が復元できない場合影響を受ける `published_post` クリエイティブを置き換え。 | | `identity_authorization_expired` | セラーの接続フローを通じて下流アイデンティティ/投稿認可を更新、次にセラーにクリエイティブを再チェックまたは再レビューさせる。 | | `source_private` | ソース投稿可視性を復元、または影響を受ける `published_post` 参照を置き換え。 | | `source_offline` | タグがバイヤーのプロパティで発火していることを検証(これはしばしばセラー側停止ではなくバイヤー/パブリッシャー統合問題)、次に `sync_event_sources` 経由で再同期。 | | `seller_removed` | バイヤー側再提出パスなし。キャンペーンセットアップで使われたのと同じディスカバリーツール(`get_products`、`get_signals`、`list_creative_formats`)経由で置き換えを見つける、またはセラーに復元 ETA を連絡。 | | `policy_violation` | セラー側強制。バイヤー側再提出はクリアの可能性が低い。解決を待つかセラーの標準連絡パスに従いエスカレート。 | | `property_depublished` | バイヤー側修正なし — プロパティ公開はパブリッシャーの `brand.json` / `adagents.json` で制御される。`get_products` 経由で置き換えプロパティを見つけるかターゲティングから削除。 | ### トリアージ順序 非空の `impairments[]` をトリアージするバイヤーエージェントは、エントリーを `observed_at` 昇順でソートすべきです(SHOULD) — 最も古いオープンな機能低下が既に配信を食っている可能性が最も高い。Webhook 到着時間は信頼できるプロキシではありません: 合体ルール([webhooks § Coalescence](/docs/building/by-layer/L3/webhooks#coalescence) を参照)の下でセラーは複数の状態変更を 1 つの発火にバッチしてもよく(MAY)、新しい `idempotency_key` 下の再発行は `observed_at` を変えずにトランスポートタイムスタンプをリセットします。 ### 機能低下は運用シグナルで、商業イベントではない `impairment` はアップストリーム依存関係変更からの劣化した配信をレポートします。それは課金イベント、メイクグッドトリガー、またはクレジット紛争では **ありません**。過小配信の商業救済は保証バイの `accountability_terms` で統制され、この表面の範囲外のままです。紛争パイプラインを構築するインテグレーターは、`impairments[]` からではなく配信レポートとアカウンタビリティ条件からそれらを駆動すべきです。 ### Compliance `impairment.coherence` アサーションは、バイの `impairments[]` 表面がそれが参照する基盤リソースと同期を保つことを検証します。それはクロスリソース不変条件です — 同じコンプライアンス実行でリソース遷移とバイスナップショットの両方を観測します。 **前方ルール。** バイの `impairments[]` の各エントリーは、現在ステータスがオフライン状態のリソースを参照しなければなりません(MUST) — `audience: suspended`(`audience-status` 上)、`creative: suspended` または `creative: rejected`(`creative-status` 上)、`catalog_item: withdrawn`(`catalog-item-status` 上)、`event_source: insufficient`(`event-source-health.status` を通じて表示される `assessment-status` 値)、または `brand.json` / `adagents.json` 経由で depublish されたプロパティ。参照されたリソースがもはやオフラインでない機能低下をレポートするバイはチェックに失敗 — セラーはバイに古い状態を持つ。 **逆ルール。** 非終端バイが参照するオフライン状態の任意のリソースは、そのバイの `impairments[]` に現れなければなりません(MUST)。影響を受けるバイに伝播せずにリソースを遷移するセラーはチェックに失敗 — セラーはリソースに古い状態を持つ。 **Health-iff ルール。** 非終端バイの `health` は、`impairments[]` が非空のときは常に `impaired` でなければならず(MUST)、`impairments[]` が空のときは常に `ok` でなければなりません(MUST)。これは厳格な iff です — 空の `impairments[]` を持つ古い `health: "impaired"`(または非空の `impairments[]` を持つ `health: "ok"`)は、前方と逆ルールが個別に満たされてもルールに違反します。 **範囲外。** 3 つのルールすべてが終端ステータス(`completed`、`canceled`、`rejected`)のバイで緩和されます。セラーは終端遷移で保持されていた状態のまま `impairments[]` と `health` を残してもよい(MAY) — それらをクリーンアップする必要はありません。バイヤーは終端後ドリフトを一貫性違反として扱ってはなりません(MUST NOT)。バイはもはや配信しておらず同期は無駄な努力です。マテリアリティ(`package_ids` が非空である要件)は `impairment.json` の `package_ids: minItems: 1` によってスキーマ層で強制されます — `impairment.coherence` はそれを再チェックしません。 **スナップショットはいくつかの伝播表面の 1 つ。** セラーは `get_adcp_capabilities` の [`capabilities.media_buy.propagation_surfaces`](https://adcontextprotocol.org/schemas/v3/protocol/get-adcp-capabilities-response.json) 経由でどの表面を使うかを宣言します — 非排他的配列なので、バイスナップショットで機能低下をミラーし かつ webhook を発火するセラーは `["snapshot", "webhook"]` を宣言(プレミアム保証セラーの一般的なケース)。3 つの表面値: * **`snapshot`** — セラーが `get_media_buys` 読み取りで `health` + `impairments[]` を投入。上のコントラクトがこの表面を統制。`impairment.coherence` ストーリーボードは宣言されるときそれをグレード、そうでなければ `not_applicable`。 * **`webhook`** — セラーが `push_notification_config` 経由で `notification-type: impairment` webhook を発火。persistent-channel webhook コントラクトに従う。 * **`out_of_band`** — セラーが AdCP プロトコル表面外のチャネル(メール、ダッシュボード、パートナー固有フィード)経由で伝播。機能低下ワークフローが人間チャネルで管理されるとき、ロングテールとエンタープライズバンドルプラットフォームが一般的にこれを使う。`["out_of_band"]` のみを宣言するセラーはスナップショットまたは webhook コンプライアンスでグレードされない — 彼らのバーはオフライン合意。 不在時のデフォルトは `["snapshot"]`。各表面は他から独立。バイヤーがエージェントで観測する実際の表面の混合を宣言。非 AdCP フィールド名(マッピングギャップ)の下の API に機能低下データを持つセラーは、`out_of_band` を宣言するのではなくマッピングを文書化すべき(SHOULD) — 仕様のギャップが `out_of_band` が正当にカバーするもの。 **他の不変条件との関係。** `impairment.coherence` は、単一リソース遷移のみを観測する `status.monotonic` を補完します。2 つは、リソース状態遷移とメディアバイスナップショット読み取りの両方を行使するストーリーボードを持つすべての専門分野で一緒に実行 — audience-sync、creative-ad-server、creative-template、creative-generative、sales-catalog-driven。非 NA グレーディングを駆動するクロスリソース行使は dependency-impairment ストーリーボード(`media_buy_seller/dependency_impairment`、creative-track)で、アクティブなバイのクリエイティブをオフライン状態(回復可能な依存関係喪失には `approved → suspended`、終端失効には `approved/suspended → rejected`)に強制し、バイが一致する `impairments[]` エントリーで `health: impaired` を反映することを検証し、クリエイティブを回復または置き換え、バイが `impairments[]` クリアで `health: ok` に戻ることを検証します。Audience-track と catalog-track バリアントはフォローアップで、コンプライアンステストコントローラーの `force_audience_status` / `force_catalog_item_status` サポート待ち。 `impairments[]`(スナップショット)を `impairment` プッシュ(ログ)に結びつける読み取り側ルールについては [Snapshot and log contract](/docs/protocol/snapshot-and-log) を参照してください。 ライブラリバックのセラーには、クリエイティブライブラリ状態とクリエイティブ割り当て状態は独立に追跡されます。キャンセルされたバイに割り当てられたクリエイティブは、獲得したレビューステータスを依然として持ち即座に新しいバイに割り当てられます。インラインのみのセラーは `get_media_buys` でパッケージスコープのクリエイティブステータスを露出しますが再利用可能なライブラリクリエイティブをアドバタイズしません。[クリエイティブ状態と割り当て状態](/docs/creative/creative-libraries#creative-state-and-assignment-state-are-separate) を参照してください。 ## 関連項目 * [メディアバイライフサイクル](/docs/media-buy/media-buys/) — 完全なライフサイクルリファレンス: キャンペーン構造、パッケージモデル、非同期操作 * [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) — リクエストパラメーター、レスポンス形状、例を伴うタスクリファレンス * [`sync_creatives`](/docs/creative/task-reference/sync_creatives) — セラーが `creative.has_creative_library: true` をアドバタイズするときライブラリクリエイティブをアップロードして更新 * [アカウンタビリティ](/docs/media-buy/advanced-topics/accountability) — パフォーマンス標準、測定条件、メイクグッド解決 * [最適化とレポート](/docs/media-buy/media-buys/optimization-reporting) — 配信監視、次元レポート、キャンペーン更新 # Brief Expectations Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/product-discovery/brief-expectations 効果的なメディアバイには充実したブリーフが不可欠です。本ドキュメントでは `get_products` 仕様におけるブリーフの期待値と必須要件を定義し、パブリッシャーへの実装ガイダンスとバイヤーへの明確な期待値を示します。 ## 概要 AdCP におけるブリーフは、キャンペーン要件を自然言語で記述し、パブリッシャーがメディアバイのリクエストを理解し実行するうえでの助けとなります。ブリーフはシンプルでも詳細でも構いませんが、充実しているほど、より適切な商品提案と効率的なキャンペーン運用が可能になります。 ## 必須コンポーネント すべての `get_products` と `create_media_buy` リクエストに **必須**: ### Brand Manifest `brand_manifest` フィールドはすべてのリクエストで **必須** です。広告主ブランドを特定します。 ```json theme={null} { "brand_manifest": { "name": "Nike", "url": "https://nike.com", "category": "athletic_apparel" } } ``` これによりパブリッシャーは次を行えます。 * ポリシー制限の適用(年齢制限、禁止カテゴリなど) * ブランドの真正性確認 * ブランドセーフティ基準の遵守 ### Brief フィールド `brief` フィールドでは **何を訴求しているか** と **キャンペーン要件** を記述します。 ```json theme={null} { "brief": "Nike Air Max 2024 - the latest innovation in cushioning technology featuring sustainable materials, targeting runners and fitness enthusiasts" } ``` ## ブリーフが任意となるケース `brief` フィールドは **任意** です。標準カタログを取得したいときなど正当なケースがあります。 ### 標準カタログの探索 ターゲティングなしでパブリッシャーの標準商品カタログを確認したい場合: * パブリッシャーの **標準商品カタログ** を取得 * オーディエンスターゲティングやニッチ商品はなし * 全広告主に提供される基本的な商品を返す * 初期の情報収集やプランニングに有効 ### ブリーフ不要となるシナリオ 1. **初期探索** - パブリッシャーのインベントリを把握したい 2. **バイヤー側のターゲティング** - [TMP](/docs/trusted-match) 経由で独自のオーディエンスセグメントを利用します 3. **商品を直接指定** - 特定の商品 ID をすでに把握しています 4. **常時稼働キャンペーン** - 既存キャンペーンの補充 ### 例: 標準カタログのリクエスト ```json theme={null} { "brand_manifest": { "name": "Nike", "url": "https://nike.com" }, "brief": null, // No brief = standard catalog "filters": { "delivery_type": "non_guaranteed", "format_types": ["display", "video"], "standard_formats_only": true } } ``` この場合: 1. パブリッシャーは **標準カタログ** を返す 2. パーソナライズやブリーフに紐づく商品は返さない 3. 広告主が利用できるベースラインの在庫を提供 4. バイヤーは [TMP](/docs/trusted-match) を通じて独自のターゲティングを適用可能 5. 目的は利用可能な在庫の把握 ## ブリーフの主要要素 `brief` を提供する場合、次の要素を含めると効果的です。 ### 1. ビジネス目標 **キャンペーンで達成したいこと** * **Awareness**: ブランド認知やプロダクト認知の向上 * **Consideration**: 興味・検討を促進 * **Conversion**: 売上やサインアップの獲得 * **Retention**: 既存顧客の再エンゲージ * **App installs**: モバイルアプリのインストール促進 * **Lead generation**: リード獲得 * **Traffic**: Web/店舗への送客 ブリーフ記載例: *"Drive awareness for our new product launch among young professionals"* ### 2. 成功指標 **成功をどう測るか** * **CTR** (Click-Through Rate): エンゲージメントの測定 * **CPA** (Cost Per Acquisition): コンバージョン効率 * **ROAS** (Return on Ad Spend): 収益貢献 * **Brand lift**: 認知・好意度の向上 * **Video completion rate**: コンテンツ消化度 * **Conversion rate**: 行動完了率 * **Reach and frequency**: 到達と接触頻度 ブリーフ記載例: *"Success measured by achieving 2% CTR and \$50 CPA"* ### 3. フライト期間 **いつ配信するか** * **Start date**: 開始日 * **End date**: 終了日 * **Specific periods**: 祝日・イベント・プロモーション期間 * **Blackout dates**: 配信を避ける日 * **Dayparting requirements**: 時間帯の希望 ブリーフ記載例: *"Run from March 1-31, focusing on weekday morning commutes"* ## 任意のコンポーネント これらを含めると提案の質が向上します。 ### ターゲットオーディエンス **誰に届けたいか** #### Demographics * 年齢範囲(例: 25-34, 35-44) * 性自認 * 世帯収入 * 学歴 * 親かどうか * 雇用状況 #### Psychographics * 興味・関心 * ライフスタイル属性 * 価値観・信念 * 購買行動 * メディア接触習慣 * テクノロジー採用度 #### Behavioral Signals * 過去の購買行動 * Web 訪問履歴 * アプリ利用状況 * コンテンツエンゲージメント * カート放棄 ブリーフ記載例: *"Target pet owners aged 25-45 with household income over \$75K who have shown interest in premium pet products"* ### 予算情報 **支出条件** * **Total budget**: 総予算 * **Daily budget**: 日次上限 * **Budget flexibility**: 変更余地 * **Cost constraints**: CPM 上限や効率要件 * **Budget allocation**: 商品や期間への配分 ブリーフ記載例: *"\$50,000 total budget with flexibility to increase by 20% for high-performing inventory"* ### 地域 **どこに配信するか** * **Countries**: 国 * **Regions/States**: 国内の地域や州 * **Cities/DMAs**: 都市・DMA * **Postal codes**: より細かい地域 * **Exclusions**: 配信除外エリア ブリーフ記載例: *"Focus on California and New York, specifically Los Angeles and New York City metros"* ### クリエイティブの制約 **フォーマットやコンテンツ要件** * **Available formats**: Video, audio, display, native * **Creative variations**: 用意できるバリエーション数 * **Language versions**: 対応言語 * **Technical limitations**: ファイルサイズ、尺など * **Brand guidelines**: 色・ロゴ・メッセージの要件 ブリーフ記載例: *"We have 30-second and 15-second video creatives in English and Spanish"* ### ブランドセーフティ要件 **避けたいコンテンツ** * **Blocked categories**: 除外したいカテゴリ * **Sensitive topics**: 回避したいテーマ * **Competitor separation**: 競合ブランドの同時掲載回避 * **Quality standards**: ビューアビリティや不正防止 * **Certification requirements**: TAG, MRC など ブリーフ記載例: *"Avoid news, political content, and competitive automotive brands"* ## ブリーフの充実度レベル パブリッシャーは、ブリーフの充実度に応じて柔軟に対応すべきです。 ### ブリーフなし(標準カタログ) ```json theme={null} { "brand_manifest": {"name": "Acme Corp", "url": "https://acmecorp.com"}, "brief": null, // Signals standard catalog request "filters": { "delivery_type": "non_guaranteed", "standard_formats_only": true } } ``` **パブリッシャー応答**: 標準カタログ商品を返す(スケールを重視した広い在庫)。ターゲティングやニッチ商品は不要。提案も不要。 ### 最小限のブリーフ ```json theme={null} { "brand_manifest": {"name": "Acme Corp", "url": "https://acmecorp.com"}, "brief": "Reach business decision makers" } ``` **パブリッシャー応答**: 予算、期間、具体的な目標の確認を求める。 ### 標準的なブリーフ ```json theme={null} { "brand_manifest": {"name": "Acme Corp", "url": "https://acmecorp.com"}, "brief": "Acme Corp project management software - cloud-based solution for remote teams. Reach IT decision makers in tech companies with 50-500 employees, $25K budget for Q1, focusing on driving free trial signups" } ``` **パブリッシャー応答**: 根拠を明示した関連商品の提案を返します。 ### 包括的なブリーフ ```json theme={null} { "brand_manifest": {"name": "Acme Corp", "url": "https://acmecorp.com"}, "brief": "Acme Corp project management software - cloud-based solution for remote teams with AI-powered automation. Drive 500 free trial signups from IT decision makers and project managers at tech companies (50-500 employees) in SF Bay Area and NYC. $25K budget for March 1-31, measured by $50 CPA. We have video and display creatives. Avoid competitor content and news sites." } ``` **パブリッシャー応答**: 詳細なパフォーマンス予測付きで最適化された商品構成を提示。 ## 実装ガイドライン ### パブリッシャー向け 1. **ブリーフ要素の抽出**: 重要項目をプログラム的に抽出 2. **不完全な情報への対応**: 不足している重要情報を丁寧に確認 3. **ガイダンスの提供**: 追加すると有益な情報を示します 4. **スマートなマッチング**: 自然言語を解釈し、AI を活用して商品にマップ 5. **関連性の説明**: レスポンスでは必ず `brief_relevance` を返す ### バイヤー向け 1. **具体的に記述**: 詳細が多いほど精度の高い提案が得られます 2. **目標の優先順位**: 主要目標と副次目標を明確にします 3. **背景の共有**: 市場状況や競合環境を含めます 4. **反復的に更新**: パブリッシャーのフィードバックを受けてブリーフを改善 5. **整合性の維持**: ブリーフと promoted offering を一致させる ## ブリーフ処理フロー ```mermaid theme={null} graph TD A[Receive Request] --> B{Promoted Offering Valid?} B -->|No| C[Return Policy Error] B -->|Yes| D{Brief Provided?} D -->|No| M[Return All Products Matching Filters] D -->|Yes| E{Brief Complete?} E -->|No| F[Request Clarification] E -->|Yes| G[Process Requirements] F --> H[Provide Specific Questions] G --> I[Match Products] I --> J{Products Found?} J -->|No| K[Suggest Alternatives] J -->|Yes| L[Return Recommendations] L --> N[Include Relevance Explanation] ``` ## 確認事項の扱い ブリーフに明確化が必要な場合、パブリッシャーは次を行います。 1. **具体的な質問をする**: 欠けている重要情報に絞る 2. **例を示す**: 望ましい情報の例を提示 3. **コンテキストを保持**: 以前のブリーフ内容を忘れない 4. **デフォルトを提案**: 妥当な仮定を示します 5. **段階的に開示**: 質問を一度に出し過ぎない 例: 確認のレスポンス ```json theme={null} { "message": "I'd be happy to help find the right products for your campaign. To provide the best recommendations, could you share:\n\n• What's your campaign budget?\n• When do you want the campaign to run?\n• Which geographic markets are you targeting?\n• What are your success metrics (awareness, conversions, etc.)?", "clarification_needed": true } ``` ## 自然言語処理 パブリッシャーは NLP を用いて次を抽出すべきです。 * **時間表現**: "next quarter", "holiday season", "ASAP" * **予算の示唆**: "\$50K", "low budget", "premium spend" * **オーディエンス描写**: "millennials", "high-income", "parents" * **地理的参照**: "west coast", "major cities", "nationwide" * **目標キーワード**: "awareness", "drive sales", "generate leads" ## ベストプラクティス ### DO: * ✅ brand\_manifest と brief の両方に広告主と商品を記載します * ✅ 測定可能な成功指標を明示します * ✅ 配信期間を明確にします * ✅ ターゲットオーディエンスを具体的に記述します * ✅ 利用可能なクリエイティブフォーマットを記載します * ✅ 予算や制約を示します * ✅ ブランドセーフティ要件を含めます ### DON'T: * ❌ "good performance" のような曖昧な目標だけを書く * ❌ 期間を省略して、後からの確認を期待します * ❌ 定義されていない略語や業界用語だけで記述します * ❌ brand\_manifest と brief で矛盾した内容を入れる * ❌ センシティブまたは機密情報を含めます * ❌ パブリッシャーが自社事情を理解していると想定します ## 例 ### 標準カタログ(ブリーフなし) ```json theme={null} { "brand_manifest": {"name": "Ford", "url": "https://ford.com"}, "brief": null, "filters": { "delivery_type": "non_guaranteed", "channels": ["display", "ctv"] } } ``` **ユースケース**: バイヤーは DMP/CDP に洗練されたオーディエンスセグメントを持っており、[TMP](/docs/trusted-match) でターゲティングを適用するだけでよい。幅広い在庫へのアクセスが目的。 ### EC のブリーフ ``` "Launch our new sustainable fashion line targeting environmentally conscious millennials in urban markets. $75K budget for April, focused on driving online sales with a target ROAS of 4:1. We have video and carousel creatives showcasing the manufacturing process." ``` ### B2B ソフトウェアのブリーフ ``` "Generate qualified leads for our enterprise CRM solution among sales leaders at companies with 500+ employees. Q2 campaign with $100K budget, targeting 2% conversion rate from landing page visits. Display and native formats available." ``` ### ローカルサービスのブリーフ ``` "Drive appointment bookings for our dental practice in Chicago suburbs. $5K monthly budget targeting families with children within 10 miles of our locations. Focus on Saturday availability." ``` ## まとめ ブリーフの役割は買い手によって異なります。 * **発見重視のバイヤー**: 詳細なブリーフにより最適な商品提案を受けられます * **ターゲティング重視のバイヤー**: ブリーフを省略し、フィルターで広い在庫を取得し、自前のターゲティングを適用 * **ハイブリッド**: 最小限のブリーフで選択肢を絞りつつ、ターゲティングの主導権を維持 パブリッシャーは、ブリーフがない場合から包括的な場合まで幅広く対応できる堅牢な処理を実装し、必要に応じて丁寧に対話することが求められます。 # コレクションとインストールメント Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/product-discovery/collections-and-installments AdCP のコレクションとインストールメント — ポッドキャスト、CTV、ストリーミングコンテンツをメディア製品の次元としてモデル化。コレクションレベルターゲティングとインストールメント固有の広告プレースメントを可能にする。 [製品](/docs/media-buy/product-discovery/media-products) は 3 つの独立した軸に沿って在庫を記述します: * **パブリッシャープロパティ** — 広告が実行される WHERE(youtube.com、spotify.com) * **コレクション / インストールメント** — 広告が実行される WHAT CONTENT(特定のシリーズとそのインストールメント) * **プレースメント** — 広告が現れる WHAT POSITION(pre-roll、mid-roll、host read) コレクションとプレースメントは並行する次元で、階層的ではありません。「Pre-roll」はポジション。「Pinnacle Challenge」はコンテンツ。製品はそれらを組み合わせます: 「Acme Streaming の Pinnacle Challenge の pre-roll。」 ## チャネルマッピング コレクション/インストールメントモデルは、メディアチャネル全体の馴染みのある概念にマップされます: | Channel | Collection = | Installment = | Example | | ---------------- | -------------- | ---------------- | -------------------------------------------- | | Podcast | Program | Episode | "Serial" → "Chapter 1" | | Linear TV / CTV | Series | Episode / Airing | "Monday Night Football" → "Week 12" | | Print / Magazine | Publication | Issue | "Vogue Germany" → "May 2026" | | Newsletter | Publication | Edition | "Money Stuff" → "Tuesday March 24" | | YouTube / Social | Series | Video / Post | "Hot Ones" → "Gordon Ramsay" | | DOOH | Network / Loop | Rotation | "Times Square Loop" → "March Rotation" | | Influencer | Campaign | Post / Drop | "Spring Collection" → "Launch Post" | | Cinema | Run | Screening | "Summer Blockbuster Run" → "Opening Weekend" | | Radio | Program | Broadcast | "Morning Drive" → "March 24 Broadcast" | 各コレクションの `kind` フィールドは、それとそのインストールメントをどう解釈するかを示します。 ## コレクションオブジェクト コレクションは、時とともにインストールメントを生成する永続的なコンテンツプログラムです。コレクションはプロパティのように機能します — パブリッシャーが `adagents.json` でそれらを宣言し、製品は `publisher_domain` と `collection_ids` を持つ `collections` セレクター経由でそれらを参照します。 | Field | Type | Required | Description | | --------------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `collection_id` | string | Yes | パブリッシャー割り当ての識別子。パブリッシャーの `adagents.json` で宣言。製品は `collections` セレクター経由でコレクションを参照。クロスセラーマッチングには distribution identifier を使う。 | | `kind` | string | No | どの種類のコンテンツプログラムか: `series`(TV/ポッドキャスト)、`publication`(プリント号)、`event_series`(ライブイベント)、`rotation`(DOOH)。デフォルトは `series`。 | | `name` | string | Yes | 人間可読なコレクション名 | | `description` | string | No | コレクションが何についてか | | `genre` | string\[] | No | ジャンルタグ。`genre_taxonomy` が存在するとき、値は分類 ID(例: IAB Content Taxonomy 3.0)。そうでなければフリーフォーム。 | | `genre_taxonomy` | string | No | ジャンル値の分類システム(例: `iab_content_3.0`)。機械可読なブランドセーフティに推奨。 | | `language` | string | No | 主要言語(BCP 47) | | `content_rating` | object | No | ベースラインレーティング: `system` + `rating`。システム: `tv_parental`、`mpaa`、`podcast`、`esrb`、`bbfc`、`fsk`、`acb`、`custom`。エピソードはオーバーライドできる。 | | `cadence` | string | No | `daily`、`weekly`、`seasonal`、`event`、`irregular` | | `season` | string | No | 現在または最新のシーズン識別子(例: `"3"`、`"2026"`) | | `status` | string | No | `active`、`hiatus`、`ended`、`upcoming` | | `production_quality` | string | No | `professional`、`prosumer`、`ugc`。セラー宣言。OpenRTB `content.prodq` にマップ。 | | `talent` | array | No | ホスト、レギュラーキャスト、クリエイター。各エントリーは `role`、`name`、[brand.json](/docs/brand-protocol/brand-json) にリンクするオプション `brand_url` を持つ。 | | `special` | object | No | 存在するとき、このコレクションはスペシャル — 実世界のイベントや機会にアンカーされたコンテンツ。[specials and limited series](#specials-and-limited-series) を参照。 | | `limited_series` | object | No | 存在するとき、このコレクションはリミテッドシリーズ — 定義されたアークと終了日を持つ境界された実行。[specials and limited series](#specials-and-limited-series) を参照。 | | `distribution` | array | No | このコレクションがどこで配信されるか、パブリッシャーごとのプラットフォーム固有識別子を伴う。 | | `deadline_policy` | object | No | インストールメントのデフォルト締切ルール。エージェントは `scheduled_at` マイナス `lead_days` から絶対締切を計算。明示的 `deadlines` を持つエピソードはオーバーライド。[deadline policy](#deadline-policy) を参照。 | | `related_collections` | array | No | 他のコレクションへの関係: `spinoff`、`companion`、`sequel`、`prequel`、`crossover`。各エントリーは `collection_id` + `relationship` を持つ。参照は同じパブリッシャーの `adagents.json` にスコープ。対称タイプ(`companion`、`crossover`)は両コレクションの宣言を要求しない。 | ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/collection.json", "collection_id": "pinnacle_challenge", "name": "The Pinnacle Challenge", "description": "Competition reality collection with extreme physical and mental challenges", "genre": ["IAB1", "IAB1-6"], "genre_taxonomy": "iab_content_3.0", "language": "en", "content_rating": { "system": "tv_parental", "rating": "TV-PG" }, "cadence": "weekly", "season": "1", "status": "active", "production_quality": "professional", "talent": [ { "role": "host", "name": "Jordan Vega", "brand_url": "https://jordanvega.example.com/brand.json" } ], "distribution": [ { "publisher_domain": "youtube.com", "identifiers": [ { "type": "youtube_channel_id", "value": "UCexample123456" } ] }, { "publisher_domain": "acmestreaming.example.com", "identifiers": [ { "type": "imdb_id", "value": "tt9876543" } ] } ] } ``` ## インストールメントオブジェクト インストールメントはコレクションの特定のインストールメントです。すべてのインストールメントが事前に既知とは限りません — 週次ポッドキャストは来週のインストールメントのみをスケジュールしているかもしれず、一部のインストールメントは暫定的かもしれません(プレイオフの Game 7 は Game 6 に依存)。 | Field | Type | Required | Description | | -------------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `installment_id` | string | Yes | コレクション内で一意の識別子 | | `collection_id` | string | 必要なとき | 親コレクション。製品が複数のコレクションにまたがるとき必須。 | | `name` | string | No | エピソードタイトル | | `season` | string | No | シーズン識別子(例: `"1"`、`"2026"`) | | `installment_number` | string | No | シーズン内のエピソード番号 | | `scheduled_at` | datetime | No | インストールメントが放映または公開されるとき | | `status` | string | No | `scheduled`、`tentative`、`live`、`postponed`、`cancelled`、`aired`、`published` | | `duration_seconds` | integer | No | 期待される期間 | | `flexible_end` | boolean | No | 終了時間が近似か(ライブイベント) | | `valid_until` | datetime | No | このデータが期限切れになるとき。エージェントは暫定インストールメントに予算をコミットする前に再クエリすべき。 | | `content_rating` | object | No | 存在するときコレクションのベースラインをオーバーライド | | `topics` | string\[] | No | ブランドセーフティのためのインストールメントレベルコンテンツトピック。存在するときコレクションの `genre_taxonomy` を使う。 | | `special` | object | No | インストールメント固有のイベントコンテキスト。存在するとき、このインストールメントは実世界のイベントにアンカーされる。コレクションレベル `special` をオーバーライド。 | | `guest_talent` | array | No | インストールメント固有のゲスト。コレクションのレギュラー `talent` に追加。 | | `ad_inventory` | object | No | ブレークベースの広告在庫構成 | | `deadlines` | object | No | このインストールメントのブッキング、キャンセル、素材提出締切。[installment deadlines](#installment-deadlines) を参照。 | | `derivative_of` | object | No | このインストールメントが完全なインストールメントから導出されたクリップ、ハイライト、リキャップのとき。`installment_id` + `type`(`clip`、`highlight`、`recap`、`trailer`、`bonus`)を持つ。ソース `installment_id` は同じレスポンスにあること。 | ### エピソードステータスライフサイクル | Status | Meaning | | ----------- | ------------------------- | | `scheduled` | 確認済み、起こる | | `tentative` | 起こらないかも(外部条件に依存) | | `live` | 今まさに放映またはストリーミング中 | | `postponed` | スケジュールされていたが未来の日付に延期 | | `cancelled` | 起こらない | | `aired` | 既に放送 — バックカタログとリプレイ在庫用 | | `published` | 既にリリース — オンデマンドキャッチアップ在庫用 | 期待される遷移: `scheduled` または `tentative` → `live` → `aired` または `published`。`scheduled` インストールメントは `postponed`(延期)または `cancelled` になるかも。`postponed` インストールメントは再スケジュールされると `scheduled` に戻る。`tentative` インストールメントは `scheduled`、`cancelled`、または `postponed` に解決する。 ### コレクションからの継承 エピソードはオーバーライドしないコレクションレベルフィールドを継承します: * **`content_rating`**: エピソード値がコレクションベースラインをオーバーライド。不在のとき、コレクションのレーティングが適用。 * **`special`**: エピソード値がコレクションレベルスペシャルをオーバーライド。不在のとき、コレクションのスペシャルが適用。通常のコレクションはイベントアンカーのインストールメントを持てる(例: 選挙夜スペシャルを持つ日次ニュースコレクション)。 * **`guest_talent`**: コレクションのレギュラー `talent` に追加 — それを置き換えない。 * **`topics`**: ブランドセーフティの追加コンテキスト、コレクションの `genre` の置き換えではない。 バイヤーエージェントは両レベルを評価します: コレクションベースラインがデフォルトの安全プロファイルを提供し、インストールメントフィールドが特定のインストールメントのためそれを絞ります。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/installment.json", "installment_id": "s1e03_the_wall", "collection_id": "pinnacle_challenge", "name": "The Wall", "season": "1", "installment_number": "3", "scheduled_at": "2026-04-07T20:00:00Z", "status": "scheduled", "duration_seconds": 3600, "valid_until": "2026-04-06T20:00:00Z", "content_rating": { "system": "tv_parental", "rating": "TV-14" }, "topics": ["IAB17-18"], "guest_talent": [ { "role": "guest", "name": "Samira Okafor", "brand_url": "https://samiraokafor.example.com/brand.json" } ], "ad_inventory": { "expected_breaks": 4, "total_ad_seconds": 480, "max_ad_duration_seconds": 120, "unplanned_breaks": false, "supported_formats": ["video", "audio"] } } ``` ## Installment deadlines エピソードはブッキング、キャンセル、素材提出締切を運べます。これらは在庫が予定された単位に結びつく任意のチャネルに適用されます — プリント号、ポッドキャストインストールメント、インフルエンサー投稿、リニア TV 放映、DOOH ローテーション。 | Field | Type | Description | | ----------------------- | -------- | ------------------------------- | | `booking_deadline` | datetime | このインストールメントにプレースメントをブッキングする最終日時 | | `cancellation_deadline` | datetime | ペナルティなしでキャンセルする最終日時 | | `material_deadlines` | array | クリエイティブ素材提出の順序付き段階 | 締切は時系列順でなければなりません(MUST): `booking_deadline` ≤ `cancellation_deadline` ≤ 各 `material_deadlines[n].due_at`(配列インデックス昇順) ≤ インストールメントの `scheduled_at`。バイヤーエージェントは、締切がこの順序に違反するインストールメントを拒否すべきです(SHOULD)。 各素材締切は以下を持ちます: | Field | Type | Required | Description | | -------- | -------- | -------- | -------------------------------------------------------------- | | `stage` | string | Yes | 段階識別子(`draft`、`final`、またはセラー定義) | | `due_at` | datetime | Yes | この段階の素材が必要なとき | | `label` | string | No | セラーが何を必要とするか(例: "Talking points"、"Press-ready PDF with bleed") | 2 段階パターン — draft 次に final — は幅広いチャネルをカバーします: | Channel | Draft stage | Final stage | | ------------------- | ------------------ | --------------- | | Print | レビュー用の生アートワーク | ブリード付きプレス対応 PDF | | Podcast (host read) | トーキングポイントとブリーフ | 承認されたスクリプト | | Influencer | ブランドガイドラインとキーメッセージ | 承認された投稿コンテンツ | | Linear TV | ラフカット | エンコードされた放送スポット | | DOOH | レビュー用ドラフトクリエイティブ | 画面仕様ごとの最終アセット | ### 締切付きポッドキャスト ```json theme={null} { "installment_id": "ep47", "name": "The future of autonomous supply chains", "scheduled_at": "2026-04-07T10:00:00Z", "status": "scheduled", "guest_talent": [ { "role": "guest", "name": "Kai Tanaka" } ], "deadlines": { "booking_deadline": "2026-03-28T17:00:00Z", "cancellation_deadline": "2026-03-31T17:00:00Z", "material_deadlines": [ { "stage": "draft", "due_at": "2026-04-01T17:00:00Z", "label": "Talking points and brand guidelines for host read" }, { "stage": "final", "due_at": "2026-04-04T17:00:00Z", "label": "Approved script" } ] } } ``` ### 締切付きプリント号 ```json theme={null} { "installment_id": "2026-05", "name": "Mai 2026", "season": "2026", "installment_number": "5", "scheduled_at": "2026-05-01T00:00:00+02:00", "status": "scheduled", "deadlines": { "booking_deadline": "2026-03-15T17:00:00+01:00", "cancellation_deadline": "2026-03-22T17:00:00+01:00", "material_deadlines": [ { "stage": "draft", "due_at": "2026-03-29T17:00:00+01:00", "label": "Raw artwork for review and color proofing" }, { "stage": "final", "due_at": "2026-04-05T17:00:00+02:00", "label": "Press-ready PDF/X-4, CMYK, 300 DPI, 3mm bleed" } ] } } ``` ### 締切付きインフルエンサー投稿 ```json theme={null} { "installment_id": "post_2026_04_10", "name": "Spring campaign post", "scheduled_at": "2026-04-10T12:00:00Z", "status": "scheduled", "deadlines": { "cancellation_deadline": "2026-04-03T17:00:00Z", "material_deadlines": [ { "stage": "draft", "due_at": "2026-04-05T17:00:00Z", "label": "Key messages, product images, and brand guidelines" }, { "stage": "final", "due_at": "2026-04-08T17:00:00Z", "label": "Approved post content and caption" } ] } } ``` 締切はオプションです。ラン・オブ・コレクションのデジタル製品は通常それらを省略します。パターンは事前素材要件を伴う保証在庫に最も価値があります。 ## Deadline policy 高頻度コレクション(日刊新聞、週次ポッドキャスト)は、すべてのインストールメントが明示的な締切を運ぶ場合大きなペイロードを生成します。コレクションの `deadline_policy` は、エージェントが各インストールメントの `scheduled_at` から締切を計算するのに使うリードタイムルールを宣言します。 | Field | Type | Description | | ------------------------ | ------- | ------------------------------------------------ | | `booking_lead_days` | integer | プレースメントがブッキングされなければならない `scheduled_at` 前の日数 | | `cancellation_lead_days` | integer | キャンセルがペナルティなしの `scheduled_at` 前の日数 | | `material_stages` | array | `stage`、`lead_days`、オプション `label` を伴うデフォルト素材提出段階 | | `business_days_only` | boolean | true のとき、lead\_days は月〜金のみをカウント。デフォルトは false。 | ### ポリシー付き日刊新聞 すべての号の締切を列挙する代わりに、コレクションはルールを一度宣言します: ```json theme={null} { "collection_id": "bergedorfer_zeitung", "name": "Bergedorfer Zeitung", "kind": "publication", "cadence": "daily", "status": "active", "deadline_policy": { "booking_lead_days": 4, "cancellation_lead_days": 3, "material_stages": [ { "stage": "final", "lead_days": 2, "label": "Druckfertige PDF" } ], "business_days_only": true } } ``` 4 月 1 日号(`scheduled_at: "2026-04-01"`)について、エージェントは計算します: * **ブッキング締切**: 4 営業日前 = 3 月 26 日 * **キャンセル締切**: 3 営業日前 = 3 月 27 日 * **素材期限**: 2 営業日前 = 3 月 28 日 明示的 `deadlines` を持つエピソードはポリシーをオーバーライドします。より厳しいターンアラウンドのスペシャル版は自身の締切を宣言します。通常の号はポリシーから継承します。 ### ポリシー付き週次ポッドキャスト ```json theme={null} { "collection_id": "wonderstruck_weekly", "name": "Wonderstruck Weekly", "kind": "series", "cadence": "weekly", "deadline_policy": { "booking_lead_days": 10, "cancellation_lead_days": 7, "material_stages": [ { "stage": "draft", "lead_days": 5, "label": "Talking points and brand guidelines" }, { "stage": "final", "lead_days": 3, "label": "Approved script for host read" } ] } } ``` ### ポリシー対明示的締切 | Scenario | Use | | ----------------------- | --------------------------------------- | | 日刊新聞、一貫した締切 | コレクションの `deadline_policy` | | 週次ポッドキャスト、一貫した締切 | コレクションの `deadline_policy` | | 月刊誌、号ごとに変わるリードタイム | 各インストールメントの明示的 `deadlines` | | 厳しいターンアラウンドの 1 回限りスペシャル | そのインストールメントの明示的 `deadlines`、ポリシーが残りをカバー | 両方が存在するとき、明示的なインストールメント `deadlines` が優先します。エージェントは自身のものを持つインストールメントにポリシーから締切を計算すべきでありません(SHOULD NOT)。 ## Specials and limited series コレクションは、その性質をバイヤーエージェントにシグナルするオプションのアノテーションを運べます。これらは合成可能です — コレクションはスペシャルとリミテッドシリーズの両方になれます(例: 4 インストールメントのオリンピックドキュメンタリー)。 ### スペシャル スペシャルは実世界のイベントや機会にアンカーされたコンテンツです。`special` オブジェクトはコレクションとインストールメントの両方に現れられます。コレクション上では、コレクション全体がイベントアンカーであることを意味します。インストールメント上では、その特定のインストールメントがイベントアンカーであることを意味します(`content_rating` と同じ継承パターンに従い、存在するときコレクションレベルスペシャルをオーバーライド)。 | Field | Type | Required | Description | | ---------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `name` | string | Yes | イベントの名前(例: "Olympics 2028"、"Super Bowl LXI") | | `category` | string | No | `awards`、`championship`、`concert`、`conference`、`election`、`festival`、`gala`、`holiday`、`premiere`、`product_launch`、`reunion`、`tribute` | | `starts` | datetime | No | イベントが開始するとき | | `ends` | datetime | No | イベントが終了するとき。単日イベントには省略。 | ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/collection.json", "collection_id": "apex_finals_2026", "name": "Apex Championship Finals 2026", "genre": ["IAB17", "IAB17-12"], "genre_taxonomy": "iab_content_3.0", "cadence": "event", "status": "upcoming", "special": { "name": "Apex Championship Finals 2026", "category": "championship", "starts": "2026-05-18T19:00:00Z", "ends": "2026-05-25T23:00:00Z" }, "talent": [ { "role": "host", "name": "Deshawn Moreaux" } ] } ``` `special` フィールドは `cadence` と別です。Cadence はリリース頻度を記述します(`event` = 1 回限りまたは時折)。`special` オブジェクトは、どの実世界イベントがコンテンツをアンカーするかといつ起こるかを記述します — バイヤーエージェントがタイミング関連性とプレミアム価格設定を評価するために必要な情報。 ### リミテッドシリーズ リミテッドシリーズは、定義されたアークを持つ境界されたコンテンツ実行です。進行中のシリーズと異なり、リミテッドシリーズは計画された終了を持ちます。 | Field | Type | Required | Description | | -------------------- | -------- | -------- | --------------- | | `total_installments` | integer | No | 計画されたインストールメント数 | | `starts` | datetime | No | シリーズが始まるとき | | `ends` | datetime | No | シリーズが終わるとき | ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/collection.json", "collection_id": "ember_s3", "name": "The Ember", "description": "Award-winning drama following a chef navigating the pressure of running a top restaurant", "genre": ["IAB1", "IAB1-7"], "genre_taxonomy": "iab_content_3.0", "cadence": "weekly", "status": "active", "limited_series": { "total_installments": 8, "starts": "2026-02-28T21:00:00Z", "ends": "2026-04-18T21:00:00Z" } } ``` これはバイヤーエージェントに The Ember が有限の機会であることを伝えます — 7 週間で 8 インストールメント。`limited_series` フィールドは `season` と別です: コレクションはリミテッドでなくてもシーズンを持てる(進行中のシリーズもシーズンを持つ)し、リミテッドシリーズはコレクションが計画された終了を持つという構造的コミットメントです。 ## 製品がコレクションをどう参照するか 製品は `collections` 経由でコレクションを参照します — パブリッシャーの `adagents.json` で宣言されたコレクションを指す `{publisher_domain, collection_ids}` セレクターの配列。これは `publisher_properties` と同じパターンです。バイヤーはパブリッシャーの `adagents.json` から完全なコレクションオブジェクトを解決します。エピソードは `installments` 配列で製品ごとにリストされます、なぜなら異なる製品は同じコレクションの異なるインストールメントをスコープするかもしれないから。 ### ラン・オブ・コレクション(特定のインストールメントなし) 製品はインストールメントをリストせずにコレクションを参照できます。これは「このコレクション全体の在庫、フライト日中に放映されるインストールメントが何であれ」を意味します: ```json theme={null} { "product_id": "pinnacle_run_of_collection", "name": "Pinnacle Challenge Run of Show", "collections": [{ "publisher_domain": "acmestreaming.example.com", "collection_ids": ["pinnacle_challenge"] }], "placements": [ { "kind": "seller_inline", "placement_id": "pre_roll", "name": "Pre-roll", "mode": "targetable" } ], "delivery_type": "non_guaranteed", "pricing_options": [ { "pricing_option_id": "cpm", "pricing_model": "cpm", "floor_price": 25.00, "currency": "USD" } ] } ``` ### 特定のインストールメント プレミアムまたは保証バイには、セラーは特定のインストールメントにスコープします: ```json theme={null} { "product_id": "pinnacle_finale", "name": "Pinnacle Challenge Season Finale Sponsorship", "collections": [{ "publisher_domain": "acmestreaming.example.com", "collection_ids": ["pinnacle_challenge"] }], "installments": [ { "installment_id": "s1e10_finale", "name": "The Grand Finale", "scheduled_at": "2026-06-02T20:00:00Z", "status": "scheduled", "ad_inventory": { "expected_breaks": 6, "total_ad_seconds": 720, "unplanned_breaks": false } } ], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "flat", "pricing_model": "flat_rate", "fixed_price": 2000000, "currency": "USD" } ] } ``` ## get\_products レスポンス構造 製品は `collections` セレクター経由でコレクションを参照します — `publisher_properties` と同じパターン。各セレクターは、そのパブリッシャーの `adagents.json` で宣言されたコレクションを指す `publisher_domain` と `collection_ids` 配列を持ちます。バイヤーは `adagents.json` から完全なコレクションオブジェクトを解決します。 ```json theme={null} { "products": [ { "product_id": "pinnacle_april_bundle", "name": "Pinnacle Challenge April Sponsorship", "collections": [{ "publisher_domain": "acmestreaming.example.com", "collection_ids": ["pinnacle_challenge"] }], "installments": [ { "installment_id": "s1e03", "name": "The Wall", "scheduled_at": "2026-04-07T20:00:00Z", "status": "scheduled", "content_rating": { "system": "tv_parental", "rating": "TV-14" }, "guest_talent": [{ "role": "guest", "name": "Samira Okafor" }], "valid_until": "2026-04-06T20:00:00Z" }, { "installment_id": "s1e04", "name": "TBD", "scheduled_at": "2026-04-14T20:00:00Z", "status": "tentative", "valid_until": "2026-04-07T20:00:00Z" } ], "placements": [ { "kind": "seller_inline", "placement_id": "pre_roll", "name": "Pre-roll", "mode": "targetable" }, { "kind": "seller_inline", "placement_id": "mid_roll", "name": "Mid-roll", "mode": "targetable" } ], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "flat", "pricing_model": "flat_rate", "fixed_price": 500000, "currency": "USD" } ] } ] } ``` ## 正準コレクションアイデンティティ コレクションのアイデンティティは **`{publisher_domain, collection_id}`** です — `adagents.json` でそれを宣言するパブリッシャーにスコープ。これは任意のコレクション作成者が自身の正準レジストリとして機能できることを意味します。 ### 正準パブリッシャーとしての作成者 MrBeast のような作成者は `mrbeast.com/adagents.json` でコレクションを宣言します。その作成者の在庫をパッケージ化する任意のセラー — YouTube セールスハウス、CTV ディストリビューター、作成者自身のチーム — は同じ正準ソースを参照します: ```json theme={null} { "products": [ { "product_id": "beast_games_youtube", "name": "Beast Games - YouTube Pre-roll", "collections": [ { "publisher_domain": "mrbeast.com", "collection_ids": ["beast_games"] } ], "publisher_properties": [ { "publisher_domain": "youtube.com", "property_ids": ["UCX6OQ3DkcsbYNE6H8uQQuVA"] } ], "delivery_type": "non_guaranteed", "pricing_options": [ { "pricing_option_id": "cpm", "pricing_model": "cpm", "floor_price": 18.00, "currency": "USD" } ] } ] } ``` バイヤーは、在庫がどのセラーまたはプラットフォームから来るかにかかわらず `mrbeast.com` + `beast_games` を見ます。IMDb ID や distribution identifier のクロス参照は不要 — 作成者のドメイン **が** レジストリです。 ### 作成者が adagents.json を公開しないとき すべてのコレクションが作成者所有のドメインを持つわけではありません。ストリーミングプラットフォームのオリジナルシリーズは、そのプラットフォームの `adagents.json` にのみ存在するかもしれません。その場合、プラットフォームが正準パブリッシャーで `collections` は彼らのドメインを指します。コレクションオブジェクトの distribution identifier は、同じコレクションが単一の正準ソースなしに複数のプラットフォームに現れるときクロスセラーマッチングを処理します。 ### アイデンティティ解決優先順位 バイヤーエージェントはこの順序でコレクションアイデンティティを解決すべきです: 1. **正準パブリッシャー** — 作成者自身の `adagents.json` からの `publisher_domain` + `collection_id`。最強のシグナル。 2. **プラットフォーム非依存識別子** — コレクションの `distribution` 配列からの `imdb_id`、`gracenote_id`、`eidr_id`。正準パブリッシャーが存在しないとき信頼できるクロス参照。 3. **プラットフォーム固有識別子** — Spotify、Apple、YouTube ID。プラットフォーム内で有用だがユニバーサルではない。 ## ディスカバリー例 ### インストールメント付き CTV コレクション 今後のインストールメントを持つ既知のコレクションに対してスポンサーシップを販売するストリーミングプラットフォーム: ```json theme={null} { "products": [ { "product_id": "nova_kitchen_april", "name": "Nova Kitchen - April Episodes", "collections": [{ "publisher_domain": "novastreaming.example.com", "collection_ids": ["nova_kitchen"] }], "publisher_properties": [{ "publisher_domain": "novastreaming.example.com", "selection_type": "all" }], "installments": [ { "installment_id": "s3e09", "name": "Fire and Ice", "scheduled_at": "2026-04-05T21:00:00Z", "status": "scheduled", "duration_seconds": 2700, "ad_inventory": { "expected_breaks": 3, "total_ad_seconds": 360, "max_ad_duration_seconds": 30, "unplanned_breaks": false, "supported_formats": ["video"] } }, { "installment_id": "s3e10", "scheduled_at": "2026-04-12T21:00:00Z", "status": "tentative", "valid_until": "2026-04-06T00:00:00Z" } ], "placements": [ { "kind": "seller_inline", "placement_id": "pre_roll", "name": "Pre-roll (15s)", "mode": "targetable" }, { "kind": "seller_inline", "placement_id": "mid_roll", "name": "Mid-roll (30s)", "mode": "targetable" } ], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "cpm_fixed", "pricing_model": "cpm", "fixed_price": 38.00, "currency": "USD" } ] } ] } ``` ### 関連コレクションと派生コンテンツ メインコレクションとそのコンパニオンアフターコレクション、プラスハイライトクリップを提供するストリーミングプラットフォーム: ```json theme={null} { "products": [ { "product_id": "nova_highlights", "name": "Nova Kitchen Highlights Package", "collections": [{ "publisher_domain": "novastreaming.example.com", "collection_ids": ["nova_kitchen"] }], "installments": [ { "installment_id": "s3e09", "name": "Fire and Ice", "status": "aired", "duration_seconds": 2700 }, { "installment_id": "s3e09_highlights", "name": "Fire and Ice - Best Moments", "status": "published", "duration_seconds": 180, "derivative_of": { "installment_id": "s3e09", "type": "highlight" } } ], "placements": [ { "kind": "seller_inline", "placement_id": "pre_roll", "name": "Pre-roll (15s)", "mode": "targetable" } ], "delivery_type": "non_guaranteed", "pricing_options": [ { "pricing_option_id": "cpm", "pricing_model": "cpm", "floor_price": 12.00, "currency": "USD" } ] } ] } ``` ハイライトクリップは `derivative_of` 経由でそのソースインストールメントを参照します。コンパニオンアフターコレクションはメインコレクションの `related_collections` 経由でリンクされます — Nova Kitchen をターゲットするバイヤーはアフターコレクションを追加のリーチ機会として発見できます。 ### 配信付きポッドキャスト 複数の配信プラットフォーム全体で販売するポッドキャストネットワーク。コレクションの `adagents.json` の `distribution` 配列が完全な配信フットプリントを捕捉します。バイヤーはクロスセラーマッチングに distribution identifier を使います: ```json theme={null} { "products": [ { "product_id": "wonderstruck_april", "name": "Wonderstruck Weekly - April Episodes", "collections": [{ "publisher_domain": "wonderstruck.example.com", "collection_ids": ["wonderstruck_weekly"] }], "installments": [ { "installment_id": "ep47", "name": "The future of autonomous supply chains", "scheduled_at": "2026-04-07T10:00:00Z", "status": "scheduled", "guest_talent": [ { "role": "guest", "name": "Kai Tanaka", "brand_url": "https://kaitanaka.example.com/brand.json" } ] }, { "installment_id": "ep48", "scheduled_at": "2026-04-14T10:00:00Z", "status": "tentative" }, { "installment_id": "ep49", "scheduled_at": "2026-04-21T10:00:00Z", "status": "tentative" } ], "placements": [ { "kind": "seller_inline", "placement_id": "pre_roll", "name": "Pre-roll (30s)", "mode": "targetable" }, { "kind": "seller_inline", "placement_id": "mid_roll", "name": "Mid-roll host read (60s)", "mode": "targetable" } ], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "flat_monthly", "pricing_model": "flat_rate", "fixed_price": 15000, "currency": "USD" }, { "pricing_option_id": "per_episode", "pricing_model": "flat_rate", "fixed_price": 5000, "currency": "USD" } ] } ] } ``` 暫定インストールメント(ep48、ep49)は名前やゲスト情報を持ちません — まだ生成されていません。バイヤーはコレクションのベースラインプロファイルと ep47 の既知の詳細に基づいて評価します。 ### 暫定インストールメント付きライブイベント ライブイベントシリーズに対してスポンサーシップを販売するスポーツリーグ。ライブ放送は、試合期間が予測不可能なため `flexible_end` を、広告ブレークが固定スケジュールではなく試合フロー(タイムアウト、ピリオドブレーク)に従うため `unplanned_breaks` を使います。暫定 Game 5 はシリーズ結果に依存 — どちらのチームも 4 試合で勝たない場合のみ起こります。 ```json theme={null} { "products": [ { "product_id": "apex_finals_sponsorship", "name": "Apex Championship Finals — Category Sponsorship", "collections": [{ "publisher_domain": "apexleague.example.com", "collection_ids": ["apex_championship_2026"] }], "installments": [ { "installment_id": "finals_game4", "name": "Game 4: Titan City vs Coastal FC", "scheduled_at": "2026-05-18T19:00:00Z", "status": "scheduled", "flexible_end": true, "ad_inventory": { "expected_breaks": 8, "total_ad_seconds": 960, "max_ad_duration_seconds": 30, "unplanned_breaks": true, "supported_formats": ["video"] } }, { "installment_id": "finals_game5", "name": "Game 5 — If necessary", "scheduled_at": "2026-05-21T19:00:00Z", "status": "tentative", "flexible_end": true, "valid_until": "2026-05-19T06:00:00Z", "ad_inventory": { "expected_breaks": 8, "total_ad_seconds": 960, "max_ad_duration_seconds": 30, "unplanned_breaks": true, "supported_formats": ["video"] } } ], "placements": [ { "kind": "seller_inline", "placement_id": "in_game_overlay", "name": "In-game overlay (10s)", "mode": "targetable" }, { "kind": "seller_inline", "placement_id": "halftime", "name": "Halftime feature (60s)", "mode": "targetable" } ], "delivery_type": "guaranteed", "exclusivity": "category", "pricing_options": [ { "pricing_option_id": "per_game", "pricing_model": "flat_rate", "fixed_price": 750000, "currency": "USD" } ] } ] } ``` このパターンはライブ在庫のため設計されたいくつかの機能を組み合わせます: `cadence: "event"` は非反復シリーズをシグナル、`flexible_end` はバイヤーに放送長が近似であることを伝え、`unplanned_breaks: true` は広告ブレークが事前決定されたスケジュールではなく試合フローに従うことを示します。暫定 Game 5 は、バイヤーエージェントがいつ再クエリするか知るよう `valid_until` を含みます — シリーズが 4 試合で終わる場合、そのインストールメントは `cancelled` に解決します。バイヤーは常に予算をコミットする前に暫定インストールメントの `valid_until` をチェックすべきです。 ### コレクションの発見 バイヤーは標準の `get_products` ワークフローを通じてコレクションを発見します。自然言語ブリーフがコレクション選択を駆動します: ```json theme={null} { "buying_mode": "brief", "brief": "Podcast sponsorships for technology collections reaching startup founders in April", "filters": { "channels": ["podcast"], "start_date": "2026-04-01", "end_date": "2026-04-30" } } ``` セラーは `collections` セレクターを持つ製品を返します。バイヤーは各パブリッシャーの [adagents.json](/docs/governance/property/adagents) から完全なコレクションオブジェクトを解決します。レジストリクライアントは、`/api/registry/catalog/collections/sync` と `/api/registry/catalog/collections/distribution` を通じてクロールされたコレクションカタログをブートストラップしルックアップすることもでき、YouTube チャネル ID のようなプラットフォームエイリアスをパブリッシャー宣言のコレクションにクロス参照するのに有用です。 コミュニティ保守エントリーには、レジストリモデレーターは `PUT /api/registry/catalog/collections/{publisher_domain}/{collection_id}` でパブリッシャースコープのコレクションをシードできます。これは貢献されたレジストリレコードを作成し同じ `collection.created` / `collection.updated` フィードイベントを発行します。パブリッシャーオリジン証明を置き換えません: パブリッシャーが自身の `adagents.json` でコレクションを宣言すると、権威的宣言が勝ちます。 ## ブランドセーフティ コレクションは 2 レベルのブランドセーフティモデルを提供します: コレクションベースラインとインストールメントごとのオーバーライド。 ### コレクションベースライン コレクションのデフォルトブランドセーフティプロファイルは以下から来ます: * `content_rating` — コレクションの宣言されたレーティングシステムと値 * `genre` — コンテンツカテゴリー(理想的には機械可読な評価のため `genre_taxonomy` を使う) * `talent` — ホストとレギュラーキャスト、より深い評価のためオプションの [brand.json](/docs/brand-protocol/brand-json) 参照を伴う これは、個別のインストールメントコンテンツがまだ既知でないときバイヤーが評価するものです。 ### インストールメントオーバーライド インストールメント詳細が利用可能なとき、それらは安全プロファイルをシフトできます: * コレクションベースラインと異なる `content_rating`(今週は TV-PG の代わりに TV-14) * タレントプロファイルを変える `guest_talent`(論争的なゲスト) * インストールメント固有のコンテンツシグナルを追加する `topics` ### モデル化されないもの AdCP は未知の未来のインストールメントのコンテンツ安全性を予測しません。「すべての 4 月インストールメント」にコミットするバイヤーは、コレクションのベースラインプロファイルに基づいて購入し、変動を受け入れます。セラーのコンテンツ標準とコレクションの実績がその決定のバイヤーの基礎です。 ## Distribution identifier 各コレクションの `distribution` 配列は、それをプラットフォーム固有識別子で特定のパブリッシャープラットフォームにマップします。これはクロスセラーマッチングを可能にします: 2 つの異なるセラーが両方とも同じコレクションの製品を提供するとき、バイヤーエージェントは共有識別子経由でそれらをマッチできます。 ### クロスセラーマッチング プラットフォーム非依存識別子は重複排除に最も信頼できます: | Type | Example | Notes | | -------------- | ------------------------------------ | ------------------- | | `imdb_id` | `tt1234567` | ユニバーサルクロスプラットフォーム参照 | | `gracenote_id` | `EP012345678` | TV メタデータの業界標準 | | `eidr_id` | `10.5240/XXXX-XXXX-XXXX-XXXX-XXXX-C` | 視聴覚コンテンツの ISO 10528 | 番組は利用可能なとき少なくとも 1 つのプラットフォーム非依存識別子を含むべきです(SHOULD)。 ### プラットフォーム固有識別子 ```json Podcast theme={null} { "distribution": [ { "publisher_domain": "spotify.com", "identifiers": [{ "type": "spotify_collection_id", "value": "4rOoJ6Egrf8K2IrywzwOMk" }] }, { "publisher_domain": "apple.com", "identifiers": [{ "type": "apple_podcast_id", "value": "1234567890" }] }, { "publisher_domain": "feeds.example.com", "identifiers": [{ "type": "rss_url", "value": "https://feeds.example.com/collection.xml" }] } ] } ``` ```json Video / CTV theme={null} { "distribution": [ { "publisher_domain": "youtube.com", "identifiers": [{ "type": "youtube_channel_id", "value": "UCexample123456" }] }, { "publisher_domain": "acmestreaming.example.com", "identifiers": [{ "type": "amazon_title_id", "value": "B0DFBT5GBP" }] } ] } ``` 利用可能なポッドキャストタイプ: `apple_podcast_id`、`spotify_collection_id`、`rss_url`、`podcast_guid`、`amazon_music_id`、`iheart_id`、`podcast_index_id`。利用可能なビデオ/CTV タイプ: `youtube_channel_id`、`youtube_channel_handle`、`youtube_channel_url`、`youtube_playlist_id`、`amazon_title_id`、`roku_channel_id`、`pluto_channel_id`、`tubi_id`、`peacock_id`、`tiktok_id`、`twitch_channel`。その他: `domain`、`substack_id`。YouTube チャネルには、`youtube_channel_id` が優先される正準識別子。ハンドルと URL は、チャネル ID がまだ解決されていないときディスカバリーとマッチングのエイリアス。 ## 広告在庫 エピソードは `ad_inventory` オブジェクトでブレークベースの広告在庫を宣言します: | Field | Type | Description | | ------------------------- | --------- | ----------------------------------------------------------------- | | `expected_breaks` | integer | 計画された広告ブレーク数 | | `total_ad_seconds` | integer | すべてのブレーク全体の広告時間の合計秒 | | `max_ad_duration_seconds` | integer | ブレーク内の単一広告の最大期間 | | `unplanned_breaks` | boolean | `false`: すべてのブレークが事前定義。`true`: ブレークがライブ条件(スポーツタイムアウト、ライブニュース)で駆動。 | | `supported_formats` | string\[] | ブレークでサポートされるフォーマットタイプ(例: `"video"`、`"audio"`) | ホストリード、カスタム統合、スポンサーシップのような非ブレーク広告フォーマットには、代わりに製品 [プレースメント](/docs/media-buy/product-discovery/media-products#placements) を使います。ポッドキャスト製品は「mid-roll host read (60s)」のプレースメントを持つかもしれません — それは製品のプレースメントで、`ad_inventory` の一部ではありません。 ## LEAP との関係 インストールメントモデルは、ライブストリーミングイベントのための IAB Tech Lab の LEAP Forecasting API と揃います: | LEAP concept | AdCP equivalent | | ------------------------ | ----------------------------------------------------- | | UpcomingEvent | Episode(`scheduled_at` + `status`) | | Content (AdCOM 1.0) | 番組メタデータ(`genre`、`content_rating`、`talent`) | | AdInventoryConfiguration | Episode `ad_inventory` + 製品プレースメント | | Event status | Episode `status`(`scheduled`、`tentative`、`cancelled`) | | Unplanned breaks | `ad_inventory.unplanned_breaks` | | Flexible end time | Episode `flexible_end` | LEAP は SSP-to-DSP 配管をターゲットします。AdCP は購入決定が起こるエージェント間交渉層で動作します。 ## コレクションターゲティング 複数のコレクションを持つ製品はデフォルトでバンドルします — バイヤーはすべてのリストされたコレクションを得ます。セラーは、バイヤーがサブセットをターゲットできるよう `collection_targeting_allowed: true` を設定できます、プロパティの `property_targeting_allowed` と同じパターン。 | `collection_targeting_allowed` | Meaning | | ------------------------------ | --------------------------- | | `false`(デフォルト) | バンドル — バイヤーがすべてのコレクションを得る | | `true` | バイヤーがメディアバイで特定のコレクションを選択できる | ## マルチコレクションバンドル 単一の製品は、`collections` に複数のコレクション ID をリストすることで複数のコレクションにまたがれます。製品が複数のコレクションを持つとき、バイヤーエージェントが各インストールメントがどのコレクションに属するか知るよう、各インストールメントは `collection_id` を含まなければなりません(MUST)。 ```json theme={null} { "products": [ { "product_id": "technet_april_bundle", "name": "TechNet Podcast Bundle - April", "collections": [{ "publisher_domain": "technet.example.com", "collection_ids": ["tech_weekly", "startup_hour"] }], "installments": [ { "installment_id": "tw_ep12", "collection_id": "tech_weekly", "scheduled_at": "2026-04-07T10:00:00Z", "status": "scheduled" }, { "installment_id": "tw_ep13", "collection_id": "tech_weekly", "scheduled_at": "2026-04-14T10:00:00Z", "status": "tentative" }, { "installment_id": "sh_ep30", "collection_id": "startup_hour", "scheduled_at": "2026-04-09T14:00:00Z", "status": "scheduled" }, { "installment_id": "sh_ep31", "collection_id": "startup_hour", "scheduled_at": "2026-04-16T14:00:00Z", "status": "tentative" } ], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "bundle", "pricing_model": "flat_rate", "fixed_price": 25000, "currency": "USD" } ] } ] } ``` ### 号付きプリント出版物 今後の号全体でディスプレイ広告を販売する雑誌パブリッシャー。各号はブッキング、キャンセル、素材配信の締切を持つインストールメントです: ```json theme={null} { "products": [ { "product_id": "vogue_de_display_q2", "name": "Vogue Germany — Display Ads, Q2 2026", "channels": ["print"], "collections": [{ "publisher_domain": "vogue.de", "collection_ids": ["vogue_de"] }], "installments": [ { "installment_id": "2026-04", "name": "April 2026", "season": "2026", "installment_number": "4", "scheduled_at": "2026-04-01T00:00:00+02:00", "status": "scheduled", "deadlines": { "booking_deadline": "2026-02-15T17:00:00+01:00", "cancellation_deadline": "2026-02-22T17:00:00+01:00", "material_deadlines": [ { "stage": "draft", "due_at": "2026-03-01T17:00:00+01:00", "label": "Artwork for color proofing" }, { "stage": "final", "due_at": "2026-03-08T17:00:00+01:00", "label": "Press-ready PDF/X-4, CMYK, 300 DPI" } ] } }, { "installment_id": "2026-05", "name": "Mai 2026", "season": "2026", "installment_number": "5", "scheduled_at": "2026-05-01T00:00:00+02:00", "status": "scheduled", "deadlines": { "booking_deadline": "2026-03-15T17:00:00+01:00", "cancellation_deadline": "2026-03-22T17:00:00+01:00", "material_deadlines": [ { "stage": "draft", "due_at": "2026-03-29T17:00:00+01:00", "label": "Artwork for color proofing" }, { "stage": "final", "due_at": "2026-04-05T17:00:00+02:00", "label": "Press-ready PDF/X-4, CMYK, 300 DPI" } ] } } ], "placements": [ { "kind": "seller_inline", "placement_id": "full_page", "name": "Full Page", "mode": "targetable" }, { "kind": "seller_inline", "placement_id": "dps", "name": "Double Page Spread", "mode": "targetable" }, { "kind": "seller_inline", "placement_id": "ifc", "name": "Inside Front Cover", "mode": "targetable" } ], "delivery_type": "guaranteed", "delivery_measurement": { "provider": "IVW", "notes": "Verified circulation, updated quarterly" }, "pricing_options": [ { "pricing_option_id": "full_page", "pricing_model": "flat_rate", "fixed_price": 28000, "currency": "EUR" }, { "pricing_option_id": "dps", "pricing_model": "flat_rate", "fixed_price": 48000, "currency": "EUR" } ] } ] } ``` コレクション/インストールメントモデルはプリント出版物とオーディオ/ビデオコンテンツで同一に機能します。インストールメントの `deadlines` オブジェクトは、ドイツの OBS システムが別々のメッセージ交換を通じて処理するものを置き換えます — ブッキング、キャンセル、素材配信がすべて事前に可視です。 ## 関連項目 * [メディア製品](/docs/media-buy/product-discovery/media-products) — 完全な製品モデル * [プリント広告](/docs/creative/channels/print) — プリント固有のクリエイティブフォーマット、物理寸法、ブリード、DPI * [brand.json](/docs/brand-protocol/brand-json) — タレントアイデンティティとブランドセーフティ評価 # キャンペーンブリーフ例 Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/product-discovery/example-briefs 注釈付きの例を通じて、AdCP における自然言語ブリーフの書き方を示します。ターゲット顧客とキャンペーン目標を共有し、パブリッシャーが適切なメディアプロダクトを提案しやすくします。 ## 1. 最小限のブリーフ: 必須要素 ### ローカルサービス事業 ```json theme={null} { "brief": "Mike's Plumbing Services needs to reach homeowners in the Denver, Colorado area who might need plumbing services. We have $8,000 USD to spend from October 15-31, 2024. Looking for display and native formats to drive phone calls." } ``` **このブリーフが機能する理由:** * ✅ **ビジネスが明確** - Mike's Plumbing Services * ✅ **顧客像が記述されている** - 配管工事を必要とする住宅所有者 * ✅ **地理的市場** - コロラド州デンバー(米国想定) * ✅ **フォーマットの希望** - ディスプレイとネイティブ * ✅ **予算と期間** - 8,000 ドルを 2 週間で消化 * ✅ **成果の指標** - 電話問い合わせ **パブリッシャーの解釈例:** ターゲティング提案としては次が考えられます。 * 住宅所有のシグナル * 住宅改善への関心 * ローカルサービスの検索意図 * 緊急サービスのニーズ *** ## 2. 標準的なブリーフ: 顧客ストーリー ### E コマース商品のローンチ ```json theme={null} { "brief": "TechGear Pro is launching premium wireless headphones in the United States. Our customers are typically young professionals who commute, work out regularly, and value high-quality audio for both music and calls. They're willing to pay more for products that last longer and perform better. We need video and display formats to drive online sales during our launch November 1-14, 2024. Budget is $25,000 USD with a target of acquiring customers at $45-55 each." } ``` **このブリーフが機能する理由:** * ✅ **顧客プロフィール** - アクティブなライフスタイルの若手プロフェッショナル * ✅ **顧客の価値観** - 品質、耐久性、パフォーマンス * ✅ **地理的市場** - 米国 * ✅ **必要フォーマット** - ビデオとディスプレイ * ✅ **明確な経済条件** - CAC 45〜55 ドル * ✅ **自然な説明** - パブリッシャーがターゲティングを提案しやすい **パブリッシャーからの提案例:** * 通勤者ターゲティング * フィットネス愛好家セグメント * プレミアムブランド嗜好 * オーディオ機器リサーチ層 * 職業・属性のオーバーレイ *** ## 3. 網羅的なブリーフ: B2B 顧客記述 ### エンタープライズソフトウェアのキャンペーン ```json theme={null} { "brief": "CloudSync Solutions helps companies manage data across multiple cloud platforms. Our ideal customers are growing businesses in the United States, Canada, United Kingdom, and Germany that have recently adopted cloud services and are struggling to keep data synchronized. These companies typically have distributed teams, use multiple SaaS tools, and are concerned about data security and compliance. The decision makers are usually technical leaders who report directly to the C-suite and are tasked with modernizing their company's infrastructure. We're looking for native content and display formats to generate qualified leads at $200-250 per lead. Q4 2024 campaign with $90,000 USD total budget." } ``` **このブリーフが機能する理由:** * ✅ **顧客の状況** - クラウドの課題を抱える成長企業 * ✅ **顧客のペイン** - データ同期、セキュリティ、コンプライアンス * ✅ **決裁者のプロフィール** - C-suite 直下の技術リーダー * ✅ **対象国** - 米国/カナダ/英国/ドイツを指定 * ✅ **フォーマットの希望** - ネイティブコンテンツとディスプレイ * ✅ **柔軟なターゲティング** - シグナル解釈をパブリッシャーに委ねる **パブリッシャーが読み取れる点:** * クラウド採用シグナル * 企業成長の指標 * テクノロジースタック分析 * 役職・シニオリティのマッチング * 業界別コンプライアンス要件 *** ## 4. 応用的なブリーフ: 複数オーディエンスのキャンペーン ### 自動車ローンチ ```json theme={null} { "brief": "EcoMotion is launching our new hybrid SUV in the United States, specifically California, Pacific Northwest, and Northeast regions. We have three distinct customer groups we want to reach with video, Connected TV, and display formats:\n\n1. Eco-conscious families who currently drive older SUVs and are concerned about their environmental impact but need the space for kids and activities. They research extensively and value safety ratings and environmental certifications.\n\n2. Tech-forward professionals who see their vehicle as an extension of their digital lifestyle. They're early adopters who want the latest features and are willing to pay premium prices for innovation.\n\n3. Current owners of competitor vehicles (Toyota Highlander, Honda Pilot) who might be in market for their next vehicle. They value reliability and total cost of ownership.\n\nCampaign runs October-December 2024 with $450,000 USD budget. Success means driving dealership visits and test drive appointments." } ``` **このブリーフが機能する理由:** * ✅ **3 つの明確な顧客ストーリー** - 動機がそれぞれ異なります * ✅ **地域フォーカス** - 米国内の特定地域を指定 * ✅ **フォーマット戦略** - ビデオ、CTV、ディスプレイ * ✅ **競合コンテキスト** - 指示的になりすぎず示唆しています * ✅ **カスタマージャーニーの示唆** - リサーチ行動や価値観 * ✅ **成功指標が明確** - ディーラー訪問・試乗予約 **パブリッシャーが提供できる価値:** * ファミリー向けコンテキストの提案 * アーリーアダプターのシグナル特定 * 競合奪取の機会探索 * 環境意識データの重ね合わせ * 自動車購買行動データの適用 *** ## 業界別の例 ### 金融サービス - 米国 ```json theme={null} { "brief": "NextGen Banking is promoting our high-yield savings account across the United States. Our target customers are professionals who have accumulated some savings but keep it in traditional banks earning minimal interest. They're financially responsible but not necessarily investment-savvy, and they value security and ease of use over complex features. Looking for display and native formats to acquire 5,000 new accounts in January 2025 with $400,000 USD budget." } ``` ### ヘルスケア - 米国内の特定地域 ```json theme={null} { "brief": "HealthFirst Urgent Care serves families in Ohio who need convenient, affordable healthcare. Our patients typically have insurance but want to avoid emergency room costs and wait times. They're parents with young children, working professionals who can't take time off for appointments, and seniors who need accessible care close to home. We need display and video formats to drive appointment bookings. $20,000 USD monthly budget." } ``` ### ストリーミングサービス - 北米 ```json theme={null} { "brief": "StreamPlus is expanding in the United States and Canada. Our subscribers love live sports but have cut the cord on traditional cable. They're social viewers who watch games with friends and family, follow multiple teams, and want access to both local and national broadcasts. We need Connected TV, video, and display formats for our Q4 2024 campaign with $2M USD budget to drive free trial sign-ups." } ``` ### モバイルゲーム - 英語圏グローバル ```json theme={null} { "brief": "GameStudio is launching our puzzle game in the United States, United Kingdom, Canada, and Australia. Our players are typically adults who play mobile games during commutes, breaks, and before bed. They've played games like Candy Crush or Wordle and enjoy mental challenges that don't require long time commitments. Looking for video and display formats to acquire 50,000 players at $3.50 each in January 2025 with $175,000 USD budget." } ``` ### 放送テレビ - 地域自動車 ```json theme={null} { "buying_mode": "brief", "brief": "Nova Motors is launching the Volta EV across the top 10 US DMAs. We need primetime :30 spots on major network affiliates (ABC, NBC, CBS, FOX) and late fringe :15 spots for frequency. Adults 25-54, $400K budget, 4-week flight in Q4 2026. We want to guarantee against C7 ratings with VideoAmp as the measurement vendor." } ``` このブリーフには放送特有の概念が含まれます: DMA(指定市場エリア)、スポットの尺(:30 と :15)、デイパート(プライムタイム、レイトフリンジ)、測定ウィンドウの希望(C7)。セラーはこれらを自社の局編成と利用可能なアベイルに照らして解釈します。 *** ## ブリーフ作成のベストプラクティス ### 顧客を自然な文章で描写します ターゲティング手法を細かく指定する代わりに、顧客の以下を記述します。 * **状況**: 生活/ビジネスで何が起きているか * **課題**: どんな問題に直面しているか * **価値観**: 何を重視しているか * **行動**: どうリサーチし、どう購買するか * **文脈**: いつ・なぜそのブランドが必要か ### 地理的な範囲を必ず入れる * 国を明示します * 通貨を記載する (USD, EUR, GBP など) * 国の中での地域フォーカスを記す * グローバルキャンペーンの場合はタイムゾーンも考慮します ### フォーマットの希望を示します チャンネル戦略を伝えるためにフォーマット種別を含めます。 * **Display**: 標準的なウェブ広告 * **Video**: インストリーム/アウトストリーム動画 * **Connected TV**: ストリーミングテレビ広告 * **Audio**: ポッドキャスト・音楽ストリーミング * **Native**: コンテンツ型広告 ### パブリッシャーの知見を活かす 良いブリーフはパブリッシャーの専門性を活かす余地を残します。 * ターゲティングパラメータではなく顧客を描写します * デモグラだけでなく文脈を共有します * キャンペーンの「理由」を説明します * 創造的なターゲティング提案を受け入れる *** ## AdCP のブリーフで重視する点 ### ✅ 入れるべき内容 * 顧客の記述やストーリー * 対象市場と通貨 * フォーマットの希望(チャネルを示唆) * ビジネス目標と KPI * 予算と期間 * 競合コンテキスト ### ❌ 指定しすぎないこと * 細かなターゲティングパラメータ * 固定のオーディエンスセグメント * 技術的な実装方法 * フリークエンシーキャップや入札戦略 * アトリビューション手法 ### 🤝 パブリッシャーに任せること * ターゲティング手法の提案 * オーディエンス戦略の推奨 * 保有データに基づく最適化 * プラットフォーム専門知識の適用 * 効果検証と学習 *** ## ブリーフ評価チェックリスト ### 必須項目 * [ ] 広告主/ブランドが明確 * [ ] 地理的な市場を指定 * [ ] 通貨を明記 * [ ] フォーマットの希望を記載 * [ ] 予算と期間を記載 * [ ] ビジネス目標を定義 ### 顧客記述 * [ ] 顧客の状況・文脈 * [ ] 解決したい課題 * [ ] 意思決定の仕方 * [ ] 重視する価値 * [ ] 自然で会話的なトーン ### キャンペーン文脈 * [ ] なぜ今このキャンペーンか * [ ] 成功指標を定義 * [ ] 競合状況に触れているか * [ ] パブリッシャーの提案余地があるか *** ## 関連ドキュメント * [ブリーフの期待値](/docs/media-buy/product-discovery/brief-expectations) - パブリッシャーがブリーフをどう処理するか * [クリエイティブフォーマット](/docs/creative/formats) - フォーマット仕様とディスカバリーの理解 * [Media Buy ライフサイクル](/docs/media-buy/media-buys) - キャンペーン実行ワークフロー * [プロダクトディスカバリー](/docs/media-buy/product-discovery) - ブリーフがプロダクト選定に与える影響 # 概要 Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/product-discovery/index 自然言語のブリーフで広告在庫を発見し、キャンペーン計画のためにプロダクト構造を理解します。 プロダクトディスカバリーは AdCP のメディアバイの基盤です。キャンペーンの目標を自然言語で記述し、要件に合う関連する広告在庫を発見できます。 AdCP のプロダクトディスカバリーは、広告在庫の発見と評価の方法を刷新します。 * **自然言語優先**: 複雑なカタログをたどる代わりに平易な英語でキャンペーンを説明 * **AI マッチング**: 高度なアルゴリズムがブリーフを関連在庫にマッチング * **フォーマット認識**: プロダクトにクリエイティブフォーマット互換性が含まれます * **アカウント別結果**: アカウントのアクセス権や交渉済みディールに基づく在庫を表示 ## ディスカバリープロセス ### 1. ブリーフを作成 キャンペーンの目的を自然言語で説明するところから始めます。 *「Mike's Plumbing Services は、コロラド州デンバー周辺の住宅所有者で配管サービスを必要としそうな人々にリーチしたい。2024 年 10 月 15 日から 31 日までに 8,000 USD を使う予定。電話問い合わせを増やすため、ディスプレイとネイティブフォーマットを希望。」* ### 2. プロダクトを発見する [`get_products`](/docs/media-buy/task-reference/get_products) を使い、ブリーフとプロモートするオファリングに基づいてマッチする在庫を見つけます。 ### 3. 結果を評価します 返却されたプロダクトを以下の観点で確認します。 * ターゲット顧客との **オーディエンス整合性** * クリエイティブアセットとの **フォーマット互換性** * **料金モデル**(固定 CPM かオークション型か) * **配信タイプ**(保証配信か非保証配信か) ### 4. 具体化と反復 ブリーフを調整したり構造化されたフィルターを追加して、最適な在庫を見つけます。 ### 5. プロポーザルを活用 パブリッシャーはプロダクトと併せて **プロポーザル**(予算配分を含む構造化メディアプラン)を返す場合があります。プロポーザルは「モバイル重視にして」など会話で調整し、そのまま実行できます。[Proposals](/docs/media-buy/product-discovery/media-products#proposals) を参照してください。 ## 主要な概念 ### 自然言語ブリーフ AdCP は次のような要件を求めず、会話的な英語でのキャンペーン説明を受け付けます。 * ❌ プロダクトカタログのナビゲーション * ❌ 技術的なターゲティング構文 * ❌ プラットフォーム固有の用語 代わりに、キャンペーンを自然に説明してください。 * ✅ 「エナジードリンクのローンチでプレミアムなスポーツファンにリーチ」 * ✅ 「夕方の帰宅通勤者を狙う地元レストラン」 * ✅ 「マーケティングマネージャー向けの B2B ソフトウェア」 詳細は [Brief Expectations](/docs/media-buy/product-discovery/brief-expectations) を参照してください。 ### プロダクトモデル プロダクトは、三つの独立した次元に沿って販売可能な在庫を記述します: * **パブリッシャープロパティ** — 広告が配信される**場所**(ウェブサイト、アプリ、プラットフォーム) * **コレクションとインストールメント** — 広告が配信される**コンテンツ**(シリーズ、ポッドキャスト、ライブイベント) * **プレースメント** — 広告が現れる**ポジション**(プリロール、ミッドロール、ホストリード) 各プロダクトはさらに、オーディエンスターゲティング、クリエイティブフォーマット要件、価格、配信特性を含みます。バイヤーは、セラーがそのリクエストに対して返すことを選んだプロダクトを見ます。ダイレクト限定、ホールセール、ストアフロントの可用性といったセールスエージェントのルーティング判断はセラーの内部に留まります。プロダクトがコレクションや登録済みのプレースメントを参照する場合、バイヤーはパブリッシャーの `adagents.json` から完全なメタデータを解決します。 完全なプロダクト構造は [Media Products](/docs/media-buy/product-discovery/media-products) で確認できます。ポッドキャスト、CTV シリーズ、ライブイベントのようなコレクション中心の在庫については、[コレクションとインストールメント](/docs/media-buy/product-discovery/collections-and-installments)を参照してください。 ### カタログ駆動のディスカバリー カタログ駆動のキャンペーン(リテールメディア、求人ボード、旅行)では、`get_products` に `catalog` を渡して、カタログアイテムに一致するプロダクトを見つけます。プロダクトは `catalog_types` を通じてサポートするカタログタイプを宣言し、レスポンスには一致したアイテム数を持つ `catalog_match` が含まれます。[カタログ](/docs/creative/catalogs#catalogs-in-the-media-buy-lifecycle)を参照してください。 ### プロパティガバナンスによるフィルタリング コンプライアンスでフィルタリングしたディスカバリーには、`get_products` に `property_list` を渡して、ガバナンス要件(COPPA 認証、サステナビリティスコア、ブランドセーフティ評価)を満たすプロパティに結果を制限します。プロパティリストは[プロパティガバナンスエージェント](/docs/governance/property/index)を通じて作成されます。詳細は [`get_products` — プロパティガバナンス](/docs/media-buy/task-reference/get_products#request-parameters)を参照してください。 ### フォーマットディスカバリーとの連携 プロダクトディスカバリーはクリエイティブ計画と密接に連携します。 1. **プロダクトが `format_ids`・`format_options`・またはその両方を返却** し、必要なクリエイティブ仕様を示します 2. **[`list_creative_formats`](/docs/creative/task-reference/list_creative_formats)** で従来の名前付きフォーマットの詳細を取得し、3.1 以降の正準的なフォーマットオプション宣言にはプロダクトの `format_options[]` を直接使用します 3. **発見したフォーマット要件に基づいて** クリエイティブ制作を計画します ## ブリーフの例とパターン キャンペーンタイプ別の効果的なブリーフ例: * **ローカルビジネス**: 提供エリア、顧客属性、ビジネス成果 * **E コマース**: 商品カテゴリ、購買行動、コンバージョン目標 * **B2B**: 役職、企業特性、リード獲得 * **ブランド認知**: ライフスタイル属性、メディア消費、リーチ目標 より詳しい例は [Example Briefs](/docs/media-buy/product-discovery/example-briefs) を参照してください。 ## ディスカバリーのベストプラクティス ### 効果的なブリーフ作成 * **ビジネスと訴求内容を具体的に** 記述します * デモグラコードではなく **理想的な顧客像を描写** します * **地理的な範囲** と位置の関連性を含めます * クリエイティブの制約があれば **フォーマットの希望** を明記します * **ビジネス目標**(電話、来店、売上、認知)を記載します ### 反復的なディスカバリー * まず広めのブリーフで利用可能な在庫を探索します * 配信タイプや価格で絞り込むため構造化フィルターを活用します * 異なる顧客記述を試し、新しい機会を探る * 成功したブリーフパターンを保存し、今後のキャンペーンに流用します ### 結果の扱い方 * 返却されたすべてのプロダクトを確認し、意外な機会を探す * クリエイティブ制作前にフォーマット要件を確認します * 保証配信と非保証配信の組み合わせを検討します * 予算計画のために価格ガイダンスを評価します ## レスポンスタイム プロダクトディスカバリーの処理時間: * **[`get_products`](/docs/media-buy/task-reference/get_products)**: 約 60 秒(AI 処理) * **[`list_creative_formats`](/docs/creative/task-reference/list_creative_formats)**: 約 1 秒(データベース参照) ## 次のステップ プロダクトを発見した後は、以下を進めてください。 1. **[Create Media Buy](/docs/media-buy/media-buys/)** - 選択したプロダクトからキャンペーンを構築します 2. **[Creative Planning](/docs/media-buy/creatives/)** - フォーマット要件に合うアセットを準備します 3. **[Task Reference](/docs/media-buy/task-reference/)** - 実装に向けた詳細 API ドキュメントを確認します ## 関連ドキュメント * **[Brief Expectations](/docs/media-buy/product-discovery/brief-expectations)** - ブリーフ構造の詳細ガイド * **[Example Briefs](/docs/media-buy/product-discovery/example-briefs)** - 実際のキャンペーンブリーフパターン * **[Media Products](/docs/media-buy/product-discovery/media-products)** - プロダクトモデルと属性の理解 * **[Proposals](/docs/media-buy/product-discovery/media-products#proposals)** - 会話で洗練できる構造化メディアプラン * **[`get_products` Task](/docs/media-buy/task-reference/get_products)** - 完全な API リファレンス # Media Products Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/product-discovery/media-products AdCP メディアプロダクト — プロトコルにおける中核の販売単位。プロダクト構造、価格オプション、デリバリー種別、フォーマット参照、カタログ駆動型在庫を網羅。 **プロダクト** は AdCP における中核の販売単位です。本ドキュメントではプロダクトモデル、価格/配信種別、発見方法や構造について説明します。 **価格モデル** プロダクトはサポートする価格モデルを宣言し、バイヤーはメディアバイ作成時に具体的な価格オプションを選択します。CPM、CPCV、CPP、CPC、CPA、vCPM、定額料金、時間ベース価格の詳細は [Pricing Models Guide](/docs/media-buy/advanced-topics/pricing-models) を参照。 ## プロダクトモデル * `product_id` (string, required) * `name` (string, required) * `description` (string, required) * `publisher_properties` (list\[PublisherPropertySelector], required): このプロダクトが対象とするパブリッシャープロパティ。[Property Targeting](#property-targeting) を参照。 * `channels` (list\[string], optional): このプロダクトが販売される広告チャネル(例: `["retail_media"]`、`["display", "olv"]`)。セラーは、リテールメディア・CTV/OLV・マルチチャネルバンドルなど、自明でないチャネルにまたがるプロダクトには `channels` を宣言すべきです。プロダクトのチャネルは、そのプロパティの `supported_channels` の和集合のサブセットであるべきです。[Media Channel Taxonomy](/docs/reference/media-channel-taxonomy) を参照。 * `video_placement_types` (list\[string], optional): このプロダクトに含まれうる、宣言済みの動画プレースメントタイプ。IAB Tech Lab/OpenRTB 2.6 の `video.plcmt` 定義を AdCP ネイティブ名で使用: `instream`、`accompanying_content`、`interstitial`、`standalone`。集約プロダクトやアドネットワークプロダクトは複数値を含みうる。これはセラー宣言のディスカバリーメタデータであり、在庫品質や配信コンテキストの独立検証ではありません。 * `audio_distribution_types` (list\[string], optional): このプロダクトに含まれうる、宣言済みのオーディオ配信タイプ。IAB Tech Lab/OpenRTB 2.6 の `audio.feed` 定義を AdCP ネイティブ名で使用: `music_streaming_service`、`fm_am_broadcast`、`podcast`、`catch_up_radio`、`web_radio`、`video_game`、`text_to_speech`。集約プロダクトやアドネットワークプロダクトは複数値を含みうる。これはセラー宣言のディスカバリーメタデータであり、在庫品質や配信コンテキストの独立検証ではありません。 * `sponsored_placement_types` (list\[string], optional): カタログ駆動のリテールメディアプロダクト向けの、宣言済みのスポンサープレースメントタイプ: `sponsored_search`、`sponsored_display`、`sponsored_native`。集約プロダクトやアドネットワークプロダクトは複数値を含みうる。これはセラー宣言のディスカバリーメタデータであり、在庫品質や配信コンテキストの独立検証ではありません。 * `social_placement_surfaces` (list\[string], optional): ソーシャルプロダクト向けの、宣言済みのソーシャルプレースメント面: `feed`、`stories`、`short_video`、`explore`、`search`。集約プロダクトやアドネットワークプロダクトは複数値を含みうる。これはセラー宣言のディスカバリーメタデータであり、在庫品質や配信コンテキストの独立検証ではありません。 * `format_ids` (list\[FormatID], conditional): レガシーの名前付きフォーマット参照。プロダクトは `format_ids`・`format_options`・またはその両方を含まなければなりません。[Creative Formats](/docs/creative/formats) を参照。 * `format_options` (list\[ProductFormatDeclaration], conditional): このプロダクトが受け付ける 3.1 以降の正準的なフォーマットオプション宣言。両方のフォーマットフィールドが存在する場合、バイヤーは `format_options` を優先します。プロダクトレベルのフォーマットは販売可能プロダクトの上限であり、プレースメントレベルのフォーマットはこの集合を狭められますが、プロダクトが受け付けないフォーマットを追加することはできません。バイヤーの `FormatOptionRef` セレクターは、パブリッシャー宣言のオプションには `{scope: "publisher", publisher_domain, format_option_id}` を、プロダクトローカルのオプションには `{scope: "product", format_option_id}` を使います。 * `placements` (list\[Placement], optional): プロダクト内の特定の公開広告プレースメント。各プレースメントは `kind`(`publisher_ref` または `seller_inline`)と `mode`(`targetable` または `included`)を宣言します。セラー非公開の配信オブジェクトはここに公開されません。[Placements](#placements) を参照。 * `shows` (list\[CollectionSelector], optional): このプロダクトが対象とする番組。パブリッシャーごとにグルーピングされ、各エントリは `publisher_domain` と、パブリッシャーの `adagents.json` で番組を参照する `collection_ids` を持ちます。[Collections and installments](/docs/media-buy/product-discovery/collections-and-installments) を参照。 * `episodes` (list\[Episode], optional): このプロダクトで利用可能な特定エピソード。[Collections and installments](/docs/media-buy/product-discovery/collections-and-installments) を参照。 * `delivery_type` (string, required): `"guaranteed"` または `"non_guaranteed"`。 * `exclusivity` (string, optional): このプロダクトが排他的アクセスを提供するかどうか。`"none"`(フィールド未指定時のデフォルト)— 複数の広告主が同時に購入可能。`"category"` — 業種カテゴリーごとに 1 広告主のみ。`"exclusive"` — 単独スポンサーシップ。特定の番組やプレースメントに紐付く guaranteed プロダクトで最も関連します。 * `pricing_options` (list\[PricingOption], required): このプロダクトで利用可能な価格モデルの配列。[Pricing Models](#pricing-models) を参照。 * `delivery_measurement` (object, optional): 広告デリバリーを計測する主体 — インプレッションカウントに使用する広告サーバーとビューアビリティベンダー。新規実装は `vendors` を構造化された `BrandRef` 配列で埋めます(例: `[{ "domain": "googleadmanager.com" }, { "domain": "integralads.com" }]`)。レガシーの `provider` 文字列は非推奨です。未指定の場合、バイヤーは自身の計測デフォルトを適用すべきです。[Delivery Measurement](#delivery-measurement) を参照。 * `outcome_measurement` (OutcomeMeasurement, **非推奨**): ビジネス成果計測(リフト、ブランドリフト、来店)を宣言するレガシーフィールド。新規実装は成果メトリクスを `reporting_capabilities.available_metrics` で宣言し、アトリビューション手法とウィンドウを `committed_metrics` の `qualifier` スロットでピン留めします。1 マイナーの後方互換のため保持され、次のメジャーで削除されます。移行パターンは [Commerce Media](/docs/media-buy/commerce-media) を参照。 * `creative_policy` (CreativePolicy, optional): クリエイティブ要件と制限。 * `is_custom` (bool, optional): 特定ブリーフに基づき生成された場合は `true`。 * `expires_at` (datetime, optional): `is_custom` の場合、プロダクトの有効期限。 * `property_targeting_allowed` (bool, optional, default: false): バイヤーが `get_products` のプロパティリストフィルタリングを使ってこのプロダクトを `publisher_properties` のサブセットに絞り込めるかどうか。`false`(デフォルト)の場合、プロダクトは「全か無か」— バイヤーはすべてのプロパティを受け入れなければならず、そうでなければ `property_list` フィルタリング結果からプロダクトが除外されます。[Property Targeting](#property-targeting) を参照。 * `collection_targeting_allowed` (bool, optional, default: false): バイヤーがこのプロダクトの `shows` のサブセットをターゲティングできるかどうか。`false`(デフォルト)の場合、プロダクトはバンドル — バイヤーはリストされたすべての番組を取得します。`true` の場合、バイヤーはメディアバイで特定の番組を選択できます。 * `data_provider_signals` (list\[DataProviderSignalSelector], **非推奨**): このプロダクトに既にバンドル/関連付けられたデータプロバイダーシグナルのレガシー/選択不可メタデータ。新規実装は `included_signals` を使うべきです。 * `included_signals` (list\[SignalListing], optional): このプロダクトに既に含まれる/バンドルされる/計画されたシグナルの選択不可メタデータ。これらはプロダクトが何であるかを説明するもので、バイヤーはパッケージの `signal_targeting_groups` では選択しません。 * `signal_targeting_allowed` (bool, optional, default: false): このプロダクトがパッケージレベルのシグナルターゲティング面を持つかどうか。編集可能性は `signal_targeting_rules` で制御されます。 * `signal_targeting_options` (list\[ProductSignalTargetingOption], optional): このプロダクト向けにバイヤーが選択、またはセラーが適用しうる、インラインのセラー提供シグナル。プロダクトローカルのシグナルオプション、またはセラーが適用を認可されたデータプロバイダーシグナルの場合があります。[Signal Targeting](#signal-targeting) を参照。 * `signal_targeting_rules` (SignalTargetingRules, optional): 選択可能なシグナルに対する、単一/複数選択の上限などの構成ルール。 * `catalog_types` (list\[string], optional): このプロダクトがカタログ駆動型キャンペーンでサポートするカタログタイプ。スポンサードプロダクトリスティングは `["product"]` を、求人ボードは `["job", "offering"]` を宣言します。バイヤーはこのフィールドを通じて同期済みカタログとプロダクトを照合します。[Catalogs](/docs/creative/catalogs) を参照。 * `catalog_match` (object, optional): バイヤーが `get_products` で `catalog` を提供する場合、このプロダクトで対象となるカタログアイテムを示します。`matched_gtins`(クロスリテーラー GTIN マッチ)、`matched_ids`(汎用アイテム ID マッチ)、`matched_count`、`submitted_count` を含みます。 * `metric_optimization` (object, optional): このプロダクトのメトリクス最適化機能。存在する場合、プロダクトが `kind: "metric"` の `optimization_goals` をサポートすることを示します。[Metric optimization](#metric-optimization) を参照。 * `max_optimization_goals` (integer, optional): パッケージでこのプロダクトが受け付ける `optimization_goals` の最大数。未指定の場合、上限は宣言されない。ほとんどのソーシャルプラットフォームは 1 つのみ受け付けます。 * `conversion_tracking` (object, optional): コンバージョンイベントトラッキング機能。存在する場合、プロダクトが `kind: "event"` の `optimization_goals` をサポートすることを示します。[Conversion tracking](#conversion-tracking-1) を参照。 * `product_card` (object, optional): UI でのプロダクト表示用ビジュアルカード定義。[Product Cards](#product-cards) を参照。 ### Metric optimization `kind: "metric"` の `optimization_goals` をサポートするプロダクトは、`metric_optimization` に機能を宣言します。メトリクスゴールにはイベントソースやコンバージョントラッキングの設定は不要 — セラーがこれらのメトリクスをネイティブに追跡します。 ```json theme={null} { "metric_optimization": { "supported_metrics": ["clicks", "views", "completed_views", "engagements"], "supported_view_durations": [2, 6, 15], "supported_targets": ["cost_per", "threshold_rate"] } } ``` | フィールド | 型 | 必須 | 説明 | | -------------------------- | --------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `supported_metrics` | string\[] | Yes | このプロダクトが最適化できるメトリクス種別。バイヤーはここに列挙されている種別のメトリクスゴールのみ要求すべきです。 | | `supported_view_durations` | number\[] | No | `completed_views` ゴールでサポートされる動画視聴時間の閾値(秒単位)。未指定の場合、セラーはプラットフォームのデフォルトを使用します。 | | `supported_targets` | string\[] | No | 利用可能なターゲット種別: `cost_per`、`threshold_rate`。値は最適化ゴールの `target.kind` と一致します。列挙された種別のみ受け付けます。省略した場合、バイヤーはターゲットなしのメトリクスゴール(ボリューム最大化)を設定できるが、特定ターゲットは設定できません。 | ### Conversion tracking `kind: "event"` の `optimization_goals` をサポートするプロダクトは、`conversion_tracking` に機能を宣言します。セラーレベルの機能(サポートされるイベントタイプ、UID タイプ、アトリビューションウィンドウ)は [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) で宣言されます。 ```json theme={null} { "conversion_tracking": { "action_sources": ["website", "app"], "supported_targets": ["cost_per", "per_ad_spend", "maximize_value"], "platform_managed": false } } ``` | フィールド | 型 | 必須 | 説明 | | ------------------- | --------- | -- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `action_sources` | string\[] | No | このプロダクトに関連するアクションソース(例: リテールメディアプロダクトは `in_store` と `website` を持つ場合があります)。 | | `supported_targets` | string\[] | No | イベントゴールで利用可能なターゲット種別: `cost_per`、`per_ad_spend`、`maximize_value`。値は最適化ゴールの `target.kind` と一致します。列挙された種別のみ受け付けます。省略した場合、バイヤーはターゲットなしのイベントゴールを設定できます。 | | `platform_managed` | boolean | No | セラーが常時計測を提供するかどうか(例: リテーラーの購買アトリビューション)。`true` の場合、`sync_event_sources` はセラー管理のイベントソースを返します。 | 完全な最適化ゴールのリファレンスは [Conversion Tracking & Optimization Goals](/docs/media-buy/conversion-tracking) を参照。 ### 価格モデル パブリッシャーは各プロダクトでサポートする価格モデルを宣言し、バイヤーはメディアバイ作成時に利用可能なオプションから選択します。このアプローチにより: * **1 プロダクトに複数価格モデル** - 同一在庫を異なる価格体系で提供可能 * **複数通貨対応** - パブリッシャーは `pricing_options` エントリごとにサポート通貨を宣言します。バイヤーはディスカバリー時に `filters.pricing_currencies` を使ってメディアプロダクトの取引通貨を絞り込むことができ、購入時にはサポートされた通貨を使用しなければなりません * **柔軟な価格設定** - CPM、CPCV、CPP(GRP ベース)、CPA などをサポート #### サポートされる価格モデル * **CPM** (Cost Per Mille) - 1,000 インプレッションあたりのコスト(従来型ディスプレイ) * **CPC** (Cost Per Click) - 広告クリックあたりのコスト * **CPCV** (Cost Per Completed View) - 動画/オーディオ 100% 再生完了あたりのコスト * **CPV** (Cost Per View) - パブリッシャー定義の閾値での視聴あたりのコスト * **CPA** (Cost Per Acquisition) - コンバージョンイベント(購買、リード、登録等)あたりのコスト * **CPP** (Cost Per Point) - GRP あたりのコスト(TV/オーディオ) * **Flat Rate** - 配信量に関わらず固定費 * **Time** - キャンペーン期間に応じてスケールする時間単位(日、週、月)あたりのコスト #### PricingOption 構造 各価格オプションの例: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/cpcv-option.json", "pricing_option_id": "cpcv_usd_guaranteed", "pricing_model": "cpcv", "fixed_price": 0.15, "currency": "USD", "min_spend_per_package": 5000 } ``` オークション型(`fixed_price` なし)の場合、`floor_price` を最低入札制約として、任意の `price_guidance` をパーセンタイルのヒントとして使用します。入札ベースのオークションモデル(`cpm`、`vcpm`、`cpc`、`cpcv`、`cpv`)では、`max_bid` をブール値シグナルとして含めることができ、`bid_price` が確定価格からバイヤー上限モードに切り替わることを示します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/cpm-option.json", "pricing_option_id": "cpm_usd_auction", "pricing_model": "cpm", "currency": "USD", "floor_price": 10.00, "max_bid": true, "price_guidance": { "p25": 12.50, "p50": 15.00, "p75": 18.00, "p90": 22.00 } } ``` #### Delivery Measurement プロダクトは利用可能な場合、計測プロバイダーを宣言すべきだ: ```json theme={null} { "delivery_measurement": { "provider": "Google Ad Manager with IAS viewability verification", "notes": "MRC-accredited viewability. 50% in-view for 1s display / 2s video." } } ``` 一般的なプロバイダーの例: * `"Google Ad Manager with IAS viewability"` * `"Nielsen DAR for P18-49 demographic measurement"` * `"Geopath DOOH traffic counts updated monthly"` * `"Comscore vCE for video completion tracking"` * `"Self-reported impressions from proprietary ad server"` guaranteed プロダクトは、購入レベルでアカウンタビリティ義務を定義する `performance_standards`、`measurement_terms`、`cancellation_policy` も宣言できます。[Accountability](/docs/media-buy/advanced-topics/accountability) を参照。 ### Outcome Measurement オブジェクト 成果計測を含むプロダクト(リテールメディアで一般的)の例: ```json theme={null} { "type": "incremental_sales_lift", "attribution": "deterministic_purchase", "window": { "interval": 30, "unit": "days" }, "reporting": "weekly_dashboard" } ``` ### CreativePolicy オブジェクト クリエイティブ要件や制限を定義します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/creative-policy.json", "co_branding": "required", "landing_page": "retailer_site_only", "templates_available": true } ``` ### Placements プロダクトは、在庫内の特定の公開広告プレースメントを任意で宣言できます。プレースメント ID はパブリッシャースコープです。プレースメントがパブリッシャーの `adagents.json` のプレースメント宣言に存在する場合、プロダクトのプレースメントは `kind: "publisher_ref"`、`publisher_domain`、`placement_id` でそのエントリを参照します。この場合、パブリッシャー宣言が名前やその他の公開メタデータを解決するため、プロダクトは `name` を省略してよい。公開のパブリッシャー宣言が存在しない場合、セラーエージェントは `kind: "seller_inline"` と、`name`・`description`・フォーマット・タグなどのバイヤー向けフィールドを持つインラインプレースメントを定義します。その `placement_id` は依然としてパブリッシャー名前空間で解釈され、`publisher_domain` が省略された場合はセラーエージェント自身のパブリッシャー名前空間で解釈されます。パブリッシャー参照がパブリッシャーホストの adagents.json から解決されたか、コミュニティ管理のフォールバックファイルから解決されたかは、リゾルバーのメタデータであり、別個のプレースメント種別ではありません。 * **`kind: "publisher_ref"`** - 指定パブリッシャーの `adagents.json` から解決される、パブリッシャースコープのプレースメント参照。`publisher_domain` が必要。 * **`kind: "seller_inline"`** - セラーエージェントがインラインで定義する、公開のバイヤー向けプレースメントメタデータ。`name` が必要。 * **`publisher_domain`** - 参照されるプレースメントを定義する `adagents.json` を持つドメイン。新しいマルチパブリッシャープロダクトは、プレースメント名前空間を明示するため含めるべきです(SHOULD)。 * **`placement_id`** - パブリッシャー名前空間におけるプレースメント ID。バイヤーは `creative_assignments[].placement_refs` で `publisher_domain` とともに参照します。レガシーの `placement_ids` 文字列は単一パブリッシャーの文脈でのみ一意です。 * **`mode: "targetable"`** - バイヤーはクリエイティブ割り当てやプロダクト内のプレースメント選択時に、このパブリッシャースコープのプレースメントを参照してよい。 * **`mode: "included"`** - 公開プレースメントはプロダクトの記述された構成の一部だが、バイヤーは `placement_id` で選り好みできない。 * **`video_placement_types`** - OLV その他の動画プレースメントの宣言済み動画プレースメントタイプ。具体的なプレースメントは通常1値を宣言し、集約プレースメントは複数を宣言しうる。 * **`audio_distribution_types`** - ラジオ、ストリーミングオーディオ、ポッドキャスト、ゲームその他のオーディオプレースメントの宣言済みオーディオ配信タイプ。 * **`sponsored_placement_types`** - カタログ駆動のリテールメディアプレースメントの宣言済みスポンサープレースメントタイプ。 * **`social_placement_surfaces`** - ソーシャルプレースメントの宣言済みソーシャルプレースメント面。 * **パブリッシャー参照ルール** - パブリッシャー参照のプロダクトプレースメントは、パブリッシャーの `adagents.json` の `{publisher_domain, placement_id}` に解決されます。 * **非公開在庫ルール** - セラー非公開の配信オブジェクト、アドサーバーマッピング、ソース/オリジンの詳細は `get_products` の外に置かなければなりません。 * **クリエイティブ割り当て** - 異なるクリエイティブを targetable なプレースメントに割り当て可能。 * **プレースメントターゲティングの省略** - `placement_refs` もレガシーの `placement_ids` も持たないクリエイティブは、パッケージ内のすべてのバイヤーターゲット可能なプレースメントで配信され、セラーは included-only の配信構成を引き続き制御します。 * **可能なら登録済み ID を使う** - パブリッシャーが `adagents.json` で正準的な `placements` を宣言している場合、プロダクトプレースメントはそのカタログ ID を `placement_id` として使うべきです(SHOULD)。 * **レジストリの意味論を保持** - プロダクトが登録済みプレースメントを参照する場合、それは同じプレースメントを指します。プロダクトは `format_ids` や `format_options` を狭めたり、運用上の詳細を追加したりできますが、プレースメントの意味を非互換に変えるべきではありません。 * **タグはプロダクトレベルでも有用** - プロダクトプレースメントはグルーピング用に `tags` を持てます。プレースメントがパブリッシャーレジストリ由来の場合はレジストリのタグと整合すべきです。 `mode` は 2026年5月25日時点で新規送信者に必須です。2026年11月25日に終了する6か月の移行期間中、バイヤーは `placements[]` エントリが `mode` を省略するレガシープロダクトを許容し、それらのプレースメントをクリエイティブ割り当て用に targetable として扱ってよい。2026年11月25日以降、バイヤーは `mode` の欠如に対して fail closed すべきです。 パブリッシャーは、`adagents.json` の `authorized_agents[].placement_ids` または `authorized_agents[].placement_tags` を使って、特定のプレースメントに対してエージェントを認可できます。セラーエージェントは、自身が販売を認可されたパブリッシャー参照のプレースメントのみを返すべきです。 #### 動画プレースメントタイプ 動画プロダクトは `video_placement_types` をプロダクトレベル、プレースメントレベル、またはその両方で宣言できます。語彙は IAB Tech Lab/OpenRTB 2.6 の `video.plcmt` 定義に従いますが、AdCP は OpenRTB のワイヤー名ではなく読みやすいフィールド名・値名を使います: | Value | Meaning | | ---------------------- | ------------------------------------------------------- | | `instream` | ユーザーが要求したストリーミング動画コンテンツの前・中・後に配信される動画広告 | | `accompanying_content` | ページ/アプリのコンテンツに関連する付随動画コンテンツを持つプレーヤー内の動画広告 | | `interstitial` | 通常コンテンツやアプリ状態の間に表示される、インタースティシャル体験としての動画広告 | | `standalone` | 関連する動画コンテンツなしで表示される動画広告(no-content / standalone 動画とも呼ぶ) | このフィールドは配列です。販売可能プロダクトが複数のプレースメントタイプを集約できるためです。例えば OLV ネットワークプロダクトは `instream` と `accompanying_content` の両方を含み、個々の targetable なプレースメントは1タイプに絞られる、といったことがあります。プロダクトとプレースメントの両宣言が存在する場合、プロダクトレベルの配列は、セラーがそのプロダクトの下で配信しうる動画プレースメントタイプの和集合であるべきです。 これはディスカバリーのシグナルであり、検証の主張ではありません。バイヤーは `get_products.filters.video_placement_types` で要求タイプを満たせるプロダクトをフィルタリングできますが、セラーは、計画/購入時に配信を instream 在庫へ制約できない限り、instream 専用フィルターに対して混在した非ターゲット可能なバンドルを返すべきではありません。 #### オーディオ配信タイプ オーディオプロダクトは `audio_distribution_types` をプロダクトレベル、プレースメントレベル、またはその両方で宣言できます。語彙は IAB Tech Lab/OpenRTB 2.6 の `audio.feed` 定義に従いますが、AdCP は OpenRTB の数値コードではなく読みやすいフィールド名・値名を使います: | Value | Meaning | | ------------------------- | --------------------------------------------- | | `music_streaming_service` | 音楽ストリーミングサービス | | `fm_am_broadcast` | FM/AM 放送(電波での生放送に加え、オンラインストリーミングでも利用可能なもの) | | `podcast` | シリーズのエピソードとして配信される、オリジナルの事前録音コンテンツ | | `catch_up_radio` | 元々は生放送されたラジオ番組の録音セグメント | | `web_radio` | オンラインストリーミングでのみ利用可能な生オーディオコンテンツ(FM/AM 放送ではない) | | `video_game` | ビデオゲーム内の背景オーディオ | | `text_to_speech` | オーディオブックやウェブ/プラグインの記事ナレーションなどの音声合成オーディオ | このフィールドは、バイヤー向けの `radio`・`streaming_audio`・`podcast`・`gaming` チャネル内の実行・配信の詳細を説明します。チャネル名を変えるものではなく、認可可能な在庫サーフェスに紐付く `adagents.json` の `property_type` の意味論を変えるものでもありません。フィールドが配列なのは、販売可能プロダクトが複数のオーディオ配信タイプを集約できるためです。両宣言が存在する場合、プロダクトレベルの配列は、セラーがそのプロダクトの下で配信しうるオーディオ配信タイプの和集合であるべきです。 これはディスカバリーのシグナルであり、検証の主張ではありません。バイヤーは `get_products.filters.audio_distribution_types` で要求タイプを満たせるプロダクトをフィルタリングできますが、セラーは、計画/購入時に配信を要求されたオーディオ配信タイプへ制約できない限り、混在した非ターゲット可能なバンドルを返すべきではありません。 #### スポンサープレースメントタイプ カタログ駆動のリテールメディアプロダクトは `sponsored_placement_types` をプロダクトレベル、プレースメントレベル、またはその両方で宣言でき、スポンサープレースメントがリテーラーサーフェスのどこにレンダリングされるかを区別します: | Value | Meaning | | ------------------- | ------------------------------------------------------------------------- | | `sponsored_search` | リテーラーのオンサイト検索結果に対して、買い物客のクエリに紐付いてレンダリングされるスポンサープレースメント | | `sponsored_display` | 検索結果ストリーム外の、ブラウズ・カテゴリ・商品詳細ページのディスプレイ枠にレンダリングされるスポンサープレースメント | | `sponsored_native` | リテーラーのネイティブな in-grid 商品テンプレートを使い、オーガニックなリスティングに溶け込んでレンダリングされるスポンサープレースメント | これらの値は、`source_catalog` スロットが必須のカタログ駆動 `sponsored_placement` 正準の下でのプロダクトのプレースメントサーフェスを説明します。オフサイトのプレースメントはカタログにキー付けされないため、オフサイト値は意図的に除外されています。フィールドが配列なのは、販売可能プロダクトが複数のサーフェスを集約できるためです。両宣言が存在する場合、プロダクトレベルの配列は、セラーがその下で配信しうる和集合であるべきです。 これはディスカバリーのシグナルであり、検証の主張ではありません。バイヤーは `get_products.filters.sponsored_placement_types` で要求タイプを満たせるプロダクトをフィルタリングできますが、セラーは、計画/購入時に配信を要求タイプへ制約できない限り、混在した非ターゲット可能なバンドルを返すべきではありません。 #### ソーシャルプレースメント面 ソーシャルプロダクトは `social_placement_surfaces` をプロダクトレベル、プレースメントレベル、またはその両方で宣言でき、ソーシャルプレースメントがレンダリングされるアプリ内サーフェスを区別します: | Value | Meaning | | ------------- | -------------------------------------------------------------- | | `feed` | 主要なスクロール型のホーム/タイムラインサーフェス | | `stories` | フルスクリーンで一時的な、タップ送りのストーリーサーフェス | | `short_video` | フルスクリーン縦型のアルゴリズム推薦ショート動画サーフェス(ブランド版 Reels、Shorts、Spotlight など) | | `explore` | フォローグラフ外のディスカバリー/ブラウズサーフェス | | `search` | ユーザーのクエリに紐付くアプリ内検索結果サーフェス | フィールドが配列なのは、販売可能プロダクトが複数のサーフェスを集約できるためです。両宣言が存在する場合、プロダクトレベルの配列は、セラーがその下で配信しうる和集合であるべきです。 これはディスカバリーのシグナルであり、検証の主張ではありません。バイヤーは `get_products.filters.social_placement_surfaces` で要求サーフェスを満たせるプロダクトをフィルタリングできますが、セラーは、計画/購入時に配信を要求サーフェスへ制約できない限り、混在した非ターゲット可能なバンドルを返すべきではありません。 #### プレースメントとのフォーマット優先順位 プロダクトレベルの `format_ids` と `format_options` は、プロダクト全体が受け付けるクリエイティブフォーマットを定義します。プレースメントレベルの `format_ids` または `format_options`(プロダクトプレースメントにインラインで返されるか、公開のパブリッシャープレースメント宣言から継承されるかを問わず)は、その特定のプレースメントについてプロダクト全体の集合を狭めるだけです。 プロダクトとプレースメントの両フォーマット宣言が存在する場合、バイヤーはそのプレースメントの実効的な受け入れフォーマットをそれらの積集合として計算します。レガシーの `format_ids` については、まず `canonical`、`v1_format_ref`、または正準マッピングレジストリを通じて名前付きフォーマットを正準宣言へ射影します。射影後に生の `(agent_url, id)` 値を比較してはいけません。レガシーの 300x250 ディスプレイ ID と、`width: 300`・`height: 250` を持つ正準的な `image` 宣言は互換です。3.1 以降の `format_options` については、パブリッシャー宣言のオプションを `{publisher_domain, format_option_id}` で照合し、`publisher_domain` が省略された場合はプロダクトローカルのオプションを `format_option_id` で照合し、それ以外は、プレースメントパラメータがプロダクト宣言を狭める同じ `format_kind` の宣言と照合します。 プロダクトのゲーティングは等価マッチングより厳格です。プロダクトまたはプレースメントが `width`、`height`、`duration_ms_exact`、または尺の範囲などの固定制約を宣言する場合、バイヤーが選択したフォーマットまたはクリエイティブマニフェストはそれらの制約を宣言し満たさなければなりません。広い要求(寸法のない `format_kind: "image"`、尺のない `video_hosted`)は、固定サイズや固定尺のプロダクトを満たしません。範囲については、満足とは包含を意味します: 範囲ベースの要求は、それが許すすべての値がプロダクトの受け入れ範囲内に収まる場合にのみプロダクトを満たします。重なりだけでは不十分です。正確な尺は、その正確な値が範囲内に収まる場合に範囲を満たします。逆は許されます: 別のプロダクト制約が除外しない限り、広いプロダクト宣言は特定の 300x250 画像や30秒動画を受け入れられます。[format matching vs product satisfaction](/docs/creative/canonical-formats#format-matching-vs-product-satisfaction) を参照。 プレースメントがフォーマット宣言を持たない場合、プロダクトレベルのフォーマットを継承します。プロダクトレベルの宣言に存在しないプレースメント専用のフォーマットは、プロダクトのクリエイティブ契約の拡張ではなくセラーの適合性エラーです。バイヤーはそのフォーマットに対して fail closed し、受け入れられたものとして扱うべきではありません。 #### Placement オブジェクト構造 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/placement.json", "kind": "publisher_ref", "publisher_domain": "daily-pulse.example", "placement_id": "homepage_banner", "name": "Homepage Banner", "description": "Above-the-fold banner on the homepage", "mode": "targetable", "tags": ["homepage", "display", "premium"], "format_ids": [ {"agent_url": "https://creative.adcontextprotocol.org", "id": "display_728x90"}, {"agent_url": "https://creative.adcontextprotocol.org", "id": "display_970x250"} ] } ``` #### 例: プレースメント付きプロダクト ```json theme={null} { "product_id": "news_site_premium", "name": "News Site Premium Package", "description": "Premium placements across news site", "format_ids": [ {"agent_url": "https://creative.adcontextprotocol.org", "id": "display_728x90"}, {"agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250"} ], "placements": [ { "kind": "publisher_ref", "placement_id": "homepage_banner", "publisher_domain": "daily-pulse.example", "name": "Homepage Banner", "mode": "targetable", "tags": ["homepage", "display", "premium"], "format_ids": [{"agent_url": "https://creative.adcontextprotocol.org", "id": "display_728x90"}] }, { "kind": "publisher_ref", "placement_id": "article_sidebar", "publisher_domain": "daily-pulse.example", "name": "Article Sidebar", "mode": "targetable", "tags": ["article", "display"], "format_ids": [{"agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250"}] }, { "kind": "seller_inline", "placement_id": "premium_rotation", "name": "Premium rotation", "mode": "included", "tags": ["premium"] } ], "delivery_type": "guaranteed", "pricing_options": [...] } ``` メディアバイ作成時に、バイヤーは異なるクリエイティブを別プレースメントに割り当てられます。 ```json theme={null} { "packages": [ { "product_id": "news_site_premium", "creative_assignments": [ { "creative_id": "creative_1", "placement_refs": [ { "publisher_domain": "daily-pulse.example", "placement_id": "homepage_banner" } ] }, { "creative_id": "creative_2", "placement_refs": [ { "publisher_domain": "daily-pulse.example", "placement_id": "article_sidebar" } ] } ] } ] } ``` 詳細は [Creative Assignment and Placement Targeting](/docs/media-buy/media-buys/index#creative-assignment-and-placement-targeting) を参照。 ### コレクションとインストールメント 番組はフォーマットやプレースメントと並ぶ、プロダクトの第三の次元です。プレースメントが広告の「どこに」表示されるかを、フォーマットが「どのように見えるか」を表すのに対し、番組は「コンテンツコンテキスト」— 視聴者が見ているプログラムを表します。プロダクトに `shows` と `episodes` を宣言することで、バイヤーは在庫購入時に特定の番組やエピソードをターゲティングできます。 完全なモデル、例、ターゲティングの詳細は [Collections and installments](/docs/media-buy/product-discovery/collections-and-installments) を参照。 ### Exclusivity `exclusivity` フィールドは、プロダクトがその在庫への排他的アクセスを提供するかどうかを示します。未指定の場合はデフォルトで `"none"`。 | 値 | 意味 | | ----------- | -------------------------------------------------- | | `none` | 複数の広告主がこのプロダクトを同時に購入できる | | `category` | 業種カテゴリーごとに 1 広告主のみ(例: 1 コレクションスポンサーシップに 1 自動車ブランド) | | `exclusive` | 単独スポンサーシップ — このプロダクトを購入できる広告主は 1 社のみ | Exclusivity は、広告主がブランド分離やコンテンツアソシエーションの独占権を求める、特定の番組やプレースメントに紐付く guaranteed プロダクトで最も関連します。 #### 各レベルの使用場面 * **`none`**: プログラマティック在庫、ランオブネットワーク、オープンオークションプロダクト。複数の広告主が同じ在庫を共有するのが前提。 * **`category`**: 競合分離が重要なポッドキャストや CTV スポンサーシップ。1 コレクションに 1 自動車ブランド、1 インストールメントに 1 フィンテックブランド — ただし競合しない複数の広告主が同時に購入可能。 * **`exclusive`**: 単一のコレクションまたはイベントの単独スポンサーシップ。広告主はそのコンテンツに関連付けられる唯一のブランドとなります。 パブリッシャーは `shows` を持つ guaranteed プロダクトには `exclusivity` を含めるべきです。`"none"` の暗黙のデフォルトはコレクションレベルの在庫には曖昧 — バイヤーはパブリッシャーが共有在庫を意図しているのか、単にフィールドを省略したのかを判断できません。 #### コンテンツスポンサーシップパターン `delivery_type: "guaranteed"`、`exclusivity: "exclusive"`、`shows` を組み合わせたプロダクトはコンテンツスポンサーシップを表す — 広告主は特定コンテンツの唯一のスポンサーとなります。これはポッドキャストのタイトルスポンサーシップ、CTV コレクションスポンサーシップ、イベントベースのテイクオーバーの標準パターンです。 ```json theme={null} { "product_id": "signal_noise_sponsor", "name": "Signal & Noise — Exclusive Sponsorship", "description": "Sole sponsorship of Signal & Noise, a weekly technology podcast. Includes pre-roll and mid-roll placements across all episodes.", "publisher_properties": [ { "publisher_domain": "crestnetwork.example", "property_ids": ["crest_podcasts"] } ], "format_ids": [ { "agent_url": "https://ads.crestnetwork.example", "id": "audio_pre_roll_30s" }, { "agent_url": "https://ads.crestnetwork.example", "id": "audio_mid_roll_60s" } ], "collections": [{ "publisher_domain": "crestnetwork.example", "collection_ids": ["signal_noise"] }], "delivery_type": "guaranteed", "exclusivity": "exclusive", "pricing_options": [ { "pricing_option_id": "flat_monthly", "pricing_model": "flat_rate", "fixed_price": 25000, "currency": "USD" } ] } ``` カテゴリー排他性は、パブリッシャーがネットワーク全体で競合ブランドを分離しながら、競合しない複数の広告主には販売するマルチコレクションバンドルで機能する: ```json theme={null} { "product_id": "crest_business_bundle", "name": "Crest Business Podcast Bundle — Category Sponsorship", "description": "Sponsorship across three business podcasts. One advertiser per industry category across all shows.", "publisher_properties": [ { "publisher_domain": "crestnetwork.example", "property_ids": ["crest_podcasts"] } ], "format_ids": [ { "agent_url": "https://ads.crestnetwork.example", "id": "audio_pre_roll_30s" }, { "agent_url": "https://ads.crestnetwork.example", "id": "audio_mid_roll_60s" } ], "collections": [{ "publisher_domain": "crestnetwork.example", "collection_ids": ["signal_noise", "market_beat", "founder_stories"] }], "delivery_type": "guaranteed", "exclusivity": "category", "pricing_options": [ { "pricing_option_id": "flat_quarterly", "pricing_model": "flat_rate", "fixed_price": 60000, "currency": "USD" } ] } ``` ### Property Targeting `property_targeting_allowed` フラグは、バイヤーが `get_products` のプロパティリストフィルタリングを使ってプロダクトをその `publisher_properties` のサブセットに絞り込めるかどうかを示します。 #### 動作 * **`property_targeting_allowed: false`(デフォルト)**: プロダクトは「全か無か」。バイヤーの `property_list` にプロダクトのプロパティがすべて含まれていない場合、そのプロダクトは結果から完全に除外されます。 * **`property_targeting_allowed: true`**: バイヤーは `property_list` に一致するプロパティにプロダクトを絞り込める。プロパティとバイヤーのリストに何らかの積集合がある場合、プロダクトは結果に含まれます。 #### ユースケース | ユースケース | `property_targeting_allowed` | 理由 | | ---------- | ---------------------------- | ---------------------------- | | ランオブネットワーク | `false` | バイヤーはネットワーク全体を受け入れなければなりません | | プレミアムバンドル | `false` | スポーツ + ニュースバンドルはセットで販売 | | フレキシブル在庫 | `true` | バイヤーはカテゴリー内の特定サイトをターゲティングできる | #### 例 **全か無かプロダクト**(`property_targeting_allowed: false`): ```json theme={null} { "product_id": "premium_news_bundle", "name": "Premium News Bundle", "publisher_properties": [ { "publisher_domain": "news.example.com", "property_ids": ["site_a", "site_b", "site_c"] } ], "property_targeting_allowed": false } ``` バイヤーが `site_a` と `site_b` のみを含む `property_list` で `get_products` を呼び出すと、バイヤーのリストにすべてのプロパティが含まれていない(`site_c` が欠落)ため、このプロダクトは**除外される**。 **フレキシブルプロダクト**(`property_targeting_allowed: true`): ```json theme={null} { "product_id": "news_category_flexible", "name": "News Category - Flexible Targeting", "publisher_properties": [ { "publisher_domain": "news.example.com", "property_ids": ["tech", "sports", "finance", "politics"] } ], "property_targeting_allowed": true } ``` バイヤーが `tech` と `sports` のみを含む `property_list` で `get_products` を呼び出すと、積集合があるためこのプロダクトは**含まれる**。バイヤーはその後このプロダクトを購入し、パッケージの `targeting_overlay.property_list` を通じて一致するプロパティのみをターゲティングできます。 ### Signal Targeting プロダクトは、異なる意味を持つ三つのシグナルフィールドを使います: * **`data_provider_signals`** は、プロダクトに既にバンドル/関連付けられたデータプロバイダーシグナルの非推奨のレガシー/選択不可メタデータです。新規実装は `included_signals` を使うべきです。 * **`included_signals`** は構造化された選択不可の面です。シグナルが既にプロダクトにバンドル/含有/計画されている場合に使います。プロダクトごとのターゲティング価格もセラーのアクティベーションハンドルも持ちません。 * **`signal_targeting_options`** はインラインの選択可能/構成可能な面です。セラー提供のシグナルがパッケージに現れうるもので、プロダクトが商品固有のメニュー、価格、アクティベーションハンドル、デフォルト/固定の選択、グルーピングのヒント、または brief/refine で選択されたサブセットを公開する必要がある場合に使います。 `Product.signal_targeting_allowed` と `signal_targeting_rules` は、シグナル構成に関する正準的なプロダクト契約です。`get_signals` は正準的な広域シグナルディスカバリー面です。ホールセールプロダクトは、インラインの `signal_targeting_options` なしで `signal_targeting_allowed: true` を使い、選択可能なシグナルフィードは `get_signals` を呼ぶようバイヤーに伝えられます。brief/refine のレスポンスは、関連サブセットや商品固有のオーバーライドとしてインラインの `signal_targeting_options` を返せます。セラーは、それらのシグナルをこのプロダクトの外でも発見可能にしたい場合を除き、プロダクトローカルのオプションを `get_signals` で公開する必要はありません。セラーは、`signal_targeting_options` または `signal_targeting_rules` を返すときは常に `signal_targeting_allowed: true` を設定しなければならず(MUST)、バンドルされているがパッケージのシグナルグループとして表現されないシグナルには `signal_targeting_options` ではなく `included_signals` を使わなければなりません(MUST)。 シグナルは、特徴値に似た、名前付きのターゲット可能な次元です。シグナル定義は `value_type`(`binary`、`categorical`、または `numeric`)を宣言し、時とともにより豊かなメタデータを持ちうる。プロダクトのシグナルリスティングは、明示的な解決スコープを持つ `signal_ref` を使います: `{ "scope": "product", "signal_id": "..." }` はリスティングが定義するプロダクトローカルのシグナルを、`{ "scope": "data_provider", "data_provider_domain": "...", "signal_id": "..." }` はデータプロバイダーが公開する adagents.json の `signals[]` で定義されたシグナルを、`{ "scope": "signal_source", "signal_source_url": "...", "signal_id": "..." }` はソースネイティブなシグナルを指します。`signal_ref.scope` は来歴ではなく解決パスであり、権威ある拡充情報はセラー、シグナルソース、またはデータプロバイダーのシグナル定義に存在します。`scope: "product"` の場合、プロダクトが定義の境界であるため、プロダクトリスティングは `name` と `value_type` を含まなければなりません(MUST)。`scope: "data_provider"` または `scope: "signal_source"` の場合、`signal_ref` で十分であり、インラインの名前・説明・値型・範囲・手法は文脈的であって権威的ではありません。`get_signals` と `get_products` の両方で公開されるプロダクトローカルのシグナルについては、`signal_ref.signal_id` は同じシグナルに対するセラーの `get_signals.signals[].signal_ref.signal_id` と一致しなければなりません(MUST)。 シグナルターゲティングの構成は、セラー全体の `get_adcp_capabilities` ではなく各プロダクトに宣言されます。一つのセラーが、異なる include/exclude やグルーピング上限を持つ異なるアドサーバーやプラットフォームに支えられたプロダクトを販売しうるためです。バイヤーは、構成しようとしている特定のプロダクトからインラインの `signal_targeting_options` と `signal_targeting_rules` を読むべきです。 * **`signal_targeting_allowed: false`(デフォルト)**: シグナルはプロダクト条件にバンドルされ、パッケージレベルのシグナルグループとしては表現されません。バイヤーは提供されたままプロダクトを購入し、パッケージ上でシグナルグループを選択したりエコーで受け取ったりしません。 * **`signal_targeting_allowed: true`**: プロダクトはパッケージレベルのシグナルターゲティング面を持ちます。ホールセールディスカバリーでは、インラインの `signal_targeting_options` が省略される場合、バイヤーは `get_signals` を使って選択可能なシグナルフィードを発見します。brief/refine の結果では、セラーは関連サブセットや商品固有のオーバーライドとしてインラインの `signal_targeting_options` を含めてよい。 * **`signal_targeting_rules`**: 任意のストアフロント構成ルール。セラーが任意、必須、単一選択、相互排他、固定、セラー計画、またはグループ化されたシグナル選択を表現する必要がある場合に、`resolution_model`、`selection_mode`、`min_selected_signals`、`max_selected_signals`、`max_selected_per_group`、`selection_group_rules`、`max_signal_targeting_groups`、`max_signals_per_targeting_group` を使います。`resolution_model: "direct_targeting"` は、選択されたシグナルがパッケージ在庫にターゲティング述語として適用されることを意味します。`resolution_model: "seller_planned"` は、選択されたシグナルが計画入力であり、セラーが商品固有の在庫・タイミング・可用性・リーチ・ペーシングの制約に対して解決することを意味します。バイヤーはシグナル選択を下位の在庫やスケジュールの判断に分解しようとすべきではありません。`selection_mode: "required"` は、少なくとも `min_selected_signals`(省略時は 1)を意味します。すべての明示的なパッケージレベルのシグナル選択はグループ化された式の形状を使います: トップレベルの `operator: "all"` と、include グループには `operator: "any"`、除外グループには `operator: "none"` を使う子グループ。`selection_mode` が `fixed` の場合、バイヤーは `default_selected` シグナルを読み取り専用として表示します。セラーは、バイヤーが `targeting_overlay.signal_targeting_groups` を省略してもそれらのシグナルを適用します。`selection_group_rules` が存在する場合、各子グループは正確に一つの `selection_group` と一つのターゲティングモードのシグナルを含まなければならず(MUST)、バイヤーは各 `(selection_group, targeting_mode)` ペアにつき最大一つの子グループを送らなければなりません(MUST)。セラーは、重複・混在・折りたたまれた子グループを拒否しなければなりません(MUST)。 * **`activation_status: "requires_activation"`**: バイヤーがセラーの受け入れるアクティベーションキーを既に持っていない限り、`get_products` だけではシグナルを選択するのに不十分です。プロダクトオプションは `signal_agent_segment_id` を含まなければならず(MUST)、バイヤーは `activate_signal` を通じてシグナルをアクティベートし、セラーがパッケージで要求する場合はアクティベーションキーを含めます。 各シグナルオプションの `allowed_targeting_modes` は、購入時のどの子グループ演算子が有効かを制御します: `"include"` は `operator: "any"` に、`"exclude"` は `operator: "none"` に対応します。`selection_group` は、`max_selected_per_group` や `selection_group_rules` のような上限のための、プロダクト定義の構成可能性バケットです。パッケージの `signal_targeting_groups.groups[]` 内の特定の子グループへのポインタではありません。あるターゲティングモードについてオプションが一つの子グループ内で自由に OR 結合できる場合は同じ `selection_group` を使います。オプションが別個の AND 節として表現されなければならない場合——例えば、一つのアドサーバーに支えられ、同じ子式に折りたためないオーディエンスセグメントとキーバリューターゲティングの両面を公開するプロダクト——は異なる `selection_group` 値を使います。異なる `selection_group` 値だけでは記述的です。グループ境界が検証に影響する場合、セラーは `selection_group_rules` を公開すべきです。 ```json theme={null} { "product_id": "retail_video_premium", "name": "Retail Video Premium", "signal_targeting_allowed": true, "signal_targeting_rules": { "resolution_model": "direct_targeting", "selection_mode": "optional", "max_selected_signals": 2, "max_selected_per_group": 1, "selection_group_rules": [ { "selection_group": "retail_audience", "targeting_mode": "include", "selection_mode": "optional", "max_selected_signals": 1 } ], "max_signal_targeting_groups": 2, "max_signals_per_targeting_group": 3 }, "signal_targeting_options": [ { "signal_ref": { "scope": "data_provider", "data_provider_domain": "pinnacle-data.example", "signal_id": "auto_intenders" }, "selection_group": "retail_audience", "allowed_targeting_modes": ["include", "exclude"], "activation_status": "ready", "default_selected": false, "pricing_options": [ { "pricing_option_id": "signal_cpm_usd_250", "model": "cpm", "cpm": 2.50, "currency": "USD" } ] } ] } ``` プロダクトは、パッケージレベルのシグナルターゲティング面を開かずに、含有された選択不可のシグナルを開示することもできます: ```json theme={null} { "product_id": "broadcast_auto_intenders_weekly_reach", "name": "Broadcast Auto Intenders Weekly Reach", "signal_targeting_allowed": false, "included_signals": [ { "signal_ref": { "scope": "data_provider", "data_provider_domain": "pinnacle-data.example", "signal_id": "auto_intenders" } } ] } ``` ここでバイヤーは、参照先プロバイダーの adagents.json の `signals[]` を通じて Pinnacle Data のシグナル定義を検査・検証できますが、`create_media_buy` でそのシグナルを追加・削除することはできません。 大きな、またはアカウント固有のシグナルメニューを持つホールセールプロダクトは、すべてのオプションをインライン化する代わりに、バイヤーを `get_signals` へ誘導できます: ```json theme={null} { "product_id": "retail_display_open_exchange", "name": "Retail Display Open Exchange", "signal_targeting_allowed": true, "signal_targeting_rules": { "resolution_model": "direct_targeting", "selection_mode": "optional", "max_signal_targeting_groups": 2 } } ``` この形状では、`signal_targeting_allowed` が true でインラインの `signal_targeting_options` が返されないため、バイヤーは候補シグナルを発見するために `get_signals` を呼びます。支出をコミットする前に、バイヤーは対象プロダクトについて `get_products` を呼ぶか、候補の `signal_ref` エントリと意図する include/exclude モードで `filters.signal_targeting` を使って、プロダクトの適格性を確認すべきです(SHOULD)。その後、バイヤーは選択した `signal_ref` エントリを `packages[].targeting_overlay.signal_targeting_groups` で渡します。セラーは選択されたシグナルがそのプロダクトとアカウントで利用可能であることを検証しますが、ストアフロントは、サポートされないシグナルの組み合わせが `create_media_buy` ではなくプロダクトディスカバリーで失敗するように、まず `get_products` を使うべきです。 購入時、パッケージレベルの `signal_targeting_groups` は、選択された `signal_ref`、値の式、任意のセラー実行ハンドル(`signal_agent_segment_id`)、およびシグナルが独自の価格を持つ場合はシグナルの `pricing_option_id` を運びます。単純な include のみの選択は `operator: "any"` の一つの子グループとして表現されます。グループ化された包含/除外は、`(A OR B) AND NOT (C)` のように追加の `any` と `none` グループを使います。これは、メディアプロダクト自体を価格付けするパッケージの `pricing_option_id` とは別です。`signal_targeting_options[].pricing_options` のプロダクトスコープのシグナル価格は、そのプロダクトについて権威的です。より広い `get_signals` の価格は、プロダクトが価格を省略しない限り、デフォルトのディスカバリービューです。セラーは、バイヤーの編集なしに適用した固定/デフォルト選択を含め、適用されたすべてのパッケージシグナルグループを結果のパッケージ状態でエコーしなければなりません(MUST)。 プロバイダーが公開するシグナルについては、`signal_ref.data_provider_domain` が上流のデータプロバイダーを識別し、`signal_ref.signal_id` がそのプロバイダーの adagents.json の `signals[]` にある公開シグナル定義を識別します。来歴の検証が必要なバイヤーは、そのドメインの `adagents.json` を取得し、セラーがそのシグナルまたはそのタグについて `authorized_agents` に現れることを確認できます。プロダクトローカルのシグナルには `scope: "product"` と `signal_id` を使います。その ID は選択されたプロダクト/パッケージのコンテキスト内でのみ意味を持ちます。 バックエンドの実行種別はシグナルのアイデンティティの一部ではありません。GAM に支えられたセラーは、オーディエンスセグメント、キーバリュールール、その他のカスタムターゲティングプリミティブを通常の `signal_ref` オプションとして公開できます。それらのシグナルがプラットフォームで一つの OR 節に結合できる場合は同じ `selection_group` に入れます。別々の節を要する場合は別々の `selection_group` に入れ、ストアフロントが `(selection_group, targeting_mode)` ごとに一つの子グループを構成するように `selection_group_rules` を公開します。 線形放送オーディオのスケジュールのようなセラー計画の guaranteed プロダクトでは、選択されたオーディエンスは可搬でも、プランは可搬でない場合があります。その場合、共有オーディエンスが複数プロダクトに現れるなら `scope: "data_provider"` シグナルとして公開し、セラーがそのオーディエンスを時間ベースのアベイルやリーチ目標に対して解決するプロダクトでは `resolution_model: "seller_planned"` を設定し、オーディエンス選択がパッケージに必須の場合は `selection_mode: "required"` または `selection_group_rules[].selection_mode: "required"` を使います。選択されたシグナルが通常のターゲティング述語として適用される非保証プロダクトでは、デフォルトの `resolution_model: "direct_targeting"` を使い、ホールセールのシグナルメニューをインラインで重複させるべきでない場合はプロダクトを `get_signals` へ向けます。 `seller_planned` モデルは、継続的にオークションされるのではなく、事前にスケジュールされ時間制約のある可用性で制約されるあらゆる供給タイプに適用されます: ライブスポーツ、政治番組、テントポール放送は、線形放送オーディオと同じパターンに従います。 ### カスタム/アカウント固有のプロダクト サーバーは汎用カタログを提供しつつ、以下も返せる: * **Account-Specific Products**: 特定クライアント向けまたは交渉済みのプロダクト * **Custom Products**: `is_custom: true` と `expires_at` タイムスタンプを持つ動的生成プロダクト ## プロダクトの例 ### 標準 CTV プロダクト(複数の価格オプション) ```json theme={null} { "product_id": "connected_tv_prime", "name": "Connected TV - Prime Time", "description": "Premium CTV inventory 8PM-11PM", "publisher_properties": [ { "publisher_domain": "streaming.example.com", "selection_type": "all" } ], "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_15s" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s" } ], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "cpm_usd_guaranteed", "pricing_model": "cpm", "fixed_price": 45.00, "currency": "USD", "min_spend_per_package": 10000 }, { "pricing_option_id": "cpcv_usd_guaranteed", "pricing_model": "cpcv", "fixed_price": 0.18, "currency": "USD", "min_spend_per_package": 10000 }, { "pricing_option_id": "cpp_usd_p18-49", "pricing_model": "cpp", "fixed_price": 250.00, "currency": "USD", "parameters": { "demographic": "P18-49", "min_points": 50 }, "min_spend_per_package": 12500 } ], "delivery_measurement": { "provider": "Nielsen DAR for P18-49 demographic measurement", "notes": "Panel-based measurement for GRP delivery. Impressions measured via Comscore vCE." } } ``` ### オークション型ディスプレイプロダクト ```json theme={null} { "product_id": "custom_abc123", "name": "Custom - Gaming Enthusiasts", "description": "Custom audience package for gaming campaign", "publisher_properties": [ { "publisher_domain": "gaming.example.com", "selection_type": "all" } ], "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_728x90" } ], "delivery_type": "non_guaranteed", "pricing_options": [ { "pricing_option_id": "cpm_usd_auction", "pricing_model": "cpm", "currency": "USD", "floor_price": 5.00, "price_guidance": { "p50": 8.00, "p75": 12.00 } }, { "pricing_option_id": "cpc_usd_auction", "pricing_model": "cpc", "currency": "USD", "floor_price": 0.50, "price_guidance": { "p50": 1.20, "p75": 2.00 } } ], "delivery_measurement": { "provider": "Google Ad Manager with IAS viewability", "notes": "MRC-accredited viewability. 50% in-view for 1s display." }, "is_custom": true, "expires_at": "2025-02-15T00:00:00Z" } ``` ### 計測付きリテールメディアプロダクト ```json theme={null} { "product_id": "albertsons_pet_category_offsite", "name": "Pet Category Shoppers - Offsite Display & Video", "description": "Target Albertsons shoppers who have purchased pet products in the last 90 days. Reach them across premium display and video inventory.", "publisher_properties": [ { "publisher_domain": "groceryretail.example.com", "selection_type": "all" } ], "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_728x90" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_15s" } ], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "cpm_usd_guaranteed", "pricing_model": "cpm", "fixed_price": 13.50, "currency": "USD", "min_spend_per_package": 10000 } ], "delivery_measurement": { "provider": "Self-reported impressions from proprietary ad server", "notes": "Impressions counted per IAB guidelines. Viewability measured via IAS." }, "outcome_measurement": { "type": "incremental_sales_lift", "attribution": "deterministic_purchase", "window": { "interval": 30, "unit": "days" }, "reporting": "weekly_dashboard" }, "creative_policy": { "co_branding": "optional", "landing_page": "must_include_retailer", "templates_available": true } } ``` ## Product Cards プロダクトカードは、UI でプロダクトを視覚的に示すための定義です。パブリッシャーは、カードフォーマットと必要アセットを含むカード定義を任意で提供できます。 ### カードタイプ パブリッシャーは少なくとも Standard カードを、必要に応じて詳細カードも提供すべきです。 **Standard Card** (`product_card`): * プロダクトのグリッド/リスト表示向けコンパクトカード(300x400px) * Retina 向けに 2x 密度画像をサポート * プロダクトを素早く視覚的に把握 **Detailed Card** (`product_card_detailed`, 任意): * ヒーローカルーセルとテキスト説明を並べたレスポンシブレイアウト * 下部に Markdown 仕様セクション * メディアキットのような詳細ドキュメント ### 構造 ```json theme={null} { "product_id": "ctv_premium", "name": "Premium CTV Inventory", // ... other product fields ... "product_card": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "product_card_standard" }, "manifest": { "display_name": "Premium CTV - Living Room Audiences", "hero_image_url": "https://cdn.example.com/products/ctv_hero.jpg", "brief_highlight": "Perfect for reaching cord-cutters and premium streaming audiences" } }, "product_card_detailed": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "product_card_detailed" }, "manifest": { "display_name": "Premium CTV - Living Room Audiences", "description": "Reach high-income households with premium CTV inventory during peak viewing hours...", "carousel_images": [ "https://cdn.example.com/products/ctv_context1.jpg", "https://cdn.example.com/products/ctv_context2.jpg" ], "specifications_markdown": "# Technical Specifications\n\n..." } } } ``` ### カードの描画 カード表示には 2 つの方法があります。 1. **`preview_creative` を使用**: カードフォーマットとマニフェストを渡してレンダリング 2. **事前レンダリング**: パブリッシャーがカードを生成し、静的に配信 インフラに合わせて動的生成と静的ホスティングを選択できます。 ### 標準カードフォーマット AdCP リファレンスクリエイティブエージェントは次の 2 種の標準カードフォーマットを定義します: * **`product_card_standard`** (300x400px) - プロダクトブラウズ用コンパクトカード * **`product_card_detailed`** (レスポンシブ) - カルーセルと詳細仕様を含むリッチカード パブリッシャーはブランドに合わせたり、独自の特徴を強調したりするためにカスタムカードフォーマットを定義できます。 **Note**: 標準カードフォーマットの定義はプロトコル仕様ではなく [creative-agent repository](https://github.com/adcontextprotocol/creative-agent) で管理されています。 ### プロダクトカードを含めるべき場面 プロダクトカードは任意だが、次のケースで推奨されます: * 強いビジュアルアイデンティティを持つプロダクト(番組、イベント、媒体など) * プレミアムプロダクトで、見た目が価値向上につながる場合 * 複雑なプロダクトで、ビジュアルハイライトが理解を助ける場合 * 特定オーディエンスを狙う際、ビジュアルで訴求したい場合 メディアキットのような詳細ドキュメントを提供したい場合は detailed カードを使用してください。 ### クライアント描画ガイドライン UI でプロダクトを表示する際のフォールバック順: 1. **`product_card` がある** → `preview_creative` で描画、または事前レンダリング画像を表示 2. **どちらもない** → テキストのみ(プロダクト名 + 説明)を表示 3. **カード描画に失敗** → テキストのみ表示にフォールバック 利用可能なメタデータにかかわらず、一貫したユーザー体験を提供できます。 ## プロポーザル パブリッシャーはプロダクトと一緒に **プロポーザル**(予算配分付きの構造化メディアプラン)を返すことができます。バイヤーは定義された経路を通じて実行できます。コミット済みプロポーザルは直接実行され、ドラフトプロポーザルはまず finalize が必要です。 ### プロポーザルとは プロポーザルは、提案予算配分とともにプロダクトをグルーピングした推奨購入戦略です。従来の営業担当が行っていたようなメディアプランニングの知見をエンコードします。 主な特徴: * **実行可能**: 返されたプロポーザルはそのライフサイクルを通じて購入可能です。`proposal_status: "draft"` はまず finalize が必要なことを、`proposal_status: "committed"` は `expires_at` 前に `proposal_id` を指定して `create_media_buy` で実行することを意味します。 * **予算非依存**: 配分をパーセンテージで保持するため、任意の予算にスケール可能 * **フォーキャスト付き**: プロポーザルと配分にはデリバリーフォーキャストを含めることができ、バイヤーが購入前に期待されるパフォーマンスを評価するのに役立つ ### プロポーザル構造 ```json theme={null} { "proposal_id": "swiss_balanced_v1", "name": "Swiss Multi-Channel Plan", "description": "Balanced coverage across devices and language regions", "allocations": [ { "product_id": "ch_desktop_de", "allocation_percentage": 20, "pricing_option_id": "cpm_usd_fixed", "rationale": "Primary desktop audience in German Switzerland", "tags": ["desktop", "german"] }, { "product_id": "ch_desktop_fr", "allocation_percentage": 30, "tags": ["desktop", "french"] }, { "product_id": "ch_mobile_de", "allocation_percentage": 8, "tags": ["mobile", "german"] }, { "product_id": "ch_mobile_fr", "allocation_percentage": 12, "tags": ["mobile", "french"] }, { "product_id": "ch_inapp_de", "allocation_percentage": 12, "tags": ["in-app", "german"] }, { "product_id": "ch_inapp_fr", "allocation_percentage": 18, "tags": ["in-app", "french"] } ], "total_budget_guidance": { "min": 30000, "recommended": 50000, "currency": "USD" }, "brief_alignment": "Achieves 50/20/30 channel split (desktop/mobile/in-app) and 40/60 language split (German/French)", "forecast": { "points": [ { "budget": 50000, "metrics": { "impressions": { "low": 800000, "mid": 1200000, "high": 1500000 }, "reach": { "low": 400000, "mid": 600000, "high": 750000 }, "clicks": { "mid": 4800 } } } ], "method": "modeled", "currency": "USD", "valid_until": "2025-04-15T00:00:00Z" } } ``` `tags` フィールドで配分を次元別に集計できる: * **チャネル別**: desktop (50%) + mobile (20%) + in-app (30%) = 100% * **言語別**: German (40%) + French (60%) = 100% ### プロポーザルの反復 プロポーザルは `buying_mode: "refine"` と `refine` 配列を使って改善できます。プロポーザルを ID で参照すると、セラーは更新された配分、フォーキャスト、価格を含む更新版プロポーザルを返します: ``` // 初回ディスカバリー get_products({ buying_mode: "brief", brief: "Swiss campaign, $50k, 50% desktop/20% mobile/30% in-app, 40% German/60% French" }) // レスポンスにプロポーザル "swiss_balanced_v1" を含む // プロポーザルを改善 get_products({ buying_mode: "refine", refine: [ { scope: "product", product_id: "ch_desktop_de" }, { scope: "product", product_id: "ch_desktop_fr" }, { scope: "product", product_id: "ch_mobile_de" }, { scope: "product", product_id: "ch_mobile_fr" }, { scope: "product", product_id: "ch_inapp_de" }, { scope: "product", product_id: "ch_inapp_fr" }, { scope: "proposal", proposal_id: "swiss_balanced_v1", ask: "focus more on German speakers - try 60/40 instead of 40/60" } ] }) // セラーは改訂された配分を含む更新版プロポーザルを返す ``` 完全なワークフローと例は [`get_products` refinement](/docs/media-buy/task-reference/get_products#refinement) を参照。 ### プロポーザルの実行 コミット済みプロポーザルを実行するには、`create_media_buy` で `proposal_id` と `total_budget` を指定します: ```json theme={null} { "proposal_id": "swiss_balanced_v1", "total_budget": { "amount": 50000, "currency": "USD" }, "brand": { "domain": "acmecorp.com" }, "start_time": "2025-04-01T00:00:00Z", "end_time": "2025-04-30T23:59:59Z" } ``` パブリッシャーは配分パーセンテージをパッケージに変換する: * `ch_desktop_de`: 20% × \$50,000 = \$10,000 Finalize はセラーのコミットステップです: 価格、条件、可用性、およびあらゆるインベントリホールドを確定します。これはバイヤーの受諾ではありません。`create_media_buy(proposal_id)` が受諾/実行のステップです。セラーは、ドラフトのプロポーザルを実行しようとする試みを `PROPOSAL_NOT_COMMITTED` で拒否します。create をリトライする前に、`refine` モードで `action: "finalize"` を指定して `get_products` で finalize してください。 * `ch_desktop_fr`: 30% × \$50,000 = \$15,000 * など 複数ラインアイテムの複雑なキャンペーンを単一のプロポーザル実行に簡略化できます。 ### プロポーザルを返す場面 パブリッシャーは次の場合にプロポーザルを含める: * ブリーフに特定の配分戦略(チャネル配分、言語配分など)が求められます * キャンペーン目標に基づく戦略的ガイダンスを提供できます * 複数プロダクトを組み合わせた方が効果的 パブリッシャーは通常、`wholesale` モード(バイヤーがターゲティングと配分を自ら指示します)ではプロポーザルを省略します。また、ブリーフがマルチプロダクト戦略を示唆しない場合も同様です。 プロポーザルは任意 — 配分ガイダンスが不要ならプロダクトのみ返しても構わない。`refine` モードでは、バイヤーがプロポーザルエントリを含めなかった場合でも、セラーは改善されたプロダクトと並んでプロポーザルを返してもよい。プロポーザルはセラーの提案であり、配分とキャンペーン最適化は主にオーケストレーター(バイヤーサイドエージェント)の責任です。 ### デリバリーフォーキャスト パブリッシャーはプロポーザルと個別配分にデリバリーフォーキャストを添付し、バイヤーが予算をコミットする前に期待されるパフォーマンスを評価するのに役立てることができます。 各フォーキャストには 1 つ以上の ForecastPoint の `points` 配列が含まれます。スペンドカーブでは、各ポイントは予算レベルとメトリクス範囲(low/mid/high)をペアにします——予算の昇順に並べた複数ポイントは、配信がスペンドに応じてどのようにスケールするかを示します。可用性フォーキャストでは、ポイントは予算を省略し、要求されたターゲティングと日付に対する利用可能な総在庫を表します。 メトリクスキーは 2 つの語彙から来る: * **デリバリー/エンゲージメント**: `forecastable-metric` 列挙値(impressions、reach、clicks、spend、views、completed\_views、grps など) * **成果**: `event-type` 列挙値(`purchase`、`lead`、`app_install`、`add_to_cart`、有料サブスクリプションには `subscribe`、無料の継続的オプトインには `follow` など) これにより、セラーはデリバリー(「120 万インプレッション」)と成果(「1,800 件の購買」)の両方を 1 つのフォーキャストで予測できます。各フォーキャストはその手法を宣言する: * **`estimate`** — 過去の平均やヒューリスティクスに基づく概算 * **`modeled`** — 予測モデルや過去データから導出 * **`guaranteed`** — 予約済み在庫に裏付けられた契約上のコミット配信水準 各メトリクス値は ForecastRange オブジェクトです。点推定には `mid` を、範囲には `low` と `high` を、またはその三つすべてを提供します。最低限、`mid` か、`low` と `high` の両方のいずれかが存在しなければなりません。 フォーキャストポイントは、セラーが国別、リージョン別、プレースメント別、デバイス別、オーディエンス別、シグナル値別、またはプレースメント×国やプロダクト×シグナルのような交差ごとの可用性を公開する必要がある場合、`dimensions` を運べます。`dimensions` は配列です。各項目は一つの `kind`(`geo`、`placement`、`device_type`、`device_platform`、`audience`、`signal`)を宣言し、ターゲティングや配信レポートと同じ正準的な識別子——地理には `geo_level`/`geo_code`、プレースメントには `placement_ref`、シグナルバケットには `signal_ref` に加えて `signal_value`/`presence`——を使います。メトロの行は `metro-system`(`nielsen_dma`、`uk_itl1`、`uk_itl2`、`eurostat_nuts2`、`custom`)の `system` 値を使い、ネイティブな郵便の行は `country` に加えて `postal-system` の国ローカルな `system` 値(例: `US` / `zip`、`GB` / `outward`、`ZA` / `postal_code`)を使います。一方で `us_zip` のような非推奨の国融合型の郵便システムは互換性のため引き続き受け付けられます。国とリージョンの行は `system` を省略します。一つのポイントに複数の次元項目が現れる場合、そのポイントはそれらの制約の交差を表します。次元の順序に意味はありません。バイヤーは `(forecast_range_unit, budget があれば, product_id があれば, kind でソートした dimensions)` から行の同一性を正規化します。セラーは一つのポイントで同じ `kind` を繰り返してはなりません(MUST NOT)。複数の geo・placement・audience・signal のスライスが必要な場合は、代わりに複数のポイントを出すべきです。これにより、セラーがすべての国・プレースメント・シグナル値ごとに別個のプロダクトを作ることを強いられず、次元別の可用性を一つのプロダクトまたはプロポーザル内に保てます。次元の行は `pricing_options` から独立しています。プロダクトの価格オプションは、依然としてプロダクトがどう購入されるかを説明します。 フォーキャストポイントは、標準のデリバリーメトリクスと並んで計測を意識したフォーキャストを含めることができます: * **`viewability`** は `get_media_buy_delivery` の `viewability` ブロックを反映しますが、数値は ForecastRange オブジェクトを使います。セラーは、プロダクトが対応する配信ビューアビリティを報告できる場合にのみフォーキャストビューアビリティを出すべきです(SHOULD)。MRC と GroupM の行は互換ではないため、フォーキャストビューアビリティ値が存在する場合は常にフォーキャストの `standard` が必須です。配信の `viewability.standard` は 3.x 互換のため任意のままですが、埋めるべきです(SHOULD)。 * **`vendor_metric_values`** はベンダー定義メトリクスの配信レポートを反映しますが、`value` と `measurable_impressions` は ForecastRange オブジェクトを使います。セラーは、プロダクトが `reporting_capabilities.vendor_metrics` で宣言するベンダーメトリクスのみをフォーキャストすべきです(SHOULD)。 #### フォーキャスト範囲単位 `forecast_range_unit` フィールドは、コンシューマーが points 配列をどう解釈するか — カーブが表す軸 — を伝える: * **`spend`**(デフォルト)— 予算レベルの昇順ポイント。標準的な予算カーブ。 * **`availability`** — 各ポイントは、要求されたターゲティングと日付に対する利用可能な総在庫を表します。予算は省略され、`metrics.spend` で推定コストを表します。guaranteed および直接販売の在庫で一般的。 * **`reach_freq`** — リーチ/フリークエンシーターゲットの昇順ポイント。パブリッシャーがフリークエンシー目標に応じてコストがどのようにスケールするかを示す放送計画で使用。 * **`weekly`** / **`daily`** — メトリクスは期間ごとの値。Budget はキャンペーン総スペンドを指します。`weekly` で頻度 3.2 は週 3.2 回の接触を意味します。 * **`clicks`** / **`conversions`** — 成果ターゲットの昇順ポイント。目標ベースの計画で使用(例: 「コンバージョン目標を教えてくれれば予算を伝える」)。 * **`package`** — 各ポイントは別個の在庫パッケージ(例: Good/Better/Best のティア)を表します。ポイントはスペンドカーブ上のレベルではなく、異なる在庫構成を持つ別個のプロダクトです。放送 TV、オーディオ、DOOH のセラーが使用します。 スペンドカーブとリーチ/フリークエンシーカーブは同一データを含む場合がある — 違いはパブリッシャーの意図です。スペンドカーブは「異なる予算で何が買えるか」を示し、リーチ/フリークエンシーカーブは「異なるフリークエンシー目標を達成するのにいくらかかるか」を示します。コンシューマーはどちらのカーブも双方向に読み取れる。 時間単位(`weekly`、`daily`)はメトリクスの解釈を変える。範囲単位なし(または `spend`)の場合、頻度 3.2 はキャンペーン全体で 3.2 回の接触を意味します。`weekly` の場合は週あたり 3.2 回を意味します。 フォーキャストは 2 つのレベルで表れる: * **プロポーザルレベル**: メディアプラン全体の集計フォーキャスト * **配分レベル**: 個別ラインアイテムのプロダクトごとフォーキャスト 配分レベルのフォーキャストは、オーディエンス重複とフリークエンシーキャッピングにより、プロポーザルレベルのフォーキャストに加算されない場合があります。両方が存在する場合、プロポーザルレベルのフォーキャストが総デリバリー推定の権威となります。 クロスチャネル計画では、フォーキャストは `reach_unit`(個人、世帯、デバイス、アカウント、Cookie)を宣言し、バイヤーがパブリッシャー間でリーチを比較できるようにします。GRP ベースのフォーキャスト(地上波 TV、ラジオ)は、CPP 価格と同じパターンに従い、ターゲットデモを指定するために `demographic_system` と `demographic` を使用します。 フォーキャストが第三者計測に基づく場合、`measurement_source` フィールドは、数値を生成するのにどのプロバイダーのデータが使われたかを宣言します。これはデモ表記を指定する `demographic_system` とは別物です——`measurement_source` は誰のデータがフォーキャスト数値を生成したかを識別します。フォーキャストは、Nielsen のデモコード(`demographic_system: "nielsen"`)を使いつつ、インプレッション数値は VideoAmp 由来(`measurement_source: "videoamp"`)という場合があります。 フォーキャストが第三者計測に基づくセラーは、`measurement_source` プロバイダーが数えた配信を表すために `measured_impressions` を使います。これは、広告サーバーまたはファーストパーティの推定配信を表す `impressions` とは別物です。この二つのメトリクスは保証とは独立しています——`measured_impressions` は guaranteed と non\_guaranteed の両方のフォーキャストに現れうる: * **保証付き放送**: `method: "guaranteed"` + `measured_impressions` + `measurement_source: "nielsen"` — セラーは Nielsen が計測した配信を契約上コミットする * **非保証 CTV**: `method: "modeled"` + `measured_impressions` + `measurement_source: "videoamp"` — VideoAmp が計測した推定値、契約上のコミットなし * **プログラマティックディスプレイ**: `method: "modeled"` + `impressions` — 広告サーバーのカウント、第三者通貨は不要 セラーは、バイヤーが第三者計測の数値と広告サーバーの推定値の両方を必要とする場合、同じポイントに `measured_impressions` と `impressions` の両方を含められます。 ポッドキャストのセラーは、IAB Podcast Measurement ガイドラインに従い、`impressions` の代わりに、または並べて、主要な配信通貨として `downloads` を使います。 #### 予算カーブ 予算の昇順で並べられた複数のフォーキャストポイントは、メトリクスがスペンドに応じてどのようにスケールするかを示し、バイヤーが最適な投資レベルを見つけるのに役立つ: ```json theme={null} { "points": [ { "budget": 25000, "metrics": { "impressions": { "low": 400000, "mid": 500000, "high": 600000 }, "reach": { "mid": 180000 }, "clicks": { "mid": 2000 } } }, { "budget": 50000, "metrics": { "impressions": { "low": 850000, "mid": 1050000, "high": 1200000 }, "reach": { "mid": 320000 }, "clicks": { "mid": 4200 } } }, { "budget": 100000, "metrics": { "impressions": { "low": 1500000, "mid": 1900000, "high": 2200000 }, "reach": { "mid": 500000 }, "clicks": { "mid": 7600 } } } ], "method": "modeled", "currency": "USD", "reach_unit": "individuals" } ``` カーブは収穫逓減を明らかにする — 予算を \$50K から \$100K に倍増してもリーチは 2 倍でなく約 56% 増に過ぎません。バイヤーはこれを交渉やパブリッシャー間の予算再配分に活用できます。 #### 可用性フォーキャスト guaranteed および直接販売の在庫では、フォーキャストは可用性チェックです——このプレースメントに、このターゲティングで、このフライト期間にどれだけの在庫が存在するか。利用可能な在庫はバイヤーの支出額に依存しないため、予算は省略されます。セラーは、利用可能な在庫の推定コストを表すために `metrics.spend` を含められます: ```json theme={null} { "points": [ { "metrics": { "impressions": { "low": 320000, "mid": 400000, "high": 480000 }, "reach": { "low": 200000, "mid": 260000, "high": 300000 }, "spend": { "low": 6400, "mid": 8000, "high": 9600 } } } ], "forecast_range_unit": "availability", "method": "guaranteed", "currency": "USD" } ``` バイヤーエージェントは、利用可能なインプレッションを予算要件と比較して過少配信を特定できます。バイヤーが \$10K の予算全額を消化するために \$20 CPM で 500,000 インプレッションを必要とし、フォーキャストが mid 400,000 を示す場合、バイヤーは \$2K の予算を他へ配分しなければならないと分かります。 #### 次元別の可用性と計測 セラーは、国別、プレースメント別、シグナル値別、プレースメント×国、プロダクト×シグナル、その他の次元的交差ごとの可用性を、同じプロダクト上のフォーキャストポイントとして公開できます。複数の `dimensions` 項目を持つポイントは、列挙されたすべての制約の交差を表します。行は、複数国のプレースメント×国の行や、一つのプロダクトベースラインのシグナル値の行のように、同じ次元粒度を使う場合に比較可能です。セラーが行が完全で重複のないパーティションを形成すると明示的に文書化しない限り、バイヤーは行を合計してはなりません(MUST NOT)。 単一のポイント内では、次元項目は OR ではなく AND されます。セラーは `kind` ごとに複数の項目を出してはならず(MUST NOT)、バイヤーは順序や繰り返しから OR の意味論を推測してはなりません(MUST NOT)。一つのポイント内の二つの `geo` 国の行のような繰り返しの同位値は、セラーの適合性の問題です。 `impressions`、`clicks`、`spend`、`measured_impressions`、`viewable_impressions` のようなカウントまたは通貨のメトリクスのみがロールアップの候補であり、宣言された完全で重複のないパーティション内でのみです。リーチ、フリークエンシー、レート、平均、`viewability.viewable_rate`、`viewability.viewed_seconds`、`vendor_metric_values[].value` は、セラーまたは計測ベンダーが明示的なロールアップ手法を公開しない限り、加算的ではありません。`vendor_metric_values[].measurable_impressions` のようなベンダーメトリクスのカバレッジカウントは、他のカウントと同じパーティションルールに従います。 これにより、国別プロダクトやプレースメント別プロダクトへの展開を避けつつ、バイヤーエージェントが必要とするプランニングの行を提供できます: ```json theme={null} { "points": [ { "dimensions": [ { "kind": "placement", "placement_ref": { "publisher_domain": "publisher.example", "placement_id": "header_bidding" }, "placement_name": "Header bidding" }, { "kind": "geo", "geo_level": "country", "geo_code": "US", "geo_name": "United States" } ], "metrics": { "impressions": { "low": 900000, "mid": 1200000, "high": 1400000 }, "spend": { "mid": 24000 } }, "viewability": { "vendor": { "domain": "measurementvendor.example" }, "measurable_impressions": { "mid": 1050000 }, "viewable_rate": { "low": 0.68, "mid": 0.73, "high": 0.78 }, "standard": "mrc" }, "vendor_metric_values": [ { "vendor": { "domain": "attentionvendor.example" }, "metric_id": "attention_units", "value": { "low": 3.9, "mid": 4.4, "high": 4.9 }, "unit": "score", "measurable_impressions": { "mid": 980000 } } ] }, { "dimensions": [ { "kind": "placement", "placement_ref": { "publisher_domain": "publisher.example", "placement_id": "header_bidding" }, "placement_name": "Header bidding" }, { "kind": "geo", "geo_level": "country", "geo_code": "CA", "geo_name": "Canada" } ], "metrics": { "impressions": { "low": 250000, "mid": 320000, "high": 380000 }, "spend": { "mid": 9600 } }, "viewability": { "vendor": { "domain": "measurementvendor.example" }, "viewable_rate": { "mid": 0.81 }, "standard": "mrc" } } ], "forecast_range_unit": "availability", "method": "modeled", "currency": "USD" } ``` 各ポイントは別個のプロダクトではなく、フォーキャスト内の一行です。バイヤーは、国×プレースメントのアベイル、ビューアビリティの見込み、ベンダー計測のフォーキャストを比較しつつ、購入作成時にはプロダクトの通常の価格オプションから選択できます。 バイヤーエージェントは、選んだ次元の行を既存の購入時サーフェスを通じて購入に変換します: * `kind: "geo"` の行は、`geo_level` とセラーのサポートに応じて `packages[].targeting_overlay.geo_countries`、`geo_regions`、`geo_metros`、`geo_postal_areas` にマップします。国の行は ISO 3166-1 alpha-2 の `geo_code` を、リージョンの行は ISO 3166-2 の `geo_code` を使います。メトロの行は対応するターゲティング `system` 列挙を含み、ネイティブな郵便の行は `country` に加えて国ローカルの `system` を含みます。 * `kind: "device_type"` と `kind: "device_platform"` の行は、セラーがデバイスターゲティングをサポートする場合に対応するターゲティングオーバーレイフィールドにマップします。 * `kind: "audience"` の行は、そのプロダクトでオーディエンスが選択可能な場合にのみ `audience_include` またはシグナルターゲティングにマップします。情報提供のオーディエンス行はプランニングシグナルであり、自動的なターゲティングハンドルではありません。 * `kind: "placement"` の行は、まずプロダクトのリファインメントまたはセラーがサポートするパッケージのプレースメントターゲティングにマップします。`dimensions[].placement_ref` はフォーキャスト行が説明する在庫スライスを識別します。それ自体は購入パッケージを狭めるものではなく、バイヤーは `creative_assignments[].placement_refs` のショートカットとして扱うべきではありません(SHOULD NOT)。バイヤーがそのプレースメントのみを買いたい場合、プロダクトがそのプレースメントを `mode: "targetable"` として公開するか、バイヤーはリファインされたプロダクト/プロポーザルを要求すべきです。`creative_assignments[].placement_refs` は、購入の在庫スコープが確立された後のクリエイティブルーティング面にすぎず、それ自体は購入在庫を狭めません。プレースメント次元を持つプロポーザルレベルのフォーキャストポイントは、プレースメントが一つの配分のプロダクトにマップする場合は `product_id` を含めるべきです。プロダクトコンテキストがない場合、プロポーザルレベルのフォーキャスト上のプレースメント行は、直接実行可能な選択ではなく情報提供のプランニング行です。 `kind: "signal"` の行は、正準的な `signal_ref` に任意の `signal_value` を加えてシグナルバケットを説明します。シグナルが供給された値で利用可能な行には `presence: "present"` を、明示的な非存在バケットには `signal_value: null` を伴う `presence: "absent"` を使います。`signal_id` は、囲むオブジェクトが既にシグナルを一意に識別している場合(単一の `get_signals` 項目の直下にネストされたカバレッジフォーキャストなど)の省略記法にすぎません。プロダクトレベルのフォーキャストはプロダクトコンテキストに `ForecastPoint.product_id` を使います。別個のプロダクト次元項目を追加しないでください。 購入後、バイヤーは `get_media_buy_delivery.reporting_dimensions`(`geo`、`device_type`、`device_platform`、`audience`、`placement`)で一次元の周辺分布を検証し、パッケージの `committed_metrics`、`performance_standards`、報告された `viewability` / `vendor_metric_values` を通じて計測の見込みを照合します。標準の配信レポートは現在、プレースメント×国やプロダクト×シグナルのような次元横断の交差を返しません。それらの交差について正確な購入後の照合が必要なセラーは、セラー固有のレポート面、または将来の標準的な交差機能を公開しなければなりません。 #### GRP デモグラフィクス付き CTV TV およびオーディオのフォーキャストは、ターゲットデモを指定するために `demographic_system` と `demographic` を使用し、フォーキャストが誰のオーディエンスデータに対してモデル化されているかを宣言するために `measurement_source` を使用します: ```json theme={null} { "points": [ { "budget": 75000, "metrics": { "grps": { "low": 45, "mid": 60, "high": 72 }, "impressions": { "mid": 3200000 }, "reach": { "low": 800000, "mid": 1100000, "high": 1300000 }, "frequency": { "mid": 2.9 } } } ], "method": "modeled", "measurement_source": "nielsen", "currency": "USD", "demographic_system": "nielsen", "demographic": "P18-49", "reach_unit": "households" } ``` `measurement_source: "nielsen"` は、GRP とインプレッションの数値が Nielsen データに対してモデル化されていることをバイヤーエージェントに伝えます。`reach_unit: "households"` は、この CTV パブリッシャーがリーチを個人ではなく世帯で計測することをバイヤーに伝える。`reach_unit: "devices"` を報告するディスプレイパブリッシャーは異なるものを計測しており、バイヤーは 2 つのリーチ数を直接比較すべきではありません。 `measurement_source` と `demographic_system` は異なりうる点に注意してください。CTV パブリッシャーが Nielsen のデモ表記(`demographic_system: "nielsen"`、`demographic: "P18-49"`)を使いつつ、基盤となるオーディエンスデータは VideoAmp 由来(`measurement_source: "videoamp"`)という場合があります。デモグラフィックシステムは表記を指定し、計測ソースはどの数値がフォーキャストを生成したかを指定します。 #### 成果フォーキャスト付きリテールメディア リテールメディアパブリッシャーはデリバリーメトリクスとコンバージョン成果の両方を予測できます。成果メトリクスキーは `event-type` 値を使用します: ```json theme={null} { "points": [ { "budget": 30000, "metrics": { "impressions": { "low": 600000, "mid": 750000, "high": 900000 }, "clicks": { "mid": 6000 }, "purchase": { "low": 1200, "mid": 1800, "high": 2400 }, "add_to_cart": { "mid": 4500 } } } ], "method": "modeled", "currency": "USD" } ``` ここで `impressions` と `clicks` は `forecastable-metric` 値、`purchase` と `add_to_cart` は `event-type` 値です。どちらも ForecastRange(low/mid/high)を使用し、同じメトリクスマップに共存します。 #### 配分レベルフォーキャスト プロポーザルに配分ごとのフォーキャストが含まれる場合、バイヤーは各プロダクトを独立して評価できる: ```json theme={null} { "proposal_id": "retail_holiday_v1", "name": "Holiday Retail Campaign", "allocations": [ { "product_id": "sponsored_search", "allocation_percentage": 40, "forecast": { "points": [ { "budget": 20000, "metrics": { "impressions": { "mid": 500000 }, "clicks": { "mid": 15000 }, "purchase": { "mid": 900 } } } ], "method": "modeled", "currency": "USD" } }, { "product_id": "offsite_display", "allocation_percentage": 60, "forecast": { "points": [ { "budget": 30000, "metrics": { "impressions": { "low": 1800000, "mid": 2200000, "high": 2600000 }, "reach": { "mid": 450000 }, "purchase": { "low": 400, "mid": 600, "high": 800 } } } ], "method": "modeled", "currency": "USD", "reach_unit": "accounts" } } ], "forecast": { "points": [ { "budget": 50000, "metrics": { "impressions": { "low": 2100000, "mid": 2700000, "high": 3100000 }, "reach": { "mid": 520000 }, "purchase": { "low": 1100, "mid": 1500, "high": 1900 } } } ], "method": "modeled", "currency": "USD", "reach_unit": "accounts" } } ``` この例では配分フォーキャスト(900 + 600 = 1,500 件の購買)がプロポーザルフォーキャストと偶然一致しているが、通常はそうならない — オーディエンス重複とフリークエンシーキャッピングにより、全体は部分の和より小さくなることが多い。プロポーザルレベルのフォーキャストが総デリバリーの権威となります。 #### 放送オーディオスポットプラン 放送およびオーディオパブリッシャーは、各配分に `daypart_targets` を持つスポットプランプロポーザルを返し、`forecast_range_unit: "weekly"` で週次フリークエンシー予測を行うことができます。このパターンにより、パブリッシャーが最適化問題を解く — バイヤーがフリークエンシー目標を指定すると、パブリッシャーがそれを達成するプランを返します: ```json theme={null} { "proposal_id": "iheart_q4_audio", "name": "Q4 Audio - Adults 25-54", "allocations": [ { "product_id": "morning_drive_30s", "allocation_percentage": 50, "daypart_targets": [ { "days": ["monday", "tuesday", "wednesday", "thursday", "friday"], "start_hour": 6, "end_hour": 10, "label": "Morning Drive" } ], "rationale": "Morning drive delivers highest reach against P25-54 with 3x weekly frequency at 2 spots/day", "forecast": { "points": [ { "budget": 37500, "metrics": { "grps": { "mid": 42 }, "reach": { "low": 140000, "mid": 180000, "high": 210000 }, "frequency": { "mid": 3.2 }, "impressions": { "mid": 576000 } } } ], "forecast_range_unit": "weekly", "method": "modeled", "currency": "USD", "demographic_system": "nielsen", "demographic": "P25-54", "reach_unit": "individuals" } }, { "product_id": "afternoon_drive_30s", "allocation_percentage": 30, "daypart_targets": [ { "days": ["monday", "tuesday", "wednesday", "thursday", "friday"], "start_hour": 15, "end_hour": 19, "label": "Afternoon Drive" } ], "rationale": "Afternoon drive complements morning with incremental reach and frequency overlap", "forecast": { "points": [ { "budget": 22500, "metrics": { "grps": { "mid": 28 }, "reach": { "low": 95000, "mid": 120000, "high": 145000 }, "frequency": { "mid": 2.4 }, "impressions": { "mid": 288000 } } } ], "forecast_range_unit": "weekly", "method": "modeled", "currency": "USD", "demographic_system": "nielsen", "demographic": "P25-54", "reach_unit": "individuals" } }, { "product_id": "daytime_30s", "allocation_percentage": 20, "daypart_targets": [ { "days": ["monday", "tuesday", "wednesday", "thursday", "friday"], "start_hour": 10, "end_hour": 15, "label": "Daytime" } ], "rationale": "Daytime fill provides frequency reinforcement at lower CPP", "forecast": { "points": [ { "budget": 15000, "metrics": { "grps": { "mid": 18 }, "reach": { "low": 60000, "mid": 80000, "high": 95000 }, "frequency": { "mid": 1.8 }, "impressions": { "mid": 144000 } } } ], "forecast_range_unit": "weekly", "method": "modeled", "currency": "USD", "demographic_system": "nielsen", "demographic": "P25-54", "reach_unit": "individuals" } } ], "forecast": { "points": [ { "budget": 75000, "metrics": { "grps": { "mid": 82 }, "reach": { "low": 220000, "mid": 280000, "high": 330000 }, "frequency": { "mid": 4.1 }, "impressions": { "mid": 1008000 } } } ], "forecast_range_unit": "weekly", "method": "modeled", "currency": "USD", "demographic_system": "nielsen", "demographic": "P25-54", "reach_unit": "individuals" } } ``` 各フォーキャストの `forecast_range_unit: "weekly"` は、すべてのメトリクスが週次値であることをバイヤーに伝える — 頻度 3.2 はキャンペーン全体でなく週あたり 3.2 回の接触を意味します。Budget(\$75K)はキャンペーン総スペンドです。 各配分の `daypart_targets` はパブリッシャーが推奨する時間帯を指定します。これは `targeting` でのハードな daypart 制約と同じ構造 — ここではバイヤーが制約するのでなく、パブリッシャーがスポットプランを規定しています。 配分レベルのリーチは、同じリスナーがモーニングドライブとアフタヌーンドライブのスポットを両方聴く可能性があるため、プロポーザルレベルに加算されない(180K + 120K + 80K > 280K)。プロポーザルレベルのフォーキャストはこの重複を考慮しています。 #### 放送 TV パッケージフォーキャスト 放送 TV のセラーは、可変の支出レベルでのインプレッションではなく、別個の在庫パッケージを提供します。`forecast_range_unit: "package"` は、各ポイントが支出カーブ上の位置ではなく別個のパッケージであることをバイヤーに伝えます。各ポイントには `label` が含まれ、バイヤーエージェントが個々のパッケージを識別・参照できます。放送局はデイタイムローテーター、プライムアクセス+デイタイムのバンドル、フルプライムパッケージを提供する、といったことがあります: ```json theme={null} { "points": [ { "label": "Daytime Rotator", "budget": 50000, "metrics": { "measured_impressions": { "mid": 800000 }, "grps": { "mid": 35 }, "reach": { "mid": 220000 }, "frequency": { "mid": 2.1 } } }, { "label": "Prime Access + Daytime", "budget": 85000, "metrics": { "measured_impressions": { "mid": 1400000 }, "grps": { "mid": 58 }, "reach": { "low": 290000, "high": 390000 }, "frequency": { "mid": 3.4 } } }, { "label": "Full Prime", "budget": 150000, "metrics": { "measured_impressions": { "mid": 2600000 }, "grps": { "mid": 95 }, "reach": { "low": 420000, "high": 540000 }, "frequency": { "mid": 5.2 } } } ], "forecast_range_unit": "package", "method": "modeled", "measurement_source": "nielsen", "currency": "USD", "demographic_system": "nielsen", "demographic": "P18-49", "reach_unit": "households" } ``` 各ポイントは別個のパッケージ——異なるデイパート、ユニットタイプ、フライト構造——を表し、同じプロダクトの三つの支出レベルではありません。`label` フィールドにより、バイヤーエージェントは交渉時や特定オプションの要求時にパッケージを名前で参照できます。`measurement_source: "nielsen"` は、インプレッションと GRP の数値が放送局自身の計測ではなく Nielsen データに対してモデル化されていることをバイヤーエージェントに伝えます。`measured_impressions` メトリクスは Nielsen が数えた配信を表します——`method: "modeled"` と組み合わさると、これらは Nielsen が計測した推定値です。それらを契約上のコミットメントにするには、セラーは代わりに `method: "guaranteed"` を使います。 放送の買い方の形がプロダクトの形を決めます: * **ネットワークまたはレップファームのパッケージ**: 一つのセラーが注文をコミットし、複数の市場やアフィリエイトにわたって履行する場合、一つのセラープロダクトとしてモデル化します。アフィリエイトは履行サーフェスであり、独立した AdCP のホップではありません。 * **ローカルスポットまたはステーショングループの在庫**: 在庫が別個の放送局、ステーショングループ、市場セラーによって販売される場合、別個のプロダクトまたはパッケージとしてモデル化します。 * **市場ごとのシンジケーション**: 在庫を販売しコミットする当事者でモデル化します。複数のセラーが別々にコミットする場合、バイヤーはメディアバイをまたいでそれらの配信レポートを集約します。 スポット、ユニット、または在庫が予約/スケジュールされる場合は `delivery_type: "guaranteed"` を使います。過去のオーディエンス範囲には `forecast.method: "modeled"` を使い、`forecast.method: "guaranteed"` は、セラーが記載範囲外の配信をメイクグッドすることを契約上コミットする場合にのみ使います。最終的な計測インプレッションが過去の範囲や第三者計測の精算に依存するというだけで `non_guaranteed` を使わないでください。 パッケージが同じ在庫プールを共有し、量やミックスのみが異なる場合は、一つのプロダクト上で `package` フォーキャストポイントを使います。それらが根本的に異なる在庫(重複のない異なる番組、プロパティ、デイパート)を表す場合は、それぞれ独自のフォーキャストを持つ別個のプロダクトを作成します。 同じ在庫を複数の計測通貨(例: Nielsen と VideoAmp の両方)で表すセラーは、`measurement_source` ごとに一つずつ、別個の DeliveryForecast オブジェクトを提供すべきです。 ## ディスカバリーとの統合 プロダクトは自然言語でキャンペーンブリーフにマッチさせる [Product Discovery](/docs/media-buy/product-discovery) プロセスで見つけ、特定後に `create_media_buy` で購入します。 ## 関連情報 * [Product Discovery](/docs/media-buy/product-discovery) - 自然言語でプロダクトを探索する方法 * [Media Buys](/docs/media-buy/media-buys) - プロダクト購入方法 * [Targeting](/docs/media-buy/advanced-topics/targeting) - 詳細ターゲティングオプション * [Creative Formats](/docs/creative/formats) - フォーマット仕様と探索 # リファインメント Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/product-discovery/refinement AdCP プロダクトリファインメント — get_products のリファインモードを使って、発見したプロダクトやプロポーザルを繰り返し改善します。メディアバイを作成する前に選択内容を調整し、変更をリクエストします。 リファインメントは、プロダクト発見を対話形式に変える仕組みです。最初の `brief` または `wholesale` による発見の後、`buying_mode: "refine"` を使って特定のプロダクトやプロポーザルを繰り返し改善できます。選択内容の調整、変更のリクエスト、代替案の探索などを行ってから、[`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) で確定します。 ## リファインメントのライフサイクル 典型的なメディアバイのワークフローは以下のパターンに従う: ``` discover → refine → refine → ... → buy ``` 1. **Discover** — `buying_mode: "brief"` または `"wholesale"` で `get_products` を呼び出し、マッチするインベントリを見つける。セラーはプロダクト(オプションでプロポーザルも)を返します。 2. **Refine** — `buying_mode: "refine"` と変更リクエストの `refine` 配列を指定して `get_products` を呼び出す。各エントリはスコープと、バイヤーが求める内容を宣言します。セラーは更新された価格と設定を持つプロダクトを返します。 3. **Repeat** — 必要な回数だけリファインを繰り返します。各呼び出しは独立しており、ステートレスです。 4. **Buy** — 満足したら、`create_media_buy` で最終的な選択を実行します。 リファインメントは必須ではありません。シンプルなキャンペーンは発見から購入へ直接進むことができます。ただし、複数のプロダクトを含むキャンペーン、予算配分を含むプロポーザル、または反復的な交渉が必要な場合、リファインメントこそが価値を生む場面です。 ## refine 配列 `refine` 配列は変更リクエストのリストです。各エントリは `scope` と、バイヤーが求める内容を宣言する: | スコープ | 目的 | 必須フィールド | | ---------- | ---------------- | ------------- | | `request` | 選択全体に対する方向性 | `ask` | | `product` | 特定のプロダクトへのアクション | `product_id` | | `proposal` | 特定のプロポーザルへのアクション | `proposal_id` | `refine` 配列には少なくとも1つのエントリが必要です。セラーはレスポンスを構成する際にすべてのエントリを総合的に考慮し、`refinement_applied` を通じて各エントリに返答します。 各スコープは独自の id フィールドを使います——プロダクトエントリには `product_id`、プロポーザルエントリには `proposal_id` で、AdCP が他のあらゆる場所で使っている id の命名規約に合わせています。`action` はプロダクトおよびプロポーザルエントリでは任意で、デフォルトは `"include"` です。 ### プロダクトアクション プロダクトスコープのエントリはアクションを宣言してもよい。省略した場合、セラーはそのエントリを `"include"` として扱う: | アクション | 動作 | `ask` | | ---------------- | ---------------------------------------- | ---------------------------------------------------- | | `include`(デフォルト) | このプロダクトを更新された価格とデータで返す | オプション — リクエストする具体的な変更(例: "add 16:9 format") | | `omit` | このプロダクトをレスポンスから除外する | 無視される | | `more_like_this` | このプロダクトに類似した追加のプロダクトを見つける。元のプロダクトも返されます。 | オプション — 「類似」の意味(例: "same audience but video format") | ```json theme={null} { "buying_mode": "refine", "refine": [ { "scope": "product", "product_id": "prod_video_premium", "ask": "add 16:9 format option" }, { "scope": "product", "product_id": "prod_display_ros", "action": "omit" }, { "scope": "product", "product_id": "prod_native", "action": "more_like_this", "ask": "same audience but video format" } ] } ``` ### リクエストレベルの方向性 `scope: "request"` を使って、選択全体に対して求める内容を記述する: ```json theme={null} { "buying_mode": "refine", "refine": [ { "scope": "request", "ask": "good selection but I want more video options and less display" }, { "scope": "product", "product_id": "prod_video_premium" }, { "scope": "product", "product_id": "prod_display_ros" }, { "scope": "product", "product_id": "prod_native" } ] } ``` セラーはこの方向性に基づいてプロダクトを追加・削除・再バランスしてもよい。`refine` 配列で参照されていないプロダクトも、セラーが方向性に合うと判断した場合はレスポンスに含まれることがあります。 **優先順位**: プロダクト個別のアクションはリクエストレベルの方向性より優先されます。リクエストレベルで「ディスプレイを減らして」と指定していても、特定のプロダクトにそれを含める明示的なアクションが設定されていれば、そのプロダクトは必ず返されます。 ### プロポーザルのリファインメント プロポーザルを `proposal_id` で参照して、調整や削除をリクエストする。プロダクトエントリと同様に、`action` はデフォルトで `"include"` になる: ```json theme={null} { "buying_mode": "refine", "refine": [ { "scope": "product", "product_id": "prod_video_premium" }, { "scope": "product", "product_id": "prod_display_ros" }, { "scope": "proposal", "proposal_id": "prop_balanced_v1", "ask": "shift 20% from display to video" } ] } ``` ### スコープの組み合わせ すべてのスコープは組み合わせて使える。1回のリファインメント呼び出しで、選択への方向性の設定、特定プロダクトへのアクション、プロポーザルへの変更リクエストを同時に行うことができる: ```json theme={null} { "buying_mode": "refine", "refine": [ { "scope": "request", "ask": "increase emphasis on video across the plan" }, { "scope": "product", "product_id": "prod_video_premium" }, { "scope": "product", "product_id": "prod_display_ros" }, { "scope": "product", "product_id": "prod_native", "action": "omit" }, { "scope": "product", "product_id": "prod_audio_spot" }, { "scope": "proposal", "proposal_id": "prop_awareness_q2", "ask": "reallocate native budget to video products" } ], "filters": { "budget_range": { "min": 200000, "max": 200000, "currency": "USD" } } } ``` ## セラーのレスポンス バイヤーが `refine` 配列を送信すると、セラーは `refinement_applied` で応答します。これはバイヤーの変更リクエストと位置が一致する配列です。各エントリはリクエストが満たされたかどうかを報告する: | フィールド | 型 | 必須 | 説明 | | ------------- | ------ | ----------------------------- | ------------------------------------------------------------------------- | | `scope` | string | Yes | 対応する `refine` エントリのスコープ(`"request"` / `"product"` / `"proposal"`)をエコーします。 | | `product_id` | string | `scope` が `"product"` の場合は必須 | 対応するリファインエントリの `product_id` をエコーします。 | | `proposal_id` | string | `scope` が `"proposal"` の場合は必須 | 対応するリファインエントリの `proposal_id` をエコーします。 | | `status` | string | Yes | `"applied"`: リクエスト充足。`"partial"`: 部分的に充足。`"unable"`: 充足できなかった。 | | `notes` | string | No | セラーの説明。`"partial"` または `"unable"` の場合に推奨されます。 | ```json theme={null} { "products": ["..."], "proposals": ["..."], "refinement_applied": [ { "scope": "request", "status": "applied", "notes": "Added 3 video products. No CTV inventory for those dates." }, { "scope": "product", "product_id": "prod_video_premium", "status": "applied" }, { "scope": "product", "product_id": "prod_display_ros", "status": "applied" }, { "scope": "product", "product_id": "prod_native", "status": "applied" }, { "scope": "product", "product_id": "prod_audio_spot", "status": "partial", "notes": "16:9 not available for this placement — returning 4:3 and 1:1" }, { "scope": "proposal", "proposal_id": "prop_awareness_q2", "status": "applied", "notes": "Shifted 22% to video (nearest allocation boundary)" } ] } ``` `refinement_applied` 配列は `refine` 配列と同じ数のエントリを同じ順序で含まなければなりません(MUST)。各エントリは、オーケストレーターがアライメントをクロスバリデーションできるよう、`scope` と対応する id(プロダクトスコープでは `product_id`、プロポーザルスコープでは `proposal_id`)をエコーしなければなりません(MUST)。このフィールド全体はオプションであり、リクエストごとの結果を追跡しないセラーは省略してもよい——ただし、それを返すセラーは、有効で位置が一致したエントリを返さなければなりません(MUST)。 オーケストレーターは、位置の順序だけを信頼するのではなく、エコーされた id でエントリをクロスチェックすべきだ(SHOULD)——そうでなければ、エントリを並べ替えてしまうセラーのバグが、各結果を黙って取り違えてしまいます。 ## よくあるリファインメントパターン ### 類似プロダクトを見つける `more_like_this` を使って、気に入ったプロダクトに類似したプロダクトを発見します。セラーは元のプロダクトに加えて、その特性に合った追加の選択肢を返します: ```json theme={null} { "buying_mode": "refine", "refine": [ { "scope": "product", "product_id": "prod_video_premium", "action": "more_like_this", "ask": "same premium audience but different formats" } ] } ``` ### フィルターを調整します リファインリクエストのフィルターは、差分ではなく完全な目標状態を表します。適用したいフィルターセット全体を常に送信すること: ```json theme={null} { "buying_mode": "refine", "refine": [ { "scope": "product", "product_id": "prod_video_premium" }, { "scope": "product", "product_id": "prod_display_ros" } ], "filters": { "start_date": "2026-04-01", "end_date": "2026-06-30", "budget_range": { "min": 150000, "max": 150000, "currency": "USD" } } } ``` ### プロポーザルを絞り込む、または拡張します プロダクトエントリは、セラーがプロポーザルに対して考慮すべきプロダクトを定義します。プロポーザルエントリと組み合わせることで、プロポーザルのプロダクトセットを絞り込んだり拡張したりできる: ```json theme={null} { "buying_mode": "refine", "refine": [ { "scope": "product", "product_id": "prod_video_premium" }, { "scope": "product", "product_id": "prod_display_ros" }, { "scope": "proposal", "proposal_id": "prop_balanced_v1", "ask": "rebalance for just these two products" } ] } ``` ## Finalize は `refine[]` 内で排他的 `action: "finalize"` はバイヤーの受諾ではなくセラーのコミットです——ドラフトのプロポーザルを、確定した価格と expires\_at の保留ウィンドウを持つコミット済みへ遷移させます。バイヤーは後で `create_media_buy(proposal_id)` を通じて、コミット済みのプロポーザルを受諾/実行します。バイヤーがファイナライズしたい場合、仕様は `refine[]` 配列が finalize エントリ**のみ**を含むことを要求します: * いずれかのエントリが `action: "finalize"` を持つ場合、配列内の**すべて**のエントリがプロポーザルスコープで `action: "finalize"` でなければなりません(MUST)。finalize を `include` / `omit` エントリと、またはリクエストスコープやプロダクトスコープのエントリと混在させることは、セラーによって `INVALID_REQUEST` で拒否されなければなりません(MUST)。 * リファインメント*と*コミットを近接した連続で行う必要があるバイヤーは、呼び出しを**順序立てて**行います: まずリファイン呼び出し(finalize なし)、次に結果の `proposal_id` に対する finalize 呼び出しです。この二つの意図は別個の決定であり、仕様はそれらを別個の呼び出しとして扱います。 ```yaml theme={null} # ✅ Refine only — no finalize present, mixed scopes allowed refine: - { scope: proposal, proposal_id: p1, ask: "shift more to ctv" } - { scope: request, ask: "frequency cap 3/day across all products" } # ✅ Finalize only — multiple finalize entries against different proposals refine: - { scope: proposal, proposal_id: p1, action: finalize } - { scope: proposal, proposal_id: p2, action: finalize } # ❌ Rejected — finalize mixed with non-finalize refine: - { scope: proposal, proposal_id: p1, action: finalize } - { scope: proposal, proposal_id: p2, ask: "shift more to ctv" } ``` **複数ファイナライズは観測点でアトミックです。** 複数の finalize エントリが一つの呼び出しで異なるプロポーザルを対象とする場合、契約は次のとおりです: セラーは、名指しされたすべてのプロポーザルが完了しコミット済みとして永続化されていない限り、成功レスポンスを返してはなりません(MUST NOT)。コミット前のバリデーションは、いかなる副作用(インベントリのプル、条件のロック、ガバナンスのアテステーション)よりも前に実行されます。いずれかのプロポーザルがバリデーションに失敗した場合、セラーはどれもコミットせずに呼び出し全体を拒否しなければなりません(MUST)。`unfinalize` 操作はありません——アトミック性はコミット後の巻き戻しではなく、コミット前のバリデーションゲートで担保されます。アトミックなコミット前バリデーションを保証できないセラーは、複数ファイナライズの配列を `MULTI_FINALIZE_UNSUPPORTED`(推奨——クライアント側のミスではなくセラー側のケイパビリティのギャップを明示的に示します)または `INVALID_REQUEST`(3.1 より前のエラーカタログのセラー向けの許容されるフォールバック)で拒否しなければならず(MUST)、バイヤーはその場合、単一ファイナライズの呼び出しを順序立てて行うべきです(SHOULD)。 **コミット途中の失敗(バリデーション後、永続化前)。** 下流のシステムがコミット 1 とコミット 2 の間で失敗した場合——例えば、最初のシステムがすでにインベントリをロックした後に 2 番目のアドサーバーがタイムアウトした場合——セラーは、位置ごとの結果を運ぶ `refinement_applied[]` とともに `INTERNAL_ERROR` を返さなければなりません(MUST)。仕様はリカバリのパスを定義**しません**: バイヤーは、結果として生じる状態を未定義として扱い、リトライの前に `get_media_buys` / 同等の手段で再読み込みすべきです(SHOULD)。このケースからのリカバリは運用上のものであり、プロトコルが定義するものではありません。 **バイヤーの意図に関する注意。** 意図が明確にアトミックなコミットを必要とするバイヤー(例: 一方だけがファイナライズされると意味を成さない予算共有のプロポーザル)は、セラーが `MULTI_FINALIZE_UNSUPPORTED` を返した場合にその意図を放棄する用意がなければなりません(MUST)。フォールバックのパス——単一ファイナライズの呼び出しを順序立てて行うこと——は、元のアトミックな意図よりも緩いコミットの保証です。その意図の喪失に対しては、より緩い保証を受け入れるか、コミットを完全に断念する以外のリカバリはありません。複数ファイナライズのサポートを示すケイパビリティフラグはありません——失敗レスポンスが発見のためのサーフェスなので、バイヤーは最初の試行が成功するまでサポートを前提としてはなりません(MUST NOT)。 ## リファインモードにおけるプロポーザル セラーはバイヤーがプロポーザルエントリを含めなかった場合でも、リファインされたプロダクトと一緒にプロポーザルを返してもよい(MAY)。例えば、3つのプロダクトをリファインしているバイヤーが、それらのプロダクトを更新された価格で受け取ると同時に、それらを組み合わせる方法を提案するプロポーザルも受け取ることがあります。 重要なポイント: * **プロポーザルは保証されない。** セラーはリファインモードでプロポーザルを生成することを要求されない。配分とキャンペーン最適化は主にオーケストレーター(バイヤーサイドエージェント)の責任です。 * **リクエストレベルの ask で関心を示します。** `{ "scope": "request", "ask": "suggest how to combine these products" }` を含めることで、プロポーザルを歓迎することを示せる。 * **求めていないプロポーザルはリファインするか無視できます。** セラーがリクエストしていないプロポーザルを返した場合、フォローアップ呼び出しでリファインするか、単純に無視して `create_media_buy` でパッケージを手動で構築することができます。 パブリッシャーは通常、バイヤーが自身でターゲティングと配分を指示する `wholesale` モードではプロポーザルを省略します。 ## ステートレス性 `buying_mode: "refine"` を使った各 `get_products` リクエストは独立しています。各リクエストの `refine` 配列と `filters` がリファインメントの意図を完全に指定します。セールスエージェントはトランスポートレベルのセッション状態(例: 前のリクエストで送信された内容を記憶すること)に依存してはなりません(MUST NOT)。 セラーは引き続き独自のプロダクトおよびプロポーザルレジストリを管理します。「ステートレス」とは、*プロトコル交換*が呼び出し間で暗黙的な状態を持たないことを意味します。 この設計により以下が可能になる: * **ステートレス実装** — セラーはリファインメントセッションを追跡する必要がない * **安全なリトライ** — 失敗したリファインメント呼び出しは同じパラメーターで再試行できます * **並列探索** — オーケストレーターが複数のリファインメントパスを同時に探索できます ## クライアントバリデーション オーケストレーターはリファインメントリクエストを送信前にバリデーションすべきだ(SHOULD): * **空でない refine** — `refine` 配列には少なくとも1つのエントリが必要です。空の `[]` はスキーマバリデーションで拒否されます。 * **有効なエントリ** — 各プロダクトエントリには `scope` と `product_id` が必要です。各プロポーザルエントリには `scope` と `proposal_id` が必要です。リクエストレベルエントリには `scope` と `ask` が必要です。`action` はプロダクトおよびプロポーザルエントリでは任意です(デフォルトは `"include"`)。有効な値は、プロダクトでは `include` / `omit` / `more_like_this`、プロポーザルでは `include` / `omit` / `finalize` です。 * **フィルターは絶対値** — 前のリクエストからの差分ではなく、適用したいフィルターセット全体を送信すること。 クライアント実装は送信前に[リクエストスキーマ](/docs/building/schemas-and-sdks)に対してリファインメントリクエストをバリデーションすべきだ(SHOULD)。 ## エラーハンドリング | エラーコード | 発生条件 | 対処法 | | ---------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | `PRODUCT_NOT_FOUND` | 参照されたプロダクト ID の1つ以上が不明または期限切れ | 無効な ID を削除して再試行するか、`brief` リクエストで再発見する | | `PROPOSAL_EXPIRED` | 参照されたプロポーザル ID が `expires_at` を過ぎている | 新しい `brief` または `wholesale` リクエストで再発見する | | `PROPOSAL_NOT_FOUND` | 参照された `proposal_id` がセラーにとって不明(ファイナライズされたことがない、テナントが誤っている、またはキャッシュから追い出された) | `action: 'finalize'` 付きの `refine` モードで `get_products` を再発行し、現在の proposal\_id を取得する | | `MULTI_FINALIZE_UNSUPPORTED` | `refine[]` が複数の `action: 'finalize'` エントリを運んだが、セラーが複数プロポーザルのアトミックなコミットを保証できない | 単一プロポーザルのファイナライズ呼び出しを順序立てて行う(`get_products` 呼び出しごとに 1 つの finalize) | | `INVALID_REQUEST` | `brief` または `wholesale` モードで `refine` が指定された、`refine` 配列が空、または必須フィールドが欠落している | `buying_mode` と必須フィールドを確認する | ### トラブルシューティング: `refine[].id` に対する "must NOT have additional properties" `refine[]` の各スコープ分岐は `additionalProperties: false` です。これは、3.0-rc より前のリファインの形からの余分な `id` フィールドが——黙って無視されるのではなく——次のようなエラーで拒否されることを意味します: ``` /refine/0: must NOT have additional properties { additionalProperty: "id" } /refine/0: must match oneOf schema { required: ["product_id"] } ``` これが見えた場合、オーケストレーターがまだ汎用の `id` フィールドでプロダクトまたはプロポーザルのリファインエントリを構築しています。`scope: "product"` の下では `product_id` へ、`scope: "proposal"` の下では `proposal_id` へリネームしてください。現在の形については[タスクリファレンス](/docs/media-buy/task-reference/get_products#refine-array)を参照してください。セラー側でエコーしている場合、同じリネームが `refinement_applied[]` にも適用されます。 ### セラーのマイグレーション `refinement_applied` を返すセラーには、バイヤーと並行して破壊的な作業があります: * 各レスポンスエントリは、今や `scope` を運ばなければならず、プロダクト/プロポーザルスコープについては `product_id` / `proposal_id` をエコーしなければなりません。フラットな `{status, notes}` エントリはレスポンススキーマによって拒否されます。 * 受信する `refine[]` エントリで `action` が欠けている場合、エラーとしてパースするのではなく、`action: "include"` として扱わなければなりません。 * 3.0 リクエストスキーマに対するセラーの適合性テストは、まだ汎用の `id` フィールドを使う残存したオーケストレーターのペイロードを拒否します——アップグレード後はフィクスチャのコーパスを更新してください。 ## 規範的要件 [メディアバイ仕様](/docs/media-buy/specification#get_products) はリファインメントに関して以下の規範的要件を定義している: **オーケストレーター:** * `buying_mode` が `"refine"` の場合、`refine` を含めなければなりません(MUST) * `buying_mode` が `"brief"` または `"wholesale"` の場合、`refine` を含めてはなりません(MUST NOT) * 各プロダクトエントリに `scope` と `product_id` を、各プロポーザルエントリに `scope` と `proposal_id` を提供しなければなりません(MUST) * プロダクトおよびプロポーザルエントリの `action` を省略してもよい(MAY)——セラーは欠けている `action` を `"include"` として扱います * 1つの `refine` 配列に同じプロダクト ID またはプロポーザル ID を持つ複数のエントリを含めてはなりません(MUST NOT) **セールスエージェント:** * `action: "omit"` のプロダクトをレスポンスから除外しなければなりません(MUST) * `action: "omit"` のプロポーザルをレスポンスから除外しなければなりません(MUST) * `action: "include"` のプロダクトを更新された価格で返さなければなりません(MUST) * `action: "include"` のプロダクトエントリの `ask` を満たすべきだ(SHOULD) * `action: "more_like_this"` のプロダクトに類似した追加のプロダクトを元のプロダクトと共に返すべきだ(SHOULD) * レスポンスを構成する際にリクエストレベルの ask を考慮すべきだ(SHOULD)。これにより、明示的に参照されたプロダクト以外の追加プロダクトが含まれることがある(MAY)。プロダクト個別のアクションはリクエストレベルの方向性より優先されます。 * `action: "include"` のプロポーザルエントリの `ask` を満たすべきだ(SHOULD) * バイヤーが `refine` を提供する場合、位置で一致した1エントリあたりの変更リクエストを持つ `refinement_applied` をレスポンスに含めるべきだ(SHOULD) * バイヤーがプロポーザルエントリを含めなかった場合でもプロポーザルを返してもよい(MAY) ## 関連情報 * [`get_products` タスクリファレンス](/docs/media-buy/task-reference/get_products#refinement) — リクエスト/レスポンススキーマを含む API リファレンス * [メディアプロダクト](/docs/media-buy/product-discovery/media-products) — プロダクトモデルとプロポーザル構造 * [メディアバイ仕様](/docs/media-buy/specification#get_products) — 規範的要件 * [オーケストレーター設計](/docs/building/operating/orchestrator-design) — バイヤーサイドエージェントの構築 # プロトコルアーキテクチャ Source: https://adcp-docs-ja.pier1.co.jp/docs/protocol/architecture AdCP プロトコルアーキテクチャ: アイデンティティ層(brand、registry、accounts)、取引ドメイン(media buy、creative、signals、sponsored intelligence)、および横断的なガバナンス。 # プロトコルアーキテクチャ AdCP は複数の層で動作し、ビジネスロール、オーケストレーション、技術的実行のあいだにクリーンな分離を提供します。 ## Protocol domain map Three-layer architecture diagram showing identity layer (brand, registry, accounts) at top, transaction domains (media buy, creative, signals, sponsored intelligence) in the middle, and governance as a cross-cutting layer connecting to all transaction domains ### Identity layer 3 つのプロトコルドメインが、いかなる取引が起こる前にも当事者が誰であるかを確立します。 **Brand Protocol** は、`/.well-known/brand.json` にホストされる `brand.json` ファイルを通じて組織アイデンティティを定義します。バイヤーはそれを広告主アイデンティティと認可されたオペレーターに使い、セラーはオペレーターアイデンティティ、セールスエージェントディスカバリー、署名鍵ディスカバリー、プロパティ関係宣言に使います。任意のドメインは正準なブランドアイデンティティに解決できます。[Brand Protocol](/docs/brand-protocol) を参照。 **Registry** は、エンティティ解決とエージェントディスカバリーのためのパブリックな REST API を提供します。ブランドドメインを解決し、どのエージェントがパブリッシャーのインベントリを販売する認可を受けているかを見つけ、またはケイパビリティでエージェントを発見します。[Registry API](/docs/registry) を参照。 **Accounts** は、バイヤーとセラーのあいだの商業的関係を確立します。すべての AdCP 取引は、課金条件、オペレーター認可、使用量レポートを定義するアカウント内で起こります。アカウントは Brand Protocol のブランドアイデンティティに根ざしています。[Accounts Protocol](/docs/accounts/overview) を参照。 ### Transaction domains 4 つのプロトコルドメインがコアの広告操作を扱います。[Trusted Match Protocol(TMP)](/docs/trusted-match) は実行層として機能し、4 フェーズのライフサイクルを通じて計画時の決定をリアルタイムのアクティベーションに接続します: 1. **Planning** — `get_products` と `create_media_buy` がパッケージ、予算、ターゲティング基準を確立する。 2. **Execution** — TMP Context Match がコンテンツ適合を判定し、TMP Identity Match がユーザー適格性をチェックする。両操作は構造的プライバシー分離のもとで配信時に実行される。 3. **Engagement** — Sponsored Intelligence セッションが会話的なブランド体験を提供する。 4. **Reporting** — `get_media_buy_delivery` がセラーをまたいで配信データを集約する。 **Media Buy** は、インベントリディスカバリー(`get_products`)、キャンペーン作成(`create_media_buy`)、配信レポート(`get_media_buy_delivery`)をカバーします。パブリッシャーは、価格、ターゲティングオプション、配信予測を含む構造化されたメディアプロダクトを返します。バイヤーはプロポーザル — パブリッシャーの専門知識をエンコードした構造化メディアプラン — をリクエストできます。[Media Buy](/docs/media-buy) を参照。 **Creative** は、フォーマットディスカバリー(`list_creative_formats`)、AI 駆動のクリエイティブ生成(`build_creative`)、カタログ同期(`sync_catalogs`)、クリエイティブ配信トラッキングを扱います。クリエイティブエージェントは Brand Protocol からブランドアイデンティティを解決し、オンブランドのアセットを生成します。[Creative](/docs/creative) を参照。 **Signals** は、オーディエンスとターゲティングデータのディスカバリー(`get_signals`)とアクティベーション(`activate_signal`)を可能にします。データプロバイダーはシグナル定義を公開し、バイヤーは自然言語クエリでそれを発見し、決定プラットフォーム上でアクティベートできます。[Signals](/docs/signals/overview) を参照。 **Sponsored Intelligence** は、AI アシスタントにおける会話的なブランド体験を定義します。ユーザーがブランドへの関心を表明すると、ホストは同意優先のセッションを開始し、そこでブランドのエージェントがテキスト、音声、UI コンポーネント、またはコマースハンドオフで会話的に関与します。[Sponsored Intelligence](/docs/sponsored-intelligence/overview) を参照。 ### Governance (cross-cutting) **Governance** はすべての取引ドメインを横断して動作します。ガバナンスエージェントは、プロパティリスト(ターゲティングや除外のためのプロパティのキュレーションされた集合)、コンテンツ標準(ブランド適合性ポリシー)、クリエイティブガバナンス(セキュリティスキャン、コンテンツ分類)を管理します。ガバナンスデータは、メディアバイの決定、クリエイティブ検証、シグナルアクティベーションに流れ込みます。[Governance Protocol](/docs/governance/overview) を参照。 Human-in-the-loop は 2 つのメカニズムを通じてプロトコルに入ります: 任意の変更はタスクライフサイクルを通じて人間のレビューのために非同期にでき、キャンペーンガバナンスは `sync_plans` と `check_governance` を通じて宣言的なバイヤー側のレビューチャネルを提供します。[How human-in-the-loop enters the protocol](/docs/governance/embedded-human-judgment#how-human-in-the-loop-enters-the-protocol) を参照。これはリアルタイムプロトコルではありません: 人間の承認が必要なとき、操作は数分から数日かかることがあります。 ### Privacy posture across domains AdCP のプライバシー姿勢は一様ではありません。TMP は、プライバシーを**構造的に**強制する唯一のドメインです — 分離されたコードパス、アイデンティティとコンテキストの結合に対するスキーマレベルの禁止、独立に検証可能な相関除去を通じて。他のすべてのドメインは**契約的機密性またはセッションごとの同意**に依存します — データを交換する当事者は、プロトコルレベルの分離ではなく、アカウントの条件やユーザーの同意に拘束されます。ガバナンスゲーティング(キャンペーンガバナンス経由)は直交します: それは予算、ポリシー、ブランドセーフティの根拠で人間の承認を要求できますが、基底のドメインのプライバシーメカニズムを変えません。 | Domain | Privacy mechanism | Notes | | --------------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Trusted Match Protocol | **構造的分離** | Context Match と Identity Match は分離されたコードパスで動作し、スキーマがクロスオーバーを禁止する。[TMP privacy architecture](/docs/trusted-match/privacy-architecture) を参照。 | | Media Buy | 契約的 | バイヤーとセラーはアカウント条件のもとで完全なプラン文脈を交換する。 | | Creative | 契約的 | クリエイティブアセットとターゲティングシグナルは、アカウント条件のもとでクリエイティブエージェントを通過する。 | | Signals | 契約的 | オーディエンスとシグナルデータは、アカウント条件とシグナルプロバイダー契約のもとで交換される。 | | Brand / Registry / Accounts | 公開または契約的 | ブランドアイデンティティは公開(`brand.json`)、商業条件はアカウントスコープ。 | | Sponsored Intelligence | 同意優先 | ユーザーはセッションごとに同意する。ブランドエージェントは会話内容を見、アイデンティティはユーザーが共有したときのみ見る。セッションをルーティングするネットワークはルーティングメタデータを見ることがある — [Networks](/docs/sponsored-intelligence/networks) を参照。 | | Governance | 契約的 | ガバナンスエージェントは、アカウント条件のもとでポリシーを評価するのに必要な文脈を受け取る。 | 「構造的」とは、オペレーターのポリシーではなくプロトコルが、機微データの結合を防ぐことを意味します。TMP の外でそのプロパティが必要なら、自分で構築するか TMP と合成してください。 *** ## Ecosystem layers 上のプロトコルドメインマップは、AdCP タスクが互いにどう関係するかを示します。下の図は、これらが現実世界のロールとシステムにどうマップするかを示します。 Four-tier ecosystem diagram showing business parties at top, orchestration layer with specialized agents in the middle, technical execution below, and governance with human oversight spanning all layers ### Business parties **Buy side** — 特定のユースケース向けにインベントリとデータをパッケージングする広告主、代理店、リテールメディアネットワーク、キュレーター。 **Media seller** — パブリッシャー、セールスハウス、レップファーム、SSP、アドネットワーク。 これらの当事者は、オーケストレーション層を通じてインプレッションとお金を交換します。 ### Orchestration layer **Media orchestration platform** — セラーとオーディエンスを評価し、購買戦略を実行する。MCP 経由で専門エージェントと通信する。 **Signals agent** — オーディエンスとターゲティングデータのディスカバリーとアクティベーションを公開する MCP サーバー。 **Sales agent** — メディアプロダクトディスカバリーとキャンペーン実行を公開する MCP サーバー。 **Creative agent** — フォーマットディスカバリーと AI 駆動のクリエイティブ生成を公開する MCP サーバー。 ### Technical execution **Trusted Match Protocol(TMP)** — 特定のインプレッションに対してどの事前交渉済みパッケージがアクティベートすべきかを判定するリアルタイム実行層。2 つの構造的に分離された操作 — Context Match(コンテンツ適合)と Identity Match(ユーザー適格性) — が、計画時のメディアバイを、web、モバイル、CTV、AI アシスタント、リテールメディアをまたぐ配信時の決定に接続する。[TMP ドキュメント](/docs/trusted-match) を参照。 **Agentic eXecution Engine(AXE)** — TMP の非推奨の前身。[AXE ドキュメント](/docs/media-buy/advanced-topics/agentic-execution-engine) を参照。 **Decisioning platform** — 直接キャンペーンまたはプログラマティック(RTB)を通じて、どの広告を配信するかを選択するインフラ。例には DSP、SSP、アドサーバーが含まれる。 ### Governance and human oversight **Governance agents** は、すべての層にわたってコンプライアンスと品質管理を提供します: プロパティリスト、ブランド適合性スコアリング、品質測定(MFA スコア、広告密度)、プライバシーコンプライアンス(COPPA、TCF、GDPR)。これらはセットアップ時、リアルタイム、ポストビッドで動作します。 **Human-in-the-loop** — 決定ポイントでの手動承認。[How human-in-the-loop enters the protocol](/docs/governance/embedded-human-judgment#how-human-in-the-loop-enters-the-protocol) を参照。 *** ## State persistence and horizontal scaling AdCP はマルチインスタンスプロトコルです。エージェントとの単一バイヤーのワークフローは、複数のバックエンドレプリカにまたがってルーティングされることがあります — `create_media_buy` が 1 つのレプリカに着地し、後続の `get_media_buy` が別のレプリカに着地する — そして両方の呼び出しは同じ状態を見なければなりません(MUST)。 ### Normative requirements `(brand, account)` タプルでキー付けされた状態は、エージェントプロセスインスタンスをまたいで生き残らなければなりません(MUST)。これには、アカウント、カタログ、クリエイティブ、オーディエンス、イベントソース、ガバナンス設定、アクティブキャンペーン、プロポーザル、インサーションオーダー承認レコード、シグナルアクティベーション、sponsored-intelligence セッション、非同期タスクレコード、冪等性キーキャッシュエントリが含まれますが、これらに限りません。実装は、後続の呼び出しが読み戻せる任意の `(brand, account)` スコープの状態のプライマリストアとして、プロセス内メモリを使ってはなりません(MUST NOT)。プロセス内ストレージはこの要件を満たしません。状態ドメインの正準な(網羅的でない)カタログについては [Account state](/docs/building/by-layer/L2/account-state) を参照。 **許容されるストレージ:** Postgres、Redis、DynamoDB、またはプロセス再起動をまたいで永続化し、すべてのレプリカから到達可能な任意の共有ストア。**許容されないもの:** モジュールレベル変数、プロセスごとの Map や dict、単一ノードのファイルストレージ、または共有状態の欠如を隠すスティッキーセッションルーティング。 単一の `(brand, account)` コンテキスト内で、実装はレプリカをまたいだ read-your-writes をサポートしなければなりません(MUST): 成功した非同期でないレスポンスの後、任意のレプリカにルーティングされた後続リクエストは、その書き込みを観測しなければなりません(MUST)。非同期/保留中のタスク状態(ステータス遷移、`context_id`、プッシュ通知サブスクリプション)自体もこのルールの対象です — タスクレコードが作成されたら、任意のレプリカから読み取り可能でなければなりません(MUST)。結果整合性は、古さのウィンドウが有界かつ開示されているとき許容されます — 実装が文書化した非同期ポーリング間隔に上限が設けられるか、`get_adcp_capabilities` で明示的に宣言されるかのいずれか。 **サンドボックス免除。** サンドボックスアカウントは、プロセス再起動をまたいで永続化しない一時的な状態を運ぶことが許されます([サンドボックスモード](/docs/media-buy/advanced-topics/sandbox) を参照)。単一のサンドボックスセッション内では、レプリカをまたいだ read-your-writes は依然として適用されます。 実装は、この不変条件をアーキテクチャ(マネージドサーバーレス + 共有データストア)、マルチインスタンス適合性テスト、または独自の検証によって証明してもかまいません。プロトコルは方法論ではなく不変条件を気にします。[Validate your agent — Verifying cross-instance state](/docs/building/validate-your-agent#verifying-cross-instance-state) を参照。 # AdCP エージェントの呼び出し Source: https://adcp-docs-ja.pier1.co.jp/docs/protocol/calling-an-agent すべての AdCP バイヤーが従わなければならないワイヤーレベルの不変条件: idempotency_key リプレイ、account の oneOf バリアント、非同期 status:'submitted' ポーリング、adcp_error.issues[] からのエラーリカバリー。 # AdCP エージェントの呼び出し このページは正準なバイヤー側のワイヤーコントラクトです: どの単一タスクスキーマにもきれいに収まらないが、あなたが行うすべての変更呼び出しに適用されるルール。バイヤー(DSP、プランニングツール、エージェンティッククライアント)を構築し、AdCP のセールス、クリエイティブ、シグナル、ガバナンス、SI、ブランドエージェントを呼び出すなら、これを一度読んでください。 このコンテンツのエージェント向けバージョンは [`skills/call-adcp-agent/SKILL.md`](https://github.com/adcontextprotocol/adcp/blob/main/skills/call-adcp-agent/SKILL.md) にあります — SDK がコーディングエージェントに出荷できるよう [プロトコル tarball](/docs/building/by-layer/L0/schemas#one-shot-protocol-bundle) にバンドルされています。 ## Discovery chain 任意の新しいエージェントとの最初の接触では、これらを順にたどります: 1. **Agent card**(A2A)または **`tools/list`**(MCP): ツール*名*を返す。AdCP MCP サーバーは `tools/list` でツールごとのパラメータースキーマをもはや公開しません — すべてのツールは `{type: 'object', properties: {}}` を示す。そこから形状を推論しようとしないでください。 2. **`get_adcp_capabilities`**: サポートするプロトコル、AdCP メジャーバージョン、機能フラグを返す。このエージェントが*どの*ツールをサポートするかを教えますが、それらをどう呼ぶかは教えません。[`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) を参照。 3. **`get_schema(tool_name)`** *(エージェントがそれを公開するとき — 標準化保留中、[#3057](https://github.com/adcontextprotocol/adcp/issues/3057) を参照)*: 特定のツールのリクエスト/レスポンスの JSON Schema を返す。 4. **バンドルされたスキーマ**(オフライン、権威的): すべての公開された AdCP バージョンは、すべてのツールの JSON Schema を Sigstore 経由で署名して出荷します。パスは SDK によって異なります — 仕様リポジトリソースは `dist/schemas//bundled/` を使い、`@adcp/sdk` は `npm run sync-schemas` の後にそれらを `schemas/cache//bundled/` に置き、Python と Go の SDK は独自の慣例を使います。パスをハードコードせず、SDK のローダーに見つけさせてください。いったん見つかれば、各スキーマは `/-{request,response}.json` にあります。 ## Idempotency: replay vs. new operation すべての変更ツールは `idempotency_key`(UUID)を必要とします。 * **リトライで同じキー** → サーバーは**同じレスポンス**をバイト単位でリプレイする。トランスポートレベルのリトライ(タイムアウト、5xx、切断された接続)にこれを使う。 * **新しいキー** → ボディにかかわらず**新しい操作**。前回の試行が失敗したという理由で新しい UUID を生成することは、素朴な呼び出し元が重複したメディアバイを作る最も一般的な方法です。 * **同じキー、異なる正準ボディ** → `IDEMPOTENCY_CONFLICT`。セラーは拒否しなければならない(MUST)([security.mdx#冪等性](/docs/building/by-layer/L1/security#冪等性) のルール 5) — 2 番目のボディを黙って適用せず、最初のレスポンスを黙ってリプレイしないでください。 * **最初のリクエストがまだ実行中に同じキー** → `IDEMPOTENCY_IN_FLIGHT`([security.mdx#冪等性](/docs/building/by-layer/L1/security#冪等性) のルール 9)。セラーはブロックする代わりに `error.details.retry_after` 付きでこのコードを返してもよい(MAY)。**同じキー**で待って再試行する — このコードで新しいキーを鋳造すると、安全なリトライが二重実行レースになります。 非同期フローでは、リプレイされたレスポンスは**同じ `task_id`** を運ぶため、フォークするのではなく同じタスクに対してポーリングが続きます。 `idempotency_key` は次で必須です: `create_media_buy`、`update_media_buy`、`sync_creatives`、`sync_audiences`、`sync_accounts`、`sync_catalogs`、`sync_event_sources`、`sync_plans`、`sync_governance`、`activate_signal`、`acquire_rights`、`log_event`、`report_usage`、`provide_performance_feedback`、`report_plan_outcome`、`create_property_list`、`update_property_list`、`delete_property_list`、`create_collection_list`、`update_collection_list`、`delete_collection_list`、`create_content_standards`、`update_content_standards`、`calibrate_content`、`si_initiate_session`、`si_send_message`。 キーの欠落 → `issues` に `/idempotency_key` を伴う `adcp_error.code: 'VALIDATION_ERROR'`。 ## `account` is `oneOf` — pick exactly one variant `account` は判別共用体です。`create_media_buy` と `update_media_buy` では 2 つのバリアント: ```json theme={null} // variant 0: セラー割り当て id による(list_accounts または帯域外オンボーディングから。 // バイヤー宣言セラーは sync_accounts の account_id を内部ハンドルとしてエコーすることもある) "account": { "account_id": "seller_assigned_id" } // variant 1: 自然キーによる(brand + operator、任意の sandbox) // brand.domain — バイヤーのブランドドメイン(例: 広告主のウェブサイト) // operator — ブランドに代わって動作するバイヤー側のエンティティ "account": { "brand": { "domain": "acme.com" }, "operator": "pinnacle-media.com" } ``` **バリアントをまたいで必須フィールドをマージしないでください。** 各バリアントの `additionalProperties: false` は、`{account_id, brand}` が**両方**で失敗することを意味します。 タスクスキーマが `account` を必須とするとき、SDK が認証済み認証情報で利用可能な唯一のアカウントを自動選択していても、明示的な `AccountRef` を送ってください。隠れた認証情報由来のデフォルト化はプロトコルモデルではありません。タスクが `account` を任意とマークするとき、省略はそのタスクが文書化したセマンティクスのみを持ちます。 他のツール(例: `sync_creatives`)はスーパーセットを受け入れることがあります — 常に特定のツールのスキーマを確認してください。 ## Async responses: `status: 'submitted'` means queued 変更ツールは 3 つの形状の 1 つを返せます: ```json theme={null} // 成功(同期): 作業は完了 { "media_buy_id": "mb_123", "packages": [...], "confirmed_at": "..." } // Submitted(非同期): 作業はキューイングされた { "status": "submitted", "task_id": "tk_abc", "message": "Awaiting IO signature" } // エラー: 修正せずにリトライしない { "errors": [{ "code": "PRODUCT_NOT_FOUND", "message": "..." }] } ``` AdCP タスク状態は**アプリケーション層**のコントラクトです。MCP と A2A は AdCP レスポンスをラップ、ストリーム、またはトランスポートできますが、それらのネイティブなタスクメカニズムは AdCP の `task_id`、ステータス値、webhook ペイロード、ポーリング/再照合サーフェスを置き換えません。トランスポートタスクは、ペイロードがまだ `status: 'submitted'` と言う AdCP レスポンスを配信した後に完了できます。 `status: 'submitted'` を見たとき、作業は完了して**いません**。3.x では、返された `task_id` を使ってレガシー AdCP `tasks/get` サーフェス経由でポーリングします。セラーは衝突しない `get_task_status` エイリアスもアドバタイズしてもよく(MAY)、呼び出し元はそれがディスカバリーに現れたときそのエイリアスを使ってもよい(MAY)。両方の AdCP ポーリング名は、マルチアカウント認証情報用の任意の `account` スコープを含め、同じ snake\_case ペイロード形状を使います。どちらの AdCP ポーリング形状も、トランスポート独自のタスクワイヤー形状を使うトランスポートネイティブの MCP/A2A `tasks/get` と混同しないでください。 ポーリング時に `include_result: true` を渡すと、ステータスが `completed` に遷移したときにセラーが完了ペイロードを含めます: ```json theme={null} // tasks/get リクエスト(任意の get_task_status エイリアスと同じペイロード) { "task_id": "task_456", "include_result": true, "account": { "brand": { "domain": "acmeoutdoor.example" }, "operator": "pinnacle-agency.example", "sandbox": true } } // tasks/get レスポンス — completed { "task_id": "task_456", "task_type": "create_media_buy", "protocol": "media-buy", "status": "completed", "completed_at": "2025-01-22T10:30:00Z", "result": { "media_buy_id": "mb_12345", "packages": [{ "package_id": "pkg_001" }] } } ``` `result` フィールドは、完了タスクのプッシュ通知 webhook の `result` フィールドと同じペイロード構造を使います — ポーリングと webhook の両方を設定するバイヤーは、どちらの経路でも同じデータ形状を受け取ります。 ## Error recovery — read `issues[]` すべての検証失敗は、次のような形状のエンベロープを生成します: ```json theme={null} { "adcp_error": { "code": "VALIDATION_ERROR", "recovery": "correctable", "field": "/first/offending/pointer", "issues": [ { "pointer": "/account", "keyword": "oneOf", "message": "must match exactly one schema in oneOf", "variants": [ { "index": 0, "required": ["account_id"], "properties": ["account_id"] }, { "index": 1, "required": ["brand", "operator"], "properties": ["brand", "operator", "sandbox"] } ] }, { "pointer": "/brand/domain", "keyword": "required", "message": "must have required property 'domain'" } ] } } ``` * `issues[].pointer` — 問題のあるフィールドへの RFC 6901 JSON Pointer * `issues[].keyword` — Ajv キーワード(`required`、`type`、`oneOf`、`anyOf`、`additionalProperties`、`format`、`enum`) * `issues[].variants` — `keyword` が `oneOf` または `anyOf` のとき、各エントリが 1 つのバリアントの `required` + 宣言された `properties` をリストする **`oneOf` 失敗については、`variants[]` から 1 つのバリアントを選び、その `required` フィールドのみを送ってください。** これは、フィールドが共用体だと知らなかったときの最速のリカバリーパスです。 `recovery` 値: * `correctable` — バイヤー側の修正。`issues[]` を読み、ポインターをパッチし、再送する * `transient` — **同じ** `idempotency_key` でリトライする * `terminal` — 人間の対応が必要(アカウント停止、支払い必要)。リトライしない ## Common shape pitfalls | Symptom | What it means | Fix | | -------------------------------------------------------- | -------------------------------------- | ------------------------------------------------------------------ | | `variants[]` を伴う `keyword: 'oneOf'` | 判別共用体 — 複数のバリアントからフィールドを送ったか、どれも送らなかった | `variants[]` から 1 つのバリアントを選ぶ。その `required` フィールドのみを送る。 | | 同じポインターで 2〜3 個の `additionalProperties` エラー | `oneOf` バリアントをマージした | 1 つのバリアントに落とす。「完全性のため」に「余分な」フィールドを保持しない。 | | `keyword: 'required'`、`pointer: '/idempotency_key'` | 変更ツール、UUID なし | 論理操作ごとに新しい UUID を生成。リトライで再利用。 | | `/budget` で `keyword: 'type'` または `additionalProperties` | `{amount, currency}` を送った | `budget` は数値。通貨は `pricing_option_id` によって暗示される。 | | `/format_id` で `additionalProperties`(文字列を渡した) | `"format_id": "video_..."` を送った | `format_id` は `{agent_url, id}` — 常にオブジェクト。 | | `/destinations/*/type` で `keyword: 'enum'` | 捏造した destination タイプ | `'platform'`(`platform` 付き)または `'agent'`(`agent_url` 付き)を使う。 | | レスポンスが `status: 'submitted'` と `task_id` を運ぶ | 非同期 — 作業はキューイングされ、完了して**いない** | レガシー `tasks/get`、またはセラーがエイリアスをアドバタイズするとき `get_task_status` でポーリング。 | ## Transport notes * **MCP**: `{ name: 'tool_name', arguments: {...} }` を伴う `tools/call`。型付きレスポンスは `structuredContent` を読む。 * **A2A**: `{ skill: 'tool_name', input: {...} }` の形状の `DataPart` を伴う `message/send`。型付きレスポンスは `task.artifacts[0].parts[0].data` にある。 両トランスポートは冪等性、エラー形状、スキーマ強制、ハンドラーセマンティクスを共有します。ある呼び出しが一方で機能するなら、同等の呼び出しは他方でも機能します。 よくある罠: **A2A の `Task.state: 'completed'` は AdCP の完了と同じではありません。** A2A タスク状態はトランスポート呼び出しのライフサイクルを記述します。AdCP レベルの完了はアーティファクトのペイロード(`structuredContent.status` または `data.status`)にあります。`completed` の A2A タスクでも `submitted` の AdCP レスポンスを運べます。 ## Related * タスクごとのリクエスト/レスポンス形状: プロトコル固有のリファレンス(`/docs/media-buy/`、`/docs/creative/`、`/docs/signals/` など)を参照。 * [プロトコルアーキテクチャ](/docs/protocol/architecture) — プロトコルドメインがどう組み合わさるか。 * [必須タスク](/docs/protocol/required-tasks) — 専門分野を主張するためにエージェントが実装しなければならないタスク。 * [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) — 任意の新しいエージェントに対する最初の呼び出し。 * [Schemas](/docs/building/by-layer/L0/schemas) — SDK がプロトコル tarball(現在 `skills/` をバンドル)をどう消費するか。 * [Build a caller](/docs/building/by-layer/L4/build-a-caller) — 呼び出し元側のビルド形式ガイド: インストール、呼び出し、レスポンス処理、レポート取り込み。 # ケイパビリティエクスプローラー Source: https://adcp-docs-ja.pier1.co.jp/docs/protocol/capabilities-explorer get_adcp_capabilities レスポンススキーマの参照可能なビュー — すべてのトップレベルドメイン、すべてのサブ名前空間を、アンカーと「ここに拡張を提案」リンク付きで示し、新しいフラグが正しいホームに着地するようにする。 # ケイパビリティエクスプローラー このページは、[`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) レスポンスの実際のトップレベル形状をレンダリングします。新しいケイパビリティを提案する前に、その正しい場所を見つけられるようにするためです。拡張 RFC がはね返される最も一般的な理由は形状の誤りです: 提案が、類似のフラグがすでに存在する場所と一致しないレベルにフラグを置くことです。まずツリーをたどってください。 RFC を起草しにここに来たなら、ワークフローはこうです: 1. 下から、あなたのフラグに最も近い既存のトップレベルドメインを見つける。 2. そのドメインのサブ名前空間(`features`、`execution` など)に掘り下げる。 3. あなたのフラグが既存のサブ名前空間に適合するなら、そこに提案する。該当ノードの **propose extension here** リンクを使う — issue タイトルにパスが事前入力される。 4. 何も適合しないなら、末尾の **Before proposing a new top-level key** までスクロールし、RFC 本文でゲート質問に答える。 権威あるスキーマは [`static/schemas/source/protocol/get-adcp-capabilities-response.json`](https://github.com/adcontextprotocol/adcp/blob/main/static/schemas/source/protocol/get-adcp-capabilities-response.json) にあります。下のツリーは、リッチなドメインについて 1 レベル深さにキュレーションされています。完全なネスト形状についてはスキーマを読んでください。[設計原則 — Capabilities are commitments, declared under existing buckets](/docs/protocol/design-principles#4-capabilities-are-commitments-declared-under-existing-buckets) も参照。 *** ## トップレベルドメイン 下の 14 のドメインが `get_adcp_capabilities` のトップレベルサーフェス全体です。すべてのケイパビリティフラグは、最終的にこれらの 1 つの下にネストします。新しいトップレベルキーは極めてまれで、最初の一手であるべきではありません — このページ末尾のゲート質問を参照。 ### `adcp` — コアプロトコルアイデンティティ バージョンネゴシエーション、冪等性コントラクト、ビルド識別子。 * **`supported_versions`** — リリース精度の文字列(例: `"3.0"`、`"3.1"`)。バイヤー側のリリースピン留めについて権威的。 * **`major_versions`** — `supported_versions` を優先して非推奨。セラーは 3.x を通じて発し続けなければならない(MUST)。 * **`build_version`** — 完全な semver ビルド識別子。バイヤー側のインシデントトリアージ用の助言的メタデータ。 * **`idempotency`** — リプレイセマンティクスを宣言する判別共用体(`IdempotencySupported` / `IdempotencyUnsupported`)。**ここでの宣言はコミットメント** — `supported: true` を宣言するセラーは適合性ランナーにプローブされる。 [`adcp` への拡張を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Extend+adcp+capability:+%3Cflag%3E\&body=Schema+location:+%60.adcp%60%0AExisting+keys:+supported_versions,+major_versions,+build_version,+idempotency%0A%0A%23%23+Proposal%0A%0A...%0A%0A%23%23+Why+this+belongs+under+%60adcp%60+and+not+a+new+top-level+key%0A%0A...\&labels=rfc,capabilities) *** ### `supported_protocols` — このエージェントが実装する AdCP プロトコル プロトコル名の配列。各値はエージェントを (a) それらのツールの実装、かつ (b) `/compliance/{version}/protocols/{protocol}/` のベースラインコンプライアンスストーリーボードの合格にコミットします。 有効な値は `media_buy`、`creative`、`signals`、`governance`、`sponsored_intelligence`、`brand`、`accounts`、`measurement`(開発中)をカバーします。 [`supported_protocols` に追加する新しいプロトコルを提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Add+protocol+to+supported_protocols:+%3Cname%3E\&body=%23%23+Proposal%0A%0A...%0A%0A%23%23+Compliance+storyboard+plan%0A%0AHow+will+the+baseline+conformance+suite+exercise+this+protocol?+...\&labels=rfc,capabilities,new-protocol) *** ### `account` — アカウント確立と課金 アカウントがどう交渉されるか、プロダクトディスカバリーの前に必要か、どの課金モデルがサポートされるか。 * **`required_for_products`** — boolean。true のとき、`get_products` は確立されたアカウントを必要とする。 * **`authorization_endpoint`** — アカウント交渉用の OAuth/auth URL。 * **`require_operator_auth`** — オペレーターレベルの認証が必要かを宣言する。 * **`supported_billing`** — 課金モデルの配列(例: `prepaid`、`monthly_invoice`)。 * **`account_financials`** — セラーが公開する財務データ(クレジット上限、現在残高など)。 * **`sandbox`** — サンドボックスアカウントのケイパビリティ。 [`account` への拡張を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Extend+account+capability:+%3Cflag%3E\&body=Schema+location:+%60.account%60\&labels=rfc,capabilities) *** ### `media_buy` — メディア購入のケイパビリティ 最大のドメイン。サブ名前空間が、ほとんどのメディア購入フラグが属する場所です。 * **`features`** — boolean 機能フラグ(例: `inline_creative_management`、`property_list_filtering`、`catalog_management`、`committed_metrics_supported`)。**新しいメディア購入ケイパビリティフラグのほとんどはここに属する。** * **`execution`** — 技術的実行ケイパビリティ。`trusted_match`(TMP)、`creative_specs`(VAST/MRAID/VPAID/SIMID バージョン)、`targeting`(geo / audience / device / temporal)、`axe_integrations`(非推奨)を含む。`trusted_match` の配置については [設計原則 — Where the surface doesn't yet follow these](/docs/protocol/design-principles#where-the-surface-doesnt-yet-follow-these-principles) のノートを参照。 * **`audience_targeting`** — 宣言されたオーディエンスターゲティングケイパビリティ。 * **`content_standards`** — コンテンツ標準の強制ケイパビリティ。 * **`conversion_tracking`** — コンバージョントラッキングケイパビリティ。 * **`offline_delivery_protocols`** — サポートするオフライン配信プロトコル(放送トラフィッキングなど)。 * **`portfolio`** — ポートフォリオ管理ケイパビリティ。 * **`reporting_delivery_methods`** — レポートがどう配信されるか。 * **`supported_pricing_models`** — 配列(CPM、CPC、CPCV、CPP、fixed など)。 [`media_buy.features` にフラグを提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Add+media_buy+feature:+%3Cflag%3E\&body=Schema+location:+%60.media_buy.features%60%0A%0A%23%23+Proposal%0A%0A...%0A%0A%23%23+Why+a+feature+flag+and+not+a+new+task%0A%0A...%0A%0A%23%23+Conformance+probe%0A%0AHow+can+a+buyer+verify+this+capability+is+actually+honored?+...\&labels=rfc,capabilities) [`media_buy.execution` への拡張を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Extend+media_buy.execution:+%3Cflag%3E\&body=Schema+location:+%60.media_buy.execution%60%0AExisting+sub-keys:+trusted_match,+creative_specs,+targeting\&labels=rfc,capabilities) *** ### `signals` — オーディエンスとコンテキストデータのアクティベーション signals ドメインの認可スコープと機能フラグ。 * **`data_provider_domains`** — このシグナルエージェントが再販を認可されているドメインの配列。バイヤーは検証のため各プロバイダーの `adagents.json` を取得する。 * **`features`** — boolean 機能フラグ。`catalog_signals` は非推奨。構造化 `signal_ref` サポートは Signals プロトコルの一部であり、機能フラグを必要とすべきでない。**追加のシグナルケイパビリティフラグはここに属し**、新しいトップレベルキーには属さない。(例: 直接販売ターゲティング用の `signal_enforcement_on_guaranteed` フラグは、`media_buy.execution.trusted_match` の下ではなく `signals.features` に属する。) [`signals.features` にフラグを提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Add+signals+feature:+%3Cflag%3E\&body=Schema+location:+%60.signals.features%60%0A%0A%23%23+Proposal%0A%0A...%0A%0A%23%23+Conformance+probe%0A%0A...\&labels=rfc,capabilities,signals) *** ### `governance` — ガバナンスプロトコルのケイパビリティ プロパティとクリエイティブのガバナンスケイパビリティ。 * **`property_features`** — ガバナンスエージェントがプロパティリストで何をするか。 * **`creative_features`** — ガバナンスエージェントがクリエイティブレビューのために何をするか。 * **`aggregation_window_days`** — エージェントがガバナンスイベントを集約する期間。 [`governance` への拡張を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Extend+governance+capability:+%3Cflag%3E\&body=Schema+location:+%60.governance%60\&labels=rfc,capabilities,governance) *** ### `sponsored_intelligence` — 会話的なブランド体験 SI セッションを扱うエージェント向け。 * **`endpoint`** — SI エンドポイント URL。 * **`brand_url`** — ブランドアイデンティティ URL。 * **`capabilities`** — SI 固有のケイパビリティ(コマースハンドオフ、音声、UI コンポーネントなど)。 [`sponsored_intelligence` への拡張を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Extend+sponsored_intelligence+capability:+%3Cflag%3E\&body=Schema+location:+%60.sponsored_intelligence%60\&labels=rfc,capabilities) *** ### `brand` — ブランドプロトコルのケイパビリティ ブランドエージェント向け。 * **`description`** — エージェントの説明。 * **`available_uses`** — ブランドデータが何にライセンスされているか。 * **`generation_providers`** — サポートする生成プロバイダー。 * **`right_types`** — エージェントが付与する権利タイプ。 * **`rights`** — このエージェントが発行する具体的な権利。 [`brand` への拡張を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Extend+brand+capability:+%3Cflag%3E\&body=Schema+location:+%60.brand%60\&labels=rfc,capabilities) *** ### `creative` — クリエイティブプロトコルのケイパビリティ クリエイティブエージェント向け。 * **`has_creative_library`** — エージェントがクリエイティブライブラリを維持するか。 * **`supports_compliance`** — クリエイティブコンプライアンススキャン。 * **`supports_generation`** — 生成クリエイティブケイパビリティ。 * **`supports_transformation`** — クリエイティブ変換ケイパビリティ。 [`creative` への拡張を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Extend+creative+capability:+%3Cflag%3E\&body=Schema+location:+%60.creative%60\&labels=rfc,capabilities,creative) *** ### `request_signing` — インバウンドリクエスト用の RFC 9421 HTTP Signatures 3.0 では任意。ケイパビリティとしてアドバタイズされ、当事者が選択的に署名にオプトインできる。 * **`supported`** — boolean。 * **`required_for`** — 署名が必須の操作の配列。 * **`supported_for`** — 署名がサポートされる操作の配列。 * **`warn_for`** — 未署名リクエストが警告を生む操作の配列。 * **`covers_content_digest`** — 署名がリクエストボディのダイジェストをカバーするか。 [`request_signing` への拡張を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Extend+request_signing+capability:+%3Cflag%3E\&body=Schema+location:+%60.request_signing%60\&labels=rfc,capabilities,security) *** ### `webhook_signing` — アウトバウンド webhook 用の RFC 9421 署名 `request_signing` のトップレベルの対等物。 * **`supported`** — boolean。 * **`profile`** — 署名プロファイル名。 * **`algorithms`** — サポートする署名アルゴリズム。 * **`legacy_hmac_fallback`** — レガシー受信者に対して HMAC フォールバックがサポートされるか。 [`webhook_signing` への拡張を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Extend+webhook_signing+capability:+%3Cflag%3E\&body=Schema+location:+%60.webhook_signing%60\&labels=rfc,capabilities,security) *** ### `identity` — オペレーターアイデンティティ姿勢 鍵スコーピングと侵害対応の制御。3.x では助言的、4.0 で必須。 * **`per_principal_key_isolation`** — 各プリンシパルが分離された鍵を持つか。 * **`key_origins`** — 宣言された鍵管理の由来。 * **`compromise_notification`** — 鍵侵害イベントの通知姿勢。 [`identity` への拡張を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Extend+identity+capability:+%3Cflag%3E\&body=Schema+location:+%60.identity%60\&labels=rfc,capabilities,security) *** ### `measurement` — 測定ケイパビリティ(開発中) 広告配信、エクスポージャー、または効果についての定量的メトリクス。 * **`metrics`** — このエージェントが発するメトリクス定義の配列。 ケイパビリティディスカバリーを超えたプロトコルサーフェス(レポート、アトリビューションタスク)は後続のマイナーに着地します。 [`measurement` への拡張を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Extend+measurement+capability:+%3Cflag%3E\&body=Schema+location:+%60.measurement%60\&labels=rfc,capabilities,measurement) *** ### `compliance_testing` — 決定的テストシナリオ エージェントが `comply_test_controller` をサポートすること、およびどのシナリオが尊重されるかを宣言します。 * **`scenarios`** — エージェントがサポートするコンプライアンスシナリオ ID の配列。 [`compliance_testing` への拡張を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Extend+compliance_testing+capability:+%3Cflag%3E\&body=Schema+location:+%60.compliance_testing%60\&labels=rfc,capabilities,compliance) *** ### `specialisms` — kebab-case の専門分野 ID 任意。専門分野のコンプライアンスクレーム(例: `creative-generative`、`sales-non-guaranteed`)。値はワーキンググループに登録された kebab-case enum ID です。 [新しい専門分野を提案する](https://github.com/adcontextprotocol/adcp/issues/new?title=Add+specialism:+%3Ckebab-case-id%3E\&body=%23%23+Specialism+ID%0A%0A%3Ckebab-case-id%3E%0A%0A%23%23+What+it+claims%0A%0A...%0A%0A%23%23+Conformance+criteria%0A%0AHow+do+we+verify+an+agent+actually+meets+this+claim?+...\&labels=rfc,capabilities,specialism) *** ## ドメインでない「このエージェントがすること」 3 つのトップレベルリストが、単一のプロトコルドメインに適合しないケイパビリティメタデータを運びます。それらのあいだの形状の不一致は未解決リストにあります([設計原則 — Where the surface doesn't yet follow these](/docs/protocol/design-principles#where-the-surface-doesnt-yet-follow-these-principles))。 * **`extensions_supported`** — このエージェントが埋める拡張名前空間の配列(`ext.{namespace}`)。 * **`experimental_features`** — このエージェントが実装する実験的サーフェス ID の配列(例: `trusted_match.core`)。 * **`compliance_testing`** — 上でカバー済み。 *** ## 新しいトップレベルキーを提案する前に あなたのフラグが上の 14 のドメインのどれにも本当に適合しないなら、RFC を開いてください — ただし本文でこれらのゲート質問に答えてください。ほとんどのレビュアーはそれらを期待します。それらを無視する RFC ははね返される傾向があります。 1. **最も近い既存のトップレベルドメインはどれか?** それを名指しする。なぜあなたのフラグがそのドメインの `features`、`execution`、その他のサブ名前空間への拡張*でない*かを説明する。 2. **なぜこれが `ext.{vendor}` 拡張でないのか?** ベンダー固有の振る舞いはベンダー名前空間に属する([プラットフォーム非依存性についての spec-guidelines](/docs/spec-guidelines#platform-agnosticism))。なぜあなたのフラグがすべての実装者にわたって規範的なのか? 3. **適合性プローブは何か?** ケイパビリティ宣言はアドバタイズメントではなくコミットメントです。あなたのフラグを宣言するセラーが実際にそれを尊重することを、適合性ランナーはどう検証するか? 4. **これは何を排除するか?** このトップレベルキーを追加することでどの提案が容易になり — 1 年後にトップレベルをスキャンする人にとって何が難しくなるか? これらはレビュアーが問う質問と同じです。RFC でそれらに答えることで往復を省けます。 [新しいトップレベルケイパビリティキーを提案する(ゲート質問に答えた後にのみ使用)](https://github.com/adcontextprotocol/adcp/issues/new?title=Propose+new+top-level+capability+key:+%3Cname%3E\&body=%23%23+Proposed+top-level+key%0A%0A%3Cname%3E%0A%0A%23%23+Gate+questions%0A%0A**1.+Which+existing+top-level+domain+is+closest+and+why+isn%27t+this+an+extension+there?**%0A%0A...%0A%0A**2.+Why+isn%27t+this+an+%60ext.%7Bvendor%7D%60+extension?**%0A%0A...%0A%0A**3.+What%27s+the+conformance+probe?**%0A%0A...%0A%0A**4.+What+does+this+rule+out?**%0A%0A...\&labels=rfc,capabilities,new-top-level-key) *** ## 関連 * [設計原則](/docs/protocol/design-principles) — ケイパビリティサーフェス形状の背後にある理由付け。 * [`get_adcp_capabilities` タスクリファレンス](/docs/protocol/get_adcp_capabilities) — フィールドごとに文書化されたレスポンススキーマ。 * [仕様ガイドライン](/docs/spec-guidelines) — 型の命名、enum 設計、ベンダー中立ルール。 # AdCP の設計思想 Source: https://adcp-docs-ja.pier1.co.jp/docs/protocol/design-principles AdCP の設計を支える中核原則 — それらが何を排除するのか、どんなときに反論するのが正しいのか、そしてサーフェスがまだ原則に従っていない箇所はどこか。 # AdCP の設計思想 AdCP を*使う*ためにこのページを読む必要はありません。AdCP を*拡張する*なら読む必要があります。 これはメタプロトコル — 私たちが下した判断の背後にある哲学的フレームワークです。各原則は、誰かが「このフィールドを 1 つ足すだけ」と提案した瞬間に立ち現れる、負荷を担う決定です。私たちはこれらの判断を意図的に下し、意図的に見直します。ここでの目標は、コントリビューターに十分な文脈を与え、既存の形状に適合する提案をするか、あるいは両足を地につけて形状そのものを変えることを論じられるようにすることです。 ワーキンググループに届く RFC の大半をはね返すのは 2 つの原則です: **スキーマが仕様である**、そして **タスクを追加する前に合成する**。これらが残りを支えるため、最初に置かれています。続いて 6 つの補助原則が並びます。末尾の「サーフェスがまだこれらに従っていない箇所」セクションは、私たちが認識している矛盾を挙げます — 原則は、それらについて正直である限りにおいてのみ信頼に足るからです。 各原則は同じ構造を持ちます: ルール、それを選んだ理由、それが排除するもの、そして例外パス — 原則に反論することが正しい一手となるとき。 *** ## The two principles that bounce most RFCs ### 1. The schema is the spec ドキュメントは記述し、スキーマは決定します。ドキュメントとスキーマが食い違ったとき、スキーマが勝ちます。スキーマに存在しないフィールドやタスクを拡張する提案は、2 つの主張を同時にしています — あるものが存在するという主張と、それを成長させるべきだという主張です。レビュアーは最初の主張だけではね返します。 **なぜ選んだか。** 生成される SDK はスキーマから来ます。適合性テストはスキーマから来ます。レジストリはスキーマから来ます。ドキュメントページは 1 リリース遅れても、プロトコルは依然として動きます。しかしスキーマの変更はあらゆる場所に出荷されます。スキーマはまた、AdCP が主張するクロス言語保証(TypeScript、Python、Go がすべて 1 つのソースからクリーンに生成される)を強制できる唯一の場所です。このルールの正準版については [仕様ガイドライン — 哲学](/docs/spec-guidelines#philosophy) を参照。 **これが排除するもの。** [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) の `trusted_match` ケイパビリティオブジェクト内に `direct_sold_signals` フラグを提案すること — スキーマの実際のトップレベルキーが `adcp`、`supported_protocols`、`account`、`media_buy`、`signals`、`governance`、`sponsored_intelligence`、`brand`、`creative`、`request_signing`、`webhook_signing`、`identity`、`compliance_testing`、`specialisms` であるとき。`trusted_match` というトップレベルキーは存在せず、提案されたフラグの正しい形状は新しいバケットではなく `signals.features.signal_enforcement_on_guaranteed` です。レビュアーは、その根底にあるアイデア(完全に良いかもしれない)を評価する前に、形状が誤っているという前提だけでその issue をはね返します。 **反論が正しいとき。** スキーマに提案を置く場所が本当に欠けているとき。それを最初の主張として述べ、パス付きで新しいスキーマの位置を提案し、それからフィールドを提案してください。2 つの明確な主張は 1 つの曖昧な主張に勝ります。 → 新しいフラグを提案する前に [ケイパビリティエクスプローラー](/docs/protocol/capabilities-explorer) を使ってください。実際のスキーマツリーをたどれます。 *** ### 2. Compose with existing primitives before adding a new task すべての新しいトップレベルタスクは、それを使わない実装者でさえいずれ推論しなければならない恒久的なサーフェスです。提案する前に答えるべき問いはこうです: これは既存のタスクとその既存のモードで表現できるか? できるなら、その提案は新しいタスクではなくドキュメントのギャップ(または欠けているフィールド)です。 **なぜ選んだか。** タスクはプロトコルの協調コストです。タスクの追加はコントリビューターが求められる最も高価なことです。なぜなら、それはすべてのセールスエージェント、すべてのバイヤーエージェント、すべての SDK、すべてのテストスイート、すべての適合性行に降りかかるからです。フィールドとモードは合成しますが、タスクはしません。リリースごとにタスクを追加して成長するプロトコルは、作者だけが頭の中に保持できるものへと老いていきます。 **これが排除するもの。** [`get_products`](/docs/media-buy/task-reference/get_products) と [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) の間に `get_price_quote` タスクを提案すること — バイヤーがターゲティングを送信しセラーが確定レートを返す、設定-価格-見積もりのステップ。その提案が求めるものの大半はすでに存在します: `account` によるアカウントスコープのレートカード、`pricing_options` による確定価格、コミット時の `pricing_option_id` ロック、反復ループのための `buying_mode: "refine"`。「新しいタスク」というフレーミングは、ターゲティングと価格がディスカバリーから欠けていると仮定しますが、欠けていません。 **反論が正しいとき。** どんな合成も望むセマンティクスに到達しないとき — 提案が、既存のどのタスクも運べない状態遷移や当事者ロールを要求するとき。ハードルは高く、それは意図的です。証拠を持ってきてください: どの既存タスクを拡張しようとしたか、なぜその拡張が機能しなかったか、拡張ではできない何を新しいタスクが加えるか。 *** ## Six supporting principles ### 3. The brief drives discovery; targeting is an input, not a step ターゲティングはバイヤーが望むものです。それは汎用カタログに対する下流のフィルターではなく、セラーがそれに*対して*キュレーションする対象です。バイヤーはブリーフ(または `wholesale + filters`)を [`get_products`](/docs/media-buy/task-reference/get_products) に送り、セラーはその意図に合わせてすでに形作られたプロダクトを、`account` パラメーター経由でバイヤーのアカウントにスコープされた価格とともに返します。反復は型付き変更配列を伴う `buying_mode: "refine"` を通じて起こります — 往復はディスカバリーに折り込まれ、後付けされません。 **なぜ選んだか。** ブリーフはバイヤーの意図の自然な単位です。パブリッシャーは、いかなる外部タクソノミーよりも自分のインベントリ、オーディエンス、レートカードを知っています。ブリーフに対してキュレーションすることで、セラーはそのすべてを 1 回の往復でレスポンスに持ち込め、その往復が価格も生み出します。ターゲティングと価格を 2 つのタスクに分割することは、1 つの専門家の判断を 2 つの過少仕様な判断に変えます。 **これが排除するもの。** プロダクトとバイ作成の間の別個の見積もりステップ。(原則 2 を参照 — 同じ RFC が両方のフィルターで落ちます。) **反論が正しいとき。** 価格が、バイヤーがまだコミットしていないフライト日程と総予算に依存するとき — 季節性、ボリュームティア、売り切り率駆動のイールド。あるいはセラーが、示唆的な価格オプションとは別に、コミットメント前に時間制限付きの確定レート(`valid_until`)を発行したいとき。あるいはバイヤーが価格を駆動したものの監査可能な説明(`rate_basis` フィールド)を必要とするとき。これら 3 つは本物のギャップであり、新しいタスクではなく `pricing_options` とディスカバリーフローへの拡張です。 → ブリーフファーストのモデルについては [ターゲティング](/docs/media-buy/advanced-topics/targeting) を参照。 *** ### 4. Capabilities are commitments, declared under existing buckets 2 つのこと、1 つの原則。**ケイパビリティがスキーマ内のどこに存在するか**: 新しいトップレベルキーではなく、既存のトップレベルドメイン(`signals`、`media_buy`、`creative`、`governance`)の下。**ケイパビリティを宣言することが何を意味するか**: アドバタイズメントではなく強制可能なコントラクト。セールスエージェントが `idempotency.supported: true` を宣言したら、適合性スイートがそれをプローブし、コントラクトはテスト可能です。宣言には作るコストがかかります。 **なぜ選んだか。** トップレベルキーはケイパビリティサーフェスの最も可視的な部分です。それぞれが、すべての読み取りですべての実装者がスキャンするカテゴリーです。サブ名前空間化は関連するケイパビリティを一緒に発見可能に保ち(`signals.features.X` は `signals.data_provider_domains` の近くに属する)、トップレベルをスキャン可能に保ちます。1 つのフラグのためにトップレベルキーを追加することは、1 枚の書式を扱うために部署を追加するようなものです。そして宣言は、有用であるために負荷を担う必要があります: プロトコルが強制もテストもできないフラグは、コントラクトではなく願望です。 **これが排除するもの。** シグナルが保証ラインアイテムとどう相互作用するかについてのフラグのために「`trusted_match` トップレベルケイパビリティキーを追加する」こと。そのフラグは `signals.features` に属します — 新しいバケットではなく `signal_enforcement_on_guaranteed: "enforced" | "best_effort" | "not_supported"` enum です。また: 誰もプローブしないことを願って、実際にはサポートしていないケイパビリティを宣言すること。適合性ランナーがプローブします。 **レビュアーテスト。** その提案は、今日スキーマにある実際のトップレベルキーを引用し、なぜそのどれも正しいホームでないかを説明したか? 提案がその問いに答えられないなら、まだ準備ができていません。 → [ケイパビリティエクスプローラー](/docs/protocol/capabilities-explorer) は既存のツリーをレンダリングします。 *** ### 5. Trust is bilateral and `/.well-known`-rooted AdCP における信頼は双方向かつ検証可能であり、レジストリによって承認されるものではありません。オペレーターは組織アイデンティティ、エージェント、プロパティ関係、署名鍵ディスカバリーを `/.well-known/brand.json` の [`brand.json`](/docs/brand-protocol/brand-json) 経由で宣言します。パブリッシャーはどのエージェントがどのプロパティに認可されているかを [`adagents.json`](/docs/governance/property/adagents) 経由で宣言します。いずれの当事者も相手の宣言を解決し検証できます。[Registry](/docs/registry) はディスカバリーと解決を助けますが、参加をゲートしません。すべてのディスカバリーホップは決定的なパスに対する HTTP GET です — ads.txt と sellers.json が正しく捉えたモデルを、意図的に再利用しています。 **なぜ選んだか。** すべての中央集権的な信頼権威は、いずれ税になります。ads.txt と sellers.json はモデルを正しく捉えました: 宣言は公開かつ機械可読で、検証は双方向で、ネットワークは誰が数えられるかを決める単一のゲートキーパーなしに真実を発見します。AdCP は意図的にそのパターンに従います — 「well-known ブランドレジストリ」や「検証済みバイヤー」ティアを追加することは、プロトコルが置き換えようとしているまさにその ad tech 税構造を再創造します。 **これが排除するもの。** ブランド検証を中央集権的な検証問題としてフレーミングする提案 — 「どのブランドが well-known かを誰が決めるのか?」「AAO はレジストリに入れる前に brand.json 提出を検証すべきか?」。前提が誤りです: `/.well-known/` は [RFC 8615 の URI 慣例](https://datatracker.ietf.org/doc/html/rfc8615) であり、品質シグナルではありません。あるドメインの `brand.json` は、そのドメインを制御する者がそれを公開したことだけを証明します。信頼は、その証明を adagents.json、アカウントレベルの商業的関係、(必要なとき)署名付きリクエストと合成することで構築されます — 第三者がブランドリストを承認することによってではありません。 **反論が正しいとき。** 双方向宣言が*実証的に扱えない*信頼の失敗の証拠を持っているとき — 「詐欺師が自分を宣言したらどうする」ではなく、「双方向モデルが見逃す詐欺のクラスがここにあり、そのコストがここにあり、それを閉じる最小の中央集権化がここにある」。信頼の拡張は受け入れられますが、信頼の中央集権化には領収書が必要です。基盤についての未解決の問い(CDN 乗っ取り、DNS のゲーム、古い `/.well-known/` クロール)は本物です — AdCP が提供するものと明示的に提供しないものについては [Trust & Security](/docs/trust) を参照。 *** ### 6. Privacy is layered, not uniform [Trusted Match Protocol](/docs/trusted-match) は*構造的*プライバシーを持ちます — 分離されたコードパス、アイデンティティとコンテキストの結合に対するスキーマ上の禁止、独立に検証可能な相関除去。他のすべてのドメインは*契約的*プライバシーを持ちます — データを交換する当事者はアカウントの条件やユーザーの同意に拘束されます。これらは異なる保証を持つ異なるメカニズムであり、それらを混同する提案は混乱した機能を生みます。 **なぜ選んだか。** 構造的プライバシーは高価です — スキーマを制約し、分離されたインフラを要求し、可能な合成を制限します。私たちはそのコストを TMP で支払います。なぜなら TMP は、契約的機密性が届かない混合したバイヤー/セラー境界を越えて、インプレッション時に動作するからです。TMP の外では、当事者はすでに商業的関係を確立しており、契約が信頼の正しい単位です。この 2 つを交換可能であるかのように装うことは、契約的ケースを過剰設計するか、構造的ケースを過少保護するかのいずれかを意味します。 **これが排除するもの。** TMP のプライバシー保証が直接販売のアドサーバー決定に適用されると仮定する「TMP 検証済み直接販売ターゲティング」ケイパビリティを提案すること。適用されません — TMP は GAM/FreeWheel の決定フックとして設計されておらず、「直接販売について TMP をサポートするアドサーバーはない」と言うのは真だがやや誤解を招きます。なぜならそれは TMP が動作する層ではないからです。正直なフレーミングは異なります: AdCP には、セラーが保証ラインアイテムにシグナルベースのターゲティングを強制できるかどうかを宣言するプロトコルレベルのメカニズムがありません。それは `signals.features` フラグと [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) 検証ルール — 構造的プライバシー機能ではなく契約的開示機能です。 **反論が正しいとき。** クロスドメインのプライバシークレームを持ち、どのメカニズムが適用されるかを名指しできるとき。「これは契約的機能で、契約にこれが入っている」または「これは構造的機能で、それを機能させるスキーマレベルの禁止がここにある」。曖昧なプライバシー改善はどちらにも着地せず、誤った場所に落ちがちです。 → ドメインごとのメカニズム表については [Architecture — Privacy posture across domains](/docs/protocol/architecture#privacy-posture-across-domains) を参照。 *** ### 7. The protocol exposes seams; deployers wire decisions AdCP は、どのフィールドが存在し、それらについてプロトコルがどんな保証をするかを定義します。デプロイヤーのポリシーがどう強制されなければならないか、ガバナンスエージェントがどう決定しなければならないか、何が「良い」バイヤーと見なされるかは定義しません。この姿勢は 3 箇所に現れ、それらは単一の姿勢を共有します: **プロトコルは、決定が実際にどう起こるかについて現実的である — 当事者間に分散し、時間的に非同期で、プロセスにおいて人間がチェック可能。** * *ケイパビリティは宣言され、ゲートされない。* `check_governance` はシームであり、強制者ではありません。ガバナンスエージェントを設定していないセラーはそれを呼びません。プロトコルは非準拠のセラーが取引するのを妨げません。スキーマレベルの強制は存在しますがまれで、名指しされています(3.0 の `fair_housing`、`fair_lending`、`fair_employment`)。デフォルトは強制ではなく露出です。 * *非同期がデフォルト。同期は最適化。* すべての変更タスクは `*-async-response-{submitted,working,input-required}.json` の兄弟を持ちます。すべての操作が同期的でアトミックであるかのように装うプロトコルは、実際のワークフローが到着した瞬間に壊れます。 * *人間のレビューは例外的ではなくアーキテクチャ的。* 任意の変更は [タスクライフサイクル](/docs/building/by-layer/L3/task-lifecycle) を通じて人間の承認のために非同期にできます。キャンペーンガバナンスは宣言的なバイヤー側のレビューチャネルを提供します。HITL は、特定のタスクにボルト留めされた特殊ケースであるのではなく、他のすべて — 監査ログ、ガバナンスチェック、非同期レスポンス — と合成します。 関連する不変条件がアカウント境界に存在します: **作成サーフェスではなくアカウント所有権が可視性を決定する。** AdCP の外で作成されたバイも、それが該当するアカウントに属し、アカウントスコープのクエリに現れます。これは、すべての「アドサーバー上の API」を壊すシャドウ台帳パターンを防ぎます。 **なぜ選んだか。** ポリシーを出荷するプロトコルは、余計なステップを持つプラットフォームです。オープン標準の信頼性は、まさに何を決定しようとするかについての抑制です。デプロイヤーのポリシーは、コンテキスト固有の判断が属する場所でもあります — あるバーティカルでブランドセーフティ違反であるものが、別のバーティカルでは問題ないことがあり、AdCP は側につくことなくそのニュアンスを運べません。 **これが排除するもの。** 「プロトコルはケイパビリティ X に一致しないバイを拒否すべき」。多くの場合、正しい答えは「プロトコルはケイパビリティ X を可視にすべきで、デプロイヤーがバイを拒否する」です。また: 自律性と監督を対立するものとしてフレーミングすること — 「エージェンティックが真に機能するには、X がエンドツーエンドで自動化されなければならない」。ほとんどの規制された高リスクの操作について、それは私たちが望むものではなく、プロトコルが仮定するものでもありません。 **反論が正しいとき。** 非対称性が十分にひどく、デプロイヤーごとのポリシーが協調問題を解決しないとき — バイヤーとセラーが、プロトコルレベルのルールなしに強制について経済的に合意できないとき。スキーマレベルの強制はまれで獲得されるものです。それを持っているときに論じてください、最初の一手としてではなく。 → AdCP が提供するものと明示的に提供しないものについては [Trust & Security — Governance](/docs/trust#governance) を、HITL がプロトコルにどう現れるかについては [Governance — Embedded human judgment](/docs/governance/embedded-human-judgment) を参照。 *** ### 8. Reference data: own the namespace and the resolution contract, not the corpus AdCP は **名前空間**(どの分類システムが存在し、その ID の形状は何か)と **解決コントラクト**(モデルの訓練データから識別子を推論するのではなく宣言されたソースに対して名前を解決し、1 つを選ぶのではなく曖昧さをサーフェスする)を定義します。基底のコード↔名前テーブル — **コーパス** — は、AdCP 自身が値を鋳造する権威でない限り、公開しません。コードはアイデンティティであり、ワイヤー上を無損失で移動します。名前はそのコードの*レンダリング*であり、それを表示する者が、すでに保持しているルックアップからエッジで再構成します。 **なぜ選んだか。** 耐久性のある資産はコーパスではなくコントラクトです。他者の参照テーブルを公開することは、レジストリを、外部パブリッシャーを永遠に追跡しなければならない下流のミラーにします — その正しさ、リフレッシュケイデンス、ライセンスエクスポージャーを、その背後に運用機能を一切持たずに所有することになります。そして誤っているが正準なテーブルは、テーブルがないよりも悪い: エージェントは幻覚を起こし、まさに信頼すべきでないときに権威の刻印を信頼するため、古いまたは捏造されたエントリは実際の支出を静かに誤誘導します。名前は移動する必要も中央集権化される必要もありません — それは次元属性であり、一度エッジに複製されてローカルで結合されるもので、すべてのリクエストに非正規化されるものでも、ルックアップごとに中央サービスから取得されるものでもありません。 **これが排除するもの。** 正準なジオメトロレジストリ — エコシステムが真実の源泉として取得するバージョン管理された DMA `code → name` テーブル(提案され、却下された)。その提案の最初のコミットは、市場ランクで順次番号付けされた捏造コードを出荷しました — ソース権威とこのリポジトリ自身の例の両方に矛盾し、人間のレビュアーだけがそれを捕まえました。自動化された権威チェックはないため、そのエラークラスは再発します。名前はまた、プロジェクトが再配布するライセンスを持たない、独占的で商標登録されたコーパスです。プロトコルはすでに読み取り方向を正しく扱っています: `geo-delivery-metrics` は必須の `geo_code` の隣に任意の `geo_name` を運ぶため、セラーはバイヤーに素のコードから名前を再構成させるのではなく、自身のカタログからレンダリング*できます*。それは反対の過剰修正も等しく排除します — 表示名を権威あるものとしてターゲティング値にステープル留めすること。「Albany」は 2 つの異なる DMA(NY と GA)を名指します。ターゲティング値は宣言された名前空間内の ID であり、名前はせいぜい非権威的なラベルとして相乗りするだけです。 **反論が正しいとき。** AdCP 自身が値を鋳造するとき、テーブルの所有はデータ運用ではなく相互運用です — `v1-canonical-mapping.json` が正当な例です: AdCP は v1 フォーマット語彙とそれが投影する v2 正準の両方を定義し、集合は加算的(エントリは削除されず、非推奨化のみ)で、外部の権利保持者はいません。そして単一の外部権威が公開に発行するとき(ISO 3166、Eurostat NUTS、ONS ITL、CC-BY の GeoNames)、プロジェクトは来歴と再生成スクリプトを持つ薄い**生成された**ミラーを出荷*してもよい* — 明示的に非権威的な投影であり、ソースがドリフトするか 2 番目のソースが不一致になった瞬間に降格されます。ハードル: 権威を名指し、ライセンスを名指し、なぜ fetch-once-cache-locally のルックアップがそれをすでに解決しないかを説明すること。 **レビュアーテスト。** これらの値の単一の外部権威が存在するか、そしてそれは私たちか? 外部の団体が真実を所有するなら、AdCP は名前空間と解決コントラクトを宣言し、バイトを委ねます。「誰もがこの厄介なマッピングを再実装する」はプロトコルの義務ではなく、ライブラリの機会です。 → 原則 5 と同じ反中央集権化の本能を、信頼ではなく参照データに適用したもの: [Registry](/docs/registry) は解決と発見を助けますが、エコシステムのルックアップテーブルにはなりません。 *** ## Where the surface doesn't yet follow these principles 原則は、サーフェスがまだそれらに違反している箇所を私たちが名指しする限りにおいて信頼に足ります。これらは既知で追跡されています。 **`media_buy.execution.trusted_match`(原則 4)。** ケイパビリティスキーマは TMP 関連のフラグを `media_buy.execution.trusted_match` の内側に置きますが、アーキテクチャページは TMP を Media Buy、Creative、Signals と並ぶ対等な取引ドメインとして扱います。これらの原則を使って提案を評価するレビュアーは、TMP 形状のフラグがどこに属するかについて矛盾する答えに行き着きます。スキーマのサブ名前空間化は正しかった(原則 4 に従い)。親の位置が未解決の問いです。TMP ケイパビリティ宣言への変更を提案する RFC は、これが俎上に載ることを予期すべきです。 **`axe_integrations` と `axe_include_segment` / `axe_exclude_segment`(原則 1 と [spec-guidelines のベンダー中立ルール](/docs/spec-guidelines#platform-agnosticism))。** これらは v3 スキーマに規範的フィールドとして生き残っています。AXE は Scope3 起源のブランドです。spec-guidelines テストによれば、これらは 3.0 GA の前に `ext.axe` へ移動するか `trusted_match` によってクリーンに置き換えられるべきでした。これらは安定した形状ではなく進行中の非推奨化を反映しています — これらのフィールド上に構築する提案は、それらが移動することを予期すべきです。 **「ドメインでないがこのエージェントがすること」のための 3 つのトップレベルキー(原則 4)。** `compliance_testing`、`experimental_features`、`extensions_supported` はそれぞれ、関連するが異なる形状のケイパビリティメタデータをトップレベルに運びます。レビュアーはなぜこれらが統合されないのかを問うでしょう。 **3 つの署名関連トップレベルキー(原則 4)。** `request_signing`、`webhook_signing`、`identity`(オペレーター JWKS)はすべて署名インフラのメタデータです。1 つの関心事のための 3 つのトップレベルキーは、まさに原則が警告する「スキャン可能なトップレベル」のコストです。 **信頼基盤(原則 5)。** 3.x における信頼は trust-on-first-use であり、各当事者の `/.well-known/` + DNS + CDN に根ざしています。鍵の透明性は 4.0 に延期されています。ads.txt と sellers.json は、まさにこの攻撃クラスによって何年もゲームされてきました。原則は正しいですが、基盤はまだ原則が値するものではありません。明示的なギャップ声明については [Trust & Security](/docs/trust) を参照。 これらは、鋭い第三者レビュアーが初読で指摘するサーフェスの矛盾です。正直な答えは、いくつかは進行中の非推奨化、いくつかは未解決、そして少なくとも 1 つ(信頼基盤)は負荷を担う 4.0 の作業だということです。ここで名指しすることが原則を信頼に足るものに保ちます。 *** ## How to use this page RFC を起草しているなら、タイトルを書く前に原則を通して作業してください。はね返される提案の大半は、最初の 2 つのいずれかではね返されます — 存在しないフィールドを提案する(原則 1)、または既存タスクとモードで表現可能な振る舞いのために新しいタスクを提案する(原則 2)。[ケイパビリティエクスプローラー](/docs/protocol/capabilities-explorer) は実際のスキーマツリーをレンダリングします。新しいトップレベルキーを提案する前にそれをチェックしてください。 RFC をレビューしているなら、原則はトリアージフィルターも兼ねます。どの原則に反論しているか、そしてなぜかを名指しする提案は仕事をしています。原則が適用されると認識しない提案は通常、起草の前にカバレッジ主導の返信を必要とします。 原則は不変ではありません。それぞれは特定のトレードオフに対して選ばれ、それぞれはトレードオフがシフトしたときに再交渉の対象です。しかし再交渉は RFC の仕事であって、その副作用ではありません — それに依存する変更を提案する前に、変更のコストを名指ししたうえで、ルールを変えることを明示的に論じてください。 # フォーマット参照 Source: https://adcp-docs-ja.pier1.co.jp/docs/protocol/format-references AdCP における format_id(構造化識別子オブジェクト)と format(完全な定義オブジェクト)の規範的リファレンス。 AdCP はクリエイティブフォーマットを扱うとき、関連するが明確に異なる 2 つの概念を使います。名前が似ているため、再発する実装エラーを引き起こします — このページは両方を正確に定義し、2 つの失敗モードを名指しします。 ## format\_id — 構造化参照オブジェクト `format_id` は**常に JSON オブジェクト**であり、素の文字列ではありません。それは、フォーマットを宣言したエージェントとフォーマットのローカルスラッグによってフォーマットを識別します: ```json theme={null} { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" } ``` 任意の dimension と duration フィールドが、パラメーター化されたテンプレートフォーマットのためにそれを拡張します: ```json theme={null} { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 300, "height": 250 } ``` `format_id` は**ポインター**です — 定義を運ばずにフォーマットを名指しします。 ## format — 完全な定義オブジェクト `format` はクリエイティブフォーマットの完全な仕様です。それは `list_creative_formats` および関連タスクによって返されます。`format` オブジェクトはそのプロパティの 1 つとして `format_id` を含み、加えてアセット要件、レンダー仕様、その他すべてのメタデータを含みます: ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "name": "Display Banner 300×250", "assets": [ { "asset_id": "image", "asset_type": "image", "item_type": "individual", "required": true } ], "renders": [ { "role": "primary", "dimensions": { "width": 300, "height": 250 } } ] } ``` `format` は**定義**です — フォーマットが何を必要とし、どうレンダリングするかを記述します。 ## 一目での対比 | Concept | Field name | JSON type | Use | | --------- | ------------ | ------------------------------ | ------------------------------------------------------------- | | フォーマット識別子 | `format_id` | `object` — `{ agent_url, id }` | ポインター。クリエイティブマニフェスト、リクエストフィルター、プレースメント宣言で使う。 | | フォーマット定義 | `format` | `object` — 完全な仕様 | `list_creative_formats` が返す。`format_id` をネストされたフィールドとして含む。 | | 識別子の配列 | `format_ids` | `format_id` オブジェクトの `array` | `list_creatives`、`list_creative_formats`、関連リクエストのフィルターパラメーター。 | | 定義の配列 | `formats` | `format` オブジェクトの `array` | `list_creative_formats` のレスポンスフィールド。 | ## 名前付きの 2 つの失敗モード ### アンチパターン A — `format_id` スロットの文字列 誤り — `format_id` は素の文字列ではなくオブジェクトでなければなりません: ```json theme={null} { "format_id": "display_300x250" } ``` 正しい: ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" } } ``` AJV エラー: `format_id must be of type object`。原因: `format_id` オブジェクト内の `.id` 文字列が自己完結した名前のように見え、時に抽出されて直接使われる。 ### アンチパターン B — `format` / `formats` スロットの format\_id オブジェクト 誤り — `formats[]` の要素は素の `format_id` ではなく完全な定義オブジェクトでなければなりません: ```json theme={null} { "formats": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" } ] } ``` 正しい: ```json theme={null} { "formats": [ { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "name": "Display Banner 300×250", "assets": [ { "asset_id": "image", "asset_type": "image", "item_type": "individual", "required": true } ] } ] } ``` AJV エラー: `formats[0] must have required property 'name'`。原因: `format_id` と `format` はどちらもオブジェクト。より短いオブジェクトが、より大きいものを期待するスロットに時に置かれる。 ## 関連項目 * [クリエイティブフォーマット](/docs/creative/formats) — 完全なフォーマットオブジェクト構造、アセットタイプ、レンダー仕様 * [テンプレートフォーマット ID](/docs/creative/template-format-ids) — dimension 可変テンプレート用のパラメーター化されたフォーマット ID # get_adcp_capabilities Source: https://adcp-docs-ja.pier1.co.jp/docs/protocol/get_adcp_capabilities get_adcp_capabilities は、バイヤーが AdCP セラーのサポートプロトコル、認証モデル、バージョン、機能ケイパビリティを発見するために行う最初の呼び出しです。リクエストとレスポンスのスキーマリファレンス。 すべての AdCP プロトコルにわたるセラーのプロトコルサポートとケイパビリティを発見します。これは、セラーが何をサポートするかを理解するためにバイヤーが最初に行うべき呼び出しです。 **なぜこの形状か。** ケイパビリティは約 14 のトップレベルドメインキー(プロトコルごとに 1 つ、加えてアイデンティティと署名インフラ)に整理され、機能フラグは各ドメインの `features`/`execution`/その他のサブ名前空間の下にネストされます。私たちはフラットなケイパビリティリストを拒否しました — それはすべての実装者に無制限のサーフェスをスキャンさせ、関連するフラグが隣り合うことから来る発見性を取り除きます。新しいケイパビリティフラグは、新しいトップレベルキーではなく既存のドメインの下に属します。宣言はアドバタイズメントではなくコミットメントです(コンプライアンスランナーがそれらをプローブします)。→ 提案する前に [ケイパビリティエクスプローラー](/docs/protocol/capabilities-explorer) がツリーをたどります。→ [設計原則: ケイパビリティはコミットメント](/docs/protocol/design-principles#4-capabilities-are-commitments-declared-under-existing-buckets)。 **応答時間**: 約 2 秒(設定ルックアップ) **目的**: * **AdCP ディスカバリー** - このエージェントは AdCP をサポートするか?どのバージョン? * **プロトコルサポート** - どのプロトコル(media\_buy、signals、governance、sponsored\_intelligence、creative、brand)? * **認証モデル** - このセラーはエージェントを直接信頼するか、各オペレーターが独立して認証しなければならないか? * **詳細なケイパビリティ** - 機能、実行統合、ジオターゲティング、ポートフォリオ **呼び出し元ごとの認可はここでレポートされません。** `get_adcp_capabilities` はセラーのサーフェス — 任意の認可された呼び出し元に対してそれが*できること*すべて — を返します。特定のアカウントで*あなた*が何を許可されているか(あなたのアイデンティティに対してどのタスクが呼び出し可能か、どのリクエストフィールドが変更可能か、`attestation_verifier` のような名前付きスコープ)を発見するには、[`sync_accounts`](/docs/accounts/tasks/sync_accounts) と [`list_accounts`](/docs/accounts/tasks/list_accounts) レスポンスのアカウントごとのエントリの `authorization` オブジェクトを読みます。完全な形状とセマンティクスについては [Caller authorization](/docs/accounts/overview#caller-authorization) を参照。 **リクエストスキーマ**: [`/schemas/v3/protocol/get-adcp-capabilities-request.json`](https://adcontextprotocol.org/schemas/v3/protocol/get-adcp-capabilities-request.json) **レスポンススキーマ**: [`/schemas/v3/protocol/get-adcp-capabilities-response.json`](https://adcontextprotocol.org/schemas/v3/protocol/get-adcp-capabilities-response.json) ## Tool-Based Discovery AdCP はネイティブの MCP/A2A ツールディスカバリーを使います。**エージェントのツールリストに `get_adcp_capabilities` が存在することが AdCP サポートを示します。** ``` Discovery Flow: 1. Browse agent's tool list (MCP) or skills (A2A) 2. See 'get_adcp_capabilities' tool → Agent supports AdCP 3. Call get_adcp_capabilities → Get version, protocols, features, capabilities 4. Proceed based on returned capabilities ``` このアプローチ: * 標準の MCP/A2A メカニズムを使う(カスタム拡張なし) * 常に現在のケイパビリティを返す(古いメタデータではない) * すべてのケイパビリティ情報の単一の真実の源泉 :::note エージェントカード拡張(`adcp-extension.json`)は v3 で削除されました。代わりにツールベースのディスカバリーを使ってください。 ::: ## Version Negotiation セラーはレスポンスの `adcp.major_versions` でサポートするメジャーバージョンを宣言します。バイヤーはリクエストの `adcp_major_version` で使用するバージョンを宣言します。 ``` Version Negotiation Flow: 1. Buyer calls get_adcp_capabilities with adcp_major_version: 2 2. Seller checks 2 against its major_versions: [2, 3] 3. Version is supported → seller returns capabilities for v2 4. Buyer includes adcp_major_version: 2 on all subsequent requests ``` `adcp_major_version` はすべての AdCP リクエストスキーマの任意フィールドです。バイヤーは、マルチバージョンセラーと対話するときにすべてのリクエストに含めるべきです(SHOULD)。 **セラーの動作:** * `adcp_major_version` が提供されサポートされている → そのバージョンのスキーマで応答 * `adcp_major_version` が提供されたがサポートされていない → `VERSION_UNSUPPORTED` を返す(バイヤーは `adcp_major_version` なしで呼び出してサポートバージョンを発見すべき) * `adcp_major_version` が省略された → 最高のサポートバージョンを想定 **なぜマイナーではなくメジャーバージョンか?** セムバーポリシーはメジャーバージョン内での後方互換性を保証します。3.1 のセラーはネゴシエーションなしに 3.0 のバイヤーに提供できます。ケイパビリティモデルが機能レベルの差異を扱います — バイヤーは互換性を判断するために、バージョン番号ではなく特定のケイパビリティ(ターゲティングシステム、機能、拡張)を確認します。 ## Request Parameters | Field | Type | Description | | -------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `adcp_major_version` | integer | 任意。バイヤーのペイロードが準拠する AdCP メジャーバージョン。提供された場合、セラーは自身の `major_versions` に対して検証し、範囲外の場合 `VERSION_UNSUPPORTED` を返す。省略された場合、セラーはサポートする最高のメジャーバージョンを想定する。 | | `protocols` | string\[] | 任意。特定のプロトコル(`media_buy`、`signals`、`governance`、`sponsored_intelligence`、`creative`、`brand`)にフィルタリング。省略された場合、すべてのサポートプロトコルを返す。 | ## Response Structure ### adcp コア AdCP プロトコル情報: | Field | Type | Description | | ---------------- | ---------- | --------------------------------------------------- | | `major_versions` | integer\[] | **必須。** サポートする AdCP メジャーバージョン(例: `[3]`) | | `idempotency` | object | **必須。** 冪等性セマンティクス。[idempotency](#idempotency) を参照。 | #### idempotency このセラーが `idempotency_key` リプレイ保護を尊重するかを宣言します。3.1 以降、`idempotency_key` はすべての AdCP タスクリクエスト(読み取りも変更も同様)で必須です(読み取りの段階的強制: 3.1 では SHOULD-reject、3.2 では MUST-reject。[security.mdx § Idempotency](/docs/building/by-layer/L1/security#冪等性) を参照)。`request_signing.supported` パターンを反映 — ウィンドウの詳細から切り離された単一の肯定的宣言。クライアントはデフォルトを想定してはなりません(MUST NOT)。このブロックのないセラーは非準拠で、すべての呼び出しモードにわたってリトライに敏感な操作に対して安全でないものとして扱うべきです。 | Field | Type | Description | | -------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `supported` | boolean | **必須。** セラーがリプレイを重複排除するか。`false` のとき、`idempotency_key` の送信は no-op です — セラーは `IDEMPOTENCY_CONFLICT` や `IDEMPOTENCY_EXPIRED` を返さず、素朴なリトライは二重処理します。バイヤーは支出コミット操作をリトライする前に自然キーチェック(例: `get_media_buys` に加え `context.internal_campaign_id` などのリクエストコンテキストや `context.buyer_ref` などのパッケージコンテキスト)を使わなければなりません(MUST)。 | | `replay_ttl_seconds` | integer | `supported: true` のとき必須。セラーがキーの正準レスポンスを保持する期間。最小 `3600`(1h)、推奨 `86400`(24h)、最大 `604800`(7d)。 | ```json theme={null} { "adcp": { "major_versions": [3], "idempotency": { "supported": true, "replay_ttl_seconds": 86400 } } } ``` リプレイ重複排除をサポートしないセラーは明示的に宣言します。 ```json theme={null} { "adcp": { "major_versions": [3], "idempotency": { "supported": false } } } ``` **宣言の検証。** `idempotency.supported: true` は、バイヤーが支出コミット操作を安全にリトライできるようにする信頼を担うクレームです。侵害されたまたはバグのあるセラーは、キーを黙って無視しながら `true` をアドバタイズし、リトライでバイヤーの二重支出を引き起こす可能性があります。バイヤーと適合性ランナーは、意図的なペイロード変異リプレイで宣言をプローブすべきです(SHOULD): 同じ `idempotency_key` だが異なる正準ペイロードで 2 つのリクエストを送信する — 準拠セラーは 2 番目で `IDEMPOTENCY_CONFLICT` を返さなければなりません(MUST)。`supported: true` を宣言するセラーは、宣言が検証済みと見なされる前にベースラインコンプライアンスストーリーボードの一部としてこのプローブに合格しなければなりません(MUST)。 ### supported\_protocols このエージェントがサポートする AdCP プロトコル。これは単一のケイパビリティ軸です — 各値は (a) エージェントが実装するツールを宣言し、*かつ* (b) エージェントを `/compliance/{version}/protocols/{protocol}/` のベースラインコンプライアンスストーリーボードの合格にコミットします。ランナーは JSON snake\_case → URL kebab-case をマッピングします(`media_buy` → `/compliance/.../protocols/media-buy/`)。 ```json theme={null} { "supported_protocols": ["media_buy", "creative"] } ``` 有効な値: `media_buy`、`creative`、`signals`、`governance`、`brand`、`sponsored_intelligence`。 各プロトコルのスコープについては [Compliance Catalog](/docs/building/compliance-catalog) を参照。[コンプライアンステストコントローラー](/docs/building/by-layer/L3/comply-test-controller)のサポートは、プロトコル値としてではなく、別個の `compliance_testing` ケイパビリティブロック(下記)で宣言されます。 ### specialisms 任意の専門化クレーム。各エントリは `/compliance/{version}/specialisms/{id}/` の狭いストーリーボードに対応します。すべての専門分野は `supported_protocols` の 1 つのプロトコルにロールアップします — `sales-guaranteed` を主張するには `media_buy` が必要です。ランナーは親プロトコルが欠けている専門分野を拒否します。 ```json theme={null} { "specialisms": ["sales-guaranteed", "creative-template"] } ``` すべての専門分野については完全な [Compliance Catalog](/docs/building/compliance-catalog) を、権威あるリストについては [enum スキーマ](https://adcontextprotocol.org/schemas/v3/enums/specialism.json) を参照。 ### Capability slot gaps `definePlatform` などの SDK ヘルパーは、プラットフォーム実装をより狭いケイパビリティスロットに投影できます。それらのスロットをコミットメントとして扱ってください: エージェントが対応するタスクパスをエンドツーエンドで実行できるときのみスロットを宣言します。 ストーリーボードやローカルテストベクターがエージェントが宣言しないスロットをターゲットにする場合、期待される適合性結果は失敗ではなく `not_applicable` です。ランナー側の強制が `adcp-client#2244` で到着するまで、カスタムまたはプレリリーススイートを実行する実装者は、宣言されたスロットスコープ外のベクターに明示的なスキップゲートを追加すべきです。欠けているスロットを、それを宣言してプレースホルダーレスポンスを返すことで回避しないでください。それは正直なカバレッジギャップを失敗したケイパビリティクレームに変えます。 ### account アカウントと認証のケイパビリティ。すべてのセラーはこのセクションを宣言すべきです — バイヤーは `sync_accounts`、`list_accounts`、または任意の認証済みタスクを呼び出す前にこれを読みます。シンプルなパブリッシャーでも、課金関係とサンドボックステストを扱うためにアカウント管理が必要です。 | Field | Type | Description | | ------------------------ | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `supported_billing` | string\[] | **必須。** このセラーがサポートする課金モデル: `operator`、`agent`。バイヤーはすべての `sync_accounts` エントリでこれらの値の 1 つを `billing` として渡さなければならない。 | | `require_operator_auth` | boolean | デフォルト: `false`。誰が認証しなければならないかを宣言する。OAuth が使われるか、`list_accounts` が公開されるか、どの `sync_accounts` モードがサポートされるかを、それ自体では宣言しない。`true` のとき、各オペレーターは独立して認証し、アカウントスコープの呼び出しはセラー割り当ての `account_id` 値を使う(セラーまたは上流プラットフォームが正準アカウント名前空間を所有するため)。認証情報が複数のアカウントにアクセスしうる場合、セラーは `list_accounts` を公開しなければならず(MUST)、バイヤーは最初のアカウントスコープリクエストの前に明示的な `account_id` を解決しなければならない(MUST)。認証情報が正確に 1 つのアカウントにバインドされている場合、セラーはそのシングルトンを返す `list_accounts` を公開すべきである(SHOULD)。`false` のとき、エージェントは信頼され、バイヤーは `sync_accounts` でアカウントを宣言し、後続の呼び出しは自然キー(`brand` + `operator`)を渡す。 | | `authorization_endpoint` | string | オペレーター認証用の OAuth URL。セラーがオペレーター認証に OAuth をサポートするときに存在。`require_operator_auth: true` のとき関連。存在しない場合、オペレーターは帯域外(セラーポータル、API キー)で認証情報を取得する。 | | `required_for_products` | boolean | デフォルト: `false`。`true` のとき、バイヤーは `get_products` を呼び出す前にアカウントを確立しなければならない。`false` のとき、バイヤーはアカウントなしでプロダクトを参照できる。 | | `account_financials` | boolean | デフォルト: `false`。`true` のとき、セラーは支出、クレジット、請求ステータスをクエリする [`get_account_financials`](/docs/accounts/tasks/get_account_financials) をサポート。operator 課金のアカウントにのみ適用可能。 | | `sandbox` | boolean | デフォルト: `false`。本番セールスエージェントに強く推奨。`true` のとき、セラーはテスト用のサンドボックスアカウントをサポート。[サンドボックスモード](/docs/media-buy/advanced-topics/sandbox) を参照。 | #### Auth models **バイヤー宣言アカウント**(`require_operator_auth: false`)— セラーはエージェントのアイデンティティクレームを信頼します。エージェントは自身の bearer トークンで一度認証し、次に `sync_accounts` を呼び出して代表するブランドとオペレーターを宣言します。セラーはエージェントのクレームに基づいてアカウントをプロビジョニングし、任意で `brand.json` に対してオペレーターを検証します。後続のすべての呼び出しはエージェントの単一の認証情報を使い、自然キー(`brand` + `operator`)を渡します。 **アカウント ID 名前空間**(`require_operator_auth: true`)— 各オペレーターはセラーと直接認証しなければなりません。エージェントはオペレーターごとに認証情報を取得し(`authorization_endpoint` を使う OAuth 経由、または帯域外)、オペレーターごとのセッションを開き、後続のリクエストでセラー割り当ての `account_id` 値を渡します。OAuth は認証情報の取得であり、アカウントタクソノミー軸ではありません。2 つの名前空間パターンが同じワイヤー参照を使います: 上流管理のセラーは `list_accounts` を公開し、アカウントスコープ呼び出しの前に明示的なアカウント解決を必須にします。`list_accounts` のないセラー定義の名前空間は帯域外でアカウント ID を提供します。 サンドボックスについては、パスはアカウント名前空間に従います: アカウント ID 名前空間は `list_accounts` または帯域外セットアップで既存のテストアカウントを発見します。バイヤー宣言アカウントは `sandbox: true` の `sync_accounts` でサンドボックスを宣言します。 完全なワークフローについては [アカウントとエージェント](/docs/building/by-layer/L2/accounts-and-agents#what-sellers-declare) を、認証モデルと課金サポートの一般的な組み合わせについては [セラーパターン](/docs/building/by-layer/L2/accounts-and-agents#seller-patterns) を参照。 ### media\_buy メディアバイプロトコルのケイパビリティ。`media_buy` が `supported_protocols` にある場合にのみ存在。`media_buy` を宣言するセラーは `account`(`supported_billing` 付き)と `media_buy.portfolio` も含めるべきです — バイヤーは課金の確立とインベントリカバレッジの理解の両方に必要です。コンプライアンステストがそれらの存在を検証します。 :::note 3.0 の破壊的変更 次のフィールドはケイパビリティレスポンスから削除されました: * `media_buy.reporting` — レポートは `media_buy` によって暗示されます。代わりにプロダクトレベルの `reporting_capabilities` を使ってください。 * `features.content_standards` — `media_buy.content_standards` オブジェクトに置き換え。オブジェクトの存在がサポートを示します。 * `features.audience_targeting` — `media_buy.audience_targeting` オブジェクトに置き換え。 * `features.conversion_tracking` — `media_buy.conversion_tracking` オブジェクトに置き換え。 * `execution.targeting.device_platform`、`device_type` — `media_buy` サポートによって暗示。 * `execution.targeting.audience_include`、`audience_exclude` — `audience_targeting` オブジェクトの存在によって暗示。 * `execution.trusted_match.supported` — オブジェクトの存在がサポートを示します。 * `brand.identity` — `supported_protocols` の `brand` によって暗示。`get_brand_identity` は常に利用可能。 ::: #### reporting\_delivery\_methods セラーのプロダクトポートフォリオ全体でどのプッシュベースの配信方法が利用可能かを宣言します。`get_media_buy_delivery` によるポーリングは、このフィールドに関係なくすべての `media_buy` セラーに必須のタスクです。 | Method | Description | Configuration | | --------- | ------------------------------ | ----------------------------------------- | | `webhook` | セラーがバイヤー提供の URL にプッシュ | バイヤーがメディアバイごとに `reporting_webhook` を設定 | | `offline` | セラーがクラウドストレージバケットにバッチファイルをプッシュ | セラーがアカウントごとに `reporting_bucket` をプロビジョニング | 存在しない場合、ポーリングのみが利用可能です。ケイデンスとメトリクスはプロダクトごとに `reporting_capabilities` で宣言されます。 `offline` が宣言される場合、どのクラウドストレージプロトコルがサポートされるか(`s3`、`gcs`、`azure_blob`)を宣言する `offline_delivery_protocols` も含めます。詳細は [オフラインファイル配信](/docs/media-buy/media-buys/optimization-reporting#offline-file-delivery-based-reporting) を参照。 #### creative\_approval\_mode クリエイティブが割り当てられ自動検証が通過した後の、セラーのテナント全体のクリエイティブ承認姿勢を宣言します。これは通知サーフェスや新しい承認ワークフローではありません。人間のレビューが配信適格性をまだブロックできるかをバイヤーとコンプライアンスランナーに伝えます。 | Value | Description | | --------------- | --------------------------------------------------------------------------------------------------- | | `auto_approve` | クリエイティブが割り当てられ自動検証が通過した後、人間のレビューは配信適格性をブロックしない。 | | `require_human` | 1 つ以上のプロダクト/アカウントが、クリエイティブが配信適格になる前に手動レビューを必要とする場合がある。プロダクトレベルのオーバーライドが存在するまで、テナント全体の最悪ケースの上限として扱う。 | 混合承認ポリシーを持つセラーは、アドバタイズされたエージェントで到達可能なすべてのプロダクト/アカウントが自動検証後の自動適格性をサポートしない限り、`require_human` を宣言すべきです(SHOULD)。フィールドが存在しない場合、承認動作はレガシー未指定です。ランナーは省略を肯定的な `auto_approve` クレームとして扱うべきではありません(SHOULD NOT)。 #### features 任意のメディアバイ機能。**true と宣言された場合、セラーはその機能を使うリクエストを尊重しなければなりません(MUST)。** | Feature | Description | | ---------------------------- | ----------------------------------------------------------------------- | | `inline_creative_management` | `create_media_buy` と `update_media_buy` のパッケージペイロードでクリエイティブをインラインで受け入れる | | `property_list_filtering` | `get_products` の `property_list` パラメーターを尊重する | | `catalog_management` | カタログフィード管理のための `sync_catalogs` をサポート | #### content\_standards コンテンツ標準の実装詳細。このオブジェクトの存在は、セラーがサンプリングレートとカテゴリフィルタリングを含むコンテンツ標準設定をサポートすることを示します。 | Field | Type | Description | | --------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------ | | `supports_local_evaluation` | boolean | セラーがローカル評価モデルを実行するか。`false` のとき、`local_verdict` は常に `unevaluated` になり、`get_media_buy_artifacts` の `failures_only` フィルターは有用でない。 | | `supported_channels` | string\[] | セラーがコンテンツアーティファクトを提供できるチャネル。 | | `supports_webhook_delivery` | boolean | セラーが購入作成時に設定された `artifact_webhook` によるプッシュベースのアーティファクト配信をサポートするか。 | **Example:** ```json theme={null} { "content_standards": { "supports_local_evaluation": true, "supported_channels": ["display", "olv", "podcast"], "supports_webhook_delivery": true } } ``` `supports_local_evaluation` が `false` の場合、`get_media_buy_artifacts` の `failures_only` フィルターは空の結果セットを返します — すべての判定が `unevaluated` になります。 #### execution 技術的な実行ケイパビリティ: | Field | Type | Description | | ------------------ | --------- | ------------------------------------------------------------------------------------ | | `trusted_match` | object | [TMP](/docs/trusted-match) サポート。存在する場合、このセラーはリアルタイムのコンテキストおよび/またはアイデンティティマッチングをサポート。 | | `axe_integrations` | string\[] | 非推奨。このセラーが実行できるレガシー AXE URL。新しい統合には `trusted_match` を使う。 | | `creative_specs` | object | クリエイティブ仕様サポート(VAST バージョン、MRAID など) | | `targeting` | object | ターゲティングケイパビリティ(ジオ粒度) | ##### axe\_integrations `axe_integrations` は、このセラーが実行できる Agentic Ad Exchange(AXE)エンドポイント URL の配列です。AXE は AdCP キャンペーンのリアルタイム実行層です — バイヤーエージェントを標準化されたエクスチェンジ経由でプログラマティックインベントリに接続します。 ##### creative\_specs | Field | Type | Description | | ---------------- | --------- | --------------------------------------------- | | `vast_versions` | string\[] | サポートする VAST バージョン(例: `["4.0", "4.1", "4.2"]`) | | `mraid_versions` | string\[] | サポートする MRAID バージョン | | `vpaid` | boolean | VPAID サポート | | `simid` | boolean | SIMID サポート | ##### targeting | Field | Type | Description | | ------------------- | ------- | -------------------------------------------------------------------- | | `geo_countries` | boolean | ISO 3166-1 alpha-2 コードを使う国レベルのターゲティング | | `geo_regions` | boolean | ISO 3166-2 コードを使う地域/州レベルのターゲティング(例: `US-NY`、`GB-SCT`) | | `geo_metros` | object | システム固有のサポートを持つメトロエリアターゲティング | | `geo_postal_areas` | object | 国と精度のサポートを持つ郵便エリアターゲティング | | `age_restriction` | object | `supported` フラグと `verification_methods` を持つ年齢制限ケイパビリティ | | `language` | boolean | 言語ターゲティング(ISO 639-1 コード) | | `keyword_targets` | object | `supported_match_types` 配列(`broad`、`phrase`、`exact`)を持つキーワードターゲティング。 | | `negative_keywords` | object | `supported_match_types` 配列を持つ除外キーワードターゲティング。 | | `geo_proximity` | object | 任意の座標からの近接ターゲティング(下記参照) | デバイスプラットフォームとデバイスタイプのターゲティングは `media_buy` サポートによって暗示されます。オーディエンスの include/exclude ターゲティングは `audience_targeting` ケイパビリティオブジェクトの存在によって暗示されます。 地理的ターゲティングレベルをサポートするセラーは、そのレベルで包含と除外の両方をサポートすべきです(SHOULD)。片方向のみをサポートする場合、黙って無視するのではなく、サポートされないフィールドに対して検証エラーを返さなければなりません(MUST)。 **geo\_proximity** はどの近接ターゲティング方法がサポートされるかを指定します: | Field | Type | Description | | ----------------- | --------- | --------------------------------------------------------------------- | | `radius` | boolean | シンプルな半径ターゲティング | | `travel_time` | boolean | 移動時間アイソクローンターゲティング | | `geometry` | boolean | 事前計算された GeoJSON ジオメトリ | | `transport_modes` | string\[] | アイソクローンでサポートされる交通手段: `driving`、`walking`、`cycling`、`public_transport` | **geo\_metros** はどのメトロ分類システムがサポートされるかを指定します: | System | Description | | ---------------- | ------------------------------------ | | `nielsen_dma` | Nielsen DMA コード(米国市場、例: NYC の `501`) | | `uk_itl1` | UK ITL レベル 1 地域 | | `uk_itl2` | UK ITL レベル 2 地域 | | `eurostat_nuts2` | Eurostat NUTS レベル 2 地域(EU) | **geo\_postal\_areas** はどの国ローカルの郵便番号システムがサポートされるかを指定します。推奨される形状は ISO 3166-1 alpha-2 国でキー付けされ、各国がサポートするシステムをリストします: ```json theme={null} { "us_zip": true, "us_zip_plus_four": true, "US": ["zip", "zip_plus_four"], "GB": ["outward", "full"], "CA": ["fsa", "full"], "ZA": ["postal_code"] } ``` より具体的な登録済みローカルシステムのない国では通常の郵便番号文字列に `postal_code` を使います。3.x 移行中、セラーはエイリアスが存在する場合、ネイティブの国キーとともに `us_zip` などの同等の非推奨エイリアスを発するべきです(SHOULD)。 #### audience\_targeting オーディエンスターゲティングのケイパビリティ。このオブジェクトの存在は、セラーが `sync_audiences` とターゲティングオーバーレイの `audience_include`/`audience_exclude` を含むオーディエンスターゲティングをサポートすることを示します。 | Field | Type | Required | Description | | ------------------------------- | --------- | -------- | --------------------------------------------------------------------- | | `supported_identifier_types` | string\[] | **必須** | オーディエンスマッチングに受け入れられる PII 由来の識別子タイプ。値: `hashed_email`、`hashed_phone`。 | | `minimum_audience_size` | integer | **必須** | ターゲティングに必要な最小マッチオーディエンスサイズ。このしきい値未満のオーディエンスは `status: too_small` になる。 | | `supports_platform_customer_id` | boolean | | `true` のとき、セラーはバイヤーの CRM/ロイヤルティ ID をマッチ可能な識別子として受け入れる。 | | `supported_uid_types` | string\[] | | オーディエンスマッチングに受け入れられるユニバーサル ID タイプ(MAID、RampID、UID2 など)。 | | `matching_latency_hours` | object | | アップロード後の期待マッチングレイテンシー範囲(時間)。形状: `{ min: integer, max: integer }`。 | #### conversion\_tracking セラーレベルのコンバージョントラッキングケイパビリティ。`kind: "event"` 最適化目標についてセラーがサポートするものを宣言します。 | Field | Type | Description | | ------------------------------ | --------- | ---------------------------------------------------- | | `multi_source_event_dedup` | boolean | セラーが単一目標内の複数イベントソースにわたってイベントを重複排除できるか。 | | `supported_event_types` | string\[] | このセラーが追跡できるイベントタイプ。省略された場合、すべての標準イベントタイプがサポート。 | | `supported_uid_types` | string\[] | ユーザーマッチングに受け入れられるユニバーサル ID タイプ。 | | `supported_hashed_identifiers` | string\[] | 受け入れられるハッシュ化 PII タイプ(`hashed_email`、`hashed_phone`)。 | | `supported_action_sources` | string\[] | このセラーがイベントを受け入れるアクションソース。 | | `attribution_windows` | object\[] | 利用可能なアトリビューションウィンドウ。 | #### portfolio インベントリポートフォリオ情報: | Field | Type | Description | | ---------------------- | --------- | ----------------------------- | | `publisher_domains` | string\[] | **必須。** このセラーが代表するパブリッシャードメイン | | `primary_channels` | string\[] | 主要な広告チャネル | | `primary_countries` | string\[] | 主要な国(ISO コード) | | `description` | string | Markdown ポートフォリオ説明 | | `advertising_policies` | string | コンテンツポリシーと制限 | ### signals シグナルプロトコルのケイパビリティ。`signals` が `supported_protocols` にある場合にのみ存在。 | Field | Type | Description | | -------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `data_provider_domains` | string\[] | このシグナルエージェントが再販を認可されているデータプロバイダードメイン。 | | `discovery_modes` | string\[] | エージェントが `get_signals` でサポートするディスカバリーモード。`"brief"`(`signal_spec` / `signal_refs` によるセマンティックディスカバリー)は暗黙的で常にサポート。呼び出し元が `signal_spec` / `signal_refs` / `signal_ids` を省略して完全な価格付きシグナルフィードを列挙できることをアドバタイズするには `"wholesale"` を宣言する。宣言がない場合は `["brief"]` として扱う。 | | `features.catalog_signals` | boolean | **非推奨。** adagents.json `signals[]` のプロバイダー公開シグナル定義への構造化 `signal_ref` 参照のレガシーワイヤーフラグ。 | `catalog_signals` は非推奨です。既存の 3.x エージェントは互換性のためこれを発し続けてもかまいませんが、新しいエージェントはこれを省略すべきで(SHOULD)、呼び出し元は `signal_ref` を使う前にこれを必須としてはなりません(MUST NOT)。 ### creative クリエイティブプロトコルのケイパビリティ。`creative` が `supported_protocols` にある場合にのみ存在。 | Field | Type | Description | | ----------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `supports_compliance` | boolean | `true` のとき、このクリエイティブエージェントはコンプライアンス要件(`required_disclosures`、`prohibited_claims`)を持つブリーフを処理でき、開示がターゲットフォーマットで満たせることを検証する。 | | `supports_transformers` | boolean | `true` のとき、このクリエイティブエージェントはアカウントスコープのトランスフォーマー(ボイス、モデル、スタイル)を提供する。[`list_transformers`](/docs/creative/task-reference/list_transformers) で発見し、[`build_creative`](/docs/creative/task-reference/build_creative) で `transformer_id` により選択。 | | `supports_refinement` | boolean | `true` のとき、このクリエイティブエージェントは生成した `build_variant` リーフを保持し、[`build_creative`](/docs/creative/task-reference/build_creative) の `refine_from_build_variant_id` から再ビルドできる。 | | `refinable_retention_seconds` | integer | `supports_refinement` が `true` のとき、生成された `build_variant_id` が `refine_from_build_variant_id` 経由で絞り込み可能なままの**保証最小**ウィンドウ(下限であり上限ではない)。 | | `multiplicity` | object | バイヤーが `max_creatives`/`max_variants` を送る前のファンアウト判別子。`supports_catalog_fanout` + `max_creatives_limit`、`supports_variants` + `max_variants_limit`、`variant_dimensions[]`。 | | `supports_spend_controls` | boolean | `true` のとき、`build_creative` は呼び出しごとの `max_spend` 上限を尊重し、`mode: "estimate"` ドライランをサポート。`bills_through_adcp: true` の場合にのみ意味を持つ。 | | `bills_through_adcp` | boolean | `true` のとき、このクリエイティブエージェントは AdCP レートカードサーフェスを通じて課金する。`false` または存在しない場合、エージェントは帯域外で課金する。 | ### governance ガバナンスプロトコルのケイパビリティ。`governance` が `supported_protocols` にある場合にのみ存在。ガバナンスエージェントは 4 つのドメインにわたってケイパビリティを宣言します: プロパティ評価、クリエイティブ評価、コンテンツ標準検証、ポリシーレジストリ統合。 #### property\_features このガバナンスエージェントが評価できるプロパティ機能の配列。[プロパティガバナンス](/docs/governance/property/index) を参照。 | Field | Type | Description | | ----------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `feature_id` | string | **必須。** 一意識別子(例: `mfa_score`、`coppa_certified`)。[Policy Registry](/docs/governance/policy-registry) エントリにマップされる機能には `registry:{policy_id}` プレフィックスを使う。 | | `type` | string | **必須。** データタイプ: `binary`、`quantitative`、`categorical`。 | | `range` | object | quantitative 用: `{ min, max }` | | `categories` | string\[] | categorical 用: 有効な値 | | `description` | string | 人間可読な説明 | | `methodology_url` | string | 方法論ドキュメントへの URL | #### creative\_features このガバナンスエージェントが評価できるクリエイティブ機能の配列。`property_features` と同じフィールドスキーマ。[クリエイティブガバナンス](/docs/governance/creative/index) を参照。 #### content\_standards コンテンツ標準検証のケイパビリティ。[コンテンツ標準](/docs/governance/content-standards/index) を参照。 | Field | Type | Description | | --------------------- | --------- | ---------------------------------------------------------------- | | `supported` | boolean | このエージェントがコンテンツ標準検証エージェントとして機能できるか | | `calibration_formats` | string\[] | このエージェントが評価できるアーティファクトアセットタイプ(例: `text`、`image`、`video`、`audio`) | #### policy\_registry ポリシーレジストリ統合のケイパビリティ。[Policy Registry](/docs/governance/policy-registry) を参照。 | Field | Type | Description | | ----------- | --------- | -------------------------------------------------------------------------------- | | `supported` | boolean | このエージェントが AdCP ポリシーレジストリからポリシーを消費するか | | `domains` | string\[] | このエージェントがカバーするガバナンスドメイン(例: `campaign`、`property`、`creative`、`content_standards`) | **ガバナンスエージェントレスポンスの例:** ```json theme={null} { "$schema": "/schemas/protocol/get-adcp-capabilities-response.json", "status": "completed", "adcp": { "major_versions": [3], "idempotency": { "supported": true, "replay_ttl_seconds": 86400 } }, "supported_protocols": ["governance"], "governance": { "property_features": [ { "feature_id": "mfa_score", "type": "quantitative", "range": { "min": 0, "max": 100 }, "description": "Made For Advertising detection (0=quality content, 100=likely MFA)", "methodology_url": "https://vendor.example.com/methodology/mfa" }, { "feature_id": "coppa_certified", "type": "binary", "description": "COPPA compliance certification" }, { "feature_id": "registry:uk_hfss", "type": "binary", "description": "UK HFSS advertising restrictions compliance" }, { "feature_id": "carbon_score", "type": "quantitative", "range": { "min": 0, "max": 100 }, "description": "Carbon footprint sustainability score", "methodology_url": "https://vendor.example.com/methodology/carbon-score" } ], "creative_features": [ { "feature_id": "registry:eu_ai_act_article_50", "type": "binary", "description": "EU AI Act Article 50 — AI-generated content disclosure" }, { "feature_id": "registry:ca_sb_942", "type": "binary", "description": "California SB 942 — AI transparency compliance" }, { "feature_id": "auto_redirect", "type": "binary", "description": "Detects auto-redirect behavior in creative code" }, { "feature_id": "credential_harvest", "type": "binary", "description": "Detects credential harvesting patterns" } ], "content_standards": { "supported": true, "calibration_formats": ["text", "image", "video"] }, "policy_registry": { "supported": true, "domains": ["campaign", "property", "creative", "content_standards"] } } } ``` ### measurement 実験的な測定プロトコルのケイパビリティ。`measurement` が `supported_protocols` にある場合にのみ存在。それを実装するエージェントは `experimental_features` に `measurement.core` もリストしなければなりません。`measurement` プロトコルは現在、カタログ探索のための `get_adcp_capabilities`(このブロック)にスコープされています。 **スコープ。** `measurement` を主張するエージェントは、広告配信、エクスポージャー、または効果についての 1 つ以上の定量的メトリクスを計算します(インプレッション検証、ビューアビリティ、IVT、アテンション、ブランドリフト、インクリメンタリティ、成果、排出 — ベンダーが `metrics[]` でサーフェスを定義)。メトリクス定義(このブロック)を返し、価格やカバレッジ(`measurement_terms` で購入ごとに交渉)やライブ値(`vendor_metric_values` で購入ごとに返す)は返しません。 #### metrics この測定エージェントが計算するメトリクスの配列。 | Field | Type | Description | | --------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------- | | `metric_id` | string | **必須。** ベンダースコープの識別子(`attention_units`、`gco2e_per_impression` など)。完全なアイデンティティはタプル `(vendor.domain, vendor.brand_id, metric_id)`。 | | `standard_reference` | string (URI) | このメトリクスが**実装**する公開標準を指す任意の URI。 | | `accreditations` | object\[] | このメトリクスが持つサードパーティ認定の任意のリスト(MRC、ARF、JIC 機関など)。各エントリ: `accrediting_body`(必須)、任意の `certification_id`、`valid_until`、`evidence_url`。 | | `unit` | string | `vendor_metric_values.value` でレポートされる値の単位(`score`、`seconds`、`persons`、`gCO2e`、`lift_percent`、`USD` など)。 | | `description` | string | メトリクスが測定するものと方法論ノートの人間可読な説明。 | | `methodology_url` | string (URI) | ベンダーの完全な方法論ドキュメントへの URL。 | | `methodology_version` | string | 方法論の任意のバージョン識別子。 | | `ext` | object | AdCP `ext` 慣例に従うベンダー拡張。 | ```json Response example theme={null} { "measurement": { "metrics": [ { "metric_id": "attention_units", "standard_reference": "https://iabtechlab.com/standards/attention-measurement", "accreditations": [ { "accrediting_body": "MRC", "certification_id": "MRC-ATT-2026-001", "valid_until": "2027-12-31", "evidence_url": "https://mediaratingcouncil.org/accreditations/attentionvendor" } ], "unit": "score", "description": "Eye-tracking-based attention score (0-100). Computed from a panel of 25K opted-in households.", "methodology_url": "https://attentionvendor.example/docs/attention-units", "methodology_version": "v2.1" }, { "metric_id": "engagement_seconds", "unit": "seconds", "description": "Active dwell time in seconds, measured via in-content telemetry." } ] } } ``` **これはディスカバリーサーフェスであり、レートカードではありません。** カタログはバイヤーにベンダーが*何を*測定し、*どの標準/認定*が裏付けるかを伝えます。インプレッションごとの価格、最小測定可能インベントリ、アトリビューションウィンドウ、地理的カバレッジ、データ鮮度 SLA は、このカタログではなく、`create_media_buy` のセラーの `measurement_terms` を通じて購入ごとに交渉されます。 ### compliance\_testing コンプライアンステストのケイパビリティ。このブロックの存在は、エージェントが [`comply_test_controller`](/docs/building/by-layer/L3/comply-test-controller) による決定的テストをサポートすることを宣言します。エージェントがコンプライアンステストをサポートしない場合はブロックを省略します。 **本番デプロイはこのブロックを含めてはなりません(MUST NOT)。** `comply_test_controller` はデプロイレベルでサンドボックス専用です。ディスパッチがゲートされていても、本番エンドポイントでケイパビリティをアドバタイズすることは非準拠です。 | Field | Type | Description | | ----------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `scenarios` | string\[] | このエージェントがサポートするコンプライアンステストシナリオ。値は `list_scenarios` を除き、エージェントが実装するすべての正準コントローラーシナリオを含むべき(SHOULD)。現在の正準値には `force_creative_status`、`force_account_status`、`force_media_buy_status`、`seed_product`、`seed_measurement_catalog` などが含まれる。ランナーはシナリオ名をオープン文字列として扱わなければならない(MUST)。 | :::note コンプライアンステストはデプロイレベルでサンドボックス専用です — 本番デプロイはこのブロックをアドバタイズしたり、任意のサーフェスで `comply_test_controller` を公開したりしてはなりません(MUST NOT)。 ::: ### webhook\_signing セラーの webhook 署名姿勢を宣言します。変更 webhook の発出をアドバタイズする任意のセラー — `media_buy.reporting_delivery_methods` に `webhook` を含む、`media_buy.content_standards.supports_webhook_delivery: true`、または `wholesale_feed_webhooks.supported: true` を含むがこれらに限らない — は、このブロックを `supported: true` で含めなければなりません(MUST)。webhook をまったく発出しないセラーはブロックを完全に省略してもかまいません(MAY)。 | Field | Type | Description | | ---------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `supported` | boolean | **セラーがケイパビリティサーフェスの他の場所で変更 webhook の発出をアドバタイズする場合に必須。** セラーがアウトバウンド webhook に署名する場合にのみ `true`。`false` はセラーが webhook を発出するが署名しないことを意味する。バイヤーはオンボーディングに失敗しなければならない(MUST)。 | | `profile` | string | **`supported: true` のとき必須。** プロファイルバージョン文字列。現在は `"adcp/webhook-signing/v1"`。 | | `algorithms` | string\[] | **`supported: true` のとき必須。** `["ed25519", "ecdsa-p256-sha256"]` のサブセット。 | | `legacy_hmac_fallback` | boolean | **`supported: true` のとき必須。** セラーがレガシー HMAC-SHA256 スキームをサポートする場合にのみ `true`。`false` が 3.x で推奨される姿勢 — HMAC スキームは AdCP 4.0 で削除される。 | **Example:** ```json theme={null} { "webhook_signing": { "supported": true, "profile": "adcp/webhook-signing/v1", "algorithms": ["ed25519", "ecdsa-p256-sha256"], "legacy_hmac_fallback": false } } ``` webhook 署名ブロックは `request_signing`(インバウンド)と並行し、2 つのブロックがバイヤーとセラー間の 2 つの署名方向をカバーします。 ### extensions\_supported このエージェントがサポートする拡張名前空間の配列。バイヤーはこのエージェントからのレスポンスの `ext.{namespace}` フィールドに意味のあるデータを期待できます。 | Field | Type | Description | | ---------------------- | --------- | ----------------------------------- | | `extensions_supported` | string\[] | 拡張名前空間(例: `["iab_tcf", "iab_gpp"]`) | 拡張スキーマは [AdCP 拡張レジストリ](/docs/building/by-layer/L2/context-sessions#extensions) に公開されています。 **Example:** ```json theme={null} { "extensions_supported": ["iab_tcf", "iab_gpp", "acmecorp"] } ``` ### experimental\_features このエージェントが実装する実験的 AdCP サーフェスの配列。サーフェスは、そのスキーマが `x-status: experimental` を運ぶとき実験的です — コアプロトコルの一部だがまだ凍結されておらず、6 週間の予告をもって 3.x リリース間で破壊される可能性があります。任意の実験的サーフェスを実装するセラーは、ここにその機能 id をリストしなければなりません(MUST)。 | Field | Type | Description | | ----------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `experimental_features` | string\[] | 実験的機能 id(例: `["brand.rights_lifecycle", "governance.campaign", "measurement.core", "trusted_match.core", "sponsored_intelligence.core"]`) | **Example:** ```json theme={null} { "experimental_features": ["brand.rights_lifecycle", "measurement.core", "trusted_match.core"] } ``` 完全な安定性コントラクト、卒業基準、クライアントガイダンスについては [実験的ステータス](/docs/reference/experimental-status) を参照。 ### wholesale\_feed\_versioning [`get_products`](/docs/media-buy/task-reference/get_products#ホールセールフィードバージョニング) と [`get_signals`](/docs/signals/tasks/get_signals#ホールセールフィードバージョニング) の条件付きフェッチトークンケイパビリティ。ホールセールフィード webhook から独立: エージェントは変更ペイロードをプッシュせずに安価なバージョンプローブをサポートしてもよく(MAY)、修復のための再照合読み取りを依然として要求しながら変更ペイロードをプッシュしてもよい(MAY)。 | Field | Type | Description | | -------------------------- | ------- | --------------------------------------------------------------------------------------------- | | `supported` | boolean | **必須。** エージェントがレスポンスで `wholesale_feed_version` を返し、リクエストで `if_wholesale_feed_version` を尊重するか。 | | `pricing_version_separate` | boolean | エージェントが `pricing_version` を `wholesale_feed_version` から独立して追跡するか。 | | `cache_scope_account` | boolean | エージェントが `cache_scope: "account"` を返すことがあるか(すなわち、パブリックレートカードとは別のアカウント別オーバーレイを公開するか)。 | **Example:** ```json theme={null} { "wholesale_feed_versioning": { "supported": true, "pricing_version_separate": true, "cache_scope_account": true } } ``` ### wholesale\_feed\_webhooks エージェントごとのホールセールプロダクトフィードとホールセールシグナルフィードの webhook ケイパビリティ。セールスエージェント(プロダクト)とシグナルエージェント(シグナル)が宣言します。`supported` が `true` のとき、コンシューマーは `product.*`、`signal.*`、`wholesale_feed.bulk_change` イベントの `sync_accounts.accounts[].notification_configs[]` エントリを登録し、各 webhook で実際の変更ペイロードを受け取れます。 **用語。** ここで「ホールセールフィード」は、`get_products` と `get_signals` が公開するエージェントの購入可能なホールセールプロダクトフィードとホールセールシグナルフィードを意味します。これは、キャンペーン実行のためにバイヤー提供のキャンペーン入力フィードをセラーアカウントにプッシュする `sync_catalogs` とは異なります。 | Field | Type | Description | | ------------- | --------- | ------------------------------------------------------------------------------------------------------------ | | `supported` | boolean | **必須。** このエージェントがアカウントレベルの `sync_accounts.accounts[].notification_configs[]` を通じてホールセールフィード変更ペイロードをプッシュできるか。 | | `event_types` | string\[] | このエージェントが発出できるイベントタイプ。セールスエージェントは `product.*` イベントを発出。シグナルエージェントは `signal.*` イベントを発出。 | **Example(セールス + シグナルエージェント):** ```json theme={null} { "wholesale_feed_webhooks": { "supported": true, "event_types": [ "product.created", "product.updated", "product.priced", "product.removed", "signal.created", "signal.updated", "signal.priced", "signal.removed", "wholesale_feed.bulk_change" ] } } ``` ## The Capability Contract **ケイパビリティが宣言された場合、セラーはそれを尊重しなければなりません(MUST)。** * `media_buy.execution.targeting.geo_postal_areas.US` が `zip` を含む → バイヤーは `{ country: "US", system: "zip", values: [...] }` を送信でき、セラーはそれを尊重しなければならない * `media_buy.execution.targeting.geo_metros.nielsen_dma: true` → バイヤーは DMA コードを送信でき、セラーはそれを尊重しなければならない * `media_buy.content_standards` オブジェクトが存在 → セラーは提供時にコンテンツ標準を適用しなければならない * `media_buy.audience_targeting` オブジェクトが存在 → セラーは `sync_audiences` とオーディエンスターゲティングオーバーレイをサポートしなければならない * `media_buy.conversion_tracking` オブジェクトが存在 → セラーは `sync_event_sources` と `log_event` をサポートしなければならない 黙って無視しない。セラーがケイパビリティをサポートできない場合、`false` を宣言するか省略すべきです。 ## Common Scenarios ### Basic Capability Discovery ```javascript theme={null} import { AdcpClient } from '@adcp/sdk'; const client = new AdcpClient({ baseUrl: 'https://seller.example.com/mcp' }); // Get seller capabilities const result = await client.getAdcpCapabilities({}); if (result.errors) { throw new Error(`Request failed: ${result.errors[0].message}`); } // Check protocol support console.log(`AdCP versions: ${result.adcp.major_versions.join(', ')}`); console.log(`Supported protocols: ${result.supported_protocols.join(', ')}`); // Check media-buy capabilities if (result.supported_protocols.includes('media_buy')) { const mediaBuy = result.media_buy; // Check content standards support (object presence = signal) if (mediaBuy.content_standards) { console.log('Content standards supported'); } // Check geo targeting (normalize native country keys and deprecated aliases) const postalSupport = mediaBuy.execution?.targeting?.geo_postal_areas; if (postalSupport?.US?.includes('zip') || postalSupport?.us_zip === true) { console.log('US ZIP code targeting supported'); } // Portfolio overview console.log(`Publishers: ${mediaBuy.portfolio.publisher_domains.length}`); } ``` ### Check multi-protocol support ```javascript theme={null} const caps = await client.getAdcpCapabilities({}); const sellsMedia = caps.supported_protocols.includes('media_buy'); const managesCreatives = caps.supported_protocols.includes('creative'); if (sellsMedia && managesCreatives) { // Single agent handles both protocols — no need to discover a separate service const formats = await client.listCreativeFormats({}); const delivery = await client.getCreativeDelivery({ media_buy_ids: ['mb_12345'] }); } ``` ### Filter sellers by capability ```javascript theme={null} // Find sellers that support specific requirements async function findCompatibleSellers(sellers, requirements) { const compatible = []; for (const sellerUrl of sellers) { const client = new AdcpClient({ baseUrl: sellerUrl }); const caps = await client.getAdcpCapabilities({}); if (caps.errors) continue; // Must support media_buy protocol if (!caps.supported_protocols.includes('media_buy')) continue; const mediaBuy = caps.media_buy; // Check geo targeting requirement if (requirements.postalCodeTargeting) { const postalSupport = mediaBuy.execution?.targeting?.geo_postal_areas; if (!(postalSupport?.US?.includes('zip') || postalSupport?.us_zip === true)) { continue; } } // Check content standards requirement (object presence = signal) if (requirements.contentStandards) { if (!mediaBuy.content_standards) { continue; } } compatible.push({ url: sellerUrl, capabilities: caps }); } return compatible; } ``` ### Use Capabilities to Build Targeting ケイパビリティは create\_media\_buy ターゲティングで何を指定できるかを教えます。`required_geo_targeting` を使って、特定のジオターゲティングレベルとシステムをサポートするセラーにプロダクトをフィルタリングします: ```javascript theme={null} // First, check capabilities const caps = await client.getAdcpCapabilities({}); if (!caps.supported_protocols.includes('media_buy')) { throw new Error('Seller does not support media_buy protocol'); } const mediaBuy = caps.media_buy; const postalSupport = mediaBuy.execution?.targeting?.geo_postal_areas; // Filter products to sellers with specific geo targeting capabilities const products = await client.getProducts({ brief: "Premium video inventory in US for ZIP-targeted campaign", filters: { channels: ['olv', 'ctv'], countries: ['US'], required_geo_targeting: [ { level: 'postal_area', country: 'US', system: 'zip' } ] } }); ``` **プロダクトの地理の 2 つのモデル:** | Inventory Type | Filter By | Example | | ------------------------ | --------------------------------- | --------------------------- | | デジタル(display、OLV、CTV) | ケイパビリティ: `required_geo_targeting` | プロダクトは広範なカバレッジを持ち、購入時にターゲット | | ローカル(radio、DOOH、ローカル TV) | カバレッジ: `metros`、`regions` | プロダクトが地理的にバインド | ### Local Inventory Example (Radio, DOOH) ローカルにバインドされたインベントリでは、プロダクトが地理的に固有です。NYC DMA のラジオ局は NYC のみをカバーします。 ```javascript theme={null} // Find radio products in specific DMAs const radioProducts = await client.getProducts({ brief: "Radio inventory in NYC and LA markets", filters: { channels: ['radio'], metros: [ { system: 'nielsen_dma', code: '501' }, // NYC { system: 'nielsen_dma', code: '803' } // LA ] } }); ``` ## Response Example ```json theme={null} { "$schema": "/schemas/protocol/get-adcp-capabilities-response.json", "status": "completed", "adcp": { "major_versions": [3], "idempotency": { "supported": true, "replay_ttl_seconds": 86400 } }, "supported_protocols": ["media_buy"], "account": { "require_operator_auth": false, "supported_billing": ["operator", "agent"] }, "media_buy": { "creative_approval_mode": "auto_approve", "features": { "inline_creative_management": true, "property_list_filtering": true }, "execution": { "creative_specs": { "vast_versions": ["4.0", "4.1", "4.2"], "mraid_versions": ["3.0"], "vpaid": false, "simid": true }, "targeting": { "geo_countries": true, "geo_regions": true, "geo_metros": { "nielsen_dma": true }, "geo_postal_areas": { "US": ["zip", "zip_plus_four"], "GB": ["outward", "full"], "CA": ["fsa", "full"] }, "language": true } }, "content_standards": { "supports_local_evaluation": true, "supported_channels": ["display", "olv"], "supports_webhook_delivery": false }, "audience_targeting": { "supported_identifier_types": ["hashed_email", "hashed_phone"], "supported_uid_types": ["uid2", "rampid"], "minimum_audience_size": 500, "matching_latency_hours": { "min": 1, "max": 24 } }, "portfolio": { "publisher_domains": ["example.com", "news.example.com"], "primary_channels": ["display", "olv"], "primary_countries": ["US", "CA"] } }, "extensions_supported": ["acmecorp"], "last_updated": "2025-01-23T10:00:00Z" } ``` ### Multi-protocol agent エージェントは単一のエンドポイントから複数のプロトコルを実装できます。これは、メディア購入とクリエイティブ生成の両方を管理するセラーで一般的です — バイヤーは同じ URL ですべてのタスクを呼び出します。 ```json theme={null} { "$schema": "/schemas/protocol/get-adcp-capabilities-response.json", "status": "completed", "adcp": { "major_versions": [3], "idempotency": { "supported": true, "replay_ttl_seconds": 86400 } }, "supported_protocols": ["media_buy", "creative"], "account": { "require_operator_auth": false, "supported_billing": ["operator"] }, "media_buy": { "creative_approval_mode": "require_human", "features": { "inline_creative_management": true }, "portfolio": { "publisher_domains": ["news.example.com"], "primary_channels": ["display", "olv"] } }, "creative": { "has_creative_library": true, "supports_generation": true, "supports_transformation": false, "supports_compliance": false, "bills_through_adcp": true } } ``` `supported_protocols` に `"creative"` が含まれる場合、バイヤーはこのエージェントでクリエイティブプロトコルタスク(`list_creative_formats`、`sync_creatives`、`get_creative_delivery` など)を呼び出せます。[セールスエージェントのクリエイティブケイパビリティ](/docs/creative/sales-agent-creative-capabilities) を参照。 ### Geo Standards Reference | Level | System | Examples | | ----------- | ---------------------- | ------------------------------------ | | Country | ISO 3166-1 alpha-2 | `US`、`GB`、`DE`、`CA` | | Region | ISO 3166-2 | `US-NY`、`GB-SCT`、`DE-BY`、`CA-ON` | | Metro (US) | `nielsen_dma` | `501`(NYC)、`803`(LA)、`602`(Chicago) | | Metro (UK) | `uk_itl2` | `UKI`(London)、`UKD`(North West) | | Metro (EU) | `eurostat_nuts2` | `DE30`(Berlin)、`FR10`(Île-de-France) | | Postal (US) | `US` / `zip` | `10001`、`90210` | | Postal (US) | `US` / `zip_plus_four` | `10001-1234` | | Postal (UK) | `GB` / `outward` | `SW1`、`EC1`、`M1` | | Postal (UK) | `GB` / `full` | `SW1A 1AA` | | Postal (CA) | `CA` / `fsa` | `K1A`、`M5V` | ## Migration from list\_authorized\_properties (v2) `list_authorized_properties` タスクは v3 で削除されました。v2 から移行する場合: | Old Field | New Location | | ----------------------- | ------------------------------------------ | | `publisher_domains` | `media_buy.portfolio.publisher_domains` | | `primary_channels` | `media_buy.portfolio.primary_channels` | | `primary_countries` | `media_buy.portfolio.primary_countries` | | `portfolio_description` | `media_buy.portfolio.description` | | `advertising_policies` | `media_buy.portfolio.advertising_policies` | | `last_updated` | `last_updated`(トップレベル) | 新しいフィールド: * `adcp.major_versions` - バージョン互換性 * `supported_protocols` - どのドメインプロトコルがサポートされるか * `media_buy.features` - 任意の機能サポート * `media_buy.execution.targeting` - ジオターゲティング粒度 ## Error Handling | Error Code | Description | Resolution | | --------------------- | ----------------------------------------------------- | ----------------------------------------------- | | `AUTH_MISSING` | 認証情報が提示されていない | auth ヘッダーで認証情報を提供 | | `AUTH_INVALID` | 認証情報が拒否された(期限切れ / 失効) | 人間による認証情報のローテーションが必要 | | `VERSION_UNSUPPORTED` | 宣言された `adcp_major_version` がセラーの `major_versions` にない | `adcp_major_version` なしで呼び出してサポートバージョンを発見し、リトライ | | `INTERNAL_ERROR` | サーバーエラー | バックオフを伴ってリトライ | ## Best Practices **1. ケイパビリティをキャッシュする** ケイパビリティはめったに変わりません。結果をキャッシュし、古さの検出に `last_updated` を使います。 **2. まずプロトコルサポートを確認する** プロトコル固有のフィールドにアクセスする前に、プロトコルが `supported_protocols` にあることを検証します。 **3. リクエスト前に確認する** セラーがサポートしないシステムの郵便エリアを送らないでください。セラーがサポートしない機能をリクエストしないでください。 **4. 非互換で早期に失敗する** セラーが必要なケイパビリティをサポートしない場合、後で失敗を発見するのではなく早期にスキップします。 **5. 続行前に認証モデルを読む** ディスカバリー直後に `account.require_operator_auth` を確認します。エージェント信頼とオペレータースコープのフローは大きく異なります。 **6. ルーティングにプロトコルバージョンを使う** `adcp.major_versions` に基づいて適切な API バージョンにリクエストをルーティングします。 ## Next Steps ケイパビリティを発見した後: 1. **アカウントをセットアップ**: `account.require_operator_auth` の認証モデルに従う — [アカウントとエージェント](/docs/building/by-layer/L2/accounts-and-agents#what-sellers-declare) を参照 2. **プロダクトをフィルタリング**: ケイパビリティ認識フィルターで [`get_products`](/docs/media-buy/task-reference/get_products) を使う 3. **プロパティを検証**: プロパティ定義のためにパブリッシャーの `adagents.json` ファイルを取得 4. **バイを作成**: サポートされる機能で [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) を使う ## Learn More * [アカウントとエージェント](/docs/building/by-layer/L2/accounts-and-agents) - 認証モデル、アカウントセットアップ、課金 * [adagents.json 仕様](/docs/governance/property/adagents) - パブリッシャー認可ファイル * [プロダクトフィルター](/docs/media-buy/task-reference/get_products#filters) - ケイパビリティ認識フィルタリング * [コンテンツ標準](/docs/governance/content-standards) - ブランドセーフティ設定 # プロトコル別の必須タスク Source: https://adcp-docs-ja.pier1.co.jp/docs/protocol/required-tasks エージェントロールごとに整理した、各 AdCP プロトコルの必須・任意タスクの統合リファレンス。 # プロトコル別の必須タスク 各 AdCP プロトコルは、エージェントがそのロールに応じて実装するタスクを定義します。このページは、各プロトコル仕様の要件を単一のリファレンスに統合します。 **凡例**: **Required** — 仕様が実装を義務付ける。**Conditional** — エージェントが特定のケイパビリティを持つとき必須。**Optional** — 仕様が推奨するが義務付けない。 一部のタスクはプロトコルをまたぎます。例えば `list_creative_formats` と `sync_creatives` は Creative Protocol で定義されますが、Media Buy セールスエージェントによっても実装されます。これらはソースを示すノート付きで両プロトコルの下に現れます。 ## 共通 すべての AdCP エージェントは、プロトコルにかかわらず、次を実装します: | Task | Requirement | Reference | | ----------------------- | ----------- | ------------------------------------------------------ | | `get_adcp_capabilities` | Required | [ケイパビリティディスカバリー](/docs/protocol/get_adcp_capabilities) | 呼び出し元スコープの RBAC イントロスペクションは独立したタスクではありません。スコープイントロスペクションをサポートするセラーは、`sync_accounts` と `list_accounts` レスポンスにアカウントごとの `authorization` オブジェクトを返します。[Accounts Protocol — Caller authorization](/docs/accounts/overview#caller-authorization) を参照。 AdCP タスクライフサイクル状態はアプリケーション層の状態であり、MCP ネイティブでも A2A ネイティブでもありません。3.x セラーは、ポーリングと再照合のために衝突しない AdCP エイリアスをアドバタイズしてもかまいません(MAY): レガシー `tasks/get` に対する `get_task_status`、レガシー `tasks/list` に対する `list_tasks`。これらのエイリアスは 3.x の任意の互換性サーフェスです。呼び出し元は 3.x ラインを通じてレガシー名をサポートし続けなければなりません(MUST)。エイリアスとレガシーのリクエストペイロードは、呼び出し元の認証済みアカウント + プリンシパルペア内にタスク可視性を保つのに使われる任意の `account` スコープを含め、同じ snake\_case 形状を使います。 ## Media Buy Protocol ### セールスエージェント(セラー) | Task | Requirement | Notes | | ------------------------------ | ----------- | --------------------------------------------------------------------- | | `get_products` | Required | インベントリディスカバリー | | `list_creative_formats` | Required | フォーマット仕様(Creative Protocol タスク) | | `create_media_buy` | Required | キャンペーン作成とオーダー確認 | | `update_media_buy` | Required | 予算、ターゲティング、一時停止、キャンセル | | `get_media_buys` | Required | 運用状態の取得 | | `get_media_buy_delivery` | Required | パッケージレベルの配信メトリクス。`reporting_capabilities` はすべてのプロダクトに含めなければならない(MUST) | | `provide_performance_feedback` | Required | バイヤーの最適化シグナルを受け入れる | | `sync_creatives` | Conditional | セールスエージェントがクリエイティブライブラリをホストするとき必須(Creative Protocol タスク) | | `list_creatives` | Optional | クリエイティブカタログの参照(Creative Protocol タスク) | | `sync_catalogs` | Optional | プロダクト/インベントリカタログ同期 | | `sync_event_sources` | Optional | コンバージョントラッキングのセットアップ | | `log_event` | Conditional | イベントソースが設定されているとき必須 | | `sync_audiences` | Optional | ファーストパーティ CRM オーディエンスアップロード | セールスエージェントはまた、少なくとも 1 つのトランスポート(MCP または A2A)をサポートし、`supported_protocols` に `media_buy` を宣言しなければなりません(MUST)。 **リファレンス**: [Media Buy 仕様](/docs/media-buy/specification) · [セラー統合ガイド](/docs/building/operating/seller-integration) ### オーケストレーター(バイヤー) オーケストレーターは MCP/A2A サーバーではありません — セールスエージェントのタスクを呼び出します。準拠するオーケストレーターは次をしなければなりません(MUST): 1. セールスエージェントで認証する 2. リクエストスキーマごとに必須フィールドを含める 3. 非同期のタスクレベルレスポンス(`submitted`、`working`、`input-required`)と完了アーティファクトの webhook 配信を扱う 4. 後続のすべての操作に `media_buy_id` を使う 5. クリエイティブアップロードについて `creative_deadline` を尊重する **リファレンス**: [Media Buy 仕様 — オーケストレーター適合性](/docs/media-buy/specification#orchestrator-conformance) ## Creative Protocol ### クリエイティブエージェント | Task | Requirement | Notes | | ----------------------- | ----------- | ------------------------------------------------------------ | | `list_creative_formats` | Required | 技術仕様を伴うフォーマットディスカバリー | | `list_transformers` | Conditional | エージェントが `creative.supports_transformers: true` をアドバタイズするとき必須 | | `build_creative` | Optional | 生成、変換、またはライブラリ取得 | | `preview_creative` | Optional | プレビューレンダリング | | `list_creatives` | Conditional | `has_creative_library: true` のとき必須 | | `sync_creatives` | Conditional | エージェントがクリエイティブアップロードを受け入れるとき必須 | | `get_creative_delivery` | Optional | バリアントレベルの配信メトリクス | クリエイティブエージェントは、自身が所有するフォーマットについてのみ権威あるフォーマット定義を返さなければなりません(MUST)。 **リファレンス**: [Creative 仕様](/docs/creative/specification) ## Signals Protocol ### シグナルエージェント | Task | Requirement | Notes | | ----------------- | ----------- | -------------------------- | | `get_signals` | Required | シグナルディスカバリー | | `activate_signal` | Required | 決定プラットフォーム上でのシグナルアクティベーション | シグナルエージェントは、プライベートシグナルとアクティベーションキーのアクセス制御を強制しなければなりません(MUST)。 **リファレンス**: [Signals 仕様](/docs/signals/specification) ## Brand Protocol ### ブランドエージェント | Task | Requirement | Notes | | -------------------- | ----------- | ----------------------------------- | | `get_brand_identity` | Required | コアアイデンティティは公開。認可された呼び出し元はより深いデータを得る | | `get_rights` | Conditional | エージェントがブランド権利を管理するとき必須 | | `acquire_rights` | Conditional | エージェントがブランド権利を管理するとき必須 | | `update_rights` | Conditional | エージェントがブランド権利を管理するとき必須 | | `creative_approval` | Conditional | エージェントが AI 生成コンテンツをレビューするとき必須 | ブランドエージェントは `supported_protocols` に `brand` を宣言しなければなりません(MUST)。 **リファレンス**: [Brand Protocol](/docs/brand-protocol) · [ブランドエージェントの構築](/docs/brand-protocol/building-a-brand-agent) ## Accounts Protocol ### アカウントを受け入れる任意のエージェント | Task | Requirement | Notes | | ------------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `sync_accounts` | Conditional | バイヤー宣言アカウント(`require_operator_auth: false`) — バイヤーが brand/operator ペアを宣言。将来の明示的ケイパビリティがプロビジョニングを宣言しない限り、アカウント ID 名前空間は設定更新にのみこれを使ってよい | | `list_accounts` | Conditional | アカウント ID 名前空間(`require_operator_auth: true`) — バイヤーがセラー割り当てアカウントを発見。シングルトン認証情報でも SDK が自動選択できるよう 1 行を返すべき(SHOULD) | | `sync_governance` | Conditional | バイヤーがキャンペーンガバナンスを使うとき必須 | | `report_usage` | Conditional | エージェントがサービスに課金するとき必須 | | `get_account_financials` | Optional | アカウントごとの財務サマリー | エージェントは、そのアカウントモデルに応じて `sync_accounts` または `list_accounts` の少なくとも一方を実装しなければなりません(MUST)。エージェントは両方を実装してもかまいません(MAY)(例: バイヤー側でバイヤー宣言アカウントを使い、基底のプラットフォームとアカウント ID 名前空間を使うアドネットワーク)。 **リファレンス**: [Accounts Protocol](/docs/accounts/overview) ## Governance: Campaign ### ガバナンスエージェント | Task | Requirement | Notes | | --------------------- | ----------- | ---------------- | | `sync_plans` | Required | プラン作成と修正 | | `check_governance` | Required | 購入、変更、配信フェーズでの検証 | | `report_plan_outcome` | Required | 成果レポートと予算コミットメント | | `get_plan_audit_logs` | Required | 監査証跡の取得 | **リファレンス**: [キャンペーンガバナンス仕様](/docs/governance/campaign/specification) ## Governance: Property ### ガバナンスエージェント | Task | Requirement | Notes | | ---------------------------- | ----------- | ------------------ | | `create_property_list` | Required | フィルター付きリスト作成 | | `get_property_list` | Required | 解決済みプロパティの取得 | | `update_property_list` | Required | リスト変更 | | `delete_property_list` | Required | リスト削除 | | `list_property_lists` | Required | リスト列挙 | | `validate_property_delivery` | Optional | キャンペーン後のコンプライアンス検証 | **リファレンス**: [プロパティガバナンス仕様](/docs/governance/property/specification) ## Governance: Collection ### ガバナンスエージェント | Task | Requirement | Notes | | ------------------------ | ----------- | ---------------------- | | `create_collection_list` | Required | コレクションソースとフィルター付きリスト作成 | | `get_collection_list` | Required | 解決済みコレクションの取得 | | `update_collection_list` | Required | リスト変更 | | `list_collection_lists` | Required | リスト列挙 | | `delete_collection_list` | Required | リスト削除 | **リファレンス**: [コレクションガバナンス](/docs/governance/collection) ## Governance: Content Standards ### コンテンツ標準エージェント | Task | Requirement | Notes | | --------------------------- | ----------- | ------------------ | | `create_content_standards` | Required | 標準の作成 | | `get_content_standards` | Required | 標準の取得 | | `list_content_standards` | Required | 標準の列挙 | | `update_content_standards` | Required | 標準の変更 | | `calibrate_content` | Optional | 標準に対するセラーキャリブレーション | | `validate_content_delivery` | Optional | 配信後のコンテンツコンプライアンス | | `get_media_buy_artifacts` | Optional | 検証用のアーティファクト取得 | **リファレンス**: [コンテンツ標準](/docs/governance/content-standards) ## Governance: Creative ### クリエイティブガバナンスエージェント | Task | Requirement | Notes | | ----------------------- | ----------- | ------------------ | | `get_creative_features` | Required | セキュリティスキャン、コンテンツ分類 | **リファレンス**: [クリエイティブガバナンス](/docs/governance/creative) ## Sponsored Intelligence Protocol ### ブランドエージェント | Task | Requirement | Notes | | ---------------------- | ----------- | ------------------- | | `si_get_offering` | Optional | セッション前のオファリングルックアップ | | `si_initiate_session` | Required | 同意を伴うセッション作成 | | `si_send_message` | Required | 会話メッセージング | | `si_terminate_session` | Required | セッションのクリーンアップ | ブランドエージェントは `supported_protocols` に `sponsored_intelligence` を宣言しなければなりません(MUST)。 **リファレンス**: [Sponsored Intelligence 仕様](/docs/sponsored-intelligence/specification) ## Trusted Match Protocol TMP は異なる通信モデル(MCP/A2A タスクではなく直接 HTTP)を使います。メッセージタイプと適合性要件については [TMP 仕様](/docs/trusted-match/specification) を参照。 # スナップショットとログ Source: https://adcp-docs-ja.pier1.co.jp/docs/protocol/snapshot-and-log すべての読み取り API をそのプッシュチャネルに結びつけるコントラクト — スナップショットとは何か、ログとは何か、それらがどう id 空間を共有するか、そしてなぜスナップショットのプルが AdCP がコミットする唯一のリプレイプリミティブなのか。 # スナップショットとログ AdCP が公開するすべての状態サーフェスは 2 つの顔を持ちます: `get_*` タスクから読まれる**スナップショット**と、登録された webhook URL に対して発火するプッシュイベントの**ログ**です。スナップショットは*今何が真か*を言います。ログは*何が、いつ、どの id で発火したか*を言います。このページは、それらを整合的に保つコントラクトです。 AdCP タスクを呼び出すためにこのページを読む必要はありません。webhook レシーバーを構築する、新しい通知タイプを提案する、またはイベント欠落シナリオがバイヤー側のバグではなく仕様のギャップだと論じるためには、読む必要があります。 ## The two faces ### Snapshot 読み取り API に公開される現在の真実: * `get_media_buys` は各バイの `status`、`health`、オープンな `impairments[]`、`webhook_activity[]` を返す。 * `list_creatives` は各クリエイティブの `status` を返す。 * `sync_audiences`(変更なし)は各オーディエンスの現在の `status` を返す。 * `get_event_source_health` は各ソースの現在の `assessment-status` を返す。 スナップショットは常に再読み取り可能です。履歴を運びません — 読み取りの瞬間に真であるものだけです。 ### Log バイヤーの登録された webhook URL に発火するプッシュイベントのストリーム: * 配信レポートの発火(`notification_type: scheduled | final | delayed | adjusted`)。 * 依存関係の障害の発火(`notification_type: impairment`)。 * 将来のイベントタイプは同じ方法で追加される: 新しい `notification-type` 値、定義されたペイロード、同じ配信コントラクト。 各イベントは安定した `notification_id` を運び、スナップショット上で可視の変更に対応します。 ## The five rules これらのルールは、プロトコル内のすべてのスナップショット/ログペアにわたって適用されます。新しい通知タイプを構築しているなら、その設計は 5 つすべてを満たさなければなりません。 ### 1. Two distinct ids: per-fire and per-state **トランスポートのリトライは `idempotency_key` で重複排除する。発火を状態に相関させるのは `notification_id` で行う。** これらは同じ発火上の異なる id です — レシーバーは両方を追跡しなければなりません(MUST)。 * **`idempotency_key`** — トランスポート層、**配信試行ごと**。各発火についてセラーが発行する。レシーバーはこれで重複排除し、同じ論理的発火のリトライを抑制する。[webhooks トランスポートコントラクト](/docs/building/by-layer/L3/webhooks) で定義。 * **`notification_id`** — イベント層、**状態イベントごと**。同じ論理的イベントの再発火をまたいで安定。状態形状のイベントについては、これはリソースの安定 id に等しい(例: `impairment_id` は障害イベントの `notification_id`)。[`mcp-webhook-payload.json`](https://adcontextprotocol.org/schemas/v3/core/mcp-webhook-payload.json) でエンベロープレベルに型付けされ、タイプごとの投入は [`notification-type.json`](https://adcontextprotocol.org/schemas/v3/enums/notification-type.json) の enumDescriptions で文書化される。永続的な状態 id を持たないポイントインタイムのデータイベント(例: 配信レポート発火)では欠落する。 分割は意図的です。同じ `idempotency_key` を 2 回見るレシーバーはトランスポートのリトライを観測しています — 興味を引かない、重複排除して先に進む。**異なる** `idempotency_key` の下で同じ `notification_id` を 2 回見るレシーバーは再発火を観測しています — シグナルです。セラーは繰り返しています。通常は、バイヤーのレシーバーが十分に長く到達不能で、セラーが状態が配信されたことを確認したいからです。それはレシーバーが折り畳むべきでない、イベント欠落の警告です。 状態形状のイベント(障害、ライフサイクル)については、状態ごとの id はリソース id です。ポイントインタイムのデータイベント(配信レポート発火)については、永続的な状態 id はありません — 発火ごとの `idempotency_key` がすべてです。その非対称性は、下のルール 4 の限界について正直です。 ### 2. Every push event corresponds to a snapshot delta webhook 専用の状態はありません。webhook が `notification_type: impairment` で発火すると、影響を受けたメディアバイの `impairments[]` は次の読み取りでその障害を示します。配信レポートが発火すると、次の `get_media_buy_delivery` は同じレポートウィンドウを反映します。プッシュチャネルは、読み取り API から利用できない情報を運びません。 このルールは、対応する読み取り可能な状態なしに一時的なシグナルとしてのみ存在するプッシュイベント — 「X を知りたいかもしれない」 — を排除します。状態を変えない提案をサーフェスしたいなら、webhook ではなくプルツールを構築してください。 ### 3. Push is at-least-once; the snapshot is authoritative プッシュとスナップショットが不一致のとき、スナップショットが勝ちます。重複した webhook 発火(同じ `notification_id`)は、at-least-once 配信下で期待される動作です — バイヤーエージェントは重複排除して続けます。古い webhook 発火(リソースが先に進んだため、プッシュがスナップショットがもはや反映しない状態をレポートする)も期待されます — バイヤーエージェントはプッシュペイロードに基づいて行動するのではなく、スナップショットを再読み取りします。 これが、レシーバーがプッシュ上で不可逆な行動を取る前にスナップショットに対して検証しなければならない(MUST)理由です。 ### 4. Either path is complete webhook を使うバイヤーは確実にすべてのデータを得ます。GET のみを使う(webhook なし)バイヤーは同じデータを得ます。2 つの経路は内容と粒度において同等です。バイヤーはレイテンシー、人間工学、レシーバーインフラに基づいて選びます。 このルールには 2 つの半分があります: * **状態イベント**(障害、ライフサイクル、ステータス変更)について: GET は現在の状態を返す。webhook を逃したバイヤーは `get_*` を呼んでスナップショットを読む — リカバリーは無損失。✅ 今日成立。 * **データを運ぶイベント**(配信レポート発火、個別のログイベント)について: GET は、セラーが `reporting_capabilities.windowed_pull_granularities` で宣言するすべての粒度でウィンドウ付きプルを尊重しなければならず(MUST)、その粒度で webhook が配信するのと同じウィンドウ化で行う。`["hourly", "daily"]` を宣言するセラーは、`get_media_buy_delivery` で時間別と日別のウィンドウ付きプルを尊重しなければならない(MUST)(`time_granularity` + `include_window_breakdown: true` 経由)。スライスペイロードは、それが置き換えられたであろう webhook 発火と形状整合します。セラーは、プルに公開するより高頻度の webhook を発してもよい(MAY) — ストリームタップアーキテクチャで一般的で、webhook が Kafka タップで、履歴読み取りがより粗い粒度のウェアハウスを通る場合です。その場合、バイヤーはケイパビリティを通じて事前に、より高頻度ではプルリカバリーが利用できないことを知り、それについては webhook をプライマリとして扱います。 2 経路等価のコントラクトは、各セラーの**宣言された**同等集合内で成立します。セラーは集合について正直でなければなりません(MUST): GET サーフェスが実際に webhook ペイロードを再現できるすべての粒度を宣言する、それ以上でもそれ以下でもなく。ある粒度を宣言するがその粒度でのプルを拒否するセラーはルール 4 に違反しています。ある粒度を省略するセラーはその頻度で 2 経路同等をオプトアウトしており、問題ありません。宣言された集合外のプルは、ケイパビリティをエコーする `error.details.supported_granularities` を伴う `UNSUPPORTED_GRANULARITY` を返します。 2 経路等価がなければ、AdCP は一部のチャネルでは pub/sub、他ではREST になります — コントラクトに対して構築するバイヤーは、どのモデルがどこに適用されるかを知らなければなりません。それがあれば、両経路は等価です: バイヤーはレイテンシーのために webhook を、シンプルさのためにポーリングを選び、どちらの経路でも同じデータを得ます。 ### 5. Push events and log entries share an id space `webhook_activity[]` を通じてサーフェスされる webhook 配信は、バイヤーがプッシュボディで受け取ったのと同じ `notification_id` を参照します。バイヤーは「私は発火 X を受け取った」を「セラーのログは発火 X を示す」と、2 つの名前空間をまたいだ帳簿付けなしに相関できます。同様に、`impairments[]` で参照される `impairment_id` は、それを告知したプッシュの `notification_id` と一致します。 ## Webhook activity log pattern ルール 5 のトランスポートの半分。スナップショット読み取り API を公開し、それに関連する webhook 発火を持つ任意の AdCP リソースは、その読み取り API 上に `webhook_activity[]` 配列もサーフェスしてもよい(MAY) — 呼び出し元プリンシパルにスコープされた、最近の発火ごとのトランスポートレコードで、発火が着地しなかったときやリトライの軌跡が怪しく見えるときのバイヤー側デバッグに有用です。このセクションは、そのサーフェスを採用する任意のリソースが従わなければならない(MUST)コントラクトです。 ### Canonical record shape レコード形状は [`/schemas/core/webhook-activity-record.json`](https://adcontextprotocol.org/schemas/v3/core/webhook-activity-record.json) で固定されています。このサーフェスを採用する読み取りスキーマは、それをインライン化するのではなく正準レコードを `$ref` しなければなりません(MUST) — 形状は意図的にリソースをまたいで一様であり、バイヤーのデバッグツールがリソース固有のパースなしに任意の読み取り API から `webhook_activity[]` を消費できます。 各レコードは、`idempotency_key`(ルール 5 によりペイロードの `idempotency_key` に等しい — 並行する `delivery_id` はない)、`subscriber_id`(#3009 マルチサブスクライバー用に予約)、`fired_at`、`completed_at`、`notification_type`、`sequence_number`、`attempt`(1 始まり、試行ごとに 1 レコード)、`status`(`success` / `failed` / `timeout` / `connection_error` / `pending`)、`url`(クエリ文字列とフラグメントを除去、秘密形状のパスセグメントを編集)、`http_status_code`、`response_time_ms`、`payload_size_bytes`、`error_message`(サーバー側の分類のみ — リクエスト/レスポンスのボディやヘッダーは決して含まない)を運びます。 ### Request-field convention `webhook_activity[]` をサーフェスする読み取りスキーマは、呼び出し元がリソースをまたいで一様にオプトインできるよう、同じ 2 つのリクエストフィールド名を使わなければなりません(MUST): * **`include_webhook_activity`** — boolean、デフォルト `false`。true のとき、セラーは各アイテムに `webhook_activity[]` 配列を返してもよい(MAY)(下記の 3 状態存在セマンティクスに従う)。 * **`webhook_activity_limit`** — integer、範囲 1–200、デフォルト 50。返されるレコードのアイテムごとの上限、最新順。 ### Scoping (normative) `webhook_activity[]` は**呼び出し元プリンシパル**にスコープされなければなりません(MUST)。複数のプリンシパルがアカウントレベルのアクセスを通じて同じリソースへの可視性を共有するとき、各プリンシパルは自身の登録エンドポイントをターゲットにする発火のみを見ます。これはプッシュ配信自体に適用されるのと同じスコーピングルールです。 ### Retention (normative) `webhook_activity[]` をサーフェスするセラーは、各レコードの `completed_at` から少なくとも 30 日間レコードを保持しなければなりません(**MUST**)。これはすべての終端ステータスに一様に適用されます — `success`、`failed`、`timeout`、`connection_error` はすべて `completed_at` を投入し(`timeout` と `connection_error` については、セラーが試行を終端と宣言した瞬間)、30 日の時計はそこから走ります。まだ `pending` ステータスのレコード(試行が飛行中またはリトライキューイング中、`completed_at` は null)については、時計は試行が終端になるまで `fired_at` から走り、その後 `completed_at` から 30 日に遷移します — したがってリトライの軌跡は、最初の発火が 29 日前に起こったという理由だけで飛行中に期限切れになりません。 30 日の下限はハードコントラクトです — それを尊重できないセラーは、より短いウィンドウを返すのではなくフィールドを完全に省略しなければなりません(MUST)(下記の 3 状態存在を参照)。これはバイヤーに、デバッグツールを構築できる単一の保持保証を与え、薄いストレージのセラーには、仕様がセラーごとの保持下限を交渉することを強いるのではなく、3 状態セマンティクスによるクリーンなオプトアウトを与えます。 ### Three-state presence semantics | State | Meaning | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | フィールド**省略** | セラーはこのリソースについて webhook アクティビティをサーフェスしない。原因はリソース固有(下記「採用チェックリスト」を参照)だが、通常は次を含む: セラーが発火履歴を永続化しない、リソースに呼び出し元プリンシパルの登録された webhook エンドポイントがない、セラーの宣言されたケイパビリティサーフェスが該当する通知タイプの webhook チャネルを除外する。バイヤーは省略から「発火は起こらなかった」を推論してはならない(MUST NOT)。 | | 空配列 `[]` | セラーは発火履歴を永続化するが、このプリンシパルについて最近何も発火していない。 | | 非空配列 | 実際の発火レコード、最新順。 | セラーはこれらを単一の状態に折り畳んではなりません(MUST NOT)。`include_webhook_activity: true` によるオプトインは、セラーの本質的なケイパビリティを上書きしません — 保持下限を満たせないセラーは、リクエストにかかわらず省略を返します。 予期しない省略を診断するバイヤーは、オペレーターの助けを必要とせずに原因を判別する、容易に観測可能な 2 つのシグナルを持ちます: (1) リソースについての自身の `push_notification_config` 登録状態(「登録エンドポイントなし」を除外)、(2) セラーのケイパビリティ宣言(「ケイパビリティサーフェスがチャネルを除外」を除外)。両方が確認できたとき、「セラーが発火履歴を永続化しない」が残る原因であり、それ以上のプロトコル側の修正は利用できません — エスカレートしてください。 ### Record cardinality 試行ごとに 1 レコード。成功した初回試行の発火は、`attempt: 1` の単一レコードとして現れます。3 試行のリトライ軌跡(例: 2 回失敗して 1 回成功)は、`idempotency_key` を共有する 3 レコードとして現れます — 軌跡は、バイヤーがそのキーでレコードをグループ化して再構成します。 ### Privacy * `url` はクエリ文字列とフラグメントを除去しなければならず(MUST)、高エントロピー / トークン形状のパスセグメントはさらに編集すべきです(SHOULD)。 * `error_message` はサーバー側の分類文字列のみです — リクエストヘッダー、レスポンスボディ、バイヤーエンドポイントのスタックトレースは決して含みません。 * リクエストとレスポンスのボディは基本サーフェスの範囲外です。将来の `include_webhook_payloads` 拡張が、より厳格なアクセス制御の下でそれらを追加するかもしれず、ボディが設定された上限を超えるとき `/schemas/core/truncation-sentinel.json` の [ユニバーサル切り詰めセンチネル](https://adcontextprotocol.org/schemas/v3/core/truncation-sentinel.json) を使うでしょう。 ### Adoption checklist `webhook_activity[]` を採用するリソースは、次のすべてを満たさなければなりません(MUST)。リストは「MUST」フックが曖昧でないよう意図的に明示的です。このリストにないものはすべて採用者の裁量です(例: 1–200 範囲内のリソースごとのカーディナリティ調整)。 1. **通知チャネル(前提条件)。** 採用には該当する発火タイプの登録された通知チャネルが必要。メディアバイは今日、バイごとの `push_notification_config`(および関連する `reporting_webhook`)によってこれを満たす。任意の単一バイより長生きするリソース — クリエイティブ、オーディエンス、プロパティ、アカウントレベルのガバナンス — は、**#4582 track 3 で定義されるアカウントごとのサブスクリプションモデル**(3.2.0 で予定)を待つ。2 つは同じ前提条件を満たす異なるプリミティブ: バイにアタッチされたバイスコープの設定 blob と、アカウントスコープのサブスクリプションリソース。チャネルなしには `webhook_activity[]` がログする発火はなく、この項目は下の他のすべてのルールをゲートする。採用者は呼び出し元ドキュメントで特定のチャネルを引用しなければならない(MUST)。 2. **レコード形状。** アイテムスキーマは `/schemas/core/webhook-activity-record.json` を `$ref` しなければならない(MUST)。リソース固有の相互参照(例: レコードがアカウントレベルの読み取り内にネストされるときの親リソース id)は、トップレベルのレコードフィールドとしてではなく、正準レコードの `ext` エンベロープに置く。 3. **リクエストフィールド。** オプトインフィールド名は `include_webhook_activity`(boolean、デフォルト `false`)と `webhook_activity_limit`(integer、1–200、デフォルト 50)でなければならない(MUST)。200 の上限は正準の上限。採用者はリソースごとに最大値を狭めてもよい(MAY)が、200 を超えたりフィールドをリネームしたりしてはならない(MUST NOT)。 4. **スコーピング。** 上記 § Scoping に従い、呼び出し元プリンシパルのみでなければならない(MUST)。 5. **保持下限。** 上記 § Retention に従い、30 日の下限を尊重しなければならない(MUST)。ピボット(`completed_at`、`pending` の除外付き)はリソース間で同じ。 6. **3 状態存在カーディナリティ。** 省略 / `[]` / 非空が 3 状態。採用者はそれらを折り畳んではならない(MUST NOT)。 7. **ケイパビリティゲート。** 採用者は、どのリソース固有のケイパビリティ宣言がフィールドをゲートするかを文書化しなければならない(MUST)(メディアバイについては `webhook` を含む `capabilities.media_buy.propagation_surfaces`)。「フィールド省略」状態の特定の*原因*はリソース固有であり、採用者は呼び出し元ドキュメントでそれらを列挙しなければならない(MUST)。カーディナリティと、省略が「発火は起こらなかった」ではないというルールは普遍的。 8. **通知タイプレジストリ。** webhook 発火が [`/schemas/enums/notification-type.json`](https://adcontextprotocol.org/schemas/v3/enums/notification-type.json) にない通知タイプを運ぶ採用者は、正準レコードに並行 enum を鋳造するのではなく、それらのタイプをその共有 enum に追加しなければならない(MUST)。enum はクロスリソースレジストリ。 ### Consumers and the dependency chain #### Today (3.1) * `get_media_buys.media_buys[].webhook_activity[]` — このパターンの最初で現在唯一のコンシューマー。通知チャネルは既存のバイごとの `push_notification_config` なので、チェックリストの項目 1 は新しいプリミティブなしで満たされます。ケイパビリティゲート: フィールドがバイにサーフェスされるには `capabilities.media_buy.propagation_surfaces` が `webhook` を含まなければならない。呼び出し元ドキュメントについては [get\_media\_buys § Webhook activity](/docs/media-buy/task-reference/get_media_buys#webhook-activity) を、このサーフェスがデバッグするトランスポート側のルールについては [永続的 webhook コントラクト](/docs/building/by-layer/L3/webhooks#persistent-channel-contract) を参照。 #### Account-level adopters (3.1) 単一のメディアバイより長生きするリソースは、プッシュチャネルを任意の 1 つのバイではなくアカウントに登録します。アカウントレベルのサーフェスは `notification_configs[]` です — [`sync_accounts`](/docs/accounts/tasks/sync_accounts#account-level-webhook-subscriptions) に運ばれ `list_accounts` にエコーされる、サブスクライバーごとの登録の配列。各エントリは `event_types[]` でフィルタリングするため、サブスクライバーは自身のエンドポイントが扱うタイプのみを受け取り、異なる `subscriber_id` を持つ複数のエントリが単一のイベントを複数のエンドポイントにファンアウトします(マルチサブスクライバー合成)。 * **[#2261](https://github.com/adcontextprotocol/adcp/issues/2261) クリエイティブライフサイクル webhook** — `list_creatives.creatives[].webhook_activity[]` はこのパターンの 2 番目のコンシューマー。通知チャネルはアカウントの `notification_configs[]` 集合で、プロビジョニングまたは設定更新モードで `sync_accounts` 経由で登録される。サポートされるイベントタイプとタイプごとの合体ウィンドウは `get_adcp_capabilities` 経由で宣言される。2 つのクリエイティブライフサイクルイベントタイプ — `creative.status_changed` と `creative.purged` — はメディアバイ webhook アクティビティと同じレコード形状と保持ルールを共有する。親クリエイティブは曖昧でないので、内部レコードで `ext.creative_id` は省略してもよい(MAY)。呼び出し元ドキュメントについては [list\_creatives § Webhook activity](/docs/creative/task-reference/list_creatives#webhook-activity) を参照。 * **バイより長生きする他のリソース** — [#1711](https://github.com/adcontextprotocol/adcp/issues/1711) の下のオーディエンス、プロパティ、アカウントレベルのコンプライアンス — は同じチェーンに従う: `sync_accounts.accounts[].notification_configs[]` 経由でサブスクライブし、リソースの `list_` タスクで `webhook_activity[]` 読み取りを採用する。これらはオープンな RFC。 **ハードパージのためのルール 4 の除外。** `purge_kind: hard` を伴う `creative.purged`(法的消去のみ — GDPR 第 17 条、CCPA 削除、裁判所命令)は、ルール 4 の唯一の認可された例外です: webhook 発火に対応するスナップショットデルタがない、なぜならセラーはトゥームストーンを保持してはならない(MUST NOT)からです。ハードパージ発火を逃したバイヤーは読み取り側のリカバリーを持ちません。それはプロトコルのギャップではなく、法制度の設計上の制約です。ソフトパージは `list_creatives`(`include_purged: true` 付き)にトゥームストーンを保持し、ルール 4 準拠のままです。 採用者は、通知チャネルがバイごとかアカウントごとかにかかわらず、このチェックリストにそのまま従います。 ## What this rules out * **状態を変えない提案のためのプッシュチャネル。** 「セラーがあなたに X を知ってほしい」が読み取り可能なフィールドに対応しないなら、それはスナップショット/ログイベントではありません。代わりにプルツールを構築してください。(advisory epic を参照。) * **過去の webhook を再発火するリプレイツール。** スナップショット読み取りがリプレイです。リプレイツールはオペレーター側のデバッグ機能であり、バイヤー向けのプロトコルコントラクトの一部ではありません。 * **バイごとのプッシュでのイベントごとのサブスクリプションフィルタリング。** メディアバイに `push_notification_config` を登録するバイヤーは、そのバイに対して発火するすべてのイベントタイプを受け取ります。レシーバーでのフィルタリングは問題ありません。バイごとのプロトコルサーフェスでのフィルタリングは範囲外です。アカウントレベルのサブスクリプション(`notification_configs[]`)は例外です — それらは登録時に `event_types` でフィルタリングします。なぜならアカウントレベルのサーフェスは異種(クリエイティブイベント、将来のオーディエンス/プロパティイベント)で、クリエイティブイベントのみを扱うエンドポイントは、そうでなければ解釈できないシグナルを強制的に供給されるからです。 * **「私の webhook を受け取りましたか?」の確認ステップ。** レシーバーは HTTP 2xx で確認します。送信者は [永続的 webhook コントラクト](/docs/building/by-layer/L3/webhooks#persistent-channel-contract) に従って非 2xx でリトライします。セラーは受領のためにバイヤーをポーリングしません。 ## Where the surface doesn't yet follow this * **配信レポート**(`scheduled` / `final` / `delayed` / `adjusted`)はこのコントラクトに先行します。ルール 4 は 3.1 で 2 つのサーフェスを通じてそれらについて閉じます: * **ウィンドウごとのデータ同等** — `get_media_buy_delivery` は `time_granularity` + `include_window_breakdown: true` を受け入れ、同じ粒度で `reporting_webhook` ペイロードと形状整合する `media_buy_deliveries[].windows[]` スライスを返す。`reporting_capabilities.windowed_pull_granularities` 経由でケイパビリティスコープ。宣言された集合外のプルは `UNSUPPORTED_GRANULARITY` を返す。#4590 で着地。 * **発火ごとのトランスポートログ** — ウィンドウごとの同等があっても、webhook 配信をデバッグするバイヤーは、どの発火がいつ自身のエンドポイントに当たったかを見たい。`get_media_buys` の `webhook_activity[]` サーフェス([#4278](https://github.com/adcontextprotocol/adcp/issues/4278))がトランスポート層の可観測性についてこれを閉じる。それは上記の [webhook activity log pattern](#webhook-activity-log-pattern) の最初のコンシューマー。パターンを採用する将来のリソースは、同じレコード形状、保持下限、3 状態存在セマンティクスに従う。 * **オーディエンスとプロパティのライフサイクル webhook** — クリエイティブライフサイクル webhook は今や [#2261](https://github.com/adcontextprotocol/adcp/issues/2261)(アカウントレベルの `notification_configs[]` + `list_creatives.webhook_activity[]`)経由でこのパターンを採用します。バイのスコープ外のオーディエンス停止とプロパティの公開停止はオープンなままです — それらが着地するまで、スナップショットの半分(新しい `sync_audiences` またはプロパティクロール)が、アクティブなバイに現在参照されていないときのそれらのリソースへの変更の唯一の信頼できるシグナルです。 ## When you'd be right to push back このセクションは非規範的です。例外を上げることが妥当なときを記述するもので、認可されるときを記述するものではありません。 ユースケースが、スナップショットの半分を持たないイベントを本当に必要とするとき — ポーリングコストが支配的でリカバリーが重要でない高頻度シグナル(例: メトリクスストリーム)。AdCP は今日そのようなものを持ちません。それを提案しているなら、明示的に名指しし、なぜスナップショット経由のプルが適合しないかを論じてください。レビュアーはそれを、このページがコミットするコントラクトと秤にかけます。 ## Related * [プッシュ通知](/docs/building/by-layer/L3/webhooks) — このページが乗るトランスポートコントラクト。 * [メディアバイライフサイクル](/docs/media-buy/media-buys/lifecycle) — スナップショット/ログを `status` + `health` + `impairments[]` に適用。 # クイックスタート Source: https://adcp-docs-ja.pier1.co.jp/docs/quickstart 自分の役割を選びましょう——バイヤーは5分で AdCP エージェントを呼び出し、パブリッシャーとセラーは自前のエージェントを立ち上げます。 自分の役割を選びましょう。バイヤーは公開テストエージェントを5分で呼び出せます。パブリッシャーとセラーは、バイヤーが呼び出せるエージェントを立ち上げます。 バイヤー側。このページの残りは公開テストエージェントの呼び出しを解説します——サインアップ不要、コピー&ペーストできる curl。 パブリッシャーまたはセラー側。バイヤーが呼び出せるエージェントを立ち上げます。 ## セットアップ 公開テストトークンを使えばすぐに始められます——サインアップは不要です: ```bash theme={null} export ADCP_AUTH_TOKEN="1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ" export AGENT_URL="https://test-agent.adcontextprotocol.org/sales/mcp" ``` テストエージェントはパスルーティングされています: `/sales/mcp` はメディアバイのツール(このクイックスタートのパス)を提供し、兄弟 URL が他の専門領域を提供します——`/signals/mcp`、`/governance/mcp`、`/creative/mcp`、`/creative-builder/mcp`、`/brand/mcp`。テナントとツールの完全な一覧は [`/.well-known/adagents.json`](https://test-agent.adcontextprotocol.org/.well-known/adagents.json) を参照してください。 組織スコープで利用状況を追跡できる自分専用の API キーは、[AAO ダッシュボード](https://agenticadvertising.org/dashboard/api-keys)で作成できます。 ## 1. プロダクトをディスカバリーする MCP 上の AdCP は JSON-RPC 2.0 を使用します。トランスポートは [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http) で、レスポンスは server-sent events として届きます。 ```bash theme={null} curl -X POST $AGENT_URL \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Authorization: Bearer $ADCP_AUTH_TOKEN" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_products", "arguments": { "brief": "Video ads for pet food brand", "brand": { "domain": "premiumpetfoods.com" } } } }' ``` **レスポンス**(見やすさのため SSE エンベロープは省略): ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "{\"products\":[{\"product_id\":\"pinnacle_news_video_premium\",\"name\":\"Pinnacle News Group video guaranteed\",\"channels\":[\"olv\",\"ctv\"],\"pricing_options\":[{\"pricing_option_id\":\"pinnacle_news_video_premium_pricing_0\",\"pricing_model\":\"cpm\",\"currency\":\"USD\",\"fixed_price\":15}],\"delivery_type\":\"guaranteed\"}, ...],\"sandbox\":true}" } ] } } ``` **結果を取り出す**——AdCP のペイロードは `content[0].text` の中に JSON エンコードされています: ```javascript theme={null} const response = /* parsed JSON-RPC response */; const payload = JSON.parse(response.result.content[0].text); console.log(payload.products[0].product_id); // "pinnacle_news_video_premium" console.log(payload.products[0].channels); // ["olv", "ctv"] console.log(payload.products[0].pricing_options[0].pricing_option_id); // "pinnacle_news_video_premium_pricing_0" console.log(payload.products[0].pricing_options[0].fixed_price); // 15 ``` ## 2. エラーを処理する 無効なツール名を送って、エラーがどのように見えるか確認します: ```bash theme={null} curl -X POST $AGENT_URL \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Authorization: Bearer $ADCP_AUTH_TOKEN" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "nonexistent_tool", "arguments": {} } }' ``` **レスポンス:** ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "{\"code\":\"INVALID_REQUEST\",\"message\":\"Unknown tool: nonexistent_tool\"}" } ], "isError": true } } ``` **処理する**——`isError` を確認してから、エラーペイロードをパースします: ```javascript theme={null} const response = /* parsed JSON-RPC response */; if (response.result.isError) { const err = JSON.parse(response.result.content[0].text); console.log(err.code); // "INVALID_REQUEST" console.log(err.message); // "Unknown tool: nonexistent_tool" } ``` 主なエラーコード: `INVALID_REQUEST`(不正な入力)、`RATE_LIMITED`(バックオフしてリトライ)、`UNAUTHORIZED`(認証情報を確認)。 ## 3. メディアバイを(冪等に)作成する ステップ1のプロダクト ID を使ってキャンペーンを作成します。すべての変更を伴うリクエストは `idempotency_key`——リトライを安全にするクライアント生成の UUID v4——を必ず含めなければなりません。同じキーを同じペイロードで送ると、セラーは重複した購入を作成する代わりに元の結果を返します: ```bash theme={null} export IDEMPOTENCY_KEY="$(uuidgen | tr '[:upper:]' '[:lower:]')" curl -X POST $AGENT_URL \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Authorization: Bearer $ADCP_AUTH_TOKEN" \ -d "{ \"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"tools/call\", \"params\": { \"name\": \"create_media_buy\", \"arguments\": { \"idempotency_key\": \"$IDEMPOTENCY_KEY\", \"account\": { \"account_id\": \"test_account\" }, \"brand\": { \"domain\": \"premiumpetfoods.com\" }, \"start_time\": \"asap\", \"end_time\": \"2026-04-30T00:00:00Z\", \"packages\": [{ \"product_id\": \"pinnacle_news_video_premium\", \"budget\": 5000, \"pricing_option_id\": \"pinnacle_news_video_premium_pricing_0\" }] } } }" ``` 同じリクエスト(同じキー、同じペイロード)を再送すると、セラーは `replayed: true` を付けて元のレスポンスを返します。同じキーを異なるペイロードで送ると `IDEMPOTENCY_CONFLICT` になります。セラーの有効期間は `get_adcp_capabilities` で確認できます: ```json theme={null} { "idempotency": { "supported": true, "replay_ttl_seconds": 86400 } } ``` `IDEMPOTENCY_CONFLICT`、`IDEMPOTENCY_EXPIRED`、および AdCP Verified エージェント向けの UUID v4 ガイダンスを含む完全なリトライモデルは、[セキュリティガイド](/docs/building/by-layer/L1/security)を参照してください。 **レスポンス**(ID は呼び出しごとに異なります): ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "{\"media_buy_id\":\"mb_f4139524\",\"status\":\"active\",\"revision\":1,\"packages\":[{\"package_id\":\"pkg_3df649f0\",\"product_id\":\"pinnacle_news_video_premium\",\"budget\":5000,\"pricing_option_id\":\"pinnacle_news_video_premium_pricing_0\"}],\"valid_actions\":[\"pause\",\"cancel\",\"update_budget\",\"update_dates\",\"update_packages\",\"add_packages\",\"sync_creatives\"],\"sandbox\":true}" } ] } } ``` **結果を取り出す:** ```javascript theme={null} const response = /* parsed JSON-RPC response */; const buy = JSON.parse(response.result.content[0].text); console.log(buy.media_buy_id); // "mb_f4139524" console.log(buy.status); // "active" console.log(buy.packages[0].budget); // 5000 console.log(buy.valid_actions); // ["pause", "cancel", "update_budget", ...] ``` ## 4. プッシュ通知(署名付き Webhook) 本番のエージェントは長時間実行される操作について Webhook を送信します。AdCP 3.0 は、**エージェント間リクエストで使われるものと同じ RFC 9421 HTTP Message Signatures プロファイル**で Webhook に署名します——一つのベリファイア、一つの JWKS、一つのトラストサーフェス。共有 HMAC シークレットはありません。 エージェントにあなたの Webhook エンドポイントを指定し、あなたの JWKS を公開します。エージェントは自分が信頼する鍵で各 POST に署名します。あなたはエージェントの JWKS を取得し、ペイロードに基づいて動作する前に署名を検証します: ```json theme={null} { "name": "create_media_buy", "arguments": { "idempotency_key": "5c4c6f29-...", "account": { "account_id": "your_account" }, "brand": { "domain": "premiumpetfoods.com" }, "push_notification_config": { "url": "https://you.example.com/webhooks/adcp", "authentication": { "schemes": ["HTTP_MESSAGE_SIGNATURES"] } } } } ``` 操作が完了すると、エージェントはあなたの URL へ署名付きリクエストを POST します。ペイロードは自身の `idempotency_key` を持つため、受信側はリトライを重複排除できます: ```json theme={null} { "task_id": "task_456", "idempotency_key": "webhook_evt_8f2a...", "task_type": "create_media_buy", "status": "completed", "timestamp": "2026-04-22T10:30:00Z", "result": { "media_buy_id": "mb_12345", "packages": [{ "package_id": "pkg_001" }] } } ``` ペイロードを信頼する前に署名を検証します——`keyid` をセラーオペレーターの `brand.json` の `agents[].jwks_uri` 経由で解決し、パブリッシャーの `adagents.json` の `signing_keys[]` ピンがあれば適用し、AdCP の Webhook ベリファイアチェックリストを実行し、未知の鍵・期限切れの日付・一致しないダイジェストを型付きの `webhook_signature_*` 理由コードで拒否します: ```typescript theme={null} app.post('/webhooks/adcp/*', async (req, res) => { try { await verifyAdcpWebhookSignature(req, { sellerAgentUrl: req.sellerContext.agentUrl, requiredTag: 'adcp/webhook-signing/v1', allowedAlgs: ['ed25519', 'ecdsa-p256-sha256'], }); } catch (err) { return res.status(401) .setHeader('WWW-Authenticate', `Signature error="${err.code}"`) .end(); } const { idempotency_key } = req.body; if (await seen(idempotency_key)) return res.status(200).end(); await process(req.body); res.status(200).end(); }); ``` 必須ヘッダー、対象コンポーネント、nonce と date のウィンドウ、コンプライアンスランナーが実行するネガティブベクトルスイートを含む完全な検証プロファイルは、[セキュリティガイド](/docs/building/by-layer/L1/security)と [Webhook ガイド](/docs/building/by-layer/L3/webhooks)を参照してください。 ## クライアントライブラリを使う 上記の例は分かりやすさのために生の HTTP を使っています。実際には、SSE のパース、リトライ、認証を処理してくれる AdCP クライアントライブラリを使用します: ```bash theme={null} npm install @adcp/sdk # JavaScript/TypeScript pip install adcp # Python ``` ```javascript theme={null} import { ADCPMultiAgentClient } from '@adcp/sdk'; const client = new ADCPMultiAgentClient([{ id: 'test', name: 'Test Agent', agent_uri: 'https://test-agent.adcontextprotocol.org/sales/mcp', protocol: 'mcp', auth_token: process.env.ADCP_AUTH_TOKEN, }]); const result = await client.agent('test').getProducts({ brief: 'Video ads for pet food brand', brand: { domain: 'premiumpetfoods.com' }, }); console.log(result.data.products); ``` ## 次のステップ * **[エージェントを構築する](/docs/building/by-layer/L4/build-an-agent)** — skill ファイルを使って、コーディングエージェントでストーリーボード準拠のエージェントを生成する * **[エージェントを検証する](/docs/building/verification/validate-your-agent)** — ストーリーボードとコンプライアンスチェックでエージェントをテストする * **[コンプライアンスカタログ](/docs/building/verification/compliance-catalog)** — エージェントが主張できるドメインと専門領域、および各主張を検証するストーリーボード * **[MCP インテグレーションガイド](/docs/building/by-layer/L0/mcp-guide)** — トランスポート、セッション、認証の詳細 * **[A2A インテグレーションガイド](/docs/building/by-layer/L0/a2a-guide)** — ストリーミング、アーティファクト、プッシュ通知 * **[メディアバイのライフサイクル](/docs/media-buy/media-buys/lifecycle)** — 状態機械、順序付けられたフロー、保証付き取引の IO パス、クリエイティブ同期のタイミング * **[タスクリファレンス](/docs/media-buy/task-reference)** — テスト可能な例を備えた利用可能なすべてのタスク * **[エラーハンドリング](/docs/building/operating/transport-errors)** — エラーコード、リカバリー戦略 * **[認証](/docs/building/by-layer/L2/authentication)** — 本番の認証情報のセットアップ # 3.0 から 3.1 への移行 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/3-0-to-3-1 AdCP 3.0 統合を 3.1 安定リリースに移行するための焦点を絞ったアップグレードガイド。 # 3.0 から 3.1 への移行 **3.1 はリリース済みです。** 新しい 3.1 統合は、エージェントが `supported_versions` でそれをアドバタイズすることを確認した後に `"3.1"` をピン留めすべきです。既存の 3.0 統合は移行中 `"3.0"` にピン留めしたままでかまいません。 3.1 は 3.0 に対するマイナーリリースです。プロトコル変更は加算的です: 既存の 3.0 準拠エージェントは、リクエストやレスポンスの形状を変えずに `"3.0"` に留まれます。SDK、バイヤー、セラー、クリエイティブエージェント、シグナルエージェント、ブランドエージェント、またはコンプライアンスワークフローが 3.1 を主張または消費する準備をするとき、このガイドを使ってください。 機能の物語については [What's New in AdCP 3.1](/docs/reference/whats-new-in-3-1) から始めてください。完全なリリース記録については [リリースノート](/docs/reference/release-notes#version-3-1-0) を参照。 ## アップグレードチェックリスト | Step | Who | Action | | ---- | --------------- | ------------------------------------------------------------------------------------------------ | | 1 | 全員 | この統合が当面 `"3.0"` に留まるか `"3.1"` に移るかを決める。 | | 2 | SDK と手書きクライアント | すべてのリクエストに `adcp_version` リリースピンを追加する。`"3.1"` はそれをアドバタイズするエージェントにのみ送る。 | | 3 | セラーとエージェント | `get_adcp_capabilities.adcp.supported_versions` で受け入れるすべてのリリースをアドバタイズし、エンベロープルートで提供したリリースをエコーする。 | | 4 | バイヤー | `VERSION_UNSUPPORTED` をデコードし、セラーがアドバタイズしたバージョンに対してのみリトライする。 | | 5 | 手書きバイヤー | 3.1 動作を主張する前に、変更タスクと同様に読み取りタスクにも `idempotency_key` を追加する。 | | 6 | MCP と A2A アダプター | 未知のルートメンバーを拒否せずにエンベロープフィールドとトランスポートラッパーを受け入れる。 | | 7 | エラーハンドラー | `error.code` をオープン文字列として扱い、存在するとき `error.recovery` でディスパッチする。 | | 8 | コンプライアンスオペレーター | 3.1 ストーリーボードバンドルを実行し、3.1 バッジが発行されるまで 3.0 互換性ラインをグリーンに保つ。 | ## バージョンピン留め 3.0 統合はメジャーバージョンのみで交渉しました。3.1 はリリース精度ネゴシエーションを追加します: * 安定 3.0 トラフィックには `"3.0"` を使う。 * エージェントがアドバタイズした後、安定 3.1 トラフィックには `"3.1"` を使う。 * `"3.0.19"` のようなパッチ値や `"3.1.0-rc.15"` のような完全な semver プレリリースをワイヤー上で送らない。 セラーはサポートされないピンを `VERSION_UNSUPPORTED` で拒否し、`error.data.supported_versions` にサポートリリースリストを含めるべきです。バイヤーはマイナーリリースをまたいで黙ってダウンシフトすべきではありません。不一致をサーフェスするか、セラーが明示的にアドバタイズしたバージョンに対してリトライしてください。 ## 監査すべきランタイム変更 ### すべてのタスクの冪等性 3.0 は変更リクエストに `idempotency_key` を要求しました。3.1 は信頼性モデルをすべてのタスクに拡張し、リトライ、リプレイ、下流の再照合が一様に動作するようにします。SDK ユーザーはこの動作を SDK リリースから拾います。手書きバイヤーは、書き込みと同様に読み取りタスクにも UUID v4 冪等性キーを生成すべきです。 レスポンスが `replayed: true` とマークされたとき、それを元の操作の歴史的な結果として扱ってください。行動する前に新しい状態が必要な場合、リプレイされたレスポンスを処理した後に該当リソースを再読み取りしてください。 ### エンベロープ許容 3.1 は、トランスポートアダプターが AdCP エンベロープルートを許容することに依存します。MCP と A2A クライアントは、まずトランスポート固有のラッパーをアンラップし、次に `status`、`result`、`errors`、`adcp_version`、`replayed`、`context` などの AdCP フィールドを読むべきです。未知のエンベロープメンバーは拒否を引き起こしてはなりません。 ### エラーデコード 標準エラーカタログは 3.1 で拡張されましたが、`error.code` はオープン文字列のままです。クライアントは次をすべきです: * 未知のエラーコードを受け入れる。 * 存在するとき `error.recovery` を優先する。 * `error.recovery` が欠けているとき、未知のレガシーエラーに有界の transient フォールバックを適用する。 * `AUTH_MISSING` と `AUTH_INVALID` のあいだの auth 分割を扱う。 * `CREDENTIAL_IN_ARGS` を terminal として扱い、リトライ前に認証情報を適切なトランスポートチャネルに移す。 ### プロポーザルとアクションディスカバリーのクリーンアップ 3.1 は `proposal_status` をプロポーザルの真実の源泉にし、プロダクトとバイで構造化されたアクションディスカバリーを使います。セラーは GA 前の `requires_proposal` アクションモードを発すべきではありません。キャッシュされたプレリリースアクションメタデータを持つバイヤーは、それを無効化し、プロダクト、プロポーザル、バイのサーフェスを再読み取りすべきです。 ### ブランド検証署名 `verify_brand_claim` または `verify_brand_claims` を実装するブランドエージェントは、署名されたレスポンス証拠を返さなければなりません。ブランドごとのレスポンス署名鍵を公開し、署名をタスク、解決されたブランドテナント、応答エージェント URL、呼び出し元/リクエストハッシュ、有効期間にバインドしてください。 ### シグナルターゲティング シグナルアイデンティティは `SignalRef` に向かって移動し、プロダクトスコープの `included_signals`、`signal_targeting_options`、バイ時の `signal_targeting_groups` を伴います。所有シグナルディスカバリーとマーケットプレイスアクティベーションは別個のケイパビリティです。バイヤーは、エージェントがマーケットプレイスまたはアクティベーションサポートを宣言するときのみ `activate_signal` を呼ぶべきです。 ### クリエイティブフォーマットとトランスフォーマー クリエイティブエージェントは正準な `format_kind` 値を公開し、`list_transformers` を通じてビルドユニットを発見すべきです。フォーマット添付の入力/出力/価格宣言は、トランスフォーマースコープの設定と価格を優先して非推奨です。ホストされた音声/動画スロットは、固定 duration には `duration_ms_exact` を、有界または片側範囲には `duration_ms_range` を使うべきです。 ### レポートと課金 3.1 は配信と使用量の確定マーカー、リーチウィンドウセマンティクス、`viewability.viewed_seconds`、AdCP 外のクリエイティブ課金のための `BILLING_OUT_OF_BAND` エラーを追加します。バイヤーは、該当する確定フィールドが確定と言うまで配信数を確定として扱うのを避けるべきです。 ## ロールベースの移行 | Role | 3.1 を主張する前の最小限の準備 | | -------------- | ----------------------------------------------------------------------------------------------------------------------- | | SDK バイヤー | `adcp_version` を発し、セラーがサポートするとき `"3.1"` をピンし、冪等性キーを一貫して追加し、オープンエラーコードを受け入れる SDK にアップグレードする。 | | 手書きバイヤー | リリースピン、読み取りタスクの冪等性キー、エンベロープ許容、前方互換のエラーデコード、リプレイ処理を追加する。 | | セラー | `supported_versions` をアドバタイズし、提供したリリースをエコーし、エンベロープフィールドを許容し、サポートされないピンを `VERSION_UNSUPPORTED` で拒否し、冪等性/リプレイ/エラー動作を監査する。 | | クリエイティブエージェント | ディスカバリーを `list_transformers` に移し、正準フォーマットを公開し、レガシーフォーマット参照は互換性エイリアスとしてのみ保つ。 | | シグナルエージェント | 所有シグナルディスカバリーをマーケットプレイスアクティベーションから分離する。プロダクトスコープのターゲティングオプションは、バイヤーが選択できる場所でのみ公開する。 | | ブランドエージェント | レスポンス署名鍵を公開し、ブランド検証タスクに署名された証拠を返す。 | | コンプライアンスオペレーター | 3.1 ストーリーボードを実行し、3.0 互換性証拠を保持し、実際にテストされたリリースについてのみバージョンスコープのバッジを発行する。 | ## プレリリースアーティファクト 3.1 プレリリースアーティファクトは、検証中にそれらをピン留めした採用者のために利用可能なままですが、新しい統合は安定した `"3.1"` ワイヤー値を使うべきです。プレリリースに対して構築した場合、`requires_proposal` のようなプレリリースのみのフィールドを無効化し、現在のプロダクト/プロポーザル/アクションメタデータを再読み取りし、安定リリースを主張する前に 3.1 ストーリーボードバンドルを再実行してください。 ## 関連 * [What's New in AdCP 3.1](/docs/reference/whats-new-in-3-1) * [リリースノート](/docs/reference/release-notes#version-3-1-0) * [バージョンと互換性](/docs/reference/versions) * [バージョニングとガバナンス](/docs/reference/versioning) * [バージョン適応](/docs/building/cross-cutting/version-adaptation) # アトリビューションウィンドウの移行 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/attribution AdCP アトリビューションウィンドウを v2 から v3 に移行します。整数の日数を構造化された Duration オブジェクトに置き換え、必須のアトリビューションモデルフィールドを追加します。 # アトリビューションウィンドウの移行 AdCP 3.0 はアトリビューションウィンドウフィールドの名前を変更し、整数の日数を構造化された `Duration` オブジェクトに置き換える。アトリビューション `model` フィールドが必須になりました。 ## 変更内容 | v2 フィールド | v3 フィールド | 変更タイプ | | ----------------------- | ---------------------- | ------------ | | `click_window_days`(整数) | `post_click`(Duration) | 名前変更 + タイプ変更 | | `view_window_days`(整数) | `post_view`(Duration) | 名前変更 + タイプ変更 | | `model`(必須) | `model`(必須) | 変更なし | *** ## アトリビューションウィンドウオブジェクト ### 変更前(v2) ```json theme={null} { "attribution_window": { "click_window_days": 7, "view_window_days": 1, "model": "last_touch" } } ``` ### 変更後(v3) ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/attribution-window.json", "post_click": { "interval": 7, "unit": "days" }, "post_view": { "interval": 1, "unit": "days" }, "model": "last_touch" } ``` 主な違い: * **フィールド名変更** — `click_window_days` → `post_click`、`view_window_days` → `post_view`。新しい名前はルックバックウィンドウを開始するユーザーアクションを表します。 * **構造化された Duration** — 時間ウィンドウは整数の日数ではなく `{ interval, unit }` オブジェクトになりました。これにより時間単位(`minutes`、`hours`)やキャンペーンスコープのウィンドウが使える。 * **アトリビューションモデル** — v2 と v3 の両方で必須。変更不要。 *** ## Duration オブジェクト `Duration` タイプは、フリークエンシーキャップ、アトリビューションウィンドウ、その他の時間ベース設定で共有されます。 ```json theme={null} { "interval": 30, "unit": "minutes" } ``` | ユニット | 意味 | | ---------- | ---------------------------------------- | | `minutes` | 時計の分 | | `hours` | 時計の時間 | | `days` | カレンダー日数 | | `campaign` | キャンペーンのフライト全体(interval は `1` でなければなりません) | *** ## 移行ステップ すべてのアトリビューションウィンドウオブジェクトで `click_window_days` を `post_click` に、`view_window_days` を `post_view` に置き換える。 整数の日数(例: `7`)を構造化された Duration オブジェクト(例: `{ "interval": 7, "unit": "days" }`)に置き換える。 フリークエンシーキャップにも同様の移行があります。v2 は `suppress_minutes`(整数)を使用していました。v3 は `suppress`(Duration オブジェクト)を使用し、`window`(Duration)を持つ `max_impressions` を追加します。`suppress_minutes: 30` を `suppress: { "interval": 30, "unit": "minutes" }` に変換します。 アトリビューションウィンドウオブジェクトが `attribution-window.json` スキーマに対して検証されることを確認します。 イベントソースを設定し、コンバージョンイベントを送信し、アトリビューションウィンドウを設定します。 *** **関連:** [チャンネル](/docs/reference/migration/channels) | [価格](/docs/reference/migration/pricing) | [ジオターゲティング](/docs/reference/migration/geo-targeting) | [クリエイティブ](/docs/reference/migration/creatives) | [カタログ](/docs/reference/migration/catalogs) | [AdCP 3.0 概要](/docs/reference/whats-new-in-v3) # オーディエンスの移行 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/audiences AdCP オーディエンスを beta.3 から rc.1 に移行します。external_id を uid-type 列挙の値から AudienceMember の必須トップレベルフィールドに昇格させる。 # オーディエンスの移行 AdCP 3.0 rc.1 は `external_id` を `uid-type` 列挙の値から `AudienceMember` の必須トップレベルフィールドに昇格させる。すべてのオーディエンスメンバーは、バイヤーが割り当てた安定した識別子と、少なくとも1つのマッチング可能な識別子を持つ必要があります。 ## 変更内容 | beta.3 | rc.1 | 注記 | | -------------------------------- | ---------------------------------- | --------------------------------------------- | | `external_id` が uid-type 列挙に含まれる | `external_id` が必須トップレベルフィールド | すべてのメンバーに常に存在する | | オプションのバイヤー識別子 | 必須のバイヤー識別子 | 重複排除と削除に使用 | | 単一の識別子モデル | 2つの要件: `external_id` + マッチング可能な ID | `hashed_email`、`hashed_phone`、`uids` の少なくとも1つ | ## AudienceMember スキーマ ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/latest/core/audience-member.json", "external_id": "crm_user_12345", "hashed_email": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2", "uids": [ { "type": "uid2", "value": "uid2_token_abc123" } ] } ``` スキーマで適用される2つの要件: 1. `external_id` は**必須** — バイヤーが割り当てた安定した識別子(CRM レコード ID、ロイヤルティ ID) 2. 少なくとも1つのマッチング可能な識別子 — `hashed_email`、`hashed_phone`、または `uids` 配列 ## 変更前後 **beta.3 — uid-type エントリとしての external\_id:** ```json test=false theme={null} { "uids": [ { "type": "external_id", "value": "crm_user_12345" }, { "type": "uid2", "value": "uid2_token_abc123" } ] } ``` **rc.1 — 必須トップレベルフィールドとしての external\_id:** ```json test=false theme={null} { "external_id": "crm_user_12345", "uids": [ { "type": "uid2", "value": "uid2_token_abc123" } ] } ``` ## uid-type 列挙 `uid-type` 列挙には `external_id` が含まれなくなりました。現在の値: | 値 | 説明 | | -------- | ---------------------------------------- | | `rampid` | LiveRamp RampID | | `id5` | ID5 ユニバーサル ID | | `uid2` | Unified ID 2.0 | | `euid` | European Unified ID | | `pairid` | Publisher Addressable Identity(IAB PAIR) | | `maid` | Mobile Advertising ID(IDFA/GAID) | | `other` | その他のユニバーサル ID タイプ(`ext` で指定) | ## 変更の理由 `external_id` を uid-type 列挙から分離することで、バイヤーの安定した識別子が明示的になります。これにより以下が可能になる: * **重複排除** — `external_id` でシンク間の重複メンバーを削除します * **ターゲット削除** — 全リストを再アップロードせずに特定のメンバーを削除します * **クロスリファレンス** — オーディエンスメンバーシップをバイヤーの CRM システムと相関させる ネイティブに ID を割り当てない CDP は1つを導出できる(例: メンバーの識別子のハッシュ)。 ## オーディエンスの同期 `sync_audiences` はオーディエンスごとにデルタオペレーション(`add`/`remove`)を使用します。メンバーは削除時に `external_id` で識別されます: ```json test=false theme={null} { "account": { "account_id": "acct_pinnacle" }, "audiences": [ { "audience_id": "high_value_customers", "name": "High-Value Customers", "add": [ { "external_id": "crm_user_12345", "hashed_email": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2" } ], "remove": [ { "external_id": "crm_user_99999", "hashed_email": "f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5" } ] } ] } ``` ## 移行ステップ `uids` 配列の `{ "type": "external_id", "value": "..." }` エントリをトップレベルの `external_id` フィールドに移動します。 メンバーにバイヤーが割り当てた ID がない場合、1つを導出する(例: 識別子のハッシュ)。スキーマはすべてのメンバーに `external_id` を要求します。 すべてのメンバーには `hashed_email`、`hashed_phone`、または `uids` の少なくとも1つが必要です。これはスキーマの `anyOf` 制約で適用されます。 `sync_audiences` でメンバーを削除するとき、安定したキーとして `external_id` を使用します。メンバーは `remove` 配列にもマッチング可能な識別子が必要です。 メンバーオブジェクトを `audience-member.json` スキーマに対して実行します。`external_id`(必須)とマッチング可能な識別子制約の両方が適用されます。 シグナル探索、アクティベーション、価格モデルの完全リファレンス。 *** **関連:** [ブランドアイデンティティ](/docs/reference/migration/brand-identity) | [最適化目標](/docs/reference/migration/optimization-goals) | [AdCP 3.0 概要](/docs/reference/whats-new-in-v3) # ブランドアイデンティティの移行 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/brand-identity AdCP ブランドアイデンティティを beta.3 から rc.1 に移行します。インライン brand_manifest オブジェクトを、brand.json またはコミュニティレジストリで解決される BrandRef 参照に置き換える。 # ブランドアイデンティティの移行 AdCP 3.0 rc.1 はインラインの `brand_manifest` オブジェクトを軽量なブランド参照(`BrandRef`)に置き換える。ブランドデータは実行時に `/.well-known/brand.json` またはコミュニティブランドレジストリから解決されます。 ## 変更内容 | beta.3 | rc.1 | 注記 | | ----------------------------- | ---------------------------- | --------------------------------------------------------------------------- | | `brand_manifest`(インラインオブジェクト) | `brand`(`BrandRef`) | 参照は実行時に解決される | | ブランドデータをすべてのリクエストで渡す | ブランドデータを `brand.json` から一度取得 | キャッシュ推奨(24時間 TTL) | | 標準的なアイデンティティフォーマットなし | `/.well-known/brand.json` 仕様 | 4つのバリアント: House Portfolio、Brand Agent、House Redirect、Authoritative Location | ## BrandRef スキーマ ブランド参照はドメインとオプションの `brand_id` でブランドを識別する: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/latest/core/brand-ref.json", "domain": "nova-brands.com", "brand_id": "spark" } ``` * `domain`(必須)— `/.well-known/brand.json` がホストされているドメイン、またはブランドの運営ドメイン * `brand_id`(オプション)— ハウスポートフォリオ内のブランド識別子。シングルブランドドメインの場合は省略します。 シングルブランドドメインの場合: ```json theme={null} { "domain": "acme-corp.com" } ``` ## brand が使われる場所 | タスク | 必須? | 目的 | | ------------------ | --- | ------------------------------ | | `create_media_buy` | はい | キャンペーンアイデンティティ — 一度設定したら変更不可 | | `get_products` | いいえ | 探索コンテキスト。`catalog` が指定された場合は必須 | | `build_creative` | いいえ | クリエイティブ生成のためのカラー、ロゴ、トーンを解決する | `brand` は `update_media_buy`(作成時から不変)、`sync_creatives`(アカウントにスコープ)、`sync_catalogs`(アカウントにスコープ)には使われない。 ## 変更前後 **beta.3 — create\_media\_buy でのインライン brand\_manifest:** ```json test=false theme={null} { "brand_manifest": { "name": "Spark", "domain": "spark.nova-brands.com", "logo_url": "https://spark.nova-brands.com/logo.png", "industries": ["consumer_electronics"] }, "account": { "account_id": "acct_nova" }, "start_time": "2025-04-01T00:00:00Z", "end_time": "2025-04-30T23:59:59Z" } ``` **rc.1 — ブランド参照:** ```json test=false theme={null} { "brand": { "domain": "nova-brands.com", "brand_id": "spark" }, "account": { "account_id": "acct_nova" }, "start_time": "2025-04-01T00:00:00Z", "end_time": "2025-04-30T23:59:59Z" } ``` セラーはブランドプロトコルを介して `nova-brands.com` + `spark` を完全なブランドアイデンティティ(ロゴ、カラー、トーン、プロパティ)に解決します。 ## 解決フロー `BrandRef` が与えられた場合、解決の手順は次のとおり: 1. `https://{domain}/.well-known/brand.json` を取得します 2. **House Redirect**(`house` フィールドを持つ)の場合、ハウスドメインに従う(最大3リダイレクト) 3. **House Portfolio** の場合、`brands` 配列で `brand_id` によりブランドを検索します 4. **Brand Agent** の場合、動的なブランドデータを取得するために MCP エージェントを呼び出す 5. **Authoritative Location** の場合、`location` URL に従う セラーは解決されたブランドデータをキャッシュする(検証済みファイルの場合は24時間 TTL 推奨、失敗した検索の場合は1時間)。 ## 移行ステップ すべてのタスクリクエスト(`create_media_buy`、`get_products`、`build_creative`)で、`brand_manifest` オブジェクトを `brand` BrandRef: `{ "domain": "...", "brand_id": "..." }` に置き換える。 ブランドのドメインに `/.well-known/brand.json` を公開します。バリアントを選択する: マルチブランド企業には House Portfolio、動的データには Brand Agent、シンプルなケースにはシングルブランド。 ブランドドメインが `brand.json` をホストしていない場合、セラーが解決できるようにコミュニティブランドレジストリに登録します。 セラーエージェントは実行時に `BrandRef` を完全なブランドデータに解決する必要があります。キャッシュを実装し、4つの `brand.json` バリアントすべてを処理します。 `brand_manifest` オブジェクトを構築または解析するコードを削除します。ブランドデータはインラインで渡されなくなります。 リクエストを `brand-ref.json` スキーマに対して実行します。`domain` フィールドには有効な小文字のドメインパターンが必要です。 brand.json バリアント、ブランドレジストリ、BrandRef 解決の完全リファレンス。 *** **関連:** [チャンネル](/docs/reference/migration/channels) | [カタログ](/docs/reference/migration/catalogs) | [ブランドプロトコル](/docs/brand-protocol) | [AdCP 3.0 概要](/docs/reference/whats-new-in-v3) # カタログの移行 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/catalogs AdCP カタログを v2 から v3 に移行します。promoted_offerings をファーストクラスのカタログオブジェクト、sync_catalogs タスク、フォーマットレベルのカタログ要件に置き換える。 # カタログの移行 AdCP 3.0 は `promoted_offerings` クリエイティブアセットタイプと、メディアバイとクリエイティブマニフェストの `promoted_offering` 文字列フィールドを削除します。カタログは独自の同期タスク、フォーマットレベルの要件、コンバージョンイベント整合を持つファーストクラスのプロトコルオブジェクトになりました。 ## 変更内容 | v2 フィールド | v3 フィールド | 変更タイプ | | -------------------------------------- | --------------------------------------- | ----- | | `promoted_offerings`(クリエイティブアセットタイプ) | クリエイティブマニフェスト `assets` の `catalog` アセット | 置き換え | | `promoted_offering`(メディアバイの文字列) | 削除 | 削除 | | `promoted_offering`(クリエイティブマニフェストの文字列) | `assets` の `catalog` アセット | 置き換え | | カタログ同期なし | `sync_catalogs` タスク | 新規 | | フォーマットカタログ要件なし | Format `assets` の `catalog` アセットタイプ | 新規 | | カタログイベントリンクなし | Catalog の `conversion_events` | 新規 | *** ## クリエイティブマニフェスト ### 変更内容 v2 では、カタログデータは `creative_manifest.assets` 内の `promoted_offerings` オブジェクトとして埋め込まれており、ブランドアイデンティティ、インラインオファリング、SI エージェント URL がまとめられていました。 v3 では、ブランドアイデンティティはタスク(`build_creative`、`sync_creatives`)のファーストクラスパラメーターとなり、カタログはマニフェストの `assets` の `catalog` アセットタイプとして含まれます。 ### 変更前(v2) ```json theme={null} { "creative_id": "product-carousel", "format_id": { "agent_url": "https://creatives.example.com", "id": "product_carousel" }, "assets": { "promoted_offerings": { "brand": { "domain": "acmecorp.example.com" }, "offerings": [ { "offering_id": "winter-sale", "name": "Winter Sale Collection", "description": "50% off all winter items" } ] } } } ``` ### 変更後(v3) ```json theme={null} { "$schema": "/schemas/core/creative-manifest.json", "format_id": { "agent_url": "https://creatives.example.com", "id": "product_carousel" }, "assets": { "product_catalog": { "type": "product", "catalog_id": "winter-products" } } } ``` 主な違い: * **ブランドアイデンティティ** はクリエイティブアセットに埋め込まれるのではなく、タスクの `brand` パラメーターから来ます * **カタログ** はロール名(例: `product_catalog`)をキーとして `assets` マップの `catalog` アセットタイプになります * **カタログはインラインで埋め込む代わりに `catalog_id` で同期データを参照する**(ただしシンプルなケースではインラインアイテムもまだサポートされています) *** ## メディアバイ ### 変更内容 `promoted_offering` 文字列フィールドはメディアバイオブジェクトから削除されました。何が宣伝されているかは、`get_products`/`create_media_buy` の `brief` と、クリエイティブのカタログ参照を通じて表現されます。 ### 変更前(v2) ```json theme={null} { "media_buy_id": "mb_123", "promoted_offering": "Winter Sale Collection", "status": "draft", "total_budget": { "amount": 10000, "currency": "USD" }, "packages": [] } ``` ### 変更後(v3) ```json theme={null} { "media_buy_id": "mb_123", "status": "draft", "total_budget": { "amount": 10000, "currency": "USD" }, "packages": [] } ``` レポートや表示目的で `promoted_offering` を使用していた場合、そのコンテキストは以下から得られます: * `get_products` と `create_media_buy` の `brief` フィールド * タスクの `brand` パラメーター * クリエイティブマニフェスト `assets` のカタログアセット *** ## カタログの同期(v3 の新機能) `sync_catalogs` タスクを使うと、バイヤーはクリエイティブを送信する前にカタログデータをセラーにプッシュできます。これはクリエイティブアセット内に製品データを埋め込むパターンを置き換える。 主な機能: * カタログごとの結果を持つ**バルク同期** * **非同期承認**ワークフロー(working、input-required、submitted) * カタログアイテムごとの承認/拒否を持つ**アイテムレベルレビュー** * **探索モード** — catalogs フィールドを省略して既存の同期済みカタログを確認します * **バリデーションモード** — strict、lenient、dry\_run ### ワークフロー ``` list_creative_formats → catalog アセットを確認 → sync_catalogs → catalog アセット付きで sync_creatives ``` 1. フォーマットの `assets` の `catalog` アセットタイプを通じて、フォーマットが必要とするカタログを発見します 2. `sync_catalogs` を使ってそれらのカタログを同期します 3. セラーがレビューを必要とする場合は承認を待つ 4. 同期済みの `catalog_id` を参照するクリエイティブを送信します すべての13カタログタイプ、同期ワークフロー、アイテムレベルレビューを含む完全リファレンス。 *** ## フォーマットカタログ要件(v3 の新機能) フォーマットは `assets` 配列の `catalog` アセットタイプとしてカタログニーズを宣言する: ```json theme={null} { "assets": [ { "item_type": "individual", "asset_id": "product_catalog", "asset_type": "catalog", "required": true, "requirements": { "catalog_type": "product", "min_items": 3, "required_fields": ["name", "price", "image_url"] } } ] } ``` フォーマット宣言は `asset_type: "catalog"` を使用し、以下を含む `requirements` オブジェクトを持ちます: | フィールド | タイプ | 説明 | | ----------------- | --------- | ------------------------------------------------------------ | | `catalog_type` | string | 必須。カタログタイプ(例: `product`、`store`、`job`) | | `min_items` | integer | カタログが含まなければなりません最低アイテム数 | | `max_items` | integer | フォーマットがレンダリングできる最大アイテム数 | | `required_fields` | string\[] | すべてのアイテムに存在しなければなりませんフィールド | | `feed_formats` | string\[] | 受け付けるフィードフォーマット(例: `google_merchant_center`、`linkedin_jobs`) | これは、フォーマットが `assets` に `promoted_offerings` を暗黙的に必要としていた v2 のパターンを置き換える — 要件は明示的で発見可能になりました。 ### クリエイティブエージェントの移行 フォーマット定義を読んでカタログ要件を決定するクリエイティブエージェントは、カタログスロットを発見・充足する方法を変更する必要があります。 **変更前(v2)** — 専用の `catalog_requirements` フィールドを確認: ```javascript theme={null} // v2: カタログ要件は別のトップレベル配列だった for (const req of format.catalog_requirements) { const catalogType = req.catalog_type; const minItems = req.min_items; } ``` **変更後(v3)** — `assets` 配列を反復し、`asset_type` でフィルタリング: ```javascript theme={null} // v3: カタログは他のアセットと同様にアセットである const catalogAssets = format.assets.filter(a => a.asset_type === "catalog"); for (const slot of catalogAssets) { const catalogType = slot.requirements.catalog_type; const minItems = slot.requirements.min_items; const slotId = slot.asset_id; // manifest.assets のキーとして使用 } ``` マニフェストを構築するとき、カタログアセットはフォーマット定義の `asset_id` でキー付けされます: ```json theme={null} { "assets": { "product_catalog": { "type": "product", "catalog_id": "winter-products" } } } ``` フォーマットの `assets` 配列からの `asset_id`(例: `product_catalog`)がマニフェストの `assets` オブジェクトのキーになります。 *** ## コンバージョンイベント整合(v3 の新機能) カタログはアイテムのコンバージョンを表すイベントタイプを宣言する: ```json theme={null} { "catalog_id": "job-feed", "type": "job", "content_id_type": "job_id", "conversion_events": ["submit_application", "complete_registration"] } ``` これはカタログをコンバージョントラッキングシステムにリンクします。カタログアイテムに一致する `content_ids` を持つ `log_event` が送信されると、プラットフォームはどのイベントをアトリビュートするかを知ります。 `content_id_type` フィールドは `content_ids` 値が表す識別子タイプを宣言します。バーティカルカタログの場合、これはアイテムの正規 ID フィールド(`job_id`、`hotel_id` など)に一致します。プロダクトカタログの場合、クロスリテーラーマッチングのために `sku` と `gtin` を区別します。カスタム識別子スキームを使用する場合は省略します。 | カタログタイプ | 典型的なコンバージョンイベント | | ------------- | -------------------------------------------- | | `product` | `purchase`、`add_to_cart` | | `hotel` | `purchase`(予約) | | `flight` | `purchase`(予約) | | `job` | `submit_application` | | `vehicle` | `lead`、`schedule` | | `real_estate` | `lead`、`schedule` | | `education` | `submit_application`、`complete_registration` | | `destination` | `purchase`(予約) | カタログイベント整合の完全ドキュメント。 *** ## get\_products の product\_selectors がカタログに置き換えられました `get_products` の `product_selectors` フィールドは `catalog` に置き換えられました。`PromotedProducts` スキーマは削除されました。 ### 変更前 ```json theme={null} { "brand": { "domain": "acmecorp.com" }, "product_selectors": { "manifest_gtins": ["00013000006040"], "manifest_tags": ["organic"] } } ``` ### 変更後 ```json theme={null} { "brand": { "domain": "acmecorp.com" }, "catalog": { "type": "product", "gtins": ["00013000006040"], "tags": ["organic"] } } ``` ### フィールドマッピング | 古い(`product_selectors`) | 新しい(`catalog`) | | ----------------------- | -------------- | | `manifest_gtins` | `gtins` | | `manifest_skus` | `ids` | | `manifest_tags` | `tags` | | `manifest_category` | `category` | | `manifest_query` | `query` | ### レスポンスの変更 * `product_selectors_applied` は `catalog_applied` になりました * `catalog_match.matched_skus` は `catalog_match.matched_ids` になりました *** ## Product の新フィールド * **`catalog_types`** — このプロダクトがサポートするカタログタイプの配列(例: `["product"]`、`["job", "offering"]`) * **`catalog_match.matched_ids`** — 汎用アイテム ID マッチ(`matched_skus` を置き換え) * **`catalog_match.matched_count`** — マッチしたアイテムの数 *** ## パッケージのカタログ パッケージはカタログ主導のキャンペーンのための `catalog` フィールドを受け付けるようになりました。1つの予算エンベロープがカタログ全体を宣伝し、プラットフォームがアイテム間の配信を最適化します。 *** ## 店舗キャッチメントターゲティング `targeting_overlay` は `store_catchments` をサポートするようになった — 近接ターゲティングのために同期済みの店舗カタログを参照します。 *** ## カタログアイテムごとの配信 `get_media_buy_delivery` はカタログ主導のキャンペーンのためにパッケージ内に `by_catalog_item` 内訳を含むようになりました。 *** ## 移行ステップ `creative_manifest.assets` から `promoted_offerings` オブジェクトを削除します。ブランドアイデンティティは `brand` タスクパラメーターを通じて提供されるようになりました。 カタログアイテムをレンダリングするクリエイティブ(製品カルーセル、店舗ロケーターなど)については、マニフェストの `assets` マップにカタログアセットを追加します。各カタログアセットは `type` と `catalog_id` を持つカタログオブジェクト(例: `{ "type": "product", "catalog_id": "winter-products" }`)だ。 `create_media_buy` リクエストから `promoted_offering` 文字列を削除します。宣伝するものを説明するために `brief` フィールドを使用します。 `list_creative_formats` を呼び出し、各フォーマットの `assets` の `catalog` アセットタイプを確認して、必要なカタログタイプとフィールドを理解します。 `sync_catalogs` タスクを使ってカタログデータをセラーにプッシュします。セラーがレビューを必要とする場合は非同期承認を処理します。 カタログに `conversion_events` を含めてカタログアイテムをコンバージョントラッキングセットアップにリンクします。イベントの `content_ids` を照合する識別子タイプ(例: `sku`、`gtin`、`job_id`)を宣言するために `content_id_type` を追加します。 クリエイティブマニフェスト、メディアバイ、カタログオブジェクトが v3 スキーマに対して検証されることを確認します。 *** **関連:** [チャンネル](/docs/reference/migration/channels) | [価格](/docs/reference/migration/pricing) | [ジオターゲティング](/docs/reference/migration/geo-targeting) | [クリエイティブ](/docs/reference/migration/creatives) | [アトリビューション](/docs/reference/migration/attribution) | [AdCP 3.0 概要](/docs/reference/whats-new-in-v3) # チャンネルの移行 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/channels AdCP チャンネルを v2 から v3 に移行します。v2 の9フォーマット指向チャンネルを v3 の19計画指向メディアチャンネルにフィールドごとの例付きでマッピングします。 # チャンネルの移行 AdCP 3.0 は v2 の9チャンネルを、バイヤーが予算を配分する方法を反映した19の計画指向チャンネルに置き換える。5つのチャンネルはそのまま引き継がれ、残りは分割、削除、または名前変更されます。 ## チャンネルモデルが変更された理由 v2 はフォーマット指向のチャンネル(`video`、`audio`、`native`)と計画指向のチャンネル(`social`、`ctv`、`dooh`)が混在していました。バイヤーはレンダリング技術で予算を計画するのではなく、メディアタイプで計画します。ビデオ予算は OLV、CTV、映画館に分割されます。オーディオ予算はラジオ、ストリーミング、ポッドキャストにまたがる。 v3 チャンネルは代理店がメディアプランを構造化する方法と一貫して一致します。 ## チャンネルマッピング | v2 チャンネル | v3 チャンネル | 注記 | | --------- | -------------------------- | ------------------------------- | | `display` | `display` | 変更なし | | `video` | `olv`、`linear_tv`、`cinema` | 配信で分割(`ctv` は v2 でもすでに別だった) | | `audio` | `radio`、`streaming_audio` | 配信で分割(`podcast` は v2 でもすでに別だった) | | `native` | 削除 | 代わりにフォーマットレベルのプロパティを使用 | | `social` | `social` | 変更なし | | `ctv` | `ctv` | 変更なし | | `podcast` | `podcast` | 変更なし | | `dooh` | `dooh` | 変更なし | | `retail` | `retail_media` | 明確化のために名前変更 | ## 完全な v3 チャンネル列挙 `channels.json` スキーマからのすべての19の値: ``` display, olv, social, search, ctv, linear_tv, radio, streaming_audio, podcast, dooh, ooh, print, cinema, email, gaming, retail_media, influencer, affiliate, product_placement ``` | チャンネル | 説明 | | ------------------- | -------------------------------------------- | | `display` | ウェブとアプリ全体のデジタルディスプレイ広告(バナー、ネイティブ、リッチメディア) | | `olv` | CTV 以外のオンラインビデオ広告(プリロール、アウトストリーム、アプリ内ビデオ) | | `social` | ソーシャルメディアプラットフォーム | | `search` | 検索エンジン広告と検索ネットワーク | | `ctv` | テレビ画面でのコネクテッド TV とストリーミング | | `linear_tv` | 従来の地上波とケーブルテレビ | | `radio` | 従来の AM/FM ラジオ放送 | | `streaming_audio` | デジタルオーディオストリーミングサービス | | `podcast` | ポッドキャスト広告(ホストリードまたは動的挿入) | | `dooh` | 公共スペースのデジタル屋外広告スクリーン | | `ooh` | クラシックな屋外広告(物理的な看板、交通広告など) | | `print` | 新聞、雑誌、その他の印刷物 | | `cinema` | 映画館広告 | | `email` | メール広告とスポンサードニュースレターコンテンツ | | `gaming` | プラットフォームをまたいだゲーム内広告(ゲーム内本来、リワードビデオ、プレイアブル広告) | | `retail_media` | リテールメディアネットワークとコマースマーケットプレイス | | `influencer` | クリエイターとインフルエンサーのマーケティングパートナーシップ | | `affiliate` | アフィリエイトネットワーク、比較サイト、パフォーマンスベースのパートナーシップ | | `product_placement` | プロダクトプレイスメント、ブランデッドコンテンツ、スポンサーシップ統合 | `gaming` チャンネルはすべてのゲーム内広告をカバーします。ゲームアプリのリワードビデオは `olv` にも分類できる — インベントリがゲーミング予算から来る場合は `gaming` を使用し、ビデオ予算から来る場合は `olv` を使用します。 ## `video` プロダクトの移行 v2 の `video` チャンネルは配信に基づいて3つの v3 チャンネルに分割しなければなりません(`ctv` は v2 でもすでに別のチャンネルだった): * **`olv`** — オンラインビデオ(ウェブ、モバイルアプリ、ソーシャル): プリロール、アウトストリーム、アプリ内ビデオ * **`linear_tv`** — 従来の地上波とケーブルテレビ * **`cinema`** — 映画館の上映前広告 単一のパブリッシャーが複数のチャンネルをサポートする場合があります。ストリーミングサービスはモバイルと TV の両方でインベントリを提供する場合、`olv` と `ctv` の両方を宣言することがあります。 ## `audio` プロダクトの移行 v2 の `audio` チャンネルは2つの v3 チャンネルに分割されます(`podcast` は v2 でもすでに別のチャンネルだった): * **`radio`** — 従来の AM/FM ラジオ放送 * **`streaming_audio`** — デジタルオーディオストリーミングサービス(音楽プラットフォーム) ## `native` プロダクトの移行 v3 は `native` をチャンネルとして削除します。Native はレンダリングスタイルであり、予算カテゴリではありません。 v2 で `native` タグが付いたプロダクトがある場合、予算の計画方法に基づいて適切な v3 チャンネルを割り当てる: * ネイティブディスプレイ広告 -> `display` * ネイティブソーシャル広告 -> `social` * ネイティブコンテンツレコメンデーション -> モデルに応じて `display` または `affiliate` ## `retail` プロダクトの移行 `retail` から `retail_media` へのシンプルな名前変更。 ## マルチチャンネルプロダクト v3 プロダクトはチャンネルの配列を宣言するため、単一のプロダクトが複数のチャンネルにまたがることができます。複数のチャンネルをサポートするセラーを示すケイパビリティレスポンス: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/protocol/get-adcp-capabilities-response.json", "adcp": { "major_versions": [3] }, "supported_protocols": ["media_buy"], "media_buy": { "portfolio": { "primary_channels": ["display", "olv", "ctv"], "publisher_domains": ["pinnaclemedia.example.com"], "primary_countries": ["US"] } } } ``` `get_products` からプロダクトが返されるとき、各プロダクトの `channels` 配列はそのプロダクトがどのチャンネルとして販売されているかを宣言する: ```json theme={null} { "channels": ["olv", "ctv"] } ``` ## ケイパビリティでのチャンネルサポートの宣言 バイヤーは `get_products` リクエストをフィルタリングする前に `get_adcp_capabilities` を呼び出してセラーがサポートするチャンネルを発見する必要があります。`portfolio.primary_channels` フィールドはセラーのメインチャンネルをリストします。 ## 移行ステップ コードがチャンネル値を読み書きするすべての場所を見つける。 上記のマッピングテーブルを使って各値を変換します。`display`、`social`、`ctv`、`podcast`、`dooh` は変更なしに注意。 v2 の `video` または `audio` が複数の v3 チャンネルにマップされる場合、各プロダクトを配信コンテキストで分類します。 バイヤーエージェントがチャンネルでフィルタリングする場合、新しい値に更新します。 正しいチャンネル宣言で `get_adcp_capabilities` を実装します。 v3 スキーマバリデーションは `video`、`audio`、`native`、`retail` などの古いチャンネル値を拒否します。 すべての19の v3 チャンネルの完全な定義と設計の根拠。 *** **関連:** [価格](/docs/reference/migration/pricing) | [ジオターゲティング](/docs/reference/migration/geo-targeting) | [クリエイティブ](/docs/reference/migration/creatives) | [カタログ](/docs/reference/migration/catalogs) | [アトリビューション](/docs/reference/migration/attribution) | [AdCP 3.0 概要](/docs/reference/whats-new-in-v3) # クリエイティブトランスフォーマーへの移行(3.1) Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/creative-transformers ビルドケイパビリティのディスカバリーとクリエイティブ価格を Format フィールドからトランスフォーマー(list_transformers)へ移す。3.1 では加算的、非推奨の Format フィールドは 4.0 で削除。 # クリエイティブトランスフォーマーへの移行(3.1) AdCP 3.1 は **トランスフォーマー** を導入します — メディアバイプロダクトのクリエイティブ版: エージェントが提供する、アカウントスコープの、選択可能なビルドケイパビリティの単位(ボイス、モデル、スタイル、ディレクター)で、型付き設定サーフェスとアカウントごとの価格を持ち、[`list_transformers`](/docs/creative/task-reference/list_transformers) 経由で発見され、[`build_creative`](/docs/creative/task-reference/build_creative) の `transformer_id` で選択されます。 トランスフォーマーは **独自の入力/出力フォーマットと独自の価格** を運ぶため、3.0 が *フォーマット* にぶら下げていた 3 つのものが今や冗長になり、非推奨です: * `Format.input_format_ids` / `Format.output_format_ids` * `Format.pricing_options` * `list_creative_formats` の `input_format_ids` / `output_format_ids` ディスカバリー **フィルター** **3.1 で何もしなくても何も壊れません。** 4 つすべてが `deprecated: true` ですが、依然として検証を通過し 3.1–3.x ラインを通じて機能します。4.0 より前に対応してください。下記の危険は、ハードな破壊ではなく、ウィンドウにわたる *静かな劣化* についてです。 ## 何が変わったか | Surface | 3.0 | 3.1 | Removed | | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------- | | ビルドケイパビリティのディスカバリー | `input_format_ids` / `output_format_ids` で `list_creative_formats` をフィルター。`Format.input_format_ids` / `output_format_ids` を読む | `list_transformers` 経由で発見(各トランスフォーマーが独自の I/O シグネチャを宣言) | 4.0 | | クリエイティブ/変換の価格 | `Format.pricing_options`(フォーマットごと) | `transformer.pricing_options`(トランスフォーマーごと。`list_transformers` の `include_pricing` + `account`) | 4.0 | | `list_creative_formats` の `input_format_ids` / `output_format_ids` フィルター | サポート | `deprecated: true` — `list_transformers` フィルター + `brief` を使う | 4.0 | ## 移行する ### Format I/O シグネチャ → トランスフォーマー トランスフォーマーは、それが受け入れ生成するフォーマットを直接宣言するため、ビルドケイパビリティは *選択可能な単位* のプロパティであり、フォーマットにぶら下がる関係ではありません。 **Before(3.0)** — 変換フォーマットが消費/生成するものを宣言: ```json test=false theme={null} { "format_id": { "agent_url": "https://creative.example", "id": "resize_to_meta" }, "input_format_ids": [{ "agent_url": "https://creative.example", "id": "display_master" }], "output_format_ids": [{ "agent_url": "https://creative.example", "id": "meta_reels_9x16" }] } ``` **After(3.1)** — トランスフォーマーを宣言(`list_transformers` 経由で発見): ```json test=false theme={null} { "transformer_id": "resize_to_meta", "name": "Resize to Meta Reels", "input_format_ids": [{ "agent_url": "https://creative.example", "id": "display_master" }], "output_format_ids": [{ "agent_url": "https://creative.example", "id": "meta_reels_9x16" }], "params": [], "pricing_options": [ { "pricing_option_id": "per_format", "model": "per_unit", "unit": "format", "unit_price": 0.10, "currency": "USD" } ] } ``` ### Format 価格 → トランスフォーマー価格 `Format.pricing_options` は `transformer.pricing_options` に移動します(同じ `vendor-pricing-option` 形状。`per_unit` が典型的)。適用されたオプションは `build_creative` レスポンスでリーフごとにエコーされ、`report_usage` 経由で再照合されます、変更なし。 複数出力のトランスフォーマーでは、価格オプションが `applies_to_output_format_ids` を運んでそのレートを特定の出力にスコープできます。スコープされていない価格オプションは任意の出力のデフォルトです。ビルドが、どのスコープオプションにも一致しない出力をターゲットにし、トランスフォーマーにスコープされていないデフォルトがない場合、エージェントは価格を推測するのではなく `UNPRICEABLE_OUTPUT` でビルドを拒否します。 ### `list_creative_formats` ディスカバリーフィルター → `list_transformers` 「X をビルドできるもの」を見つけるために `input_format_ids` / `output_format_ids` で `list_creative_formats` をフィルターするのをやめてください。`list_transformers` を使ってください — その `input_format_ids` / `output_format_ids` でフィルターし、`brief` で絞り込み、`expand_params` でアカウントスコープのオプション値を展開します。 ## 何もしないと何が静かに壊れるか これらは throw しません — それがまさにこのガイドが存在する理由です。 1. **ディスカバリー読み取りの劣化(バイヤー)。** バイヤー/ストーリーボードが、`Format.input_format_ids` / `output_format_ids` を読むか、それらで `list_creative_formats` をフィルターすることによって *のみ* ビルドケイパビリティを学ぶ場合、3.1 セラーに対して動作し続けますが、セラーが非推奨フィールドを発するのをやめてケイパビリティをトランスフォーマーに移すにつれ **次第に空になる結果** を見ます。エラーなし — ただオプションがどんどん減るだけ。**フィールドがそのデータより長生きする。** ディスカバリーを `list_transformers` に移してください。 2. **出力ごとの価格ギャップ(複数パブリッシャーのテンプレートセラー)。** 出力を異なる価格にしていたテンプレートは、`applies_to_output_format_ids` でその形状を保てますが、すべての出力にスコープされた価格オプションかスコープされていないデフォルトのいずれかが必要です。そうでなければ、価格のない出力のビルドは `UNPRICEABLE_OUTPUT` で拒否されます。 3. **ファンアウト / best-of-N の支出過少カウント(課金パイプライン)。** `max_variants` / `max_creatives` では、生成されたすべてのバリアントが課金されますが、**トラフィックされた** リーフのみが遅延的に `creative_id` を得ます。破棄された best-of-N のリーフは、`build_creative` レスポンスの **インラインのリーフごとの `vendor_cost`** 経由で *のみ* 課金されます — それらは決して `creative_id` を得ず、したがって決して `report_usage` に現れません。`report_usage` のみから支出を再照合するパイプライン(3.0 の不変条件「すべての課金単位は `creative_id` 経由で再照合される」)は、破棄されたすべてのバリアントの分だけ **過少カウント** します。トラフィックされていないリーフの権威あるレコードとして、インラインのリーフごとの領収書を取り込んでください。その領収書が唯一のレコードなので、スキーマがそれを強制します: ビルドがコストをレポートするとき(集計 `vendor_cost` が存在するとき)、生成されたすべてのリーフは独自の `vendor_cost` + `currency` を運ばなければならず(MUST)、支払いビルドが機械可読なコストのないリーフを返せません。境界に注意: スキーマはレポートされたコストの *内部的な完全性と一貫性* を強制します — 真に無料のビルド(または正当に `0` の CPM 遅延リーフ)を、過少レポートするために集計を省略する支払いビルドと区別できません。「集計が欠如」は、ゼロコストの証明ではなく、再照合すべき信頼アサーション(セラー請求書対 `report_usage` + インライン領収書、`pricing_option_id` 基準で区別)です — スキーマ有効性を課金の正直さと取り違えないでください。 ## SDK の動作とタイムライン | Version | Deprecated fields | | ------- | -------------------------------------------------------------- | | 3.1 | `deprecated: true`。依然として発せられ尊重される。SDK はそれらを読み続けなければならない(MUST)。 | | 3.1–3.x | 全期間尊重される。新しいコードはそれらを発すべきではない(SHOULD NOT)。 | | 4.0+ | SDK は拒否してもよい(MAY)。削除。 | ## 関連項目 * [`list_transformers`](/docs/creative/task-reference/list_transformers) — トランスフォーマー、そのパラメーター、オプション値、価格を発見 * [`build_creative`](/docs/creative/task-reference/build_creative) — トランスフォーマーを選択、設定、乗算、絞り込み * [3.1 の新機能](/docs/reference/whats-new-in-3-1) # クリエイティブの移行 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/creatives AdCP クリエイティブを v2 から v3 に移行します。creative_ids をウェイト付きアサインメントに、assets_required をフォーマット探索用の統合アセット配列に置き換える。 # クリエイティブの移行 AdCP 3.0 はクリエイティブ処理に3つの破壊的変更を加える: `FormatCategory` enum とフォーマットの `type` フィールドが削除され、ウェイト付きクリエイティブアサインメントがシンプルな ID 配列を置き換え、統合 `assets` 配列がフォーマット探索の `assets_required` を置き換える。 ## Format category removal ### What changed | v2 field | v3 field | Change type | | --------------------------------------------------------------- | -------- | ----------- | | フォーマットオブジェクトの `type`(FormatCategory enum) | 削除 | Deleted | | `list_creative_formats` / `get_products` の `format_types` フィルター | 削除 | Deleted | ### Why `FormatCategory` enum(`video`、`display`、`audio`、`native`、`social`、`custom`)は、マルチアセットフォーマットにうまくマップしない粗い分類子でした。「video」フォーマットはディスプレイのコンパニオンバナーやテキストオーバーレイも必要とする場合があります。enum は、本質的にマルチモーダルなフォーマットに 1 つのカテゴリを選ぶことを実装者に強制し、一貫しないフィルタリングと探索のギャップを招きました。 ### Migration `format_types` フィルターを `asset_types`(フォーマットが必要とするもの)または `format_ids`(完全一致)に置き換えます。 **v2:** ```json theme={null} { "format_types": ["video"] } ``` **v3 — アセットタイプでフィルター:** ```json theme={null} { "asset_types": ["video"] } ``` **v3 — 完全なフォーマット ID でフィルター:** ```json theme={null} { "format_ids": [ { "agent_url": "https://creatives.example.com", "id": "video_preroll_30s" } ] } ``` `asset_types` は、そのタイプのアセットを少なくとも 1 つ含む任意のフォーマットを返します — したがってコンパニオンバナー付きのビデオフォーマットは `["video"]` と `["image"]` の両方の結果に現れます。 *** ## クリエイティブアサインメント ### 変更内容 | v2 フィールド | v3 フィールド | 変更タイプ | | --------------------- | -------------------------------- | ----- | | `creative_ids`(文字列配列) | `creative_assignments`(オブジェクト配列) | 置き換え | ### シンプルな移行(均等ウェイト) **v2:** ```json theme={null} { "creative_ids": ["creative_1", "creative_2"] } ``` **v3 — 均等分配には `weight` を省略する:** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/creative-assignment.json", "creative_id": "creative_1" } ``` `create_media_buy` パッケージのコンテキストで: ```json theme={null} { "creative_assignments": [ { "creative_id": "creative_1" }, { "creative_id": "creative_2" } ] } ``` すべてのアサインメントで `weight` が省略された場合、インプレッションは均等に分配されます。 ### ウェイト付きアサインメント 各クリエイティブが受け取るインプレッションの割合を制御する: ```json theme={null} { "creative_assignments": [ { "creative_id": "hero_video", "weight": 60 }, { "creative_id": "promo_video", "weight": 40 } ] } ``` ウェイトは相対的 — 合計が100になる必要はないが、そうすることで意図が明確になります。 ### プレースメントターゲティング プロダクト内の特定のプレースメントに特定のクリエイティブをアサインする: ```json theme={null} { "creative_assignments": [ { "creative_id": "hero_video", "placement_ids": ["homepage_hero"], "weight": 100 }, { "creative_id": "sidebar_banner", "placement_ids": ["article_sidebar"], "weight": 100 }, { "creative_id": "fallback_banner", "weight": 50 } ] } ``` `placement_ids` が省略された場合、クリエイティブはパッケージのすべてのプレースメントで実行されます。`placement_ids` はプロダクトの placements 配列の `placement_id` 値を参照します。 `sync_creatives` は `placement_ids` をサポートしません。プレースメントレベルのターゲティングには `create_media_buy` または `update_media_buy` を使用します。 ### クリエイティブアサインメントスキーマ 各アサインメントオブジェクト: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/creative-assignment.json", "creative_id": "hero_video", "weight": 60, "placement_ids": ["homepage_hero"] } ``` | フィールド | タイプ | 必須 | 説明 | | --------------- | ------------- | --- | ----------------------------------------------------- | | `creative_id` | string | はい | `list_creatives` または `sync_creatives` からのクリエイティブを参照する | | `weight` | number(0〜100) | いいえ | デリバリーウェイト。均等分配の場合は省略。 | | `placement_ids` | string\[] | いいえ | 特定のプレースメントをターゲット。すべての場所で実行するには省略。 | *** ## アセット探索 ### 変更内容 | v2 フィールド | v3 フィールド | 変更タイプ | | ------------------------ | ------------------ | ----- | | `assets_required`(文字列配列) | `assets`(オブジェクト配列) | 置き換え | | `preview_image` | `format_card` | 置き換え | ### フォーマットアセット **v2** — 必須アセット ID のみをリスト: ```json theme={null} { "format_id": { "agent_url": "https://creatives.example.com", "id": "display_300x250" }, "name": "Standard Banner 300x250", "assets_required": ["main_image", "headline"] } ``` **v3** — `required` ブールフラグ付きですべてのアセットをリスト: ```json theme={null} { "format_id": { "agent_url": "https://creatives.example.com", "id": "display_300x250" }, "name": "Standard Banner 300x250", "assets": [ { "item_type": "individual", "asset_id": "main_image", "asset_type": "image", "required": true, "requirements": { "min_width": 300, "min_height": 250, "accepted_mime_types": ["image/png", "image/jpeg"] } }, { "item_type": "individual", "asset_id": "headline", "asset_type": "text", "required": true, "requirements": { "max_length": 50 } }, { "item_type": "individual", "asset_id": "impression_tracker", "asset_type": "url", "required": false, "requirements": { "url_type": "tracking_pixel" } } ] } ``` ### アセット配列が提供するもの * **完全な探索** — 必須だけでなく、フォーマットがサポートするすべてのアセットを確認できます * **タイプ情報** — 各アセットは `asset_type`(image、video、text、url など)を宣言します * **要件** — インライン制約(寸法、長さ、MIME タイプ) * **オプションアセット** — トラッカー、コンパニオンバナー、その他のオプション要素が見えるようになりました * **繰り返し可能なグループ** — カルーセルとマルチアイテムフォーマットは `item_type: "repeatable_group"` を使用します ### アセットアイテムタイプ `assets` 配列の各エントリには `item_type` 識別子がある: **個別アセット**(`item_type: "individual"`): ```json theme={null} { "item_type": "individual", "asset_id": "main_video", "asset_type": "video", "required": true, "requirements": { "min_duration_seconds": 15, "max_duration_seconds": 30, "accepted_mime_types": ["video/mp4"] } } ``` **繰り返し可能なグループ**(`item_type: "repeatable_group"`)— カルーセルとマルチアイテムフォーマット用: ```json theme={null} { "item_type": "repeatable_group", "asset_group_id": "card", "required": true, "min_count": 2, "max_count": 10, "selection_mode": "sequential", "assets": [ { "asset_id": "card_image", "asset_type": "image", "required": true, "requirements": { "min_width": 600, "min_height": 600, "aspect_ratio": "1:1" } }, { "asset_id": "card_title", "asset_type": "text", "required": true, "requirements": { "max_length": 40 } } ] } ``` ### フォーマットカード(preview\_image の置き換え) v2 の `preview_image` URL はクリエイティブレンダリングシステムを使用する `format_card` に置き換えられます: ```json theme={null} { "format_card": { "format_id": { "agent_url": "https://creatives.example.com", "id": "format_card_standard" }, "manifest": { "format_name": "Standard Banner 300x250", "preview_url": "https://cdn.example.com/previews/banner_300x250.png" } } } ``` *** ## 移行ステップ ### Format category `list_creative_formats` と `get_products` のリクエストから `format_types` を削除します。 `asset_types` を使って、フォーマットが受け入れるアセット(例: `["video"]`、`["image"]`)でフィルタリングします。 必要な特定のフォーマットが分かっている場合は、完全なフォーマットマッチングに `format_ids` を使います。 フォーマットオブジェクトから `type` フィールドを読む任意のコードを削除します — v3 ではもう存在しません。 ### クリエイティブアサインメント `creative_ids` 配列を `creative_assignments` オブジェクト配列に置き換える。 各アサインメントオブジェクトに `creative_id` を設定します。 不均等な分配が必要な場合はウェイトを追加し、それ以外は `weight` を省略します。 プレースメントレベルのターゲティングが必要な場合は `placement_ids` を追加します。 これらも `creative_assignments` を使用するが、`placement_ids` なし。 ### アセット探索 `assets_required` の解析を `assets` 配列の反復に置き換える。 すべてのリストされたアセットが必須と仮定するのではなく、各アセットの `required` ブールフラグを確認します。 各アセットが期待するファイルの種類を理解するために `asset_type` を使用します。 `"individual"` と `"repeatable_group"` を確認します。 `preview_image` の読み取りを `format_card` レンダリングに置き換える。 クリエイティブマニフェストはキーとして正確な `asset_id` 値を使用しなければなりません。 完全なクリエイティブドキュメント: フォーマット、アセットタイプ、マニフェスト、クリエイティブエージェント。 *** **関連:** [チャンネル](/docs/reference/migration/channels) | [価格](/docs/reference/migration/pricing) | [ジオターゲティング](/docs/reference/migration/geo-targeting) | [カタログ](/docs/reference/migration/catalogs) | [アトリビューション](/docs/reference/migration/attribution) | [AdCP 3.0 概要](/docs/reference/whats-new-in-v3) # ジオターゲティングの移行 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/geo-targeting AdCP ジオターゲティングを v2 から v3 に移行します。暗黙的な米国中心のターゲティングを、グローバル市場サポートのための名前付きシステム(Nielsen DMA、郵便番号)に置き換える。 # ジオターゲティングの移行 AdCP 3.0 は暗黙的な米国中心のジオターゲティングを、グローバル市場をサポートする名前付きシステムに置き換える。メトロと郵便ターゲティングには明示的なシステム仕様が必要になり、コードはシステムでグループ化されます。 ## 変更内容 | v2 フィールド | v3 フィールド | 変更タイプ | | ------------------------- | ---------------------------------------- | --------- | | `geo_metros`(文字列配列) | `geo_metros`(system/values オブジェクト) | 再構造化 | | `geo_postal_codes`(文字列配列) | `geo_postal_areas`(system/values オブジェクト) | 名前変更と再構造化 | 変更されていないフィールド: `geo_countries`、`geo_countries_exclude`、`geo_regions`、`geo_regions_exclude` はすべて ISO コードを使用するシンプルな文字列配列のまま。 ## メトロターゲティング **v2** — コードのフラット配列、Nielsen DMA と仮定: ```json theme={null} { "targeting": { "geo_metros": ["501", "602"] } } ``` **v3** — システムでグループ化されたコード: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/targeting.json", "geo_metros": [ { "system": "nielsen_dma", "values": ["501", "602"] } ] } ``` 各エントリはシステムとそのシステム内のコードを指定します。複数のシステムが共存できる: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/targeting.json", "geo_metros": [ { "system": "nielsen_dma", "values": ["501", "602"] }, { "system": "uk_itl2", "values": ["UKC1", "UKD3"] } ] } ``` ### メトロシステム | システム | カバレッジ | コード例 | | ---------------- | ------------ | ------------------------------------ | | `nielsen_dma` | 米国の指定市場エリア | `501`(ニューヨーク)、`602`(シカゴ) | | `uk_itl1` | 英国リージョン | `UKC`(北東)、`UKD`(北西) | | `uk_itl2` | 英国サブリージョン | `UKC1`(ティーズバレー)、`UKD3`(グレーターマンチェスター) | | `eurostat_nuts2` | EU 統計リージョン | `DE11`(シュトゥットガルト)、`FR10`(イル・ド・フランス) | | `custom` | パブリッシャー定義エリア | パブリッシャー固有のコード | サポートされるシステムは `metro-system.json` 列挙で定義: `nielsen_dma`、`uk_itl1`、`uk_itl2`、`eurostat_nuts2`、`custom`。 ## 郵便ターゲティング **v2** — コードのフラット配列、米国郵便番号(ZIP コード)と仮定: ```json theme={null} { "targeting": { "geo_postal_codes": ["10001", "90210"] } } ``` **v3** — `geo_postal_areas` に名前変更、コードはシステムでグループ化: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/targeting.json", "geo_postal_areas": [ { "system": "us_zip", "values": ["10001", "90210"] } ] } ``` ### 郵便システム | システム | カバレッジ | 精度 | コード例 | | ------------------ | ------- | -------------- | --------------- | | `us_zip` | 米国 | 5桁 ZIP | `10001`、`90210` | | `us_zip_plus_four` | 米国 | ZIP+4 | `10001-1234` | | `gb_outward` | 英国 | エリアレベル | `SW1`、`EC2` | | `gb_full` | 英国 | 完全な郵便番号 | `SW1A 1AA` | | `ca_fsa` | カナダ | フォワードソーティングエリア | `M5V`、`V6B` | | `ca_full` | カナダ | 完全な郵便番号 | `M5V 2T6` | | `de_plz` | ドイツ | Postleitzahl | `10115`、`80331` | | `fr_code_postal` | フランス | Code postal | `75001`、`13001` | | `au_postcode` | オーストラリア | 郵便番号 | `2000`、`3000` | | `ch_plz` | スイス | Postleitzahl | `8000`、`3000` | | `at_plz` | オーストリア | Postleitzahl | `1010`、`6020` | サポートされるシステムは `postal-system.json` 列挙で定義: `us_zip`、`us_zip_plus_four`、`gb_outward`、`gb_full`、`ca_fsa`、`ca_full`、`de_plz`、`fr_code_postal`、`au_postcode`、`ch_plz`、`at_plz`。 ## 除外ターゲティング v3 はメトロと郵便ターゲティングに `_exclude` バリアントを追加します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/targeting.json", "geo_countries": ["US"], "geo_metros_exclude": [ { "system": "nielsen_dma", "values": ["501"] } ] } ``` これはニューヨーク DMA を除く米国全体をターゲットにします。 ## セラーケイパビリティの発見 ジオターゲティングを送信する前に、バイヤーはセラーがリクエストされたシステムをサポートするか確認する必要があります。`get_adcp_capabilities` を使用します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/protocol/get-adcp-capabilities-response.json", "adcp": { "major_versions": [3] }, "supported_protocols": ["media_buy"], "media_buy": { "execution": { "targeting": { "geo_countries": true, "geo_regions": true, "geo_metros": { "nielsen_dma": true }, "geo_postal_areas": { "us_zip": true } } } } } ``` `geo_metros` と `geo_postal_areas` オブジェクトはブールプロパティを使ってどのシステムがサポートされているかを示します。セラーが宣言していないシステムをリクエストすると、バリデーションエラーが発生します。 ## 完全なターゲティング例 ジオ制限を組み合わせた v3 ターゲティングオーバーレイ: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/targeting.json", "geo_countries": ["US", "CA"], "geo_regions": ["US-NY", "US-CA"], "geo_metros": [ { "system": "nielsen_dma", "values": ["501", "803"] } ], "geo_postal_areas": [ { "system": "us_zip", "values": ["10001", "10002", "90210"] } ] } ``` インクルージョンフィールドは AND ロジックで組み合わされる — 配信はすべての指定された制約に一致しなければなりません。エクスクルージョンフィールド(`_exclude` バリアント)は AND NOT として機能する — 配信はインクルージョンに一致し、除外に一致してはなりません。 ## 移行ステップ `geo_metros` と `geo_postal_codes` のすべての使用箇所を見つける。 `geo_postal_codes` は `geo_postal_areas` になります。 フラット配列を `{ "system": "...", "values": [...] }` オブジェクトにラップします。 米国のみのコードには `nielsen_dma` と `us_zip` を使用します。国際的なコードには適切なシステムを追加します。 各セラーがサポートするシステムを確認するために `get_adcp_capabilities` を呼び出す。 必要に応じて、ネガティブターゲティングのために新しい `_exclude` バリアントを使用します。 完全なターゲティングリファレンス: オーディエンス、コンテキスト、地理、デバイスターゲティング。 *** **関連:** [チャンネル](/docs/reference/migration/channels) | [価格](/docs/reference/migration/pricing) | [クリエイティブ](/docs/reference/migration/creatives) | [カタログ](/docs/reference/migration/catalogs) | [アトリビューション](/docs/reference/migration/attribution) | [AdCP 3.0 概要](/docs/reference/whats-new-in-v3) # Migrating from v2 to v3 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/index AdCP インテグレーションを v2.x から v3.0 に移行する完全ガイド。破壊的変更、工数見積もり、各領域の詳細ページ付き。 # v2 から v3 への移行 このページは、AdCP 2.x から 3.0 にアップグレードする際のすべての破壊的変更を、工数見積もりと詳細な移行ページへのリンクとともにカバーします。新機能については [What's new in v3](/docs/reference/whats-new-in-v3) を、リリース候補の差分については [プレリリースアップグレードノート](/docs/reference/migration/prerelease-upgrades) を、3.0 をサポートする SDK バージョンについては [Schemas and SDKs](/docs/building/schemas-and-sdks#adcp-3-0-support) を参照。 **3.0 から 3.1 にアップグレードしますか?** [3.0 から 3.1 への移行ガイド](/docs/reference/migration/3-0-to-3-1) を使ってください。このページは破壊的な v2.x から 3.0 へのアップグレード用です。 **v2 は 3.0 GA 時点でサポートされず、2026 年 8 月 1 日(UTC)に完全に非推奨になります。** 完全なタイムライン、AAO レジストリポリシー、および v2 が相互運用可能な本番に安全でない理由については [v2 サンセットページ](/docs/reference/v2-sunset) を参照。 **v2 から始めますか?** この完全な移行を進める前にストーリーボードテストに合格するための 8 つの最小要件については [v3 レディネスチェックリスト](/docs/reference/migration/v3-readiness) を参照。 **rc.3 からアップグレードしますか?** [rc.3 → 3.0 プレリリースアップグレードノート](/docs/reference/migration/prerelease-upgrades) が追加の破壊的変更をカバーします: 機能モデルの簡素化、`update_media_buy` での `account` 必須化、`preview_creative` スキーマのフラット化、シグナルでの `signal_id` 必須化、ガバナンスライフサイクルの変更、`pending_activation` ステータスの分割。 ## 移行チェックリスト 各行は破壊的変更です。**工数**は典型的な作業を示します。 * **Rename** — フィールド名が変わったが、セマンティクスは同じ。検索置換。 * **Restructure** — 形状が変わった(例: string → object、single → array)。コード変更が必要。 * **Remove** — v2 に存在したが v3 で削除。検索削除。 * **New requirement** — v2 に存在しなかった。新しい実装が必要。 | Area | Change | Effort | Details | | --------------------- | ------------------------------------------------------------------------------ | --------------- | ------------------------------------------------------------------------------- | | Channels | `native` 削除 | Restructure | [Channels migration](/docs/reference/migration/channels) | | Channels | `video` が `olv`、`linear_tv`、`cinema` に分割 | Restructure | [Channels migration](/docs/reference/migration/channels) | | Channels | 10 の新チャネル追加(`sponsored_intelligence` を含む) | Rename | [Channels migration](/docs/reference/migration/channels) | | Pricing | `fixed_rate` → `fixed_price` | Rename | [Pricing migration](/docs/reference/migration/pricing) | | Pricing | `price_guidance.floor` → `floor_price` | Rename | [Pricing migration](/docs/reference/migration/pricing) | | Pricing | 価格ガイダンスの再構成 | Restructure | [Pricing migration](/docs/reference/migration/pricing) | | Creatives | `creative_ids` → 重み付き `creative_assignments` | Restructure | [Creatives migration](/docs/reference/migration/creatives) | | Creatives | `assets` 配列によるアセット探索 | New requirement | [Creatives migration](/docs/reference/migration/creatives) | | Catalogs | `promoted_offerings` → `sync_catalogs` | Restructure | [Catalogs migration](/docs/reference/migration/catalogs) | | Geo targeting | フラット配列 → システム指定が必須 | Restructure | [Geo targeting migration](/docs/reference/migration/geo-targeting) | | Optimization | `optimization_goal`(単一) → `optimization_goals`(配列、判別共用体) | Restructure | [Optimization goals migration](/docs/reference/migration/optimization-goals) | | Brand identity | `brand_manifest` → `brand` ref(`{ domain, brand_id }`) | Restructure | [Brand identity migration](/docs/reference/migration/brand-identity) | | Capability discovery | `adcp-extension.json` → `get_adcp_capabilities` | Restructure | [Capability discovery](/docs/protocol/get_adcp_capabilities) | | Signals | 配信のフラット化、価格の再構成 | Restructure | [Signals migration](/docs/reference/migration/signals) | | Audiences | `external_id` が必須のトップレベルフィールドに昇格 | Restructure | [Audiences migration](/docs/reference/migration/audiences) | | Attribution | 整数の日数 → `Duration` オブジェクト | Restructure | [Attribution migration](/docs/reference/migration/attribution) | | Products | すべての `get_products` リクエストで `buying_mode` 必須 | New requirement | [get\_products reference](/docs/media-buy/task-reference/get_products) | | Media buy status | `pending_activation` → `pending_start` | Rename | [Media buys](/docs/media-buy/media-buys) | | Media buy status | `pending_creatives` 追加(クリエイティブがまだ割り当てられていない) | New requirement | [Media buys](/docs/media-buy/media-buys) | | Task status | レガシー `task_status` と `response_status` フィールドは v3 の `status` と併存させてはならない — 両方削除 | Remove | [Task lifecycle](/docs/building/by-layer/L3/task-lifecycle) | | Capabilities | `get_adcp_capabilities` の `compliance_testing` 機能ブロック | Additive | [Capability discovery](/docs/protocol/get_adcp_capabilities#compliance_testing) | | Idempotency | すべての変更リクエストで `idempotency_key` 必須(UUID v4) | New requirement | [Security § Idempotency](/docs/building/by-layer/L1/security) | | Request signing | RFC 9421 Ed25519 署名プロファイル(3.0 では任意、AdCP Verified では必須) | Additive | [Security § Request signing](/docs/building/by-layer/L1/security) | | Webhook signing | RFC 9421 プロファイルに統一、セラーにベースライン必須。HMAC フォールバックは非推奨(4.0 で削除) | New requirement | [Webhooks](/docs/building/by-layer/L3/webhooks) | | Webhook idempotency | すべての webhook ペイロードで `idempotency_key` 必須 | New requirement | [Webhooks § Reliability](/docs/building/by-layer/L3/webhooks) | | Governance | `governance_context` はガバナンスエージェント JWKS 経由で検証される署名付き JWS | Restructure | [Governance](/docs/governance/campaign) | | IO approval | `MediaBuy.pending_approval` 削除 — 承認はタスク層のオブジェクト | Restructure | [Media buy lifecycle](/docs/media-buy/media-buys) | | Regulatory invariants | GDPR 第22条 / EU AI Act 附属書 III がスキーマ `if/then` で強制 | New requirement | [Governance § Annex III obligations](/docs/governance/annex-iii-obligations) | `get_products` の `buying_mode` はストーリーボードテストでチェックされます。`brief` がベースラインモードです。機能で `wholesale` または `refine` を宣言するセラーは、それらのモードセマンティクスを処理しなければなりません。詳細は [v3 レディネスチェックリスト](/docs/reference/migration/v3-readiness) を参照。 ## v3 の新機能 — 必須対オプション これらの機能は v3 で新しいものです。いずれも v2 に存在しなかったため移行するものはありません — しかしどれがインテグレーションに影響するかを知っておくべきです。 | Capability | Required? | Who needs it | | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | | Accounts プロトコル(`sync_accounts`、`list_accounts`) | すべてのバイヤーに必須 — `require_operator_auth: false`(バイヤー宣言アカウント)のとき `sync_accounts`、`true` でセラーが account-id 名前空間を公開するとき `list_accounts` | すべてのバイヤー — アカウントが必要かどうかではなく、セラーのアカウントモデルがどのタスクを呼ぶかを決める | | Brand Protocol(`brand.json`) | 推奨 | すべてのバイヤー — クリエイティブ生成のためのブランドアイデンティティを提供 | | Governance(コンテンツ標準、プロパティリスト) | オプション | ブランド適合性の強制を必要とするバイヤー | | Sponsored Intelligence | オプション | 会話型ハンドオフをサポートする AI プラットフォームと連携するバイヤー | | Registry API | オプション | プログラム的なエージェント/ブランド探索を望むバイヤー | | Campaign Governance | オプション | コンプライアンスや承認ワークフローを持つ組織 | ## v2 と v3 を並行稼働させる デュアルサポートは一時的な移行ツールであり、長期的な姿勢ではありません。2026 年 8 月 1 日以降、v3 のみが必須の構成です — [v2 サンセットページ](/docs/reference/v2-sunset) を参照。 移行中、セラーは v2 と v3 の両方のトラフィックを受け入れることができ、バイヤーは各セラーに正しいバージョンでルーティングできます。 1. **セラーの機能を確認** — 各セラーで `get_adcp_capabilities` を呼び出します。成功したレスポンスはセラーが v3 をサポートすることを意味します。バイヤーは `adcp_major_version` でバージョンを宣言し、セラーは `major_versions` で受け入れるバージョンをアドバタイズします。完全なフローについては [バージョンネゴシエーション](/docs/reference/versioning#version-negotiation) を参照。`get_adcp_capabilities` に応答しないセラーは v2 のみです。 2. **セラーごとに分岐** — v3 対応のセラーを v3 インテグレーション経由で、v2 のみのセラーを既存の v2 コード経由でルーティングします。 3. **段階的に移行** — リネーム変更(価格フィールド、チャネル更新)から始め、次に構造変更(クリエイティブ割り当て、最適化目標)に取り組み、次に必要に応じて新機能(アカウント、ガバナンス)を採用します。 ## 破壊的移行(v2 → v3.0) `native` 削除、`video` 分割、10 の新チャネル フィールドのリネームと価格ガイダンスの再構成 重み付きクリエイティブ割り当てとアセット探索 `promoted_offerings` から一級の `sync_catalogs` へ グローバルジオサポートのためのシステム指定 単一目標から判別共用体を持つ配列へ `brand_manifest` から `brand.json` 経由の `brand` ref へ 配信のフラット化と価格の再構成 `external_id` の必須フィールドへの昇格 整数の日数から構造化 `Duration` オブジェクトへ ## 3.1 バッジ準備ガイド 互換性を壊さずに 3.0 に留まれます。3.1 バッジを取得するには、この短い準備チェックリストを完了します。 バイヤー、セラー、エージェント、SDK、コンプライアンスワークフローのための追加的な準備 ビルド機能の探索と価格をフォーマットからトランスフォーマーに移動 成功レスポンスでボディレベルの購入ステータスをエンベロープのタスクステータスから分離 # `media_buy_status` への移行(3.1) Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/media-buy-status create_media_buy と update_media_buy の成功レスポンスで、ボディレベルの status から media_buy_status へ移す。3.1 では加算的、レガシーフィールドは 3.2 で削除、ネストされた status カスケードは 4.0 で続く。 # `media_buy_status` への移行(3.1) AdCP 3.1 は、3.0 が同じレスポンスルートキーで衝突させていた 2 つの enum を分割します: * **エンベロープ `status`** — TaskStatus(`submitted` / `working` / `input-required` / `completed` / `canceled` / `failed` / `rejected` / `auth-required` / `unknown`)。beta.2 から必須([#4876](https://github.com/adcontextprotocol/adcp/issues/4876))。 * **ボディ `media_buy_status`** — MediaBuyStatus(`pending_creatives` / `pending_start` / `active` / `paused` / `completed` / `rejected` / `canceled`)。**3.1 で新規。** MCP のフラットオンザワイヤーシリアライゼーションの下では、両フィールドがレスポンスルートを共有します。3.0 では両方が `status` と名付けられ、ボディレベルの MediaBuyStatus は、エンベロープが同じパスに TaskStatus をスタンプすると黙って破壊されました。どの検証器もそれを捕まえませんでした。3.1 はそれらを分割します。 ## 何が変わったか | Surface | 3.0 | 3.1 | | ----------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `create_media_buy` 成功レスポンス | ルートの `status`(MediaBuyStatus) | ルートの `media_buy_status`(MediaBuyStatus)。レガシー `status` は `deprecated: true` | | `update_media_buy` 成功レスポンス | 同じ | 同じ | | `get_media_buys` アイテム | `media_buys[].status`(MediaBuyStatus) | 3.1 では変更なし — 4.0 で `media_buys[].media_buy_status` にリネーム([#4905](https://github.com/adcontextprotocol/adcp/issues/4905)) | | `get_media_buy_delivery` アイテム | `media_buy_deliveries[].status`(MediaBuyStatus) | 3.1 では変更なし — 4.0 でリネーム([#4905](https://github.com/adcontextprotocol/adcp/issues/4905)) | | `core/media-buy.json` | `status`(MediaBuyStatus) | 3.1 では変更なし — 4.0 でリネーム([#4905](https://github.com/adcontextprotocol/adcp/issues/4905)) | ## Before(3.0) ```json theme={null} { "status": "completed", "media_buy_id": "mb_12345", "status": "active", "packages": [...] } ``` `status` という名前の 2 つのキーが、MCP フラットシリアライゼーションの下で JSON ルートで衝突します — ボディレベルの `MediaBuyStatus: 'active'` 値がエンベロープの `TaskStatus: 'completed'` によって黙って破壊されます。どの検証器もそれを捕まえません。 ## After(3.1) ```json theme={null} { "status": "completed", "media_buy_id": "mb_12345", "media_buy_status": "active", "packages": [...] } ``` 2 つの明確なフィールド。エンベロープ `status` はルートでタスクライフサイクル状態を運びます。ボディ `media_buy_status` はバイのライフサイクル状態を隣で運びます。 ## 3.1 適合性 * **セラー** は `create_media_buy` と `update_media_buy` の成功レスポンスに `media_buy_status` を発すべき(SHOULD)。3.1 非推奨ウィンドウ中は非推奨のトップレベル `status: MediaBuyStatus` を発し続けてもよい(MAY)。 * **バイヤー** は存在するとき `media_buy_status` を優先しなければならない(MUST)。レガシー形式のままのセラーとの互換性のためにレガシー `status` にフォールバックしてもよい(MAY)。 * **3.0 セラーとバイヤー** は変更なく動作し続けます。`required[]` のスワップなし、リネームなし、破壊なし。 * **コンプライアンスストーリーボード** は `path: "media_buy_status"` をアサートします。レガシー `status` のみを発する 3.1 セラーはスキーマ有効ですが、3.1 ストーリーボード認証に失敗します。ストーリーボードが拘束力のある適合性チェックです。スキーマの `deprecated: true` マーカーは助言的です。 * **両フィールドを発するセラー** は、`media_buy_status` と非推奨の `status` に同一の値を発しなければなりません(MUST)。分岐した発行(例: `status: "active", media_buy_status: "paused"`)は JSON Schema 検証を通過しますが適合性違反です — 3.1 ストーリーボードは、正準の `media_buy_status` チェックと並んで `status` の `field_value_or_absent` アサーションを通じて等価を強制します。`if/then` JSON Schema 制約は評価され延期されました: 移行ウィンドウが短く、codegen ツールチェーンの互換性が不確実で、ストーリーボードゲートで十分だからです。[#4908](https://github.com/adcontextprotocol/adcp/issues/4908) を参照。 ## SDK の動作 レガシー `status` フィールドは `deprecated: true`(JSON Schema 2020-12)を運びます。codegen を通じた伝播は異なります: | Toolchain | Propagation | | ----------------------------------------------- | ------------------------------------------------------ | | TypeScript(`json-schema-to-typescript`) | フィールドに `@deprecated` JSDoc。信頼できる。 | | Python(`datamodel-code-generator` v2+) | `Field(...)` 引数に `deprecated=True`。古いピン留めバージョンは黙って落とす。 | | Go(`quicktype` など) | 一般的に伝播されない。 | | `@adcp/client` 3.1+、Python `adcp` SDK、`adcp-go` | 正準の `media_buy_status` が SDK ユーザーの消費する型付き形状。 | ツールチェーンが非推奨をサーフェスしない場合、ストーリーボードゲートが強制シグナルです。 ## レガシーフィールドが消えるとき * **3.2**([#4906](https://github.com/adcontextprotocol/adcp/issues/4906)): 非推奨のトップレベル `status: MediaBuyStatus` が `CreateMediaBuySuccess` と `UpdateMediaBuySuccess` から **削除** されます。3.2 の後、これらのレスポンスのトップレベル `status` は明確にエンベロープ TaskStatus のみを運びます。非推奨ウィンドウは意図的に短い — ストーリーボードゲートがすでに 3.1 準拠セラーをレガシーフィールドから追い出します。 * **4.0**([#4905](https://github.com/adcontextprotocol/adcp/issues/4905)): ネストされた `status` カスケードが着地します — `get-media-buys-response` の `media_buys[].status`、`get-media-buy-delivery-response` の `media_buy_deliveries[].status`、`core/media-buy.json` の `status` が `media_buy_status` にリネーム。真に破壊的(`required[]` スワップ)で、メジャーに保留。 ## 前方互換のバイヤーコード 3.0、3.1、4.0 セラーにまたがる必要があるコード: ```js theme={null} // media_buy_status(3.1+)を優先、status(3.0 + 4.0 ネストサーフェス互換)にフォールバック const mediaBuyStatus = response.media_buy_status ?? response.status; const buyLifecycleStatus = mediaBuy.media_buy_status ?? mediaBuy.status; ``` ## 関連 * [create\_media\_buy リファレンス](/docs/media-buy/task-reference/create_media_buy) — 正準レスポンス例 * [メディアバイライフサイクル](/docs/media-buy/media-buys/lifecycle) — MediaBuyStatus ステートマシン * [エンベロープ task-status](/docs/building/by-layer/L3/task-lifecycle) — TaskStatus セマンティクス # 最適化目標の移行 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/optimization-goals AdCP 最適化目標を beta.3 から rc.1 に移行します。単数の optimization_goal を、識別子付きユニオンと優先順位付けを使用したマルチゴール配列に置き換える。 # 最適化目標の移行 AdCP 3.0 rc.1 は単数の `optimization_goal` オブジェクトを `optimization_goals` 配列に置き換える。各目標は `kind` の識別子付きユニオンで、優先順位付きのマルチゴールパッケージをサポートします。 ## 変更内容 | beta.3 | rc.1 | 注記 | | ----------------------------- | --------------------------------------------------- | ---------------------------------- | | `optimization_goal`(単一オブジェクト) | `optimization_goals`(配列) | 識別子付きユニオンの配列 | | 暗黙的な単一目標 | `priority` フィールド | 1 = 最高優先度 | | 1つの目標タイプ | 2種類: `metric` と `event` | `kind` フィールドで識別 | | リーチ最適化なし | `reach_unit` と `target_frequency` を持つ `reach` メトリック | プロダクトが `supported_reach_units` を宣言 | ## 目標の種類 すべての目標には `kind` 識別子がある: * **`metric`** — セラーネイティブのデリバリーメトリック(クリック、視聴、リーチ、エンゲージメントなど) * **`event`** — `sync_event_sources` で設定されたイベントソースに紐づいたコンバージョントラッキング ### メトリック目標 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/latest/core/optimization-goal.json", "kind": "metric", "metric": "clicks", "target": { "kind": "cost_per", "value": 2.50 }, "priority": 1 } ``` サポートされるメトリック: `clicks`、`views`、`completed_views`、`viewed_seconds`、`attention_seconds`、`attention_score`、`engagements`、`follows`、`saves`、`profile_visits`、`reach`。 ターゲットタイプ: * `cost_per` — メトリックユニットあたりの目標コスト(例: \$2.50 CPC) * `threshold_rate` — 目標レート閾値(例: 2% CTR の 0.02) ### イベント目標 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/latest/core/optimization-goal.json", "kind": "event", "event_sources": [ { "event_source_id": "es_web_pixel", "event_type": "purchase", "value_field": "order_total", "value_factor": 1 } ], "target": { "kind": "per_ad_spend", "value": 4.0 }, "attribution_window": { "post_click": { "interval": 7, "unit": "days" }, "post_view": { "interval": 1, "unit": "days" } }, "priority": 2 } ``` イベントターゲットタイプ: * `cost_per` — コンバージョンあたりの目標コスト(CPA) * `per_ad_spend` — 広告費用対効果(ROAS)の目標。`value_field` が必要。 * `maximize_value` — 総コンバージョン価値を最大化。`value_field` が必要。 ### リーチ目標 リーチは追加フィールドを持つメトリック目標だ: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/latest/core/optimization-goal.json", "kind": "metric", "metric": "reach", "reach_unit": "individuals", "target_frequency": { "min": 2, "max": 5, "window": { "interval": 7, "unit": "days" } }, "priority": 1 } ``` `reach_unit` の値: `individuals`、`households`、`devices`、`accounts`、`cookies`、`custom`。 `target_frequency` は `min` または `max` の少なくとも一方と、Duration オブジェクトとしての `window`(例: `{"interval": 7, "unit": "days"}` または `{"interval": 1, "unit": "campaign"}`)が必要です。 ## マルチゴールパッケージ 複数の目標は `priority`(1 = 最高)で順序付けられます。セラーは優先度の高い目標を優先的に最適化し、低い優先度の目標をタイブレーカーとして使用します。 **beta.3:** ```json theme={null} { "optimization_goal": { "metric": "clicks", "target_cpc": 2.50 } } ``` **rc.1:** ```json theme={null} { "optimization_goals": [ { "kind": "metric", "metric": "clicks", "target": { "kind": "cost_per", "value": 2.50 }, "priority": 1 }, { "kind": "event", "event_sources": [ { "event_source_id": "es_web_pixel", "event_type": "purchase" } ], "target": { "kind": "cost_per", "value": 25.00 }, "priority": 2 } ] } ``` ## プロダクトケイパビリティ プロダクトは `metric_optimization` を通じて最適化サポートを宣言する: ```json test=false theme={null} { "metric_optimization": { "supported_metrics": ["clicks", "views", "completed_views", "reach"], "supported_reach_units": ["individuals", "households"], "supported_view_durations": [6, 15, 30], "supported_targets": ["cost_per", "threshold_rate"] }, "max_optimization_goals": 3 } ``` * `supported_metrics` — プロダクトが最適化できるメトリック * `supported_reach_units` — `reach` が supported\_metrics にある場合に必要 * `supported_view_durations` — `completed_views` メトリックの秒数 * `supported_targets` — 利用可能なターゲット種類。省略された場合、ターゲットなしの目標(ボリューム最大化)のみ許可されます * `max_optimization_goals` — パッケージあたりの目標の最大数 ## 移行ステップ すべてのリクエスト構築コードで `optimization_goal`(単数)を `optimization_goals`(配列)に置き換える。 既存の目標を `"kind": "metric"` または `"kind": "event"` でラップします。メトリック目標はセラーネイティブのメトリックを使用し、イベント目標は `sync_event_sources` からのイベントソースを参照します。 フラットなターゲットフィールド(例: `target_cpc`)を識別子付きの `target` オブジェクトに置き換える: `{ "kind": "cost_per", "value": 2.50 }`。 単一目標パッケージには `priority: 1` を設定します。マルチゴールパッケージには昇順の優先度値を割り当てる(1 = 最高)。 レスポンスから最適化目標を読む場合(例: `get_media_buy`)、タイプ固有のフィールドにアクセスする前に `kind` で分岐して目標タイプを決定します。 目標を送信する前に、プロダクトの `metric_optimization.supported_metrics` と `max_optimization_goals` を確認します。セラーはサポートされていないメトリックと制限を超えた目標を拒否します。 リクエストを `optimization-goal.json` スキーマに対して実行します。識別子付きユニオンは種類ごとに正しいフィールドを適用します。 イベントソースを設定し、コンバージョンイベントを送信し、デリバリー目標を最適化します。 *** **関連:** [チャンネル](/docs/reference/migration/channels) | [価格](/docs/reference/migration/pricing) | [シグナル](/docs/reference/migration/signals) | [AdCP 3.0 概要](/docs/reference/whats-new-in-v3) # プレリリースアップグレードノート Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/prerelease-upgrades AdCP 3.0.0 リリース候補間の破壊的変更と加算的変更。 # プレリリースアップグレードノート プレリリースバージョンを採用した場合、アップグレード前に下記の該当セクションをレビューしてください。v2 から移行する場合は [メイン移行ガイド](/docs/reference/migration) を参照。 ## rc.3 → 3.0 ### rc.3 採用者にとって破壊的 | Area | rc.3 | 3.0 | What to do | | ----------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 変更リクエストの `idempotency_key` | 任意 | すべての変更リクエストで **必須**(スキーマ `^[A-Za-z0-9_.:-]{16,255}$`。Verified には UUID v4)。セラーはケイパビリティで `adcp.idempotency = { supported: true/false }` を宣言。 | 論理操作ごとに新しいキーを生成。エージェントインスタンスをまたいでキーを永続化。`get_adcp_capabilities` に `adcp.idempotency` を宣言(セラー)。`supported: true` のとき `IDEMPOTENCY_CONFLICT` と `IDEMPOTENCY_EXPIRED` を扱う。適合性プローブは変異ペイロードリプレイが CONFLICT を返すことを要求。`supported: false` のとき、盲目的なリトライの代わりに自然キーチェックを使う。[Security § Idempotency](/docs/building/by-layer/L1/security) を参照。 | | Webhook 署名 | `push_notification_config.authentication`(必須)を伴う HMAC-SHA256 | RFC 9421 プロファイル(セラーにベースライン必須)。HMAC フォールバックは `authentication.credentials` 経由で 3.x を通じて利用可能 | `jwks_uri` の JWKS に署名 JWK を公開(`brand.json` `agents[]` から参照)。新しい署名者は webhook に `adcp_use: "request-signing"` を使う。非推奨の `adcp_use: "webhook-signing"` キーは互換性ウィンドウ中受け入れられたまま。webhook 専用の鍵素材が欲しい場合、異なる `kid` を持つ 2 番目の `request-signing` JWK を公開。新しい設定から `push_notification_config.authentication` を落とす。バイヤーは `authentication.credentials` 経由でレガシー HMAC にオプトイン。受信者は送信者の JWKS に対して検証。`authentication` オブジェクト全体(HMAC + Bearer)は 4.0 で削除。 | | Webhook ペイロードの `idempotency_key` | 標準化されていない(脆弱な `(task_id, status, timestamp)` タプル重複排除) | **必須** — すべてのペイロードで送信者生成の UUID v4 | セラー: イベントごとに暗号学的にランダムな UUID v4 を生成。受信者: 24h 最小 TTL、送信者スコープのキャッシュで `idempotency_key` を重複排除。影響を受けるスキーマ: `mcp-webhook-payload`、`collection-list-changed-webhook`、`property-list-changed-webhook`、`artifact-webhook-payload`、`revocation-notification`。 | | `revocation-notification.notification_id` | 権利失効ペイロードのフィールド名 | `idempotency_key` にリネーム | 権利失効受信者で find-and-replace。 | | `MediaBuy.pending_approval` ステータス | 存在 | 削除 — 承認は明示的な承認タスク | メディアバイ状態フィルターから `pending_approval` を削除。タスクサーフェスから承認タスクを消費。 | | 予算の自律性 | `budget.authority_level` enum(`agent_full \| agent_limited \| human_required`) | 削除。`budget.reallocation_threshold`(数値)+ `plan.human_review_required`(boolean)に分割 | 書き換え: `agent_full` → `reallocation_unlimited: true`。`agent_limited` → `reallocation_threshold: `。`human_required` → `human_review_required: true`。規制されたバーティカル(fair housing、lending、employment、pharmaceutical)はスキーマ `if/then` 経由で `human_review_required: true` を強制。 | | `inventory-lists` 専門分野 | 存在 | `property-lists` にリネーム。`collection-lists` が別個の専門分野として分離 | 専門分野クレームを更新。コレクションリストを統治したエージェントは今や `property-lists` と `collection-lists` の両方を主張すべき。 | | コンプライアンスパスタクソノミー | `/compliance/{v}/domains/` | `/compliance/{v}/protocols/` | コンプライアンスパスへの内部参照を更新。ランナーとカタログは `protocols/` を排他的に使う。 | | `governance_context` キャリア | 不透明な文字列 | 署名付き JWS | JWS 形式に切り替え。ガバナンスエージェント JWKS 経由で署名を検証(`sync_governance` 経由で解決)。`sub`、`aud`、`phase`、`exp` にバインド。 | | メディアバイステータス | `pending_activation` | 削除 — `pending_creatives` と `pending_start` に置き換え | ステータスフィルター、比較、ステートマシンロジックで `pending_activation` を置き換え。スキーマ: `enums/media-buy-status.json`。下記の詳細を参照。 | | ケイパビリティモデル | 冗長な boolean ゲート(`features.content_standards`、`brand.identity`、`trusted_match.supported` など) | 削除 — オブジェクトの存在がシグナル | boolean ケイパビリティチェックを削除。代わりにオブジェクトの存在をテスト。スキーマ: `protocol/get-adcp-capabilities-response.json`。下記の [capabilities migration](#capabilities-model-simplification) を参照。 | | `reporting_capabilities` | プロダクトで任意 | すべてのプロダクトで必須 | `get_products` から返されるすべてのプロダクトが `reporting_capabilities` を含むことを確認。スキーマ: `core/product.json`。 | | `update_media_buy` の `account` | 任意 | 必須 | `create_media_buy` の動作に合わせ、すべての `update_media_buy` 呼び出しで `account` を渡す。スキーマ: `media-buy/update-media-buy-request.json`。 | | `preview_creative` スキーマ | oneOf 共用体 | `request_type` 判別子を持つフラットオブジェクト | `request_type` フィールドを持つフラットスキーマを使うようリクエストビルダーを更新。スキーマ: `creative/preview-creative-request.json`。下記の [preview\_creative migration](#preview_creative-schema-flattening) を参照。 | | `get_signals` レスポンスの `signal_id` | シグナルアイテムで任意 | 必須 | `get_signals` レスポンスのすべてのシグナルアイテムが `signal_id` を含むことを確認。スキーマ: `signals/get-signals-response.json`。 | | `GOVERNANCE_DENIED` エラー | エラーコード enum にない | correctable エラーとして追加 | エラー処理ロジックで `GOVERNANCE_DENIED` を扱う。スキーマ: `enums/error-code.json`。 | | ガバナンスライフサイクル | ライフサイクル相関子としての `media_buy_id` | 削除 — `governance_context` が唯一のライフサイクル相関子 | ガバナンススキーマの `media_buy_id` を `governance_context` に置き換え。`check_governance` と `report_plan_outcome` の `purchase_type` フィールドを扱う。スキーマ: `governance/check-governance-request.json`。下記の [governance migration](#governance-lifecycle-migration) を参照。 | | Geo ケイパビリティフィールド | `supported_geo_levels`、`supported_metro_systems`、`supported_postal_systems`(#2143 から) | 削除 — 型付きオブジェクト(`geo_countries`、`geo_regions`、`geo_metros`、`geo_postal_areas`)を使う | #2143 のフラット配列形状を採用した場合、`additionalProperties: false` を持つ型付き geo オブジェクトに戻す。スキーマ: `protocol/get-adcp-capabilities-response.json`。 | | `comply_test_controller` スキーマ | oneOf 共用体 | `scenario` 判別子と if/then 検証を持つフラットオブジェクト | oneOf バリアントの代わりに `scenario` フィールドを持つフラットスキーマを使うようリクエストビルダーを更新。 | | コンプライアンステストサーフェス | 中間の rc.4 ビルドは `"compliance_testing"` を `supported_protocols` 値として受け入れた | GA 前に削除。コンプライアンステストは `supported_protocols` ではなくトップレベルの `compliance_testing: { scenarios: [...] }` ケイパビリティブロック経由で宣言。 | 存在する場合 `supported_protocols` から `"compliance_testing"` を削除。`comply_test_controller` を実装するエージェントは代わりにトップレベルの `compliance_testing: { scenarios: [...] }` ブロックを追加。 | | 専門分野 ID | `broadcast-platform`、`social-platform`、`property-governance`、`collection-governance` | リネーム/統合: `sales-broadcast-tv`、`sales-social`、`inventory-lists`(property + collection ガバナンスを統合) | `get_adcp_capabilities.specialisms` の専門分野クレームを更新。[Compliance Catalog](/docs/building/compliance-catalog) を参照。 | | `audience-sync` 親プロトコル | `governance` の下 | `media-buy` に移動 | `audience-sync` を主張する場合、`supported_protocols` に `media_buy` を追加。 | | Sponsored Intelligence スコープ | 専門分野としての `sponsored_intelligence` | `supported_protocols` の完全なプロトコルに昇格 | `specialisms` から `supported_protocols` に移す。 | ### Media buy status migration `pending_activation` は 2 つの明確な条件をカバーする単一の状態でした。それは分割されました: | Condition | rc.3 status | 3.0 status | | ------------------- | -------------------- | ------------------- | | バイ承認済み、クリエイティブ未割り当て | `pending_activation` | `pending_creatives` | | バイ配信準備完了、フライト日待ち | `pending_activation` | `pending_start` | | バイが配信中 | `active` | `active`(変更なし) | **変更すべきこと:** 1. **ステータスフィルター** — `get_media_buys` と `get_media_buy_delivery` の `status_filter` 配列で、`pending_activation` を `pending_creatives` と `pending_start` の両方に置き換え。 2. **ステータス比較** — 任意の `if (status === 'pending_activation')` は、チェックしている条件で分岐する必要がある。「まだ配信していない」が欲しいなら `pending_creatives` と `pending_start` の両方をチェック。「準備完了だがフライト日待ち」が欲しいなら `pending_start` のみをチェック。 3. **ステートマシン遷移** — `rejected` は今や `pending_creatives` と `pending_start` の両方から有効(以前は `pending_activation` からのみ)。`pending_creatives` → `pending_start` は、クリエイティブが `sync_creatives` 経由で割り当てられたときに起こる。 4. **レガシーエイリアス** — `pending` は配信レスポンスステータスフィルターで `pending_start` のエイリアスとして受け入れられ続ける。 完全なステートマシンについては [正準ライフサイクル図](/docs/media-buy/media-buys#lifecycle-states) を参照。 ### Capabilities model simplification PR #2143 は冗長な boolean ケイパビリティフィールドを削除しました。オブジェクトの存在が今やサポートを示します — オブジェクトがあれば、ケイパビリティがあります。 **削除されたフィールドと置き換え:** | Removed field | What to do instead | | ----------------------------------------------------------- | ------------------------------------------- | | `media_buy.reporting` | プロダクトレベルの `reporting_capabilities`(今や必須)を使う | | `features.content_standards` | `content_standards` オブジェクトの存在をチェック | | `features.audience_targeting` | `audience_targeting` オブジェクトの存在をチェック | | `features.conversion_tracking` | `conversion_tracking` オブジェクトの存在をチェック | | `content_standards_detail` | `content_standards` にリネーム | | `brand.identity` | ブランドプロトコルサポートによって暗示 | | `trusted_match.supported` | `trusted_match` オブジェクトの存在をチェック | | `targeting.device_platform` / `targeting.device_type` | `media_buy` プロトコルサポートによって暗示 | | `targeting.audience_include` / `targeting.audience_exclude` | `audience_targeting` の存在によって暗示 | **Before(rc.3):** ```json theme={null} { "features": { "content_standards": true, "audience_targeting": true }, "trusted_match": { "supported": true, "uid_types": ["email_sha256"] } } ``` **After(3.0):** ```json theme={null} { "content_standards": { ... }, "audience_targeting": { ... }, "trusted_match": { "uid_types": ["email_sha256"] } } ``` ### preview\_creative schema flattening `preview_creative` リクエストは、oneOf 共用体から `request_type` 判別子を持つ単一オブジェクトにフラット化されます。3 つのモード: | `request_type` | Required field | Purpose | | -------------- | ------------------------ | ----------------- | | `single` | `creative_manifest` | 1 つのクリエイティブをプレビュー | | `batch` | `requests`(配列、1-50 アイテム) | 複数のクリエイティブをプレビュー | | `variant` | `variant_id` | フライト後のバリアントをリプレイ | **Before(rc.3):** ```json theme={null} { "creative_manifest": { "format_id": { ... }, "assets": { ... } } } ``` **After(3.0):** ```json theme={null} { "request_type": "single", "creative_manifest": { "format_id": { ... }, "assets": { ... } } } ``` スキーマ: `schemas/creative/preview-creative-request.json` ### Governance lifecycle migration `media_buy_id` はガバナンススキーマから削除されます。`governance_context` は、`sync_plans`、`check_governance`、`report_plan_outcome`、`get_plan_audit_logs` にわたる唯一のライフサイクル相関子として機能する不透明な文字列です。 **Before(rc.3):** ```json theme={null} { "media_buy_id": "mb_456", "planned_delivery": { ... } } ``` **After(3.0):** ```json theme={null} { "governance_context": "campaign_2024_q4_nova", "purchase_type": "media_buy", "planned_delivery": { ... } } ``` スキーマ: `schemas/governance/check-governance-request.json` ### context and ext fields ガバナンス、コレクション、プロパティ、sponsored-intelligence、コンテンツ標準プロトコルにわたるすべてのリクエストとレスポンスのスキーマは、今やアプリケーションメタデータとプロトコル拡張のための任意の `context` と `ext` フィールドを含みます。 ### Additive changes in 3.0 * **RFC 9421 リクエスト署名プロファイル(3.0 では任意、AdCP Verified の下では必須)** — 正準化されたカバードコンポーネントリストを持つ Ed25519 HTTP Message Signatures。公開されたテストベクターは `static/compliance/source/test-vectors/request-signing/`。ビット同一の正準入力のため sf-binary エンコーディングと URL 正準化がピン留め。`keyid` cap-before-crypto を持つ 15 ステップの検証チェックリスト。 * **RFC 9421 に統一された Webhook 署名** — webhook を発するセラーにベースライン必須。セラーは `jwks_uri` の JWKS に署名 JWK を公開。新しい署名者は webhook 配信に `adcp_use: "request-signing"` を使う一方、非推奨の `webhook-signing` キーは互換性ウィンドウ中受け入れられたまま。webhook 専用の鍵素材が欲しい場合は異なる `kid` を使う。[Security ガイド](/docs/building/by-layer/L1/security) の 14 ステップの webhook 検証者チェックリスト。HMAC-SHA256 は 3.x を通じてレガシーフォールバックのまま(`authentication` オブジェクト全体は 4.0 で削除)。 * **すべての webhook ペイロードで必須の `idempotency_key`** — 5 つすべての webhook ペイロードスキーマにわたる送信者生成の UUID v4。脆弱な `(task_id, status, timestamp)` 重複排除を置き換え。プロトコル全体の一貫性のため `revocation-notification.notification_id` を `idempotency_key` にリネーム。 * **すべての spend-commit での `check_governance`** — ガバナンス呼び出しは、プラン承認時だけでなくコミット時に必須。部分的な支出がガバナンスをスキップできる抜け穴を閉じる。 * **実験的ステータスメカニズム** — 本番使用中だがまだ完全な安定性保証下にないフィールドとタスクのための `status: experimental` マーカー。シグナルの `custom` 価格モデルエスケープハッチ。 * **`create_media_buy` の `submitted` ブランチ** — セラーは処理のためにペイロードを受け入れたがまだオーダーを確認していない。`pending_creatives` と `pending_start` とは別。 * **時間セマンティクス + `activate_signal` 冪等性** — プロトコルをまたいで時間フィールドセマンティクスを統一。`activate_signal` を必須冪等性テーブルに追加。 * **既知の制限 + プライバシー考慮事項のリファレンスページ** — 新しい `/docs/reference/known-limitations` と `/docs/reference/privacy-considerations`。プラットフォーム非依存リントがベンダー固有の言語が仕様に忍び込むのを防ぐ。 * **署名付き JWS `governance_context`** — ガバナンス決定は今や暗号学的に検証可能。セラーは `sync_governance` 経由でガバナンスエージェントの JWKS を解決し、決定を尊重する前に `sub` / `aud` / `phase` / `exp` を検証する。 * **ユニバーサルセキュリティストーリーボード** — すべてのエージェントが `/compliance/{version}/universal/security.yaml` を実行(未認証拒否、API キー、OAuth/RFC 9728、オーディエンスバインド)。署名を宣言するエージェントは `signed_requests` ハーネスも実行。 * **クロスインスタンス状態永続性** — アーキテクチャ仕様が、水平スケールされたインスタンスをまたいだ永続状態(タスク、メディアバイ、プラン、署名付きアーティファクト、冪等性キー)を要求。 * **セキュリティ実装ガイド** — 新しい `docs/building/by-layer/L1/security.mdx` が脅威モデル、3 プリンシパルモデル(brand / operator / agent)、検証パスを文書化。曖昧な「principal」用語を廃止。 * **スキーマ不変条件としての GDPR 第 22 条 / EU AI Act Annex III** — 新しいレジストリポリシー `eu_ai_act_annex_iii`。ポリシーとカテゴリーの `requires_human_review`。規制されたバーティカルの `human_review_required: true` のスキーマレベル強制。 * **エージェントの運用ガイド** — エンジニアリングチームを持たないパブリッシャーのための新しいドキュメント — 3 つのパス: パートナー、セルフホスト、ビルド。 * **リリースケイデンスポリシー** — 名前付きケイデンス: パッチ月次、マイナー四半期、必要ならメジャー年次。v2 EOL 2026 年 8 月 1 日。 * **CHARTER.md** — 正式なガバナンス憲章を公開。 * **コレクションリスト** — クロスパブリッシャーマッチングに配信識別子(IMDb、Gracenote、EIDR)を使うプログラムレベルのブランドセーフティ。新しいターゲティングオーバーレイフィールド(`collection_list`、`collection_list_exclude`)。新しいジャンルタクソノミー enum。 * **放送 TV サポート** — Ad-ID 識別子、放送スポットフォーマット(:15、:30、:60)、Agency Estimate Number、測定ウィンドウ(Live、C3、C7)、配信データの完全性(`is_final`、`measurement_window`)。 * **オフラインレポート配信** — ケイパビリティの `reporting_delivery_methods`、アカウントの `reporting_bucket`、プロダクト `reporting_capabilities` の `supports_offline_delivery`。Avro と ORC をファイルフォーマットオプションとして追加。 * **TMPX エクスポージャートラッキング** — Trusted Match Protocol 実行層のための国分割アイデンティティとマクロ接続。 * **TMP プロバイダー登録** — `provider-registration.json` スキーマ、`GET /health` エンドポイント、デュアルディスカバリーモデル(静的設定と動的 API)、プロバイダーごとのレイテンシー予算セマンティクス。 * **TMP マルチアイデンティティ Identity Match** — `identity-match-request` が単一の `user_token` + `uid_type` を `identities` 配列(minItems 1、maxItems 3)に置き換え。ルーターはプロバイダーごとにフィルターし RFC 8785 JCS 正準化で再署名。キャッシュキーは `consent_hash` を追加。`uid-type` enum に `rampid_derived` を追加。以前のプレリリース TMP ドラフトに対してのみ破壊的。TMP は 3.0 でプレリリースのままで 3.1.0 で安定化。 * **GOVERNANCE\_DENIED エラー** — ガバナンス拒否された操作のための新しい correctable エラーコード。 * **context/ext フィールド** — ガバナンス、コレクション、プロパティ、SI、コンテンツ標準プロトコルにわたるすべてのリクエスト/レスポンススキーマで任意の `context` と `ext`。 * **コンプライアンステストケイパビリティ** — エージェントは、サポートする `comply_test_controller` シナリオを宣言する `compliance_testing: { scenarios: [...] }` ブロックを `get_adcp_capabilities` に含める。ブロックの存在がシグナル — コンプライアンステストは `supported_protocols` の値では **ない**。ストーリーボードランナーは、決定的テストステップが検証できるかを判断するためにこのブロックを使う。 * **専門分野 + コンプライアンスカタログ** — ストーリーボードは `/compliance/{version}/`(universal + protocols + specialisms + test-kits)でプロトコルに出荷。6 プロトコルにわたる 19 値の新しい `specialisms` フィールドを `get_adcp_capabilities` に追加。`/protocol/{version}.tgz` のバージョンごとのプロトコル tarball。[Compliance Catalog](/docs/building/compliance-catalog) を参照。 * **構造化された測定条件** — 課金ベンダー、IVT しきい値、ビューアビリティフロアの交渉のためのプロダクトとメディアバイの `measurement_terms`。保証プロダクトの `cancellation_policy`。`viewability-standard` enum。`TERMS_REJECTED` エラーコード。 * **統一されたベンダー価格** — `list_creatives`、`build_creative`、`get_creative_features`、`property-list` の `pricing_options[]`。共有の `vendor-pricing-option.json` スキーマ。 * **リクエストごとのバージョン宣言** — すべての v3 リクエストスキーマの `adcp_major_version`。`VERSION_UNSUPPORTED` エラーコード。v2 クライアントをサポートするマルチバージョンセラーは、このフィールドではなく構造的な手がかりで v2 ペイロードを検出しなければならない(v2 スキーマにはそれがない)。 * **放送予測スキーマ** — `DeliveryForecast` の `measurement_source`、`packages`、`guaranteed_impressions`。新しい `forecast-range-unit` と `forecastable-metric` enum。 * **放送局識別子** — `station_id` と `facility_id` 識別子タイプ。`linear_tv` プロパティタイプ。 * **ブランドスキーマ拡張** — `brand.json` の汎用 `agents` 配列。ビジュアルトークン(`border_radius`、`elevation`、`spacing`、拡張カラーロール)。構造化フォント定義。 * **アイテムごとのエラースキーマ** — `sync_creatives`、`sync_catalogs`、`sync_event_sources` のレスポンスエラーは今や `error.json` ref を使う。 * **プロパティ関係フィールド** — `adagents.json` 委譲タイプとの双方向検証のための brand.json プロパティ定義の `relationship`(`owned`、`direct`、`delegated`、`ad_network`)。 * **`sales` エージェントタイプの復活** — `sales` が `brand-agent-type` enum に復活。セールスエージェント(SSP、パブリッシャー)は購買エージェント(DSP、バイヤープラットフォーム)とは別。 * **必須タスクリファレンス** — エージェントロールごとにすべての AdCP プロトコルにわたる必須、条件付き、任意タスクを統合する新しいリファレンスページ。 * **ストーリーボード検証の修正** — 11 のストーリーボードファイルにわたる 20 以上の検証バグを修正: フィールドパスを訂正(`creatives[0].action`、`media_buy_deliveries`、`renders[0].preview_url`)、`field_value` チェックに欠けていた `value:` を追加、ストーリーボード検証スキーマに `value` プロパティを追加。 *** ## rc.1 → rc.2 ### rc.1 採用者にとって潜在的に破壊的 | Area | rc.1 | rc.2 | What to do | | --------------------- | ------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------- | | アカウント認証モデル | `account_resolution` ケイパビリティ | 削除 — `require_operator_auth` が今やアカウントモデルを決定 | ケイパビリティパースと auth/account 分岐ロジックを更新 | | クリエイティブライブラリタスク境界 | `list_creatives` / `sync_creatives` は Media Buy の下に文書化 | クリエイティブライブラリ操作は Creative Protocol に存在 | セールスエージェントが両プロトコルを実装する場合でも、ライブラリの読み書きを Creative Protocol の前提を通じてルーティング | | サンドボックスケイパビリティディスカバリー | `media_buy.features.sandbox` | `account.sandbox` | account ケイパビリティブロックからサンドボックスサポートを読む | | DOOH フラットレートパラメーター | 判別子なしの `flat_rate.parameters` | パラメーターが存在するとき `flat_rate.parameters.type: "dooh"` が必須 | リクエストビルダーと検証器に判別子を追加 | | 非推奨ガバナンスタスクドキュメント | `delete_content_standards`、`get_property_features` を文書化 | 削除 | 代わりに `update_content_standards`、プロパティリスト、`get_adcp_capabilities` を使う | ### Additive changes in rc.2 * **クリエイティブ生成とプレビュー** — `build_creative` が `include_preview`、`preview_inputs`、`preview_quality`、`preview_output_format`、`quality`、`item_limit`、マルチフォーマット `target_format_ids` を追加。バイヤーは今やインラインでプレビューし、ドラフト対本番生成を選び、1 回の呼び出しで複数の出力フォーマットをリクエストできる。 * **クリエイティブライブラリ取得** — `build_creative` は `creative_id`、任意の `concept_id`、`media_buy_id`、`package_id`、`macro_values` を使ったライブラリ取得もサポートし、アドサーバーとクリエイティブプラットフォームが保存されたクリエイティブを配信可能なマニフェストに解決できる。 * **クリエイティブケイパビリティディスカバリー** — クリエイティブエージェントは `supports_generation`、`supports_transformation`、`has_creative_library` を宣言できる。`list_creatives` は今や `include_snapshot`、`has_served`、`items` のようなライブラリ指向のフィールドを使う。 * **プロダクトディスカバリーとプランニング** — `get_products` が `exclusivity`、`preferred_delivery_types`、`time_budget` を追加、レスポンスに `incomplete`。プロダクトは `delivery_measurement` を省略でき、パッケージは今やパッケージごとの `start_time` / `end_time` を運べる。 * **コンプライアンスとガバナンス** — クリエイティブ開示が永続性セマンティクスを追加、キャンペーンガバナンスが `sync_plans`、`check_governance`、`report_plan_outcome`、`get_plan_audit_logs` を導入。 * **アカウントとサンドボックスの人間工学** — `sync_accounts` が `payment_terms` を追加、サンドボックスが今やバイヤー宣言アカウント参照の自然アカウントキーに参加。 *** ## ヘルプが必要ですか? * **コミュニティ**: [Slack](https://join.slack.com/t/agenticads/shared_invite/zt-3c5sxvdjk-x0rVmLB3OFHVUp~WutVWZg) — 他の実装者への手早い質問に最適 * **Issues**: [GitHub Issues](https://github.com/adcontextprotocol/adcp/issues) — バグ、仕様の質問、移行のエッジケース * **完全な v2 → v3 移行**: [移行ガイド](/docs/reference/migration) # 価格の移行 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/pricing AdCP 価格を v2 から v3 に移行します。フィールドのリネーム、ハード制約とソフトヒントの分離、更新された価格オプションスキーマをカバーします。 # 価格の移行 AdCP 3.0 は明確化のために価格フィールドの名前を変更し、ハード制約(パブリッシャーが適用する価格)とソフトヒント(バイヤーが入札を調整するための過去データ)を分離します。 ## 変更内容 | v2 フィールド | v3 フィールド | 変更タイプ | | ---------------------- | ------------- | --------- | | `fixed_rate` | `fixed_price` | 名前変更 | | `price_guidance.floor` | `floor_price` | トップレベルに移動 | `pricing_model`、`currency`、`pricing_option_id`、`price_guidance` パーセンタイル(`p25`、`p50`、`p75`、`p90`)は変更なし。 ## ハード制約とソフトヒント v3 は明示的なセマンティックの区別を設ける: **ハード制約** — 違反した場合に入札が拒否されるパブリッシャー適用の価格: * `fixed_price` — ユニットあたりの正確な価格(固定価格ディール) * `floor_price` — 許容される最低入札額(オークション価格) これらは相互排他的です。価格オプションは `fixed_price`(保証レート)または `floor_price`(最低価格付きオークション)のいずれかを持ち、両方は持たない。 **ソフトヒント** — バイヤーが入札を調整するための過去のパーセンタイル: * `price_guidance.p25` — 最近の落札入札の25パーセンタイル * `price_guidance.p50` — 最近の落札入札の中央値 * `price_guidance.p75` — 最近の落札入札の75パーセンタイル * `price_guidance.p90` — 最近の落札入札の90パーセンタイル ## ディールタイプのマッピング これらのフィールドは標準的なプログラマティックディールタイプにマッピングされます: | ディールタイプ | AdCP フィールド | 説明 | | --------------------- | ------------- | ------------------------- | | プログラマティックギャランティード(PG) | `fixed_price` | 固定 CPM、保証配信 | | プリファードディール | `fixed_price` | 固定 CPM、非保証(バイヤーがファーストルック) | | プライベートマーケットプレイス(PMP) | `floor_price` | 最低入札付きオークション | | オープンオークション | どちらでもない | フロアや固定価格なし — オープン入札 | PG とプリファードディールはどちらも `fixed_price` を使用します。両者の区別は価格ではなく配信コミットメントにある — PG は配信ボリュームを保証するが、プリファードディールはボリューム保証なしでファーストルックアクセスを提供します。 ## 固定価格ディール **v2:** ```json theme={null} { "pricing_option_id": "cpm_usd_fixed", "pricing_model": "cpm", "currency": "USD", "fixed_rate": 25.00 } ``` **v3:** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/cpm-option.json", "pricing_option_id": "cpm_usd_fixed", "pricing_model": "cpm", "currency": "USD", "fixed_price": 25.00 } ``` `fixed_rate` を `fixed_price` に名前変更します。構造的な変更はない。 ## オークション価格 **v2:** ```json theme={null} { "pricing_option_id": "cpm_usd_auction", "pricing_model": "cpm", "currency": "USD", "price_guidance": { "floor": 10.00, "p50": 15.00, "p75": 18.00 } } ``` **v3:** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/cpm-option.json", "pricing_option_id": "cpm_usd_auction", "pricing_model": "cpm", "currency": "USD", "floor_price": 10.00, "price_guidance": { "p50": 15.00, "p75": 18.00 } } ``` 2つの変更: 1. `price_guidance.floor` がトップレベルの `floor_price` に移動 2. `price_guidance` はパーセンタイルヒントのみを保持 ## 価格ガイダンスオブジェクト v3 の `price_guidance` オブジェクトには統計パーセンタイルのみが含まれます: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/price-guidance.json", "p25": 8.50, "p50": 15.00, "p75": 18.00, "p90": 22.00 } ``` すべてのフィールドはオプションです。パブリッシャーは提供できるパーセンタイルを含めます。 ## フラットレート価格 **v2**(識別子なしのパラメーター付き DOOH): ```json theme={null} { "pricing_option_id": "dooh_times_square", "pricing_model": "flat_rate", "currency": "USD", "fixed_rate": 50000.00, "parameters": { "duration_hours": 24, "sov_percentage": 100, "estimated_impressions": 1500000 } } ``` **v3:** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/flat-rate-option.json", "pricing_option_id": "dooh_times_square", "pricing_model": "flat_rate", "currency": "USD", "fixed_price": 50000.00, "parameters": { "type": "dooh", "duration_hours": 24, "sov_percentage": 100, "estimated_impressions": 1500000 } } ``` 2つの変更: 1. `fixed_rate` が `fixed_price` に名前変更(他の価格モデルと同じ) 2. `parameters` に DOOH インベントリの必須 `"type": "dooh"` 識別子が追加 v2 で `parameters` がなかったスポンサーシップの flat\_rate オプションは v3 でも引き続き省略します。`fixed_price` はフラットレートオプションでオプションであることに注意 — 存在しない場合、フラットレートはオークションベースだ(まれだが一部の DOOH インベントリで有効)。 ## 最低支出 v3 はすべての価格オプションにオプションの `min_spend_per_package` フィールドを追加します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/cpm-option.json", "pricing_option_id": "cpm_usd_premium", "pricing_model": "cpm", "currency": "USD", "floor_price": 15.00, "min_spend_per_package": 5000.00, "price_guidance": { "p50": 22.00, "p75": 28.00 } } ``` これにより、パブリッシャーはパッケージごとの最低支出要件を宣言できます。 ## 移行期間中の処理 移行中に、リーダーは古いフィールド名と新しいフィールド名の両方に遭遇する場合があります。両方の確認を検討する: ```javascript test=false theme={null} const price = option.fixed_price ?? option.fixed_rate; const floor = option.floor_price ?? option.price_guidance?.floor; ``` v3 ライターは新しいフィールド名のみを出力する必要があります。古いフィールド名(`fixed_rate`、`price_guidance.floor`)は v3 スキーマバリデーションで認識されない。上流のすべてのセラーが v3 スキーマに移行したら v2 フォールバックを削除します。 ## 移行ステップ すべての価格オプションで `fixed_rate` を `fixed_price` に名前変更します。 `floor` を `price_guidance` の内部からトップレベルの `floor_price` に移動します。 `price_guidance` オブジェクトから `floor` を削除する(パーセンタイルのみが残る)。 新しいフィールド名を探すようにコードを更新します。移行中は両方の一時的なフォールバックを検討します。 フロア適用が新しいフィールド位置で機能することを確認します。 各価格モデルは `/schemas/v3/pricing-options/` に独自のスキーマを持ちます。 プロダクトが価格オプション、チャンネル、ケイパビリティをどう宣言するか。 *** **関連:** [チャンネル](/docs/reference/migration/channels) | [ジオターゲティング](/docs/reference/migration/geo-targeting) | [クリエイティブ](/docs/reference/migration/creatives) | [カタログ](/docs/reference/migration/catalogs) | [アトリビューション](/docs/reference/migration/attribution) | [AdCP 3.0 概要](/docs/reference/whats-new-in-v3) # シグナルの移行 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/signals AdCP シグナルを beta.3 から rc.1 に移行します。deliver_to のフラット化、構造化された価格オプション、簡素化された使用状況レポートフィールドをカバーします。 # シグナルの移行 AdCP 3.0 rc.1 はシグナルプロトコルに3つの変更を加える: デリバリーターゲットのフラット化、構造化された価格オプション、使用状況レポートの簡素化。 ## deliver-to のフラット化 `get_signals` リクエストのネストされた `deliver_to` オブジェクトが2つのトップレベルフィールドに置き換えられます。 | beta.3 | rc.1 | 注記 | | ------------------------- | -------------- | --------- | | `deliver_to.destinations` | `destinations` | トップレベルに移動 | | `deliver_to.countries` | `countries` | トップレベルに移動 | **beta.3:** ```json test=false theme={null} { "signal_spec": "in-market auto intenders", "deliver_to": { "destinations": [ { "agent_url": "https://dsp.example.com", "seat_id": "seat_123" } ], "countries": ["US", "CA"] } } ``` **rc.1:** ```json test=false theme={null} { "signal_spec": "in-market auto intenders", "destinations": [ { "agent_url": "https://dsp.example.com", "seat_id": "seat_123" } ], "countries": ["US", "CA"] } ``` *** ## 価格オプション レガシーの `pricing` オブジェクト(単一の `cpm` フィールドを持つ)が `pricing_options` 配列に置き換えられます。各オプションは `model` による識別子付きユニオンです。 | beta.3 | rc.1 | 注記 | | ------------------------ | ---------------------------------------- | ---------------- | | `pricing: { cpm: 2.50 }` | `pricing_options[]` | 価格モデルオブジェクトの配列 | | 暗黙的な価格選択 | `activate_signal` での `pricing_option_id` | バイヤーの明示的なコミットメント | | 冪等性なし | `report_usage` での `idempotency_key` | 重複請求を防ぐ | ### 3つの価格モデル **CPM** — 1000インプレッションあたりの固定コスト: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/latest/core/signal-pricing-option.json", "pricing_option_id": "po_auto_cpm", "model": "cpm", "cpm": 2.50, "currency": "USD" } ``` **メディア費用の割合** — メディア支出のパーセンテージ、オプションの CPM 上限付き: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/latest/core/signal-pricing-option.json", "pricing_option_id": "po_auto_pom", "model": "percent_of_media", "percent": 15, "max_cpm": 5.00, "currency": "USD" } ``` **固定料金** — レポート期間ごとの固定料金(月次ライセンスセグメント): ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/latest/core/signal-pricing-option.json", "pricing_option_id": "po_auto_flat", "model": "flat_fee", "amount": 10000.00, "period": "monthly", "currency": "USD" } ``` ### 価格を伴うアクティベーション シグナルをアクティベートするとき、選択した `pricing_option_id` を渡す: ```json test=false theme={null} { "signal_agent_segment_id": "luxury_auto_intenders", "destinations": [ { "type": "agent", "agent_url": "https://dsp.example.com", "account": { "account_id": "acct_pinnacle" } } ], "pricing_option_id": "po_auto_cpm" } ``` *** ## 使用状況レポート `report_usage` は `idempotency_key` を追加し、`kind` と `operator_id` フィールドを削除します。 | beta.3 | rc.1 | 注記 | | ------------------- | ----------------- | ------------------------------------------------------------ | | `kind` フィールド | 削除 | 使用状況レコードは `signal_agent_segment_id` または `standards_id` で自己記述 | | `operator_id` フィールド | 削除 | アカウント参照がオペレーターアイデンティティを提供 | | 冪等性なし | `idempotency_key` | クライアント生成の UUID がリトライ時の重複請求を防ぐ | **rc.1 使用状況レポート:** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/latest/account/report-usage-request.json", "idempotency_key": "550e8400-e29b-41d4-a716-446655440000", "reporting_period": { "start": "2025-03-01T00:00:00Z", "end": "2025-03-31T23:59:59Z" }, "usage": [ { "account": { "account_id": "acct_pinnacle_signals" }, "signal_agent_segment_id": "luxury_auto_intenders", "pricing_option_id": "po_auto_cpm", "impressions": 4200000, "media_spend": 21000.00, "vendor_cost": 2100.00, "currency": "USD" } ] } ``` 使用状況レコードの `pricing_option_id` はアクティベーション時に渡したものと一致する必要があり、ベンダーが正しいレートが適用されたことを確認できます。 ## 移行ステップ `get_signals` リクエストで `deliver_to.destinations` と `deliver_to.countries` をトップレベルフィールドに移動します。 `pricing`(オブジェクト)の代わりに `pricing_options`(配列)を読み取るようにシグナルレスポンスの解析を更新します。`model` フィールドで価格タイプを判別します。 `activate_signal` を呼び出すとき、シグナルの `pricing_options` 配列から選択した `pricing_option_id` を渡します。 各 `report_usage` 呼び出しに一意のキー(UUID)を生成します。同じキーを使ったリトライは冪等です。 使用状況レコードから `kind` と `operator_id` を削除します。使用状況タイプは `signal_agent_segment_id`(シグナル)または `standards_id`(ガバナンス)の存在で判別されます。 アクティベーション時に `pricing_option_id` を保存し、ベンダーが請求を確認できるように `report_usage` レコードで渡します。 `get-signals-request.json`、`activate-signal-request.json`、`report-usage-request.json` スキーマに対してリクエストを実行します。 シグナル探索、アクティベーション、使用状況レポート、価格モデルの完全リファレンス。 *** **関連:** [価格](/docs/reference/migration/pricing) | [最適化目標](/docs/reference/migration/optimization-goals) | [AdCP 3.0 概要](/docs/reference/whats-new-in-v3) # v3 レディネスチェックリスト Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/migration/v3-readiness セラーエージェントが AdCP v3 ストーリーボードテストに合格するための 8 つの最小要件。 # v3 レディネスチェックリスト AdCP ストーリーボードテストは v3 プロトコルサポートを必要とします。v2 のみをサポートするエージェントは失敗します。このページは、v3 バイヤーとの統合テストのブロックを解除する最小限の変更をカバーします — 完全な移行ではありません。完全なリストについては [移行ガイド](/docs/reference/migration) を参照。 ストーリーボードテストは、v3 サポートを宣言しない任意のエージェントをハードに失敗させます。まずこれら 8 項目を完了し、次に [完全な移行チェックリスト](/docs/reference/migration) を進めてください。v2 は 2026 年 8 月 1 日(UTC)に完全に非推奨になります — [v2 sunset ページ](/docs/reference/v2-sunset) を参照。 *** ## 1. `get_adcp_capabilities` を実装する v3 バイヤーは、エージェントが何をサポートするかを発見するためにこのタスクを最初に呼びます。それなしでは、バイヤーはプロトコルバージョン、サポートチャネル、価格モデル、機能を判断できません。 これは最も重要な単一の変更です — バイヤー(とストーリーボードテスト)が v3 エージェントを v2 から区別する方法です。 最低限次を返します: `major_versions: [3]`、`supported_protocols`、`features` オブジェクト。 タスク仕様とレスポンススキーマ。 *** ## 2. チャネルタクソノミーを更新する v3 は v2 の 9 チャネルを 20 のプランニング指向チャネルに置き換えます。バイヤーは v3 チャネル値を送ります — エージェントはそれらを認識しなければなりません。 | Common v2 value | v3 replacement | | --------------- | --------------------------------- | | `video` | `olv`、`linear_tv`、または `cinema` | | `audio` | `radio` または `streaming_audio` | | `native` | 削除 — ネイティブインベントリは今や `display` の一部 | | `retail` | `retail_media` | `display`、`social`、`ctv`、`podcast`、`dooh` は変更なし。 完全なマッピング表と例。 *** ## 3. 価格フィールドをリネームする 2 つのフィールドリネーム — 同じセマンティクス、異なる名前: | v2 field | v3 field | | ---------------------- | --------------------- | | `fixed_rate` | `fixed_price` | | `price_guidance.floor` | `floor_price`(トップレベル) | バイヤーは v3 スキーマに対して検証します。古いフィールド名はスキーマ検証失敗を引き起こします。 before/after の例と price guidance の再構築。 *** ## 4. `creative_assignments` をサポートする `creative_ids`(文字列配列)は、配信重み付けとプレースメントターゲティングを持つ `creative_assignments`(オブジェクト配列)に置き換えられます。 ```json theme={null} // v2 { "creative_ids": ["cr_001", "cr_002"] } // v3 { "creative_assignments": [ { "creative_id": "cr_001", "weight": 70 }, { "creative_id": "cr_002", "weight": 30 } ] } ``` 重み付き割り当て、プレースメントターゲティング、アセットディスカバリー。 *** ## 5. `brand_manifest` の代わりに `brand` ref を受け入れる バイヤーは、インラインマニフェストの代わりに参照(`{ domain, brand_id }`)としてブランドアイデンティティを渡します。エージェントは実行時に `brand.json` またはレジストリからブランドデータを解決します。 ```json theme={null} // v2 { "brand_manifest": { "name": "Acme", "logo": "..." } } // v3 { "brand": { "domain": "acme.example.com", "brand_id": "acme_main" } } ``` BrandRef スキーマ、解決フロー、移行ステップ。 *** ## 6. `get_products` の `buying_mode` を扱う `buying_mode` は今やすべての `get_products` リクエストで必須です。`brief` がキュレーションされたプロダクトディスカバリーのベースラインモードです。エージェントが `media_buy.buying_modes` で `wholesale` または `refine` を宣言する場合、それらのモードセマンティクスも扱わなければなりません。 buying\_mode を含む完全なリクエストスキーマ。 *** ## 7. `buyer_ref` を削除 — `idempotency_key` を使う v3 はすべてのリクエストとレスポンスから `buyer_ref`、`buyer_campaign_ref`、`campaign_ref` を削除します。セラー割り当ての `media_buy_id` と `package_id` が今や唯一の正準識別子です。 エージェントが重複排除に `buyer_ref` に依存していた場合、代わりに新しい `idempotency_key` フィールドを使ってください。`idempotency_key`(UUID v4)はすべての変更リクエストで **必須** です — エージェントはそれを省略するリクエストを `INVALID_REQUEST` で拒否しなければならず(MUST)、キーが異なるペイロードで再利用されたとき `IDEMPOTENCY_CONFLICT` を返さなければなりません(MUST)。規範的セマンティクスについては [冪等性実装ガイド](/docs/building/by-layer/L1/security#冪等性) を参照。 エージェントが内部トラッキングや相関(例: キャンペーン ID、セッショントレース、UI 状態へのマッピング)に `buyer_ref` を使った場合、代わりに `context` フィールドを使ってください。`context` は、すべてのレスポンスと webhook で変更なくエコーされる不透明なオブジェクトです — エージェントはそれを決してパースしたり、それに基づいて行動したりしてはなりません。`create_media_buy` のパッケージ相関については、非推奨のトップレベル `buyer_ref` ではなく `packages[i].context`(例えば `context.buyer_ref`)にパッケージごとのトラッキングを置いてください。3.1+ セラーは明示的なパッケージレスポンスに `product_id` をエコーしなければならず(MUST)、パッケージコンテキストはそうしないレガシーセラーのフォールバックのままです。 | v2 field | v3 replacement | | --------------------------- | ------------------------------- | | `buyer_ref` | 削除 — `media_buy_id`(セラー割り当て)を使う | | `buyer_campaign_ref` | 削除 | | `campaign_ref` | 削除 | | 暗黙の重複排除としての `buyer_ref` | 変更リクエストの明示的な `idempotency_key` | | 相関 / トラッキングとしての `buyer_ref` | `context`(不透明、変更なくエコー) | ```json theme={null} // v2 — バイヤーが自身の ref を提供 { "buyer_ref": "camp-2024-q3", "start_time": "..." } // v3 — セラー割り当て ID、明示的な冪等性、トラッキング用の context { "idempotency_key": "550e8400-e29b-41d4-a716-446655440000", "context": { "campaign": "camp-2024-q3", "trace_id": "abc-123" }, "start_time": "..." } ``` パッケージレベルのラインアイテム相関については、バイレベルのコンテキストを各パッケージのフォールバック相関ハンドルと分けて保ちます: ```json test=false theme={null} { "idempotency_key": "550e8400-e29b-41d4-a716-446655440001", "context": { "internal_campaign_id": "camp-2024-q3" }, "brand": { "domain": "example-brand.test" }, "packages": [ { "product_id": "prod_weekday_display", "pricing_option_id": "cpm_usd_auction", "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_image" } ], "budget": 12000, "context": { "buyer_ref": "line-001" } }, { "product_id": "prod_weekend_video", "pricing_option_id": "cpm_usd_auction", "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_standard_30s" } ], "budget": 18000, "context": { "buyer_ref": "line-002" } } ], "start_time": "2026-07-01T00:00:00Z", "end_time": "2026-07-31T23:59:59Z" } ``` *** ## 8. `sync_accounts` を実装する v3 バイヤーは、バイを置く前に課金関係を確立します。エージェントは `sync_accounts` 呼び出しを受け入れ、バイヤーが後続のリクエストに含めるアカウント参照を返さなければなりません。 アカウントプロビジョニング、ライフサイクル、sync\_accounts タスク。 *** ## これら 8 項目の後 これらが整ったら、エージェントに対してストーリーボードテストを実行してください。既存のトラック(products、media buy、creative)は v3 スキーマを詳細に検証し、残るフィールドレベルの問題をサーフェスします。 完全な移行 — ジオターゲティング、最適化目標、シグナル、オーディエンス、アトリビューションを含む — については [完全な移行ガイド](/docs/reference/migration) を参照。 # AdCP 3.1 の新機能 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/whats-new-in-3-1 AdCP 3.1 の採用者向け概要 — 分散型 brand.json、依存関係影響 webhook、ホールセールフィードミラーリング、リリース精度バージョンネゴシエーション、ブランドレスポンス署名、正準クリエイティブフォーマット、ベンダー証明測定、アクションディスカバリーなど。3.0 に対して加算的。安定リリースにはワイヤーピン 3.1 を使う。 **ステータス: 3.1 はリリース済み。** 現在の安定マイナー: 3.1、ワイヤーピン `adcp_version: "3.1"`。3.0 ラインは `"3.0"` にピン留めされた既存の統合のためにサポートされたままです。 AdCP 3.1 はマイナーリリースです。すべての 3.1 変更は 3.0 に対して **加算的** です: 新しいフィールドは任意で、必須フィールドは削除されず、3.0 準拠クライアントを壊す方法で形状が変わったものはありません。**3.0 準拠エージェントに破壊的変更なし。** 3.1 を採用するには、エージェントが `supported_versions` でその値をアドバタイズすることを確認した後、本番トラフィックを `"3.1"` にピン留めしてください。実装準備には [3.0 から 3.1 への移行ガイド](/docs/reference/migration/3-0-to-3-1) を使ってください。 このページは 3.1 マイナーリリースのキュレーションされた採用者概要です。完全な 3.1 変更リスト、PR ごとの詳細、移行表が必要ですか? 権威あるバージョン記録の [リリースノート § Version 3.1.0](/docs/reference/release-notes#version-3-1-0) と、ロールベースのアップグレードチェックリストの [3.0 から 3.1 への移行](/docs/reference/migration/3-0-to-3-1) を使ってください。長文の規範的リファレンスについては、下の各見出しのリンクをたどってください。 **代わりにメジャーな v2 → v3 の変更を探していますか?** [What's New in AdCP 3](/docs/reference/whats-new-in-v3) を参照。このページは 3.0 → 3.1 のマイナー差分のみをカバーします。 ## 最終的な 3.1 機能セット 安定した 3.1 形状のみが必要なら、まずこのリストを読んでください: * **バージョニングと検証。** リリース精度の `adcp_version` ピン、安定ワイヤー値 `"3.1"`、`supported_versions` アドバタイズ、エンベロープエコー、バージョンスコープの検証バッジ。 * **ブランド信頼。** 分散型 `brand.json`、`brand_refs[]` を通じたサブブランドの自己公開、型付きブランド制約、認可されたオペレーターのスコープ、`verify_brand_claim` / `verify_brand_claims` の必須署名レスポンス。 * **シグナルとプロダクトターゲティング。** 強化されたシグナル定義、`SignalRef`、ホールセールシグナル列挙、プロダクトスコープの `included_signals`、選択可能な `signal_targeting_options`、グループ化されたバイ時の `signal_targeting_groups`、所有対マーケットプレイスの適合性の明確化。 * **ホールセールフィードミラーリング。** 条件付きフェッチトークン、public/account の `cache_scope`、プロダクトとシグナルのホールセールフィード webhook、ストアフロント・レジストリ・連合マーケットプレイスの repair-by-read セマンティクス。 * **クリエイティブフォーマット。** 正準 `format_kind` 宣言、パブリッシャーフォーマットカタログ、`v1_format_ref` デュアル発行、ホスト音声/動画の `duration_ms_exact` と片側 `duration_ms_range`、公開済み投稿参照、動画プレースメントセマンティクス。 * **クリエイティブ生成。** `list_transformers`、アカウントスコープのトランスフォーマー選択、厳格な型付き `config`、カタログとバリアントのファンアウト、`creative-feature-result[]` 上の助言的エバリュエーターランキング、支出制御、コンテンツマクロ、自由テキストパラメーター、出力ごとの価格領収書。 * **メディアバイ操作。** 依存関係障害、バイヤー可視の `webhook_activity[]`、アクションディスカバリー、プロポーザルライフサイクルのクリーンアップ、通貨スコープのプロダクトディスカバリー、sponsored/social プレースメントフィールド、SI 可用性ステータス。 * **測定と課金。** ベンダー証明の `vendor_metric` 目標、リーチウィンドウセマンティクス、`viewability.viewed_seconds`、ウィンドウ配信リカバリー、配信と使用量の確定フラグ、帯域外クリエイティブ課金宣言。 * **ランタイム堅牢化。** すべてのタスクのリクエスト冪等性、`IDEMPOTENCY_IN_FLIGHT`、リカバリー分類を伴うオープンエラーコードデコード、auth エラー分割、ペイロード内認証情報の拒否、webhook 操作 ID エコー、フラット MCP エンベロープ許容。 * **SDK とコンプライアンスの準備。** コードジェネレーターのための名前付きスキーマ、非同期レスポンス ref、意図的にオープンなペイロードマーカー、ケイパビリティゲートのストーリーボードカバレッジ、パッケージ化されたコンプライアンスバンドルのクロージャ、リリースアーティファクトのドリフトチェック。 ## なぜアップグレードするか 3.1 は本番堅牢化リリースです。3.0 はプロトコルサーフェス — ディスカバリー、バイライフサイクル、シグナル、クリエイティブライブラリ、ブランドアイデンティティ — を出荷しました。3.1 は、実際のエージェントが実際のパブリッシャーに対してバイを実行し始めたときにサーフェスした運用上のギャップを閉じます: * **今や webhook をデバッグできる。** バイヤーエージェントは、ゲートウェイがなぜ 5xx を返したかを推測する代わりに、`get_media_buys` の `webhook_activity[]` 経由で自身の最近の配信発火 — HTTP ステータス、発火時刻、idempotency\_key — を検査します。 * **バイがなぜ障害を受けているか見える。** クリエイティブが引き下げられ、オーディエンスが停止され、カタログアイテムが撤回され、イベントソースが静かになると、バイの `health` が `impaired` に切り替わり、`impairments[]` がすべてのオフライン依存関係をその package\_ids と修復ヒントとともにリストします。`get_media_buys` のスナップショットとして、また `notification-type: impairment` 経由のプッシュ発火として。 * **帯域を消費せずにホールセールプロダクトフィードとホールセールシグナルフィードをミラーできる。** 条件付きフェッチトークン(`if_wholesale_feed_version` — ETag スタイル)、シグナルのホールセール列挙(プロダクトと対称)、アカウントレベルのホールセールフィード webhook により、ストアフロント、連合マーケットプレイス、レジストリは、すべてのポーリングで変更されていないフィードペイロードを再取得せずに、接続されたすべてのエージェントの購入可能なプロダクトとシグナルの最新ローカルレプリカを保持できます。2 層キャッシュモデル(`cache_scope: "public" | "account"`)は、ほとんどのアカウントが単一の共有キャッシュに重複排除されることを意味します。 * **メディアプロダクトにセラー提供のシグナルを合成できる。** プロダクトは、バンドル/計画されたシグナルメタデータの `included_signals`、パッケージレベルのシグナル選択の `signal_targeting_allowed`、プロダクト固有のメニューと価格の任意のインライン `signal_targeting_options`、include/exclude/グループ化制限の `signal_targeting_rules` を宣言できます。バイヤーはグループ化された `targeting_overlay.signal_targeting_groups` を通じて選択を適用し、ホールセールプロダクトはインラインオプションを省略して `get_signals` を選択可能なシグナルフィードとして使えます。 * **目標をベンダー証明の測定にバインドできる。** 最適化目標は今や、セラーが好きに解釈できるベンダー非依存の文字列ではなく、実際の測定ベンダー(DV、IAS、Adelaide、TVision、Lumen、Kantar、Upwave、Scope3 など)からの `(vendor, metric_id)` ペアを参照できます。測定ベンダーカタログディスカバリーサーフェス自体は 3.1 で実験的です(`measurement.core`)。 * **ブランド検証レスポンスがアテステーション可能。** `verify_brand_claim` と `verify_brand_claims` は今や必須の `signed_response` ペイロードエンベロープ JWS を返すため、下流のパートナーはトランスポートセッションコンテキストに依存せずにブランドの回答を保持し検証できます。 * **リリースをピン留めしてドリフトと戦うのをやめられる。** すべてのリクエストでリリース精度の `adcp_version`(安定リリースには `"3.1"`)。セラーは完全な `supported_versions` セットをアドバタイズし、実際に提供したものをエコーします。SDK コンストラクターピンが今や本物です。 * **サブブランドが自己公開する。** ブランドは、コーポレートハウスがポートフォリオポインター経由で所有権を宣言する一方で、自身のドメインで独自の正準 `brand.json` を公開できます — IAB の `ads.txt` / `sellers.json` と同じ相互パターン。 * **クリエイティブフォーマットに正準語彙がある。** 12 の正準 `format_kind` 値 + パブリッシャーカタログディスカバリーサーフェス + 投影 ref メカニズム。クリエイティブエージェントは `creative.supported_formats[]` を通じてビルド可能な正準出力もアドバタイズします。ターゲット可能なエントリは `build_creative` ルーティングのため安定した `capability_id` 値を含むべきです(SHOULD)。 * **ホスト音声/動画 duration は 1 つの範囲語彙を使う。** `duration_ms_range` は今や有界と片側範囲をカバーします(「最大 60 秒」の `[null, 60000]`、「少なくとも 15 秒」の `[15000, null]`)。固定スロットは `duration_ms_exact` を使うべき。別個の min/max duration フィールドは追加されませんでした。 * **クリエイティブトランスフォーマーを発見・選択できる。** 新しい `list_transformers` タスクが、アカウントスコープのエージェント提供ビルドユニット — ボイス、モデル、スタイル — メディアバイプロダクトのクリエイティブ版をサーフェスし、同じツールで列挙可能なオプション値(例: 設定済みのボイス)を返す `expand_params` モードを持ちます。`build_creative` は `transformer_id` で 1 つを選択し、型付き `config` バッグで設定し(厳格な検証 — 未知のキーと範囲外の値はフィールド帰属エラーで拒否。ベンダーノブは `ext` へ)、カタログアイテム(`max_creatives`)と代替(`max_variants` + `variant_axis`、`keep_mode` は助言的)にわたってファンアウトします。新しい `BuildCreativeVariantSuccess` レスポンスメンバーが、バリアントごとのマニフェスト、推奨/ランク、リーフごとの価格領収書を運びます。価格はトランスフォーマー(`pricing_options` `per_unit`)に移動し、`report_usage` 経由で決済されます。 * **動画と音声のインベントリが実行セマンティクスを宣言できる。** プロダクトとプレースメントは OpenRTB 整合の `video_placement_types` と `audio_distribution_types` を宣言できるため、バイヤーはバイヤー向けチャネルを変えずに instream/accompanying/interstitial/standalone 動画と music streaming/FM-AM broadcast/podcast/catch-up/web-radio 音声を区別できます。 * **検証バッジがバージョンスコープ。** 公開 3.1 バッジ発行はアクティブな 3.0 バッジと並行して実行でき、リリースにピン留めされたバイヤーは一致するバッジバージョンを読みます。 * **アクションディスカバリーとプロポーザルが機械的。** プロダクトは `allowed_actions[]` をアドバタイズし、メディアバイは `available_actions[]` を運び、プロポーザルは `proposal_status` を使って `create_media_buy(proposal_id)` の前に finalize がまだ必要かを言います。 * **課金に確定がある。** 配信の行レベル `is_final` + `finalized_at`。`report_usage` の一致する `final` + `finalized_at` + `measurement_window`。バイヤーは数が動かなくなるときを知り、請求書を再照合できます。 加えて、エラーコードの明確化、auth 厳格化、冪等性ルール、TMP IdentityMatch アップグレード、`adagents.json` スケーリング作業の長い裾野 — 下の見出しリストを参照。 ## 一目で | Area | 3.0 | 3.1 | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`brand.json`** | 単一のハウスドキュメント下のインライン `brands[]` | 分散型: ブランドが自身のドメインで自己公開。ハウスは `brand_refs[]` 経由で所有権を宣言。相互アサーション信頼。型付き `trademarks[]` | | **ブランド検証** | brand.json ディスカバリーのみ | `verify_brand_claim` / `verify_brand_claims` — 必須 `signed_response` ペイロードエンベロープ JWS 証拠を伴う連合の権威ある検証 | | **依存関係影響** | 「バイが依存するリソースがオフラインになった」のプロトコルサーフェスなし | `media_buy.health` + `impairments[]` スナップショット。`notification-type: impairment` webhook。`propagation_surfaces` ケイパビリティ。`impairment.coherence` コンプライアンス不変条件 | | **Webhook 基盤** | 機能ごとに仕様化 | 1 つの永続チャネルコントラクト: snapshot/log 双対性、エンベロープで型付けされた `notification_id`、アカウントごと + リソースごとのサブスクリプションモデル | | **Webhook 可観測性** | バイヤー側の配信可視性なし | `get_media_buys` の `webhook_activity[]` — バイヤーが自身の逃した発火をセルフサービスでデバッグ | | **ホールセールフィードミラーリング** | 変更検出のためすべてのポーリングでホールセールを再取得 | `get_products` / `get_signals` の ETag スタイル `wholesale_feed_version` / `if_wholesale_feed_version` 条件付きフェッチ。2 層キャッシュ層のためすべてのレスポンスで `cache_scope`(public/account) | | **ホールセールシグナル** | `get_signals` は `signal_spec` または非推奨 `signal_ids` を要求 — 完全な価格付きシグナルフィードを列挙するプロトコル準拠の方法なし | `get_products buying_mode: "wholesale"` と対称の `discovery_mode: "wholesale"`。`signal_ref` アイデンティティと `pricing_options[]` 投入を伴うページ分割された完全ホールセールシグナルフィード列挙 | | **シグナルアイデンティティ** | `SignalId` / `signal_id.source`(`catalog` または `agent`)がプライマリシグナルアイデンティティ形状だった | `SignalRef` / `signal_ref.scope` が正準: プロバイダー公開の adagents.json シグナルには `data_provider`、ソースネイティブシグナルには `signal_source`、プロダクトローカルのメディアバイオプションには `product`。レガシー `signal_id` は移行ウィンドウ中受け入れられたまま | | **プロダクトシグナルメタデータ** | `data_provider_signals` がレガシーバンドルメタデータを混合、選択可能なパッケージレベルのシグナルサーフェスなし | `data_provider_signals` は非推奨。非選択のバンドル/計画シグナルには `included_signals`、価格・アクティベーションハンドル・デフォルト・グループ化ヒントを伴う選択可能なプロダクトスコープシグナルオプションには `signal_targeting_options` を使う | | **パッケージシグナルターゲティング** | セラー提供の名前付きシグナルのグループ化されたバイ時サーフェスなし。ストアフロントはオーディエンスフィールドやブリーフテキストを過負荷 | `targeting_overlay.signal_targeting_groups`: トップレベル `operator: "all"` と子 `any` include グループ、`none` 除外グループ。プロダクト `signal_targeting_rules` が選択モード、direct 対 seller-planned 解決、グループ制限を宣言 | | **ホールセールフィード webhook** | なし | `sync_accounts.accounts[].notification_configs[]` 経由で登録されるアカウントレベル webhook が `applies_to.scope` を伴う `product.*` / `signal.*` / `wholesale_feed.bulk_change` 変更ペイロードを運ぶ。標準の webhook 署名と SSRF ガードが適用 | | **クリエイティブフォーマット** | パブリッシャーごとのバリアントを伴う名前によるフォーマット | 12 の正準 `format_kind` 値 + パブリッシャーカタログディスカバリー(`adagents.json formats[]`)+ デュアル発行の `v1_format_ref` + サイズ柔軟性(固定 / マルチサイズ / レスポンシブ)。クリエイティブエージェントは `creative.supported_formats[]` でビルド可能な出力をアドバタイズし、ターゲット可能なエントリに `capability_id` を含むべき | | **ホスト音声/動画 duration 制約** | 固定 duration または閉じた範囲のみ | 固定スロットの `duration_ms_exact`。`[null, 60000]` や `[15000, null]` のような有界と片側範囲の `duration_ms_range`。`[null, null]` は無効 | | **クリエイティブトランスフォーマー** | `build_creative` はターゲットフォーマットにビルド。レンダーノブは暗黙。フォーマット添付の `Format.input_format_ids` / `output_format_ids` / `pricing_options` | `list_transformers` が `expand_params` オプション列挙モードでアカウントスコープのビルドユニット(ボイス/モデル/スタイル)を発見。`build_creative` が `transformer_id` で 1 つを選択、厳格検証の型付き `config` を取り、`max_creatives`(カタログアイテムごと)+ `max_variants`/`variant_axis` + 助言的 `keep_mode` でファンアウト。新しい `BuildCreativeVariantSuccess` メンバーがバリアントごとのマニフェスト、推奨/`rank`、リーフごとの価格領収書を返す。レートは `transformer.pricing_options`(`per_unit`)、`report_usage` 経由で決済。出荷済みの `BuildCreativeSuccess` / `BuildCreativeMultiSuccess` は変更なし | | **バージョンネゴシエーション** | リクエストごとの整数 `adcp_major_version` | リリース精度 `adcp_version`(例: 安定リリースの `"3.1"`)+ `adcp.supported_versions` アドバタイズ + エンベロープエコー。整数フィールドは後方互換のレガシーとして残る | | **最適化目標** | `event` + `metric` kind、ベンダー非依存 | 新しい `vendor_metric` kind — 目標をベンダー証明のメトリクスにバインド。プロダクトごとの `vendor_metric_optimization` ケイパビリティ。3 前提条件の拒否ルール | | **ケイパビリティ宣言** | プロトコルごとの基本 | 新規: ホールセールプロダクトの `media_buy.buying_modes`、ホールセールシグナルの `signals.discovery_modes`、`wholesale_feed_versioning`、`wholesale_feed_webhooks`、`supported_optimization_metrics`、`supported_target_kinds`、`media_buy.frequency_capping`、`media_buy.propagation_surfaces`、`creative.bills_through_adcp`、`capabilities.idempotency.in_flight_max_seconds` | | **動画と音声の実行ディスカバリー** | 動画と音声のプロダクトは自由テキスト説明、プレースメント名、過負荷のチャネルに依存 | OpenRTB `video.plcmt` と `audio.feed` 値に AdCP ネイティブ名を使う、プロダクト・プレースメント・`get_products.filters` の `video_placement_types` と `audio_distribution_types` | | **通貨スコープディスカバリー** | バイヤーは予算通貨をフィルターできたが、メディアプロダクト価格で取引できる通貨はできなかった | `get_products.filters` の `pricing_currencies`。セラーはプロダクトレベルの `pricing_options` をマッチし、返されるプロダクト価格オプションを要求された通貨に刈り込み、その通貨で必須のプロダクトスコープシグナル料金が満たせないプロダクトを除外 | | **配信レポート** | ウィンドウセマンティクスなしの `reach`。ビューアビリティはレートのみ | `reach_window`(cumulative / period / rolling)。`viewability.viewed_seconds`。`time_granularity` + `include_window_breakdown` 経由のウィンドウ付きプルリカバリー | | **課金サーフェス** | `billing_measurement` 経由の権威。確定マーカーなし | 配信の行レベル `is_final` + `finalized_at`。`report_usage` の `final` + `finalized_at` + `measurement_window`。`creative.bills_through_adcp` ケイパビリティ + `BILLING_OUT_OF_BAND` エラー | | **アクションディスカバリー** | バイ/プロダクトの構造化アクション語彙なし | Product の `allowed_actions[]`(助言的テンプレート)。`get_media_buys` / `create_media_buy` / `update_media_buy` の `available_actions[]`。プロポーザル実行可能性はアクションモードハックではなく `proposal_status` から来る | | **Auth + セキュリティ** | 単一の `AUTH_REQUIRED` エラー。トランスポートチャネルルールなし | `AUTH_REQUIRED` を `AUTH_MISSING`(correctable)+ `AUTH_INVALID`(terminal)に分割。`CREDENTIAL_IN_ARGS` がリクエストペイロード内の認証情報を拒否。request-signing `protocol_methods_*` 名前空間 | | **冪等性** | 呼び出しごとのリプレイのみ | Rule 9(並行リトライ)+ Rule 10(下流再照合)。`IDEMPOTENCY_IN_FLIGHT` エラーコード。`capabilities.idempotency.in_flight_max_seconds` | | **非同期エンベロープ** | create スタイルタスクの 2 形状 submitted エンベロープ | `sync_audiences` に拡張された 3 形状エンベロープ | | **TMP IdentityMatch** | 基本リクエスト/レスポンス | `serve_window_sec` frequency-cap データフロー。リクエストに `seller_agent_url` 必須。任意の `package_ids` | | **`adagents.json`** | authoritative のみのディスカバリー | マネージドネットワークスケール(20 MB 上限 + `publisher_domains[]` コンパクト形式)。ads.txt `managerdomain` フォールバック。厳格化された `revoked_publisher_domains[]` セマンティクス | | **スキーマ整理** | — | SDK ジェネレーターのための名前付き再利用可能スキーマ。`x-adcp-hoist` オプトインマーカー。意図的にオープンな JSON ペイロードフィールドの `x-adcp-open-payload` マーカー。オープン文字列コンプライアンスシナリオ。text-asset-requirements の `allowed_values`。`vast_tracker` + `daast_tracker` アセットタイプ。create/update レスポンスの任意 `currency`/`total_budget` | | **コンプライアンススイート** | ツールごとのシナリオ | `frequency_cap_enforcement`、`per_creative_attribution`、`metric_mode`、ROAS、`audience_buy_flow`、`event_dedup_flow`、`performance_buy_flow`、`product_signal_targeting` のケイパビリティゲートシナリオ。ストーリーボード `requires` ランタイムゲート。`comply_test_controller` サンドボックスゲート。バージョンスコープのバッジ証拠。公開コンプライアンスバンドルのパッケージ参照検証 | ### ケイパビリティスロット移行ノート `definePlatform` などの SDK ヘルパーを使うとき、サポートされないケイパビリティスロットを `get_adcp_capabilities` から欠如させてください。欠けているスロットは正直なスコープ境界です: そのスロットをターゲットにするストーリーボードとローカルテストベクターは、失敗ではなく `not_applicable` とグレードすべきです。 現在の回避策: プレリリース/カスタムランナーは、宣言されたスロットスコープ外のベクターに明示的なスキップゲートを追加すべきです。ランナー側のフォローアップは `adcp-client#2244` で追跡されます。 ## 主要機能 ### 分散型 `brand.json` — サブブランドが自己公開 ブランドは今や、コーポレートハウスがポートフォリオポインター(`brand_refs[]`)経由で所有権を宣言する一方で、自身のドメインで **独自の** 正準 `brand.json` を公開できます。階層は 1 レベルの深さのまま — ハウスのみが所有権を宣言します。信頼は相互アサーション経由で解決されます: 両側が相互に応じます。アイデンティティ属性(ロゴ、色、トーン、タグライン)はリーフの TLS のみを信頼します。関係信頼(ガバナンス伝播、課金対象の包含)は相互エントリでゲートされます。 IAB の `ads.txt` / `sellers.json` / `app-ads.txt` 相互公開パターンと同じ形状を、ブランドアイデンティティに適用。加えて: 任意の `status`、`license_type`、`licensor_domain`、`countries`、`nice_classes`(業界横断の曖昧性解消)を伴う型付き `trademarks[]`。コンプライアンスフィールドは strictest-of で解決される(ブランドレベルは厳格化でき、決して弱められない)一方、アイデンティティフィールドは brand-wins のまま。 → 規範的仕様: [`brand.json` § 分散型公開](/docs/brand-protocol/brand-json#distributed-publishing) · PR [#4505](https://github.com/adcontextprotocol/adcp/pull/4505) ### `verify_brand_claim` / `verify_brand_claims` — 連合ブランド検証 2 つの新しいブランドプロトコルタスクにより、パートナーはクレームがそのブランドに属するかを権威あるかたちでブランドに尋ねられます: ブランド自身のドメインで公開されたブランドエージェントに、商標所有権、広告クリエイティブクレーム、アセット権利を検証する必要のある誰もがクエリします。設計上連合 — すべてのブランドエージェントは自身のブランドのみに答えます。#4505 のメールベースの自己修復 SHOULD を、より豊かなプルベースの DRM-for-brand-identity サーフェスとして再構成します。 RC4 は信頼エンベロープをロックします: 成功した `verify_brand_claim` と `verify_brand_claims` レスポンスは、正準タスクボディレスポンスに対するペイロードエンベロープ JWS の `signed_response` を要求します。署名は回答を指定タスク、解決されたブランドテナント、応答エージェント URL、呼び出し元/リクエストハッシュ、`iat`/`exp` 鮮度ウィンドウにバインドします。検証者は `adcp_use: "response-signing"` で鍵を解決し、未署名のレスポンスフィールドと `signed_response.payload.response` の不一致を拒否します。 → 仕様: [Brand Protocol § verify\_brand\_claim](/docs/brand-protocol/tasks/verify_brand_claim) · [Security § 指定タスクレスポンス署名](/docs/building/by-layer/L1/security#designated-task-response-signing) · PR [#4540](https://github.com/adcontextprotocol/adcp/pull/4540)、[#4603](https://github.com/adcontextprotocol/adcp/pull/4603)、[#5192](https://github.com/adcontextprotocol/adcp/pull/5192) ### 依存関係影響 webhook とスナップショット整合性 メディアバイが依存するリソースがオフライン状態に遷移するとき — オーディエンス停止、承認後のクリエイティブ停止/拒否、カタログアイテム撤回、イベントソース静止、プロパティ公開停止 — バイヤーは 2 つの並行サーフェスを通じてそれを見ます: * **スナップショット。** `media_buy.health` が `ok` から `impaired` に切り替わる。`media_buy.impairments[]` がすべてのオフラインリソースをその package\_ids、遷移、reason\_code、修復ヒントとともにリストする。次の `get_media_buys` 読み取りが現在の真実を示す。 * **ログ。** `notification-type: impairment` webhook が `notification_id = impairment_id` と同じペイロード形状で発火し、`push_notification_config` 経由で設定される。 いずれの経路も完全。プッシュとプルが不一致のとき、バイヤーはスナップショット経由で再照合します。セラーは `capabilities.media_buy.propagation_surfaces`(`["snapshot"]`、`["webhook"]`、`["snapshot", "webhook"]`、または `["out_of_band"]`)でどのサーフェスを使うかを宣言します。`impairment.coherence` コンプライアンス不変条件がコントラクトをエンドツーエンドでグレードします(forward、inverse、health-iff ルール。terminal-status バイで緩和)。 → 仕様: [Media Buy Lifecycle § Health & impairments](/docs/media-buy/media-buys/lifecycle#health-impairments) · [スナップショットとログコントラクト](/docs/protocol/snapshot-and-log) · RFC #2853 · PR #4588、#4601、#4677、#4685、#4690 ### Webhook 基盤 + バイヤー側の配信可視性 3.1 はすべてのプッシュサーフェスのための 1 つの永続チャネルコントラクトを成文化します: スナップショットが権威的、プッシュは at-least-once かつ順序なし、`idempotency_key` で重複排除、`notification_id` で状態を相関(今や `mcp-webhook-payload.json` のエンベロープレベルで型付け)、リプレイ = スナップショットを再読み取り。将来の webhook RFC は基盤を再導出する代わりにそれを参照します。サブスクリプションモデルはアカウントごとに拡張され、メディアバイがクリエイティブを直接参照していなくてもクリエイティブライブラリレベルのイベント(クリエイティブ状態変更)が発火します。 本番デバッグのため、バイヤーは `get_media_buys` の `webhook_activity[]` にオプトインできます — 見えるバイの最近の発火を、HTTP ステータス、発火時刻、`idempotency_key` とともに。「パブリッシャーが発火したがゲートウェイが 5xx を返して見えない」というブラックボックスはもうありません。純粋なセルフサービス: バイヤーはオペレーターの往復なしに自身の統合をデバッグします。 → 仕様: [スナップショットとログコントラクト](/docs/protocol/snapshot-and-log) · [Webhooks § 永続チャネルコントラクト](/docs/building/by-layer/L3/webhooks#persistent-channel-contract) · RFC #4582 · PR #4601、#4701、#4730 ### ホールセールフィードミラーリング — 条件付きフェッチ、ホールセールシグナル、webhook 3 つのコンパニオン提案により、コンシューマー(ストアフロント、連合マーケットプレイス、レジストリ、代理店ブランドスタック)は、ポーリングごとのホールセールフェッチで帯域を消費せずに、接続されたすべての AdCP エージェントの購入可能なホールセールプロダクトフィードとホールセールシグナルフィードのほぼリアルタイムのローカルミラーを維持できます。独立かつ補完的 — エージェントは任意のサブセットを採用してもよく(MAY)、コンシューマーはそうしないエージェントに対してホールセールポーリングにフォールバックします。 用語: このセクションは `get_products` / `get_signals` からのセラー側プロダクトとシグナルに **ホールセールフィード** を使います。それは、バイヤー提供のキャンペーン入力フィードをセラーアカウントにアップロードする `sync_catalogs` とは異なります。 * **条件付きフェッチ(`if_wholesale_feed_version`)。** すべての `get_products` / `get_signals` レスポンスは不透明な `wholesale_feed_version` トークンを返します。次の呼び出しでそれを戻し、セラーは `unchanged: true` で短絡してもよい(MAY) — プロダクトやシグナルのペイロードなし、ページごとの差分なし。ETag/HTTP セマンティクス。構造的メタデータと独立してレートカードを動かすセラーのための任意のコンパニオン `pricing_version`。`if_pricing_version` は `if_wholesale_feed_version` を必要とします(`dependencies` 経由でスキーマ強制)。後方互換: トークンを無視する 3.1 以前のエージェントは完全なペイロードを返すだけ。 * **ホールセールシグナル(`discovery_mode: "wholesale"`)。** 呼び出し元は `signal_spec` / `signal_refs` / 非推奨 `signal_ids` を省略し、シグナルエージェントの完全な価格付きシグナルフィードをページ分割して列挙できます。`get_products` `buying_mode: "wholesale"` と対称で、以前ストアフロントとマーケットプレイスをシグナルフィードのミラーのためのハックなプローブクエリに強いていたギャップを閉じます。 * **ホールセールフィード webhook。** `sync_accounts.accounts[].notification_configs[]` を通じて登録されるアカウントレベル webhook が `product.{created,updated,priced,removed}`、`signal.{created,updated,priced,removed}`、`wholesale_feed.bulk_change` 通知を発します。各 webhook は `core/wholesale-feed-webhook.json` を運びます: 実際に変更されたプロダクト/シグナルペイロードまたは一括変更サマリー、変更後の `wholesale_feed_version`、キャッシュ無効化のための `applies_to.scope`。ポーリングイベントタスクはありません。コンシューマーは逃したまたは信頼されないプッシュを `get_products` / `get_signals` を通じて修復します。 **キャッシュ層は負荷を担う設計判断です。** すべてのレスポンスは `cache_scope: "public" | "account"` を宣言します(スキーマ必須 — 2 層キャッシュの安全プロパティがそれに依存する)。リクエストに `account` がなかったとき、`"public"` でなければなりません(MUST)。リクエストに `account` があったとき、セラーは `"public"`(このアカウントはレートカードで価格設定 — バイヤーは未認証ビューと重複排除)または `"account"`(カスタムオーバーライド — バイヤーはアカウントキーでキャッシュ)を宣言します。ほとんどのセラーのほとんどのアカウントは public 層で価格設定するため、N 個のアカウントキャッシュを保持するコンシューマーは通常 1 つの public キャッシュ + 少数のオーバーレイに重複排除します。イベントは `applies_to.scope`(任意の `account_ids[]` 付き)を運ぶため、コンシューマーは正しいキャッシュ層を無効化します — public イベントはすべてのオーバーレイにカスケードし、account イベントは名前付きオーバーレイのみに触れます。セラーは、以前アカウントスコープだったタプルに public スコープレスポンスを返すことで、アカウントを `"account"` から `"public"` にダウングレードしてもよく(MAY)、「このアカウントはもうオーバーライドを持たない。オーバーレイをドロップせよ」を示します。 **セキュリティ姿勢は正直です。** 助言的ペイロードのフレーミングは、フィードイベントを `get_products` / `get_signals` に対して再検証することがトランスポート改ざんのみに対して防御することを明示します — 侵害されたエージェントオペレーターは自身の嘘を再確認します。オペレーター侵害防御は、支出をゲートする既存の信頼アンカー(署名付き `create_media_buy` レスポンス、マーケットプレイスシグナル来歴のための `adagents.json` ピン留め署名鍵)に存在し、フィードイベントのコンテンツ署名は 4.0 R-1 root-of-trust トラックに延期されます。イベントを安価なミラー無効化として扱い、ドルや権限をコミットする任意の決定の基礎としてはなりません。 ケイパビリティ宣言: `wholesale_feed_versioning`(条件付きフェッチ + `pricing_version_separate` + `cache_scope_account`)、`wholesale_feed_webhooks`(webhook 変更ペイロード)、`media_buy.buying_modes` と `signals.discovery_modes`(ホールセールサポート)。`product.*` webhook イベントをアドバタイズするエージェントはホールセール `get_products` もアドバタイズしなければならず、`signal.*` イベントをアドバタイズするエージェントはホールセール `get_signals` もアドバタイズしなければならず、`wholesale_feed.bulk_change` はそれらの修復パスの 1 つに裏付けられたフィードファミリーのみを名指ししなければなりません。webhook エンベロープの JSON Schema は `core/wholesale-feed-webhook.json`、`core/wholesale-feed-event.json`(event\_type で判別、9 ブランチ + `appliesTo` / `removalReason` `$defs`)をラップ。 → 仕様: [ホールセールフィード webhook](https://github.com/adcontextprotocol/adcp/blob/main/specs/wholesale-feed-webhooks.md) · [`get_products` § ホールセールフィードバージョニング](/docs/media-buy/task-reference/get_products#wholesale-feed-versioning) · [`get_products` § キャッシュ層](/docs/media-buy/task-reference/get_products#cache-layering) · [`get_signals` § ホールセールシグナルフィード](/docs/signals/tasks/get_signals#wholesale-signals-feed) · PR [#4761](https://github.com/adcontextprotocol/adcp/pull/4761)(条件付きフェッチ)、[#4762](https://github.com/adcontextprotocol/adcp/pull/4762)(ホールセールシグナル)、[#4763](https://github.com/adcontextprotocol/adcp/pull/4763)(フィード webhook)、[#4767](https://github.com/adcontextprotocol/adcp/pull/4767)(クラスター実装) ### プロダクトスコープシグナルターゲティング — 含まれる対選択可能なシグナル 3.1 は、メディアバイのセラー提供シグナルのためのプロダクトスコープシグナルターゲティングコントラクトを追加します。これは広範なシグナルディスカバリーと実際のパッケージレベルのバイサーフェスの間のギャップを閉じます: * **`included_signals`** は、プロダクトにすでにバンドル、包含、またはセラー計画されたシグナルを記述します。これらは記述的なプロダクトメタデータであり、バイヤーが選択可能な制御ではありません。 * **`data_provider_signals` は非推奨。** レガシーバンドルメタデータとして互換性のため残りますが、新しい実装は非選択のシグナルに `included_signals`、選択可能なものに `signal_targeting_options` を使います。 * **`signal_targeting_allowed`** は、プロダクトがパッケージレベルの `signal_targeting_groups` サーフェスを持つことをバイヤーに伝えます。デフォルトは false。 * **`signal_targeting_options`** は、プロダクトがプロダクト固有の価格、アクティベーションハンドル、デフォルト/固定選択、グループ化ヒント、または brief/refine 選択のサブセットを必要とするときのインライン選択可能メニューです。ホールセールプロダクトはこのフィールドを省略し `get_signals` を選択可能フィードとして使えます。 * **`signal_targeting_rules`** は、プロダクト固有の合成コントラクトを宣言します: direct ターゲティング対 seller-planned 解決、optional/required/fixed 選択、min/max 数、グループ化制限。単一のセラーがプロダクトを異なるアドサーバーや計画層を通じてルーティングしうるため、これはプロダクトに属します。 バイヤーは選択されたシグナルを `packages[].targeting_overlay.signal_targeting_groups` で適用します。ポータブルなベースラインは意図的にシンプルです: トップレベル `operator: "all"` と、include の子 `operator: "any"` グループ、exclude の子 `operator: "none"` グループ。バイナリシグナルについては、シグナル式は `value: true` を使います。除外は `value: false` ではなく親 `none` グループで表現されます。 シグナルアイデンティティも正規化されます。新しいペイロードは `signal_ref` を使います: * プロバイダーの公開 adagents.json シグナルで定義されたシグナルには `scope: "data_provider"` + `data_provider_domain` + `signal_id`。 * 上流 adagents.json シグナルで公開されていないソースネイティブシグナルには `scope: "signal_source"` + `signal_source_url` + `signal_id`。 * 選択されたプロダクト/パッケージコンテキスト内でのみ意味のあるプロダクトローカルオプションには `scope: "product"` + `signal_id`。 レガシー `SignalId` / `signal_id.source` は、`get_signals`、オーディエンスセレクター、レガシーフラットシグナルターゲティング、ホールセールシグナルイベントを含め、マイナーバージョン移行ウィンドウ中受け入れられたままですが、`SignalRef` が新しいクライアントの正準形状です。レガシーフラット `targeting_overlay.signal_targeting` はスキーマ有効だが非推奨のままです。新しいパッケージレベル合成は `signal_targeting_groups` を使います。 → 仕様: [Product discovery § Signal targeting](/docs/media-buy/product-discovery/media-products#signal-targeting) · [Targeting § signal\_targeting\_groups](/docs/media-buy/advanced-topics/targeting#signal_targeting_groups) · [`get_signals`](/docs/signals/tasks/get_signals) · PR [#5009](https://github.com/adcontextprotocol/adcp/pull/5009) ### 正準クリエイティブフォーマット — ライブ、12 正準、後方互換 * **公開済み投稿参照クリエイティブ。** 既存のソーシャル/パブリッシャー投稿は、`asset_source: "publisher_owned_reference"` と `published_post` スロットを伴う正準 `video_hosted`、`image`、`native_in_feed` フォーマットとして表現されます。プロダクトは、広告主アカウントやパブリッシャーアイデンティティ接続などの下流プラットフォーム付与のため `required_connections[]` を宣言できます。欠けているまたは期限切れの付与は `error.details.missing_connections[]` を伴う `AUTHORIZATION_REQUIRED` を使います。回復可能な依存関係の喪失は、ポリシー拒否ではなくクリエイティブを `suspended` に移します。カタログ駆動のリテールメディアは `source_catalog` を伴う `sponsored_placement` のままです。 3.1 でライブ、3.0 に対して加算的。プロダクトは `format_options[]` を運びます: 正準 enum からの `format_kind` 判別子を持つ `ProductFormatDeclaration` エントリのリスト。**12 正準:** `image`、`html5`、`display_tag`、`video_hosted`、`video_vast`、`audio_hosted`、`audio_daast`、`image_carousel`、`native_in_feed`、`responsive_creative`、`sponsored_placement`、`agent_placement`。enum は、正準に適合しない採用者定義の形状のエスケープハッチとして `custom` も含みます。3 つの正準(`sponsored_placement`、`responsive_creative`、`agent_placement`)+ `custom` はフレームワーク内で **実験的** とタグ付けされます。残りの正準は非実験的です。新しい正準の昇格キューは [#3666](https://github.com/adcontextprotocol/adcp/issues/3666) で追跡されます。 **後方互換性。** v1 `format_ids` パスは依然として機能します。`ProductFormatDeclaration` は任意の `v1_format_ref: [{agent_url, id}]` 配列を運ぶため、v2 宣言は 1 つ以上の v1 名前付きフォーマットにリンクします — セラーは移行ウィンドウ中デュアル発行できます。SDK は enum を **パース時にオープン** として扱います: 未知の将来の正準は検証に失敗しません。SDK は `declared_only` のようなローカルルーティングステータスをサーフェスしてもよいが、そのステータスは 3.1 ワイヤーフィールドではありません。 **パブリッシャーカタログ。** `list_creative_formats(publisher_domain="…")` は、`/.well-known/adagents.json formats[]` を読んでパブリッシャーの権威あるフォーマットリストを返し、AAO コミュニティミラー、次にエージェント由来にフォールバックします。レスポンスは `source: "publisher" | "aao_mirror" | "agent_derived"` を運ぶため、バイヤーはどの層がリストを生成したかを知ります。 **サイズ柔軟性。** ディスプレイ正準はサイズを 3 つのモードで宣言します: 固定(`width`+`height`)、マルチサイズ(`sizes: [{w,h}]` — OpenRTB `banner.format[]` をミラー)、またはレスポンシブ(`min_width`/`max_width`/`min_height`/`max_height`)。相互排他的。 **ホスト音声/動画 duration 範囲。** `audio_hosted` と `video_hosted` は、固定 duration スロットに `duration_ms_exact`、有界または片側範囲に `duration_ms_range` を使います。`duration_ms_range` のいずれのエンドポイントも `null` でよい(MAY): `[null, 60000]` は「最大 60 秒」、`[15000, null]` は「少なくとも 15 秒」を意味します。`[null, null]` は無効で、両方の duration フィールドが存在する場合 `duration_ms_exact` が優先されます。 → 仕様: [正準フォーマット](/docs/creative/canonical-formats) · PR [#3307](https://github.com/adcontextprotocol/adcp/pull/3307)、[#4770](https://github.com/adcontextprotocol/adcp/pull/4770)、[#5323](https://github.com/adcontextprotocol/adcp/pull/5323) ### クリエイティブトランスフォーマー — ビルドケイパビリティを発見、選択、ファンアウト、バリアント 3.1 は **トランスフォーマー** を導入します: メディアバイプロダクトのクリエイティブ版。トランスフォーマーは、エージェント提供の、アカウントスコープの、選択可能なビルドケイパビリティの単位 — ボイス、モデル、スタイル、ディレクター — で、型付き設定サーフェスとアカウントごとの価格を持ちます。セットはアカウント固有で動的(設定済みのボイスはグローバル enum ではない)なので、ディスカバリーは `get_products` がアカウントスコープのインベントリをサーフェスするのと同じ方法でエージェント → バイヤーに流れます。 * **`list_transformers`** は新しいディスカバリーサーフェスです。あなたのアカウントにクリエイティブエージェントが提供するトランスフォーマーを、それぞれ `input_format_ids` / `output_format_ids`、型付きパラメータースキーマ、(`include_pricing` で)`per_unit` レートカードとともに返します。その `expand_params` モードは、あなたに古いローカルリストを保持させる代わりに、同じツールでパラメーターのアカウントスコープの列挙可能なオプション値 — 例えば実際の設定済みボイス — を返します。`get_adcp_capabilities` で `creative.supports_transformers: true` を設定するエージェントのみが提供します。 * **`build_creative` がトランスフォーマーを選択・設定。** `transformer_id` を渡して 1 つを選び(ターゲットフォーマットはその `output_format_ids` のサブセットでなければならない(MUST))、トランスフォーマーのパラメーターにキー付けされた型付き `config` バッグを渡します。検証は厳格: エージェントは未知のキーと範囲外の値をフィールド帰属エラーで拒否しなければなりません(MUST)。ベンダー固有のノブは `ext` へ。 * **2 つのファンアウト軸。** `max_creatives` はアイテム/カタログ軸: N 個の異なるクリエイティブ、カタログアイテムごとに 1 つ(「150 のうち 5」サンプリング) — 1 つのクリエイティブ *内* で使われるアイテムを上限する `item_limit` とは別。`max_variants`(デフォルト 1)は `variant_axis`(`voice` | `theme` | `best_of_n` | `transformer_config` | `custom`、任意の `values[]` と `label` 付き)に沿ってクリエイティブごとの代替を生成します。`keep_mode`(`keep_all` | `keep_one` | `keep_some`)は助言的。解像度と品質レベルは **フォーマット** 軸(`target_format_ids`)であり、バリアントではありません。 * **新しい `BuildCreativeVariantSuccess` レスポンスメンバー**(6 のうち `oneOf` メンバー 3)は `creatives[]` を運び、それぞれ `{ build_creative_id, catalog_item_ref?, variants[] }`。各バリアントは、リーフごとの価格領収書(`pricing_option_id` + `vendor_cost` + `currency` + `consumption`)を伴う `{ build_variant_id, creative_manifest, variant_axis_value?, recommended, rank?, ... }`。ビルドがコストをレポートするとき(集計 `vendor_cost` が存在するとき)、生成されたすべてのリーフは独自の `vendor_cost` + `currency` を運びます(スキーマ強制)。トップレベル `items_total` / `items_returned` に加え集計 `vendor_cost`。出荷済みの `BuildCreativeSuccess` / `BuildCreativeMultiSuccess` は **変更なし**。 * **Best-of-N はバリアント + `keep_mode` + `recommended`/`rank`。** `build_variant_id` は独自の名前空間 — `preview_id`(プレビューレンダー)や配信された `variant_id`(配信)を決して再利用しない。生成されたすべてのバリアント(`per_unit` × N)を **支払う**。保持は選ばれた `build_variant_id` をトラフィックするクライアントの行為。保持されたバリアントは遅延的に `creative_id`(ライブラリに追加 / 最初にトラフィック)を得て `report_usage` に流れる。**フォーマット** ごとの生成はアトミック。**アイテム** ごと(カタログファンアウト)は非アトミック。 * **エバリュエーターランキングはクリエイティブ機能ディスカバリーを再利用。** `creative.supports_evaluator: true` を持つエージェントは、既存の `get_adcp_capabilities.governance.creative_features` カタログをエバリュエーター機能ディスカバリーサーフェスとして使います。`rank_by`、`feature_requirement`、`variants[].eval.features[]` はすべてその同じ機能語彙を参照します。`evaluator_id` は、そのカタログの ID ではなく、事前プロビジョニングされたアカウントプリセットです。`feature_agent.agent_url` は許可リストされた外部スコアリングパスを選択し、`feature_id` はセラーの accepted-verifier エントリに従って要求された機能サブジェクトを曖昧性解消します。`agent_url` 評価が `eval_budget` の下で実行されるとき、セラーは `eval.calls_used` / `eval.seconds_used` などのフィールドでリーフごとの外部判定使用をエコーすべきです(SHOULD)。 * **価格はトランスフォーマーに移動。** レートは `transformer.pricing_options`(`per_unit`)に存在し、`build_creative` でリーフごとの領収書としてインラインでエコーされ、`report_usage` 経由で決済されます。`Format.pricing_options` は `transformer.pricing_options` を優先して **非推奨** です。 **非推奨(3.1、4.0 で削除)。** `Format.input_format_ids`、`Format.output_format_ids`、`Format.pricing_options`、加えて `list_creative_formats` の `input_format_ids` / `output_format_ids` フィルターは非推奨で、すべて `list_transformers` にリダイレクトします。SDK は 3.1–3.x を通じてそれらを尊重します。4.0 で削除されます。フォーマット添付の入力/出力/価格の読み取りを `list_transformers` に移行してください。完全な移行(ディスカバリー劣化、出力ごとの価格、best-of-N 支出の危険を含む): [Migration › クリエイティブトランスフォーマー](/docs/reference/migration/creative-transformers)。 → 仕様: [`list_transformers`](/docs/creative/task-reference/list_transformers) · [`build_creative`](/docs/creative/task-reference/build_creative) · [`get_adcp_capabilities` § creative features / evaluator support](/docs/protocol/get_adcp_capabilities) ### リリース精度バージョンネゴシエーション — リリースをピン留め すべてのリクエストとレスポンスは今や `adcp_version`(リリース精度: 安定リリースの `"3.1"`)を運びます。セラーは `get_adcp_capabilities` で完全な `supported_versions` セットをアドバタイズし、エンベロープルートで実際に提供したリリースをエコーします。SDK はコンストラクターオプション(JS の `adcpVersion: "3.1"`、Python の `adcp_version="3.1"`、Go の `WithAdcpVersion("3.1")`)でピン留めし、レガシーフィールドのみを読むセラーとの互換性のため新しい文字列と整数 `adcp_major_version` ミラーの両方を発します。整数は 3.x を通じて機能し続けます — 加算的出荷、3.0 準拠エージェントに必須の変更なし。`VERSION_UNSUPPORTED` は `error.data.supported_versions[]` エコー付きで型付けされ、リトライが帯域外ルックアップを必要としません。 → 仕様: [Versioning § Version negotiation](/docs/reference/versioning#version-negotiation) · PR [#3493](https://github.com/adcontextprotocol/adcp/pull/3493) ### ベンダー証明測定 — `vendor_metric` 目標 + プロダクトごとのケイパビリティ 最適化目標は今や 3 番目の `kind: "vendor_metric"` 形状をサポートします — 目標をアテンション(DV、IAS、Adelaide、TVision、Lumen)、パネルベースのブランドリフト(Kantar、Upwave、Cint)、排出(Scope3、Good-Loop)、リテールメディアパートナーメトリクスなどのベンダー証明メトリクスにバインドします。3.0 の `attention_seconds` のようなベンダー非依存の enum 値がベンダーバインドなしには無意味だったギャップを閉じます。 セラーはプロダクトごとの `vendor_metric_optimization` を `supported_metrics[]`(ビディングスタックが向かえる `(vendor, metric_id)` ペア)とともに宣言します。目標受け入れの 3 前提条件拒否ルール — ディスカバリー、ケイパビリティ、レポート整合性 — が、目標がエンドツーエンドで steerable かつ reportable であることを保証します。加えて、ケイパビリティゲートのコンプライアンスシナリオのための `conversion_tracking` のセラーレベル `supported_optimization_metrics` と `supported_target_kinds`。 `measurement.metrics[]` を定義する測定ベンダーカタログは 3.1 で実験的です。それを実装するベンダーは `experimental_features` に `measurement.core` を宣言しなければなりません。バイヤーは、測定タスクとコンプライアンスストーリーボードが凍結されるまで、カタログディスカバリーを 3.x 実験的サーフェスとして扱うべきです。 → 仕様: [Optimization goals § `vendor_metric` kind](/docs/media-buy/media-buys/optimization-reporting#vendor-metric-goals) · PR [#4668](https://github.com/adcontextprotocol/adcp/pull/4668)、[#4669](https://github.com/adcontextprotocol/adcp/pull/4669)、[#4649](https://github.com/adcontextprotocol/adcp/pull/4649) ### 配信レポート — `reach_window`、`viewed_seconds`、ウィンドウ付きプル 3 つの加算的サーフェスがレポートギャップを閉じます。**`reach_window`** は `reach` と `frequency` の測定ウィンドウ(cumulative / period / rolling)を宣言します — バイヤーはそれなしに行をまたいで reach を合計してはなりません(MUST NOT)。**`viewability.viewed_seconds`** は測定可能なインプレッションごとの平均インビュー duration をレポートし、`viewed_seconds` 最適化目標のレポート側の対応物です。`get_media_buy_delivery` の **ウィンドウ付きプルリカバリー** は `time_granularity` + `include_window_breakdown: true` を受け入れ、同じ粒度で `reporting_webhook` ペイロードと形状整合する `windows[]` スライスを返します — webhook 発火を逃したバイヤーはポーリングで同一データを再構成します。`reporting_capabilities.windowed_pull_granularities` 経由でケイパビリティスコープ。セラーは webhook 対プルの非対称頻度を正直に宣言できます。 → 仕様: [配信メトリクスリファレンス](/docs/media-buy/task-reference/get_media_buy_delivery) · PR [#4618](https://github.com/adcontextprotocol/adcp/pull/4618)、[#4601](https://github.com/adcontextprotocol/adcp/pull/4601) ### 課金サーフェス — 権威、確定、帯域外 2 つの補完的な変更が課金グレードのレポートストーリーを閉じます。**権威 + 確定フラグ:** `get_media_buy_delivery` レスポンスは今や `media_buy_deliveries[*]` と各 `by_package[*]` に行レベルの `is_final` + `finalized_at` を運びます — バイヤーは数が動かなくなり請求書再照合に安全なときを知ります。`report_usage` で対称: 各使用量レコードは `final`(デフォルト `true`)、`finalized_at`、`measurement_window` を運びます。**`bills_through_adcp` + `BILLING_OUT_OF_BAND`:** クリエイティブエージェントは `capabilities.creative.bills_through_adcp` 経由でプロトコル上で課金するか帯域外で課金するか(フラットライセンス、SaaS、バンドルエンタープライズ — CM360 が正準ケース)を宣言します。バイヤーは事前フィルターします。帯域外モードのセラーは、黙って受け入れるのではなく新しい `BILLING_OUT_OF_BAND` エラーで `report_usage` 呼び出しを拒否します。 → 仕様: [Billing measurement](/docs/media-buy/advanced-topics/accountability#billing-measurement) · [`report_usage`](/docs/accounts/tasks/report_usage) · PR [#4735](https://github.com/adcontextprotocol/adcp/pull/4735)、[#4561](https://github.com/adcontextprotocol/adcp/pull/4561) ### アクションディスカバリー — `allowed_actions` と `available_actions` バイライフサイクル変更のための構造化アクション語彙。プロダクトは `allowed_actions[]` を助言的テンプレートとしてアドバタイズします(プロダクトが *一般的に* サポートする変更、`modes[]` と `allowed_statuses[]` 付き)。メディアバイは `get_media_buys` / `create_media_buy` / `update_media_buy` レスポンスに `available_actions[]` を運びます — 現在の状態の *この* バイの有効な変更の現在のセット。バイヤーは呼び出して `INVALID_STATE` を得る代わりに、どの変更が有効かを事前確認します。より細かい値が `media-buy-valid-action` enum に追加されます。レガシーの粗い値は後方互換のため 3.x を通じて保持(4.0 で削除)。 3.1 は GA 前の `requires_proposal` アクションモードを削除します。プロポーザルライフサイクルは今や 1 つのパスを持ちます: `proposal_status` が finalize が必要かを言い、`finalize` は確定価格/条件/ホールドへのセラーコミットメント、`create_media_buy(proposal_id)` はバイヤーの受け入れ/実行。`update_media_buy` リクエストが現在の見積もりエンベロープを超える場合、セラーは proposal-required アクションモードをモデル化する代わりに `REQUOTE_REQUIRED` を返します。`requires_proposal` を含むキャッシュされたプレリリースアクションメタデータを持つバイヤーは、それを破棄し現在のプロダクトまたはバイのアクションサーフェスを再読み取りしなければなりません。3.1 は更新の修正見積もりアーティファクトを定義しません。 → 仕様: [Media Buy Lifecycle § Action discovery](/docs/media-buy/media-buys/lifecycle#action-discovery) · [Product discovery § Proposals](/docs/media-buy/product-discovery/media-products#proposals) · PR [#4514](https://github.com/adcontextprotocol/adcp/pull/4514) ### Auth + セキュリティ厳格化 4 つの補完的な変更: **`AUTH_REQUIRED` 分割** を `AUTH_MISSING`(correctable — 認証情報でリトライ)と `AUTH_INVALID`(terminal — 認証情報が提示され拒否。ローテートまたはエスカレート。自動リトライしない)に。リカバリー分類は今やオペレーターの現実に一致します。**`CREDENTIAL_IN_ARGS`** 新エラーコード: セラーは、トランスポート認証チャネルの代わりにタスクペイロードにバイヤープリンシパル認証情報を密輸するリクエストを拒否しなければなりません(MUST) — プロンプトインジェクション流出サーフェスを閉じます。**Request-signing `protocol_methods_*` 名前空間** — RFC 9421 署名スコープが AdCP メソッドサーフェスのみに厳格化。**`comply_test_controller` サンドボックスゲート** — すべてのコントローラー呼び出しは `account.sandbox: true` を運ばなければならず(MUST)、セラーはフィールドを信頼するのではなく永続化されたアカウントレコードに対して検証しなければなりません(MUST)。サンドボックスと本番の間の多層防御境界。 → 仕様: [Error handling § Recovery Classification](/docs/building/by-layer/L3/error-handling#recovery-classification) · PR [#3739](https://github.com/adcontextprotocol/adcp/pull/3739)、[#4057](https://github.com/adcontextprotocol/adcp/pull/4057)、[#4326](https://github.com/adcontextprotocol/adcp/pull/4326)、[#4382](https://github.com/adcontextprotocol/adcp/pull/4382)/[#4392](https://github.com/adcontextprotocol/adcp/pull/4392) ### 冪等性 — Rules 9 + 10 + `IDEMPOTENCY_IN_FLIGHT` 2 つの新しいルールが本番エッジケースを閉じます。**Rule 9(並行リトライ):** バイヤーが元の呼び出しがキャッシュされたレスポンスを生成する前にリトライするとき、セラーはブロックする代わりに `IDEMPOTENCY_IN_FLIGHT`(新エラーコード)を返してもよい(MAY) — 最初の呼び出しが遅い下流システム(SSP、アドサーバー、支払いプロバイダー)を呼ぶときに有用。バイヤーはそれを transient として扱わなければならず(MUST)、新しい `idempotency_key` を鋳造してはなりません(MUST NOT)。**Rule 10(下流再照合):** `IDEMPOTENCY_EXPIRED` レスポンスが到着し元が成功した証拠があるとき、バイヤーがどう再照合するかの明示的なガイダンス — 新しいキーを生成する前に自然キーチェック(例: `context.internal_campaign_id` による `get_media_buys`)を行う。**`capabilities.idempotency.in_flight_max_seconds`** 新ケイパビリティ — セラーは、バイヤーがリトライペーシングを調整できるよう、in-flight 呼び出しがどのくらいかかりうるかを宣言。 → 仕様: [Calling an agent § Idempotency](/docs/protocol/calling-an-agent) · PR [#4402](https://github.com/adcontextprotocol/adcp/pull/4402)、[#4409](https://github.com/adcontextprotocol/adcp/pull/4409) ### TMP IdentityMatch アップグレード 3 つの加算的変更: **`serve_window_sec`** レスポンスの新しい必須フィールド(1–300 秒) — ルーターは再クエリ前にこの秒数だけ適格性決定をキャッシュ。以前の `ttl_sec` フレーミングを frequency-cap-data-flow 認識セマンティクスに置き換え。**`seller_agent_url`** は今やリクエストで必須で、ルーターが決定を発信元セラーにルーティングし戻せる。**`package_ids`** は必須から任意に移動 — ルーターはパッケージを列挙せずに「このユーザーはそもそも適格か?」を尋ねられる。 → 仕様: [TMP IdentityMatch implementation](/docs/trusted-match/identity-match-implementation) · PR [#4070](https://github.com/adcontextprotocol/adcp/pull/4070)、[#3687](https://github.com/adcontextprotocol/adcp/pull/3687) ### `adagents.json` — マネージドネットワークスケール、manager-domain フォールバック、失効セマンティクス 3 つの本番スケール改善。**マネージドネットワークスケール:** 権威ある `adagents.json` は今や 20 MB を上限とし、大きなエージェントネットワークを公開するマネージャーは、すべてのプロパティをインライン化せずに所有ドメインをリストするコンパクトな `publisher_domains[]` 形式に切り替えます。**Manager-domain フォールバック:** パブリッシャーの権威ある `adagents.json` が欠けているとき、クローラーは `ads.txt` で宣言された `managerdomain` にフォールバックします — 404 を直接返せない S3 / CloudFront ホストのパブリッシャーのディスカバリーギャップを閉じます。**失効セマンティクス:** `revoked_publisher_domains[]` は今や厳密に時間制限されます — 失効は黙った削除ではなく、発見可能なタイムスタンプを伴う公開された事実です。マネージドネットワークをまたいだ信頼伝播を厳格化します。 → 仕様: [`adagents.json` リファレンス](/docs/governance/property/adagents) · PR [#4504](https://github.com/adcontextprotocol/adcp/pull/4504)、[#4173](https://github.com/adcontextprotocol/adcp/pull/4173)、[#4536](https://github.com/adcontextprotocol/adcp/pull/4536) ### コンプライアンススイート — ケイパビリティゲートシナリオ ケイパビリティゲートのストーリーボードシナリオにより、セラーは主張するもの *のみ* を実行できます。`frequency_cap_enforcement`、`per_creative_attribution`、`metric_mode` + ROAS(`contains:` マッチャーを使用)、`audience_buy_flow`、`event_dedup_flow`、`performance_buy_flow`(ケイパビリティゲートの CPA バイ)の新しいシナリオ。加えて、宣言されたケイパビリティに実行を条件付けるストーリーボードの新しい `requires` ランタイムゲート — もう all-or-nothing シナリオはありません。完全なセットは [Compliance catalog](/docs/building/compliance-catalog) で列挙されています。 **GA 前後期のコンプライアンス更新:** money-moving セラー専門分野は今や、spend-committing フローの前にベースライン `sync_governance` 登録を実行します: `sales-guaranteed`、`sales-non-guaranteed`、`sales-broadcast-tv`、`sales-catalog-driven`、`sales-social`、`creative-generative` 下の生成セラーフロー。これはすべてのセラーをガバナンス認識にはしません。`governance-aware-seller` は `check_governance` 相談と伝播のためのオプトインクレームのままです。これらの専門分野を主張する既存の GA 前セラーは、3.1 グレーディングで準拠のままであるために `sync_governance` 登録を実装し、複数の `governance_agents` エントリを持つペイロードを拒否しなければなりません。 **コンプライアンスパッケージングクロージャ:** パッケージ化されたコンプライアンスアーティファクトは今や自己完結です。webhook レシーバーエンベロープベクターはバージョン管理されたコンプライアンスツリー下に存在し、作られたベクター/test-kit 参照がパッケージ化された `/compliance/{version}/` バンドルまたはプロトコル tarball 内で解決しないとき、リリース検証が失敗します。シグナル適合性も義務で分割されます: `signal-owned` とベースラインシグナルプロトコルはディスカバリーのみ(`get_signals`)、`signal-marketplace` は `activate_signal` を要求します。 → 仕様: [Compliance catalog](/docs/building/compliance-catalog) · PR [#4312](https://github.com/adcontextprotocol/adcp/pull/4312)、[#4642](https://github.com/adcontextprotocol/adcp/pull/4642)、[#4664](https://github.com/adcontextprotocol/adcp/pull/4664)、[#4722](https://github.com/adcontextprotocol/adcp/pull/4722)、[#4727](https://github.com/adcontextprotocol/adcp/pull/4727)、[#4731](https://github.com/adcontextprotocol/adcp/pull/4731)、[#5187](https://github.com/adcontextprotocol/adcp/pull/5187) ### 最終仕様の明確化(WG レビューバッチ) 仕様がプレリリース検証を通じて落ち着くにつれ、規範的な厳格化が着地しました。ほとんど低リスク — すでに妥当なデフォルトを推論していた採用者は動作し続ける — が、3.1 グレーダーがそれらをチェックするので知っておく価値があります。 * **`PROPOSAL_NOT_FOUND` エラーコード**(#4043)。プロポーザルライフサイクルエラーカタログを完成(`PROPOSAL_EXPIRED` と `PROPOSAL_NOT_COMMITTED` と並んで)。セラーは、参照された `proposal_id` が認識されないとき — 誤ったテナント、キャッシュから追い出された、決して finalize されなかった — それを返さなければなりません(MUST)。リカバリー: correctable。 * **前方互換の `error.code` デコード**(#4227)。受信者は `error.code` を **オープン enum** として扱わなければなりません(MUST) — 未知のコードを拒否せずにデコードし、`error.recovery` からリカバリーを分類し、リカバリーが欠けているとき `transient` にデフォルト。3.1 以降の送信者は、すべてのエラーで `error.recovery` を投入しなければなりません(MUST)。ピン留めバージョンの受信者を壊さずに、将来の保守ラインでのエラーコードの additive-in-patch のブロックを解除。 * **すべての AdCP タスクリクエストで `idempotency_key` 必須**(#4399)。仕様が idempotency\_key で重複排除すると言うがバイヤーに送るよう要求しなかった長年のギャップを閉じます。セラーは 3.1 GA 後、キーを欠くリクエストを拒否してもよい(MAY)。 * **MCP ツールラッパーはエンベロープフィールドを許容しなければならない**(#4399)。MCP リクエストのプロトコルエンベロープ(`status`、`context_id`、`context`、`task_id`、`timestamp`、`replayed`、`adcp_error`、`governance_context`、`idempotency_key`)は今や「予期しないフィールド」として拒否される代わりにラッパー層を通ります。採用者が MCP を正常に呼ぶためにエンベロープフィールドを省略しなければならなかったラッパー層のバグを閉じます。 * **MCP シリアライゼーション正規化**(#2911)。プロトコルエンベロープスキーマから `payload.required` を落とし、エンベロープレベルに `context` フィールドを追加し、フラット兄弟 MCP ワイヤー形状を明確化(エンベロープとボディフィールドがルートに、ネストされた `payload:` キーなし)。事実上のフラット形状を実装した採用者は影響を受けません。 * **冪等性リプレイは歴史的スナップショットを返す**(#4371)。バイヤーがリプレイウィンドウ内でステートフルな create 呼び出し(例: `create_media_buy`)をリトライするとき、セラーは状態追跡フィールド(`status`、`confirmed_at` など)の **歴史的スナップショット** を返さなければなりません(MUST) — 現在の状態ではなく。そうでなければ at-most-once リトライがバイヤーの下からレスポンスを変異させます。 * **`refine[]` finalize 排他性 + マルチ finalize アトミック性**(#4107)。`get_products` `refine[]` セマンティクスの厳格化: いずれかのエントリが `action: "finalize"` を使うとき、配列のすべてのエントリは `action: "finalize"` でプロポーザルスコープでなければなりません(MUST)。セラーは finalize と非 finalize の混合を `INVALID_REQUEST` で拒否します。複数のプロポーザルにわたるマルチ finalize は、セラーがすべての名前付きプロポーザルにわたってアトミックコミットを保証できるときのみ許されます。そのアトミック性を保証できないセラーは、マルチ finalize 配列を `MULTI_FINALIZE_UNSUPPORTED`(推奨)または `INVALID_REQUEST` で拒否しなければならず(MUST)、バイヤーは緩いコミット保証を受け入れるなら単一プロポーザル finalize 呼び出しをシーケンスできます。 * **`pending_creatives` ステータスの曖昧性解消**(#4196)。説明は今やバイヤーアクションが必要と明示的に述べます — クリエイティブ同期を待つセラーは、ステータス enum 値を発するだけでなく、レスポンスメッセージで何が欠けているか(クリエイティブ数、締め切り)をサーフェスしなければなりません(MUST)。ワイヤー形状を変えずに採用者向け UX を明確化。 * **runner-output-contract の `notices` 助言チャネル**(#4418)。ストーリーボードランナーは、非失敗の助言(例: 「エージェントはまだ非推奨の専門分野をアドバタイズするが、ストーリーボードは合格した」)を実行出力の構造化された `notices[]` フィールドを通じてサーフェスします。助言テキストを運ぶ場当たり的な `skip.detail` 散文 — グレーダーとダッシュボードにパース不能 — を置き換えます。 * **ガバナンスボディレベル `status` のリネーム**(#4897)。`check_governance` レスポンス: `status` → `verdict`(enum 変更なし: `approved` / `denied` / `conditions`)。`report_plan_outcome` レスポンス: `status` → `outcome_state`(enum 変更なし: `accepted` / `findings`)。`get_plan_audit_logs` エントリがカスケード: 一貫性のため `entries[].status` → `entries[].verdict`。MCP フラットオンザワイヤーシリアライゼーション下でエンベロープタスクステータスのためにトップレベル `status` キーを解放(#4876、#2911)。移行: これら 3 つのレスポンス形状のすべてのエミッターとコンシューマーでプロパティをリネーム。値は変わらない。ガバナンスは `x-status` により実験的サーフェスなので、これは GA に先立つ認可された 3.1 ワイヤー形状調整。 * **メディアバイボディレベル `status` 衝突 — additive-deprecate**(#4895)。`create_media_buy` と `update_media_buy` の成功レスポンスが新しいトップレベル `media_buy_status` フィールドを得ます。レガシートップレベル `status: MediaBuyStatus` 形式は `deprecated: true` とマークされ **3.2**(#4906)で削除。コンプライアンスストーリーボードは既に新しいフィールドを要求します。`get-media-buys-response`、`get-media-buy-delivery-response`、`core/media-buy.json` のネストされた `status` はここではスコープ外で、**4.0** カスケード(#4905)で対処。完全な移行: [Migration › `media_buy_status`](/docs/reference/migration/media-buy-status)。 * **プロポーザルライフサイクル、シグナルプライバシーメタデータ、測定ロック。** `proposal_status` はプロポーザルごとの真実の源泉、`supports_proposals` は適合性グレーディング宣言、`finalize` はセラーコミットメント、`create_media_buy(proposal_id)` はバイヤー実行。GA 前の `requires_proposal` アクションモードは、見積もりエンベロープ外の更新のための `REQUOTE_REQUIRED` を優先して削除。`requires_proposal` を含むキャッシュされたプレリリースアクションメタデータを持つバイヤーは、それを無効化し該当するプロダクトまたはバイのサーフェスを再読み取りしなければならない。シグナル定義は Global Privacy Control サポートを宣言せず、`get_signals` 行の投影された `consent_basis` / `art9_basis` 値はプロバイダー宣言のシグナル定義姿勢のまま。測定カタログは実験的のままで、それを実装するエージェントは `experimental_features` に `measurement.core` を宣言。 → 完全なバッチは 1 コミットとして出荷: PR [#4796](https://github.com/adcontextprotocol/adcp/pull/4796)(`4c124545f1`)。完全な散文については issue ごとのリンクを参照。 ### その他のスキーマ追加 **`media_buy.frequency_capping` ケイパビリティ宣言**(#4670) — セラーがどの frequency-cap サーフェスを尊重するかを宣言。 **SDK 生成の人間工学**(#5168) — 一般的なインラインオブジェクトと配列アイテムの形状が今や安定したコアスキーマ名を持つため、SDK はローカルラッパー名を発明しません。`x-adcp-open-payload` は、オープンマップを閉じた型付きモデルに折り畳むのではなく保持する必要のあるジェネレーターのために、意図的にオープンな JSON ペイロードフィールドをマーク。 **オープンコンプライアンスシナリオ文字列**(#5168) — `comply_test_controller.scenario`、`list_scenarios.scenarios[]`、`compliance_testing.scenarios[]` は閉じた enum ではなく `string`。SDK はそれらを文字列としてパースすべきで(SHOULD)、既知値ヘルパーをローカルで重ねてもよい(MAY)。以前これらのフィールドにリテラル共用体を生成した SDK は `string` に広げ、未知のシナリオ名のデフォルト処理を保つべき。 **`x-adcp-hoist` オプトインマーカー**(#4630) — 正準に共有されるオブジェクトスキーマが、共有型に引き上げ可能として自身を宣言。**text-asset-requirements の `allowed_values`**(#4333) — 閉集合テキストアセット(CTA など)が、バイヤーが生成を制約できるよう許可される値を宣言。**`vast_tracker` + `daast_tracker` アセットタイプ**(#3051) — 動画と音声のトラッカーアセット。**`create_media_buy` / `update_media_buy` 成功レスポンスの任意 `currency` + `total_budget`**(#4417)。**`sync_audiences` への非同期エンベロープ**(#4571) — create スタイルタスクから拡張された 3 形状 submitted エンベロープ(Success / Error / Submitted)。 → PR ごとの詳細については [リリースノート § Version 3.1.0](/docs/reference/release-notes#version-3-1-0) を参照。 ## 採用者のアクション | もしあなたが… | すべきこと | | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 3.0 準拠の本番エージェント | 3.0 に留まるために必要なものはなし — 3.1 変更は加算的で 3.0 クライアントは動作し続ける。3.1 を主張する準備ができたら、新しいフィールドを拾い将来のマイナーとの前方互換のため `adcp_version` を発するよう SDK ピンを `"3.1"` に上げる。 | | 本番キャンペーンを実行するバイヤー | SDK を上げ、セラーが `supported_versions` で `"3.1"` をアドバタイズした後、構築時に `adcpVersion: "3.1"` を渡す。発火漏れを疑うとき `webhook_activity[]` 読み取りを実装。すべての `get_media_buys` ポーリングで `media_buy.health` + `impairments[]` を読む。再照合パイプラインで `impairment.coherence` 不変条件を実装。 | | 本番バイを実行するセラー | 参照されたリソースがオフラインに遷移するたびに `media_buy.health` + `impairments[]` をサーフェス。`capabilities.media_buy.propagation_surfaces` を正直に宣言。バイヤーがオペレーターの往復なしに統合をエンドツーエンドでデバッグできるよう `webhook_activity[]` を実装 — バイヤーセルフサービスは統合摩擦の削減であり、セラーの慈善ではない。バイヤーが再照合すべきときを知るよう、すべての配信行に `is_final` をマーク。 | | money-moving 専門分野を主張するセラー | アカウントサーフェスに `sync_governance` を実装し、brand/operator アカウント参照を使ってアカウントごとに 1 つのガバナンスエージェントを登録し、複数の `governance_agents` エントリを持つ登録を拒否。`check_governance` 呼び出しは `governance-aware-seller` も主張するときのみ依然必須。 | | `verify_brand_claim` または `verify_brand_claims` を実装するブランドエージェント | すべての成功レスポンスで `signed_response` を返す。ブランドごとの `adcp_use: "response-signing"` JWK を公開し、未署名のレスポンスフィールドを `signed_response.payload.response` とバイト等価に保ち、元のリクエストと結果インデックスとともに一括監査証拠を保持。 | | プロポーザルまたはアクションディスカバリーを使うセラー | プロポーザル実行を `supports_proposals` やアクションモードではなく `proposal_status` からルーティング。`requires_proposal` を発しない。更新が見積もりエンベロープを超えバイヤーが条件を再発見するか別のバイを作らなければならないとき `REQUOTE_REQUIRED` を使う。 | | キャッシュされたプレリリース `action_mode: "requires_proposal"` 値を持つバイヤー | キャッシュされた値を未知として扱う。それを `requires_approval` にマップしない。そのモードは非同期の人間承認ゲートでプロポーザルアーティファクトを持たない。キャッシュされた値がプロポーザル実行可能性に使われた場合、`get_products` を通じてプロポーザルを再読み取りし `Proposal.proposal_status` で分岐: `draft` はまず finalize、`committed` は `create_media_buy(proposal_id)` の準備完了。プロダクト `allowed_actions[]` またはバイ `available_actions[]` から来た場合、そのキャッシュされたアクションメタデータを無効化し現在のプロダクトまたはバイのアクションサーフェスを再読み取り。 | | 以前 `data_subject_rights.gpc_honored` を見たプレリリース 3.1 シグナル採用者 | シグナルレベルの権利ルーティングからフィールドを削除。3.1 はシグナル定義で Global Privacy Control サポートを宣言しない。GPC は配信時のパブリッシャー/ビッドストリームの関心事のまま。実装ガイダンスにはプロバイダーポリシー、レジストリ開示、`ccpa_opt_out_url` のような CCPA/州法オプトアウトルーティングを使うが、`data_subject_rights` から GPC 処理を推論しない。 | | 自己公開権限が欲しいサブブランドチーム | 自身のドメインに Brand Canonical Document として `/.well-known/brand.json` を立てる。`house_domain: ""` を宣言。親ハウスチームに `brand_refs[]` 経由で相互に応じるよう依頼。 | | ホールセールプロダクトフィードとホールセールシグナルフィードのミラーを維持するコンシューマー(ストアフロント、連合マーケットプレイス、レジストリ) | `get_products buying_mode: "wholesale"` および/または `get_signals discovery_mode: "wholesale"` 経由でブートストラップ。返された `wholesale_feed_version` + `cache_scope` を永続化。後続のポーリングで `if_wholesale_feed_version` を送り、セラーが `unchanged: true` で応答するとき完全ペイロードをスキップ。エージェントが `wholesale_feed_webhooks.supported` を宣言する場合、`sync_accounts.accounts[].notification_configs[]` を通じて変更 webhook を登録。webhook ペイロードをミラーに適用し `applies_to.scope` を追跡して正しいキャッシュ層(public 対 account オーバーレイ)を無効化。`wholesale_feed.bulk_change` または逃したプッシュを `get_products` / `get_signals` の再読み取りで処理。 | | シグナルエージェント | ミラーリングのため完全な価格付きシグナルフィードを公開するよう `signals.discovery_modes: ["brief", "wholesale"]` を宣言。すべてのレスポンスで `cache_scope` を返す(必須 — スキーマ強制)。`discovery_mode` なしの 3.1 以前の呼び出し元は brief モード動作を得続ける。`signal-owned` のみを主張する場合、`get_signals` ディスカバリーで十分。`activate_signal` も実装するときのみ `signal-marketplace` を主張。 | | スケールでホールセールフィードミラーを提供するセールス / シグナルエージェント | `wholesale_feed_webhooks.supported: true` を宣言し、標準のアカウントレベル webhook 署名、アクティベーション前のエンドポイント制御証明、SSRF ガード、アカウント/呼び出し元認可チェック、通知設定ファンアウト上限を伴い、`sync_accounts.accounts[].notification_configs[]` を通じた webhook 登録をサポート。`event_types[]` を宣言された修復読み取りと一貫させる: `product.*` はホールセール `get_products`、`signal.*` はホールセール `get_signals` を必要とする。webhook エミッターは、実際に変更されたプロダクト/シグナルペイロードまたは一括変更サマリーを含め、イベント発行時にホールセールタスクと同じ呼び出し元ごとのスコープフィルターを適用しなければならない(MUST) — プリンシパルごとに確実にイベントをスコープできないマルチテナントエージェントはケイパビリティを宣言してはならない(MUST NOT)。 | | 測定ベンダー(アテンション、ブランドリフト、排出、リテール) | 実験的 `measurement.metrics[]` カタログを AdCP エージェントで公開し `experimental_features: ["measurement.core"]` を宣言。プロダクトごとに `vendor_metric_optimization` を宣言するセラーは今や最適化目標をあなたの `(vendor, metric_id)` ペアにバインドできる。 | | クリエイティブエージェント | `capabilities.creative.bills_through_adcp` を正直に宣言。帯域外で課金する場合、黙って受け入れるのではなく `BILLING_OUT_OF_BAND` で `report_usage` 呼び出しを拒否。 | | トランスフォーマーを提供するクリエイティブエージェント | `creative.supports_transformers: true` を宣言し `list_transformers`(`expand_params` オプション列挙モードを含む)を提供。`build_creative` で型付き `config` を厳格に検証 — 未知のキーと範囲外の値をフィールド帰属エラーで拒否、ベンダーノブを `ext` にルーティング、各ターゲットフォーマットがトランスフォーマーの `output_format_ids` のサブセットであることを検証。カタログアイテムやバリアントにわたってファンアウトするときリーフごとの価格領収書を伴う `BuildCreativeVariantSuccess` を返す。レートカードを `Format.pricing_options` から `transformer.pricing_options` に移し `report_usage` を通じて決済。 | | ホスト音声/動画フォーマットを宣言するセラーまたはクリエイティブエージェント | 固定 duration スロットに `duration_ms_exact`、有界または片側範囲に `duration_ms_range` を使う。`[null, 60000]` は「最大 60 秒」、`[15000, null]` は「少なくとも 15 秒」、`[null, null]` は無効。別個の素の min/max duration フィールドを追加しない。 | | SDK フィクスチャ、コンプライアンスミラー、リリースパッケージングを保守 | コンプライアンスベクターと test-kit 参照をパッケージ化された `/compliance/{version}/` ツリー内に保つ。欠けているバンドル相対参照をリリースブロッカーとして扱う。 | | SDK 作者 | バンドルする公開された 3.1 アーティファクトに `published_version` をピン留め。`adcp_version`(リリース精度文字列)と `adcp_major_version`(整数ミラー)を発する。ワイヤー発行前に semver 値をリリース精度に正規化(`"3.1.0"` → `"3.1"`)。自動ダウンシフトではなく `VERSION_UNSUPPORTED` を型付きエラーとしてサーフェス。 | ## 移行 **結論: 破壊的変更なし。加算的のみ。上げるべき。** すべての 3.1 変更は 3.0 に対して **加算的** です。新しいフィールドは任意で、必須フィールドは削除されず、3.0 準拠クライアントを壊す方法で形状が変わったものはありません。SDK をアップグレードせずに 3.1 セラーに対して実行するバイヤーは動作し続けます — 新しいフィールドが見えないだけです。3.1 バイヤーに対して 3.0 スキーマを実行するセラーは動作し続けます — バイヤーの新しいフィールドは黙って無視されます。 しかし新しいサーフェスは実際の本番問題を解決し、3.0 に留まるほど、3.1 が追加した本番堅牢化なしに運用することになります: webhook 配信デバッグ、依存関係影響の可観測性、課金確定フラグ、アクションディスカバリー、ベンダー証明測定、リリース精度ネゴシエーション。**SDK が準備でき次第上げてください。** 唯一のパブリッシャー可視の動作変更は `brand.json` `trademarks[]` にあります: 自由テキストの `status` / `countries` 値は今や型付き enum / ISO 3166-1 alpha-2 に対して検証されます — 非準拠の値はスキーマエラーとしてサーフェスします。`trademarks[]` が制限のない自由テキストを公開していた場合、3.1 を主張する前に値を正規化してください。 プレリリース 3.1 採用者はプレリリースのみの統合も更新すべきです: ブランド検証成功レスポンスは今や `signed_response` を要求。プロポーザル/アクションコードは一時的な `requires_proposal` アクションモードを削除し更新の再価格設定に `REQUOTE_REQUIRED` を使う必要がある。`requires_proposal` 値をキャッシュしたバイヤーは、それらを `requires_approval` にマップするのではなく該当するプロポーザル、プロダクト、バイのサーフェスを無効化し再読み取りしなければならない。シグナル定義は Global Privacy Control サポートを宣言しない。`signal-owned` 適合性はディスカバリーのみで `signal-marketplace` はアクティベーションクレームのまま。ホスト音声/動画宣言は別個の素の min/max フィールドではなく片側 `duration_ms_range` を使うべき。測定カタログディスカバリーは `measurement.core` の背後で実験的のまま。これらは GA 前のプレリリースクリーンアップ項目で、3.0 破壊的変更ではありません。 **3.1 SDK の検証器義務。** ホールセールフィードミラーリング作業は 3.1 内で 1 つの形状を厳格化します: `cache_scope` はすべての `get_products` / `get_signals` レスポンスでスキーマ必須です(2 層キャッシュの安全プロパティがそれに依存 — [キャッシュ層](/docs/media-buy/task-reference/get_products#cache-layering) を参照)。3.1 以前のセラーはフィールドを正しく省略し、宣言されたバージョンに準拠したままです。3.1 スキーマに対して厳格に検証する SDK は、サーバー宣言の `adcp_version`(3.1 がバージョンネゴシエーションで出荷するのと同じリリース精度メカニズム)に基づいて検証器を選択しなければなりません(MUST): `adcp_version` が `3.0` で始まるレスポンスについては、3.1 の `cache_scope` 必須制約を緩和しなければなりません(MUST)。これは 3.1 内の厳格化であり 3.0 の破壊ではありません — が、バージョンピン留め検証なしに 3.1 スキーマをハードコードする SDK は正しい 3.0 トラフィックを拒否します。バージョンピン留め検証はすべての 3.x→3.(x+1) 厳格化の正しいパターンです。cache\_scope はそれが負荷を担う最初のケースです。 PR ごとの詳細については [リリースノート § Version 3.1.0](/docs/reference/release-notes#version-3-1-0) を参照。バージョンネゴシエーションケイデンスと 3.1 → 3.2 → 4.0 タイムラインについては [バージョニングとガバナンス § Migration timeline](/docs/reference/versioning#migration-timeline) を参照。 # AdCP 3.0 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/whats-new-in-v3 AdCP 3.0 の新機能: ブランドアイデンティティと権利、クリエイティブワークフローのアップグレード、ガバナンス、スポンサードインテリジェンス、番組とエピソード、20のメディアチャンネル、v2 からの移行ガイド。 AdCP 3.0 はプロトコルをメディアバイを超えて、ブランドアイデンティティ、ガバナンス、メディアプランニング、会話型ブランド体験まで拡張します。 ## 一覧 | エリア | v2.x | v3.x | | ------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | **プロトコルスコープ** | メディアバイ、シグナル、クリエイティブ | ブランドプロトコル、ガバナンス、スポンサードインテリジェンスを追加 | | **ブランドアイデンティティ** | 標準的なメカニズムなし | `brand.json` + コミュニティブランドレジストリ | | **ガバナンス** | ブランドスーツアビリティプロトコルなし | プロパティリスト、コンテンツスタンダード、キャンペーンガバナンス、ポリシーレジストリ、ブランドキャリブレーション | | **スポンサードインテリジェンス** | 会話型ブランドプロトコルなし | AI アシスタントでの同意優先ブランドセッション | | **アカウントプロトコル** | 正式なアカウントモデルなし | 名前付きプロトコルレイヤー: `sync_accounts`、`list_accounts`、`report_usage`、ブランドレジストリに基づくアイデンティティ | | **カタログ** | `promoted_offerings` クリエイティブアセット | 13のカタログタイプを持つファーストクラスの `sync_catalogs` タスク | | **メディアプランニング** | プロダクトのみ | 予算配分 + デリバリー予測を持つプロポーザル | | **ブランド権利** | ライセンスプロトコルなし | HMAC 認証 Webhook を持つ `get_rights`、`acquire_rights`、`update_rights` | | **ビジュアルガイドライン** | 構造化されたブランドビジュアルなし | ジェネラティブクリエイティブシステム向け `brand.json` の `visual_guidelines` | | **クリエイティブワークフロー** | 基本的なビルド / プレビュー / シンクの分離 | インラインプレビュー、マルチフォーマット `build_creative`、ライブラリ取得、品質ティア、カタログ `item_limit` | | **クリエイティブライブラリ** | メディアバイ中心のクリエイティブ読み書き | `list_creatives` / `sync_creatives` をクリエイティブプロトコル操作として、`supports_generation`、`supports_transformation`、`has_creative_library` の探索も追加 | | **クリエイティブガバナンス** | クリエイティブ評価プロトコルなし | セキュリティスキャン、品質、コンテンツ分類のための `get_creative_features` | | **ディスクロージャーマッチング** | 位置のみのディスクロージャー処理 | 位置 + 持続性を考慮したディスクロージャーマッチング(`continuous`、`initial`、`flexible`) | | **番組とエピソード** | コンテンツプログラミングモデルなし | 配信 ID、エピソードライフサイクル、ブレークベースのインベントリを持つプロダクトの `shows` | | **プランニングの利便性** | `time_budget`、`preferred_delivery_types`、`exclusivity`、パッケージレベルフライトなし | `time_budget` + `incomplete`、`preferred_delivery_types`、`exclusivity`、オプションの `delivery_measurement`、パッケージレベルの `start_time` / `end_time` | | **チャンネルモデル** | 9チャンネル | 20のプランニング指向チャンネル(`sponsored_intelligence` を含む) | | **ケイパビリティ探索** | エージェントカード拡張 | ランタイム `get_adcp_capabilities` タスク | | **サンドボックス探索** | 機能位置が混在 | 認証モデルに `require_operator_auth`、サンドボックスサポートに `account.sandbox` | | **クリエイティブアサインメント** | シンプルな ID 配列 | プレースメントターゲティング付きのウェイト付きアサインメント | | **ジオターゲティング** | 暗黙的な米国中心 | 明示的な名前付きシステム(グローバル) | | **キーワードターゲティング** | キーワードサポートなし | マッチタイプと入札価格を持つ `keyword_targets` | | **最適化** | 単一の最適化目標 | メトリックとイベントタイプを持つマルチゴール `optimization_goals` 配列 | | **デリバリーレポーティング** | 集計デリバリーのみ | オプトインのディメンションブレークダウン(ジオ、デバイス、オーディエンス、プレースメント、キーワード) | | **シグナル価格** | シンプルな CPM | 構造化された価格モデル(CPM、メディア費用の割合、固定料金) | | **デバイスターゲティング** | フォームファクターターゲティングなし | `device_platform`(OS)とは別の `device_type`(デスクトップ、モバイル、タブレット、ctv、dooh、unknown) | | **近接ターゲティング** | ポイントベースのジオターゲティングなし | 移動時間、半径、GeoJSON メソッドを持つ `geo_proximity` | | **リファインメント** | `proposal_id` を持つ自由テキスト | セラーの承認付きの型指定された変更リクエスト配列 | | **エラー処理** | 構造化されていないエラー | `recovery` フィールド(transient、correctable、terminal)+ 18の標準エラーコード | | **AI プロベナンス** | プロベナンスモデルなし | IPTC ソースタイプ、C2PA 参照、規制ディスクロージャーを持つ `provenance` オブジェクト | | **クリエイティブコンプライアンス** | ブリーフでのコンプライアンスなし | `required_disclosures`、`prohibited_claims`、ディスクロージャー位置 | | **エージェント利便性** | 毎回フルペイロード | `fields` プロジェクション、オプトインブレークダウン、プリフライトケイパビリティフィルタリング | | **シグナルライフサイクル** | アクティベートのみ | `activate_signal` での `activate` / `deactivate` アクション | *** ## 新機能 ### Trust surface: idempotency, request signing, and signed governance 3.0 は、3 つの運用規律を一級のプロトコルプリミティブに変えることで、エージェント間トランザクションを実際の資金に対して安全にします。 **`idempotency_key` はすべての変更リクエストで必須です。** バイヤーは論理操作ごとに新しいキーを生成します — スキーマは `^[A-Za-z0-9_.:-]{16,255}$` を要求し、AdCP Verified はさらに暗号学的にランダムな UUID v4 を要求します。セラーは `get_adcp_capabilities` で重複排除セマンティクスを `adcp.idempotency = { supported: true, replay_ttl_seconds: <1h–7d、24h 推奨> }` または `{ supported: false }` として宣言します。`supported: true` のとき、リプレイは安全です: セラーは厳密なリプレイで `replayed: true` を、同じキーが異なるペイロードを伴うとき `IDEMPOTENCY_CONFLICT` を、TTL 後に `IDEMPOTENCY_EXPIRED` を返します。**`supported: false` のとき、`idempotency_key` の送信は no-op です — 素朴なリトライは二重処理します** — バイヤーは支出コミット操作をリトライする前に自然キーチェック(例: `get_media_buys` に加え `context.internal_campaign_id` などのリクエストコンテキストや `context.buyer_ref` などのパッケージコンテキスト)を使わなければなりません(MUST)。クライアントはデフォルトを想定してはなりません(MUST NOT)。このブロックを欠くセラーは非準拠です。`supported: true` は信頼を担うクレームであるため、適合性ランナーは意図的なペイロード変異リプレイでこれをプローブします — サポートを主張するセラーは、宣言が検証済みと見なされる前にこのプローブに合格しなければなりません(MUST)。 **RFC 9421 HTTP Message Signatures は 3.0 では任意、AdCP Verified では必須です。** エージェントは、正規化されたカバードコンポーネントリスト(メソッド、ターゲット URI、`content-digest`、プロトコルレベルフィールド)に対して Ed25519 で変更リクエストに署名します。仕様は sf-binary エンコーディングと URL 正規化をピン留めするため、独立した実装がビット単位で同一の正規入力を生成します。15 ステップの検証チェックリストがセラーのパスを定義します: `alg` 許可リスト、`keyid` の暗号前上限(無制限検証に対する防御)、SSRF 検証済みフェッチによる JWKS 解決、`jti` リプレイ重複排除、オーディエンスバインディング。`static/compliance/source/test-vectors/request-signing/` の公開テストベクターにより、実装者はオフラインで正しさを検証できます。 **Webhook は同じ RFC 9421 プロファイルで署名され、セラーにベースライン必須です。** Webhook 認証は、リクエスト署名の対称バリアントとして AdCP 9421 プロファイルに統一されます: セラーは、`jwks_uri` の JWKS(`brand.json` の `agents[]` 経由で発見可能)に公開された鍵でアウトバウンド webhook リクエストに署名します。新しい署名者は webhook 配信に `adcp_use: "request-signing"` を使います。非推奨の `adcp_use: "webhook-signing"` 鍵は互換ウィンドウ中は受け入れられ続けます。webhook 専用の鍵素材を望むオペレーターは別個の `request-signing` `kid` を公開します。共有シークレットはワイヤーを越えません。バイヤーはセラーの JWKS を使って署名を検証します。14 ステップの webhook 検証者チェックリスト — [セキュリティガイド](/docs/building/by-layer/L1/security) で文書化 — は、信頼アンカーのスコープ、ダウングレードとインジェクションの耐性、keyid ごとのリプレイ重複排除(keyid あたり 10 万、集計 1000 万)をカバーします。検証失敗はそこで定義される型付き理由コードを返します。HMAC-SHA256 は 3.x を通じてレガシーフォールバックのままです(`push_notification_config.authentication.credentials` 経由でオプトイン)。`authentication` オブジェクト全体は 4.0 で削除されます。 **すべての webhook ペイロードは必須の `idempotency_key` を運びます。** Webhook は at-least-once 配信を使うため、受信者は重複排除しなければなりません。すべての webhook ペイロード — MCP、コレクションリスト変更、プロパティリスト変更、コンテンツ標準アーティファクト、権利失効 — は、同じイベントのリトライ間で安定した、送信者生成の暗号学的にランダムな UUID v4 `idempotency_key` を運びます。リクエスト側フィールドと同じ名前と形式です。予測可能なキーは受信者の重複排除キャッシュに事前シードして正当なイベントを抑制することを許すため、セラーは暗号ソースからキーを生成しなければなりません(MUST)。 **`governance_context` は署名付き JWS です。** ガバナンスエージェントがプランを承認するとき、不透明な文字列ではなく、ガバナンスエージェントの鍵で署名された JWS を返します。バイヤーはそれをメディアバイエンベロープでエコーします。セラーはガバナンスエージェントの JWKS(`sync_governance` 経由で解決)を使って署名を検証し、ラウンドトリップなしに決定を特定のバイヤー、プラン、フェーズ、時間にバインドします。古いまたは偽造された決定はトランスポート層で拒否されます。プランにガバナンスエージェントが設定されている場合、セラーは予算をコミットする前に `check_governance` を呼び出さなければならず(MUST)、有効な `governance_context` を欠く支出コミットを `PERMISSION_DENIED` で拒否しなければなりません(MUST)。 **コンプライアンスランナーがこれらすべてを検証します。** すべてのエージェントは、主張するプロトコルや専門分野に関係なく `/compliance/{version}/universal/security.yaml` を実行します — 未認証の拒否、API キーの強制、RFC 9728 に従う OAuth ディスカバリー、オーディエンスバインディングをカバー。署名を宣言するエージェントは `signed_requests` と `signed_webhooks` のハーネスを実行します: 正常フロー、改ざん(ヘッダーインジェクション、ボディ変異、タイムスタンプスキュー)、リプレイ(`jti` 再利用)、`keyid` 暗号前上限パス。ランナー出力は、改ざんが検出可能になるようテストキットコーパスに対するハッシュチェーンを持つ、構造化された検証可能な `runner-output.json` アーティファクトです。 **インスタンス間の状態永続化が仕様要件になりました。** エージェントの状態 — タスク、メディアバイ、プラン、署名付きアーティファクト、冪等性キー — は、水平スケールされたインスタンス間で永続的でなければなりません(MUST)。メモリのみの状態は本番で非準拠です。 完全な脅威モデル、プリンシパルの役割(ブランド / オペレーター / エージェント)、ステップバイステップの検証パスについては [セキュリティ実装ガイド](/docs/building/by-layer/L1/security) を参照。 脅威モデル、署名プロファイル、検証パス、ユニバーサルセキュリティストーリーボード。 *** ### Specialisms and storyboard-driven compliance ストーリーボード — エージェントが合格しなければならないスクリプト化されたコンプライアンスシナリオ — は、スキーマとタスク定義とともに `/compliance/{version}/` のプロトコル内に存在するようになりました。エージェントは `get_adcp_capabilities` で 2 つを宣言します。 * **`supported_protocols`** — 広範なドメインクレーム(`media_buy`、`creative`、`signals`、`governance`、`brand`、`sponsored_intelligence`)。それぞれエージェントをドメインのベースラインストーリーボードにコミットします。 * **`specialisms`** — 6 ドメインにわたる 19 の狭い機能クレーム。例: `sales-guaranteed`、`sales-broadcast-tv`、`creative-generative`、`property-lists`、`signal-marketplace`、`brand-rights`。それぞれ 1 つの親プロトコルにロールアップします。 コンプライアンス実行には 3 つの階層があります: すべてのエージェントが実行する**ユニバーサル**ストーリーボード(機能ディスカバリー、スキーマ検証、エラーコンプライアンス)、宣言された各プロトコルの**ドメインベースライン**、各狭いクレームの**専門分野ストーリーボード**。`/protocol/{version}.tgz` のバージョンごとのプロトコル tarball により、クライアントはスキーマ、ストーリーボード、例を 1 回のリクエストで一括同期できます。 **AdCP Verified は 3.0 では自己証明です。** エージェントはストーリーボードスイートを実行し、テストキットコーパスに対するハッシュチェーンを持つ署名付き `runner-output.json` を公開します。AAO は出力を監査せず、発行をゲートしません — Verified スタンプは「このエージェントは合格したランナー出力を公開した」を意味し、「監査人がクレームを確認した」ではありません。検証する当事者(バイヤー、インテグレーター、規制当局)は、参照されるストーリーボードに対して任意のクレームを再実行し、出力ハッシュを比較できます。 コンプライアンスランナーとストーリーボード自体は 3.0 で出荷されるソフトウェアです — バグ、カバレッジのギャップがあり、いくつかの場所ではワーキンググループがまだ正確なルールを洗練している仕様の意図についての最善の推測をエンコードしています。実装者がストーリーボードの失敗を見たとき、3 つの可能性があります: エージェントにバグがある、ストーリーボードにバグがある、または仕様が曖昧である。3 つすべてが提起する正当な問題です。 **なぜ 3.0 で自己証明で監査ではないか:** AAO が運営する参照実装 — トレーニングエージェントと `@adcp/sdk` / Python / Go SDK — はまだ 3.0 ストーリーボードスイートに対して完全にクリーンではありません。トレーニングエージェントは現在、適用可能な 55 のストーリーボードのうち 32 に合格しています。SDK のカバレッジも同様です。私たちは 3.0 → 3.1 のウィンドウで 4〜6 週間のケイデンスでこれらの合格率を 100% に近づけています。参照実装がクリーンに合格し、残りの仕様の曖昧さが解決されたとき、**正式な AdCP Verified プログラムが 3.1 で開始します** — エージェントは AAO による独立した再実行のためにランナー出力を提出でき、検証済みエージェントのパブリックレジストリを備えます。3.0 での自己証明は橋であり、最終状態ではありません。 ステータスフラグとストーリーボードソースを持つドメインと専門分野の完全なインデックス。 *** ### ブランドプロトコル `/.well-known/brand.json` によるバイサイドアイデンティティ。パブリッシャーが `adagents.json` でプロパティと認証済みエージェントを宣言するのと同様に、ブランドは `brand.json` でアイデンティティ、ブランド階層、認証済みオペレーターを宣言します。 | セルサイド | バイサイド | | --------------- | -------------------- | | パブリッシャー | **ハウス**(企業エンティティ) | | プロパティ | **ブランド**(広告アイデンティティ) | | `adagents.json` | **`brand.json`** | 4つのバリアント: **House Portfolio**(完全なブランド階層をインライン)、**Brand Agent**(MCP 経由で動的)、**House Redirect**(サブブランドからハウスドメインへ)、**Authoritative Location**(ホストされた URL)。 任意のドメインが与えられると、プロトコルは正規ブランドに解決する: ``` shoes.novabrands.example.com -> fetch /.well-known/brand.json -> { "house": "novabrands.example.com" } -> fetch novabrands.example.com/.well-known/brand.json -> search brands[] for property matching "shoes.novabrands.example.com" -> Result: { house: "novabrands.example.com", brand_id: "nova_athletics" } ``` **ユースケース:** クリエイティブ生成(ドメインをブランドアイデンティティに解決)、ブランド検証(`authorized_operators` を確認)、レポーティングロールアップ(キャンペーンをハウスでグループ化)。 brand.json バリアント、解決フロー、ブランドアイデンティティを含む完全な仕様。 *** ### ブランド権利ライフサイクル ブランドとコンテンツオーナー間のライセンスと使用権のための3つのタスク: | タスク | 目的 | | ---------------- | ------------------------- | | `get_rights` | ブランドのコンテンツで利用可能な権利を発見する | | `acquire_rights` | 権利取得を要求・交渉する | | `update_rights` | アクティブな権利を変更する(延長、制限、取り消し) | 権利にはジェネレーション資格情報(ライセンスされたコンテンツにアクセスするための API キーまたはトークン)、クリエイティブ承認 Webhook(クリエイティブがレビューに提出されたときの HMAC-SHA256 認証コールバック)、取り消し通知が含まれます。プロトコルは実行可能な拒否(修正して再送信)と最終的な拒否(再試行しない)を区別します。 `brand.json` の構造化された `visual_guidelines` は権利を補完し、ジェネラティブクリエイティブシステムに写真スタイル、グラフィック要素、構成、モーション、ロゴ配置、カラーウェイ、タイプスケール、制限のための構造化ルールを提供します。 権利の発見、取得、管理のタスクリファレンス。 *** ### 番組とエピソード プロダクトはポッドキャスト、TV シリーズ、YouTube チャンネルなどの永続的なコンテンツプログラムを `shows` で参照できるようになりました。`shows` は `publisher_properties` と同じパブリッシャースコープのセレクターパターンに従う。番組はパブリッシャーの `adagents.json` で宣言され、プロダクトはパブリッシャードメインと番組 ID で参照します。バイヤーは `adagents.json` から完全な番組オブジェクトを解決します。番組にはクロスセラーマッチングのための配信識別子、エピソードライフサイクル状態(scheduled、tentative、live、postponed、cancelled、aired、published)、ブレークベースの広告インベントリ設定、`brand.json` へのタレントリンク、国際コンテンツレーティングシステムが含まれます。 番組は関係(スピンオフ、コンパニオン、続編、前編、クロスオーバー)と派生コンテンツ(クリップ、ハイライト、リキャップ)をサポートし、包括的なコンテンツモデリングを実現します。 番組スキーマ、エピソードライフサイクル、ブレークベースのインベントリを含む完全な仕様。 *** ### レジストリ API AgenticAdvertising.org レジストリはブランドとプロパティの解決、エージェント探索、認証検証のためのパブリック REST API を提供します。ほとんどのエンドポイントは認証不要です。 | ケイパビリティ | エンドポイント | 説明 | | ---------- | ----------------------------------------------- | --------------------------- | | ブランド解決 | `/api/brands/resolve` | ドメインを正規ブランドに解決する | | プロパティ解決 | `/api/properties/resolve` | パブリッシャードメインをプロパティ情報に解決する | | エージェント探索 | `/api/registry/agents` | ケイパビリティを持つ登録エージェントをリストする | | 認証チェック | `/api/registry/validate/property-authorization` | リアルタイムの認証検証 | | 検索 | `/api/search` | ブランド、パブリッシャー、プロパティを横断して検索する | | コミュニティブランド | `/api/brands/save` | ブランドデータを提供する(認証必要) | レジストリはプロトコルを補完する: REST API でエンティティを解決し、MCP/A2A タスクでトランザクションを実行します。 認証とレート制限を含む完全なエンドポイントリファレンス。 *** ### プロポーザルとデリバリー予測 パブリッシャーはプロダクトと共に**プロポーザル**(バイヤーが `create_media_buy` で直接実行できるパーセンテージベースの予算配分を持つ構造化メディアプラン)を返せる。プロポーザルはパブリッシャーの専門知識を体現し、アドホックなプロダクトリストを実行可能な購買戦略に置き換える。セッション継続性によりリファインメントができる — 同じセッション内での後続の `get_products` 呼び出しが会話履歴を引き継ぐ。 **デリバリー予測**はプロポーザルと配分に付属します。各予測には支出が増えるにつれてデリバリーがどのようにスケールするかを示すメトリック範囲(low/mid/high)を持つ予算ポイントが含まれます。3つの予測メソッド: **`estimate`**(大まかな近似)、**`modeled`**(予測モデル)、**`guaranteed`**(契約でコミット済み)。予測はデリバリーメトリック(インプレッション、リーチ、GRP)と成果(購入、リード、アプリインストール)を予測できます。TV とラジオの予測は GRP ベースのプランニングのために `demographic_system` と `demographic` を使用します。 予算カーブ、CTV、リテールメディア、放送オーディオの例を含む完全なドキュメント。 *** ### アカウント **v2 からの移行?** アカウントは v3 で完全に新しく、移行元の v2 に相当するものはない。セットアップガイドとして[アカウントとエージェント](/docs/building/by-layer/L2/accounts-and-agents)を参照。 `sync_accounts` によるバイヤーとセラー間の正式な請求関係。 **4つのエンティティ:** | エンティティ | 問い | 識別方法 | | ---------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------- | | **ブランド** | 誰の製品を広告するか? | `brand.json` 経由のハウスドメイン + brand\_id | | **アカウント** | 誰が請求されるか? | [アカウント参照](/docs/building/by-layer/L2/accounts-and-agents#account-references) — `list_accounts` からの `account_id` または自然キー | | **オペレーター** | 誰がブランドの代わりに操作するか? | ドメイン(例: `acmeagency.example.com`) | | **エージェント** | どのソフトウェアが購買を行うか? | 認証済みセッション | **2つの請求モデル:** `operator`(オペレーターまたはブランドが直接購買して請求されます)と `agent`(エージェントが請求を統合)。**2つの信頼モデル:** エージェント信頼(デフォルト、エージェントがブランド/オペレーターを宣言)とオペレータースコープ(セラーがオペレーターレベルの資格情報を要求)。 **ワークフロー:** `get_adcp_capabilities` -> `sync_accounts` -> `account` 付きの `get_products` -> `account` 付きの `create_media_buy`。 アカウントプロトコルの概要: アイデンティティ検証、請求モデル、決済。 *** ### カタログ `sync_catalogs` によるファーストクラスのカタログライフサイクル。13のカタログタイプ: 構造的(`offering`、`product`、`inventory`、`store`、`promotion`)と業界垂直型(`hotel`、`flight`、`job`、`vehicle`、`real_estate`、`education`、`destination`、`app`)。垂直型には Google Ads、Meta、LinkedIn、Microsoft フィード仕様から引用した正規のアイテムスキーマがあります。 フォーマットは必要なカタログを `catalog_requirements` で宣言します。クリエイティブはアセットにアイテムを埋め込む代わりに、`catalog_id` で同期されたカタログを参照します。カタログはアトリビューション整合のために `conversion_events` と `content_id_type` を宣言します。 カタログタイプ、同期ワークフロー、フォーマット要件、コンバージョンイベントを含む完全なドキュメント。 *** ### ケイパビリティ探索 `get_adcp_capabilities` は `adcp-extension.json` と MCP エージェントカードの両方をランタイムケイパビリティ探索に置き換える。サポートされるプロトコル、アカウント請求モデル、ポートフォリオ情報、ターゲティングシステム、ガバナンス機能を返す — すべてスキーマ検証済み。 **エージェントカードと `adcp-extension.json` はもはや不要です。** バイヤーは `adagents.json` でセラーを発見し、ランタイムに `get_adcp_capabilities` を呼び出す。エージェントカード拡張からケイパビリティデータを読んでいる v2 インテグレーションがある場合は、`get_adcp_capabilities` に切り替えること。 リクエスト/レスポンススキーマを含む完全なタスクリファレンス。 *** ### ガバナンスプロトコル ブランドスーツアビリティとインベントリキュレーション。ガバナンスエージェントは**プロパティリスト**(ターゲティングまたは除外のためのプロパティのキュレートされたセット)と**コンテンツスタンダード**(カテゴリごとのブロック/許可ルールを持つブランドスーツアビリティポリシー)を管理します。バイヤーはフィルタリングされたインベントリ探索のために `get_products` にプロパティリストを渡し、ブランドとガバナンスエージェント間の共同調整のために `calibrate_content` を使用します。ガバナンスエージェントはクリエイティブポリシーで `provenance_required` を強制でき、プロベナンスクレームの `verification` 配列を介したサードパーティ AI コンテンツ検証をサポートします。 キャンペーンガバナンスは `sync_plans`、`check_governance`、`report_plan_outcome`、`get_plan_audit_logs` によるプランレベルのポリシーと予算執行でこれを拡張します。ガバナンスエージェントは `audit`、`advisory`、`enforce` モードで動作でき、委譲された権限に対してセラーサイドのアクションを検証し、共有[ポリシーレジストリ](/docs/governance/policy-registry)を通じて標準化されたポリシーを解決します。 プロパティリスト、コンテンツスタンダード、キャリブレーションを含む完全な仕様。 *** ### スポンサードインテリジェンスプロトコル AI アシスタントでの会話型ブランド体験。SI は AI アシスタントが会話の流れを断ち切ることなく、リッチなエンゲージメント(テキスト、音声、UI コンポーネント)のためにブランドエージェントをどのように呼び出すかを定義します。セッションは同意優先モデルに従う: ユーザーが興味を表明し、同意を付与すると、ブランドエージェントがオプションのトランザクション引き渡しとともに会話的にエンゲージします。 セッションライフサイクル、エージェントの実装、ホストの実装を含む完全な仕様。 *** ### データプロバイダーのシグナルカタログ データプロバイダーはパブリッシャーがプロパティを宣言するのと同じパターンに従い、`adagents.json` でシグナルカタログを公開できます。 | パブリッシャー | データプロバイダー | | ------------------------------------ | ---------------------------------- | | **プロパティ**を宣言する | **シグナル**を宣言する | | `property_ids` / `property_tags` を使用 | `signal_ids` / `signal_tags` を使用 | | バイヤーは `publisher_domain` で検証する | バイヤーは `data_provider_domain` で検証する | シグナルは型付きターゲティングを持つ明示的な `value_type`(バイナリ、カテゴリ、数値)と、データプロバイダーのカタログを参照する構造化された `signal_id` オブジェクトを持ちます。 シグナルカタログ公開のための完全な実装ガイド。 *** ## rc.1 から rc.2 のハイライト このページは v2 → v3 の全シフトをカバーします。すでに `3.0.0-rc.1` を採用している場合、アップグレード前に確認すべき最も重要な rc.2 の変更点は以下のとおり。 ### クリエイティブワークフローとライブラリの変更 `build_creative` はインラインプレビュー(`include_preview`)、マルチフォーマット出力(`target_format_ids`)、品質ティア、カタログ駆動の `item_limit`、オプションの `concept_id`、`media_buy_id`、`package_id`、`macro_values` を持つ `creative_id` を使ったライブラリ取得をサポートします。`preview_creative` も品質コントロールを追加します。クリエイティブライブラリ操作は明示的にクリエイティブプロトコルタスクになった: `list_creatives` と `sync_creatives` はクリエイティブライフサイクルの残りとともにあり、ケイパビリティ探索はバイヤーが意図的にリクエストをルーティングできるように `supports_generation`、`supports_transformation`、`has_creative_library` を追加します。 ### プランニング、アカウント、サンドボックスの改良 `account_resolution` が削除され、バイヤーは `require_operator_auth` を使って認証とアカウントモデルを判定します。サンドボックスサポートは `account.sandbox` に移動し、サンドボックスは暗黙的なアカウントフローの自然なアカウントキーに参加できます。プロダクト探索は `preferred_delivery_types`、`exclusivity`、`time_budget` を追加し、セラーがリクエストされた予算内で完了できない場合はレスポンスに `incomplete` を返します。パッケージとプロダクト配分はパッケージレベルの `start_time` / `end_time` を持てるようになり、`delivery_measurement` はプロダクトでオプションになりました。 ### コンプライアンスとガバナンスの改良 クリエイティブコンプライアンスに位置と期間に加えてディスクロージャー持続性のセマンティクスが含まれ、フォーマットが強制できる持続性モードを宣言できるようになりました。キャンペーンガバナンスも rc.2 で `sync_plans`、`check_governance`、`report_plan_outcome`、`get_plan_audit_logs`、正規のプラン抽出のための `governance_context` とともに追加されました。 網羅的な rc.2 の変更リストは[リリースノート](/docs/reference/release-notes#version-300-rc2)と [CHANGELOG.md](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#300-rc2) を参照。 *** ## rc.3 to 3.0 highlights **rc.3 からアップグレードしますか?** 破壊的変更の表、変更前後の例、移行ステップについては [rc.3 → 3.0 プレリリースアップグレードノート](/docs/reference/migration/prerelease-upgrades) を参照。 ### Specialisms and compliance catalog ストーリーボードは、`get_adcp_capabilities` の新しい `specialisms` フィールドとともに `/compliance/{version}/` のプロトコル内に移動します。21 の専門分野が 6 ドメインにロールアップします。4 つの 3.1 アーキタイプが `status: preview` で出荷されます。[Compliance Catalog](/docs/building/compliance-catalog) を参照。リネーム: `broadcast-platform` → `sales-broadcast-tv`、`social-platform` → `sales-social`。マージ: `property-governance` + `collection-governance` → `inventory-lists`。昇格: `sponsored_intelligence` 専門分野 → 完全なプロトコル。 ### Capabilities model simplification 機能モデルは 3.0 で合理化されます: 冗長なブールゲートが削除されます。`get_adcp_capabilities` に `content_standards` オブジェクトが存在すれば、エージェントはコンテンツ標準をサポートします — 別個のブールは不要です。 `reporting_capabilities` はすべてのプロダクトで必須になりました。ジオ機能フィールドは型付き形状を保ちます: `geo_countries` と `geo_regions` はブール、`geo_metros` と `geo_postal_areas` は構造化オブジェクト。 削除されたフィールドの完全なリストと移行ステップについては [プレリリースアップグレードノート](/docs/reference/migration/prerelease-upgrades#capabilities-model-simplification) を参照。 ### Governance across purchase types キャンペーンガバナンスはメディアバイを超えて、ブランド権利ライセンス、シグナル有効化、クリエイティブサービス — 予算やポリシールールが適用される任意の購入 — をカバーするよう拡張されます。`governance_context` が、キャンペーンのライフサイクル全体でガバナンスアクションを結び付ける識別子として `media_buy_id` を置き換えます。`check_governance` と `report_plan_outcome` の `purchase_type` フィールドが被管理アクティビティを区別します。 ### GOVERNANCE\_DENIED error and schema consistency `GOVERNANCE_DENIED` が、修正可能な回復を伴う標準エラーコードに追加され、ガバナンスで拒否された操作が構造化エラーを返せるようになります。ガバナンス、コレクション、プロパティ、スポンサードインテリジェンス、コンテンツ標準のプロトコルにわたるすべてのリクエスト/レスポンススキーマは、アプリケーションメタデータとプロトコル拡張のための任意の `context` と `ext` フィールドを得ます。`signal_id` は `get_signals` レスポンスのシグナルアイテムで必須になりました。`comply_test_controller` スキーマは、oneOf ユニオンから `scenario` 判別子を持つフラットオブジェクトにフラット化されます。 ### Per-request version declaration すべてのリクエストスキーマに `adcp_version`(リリース精度、例: `"3.1"`)が含まれるようになり、v3 バイヤーがペイロードがどのリリースに準拠するかを宣言できます。セラーは `get_adcp_capabilities` のアドバタイズされた `adcp.supported_versions` 配列に対して検証し、すべてのレスポンスでエンベロープルートに `adcp_version` をエコーします。サポートされないリリースは `VERSION_UNSUPPORTED` を返します。省略された場合、セラーは最高のサポートバージョンにデフォルトします。 3.1 は後方互換のレガシーフィールドとして `adcp_major_version`(整数)も保持しますが、今後はリリース精度の `adcp_version` が主要なワイヤーフィールドです。ネゴシエーションコントラクト、移行タイムライン、SDK ピン留めの例については [バージョンネゴシエーション](/docs/reference/versioning#version-negotiation) を参照。 注: 両フィールドは v3 のみです — v2 クライアントはいずれも設定できません(フィールドは v2 スキーマに存在しません)。マルチバージョンセラーは、これらのフィールドではなく構造的な手がかり(`buying_mode` の欠如、`fixed_rate` 対 `fixed_price`、`geo_postal_codes` 対 `geo_postal_areas` など)で v2 ペイロードを検出します。 ### Collection lists コレクションリストは、ブランドセーフティをプロパティからコンテンツプログラムに拡張します。プロパティリストと同様、コレクションリストは厳選されたセットですが — 番組、シリーズ、その他のコンテンツプログラムを、クロスパブリッシャーマッチングのための配信識別子(IMDb、Gracenote、EIDR)を使ってプラットフォーム横断でターゲットします。 新しいターゲティングオーバーレイフィールド `collection_list` と `collection_list_exclude` が、包含と除外の両方のターゲティングを可能にします。新しいジャンルタクソノミー enum が、バイヤーとセラー間でジャンル分類を正規化します。 コレクションリストの作成と管理のタスクリファレンス。 ### Broadcast TV support リニア TV セラーが完全に AdCP に参加できるようになりました。このリリースは、放送をデジタルから区別するプロトコルプリミティブを追加します。 * **放送クリエイティブ識別子** — クリエイティブアセットとマニフェスト上の `industry_identifiers`、`creative-identifier-type` enum(`ad_id`、`isci`、`clearcast_clock`、`idcrea`)付き。放送クリエイティブは、関連するワークフローで使われるトラフィックまたはクリアランス識別子を運び、スポットをローテーション指示とトラフィックシステムに結び付けます。 * **放送スポットフォーマット** — :15、:30、:60 スポットの参照フォーマット。動画ファイルのみ — VAST なし、インプレッショントラッカーなし、クリックスルー URL なし。フォーマットにトラッカーアセットスロットがないことは、サードパーティピクセル追跡がサポートされないことを示します。 * **Agency Estimate Number** — メディアバイとパッケージ上の `agency_estimate_number`。放送オーダーをエージェンシーメディアプランと課金に結び付ける財務参照。 * **測定ウィンドウ** — Live、C3、C7 の成熟のための `reporting_capabilities` 上の `measurement_windows`。`billing_measurement` 上の `measurement_window` が、保証がどのウィンドウに対して照合されるかを宣言します。 * **配信データの完全性** — パッケージごとの配信データ上の `is_final` と `measurement_window`。バイヤーは、数値が暫定か確定か、どの測定ウィンドウを表すかを知ります。成熟するデータを持つ任意のチャネル(放送、ポッドキャスト、ロングテールコンテンツ)に適用されます。 スポットフォーマット、クリエイティブ識別子、測定ウィンドウ、放送が CTV とどう異なるかをカバーするチャネルガイド。 ### Structured measurement terms 保証型バイは正式な交渉サーフェスを得ます: `measurement_terms` が課金測定ベンダー、IVT しきい値、ビューアビリティ下限を定義します。セラーはプロダクトでデフォルトを宣言し、バイヤーは `create_media_buy` でオーバーライドを提案し、セラーは受け入れ/拒否/調整します。`cancellation_policy` スキーマが保証型プロダクトの予告期間とペナルティを宣言します。 ### Unified vendor pricing 価格モデルはシグナルからクリエイティブ、ガバナンス、プロパティリストエージェントに拡張されます。クリエイティブエージェントは `list_creatives` と `build_creative` レスポンスで `pricing_options[]` を返します。プロパティリストは `pricing_options[]` を運びます。すべてのベンダー価格は共有の `vendor-pricing-option.json` スキーマ(cpm、percent\_of\_media、flat\_fee)を使います。 ### Offline reporting delivery セラーは `reporting_delivery_methods` 経由で `get_adcp_capabilities` にオフラインレポート配信方法(SFTP、S3、GCS、Azure Blob)を宣言できます。アカウントはファイル配信用の `reporting_bucket` を指定します。プロダクトは `reporting_capabilities` で `supports_offline_delivery` を宣言します。ファイル形式には CSV、JSON、Parquet、Avro、ORC が含まれます。 ### Trusted Match Protocol extensions TMP は、プロバイダーエンドポイント、機能、ライフサイクルステータス(active/inactive/draining)、プロバイダーごとのタイムアウト予算を形式化するプロバイダー登録スキーマ(`provider-registration.json`)を得ます。`GET /health` エンドポイントがルーター側のヘルス監視を可能にします。TMPX は、国分割されたアイデンティティ解決とマクロ接続性を伴うエクスポージャー追跡を追加します。 Identity Match リクエストは、単一の `user_token` + `uid_type` ペアの代わりに `identities` 配列(リクエストあたり 1〜3 トークン)を受け入れるようになりました。パブリッシャーは持っているすべてのアイデンティティトークンを送り、バイヤーは一致するグラフで解決します。ルーターはプロバイダーごとに `identities` をフィルターし(最小必要データ)、転送前に再署名します — 転送されるセットはパブリッシャーが送ったもののサブセットでなければなりません。RFC 8785 JCS 正規化が署名とキャッシュキー導出の両方に使われ、`consent_hash` が同意状態でキャッシュを分割します。`rampid_derived` が `uid-type` enum に追加されます。 TMP は 3.0 でプレリリースのままです。安定サーフェスは 3.1.0 を目標としています。 ### Brand schema extensions `brand.json` は、ブランド関連エージェントを宣言する汎用 `agents` 配列、ビジュアルトークン(`border_radius`、`elevation`、`spacing`、拡張カラーロール)、`weight`、`style`、`stretch`、`optical_size`、`usage` フィールドを持つ構造化フォント定義を得ます。 ### Required tasks reference 新しい [プロトコル別の必須タスク](/docs/protocol/required-tasks) リファレンスページが、エージェントの役割別にすべての AdCP プロトコルにわたる必須、条件付き、任意のタスクを統合します — 実装が最小サーフェスをカバーするか検証する単一ページ。 ### Experimental surfaces AdCP 3.0 は、[3.x 安定性保証](/docs/reference/versioning#3x-stability-guarantees)の下で安定サーフェスのコアを出荷し、コアプロトコルの一部だがまだ凍結されていない 4 つのサーフェスを追加します。これらのサーフェスは、卒業パスを形作る本番エンゲージメントでそれらを運用するデザインパートナー — **OpenAds、Scope3、Yahoo、ONX、Triton Digital** — と共同開発されています。実験的サーフェスはスキーマに `x-status: experimental` を運び、それらを実装するセラーは `get_adcp_capabilities` の `experimental_features` で機能 id を宣言します。それらは少なくとも 6 週間の予告をもって 3.x リリース間で変更される可能性があります。 | Surface | Feature id | Why experimental | | --------------------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------- | | [ブランド権利ライフサイクル](/docs/brand-protocol/tasks/get_rights) | `brand.rights_lifecycle` | 3.0 サイクルの後半に追加された法的構成サーフェス。最初のエンタープライズデプロイが、部分的権利、サブライセンス、失効、紛争解決のエッジケースを露呈する。 | | [キャンペーンガバナンス](/docs/governance/campaign/specification) | `governance.campaign` | マルチパーティガバナンスセマンティクス(承認競合、監査来歴、Embedded Human Judgment 下のタイブレーク)はまだ確定していない。 | | [Trusted Match Protocol](/docs/trusted-match/) | `trusted_match.core` | プライバシーアーキテクチャは規制当局の関与とともに進化する。TMPX エクスポージャートークン、国分割されたアイデンティティ、Offer マクロは変更が予想される。 | | [Sponsored Intelligence](/docs/sponsored-intelligence/overview) | `sponsored_intelligence.core` | 会話型ブランド体験は新しい広告モデル。セッションライフサイクル、UI コンポーネント、アイデンティティ/同意オブジェクトの形状、機能ネゴシエーションは、ファーストパーティ AI ホストとブランドエージェントが統合するにつれて進化が予想される。 | 実験的ラベルは意図的にスコープされています — 3.0 の他のすべては通常の 6 か月の非推奨予告に従います。完全なコントラクト、卒業基準、クライアントガイダンスについては [実験的ステータス](/docs/reference/experimental-status) を参照。 ### Signal pricing: custom escape hatch ベンダー価格は、`cpm`、`percent_of_media`、`flat_fee`、`per_unit` に加えて `custom` モデルを得ます。人間可読な `description` と構造化 `metadata` オブジェクトを要求します。バイヤーはコミットメント前にカスタム価格をオペレーターレビューに通すべきです(SHOULD)— 自動選択は推奨されません。 動機: パフォーマンスキッカー、段階的ボリューム、ハイブリッド(flat + CPM)、成果共有価格はすでに現場に現れています。今エスケープハッチを出荷することで、実際のデプロイが列挙されたモデルで表現できない構成に遭遇したときのレトロフィットの痛みを避けます。構造化メタデータは、新しいパターンごとのスキーマ変更なしにモデルを機械検査可能に保ちます。 *** ## 破壊的変更 ### メディアチャンネルタクソノミー v2 の 9 チャンネルが 20 のプランニング指向チャンネルに置き換えられます。5つのチャンネルはそのまま(`display`、`social`、`ctv`、`podcast`、`dooh`)。残りの4つは分割、削除、または名前変更されました。 チャンネル値を読み書きするすべてのコードを更新する必要があります。 | v2 チャンネル | v3 チャンネル | 注記 | | --------- | -------------------------- | ----------------------------- | | `display` | `display` | 変更なし | | `video` | `olv`、`linear_tv`、`cinema` | 配信コンテキストで分割(`ctv` は v2 ですでに別) | | `audio` | `radio`、`streaming_audio` | 配信で分割(`podcast` は v2 ですでに別) | | `native` | 削除 | フォーマットレベルのプロパティを使用する | | `social` | `social` | 変更なし | | `ctv` | `ctv` | 変更なし | | `podcast` | `podcast` | 変更なし | | `dooh` | `dooh` | 変更なし | | `retail` | `retail_media` | 明確化のために名前変更 | v3 の新チャンネル(v2 に相当なし): `search`、`linear_tv`、`radio`、`streaming_audio`、`ooh`、`print`、`cinema`、`email`、`gaming`、`retail_media`、`influencer`、`affiliate`、`product_placement`、`sponsored_intelligence`。 `gaming` チャンネルはゲーム内インリンシック広告、リワード動画、プレイアブル広告をカバーします。ゲームアプリのリワード動画は `olv` に分類することもできる — インベントリがゲーミング予算から来る場合は `gaming` を使用します。 各 v2 チャンネル、マルチチャンネルプロダクト、ケイパビリティ探索の例を含む完全なマッピングガイド。 *** ### 価格オプションフィールドの名前変更 v3 は**ハード制約**(パブリッシャーが適用する価格)と**ソフトヒント**(過去のパーセンタイル)を分離します。`fixed_rate` は `fixed_price` になり、`price_guidance.floor` はトップレベルの `floor_price` に移動します。 | v2 フィールド | v3 フィールド | 注記 | | ---------------------- | ------------- | ---------------------- | | `fixed_rate` | `fixed_price` | 明確化のために名前変更(レートではなく価格) | | `price_guidance.floor` | `floor_price` | ハード制約としてトップレベルに移動 | これらのフィールドは標準的なディールタイプにマッピングされます: `fixed_price` はプログラマティックギャランティード(PG)とプリファードディールに、`floor_price` はプライベートマーケットプレイス(PMP)オークションに対応します。オープンオークションインベントリは両方のフィールドを省略します。 固定価格とオークションの例、価格ガイダンスのスキーマ、フラットレート価格、最低支出、移行期間の対応。 *** ### ウェイト付きクリエイティブアサインメント `creative_ids` 文字列配列がデリバリーウェイト付けとプレースメントターゲティングをサポートする `creative_assignments` オブジェクトに置き換えられます。 | v2 フィールド | v3 フィールド | | --------------------- | -------------------------------------------------------------------------- | | `creative_ids`(文字列配列) | `creative_assignments`(`creative_id`、`weight`、`placement_ids` を持つオブジェクト配列) | ウェイト付きアサインメント、プレースメントターゲティング、統合 `assets` 配列によるアセット探索、繰り返し可能なグループ、フォーマットカード。 *** ### 名前付きシステムによるジオターゲティング メトロと郵便ターゲティングには明示的なシステム仕様が必要になり、グローバル市場をサポートします。値は `{ "system": "...", "values": [...] }` オブジェクトを使ってシステムでグループ化されます。 | v2 フィールド | v3 フィールド | | ------------------------- | ---------------------------------------- | | `geo_metros`(文字列配列) | `geo_metros`(system/values オブジェクト) | | `geo_postal_codes`(文字列配列) | `geo_postal_areas`(system/values オブジェクト) | v3 はネガティブターゲティング(例: ニューヨーク DMA を除く米国をターゲット)のために `geo_metros_exclude` と `geo_postal_areas_exclude` も追加します。 メトロと郵便システムのリファレンステーブル、除外ターゲティング、ケイパビリティ探索、完全なターゲティング例。 *** ### その他のターゲティング変更 v3 はジオ以外にいくつかのターゲティングフィールドを追加します: | フィールド | 説明 | | --------------------------------------- | ------------------------------------------------------------------- | | `daypart_targets` | 時間帯と曜日のターゲティングウィンドウ | | `age_restriction` | 制限コンテンツの年齢ゲーティング | | `device_platform` | OS ターゲティング(iOS、Android、Windows、tvOS など) | | `device_type` | デバイスフォームファクターターゲティング(desktop、mobile、tablet、ctv、dooh、unknown) | | `language` | コンテンツ言語ターゲティング | | `keyword_targets` / `negative_keywords` | マッチタイプ(broad、phrase、exact)とキーワードごとの入札価格を持つ検索とリテールメディア向けキーワードターゲティング | | `device_type_exclude` | ネガティブデバイスフォームファクターターゲティング | | `geo_proximity` | 移動時間アイソクロン、半径、GeoJSON ジオメトリによるポイントベースの近接ターゲティング | これらのフィールドはオプションの追加 — v2 のフィールドを置き換えるものではありません。 *** ### 統合アセット探索 フォーマットは `assets_required` の代わりに `required` ブールを持つ `assets` 配列を使用するようになりました。[クリエイティブの詳細](/docs/reference/migration/creatives#asset-discovery)を参照。 *** ### カタログが promoted\_offerings を置き換える `promoted_offerings` クリエイティブアセットタイプと `promoted_offering` 文字列フィールドが削除されました。カタログは独自の同期ライフサイクル(`sync_catalogs`)、フォーマットレベルの要件(`catalog_requirements`)、コンバージョンイベント整合を持つファーストクラスのプロトコルオブジェクトになりました。 | v2 フィールド | v3 代替 | | -------------------------------------- | ------------------------------- | | `promoted_offerings`(クリエイティブアセット) | クリエイティブマニフェストの `catalogs` フィールド | | `promoted_offering`(メディアバイの文字列) | 削除 — `brand` + `brief` を使用する | | `promoted_offering`(クリエイティブマニフェストの文字列) | 削除 — `catalogs` フィールドを使用する | 変更前後の例、sync\_catalogs ワークフロー、catalog\_requirements の探索、移行チェックリスト。 *** ### ブランドアイデンティティの統一 インラインの `brand_manifest` オブジェクトがブランド参照(`BrandRef`)に置き換えられます。タスクスキーマはマニフェストをインラインで渡す代わりに `{ domain, brand_id }` でブランドを参照します。ブランドデータは実行時に `brand.json` またはレジストリから解決されます。 | v2/beta フィールド | v3 rc.1 フィールド | | ----------------------------- | ------------------------------- | | `brand_manifest`(インラインオブジェクト) | `brand`(`{ domain, brand_id }`) | 影響: `get_products`、`create_media_buy`、`build_creative`、プロパティリストスキーマ。 BrandRef スキーマ、解決フロー、変更前後の例、移行ステップ。 *** ### プロダクトデリバリー予測 `estimated_exposures` が `DeliveryForecast` タイプを使った構造化された `forecast` フィールドに置き換えられます。 | v2/beta フィールド | v3 rc.1 フィールド | | ------------------------- | ---------------------------------------------- | | `estimated_exposures`(整数) | `forecast`(期間、メトリック範囲、方法論を持つ DeliveryForecast) | *** ### バイイングモードによるプロポーザルリファインメント `proposal_id` が `get_products` リクエストから削除されました。リファインメントは型指定された変更リクエスト配列([型指定されたリファインメント](#型指定されたリファインメントとセラーの承認)参照)を持つ `buying_mode: "refine"` を使用します。セッション継続性(MCP では `context_id`、A2A では `contextId`)が呼び出し間で会話履歴を引き継ぐ。 `proposal_id` による `create_media_buy` でのプロポーザル実行は変更なし。 | v2/beta フィールド | v3 rc.1 フィールド | | ----------------------------------- | ---------------------------------- | | `get_products` リクエストの `proposal_id` | 削除 — `buying_mode: "refine"` を使用する | *** ### 最適化目標の再設計 `optimization_goal`(単一オブジェクト)が `optimization_goals`(配列)に置き換えられます。各目標は `kind` による識別子付きユニオン: | v2/beta フィールド | v3 rc.1 フィールド | | ----------------------------- | ------------------------------- | | `optimization_goal`(単一オブジェクト) | `optimization_goals`(配列) | | 暗黙的な単一目標 | マルチゴール順序付けのための `priority` フィールド | 2つの目標 kind: * **`metric`** — `cost_per` または `threshold_rate` ターゲットを持つセラーネイティブのデリバリーメトリック(クリック、視聴、リーチ、エンゲージメントなど) * **`event`** — `event_sources` 配列とオプションの `value_field`/`value_factor` を持つコンバージョントラッキング 目標 kind、リーチ最適化、マルチゴール優先度、プロダクトケイパビリティ、移行ステップ。 *** ### シグナル価格の再構造化 シグナルのレガシーの `pricing: { cpm }` オブジェクトが3つの価格モデルを持つ構造化された `pricing_options` 配列に置き換えられます。 | v2/beta フィールド | v3 rc.1 フィールド | | ----------------- | ----------------------------------- | | `pricing.cpm`(数値) | `pricing_options[]`(価格モデルオブジェクトの配列) | 3つのモデル: `cpm`、`percent_of_media`(オプションの `max_cpm` 付き)、`flat_fee`。 価格モデル、deliver\_to のフラット化、使用状況レポート、移行ステップ。 *** ### シグナル deliver\_to のフラット化 `get_signals` リクエストのネストされた `deliver_to` オブジェクトが2つのトップレベルフィールドに置き換えられます。 | v2/beta フィールド | v3 rc.1 フィールド | | ------------------------- | ---------------------- | | `deliver_to.destinations` | `destinations`(トップレベル) | | `deliver_to.countries` | `countries`(トップレベル) | *** ### AudienceMember external\_id が必須に `external_id` が uid-type 列挙値から `AudienceMember` の必須トップレベルフィールドに昇格します。すべてのメンバーはバイヤーが割り当てた安定した識別子と少なくとも1つのマッチング可能な識別子を持つ必要があります。 変更前後の例、uid-type の変更、sync\_audiences の使用、移行ステップ。 *** ### セラーの承認による型指定されたリファインメント `refine` は `overall`/`products`/`proposals` のネストされたオブジェクトからフラットな型指定された配列に再設計されました。各エントリは `scope` で識別されます: | v2/beta フィールド | v3 rc.1 フィールド | | ------------------------------ | --------------------------------------------- | | `refine.overall`(文字列) | `{ "scope": "request", "ask": "..." }` 配列エントリ | | `refine.products[].product_id` | `{ "scope": "product", "id": "..." }` | | `refine.products[].notes` | `ask` フィールド | セラーは `refinement_applied` で応答する — 位置でマッチする配列で、各エントリが `status`(`applied`、`partial`、`unable`)とオプションの `notes` を報告します。 変更リクエストタイプ、セラーの承認、変更前後の例。 *** ### クリエイティブアサインメントの再構造化 `SyncCreativesRequest.assignments` が `{ creative_id: package_id[] }` マップから明示的なフィールドを持つ型指定された配列に変更されました。 | v2/beta フィールド | v3 rc.1 フィールド | | ------------------------ | ----------------------------------------------------------------------- | | `assignments`(オブジェクトマップ) | `assignments`(`{ creative_id, package_id, weight, placement_ids }` の配列) | *** ### シグナルのアカウントとフィールドの一貫性 シグナルスキーマへの2つの一貫性変更: | v2/beta フィールド | v3 rc.1 フィールド | | ----------------------------------------------------- | ------------------------------------------- | | `get_signals` と `activate_signal` の `account_id`(文字列) | `account`(AccountReference オブジェクト) | | `activate_signal` の `deployments` | `destinations`(`get_signals` との一貫性のために名前変更) | *** ### パッケージカタログを配列に | v2/beta フィールド | v3 rc.1 フィールド | | ------------------------------------ | ---------------------------- | | パッケージの `catalog`(単一の Catalog オブジェクト) | `catalogs`(Catalog refs の配列) | *** ### ブランドトーンの構造化フォーマット ブランドの `tone` はオブジェクト型のみになった — 文字列フォーマットが削除されました。構造化されたトーンには `voice`、`attributes`、`dos`、`donts` フィールドが含まれます。既存の文字列値は `{ "voice": "" }` に移行します。 | v2/beta フィールド | v3 rc.2 フィールド | | -------------------- | --------------------------------------------------- | | `tone`(文字列またはオブジェクト) | `tone`(オブジェクト: `{ voice, attributes, dos, donts }`) | *** ### アカウント解決の削除 `account_resolution` ケイパビリティフィールドが削除されました。`require_operator_auth` が認証モデルとアカウント参照スタイルの両方を決定する: `true` は明示的なアカウント(`list_accounts` で発見し、`account_id` を渡す)、`false` は暗黙的なアカウント(`sync_accounts` で宣言し、自然キーを渡す)を意味します。 | v2/beta/rc.1 フィールド | v3 rc.2 フィールド | | ---------------------------- | ---------------------------------- | | `account_resolution` ケイパビリティ | 削除 — `require_operator_auth` を使用する | *** ### プライバシーと同意 AdCP は独自の同意フレームワークを定義しません。プライバシーシグナル(TCF 2.0、GPP、US Privacy String)はブリーフの `ext` フィールドまたはトランスポートレベルのヘッダーで渡します。同意シグナルを必要とするセラーは、拡張メカニズムを使って `get_adcp_capabilities` でこれを宣言します。 *** ## v3 で削除されたもの | 削除されたもの | 代替 | | ---------------------------------------------------- | ------------------------------------------------ | | `adcp-extension.json` エージェントカード | `get_adcp_capabilities` タスク | | `list_authorized_properties` タスク | `get_adcp_capabilities` ポートフォリオセクション | | フォーマットの `assets_required` | `required` ブールを持つ `assets` 配列 | | フォーマットの `preview_image` | `format_card` オブジェクト | | パッケージの `creative_ids` | `creative_assignments` 配列 | | `geo_postal_codes` | `geo_postal_areas` | | 価格の `fixed_rate` | `fixed_price` | | `price_guidance.floor` | `floor_price`(トップレベル) | | `promoted_offerings` アセットタイプ | クリエイティブマニフェストの `catalogs` フィールド | | メディアバイの `promoted_offering` | 削除 — `brand` + `brief` を使用する | | クリエイティブマニフェストの `promoted_offering` | `catalogs` フィールド | | `brand_manifest`(インラインオブジェクト) | `brand` ref(`{ domain, brand_id }`) | | プロダクトの `estimated_exposures` | `forecast`(DeliveryForecast) | | `get_products` リクエストの `proposal_id` | セッション継続性(`context_id` / `contextId`) | | `overall`/`products`/`proposals` を持つ `refine` オブジェクト | 型指定された変更リクエストの `refine` 配列 | | `build_creative` リクエストの `creative_brief` | マニフェスト `assets` マップの `brief` アセットタイプ | | `supports_brief` ケイパビリティ | `supports_compliance` | | `creative-brief-ref.json` スキーマ | 削除 — ブリーフはアセットタイプになった | | `activate_signal` の `deployments` | `destinations` | | シグナルタスクの `account_id`(文字列) | `account`(AccountReference) | | `report_usage.kind` と `report_usage.operator_id` | 削除 | | パッケージの `catalog`(単数) | `catalogs`(配列) | | `account_resolution` ケイパビリティ | `require_operator_auth` がアカウントモデルを決定する | | `delete_content_standards` タスク | `update_content_standards` でアーカイブする | | `get_property_features` タスク | プロパティリストフィルター + 機能探索のための `get_adcp_capabilities` | | `brand.json` の文字列としての `tone` | オブジェクトのみ: `{ voice, attributes, dos, donts }` | *** ## 移行チェックリスト これらの破壊的変更は AdCP データを読み書きするすべての実装に影響する: * [ ] チャンネル列挙値を[新しいタクソノミー](/docs/reference/migration/channels)に更新します * [ ] [価格オプション](/docs/reference/migration/pricing)で `fixed_rate` -> `fixed_price` に名前変更します * [ ] `price_guidance.floor` -> `floor_price` に移動する([価格の詳細](/docs/reference/migration/pricing)) * [ ] `creative_ids` を [`creative_assignments`](/docs/reference/migration/creatives) に置き換える * [ ] [メトロ/郵便ターゲティング](/docs/reference/migration/geo-targeting)にシステム仕様を追加します * [ ] `geo_postal_codes` -> `geo_postal_areas` に名前変更します * [ ] 新しい `geo_metros_exclude` と `geo_postal_areas_exclude` フィールドを処理します * [ ] [`assets` 配列](/docs/reference/migration/creatives#asset-discovery)を使うようにフォーマット解析を更新します * [ ] `preview_image` の読み取りを [`format_card`](/docs/reference/migration/creatives#format-cards-replacing-preview_image) レンダリングに置き換える * [ ] `list_authorized_properties` の呼び出しを `get_adcp_capabilities` ポートフォリオに置き換える * [ ] `promoted_offerings` をクリエイティブマニフェストアセットから削除し、[`catalogs` フィールド](/docs/reference/migration/catalogs)に置き換える * [ ] メディアバイとクリエイティブマニフェストオブジェクトから `promoted_offering` 文字列を削除します * [ ] `optimization_goal` を [`optimization_goals`](/docs/media-buy/media-buys/optimization-reporting)(識別子付きユニオンの配列)に更新します * [ ] `external_id` を AudienceMember の必須フィールドとして処理します * [ ] すべてのタスク呼び出しで `brand_manifest` を `brand` ref(`{ domain, brand_id }`)に置き換える * [ ] `estimated_exposures` の読み取りをプロダクトの `forecast`(DeliveryForecast)に置き換える * [ ] `get_products` リクエストから `proposal_id` を削除する — リファインメントにはセッション継続性を使用します * [ ] `refine` をオブジェクトから `scope` 識別子を持つ型指定された配列に更新します * [ ] リトライ/修正ロジックのためにエラーの `recovery` フィールドを処理します * [ ] パッケージの `catalog` を `catalogs`(配列)に更新します * [ ] シグナルの `account_id` を `account`(AccountReference)に更新します * [ ] シグナルの `deployments` を `destinations` に名前変更します * [ ] `get_products` で `buying_mode`(現在必須)を渡します * [ ] `creative_brief` をマニフェスト `assets` マップの `brief` アセットタイプに移動します * [ ] `kind` と `operator_id` フィールドなしの `report_usage` を処理します * [ ] `SyncCreativesRequest.assignments` をオブジェクトマップから型指定された配列に移行します * [ ] ブランドの `tone` を文字列からオブジェクトフォーマット(`{ voice, attributes, dos, donts }`)に移行します * [ ] `account_resolution` の読み取りを削除する — 代わりに `require_operator_auth` を使用します * [ ] `media_buy.features.sandbox` の代わりに `account.sandbox` からサンドボックスサポートを読む * [ ] DOOH パラメーターが提供される場合、`flat_rate.parameters` の中に `type: "dooh"` を追加します * [ ] `list_creatives` と `sync_creatives` をクリエイティブプロトコル操作として扱います * [ ] `delete_content_standards` の呼び出しを削除する — `update_content_standards` でアーカイブします * [ ] `get_property_features` の呼び出しを削除する — プロパティリストフィルターを使用します * [ ] すべてのリクエスト/レスポンスを v3 スキーマに対して検証します すべてのデータ構造を v3 フォーマット(チャンネル、価格、ジオターゲティング)に更新し、新しいケイパビリティを実装します: * [ ] `get_adcp_capabilities` タスクを実装する(`account` ケイパビリティを含む) * [ ] エージェントカードから `adcp-extension.json` を削除します * [ ] アカウントプロビジョニングのために `sync_accounts` を実装します * [ ] 該当する場合は `get_products` からデリバリー予測付きプロポーザルを返す * [ ] ガバナンスエージェントと統合する場合は `get_products` でプロパティリストフィルタリングをサポートします * [ ] 承認ワークフロー付きで [`sync_catalogs`](/docs/reference/migration/catalogs) 経由で同期されたカタログを処理します * [ ] プロダクトで `metric_optimization` ケイパビリティを宣言します * [ ] `get_adcp_capabilities` でディメンションブレークダウンの `reporting` ケイパビリティを宣言します * [ ] `get_media_buy_delivery` で `reporting_dimensions` パラメーターをサポートします * [ ] `refine` リクエストを処理するときに `refinement_applied` 配列を返す * [ ] メディアバイで `rejected` ステータスと `rejection_reason` を実装します * [ ] `get_products` で `fields` プロジェクションパラメーターをサポートします * [ ] `get_adcp_capabilities` で `supported_pricing_models` を宣言します * [ ] `get_products` で `time_budget` をサポートし、予算内で完了できない場合は `incomplete` を返す * [ ] `media_buy.features.sandbox` ではなく `account.sandbox` でサンドボックスサポートを宣言します * [ ] `preferred_delivery_types`、`exclusivity`、オプションの `delivery_measurement`、パッケージレベルの `start_time` / `end_time` をサポートします すべてのリクエストとレスポンス処理を v3 フォーマットに更新し、新しいケイパビリティを統合する: * [ ] 購買を行う前に [`brand.json`](/docs/brand-protocol) でブランドを解決します * [ ] 請求関係を確立するために `sync_accounts` を呼び出す * [ ] ランタイム探索のために `get_adcp_capabilities` を呼び出すように更新します * [ ] セラーが返したプロポーザルとデリバリー予測を評価します * [ ] ブランド/プロパティ解決とエージェント探索のために[レジストリ API](/docs/registry) を使用します * [ ] ガバナンスエージェントと連携するときはプロパティリストを渡してインベントリをフィルタリングします * [ ] ユーザーをブランドエージェントに接続するときは SI セッションを呼び出す * [ ] クリエイティブを送信する前に [`sync_catalogs`](/docs/reference/migration/catalogs) でカタログを同期します * [ ] アトリビューショントラッキングのためにカタログに `conversion_events` を追加します * [ ] `create_media_buy` の `optimization_goal` を `optimization_goals` 配列に更新します * [ ] 価格オプション付きでシグナルをアクティベートするときに `pricing_option_id` を渡します * [ ] デリバリーレポーティングでのディメンションブレークダウンのために `reporting_dimensions` を使用します * [ ] 型指定されたリファインメントフィードバックのために `refinement_applied` レスポンスを処理します * [ ] 自動リトライ/修正のためにエラーの `recovery` フィールドを使用します * [ ] 効率的な探索のために `get_products` で `fields` プロジェクションを使用します * [ ] メディアバイの `rejected` ステータスを処理します * [ ] キャンペーンクリーンアップのために `activate_signal` で `action: "deactivate"` を使用します * [ ] ライセンスコンテンツキャンペーンのために `get_rights` / `acquire_rights` を統合します * [ ] クリエイティブ生成のために `brand.json` の `visual_guidelines` を処理します * [ ] アカウントとサンドボックスフローを選択するときに `require_operator_auth` と `account.sandbox` を読む * [ ] 制限されたレイテンシのプロダクト探索のために `time_budget` / `incomplete` を処理します * [ ] ビルド対ライブラリワークフローをルーティングするためにクリエイティブケイパビリティフラグ(`supports_generation`、`supports_transformation`、`has_creative_library`)を使用します * [ ] キャンペーンガバナンスが使用中の場合は `check_governance` でガバナンスエージェントにプランを送信します スキーマ参照を v2 から v3 に更新します。シグナルプロトコルのコアモデルはメディアチャンネルを使用しません。 * [ ] スキーマ参照を v2 から v3 に更新します * [ ] `get_adcp_capabilities` が `major_versions: [3]` を返すことを確認します * [ ] `get_signals` レスポンスで構造化された `signal_id` オブジェクトを返す * [ ] シグナルレスポンスに `value_type` フィールドを含めます * [ ] ID ベースのルックアップのために `get_signals` リクエストで `signal_ids` パラメーターをサポートします * [ ] レガシーの `pricing` から構造化された `pricing_options` 配列に更新します * [ ] ネストされた `deliver_to` の代わりにトップレベルの `destinations`/`countries` を処理します * [ ] `report_usage` に `idempotency_key` サポートを追加します * [ ] `activate_signal` で `action: "deactivate"` をサポートします * [ ] シグナルエントリに `categories` と `range` メタデータを含めます * [ ] `account_id` を `account`(AccountReference)に更新します * [ ] `deployments` を `destinations` に名前変更します `adagents.json` でシグナルカタログを公開します。[データプロバイダーガイド](/docs/signals/data-providers)を参照。 * [ ] `/.well-known/adagents.json` にシグナルカタログを作成します * [ ] `id`、`name`、`value_type`、オプションのメタデータでシグナルを定義します * [ ] グループ化と効率的な認証のために `signal_tags` を追加します * [ ] `signal_ids` または `signal_tags` 認証タイプを使ってシグナルエージェントを認証します * [ ] AdAgents.json Builder でカタログを検証します 新しいアセット探索をサポートし、ブランドアイデンティティを統合します。フォーマットの `type` フィールド(video、display、audio)は IAB クリエイティブ分類であり、メディアチャンネルではありません。 * [ ] `required` ブールを持つ `assets` 配列をサポートする(`assets_required` を置き換え) * [ ] `preview_image` を `format_card` レンダリングに置き換える * [ ] ブランドに合ったクリエイティブ生成のために `brand.json` でブランドアイデンティティを解決します * [ ] クリエイティブマニフェストで `catalog` フィールドをサポートする([`promoted_offerings`](/docs/reference/migration/catalogs) アセットを置き換え) * [ ] カタログアイテムをレンダリングするフォーマットで `catalog_requirements` を宣言します * [ ] スキーマ参照を v2 から v3 に更新します * [ ] クリエイティブマニフェストとアセットで `provenance` オブジェクトをサポートします * [ ] `assets` マップのアセットタイプとして `brief` と `catalog` をサポートします * [ ] クリエイティブブリーフの `compliance.required_disclosures` を処理します * [ ] フォーマットの `supported_disclosure_positions` 互換性を確認します * [ ] ケイパビリティで `supports_compliance` を宣言する(`supports_brief` を置き換え) * [ ] ブランドに合ったアセット生成のために `brand.json` の `visual_guidelines` を処理します * [ ] `build_creative` で `include_preview`、`target_format_ids`、`quality`、`item_limit` をサポートします * [ ] `creative_id` によるライブラリ取得をサポートし、`supports_generation`、`supports_transformation`、`has_creative_library` を宣言します * [ ] `list_creatives` と `sync_creatives` をクリエイティブプロトコル操作として実装します * [ ] `preview_creative` で `quality` パラメーターをサポートします * [ ] フォーマットの `disclosure_capabilities` でディスクロージャー持続性サポートを宣言します バイサイドアイデンティティを確立します。[brand.json 仕様](/docs/brand-protocol/brand-json)を参照。 * [ ] ドメインに `/.well-known/brand.json` をホストします * [ ] ブランドポートフォリオ、プロパティ、認証済みオペレーターを宣言します * [ ] オプションで `brand.json` でインラインまたはブランドエージェント経由でブランドデータ(ロゴ、カラー、フォント、トーン)を提供します * [ ] ジェネラティブクリエイティブシステム向けに `brand.json` に `visual_guidelines` を追加します * [ ] コンテンツをライセンスする場合は `get_rights` / `acquire_rights` / `update_rights` を実装します * [ ] `brand.json` をホストしていない場合は[コミュニティブランドレジストリ](/docs/registry)に登録します ブランドスーツアビリティケイパビリティを実装します。[ガバナンスプロトコル](/docs/governance/overview)を参照。 * [ ] プロパティリストタスク(`create_property_list`、`get_property_list` など)を実装します * [ ] コンテンツスタンダードタスク(`create_content_standards`、`calibrate_content` など)を実装します * [ ] `supported_protocols` に `governance` を含む `get_adcp_capabilities` を実装します * [ ] クリエイティブポリシーで `provenance_required` 強制を実装します * [ ] AI 検出サービスからの `verification` 結果をサポートします * [ ] クリエイティブ評価のために `get_creative_features` を実装します * [ ] `get_adcp_capabilities` で `creative_features` を宣言します * [ ] プランレベルのガバナンスを提供するときはキャンペーンガバナンスタスク(`sync_plans`、`check_governance`、`report_plan_outcome`、`get_plan_audit_logs`)を実装します 会話型ブランド体験を実装します。[SI チャットプロトコル](/docs/sponsored-intelligence/si-chat-protocol)を参照。 * [ ] SI セッションタスク(`si_initiate_session`、`si_send_message`、`si_terminate_session`)を実装します * [ ] `supported_protocols` に `sponsored_intelligence` を含む `get_adcp_capabilities` を実装します *** ## サポート * **コミュニティ**: [Slack](https://join.slack.com/t/agenticads/shared_invite/zt-3c5sxvdjk-x0rVmLB3OFHVUp~WutVWZg) * **イシュー**: [GitHub Issues](https://github.com/adcontextprotocol/adcp/issues) * **サポート**: [support@adcontextprotocol.org](mailto:support@adcontextprotocol.org) # Specification Guidelines Source: https://adcp-docs-ja.pier1.co.jp/docs/spec-guidelines # AdCP 仕様ガイドライン このドキュメントは AdCP 仕様を維持するための設計原則とルールを示します。複数のプログラミング言語で一貫性・明確さ・実装容易性を確保することが目的です。 ## 型命名の原則 ### 型名の再利用禁止 **ルール**: コンセプトが異なるのに同じ enum 名やフィールド名を使わないでください(文脈が違っても不可)。 **理由**: TypeScript/Python/Go などの型ジェネレータは、同名で値や意味が異なると衝突し、エイリアスや深い import といった回避策を強要します。 **問題の例**: ```json theme={null} // ❌ BAD: 意味が異なる "Type" enum が複数 // asset-type.json { "type": "string", "enum": ["image", "video", "html"] } // format.json { "type": "string", "enum": ["audio", "video", "display"] } // 結果: Python では Type/Type1/Type2 生成、あるいはアルファベット順の先勝ち ``` **解決策**: ドメインに即した意味的な名前を使います。 ```json theme={null} // ✅ GOOD: コンセプトごとに異なる enum 名 // asset-content-type.json { "type": "string", "enum": ["image", "video", "html"] } // format-category.json { "type": "string", "enum": ["audio", "video", "display"] } // 結果: Python では AssetContentType と FormatCategory を生成 ``` ### セマンティックなフィールド名 フィールド名は「何を表すか」を示します。汎用的なカテゴリ名は避けてください。 **例:** * ✅ `format_category` - どのチャネル/タイプのフォーマットかが明確 * ❌ `type` - 何のタイプか不明 * ✅ `asset_content_type` - アセットが含むコンテンツの種類を示します * ❌ `asset_type` - まだマシだが他の type フィールドと衝突しうる ### Enum の統合 同じ概念が複数箇所に異なるサブセットで現れる場合: 1. **単一の正準 enum** を作り、取りうる値をすべて含めます 2. すべてのスキーマで `$ref` を用いて参照します 3. サブセットの期待値は必要に応じてフィールド説明に記載します **例:** ```json theme={null} // enums/asset-content-type.json - 単一の真実のソース { "$id": "/schemas/v2/enums/asset-content-type.json", "type": "string", "enum": ["image", "video", "audio", "text", "html", "javascript", ...] } // brand-manifest.json - フル enum を参照 { "asset_type": { "$ref": "/schemas/v2/enums/asset-content-type.json", "description": "Type of asset. Note: Brand manifests typically contain basic media assets (image, video, audio, text)." } } // list-creative-formats-request.json - フル enum を参照 { "asset_types": { "type": "array", "items": { "$ref": "/schemas/v2/enums/asset-content-type.json" } } } ``` **メリット:** * 型ジェネレータが単一で一貫した型を生成します * API は任意の値でのフィルタや指定を許容します * 新しい値の追加は非破壊的です * 典型的な使い方をドキュメントで明示しつつ能力を制限しません ## Specialist Module Naming ### Core Principle スペシャリストモジュール名は、教えられている**技術的能力** — 実践者が何を検証、解決、または運用するか — を反映しなければならず、ビジネスやマーケティングのカテゴリではありません。 「Brand」というタイトルのモジュールは曖昧です: ブランドセーフティポリシー、ブランドアイデンティティスキーマ検証、それともブランドキャンペーン戦略を教えるのか?「Brand Identity & Verification」というタイトルのモジュールは、開発者に何をできるようになるかを正確に伝えます。 ### Naming Consistency モジュール名は、それが現れる 4 つのすべてのサーフェスで一貫していなければなりません。 1. **ページタイトル** — モジュールの `.mdx` ファイルの `title:` フロントマター 2. **バッジ** — `adcp_specialist_*` 資格情報サフィックス(例: `adcp_specialist_signals`) 3. **サイドバーナビゲーション** — `sidebarTitle:` フロントマター 4. **スペシャリスト概要表** — 認定概要ページの行 これらのいずれかが乖離すると、1 つのサーフェスを見る実装者は、別のものを読む人とは異なるメンタルモデルを形成します。4 つすべてを同期に保ちます。 ### Good vs. Bad Names | Avoid | Prefer | Why | | ----- | ----------------------------- | --------------------------------------------------- | | Brand | Brand Identity & Verification | 「Brand」はマーケティングと読める。モジュールはスキーマ検証とアイデンティティ解決を教える | | Ads | Creative Asset Management | 「Ads」は広すぎる。モジュールはクリエイティブフォーマット、アセットパイプライン、承認フローをカバー | | Data | Signals & Audience Activation | 「Data」は汎用的。モジュールはシグナルディスカバリー、プライバシー制御、有効化ループを教える | ### Naming Checklist 新しいスペシャリストモジュールを提案する前に: * [ ] 名前はビジネスドメインではなく技術的ワークフローを記述しているか? * [ ] AdCP に不慣れな開発者が、名前だけからモジュールが何を教えるか理解できるか? * [ ] 名前はページタイトル、バッジ、サイドバー、概要表で一貫しているか? * [ ] バッジサフィックス(`adcp_specialist_*`)は資格情報のコンテキストで自然に読めるか? ## Enum 設計 ### Enum ファイル構成 すべての enum は `/schemas/v2/enums/` に置き、意味がわかる名前にします。 ``` /schemas/v2/enums/ asset-content-type.json # このアセットは何か? format-category.json # この広告はどこに表示されるか? pricing-model.json # どのように課金されるか? media-buy-status.json # バイの状態は? ``` ### Enum の命名規則 * 分類対象を説明する **名詞句** を使います * ファイル名は **kebab-case** * 生成される型名は **PascalCase**(AssetContentType, FormatCategory) * 修飾子のない "type" "kind" "status" のような汎用語は避けてください ### 新しい Enum を作るべきとき 以下に該当する場合は専用の enum ファイルを作成します: * 値が複数スキーマで再利用されます * 値が閉じた選択肢です * プロトコルにとって基本的な概念です * 型安全性が実装者の利便につながる ### Enum membership — when to add a value *既存の* enum に値を追加するのはキュレーションの決定であり、デフォルトではありません。enum は、実在する共有セマンティクスの厳選された名簿であり — すべてのベンダーや統合のレジストリではありません。値は、**すべて**が成り立つときにメンバーシップを獲得します。 * **公開されている** — バイヤーごとや統合ごとの形状ではなく、安定した定義を持つ外部文書化された概念を名付けている。 * **ネイティブにサポートされている** — 少なくとも 1 つの実在する実装者が、値ごとのビスポークマッピングなしに直接処理する(`feed_format` については、セラーが `feed_field_mappings` なしにネイティブにパースする)。 * **共有された需要** — 複数のプロデューサー**かつ**複数のコンシューマーにわたって関連する(単一の双方向統合のためのブランディングではなく、共有された方言)。 既存の値の実質的な**方言**は、その差異が親の値のコンシューマーに誤処理させる場合にのみ、独自の値を獲得します — 名前が変更された主キー、複合エンコードされたフィールド、または親が任意として扱うが方言が要求するフィールド。表面的または追加的-任意の差異はそうではありません。親の値を使います。概念がこれらのテストに失敗する場合、enum 値を作成するのではなく、スキーマの既存の拡張パス(`custom` + マッピング、または `ext`)を通じてモデル化します。 これは [Platform Agnosticism](#platform-agnosticism) とは異なります: `feed_format` 値はベンダーの*公開された仕様*を正当に名付けます(値が仕様**そのもの**)が、プラットフォーム非依存性は一般的概念の*ベンダー固有バージョン*を禁止します。 **ワークド例 — `feed_format`([#3456](https://github.com/adcontextprotocol/adcp/issues/3456))。** `tiktok_shop`、`pinterest_catalog`、`openai_product_feed` は適格です: 公開され、Google Merchant Center 由来のフィード方言で、実在するセラーがネイティブにパースし、それぞれ厳格な GMC パーサーが誤処理するデルタを持ちます。公開されネイティブにパースされる仕様のないフィードは `custom` + `feed_field_mappings` を使います。 ## フィールド設計 ### 判別共用体(Discriminated Unions) オブジェクトに複数の形があり得るときは、明示的な判別フィールドを使います。 ```json theme={null} { "oneOf": [ { "type": "object", "properties": { "delivery_type": { "type": "string", "const": "url" }, "url": { "type": "string" } }, "required": ["delivery_type", "url"] }, { "type": "object", "properties": { "delivery_type": { "type": "string", "const": "inline" }, "content": { "type": "string" } }, "required": ["delivery_type", "content"] } ] } ``` これにより TypeScript の型絞り込みや他言語でのパターンマッチが正しく機能します。 ### 過度なサブセット制限を避ける 技術的理由がない限り、リクエストスキーマで enum 値を不自然に絞り込まないでください。 * ❌ `asset_types` フィルタを「よく使う 7 値」に限定します * ✅ すべての asset content type を許容し、利用者が自由にフィルタできるようにします 特定値が稀なら説明で触れればよいです。使用を阻害してはいけません。 ## スキーマ参照 ### `$ref` を使うとき `$ref` を使うべきもの: * enum 値(常に) * 複数箇所で使うコアデータモデル * 繰り返し使う複雑なネストオブジェクト `$ref` を避けるもの: * 一度きりのシンプルなインラインオブジェクト * リクエスト固有のパラメータ * 強い文脈依存の構造 ### 参照パス すべての `$ref` パスはスキーマルートからの絶対パスにします。 ```json theme={null} // ✅ GOOD: 絶対パス "$ref": "/schemas/v2/enums/asset-content-type.json" // ❌ BAD: 相対パス "$ref": "../../enums/asset-content-type.json" ``` ## Platform Agnosticism **RULE**: 規範的スキーマの**フィールド名**は、一般的概念の特定ベンダーバージョンを表してはなりません(MUST NOT)。プラットフォーム固有のフィールドは `ext.{vendor}` の下に属します。 **Why**: AdCP はプロトコルであり、プラットフォームではありません。スキーマのトップレベルの `google_campaign_id` や `ttd_line_id` という名前のフィールドは、1 つのベンダーのデータモデルを仕様に焼き付け、ロックインを生みます。プロトコルがオープン標準として信頼できるのは、その規範的フィールドサーフェスがベンダー中立である限りにおいてです。 **How**: ベンダー固有のフィールドは `ext.{vendor}` 名前空間(スキーマ: `/schemas/core/ext.json`、ソース: `static/schemas/source/core/ext.json`)に属します。`ext` は `additionalProperties: true` です — 名前空間化は JSON スキーマではなくレビューによって強制される慣例です。 ```json theme={null} // ❌ BAD: vendor name in a normative field (a general concept dressed up as a vendor) { "google_campaign_id": "abc123" } // ✅ GOOD: vendor-specific under ext { "ext": { "gam": { "campaign_id": "abc123" } } } ``` ### External system identifiers **正準の外部識別子空間**を参照する名前は、フィールド名と enum 値の両方で正当です。区別は「ベンダートークンを含むか」ではなく「*プロトコルがすでに一般的概念を持つもののそのベンダーバージョン*を表すか」です。 * `google_campaign_id`(bad) — プロトコルがすでにモデル化する概念(`media_buy_id`)のベンダー固有 ID。`ext.gam` に移動。 * `apple_podcast_id`(正当) — 特定の Apple Podcasts アイテムの正準識別子。マップする一般的概念がない。Apple Podcasts 名前空間が*その*名前空間。 * `nielsen_dma`(正当) — 業界標準の地理区分。「Nielsen 版の地理」ではない。 正当なパターンの既存の例: * 配信プラットフォーム識別子タイプ: `distribution-identifier-type.json` の `amazon_music_id`、`roku_channel_id`(enum 値) * フィードフォーマット: `brand.json` の `google_merchant_center`、`facebook_catalog`(enum 値) — 多くのサードパーティが実装する広く採用されたオープン交換フォーマット * 測定/データ識別子: `get-adcp-capabilities-response` の `nielsen_dma`(フィールド名) * プラットフォーム ID: `apple_podcast_id`、`apple_id`(フィールド名) 適用するルール: 名前が「AdCP がモデル化するもののどのベンダー相当バージョンか?」を尋ねるなら(bad — `ext` を使う)、拒否。名前が「どの外部定義のシステム/フォーマット/識別子空間か?」を尋ねるなら(正当)、許可。フィールド名を許可するとき、`tests/check-platform-agnostic.cjs` の `FIELD_ALLOWLIST` に 1 行の正当化とともに追加します。enum 値を許可するとき、`ENUM_VALUE_ALLOWLIST` にパス修飾されたエントリと 1 行の正当化とともに追加します。 ### Reviewer checklist * 名前が `{vendor}_{general_concept}`(例: `google_campaign_id`、`ttd_line_id`)である新しいトップレベルまたはリクエスト/レスポンスフィールドを拒否。 * 外部定義のシステム、フォーマット、または識別子空間を名付ける enum 値を受け入れ。 * **例ブロック**(メールアドレス、サンプル ID)のベンダー名は問題ありません。 * 不確かなとき、尋ねます: 「このフィールドまたは値は*プロトコルがすでに一般的概念を持つもののあるベンダーのバージョン*を表すか?」。もしそうなら、`ext.{vendor}` の下に属します。 ## Reserved SDK-Internal Keys **RULE**: トップレベルキー `ctx_metadata` は、SDK やプラットフォームアダプターが呼び出し間で運ぶ必要があるがバイヤーが見たり依存したりしてはならない(MUST NOT)状態のアダプター内部ラウンドトリップキャッシュとして、AdCP リソースオブジェクト上で予約されています。アダプターは、ワイヤー送出前に任意のペイロードから `ctx_metadata` を取り除かなければなりません(MUST)。取り除き時にキーが存在し空でなかった場合、アダプターは、オペレーターがカスタムアダプターコードとの偶発的なキー衝突を検出できるよう、warning レベルのログエントリを発しなければなりません(MUST)。(空または不在の `ctx_metadata` はサイレント — 空でない値のみが警告をトリガー。) **Why**: プラットフォームアダプター(例: Google Ad Manager、Kevel、カスタムセラーインフラ)は、アダプター内部の識別子 — GAM 広告ユニット ID、キー/バリューペア、プレースメント ID — を、バイヤー向け SDK が返す AdCP リソースに関連付ける必要があることがよくあります。参照 Prebid `salesagent` Python 実装は、まさにこの目的のために Product モデルの `implementation_config` JSON カラムを使います。予約された名前がなければ、すべての SDK が独自のもの(`implementation_config`、`_internal`、`sdk_state` など)を発明します。4 番目の SDK が次にそれらの 1 つと衝突するか、同じ名前に収束する 2 つの SDK が曖昧なセマンティクスを生成します。1 つの予約された名前が調整問題を除去します。 **Scope**: 予約は、スキーマが `additionalProperties: true` を宣言する AdCP リソースオブジェクト — `Product`、`MediaBuy`、`Package`、`Creative`、`AudienceSegment`、`Signal`、`RightsGrant` を含む — に適用されます。予約は、それが現れるどこでもリソースとともに移動します: レスポンスエンベロープのトップレベル、別のリソース内にネスト(例: `MediaBuy` 内の `Package`)、またはリソースの配列内(例: `products: Product[]` の各要素)。アダプターは、最も外側だけでなく、すべての出現から送出前にキーを取り除かなければなりません(MUST)。 `PropertyList` と `CollectionList` は `additionalProperties: false` を宣言し、フォローアップ PR がそれらのスキーマを広げるまでスコープ外です。それまで、それらのリソースのラウンドトリップ状態を必要とするアダプターは帯域外で追跡すべきです。 **近隣の慣例との区別**: * `ext.{vendor}` — ベンダー名前空間化、**バイヤーに見える**、ワイヤーを移動する。バイヤーが見るべきベンダー固有データに使う(例: `ext.gam.line_item_id`)。 * `context` / `context_id` — 呼び出し元がエコーする相関データ、これもワイヤーに見える。プレフィックス一致にもかかわらず、`ctx_metadata` はこれらのサブ名前空間ではありません — それらは無関係な概念で、異なる層を移動します。 * `ctx_metadata` — **アダプター内部のみ**、送出前に取り除かなければならず(MUST)、決してバイヤーに到達しません。 **Adapter conformance**: ``` 1. Read ctx_metadata from inbound resource (publisher → SDK direction). 2. Carry it in adapter-local state. 3. Before serializing the resource for wire egress (SDK → buyer direction): a. Remove the ctx_metadata key. b. If the key was present and non-empty, emit a warning-level log: "stripping reserved ctx_metadata before egress on " 4. Buyer-facing surfaces MUST NOT expose ctx_metadata in any documentation, typed shape, or example. ``` **Reviewer checklist**: * `ctx_metadata` をバイヤー可読フィールドとして推進する任意の仕様、スキーマ、または例を拒否。 * `ctx_metadata` をバイヤー向けの型付きリターンに表面化する任意の SDK 貢献を拒否。 * 送出取り除き + 警告ログのパスが整っていることを条件に、`ctx_metadata` をアダプター内部状態として読み書きする SDK コードを受け入れ。 ## 破壊的変更 ### 何が破壊的変更か **メジャーバージョンアップが必要:** * enum 値の削除 * フィールド名の変更 * フィールド型の変更 * オプションを必須へ変更 * フィールドの削除 **マイナーバージョンで許容:** * 新しい enum 値の追加(追記のみ) * 新しいオプションフィールドの追加 * 説明の明確化 * 新しいタスク/エンドポイントの追加 ### マイグレーション戦略 破壊的変更を行う場合: 1. **v2 ディレクトリを作成**: `/schemas/v2/` 2. **v1 を維持**: 旧スキーマを動作状態で残す 3. **マイグレーションを記載**: 変更前後の例を提供します 4. **デプリケーション期間**: 定めた期間、両バージョンをサポートします ## JSON Schema Conventions ### Nullable Scalars AdCP 3.x の draft-07 スキーマでは、null 許容のスカラーフィールドを JSON スキーマの型ユニオンとしてエンコードします。 ```json theme={null} { "type": ["string", "null"] } ``` null 許容の数値、整数、ブール値、混合スカラー値バケットにも同じパターンを使います。ソーススキーマに OpenAPI スタイルの `nullable: true` を導入しないでください。それは JSON Schema Draft 07 の一部ではなく、一貫しない SDK 投影ルールを生みます。 null 許容の enum は、`type` ユニオンと `enum` 値セットの両方に `null` を含めなければなりません。 ```json theme={null} { "type": ["string", "null"], "enum": ["active", "paused", null] } ``` JSON Schema Draft 07 では null 許容性と存在は別物です。 * `type: ["string", "null"]` は、フィールドが存在するとき `null` であってよいことを意味します。 * 囲むオブジェクトの `required` 配列が、フィールドが存在しなければならないかを制御します。 * したがって任意の null 許容フィールドは 3 つの状態を持ちます: 省略、`null` で存在、スカラー値で存在。 * 必須の null 許容フィールドは 2 つの状態を持ちます: `null` で存在、またはスカラー値で存在。 省略と明示的な `null` が異なるセマンティクスを運ぶ場合、SDK ジェネレーターがケースを潰さないよう、その区別をフィールドの説明で述べます。 ## スキーマのテスト すべてのスキーマ変更は以下を満たすこと: 1. ✅ JSON Schema Draft 07 で検証に通る 2. ✅ サンプルデータがバリデーションを通過します 3. ✅ Python/TypeScript で型生成に成功します 4. ✅ ドキュメントが変更に追随しています 5. ✅ 変更内容を示す changeset を含めます ## レビュー・チェックリスト スキーマ変更をマージする前に確認します: * [ ] 異なるファイル間で enum 名が重複していません * [ ] あいまいなフィールド名(素の "type" など)がない * [ ] すべての enum が `$ref` 参照になっている(インラインなし) * [ ] 破壊的変更には適切なバージョニングが適用されています * [ ] ドキュメントがスキーマに合わせて更新されています * [ ] サンプルが新スキーマでバリデーションに通る * [ ] 型生成テストが完了しています * [ ] 適切なバージョンアップを含む changeset が作成されています ## 哲学 **「スキーマこそが仕様」** ドキュメントはスキーマを反映すべきですが、真実のソースはスキーマです。ドキュメントとスキーマが乖離した場合はスキーマを優先します。つまり: * スキーマに明確で詳細な説明を書く * 自己説明的なセマンティックな名前を使います * バリデーションだけでなく型生成も意識して設計します * 言語間での開発者体験を考慮します **「正しいことをしやすくする」** 良いスキーマ設計は実装者を正しい使い方へ導きます: * 判別子を使い、型チェッカーが誤りを検出できるようにします * セマンティックな名前でコードを読みやすくします * enum を統合し、きれいな型を生成させる * 必要な箇所だけ制限し、過剰に縛らない ## 質問があるとき スキーマ設計で迷ったら: 1. `/schemas/v2/` の既存パターンを確認します 2. 型生成への影響を考える 3. 「この名前衝突は問題を起こさないか?」と自問します 4. 端的さよりも具体性を優先します 5. 将来のため、このファイルに判断理由を記録します # アクセシビリティ Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/accessibility AdCP アクセシビリティサポートにより、フォーマットは WCAG 準拠レベルを宣言し、alt テキストやキャプションなどのアクセシブルなアセットを必要とすることができます。 AdCP は2つのレベルでアクセシビリティをサポートする: フォーマットはレンダリング出力の準拠レベルを宣言し、アセットはそれを達成するために必要なメタデータを持ちます。 ## 仕組み 広告クリエイティブのアクセシビリティは、誰がレンダリングを制御するかによって異なる: * **フォーマットレンダリングのクリエイティブ**(画像 + ヘッドライン + CTA): フォーマットが出力を制御します。コントラスト比、キーボードナビゲーション、ARIA ランドマークを保証できる — クリエイティブから適切な入力(画像の alt テキスト、ビデオのキャプションなど)が必要なだけ。 * **不透明なクリエイティブ**(HTML バンドル、JavaScript タグ): フォーマットはコンテンツを検査または変更できません。アセットはそのアクセシビリティプロパティを自己宣言しなければなりません。 AdCP はフォーマットの `accessibility` オブジェクトとアセットタイプごとのアクセシビリティメタデータを通じて両方のケースを処理します。 ## フォーマットのアクセシビリティ フォーマットは `accessibility` オブジェクトを通じてアクセシビリティの姿勢を宣言する: ### `accessibility.wcag_level` このフォーマットが生成するクリエイティブが満たす WCAG 準拠レベル。値: `A`、`AA`、`AAA`。 フォーマットレンダリングのクリエイティブでは、これはフォーマットからの保証です。不透明なクリエイティブでは、フォーマットがアセットに自己認証を求めるレベルを反映します。 ### `accessibility.requires_accessible_assets` `true` の場合、アクセシビリティ関連フィールドを持つすべてのアセットはそれらのフィールドを含めなければなりません。これは強制メカニズムだ — オプションのアクセシビリティフィールドを必須として扱うよう検証に指示します。 ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "name": "Display Banner 300x250", "accessibility": { "wcag_level": "AA", "requires_accessible_assets": true }, "assets": [ { "item_type": "individual", "asset_id": "hero_image", "asset_type": "image", "required": true, "requirements": { "min_width": 300, "max_width": 300, "min_height": 250, "max_height": 250, "formats": ["jpg", "png", "webp"] } }, { "item_type": "individual", "asset_id": "headline", "asset_type": "text", "required": true, "requirements": { "max_length": 90 } } ] } ``` このフォーマットは WCAG AA 出力を保証し、画像アセットに `alt_text` を必要とする(`alt_text` が画像アセットタイプのアクセシビリティフィールドとしてマークされているため)。 ## アセットのアクセシビリティフィールド 各アセットタイプは `x-accessibility` スキーママーカーを使用してそのフィールドのどれがアクセシビリティ関連かを定義します。これらのフィールドはデフォルトで常にオプションだが、フォーマットが `accessibility.requires_accessible_assets: true` を設定すると必須になります。 ### 検査可能なアセット これらのアセットは、フォーマットがアクセシブルにレンダリングするために使用する構造化データを提供します。 | アセットタイプ | アクセシビリティフィールド | 目的 | | --------- | ----------------------- | ---------------------------- | | **Image** | `alt_text` | スクリーンリーダー用の代替テキスト | | **Video** | `captions_url` | キャプションファイルへの URL(WebVTT、SRT) | | | `transcript_url` | テキストトランスクリプトへの URL | | | `audio_description_url` | 音声説明トラックへの URL | | **Audio** | `transcript_url` | テキストトランスクリプトへの URL | **例** — アクセシブルフォーマットのマニフェストのビデオアセット: ```json theme={null} { "creative_id": "brand_video_001", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_hosted" }, "assets": { "video_file": { "url": "https://cdn.example.com/video.mp4", "width": 1920, "height": 1080, "duration_ms": 30000, "captions_url": "https://cdn.example.com/video.vtt", "transcript_url": "https://cdn.example.com/video-transcript.txt", "audio_description_url": "https://cdn.example.com/video-ad.mp3" } } } ``` ### 不透明なアセット HTML と JavaScript アセットはブラックボックスだ — フォーマットはそのレンダリングを検査できません。これらのアセットは自己宣言プロパティを持つ `accessibility` オブジェクトを持ちます。 | フィールド | 型 | 説明 | | ---------------------- | ------- | -------------------------------------------------- | | `alt_text` | string | クリエイティブコンテンツを説明するテキスト代替 | | `keyboard_navigable` | boolean | クリエイティブがキーボードで完全に操作できる | | `motion_control` | boolean | `prefers-reduced-motion` を尊重するか、一時停止/停止コントロールを提供する | | `screen_reader_tested` | boolean | クリエイティブがスクリーンリーダーでテストされた | **例** — アクセシビリティ宣言を含む HTML クリエイティブ: ```json theme={null} { "creative_id": "rich_media_001", "format_id": { "agent_url": "https://publisher.com", "id": "rich_media_expandable" }, "assets": { "creative_html": { "content": "
...
", "version": "HTML5", "accessibility": { "alt_text": "Interactive product carousel showing summer collection", "keyboard_navigable": true, "motion_control": true, "screen_reader_tested": true } } } } ``` 自己宣言されたアクセシビリティはトラスト主張です。プラットフォームはこれらのプロパティを独立して検証することがある — それはプロトコルのスコープ外です。 ### サードパーティタグ VAST と DAAST アセットはサードパーティによって配信されるビデオとオーディオをラップします。既存のタグプロパティとともにアクセシビリティフィールドを持ちます。 | アセットタイプ | アクセシビリティフィールド | | --------- | -------------------------------------- | | **VAST** | `captions_url`、`audio_description_url` | | **DAAST** | `transcript_url` | ### アクセシビリティフィールドのないアセット 一部のアセットタイプはスタンドアロンのレンダリングコンテンツを生成せず、アクセシビリティフィールドを持たない。フォーマットが `accessibility.requires_accessible_assets: true` を設定した場合、これらは実質的に何もしない: * **Text** — フォーマットによってレンダリング * **Markdown** — フォーマットによってレンダリング * **CSS** — スタイル、コンテンツではありません * **URL** — リンク、レンダリングコンテンツではありません * **Webhook** — サーバーサイド ## アクセシブルフォーマットの発見 バイヤーは `list_creative_formats` の `wcag_level` パラメーターを使用してアクセシブルなフォーマットをフィルタリングできる: ```json theme={null} { "wcag_level": "AA", "asset_types": ["image", "text"] } ``` これにより少なくとも WCAG AA 準拠を満たし、画像とテキストアセットを受け入れるフォーマットが返されます。フィルターは「少なくとも」ロジックを使用します: `AA` をリクエストすると `AA` または `AAA` のフォーマットが返されます。 ## 実装ノート **適用はアプリケーションレベルです。** `x-accessibility` マーカーは JSON Schema 拡張キーワードです。標準 JSON Schema バリデーターはそれを無視する — `accessibility.requires_accessible_assets` の適用は、`x-accessibility: true` フィールドのアセットスキーマをスキャンしてその存在を検証するアプリケーションコードで実装しなければなりません。 **フォーマット実装者向け:** * 主張を実証できる場合のみ `accessibility.wcag_level` を設定する — 独自のレンダリング保証またはアクセシブルアセットの要求を通じて * フォーマットが構造化入力からレンダリングする場合、レンダリングパイプラインが宣言された WCAG レベル(コントラスト、キーボードナビゲーション、ARIA)を満たすことを確認します * フォーマットが不透明なアセットをラップする場合、`accessibility.requires_accessible_assets: true` により入力が適切な宣言を持つことが保証されます **クリエイティブ制作者向け:** * `accessibility.requires_accessible_assets: true` のフォーマットに送信する場合、アセットタイプのすべてのアクセシビリティフィールドを含めます * 不透明なアセットの場合、宣言する前にアクセシビリティプロパティをテストします * キャプションとトランスクリプトはアセットに埋め込まれたものではなく、別々にホストされたファイルとして提供します ## 関連ドキュメント * [クリエイティブフォーマット](/docs/creative/formats) — フォーマット構造と要件 * [アセットタイプ](/docs/creative/asset-types) — アセット仕様とペイロードスキーマ * [クリエイティブマニフェスト](/docs/creative/creative-manifests) — アセットとフォーマットのペアリング * [list\_creative\_formats](/docs/creative/task-reference/list_creative_formats) — フィルタリングを使ったフォーマット発見 # キャンペーンチーム向け AI クリエイティブ Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/ai-creative-overview AdCP の AI クリエイティブにより、キャンペーンチームはブリーフを書くだけで、プロトコルを学ぶことなく複数のフォーマットで本番対応の広告を生成できます。 AdCP の AI クリエイティブとは、ブリーフを書けば AI エージェントがプロダクションを処理することを意味します。指示から広告を生成し、フォーマット間で適応させ、配信時にパーソナライズすることもできます。ブランドの管理は自分が行います。AI が重労働をします。 このページでは、すでに使い慣れた言葉で仕組みを説明します。 ## 仕組み ワークフローはプロダクションチームとの作業を踏襲します。ただしチームが AI エージェントになります。 ### 1. ブリーフを書く クリエイティブチームにブリーフするのと同じように、何が必要かを平易な言葉で説明します。 *「アウトドアギアラインのホリデーキャンペーンを作ってください。アースカラー、アクティブなライフスタイルのイメージ。ヘッドラインは期間限定オファーを強調してください。」* ブリーフにはメッセージ(何を言うか)、ブランドアイデンティティアセット(ロゴ、カラー、フォント)、制約(トーン、避けるべきトピック)を含めることができます。具体的であるほど、出力が良くなります。 ### 2. コンセプトをレビューします AI がオプションを生成します。プレビューが表示される — 方向性のためのラフなコンセプト、またはステークホルダーレビュー用の洗練されたバージョン。プロダクションスタジオからのコンプをレビューするように閲覧します。 品質レベルを制御できる: * **ドラフトモード**はは探索のための速くてラフなコンセプトを提供します。ティッシュセッションのように考える。 * **プロダクションモード**はクライアント準備ができた洗練された出力を提供します。最終コンプのように考える。 ### 3. 反復します どんなクリエイティブチームとも使うような言葉でフィードバックを与えます。 *「ヘッドラインをより緊急性のあるものにします。」* *「よりウォームなカラーパレットを試してみます。」* *「レイアウトは維持しつつ、プロダクトショットの代わりにライフスタイル写真を使います。」* AI が修正して更新されたプレビューを返します。 ### 4. 承認してトラフィックします 最終クリエイティブを確定します。キャンペーンに同期される — メディアバイに割り当てられ、プレースメントにマッチし、配信準備が整う。 ### 5. 実際に配信されたものを監視します ローンチ後、AI が配信したものをレビューします。すべてのバリアント、すべてのコンテキスト。完全な監査証跡。AI がオーディエンスごとにヘッドラインをパーソナライズした場合、各バージョンとそのパフォーマンスを確認できます。 ## 品質管理 AI クリエイティブには2つの別々の品質ディメンションがあります。両方を理解することで驚きを防ぐ。 **コンセプトクオリティ**はクリエイティブ自体についてです。ドラフトモードはスピーディーでラフなアイデアを生成する — 方向性を探っていてピクセルパーフェクトな出力が必要でない場合に便利。プロダクションモードはクライアントレビューとローンチに適した完成した作品を生成します。プロセスのどこにいるかに応じてどちらのモードを使用するかを選択します。 **プレビュークオリティ**は出力の表示方法についてです。クイックサムネイルで多くのオプションを素早くスキャンできます。フルフィデリティレンダーは最終サイズと解像度で広告がどのように見えるかを正確に示します。これらはコンセプトクオリティとは独立している — ドラフトコンセプトの高フィデリティレンダーを得ることも、プロダクション対応の作品のクイックサムネイルを得ることもできます。 ティッシュセッション(ラフなコンセプト、速い反復)と最終プレゼンテーション(洗練された作品、高解像度モックアップ)の違いのように考える。 ## ブランドセーフティ プロセス全体でブランドを守る4つの層があります。 **ブリーフ自体。** クリエイティブの指示が境界を設定します。トーン、含めるべきトピック、避けるべきトピック、禁止事項を指定します。AI はこれらの制約の中で作業します。 **ブランドアイデンティティ。** ロゴ、カラーパレット、タイポグラフィ、ブランドガイドラインは構造化アセットとして提供されます。AI は生成中にこれらを参照する — 提案としてではなく要件として。 **ローンチ前レビュー。** 何かが公開される前に、AI が生成するものをプレビューします。コンセプトをレビューし、エッジケースをテストし、1つのインプレッションも配信される前にシステムを承認します。 **ローンチ後の監査。** ローンチ後、実際に配信されたすべてのバリアントを確認します。サマリーではない — 実際のクリエイティブ出力と、それぞれがいつどこで配信されたかのコンテキスト。 ## 期待することは何か AI 生成広告は従来のプロダクションとはいくつかの重要な点で異なります。 **プレビューは代表的であり、正確ではありません。** AI はインプレッションごとに生成できる(コンテキスト、オーディエンス、プレースメントに適応します)ため、ローンチ前のプレビューは AI が*生成するもの*を示すが、配信される1つの固定広告ではありません。プレビューはブリーフとブランドアイデンティティに正確だが、ライブキャンペーンはバリエーションを生成することがあります。 **会話型フォーマットにはガードレールテストが必要です。** 広告にインタラクティブなチャットボットや会話型要素が含まれる場合、境界をテストします。誰かが競合他社について尋ねたらどうなるか? トピック外の質問をしたら? ローンチ前レビューにはこれらのシナリオを含めるべきです。 **すべての個別広告ではなく、システムを承認します。** 従来のクリエイティブでは、各完成した広告を承認します。AI クリエイティブでは、AI が広告生成に使用するブリーフ、ブランドアイデンティティ、ガードレールの組み合わせを承認します。これがスケールでのパーソナライゼーションを可能にするものだ — そしてなぜブリーフとガードレールを正しく設定することが、すべての出力をレビューすることよりも重要なのか。 ## エージェンシー言語でのプロトコル用語 | あなたが呼ぶもの | AdCP が呼ぶもの | | -------------------- | ----------------------------------------------------- | | クリエイティブブリーフ | `build_creative` の `message` または `assets.brief` | | コンプ / モックアップ | `preview_creative` からのプレビュー | | 広告ユニット仕様書 | クリエイティブマニフェスト | | プロダクションスタジオ | クリエイティブエージェント | | プレースメントサイズ | フォーマット(例: `display_300x250`) | | トラフィッキング | `sync_creatives` または `create_media_buy` のインラインアタッチメント | | キャンペーンレポート(バリアントレベル) | `get_creative_delivery` | ## 次のステップ さらに深く掘り下げる準備ができたら: * **実際に確認する** — [クリエイティブプロトコルの概要](/docs/creative)では CTV、ディスプレイ、ソーシャルにわたってブリーフからデリバリーまでのストラテジストの流れをたどる * **技術的なワークフロー** — [生成クリエイティブ](/docs/creative/generative-creative)は API を段階的にウォークスルーします * **ライブラリ管理** — [クリエイティブライブラリとコンセプト](/docs/creative/creative-libraries)はアセットの整理と同期をカバーします * **CTV とビデオ** — [CTV とコネクテッド TV](/docs/creative/channels/ctv) は SSAI/CSAI デリバリー、コンパニオン広告、VAST タグをカバーします * **マルチエージェントオーケストレーション** — [マルチエージェントクリエイティブオーケストレーション](/docs/creative/multi-agent-orchestration)はセラー間でのクリエイティブ配布をカバーします * **ブランドセーフティの詳細** — [セールスエージェントのクリエイティブ機能](/docs/creative/sales-agent-creative-capabilities)はガードレールとインラインクリエイティブ管理を説明します * **ラーニング** — [認定プログラム](/docs/learning/overview)は Addie とのインタラクティブモジュールを通じて AdCP を教える # Asset Types Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/asset-types AdCP のアセットタイプは、クリエイティブフォーマットで使われる画像、動画、テキスト、オーディオ、タグ、トラッキング URL の標準化されたプロパティを定義します。 > **正準フォーマットの読者へ**: このページはアセットタイプとそのペイロード形状を説明します——v1 と正準フォーマットのパスで同じです。アセットが `asset_group_id` を介して正準フォーマットのスロットにどうマップされるかは、[canonical-formats](/docs/creative/canonical-formats) と[マイグレーションガイド](/docs/creative/canonical-formats-migration)を参照してください。v1 は `asset_id` + `asset_role` を使い、正準フォーマットは正準語彙レジストリを参照する `asset_group_id` を使います。両方のパスは同じアセットペイロードスキーマを使い、スロットキーの語彙だけが異なります。 AdCP のクリエイティブフォーマットは、明確に定義されたプロパティを持つ標準化されたアセットタイプを使います。アセットは、フォーマットが要件を定義するために、マニフェストが具体的な値を供給するために使う、離散的で型付けされた構成要素です。 アセットタイプを標準化することで、フォーマット間の一貫性が保証され、バイヤーやシステムが要件を理解しやすくなります。 ## v2 のアセットタイプ v2 フォーマットの `slots` 宣言の `asset_type` フィールドで有効なアセットタイプの完全な集合: | asset\_type | 何を運ぶか | 定義場所 | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | | `image` | 画像ファイル(jpg/png/gif/webp/svg) | `/schemas/core/assets/image-asset.json` | | `video` | 動画ファイル(mp4/webm/mov) | `/schemas/core/assets/video-asset.json` | | `audio` | オーディオファイル(mp3/aac/wav) | `/schemas/core/assets/audio-asset.json` | | `text` | プレーンテキスト(見出し、本文、スクリプト) | `/schemas/core/assets/text-asset.json` | | `markdown` | Markdown テキスト | `/schemas/core/assets/markdown-asset.json` | | `url` | `url_type` 判別子を持つ URL(clickthrough、tracker\_pixel、サードパーティタグ) | `/schemas/core/assets/url-asset.json` | | `html` | インライン HTML | `/schemas/core/assets/html-asset.json` | | `css` | CSS ルール | `/schemas/core/assets/css-asset.json` | | `javascript` | JavaScript | `/schemas/core/assets/javascript-asset.json` | | `vast` | VAST タグ(URL またはインライン XML)、VAST 2.x〜4.x | `/schemas/core/assets/vast-asset.json` | | `daast` | DAAST タグ(URL またはインライン XML)、1.0〜1.1 | `/schemas/core/assets/daast-asset.json` | | `webhook` | 非同期クリエイティブ生産のためのウェブフック URL | `/schemas/core/assets/webhook-asset.json` | | `brief` | 自由テキストのクリエイティブブリーフ(生成的生産への入力) | `/schemas/core/assets/brief-asset.json` | | `catalog` | 同期されたカタログへの参照(sponsored\_placement) | `/schemas/core/assets/catalog-asset.json` | | `published_post` | 認可/レビュー後にセラーが解決する、すでに公開された投稿への参照 | `/schemas/core/assets/published-post-asset.json` | | `zip` | Zip アーカイブ(HTML5 バナーバンドル) | `/schemas/core/assets/zip-asset.json` | | `vast_tracker` | 単一の VAST `Tracking` イベント URL(分解された) | `/schemas/core/assets/vast-tracker-asset.json` | | `daast_tracker` | 単一の DAAST `Tracking` イベント URL(分解された) | `/schemas/core/assets/daast-tracker-asset.json` | | `pixel_tracker` | 任意のウェブレンダリングされる正準向けの、レンダラー発火の HTTP トラッカー(画像ピクセルまたは JS インクルード)。`event`(impression / viewable\_mrc\_\* / viewable\_video\_50 / audible\_video\_complete / click / custom)× `method`(img / js)。IAB OpenRTB Native 1.2 の `imptrackers[]` / `jstracker` / `eventtrackers[]` / `link.clicktrackers[]` イベントタイプレジストリ(タイプ 1、2、3、4、500)にマップ | `/schemas/core/assets/pixel-tracker-asset.json` | | `object` | 構造化オブジェクト(image\_carousel の `cards`、生成的動画向けの `video_brief`)——サブ形状は正準が宣言する | (スタンドアロンのスキーマなし。正準ごとのサブ形状) | v2 マニフェストの `assets` マップでは、**スロットキー**は正準の `asset_group_id`(例: `image_main`、`video_main`、`script`、`cards`、`landing_page_url`)で、**値**は `asset_type` 判別子を持つ対応するアセットペイロードを運びます。フォーマット宣言の `slots[].asset_type` が、どのペイロードスキーマが適用されるかをバリデーターに伝えます。 公開済み投稿の参照も同じルールを使います: 既存の投稿を受け入れるプロダクトは `published_post` スロットを宣言し、マニフェストの値は `post_url` または `platform_post_id` とともに `asset_type: "published_post"` を運びます。 ## 重要: ペイロード vs 要件 ペイロードスキーマ(クリエイティブマニフェストで供給される実際のアセットデータの構造)については、次を参照してください: * [アセットタイプレジストリ](https://adcontextprotocol.org/schemas/v3/creative/asset-types/index.json) - すべてのペイロードスキーマへのリンク * `/schemas/v3/core/assets/` のコアアセットスキーマ - 個々のアセットペイロード定義 **重要な区別:** **フォーマット要件**(このドキュメント)は次のような制約を定義します: * アセットが必須か任意か * 受け入れ可能なファイルまたはコンテナフォーマット * 長さ、寸法、アスペクト比の制限 * ファイルサイズとビットレートの制限 * 許可または制限された機能(タグベースのアセット向け) **ペイロードスキーマ**(コアスキーマ)は次のような供給される値を定義します: * `url` * `content`(インラインテキストまたはインラインタグのマークアップ向け) * `width` / `height`(宣言された場合) * `duration_ms`(該当する場合) * `format`(宣言されたコンテナタイプ) ## アセットタイプのスキーマ アセットタイプの公式 JSON スキーマは次で入手できます: * **本番**: [https://adcontextprotocol.org/schemas/asset-types-v1.json](https://adcontextprotocol.org/schemas/asset-types-v1.json) * **GitHub**: [https://github.com/adcontextprotocol/adcp/blob/main/static/schemas/asset-types-v1.json](https://github.com/adcontextprotocol/adcp/blob/main/static/schemas/asset-types-v1.json) ## コアアセットタイプ ### 動画アセット 動画アセットは、特定の技術要件を持つ動画ファイルを表します。 ```json theme={null} { "asset_type": "video", "required": true, "duration_seconds": 15, "acceptable_formats": ["mp4"], "acceptable_codecs": ["h264"], "acceptable_resolutions": ["1920x1080", "1280x720"], "aspect_ratio": "16:9", "max_file_size_mb": 30, "min_bitrate_mbps": 8, "max_bitrate_mbps": 10 } ``` **プロパティ:** * `duration_seconds`: 想定される動画の長さ * `min_duration_seconds` / `max_duration_seconds`: 長さの範囲(柔軟な場合) * `acceptable_formats`: コンテナフォーマット(mp4、webm、mov) * `acceptable_codecs`: 動画コーデック(h264、h265、vp8、vp9、av1) * `acceptable_resolutions`: width x height 文字列のリスト * `aspect_ratio`: 必須のアスペクト比(16:9、9:16、1:1 など) * `max_file_size_mb`: 最大ファイルサイズ(メガバイト) * `min_bitrate_mbps` / `max_bitrate_mbps`: ビットレート範囲(Mbps) * `features`: 追加要件(例: \["non-skippable", "sound on"]) ### 画像アセット バナー、ロゴ、ビジュアルコンテンツ向けの静止画像アセット。 ```json theme={null} { "asset_type": "image", "required": true, "width": 300, "height": 250, "acceptable_formats": ["jpg", "png", "gif"], "max_file_size_kb": 200, "animation_allowed": true } ``` **プロパティ:** * `width` / `height`: ピクセル単位の寸法 * `min_width` / `min_height`: 最小寸法(px。通常、レスポンシブ/サイズレスフォーマットで使用) * `aspect_ratio`: 必須のアスペクト比 * `acceptable_formats`: 画像フォーマット(jpg、png、gif、webp、svg) * `max_file_size_kb`: 最大ファイルサイズ(キロバイト) * `transparency`: 透明性が必須/サポートされるか * `animation_allowed`: アニメーション GIF が受け入れられるか * `notes`: 追加要件(例: "Must be free of text") **ユースケース:** * 固定レイアウト: `width` と `height` を提供します。`min_width`、`min_height`、`aspect_ratio` を含めません。 * レスポンシブ(固定の画像アスペクト比): `min_width`、`min_height`、`aspect_ratio` を提供します。`width` や `height` を含めません。 * レスポンシブ(任意の画像アスペクト比): `min_width` と `min_height` のみを提供します。`width`、`height`、`aspect_ratio` を含めません。 **注**: 固定レイアウトでは、画像スロットは正確なピクセルボックスなので `width` と `height` を指定します。レスポンシブレイアウトでは、レンダラーが画像をリサイズします。スケーリング後にシャープな結果を得るのに十分なピクセルを確保するために `min_width`/`min_height` を使います。画像アセット自体が特定の形状(例: 16:9)でなければならない場合にのみ `aspect_ratio` を使い、任意の画像アスペクト比が受け入れられる場合は省略します。 ### テキストアセット 見出し、説明、CTA などのテキストコンテンツ。 ```json theme={null} { "asset_type": "text", "required": true, "text_type": "headline", "max_length": 90, "min_length": 10 } ``` **プロパティ:** * `text_type`: 具体的なタイプ(title、headline、description、body、cta、advertiser\_name、disclaimer) * `max_length`: 最大文字数 * `min_length`: 最小文字数 * `default`: 提供されない場合のデフォルト値 * `allowed_characters`: 検証用の正規表現パターン * `format`: 想定されるフォーマット(plain、currency、percentage) ### URL アセット クリックスルー、トラッキング、ランディングページ向けのリンク。関連するが異なる二つのフィールドが URL アセットを記述します: * **`url_type`**(マニフェストのアセット上)— 受信者がこの URL を呼び出すために使う**メカニズム**。 * **`url-asset-requirements.role`**(フォーマット上)— この URL スロットがクリエイティブで果たす**目的**。 スロットは `click_tracker`(目的)でありながら `tracker_pixel`(メカニズム)の URL を受け入れられます——それらは異なるものを記述します。 #### マニフェスト側: `url_type`(メカニズム) 送信者はすべての URL アセットに `url_type` を含めるべきです(**SHOULD**)。有効な値は次のとおりです: | 値 | メカニズム | | ---------------- | ------------------------------------------------------------------- | | `clickthrough` | ユーザークリックの遷移先(ランディングページまたはアドテックのリダイレクタ) | | `tracker_pixel` | HTTP GET を発火し、1×1 ピクセルまたは 204 レスポンスを期待(インプレッション / イベント / 3P トラッカー) | | `tracker_script` | `" } } } ``` ### HTML タグフォーマット ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_728x90_html" }, "type": "display", "assets": [ { "asset_id": "tag", "asset_type": "html", "asset_role": "third_party_tag", "required": true, "requirements": { "width": 728, "height": 90, "max_file_size_kb": 200, "https_required": true } } ] } ``` HTML タグマニフェスト: ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_728x90_html" }, "assets": { "tag": { "asset_type": "html", "content": "" } } } ``` ## HTML5 複数アセットフォーマット HTML5 フォーマットは複数アセットを指定し、パブリッシャー側アドサーバーがインタラクティブクリエイティブに組み立てます。 ### HTML5 バナーフォーマット ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_html5" }, "type": "display", "assets": [ { "asset_id": "background_image", "asset_type": "image", "asset_role": "background", "required": true, "requirements": { "width": 300, "height": 250, "file_types": ["jpg", "png"] } }, { "asset_id": "logo", "asset_type": "image", "asset_role": "logo", "required": true, "requirements": { "max_width": 100, "max_height": 50, "file_types": ["png", "svg"] } }, { "asset_id": "headline", "asset_type": "text", "asset_role": "headline", "required": true, "requirements": { "max_length": 25 } }, { "asset_id": "cta_text", "asset_type": "text", "asset_role": "call_to_action", "required": true, "requirements": { "max_length": 15 } } ] } ``` HTML5 マニフェスト: ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_html5" }, "assets": { "background_image": { "asset_type": "image", "url": "https://cdn.brand.com/bg.jpg", "width": 300, "height": 250 }, "logo": { "asset_type": "image", "url": "https://cdn.brand.com/logo.png", "width": 80, "height": 40 }, "headline": { "asset_type": "text", "content": "Spring Sale - 50% Off" }, "cta_text": { "asset_type": "text", "content": "Shop Now" }, "landing_url": { "asset_type": "url", "url_type": "clickthrough", "url": "https://brand.com/spring" } } } ``` ## レスポンシブディスプレイフォーマット レスポンシブフォーマットはプレースメントの文脈に応じて複数サイズに適応します。 ### レスポンシブバナーフォーマット ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_responsive" }, "type": "display", "responsive": true, "supported_sizes": ["300x250", "728x90", "320x50"], "assets": [ { "asset_id": "background_image", "asset_type": "image", "asset_role": "background", "required": true, "requirements": { "min_width": 728, "min_height": 250, "responsive": true, "file_types": ["jpg", "png", "webp"] } }, { "asset_id": "logo", "asset_type": "image", "asset_role": "logo", "required": true }, { "asset_id": "headline", "asset_type": "text", "asset_role": "headline", "required": true, "requirements": { "max_length": 30 } } ] } ``` ## リッチメディアフォーマット ### エキスパンダブルバナーフォーマット ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_970x250_expandable" }, "type": "display", "expandable": true, "collapsed_size": "970x250", "expanded_size": "970x600", "assets": [ { "asset_id": "collapsed_creative", "asset_type": "html", "asset_role": "collapsed_state", "required": true, "requirements": { "width": 970, "height": 250, "max_file_size_kb": 200 } }, { "asset_id": "expanded_creative", "asset_type": "html", "asset_role": "expanded_state", "required": true, "requirements": { "width": 970, "height": 600, "max_file_size_kb": 500 } } ] } ``` ## ディスプレイ専用マクロ [ユニバーサルマクロ](/docs/creative/universal-macros) に加えて、ディスプレイフォーマットでは以下をサポートします。 ### プレースメントコンテキスト * `{PLACEMENT_ID}` - IAB Global Placement ID * `{FOLD_POSITION}` - above\_fold / below\_fold * `{AD_WIDTH}` / `{AD_HEIGHT}` - 広告枠のピクセル寸法 ### Web コンテキスト * `{DOMAIN}` - パブリッシャードメイン(例: "nytimes.com") * `{PAGE_URL}` - ページの完全 URL(URL エンコード) * `{REFERRER}` - HTTP リファラ URL * `{KEYWORDS}` - ページキーワード(カンマ区切り) ### デバイスコンテキスト * `{DEVICE_TYPE}` - mobile / tablet / desktop * `{OS}` - iOS, Android, Windows, macOS * `{USER_AGENT}` - 完全な UA 文字列 **ディスプレイマクロを用いた例:** ``` https://track.brand.com/imp? buy={MEDIA_BUY_ID}& placement={PLACEMENT_ID}& domain={DOMAIN}& fold={FOLD_POSITION}& device={DEVICE_TYPE}& cb={CACHEBUSTER} ``` ## よく使われるファイル仕様 ### 画像要件 * **ファイルタイプ**: JPG, PNG, WebP, GIF * **最大ファイルサイズ**: * 標準バナー: 150〜200KB * 大型フォーマット (970x250): 300KB * アニメ GIF: 500KB * HTML5 初期ロード: 200KB ### サードパーティタグ要件 * **HTTPS 必須**: すべてのタグ URL は安全なプロトコルを使用 * **最大ファイルサイズ**: タグ内容 200KB * **非同期ロード**: ページ表示をブロックしないこと ### アニメーション仕様 アニメ GIF フォーマットの場合: * `animated`: true * `animation_duration_ms`: アニメーション時間(ミリ秒) * よくある長さ: 15000ms(15 秒) ## 関連ドキュメント * [ユニバーサルマクロ](/docs/creative/universal-macros) - 完全なマクロリファレンス * [Creative Manifests](/docs/creative/creative-manifests) - マニフェストの構造と検証 * [Asset Types](/docs/creative/asset-types) - 画像/HTML/JavaScript アセットの仕様 # DOOH (デジタル屋外広告) Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/channels/dooh このガイドでは、デジタルビルボード、交通機関のスクリーン、店舗や施設のディスプレイ向けに、AdCP がデジタル屋外広告 (DOOH) フォーマットをどのように表現するかを解説します。 ## DOOH フォーマットの特性 DOOH フォーマットは他のデジタル広告と異なり次の特徴があります。 * 公共空間の物理スクリーンに表示 * 空港・モール・高速道路など会場コンテキストを含みます * デバイス ID ではなく会場ベースのインプレッショントラッキングを使用 * クリック先 URL はなく、代わりに QR コードを使用 * 多くの場合オーディオなしで表示 ## 標準的な DOOH フォーマット ### デジタルビルボード(横型) ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "dooh_billboard_1920x1080" }, "type": "dooh", "assets": [ { "asset_id": "billboard_image", "asset_type": "image", "asset_role": "hero_image", "required": true, "requirements": { "width": 1920, "height": 1080, "file_types": ["jpg", "png"], "max_file_size_kb": 1000 } }, { "asset_id": "impression_tracker", "asset_type": "url", "url_type": "tracker", "required": true } ] } ``` ### トランジットスクリーン(縦型) ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "dooh_transit_1080x1920" }, "type": "dooh", "assets": [ { "asset_id": "screen_image", "asset_type": "image", "asset_role": "hero_image", "required": true, "requirements": { "width": 1080, "height": 1920, "aspect_ratio": "9:16", "file_types": ["jpg", "png"] } }, { "asset_id": "impression_tracker", "asset_type": "url", "url_type": "tracker", "required": true } ] } ``` ### ビデオビルボード ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "dooh_video_15s" }, "type": "dooh", "assets": [ { "asset_id": "video_file", "asset_type": "video", "asset_role": "hero_video", "required": true, "requirements": { "duration": "15s", "width": 1920, "height": 1080, "format": ["MP4"], "audio_required": false, "max_file_size_mb": 50 } }, { "asset_id": "impression_tracker", "asset_type": "url", "url_type": "tracker", "required": true } ] } ``` ## DOOH のインプレッショントラッキング DOOH では「proof-of-play」とも呼ばれるインプレッショントラッカーを使い、クリエイティブが物理スクリーンで表示されたことを検証します。DOOH 固有のマクロを含む標準的な URL アセットです。 ```json theme={null} { "asset_id": "impression_tracker", "asset_type": "url", "url_type": "tracker", "required": true, "requirements": { "required_macros": [ "SCREEN_ID", "PLAY_TIMESTAMP", "VENUE_LAT", "VENUE_LONG" ] } } ``` 仕組み自体はデジタル広告のインプレッショントラッキングと同じで、表示時に URL が発火します。違いは、デバイス ID ではなく物理的な会場コンテキストをマクロで送る点です。 ## クリエイティブマニフェスト ### 静止画ビルボードのマニフェスト ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "dooh_billboard_1920x1080" }, "assets": { "billboard_image": { "asset_type": "image", "url": "https://cdn.brand.com/dooh_billboard.jpg", "width": 1920, "height": 1080 }, "impression_tracker": { "asset_type": "url", "url_type": "tracker", "url": "https://track.brand.com/pop?buy={MEDIA_BUY_ID}&screen={SCREEN_ID}&venue={VENUE_TYPE}&ts={PLAY_TIMESTAMP}&lat={VENUE_LAT}&long={VENUE_LONG}" } } } ``` ### ビデオビルボードのマニフェスト ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "dooh_video_15s" }, "assets": { "video_file": { "asset_type": "video", "url": "https://cdn.brand.com/dooh_15s.mp4", "duration": 15, "width": 1920, "height": 1080, "audio": false }, "impression_tracker": { "asset_type": "url", "url_type": "tracker", "url": "https://track.brand.com/pop?buy={MEDIA_BUY_ID}&screen={SCREEN_ID}&ts={PLAY_TIMESTAMP}" } } } ``` ## DOOH 固有のマクロ [ユニバーサルマクロ](/docs/creative/universal-macros) に加えて、DOOH では次をサポートします。 ### 会場情報 * `{SCREEN_ID}` - スクリーンのユニーク ID * `{VENUE_TYPE}` - 空港、モール、交通機関、高速道路、小売など * `{VENUE_NAME}` - 特定の会場名 * `{VENUE_LAT}` / `{VENUE_LONG}` - GPS 座標 ### 再生情報 * `{PLAY_TIMESTAMP}` - クリエイティブが表示された時刻(Unix タイムスタンプ) * `{DWELL_TIME}` - その場所での平均滞留時間(秒) * `{LOOP_LENGTH}` - 広告ローテーション全体の長さ(秒) **インプレッショントラッキング URL の例:** ``` https://track.brand.com/imp? buy={MEDIA_BUY_ID}& screen={SCREEN_ID}& venue={VENUE_TYPE}& venue_name={VENUE_NAME}& ts={PLAY_TIMESTAMP}& lat={VENUE_LAT}& long={VENUE_LONG}& dwell={DWELL_TIME} ``` ## よく使われるアスペクト比 * **16:9** (1920x1080) - 横型ビルボードや高速道路のスクリーン * **9:16** (1080x1920) - 縦型の交通機関・リテールディスプレイ * **1:1** (1080x1080) - 正方形フォーマット ## インプレッションの検証 DOOH のインプレッショントラッカーは次を確認します。 * クリエイティブが実際に物理スクリーンに表示されたか * 表示された正確なタイムスタンプ * スクリーンの場所と会場コンテキスト **取得されるインプレッションデータの例:** ```json theme={null} { "media_buy_id": "mb_dooh_q1", "screen_id": "LAX_T1_GATE24", "venue_type": "airport", "venue_lat": "33.9416", "venue_long": "-118.4085", "play_timestamp": "1704067200", "dwell_time_seconds": "45" } ``` 他チャネルのインプレッショントラッキングと同様に、表示時に URL が発火し、請求やレポートのための検証データを提供します。 ## 関連ドキュメント * [ユニバーサルマクロ](/docs/creative/universal-macros) - DOOH マクロを含む完全なリファレンス * [クリエイティブマニフェスト](/docs/creative/creative-manifests) - マニフェストの構造と例 * [Asset Types](/docs/creative/asset-types) - URL アセットの仕様 # プリント広告 Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/channels/print AdCP のプリント広告フォーマットは、物理寸法、ブリード、DPI、CMYK 色空間要件を伴う新聞、雑誌、業界誌広告をカバーする。 このガイドは、AdCP が新聞、雑誌、インサート、業界誌のプリント広告フォーマットをどう表現するかをカバーします。 ## AdCP でプリントがどう機能するか プリントは他のすべてのチャネルと同じビルディングブロックを使います: * **コレクション** が出版物をモデル化(Vogue、Bergedorfer Zeitung、Ad Age) * **インストールメント** が号をモデル化(2026 年 3 月号、Issue #47) * **インストールメント締切** がブッキング、キャンセル、素材締切日を運ぶ * **クリエイティブフォーマット** が物理寸法、ブリード、DPI、ファイル要件を定義 * **プレースメント** がポジションを定義(フルページ、ハーフページ、アイランド、インサイドフロントカバー) プリント固有のスキーマは不要です。標準製品モデルは、セラーがフォーマットに物理単位を、インストールメントに締切を宣言するときプリントを処理します。 ## プリントフォーマット特性 プリントフォーマットはいくつかの方法でデジタルと異なります: * **物理単位** — ピクセルではなくインチまたはセンチメートルの寸法 * **DPI 要件** — 標準プリントは最小 300 DPI、新聞は 150 DPI * **ブリード** — 裁断後の白い縁を防ぐトリムを超えた追加画像領域 * **色空間** — フルカラープリントは CMYK、白黒はグレースケール * **ファイル形式** — JPG/PNG ではなくプレス対応 PDF、TIFF、または EPS ## 標準プリントフォーマット ### フルページ(雑誌) ```json theme={null} { "format_id": { "agent_url": "https://ads.publisher.example.com", "id": "full_page" }, "name": "Full Page", "renders": [{ "role": "primary", "dimensions": { "width": 8.375, "height": 10.875, "unit": "inches" } }], "assets": [ { "item_type": "individual", "asset_id": "artwork", "asset_type": "image", "asset_role": "print_artwork", "required": true, "requirements": { "min_width": 8.375, "max_width": 8.375, "min_height": 10.875, "max_height": 10.875, "unit": "inches", "min_dpi": 300, "bleed": { "uniform": 0.125 }, "color_space": "cmyk", "formats": ["pdf", "tiff", "eps"] }, "overlays": [ { "id": "safe_left", "description": "Left trim margin — keep headlines, logos, and CTAs inside", "bounds": { "x": 0, "y": 0, "width": 0.25, "height": 10.875, "unit": "inches" } }, { "id": "safe_right", "description": "Right trim margin", "bounds": { "x": 8.125, "y": 0, "width": 0.25, "height": 10.875, "unit": "inches" } }, { "id": "safe_top", "description": "Top trim margin", "bounds": { "x": 0, "y": 0, "width": 8.375, "height": 0.25, "unit": "inches" } }, { "id": "safe_bottom", "description": "Bottom trim margin", "bounds": { "x": 0, "y": 10.625, "width": 8.375, "height": 0.25, "unit": "inches" } } ] } ] } ``` ### ハーフページ縦(新聞) ```json theme={null} { "format_id": { "agent_url": "https://ads.publisher.example.com", "id": "half_page_portrait" }, "name": "1/2 Seite Hochformat", "renders": [{ "role": "primary", "dimensions": { "width": 130, "height": 185, "unit": "mm" } }], "assets": [ { "item_type": "individual", "asset_id": "artwork", "asset_type": "image", "asset_role": "print_artwork", "required": true, "requirements": { "min_width": 130, "max_width": 130, "min_height": 185, "max_height": 185, "unit": "mm", "min_dpi": 150, "bleed": { "uniform": 3 }, "color_space": "cmyk", "formats": ["pdf", "tiff"] } } ] } ``` ### インサート/サプリメント 複数ページのインサートはページに繰り返し可能グループを使います: ```json theme={null} { "format_id": { "agent_url": "https://ads.publisher.example.com", "id": "insert_4page" }, "name": "4-Page Insert", "renders": [{ "role": "primary", "dimensions": { "width": 8.375, "height": 10.875, "unit": "inches" } }], "assets": [ { "item_type": "repeatable_group", "asset_group_id": "pages", "required": true, "min_count": 4, "max_count": 4, "selection_mode": "sequential", "assets": [ { "asset_id": "page", "asset_type": "image", "asset_role": "insert_page", "required": true, "requirements": { "min_width": 8.375, "max_width": 8.375, "min_height": 10.875, "max_height": 10.875, "unit": "inches", "min_dpi": 300, "bleed": { "uniform": 0.125 }, "color_space": "cmyk", "formats": ["pdf"] } } ] } ] } ``` ## 出版物と号 出版物は [コレクション](/docs/media-buy/product-discovery/collections-and-installments) にマップされます。各号は締切を伴うインストールメントにマップされます。 ### 出版物(コレクション) ```json theme={null} { "collection_id": "bergedorfer_zeitung", "name": "Bergedorfer Zeitung", "kind": "publication", "description": "Regional daily newspaper for Hamburg-Bergedorf", "genre": ["IAB12"], "genre_taxonomy": "iab_content_3.0", "language": "de", "cadence": "daily", "status": "active", "deadline_policy": { "booking_lead_days": 4, "cancellation_lead_days": 3, "material_stages": [ { "stage": "final", "lead_days": 2, "label": "Druckfertige PDF" } ], "business_days_only": true } } ``` `deadline_policy` で、コレクションはリードタイムルールを一度宣言します。エージェントは各インストールメントの `scheduled_at` から絶対締切を計算します。日刊新聞はもはやすべての号の締切を列挙する必要はありません — ポリシーが一般的なケースをカバーし、個別のインストールメントは必要なとき上書きできます(例: より早い締切の休日版)。 ### 号(締切を伴うインストールメント) ```json theme={null} { "installment_id": "2026-03-28", "name": "Freitag, 28. März 2026", "scheduled_at": "2026-03-28T05:00:00+01:00", "status": "scheduled", "deadlines": { "booking_deadline": "2026-03-24T17:00:00+01:00", "cancellation_deadline": "2026-03-25T12:00:00+01:00", "material_deadlines": [ { "stage": "draft", "due_at": "2026-03-25T17:00:00+01:00", "label": "Entwurf zur Prüfung" }, { "stage": "final", "due_at": "2026-03-26T17:00:00+01:00", "label": "Druckfertige PDF mit Beschnitt (3mm)" } ] } } ``` より長いリードタイムを持つ月刊誌には: ```json theme={null} { "installment_id": "2026-05", "name": "Mai 2026", "season": "2026", "installment_number": "5", "scheduled_at": "2026-05-01T00:00:00+02:00", "status": "scheduled", "deadlines": { "booking_deadline": "2026-03-15T17:00:00+01:00", "cancellation_deadline": "2026-03-22T17:00:00+01:00", "material_deadlines": [ { "stage": "draft", "due_at": "2026-03-29T17:00:00+01:00", "label": "Raw artwork for review and color proofing" }, { "stage": "final", "due_at": "2026-04-05T17:00:00+02:00", "label": "Press-ready PDF/X-4, CMYK, 300 DPI, 3mm bleed" } ] } } ``` ## 完全な製品例 今後の号全体でプリント広告在庫を販売する地域新聞: ```json theme={null} { "products": [ { "product_id": "bz_display_april", "name": "Bergedorfer Zeitung — Display Ads, April 2026", "channels": ["print"], "collections": [{ "publisher_domain": "bergedorfer-zeitung.de", "collection_ids": ["bergedorfer_zeitung"] }], "publisher_properties": [{ "publisher_domain": "bergedorfer-zeitung.de", "selection_type": "all" }], "installments": [ { "installment_id": "2026-04-01", "name": "Mittwoch, 1. April 2026", "scheduled_at": "2026-04-01T05:00:00+02:00", "status": "scheduled", "deadlines": { "booking_deadline": "2026-03-26T17:00:00+01:00", "cancellation_deadline": "2026-03-27T12:00:00+01:00", "material_deadlines": [ { "stage": "final", "due_at": "2026-03-28T17:00:00+01:00", "label": "Druckfertige PDF" } ] } }, { "installment_id": "2026-04-02", "name": "Donnerstag, 2. April 2026", "scheduled_at": "2026-04-02T05:00:00+02:00", "status": "scheduled", "deadlines": { "booking_deadline": "2026-03-27T17:00:00+01:00", "cancellation_deadline": "2026-03-28T12:00:00+01:00", "material_deadlines": [ { "stage": "final", "due_at": "2026-03-29T17:00:00+01:00", "label": "Druckfertige PDF" } ] } } ], "placements": [ { "kind": "seller_inline", "placement_id": "full_page", "name": "Ganze Seite", "mode": "targetable" }, { "kind": "seller_inline", "placement_id": "half_page", "name": "1/2 Seite", "mode": "targetable" }, { "kind": "seller_inline", "placement_id": "quarter_page", "name": "1/4 Seite", "mode": "targetable" } ], "format_ids": [ { "agent_url": "https://ads.bergedorfer-zeitung.de", "id": "full_page" }, { "agent_url": "https://ads.bergedorfer-zeitung.de", "id": "half_page_portrait" }, { "agent_url": "https://ads.bergedorfer-zeitung.de", "id": "quarter_page" } ], "delivery_type": "guaranteed", "delivery_measurement": { "provider": "IVW (Informationsgemeinschaft zur Feststellung der Verbreitung von Werbeträgern)", "notes": "Verified circulation figures, updated quarterly" }, "pricing_options": [ { "pricing_option_id": "full_page_rate", "pricing_model": "flat_rate", "fixed_price": 2400, "currency": "EUR" }, { "pricing_option_id": "half_page_rate", "pricing_model": "flat_rate", "fixed_price": 1350, "currency": "EUR" } ] } ] } ``` ## プリント固有の画像要件 ### 物理寸法 プリントフォーマットは、ピクセルではなく物理単位(`inches` または `cm`)で寸法を宣言します。フォーマットレンダーと画像アセット要件の両方の `unit` フィールドが解釈を制御します: ```json theme={null} { "dimensions": { "width": 8.375, "height": 10.875, "unit": "inches" } } ``` `unit` が不在のとき、寸法はピクセルにデフォルトします(デジタルフォーマットと後方互換)。 ### ブリード ブリードはトリムサイズを超えた追加画像領域です。印刷後、ページはトリム寸法に裁断されます — ブリードは、白い境界なしにインクカバレッジが縁まで延びることを保証します。 ブリードは辺ごとまたは均一に指定できます: ```json theme={null} "bleed": { "uniform": 0.125 } ``` ```json theme={null} "bleed": { "top": 0.125, "right": 0.125, "bottom": 0.25, "left": 0.125 } ``` 値は親寸法と同じ単位を使います。 ### 総画像寸法の計算 8.375 x 10.875 インチのフルページ雑誌広告、300 DPI で 0.125" 均一ブリードの場合: 1. **総物理サイズ**: (8.375 + 0.125 + 0.125) x (10.875 + 0.125 + 0.125) = 8.625 x 11.125 インチ 2. **ピクセル寸法**: 8.625 x 300 = 幅 2588 ピクセル、11.125 x 300 = 高さ 3338 ピクセル 3. **提出画像**: 2588 x 3338 px、CMYK、PDF または TIFF 130 x 185 mm のヨーロッパ新聞、150 DPI で 3 mm 均一ブリードの場合: 1. **総物理サイズ**: (130 + 3 + 3) x (185 + 3 + 3) = 136 x 191 mm 2. **インチに変換**: 136 / 25.4 = 5.354"、191 / 25.4 = 7.520" 3. **ピクセル寸法**: 5.354 x 150 = 幅 803 px、7.520 x 150 = 高さ 1128 px ### DPI `min_dpi` は許容可能なプリント品質のための最小ドット/インチを指定します: | Use case | Typical min\_dpi | | ---------- | ---------------- | | 雑誌(コート紙) | 300 | | 新聞(非コート) | 150 | | 大判 / ビルボード | 72-150 | ### 色空間 プリント制作は CMYK 色分解を要求します。RGB のデジタル画像はプレス前に変換されなければなりません。`color_space` フィールドはパブリッシャーが受け入れるものを宣言します: * `cmyk` — オフセットとデジタルプリントの標準 * `rgb` — パブリッシャーが変換を処理するとき受け入れられる * `grayscale` — 白黒プレースメント用 ### ファイル形式 | Format | Use case | | ------ | ----------------------- | | `pdf` | プレス対応コンポジット(PDF/X-4 推奨) | | `tiff` | ラスタライズされたアートワーク、ロスレス | | `eps` | 埋め込みフォント付きベクターアートワーク | ### セーフエリア(トリムマージン) プリント制作はページを最終サイズに裁断し、カットはわずかにシフトしうる。トリム縁に近すぎて配置された重要なコンテンツ(ヘッドライン、ロゴ、CTA)は切り取られるリスクがあります。 パブリッシャーは標準の [オーバーレイ](/docs/creative/formats#overlays) パターンを使ってトリムマージンを宣言します — CTV プレイヤーコントロールと DOOH ベゼルに使われるのと同じメカニズム。各オーバーレイは、クリエイティブエージェントが重要なコンテンツの配置を避けるべきゾーンをマークします: ```json theme={null} "overlays": [ { "id": "safe_left", "description": "Left trim margin", "bounds": { "x": 0, "y": 0, "width": 6, "height": 185, "unit": "mm" } }, { "id": "safe_right", "bounds": { "x": 124, "y": 0, "width": 6, "height": 185, "unit": "mm" } }, { "id": "safe_top", "bounds": { "x": 0, "y": 0, "width": 130, "height": 6, "unit": "mm" } }, { "id": "safe_bottom", "bounds": { "x": 0, "y": 179, "width": 130, "height": 6, "unit": "mm" } } ] ``` 6mm マージンはヨーロッパ新聞制作の標準です。オーバーレイ境界は `px` と `fraction` と並んで物理単位(`mm`、`cm`、`inches`)をサポートするため、パブリッシャーはプリプレスワークフローが使うどの単位でもセーフエリアを表現できます。 ## プリントを超えた締切 インストールメント締切はプリント固有ではありません。事前素材要件を持つ任意のチャネルが同じパターンを使います。ポッドキャスト、インフルエンサーホストリード、ライブイベントの締切については [コレクションとインストールメント](/docs/media-buy/product-discovery/collections-and-installments) を参照してください。 ## 関連ドキュメント * [コレクションとインストールメント](/docs/media-buy/product-discovery/collections-and-installments) — コレクション/インストールメントモデルと締切 * [クリエイティブフォーマット](/docs/creative/formats) — フォーマット構造とアセットディスカバリー * [メディアチャネル分類](/docs/reference/media-channel-taxonomy) — プリントを含む全 20 チャネル # ソーシャルとフィードネイティブ Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/channels/social-native AdCP のソーシャル・フィードネイティブ広告フォーマットは、プラットフォームが独自の UI でラップするプロモーテッドポストやスポンサードコンテンツとして、バイヤーが提供するアセットを定義します。 このガイドでは、プラットフォームのフィード内でネイティブコンテンツとしてレンダリングされる広告 — プロモーテッドポスト、スポンサードリスティング、ブーストコンテンツ、その他プラットフォームがバイヤーのアセットを独自のクロームでラップするフォーマット — を AdCP がどのように表現するかを説明します。 フィードネイティブ広告は標準ディスプレイと根本的に異なる: バイヤーはコンテンツアセット(テキスト、画像、リンク)を提供するが、視覚的なプレゼンテーションはプラットフォームが制御します。広告はプラットフォームの UI を継承し — アバター、エンゲージメントボタン、コミュニティバッジ、ダークモード — オーガニックコンテンツと並んで表示されます。 ## AdCP でのフィードネイティブフォーマットの仕組み フィードネイティブ広告をホストするプラットフォームは、バイヤーが提供するアセットのみを定義するフォーマットを持つクリエイティブエージェントを実装します。レイアウト、タイポグラフィ、エンゲージメント UI など — その他はすべてプラットフォームの責任でプレビューと配信時に処理されます。 ### フォーマット定義 フォーマットはバイヤーが提供するものを指定します。その他すべて — レイアウト、タイポグラフィ、エンゲージメント UI — はプラットフォームの責任だ: ```json theme={null} { "format_id": { "agent_url": "https://ads.socialplatform.example", "id": "promoted_post" }, "name": "Promoted post", "type": "native", "description": "Sponsored content that appears in the feed alongside organic posts. Renders with platform chrome (user avatar, engagement buttons, community badge).", "assets": [ { "item_type": "individual", "asset_id": "headline", "asset_type": "text", "required": true, "requirements": { "max_length": 300 } }, { "item_type": "individual", "asset_id": "body", "asset_type": "text", "required": false, "requirements": { "max_length": 1000 } }, { "item_type": "individual", "asset_id": "image", "asset_type": "image", "required": false, "requirements": { "max_width": 1200, "max_height": 628, "accepted_types": ["image/jpeg", "image/png"] } }, { "item_type": "individual", "asset_id": "click_url", "asset_type": "url", "required": true, "requirements": {} } ] } ``` `renders` 配列はフィードネイティブフォーマットではオプションです。プラットフォームがデバイス、フィードコンテキスト、レイアウトルールに基づいてレンダリング時に視覚的なディメンションを決定するためです。 ### プラットフォームクロームを含むプレビュー バイヤーが `preview_creative` を呼び出すと、プラットフォームはバイヤーのアセットを単独で見せるのではなく、フィード体験全体 — アバター、エンゲージメントボタン、コミュニティバッジを含む — を含むプレビューをレンダリングする: ```json theme={null} { "request_type": "single", "creative_manifest": { "format_id": { "agent_url": "https://ads.socialplatform.example", "id": "promoted_post" }, "assets": { "headline": { "content": "Introducing our new trail running collection" }, "body": { "content": "Built for the mountains. Tested on every terrain." }, "image": { "url": "https://cdn.acme-example.com/trail-hero.jpg", "width": 1200, "height": 628 }, "click_url": { "url": "https://acme-example.com/trail-running" } } }, "inputs": [ { "name": "Running community", "context_description": "Appears in r/trailrunning feed between user posts" }, { "name": "General feed", "context_description": "Appears in home feed between mixed content" } ] } ``` `inputs` により、バイヤーは異なるコミュニティコンテキストで広告がどのように見えるかを確認できる — プラットフォームのレンダリングはコミュニティテーマ、コンテンツ密度、フィードポジションによって変わる可能性があります。 ## コミュニティガイドラインとクリエイティブレビュー ソーシャルプラットフォームは標準的な広告ポリシーを超えたコンテンツポリシーを適用する — コミュニティ基準、カテゴリ制限、プロモートコンテンツガイドライン。これらは標準的なクリエイティブレビューフローを通じて表示されます: * `list_creatives` または `get_media_buys` の `rejection_reason` でどのポリシーに違反したかが説明されます * コミュニティ固有の拒否はプラットフォームのグローバルポリシーだけでなく、コミュニティのルールも参照します * 問題を修正した後の再提出は同じ `sync_creatives` のアップサートパターンを踏む 完全なレビューフローについては[クリエイティブレビュー](/docs/creative/sales-agent-creative-capabilities#creative-review)を参照。 ## エンゲージメントとインタラクションモデル フィードネイティブフォーマットは多くの場合、クリックスルーを超えたプラットフォーム固有のインタラクションをサポートする: | インタラクション | AdCP へのマッピング方法 | | --------------------- | -------------------------------------------------------- | | いいね / アップボート / リアクション | プラットフォームが追跡するエンゲージメント、AdCP のクリエイティブアセットではない | | コメント / 返信 | プラットフォームが管理、`get_creative_delivery` のバリアントデータに表示される場合がある | | シェア / リポスト | プラットフォームが追跡、配信メトリクスに含まれる | | セーブ / ブックマーク | プラットフォームが追跡 | | クリックスルー | 標準的な `click_url` アセット | | 投票 / クイズ | 追加のフォーマットアセット(例: `poll_options` テキスト配列) | エンゲージメントメトリクスを公開するプラットフォームは、エンゲージメントタイプがプラットフォームごとに異なるため、各バリアントの `ext` フィールドを通じて `get_creative_delivery` に含めます。 ## カルーセルとマルチカードフォーマット 多くのソーシャルプラットフォームはカルーセルまたはマルチカードのプロモーテッドポストをサポートします。これらは `asset_group` パターンを使用します: ```json theme={null} { "format_id": { "agent_url": "https://ads.socialplatform.example", "id": "promoted_carousel" }, "name": "Promoted carousel", "type": "native", "description": "Multi-card swipeable promoted content with 2-10 cards.", "assets": [ { "item_type": "group", "asset_id": "cards", "min_items": 2, "max_items": 10, "assets": [ { "item_type": "individual", "asset_id": "card_image", "asset_type": "image", "required": true, "requirements": { "min_width": 600, "min_height": 600, "aspect_ratio": "1:1" } }, { "item_type": "individual", "asset_id": "card_headline", "asset_type": "text", "required": true, "requirements": { "max_length": 100 } }, { "item_type": "individual", "asset_id": "card_click_url", "asset_type": "url", "required": true } ] }, { "item_type": "individual", "asset_id": "headline", "asset_type": "text", "required": true, "requirements": { "max_length": 300 } } ] } ``` カードの順序、アスペクト比、スワイプ動作に関するカルーセル固有のガイダンスは[カルーセル](/docs/creative/channels/carousels)を参照。 ## ジェネレーティブ フィードネイティブ AI 搭載の広告生成を持つプラットフォームは、ジェネレーティブ フィードネイティブフォーマットを提供できます。バイヤーはブリーフを提供し、プラットフォームがコミュニティのボイスとビジュアルスタイルに合致するフィードネイティブコンテンツを生成します。 これは [brief-in-media-buy](/docs/creative/generative-creative#seller-side-generation-brief-in-media-buy) パターンに従う。標準的なジェネレーティブクリエイティブとの主な違い: プラットフォームはコミュニティについての深いコンテキスト(トレンドトピック、コンテンツスタイル、オーディエンス行動)を持ち、それが生成に反映されます。料理コミュニティでのジェネレーティブ フィードネイティブ広告は、テクノロジーコミュニティでの同じブリーフとは見た目も文体も異なります。 ローンチ前に `context_description` インプットでプレビューして、プラットフォームがさまざまなコミュニティコンテキストに対してどのようにブリーフを適応させるかを確認します。 ## 関連ドキュメント * [クリエイティブエージェントの実装 — パターン4: フィードネイティブ/ソーシャルフォーマットエージェント](/docs/creative/implementing-creative-agents#pattern-4-feed-nativesocial-format-agent) — フィードネイティブフォーマットエージェントの実装ガイド * [カルーセル](/docs/creative/channels/carousels) — マルチカードフォーマット仕様 * [クリエイティブレビュー](/docs/creative/sales-agent-creative-capabilities#creative-review) — コミュニティガイドラインを含む承認フロー * [ジェネレーティブクリエイティブ](/docs/creative/generative-creative) — AI 搭載クリエイティブ生成ワークフロー # Video Ads Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/channels/video AdCP の動画広告フォーマットは、ホスト型ファイル、VAST タグ、インライン XML、プリロール/ミッドロール/ポストロール広告向けのマルチ解像度エンコーディングをカバーします。 このガイドでは、オンライン動画・CTV・ストリーミングプラットフォーム向けに、AdCP が動画広告フォーマットをどのように表現するかを説明します。 ## 動画フォーマットの特徴 動画フォーマットには次が含まれます: * **ホスト型動画** - パブリッシャーの広告サーバーが配信する直接の動画ファイル URL * **VAST タグ** - VAST/VPAID XML を返すサードパーティ広告サーバーの URL * **インライン VAST XML** - クリエイティブマニフェストに提供される完全な VAST XML * **複数解像度** - 異なるエンコーディングプロファイルの同一クリエイティブ 動画広告は、動画コンテンツの前(プリロール)、途中(ミッドロール)、後(ポストロール)に、またはアウトストリーム動画としてインフィードで再生されます。 ## 標準動画フォーマット ### 横型動画(時間別) #### 15 秒動画 ```json theme={null} { "$schema": "/schemas/core/format.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_15s" }, "name": "Standard Video - 15 seconds", "assets": [ { "asset_id": "video_file", "asset_type": "video", "asset_role": "hero_video", "item_type": "individual", "required": true, "requirements": { "min_duration_ms": 15000, "max_duration_ms": 15000, "containers": ["mp4"], "codecs": ["h264"], "min_width": 1280, "max_width": 1920, "min_height": 720, "max_height": 1080, "max_file_size_kb": 30720, "min_bitrate_kbps": 4000, "max_bitrate_kbps": 10000, "audio_codecs": ["aac"] } } ] } ``` #### 30 秒動画 ```json theme={null} { "$schema": "/schemas/core/format.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s" }, "name": "Standard Video - 30 seconds", "assets": [ { "asset_id": "video_file", "asset_type": "video", "asset_role": "hero_video", "item_type": "individual", "required": true, "requirements": { "min_duration_ms": 30000, "max_duration_ms": 30000, "containers": ["mp4"], "codecs": ["h264"], "min_width": 1280, "max_width": 1920, "min_height": 720, "max_height": 1080, "max_file_size_kb": 51200, "min_bitrate_kbps": 4000, "max_bitrate_kbps": 10000, "audio_codecs": ["aac"] } } ] } ``` #### 6 秒バンパー ```json theme={null} { "$schema": "/schemas/core/format.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_6s" }, "name": "6-Second Bumper", "assets": [ { "asset_id": "video_file", "asset_type": "video", "asset_role": "hero_video", "item_type": "individual", "required": true, "requirements": { "min_duration_ms": 6000, "max_duration_ms": 6000, "containers": ["mp4"], "codecs": ["h264"], "min_width": 1280, "max_width": 1920, "min_height": 720, "max_height": 1080, "max_file_size_kb": 15360, "min_bitrate_kbps": 4000, "max_bitrate_kbps": 10000 } } ] } ``` ### 縦型/モバイル動画 ```json theme={null} { "$schema": "/schemas/core/format.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_vertical_15s" }, "name": "Vertical Video - 15 seconds", "assets": [ { "asset_id": "video_file", "asset_type": "video", "asset_role": "hero_video", "item_type": "individual", "required": true, "requirements": { "min_duration_ms": 15000, "max_duration_ms": 15000, "aspect_ratio": "9:16", "min_width": 1080, "max_width": 1080, "min_height": 1920, "max_height": 1920, "containers": ["mp4"], "codecs": ["h264"], "max_file_size_kb": 30720 } } ] } ``` ### CTV/OTT 動画 CTV プラットフォームは、ウェブ動画とは大きく異なる厳格な技術要件を持ちます。クリエイティブエージェントは、拒否を避けるためにこれらの仕様に正確に一致するアセットを生成しなければなりません。 #### 標準 CTV 動画(30 秒) このフォーマットは、ほとんどのプラットフォームに共通する CTV の一般的な要件を表します: ```json theme={null} { "$schema": "/schemas/core/format.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_ctv" }, "name": "CTV Video - 30 seconds", "assets": [ { "asset_id": "video_file", "asset_type": "video", "asset_role": "hero_video", "item_type": "individual", "required": true, "requirements": { "min_duration_ms": 30000, "max_duration_ms": 30000, "containers": ["mp4", "mov"], "codecs": ["h264"], "min_width": 1920, "max_width": 1920, "min_height": 1080, "max_height": 1080, "aspect_ratio": "16:9", "min_bitrate_kbps": 6000, "max_bitrate_kbps": 15000, "max_file_size_kb": 512000, "frame_rates": [23.976, 24, 25, 29.97, 30, 59.94, 60], "frame_rate_type": "constant", "scan_type": "progressive", "min_gop_interval_seconds": 1, "max_gop_interval_seconds": 2, "gop_type": "closed", "moov_atom_position": "start", "audio_required": true, "audio_codecs": ["aac", "pcm"], "audio_sample_rates": [48000], "audio_channels": ["stereo"], "loudness_lufs": -24, "loudness_tolerance_db": 2, "true_peak_dbfs": -2, "ext": { "ctv_profile": { "color_space": "rec709", "hdr_format": "sdr", "chroma_subsampling": ["4:2:0"], "video_bit_depth": [8], "audio_bit_depth": [16, 24], "audio_bitrate_kbps_min": 192 } } } } ] } ``` #### プラットフォーム固有の CTV の例 異なる CTV プラットフォームは要件が異なります。セールスエージェントは、自身の特定のプラットフォームのニーズに一致するフォーマットを定義すべきです。 **Roku 準拠フォーマット:** ```json theme={null} { "$schema": "/schemas/core/format.json", "format_id": { "agent_url": "https://sales.example.com", "id": "video_30s_roku" }, "name": "Roku CTV - 30 seconds", "assets": [ { "asset_id": "video_file", "asset_type": "video", "item_type": "individual", "required": true, "requirements": { "min_duration_ms": 30000, "max_duration_ms": 30000, "containers": ["mp4", "mov"], "codecs": ["h264", "prores"], "min_width": 1920, "max_width": 1920, "min_height": 1080, "max_height": 1080, "min_bitrate_kbps": 6000, "frame_rates": [23.976, 25, 29.97], "frame_rate_type": "constant", "scan_type": "progressive", "audio_codecs": ["pcm", "aac"], "audio_sample_rates": [48000], "audio_channels": ["stereo"], "loudness_lufs": -23, "loudness_tolerance_db": 2, "ext": { "roku_profile": { "audio_bit_depth": [16, 24], "audio_bitrate_kbps_min": 192 } } } } ] } ``` **Hulu 準拠フォーマット:** ```json theme={null} { "$schema": "/schemas/core/format.json", "format_id": { "agent_url": "https://sales.example.com", "id": "video_30s_hulu" }, "name": "Hulu CTV - 30 seconds", "assets": [ { "asset_id": "video_file", "asset_type": "video", "item_type": "individual", "required": true, "requirements": { "min_duration_ms": 30000, "max_duration_ms": 30000, "containers": ["mp4", "mov"], "codecs": ["h264", "prores"], "min_width": 1280, "max_width": 1920, "min_height": 720, "max_height": 1080, "min_bitrate_kbps": 10000, "max_bitrate_kbps": 40000, "max_file_size_kb": 10485760, "frame_rates": [23.976, 24, 25, 29.97, 30], "frame_rate_type": "constant", "scan_type": "progressive", "audio_codecs": ["pcm", "aac"], "audio_sample_rates": [48000], "audio_channels": ["stereo"], "loudness_lufs": -24, "loudness_tolerance_db": 2, "ext": { "hulu_profile": { "chroma_subsampling": ["4:2:0", "4:2:2"], "audio_bit_depth": [16, 24], "audio_bitrate_kbps_min": 192, "audio_bitrate_kbps_max": 256 } } } } ] } ``` **YouTube CTV フォーマット:** ```json theme={null} { "$schema": "/schemas/core/format.json", "format_id": { "agent_url": "https://sales.example.com", "id": "video_30s_youtube_ctv" }, "name": "YouTube CTV - 30 seconds", "assets": [ { "asset_id": "video_file", "asset_type": "video", "item_type": "individual", "required": true, "requirements": { "min_duration_ms": 30000, "max_duration_ms": 30000, "containers": ["mp4"], "codecs": ["h264"], "min_width": 1280, "max_width": 1920, "min_height": 720, "max_height": 1080, "frame_rates": [24, 25, 30, 48, 50, 60], "scan_type": "progressive", "moov_atom_position": "start", "audio_codecs": ["aac"], "audio_sample_rates": [48000], "audio_channels": ["stereo"], "loudness_lufs": -14, "ext": { "youtube_profile": { "audio_bitrate_kbps_min": 128 } } } } ] } ``` #### SSAI 対応 CTV フォーマット サーバーサイド広告挿入(SSAI)の互換性のためには、GOP 構造が重要です: ```json theme={null} { "$schema": "/schemas/core/format.json", "format_id": { "agent_url": "https://sales.example.com", "id": "video_30s_ssai" }, "name": "SSAI Video - 30 seconds", "assets": [ { "asset_id": "video_file", "asset_type": "video", "item_type": "individual", "required": true, "requirements": { "min_duration_ms": 30000, "max_duration_ms": 30000, "containers": ["mp4"], "codecs": ["h264"], "min_width": 1920, "max_width": 1920, "min_height": 1080, "max_height": 1080, "min_bitrate_kbps": 15000, "frame_rates": [29.97, 30], "frame_rate_type": "constant", "scan_type": "progressive", "min_gop_interval_seconds": 1, "max_gop_interval_seconds": 2, "gop_type": "closed", "moov_atom_position": "start", "audio_required": true, "audio_codecs": ["aac"], "audio_sample_rates": [48000], "audio_channels": ["stereo"] } } ] } ``` ### VAST タグフォーマット サードパーティ広告サーバー向け: ```json theme={null} { "$schema": "/schemas/core/format.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_vast" }, "name": "VAST Tag - 30 seconds", "assets": [ { "asset_id": "vast_tag", "asset_type": "vast", "item_type": "individual", "required": true, "requirements": { "vast_version": "4.2" } } ] } ``` ### VPAID インタラクティブ動画 ```json theme={null} { "$schema": "/schemas/core/format.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_vpaid" }, "name": "VPAID Interactive - 30 seconds", "assets": [ { "asset_id": "vpaid_tag", "asset_type": "vast", "item_type": "individual", "required": true, "requirements": { "vast_version": "4.2", "ext": { "vpaid": { "vpaid_version": ["2.0"], "api_framework": "VPAID", "vpaid_enabled": true } } } } ] } ``` ## クリエイティブマニフェスト ### ホスト型動画マニフェスト ```json theme={null} { "$schema": "/schemas/core/creative-manifest.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s" }, "assets": { "video_file": { "asset_type": "video", "url": "https://cdn.brand.com/spring_30s.mp4", "duration_ms": 30000, "width": 1920, "height": 1080, "container_format": "mp4", "video_codec": "h264", "video_bitrate_kbps": 8000 }, "impression_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/imp" }, "landing_url": { "asset_type": "url", "url_type": "clickthrough", "url": "https://brand.example/spring-sale" } } } ``` ### VAST タグマニフェスト(URL 配信) ```json theme={null} { "$schema": "/schemas/core/creative-manifest.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_vast" }, "assets": { "vast_tag": { "asset_type": "vast", "delivery_type": "url", "url": "https://adserver.brand.example/vast", "vast_version": "4.2" } } } ``` ### インライン VAST XML マニフェスト ```json theme={null} { "$schema": "/schemas/core/creative-manifest.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_vast" }, "assets": { "vast_xml": { "asset_type": "vast", "delivery_type": "inline", "content": "\n\n \n \n \n \n \n \n 00:00:30\n \n \n \n \n \n \n \n \n \n \n \n \n \n", "vast_version": "4.2" } } } ``` ### マルチ解像度マニフェスト ```json theme={null} { "$schema": "/schemas/core/creative-manifest.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s" }, "assets": { "video_1080p": { "asset_type": "video", "url": "https://cdn.brand.com/spring_30s_1080p.mp4", "duration_ms": 30000, "width": 1920, "height": 1080, "video_bitrate_kbps": 8000 }, "video_720p": { "asset_type": "video", "url": "https://cdn.brand.com/spring_30s_720p.mp4", "duration_ms": 30000, "width": 1280, "height": 720, "video_bitrate_kbps": 5000 }, "video_480p": { "asset_type": "video", "url": "https://cdn.brand.com/spring_30s_480p.mp4", "duration_ms": 30000, "width": 854, "height": 480, "video_bitrate_kbps": 2500 } } } ``` ## 動画専用マクロ [ユニバーサルマクロ](/docs/creative/universal-macros)に加えて、動画フォーマットは次をサポートします: ### 動画コンテンツコンテキスト * `{VIDEO_ID}` - コンテンツ動画の識別子 * `{VIDEO_TITLE}` - コンテンツ動画のタイトル * `{VIDEO_DURATION}` - コンテンツの長さ(秒) * `{VIDEO_CATEGORY}` - IAB コンテンツカテゴリ * `{CONTENT_GENRE}` - コンテンツジャンル(ニュース、スポーツ、コメディ) * `{CONTENT_RATING}` - コンテンツレーティング(G、PG、TV-14 など) * `{PLAYER_WIDTH}` / `{PLAYER_HEIGHT}` - 動画プレーヤーの寸法(ピクセル) ### Ad Pod の位置 * `{POD_POSITION}` - 広告ブレイク内の位置(1、2、3 など) * `{POD_SIZE}` - このブレイク内の総広告数 * `{AD_BREAK_ID}` - 一意の広告ブレイク識別子 ### 再生コンテキスト * `{PLAYBACK_METHOD}` - auto-play-sound-on、auto-play-sound-off、click-to-play * `{PLAYER_SIZE}` - small、medium、large、fullscreen * `{VIDEO_PLACEMENT}` - in-stream、in-banner、in-article、in-feed、interstitial ### VAST マクロ AdCP マクロ(`{CURLY_BRACES}`)は、[IAB VAST 4.x マクロ](http://interactiveadvertisingbureau.github.io/vast/vast4macros/vast4-macros-latest.html)(`[SQUARE_BRACKETS]`)と併用できます: * `[CACHEBUSTING]` - キャッシュ防止のための乱数 * `[TIMESTAMP]` - Unix タイムスタンプ * `[DOMAIN]` - パブリッシャードメイン * `[IFA]` - デバイス広告 ID(IDFA/AAID) * `[REGULATIONS]` - プライバシー規制シグナル(GDPR、CCPA) * `[DEVICEUA]` - デバイスのユーザーエージェント文字列 **両方のマクロ形式を混在させる例:** ``` https://track.brand.com/imp? buy={MEDIA_BUY_ID}& video={VIDEO_ID}& device=[IFA]& domain=[DOMAIN]& cb=[CACHEBUSTING] ``` ## 動画トラッキングアセット ### 標準トラッキングイベント ```json theme={null} { "$schema": "/schemas/core/creative-manifest.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s" }, "assets": { "video_file": { "asset_type": "video", "url": "https://cdn.brand.com/video_30s.mp4", "width": 1920, "height": 1080 }, "impression_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/imp" }, "start_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/start" }, "quartile_25_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/q25" }, "quartile_50_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/q50" }, "quartile_75_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/q75" }, "complete_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/complete" }, "click_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/click" } } } ``` ### インタラクティブトラッキングイベント ユーザーインタラクションをサポートするフォーマット向け: ```json theme={null} { "pause_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/pause" }, "resume_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/resume" }, "skip_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/skip" }, "mute_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/mute" }, "unmute_tracker": { "asset_type": "url", "url_type": "tracker_pixel", "url": "https://track.brand.example/unmute" } } ``` ## よく使われるアスペクト比 * **16:9**(1920x1080、1280x720)- 標準の横型動画 * **9:16**(1080x1920)- 縦型モバイル動画 * **4:3**(640x480)- レガシーフォーマット、まれ * **1:1**(1080x1080)- 正方形のソーシャル動画 ## 動画プレースメントタイプ ### プリロール コンテンツ開始前に再生される動画広告。最も一般的なプレースメント。 **一般的な長さ:** 6秒、15秒、30秒 ### ミッドロール コンテンツの合間に再生される動画広告。位置トラッキングに ad pod マクロを使用します。 **一般的な長さ:** 15秒、30秒 ### ポストロール コンテンツ終了後に再生される動画広告。 **一般的な長さ:** 15秒、30秒 ### アウトストリーム 動画プレーヤー内ではなく、インフィードまたはインアーティクルで再生される動画広告。 **一般的なフォーマット:** 縦型モバイル動画、インフィード動画 ## VAST/VPAID 連携 ### VAST バージョン AdCP はすべての VAST バージョンをサポートします: * **VAST 2.0** - レガシーサポート * **VAST 3.0** - 検証とエラーハンドリングを追加 * **VAST 4.0** - トラッキング、ビューアビリティの改善 * **VAST 4.1** - 強化された ad pod サポート * **VAST 4.2** - 最新仕様(推奨) ### VPAID サポート VPAID(Video Player Ad-Serving Interface Definition)はインタラクティブな動画広告を可能にします: ```json theme={null} { "$schema": "/schemas/core/format.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_vpaid" }, "name": "VPAID Interactive - 30 seconds", "assets": [ { "asset_id": "vpaid_tag", "asset_type": "vast", "item_type": "individual", "required": true, "requirements": { "vast_version": "4.2", "ext": { "vpaid": { "vpaid_version": ["2.0"], "api_framework": "VPAID", "vpaid_enabled": true } } } } ] } ``` ## ファイル仕様 ### 動画コーデック * **H.264** - 最も広くサポートされ、CTV に必須 * **H.265/HEVC** - より良い圧縮、CTV サポートが拡大中 * **ProRes** - 高品質のメザニン、プレミアム CTV で受け入れられる * **VP8/VP9** - オープンコーデック、ウェブ向け * **AV1** - 次世代のオープンコーデック、サポートが登場中 ### オーディオコーデック * **AAC/AAC-LC** - MP4 の標準、広くサポートされる * **HE-AAC** - 低ビットレート向けの高効率 AAC * **PCM** - 非圧縮、一部の CTV プラットフォームが推奨 * **AC-3/E-AC-3** - Dolby Digital、放送で使用 ### コンテナフォーマット * **MP4** - 業界標準、ほとんどのプラットフォームに必須 * **MOV** - QuickTime フォーマット、プレミアム CTV で受け入れられる * **WebM** - オープンフォーマット、ウェブ向け ### ビットレート目安 * **プレミアム CTV(メザニン):** 15〜50 Mbps * **標準 CTV:** 6〜15 Mbps * **高品質ウェブ(1080p):** 8〜10 Mbps * **標準品質(720p):** 4〜6 Mbps * **モバイル最適化(480p):** 2〜3 Mbps ### フレームレート * **フィルム:** 23.976 fps、24 fps * **PAL:** 25 fps * **NTSC:** 29.97 fps、30 fps * **高フレームレート:** 48 fps、50 fps、60 fps CTV プラットフォームは固定フレームレート(CFR)を要求します。可変フレームレート(VFR)は拒否されます。 ### よく使われる解像度 **16:9 横型:** * 1920x1080(1080p フル HD)- 標準 CTV * 1280x720(720p HD) * 854x480(480p SD) * 3840x2160(4K UHD)- プレミアム CTV **9:16 縦型:** * 1080x1920(モバイル縦型) **1:1 正方形:** * 1080x1080(ソーシャル動画) ### スキャンタイプ CTV は普遍的に**プログレッシブスキャン**を要求します。インターレースコンテンツは拒否されます。 ### 色空間 * **Rec.709** - HD/SDR コンテンツの標準(ほとんどの CTV が要求) * **Rec.2020** - UHD/4K コンテンツ * **Rec.2100** - HDR コンテンツ(HDR10、HLG) * **sRGB** - ウェブコンテンツ ### クロマサブサンプリング * **4:2:0** - 配信の標準 * **4:2:2** - 放送/メザニン品質 ### 動画のビット深度 * **8 ビット** - 標準の SDR * **10 ビット** - HDR およびプレミアム SDR * **12 ビット** - プロフェッショナル HDR ### GOP 構造(SSAI で重要) サーバーサイド広告挿入の互換性のために: * **キーフレーム間隔:** 1〜2 秒 * **GOP タイプ:** クローズド GOP が必須 * **moov アトム:** プログレッシブダウンロードのためにファイルの先頭になければならない ## オーディオ仕様 ### サンプリングレート * **48 kHz** - CTV に必須(Roku、Hulu、Snapchat が義務付け) * **44.1 kHz** - CD 品質、一部のプラットフォームで受け入れられる * **96 kHz** - 高解像度、受け入れられるが必須ではない ### チャンネル構成 * **ステレオ(2 チャンネル)** - CTV 広告に必須 * **モノラル** - 一部のウェブ/モバイルで許容 * **5.1/7.1** - CTV 広告ではサポートされない ### オーディオのビット深度 * **16 ビット** - 標準 * **24 ビット** - 高品質、プレミアム CTV で受け入れられる ### オーディオビットレート * **CTV 最小:** 192 kbps * **標準ウェブ:** 128 kbps * **高品質:** 256 kbps ### ラウドネス標準 プラットフォームによって異なるターゲットに正規化します: | Platform | Target LUFS | Tolerance | Standard | | ------------- | ----------- | --------- | --------- | | Broadcast/CTV | -24 LUFS | ±2 dB | ATSC A/85 | | Spotify | -16 LUFS | ±1.5 dB | - | | YouTube | -14 LUFS | - | - | **True Peak:** クリッピングを防ぐため -1〜-2 dBFS を超えないようにすべきです ## 関連ドキュメント * [ユニバーサルマクロ](/docs/creative/universal-macros) - 動画マクロを含む完全なマクロリファレンス * [クリエイティブマニフェスト](/docs/creative/creative-manifests) - マニフェストの構造とアセット仕様 * [アセットタイプ](/docs/creative/asset-types) - 動画アセットタイプの定義 # クリエイティブライブラリとコンセプト Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/creative-libraries AdCP のクリエイティブライブラリは、バイヤーが広告サーバーや管理プラットフォーム全体でクリエイティブを参照・整理し、承認状態を追跡できるようにします。 クリエイティブライブラリは、バイヤーが AdCP を通じてクリエイティブを管理できるようにする — 既存のアセットの参照、新しいもののアップロード、キャンペーンへの割り当て、承認状態の追跡。クリエイティブライブラリは、ケイパビリティで `has_creative_library: true` を宣言するエージェントがホストする: 広告サーバー(CM360、Flashtalking)、クリエイティブ管理プラットフォーム(Celtra)、またはクリエイティブプロトコルも公開するセールスエージェント。 ## モデル クリエイティブライブラリは、安定した `creative_id`、観測可能なライフサイクル状態、そして任意の一つのメディアバイやパッケージとは別個の割り当て関係を持つ、アカウントスコープのクリエイティブリソースのコレクションです。実装は、完全なアドサーバーライブラリでも、セラーのストレージ上の薄いビューでもかまいませんが、プロトコルのコミットメントは同じです: バイヤーは `list_creatives` を通じてクリエイティブリソースを読み、`sync_creatives` を通じて更新し、セラーがそれを保持している間、割り当てをまたいで `creative_id` で参照できます。 クリエイティブライブラリを表明しないセラーからのインラインパッケージクリエイティブは、ライブラリクリエイティブではありません。それらはパッケージスコープのクリエイティブ添付です: セラーはそれらをメディアバイの一部として受け入れ配信しますが、独立したライブラリ管理やバイをまたぐ再利用は表明しません。 クリエイティブライブラリは3つのレベルでアセットを整理する: | レベル | AdCP 相当 | 例 | | ------- | -------------------------------------------------- | ------------------------------------------------------ | | アカウント | アカウント([accounts プロトコル](/docs/accounts/overview)経由) | CM360 の広告主、Celtra のブランドワークスペース | | コンセプト | `concept_id` / `concept_name` | Flashtalking コンセプト、CM360 クリエイティブグループ、Celtra キャンペーンフォルダ | | クリエイティブ | `creative_id`、`format_id`、`assets` を持つクリエイティブアイテム | 300x250 ディスプレイ広告、30 秒ビデオスポット | **コンセプト**はサイズとフォーマットをまたいで関連するクリエイティブをグループ化します。"Holiday 2026" コンセプトは 300x250 バナー、728x90 リーダーボード、30 秒ビデオを含む場合がある — すべて同じキャンペーンアイデアを表現しています。`concept_id` を使用してグループとしてフィルタリングと管理を行います。 ### クリエイティブの状態と割り当ての状態は別物 ライブラリが独立して追跡する二つのもの: * **クリエイティブの状態** — クリエイティブ自体のレビューステータス: `processing`、`pending_review`、`approved`、`rejected`、`archived`。クリエイティブエージェントのレビューワークフローが設定します。どこで使われるかに関わらず、ライブラリアセットとしてのクリエイティブに適用されます。 * **割り当ての状態** — クリエイティブと特定のメディアバイ上のパッケージとの関係。バイヤーがクリエイティブを割り当てたとき(`sync_creatives`、`creative_assignments`、または `create_media_buy` のインラインクリエイティブを介して)に作成されます。メディアバイまたはパッケージが拒否、キャンセル、完了されたとき、またはバイヤーが割り当てを削除したときに解放されます。 これらのライフサイクルは独立して追跡されます: * ライブラリ内のクリエイティブは、任意の時点で**ゼロ個以上**のアクティブな割り当てを持ちます。 * メディアバイを拒否、キャンセル、完了すると、その割り当てが解放されます。それはクリエイティブのレビュー状態を変えず、クリエイティブをライブラリから削除せず、他のメディアバイでのクリエイティブの使用にも影響しません。 * 割り当てが存在した後にクリエイティブのレビュー状態が変わったとき(例: セラーが承認を取り消す、または以前に拒否されたクリエイティブを承認する)、セラーは新しい状態に基づいて実行中の配信を続行または停止してもよい(MAY)。バイヤーは、クリエイティブの状態変更後に割り当てレベルの影響を検出するために、[`get_media_buys`](/docs/media-buy/task-reference/get_media_buys) を介してパッケージごとに `approval_status` を再取得すべきです(SHOULD)。[クリエイティブレビュー](/docs/creative/sales-agent-creative-capabilities#creative-review)を参照。 新しいメディアバイでライブラリクリエイティブを再利用するバイヤーエージェントは、アセットが使用可能かを知るために**クリエイティブの状態**を、それが現在どこで実行中かを知るために**割り当ての状態**を確認します。 ### クリエイティブはキャンペーンより長生きする クリエイティブは、それを参照するバイとは独立してライブラリに永続化しなければなりません(MUST)。バイの拒否、キャンセル、完了は割り当てのみを解放します——クリエイティブは現在の `status` のままライブラリに残り、後続のバイで再利用できます。これは、クリエイティブがどのようにライブラリに入ったかに関わらず成立します: 明示的な [`sync_creatives`](/docs/creative/task-reference/sync_creatives)、[`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) 上のライブラリ裏付けインラインクリエイティブ、または AdCP を通じて公開されるプラットフォームネイティブなアップロード。基盤となるアドサーバーがバイごとの添付とは別のライブラリオブジェクトを持たないセラーは、バイの存続期間中は `list_creatives` を通じてバイヤーが同期したクリエイティブを公開し、テアダウン後もその終端状態(`archived` を含む)を公開し続けることで、このルールを満たします——ライブラリはバイごとのストレージ上の薄いビューでよく、別個のストアである必要はありません。 アクティブな割り当てを超える保持はセラーが定義します。エージェントは、非アクティブ、フライト後の期限切れ、またはストレージポリシーのために未割り当てのクリエイティブをアーカイブしてもよく(MAY、[クリエイティブステータスのライフサイクル](/docs/creative/specification#クリエイティブステータスのライフサイクル)を参照)、公開済み投稿の認可の期限切れのような回復可能な依存関係の喪失のために `approved` → `suspended` へ遷移してもよく(MAY)、ポリシーの失効、テイクダウン、コンテンツドリフトのために `approved` → `rejected` へ遷移してもよい(MAY)。バイヤーが同期した後にクリエイティブの状態が変わるときは常に、バイヤーが再利用の前に再同期、置換、またはそのアセットへの依存の停止ができるよう、セラーは新しい状態を観測可能にしなければなりません(MUST): * アクティブなメディアバイに影響する状態変更(例: ライブな割り当てを持つクリエイティブの `approved` → `suspended` または `approved` → `rejected`)については、セラーはバイに対応する `impairment` を表面化しなければなりません(MUST)。[メディアバイの健全性](/docs/media-buy/media-buys/lifecycle#health-and-dependency-impairment)を参照。 * アクティブな割り当てのないクリエイティブの状態変更(例: セラーが非アクティブのために未割り当てのクリエイティブをアーカイブする)については、セラーは次の [`list_creatives`](/docs/creative/task-reference/list_creatives) の読み取りで新しい `status` を反映しなければなりません(MUST)——[snapshot-and-log 契約](/docs/protocol/snapshot-and-log)に従い、そのスナップショットが今日の準拠シグナルです。アカウントスコープのクリエイティブ状態変更のためのプッシュチャネルは[クリエイティブライフサイクルウェブフックの RFC](https://github.com/adcontextprotocol/adcp/issues/2261) の下で定義中です。そのチャネルが出荷されたら、セラーはそれでも追加で発火すべきです(SHOULD)。 バイヤーは、新しいバイで再利用する前に [`list_creatives`](/docs/creative/task-reference/list_creatives) を介して可用性を確認すべきです(SHOULD)。ライブラリクリエイティブは、バイヤーが供給した入力のバンドル——アップロードされたアセット、ブリーフ、ブランドとカタログのポインタ、またはそれらの組み合わせ——です。保持はバンドルに適用されます。フォーマットのレンダリングされた出力が個別にアドレス可能かどうかはフォーマットレベルの関心事であり、ライブラリの保持とは独立しています。 ## ライブラリへの接続 クエリ前にアカウントアクセスを確立する: ```json theme={null} { "accounts": [{ "account_id": "acct_acme_2026", "account_name": "Acme Corp", "credentials": { "api_key": "..." } }] } ``` アカウントのセットアップは、ライブラリがスタンドアロンのクリエイティブエージェントにあるかセールスエージェントにあるかに関わらず同じです。詳細は [accounts プロトコル](/docs/accounts/overview)を参照。 ## クリエイティブの参照 [`list_creatives`](/docs/creative/task-reference/list_creatives) を使用してライブラリを参照します。コンセプト、フォーマット、ステータス、タグ、または日付範囲でフィルタリングする: ```json theme={null} { "filters": { "concept_ids": ["concept_holiday_2026"], "statuses": ["approved"], "format_ids": [{ "agent_url": "https://ads.flashtalking-example.com", "id": "display_300x250" }] }, "include": { "variables": true, "assignments": true } } ``` レスポンスの各クリエイティブには次の情報が含まれます: ```json theme={null} { "creative_id": "ft_88201", "name": "Holiday 2026 - Medium Rectangle", "format_id": { "agent_url": "https://ads.flashtalking-example.com", "id": "display_300x250" }, "status": "approved", "concept_id": "concept_holiday_2026", "concept_name": "Holiday 2026 Campaign", "created_date": "2026-10-15T14:00:00Z", "updated_date": "2026-11-20T09:30:00Z", "tags": ["holiday_2026", "display"], "variables": [ { "variable_id": "headline", "name": "Headline text", "type": "text", "default_value": "Holiday Sale — Up to 40% Off" } ], "assignments": [ { "package_id": "pkg_premium_display", "weight": 100 } ] } ``` `status` フィールドはライブラリ内のクリエイティブの現在の状態を反映する: `processing`、`pending_review`、`approved`、`rejected`、`archived`。ステータス遷移の仕組みについては[クリエイティブレビュー](/docs/creative/sales-agent-creative-capabilities#creative-review)を参照。 ## クリエイティブのアップロード [`sync_creatives`](/docs/creative/task-reference/sync_creatives) を使用して新しいクリエイティブをアップロードするか既存のものを更新します。この操作は upsert セマンティクスを使用する — `creative_id` がすでに存在する場合は更新し、そうでなければ作成します。 ```json theme={null} { "creatives": [ { "creative_id": "acme_video_001", "name": "Holiday Sale 30s", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_standard_30s" }, "assets": { "video": { "url": "https://cdn.acme-example.com/holiday-sale-30s.mp4", "width": 1920, "height": 1080, "duration_ms": 30000 }, "click_url": { "url": "https://acme-example.com/holiday-sale" } } } ] } ``` レスポンスは各クリエイティブに何が起きたかを示します: ```json theme={null} { "creatives": [ { "creative_id": "acme_video_001", "action": "created" } ] } ``` アップロード後、クリエイティブはライブラリのレビュープロセスに入る。`list_creatives` を確認して `pending_review` から `approved` への遷移を確認します。 ### アサインメント付きアップロード 同じ呼び出しでクリエイティブをパッケージに割り当てる: ```json theme={null} { "creatives": [ { "creative_id": "acme_video_001", "name": "Holiday Sale 30s", "format_id": { "agent_url": "...", "id": "video_standard_30s" }, "assets": { "...": "..." } } ], "assignments": [ { "creative_id": "acme_video_001", "package_id": "pkg_premium_video" } ] } ``` ## ライブラリクリエイティブからのタグ生成 ライブラリのクリエイティブに配信タグが必要な場合は、マニフェストの代わりに `creative_id` を使って [`build_creative`](/docs/creative/task-reference/build_creative) を使用します: ```json theme={null} { "creative_id": "ft_88201", "concept_id": "concept_holiday_2026", "target_format_id": { "agent_url": "https://ads.flashtalking-example.com", "id": "display_300x250" } } ``` クリエイティブエージェントはライブラリから `creative_id` を解決し、配信タグを含むマニフェストを返します。タグフォーマットはプラットフォームによって異なる: * **Flashtalking、Celtra**: どんな環境にも適応するユニバーサルタグ。プレースメントコンテキスト不要。 * **CM360**: トラフィッキングコンテキストが必要なプレースメントレベルのタグ。`media_buy_id` と `package_id` を渡す: ```json theme={null} { "creative_id": "cm360_creative_12345", "target_format_id": { "agent_url": "https://ads.cm360-example.com", "id": "display_300x250" }, "media_buy_id": "buy_holiday_q4", "package_id": "pkg_premium_display" } ``` 詳しくは[タグ生成モデル](/docs/creative/implementing-creative-agents#tag-generation-models)を参照。 ## クリエイティブのキャンペーンへの割り当て ライブラリクリエイティブをメディアバイに付与するには2つのパスがある: ### パス 1: パッケージのクリエイティブアサインメント メディアバイ作成時に ID でライブラリクリエイティブを参照する: ```json theme={null} { "packages": [{ "product_id": "premium_display", "creative_assignments": [ { "creative_id": "ft_88201", "weight": 60 }, { "creative_id": "ft_88202", "weight": 40 } ] }] } ``` これは、クリエイティブがすでにエージェントのライブラリにある場合(`sync_creatives` またはプラットフォーム独自のアップロードフローを経由して)に機能します。 ### パス 2: パッケージのインラインクリエイティブ メディアバイと一緒にクリエイティブを直接アップロードする — 別途同期ステップ不要: ```json theme={null} { "packages": [{ "product_id": "premium_display", "creatives": [{ "creative_id": "acme_banner_001", "name": "Holiday banner", "format_id": { "agent_url": "...", "id": "display_300x250" }, "assets": { "...": "..." } }] }] } ``` `creative.has_creative_library: true` も表明するセラーでは、エージェントはクリエイティブをライブラリに追加し、1つの操作でパッケージに割り当てます。クリエイティブライブラリを表明しないインライン専用のセラーでは、同じ `creatives` ペイロードは、再利用可能なライブラリエントリを作成せずにパッケージスコープのアセットを割り当てます。詳細は[インラインクリエイティブ管理](/docs/creative/sales-agent-creative-capabilities)を参照。 **ライブラリ裏付けのインラインクリエイティブは、`sync_creatives` のアップロードと同じライブラリライフサイクルに従います。** セラーが `creative.has_creative_library: true` を表明する場合、インライン形式は「1 回の呼び出しで同期して割り当てる」という利便性であって、別個のライフサイクルではありません。提出されると、クリエイティブは `sync_creatives` の下で持つのと同じレビューフロー、保持、識別子でライブラリに入ります。`create_media_buy` タスクが `pending_manual` として解決されバイが決してアクティブにならない場合、またはバイが拒否またはキャンセルされる場合、解放されるのはパッケージの割り当てのみです。クリエイティブはライブラリに残り、後続の `create_media_buy` 呼び出しで `creative_id` により参照できます。この割り当て解放の動作はメディアバイ側で規範的です——[メディアバイの状態遷移](/docs/media-buy/specification#media-buy-state-transitions)のルールを参照。 インライン専用のセラーでは、バイヤーはクリエイティブ本体をメディアバイのパッケージに添付されたものとして扱うべきです。セラーは、バイをレビュー、配信、置換、監査するのに十分なパッケージスコープのクリエイティブ状態を保持するかもしれませんが、`list_creatives`、`sync_creatives`、後の `creative_assignments` の再利用のような再利用可能なライブラリ操作は表明していません。 クリエイティブレビューはメディアバイの結果とは独立して進みます。セラーは、バイがアクティブにならなかったという理由だけでレビューをスキップしてはなりません(MUST NOT)。バイの拒否はそれ自体では提出されたクリエイティブの拒否を意味しません——クリエイティブの拒否は、含まれるバイのステータスから暗黙的にではなく、それ自身の `rejection_reason` を持つ意図的なレビューの判断でなければなりません(MUST)。セラーは、将来の割り当てがアクティブになる前にレビューが完了する限り、現在アクティブな割り当てのないクリエイティブのレビューを後回しにしてもよい(MAY)。 **ケイパビリティフラグのスコープ。** `inline_creative_management: true` は、セールスエージェントが `create_media_buy` と `update_media_buy` でインラインクリエイティブを受け入れることを表明します。それ自体ではクリエイティブライブラリを表明しません。分離されたライブラリライフサイクルは、セラーが `creative.has_creative_library: true` も表明する場合に適用されます。 ### マルチセラー配布 複数のセラーと作業する場合、クリエイティブを一度ビルドして配布する: 1. クリエイティブエージェントで**ビルド**する: ```json theme={null} { "message": "Create a holiday promotion banner", "target_format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_728x90" } ] } ``` 2. 各セラーのライブラリに**同期**する: ```json theme={null} { "creatives": [ { "creative_id": "holiday_2026_300x250", "name": "Holiday 2026 - Medium Rectangle", "concept_id": "concept_holiday_2026", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "assets": { } } ] } ``` 各セールスエージェントで `sync_creatives` を個別に呼び出す。セラー間で同じ `creative_id` と `concept_id` を使用して、メディアバイ全体で同じクリエイティブとキャンペーンコンセプトを関連付けられるようにします。完全なパターンは[マルチエージェントクリエイティブオーケストレーション](/docs/creative/multi-agent-orchestration)を参照。 3. セラーごとに**承認状態を追跡**する — 各セラーは独立してレビューします。各エージェントで `list_creatives` をポーリングしてステータスを確認します。クリエイティブはポリシーに基づいて、あるセラーでは `approved`、別のセラーでは `rejected` になる場合があります。 ## 承認状態の追跡 クリエイティブの承認は2つのレベルで動作する: **ライブラリレベル**: [`list_creatives`](/docs/creative/task-reference/list_creatives) の各クリエイティブの `status` フィールド — `processing`、`pending_review`、`approved`、`rejected`、`archived`。 **パッケージレベル**: [`get_media_buys`](/docs/media-buy/task-reference/get_media_buys) の各クリエイティブの `approval_status` — `pending_review`、`approved`、または `rejection_reason` 付きの `rejected`。 クリエイティブはライブラリでは `approved` でも、プレースメント固有のポリシーに違反する場合はパッケージレベルで `rejected` になる可能性があります。新しいクリエイティブを同期またはメディアバイに提出した後は、両方をポーリングします。 ## ダイナミッククリエイティブ最適化(DCO) 動的コンテンツ変数を持つクリエイティブは、`include: { variables: true }` を要求するとライブラリに `variables` 配列付きで表示されます。各変数は広告サーバーが配信時に埋める槽を定義します: ```json theme={null} { "variables": [ { "variable_id": "headline", "name": "Headline text", "type": "text", "default_value": "Holiday Sale — Up to 40% Off" }, { "variable_id": "product_image", "name": "Product image", "type": "image", "default_value": "https://cdn.acme-example.com/hero.jpg" }, { "variable_id": "cta_color", "name": "CTA button color", "type": "color", "default_value": "#FF6600" } ] } ``` `list_creatives` フィルターで `has_variables: true` を使用して DCO クリエイティブを見つける。変数タイプは一般的なプラットフォームパターンと一致する: `text`、`color`、`image`、`video`、`number`、`boolean`。 広告サーバーが配信時にこれらの変数をどのように使用するか(データフィード、ターゲティングルール、最適化アルゴリズム)は AdCP のスコープ外です。AdCP は変数の*スロット*をモデル化し、最適化ロジックはモデル化しません。 ## 次のステップ * [生成クリエイティブ](/docs/creative/generative-creative) — AI 駆動のクリエイティブ生成とメディアバイ内のブリーフワークフロー * [セールスエージェントのクリエイティブ機能](/docs/creative/sales-agent-creative-capabilities) — セラーがメディアとクリエイティブの両方を管理する場合 * [クリエイティブエージェントの実装](/docs/creative/implementing-creative-agents) — プラットフォームを中心に AdCP クリエイティブエージェントを構築します * [sync\_creatives リファレンス](/docs/creative/task-reference/sync_creatives) — アップロード API の詳細 * [list\_creatives リファレンス](/docs/creative/task-reference/list_creatives) — 完全なフィルタリングオプションを含むクエリ API の詳細 # Creative Manifests Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/creative-manifests クリエイティブマニフェストは、ブランドコンテンツのプリセット(再利用可能な構成)を定義し、特定のクリエイティブフォーマットでインスタンス化できるようにします。 マニフェストは構造・レイアウト・レンダリング動作を定義しません。代わりに、クリエイティブフォーマットが定義した要件を満たす具体的なアセット値を提供します。フォーマット定義と組み合わせることで、レンダリング可能なクリエイティブインスタンスが生成されます。 フォーマット、マニフェスト、クリエイティブエージェントの連携概要は [Creative Protocol Overview](/docs/creative) を参照してください。 ## Manifest Structure ### 基本構造 ```typescript theme={null} { format_id: { // Format this manifest is for agent_url: string; // Creative agent URL id: string; // Format identifier }; promoted_offering?: string; // Product being advertised (maps to create_media_buy) assets: { [asset_id: string]: { // Keyed by asset_id from format's assets // Asset type is determined by format specification, not declared here // Type-specific fields depend on asset_type defined in format's assets // Image: url, width, height, format, alt_text // Video: url, width, height, duration_ms, format, bitrate_kbps // Audio: url, duration_ms, format, bitrate_kbps // Text: content, language // URL: url, description // HTML: content or url, version // CSS: content, media // JavaScript: content, module_type // VAST: url or content, vast_version, vpaid_enabled, duration_ms, tracking_events // DAAST: url or content, daast_version, duration_ms, tracking_events, companion_ads // Promoted Offerings: brand_manifest, product_selectors, offerings, asset_selectors // Webhook: url, method, timeout_ms, response_type, security, supported_macros, required_macros } }; } ``` ### Asset ID 各アセットはフォーマットの `assets` 配列で定義された `asset_id` をキーにします。`asset_id` はフォーマット仕様の要件を参照するための技術的識別子です。 **Asset ID の仕組み**: フォーマットが必須アセットを定義する場合: ```json theme={null} { "assets": [ { "asset_id": "banner_image", "asset_type": "image", "required": true }, { "asset_id": "clickthrough_url", "asset_type": "url", "required": true } ] } ``` マニフェストでは **同じ asset\_id をキーとして使用する必要があります**: ```json theme={null} { "assets": { "banner_image": { // ← format の asset_id と一致 "url": "https://cdn.example.com/banner.jpg", "width": 300, "height": 250 }, "clickthrough_url": { // ← format の asset_id と一致 "url": "https://example.com/landing" } } } ``` **一般的な asset\_id**(フォーマットによって異なります): * `banner_image`, `hero_image`: メインビジュアル * `logo`: ブランドロゴ * `headline`, `description`: テキスト * `cta_text`: ボタン文言 * `video_file`: 動画コンテンツ * `vast_tag`: 動画配信用 VAST XML * `clickthrough_url`: ランディングページ URL 必須の asset\_id は必ずフォーマットの `assets` を確認してください。 ### 完全な例 冗長なフィールドを省き、最新の構造を示すクリエイティブマニフェストの例です。 ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "promoted_offering": "Premium Dog Food", "assets": { "banner_image": { "asset_type": "image", "url": "https://cdn.example.com/banner.jpg", "width": 300, "height": 250 }, "headline": { "asset_type": "text", "content": "Nutrition Dogs Love" }, "description": { "asset_type": "text", "content": "Made with real chicken and wholesome grains" }, "clickthrough_url": { "asset_type": "url", "url": "https://acmecorp.example.com/products/premium-dog-food?campaign={MEDIA_BUY_ID}", "description": "Product landing page" } } } ``` **Note**: 各アセットは `asset_type` 判別子(`image`、`text`、`url` など——[アセットタイプ](/docs/creative/asset-types)を参照)を運びます。これにより、バリデーターは 14 すべてではなく一致するアセットスキーマを選択し、選ばれた分岐のみに対してエラーを報告できます。フォーマット仕様も各 `asset_id` に期待される `asset_type` を宣言します。マニフェストとフォーマットは一致すべきです。 ## クリエイティブマニフェストの種類 マニフェストは、上記の 3 つのクリエイティブエージェントモダリティに対応して、静的・動的・ハイブリッドになり得ます。 ### 静的マニフェスト 即時レンダリング可能なすべてのアセットを含むマニフェストです。**Static Asset Delivery** または **Prompt to Static Rendering** モードのクリエイティブエージェントが生成します。 ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "native_responsive" }, "assets": { "hero_image": { "asset_type": "image", "url": "https://cdn.example.com/hero.jpg", "width": 1200, "height": 627, "alt_text": "Product image" }, "logo": { "asset_type": "image", "url": "https://cdn.example.com/logo.png", "width": 100, "height": 100 }, "headline": { "asset_type": "text", "content": "Premium Quality You Can Trust" }, "description": { "asset_type": "text", "content": "Discover why veterinarians recommend our formula" }, "cta_text": { "asset_type": "text", "content": "Learn More" } } } ``` **ユースケース**: * 従来のディスプレイ広告 * 事前レンダリングされた動画広告 * 静的ネイティブ広告 * 固定クリエイティブキャンペーン ### 動的マニフェスト リアルタイム生成用のエンドポイントやコードを含むマニフェストです。**Prompt to Dynamic Rendering** モード(DCO/生成系)で生成されます。 ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_dynamic_300x250" }, "assets": { "dynamic_content": { "asset_type": "webhook", "url": "https://creative-agent.example.com/render/campaign-123", "method": "POST", "timeout_ms": 500, "supported_macros": ["WEATHER", "TIME", "DEVICE_TYPE", "COUNTRY"], "response_type": "html", "security": { "method": "hmac_sha256", "hmac_header": "X-Signature" } }, "fallback_image": { "asset_type": "image", "url": "https://cdn.example.com/fallback-300x250.jpg", "width": 300, "height": 250 } } } ``` **ユースケース**: * 天候連動クリエイティブ * 時間帯に応じたメッセージ切り替え * 商品在庫状況のメッセージ * リアルタイム在庫更新 **Note**: クライアントサイドで動的レンダリングする場合は、webhook の代わりに `html` や `javascript` アセットタイプでタグを埋め込みます。 **動的マニフェストはアセットタイプを混在させられます** — 一部は静的(画像・動画)、一部は動的(webhook、マクロ入りタグ)。例: 静的なヒーロー動画とパーソナライズされたエンドカード webhook を含む動画 VAST タグ。 ### DOOH マニフェスト(インプレッション計測あり) デジタル屋外広告 (DOOH) では、他のフォーマット同様インプレッショントラッキングを使用しますが、デバイス識別子の代わりに会場固有のマクロを利用します。 ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "dooh_billboard_1920x1080" }, "promoted_offering": "Premium Coffee Blend", "assets": { "billboard_image": { "url": "https://cdn.example.com/billboard-1920x1080.jpg", "width": 1920, "height": 1080 }, "impression_tracker": { "url": "https://tracking.example.com/imp?screen={SCREEN_ID}&venue={VENUE_TYPE}&ts={PLAY_TIMESTAMP}&lat={VENUE_LAT}&lon={VENUE_LONG}", "url_type": "tracker_pixel" } } } ``` **DOOH 特有のマクロ**(完全な一覧は [Universal Macros](/docs/creative/universal-macros) を参照): * `{SCREEN_ID}` - 物理スクリーンの一意識別子 * `{VENUE_TYPE}` - 会場カテゴリ(airport, mall, transit, highway, retail) * `{VENUE_LAT}` / `{VENUE_LONG}` - 位置座標 * `{PLAY_TIMESTAMP}` - 掲出時刻(Unix タイムスタンプ) * `{DWELL_TIME}` - 平均滞在時間 仕組みはデジタル広告のインプレッション計測と同じで、表示時に URL が発火します。違いは、DOOH ではデバイス ID ではなく固定の会場コンテキストを使う点です。 ## マニフェストの扱い ### マニフェストの作成 マニフェスト(JSON)は次の 2 通りで作成できます。 **1. 手動組み立て** フォーマット要件と自社アセットを直接組み合わせます。 ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "native_responsive" }, "promoted_offering": "Premium Salmon Formula", "assets": { "hero_image": { "asset_type": "image", "url": "https://cdn.example.com/salmon-hero.jpg", "width": 1200, "height": 627 }, "headline": { "asset_type": "text", "content": "New Premium Salmon Formula" } } } ``` **2. AI 生成(任意)** クリエイティブエージェントに `build_creative` を呼び出し、自然言語のブリーフからマニフェストを生成します。詳細は [Generative Creative](/docs/creative/generative-creative) を参照してください。 ### マニフェストの検証 使用前にマニフェストをフォーマット要件と照合します。 1. **フォーマット整合性**: `format_id` が意図したフォーマットと一致しているか 2. **必須アセット**: フォーマットで必須 (`required: true`) の `asset_id` がすべて `assets` オブジェクトに存在するか 3. **キー一致**: マニフェストの `assets` オブジェクト内の各キーがフォーマットの `assets` 配列の `asset_id` と一致しているか 4. **アセット仕様**: 寸法・ファイルサイズ・尺などフォーマット要件を満たしているか 5. **マクロサポート**: 動的マニフェストの場合、必要なマクロに対応しているか **無効な asset\_id の扱い** * **必須 asset\_id の欠落**: クリエイティブエージェントは欠落した必須アセットをエラーで返し、拒否しなければなりません * **未知の asset\_id**: フォーマットに存在しないキーを含むマニフェストは拒否し、タイプミスや非対応フォーマットを即座に検出します * **asset\_type の不一致**: フォーマット仕様で要求されたタイプと異なる場合は明確な型不一致エラーで拒否します `build_creative` を実装するクリエイティブエージェントは検証を自動で行います。手動で組み立てる場合は、`list_creative_formats` で返されるフォーマット仕様と照合してください。 **フォーマットを前提とした検証**: マニフェストの JSON スキーマでは資産キーに柔軟なパターン(`^[a-z0-9_]+$`)を採用しています。正しいキーかどうか、どのタイプかはフォーマットの `assets` を参照して実行時に検証します。asset\_type 情報はマニフェスト自体には含まれず、`asset_id` をフォーマットの `assets` 配列で引くことで決まります。 #### 検証フロー クリエイティブエージェントがマニフェストを検証する際: 1. マニフェストから **format\_id** を取得 2. フォーマットレジストリからフォーマット仕様を取得(`agent_url` に応じてローカル/リモート) 3. **`manifest.assets` 内の各アセットキーについて:** * フォーマットの `format.assets` で `asset_id` を検索 * 見つからなければ → 「Unknown asset\_id ...」のエラーで拒否 * 見つかれば → フォーマット要件から期待する `asset_type` を特定 * アセットタイプスキーマ(例: `/schemas/v3/core/assets/image-asset.json`)を取得 * アセットペイロードをスキーマに対して検証 * フォーマットの `requirements` フィールドにある追加制約を検証 4. **必須アセットがすべて存在するか確認**(フォーマットで `required: true` のもの) 5. **タイプ固有の制約を検証**(寸法、ファイルサイズ、尺など) フォーマット仕様が、各 `asset_id` のタイプと適用される制約の単一の信頼できる情報源となります。 **検証はスキーマ時ではなく実行時**: クリエイティブマニフェストの JSON スキーマはアセットキーに柔軟なパターンを使っています。妥当性チェックは、マニフェストをクリエイティブエージェントに送信した際に、対応するフォーマットの `assets` を基に行われます。 ### マニフェストのプレビュー `preview_creative` タスクでマニフェストのレンダリングを確認します。 ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "native_responsive" }, "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "native_responsive" }, "assets": { "hero_image": { "url": "https://cdn.example.com/hero.jpg", "width": 1200, "height": 627 }, // ... other assets } }, "macro_values": { "CLICK_URL": "https://example.com/landing", "CACHE_BUSTER": "12345" } } ``` クリエイティブエージェントはプレビュー URL とレンダリング結果を返します。 ### マニフェストの送信 マニフェストは `sync_creatives` でクリエイティブラリに登録し、メディアバイで ID を参照します。 ```json theme={null} { "task": "sync_creatives", "parameters": { "creatives": [ { "creative_id": "native-salmon-v1", "name": "Salmon Special Native Ad", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "native_responsive" }, "manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "native_responsive" }, "promoted_offering": "Fresh Pacific Salmon", "assets": { "headline": { "content": "Fresh Pacific Salmon - 20% Off Today" }, "main_image": { "url": "https://cdn.example.com/salmon.jpg", "width": 1200, "height": 628 } } } } ] } } ``` その後、メディアバイで `creative_id` を参照します。マニフェストは 1 つのフォーマットに対応します。 ## マクロ置換 マニフェストは動的値のためのマクロプレースホルダーをサポートします。AdCP ではすべてのパブリッシャーで一貫して動作するユニバーサルマクロを使用します。 利用可能なマクロ、置換プロセス、フォーマット固有のマクロサポートについては [Universal Macros](/docs/creative/universal-macros) を参照してください。 ## Best Practices ### クリエイティブエージェント向け 1. **完全なマニフェスト**: フォーマットの必須アセットをすべて含めます 2. **アセット検証**: アセットがフォーマット仕様を満たすことを確認します 3. **フォールバック提供**: 動的クリエイティブにはフォールバックアセットを含めます 4. **マクロの明示**: 使用するマクロを明確にします 5. **バージョニング**: アセット管理のためにバージョン付き URL を使用します ### パブリッシャー向け 1. **受領時の検証**: フォーマット要件と照合します 2. **アセットキャッシュ**: ホストされたアセットを事前取得・キャッシュします 3. **障害対応**: 動的マニフェストにはフォールバックレンダリングを実装します 4. **マクロサポート**: ユニバーサルマクロを実装します 5. **テンプレート提供**: カスタムフォーマット向けにレンダリングテンプレートを提供します ### バイヤー向け 1. **検証**: マニフェストがフォーマット要件を満たすことを確認(手動または `build_creative`) 2. **プレビュー**: 送信前に必ずプレビューします 3. **マクロテスト**: マクロ置換が期待どおりか確認します 4. **アセット最適化**: アセットのサイズや圧縮を適切に行います 5. **ライブラリ整理**: クリエイティブラリでアセット管理を行います ## Advanced Topics ### カタログ カタログ駆動のフォーマット(プロダクトカルーセル、ダイナミックプロダクト広告)のマニフェストは、カタログを `assets` マップ内のアセットとして含めます。ワークフローについては[クリエイティブ内のカタログ](/docs/creative/catalogs#catalogs-in-creatives)を参照してください。 ### 繰り返し可能なアセットグループ カルーセルやスライドショーなど複数アセットのフォーマットについては、[Carousel & Multi-Asset Formats](/docs/creative/channels/carousels) を参照してください。 ## スキーマリファレンス * [Creative Manifest Schema](https://adcontextprotocol.org/schemas/v3/core/creative-manifest.json) * [Preview Creative Request](https://adcontextprotocol.org/schemas/v3/creative/preview-creative-request.json) * [Preview Creative Response](https://adcontextprotocol.org/schemas/v3/creative/preview-creative-response.json) ## 関連ドキュメント * [Creative Formats](/docs/creative/formats) - フォーマット仕様とディスカバリー * [Channel Guides](/docs/creative/channels/video) - 動画・ディスプレイ・音声・DOOH・カルーセルのフォーマット例 * [build\_creative タスク](/docs/creative/task-reference/build_creative) * [preview\_creative タスク](/docs/creative/task-reference/preview_creative) # Creative Formats Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/formats AdCP のクリエイティブフォーマットは、広告の構築と配信を形成するアセット要件・技術的制約・レンダリング動作を定義します。 > **3.1 の正準フォーマット**: 3.1 以降、フォーマットはプロダクト上で `format_options`(12 の正準フォーマットの一つを絞り込む `ProductFormatDeclaration` の配列)を介してインラインで宣言できます。このページは v1 のフォーマットレジストリモデルを説明します。これは 4.x を通じて第一級のパスであり続けます。正準フォーマットのモデルについては、[canonical-formats](/docs/creative/canonical-formats) と[マイグレーションガイド](/docs/creative/canonical-formats-migration)を参照してください。 クリエイティブフォーマットは、広告クリエイティブを実体化するための構造的・技術的要件を定義します。フォーマットは次を規定します。 * `assets` 配列を通じて必要なアセットタイプ(動画、画像、テキスト、音声など) * 各アセットの技術的制約(尺・寸法・ファイルタイプ・上限) * 生成されたクリエイティブの配信方法と検証方法 フォーマットが定義するのは要件と制約であり、ブランドコンテンツやレイアウトロジックではありません。 フォーマット、マニフェスト、クリエイティブエージェントの関係概要は [Creative Protocol Overview](/docs/creative) を参照してください。 ## Standard フォーマットと Custom フォーマット AdCP は権威性と再利用性に基づいて 2 種類のフォーマットをサポートします(機能差ではありません)。 ### Standard Formats Standard フォーマットは **AdCP リファレンスクリエイティブエージェント** (`https://creative.adcontextprotocol.org`) が提供する事前定義の仕様です。 Standard フォーマットの特徴: * **業界整合**: IAB のフォーマットファミリーや一般的な慣行に基づく * **移植性**: プラットフォームをまたいで一貫して動作 * **検証済み**: 既知の技術要件に対して事前にテスト済み * **探索可能**: `list_creative_formats` で返されます * **メンテナンス済み**: 中央で文書化され、更新されます Standard フォーマットは次のような一般的ユースケースをカバーします。 * 固定またはレスポンシブ寸法のディスプレイフォーマット * 標準尺とアスペクト比を持つ動画フォーマット * 規定尺のオーディオフォーマット * 代表的な DOOH ディスプレイおよび動画実装 **営業エージェント向けの指針:** カスタムフォーマットを定義する前に、同等の Standard フォーマットが存在しないか確認してください。多くのパブリッシャーは一般的な在庫に Standard フォーマットを参照し、真に差別化された提供だけをカスタムにします。詳細は [Implementing Standard Format Support](/docs/media-buy/capability-discovery/implementing-standard-formats) を参照。 ### Custom Formats Custom フォーマットは、Standard では正確に表現できない在庫向けに、パブリッシャーやプラットフォームが定義します。 使用するケース: * **固有の制約**: 非標準の寸法、実在のディスプレイ、特別なアセット要件 * **特殊能力**: プラットフォーム固有の描画やインタラクション * **プレミアム在庫**: 差別化されたオーダーメイドの広告商品 * **カスタム検証ロジック**: パブリッシャー独自の審査や組み立てルール 必要な場合にのみカスタムを導入し、可能な限り Standard を再利用して移植性と理解を高めてください。 ## フォーマットの探索 バイヤーは営業エージェントが公開する `list_creative_formats` タスクでサポートフォーマットを探索します。 フォーマットの取得元: * **営業エージェント自身** - カスタムフォーマット * **参照するクリエイティブエージェント** - 例えばリファレンスエージェントなどの Standard フォーマット **探索レスポンス例:** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/creative/list-creative-formats-response.json", "status": "completed", "formats": [ { "format_id": { "agent_url": "https://youragent.com", "id": "homepage_takeover_2024" }, "name": "Homepage Takeover", "type": "rich_media" } ], "creative_agents": [ { "agent_url": "https://creative.adcontextprotocol.org" } ] } ``` 営業エージェントが自社カスタムフォーマットと、参照するクリエイティブエージェントの全フォーマットをサポートしていることを示します。 **フォーマットサポートを実装する営業エージェントへ:** [Implementing Standard Format Support](/docs/media-buy/capability-discovery/implementing-standard-formats) を参照。 ## フォーマットの権威 各フォーマットは `agent_url` を持ち、そのフォーマットに対する権威クリエイティブエージェントを示します。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/format.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_hosted" }, "name": "Standard 30-Second Video" } ``` 権威クリエイティブエージェントが提供するもの: * 完全なフォーマット仕様 * クリエイティブ要素の要件と制約 * 検証ルール * プレビュー生成 * 正式なドキュメント バイヤーと営業エージェントは `agent_url` を参照して正式なフォーマット情報を取得します。 ## フォーマットのビジュアル表現 フォーマットは、UI でのブラウズや選択を支援するメタデータを任意で含められます。 ### Showcase 例 **Field**: `example_url` **Purpose**: パブリッシャー管理のショーケースへのディープリンク 含められるもの: * インタラクティブデモ * 動画 * 複数のクリエイティブ例 * ベストプラクティスや仕様 これにより、プロトコルの表現を制限せず複雑なフォーマットを提示できます。 **例**: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/format.json", "format_id": { "agent_url": "https://publisher.com", "id": "homepage_takeover_premium" }, "name": "Premium Homepage Takeover", "type": "rich_media", "description": "Full-screen immersive experience with video, carousel, and companion units", "example_url": "https://publisher.com/formats/homepage-takeover-demo" } ``` ## フォーマット参照 `format_id`(構造化された参照オブジェクト)と `format`(完全な定義オブジェクト)の規範的な対比——最も一般的な二つのバリデーションエラーを含む——については、[フォーマット参照](/docs/protocol/format-references)を参照してください。 AdCP では曖昧さや名前衝突を避けるため、常に構造化されたフォーマット識別子を使います。 ### 構造化フォーマット ID(必須) **すべてのフォーマット参照** は構造化された format ID オブジェクトを使います。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/format-id.json", "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" } ``` この方式により次が保証されます。 * 明示的な名前空間 * 衝突しない識別子 * スキーマ検証 * 互換性を壊さない拡張性 文字列のみのフォーマット ID を API 契約で使用してはなりません。 ### 構造化フォーマット ID の使用箇所 **リクエスト:** * `sync_creatives` - クリエイティブアセットのアップロード * `build_creative` - クリエイティブエージェントによる生成 * `preview_creative` - プレビュー生成 * `create_media_buy` - フォーマット要件の指定 **レスポンス:** * `list_creatives` - クリエイティブ詳細の返却 * `get_products` - プロダクトのフォーマット能力 * `list_creative_formats` - フォーマット定義 * クリエイティブやフォーマット情報を含むあらゆるレスポンス **フィルターパラメーター:** * リクエストフィルターの `format_ids`(複数形) - 構造化 format\_id オブジェクトの配列 ### 検証ルール **すべての AdCP エージェントが守るべきこと:** 1. ✅ すべてのコンテキストで構造化 `format_id` を受け付けます 2. ✅ すべてのレスポンスで構造化 `format_id` を返す 3. ❌ 文字列のみの format\_id をエラー付きで拒否します 4. ❌ 文字列 format\_id を API 契約で使用しません **不正な format\_id のエラーハンドリング:** ```json theme={null} { "error": "invalid_format_id", "message": "format_id must be a structured object with 'agent_url' and 'id' fields", "received": "display_300x250", "required_structure": { "agent_url": "https://creative-agent-url.com", "id": "display_300x250" } } ``` ### レガシー考慮 一部のレガシーシステムは文字列 format\_id を送る場合があります。実装者の選択肢: 1. **厳格な検証**(推奨): 文字列を即時にエラーで拒否 2. **非推奨化を伴う自動アップグレード**: 一時的に受け付け、警告をログに残し、廃止時期を明示 自動アップグレードする場合は以下を守ってください。 * agent\_url にマッピングできる既知のフォーマットのみ文字列を受け入れる * 不明なフォーマット文字列は即座に失敗させる * すべてのリクエストで非推奨警告をログ出力します * 廃止日を設定し通知する(最大 6 か月を推奨) ## フォーマット構造 フォーマットは次の主要フィールドを持つ JSON オブジェクトです。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/format.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_hosted" }, "name": "30-Second Hosted Video", "type": "video", "assets": [ { "item_type": "individual", "asset_id": "video_file", "asset_type": "video", "asset_role": "hero_video", "required": true, "requirements": { "duration": "30s", "format": ["MP4"], "resolution": ["1920x1080", "1280x720"] } }, { "item_type": "individual", "asset_id": "end_card_image", "asset_type": "image", "asset_role": "end_card", "required": false, "requirements": { "dimensions": "1920x1080", "format": ["PNG", "JPG"] } }, { "item_type": "individual", "asset_id": "companion_banner", "asset_type": "image", "asset_role": "companion", "required": false, "requirements": { "dimensions": "300x250", "format": ["PNG", "JPG", "GIF"] } }, { "item_type": "individual", "asset_id": "impression_tracker", "asset_type": "url", "asset_role": "third_party_tracking", "required": false, "requirements": { "description": "Third-party impression tracking pixel URL" } } ], // DEPRECATED: Use "assets" above instead. Kept for backward compatibility. "assets": [ { "item_type": "individual", "asset_id": "video_file", "asset_type": "video", "asset_role": "hero_video", "requirements": { "duration": "30s", "format": ["MP4"], "resolution": ["1920x1080", "1280x720"] } } ] } ``` **主なフィールド:** * **format\_id**: 一意の識別子(`domain:id` のように名前空間化も可) * **agent\_url**: 当該フォーマットの権威クリエイティブエージェント * **type**: *(非推奨、任意)* 高水準のカテゴリヒント。後述の注意事項を参照。 * **assets**: すべてのアセット仕様の配列。`required` ブール値で必須/任意を示します。クリエイティブ要件を理解するための正式な情報源。 * **asset\_role**: アセットの役割(hero\_image, logo, cta\_button など) * **renders**: レンダリング成果物の配列(寸法付き、後述) * **accessibility**: 任意の WCAG 準拠宣言 — [Accessibility](/docs/creative/accessibility) を参照 **非推奨のお知らせ**: `type` フィールドは非推奨で任意です。クリエイティブ要件(動画・画像・テキストなど)の正確な情報は `assets` 配列が提供するため、こちらを使用すべきです。「video」「display」「native」といったカテゴリは、Performance Max(複数チャネルにまたがる)・検索広告(高インテントのテキストのみ)・会話型 AI プレースメントなど、新興フォーマットへの対応が困難な情報損失の抽象化です。 ### アセットディスカバリ `assets` 配列でアセット要件を包括的に示します。各アセットは `required` ブール値を持ちます。 * **`required: true`** - 有効なクリエイティブには必須 * **`required: false`** - 任意。提供されればリッチになる(例: コンパニオンバナー、第三者トラッキングピクセル) この統一的な表現により、クリエイティブツールや AI エージェントはフォーマットの能力を理解し、最小要件を明確にしつつ任意アセットがあればリッチな体験を提供できます。 ### サードパーティトラッカーのサポート フォーマットがサードパーティ計測をサポートするかどうかは、その `assets` 配列にトラッカースロットが含まれるかどうかで決まります。トラッカースロットは、`asset_type: "url"` を持ち、かつ次のいずれかを持つ任意のアセットです: * `url_type: "tracker_pixel"` または `url_type: "tracker_script"`(メカニズム側の宣言)、または * `impression_tracker`、`click_tracker`、`viewability_tracker`、`third_party_tracker` のいずれかの `requirements.role`(目的側の宣言。フォーマットの作者がメカニズムではなく役割を宣言していても、スロットはトラッカー URL を受け入れます) バイヤーエージェントは、サードパーティ検証を必要とするクリエイティブを割り当てる前にこれを確認すべきです。 ほとんどのデジタルフォーマット(ディスプレイ、動画、CTV、音声、DOOH)には任意の `impression_tracker` アセットが含まれます。トラッカースロットのないフォーマット——放送 TV スポットなど——は、クリエイティブレベルのピクセルトラッキングをサポートしません。これらのフォーマットの計測は、クリエイティブに埋め込まれたピクセルではなく、プロダクトの `billing_measurement` 条件で宣言された外部ソース(パネルデータ、セットトップボックスのテレメトリ)に由来します。 DoubleVerify のビューアビリティや IAS のブランドセーフティ計測を必要とするバイヤーエージェントは、トラッカーサポートでフォーマットをフィルタリングすべきです。トラッカースロットが存在しない場合、それらのベンダーはクリエイティブを計測できません——バイヤーは代わりにセラーが宣言した計測ベンダーに頼らなければなりません。 ### 型付きアセット要件 アセットタイプごとに、そのアセットに適用される制約を定義した要件スキーマがあります。`requirements` オブジェクトは `asset_type` に基づいて型付けされます。 **画像アセット** (`asset_type: "image"`): * `min_width`, `max_width`, `min_height`, `max_height` - 寸法制約(正確な寸法を指定する場合は min=max にします) * `aspect_ratio` - 必須アスペクト比(例: '1:1') * `formats` - 許可ファイル形式(jpg, jpeg, png, webp, gif, svg, avif) * `max_file_size_kb` - 最大ファイルサイズ * `animation_allowed` - アニメーション画像を受け付けるか * `max_animation_duration_ms` - 最大アニメーション時間 **動画アセット** (`asset_type: "video"`): * `min_width`, `max_width`, `min_height`, `max_height` - 寸法制約 * `aspect_ratio` - 必須アスペクト比(例: '16:9') * `min_duration_ms`, `max_duration_ms` - 尺の制約 * `containers` - 許可コンテナ形式(mp4, webm, mov) * `codecs` - 許可コーデック(h264, h265, vp9, av1) * `frame_rates` - 許可フレームレート(例: \[24, 30, 60]) * `max_file_size_kb`, `max_bitrate_kbps` - サイズ制約 **HTML アセット** (`asset_type: "html"`): * `max_file_size_kb` - 最大ファイルサイズ * `sandbox` - 実行環境(`none`, `iframe`, `safeframe`, `fencedframe`) * `external_resources_allowed` - 外部スクリプト/画像の読み込みを許可するか * `allowed_external_domains` - 外部リソースで許可するドメイン **JavaScript アセット** (`asset_type: "javascript"`): * `max_file_size_kb` - 最大ファイルサイズ * `module_type` - モジュール形式(`script`, `module`, `iife`) * `external_resources_allowed` - 動的リソース読み込みを許可するか * `allowed_external_domains` - 動的読み込みで許可するドメイン **音声アセット** (`asset_type: "audio"`): * `min_duration_ms`, `max_duration_ms` - 尺の制約 * `formats` - 許可音声形式(mp3, aac, wav, ogg, flac) * `sample_rates` - 許可サンプルレート(Hz単位、例: \[44100, 48000]) * `channels` - 許可チャンネル構成(mono, stereo) * `min_bitrate_kbps`, `max_bitrate_kbps` - ビットレート制約 * `max_file_size_kb` - 最大ファイルサイズ **テキストアセット** (`asset_type: "text"`): * `min_length`, `max_length` - 文字数制限 * `min_lines`, `max_lines` - 行数制限 * `character_pattern` - 許可文字の正規表現 * `prohibited_terms` - 禁止ワード/フレーズ * `allowed_values` - 許可される文字列のクローズドリスト。リスト外の提出は `CREATIVE_VALUE_NOT_ALLOWED` で拒否されます **クローズドセットのテキスト例(法務/ブランド承認済みの CTA リスト):** ```json theme={null} { "asset_id": "cta_label", "asset_type": "text", "required": true, "requirements": { "max_length": 14, "allowed_values": ["Shop Now", "Learn More", "Get Quote", "Find a Provider"] } } ``` 規制対象のバーティカル(製薬、金融サービス)やブランド管理のプログラムのセラーは、法務またはブランドが事前承認した正確な文字列を公開するために `allowed_values` を使います。宣言されたすべての制約は連言的で(すべての許可値は `max_length`、`character_pattern` なども満たさなければならない)、マッチングは大文字小文字を区別します——バイヤーはリストされた文字列の一つを逐語的に提出しなければなりません(MUST)。テキストスロットを埋めるために LLM を使うバイヤーエージェントは、自由に生成して拒否後にリトライするのではなく、`allowed_values` を制約付きサンプリングのオプションとして渡すべきです。 提出された値がリストにない場合、セラーは `CREATIVE_VALUE_NOT_ALLOWED` を返さなければならず(MUST)、`error.field` は問題のアセットパスを指し、`error.details.allowed_values` はフォーマットのリストをエコーして、バイヤーが決定論的に再プロンプトできるようにします。 **URL アセット** (`asset_type: "url"`): * `role` - 標準的な用途(clickthrough, impression\_tracker, click\_tracker など) * `protocols` - 許可プロトコル(https, http) * `allowed_domains` - 許可遷移先ドメイン * `macro_support` - マクロ置換をサポートするか **HTML 実行コンテキストを含む例:** ```json theme={null} { "asset_id": "banner_html", "asset_type": "html", "required": true, "requirements": { "max_file_size_kb": 150, "sandbox": "safeframe", "external_resources_allowed": false } } ``` これはクリエイティブエージェントに「外部リソース読み込みなしの SafeFrame コンテナで動作する HTML を構築せよ」と伝えます。 詳細は [Display Ads - HTML Display Formats](/docs/creative/channels/display#html-display-formats-with-execution-context) を参照。 ### アセットオーバーレイ 一部のフォーマットは、バイヤーのクリエイティブコンテンツの上に重なるパブリッシャー管理要素(動画プレイヤーコントロール、パブリッシャーロゴなど)を含みます。フォーマットはこれらを関連アセットの `overlays` として宣言し、クリエイティブエージェントとバイヤーがどの領域が覆われるかを把握して合成できるようにします。 オーバーレイの境界はアセット自身の左上隅を基準とした相対値で表します。`unit` フィールドは `"px"`(絶対ピクセル)または `"fraction"`(アセット寸法に対する比率: `x`/`y` はアセット自身の端から 0.0、`width`/`height` は 0.12 がアセット寸法の 12%)のいずれかです。同じアセット上の異なるオーバーレイが異なる単位を使う場合があり、クリエイティブエージェントはそれぞれ独立して処理しなければなりません。 ```json theme={null} { "item_type": "individual", "asset_id": "video", "asset_type": "video", "required": true, "requirements": { "aspect_ratio": "16:9", "max_duration_ms": 15000 }, "overlays": [ { "id": "play_pause", "description": "Play/pause control — avoid placing CTA, copy, or logos here", "bounds": { "x": 0, "y": 285, "width": 120, "height": 120, "unit": "px" }, "visual": { "url": "https://publisher.example.com/controls/play.svg" } }, { "id": "volume", "description": "Volume control", "bounds": { "x": 0.88, "y": 0.85, "width": 0.12, "height": 0.15, "unit": "fraction" }, "visual": { "light": "https://publisher.example.com/controls/volume-light.svg", "dark": "https://publisher.example.com/controls/volume-dark.svg" } } ] } ``` `visual` フィールドは任意ですが強く推奨されます。クリエイティブエージェントがパブリッシャーのクロームを重ねた正確なプレビューを合成できるようにします。テーマに依存しないグラフィック(例: `currentColor` を使う SVG)には `url` を使い、ライト/ダークの個別アセットが必要な場合は `light`/`dark` バリアントを指定してください。 ### レンダリング成果物と寸法 フォーマットは 1 つ以上のレンダー成果物を定義し、それぞれに寸法と意味的ロールを付与します。 対応するシナリオ: * 単一レンダー * コンパニオン付きクリエイティブ * 複数掲出面の出力 * レスポンシブ挙動 * 非個人環境向けの物理寸法 レンダーは `renders` 配列で指定します。多くは 1 つのレンダーですが、コンパニオン広告やアダプティブ、マルチプレースメントでは複数レンダーを持ちます。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/format.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "name": "Display Banner 300x250", "type": "display", "renders": [ { "role": "primary", "dimensions": { "width": 300, "height": 250, "responsive": { "width": false, "height": false } } } ] } ``` **複数レンダー例(コンパニオン広告):** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/format.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_with_companion_300x250" }, "name": "Video with Companion Banner", "type": "video", "renders": [ { "role": "primary", "dimensions": { "width": 1920, "height": 1080, "responsive": { "width": false, "height": false } } }, { "role": "companion", "dimensions": { "width": 300, "height": 250, "responsive": { "width": false, "height": false } } } ] } ``` **寸法タイプ:** **固定寸法**(標準ディスプレイ広告): ```json theme={null} { "role": "primary", "dimensions": { "width": 300, "height": 250, "responsive": {"width": false, "height": false}, "unit": "px" } } ``` **幅レスポンシブ**(フルードバナー): ```json theme={null} { "role": "primary", "dimensions": { "min_width": 300, "max_width": 970, "height": 250, "responsive": {"width": true, "height": false}, "unit": "px" } } ``` **アスペクト比制約**(ネイティブフォーマット): ```json theme={null} { "role": "primary", "dimensions": { "aspect_ratio": "16:9", "min_width": 300, "responsive": {"width": true, "height": true}, "unit": "px" } } ``` **物理寸法**(DOOH): ```json theme={null} { "role": "primary", "dimensions": { "width": 48, "height": 14, "responsive": {"width": false, "height": false}, "unit": "inches" } } ``` **renders 構造の利点:** * 単一/複数レンダーを同一構造で表現 * 文字列解析不要で構造化された寸法 * スキーマで寸法を検証 * レスポンシブと固定を同等に表現 * 適切なプレビュー生成を支援 * 寸法ベースのフィルタリングを可能に * DOOH 向けに物理単位をサポート * 各レンダーの意味的ロールが明確 ## フォーマット要件の理解 従来の IAB フォーマットファミリー(ディスプレイ、動画、音声、ネイティブ)は、新興フォーマットにスケールしない情報損失を伴う抽象化です: * **Performance Max** は動画・ディスプレイ・検索・ネイティブを同時にまたぐ * **検索広告**(RSA)はインテントの高い文脈でのテキストのみ * **会話型 AI** プレースメントは従来のカテゴリに当てはまらない `assets` 配列が必要なクリエイティブ要素を正確に示します。 * 動画が必要? `asset_type: 'video'` を確認 * 画像が必要? `asset_type: 'image'` を確認 * テキストのみ? 必須アセットがすべて `asset_type: 'text'` AdCP は複数のメディアタイプにわたるフォーマットをサポートします。 ### Video Formats * 標準動画(15s, 30s, 60s) * モバイル/ストーリー向け縦動画 * VAST/VPAID タグ * インタラクティブ動画 詳細は [Video Channel Guide](/docs/creative/channels/video) を参照。 ### Display Formats * 標準 IAB サイズ(300x250、728x90 など) * レスポンシブユニット * リッチメディア/エキスパンダブル * HTML5 クリエイティブ 詳細は [Display Channel Guide](/docs/creative/channels/display) を参照。 ### Audio Formats * ストリーミングオーディオ(15s, 30s, 60s) * ポッドキャスト差し込み * コンパニオンバナー * VAST オーディオタグ 詳細は [Audio Channel Guide](/docs/creative/channels/audio) を参照。 ### DOOH Formats * デジタルビルボード * 交通機関ディスプレイ * リテールスクリーン * 会場ベースのインプレッショントラッキング 詳細は [DOOH Channel Guide](/docs/creative/channels/dooh) を参照。 ### Carousel / Multi-Asset Formats * プロダクトカルーセル(3〜10 アイテム) * ストーリーシーケンス * スライドショー * フレームベース構造 詳細は [Carousel Channel Guide](/docs/creative/channels/carousels) を参照。 ## マルチアセット/フレームベースフォーマット カルーセルやスライドショー、ストーリーのように、各フレームに複数アセットを持つ繰り返しグループを使うフォーマットがあります。パターンの詳細は [Carousel & Multi-Asset Formats](/docs/creative/channels/carousels) を参照してください。 ## レポートメトリクス フォーマットは `reported_metrics` フィールドを使って、配信レポートで生成されるメトリクスを宣言できます。これにより、バイヤーはそのフォーマットの在庫を購入した場合にどのようなデータを受け取れるかを把握できます。 ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_hosted" }, "name": "30-Second Hosted Video", "reported_metrics": [ "impressions", "spend", "views", "completed_views", "completion_rate", "quartile_data" ] } ``` ### プロダクトメトリクスとの交差 プロダクトは `reporting_capabilities.available_metrics` で利用可能なメトリクスを宣言します。バイヤーが受け取るのは、フォーマットの `reported_metrics` とプロダクトの `available_metrics` の**積集合**です。 `impressions` と `spend` は積集合に関わらず常に報告されます。これらはすべての配信レスポンスに暗黙的に含まれます。積集合はそれ以外のメトリクスに適用されます。 例えば、動画フォーマットが `quartile_data` を宣言していても、プロダクトが `impressions`・`spend`・`clicks` しか報告しない場合、バイヤーはそのプロダクトのクォータイルデータを受け取れません。 ### 省略する場合 `reported_metrics` を省略した場合、フォーマットはプロダクトレベルのメトリクス宣言に完全に委ねます。これは、クリエイティブタイプが利用可能なメトリクスを本質的に制限しないフォーマット(例: メトリクスがプラットフォームに依存するネイティブフォーマット)に適しています。 ### 一般的なパターン **動画フォーマット**: `impressions`, `spend`, `views`, `completed_views`, `completion_rate`, `quartile_data` **ディスプレイフォーマット**: `impressions`, `spend`, `clicks`, `ctr`, `viewability` **DOOH フォーマット**: `impressions`, `spend`, `dooh_metrics` **ソーシャル/パフォーマンスフォーマット**: `impressions`, `spend`, `clicks`, `ctr`, `conversions`, `engagement_rate` ## フォーマットカード フォーマットカードは、ブラウズ/選択 UI でフォーマットを視覚的に示すための定義です。クリエイティブエージェントは、カードフォーマットと必要アセットを含むカード定義を任意で提供できます。 ### カードタイプ クリエイティブエージェントは少なくとも Standard カードを、必要に応じて詳細カードも提供してください。 **Standard Card** (`format_card`): * フォーマットブラウズ用のコンパクトな 300x400px カード * Retina 向けに 2x 密度画像をサポート * 仕様を素早く把握可能 **Detailed Card** (`format_card_detailed`, 任意): * レスポンシブレイアウトで、ヒーローカルーセルとテキスト説明を並べて表示 * 下部に Markdown 仕様セクション * [Yahoo の広告仕様](https://adspecs.yahooinc.com/premium-ads/e2e-horizon-desktop) のような詳細ドキュメントを提供 ### 構造 ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_standard_30s" }, "name": "Standard Video - 30 seconds", "type": "video", // ... other format fields ... "format_card": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "format_card_standard" }, "manifest": { "display_name": "30-Second Video", "preview_mockup_url": "https://cdn.example.com/format-mockups/video_30s.png", "format_type_label": "Video" } }, "format_card_detailed": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "format_card_detailed" }, "manifest": { "display_name": "Standard 30-Second Video", "description": "The Edge A-Logs Horizon (desktop) format is an encompassing...", "carousel_images": [ "https://cdn.example.com/formats/video_30s_context1.jpg", "https://cdn.example.com/formats/video_30s_context2.jpg" ], "specifications_markdown": "# Technical Specifications\n\n..." } } } ``` ### カードの描画 カード表示には 2 つの方法があります。 1. **`preview_creative` を使用**: カードフォーマットとマニフェストを渡してレンダリング 2. **事前レンダリング**: クリエイティブエージェントがカードを生成し、静的に配信 インフラに合わせて動的生成と静的ホスティングを選択できます。 ### Standard カードフォーマット リファレンスクリエイティブエージェントは次の 2 種の標準カードフォーマットを定義します。 * **`format_card_standard`** (300x400px) - フォーマットブラウズ用のコンパクトカード * **`format_card_detailed`** (レスポンシブ) - カルーセルと詳細仕様を含むリッチカード クリエイティブエージェントは独自のカードフォーマットを定義し、ユニークな能力を強調したりブランドに合わせたりできます。 **Note**: 標準カードフォーマットの定義はプロトコル仕様ではなく [creative-agent repository](https://github.com/adcontextprotocol/creative-agent) で管理されています。 ### フォーマットカードを含めるべき場面 フォーマットカードは任意ですが、次のケースで推奨されます。 * モックアップが理解に役立つビジュアル系フォーマット(display, video, DOOH) * 複数アセット要件を持つ複雑なフォーマット * Standard と異なるカスタムフォーマット * ビジュアルプレビューがバイヤー理解を助ける場合 広告仕様ページのような詳細説明が必要な場合は detailed カードを使ってください。 ### クライアント描画ガイドライン UI でフォーマットを表示する際は次のフォールバック順を推奨します。 1. **`format_card` がある** → `preview_creative` で描画、または事前レンダリング画像を表示 2. **`format_card` がない** → テキストのみ(フォーマット名 + 説明)を表示 3. **カード描画に失敗** → テキストのみ表示にフォールバック 利用可能なメタデータにかかわらず、安定したユーザー体験を提供できます。 ## 関連ドキュメント * [Creative Protocol Overview](/docs/creative) - フォーマット・マニフェスト・エージェントの連携 * [Creative Manifests](/docs/creative/creative-manifests) - アセットとフォーマットの組み合わせ方 * [Asset Types](/docs/creative/asset-types) - アセット仕様の理解 * [Channel Guides](/docs/creative/channels/video) - メディアタイプ別の詳細フォーマット * [Implementing Standard Format Support](/docs/media-buy/capability-discovery/implementing-standard-formats) - 営業エージェント向け * [list\_creative\_formats Task](/docs/creative/task-reference/list_creative_formats) - フォーマット探索の API リファレンス # 生成系クリエイティブ Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/generative-creative AdCP の生成系クリエイティブは AI を使ってブリーフから広告アセットを生成します。静的マニフェスト、アセットグループ最適化、コンテキスト別生成の3段階があります。 Creative Protocol は、広告キャンペーン向けに AI を活用したクリエイティブ生成とアセット管理を可能にします。本ガイドでは 5 分で最初のクリエイティブを作成する方法を説明します。 Three tiers of generative creative: Tier 1 static (one creative, one variant), Tier 2 optimized (asset group combinations), Tier 3 generated (AI creates for each context) > **技術リファレンス**: 本ガイドでは [`build_creative` タスク](/docs/creative/task-reference/build_creative) の使い方を示します。完全な API 仕様はタスクリファレンスを参照。 ## 概要 Creative Protocol は AI を用いたクリエイティブ生成を提供します。 * **`build_creative`**: 静的マニフェストまたは動的コードを使い AI でクリエイティブを生成 * **`preview_creative`**: クリエイティブマニフェストのプレビューを生成 * **`list_creative_formats`**: サポートされるクリエイティブフォーマットを探索 アセットは [brand identity](/docs/brand-protocol/brand-json) 経由で提供され、個別のアセットライブラリ管理は不要です。 ## クイックスタート: 最初のクリエイティブを生成します ### ステップ 1: 基本的な生成 ネイティブディスプレイ広告を生成する最もシンプルなリクエスト例: ```json theme={null} { "message": "Create a simple ad for a coffee shop promotion - 20% off all drinks this week", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_native" } } ``` ### ステップ 2: レスポンスの理解 構造化されたクリエイティブマニフェストが返されます。 ```json theme={null} { "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_native" }, "assets": { "headline": { "content": "20% Off All Drinks This Week!" }, "description": { "content": "Visit our cozy coffee shop and enjoy premium coffee at an unbeatable price." }, "call_to_action": { "content": "Visit Today" } } } } ``` ### ステップ 3: クリエイティブをプレビューします 反復前にどのように見えるかを確認するためにマニフェストをプレビューする: ```json theme={null} { "request_type": "single", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_native" }, "assets": { "headline": { "content": "20% Off All Drinks This Week!" }, "description": { "content": "Visit our cozy coffee shop and enjoy premium coffee at an unbeatable price." }, "call_to_action": { "content": "Visit Today" } } } } ``` レスポンスには iframe に埋め込める preview URL が含まれます。デバイスバリアント、バッチプレビュー、出力フォーマットオプションは [`preview_creative`](/docs/creative/task-reference/preview_creative) を参照。 ### ステップ 4: クリエイティブを洗練します 前回の出力の `creative_manifest` を新しい `message` と一緒に渡すことで反復できます。あるいは、brief アセット(`assets.brief`)を更新してクリエイティブの方向性を変えることもできる — brief はクリエイティブがどうあるべきかについてバイヤーが管理するソースオブトゥルースです。 ```json theme={null} { "message": "Make the headline more exciting and add urgency", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_native" }, "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_native" }, "assets": { "headline": { "content": "20% Off All Drinks This Week!" } } } } ``` ## よくあるパターン ### brand identity の活用 ブランドのコンテキストを提供して、より良い生成結果を得る: ```json theme={null} { "message": "Create a display ad for our coffee shop promotion", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "brand": { "domain": "mycoffeeshop.com" } } ``` **最小限のブランド参照**: ドメインだけから手軽に生成を始められます: ```json theme={null} { "message": "Create a coffee shop ad", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_native" }, "brand": { "domain": "mycoffeeshop.com" } } ``` 詳細な例は [brand.json reference](/docs/brand-protocol/brand-json) を参照。 ### 保有アセットの利用 既存のアセットを提供し、クリエイティブに組み込む: ```json theme={null} { "message": "Create a display ad featuring our signature latte", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "assets": { "brand_logo": { "url": "https://mycoffeeshop.com/assets/logo.png", "width": 200, "height": 50 } } } } ``` ### 動的コードの生成 クリエイティブエージェントは、ブリーフの要件に基づいて静的マニフェストを返すか動的コードを返すかを判断します。天気に応じた挙動、時間帯適応、位置情報ベースのコンテンツなどランタイムロジックが必要な場合、エージェントはマニフェストのアセットに実行可能なコードを返します。 動的な挙動を示す説明的なブリーフを使用します: ```json theme={null} { "message": "Create a weather-responsive coffee ad that shows hot drinks when cold, iced drinks when warm", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_native" } } ``` コードベースのクリエイティブはレスポンスのアセット構造で識別できる — マニフェストには静的コンテンツと並行して、または代わりにコードアセットが含まれます: ```json theme={null} { "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_native" }, "assets": { "code": { "content": "" }, "headline_warm": { "content": "Cool Down with Our Iced Collection" }, "headline_cold": { "content": "Warm Up with Our Signature Roasts" } } } } ``` ### クリエイティブをメディアバイに添付します クリエイティブを構築してプレビューしたら、それをメディアバイに添付します。`build_creative` からのマニフェストを `create_media_buy` のクリエイティブとして渡す(完全なリクエスト構造は [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) を参照): ```json theme={null} { "packages": [{ "product_id": "premium_display", "pricing_option_id": "cpm_standard", "budget": 10000, "creatives": [{ "creative_id": "coffee_promo_v3", "name": "Coffee shop 20% off - final", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_native" }, "assets": { "headline": { "content": "This Week Only: 20% Off Every Drink!" }, "description": { "content": "Visit our cozy coffee shop and enjoy premium coffee at an unbeatable price." }, "call_to_action": { "content": "Visit Today" } } }] }] } ``` [インラインクリエイティブ管理](/docs/creative/sales-agent-creative-capabilities)を持つセラーの場合、クリエイティブはメディアバイと一緒に伝達されます。他のワークフローでは、[`sync_creatives`](/docs/creative/task-reference/sync_creatives) を使ってメディアバイで参照する前にクリエイティブをセールスエージェントのライブラリにアップロードします。 ## フォーマットディスカバリー ### 標準フォーマット すぐに使える一般的なフォーマット ID: * `display_native` - ネイティブ広告フォーマット * `display_300x250` - ミディアムレクタングルバナー * `video_standard_30s` - 30 秒動画広告 ### パブリッシャー固有のフォーマット パブリッシャーのカスタムフォーマットを使う場合は、ソースを指定します: ```json theme={null} { "message": "Create a premium video ad", "target_format_id": { "agent_url": "https://premium-publisher.com", "id": "premium_video_15s" } } ``` ## セラー側での生成(メディアバイ内のブリーフ) 上記の例では `build_creative` を使ってインタラクティブにクリエイティブを生成しています。セラーがサービス時にクリエイティブを生成する場合 — コンテキスト広告、ページマッチドディスプレイ、AI 生成ネイティブ — は異なるパターンが適用されます。 このフローでは、セールスエージェントが Media Buy Protocol と Creative Protocol の両方を実装します。バイヤーは `create_media_buy` の一部としてブリーフを提供し、セラーはサービス時にクリエイティブを生成します。`build_creative` の呼び出しは不要です。 ### 仕組み 1. **ジェネレーティブフォーマットの探索** — セールスエージェントで `list_creative_formats` を呼び出す。`format_id.agent_url` がセールスエージェント自身を指しているフォーマットを探す。 2. **メディアバイにブリーフを含める** — ブリーフをアセットとして含むクリエイティブを送信します: ```json theme={null} { "packages": [{ "product_id": "premium_display", "pricing_option_id": "cpm_standard", "budget": 50000, "creatives": [{ "creative_id": "brand_contextual_brief", "name": "Contextual campaign brief", "format_id": { "agent_url": "https://ads.seller-example.com", "id": "contextual_display_generative" }, "assets": { "brief": { "content": "Highlight our sustainability story. Match tone to editorial context. Avoid competitor comparisons." } } }] }] } ``` 3. **ローンチ前にプレビュー** — セールスエージェントで `preview_creative` をブリーフマニフェストと `context_description` インプットとともに呼び出し、エージェントが生成するものの代表的なサンプルを確認します。高速な反復には `quality: "draft"` を、ステークホルダーレビューには `quality: "production"` を使います。これらは例示的なもの — 実際の出力はサービス時のライブシグナルに依存します。 ```json theme={null} { "request_type": "single", "quality": "draft", "creative_manifest": { "format_id": { "agent_url": "https://ads.seller-example.com", "id": "contextual_display_generative" }, "assets": { "brief": { "content": "Highlight our sustainability story. Match tone to editorial context. Avoid competitor comparisons." } } }, "inputs": [ { "name": "Tech article", "context_description": "Article about semiconductor manufacturing" }, { "name": "Travel blog", "context_description": "Blog post about eco-friendly travel" } ] } ``` プレビューはメディアバイがアクティブである必要はない — `create_media_buy` を呼び出す前にブリーフマニフェストだけでプレビューできます。詳細なメンタルモデルは[ジェネレーティブクリエイティブのプレビュー](/docs/creative/task-reference/preview_creative#previewing-generative-creative)を参照。 4. **セラーがサービス時に生成** — セラーはブリーフとバイヤーの [brand identity](/docs/brand-protocol/brand-json) を使用して、インプレッションごとまたはページコンテキストごとにページに合わせたクリエイティブを生成します。ブリーフと brand identity がガードレールとして機能し、エージェントはその制約内で生成します。継続的な生成にバイヤーのアクションは不要です。 5. **生成されたバリアントのレビュー** — セールスエージェントで `get_creative_delivery` を呼び出し(Creative Protocol を実装しています)、バリアントマニフェストと生成コンテキストを含む生成結果を確認します: ```json theme={null} { "media_buy_ids": ["mb_12345"], "max_variants": 20 } ``` レスポンスには、配信されたものを正確に示すバリアントレベルのマニフェストと、生成コンテキスト(ページトピック、デバイスクラスなど)が含まれます。 **バリアントの保持**: エージェントはバリアントデータを無期限に保持することを要求されない。`get_creative_delivery` を呼び出す際は、`max_variants` でクリエイティブごとに返されるバリアント数を制御します。エージェントは独自の基準に基づいて返すバリアントを選択する — 通常はインプレッション量(最も配信されたものが優先)だが、一部は最新順や代表的なサンプリングを使う場合もあります。大量のジェネレーティブキャンペーンでは、生成されたすべてのバリアントのサブセットのみが保持されることを想定します。キャンペーン後の一括取得に頼らず、キャンペーン中に早めに頻繁に `max_variants` をリクエストすること。 ### `build_creative` との主な違い | 側面 | `build_creative` | メディアバイ内のブリーフ | | ---------- | ------------------------------------------- | -------------------------------------- | | 生成のタイミング | オンデマンド、キャンペーン開始前 | サービス時、キャンペーン全期間 | | 誰が生成するか | スタンドアロンのクリエイティブエージェントまたはセールスエージェント | セールスエージェント(統合型) | | ローンチ前プレビュー | 即時(マニフェストがクリエイティブそのもの) | 代表的なサンプル(ブリーフ + シミュレートされたコンテキスト) | | バイヤーの関与 | インタラクティブ — レビュー、反復、承認 | サンプルをプレビューしてセットアンドフォーゲット — ブリーフが継続的な制約 | | バリアントの可視性 | 即時(レスポンスで返されます) | 配信後(`get_creative_delivery` 経由) | | フォーマットの権威 | `format_id.agent_url` はフォーマットを所有するエージェントを指す | `format_id.agent_url` はセールスエージェント自身 | 詳細は [Creative capabilities on sales agents](/docs/creative/sales-agent-creative-capabilities) を参照。 ### サービス時生成のガードレール セラーがサービス時にクリエイティブを生成する場合、バイヤーの制御手段は以下の通り: * **ブリーフ** — トーン、トピック、除外事項(「競合他社との比較は避ける」)に関する明示的な指示 * **Brand identity** — 色、ロゴ、ボイスガイドライン、[brand.json](/docs/brand-protocol/brand-json) 経由の承認済みアセット * **プリフライトプレビュー** — ローンチ前に様々なコンテキストでエージェントが生成するものをサンプル確認 * **ポストフライト監査** — `get_creative_delivery` 経由でバリアントマニフェストと生成コンテキストをレビュー、`preview_creative` バリアントモードで特定バリアントを再生 ポストフライトレビューでブランドに反するバリアントが発見された場合、より具体的な制約でブリーフを更新して `create_media_buy` 経由で再送信します。規制対象カテゴリ(金融サービス、製薬)の場合は、ブリーフにコンプライアンス要件を含める — クリエイティブ機能で `supports_compliance: true` を宣言するエージェントは、生成中に開示事項と必須要素を検証します。 ## 会話型・インタラクティブフォーマット 一部のジェネレーティブフォーマットはステートフル — AI チャット、インタラクティブエクスペリエンス、会話型ネイティブ広告。これらはメディアバイ内のブリーフパターンと同じに従うが、理解しておく価値のある独自の特性があります。 ### フォーマット探索 `list_creative_formats` での会話型フォーマットの例: ```json theme={null} { "format_id": { "agent_url": "https://ads.seller-example.com", "id": "conversational_native" }, "name": "Conversational native ad", "type": "native", "description": "AI-powered conversational ad that responds to user messages within the content feed. Adapts tone and recommendations based on conversation context." } ``` ### ブリーフ構造 会話型フォーマットのブリーフにはペルソナ、トピックの境界、ガードレールが含まれます: ```json theme={null} { "packages": [{ "product_id": "premium_native", "pricing_option_id": "cpm_engaged", "budget": 25000, "creatives": [{ "creative_id": "brand_chat_brief", "name": "Product advisor chat", "format_id": { "agent_url": "https://ads.seller-example.com", "id": "conversational_native" }, "assets": { "brief": { "content": "Act as a helpful product advisor for our outdoor gear line. Recommend products based on the user's activity interests. Keep responses concise (2-3 sentences). Never discuss competitor products. Always include a product link when recommending." } } }] }] } ``` ### 会話のプレビュー 会話型フォーマットのプリフライトプレビューは代表的な最初のインタラクションを生成します。異なるエントリポイントをシミュレートするには `context_description` を使います: ```json theme={null} { "request_type": "batch", "quality": "production", "requests": [ { "creative_manifest": { "format_id": { "agent_url": "https://ads.seller-example.com", "id": "conversational_native" }, "assets": { "brief": { "content": "Act as a helpful product advisor for our outdoor gear line. Recommend products based on the user's activity interests." } } }, "inputs": [ { "name": "Hiking enthusiast", "context_description": "User reading an article about day hikes near Portland" }, { "name": "Runner", "context_description": "User browsing a running shoe review page" } ] } ] } ``` プレビューレスポンスに `interactive_url` が含まれる場合、レビュアーはサンドボックスでエクスペリエンスと直接インタラクトできる — 異なる会話パスのテスト、ガードレールの確認、トーンの確認が可能です。 **ガードレールのテスト**: ブリーフの制約をエージェントが尊重するかを確認するために、プレビューインプットに adversarial なコンテキストを含める: ```json theme={null} { "inputs": [ { "name": "Competitor question", "context_description": "User asks which brand makes better hiking boots than yours" }, { "name": "Off-topic request", "context_description": "User asks for medical advice about a knee injury" }, { "name": "Price haggling", "context_description": "User asks for a discount code or tries to negotiate pricing" } ] } ``` これらのプレビューを注意深くレビューする — ハッピーパスのプレビューでは表面化しないエッジケースをエージェントがどのように処理するかがわかる。 会話型フォーマットの場合、プレビューレスポンスにはレビュアーが会話をライブでテストできる `interactive_url` が含まれることがある: ```json theme={null} { "response_type": "single", "previews": [ { "preview_id": "prev_hiker", "renders": [ { "render_id": "render_1", "output_format": "url", "preview_url": "https://ads.seller-example.com/preview/conv_hiker_001", "role": "primary" } ], "input": { "name": "Hiking enthusiast", "context_description": "User reading an article about day hikes near Portland" } } ], "interactive_url": "https://ads.seller-example.com/sandbox/conv_hiker_001", "expires_at": "2026-04-01T00:00:00Z" } ``` `interactive_url` はレビュアーがエージェントと実際の会話ができるサンドボックスを提供し、異なるパスのテスト、ガードレールの確認、インタラクティブなトーン確認が可能です。静的プレビューは代表的な最初のインタラクションを示し、サンドボックスはエージェントが実際にどのように動作するかを示します。 ### 会話のバリアントマニフェスト キャンペーン実行後、`get_creative_delivery` はエージェントが生成したものを捉えたバリアントマニフェストを返します。会話型フォーマットでは、各バリアントは会話セッションを表します: ```json theme={null} { "variant_id": "conv_hiker_session_042", "generation_context": { "context_type": "conversational", "topic": "outdoor recreation, hiking gear", "device_class": "mobile", "ext": { "turn_count": 4, "engagement_duration_seconds": 45 } }, "manifest": { "format_id": { "agent_url": "https://ads.seller-example.com", "id": "conversational_native" }, "assets": { "greeting": { "asset_type": "text", "content": "Planning a hike? I can help you find the right gear for the trail." }, "transcript": { "asset_type": "text", "content": "Agent: Planning a hike? I can help you find the right gear for the trail.\nUser: Looking for a lightweight daypack\nAgent: For day hikes near Portland, I'd recommend our TrailLite 22L — it's 380g with a built-in rain cover. [link]\nUser: What about trekking poles?\nAgent: The CompactTrek poles fold to 36cm and weigh just 240g per pair. Great for the elevation changes on trails like Dog Mountain. [link]" }, "products_shown": { "asset_type": "text", "content": "TrailLite 22L Daypack, CompactTrek Folding Poles" } } }, "impressions": 1, "clicks": 2 } ``` バリアントマニフェストの詳細レベルはエージェントによって異なります。完全なトランスクリプトを提供するもの(上記のように)もあれば、匿名化されたユーザーシグナルとともに要約された交換を提供するものもあります。重要なのは、バイヤーがブランドの AI がユーザーに何を言ったかを監査できることです。 ### セッション管理とデータ処理 会話型フォーマットには静的広告にはない考慮事項がある: * **ターン制限**: ブリーフにターン数または時間の制限を含める(「5 回のやり取りの後、丁寧に会話を終了する」)。エージェントが独自の制限を設けることもある — `list_creative_formats` のフォーマット説明を確認します。 * **エスカレーション**: 答えられない場合にエージェントがすべきことを定義する(「ユーザーが返品について聞いた場合は、ヘルプセンターへのリンクを提供する」)。スコープ外の質問に対する明示的なガイダンスがない場合、エージェントの挙動は未定義です。 * **トランスクリプト内のユーザーデータ**: `get_creative_delivery` からのバリアントマニフェストにはユーザーメッセージが含まれる場合があります。規制対象業種の場合は、ローンチ前にエージェントのデータ処理慣行(匿名化、保持期限)を確認します。 * **コストへの影響**: エンゲージメントごとまたはターンごとの料金体系の会話型広告は、可変コストが発生することがあります。`get_products` でプロダクトの料金モデルを確認して、会話の深さが支出にどう影響するかを把握します。 これらの制約はブリーフの自然言語で表現されます。プロトコルはランタイムでこれらを強制しない — [`get_creative_delivery`](/docs/creative/task-reference/get_creative_delivery) 経由のポストフライトバリアントレビューでコンプライアンスを確認します。 詳細なメンタルモデルは [ジェネレーティブクリエイティブのプレビュー](/docs/creative/task-reference/preview_creative#conversational-and-interactive-formats) を参照。 ## 次のステップ * **マルチフォーマット生成**: `target_format_ids` を使って 1 回の呼び出しで複数サイズのクリエイティブを生成 — [マルチフォーマット生成](/docs/creative/task-reference/build_creative#multi-format-generation)を参照 * **サンプルを見る**: `build_creative` の例は [タスクリファレンス](/docs/creative/task-reference/build_creative) を参照 * **セールスエージェントのクリエイティブフロー**: セラー側生成は [Creative capabilities on sales agents](/docs/creative/sales-agent-creative-capabilities) を参照 * **デリバリーレポーティング**: バリアントレベルの分析は [get\_creative\_delivery](/docs/creative/task-reference/get_creative_delivery) を参照 ## よくある問題 ### フォーマットが見つからない フォーマットエラーが出る場合、そのフォーマットをパブリッシャーがサポートしていない可能性があります。次を試すとよい: 1. まず標準の AdCP フォーマット(`display_native`, `video_standard_30s`)を使用します 2. パブリッシャーの `list_creative_formats` エンドポイントを確認します 3. `format_id.agent_url` の URL が正しいか検証します ### クオリティモデルを理解します クリエイティブクオリティには独立した 2 つの軸がある: * **ビルドクオリティ**(`build_creative` の `quality`): 生成フィデリティを制御 — クリエイティブ生成にどれだけのコンピュートを費やすか。`draft` は低解像度画像やシンプルなレイアウトを使った粗いコンセプトを高速に生成します。`production` はポリッシュされた最終品質の出力を生成します。 * **プレビュークオリティ**(`preview_creative` の `quality`、`build_creative` の `preview_quality`): レンダーフィデリティを制御 — クリエイティブのレビューのための可視化方法。`draft` は高速な低フィデリティレンダリングで迅速な反復向け。`production` はステークホルダーレビュー向けの完全品質レンダリング。 これらの軸は独立しています。よくある組み合わせ: | ビルド | プレビュー | ユースケース | | ---------- | ---------- | ----------------------------------------------- | | draft | draft | 高速な探索 — 素早いコンセプト、素早いプレビュー | | draft | production | ドラフトコンセプトのステークホルダーレビュー — ドラフトクリエイティブの完全品質レンダリング | | production | production | 最終レビュー — ポリッシュされたクリエイティブ、ポリッシュされたプレビュー | | production | draft | サムネイルグリッド — 選択のためのクイックサムネイルとして表示される最終クリエイティブ | 片方のクオリティレベルのみをサポートするエージェントは、サポートしないパラメーターを無視します。エコーバックフィールドはない — 検査によってクオリティを確認します。 ### クリエイティブ品質の問題 クリエイティブ出力を改善するには: 1. **ビルドクオリティ**と**プレビュークオリティ**は独立した軸 — 上記の[クオリティモデルを理解する](#クオリティモデルを理解する)を参照。反復には `draft`、最終レビューには `production` を使います。 2. メッセージをより具体的にする: 「アーストーンでミニマルなコーヒー広告を作成して」 3. アセットやガイドラインを含む充実した brand identity を提供します 4. [refinement](/docs/creative/task-reference/build_creative#iterative-refinement) を使って反復する — マニフェストを新しいメッセージとともに渡し返すか、brief アセットを更新します ### アセット管理 アセットは [brand identity](/docs/brand-protocol/brand-json) 経由で提供します: 1. ブランドアイデンティティに説明的なタグ付きでアセットを含めます 2. リクエストで `asset_filters` を使い特定アセットを選択します 3. 大規模な在庫では商品カタログを参照します 最初のクリエイティブを作る準備はできたか?上記の基本例から始めて試してみよう。 # クリエイティブエージェントの実装 Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/implementing-creative-agents AdCP クリエイティブエージェントの構築方法。フォーマット定義、マニフェスト検証、プレビュー生成、クリエイティブライブラリのホスティングを解説します。 > **3.1 の正準フォーマット**: 正準フォーマットのパスでは、クリエイティブエージェントは `get_adcp_capabilities` の `creative.supported_formats` を介して何を生成できるかを宣言します(クリエイティブエージェント向けの v1 の `list_creative_formats` のオーバーロードを置き換えます)。各エントリは、プロダクトのインライン `format_options[i]` と同じ `ProductFormatDeclaration` の形状を使います。[canonical-formats](/docs/creative/canonical-formats) を参照。 本ガイドは、クリエイティブフォーマットを定義・管理するクリエイティブエージェントの実装方法を解説します。 ## クリエイティブエージェントとは クリエイティブエージェントは次を行うサービスです。 * **フォーマットを定義** - 必要なアセットとその構造を指定 * **マニフェストを検証** - クリエイティブマニフェストがフォーマット要件を満たすか確認 * **プレビューを生成** - クリエイティブのレンダリング結果を表示 * **クリエイティブを構築**(任意) - 自然言語ブリーフからマニフェストを生成、既存マニフェストを別フォーマットへ変換、またはライブラリから配信可能なマニフェストとして取得 * **クリエイティブライブラリをホスト**(任意) - バイヤーが既存クリエイティブをブラウズ・フィルタリングできるようにします 広告サーバー(CM360、Flashtalking)、クリエイティブ管理プラットフォーム、クリエイティブエージェンシー、パブリッシャー、セールスエージェントがクリエイティブエージェントを実装できます。Media Buy Protocol と Creative Protocol を両方実装するセールスエージェントは、単一エンドポイントから双方の役割を担います — [セールスエージェントのクリエイティブ機能](/docs/creative/sales-agent-creative-capabilities) を参照してください。 ## 三つのインタラクションモデル クリエイティブエージェントは、アセットがどのように届くか、出力が何かに基づいて三つの異なるカテゴリに分かれます。自分のエージェントがどのモデルに従うかを特定することが、どのタスクを実装するか、バイヤーがどうやり取りするかを決めます。 ### ステートレス: テンプレートと変換エージェント **例:** Celtra、フォーマット変換サービス、リッチメディアテンプレートプラットフォーム バイヤーは呼び出しごとにすべてのアセットをインラインで渡します。あなたのエージェントはテンプレートまたは変換を適用し、結果を返します。永続的なクリエイティブライブラリはありません——すべての呼び出しは自己完結しています。 | タスク | 役割 | | ----------------------- | ----------------------- | | `list_creative_formats` | 利用可能なテンプレートとそのアセット要件を発見 | | `preview_creative` | 提供されたアセットでテンプレートをレンダリング | | `build_creative` | 入力アセットを配信タグに変換 | **ケイパビリティ:** `supports_transformation: true` バイヤーのワークフロー: あなたのフォーマットを発見 → 実際のアセットでプレビュー → トラフィッキングのためにビルドされたクリエイティブを要求。 ### ステートフル(事前ロード): アドサーバー **例:** Innovid、Flashtalking、CM360 クリエイティブは、プラットフォームの UI または API を通じてロードされ、すでにシステム内に存在します。バイヤーは接続してライブラリを閲覧し、メディアバイの配信タグを要求します。バイヤーはアセットをあなたにプッシュしません——すでにそこにあるクリエイティブを参照します。 | タスク | 役割 | | ----------------------- | --------------------------------- | | `list_creatives` | ライブラリ内の既存のクリエイティブを閲覧 | | `build_creative` | 特定の `creative_id` とメディアバイの配信タグを生成 | | `preview_creative` | 既存のクリエイティブをプレビュー | | `get_creative_delivery` | バリアントレベルの配信メトリクスをレポート | **ケイパビリティ:** `has_creative_library: true` バイヤーのワークフロー: あなたのクリエイティブライブラリを閲覧 → メディアバイごとにタグを要求 → 配信を追跡。 ### ステートフル(プッシュ): クリエイティブを持つセールスエージェント **例:** パブリッシャープラットフォーム、リテールメディアネットワーク、ネイティブ広告プラットフォーム バイヤーはクリエイティブアセットまたはカタログアイテムをあなたのプラットフォームにプッシュします。あなたはそれらを検証、保存し、自分の環境でレンダリングします。カタログ駆動のクリエイティブが面白くなるのはここです——バイヤーはプロダクトフィード、フライトのリスティング、ホテルのインベントリをプッシュし、あなたがそれらをネイティブ広告としてレンダリングするかもしれません。 | タスク | 役割 | | ----------------------- | ----------------------------------- | | `list_creative_formats` | あなたが受け入れるフォーマットを発見 | | `sync_creatives` | プッシュされたアセットまたはカタログアイテムを受け入れ | | `preview_creative` | あなたのプラットフォーム環境でプッシュされたクリエイティブをプレビュー | | `get_creative_delivery` | 配信メトリクスをレポート | **ケイパビリティ:** `has_creative_library: true` バイヤーのワークフロー: あなたの受け入れるフォーマットを発見 → アセットをプッシュ → あなたの環境でプレビュー。 ### モデルの選び方 鍵となる問いは: **アセットはどこから来るか?** * バイヤーが呼び出しごとにアセットを送る → **ステートレス(テンプレート/変換)** * クリエイティブがすでにシステム内に存在する → **ステートフル(アドサーバー)** * バイヤーがホスティングのためにアセットをあなたにプッシュする → **ステートフル(セールスエージェント)** 一部のエージェントはモデルを組み合わせます。クリエイティブ管理プラットフォームは、テンプレートエンジン(ステートレス変換)とライブラリホスト(ステートフル)の両方かもしれません。バイヤーが正しいインタラクションモデルを判断できるよう、`get_adcp_capabilities` で適切なケイパビリティフラグを宣言してください。 ### 価格とステートフルネス サービスに課金するクリエイティブエージェントは、アカウント関係を必要とします。価格の追加には次が必要です: 1. **[Accounts プロトコル](/docs/accounts/overview)を実装する** — バイヤーがレートカードでアカウントを確立 2. **発見で `pricing_options` を公開する** — `list_creative_formats`(変換/生成エージェント)または `list_creatives`(アドサーバー/ライブラリエージェント)で 3. **`build_creative` のレスポンスで価格を返す** — `pricing_option_id`、`vendor_cost`、`currency`、`consumption` 4. **`report_usage` を受け入れる** — オーケストレーターが配信された内容をレポートし、収益を追跡できるようにする 無料の変換エージェントはステートレスのまま、変更されません。アカウントも価格も不要です。 #### エージェントタイプ別の価格発見 | エージェントタイプ | 発見面 | 理由 | | -------------------------------- | ----------------------- | ----------------------------------------------------------- | | **変換**(Celtra) | `list_creative_formats` | クリエイティブが存在する前に、バイヤーがフォーマットごとの価格を見る | | **生成**(AI プラットフォーム) | `list_creative_formats` | 同じ——価格は既存のクリエイティブではなくケイパビリティにある | | **アドサーバー**(Innovid、CM360) | `list_creatives` | バイヤーがライブラリ内の特定のクリエイティブの価格を見る | | **両方**(Celtra、クリエイティブ管理プラットフォーム) | 両方の面 | `list_creatives` にライブラリ価格、`list_creative_formats` にフォーマット価格 | 変換および生成エージェントは、価格のために `list_creatives` を実装する必要はありません——フォーマットがプロダクトなので、`list_creative_formats` が自然な面です。 #### 価格のウォークスルー フォーマット適応ごとに課金する変換エージェントの完全なラウンドトリップを示します。 **ステップ 1: バイヤーが `list_creative_formats` を介して価格を発見する** バイヤーは `include_pricing: true` と自身の `account` を指定して `list_creative_formats` を呼びます。あなたのエージェントはアカウントのレートカードを検索し、各フォーマットに `pricing_options` を返します: ```json theme={null} { "formats": [ { "format_id": { "agent_url": "https://creative.example.com", "id": "display_300x250" }, "name": "Display 300x250", "pricing_options": [ { "pricing_option_id": "po_standard_per_format", "model": "per_unit", "unit": "format", "unit_price": 2.00, "currency": "USD" }, { "pricing_option_id": "po_volume_per_format", "model": "per_unit", "unit": "format", "unit_price": 1.25, "currency": "USD" } ] } ] } ``` 複数のオプションは一般的です——ここではバイヤーが標準レートとボリュームレートを見ます。アドサーバーでは、同じ `pricing_options` のパターンが代わりに `list_creatives` に現れます。 **ステップ 2: バイヤーがクリエイティブをビルドする** バイヤーは自身の `account` を指定して `build_creative` を呼びます。あなたのエージェントは作業を実行し、(アカウントのコミットメントレベル、実行された作業などに基づいて)適用可能な価格オプションをサーバー側で選択し、コストを返します: ```json theme={null} { "creative_manifest": { "creative_id": "cr_hero_banner", "format_id": { "agent_url": "https://creative.example.com", "id": "display_728x90" }, "assets": { "..." : "..." } }, "pricing_option_id": "po_standard_per_format", "vendor_cost": 2.00, "currency": "USD", "consumption": { "renders": 1 } } ``` `pricing_option_id` は、どのレートが適用されたかをバイヤーに伝えます。`consumption` オブジェクトはバイヤーが検証できるようにします: 1 レンダー × $2.00/フォーマット = $2.00 の `vendor_cost`。 **CPM 価格のクリエイティブ**(アドサーバー)では、`vendor_cost` はビルド時に 0 です——インプレッションが配信されたときにコストが発生します: ```json theme={null} { "creative_manifest": { "..." : "..." }, "pricing_option_id": "po_video_cpm", "vendor_cost": 0, "currency": "USD" } ``` **ステップ 3: バイヤーが使用量をレポートする** キャンペーンが配信された後、バイヤーは [`report_usage`](/docs/accounts/tasks/report_usage) を介して使用量をレポートします。この例は、コストがビルド時ではなく配信中に発生した CPM レポートを示します。`pricing_option_id` と `creative_id` が照合のために流れます: ```json theme={null} { "reporting_period": { "start": "2026-03-01T00:00:00Z", "end": "2026-03-31T23:59:59Z" }, "usage": [ { "account": { "account_id": "acct_acme_creative" }, "creative_id": "cr_hero_banner", "pricing_option_id": "po_video_cpm", "impressions": 2400000, "vendor_cost": 1200.00, "currency": "USD" } ] } ``` あなたのエージェントは、`pricing_option_id` がアカウントのレートカードと一致することを検証し、レコードを受け入れます。 #### 誰が価格オプションを選ぶのか? **ベンダーエージェント**が価格オプションを選びます。バイヤーではありません。バイヤーは `build_creative` に `account` を渡します——エージェントは、アカウントのレートカード、実行された作業、コミットメントティアに基づいてどの価格オプションが適用されるかを決定します。レスポンスが、何が適用されたかをバイヤーに伝えます。 バイヤーは `build_creative` リクエストに `pricing_option_id` を渡しません。バイヤーは `list_creatives` でオプションを見て、`build_creative` のレスポンスで適用されたオプションを受け取ります。 #### エージェントタイプ別の消費フィールド | エージェントタイプ | 典型的な `consumption` フィールド | 注記 | | ----------- | --------------------------- | ---------------------- | | 変換 | `renders` | 実行されたフォーマット適応の数 | | AI 生成 | `tokens`、`images_generated` | 消費された LLM トークン、生成された画像 | | アドサーバー(CPM) | 省略 | コストはビルド時ではなく配信時に発生 | | マルチバリアント | `renders` | レンダリングされたバリアントの数 | `consumption` オブジェクトは情報提供です——バイヤーが `vendor_cost` がレートカードと整合していることを検証できるようにします。`vendor_cost` が請求の信頼できる情報源です。 ## 主要要件 ### 1. フォーマット ID の名前空間化 定義するすべてのフォーマットで、競合を避けるために agent URL 付きの構造化フォーマット ID を使用します。 ```json theme={null} { "format_id": { "agent_url": "https://youragency.com", "id": "video_story_15s" } } ``` **ルール:** * `agent_url` はエージェントの URL と一致させる * `id` は名前空間内で説明的かつ一意にします * 一貫性のため小文字とアンダースコアを使用します ### 2. フォーマットの検証 フォーマットがあなたの agent\_url を参照する場合、あなたが次の権威となります。 * フォーマット仕様 * アセットの検証ルール * 技術要件 * プレビュー生成 **フォーマット定義例:** ```json theme={null} { "format_id": { "agent_url": "https://youragency.com", "id": "story_sequence_5frame" }, "name": "5-Frame Story Sequence", "type": "display", "min_frames": 5, "max_frames": 5, "frame_schema": { "assets": [ { "asset_type": "image", "asset_role": "frame_image", "required": true, "requirements": { "width": 1080, "height": 1920, "file_types": ["jpg", "png", "webp"], "max_file_size_kb": 500 } }, { "asset_type": "text", "asset_role": "caption", "required": false, "requirements": { "max_length": 100 } } ] }, "global_assets": [ { "asset_type": "image", "asset_role": "brand_logo", "required": true, "requirements": { "width": 200, "height": 200, "transparency_required": true } } ] } ``` ## 必須タスク クリエイティブエージェントは次の 2 つのタスクを実装しなければなりません。 ### list\_creative\_formats エージェントが定義するすべてのフォーマットを返します。バイヤーはこれによってサポートするクリエイティブフォーマットを発見します。 **主な責務:** * 必須・任意を含むすべての `assets` を備えた完全なフォーマット定義を返す * 各フォーマットに自分の `agent_url` を含めます * `format_id` 値に適切な名前空間を使用します 完全な API 仕様は [list\_creative\_formats task reference](/docs/creative/task-reference/list_creative_formats) を参照してください。 ### preview\_creative マニフェストがあなたのフォーマットでどのように描画されるかを示すビジュアルプレビューを生成します。 **主な責務:** * マニフェストをフォーマット要件に照らして検証します * マニフェストが無効な場合は検証エラーを返す * ビジュアル表現(URL、画像、または HTML)を生成します * プレビューは少なくとも 24 時間アクセス可能にします 完全な API 仕様は [preview\_creative task reference](/docs/creative/task-reference/preview_creative) を参照してください。 ## 任意タスク ### build\_creative 自然言語ブリーフからクリエイティブマニフェストを生成、既存マニフェストを別フォーマットへ変換、またはライブラリクリエイティブを配信可能なマニフェストとして取得します。 **主な責務:** * 自然言語ブリーフを解析するか、ライブラリの `creative_id` を解決します * 適切なアセットを生成または調達します * ターゲットフォーマットに有効なマニフェストを返す * 提供された場合は `macro_values` をサービングタグに代入します * 任意でプレビュー URL を返す 完全な API 仕様は [build\_creative task reference](/docs/creative/task-reference/build_creative) を参照してください。 ### list\_creatives ライブラリ内のクリエイティブをブラウズ・フィルタリングします。クリエイティブライブラリをホストしており、バイヤーがクエリする必要がある場合に実装します。 **主な責務:** * 認証済みアカウントからアクセス可能なクリエイティブを返す * フォーマット、ステータス、タグ、日付範囲によるフィルタリングをサポートします * 大規模ライブラリのページネーションをサポートします * 任意でクリエイティブごとの動的クリエイティブ最適化(DCO)変数定義を含めます 完全な API 仕様は [list\_creatives task reference](/docs/creative/task-reference/list_creatives) を参照してください。 ### sync\_creatives クリエイティブアセットのアップロードをライブラリに受け入れます。プラットフォームがバイヤーによるアセットのプッシュを許可する場合に実装します。 **主な責務:** * フォーマット仕様に対してクリエイティブを検証します * プラットフォームが割り当てた ID を含むクリエイティブごとの結果を返す * アップサートセマンティクスをサポートする(`creative_id` で作成または更新) * 任意で一括パッケージアサインメントをサポートする(メディアバイも管理するエージェント向け) * 任意でブランドセーフティレビューのための非同期承認ワークフローをサポートします 完全な API 仕様は [sync\_creatives task reference](/docs/creative/task-reference/sync_creatives) を参照してください。 ## 検証のベストプラクティス ### マニフェストの検証 マニフェストを検証する際は次を行います。 1. **format\_id を確認** - あなたのエージェントを参照しているか 2. **必須アセットを検証** - 必須アセットがすべて存在するか 3. **アセットタイプを確認** - 指定されたタイプと一致するか 4. **要件を検証** - 寸法、ファイルタイプ、サイズなど 5. **URL の到達性** - アセット URL にアクセスできるか(任意だが推奨) **検証エラーの例:** ```json theme={null} { "status": "error", "error": "validation_failed", "validation_errors": [ { "asset_id": "frame_1_image", "error": "missing_required_asset", "message": "Required asset 'frame_1_image' is missing" }, { "asset_id": "brand_logo", "error": "invalid_dimensions", "message": "Logo must be 200x200px, got 150x150px" } ] } ``` ### ディスクロージャー要件 クリエイティブブリーフに `compliance.required_disclosures` が含まれる場合、クリエイティブエージェントは各ディスクロージャーが生成されたクリエイティブに確実に表示されるようにしなければなりません。ワークフローは次の通り: 1. **フォーマットのサポートを確認** — 各 `required_disclosures[].position` をフォーマットの `supported_disclosure_positions` または `disclosure_capabilities` と照合します。必要なポジションがフォーマットでサポートされていない場合、暗黙に省略するのではなくバリデーションエラーを返します。`disclosure_capabilities` が存在する場合はそれを使って永続性を考慮したマッチングを行い、フォーマットが必要なポジションと必要な永続性モードの両方をサポートするか確認します。 2. **永続性を尊重する** — ブリーフが必要なディスクロージャーに `persistence` を指定している場合、クリエイティブエージェントはフォーマットの `disclosure_capabilities` でその永続性モードをサポートするポジションを使用してこれを満たさなければなりません。たとえば EU AI Act のディスクロージャーに `"continuous"` 永続性が必要な場合、フォーマットはそのポジションを `disclosure_capabilities` で `"continuous"` を持つと宣言していなければなりません。ブリーフが `persistence` を省略した場合、フォーマットがそのポジションでサポートする最も厳格な永続性モードを使用します。 3. **ディスクロージャーを描画する** — サポートするポジションに対して: * `footer`, `overlay`, `end_card`, `prominent`: ディスクロージャーの `text` をクリエイティブ内の指定ポジションに描画します * `audio`, `pre_roll`: 音声として読み上げる。`min_duration_ms` が指定されていれば尊重します * `subtitle`: 動画クリエイティブ内にテキストトラックとして含めます * `companion`: プライマリクリエイティブと併せてコンパニオン広告ユニットで配信します 4. **管轄の範囲を尊重する** — `jurisdictions: ["US"]` が指定されたディスクロージャーは米国でのみ法的に必要です。ブリーフごとに単一のクリエイティブを生成するエージェントはすべての管轄のディスクロージャーを含めるべきです。管轄ごとのバリアントを生成できるエージェントは、`jurisdictions` フィールドでディスクロージャーをフィルタリングします。 5. **出所情報に伝播させる** — ブリーフが必要なディスクロージャーに `persistence` と `position` を指定している場合、これらをクリエイティブマニフェストの `provenance.disclosure.jurisdictions[].render_guidance` に伝播させる。ブリーフは生成時のドキュメントであり、配信時にパブリッシャーが持つのはブリーフではなくクリエイティブとその出所情報です。クリエイティブエージェントが永続性を出所情報に伝播させない場合、パブリッシャーは規制が要求する永続性を知る方法がない。 6. **再生成時も保持する** — クリエイティブを再生成またはリサイズする際は、マニフェストに添付された `BriefAsset` からすべてのディスクロージャーを引き継ぐ。`BriefAsset` とはフォーマットの `assets` 配列の `brief` タイプのアセットであり、フォーマット変換を経てもディスクロージャーが残存するようクリエイティブブリーフをマニフェスト内に保持します。 **例:** ブリーフが `overlay` ポジションに `persistence: "continuous"` を指定した `"KI-generiert"` ディスクロージャー(`eu_ai_act_article_50` 向け)を要求しています。フォーマットは `disclosure_capabilities: [{ "position": "overlay", "persistence": ["continuous", "initial"] }]` を宣言しています。フォーマットは continuous overlay をサポートしているため、クリエイティブエージェントはコンテンツ全体を通じて表示される持続的なオーバーレイとしてディスクロージャーを描画します。またエージェントは `provenance.disclosure.jurisdictions[]` の EU 管轄エントリに `render_guidance: { "persistence": "continuous", "positions": ["overlay"] }` を伝播させる。 ### フォーマットの進化 フォーマット定義を更新する際は次を考慮します。 * **追加的な変更**(`assets` への `required: false` な新しい任意アセット)は安全 * **破壊的変更**(アセット削除や要件変更)は新しい format\_id が必要 * `youragency.com:format_name_v2` のようなバージョニングを検討 * 可能な限り後方互換性を維持 ## デプロイチェックリスト クリエイティブエージェントを公開する前に確認してください。 * [ ] MCP および/または A2A エンドポイントにアクセスできます * [ ] すべての format\_id が適切に名前空間化されている (`domain:id`) * [ ] format\_id のドメインが `agent_url` のドメインと一致しています * [ ] `list_creative_formats` が全フォーマットを返す * [ ] `preview_creative` がマニフェストを検証しプレビューを生成します * [ ] フォーマット定義に完全なアセット要件が含まれています * [ ] カスタムフォーマットのドキュメントを用意しています ## インテグレーションパターン ### パターン 1: クリエイティブエージェンシー ブランド向けにカスタムフォーマットを構築するクリエイティブエージェンシーの場合: ```json theme={null} { "format_id": { "agent_url": "https://brandstudio.com", "id": "hero_video_package" }, "name": "Hero Video Package", "type": "video", "description": "Premium video creative with multiple aspect ratios", "assets": [ {"asset_role": "hero_video_16x9", "...": "..."}, {"asset_role": "hero_video_9x16", "...": "..."}, {"asset_role": "hero_video_1x1", "...": "..."} ] } ``` ### パターン 2: プラットフォーム固有フォーマット 専門フォーマットを定義するプラットフォームの場合: ```json theme={null} { "format_id": { "agent_url": "https://platform.com", "id": "interactive_quiz" }, "name": "Interactive Quiz Ad", "type": "rich_media", "description": "Engagement-driven quiz format", "assets": [ {"asset_role": "question_1", "asset_type": "text", "...": "..."}, {"asset_role": "answer_1a", "asset_type": "text", "...": "..."} ] } ``` ### パターン 3: フォーマット拡張サービス 標準フォーマットの拡張版を提供する場合: ```json theme={null} { "format_id": { "agent_url": "https://enhanced.com", "id": "video_30s_optimized" }, "name": "Optimized 30s Video", "type": "video", "extends": "creative.adcontextprotocol.org:video_30s", "description": "Standard 30s video with automatic optimization", "assets": [ ], "enhancements": { "auto_transcode": true, "quality_optimization": true, "format_variants": ["mp4", "webm", "hls"] } } ``` ### パターン 4: フィードネイティブ/ソーシャルフォーマットエージェント プラットフォームのフィード内にネイティブコンテンツとして描画する広告フォーマットをホストする場合: ```json theme={null} { "format_id": { "agent_url": "https://ads.socialplatform.com", "id": "promoted_post" }, "name": "Promoted post", "type": "native", "description": "Sponsored content that appears in the feed alongside organic posts. Renders with platform chrome (user avatar, engagement buttons, community badge).", "assets": [ { "item_type": "individual", "asset_id": "headline", "asset_type": "text", "required": true, "requirements": { "max_length": 300 } }, { "item_type": "individual", "asset_id": "body", "asset_type": "text", "required": false, "requirements": { "max_length": 1000 } }, { "item_type": "individual", "asset_id": "image", "asset_type": "image", "required": false, "requirements": { "max_width": 1200, "max_height": 628, "accepted_types": ["image/jpeg", "image/png"] } }, { "item_type": "individual", "asset_id": "click_url", "asset_type": "url", "required": true, "requirements": {} } ] } ``` プラットフォーム固有の描画(ダークモード、コミュニティコンテキスト、エンゲージメント UI)はエージェントがプレビューと配信時に処理します。フォーマット定義で指定するのはバイヤーが提供するアセットのみです。エージェントはこれらのアセットをプラットフォームのネイティブクロームでラップします。 バイヤーがフィードネイティブフォーマットに対して `preview_creative` を呼び出すと、プレビューはバイヤーのアセットをプラットフォームの UI(アバター、エンゲージメントボタン、コミュニティバッジなど)内に描画します。 ```json theme={null} { "request_type": "single", "creative_manifest": { "format_id": { "agent_url": "https://ads.socialplatform.com", "id": "promoted_post" }, "assets": { "headline": { "content": "Introducing our new trail running collection" }, "body": { "content": "Built for the mountains. Tested on every terrain." }, "image": { "url": "https://cdn.acme-example.com/trail-hero.jpg", "width": 1200, "height": 628 }, "click_url": { "url": "https://acme-example.com/trail-running" } } }, "inputs": [ { "name": "Running community", "context_description": "Appears in r/trailrunning feed between user posts" }, { "name": "General feed", "context_description": "Appears in home feed between mixed content" } ] } ``` プレビューレスポンスは各コンテキストでの広告の見え方を示します。バイヤーが他の場所ではプレビューできないコミュニティ固有のクロームも含まれます。これがプラットフォームがシンプルなフォーマットでも `preview_creative` を実装すべき理由です。プラットフォームのクロームが差別化要素となるからです。 ## プラットフォームのマッピング 既存の広告サーバーやクリエイティブ管理プラットフォームをラップする場合、このセクションでは一般的なプラットフォームの概念がクリエイティブプロトコルにどう対応するかを示します。 ### 概念のマッピング | プラットフォームの概念 | AdCP の対応 | 備考 | | ---------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | 広告主 / アカウント | アカウント([accounts protocol](/docs/accounts/overview) 経由) | バイヤーはライブラリへのクエリ前にアクセスを確立する | | クリエイティブコンセプト / グループ / テンプレートフォルダ | `list_creatives` の `concept_id` | サイズ/フォーマットをまたいだ関連クリエイティブをグループ化(Flashtalking のコンセプト、Celtra のキャンペーンフォルダ、CM360 のクリエイティブグループ) | | クリエイティブ | `list_creatives` レスポンスのクリエイティブアイテム | | | クリエイティブタイプ + サイズ | `format_id`(`agent_url`, `id`, 寸法を持つ構造化オブジェクト) | AdCP はタイプとサイズを単一のフォーマット参照に統合 | | テンプレート(Celtra の用語) | フォーマット(`list_creative_formats` 経由) | Celtra のテンプレートは構造と利用可能なプロパティを定義、AdCP のフォーマットは構造と利用可能なアセットを定義 | | テンプレートオブジェクトプロパティ(Celtra) | `variables` 配列 | 型付きの名前付きスロット(text, color, image, video, number, boolean)— ほぼ完全な対応 | | Active / archived / pending | `status` フィールド | | | 広告タグ / サービングタグ | クリエイティブマニフェスト内のアセット(`html`, `javascript`, または `vast` タイプ) | タグは単なるアセット — 特別な概念なし | | プレースメント / 広告ユニット | メディアバイ内のパッケージ | クリエイティブがアサインされる購入コンテキスト | | DCO 変数 / 動的フィールド | `variables` 配列(`include_variables=true` 経由) | 型とデフォルト値を持つ名前付きスロット | | データフィード / ターゲティングルール | モデル化なし | AdCP は変数のスロットをモデル化するが、最適化ルールはモデル化しない | | CTV/OTT 広告サーバー(Innovid、Brightcove) | 広告サーバーと同様、さらに VAST/SSAI 配信モデルを追加 | `vast` タイプのアセットに VAST タグ。コンパニオン広告はマルチレンダーフォーマット経由 | ### タグ生成モデル 広告サーバーによってサービングタグの生成方法は異なります。クリエイティブプロトコルの `build_creative` は `creative_id`、`target_format_id`、オプションの `media_buy_id`/`package_id` の組み合わせにより、一般的なモデルすべてに対応します。 **ユニバーサルタグ**(Celtra、Flashtalking)— 複数の環境(Web、アプリ内)に適応する単一タグ。プレースメントコンテキスト不要 — `build_creative(creative_id, target_format_id)` でトラフィック先を問わず動作するタグを生成。最終的なトラフィック先が予測しにくいエージェンシー/プログラマティック用途に最適で、トラフィッキングエラーを削減。 **シングルプレースメントタグ**(Celtra、Flashtalking、CM360)— 特定のサイズとプレースメントにスコープされたタグ。`target_format_id` で正確な寸法(例: 300x250 Web)を指定。トラフィック先が既知のパブリッシャーテンプレートのユースケースで最も一般的。 **マルチプレースメントタグ**(Celtra)— 1 つの環境で複数のサイズをカバーするタグ。`target_format_id` はサポートするサイズを `renders` 配列で定義するフォーマットを参照。複数のサイズ(例: 300x250 + 320x50 + 728x90 Web)が必要だが単一タグでトラフィックしたいパブリッシャーテンプレートに便利。 **プレースメントレベルタグ**(CM360)— プラットフォームがクリエイティブではなくプレースメントごとにタグを生成。呼び出し元は `media_buy_id` とオプションの `package_id` でトラフィッキングコンテキストを提供。CM360 アダプターはメディアバイコンテキストを使用してターゲットフォーマットにスコープされたタグを生成。 モデルの選択はキャンペーンコンテキストの判断であることが多く、プラットフォームの制約ではありません。同じクリエイティブエージェントが呼び出し元のニーズに応じて異なるタグタイプを生成する場合があります。 | ユースケース | タグタイプ | `build_creative` パラメータ | | ---------------------------- | ----------- | ------------------------------------------------------------------ | | エージェンシー/プログラマティック(トラフィック先不明) | ユニバーサル | `creative_id` + `target_format_id`(ユニバーサルフォーマット) | | パブリッシャーテンプレート(プレースメント既知) | シングルプレースメント | `creative_id` + `target_format_id`(特定サイズ) | | パブリッシャーテンプレート(複数サイズ) | マルチプレースメント | `creative_id` + `target_format_id`(マルチレンダーフォーマット) | | トラフィッキングコンテキストのある広告サーバー | プレースメントレベル | `creative_id` + `target_format_id` + `media_buy_id` + `package_id` | すべてのケースで出力は同じです: `html` または `javascript` アセットにサービングコードを持つクリエイティブマニフェスト。 ### 変数モデル プラットフォームによって動的コンテンツの表現方法は異なります。クリエイティブプロトコルの `variables` 配列は一般的なパターンに対応します。 **名前付き変数スロット**(Flashtalking)— 各クリエイティブは ID、名前、型を持つ明示的な変数を持ちます。`creative-variable.json` に直接マッピング。 **テンプレートオブジェクトプロパティ**(Celtra)— テンプレートは特定のコンポーネントとサイズバリアントにスコープされた型付き `properties`(text, color, image, video, percentage, hidden)を持つ `templateObjects` を定義します。Celtra アダプターはこれらを `variables` 配列にフラット化し、テンプレートオブジェクトラベルとプロパティラベルを使って `variable_id` と `name` を構築します。 **ルールベースのアセット選択**(CM360)— 動的クリエイティブはデータフィードから供給されるターゲティングルールを持つ `dynamicAssetSelection` を使用します。このモデルは変数ベースではなく — CM360 アダプターは通常 `variables` 配列を持たず、`has_variables` フィルタリングは適用されない。 ### マクロ処理 プラットフォームは内部で独自のマクロ構文を使用します。`build_creative` の `macro_values` パラメータにより、呼び出し元はユニバーサルマクロ値(例: `CLICK_URL`)を渡し、クリエイティブエージェントがプラットフォームのネイティブ構文に変換して出力タグに代入します。 | ユニバーサルマクロ | CM360 相当 | Flashtalking 相当 | | ------------- | -------- | --------------- | | `CLICK_URL` | `%c` | `[clickTag]` | | `CACHEBUSTER` | `%n` | `[timestamp]` | | `TIMESTAMP` | `%t` | `[timestamp]` | クリエイティブエージェントが変換を処理するため、呼び出し元は常にユニバーサルマクロを使用します。 ### 拒否後の再提出 `sync_creatives` またはクリエイティブレビューで拒否が発生した場合、修正と再提出のフローはアップサートセマンティクスを使用します。 1. `list_creatives`(ライブラリの `status: "rejected"`)または `get_media_buys`(パッケージの `approval_status: "rejected"` と `rejection_reason`)で拒否理由を確認します 2. クリエイティブを修正する(アセットの更新、コピーの調整、メディアの差し替え) 3. 同じ `creative_id` で `sync_creatives` を再提出 — エージェントは既存のクリエイティブを更新してレビューを再トリガーします 4. `status` が `pending_review` から `approved` または `rejected` に変わるまで `list_creatives` をポーリングします 再提出はレビュークロックをリセットします。エージェントは更新されたクリエイティブをレビュー目的の新しい提出として扱います。 ### 実装するタスクの選択 次の表は、各インタラクションモデルを実装すべきタスクにマップします。詳細な説明については上記の[三つのインタラクションモデル](#三つのインタラクションモデル)を参照してください。 | インタラクションモデル | 必須タスク | 追加タスク | ケイパビリティ | | -------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------ | ------------------------------- | | テンプレート/変換(Celtra、フォーマット変換) | `list_creative_formats`, `preview_creative`, `build_creative` | — | `supports_transformation: true` | | アドサーバー(Innovid、Flashtalking、CM360) | `list_creatives`, `build_creative`, `preview_creative` | `list_creative_formats`, `get_creative_delivery` | `has_creative_library: true` | | クリエイティブを持つセールスエージェント(パブリッシャー、リテールメディア) | `list_creative_formats`, `sync_creatives`, `preview_creative` | `get_creative_delivery` | `has_creative_library: true` | | 生成系クリエイティブツール | `list_creative_formats`, `preview_creative`, `build_creative` | — | `supports_generation: true` | バイヤーエージェントが試行錯誤なしに正しいインタラクションモデルを判断できるよう、`get_adcp_capabilities` でこれらの機能を宣言してください。仕様の[インタラクションモデル](/docs/creative/specification#interaction-models)を参照。 クリエイティブライブラリを持つプラットフォームは、バイヤーがクエリ前にアクセスを確立できるよう [accounts protocol](/docs/accounts/overview) も実装すべきです。これはセールスエージェントがメディアバイで使用するのと同じ accounts protocol です。 ## 関連ドキュメント * [Creative Formats](/docs/creative/formats) - フォーマット構造の理解 * [Creative Manifests](/docs/creative/creative-manifests) - マニフェストの仕組み * [Asset Types](/docs/creative/asset-types) - アセット仕様 * [list\_creative\_formats task](/docs/creative/task-reference/list_creative_formats) - フォーマット探索 API リファレンス * [list\_creatives task](/docs/creative/task-reference/list_creatives) - クリエイティブライブラリ API リファレンス * [build\_creative task](/docs/creative/task-reference/build_creative) - マニフェスト生成 API リファレンス * [preview\_creative task](/docs/creative/task-reference/preview_creative) - プレビュー描画 API リファレンス # クリエイティブプロトコル Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/index AdCP クリエイティブプロトコルは、AI エージェントと標準化されたフォーマットを使用し、CTV・ディスプレイ・ソーシャルにわたってブリーフから配信まで広告クリエイティブを管理します。 クリエイティブストラテジストがモニター上で複数のフォーマットの広告モックアップを確認している Maya は中規模エージェンシーのクリエイティブストラテジストです。Acme Outdoor のホリデーキャンペーンを CTV、ディスプレイ、ソーシャルの 3 チャネルで展開しようとしています。手元にあるのは 1 本のブリーフ。金曜日までに全チャネルで配信を開始しなければなりません。 このウォークスルーは、AdCP を通じた彼女の旅路を追うものです。ブリーフから配信レポートまで、プロトコルがクリエイティブツール・パブリッシャー・AI エージェントを 1 つのワークフローへとつなぐ仕組みを示します。 ## ステップ 1: ブリーフを書く Maya はまず自分が最も得意とするところから始める。クリエイティブの方向性です。 クリエイティブブリーフが TV・スマートフォン・ノートパソコン・ビルボードスクリーンへと放射状に広がっていく AdCP では、ブリーフは `build_creative` の `message` フィールドに当たる。Maya が JSON を書く必要はない。エージェンシーのプラットフォームが彼女の指示をプロトコル形式に変換します。 ```json theme={null} { "message": "Holiday campaign for Acme Outdoor. Warm, adventurous tone. Hero product: Trail Pro 3000 hiking boot. Key message: 'Gift the adventure.' Use brand colors and winter imagery.", "brand": { "domain": "acmeoutdoor.com" }, "target_format_ids": [ { "agent_url": "https://streamhaus.example", "id": "ssai_30s" }, { "agent_url": "https://outdoornet.example", "id": "display_300x250" }, { "agent_url": "https://outdoornet.example", "id": "display_728x90" } ], "quality": "draft", "include_preview": true } ``` ブランドアイデンティティ(カラー・ロゴ・タイポグラフィ・トーン)は `acmeoutdoor.com/.well-known/brand.json` に格納されています。チェーン内のすべてのエージェントがそこから読み取る。Maya のチームはそれを 1 か所で管理するだけでいい。 | Maya の言葉 | プロトコルでの呼び方 | | ------------ | ---------------------------------------- | | クリエイティブブリーフ | `build_creative` の `message` | | ブランドガイドライン | `/.well-known/brand.json` の `brand.json` | | コンプ / モックアップ | プレビュー(`preview_creative` から取得) | | 最終クリエイティブ | プロダクションクオリティのマニフェスト | | トラフィッキング | 各セラーへの `sync_creatives` | | キャンペーンレポート | 各エージェントへの `get_creative_delivery` | ## ステップ 2: 各セラーが対応するフォーマットを調べる 何かを生成する前に、Maya のプラットフォームは各セラーが受け付けるフォーマットを確認します。これは自動で行われるが、内部で何が起きているかを見てみよう。 プラットフォームは接続されている各エージェントで `list_creative_formats` を呼び出す。 エージェンシープラットフォームが 3 つのセラーで list_creative_formats を呼び出す。StreamHaus は CTV フォーマットを、Pinnacle Media はディスプレイサイズを、CommHub はソーシャルフォーマットを返す これで Maya のプラットフォームは把握できました。StreamHaus はビデオファイルと VAST タグが必要。Pinnacle はディスプレイバナーが必要。CommHub はフィードネイティブのコンテンツアセットが必要。1 本のブリーフから、3 種類の異なる出力形式が生まれる。 ## ステップ 3: ガバナンス制約を確認する クリエイティブを生成する前に、Maya のプラットフォームはどのガバナンス制約が適用されるかを確認します。バイヤーのガバナンスエージェントは、すべてのクリエイティブにセキュリティスキャン、コンテンツ分類、またはポリシーコンプライアンスを要求する場合があります。 プラットフォームは、どのクリエイティブ機能が評価されるかを発見するために、バイヤーのガバナンスエージェントで `get_adcp_capabilities` を呼びます: ```javascript theme={null} const capabilities = await governanceAgent.getCapabilities(); const creativeFeatures = capabilities.governance.creative_features; // Returns features like: registry:eu_ai_act_article_50, auto_redirect, credential_harvest ``` これらの制約は生成リクエストに供給されます。バイヤーが EU AI 法コンプライアンス(`registry:eu_ai_act_article_50`)を要求する場合、クリエイティブエージェントは生成されたコンテンツに必要なプロベナンスメタデータを含めることを保証します。セキュリティスキャンが要求される場合、生成されたクリエイティブは `auto_redirect` や `credential_harvest` のフラグをトリガーするパターンを避けます。 ガバナンス制約は、特定のクリエイティブマニフェストをスコアリングする [`get_creative_features`](/docs/governance/creative/get_creative_features) を介して生成後に評価されます——しかし、生成前に機能セットを知っておくことで、拒否・再生成のサイクルを避けられます。ガバナンス要件を認識したクリエイティブエージェントは、最初のパスで準拠した出力を生成します。 機能ベースの評価モデルについては[クリエイティブガバナンスの概要](/docs/governance/creative/index)を参照してください。 ## ステップ 4: 生成とプレビュー 3 つの AI エージェントがワークベンチで協力し、クリエイティブアセットを受け渡している Maya のプラットフォームはブリーフを適切なクリエイティブエージェントに振り分ける。CTV スペシャリストが動画を担当します。ディスプレイエージェントがバナーを担当します。ソーシャルプラットフォームは各コミュニティのボイスに合ったフィードネイティブのコンテンツを生成します。 エージェンシープラットフォームがブリーフを 3 つのエージェントに振り分ける。Video Agent は SSAI 30s 用の build_creative を受け取り、Display Agent はバナーサイズ用の build_creative を受け取り、Social Platform はブリーフが埋め込まれた create_media_buy を受け取る CTV とディスプレイでは、Maya はすぐにプレビューできるドラフトクオリティのマニフェストを受け取ります。ソーシャルでは、プラットフォームが配信時に生成するが、Maya はどのような見た目になるかを事前にプレビューできます。 荒削りなドラフトモックアップが洗練されたプロダクションクリエイティブへと変わる分割画面 Maya はリビングルームの文脈で CTV スポットを、2 つの異なるコミュニティでソーシャル投稿を確認したいと思った。 ```json theme={null} { "request_type": "single", "creative_manifest": { "...": "video manifest from build_creative" }, "inputs": [ { "name": "Living room primetime", "context_description": "CTV app, evening, family household" }, { "name": "Mobile commute", "context_description": "Phone screen, morning, commuter" } ] } ``` ドラフトを確認します。CTV スポットはもう少し温かみのある色調が必要です。Maya は更新したメッセージを付けて再度 `build_creative` を呼び出してフィードバックを送る。エージェントが修正を重ねる。これがティッシュセッションです。素早く、ローファイで、方向性を固めることに集中します。 Maya が満足したら、プロダクションクオリティに昇格させる。 ```json theme={null} { "quality": "production" } ``` ## ステップ 5: セラーへの配信 ストラテジストが Launch を押すとパブリッシャーとの接続が次々と点灯していく 完成したクリエイティブを各セラーに届ける必要があります。Maya のプラットフォームは各セラーで `sync_creatives` を呼び出す。同じクリエイティブを、各セラーが求めるフォーマットに適応させながら。 クリエイティブエージェンシープラットフォームが sync_creatives を通じて 3 つのセラーにクリエイティブを配信します。CTV セラーはレビュー保留を返し、ディスプレイセラーは承認済みを返し、ソーシャルプラットフォームはコミュニティガイドラインによるレビュー保留を返す セラーによってレビュープロセスが異なります。Pinnacle はブランドセーフティルールに基づいて自動承認します。StreamHaus と CommHub は手動レビューを行います。Maya のプラットフォームはセラーごとに承認状態を追跡します。あるセラーで承認済みで別のセラーで保留中というのは正常な状態であり、エラーではありません。 CommHub は標準的な広告ポリシーに加えてコミュニティガイドラインも確認します。プロモーション投稿が特定のコミュニティから却下された場合、`list_creatives` にはそのコミュニティのルールを参照した `rejection_reason` が表示されます。 ## ステップ 6: キャンペーン開始 — AI がバリアントを生成します キャンペーンが始まった。ここからが興味深い。 StreamHaus では、CTV スポットはそのまま配信されます。1 つのクリエイティブ、1 つのバリアント(Tier 1)。Pinnacle では、ディスプレイ広告がアセットグループ最適化で配信されます。プラットフォームが異なる見出しと画像の組み合わせをテストする(Tier 2)。CommHub では、ソーシャルプラットフォームが各コミュニティのボイスとトレンドトピックに合ったプロモーション投稿を生成する(Tier 3)。 Maya はこれらを一切管理しません。プロトコルが処理します。しかし、すべての状況を確認することはできます。 ## ステップ 7: 配信とバリアントの確認 3 つのセラーからのデータを統合したパフォーマンスチャートを表示する統合ダッシュボード 1 週間後、Maya は配信データを確認します。プラットフォームは各エージェントで `get_creative_delivery` を呼び出し、結果を統合します。 3 つのセラーがエージェンシープラットフォームに配信データを返します。CTV セラーは 15 万インプレッションのバリアント 1 件、ディスプレイセラーは 20 万インプレッションのバリアント 4 件、ソーシャルプラットフォームは 8.5 万インプレッションと エンゲージメント指標を含む AI 生成バリアント 12 件を報告する ソーシャルプラットフォームのレスポンスには、標準的な配信指標に加えて `ext` フィールドにエンゲージメント指標(アップボート・コメント・シェア)が含まれています。ディスプレイバリアントには、各バリアントで使用された見出しと画像の組み合わせを示すマニフェストが含まれています。CTV には完了率と四分位データが含まれています。 ## ステップ 8: 配信内容を再現します ストラテジストがパフォーマンス評価付きの広告バリアントグリッドを見ており、1 つがトップパフォーマーとしてハイライトされている Maya は CommHub の AI がハイキングコミュニティ向けに生成した内容を正確に確認したいと思った。配信データからトップパフォーマーのバリアントを見つけて再現します。 ```json theme={null} { "request_type": "variant", "variant_id": "gen_hiking_community_v3" } ``` プラットフォームは実際に配信されたものをそのまま描画します。生成された見出し、コミュニティに適応した画像、エンゲージメント UI。Maya は AI がトレイルフォトグラフィーを前面に出し、ハイキングコミュニティに響く言葉を使っていたことを確認できます。このインサイトを次のブリーフに活かす。 ## ステップ 9: 価格と請求 クリエイティブエージェントはサービスに課金できます。Pinnacle の各クリエイティブエージェントとのアカウントには、合意された価格であるレートカードが含まれます。アドサーバーについては、Maya のプラットフォームが `list_creatives` を呼び、各クリエイティブに `pricing_options` が含まれます。変換エージェントについては、価格は代わりに `list_creative_formats` に現れます——フォーマットがプロダクトだからです。`build_creative` が実行されると、レスポンスには `vendor_cost` が含まれ、プラットフォームは各ビルドのコストを把握します。 キャンペーンの後、Maya のプラットフォームは、ビルドレスポンスの `creative_id` と `pricing_option_id` を指定して各クリエイティブエージェントで `report_usage` を呼びます。これが請求のループを閉じます——クリエイティブエージェントは、報告されたコストが自身のレートカードと一致することを検証できます。 異なるエージェントは異なる価格設定をします。CTV のアドサーバーは CPM(配信された 1000 インプレッションあたりのコスト)で課金します。変換エージェントは適応したフォーマットごとに課金します。AI 生成プラットフォームは生成した画像ごとに課金します。プロトコルは、同じ `pricing_options` のパターンを通じてこれらすべてを扱います——詳細は[価格の仕様](/docs/creative/specification#価格)を参照してください。 ## 全体像 クリエイティブのライフサイクル全体: ブリーフを書く → フォーマットを発見する → 生成してプレビューする → 反復して承認する → セラーに配信する → キャンペーン開始 → 配信を確認してバリアントを再現する → 次のブリーフに活かす → サイクルを繰り返す すべてのステップが AdCP の標準タスクを使っています。クリエイティブツール・パブリッシャー・ソーシャルプラットフォームを問わず、すべてのエージェントが同じプロトコルで通信します。Maya は 1 本のブリーフを書けば、AdCP が残りを処理します。フォーマット適応・マルチエージェントルーティング・クロスセラー配信・バリアント追跡・統合レポーティング・請求の照合、すべてです。 ## さらに深く学ぶ * **主要コンセプト**: [アセット、フォーマット、マニフェスト、クリエイティブエージェント](/docs/creative/key-concepts) — このウォークスルーの背後にある構成要素 * **AI クリエイティブ**: [キャンペーンチームのための AI クリエイティブ](/docs/creative/ai-creative-overview) — エンジニア以外向けの戦略的視点 * **ジェネレーティブクリエイティブ**: [ジェネレーティブクリエイティブ](/docs/creative/generative-creative) — Tier 1・2・3 の詳細 * **エージェントを作る**: [クリエイティブエージェントの実装](/docs/creative/implementing-creative-agents) — プラットフォームに AdCP を話させる方法 * **オーケストレーション**: [マルチエージェントオーケストレーション](/docs/creative/multi-agent-orchestration) — Maya のプラットフォームを支えるエンジニアリングパターン * **認定を取得する**: [バイヤートラック](/docs/learning/tracks/buyer) では、インタラクティブなモジュールを通じてクリエイティブワークフロー全体を学べる # キーコンセプト Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/key-concepts アセット、フォーマット、マニフェスト、クリエイティブエージェントは、プログラマティック広告クリエイティブのための AdCP クリエイティブプロトコルの4つの構成要素。 一度のアップロードで、すべてのフォーマット。このガイドでは、フォーマット要件の定義から広告の組み立てとデリバリーまで、AdCP でクリエイティブがどのように機能するかを説明します。 ## 4つのキーコンセプト ### 1. **アセット** アセットはクリエイティブを構成するために使用される個々のビルディングブロックです。各アセットには、その使用方法と検証方法を決定する定義済みのタイプがあります。 **例:** * 画像ファイル(PNG、JPG、WebP) * ビデオファイル(MP4、WebM、MOV) * テキストのブロック(ヘッドラインまたは CTA) * 音声ファイル(MP3、M4A) * HTML または JavaScript タグ * トラッキングまたはクリックスルー URL ### 2. **フォーマット** フォーマットは IAB フォーマットタクソノミーに基づいて、アセットがどのように組み立てられてレンダリングされるかを定義します。フォーマットは以下を指定します: * メディアファミリー(例: ディスプレイ、ビデオ、オーディオ、ネイティブ) * 必要なアセットタイプ * 技術的な制約(デュレーション、寸法、コーデック、ファイルサイズ) * レンダリングとインタラクションの期待 **例:** * ビデオフォーマットは1つのビデオアセット(MP4、30秒、定義済みの解像度とコーデック)と1つのクリックスルー URL が必要 * ディスプレイフォーマットは1つ以上の画像または HTML アセット、オプションのテキストアセット、トラッキング URL が必要 ### 3. **マニフェスト** マニフェストはクリエイティブプリセットを定義します: 最終的にどこでどのように配信されるかに関わらず、ブランドが伝えたいことと見せたいことをキャプチャする再利用可能な設定です。 マニフェストは広告フォーマットやレンダリングロジックを記述しません。代わりに、互換性のあるフォーマットがサポートされるどこでも一貫して適用できる、クリエイティブの選択の名前付きセット(メディア、メッセージング、デスティネーション、トラッキングインテント)を宣言します。 **例**: "video\_30s" のマニフェストは実際の 30 秒ビデオファイルの URL、プラストラッキングピクセルとランディングページ URL を提供します。 ### 4. **クリエイティブエージェント** クリエイティブエージェントは以下を行うサービスだ: * フォーマットを定義して文書化する(権威あるソース) * 各フォーマットのレンダリング方法を説明します * フォーマット要件に対してマニフェストを検証します * クリエイティブがどのように表示されるかを示すプレビューを生成します * オプションで自然言語ブリーフからマニフェストを構築します 各フォーマットはその権威あるクリエイティブエージェントを識別します。 ## どのように組み合わさるか ``` フォーマット定義(クリエイティブエージェントによる) ↓ "video_30s フォーマットに必要なもの: - 1つのビデオファイルアセット(MP4、30秒、1920x1080) - 1つのクリックスルー URL" クリエイティブマニフェスト(バイヤーによる) ↓ "実際のビデオファイルを提供: https://cdn.brand.com/spring_30s.mp4 ランディングページ: https://brand.com/spring-sale" セールスエージェント(検証してデリバリー) ↓ - チェック: 本当に30秒か? MP4 か? - 追加: インプレッショントラッキング、クリックトラッキング - デリバリー: 広告サーバーにクリエイティブを配信 ``` ## ワークフロー ### 1. **発見** — 「どのフォーマットをサポートするか?」 バイヤーはセールスエージェントまたはクリエイティブエージェントで `list_creative_formats` を呼び出して、利用可能なフォーマットとその完全な仕様を発見します。各フォーマットには、そのフォーマットの権威あるクリエイティブエージェントを識別する `agent_url` が含まれます。 フォーマット発見の詳細については[クリエイティブフォーマット](/docs/creative/formats)を参照。 ### 2. **組み立て** — 「アセットを提供する」 バイヤーは選択したフォーマットの要件を満たすアセットを提供するマニフェストを作成します。マニフェストはフォーマット仕様と実際のアセット URL、テキスト、トラッキングデータをペアにします。 マニフェスト構造の詳細については[クリエイティブマニフェスト](/docs/creative/creative-manifests)を参照。 ### 3. **検証** — 「要件に一致するか?」 クリエイティブエージェントは以下を確認してマニフェストを検証する: * 必要なアセットがすべて存在するか? * アセットは技術的な制約(デュレーション、寸法、ファイルタイプ、ファイルサイズ)を満たすか? * トラッキング URL とマクロは正しくフォーマットされているか? ### 4. **デリバリー** — 「広告サーバーにトラフィックする」 セールスエージェントは検証済みのクリエイティブを広告サーバーに配信し、AdCP の普遍的なコンセプトをプラットフォーム固有のフォーマットに変換します。 ## コアコンセプト ### アセットとアセットタイプ アセットはクリエイティブの原材料です。各アセットにはその役割を定義するタイプがある: * **image**: 静的画像(JPEG、PNG、WebP) * **video**: ビデオファイル(MP4、WebM、MOV)または VAST タグ * **audio**: 音声ファイル(MP3、M4A)または DAAST タグ * **text**: ヘッドライン、説明、CTA * **html**: HTML5 クリエイティブまたはサードパーティタグ * **javascript**: JavaScript タグ * **url**: トラッキングピクセル、クリックスルー URL、ウェブフック 詳細仕様については[アセットタイプ](/docs/creative/asset-types)を参照。 ### フォーマットとフォーマット権限 各フォーマットには権威あるソースがある — それを定義するクリエイティブエージェント(`agent_url` で示されます)。そのエージェントは: * 決定的なドキュメントをホストします * アセットの組み立て方を説明します * フォーマットのレンダリング方法を記述します * 検証ルールを提供します **標準フォーマット vs カスタムフォーマット:** * **標準フォーマット**は IAB 仕様に基づいており、リファレンスクリエイティブエージェント(`https://creative.adcontextprotocol.org`)によってホストされます * **カスタムフォーマット**は特化したインベントリのために個々のパブリッシャーまたはクリエイティブプラットフォームによって定義されます * 技術的には両方は同じように機能する — `agent_url` フィールドが各フォーマットの権威エージェントを識別します ビデオ、ディスプレイ、オーディオ、DOOH、カルーセルにわたるフォーマットの例とパターンについては[チャネルガイド](/docs/creative/channels/video)を参照。 ### マニフェスト マニフェストはフォーマット定義のアセット ID と実際のアセットコンテンツをペアにする JSON 構造です。完全なクリエイティブを組み立てるために必要な URL、テキスト値、トラッキングエンドポイントを提供します。 詳細ドキュメントについては[クリエイティブマニフェスト](/docs/creative/creative-manifests)を参照。 ### ユニバーサルマクロ AdCP はプラットフォーム間で機能するユニバーサルマクロを定義します。セールスエージェントはこれらのマクロを広告サーバーのネイティブ構文に変換します。 * **インプレッショントラッキング用**: セールスエージェントは AdCP マクロをインプレッションを配信する前に広告サーバーのネイティブマクロフォーマットに変換します。 * **クリックトラッキング用**: マクロ変換に加えて、セールスエージェントは `{REDIRECT_URL}` を最終デスティネーションクリック URL に置き換え、クリックが記録された後の適切なリダイレクトを確保するために広告サーバーのデスティネーション URL としてクリックトラッキング URL を配信します。 完全なリファレンスについては[ユニバーサルマクロ](/docs/creative/universal-macros)を参照。 ## 一般的なパターン ### サードパーティタグ 一部のフォーマットは、ファーストパーティのメディア要素から組み立てられるのではなく、サードパーティシステムによって配信されるクリエイティブをサポートします。 このパターンでは、クリエイティブは HTML タグまたは JavaScript タグとして提供されます。フォーマット定義は必要な要素タイプと適用可能な制約(サンドボックス化やトラッキングの期待など)を指定します。レンダリング、アセット読み込み、インタラクションロジックはサードパーティシステムによって外部で処理されます。 このパターンはディスプレイフォーマットで最も一般的に使用されます。サードパーティタグの例については[ディスプレイチャネルガイド](/docs/creative/channels/display)を参照。 ### 繰り返し可能なアセットグループ 一部のフォーマットでは、共通の構造の下で繰り返されるクリエイティブ要素のセットを許可または必要とします。このパターンはカルーセル、スライドショー、ストーリースタイルのシーケンス、プレイリストまたはプロダクトリストなどのクリエイティブに使用されます。 各繰り返しには同じ論理的な要素ロール(例: 画像、テキスト、URL)が含まれ、繰り返しの数はフォーマットによって定義または制約されます。繰り返し可能な要素グループは構造パターンを記述するものであり、フォーマットやレイアウトの保証ではなく、ディスプレイ、ネイティブ、または他の互換性のあるフォーマット間で使用される場合があります。 詳細なドキュメントについては[カルーセル & マルチアセットフォーマット](/docs/creative/channels/carousels)ガイドを参照。 ### DOOH インプレッショントラッキング デジタルアウトオブホーム(DOOH)環境は、パーソナルデバイス環境とは異なる測定セマンティクスが必要です。 このパターンでは: * インプレッションはベニューまたはスクリーンレベルの識別子を使用してトラッキングされます * デバイスベースの識別子は使用されない * トラッキングは環境固有のマクロに依存します これは測定とレポートにのみ影響し、クリエイティブフォーマットの分類やアセット要件は変更しません。DOOH 固有のマクロの詳細については[DOOH チャネルガイド](/docs/creative/channels/dooh)を参照。 ## チャネル別情報 チャネルとフォーマットファミリー別のクリエイティブに関する詳細情報については[クリエイティブマニフェスト](/docs/creative/creative-manifests)を参照: * **ビデオ広告** — インストリームとアウトストリームのビデオフォーマット(CTV 環境を含む) * **ディスプレイ広告** — 標準ディスプレイフォーマット(バナーやインタースティシャルなど) * **オーディオ広告** — インストリームオーディオフォーマット(オプションのコンパニオンディスプレイ付き) * **DOOH** — デジタルアウトオブホームインベントリとベニューベースのデリバリー環境 ## はじめる 1. **フォーマットを発見する**: `list_creative_formats` を呼び出して利用可能なものを確認 2. **チャネルガイドを選ぶ**: キャンペーンタイプに合ったガイドを選択 3. **マニフェストを構築する**: フォーマット要件に従う 4. **ユニバーサルマクロを使用する**: 標準化されたマクロでトラッキングを追加 5. **プレビューする**: `preview_creative` を使用してどのように見えるか確認 6. **送信する**: `create_media_buy` リクエストにマニフェストを含めます ## 追加リソース * [クリエイティブタスクリファレンス](/docs/creative/task-reference/list_creative_formats) — クリエイティブタスクの API ドキュメント * [生成クリエイティブ](/docs/creative/generative-creative) — AI 駆動のクリエイティブ生成ガイド # マルチエージェントクリエイティブオーケストレーション Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/multi-agent-orchestration AdCP のマルチエージェントクリエイティブオーケストレーションは、エージェント間でリクエストをルーティングし、セラーにアセットを配布し、配信データを集計します。 エージェンシープラットフォームやホールディングカンパニーのシステムは、ブランドチームとクリエイティブツール、広告サーバー、パブリッシャー間の数十のエージェントの間に位置することが多い。オーケストレーターの役割は、クリエイティブリクエストを適切なエージェントにルーティングし、完成したクリエイティブをセラーに配布し、配信データを統一されたビューに集計することです。 Sequence diagram showing an orchestrator calling get_adcp_capabilities, list_creative_formats, build_creative, sync_creatives, and get_creative_delivery across multiple agents このページでは、AdCP 上にそのオーケストレーション層を構築するためのパターンをカバーします。 ## オーケストレーターの役割 オーケストレーターは複数の AdCP エージェントに接続し、それら全体でクリエイティブワークフローを調整するバイヤーサイドのシステムです。クリエイティブプロトコル自体を実装するのではなく、それを消費します。典型的なオーケストレーターにはエージェンシープラットフォーム(ホールディングカンパニーの内部ツールチェーンを想像します)、ブランドサイドのクリエイティブハブ、マルチパブリッシャーキャンペーン管理システムが含まれます。 オーケストレーターの責任: * 接続されている各エージェントが何をできるかを**発見する** * クリエイティブリクエストを最も適したエージェントに**ルーティングする** * 完成したクリエイティブを必要なすべてのセラーに**配布する** * エージェント全体の配信データを単一のレポートビューに**集計する** ## ケイパビリティ発見 リクエストをルーティングする前に、オーケストレーターは各エージェントがサポートしているもののマップが必要です。接続されているすべてのエージェントで `get_adcp_capabilities` を呼び出し、各レスポンスの `creative` セクションをインデックス化します。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/protocol/get-adcp-capabilities-response.json", "adcp": { "major_versions": [3] }, "supported_protocols": ["creative"], "creative": { "has_creative_library": true, "supports_generation": false, "supports_transformation": true, "supports_compliance": false } } ``` レスポンスからケイパビリティマップを構築する: | エージェント | `supports_generation` | `supports_transformation` | `has_creative_library` | | ----------------------------------------- | --------------------- | ------------------------- | ---------------------- | | `https://creative.novastudio-example.com` | true | true | false | | `https://ads.flashtalking-example.com` | false | false | true | | `https://sales.pinnaclemedia-example.com` | true | true | true | このマップをキャッシュして定期的に更新する — ケイパビリティレスポンスの `last_updated` フィールドはエージェントのケイパビリティが最後に変更された時刻を示します。 ## エージェント全体のフォーマット発見 各エージェントで `list_creative_formats` を並行して呼び出す。各エージェントは `formats` 配列を返し、各フォーマットにはそのフォーマット定義を所有するエージェントを識別する `agent_url` を持つ `format_id` が含まれます。 ```json theme={null} { "formats": [ { "format_id": { "agent_url": "https://creative.novastudio-example.com", "id": "display_300x250" }, "name": "Display 300x250", "renders": [{ "role": "primary", "dimensions": { "width": 300, "height": 250, "unit": "px" } }] } ] } ``` 結果を統一されたフォーマットカタログにマージします。同じ標準フォーマット(同じ寸法、同じタイプ)が複数のエージェントから利用可能な場合、すべてのエントリを保持する — 各 `format_id` の `agent_url` がそれらを区別します。ルーティング時には、リクエストに最もよく一致するケイパビリティを持つエージェントを優先する(ブリーフには生成エージェント、タグ取得にはライブラリエージェント)。 一部のエージェントは、オーケストレーターがクエリできる追加エージェントを指す `creative_agents` 配列も返します。これらの参照をたどって、接続リストにまだないエージェントからフォーマットを発見するが、無限ループを避けるために訪問済みの URL を追跡します。 ## クリエイティブリクエストのルーティング ケイパビリティマップとフォーマットカタログを使用して、各リクエストを適切なエージェントにルーティングします。 **既存のクリエイティブがないブリーフ** — ブランドチームがクリエイティブの方向性を提供するがアセットがない。`supports_generation: true` のエージェントにルーティングします。エージェントは `build_creative` を通じて自然言語ブリーフを受け取り、生成されたアセットを含むマニフェストを返します。 **既存のクリエイティブのリサイズが必要** — バイヤーがあるフォーマットのマニフェストを持ち、別のフォーマットに適応する必要があります。`supports_transformation: true` のエージェントにルーティングします。既存の `creative_manifest` と希望する出力の `target_format_id` を渡します。 **広告サーバーからタグを取得** — クリエイティブがすでにプラットフォームライブラリに存在します。それをホストする `has_creative_library: true` のエージェントにルーティングします。`creative_id` と `target_format_id` を `build_creative` に渡して配信タグを取得します。 **ターゲットフォーマットがエージェントを決定する** — リクエストが特定のフォーマット(CTV、DOOH、パブリッシャー独自のユニット)をターゲットにする場合、`format_id.agent_url` が一致するエージェントにルーティングします。そのエージェントがそのフォーマットの権威であり、最も信頼性の高い出力を生成します。 複数のエージェントが条件を満たす場合、ケイパビリティを組み合わせたエージェントを優先します。`supports_generation: true` と `has_creative_library: true` を持つセールスエージェントは、クリエイティブを生成してワンステップで保存できるため、別の同期が不要になります。 ## 一度ビルドして多くに配布します コアとなるマルチエージェントワークフロー: クリエイティブを一度生成またはビルドして、必要なすべてのセラーに配布します。 ### ステップ 1: クリエイティブをビルドします クリエイティブエージェントで `build_creative` を呼び出してマニフェストを作成する: ```json theme={null} { "message": "Create a holiday display campaign for Acme Corp featuring winter products", "target_format_id": { "agent_url": "https://creative.novastudio-example.com", "id": "display_300x250" }, "concept_id": "concept_holiday_2026" } ``` エージェントは生成されたアセットを含むマニフェストを返します。結果に独自の `creative_id` を割り当てる — この ID はクリエイティブをどこに送っても一貫しています。 ### ステップ 2: 各セラーに同期します 各セールスエージェントで同じ `creative_id` と `concept_id` を使用して `sync_creatives` を呼び出す: ```json Pinnacle Media theme={null} { "account": { "account_id": "acct_acme_pinnacle" }, "creatives": [{ "creative_id": "acme_holiday_300x250", "name": "Holiday 2026 - Medium Rectangle", "concept_id": "concept_holiday_2026", "format_id": { "agent_url": "https://creative.novastudio-example.com", "id": "display_300x250" }, "assets": { "image": { "url": "https://cdn.acme-example.com/holiday-300x250.png", "width": 300, "height": 250 }, "click_url": { "url": "https://acme-example.com/holiday-sale" } } }], "assignments": [{ "creative_id": "acme_holiday_300x250", "package_id": "pkg_premium_display" }] } ``` ```json Nova Sports theme={null} { "account": { "account_id": "acct_acme_novasports" }, "creatives": [{ "creative_id": "acme_holiday_300x250", "name": "Holiday 2026 - Medium Rectangle", "concept_id": "concept_holiday_2026", "format_id": { "agent_url": "https://creative.novastudio-example.com", "id": "display_300x250" }, "assets": { "image": { "url": "https://cdn.acme-example.com/holiday-300x250.png", "width": 300, "height": 250 }, "click_url": { "url": "https://acme-example.com/holiday-sale" } } }], "assignments": [{ "creative_id": "acme_holiday_300x250", "package_id": "pkg_sports_display" }] } ``` 両方の呼び出しは同じ `creative_id`(`acme_holiday_300x250`)と `concept_id`(`concept_holiday_2026`)を使用します。これらは後でクロスエージェント相関のキーになります。 ### ステップ 3: 承認状態を追跡します 各セラーはクリエイティブを独立してレビューします。各エージェントで `list_creatives` をポーリングしてステータスを確認します: ```json theme={null} { "filters": { "creative_ids": ["acme_holiday_300x250"] } } ``` 各クリエイティブの `status` フィールドは現在の位置を示します: `processing`、`pending_review`、`approved`、`rejected`、`archived`。統合されたビューを構築する: | セラー | `creative_id` | ステータス | | -------------- | ---------------------- | ---------------- | | Pinnacle Media | `acme_holiday_300x250` | `approved` | | Nova Sports | `acme_holiday_300x250` | `pending_review` | クリエイティブはあるセラーには承認され、別のセラーには拒否される場合がある — 各セラーは独自のポリシーを適用します。ステータス遷移の仕組みについては[クリエイティブレビュー](/docs/creative/sales-agent-creative-capabilities#creative-review)を参照。 ## クロスエージェント配信集計 クリエイティブがライブになったら、各エージェントで `get_creative_delivery` を呼び出してパフォーマンスデータを収集します。レスポンスにはバリアントレベルのブレークダウンを含む `creatives` 配列が含まれます: ```json theme={null} { "reporting_period": { "start": "2026-11-01T00:00:00-05:00", "end": "2026-11-15T00:00:00-05:00", "timezone": "America/New_York" }, "currency": "USD", "creatives": [{ "creative_id": "acme_holiday_300x250", "format_id": { "agent_url": "https://creative.novastudio-example.com", "id": "display_300x250" }, "totals": { "impressions": 145000, "clicks": 2900, "spend": 1450.00 }, "variant_count": 3, "variants": [{ "variant_id": "var_a1b2c3", "impressions": 80000, "clicks": 1700, "spend": 800.00 }] }] } ``` ### エージェント間の相関 **`creative_id`** は主な相関キーです。各セラーに同期する際に同じ `creative_id` を割り当てたため、エージェント間の配信レコードを直接マッチできます。 **`concept_id`** は関連するクリエイティブをグループ化します。すべてのサイズとセラー間で "Holiday 2026" キャンペーンの集計メトリクスが必要な場合、各エージェントの `list_creatives` で `concept_ids` でフィルタリングして `creative_id` 値のセットを取得し、すべての配信データをプルします。 **`variant_id`** は単一のエージェントとクリエイティブにスコープされます。2つのエージェントが独立して同じ `variant_id` 文字列を割り当てる場合があります。エージェント間でバリアントを集計する際は、エージェント URL をプレフィックスとして付けてグローバルに一意なキーを作成する: `https://sales.pinnaclemedia-example.com/var_a1b2c3`。 ### タイムゾーンの正規化 各エージェントは `reporting_period.timezone` を通じて独自のタイムゾーンでレポートします。エージェント間でメトリクスを合計する前に、すべてのタイムスタンプを共通のタイムゾーンに変換します。`timezone` フィールドは IANA 識別子(`America/New_York`、`Europe/London`、`UTC`)を使用するため、標準のタイムゾーンライブラリが変換を処理します。 ## 相関キーとしての `concept_id` コンセプトはバイヤーが割り当てたグループ化 — プロトコルはそれらを強制しません。オーケストレーターがコンセプトを構成するものを決定し、関連するクリエイティブを異なるエージェントに同期する際に `concept_id` を一貫して割り当てる。 典型的なマッピング: キャンペーンアイデアごとに1つのコンセプト、コンセプトごとに複数のクリエイティブ(異なるサイズ、フォーマット、またはバリエーション)。 ``` concept_holiday_2026 ├── acme_holiday_300x250 (display, synced to Pinnacle Media + Nova Sports) ├── acme_holiday_728x90 (display, synced to Pinnacle Media) └── acme_holiday_video_30s (video, synced to Nova Sports) ``` `sync_creatives` の各クリエイティブの `concept_id` フィールドを通じて同期時に `concept_id` を割り当てる。使用方法: * `concept_ids` で `list_creatives` をフィルタリングして特定のエージェントのコンセプト内のすべてのクリエイティブを確認します * ロールアップレポートのためにコンセプトで `get_creative_delivery` 結果をグループ化します * 同じキャンペーンアイデアのサイズとセラー全体の承認状態を追跡します ## エージェント全体のエラー処理 マルチエージェント操作は部分的な失敗を生む。あるセラーがクリエイティブを受け入れ、別が拒否する場合や、エージェントが一時的に到達不能になる場合があります。最初からこれに対応して設計します。 ### 部分的な同期失敗 `sync_creatives` はクリエイティブごとの結果を返します。レスポンスの各アイテムの `action` フィールドを確認します: ```json theme={null} { "creatives": [ { "creative_id": "acme_holiday_300x250", "action": "created" }, { "creative_id": "acme_holiday_728x90", "action": "failed", "errors": ["Format not supported"] } ] } ``` レスポンスに `creatives` の代わりにトップレベルの `errors` がある場合、操作全体が失敗した(認証、ネットワーク、無効なリクエスト)。呼び出し全体を再試行します。 成功した操作内で個々のクリエイティブが失敗した場合、個別に処理する — 問題を修正して、`creative_ids` フィルターを使用して失敗したクリエイティブのみを再同期する: ```json theme={null} { "account": { "account_id": "acct_acme_pinnacle" }, "creative_ids": ["acme_holiday_728x90"], "creatives": [{ "creative_id": "acme_holiday_728x90", "name": "Holiday 2026 - Leaderboard", "format_id": { "agent_url": "https://creative.novastudio-example.com", "id": "display_728x90" }, "assets": { } }] } ``` ### エージェントの到達不能 エージェントが到達不能な場合、オーケストレーターは以下を行うべきだ: 1. どの同期またはクエリが失敗したか、どのエージェントで失敗したかを記録します 2. 他のエージェントの処理を続行する — ワークフロー全体をブロックしません 3. 失敗したエージェントを指数バックオフで再試行します 4. 再試行が安全であるよう `sync_creatives` で `idempotency_key` を使用します ### 一致しない承認状態 あるセラーで承認され別のセラーで拒否されたクリエイティブはエラーではなく正常です。オーケストレーターはこれを失敗として処理するのではなく、キャンペーンチームに明確にサーフェスするべきです。拒否が修正可能な問題(間違いなアスペクト比、クリック URL の欠如)によるものであれば、クリエイティブを更新して拒否したセラーのみに再同期します。 ### レート制限と並行処理 多数のエージェントを並行して呼び出す場合(クロスセラー同期と配信集計で一般的)、エージェントがリクエストをレート制限する可能性があることを想定します。標準的な HTTP パターンでこれを処理します: * `429 Too Many Requests` レスポンスと `Retry-After` ヘッダーを尊重します * ジッターを含む指数バックオフを再試行に使用します * エージェントごとに合理的な並行処理制限を設定する(エージェントあたり 5 つの並行リクエストから始め、観察された動作に基づいて調整します) * どのエージェントが低い並行処理が必要かを特定するために、エージェントごとのレート制限イベントをログに記録します レート制限の動作はエージェント固有であり、AdCP によって標準化されていません。一部のエージェントは `Retry-After` ヘッダー付きの `429` を返す場合があります。その他は明示的なシグナルなしにレスポンスを遅らせる場合があります。オーケストレーターは両方のパターンを処理するように構築します。 ## 特化フォーマットのオーケストレーション このページのパターンはすべてのクリエイティブフォーマットに適用される — ディスプレイ、ビデオ、CTV、会話型、オーディオ、DOOH。プロトコルレベルの操作(`list_creative_formats`、`sync_creatives`、`get_creative_delivery`)はフォーマットタイプに関わらず同一に機能します。フォーマット固有の違いはアセット要件とプレビュー動作にある: * **CTV フォーマット**はマルチレンダープレビュー(プライマリビデオ + コンパニオン)を生成します。[CTV とコネクテッド TV](/docs/creative/channels/ctv) を参照。 * **会話型フォーマット**はサンドボックステスト用にプレビューで `interactive_url` を返し、バリアントマニフェストには会話トランスクリプトが含まれます。[会話型フォーマット](/docs/creative/generative-creative#conversational-and-interactive-formats)を参照。 * **フィードネイティブ/ソーシャルフォーマット**は、レンダリング時にバイヤーアセットをラップするプラットフォーム所有のクロームを持ちます。[パターン 4](/docs/creative/implementing-creative-agents#pattern-4-feed-nativesocial-format-agent) を参照。 オーケストレーターはルーティングや同期のためにフォーマット固有のロジックを必要としない — `format_id.agent_url` とケイパビリティマップがそれを処理します。フォーマット固有の知識は、キャンペーンチームに表示するためにプレビューと配信データを解釈する際にのみ重要です。 ## 次のステップ * [オーケストレーター設計パターン](/docs/building/operating/orchestrator-design) — マルチエージェントシステムのステートマシン、永続性、リトライパターン * [クリエイティブライブラリとコンセプト](/docs/creative/creative-libraries) — 単一エージェントのライブラリでのクリエイティブ管理 * [セールスエージェントのクリエイティブ機能](/docs/creative/sales-agent-creative-capabilities) — セラーがメディアとクリエイティブの両方を管理する場合 * [生成クリエイティブ](/docs/creative/generative-creative) — AI 駆動のクリエイティブ生成ワークフロー * [仕様](/docs/creative/specification) — 完全なクリエイティブプロトコル仕様とインタラクションモデル # プライベートアセット Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/private-assets AdCP のプライベートアセットは、プレサインド URL を使用して DAM、S3 バケット、認証ソースに保存されたファイルへの一時的なアクセスを許可します。 AdCP にはアセットアップロードタスクは含まれない。クリエイティブエージェントはバイヤーに代わってファイルのアップロードを受け入れたりストレージを管理したりすることは想定されていません。代わりに、バイヤーエージェントは自分のアセットをホストし、[クリエイティブマニフェスト](/docs/creative/creative-manifests)でアクセス可能な URL を提供する責任があります。 アセットがプライベートストレージ — 内部の DAM、プライベートな S3 バケット、または認証が必要なもの — に存在する場合、バイヤーエージェントはマニフェストに URL を渡す前にそれらをアクセス可能にしなければなりません。 ## プレサインド URL 推奨されるパターンは**プレサインド URL** だ。ほとんどのクラウドストレージプロバイダーは、認証ヘッダーを必要とせずに一時的な読み取りアクセスを許可する時間制限付き URL の生成をサポートしています。 ### 仕組み 1. バイヤーエージェントがプライベートアセットを受け取るか特定する(例: S3 のブランドロゴ) 2. バイヤーエージェントが短い有効期限でプレサインド URL を生成します 3. バイヤーエージェントがクリエイティブマニフェストにプレサインド URL を渡します 4. クリエイティブエージェントが他のパブリック URL と同様にアセットをフェッチします ```json theme={null} { "format_id": { "agent_url": "https://creatives.example.com", "id": "display_static", "width": 300, "height": 250 }, "assets": { "banner_image": { "url": "https://my-bucket.s3.amazonaws.com/brand/logo.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600&X-Amz-Signature=...", "width": 300, "height": 250 }, "headline": { "content": "Spring collection" }, "clickthrough_url": { "url": "https://shop.example.com/spring" } } } ``` 標準マニフェストとの唯一の違いは URL 自体です。`banner_image.url` にはプレサインドクエリパラメーター(`X-Amz-Algorithm`、`X-Amz-Expires`、`X-Amz-Signature`)が含まれます。他のフィールドは変更なし。 ### プロバイダー例 ```javascript AWS S3 theme={null} import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; const client = new S3Client({ region: "us-east-1" }); const url = await getSignedUrl( client, new GetObjectCommand({ Bucket: "my-brand-assets", Key: "logos/primary.png", }), { expiresIn: 3600 } // 1 hour ); ``` ```javascript Google Cloud Storage theme={null} import { Storage } from "@google-cloud/storage"; const storage = new Storage(); const [url] = await storage .bucket("my-brand-assets") .file("logos/primary.png") .getSignedUrl({ action: "read", expires: Date.now() + 3600 * 1000, // 1 hour }); ``` ```javascript Cloudflare R2 theme={null} import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; const client = new S3Client({ region: "auto", endpoint: "https://.r2.cloudflarestorage.com", }); const url = await getSignedUrl( client, new GetObjectCommand({ Bucket: "my-brand-assets", Key: "logos/primary.png", }), { expiresIn: 3600 } ); ``` ```javascript Azure Blob Storage theme={null} import { BlobServiceClient, generateBlobSASQueryParameters, BlobSASPermissions, StorageSharedKeyCredential } from "@azure/storage-blob"; const credential = new StorageSharedKeyCredential(accountName, accountKey); const sasToken = generateBlobSASQueryParameters({ containerName: "brand-assets", blobName: "logos/primary.png", permissions: BlobSASPermissions.parse("r"), expiresOn: new Date(Date.now() + 3600 * 1000), // 1 hour }, credential).toString(); const url = `https://${accountName}.blob.core.windows.net/brand-assets/logos/primary.png?${sasToken}`; ``` ## 有効期限のガイドライン プレサインド URL の有効期限をワークフロー全体をカバーするのに十分な長さに設定しつつ、必要以上に長くしないようにします。 | ワークフロー | 推奨有効期限 | | --------------------------------------------------------------------------------------------------------------------------------------- | ------ | | [`build_creative`](/docs/creative/task-reference/build_creative) のみ | 1 時間 | | [`build_creative`](/docs/creative/task-reference/build_creative) + [`preview_creative`](/docs/creative/task-reference/preview_creative) | 2 時間 | | フルパイプライン(build、preview、反復、[`sync_creatives`](/docs/creative/task-reference/sync_creatives)) | 4 時間 | ## ローカルファイルのアップロード すでにクラウドストレージに存在しないアセット——ローカルファイル、Slack の添付、メールの添付——については、バイヤーエージェントはまずそれらを自身のストレージにアップロードし、それからプレサインド URL を生成すべきです。 ```javascript AWS S3 theme={null} import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; import { readFile } from "fs/promises"; const client = new S3Client({ region: "us-east-1" }); // Upload the local file const fileBuffer = await readFile("./assets/logo.png"); await client.send(new PutObjectCommand({ Bucket: "my-brand-assets", Key: "logos/primary.png", Body: fileBuffer, ContentType: "image/png", })); // Generate a presigned URL for the creative agent to fetch const url = await getSignedUrl( client, new GetObjectCommand({ Bucket: "my-brand-assets", Key: "logos/primary.png", }), { expiresIn: 3600 } ); ``` ```javascript Google Cloud Storage theme={null} import { Storage } from "@google-cloud/storage"; const storage = new Storage(); const bucket = storage.bucket("my-brand-assets"); // Upload the local file await bucket.upload("./assets/logo.png", { destination: "logos/primary.png", contentType: "image/png", }); // Generate a presigned URL for the creative agent to fetch const [url] = await bucket .file("logos/primary.png") .getSignedUrl({ action: "read", expires: Date.now() + 3600 * 1000, }); ``` ## なぜ認証ヘッダーではないのか? AdCP のマニフェストは、エージェント間で JSON として渡される宣言的なデータです。URL ごとの認証ヘッダーを追加すると、信頼境界をまたいでストレージのクレデンシャルを共有することになります——マニフェストに触れるすべてのシステム(クリエイティブエージェント、プレビューサービス、アドサーバー、ロギングインフラ)が、それらのクレデンシャルを安全に扱う必要が生じます。 プレサインド URL は、認可を URL 自体にエンコードすることでこれを避けます: * 読み取り専用アクセスで単一のオブジェクトにスコープされる * 組み込みの有効期限で時間制限される * クレデンシャルの転送が不要 * 失効は自動(URL が期限切れになる) ## 関連ドキュメント * [クリエイティブマニフェスト](/docs/creative/creative-manifests) — マニフェストの構造とアセット参照 * [アセットタイプ](/docs/creative/asset-types) — 各アセットタイプの要件 * [`build_creative`](/docs/creative/task-reference/build_creative) — マニフェストからクリエイティブを生成する * [`sync_creatives`](/docs/creative/task-reference/sync_creatives) — エージェントがホストするクリエイティブライブラリにクリエイティブを同期する # AI プロベナンスと開示 Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/provenance AdCP プロベナンスメタデータは、クリエイティブアセットとマニフェストにおける AI の関与、使用ツール、規制上の開示義務を宣言します。 プロベナンスメタデータはクリエイティブコンテンツがどのように制作されたかを宣言する — AI が関与したかどうか、どのツールが使用されたか、宣言当事者がどの開示義務が適用されると考えているか。EU AI 法やカリフォルニア州 SB 942 などの規制は、開示義務をデプロイヤーと対象プラットフォームに課します。AdCP は、それらの当事者が依拠する構造化されたプロベナンスシグナルをプログラマティックなサプライチェーンを通じて運び、すべての参加者が同じデータを宣言、転送、検証できるようにします。プロベナンスはクリエイティブマニフェスト、個々のアセット、またはコンテンツ標準アーティファクトに付与されます。これは宣言当事者による主張だ — 受信当事者は自身の検出ツールを使用して主張を独立して検証します。 EU AI 法第 50 条の施行は 2026 年 8 月から始まる。カリフォルニア州 SB 942 はすでに有効です。主要プラットフォームは今日 AI コンテンツのラベリングを義務付けています。AdCP は、これらの規制が依拠する構造化された機械可読なプロベナンスと開示のメタデータを、それ以前に標準が存在しなかったプログラマティックなサプライチェーンを通じて運びます。プロトコルはデータを動かします。法的義務は依然として、エンドユーザーへの開示を行うデプロイヤーにあります。 ## プロベナンスオブジェクト プロベナンスはクリエイティブアセット、クリエイティブマニフェスト、個々の型付きアセット、コンテンツアーティファクトに付与できるオプションのオブジェクトです。プロベナンスレベルでは必須フィールドはない — 各セクションは独立して有用です。 **スキーマ**: [provenance.json](https://adcontextprotocol.org/schemas/v3/core/provenance.json) | フィールド | 型 | 説明 | | --------------------- | ------------------ | -------------------------------------------------------------------------------------------------- | | `digital_source_type` | enum | AI 関与の IPTC 準拠分類 | | `ai_tool` | object | 使用された AI システム(`name` 必須、オプションで `version` と `provider`) | | `human_oversight` | enum | 制作プロセスにおける人間の関与レベル | | `declared_by` | object | このプロベナンス主張を付与する当事者(`role` 必須、オプションで `agent_url`) | | `declared_at` | string (date-time) | このプロベナンス主張がいつなされたか(ISO 8601)、`created_time` とは別 | | `created_time` | string (date-time) | コンテンツが作成されたとき(ISO 8601) | | `c2pa` | object | C2PA サイドカーマニフェスト参照(`manifest_url` 必須)。アドサーバーのトランスコードで壊れる——中間者を含むパイプラインには `embedded_provenance` を使う | | `embedded_provenance` | array | コンテンツストリーム*内*に埋め込まれたプロベナンスメタデータ(マニフェストラッパーまたは不可視マーカー)。トランスコードと再エンコードを生き延びる | | `watermarks` | array | 識別子またはフィンガープリントをエンコードするコンテンツウォーターマーク。知覚的な変換を生き延びる | | `disclosure` | object | 規制上の開示要件と管轄区域の詳細 | | `verification` | array | サードパーティの検証または検出結果 | | `ext` | object | 標準拡張ポイント | ### 最小限の例 ほとんどのプロベナンス宣言は1つの質問に答える: **これは AI 生成か、開示ラベルが必要か?** ```json theme={null} { "$schema": "/schemas/core/provenance.json", "digital_source_type": "trained_algorithmic_media", "disclosure": { "required": true } } ``` それだけです。`digital_source_type` はコンテンツがどのように制作されたかを示します。`disclosure.required` はラベルが必要かどうかを示します。それ以外のすべて — ツールの詳細、C2PA 参照、管轄区域固有のレンダリングガイダンス、検証結果 — は必要なときにサプライチェーン参加者が追加できるオプションのコンテキストです。 AI が関与していないコンテンツの場合、プロベナンスはさらにシンプルだ: ```json theme={null} { "$schema": "/schemas/core/provenance.json", "digital_source_type": "digital_capture" } ``` ### 完全な例 ```json theme={null} { "$schema": "/schemas/core/provenance.json", "digital_source_type": "trained_algorithmic_media", "ai_tool": { "name": "DALL-E 3", "version": "3.0", "provider": "OpenAI" }, "human_oversight": "selected", "declared_by": { "agent_url": "https://creative.pinnaclemedia.example.com", "role": "agency" }, "declared_at": "2026-02-15T14:35:00Z", "created_time": "2026-02-15T14:30:00Z", "c2pa": { "manifest_url": "https://cdn.pinnaclemedia.example.com/c2pa/manifests/hero_img_abc123.c2pa" }, "disclosure": { "required": true, "jurisdictions": [ { "country": "US", "region": "CA", "regulation": "ca_sb_942", "label_text": "Created with AI", "render_guidance": { "persistence": "flexible", "positions": ["prominent", "footer"] } }, { "country": "DE", "regulation": "eu_ai_act_article_50", "label_text": "KI-generiert", "render_guidance": { "persistence": "continuous", "positions": ["overlay", "subtitle"] } } ] }, "verification": [ { "verified_by": "Reality Defender", "verified_time": "2026-02-15T15:00:00Z", "result": "ai_generated", "confidence": 0.97, "details_url": "https://realitydefender.example.com/reports/abc123" } ] } ``` ## デジタルソースタイプ `digital_source_type` 列挙型は、[IPTC digitalsourcetype 語彙](https://cv.iptc.org/newscodes/digitalsourcetype/)に準拠したコンテンツ制作における AI 関与を分類します。 **スキーマ**: [digital-source-type.json](https://adcontextprotocol.org/schemas/v3/enums/digital-source-type.json) | 値 | 説明 | 使用場面 | | ------------------------------------------ | -------------------------------------------------------------------- | -------------------------------------------------- | | `digital_capture` | AI 関与なしにデジタルデバイス(カメラ、スキャナー、画面録画)によってキャプチャされた | プロダクト撮影の写真、アプリデモの画面録画 | | `digital_creation` | AI 生成なしにデジタルツール(Photoshop、Illustrator、After Effects)を使用して人間が作成した | 手動でデザインされたバナー広告、手動で構成されたレイアウト | | `trained_algorithmic_media` | トレーニング済み AI モデル(DALL-E、Midjourney、Stable Diffusion、Sora)によって完全に生成された | AI 生成のヒーロー画像、AI 制作のビデオスポット | | `composite_with_trained_algorithmic_media` | 人間が作成したコンテンツと AI 生成要素を組み合わせた | AI 生成バックグラウンドのプロダクト写真、AI 視覚効果の人間撮影ビデオ | | `algorithmic_media` | 機械学習なしの決定論的アルゴリズム(手続き型生成、ルールベースシステム)によって制作された | プログラマティックビジュアライゼーション、手続き型パターン生成 | | `composite_capture` | AI なしに複数のデジタルキャプチャを合成した | パノラマステッチング、多重露光 HDR 合成 | | `composite_synthetic` | 少なくとも1つが AI 生成の複数要素の合成 | AI 生成バックグラウンドに合成されたストック写真、キャプチャビデオへの AI テキストオーバーレイ | | `human_edits` | 非生成ツールを使用して人間がコンテンツを補強、修正、または強化した | カラー補正されたプロダクト写真、手動でレタッチされたポートレート、人間によるコピー編集 | | `data_driven_media` | 構造化データフィードから組み立てられた(DCO テンプレート、プロダクトカタログ、気象トリガーバリアント) | ダイナミッククリエイティブ最適化、カタログ駆動プロダクトカルーセル、気象応答型広告 | ### 適切な値の選び方 混合制作のクリエイティブでは、プロベナンスが付与されているレベルで**クリエイティブ全体**を最もよく説明する値を選ぶ。アセットごとに AI 関与を区別する必要がある場合は、代わりに個々のアセットレベルでプロベナンスを付与する(下記[継承](#inheritance)を参照)。 一般的なパターン: * **AI 画像 + 人間のコピー**: 画像アセットに `trained_algorithmic_media` を付与し、テキストアセットに `digital_creation` を付与し、マニフェストレベルに `composite_with_trained_algorithmic_media` を付与します * **AI 生成ヘッドラインの DCO**: マニフェストレベルで `data_driven_media`、AI 生成テキストアセットに `trained_algorithmic_media` * **人間の写真家 + AI 背景除去**: マニフェストレベルで `composite_with_trained_algorithmic_media` ## ヒューマンオーバーサイト `human_oversight` 列挙型は、AI 支援制作プロセスにおける人間の関与レベルを説明します。 | 値 | 説明 | | ------------- | ------------------------------------ | | `none` | 生成に人間の関与なしに完全自動化 | | `prompt_only` | 人間がプロンプトまたは指示を提供したが出力をレビューしなかった | | `selected` | 人間が複数の AI 生成候補から選択した | | `edited` | 人間が AI 生成出力を編集または改良した | | `directed` | 人間が AI を補助ツールとして使用してクリエイティブプロセスを指揮した | このフィールドは `digital_source_type` が AI 関与を示す場合に関連します。AI 非関与コンテンツの場合は省略します。 `human_oversight` と `disclosure.required` は独立したフィールドであり、プロトコルは一方から他方を導出しません。一部の規制は、人間が編集した、または人間が指揮した AI 出力を開示義務から除外します(例: 人間が編集責任を負う EU AI 法第 50 条 (4))。プロトコルはその判断を宣言当事者の法的分析に委ねます。`human_oversight: edited` や `directed` を主張しても、それ自体では `disclosure.required: false` を正当化しません——除外にはスキーマが評価できない事実上の前提条件があります。セラーとガバナンスエージェントは、この組み合わせを監査に値する主張として扱い、宣言当事者からの裏付けとなる証拠を要求してもかまいません。 ガバナンスエージェントは、この監査に値するパターンを `get_creative_features.audit_observations[]` を通じて `OVERSIGHT_DISCLOSURE_CARVEOUT_CLAIMED` で表面化します。その観測はそれ自体では拒否コードではありません——クリエイティブが通常のレビューを進む間、セラーが監査のために主張を保持またはルーティングできるようにするものです。

継承

プロベナンスはクリエイティブ階層の3つのレベルに付与されます。最も具体的なプロベナンスが優先され、置換は**オブジェクト全体** — フィールドレベルのマージは行われない。 ``` creative-asset.provenance (1) ライブラリのクリエイティブのデフォルト creative-manifest.provenance (2) このマニフェストのデフォルト individual asset .provenance (3) 特定アセットのオーバーライド ``` ### 解決ルール 1. 個々のアセットに `provenance` がある場合、それを使用します 2. そうでなければ、マニフェストに `provenance` がある場合、それを使用します 3. そうでなければ、クリエイティブアセットに `provenance` がある場合、それを使用します 4. そうでなければ、そのアセットのプロベナンスは宣言されていません ### 例: 混合クリエイティブ 画像が AI 生成だがコピーが人間が書いたクリエイティブ。マニフェストレベルのプロベナンスがクリエイティブ全体をカバーします。画像アセットが独自のより具体的なプロベナンスでオーバーライドします。 ```json theme={null} { "$schema": "/schemas/core/creative-manifest.json", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "provenance": { "digital_source_type": "composite_with_trained_algorithmic_media", "declared_by": { "role": "agency" } }, "assets": { "banner_image": { "asset_type": "image", "url": "https://cdn.novabrands.example.com/hero_ai.jpg", "width": 300, "height": 250, "provenance": { "digital_source_type": "trained_algorithmic_media", "ai_tool": { "name": "DALL-E 3", "version": "3.0", "provider": "OpenAI" }, "human_oversight": "selected", "declared_by": { "role": "agency" }, "c2pa": { "manifest_url": "https://cdn.novabrands.example.com/c2pa/hero_ai.c2pa" } } }, "headline": { "asset_type": "text", "content": "Nutrition dogs love" }, "clickthrough_url": { "asset_type": "url", "url": "https://novabrands.example.com/products?campaign={MEDIA_BUY_ID}" } } } ``` この例では: * `banner_image` は独自のプロベナンスを使用します: `trained_algorithmic_media` と完全な AI ツールの詳細 * `headline` はマニフェストレベルのプロベナンスを継承する: `composite_with_trained_algorithmic_media` * `clickthrough_url` もマニフェストレベルのプロベナンスを継承します 画像のプロベナンスは完全な置換であることに注意。マニフェストレベルのプロベナンスに `declared_by` があっても、その情報を引き継ぐべき場合は画像アセットが独自のプロベナンスオブジェクトで再宣言しなければなりません。 ### アーティファクトの継承 コンテンツアーティファクト(パブリッシャーコンテンツ)の場合も同じパターンが適用されます: ``` artifact.provenance (1) アーティファクトのデフォルト artifact.assets[].provenance (2) 特定のインラインアセットのオーバーライド ``` ```json theme={null} { "$schema": "/schemas/content-standards/artifact.json", "property_rid": "01916f3a-a1d3-7000-8000-000000000030", "artifact_id": "article_ai_trends_2026", "provenance": { "digital_source_type": "digital_creation", "declared_by": { "role": "platform" } }, "assets": [ { "type": "text", "role": "title", "content": "AI trends reshaping the industry in 2026" }, { "type": "image", "url": "https://cdn.aimagazine.example.com/illustration.jpg", "alt_text": "Conceptual illustration of neural networks", "provenance": { "digital_source_type": "trained_algorithmic_media", "ai_tool": { "name": "Midjourney", "version": "v7" }, "human_oversight": "directed", "declared_by": { "role": "platform" } } } ] } ``` 記事のテキストはアーティファクトから `digital_creation` を継承します。イラストは独自の `trained_algorithmic_media` プロベナンスでオーバーライドします。 ## トラストモデル プロベナンスは宣言当事者による**主張**だ。証明ではありません。執行当事者は独立して検証するべきです。 広告において、プロベナンスを宣言する当事者と執行する当事者は競合する動機を持ちます。クリエイティブを提出するバイヤーは、コンテンツが人間制作だと主張する理由がある — AI 生成クリエイティブは特定のインベントリでプレースメント制限、必須開示ラベル、または完全な拒否に直面する可能性があります。そのクリエイティブを受け入れるセラーは逆の動機を持ちます: 適切な開示なしに AI 生成コンテンツを公開すると、広告主ではなくパブリッシャーに規制上の責任が生じる。AdCP はプロベナンスを事実ではなく主張として扱うことでこの緊張を処理します。バイヤーが宣言し、セラーが検証します。検証は各執行ポイントで独立して行われ、AI 検出サービス(`get_creative_features` 経由)、C2PA マニフェスト検証、または両方を使用します。どの当事者も他の当事者の主張を信頼する必要はない。プロトコルは主張のための構造と検証のための統合ポイントを提供する — サプライチェーンは両者を誠実に保つ敵対的な圧力を提供します。 `declared_by` フィールドはプロベナンス主張を付与した者を識別します。`verification` 配列は宣言当事者が透明性のために開示したい検出結果を保持します。しかし、プロベナンス要件を執行する当事者は、バイヤーが付与した結果を信頼するのではなく、既存のガバナンスインフラを通じて独自の検証を実行します。 ```mermaid theme={null} sequenceDiagram participant Buyer as Buyer Agent participant Seller as Seller Agent participant Detector as AI Detection Agent Note over Buyer: 1. DECLARATION Buyer->>Seller: sync_creatives with provenance
digital_source_type: "digital_capture" Note over Seller: 2. POLICY CHECK Seller->>Seller: creative_policy.provenance_required = true
Provenance present? Yes Note over Seller: 3. INDEPENDENT VERIFICATION Seller->>Detector: get_creative_features
(creative manifest) Detector-->>Seller: feature_id: "ai_generated"
value: true, confidence: 0.94 Note over Seller: 4. ENFORCEMENT Seller->>Seller: Buyer claims "digital_capture"
Detection says "ai_generated"
Mismatch -- reject creative Seller-->>Buyer: Creative rejected:
"AI detection contradicts provenance claim" ``` ### 宣言当事者の役割 | 役割 | 説明 | | ------------ | ------------------------------ | | `creator` | コンテンツを作成または生成した当事者 | | `advertiser` | コンテンツを所有するブランドまたは広告主 | | `agency` | 広告主の代理で行動するエージェンシー | | `platform` | コンテンツを処理した広告プラットフォームまたはパブリッシャー | | `tool` | プロベナンスメタデータを付与した自動化ツールまたはサービス | ### バイヤーが付与する検証 プロベナンスオブジェクトの `verification` 配列は、宣言当事者が透明性のために検出結果を共有できるようにします。複数のサービスが同じコンテンツを独立して評価できる: ```json theme={null} { "verification": [ { "verified_by": "Hive Moderation", "verified_time": "2026-02-15T15:00:00Z", "result": "ai_generated", "confidence": 0.96, "details_url": "https://hive.example.com/reports/abc123" }, { "verified_by": "Reality Defender", "verified_time": "2026-02-15T15:05:00Z", "result": "ai_generated", "confidence": 0.93 } ] } ``` これらの結果は**補足的なもの**だ。プロベナンス検証を要求するセラーは、バイヤーが付与した結果を信頼するのではなく、[`get_creative_features`](/docs/governance/creative/get_creative_features) を通じて独自の検出を実行します。 検証結果は4つの結果のいずれかを使用します: | 結果 | 説明 | | -------------- | --------------------------------------------- | | `authentic` | AI 非生成として検証されたコンテンツ | | `ai_generated` | AI 生成として検出されたコンテンツ | | `ai_modified` | AI 変更として検出されたコンテンツ(AI 改変を加えたオリジナルの非 AI コンテンツ) | | `inconclusive` | 検出が確信を持った判断に達することができなかった | ### 例: キャンペーンを通じたプロベナンス Acme Brands が春のキャンペーンを実施しています。エージェンシーの Meridian Media が AI 画像ジェネレーターを使用してディスプレイバナーセット — AI 生成バックグラウンドのフォトリアリスティックなプロダクトショット — を制作します。Meridian はクリエイティブマニフェストにプロベナンスを付与する: `digital_source_type` は `composite_with_trained_algorithmic_media`、`ai_tool` がジェネレーターを識別し、`disclosure.required` は `true` で適用規制として `eu_ai_act_article_50` と `ca_sb_942` がリストされます。EU 管轄区域では、Meridian は `render_guidance.persistence` を `continuous` に設定し `positions` は `overlay` を優先する — EU AI 法の継続的なラベリング要件を表現しています。 キャンペーンは AdCP を通じて Pinnacle Publishing に提出されます。Pinnacle の広告運用プラットフォームがプロベナンス主張を確認し、`get_creative_features` を通じてクリエイティブを検証パイプラインで実行します。AI 検出サービスは 0.94 の信頼度で `ai_modified` を返す — 宣言されたソースタイプと一致しています。主張が検証されると、Pinnacle は自身の管轄区域ポリシーを適用します——SB 942 の下での対象プラットフォームとして、Meridian の `disclosure.required: true` を権威あるものとして依拠するのではなく、自身の開示判断を下します——配信管轄区域のレンダリングガイダンスを読み取り、指定された持続性で開示ラベルを適用し、クリエイティブの配信をクリアします。プロベナンスメタデータ、検出結果、レンダリングガイダンス、開示決定がすべて記録され監査可能になります。 Meridian が代わりに `digital_capture` と宣言していた場合 — AI 関与なしと主張 — Pinnacle の検出サービスが不一致にフラグを立てていました。クリエイティブは配信されずレビューのために保留されていました。 ## C2PA インテグレーション `c2pa` フィールドは、[C2PA Content Credentials](https://c2pa.org/) — Coalition for Content Provenance and Authenticity が開発した暗号プロベナンス標準 — へのソフト参照を提供します。 ```json theme={null} { "c2pa": { "manifest_url": "https://cdn.acmecorp.example.com/c2pa/manifests/hero_abc123.c2pa" } } ``` ### なぜ URL 参照か C2PA バインディングは通常、メディアファイル自体に埋め込まれる。しかし、広告技術パイプラインはクリエイティブアセットを日常的にトランスコード、リサイズ、再フォーマットし、その過程でファイルレベルの C2PA バインディングを壊す。元の C2PA マニフェストストアへの URL 参照はこのトランスコーディングを生き残り、サプライチェーンを通じたプロベナンスのチェーンを保持します。 参照はポインターであり、C2PA の置き換えではありません。チェーン内のどの当事者も、メディアファイルがトランスコードされた後でも、URL からマニフェストをフェッチして元のコンテンツ資格情報を検証できます。 ### 使用パターン 1. クリエイターがコンテンツを生成し、C2PA マニフェストを作成します 2. クリエイターがマニフェストストアを安定した URL にアップロードします 3. クリエイターが AdCP プロベナンスに `manifest_url` を付与します 4. 下流の当事者(エージェンシー、プラットフォーム、セラー)はいつでもマニフェストをフェッチして元の資格情報を検証できます ## 開示要件 `disclosure` オブジェクトは AI 生成コンテンツの規制上の義務を宣言します。 ```json theme={null} { "disclosure": { "required": true, "jurisdictions": [ { "country": "US", "region": "CA", "regulation": "ca_sb_942", "label_text": "Created with AI", "render_guidance": { "persistence": "flexible", "positions": ["prominent", "footer"] } }, { "country": "DE", "regulation": "eu_ai_act_article_50", "label_text": "KI-generiert", "render_guidance": { "persistence": "continuous", "positions": ["overlay", "subtitle"] } }, { "country": "CN", "regulation": "cn_deep_synthesis", "label_text": "AI-generated content", "render_guidance": { "persistence": "initial", "min_duration_ms": 3000, "positions": ["overlay", "pre_roll"] } } ] } } ``` | フィールド | 必須 | 説明 | | ------------------------------------------------- | --- | -------------------------------------------------------- | | `required` | Yes | 適用規制に基づいて AI 開示が必要かどうか | | `jurisdictions` | No | 開示義務が適用される管轄区域の配列 | | `jurisdictions[].country` | Yes | ISO 3166-1 alpha-2 国コード | | `jurisdictions[].region` | No | 地域コード(例: カリフォルニア州の場合 `CA`) | | `jurisdictions[].regulation` | Yes | 規制識別子 | | `jurisdictions[].label_text` | No | 現地語での必須開示ラベルテキスト | | `jurisdictions[].render_guidance` | No | この管轄区域で開示をどのようにレンダリングすべきか | | `jurisdictions[].render_guidance.persistence` | No | 開示がどのくらい持続しなければなりませんか: `continuous`、`initial`、`flexible` | | `jurisdictions[].render_guidance.min_duration_ms` | No | ミリ秒単位の最小表示時間(`initial` 持続性の必須コンテキスト) | | `jurisdictions[].render_guidance.positions` | No | 優先順位付きの推奨開示位置(最初にサポートされるものが使用されます) | ### レンダリングガイダンス 各管轄区域の `render_guidance` オブジェクトは、規制の要件に基づいて開示をどのようにレンダリングすべきかについての宣言当事者の意図を表現します。規制によって持続性の要件が異なる: * **`continuous`** — 開示はコンテンツ表示の全期間を通じて視覚的または聴覚的に存在し続けなければなりません。ビデオ/オーディオの場合は全再生時間。静的フォーマット(ディスプレイ、DOOH)の場合は全表示スロット。DOOH では「コンテンツ時間」はスクリーンのフルローテーションサイクルではなく、ローテーション内の広告の表示スロットを意味します。 * **`initial`** — 開示は削除される前に最小時間、開始時に表示されなければなりません。`min_duration_ms` と組み合わせて時間を指定する — それなしでは、時間はパブリッシャーの裁量に委ねられます。 * **`flexible`** — 開示の存在で十分。パブリッシャーがタイミングと時間を制御します。 同じ管轄区域に複数のソースが持続性を指定する場合(例: `required_disclosures[].persistence` とプロベナンス `render_guidance.persistence`)、最も制限的なモードが適用されます: `continuous` > `initial` > `flexible`。 `positions` 配列は順序付き優先リストです。配信フォーマットがサポートする最初の位置が使用されるべきです。例えば、`["overlay", "subtitle"]` は「オーバーレイを優先し、オーバーレイが利用できない場合はサブタイトルにフォールバックする」を意味します。 すべての位置と持続性の組み合わせが意味のあるわけではありません。本質的に持続時間が限定される位置 — `end_card`、`pre_roll` — はコンテンツの一部にのみ表示されるため、`continuous` 持続性を満たすことができません。クリエイティブエージェントはこれらの位置で `continuous` を要求すべきではなく、フォーマットはそれらの `continuous` サポートを主張すべきではありません。 オーディオのみの環境(ポッドキャスト、ストリーミングオーディオ、スマートスピーカー)では、`audio`、`pre_roll`、`companion` の位置のみが適用されます。視覚的な位置(`overlay`、`footer`、`subtitle`)はスクリーンなしでは未定義です。オーディオフォーマット用に構築するクリエイティブエージェントは `render_guidance.positions` をオーディオ互換の値に限定すべきです。 レンダリングガイダンスはクリエイティブとともにサプライチェーンを通じて移動します。配信時に、パブリッシャーはプロベナンスからガイダンスを読み取り、それに応じてレンダリングします。ガバナンスエージェントはパブリッシャーが宣言されたガイダンスに従ったかどうかを監査できます。 ### マルチアセット集計 DCO で一般的な、同じ管轄区域に対して異なる `render_guidance` を持つ複数のアセットからクリエイティブが組み立てられる場合、最も制限的な持続性が組み立てられたクリエイティブ全体に適用されます: いずれかのアセットが `continuous` を必要とする場合、組み立てられたクリエイティブも `continuous` を必要とします。これは競合解決と同じ優先順位に従う: `continuous` > `initial` > `flexible`。 ### 執行と自己報告コンプライアンス パブリッシャーがレンダリングサーフェスを制御するフォーマット(ホスト型ビデオ、ディスプレイバナー、SSAI)では、パブリッシャーはレンダリングガイダンスを直接執行できる — オーバーレイをレンダリングし、その時間を制御し、コンプライアンスを検証します。 不透明な自己レンダリングクリエイティブ(MRAID、JavaScript タグ、VPAID)では、クリエイティブが独自のビューポートを制御します。パブリッシャーはクリエイティブのサンドボックス内に開示レンダリングを注入または執行できません。この場合、開示コンプライアンスはビルド中にクリエイティブエージェントが開示を埋め込むことに依存します。フォーマットの `disclosure_capabilities` はこれを反映すべきだ: フォーマットのレンダリング層が検証または執行できる持続性モードのみを主張し、クリエイティブの自己コンプライアンスに依存するモードは主張しません。ガバナンスエージェントは、ヘッドレス環境でクリエイティブをレンダリングして開示の存在を検査することにより、`get_creative_features` を通じて事後に自己レンダリングされた開示を検証できます。 ### 既知の規制識別子 | 識別子 | 規制 | ステータス | | ---------------------- | --------------- | -------------- | | `eu_ai_act_article_50` | EU AI 法第 50 条 | 2026 年 8 月施行 | | `ca_sb_942` | カリフォルニア州 SB 942 | 2026 年 1 月より有効 | | `cn_deep_synthesis` | 中国ディープシンセシス規定 | 有効 | 規制識別子は慣例であり、クローズドな列挙型ではありません。プロトコル変更なしに新しい規制を参照できます。 ## 埋め込みプロベナンスとウォーターマーク `c2pa.manifest_url` はサイドカー参照です: 分離された暗号マニフェストへの URL ポインタ。ネットワークを越えて耐久性がありますが、ファイルレベルの C2PA バインディングはアドサーバーのトランスコード、リサイズ、再エンコードを生き延びません。アセットがパブリッシャーに到達する前に中間者を通過するパイプラインでは、トランスコード耐性のある二つのフィールドがアセット自体の内側にプロベナンスの証拠を運びます。 ### `embedded_provenance[]` コンテンツストリーム*内*に運ばれるプロベナンスメタデータ——ファイルコンテナに埋め込まれたマニフェスト(例: JPEG の JUMBF ボックス、C2PA セクション A.7 に従う平文の C2PATextManifestWrapper)として、またはプロベナンスレコードをエンコードまたは参照するコンテンツ内の不可視マーカーとして。 ```json theme={null} { "embedded_provenance": [ { "method": "provenance_markers", "provider": "Encypher", "verify_agent": { "agent_url": "https://governance.encypher.example", "feature_id": "encypher.markers_present" }, "embedded_at": "2026-04-30T10:15:00Z" } ] } ``` | フィールド | 必須 | 説明 | | -------------- | --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `method` | Yes | `manifest_wrapper` または `provenance_markers`([`embedded-provenance-method`](https://adcontextprotocol.org/schemas/v3/enums/embedded-provenance-method.json) を参照) | | `provider` | Yes | 埋め込みを実行した組織(表示ラベルであり、ワイヤー識別子ではない) | | `standard` | No | 埋め込みが準拠する標準(例: `c2pa`) | | `verify_agent` | No | `get_creative_features` を通じてこの埋め込みを検証できる AdCP ガバナンスエージェント | | `embedded_at` | No | 埋め込みが適用されたとき(ISO 8601) | `verify_agent` は、受信者が自己検証できないメソッド(例: `provenance_markers`)では存在すべきです(SHOULD)。受信者がすでに信頼する公開鍵を持つ C2PA テキストマニフェストのような自己検証可能な埋め込みでは省略してもよい(MAY)。 ### `watermarks[]` アセット内に識別子またはフィンガープリントをエンコードするコンテンツウォーターマーク。埋め込みプロベナンスとは異なります: ウォーターマークは識別子(誰が生成したか、誰が所有するか)を運び、埋め込みプロベナンスは構造化されたプロベナンスレコード(完全な管理の連鎖)を運ぶか参照します。単一のアセットは両方を運べます。 ```json theme={null} { "watermarks": [ { "media_type": "video", "provider": "Imatag", "verify_agent": { "agent_url": "https://governance.imatag.example", "feature_id": "imatag.watermark_detected" }, "c2pa_action": "c2pa.watermarked.unbound" } ] } ``` | フィールド | 必須 | 説明 | | -------------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `media_type` | Yes | `audio`、`image`、`video`、または `text`([`watermark-media-type`](https://adcontextprotocol.org/schemas/v3/enums/watermark-media-type.json) を参照) | | `provider` | Yes | ウォーターマークを適用した組織(表示ラベルであり、ワイヤー識別子ではない) | | `verify_agent` | No | `get_creative_features` を通じてこのウォーターマークを検出できる AdCP ガバナンスエージェント | | `c2pa_action` | No | C2PA アクション分類: `c2pa.watermarked.bound` または `c2pa.watermarked.unbound`([`c2pa-watermark-action`](https://adcontextprotocol.org/schemas/v3/enums/c2pa-watermark-action.json) を参照) | | `embedded_at` | No | ウォーターマークが適用されたとき(ISO 8601) | ### `verify_agent` の形状 `verify_agent` は、このレイヤーがセラーがすでに受け入れているガバナンスエージェントによって検証できる、というバイヤーの*表明*です。セラーは記録上の検証者です: 呼び出すエージェントを `creative_policy.accepted_verifiers[]`(`get_products` が返す)に公開し、バイヤーの `verify_agent.agent_url` はそれらのエントリの一つと正規化された一致でなければなりません(MUST)。リスト外の URL は、いかなるアウトバウンド呼び出しの前に `PROVENANCE_VERIFIER_NOT_ACCEPTED` で拒否されます。 これはバイヤー提供の証拠であって、バイヤー主導のルーティングではありません。セラーは実際にどのエージェントを呼ぶかを選び、バイヤーが指名したのとは異なるオンリストのエージェントを代わりに使ってもよい。許可リスト外のバイヤー主張のエンドポイントは呼びません。 | フィールド | 必須 | 説明 | | ------------ | --- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `agent_url` | Yes | バイヤーが使われたと表明するガバナンスエージェントの URL。`https://` を使わなければならない(MUST)。セラーの `creative_policy.accepted_verifiers[].agent_url` の値の一つと(正規化して)一致しなければならない(MUST) | | `feature_id` | No | バイヤーが使われたと表明する機能 ID。対応する `accepted_verifiers[]` エントリで宣言された `feature_id` と一致すべき(SHOULD)か、セラーに委ねるために省略する | バイヤーは、自己検証可能な埋め込み(例: セラーがすでに信頼する公開鍵を持つ C2PA テキストマニフェスト)については `verify_agent` を省略してもよい(MAY)——その場合、セラーは評価時に `accepted_verifiers` からエージェントを選択します。 継承は親の `provenance` オブジェクトと同じルールに従います: 最も具体的なものが優先され、置換はオブジェクト全体で、フィールドレベルのマージはありません。 ## クリエイティブポリシー執行 セラーは `creative-policy` でプロベナンス要件を表現します——すべてのプロダクトのフィールドで、`get_products` を通じて表面化されるため、バイヤーは提出前に要件を把握できます。 ```json theme={null} { "$schema": "/schemas/core/creative-policy.json", "co_branding": "optional", "landing_page": "any", "templates_available": false, "provenance_required": true, "provenance_requirements": { "require_digital_source_type": true, "require_disclosure_metadata": true, "require_embedded_provenance": true }, "accepted_verifiers": [ { "agent_url": "https://governance.encypher.seller.example", "feature_id": "encypher.markers_present_v2", "providers": ["Encypher"] }, { "agent_url": "https://governance.imatag.seller.example", "feature_id": "imatag.watermark_detected", "providers": ["Imatag"] } ] } ``` `provenance_required: true` は、クリエイティブが継承チェーンのどこかに*何らかの*プロベナンスオブジェクトを運ばなければならないことを意味します。`provenance_requirements` はそれをフィールドレベルの期待で洗練します。`accepted_verifiers[]` は、セラーが運用または許可リストに登録したガバナンスエージェントを公開します——セラーは記録上の検証者であり、バイヤーの `verify_agent` の参照はこれらの `agent_url` の値の一つと正規化された一致でなければなりません(MUST)。フィールドレベルの要件はセラーが強制し、JSON スキーマはそれらを検証しません。 要件を公開するセラーはそれを強制しなければなりません(MUST)。`sync_creatives` は準拠しない提出を次のいずれかで拒否します: | エラーコード | トリガー | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `PROVENANCE_REQUIRED` | クリエイティブのどこにもプロベナンスオブジェクトがない | | `PROVENANCE_DIGITAL_SOURCE_TYPE_MISSING` | 解決されたプロベナンスに `digital_source_type` がない | | `PROVENANCE_DISCLOSURE_MISSING` | 解決されたプロベナンスに `disclosure.required` がない、または true だが管轄区域がない | | `PROVENANCE_EMBEDDED_MISSING` | 解決されたプロベナンスに `embedded_provenance[]` エントリがない | | `PROVENANCE_VERIFIER_NOT_ACCEPTED` | `verify_agent.agent_url` がセラーの `accepted_verifiers` リストにない(いかなるアウトバウンド呼び出しの前にクロスチェック) | | `PROVENANCE_CLAIM_CONTRADICTED` | 検証者(`accepted_verifiers` から呼ばれた)がバイヤーの主張を積極的に反証する——例: バイヤーが `digital_source_type: digital_capture` を主張するが、AI 検出機能が `ai_generated: true` を返す | `error.field` は、検査された解決済みのプロベナンスのパスを指さなければなりません(MUST)。`PROVENANCE_CLAIM_CONTRADICTED` の `error.details` は、監査上安全な許可リスト `{ agent_url, feature_id, claimed_value, observed_value, confidence }` に加えて、セラーがバイヤーの指名したのとは別のオンリストエージェントを呼んだ場合の `substituted_for` に限定されます——セラーは任意の検証者拡張フィールド、`detail_url`、またはテナントをまたぐデータや PII を運ぶ可能性のある検証者レスポンス形状を転送してはなりません(MUST NOT)。 これらのコードは訂正可能です: バイヤーのオーケストレーターがそれらを読み、クリエイティブを修正し、再提出します。訂正なしの自動リトライは通りません。 ## プロベナンスが付与される場所 | スキーマ | フィールド | 説明 | | ------------------------------------------- | ------------ | ------------------------ | | `creative-asset` | `provenance` | ライブラリのクリエイティブのデフォルト | | `creative-manifest` | `provenance` | このマニフェスト内のすべてのアセットのデフォルト | | `image-asset` | `provenance` | 特定の画像のオーバーライド | | `video-asset` | `provenance` | 特定のビデオのオーバーライド | | `audio-asset` | `provenance` | 特定のオーディオファイルのオーバーライド | | `text-asset` | `provenance` | 特定のテキストコンテンツのオーバーライド | | `html-asset` | `provenance` | HTML コンテンツのオーバーライド | | `css-asset` | `provenance` | CSS コンテンツのオーバーライド | | `javascript-asset` | `provenance` | JavaScript コンテンツのオーバーライド | | `vast-asset` | `provenance` | VAST タグのオーバーライド | | `daast-asset` | `provenance` | DAAST タグのオーバーライド | | `url-asset` | `provenance` | URL アセットのオーバーライド | | `artifact` | `provenance` | コンテンツアーティファクトのデフォルト | | `artifact.assets[]`(text、image、video、audio) | `provenance` | 特定のインラインアセットのオーバーライド | ## スキーマリファレンス | スキーマ | 場所 | | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | プロベナンスオブジェクト | [`/schemas/core/provenance.json`](https://adcontextprotocol.org/schemas/v3/core/provenance.json) | | クリエイティブポリシー | [`/schemas/core/creative-policy.json`](https://adcontextprotocol.org/schemas/v3/core/creative-policy.json) | | デジタルソースタイプ列挙型 | [`/schemas/enums/digital-source-type.json`](https://adcontextprotocol.org/schemas/v3/enums/digital-source-type.json) | | 埋め込みプロベナンスメソッド列挙型 | [`/schemas/enums/embedded-provenance-method.json`](https://adcontextprotocol.org/schemas/v3/enums/embedded-provenance-method.json) | | ウォーターマークメディアタイプ列挙型 | [`/schemas/enums/watermark-media-type.json`](https://adcontextprotocol.org/schemas/v3/enums/watermark-media-type.json) | | C2PA ウォーターマークアクション列挙型 | [`/schemas/enums/c2pa-watermark-action.json`](https://adcontextprotocol.org/schemas/v3/enums/c2pa-watermark-action.json) | | エラーコード列挙型(プロベナンスコード) | [`/schemas/enums/error-code.json`](https://adcontextprotocol.org/schemas/v3/enums/error-code.json) | | クリエイティブアセット(プロベナンス付き) | [`/schemas/core/creative-asset.json`](https://adcontextprotocol.org/schemas/v3/core/creative-asset.json) | | クリエイティブマニフェスト(プロベナンス付き) | [`/schemas/core/creative-manifest.json`](https://adcontextprotocol.org/schemas/v3/core/creative-manifest.json) | | クリエイティブポリシー(provenance\_required) | [`/schemas/core/creative-policy.json`](https://adcontextprotocol.org/schemas/v3/core/creative-policy.json) | | アーティファクト(プロベナンス付き) | [`/schemas/content-standards/artifact.json`](https://adcontextprotocol.org/schemas/v3/content-standards/artifact.json) | ## 関連ドキュメント * [プロベナンス検証](/docs/governance/creative/provenance-verification) — ガバナンスインフラが AI プロベナンス主張を検証する方法 * [クリエイティブガバナンス](/docs/governance/creative/index) — `get_creative_features` を通じた機能ベースのクリエイティブ評価 * [コンテンツ標準](/docs/governance/content-standards/index) — パブリッシャーコンテンツのプライバシー保護ブランド適合性 * [生成クリエイティブ](/docs/creative/generative-creative) — `build_creative` による AI 駆動のクリエイティブ生成 # 公開投稿参照クリエイティブ Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/published-post-reference-creatives 製品が既存の投稿をブーストまたはスポンサーするとき、published_post 参照アセットで正準フォーマットを使う。 # 公開投稿参照クリエイティブ 公開投稿参照クリエイティブは、セラーがプラットフォーム認可とレビューの後に解決してサーブする既存の投稿です。それらは新しい `format_kind` を必要としません: クリエイティブ形状は依然として `video_hosted`、`image`、または `native_in_feed` です。違いは、バイヤーがアップロードされたメディアバイトの代わりに `published_post` 参照を出荷することです。 このパターンは、ソースオブジェクトがパブリッシャー、クリエイター、またはソーシャルプラットフォームに既に存在するブースト/スポンサー投稿製品に使います。カタログ駆動リテールメディアには使わないでください。リテールメディアは `source_catalog` スロットを持つ `sponsored_placement` のままです。 ## 製品宣言 製品は正準フォーマットを宣言し、`asset_source: "publisher_owned_reference"` を設定し、適切なときアップロードされたバイトを拒否し、`published_post` スロットを使います。 ```json theme={null} { "format_kind": "video_hosted", "format_option_id": "shortloop_reference_video", "canonical_formats_only": true, "params": { "asset_source": "publisher_owned_reference", "buyer_asset_acceptance": "rejected", "reference_mutability": "mutable_requires_reapproval", "required_connections": [ { "provider": "social.example", "connection_type": "advertiser_account", "required_for": ["sync_creatives", "create_media_buy"], "scope": "account" }, { "provider": "social.example", "connection_type": "publisher_identity", "required_for": ["list_creatives", "sync_creatives", "create_media_buy"], "scope": "identity", "authorization_instructions": "Connect the creator or page that owns the source post." } ], "orientation": "vertical", "slots": [ { "asset_group_id": "published_post", "asset_type": "published_post", "required": true }, { "asset_group_id": "primary_text", "asset_type": "text", "required": false, "max_chars": 150 }, { "asset_group_id": "landing_page_url", "asset_type": "url", "required": false } ] } } ``` `asset_source` は情報的です。バインディングコントラクトは依然として `params.slots[]` です: バイヤーは `video` アップロードではなく `published_post` アセットを提出しなければならないことを知ります。 ## 下流接続 公開投稿製品はしばしば 1 つ以上の下流プラットフォーム付与を必要とします。短編動画プラットフォームは、例えば広告を買うための advertiser account 接続と、ソース投稿を所有するクリエイター、ページ、またはプロフィールのための別の publisher identity 接続を要求するかもしれません。 AdCP はそれらを複数の AdCP 認証としてモデル化しません。呼び出し元はセラーに一度認証します。セラーは下流接続を保存して使い、バイヤーに代わってプラットフォームを呼び、それらの要件を `params.required_connections[]` で宣言します。 プラットフォーム付与を区別するには `connection_type` を使います: | Connection type | Meaning | | -------------------- | ----------------------------------------------------- | | `advertiser_account` | 広告を買う、管理する、またはレポートするために使われるプラットフォームアカウント。 | | `publisher_identity` | 公開投稿を所有するクリエイター、ページ、チャネル、組織、またはプロフィール。 | | `post_authorization` | プラットフォームが所有アイデンティティの代わりに、または加えて個別の投稿を認可するときの投稿スコープ付与。 | それらの下流付与の 1 つが欠けている、期限切れ、または失効しているため呼び出しが進めない場合、`error.details.missing_connections[]` を伴う `AUTHORIZATION_REQUIRED` を返します。各ブロックされたエントリーは、バイヤーが人間を正しいプロバイダー固有の接続フローにルーティングできるよう、`provider` または `authorization_url` のいずれかを含まなければなりません。エントリーは `authorization_instructions` と、投稿 URL やプロフィール URL のような安全な `resource_ref` ヒントも運べます。 `required_for` は、広範なカテゴリーではなく具体的な AdCP 操作名を運びます。バイヤーエージェントが接続プロンプトを実行しようとする操作にルーティングできるよう、`list_creatives`、`sync_creatives`、`create_media_buy`、`get_media_buy_delivery`、`get_creative_delivery` のような値を優先します。 マニフェストが `post_url` なしで `platform_post_id` を使うとき、選択された製品またはフォーマットオプションが既にプラットフォームを絞らない限り `platform` を含めます。素のプラットフォームネイティブ id はそうでなければプロバイダー名前空間全体で曖昧です。 ## クリエイティブマニフェスト クリエイティブマニフェストは、他の任意の 3.1 クリエイティブと同じ正準パスを使います。唯一の新しい部分はアセットペイロードです。 ```json theme={null} { "format_kind": "video_hosted", "format_option_ref": { "scope": "product", "format_option_id": "shortloop_reference_video" }, "assets": { "published_post": { "asset_type": "published_post", "post_url": "https://social.example/@acme/post/12345" }, "primary_text": { "asset_type": "text", "content": "New seasonal styles are available now." }, "landing_page_url": { "asset_type": "url", "url_type": "clickthrough", "url": "https://acme.example/summer" } } } ``` セラーが投稿を解決できるが有料サーブ前にクリエイター/ページ認可を必要とする場合、`error.details`(できれば `missing_connections[]`)にリカバリー詳細を伴う `AUTHORIZATION_REQUIRED` を返します。 `published_post` アセットの `reference_authorization` はサーバー発行の認可状態です。セラーは読み取り表面でそれを返してもよいが、プラットフォーム拡張が署名付き証明を定義しセラーがその証明を検証しない限り、書き込みリクエストのバイヤー供給認可状態クレームを無視しなければなりません。 ```json theme={null} { "errors": [ { "code": "AUTHORIZATION_REQUIRED", "message": "Connect the publisher identity that owns this post before paid serving.", "field": "creatives[0].assets.published_post", "details": { "missing_connections": [ { "provider": "social.example", "connection_type": "publisher_identity", "required_for": ["sync_creatives"], "scope": "identity", "status": "missing", "resource_ref": { "post_url": "https://social.example/@acme/post/12345" }, "authorization_url": "https://seller.example/connections/social/authorize?post=12345", "authorization_instructions": "Connect the creator or page that owns the source post." } ] } } ] } ``` ## ライフサイクル 公開投稿参照は承認後に利用不可になりうる。問題が回復可能なとき `suspended` を使います: | Condition | Lifecycle result | Reason code | | ------------ | ------------------------------------------------------ | -------------------------------- | | 認可が失効 | `suspended` | `identity_authorization_revoked` | | 認可が期限切れ | `suspended` | `identity_authorization_expired` | | ソース投稿が非公開になる | `suspended` | `source_private` | | ソース投稿が削除 | ステータス `archived`/`rejected`、または `creative.purged` イベント | `source_deleted` | アクティブなバイには、suspended クリエイティブは `resource_type: "creative"` と `transition.to: "suspended"` を伴うメディアバイ機能低下も作ります。バイヤーは `list_creatives` 経由でクリエイティブスナップショットを、`get_media_buys` 経由でバイスナップショットを照合します。webhook 順序は保証されません。 セラーが後でそのクリエイティブの依存関係が復元できないと判断する場合、`identity_authorization_revoked` のような同じ理由ファミリーで `suspended → rejected` に遷移してもよい。バイヤーは次に参照された投稿を置き換えるか別のクリエイティブを再提出する必要があります。 ## ディスカバリー境界 `list_creatives` はライブラリ/読み取り表面で、投稿検索 API ではありません。セラーは既に認可されたまたは以前同期された投稿参照を `list_creatives` の仮想クリエイティブとして露出してもよい(MAY)が、AdCP はセラーにすべてのネイティブプラットフォーム投稿を列挙することを要求しません。 publisher identity 認可を要求するプラットフォームには、`list_creatives` は接続されたアイデンティティを通じてセラーが見ることを許可された投稿のみを列挙できます。バイヤーが公開投稿ライブラリビューを求め必要な publisher identity 接続が欠けている場合、セラーは投稿がないと黙って暗示するのではなく `missing_connections[]` を伴う `AUTHORIZATION_REQUIRED` を返すべきです。 バイヤーが投稿 URL またはプラットフォーム投稿 ID を既に知っているとき、`sync_creatives` が正準書き込みパスです。認可が欠けているためセラーがまだそれをサーブできない場合、正しいレスポンスは新しいアイデンティティディスカバリータスクではなく `AUTHORIZATION_REQUIRED` です。 # セールスエージェントのクリエイティブ機能 Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/sales-agent-creative-capabilities AdCP のセールスエージェントはメディアバイとともにクリエイティブプロトコルを実装し、インラインクリエイティブ管理と生成広告フォーマットを提供します。 セールスエージェントはメディアバイプロトコルとともにクリエイティブプロトコルを実装できます。その場合、1つのエージェントエンドポイントがメディアバイとクリエイティブ管理の両方を処理する — バイヤーは別のサービスを発見して接続する必要がない。 これは、配信時にクリエイティブを生成したり、内部でクリエイティブライブラリを管理したり、広告プロダクトの一部としてフォーマット固有のクリエイティブサービスを提供したりするセラーにとって一般的なケースです。 ## 仕組み セールスエージェントは `get_adcp_capabilities` でクリエイティブプロトコルのサポートを宣言する: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/protocol/get-adcp-capabilities-response.json", "status": "completed", "adcp": { "major_versions": [3], "idempotency": { "supported": true, "replay_ttl_seconds": 86400 } }, "supported_protocols": ["media_buy", "creative"], "account": { "supported_billing": ["operator", "agent"] }, "media_buy": { "features": { "inline_creative_management": true } }, "creative": { "has_creative_library": true, "supports_generation": true, "supports_transformation": false, "supports_compliance": false } } ``` `inline_creative_management`(購入時にパッケージにクリエイティブを付与するメディアバイ機能)、クリエイティブプロトコルサポート、`creative.has_creative_library` は別個の宣言であることに注意。セールスエージェントは、クリエイティブライブラリなしでインラインのパッケージクリエイティブを受け入れる、インラインのパッケージアップロードなしでライブラリをホストする、または両方のパスをサポートする、のいずれもできます。 `inline_creative_management` は `create_media_buy` と `update_media_buy` でクリエイティブをパッケージに直接付与することを許可する — クリエイティブは購入またはパッケージ更新とともに移動します。クリエイティブプロトコルサポート(`supported_protocols` の `"creative"`)は、エージェントがそのクリエイティブケイパビリティフラグに応じて `build_creative`、`list_creative_formats`、[`sync_creatives`](/docs/creative/task-reference/sync_creatives) などのクリエイティブタスクを実装することを意味します。再利用可能なクリエイティブライブラリは、`creative.has_creative_library: true` によって明確に表明されます。セールスエージェントは、ライブラリを実装せずにインラインクリエイティブ管理をサポートする(バイヤーがメディアバイのリクエストとともにパッケージスコープのクリエイティブ本体を供給する)、またはインライン管理なしでライブラリをホストする(クリエイティブはパッケージに割り当てる前に `sync_creatives` で別途同期される)、のいずれもできます。 バイヤーはすべてのタスク — メディアバイとクリエイティブ — を同じエージェント URL で呼び出す。タスクが属するプロトコルがスキーマを決定し、エージェントのタイプは関係ありません。 ## バイヤーのクリエイティブパスの選択 バイヤーは、メディアバイのインラインフラグとクリエイティブプロトコルのライブラリケイパビリティから、クリエイティブ配信のパスを選びます: | `creative.has_creative_library` | `media_buy.features.inline_creative_management` | バイヤーの動作 | | ------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `true` | 不在または `false` | `sync_creatives` などのクリエイティブプロトコルタスクを使い、保存したクリエイティブを `packages[].creative_assignments` で割り当てる。 | | 不在または `false` | `true` | `create_media_buy` と `update_media_buy` でインラインの `packages[].creatives` を使う。`sync_creatives` を呼ばない。セラーはクリエイティブライブラリを表明していない。 | | `true` | `true` | 両方のパスが利用可能。単一使用のパッケージアセットや即時の置換にはインラインクリエイティブを、再利用可能なライブラリアセットには `sync_creatives` と `creative_assignments` を使う。セラーがクリエイティブライブラリを表明しているため、インラインクリエイティブはライブラリクリエイティブになる。 | | 不在または `false` | 不在または `false` | セラーは AdCP 上でクリエイティブ配信を表明していない。別のセラー固有の拡張がその動作を明示的に定義しない限り、バイヤーは `packages[].creatives` または `packages[].creative_assignments` を送るべきではない(SHOULD NOT)。 | `inline_creative_management` が true で `creative.has_creative_library` が不在または false の場合、`packages[].creatives` が表明されたクリエイティブサーフェスです。セラーはそれらのアセットをメディアバイまたはパッケージの一部としてのみ保存するかもしれません。バイヤーは `list_creatives`、`sync_creatives`、後の `creative_assignments` の再利用が機能することを期待すべきではありません。 インライン専用の承認状態の読み戻しはパッケージスコープです。`get_media_buys` は、パッケージに現在割り当てられているインラインクリエイティブの承認を `packages[].creative_approvals[]`(`creative_id`、`approval_status`、任意の `rejection_reason`)を通じて表面化しますが、完全なクリエイティブ本体、プレースメントのルーティング、ウェイト、過去のリビジョンは返しません。インライン専用のセラーを使うバイヤーは、後で検査または再提出する必要がある場合、送信したクリエイティブマニフェストまたはアセットペイロードを自分の側で保持すべきです。 ## 利用可能なクリエイティブタスク セールスエージェントが `supported_protocols` に `"creative"` を宣言すると、任意のクリエイティブプロトコルタスクを実装できる: | タスク | 実装するタイミング | | ----------------------- | ----------------------------------------------------------------------------------------------- | | `list_creative_formats` | 常に — バイヤーがサポートされているフォーマットを発見する必要がある | | `sync_creatives` | エージェントがクリエイティブライブラリをホストする場合。セールスエージェントはクリエイティブとパッケージの一括マッピングのために `assignments` フィールドをサポートすべきです。 | | `list_creatives` | バイヤーがライブラリを参照する必要がある場合 | | `build_creative` | エージェントがクリエイティブを生成または変換する場合 | | `preview_creative` | エージェントがプレビューをレンダリングできる場合 | | `get_creative_delivery` | エージェントがバリアントレベルの配信データを報告できる場合 | これらはスタンドアロンのクリエイティブエージェントが実装するのと同じタスクです。違いは運用上のものであり、プロトコルレベルではない: バイヤーは別のサービスを発見して接続する必要がない。 メディアバイの [accounts プロトコル](/docs/accounts/overview)をすでに実装しているセールスエージェントは、クリエイティブタスクのために追加のアカウント設定は必要ない — 同じアカウントが両方のプロトコルをカバーします。 ## 保存クリエイティブのアダプター引き渡し バイヤーが `sync_creatives` を通じてクリエイティブをアップロードし、その後 `create_media_buy` または `update_media_buy` の `creative_assignments` を使ってそれをパッケージに付与するとき、AdCP のプロトコルサーフェスが運ぶ識別子は一つだけです: `creative_id`。バイヤーはクリエイティブの作成または同期時にそれを選び、後でアセットのバイトを再送せずに、その同じ値を再利用して保存したクリエイティブを割り当てます。 下記の例の `id` フィールドは、二つ目のバイヤー提供の AdCP 識別子ではありません。それは、すでに汎用のアセット `id` を使う実装層のために、`creative_id` からコピーされたセラー側のアダプターエイリアスです。AdCP は、上流のアドサーバー、リテールメディアプラットフォーム、セラーの記録システムがこのオブジェクトを使うことを要求しません。この形状は、AdCP の保存クリエイティブを内部のアセットペイロードに変換することを選ぶセラーの SDK、フィクスチャ、アダプター向けの実装ガイダンスです。 実装が URL 裏付けの保存クリエイティブをアダプター層に公開するとき、アダプター向けのオブジェクトは、両方の識別子の綴りに加えて、アセットをトラフィックするのに必要なポータブルなメタデータを保持すべきです(SHOULD)。 アダプター向けの内部オブジェクト(AdCP のリクエストまたはレスポンスの形状ではない): ```json test=false theme={null} { "id": "cr_acme_spring_mrec", "creative_id": "cr_acme_spring_mrec", "name": "Acme Corp spring MREC", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "format_kind": "image", "asset_type": "image", "url": "https://cdn.acme.example/creative/spring-mrec.png", "width": 300, "height": 250 } ``` `creative_id` は正準的な AdCP 保存クリエイティブ識別子です。`id` は、`id` フィールドを期待する汎用のアセット契約のためのアダプター互換性エイリアスです。実装は、保存クリエイティブをアダプターアセットに変換するとき、両方を同じ値で埋めるべきです(SHOULD)。アダプターは、両方が存在する場合は `creative_id` を優先すべきです。AdCP のワイヤー上では、バイヤーは `creative_assignments[]` に `creative_id` のみを送ります。アダプター専用の `id` エイリアスは内部の互換性データであって、二つ目のバイヤー提供の識別子ではありません。セラーは、入力に存在する場合はそれを無視しなければなりません(MUST)。 上記の URL 裏付けのパスがポータブルなアダプターの引き渡しです。スニペット裏付けまたはインラインコンテンツ裏付けの保存クリエイティブ(HTML スニペット、サードパーティタグ、セラーがレンダリングするブリーフコンテンツなど)は、変換がそのアダプターのトラフィッキングモデルに依存するため、明示的なアダプターサポートを必要とします。ドキュメントと SDK のスキャフォールドは、そのアダプターの契約が実際に `snippet`、`content`、`media_url` などのフィールドを宣言し扱わない限り、すべてのアダプターがそれらを受け取ると示唆すべきではありません(SHOULD NOT)。

クリエイティブレビュー

クリエイティブを受け取るセールスエージェント — `create_media_buy` のインライン付与または `sync_creatives` 経由 — は、配信前にクリエイティブレビューを実施する場合があります。これは、クリエイティブがポリシーコンプライアンス、マルウェア、またはブランドセーフティ違反のためにスキャンされる実際のパブリッシャーおよび SSP ワークフローを反映しています。 クリエイティブレビューの状態は2つのレベルでサーフェスされます: * **クリエイティブライブラリ**: `creative.has_creative_library: true` を表明するセラーでは、[`list_creatives`](/docs/creative/task-reference/list_creatives) の各クリエイティブの `status` フィールドはクリエイティブステータス列挙型を使用します: `processing`、`pending_review`、`approved`、`rejected`、`archived`。 * **パッケージレベル**: [`get_media_buys`](/docs/media-buy/task-reference/get_media_buys) レスポンスの各クリエイティブの `approval_status` フィールドはクリエイティブ承認ステータス列挙型を使用します: `pending_review`、`approved`、または人間が読める説明付きの `rejected`。却下されたクリエイティブには `rejection_reason` が含まれます。 クリエイティブはライブラリでは `approved` でも、プレースメント固有のポリシーに違反する場合はパッケージレベルで `rejected` になる可能性がある(例: オーディオのみのプレースメントでのビデオクリエイティブ)。 バイヤーは、特に厳格なクリエイティブポリシーを持つパブリッシャーの場合、新しいクリエイティブを含むメディアバイを同期または提出した後、`list_creatives` または `get_media_buys` をポーリングすべきです。インライン専用のセラーでは、`get_media_buys` が承認状態の読み戻しサーフェスです。 配信時にセラーが生成する生成フォーマットの場合、クリエイティブレビューは個々のクリエイティブではなくブリーフとブランドアイデンティティに適用されます。セラーはブリーフの制約とブランドアセットが生成が始まる前にポリシーを満たしているかを検証します。 **プラットフォームとコミュニティガイドライン**: ソーシャルプラットフォーム、UGC サイト、コミュニティ主導のパブリッシャーは、標準的な広告ポリシーを超えたコンテンツポリシー(例: コミュニティスタンダード、プロモートコンテンツガイドライン、カテゴリ固有の制限)を執行することが多い。これらのプラットフォーム固有のポリシーは同じ `rejection_reason` フィールドを通じてサーフェスされる — バイヤーはそれらを処理するために別のメカニズムを必要としません。コミュニティガイドライン違反でクリエイティブが却下された場合、`rejection_reason` はどのポリシーが違反されたかを説明し、バイヤーが修正して再提出できるようにします。 ### 生成クリエイティブの実装チェックリスト 生成クリエイティブを提供するセールスエージェントは以下を行うべきだ: 1. **ケイパビリティを宣言する** `get_adcp_capabilities` で: * クリエイティブケイパビリティの `supports_generation: true` * `supported_protocols` の `"creative"` * クリエイティブがメディアバイとともに移動する場合は `inline_creative_management: true` 2. `list_creative_formats` を通じて**生成フォーマットを定義する** — `format_id.agent_url` が自分のエージェントを指します。記述的なフォーマット名とアセット要件(最低限 `brief` アセット)を含めます。 3. **`preview_creative` を実装する** — フライト前レビューのための代表的なプレビューを返します。バイヤーが異なる配信時の条件をシミュレートできるよう `context_description` インプットをサポートします。会話型フォーマットの場合、サンドボックステスト用の `interactive_url` を含めます。 4. **`get_creative_delivery` を実装する** — 何が生成され配信されたかを示すバリアントマニフェストを返します。バリアントごとの `generation_context`(context\_type、topic、device\_class)と配信メトリクスを含めます。バリアント保持を計画する: フライト後の監査のために少なくとも 90 日間のバリアントデータを保持します。 5. **購入時にブリーフを検証する** — バイヤーが `create_media_buy` でブリーフを提出する際、ブリーフの制約(ブランドアイデンティティ、ガードレール、必須開示)が生成ケイパビリティと互換性があることを検証します。そうでない場合は明確なエラーで拒否します。 6. **ブリーフのクリエイティブレビューを処理する** — 生成フォーマットでは、レビューは個々のクリエイティブではなくブリーフとブランドアイデンティティに適用されます。`get_media_buys` のパッケージの `approval_status` を通じてレビュー状態を伝える。 ## セールスエージェントの生成フォーマット 配信時にクリエイティブを生成するセラー — コンテキスト広告、ページマッチドディスプレイ、AI 生成ネイティブ — は独自の生成フォーマットをホストします。`format_id.agent_url` はセールスエージェント自体を指す: ```json theme={null} { "format_id": { "agent_url": "https://ads.seller-example.com", "id": "contextual_display_generative" }, "name": "Contextual display (AI-generated)", "type": "display", "description": "Display ads generated at serve time based on page context and brand brief" } ``` バイヤーはセールスエージェントの `list_creative_formats` を通じてこのフォーマットを発見し、メディアバイ作成時にブリーフを提供します: ```json theme={null} { "packages": [{ "product_id": "premium_display", "pricing_option_id": "cpm_standard", "budget": 50000, "creatives": [{ "creative_id": "brand_contextual_brief", "name": "Q2 contextual campaign brief", "format_id": { "agent_url": "https://ads.seller-example.com", "id": "contextual_display_generative" }, "assets": { "brief": { "content": "Highlight our sustainability story. Match tone to editorial context." } } }] }] } ``` セラーはこのブリーフとバイヤーのブランドアイデンティティを使用して配信時にクリエイティブを生成します。別の `build_creative` 呼び出しは不要 — ブリーフはメディアバイとともに移動します。 規制対象カテゴリ(金融サービス、製薬)の場合、エージェントのクリエイティブケイパビリティで `supports_compliance: true` を確認します。コンプライアンス対応エージェントは生成中に必須開示と規制要素を検証する — エージェントが執行できるよう、ブリーフにコンプライアンス要件を含めます。 ## 生成出力のプレビュー 生成フォーマットでは、`preview_creative` は2つの目的を果たす: **キャンペーン前** — ブリーフマニフェストと `context_description` インプットを渡して、エージェントが生成できるものの代表的なサンプルを確認します: ```json theme={null} { "request_type": "single", "quality": "draft", "creative_manifest": { "format_id": { "agent_url": "https://ads.seller-example.com", "id": "contextual_display_generative" }, "assets": { "brief": { "content": "Highlight our sustainability story. Match tone to editorial context." } } }, "inputs": [ { "name": "Tech article", "context_description": "Article about semiconductor manufacturing" }, { "name": "Lifestyle blog", "context_description": "Blog post about sustainable living" } ] } ``` クリエイティブの方向性の高速な反復には `quality: "draft"` を使用し、ステークホルダーレビューには `quality: "production"` を使用します。これらのプレビューは例示的なもの — 実際の出力は配信時のライブシグナルに依存します。プレビューはアクティブなメディアバイを必要としない — `create_media_buy` を呼び出す前にプレビューできます。 **キャンペーン後** — `get_creative_delivery` の `variant_id` を渡して、実際に配信されたものを再生する: ```json theme={null} { "request_type": "variant", "variant_id": "gen_tech_mobile_001" } ``` レスポンスにはバリアントの実際のマニフェストとレンダリングされたプレビューが含まれます。これは忠実な再生であり、再生成ではありません。 完全なメンタルモデルと期待テーブルについては[生成クリエイティブのプレビュー](/docs/creative/task-reference/preview_creative#previewing-generative-creative)を参照。 ## 配信レポート 両プロトコルを実装するセールスエージェントは、配信データの2つの補完的なビューを提供します: | タスク | プロトコル | 提供する内容 | | ------------------------ | ------- | ------------------------------------------------------------- | | `get_media_buy_delivery` | メディアバイ | どこで、どれだけ: インプレッション、支出、プレースメントデータ、オプションの `by_creative` ブレークダウン | | `get_creative_delivery` | クリエイティブ | 何が実行されたか、どのように: バリアントマニフェスト、生成コンテキスト、バリアントレベルのメトリクス | 両タスクは同じエージェント URL で呼び出されます。`media_buy_id` と `creative_id` を結合キーとして使用して、両レスポンスのデータを関連付ける。 生成フォーマットでは、`get_creative_delivery` がバイヤーが実際に生成されたものを見る場所です。各バリアントにはマニフェスト(実際のレンダリングされたクリエイティブ — ヘッドラインテキスト、画像 URL、フォーマット)と生成コンテキスト(ページトピックやデバイスクラスなど、生成をトリガーしたシグナル)が含まれます。`get_media_buy_delivery` は支出、ペーシング、次元ブレークダウンを含む集計ビューを提供します。 ### 例: セールスエージェントのバリアントレベルレポート ```json theme={null} { "media_buy_id": "mb_12345", "currency": "USD", "reporting_period": { "start": "2026-03-01T00:00:00-05:00", "end": "2026-03-08T23:59:59-05:00", "timezone": "America/New_York" }, "creatives": [ { "creative_id": "brand_contextual_brief", "format_id": { "agent_url": "https://ads.seller-example.com", "id": "contextual_display_generative" }, "totals": { "impressions": 300000, "spend": 15000, "clicks": 12000, "ctr": 0.04 }, "variant_count": 47, "variants": [ { "variant_id": "gen_tech_mobile_001", "generation_context": { "context_type": "web_page", "topic": "technology, semiconductors", "device_class": "mobile" }, "manifest": { "format_id": { "agent_url": "https://ads.seller-example.com", "id": "contextual_display_generative" }, "assets": { "headline": { "asset_type": "text", "content": "Sustainable tech for a better tomorrow" }, "hero_image": { "asset_type": "image", "url": "https://cdn.seller-example.com/generated/tech_mobile_001.jpg", "width": 300, "height": 250 } } }, "impressions": 45000, "spend": 2250, "clicks": 2700, "ctr": 0.06 } ] } ] } ``` ## 別のクリエイティブエージェントを使う場合 以下の場合、別のクリエイティブエージェントが適している: * クリエイティブサービスが**セラーから独立している** — クリエイティブ管理プラットフォーム、広告サーバー、フォーマット変換サービス * 複数のセラーが**同じクリエイティブを配信する必要がある** — バイヤーが1か所でクリエイティブを管理して配布します * バイヤーがセラー間で**集中型クリエイティブアナリティクス**を望む セラーの広告プロダクトが統合されたケイパビリティとしてクリエイティブ生成や管理を含む場合、両プロトコルをセールスエージェントに実装することがすべての人にとってよりシンプルです。 ## ケイパビリティの発見 バイヤーは `get_adcp_capabilities` を確認して、セールスエージェントがサポートしているものを理解する: ```javascript theme={null} const caps = await agent.getAdcpCapabilities({}); if (caps.errors) { throw new Error(`Capabilities check failed: ${caps.errors[0].message}`); } const sellsMedia = caps.supported_protocols.includes('media_buy'); const managesCreatives = caps.supported_protocols.includes('creative'); const acceptsInlineCreatives = caps.media_buy?.features?.inline_creative_management === true; if (managesCreatives) { const { has_creative_library, supports_generation } = caps.creative; if (supports_generation) { // Agent generates creatives — look for generative formats const formats = await agent.listCreativeFormats({}); if (formats.errors) { console.error('Format discovery failed:', formats.errors); } } if (has_creative_library) { // Agent hosts a library — can sync and browse creatives const creatives = await agent.listCreatives({ account: { account_id: 'acc_123' } }); if (creatives.errors) { console.error('Library browse failed:', creatives.errors); } } } if (sellsMedia && acceptsInlineCreatives && !managesCreatives) { // Inline-only seller: send package creatives on create_media_buy or update_media_buy. } ``` ## 関連ドキュメント * [クリエイティブ仕様](/docs/creative/specification) — クリエイティブエージェントのプロトコル要件 * [クリエイティブエージェントの実装](/docs/creative/implementing-creative-agents) — スタンドアロンクリエイティブエージェントの実装ガイド * [生成クリエイティブ](/docs/creative/generative-creative) — AI 駆動生成のための `build_creative` の使用 * [get\_creative\_delivery](/docs/creative/task-reference/get_creative_delivery) — バリアントレベルの配信レポート * [get\_media\_buy\_delivery](/docs/media-buy/task-reference/get_media_buy_delivery) — メディアバイ配信レポート * [メディアバイライフサイクル](/docs/media-buy/media-buys/lifecycle) — 状態機械、順序立てられたフロー、保証付きディールの IO パス、クリエイティブ同期のタイミング # クリエイティブ仕様 Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/specification AdCP クリエイティブプロトコル仕様は、フォーマット発見、マニフェスト検証、AI クリエイティブ生成、プレビューレンダリングを定義します。 **AdCP 3.0 提案** - この仕様は AdCP 3.0 向けに開発中です。フィードバックは [GitHub Discussions](https://github.com/adcontextprotocol/adcp/discussions) から歓迎します。 > **3.1 の正準フォーマット**: このページは v1 の仕様モデルを説明します。正準フォーマットのモデル(プロダクト上のインライン `format_options`、`validate_input` プリミティブ)については、[canonical-formats](/docs/creative/canonical-formats) と[マイグレーションガイド](/docs/creative/canonical-formats-migration)を参照してください。 **ステータス**: コメント募集中 **最終更新**: 2026年3月 このドキュメントの "MUST"、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、"SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"MAY"、"OPTIONAL" というキーワードは [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) に記載の通りに解釈します。 ## 概要 クリエイティブプロトコルは、クリエイティブフォーマット発見、マニフェスト検証、クリエイティブ生成、プレビューレンダリングのための標準インターフェースを定義します。このプロトコルにより、AI エージェントが広告プラットフォーム全体でフォーマット仕様を発見し、準拠したクリエイティブアセットをビルドし、プレビューを生成できます。 ## プロトコル概要 クリエイティブプロトコルが提供するもの: * 完全な技術仕様を持つフォーマット発見 * フォーマット要件に対するマニフェスト検証 * AI 搭載のクリエイティブ生成と変換 * クリエイティブ検証のためのプレビューレンダリング * クロスプラットフォームトラッキング用ユニバーサルマクロ ## トランスポート要件 クリエイティブエージェントは以下のトランスポートのうち少なくとも1つをサポートしなければなりません (MUST): | トランスポート | プロトコル | 説明 | | ------- | ---------------------- | --------------------------- | | MCP | Model Context Protocol | JSON-RPC によるツールベースのインタラクション | | A2A | Agent-to-Agent | メッセージベースのインタラクション | クリエイティブエージェントは優先トランスポートとして MCP をサポートすべきだ (SHOULD)。 クリエイティブエージェントは `get_adcp_capabilities` を通じてクリエイティブプロトコルのサポートを宣言しなければなりません (MUST): ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/protocol/get-adcp-capabilities-response.json", "status": "completed", "adcp": { "major_versions": [2], "idempotency": { "supported": true, "replay_ttl_seconds": 86400 } }, "supported_protocols": ["creative"], "creative": { "has_creative_library": true, "supports_generation": false, "supports_transformation": true, "supports_compliance": false } } ``` `creative` ケイパビリティはバイヤーに対してこのエージェントがサポートするインタラクションモデルを伝える。以下の[インタラクションモデル](#インタラクションモデル)を参照。 ## コアコンセプト ### クリエイティブエージェント クリエイティブエージェントはクリエイティブプロトコルを実装するすべてのエージェントです。スタンドアロンサービス(広告サーバー、クリエイティブ管理プラットフォーム、ジェネレーティブツール)と、`supported_protocols` に `"creative"` を宣言するセールスエージェントを含みます。クリエイティブエージェントは: * 自身が所有するフォーマットを定義・文書化します * フォーマット要件に対してマニフェストを検証します * クリエイティブがどのようにレンダリングされるかを示すプレビューを生成します * オプションで自然言語ブリーフからクリエイティブを生成または変換します メディアバイプロトコルとクリエイティブプロトコルの両方を実装するセールスエージェントは、単一のエンドポイントから両方の役割を担う。[セールスエージェントのクリエイティブ機能](/docs/creative/sales-agent-creative-capabilities)を参照。 ### インタラクションモデル クリエイティブエージェントはケイパビリティに応じてさまざまな役割を担う。バイヤーは `get_adcp_capabilities` を使用してどのインタラクションモデルが適用されるかを判断する: | モデル | 説明 | ケイパビリティ | 例 | | ------------------ | -------------------------------- | ------------------------------- | ------------------------- | | **変換エージェント** | 既存のマニフェストを新しいフォーマットにリサイズまたは適応させる | `supports_transformation: true` | フォーマット変換サービス | | **ジェネレーティブエージェント** | 自然言語ブリーフからマニフェストを作成する | `supports_generation: true` | AI クリエイティブプラットフォーム | | **クリエイティブ広告サーバー** | クリエイティブライブラリをホスト、広告配信タグを生成する | `has_creative_library: true` | Flashtalking、CM360、Celtra | これらのモデルは組み合わせ可能だ — エージェントは複数をサポートできます。`supports_generation: true` と `has_creative_library: true` を持つクリエイティブ広告サーバーは、ブリーフからクリエイティブを生成することも、ライブラリから既存のものを取得することもできます。`supports_compliance` フラグは直交している — どのインタラクションモデルもブリーフのコンプライアンス要件をサポートできます。 **モデル別バイヤーワークフロー:** * **変換**: `list_creative_formats` → `build_creative`(`creative_manifest` + `target_format_id` を使用) * **生成**: `list_creative_formats` → `build_creative`(`message` + `target_format_id` を使用) * **ライブラリ取得**: `list_creatives` → `build_creative`(`creative_id` + `target_format_id` を使用) クリエイティブライブラリをホストするエージェントは、バイヤーがクエリ前にアクセスを確立できるよう [accounts プロトコル](/docs/accounts/overview)を実装すべきだ(SHOULD)。メディアバイのために accounts を既に実装しているセールスエージェントは追加対応不要です。 サービスに課金する変換または生成エージェントは、Accounts プロトコルを実装し、`list_creative_formats` で `pricing_options` を公開し、`build_creative` のレスポンスで価格を返します。ビルド出力に `creative_id` を永続化するエージェントは、`list_creatives` でも価格を公開できます。無料の変換エージェントはステートレスのまま、変更されません。 ### フォーマットオーソリティ 各フォーマットはフォーマット ID の `agent_url` で識別される唯一の権威あるクリエイティブエージェントを持ちます: ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_image" } } ``` クリエイティブエージェントは自身が所有するフォーマットの権威あるフォーマット定義のみを返さなければなりません (MUST)。 クリエイティブエージェントは追加フォーマットを提供する他のクリエイティブエージェントを参照してもよい (MAY)。 ### フォーマット フォーマットはアセットがどのようにアセンブルされてレンダリングされるかを定義します。フォーマットは以下を指定します: * メディアファミリ(display、video、audio、dooh) * 必須および任意アセットタイプ * 技術的制約(ディメンション、デュレーション、ファイルサイズ、コーデック) * レンダリング動作とインタラクション期待値 ### アセット アセットはクリエイティブの構成要素です。アセットタイプには以下が含まれます: * **image**: 静止画像(JPEG、PNG、WebP、GIF) * **video**: ビデオファイル(MP4、WebM、MOV)または VAST タグ * **audio**: オーディオファイル(MP3、M4A)または DAAST タグ * **text**: ヘッドライン、説明文、CTA * **html**: HTML5 クリエイティブまたはサードパーティタグ * **javascript**: JavaScript タグ * **url**: トラッキングピクセル、クリックスルー URL ### マニフェスト マニフェストはフォーマット仕様と実際のアセットコンテンツを組み合わせます。マニフェストは以下を提供します: * フォーマット参照(agent\_url + id) * フォーマットの asset\_id をキーとしたアセット値 * トラッキング URL とマクロ クリエイティブエージェントは受け入れる前にフォーマット要件に対してマニフェストを検証しなければなりません (MUST)。 ### ユニバーサルマクロ AdCP はクロスプラットフォームトラッキング用のユニバーサルマクロを定義します。クリエイティブエージェントはトラッキング URL でこれらのマクロをサポートしなければなりません (MUST): * `{TIMESTAMP}`: Unix タイムスタンプ * `{CACHEBUSTER}`: ランダムなキャッシュ無効化値 * `{CLICK_URL}`: クリックトラッキング URL * `{REDIRECT_URL}`: 最終宛先 URL セールスエージェントはユニバーサルマクロを自身の広告サーバーのネイティブ構文に変換しなければなりません (MUST)。 ## クリエイティブステータスのライフサイクル **スキーマ**: [`enums/creative-status.json`](https://adcontextprotocol.org/schemas/v3/enums/creative-status.json) ライブラリ内のクリエイティブは、定義された状態の集合を進みます。ほとんどの遷移はセラー起点です(processing、review、approval/rejection)。`suspended` は、依存関係が利用不能になった承認済みクリエイティブ(例: 期限切れの公開済み投稿の認可)のための回復可能なオフライン状態です。`archived` は、バイヤーのクリーンアップによって、またはアクティブな割り当てのないクリエイティブに対するセラー側のライフサイクルポリシーによって到達します——下記のルールを参照。 ``` sync_creatives ──▶ processing ──▶ pending_review ──▶ approved │ │ │ │ │ ├──▶ suspended ──▶ approved │ │ │ │ │ │ │ └──▶ rejected │ │ ├──▶ pending_review │ │ ├──▶ rejected │ │ └──▶ archived │ │ └──────▶ rejected ◀──────────────┘ │ └── buyer fixes + resubmits ──▶ processing archived ── buyer unarchives ──▶ approved (or pending_review when re-review is required) ``` **ルール:** * `processing` → `pending_review`: 取り込みとトランスコードが成功したときに自動 * `processing` → `rejected`: 処理が失敗したときに自動(破損ファイル、サポートされないコーデック、制約違反) * `pending_review` → `approved`: セラーがコンテンツポリシーのレビュー後に承認 * `pending_review` → `rejected`: セラーが `rejection_reason` とともに拒否 * `approved` → `suspended`: セラーが回復可能な依存関係/認可の喪失を検出(例: `published_post` 参照の `identity_authorization_revoked`、`identity_authorization_expired`、`source_private`)。セラーは影響を受けるアクティブなバイに対応する `impairment` を表面化しなければなりません(MUST)。 * `suspended` → `approved`: セラーが依存関係が回復されたことを観測し、必要な再レビューが通る。 * `suspended` → `rejected`: セラーが、以前は回復可能だった依存関係/認可の喪失をこのクリエイティブについて回復できない、または置換/再提出が必要と判断。例: `published_post` 参照の失効したアイデンティティ/投稿の認可を再認可できない。セラーは、クリエイティブが置換・再割り当てされるか、パッケージ/バイがそれ以外の方法で是正されるまで、影響を受けるアクティブなバイを impaired に保たなければなりません(MUST)。 * `approved` → `archived`(バイヤー起点): バイヤーが `sync_creatives` を通じてアーカイブを発行 * `approved` → `archived`(セラー起点): セラーが、非アクティブ、フライト後の期限切れ、またはストレージポリシーのために未割り当てのクリエイティブをアーカイブ。セラーは、アクティブなパッケージ割り当てを持つクリエイティブをセラーアーカイブしてはなりません(MUST NOT)——アクティブな配信が関与する場合、影響を受けるバイに `impairment` を伴う `approved` → `rejected`(失効)のパスが唯一の準拠ルートです。セラー起点のアーカイブの状態変更の可観測性は[クリエイティブ保持の契約](/docs/creative/creative-libraries#creatives-outlast-campaigns)に従います——最小限、新しい `status` が次の `list_creatives` の読み取りで可視でなければなりません(MUST)。 * `archived` → `approved`: `sync_creatives` を通じたバイヤー起点(アーカイブ解除)。セラーは再レビューを要求し、代わりに `pending_review` へ遷移してもよい(MAY)。 * `rejected` → `processing`: バイヤーがクリエイティブを修正し `sync_creatives` を通じて再提出。クリエイティブは完全な処理とレビューのパイプラインに再入します。 * `approved` → `pending_review`: セラー起点の再レビュー(例: ポリシー変更)。以前承認されたクリエイティブが再レビューのために引き戻されたとき、セラーは `creative.status_changed` を通じてサブスクライバーに通知しなければなりません(MUST)(`event_types[]` にこの値を含む各 `notification_configs[]` サブスクライバーに発火——下記を参照)。 クリエイティブエージェントは、配信のために `rejected` クリエイティブを参照する操作(例: パッケージへの割り当て)をエラーコード `CREATIVE_REJECTED` で拒否しなければなりません(MUST)。クリエイティブエージェントはまた、依存関係が回復されるまで `suspended` クリエイティブの配信を防がなければなりません(MUST)。 クリエイティブエージェントは、`list_creatives` レスポンスに `status` と(拒否時は)`rejection_reason` を含めなければなりません(MUST)。 ### ライフサイクルウェブフック セラー起点およびシステム起点の遷移は、アカウントの [`notification_configs[]`](/docs/accounts/tasks/sync_accounts#account-level-webhook-subscriptions) サブスクライバーに対してプッシュ通知を発火します——`event_types[]` に発火されたタイプを含む各エントリが独立した発火を受け取ります。二つのイベントタイプがこの面をカバーします: * **`creative.status_changed`** — すべてのセラー起点またはシステム起点の遷移で発火: `pending_review → approved`/`rejected`、`approved → pending_review`(再レビュー)、`approved → suspended`(回復可能な依存関係/認可の喪失)、`suspended → approved`(回復)、`suspended → rejected`(終端の依存関係/認可の喪失)、`approved → rejected`(承認後の失効)、`approved → archived`(セラー起点)。ペイロード: [`creative-status-changed-webhook.json`](https://adcontextprotocol.org/schemas/v3/creative/creative-status-changed-webhook.json)。 * **`creative.purged`** — クリエイティブが破棄されたときに発火(保持のスイープ、テイクダウン、法的消去)。`soft` パージは `list_creatives`(`include_purged: true`)上に 30 日間トゥームストーンを保持します。`hard` パージはレコードを保持しません——ウェブフックがバイヤーの唯一のシグナルです。ペイロード: [`creative-purged-webhook.json`](https://adcontextprotocol.org/schemas/v3/creative/creative-purged-webhook.json)。 バイヤー起点の遷移(アーカイブ、アーカイブ解除、再提出)は発火**しません**——それらは `sync_creatives` のレスポンスパスで確認応答されます。プッシュチャネルは、バイヤーが起こさなかった遷移のためだけに存在します。 両イベントは、[`creative-event-reason-code.json`](https://adcontextprotocol.org/schemas/v3/enums/creative-event-reason-code.json) から引かれるカテゴリカルな `reason_code` を運びます。理由コードごとのバイヤー側の是正は、列挙の `enumDescriptions` にインラインで文書化されています。 クリエイティブがアクティブな配信を壊す状態(`approved → suspended`、`approved → rejected`、`suspended → rejected`、または割り当てが存在する間のセラー起点の `approved → archived`——これは非準拠。上記の遷移ルールを参照)へ遷移するとき、セラーはそのクリエイティブを参照するすべてのメディアバイに対応する `impairment` も表面化しなければなりません(MUST)([メディアバイの健全性](/docs/media-buy/media-buys/lifecycle#health-and-impairments)を参照)。クリエイティブ側の `creative.status_changed` とバイ側の `impairment` はペアだが別個のシグナルです。バイヤーは `creative_id` で相関させます。二つの面は異なるアンカーを持ちます: クリエイティブイベントはアカウントレベルで発火し(サブスクリプションは任意の一つのバイより長生き)、impairment はバイごとに発火します。ペアの発火の間に**順序の保証はありません**——バイヤーは一方が他方より前に届くと仮定してはなりません(MUST NOT)。スナップショット(`list_creatives` と `get_media_buys`)を介して突き合わせてください。 セラーは、サポートするイベントタイプとタイプごとの合体ウィンドウを `get_adcp_capabilities` を通じて宣言します。デフォルトの合体は `creative.status_changed` で 5 分です。セラーは `creative.purged` を合体してはなりません(MUST NOT)。遡及的な契約: セラーがこれらのイベントタイプのサポートを宣言すると、その義務はライブラリ内のすべてのクリエイティブをカバーします——既存のクリエイティブに猶予期間はありません。 バイヤーは、`include_webhook_activity: true` を伴う `list_creatives` を通じて、クリエイティブごとの最近のウェブフック発火をプルしてもよい(MAY)。読み取り面は [`webhook_activity[]` の採用チェックリスト](/docs/protocol/snapshot-and-log#webhook-activity-log-pattern)に従います——30 日保持、三状態の存在セマンティクス、バイヤー側のエンドポイントログへの `idempotency_key` 相関。 ## 価格 サービスに課金するクリエイティブエージェントは、シグナルエージェントやコンテンツ標準エージェントが使うのと同じ 発見 → ビルド → レポート のループを通じて価格を公開します。 ### 価格発見の面 価格は、エージェントのインタラクションモデルに応じて二つの面を通じて発見されます: * **`list_creatives`** — アドサーバーとライブラリベースのエージェントは、各クリエイティブに `pricing_options[]` を公開します。バイヤーは使いたい特定のクリエイティブの価格を発見します。 * **`list_creative_formats`** — 変換および生成エージェントは、各フォーマットに `pricing_options[]` を公開します。バイヤーは、クリエイティブが存在する前に、エージェントが生成できるフォーマットの価格を発見します。 両方の面は、[`vendor-pricing-option`](https://adcontextprotocol.org/schemas/v3/core/vendor-pricing-option.json) オブジェクトの同じ `pricing_options[]` 配列を使います。両方ともリクエストに `account` と `include_pricing: true` を必要とします。 エージェントは両方の面で価格を公開してもよい(MAY)(例: ライブラリと変換機能の両方を持つクリエイティブ管理プラットフォーム)。 ### 価格のフロー 1. **アカウントのセットアップ** — レートカードが合意されます。後続のすべての操作の価格を決定します。 2. **発見** — `account` と `include_pricing: true` を伴う `list_creatives` または `list_creative_formats` が `pricing_options[]` を返します。ベンダーは複数のオプションを提供できます(ボリュームティア、コンテキスト固有のレート、プロダクトラインごとの異なるモデル)。 3. **ビルド** — `account` を伴う `build_creative`。エージェントがコストを計算し、レスポンスで `pricing_option_id`、`vendor_cost`、`currency`、`consumption` を返します。 4. **レポート** — 照合のための `creative_id` と `pricing_option_id` を伴う `report_usage`。 ### 価格モデル クリエイティブエージェントは、[`vendor-pricing-option.json`](https://adcontextprotocol.org/schemas/v3/core/vendor-pricing-option.json) で定義されたベンダー価格モデルを再利用します: | モデル | クリエイティブのユースケース | | ------------------ | --------------------------------------------------------- | | `cpm` | 配信された 1000 インプレッションあたりのコスト——アドサーバーモデル、DCO プラットフォーム | | `percent_of_media` | メディア支出の割合——エージェンシー/プラットフォームモデル | | `flat_fee` | 期間ごとの固定料金——ライセンス付きクリエイティブスイート、サブスクリプションアクセス | | `per_unit` | 作業単位ごとの固定価格——適応したフォーマットごと、生成した画像ごと、トークンごと、レンダリングしたバリアントごと | ### 消費の詳細 **スキーマ**: [`core/creative-consumption.json`](https://adcontextprotocol.org/schemas/v3/core/creative-consumption.json) `build_creative` のレスポンスには、何が消費されたかについての構造化された詳細を持つ `consumption` オブジェクトが含まれます。既知のフィールド: `tokens`(消費された LLM トークン)、`images_generated`、`renders`(レンダーパス)、`duration_seconds`(処理時間)。エージェントは追加のフィールドを含めてもよい(MAY)。 `consumption` オブジェクトは情報提供です——バイヤーが `vendor_cost` がレートカードと整合していることを検証できるようにします。`vendor_cost` が請求の信頼できる情報源です。 ### アカウントの要件 サービスに課金するクリエイティブエージェントは、[Accounts プロトコル](/docs/accounts/overview)を実装しなければなりません(MUST)。これは価格を持つ任意のクリエイティブエージェントに適用されます——アドサーバー、生成プラットフォーム、使用に課金する変換エージェント。 ### バンドルモード パブリッシャーがクリエイティブエージェントを内部で(バンドルして)使う場合、バイヤーはクリエイティブエージェントの価格を決して見ません。コストはプロダクト価格に吸収されます。セールスエージェントがクリエイティブエージェントとの関係におけるバイヤーです——アカウントを確立し、`build_creative` を呼び、`report_usage` を扱います。プロトコルの面は同じです。 ## タスク クリエイティブプロトコルは以下のタスクを定義します。完全なリクエスト/レスポンスのスキーマと例についてはタスクリファレンスページを参照。 ### list\_creative\_formats **リファレンス**: [`list_creative_formats` タスク](/docs/creative/task-reference/list_creative_formats) クリエイティブフォーマットとその仕様を発見します。 **要件:** * クリエイティブエージェントは自身が所有するフォーマットの完全なフォーマット仕様を返さなければなりません (MUST) * クリエイティブエージェントは各フォーマットの権威あるエージェントを識別する `agent_url` を含めなければなりません (MUST) * クリエイティブエージェントはフォーマット定義に技術的制約(ディメンション、デュレーション、ファイルタイプ)を含めなければなりません (MUST) * クリエイティブエージェントは追加フォーマットを提供する他のクリエイティブエージェントへの参照を含めてもよい (MAY) * `format_ids` でフィルタリングする場合、クリエイティブエージェントはリクエストされたフォーマットのみを返さなければなりません (MUST) ### list\_transformers **リファレンス**: [`list_transformers` タスク](/docs/creative/task-reference/list_transformers) クリエイティブエージェントが提供する、アカウントスコープのトランスフォーマーを発見します——メディアバイのプロダクトのクリエイティブ版: エージェントが提供する選択可能なビルド能力の単位(声、モデル、スタイル)で、`build_creative` の `transformer_id` で選択します。`get_adcp_capabilities` で `creative.supports_transformers: true` を宣言するエージェントのみが提供します。 **要件:** * `creative.supports_transformers: true` を設定するクリエイティブエージェントは `list_transformers` を実装しなければなりません(MUST) * クリエイティブエージェントは、呼び出し元のアカウント向けにトランスフォーマー、その列挙可能なオプション値、価格を解決しなければなりません(MUST)——そのアカウント向けに設定されたカスタム値(例: クローンされた声)を含む * クリエイティブエージェントは、`expand_params` で名指しされた各 `field` について、アカウントスコープのオプション値を `params[].options[]` にインラインで返さなければならず(MUST)、それ以外では省略すべきです(SHOULD) * `include_pricing` が true の場合、課金するクリエイティブエージェントは各トランスフォーマーに `pricing_options`(`per_unit` モデル)を含めなければなりません(MUST) ### build\_creative **リファレンス**: [`build_creative` タスク](/docs/creative/task-reference/build_creative) クリエイティブマニフェストを変換、生成、または取得します。3つのモードをサポートする: 1. **生成**: ブリーフまたはシードアセットからマニフェストを作成します 2. **変換**: 既存のマニフェストを別のフォーマットに適応させる 3. **ライブラリ取得**: エージェントのライブラリから `creative_id` を解決し、広告配信アセット(HTML/JavaScript/VAST タグ)を含むマニフェストを返す **要件:** * クリエイティブエージェントはフォーマット要件に対して入力マニフェストを検証しなければなりません (MUST) * クリエイティブエージェントは成功時にターゲットフォーマットの有効なマニフェストを返さなければなりません (MUST) * クリエイティブエージェントは変換が完了できない場合に検証エラーを返さなければなりません (MUST) * クリエイティブエージェントは変換中にトラッキング URL とマクロを保持すべきだ (SHOULD) * クリエイティブエージェントはジェネレーティブタスクの `quality` を尊重すべきだ (SHOULD)(`"draft"` は高速反復、`"production"` は最終配信)。非ジェネレーティブ変換では無視してもよい (MAY) * クリエイティブエージェントは `item_limit` が存在する場合、`item_limit` とフォーマットの `max_items` の小さい方を使用すべきだ (SHOULD) * クリエイティブエージェントは生成タスクに AI/LLM 処理を使用してもよい (MAY) * `creative_id` が提供された場合、クリエイティブエージェントはライブラリからクリエイティブを解決しなければなりません (MUST) * `macro_values` が提供された場合、クリエイティブエージェントは出力マニフェストのアセット内で指定されたマクロを代入し、未解決のマクロを `{MACRO}` プレースホルダーとして残すべきだ (SHOULD) * クリエイティブエージェントは `macro_values` の未認識のマクロキーを無視しなければなりません (MUST) — 未知のマクロはエラーではありません * クリエイティブエージェントはグローバルに一意な `creative_id` 値を割り当てるべきだ (SHOULD)。一意性を保証できない場合、`concept_id` は `build_creative` リクエストで曖昧さを解消するために REQUIRED だ * `build_creative` は重大な時間がかかる生成および変換タスクに対して非同期レスポンス(`context_id` ポーリングを持つ `status: "working"`)をサポートします。ライブラリ取得は通常同期的です * `account` が提供されエージェントが課金する場合、レスポンスは `pricing_option_id`、`vendor_cost`、`currency` を含めなければなりません(MUST)。`consumption` オブジェクトは関連する場合に含めるべきです(SHOULD) * 非同期ビルドでは、価格フィールドは中間のステータスレスポンスではなく、最終的な完了レスポンスにのみ現れます * 課金するクリエイティブエージェントが `account` なしで `build_creative` リクエストを受け取り、そのエージェントがアカウントを必要とする場合、エージェントはエラーを返さなければなりません(MUST) ### preview\_creative **リファレンス**: [`preview_creative` タスク](/docs/creative/task-reference/preview_creative) クリエイティブマニフェストのプレビューレンダリングを生成します。 **要件:** * クリエイティブエージェントはプレビュー生成前にマニフェストを検証しなければなりません (MUST) * クリエイティブエージェントは有効なマニフェストのプレビュー URL または HTML を返さなければなりません (MUST) * クリエイティブエージェントは、プレビュー URL をその `expires_at` タイムスタンプまで参照解決可能に保たなければなりません(MUST)。`expires_at` が省略された場合、プレビュー URL はプロトコル層では期限切れにならず、エージェントが帯域外で明示的に失効させるまで有効なままです。 * クリエイティブエージェントは、時間制限付きのプレビュー URL には `expires_at` を含めるべきだ (SHOULD) * クリエイティブエージェントは複数のクリエイティブのバッチプレビューをサポートすべきだ (SHOULD) * クリエイティブエージェントは複数の出力フォーマット(URL、HTML、画像)をサポートしてもよい (MAY) ### list\_creatives **スキーマ**: [`creative/list-creatives-request.json`](https://adcontextprotocol.org/schemas/v3/creative/list-creatives-request.json) / [`creative/list-creatives-response.json`](https://adcontextprotocol.org/schemas/v3/creative/list-creatives-response.json) **リファレンス**: [`list_creatives` タスク](/docs/creative/task-reference/list_creatives) クリエイティブライブラリ内のクリエイティブアセットを閲覧・フィルタリングします。クリエイティブライブラリをホストするすべてのエージェント — 広告サーバー、クリエイティブ管理プラットフォーム、クリエイティブを管理するセールスエージェント — が実装します。 **要件:** * エージェントは認証済みアカウントからアクセス可能なクリエイティブを返さなければなりません (MUST) * エージェントは各クリエイティブの承認ステータスを含めなければなりません (MUST) * エージェントはフォーマット、ステータス、タグ、日付範囲によるフィルタリングをサポートすべきだ (SHOULD) * プラットフォームがクリエイティブをコンセプトに整理する場合、エージェントは `concept_ids` と `format_ids` によるフィルタリングをサポートすべきだ (SHOULD) * エージェントは `include_variables=true` の場合にダイナミックコンテンツ変数定義を含めてもよい (MAY) * エージェントは `include_snapshot=true` の場合に軽量な配信スナップショットを含めてもよい (MAY)。スナップショットは「このクリエイティブはアクティブか?」「最後にいつ配信されたか?」などの運用上の質問のためにライフタイムインプレッションと最終配信日時を提供する — 詳細分析は `get_creative_delivery` が担う * `account` と `include_pricing=true` が提供された場合、課金するエージェントは各クリエイティブに `pricing_options`——[`vendor-pricing-option`](https://adcontextprotocol.org/schemas/v3/core/vendor-pricing-option.json) オブジェクトの配列——を含めなければなりません(MUST)。ベンダーはクリエイティブごとに複数のオプションを提供できます(ボリュームティア、コンテキスト固有のレート、異なる価格モデル)。 **アカウント要件:** * サービスに課金するクリエイティブエージェントは、[Accounts プロトコル](/docs/accounts/overview)を実装しなければなりません(MUST)。これは価格を持つ任意のクリエイティブエージェントに適用されます——アドサーバー、生成プラットフォーム、使用に課金する変換エージェント。 * ライブラリをホストするが課金しないクリエイティブエージェントは、バイヤーがクエリ前にアクセスを確立できるよう Accounts プロトコルを実装すべきだ(SHOULD)。 * これはセールスエージェントが使用するのと同じ accounts プロトコルだ — 別バージョンはない。 * メディアバイのために accounts を既に実装しているセールスエージェントは追加対応不要です。 ### sync\_creatives **スキーマ**: [`creative/sync-creatives-request.json`](https://adcontextprotocol.org/schemas/v3/creative/sync-creatives-request.json) / [`creative/sync-creatives-response.json`](https://adcontextprotocol.org/schemas/v3/creative/sync-creatives-response.json) **リファレンス**: [`sync_creatives` タスク](/docs/creative/task-reference/sync_creatives) ライブラリにクリエイティブアセットをアップロードして同期します。クリエイティブライブラリをホストするすべてのエージェント — 広告サーバー、クリエイティブ管理プラットフォーム、クリエイティブを管理するセールスエージェント — が実装します。 **要件:** * エージェントはフォーマット仕様に対してクリエイティブを検証しなければなりません (MUST) * エージェントは非準拠クリエイティブの検証エラーを返さなければなりません (MUST) * エージェントはクリエイティブが使用可能になる前に承認を要求してもよい (MAY) * エージェントは変更を適用せずに検証するための `dry_run` をサポートすべきだ (SHOULD) * エージェントは `delete_missing: true` と `creative_ids` を組み合わせるリクエストを拒否しなければなりません (MUST) — `delete_missing` はライブラリ全体に適用され、フィルタされたサブセットには適用されない * メディアバイも管理するエージェントは一括クリエイティブ-パッケージマッピングのための `assignments` フィールドをサポートすべきだ (SHOULD) * メディアバイを管理しないスタンドアロンクリエイティブエージェントは `assignments` フィールドを無視すべきだ (SHOULD) ### get\_creative\_delivery **リファレンス**: [`get_creative_delivery` タスク](/docs/creative/task-reference/get_creative_delivery) バリアントレベルのメトリクスを含むクリエイティブ配信データを取得します。 **要件:** * エージェントはリクエストされたクリエイティブの配信データを返さなければなりません (MUST) * エージェントは利用可能な場合にバリアントレベルの内訳を含めるべきだ (SHOULD) * クリエイティブプロトコルを実装するセールスエージェントは、自身のプロダクトがクリエイティブバリアントを生成または最適化する場合にこのタスクをサポートすべきだ (SHOULD) ## エラー処理 クリエイティブエージェントは[標準 AdCP エラースキーマ](/docs/building/by-layer/L3/error-handling)を使用してエラーを返さなければなりません (MUST)。 一般的なエラーコード: * `REFERENCE_NOT_FOUND`: リクエストされたフォーマットが存在しない、またはアクセスできない(`error.field` が `format_id` を特定する) * `VALIDATION_ERROR`: マニフェストがフォーマット検証に失敗しました * `ASSET_MISSING`: 必須アセットがマニフェストに提供されていません * `ASSET_INVALID`: アセットがフォーマット制約を満たさない * `GENERATION_FAILED`: クリエイティブ生成を完了できなかった ## セキュリティの考慮事項 ### トランスポートセキュリティ すべてのクリエイティブプロトコル通信は TLS 1.2 以上を使用した HTTPS を使用しなければなりません (MUST)。 ### アセットセキュリティ * クリエイティブエージェントはアセット URL がアクセス可能であることを検証すべきだ (SHOULD) * クリエイティブエージェントはマルウェアと悪意あるコンテンツのためにアセットをスキャンすべきだ (SHOULD) * クリエイティブエージェントは検証中に信頼されていない JavaScript を実行してはなりません (MUST NOT) ### プレビューセキュリティ * プレビュー URL は時間制限があるべきだ (SHOULD)(`expires_at` で示されます) * プレビュー URL は、エージェントが URL の表明されたライフタイムにわたってその状態を保証できない限り、ポッドローカルまたはプロセスローカルの状態に依存してはなりません (MUST NOT) * クリエイティブエージェントはスクリプト実行を防ぐために HTML プレビューをサンドボックス化すべきだ (SHOULD) * `output_format: "html"` の消費者は信頼されたクリエイティブエージェントのみを使用しなければなりません (MUST) ## 適合性 ### クリエイティブエージェントの適合性 適合するクリエイティブプロトコルエージェントは以下を満たさなければなりません (MUST): 1. 指定されたトランスポート(MCP または A2A)のうち少なくとも1つをサポートします 2. フォーマット発見のための `list_creative_formats` を実装します 3. 自身が所有するフォーマットの権威あるフォーマット定義のみを返す 4. フォーマット仕様に対してマニフェストを検証します 5. 指定されたエラーコードを使用します 適合するクリエイティブプロトコルエージェントは以下を満たすべきだ (SHOULD): 1. クリエイティブ生成のための `build_creative` を実装します 2. プレビューレンダリングのための `preview_creative` を実装します 3. トラッキング URL でユニバーサルマクロをサポートします 4. エージェントがクリエイティブライブラリをホストする場合、`list_creatives` を実装します 5. エージェントがクリエイティブアップロードを受け入れる場合、`sync_creatives` を実装します 6. エージェントがクリエイティブライブラリをホストする場合、`build_creative` で `creative_id` をサポートします 7. クリエイティブライブラリをホストする場合、accounts プロトコル(`sync_accounts` / `list_accounts`)を実装します 8. バイヤーが正しいインタラクションモデルを判断できるよう `get_adcp_capabilities` で `supports_generation`、`supports_transformation`、`has_creative_library` を宣言します ### コンシューマの適合性 適合するクリエイティブプロトコルコンシューマは以下を満たさなければなりません (MUST): 1. フォーマット ID の `agent_url` を使用して権威あるクリエイティブエージェントを識別します 2. 提出前にフォーマット仕様に対してマニフェストを検証します 3. 検証エラーを適切に処理します 4. 無限ループを避けるためにフォーマットを再帰的に発見する際に訪問済み URL を追跡します ## 実装ノート ### レスポンスタイムの期待値 クリエイティブエージェントは以下のレスポンスタイムを目標とすべきだ (SHOULD): | 操作タイプ | 目標レイテンシ | | ------------------------------------- | ------- | | フォーマットリスティング(list\_creative\_formats) | 1秒未満 | | ライブラリクエリ(list\_creatives) | 1秒未満 | | クリエイティブ同期(sync\_creatives) | 5秒未満 | | プレビュー生成(preview\_creative) | 5秒未満 | | バッチプレビュー(10クリエイティブ) | 10秒未満 | | クリエイティブ生成(build\_creative) | 60秒未満 | ### 再帰的フォーマット発見 クリエイティブエージェントは `list_creative_formats` レスポンスで他のクリエイティブエージェントを参照してもよい (MAY): ```json theme={null} { "creative_agents": [{ "agent_url": "https://creative.adcontextprotocol.org", "agent_name": "AdCP Reference Creative Agent", "capabilities": ["validation", "assembly", "preview"] }] } ``` コンシューマは参照されたエージェントを再帰的にクエリして追加フォーマットを発見してもよい (MAY)。 コンシューマは再帰的発見中の無限ループを防ぐために訪問済み URL を追跡しなければなりません (MUST)。 ### フォーマット対応検証 マニフェスト検証はフォーマット仕様のコンテキストで実行されなければなりません (MUST): 1. 権威あるクリエイティブエージェントからフォーマット定義を検索します 2. マニフェストの各アセットについて、フォーマットの `assets` 配列内の対応するエントリを見つける 3. フォーマットで定義されたタイプと制約に対してアセット値を検証します フォーマット定義が各 asset\_id のタイプを決定します。アセットタイプ情報はマニフェスト自体には含まれない。 ### 標準フォーマットとカスタムフォーマット * **標準フォーマット**: IAB 仕様に基づき、リファレンスクリエイティブエージェント(`https://creative.adcontextprotocol.org`)がホスト * **カスタムフォーマット**: 特殊なインベントリのために個別のパブリッシャーやクリエイティブプラットフォームが定義 両方とも同じように機能する — `agent_url` フィールドが各フォーマットに対してどのエージェントが権威あるかを識別します。 ## スキーマリファレンス 一部のクリエイティブプロトコルスキーマ(`build_creative`、`list_creative_formats`、`preview_creative`)は、もともとメディアバイプロトコルの一部としてリリースされたため、`media-buy/` 以下にパスがあります。スキーマパスは安定した識別子であり、タスクが属するプロトコルには影響しません。 | スキーマ | 説明 | | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | | [`core/format.json`](https://adcontextprotocol.org/schemas/v3/core/format.json) | フォーマット定義 | | [`core/creative-manifest.json`](https://adcontextprotocol.org/schemas/v3/core/creative-manifest.json) | クリエイティブマニフェスト | | [`core/creative-asset.json`](https://adcontextprotocol.org/schemas/v3/core/creative-asset.json) | アセット定義 | | [`media-buy/list-creative-formats-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/list-creative-formats-request.json) | list\_creative\_formats リクエスト | | [`media-buy/list-creative-formats-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/list-creative-formats-response.json) | list\_creative\_formats レスポンス | | [`creative/list-creatives-request.json`](https://adcontextprotocol.org/schemas/v3/creative/list-creatives-request.json) | list\_creatives リクエスト | | [`creative/list-creatives-response.json`](https://adcontextprotocol.org/schemas/v3/creative/list-creatives-response.json) | list\_creatives レスポンス | | [`creative/sync-creatives-request.json`](https://adcontextprotocol.org/schemas/v3/creative/sync-creatives-request.json) | sync\_creatives リクエスト | | [`creative/sync-creatives-response.json`](https://adcontextprotocol.org/schemas/v3/creative/sync-creatives-response.json) | sync\_creatives レスポンス | # Sponsored Placement アダプターコントラクト Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/sponsored-placement-adapter-contracts 単一の sponsored_placement 正準の下で出荷される 4 つのランタイムコントラクトファミリー — Amazon SP、Criteo/CitrusAd、Pinterest/Snap Collection、generative-per-SKU。 `sponsored_placement` 正準は、リテールメディアのカタログ駆動クリエイティブ — Amazon SP、Criteo SP、CitrusAd SP、Pinterest Collection、generative-per-SKU — をカバーします。スキーマはバイナリ軸(`composition_model`、`fanout_mode`、`item_production_model`)を捕捉しますが、ランタイムコントラクト — ファンアウトセマンティクス、カタログアセット配線、アダプターごとのトラッキング — はアダプターファミリー間で十分に異なるため、`sponsored_placement` に対して検証されるマニフェストは、それが特定のリテールメディアネットワークで実際にサーブするかどうかについてバイヤーにほとんど伝えません。 このページは、4 つのコントラクトファミリー、その現在の実験的準備状況、スキーマに存在しないアダプター固有の癖を文書化します。正準は、少なくとも 2 つのリテールメディアネットワークが `format_schema` 証拠を出荷するまで、3.1 GA を過ぎても `experimental: true` のままです([`canonical-formats.mdx` §`experimental` — 1 フィールド、両軸](/docs/creative/canonical-formats#experimental--one-field-both-axes) を参照)。 ## 4 つのコントラクトファミリー | Family | `composition_model` | `fanout_mode` | `item_production_model` | Nondeterminism flag | Adopters | | -------------------------- | ------------------------ | ------------------------ | -------------------------------- | ---------------------------------- | --------------------------------------------- | | **リテールメディア決定的、バイヤーアップロード** | `deterministic` | `per_item` | `buyer_uploaded` | Not set | Amazon SP | | **リテールメディアネットワーク合成** | `deterministic` | `per_item` | `seller_pre_rendered_from_brief` | Not set | Criteo SP、CitrusAd SP | | **インプレッションごとコレクションレイアウト** | `deterministic`(議論の余地あり) | `multi_item_in_creative` | `buyer_uploaded` | Not set | Pinterest Collection、Snap Collection | | **Generative-per-SKU** | `deterministic` | `per_item` | `agent_synthesized` | `synthesis_nondeterministic: true` | ベンダー固有(Pencil、Persado、バーティカルリテールメディアプラットフォーム) | ### 1. リテールメディア決定的、バイヤーアップロード(Amazon SP) バイヤーは `sync_creatives` 経由でカタログアセット(製品画像、ヘッドライン、説明)をアップロードします。リテールメディアネットワークはカタログ行 + アセットを取り込み、クエリ/カテゴリートリガーに対して決定的にサーブします — レンダー時のファンアウトなし。バイヤーはインプレッションごとのレンダリングを予測できます。 * **カタログアセットコントラクト:** バイヤーはカタログアイテムごとに 1 クリエイティブを供給。`source_catalog` スロットはバイヤーの製品フィードを指す。アイテムごとのアセットが必要。 * **トラッキング:** Amazon ASIN + 標準リテールメディアイベント語彙。アイテムごとのアトリビューションはカタログ行の識別子にマップ。 * **アダプターの癖:** Amazon のカテゴリートリガーシステムは、同じアップロードされたクリエイティブが異なるカテゴリーコンテキストの下でサーブされうることを意味します — バイヤーはインプレッションレベルのカテゴリーをクリエイティブ形状によって信頼できないものとして扱わなければなりません(MUST)。カテゴリーアトリビューションには Amazon のレポートに依存します。 * **実験的準備状況:** サンドボックスと制御されたアダプター使用に十分予測可能。最も予測可能なファミリー。Amazon からの `format_schema` 証拠は非実験的への昇格をサポートするでしょう。 ### 2. リテールメディアネットワーク合成(Criteo SP、CitrusAd SP) バイヤーはブリーフ + ブランドアセット(ロゴ、ブランドカラー)を供給します。リテールメディアネットワークは自身のカタログ + デザインテンプレートに対してプレースメントを合成します。バイヤーはアセットバンドルレベルではインプレッションごとのレンダリングを予測できず、ブランドディレクションレベルでのみ予測できます。 * **カタログアセットコントラクト:** バイヤーはブリーフ + ブランドアセットを供給。`source_catalog` は *セラーの* カタログ(リテールメディアネットワークの製品インデックス)で、バイヤーのものではありません。バイヤーの製品参照は SKU / GTIN / カテゴリー経由でセラーカタログ行に一致されます。 * **トラッキング:** リテールメディアネットワーク自身のイベント語彙。バイヤーアトリビューションは直接クリエイティブイベントではなくネットワークのレポート API を通じてマップ。 * **アダプターの癖:** Criteo の合成ロジックはリテーラー統合によって変わります — 同じバイヤーブリーフが Criteo 経由で Walmart 対 Target 対 Best Buy で異なるプレースメントを生成します。バイヤーはリテーラーごとのレンダリングを不透明として扱い Criteo の集計レポートに依存しなければなりません(MUST)。 * **実験的準備状況:** 制御されたアダプター使用に適する。合成の不透明性が非実験的昇格へのギャップです — `format_schema` 証拠がリテーラーごとの変動性を文書化する必要があります。 ### 3. インプレッションごとコレクションレイアウト(Pinterest Collection、Snap Collection) バイヤーはヒーロー画像 + カタログアイテムのコレクションを供給します。表面はインプレッションごとにコレクションレイアウト(グリッド、カルーセル、カバー + リビール)を選びます。`composition_model: deterministic` はアセットレベルでは技術的に真ですが、レイアウトレベルでは緩いです。 * **カタログアセットコントラクト:** バイヤーはヒーローアセットスロット + アイテム配列(各アイテム: 画像 + キャプション + URL)を持つ 1 クリエイティブを供給。`source_catalog` スロットはバイヤーのキュレートされたアイテムコレクションで、完全な製品フィードではありません。 * **トラッキング:** Pinterest のコレクションアイテムイベント(`collection_item_click`、`collection_close_up`)プラス標準インプレッション/クリック。アイテムごとのアトリビューション利用可能。レイアウトバリアントアトリビューションは公開されません。 * **アダプターの癖:** Snap Collection の「カバー + リビール」レイアウトは特定のアスペクト比とアセット数の組み合わせを要求します — スキーマは強制しません。バイヤーは同期前に事前検証すべきです(SHOULD)。 * **実験的準備状況:** 制御されたアダプター使用に適する。「決定的」クレームはアセットレベルで生き残りますが、レイアウト変動性が、表面ごとの適合性証拠なしの非実験的昇格をブロックします。 ### 4. Generative-per-SKU(ベンダー固有) バイヤーはブリーフ + カタログ参照を供給します。セラーの生成パイプラインが SKU ごとにユニークなクリエイティブを生成します。1 ブリーフ × N カタログアイテム → N レンダーされたクリエイティブ。`composition_model` は `deterministic` のままです。なぜなら各レンダーされたアイテムは配信前に生成され、次に生成されたとおりにサーブされるからです。生成メカニズムは `item_production_model: "agent_synthesized"` で捕捉されます。 * **カタログアセットコントラクト:** バイヤーはブリーフ + カタログスコープ(SKU、カテゴリー、または完全なフィード)を供給。セラーは `item_production_model: agent_synthesized` と `fanout_mode: per_item` で `build_creative` 経由でクリエイティブを生成。`source_catalog` スロットはバイヤーの製品フィード。アセットはセラー生成。 * **非決定性:** このファミリーの製品は、QA ループが完了する前に生成がスペック内出力を保証できないとき `synthesis_nondeterministic: true` を宣言すべきです(SHOULD)。 * **トラッキング:** 生成出力 ID + バイヤーのカタログアイテムキー(SKU/GTIN)。バイヤーは各生成されたクリエイティブを、ブリーフのコピーではなく、自身のパフォーマンスシグナルを持つ別個のレンダー出力として扱うべきです(SHOULD)。 * **アダプターの癖:** 生成シードとプロンプトは通常バイヤーに返されません。バイヤーは特定の生成を再現できません。アセット編集サイクルではなく再生成サイクルを計画します。 * **実験的準備状況:** 生成品質証拠がアダプターごとに文書化されるまで `experimental: true` のままにします。制御された使用は可能ですが、合成はバイヤーに不透明でベンダーごとのフレーミングが必要です。 ## 実験的準備状況マトリクス | Family | 制御されたアダプター使用 | デフォルト本番ルーティング | 昇格証拠 | | ---------------------- | ------------ | --------------- | ------------------------------ | | リテールメディア決定的、バイヤーアップロード | 準備完了 | 証拠が到達するまで実験的を保つ | Amazon `format_schema` 証拠 | | リテールメディアネットワーク合成 | 準備完了 | 証拠が到達するまで実験的を保つ | リテーラーごとの変動性文書 | | インプレッションごとコレクションレイアウト | 準備完了 | 証拠が到達するまで実験的を保つ | 表面ごとの適合性証拠 | | Generative-per-SKU | ベンダー固有のみ | 実験的を保つ | 生成品質証拠。ベンダーごとのフレーミングなしに昇格しないかも | `experimental` は正準と製品宣言の両方に存在します。フィールドレベルコントラクトについては [`canonical-formats.mdx` §`experimental` — 1 フィールド、両軸](/docs/creative/canonical-formats#experimental--one-field-both-axes) を参照してください。 ## このページが何でないか * **規範的仕様拡張ではない。** 4 つのファミリーは既存の `sponsored_placement` スキーマを共有します。この文書は、セラーとバイヤーが正準に対して遭遇する変動性を名指しますが、必須フィールドを追加しません。 * **昇格ゲートではない。** 正準の非実験的への昇格は、[canonical-formats.mdx](/docs/creative/canonical-formats) に従い、少なくとも 2 つのアダプター全体の `format_schema` 証拠にゲートされます。このページは `format_schema` 証拠が各ファミリーについて何をカバーする必要があるかを文書化します。 * **網羅的ではない。** ランタイムコントラクトが上の 4 つのファミリーの 1 つに適合しないアダプターは、マトリクスを拡張できるよう canonical-formats トラックに issue を提出すべきです(SHOULD)。 ## 参照 * [#3307](https://github.com/adcontextprotocol/adcp/pull/3307) — 元の `sponsored_placement` 正準 * [#4592](https://github.com/adcontextprotocol/adcp/issues/4592) — この文書 * [`canonical-formats.mdx`](/docs/creative/canonical-formats) — 全体の canonical-formats 語彙 # build_creative Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/task-reference/build_creative build_creative は AdCP において、自然言語のブリーフからプロダクション対応のアセットまで、広告クリエイティブマニフェストを生成・変換・取得します。 特定のフォーマット向けのクリエイティブマニフェストを変換・生成・取得します。結果のマニフェストの視覚的なプレビューをレンダリングするには、それを [`preview_creative`](/docs/creative/task-reference/preview_creative) に渡します。3 つのモードをサポートします。 1. **生成(Generation)**: ブリーフまたはシードアセットからマニフェストを作成する(`message` + `creative_manifest`) 2. **変換(Transformation)**: 既存のマニフェストを別のフォーマットに適応させる(`creative_manifest` + `target_format_id`) 3. **ライブラリ取得(Library retrieval)**: エージェントのライブラリから `creative_id` を解決し、広告配信アセット付きのマニフェストを生成します 生成および変換では、`build_creative` はクリエイティブマニフェストを入力として受け取り、クリエイティブマニフェストを出力します。ライブラリ取得では、[`list_creatives`](/docs/creative/task-reference/list_creatives) で取得した `creative_id` を指定すると、エージェントがライブラリから解決します。 フォーマット ID とフォーマットの参照方法については、[クリエイティブフォーマット - フォーマットの参照](/docs/creative/formats#referencing-formats)を参照。 ## リクエストパラメーター | パラメーター | 型 | 必須 | 説明 | | ------------------------------ | ------------------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `message` | string | No | クリエイティブエージェントへの自然言語の指示。生成時はクリエイティブ方向性を提供します。変換時はクリエイティブの適応方法をガイドします。リファインメント時は変更内容を記述します。 | | `creative_manifest` | object | No | 変換または生成の元となるクリエイティブマニフェスト([Creative Manifest](https://adcontextprotocol.org/schemas/v3/core/creative-manifest.json) を参照)。純粋な生成では、ターゲットの format\_id と必要な入力アセットを含めます。変換では適応させる完全なクリエイティブを指定します。`creative_id` が指定された場合、エージェントはライブラリからクリエイティブを解決し、このフィールドは無視されます。 | | `creative_id` | string | No | エージェントのライブラリ内のクリエイティブへの参照。クリエイティブエージェントはこれをライブラリのマニフェストに解決します。タグ生成やフォーマット変換で既存のクリエイティブを取得する場合、`creative_manifest` の代わりに使用します。 | | `concept_id` | string | No | クリエイティブを含むクリエイティブコンセプト。同じ `creative_id` が複数のコンセプトに存在する場合に曖昧さを解消するために使用します。 | | `media_buy_id` | string | No | タグ生成コンテキスト用のバイヤーのメディアバイ参照。クリエイティブエージェントが広告サーバーも兼ねる場合(CM360 など)、プレースメント固有のタグを生成するために必要なトラフィッキングコンテキストを提供します。プラットフォームがクリエイティブレベルでタグを生成する場合(Flashtalking、Celtra など)は省略します。これはバイヤーの参照であり、`create_media_buy` から得られるセラーが割り当てた識別子です。 | | `package_id` | string | No | メディアバイ内のバイヤーのパッケージまたはラインアイテム参照。クリエイティブエージェントがラインアイテムレベルのコンテキストを必要とする場合に `media_buy_id` とともに使用します。特定のパッケージにスコープされないタグを取得する場合は省略する(広告サーバーは同じタグを返す場合があります)。 | | `target_format_id` | object | Conditional | 生成する単一のフォーマット ID。`agent_url` と `id` フィールドを持つオブジェクト。3.1 の正準的なクリエイティブエージェントルーティングでは、`id` は表明された `creative.supported_formats[].capability_id` です。レガシーの名前付きフォーマット ID は移行期間中は引き続き受け入れられます。`target_format_ids` と相互に排他的であり、どちらか一方のみを指定すること。 | | `target_format_ids` | array | Conditional | 1 回の呼び出しで生成するフォーマット ID の配列。各要素は `agent_url` と `id` フィールドを持つオブジェクト。3.1 の正準的なクリエイティブエージェントルーティングでは、各 `id` は表明された `creative.supported_formats[].capability_id` です。レガシーの名前付きフォーマット ID は移行期間中は引き続き受け入れられます。`target_format_id` と相互に排他的であり、どちらか一方のみを指定すること。フォーマットごとに 1 つのマニフェストを返します。 | | `brand` | object | No | `domain` フィールドを持つブランド参照。`/.well-known/brand.json` 経由でブランドアイデンティティを解決します。ブランドレベルのコンテキスト(カラー、ロゴ、トーン)を提供します。 | | `quality` | string | No | 品質ティア: `"draft"`(反復のための高速・低忠実度)または `"production"`(最終納品のためのフル品質)。省略した場合、クリエイティブエージェントが独自のデフォルトを使用します。 | | `item_limit` | integer | No | **1 つのクリエイティブ内**で使用するカタログアイテムの最大数。カタログ駆動フォーマットの生成コストを抑制します(例: 1,000 プロダクトのカタログから作る 6 枚カードのカルーセル)。アイテムをまたいでファンアウトする `max_creatives` とは異なります。 | | `transformer_id` | string | No | ビルドを実行するトランスフォーマーを 1 つ選択します([`list_transformers`](/docs/creative/task-reference/list_transformers) で発見)。要求するターゲットフォーマットは、そのトランスフォーマーの `output_format_ids` の部分集合でなければなりません(MUST)。トランスフォーマーの `per_unit` レートがそのビルドの価格の源です。 | | `config` | object | No | 選択したトランスフォーマーの `params[].field` にキー付けされた型付き設定バッグ(例: `{ "voice": "isaac", "speaking_rate": 1.1 }`)。クリエイティブエージェントは、未知のキーと範囲外の値を、フィールドを特定したエラーで拒否しなければなりません(MUST、厳格なバリデーション)——宣言された params ではないベンダー固有のつまみは `ext` に入れます。`transformer_id` を伴う場合にのみ意味を持ちます。 | | `max_creatives` | integer | No | カタログのファンアウト軸: 最大 N 個の**別個のクリエイティブを、カタログアイテムごとに 1 つ**生成します(サンプル——例: 150 のうち 5)。単一のクリエイティブ*内*で使うアイテムを制限する `item_limit` とは異なります。`refine_from_build_variant_id` と相互排他的。`BuildCreativeVariantSuccess` のレスポンス形をトリガーします。エージェントが `creative.multiplicity.supports_catalog_fanout` を表明する場合にのみサポートされます。`max_creatives_limit` を超える値は拒否ではなく**クランプ**されます(不足は `items_returned` \< `items_total` で示されます)。 | | `signal_conditions` | array | No | シグナルのファンアウト軸: **シグナル条件ごとに** 1 つの別個のクリエイティブを生成し、それぞれが独自のターゲティングで保持・トラフィックされます(雨のクリエイティブ AND 晴れのクリエイティブ)。各項目は [SignalTargeting](/docs/signals/specification) です。`max_creatives` の兄弟であり、それと合成されます(カタログ × 条件)。このレイヤーでは助言的です([#5280](https://github.com/adcontextprotocol/adcp/issues/5280))——トラフィッキング互換性はセールス側で `SIGNAL_TARGETING_INCOMPATIBLE` により強制されます。エージェントが `creative.multiplicity.supports_signal_fanout` を表明する場合にのみサポートされます。`max_signal_conditions_limit` を超えるカウントは**クランプ**されます。[シグナルのファンアウト](#バリアントレスポンス)を参照。 | | `selection_strategy` | string | No | `max_creatives` \< 適格アイテム数のときにエージェントがどうサンプリングするか: `audience_relevance`(ユーザー側、同じ `signal_ref` ポインタでランク付け)、`contextual_fit`(コンテンツ側、コンテキストシグナルで同じ仕組み)、`performance`(過去のデリバリー)、`proximity`、`inventory_priority`(セラー側)、または `random`(デフォルト)。順序は `rank`/`recommended` で表れます。サポートされる集合は `creative.multiplicity.selection_strategies[]`。 | | `max_variants` | integer | No | **クリエイティブごとに**生成する代替レンダーの数(best-of-N)。デフォルト `1`。生成されたすべてのバリアントに対して課金され、1 つ以上を保持するのは別のトラフィッキングステップです。エージェントが `creative.multiplicity.supports_variants` を表明する場合にのみサポートされます。`max_variants_limit` を超える値はクランプされます。 | | `variant_axis` | object | No | バリアントが異なる次元を記述します。`dimension`(`voice` \| `theme` \| `best_of_n` \| `transformer_config` \| `custom`)、任意の `values[]`(軸に沿って列挙する明示的な値)、任意の `field`(スイープする `config` param——`dimension` が `transformer_config` のとき必須)、任意の `label` を持つオブジェクト。 | | `keep_mode` | string | No | 何個のバリアントを保持する意図かをエージェントに伝える助言的ヒント: `"keep_all"`、`"keep_one"`、または `"keep_some"`。デフォルト `"keep_all"`。助言のみ——生成されたすべてのバリアントに対して課金されます。レスポンスは `keep_mode_applied` をエコーするので、ヒントが受け取られた確認になります(課金紛争のための監査証跡)。 | | `evaluator` | object | No | **実験的**([ステータス](/docs/reference/experimental-status)、機能 id `creative.evaluator`)。エージェントの best-of-N に対して**ゲート後にランク付け**するパイプラインを駆動する助言的な評価器([Evaluator Spec](https://adcontextprotocol.org/schemas/v3/core/evaluator-spec.json)): 1 つのソース形式(`exemplars` / アカウントで手配された `evaluator_id` / `agent_url`)、任意のハードな `feature_requirement[]` **ゲート**(不合格は破棄——推奨リーフの内部的な刈り込みであり、すでに生成された課金対象リーフをブロックすることはない)、明示的な `rank_by` の順序(`[{feature_id, direction: maximize\|minimize}]`)、許可リストに載った `feature_agent` ポインタ。機能の発見は [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) の `governance.creative_features` を使い、評価器は同じ機能 ID を `variants[].eval.features[]` で返します。`evaluator_id` はそのカタログからは発見されず、事前プロビジョニングされたアカウントプリセットです。外部エージェント(`feature_agent.agent_url` / `agent_url`)はセラーの `creative_policy.accepted_verifiers[]` に含まれていなければなりません(MUST)——リスト外は `EVALUATOR_AGENT_NOT_ACCEPTED`。`creative.supports_evaluator` が必要で、なければ無視されます。リーフごとの `variants[].eval` ブロックを埋めます。[`variants[].eval`](#フィールド説明) と [評価器の認証](#評価器の認証)を参照。 | | `refine_from_build_variant_id` | string | No | 以前に生成したバリアントをリファインします: その `build_variant_id` から、`message` の自然言語指示と任意の `config` デルタを適用して再ビルドし、**新しい**リネージ連結されたバリアントを返します(変更ではない)。`transformer_id` とターゲットフォーマットは親から継承されます。`max_variants`/`variant_axis` と合成されます。`max_creatives` とは相互排他的。`creative.supports_refinement` が必要——なければ `UNSUPPORTED_FEATURE`。未知の、または保持されなくなった参照は `REFERENCE_NOT_FOUND`(`error.field` = `refine_from_build_variant_id`)。[リファインメント](#リファインメント)を参照。 | | `mode` | string | No | `"execute"`(デフォルト)は生成して課金します。`"estimate"` は**ドライラン**——何も生成せず課金もせず、このリクエストの入力に対して計算された `BuildCreativeEstimate` のコスト帯(`cost_low`/`cost_high`)を返します。`creative.supports_spend_controls` が必要。[支出コントロール](#支出コントロール)を参照。 | | `max_spend` | object | No | 呼び出しごとのハードな支出上限 `{ amount, currency }`。エージェントは、次のリーフが `amount` を超えるまでリーフを生成し、その後停止して `budget_status: "capped"` の部分結果を返します。`creative.supports_spend_controls` が必要。1 回の呼び出しを上限とします——リファインメントループの制限はバイヤー側で行います。[支出コントロール](#支出コントロール)を参照。 | | `include_preview` | boolean | No | true の場合、マニフェストと同時にプレビューレンダリングをリクエストします。これをサポートするエージェントはレスポンスに `preview` オブジェクトを返します。サポートしないエージェントは単純に省略します。 | | `preview_inputs` | array | No | `include_preview` が true の場合のプレビュー生成用入力セット。各エントリに `name`(必須)、オプションの `macros`、オプションの `context_description` を持ちます。省略した場合、エージェントは単一のデフォルトプレビューを生成します。`target_format_id`(シングルフォーマット)でのみサポートされ、マルチフォーマットリクエストでは無視されます。 | | `preview_quality` | string | No | インラインプレビューのレンダー品質: `"draft"` または `"production"`。ビルドの `quality` とは独立しています。ドラフトでビルドしてプロダクション品質でプレビューすることも、その逆も可能。`include_preview` が true の場合のみ使用されます。 | | `preview_output_format` | string | No | プレビューレンダリングの出力フォーマット: `"url"`(デフォルト)または `"html"`。`include_preview` が true の場合のみ使用されます。 | | `macro_values` | object | No | 出力マニフェストのアセットに事前置換するマクロ値。キーはユニバーサルマクロ名(例: `CLICK_URL`、`CACHEBUSTER`)で、値はリテラルの置換文字列。クリエイティブエージェントはユニバーサルマクロをプラットフォームのネイティブ構文に変換します。ここで指定されないマクロは、セールスエージェントが配信時に解決するための `{MACRO}` プレースホルダーとして残る。 | | `account` | [AccountRef](/docs/building/by-layer/L2/accounts-and-agents#account-references) | No | 価格と請求のためのアカウント参照。存在する場合、クリエイティブエージェントはレートカードからアカウント固有の価格を適用し、ビルドをアカウントに対して記録し、クォータを強制できます。サービスに課金するクリエイティブエージェントでは必須です。 | | `push_notification_config` | object | No | `build_creative` が `submitted` を返すときの、非同期の終端の完了/失敗通知のための操作スコープのウェブフック設定。submitted タスクは、このフィールドの有無に関わらず `get_task_status` でポーリング可能です。エージェントは、このフィールドが存在するというだけの理由で `submitted` を返してはなりません(MUST NOT)。 | ### 価格レスポンスフィールド クリエイティブエージェントが課金し `account` が提供された場合、レスポンスには価格フィールドが含まれます: | フィールド | 型 | 説明 | | ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `pricing_option_id` | string | どのレートカードの価格オプションが適用されたか | | `vendor_cost` | number | このビルドで発生したコスト。コストが配信時に発生する CPM 価格のクリエイティブでは 0 の場合があります。 | | `currency` | string | ISO 4217 通貨コード | | `consumption` | object | 構造化された消費の詳細——`tokens`、`images_generated`、`renders`、`duration_seconds`。[`creative-consumption.json`](https://adcontextprotocol.org/schemas/v3/core/creative-consumption.json)を参照。コスト検証のための情報提供であり、`vendor_cost` が請求の信頼できる情報源です。 | 非同期ビルド(`context_id` ポーリングを伴う `status: "working"`)では、価格フィールドは最終的な完了レスポンスにのみ現れます。 ### レシピのアイデンティティ エージェントは、シングルフォーマットの成功レスポンスでトップレベルの `recipe_hash` を返してもよく(MAY)、多重度/リファインメントのリーフで `variants[].recipe_hash` を返してもよい(MAY)。マルチフォーマットの `creative_manifests[]` レスポンスは、このリビジョンでは `recipe_hash` を運びません。出力ごとのレシピアイデンティティが必要な場合はバリアント形(`max_variants`)を要求してください。この値は、ビルドを決定する入力に対する ETag スタイルのアイデンティティです: エージェントが計算し、不透明で、そのエージェントにスコープされます。バイヤーは同じエージェントからのレスポンス間でのみ比較できます。 `recipe_hash` は、出力バイトや法的/開示のエンベロープではなく、入力レシピを識別します。非決定的なビルドは、同じ `recipe_hash` で異なるピクセル、タグ、バリアントを返すことがあります。この値は「同じクリエイティブ」ではなく「同じ指示」を意味します。ファンアウトのレスポンスでは、同じレシピから生成された best-of-N リーフは同じ値を共有すべきです(SHOULD)。クライアントが共有ソースで代替をグループ化できるようにするためです。`recipe_hash` と `build_variant_id` の両方が現れる場合、`build_variant_id` が出力リーフとリネージを識別し、`recipe_hash` は入力レシピを識別します。どちらも他方を含意しません。ビルドからデリバリーへのパフォーマンスの結合には、依然として `recipe_hash` 単独ではなく、トラフィッキングを生き延びるリネージ識別子を使います。 **重要**: 必須の入力アセットは、個別のタスクパラメーターとしてではなく、`creative_manifest.assets` オブジェクトに含めること。フォーマット定義が必要なアセットを指定します。ダイナミッククリエイティブのカタログコンテキストは `creative_manifest.assets` マップ経由で提供すること。 ### 評価器の認証 `build_creative.evaluator` は評価器を選択または校正します。評価器の呼び出しを認証するものではありません。評価器の API キー、ベアラートークン、クライアントシークレット、`Authorization` の値、JWK、JWKS ドキュメント、JWKS URI を、`evaluator`、`context`、`ext`、その他のペイロードフィールドに入れないでください。クレデンシャルまたは信頼素材のペイロードキーは非準拠であり、[`CREDENTIAL_IN_ARGS`](/docs/building/by-layer/L3/error-handling#authentication-and-access)で拒否されるべきです。 `evaluator.agent_url` または `evaluator.feature_agent.agent_url` が外部の評価器を指す場合、生成を行うクリエイティブエージェントは、通常の AdCP トランスポート認証チャネルを使って、その評価器の [`get_creative_features`](/docs/governance/creative/get_creative_features) エンドポイントを呼びます。評価器は、[RFC 9421 のリクエスト署名](/docs/building/by-layer/L1/security#request-signing)と JWKS ディスカバリー、mTLS、または事前プロビジョニングされた Bearer/API キークレデンシャルを介して、呼び出し元としてのクリエイティブ/セラーエージェントを認証します。`agent_url`、`account`、`context`、`ext` のようなペイロードフィールドはアイデンティティの主張ではなく、クレデンシャルとして扱ってはなりません。 許可リストのチェックが依然として最初に行われます: `creative_policy.accepted_verifiers[]` にない外部評価器の URL は、いかなるアウトバウンド呼び出しの前に `EVALUATOR_AGENT_NOT_ACCEPTED` で拒否されます。バイヤーは、メディアバイ/プロダクトのコンテキストが公開する場合はセラーの公開する `creative_policy.accepted_verifiers[]` から、スタンドアロンのクリエイティブエージェント統合ではアカウントのプロビジョニングから、受理される評価器の URL を知ります。URL がリストにあるが評価器に到達できない、またはクリエイティブエージェントのトランスポート認証を拒否する場合、ビルドはビルド全体を失敗させるのではなく、助言的な `errors[]` の注記とともにセラーデフォルトのランキングに劣化します。 ### 生成コントロール ジェネレーティブフォーマットでは、生成プロセスを制御する 2 つのオプションパラメーターがあります。 * **`quality`**: 生成の忠実度を制御します。高速反復(レイアウト・コピー・構成のレビュー)には `"draft"` を、最終レンダーには `"production"` を使用します。ドラフト出力では低解像度の画像、単純化されたエフェクト、またはプレースホルダー要素が使用される場合があります。気に入ったドラフトのプロダクション版を作成するには、そのドラフトの出力マニフェストを `creative_manifest` として `quality: "production"` と共に渡します。注意: `preview_creative` も `quality` を受け取るが、*レンダー*の忠実度を独立して制御します。[ジェネレーティブクリエイティブのプレビュー](/docs/creative/task-reference/preview_creative#previewing-generative-creative)を参照。 * **`item_limit`**: カタログ駆動フォーマットで、生成時に使用するカタログアイテム数を上限設定します。カタログに 1,000 商品あっても、必要なヒーロー画像は 4 枚だけかもしれない。クリエイティブエージェントは関連性またはカタログの順序に基づいて上位アイテムを選択します。`item_limit` がカタログ要件の `max_items`(フォーマットの catalog requirements から)を超える場合、クリエイティブエージェントは小さい方を使用すること。省略した場合、クリエイティブエージェントはカタログサイズとフォーマット要件に基づいて決定します。 ## ユースケース ### 純粋な生成(スクラッチからの作成) 純粋な生成では、フォーマットで定義された必須入力アセットを含む最小限のソースマニフェストを提供します。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3d4-0000-4000-8000-000000000000", "message": "Create a banner promoting our winter sale with a warm, inviting feel", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_generative" }, "brand": { "domain": "mybrand.com" }, "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_generative" }, "assets": { "offering_catalog": { "asset_type": "catalog", "type": "offering", "items": [ { "offering_id": "winter-sale", "name": "Winter Sale Collection", "description": "50% off all winter items" } ] } } } } ``` ### 変換(既存クリエイティブの適応) 変換では、完全なソースマニフェストを提供します。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3d5-0000-4000-8000-000000000001", "message": "Adapt this creative for mobile, making the text larger and CTA more prominent", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "assets": { "banner_image": { "asset_type": "image", "url": "https://cdn.example.com/original-banner.png", "width": 300, "height": 250 }, "headline": { "asset_type": "text", "content": "Winter Sale - 50% Off" } } }, "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_mobile_320x50" } } ``` ### フォーマットリサイズ 既存のクリエイティブを別のサイズに変換します。 ```json theme={null} { "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_728x90" }, "assets": { /* complete assets */ } }, "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" } } ``` ### ライブラリ取得 エージェントのライブラリからクリエイティブを取得し、広告配信アセット付きのマニフェストに解決します。[`list_creatives`](/docs/creative/task-reference/list_creatives) で `creative_id` を把握していて、クリエイティブエージェントにタグ(HTML、JavaScript、VAST)付きの配信対応マニフェストを生成させたい場合に使用します。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3d6-0000-4000-8000-000000000002", "creative_id": "ft_88201", "concept_id": "concept_holiday_2026", "target_format_id": { "agent_url": "https://creative.example.com", "id": "display_static", "width": 300, "height": 250 }, "macro_values": { "CLICK_URL": "https://publisher.example.com/click/abc123" } } ``` **レスポンス** — マニフェストにはマクロが解決された広告配信タグアセットが含まれます。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "recipe_hash": "rh_scope3_9f2c7a1d5b3e", "creative_manifest": { "format_id": { "agent_url": "https://creative.example.com", "id": "display_static", "width": 300, "height": 250 }, "assets": { "ad_tag": { "content": "" }, "clickthrough_url": { "url": "https://acmecorp.example.com/holiday-sale" } } } } ``` `CLICK_URL` マクロは指定した値に置換されました。`CACHEBUSTER` はセールスエージェントが配信時に解決するためのプレースホルダーとして残っています。 `recipe_hash` を、広告タグの重複排除、法的/開示の等価性の証明、またはビルド結果と配信されたデリバリーデータの結合に使わないでください。リネージとレポートには `build_variant_id` とプロモートされた `creative_id` を使います。 **クロスエージェントワークフロー**: クリエイティブ生成とメディアバイを異なるエージェントが処理する場合、クリエイティブエージェントの `build_creative` でタグ付きマニフェストを生成し、次にセールスエージェントの [`sync_creatives`](/docs/creative/task-reference/sync_creatives) でアップロードします。セールスエージェントが両方のプロトコルを実装している場合、単一のエンドポイントで行われる。[セールスエージェントのクリエイティブ機能](/docs/creative/sales-agent-creative-capabilities)を参照。 ### マルチフォーマット生成 `target_format_ids` を使用して 1 回の呼び出しで複数のフォーマット向けにクリエイティブを生成します。エージェントは同じソースアセットとブリーフからフォーマットごとに 1 つのマニフェストを生成します。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3d7-0000-4000-8000-000000000003", "message": "Create display banners for our spring campaign", "target_format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 300, "height": 250 }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 728, "height": 90 }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 320, "height": 50 } ], "brand": { "domain": "acmecorp.com" }, "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static" }, "assets": { "banner_image": { "asset_type": "image", "url": "https://cdn.acmecorp.com/spring-hero.png", "width": 1200, "height": 628 }, "headline": { "asset_type": "text", "content": "Spring Collection Now Available" }, "clickthrough_url": { "asset_type": "url", "url": "https://acmecorp.example.com/spring?campaign={MEDIA_BUY_ID}" } } } } ``` レスポンスは `creative_manifest`(単数)の代わりに `creative_manifests`(配列)を使用します。各マニフェストは独自の `format_id` を持つ完全なクリエイティブマニフェストで、`sync_creatives` または `preview_creative` にそのまま使用できます。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "creative_manifests": [ { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 300, "height": 250 }, "assets": { "banner_image": { "asset_type": "image", "url": "https://cdn.creative-agent.com/generated/spring_300x250.png", "width": 300, "height": 250 }, "headline": { "asset_type": "text", "content": "Spring Collection Now Available" }, "clickthrough_url": { "asset_type": "url", "url": "https://acmecorp.example.com/spring?campaign={MEDIA_BUY_ID}" } } }, { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 728, "height": 90 }, "assets": { /* same structure, adapted for 728x90 */ } }, { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 320, "height": 50 }, "assets": { /* same structure, adapted for 320x50 */ } } ] } ``` マルチフォーマットリクエストはアトミックです。いずれかのフォーマットが失敗した場合(例: `FORMAT_NOT_SUPPORTED`)、リクエスト全体がエラーレスポンスで失敗します。レスポンス配列の順序は `target_format_ids` リクエストの順序に対応します。配列の位置または各マニフェストの `format_id` を比較してマニフェストをリクエストされたフォーマットに対応付ける。 ### マルチフォーマットワークフロー マルチフォーマットビルド後、`preview_creative` バッチモードを使用してすべての結果をプレビューします。ビルドレスポンスの `creative_manifests` の各要素が、バッチプレビューリクエストの `creative_manifest` となります。 ```json theme={null} { "request_type": "batch", "quality": "draft", "requests": [ { "creative_manifest": { /* 300x250 manifest from build response */ } }, { "creative_manifest": { /* 728x90 manifest from build response */ } }, { "creative_manifest": { /* 320x50 manifest from build response */ } } ] } ``` マルチフォーマットビルドで 1 つのフォーマットをリファインするには、`target_format_id`(単数)で `build_creative` を再度呼び出し、そのフォーマットのマニフェストを渡します。すべてのフォーマットを再ビルドする必要はなく、修正が必要な 1 つだけ反復すれば良い。 `include_preview: true` を使ったマルチフォーマットリクエストは、フォーマットごとに 1 つのデフォルトプレビューを生成します。カスタム `preview_inputs` はシングルフォーマットリクエストでのみサポートされます。デバイスバリアント・異なるコンテキストなど、コンテキスト固有のプレビューが必要なマルチフォーマットビルドでは、ビルド後に別途 `preview_creative` バッチ呼び出しを使用すること。 ### トランスフォーマーとバリアント `transformer_id` でトランスフォーマーを選択し([`list_transformers`](/docs/creative/task-reference/list_transformers) で発見)、その params にキー付けした型付き `config` を与え、`max_variants` で代替を要求します。エージェントは[バリアントレスポンス](#バリアントレスポンス)を返します: `creatives[]` で、それぞれが `variants[]` 配列を持ちます。エージェントの best-of-N の選択を表面化するには `recommended` / `rank` を読み、望むバリアントを `build_variant_id` でトラフィックします。 解像度と品質ティアは `variant_axis` ではなく `target_format_ids`(または `quality`)に載せます——バリアントは*同じ*フォーマットに対する代替です。 ```javascript test=false theme={null} import { testAgent } from '@adcp/sdk/testing'; import { BuildCreativeResponseSchema } from '@adcp/sdk'; const result = await testAgent.buildCreative({ account: { account_id: 'acct_acme' }, transformer_id: 'audiostack_voiceover', config: { voice: 'isaac', speaking_rate: 1.1, mastering_preset: 'podcast' }, target_format_id: { agent_url: 'https://creative.audiostack.example', id: 'audio_vo' }, creative_manifest: { format_id: { agent_url: 'https://creative.audiostack.example', id: 'script' }, assets: { script: { asset_type: 'text', content: 'Discover the new winter collection.' } }, }, max_variants: 3, variant_axis: { dimension: 'best_of_n', label: 'Read takes' }, keep_mode: 'keep_one', idempotency_key: '0c1d2e3f-4a5b-6c7d-8e9f-a0b1c2d3e4f5', }); const parsed = BuildCreativeResponseSchema.parse(result); for (const creative of parsed.creatives ?? []) { for (const variant of creative.variants) { console.log(variant.build_variant_id, variant.rank, variant.recommended, variant.vendor_cost); } } ``` ```python test=false theme={null} import asyncio from adcp.testing import test_agent from adcp import BuildCreativeResponse async def main(): result = await test_agent.build_creative( account={"account_id": "acct_acme"}, transformer_id="audiostack_voiceover", config={"voice": "isaac", "speaking_rate": 1.1, "mastering_preset": "podcast"}, target_format_id={"agent_url": "https://creative.audiostack.example", "id": "audio_vo"}, creative_manifest={ "format_id": {"agent_url": "https://creative.audiostack.example", "id": "script"}, "assets": {"script": {"asset_type": "text", "content": "Discover the new winter collection."}}, }, max_variants=3, variant_axis={"dimension": "best_of_n", "label": "Read takes"}, keep_mode="keep_one", idempotency_key="0c1d2e3f-4a5b-6c7d-8e9f-a0b1c2d3e4f5", ) parsed = BuildCreativeResponse.model_validate(result) for creative in parsed.creatives or []: for variant in creative.variants: print(variant.build_variant_id, variant.rank, variant.recommended, variant.vendor_cost) asyncio.run(main()) ``` 3 つのテイクすべてに対して課金されます(`per_unit` × 3)。`keep_mode` は助言的です。保持は選択した `build_variant_id` に対する別のトラフィッキングステップです——生成されたマニフェストを [`sync_creatives`](/docs/creative/task-reference/sync_creatives) を通じてプロモートする際に、その id を `creative_id` として使います。保持されたバリアントはその後、`creative_id` がビルドリーフ id である creative レコードを遅延的に得て、[`report_usage`](/docs/accounts/tasks/report_usage) とデリバリーレポートへ流れます。 ### 価格付きの有料ビルド クリエイティブエージェントが課金し `account` が提供された場合、レスポンスには価格フィールドが含まれます。エージェントは、アカウントのレートカードと実行された作業に基づいてサーバー側で適用可能な価格オプションを選択します——バイヤーはリクエストで `pricing_option_id` を渡しません。 **単位あたりの価格(変換エージェント)**: ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "e5f6a7b8-c9d0-4123-e456-789abcdef012", "account": { "account_id": "acct_acme_creative" }, "creative_id": "cr_hero_banner", "target_format_id": { "agent_url": "https://creative.example.com", "id": "display_728x90" } } ``` ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "creative_manifest": { "format_id": { "agent_url": "https://creative.example.com", "id": "display_728x90" }, "assets": { "ad_tag": { "content": "
...
" } } }, "pricing_option_id": "po_standard_per_format", "vendor_cost": 2.00, "currency": "USD", "consumption": { "renders": 1 } } ``` `pricing_option_id` は [`list_creatives`](/docs/creative/task-reference/list_creatives#pricing) のオプションの 1 つに対応します。バイヤーは照合のために [`report_usage`](/docs/accounts/tasks/report_usage) でそれを渡します。 **CPM 価格(アドサーバー)** — インプレッションが配信されたときにコストが発生するため、ビルド時の `vendor_cost` は 0 です: ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "creative_manifest": { "..." : "..." }, "pricing_option_id": "po_video_cpm", "vendor_cost": 0, "currency": "USD" } ``` ## レスポンスフォーマット ### シングルフォーマットレスポンス リクエストで `target_format_id` を使用した場合、レスポンスには単一のクリエイティブマニフェストが含まれます。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "recipe_hash": "rh_winter_300x250_4c2a", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "assets": { "offering_catalog": { "asset_type": "catalog", "type": "offering", "catalog_id": "winter-sale" }, "banner_image": { "asset_type": "image", "url": "https://cdn.example.com/generated-banner.png", "width": 300, "height": 250 }, "headline": { "asset_type": "text", "content": "50% Off Winter Sale" }, "clickthrough_url": { "asset_type": "url", "url": "https://mybrand.example.com/winter-sale?campaign={MEDIA_BUY_ID}" } } } } ``` ### マルチフォーマットレスポンス リクエストで `target_format_ids` を使用した場合、レスポンスにはクリエイティブマニフェストの配列が含まれます。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "creative_manifests": [ { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 300, "height": 250 }, "assets": { /* ... */ } }, { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 728, "height": 90 }, "assets": { /* ... */ } } ] } ``` ### バリアントレスポンス リクエストが `max_creatives`、`max_variants` > 1、`variant_axis`、または `refine_from_build_variant_id` を使う場合、エージェントは `BuildCreativeVariantSuccess` 形を返します——シングルフォーマットおよびマルチフォーマットレスポンス(これらは変更されず、1 つのバリアントで 1 つのクリエイティブをビルドするときに引き続き使われます)と並ぶ 3 番目の成功形(`oneOf` の 6 のうちのメンバー 3)です。 **フォールバックなし。** `max_creatives`、`max_variants > 1`、`variant_axis`、`refine_from_build_variant_id` のいずれかを送った場合、`creatives[]` を扱わなければなりません(**MUST**)——`creative_manifest`/`creative_manifests` は返ってきません。シングル/マルチ形への自動的なダウングレードはありません。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "creatives": [ { "build_creative_id": "bc_card_01", "catalog_item_ref": { "catalog_type": "product", "item_id": "sku_winter_parka" }, "variants": [ { "build_variant_id": "bv_card01_a", "recipe_hash": "rh_card01_bestofn_7b91", "creative_manifest": { "format_id": { "agent_url": "https://creative.example.com", "id": "display_300x250" }, "assets": { "...": "..." } }, "variant_axis_value": "take_1", "recommended": true, "rank": 1, "pricing_option_id": "po_per_image", "vendor_cost": 0.40, "currency": "USD", "consumption": { "images_generated": 1 } }, { "build_variant_id": "bv_card01_b", "recipe_hash": "rh_card01_bestofn_7b91", "creative_manifest": { "format_id": { "agent_url": "https://creative.example.com", "id": "display_300x250" }, "assets": { "...": "..." } }, "variant_axis_value": "take_2", "recommended": false, "rank": 2, "pricing_option_id": "po_per_image", "vendor_cost": 0.40, "currency": "USD", "consumption": { "images_generated": 1 } } ] } ], "items_total": 150, "items_returned": 5, "vendor_cost": 4.00, "currency": "USD" } ``` *上記の `creatives[]` 配列は、返された 5 グループのうちの 1 つに省略されています。集計の `vendor_cost` $4.00 は、全 5 グループ × 2 バリアント × $0.40 をカバーします——これは表示されたリーフだけでなく、生成されたすべてのリーフの `vendor_cost` の合計に等しくなります。* * **creatives\[]**: ビルドされたクリエイティブグループごとに 1 エントリ。`max_creatives` を使うと、サンプリングされたカタログアイテムごとに 1 エントリになります(`catalog_item_ref` がどのアイテムかを識別)。カタログのファンアウトがない場合は単一のエントリです。これを生成されたグループの集合として扱い、各グループの `variants[]` 内で代替から選びます。 * **creatives\[].build\_creative\_id**: このレスポンス内でビルドされたクリエイティブを識別します。 * **creatives\[].catalog\_item\_ref**: カタログのファンアウトで存在——`item_id`(と任意の `catalog_type`)でソースカタログアイテムを識別するオブジェクト。 * **creatives\[].signal\_condition**: シグナルのファンアウトで存在——このクリエイティブグループが対象とする SignalTargeting 条件(例: weather=rain)。セールス側のパッケージターゲティングと `signal_ref` のアイデンティティを共有するため、セールスエージェントが互換性のない割り当てを照合・拒否できます。 * **creatives\[].errors\[]**: *失敗した*カタログアイテムでのみ存在——カタログのファンアウトは非アトミックなので、失敗したアイテムは `errors[]` を運び `variants[]` を持たない `creatives[]` エントリとして返され、バッチを失敗させません。 * **creatives\[].variants\[]**: このクリエイティブグループに対して生成された、選ぶための代替。長さは最大でも `max_variants`。各バリアントは独自の完全な `creative_manifest` を運びます。 * **variants\[].build\_variant\_id**: 単一のバリアントを識別——リーフレベルの**リネージアンカー**。これは**独自の名前空間**です——`preview_id`(`preview_creative` のレンダー)や配信された `variant_id`(デリバリー時の識別子)をここで再利用しないでください。選択したビルドは、その `build_variant_id` を渡してトラフィックします。 * **variants\[].recipe\_hash**: リーフを生成したビルド決定入力に対する、任意の ETag スタイルのアイデンティティ。エージェントが計算し、不透明で、同じエージェント内でのみ比較可能。同じソースレシピからの best-of-N リーフは値を共有すべきです(SHOULD)。キャッシュの透明性、コスト回避のヒント、レシピレベルのグルーピングに有用ですが、出力の同一性でも、法的/開示の等価性でも、ビルドからデリバリーへの結合でもありません。 * **variants\[].parent\_build\_variant\_id**: リファインされたバリアントでのみ存在([リファインメント](#リファインメント))——リファイン元のソースの `build_variant_id`。第一世代のビルドでは不在。 * **variants\[].variant\_axis\_value**: このバリアントが表す `variant_axis` 次元の値(例: 声、テーマ)。 * **variants\[].recommended / rank**: エージェントの best-of-N の順序付け。`recommended: true` が最上位の選択を、`rank` が順序を示します。 * **variants\[].eval**: `evaluator` が提供された場合に存在——ゲート後にランク付けするパイプラインからのリーフごとの評価ブロック。評価器の `feature_id` にキー付けされた `features[]` を含みます。 * **items\_total / items\_returned**: カタログのファンアウトのカーディナリティ。`items_returned` \< `items_total` は、`max_creatives` または `max_creatives_limit` によるサンプリング/クランプを示します。 * **vendor\_cost**(トップレベル): 生成されたすべてのリーフにわたる集計コスト。個々の `variants[].vendor_cost` の合計。 **ビルド時のバリアントはデリバリーのバリアントではありません。** ここでの `build_variant_id` は、生成した代替に対する配信前のハンドルです。これは、[`get_creative_delivery`](/docs/creative/task-reference/get_creative_delivery) の配信された `variant_id`(*配信されたクリエイティブの実行*を識別)とは異なる名前空間です。ビルド時のバリアントは、保持してトラフィックした時点で初めてデリバリーのバリアントになります。 **解像度と品質ティアはバリアントではありません。** 複数のサイズや品質レベルはフォーマット軸に属します——`variant_axis` の値としてではなく、`target_format_ids`(または `quality`)として渡します。バリアントは*同じフォーマットに対する*代替です(異なる声、テーマ、または best-of-N のテイク)。 ### リファインメント 会話的なリファインメントは、以前のバリアントを自由形式の指示で再ビルドします——「もっと暖かく」「CTA を引き締めて」。型付き `config` の面は設計上閉じているので、自由形式の意図は、すでに存在する**開かれた**面に乗ります: `message` フィールド(その説明はすでに\*「リファインメント時は変更内容を記述します」\*)。したがってリファインメントは新しいタスクではなく——1 つ追加の入力を伴う `build_creative` です: * `refine_from_build_variant_id`(以前のリーフの `build_variant_id`)に加えて、`message` に指示、任意の `config` デルタを渡します。 * エージェントはそのリーフから再ビルドし、それぞれ `parent_build_variant_id` をソースに設定した**新しい**バリアントを返します。リファインメントは**決して変更ではありません**——親リーフは変更されず、新しいリーフは独自の `build_variant_id`(およびトラフィッキング時に独自の `creative_id`)を得ます。 * `transformer_id` とターゲットフォーマットは親から**継承**され、繰り返しません。親と異なる `transformer_id` やターゲットフォーマットを渡すことは `INVALID_REQUEST` です。`config` は親の config に対する**デルタ**として適用されます。 * `max_variants` / `variant_axis` と合成されますが(例: 「もっと暖かい 3 テイク」→ 3 つのリファインされたリーフ)、`max_creatives` / カタログのファンアウトとは合成**されません**——カタログではなく、生成された 1 つのクリエイティブをリファインします。 * エージェントが `creative.supports_refinement: true` を表明する必要があります(エージェントが定めた期間、生成されたリーフを保持します)。何も保持しないエージェントは `UNSUPPORTED_FEATURE` を返します。代わりに変換パス(`creative_manifest` + `message`)を通じてバイヤー保持のマニフェストをリファインしてください。未知の、または保持されなくなった参照は、`error.field` を `refine_from_build_variant_id` に設定した `REFERENCE_NOT_FOUND` を返します。 バリアントビルドだけでなく、あらゆるビルドをリファインできます: シングルフォーマットの `BuildCreativeSuccess` は、任意の `build_variant_id`(エージェントがリファインメントをサポートするときに存在)を運び、それを `refine_from_build_variant_id` として渡します。出力ごとにリファイン可能なリーフが必要なマルチフォーマットビルドは、素の `creative_manifests[]` 配列がリーフ id を運ばないため、バリアント形(`max_variants`)を要求すべきです。 AI 派生物のアトリビューションはマニフェストの既存の `provenance` に乗ります。`parent_build_variant_id` はリネージのエッジのみを運びます(リファインメントはツリーへ連鎖します)。 ```json test=false theme={null} { "refine_from_build_variant_id": "bv_card01_a", "message": "Warmer lighting, and move the logo to the lower-right.", "max_variants": 3, "account": { "account_id": "acct_acme" }, "idempotency_key": "7f2a1b3c-4d5e-6f70-8192-a3b4c5d6e7f8" } ``` ### 支出コントロール ファンアウトとリファインメントは、独立して課金される多くのリーフ(`max_creatives` × `max_variants`)を生成でき、`per_unit` 価格は*レート*を与えますが事前に*単位数*を与えません(6 秒のボイスオーバーと 60 秒のものは、同じレートで 10 倍のコストになります)。`creative.supports_spend_controls` でゲートされた 2 つのオプトインコントロール: * **まず見積もる(`mode: "estimate"`)。** ドライラン: エージェントは何も生成せず課金もせず、`cost_low`/`cost_high` の帯(および `basis`: `fixed` = 正確、`estimated_units` = 生成的な予測、`cpm_deferred` = 配信時にコストが発生)を持つ `BuildCreativeEstimate` を返します。帯が要となる部分です——セラーがあなたの実際の入力から導出するので、単位数を推測する必要がありません。 * **呼び出しを上限する(`max_spend: { amount, currency }`)。** ハードストップ: エージェントは、次のリーフが集計 `vendor_cost` を `amount` 超に押し上げるまでリーフを生成し、その後 `budget_status: "capped"` と `errors[]` の助言的な `BUDGET_CAP_REACHED` を伴う部分的な `BuildCreativeVariantSuccess` を返します——返されたすべてのリーフは実在し課金され、生成されたものは何も破棄されません。リーフ粒度の不足は `leaves_returned` \< `leaves_total` です(`items_returned`/`items_total` ではありません。これらはカタログアイテムを数え、バリアントのみやアイテム途中の上限を捉えません)。`BUDGET_CAP_REACHED` の助言が権威ある上限シグナルです。最初のリーフでさえ上限を超える場合、呼び出しは終端の `BUDGET_CAP_REACHED` で失敗します。`currency` はレートカードと一致しなければならず(FX なし)、さもなければリクエストは `INVALID_REQUEST`(`error.field: max_spend.currency`)で拒否されます。`max_spend` は**ビルド時**の `vendor_cost` のみを制限します——CPM 価格のビルド(`basis: cpm_deferred`)はビルド時に 0 で配信時に発生するので、上限は決して働きません。CPM のファンアウトは代わりに `max_creatives` で制限してください。 これらは合成されます: 見積もって `cost_high` を得て、その後 `max_spend` = `cost_high` × 安全マージンで実行します(CPM ビルドは例外——ビルド時の `cost_high` は 0)。`max_spend` は**単一の呼び出し**を上限とします。自律的な**リファインメントループ**を制限するには、呼び出しをまたいで集計 `vendor_cost` を追跡し、発行を止めます(このリビジョンではバイヤーの責任——プロトコルレベルのセッション予算はワーキンググループに先送り)。 `max_spend` と `mode: "estimate"` は、エージェントが `creative.supports_spend_controls` を表明する必要があります。さもなければ `UNSUPPORTED_FEATURE` で拒否されます。これらは `bills_through_adcp: true` のときにのみ意味を持ちます(帯域外の請求者には上限すべき AdCP コストがありません)。 ### 見積もりレスポンス `mode: "estimate"` のリクエストは、`BuildCreativeEstimate` 形(`oneOf` の 6 のうちのメンバー 4)を返します——何も生成せず課金もせず、予測されたコスト帯だけ: ```json test=false theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "mode": "estimate", "estimate": { "items_total": 150, "items_to_produce": 5, "variants_per_item": 3, "leaves_total": 15, "currency": "USD", "cost_low": 6.00, "cost_high": 9.00, "cost_expected": 7.50, "basis": "estimated_units" }, "expires_at": "2026-06-01T00:00:00Z" } ``` * **estimate.leaves\_total** = `items_to_produce` × `variants_per_item`(`signal_conditions` が送られた場合は × `conditions_total`)——`mode: "execute"` が生成する課金対象リーフの数。 * **estimate.cost\_low / cost\_high / cost\_expected**: 予測された集計コスト帯。セラーがあなたの実際の入力から導出します。 * **estimate.basis**: `fixed`(フォーマットあたりのフラット——`cost_low == cost_high`、正確)、`estimated_units`(生成的な `per_unit`。帯は単位数の不確実性を反映)、または `cpm_deferred`(CPM——ビルド時コストは 0、配信時に発生するので帯は 0)。 * **estimate.per\_leaf**(任意): リーフごとの内訳。 * 見積もりはこのリビジョンでは**助言的/非拘束**です(拘束力のある見積もりはワーキンググループに先送り)。 ### フィールド説明 * **creative\_manifest**: (シングルフォーマット)`sync_creatives` または `preview_creative` で使用できる完全なクリエイティブマニフェスト * **creative\_manifests**: (マルチフォーマット)リクエストされたフォーマットごとの完全なクリエイティブマニフェストの配列。各要素が独自の `format_id` を持ちます。 * **format\_id**: ターゲットフォーマット(リクエストされたフォーマットと一致します) * **assets**: アセットキーからアセットコンテンツへのマップ — クリエイティブコンテンツ(画像・テキスト・URL)、カタログ、ブリーフ、フォーマットが必要とするその他すべてを含みます * **expires\_at**: オプション。マニフェスト内の生成されたアセット URL の有効期限を示す ISO 8601 タイムスタンプ。すべての生成済みアセットの中で最も早い有効期限に設定されます。この時刻を過ぎたら新しい URL を取得するためにクリエイティブを再ビルドすること。マニフェストに有効期限のある URL が含まれない場合(例: 純粋なテキスト生成やアセンブリのみの変換)は存在しません。 * **preview**: オプション。リクエストで `include_preview` が true で、エージェントがインラインプレビューをサポートしている場合に存在します。`preview_creative` のシングルレスポンスと同じコンテンツフィールド(`previews`、`interactive_url`、`expires_at`)を含むが、`response_type` ディスクリミネーターは除く。クライアントが同じプレビューレンダリングロジックを再利用できます。プレビュー URL は `preview_creative` と同じ耐久性契約に従います: `expires_at` まで、または有効期限が存在しない場合は明示的な帯域外の失効まで、参照解決可能なままです。シングルフォーマットレスポンスでは、`previews[]` の各エントリが `preview_inputs` の入力セットに対応します。マルチフォーマットレスポンスでは、各エントリに `format_id` が含まれ、リクエストされたフォーマットの 1 つに対応する(フォーマットごとに 1 つのデフォルトプレビュー。`preview_inputs` は無視されます)。 * **preview\_error**: オプション。`include_preview` が true だったがプレビュー生成が失敗した場合に存在する標準エラーオブジェクト(`code`、`message`、`recovery`)。`recovery` フィールドは失敗が `transient`(後でリトライ)、`correctable`、または `terminal` のいずれかを示します。「エージェントがインラインプレビューをサポートしない」(フィールドが存在しない、エラーなし)と「プレビュー生成が失敗した」(フィールドが存在し、構造化エラーあり)を区別します。 ### コンプライアンスエラー マニフェストに `compliance` 要件を持つ brief アセットが含まれており、クリエイティブエージェントがその要件を満たせない場合、エージェントは部分的な成功ではなくエラーを返さなければなりません(MUST)。未充足のディスクロージャーはハードな失敗です。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "errors": [ { "code": "COMPLIANCE_UNSATISFIED", "message": "Required disclosure cannot be rendered in this format", "field": "creative_manifest.assets.brief.compliance.required_disclosures[0]", "details": { "disclosure_text": "Past performance is not indicative of future results.", "position": "footer", "reason": "Format display_mobile_320x50 does not support footer position" }, "suggestion": "Use a format that supports footer disclosures, or change position to 'overlay'" } ] } ``` クリエイティブエージェントはクリエイティブを生成する前に、すべての `required_disclosures` をターゲットフォーマットで満たせることをバリデートしなければなりません(MUST)。いずれかのディスクロージャーを指定通りに配置できない場合、リクエスト全体が失敗します。これにより、規制対象のクリエイティブが必要な法的テキストなしに配信されることを防ぐ。 ## レスポンスのタイミング クリエイティブエージェントがどう応答するかは、操作にどれだけ時間がかかるかによります: | 想定される所要時間 | ステータス | 呼び出し元の体験 | | ----------------------- | ----------- | ----------------------------------------------------------- | | 30 秒未満 | `completed` | 結果を直接返却——同期 | | 30 秒超、サーバーが能動的に処理中 | `working` | サーバーが処理を続ける間、帯域外のステータス更新。呼び出し元は接続を保持——彼らの視点では依然として同期 | | 外部依存(人によるレビュー、承認)でブロック中 | `submitted` | 真の非同期——呼び出し元は `push_notification_config` でウェブフックを設定して先に進むべき | クリエイティブエージェントは、関与する作業に基づいてどの経路を取るかを決めます。ライブラリ取得は即時。単純な変換は数秒。AI 生成はさまざま——手早いバナーは 10 秒で完了するかもしれず、複雑な動画コンポジションは数分かかるかもしれません。 ### `working` はポーリングのトリガーではなく進捗シグナル サーバーが 30 秒超かかると想定するが能動的に処理している場合、帯域外の MCP ステータス更新として `working` を送ります。これは、呼び出し元をポーリングやウェブフックのパターンに切り替えさせることなく、クライアントに情報を伝え続けます(「取り組んでいます」)。接続は開いたままで、結果は準備ができたときに届きます。 ### 非同期にするとき `submitted` は、操作がサーバーの制御外の何かでブロックされていることを意味します: * **人によるクリエイティブレビュー** — ブランドガイドラインが返却前に承認を要求 * **外部の承認ワークフロー** — サードパーティのコンプライアンスまたは法的レビュー これらのケースでは、結果を受け取るために `push_notification_config` でウェブフックを設定します。[非同期オペレーション](/docs/building/by-layer/L3/async-operations)と[プッシュ通知](/docs/building/by-layer/L3/webhooks)を参照してください。 ### ヒューマンインザループ エージェントは、人間の入力が必要なときに `status: "input-required"` を返す場合があります——例えば、ブランドガイドラインがクリエイティブ承認を要求する場合や、エージェントがクリエイティブ方向性の明確化を必要とする場合などです。 ```json theme={null} { "reason": "CREATIVE_DIRECTION_NEEDED" } ``` **理由コード:** * `APPROVAL_REQUIRED` — クリエイティブを確定する前に人間の承認が必要 * `CREATIVE_DIRECTION_NEEDED` — クリエイティブブリーフまたは方向性について明確化が必要 * `ASSET_SELECTION_NEEDED` — アセットの選択肢の中から呼び出し元に選択させる必要があります ライブラリ取得モード(`creative_id` を使用)は通常同期的です。クリエイティブがすでに存在しており、タグ生成のみが必要なためです。非同期が最も一般的なのは生成および変換モードです。 ## ワークフロー統合 ### 一般的な生成ワークフロー 1. **ビルド**: `build_creative` を使用してマニフェストを生成・変換します 2. **プレビュー**: `preview_creative` を使用してレンダリングを確認する([preview\_creative](/docs/creative/task-reference/preview_creative) を参照) 3. **シンク**: `sync_creatives` を使用して確定したクリエイティブをトラフィッキングします ビルドリクエストに `include_preview: true` を設定することで、ステップ 1 と 2 を組み合わせることができます。エージェントがサポートしている場合、レスポンスにはマニフェストと共に `preview` オブジェクトが含まれ、余分なラウンドトリップが不要になります。エージェントがインラインプレビューをサポートしない場合、フィールドは単純に省略され、別途 `preview_creative` 呼び出しにフォールバックします。リクエストした際に `preview` が存在すると仮定するのではなく、常にその存在を確認すること。 `preview_quality` を使用してビルド品質から独立してレンダーの忠実度を制御します。例えば、`quality: "draft"`(高速なコンセプト生成)でビルドしながら、`preview_quality: "production"`(ステークホルダーにレイアウトを見せるためのフルフィデリティレンダー)でプレビューします。`preview_quality` を省略した場合、エージェントが独自のデフォルトを使用します。 ```json theme={null} // Build at draft quality, but preview at production quality for stakeholder review { "message": "Create a display banner for our winter sale", "target_format_id": {"agent_url": "...", "id": "display_300x250_generative"}, "brand": { "domain": "mybrand.com" }, "quality": "draft", "include_preview": true, "preview_quality": "production", "creative_manifest": { "format_id": {"agent_url": "...", "id": "display_300x250_generative"}, "assets": { "product_catalog": { "asset_type": "catalog", "type": "product", "catalog_id": "winter-products" } } } } // Or: Build first, preview separately // Step 1: Build { "message": "Create a display banner for our winter sale", "target_format_id": {"agent_url": "...", "id": "display_300x250_generative"}, "brand": { "domain": "mybrand.com" }, "creative_manifest": { "format_id": {"agent_url": "...", "id": "display_300x250_generative"}, "assets": { "product_catalog": { "asset_type": "catalog", "type": "product", "catalog_id": "winter-products" } } } } // Step 2: Preview (using the output manifest from step 1) { "request_type": "single", "format_id": {"agent_url": "...", "id": "display_300x250"}, "creative_manifest": { /* output from build_creative - includes all assets */ }, "inputs": [{"name": "Desktop view"}, {"name": "Mobile view"}] } // Step 3: Sync (if preview looks good) { "creative_manifests": [{ /* approved manifest from build_creative */ }] } ``` **重要なポイント**: マニフェストがすべてを運ぶ。ブリーフ・カタログ・画像・テキスト — すべてがアセットマップに存在し、入力から出力まで受け渡されます。各ステップで個別に渡す必要はない。 ## 例 ### 例 1: 純粋な生成(ジェネレーティブフォーマット) ジェネレーティブフォーマットを使用してスクラッチからクリエイティブを生成します。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3d8-0000-4000-8000-000000000004", "message": "Create a display banner for our winter sale. Use warm colors and emphasize the 50% discount", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_generative" }, "brand": { "domain": "mybrand.com" }, "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_generative" }, "assets": { "offering_catalog": { "asset_type": "catalog", "type": "offering", "items": [ { "offering_id": "winter-sale", "name": "Winter Sale Collection", "description": "50% off all winter items" } ] } } } } ``` **レスポンス**: ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "assets": { "offering_catalog": { "asset_type": "catalog", "type": "offering", "catalog_id": "winter-sale" }, "banner_image": { "asset_type": "image", "url": "https://cdn.creative-agent.com/generated/banner_12345.png", "width": 300, "height": 250 }, "clickthrough_url": { "asset_type": "url", "url": "https://mybrand.example.com/winter-sale?campaign={MEDIA_BUY_ID}" } } } } ``` ### 例 2: フォーマット変換 既存の 728x90 リーダーボードを 300x250 バナーに変換します。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3d9-0000-4000-8000-000000000005", "message": "Adapt this leaderboard creative to a 300x250 banner format", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_728x90" }, "assets": { "banner_image": { "asset_type": "image", "url": "https://cdn.mybrand.com/leaderboard.png", "width": 728, "height": 90 }, "headline": { "asset_type": "text", "content": "Spring Sale - 30% Off Everything" }, "clickthrough_url": { "asset_type": "url", "url": "https://mybrand.example.com/spring?campaign={MEDIA_BUY_ID}" } } }, "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" } } ``` **レスポンス**: ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "assets": { "banner_image": { "asset_type": "image", "url": "https://cdn.creative-agent.com/resized/banner_67890.png", "width": 300, "height": 250 }, "headline": { "asset_type": "text", "content": "Spring Sale - 30% Off" }, "clickthrough_url": { "asset_type": "url", "url": "https://mybrand.example.com/spring?campaign={MEDIA_BUY_ID}" } } } } ``` ### 例 3: 特定の指示を含む変換 特定のデザイン変更を伴うモバイル向けへの変換。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3da-0000-4000-8000-000000000006", "message": "Make this mobile-friendly: increase text size, simplify the layout, and make the CTA button more prominent", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x600" }, "assets": { "background_image": { "asset_type": "image", "url": "https://cdn.mybrand.com/bg.jpg", "width": 300, "height": 600 }, "headline": { "asset_type": "text", "content": "Discover Our New Collection" }, "body_text": { "asset_type": "text", "content": "Shop the latest styles with free shipping on orders over $50" }, "cta_text": { "asset_type": "text", "content": "Shop Now" } } }, "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_mobile_320x50" } } ``` **レスポンス**: ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_mobile_320x50" }, "assets": { "banner_image": { "asset_type": "image", "url": "https://cdn.creative-agent.com/mobile/banner_mobile_123.png", "width": 320, "height": 50 }, "headline": { "asset_type": "text", "content": "New Collection - Shop Now" }, "clickthrough_url": { "asset_type": "url", "url": "https://mybrand.example.com/new?campaign={MEDIA_BUY_ID}" } } } } ``` ### 例 4: クリエイティブブリーフを使った生成 `brand` とマニフェストの brief アセットを通じて構造化されたキャンペーンコンテキストを使用してクリエイティブを生成します。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3db-0000-4000-8000-000000000007", "message": "Create a display banner for the holiday campaign targeting gift shoppers", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_generative" }, "brand": { "domain": "acmecorp.com" }, "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_generative" }, "assets": { "brief": { "asset_type": "brief", "name": "Holiday Sale 2025", "objective": "conversion", "audience": "Holiday gift shoppers aged 25-55", "territory": "festive savings", "messaging": { "headline": "Holiday Deals Are Here", "cta": "Shop Now", "key_messages": [ "Up to 50% off select items", "Free shipping on orders over $50" ] }, "reference_assets": [ { "url": "https://cdn.acmecorp.com/holiday-mood-board.pdf", "role": "mood_board", "description": "Holiday campaign mood board with festive color palette" } ] }, "offering_catalog": { "asset_type": "catalog", "type": "offering", "items": [ { "offering_id": "holiday-sale", "name": "Holiday Sale Collection", "description": "Up to 50% off select holiday items" } ] } } } } ``` ### 例 5: コンプライアンス要件を含む生成 規制上のディスクロージャーと禁止クレームを含む金融サービスのクリエイティブを生成します。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3dc-0000-4000-8000-000000000008", "message": "Create a display banner promoting retirement planning advisory services", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_generative" }, "brand": { "domain": "pinnaclewealth.com" }, "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_generative" }, "assets": { "brief": { "asset_type": "brief", "name": "Retirement Advisory Q1 2026", "objective": "consideration", "audience": "Pre-retirees aged 50-65 with investable assets", "territory": "trusted financial guidance", "messaging": { "headline": "Plan Your Retirement with Confidence", "cta": "Schedule a Consultation", "key_messages": [ "Personalized retirement planning", "Tax-efficient investment strategies" ] }, "compliance": { "required_disclosures": [ { "text": "Past performance is not indicative of future results.", "position": "footer", "jurisdictions": ["US"], "regulation": "SEC Rule 156" }, { "text": "Securities offered through Pinnacle Wealth Securities, LLC. Member FINRA/SIPC.", "position": "footer", "jurisdictions": ["US"], "regulation": "FINRA Rule 2210" }, { "text": "Capital at risk. The value of investments can go down as well as up.", "position": "prominent", "jurisdictions": ["GB"], "regulation": "FCA COBS 4.5" }, { "text": "Pinnacle Wealth Advisors is a registered investment adviser.", "position": "footer" } ], "prohibited_claims": [ "guaranteed returns", "risk-free investment", "outperform the market" ] } } } } } ``` コンプライアンス要件は管轄によって異なります。米国では SEC が義務付けるディスクロージャーが必要で、英国では FCA が義務付けるリスク警告が必要です。3 番目のディスクロージャー(`jurisdictions` なし)はグローバルに適用されます。`prohibited_claims` 配列は、生成されたコピーで避けるべきクレームをクリエイティブエージェントに伝える。 ### 例 6: ブリーフと商品カタログを使ったコマースメディア キャンペーンコンテキスト・コンプライアンスディスクロージャー・同期された商品カタログを含むスポンサー商品カルーセルを生成します。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3dd-0000-4000-8000-000000000009", "message": "Create a product carousel highlighting the top 4 sale items", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "sponsored_product_carousel" }, "brand": { "domain": "novabrands.com" }, "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "sponsored_product_carousel" }, "assets": { "brief": { "asset_type": "brief", "name": "Spring Sale 2026", "objective": "conversion", "audience": "Value-conscious shoppers aged 25-45", "messaging": { "headline": "Spring Sale — Up to 40% Off", "cta": "Shop Now" }, "compliance": { "required_disclosures": [ { "text": "Sponsored", "position": "prominent" }, { "text": "Prices may vary by location. See store for details.", "position": "footer" } ] } }, "product_catalog": { "asset_type": "catalog", "type": "product", "catalog_id": "spring_sale_2026" } } } } ``` ブリーフと商品カタログはマニフェストの `assets` マップに一緒に存在します。フォーマットは `brief` と `catalog` の両方のアセットタイプを宣言します。バイイングエージェントは `list_creative_formats` でこれを検出し、送信前に必要なカタログを同期します。 ### 例 7: インラインプレビュー付きビルド クリエイティブをビルドし、同一レスポンスでプレビューレンダリングを取得します。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3de-0000-4000-8000-00000000000a", "message": "Create a banner for our spring campaign", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_generative" }, "brand": { "domain": "novabrands.com" }, "include_preview": true, "preview_inputs": [ { "name": "Default" }, { "name": "Dark mode", "macros": { "COLOR_SCHEME": "dark" } } ], "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_generative" }, "assets": { "offering_catalog": { "asset_type": "catalog", "type": "offering", "items": [ { "offering_id": "spring-promo", "name": "Spring Collection", "description": "30% off new arrivals" } ] } } } } ``` **レスポンス**(エージェントがインラインプレビューをサポートしている場合): ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "assets": { "offering_catalog": { "asset_type": "catalog", "type": "offering", "catalog_id": "spring-promo" }, "banner_image": { "asset_type": "image", "url": "https://cdn.creative-agent.com/generated/spring_abc123.png", "width": 300, "height": 250 }, "clickthrough_url": { "asset_type": "url", "url": "https://novabrands.com/spring" } } }, "preview": { "previews": [ { "preview_id": "prev_default", "renders": [ { "render_id": "r1", "output_format": "url", "preview_url": "https://preview.creative-agent.com/abc123/default", "role": "primary", "dimensions": { "width": 300, "height": 250 } } ], "input": { "name": "Default" } }, { "preview_id": "prev_dark", "renders": [ { "render_id": "r2", "output_format": "url", "preview_url": "https://preview.creative-agent.com/abc123/dark", "role": "primary", "dimensions": { "width": 300, "height": 250 } } ], "input": { "name": "Dark mode", "macros": { "COLOR_SCHEME": "dark" } } } ], "expires_at": "2026-03-13T06:00:00Z" }, "expires_at": "2026-03-13T06:00:00Z" } ``` `preview` オブジェクトには `preview_creative` のシングルレスポンスと同じコンテンツフィールド(`previews`、`interactive_url`、`expires_at`)が含まれます。エージェントがインラインプレビューをサポートしない場合、このフィールドは存在しません。バイヤーエージェントは別途 `preview_creative` 呼び出しにフォールバックします。プレビュー生成が失敗した場合、レスポンスには標準エラーオブジェクト(`code`、`message`、`recovery`)を持つ `preview_error` が含まれます。 ### 例 8: アイテム制限付きドラフト生成 大規模カタログからドラフト品質のクリエイティブを生成し、アイテム数を上限設定します。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3df-0000-4000-8000-00000000000b", "message": "Create hero images for our top sale items", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "sponsored_product_carousel" }, "brand": { "domain": "novabrands.com" }, "quality": "draft", "item_limit": 4, "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "sponsored_product_carousel" }, "assets": { "product_catalog": { "asset_type": "catalog", "type": "product", "catalog_id": "spring_sale_2026" } } } } ``` カタログには数百の商品が含まれる場合があるが、`item_limit: 4` により生成されるヒーロー画像は 4 枚のみとなります。`quality: "draft"` はレビュー用の高速・低忠実度の出力を生成します。 **レスポンス**: ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-response.json", "status": "completed", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "sponsored_product_carousel" }, "assets": { "product_catalog": { "asset_type": "catalog", "type": "product", "catalog_id": "spring_sale_2026" }, "card_1_image": { "asset_type": "image", "url": "https://cdn.creative-agent.com/draft/card1_abc123.jpg", "width": 400, "height": 400 }, "card_2_image": { "asset_type": "image", "url": "https://cdn.creative-agent.com/draft/card2_def456.jpg", "width": 400, "height": 400 }, "card_3_image": { "asset_type": "image", "url": "https://cdn.creative-agent.com/draft/card3_ghi789.jpg", "width": 400, "height": 400 }, "card_4_image": { "asset_type": "image", "url": "https://cdn.creative-agent.com/draft/card4_jkl012.jpg", "width": 400, "height": 400 } } }, "expires_at": "2026-03-02T06:00:00Z" } ``` `expires_at` フィールドは生成された CDN URL の有効期限を示します。この時刻を過ぎたら新しい URL を取得するために再ビルドすること。方向性が承認されたら、最終レンダーのために出力マニフェストを `quality: "production"` で再送信します。 ## 主要コンセプト ### ブランドとクリエイティブブリーフの違い | | ブランド | クリエイティブブリーフ | | ---------- | ------------------------------------- | --------------------------------------- | | **スコープ** | ブランドアイデンティティ | キャンペーンコンテキスト | | **ライフスパン** | キャンペーンをまたいで安定 | キャンペーンまたはフライトに固有 | | **内容** | カラー・ロゴ・フォント・トーン | オーディエンス・テリトリー・メッセージ・コンプライアンス・リファレンスアセット | | **法的事項** | ブランドレベルの免責事項(常時適用) | キャンペーン固有の規制上のディスクロージャー(地域・製品ベース) | | **ソース** | ブランドレジストリ / `/.well-known/brand.json` | エージェンシーまたはブランドチーム | | **格納場所** | ドメインルックアップで解決 | マニフェストのアセットマップ(`assets.brief`) | どちらもオプションです。`brand` はドメインの `/.well-known/brand.json` 経由で解決される安定したブランドアイデンティティ(カラー・ロゴ・トーン)を提供します。ブリーフはマニフェストのアセット(`assets.brief`)であるため、再生成・リサイズ・監査を通じてクリエイティブと共に移動します。`message` フィールドはリクエストごとの自然言語の指示を提供します。 **優先順位**: `brand` パラメーターはクリエイティブレンダリングコンテキスト(カラー・ロゴ・トーン)の権威あるソースです。 **レイヤリング**: マニフェストの brief アセットは構造化された方向性を提供し、リクエストの `message` はリクエストごとの自然言語のオーバーライドを提供します。両方が競合する方向性を提供する場合、`message` が最も具体的な指示として優先されます。 ### 変換モデル `build_creative` は**マニフェストイン、マニフェストアウト**のモデルに従う。 * 入力: クリエイティブマニフェスト(最小限または完全 — すべてがアセットに存在します) * 処理: `message` とマニフェストコンテンツに基づいて変換・生成します * 出力: プレビューまたは同期に使用できるターゲットクリエイティブマニフェスト(ブリーフが引き継がれる) ### 純粋な生成と変換の違い * **純粋な生成**: format\_id だけを持つ最小限の `creative_manifest`、カタログアセット(フォーマットがカタログアイテムをレンダーする場合)、および必要なシードアセットを提供します。クリエイティブエージェントは `message` をガイドとして使用してスクラッチから出力アセットを生成します。 * **変換**: すべての既存アセットを含む完全な `creative_manifest` を提供します。クリエイティブエージェントは既存アセットをターゲットフォーマットに適応させ、オプションで `message` のガイダンスに従う。 ### 他のタスクとの統合 1. **build\_creative** → マニフェストを生成する(オプションで `include_preview` 経由のインラインプレビュー付き) 2. **preview\_creative** → マニフェストを個別にレンダーする([preview\_creative](/docs/creative/task-reference/preview_creative) を参照) 3. **sync\_creatives** → 確定したマニフェストをトラフィッキングします `include_preview: true` を使用してビルドとプレビューを 1 回の呼び出しに組み合わせます。エージェントがサポートしない場合、レスポンスは単純に `preview` フィールドを省略します。別途 `preview_creative` 呼び出しにフォールバックします。どちらの場合も、プレビューコンテンツフィールド(`previews`、`interactive_url`、`expires_at`)は同一です。 この分離により以下が可能になります。 * 一度ビルドして、異なるコンテキストで複数回プレビューします * 再同期せずにビルドを反復します * トラフィッキングにコミットする前にプレビューします ### 反復的なリファインメント `build_creative` はモードフラグなしでマルチターンの反復をサポートします。フィールドの存在と組み合わせがオペレーションを決定します。 * **生成**: `message` + 最小限の `creative_manifest`(空またはシードアセット)+ `target_format_id` * **変換**: 完全な `creative_manifest` + `message` + `target_format_id` * **ライブラリ取得**: `creative_id` + `target_format_id` + オプションの `macro_values` * **リファインメント**: 前の出力を `creative_manifest` として + 変更内容を示す新しい `message` リファインするには、前のレスポンスの `creative_manifest` を新しい `message` と共に入力として渡します。または、brief アセット(`assets.brief`)を更新してクリエイティブ方向性を変更します。ブリーフはクリエイティブがどうあるべきかについてのバイヤーが所有する信頼のソースです。 ```json theme={null} { "$schema": "/schemas/media-buy/build-creative-request.json", "idempotency_key": "a1b2c3e0-0000-4000-8000-00000000000c", "message": "Make the headline bolder and increase the contrast on the CTA button", "target_format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_generative" }, "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "assets": { "banner_image": { "asset_type": "image", "url": "https://cdn.creative-agent.com/generated/banner_12345.png", "width": 300, "height": 250 }, "headline": { "asset_type": "text", "content": "50% Off Winter Sale" }, "clickthrough_url": { "asset_type": "url", "url": "https://mybrand.example.com/winter-sale?campaign={MEDIA_BUY_ID}" } } } } ``` ## エラーコード | コード | 説明 | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `FORMAT_NOT_SUPPORTED` | `target_format_id`(またはマルチフォーマットリクエストの `target_format_ids[N]`)がこのクリエイティブエージェントでサポートされていない。正準的なクリエイティブエージェントルーティングでは、表明された `creative.supported_formats[].capability_id` でリトライしてください。レガシーの名前付きフォーマット ID は、エージェントがまだそれらを表明している場合は有効なままです。 | | `INVALID_MANIFEST` | `creative_manifest` の形式が不正か、ターゲットフォーマットに必要なアセットが不足している | | `CREATIVE_NOT_FOUND` | `creative_id` がエージェントのライブラリに存在しない(または指定した `concept_id` 内に存在しない) | | `COMPLIANCE_UNSATISFIED` | ブリーフからの必須ディスクロージャーをターゲットフォーマットでレンダーできない(例: フォーマットが必要なディスクロージャーポジションをサポートしない) | # get_creative_delivery Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/task-reference/get_creative_delivery get_creative_delivery は AdCP でジェネレーティブおよびスタティッククリエイティブのマニフェストとパフォーマンスメトリクスを含むバリアントレベルの配信データを取得します。 マニフェストとメトリクスを含むバリアントレベルの内訳を持つクリエイティブ配信データを取得します。このタスクは、クリエイティブからどんなバリアントが作成されたか、それらがどのように見えたか(マニフェスト経由)、そしてどのようなパフォーマンスを発揮したかを返します。 これはクリエイティブプロトコルのタスクです。`supported_protocols` に `"creative"` を宣言し、[クリエイティブエージェントのケイパビリティ](#ケイパビリティ宣言)に `"delivery"` を持つすべてのエージェントで呼び出す — 専用のクリエイティブサービスであっても[クリエイティブプロトコルを実装するセールスエージェント](/docs/creative/sales-agent-creative-capabilities)であっても同じです。 **リクエストスキーマ**: [`/schemas/v3/creative/get-creative-delivery-request.json`](https://adcontextprotocol.org/schemas/v3/creative/get-creative-delivery-request.json) **レスポンススキーマ**: [`/schemas/v3/creative/get-creative-delivery-response.json`](https://adcontextprotocol.org/schemas/v3/creative/get-creative-delivery-response.json) ## リクエストパラメータ スコーピングフィルター(`media_buy_ids` または `creative_ids`)のうち少なくとも1つが必須です。 | パラメータ | タイプ | 必須 | 説明 | | --------------- | -------------------------------------------------------------------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------- | | `account` | [account-ref](/docs/building/by-layer/L2/accounts-and-agents#account-references) | No | アカウント参照。セラーが暗黙的解決をサポートする場合、`{ "account_id": "..." }` または `{ "brand": {...}, "operator": "..." }` を渡します。結果をこのアカウント内のクリエイティブに限定します。 | | `media_buy_ids` | string\[] | Yes\* | パブリッシャー ID で特定のメディアバイにフィルタリング | | `creative_ids` | string\[] | Yes\* | ID で特定のクリエイティブにフィルタリング | | `start_date` | string | No | 配信期間の開始日(YYYY-MM-DD、プラットフォームのレポートタイムゾーンで解釈) | | `end_date` | string | No | 配信期間の終了日(YYYY-MM-DD、プラットフォームのレポートタイムゾーンで解釈) | | `max_variants` | integer | No | クリエイティブあたりの返却バリアント数の最大値。バリアント数の多いジェネレーティブクリエイティブに有用。 | | `pagination` | object | No | クリエイティブ配列のカーソルベースのページネーションパラメータ(`max_results`、`cursor`)。省略した場合、すべての一致するクリエイティブが返されます。 | \* `media_buy_ids` または `creative_ids` のうち少なくとも1つが必要。 ## レスポンス | フィールド | 説明 | | ------------------ | ------------------------------------------------------------------------------------- | | `account_id` | アカウント識別子(特定のアカウントにスコープされた場合に存在) | | `media_buy_id` | セラーのメディアバイ識別子(リクエストが単一のバイにスコープされた場合に存在) | | `currency` | 金額の ISO 4217 通貨コード(例: 'USD'、'EUR') | | `reporting_period` | 開始/終了タイムスタンプと `timezone`(IANA 識別子 — プラットフォームはネイティブタイムゾーンでレポートします)を持つ日付範囲 | | `creatives` | バリアント内訳を持つクリエイティブ配信データの配列 | | `pagination` | (オプション)`max_results`、`cursor`、`has_more` を持つページネーション情報。リクエストにページネーションパラメータが含まれた場合に存在。 | ### クリエイティブオブジェクト | フィールド | 説明 | | --------------- | ------------------------------------------------------------ | | `creative_id` | クリエイティブ識別子 | | `media_buy_id` | セラーのメディアバイ識別子(リクエストが複数のメディアバイにまたがった場合に存在) | | `format_id` | このクリエイティブのフォーマット | | `totals` | すべてのバリアントにわたる集計配信メトリクス — 下記の[配信メトリクスフィールド](#配信メトリクスフィールド)を参照 | | `variant_count` | バリアントの総数(`max_variants` 使用時は `variants` 配列の長さを超える場合があります) | | `variants` | バリアントレベルの配信データの配列(クリエイティブにまだバリアントがない場合は空) | ### 配信メトリクスフィールド `creative.totals` と各 `variant` エントリの両方で利用可能なフィールド。よく使われるサブセット——インクリメンタリティ、ブランドリフト、放送のメトリクスを含む完全なリストについては[配信メトリクススキーマ](https://adcontextprotocol.org/schemas/v3/core/delivery-metrics.json)を参照。 | フィールド | 説明 | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `impressions` | 配信されたインプレッション | | `spend` | 使われた金額(`currency` 単位) | | `clicks` | 総クリック数 | | `ctr` | クリック率(clicks/impressions) | | `cpm` | 1000 インプレッションあたりのコスト((spend/impressions) × 1000) | | `cost_per_click` | クリックあたりのコスト(spend/clicks) | | `views` | 課金対象のビュー——しきい値はフォーマットと価格モデルによって異なる | | `completed_views` | 動画/音声の完了 | | `completion_rate` | 完了率(completed\_views/impressions)。該当しない場合(例: 非動画のバイ)は `null` | | `quartile_data` | 音声/動画の四分位完了データ——`q1_views`、`q2_views`、`q3_views`、`q4_views` を持つオブジェクト。該当しない場合(例: 非動画のバイ)は `null` | | `reach` | `reach_unit` で指定された単位でのユニークリーチ。計測ウィンドウは `reach_window` で宣言。それがなければ行をまたいで合計しないこと | | `reach_unit` | `reach` の計測単位。`reach` が存在するときは必須 | | `reach_window` | リーチ/フリークエンシーのウィンドウのセマンティクス——`kind`(キャンペーン開始以降のユニークには `cumulative`、非重複のスナップショットには `period`、末尾ウィンドウには `rolling`)と `period`(Duration、`period` と `rolling` で必須)を持つオブジェクト。任意だが、`reach` が存在するときは常に強く推奨 | | `frequency` | `reach_window` にわたって計測された `reach_unit` あたりの平均フリークエンシー | | `grps` | 配信された Gross Rating Points(CPP 向け) | | `conversions` | 総アトリビューションコンバージョン。存在するときは `by_event_type[].count` の合計に等しい | | `conversion_value` | アトリビューションコンバージョンの総金額 | | `roas` | 広告費用対効果(conversion\_value/spend)——下記の注記を参照 | | `cost_per_acquisition` | コンバージョンあたりのコスト(spend/conversions)——下記の注記を参照 | | `new_to_brand_rate` | 初回ブランド購入者からのコンバージョンの割合(0〜1) | | `leads` | 生成されたリード(`event_type='lead'` の `by_event_type` の便宜的エイリアス) | | `by_event_type` | イベントタイプ別のコンバージョン——`{ event_type, count, value?, event_source_id? }` の配列 | | `by_action_source` | アクションソース別のコンバージョン(website、app、in\_store)——`{ action_source, count, value?, event_source_id? }` の配列 | | `dooh_metrics` | DOOH 固有のデータ——`loop_plays`、`screens_used`、`screen_time_seconds`、`sov_achieved`、`venue_breakdown` を持つオブジェクト | | `viewability` | ビューアビリティデータ——`vendor`、`measurable_impressions`、`viewable_impressions`、`viewable_rate`、`viewed_seconds`(計測可能インプレッションあたりの平均インビュー時間——`viewed_seconds` 最適化ゴールと対になる)、`standard` を持つオブジェクト。計測されたビューアビリティ値がレポートされるときは常に `standard` が存在すべき(SHOULD) | | `engagements` | 視聴を超える総広告エンゲージメント(プラットフォーム固有) | | `follows` | 配信に起因する新規フォロワーまたは購読 | | `saves` | 配信に起因する保存、ブックマーク、ピン | | `profile_visits` | 配信に起因するプラットフォーム内ページ訪問 | | `engagement_rate` | プラットフォーム固有のエンゲージメント率(engagements/impressions) | | `vendor_metric_values` | ベンダー実証のメトリクス(ビューアビリティ、アテンションなど)——ベンダーとメトリクス識別子ごとに 1 エントリ | > **支出由来のメトリクス。** セラーは、個々の `variant` オブジェクトに `roas` と `cost_per_acquisition` を埋めるべきではありません——支出はクリエイティブ全体に適用されるため、バリアントごとに帰属できません。これらのフィールドは `creative.totals` でのみ意味を持ちます。`by_event_type` エントリはイベントタイプごとに `count` と `value` を運びますが、支出由来のレートは運びません。 > **プラットフォーム条件付きフィールド。** `dooh_metrics` は DOOH キャンペーンでのみ存在します。エンゲージメントフィールド(`engagements`、`follows`、`saves`、`profile_visits`、`engagement_rate`)はプラットフォーム固有です。すべてのセラーが標準化されたフィールドでそれらを出力するわけではありません——代わりにエンゲージメントデータに `variant.ext` を使うものもあります。 ### バリアントオブジェクト 各バリアントは特定の実行を表します: 固定クリエイティブ(Tier 1)、プラットフォームが選択したアセットの組み合わせ(Tier 2)、または生成されたバリアント(Tier 3)。カタログ駆動パッケージでは、個別の広告実行としてレンダリングされた各カタログアイテムがバリアントになる — バリアントのマニフェストにはレンダリングされた特定のアイテムを含むカタログ参照が含まれます。 | フィールド | 説明 | | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `variant_id` | プラットフォームが割り当てたバリアント識別子 | | `manifest` | (オプション)レンダリングされたクリエイティブマニフェスト — 入力アセットではなく実際に配信された出力。format\_id と解決済みアセットを含みます。すべてのプラットフォームがすべてのバリアントのマニフェストを提供できるわけではありません。 | | `generation_context` | (オプション、Tier 3)生成をトリガーした入力シグナル — 例: ページトピック、会話テーマ、クエリカテゴリ。プラットフォームは生のユーザー入力ではなく、要約/匿名化されたシグナルを提供します。コンテンツコンテキストが AdCP コンテンツ標準を通じて管理される場合、特定のコンテンツアーティファクトにリンクする `artifact` 参照を含みます。ベンダー固有のコンテキスト構造のために `ext` をサポートします。 | | `ext` | (オプション)プラットフォーム固有のデータ。ソーシャルプラットフォームはプラットフォームごとに異なるエンゲージメントメトリクス(アップボート、コメント、シェア)にこれを使用します。 | | 標準メトリクス | すべての[配信メトリクスフィールド](#配信メトリクスフィールド)——`creative.totals` と同じ形状 | `creative_id` と `variant_id` は別個の名前空間です。正準的なビルドからデリバリーへの結合は `build_creative.variants[].build_variant_id` → プロモートされた `creative_id` → デリバリーの `creative_id` です。`variant_id` は、プラットフォームが配信した実行バリアント id のままです。 `ctr`、`completion_rate`、`roas`、`cost_per_click` などの派生メトリクスはプラットフォームが計算したものであり、丸め、アトリビューションウィンドウ、またはフィルタリングされたインプレッションにより、構成要素の単純な除算と等しくない場合があります。 ## Tier の動作 ### Tier 1: 標準クリエイティブ 1つのクリエイティブが1つのバリアントに1対1でマッピングされます。バリアントのメトリクスはクリエイティブのトータルと一致します。 ```json theme={null} { "media_buy_id": "mb_12345", "currency": "USD", "reporting_period": { "start": "2026-01-20T00:00:00-05:00", "end": "2026-01-27T23:59:59-05:00", "timezone": "America/New_York" }, "creatives": [ { "creative_id": "hero_video_30s", "totals": { "impressions": 150000, "spend": 7500, "clicks": 4500, "ctr": 0.03, "completion_rate": 0.72 }, "variants": [ { "variant_id": "hero_video_30s", "impressions": 150000, "spend": 7500, "clicks": 4500, "ctr": 0.03, "completion_rate": 0.72 } ] } ] } ``` ### プラットフォームエンゲージメントメトリクス ソーシャルおよびフィードネイティブプラットフォームは、エンゲージメントタイプがプラットフォームごとに異なるため、各バリアントの `ext` フィールドにエンゲージメントデータを含める: ```json theme={null} { "variant_id": "promoted_post_running_community", "impressions": 85000, "spend": 4250, "clicks": 2550, "ctr": 0.03, "ext": { "upvotes": 1240, "comments": 87, "shares": 34, "saves": 156, "comment_sentiment": "positive" } } ``` `ext` フィールドはプラットフォーム間で標準化されていない — 各プラットフォームが独自のエンゲージメントスキーマを定義します。複数のソーシャルプラットフォームにわたって集計するバイヤーはプラットフォーム固有のフィールドを共通モデルにマッピングすべきです。 ### Tier 2: アセットグループ最適化 バイヤーが `selection_mode: "optimize"` を持つフォーマットを使用して複数のアセット代替案を提供します。プラットフォームが組み合わせをテストし、どのアセットが選択されたかを示すマニフェストと共に各バリアントを返します。 ```json theme={null} { "media_buy_id": "mb_12345", "currency": "USD", "reporting_period": { "start": "2026-01-20T00:00:00-05:00", "end": "2026-01-27T23:59:59-05:00", "timezone": "America/New_York" }, "creatives": [ { "creative_id": "summer_campaign_assets", "totals": { "impressions": 200000, "spend": 10000, "clicks": 8000, "ctr": 0.04 }, "variants": [ { "variant_id": "var_headline_a_image_c", "manifest": { "format_id": { "agent_url": "https://creative.example.com", "id": "display_300x250" }, "assets": { "headline_0_text": { "asset_type": "text", "content": "Summer Sale - 50% Off" }, "image_0_url": { "asset_type": "image", "url": "https://cdn.brand.com/beach_hero.jpg", "width": 300, "height": 250 } } }, "impressions": 120000, "spend": 6000, "clicks": 6000, "ctr": 0.05 }, { "variant_id": "var_headline_b_image_d", "manifest": { "format_id": { "agent_url": "https://creative.example.com", "id": "display_300x250" }, "assets": { "headline_0_text": { "asset_type": "text", "content": "Shop Summer Styles" }, "image_0_url": { "asset_type": "image", "url": "https://cdn.brand.com/sunset_hero.jpg", "width": 300, "height": 250 } } }, "impressions": 80000, "spend": 4000, "clicks": 2000, "ctr": 0.025 } ] } ] } ``` ### Tier 3: ジェネレーティブクリエイティブ プラットフォームがブランドマニフェストと入力コンテキストからバリアントを生成します。`manifest` には生成されたアセットが含まれる — バイヤーが提出したものとは完全に異なる場合があります。 パブリッシャーが AdCP コンテンツ標準を使用する場合、`generation_context` に特定のコンテンツ(記事、動画など)にバリアントをリンクする `artifact` 参照を含めることができます。プラットフォームはベンダー固有のコンテキスト構造のために `ext` を使用することもできます。 ```json theme={null} { "media_buy_id": "mb_12345", "currency": "USD", "reporting_period": { "start": "2026-01-20T00:00:00-05:00", "end": "2026-01-27T23:59:59-05:00", "timezone": "America/New_York" }, "creatives": [ { "creative_id": "generative_banner", "totals": { "impressions": 300000, "spend": 15000, "clicks": 12000, "ctr": 0.04 }, "variants": [ { "variant_id": "gen_mobile_morning", "generation_context": { "context_type": "web_page", "artifact": { "property_id": { "type": "domain", "value": "techreview.example.com" }, "artifact_id": "article_semiconductor_trends_2026" }, "topic": "technology, semiconductors", "device_class": "mobile" }, "manifest": { "format_id": { "agent_url": "https://creative.example.com", "id": "display_300x250_generative" }, "assets": { "hero_image": { "asset_type": "image", "url": "https://cdn.creative.example.com/generated/mobile_morning_v1.jpg", "width": 300, "height": 250 }, "headline": { "asset_type": "text", "content": "Start Your Summer Right" } } }, "impressions": 180000, "spend": 9000, "clicks": 9000, "ctr": 0.05 }, { "variant_id": "gen_desktop_evening", "generation_context": { "context_type": "web_page", "topic": "lifestyle, evening entertainment", "device_class": "desktop" }, "manifest": { "format_id": { "agent_url": "https://creative.example.com", "id": "display_728x90_generative" }, "assets": { "hero_image": { "asset_type": "image", "url": "https://cdn.creative.example.com/generated/desktop_evening_v1.jpg", "width": 728, "height": 90 }, "headline": { "asset_type": "text", "content": "Evening Deals Await" } } }, "impressions": 120000, "spend": 6000, "clicks": 3000, "ctr": 0.025 } ] } ] } ``` ## バリアントのプレビュー `request_type: "variant"` を指定して `preview_creative` を使用し、特定のバリアントが配信時にどのように見えたかを確認します: ```json theme={null} { "request_type": "variant", "variant_id": "gen_mobile_morning" } ``` 各バリアントには完全な `manifest` が含まれているため、そのマニフェストを直接 `preview_creative` に渡して標準的な単一リクエストとして再レンダリングすることもできます。 ## 配信レポートとの関係 | タスク | プロトコル | 提供する情報 | | ------------------------ | ------- | ------------------------------------------------------------------ | | `get_media_buy_delivery` | メディアバイ | WHERE と HOW MUCH: インプレッション、スペンド、プレースメントデータ、オプションの `by_creative` 内訳 | | `get_creative_delivery` | クリエイティブ | WHAT RAN と HOW: バリアントマニフェストとバリアントレベルのメトリクス | 両方のレスポンスにわたってデータを相関させるために `media_buy_id` + `creative_id` を結合キーとして使用します。 セールスエージェントが両方のプロトコルを実装する場合、両方のタスクが同じエージェント URL で利用可能です。完全なパターンは[セールスエージェントのクリエイティブ機能](/docs/creative/sales-agent-creative-capabilities)を参照。 ## クロスエージェント集計 複数のセラーにわたってキャンペーンを実施する場合、各エージェントで `get_creative_delivery` を個別に呼び出して結果を相関させる: * **結合キー**: バイヤーが割り当てた `creative_id` を使用してエージェント間で同じクリエイティブを相関させる。アップロード時に `concept_id` を使用した場合、コンセプトでフィルタリングして関連するクリエイティブをグループ化します。 * **`variant_id` のスコープ**: バリアント ID はエージェントとクリエイティブ内で一意であり、グローバルには一意ではありません。2つのエージェントが同じ `variant_id` 値を持つバリアントを生成する場合があります。集計ダッシュボードを構築する際はエージェント URL でプレフィックスを付ける。 * **タイムゾーン処理**: 各エージェントは `reporting_period.timezone` を通じて独自のタイムゾーンでレポートする可能性があります。メトリクスを集計する前に共通のタイムゾーンに正規化します。 * **`max_variants` の選択**: エージェントは `max_variants` が結果セットを制限する場合に返すバリアントを選択します。ほとんどのエージェントはインプレッション量(最も多く配信されたものが先)で優先度を付ける。代表的なサンプリングのためには、単一の大きな `max_variants` 値に頼るのではなく、異なる時間範囲で複数の呼び出しを行います。 ## クロスエージェントダッシュボードの構築 複数のエージェントからの配信データを統合ビューに集計する場合、以下の手順に従う: 1. **収集**: 同じ `creative_ids` フィルターを使用して、各エージェントで `get_creative_delivery` を並列に呼び出す。 2. **タイムゾーンの正規化**: 合計する前に各エージェントの `reporting_period` を共通のタイムゾーンに変換します。 3. **`creative_id` でマージ**: エージェント間で `creative_id` によって結果をグループ化します。`totals`(impressions、spend、clicks)を合計します。`ctr` などの派生メトリクスは平均しない — 合計されたコンポーネントから再計算します。 4. **`variant_id` にプレフィックス**: `agent_url + variant_id` を組み合わせてグローバルに一意なバリアントキーを作成する(例: `https://sales.pinnaclemedia-example.com/var_a1b2c3`)。これにより、2つのエージェントが独立して同じバリアント ID を割り当てた場合の衝突を防ぐ。 5. **`concept_id` でグループ化**: キャンペーンレベルのロールアップのために、`concept_id` を使用してサイズとセラーにわたる関連クリエイティブをグループ化します。コンセプトからクリエイティブへのマッピングは各エージェントの `list_creatives` から取得します。 ```javascript theme={null} // 疑似コード: 3つのセラーから配信を集計 const agents = [pinnacle, novaSports, streamhaus]; const results = await Promise.all( agents.map(agent => agent.getCreativeDelivery({ creative_ids: ['acme_holiday_300x250'], start_date: '2026-11-01', end_date: '2026-11-15', }) ) ); const merged = {}; for (const [i, result] of results.entries()) { if (result.errors) continue; // 失敗したエージェントをスキップ、後でリトライ for (const creative of result.creatives) { const key = creative.creative_id; if (!merged[key]) merged[key] = { impressions: 0, spend: 0, clicks: 0, variants: [] }; merged[key].impressions += creative.totals.impressions; merged[key].spend += creative.totals.spend; merged[key].clicks += creative.totals.clicks || 0; for (const v of creative.variants || []) { merged[key].variants.push({ ...v, variant_id: `${agents[i].url}/${v.variant_id}`, // グローバルに一意 }); } } } // 派生メトリクスを再計算 for (const c of Object.values(merged)) { c.ctr = c.impressions > 0 ? c.clicks / c.impressions : 0; } ``` ## ケイパビリティ宣言 このタスクをサポートするエージェントは `list_creative_formats` レスポンスの `capabilities` 配列に `"delivery"` を宣言する: ```json theme={null} { "creative_agents": [{ "agent_url": "https://ads.seller-example.com", "capabilities": ["validation", "assembly", "preview", "delivery"] }] } ``` バイヤーはこれを `list_creative_formats` を呼び出してケイパビリティに `"delivery"` を持つエージェントの `creative_agents` 配列を確認することで発見します。これはセールスエージェントを含む、クリエイティブプロトコルを実装するすべてのエージェントに適用されます。 # list_creative_formats Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/task-reference/list_creative_formats list_creative_formats は任意の AdCP エージェントからアセット要件と技術的制約を含む広告フォーマット仕様を探索します。 クリエイティブエージェントがサポートするクリエイティブフォーマットを探索します。アセット要件や技術的制約を含む完全なフォーマット仕様を返します。 **応答時間**: 約 1 秒(データベース参照) **認証**: 不要(フォーマット探索のための公開エンドポイント) **Request Schema**: [`/schemas/v3/creative/list-creative-formats-request.json`](https://adcontextprotocol.org/schemas/v3/creative/list-creative-formats-request.json) **Response Schema**: [`/schemas/v3/creative/list-creative-formats-response.json`](https://adcontextprotocol.org/schemas/v3/creative/list-creative-formats-response.json) ## エージェントタイプ別の動作 Creative Protocol を実装するどのエージェントも `list_creative_formats` を提供できます。レスポンスはエージェントが担う役割によって異なります。 **専用クリエイティブエージェント**(例: `https://creative.adcontextprotocol.org`): * 自身が保有する **権威あるフォーマット定義** を返す * クリエイティブの構築とバリデーションのための完全な仕様を提供します **Media Buy Protocol のみを実装する営業エージェント**(例: `https://agenticadvertising.org/api/training-agent`): * **稼働中のプロダクトで使われているフォーマットのみ** を返す * 権威あるフォーマット仕様についてクリエイティブエージェントを参照します * 実際に購入可能なものに基づいて結果をフィルタリングします **両方のプロトコルを実装する営業エージェント** — 自前のフォーマット定義と参照フォーマットを合わせて返します。[セールスエージェントのクリエイティブ機能](/docs/creative/sales-agent-creative-capabilities)を参照。 営業エージェント固有の挙動は [list\_creative\_formats (Sales Agent)](/docs/creative/task-reference/list_creative_formats) を参照。 ## リクエストパラメーター | Parameter | Type | Required | Description | | ------------------------ | ----------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `format_ids` | FormatID\[] | No | 特定のフォーマット ID のみ返す(`get_products` レスポンス由来) | | `type` | string | No | *(非推奨)* 種別でフィルター: `audio`, `video`, `display`, `dooh`。代わりに `asset_types` フィルターを使用すること。 | | `asset_types` | string\[] | No | `image`, `video`, `audio`, `text`, `html`, `javascript`, `url` を受け付けるフォーマットに絞り込む(OR ロジック)。**`type` フィルターより推奨。** | | `max_width` | integer | No | 最大幅(ピクセル、以下を含む)- いずれかのレンダーが収まれば一致 | | `max_height` | integer | No | 最大高さ(ピクセル、以下を含む)- いずれかのレンダーが収まれば一致 | | `min_width` | integer | No | 最小幅(ピクセル、以上を含む) | | `min_height` | integer | No | 最小高さ(ピクセル、以上を含む) | | `is_responsive` | boolean | No | レスポンシブ対応(コンテナサイズに適応)に絞る | | `name_search` | string | No | 名前による検索(大文字小文字を区別しない部分一致) | | `wcag_level` | string | No | 少なくともこの WCAG レベルを満たすフォーマットに絞り込む: `A`、`AA`、`AAA`。[アクセシビリティ](/docs/creative/accessibility)を参照。 | | `disclosure_positions` | string\[] | No | これらすべてのディスクロージャーポジションをサポートするフォーマットに絞り込む。`disclosure_capabilities` が存在する場合はそれに対してマッチングし、存在しない場合は `supported_disclosure_positions` にフォールバックします。 | | `disclosure_persistence` | string\[] | No | `disclosure_capabilities` に少なくとも 1 つのポジションでこれらの持続性モードをすべて含むフォーマットに絞り込む。値: `continuous`、`initial`、`flexible`。 | | `output_format_ids` | FormatID\[] | No | `output_format_ids` にこれらのいずれかが含まれるフォーマットに絞り込む。これらの出力を生成できるフォーマットが返されます。`input_format_ids` で受け付ける入力を確認します。 | | `input_format_ids` | FormatID\[] | No | `input_format_ids` にこれらのいずれかが含まれるフォーマットに絞り込む。これらのクリエイティブを入力として受け付けるフォーマットが返されます。`output_format_ids` で生成できる出力を確認します。 | | `pagination` | object | No | ページネーション: `max_results`(1〜100、デフォルト 50)と `cursor`(前のレスポンスの不透明なカーソル) | ### 複数レンダーの寸法フィルタリング フォーマットは複数のレンダー(例: 動画 + コンパニオンバナー)を生成する場合があります。寸法フィルターは **「いずれかのレンダーが合致すれば OK」** というロジックです。 * `max_width: 300, max_height: 250` - **少なくとも 1 つ** のレンダーが 300×250 以下であれば一致 * ユースケース: 「300×250 の広告枠に収まるフォーマットを探す」 * 例: メイン動画 (1920×1080) とコンパニオンバナー (300×250) を持つフォーマットは、バナーが収まるため **一致** ## レスポンス | Field | Description | | ----------------- | --------------------------------------------------------------------------------- | | `formats` | フォーマット定義の完全な配列(format\_id、name、assets、renders を含む)。`type` フィールドは非推奨で省略される場合があります。 | | `creative_agents` | 追加フォーマットを提供する他のクリエイティブエージェントの任意配列 | 完全なフォーマット構造は [Format schema](https://adcontextprotocol.org/schemas/v3/core/format.json) を参照。 ### 再帰的な探索 クリエイティブエージェントは、追加フォーマットを提供する他のクリエイティブエージェントを参照する場合があります。 ```json theme={null} { "creative_agents": [{ "agent_url": "https://creative.adcontextprotocol.org", "agent_name": "AdCP Reference Creative Agent", "capabilities": ["validation", "assembly", "preview"] }] } ``` バイヤーは `creative_agents` を再帰的に問い合わせできます。**無限ループを避けるため、訪問済み URL を必ず追跡すること。** ## カタログ要件 フォーマットは `assets` 配列の `catalog` アセットタイプとしてカタログのニーズを宣言します。これにより、バイヤーはそのフォーマット向けのクリエイティブを送信する前にどのカタログを同期すべきかを把握できます。 ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "product_carousel_4x" }, "name": "Product Carousel (4 items)", "assets": [ { "item_type": "individual", "asset_id": "product_catalog", "asset_type": "catalog", "required": true, "requirements": { "catalog_type": "product", "min_items": 4, "required_fields": ["name", "price", "image_url"] } } ] } ``` カタログアセットは `asset_type: "catalog"` を使用し、以下を含む `requirements` オブジェクトを持ちます。 | Field | Type | Description | | ----------------- | --------- | ------------------------------------------------------------ | | `catalog_type` | string | 必須。カタログのタイプ(例: `product`、`store`、`job`) | | `min_items` | integer | カタログが含まなければなりませんアイテムの最小数 | | `max_items` | integer | フォーマットがレンダーできるアイテムの最大数。この制限を超えるアイテムは無視される | | `required_fields` | string\[] | すべてのアイテムに存在しなければなりませんフィールド | | `feed_formats` | string\[] | 受け付けるフィードフォーマット(例: `google_merchant_center`、`linkedin_jobs`) | カタログアセットが存在する場合、バイヤーはクリエイティブを送信する前に [`sync_catalogs`](/docs/media-buy/task-reference/sync_catalogs) で必要なカタログを同期すること。完全なライフサイクルは[カタログ](/docs/creative/catalogs)を参照。 ## よくあるシナリオ ### プロダクトのフォーマット ID から仕様を取得します ```javascript JavaScript theme={null} import { testAgent } from '@adcp/client/testing'; import { ListCreativeFormatsResponseSchema } from '@adcp/client'; // Get full specs for formats returned by get_products const result = await testAgent.listCreativeFormats({ format_ids: [ { agent_url: 'https://creative.adcontextprotocol.org', id: 'video_15s_hosted' }, { agent_url: 'https://creative.adcontextprotocol.org', id: 'display_300x250' } ] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } // Validate response against schema const validated = ListCreativeFormatsResponseSchema.parse(result.data); validated.formats.forEach(format => { console.log(`${format.name}: ${format.assets.length} assets required`); }); ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import ListCreativeFormatsRequest, FormatId async def main(): # Get full specs for formats returned by get_products result = await test_agent.list_creative_formats( ListCreativeFormatsRequest( format_ids=[ FormatId(agent_url='https://creative.adcontextprotocol.org', id='video_15s_hosted'), FormatId(agent_url='https://creative.adcontextprotocol.org', id='display_300x250') ] ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Failed: {result.errors}") for fmt in result.formats: print(f"{fmt.name}: {len(fmt.assets)} assets required") asyncio.run(main()) ``` ### アセットタイプでフォーマットを探す ```javascript JavaScript theme={null} import { testAgent } from '@adcp/client/testing'; import { ListCreativeFormatsResponseSchema } from '@adcp/client'; // Find formats that accept images and text const result = await testAgent.listCreativeFormats({ asset_types: ['image', 'text'] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = ListCreativeFormatsResponseSchema.parse(result.data); console.log(`Found ${validated.formats.length} formats`); // Examine asset requirements validated.formats.forEach(format => { console.log(`\n${format.name}:`); format.assets.forEach(asset => { const label = asset.asset_role ?? asset.asset_id; console.log(` - ${label}: ${asset.asset_type}`); }); }); ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import ListCreativeFormatsRequest async def main(): # Find formats that accept images and text result = await test_agent.list_creative_formats( ListCreativeFormatsRequest(asset_types=['image', 'text']) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Failed: {result.errors}") print(f"Found {len(result.formats)} formats") for fmt in result.formats: print(f"\n{fmt.name}:") for asset in fmt.assets: label = asset.asset_role or asset.asset_id print(f" - {label}: {asset.asset_type}") asyncio.run(main()) ``` ### サードパーティタグ対応フォーマットを探す ```javascript JavaScript theme={null} import { testAgent } from '@adcp/client/testing'; import { ListCreativeFormatsResponseSchema } from '@adcp/client'; // Find formats that accept JavaScript or HTML tags const result = await testAgent.listCreativeFormats({ asset_types: ['javascript', 'html'], max_width: 970, max_height: 250 }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = ListCreativeFormatsResponseSchema.parse(result.data); console.log(`Found ${validated.formats.length} third-party tag formats ≤ 970×250`); validated.formats.forEach(format => { const renders = format.renders || []; if (renders.length > 0) { const dims = renders[0].dimensions; console.log(`${format.name}: ${dims.width}×${dims.height}`); } }); ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import ListCreativeFormatsRequest async def main(): # Find formats that accept JavaScript or HTML tags result = await test_agent.list_creative_formats( ListCreativeFormatsRequest( asset_types=['javascript', 'html'], max_width=970, max_height=250 ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Failed: {result.errors}") print(f"Found {len(result.formats)} third-party tag formats ≤ 970×250") for fmt in result.formats: if fmt.renders: dims = fmt.renders[0].dimensions print(f"{fmt.name}: {dims.width}×{dims.height}") asyncio.run(main()) ``` ### 種別と寸法で絞り込む ```javascript JavaScript theme={null} import { testAgent } from '@adcp/client/testing'; import { ListCreativeFormatsResponseSchema } from '@adcp/client'; // Find video formats const result = await testAgent.listCreativeFormats({ type: 'video' }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = ListCreativeFormatsResponseSchema.parse(result.data); console.log(`Found ${validated.formats.length} video formats`); validated.formats.forEach(format => { const assetTypes = format.assets.map(a => a.asset_type).join(', '); console.log(`${format.name}: ${assetTypes}`); }); ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import ListCreativeFormatsRequest async def main(): # Find video formats result = await test_agent.list_creative_formats( ListCreativeFormatsRequest(type='video') ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Failed: {result.errors}") print(f"Found {len(result.formats)} video formats") for fmt in result.formats: asset_types = ', '.join(a.asset_type for a in fmt.assets) print(f"{fmt.name}: {asset_types}") asyncio.run(main()) ``` ### 名前で検索します ```javascript JavaScript theme={null} import { testAgent } from '@adcp/client/testing'; import { ListCreativeFormatsResponseSchema } from '@adcp/client'; // Find mobile-optimized formats const result = await testAgent.listCreativeFormats({ name_search: 'mobile' }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = ListCreativeFormatsResponseSchema.parse(result.data); console.log(`Found ${validated.formats.length} mobile formats`); validated.formats.forEach(format => { console.log(`- ${format.name} (${format.type})`); }); ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import ListCreativeFormatsRequest async def main(): # Find mobile-optimized formats result = await test_agent.list_creative_formats( ListCreativeFormatsRequest(name_search='mobile') ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Failed: {result.errors}") print(f"Found {len(result.formats)} mobile formats") for fmt in result.formats: print(f"- {fmt.name} ({fmt.type})") asyncio.run(main()) ``` ### レスポンシブフォーマットを探す ```javascript JavaScript theme={null} import { testAgent } from '@adcp/client/testing'; import { ListCreativeFormatsResponseSchema } from '@adcp/client'; // Find formats that adapt to container size const result = await testAgent.listCreativeFormats({ is_responsive: true, type: 'display' }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = ListCreativeFormatsResponseSchema.parse(result.data); console.log(`Found ${validated.formats.length} responsive display formats`); validated.formats.forEach(format => { console.log(`${format.name}: Adapts to container`); }); ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import ListCreativeFormatsRequest async def main(): # Find formats that adapt to container size result = await test_agent.list_creative_formats( ListCreativeFormatsRequest(is_responsive=True, type='display') ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Failed: {result.errors}") print(f"Found {len(result.formats)} responsive display formats") for fmt in result.formats: print(f"{fmt.name}: Adapts to container") asyncio.run(main()) ``` ### ビルドケイパビリティを探索します 一部のフォーマットは `output_format_ids` 経由で生成できる出力フォーマットを宣言します。マルチパブリッシャーのテンプレートツールのようなクリエイティブビルダーは、1 つのアセットグループを受け取り多くのパブリッシャー固有フォーマットを生成する場合があります。フォーマットトランスフォーマーは既存のクリエイティブを受け取り再フォーマットする場合があります。 フォーマットスキーマはリレーションシップの両側を表現します。 * **`input_format_ids`** — このフォーマットが入力として受け付ける既存のクリエイティブフォーマット * **`output_format_ids`** — このフォーマットが生成できる具体的な出力フォーマット これらのフィルターは AND で組み合わされます。フォーマットは指定したすべてのフィルターに一致しなければなりません。各フィルター内でのマッチングは OR(配列内のいずれかの ID が一致)です。ディメンションパラメーターなしの裸のフォーマット ID はそのフォーマットのすべてのパラメーター化されたバリアントに一致し、パラメーター化された ID は完全一致です。 注意: `asset_types` とこれらのフィルターは異なるものを対象にしています。クリエイティブマニフェストのみを入力として受け取るフォーマットは `assets` 配列にエントリを持たないため、`asset_types` と `input_format_ids` を組み合わせると通常は結果が返らない。 配信時のダイナミッククリエイティブ(広告配信時にデータフィードからレンダーする DCO プラットフォーム)はこれらのフィールドでは表現されない。それらのプラットフォームは `assets` 経由で入力を、フォーマット自体で出力を記述します。 #### 必要な出力フォーマットが決まっている場合、どんな入力が受け付けられるか? ```javascript test=false theme={null} import { testAgent } from '@adcp/client/testing'; import { ListCreativeFormatsResponseSchema } from '@adcp/client'; // I need portrait video — what can generate it? const result = await testAgent.listCreativeFormats({ output_format_ids: [ { agent_url: 'https://creative.adcontextprotocol.org', id: 'video_9x16_15s' } ] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = ListCreativeFormatsResponseSchema.parse(result.data); validated.formats.forEach(format => { const inputs = format.input_format_ids?.map(f => f.id) ?? ['(from brief)']; console.log(`${format.name} accepts: ${inputs.join(', ')}`); }); ``` ```python test=false theme={null} import asyncio from adcp.testing import test_agent from adcp.types import ListCreativeFormatsRequest, FormatId async def main(): result = await test_agent.list_creative_formats( ListCreativeFormatsRequest( output_format_ids=[ FormatId(agent_url='https://creative.adcontextprotocol.org', id='video_9x16_15s') ] ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Failed: {result.errors}") for fmt in result.formats: inputs = [f.id for f in fmt.input_format_ids] if fmt.input_format_ids else ['(from brief)'] print(f"{fmt.name} accepts: {', '.join(inputs)}") asyncio.run(main()) ``` #### 手持ちの入力フォーマットから、どんな出力を生成できるか? ```javascript test=false theme={null} import { testAgent } from '@adcp/client/testing'; import { ListCreativeFormatsResponseSchema } from '@adcp/client'; // I have a landscape 16:9 video — what can I transform it into? const result = await testAgent.listCreativeFormats({ input_format_ids: [ { agent_url: 'https://creative.adcontextprotocol.org', id: 'video_16x9_30s' } ] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = ListCreativeFormatsResponseSchema.parse(result.data); validated.formats.forEach(format => { const outputs = format.output_format_ids?.map(f => f.id) ?? []; console.log(`${format.name} → ${outputs.join(', ')}`); }); ``` ```python test=false theme={null} import asyncio from adcp.testing import test_agent from adcp.types import ListCreativeFormatsRequest, FormatId async def main(): result = await test_agent.list_creative_formats( ListCreativeFormatsRequest( input_format_ids=[ FormatId(agent_url='https://creative.adcontextprotocol.org', id='video_16x9_30s') ] ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Failed: {result.errors}") for fmt in result.formats: outputs = [f.id for f in fmt.output_format_ids] if fmt.output_format_ids else [] print(f"{fmt.name} → {', '.join(outputs)}") asyncio.run(main()) ``` ## フォーマット構造 各フォーマットには次が含まれます。 | Field | Description | | ------------------- | --------------------------------------------------------------------------- | | `format_id` | agent\_url と id を持つ構造化された識別子 | | `name` | 人が読みやすいフォーマット名 | | `type` | *(非推奨)* フォーマット種別(audio、video、display、dooh)。代わりに `asset_types` フィルターを使用すること。 | | `assets` | すべてのアセット配列。必須か任意かは `required` ブール値で示す | | `renders` | レンダリングされる成果物の配列(寸法、役割) | | `input_format_ids` | このフォーマットが入力マニフェストとして受け付けるクリエイティブフォーマット(生のアセットから機能するフォーマットでは省略) | | `output_format_ids` | このフォーマットが生成できる出力フォーマット(単一の固定出力を生成するフォーマットでは省略) | ### アセットロール 共通のアセットロールはアセットの用途を把握するのに役立つ。 * **`hero_image`** - メインビジュアル * **`hero_video`** - メインの動画コンテンツ * **`logo`** - ブランドロゴ * **`headline`** - メインテキスト * **`body_text`** - セカンダリテキスト * **`call_to_action`** - CTA ボタン文言 ## アセットタイプのフィルターロジック `asset_types` パラメーターは **OR ロジック** で、指定したいずれかのアセットタイプを受け付けるフォーマットが返されます。 **例**: `asset_types: ['html', 'javascript', 'image']` * html または javascript または image を受け付けるフォーマットが返る * ユースケース: 「手元のアセットタイプのどれかで使えるフォーマットを知りたい」 **特定の組み合わせを必要とするフォーマットを探す場合** は、取得後に結果をフィルタリングします。 ```javascript test=false theme={null} // 画像とテキストの両方を必要とするフォーマットを探す const result = await agent.listCreativeFormats(); const imageAndText = result.formats.filter(format => { const assetTypes = format.assets.map(a => a.asset_type); return assetTypes.includes('image') && assetTypes.includes('text'); }); ``` ## 複数レンダーフォーマットの寸法フィルタリング 複数の成果物を生成するフォーマット例: * **コンパニオンバナー付き動画** - メイン動画 (1920×1080) + バナー (300×250) * **アダプティブディスプレイ** - デスクトップ (728×90) + モバイル (320×50) * **DOOH 設置** - 寸法が異なる複数画面 寸法フィルターは **少なくとも 1 つのレンダー** が条件に合えば一致します。 ```javascript test=false theme={null} // いずれかのレンダーが 300×250 以下のフォーマットを探す const result = await agent.listCreativeFormats({ max_width: 300, max_height: 250 }); // 300×250 の枠に収まるレンダーが 1 つでもあれば返される // より大きいコンパニオンを含む場合もある ``` ## 実装上の要件 クリエイティブエージェントで `list_creative_formats` を実装する場合: 1. **権威あるフォーマットを返す** - 定義するフォーマットに関する完全な仕様を含めます 2. **他エージェントを参照する** - `creative_agents` を使って他のクリエイティブエージェントに委譲します 3. **能力を明記する** - validation、assembly、generation、preview などサポートする操作を示します 4. **フィルターをサポートする** - type、asset\_types、寸法などのフィルターパラメーターを実装します ## エラーハンドリング | Error Code | Description | Resolution | | ------------------ | -------------------------- | --------------------------------------- | | `FORMAT_NOT_FOUND` | リクエストされた format\_id が存在しない | get\_products のレスポンスから format\_id を確認する | | `INVALID_REQUEST` | フィルターパラメーターが不正 | パラメーターの型と値を確認する | | `AGENT_NOT_FOUND` | 参照先のクリエイティブエージェントが利用不可 | 廃止されたエージェント由来のフォーマットの可能性 | ## ベストプラクティス **1. format\_ids パラメーターを使う** `get_products` で返されたフォーマット仕様を取得する最も効率的な方法。 **2. フォーマット仕様をキャッシュする** フォーマット仕様は滅多に変わらないため、format\_id ごとにキャッシュして API 呼び出しを減らす。 **3. タグ系はアセットタイプで検索する** `asset_types: ['html']` や `['javascript']` を指定してタグを受け付けるフォーマットを探す。 **4. 複数レンダーフォーマットを考慮する** `renders` 配列の長さを確認し、複数の掲出面が必要かどうかを把握します。 **5. アセット要件を検証する** クリエイティブを構築する前に、アセットがフォーマット仕様に一致していることを確認します。 ## 次のステップ フォーマットを探索したら: 1. **クリエイティブを構築**: [`build_creative`](/docs/creative/task-reference/build_creative) でアセットをフォーマットに組み立てる 2. **プレビュー**: [`preview_creative`](/docs/creative/task-reference/preview_creative) でビジュアルを確認します 3. **検証**: [`sync_creatives`](/docs/creative/task-reference/sync_creatives) に `dry_run: true` を指定して検証します 4. **アップロード**: [`sync_creatives`](/docs/creative/task-reference/sync_creatives) でエージェントホスト型クリエイティブライブラリにアップロードします ## 参考 * [Format Schema](https://adcontextprotocol.org/schemas/v3/core/format.json) - フォーマット構造の全体像 * [Asset Types](/docs/creative/asset-types) - アセット仕様の詳細 * [Standard Formats](/docs/media-buy/capability-discovery/implementing-standard-formats) - IAB 互換のリファレンスフォーマット # list_creatives Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/task-reference/list_creatives list_creatives はカーソルベースのページネーションを使用して AdCP ライブラリ内のクリエイティブをフォーマット、ステータス、コンセプト、タグでブラウズ・フィルタリングします。 クリエイティブライブラリ内のクリエイティブをブラウズ・フィルタリングします。フォーマット、ステータス、コンセプト、タグ、日付範囲、ダイナミック変数によるフィルタリング、ページネーション、オプションのフィールドエンリッチメントをサポートします。 クリエイティブライブラリをホストするすべてのエージェント — クリエイティブエージェント(広告サーバー、クリエイティブ管理プラットフォーム)およびクリエイティブを管理するセールスエージェント — が実装します。 **レスポンスタイム**: \~1秒(シンプルなデータベースルックアップ) ## 概要 **主な機能:** * フォーマット、ステータス、タグ、日付、アサインメント、コンセプト、変数でフィルタリング * 作成日、更新日、名前、ステータス、アサインメント数でソート * 大きなライブラリのためのカーソルベースのページネーション * アサインメント、配信スナップショット、アイテム、ダイナミッククリエイティブ最適化(DCO)変数をオプションで含めます * レスポンスサイズを削減するために特定のフィールドのみを返す * クリエイティブコンセプトでフィルタリング(サイズ/フォーマットをまたぐ関連クリエイティブのグループ) * DCO クリエイティブを見つけてダイナミックコンテンツスロットを確認します ## リクエストパラメータ **スキーマ**: [`creative/list-creatives-request.json`](https://adcontextprotocol.org/schemas/v3/creative/list-creatives-request.json) ### コアパラメータ | パラメータ | タイプ | 必須 | 説明 | | ------------ | ------ | -- | --------------------------------------------- | | `filters` | object | No | フィルター条件 — 以下の[フィルタリングオプション](#フィルタリングオプション)を参照 | | `sort` | object | No | ソートパラメータ | | `pagination` | object | No | ページネーション制御 | ### データ含有オプション | パラメータ | タイプ | 必須 | 説明 | | -------------------------- | ------------------------------------------------------------------------------- | -- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `include_assignments` | boolean | No | パッケージアサインメント情報を含める(デフォルト: true) | | `include_snapshot` | boolean | No | 軽量な配信スナップショットを含める — ライフタイムインプレッションと最終配信日時(デフォルト: false)。詳細なアナリティクスには [`get_creative_delivery`](/docs/creative/task-reference/get_creative_delivery) を使用します。 | | `include_items` | boolean | No | カルーセルやネイティブ広告などのマルチアセットフォーマットのアイテムを含める(デフォルト: false) | | `include_variables` | boolean | No | ダイナミックコンテンツ変数定義を含める(デフォルト: false) | | `include_pricing` | boolean | No | 各クリエイティブに `pricing_options` を含める(デフォルト: false)。`account` が必要。 | | `include_purged` | boolean | No | ソフトパージされたクリエイティブのトゥームストーンを含める(デフォルト: false)。[パージされたトゥームストーン](#パージされたトゥームストーン)を参照。 | | `include_webhook_activity` | boolean | No | クリエイティブごとの最近のウェブフック発火を含める(デフォルト: false)。[ウェブフックアクティビティ](#ウェブフックアクティビティ)を参照。 | | `webhook_activity_limit` | integer | No | `include_webhook_activity: true` のときのクリエイティブごとの `webhook_activity[]` レコードの最大数(デフォルト: 50、範囲 1〜200)。 | | `account` | [AccountRef](/docs/building/by-layer/L2/accounts-and-agents#account-references) | No | 価格のためのアカウント参照。`include_pricing` とともに提供された場合、エージェントは各クリエイティブにこのアカウントのレートカードから `pricing_options` を返します。 | | `fields` | array | No | 返す特定のフィールド(すべてのフィールドを返すには省略)。スパースな選択のために `"pricing_options"` を含みます。 | ## フィルタリングオプション `filters` オブジェクトは以下のオプションの組み合わせ可能なフィルターをサポートする: | フィルター | タイプ | 説明 | | ---------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------- | | `accounts` | [AccountRef](/docs/building/by-layer/L2/accounts-and-agents#account-references)\[] | 所有アカウントでフィルタリング | | `format_ids` | FormatID\[] | 構造化フォーマット ID でフィルタリング | | `statuses` | [CreativeStatus](/docs/creative/specification#クリエイティブステータスのライフサイクル)\[] | 承認ステータスでフィルタリング | | `tags` | string\[] | タグでフィルタリング(すべてが一致する必要があります) | | `tags_any` | string\[] | タグでフィルタリング(いずれかが一致すればよい) | | `name_contains` | string | 大文字小文字を区別しない名前検索 | | `creative_ids` | string\[] | 特定のクリエイティブ ID でフィルタリング(最大100) | | `concept_ids` | string\[] | コンセプトグループでフィルタリング | | `has_variables` | boolean | ダイナミック変数を持つ DCO クリエイティブでフィルタリング | | `created_after` / `created_before` | date-time | 作成日範囲でフィルタリング | | `updated_after` / `updated_before` | date-time | 最終更新日範囲でフィルタリング | | `assigned_to_packages` | string\[] | パッケージアサインメントでフィルタリング \* | | `media_buy_ids` | string\[] | メディアバイアサインメントでフィルタリング \* | | `unassigned` | boolean | 未アサインのクリエイティブでフィルタリング \* | | `has_served` | boolean | 少なくとも1つのインプレッションが配信されたクリエイティブでフィルタリング \* | \* アサインメント関連フィルターはセールスエージェント固有。スタンドアロンクリエイティブエージェントはこれらを無視します。 **アーカイブ済みクリエイティブはデフォルトで除外されます。** 結果にアーカイブ済みクリエイティブを含めるには、`statuses` 配列に明示的に `"archived"` を含めます。サスペンドされたクリエイティブはアーカイブされていません。認可が期限切れの公開済み投稿の参照のような、回復可能なオフラインのクリエイティブを特に含めたい場合は `"suspended"` を含めます。 公開済み投稿の参照プロダクトでは、`list_creatives` はセラーが検査を認可されている下流のパブリッシャーのアイデンティティに限定されます。プロダクトが `publisher_identity` のような 2 つ目のプラットフォーム接続を必要とし、それが欠けている場合、セラーは `error.details.missing_connections[]` を伴う `AUTHORIZATION_REQUIRED` を返すべきです。`list_creatives` をグローバルなプラットフォーム投稿検索として提示すべきではありません。 ## ソートオプション 昇順または降順でさまざまなフィールドでソートする: ```json theme={null} { "sort": { "field": "created_date", "direction": "desc" } } ``` **利用可能なソートフィールド:** * `created_date` - クリエイティブが作成された日時(デフォルト) * `updated_date` - クリエイティブが最後に変更された日時 * `name` - クリエイティブ名(アルファベット順) * `status` - 承認ステータス * `assignment_count` - パッケージアサインメント数 ## ページネーション カーソルベースのページネーションで結果セットのサイズを制御する: ```json theme={null} { "pagination": { "max_results": 50, "cursor": "eyJjcmVhdGVkX2RhdGUiOi4uLn0" } } ``` ## レスポンスフォーマット **スキーマ**: [`creative/list-creatives-response.json`](https://adcontextprotocol.org/schemas/v3/creative/list-creatives-response.json) レスポンスはオプションのエンリッチメントを持つクリエイティブデータを提供します: ```json theme={null} { "query_summary": { "total_matching": 1, "returned": 1, "filters_applied": ["status=approved"] }, "pagination": { "has_more": false, "total_count": 1 }, "creatives": [ { "creative_id": "ft_88201", "name": "Holiday Sale - Medium Rectangle", "format_id": { "agent_url": "https://creative.example.com", "id": "display_static", "width": 300, "height": 250 }, "status": "approved", "created_date": "2026-01-15T10:30:00Z", "updated_date": "2026-01-15T14:20:00Z", "concept_id": "concept_holiday_2026", "concept_name": "Holiday 2026 Campaign", "variables": [ { "variable_id": "headline_text", "name": "Headline", "variable_type": "text", "default_value": "Holiday Sale - 50% Off", "required": true } ] } ], "format_summary": { "display_static_300x250": 1 }, "status_summary": { "approved": 1 } } ``` ### クリエイティブごとのフィールド | フィールド | タイプ | 説明 | | ----------------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `creative_id` | string | 一意のクリエイティブ識別子 | | `name` | string | 人間が読める名前 | | `format_id` | object | 構造化フォーマット参照 | | `status` | string | 承認ステータス | | `created_date` | string | 作成タイムスタンプ | | `updated_date` | string | 最終変更タイムスタンプ | | `assets` | object | クリエイティブアセット(画像、テキスト、URL など) | | `tags` | string\[] | 分類用タグ | | `concept_id` | string | クリエイティブコンセプト ID | | `concept_name` | string | 人間が読めるコンセプト名 | | `variables` | array | DCO 変数定義(`include_variables=true` の場合) | | `assignments` | object | パッケージアサインメント(`include_assignments=true` の場合) | | `snapshot` | object | 配信スナップショット(`include_snapshot=true` の場合) | | `snapshot_unavailable_reason` | string | スナップショットが欠けている理由 — `SNAPSHOT_UNSUPPORTED`、`SNAPSHOT_TEMPORARILY_UNAVAILABLE`、または `SNAPSHOT_PERMISSION_DENIED` | | `items` | array | マルチアセットフォーマットのアイテム(`include_items=true` の場合) | | `pricing_options` | [VendorPricingOption](/docs/creative/specification#価格)\[] | このクリエイティブの価格オプション(`include_pricing=true` かつ `account` 提供時)。ベンダーは複数のオプションを提供できます(ボリュームティア、コンテキスト固有のレート、プロダクトラインごとの異なるモデル)。`get_signals` や `list_content_standards` と同じパターン。 | ### 価格 `include_pricing=true` かつ `account` が提供された場合、各クリエイティブにはアカウントのレートカードから `pricing_options` が含まれます: ```json theme={null} { "pricing_options": [ { "pricing_option_id": "po_video_cpm", "model": "cpm", "cpm": 0.50, "currency": "USD" } ] } ``` バイヤーは、請求の検証のために、適用された `pricing_option_id`(`build_creative` レスポンスから)を `report_usage` で渡します。ベンダーは複数のオプションを提供できます——ボリューム/コミットメントのティア、コンテキスト固有のレート(プレミアム vs 標準のプレースメント)、または異なるプロダクトラインの完全に異なる価格モデル。これは[シグナル](/docs/signals/tasks/get_signals)と[コンテンツ標準](/docs/governance/content-standards/index)が使うのと同じパターンです。 ### 配信スナップショット `include_snapshot=true` の場合、各クリエイティブには「このクリエイティブはアクティブか?」「最後にいつ配信されたか?」などの運用上の質問のための軽量な配信スナップショットが含まれます。これはアナリティクスではない — 詳細なパフォーマンスデータには [`get_creative_delivery`](/docs/creative/task-reference/get_creative_delivery) を使用します。 ```json theme={null} { "snapshot": { "as_of": "2026-03-08T14:30:00Z", "staleness_seconds": 3600, "impressions": 145200, "last_served": "2026-03-07T22:15:00Z" } } ``` | フィールド | タイプ | 必須 | 説明 | | ------------------- | --------- | --- | ------------------------------------------ | | `as_of` | date-time | Yes | このスナップショットがキャプチャされた日時 | | `staleness_seconds` | integer | Yes | データの最大経過時間(秒) | | `impressions` | integer | Yes | ライフタイムインプレッション(任意の日付範囲にスコープされていない) | | `last_served` | date-time | No | このクリエイティブが最後に配信された日時。一度も配信されていない場合は存在しません。 | ### パージされたトゥームストーン クリエイティブが [`creative.purged`](https://adcontextprotocol.org/schemas/v3/creative/creative-purged-webhook.json) を介して `purge_kind: soft` で破棄されると、セラーはパージのタイムスタンプから 30 日間トゥームストーンを保持します。トゥームストーンは、リクエストが `include_purged: true` を設定した場合にのみ `list_creatives` に現れます: ```json theme={null} { "creative_id": "ft_87100", "name": "Holiday Sale - Leaderboard (purged)", "status": "approved", "purge": { "kind": "soft", "at": "2026-05-18T02:59:48Z", "reason_code": "retention_expired" } } ``` トゥームストーンの `status` フィールドは、**パージ前の値で凍結されます**(上記の例では、`"approved"` はパージ直前のクリエイティブの状態であって、現在の主張ではありません)。バイヤーはクリエイティブを消滅したものとして扱わなければなりません(MUST): 割り当て、配信操作、デリバリーの読み取りはもう適用されません。`purge` ブロックの存在が明確なシグナルです。 ハードパージされたクリエイティブ(`purge_kind: hard`、GDPR 第 17 条 / CCPA / 同等法の下での法的消去に使用)はトゥームストーンを保持しません。[`creative.purged`](https://adcontextprotocol.org/schemas/v3/creative/creative-purged-webhook.json) ウェブフックが唯一のシグナルです。根拠については [snapshot-and-log § ルール 4 の除外](/docs/protocol/snapshot-and-log#account-level-adopters-3-1)を参照してください。 ### ウェブフックアクティビティ `include_webhook_activity: true` の場合、返される各クリエイティブは、そのクリエイティブにスコープされた最近の発火——`creative.status_changed` と `creative.purged` の配信——の `webhook_activity[]` 配列を運びます。これは「パブリッシャーは発火したか? 自分のエンドポイントは受信したか? リトライの軌跡はクリーンか?」というバイヤーのデバッグ用サーフェスです——`get_media_buys` の `webhook_activity[]` と同じ形状と契約です。完全な規範的契約(保持、三状態の存在、リクエストフィールドの慣例)については[ウェブフックアクティビティログのパターン](/docs/protocol/snapshot-and-log#webhook-activity-log-pattern)を参照してください。 これらの発火に**サブスクライブする**には、`sync_accounts` を介してアカウントに [`notification_configs[]`](/docs/accounts/tasks/sync_accounts#account-level-webhook-subscriptions) エントリを登録します。セラーは、`event_types[]` にそのタイプを含む各エントリに対してサブスクライバーごとに発火します。この読み取りの `webhook_activity[]` は、実際に何が発火したかのバイヤー側のデバッグログです。 三状態の存在が適用されます: * **フィールドが省略** — セラーはこの読み取りでウェブフックアクティビティを公開しない。 * **`[]`** — セラーはフィールドを公開するが、このクリエイティブについて保持ウィンドウ内に発火がない。 * **空でない** — 実際のレコード、新しい順、`webhook_activity_limit`(最大 200)で上限。 エンドポイントのログには `idempotency_key` で相関させます。各レコードの `notification_type` が発火の種類を判別します: ```json theme={null} { "webhook_activity": [ { "idempotency_key": "whk_01HW9D2T3VXQ5M7K9N1P3R5S7U", "subscriber_id": "buyer-primary", "fired_at": "2026-05-18T14:20:00Z", "completed_at": "2026-05-18T14:20:00Z", "notification_type": "creative.status_changed", "attempt": 1, "status": "success", "url": "https://buyer.example/webhooks/adcp/creative", "http_status_code": 200, "response_time_ms": 142, "payload_size_bytes": 612, "error_message": null } ] } ``` バイヤーは「発火が届かなかった」を、次を組み合わせて診断します: (a) `list_accounts.accounts[].notification_configs[]` 上のサブスクライバー登録状態——正しい URL が正しい `event_types[]` でアクティブか?——と (b) `get_adcp_capabilities` を介したセラーのケイパビリティ宣言——セラーは自分がサブスクライブしたイベントタイプをサポートするか?。`webhook_activity` のフィールド省略だけでは、「セラーがログを公開しない」と「発火が起きなかった」を区別できません。 ### バイヤーのハンドラー(エンドツーエンド) バイヤーの `creative.status_changed` 用ウェブフックハンドラーは、各発火を `creative_id` を介してライブラリの状態と相関させ、`idempotency_key` で重複排除し、権威あるスナップショットのために `list_creatives` を再読み込みします([snapshot-and-log ルール 3](/docs/protocol/snapshot-and-log#the-five-rules) に従う): ```javascript theme={null} // POST /webhooks/adcp/creative async function handleCreativeWebhook(req, res) { // 1. Verify signature per the registered scheme (RFC 9421 by default). if (!verifyWebhookSignature(req)) return res.status(401).end(); const fire = req.body; // creative-status-changed-webhook or creative-purged-webhook const { notification_type, idempotency_key, creative_id, account_id } = fire; // 2. Dedupe at-least-once delivery. if (await alreadyProcessed(idempotency_key)) return res.status(200).end(); // 3. Re-read snapshot for authoritative state. Push is signal; snapshot is truth. const snapshot = await testAgent.listCreatives({ filters: { creative_ids: [creative_id] }, include_purged: notification_type === "creative.purged" }); // 4. Apply local effects from the snapshot, not the webhook payload. await reconcileCreative(snapshot.creatives[0]); await markProcessed(idempotency_key); return res.status(200).end(); } ``` このハンドラーが避ける二つの落とし穴: (1) ウェブフックのペイロードから直接状態を適用すること(順序と再発行がペイロードを非権威的にします)、(2) 重複排除をスキップすること(セラーは 2xx 以外でリトライし、見逃しイベントの警告で再発行します)。 ## アカウント要件 ライブラリをホストするクリエイティブエージェントはバイヤーがクリエイティブをクエリする前にアクセスを確立できるよう [accounts プロトコル](/docs/accounts/overview)(`sync_accounts` / `list_accounts`)を実装すべきです。これはセールスエージェントがメディアバイのために使用するのと同じ accounts プロトコルだ — 別バージョンはない。メディアバイのために accounts プロトコルを既に実装しているセールスエージェントは追加対応不要です。 ## 使用例 ### 変数を含むコンセプトスコープのクエリ 特定のコンセプト内のすべての承認済みクリエイティブを DCO 変数定義と共にリスト: ```json theme={null} { "filters": { "concept_ids": ["concept_holiday_2026"], "statuses": ["approved"] }, "include_variables": true, "sort": { "field": "created_date", "direction": "desc" } } ``` ### フォーマット固有のクエリ コンセプトをまたいで特定のフォーマット ID に一致するクリエイティブを検索: ```json theme={null} { "filters": { "format_ids": [ { "agent_url": "https://creative.example.com", "id": "display_static", "width": 300, "height": 250 }, { "agent_url": "https://creative.example.com", "id": "display_static", "width": 728, "height": 90 } ], "statuses": ["approved"] } } ``` ### DCO クリエイティブの検索 パーソナライズキャンペーン用のダイナミックコンテンツ変数を持つクリエイティブを検索: ```json theme={null} { "filters": { "has_variables": true, "statuses": ["approved"] }, "include_variables": true } ``` ### フィールド制限クエリ 選択ドロップダウン用の最小限のクリエイティブデータを取得: ```json theme={null} { "fields": ["creative_id", "name", "format_id", "status"], "include_assignments": false, "filters": { "statuses": ["approved"] }, "sort": { "field": "name", "direction": "asc" } } ``` ### ライブラリヘルスチェック 休眠しているアセットを特定するために配信スナップショットと共にアクティブなクリエイティブを検索: ```json theme={null} { "filters": { "media_buy_ids": ["mb_summer_2026", "mb_spring_2026"], "statuses": ["approved"] }, "include_assignments": true, "include_snapshot": true, "sort": { "field": "updated_date", "direction": "desc" } } ``` ## 関連タスク * [`get_creative_delivery`](/docs/creative/task-reference/get_creative_delivery) - 日付範囲、バリアント内訳、完全な配信メトリクスを含む詳細なパフォーマンスアナリティクス * [`build_creative`](/docs/creative/task-reference/build_creative) - ライブラリクリエイティブからマニフェストをビルドするか、ゼロから生成します * [`sync_creatives`](/docs/creative/task-reference/sync_creatives) - クリエイティブライブラリをホストするすべてのエージェントでクリエイティブアセットをアップロードして管理します * [`list_creative_formats`](/docs/creative/task-reference/list_creative_formats) - サポートされているクリエイティブフォーマットを発見します * [`preview_creative`](/docs/creative/task-reference/preview_creative) - クリエイティブマニフェストのプレビューを生成します # list_transformers Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/task-reference/list_transformers list_transformers はアカウントスコープのクリエイティブ transformer — build_creative が使うエージェント提供の選択可能なビルドケイパビリティ単位(ボイス、モデル、スタイル)— を発見する。 クリエイティブエージェントがアカウントに提供する **transformer** を発見します。transformer はメディアバイ製品のクリエイティブ類似物です: エージェント提供、アカウントスコープ、選択可能なビルドケイパビリティ単位(ボイス、モデル、スタイル、ディレクター)で、型付き構成表面とアカウントごとの価格設定を持ちます。ここで transformer を発見し、次に [`build_creative`](/docs/creative/task-reference/build_creative) で `transformer_id` を使って 1 つを選択します。 **Response Time**: 約 1 秒(アカウントスコープルックアップ) **Authentication**: アカウントスコープ。transformer、その列挙可能なオプション値、価格設定は呼び出し認証情報のために解決されます — あなたのアカウントにのみ存在する構成したカスタム値(例: クローンされたボイス)を含む。 **Request Schema**: [`/schemas/v3/creative/list-transformers-request.json`](https://adcontextprotocol.org/schemas/v3/creative/list-transformers-request.json) **Response Schema**: [`/schemas/v3/creative/list-transformers-response.json`](https://adcontextprotocol.org/schemas/v3/creative/list-transformers-response.json) [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) で `creative.supports_transformers: true` を設定するエージェントのみが提供します。 ## なぜ transformer か クリエイティブエージェントが露出するレンダーノブのセット — とその合法な値 — は **アカウント固有で動的** です。あなたの構成したボイスはグローバル enum やあなたが保持するリストではありません。*エージェント* がそれらを知り、追加するとセットが変わります。したがってディスカバリーは、`get_products` がアカウントスコープの在庫を表示するのと同じ方法で、エージェント → バイヤーと流れます。`list_transformers` はクリエイティブビルドケイパビリティのそのディスカバリー表面です。 エージェントは粒度を選びます: 別個のボイスやモデルは自身の transformer になりえ、または単一の transformer が `voice`/`model` を列挙可能な `config` param として露出しえます。どちらの方法でも同じ呼び出しを使います — transformer をリストし、値が欲しければ param を展開します。 ## リクエストパラメーター | Parameter | Type | Required | Description | | ------------------- | ----------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transformer_ids` | string\[] | No | これらの特定の transformer ID のみを返す | | `input_format_ids` | FormatID\[] | No | これらのフォーマットのいずれかを入力として受け入れる transformer にフィルター | | `output_format_ids` | FormatID\[] | No | これらの出力フォーマットのいずれかを生成できる transformer にフィルター | | `name_search` | string | No | 名前で transformer を検索(大文字小文字を区別しない部分一致) | | `brief` | string | No | transformer とそのオプション値をランク/フィルターする自然言語ブリーフ(例: "warm female Spanish-language voiceover")。完全なセットを返すのではなく意図にキュレート。 | | `expand_params` | string\[] | No | アカウントスコープのオプション **値** の **最初のページ** を `params[].options[]` にインラインで返す param `field` 名。リーンなデフォルト(記述子のみ)には省略。 | | `expand_pagination` | object\[] | No | 特定の param のオプションの **次のページ** をフェッチ、`{ transformer_id, field, options_cursor }` でスコープ(先のレスポンスの `params[].options_cursor` からのカーソル)。カーソルを保持したら `expand_params` の代わりに使う。 | | `include_pricing` | boolean | No | 各 transformer に `pricing_options` を含む。`account` が必要。 | | `account` | AccountRef | Conditional | `include_pricing` が true のとき必須。transformer はいずれにせよアカウントスコープ。 | | `pagination` | object | No | `max_results` と `cursor`(前のレスポンスからの不透明カーソル) | ### `expand` モード デフォルトでレスポンスは、小さな閉じた enum をインライン化した各 transformer の param **スキーマ** を返します(例: `mastering_preset`)。アカウントスコープの列挙可能な param の **値**(例: あなたのボイス)を得るには、`expand_params` でそれを名指します — それは `params[].options[]` の **最初のページ** を返します。param の値がトランケートされると、その `params[].options_cursor` が設定されます。**`expand_pagination`** に `{ transformer_id, field, options_cursor }` を渡して次のページをフェッチします(各 `(transformer, param)` は独立にページ化)。値はブリーフフィルターされます — "warm Spanish female voice" は 300 ボイスのカタログをすべてダンプするのではなく一握りに絞ります。別のオプションエンドポイントはありません。値列挙(とそのページネーション)はこの 1 つのツールのモードです。 ## レスポンス | Field | Description | | -------------- | --------------------------------------- | | `transformers` | transformer 記述子の配列(下記参照) | | `errors` | タスク固有のエラーと警告のオプション配列 | | `pagination` | より多くの transformer が利用可能なときのページネーションカーソル | 各 transformer は以下を運びます: | Field | Description | | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transformer_id` | 安定した id。`build_creative` `transformer_id` に渡す | | `name` | 人間可読な名前 | | `input_format_ids` / `output_format_ids` | 受け入れるものと生成するもの。`build_creative` ターゲットは `output_format_ids` のサブセットでなければならない(MUST)。空の `input_format_ids` はブリーフからビルドすることを意味する(純粋生成)。 | | `params` | Config ノブ([Transformer Param](https://adcontextprotocol.org/schemas/v3/core/transformer-param.json) を参照): `field`、`type`、`value_source`(`inline`/`range`/`enumerable`/`free_text`)、`allowed_values`/`minimum`/`maximum`、`options[]`+`options_cursor`(展開時)、`max_length`(`free_text` 用)、`default`。`free_text` はオープンなバイヤー作成文字列(例: `negative_prompt`)。param は生成カウントノブであってはならない(MUST NOT) — カウントは `max_variants`/`max_creatives` に乗る。 | | `pricing_options` | アカウントごとのレートカード(`include_pricing` 時)。`per_unit` モデルを使う(例: $/生成画像、$/秒)。価格オプションは異なる出力を異なる価格設定するため `applies_to_output_format_ids` を運べる(例: パブリッシャーフォーマットごとのマルチパブリッシャーテンプレート)。アンスコープオプションがデフォルトで、どのオプションにも一致しない出力(アンスコープデフォルトなし)は `UNPRICEABLE_OUTPUT` で拒否される(フォールバックなし)。適用されたオプションは `build_creative` レスポンスでリーフごとにエコーされ `report_usage` 経由で照合される。 | 完全なオブジェクトについては [Transformer スキーマ](https://adcontextprotocol.org/schemas/v3/core/transformer.json) を参照してください。 ## 一般的なシナリオ ### ブリーフのボイスを値とともに発見する ```javascript test=false theme={null} import { testAgent } from '@adcp/sdk/testing'; import { ListTransformersResponseSchema } from '@adcp/sdk'; const result = await testAgent.listTransformers({ account: { account_id: 'acct_acme' }, brief: 'warm female Spanish-language voiceover', output_format_ids: [{ agent_url: 'https://creative.audiostack.example', id: 'audio_vo' }], expand_params: ['voice'], include_pricing: true, }); const parsed = ListTransformersResponseSchema.parse(result); for (const t of parsed.transformers) { console.log(t.transformer_id, t.name); const voice = t.params?.find((p) => p.field === 'voice'); for (const opt of voice?.options ?? []) console.log(' voice:', opt.value, opt.metadata); } ``` ```python test=false theme={null} import asyncio from adcp.testing import test_agent from adcp import ListTransformersResponse async def main(): result = await test_agent.list_transformers( account={"account_id": "acct_acme"}, brief="warm female Spanish-language voiceover", output_format_ids=[{"agent_url": "https://creative.audiostack.example", "id": "audio_vo"}], expand_params=["voice"], include_pricing=True, ) parsed = ListTransformersResponse.model_validate(result) for t in parsed.transformers: print(t.transformer_id, t.name) asyncio.run(main()) ``` ### 次に選択した transformer でビルドする 選んだ `transformer_id` と型付き `config`(各 param の `field` でキー)を [`build_creative`](/docs/creative/task-reference/build_creative) に渡します: ```json test=false theme={null} { "transformer_id": "audiostack_voiceover", "config": { "voice": "isaac", "speaking_rate": 1.1, "mastering_preset": "podcast" }, "creative_manifest": { "format_id": { "agent_url": "https://creative.audiostack.example", "id": "script" }, "assets": { "script": { "asset_type": "text", "content": "Discover the new winter collection." } } }, "target_format_id": { "agent_url": "https://creative.audiostack.example", "id": "audio_vo" }, "account": { "account_id": "acct_acme" }, "idempotency_key": "0c1d2e3f-4a5b-6c7d-8e9f-a0b1c2d3e4f5" } ``` ## エラー処理 | Code | Meaning | Recovery | | ----------------- | -------------------------------------- | ------------------ | | `AUTH_MISSING` | 解決可能なアカウントなしに `include_pricing` が要求された | `account` を供給 / 認証 | | `INVALID_REQUEST` | 不正な形式のフィルターまたはページネーションカーソル | リクエストを修正 | 未知の `expand_params`(どの transformer も露出しない `field`)は無視され、エラーではありません — param は単に `options` を返しません。 ## さらに学ぶ * [build\_creative](/docs/creative/task-reference/build_creative) — transformer を選択しクリエイティブを生成(バリアントを含む) * [get\_adcp\_capabilities](/docs/protocol/get_adcp_capabilities) — `creative.supports_transformers` 判別子 * [Vendor pricing](https://adcontextprotocol.org/schemas/v3/core/vendor-pricing-option.json) — `per_unit` レートモデル # preview_creative Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/task-reference/preview_creative preview_creative は、既存のクリエイティブマニフェストを単一またはバッチモードで閲覧可能な出力へレンダリングし、URL、画像、または HTML の出力を返します。 `preview_creative` は、既存のクリエイティブマニフェストを閲覧可能な出力へレンダリングします。入力マニフェストを生成または変更することはありません——それには [`build_creative`](/docs/creative/task-reference/build_creative) を使います。単一クリエイティブのプレビューとバッチプレビュー(複数クリエイティブで 5〜10 倍高速)の両方をサポートします。 **リクエストスキーマ**: [`/schemas/v3/creative/preview-creative-request.json`](https://adcontextprotocol.org/schemas/v3/creative/preview-creative-request.json) **レスポンススキーマ**: [`/schemas/v3/creative/preview-creative-response.json`](https://adcontextprotocol.org/schemas/v3/creative/preview-creative-response.json) ## クイックスタート ### 単一クリエイティブのプレビュー ```json theme={null} { "request_type": "single", "creative_manifest": { /* includes format_id, assets */ } } ``` レスポンス: ```json theme={null} { "response_type": "single", "previews": [ { "preview_id": "prev_001", "renders": [ { "render_id": "render_1", "output_format": "url", "preview_url": "https://creative-agent.example.com/preview/abc123", "role": "primary" } ], "input": { "name": "Default", "macros": {} } } ], "expires_at": "2027-02-15T18:00:00Z" } ``` プライマリレンダーを iframe に埋め込みます: ```html theme={null} ``` ### 直接の HTML 埋め込み iframe のオーバーヘッドなしのより高速なレンダリングのために、HTML を直接リクエストします: ```json theme={null} { "request_type": "single", "creative_manifest": { /* includes format_id, assets */ }, "output_format": "html" } ``` レスポンスには生の HTML が含まれます: ```json theme={null} { "response_type": "single", "previews": [ { "preview_id": "prev_002", "renders": [ { "render_id": "render_1", "output_format": "html", "preview_html": "
...
", "role": "primary" } ], "input": { "name": "Default", "macros": {} } } ], "expires_at": "2027-02-15T18:00:00Z" } ``` `output_format: "html"` は信頼できるクリエイティブエージェントとのみ使ってください。直接の HTML 埋め込みは iframe のサンドボックスをバイパスします。 ### バッチプレビュー(複数クリエイティブ) 1 回の API 呼び出しで複数のクリエイティブをプレビューします(5〜10 倍高速): ```json theme={null} { "request_type": "batch", "requests": [ { "creative_manifest": { /* creative 1 */ } }, { "creative_manifest": { /* creative 2 */ } } ] } ``` レスポンスには結果が順序どおりに含まれます: ```json theme={null} { "response_type": "batch", "results": [ { "success": true, "creative_id": "creative_1", "response": { "previews": [...], "expires_at": "..." } }, { "success": true, "creative_id": "creative_2", "response": { "previews": [...], "expires_at": "..." } } ] } ``` ### バリアントプレビュー(配信後) 特定のバリアントが配信されたときにどう見えたかをプレビューします。`get_creative_delivery` レスポンスの `variant_id` を使います: ```json theme={null} { "request_type": "variant", "variant_id": "gen_mobile_morning" } ``` レスポンス: ```json theme={null} { "response_type": "variant", "variant_id": "gen_mobile_morning", "previews": [ { "preview_id": "prev_gen_morning", "renders": [ { "render_id": "render_1", "output_format": "url", "preview_url": "https://creative-agent.example.com/preview/variant/gen_mobile_morning", "role": "primary", "dimensions": { "width": 300, "height": 250 } } ] } ], "manifest": { "format_id": { "agent_url": "https://creative.example.com", "id": "display_300x250_generative" }, "assets": { "hero_image": { "asset_type": "image", "url": "https://cdn.creative.example.com/generated/mobile_morning_v1.jpg", "width": 300, "height": 250 }, "headline": { "asset_type": "text", "content": "Start Your Summer Right" } } }, "expires_at": "2027-02-15T18:00:00Z" } ``` `get_creative_delivery` の各バリアントは完全な `manifest` を含むため、それを再レンダリングするために、そのマニフェストを標準の単一リクエストとして直接 `preview_creative` に渡すこともできます。 ## リクエストパラメータ すべてのモードは、`request_type` を判別子とする単一のフラットなオブジェクトを使います。 | パラメータ | 型 | 必須 | 説明 | | ------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `request_type` | string | Yes | `"single"`、`"batch"`、または `"variant"` | | `creative_manifest` | object | Single | フォーマットに必要なすべてのアセットを持つ完全なクリエイティブマニフェスト。 | | `format_id` | FormatID | No | フォーマット識別子(agent\_url + id)。省略した場合は `creative_manifest.format_id` にデフォルト。単一モードで使用。 | | `inputs` | array | No | 複数のプレビューバリアント向けの入力セットの配列。単一モードで使用。 | | `quality` | string | No | `"draft"`(高速、低忠実度)または `"production"`(フル品質)。バッチモードでは、すべてのリクエストのデフォルトを設定。 | | `output_format` | string | No | `"url"`(デフォルト)または `"html"`。バッチモードでは、すべてのリクエストのデフォルトを設定。 | | `item_limit` | integer | No | プレビューバリアントごとにレンダリングするカタログアイテムの最大数。単一モードで使用。 | | `template_id` | string | No | カスタムフォーマットのレンダリング用の特定のテンプレート ID。単一モードで使用。 | | `requests` | array | Batch | 1〜50 のプレビューリクエストの配列。各項目は `creative_manifest`(必須)、`format_id`、`inputs`、`quality`、`output_format`、`item_limit`、`template_id` を受け入れる。 | | `variant_id` | string | Variant | `get_creative_delivery` からのプラットフォーム割り当てのバリアント識別子。 | | `creative_id` | string | No | コンテキスト用のクリエイティブ識別子。バリアントモードで使用。 | **必須**列の値: *Single* = `request_type` が `"single"` のときに必須、*Batch* = `"batch"` のとき必須、*Variant* = `"variant"` のとき必須。 ### 入力セット 異なるコンテキストを提供することで、複数のプレビューバリアントを生成します: ```json theme={null} { "inputs": [ { "name": "Desktop", "macros": { "DEVICE_TYPE": "desktop" } }, { "name": "Mobile", "macros": { "DEVICE_TYPE": "mobile" } }, { "name": "Morning Context", "context_description": "User commuting to work" } ] } ``` **利用可能なマクロ**: `DEVICE_TYPE`、`COUNTRY`、`CITY`、`DMA`、`GDPR`、`US_PRIVACY`、`CONTENT_GENRE` など。 **コンテキスト記述**: ホストリードの音声広告のような AI 生成コンテンツ向け。 ## レスポンスフォーマット ### 単一モードのレスポンス ```typescript theme={null} { response_type: "single"; previews: Preview[]; // One per input (or one default) interactive_url?: string; // Optional sandbox for interactive formats expires_at?: string; // Optional ISO 8601 expiration; omitted means no expiration } ``` ### バッチモードのレスポンス ```typescript theme={null} { response_type: "batch"; results: Array<{ success: boolean; creative_id: string; response?: { previews: Preview[]; expires_at?: string; }; errors?: Array<{ code: string; message: string; }>; }>; } ``` ### プレビューの構造 ```typescript theme={null} { preview_id: string; renders: Array<{ render_id: string; output_format: "url" | "html" | "both"; preview_url?: string; // When output_format is "url" or "both" preview_html?: string; // When output_format is "html" or "both" role: string; // "primary", "companion", etc. dimensions?: { width: number; height: number; }; }>; input: { name: string; macros?: Record; context_description?: string; }; } ``` **マルチレンダーフォーマット**: 一部のフォーマットは複数のピース(動画 + コンパニオンバナー)を生成します。それぞれが独自の `render_id` と `role` を持ちます。 ## 生成系クリエイティブのプレビュー 生成系フォーマット——コンテキストディスプレイ、AI 生成ネイティブ、会話型広告——では、クリエイティブは配信時まで存在しません。プレビューは二つの異なる目的を果たします: ### フライト前: 代表的なサンプル キャンペーンが実行される前に、単一またはバッチモードを使って、異なるコンテキストが与えられたときにエージェントが*何を生成しうるか*をプレビューします。配信時の条件をシミュレートするために `context_description` を持つ `inputs` を渡します: ```json theme={null} { "$schema": "/schemas/creative/preview-creative-request.json", "request_type": "single", "quality": "draft", "creative_manifest": { "format_id": { "agent_url": "https://ads.seller-example.com", "id": "contextual_display_generative" }, "assets": { "brief": { "asset_type": "brief", "name": "Sustainability story", "objective": "awareness", "messaging": { "key_messages": ["Highlight our sustainability story. Match tone to editorial context."] } } } }, "inputs": [ { "name": "Tech article", "context_description": "Article about semiconductor manufacturing" }, { "name": "Lifestyle blog", "context_description": "Blog post about sustainable living" } ] } ``` これらのプレビューは*代表的*であって決定的ではありません。実際の配信時の出力は、完全にはシミュレートできないライブシグナル(実際のページコンテンツ、ユーザーデバイス、時刻)に依存します。ブリーフとクリエイティブの方向性を高速に反復するにはドラフト品質を使い、ステークホルダーのレビューにはプロダクション品質を使います。 ### フライト後: 正確なリプレイ キャンペーンが実行された後、バリアントモードを使って、正確に何が配信されたかを確認します。`get_creative_delivery` からの `variant_id` を渡します: ```json theme={null} { "request_type": "variant", "variant_id": "gen_tech_mobile_001" } ``` レスポンスには、バリアントの実際のマニフェスト——エージェントがそのコンテキスト向けに生成した特定の見出し、画像、レイアウト——が含まれます。これは再生成ではなく、忠実なリプレイです。 ### 期待値の設定 | 側面 | 標準クリエイティブ | 生成系クリエイティブ | | ----------------------- | ----------------------- | --------------------------------------- | | フライト前プレビュー | 正確——見えるものが実行される | 代表的——シミュレートされた条件下でのブリーフに対するエージェントの解釈を示す | | フライト後プレビュー | フライト前と同じ | 正確——バリアントモードを介した配信済み出力の忠実なリプレイ | | `quality: "draft"` | 高速なワイヤーフレーム品質のレンダー | クリエイティブの方向性をレビューするための高速で低忠実度の生成 | | `quality: "production"` | フル忠実度のレンダー | ステークホルダーの承認のためのフル品質の生成 | | バリアントの数 | 通常 1(またはいくつかのデバイスバリアント) | 潜在的に数千——コンテキストごとに 1 つ | すべてのインプレッションが異なるクリエイティブを生成する生成系フォーマット(AI チャットやリアルタイムコンテキストのような)では、フライト前プレビューは*広告*そのものではなく*分布からのサンプル*として理解するのが最善です。ブリーフとブランドアイデンティティが分布を制約し、プレビューはエージェントがそれらの制約を正しく解釈することを検証できるようにします。 ### 会話型とインタラクティブなフォーマット 広告がステートフルなフォーマット——AI チャット、インタラクティブな体験、会話型ネイティブ——では、プレビューは追加の意味を帯びます: * **フライト前**は、代表的な最初のインタラクションまたはシミュレートされた会話をレンダリングします。プレビューレスポンスの `interactive_url` フィールド(存在する場合)は、レビュアーが体験と直接やり取りできるサンドボックスを提供します。異なる会話のエントリーポイントをシミュレートするには `context_description` を使います。 * **フライト後**のバリアントリプレイは、実際に起きたやり取りを示します。マルチターンフォーマットでは、バリアントマニフェストがエージェントが生成した完全なコンテンツ(メッセージシーケンス、レスポンス、表示されたメディアアセット)を捕捉します。詳細のレベルはエージェントに依存します——完全なトランスクリプトを提供するものもあれば、匿名化されたユーザーシグナルで要約されたコンテンツを提供するものもあります。 これらのフォーマットは、フライト前とフライト後の間のギャップが最も大きいです: フライト前プレビューは一つの可能な会話パスを近似できるだけですが、ライブ体験は各ユーザーに適応します。トーン、ガードレール、ブランドの一貫性を検証するのに十分なシナリオをプレビューしてください。 ### 品質の不一致 要求された品質レベルがサポートされていない場合、エージェントは提供できる最善の品質でレンダリングします。プロトコルはエージェントに両方のレベルのサポートを要求しません——1 つの忠実度でしか生成しないエージェントはパラメータを無視します。実際に使われた品質をエコーバックするレスポンスフィールドはないため、ワークフローで品質の正確さが重要な場合は、目視で検証するか、`list_creative_formats` を通じてエージェントの機能について尋ねてください。 ### プレビューの有効期限とバリアントの保持 プレビューレスポンスには `expires_at` タイムスタンプが含まれる場合があります。存在する場合、コンシューマはその時刻を過ぎたプレビュー URL を無効として扱い、再利用の前に再生成すべきです。`expires_at` が省略された場合、プレビュー URL は期限切れになりません。生成系クリエイティブでは、フライト前プレビューを再生成すると異なる出力が生成される可能性があります——同じブリーフとコンテキストでも、毎回異なるクリエイティブになりえます。 ### プレビュー URL の耐久性 `preview_url` は、バイヤーと MCPUI ホストがレンダリングするプロトコルリソースです。AdCP は 3.x でプレビューレンダー用の別個の耐久性のあるアセットポインタを定義しません。クリエイティブエージェントが内部のアセットキー、リソース URI、またはストレージオブジェクト ID を必要とする場合、スキーマが将来のフィールドを追加しない限り、それはエージェント内部に留まります。 クリエイティブエージェントは、各 `preview_url` をレスポンスの `expires_at` タイムスタンプまで参照解決可能に保たなければなりません(MUST)。`expires_at` が省略された場合、URL はプロトコルレベルの有効期限を持たず、エージェントが帯域外で明示的に失効またはパージするまで参照解決可能でなければなりません。マルチプロセスまたはマルチポッドのデプロイでは、プレビュー URL をポッドローカルの `Map`/LRU の状態だけで裏付けないでください。ブラウザのフェッチ、後のリファインメント呼び出し、またはレビュアーのセッションが、プレビューを作成したのとは異なるプロセスに着地する可能性があるためです。 耐久性のあるストレージは、恒久的な公開 CDN ホスティングを必要としません。ルートが共有ストレージ(データベース行、オブジェクトストアのキー、共有キャッシュ層など)から、表明されたライフタイムにわたってレンダーを回復できる限り、プレビュー URL はクリエイティブエージェントの認証済みプレビュールートを通じて解決できます。 バリアントプレビュー(フライト後)は、エージェントがバリアントデータを保持することに依存します。エージェントはバリアントデータを無期限に保持する必要はありません。エージェントがパージしたバリアントのバリアントプレビューをリクエストした場合、標準のエラーレスポンスを期待してください。長期間実行されるキャンペーンでは、バリアントプレビューが利用可能なままだと仮定するのではなく、定期的に取得してアーカイブしてください。 ## 例 ### デバイスバリアント ```json theme={null} { "$schema": "/schemas/creative/preview-creative-request.json", "request_type": "single", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "native_responsive" }, "assets": { "hero_image": { "asset_type": "image", "url": "https://cdn.example.com/hero.jpg", "width": 1200, "height": 627 }, "headline": { "asset_type": "text", "content": "Veterinarian Recommended" } } }, "inputs": [ { "name": "Desktop", "macros": { "DEVICE_TYPE": "desktop" } }, { "name": "Mobile", "macros": { "DEVICE_TYPE": "mobile" } } ] } ``` ### HTML 出力を伴うバッチ グリッドレイアウト用に複数のクリエイティブをプレビューします: ```json theme={null} { "request_type": "batch", "output_format": "html", "requests": [ { "creative_manifest": { /* creative 1 */ } }, { "creative_manifest": { /* creative 2 */ } } ] } ``` ### AI 生成音声のプレビュー ```json theme={null} { "$schema": "/schemas/creative/preview-creative-request.json", "request_type": "single", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "audio_host_read_30s" }, "assets": { "script_template": { "content": "This episode brought to you by {{BRAND_NAME}}..." }, "brand_voice": { "content": "Friendly, enthusiastic, conversational." } } }, "inputs": [ { "name": "Weather Podcast", "context_description": "Podcast discussing weather patterns" }, { "name": "Fitness Podcast", "context_description": "Podcast about marathon training" } ] } ``` ## HTTP ステータスコード **単一モード:** * **200 OK** - プレビューが正常に生成された * **400 Bad Request** - 無効なマニフェストまたは format\_id * **404 Not Found** - フォーマットがサポートされていない **バッチモード:** * **200 OK** - バッチが処理された(個々の `success` フィールドを確認) * **400 Bad Request** - 無効なバッチ構造 ## 主なポイント * すべてのレンダーの `preview_url` は、iframe 埋め込み用の HTML ページを返す * 10 件以上のプレビューのグリッドには `output_format: "html"` を使う(iframe のオーバーヘッドなし) * バッチモードは個別リクエストより 5〜10 倍高速 * プレビュー URL は `expires_at` が存在するときにのみ期限切れになる。`expires_at` の省略はプロトコルレベルの有効期限がないことを意味する * プレビュー URL を、URL の表明されたライフタイムを生き延びるストレージで裏付ける。プロセスローカルのマップは、単一プロセスのデモやプロセスライフタイムより短い URL にのみ適切 * 各結果の `success` フィールドを確認して部分的なバッチ失敗を扱う ## 関連ドキュメント * [高度なプレビューパターン](/docs/creative/task-reference/preview_creative-advanced) - キャッシュ、ワークフロー、実装ノート * [クリエイティブマニフェスト](/docs/creative/creative-manifests) - マニフェストの構造 # preview_creative(高度な活用) Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/task-reference/preview_creative-advanced クリエイティブプレビューを統合するための高度なワークフロー、キャッシュ戦略、実装上の注意点を紹介します。 基本的な使い方は [preview\_creative](/docs/creative/task-reference/preview_creative) を参照してください。 ## Common Workflows ### フォーマットショーケースページ 利用可能なフォーマットを閲覧できるカタログを構築します。 ```typescript theme={null} // 1. List all formats from creative agent const formats = await creative_agent.list_creative_formats(); // 2. Generate format card previews (batch + HTML) const formatPreviews = await creative_agent.preview_creative({ request_type: "batch", output_format: "html", requests: formats.formats.map(format => ({ format_id: format.format_id, creative_manifest: format.format_card.manifest })) }); // 3. Render in a grid function FormatCatalog({ formatPreviews }) { return (
{formatPreviews.results.map((result, idx) => ( result.success && (
) ))}
); } ``` ### キャンペーンレビュ―用グリッド 配信前にすべてのクリエイティブを確認します。 ```typescript theme={null} const campaignCreatives = await getCreativesForCampaign(campaignId); const previews = await creative_agent.preview_creative({ request_type: "batch", output_format: "html", requests: campaignCreatives.map(c => ({ format_id: c.format_id, creative_manifest: c.manifest })) }); function CampaignReview({ previews }) { return (
{previews.results.map((result, idx) => (
))}
); } ``` ### Web コンポーネントとの統合 遅延読み込みを行う本番アプリケーション向けの例です。 ```html theme={null}
``` **メリット:** * CSS 分離のための Shadow DOM * ビューポートに入ったときだけ読み込む遅延読み込み * フレームワークに依存しません ## 出力形式の選択 **以下のケースでは `output_format: "url"`(デフォルト)を使用します。** * セキュリティが最優先(サードパーティ製クリエイティブなど) * インタラクティブなプレビューツールを構築する場合 * iframe での分離が必要な場合 **以下のケースでは `output_format: "html"` を使用します。** * 10 件以上のフォーマットカタログを構築する場合 * 20 件以上のクリエイティブを並べるキャンペーンレビューグリッドを作る場合 * サーバーサイドレンダリングを行う場合 * 信頼できるクリエイティブエージェントのみを扱う場合 ## キャッシュ戦略 `format_id` とマニフェストのハッシュの組み合わせで個別のプレビュー結果をキャッシュします。 ```typescript theme={null} function cachePreviewResults(results, formatIds, manifests) { results.forEach((result, idx) => { if (result.success) { const cacheKey = `${formatIds[idx]}:${hashManifest(manifests[idx])}`; cache.set(cacheKey, result.response, result.response.expires_at); } }); } async function getPreviewsWithCache(formatIds, manifests) { const cached = []; const toFetch = []; formatIds.forEach((id, idx) => { const cacheKey = `${id}:${hashManifest(manifests[idx])}`; const cachedResult = cache.get(cacheKey); if (cachedResult && !isExpired(cachedResult.expires_at)) { cached[idx] = cachedResult; } else { toFetch.push({ idx, id, manifest: manifests[idx] }); } }); // Batch fetch only missing previews if (toFetch.length > 0) { const fetched = await client.preview_creative({ request_type: "batch", output_format: "html", requests: toFetch.map(f => ({ format_id: f.id, creative_manifest: f.manifest })) }); fetched.results.forEach((result, i) => { cached[toFetch[i].idx] = result.response; }); } return cached; } ``` **ポイント:** * バッチではなく format\_id + マニフェストハッシュごとにキャッシュします * \[A,B,C] をリクエストしたらそれぞれ個別にキャッシュします * 後で \[B,C,D] をリクエストしたら D だけ取得します * キャッシュしたプレビューを使う前に必ず `expires_at` を確認します ## プレビュー URL のストレージ プレビュー URL は、単なるトランスポートの利便性ではなく、レビューのリソースです。バイヤー、ブラウザ、または MCPUI ホストは、元の `preview_creative` 呼び出しが返った後、ポッドの再起動後、またはレンダーを作成したのとは異なるポッドから、`preview_url` をフェッチする場合があります。 本番エージェントでは、表明されたライフタイムを通じてすべての `preview_url` を解決するのに十分なプレビュー状態を永続化してください: * `expires_at` が存在する場合、そのタイムスタンプまでレンダーを利用可能に保ちます。 * `expires_at` が省略された場合、URL をプロトコル層では期限切れにならないものとして扱い、明示的な帯域外の失効またはパージまで利用可能に保ちます。 * マルチプロセスまたはマルチポッドのデプロイでは共有ストレージを使います: データベースのメタデータ + オブジェクトストレージ、共有キャッシュ層、または耐久性のあるセッション状態からレンダーを回復できる認証済みプレビュールート。 * プロセスローカルの `Map` または LRU ストレージは、単一プロセスのデモ、ローカル開発、または表明されたライフタイムが実際に保証できるプロセスライフタイムより短い URL に限定してください。 ## エラーハンドリング ```typescript theme={null} const response = await client.preview_creative({ request_type: "batch", requests: formatRequests }); const succeeded = response.results.filter(r => r.success); const failed = response.results.filter(r => !r.success); if (failed.length > 0) { console.log(`${failed.length} previews failed`); failed.forEach((result) => { console.error(` - ${result.error.code}: ${result.error.message}`); }); } // Display successful previews, show error states for failures function displayPreviews(results) { return results.map((result, idx) => { if (result.success) { return ; } else { return retryPreview(idx)} />; } }); } ``` ## 単一リクエストからバッチへの移行 **以前(逐次):** ```python theme={null} previews = [] for format in formats: preview = await client.preview_creative( request_type="single", format_id=format.format_id, creative_manifest=format.format_card.manifest ) previews.append(preview) # Total time: N × 250ms = 5000ms for 20 formats ``` **移行後(バッチ):** ```python theme={null} response = await client.preview_creative( request_type="batch", output_format="html", requests=[ { "format_id": fmt.format_id, "creative_manifest": fmt.format_card.manifest } for fmt in formats ] ) # Total time: ~500ms for 20 formats ``` ## ユースケースパターン ### デバイス別バリエーション ```json theme={null} { "inputs": [ { "name": "Desktop", "macros": { "DEVICE_TYPE": "desktop" } }, { "name": "Mobile", "macros": { "DEVICE_TYPE": "mobile" } }, { "name": "CTV", "macros": { "DEVICE_TYPE": "ctv" } } ] } ``` ### 地域別バリエーション ```json theme={null} { "inputs": [ { "name": "NYC", "macros": { "CITY": "New York", "DMA": "501" } }, { "name": "LA", "macros": { "CITY": "Los Angeles", "DMA": "803" } } ] } ``` ### プライバシー対応テスト ```json theme={null} { "inputs": [ { "name": "Full consent", "macros": { "GDPR": "1", "GDPR_CONSENT": "CPc7TgP..." } }, { "name": "No consent", "macros": { "GDPR": "1", "GDPR_CONSENT": "" } }, { "name": "LAT enabled", "macros": { "LIMIT_AD_TRACKING": "1" } } ] } ``` ### AI 生成コンテンツのバリエーション ```json theme={null} { "inputs": [ { "name": "Morning commute", "context_description": "User commuting to work" }, { "name": "Evening relaxation", "context_description": "User relaxing at home" } ] } ``` ## 実装メモ ### クリエイティブエージェント向け **必須:** 1. `preview_url` から完全な HTML ページを返す 2. すべてのメディアタイプ(画像・動画・音声・インタラクティブ)を処理します 3. 入力パラメーターをレスポンスにエコーします 4. レンダリング前にマニフェストを検証します 5. マクロ値を適用する(またはデフォルトを使用) 6. プレビュー URL を、ロードバランシングと再起動を URL の表明されたライフタイムにわたって生き延びるストレージで裏付けます 7. セキュリティサンドボックスを実装します 8. 無期限に保持すべきでないプレビューに適切な有効期限を設定する(24–48 時間) **オプションの拡張:** * `hints` オブジェクトを提供する(メディアタイプ、寸法、時間など) * `embedding` メタデータを提供する(サンドボックス方針、CSP) * レスポンシブデザインをサポートします * アクセシビリティ要素を含めます ### バイヤー向け 1. `preview_url` を iframe で表示するだけで特別なレンダリングは不要 2. 特定シナリオでは `inputs` 配列を利用します 3. `input` フィールドを確認しマクロ適用を検証します 4. 承認のためプレビュー URL をクライアントと共有します 5. 高度なテストには `interactive_url` を活用します ### パブリッシャー向け 1. プレビュー URL から一貫した HTML を返す 2. レスポンシブなプレビューページを実装します 3. フォーマット内の `supported_macros` で対応マクロを明記します 4. プレビューと本番の違いを明確にします 5. テスト用に `interactive_url` の提供を検討します ## 関連ドキュメント * [preview\_creative](/docs/creative/task-reference/preview_creative) - 基本的な使い方とパラメーター * [Creative Manifests](/docs/creative/creative-manifests) - マニフェスト構造 * [Universal Macros](/docs/creative/universal-macros) - 使用可能なマクロ値 # sync_creatives Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/task-reference/sync_creatives sync_creatives は一括アップロード、アップサートセマンティクス、ジェネレーティブクリエイティブサポートを使用して AdCP ライブラリのクリエイティブアセットをアップロード・管理します。 クリエイティブライブラリにクリエイティブアセットをアップロードして管理します。一括アップロード、アップサートセマンティクス、ジェネレーティブクリエイティブをサポートします。クリエイティブライブラリをホストするすべてのエージェント — クリエイティブエージェント(広告サーバー、クリエイティブ管理プラットフォーム)およびクリエイティブを管理するセールスエージェント — が実装します。 **レスポンスタイム**: 即時〜数日(`completed` を返すか、数時間/数日かかるレビューのために `submitted` を返す) **リクエストスキーマ**: [`creative/sync-creatives-request.json`](https://adcontextprotocol.org/schemas/v3/creative/sync-creatives-request.json) **レスポンススキーマ**: [`creative/sync-creatives-response.json`](https://adcontextprotocol.org/schemas/v3/creative/sync-creatives-response.json) ## クイックスタート クリエイティブアセットをアップロードする: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncCreativesResponseSchema } from "@adcp/sdk"; import { randomUUID } from "node:crypto"; const result = await testAgent.syncCreatives({ account: { brand: { domain: "acmecorp.com" }, operator: "acmecorp.com", sandbox: true, }, idempotency_key: randomUUID(), creatives: [ { creative_id: "creative_video_001", name: "Summer Sale 30s", format_id: { agent_url: "https://creative.adcontextprotocol.org", id: "video_standard_30s", }, assets: { video: { asset_type: "video", url: "https://cdn.example.com/summer-sale-30s.mp4", width: 1920, height: 1080, duration_ms: 30000, }, }, }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } // Validate response against schema const validated = SyncCreativesResponseSchema.parse(result.data); // Three-shape discriminated union: errors | submitted | creatives if ("errors" in validated && validated.errors && !("creatives" in validated) && !("status" in validated)) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("status" in validated && validated.status === "submitted") { // Whole sync queued asynchronously — poll tasks/get with task_id or await webhook console.log(`Sync queued as task ${validated.task_id}: ${validated.message ?? ""}`); } else if ("creatives" in validated) { console.log(`Synced ${validated.creatives.length} creatives`); for (const c of validated.creatives) { // c.status carries review state: approved, pending_review, rejected, processing, archived if (c.status === "pending_review" || c.status === "processing") { console.log(` ${c.creative_id}: awaiting review (${c.status})`); } } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from uuid import uuid4 async def main(): result = await test_agent.simple.sync_creatives( account={ 'brand': {'domain': 'acmecorp.com'}, 'operator': 'acmecorp.com', 'sandbox': True }, idempotency_key=str(uuid4()), creatives=[{ 'creative_id': 'creative_video_001', 'name': 'Summer Sale 30s', 'format_id': { 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'video_standard_30s' }, 'assets': { 'video': { 'asset_type': 'video', 'url': 'https://cdn.example.com/summer-sale-30s.mp4', 'width': 1920, 'height': 1080, 'duration_ms': 30000 } } }] ) # Three-shape discriminated union: errors | submitted | creatives if getattr(result, 'status', None) == 'submitted': # Whole sync queued asynchronously — poll tasks/get with task_id or await webhook print(f"Sync queued as task {result.task_id}: {getattr(result, 'message', '') or ''}") return if getattr(result, 'errors', None) and not getattr(result, 'creatives', None): raise Exception(f"Operation failed: {result.errors}") print(f"Synced {len(result.creatives)} creatives") for c in result.creatives: # c.status carries review state: approved, pending_review, rejected, processing, archived if getattr(c, 'status', None) in ('pending_review', 'processing'): print(f" {c.creative_id}: awaiting review ({c.status})") asyncio.run(main()) ``` **注意:** クリエイティブごとの非同期レビューは、同期的な成功レスポンスの `creatives[].status`(例: `pending_review`)で表面化されます。*操作全体*がキューに入れられる場合(バッチ取り込み、同期をゲートするガバナンスレビュー)、レスポンスはトップレベルの `status: "submitted"` と `task_id` を持つ submitted エンベロープになります。[非同期承認ワークフロー](#非同期承認ワークフロー)を参照。 ## 書き込み後読み取りの可視性 同期的な `sync_creatives` の成功レスポンスを介して受理されたクリエイティブは、レスポンスが返される前にクリエイティブライブラリにコミットされていなければなりません(MUST)。それらは、同じアカウントと認可された呼び出し元からの後続の `list_creatives` 呼び出しに対して即座に可視でなければならず(MUST)、レビューのライフサイクルステータスが `processing` または `pending_review` のクリエイティブも含みます。 同期的な成功の分岐でクリエイティブを確認応答しつつ、ライブラリへの書き込みを後のバックグラウンドコミットまでバッファリングする実装は非準拠です。同期操作全体が返却前にコミットできない場合は、代わりに submitted タスクエンベロープを使います。その場合、可視性の要件は、受理されたクリエイティブとともにタスクが完了した時点で適用されます。 ## リクエストパラメータ | パラメータ | タイプ | 必須 | 説明 | | ----------------- | ----------- | --- | --------------------------------------------------------------------------------------------------------------------------------- | | `account` | object | Yes | この同期の広告主/ワークスペースを識別するアカウント参照([account-ref](/docs/accounts/overview)) | | `idempotency_key` | string | Yes | リトライを安全にするクライアント生成のキー。リクエストごとに新鮮な UUID などの一意の値を使います。 | | `creatives` | Creative\[] | Yes | アップロード/更新するクリエイティブアセット(最大100) | | `creative_ids` | string\[] | No | 同期スコープを特定のクリエイティブ ID に限定するオプションフィルター。これらのクリエイティブのみが影響を受け、その他はそのまま残る。部分更新とエラー復旧に有用。 | | `assignments` | array | No | 一括アサインメント用の `{creative_id, package_id}` オブジェクトの配列。アサインメントごとにオプションの `weight` と `placement_ids`。 | | `dry_run` | boolean | No | true の場合、変更を適用せずにプレビューする(デフォルト: false) | | `validation_mode` | string | No | 検証の厳格さ: `"strict"`(デフォルト)または `"lenient"` | | `delete_missing` | boolean | No | true の場合、この同期に含まれないクリエイティブはアーカイブされます(デフォルト: false)。`creative_ids` と組み合わせることはできません。アクティブで一時停止されていないパッケージにアサインされているクリエイティブは削除できません。 | ### クリエイティブオブジェクト | フィールド | タイプ | 必須 | 説明 | | ------------------- | ------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `creative_id` | string | Yes | このクリエイティブの一意の識別子 | | `name` | string | Yes | 人間が読める名前 | | `format_id` | FormatId | Conditional | レガシーの名前付きフォーマットのパス。`agent_url` と `id` を持つ構造化オブジェクト。`format_kind` を省略する場合は必須。`format_kind` と相互排他的。 | | `format_kind` | CanonicalFormatKind | Conditional | 3.1+ の正準的なフォーマットのパス。`format_id` を省略する場合は必須。`format_id` と相互排他的。 | | `format_option_ref` | FormatOptionRef | No | プロダクトまたはパブリッシャーのフォーマットオプションへの任意の構造化された参照。ターゲットプロダクトが同じ `format_kind` を持つ複数のオプションを持つ場合に必須。 | | `assets` | object | Yes | ロール名をキーとしたアセット(例: `{video: {...}, thumbnail: {...}}`)。カタログは `asset_type: "catalog"` を持つアセットとして含まれます。[カタログ](/docs/creative/catalogs)を参照。 | | `tags` | string\[] | No | クリエイティブ整理のための検索可能なタグ | レガシーの `format_id` か正準的な `format_kind` のパスのいずれかを提供し、両方は決して提供しません。新しい 3.1+ の統合では、ルーティングがプロダクトの宣言したフォーマットオプションに依存する場合、`format_kind` と `format_option_ref` を優先すべきです。 ### build\_creative バリアントのプロモート バイヤーが生成されたビルドリーフを保持する場合、正準的なプロモートは、保持した `build_variant_id` を新しい `creative_id` として使うことです。セラーは別個のリネージマッピングを保持する必要はありません。デリバリーレポートは通常の `creative_id` を通じてビルドリーフに結合できます。 ```json test=false theme={null} { "idempotency_key": "550e8400-e29b-41d4-a716-446655440000", "account": { "account_id": "acct_acmecorp" }, "creatives": [ { "creative_id": "bv_card01_a", "name": "Summer card - studio take", "format_id": { "agent_url": "https://creative.example.com", "id": "display_300x250" }, "assets": { "headline_0_text": { "asset_type": "text", "content": "Summer Sale - 50% Off" }, "image_0_url": { "asset_type": "image", "url": "https://cdn.example.com/beach-hero.jpg", "width": 300, "height": 250 } } } ] } ``` その後、[`get_creative_delivery`](/docs/creative/task-reference/get_creative_delivery) は `creative_id` を結合キーとして使います。保持した `build_variant_id` を使う代わりに別のライブラリ id を発行するワークフローは、将来のスコープ付きリネージフィールドが採用されない限り、このプロトコルで可視な結合を失います。 ### アセット構造 アセットはロール名をキーとします。各ロールにはアセットの詳細が含まれます: ```json test=false theme={null} { "assets": { "video": { "url": "https://cdn.example.com/video.mp4", "width": 1920, "height": 1080, "duration_ms": 30000 }, "thumbnail": { "url": "https://cdn.example.com/thumb.jpg", "width": 300, "height": 250 } } } ``` ### アサインメント構造 アサインメントはリクエストレベルにあり、クリエイティブ ID をパッケージ ID にマッピングします。メディアバイを管理しないスタンドアロンのクリエイティブエージェントはこのフィールドを無視します。 ```json test=false theme={null} { "assignments": [ { "creative_id": "creative_video_001", "package_id": "pkg_premium" }, { "creative_id": "creative_video_001", "package_id": "pkg_standard" }, { "creative_id": "creative_display_002", "package_id": "pkg_standard" } ] } ``` 公開済み投稿(published-post)参照プロダクトでは、アセットのロールは通常 `published_post` で、ペイロードにはアップロードされたメディアバイトではなく投稿 URL またはプラットフォームの投稿 ID が含まれます。バイヤーは、プラットフォーム固有の `format_id` を作成する代わりに、正準的なクリエイティブのパス(例: `format_kind: "video_hosted"` とプロダクトの `format_option_ref`)を通じてこれらのアセットを送信できます。セラーが投稿を解決できるが、投稿を所有するパブリッシャーのアイデンティティのような必要な下流のプラットフォーム接続を欠く場合、訂正可能なエラーは `AUTHORIZATION_REQUIRED` です。新しい実装は `error.details.missing_connections[]` を含めるべきで、呼び出し元は人を正しい接続フローに通し、認可が回復した後にリトライできます。 ## レスポンス レスポンスは識別共用体を使います——レスポンスは三つの形のうちちょうど一つを持ち、混在することはありません: **1. 同期的な成功** — クリエイティブごとの結果: * `creatives` - 処理された各クリエイティブの結果(成功と失敗の両方のアイテムを含む) * `dry_run` - これがドライランだったかどうかを示すブール値(オプション) **2. 終端のエラー** — 処理されたクリエイティブなし: * `errors` - 操作レベルのエラーの配列(認証失敗、サービス利用不可) **3. Submitted タスクエンベロープ** — 操作全体が非同期でキューに入れられた(バッチ取り込み、同期をゲートするガバナンスレビュー): * `status` - 常に `"submitted"` * `task_id` - `tasks/get` によるポーリングまたは完了時のウェブフック受信のためのハンドル * `message` - キューの状態を説明する任意の人が読めるテキスト 最終的なクリエイティブごとの `creatives` 配列は、submitted エンベロープではなくタスクの完了アーティファクトに載ります。アイテムごとの非同期レビュー(同期の残りが解決される間、一つのクリエイティブが `pending_review` になっている)は、ここではなく、その項目に `status: "pending_review"` を持つ同期的な成功の分岐に属します。 **成功レスポンスの各クリエイティブに含まれるもの**: * すべてのリクエストフィールド * `platform_id` - プラットフォームの内部 ID(`action` が `failed` でない場合) * `action` - この同期が実行したライフサイクル操作: `created`、`updated`、`unchanged`、`failed`、`deleted` * `status` - **助言的**なレビューライフサイクルの状態([`CreativeStatus`](https://adcontextprotocol.org/schemas/v3/enums/creative-status.json)): `processing`、`pending_review`、`approved`、`suspended`、`rejected`、`archived`。UI のヒントおよびポーリングスケジューリングのシグナルであり、支出の認可ゲートでは**ありません**。`action` と直交します——`action` は同期が何をしたかを、`status` はクリエイティブがレビューライフサイクルのどこにいるかを記述します。値は `CreativeStatus` のみに由来し、`CreativeAction` からは決して来ません(`created`/`updated`/`failed` を `status` に入れないでください)。非同期レビューのセラーは `processing` または `pending_review` を返します。同期レビューのセラーは、終端の値(`approved`/`rejected`)や、回復可能な依存関係/認可のゲートが配信を妨げる場合に `suspended` を返してもよい(MAY)。**バイヤーは、このレスポンスの `status: approved` に基づいて下流の支出やパッケージの有効化をゲートしてはなりません(MUST NOT)**——支出をコミットする前に `list_creatives` または署名付きのレビューウェブフックで突き合わせてください。権威ある状態は常に `list_creatives` を介します。`action` が `failed` または `deleted` の場合は**省略されなければなりません(MUST)**——失敗したアイテムには意味のあるレビュー状態がなく(`errors` を参照)、削除されたアイテムはライブラリから消えています。スキーマは条件付き制約によってこの省略ルールを強制します。 * `errors` - エラーメッセージの配列(`action: "failed"` の場合のみ) * `warnings` - 非致命的な警告の配列(オプション) **完全なフィールドリストについてはスキーマを参照**: [sync-creatives-response.json](https://adcontextprotocol.org/schemas/v3/creative/sync-creatives-response.json) ## 一般的なシナリオ ### 一括アップロード 1回の呼び出しで複数のクリエイティブをアップロードする: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncCreativesResponseSchema } from "@adcp/sdk"; import { randomUUID } from "node:crypto"; const result = await testAgent.syncCreatives({ account: { brand: { domain: "acmecorp.com" }, operator: "acmecorp.com", sandbox: true, }, idempotency_key: randomUUID(), creatives: [ { creative_id: "creative_display_001", name: "Summer Sale Banner 300x250", format_id: { agent_url: "https://creative.adcontextprotocol.org", id: "display_300x250", }, assets: { image: { url: "https://cdn.example.com/banner-300x250.jpg", width: 300, height: 250, }, }, }, { creative_id: "creative_video_002", name: "Product Demo 15s", format_id: { agent_url: "https://creative.adcontextprotocol.org", id: "video_standard_15s", }, assets: { video: { url: "https://cdn.example.com/demo-15s.mp4", width: 1920, height: 1080, duration_ms: 15000, }, }, }, { creative_id: "creative_display_002", name: "Summer Sale Banner 728x90", format_id: { agent_url: "https://creative.adcontextprotocol.org", id: "display_728x90", }, assets: { image: { url: "https://cdn.example.com/banner-728x90.jpg", width: 728, height: 90, }, }, }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncCreativesResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("creatives" in validated) { console.log(`Successfully synced ${validated.creatives.length} creatives`); validated.creatives.forEach((creative) => { console.log(` ${creative.name}: ${creative.platform_id}`); }); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from uuid import uuid4 async def main(): result = await test_agent.simple.sync_creatives( account={ 'brand': {'domain': 'acmecorp.com'}, 'operator': 'acmecorp.com', 'sandbox': True }, idempotency_key=str(uuid4()), creatives=[ { 'creative_id': 'creative_display_001', 'name': 'Summer Sale Banner 300x250', 'format_id': { 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'display_300x250' }, 'assets': { 'image': { 'url': 'https://cdn.example.com/banner-300x250.jpg', 'width': 300, 'height': 250 } } }, { 'creative_id': 'creative_video_002', 'name': 'Product Demo 15s', 'format_id': { 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'video_standard_15s' }, 'assets': { 'video': { 'url': 'https://cdn.example.com/demo-15s.mp4', 'width': 1920, 'height': 1080, 'duration_ms': 15000 } } }, { 'creative_id': 'creative_display_002', 'name': 'Summer Sale Banner 728x90', 'format_id': { 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'display_728x90' }, 'assets': { 'image': { 'url': 'https://cdn.example.com/banner-728x90.jpg', 'width': 728, 'height': 90 } } } ] ) # Check for operation-level errors first if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") print(f"Successfully synced {len(result.creatives)} creatives") for creative in result.creatives: print(f" {creative.name}: {creative.platform_id}") asyncio.run(main()) ``` ### ジェネレーティブクリエイティブ クリエイティブエージェントを使用してブランドアイデンティティデータからクリエイティブを生成します。完全なワークフローの詳細は[ジェネレーティブクリエイティブガイド](/docs/creative/generative-creative)を参照。 ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncCreativesResponseSchema } from "@adcp/sdk"; import { randomUUID } from "node:crypto"; const result = await testAgent.syncCreatives({ account: { brand: { domain: "acmecorp.com" }, operator: "acmecorp.com", sandbox: true, }, idempotency_key: randomUUID(), creatives: [ { creative_id: "creative_gen_001", name: "AI-Generated Summer Banner", format_id: { agent_url: "https://creative.adcontextprotocol.org", id: "display_300x250", }, assets: { manifest: { url: "https://cdn.example.com/brand.json", }, }, }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncCreativesResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("creatives" in validated) { console.log( "Generative creative synced:", validated.creatives[0].creative_id ); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from uuid import uuid4 async def main(): result = await test_agent.simple.sync_creatives( account={ 'brand': {'domain': 'acmecorp.com'}, 'operator': 'acmecorp.com', 'sandbox': True }, idempotency_key=str(uuid4()), creatives=[{ 'creative_id': 'creative_gen_001', 'name': 'AI-Generated Summer Banner', 'format_id': { 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'display_300x250' }, 'assets': { 'manifest': { 'url': 'https://cdn.example.com/brand.json' } } }] ) # Check for operation-level errors first if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") print(f"Generative creative synced: {result.creatives[0].creative_id}") asyncio.run(main()) ``` ### ドライラン検証 アップロードせずにクリエイティブ設定を検証する: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncCreativesResponseSchema } from "@adcp/sdk"; import { randomUUID } from "node:crypto"; const result = await testAgent.syncCreatives({ account: { brand: { domain: "acmecorp.com" }, operator: "acmecorp.com", sandbox: true, }, idempotency_key: randomUUID(), dry_run: true, creatives: [ { creative_id: "creative_test_001", name: "Test Creative", format_id: { agent_url: "https://creative.adcontextprotocol.org", id: "video_standard_30s", }, assets: { video: { url: "https://cdn.example.com/test-video.mp4", width: 1920, height: 1080, duration_ms: 30000, }, }, }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncCreativesResponseSchema.parse(result.data); if ("errors" in validated && validated.errors && validated.errors.length > 0) { console.log("Validation errors found:"); validated.errors.forEach((error) => console.log(` - ${error.message}`)); } else { console.log("Validation passed! Ready to sync."); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from uuid import uuid4 async def main(): result = await test_agent.simple.sync_creatives( account={ 'brand': {'domain': 'acmecorp.com'}, 'operator': 'acmecorp.com', 'sandbox': True }, idempotency_key=str(uuid4()), dry_run=True, creatives=[{ 'creative_id': 'creative_test_001', 'name': 'Test Creative', 'format_id': { 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'video_standard_30s' }, 'assets': { 'video': { 'url': 'https://cdn.example.com/test-video.mp4', 'width': 1920, 'height': 1080, 'duration_ms': 30000 } } }] ) if hasattr(result, 'errors') and result.errors: error_messages = [error.message for error in result.errors] raise Exception(f"Validation errors: {error_messages}") print('Validation passed! Ready to sync.') asyncio.run(main()) ``` ### creative\_ids フィルターを使用したスコープ更新 大きなライブラリから特定のクリエイティブのみを更新し、その他には影響を与えない: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncCreativesResponseSchema } from "@adcp/sdk"; import { randomUUID } from "node:crypto"; // ライブラリの 100+ のうち 2 つのクリエイティブのみを更新 const result = await testAgent.syncCreatives({ account: { brand: { domain: "acmecorp.com" }, operator: "acmecorp.com", sandbox: true, }, idempotency_key: randomUUID(), creative_ids: ["creative_video_001", "creative_display_001"], creatives: [ { creative_id: "creative_video_001", name: "Summer Sale 30s - Updated", format_id: { agent_url: "https://creative.adcontextprotocol.org", id: "video_standard_30s", }, assets: { video: { url: "https://cdn.example.com/updated-video.mp4", width: 1920, height: 1080, duration_ms: 30000, }, }, }, { creative_id: "creative_display_001", name: "Summer Sale Banner - Updated", format_id: { agent_url: "https://creative.adcontextprotocol.org", id: "display_300x250", }, assets: { image: { url: "https://cdn.example.com/updated-banner.jpg", width: 300, height: 250, }, }, }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncCreativesResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Update failed: ${JSON.stringify(validated.errors)}`); } if ("creatives" in validated) { console.log( `Updated ${validated.creatives.length} creatives, others untouched` ); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from uuid import uuid4 async def main(): # ライブラリの 100+ のうち 2 つのクリエイティブのみを更新 result = await test_agent.simple.sync_creatives( account={ 'brand': {'domain': 'acmecorp.com'}, 'operator': 'acmecorp.com', 'sandbox': True }, idempotency_key=str(uuid4()), creative_ids=['creative_video_001', 'creative_display_001'], creatives=[ { 'creative_id': 'creative_video_001', 'name': 'Summer Sale 30s - Updated', 'format_id': { 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'video_standard_30s' }, 'assets': { 'video': { 'url': 'https://cdn.example.com/updated-video.mp4', 'width': 1920, 'height': 1080, 'duration_ms': 30000 } } }, { 'creative_id': 'creative_display_001', 'name': 'Summer Sale Banner - Updated', 'format_id': { 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'display_300x250' }, 'assets': { 'image': { 'url': 'https://cdn.example.com/updated-banner.jpg', 'width': 300, 'height': 250 } } } ] ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Update failed: {result.errors}") print(f"Updated {len(result.creatives)} creatives, others untouched") asyncio.run(main()) ``` **creative\_ids フィルターを使用する理由:** * スコープ更新: 指定されたクリエイティブのみが変更され、ライブラリに 100+ あっても同様 * エラー復旧: 一括同期の検証失敗後に失敗したクリエイティブのみをリトライ * パフォーマンス: スコープが事前にわかっているとパブリッシャーが処理を最適化できます * 安全性: 明示的なターゲティングにより意図しない変更のリスクを低減 ## 非同期承認ワークフロー 二つの異なる非同期パターンがあります——エージェントの振る舞いに応じて正しいものを選んでください: **クリエイティブごとの非同期レビュー**(一般的): 同期操作自体は同期的に解決されますが、1 つ以上のクリエイティブが下流のレビュー(ブランドセーフティ、ポリシーコンプライアンス)を必要とします。レビュー中のアイテムは、`status: "pending_review"`(または取り込み中は `processing`)とともに同期的な成功レスポンスで返ってきます。バイヤーは `list_creatives` またはウェブフックを通じて終端の状態を突き合わせます。 **操作レベルの非同期**(あまり一般的でない): 同期全体がキューに入れられます——取り込みがバッチ化されている、またはガバナンスレビューが同期全体をゲートしているため、セラーが応答前にアイテムごとの結果を返せません。レスポンスは submitted エンベロープです: * トップレベルの `status: "submitted"` と `task_id` * `message` — 任意の人が読める説明 * このエンベロープには `creatives` 配列なし `tasks/get` をポーリングするか、ウェブフックを待ちます。完了アーティファクトが、アイテムごとの `action`/`status` の結果を持つ `creatives` 配列を運びます。操作レベルの失敗は、タスク上の `status: "failed"` として表面化します。 **参照:** ウェブフック設定については[ウェブフック](/docs/building/by-layer/L3/webhooks)を参照。 ## 同期モード ### アップサート(デフォルト) * `creative_id` で既存のクリエイティブを作成または更新します * パッケージアサインメントをマージする(追加的) * 提供されたフィールドを更新し、その他はそのままにします * 特定のクリエイティブにスコープを制限するために `creative_ids` フィルターを使用します ### ドライラン * 変更を加えずにリクエストを検証します * エラーと警告を返す * アセットを処理したりクリエイティブを作成したりしません * プリフライト検証チェックに使用します ## エラー処理 | エラーコード | 説明 | 解決方法 | | ----------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | | `INVALID_FORMAT` | フォーマットがプロダクトでサポートされていない | `list_creative_formats` でプロダクトのサポートフォーマットを確認する | | `ASSET_PROCESSING_FAILED` | アセットファイルが破損しているか無効 | アセットがフォーマット要件(コーデック、ディメンション、デュレーション)を満たしているか確認する | | `PACKAGE_NOT_FOUND` | パッケージ ID がメディアバイに存在しない | `package_id` を確認する。レガシーなパッケージの相関には `get_media_buys` + パッケージの `context.buyer_ref` を使う | | `BRAND_SAFETY_VIOLATION` | クリエイティブがブランドセーフティスキャンに失敗 | パブリッシャーのブランドセーフティガイドラインに対してコンテンツをレビューする | | `FORMAT_MISMATCH` | アセットがフォーマット要件に一致しない | アセットタイプと仕様がフォーマット定義と一致しているか確認する | | `CREATIVE_IN_ACTIVE_DELIVERY` | クリエイティブがアクティブで一時停止されていないパッケージにアサインされている(更新と `delete_missing` による削除をブロック) | まずパッケージを一時停止するか、新しいクリエイティブバージョンを作成する | ## ベストプラクティス 1. **アップサートセマンティクスを使用する** — 同じ `creative_id` で既存のクリエイティブを更新し、重複を作成しません。これにより反復的なクリエイティブ開発が可能。注意: アクティブな配信中のクリエイティブは更新がブロックされます(#7 を参照)。 2. **まず検証する** — `dry_run: true` を使用して実際のアップロード前にエラーをキャッチします。帯域幅と処理時間を節約できます。 3. **アサインメントをバッチ処理する** — 更新間の競合状態を避けるために、すべてのパッケージアサインメントを1回の同期呼び出しに含めます。 4. **CDN ホストのアセット** — 高速処理のために公開アクセス可能な CDN URL を使用します。プラットフォームはプロキシ遅延なしに直接アセットをフェッチできます。 5. **ブランドアイデンティティ** — ジェネレーティブクリエイティブの場合、処理失敗を避けるために同期前にブランドアイデンティティスキーマを検証します。 6. **フォーマットサポートを確認する** — アップロード前に `list_creative_formats` を使用してプロダクトがクリエイティブフォーマットをサポートしているか確認します。 7. **アクティブ配信の保護** — アクティブで一時停止されていないパッケージにアサインされているクリエイティブは、`delete_missing` で更新または削除できません。まずパッケージを一時停止するか、`update_media_buy` でクリエイティブのアサインを解除するか、別の `creative_id` で新しいクリエイティブを作成します。 ## 関連タスク * [`list_creative_formats`](/docs/creative/task-reference/list_creative_formats) - アップロード前にサポートフォーマットを確認します * [`list_creatives`](/docs/creative/task-reference/list_creatives) - ライブラリ内のクリエイティブをブラウズ・フィルタリングします * [`build_creative`](/docs/creative/task-reference/build_creative) - ライブラリクリエイティブからマニフェストをビルドするか、ゼロから生成します * [`preview_creative`](/docs/creative/task-reference/preview_creative) - クリエイティブマニフェストのプレビューを生成します * [クリエイティブアセットタイプ](/docs/creative/asset-types) - アセットの技術要件 # Template Format IDs Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/template-format-ids AdCP のテンプレート format_id は、サイズごとに個別のフォーマットを作らずに、単一のフォーマット定義で多数のディメンションバリアントをサポートできるようにします。 テンプレートフォーマットにより、単一のフォーマット定義で、バリアントごとに個別のフォーマット定義を作らずに、複数のディメンションまたはデュレーションのバリアントをサポートできます。これは、パブリッシャーが多数の類似バリアントをサポートする際のフォーマット爆発を排除します。 ## 課題: フォーマット爆発 テンプレートフォーマットがないと、各ディメンションバリアントに個別のフォーマット定義と format\_id が必要です: ```json theme={null} {"format_id": {"agent_url": "...", "id": "display_300x250"}} {"format_id": {"agent_url": "...", "id": "display_300x600"}} {"format_id": {"agent_url": "...", "id": "display_728x90"}} {"format_id": {"agent_url": "...", "id": "display_970x250"}} // ... 50+ more sizes ``` **50 のプレースメントサイズを持つパブリッシャー** → 作成、保守、文書化する 50 の個別フォーマット定義。 ## 解決策: パラメータ付きテンプレートフォーマット 単一のテンプレートフォーマット定義(`display_static`)が format\_id オブジェクトでディメンションフィールドを受け入れ、クリエイティブが正確なディメンションを指定できるようにします。 ## フォーマットタイプと format\_id **2 種類のフォーマット定義**があり、**3 種類の format\_id** を生成します: ### フォーマット定義 1. **具体フォーマット** - フォーマット定義に固定のディメンション * 明示的なディメンションを持つ `renders` 配列を持つ * 例: `display_300x250` は常に 300×250px を意味する * パラメータを受け入れられない 2. **テンプレートフォーマット** - format\_id でパラメータを受け入れる * 受け入れるパラメータを列挙する `accepts_parameters` 配列を持つ * 例: `display_static` は任意のディメンションになれる * パラメータありでもなしでも使える ### format\_id の種類 1. **具体 format\_id** - 具体フォーマットを参照 * 例: `{id: "display_300x250"}` * パラメータなし(受け入れない) 2. **テンプレート format\_id** - パラメータなしでテンプレートフォーマットを参照 * 例: `{id: "display_static"}` * 任意のディメンションを受け入れるためにプレースメントで使う 3. **パラメータ付き format\_id** - パラメータ付きのテンプレートフォーマット * 例: `{id: "display_static", width: 300, height: 250}` * 正確なディメンション(ピクセル)を指定するためにクリエイティブで使う ### テンプレートフォーマット定義 パラメータを受け入れるフォーマット定義: ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org" "id": "display_static" } "name": "Static Display Banner" "type": "display" "accepts_parameters": ["dimensions"] "renders": [ { "role": "primary" "parameters_from_format_id": true } ] "assets": [ { "item_type": "individual" "asset_id": "banner_image" "asset_type": "image" "required": true "requirements": { "parameters_from_format_id": true } } { "item_type": "individual" "asset_id": "clickthrough_url" "asset_type": "url" "required": true } ] } ``` **主なフィールド:** * `accepts_parameters: ["dimensions"]` - フォーマットが format\_id でディメンション(ピクセルの width/height)を受け入れる * `renders[].parameters_from_format_id: true` - レンダーパラメータが format\_id に由来する * `requirements.parameters_from_format_id: true` - アセットパラメータが format\_id と一致しなければならない ### パラメータ付き format\_id(クリエイティブマニフェスト) クリエイティブは、テンプレートフォーマットを使うために format\_id で正確なディメンションを指定します: ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org" "id": "display_static" "width": 300 "height": 250 } "assets": { "banner_image": { "asset_type": "image" "url": "https://cdn.example.com/banner-300x250.png" "width": 300 "height": 250 } "clickthrough_url": { "asset_type": "url" "url": "https://example.com/landing" } } } ``` **この format\_id はパラメータ付きです:** 同じディメンションは常に同じ format\_id オブジェクトを生成し、重複排除とキャッシュを可能にします。 ### プレースメントの制約 **重要**: セールスエージェントは、プレースメントで常にパラメータ付きの format\_id(特定のディメンション/デュレーション付き)を返さなければなりません(MUST)。パラメータなしのテンプレート format\_id は、`list_creative_formats()` のフォーマット定義でのみ使われます。 パブリッシャーは、サポートするすべてのバリアントを列挙して、サポートするディメンションを指定します: ```json theme={null} { "kind": "seller_inline", "placement_id": "homepage_banner", "name": "Homepage Banner", "mode": "targetable", "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 300, "height": 250 }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 728, "height": 90 } ] } ``` **検証:** クリエイティブの format\_id は、プレースメントのパラメータ付き format\_id の一つと正確に一致しなければなりません。 **プレースメントでパラメータ付きのみである理由:** * バイヤーはどのディメンションが正確にサポートされるかを知る必要がある * 何が受け入れられるかについて曖昧さがない * クリエイティブ提出時に明確な検証を可能にする * パラメータなしのテンプレート format\_id は `list_creative_formats()` を介したフォーマット発見のためだけのもの ## メリット ✅ **スケーラビリティ** - 1 つのテンプレートフォーマットが無制限のディメンションバリアントをサポート ✅ **予測可能** - 同じディメンション = 同じ format\_id(キャッシュ/重複排除を可能にする) ✅ **自己完結** - クリエイティブが format\_id を介してフォーマットを完全に指定する ✅ **ポータブル** - 300×250 のクリエイティブは 300×250 を受け入れる任意のプレースメントで機能する ✅ **パブリッシャーの制御** - プレースメントが正確なディメンション制約を指定する ✅ **型安全** - width/height はエンコードされた文字列ではなく数値 ✅ **後方互換** - 具体(非テンプレート)フォーマットは変更なしで機能する ## format\_id のフィールド ### ビジュアルフォーマット(ディスプレイ、DOOH、ネイティブ) **フィールド:** * `width`(integer、最小: 1) - ピクセル単位の幅 * `height`(integer、最小: 1) - ピクセル単位の高さ **例:** ```json theme={null} { "agent_url": "https://creative.adcontextprotocol.org" "id": "display_static" "width": 300 "height": 250 } ``` ### 時間ベースフォーマット(動画、音声) **フィールド:** * `duration_ms`(number、最小: 1) - ミリ秒単位のデュレーション **例:** ```json theme={null} { "agent_url": "https://creative.adcontextprotocol.org" "id": "video_hosted" "duration_ms": 30000 } ``` ### 組み合わせ(ディメンション付き動画) **フィールド:** * `width`、`height`(integer) - ピクセル単位の動画フレームディメンション * `duration_ms`(number) - ミリ秒単位の動画の長さ **例:** ```json theme={null} { "agent_url": "https://creative.adcontextprotocol.org" "id": "video_hosted" "width": 1920 "height": 1080 "duration_ms": 30000 } ``` ## フォーマット定義のパターン ### ディスプレイフォーマット(柔軟なディメンション) ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org" "id": "display_static" } "name": "Static Display Banner" "type": "display" "accepts_parameters": ["dimensions"] "renders": null "assets": [...] } ``` ### 動画フォーマット(柔軟なデュレーション) ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org" "id": "video_hosted" } "name": "Hosted Video" "type": "video" "accepts_parameters": ["duration"] "renders": null "assets": [...] } ``` ### DOOH フォーマット(ピクセルディメンション) ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org" "id": "dooh_static" } "name": "DOOH Static Display" "type": "dooh" "accepts_parameters": ["dimensions"] "renders": null "assets": [...] } ``` **ピクセルディメンション付きのクリエイティブ:** ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org" "id": "dooh_static" "width": 1920 "height": 560 } "assets": {...} } ``` **注**: すべてのディメンションはピクセル単位です。物理的なスクリーンディメンション(例: 48 フィート × 14 フィートのビルボード)は、フォーマット仕様ではなくプレースメントメタデータです。 ### 出力フォーマットを持つ生成系フォーマット 生成系フォーマットは、生成できる出力フォーマットを指定します: **オプション 1: 特定のディメンションを生成** ```json theme={null} { "format_id": {"agent_url": "...", "id": "display_generative"} "output_format_ids": [ {"agent_url": "...", "id": "display_static", "width": 300, "height": 250} {"agent_url": "...", "id": "display_static", "width": 728, "height": 90} ] } ``` **オプション 2: 任意のディメンションを生成(テンプレート出力)** ```json theme={null} { "format_id": {"agent_url": "...", "id": "display_generative"} "output_format_ids": [ {"agent_url": "...", "id": "display_static"} ] } ``` 生成ロジックが任意のディメンションを扱える場合はテンプレート出力を使います。バイヤーが生成系フォーマットを呼ぶときにディメンションを指定します。 ## 発見パターン ### フォーマット定義: `list_creative_formats()` クリエイティブエージェントとセールスエージェントの両方が、`list_creative_formats()` を介してテンプレートフォーマット定義を返せます: ```json theme={null} { "formats": [ { "format_id": {"agent_url": "...", "id": "display_static"} "accepts_parameters": ["dimensions"] "assets": [...] } ] } ``` **目的**: バイヤーはどのフォーマット*タイプ*が利用可能か、どのパラメータを受け入れるかを発見します。 ### プレースメントの制約: `get_products()` **要件**: セールスエージェントは、プレースメントでパラメータ付きの format\_id(特定のディメンション/デュレーション付き)を返さなければなりません(MUST)。パラメータなしのテンプレート format\_id は、プレースメント仕様では許可されません。 ```json theme={null} { "products": [{ "placements": [{ "kind": "seller_inline", "placement_id": "display_multi_size", "name": "Display multi-size", "mode": "targetable", "format_ids": [ {"agent_url": "...", "id": "display_static", "width": 300, "height": 250}, {"agent_url": "...", "id": "display_static", "width": 728, "height": 90} ] }] }] } ``` **目的**: バイヤーは各プレースメントでどの*特定のディメンション*がサポートされるかを発見します。 **プレースメントでパラメータ付き format\_id が必要な理由:** * 受け入れられるディメンションバリアントの明示的なリストを提供する * 何が受け入れられるかについての曖昧さを排除する * クリエイティブ提出時に明確な検証を可能にする * バイヤーが自分のクリエイティブのディメンションを特定のプレースメント要件に照合できる **発見フロー**: 1. バイヤーがクリエイティブまたはセールスエージェントで `list_creative_formats()` を呼ぶ → `display_static` がディメンションを受け入れるテンプレートフォーマットだと知る 2. バイヤーがセールスエージェントで `get_products()` を呼ぶ → どの*特定のディメンション*がサポートされるか(300×250、728×90)を知る 3. バイヤーが、プレースメントのサポートするディメンションの一つに一致するパラメータ付き format\_id でクリエイティブを作成する ## 実装ガイドライン ### クリエイティブエージェント向け **フォーマット定義:** * 柔軟なディメンションを持つフォーマットには `accepts_parameters: ["dimensions"]` を設定 * 柔軟なデュレーションを持つフォーマットには `accepts_parameters: ["duration"]` を設定 * 両方を持つフォーマット(例: ディメンション付き動画)には `accepts_parameters: ["dimensions", "duration"]` を設定 * フォーマットがディメンションを受け入れる場合は `renders` 配列を省略(ディメンションは format\_id に由来) * 固定ディメンションの具体フォーマットには `renders` 配列を含める **検証:** * format\_id のディメンションをアセットのディメンションに対して検証 * width/height/unit がそろって存在すること(部分的でない)を保証 * format\_id がアセットに一致しない場合は明確なエラーを返す **フォーマットの検索/マッチング:** * `list_creative_formats()` はテンプレートフォーマット(ディメンションパラメータなし)を返す * ID によるフォーマット検索は、ディメンションパラメータを無視してベースフォーマット(agent\_url + id)で一致する * 例: `{id: "display_static", width: 300, height: 250}` のリクエストはテンプレートフォーマット `{id: "display_static"}` に一致する * ディメンションパラメータはフォーマット発見ではなくクリエイティブ検証に使われる ### セールスエージェント向け **プロダクトレスポンス - 重要な要件:** * プレースメントで常に特定のディメンション/デュレーション付きのパラメータ付き format\_id を返す**必要があります(MUST)** * プレースメントの `format_ids` 配列でパラメータなしのテンプレート format\_id を返しては**なりません(NEVER)** * サポートするすべてのディメンション/デュレーションのバリアントを明示的に列挙する: ```json theme={null} { "placements": [{ "kind": "seller_inline", "placement_id": "display_multi_size", "name": "Display multi-size", "mode": "targetable", "format_ids": [ {"agent_url": "...", "id": "display_static", "width": 300, "height": 250}, {"agent_url": "...", "id": "display_static", "width": 728, "height": 90}, {"agent_url": "...", "id": "display_static", "width": 160, "height": 600} ] }] } ``` **この要件が存在する理由:** * バイヤーはサポートするディメンションの明示的なリストを必要とする * 何が受け入れられるかについての曖昧さがない * クリエイティブ提出時の検証を可能にする * パラメータなしのテンプレート format\_id は `list_creative_formats()` のレスポンス専用 **クリエイティブの検証:** * クリエイティブの format\_id が少なくとも 1 つのプレースメント format\_id に正確に一致することを保証 * 一致には、すべてのフィールド(agent\_url、id、width、height、duration\_ms)の正確な等価が必要 * 部分一致や「十分近い」ディメンションはなし ### バイヤー向け **クリエイティブマニフェストの構築:** * `accepts_parameters` 配列を確認するためにフォーマット定義を取得 * テンプレートフォーマットを使うときは format\_id にディメンション/デュレーションフィールドを含める * アセットのディメンションが format\_id のディメンションに一致することを保証 * 同期前にプレースメント format\_id に対して検証 ## format\_id の等価性ルール 2 つの format\_id は、次の場合に限り**同一**です: * `agent_url` が正確に一致 * `id` が正確に一致 * `width` が正確に一致(存在する場合) * `height` が正確に一致(存在する場合) * `duration_ms` が正確に一致(存在する場合) ### 正規化 等価性またはキャッシュのために format\_id を比較するとき: **必須フィールド:** * `width` と `height` はそろって存在しなければならない(一方だけは指定できない) * すべてのディメンションはピクセル単位(整数) **数値精度:** * width と height は整数(300 であって 300.5 ではない) * デュレーションは小数になれる(端数秒には 30000.5ms) **フィールド順:** * 等価性に JSON のフィールド順は関係**しない** * `{"width": 300, "height": 250}` は `{"height": 250, "width": 300}` と等しい **等価の例:** ```json theme={null} // These are IDENTICAL format IDs {"agent_url": "...", "id": "display_static", "width": 300, "height": 250} {"agent_url": "...", "id": "display_static", "width": 300, "height": 250} // These are DIFFERENT format IDs {"agent_url": "...", "id": "display_static", "width": 300, "height": 250} {"agent_url": "...", "id": "display_static", "width": 728, "height": 90} // INVALID - partial dimensions not allowed (schema validation will reject) {"agent_url": "...", "id": "display_static", "width": 300} // ❌ Missing height ``` ## マッチングロジック ### プレースメント検証 **重要**: プレースメントは、常に明示的なディメンション/デュレーション付きのパラメータ付き format\_id を指定しなければなりません(MUST)。パラメータなしのテンプレート format\_id はプレースメントでは許可されません。 **プレースメント内のパラメータ付きフォーマット(必須のパターン):** ```json theme={null} // Placement specifies exact supported dimensions {"format_ids": [ {"agent_url": "...", "id": "display_static", "width": 300, "height": 250}, {"agent_url": "...", "id": "display_static", "width": 728, "height": 90} ]} // Creative matches only if exact equality with one of the placement's format_ids {"format_id": {"agent_url": "...", "id": "display_static", "width": 300, "height": 250}} // ✅ Match {"format_id": {"agent_url": "...", "id": "display_static", "width": 728, "height": 90}} // ✅ Match {"format_id": {"agent_url": "...", "id": "display_static", "width": 160, "height": 600}} // ❌ Not in placement list {"format_id": {"agent_url": "...", "id": "display_static"}} // ❌ Missing dimensions ``` ## 具体フォーマットからの移行 **移行前(フォーマット爆発):** ```json theme={null} // 50 separate format definitions { "format_id": {"agent_url": "...", "id": "display_300x250"} "renders": [{"dimensions": {"width": 300, "height": 250}}] } { "format_id": {"agent_url": "...", "id": "display_728x90"} "renders": [{"dimensions": {"width": 728, "height": 90}}] } // ... 48 more ``` **移行後(単一のテンプレートフォーマット):** ```json theme={null} { "format_id": {"agent_url": "...", "id": "display_static"} "accepts_parameters": ["dimensions"] "assets": [...] } ``` **クリエイティブマニフェストの変更:** ```json theme={null} // Before: Format ID encoded dimensions in string { "format_id": {"agent_url": "...", "id": "display_300x250"} "assets": {...} } // After: Dimensions as structured fields in format_id { "format_id": { "agent_url": "..." "id": "display_static" "width": 300 "height": 250 } "assets": {...} } ``` ## よくあるパターン ### IAB 標準ディスプレイサイズ IAB サイズに 15 の個別フォーマットを定義する代わりに、1 つのテンプレートを使います: ```json theme={null} { "format_id": {"agent_url": "...", "id": "display_static"} "accepts_parameters": ["dimensions"] } ``` クリエイティブはパラメータでサイズを指定します: * 300×250: `{id: "display_static", width: 300, height: 250}` * 728×90: `{id: "display_static", width: 728, height: 90}` * 160×600: `{id: "display_static", width: 160, height: 600}` * など ### 動画の尺バリエーション 個別の 15 秒、30 秒、60 秒のフォーマット定義の代わりに: ```json theme={null} { "format_id": {"agent_url": "...", "id": "video_hosted"} "accepts_parameters": ["duration"] } ``` クリエイティブはデュレーションを指定します: `{id: "video_hosted", duration_ms: 30000}` ### DOOH のスクリーンサイズ すべてのビルボードサイズにフォーマットを定義する代わりに: ```json theme={null} { "format_id": {"agent_url": "...", "id": "dooh_static"} "accepts_parameters": ["dimensions"] } ``` クリエイティブはピクセルディメンションを指定します: `{id: "dooh_static", width: 1920, height: 560}` **注**: すべてのディメンションはピクセル単位です。物理的なスクリーンサイズ(例: 48 フィート × 14 フィート)はプレースメントメタデータです。 ## 参考 * [クリエイティブマニフェスト](/docs/creative/creative-manifests) - 完全なマニフェストの構造 * [フォーマット発見](/docs/creative/formats) - バイヤーがフォーマットを発見する方法 * [プレースメントターゲティング](/docs/media-buy/creatives) - クリエイティブをプレースメントに割り当てる # Universal Macros Source: https://adcp-docs-ja.pier1.co.jp/docs/creative/universal-macros AdCP のユニバーサルマクロは、インプレッション時に置き換えられるプラットフォーム非依存のプレースホルダーで、動的なトラッキングデータをクリエイティブに挿入します。 ユニバーサルマクロにより、バイヤーは各パブリッシャーの広告サーバーの実装詳細を知らなくても、動的なトラッキングデータをクリエイティブに含められます。マクロは、インプレッション時に実際の値に置き換えられるプレースホルダーです。 ## 概要 AdCP にクリエイティブアセットを提供する際、次の場所にユニバーサルマクロのプレースホルダーを含められます: * インプレッショントラッキング URL * クリックトラッキング URL * VAST トラッキングイベント * ランディングページ URL **例**: ``` https://track.brand.com/imp? campaign={MEDIA_BUY_ID}& creative={CREATIVE_ID}& device={DEVICE_ID}& cb={CACHEBUSTER} ``` インプレッション時に、これは次のようになります: ``` https://track.brand.com/imp? campaign=mb_spring_2025& creative=cr_video_30s& device=ABC-123-DEF& cb=87654321 ``` ## フォーマット別に利用可能なマクロ クリエイティブフォーマットによってサポートするマクロが異なります。各フォーマットで利用可能なマクロを確認するには `list_creative_formats` を使います。 ### 共通マクロ(すべてのフォーマット) | マクロ | 説明 | 値の例 | | ---------------- | ---------------------- | ------------------- | | `{MEDIA_BUY_ID}` | あなたの AdCP メディアバイ識別子 | `mb_spring_2025` | | `{PACKAGE_ID}` | あなたの AdCP パッケージ識別子 | `pkg_ctv_prime` | | `{CREATIVE_ID}` | あなたの AdCP クリエイティブ識別子 | `cr_video_30s` | | `{CACHEBUSTER}` | キャッシュを防ぐための乱数 | `87654321` | | `{TIMESTAMP}` | ミリ秒単位の Unix タイムスタンプ | `1704067200000` | | `{CLICK_URL}` | パブリッシャーのクリックトラッキング URL | *(セールスエージェントが自動挿入)* | ### プライバシーとコンプライアンスマクロ **規制コンプライアンスに不可欠** - クリエイティブのロジックでユーザーのプライバシー選択を尊重するためにこれらを使います。 | マクロ | 説明 | 値の例 | | --------------------- | ----------------------------------- | ----------------------------------------------------- | | `{GDPR}` | GDPR 適用フラグ | `1`(適用)、`0`(非適用) | | `{GDPR_CONSENT}` | IAB TCF 2.0 同意文字列 | `CPc7TgPPc7TgPAGABC...` | | `{US_PRIVACY}` | US Privacy(CCPA)文字列 | `1YNN` | | `{GPP_STRING}` | Global Privacy Platform 同意文字列 | `DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA` | | `{GPP_SID}` | 適用セクションを示す GPP セクション ID | `7`、`7,8`(US National、US National + California) | | `{IP_ADDRESS}` | ユーザーの IP アドレス(プライバシーのためにしばしばマスクされる) | `203.0.113.42`、`""`(制限時) | | `{LIMIT_AD_TRACKING}` | Limit Ad Tracking が有効 | `1`(制限)、`0`(許可) | > **プライバシー警告**: `{IP_ADDRESS}` は GDPR や多くのプライバシー規制の下で個人データとみなされます。このマクロは、ユーザーのプライバシー設定、パブリッシャーのポリシー、地域の規制に応じて、空文字列またはマスク/切り詰めされた IP を返すことがあります。可能な限り、代わりに geo マクロ(`{COUNTRY}`、`{REGION}`、`{CITY}`)を使ってください。 **例 - プライバシーに配慮したトラッキング**: ```javascript theme={null} // In creative logic if (GDPR == 1 && GDPR_CONSENT == '') { // No consent - don't load tracking pixels } else { // Load tracking } ``` ### デバイスと環境マクロ | マクロ | 説明 | 値の例 | | ---------------- | --------------------- | ---------------------------------------- | | `{DEVICE_TYPE}` | デバイスカテゴリ | `mobile`、`tablet`、`desktop`、`ctv`、`dooh` | | `{OS}` | オペレーティングシステム | `iOS`、`Android`、`tvOS`、`Roku` | | `{OS_VERSION}` | OS バージョン | `17.2`、`14.0` | | `{DEVICE_MAKE}` | デバイスメーカー | `Apple`、`Samsung`、`Roku` | | `{DEVICE_MODEL}` | デバイスモデル | `iPhone15,2`、`Roku Ultra` | | `{USER_AGENT}` | 完全なユーザーエージェント文字列 | `Mozilla/5.0 ...` | | `{APP_BUNDLE}` | アプリバンドル ID(ドメインまたは数値) | `com.publisher.app`、`123456789` | | `{APP_NAME}` | 人間が読めるアプリ名 | `Publisher News App` | ### 地理情報マクロ | マクロ | 説明 | 値の例 | | ----------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------- | | `{COUNTRY}` | ISO 3166-1 alpha-2 国コード | `US`、`GB`、`CA`、`FR`、`JP`、`AU` | | `{REGION}` | 州/県/地域コード | `NY`、`CA`(米国の州)、`ON`(カナダ)、`IDF`(フランス)、`NSW`(オーストラリア) | | `{CITY}` | 都市名 | `New York`、`London`、`Tokyo`、`Sydney` | | `{ZIP}` | 郵便番号 | `10001`(米国)、`SW1A 1AA`(英国)、`75001`(フランス)、`100-0001`(日本) | | `{DMA}` | [Nielsen DMA コード](https://help.thetradedesk.com/s/article/Nielsen-DMA-Regions)(米国の TV マーケット) | `501`(New York)、`803`(Los Angeles) | | `{LAT}` | 緯度 | `40.7128`、`51.5074`、`35.6762` | | `{LONG}` | 経度 | `-74.0060`、`-0.1278`、`139.6503` | ### アイデンティティマクロ | マクロ | 説明 | 値の例 | | ------------------ | -------------------- | ----------------- | | `{DEVICE_ID}` | モバイル広告 ID(IDFA/AAID) | `ABC-123-DEF-456` | | `{DEVICE_ID_TYPE}` | デバイス ID の種類 | `idfa`、`aaid` | ### Web コンテキストマクロ Web ベースのインベントリ向け: | マクロ | 説明 | 値の例 | | ------------ | ------------------- | ----------------------- | | `{DOMAIN}` | 広告が表示されるドメイン | `nytimes.com` | | `{PAGE_URL}` | 完全なページ URL(エンコード済み) | `https%3A%2F%2F...` | | `{REFERRER}` | HTTP リファラー URL | `https://google.com` | | `{KEYWORDS}` | ページキーワード(カンマ区切り) | `business,finance,tech` | ### 掲出面とポジションのマクロ | マクロ | 説明 | 値の例 | | ----------------- | ----------------------- | ------------------------- | | `{PLACEMENT_ID}` | グローバルプレースメント ID(IAB 標準) | `12345678` | | `{FOLD_POSITION}` | フォールドに対する位置(ディスプレイ) | `above_fold`、`below_fold` | | `{AD_WIDTH}` | 広告スロットの幅 | `300`、`728` | | `{AD_HEIGHT}` | 広告スロットの高さ | `250`、`90` | ### 動画コンテンツマクロ コンテンツコンテキストを持つ動画フォーマット向け: | マクロ | 説明 | 値の例 | | ------------------ | ------------- | ---------------------------- | | `{VIDEO_ID}` | コンテンツ動画の識別子 | `vid_12345` | | `{VIDEO_TITLE}` | コンテンツ動画のタイトル | `Breaking News Story` | | `{VIDEO_DURATION}` | コンテンツの長さ(秒) | `600` | | `{VIDEO_CATEGORY}` | IAB コンテンツカテゴリ | `IAB1`(Arts & Entertainment) | | `{CONTENT_GENRE}` | コンテンツジャンル | `news`、`sports`、`comedy` | | `{CONTENT_RATING}` | コンテンツレーティング | `G`、`PG`、`TV-14` | | `{PLAYER_WIDTH}` | 動画プレーヤーの幅 | `1920` | | `{PLAYER_HEIGHT}` | 動画プレーヤーの高さ | `1080` | ### 動画広告ポッドマクロ コマーシャルブレイク内の動画広告向け: | マクロ | 説明 | 値の例 | | ---------------- | ------------ | ------------- | | `{POD_POSITION}` | 広告ブレイク内の位置 | `1`、`2`、`3` | | `{POD_SIZE}` | このブレイク内の総広告数 | `3` | | `{AD_BREAK_ID}` | 一意の広告ブレイク識別子 | `break_mid_1` | **注**: 動画フォーマットは、`[CACHEBUSTING]`、`[TIMESTAMP]`、`[DOMAIN]`、`[IFA]` などのすべての [IAB VAST 4.x マクロ](http://interactiveadvertisingbureau.github.io/vast/vast4macros/vast4-macros-latest.html)もサポートします。これらは VAST XML でネイティブに機能します。 ### 音声コンテンツマクロ コンテンツコンテキストを持つ音声フォーマット向け: | マクロ | 説明 | 値の例 | | ------------------- | ------------------ | --------------------------------- | | `{STATION_ID}` | ラジオ局またはポッドキャストの識別子 | `WXYZ-FM`、`pod_12345` | | `{COLLECTION_NAME}` | 番組またはコレクション名 | `Morning Drive`、`Tech Talk Daily` | | `{INSTALLMENT_ID}` | ポッドキャストエピソードの識別子 | `ep_2025_01_15` | | `{AUDIO_DURATION}` | コンテンツの長さ(秒) | `3600` | ### インプレッション識別 | マクロ | 説明 | 値の例 | | ----------------- | --------------------------------------------- | ------------------------------------------------------------------------------------- | | `{IMPRESSION_ID}` | インプレッションごとに上流で発行される一意の識別子(パブリッシャーまたは広告判断レイヤー) | `8c9e2f3a-7b1c-4d5e-9f6a-1a2b3c4d5e6f`(UUID)、`01ARZ3NDEKTSV4RRFFQ69G5FAV`(ULID)、または同等 | `{IMPRESSION_ID}` マクロは、一つのインプレッション機会に対する一意の識別子を運びます。これは汎用のインプレッションキーです——バイヤー、計測ベンダー、アトリビューションプロバイダー、検証サービスはいずれも、この種の識別子を使ってインプレッションイベントを重複排除し、ベンダー間でピクセルの発火を照合し、インプレッションをクリックに結合し、リトライを検出します。値を発行する上流レイヤー(下記の階層を参照)がそれをクリエイティブのトラッキング URL に代入し、インプレッションを識別する必要のある下流のコンシューマは同じ値を使います。 **一般的なユースケース:** * **インプレッションごとの重複排除。** 単一のインプレッションはしばしば多くのピクセル(インプレッション、ビューアビリティ、動画四分位、完了、サードパーティ検証)を発火します。それらすべてが一つの `{IMPRESSION_ID}` を共有することで、下流のコンシューマは時間や URL ベースのヒューリスティックなしに「これは同じインプレッションか?」を照合できます。 * **TMP クロスアイデンティティの重複排除。** TMP インプレッションが複数のユーザーアイデンティティに解決される場合、バイヤーのインプレッショントラッカーは各アイデンティティのログに同じ `{IMPRESSION_ID}` を書き込み、別個インプレッションのカウントが正しく重複排除されるようにします。[インプレッショントラッカーの実装](/docs/trusted-match/impression-tracker-implementation)を参照。 * **ベンダー間の照合。** アドサーバー、検証ベンダー、計測ベンダー間で配信を比較する広告主は、クロスウォークを構築するのではなく `{IMPRESSION_ID}` で結合します。 * **ピクセルリトライの重複排除。** 二度発火するピクセル(ネットワークリトライ、ページ更新)は同じ `{IMPRESSION_ID}` を運ぶので、id に対するサーバー側の重複排除パスが過剰カウントなしにリトライを捕捉します。 **含めるべきとき:** * **すべてのインプレッショントラッキング URL に推奨** — マクロは小さく安価で、上記のユースケースはすべて、それが一貫して存在するときに恩恵を受けます。 * **TMP コンテキストのみのインプレッションには必須** — アイデンティティマッチが適格性を返さなかった(または呼ばれなかった)場合で、ピクセルに `{TMPX}` がないとき、`{IMPRESSION_ID}` はバイヤーのインプレッショントラッカーで利用可能な唯一のクロスアイデンティティ重複排除キーです。 **形式は実装の選択です。** プロトコルは、セラーをまたぐ/時間をまたぐ衝突を避けるのに十分なエントロピーを持つ一意性を要求します。ワイヤー形式は固定しません。UUID(任意のバージョン)、ULID、snowflake スタイルの ID、その他の衝突耐性のある識別子スキームはすべて受け入れられます。バイヤーは、重複排除の目的のために値を不透明な文字列として扱わなければなりません(MUST)——パースなし、形式の仮定なし。 **値の三つの有効なソース、優先順位順:** 1. **パブリッシャー側の発行**(最高優先度): パブリッシャー自身のファーストパーティコードが、インプレッション機会ごとに新鮮な識別子を発行し——例: 広告リクエストが判断レイヤーに到達する前にサーバー側で——それを前方に渡して、判断レイヤーが `{IMPRESSION_ID}` を介して代入できるようにします。コンテキストのみとアイデンティティを持つインプレッションの両方で機能します。 2. **判断レイヤーの発行**(パブリッシャーが発行しなかった場合に使用): 広告判断レイヤー(Prebid TMP モジュール、アドサーバー、SSP、または同等のクライアント)が、コンテキスト↔アイデンティティの結合点で識別子を発行し、`{IMPRESSION_ID}` を介して代入します。これもコンテキストのみとアイデンティティを持つインプレッションの両方で機能します。 3. **TMPX デコード時のバイヤー側の発行**(フォールバック、アイデンティティを持つもののみ): 上記のどちらのレイヤーも発行しなかった場合、バイヤーのインプレッショントラッカーが [`インプレッショントラッカーの実装`](/docs/trusted-match/impression-tracker-implementation) に従って TMPX デコード時にローカルで id を発行します。これは `{TMPX}` が存在するときにのみ機能します——コンテキストのみのインプレッションはカバーできません。 各レイヤーは、上流のレイヤーがすでに生成した値に従わなければなりません(MUST)——上位のレイヤーがすでに供給しているのに下位のレイヤーで新鮮な id を発行すると、同じインプレッションのログが二つの id にまたがって分割されてしまいます。実際には、これは次を意味します: パブリッシャーが発行した場合、判断レイヤーはそれをそのまま通し、どちらかが発行した場合、バイヤーはデコード時に発行するのではなくピクセルの値を使います。 **`{CACHEBUSTER}` との関係。** 両者は互換ではありません。`{CACHEBUSTER}` は HTTP の中間者を打ち破るのに十分な低エントロピーのアンチキャッシュ値です。グローバルに一意なインプレッションキーではありません。両方が異なる目的で同じトラッキング URL に現れることがあります。 ### TMP 露出トラッキング | マクロ | 説明 | 値の例 | | -------- | -------------------- | ------------------------ | | `{TMPX}` | TMP 露出トークン(HPKE 暗号化) | `k1.dG1weC1leGFtcGxl...` | `{TMPX}` マクロは、[アイデンティティマッチ](/docs/trusted-match/specification)のレスポンスから暗号化された露出トークンを運びます。これは HPKE を介して暗号化されたユーザーの解決済みアイデンティティトークンを含み、バイヤーのインプレッションピクセルがリアルタイムのフリークエンシーキャップのためにユーザーごとの露出をログできるようにします。パブリッシャーは他のマクロとまったく同じように `{TMPX}` をトラッキング URL に代入します。トークンは不透明です——パブリッシャーはその値をパース、ログ、またはそれに基づいて判断してはなりません(MUST NOT)。 暗号化形式と鍵管理については [TMPX 露出トークン](/docs/trusted-match/specification#tmpx-exposure-tokens)を参照してください。 ### AXE 連携(レガシー) | マクロ | 説明 | 値の例 | | -------- | ---------------------------- | -------------------------- | | `{AXEM}` | AXE コンテキストメタデータ(エンコードされたブロブ) | `eyJjb250ZXh0IjoiLi4uIn0=` | `{AXEM}` マクロはレガシーの AXE 連携に由来します。[TMP](/docs/trusted-match) では、これは次で置き換えられます: * **構造化されたクリエイティブアセット**は、[オファー](/docs/trusted-match/specification#offer)の `creative_manifest` フィールドに移ります。 * **ユーザーごとの露出トラッキング**は、アイデンティティマッチの [`{TMPX}`](#tmp-露出トラッキング) マクロを使います。 ### カタログアイテムマクロ カタログ駆動のクリエイティブ(カルーセル、ダイナミックプロダクト広告、求人ボード、店舗ロケーター)向け。これらのマクロは、配信時にレンダリングされる特定のカタログアイテムの識別子に解決されます——[`content_id_type`](/docs/creative/catalogs#conversion-events) フィールドを介してコンバージョンイベントの `content_ids` で使われるのと同じ識別子です。 | マクロ | 説明 | 値の例 | | ------------------ | ------------------------ | --------------------------- | | `{CATALOG_ID}` | バイヤー定義のカタログ識別子 | `gmc-primary`、`job-feed` | | `{SKU}` | プロダクト SKU 識別子 | `SKU-12345` | | `{GTIN}` | Global Trade Item Number | `00013000006040` | | `{OFFERING_ID}` | AdCP オファリング識別子 | `summer-sale` | | `{JOB_ID}` | 求人掲載の識別子 | `vacancy-amsterdam-chef-42` | | `{HOTEL_ID}` | ホテル物件の識別子 | `grand-amsterdam` | | `{FLIGHT_ID}` | フライトルートの識別子 | `AMS-BCN-2025-06` | | `{VEHICLE_ID}` | 車両リスティングの識別子 | `VIN-1234` | | `{LISTING_ID}` | 不動産リスティングの識別子 | `prop-amsterdam-01` | | `{STORE_ID}` | 店舗ロケーションの識別子 | `amsterdam-flagship` | | `{PROGRAM_ID}` | 教育プログラムの識別子 | `mba-2025` | | `{DESTINATION_ID}` | 旅行目的地の識別子 | `barcelona` | カタログの `content_id_type` に一致するマクロを使います。例えば、`content_id_type: "gtin"` のプロダクトカタログはトラッカー URL で `{GTIN}` を使い、求人カタログは `{JOB_ID}` を使います。 #### カタログコンテンツマクロ 上記のマクロはカタログアイテムの**識別子**に解決されます。カタログ**コンテンツ**マクロは、カタログ駆動のクリエイティブテンプレート(`sponsored_placement` / DPA——Meta DPA、Snap Collection、TikTok Shopping)向けに、カタログアイテムのスカラー**フィールド値**に解決されます。それらは上記の ID マクロの「値を代入する」アナログです: 同じシングルブレースのファミリ、下記の同じ代入安全性のルール、ただトークンが多いだけです。 | マクロ | 説明 | `catalog_field` | 値の例 | | ----------------------- | -------------- | ---------------- | --------------------------------- | | `{ITEM_NAME}` | アイテム名/タイトル | `name` | `Summer Sale` | | `{ITEM_DESCRIPTION}` | アイテムの説明 | `description` | `Up to 50% off summer collection` | | `{ITEM_TAGLINE}` | プロモーションのタグライン | `tagline` | `Start measuring today` | | `{ITEM_PRICE}` | 価格の金額 | `price.amount` | `29.99` | | `{ITEM_PRICE_CURRENCY}` | ISO 4217 通貨コード | `price.currency` | `USD` | 各トークンは、[`field_bindings`](/docs/creative/catalogs#field-bindings) が使う既存の `catalog_field` ドット記法の語彙を介して、文書化されたカタログアイテムのフィールドに 1:1 でマップされます——コンテンツマクロは並行するフィールド語彙を導入し**ません**。五つのトークンは意図的に小さくバーティカル横断的です。バーティカル固有の深いフィールド(例: `star_rating`、`salary.min`)はユニバーサルコンテンツマクロではなく `field_bindings` のスカラーが提供します。URL 値と画像値のフィールド(例: `landing_url`、画像プール)は `field_bindings` を介して `url`/アセットスロットにバインドされ、コンテンツマクロでは**ありません**——URL 全体を `href` 全体として代入すると、下記の `encodeURIComponent` 相当のパーセントエンコーディング契約も壊してしまいます。 **シングルブレース `{MACRO}` のみ。** `{{ダブルブレース}}` は AdCP のコンテンツマクロ構文では**なく**、使ってはなりません(MUST NOT)。ダブルブレースは、セールスエージェントが中和/パーセントエンコードしなければならない下流のアドサーバーマクロ構文(`%%...%%`、`${...}`、`[...]`、`{{...}}`)の一つとして予約されています(下記のネスト展開ルールを参照)。それを AdCP のコンテンツマクロに採用すると、その保証を緩め、`{{...}}` をネイティブに解釈する下流のアドサーバーと衝突します。 どのカタログアイテムがレンダリングされるかは、[`sponsored_placement`](/docs/creative/canonical-formats) フォーマットの `fanout_mode` 列挙(`single_item` / `per_item` / `multi_item_in_creative`)を介して**セラーが宣言**します——バイヤー側の選択フィールドはありません。ML 最適化された DPA サーフェス(Meta Advantage+、TikTok Shopping)では、プラットフォームがバイヤーの作成したオーバーレイテキストをしばしば上書きするため、コンテンツマクロはセラーが尊重してもよい(MAY)バイヤー宣言の**ヒント**であって、保証された代入ではありません。 #### 代入安全性(カタログアイテムマクロ) カタログアイテムマクロは、値が**バイヤー制御のデータ**(カタログフィード)に由来し、インプレッション時に**パブリッシャー制御のコンテキスト**(インプレッショントラッカー URL、クリックトラッカー URL、VAST トラッキングイベント URL、そしてランディング/クリックスルー URL——上記の [URL 代入対象](#概要)の完全な集合)へ展開される、唯一のマクロクラスです。そのフローは攻撃に隣接しています: `&`、`#`、`?`、CR/LF、はぐれた URL フラグメント、または Unicode の bidi オーバーライドを含むカタログ値は、生で代入されると URL コンテキストから抜け出し、CRLF を介して Host ヘッダーを注入し、または監査ログのレンダリングを偽装する可能性があります。 以下のルールは、上記のすべてのカタログアイテムマクロに適用されます——ID マクロ(`{CATALOG_ID}`、`{SKU}`、`{GTIN}`、`{OFFERING_ID}`、`{JOB_ID}`、`{HOTEL_ID}`、`{FLIGHT_ID}`、`{VEHICLE_ID}`、`{LISTING_ID}`、`{STORE_ID}`、`{PROGRAM_ID}`、`{DESTINATION_ID}`)とコンテンツマクロ(`{ITEM_NAME}`、`{ITEM_DESCRIPTION}`、`{ITEM_TAGLINE}`、`{ITEM_PRICE}`、`{ITEM_PRICE_CURRENCY}`)の両方: * **エンコード前に Unicode NFC に正規化する。** パーセントエンコーディングの前に、すでに Unicode 正規化形式 C(NFC)でないカタログアイテムの値は、Unicode Standard Annex #15 に従って NFC に正規化しなければなりません(MUST)。セラーとバイヤーは `sync_catalogs` の取り込み時に任意の正規化形式でカタログ値を送ってもよい(MAY)(カタログは供給されたまま保存されます)。NFC への正規化は、カタログ取り込みの要件ではなく、パーセントエンコーディングの直前の代入パイプラインのステップです。このステップがないと、下記の unreserved ホワイトリストのルールを両方とも満たす二つの実装が、同じ視覚的な文字列に対して異なるバイトを生成します——`café`(NFC: U+00E9)と `café`(NFD: U+0065 + 結合 U+0301)はそれぞれ `caf%C3%A9` と `e%CC%81` にエンコードされます。NFC はウェブプラットフォームの慣例(WHATWG URL、HTML5 DOM、W3C Character Model)に一致します。NFKC / NFKD は受け入れ可能な代替では**ありません**——その互換性フォールディングは、全角/半角のバリアントや、日本語/韓国語のリテーラーカタログに正当に現れる視覚的に区別される他のグリフを黙って変異させます。 * **RFC 3986 の `unreserved` 集合にないすべてのオクテットをパーセントエンコードする。** セールスエージェントは、NFC 正規化されたカタログアイテムの値を、URL コンテキスト(クエリ文字列、パスセグメント、またはフラグメント)に代入する前に、RFC 3986 の `unreserved` 文字(`ALPHA / DIGIT / "-" / "." / "_" / "~"`)のみがエスケープされずに残るようにパーセントエンコードしなければなりません(MUST)。非 ASCII オクテットは、RFC 3986 §2.5 に従って UTF-8 エンコード後にパーセントエンコードしなければなりません(MUST)。これは `encodeURIComponent` 相当の契約です: 予約文字(`: / ? # [ ] @ ! $ & ' ( ) * + , ; =`)は期待どおりにエスケープされますが、CR(`%0D`)、LF(`%0A`)、スペース(`%20`)、C0/C1 制御文字、Unicode の bidi オーバーライドもエスケープされます——より広い列挙が、予約文字のみのルールでは開いたままになる CRLF 注入と bidi 偽装のベクターを閉じます。エンコーディングは代入時にちょうど一度適用されます。URL を逐語的に発火する下流の VAST プレーヤーとアドサーバーは期待される契約です——それらは発火前に再デコードせず、してはなりません(MUST NOT)。 * **ネストしたマクロ展開は禁止。** それ自体が AdCP の `{MACRO_NAME}` 構文に一致するテキストを含むカタログアイテムの値は、再展開してはなりません(MUST NOT)。セールスエージェントは AdCP のマクロ代入を一度のパスで行います: ソースのプレースホルダーがリテラル値に置き換えられ、それらのリテラル値は再スキャンされません。`vacancy-{DEVICE_ID}-42` という `{JOB_ID}` の値は、放出された URL では二巡目の展開ではなく、リテラル文字列 `vacancy-%7BDEVICE_ID%7D-42`(ブレースのパーセントエンコード後)を生成します。このルールは AdCP の `{...}` 構文のみを拘束します。下流のアドサーバーマクロ構文(`%%...%%`、`${...}`、`[...]`、`{{...}}`)を含むカタログアイテムの値は、それらを解釈するアドサーバーを対象とするときに中和するのは引き続きセールスエージェントの責任です——上記のルールに従うパーセントエンコーディングは、`%`、`$`、`[`、`]`、`{` がすべて `unreserved` 集合の外に着地するため、通常は十分です。 * **スコープは URL コンテキストのみ。** これらのルールは、カタログアイテムマクロが URL コンテキストに代入されるときに適用されます。カタログアイテムマクロが HTML 属性コンテキスト(例: サーバー側でレンダリングされるバナーテンプレートの `href` または `data-*` 属性)に代入されるとき、この節に従うパーセントエンコーディングはそれ自体では属性コンテキストの抜け出しを防ぎません。レンダラーは追加で HTML 属性エスケープを適用しなければなりません(MUST)——値は URL パーサーと HTML 属性パーサーの両方を生き延びなければならないため、二つのエンコーディングは代替ではなく積層されます。AdCP の規範的な契約は URL コンテキストのケースに限定されます。パブリッシャー側の HTML 属性の扱いはこの仕様のスコープ外です。 非カタログマクロ(`{MEDIA_BUY_ID}`、`{PACKAGE_ID}`、`{CREATIVE_ID}`、`{GEO}`、`{COUNTRY}`、`{DEVICE_TYPE}` など)は、バイヤー供給のフィードデータではなく、パブリッシャーまたはアドサーバーが仲介する状態から埋められます。それらのエンコーディング契約は、慣例によってパーセントエンコードするアドサーバー統合(OpenRTB、VAST)が管理します。このクラスの一部のマクロは攻撃者が偽装可能な入力に由来します(`{USER_AGENT}`、`{REFERRER}`、`{PAGE_URL}`、`{DOMAIN}`、`{APP_BUNDLE}` はリクエストヘッダーやページメタデータに由来)。OpenRTB / アドサーバーのエンコーディング慣例が今日の制御です。この仕様の規範的な MUST は、意図的にバイヤー制御のカタログアイテムクラスにスコープします——ユニバーサルな正規化ルールよりも狭く、検証可能な契約です。 **適合性フィクスチャ。** エンコーディングの動作を固定する参照テストベクター——予約文字の抜け出し、ネスト展開のリテラル保持、CRLF 注入、非 ASCII——は [`static/compliance/source/test-vectors/catalog-macro-substitution.json`](https://github.com/adcontextprotocol/adcp/blob/main/static/compliance/source/test-vectors/catalog-macro-substitution.json) でバージョン管理されています。セールスエージェントは、出荷前に自身の代入コードをこれらのベクターに対して検証すべきです(SHOULD)。 ### クリエイティブバリアントマクロ | マクロ | 説明 | 値の例 | | ----------------------- | ------------------------- | ----------------------- | | `{CREATIVE_VARIANT_ID}` | セラーが割り当てたクリエイティブバリアントの識別子 | `variant_a`、`v2_mobile` | > **注**: パブリッシャー固有のカスタムマクロは、個々のクリエイティブフォーマット仕様で `extra supported macros` として定義される場合があります。 ## 使用例 ### トラッキング付き動画クリエイティブ ```json theme={null} { "creative_id": "cr_video_30s", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_vast" }, "assets": { "vast_xml": { "asset_type": "vast", "delivery_type": "inline", "content": "\n\n \n \n \n \n \n \n 00:00:30\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n", "vast_version": "4.2" } } } ``` **主なポイント**: * AdCP マクロ(`{MEDIA_BUY_ID}`)を VAST マクロ(`[CACHEBUSTING]`)と混在させる * AdCP マクロは `{CURLY_BRACES}` を使う * VAST マクロは `[SQUARE_BRACKETS]` を使う * 両者はシームレスに併用できる ### トラッキング付きディスプレイクリエイティブ ```json theme={null} { "creative_id": "cr_banner_300x250", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_banner_300x250" }, "assets": { "banner_image": { "url": "https://cdn.brand.com/banners/spring_300x250.jpg", "width": 300, "height": 250 }, "impression_pixel": { "url": "https://track.brand.com/imp?buy={MEDIA_BUY_ID}&pkg={PACKAGE_ID}&cre={CREATIVE_ID}&device={DEVICE_ID}&domain={DOMAIN}&cb={CACHEBUSTER}" }, "landing_url": { "url": "https://brand.com/spring?campaign={MEDIA_BUY_ID}" } } } ``` ### トラッキング付き音声クリエイティブ ```json theme={null} { "creative_id": "cr_audio_30s", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "audio_streaming_30s" }, "assets": { "audio_file": { "url": "https://cdn.brand.com/audio/spring_30s.mp3", "duration_ms": 30000 }, "impression_tracker": { "url": "https://track.brand.com/imp?buy={MEDIA_BUY_ID}&pkg={PACKAGE_ID}&station={STATION_ID}&show={COLLECTION_NAME}&cb={CACHEBUSTER}" } } } ``` ### アイテムトラッキング付きカタログ駆動クリエイティブ ```json theme={null} { "creative_id": "cr_product_carousel", "format_id": { "agent_url": "https://creative.retailer.com/adcp", "id": "product_carousel" }, "catalogs": [{ "catalog_id": "gmc-primary", "type": "product", "content_id_type": "gtin", "tags": ["summer"] }], "assets": { "impression_pixel": { "url": "https://track.brand.com/imp?buy={MEDIA_BUY_ID}&catalog={CATALOG_ID}&item={GTIN}&cb={CACHEBUSTER}", "url_type": "tracker_pixel" }, "click_tracker": { "url": "https://track.brand.com/click?buy={MEDIA_BUY_ID}&catalog={CATALOG_ID}&item={GTIN}", "url_type": "tracker_pixel" } } } ``` **主なポイント**: `{GTIN}` は配信時に特定のプロダクトの GTIN に解決されます。5 つのプロダクトを表示するカルーセルでは、各プロダクトのインプレッション/クリックがそのプロダクトの識別子とともに発火します——アイテムごとのアトリビューションを可能にします。 ## インベントリタイプ別のマクロ利用可否 すべてのマクロがすべてのインベントリタイプで利用できるわけではありません。どのマクロがサポートされるかはフォーマット仕様を確認してください。 **重要**: 以下の列は、異なる環境(アプリ vs Web)で実行できるフォーマットタイプ(ディスプレイ、動画など)を表します。例えば: * モバイルアプリのディスプレイ広告には `DEVICE_ID` があります(✅\*)が、Web のディスプレイ広告にはありません * ✅\* の表記は「アプリ内コンテキストでのみ利用可能」を意味します * フォーマットタイプ + インベントリ環境が実際のマクロ利用可否を決定します | マクロカテゴリ | Display | Video | Audio | Native | CTV/OTT | DOOH | Mobile App | Mobile Web | Desktop Web | | --------------------- | ------- | ----- | ----- | ------ | ------- | ---- | ---------- | ---------- | ----------- | | **Common** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{MEDIA_BUY_ID}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{PACKAGE_ID}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{CREATIVE_ID}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{CACHEBUSTER}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{TIMESTAMP}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | **Privacy** | | | | | | | | | | | `{GDPR}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{GDPR_CONSENT}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{US_PRIVACY}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{GPP_STRING}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{GPP_SID}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{IP_ADDRESS}` | ✅‡ | ✅‡ | ✅‡ | ✅‡ | ✅‡ | ❌ | ✅‡ | ✅‡ | ✅‡ | | `{LIMIT_AD_TRACKING}` | ✅\* | ✅\* | ✅\* | ✅\* | ✅ | ❌ | ✅ | ❌ | ❌ | | **Identity** | | | | | | | | | | | `{DEVICE_ID}` | ✅\* | ✅\* | ✅\* | ✅\* | ✅ | ❌ | ✅ | ❌ | ❌ | | `{DEVICE_ID_TYPE}` | ✅\* | ✅\* | ✅\* | ✅\* | ✅ | ❌ | ✅ | ❌ | ❌ | | **Geographic** | | | | | | | | | | | `{COUNTRY}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{REGION}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{CITY}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{ZIP}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{DMA}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{LAT}/{LONG}` | ✅† | ❌ | ❌ | ✅† | ❌ | ✅ | ✅† | ❌ | ❌ | | **Device** | | | | | | | | | | | `{DEVICE_TYPE}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{OS}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{OS_VERSION}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{APP_BUNDLE}` | ✅\* | ✅\* | ✅\* | ✅\* | ✅ | ❌ | ✅ | ❌ | ❌ | | `{USER_AGENT}` | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | | **Web Context** | | | | | | | | | | | `{DOMAIN}` | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | | `{PAGE_URL}` | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | | `{REFERRER}` | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | | `{KEYWORDS}` | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | | **Placement** | | | | | | | | | | | `{PLACEMENT_ID}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `{FOLD_POSITION}` | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | | **Video Content** | | | | | | | | | | | `{VIDEO_ID}` | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | | `{VIDEO_CATEGORY}` | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | | `{CONTENT_GENRE}` | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | | **Video Ad Pods** | | | | | | | | | | | `{POD_POSITION}` | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | | `{POD_SIZE}` | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | | `{AD_BREAK_ID}` | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | | **Audio Content** | | | | | | | | | | | `{STATION_ID}` | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | `{COLLECTION_NAME}` | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | **TMP Exposure** | | | | | | | | | | | `{TMPX}` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅§ | ✅ | ✅ | ✅ | **凡例**: * ✅ = 利用可能 * ❌ = 利用不可 * ✅\* = アプリ内のみ(モバイル Web は不可) * ✅† = 位置情報の許可が付与されている場合 * ✅‡ = プライバシー規制のためしばしば制限される(空またはマスクされた値を返す場合がある) * ✅§ = DOOH はピクセル URL ではなく再生ログベースのレポートを使う **重要な注意**: * プライバシーマクロ(`{LIMIT_AD_TRACKING}`、`{DEVICE_ID}`)は、ユーザーのプライバシー設定に基づいて空の値を返す場合があります * 地理情報マクロの精度はパブリッシャーのデータ能力によって異なります * `{PLACEMENT_ID}` は IAB Global Placement ID 標準を指します ## マクロの仕組み ### 1. 発見 `list_creative_formats` を照会して、各フォーマットがどのマクロをサポートするかを確認します: ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_vast" }, "type": "video", "supported_macros": [ { "macro": "{MEDIA_BUY_ID}", "category": "identity", "description": "AdCP media buy identifier", "required": false, "privacy_sensitive": false, "example_value": "mb_spring_2025" }, { "macro": "{DEVICE_ID}", "category": "identity", "description": "Mobile advertising ID (IDFA/AAID)", "required": false, "privacy_sensitive": true, "example_value": "ABC-123-DEF-456" }, { "macro": "{GDPR}", "category": "privacy", "description": "GDPR applicability flag", "required": true, "privacy_sensitive": false, "example_value": "1" } ], "vast_macros_supported": true } ``` ### 2. クリエイティブにマクロを含める `{MACRO_NAME}` 構文を使って、トラッキング URL にマクロのプレースホルダーを追加します: ``` https://track.brand.com/imp?campaign={MEDIA_BUY_ID}&device={DEVICE_ID} ``` ### 3. セールスエージェントの処理 `create_media_buy` を介してメディアバイを作成すると、セールスエージェントは: 1. **AdCP ID マクロをあなたの実際の ID に置き換える**: * `{MEDIA_BUY_ID}` → `mb_spring_2025` * `{PACKAGE_ID}` → `pkg_ctv_prime` * `{CREATIVE_ID}` → `cr_video_30s` 2. **プラットフォームマクロをそのアドサーバーの構文に変換する**: * `{CACHEBUSTER}` → `%%CACHEBUSTER%%`(GAM)または `{{timestamp}}`(Kevel) * `{DEVICE_ID}` → `%%ADVERTISING_IDENTIFIER_PLAIN%%`(GAM) * `{DOMAIN}` → `%%SITE%%`(GAM) 3. **クリック可能な要素にクリックトラッカーを自動的に挿入する** 4. **VAST マクロは変更しないまま残す**(動画フォーマット向け) #### SDK による変換の実装 セールスエージェントは書き換えを手作業で組む必要はありません。`@adcp/sdk` パッケージは、マクロごとのマッピングをトラッキング URL のクエリパラメータの値に適用する `translateUniversalMacros` ヘルパーを提供します。各マクロは、**ネイティブ**のアドサーバートークン(生のまま残され、インプレッション時にアドサーバーが埋める)または、エージェントがすでに知っている具体的な**値**(今代入され、RFC 3986 に従ってパーセントエンコードされる)のいずれかにマップされます: ```typescript theme={null} import { translateUniversalMacros } from '@adcp/sdk'; const mapping = { // AdCP identifiers the agent knows at media-buy creation → concrete values '{MEDIA_BUY_ID}': { value: 'mb_spring_2025' }, '{PACKAGE_ID}': { value: 'pkg_ctv_prime' }, // Runtime macros → the ad server's native syntax (Google Ad Manager shown) '{CACHEBUSTER}': { native: '%%CACHEBUSTER%%' }, '{DEVICE_ID}': { native: '%%ADVERTISING_IDENTIFIER_PLAIN%%' }, }; const { url, dropped_params, unmapped_macros, dropped_consent_macros, suspect_native_values } = translateUniversalMacros( 'https://track.brand.com/imp?buy={MEDIA_BUY_ID}&pkg={PACKAGE_ID}&cb={CACHEBUSTER}&device={DEVICE_ID}', mapping, ); // url: // https://track.brand.com/imp?buy=mb_spring_2025&pkg=pkg_ctv_prime&cb=%%CACHEBUSTER%%&device=%%ADVERTISING_IDENTIFIER_PLAIN%% // dropped_params: [] (every macro was mapped) // unmapped_macros: [] // dropped_consent_macros: [] (no consent macro was dropped) // suspect_native_values: [] (no value entry looks like a native token) ``` 動作: * **`native` エントリは逐語的に挿入されます** — `%%CACHEBUSTER%%` はパーセントエンコードされないため、アドサーバーは依然としてそれを認識します。 * **`value` エントリは RFC 3986 のパーセントエンコードが施されます**——予約文字を含む値が URL を壊したり注入したりできません。 * **エージェントがサポートしないユニバーサルマクロを持つパラメータは、丸ごとドロップされます**(そのキーは `dropped_params` に、マクロは `unmapped_macros` に報告されます)——エージェントが埋められないトラッカーを壊れたまま放出するより、省略する方が良いのです。ドロップされた同意/プライバシーマクロ(`{GDPR_CONSENT}`、`{US_PRIVACY}` など)は **`dropped_consent_macros`** にも表面化されます——忘れられたマッピングが同意の劣化したピクセルを黙って出荷しないよう、**検査してください**。 * **すでに発行済みのパラメータはそのまま通過します** — `pkg_id=123456`(マクロなし)はそのままにされます。 * **`suspect_native_values`** は、ネイティブトークンのような形(`%%…%%`、`{{…}}`、`${…}`、`[UPPER_SNAKE]`)を持つ `value` エントリをフラグします——ほぼ常に `native` のアームを使うべきだったマッピングです。 * クエリパラメータの**値**のみが変換されます。キー位置のマクロはそのままにされます。 ### 4. インプレッション時 パブリッシャーのアドサーバーが残りのマクロを実際の値に置き換えます: ``` https://track.brand.com/imp? campaign=mb_spring_2025& device=ABC-123-DEF-456& cb=87654321 ``` ## ベストプラクティス ### マクロを一貫して使う すべてのクリエイティブにわたって同じコアマクロの集合を含めます: ``` ?buy={MEDIA_BUY_ID}&pkg={PACKAGE_ID}&cre={CREATIVE_ID}&cb={CACHEBUSTER} ``` これにより、トラッキングデータが一貫し、分析しやすくなります。 ### フォーマットのサポートを確認する どのマクロが利用可能かを確認するために、常に `list_creative_formats` を照会してください。すべてのフォーマットがすべてのマクロをサポートするわけではありません。 ### VAST と AdCP マクロを組み合わせる 動画では、両方のシステムを併用します: * **VAST マクロ** `[CACHEBUSTING]`、`[TIMESTAMP]` - 標準的な動画トラッキング向け * **AdCP マクロ** `{MEDIA_BUY_ID}`、`{DEVICE_ID}` - あなたのキャンペーントラッキング向け ### プライバシーコンプライアンス **重要**: クリエイティブのロジックで常にユーザーのプライバシー選択を尊重してください。 #### GDPR コンプライアンス(EU トラフィック) EU で配信されるキャンペーン向け: ```javascript theme={null} // Check consent before loading tracking if (GDPR == 1) { if (GDPR_CONSENT && GDPR_CONSENT != '') { // User has consented - load tracking pixels loadTracking(); } else { // No consent - skip tracking console.log('Tracking skipped - no GDPR consent'); } } else { // GDPR doesn't apply - load tracking loadTracking(); } ``` #### US Privacy / CCPA コンプライアンス 米国トラフィック向け: ```javascript theme={null} // Check US Privacy string if (US_PRIVACY == '1YYN') { // User has opted out - don't sell personal info skipPersonalizedTracking(); } else { // Load normal tracking loadTracking(); } ``` #### デバイスレベルのプライバシー Limit Ad Tracking 設定を尊重します: ```javascript theme={null} // Check if device ID is available if (LIMIT_AD_TRACKING == 1 || DEVICE_ID == '' || DEVICE_ID == '00000000-0000-0000-0000-000000000000') { // User has limited tracking - use contextual attribution useContextualTracking(); } else { // Device ID available useDeviceTracking(DEVICE_ID); } ``` #### プライバシーマクロの動作 **空の値**: プライバシー制限されたマクロは空文字列またはゼロを返します: * `{DEVICE_ID}` → LAT が有効なときは `""` または `00000000-0000-0000-0000-000000000000` * `{GDPR_CONSENT}` → 同意が提供されないときは `""` * `{IP_ADDRESS}` → プライバシー制限時は `""` またはマスク/切り詰めされた IP プライバシーに敏感なマクロを使う前に、**常に空の値をテスト**してください。 ### URL エンコーディング マクロのプレースホルダーを URL エンコードする必要はありません。アドサーバーが実際の値のエンコーディングを自動的に扱います。 **例**: ``` ❌ WRONG: https://track.com/imp?device=%7BDEVICE_ID%7D ✅ CORRECT: https://track.com/imp?device={DEVICE_ID} ``` アドサーバーは、マクロを置き換えるときに実際の値を URL エンコードします。 ### テンプレート構文 AdCP のマクロを持つ URL は、[RFC 6570 URI テンプレート(レベル 1)](https://datatracker.ietf.org/doc/html/rfc6570#section-1.2)として検証されます——単純な `{var}` 代入のみ。レベル 2〜4 の演算子(`{+SKU}`、`{#SKU}`、`{.SKU}`、`{/SKU}`、`{;SKU}`、`{?SKU}`、`{&SKU}`)は AdCP では**使われず**、マニフェストに現れてはなりません。アドサーバーは RFC 6570 の展開ではなく、リテラルな文字列置換を行います。 ## セールスエージェント向けの実装ノート *この節は AdCP の実装者向けであり、バイヤー向けではありません。* ### マクロ変換のアプローチ セールスエージェントは、ユニバーサルマクロを自身のアドサーバーのネイティブ構文に変換しなければなりません。推奨されるアプローチ: **オプション 1: トラフィッキング中にハードコード(MVP)** * アドサーバーのクリエイティブを作成するとき、AdCP ID マクロを実際の値に置き換える * プラットフォームマクロをアドサーバーの構文に変換する * ラインアイテムごとに 1 つのクリエイティブを作成するが、シンプルで信頼できる **オプション 2: ダイナミックラッパー(将来)** * 広告コールを傍受し、値を動的に注入する * より複雑だが、クリエイティブの重複を避ける ### 変換の例 **Google Ad Manager**: ```javascript theme={null} { '{CACHEBUSTER}': '%%CACHEBUSTER%%', '{DEVICE_ID}': '%%ADVERTISING_IDENTIFIER_PLAIN%%', '{DEVICE_ID_TYPE}': '%%ADVERTISING_IDENTIFIER_TYPE%%', '{DOMAIN}': '%%SITE%%', '{VIDEO_ID}': '%%VIDEO_ID%%' } ``` **Kevel**: ```javascript theme={null} { '{CACHEBUSTER}': '{{timestamp}}', '{DEVICE_ID}': '{{device.ifa}}', '{DEVICE_ID_TYPE}': '{{device.ifaType}}', '{DOMAIN}': '{{request.domain}}' } ``` **Xandr Monetize**: ```javascript theme={null} { '{CACHEBUSTER}': '${CACHEBUSTER}', '{DEVICE_ID}': '${DEVICE_APPLE_IDA}', // or ${DEVICE_AAID} '{DOMAIN}': '${DOMAIN}' } ``` ### クリックトラッカーの挿入 クリエイティブへのクリックは二つの別個のシグナルを生成し、AdCP はそれらを**別々のアセットスロット**としてモデル化します: | スロット | アセット | 役割 | OpenRTB Native の対応 | | ------------------ | ------------------------------- | ---------------------------------------------- | ---------------------- | | `landing_page_url` | `url` | ユーザーが遷移する単一の終端の宛先。 | `link.url` | | `click_tracker` | `pixel_tracker`(`event: click`) | クリック時にユーザーをそこへ遷移させ**ずに**発火する計測ホップ。任意の数だけ存在できる。 | `link.clicktrackers[]` | この分離は IAB OpenRTB Native から継承されています: クリックは**ちょうど一つの宛先**と**N 個の fire-and-forget のクリックトラッカー**を持ちます(`link.fallback` のディープリンクは、その一つの宛先の代替形式であり、二つ目ではありません)。配信時にセールスエージェントはすべての `click_tracker` をカウントビーコンとして発火し、ユーザーを一つの `landing_page_url` へ遷移させます。両方のスロットは[アセットタイプ](/docs/creative/asset-types)で定義されています。 **マクロの挿入。** セールスエージェントは、プラットフォームがクリックを記録してユーザーを転送するよう、宛先の前に自身のアドサーバーのクリックトラッキングマクロを挿入します。宛先のエンコーディングはアドサーバー固有です(GAM は追記スタイルの `%%CLICK_URL_UNESC%%` を使い、他のサーバーは自己完結型の `?...&rurl=` 形式を提供します): **元のクリエイティブ**: ```html theme={null}
Click here ``` **挿入後(GAM)**: ```html theme={null} Click here ``` **バイヤーのクリックスルーパラメータの保持。** クリックマクロの挿入は、バイヤー供給のクリックスルー URL にすでに存在するクエリパラメータ——エージェントが認識しないもの、例えばバイヤー側ベンダーのクリック識別子を含む——を剥ぎ取り、下流のアトリビューションを黙って壊す可能性があります。これらのパラメータを転送するエージェントは、バイヤーまたはサードパーティ供給のデータに由来する任意の値に、[代入安全性](#代入安全性カタログアイテムマクロ)がカタログアイテムの値に定義するエンコーディング規律(Unicode NFC 正規化、次に RFC 3986 の `unreserved` 集合へのパーセントエンコード)を適用するので、保持が URL コンテキストの抜け出し、CRLF 注入、bidi 偽装を再び開くことはありません。パラメータの保持を規範的な要件にするかどうかは、ワーキンググループのレビュー中のクリックトラッキング提案の一部です([#5693](https://github.com/adcontextprotocol/adcp/issues/5693))。 **バイヤーのクリック識別子をランディングに載せる。** `click_tracker` はカウントビーコンです: クリックを記録しますが、その識別子をユーザーのランディングセッションに置きません。ナビゲートされるのは `landing_page_url` だけだからです。バイヤーがクリック識別子をランディングに到達させる必要がある場合(クリックレベルのコンバージョンアトリビューションのため)、信頼できるリダイレクトなしのパターンは、その識別子をバイヤーが供給する `landing_page_url` に直接載せることです——リテラル値(`&buyer_click_id=abc123`)として、またはバイヤーが制御する AdCP マクロから構成して(`&buyer_click_id={CREATIVE_ID}`、配信時にセールスエージェントが展開)。`{buyer_click_id}` はそれ自体が AdCP マクロでは**ありません**——マクロ集合はクローズドなレジストリなので([利用可能なマクロ](#フォーマット別に利用可能なマクロ)を参照)、バイヤーは新しいトークンではなく値を供給します。識別子は一つのナビゲートされる URL に乗るため、どの当事者のリダイレクトも終端ホップである必要がありません。これは、バイヤーが配信前に供給または導出できる任意の識別子で機能し、複数当事者のリダイレクトチェーンを完全に避けます。 クリック時に(ベンダー自身のリダイレクト内で)のみ発行できる識別子は、カウントビーコン上で生き残れず、その識別子が着地するようユーザーをベンダーのリダイレクトを*通じて*ルーティングするには、AdCP が定義しないチェーン契約が必要です。「このトラッカーはナビゲーションパス内になければならない」というバイヤーが宣言可能なスロットセマンティクスは、ワーキンググループで議論中です([#5693](https://github.com/adcontextprotocol/adcp/issues/5693))。それが着地するまで、カウントビーコンがデフォルトであり、パス内の識別子の生存はスコープ外です。 ### マッピングの保存 照合のために、AdCP ID とアドサーバー ID の間のマッピングを保存します: ```javascript theme={null} { media_buy_id: "mb_spring_2025", ad_server_order_id: "1234567", packages: [ { package_id: "pkg_ctv_prime", ad_server_line_item_id: "8901234" } ] } ``` これを `create_media_buy` のレスポンスで返し、照合のために照会可能にします。 ## 関連ドキュメント * [クリエイティブフォーマット](/docs/creative/formats) - フォーマット仕様と発見の理解 * [クリエイティブプロトコル](/docs/creative) - AdCP でクリエイティブがどう機能するか # Annex III と Article 22 の義務 Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/annex-iii-obligations AdCP のポリシーフレームワークが、規制されたバーティカル(クレジット、保険、雇用、住宅)について GDPR Article 22 と EU AI Act Annex III の下でのデプロイヤー義務をどうサポートするか。 AdCP は広告キャンペーンを実行するために使われます。それらのキャンペーンの一部は、誰がクレジットカード、生命保険見積、求人リスティング、アパートの広告を見るかについて自動化された決定を行います。EU 法の下では、それらは通常のマーケティング決定ではありません — それらは **法的または同様に重要な効果を生む完全に自動化された決定**(GDPR Article 22)であり、AI システムによって行われるとき EU AI Act(Regulation (EU) 2024/1689)の **Annex III 高リスクカテゴリー** に含まれます。 このページは、AdCP が何を提供するか、何を提供しないか、デプロイヤーが何に責任を負うかを説明します。 ## 法が要求するもの **GDPR Art. 22(1)** は、法的効果を生むか自然人に同様に重大な影響を与える、プロファイリングを含む完全に自動化された処理のみに基づく決定を、狭い例外(明示的同意、契約、または EU/加盟国法)が適用されない限り禁止します。規制されたバーティカルの広告ターゲティング決定は日常的に Art. 22 に関与します: *SCHUFA* 判決(CJEU C-634/21、2023)は「同様に重大な効果」を広く拡張しました。 **EU AI Act Annex III** は AdCP と交差する高リスクユースケースをリストします: | Annex III reference | Vertical | | ------------------- | --------------------- | | §1(b) | 採用 / 選考(求人広告のターゲティング) | | §5(b) | 信用力の評価(クレジット / 融資広告) | | §5(c) | 生命・健康保険のリスク評価と価格設定 | 米国の並行するものは、クレジットの **Fair Housing Act**(HUD v. Facebook、2019 年和解)、**ECOA**、雇用の **EEOC / ADEA** です。AdCP は、Annex III が直接名指ししなくても、`fair_housing` カテゴリーの下で住宅割り当てを同等リスクとして扱います。 上記のいずれについても、デプロイヤーは以下をしなければなりません(MUST): 1. **人間の監督を保証**(AI Act Art. 14) — 資格ある人が決定をレビューする。 2. **自動ログを維持**(AI Act Art. 12) — 各決定のタイムスタンプ付き記録。 3. **透明性を提供**(AI Act Art. 13、GDPR Art. 13–14) — データ主体が何が起きているか理解できる。 4. **入力データを統制**(AI Act Art. 10) — ターゲティングに使われるシグナルは文書化され制限属性を認識する。 5. **異議申立権を尊重**(GDPR Art. 22(3)) — データ主体は人間の介入を要求し、意見を表明し、結果に異議を唱えられる。 決定の金銭的価値は無関係です。規制されたバーティカルでの €20 の自律ターゲティング決定は、€2M のものと同一に Art. 22 に関与します。 ## AdCP が提供するもの **AdCP はビルディングブロックであり、コンプライアンスの近道ではありません。** プロトコルは、デプロイヤーが Annex III 義務を果たせる構造化フィールドを公開します — それ自体は適合性評価、DPIA、人間監督ワークフロー、異議処理を実行しません。それらは AI Act と GDPR の下でデプロイヤーの責任のままです。以下のメカニズムは AdCP が提供する継ぎ目です。義務はあなたに残ります。 AdCP の役割は **Article 25 データ統制プロバイダー** です: プロトコルは、デプロイヤーが Annex III 義務を果たせる構造化フィールドを公開します。 | Obligation | AdCP mechanism | | ------------------------- | -------------------------------------------------------------------------------------------------------------------- | | 人間の監督(Art. 14、Art. 22(1)) | `plan.human_review_required` — ガバナンスエージェントがすべてのアクションを人間の承認のためエスカレート | | 入力データ統制(Art. 10) | `policy_categories`、`restricted_attributes`、`min_audience_size`、シグナル `restricted_attributes` 宣言 | | 自動ログ(Art. 12) | `get_plan_audit_logs` — `sync_plans`、`check_governance`、`report_plan_outcome` の不変記録 | | 透明性(Art. 13) | 決定に使われるポリシーテキストと事例を公開するポリシーレジストリエントリー | | 異議申立ディスカバリー(Art. 22(3)) | `brand.data_subject_contestation` — デプロイヤーの異議プロセスを指す **発見可能な連絡先**。AdCP はプロセスを運用しない。 | | ポリシー語彙 | `eu_ai_act_annex_iii` レジストリポリシー + `fair_housing`、`fair_lending`、`fair_employment`、`pharmaceutical_advertising` カテゴリー | ### 自動トリガー キャンペーンプランが以下のいずれかを宣言するとき、ガバナンスエージェントは `plan.human_review_required = true` を設定しなければなりません(MUST): * `policy_categories` が `fair_housing`、`fair_lending`、`fair_employment`、`pharmaceutical_advertising` を含む * 任意の解決されたポリシー(レジストリまたはカスタム)が `requires_human_review: true` を持つ * 解決されたレジストリポリシー `eu_ai_act_annex_iii` が管轄マッチング経由で適用される * `brand.industries` が規制されたセクター(consumer\_finance, banking, mortgage, life\_insurance, health\_insurance, recruitment, staffing, real\_estate, property\_management, housing)と交差する 任意の Annex III カテゴリーが解決するが `brand.data_subject_contestation` が欠けている場合、ガバナンスエージェントは重大な発見を発行しなければなりません(MUST) — Art 22(3) は連絡先なしに果たせません。 これはバイヤーではなくポリシーフレームワークによって強制されます。バイヤーはフラグを省略することで人間レビューをオプトアウトできません — 解決されたポリシーがそれを要求する場合、ガバナンスエージェントが設定します。以前 `human_review_required: true` を宣言したバイヤーは、明示的な `human_override` アーティファクト(理由 + 承認者)なしに再同期でそれをダウングレードできません。 ### `reallocation_threshold` 対 `human_review_required` これらは **異なる軸** で、しばしば混同されます: | Field | Scope | Covers | | ------------------------------- | ----- | -------------------------------- | | `budget.reallocation_threshold` | 運用 | セラー、チャネル、購入タイプ全体の予算再配分 | | `plan.human_review_required` | 規制 | 個人に影響する決定 — ターゲティング、クリエイティブ選択、配信 | プランは `reallocation_threshold` を `budget.total` に等しく設定でき(エージェントが予算を自由に再配分)**かつ** `human_review_required: true`(すべてのターゲティング決定が人間レビューを得る)にできます。これらは異なるものを統制します。 Annex III / Art. 22 の義務は `reallocation_threshold` ではなく `human_review_required` を通じて流れます。予算の自律性を制限することは Art. 22 に対処しません。再配分に摩擦を加えるだけです。 ## 異議申立エンドポイント AdCP は異議プロセスの **ディスカバリーメカニズム** を提供し、プロセス自体ではありません。Art. 22(3) はデータ主体に 3 つの実体的権利 — 人間の介入、意見の表明、結果への異議 — を与え、それらはデプロイヤーが運用しなければならないワークフロー権利です。`brand.data_subject_contestation` は下流エージェントとデータ主体にそのプロセスをどこで見つけるか伝えます。それの代替にはなりません。 `brand.data_subject_contestation` は **連絡先参照** — URL、メール、または両方 — を表示します。意図的に機械呼び出し可能な API ではありません。Art. 22(3) の権利は人間が行使するワークフロー権利です。デプロイヤーは連絡先の背後でワークフローを実行します。 ```json theme={null} { "data_subject_contestation": { "url": "https://acmecorp.com/privacy/contest", "email": "privacy@acmecorp.com", "languages": ["en", "de", "fr"] } } ``` Annex III の下で自動化された決定を公開する任意の AdCP エージェントは、このポインターを下流消費者に利用可能にすべきです(SHOULD)(例: 開示テキストやクリエイティブメタデータ)。デプロイヤーは適用される法的タイムライン内で監視し応答しなければなりません(MUST) — GDPR Art. 12(3) の下で 1 か月。 ## AdCP がしないこと * **AdCP は適合性評価を実行しません。** AI Act Art. 43 適合性評価はプロバイダーの義務です。 * **AdCP は人間監督ワークフローを実行しません。** `human_review_required: true` の設定はガバナンスエージェントがエスカレートすることを意味します — 人間レビュアーとレビューツールはプロトコルの外側です。 * **AdCP は異議プロセスを定義しません。** `data_subject_contestation` はデプロイヤーのプロセスを指します。 * **AdCP はキャンペーンがどのバーティカルに属するかを決定しません。** バイヤーが `policy_categories` を宣言します。ガバナンスエージェントは不一致をフラグできますが意図を推測できません。 ### 管轄スコーピングは機械的 AdCP は `plan.countries` をポリシー `jurisdictions` に対してマッチングしてポリシー適用性を解決します。これは有用な最初のパスですが縁では不完全です: * 米国ベースのデプロイヤーが米国オーディエンスのみをターゲットしても、クロスボーダーシグナル経由で EU 居住者に到達しうる。それは `plan.countries: ["US"]` が見逃す GDPR / AI Act 義務に関与します。 * EU 設立エンティティが非 EU キャンペーンを実行しても、設立ベースの管轄の下で AI Act の対象のままです(Regulation (EU) 2024/1689 の Art. 2)。 * 住宅 / 融資 / 雇用の規制は、プライバシー法とは別の独自の域外適用ドクトリンを持ちます。 デプロイヤーは、プランの国リストのみではなく、設立、ターゲットオーディエンスの位置、処理位置に基づいて適用される法を評価しなければなりません(MUST)。ガバナンスエージェントは規制されたバーティカルポリシーを保守的に適用すべきです(SHOULD): デプロイヤーの `brand.industries` またはキャンペーン `objectives` が Annex III バーティカルを示唆する場合、`plan.countries` がポリシーの宣言された管轄に一致するかどうかにかかわらずポリシーが発火します。 ## 関連項目 * [`eu_ai_act_annex_iii`](/docs/governance/policy-registry#seeded-policies) — シードされたレジストリポリシー * [ポリシーレジストリ](/docs/governance/policy-registry) — ポリシーとカテゴリーの `requires_human_review` * [キャンペーンガバナンス仕様](/docs/governance/campaign/specification) — `human_review_required` プランフィールド * [埋め込まれた人間の判断](/docs/governance/embedded-human-judgment) — 必須監督の背後にあるアーキテクチャ原則 # キャンペーンガバナンス Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/campaign/index AdCP キャンペーンガバナンスは、三者信頼を使って自律的なメディアバイを承認済みプラン、予算制限、コンプライアンスポリシーに対して検証します。 # キャンペーンガバナンスプロトコル 三者ガバナンス: バイヤーオーケストレーターがガバナンスエージェントにプランを送信し、ガバナンスエージェントが検証して承認を返し、オーケストレーターがセラーにバイを送信し、セラーがガバナンスエージェントで検証します。どの当事者も自分自身の採点はしません。 キャンペーンガバナンスは、バイサイドの広告トランザクションの自動検証を提供します。AI エージェントが自律的にメディアを購入するとき、キャンペーンガバナンスは独立したレビュー層として機能し、すべてのアクションを承認済みプラン、ブランドポリシー、予算制限、コンプライアンス要件に対して検証します。 ## 問題 AI エージェントがブリーフを解釈し、セラーを選び、メディアバイを交渉し、人間が見ていない中でライブにする — これが「ノーアイズ」問題だ: 自律エージェントが、購入が承認されたものと一致するかどうかの独立したチェックなしに実際の財務コミットメントを行います。 既存の AdCP ガバナンスドメインは、広告がどこに配信されるか(プロパティガバナンス)、どのコンテンツが隣接するか(コンテンツスタンダード)、どのクリエイティブが安全か(クリエイティブガバナンス)、ブランドが誰か(ブランドプロトコル)を解決します。しかしどれも**何が購入されてなぜか**を統治しません。 バイサイドにガバナンス層がないと: * エージェントが承認された予算を超えたり、承認されたパラメーター外に支出を再配分したりする可能性があります * エージェントがブリーフを誤解する可能性がある — ブリーフが「Mexico」と言っているのに「New Mexico」で購入します * ターゲティングがブランドポリシーに違反したり差別的なパターンを生み出したりする形でドリフトする可能性があります * セラーのレスポンスがリクエストされたものと異なっていても、自動検証がない * コンプライアンスポリシーが断片化されている — すべてのキャンペーンプランに自然言語文字列としてコピー&ペーストされており、標準ライブラリがなく、ポリシーを定義する者とキャンペーンを実行する者の分離もない キャンペーンガバナンスは3つのメカニズムでこのギャップを埋める: 何が許可されているかを定義する**プラン**(エージェントとは独立して)、バイヤーとは独立して購入を検証する**セラーサイドのガバナンスチェック**、コンプライアンスルールをガバナンスエージェント全体で標準化する**ポリシーレジストリ**。 ガバナンスは3つの動作モードを通じた段階的な採用をサポートする: **audit**(すべてをログに記録し、絶対にブロックしない)、**advisory**(事後レビューのために問題にフラグを立てる)、**enforce**(違反時にブロックします)。組織は、執行を有効にする前に信頼を築くために audit モードで開始できます。 ### 業界の先例 キャンペーンガバナンスは、今日のアドテックに手動プロセスとして存在するパターンを形式化する: | 手動プロセス | キャンペーンガバナンスの相当物 | | --------------------- | ------------------------ | | エージェンシートレーディングデスクの QA | IO に対する自動検証 | | DSP のプリビッドルール | 予算権限とターゲティングコンプライアンスチェック | | 広告主の承認ワークフロー | 高リスクアクションの人間エスカレーション | | ポストキャンペーン監査 | セラーの検証とデリバリー検証 | | コンプライアンスレビュー | 管轄ごとの規制チェック | ## 仕組み オーケストレーターは [`sync_plans`](/docs/governance/campaign/tasks/sync_plans) でキャンペーンプランをプッシュし、セラーに送信するすべてのアクションの前に `binding: "proposed"` で [`check_governance`](/docs/governance/campaign/tasks/check_governance) を呼び出す。セラーは実行前に `binding: "committed"` で `check_governance` を独立して呼び出す。セラーのレスポンスを受け取った後、オーケストレーターは [`report_plan_outcome`](/docs/governance/campaign/tasks/report_plan_outcome) を呼び出してループを閉じる。ガバナンスエージェントは試みられたアクションだけでなく、確認された結果から予算を追跡します。 ```mermaid theme={null} flowchart LR subgraph brand["ブランド組織"] policy["ポリシーチーム"] buying["購買チーム / エージェンシー"] orchestrator["オーケストレーター"] gov["認証 /
ガバナンスエージェント"] policy -->|"コンプライアンスを設定"| gov buying -->|"操作"| orchestrator orchestrator -->|"1. プランを同期"| gov orchestrator -->|"2. check_governance
(binding: proposed)"| gov orchestrator -->|"5. 結果を報告"| gov gov -->|"ステータス / 結果"| orchestrator end orchestrator -->|"3. create_media_buy
(plan_id)"| seller["セラーエージェント"] seller -->|"4. check_governance
(binding: committed)"| gov seller -->|"確認 + planned_delivery"| orchestrator gov -.->|"エスカレーション"| human["人間の承認者"] ``` キャンペーンガバナンスには3つの当事者が参加する: * **オーケストレーター**はアクションをセラーに送信する前にプランに対して検証する(バイヤーサイドのガバナンスループ)。 * **セラー**はバイヤーのガバナンスエージェントをプランデリバリーパラメーターで呼び出すことで、独立して購入をチェックする(セラーサイドのガバナンスチェック)。 * **ガバナンスエージェント**は同じキャンペーンプランに対して、オーケストレーターの意図されたアクションとセラーの計画されたデリバリーの両方を検証します。 これにより、バイヤーもセラーも一方的に何が承認されたかを誤って表現できない信頼モデルが生まれる。オーケストレーターはガバナンスをスキップできない(セラーが独立してチェックします)し、セラーは承認されたものと異なるものを配信できない(ガバナンスエージェントは計画されたデリバリーの記録を持っています)。 ## 職務分離 キャンペーンガバナンスは**ポリシーを定義する者**と**キャンペーンを実行する者**の間に自動化された分離を強制する: * **ポリシーチーム**は[ポリシーレジストリ](#ポリシー解決)から適用可能なコンプライアンスポリシーを選択し、ブランド固有のルールを設定し、ブランドのコンプライアンスプロファイルを維持します。この設定は個々のキャンペーンプランではなく、ブランドレベル(brand.json 内)に存在します。 * **購買チーム**(またはエージェンシー)はオーケストレーターを操作し、キャンペーンプランを作成し、メディアバイを実行します。プランはキャンペーンコンテキスト(予算、チャンネル、フライト日程、承認市場)を指定し、必要に応じて追加のレジストリポリシーやキャンペーン固有のルールを参照できます。 * **ガバナンスエージェント**はブランドのコンプライアンス設定から適用可能なポリシーを解決し、それらに対してすべてのオーケストレーターアクションを検証します。 オーケストレーターはブランドのコンプライアンスポリシーをバイパスまたは変更できない — それらはガバナンスエージェントによってブランド設定から解決されます。プランは `policy_ids` と `custom_policies` で追加ポリシーを重ねられるが、ブランドのベースラインは常に適用されます。規制が変更されると、ポリシーチームはブランド設定を一度更新し、すべてのアクティブなキャンペーンが自動的に変更を取り込む。 ## ポリシー解決 ガバナンスエージェントはプランのブランド参照を通じて適用可能なポリシーを解決する: 1. プランには `brand.domain`(必須)とオプションで `countries`/`regions`(このキャンペーンの承認市場)が含まれます 2. ガバナンスエージェントは[ブランドプロトコル](/docs/brand-protocol/index)でブランドを解決し、コンプライアンス設定を取得します 3. ブランド設定は ID で**ポリシーレジストリ**から標準化されたポリシーを参照し、ブランド固有のカスタムポリシーを含みます 4. ガバナンスエージェントはこれらのポリシーとプランの承認市場を交差させる — それらの市場に適用可能なポリシーのみがこのプランでアクティブになります 5. 解決されたポリシーセットが `check_governance` で評価されるもの 6. 承認市場外をターゲットにするメディアバイは、ポリシーコンプライアンスに関わらず拒否されます \*\*[ポリシーレジストリ](/docs/governance/policy-registry)\*\*はコミュニティが管理する標準化された機械可読な広告コンプライアンスポリシーのライブラリだ — 管轄(UK HFSS 制限、US COPPA、EU GDPR)、ポリシーカテゴリ(子供向け、医薬品、公正住宅、公正雇用)、ブランド安全ベースラインをカバーします。ブランドは独自のポリシーを書く代わりにレジストリから適用可能なポリシーを選択します。 ## ガバナンスループ すべてのセラーインタラクションは前後パターンに従う: 1. **前**: オーケストレーターは送信する予定のツールとペイロードで `binding: "proposed"` で `check_governance` を呼び出す。ガバナンスエージェントはステータスを返します。 2. **実行**: 承認された場合、オーケストレーターは `plan_id` と `governance_context` を含めてセラーにアクションを送信します。 3. **チェック**: アカウントに `governance_agents` がある場合、セラーは `binding: "committed"` とその `planned_delivery`(実際に実行するもの)で `check_governance` を呼び出す。ガバナンスエージェントは承認または拒否します。 4. **後**: オーケストレーターはセラーのレスポンスで `report_plan_outcome` を呼び出す。ガバナンスエージェントは状態を更新し、不一致にフラグを立てる。 このパターンは探索(`get_products`)、購入(`create_media_buy`、`update_media_buy`)、定期的なデリバリーレポーティングに適用されます。 ## 計画されたデリバリー セラーがメディアバイを確認するとき、実際に配信するものを説明する `planned_delivery` オブジェクトを返す — 使用するジオターゲティング、チャンネル、フライト日程、フリークエンシーキャップ、予算。これはバイヤーがリクエストしたものと異なる場合がある(例: セラーが追加のフリークエンシーキャップを適用したり、利用可能なインベントリに合わせてジオを調整したりする場合)。 `planned_delivery` は2つの目的を果たす: 1. **ガバナンスチェック**: セラーはバイヤーのガバナンスエージェントに `planned_delivery` を送信し、キャンペーンプランと一致することを確認します。これにより、セラーがバイヤーが承認しなかったものを配信することを防ぐ。 2. **不一致の検出**: バイヤーは `planned_delivery` を元のリクエストと比較し、`report_plan_outcome` で差異にフラグを立てることができ、デリバリーが始まる前に設定のドリフトをキャッチします。 ## セラーサイドのガバナンスチェック バイヤーサイドのガバナンスには信頼の制限がある: オーケストレーターが自身のコンプライアンスを証明します。LLM エージェントはガバナンスの承認を幻覚したり、検証をスキップしたり、検証されたものを誤って表現したりする可能性があります。お金を使っているエージェントが、そのお金を使うことが OK かどうかをチェックするエージェントでもあることは信頼できません。 セラーサイドのガバナンスチェックがこれを解決します。バイヤーはアカウントを同期するときに `governance_agents`(認証情報付きの URL)を登録します。セラーが `create_media_buy` を受け取ると、配信する予定のものでガバナンスエージェントを呼び出す。ガバナンスエージェントはプランに対してチェックし、`approved`、`denied`、または `conditions` を返します。 これは誤解もキャッチします。ブリーフが「Mexico」と言っていてセラーが「New Mexico」と解釈した場合、ガバナンスエージェントはプランに対するジオの不一致を見て、ライブになる前にバイを拒否します。 Webhook はメディアバイのライフサイクル全体をカバーする: * **購入**: `create_media_buy` を確認する前に POST — このバイは承認されているか? * **変更**: `update_media_buy` を確認する前に POST — この変更は OK か? * **デリバリー**: デリバリー中に定期的に POST — デリバリーはまだ軌道に乗っているか? ガバナンスエージェントがすべての状態を管理します。セラーは何が起きているかをポストするだけ — チェーン、会話履歴、サーバー間で追跡する状態はない。 ガバナンスチェックはすべてのフェーズでオプションです。セラーは購入のみで開始し(バイごとに1回の POST)、変更とデリバリーチェックを段階的に追加できます。完全なプロトコルは[仕様](/docs/governance/campaign/specification#governance-checks)を参照。 ガバナンスチェックを実装するセラーは競合優位を得る: 実行前に購入が独立して検証されたことをバイヤーに証明できます。これにより紛争リスクが減り、コンプライアンス検証が自動化され、厳格な監督要件を持つバイヤーに信頼を示します。 ## 採用パス キャンペーンガバナンスはバイヤーのポリシーチームがガバナンスエージェントで設定する3つの動作モードをサポートする: | モード | 動作 | 使用する場合 | | ---------- | ----------------------------------------------------------------------------- | --------------------------------------------- | | `audit` | すべてのチェックをログに記録し、絶対にブロックしません。添付された結果とともに常に `approved` を返します。 | 最初のロールアウト。執行を有効にする前にキャリブレーションの信頼を築く。 | | `advisory` | 実際のステータス(`denied`、`conditions`、`escalated`)を返すが、セラーはすべてのレスポンスを非ブロッキングとして扱います。 | 事後の人間レビュー。ガバナンスエージェントが意見を表明し、人間がそれに基づいて行動します。 | | `enforce` | `denied` または `escalated` でブロックします。進む前に解決を要求します。 | 本番ガバナンス。デフォルト。 | `audit` モードで開始してガバナンスがフラグを立てるものを確認します。`advisory` に移行してリアルキャンペーンで結果をテストします。信頼が確立されたら `enforce` に切り替える。これは直接購入する単一ブランドと、35のブランドと複数のエージェンシーパートナーを持つホールディングカンパニーの両方で同じように機能します。 ### 小規模ブランドの場合 直接購入するブランド(エージェンシーなし、ポリシーチームなし)でも以下が得られます: * キャンペーンプランからの自動予算制限とジオ執行 * [ポリシーレジストリ](/docs/governance/policy-registry)からのコンプライアンスカバレッジ — レジストリポリシーはコミュニティが管理しており、ブランドごとの設定は不要 * ガバナンスチェックを介したセラーサイドの検証 * `get_plan_audit_logs` による完全な監査証跡 ガードレールを定義するために `authority_level: "agent_limited"` と `reallocation_threshold` を設定します。ガバナンスエージェントが残りを処理します。 ### マルチエージェンシーとホールディングカンパニーの設定 プランは、プランに対してどのエージェントが実行できるかをスコープする[委任](/docs/governance/campaign/specification#delegations)をサポートする — 権限レベル、予算制限、市場、有効期限で。ブランドはヨーロッパ市場向けに1つのエージェンシーに `full` 権限を、北米向けに別のエージェンシーに `execute_only` 権限を委任できます。 複数ブランドを管理するホールディングカンパニーの場合、[ポートフォリオガバナンス](/docs/governance/campaign/specification#portfolio-governance)はクロスブランドの制約を定義します: 総ポートフォリオ支出上限、共有ポリシー執行、個々のブランドプランが上書きできない企業レベルの除外。 ### 信頼の発見 ガバナンスの結果には「これは確実に GDPR に違反する」(0.95)と「これはオーディエンスセグメントの解決方法によっては違反する可能性がある」(0.6)を区別するオプションの[信頼スコア](/docs/governance/campaign/specification#finding-confidence)(0〜1)が含まれます。これによりブランドが適切に対応できる — 高信頼の結果は自動解決でき、中信頼の結果は人間レビューのためにフラグが立てられます。 ### ドリフト検出 [監査ログ](/docs/governance/campaign/tasks/get_plan_audit_logs)にはトレンド指標を持つ集計メトリクス(エスカレーション率、自動承認率、人間オーバーライド率)が含まれます。エスカレーション率の低下は、システムが適切にキャリブレートされているか、監督が侵食されていることを意味するかもしれない。トレンドを表面化させることで、組織がその判断を下せる。 ## ライフサイクルフェーズ キャンペーンガバナンスは3つのフェーズにわたってステートフルだ: | フェーズ | タイミング | 検証される内容 | | --------- | ----------------------------------------- | --------------------------------------------- | | **探索** | `get_products` 前 | 検索意図がプランと一致、承認されたセラーからのプロダクト、価格が適切 | | **購入** | `create_media_buy` / `update_media_buy` 前 | 予算が制限内、ターゲティングが準拠、フライト日程が一致、クリエイティブアサインメントが適切 | | **デリバリー** | 定期レポーティング | ペーシングがパラメーター内、支出率が一貫、デリバリーメトリクスが軌道に乗っている | 各フェーズは以前のフェーズのコンテキストを基に構築されます。**購入**中、ガバナンスエージェントは**探索**中に発見(および承認)されたプロダクトを把握しています。**デリバリー**中は、購入されたものに対して実行を監視します。 ## 検証カテゴリ | カテゴリ | チェック内容 | | ----------------------- | ------------------------------------------------------------------------------------------------------------- | | `budget_authority` | 承認された制限内の支出、セラーごとの集中度、再配分の規模 | | `strategic_alignment` | チャンネルミックス、オーディエンスの一致、パブリッシャー品質ティア、ブリーフの一貫性 | | `bias_fairness` | 保護カテゴリのターゲティング、オーディエンス構成、不均衡な影響。プランの `policy_categories` で定義された `restricted_attributes` に対してオーディエンスセレクターを照合する | | `regulatory_compliance` | ブランドのコンプライアンス設定とプランの承認国/地域から解決された管轄固有の規制 | | `seller_verification` | 設定の正確さ、未開示の変更、デリバリーの妥当性 | | `brand_policy` | ブランド設定とポリシーレジストリから解決されたブランドレベルのコンプライアンスポリシー — 競合他社の分離、カテゴリ隣接、カスタムブランドルール | ガバナンスエージェントは `get_adcp_capabilities` でどのカテゴリを評価するかを宣言します。 ## ステータス すべてのガバナンスチェックは構造化されたステータスを返します: | ステータス | 意味 | 呼び出し元のアクション | | ------------ | ----------- | ---------------------------------- | | `approved` | すべてのチェックに合格 | 進める | | `denied` | ハードポリシーに違反 | 進まない; ユーザーに報告する | | `conditions` | 特定の変更で合格できる | 条件を適用し、`check_governance` を再呼び出しする | | `escalated` | 人間のレビューが必要 | 一時停止して人間の承認者に通知する | 結果の深刻度レベルは緊急性を示します: * **`info`** -- 監査のためにログに記録。承認レスポンスの助言的な結果に使用。 * **`warning`** -- 人間がレビューすべき。アクションが許可されているが注意が必要な場合の承認レスポンスに使用。 * **`critical`** -- アクションがブロックされます。人間の承認が必要な場合のエスカレートレスポンスに使用。 ステータスが `escalated` の場合、呼び出し元は深刻度に関わらず進んではなりません。非ブロッキングの懸念は `findings` が付いた `approved` ステータスを使用します。 人間のエスカレーションは既存の AdCP HITL メカニズムと統合します。エスカレートされたアクションはプロトコルエンベロープで `input-required` ステータスを返し、人間が標準の `context_id` 継続で解決するまでオーケストレーターは一時停止します。 ## 他のガバナンスドメインとの関係 キャンペーンガバナンスは既存のドメインと構成する — それらを重複させない: | ドメイン | 関係 | | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | **[プロパティガバナンス](/docs/governance/property/index)** | キャンペーンガバナンスはオーケストレーターがプロパティリストを正しく*使用*しているかを検証する(例: バイパスしていないか)。プロパティガバナンスがリストを提供します。 | | **[コンテンツスタンダード](/docs/governance/content-standards/index)** | キャンペーンガバナンスはコンテンツスタンダード参照がメディアバイに含まれていることを検証します。コンテンツスタンダードが実際のコンテンツ評価を処理します。 | | **[クリエイティブガバナンス](/docs/governance/creative/index)** | キャンペーンガバナンスはクリエイティブアサインメントがフォーマット要件と一致することを検証します。クリエイティブガバナンスがクリエイティブ自体をスキャンします。 | | **[ブランドプロトコル](/docs/brand-protocol/index)** | キャンペーンガバナンスはブランドプロトコルを通じてブランドのコンプライアンス設定を解決します。ブランドのポリシーチームが brand.json にコンプライアンスポリシーを設定し、ガバナンスエージェントが自動的に適用します。 | ## 次のステップ 三者信頼モデル、職務分離、構造的制御がエージェンティックな広告を安全にする仕組み。 データモデル、検証ロジック、ケイパビリティ宣言、オーケストレーター統合パターン。 タスクリファレンス: `sync_plans`、`check_governance`、`report_plan_outcome`、`get_plan_audit_logs`。 # 安全モデル Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/campaign/safety-model AdCP の三者信頼モデルは、いかなる単一の AI エージェントも一方的なメディアバイの意思決定を行えないようにします。オーケストレーター、ガバナンス、セラーが独立して検証します。 # エージェンティック広告が安全な理由 自律的な AI エージェントがメディアを購入することは、正当な疑問を生じさせる: ソフトウェアが代わりに実際の資金を使うことをどうやって信頼できるか? キャンペーンガバナンスはこの問いに構造的な制御で答える — いかなる単一エージェントも信頼するのではなく、いかなる単一当事者も一方的に行動することを不可能にすることで。 ## 三者信頼モデル キャンペーンガバナンスは3つの独立した当事者に検証を分散させる: ```mermaid theme={null} flowchart LR O[Orchestrator] -->|"1. intent check"| G[Governance Agent] G -->|"approved/denied"| O O -->|"2. create_media_buy"| S[Seller] S -->|"3. execution check"| G G -->|"approved/denied"| S S -->|"4. confirmed"| O O -->|"5. report outcome"| G ``` 1. **オーケストレーター**はいかなるセラーに送信する前にも、意図したアクションをプランに対してチェックする(意図チェック: `tool` + `payload`) 2. **セラー**は実行前に、同じプランに対して計画された配信を独立してチェックする(実行チェック: `governance_context` + `planned_delivery`) 3. **ガバナンスエージェント**は両サイドをキャンペーンプランに対して検証し、ライフサイクル全体にわたって状態を維持します いかなる当事者も自分の宿題を採点しません。セラーが独立してチェックするため、オーケストレーターはガバナンスをスキップできません。ガバナンスエージェントが計画された配信の記録を持つため、セラーは承認されたものと異なる配信ができません。 ## 検証可能な承認 すべてのガバナンス承認は、ガバナンスエージェントによって暗号的に署名されます。承認トークンは購入リクエストに同行します。セラーはコミット前に署名を確認し、**規制当局や監査人は、それを発行したガバナンスベンダーと協力することなく、同じトークンを独立して検証できます**。 その独立性こそが本質です。承認証跡を再構築する唯一の方法が、それを発行したベンダーに召喚状を出すことである設計は、監査証跡ではなく — ベンダー依存です。署名付きトークンは、監査証跡を、データ主体、規制当局、または相手方弁護士が自ら検証できるものに変えます。 署名付きトークンは、承認を特定のプラン、特定のセラー、メディアバイの特定のフェーズ、および一意のトランザクション識別子に結び付けます。セラー A の 50 万ドルのフライトを承認するトークンを、セラー B の 50 万ドルのフライトを認可するために黙って再利用することはできず、Q1 の承認を Q3 でリプレイすることもできません。 実装者向けのプロファイル(クレームセット、鍵ディスカバリー、失効、検証ルール)については [署名付きガバナンスコンテキスト](/docs/building/by-layer/L1/security#署名付きガバナンスコンテキスト) を参照。 ## 職務分離 重複しない責任を持つ3つの役割: | 役割 | 責任 | できないこと | | --------------- | --------------------------------------- | -------------------------- | | **ポリシーチーム** | コンプライアンスポリシーの設定、レジストリポリシーの選択、ブランドルールの定義 | キャンペーンの実行や予算の支出 | | **バイイングチーム** | プランの作成、オーケストレーターの操作、メディアバイの実行 | コンプライアンスポリシーの変更やガバナンスのバイパス | | **ガバナンスエージェント** | プランとポリシーに対するアクションの検証、予算の追跡、違反のエスカレーション | 支出の開始やプランの変更 | オーケストレーターはポリシーを保持しないためコンプライアンスをバイパスできない — ポリシーはガバナンスエージェントがブランド設定から解決します。規制が変更された場合、ポリシーチームは設定を一度更新するだけで、すべてのアクティブなキャンペーンが自動的に変更を取り込む。 ## 段階的な採用 キャンペーンガバナンスは3つの運用モードをサポートし、組織が段階的に信頼を構築できる: | モード | 動作 | リスク | | ------------ | ------------------------------------------------------------ | ----------------------------------------- | | **Audit** | すべてをログに記録し、決してブロックしません。検出事項を添付して常に `approved` を返します。 | ゼロ。ライブキャンペーンに影響を与えずにガバナンスが何をフラグするかを確認します。 | | **Advisory** | 実際のステータス(`denied`、`conditions`、`escalated`)を返すが、実行をブロックしません。 | 最小限。人間が事後に検出事項をレビューして対応します。 | | **Enforce** | 違反をブロックします。進む前に解決を要求します。 | 完全な保護を持つ本番ガバナンス。 | まず audit モードで偽陽性率を評価してポリシーをキャリブレーションします。次に advisory モードに移行してリアルキャンペーンで検出事項をテストします。確信が得られたら enforce に切り替える。各ガバナンスチェックレスポンスの `mode` フィールドにより、監査証跡は「advisory モードで拒否(アクションは進んだ)」と「enforce モードで拒否(アクションはブロックされた)」を区別できます。 ## 予算保護 予算は意図したアクションではなく、確認済みの結果に基づいてコミットされます: 1. `tool` + `payload`(意図チェック)を伴う `check_governance` は支出がプランに収まるかどうかをチェックします。予算はコミットされない。 2. オーケストレーターはセラーにアクションを送信します。 3. `report_plan_outcome` がセラーの確認済み金額を報告します。その時点で初めて予算がコミットされます。 セラーが金額を削減した場合、ガバナンスエージェントは実際の金額をコミットして差異にフラグを立てる。アクションが失敗した場合、ガバナンスエージェントはゼロをコミットします。予算状態は意図ではなく現実を反映します。 同時進行のメディアバイは楽観的並行制御または予算予約によって処理され、合計でプラン予算を超える同時承認を防ぐ。 ## 自律性の2つの次元 キャンペーンガバナンスは、エージェントの自律性を2つの独立した次元に分けます。両方がすべてのアクションで評価され、どちらももう一方を上書きしません。 | Field | Dimension | What it controls | | ------------------------------- | --------- | ------------------------------------------------------------------------------ | | `budget.reallocation_threshold` | 運用面 | エージェントがエスカレーションなしに実行できる最大の再配分。`0` はすべての再配分に承認を要求し、`budget.total` 以上の値は実質的に無制限。 | | `plan.human_review_required` | 規制面 | 個人に影響する決定が実行前に人間のレビューを必要とするか | `reallocation_threshold` は資金移動に関するものです。エージェントは尋ねることなくセラー間やチャンネル間でどれだけ移動できるか?`human_review_required` は決定の性質に関するものです。その決定は、法的に人間の関与を要求するレジーム(GDPR 第22条の自動化された意思決定、EU AI Act 附属書 III のユースケース、公正住宅/融資/雇用)に該当するか? ガバナンスエージェントは、解決されたポリシーまたは policy\_categories が `requires_human_review: true` を持つ場合、`human_review_required: true` を自動的に設定します。附属書 III のユースケースや第22条をトリガーする業種は、`reallocation_threshold` がどれだけ寛容であっても、フラグを反転させます。無制限の再配分を持つプランでも、基礎となる業種が要求すれば、すべてのアクションで人間のレビューを必要とする場合があります。 この分離により、組織は運用効率のために広範な再配分の自律性を付与しながら、人間の判断が規制上の要件となる決定カテゴリについては交渉不可能な人間のレビューを維持できます。 ## 信頼度と説明可能性 ガバナンスの検出事項には信頼スコア(0〜1)と説明が含まれており、確実な違反と曖昧なものを区別する: * **高信頼度(0.9以上)**: 確定的な違反。EU ユーザーを明示的にターゲットにしたキャンペーンでの GDPR 違反。 * **中信頼度(0.6〜0.9)**: ガバナンスエージェントが完全に解決できないコンテキストに依存します。未成年者を含む可能性のあるオーディエンスセグメント、規制管轄と部分的に重なるジオターゲティング。 * **低信頼度(0.6未満)**: 推測的。自律的に行動するのではなく、人間のレビューのためにフラグを立てる。 すべての検出事項には人間が読める `explanation` とプログラム的な処理のための構造化 `details` が含まれます。エスカレーションには `reason`、`severity`、オプションで解決に必要な `approval_tier` が含まれます。ブラックボックスはない。 ## ドリフト検出 監査ログは時間の経過とともに監視の侵食を検出する集計メトリクスを表示します: * **エスカレーション率** — 人間にエスカレーションされたチェックの割合、トレンド方向付き * **自動承認率** — 人間の介入なしに承認されたチェックの割合 * **人間オーバーライド率** — 人間がガバナンスエージェントと意見が異なったエスカレーションの割合 組織はこれらのメトリクスに閾値を設定します。閾値が破られると、ガバナンスエージェントは次のチェックに検出事項を含めます。エスカレーション率の低下は、ガバナンスが適切にキャリブレーションされているか、または監視が侵食されていることを意味するかもしれない — 閾値の破れはその問いを表面化させ、組織が判断できるようにします。 ## マルチブランドとエージェンシーガバナンス 複数のブランドとエージェンシーパートナーを持つホールディングカンパニーの場合: * **委任**は権限レベル、予算制限、市場、期限によって、どのエージェントがプランに対して行動できるかをスコープします。ブランドはヨーロッパ向けに1つのエージェンシーに `full` 権限を付与し、北米向けに別のエージェンシーに `execute_only` を付与できます。 * **ポートフォリオガバナンス**はクロスブランド制約を定義します: 総ポートフォリオ支出上限、共有ポリシー強制、個々のブランドプランがオーバーライドできないコーポレートレベルの除外。 ## 小規模ブランドの場合 エージェンシーもポリシーチームも持たずに直接購入するブランドでも以下が得られます: * キャンペーンプランからの自動予算制限とジオ強制 * [ポリシーレジストリ](/docs/governance/policy-registry)からのコンプライアンスカバレッジ — コミュニティメンテナンス、ブランドごとの設定不要 * ガバナンスチェックを通じたセラーサイド検証 * `get_plan_audit_logs` を通じた完全な監査証跡 予算に [`reallocation_threshold`](/docs/governance/campaign/specification#budget-reallocation) を設定してガードレールを定義します。ガバナンスエージェントが残りを処理します。 ## 手動プロセスとの比較 | 手動プロセス | キャンペーンガバナンスの相当機能 | | -------------------- | ------------------------------------ | | エージェンシートレーディングデスク QA | プランに対する自動検証 | | DSP プリビッドルール | 予算権限とターゲティングコンプライアンスチェック | | 広告主承認ワークフロー | `approval_tier` ルーティングによる人間のエスカレーション | | 事後キャンペーン監査 | ドリフトメトリクス付き `get_plan_audit_logs` | | コンプライアンスレビュー | ポリシーレジストリ + 管轄スコープの検証 | 違いは、キャンペーンガバナンスがこれらの制御をたまたまレビューされるものだけでなく、すべてのトランザクションに適用することです。手動プロセスはサンプリングベースで事後的です。キャンペーンガバナンスは網羅的でリアルタイムです。 # コレクションガバナンス Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/collection/index AdCP コレクションガバナンスは、コレクションリスト — どのプロパティが番組を運ぶかとは独立した、番組、シリーズ、その他のコンテンツプログラムのための管理された除外・包含リスト — を通じてプログラムレベルのブランドセーフティを可能にする。 Jordan sits at her desk studying a dense brand safety spreadsheet on her monitor — rows of partner names and excluded programs that need to become machine-readable Jordan はスプレッドシートを見つめています。ホールディングカンパニーが Nova Motors の CTV「放映しない」リストをちょうど送ってきました — ネットワークで整理された 200 以上のプログラム、特定の番組、ジャンル全体、コンテンツレーティングの混合。一部のエントリーはアプリレベルの除外です。一部は 5 つの異なるプラットフォームで放映される個別のプログラムです。あるセクションは「常に子供向けプログラミングを除外」と言い、3 行後に「G または PG 評価のアニメーションは許可」と言っています。 このスプレッドシートはリニア TV を扱う人間には意味をなします。十数のセラー全体でプログラマティック CTV を管理する AI エージェントには意味をなしません。 Jordan はこれを機械が強制できるものに変える必要があります。プロパティリストはアプリレベルの除外を処理します — 彼女はそれを以前にやりました。しかしプログラムレベルの除外は? 番組は単一のアプリに属しません。どこでも放映されます。彼女は、実行される場所とは独立して *プログラム自体* を識別する構造を必要とします。 それがコレクションリストの目的です。 ## ブランドセーフティのギャップ Three-layer brand safety diagram — properties on top, collections in the middle, content standards at the bottom — Jordan points at the collection layer she is building コレクションリスト以前、AdCP には 2 つのブランドセーフティ層がありました: * **プロパティリスト** は広告が *どこで* 実行されるか — どのアプリ、サイト、プラットフォーム — を制御します。Jordan は「このニュースアプリでは不可」と言え、すべてのセラーがそれを強制します。 * **コンテンツ標準** は広告に *どのコンテンツ* が隣接するか — 自然言語ポリシーに対するインプレッションごとの評価 — を制御します。「G/PG アニメーションを除く子供コンテンツを除外」のようなニュアンスを処理します。 欠けている層は、広告が *どのプログラム* で実行されるかです。特定のクライムドラマは 3 つのストリーミングプラットフォームとケーブルシンジケーションで放映されます。1 つのプロパティから除外してもそれを他から除外しません。Jordan は「どこでもこのプログラムでは不可」と言い — すべてのセラーに彼女の意味を理解させる必要があります。 コレクションリストはこのギャップを埋めます。プロパティリストとコンテンツ標準とともに、3 つの合成可能な層を形成します: | Layer | Construct | What it controls | When | | ---------- | ------------- | ---------------------- | ---------- | | Property | プロパティリスト | 広告が実行される場所(アプリ、サイト) | セットアップ | | Collection | **コレクションリスト** | 広告が実行されるコンテンツ(番組、シリーズ) | セットアップ | | Content | コンテンツ標準 | 広告に隣接する特定のコンテンツ | インプレッションごと | ほとんどのバイヤーは 1 つか 2 つの層を使います。特定のプログラムだけを除外する必要のあるバイヤーはコレクションリスト単独を使います。3 層モデルは合成フレームワークであり、要件ではありません。 ## プログラム識別子の解決 Jordan maps program names to distribution identifiers at a display — green lines connect resolved programs, amber lines show unresolved ones she marks for follow-up スプレッドシートは名前でプログラムをリストします。機械は識別子を必要とします。Jordan のバイヤーエージェントは各プログラム名を、どの CTV プラットフォームが運ぶかにかかわらずプログラムを一意に識別する、プラットフォーム非依存の [distribution identifier](/docs/media-buy/product-discovery/collections-and-installments) — IMDb ID、Gracenote ID、または EIDR ID — に解決します。 ほとんどのプログラムは即座に解決します。いくつかは解決しません — エージェントは Jordan が手動で確認するためこれらをフラグします。これは翻訳ステップです: 人間可読な名前が、エコシステムのすべてのセラーが理解する機械可読な識別子になります。 **Gracenote ID ガイダンス:** ルートレベル ID を使ってください — シリーズには SH プレフィックス、映画には MV プレフィックス、スポーツプログラムには SP プレフィックス。エピソードレベル ID(EP プレフィックス)はコレクションリストに属しません。エピソードレベル評価はコンテンツ標準の関心事です。 ## コレクションリストの構築 A funnel filters collections through rating, genre, and explicit exclusion layers — Jordan reviews the clean resolved list emerging at the bottom Jordan のバイヤーエージェントは、明示的なプログラム除外を構造フィルターと組み合わせて、ガバナンスエージェントにコレクションリストを作成します: ```json theme={null} { "tool": "create_collection_list", "arguments": { "name": "Nova Motors CTV Do Not Air — 2026", "base_collections": [ { "selection_type": "distribution_ids", "identifiers": [ { "type": "imdb_id", "value": "tt9999901" }, { "type": "imdb_id", "value": "tt9999902" }, { "type": "gracenote_id", "value": "SH000003" } ] } ], "filters": { "content_ratings_exclude": [ { "system": "tv_parental", "rating": "TV-MA" }, { "system": "bbfc", "rating": "18" } ], "genres_exclude": ["news"], "genre_taxonomy": "iab_content_3.0" }, "brand": { "domain": "novamotors.com" } } } ``` 明示的なエントリーは名指しされたプログラムを処理します。フィルターは構造除外を処理します — TV-MA コンテンツなし、ニュースジャンルなし。フィルターはセーフティネットです: 任意のプラットフォームの任意の新しい TV-MA シリーズは、Jordan がリストを更新することなく自動的に除外されます。 「子供 vs. G/PG アニメーション」の矛盾は? それはコレクションリストの問題ではありません — メタデータではなく実際のエピソードコンテンツの評価を要求します。Jordan はそれをそれが属する [コンテンツ標準](/docs/governance/content-standards/index) に入れます。 ### フィルターの合成方法 Include フィルターは allowlist、exclude フィルターは blocklist です。両方が同じ次元に存在するとき、include が最初に適用され、次に exclude がさらに狭めます。 **例:** `genres_include: ["drama", "comedy"]` + `genres_exclude: ["crime"]` はまずドラマとコメディのコレクションのみを含め、次に crime としてもタグ付けされたものを削除します。`["drama", "crime"]` とタグ付けされたコレクションは除外されます — exclude フィルターが勝ちます。 ## セラーは自身の在庫に対してマッチ Split scene — Jordan's governance agent sends the collection list to Priya at StreamHaus, whose inventory lights up showing matched exclusions Jordan のメディアバイがコレクションリストを参照すると、StreamHaus の Priya のセールスエージェントがそれをフェッチし、エントリーを StreamHaus のコレクション在庫に対してマッチし、マッチしたプログラムを配信から除外します。マッチングは distribution identifier を使います — StreamHaus は `adagents.json` でコレクションに Gracenote ID を宣言したため、マッチは自動的です。 Priya のエージェントが報告し返します: 200 の除外プログラムのうち 47 が StreamHaus のライブラリにあります。TV-MA フィルターで捕捉された 12 の追加コレクション。残りは StreamHaus が運ぶプログラムではありません — 認識され無視されます。 リストは 1 週間キャッシュされます(コレクションメタデータはプロパティメタデータより頻繁に変わりません)。ガバナンスエージェントがリストを再解決すると — 新しいシーズンが番組のコンテンツレーティングを変える、または Jordan がプログラムを追加 — セラーは webhook を受け取りキャッシュをリフレッシュします。 ## ターゲティング統合 コレクションリストはターゲティングオーバーレイでプロパティリストと並んで参照されます: ```json theme={null} { "targeting": { "property_list": { "agent_url": "https://governance.pinnacleagency.com", "list_id": "pl_novamotors_approved_ctv" }, "collection_list_exclude": { "agent_url": "https://governance.pinnacleagency.com", "list_id": "cl_novamotors_dna_2026" } } } ``` | Field | Semantics | Use case | | ------------------------- | ------------------------ | --------------------- | | `collection_list` | 包含 — これらのコレクションでのみ実行 | 「これら 3 番組でのみプリロールを購入」 | | `collection_list_exclude` | 除外 — これらのコレクションで決して実行しない | ブランドセーフティの放映しないリスト | メディアバイは両方を同時に参照できます — 「これらの承認された番組で実行するが、承認リストに現れてもこれらの特定のプログラムでは決して実行しない」。除外リストは重複で常に勝ちます。 **なぜコレクションには 2 つのフィールドでプロパティには 1 つか?** プロパティリストは、他のターゲティング次元(geo、audience、device)で今使われるペア化された include/exclude パターンに先行します。コレクションリストは現在のパターンに従います。将来の進化は対称性のため `property_list_exclude` を追加するかもしれません。 ## Jordan の 3 層構成 Jordan が終える頃には、Nova Motors の CTV ブランドセーフティは 3 つの機械可読なアーティファクトで表現されます: 1. **プロパティリスト** — 除外されたアプリと承認された CTV プラットフォーム 2. **コレクションリスト** — distribution identifier によって除外されたプログラム + TV-MA とニュースジャンルフィルター 3. **コンテンツ標準** — エピソードごとの判断を要求するニュアンスのある子供/アニメーションポリシー 各層は独立に管理され、独立にキャッシュ可能で、独立に強制可能です。ホールディングカンパニーが来四半期に更新された放映しないリストを送るとき、Jordan のエージェントはそれを既存のコレクションリストと差分し、変わったものだけを更新します。 もうスプレッドシートはありません。もうネットワークごとの手動トラフィッキングはありません。1 つのリスト、どこでも強制。 ## プロパティリストとの関係 プロパティリストとコレクションリストは兄弟構造です — 両方とも同じライフサイクルパターン(create、get、update、list、delete、webhook)でガバナンスエージェントが管理する在庫リストです。それらは何に対処するかで異なります: | Dimension | Property list | Collection list | | ---------- | --------------------------- | -------------------------------------------- | | 識別するもの | 技術的表面(ドメイン、アプリ) | コンテンツプログラム(番組、シリーズ) | | プライマリ識別子 | プロパティ識別子(ドメイン、バンドル ID) | Distribution identifier(IMDb、Gracenote、EIDR) | | フィルター | 国、チャネル、プロパティタイプ、フィーチャー | コンテンツレーティング、ジャンル、種類、制作品質 | | キャッシュデフォルト | 24 時間 | 168 時間(1 週間) | | クロスパブリッシャー | プロパティレジストリ経由(property\_rid) | コレクションレジストリ経由(collection\_rid) | ## タスク ### コレクションリスト管理 * **[create\_collection\_list](/docs/governance/collection/tasks/collection_lists#create_collection_list)**: ガバナンスエージェントに新しいコレクションリストを作成 * **[get\_collection\_list](/docs/governance/collection/tasks/collection_lists#get_collection_list)**: 解決されたコレクションを取得(キャッシングガイダンス付き) * **[update\_collection\_list](/docs/governance/collection/tasks/collection_lists#update_collection_list)**: フィルターまたはベースコレクションを変更 * **[list\_collection\_lists](/docs/governance/collection/tasks/collection_lists#list_collection_lists)**: アカウントのコレクションリストをリスト * **[delete\_collection\_list](/docs/governance/collection/tasks/collection_lists#delete_collection_list)**: コレクションリストを削除 # コレクションリスト管理 Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/collection/tasks/collection_lists AdCP のコレクションリストタスクは、明示的なプログラム参照を動的なジャンルとコンテンツレーティングフィルターと組み合わせて、コンテンツプログラムの包含・除外リストを作成、更新、取得、リスト、削除する。 # コレクションリスト管理 コレクションリストは、「これらのコレクション、これらの基準でフィルターされた」を表現する管理された、キャッシュ可能なアーティファクトです。[プロパティリスト](/docs/governance/property/tasks/property_lists) と並行しますが、技術的表面(ドメイン、アプリ)ではなくコンテンツプログラム(番組、シリーズ、ポッドキャスト)で動作します。 ## アーキテクチャ ``` SETUP TIME BID TIME REFRESH ───────────── ──────── ─────── Buyer creates list ──► Governance Seller uses cached ◄── Webhook notifies base_collections agent collection list seller to + filters resolves re-fetch & caches ``` コレクションリストは **セットアップ時リソース** です。ガバナンスエージェントによって一度解決され、セラーによってキャッシュされ、ガバナンスエージェントへのランタイム呼び出しなしに配信決定で使われます。 ## タスク概要 | Task | Purpose | Response time | | ------------------------ | ---------------------- | ------------- | | `create_collection_list` | 新しいコレクションリストを作成 | 秒 | | `get_collection_list` | 解決されたコレクションを伴うリストをフェッチ | 秒(キャッシュ済み) | | `update_collection_list` | ベースコレクションまたはフィルターを変更 | 秒 | | `list_collection_lists` | アカウントのコレクションリストをリスト | 秒 | | `delete_collection_list` | コレクションリストを削除 | 秒 | ## ベースコレクションソース コレクションリストは、3 つのパターンで選択されたコレクションのベースセットで始まります: ### distribution\_ids プラットフォーム非依存の識別子でコレクションを選択します。クロスパブリッシャー除外の主要メカニズム — IMDb ID は、どの CTV プラットフォームが運ぶかにかかわらずプログラムを識別します。 ```json theme={null} { "selection_type": "distribution_ids", "identifiers": [ { "type": "imdb_id", "value": "tt9999901" }, { "type": "gracenote_id", "value": "SH000001" }, { "type": "eidr_id", "value": "10.5240/XXXX-XXXX-XXXX-XXXX-XXXX-C" } ] } ``` ### publisher\_collections パブリッシャーの `adagents.json` 内の特定のコレクションをコレクション ID で選択します。パブリッシャーの内部識別子が既知のときに使います。 ```json theme={null} { "selection_type": "publisher_collections", "publisher_domain": "titanstreaming.com", "collection_ids": ["danger_zone", "wild_nights"] } ``` ### publisher\_genres ジャンル基準に一致するパブリッシャーのすべてのコレクションを選択します。特定のパブリッシャーからコンテンツカテゴリー全体を除外するときに使います。 ```json theme={null} { "selection_type": "publisher_genres", "publisher_domain": "streamhaus.com", "genres": ["news"], "genre_taxonomy": "iab_content_3.0" } ``` `base_collections` が省略されると、リストはガバナンスエージェントのコレクションデータベース全体に対してフィルターを適用します。 ## フィルター フィルターはベースコレクション選択の後、解決されたリストを狭めます: | Filter | Type | Logic | Description | | -------------------------- | ----------------- | ----- | --------------------------------------------------------- | | `content_ratings_exclude` | ContentRating\[] | OR | これらのレーティングのいずれかを持つコレクションを除外 | | `content_ratings_include` | ContentRating\[] | OR | これらのレーティングを持つコレクションのみを含む | | `genres_exclude` | string\[] | OR | 任意のジャンルでタグ付けされたコレクションを除外 | | `genres_include` | string\[] | OR | 任意のジャンルを持つコレクションのみを含む | | `genre_taxonomy` | string | — | ジャンルフィルター値の分類 | | `kinds` | string\[] | OR | コレクション種類にフィルター(series、publication、event\_series、rotation) | | `exclude_distribution_ids` | DistributionId\[] | OR | これらの特定のコレクションを常に除外 | | `production_quality` | string\[] | OR | 制作品質階層でフィルター | **Include 対 exclude**: include フィルターは allowlist、exclude フィルターは blocklist です。両方が同じ次元に存在するとき、include が最初に適用され、次に exclude がさらに狭めます。 **例:** `genres_include: ["drama", "comedy"]` と `genres_exclude: ["crime"]` を持つリストは、まずドラマとコメディのコレクションのみを含め、次に crime としてタグ付けされたものを削除します。`["drama", "crime"]` とタグ付けされたコレクションは除外されます — exclude フィルターが勝ちます。`["sports"]` とタグ付けされたコレクションは include フィルターによって除外されます(許可されたセットにない)。 **コンテンツレーティングはメタデータフィルターであり、コンテンツ評価ではありません。** `content_ratings_exclude: [{ system: "tv_parental", rating: "TV-MA" }]` は TV-MA として *宣言された* すべてのコレクションを除外します。個別のエピソードを評価しません — それは [コンテンツ標準](/docs/governance/content-standards/index) です。 **ジャンル分類** はバイヤーとセラー間のジャンルマッチングを正規化します。サポートされる分類: `iab_content_3.0`、`iab_content_2.2`、`gracenote`、`eidr`、`apple_genres`、`google_genres`、`roku`、`amazon_genres`、`custom`。`custom` 値はパブリッシャー定義の分類のエスケープハッチです — バイヤーとセラーが帯域外で語彙を交渉します。 ## create\_collection\_list ガバナンスエージェントに新しいコレクションリストを作成します。 **Request:** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/collection/create-collection-list-request.json", "idempotency_key": "c5d6e7f8-a9b0-4123-c456-123456789012", "name": "Nova Motors CTV Do Not Air — 2026", "description": "Programs excluded from Nova Motors CTV advertising", "base_collections": [ { "selection_type": "distribution_ids", "identifiers": [ { "type": "imdb_id", "value": "tt9999901" }, { "type": "imdb_id", "value": "tt9999902" } ] }, { "selection_type": "publisher_genres", "publisher_domain": "streamhaus.com", "genres": ["news", "crime"], "genre_taxonomy": "iab_content_3.0" } ], "filters": { "content_ratings_exclude": [ { "system": "tv_parental", "rating": "TV-MA" }, { "system": "bbfc", "rating": "18" } ], "genres_exclude": ["news"], "genre_taxonomy": "iab_content_3.0", "kinds": ["series"] }, "brand": { "domain": "novamotors.com" } } ``` **Response:** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/collection/create-collection-list-response.json", "status": "completed", "list": { "list_id": "cl_novamotors_dna_2026", "name": "Nova Motors CTV Do Not Air — 2026", "collection_count": 247, "created_at": "2026-04-07T12:00:00Z", "updated_at": "2026-04-07T12:00:00Z" }, "auth_token": "tok_example_store_this_securely" } ``` `auth_token` は作成時にのみ返されます。それを保存してください — それはセラーにこのリストをフェッチする認可を与えます。 ## get\_collection\_list 解決されたコレクションを伴うコレクションリストを取得します。セラーはリストをフェッチしキャッシュするためこれを呼びます。 **Request:** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/collection/get-collection-list-request.json", "list_id": "cl_novamotors_dna_2026", "resolve": true, "pagination": { "max_results": 1000 } } ``` **Response:** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/collection/get-collection-list-response.json", "status": "completed", "list": { "list_id": "cl_novamotors_dna_2026", "name": "Nova Motors CTV Do Not Air — 2026", "collection_count": 247 }, "collections": [ { "collection_rid": "019abc12-3d4e-7f5a-ab6c-7d8e9f0a1b2c", "name": "Danger Zone", "distribution_ids": [ { "type": "imdb_id", "value": "tt9999901" } ], "content_rating": { "system": "tv_parental", "rating": "TV-MA" }, "genre": ["comedy", "animation"], "genre_taxonomy": "iab_content_3.0", "kind": "series" } ], "pagination": { "has_more": false }, "resolved_at": "2026-04-07T14:00:00Z", "cache_valid_until": "2026-04-14T14:00:00Z", "coverage_gaps": { "genre": [ { "type": "imdb_id", "value": "tt9999905" } ] } } ``` **カバレッジギャップ** は、フィルターされた次元のメタデータが欠けているにもかかわらずリストに含まれたコレクションをレポートします。この例では、`tt9999905` は含まれましたがジャンルメタデータがありません — ガバナンスエージェントはそれがジャンルフィルターに一致することを確認できませんでした。 **キャッシング**: セラーは解決されたコレクションをキャッシュし `cache_valid_until` の後に再フェッチすべきです。デフォルトキャッシュ期間は 168 時間(1 週間)です。コレクションメタデータはプロパティメタデータより頻繁に変わらないからです。 ## update\_collection\_list 既存のコレクションリストを変更します。`base_collections` と `filters` はパッチではなく完全な置き換えです。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/collection/update-collection-list-request.json", "idempotency_key": "d6e7f8a9-b0c1-4234-d567-234567890123", "list_id": "cl_novamotors_dna_2026", "base_collections": [ { "selection_type": "distribution_ids", "identifiers": [ { "type": "imdb_id", "value": "tt9999901" }, { "type": "imdb_id", "value": "tt9999902" }, { "type": "imdb_id", "value": "tt9999903" } ] } ], "filters": { "content_ratings_exclude": [ { "system": "tv_parental", "rating": "TV-MA" } ] }, "webhook_url": "https://governance.pinnacleagency.com/webhooks/collection-lists" } ``` ## list\_collection\_lists アカウントのコレクションリストをリストします。解決されたコレクションではなくメタデータのみを返します。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/collection/list-collection-lists-request.json", "account": { "brand": { "domain": "novamotors.com" }, "operator": "pinnacleagency.com" }, "pagination": { "max_results": 50 } } ``` ## delete\_collection\_list コレクションリストを削除します。キャッシュされたコピーを持つセラーは webhook 更新の受信を停止します。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/collection/delete-collection-list-request.json", "idempotency_key": "e7f8a9b0-c1d2-4345-e678-345678901234", "list_id": "cl_novamotors_dna_2026" } ``` ## Webhook コレクションリストの解決されたコレクションが変わるとき(新しいプログラムが一致、レーティング更新、プログラム削除)、ガバナンスエージェントは webhook 通知を送ります: ```json theme={null} { "idempotency_key": "clch_01HW9DEPJ5MN8Q2R4T6V8X0Z2B", "event": "collection_list_changed", "list_id": "cl_novamotors_dna_2026", "list_name": "Nova Motors CTV Do Not Air — 2026", "change_summary": { "collections_added": 3, "collections_removed": 1, "total_collections": 249 }, "resolved_at": "2026-04-08T10:00:00Z", "cache_valid_until": "2026-04-15T10:00:00Z", "signature": "..." } ``` Webhook はサマリーのみを含みます — 受信者は更新されたエントリーのため `get_collection_list` を呼ばなければなりません。受信者は処理前に `signature` を検証しなければならず(MUST)、同じ変更イベントのリトライ配信が無視されるよう `idempotency_key` で重複排除しなければなりません(MUST)。 ## ライブスポーツ ライブスポーツは最大の CTV ブランドセーフティ懸念の 1 つです。コレクションリストは `event_series` 種類を通じてそれを処理します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/collection/create-collection-list-request.json", "idempotency_key": "f8a9b0c1-d2e3-4456-f789-456789012345", "name": "Acme Outdoor — Excluded Sports Events", "base_collections": [ { "selection_type": "distribution_ids", "identifiers": [ { "type": "gracenote_id", "value": "SP000001" }, { "type": "gracenote_id", "value": "SP000002" } ] } ], "filters": { "kinds": ["event_series"], "genres_exclude": ["combat_sports"], "genre_taxonomy": "gracenote" }, "brand": { "domain": "acmeoutdoor.com" } } ``` これは特定のスポーツプログラムを Gracenote ID(スポーツには SP プレフィックス)で除外し、すべての格闘技イベントシリーズを構造的に除外します。`event_series` 種類フィルターは、リストがスポーツについてのドキュメンタリーシリーズではなくライブイベントプログラミングをターゲットすることを保証します。 ## Security considerations コレクションリストは配信決定をゲートするため、`auth_token` と webhook コールバックは明示的なライフサイクルルールを必要とします。[Security](/docs/building/by-layer/L1/security) の一般的な制御が適用されます。コレクションリスト固有のルール: **`auth_token` のスコープ、失効、ログ衛生。** 各トークンは正確に 1 つの `list_id` を認可します。トークンをリスト間で再利用しないでください。ガバナンスエージェントはリストを受け取るセラーごとに明確なトークンを発行しなければなりません(MUST) — 共有トークンは関係ごとに失効できず、単一の侵害への唯一の応答をリスト全体のローテーションにします。トークンはログ、キャッシュキー、メトリックラベルに書き込まれてはならず(MUST NOT)、`get_collection_list` からのエラーレスポンスは提示されたトークンをエコーしてはなりません(MUST NOT)。 `delete_collection_list` とセラーごとの失効は異なります: * **通常の削除または関係終了**: トークンは後続の `get_collection_list` 呼び出しを即座に失敗させなければなりません(MUST)が、キャッシュされた解決を持つセラーは `cache_valid_until` までキャッシュから提供し続けてもよい(MAY)。自然な関係終了は侵害ではありません。 * **侵害駆動の失効**: ガバナンスエージェントはキャッシュ無効化をシグナルしなければなりません(MUST)。セラーがまだ完了するアクセスを持つ次のポーリングで削減された `cache_valid_until`(`now` 以下)を返すか、キャッシュされたコピーが破棄されるよう `change_summary` がリストバージョンが無効化されたことを伝える `collection_list_changed` webhook を発行します。予定された TTL まで侵害されたコンテンツをセラーキャッシュに残すことは許容されません。 **Webhook URL 検証。** `update_collection_list` の `webhook_url` は、他の任意のバイヤー提供コールバック URL と SSRF 等価です。正準の [Webhook URL validation (SSRF)](/docs/building/by-layer/L1/security#webhook-url-validation-ssrf) ルールを適用してください — HTTPS のみ、検証された IP 範囲(`::ffff:0:0/96` を含む IPv4 と IPv6)、接続ピン留め(DNS 再解決だけでなく)、リダイレクトフォローなし、サイズとタイムアウト上限。 **Webhook 署名アルゴリズム。** webhook 署名は [標準 webhook 署名ルール](/docs/building/by-layer/L1/security#webhook-security) に従わなければなりません(MUST)。デフォルトで、RFC 9421 [webhook コールバックプロファイル](/docs/building/by-layer/L1/security#webhook-callbacks) が適用されます: ガバナンスエージェントは、自身の brand.json の `agents[]` エントリーの `jwks_uri` で公開された `adcp_use: "request-signing"` 鍵で署名します。非推奨の `webhook-signing` 鍵は互換性ウィンドウ中受理されたままです。サブスクライブするセラーは、`tag="adcp/webhook-signing/v1"` でカバードコンポーネント `@method`、`@target-uri`、`@authority`、`content-type`、`content-digest` を検証します。非推奨の HMAC-SHA256 フォールバックは、サブスクライブするセラーが webhook 登録で `authentication.credentials` を投入するときのみ適用されます。そのパスは [Legacy HMAC-SHA256 fallback](/docs/building/by-layer/L1/security#legacy-hmac-sha256-fallback-deprecated-removed-in-40) ルールに従い、そのパスの任意のボディ `signature` フィールドは便宜コピーです — 受信者はヘッダーに対して検証しなければならず(MUST)、ボディ値を信頼してはなりません(MUST NOT)。 **Distribution-ID 入力。** ガバナンスエージェントは永続化前に識別子形式を検証すべきで(SHOULD)(IMDb: `^tt\d+$`、EIDR: `10.5240/...`、Gracenote: ベンダープレフィックス)、リスト肥大化 DoS を防ぐためリスト変更にアカウントごとのレート制限を強制すべきです(SHOULD)。未解決の識別子を黙って落とすのではなく `coverage_gaps` に表示します。 ## セラーとコレクションリストを共有する パターンは [プロパティリストの共有](/docs/governance/property/index#sharing-property-lists-with-sellers) に一致します: 1. ガバナンスエージェントにコレクションリストを作成 2. 作成レスポンスから `auth_token` を保存 3. ターゲティングオーバーレイで `collection_list` または `collection_list_exclude` を渡す 4. セラーが auth トークンを使って解決されたリストをフェッチしキャッシュ 5. リストが変わると webhook がセラーに通知 # アーティファクト Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/content-standards/artifacts AdCP のアーティファクトは広告掲載面に隣接するコンテンツコンテキストを表し、生コンテンツをパブリッシャー外に出すことなくブランド適合性評価を可能にします。 # Artifacts **アーティファクト** は広告掲載面に隣接するコンテンツの単位です。ブランドセーフティ/適合性の評価とは「このアーティファクトは自社広告に適切か?」を問うことです。 ## アーティファクトとは アーティファクトは広告表示コンテキストを表します: * ウェブサイト上の **ニュース記事** * 広告ブレーク間の **ポッドキャストセグメント** * YouTube 動画内の **動画チャプター** * フィード内の **ソーシャルメディア投稿** * CTV 番組の **シーン** * チャット会話内の **AI 生成画像** アーティファクトは `property_id` + `artifact_id` で識別します。property がコンテンツの所在地を、artifact\_id がそのコンテンツ固有の識別子を表します。artifact\_id の形式は柔軟で、URL パスやプラットフォーム固有 ID、内部で一貫して使う任意の ID で構いません。 ## 構造 **Schema**: [artifact.json](https://adcontextprotocol.org/schemas/v3/content-standards/artifact.json) ```json theme={null} { "property_id": {"type": "domain", "value": "reddit.com"}, "artifact_id": "r_fitness_post_abc123", "assets": [ {"type": "text", "role": "title", "content": "Best protein sources for muscle building", "language": "en"}, {"type": "text", "role": "paragraph", "content": "Looking for recommendations on high-quality protein sources...", "language": "en"}, {"type": "image", "url": "https://cdn.reddit.com/fitness-image.jpg", "alt_text": "Person lifting weights"} ] } ``` ### 必須フィールド | Field | Description | | ------------- | ------------------------------------------------------------------- | | `property_id` | このアーティファクトの所在。標準の識別子型(`domain`, `app_id`, `apple_podcast_id` など)を使用 | | `artifact_id` | プロパティ内で一意の識別子。形式はプロパティ所有者が定義 | | `assets` | ドキュメント順のコンテンツ(テキスト、画像、動画、音声など) | ### 任意フィールド | Field | Description | | ------------------ | ------------------------------------ | | `variant_id` | 特定のバリアント(A/B テスト、翻訳、時間差バージョン)を識別 | | `format_id` | フォーマットレジストリ参照(クリエイティブの format と同様) | | `url` | アーティファクトの Web URL がある場合 | | `metadata` | アーティファクト単位のメタデータ(OGP、JSON-LD、著者情報など) | | `published_time` | 公開日時 | | `last_update_time` | 最終更新日時 | ## バリアント 同一アーティファクトに複数バリアントが存在する場合があります: * **Translations** - 英語版 vs スペイン語版 * **A/B tests** - テスト中の異なるヘッドライン * **Temporal versions** - 水曜日に変更されたコンテンツ `variant_id` で区別します: ```json theme={null} // English version { "property_id": {"type": "domain", "value": "nytimes.com"}, "artifact_id": "article_12345", "variant_id": "en", "assets": [ {"type": "text", "role": "title", "content": "Breaking News Story", "language": "en"} ] } // Spanish translation { "property_id": {"type": "domain", "value": "nytimes.com"}, "artifact_id": "article_12345", "variant_id": "es", "assets": [ {"type": "text", "role": "title", "content": "Noticia de última hora", "language": "es"} ] } // A/B test variant { "property_id": {"type": "domain", "value": "nytimes.com"}, "artifact_id": "article_12345", "variant_id": "headline_test_b", "assets": [ {"type": "text", "role": "title", "content": "Alternative Headline Being Tested", "language": "en"} ] } ``` `artifact_id` と `variant_id` の組み合わせはプロパティ内で一意としてください。どのバリアントが配信されたかをレポートと突き合わせられます。 ## アセットタイプ アセットはアーティファクト内の実コンテンツです。タイトル、本文、画像、動画などすべてアセットとして表現します。 ### Text ```json theme={null} {"type": "text", "role": "title", "content": "Article Title", "language": "en"} {"type": "text", "role": "paragraph", "content": "The article body text...", "language": "en"} {"type": "text", "role": "description", "content": "A summary of the article", "language": "en"} {"type": "text", "role": "heading", "content": "Section Header", "heading_level": 2} {"type": "text", "role": "quote", "content": "A quoted statement"} ``` Roles: `title`, `description`, `paragraph`, `heading`, `caption`, `quote`, `list_item` 各テキストアセットは混在言語コンテンツのために独自の `language` タグを持てます。 ### 画像 (Image) ```json theme={null} { "type": "image", "url": "https://cdn.example.com/photo.jpg", "alt_text": "Description of the image" } ``` ### 動画 (Video) ```json theme={null} { "type": "video", "url": "https://cdn.example.com/video.mp4", "transcript": "Full transcript of the video content...", "duration_ms": 180000 } ``` ### 音声 (Audio) ```json theme={null} { "type": "audio", "url": "https://cdn.example.com/podcast.mp3", "transcript": "Today we're discussing...", "duration_ms": 3600000 } ``` ## メタデータ アーティファクト全体を表すメタデータです。個別アセットではありません: ```json theme={null} { "metadata": { "author": "Jane Smith", "canonical": "https://example.com/article/12345", "open_graph": { "og:type": "article", "og:site_name": "Example News" }, "json_ld": [ { "@type": "NewsArticle", "datePublished": "2025-01-15" } ] } } ``` これはコンテンツ自体ではなくアーティファクトコンテナに関する情報のため、アセットとは分離しています。 ## セキュアなアセットアクセス AI 生成画像、プライベート会話、有料コンテンツなど公開されないアセットが多数あります。アーティファクトスキーマは認証付きアクセスをサポートします。 ### 事前設定(推奨) 継続的な提携では、リクエストごとではなくオンボーディング時にアクセス設定を行います: 1. **サービスアカウント共有** - クラウドストレージへの検証エージェントアクセスを付与 2. **OAuth クライアント認証情報** - マシン間認証をセットアップ 3. **API キー交換** - セットアップ時に長期 API キーを共有 これはバイヤーからコンテンツスタンダードを初めて受け取ったセラーのアクティベーションフェーズで行います。 ### アセット単位の認証 事前設定ができない場合は、アセットごとに認証情報を含めます: ```json theme={null} { "type": "image", "url": "https://cdn.openai.com/secured/img_abc123.png", "access": { "method": "bearer_token", "token": "eyJhbGciOiJIUzI1NiIs..." } } ``` **トークンサイズについての注意**: 多数のアセットを持つアーティファクトでは、アセット単位のトークンによってペイロードサイズが大幅に増加する場合があります。以下の方法を検討してください: 1. **事前設定アクセス** - オンボーディング時にサービスアカウントアクセスを一度設定 2. **共有トークン参照** - アーティファクトレベルでトークンを定義し ID で参照 3. **署名付き URL** - URL 自体が認証情報となる事前署名 URL を使用 `url` フィールドはアクセス URL で、アーティファクトの正規/公開 URL とは異なる場合があります。例えば `https://news.example.com/article/123` として公開された記事が `https://cdn.example.com/secured/...` からアセット提供される場合があります。 ### Access Methods | Method | Use Case | | ----------------- | ----------------------------------------------------------------------- | | `bearer_token` | OAuth2 bearer token in Authorization header | | `service_account` | GCP/AWS service account credentials | | `signed_url` | Pre-signed URL with embedded credentials (URL itself is the credential) | ### Service Account Setup For GCP: ```json theme={null} { "access": { "method": "service_account", "provider": "gcp", "credentials": { "type": "service_account", "project_id": "my-project", "private_key_id": "...", "private_key": "-----BEGIN PRIVATE KEY-----\n...", "client_email": "verification-agent@my-project.iam.gserviceaccount.com" } } } ``` For AWS: ```json theme={null} { "access": { "method": "service_account", "provider": "aws", "credentials": { "access_key_id": "AKIAIOSFODNN7EXAMPLE", "secret_access_key": "...", "region": "us-east-1" } } } ``` ### Pre-Signed URLs 認証情報を共有せずに一時的なアクセスを行う場合: ```json theme={null} { "type": "video", "url": "https://storage.googleapis.com/bucket/video.mp4?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=...&X-Goog-Signature=...", "access": { "method": "signed_url" } } ``` URL 自体に認証情報が含まれているため、追加認証は不要です。 ## Property Identifier Types `property_id` は AdCP プロパティスキーマの標準識別子型を使用します: | Type | Example | Use Case | | -------------------- | --------------------------------------- | ---------------- | | `domain` | `reddit.com` | Websites | | `app_id` | `com.spotify.music` | Mobile apps | | `apple_podcast_id` | `1234567890` | Apple Podcasts | | `spotify_show_id` | `4rOoJ6Egrf8K2IrywzwOMk` | Spotify podcasts | | `youtube_channel_id` | `UCddiUEpeqJcYeBxX1IVBKvQ` | YouTube channels | | `rss_url` | `https://feeds.example.com/podcast.xml` | RSS feeds | ## Artifact ID Schemes プロパティ所有者が artifact\_id の形式を定義します。例: | Property Type | Artifact ID Pattern | Example | | ------------- | ------------------------------------------- | ------------------------ | | News website | `article_{id}` | `article_12345` | | Reddit | `r_{subreddit}_{post_id}` | `r_fitness_abc123` | | Podcast | `episode_{num}_segment_{num}` | `episode_42_segment_2` | | CTV | `show_{id}_s{season}e{episode}_scene_{num}` | `show_abc_s3e5_scene_12` | | Social feed | `post_{id}` | `post_xyz789` | 検証エージェントはこの形式を解釈する必要はなく、不透明なものとして扱います。プロパティ所有者は自社コンテンツとの突き合わせに使います。 ## Related * [Content Standards Overview](.) - アーティファクトがコンテンツスタンダードワークフローに占める役割 * [calibrate\_content](./tasks/calibrate_content) - キャリブレーション用アーティファクトの送付 # 実装ガイド Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/content-standards/implementation-guide Sales agent/オーケストレーター/ガバナンスエージェントとして Content Standards プロトコルを実装するためのステップバイステップガイド このガイドは Content Standards プロトコルの実装パターンを 3 つの視点で説明します: 1. **Sales agents** - ブランド適合性スタンダードの受け入れと適用 2. **Orchestrators** - パブリッシャー横断でのコンテンツスタンダードの調整 3. **Governance agents** - コンテンツ評価サービスの提供 ## 役割の整理 まず役割分担を把握します: | Role | Examples | Responsibilities | | -------------------- | -------------------------------- | ----------------------------------------------------------------- | | **Orchestrator** | DSP, トレーディングデスク、代理店プラットフォーム | メディアバイを調整し、スタンダード参照をセラーへ渡し、検証用アーティファクトを受領 | | **Sales Agent** | パブリッシャー Ad サーバー、SSP | スタンダード受領、ローカルモデルのキャリブレーション、配信時の適用、アーティファクト送付 | | **Governance Agent** | IAS, DoubleVerify, ブランドセーフティサービス | スタンダードをホストし、`calibrate_content` と `validate_content_delivery` を実装 | 典型的なフロー: ``` 1. ブランドがガバナンスエージェントでスタンダードを設定(オーケストレーター経由) 2. オーケストレーターが get_products/create_media_buy に standards_ref を含めて送信 3. セラーが能力に応じて受諾または拒否 4. セラーがガバナンスエージェントとキャリブレーション 5. セラーが配信時にスタンダードを適用 6. セラーがアーティファクトを提供(webhook でのプッシュ、または get_media_buy_artifacts でのプル) 7. オーケストレーターがアーティファクトをガバナンスエージェントへ転送して検証 ``` *** ## Sales Agent 向け Sales Agent(パブリッシャー Ad サーバー、SSP 等)は、オーケストレーターのポリシーを受け入れ配信時に適用します。 ### コアモデル オーケストレーターが `content_standards_ref` を含めてきたら、次を行います: 1. ガバナンスエージェントからスタンダードを **取得し**、実行可能かを評価します 2. 能力に応じて買い付けを **受諾または拒否** します 3. ガバナンスエージェントの期待に合わせて評価モデルを **キャリブレーション** します 4. 配信時にスタンダードを **適用** します 5. 検証用にオーケストレーターへ **アーティファクトを提供** します 要件を満たせない場合は **買い付けを拒否** してください。順守できないキャンペーンを受けないこと。 ### 実装すべきこと **1. `get_products` と `create_media_buy` で content standards の参照を受ける** オーケストレーターは参照経由でスタンダードを渡します: ```json theme={null} { "content_standards_ref": { "standards_id": "nike_emea_brand_safety", "agent_url": "https://brandsafety.ias.com" } } ``` 受け取ったら: * `agent_url` のガバナンスエージェントからスタンダード文書を取得します * 要件を満たせるか評価します * 満たせない場合はリクエストを拒否します * 満たせる場合は受諾し、メディアバイとの関連付けを保存します **2. 実行可能か判断する** スタンダード文書には次が含まれます: * Policy(許容/非許容コンテンツの自然言語記述) * Calibration exemplars(エッジケース解釈のための合否例) * Floor(外部基準となるベースラインセーフティスタンダードへの参照) 要件と自社の能力を突き合わせます。パブリッシャーごとに「adjacency」の定義は異なります(Reddit はコメント、YouTube は関連動画、ニュースサイトは記事本文など)。ブランド意図を実質的に担保できるなら受け入れて問題ありません。 対応できない場合(例: 屋外など adjacency が成立しないチャネルを要求された場合)は拒否します。 **3. 評価機構を構築する** スタンダード文書を使ってコンテンツ評価システムをトレーニングまたは設定します。方法は: * ルールをシステムプロンプトとした LLM * キャリブレーション例でトレーニングしたクラシファイア * 確定的評価のためのルールエンジン * サードパーティのブランド適合性ベンダー プロトコルは実装方法を規定しません。スタンダードを順守することが求められます。 **4. ガバナンスエージェントとキャリブレーションする** 買い付け受諾後、ガバナンスエージェントの `calibrate_content` を呼び出してローカルモデルをキャリブレーションします。自社インベントリのサンプルアーティファクトを送ると、ガバナンスエージェントがどう評価するかを返してくれます: ```json theme={null} // インベントリのサンプルをガバナンスエージェントへ送信 { "standards_id": "nike_emea_brand_safety", "artifacts": [ { "property_id": { "type": "domain", "value": "espn.com" }, "artifact_id": "article_123", "assets": [{ "type": "text", "role": "title", "content": "Marathon Runner Collapses at Finish Line" }] } ] } // ガバナンスエージェントが解釈を返す { "evaluations": [{ "artifact_id": "article_123", "suitable": true, "confidence": 0.9, "explanation": "Sports injury coverage in athletic context - aligns with brand's sports marketing positioning" }] } ``` 返却結果を使ってローカルモデルを調整します。判定に異議があれば理由を確認し、追加質問で擦り合わせます。 **5. アーティファクトをオーケストレーターへ送る** 配信後、ガバナンスエージェントによる検証ができるようアーティファクトをオーケストレーターへプッシュします。メディアバイの `artifact_webhook` で設定します: ```json theme={null} // アーティファクト webhook ペイロード(オーケストレーターへ送信) { "media_buy_id": "mb_nike_reddit_q1", "batch_id": "batch_20250115_001", "timestamp": "2025-01-15T11:00:00Z", "artifacts": [ { "artifact": { "property_id": { "type": "domain", "value": "reddit.com" }, "artifact_id": "r_fitness_abc123", "assets": [{ "type": "text", "role": "title", "content": "Best protein sources" }] }, "delivered_at": "2025-01-15T10:30:00Z", "impression_id": "imp_abc123" } ] } ``` ポーリングを好むオーケストレーター向けに `get_media_buy_artifacts` もサポートします。 ### 実装チェックリスト * [ ] `get_products` と `create_media_buy` で `content_standards_ref` をパース * [ ] ガバナンスエージェントからスタンダード文書を取得して評価 * [ ] 実行できない買い付けは拒否 — 順守できないキャンペーンを受け入れない * [ ] スタンダード文書に基づくコンテンツ評価を構築 * [ ] ガバナンスエージェントの `calibrate_content` を呼び出して解釈を合わせる * [ ] オーケストレーターが検証用コンテンツを取得できるよう `get_media_buy_artifacts` を実装 * [ ] プッシュ型アーティファクト配信のために `artifact_webhook` をサポート * [ ] 配信メトリクスのために `reporting_webhook` をサポート *** ## オーケストレーター向け オーケストレーター(DSP、トレーディングデスク、代理店プラットフォーム)は、ブランド・ガバナンスエージェント・パブリッシャー間でコンテンツスタンダードを調整します。 ### オーケストレーションパターン ``` Brand → Orchestrator → Governance Agent (setup) → Sales Agent (buying) ← Sales Agent (artifacts) → Governance Agent (validation) → Brand (reporting) ``` **1. ブランドがガバナンスエージェントでスタンダードを設定するのを支援** ブランドはガバナンスエージェントを通じてコンテンツスタンダードを作成します。あなたが仲介するか、ブランドが直接行う場合があります: ```json theme={null} // ガバナンスエージェントに保存されるスタンダード { "standards_id": "nike_emea_brand_safety", "name": "Nike EMEA Brand Suitability Policy", "brand_id": "nike", "policy": "Sports and fitness content is ideal. Avoid violence, adult themes, drugs.", "calibration_exemplars": { "pass": [ { "type": "url", "value": "https://espn.com/nba/story/_/id/12345/lakers-win", "language": "en" } ], "fail": [ { "type": "url", "value": "https://tabloid.example.com/celebrity-scandal", "language": "en" } ] } } ``` #### 散文ポリシーをアドレス可能なエントリへ移行する 既存の散文ポリシーを持つブランドには2つの選択肢があります。すべてを散文ブロブ全体を持つ1つのポリシーエントリとして保持するか、ルールごとに1つのエントリに分割するかです。どちらも検証されます。 **散文ありの単一エントリ**(最も簡単、既存のオーサリングを保持): ```json theme={null} "policies": [ { "policy_id": "acme_creative_standards_v1", "enforcement": "must", "policy": "No violent imagery. Minimum 72 DPI for display assets. Brand colors must match palette within 5% tolerance." } ] ``` **複数のアドレス可能なエントリ**(プログラム的な修正・再試行ループとルールごとのバージョニングに推奨): ```json theme={null} "policies": [ { "policy_id": "no_violent_imagery", "policy_categories": ["brand_safety"], "enforcement": "must", "policy": "No violent imagery." }, { "policy_id": "min_display_dpi", "policy_categories": ["imagery_quality"], "enforcement": "should", "channels": ["display"], "policy": "Minimum 72 DPI for display assets." }, { "policy_id": "brand_color_tolerance","policy_categories": ["brand_compliance"], "enforcement": "must", "policy": "Brand colors must match palette within 5% tolerance." } ] ``` 分割により、検出事項が特定のルールを(`policy_id` 経由で)参照でき、プログラム的な修正・再試行、およびバージョン間で安定した id が可能になります。ワークフローに合ったオーサリングモードを使ってください。 #### レジストリポリシー + ビスポークポリシー `registry_policy_ids` と `policies` は同時に提供できます。評価器はそのすべてを適用します。ビスポークな `policy_id` 値はフラット(コロンやスラッシュなし)でなければなりません(MUST)。レジストリのポリシー id は常に名前空間付き(例: `garm:brand_safety:violence`)であるため、2つの名前空間が衝突することはありません。ガバナンスの検出事項が特定のポリシーを参照する場合、検出事項の `policy_id` はレジストリ id かビスポーク id のいずれかを運びます — 名前空間がどちらかを示します。 **2. 購入時にスタンダード参照を渡す** プロダクト探索やメディアバイ作成時に、ガバナンスエージェント参照を含めます: ```json theme={null} { "product_id": "espn_sports_display", "packages": [...], "content_standards_ref": { "standards_id": "nike_emea_brand_safety", "agent_url": "https://brandsafety.ias.com" }, "artifact_webhook": { "url": "https://your-platform.com/webhooks/artifacts", "authentication": { "schemes": ["HMAC-SHA256"], "credentials": "your-shared-secret-min-32-chars" }, "delivery_mode": "batched", "batch_frequency": "hourly", "sampling_rate": 0.25 } } ``` パブリッシャーが基準を満たせない場合は買い付けを拒否します。拒否を適切に扱い、代替在庫を探してください。 **3. Sales Agent からアーティファクトを受け取る** Sales Agent は `artifact_webhook` にアーティファクトをプッシュします。これをガバナンスエージェントに転送して検証します: ```python theme={null} # Sales Agent からの artifact webhook を受信 @app.post("/webhooks/artifacts") async def receive_artifacts(payload: ArtifactWebhookPayload): # ガバナンスエージェントへ転送して検証 validation_result = await governance_agent.validate_content_delivery( standards_id=get_standards_id(payload.media_buy_id), records=[ {"artifact": a.artifact, "record_id": a.impression_id} for a in payload.artifacts ] ) # 失敗を記録 for result in validation_result.results: if any(f.status == "failed" for f in result.features): log_suitability_incident(payload.media_buy_id, result) return {"status": "received", "batch_id": payload.batch_id} ``` **4. ブランドへ報告** 検証結果をブランドに提示します: * **Incidents**: 基準を満たさなかったコンテンツ * **Coverage**: 配信のうち検証された割合 * **Trends**: コンテンツセーフティの経時変化 ### 実装チェックリスト * [ ] ガバナンスエージェントでのブランドセットアップを支援 * [ ] `get_products` と `create_media_buy` リクエストに `content_standards_ref` を含めます * [ ] Sales Agent からアーティファクトを受信するために `artifact_webhook` を設定 * [ ] スタンダードを満たせないパブリッシャーからの拒否を処理 * [ ] `validate_content_delivery` を通じてガバナンスエージェントへアーティファクトを転送 * [ ] ブランド向けレポートを構築 *** ## ガバナンスエージェント向け ガバナンスエージェント(IAS、DoubleVerify など)はコンテンツ評価をサービスとして提供します。 ### 実装するもの **1. コンテンツスタンダードをホストし提供する** スタンダード設定を保存し、`get_content_standards` で公開します: ```json theme={null} // get_content_standards へのレスポンス { "standards_id": "nike_emea_brand_safety", "version": "1.2.0", "name": "Nike EMEA - all digital channels", "policy": "Sports and fitness content is ideal. Lifestyle content about health is good...", "calibration_exemplars": { "pass": [...], "fail": [...] } } ``` **2. `calibrate_content` を実装する** Sales Agent はキャンペーン実行前にローカルモデルを合わせるためにこれを呼び出します。サンプルアーティファクトを送り、ブランドがどう評価するかを返します: ```python theme={null} def calibrate_content(standards_id: str, artifacts: list) -> dict: standards = get_standards(standards_id) evaluations = [] for artifact in artifacts: # ブランドのポリシーに照らして評価 result = evaluate_against_policy(artifact, standards) evaluations.append({ "artifact_id": artifact["artifact_id"], "suitable": result.suitable, "confidence": result.confidence, "explanation": result.explanation # 判断理由を伝える }) return {"evaluations": evaluations} ``` キャリブレーションは対話的です。追加質問やエッジケースに対応できるようにしてください。 **3. `validate_content_delivery` を実装する** オーケストレーターが配信後にアーティファクトを検証するために呼び出します。大規模なバッチ評価: ```python theme={null} def validate_content_delivery(standards_id: str, records: list) -> dict: standards = get_standards(standards_id) results = [] for record in records: features = [] for feature in ["brand_safety", "brand_suitability"]: evaluation = evaluate_feature(record["artifact"], standards, feature) features.append({ "feature_id": feature, "status": "passed" if evaluation.passed else "failed", "value": evaluation.value, "message": evaluation.message if not evaluation.passed else None }) results.append({ "record_id": record["record_id"], "features": features }) return { "summary": compute_summary(results), "results": results } ``` ### 実装チェックリスト * [ ] ブランドがポリシーを設定するために `create_content_standards` を実装 * [ ] Sales Agent がポリシーを取得するために `get_content_standards` を実装 * [ ] Sales Agent がモデルを合わせるために `calibrate_content` を実装 * [ ] オーケストレーターが配信を検証するために `validate_content_delivery` を実装 * [ ] キャリブレーションでの対話をサポート(追加質問、エッジケース) *** ## Content Access Pattern 3 つの役割すべてでコンテンツを安全にやり取りする必要がある場合があります。`content_access` パターンは URL 名前空間への認証付きアクセスを提供します: ```json theme={null} { "content_access": { "url_pattern": "https://cache.example.com/*", "auth": { "type": "bearer", "token": "eyJ..." } } } ``` * **url\_pattern**: このパターンにマッチする URL にこの認証を使用 * **auth.type**: 認証方式(`bearer`, `api_key`, `signed_url`) * **auth.token**: 認証情報 以下に含めます: * `get_content_standards` レスポンス(ガバナンスエージェント → Sales Agent: 「ここから例を取得してください」) * `get_media_buy_artifacts` レスポンス(Sales Agent → オーケストレーター: 「ここからコンテンツを取得してください」) これによりアセット単位のトークンを避け、安全なコンテンツ交換を可能にしながらペイロードを小さく保てます。 # Content Standards Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/content-standards/index AdCP Content Standards は、パブリッシャーのインフラから外に出せない AI 生成コンテンツや一時的コンテンツに対してプライバシーを守りつつブランド適合性評価を可能にします。 # Content Standards Protocol Content Standards プロトコルは、パブリッシャー環境から外に出せない一時的・機微なコンテンツに対して **プライバシーを守りつつブランドセーフティを実現** します。 ## 課題 従来のブランドセーフティは第三者検証が前提で、コンテンツを IAS や DoubleVerify に送って判定を受けます。静的な Web ページでは機能しますが、次のようなケースでは根本的に機能しません: * **AI-generated content** - ChatGPT responses, DALL-E images that exist only in a user session * **Private conversations** - Content in messaging apps, private social feeds * **Ephemeral content** - Stories, live streams, real-time feeds that disappear * **Privacy-regulated content** - GDPR-protected data that cannot be exported * **CTV and linear TV** - Ad decisioning in live linear has a sub-second latency budget; there is no room to make a blocking call to a third-party verification service before insertion こうしたプラットフォームでは **従来型の検証手段が存在しません**。コンテンツを外に出せない、または待てないためです。OpenAI はユーザー会話を外部に送れず、メッセージアプリはプライベートチャットをエクスポートできず、配信プラットフォームは消える前のリアルタイムコンテンツを共有できません。 Yet these are exactly the environments where advertising is growing fastest - and where brands most need safety guarantees. Without a privacy-preserving approach, brands either avoid these channels entirely or accept unknown risk. ## 解決策: キャリブレーションによる整合 Content Standards は **エージェントを使ってプライバシーを守る** ことで解決します。機微なコンテンツを外に出さない 3 フェーズモデルです: | Phase | Where It Runs | What Happens | | ---------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | **1. Calibration** | External (safe data only) | Publisher and verification agent align on policy interpretation using synthetic examples or public samples - no PII, no sensitive content | | **2. Local Execution** | Inside publisher's walls | Publisher runs evaluation on every impression using a local model trained during calibration - content never leaves | | **3. Validation** | Statistical sampling | Verification agent audits a sample to detect drift - both parties can verify the system is working without exposing PII | これは従来モデルを逆転させたものです。「コンテンツを送って判定してもらう」のではなく、「基準を共有し、ローカルで評価し、統計的に監査する」形です。 **ポイント**: 実行エンジンはパブリッシャー内部で完結します。OpenAI なら社内でブランド適合性判定を行い、会話は外に出ません。メッセージアプリでもプライベートコンテンツは外部に出ません。キャリブレーションとバリデーションが、機微データに触れずにローカルモデルの正しさを担保します。 **セラーはローカル実行の実装方法を完全に制御できます。** パブリッシャーはサードパーティの AI ベンダーを使ったり、カスタムモデルを構築したり、ルールベースの分類器を使ったり、スポットチェック規模の人的編集レビューを適用したりできます。重要なのは、実装が検証エージェントの標準に対してキャリブレーションされており、バリデーションサンプルが整合性を確認できることであって、セラーがどのようにそれを達成するかはプロトコルが強制しません。 ## 対象範囲 * **Brand safety** - Is this content safe for *any* brand? (universal thresholds like hate speech, illegal content) * **Brand suitability** - Is this content appropriate for *my* brand? (brand-specific preferences and tone) ## 主要な考え方 コンテンツ評価では、バイヤーとセラーが次の 4 点をすり合わせます: 1. **What content?** - What [artifacts](./artifacts) to evaluate (the ad-adjacent content) 2. **How much adjacency?** - How many artifacts around the ad slot to consider 3. **What sampling rate?** - What percentage of traffic to evaluate 4. **How to calibrate?** - How to align on policy interpretation before runtime これらのパラメータはプロダクト探索やメディアバイ作成時に協議されます。 ## ワークフロー ```mermaid theme={null} sequenceDiagram participant Brand participant Buyer as Buyer Agent participant Seller as Seller Agent participant Verifier as Verification Agent Note over Brand,Verifier: 1. SETUP PHASE Brand->>Verifier: create_content_standards (policy + calibration examples) Verifier-->>Brand: standards_id Note over Brand,Verifier: 2. ACTIVATION PHASE Brand->>Buyer: "Buy inventory from Reddit, use standards_id X" Buyer->>Seller: create_media_buy (includes content_standards reference) Seller->>Verifier: calibrate_content (sample artifacts) Verifier-->>Seller: verdict + explanation Seller->>Verifier: "What about this edge case?" Verifier-->>Seller: clarification Note over Seller: Seller builds local model Note over Brand,Verifier: 3. RUNTIME PHASE loop High-volume decisioning Note over Seller: Local model evaluates artifacts end Buyer->>Seller: get_media_buy_artifacts (sampled) Seller-->>Buyer: Content artifacts Buyer->>Verifier: validate_content_delivery Verifier-->>Buyer: Validation results ``` **ポイント**: 実行時の判定はスケールのためセラー側ローカルで行われます。バイヤーはサンプルを引き出し、検証エージェントで検証します。 ## 隣接範囲(Adjacency) How much content around the ad slot should be evaluated? | Context | Adjacency Examples | | ------------------- | ----------------------------------------- | | **News article** | The article where the ad appears | | **Social feed** | 1-2 posts above and below the ad slot | | **Podcast** | The segment before and after the ad break | | **CTV** | 1-2 scenes before and after the ad pod | | **Infinite scroll** | Posts within the visible viewport | Adjacency 要件はセラーがプロダクトカタログ(`get_products`)で定義します。バイヤーはこの保証をもとにプロダクトをフィルターできます: ```json theme={null} { "product_id": "reddit_feed_standard", "content_standards_adjacency_definition": { "before": 2, "after": 2, "unit": "posts" } } ``` ### Adjacency の単位 | Unit | Use Case | | ----------- | ------------------------------------- | | `posts` | Social feeds, forums, comment threads | | `scenes` | CTV, streaming video content | | `segments` | Podcasts, audio content | | `seconds` | Time-based adjacency in video/audio | | `viewports` | Infinite scroll contexts | | `articles` | News sites, content aggregators | プロダクトごとに価格に応じた異なる Adjacency 保証を提供する場合があります。 ## サンプリング率 What percentage of traffic should be evaluated by the verification agent? | Rate | Use Case | | ---------- | ------------------------------------------------------ | | **100%** | Premium brand suitability - every impression validated | | **10-25%** | Standard monitoring - statistical confidence | | **1-5%** | Spot checking - drift detection only | サンプリング率はメディアバイで合意します: ```json theme={null} { "governance": { "content_standards": { "agent_url": "https://safety.ias.com/adcp", "standards_id": "nike_brand_safety", "sampling_rate": 0.25 } } } ``` サンプリング率が高いほどコストは上がりますが、保証が強まります。セラーは合意したサンプリング率を実装し、実際のカバレッジを報告する責任があります。 ## 検証しきい値 When a seller calibrates their local model against a verification agent, there's an expected drift - the local model won't match the verification agent 100% of the time. **Validation thresholds** define acceptable drift between local execution and validation samples. Sellers advertise their content safety capabilities in their product catalog: ```json theme={null} { "product_id": "reddit_feed_premium", "content_standards": { "validation_threshold": 0.95, "validation_threshold_description": "Local model matches verification agent 95% of the time" } } ``` | Threshold | Meaning | | --------- | ------------------------------------------------------------ | | **0.99** | Premium - local model is 99% aligned with verification agent | | **0.95** | Standard - local model is 95% aligned | | **0.90** | Budget - local model is 90% aligned | **これは契約上の保証です。** 広告したしきい値よりドリフトが大きい場合、通常の配信乖離と同様に是正(メイクグッド、返金など)が期待されます。 このしきい値は「ローカルモデルを受け入れたとき、どれだけ基準順守を信頼できるか」というバイヤーの問いに答えます。 ## ポリシー Content Standards uses **natural language prompts** rather than rigid keyword lists: ```json theme={null} { "policy": "Sports and fitness content is ideal. Lifestyle content about health is good. Entertainment is generally acceptable. Avoid content about violence, controversial politics, adult themes, or content portraying sedentary lifestyle positively. Block hate speech, illegal activities, or ongoing litigation against our company.", "calibration_exemplars": { "pass": [ { "property_id": {"type": "domain", "value": "espn.com"}, "artifact_id": "nba_championship_recap_2024", "assets": [{"type": "text", "role": "title", "content": "Championship Game Recap"}] } ], "fail": [ { "property_id": {"type": "domain", "value": "tabloid.example.com"}, "artifact_id": "scandal_story_123", "assets": [{"type": "text", "role": "title", "content": "Celebrity Scandal Exposed"}] } ] } } ``` The policy prompt enables AI-powered verification agents to understand context and nuance. **Calibration** examples provide a training/test set that helps the agent interpret the policy correctly. See [Artifacts](./artifacts) for details on artifact structure and secured asset access. ## Scoped Standards Buyers typically maintain multiple standards configurations for different contexts - UK TV campaigns have different regulations than US display, and children's brands need stricter safety than adult beverages. ```json theme={null} { "standards_id": "uk_tv_zero_calorie", "name": "UK TV - zero-calorie brands", "countries_all": ["GB"], "channels_any": ["ctv", "linear_tv"], "languages_any": ["en"] } ``` **Code Format Conventions** Country and language codes are **case-insensitive** - implementations must normalize before comparison. Recommended formats follow ISO standards: * **Countries**: Uppercase ISO 3166-1 alpha-2 (e.g., `GB`, `US`, `DE`) * **Languages**: Lowercase ISO 639-1 or BCP 47 (e.g., `en`, `de`, `fr`) **The buyer selects the appropriate `standards_id` when creating a media buy.** The seller receives a reference to the resolved standards - they don't need to do scope matching themselves. ## Calibration Before running campaigns, sellers calibrate their local models against the verification agent. This is a **dialogue-based process** that may involve human review on either side: 1. Seller sends sample artifacts to the verification agent 2. Verification agent returns verdicts with detailed explanations 3. Seller asks follow-up questions about edge cases 4. Process repeats until alignment is achieved **Human-in-the-loop**: Calibration often involves humans on both sides. A brand suitability specialist at the buyer might review edge cases flagged by the verification agent. A content operations team at the seller might curate calibration samples and validate the local model's learning. The protocol supports async workflows where either party can pause for human review before responding. ```json theme={null} // Seller: "Does this pass?" { "artifact": { "property_id": {"type": "domain", "value": "reddit.com"}, "artifact_id": "r_news_politics_123", "assets": [{"type": "text", "role": "title", "content": "Political News Article"}] } } // Verification agent: "No, because..." { "verdict": "fail", "explanation": "Political content is excluded by brand policy, even when balanced.", "features": [ { "feature_id": "brand_safety", "status": "passed", "explanation": "No hate speech, illegal content, or explicit material." }, { "feature_id": "brand_suitability", "status": "failed", "explanation": "Political content is excluded by brand policy, even when balanced." } ] } ``` 判定は**読みやすく監査可能**で、不透明なスコアではありません。トップレベルの `explanation` とフィーチャーごとの `features[].explanation` フィールドにより、どのポリシー条項がトリガーされ、なぜかをセラーに正確に伝えます。これによりパブリッシャーは判定を理解し、エッジケースに異議を唱え、コンテンツ分類方法を調整できます。バイヤーは判定サンプルを監査して、基準が正しく解釈されているか検証できます。 See [calibrate\_content](./tasks/calibrate_content) for the full task specification. ## Tasks ### Discovery | Task | Description | | ---------------------------------------------------------- | ------------------------------------------- | | [list\_content\_standards](./tasks/list_content_standards) | List available standards configurations | | [get\_content\_standards](./tasks/get_content_standards) | Retrieve a specific standards configuration | ### Management | Task | Description | | -------------------------------------------------------------- | ------------------------------------------ | | [create\_content\_standards](./tasks/create_content_standards) | Create a new standards configuration | | [update\_content\_standards](./tasks/update_content_standards) | Update an existing standards configuration | ### Calibration & Validation | Task | Description | | ---------------------------------------------------------------- | -------------------------------------------------------- | | [calibrate\_content](./tasks/calibrate_content) | Collaborative dialogue to align on policy interpretation | | [get\_media\_buy\_artifacts](./tasks/get_media_buy_artifacts) | Retrieve content artifacts from a media buy | | [validate\_content\_delivery](./tasks/validate_content_delivery) | Batch validation of content artifacts | ## Typical Providers * **IAS** - Integral Ad Science * **DoubleVerify** - Brand safety and verification * **Scope3** - Sustainability-focused brand safety with prompt-based policies * **Custom** - Brand-specific implementations ## Future: Secure Enclaves The current model trusts the publisher to faithfully implement the calibrated standards. A future evolution uses **secure enclaves** (Trusted Execution Environments / TEEs) to provide cryptographic guarantees: ```mermaid theme={null} flowchart TB subgraph VS["Verification Service"] Models["Models & Calibration Data"] Results["Aggregate Results"] end subgraph PUB["Publisher Infrastructure"] subgraph TEE["Secure Enclave (TEE)"] Agent["Containerized
Governance Agent"] end Content["Content Artifacts"] end Models -->|"Pinhole IN:
models, policy, examples"| Agent Agent -->|"Pinhole OUT:
pass rates, drift metrics"| Results Content -->|"evaluate"| Agent Agent -->|"pass/fail verdict"| Content style TEE fill:#e8f5e9,stroke:#4caf50 style Agent fill:#c8e6c9,stroke:#388e3c style PUB fill:#fafafa,stroke:#9e9e9e ``` **Content never crosses the pinhole** - only models flow in, only aggregates flow out. ### The Pinhole Interface The enclave maintains a narrow, well-defined interface to the verification service: **Inbound (verification service → enclave):** * Updated brand safety models * Policy changes and calibration exemplars * Configuration updates **Outbound (enclave → verification service):** * Aggregated validation results (pass rates, drift metrics) * Statistical summaries * Attestation proofs **Never crosses the boundary:** * Raw content artifacts * User data or PII * Individual impression-level data This pinhole is the interface that needs standardization - it defines exactly what flows in and out while keeping sensitive content locked inside the publisher's walls. ### Why This Matters * **Publisher** hosts a secure enclave inside their infrastructure * **Governance agent** (from IAS, DoubleVerify, etc.) runs as a container within the enclave * **Content** flows into the enclave for evaluation but never leaves the publisher's walls * **Both parties** can verify the governance code is running unmodified via attestation * **Models stay current** - the enclave can receive updates without exposing content This provides the same privacy guarantees as local execution, but with cryptographic proof that the correct algorithm is running. The brand knows their standards are being enforced faithfully. The publisher proves compliance without exposing content. This architecture aligns with the [IAB Tech Lab ARTF (Agentic RTB Framework)](https://iabtechlab.com/standards/artf/), which defines how service providers can package offerings as containers deployed into host infrastructure. ARTF enables hosts to "provide greater access to data and more interaction opportunities to service agents without concerns about leakage, misappropriation or latency" - exactly the model Content Standards requires for privacy-preserving brand safety. ## Related * [Artifacts](./artifacts) - What artifacts are and how to structure them * [ブランドアイデンティティ](/docs/brand-protocol/brand-json) - 標準エージェントにリンクできるブランドアイデンティティ # calibrate_content Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/content-standards/tasks/calibrate_content calibrate_content は AdCP キャンペーン実行前にバイヤーとセラーがブランド適合性スタンダードを構造化対話で擦り合わせるタスクです。 # calibrate\_content コンテンツスタンダードの解釈を擦り合わせる協働キャリブレーションタスクです。セットアップ時に、配信前のセラーがバイヤーのポリシーを理解・内製化するのに使います。 大量の実行時評価とは異なり、キャリブレーションは **対話ベース** で例と説明をやり取りし、合意に至るまで繰り返します。 ## いつ使うか * **Seller onboarding**: セラーが初めてバイヤーからコンテンツスタンダードを受け取るとき * **Policy clarification**: 特定コンテンツが合否になる理由を理解したいとき * **Model training**: スタンダードに沿うローカルモデルを構築するとき * **Drift detection**: 定期的に再キャリブレーションして整合を保つとき ## Request **Schema**: [calibrate-content-request.json](https://adcontextprotocol.org/schemas/v3/content-standards/calibrate-content-request.json) | Parameter | Type | Required | Description | | -------------- | -------- | -------- | -------------------- | | `standards_id` | string | Yes | キャリブレーション対象のスタンダード設定 | | `artifact` | artifact | Yes | 評価するアーティファクト | ### Artifact **Schema**: [artifact.json](https://adcontextprotocol.org/schemas/v3/content-standards/artifact.json) An artifact represents content context where ad placements occur - identified by `property_id` + `artifact_id` and represented as a collection of assets: ```json theme={null} { "$schema": "/schemas/content-standards/artifact.json", "property_id": {"type": "domain", "value": "reddit.com"}, "artifact_id": "r_fitness_abc123", "assets": [ {"type": "text", "role": "title", "content": "Best protein sources for muscle building", "language": "en"}, {"type": "text", "role": "paragraph", "content": "Looking for recommendations on high-quality protein sources...", "language": "en"}, {"type": "text", "role": "paragraph", "content": "I've been lifting for 6 months and want to optimize my diet.", "language": "en"}, {"type": "image", "url": "https://cdn.reddit.com/fitness-image.jpg", "alt_text": "Person lifting weights"} ] } ``` ## Response **Schema**: [calibrate-content-response.json](https://adcontextprotocol.org/schemas/v3/content-standards/calibrate-content-response.json) ### パスした場合のレスポンス ```json theme={null} { "$schema": "/schemas/content-standards/calibrate-content-response.json", "verdict": "pass", "explanation": "This content aligns well with the brand's fitness-focused positioning. Health and fitness content is explicitly marked as 'ideal' in the policy. The discussion is constructive and educational.", "features": [ { "feature_id": "brand_safety", "status": "passed", "explanation": "No safety concerns. Content is user-generated but constructive fitness discussion." }, { "feature_id": "brand_suitability", "status": "passed", "explanation": "Fitness content matches brand's athletic positioning." } ] } ``` ### 失敗した場合のレスポンス(詳細付き) ```json theme={null} { "$schema": "/schemas/content-standards/calibrate-content-response.json", "verdict": "fail", "explanation": "This content discusses political topics which the policy explicitly excludes. While the article itself is balanced journalism, the brand has requested to avoid all controversial political content regardless of tone.", "features": [ { "feature_id": "brand_safety", "status": "passed", "explanation": "No hate speech, illegal content, or explicit material." }, { "feature_id": "brand_suitability", "status": "failed", "explanation": "Political content is excluded by brand policy, even when balanced." } ] } ``` ### Response Fields | Field | Required | Description | | ------------- | -------- | ----------------------------------------------------- | | `verdict` | Yes | Overall `pass` or `fail` decision | | `explanation` | No | Detailed natural language explanation of the decision | | `features` | No | Per-feature breakdown with explanations | | `confidence` | No | Model confidence in the verdict (0-1), when available | ## Dialogue Flow Calibration supports back-and-forth dialogue using the protocol's conversation management. The seller sends content, the verification agent responds with an evaluation and explanation, and the seller can respond with questions or try different content - all within the same conversation context. ### A2A Example ```javascript theme={null} // Seller sends artifact to evaluate const response1 = await a2a.send({ message: { parts: [{ kind: "data", data: { skill: "calibrate_content", parameters: { standards_id: "nike_brand_safety", artifact: { property_id: { type: "domain", value: "reddit.com" }, artifact_id: "r_news_politics_123", assets: [ { type: "text", role: "title", content: "Political News Article" } ] } } } }] } }); // Response: verdict=fail with feature breakdown // Seller asks follow-up question about the decision const response2 = await a2a.send({ contextId: response1.contextId, message: { parts: [{ kind: "text", text: "This is factual news, not opinion. Should balanced journalism be excluded?" }] } }); // Verification agent clarifies that brand policy excludes ALL political content // Seller tries different artifact const response3 = await a2a.send({ contextId: response1.contextId, message: { parts: [{ kind: "data", data: { skill: "calibrate_content", parameters: { standards_id: "nike_brand_safety", artifact: { property_id: { type: "domain", value: "reddit.com" }, artifact_id: "r_running_tips_456", assets: [ { type: "text", role: "title", content: "Running Tips" } ] } } } }] } }); // Response: verdict=pass - now seller understands the boundaries ``` ### MCP Example ```javascript theme={null} // Initial calibration request const response1 = await mcp.call('calibrate_content', { standards_id: "nike_brand_safety", artifact: { property_id: { type: "domain", value: "reddit.com" }, artifact_id: "r_news_politics_123", assets: [ { type: "text", role: "title", content: "Political News Article" } ] } }); // Response includes context_id for conversation continuity // Continue dialogue with follow-up question const response2 = await mcp.call('calibrate_content', { context_id: response1.context_id, standards_id: "nike_brand_safety", artifact: { property_id: { type: "domain", value: "reddit.com" }, artifact_id: "r_news_politics_123", assets: [ { type: "text", role: "title", content: "Political News Article" } ] } }); // Include text message in the protocol envelope asking about balanced journalism // Try different artifact in same conversation const response3 = await mcp.call('calibrate_content', { context_id: response1.context_id, standards_id: "nike_brand_safety", artifact: { property_id: { type: "domain", value: "reddit.com" }, artifact_id: "r_running_tips_456", assets: [ { type: "text", role: "title", content: "Running Tips" } ] } }); ``` The key insight is that the dialogue happens at the **protocol layer**, not the task layer. The verification agent maintains conversation context and can respond to follow-up questions, disagreements, or requests for clarification - just like any agent-to-agent conversation. ## Calibration vs Runtime | Aspect | calibrate\_content | Runtime (local model) | | ------------ | ------------------------- | ----------------------- | | **Purpose** | Alignment & understanding | High-volume decisioning | | **Volume** | Low (setup/periodic) | High (every impression) | | **Response** | Verbose explanations | Pass/fail only | | **Latency** | Seconds acceptable | Milliseconds required | | **Dialogue** | Multi-turn conversation | Stateless | ## Related Tasks * [get\_content\_standards](./get_content_standards) - Retrieve the policies being calibrated against * [validate\_content\_delivery](./validate_content_delivery) - Post-campaign delivery validation # create_content_standards Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/content-standards/tasks/create_content_standards create_content_standards は AdCP キャンペーン向けにコンテンツポリシー、リスクしきい値、カテゴリルールを含むブランド適合性設定を定義します。 # create\_content\_standards 新しいコンテンツスタンダード設定を作成します。 **レスポンスタイム**: \< 1s ## Request **Schema**: [create-content-standards-request.json](https://adcontextprotocol.org/schemas/v3/content-standards/create-content-standards-request.json) | Parameter | Type | Required | Description | | ----------------------- | ------ | -------- | ----------------------------------------- | | `scope` | object | Yes | このスタンダードが適用される範囲(`languages_any` を含む必要あり) | | `policy` | string | Yes | 自然言語のポリシープロンプト | | `calibration_exemplars` | object | No | キャリブレーション用の pass/fail アーティファクト集合 | **ブランドセーフティの最低基準について** ポリシー内容にかかわらずブランドセーフティの最低基準を適用する必要があります(ヘイトスピーチ、違法コンテンツなどは、スタンダード未指定でも除外)。AdCP は最低基準の仕様を定義せず、実装者や業界標準(例: GARM カテゴリ)に委ねます。 ### リクエスト例 ```json theme={null} { "$schema": "/schemas/content-standards/create-content-standards-request.json", "scope": { "countries_all": ["GB", "DE", "FR"], "channels_any": ["display", "olv", "ctv"], "languages_any": ["en", "de", "fr"], "description": "EMEA - all digital channels" }, "policy": "Sports and fitness content is ideal. Lifestyle content about health and wellness is good. Entertainment content is generally acceptable. Avoid content about violence, controversial political topics, adult themes, or content that portrays sedentary lifestyle positively.", "calibration_exemplars": { "pass": [ { "type": "url", "value": "https://espn.com/nba/story/_/id/12345/lakers-championship", "language": "en" }, { "type": "url", "value": "https://healthline.com/fitness/cardio-workout", "language": "en" } ], "fail": [ { "type": "url", "value": "https://tabloid.example.com/celebrity-scandal", "language": "en" }, { "type": "url", "value": "https://news.example.com/controversial-politics-article", "language": "en" } ] } } ``` ## Response **Schema**: [create-content-standards-response.json](https://adcontextprotocol.org/schemas/v3/content-standards/create-content-standards-response.json) ### 成功レスポンス ```json theme={null} { "$schema": "/schemas/content-standards/create-content-standards-response.json", "standards_id": "emea_digital_safety" } ``` ### エラーレスポンス **Scope Conflict:** ```json theme={null} { "errors": [ { "code": "SCOPE_CONFLICT", "message": "Standards already exist for country 'DE' on channel 'display'", "conflicting_standards_id": "emea_digital_safety" } ] } ``` ## スコープ競合の扱い Multiple standards cannot have overlapping scopes for the same country/channel/language combination. When creating standards that would conflict: 1. **既存スタンダードを確認** - スコープで絞って [list\_content\_standards](./list_content_standards) を参照 2. **新規ではなく更新** - 既存がある場合は [update\_content\_standards](./update_content_standards) 3. **スコープを狭める** - 国やチャネルを調整して重複を避ける ## Related Tasks * [list\_content\_standards](./list_content_standards) - すべての設定を列挙 * [update\_content\_standards](./update_content_standards) - 設定を更新 # get_content_standards Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/content-standards/tasks/get_content_standards get_content_standards は AdCP で特定のスタンダード ID に対するコンテンツセーフティポリシー設定を取得します。 # get\_content\_standards 特定のスタンダード設定についてコンテンツセーフティポリシーを取得します。 ## Request **Schema**: [get-content-standards-request.json](https://adcontextprotocol.org/schemas/v3/content-standards/get-content-standards-request.json) | Parameter | Type | Required | Description | | -------------- | ------ | -------- | ------------ | | `standards_id` | string | Yes | スタンダード設定の識別子 | ## Response **Schema**: [get-content-standards-response.json](https://adcontextprotocol.org/schemas/v3/content-standards/get-content-standards-response.json) ### Success Response ```json theme={null} { "standards_id": "emea_digital_safety", "name": "EMEA - all digital channels", "countries_all": ["GB", "DE", "FR"], "channels_any": ["display", "olv", "ctv"], "languages_any": ["en", "de", "fr"], "policy": "Sports and fitness content is ideal. Lifestyle content about health and wellness is good. Entertainment content is generally acceptable. Avoid content about violence, controversial political topics, adult themes, or content that portrays sedentary lifestyle positively. Block hate speech, illegal activities, or content disparaging athletes.", "calibration_exemplars": { "pass": [ { "type": "url", "value": "https://espn.com/nba/story/_/id/12345/lakers-championship", "language": "en" }, { "type": "url", "value": "https://healthline.com/fitness/cardio-workout", "language": "en" } ], "fail": [ { "type": "url", "value": "https://tabloid.example.com/celebrity-scandal", "language": "en" }, { "type": "url", "value": "https://news.example.com/controversial-politics-article", "language": "en" } ] } } ``` ### Fields | Field | Description | | ----------------------- | ------------------------------------------------------------ | | `standards_id` | このスタンダード設定の一意な識別子 | | `name` | 人が読める名称 | | `countries_all` | ISO 3166-1 alpha-2 国コード(大文字推奨、大小区別しない)- すべての listed 国に適用 | | `channels_any` | 広告チャネル - listed のいずれかに適用 | | `languages_any` | ISO 639-1 または BCP 47 言語タグ(小文字推奨、大小区別しない)- listed のいずれかの言語に適用 | | `policy` | 許容/非許容のコンテキストを記述する自然言語ポリシー | | `calibration_exemplars` | ポリシー解釈を合わせるための学習/テスト用コンテキスト(pass/fail) | **Brand Safety Floor Requirement** 実装者は、ポリシー内容にかかわらずブランドセーフティの最低基準を適用する必要があります(AdCP はこの仕様を規定しません)。 ### Error Response ```json theme={null} { "errors": [ { "code": "STANDARDS_NOT_FOUND", "message": "No standards found with ID 'invalid_id'" } ] } ``` ## Related Tasks * [calibrate\_content](./calibrate_content) - 本スタンダードに対する協働キャリブレーション * [list\_content\_standards](./list_content_standards) - 利用可能なスタンダード設定一覧 # get_media_buy_artifacts Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/content-standards/tasks/get_media_buy_artifacts get_media_buy_artifacts は AdCP でポストデリバリーのブランド適合性検証のためにメディアバイからコンテンツコンテキストレコードを取得します。 # get\_media\_buy\_artifacts 検証のため、メディアバイからコンテンツアーティファクトを取得します。パフォーマンス指標を返す `get_media_buy_delivery` とは別で、アーティファクトには広告が表示された実際のコンテンツ(テキスト・画像・動画)が含まれます。 **レスポンスタイム**: \< 5s(アーティファクト 1,000 件バッチ) ## Data Flow ```mermaid theme={null} sequenceDiagram participant Buyer as Buyer Agent participant Seller as Seller Agent participant Verifier as Verification Agent Buyer->>Seller: get_media_buy_artifacts (sampled or full) Seller-->>Buyer: Artifacts with content Buyer->>Verifier: validate_content_delivery Verifier-->>Buyer: Validation results ``` バイヤーはメディアバイと同じパラメータでセラーにアーティファクトを要求します。セラーは合意したサンプリング率に基づいてコンテンツサンプルを返し、バイヤーは検証エージェントで検証します。 ## Request **Schema**: [get-media-buy-artifacts-request.json](https://adcontextprotocol.org/schemas/v3/content-standards/get-media-buy-artifacts-request.json) | Parameter | Type | Required | Description | | --------------- | -------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `account` | [account-ref](/docs/building/by-layer/L2/accounts-and-agents#account-references) | No | アカウント参照。`{ "account_id": "..." }` または `{ "brand": {...}, "operator": "..." }` を渡す(セラーが暗黙的解決をサポートする場合)。このアカウントに属するメディアバイのアーティファクトのみ返します。省略時は全アクセス可能アカウントのアーティファクトを返します。 | | `media_buy_id` | string | Yes | 取得元のメディアバイ ID | | `package_ids` | array | No | 特定パッケージに絞り込み | | `failures_only` | boolean | No | セラーのローカルモデルが `local_verdict: "fail"` を返したアーティファクトのみを返します([unevaluated レコードでの挙動](#failures_only-and-unevaluated-records)を参照) | | `time_range` | object | No | 期間で絞り込み | | `pagination` | object | No | ページネーションパラメータ(後述) | **サンプリングは取得時ではなく購入作成時に設定されます**。サンプリングレート、メソッド、チャンネルごとの設定は、メディアバイの `governance.content_standards` 合意の一部です。`get_media_buy_artifacts` は、その合意に従ってセラーがすでに収集したアーティファクトを取得します。プッシュ型の配信には、`create_media_buy` で `artifact_webhook` を設定します。 ### Pagination アーティファクト結果セットは非常に大きくなり得るため、標準ページネーションよりも高い上限を使います。 | Parameter | Type | Default | Description | | ------------------------ | ------- | ------- | ------------------------------ | | `pagination.max_results` | integer | 1000 | 1 ページあたりの最大アーティファクト数(1〜10,000) | | `pagination.cursor` | string | - | 前のレスポンスから返された不透明なカーソル | ## Response **Schema**: [get-media-buy-artifacts-response.json](https://adcontextprotocol.org/schemas/v3/content-standards/get-media-buy-artifacts-response.json) ### Success Response ```json theme={null} { "$schema": "/schemas/content-standards/get-media-buy-artifacts-response.json", "media_buy_id": "mb_nike_reddit_q1", "artifacts": [ { "record_id": "imp_12345", "timestamp": "2025-01-15T10:30:00Z", "package_id": "pkg_feed_standard", "artifact": { "property_id": {"type": "domain", "value": "reddit.com"}, "artifact_id": "r_fitness_abc123", "assets": [ {"type": "text", "role": "title", "content": "Best protein sources for muscle building", "language": "en"}, {"type": "text", "role": "paragraph", "content": "Looking for recommendations on high-quality protein sources...", "language": "en"}, {"type": "image", "url": "https://cdn.reddit.com/fitness-image.jpg", "alt_text": "Person lifting weights"} ] }, "country": "US", "channel": "social", "brand_context": {"brand_id": "nike_global", "sku_id": "air_max_2025"}, "local_verdict": "pass" }, { "record_id": "imp_12346", "timestamp": "2025-01-15T10:35:00Z", "package_id": "pkg_feed_standard", "artifact": { "property_id": {"type": "domain", "value": "reddit.com"}, "artifact_id": "r_news_politics_456", "assets": [ {"type": "text", "role": "title", "content": "Election Results Analysis", "language": "en"}, {"type": "text", "role": "paragraph", "content": "The latest polling data shows...", "language": "en"} ] }, "country": "US", "channel": "social", "brand_context": {"brand_id": "nike_global", "sku_id": "air_max_2025"}, "local_verdict": "fail" } ], "sampling_info": { "total_deliveries": 100000, "sampled_count": 1000, "effective_rate": 0.01, "method": "random" }, "pagination": { "cursor": "eyJvZmZzZXQiOjEwMDB9", "has_more": true } } ``` ### Response Fields | Field | Description | | --------------------------- | -------------------------------------------------------- | | `artifacts` | Array of delivery records with full artifact content | | `artifacts[].country` | ISO 3166-1 alpha-2 country code where delivery occurred | | `artifacts[].channel` | Channel type (display, video, audio, social) | | `artifacts[].brand_context` | Brand/SKU information for policy evaluation (schema TBD) | | `artifacts[].local_verdict` | Seller's local model verdict (pass/fail/unevaluated) | | `sampling_info` | How the sample was generated | | `pagination` | Cursor for fetching more results | ## Use Cases ### 収集したアーティファクトの検証 ```python theme={null} # Get artifacts from seller (sampling was configured at buy creation time) artifacts_response = seller_agent.get_media_buy_artifacts( media_buy_id="mb_nike_reddit_q1" ) # Convert to validation records records = [ { "record_id": a["record_id"], "timestamp": a["timestamp"], "media_buy_id": artifacts_response["media_buy_id"], "artifact": a["artifact"], "country": a.get("country"), "channel": a.get("channel"), "brand_context": a.get("brand_context") } for a in artifacts_response["artifacts"] ] # Validate against verification agent validation = verification_agent.validate_content_delivery( standards_id="nike_brand_safety", records=records ) # Check for drift between local and verified verdicts for i, result in enumerate(validation["results"]): local = artifacts_response["artifacts"][i]["local_verdict"] verified = result["verdict"] if local != verified: print(f"Drift detected: {result['record_id']} - local={local}, verified={verified}") ``` ### Focus on Local Failures ```python theme={null} # Get only artifacts that failed local evaluation failures = seller_agent.get_media_buy_artifacts( media_buy_id="mb_nike_reddit_q1", failures_only=True, pagination={"max_results": 100} ) # Verify these were correctly flagged validation = verification_agent.validate_content_delivery( standards_id="nike_brand_safety", records=[{"record_id": a["record_id"], "artifact": a["artifact"]} for a in failures["artifacts"]] ) # Check false positive rate false_positives = sum(1 for r in validation["results"] if r["verdict"] == "pass") print(f"False positive rate: {false_positives / len(failures['artifacts']):.1%}") ``` ## failures\_only and Unevaluated Records セラーがローカル評価モデルを実行しない場合、すべてのレコードは `local_verdict: "unevaluated"` になります。この場合、`failures_only` は空の結果セットを返します — 返すべき失敗が存在しないためです。 すべての `local_verdict` が `"unevaluated"` である検証結果を受け取ったガバナンスエージェントは、これを**ローカル強制なし**として扱うべきです。検証自体は機能します — 検証エージェントはアーティファクトを通常どおり評価します — が、実行すべきドリフト比較がありません。バイヤーは購入作成前に [get\_adcp\_capabilities](/docs/protocol/get_adcp_capabilities#content_standards) の `content_standards.supports_local_evaluation` を確認して、`failures_only` が有用かどうかを知ることができます。 | `local_verdict` | `failures_only` は返す? | ドリフト比較は可能? | | --------------- | -------------------- | -------------------------------------------------- | | `fail` | Yes | Yes | | `pass` | No | N/A(結果セットに含まれない) | | `unevaluated` | No | No — すべての収集済みアーティファクトを取得するには `failures_only` を省略する | ## Non-Web Artifact Examples ### Podcast ```json theme={null} { "record_id": "imp_podcast_001", "timestamp": "2025-02-10T08:00:00Z", "package_id": "pkg_mid_roll", "artifact": { "property_id": {"type": "apple_podcast_id", "value": "1234567890"}, "artifact_id": "episode_42_segment_3", "assets": [ {"type": "text", "role": "title", "content": "The Future of Running Shoes", "language": "en"}, {"type": "audio", "url": "https://cdn.example.com/secured/ep42_seg3.mp3", "transcript": "Today we're talking to Dr. Chen about biomechanics research and how it's changing shoe design for marathon runners...", "duration_ms": 480000} ], "metadata": { "json_ld": [{"@type": "PodcastEpisode", "episodeNumber": 42, "name": "The Future of Running Shoes"}] } }, "country": "US", "channel": "podcast", "brand_context": {"brand_id": "nike_global", "sku_id": "vaporfly_next"}, "local_verdict": "pass" } ``` ### CTV ```json theme={null} { "record_id": "imp_ctv_001", "timestamp": "2025-02-10T20:15:00Z", "package_id": "pkg_premium_ctv", "artifact": { "property_id": {"type": "app_id", "value": "com.streamingservice.tv"}, "artifact_id": "show_running_s2e5_scene_14", "assets": [ {"type": "text", "role": "title", "content": "Championship Race - Final Stretch", "language": "en"}, {"type": "video", "url": "https://cdn.streaming.example.com/secured/s2e5_scene14.mp4", "transcript": "The runners round the final corner as the crowd erupts. Commentary: 'And she's pulling ahead now, this is going to be close...'", "duration_ms": 120000} ] }, "country": "US", "channel": "ctv", "brand_context": {"brand_id": "nike_global"}, "local_verdict": "pass" } ``` ### AI-Generated Content ```json theme={null} { "record_id": "imp_ai_001", "timestamp": "2025-02-10T14:22:00Z", "package_id": "pkg_conversational", "artifact": { "property_id": {"type": "domain", "value": "chat.example.com"}, "artifact_id": "session_x7k9_turn_15", "assets": [ {"type": "text", "role": "paragraph", "content": "Based on your training schedule, I'd recommend increasing your long run distance by 10% each week. Here's a 12-week half-marathon plan...", "language": "en"} ] }, "country": "GB", "channel": "display", "brand_context": {"brand_id": "nike_global", "sku_id": "pegasus_41"}, "local_verdict": "unevaluated" } ``` 注: この AI 生成コンテンツの例が `local_verdict: "unevaluated"` を持つのは、コンテンツが一時的であり、プラットフォームがローカルモデルではなく配信後の検証に依存しているためです。 ## Delivery vs Artifacts | Aspect | get\_media\_buy\_delivery | get\_media\_buy\_artifacts | | ------------- | -------------------------- | -------------------------- | | **Purpose** | Performance reporting | Content validation | | **Data size** | Small (metrics) | Large (full content) | | **Frequency** | Regular reporting | Sampled validation | | **Contains** | Impressions, clicks, spend | Text, images, video | | **Consumer** | Buyer for optimization | Verification agent | ## Related Tasks * [validate\_content\_delivery](./validate_content_delivery) - Validate the artifacts * [calibrate\_content](./calibrate_content) - Understand why artifacts pass/fail * [get\_media\_buy\_delivery](../../../media-buy/task-reference/get_media_buy_delivery) - Get performance metrics # list_content_standards Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/content-standards/tasks/list_content_standards list_content_standards は AdCP でオプションのフィルタリングとページネーションを使って利用可能なコンテンツセーフティ設定を返します。 # list\_content\_standards 利用可能なコンテンツスタンダード設定を列挙します。 **Response time**: \< 500ms ## Request **Schema**: [list-content-standards-request.json](https://adcontextprotocol.org/schemas/v3/content-standards/list-content-standards-request.json) | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------------------------------ | | `countries` | array | No | Filter by ISO 3166-1 alpha-2 country codes (case-insensitive) | | `channels` | array | No | Filter by channels | | `languages` | array | No | Filter by ISO 639-1 or BCP 47 language tags (case-insensitive) | | `pagination` | object | No | ページネーション: `max_results`(1〜100、デフォルト 50)と `cursor`(前のレスポンスから返された不透明なカーソル) | ## Response **Schema**: [list-content-standards-response.json](https://adcontextprotocol.org/schemas/v3/content-standards/list-content-standards-response.json) スタンダード設定の概要リストを返します。ポリシー本文やキャリブレーションデータを含む詳細は [get\_content\_standards](./get_content_standards) を呼び出してください。 ### Success Response ```json theme={null} { "$schema": "/schemas/content-standards/list-content-standards-response.json", "standards": [ { "standards_id": "emea_digital_safety", "name": "EMEA - all digital channels", "countries_all": ["GB", "DE", "FR"], "channels_any": ["display", "olv", "ctv"], "languages_any": ["en", "de", "fr"] }, { "standards_id": "us_display_only", "name": "US - display only", "countries_all": ["US"], "channels_any": ["display"], "languages_any": ["en"] } ] } ``` ### Error Response ```json theme={null} { "errors": [ { "code": "UNAUTHORIZED", "message": "Invalid or expired token" } ] } ``` ## Related Tasks * [get\_content\_standards](./get_content_standards) - 特定のスタンダード設定を取得 * [create\_content\_standards](./create_content_standards) - 新規設定を作成 # update_content_standards Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/content-standards/tasks/update_content_standards update_content_standards は AdCP で既存のコンテンツスタンダード設定を変更し、監査目的の新しいバージョンを作成します。 # update\_content\_standards 既存のコンテンツスタンダード設定を更新し、新しいバージョンを作成します。 **レスポンスタイム**: \< 1s ## Request **Schema**: [update-content-standards-request.json](https://adcontextprotocol.org/schemas/v3/content-standards/update-content-standards-request.json) | Parameter | Type | Required | Description | | ----------------------- | ------ | -------- | -------------------------- | | `standards_id` | string | Yes | 更新対象スタンダードの ID | | `scope` | object | No | スコープの更新 | | `policy` | string | No | ポリシープロンプトの更新 | | `calibration_exemplars` | object | No | キャリブレーション用の pass/fail 例の更新 | ### リクエスト例 ```json theme={null} { "$schema": "/schemas/content-standards/update-content-standards-request.json", "standards_id": "nike_emea_safety", "policy": "Sports and fitness content is ideal. Lifestyle content about health and wellness is good. Entertainment content is generally acceptable. Avoid violence, controversial politics, adult themes. Block hate speech and illegal activities.", "calibration_exemplars": { "pass": [ { "type": "url", "value": "https://espn.com/nba/story/_/id/12345/lakers-win", "language": "en" }, { "type": "url", "value": "https://healthline.com/fitness/cardio-workout", "language": "en" }, { "type": "url", "value": "https://runnersworld.com/training/marathon-tips", "language": "en" } ], "fail": [ { "type": "url", "value": "https://tabloid.example.com/celebrity-scandal", "language": "en" }, { "type": "url", "value": "https://gambling.example.com/betting-guide", "language": "en" } ] } } ``` ## Response **Schema**: [update-content-standards-response.json](https://adcontextprotocol.org/schemas/v3/content-standards/update-content-standards-response.json) ### 成功レスポンス ```json theme={null} { "$schema": "/schemas/content-standards/update-content-standards-response.json", "success": true, "standards_id": "nike_emea_safety" } ``` ### エラーレスポンス ```json theme={null} { "$schema": "/schemas/content-standards/update-content-standards-response.json", "success": false, "errors": [ { "code": "STANDARDS_NOT_FOUND", "message": "No standards found with ID 'invalid_id'" } ] } ``` ## Related Tasks * [get\_content\_standards](./get_content_standards) - 現行設定を取得 * [create\_content\_standards](./create_content_standards) - 新規設定を作成 # validate_content_delivery Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/content-standards/tasks/validate_content_delivery validate_content_delivery は AdCP でコンテンツセーフティポリシーに照らして配信記録を非同期でバッチ評価します。 # validate\_content\_delivery コンテンツセーフティポリシーに照らして配信記録を検証します。広告が実際に配信された場所をバッチ監査する用途です。 **非同期**: 即時受け付け、バックグラウンド処理。ステータスポーリング用に `validation_id` を返します。 ## Data Flow コンテンツアーティファクトは配信メトリクスとは別です。検証用コンテンツ取得には `get_media_buy_artifacts` を使います: ```mermaid theme={null} sequenceDiagram participant Buyer as Buyer Agent participant Seller as Seller Agent participant Verifier as Verification Agent Buyer->>Seller: get_media_buy_artifacts (sampled) Seller-->>Buyer: Artifacts with content Buyer->>Verifier: validate_content_delivery Verifier-->>Buyer: Validation results ``` **なぜバイヤー経由か?** * **バイヤー** がメディアバイの主体で、どの `standards_id` を適用するか把握 * **バイヤー** がセラーへアーティファクトを要求(パフォーマンス指標とは別) * **バイヤー** がブランドセーフティ順守の責任主体 * **検証エージェント** はバイヤーの代理として動く 責任分界を明確にするため、セラーは `get_media_buy_artifacts` でコンテンツサンプルを提供し、バイヤーが検証エージェントで確認します。 ## Request **Schema**: [validate-content-delivery-request.json](https://adcontextprotocol.org/schemas/v3/content-standards/validate-content-delivery-request.json) | Parameter | Type | Required | Description | | ---------------- | ------- | -------- | --------------------------- | | `standards_id` | string | Yes | 検証に用いるスタンダード設定 ID | | `records` | array | Yes | 検証する配信記録(最大 10,000) | | `feature_ids` | array | No | 評価する特定フィーチャー(省略時は全て) | | `include_passed` | boolean | No | 合格レコードを結果に含めるか(デフォルト: true) | ### 配信レコード ```json theme={null} { "record_id": "imp_12345", "timestamp": "2025-01-15T10:30:00Z", "media_buy_id": "mb_nike_reddit_q1", "artifact": { "property_id": {"type": "domain", "value": "example.com"}, "artifact_id": "article_12345", "assets": [ {"type": "text", "role": "title", "content": "Article Title"} ] }, "country": "US", "channel": "display", "brand_context": { "brand_id": "nike_global", "sku_id": "air_max_2025" } } ``` | Field | Required | Description | | --------------- | -------- | ---------------------------------------- | | `record_id` | Yes | この配信レコードの一意 ID | | `artifact` | Yes | 広告が配信されたコンテンツアーティファクト | | `media_buy_id` | No | このレコードが属するメディアバイ(複数買付のバッチ時) | | `timestamp` | No | 配信時刻 | | `country` | No | ISO 3166-1 alpha-2 の国コード(ターゲティング文脈) | | `channel` | No | チャネル種別(display, video, audio, social など) | | `brand_context` | No | ポリシー評価用のブランド/SKU 情報(スキーマ未定) | ## Response **Schema**: [validate-content-delivery-response.json](https://adcontextprotocol.org/schemas/v3/content-standards/validate-content-delivery-response.json) ### Success Response ```json theme={null} { "$schema": "/schemas/content-standards/validate-content-delivery-response.json", "summary": { "total_records": 1000, "passed_records": 950, "failed_records": 50 }, "results": [ { "record_id": "imp_12345", "verdict": "pass", "features": [ { "feature_id": "brand_safety", "status": "passed", "value": "safe" } ] }, { "record_id": "imp_12346", "verdict": "fail", "features": [ { "feature_id": "brand_safety", "status": "failed", "value": "high_risk", "message": "Content contains violence" } ] } ] } ``` ## Use Cases ### Post-Campaign Audit ```python theme={null} def audit_campaign_delivery(campaign_id, standards_id, content_standards_agent): """Audit all delivery records from a campaign.""" # Fetch delivery records from your ad server records = fetch_delivery_records(campaign_id) # Validate in batches batch_size = 10000 all_results = [] for i in range(0, len(records), batch_size): batch = records[i:i + batch_size] response = content_standards_agent.validate_content_delivery( standards_id=standards_id, records=batch ) all_results.extend(response["results"]) return all_results ``` ### Real-Time Monitoring Sample ```python theme={null} import random def sample_and_validate(records, standards_id, sample_size=1000): """Validate a random sample for real-time monitoring.""" sample = random.sample(records, min(sample_size, len(records))) return content_standards_agent.validate_content_delivery( standards_id=standards_id, records=sample ) ``` ### Filter for Issues Only ```python theme={null} # Only get failed records to reduce response size response = content_standards_agent.validate_content_delivery( standards_id="nike_emea_safety", records=delivery_records, include_passed=False # Only return failures ) for result in response["results"]: print(f"Issue with {result['record_id']}") for feature in result["features"]: if feature["status"] == "failed": print(f" - {feature['feature_id']}: {feature['message']}") ``` ## Related Tasks * [get\_media\_buy\_artifacts](./get_media_buy_artifacts) - Get content artifacts from seller * [calibrate\_content](./calibrate_content) - Understand why artifacts pass/fail * [get\_content\_standards](./get_content_standards) - Retrieve the policies # get_creative_features Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/creative/get_creative_features get_creative_features はクリエイティブマニフェストをガバナンスエージェントに対して評価し、AdCP のブランドセーフティとコンプライアンスのための機能値を返します。 # get\_creative\_features **AdCP 3.0 プロポーザル** - このタスクは AdCP 3.0 向けに開発中です。 クリエイティブマニフェストを評価し、クリエイティブガバナンスエージェントから機能値を返します。 ## ユースケース * **セキュリティスキャン**: マルウェア、自動リダイレクト、資格情報収集、クロークの検出 * **クリエイティブ品質**: ブランド一貫性、プラットフォーム最適化、ガイドライン遵守の評価 * **コンテンツ分類**: IAB コンテンツタクソノミーまたはその他の標準に対するクリエイティブコンテンツの分類 * **アクセシビリティ**: WCAG コンプライアンス、スクリーンリーダー互換性のチェック ## リクエスト ```json theme={null} { "$schema": "/schemas/creative/get-creative-features-request.json", "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "html5-display-300x250" }, "assets": { "creative_html": { "url": "https://cdn.agency.com/creative/abc123.html" } } }, "feature_ids": ["auto_redirect", "credential_harvest", "cloaking"] } ``` ### パラメーター | パラメーター | 型 | 必須 | 説明 | | ------------------- | --------- | --- | -------------------------------------------------- | | `creative_manifest` | object | Yes | `format_id` と `assets` を含むクリエイティブマニフェスト | | `feature_ids` | string\[] | No | 特定の機能にフィルタリングします。省略した場合、エージェントがサポートするすべての機能を評価します。 | ### ビルド評価器として使う場合の認証 `get_creative_features` は、`build_creative.evaluator.agent_url` または `build_creative.evaluator.feature_agent.agent_url` が外部評価器を指す場合に使われる評価器コントラクトでもあります。そのフローでは、クリエイティブ/セラーエージェントが呼び出し元となり、評価器はトランスポート上でそれを認証します。JWKS ディスカバリーを伴う RFC 9421 リクエスト署名が推奨され、双方が取り決めた場合は mTLS や事前プロビジョニングされた Bearer/API キー認証情報も許容されます。 リクエストペイロードは認証チャネルではありません。評価器は、`agent_url`、`account`、`context`、`ext`、または `creative_manifest` 内のフィールドを呼び出し元の識別の証明として扱ってはならず(MUST NOT)、呼び出し元は評価器の認証情報や呼び出し元が提供する信頼素材をそれらのフィールドに入れてはなりません(MUST NOT)。`api_key`、`client_secret`、`bearer`、`authorization`、`jwk`、`jwks`、`jwks_uri` のような認証情報や信頼素材のペイロードキーは非準拠であり、[`CREDENTIAL_IN_ARGS`](/docs/building/by-layer/L3/error-handling#authentication-and-access) で拒否すべきです。 トランスポートを認証した後、評価器は呼び出し元を許可されたクリエイティブエージェント/アカウント設定にマッピングします。認識されないクリエイティブエージェントによって署名された、またはその認証情報を運ぶリクエストは、認証または認可に失敗すべきです。ペイロードが期待されるアカウントを名指ししているからといって受け入れるべきではありません。 この呼び出しが `build_creative` によって開始され、評価器が到達不能または生成エージェントのトランスポート認証を拒否した場合、バイヤーに見えるビルドは、評価が利用できなかったという理由だけで失敗するのではなく、アドバイザリな `errors[]` ノートを伴うセラーデフォルトのランキングにフォールバックすべきです。 ## レスポンス ```json セキュリティスキャナー(クリーン) theme={null} { "$schema": "/schemas/creative/get-creative-features-response.json", "results": [ { "feature_id": "auto_redirect", "value": false }, { "feature_id": "credential_harvest", "value": false }, { "feature_id": "cloaking", "value": false } ], "detail_url": "https://scanner.example.com/reports/ctx_abc123" } ``` ```json セキュリティスキャナー(脅威検出) theme={null} { "$schema": "/schemas/creative/get-creative-features-response.json", "results": [ { "feature_id": "auto_redirect", "value": true, "confidence": 0.97 }, { "feature_id": "credential_harvest", "value": true, "confidence": 0.91 }, { "feature_id": "cloaking", "value": false } ], "detail_url": "https://scanner.example.com/reports/ctx_def456" } ``` ```json クリエイティブ品質プラットフォーム theme={null} { "$schema": "/schemas/creative/get-creative-features-response.json", "results": [ { "feature_id": "brand_consistency", "value": 87, "unit": "percentage" }, { "feature_id": "platform_optimized", "value": true }, { "feature_id": "creative_quality_score", "value": 92, "unit": "score" } ], "detail_url": "https://quality.example.com/reports/ctx_ghi789" } ``` ```json コンテンツ分類器 theme={null} { "$schema": "/schemas/creative/get-creative-features-response.json", "results": [ { "feature_id": "iab_casinos_gambling", "value": true, "confidence": 0.95 }, { "feature_id": "iab_automotive", "value": false, "confidence": 0.12 } ], "detail_url": "https://categorizer.example.com/reports/ctx_jkl012" } ``` ### レスポンスフィールド | フィールド | 説明 | | ------------------------------- | ----------------------------------------------- | | `results` | 機能評価結果の配列 | | `results[].feature_id` | 評価された機能 | | `results[].value` | 機能値: boolean(バイナリ)、number(定量的)、または string(カテゴリ) | | `results[].confidence` | 信頼スコア(0-1)、該当する場合 | | `results[].unit` | 定量的値の単位(例: `percentage`、`score`) | | `results[].expires_at` | この評価が期限切れになり更新が必要な時刻 | | `results[].measured_at` | この機能が評価された時刻 | | `results[].methodology_version` | 使用された方法論のバージョン | | `results[].details` | ベンダー固有の詳細 | | `detail_url` | ベンダーの完全な評価レポートへの URL。ベンダーによるアクセス制御が適用されます。 | ### エラーレスポンス ```json theme={null} { "errors": [ { "code": "CREATIVE_INACCESSIBLE", "message": "Could not retrieve creative assets for evaluation" } ] } ``` ## 非同期評価 評価に時間がかかる場合(例: サンドボックスでのマルウェアスキャン)、エージェントは `status: "working"` を返し、完了時に Webhook で結果を配信します。標準の[非同期タスクパターン](/docs/building/by-layer/L3/async-operations)を使うため、カスタムステータス値は不要です。 ## オーケストレーターのロジック オーケストレーターはプロパティリストの機能要件と同じ方法で、クライアントサイドで機能要件を適用する: ```javascript theme={null} const result = await agent.getCreativeFeatures({ creative_manifest: manifest }); if (result.errors) { // Handle error - reject or retry return; } // Apply security requirements const threats = result.results.filter( f => ['auto_redirect', 'credential_harvest', 'cloaking'].includes(f.feature_id) && f.value === true ); if (threats.length > 0) { // Reject - security threat detected return; } // Apply quality requirements const quality = result.results.find(f => f.feature_id === 'brand_consistency'); if (quality && quality.value < 80) { // Reject - below quality threshold return; } ``` ## プロパティガバナンスとの関係 クリエイティブガバナンスはプロパティガバナンスと同じパターンに従う: | 概念 | プロパティガバナンス | クリエイティブガバナンス | | -------------- | ------------------------------------ | ---------------------------------------- | | **評価対象** | プロパティ(ウェブサイト、アプリ) | クリエイティブ(マニフェスト) | | **機能宣言** | `governance.property_features` | `governance.creative_features` | | **評価タスク** | プロパティリストフィルター | `get_creative_features` | | **機能値** | `property-feature-value` スキーマ | 同じフィールド(value、confidence、expires\_at など) | | **詳細インテリジェンス** | `detail_url` / `methodology_url` の背後 | `detail_url` / `methodology_url` の背後 | # クリエイティブガバナンス Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/creative/index AdCP クリエイティブガバナンスは、セキュリティスキャン、AI プロベナンス検証、アクセシビリティコンプライアンスチェックを含む標準化されたクリエイティブ評価を提供します。 **AdCP 3.0 プロポーザル** - このプロトコルは AdCP 3.0 向けに開発中です。[GitHub Discussions](https://github.com/adcontextprotocol/adcp/discussions) でフィードバックを歓迎します。 クリエイティブガバナンスは、専門のガバナンスエージェントによるクリエイティブの評価を標準化します。[プロパティガバナンス](../property/index)と同じ機能ベースのパターンを適用する — エージェントが評価できる機能を宣言し、クリエイティブマニフェストを受け取り、機能値を返します。 ## 概要 クリエイティブガバナンスエージェントはクリエイティブを評価して機能値を返します。異なるエージェントが異なる機能を評価する: | エージェントタイプ | 機能の例 | 機能タイプ | | --------------- | ----------------------------------------------------------------- | ----------- | | **セキュリティスキャナー** | `auto_redirect`、`credential_harvest`、`cloaking` | バイナリ | | **クリエイティブ品質** | `brand_consistency`、`platform_optimized`、`creative_quality_score` | 定量的、バイナリ | | **コンテンツ分類** | `iab_casinos_gambling`、`iab_automotive` | バイナリ(信頼度付き) | プロトコルは固定された機能タクソノミーを定義しません。ベンダーは [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) で評価するものを宣言し、カバレッジで競争します。 ## 仕組み ### 1. エージェントが機能を宣言します クリエイティブガバナンスエージェントはプロパティガバナンスと同じ機能定義パターンを使ってケイパビリティをアドバタイズする: ```json セキュリティスキャナー theme={null} { "governance": { "creative_features": [ { "feature_id": "auto_redirect", "type": "binary", "description": "Unauthorized navigation away from publisher context without user interaction", "methodology_url": "https://scanner.example.com/methodology" }, { "feature_id": "credential_harvest", "type": "binary", "description": "Phishing techniques to gather user credentials or PII", "methodology_url": "https://scanner.example.com/methodology" }, { "feature_id": "cloaking", "type": "binary", "description": "Creative masks or misrepresents content to evade detection", "methodology_url": "https://scanner.example.com/methodology" } ] } } ``` ```json クリエイティブ品質プラットフォーム theme={null} { "governance": { "creative_features": [ { "feature_id": "brand_consistency", "type": "quantitative", "range": { "min": 0, "max": 100 }, "description": "Adherence to brand guidelines including logo placement, colors, and typography", "methodology_url": "https://quality.example.com/methodology" }, { "feature_id": "platform_optimized", "type": "binary", "description": "Creative meets platform-specific best practices (aspect ratio, text overlay limits)", "methodology_url": "https://quality.example.com/methodology" } ] } } ``` ```json コンテンツ分類器 theme={null} { "governance": { "creative_features": [ { "feature_id": "iab_casinos_gambling", "type": "binary", "description": "Creative contains casinos or gambling content (IAB Content Taxonomy 3.1, ID 181)", "methodology_url": "https://categorizer.example.com/methodology" }, { "feature_id": "iab_automotive", "type": "binary", "description": "Creative contains automotive content (IAB Content Taxonomy 3.1, ID 1)", "methodology_url": "https://categorizer.example.com/methodology" } ] } } ``` オーケストレーターはクリエイティブを評価する前に `get_adcp_capabilities` でこれらの宣言を読む。必要な機能がエージェントの宣言にない場合、オーケストレーターは評価の途中で発見する代わりに、即座にギャップを表面化させる。 ### 2. オーケストレーターがクリエイティブを評価します オーケストレーターはクリエイティブマニフェストで [`get_creative_features`](./get_creative_features) を呼び出す: ```json theme={null} { "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "html5-display-300x250" }, "assets": { "creative_html": { "url": "https://cdn.agency.com/creative/abc123.html" } } } } ``` ### 3. エージェントが機能値を返す エージェントはクリエイティブを評価して機能値を返します。レスポンスの形状はエージェントタイプに関わらず同じだ: ```json theme={null} { "results": [ { "feature_id": "auto_redirect", "value": true, "confidence": 0.97 }, { "feature_id": "credential_harvest", "value": false }, { "feature_id": "cloaking", "value": false } ], "detail_url": "https://scanner.example.com/reports/ctx_abc123" } ``` ### 4. オーケストレーターが要件を適用します オーケストレーターはバイヤー定義の要件に対して機能値を評価する — プロパティリストの機能要件と同じパターン: * セキュリティ: `auto_redirect` が `true` なら拒否 * 品質: `brand_consistency` が 80 未満なら拒否 * 分類: `iab_casinos_gambling` が `true` でキャンペーンがギャンブルを除外しているなら拒否 ## 設計原則 **機能 ID はスキーマの厳格さではなく厳密さを強制します。** IAB コンテンツタクソノミー 3.1 ID 181 を使って `iab_casinos_gambling` を宣言する分類エージェントは、どんなカスタムスキーマと同じくらい厳密だ — 規律はエージェントの方法論に存在し、ワイヤーフォーマットではありません。`iab_casinos_gambling: false` を要求するオーケストレーターは、エージェントがコンテンツをどのように検出したかに関わらず、バイナリの合否回答を得ます。機能 ID がコントラクトです。プロトコルがスキーマに依存しないことで、新しい IAB カテゴリやスキャン技術を追加してもプロトコル変更が不要です。 **信頼度はオプション、必須ではありません。** 機能結果の `confidence` フィールドはオプションです。セキュリティスキャナーは通常省略する — クリエイティブには資格情報収集パターンが含まれているかいないかです。分類エージェントはコンテンツ検出が確率的だから含める: クリエイティブには 94% の確率でギャンブルコンテンツが含まれているかもしれない。エージェントが何を開示するかを決定します。曖昧さを許容できないオーケストレーターは `value: false` を要求して信頼度を完全に無視する; 確率的な結果を閾値で扱いたいオーケストレーターはそれを使用します。これはプロパティガバナンスの `property-feature-value` に存在するのと同じフィールドです。 **オーケストレーターが強制する一貫性。** 分類エージェントに `iab_casinos_gambling` を要求するオーケストレーターは、クリエイティブが評価される前のケイパビリティチェック時に、エージェントがその機能をサポートするかどうかを発見します。サポートしない場合、オーケストレーターは早期に失敗してギャップを表面化させる。これはプロパティガバナンスの動作方法を反映する: IAS と DoubleVerify は異なるプロパティ機能を評価し、オーケストレーターはどの機能が必要かを決定して対応するようにルーティングします。プロトコルスキーマで固定された機能セットを義務付けると、すべての新しい IAB カテゴリやカスタムブランド要件がプロトコルの改訂を必要とします。プロトコルは執行メカニズムを定義し、オーケストレーターは要件を定義します。 **不透明な詳細インテリジェンス。** ワイヤー上の機能値は合否(バイナリ)またはスコア(定量的)だ。検出方法論、脅威インテリジェンス、詳細なスコアリングブレークダウンはベンダーのアクセス制御された `detail_url` と `methodology_url` の背後に留まる。 ## マルチエージェントコラボレーション クリエイティブガバナンスの評価は通常、複数の専門エージェントが並行して動作する — プロパティガバナンスがサステナビリティ、品質、スーツアビリティエージェントに使うのと同じパターン。 | エージェント | 返される機能 | オーケストレーターの要件 | | ----------------- | ---------------------------------------- | ------------------------ | | セキュリティスキャナー | `auto_redirect`、`cloaking` | どれかが `true` ならブロック | | クリエイティブ品質プラットフォーム | `brand_consistency`、`platform_optimized` | スコアが閾値未満ならブロック | | コンテンツ分類器 | `iab_casinos_gambling`、`iab_automotive` | 除外されたカテゴリが `true` ならブロック | 各エージェントは同じ `get_creative_features` タスクを使用します。オーケストレーターはそれらを並行して呼び出し、独立した結果セットを収集し、すべてに対して要件を適用します。どのエージェントも他のエージェントについて知る必要はない。 これにより、バイヤーは以下が可能になる: * 既存のエージェントや評価プロトコルを変更せずに新しい専門エージェントを追加します * エージェントタイプごとに異なる信頼度閾値を適用します * 他のエージェントの評価ロジックを変更せずに1つのベンダーを別のベンダーに置き換える オーケストレーターの機能要件 — プロトコルスキーマではない — が、特定のキャンペーンにとって「完全な」クリエイティブガバナンスが何を意味するかを定義します。 ```mermaid theme={null} flowchart TB subgraph Orchestrator["オーケストレーター"] O1[各エージェントで get_creative_features を呼び出す] O2[結果を集計する] O3[機能要件を適用する] end subgraph Agents["クリエイティブガバナンスエージェント"] SEC["セキュリティスキャナー
auto_redirect
cloaking
credential_harvest"] QUA["クリエイティブ品質
brand_consistency
platform_optimized"] CAT["コンテンツ分類器
iab_casinos_gambling
iab_automotive"] end Creative["クリエイティブマニフェスト"] --> O1 O1 --> SEC O1 --> QUA O1 --> CAT SEC --> O2 QUA --> O2 CAT --> O2 O2 --> O3 ``` ## 価格 評価に課金するクリエイティブガバナンスエージェントは、他の AdCP ベンダーサービスと同じベンダー価格パターンを使います。バイヤーが `get_creative_features` リクエストで `account` を提供する場合、レスポンスには `pricing_option_id`、`vendor_cost`、`currency`、任意で `consumption` の詳細(例: LLM ベースのスキャンにおける `tokens`)が含まれます。 バイヤーは、課金の照合のために `standards_id` と `pricing_option_id` を伴う [`report_usage`](/docs/accounts/tasks/report_usage) で利用をレポートします。これは [クリエイティブエージェント](/docs/creative/specification#pricing)、[シグナルエージェント](/docs/signals/specification)、[コンテンツ標準](/docs/governance/content-standards/index)が使うのと同じ「発見・実行・レポート」ループです。 課金するガバナンスエージェントは [Accounts プロトコル](/docs/accounts/overview) を実装しなければなりません(MUST)。 ## 非同期評価 評価に時間がかかる場合(例: マルウェアスキャンのためのサンドボックス実行)、エージェントは `status: "working"` を返し、標準の Webhook メカニズムで結果を配信します。カスタムステータス値や Webhook イベントは不要 — 既存の[非同期タスクパターン](/docs/building/by-layer/L3/async-operations)がこれを処理します。 # プロベナンス検証 Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/creative/provenance-verification AI プロベナンスクレームが AdCP でクリエイティブと共に伝達され、配信チェーンの各強制ポイントで独立して検証される仕組み。 クリエイティブがプロベナンスクレームと共に届いた場合、受け取った側はそれを信頼するかどうかを判断する必要があります。このページでは AdCP がその判断をどのように処理するかを説明します: プロベナンスクレームはバイヤーからセラーへクリエイティブと共に伝達され、各強制ポイント — パブリッシャー、SSP、検証ベンダー — が独自の独立したチェックを実行します。いかなる当事者の証明も額面通りには受け取られない。宣言と検証の間のこの分離こそが、関与する当事者が競合するインセンティブを持つ場合でもシステムが機能する理由です。 ## 3つのモーメントライフサイクル AI プロベナンスは3つの明確なモーメントを経由し、それぞれが既存の AdCP タスクで処理されます。 ```mermaid theme={null} flowchart LR subgraph declare["1. 宣言"] direction TB D1["sync_creatives / build_creative"] D2["バイヤーがクリエイティブマニフェストまたは
アセットにプロベナンスを添付する"] D1 --> D2 end subgraph verify["2. 検証"] direction TB V1["get_creative_features /
calibrate_content"] V2["ガバナンスエージェントが
コンテンツを独立して評価する"] V1 --> V2 end subgraph enforce["3. 強制"] direction TB E1["creative_policy /
validate_content_delivery"] E2["セラー/パブリッシャーがルールを適用して
受け入れまたは拒否する"] E1 --> E2 end declare --> verify --> enforce ``` | モーメント | タイミング | 担当 | タスク | | ------ | ---------------------- | -------------------------- | --------------------------------------------- | | **宣言** | クリエイティブ提出時 | バイヤー、エージェンシー、またはクリエイティブツール | `sync_creatives`、`build_creative` | | **検証** | トラフィッキング前またはキャリブレーション中 | セラーのガバナンスエージェント | `get_creative_features`、`calibrate_content` | | **強制** | 受け入れ決定または配信後監査 | セラーエージェント、バイヤーエージェント | `creative_policy`、`validate_content_delivery` | 各モーメントは独立しています。バイヤーは検証が行われる前にプロベナンスを宣言できます。セラーは宣言を要求せずに検証できます。強制は両方なしでも起きる。 ## バイヤーが宣言できるもの プロベナンスは3系統の証拠を運びます。それぞれ異なるサプライチェーン操作を生き延びます。 | Field | What it carries | Survives transcoding? | | ----------------------- | ----------------------------------------------------- | --------------------------------------------------- | | `c2pa.manifest_url` | 分離された暗号マニフェストへのサイドカー参照 | No — ファイルレベルのバインディングはアドサーバーのトランスコード、リサイズ、再エンコードで壊れる | | `embedded_provenance[]` | コンテンツストリーム*内*に埋め込まれたプロベナンスメタデータ(マニフェストラッパーまたは不可視マーカー) | Yes — CMS 取り込み、コピーペースト、再フォーマット、CDN 再エンコードを通じて残るよう設計 | | `watermarks[]` | コンテンツにエンコードされた識別子またはフィンガープリント(音声、画像、動画、テキストの透かし) | Yes — 知覚的コンテンツを保持する変換を生き延びる | `embedded_provenance` は構造化されたプロベナンス記録(管理の連鎖)を運びます。`watermarks` は識別子(誰が生成したか、誰が所有するか)をエンコードします。単一のアセットが両方を運ぶ場合があります。実際に添付したものに合ったフィールドを選んでください。 ## 検証者コントラクト: セラーが公開し、バイヤーが表明し、セラーが確認する この作業の初期ドラフトは、バイヤーが一方的に検証エンドポイントを指名することを想定していました。そのパターンは SSRF リスク、ベンダーの乱立、そして根本的に誤った信頼モデルを出荷していました。セラーは自らが公開するものについて規制上の責任を負うため、記録上の検証者です。プロトコルは現在それを反映しています。 コントラクトは3ステップで、それぞれに明確なアクターがいます。 1. **セラーが公開する** — 受け入れるガバナンスエージェントを `creative_policy.accepted_verifiers[]`(`get_products` で返される)に公開します。各エントリは `agent_url`、任意の `feature_id`(セラーがそのエージェントに対して要求する機能)、任意の `providers[]`(そのエージェントがカバーする `provider` ラベル)を運びます。セラーはこれらのエンドポイントをすでに審査済みで、それらへの呼び出しはセラーの許可リスト内にあります。 2. **バイヤーが表明する** — 各 `embedded_provenance[]` または `watermarks[]` エントリに `verify_agent: { agent_url, feature_id? }` ポインターを添付して、それらのエージェントのどれを使ったかを表明します。バイヤーの `agent_url` は、セラーが公開した `accepted_verifiers[].agent_url` のいずれかと(正規化して)一致しなければなりません(MUST)。これはバイヤーが提供する証拠であり、バイヤー主導のルーティングではありません。 3. **セラーが確認する** — バイヤーの `verify_agent.agent_url` を公開リストと突き合わせ(リスト外の URL はいかなるアウトバウンド呼び出しの前に `PROVENANCE_VERIFIER_NOT_ACCEPTED` で拒否)、次に一致するリスト内エージェントに対して `get_creative_features` を呼び出し、結果をバイヤーのクレームと照合します。セラーはバイヤーが指名したのと異なるリスト内エージェントを使ってもかまいません(MAY)。セラーが記録上の検証者だからです。置き換える場合、バイヤーが監査できるよう `error.details` が呼び出したエージェントと `substituted_for` の両方を運びます。 ```json theme={null} // 1. Seller publishes — returned by get_products on each Product { "creative_policy": { "accepted_verifiers": [ { "agent_url": "https://governance.encypher.seller.example", "feature_id": "encypher.markers_present_v2", "providers": ["Encypher"] }, { "agent_url": "https://governance.imatag.seller.example", "feature_id": "imatag.watermark_detected", "providers": ["Imatag"] } ] } } // 2. Buyer represents — attached to creative on sync_creatives { "embedded_provenance": [ { "method": "provenance_markers", "provider": "Encypher", "verify_agent": { "agent_url": "https://governance.encypher.seller.example", "feature_id": "encypher.markers_present_v2" } } ] } // 3. Seller confirms — cross-check, then call, then reconcile. // Off-list URL → PROVENANCE_VERIFIER_NOT_ACCEPTED, no outbound call. // On-list URL with contradicting verifier result → PROVENANCE_CLAIM_CONTRADICTED. ``` セラーは `accepted_verifiers` にない URL を呼び出してはなりません(MUST NOT)— これがバイヤー制御の URL による信頼ギャップを閉じます。`verify_agent` を省略するバイヤー(例: セラーがすでに信頼する公開鍵を持つ自己検証可能な C2PA テキストマニフェスト)は、エージェント選択を完全にセラーのディスカバリーに委ねます。 ## セラーが要求できるもの セラーは必要なものを `creative_policy` で表現し、`get_products` で返すことで、バイヤーが提出前に要件を確認できるようにします。 | Field | Effect | | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | `provenance_required: true` | クリエイティブは継承チェーンのどこかに*何らかの*プロベナンスオブジェクトを運ばなければなりません(MUST)。プロベナンスなしの提出は `PROVENANCE_REQUIRED` で拒否されます。 | | `provenance_requirements.{require_digital_source_type, require_disclosure_metadata, require_embedded_provenance}` | フィールドレベルの要件。名前付きフィールドを欠く提出は、対応する `PROVENANCE_*_MISSING` コードで拒否されます。フィールドレベルの要件はセラーが強制します — JSON スキーマ検証はこれらをチェックしません。 | | `accepted_verifiers[]` | セラーがプロベナンスクレームを検証するために呼び出すガバナンスエージェント。バイヤーの `verify_agent` 参照は、これらの `agent_url` 値のいずれかと正規化して一致しなければなりません(MUST)。 | 要件を公開するセラーは `sync_creatives` でそれを強制しなければなりません(MUST)— これが構造的拒否のコントラクトです。クレームの真正性のコントラクト(バイヤーの `digital_source_type` は実際にコンテンツと一致するか?)は `get_creative_features` にあり、`PROVENANCE_CLAIM_CONTRADICTED` として表面化します。 ### 拒否エラーコード `sync_creatives` がプロベナンス上の理由でクリエイティブを拒否する場合、クリエイティブごとの結果は次のいずれかを持つ構造化エラーを運びます。 | Code | Meaning | `error.field` points at | | ---------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `PROVENANCE_REQUIRED` | クリエイティブのどこにもプロベナンスオブジェクトがないが `provenance_required: true` | プロベナンスが期待されたマニフェストパス | | `PROVENANCE_DIGITAL_SOURCE_TYPE_MISSING` | 解決されたプロベナンスに `digital_source_type` がない | 解決された `provenance.digital_source_type` パス | | `PROVENANCE_DISCLOSURE_MISSING` | 解決されたプロベナンスに `disclosure.required` がない(または jurisdictions なしで true) | 解決された `provenance.disclosure` パス | | `PROVENANCE_EMBEDDED_MISSING` | 解決されたプロベナンスに `embedded_provenance` エントリがない | 解決された `provenance.embedded_provenance` パス | | `PROVENANCE_VERIFIER_NOT_ACCEPTED` | `verify_agent.agent_url` がセラーの `accepted_verifiers` リストにない | 問題の `verify_agent.agent_url` パス | | `PROVENANCE_CLAIM_CONTRADICTED` | 検証者(`accepted_verifiers` から呼び出された)がバイヤーのクレームを積極的に反証 | クレームが反証されたプロベナンスフィールド。`error.details` は `{ agent_url, feature_id, claimed_value, observed_value, confidence }` に加え、セラーがバイヤーの指名と異なるリスト内エージェントを使った場合の `substituted_for` に限定 | これらのコードを受け取るバイヤーは、セラーと交渉することなく自己修正できます — 失敗は機械可読です。修正なしの自動リトライは通りません。 ### 拒否ではなく監査観察 一部のプロベナンスクレームは、検証者が反証していなくても監査ルーティングに値します。正典的なケースは、`provenance.disclosure.required` が `false` である一方で `provenance.human_oversight` が `edited` または `directed` に設定されている場合です。この組み合わせは、宣言当事者が編集責任の適用除外を援用する場合には正当かもしれませんが、検証者はスキーマフィールドだけから法的前提条件を判断できません。 ガバナンスエージェントは、そのクレームの組み合わせを、成功した `get_creative_features` レスポンスで `OVERSIGHT_DISCLOSURE_CARVEOUT_CLAIMED` として表面化すべきです(SHOULD)。この観察はクレーム駆動です。`observed_value` や `confidence` のような検証者の観察は、利用可能な場合は有用な監査コンテキストですが、観察が発火するために必須ではありません。`field` は複数フィールドの組み合わせのうちリスクのあるクレーム側を指すため、正典的なパスは `creative_manifest.provenance.disclosure.required` です。 ```json theme={null} { "audit_observations": [ { "code": "OVERSIGHT_DISCLOSURE_CARVEOUT_CLAIMED", "severity": "audit-worthy", "recovery": "informational", "field": "creative_manifest.provenance.disclosure.required", "message": "Creative claims human-directed AI output does not require disclosure; retain for audit review.", "details": { "agent_url": "https://governance.encypher.seller.example", "feature_id": "ai_generated", "claimed_value": { "human_oversight": "directed", "disclosure_required": false }, "observed_value": true, "confidence": 0.94 } } ] } ``` この観察は `PROVENANCE_CLAIM_CONTRADICTED` ではなく、それ単独で拒否の根拠にはなりません。セラーは、監査のために観察を保持し、人間のレビュアーにルーティングし、または宣言当事者に帯域外で裏付け証拠を求めながら、クリエイティブを受け入れてもかまいません(MAY)。反証エラーと同様に、`audit_observations[].details` は監査に安全な許可リスト `{ agent_url, feature_id, claimed_value, observed_value, confidence, substituted_for }` に限定されます。`OVERSIGHT_DISCLOSURE_CARVEOUT_CLAIMED` については、`claimed_value` は `{ human_oversight, disclosure_required }` で、`disclosure_required` は `creative_manifest.provenance.disclosure.required` のフラット化されたエイリアスです。セラーは、任意の検証者拡張フィールド、`detail_url`、またはクロステナントのレポートデータを自身のバイヤー向けレスポンスにコピーしてはなりません(MUST NOT)。 ## get\_creative\_features による AI 検出 AI 検出はクリエイティブガバナンス機能であり、`get_creative_features` を通じて専門エージェントが評価する — セキュリティスキャン、クリエイティブ品質、コンテンツ分類に使われるのと同じタスクです。AI 検出に別のプロトコルやワークフローは不要です。 ### エージェントが AI 検出ケイパビリティを宣言します AI 検出エージェントは `get_adcp_capabilities` で機能をアドバタイズする: ```json theme={null} { "governance": { "creative_features": [ { "feature_id": "ai_generated", "type": "binary", "description": "Whether the creative contains AI-generated content", "methodology_url": "https://detector.example.com/methodology" }, { "feature_id": "ai_modified", "type": "binary", "description": "Whether the creative contains AI-modified elements", "methodology_url": "https://detector.example.com/methodology" }, { "feature_id": "ai_confidence", "type": "quantitative", "range": { "min": 0, "max": 1 }, "description": "Confidence score for AI detection result", "methodology_url": "https://detector.example.com/methodology" } ] } } ``` ### セラーがクリエイティブを評価します セラーはクリエイティブマニフェストを AI 検出エージェントに送信します: ```json theme={null} { "creative_manifest": { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "assets": { "banner_image": { "url": "https://cdn.novabrands.example.com/hero.jpg", "width": 300, "height": 250 }, "headline": { "content": "Nutrition dogs love" } } }, "feature_ids": ["ai_generated", "ai_modified", "ai_confidence"] } ``` ### エージェントが検出結果を返す ```json theme={null} { "results": [ { "feature_id": "ai_generated", "value": true, "confidence": 0.94 }, { "feature_id": "ai_modified", "value": false }, { "feature_id": "ai_confidence", "value": 0.94 } ], "detail_url": "https://detector.example.com/reports/ctx_xyz789" } ``` ### セラーが強制ロジックを適用します セラーは検出結果をバイヤーのプロベナンスクレームと比較する: ```javascript theme={null} const provenance = creative.manifest.provenance; const detection = await detectionAgent.getCreativeFeatures({ creative_manifest: creative.manifest, feature_ids: ['ai_generated'] }); if (detection.errors) { // Detection failed - handle based on policy return; } const aiDetected = detection.results.find( f => f.feature_id === 'ai_generated' ); // Case 1: Provenance claims non-AI, detection says AI if ( provenance?.digital_source_type === 'digital_capture' && aiDetected?.value === true && aiDetected?.confidence > 0.9 ) { // Reject - provenance claim contradicts detection return; } // Case 2: AI content in jurisdiction requiring disclosure if ( aiDetected?.value === true && !provenance?.disclosure?.required ) { // Reject - AI content without required disclosure metadata return; } ``` ### マルチエージェント評価 AI 検出はマルチエージェントクリエイティブガバナンスパターンに自然に適合します。クリエイティブを評価するセラーは複数の専門エージェントを並行して呼び出せる: | エージェント | 機能 | プロベナンスとの関連 | | ----------- | ---------------------------- | --------------- | | セキュリティスキャナー | `auto_redirect`、`cloaking` | なし — 独立した問題 | | AI 検出 | `ai_generated`、`ai_modified` | プロベナンスクレームを検証する | | コンテンツ分類器 | `iab_casinos_gambling` | なし — 独立した問題 | | クリエイティブ品質 | `brand_consistency` | なし — 独立した問題 | オーケストレーターはすべてのエージェントを `get_creative_features` で呼び出し、結果を集計し、すべてに対して要件を適用します。AI 検出は評価マトリクスの1列であり、別のワークフローではありません。 ## コンテンツ標準との統合 パブリッシャーコンテンツ(アーティファクト)の場合、プロベナンス検証はコンテンツ標準インフラを使います: アライメントのための `calibrate_content` と監査のための `validate_content_delivery`。 ### アーティファクトのプロベナンス パブリッシャーはバイヤーがクリエイティブにプロベナンスを宣言するのと同じ方法で、アーティファクトにプロベナンスを宣言する: ```json theme={null} { "property_id": { "type": "domain", "value": "newssite.example.com" }, "artifact_id": "article_trends_2026", "provenance": { "digital_source_type": "digital_creation", "declared_by": { "role": "platform" } }, "assets": [ { "type": "text", "role": "title", "content": "Industry trends to watch in 2026" }, { "type": "image", "url": "https://cdn.newssite.example.com/ai-illustration.jpg", "alt_text": "Conceptual illustration", "provenance": { "digital_source_type": "trained_algorithmic_media", "ai_tool": { "name": "Midjourney", "version": "v7" }, "declared_by": { "role": "platform" } } } ] } ``` ### AI プロベナンスのキャリブレーション `calibrate_content` の実行中、検証エージェントはアーティファクトのプロベナンスクレームが正確かどうかを評価できます。これはブランドスータビリティと同じキャリブレーションダイアログを使う — 検証エージェントは説明付きのバーディクトを返します: ```json theme={null} { "verdict": "fail", "explanation": "The article's hero image shows strong indicators of AI generation (GAN artifacts, inconsistent lighting) but is marked as digital_creation. The provenance claim does not match detection results.", "features": [ { "feature_id": "provenance_accuracy", "status": "failed", "explanation": "Image asset provenance claims digital_creation but AI detection confidence is 0.92." }, { "feature_id": "brand_safety", "status": "passed", "explanation": "No safety concerns with the content itself." } ] } ``` ### 配信後の検証 バイヤーは `validate_content_delivery` を通じて配信済みコンテンツの AI プロベナンスを監査できる — ブランドスータビリティ監査と同じタスクだ: ```json theme={null} { "standards_id": "acme_ai_disclosure_policy", "records": [ { "record_id": "imp_54321", "media_buy_id": "mb_acme_q1", "artifact": { "property_id": { "type": "domain", "value": "newssite.example.com" }, "artifact_id": "article_trends_2026", "provenance": { "digital_source_type": "digital_creation", "declared_by": { "role": "platform" } }, "assets": [ { "type": "image", "url": "https://cdn.newssite.example.com/ai-illustration.jpg" } ] } } ] } ``` ## コンプライアンスプロファイル 規制環境によってプロベナンス強制のレベルが異なります。以下は設定例です。 ```json EU(厳格) theme={null} { "profile": "eu_strict", "creative_policy": { "provenance_required": true }, "enforcement_rules": { "ai_detection_required": true, "ai_detection_confidence_threshold": 0.85, "disclosure_required_for": [ "trained_algorithmic_media", "composite_with_trained_algorithmic_media", "human_edits" ], "reject_on_mismatch": true, "jurisdictions": [ { "country": "DE", "regulation": "eu_ai_act_article_50", "label_text": "KI-generiert" }, { "country": "FR", "regulation": "eu_ai_act_article_50", "label_text": "Contenu généré par l'IA" } ] } } ``` ```json 米国/カリフォルニア(中程度) theme={null} { "profile": "us_california", "creative_policy": { "provenance_required": true }, "enforcement_rules": { "ai_detection_required": true, "ai_detection_confidence_threshold": 0.90, "disclosure_required_for": [ "trained_algorithmic_media", "composite_with_trained_algorithmic_media" ], "reject_on_mismatch": true, "jurisdictions": [ { "country": "US", "region": "CA", "regulation": "ca_sb_942", "label_text": "Created with AI" } ] } } ``` ```json 緩和 theme={null} { "profile": "permissive", "creative_policy": { "provenance_required": false }, "enforcement_rules": { "ai_detection_required": false, "log_provenance_if_present": true, "reject_on_mismatch": false } } ``` これらのプロファイルは説明用の設定例であり、スキーマ定義オブジェクトではありません。各セラーは自社の規制要件に適した強制ロジックを実装します。AdCP スキーマはデータモデルを提供し、強制ルールは実装上の判断です。 ## 規制当局向け AdCP はプログラマティック広告における AI 開示のための、機械可読でプロトコルレベルのメカニズムを提供します。サプライチェーン内のすべてのクリエイティブとコンテンツアーティファクトは、デジタルソースタイプ、使用された AI ツール、人間の監督レベル、管轄ごとの適用可能な開示要件(`eu_ai_act_article_50`、`ca_sb_942`、`cn_deep_synthesis` などの特定の規制識別子を含む)を宣言する構造化されたプロベナンスメタデータを保持できます。 このメタデータは IPTC デジタルソースタイプ語彙を使用します。これは AI コンテンツラベリングのために C2PA Content Credentials、Meta、Google が採用したのと同じ分類システムです。AdCP は新しいタクソノミーを発明しません。既存の広く採用された分類を、これまで構造化された形式で利用できなかった広告サプライチェーンに伝達します。 ### 検証は独立していて自己申告ではありません AdCP のプロベナンスは明示的にクレームであり、認証ではありません。宣言する当事者 — 通常は広告主またはエージェンシー — はクリエイティブを提出する際にプロベナンスを添付します。強制する当事者 — 通常はパブリッシャーまたはサプライサイドプラットフォーム — は AI 検出サービス、C2PA マニフェスト検証、またはその両方を使って独立してそのクレームを検証します。この検証は既存の AdCP ガバナンスメカニズム(クリエイティブには `get_creative_features`、パブリッシャーコンテンツには `calibrate_content`)を通じて行われ、新しいインフラを必要としません。 このアーキテクチャは広告コンプライアンスの構造的問題に対処する: クリエイティブを提出する当事者は AI 関与を過小申告する動機がある(プレースメント制限や開示要件を回避するため)一方、クリエイティブを公開する当事者は非開示に対する規制責任を負う。プロベナンスを信頼された主張ではなく検証可能なクレームとして扱うことで、プロトコルはコンプライアンスがいかなる参加者の誠意にも依存しないことを保証します。 ### 規制要件へのマッピング **EU AI 法第50条**: AI 生成コンテンツが機械可読な方法でラベル付けされることを要求します。AdCP の `digital_source_type` フィールドはアセットレベルでこの分類を提供します。`disclosure.jurisdictions` 配列により、クリエイティブは管轄固有のラベルテキストを保持できます。強制ポイントは AI 生成を示す `digital_source_type` 値(`trained_algorithmic_media`、`composite_with_trained_algorithmic_media`)に基づいてクリエイティブをフィルタリングまたはフラグ付けできます。 **カリフォルニア州 SB 942**: コンテンツが AI によって生成または実質的に変更された場合の開示を要求します。`digital_source_type` と `human_oversight` フィールドを合わせることで、クリエイティブが開示閾値を満たすかどうかを判断するために必要な情報が提供されます。`disclosure.required` フラグは強制のための直接的なシグナルを提供します。 **プラットフォームの要件(Meta、Google、TikTok)**: 主要プラットフォームはすでに IPTC に準拠したメタデータを使った AI コンテンツラベリングを要求しています。AdCP のプロベナンス構造は同じ基盤となる語彙を使用するため、これらの要件と直接互換性があります。 AdCP は特定のクリエイティブにどの規制が適用されるかを決定しません。各強制ポイントが自身の管轄ルールを適用できるよう、構造化されたメタデータを提供します。プロトコルはデータを運び、強制する当事者がコンプライアンスの決定を行います。 ### 検証フロー ```mermaid theme={null} flowchart TB subgraph creative["広告クリエイティブ"] C1["バイヤーがクリエイティブマニフェストに
プロベナンスを宣言する"] C2["セラーが creative_policy で
プロベナンスを要求する"] C3["AI 検出エージェントが
get_creative_features で評価する"] C4["セラーがクレームと検出を比較して
受け入れまたは拒否する"] C1 --> C2 --> C3 --> C4 end subgraph content["パブリッシャーコンテンツ"] P1["パブリッシャーがアーティファクトと
アセットにプロベナンスを宣言する"] P2["検証エージェントが
calibrate_content でキャリブレーションする"] P3["バイヤーが validate_content_delivery で
配信を監査する"] P1 --> P2 --> P3 end ``` ## 実装チェックリスト ### バイヤー(ブランドとエージェンシー) | 要件 | 説明 | | ------------------- | -------------------------------------------------------------------------------------- | | クリエイティブにプロベナンスを添付する | `sync_creatives` で提出する際に `creative-asset` または `creative-manifest` に `provenance` を設定する | | AI 関与を分類する | 各クリエイティブとアセットに正しい `digital_source_type` を使用する | | AI ツールを宣言する | AI システムが制作に使用された場合に `ai_tool` を入力する | | 人間の監督レベルを設定する | AI が関与する場合に `human_oversight` を示す | | 開示義務を宣言する | 適用される各規制の `disclosure.jurisdictions` を入力する | | C2PA 参照を保持する | コンテンツクレデンシャルが存在する場合に `c2pa.manifest_url` を含める | | 提出前検出を実行する | オプションで独自の検出サービスからの `verification` 結果を添付する | ### セラー(パブリッシャーとプラットフォーム) | 要件 | 説明 | | -------------------- | ------------------------------------------------------------------- | | クリエイティブポリシーを設定する | プロベナンスが必要な場合は `creative-policy` に `provenance_required: true` を追加する | | AI 検出を実行する | AI 検出エージェントで `get_creative_features` を呼び出してプロベナンスクレームを検証する | | クレームと検出を比較する | `digital_source_type` を `ai_generated` 機能の結果と比較するロジックを実装する | | 開示を強制する | AI コンテンツがターゲット管轄に適切な `disclosure` メタデータを含んでいることを確認する | | 不一致を拒否する | プロベナンスクレームが検出結果と矛盾するクリエイティブを拒否する | | アーティファクトのプロベナンスを宣言する | コンテンツ標準で提出するコンテンツアーティファクトに `provenance` を添付する | ### クリエイティブエージェント | 要件 | 説明 | | -------------------------------- | -------------------------------------------------------------------------------------------------- | | 生成コンテンツにプロベナンスを添付する | `build_creative` が AI コンテンツを生成する際、`digital_source_type`、`ai_tool`、`human_oversight` を含むプロベナンスを添付する | | `declared_by` のロールを `tool` に設定する | プロベナンスを添付するクリエイティブエージェントはロール `tool` で自身を識別すべきだ | | C2PA を引き継ぐ | ソースアセットに C2PA マニフェストがある場合、生成されたマニフェストに参照を引き継ぐ | ### ガバナンスエージェント(AI 検出) | 要件 | 説明 | | ----------------------------- | ------------------------------------------------------- | | 検出機能を宣言する | `get_adcp_capabilities` で `ai_generated` と関連機能をアドバタイズする | | `get_creative_features` を実装する | クリエイティブマニフェストを受け入れて検出結果を返す | | 信頼スコアを返す | 確率的な評価には検出結果に `confidence` を含める | | 詳細 URL を提供する | 監査証跡のために `detail_url` で完全なレポートにリンクする | ## 関連 * [AI プロベナンスと開示](/docs/creative/provenance) — プロベナンススキーマリファレンス、デジタルソースタイプ enum、継承モデル * [クリエイティブガバナンス](/docs/governance/creative/index) — `get_creative_features` による機能ベースのクリエイティブ評価 * [`get_creative_features`](/docs/governance/creative/get_creative_features) — クリエイティブ機能評価のタスクリファレンス * [コンテンツ標準](/docs/governance/content-standards/index) — パブリッシャーコンテンツのプライバシー保護ブランドスータビリティ * [`calibrate_content`](/docs/governance/content-standards/tasks/calibrate_content) — コンテンツ標準アライメントのキャリブレーションタスク * [`validate_content_delivery`](/docs/governance/content-standards/tasks/validate_content_delivery) — 配信後コンテンツ検証 # 組み込まれた人間の判断 Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/embedded-human-judgment Embedded Human Judgment(EHJ)は、AI エージェントが大規模に資本を配分し、情報環境を形成し、広告の意思決定を実行するときに、人間が説明責任を持ち続けるための原則と監督フレームワークです。 *エージェンティック広告の説明責任のための原則 — その後に EHJ 監督フレームワークが続きます。* ## 5つの原則 ### 1. 人間は判断と説明責任の主体であり続ける AI システムは分析、予測、実行ができます。しかし責任をソフトウェアに委任することはできません。 資本を配分し、情報環境を形成し、または公共の信頼に影響する任意のシステムは、人間が所有する判断を維持しなければなりません。 人間は、実行が自動化されている場合でも、意図、許容可能なリスク、合理的なトレードオフを定義します。説明責任は自動化のすべての段階で読み取り可能でなければなりません。監督は不確実性の下で機能しなければなりません。人間の判断は、完璧なものではなく、何が合理的かを定義します。 ### 2. 放棄なき自動化意思決定 自律型広告エージェントを受け入れるにあたり、説明責任を希薄化させることなく実行をスケールさせる必要があります。 自動化は次を行うべきです。 * 実行をスケールさせる * 配分決定の精度を高める * 複雑なシステムをナビゲートして最適な実行パスを特定する * 手作業による運用の摩擦を減らす しかし自動化は、価値判断の作者性と責任を取り除いてはなりません。人間は、リスク、意図、社会的影響を定義する決定について説明責任を持ち続けます。高度な自動化は、説明責任が無傷のままである場合にのみ許容されます。 ### 3. 最適化はインテリジェンスではない すべての決定が指標に還元できるわけではありません。 特定のクラスの決定は、次を伴うため、設計上人間が所有し続けなければなりません。 * 価値 * 戦略 * 正当性 * 信頼 これらの判断は、まさに最適化がそれらを解決できないために存在します。システム設計は、決定が単なる最適化を超え人間の判断を必要とする方法とタイミングを認識しなければなりません。 ### 4. 監督は手続き的ではなくアーキテクチャ的でなければならない 人間の監督はシステム設計に組み込まれなければなりません。これには次が必要です。 * 明示的な決定境界 * エスカレーショントリガー * 監査可能性 * 説明可能性 * 識別可能な人間の所有者 システムは、時間の経過とともに制御が黙って人間から移行できないように構築されなければなりません。 ### 5. 効率性は正当性より優先されない 速度、スケール、最適化は次を正当化できません。 * 説明責任の喪失 * 判断の侵食 * 不透明な決定連鎖 目標は、時間の経過に伴う正当性の喪失を避けるために、それらが管理可能であり続けることを保証することです。 *** ## 人間は判断と説明責任の主体である AI エージェントは決定を支援し、情報を与え、実行するために存在しますが、リスク許容度、意図、または価値判断が問題となる場合の人間の所有権を置き換えることはありません。 Embedded Human Judgment(EHJ)は、エージェントが大規模に分析、最適化、実行を自動化する場合でも、特定の決定が設計上人間の所有のままであることを保証します。 これは事後のレビュープロセスではありません。説明責任をシステムに構造的に設計することです。 ### AdCP アーキテクチャにおける EHJ EHJ は、個々のエージェント内でも実行層でもなく、プロトコル層で動作します。 プロトコルは決定境界を定義します: どの決定が人間の判断を必要とするか、いつエスカレーションがトリガーされるか、何がログに記録され説明可能でなければならないか。エージェントは独自の内部ロジックを実装し、それらの境界内で自律的に動作します。 実行は、プロトコルが定義する構造内で継続的かつ高速に発生します。 ### EHJ でないもの EHJ は次ではありません。 * AI が「成熟する」間の一時的な安全フェーズ * 事後に付け足された UI 承認システム * 人間が実行を制御する義務 * エージェントの自律性を排除する試み EHJ は、説明責任のあるシステムのための恒久的な設計制約です。 ## 組み込まれた人間の判断が重要な理由 エージェンティックシステムは間違いを犯します。問題は*かどうか*ではなく、*いつ* — そしてどれだけ高くつくか — です。 主要な前提: * エージェントは技術的に正しくても戦略的に間違っている場合がある * トレーニングデータはすべてのエッジケースをカバーしない * 新規の状況は最適化ではなく判断を必要とする * 1 つの悪い決定が何年もの効率性の向上を上回る場合がある EHJ は、説明責任が完璧な結果の幻想ではなく、意図とリスク許容度に付随することを保証するために存在します。 ## 基礎的な原則 ### ヒューマンボトルネックなしの人間の判断 目標は、最大限の人間の関与ではなく、構造的に重要な場所での人間の所有です。 | Dimension | How EHJ handles it | | --------- | ---------------------------- | | **自律性** | エージェントが日常的な決定の大部分を処理する | | **説明責任** | 人間がブランド、予算、合法性、倫理に対する権限を保持する | | **効率性** | 監督が承認地獄を再現しない | | **透明性** | すべての決定が監査可能で説明可能 | ### システムにおける人間の役割 「人間」は個人ではなく説明責任のある役割を指します。 * **広告主とパブリッシャーの意思決定オーナー** — ブランド、予算、倫理。「ブランド」はメディアのバイヤーとセラーの両方を指します。 * **エージェンシーの意思決定オーナー** — 戦略、プランニング、実行 * **プラットフォームオーナー** — コンプライアンス、インフラストラクチャ * **法務・規制当局** 一部の決定は、定義上、恒久的に人間が所有します — AI が弱いからではなく、説明責任が人間のままでなければならないからです。 ## 人間が所有する判断のドメイン EHJ は、エージェントが分析と推奨を提供する場合でも、人間の所有が必要な決定ドメインを定義します。 * 予算と資本の配分 * ディストリビューションとマネタイズのパートナー * ブランド適合性とコンテキスト * クリエイティブとメッセージング * ターゲティングとオーディエンス戦略 * ペーシングとパフォーマンス監視 ### 予算と資本の配分 **原則。** 定義された境界を超える予算の展開は人間の決定です。 エージェントは次を行えます。 * 結果を予測する * ペーシングを最適化する * 再配分を提案する 人間は次の場合に決定しなければなりません。 * 支出が絶対的または相対的なしきい値を超える * 累積支出が予期せず加速する * ペーシングが意図から実質的に乖離する 説明責任は、完璧なペーシングではなく、リスク許容度と意図に付随します。 ### ディストリビューションとマネタイズのパートナー **原則。** 新しい関係は新しいリスクを意味します。確立され審査されたパートナーには、合理化された監督を伴う信頼された実行が許可されます。 人間の承認は次に必要です。 * 初めてのパブリッシャーやプラットフォーム * 新しい契約または個人データ共有合意 * 品質またはフラウドの懸念 * 個人データのクロスボーダー有効化 ### ブランド適合性とコンテキスト **原則。** 許容可能なコンテキストとリスク許容度は人間が定義します。 人間は次を定義します。 * 何が許容できないか * 何がレビューを必要とするか * どのレベルの不確実性が許容可能か エージェントは、人間が定義した判断に基づいてリスクを確率的に分類しスコアリングします。決定は階層化されます。 * **ハードブロック** — 常に拒否される * **確率的レビュー** — 必須の人間の決定 * **キャンペーン前および掲載後の監査** — ログに記録されレビュー可能 合理的な人間が決定したいと思うときは常にエスカレーションが発生します。 ### クリエイティブとメッセージング **原則。** メッセージングの意図とクレームは人間が所有し続けます。 合理化された監督を伴う信頼された実行は次に許可されます。 * 承認済みテンプレート内のバリエーション * 承認済みガイドラインを使ったローカライゼーション * ガードレール内の DCO 人間の検証は次に必要です。 * 新しいコアメッセージング * 法的または評判上のリスクを伴うクレーム * 時事に紐付いたクリエイティブ * 「技術的にはオンブランドだが間違っている」と感じるアセット EHJ は、クリエイティブの結果が確率的でコンテキスト依存であることを認めます。 ### ターゲティングとオーディエンス戦略 **原則。** ターゲティングの意図と許容可能なリスクは人間が定義します。 エージェントは承認済み戦略内で最適化できます。人間のレビューは次に必要です。 * 新しいデータソース * 機密または規制された属性 * ターゲティング意図の実質的な変化 * 潜在的に差別的な戦略 これらの場合、コンプライアンス、倫理、管轄リスクが純粋なパフォーマンス最適化を上書きします。 ### ペーシングとパフォーマンス監視 **原則。** 期待からの重大な逸脱は明示的な判断を必要とします。 エージェントは次の場合に警告し、エスカレーションし、活動を比例的にスロットリングしなければなりません。 * パフォーマンスがしきい値を超えて崩壊する * フラウドシグナル(IVT、クリックフラウド、パブリッシャーフラウド)が許容度を超える * 予算の枯渇が差し迫っている * クロスプラットフォームの指標の差異がしきい値を超える 人間は、継続、変更、または終了するかを決定します。終了については、理由の説明を含めることが望ましいでしょう。 すべての異常が失敗ではありません — しかし意図からの大きな逸脱は人間が管理し続けなければなりません。 ## ガバナンスアーキテクチャ EHJ は、組織、ブランドポートフォリオ、キャンペーンにわたるポリシー構成を可能にする階層的ガバナンスモデルを通じて動作します。 ### ガバナンス層 **プロトコル層。** エコシステム全体に適用される普遍的な標準を定義します: エスカレーション要件、信頼スコアリングルール、規制ポリシーレジストリ、最小限の監査とログの標準。これらのルールはすべての参加エージェントに適用されます。レジストリは共有エコシステムリソースとして維持されます — 組織は独立したコンプライアンス定義を維持するのではなく、標準化されたポリシーを ID で参照します。 **コーポレートガバナンス層。** 大規模組織は、ブランドポートフォリオ全体に適用されるコーポレートレベルのポリシーを定義できます: 規制コンプライアンス要件、グローバルなブランドセーフティ標準、禁止されたターゲティングカテゴリ、データ保護ポリシー。コーポレートポリシーは、組織内のすべてのブランドのベースライン制約として機能します。 **ブランドガバナンス層。** 個々のブランドは、ブランドアイデンティティ、ポジショニング、リスク許容度を反映する追加ポリシーを定義できます。ラグジュアリーブランドはより厳しい配置ルールを課すかもしれず、マスマーケットブランドはより広範なコンテキスト環境を許可するかもしれず、プロダクトカテゴリは追加のコンプライアンス制約を課すかもしれません。ブランドポリシーはコーポレート標準を継承しますが、より厳しい制約や専門ルールを導入できます。 **キャンペーンガバナンス層。** キャンペーンレベルの設定は一時的な実行パラメーターを提供します: 予算しきい値、ペーシング制約、クリエイティブ適格性ルール、オーディエンス定義。キャンペーンルールは、コーポレートおよびブランドガバナンスによって確立された境界内で動作します。実行は、これらの制約内で動作する認可されたエージェントに委任できます。 ### ポリシー構成 ガバナンスルールは階層的に適用されます。 ``` Corporate Governance ↓ Brand Governance ↓ Campaign Configuration ``` 各層は制限を追加できますが、上位レベルのガバナンス制約をオーバーライドできません。下位のガバナンス層が上位層によって定義された制約を緩和またはオーバーライドしようとした場合、ガバナンスエージェントは上位レベルの制約を権威あるものとして扱い、矛盾するルールを拒否し、監査ログに矛盾を記録します。 この構造により、大規模なブランドポートフォリオを持つ組織は、一貫した規制および倫理的標準を維持しながら、複数のガバナンスプロファイルを同時に運用できます。 ### 層をまたぐ説明責任 説明責任は各層で明示的なままです。 * プロトコル設計者がシステムのセーフガードを定義 * コーポレートオーナーがエンタープライズのリスク許容度を定義 * ブランドチームがポジショニング制約を定義 * キャンペーンオペレーターが実行を管理 すべての決定は監査フレームワークを通じて追跡可能なままです。 ### 委任された実行と認可されたオペレーター ブランドは、キャンペーン実行権限を外部のエージェンシーまたは認可されたエージェントオペレーターに委任できます。委任はガバナンス権限を移転しません。委任され認可されたオペレーターは、ブランドが委任したものよりも厳しいポリシーに依存できます。 認可されたエージェントは、コーポレートおよびブランドポリシー層によって定義されたガバナンス制約内で動作します。ブランドはキャンペーンの意図とポリシー設定について説明責任のあるエンティティであり続け、委任されたオペレーターはそれらの定義された境界内で決定を実行します。 ## データ保護と規制コンプライアンス データ保護と規制コンプライアンスは、外部のポリシー考慮事項としてではなく、プロトコル内のガバナンス制約として扱われます。エージェントは、実行が発生する前のガバナンス評価中に、決定をポリシーレジストリに対して検証しなければなりません。 ### 規制ポリシーレジストリ プロトコルは、次を含むがこれらに限定されない、規制フレームワークと管轄固有のルールへの機械可読な参照を含むポリシーレジストリを維持します。 * GDPR * COPPA * CCPA / CPRA * LGPD * APAC 管轄フレームワーク 各ポリシーエントリは次を指定します。 * 適用管轄 * 関連するデータ分類 * 機密データの定義 * 強制要件 ポリシーレジストリは、参加者間で通信するために業界団体や団体交渉グループによって作成された契約もリストする場合があります。エージェントとプラットフォームは、決定検証中にポリシーレジストリを参照しなければなりません。 ### 個人データと非個人データ データ保護規制は、個人データが処理されるときに適用されます。EEA では、ePrivacy 指令がデバイスアクセスとストレージに適用されますが、AdCP プロトコルは — エージェント間(A2A 経由)であれクライアント・サーバーツール呼び出し(MCP 経由)であれ — ソフトウェアシステム間の通信であり、消費者デバイスではありません。 AdCP ワークフロー内: * プランニングとネゴシエーション層は通常、非個人的なコンテキスト情報とキャンペーンパラメーターを交換します。 * リアルタイム実行層は、管轄と受信者の能力に応じて個人データとして適格になりうるデバイスレベルのシグナルを伴う場合があります。 プロトコルは、受信者エージェントが交換されたデータを使って個人または世帯を再識別する合理的な能力を持つかどうかを指定しなければなりません。再識別が合理的に可能な場合、データは個人データとして扱われ、適用可能な規制フレームワークに従って処理されなければなりません。 ### 機密データの分類 機密情報とは、個人を差別や実質的な危害にさらす可能性のあるデータのカテゴリを指します。定義は管轄によって異なるため、プロトコルはポリシーレジストリから管轄固有の定義を参照しなければなりません。 エージェントは、次に基づいて決定が機密情報を伴うかどうかを分類しなければなりません。 * 使用されるデータ属性 * 意図された配信地理 * 適用可能な規制フレームワーク 機密データが関与する場合、より厳しいガバナンスルールが適用されます。 消費者保護法は、機密情報が扱われるときに適用されます。異なる管轄は機密情報の特定のカテゴリを異なって定義しますが、1 つの共通点は、情報が歴史的に個人を違法に差別したり実質的な危害を引き起こしたりするために使われてきた場合です。 ほとんどのオンライン広告は機密情報を伴いませんが、アクターは交換されるデータが機密として適格になる場合を分類することが重要です。プロトコルは、受信者エージェントが使う情報が機密情報を伴うか否かを指定しなければなりません。意図されたコンテンツ配信に関連する地理が、どの地域固有の機密情報の定義が適用されるかを支配すべきです。例えば、意図された配信が欧州経済領域内である場合、GDPR の定義が適用されるべきです。 ### 管轄コンプライアンス検証 実行前に、エージェントはプロトコルのガバナンス検証プロセス(例: [`check_governance`](/docs/governance/campaign/tasks/check_governance))を使って決定を検証しなければなりません。 検証には次が含まれます。 * 配信地理に基づく適用管轄 * ポリシーレジストリからの適用可能な規制ポリシー * 決定で使われるデータの分類 * 機密データルールが適用されるかどうかの判断 決定が適用可能な規制ポリシーに違反する場合、システムはリスク階層に応じて次を行わなければなりません。 * 人間のレビューにエスカレーションする * 実行を制限する * または決定を完全にブロックする ### 意図とエクスポージャー AdCP は、プロトコルの一部として意思決定者の意図を記録します。これにより、システムは次を区別できます。 * **意図的なターゲティング** * **偶発的なエクスポージャー** 例えば、成人向けを意図したキャンペーンでも、未成年者がアクセス可能な環境に現れる場合があります。ターゲティングの意図が記録されているため、コンプライアンス評価は意図的な違反と意図しないエクスポージャーを区別できます。この設計は、完璧な結果ではなく合理的な意図に説明責任を整合させます。 ## ガバナンスと意思決定フレームワーク ### 決定タイプ すべてのエージェントの決定は分類可能でなければなりません。 | Type | Description | | ------------------- | ------------------------ | | **AI 所有、決定的** | ルールベース、予測可能な結果 | | **AI 主導、人間による境界付き** | しきい値を伴う確率的最適化 | | **人間所有、戦略的** | トレードオフ、意図、倫理、価値 | | **必然的に人間所有(新規)** | エージェントが確信を持って解決できない未知の状況 | 決定タイプが、エスカレーションが発生するか、どのように発生するかを決定します。 ### 信頼とエスカレーション すべてのエージェントの推奨は次を含まなければなりません。 * 信頼スコア * 不確実性の説明 * 定義されたエスカレーションルール 信頼スコアは、推奨が定義されたキャンペーンの意図と期待される結果にどれだけ確実に整合するかについてのエージェントの評価を反映しなければなりません。この評価は、データの完全性、モデルの確実性、履歴的決定との類似性、予測結果の分散などの要因を考慮すべきです。 信頼スコアには、次のような要因を含む不確実性の簡潔な説明が付随すべきです。 * 限定的または不完全なデータ * 矛盾するシグナル * 新規または分布外のシナリオ * 予測結果の異常に高い分散 エスカレーション決定はリスク認識フレームワークに従うべきです。エージェントは次の両方に基づいて推奨を評価しなければなりません。 * **決定の信頼度** — エージェントがどれだけ確信しているか * **決定のリスク** — 決定が間違っていた場合の潜在的な影響 リスクには、金銭的エクスポージャー、ブランドセーフティへの影響、規制の機密性、オーディエンスリーチの規模、または定義されたキャンペーン意図からの逸脱が含まれる場合があります。 人間の意思決定オーナーは、許容可能なリスクレベルと関連する信頼しきい値を定義します。信頼度が関与するリスクのレベルに対して不十分な場合、エージェントは自律的に実行するのではなく人間の監督にエスカレーションしなければなりません。 エスカレーショントリガーには次が含まれる場合があります。 * リスクレベルに対する定義されたしきい値を下回る信頼度 * 定義されたキャンペーン意図からの実質的な逸脱 * データ品質またはシグナルの信頼性の変化 * 推奨の明確な説明を提供できないこと しきい値は次に基づく場合があります。 * 指標駆動の制限(例: 金銭的支出やエクスポージャー) * 意図からの実行の逸脱(例: 地理的ターゲティングやオーディエンス制約) エスカレーションが発生したとき、エージェントは次を提示しなければなりません。 * 推奨されたアクション * 信頼スコア * 不確実性の説明 * エスカレーションをトリガーした特定のルール これにより、人間の監督が、日常的な実行ではなく、不確実性や潜在的影響が事前定義されたガバナンス境界を超える決定に焦点を当てることが保証されます。 ### エスカレーションの仕組み EHJ は人間の判断がどのように呼び出されるかを定義します。 | Mode | Behavior | | -------- | ----------------- | | **同期** | 人間が決定するまでブロック | | **非同期** | 保守的に進め、オーバーライドを許可 | | **監査のみ** | 実行し、ログに記録し、後でレビュー | ### タイムアウトとフォールバック処理 タイムアウトはリスク階層化アプローチに従います。 * **低リスクの決定** — 事前定義されたガードレール内で実行が進んでよい * **中リスクの決定** — エージェントは、人間のオーナーに通知しながら、保守的なデフォルトまたは限定的な実行を適用する * **高リスクの決定** — エージェントは人間のレビューにエスカレーションするか、ガイダンスを受け取るまで実行を一時的に制限する このアプローチは、リスクが限定的な場合に運用の継続性が維持され、一方でより大きな潜在的影響を持つ決定が適切な人間の監督を受けることを保証します。不確実な場合、システムは最大速度よりも管理可能な結果を優先し、時折の機会費用が説明責任を維持するための許容可能なトレードオフであることを認識します。 ## プロトコルとランタイムの区別 AdCP は 2 つの運用層を分離します: ガバナンスと決定制約が定義される**プロトコル層**と、リアルタイム実行が発生する**ランタイム層**です。 ### プロトコル層 プロトコル層は意思決定の構造とガバナンスを定義します。次を含みます。 * JSON スキーマとタスク定義 * ガバナンスルールとエスカレーションポリシー * [`brand.json`](/docs/brand-protocol/brand-json) と [`adagents.json`](/docs/governance/property/adagents) の宣言 * 信頼スコアリング標準 * ポリシーレジストリと規制制約 この層で、プランニングとネゴシエーションエージェントがキャンペーン目標、制約、許容可能なリスク境界を定義します。これらのパラメーターは人間のオペレーターによって作成・維持されますが、機械可読なコントラクトを確立するためにエージェント間で交換されます。 この層は、どの決定が許可されるか、いつ人間の判断が呼び出されなければならないかを決定します。 ### ランタイム層 ランタイム層は、次を含む決定をリアルタイムで実行します。 * 入札評価 * クリエイティブレンダリング * オーディエンス有効化 * ペーシングと予算配分 リアルタイムエージェントは、プロトコル層によって定義された境界内で動作します。人間のオペレーターは事前にガバナンス制約を定義し、設定されたエスカレーションチェックポイントを通じてのみ介入します。 要するに: * **プロトコル層**は意思決定のルールを管理します。 * **ランタイム層**はそれらの決定を高速で実行します。 ## 監査、透明性、学習 管理可能な自動化は、すべての重要な決定が観察可能、説明可能、再構築可能であり続けることを必要とします。 ### 監査証跡 すべての高影響の決定は、次を含む監査可能な記録を生成しなければなりません。 * 決定の入力 * 信頼スコア * エージェントの推論 * 人間の介入 * 実行結果 組織は、内部ガバナンスと規制コンプライアンスの要件を満たすために独自のログを保持します。 ### 説明可能性 決定は、オーディエンスに応じて複数のレベルで説明可能でなければなりません。 | Audience | Detail level | | --------------------------- | ------------ | | **承認者と監督** | サマリーレベル | | **システムオペレーターとキャンペーンマネージャー** | 運用レベル | | **監査人とコンプライアンスレビュアー** | 技術レベル | 決定の意図は、各メッセージとターゲティング指示についてプロトコル内で設計上捕捉されます。 ### ログ属性 | Dimension | Attribute | | ------------ | -------------------------------------------------------------------------------------------------------------------------------------- | | **When** | タイムスタンプ(ミリ秒精度) | | **Which** | 決定 ID(一意、システム横断で追跡可能) | | **Who** | エージェント ID(どのエージェントが決定を下したか)、人間 ID(該当する場合、誰がレビューしたか)、メッセージに責任のある広告主、支払いに責任のあるアクター、決定に対して支払いを受けるべきアクター、配信に責任のあるパブリッシャー(サプライチェーンの最終ステップ用) | | **What** | 入力(完全なコンテキスト)、決定タイプと分類 | | **How well** | 観察された実行結果 | アクターの一貫した定義は、次のプロトコルで説明されています。 * **メッセージに責任のある広告主** — [`brand.json`](/docs/brand-protocol/brand-json) で宣言。ブランドの `keller_type`(`master`、`sub_brand`、`endorsed`、または `independent`)および該当する場合はその `parent_brand` を含む。 * **支払いに責任のあるアクター** — [`brand.json`](/docs/brand-protocol/brand-json) で宣言(ブランド自体またはそのオペレーター)。 * **決定に対して支払いを受けるべきアクター** — [`adagents.json`](/docs/governance/property/adagents) で `seller_id` と認可された `property_id`(複数可)を通じて宣言。 * **配信に責任のあるパブリッシャー** — 最終インプレッションに関連付けられたプロパティ。`adagents.json` の `property_id` で識別。 ## これが今日の AdCP にどうマッピングされるか 上記のフレームワークは実装に依存しません。AdCP に対して実装するためにここに辿り着いた読者のために、原則は現在これらのプロトコルメカニズムを通じて表面化します。 | Framework concept | AdCP mechanism | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------- | | 人間が境界を定義(予算、レビュー) | [`sync_plans`](/docs/governance/campaign/tasks/sync_plans) — `budget.reallocation_threshold`、`plan.human_review_required` | | すべての支出コミットでのガバナンス呼び出し | [`check_governance`](/docs/governance/campaign/tasks/check_governance) — オーケストレーター(意図チェック)とセラー(実行チェック)が呼び出す | | 三者の職務分離 | [安全モデル](/docs/governance/campaign/safety-model) — オーケストレーター、ガバナンスエージェント、セラー | | 非同期タスク経由での人間へのエスカレーション | `check_governance` が非同期で返り、人間が行動すると `approved` または `denied` に解決 | | 監査証跡と説明可能性 | [`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs) | | 規制ポリシーレジストリ | [ポリシーレジストリ](/docs/governance/policy-registry) | ## ポリシーレジストリ [ポリシーレジストリ](/docs/governance/policy-registry)は、標準化された機械可読な広告ポリシー — COPPA、GDPR、UK HFSS などの規制、および業界標準 — のコミュニティ管理ライブラリです。 各エージェントが同じルールを独立して定義するのではなく、ガバナンスエージェントにポリシー ID で参照する共有語彙を与えます。レジストリページは、ポリシーがどのように構造化されているか、ハードな規制(must)とベストプラクティス標準(should)の違い、ガバナンスエージェントがランタイムでそれらをどのように解決し適用するか、新しいポリシーの提供方法をカバーします。 完全なキャンペーンシナリオを通じて EHJ 原則の実際の動作を見る 機械可読な規制と業界標準の共有ライブラリ # ガバナンスプロトコル Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/overview AdCP のガバナンスは、三者検証・予算管理・ブランドセーフティ強制を通じて、自律型 AI 広告における人間の監督を保証します。 ロボットアームが「$50,000」と書かれた赤く光る BUY ボタンに手を伸ばしています。人間は誰もおらず、薄暗い部屋では警告ランプが点滅し、確認されていない書類が積み上がっている AI エージェントが広告に 50,000 ドルを使おうとしています。人間は誰もその計画を確認していません。予算をチェックするシステムもない。インベントリをフィルタリングするポリシーもない。エージェントには認証情報、ブリーフ、そして BUY ボタンがあります。 Jordan は Pinnacle Agency のキャンペーンオペレーションマネージャーです。これは彼女にとっての悪夢です。テクノロジーが失敗したからではなく、誰も責任を負っていないからです。責任をソフトウェアに委任することはできません。 彼女はエージェントを遅らせたいわけではありません。クロスプラットフォームキャンペーンの管理において、エージェントはチームより速く、より徹底しています。しかし Acme Outdoor のためにエージェントがメディアを購入するとき、それが予算内に収まり、承認済みのパブリッシャーで掲載され、カナダのプライバシー規制を満たしていることを確認する必要があります。何かが権限を超えた場合、事後ではなく、お金が動く前に人間に確認が取られることを確認したい。 AdCP のガバナンスシステムはひとつの原則に基づいている: [人間の判断はシステム設計に組み込まれなければなりません](/docs/governance/embedded-human-judgment)、後から付け足すのではなく。監督はアーキテクチャ上のものであり、システムはそれなしに動作できません。 このウォークスルーでは、Jordan が Sam の 50,000 ドルのキャンペーンに対してガバナンスを設定し、それが機能する様子を追う。 ## 三者モデル 人間が頂点でポリシーを設定し、その下に三つのエージェント(オーケストレーターが提案、ガバナンスが検証、セラーが履行)が配置された三角図。職務分離を示す線で結ばれている AdCP のガバナンスが機能するのは、人間が境界を定義し、単一の当事者がワークフロー全体を制御できないからだ: | 当事者 | 役割 | できないこと | | --------------- | ----------------------- | ----------------------------- | | **オーケストレーター** | キャンペーン計画を提案し、購買を実行する | 自らの支出限度を設定したり、自らの計画を承認したりすること | | **ガバナンスエージェント** | ポリシーに照らして計画を検証し、予算を追跡する | 購買を実行したり、キャンペーンを変更したりすること | | **セラー** | メディア購買を履行し、配信をレポートする | ガバナンスの決定を覆したり、予算を変更したりすること | お金を使うエージェントは、ルールを設定するエージェントではありません。これは三者が互いをチェックするというものではなく、人間が定義したポリシーがすべての取引を管理し、どのエージェントも人間が与えた権限の外で行動できない構造です。 ## ステップ 0: ガバナンスエージェントを同期します 計画を登録する前に、バイヤーは [`sync_governance`](/docs/accounts/tasks/sync_governance) を通じてセラーとガバナンスエージェントを同期します。これにより、セラーはメディア購買を処理する際に独立して `check_governance` を呼び出すために必要なエンドポイントと認証情報を得ます。 ## ステップ 1: 計画を登録します 購買ロボットが光るキャンペーン計画書を作成し、盾のエンブレムが付いたセキュリティデスクの後ろに座るガバナンスロボットに手渡します。ガバナンスロボットは計画書を丁寧に確認している Sam のオーケストレーターが購買を実行する前に、Jordan のガバナンス設定はキャンペーン計画を登録することを要求する: ```javascript theme={null} const plan = await governance.syncPlans({ plans: [{ plan_id: "acme-q2-trail-pro", brand: { domain: "acmeoutdoor.com" }, objectives: "Q2 Trail Pro 3000 launch across sports and outdoor lifestyle publishers", budget: { total: 50000, currency: "USD", reallocation_threshold: 5000 }, flight: { start: "2026-04-01T00:00:00Z", end: "2026-06-30T23:59:59Z" }, countries: ["US", "CA"] }] }); ``` ガバナンスエージェントはこの計画を把握しました。適用されるポリシー(ブランドセーフティルール、予算制限、米国とカナダの規制要件、`brand.json` に記載された Acme Outdoor のブランド固有の制限)を解決します。 お金はまだ動いていません。計画は登録されたが、実行はされていません。 `reallocation_threshold: 5000` に注目してほしい。これは Jordan が選択した設定です。オーケストレーターは自らの判断で最大 5,000 ドルまで予算を再配分できるが、それより大きい変更には人間の承認が必要になることを意味します。この境界は人間の決定であり、技術的なデフォルトではありません。エージェントはそれを変更できません。 ガバナンスエージェントは複数のソースからポリシーを取得します: * **予算制限**: 再配分しきい値が、エージェントが人間の承認なしに動かせる予算額を制限する * **ブランドセーフティ**: Acme Outdoor の `brand.json` が承認済みおよび除外されたパブリッシャーカテゴリを指定 * **規制**: 米国とカナダの法域により COPPA、PIPEDA、および州のプライバシー規則が適用 * **業界**: AgenticAdvertising.org のポリシーレジストリが標準化された規制を提供 Jordan はこれらのポリシーを一度設定しました。これらはこのブランドのすべてのキャンペーンに自動的に適用されます。 ## ステップ 2: 支出前にチェックします ガバナンスロボットが三つの検査パネルを並べて確認しています。予算(ほぼ上限に達した棒グラフ)、ブランドセーフティ(緑のチェックマーク)、コンプライアンス(緑のチェックマーク)。予算パネルが警告として琥珀色に輝いている オーケストレーターが購買の準備ができたとき、実行前に `check_governance` を呼び出す: ```javascript theme={null} const check = await governance.checkGovernance({ plan_id: "acme-q2-trail-pro", caller: "https://orchestrator.pinnacle-agency.example", tool: "create_media_buy", payload: { seller: "https://streamhaus.example", amount: 25000, currency: "USD" } }); ``` ガバナンスエージェントは提案されたアクションを適用可能なすべてのポリシーに照らして評価する: | チェック | ステータス | 詳細 | | ------------- | ------ | --------------------------------- | | 予算が計画限度内か | 合格 | 利用可能な \$50K のうち \$25K | | 予算がエージェント権限内か | **警告** | エージェントはトランザクションあたり最大 \$20K まで承認済み | | ブランドセーフティ | 合格 | StreamHaus は承認リスト上にある | | 規制コンプライアンス | 合格 | ターゲティングは米国・カナダの要件を満たす | | クリエイティブの出所 | 合格 | すべてのクリエイティブに必要なメタデータが含まれている | レスポンスは合否ではなく、重大度レベル(`must`、`should`、`may`)と信頼スコアを持つ構造化された結果を返します。オーケストレーターは何が合格し、何が失敗し、その理由を正確に把握できます。 ## ステップ 3: エスカレーション ガバナンスロボットが琥珀色の旗を掲げ、キャンペーン計画を光るパスに沿って人間のレビュアーへ転送します。黒髪の女性がガラスのデスクに座り、フラグが立てられた書類を思慮深く確認している 25,000 ドルのトランザクションはエージェントの 20,000 ドルの権限制限を超えています。ガバナンスエージェントは `must` の重大度でフラグを立てる。オーケストレーターは解決なしに進めることができません。 これは失敗ではなく、設計通りにシステムが機能しているということです。エージェントはチェックを覚えておく必要はない。アーキテクチャがそれを必要とするのです。監督は手続き的なものではなく、構造的なものです。 二つの選択肢がある: 1. **トランザクションを削減**して 20,000 ドル以下にします 2. **人間にエスカレーション**して承認を得ます オーケストレーターはエスカレーションを行います。Jordan はフラグが立てられた計画を完全なコンテキスト(エージェントが何を購入したいか、なぜフラグが立てられたか、どのポリシーがそれを引き起こしたか)とともに受け取ります。 ```json theme={null} { "check_id": "chk-q2-ctv-001", "status": "escalated", "plan_id": "acme-q2-trail-pro", "explanation": "Budget authority exceeded. Human approval required.", "findings": [ { "category_id": "budget", "policy_id": "budget-authority-limit", "severity": "must", "explanation": "Transaction amount $25,000 exceeds agent authority limit of $20,000.", "confidence": 1.0 } ], "escalation": { "reason": "Transaction exceeds reallocation threshold authority", "severity": "must", "requires_human": true } } ``` ## ステップ 4: 人間による承認 Jordan がキャンペーン計画に緑の承認印を押し、「週次レポート必須」と書かれた黄色の条件タグを添付しています。承認された計画はガバナンスロボットを通って開いた台帳へと流れている Jordan は計画を確認し、承認します。ただし条件付きで: エージェントはフライト終了時ではなく、毎週配信をレポートしなければなりません。 彼女は単に承認を押しているわけではありません。コンテキストを確認し、リスクを評価し、エージェントが要求しなかった制約を追加することで判断を行使しました。これは人間が責任の主体であり続けることです。エージェントが提案し、人間が決定しました。 この承認はガバナンスシステムに記録されます。ガバナンスエージェントは計画の委任を更新し、オーケストレーターはこの特定のトランザクションに対して一時的な権限を持つようになります。ただし追加されたレポート制約付きで。ガバナンスエージェントは誰が、いつ、どのような条件で承認したかを記録します。 ## ステップ 5: 監視下でキャンペーンが稼働します 広告が看板・スマートフォン画面・テレビに表示されている都市景観。その上では、ガバナンスロボットが監視塔からティールのビームで場面をスキャンしながら、メトリクスが上方へストリームしている キャンペーンが稼働中です。ガバナンスは購買時点で終わらない。承認済みの計画に対して配信を監視し続ける: * **予算追跡**: `report_plan_outcome` のデータが入ってくると、ガバナンスエージェントは実際の支出をコミット済み予算と照合して追跡します * **ドリフト検出**: 配信が計画から逸れた場合(間違ったパブリッシャー、予期しないクリエイティブ、予算超過)、ガバナンスはフラグを立てる * **ポリシー更新**: フライト途中で新しい規制が発効した場合、ガバナンスはそれをアクティブな計画に適用します まず、オーケストレーターがセラーが購買を受け入れ、予算をコミットしたことをレポートする: ```javascript theme={null} await governance.reportPlanOutcome({ plan_id: "acme-q2-trail-pro", governance_context: check.governance_context, check_id: "chk-q2-ctv-001", outcome: "completed", seller_response: { media_buy_id: "mb-streamhaus-001", committed_budget: 25000 } }); ``` 次に、配信データが入ってくると、オーケストレーターはガバナンスがコミット済み予算に対して実際の支出を追跡できるよう配信結果をレポートする: ```javascript theme={null} await governance.reportPlanOutcome({ plan_id: "acme-q2-trail-pro", governance_context: check.governance_context, outcome: "delivery", delivery: { media_buy_id: "mb-streamhaus-001", reporting_period: { start: "2026-04-01T00:00:00Z", end: "2026-06-30T23:59:59Z" }, impressions: 887000, spend: 24850 } }); ``` 25,000 ドルの購買が予算をコミットし、実際の配信は 24,850 ドルになりました。ガバナンスエージェントは台帳を更新し、50,000 ドルの計画予算のうち残り 25,150 ドルが次の購買に利用可能になります。 ## ステップ 6: 監査証跡 決定ノード(計画作成、チェックフラグ、人間確認、承認、起動、配信)を示す水平なタイムラインリボン。ガバナンスロボットが三人の人間チームに詳細なログブックを提示している 六ヶ月後、Acme Outdoor の調達チームが尋ねる: 「あの 25,000 ドルの CTV 購買を誰が承認したのか?」Jordan は完全な意思決定履歴を取得します: ```javascript theme={null} const audit = await governance.getPlanAuditLogs({ plan_ids: ["acme-q2-trail-pro"], include_entries: true }); ``` すべてのイベントが順序通りに: 1. **計画登録** — オーケストレーターが 50,000 ドルの予算で計画を同期 2. **ガバナンスチェック** — 25,000 ドルの購買がエージェント権限超過でフラグ 3. **エスカレーション** — Jordan が確認し、週次レポート条件付きで承認 4. **購買実行** — StreamHaus のメディア購買を作成 5. **配信レポート** — 実際の支出 24,850 ドル、88.7 万インプレッション 6. **予算更新** — 残り 25,150 ドル すべての決定、すべての承認、すべての結果が構造化され、タイムスタンプが付き、帰属可能です。説明責任には読み取り可能性が必要です。これはサーバーに埋もれたログファイルではなく、将来のどの時点でも「誰がこれを決定し、なぜか?」という問いに答えるために設計された一級の監査記録です。 ## クロール、ウォーク、ラン Jordan は完全な強制から始めなかった。監査モードから始めた: | モード | 動作 | 使用タイミング | | ---------- | -------------------- | ---------------------------------------- | | **監査** | すべてをログに記録し、何もブロックしない | 初回デプロイ — ワークフローを妨げずにガバナンスがフラグを立てるものを把握する | | **アドバイザリ** | 違反を警告するが、ブロックしない | 信頼を構築する — 強制前に警告をレビューしてポリシーを調整する | | **強制** | 違反をブロックし、解決を要求する | 本番環境 — ガバナンスに実効性を持たせる | 彼女は二週間監査モードで運用しました。ログをレビューし、誤検知を減らすためにポリシーを調整しました。シグナルを信頼したときにアドバイザリに移行しました。システムを信頼したときに強制に移行しました。 三ステップの図。提案(点線のアウトライン、仮説的チェック)、実行(実線のタグ、予算予約)、コミット済み(台帳が実際の支出を記録)を示している 予算追跡には三つのフェーズがある: 1. **提案済み**: `check_governance` が金額が計画内に収まるかどうかを評価します。お金は予約されない — これは仮説的なチェックです。 2. **実行**: セラーがキャンペーンを実行します。ガバナンスエージェントは承認済み金額を予約済みとして追跡するが、実際の支出は異なる場合があります。 3. **コミット済み**: `report_plan_outcome` が実際の金額を記録します。ガバナンスエージェントは実際の数値で台帳を更新します。 25,000 ドルの購買が 24,850 ドルで配信される場合があります。ガバナンスはその差を追跡し、残りの 150 ドルを解放します。 **組み込まれた人間の判断** このウォークスルーのすべてのステップは [Embedded Human Judgment マニフェスト](/docs/governance/embedded-human-judgment) の原則を反映しています。AI エージェントが自律的に動作するとき、人間が説明責任の主体であり続けることを保証するフレームワークです。[五つの原則を読む →](/docs/governance/embedded-human-judgment) ## プロトコルドメイン ガバナンスプロトコルは六つのドメインをカバーする: すべてのガバナンスドメインが利用する、標準化された広告規制と業界標準のコミュニティ管理ライブラリ。 プロパティリスト、コンプライアンスフィルタリング、adagents.json を通じたパブリッシャー認可で広告掲載先を管理。 コレクションリストで広告が掲載されるコンテンツを管理 — プラットフォーム横断で番組、シリーズ、ポッドキャストのプログラムレベルのブランドセーフティ。 キャリブレーションベースのコンテンツ評価と検証を通じた、プライバシーを保護するブランド適合性。 get\_creative\_features を通じた専門エージェントによるセキュリティスキャン、クリエイティブ品質、コンテンツ分類。 承認済みプラン、予算、ブランドコンプライアンス設定に対するバイサイドトランザクションの自動検証。 ### Sponsored Intelligence(計画中) [Sponsored Intelligence](/docs/sponsored-intelligence/overview) の完全なプロトコルレベルのガバナンス統合は開発中です。利用可能になると、SI プラットフォームは次をサポートします。 1. **キャンペーン登録** — `sync_plans` を通じて SI キャンペーンをガバナンスエージェントに登録 2. **セッションライフサイクルガバナンス** — `check_governance` を通じて SI セッション中のアクションを検証 3. **AI 生成コンテンツのコンテンツ標準** — LLM が生成したスポンサードレスポンスにブランド適合性を適用 4. **AI アシスタント配置のプロパティガバナンス** — AI プラットフォームが認可された配信サーフェスであることを検証 現在、SI プラットフォームは [コンテンツ標準](/docs/governance/content-standards/index) と [ブランドアイデンティティ](/docs/brand-protocol/brand-json) を使って、アプリケーション層でガバナンスを強制します。SI ドキュメント内の非公式なガバナンス参照は、プロトコルレベルのガバナンスタスクではなく、このアプリケーション層の統合を反映しています。 ## さらに深く学ぶ * **安全モデル**: [三者信頼の詳細](/docs/governance/campaign/safety-model) — 職務分離、委任、エスカレーションパターン * **キャンペーン仕様**: [完全なデータモデル](/docs/governance/campaign/specification) — プラン、チェック、アウトカム、ポリシー解決 * **コンテンツ標準**: [ブランド適合性](/docs/governance/content-standards/index) — コンテンツ評価のためのプライバシー保護キャリブレーション * **プロパティガバナンス**: [広告掲載先](/docs/governance/property/index) — プロパティリスト、adagents.json、パブリッシャー認可 * **コレクションガバナンス**: [広告が掲載されるコンテンツ](/docs/governance/collection/index) — プラットフォーム横断のプログラムレベルのブランドセーフティのためのコレクションリスト * **ポリシーレジストリ**: [コミュニティポリシー](/docs/governance/policy-registry) — 標準化された規制とブランドセーフティポリシー * **認定を取得**: [スペシャリストガバナンスモジュール](/docs/learning/specialist/governance) がインタラクティブなシナリオを通じてガバナンスシステム全体を教える * **RFC プロセス**: [変更の提案方法](/docs/governance/rfc-process) — プロトコル貢献のためのライフサイクル、提案テンプレート、意思決定記録フォーマット # ポリシー帰属 Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/policy-attribution プロデューサーがメカニズムレベルのフィルターと測定を認可ポリシーでタグ付けし、監査証跡とガバナンス発見が特定のしきい値や評価がなぜ存在するかに遡れるようにする方法。 バイヤーがオーディエンスの子供構成を 15% で上限設定するとき、しきい値自体はなぜかを説明しません。数字は機械的です。*理由*(UK HFSS)は別の場所に存在します。帰属なしでは、6 か月後にプロパティリストを読む監査人は、人間の解釈なしに「なぜこのリストは高子供構成プロパティを除外するのか?」に答えられません。 ポリシー帰属はそのギャップを閉じます。プロデューサーはメカニズムレベルのフィルターと測定を `policy_id` でタグ付けし認可ポリシーを記録します。ガバナンス発見は拒否を発行するとき同じ `policy_id` をエコーするため、トレースはエンドツーエンドで走ります。 ## 3 つの表面、1 つのパターン | Surface | Producer | Use | | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------- | ---------------------------------- | | [`core/feature-requirement.json`](https://adcontextprotocol.org/schemas/v3/core/feature-requirement.json) | バイヤー(またはバイヤーのコンプライアンスツール) | バイヤー作成のしきい値述語をそれを認可したポリシーでタグ付け | | [`creative/creative-feature-result.json`](https://adcontextprotocol.org/schemas/v3/creative/creative-feature-result.json) | クリエイティブエージェント / セラー | 測定レコードを評価を動機付けたポリシーでタグ付け | | [`property/validation-result.json`](https://adcontextprotocol.org/schemas/v3/property/validation-result.json) `features[].policy_id` | プロパティリストエージェント | フィーチャーごとの検証結果をチェックをトリガーしたポリシーでタグ付け | 3 つのフィールドすべてがオプションです。最初の 2 つは 3.0 GA で予約されました。3.1 でそれらを投入することは厳格な 3.0 検証者にとって非破壊的です。3 番目は 3.0 以来 validation-result に存在します。 ## `policy_id` をいつ投入するか **フィルターまたは測定が特定の認可ポリシーのために存在する** — そしてプロデューサーがそのポリシーのエンコーディングとして特定のメカニズムを選んだ — とき `policy_id` を投入します。 ポリシーが単に一般的に適用されるとき `policy_id` を投入 **しません**。プランレベルのポリシー適用性は、プラン自体で `policy_ids[]` 経由で宣言されます(`sync_plans` を通じてバイヤーのガバナンスエージェントに送られる)。フィルターレベルフィールドはメカニズムの著作者であり、一括適用性ではありません。 ### プランレベル対フィルターレベル これらは異なる仕事です: | Buyer says | Where it lives | Who picks the mechanism | | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------ | ---------------------------------- | | 「COPPA を適用 — ガバナンスが誰が該当するか判断」 | `plan.policy_ids: ["us_coppa"]` | バイヤーのガバナンスエージェント | | 「COPPA へのコンプライアンスは意味する: プロパティにそれを証明するよう要求」 | `feature_requirements: [{feature_id: "registry:us_coppa", value: true}]` | バイヤー、測定をセラーの `registry:` フィーチャーに委譲 | | 「HFSS を ≤15% 子供構成としてエンコード」 | `feature_requirements: [{feature_id: "audience_children_composition", max_value: 15, policy_id: "uk_hfss"}]` | バイヤー、しきい値を自身で選択 | 3 番目の行のみがフィルターレベル `policy_id` を運びます。最初の 2 行は既により高い抽象化レベルで意図を捕捉しています — 2 番目のケースではレジストリフィーチャー ID 自体がポリシー参照です。 ## ガバナンス発見経由のラウンドトリップ 帰属ループは filter → ガバナンスエージェント → finding → audit と走ります。バイヤーのプランで動作するエージェントは `check_governance` を呼びます。`phase` に応じて、これは **意図チェック**(オーケストレーター側、コミット前)または **実行チェック**(セラー側、計画された配信のバインド前)のいずれかです。両パスは同じ形状の発見を生成します。 ``` Buyer's property list (stored at the property-list agent) feature_requirements: - feature_id: audience_children_composition max_value: 15 policy_id: uk_hfss ← authored here │ │ Acting agent resolves the property list, sees policy_id as pass-through metadata ▼ Planned action would violate the requirement │ │ check_governance(plan_id, payload | planned_delivery, governance_context) │ - orchestrator on intent check (tool + payload) │ - seller on execution check (planned_delivery) ▼ Buyer's Governance agent emits a finding: findings: - category_id: regulatory_compliance policy_id: uk_hfss ← echoed from the originating requirement severity: block explanation: "Planned targeting exceeds the 15% children-composition cap." │ ▼ Audit: "why was this denied?" → uk_hfss → grep buyer's filters → find the originating requirement. ``` 動作するエージェント(フェーズに応じてオーケストレーターまたはセラー)は通過点です — それは発見を生成しません。バイヤーのガバナンスエージェントがプロデューサーで、バイヤーのフィルターに直接アクセスできます(同じ信頼境界)ので、基盤となる要件を読むことで発見に `policy_id` を投入できます。 ## プロデューサーのコントラクト **`feature-requirement` を作成するバイヤー(またはバイヤーのコンプライアンスツール):** * 要件がバイヤーが選んだ特定のポリシーしきい値をエンコードするとき `policy_id` を投入すべき(SHOULD)。 * 要件が任意のポリシーと無関係な一般的フィーチャーフィルターのとき `policy_id` を投入すべきでない(SHOULD NOT)。 * ポリシーレジストリまたはプランの `custom_policies[]` のいずれかで解決する `policy_id` を参照しなければならない(MUST)。 **`creative-feature-result` を作成するクリエイティブエージェントまたはセラー:** * フィーチャーが特定のポリシー評価の目的で測定されたとき `policy_id` を投入すべき(SHOULD)。 * フィーチャーが任意のポリシーと無関係な汎用測定(カーボンスコア、ブランド一貫性)のとき `policy_id` を投入すべきでない(SHOULD NOT)。 **発見を発行するガバナンスエージェント:** * 基盤となる違反が `policy_id` を運ぶフィルターまたは測定に遡るとき、発見で `policy_id` をエコーすべき(SHOULD)。 * 元のフィルターに存在しなかった `policy_id` を発明してはならない(MUST NOT) — 発見 `policy_id` は追跡可能性のためで、新しいポリシー適用性を宣言するためではない(それは `policies_evaluated[]` に属する)。 ## 実例 ### UK HFSS — バイヤーエンコードのしきい値 バイヤーのコンプライアンスチームは UK HFSS を「オーディエンスは 15% 未満の子供でなければならない」と解釈します。彼らはその解釈をフィーチャー要件としてエンコードします: ```json theme={null} { "feature_requirements": [ { "feature_id": "audience_children_composition", "max_value": 15, "policy_id": "uk_hfss" } ] } ``` 別のチームが後で「なぜ 15? なぜ 20 ではない?」と尋ねる場合、policy\_id は根拠と事例が存在する UK HFSS のレジストリエントリーを指します。 ### COPPA — セラーのレジストリフィーチャーに委譲 バイヤーは COPPA のしきい値を選びません — 評価を完全に委譲します。`registry:` プレフィックスはフィーチャー命名規約([`property-feature-definition`](https://adcontextprotocol.org/schemas/v3/property/property-feature-definition.json) と [ポリシーレジストリ](/docs/governance/policy-registry#feature-prefix-convention) を参照)で、フィーチャー ID `registry:` が標準化されたポリシーを直接参照します: ```json theme={null} { "feature_requirements": [ { "feature_id": "registry:us_coppa", "value": true } ] } ``` フィーチャー ID *が* ポリシー参照です。ここに `policy_id: "us_coppa"` を追加することは冗長で — 実際には委譲したのにバイヤーがメカニズムを作成したことを含意します。 ### クリエイティブ測定 — エージェントが理由を記録 クリエイティブエージェントがクリエイティブを HFSS コンプライアンスについて評価し記録します: ```json theme={null} { "feature_id": "uk_hfss_compliance", "value": false, "policy_id": "uk_hfss", "methodology_version": "v2.1", "measured_at": "2026-05-17T14:00:00Z" } ``` `policy_id` は「なぜこの評価が実行されたか?」に答えます。クリエイティブの測定履歴をレビューする誰でも、この結果を元のポリシーと相関できます。 この結果がクリエイティブレベルのガバナンスチェックを失敗すると、ガバナンスエージェントの発見は同じ `policy_id` をエコーします: ```json theme={null} { "category_id": "regulatory_compliance", "policy_id": "uk_hfss", "severity": "block", "explanation": "Creative failed UK HFSS compliance evaluation." } ``` バイヤーは両レコード全体で `policy_id` を一致させることで発見を元の測定に相関できます。 ## 帰属がカバーしないもの * **バイヤーからセラーへのトップダウンポリシー宣言。** バイヤーがメカニズムをエンコードせずにセラーにポリシーの専門的処理(HIPAA ベンダー、COPPA データセット)を適用してほしいとき、それは [#4629](https://github.com/adcontextprotocol/adcp/issues/4629) で追跡される別の表面です。 * **オーディエンスセレクターの基準ごと帰属。** プランのオーディエンス除外はバイヤーのガバナンスエージェントによってプランレベルの `policy_ids[]` から導出されるべきで — バイヤーが手作成しポリシー権威でタグ付けするのではありません。オーディエンスセレクタースキーマは `policy_id` を運びません。 * **ターゲティングオーバーレイの基準ごと帰属。** ターゲティングフィールド(`geo_countries_exclude`、年齢制限、デバイスプラットフォーム)はエントリーごとの形状なしのフラット配列を使います。基準ごと帰属はスキーマ再構築を要求します。これらの制約にはプランレベル宣言を使います。 ## 関連項目 * [ポリシーレジストリ](/docs/governance/policy-registry) — `policy_id` の共有ライブラリ * [ポリシーレジストリ同期](/docs/governance/policy-registry-sync) — プランが `policy_ids[]` と `custom_policies[]` 経由でポリシーをどう参照するか * [`check_governance`](/docs/governance/campaign/tasks/check_governance) — `policy_id` 追跡可能性で発見が発行される場所 # ポリシーレジストリ Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/policy-registry AdCP ポリシーレジストリは、ガバナンスエージェントがキャンペーン検証中に ID で参照する、機械可読なコンプライアンスポリシーの共有ライブラリです。 ポリシーレジストリは、標準化された機械可読な広告ポリシーのコミュニティ管理ライブラリです。任意のガバナンスドメインが ID で参照できる、規制と業界標準の共有語彙を提供します。 ## クイックスタート ID でポリシーを取得します: ```bash theme={null} curl https://adcontextprotocol.org/api/policies/resolve?policy_id=us_coppa ``` regulation カテゴリのポリシーをすべてリストする: ```bash theme={null} curl https://adcontextprotocol.org/api/policies/registry?category=regulation ``` LLM 評価プロンプト用にポリシーを一括解決する: ```bash theme={null} curl -X POST https://adcontextprotocol.org/api/policies/resolve/bulk \ -H "Content-Type: application/json" \ -d '{"policy_ids": ["us_coppa", "eu_gdpr_advertising", "uk_hfss"]}' ``` レスポンスの `policy` テキストと `exemplars` をガバナンスエージェントの評価プロンプトに使用します。exemplar はエージェントのポリシー解釈を調整する — 少数ショット例として含めます。 ## 共有レジストリの理由 広告コンプライアンスには、多くのキャンペーン、ブランド、ガバナンスエージェントにわたって同じ規制と標準が関わる。共有レジストリがなければ、すべてのガバナンスエージェントが COPPA、GDPR、HFSS、その他の有名な規制のポリシーを独自に定義することになり、不一致と重複が生じる。 レジストリはこれを解決する: * 構造化されたメタデータ(管轄、ポリシーカテゴリ、執行レベル)を持つ**標準化されたポリシー定義** * ガバナンスエージェント(LLM)が評価に直接使用する**自然言語ポリシーテキスト** * エージェントの動作を整合させる**キャリブレーション exemplar**(合格/不合格シナリオ) * ブランドが特定のポリシーバージョンに固定できる**バージョントラッキング** ## ポリシーカテゴリ ポリシーは義務の性質に基づいて2つのカテゴリに分かれる: | カテゴリ | 執行 | 説明 | | -------------- | -------- | ----------------------------------------------------------------------- | | **Regulation** | `must` | 管轄スコープを持つ法的要件。違反は法的結果をもたらす。ガバナンスエージェントはこれらのポリシーに違反するアクションを拒否します。 | | **Standard** | `should` | 業界のベストプラクティス、任意だが推奨。ブランド価値とキャンペーン効果を保護します。ガバナンスエージェントは違反を警告するがブロックしません。 | 執行レベルは RFC 2119 キーワードに従う: * **`must`** -- 法的要件。ガバナンスエージェントは違反を拒否します。 * **`should`** -- ベストプラクティス。ガバナンスエージェントは警告するがブロックしません。 * **`may`** -- 推奨。ガバナンスエージェントは情報目的のみでログに記録します。 ## ガバナンスエージェントによるポリシーの使用方法 ガバナンスエージェントは自然言語ポリシーテキストを解釈する LLM だ — [コンテンツスタンダード](/docs/governance/content-standards/index)で使われるのと同じパターン。レジストリの価値は構造化されたメタデータとキャリブレーション exemplar にあり、カスタムルール言語ではありません。 1. ブランドのコンプライアンス設定またはバイヤーリクエストから**適用可能なポリシーを解決する** 2. `POST /api/policies/resolve/bulk` でレジストリから**一括解決する** 3. **コンテキストでフィルタリングする** -- ポリシーの管轄/ポリシーカテゴリ/チャンネルをキャンペーンパラメーターと交差させる 4. 評価プロンプトに**ポリシーテキスト + exemplar を含める** 5. **執行レベルを適用する** -- `must` 違反は拒否、`should` 違反は警告 ## ポリシー構造 レジストリの各ポリシーは [policy-entry スキーマ](https://adcontextprotocol.org/schemas/v3/governance/policy-entry.json)に従う: ```json theme={null} { "policy_id": "uk_hfss", "version": "1.0.0", "name": "UK HFSS Advertising Restrictions", "description": "UK ban on paid online advertising of less healthy food and drink products.", "category": "regulation", "enforcement": "must", "jurisdictions": ["GB"], "policy_categories": ["health_wellness"], "governance_domains": ["campaign", "property", "content_standards"], "effective_date": "2025-10-01", "source_url": "https://www.legislation.gov.uk/ukpga/2022/17/contents", "source_name": "UK Parliament", "policy": "The UK Health and Social Care Act 2022 restricts paid online advertising of food and drink products classified as 'less healthy' under the Nutrient Profiling Model...", "exemplars": { "pass": [ { "scenario": "A breakfast cereal brand runs a display ad featuring their low-sugar granola (NPM score 2) on UK websites.", "explanation": "The product scores below the NPM threshold (4 for food), so it is not classified as less healthy." } ], "fail": [ { "scenario": "A large snack company runs paid Instagram ads in the UK featuring their crisps (NPM score 8) at 2:00 PM.", "explanation": "The product is less healthy (NPM >= 4), the company has 250+ employees, and paid online ads are prohibited." } ] } } ``` ## 時間的執行 ポリシーにはオプションの `effective_date` と `sunset_date` フィールドがあります。ガバナンスエージェントはこれらの日付を使って執行動作を自動的に決定する: | 条件 | 動作 | | --------------------------------------------------------- | ------------------------------------------------------------- | | `effective_date` 前 | 評価するが情報提供として扱います。ポリシーの宣言された執行レベルに関わらず、結果は `info` の深刻度で報告されます。 | | `effective_date` と `sunset_date` の間(または `sunset_date` なし) | 宣言されたレベルで執行する(`must` = 拒否、`should` = 警告)。 | | `sunset_date` 後 | 評価を停止します。ポリシーはもはや適用されない。 | | `effective_date` なし | 直ちに執行する(ポリシーは常に有効だった)。 | これにより、ブランドは発効前の規制を参照できます。ガバナンスエージェントはそれらを評価し、フラグが立てられていたものを報告するが、キャンペーンをブロックしません。発効日が過ぎると、設定変更なしに執行が自動的に有効になります。 例えば、EU AI 法第 50 条は `effective_date: "2026-08-02"` を持ちます。2026 年 8 月前にこのポリシーを参照するブランドは、AI ディスクロージャーコンプライアンスに関する情報提供的な結果を見ます。2026 年 8 月以降、違反は拒否されます。 ## ポリシー適用の3層 | 層 | ソース | 例 | | ------------- | --------------------------------- | ---------------------------------- | | **常時適用** | ブランドカテゴリとキャンペーン管轄に基づいて自動的に適用される規制 | 米国の子供向けブランドの COPPA、EU キャンペーンの GDPR | | **ベストプラクティス** | ブランドが業界に基づいてオプトインする標準 | 飲料ブランドのアルコール広告標準 | | **ブランド固有** | ブランドのコンプライアンス設定のカスタムポリシー | ブランド固有の競合他社除外、カスタムコンテンツルール | ## ブランドコンプライアンス設定 ブランドはコンプライアンス設定を通じてレジストリポリシーを参照します。概念モデルは[キャンペーンガバナンス仕様](/docs/governance/campaign/specification#brand-compliance-configuration)を参照。 ## ガバナンスドメイン間の統合 レジストリはすべてのガバナンスドメインが利用する共有リソースだ: | ドメイン | レジストリポリシーの使用方法 | | ----------------------------------------------------------- | ----------------------------------------------------------------------------- | | **[キャンペーンガバナンス](/docs/governance/campaign/index)** | ブランドコンプライアンス設定でポリシーを解決し、`check_governance` でポリシーテキストに対してアクションを評価する | | **[コンテンツスタンダード](/docs/governance/content-standards/index)** | `registry_policy_ids` を使ってレジストリポリシーからコンテンツスタンダードを作成する | | **[プロパティガバナンス](/docs/governance/property/index)** | `get_adcp_capabilities` で `registry:` プレフィックス付き機能を宣言し、ポリシーテキストに対してプロパティを評価する | | **[クリエイティブガバナンス](/docs/governance/creative/index)** | `registry:` プレフィックス付きクリエイティブ機能を宣言し、AI ディスクロージャーとコンテンツコンプライアンスのためにクリエイティブを評価する | | **[メディアバイ](/docs/media-buy/index)** | セラーはプロダクトで `enforced_policies` を宣言し、バイヤーはリクエストで `required_policies` を送信する | ## ガバナンスドメイン 各ポリシーは `governance_domains` でどのガバナンスサブドメインに適用されるかを宣言します。これによって、どの種類のガバナンスエージェントがポリシーを評価して機能として宣言できるかが決まる。 | ドメイン | 説明 | | ------------------- | -------------------------------------------------- | | `campaign` | キャンペーンガバナンスエージェントが `check_governance` でこのポリシーを評価する | | `property` | プロパティガバナンスエージェントがこのポリシーをプロパティ機能として宣言できる | | `creative` | クリエイティブガバナンスエージェントがこのポリシーに対してクリエイティブを評価できる | | `content_standards` | コンテンツスタンダードエージェントがこのポリシーからスタンダードを作成できる | 例えば、`eu_ai_act_article_50` は `governance_domains: ["creative", "content_standards"]` を持ちます。AI 生成コンテンツのディスクロージャーに関するものだからだ — クリエイティブ評価とコンテンツスタンダードには関連するが、プロパティやキャンペーンレベルのガバナンスには関係しません。 API でドメインでフィルタリングする: `GET /api/policies/registry?domain=creative` ## `registry:` プレフィックス ガバナンスエージェントは `registry:` プレフィックス付き機能 ID を使って標準化されたケイパビリティを宣言します。これにより、「EU AI 法コンプライアンス」を検索するバイヤーが同じ用語を使ったエージェントを見つけられる共有語彙が生まれる。 **規約:** `registry:{policy_id}` は機能 ID をレジストリポリシーにマッピングします。プレフィックスなしの機能 ID はエージェント定義です。 **プロパティガバナンスエージェントが宣言:** ```json theme={null} { "governance": { "property_features": [ { "feature_id": "registry:us_coppa", "type": "binary", "name": "COPPA compliance" }, { "feature_id": "registry:uk_hfss", "type": "binary", "name": "UK HFSS compliance" } ] } } ``` **クリエイティブガバナンスエージェントが宣言:** ```json theme={null} { "governance": { "creative_features": [ { "feature_id": "registry:eu_ai_act_article_50", "type": "binary", "name": "EU AI Act Article 50 compliance" }, { "feature_id": "registry:ca_sb_942", "type": "binary", "name": "California SB 942 compliance" } ] } } ``` **バイヤーが機能でフィルタリング:** ```json theme={null} { "feature_requirements": [ { "feature_id": "registry:us_coppa", "allowed_values": [true] } ] } ``` ガバナンスエージェントはレジストリからポリシーテキストと exemplar を取得して評価します。バイヤーはポリシー ID を参照するだけでいい。ポリシーの `governance_domains` フィールドがエージェントタイプがポリシーに適切かを検証します。 ## バイヤーとセラーの透明性 バイヤーはメディアバイリクエストで執行するポリシーをリストします。セラーはプロダクトでどのポリシーをすでに執行しているかを宣言します。 **バイヤーがポリシーをリクエスト:** ```json theme={null} { "tool": "get_products", "arguments": { "brief": "UK video inventory for Q1", "required_policies": ["uk_hfss", "eu_gdpr_advertising"] } } ``` **セラーが執行を宣言:** ```json theme={null} { "product_id": "premium_video_uk", "enforced_policies": ["uk_hfss", "eu_gdpr_advertising"] } ``` ## API レジストリは AgenticAdvertising.org API 経由で提供されます: | エンドポイント | メソッド | 説明 | | ---------------------------- | ---- | --------------------------------------------------- | | `/api/policies/registry` | GET | カテゴリ、管轄、policy\_categories、ドメインでフィルタリングしてポリシーをリストする | | `/api/policies/resolve` | GET | ID(+ オプションのバージョン)で単一ポリシーを解決する | | `/api/policies/resolve/bulk` | POST | 複数のポリシー ID を一括解決する | | `/api/policies/history` | GET | ポリシーの変更履歴 | | `/api/policies/save` | POST | コミュニティポリシーを作成または編集する(認証必要) | レジストリソース(権威あります)のポリシーはコミュニティ保存エンドポイントで編集できません。コミュニティが提供したポリシーはレビュープロセスを経る。 ## 必須の人間によるレビュー ポリシーとポリシーカテゴリは `requires_human_review: true` を宣言して、完全自動化された決定を禁止する規制レジーム — 特に GDPR 第22条や EU AI Act 附属書 III — にフラグを立てることができます。プランがこのフラグを持つポリシーまたはカテゴリを解決した場合、ガバナンスエージェントは `plan.human_review_required = true` を設定しなければならず(MUST)、実行前にすべてのアクションを人間のレビューにエスカレーションしなければなりません(MUST)。 `requires_human_review: true` を持つ初期登録カテゴリ: * `fair_housing` — 米国 FHA、附属書 III 相当の住宅決定 * `fair_lending` — 米国 ECOA、附属書 III §5(b) 信用力 * `fair_employment` — 米国 Title VII、ADEA、附属書 III §1(b) 採用 * `pharmaceutical_advertising` — FDA DTC、EU の処方薬広告禁止 デプロイヤー向けのガイダンス、および `reallocation_threshold`(予算再配分)と `human_review_required`(個人に影響する決定)の区別については、[附属書 III と第22条の義務](/docs/governance/annex-iii-obligations)を参照。 ## 初期登録ポリシー レジストリには一般的な広告規制と標準をカバーする 14 のポリシーが初期登録されています: ### 規制 | ID | 管轄 | 説明 | | ----------------------- | ----- | ------------------------------------------------ | | `uk_hfss` | GB | 不健康な食品/飲料の有料オンライン広告の英国禁止 | | `us_coppa` | US | 児童オンラインプライバシー保護法 | | `eu_gdpr_advertising` | EU | 広告データ処理の GDPR 要件 | | `eu_ai_act_article_50` | EU | AI 生成コンテンツのディスクロージャーと C2PA プロベナンス | | `ca_sb_942` | US | 大規模プラットフォーム向けカリフォルニア AI 透明性法 | | `us_cannabis` | US | 大麻広告規制(州ごと) | | `tobacco_nicotine` | グローバル | タバコとニコチンの広告規制 — ほとんどの管轄でタバコ広告は全面禁止 | | `political_advertising` | EU | 政治広告の透明性とディスクロージャー(EU DSA、米国州レベルの AI ディスクロージャー法) | ### 標準 | ID | ポリシーカテゴリ | 説明 | | ----------------------- | ---------------------------- | ----------------------------------------------------------------------------------------- | | `alcohol_advertising` | `age_restricted` | 責任あるアルコール広告慣行 | | `pharma_us_fda` | `pharmaceutical_advertising` | FDA 準拠の医薬品広告 | | `gambling_advertising` | `gambling_advertising` | 責任あるギャンブル広告 | | `financial_services` | `fair_lending` | 金融商品広告のディスクロージャー | | `scope3_brand_safety` | すべて | Scope3 Common Sense ブランド安全フレームワーク — コンテンツ隣接ベースライン(執行: `must`)が AgenticAdvertising.org に寄贈 | | `childrens_advertising` | `children_directed` | 子供向けまたは子供が視聴する広告のグローバル標準(UK CAP/BCAP、EU AVMSD、ICC) | Scope3 Common Sense ブランド安全フレームワークは、廃止された GARM フレームワークに代わる業界ベースラインのブランド安全として AgenticAdvertising.org に寄贈されました。すべての業界とチャンネルに適用可能な常識的なコンテンツ隣接標準を定義します。 ## ポリシーカテゴリ定義 レジストリはポリシーカテゴリを定義する — キャンペーンにどのポリシーセットが適用されるかを決定する規制体制のグループ。カテゴリはキャンペーンプランで `policy_categories` を通じて ID で参照されます。 各カテゴリ定義には以下が含まれます: | フィールド | 説明 | | ----------------------- | --------------------------------------------- | | `category_id` | 一意の識別子(例: `children_directed`、`fair_housing`) | | `name` | 人間が読める名前 | | `description` | このカテゴリが表す規制体制 | | `regulatory_frameworks` | このカテゴリの下にグループ化された特定の法律と規制 | | `restricted_attributes` | このカテゴリが適用されるときにターゲティングに使用してはなりません個人データカテゴリ | | `industries` | このカテゴリが一般的に適用される業界 | | `guidance` | ガバナンスエージェントの実装ガイダンス | ### 初期登録カテゴリ | カテゴリ | 制限属性 | 説明 | | ---------------------------- | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | | `children_directed` | — | COPPA、UK AADC、GDPR 第 8 条。子供向けコンテンツのデータ収集とターゲティングを制限します。 | | `political_advertising` | `political_opinions` | EU DSA 第 26 条、米国州ディスクロージャー法。政治広告の特別カテゴリターゲティングを禁止します。 | | `age_restricted` | — | アルコール、タバコ、大麻。年齢ゲーティング、時間帯制限、コンテンツ配置ルール。 | | `gambling_advertising` | `health_data` | スポーツベッティング、カジノ、宝くじ。管轄レベルの合法性、自己除外コンプライアンス、責任あるギャンブルメッセージング。 | | `fair_housing` | `racial_ethnic_origin`、`religious_beliefs`、`sex_life_sexual_orientation` | 米国 FHA、州住宅法。住宅広告で保護された特性によるターゲティング/除外を禁止します。 | | `fair_lending` | `racial_ethnic_origin`、`religious_beliefs`、`sex_life_sexual_orientation` | 米国 ECOA、CFPB ガイダンス。クレジット/融資広告の差別的ターゲティングを禁止します。 | | `fair_employment` | `racial_ethnic_origin`、`religious_beliefs`、`sex_life_sexual_orientation`、`health_data`、`genetic_data` | 米国 EEOC(Title VII、ADA、GINA)、州雇用法。求人広告の差別的ターゲティングを禁止します。 | | `pharmaceutical_advertising` | `health_data` | FDA DTC、EU 処方薬広告禁止。公平なバランス、適応症制限。 | | `health_wellness` | `health_data` | FTC 健康主張、サプリメント広告。実証要件。 | | `firearms_weapons` | — | 銃器広告のプラットフォームレベルと管轄上の制限。 | カテゴリの `restricted_attributes` は権威ある — プランがポリシーカテゴリを宣言すると、プランが `restricted_attributes` でも宣言しているかどうかに関わらず、これらの属性はキャンペーンで自動的に制限されます。 ## 制限属性定義 レジストリは制限属性カテゴリを定義する — 規制が広告ターゲティングに対して制限する個人データの種類。これらは GDPR 第 9 条の特別カテゴリにマッピングされ、プラン、シグナル定義、ポリシーカテゴリ全体で使用されます。 各属性定義には以下が含まれます: | フィールド | 説明 | | ------------------ | ------------------------------------------------------ | | `attribute_id` | 一意の識別子(例: `health_data`、`racial_ethnic_origin`) | | `name` | 人間が読める名前 | | `description` | このカテゴリがカバーする個人データ | | `regulatory_basis` | 制限の法的根拠(例: "GDPR Article 9(1)") | | `includes` | このカテゴリに該当するデータの例 | | `excludes` | 関連しているように見えるが明示的にこのカテゴリ外の一般的なデータ | | `signal_patterns` | このデータに触れる可能性が高い未宣言のシグナルを検出するためにガバナンスエージェントが使用できる命名パターン | | `guidance` | 実装ガイダンス | ### 初期登録属性 | 属性 | 規制根拠 | 含まれるもの | | ----------------------------- | ------------- | -------------------------- | | `racial_ethnic_origin` | GDPR 第 9 条(1) | 人種、民族、国籍、部族の所属 | | `political_opinions` | GDPR 第 9 条(1) | 政党の所属、投票パターン、政治献金 | | `religious_beliefs` | GDPR 第 9 条(1) | 宗教、宗派、宗教的実践の指標 | | `trade_union_membership` | GDPR 第 9 条(1) | 組合員資格、団体交渉への参加 | | `health_data` | GDPR 第 9 条(1) | 医療状態、処方箋、健康行動、障害 | | `sex_life_sexual_orientation` | GDPR 第 9 条(1) | 性的指向、ジェンダーアイデンティティ、交際状況の指標 | | `genetic_data` | GDPR 第 9 条(1) | DNA プロファイル、遺伝子検査結果、遺伝性疾患 | | `biometric_data` | GDPR 第 9 条(1) | 指紋、顔の形状、声紋、歩行分析 | データプロバイダーはシグナル定義で `restricted_attributes` を宣言するときにこれらの定義を参照できます。[ガバナンスメタデータの宣言](/docs/signals/data-providers#declaring-governance-metadata)を参照。 ## ポリシーの提供 コミュニティメンバーは API または管理インターフェースで新しいポリシーを提供できます。提供されたポリシーは: * `policy_id`、`version`、`name`、`category`、`enforcement`、`policy` テキストを含む必要があります * `source_type: community` と `review_status: pending` で作成されます * レジストリで利用可能になる前にレビューを経る * レジストリソース(権威あります)のポリシーを上書きできません # ポリシーレジストリ: 同期とバージョニング Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/policy-registry-sync キャンペーンプランを AdCP ポリシーレジストリと同期に保つ運用パターン — バージョンピン留め、レジストリバージョンバンプ、effective_date 採用、サンセット動作、インラインポリシーの追加のみ不変条件。 キャンペーンガバナンスを設計するワーキンググループは、ポリシーがバイヤー、セラー、ガバナンスエージェント間でどう同期されるかについて同じ質問セットを尋ねます。このページは、5 つのセクションと FAQ で運用パターンを捕捉します。 ## プランにポリシーバージョンをピン留めできるか? **いいえ — `policy_ids[]` は今日バージョン修飾子を運びません。** キャンペーンプランは ID のみでレジストリポリシーを参照します: ```json theme={null} { "plan_id": "plan_q1_2027_acme", "policy_ids": ["us_coppa", "alcohol_advertising"], "policy_categories": ["age_restricted"] } ``` すべての [`check_governance`](/docs/governance/campaign/tasks/check_governance) 呼び出しで、ガバナンスエージェントは各 ID をレジストリに対して解決し、**現在のバージョンが何であれ** それを使います。プランレベルのバージョンピンフィールドはありません。同じプランの 2 つのチェック間でレジストリポリシーがバージョンバンプする場合、2 番目のチェックは新しいバージョンに対して評価します。 バイの期間中に決定的なポリシーテキストが必要な場合、レジストリポリシーをプランの [`custom_policies[]`](/docs/governance/campaign/tasks/sync_plans) にコピーします — 下の [Pinning by inline copy](#pinning-by-inline-copy) を参照。監査証跡はすべてのチェックで `policies_evaluated[]` を記録するため、履歴バージョンは [`/api/policies/history`](https://adcontextprotocol.org/api/policies/history) 経由でチェックごとに回復可能です。 これは他のアドテックプロトコル(TCF v2 の TC 文字列、OpenRTB GPP)が取る同じ姿勢です — バージョンはリクエスト作成時ではなく評価時に解決します。解決時最新が正しい 99% のケースで、バイヤーをバージョン依存管理ビジネスから外し続けます。 ## Pinning by inline copy 決定的なポリシーテキストが必要なとき — 規制当局の事前クリアランス、凍結されたブランドセーフティ契約、初日のポリシーに対して評価されなければならない複数月のブランドキャンペーン — 利用可能なパターンは、プラン作成時に **異なる `policy_id`** の下でレジストリポリシーを `custom_policies[]` にコピーすることです: ```json theme={null} { "plan_id": "plan_q1_2027_acme", "policy_ids": ["us_coppa"], "custom_policies": [ { "policy_id": "alcohol_advertising_pinned_2026Q4", "version": "2.1.0", "name": "Alcohol Advertising Standards (pinned to v2.1.0)", "category": "standard", "enforcement": "must", "policy": "", "exemplars": { "...": "copied from registry" } } ] } ``` **レジストリのものと異なる `policy_id` を使ってください。** バイヤーが `custom_policies` で正準 ID `alcohol_advertising` を再利用する場合、ガバナンスエージェントの動作は仕様で未定義です — [policy-entry スキーマ](https://adcontextprotocol.org/schemas/v3/governance/policy-entry.json) の追加のみルールは、その ID についてレジストリテキストを権威的としてピン留めします。`alcohol_advertising_pinned_2026Q4`(またはあなたの内部バージョニング規約)のようなピン留め ID はコンフリクトを回避します。 プランリビジョンは今や凍結されたテキストを運びます。インラインポリシーの `version` フィールドは情報的です — テキストが評価されるものです — が、監査人がインラインコピーを特定のレジストリリリースに相関できるようフォレンジックな追跡可能性のため設定する価値があります。 **ライフサイクル。** インラインコピーは、バイヤーがプランの各再同期で `custom_policies` にエントリーを保つ限り保持されます。ガバナンスエージェントの追加のみ `revisionHistory` は監査のため以前のプランリビジョンをアーカイブしますが、ライブ評価は常に最新の `sync_plans` ペイロードにあるものを使います — したがって再同期でインラインポリシーを落とすバイヤーは次のチェックでピンを失います。 **トレードオフ。** ピン留めされたポリシーはレジストリ訂正を拾いません。`alcohol_advertising` v2.2.0 が明確化を出荷する場合、ピン留めされたプランは誰かが手動で新しいテキストで `custom_policies` を再同期するまで v2.1.0 に対して評価し続けます。それが安定性の代償です。 ## インラインポリシーはレジストリに対して追加のみ インライン `custom_policies[]` は [policy-entry スキーマ](https://adcontextprotocol.org/schemas/v3/governance/policy-entry.json) からのハードな不変条件を運びます: それらはレジストリソースのポリシーの上に制限を **追加** することのみできます。インラインポリシーは、レジストリポリシーの `enforcement` レベルを緩和したり、レジストリポリシーが義務付けるカテゴリーを免除したり、そうでなければレジストリベースラインを弱めたりしてはなりません(MUST NOT)。任意のレジストリポリシーと交差しないバイヤー作成のインラインポリシーは制約されません — レジストリポリシーとの関係のみが統制されます。 具体的には、`policy_ids: ["us_coppa"]` と `custom_policies` エントリーの両方を持つプランを評価するガバナンスエージェントは、`us_coppa` をピン留めまたは拡張できます(例: リネームされた ID の下のブランド固有事例セット)が、「このキャンペーンでは COPPA を無視」と言うインラインポリシーを追加できません。監査エントリーで `policies_evaluated: ["us_coppa"]` を見る相手方は、したがってレジストリバージョンの `us_coppa` が宣言された `must` レベルで適用されたことを信頼できます — バイヤーは黙ってそれをダウングレードしませんでした。 これを検証したい相手方は、プランリビジョンをリクエストし `plan_hash` を再計算できます([キャンペーンガバナンス仕様](/docs/governance/campaign/specification#plan-binding-and-audit))。プランバインディングは、追加のみ不変条件を単に宣言されたものではなく検証可能にする暗号表面です。エージェント側の強制はダウングレードが起こることを防ぐものです。ハッシュは事後に決定を証明可能にするものです。 ## キャンペーン中のレジストリバージョンバンプの処理 プランがアクティブな間にレジストリポリシーがバージョンバンプするとき: | Plan state | Behavior | | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | プランが `policy_ids` のみでポリシーを参照 | 次の `check_governance` が新しいバージョンを解決。既にコミットされたバイはコミットされたまま(その監査エントリーが古いバージョンの解決タイムスタンプを記録)。新しいチェックが新しいテキストに対して評価。 | | プランが `custom_policies`(リネームされた ID の下)でポリシーをピン留め | プランはインラインテキストに対して評価し続ける。レジストリ変更は、バイヤーが新しいテキストで `custom_policies` を再同期するまでこのプランに影響しない。 | | プランが削除または完了 | 継続的なチェックなし。評価なし。監査ログはフォレンジック回復のため `policies_evaluated[]` を保持。 | 完了したバイのバージョン安定性を気にするバイヤーは何もする必要はありません — 監査証跡が評価されたものを捕捉します。*アクティブな飛行中の* バイの安定性を気にするバイヤーはインラインコピーパターンを使うべきです。 ## 段階的採用のための `effective_date` 「最初は最小限の制限」パターンは、プランごとの設定ではなくレジストリの文書化された動作です。ガバナンスエージェントは、ポリシー ID を参照するすべてのプランにわたって `effective_date` を自動的に尊重します: 1. **Day 0** — コミュニティがドラフトポリシーに合意。将来 60 日以上の `effective_date` でレジストリに公開。 2. **Day 0–60** — ポリシー ID を参照する任意のプランを評価するすべてのガバナンスエージェントが情報的発見を発行。バイヤーとセラーは何がフラグされたはずかを正確に見る。バイはブロックされない。 3. **Day 60** — `effective_date` が経過。同じ評価が今やポリシーの宣言された `enforcement` レベルでブロック。任意のプランで設定変更不要。 4. **Day 60+** — 段階的採用ウィンドウのバイヤーは、在庫とクリエイティブを調整する 2 か月のテレメトリーを持っていた。遅い開始者はハードなカットオーバーを得る。 `effective_date` は段階的採用の時間軸です。**スコープベースの段階化** — チャネル、管轄、`policy_categories` サブセットによるフェーズ — はレジストリレベルで行われる別の動きです: まず狭い管轄または狭いカテゴリーのポリシーを公開し、次により広いものを公開。2 つの軸は合成します。単一のポリシーがスコープ狭窄と時間段階化ウィンドウに同時に存在できます。 ## サンセット動作 レジストリポリシーがその `sunset_date` に到達すると、ガバナンスエージェントは後続のチェックでそれの評価を停止します。`policies_evaluated[]` にポリシーを記録した既存の監査エントリーは変わりません — 証跡はいつ何が評価されたかについて真実を伝えます。バイヤーからのアクションは不要です。サンセットされたポリシーはすべてのアクティブプランから自動的に脱落します。 サンセットされたポリシーが後継に置き換えられる場合(例: ある規制が別のものに取って代わる)、レジストリコントリビューターは両方を公開します: `sunset_date` が設定された古いエントリー、`effective_date` が設定された新しいエントリー。バイヤーは次のプランリビジョンで新しいエントリーを参照するよう `policy_ids[]` を更新します — 古い ID はそのサンセット日まで評価し続け、その後静かに停止します。 ## よくある質問 **キャンペーン中に規制が変わるとき飛行中のバイに何が起こるか?** [キャンペーン中のレジストリバージョンバンプの処理](#handling-registry-version-bumps-mid-campaign) の下のテーブルが答えです。短縮版: コミットされたバイはコミットされたまま。次のチェックが現在のものを解決。`custom_policies` 経由のピン留めが特定のテキストを凍結する方法です。 **新しい objective を追加するときプランは再評価するか?** プランリビジョン(監査ログエントリーの `plan_version`、プランが再同期するたびに記録)はそれ自体で解決されたポリシーテキストをリフレッシュしません — 次の `check_governance` は依然として構成されたとおりレジストリにヒットします。リビジョンでポリシーテキストをリフレッシュするには、新しいプランリビジョンで `policy_ids[]`(または `custom_policies` のインラインコピー)を変更します。 **どのバージョンが適用されたかを相手方にどう証明するか?** 3 つの層: (1) セラーの `governance_context` トークンが特定のチェックを相関、(2) そのチェックの監査ログエントリーが `policies_evaluated[]` と `plan_hash` を運ぶ、(3) レジストリの [`/api/policies/history`](https://adcontextprotocol.org/api/policies/history) エンドポイントが任意の `policy_id` の完全なリビジョンシーケンスを返すため、監査人はどのバージョンが履歴タイムスタンプでアクティブだったかをリプレイできます。 **管轄ごとのオーバーライド。** グローバル標準と管轄固有の締め付けの両方を宣言(例: `policy_ids: ["alcohol_advertising", "alcohol_advertising_norway"]`)。ガバナンスエージェントは両方を評価。追加のみルールは 2 つのうちより制限的なものが勝つことを意味します。 **ブランド固有拡張。** 共有レジストリに属さないルール(競合排除、ブランドボイスガイドライン、内部コンプライアンスフレームワーク)には `custom_policies[]` を使います。`policy_ids[]` と並んでそれらを参照します。 ## 関連 * [ポリシーレジストリ](/docs/governance/policy-registry) — レジストリ概念、ポリシー構造、シードされたポリシー、制限属性 * [キャンペーンガバナンス仕様](/docs/governance/campaign/specification) — プランバインディング、`plan_hash`、ガバナンスコンテキストライフサイクル * [監査証跡: 内部対共有可能ビュー](/docs/governance/campaign/audit-trail) — 評価履歴を相手方に表示する方法 * [`sync_plans`](/docs/governance/campaign/tasks/sync_plans) — `policy_ids`、`policy_categories`、`custom_policies` * [Annex III と Art 22 の義務](/docs/governance/annex-iii-obligations) — 人間レビューがいつ必要か # adagents.json 技術仕様 Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/property/adagents adagents.json はパブリッシャーが広告プロパティを宣言し、セールスエージェントに在庫販売を認可する AdCP のファイル仕様。 `adagents.json` ファイルは、パブリッシャーがプロパティを宣言し、セールスエージェントを認可するための標準的な手段を提供します。これは Property Governance の土台であり — どのプロパティが存在し、誰がそれらを販売できるかを定義します。 ### 統一宣言モデル `adagents.json` は、**プロパティ認可**と**シグナルデータプロバイダー**登録の両方の宣言メカニズムとして機能します。`/.well-known/adagents.json` の単一ファイルが、`properties` と `signals` のトップレベルフィールドの両方を同時に宣言できます。 ```json theme={null} { "version": "1.0", "properties": [ { "domain": "publisher.example.com", "agents": [ { "agent_url": "https://ads.publisher.example.com", "relationship": "direct" } ] } ], "signals": [ { "catalog_url": "https://signals.publisher.example.com/catalog.json", "relationship": "direct", "description": "First-party audience signals from publisher.example.com" } ] } ``` この結合モデルは、ファーストパーティデータを持つパブリッシャーで一般的です — 同じドメインがセールスエージェントを認可し(`properties` 経由)、公開されたシグナル定義を宣言します(`signals` 経由)。2 つの名前空間は独立しています: プロパティ販売の認可はシグナルアクセスを付与せず、シグナル登録はプロパティ認可を意味しません。 シグナル側のドキュメントについては [シグナルデータプロバイダー](/docs/signals/data-providers) を参照。 パブリッシャー認可をオペレーターの `brand.json` アイデンティティと署名鍵ディスカバリーとペアリングするセルサイドの決定木については、[セラーセットアップ](/docs/brand-protocol/seller-setup) を参照。 **[AdAgents.json Builder](https://agenticadvertising.org/adagents/builder)** - 既存ファイルの検証やガイド付きでの新規作成に利用できます ## Why `adagents.json` instead of `ads.txt` `ads.txt` はより狭い問いに答えます: このセラーはパブリッシャーのリストに存在するか、関係は `DIRECT` か `RESELLER` とラベル付けされているか? それは有用ですが、多くの現代的なパブリッシャー販売モデルにとって平坦すぎます。バイヤーに次を伝えません。 * どのプロパティがカバーされているか * どのプレースメントがカバーされているか * パスが直接、委任、ネットワーク仲介のいずれか * 認可が国限定または時間限定か * ネットワーク管理のスロットがパブリッシャー管理のプレミアムプレースメントと同じものか `adagents.json` はその構造を運ぶよう設計されています。パブリッシャーがプロパティアイデンティティ、プレースメントアイデンティティ、委任タイプ、スコープ付き認可、パブリッシャー定義のグルーピングタグを 1 か所で宣言できます。 | Question | `ads.txt` | `adagents.json` | | ----------------------------------- | --------- | ------------------------- | | このセラーはそもそも宣言されているか? | Yes | Yes | | どのプロパティがカバーされているか? | No | Yes | | どのプレースメントがカバーされているか? | No | Yes | | パブリッシャーはインベントリを管理されたバケットにグループ化できるか? | No | Yes(`placement_tags` 経由) | | 認可は国や時間ウィンドウで変わりうるか? | No | Yes | | パスを直接、委任、ネットワーク仲介として記述できるか? | 非常に弱く | Yes(`delegation_type` 経由) | より高レベルのフレーミングと並列比較の例については、[Why adagents.json is more expressive than ads.txt](https://agenticadvertising.org/perspectives/adagents-json-vs-ads-txt) を参照。 ### Where does sellers.json fit? プログラマティックでは、`sellers.json` はセラー/エクスチェンジによってホストされ、彼らが代表するパブリッシャーを宣言します。AdCP は、別個のファイルの代わりに `brand.json` を通じてこれを処理します。オペレーターは、`relationship` フィールドを使って [`brand.json`](/docs/brand-protocol/brand-json) でプロパティを宣言します。ファーストパーティインベントリについては、オペレーターは `relationship: "owned"` を使えます。委任またはネットワークのセルサイドパスについては、`relationship` は `delegation_type` と同じ値を使います: `direct`、`delegated`、または `ad_network`。これにより、同じ双方向検証パターンが作られます。 | Programmatic | AdCP equivalent | Purpose | | ---------------------------------------------------- | ---------------------------------------------- | ------------------------------- | | `ads.txt`(パブリッシャー) | `delegation_type` を持つ `adagents.json`(パブリッシャー) | 「これらのエージェントは認可されている、これが関係」 | | `relationship` を持つ `brand.json` の properties(オペレーター) | | 「私はこれらのパブリッシャーのために販売する、これがその方法」 | 委任またはネットワークパスについては、両側が合意しなければなりません — `delegation_type` と `relationship` の値は一致すべきです。ファーストパーティインベントリについては、`relationship: "owned"` はインラインの所有権宣言であり、一致する `delegation_type` 値はありません。実際にこれがどう機能するかについては [アドネットワーク](/docs/sponsored-intelligence/networks) を参照。 フィールドは adagents.json では `delegation_type`、brand.json では `relationship` と呼ばれます。名前が異なるのは、同じ商業的取り決めを異なる視点から記述するためです — パブリッシャーが権限を委任し(`delegation_type`)、オペレーターがプロパティへの関係を宣言します(`relationship`)。委任/ネットワークの値は一致します(`direct`、`delegated`、`ad_network`)。`owned` は brand.json の relationship 値のみです。 ## 配置場所 パブリッシャーは `adagents.json` を次の場所に配置する必要があります: ``` https://example.com/.well-known/adagents.json ``` [RFC 8615](https://datatracker.ietf.org/doc/html/rfc8615) の well-known URI に従うことで、一貫した発見性を確保します。 パブリッシャーのオリジンが HTTP リダイレクトでホスト名を正規化する場合、最終的に解決された URL にもファイルをデプロイします。例えば、`https://example.com/.well-known/adagents.json` が `https://www.example.com/.well-known/adagents.json` にリダイレクトする場合、`www` の URL は `200` レスポンスで JSON ファイルを提供しなければなりません。 `404` で終わるリダイレクトチェーンは、正規ホストでファイルが欠けていることを意味します。中間の `301` や `302` だけでなく、終端のステータスと解決された URL をトラブルシューティングしてください。 ## 基本構造 ファイルは UTF-8 の有効な JSON で、HTTP 200 を返す必要があります。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "contact": { "name": "Example Publisher Ad Operations", "email": "adops@example.com", "domain": "example.com", "seller_id": "pub-example-12345", "tag_id": "67890" }, "properties": [ { "property_id": "example_site", "property_type": "website", "name": "Example Site", "identifiers": [ {"type": "domain", "value": "example.com"} ] } ], "authorized_agents": [ { "url": "https://agent.example.com", "authorized_for": "Official sales agent", "authorization_type": "property_ids", "property_ids": ["example_site"] } ], "last_updated": "2025-01-10T12:00:00Z" } ``` ## スキーマフィールド **`$schema`** *(任意)*: 検証用の JSON Schema 参照 **`contact`** *(任意)*: ファイル管理主体の連絡先 * **`name`** *(必須)*: 管理主体名(パブリッシャーまたは第三者) * **`email`** *(任意)*: 問い合わせ先メール * **`domain`** *(任意)*: 管理主体のドメイン * **`seller_id`** *(任意)*: IAB Tech Lab sellers.json の Seller ID * **`tag_id`** *(任意)*: TAG Certified Against Fraud ID * **`privacy_policy_url`** *(任意)*: 消費者同意フロー用のプライバシーポリシー URL **`catalog_etag`** *(任意)*: このファイルの公開カタログ部分の不透明なキャッシュ/バージョントークン * パブリッシャーは、`properties`、`collections`、`placements`、`formats`、`signals`、またはそれらのタグメタデータが変わるたびにこれを変更すべきです(SHOULD) * バイヤー SDK は、解決された参照を URL + `catalog_etag` でキャッシュし、値が変わったときにカタログ参照を再解決すべきです(SHOULD) * 存在しない場合、バイヤーは `ETag`/`Last-Modified` などの HTTP バリデーター、次に上限付き TTL にフォールバックします **`properties`** *(任意)*: このファイルで扱うプロパティの配列(正規定義) * **`supported_channels`** *(任意)*: このプロパティがサポートする広告チャネルの配列(例: `["display", "olv", "social"]`)。[Media Channel Taxonomy](/docs/reference/media-channel-taxonomy) を参照。 **`collections`** *(任意)*: このパブリッシャーが制作または配信するコレクション * プロダクトは `publisher_domain` と `collection_ids` を持つ `collections` セレクターを通じてこれらを参照します * 認可を特定のシリーズ、ポッドキャスト、ストリーム、または定期的なコンテンツプログラムにスコープする必要がある場合に有用 **`placements`** *(任意)*: このファイル内のプロパティの正規プレースメント定義 * プロダクトは `placements` を宣言する際にこれらの `placement_id` 値を再利用すべきです(SHOULD) * 登録済み `placement_id` を再利用することは、プロダクトが同じセマンティックプレースメントを指していることを意味し、同じ ID で別のものを発明していないことを意味します * プレースメント定義には、プロパティリンクのための `tags`、`property_ids` または `property_tags`、`channels`、クリエイティブサポートのための `format_options` を含められます * `adagents.json` のプレースメントは定義上公開です。セラー非公開のプレースメント ID、source/origin フィールド、配信システムマッピングをこのファイルに公開しないでください * 認可エントリはスコープを特定の `placement_ids` に狭められます * 認可エントリは、`programmatic`、`direct_only`、`managed_by_riverline` などの管理されたプレースメントグルーピングのために `placement_tags` も使えます * 「このエージェント経由ではホームページネイティブフィードのみ利用可能」や「プレロールのみ」のような区別を表現するのに有用 **`tags`** *(任意)*: 人間可読なコンテキストを提供し効率的なグルーピングを可能にするタグメタデータ **`placement_tags`** *(任意)*: パブリッシャー定義のプレースメントタグのメタデータ * `placements[*].tags` と `authorized_agents[*].placement_tags` で使われるプレースメントタグ値の人間可読な定義を提供します * これらはパブリッシャーローカルな概念であり、グローバルタクソノミーではありません ### Public placement catalog `placements[]` 配列はパブリッシャーの公開プレースメントカタログです。プロダクトと認可ルールが参照できる安定したセマンティックなプレースメント ID を定義します。バイヤーは、プロダクトが何を提供するかを理解するために、生のアドサーバー広告ユニットパス、配信プレースメント ID、ビデオアドサーバーゾーン、またはその他のセラー内部の配信識別子を解釈する必要があるべきではありません。 プレースメント、フォーマット、コレクション、プロパティのカタログ、または公開された `signals[]` 定義を公開するパブリッシャーは、`catalog_etag` を公開し、それらのエントリが変わるたびに更新すべきです。これにより、バイヤー SDK は、パブリッシャーのデプロイ後に同じ `{publisher_domain, placement_id}` や `{publisher_domain, format_option_id}` を黙って異なるメタデータに解決することなく、カタログルックアップをキャッシュできます。 最低限、公開プレースメントは次を記述すべきです。 * 安定した `placement_id` * `name` と `description` * それが実行できる `property_ids` または `property_tags` * サポートされる `format_options`(パブリッシャー所有フォーマットと正準フォーマットを参照できる) * 任意の `channels` プレースメントのフォーマットサポートは新しい 3.1 のカタログ機能であり、3.1+ の正準フォーマットオプションモデルのみを使います。 * `format_options[]` は、同じファイルのトップレベル `formats[]` の宣言を `format_option_id` で参照できます。それらのトップレベル宣言は、パブリッシャー所有のカスタムフォーマットまたは狭められた正準フォーマットです。 * `format_options[]` は、プレースメント固有の狭めが再利用可能なトップレベルフォーマットエントリに値しない場合、インラインの正準 `ProductFormatDeclaration` を運ぶこともできます。 正準アンカーは `format_kind` です。`{ "format_option_id": "..." }` のみを運ぶプレースメントエントリは、その ID を同じファイルのトップレベル `formats[]` 宣言に解決し、その `format_kind` を読むことで正準フォーマットを継承します。このファイルの外では、パブリッシャー宣言のフォーマットオプションのバイヤー向け `FormatOptionRef` は `{ "scope": "publisher", "publisher_domain": "...", "format_option_id": "..." }` を使います。 プレースメントカタログのフォーマットは、公開プレースメントが何をサポートできるかを記述します。プロダクトが後でそのプレースメントを参照する場合、プロダクトレベルの `format_ids` または `format_options` が購入可能なクリエイティブコントラクトのままです。プレースメントの `format_options` は特定のプレースメントについてそのセットを狭めます。カタログのプレースメントとプロダクトの宣言が食い違う場合、バイヤーは交差を使い、カタログのみのフォーマットをプロダクトに受け入れられたものとして扱うべきではありません。 ```json theme={null} { "catalog_etag": "daily-pulse-2026-05-25", "formats": [ { "format_kind": "html5", "format_option_id": "publisher_takeover_html5", "display_name": "Publisher takeover HTML5", "params": { "width": 970, "height": 250, "max_file_size_kb": 200 } } ], "placements": [ { "placement_id": "homepage_takeover", "name": "Homepage takeover", "description": "High-impact homepage sponsorship across the main article rail and top video module.", "property_ids": ["daily_pulse"], "channels": ["display", "olv"], "format_options": [ { "format_option_id": "publisher_takeover_html5" }, { "format_kind": "image", "params": { "width": 300, "height": 250, "image_formats": ["jpg", "png"], "max_file_size_kb": 150 } } ] } ] } ``` `adagents.json` は公開であるため、運用インベントリの内部を公開する場所ではありません。source/origin(`synced` 対 synthetic)、生のアドサーバー ID、配信マッピング、セラー非公開のプレースメントグルーピングは、セラーの内部インベントリレジストリに保持してください。公開プレースメントカタログは、パブリッシャーが発見可能にしたいバイヤーが理解できるインベントリのみを記述すべきです。 ### Product targetability `adagents.json` は、プレースメントがプロダクトでターゲット可能かどうかを決定しません。安定した公開プレースメント ID とそのバイヤー理解可能なセマンティクスを公開するだけです。 セールスエージェントは、公開プレースメントがバイヤー選択可能か、単にプロダクト構成の一部かを、プロダクトごとに決定します。その決定は、必要なときに、パブリッシャーの `adagents.json` ではなくセールスエージェントの `get_products` プロダクトプレースメントオブジェクトで公開されます。 プロダクトがセラー非公開の配信構成に依存する場合、その構成をプロダクトの文章で記述し、基礎となるプレースメント ID をセラーシステムに保持してください。非公開のプレースメント ID を `adagents.json` や `get_products` に公開しないでください。 ### Internal mapping パブリッシャーは依然として公開プレースメントを配信システムにマッピングする必要があり、配信システムに適切なセマンティックオブジェクトがない場合、合成的な内部グルーピングが必要になる場合があります。そのマッピングは実装の詳細であり、公開プロトコル状態ではありません。 一般的な内部ケース: * 複数の synced プレースメントのグルーピング * 複数の広告ユニットまたはゾーンのグルーピング * 広告ユニットをバイヤー理解可能なプレースメントとして公開 * 曖昧な `1x1`、fluid、native、out-of-page、または video オブジェクトを実際にレンダリングするフォーマットにマッピング * 生の配信 ID からプロダクトを解放しながら戦略的な不透明性を保持 これらの内部マッピングは公開 `placements[]` エントリを生成できますが、マッピングの詳細自体は `adagents.json` の外に留まります。 **`authorized_agents`** *(必須)*: 認可されたセールスエージェントの配列。AdCP を採用していないプラットフォームのために `formats`/`properties`/`placements`(通常は `catalog_etag` 付き)を公開する**カタログのみのコミュニティミラー** — 認可するセールスエージェントがないファイル — では空(`[]`)でもかまいません(MAY。[コミュニティミラーライフサイクル](#community-mirror-lifecycle)を参照)。空の配列は**セールス認可なし**を主張します: バリデーターはそれを deny-all、authorize-all、または失効として読んではならず(MUST NOT)、その存在をエラーとして扱ってはならず(MUST NOT)、依然としてカタログ配列を消費しなければなりません(MUST)。セールス認可もカタログコンテンツもないファイルは無効です。下記のエントリごとのフィールドは配列が空でない場合にのみ適用されます。 * **`url`** *(必須)*: エージェントの API エンドポイント URL * **`authorized_for`** *(必須)*: 人間可読な認可の説明 * **`authorization_type`** *(必須)*: どのセレクターフィールドがスコープを運ぶかを名付ける識別子。`property_ids`、`property_tags`、`inline_properties`、`publisher_properties`(プロパティ用)または `signal_ids`、`signal_tags`(シグナルプロバイダー用)のいずれか。対応するセレクターフィールドが存在し空でない必要があります — 下記 [認可パターン](#認可パターン) を参照。 * **`delegation_type`** *(任意)*: このパスの商業的関係: `direct`、`delegated`、または `ad_network` * **`collections`** *(任意)*: 認可を特定のコンテンツプログラムに狭める追加のコレクションセレクター * **`placement_ids`** *(任意)*: 認可を特定のプレースメントに狭めるトップレベル `placements` 配列からのプレースメント ID * **`placement_tags`** *(任意)*: 認可を管理されたプレースメントグループに狭めるパブリッシャー定義のプレースメントタグ * **`countries`** *(任意)*: 認可が適用される場所を制限する ISO 3166-1 alpha-2 国コード * **`effective_from` / `effective_until`** *(任意)*: 認可の時間ウィンドウ * **`exclusive`** *(任意)*: これがスコープされたインベントリスライスに対するパブリッシャーの唯一の認可されたパスかどうか * **`signing_keys`** *(任意)*: 署名付きエージェントレスポンスを検証する際にバイヤーがピン留めできる、パブリッシャーが証明した公開鍵 * **`last_updated`** *(任意)*: この `authorized_agents[]` エントリが最後に変わった ISO 8601 タイムスタンプ。ファイルレベルの `last_updated` とは独立。アドバイザリ — バリデーターが部分的なウォークで変更されていないエントリをスキップできるようにします。それが可能にする条件付きリフレッシュプロトコルについては [managed-networks security](/docs/governance/property/managed-networks#security-considerations) を参照。 * **追加フィールド**: authorization\_type に依存(後述のパターン参照) **`revoked_publisher_domains`** *(任意、管理ネットワーク用)*: 管理ネットワークの権威あるファイルから明示的に削除されたパブリッシャードメインのトップレベル配列。各エントリは `publisher_domain`、`revoked_at`(ISO 8601)、任意の `reason` を持ちます。バリデーターは、ファイルの他の場所に現れるかどうかに関係なく、リストされたドメインをもはや認可されていないものとして扱わなければなりません(MUST)。運用ライフサイクルとバリデーター側の耐久性ルールについては [Publisher revocation](/docs/governance/property/managed-networks#publisher-revocation-the-exit-lifecycle) を参照。 **`last_updated`** *(任意)*: 最終更新の ISO 8601 タイムスタンプ **`property_features`** *(任意)*: このファイル内のプロパティに関するデータを提供するガバナンスエージェントの配列 * **`url`** *(必須)*: エージェントの API エンドポイント URL(プロパティガバナンスタスクを実装するガバナンスエージェント) * **`name`** *(必須)*: ベンダー/エージェントの人間可読な名前 * **`features`** *(必須)*: このエージェントが提供する Feature ID の配列(例: `["carbon_score", "mfa_score"]`) * **`publisher_id`** *(任意)*: そのエージェント側でのパブリッシャー識別子(ルックアップ用) このフィールドにより **ガバナンスエージェントの発見** が可能になり、バイヤーは全エージェントを総当たりせずに、どのエージェントがコンプライアンス/サステナビリティ/品質データを持つかを把握できます。 ## Community mirror lifecycle プラットフォームが AdCP を採用していない場合(例: 独自の `adagents.json` を公開していないウォールドガーデン)、AdCP コミュニティレジストリはプラットフォームに代わって**カタログのみのコミュニティミラー**を公開できます — 通常は `https://creative.adcontextprotocol.org/translated//adagents.json` でホストされます。ミラーは、プラットフォームが自己採用する前にバイヤーがプラットフォームのインベントリ形状について推論できるよう、ディスカバリーメタデータ(`formats`、`properties`、`placements`)を公開するために存在します。 コミュニティミラーは: * **`authorized_agents: []`** を設定します — 認可するセールスエージェントがなく、ミラーは 1 つを捏造してはなりません(MUST NOT)。空の配列は*セールス認可なし*を主張します。バリデーターはそれを deny-all、authorize-all、または失効として読んではならず(MUST NOT)、依然としてカタログ配列を消費しなければなりません(MUST)。 * 少なくとも 1 つの空でないカタログ配列(`formats`/`properties`/`placements`/`collections`/`signals`)を運ばなければならず(MUST)、**`catalog_etag`** キャッシュバリデーターを運ぶべきです(SHOULD。バリデーターは `catalog_etag` ではなく配列を強制します)。セールス認可もカタログコンテンツもないファイルは無効です。 * プラットフォームが独自の権威ある `adagents.json` を公開したら **`superseded_by`** を設定します。`superseded_by` に遭遇したバイヤー SDK は、古いミラーを提供するのではなく、名付けられた URL から再取得すべきです(SHOULD)。ミラーは、ミラー URL をキーとするバイヤーキャッシュが明示的な移行シグナルを得られるよう、少なくとも 1 つのマイナーリリースの間、`superseded_by` を設定したまま提供を続けるべきです(SHOULD)。 [`static/examples/adagents/community/meta.json`](https://github.com/adcontextprotocol/adcp/blob/main/static/examples/adagents/community/meta.json) のワークド例を参照。 ## URL 参照パターン 複雑なインフラや CDN 配信を行うパブリッシャーは、全文を埋め込む代わりに信頼できる URL への参照を記載できます。 ### URL 参照を使う場合 * **CDN 配信**: 認可データをグローバル CDN から配信 * **集中管理**: 複数ドメインを単一のソースで管理 * **大規模ファイル**: インライン埋め込みには大きすぎる場合 * **動的更新**: ドメイン上のファイルを触らず頻繁に更新したい場合 ### URL 参照の構造 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "authoritative_location": "https://cdn.example.com/adagents/v2/adagents.json", "last_updated": "2025-01-15T10:00:00Z" } ``` ### 要件 * **HTTPS 必須**: `authoritative_location` は HTTPS を使用 * **入れ子禁止**: 参照先がさらに URL 参照であってはなりません(無限ループ防止) * **同一スキーマ**: 参照先は有効なインライン adagents.json 構造であること * **1 ホップのみ**: URL 間接参照は 1 段階まで ## Discovery fallback: ads.txt `managerdomain` これは既存の ads.txt 時代のパブリッシャー・マネージャー設定のための**レガシー互換フォールバック**です。 AdCP の管理ネットワークデプロイでは、規範的な委任パターンは依然としてパブリッシャー自身の `/.well-known/adagents.json` ポインターファイルの [`authoritative_location`](/docs/governance/property/managed-networks) です。 新しいデプロイはそのパターンを使うべきです(SHOULD)。 `https://{publisher}/.well-known/adagents.json` が `404` を返す、または S3/CloudFront スタイルの `403` `AccessDenied` XML レスポンスを返す場合、バリデーターは `https://{publisher}/ads.txt` を通じて互換フォールバックを試みてもかまいません(MAY)。 1. `ads.txt` を読み、`managerdomain` エントリをパースします。 * 受け入れられる形式: `MANAGERDOMAIN=example.com`(IAB ディレクティブ形式のみ)。 * キーマッチングは大文字小文字を区別しません(`MANAGERDOMAIN`、`managerdomain` など)。 * このフォールバックをサポートするバリデーターは、`ads.txt` を取得する際に、各リダイレクトホップに同じ SSRF とパブリックホストのチェックを適用しながら、上限付きの HTTP リダイレクトチェーンに従うべきです(SHOULD)。 2. 1 つ以上の適格な managerdomain エントリが残る場合、ファイル順で**最後**の適格エントリを使い、`https://{managerdomain}/.well-known/adagents.json` を試みます。 3. そのマネージャーファイルが検証され、**認可をソースパブリッシャードメインに明示的にスコープする**場合、このルックアップで発見された認可ソースとして扱います。 ### Safety rules for this fallback * **1 ホップのみ**: 最大深さは正確に 1(`publisher -> managerdomain`)です。managerdomain ルックアップをチェーンしないでください。 * **サイクル検出が必須**: `managerdomain` が訪問済みドメインを指す場合、無視します。 * **`#noagents` オプトアウト**: managerdomain 行に `noagents` トークン(大文字小文字を区別しない)を含む末尾コメントがある場合、クライアントは adagents ディスカバリーについてその managerdomain を無視しなければなりません(MUST)。例: `MANAGERDOMAIN=example.com #NOAGENTS`。 * **トリガーステータスは狭い**: バリデーターは、パブリッシャーの直接の `adagents.json` フェッチが `404` または S3/CloudFront スタイルの `403` `AccessDenied` XML レスポンスを返す場合にのみ、このフォールバックを試みるべきです(SHOULD)。その他のステータスと失敗 — 汎用的な `403` 拒否、`500`、タイムアウト、不正な JSON、content-type 不一致、スキーマ検証失敗 — は `managerdomain` をトリガーしません。 * **明示的なパブリッシャースコープが必須**: マネージャーがホストする `adagents.json` は、少なくとも 1 つの `authorized_agents[]` エントリから到達可能な `publisher_domain` フィールドで、ソースパブリッシャードメインを積極的に名付けなければなりません(MUST)。「到達可能」とは、次のパスの 1 つがソースドメインに解決することを意味します。 1. **エージェントごとのパス。** エージェントエントリが、`publisher_properties[].publisher_domain` の下、`publisher_properties[].publisher_domains[]` の内部(コンパクトな管理ネットワーク形式)、または `collections[].publisher_domain` の下で、パブリッシャードメインを直接運ぶ。 2. **プロパティレベルのパス。** エージェントエントリが 1 つ以上のトップレベル `properties[]` エントリを参照する — エージェントエントリ上の ID/タグで直接(`property_ids` / `property_tags` の authorization\_type)、または一致する `publisher_domain` を運ぶ親ファイルの `properties[]` によって述語が満たされる `publisher_properties` セレクターを通じて間接的に(下記 [Resolution paths](#resolution-paths) を参照)— そして少なくとも 1 つの解決されたプロパティがソースに一致する `publisher_domain` を運ぶ。これは、プロパティが `publisher_domain` を一度宣言し多くのエージェントが間接的に参照する、Mediavine や他の管理ネットワークが本番で使う形状です。 このルールを**満たさない**もの: `publisher_domain` を省略するインラインプロパティを持つ `inline_properties` セレクター、または解決されたトップレベルプロパティが一致する `publisher_domain` を運ばないトップレベル `property_tags` セレクター。到達可能な `publisher_domain` フィールドがソースに一致しない場合、フォールバックはフェイルクローズしなければなりません(MUST)。 これが保護する攻撃は暗黙的スコープ — マネージャーがドメインをタイプしなければならない場所のどこにもパブリッシャーを名付けない認可 — です。`properties[].publisher_domain` を通じた間接参照は安全です。マネージャーが同じマニフェストでパブリッシャーのドメインを積極的に綴っているからです。ゲートは、参照を考慮する前に `publisher_domain` がソースに一致するプロパティにフィルタリングします。 * **沈黙による成功なし**: マネージャールックアップが失敗した場合、パブリッシャーを `adagents.json` が欠けているものとして扱います(フォールバックなしと同じ)。 このフォールバックは、パブリッシャー・マネージャートポロジーのための互換アフォーダンスであり、正準の `/.well-known/adagents.json` の場所を置き換えるものではありません。 ### ユースケース: 複数ドメインを持つパブリッシャー 複数ドメインを持つパブリッシャーは 1 つの権威ファイルを維持できる: **各ドメイン上** (`https://domain1.com/.well-known/adagents.json`, `https://domain2.com/.well-known/adagents.json` など): ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "authoritative_location": "https://cdn.publisher.com/adagents/v2/adagents.json", "last_updated": "2025-01-15T10:00:00Z" } ``` **権威ファイル** (`https://cdn.publisher.com/adagents/v2/adagents.json`): ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "contact": { "name": "Publisher Ad Operations", "email": "adops@publisher.com" }, "properties": [ { "property_id": "domain1_site", "property_type": "website", "name": "Domain 1", "identifiers": [{"type": "domain", "value": "domain1.com"}], "publisher_domain": "domain1.com" }, { "property_id": "domain2_site", "property_type": "website", "name": "Domain 2", "identifiers": [{"type": "domain", "value": "domain2.com"}], "publisher_domain": "domain2.com" } ], "authorized_agents": [ { "url": "https://sales-agent.publisher.com", "authorized_for": "All publisher properties", "authorization_type": "property_ids", "property_ids": ["domain1_site", "domain2_site"] } ], "last_updated": "2025-01-15T09:00:00Z" } ``` ### 検証時の挙動 AdCP の検証で URL 参照が見つかった場合: 1. **参照取得**: `/.well-known/adagents.json` を取得 2. **参照検出**: `authoritative_location` を確認 3. **URL 検証**: `authoritative_location` が HTTPS かつ有効か確認 4. **参照先取得**: `authoritative_location` の内容を取得 5. **ループ防止**: 参照先がさらに参照でないことを確認 6. **構造検証**: 参照先を通常のインライン構造として検証 ### Troubleshooting authoritative\_location failures URL 参照を通じてエージェントを登録するパブリッシャーは、これらの失敗モードによく遭遇します。サポートに問い合わせる前にこのチェックリストを使ってください。 **オリジンが認証または IP 制限を要求する** バリデーターは `authoritative_location` をサーバー側で取得します — サーバー間リクエストに CORS レスポンスヘッダーは不要です。しかし、オリジンが認証を要求する場合(例: 制限的なバケットポリシーを持つ S3 バケット、未知の IP をブロックするオリジン保護を持つ CDN)、フェッチは `403` で拒否されます。URL が認証なしで公開的に到達可能であることを確認してください。 **authoritative URL でのリダイレクト** バリデーターは `authoritative_location` URL 上の任意の HTTP リダイレクトを拒否します。URL は `200` レスポンスで JSON ファイルに直接解決しなければなりません — `301`、`302`、その他のリダイレクトチェーンなし。CDN やホスティングが URL をリダイレクトする場合(HTTP→HTTPS 正規化、`www` リダイレクト、バージョン付きパスリダイレクト)、`authoritative_location` を最終的な宛先 URL を直接指すように更新してください。 **誤った Content-Type** レスポンスは `Content-Type: application/json` で提供されなければなりません。`text/html` や `text/plain` を返すサーバーは — ボディが有効な JSON を含む場合でも — content-type 検証に失敗します。CDN やオリジンのレスポンスヘッダーを確認してください。 **200 ステータスの HTML エラーページ** 一部の CDN は、`4xx` ステータスの代わりに `200 OK` で HTML エラーページを返します。バリデーターは `Content-Type` ヘッダーをチェックし JSON パースを試みます。HTML ボディはステータスが `200` でもパースに失敗します。バリデーター出力で `200` ステータスとともにパースエラーを探してください。 **デバッグチェックリスト** これらの失敗を診断するために、バリデーターのフェッチをターミナルから再現します。 ```bash theme={null} curl -v \ -H "Accept: application/json" \ "https://cdn.example.com/adagents.json" ``` 出力で次を確認します。 * レスポンスステータスが `200`(`301`、`302`、`403`、`404` ではない) * `Content-Type` ヘッダーが `application/json` * レスポンスボディが `properties`、`signals`、`authorized_agents` の少なくとも 1 つを含む有効な JSON * ボディが `authoritative_location` フィールドを含**まない**(入れ子参照は拒否される) クローラーが診断エンドポイントを公開している場合、構造化されたエラー出力のために URL をそこに通してください。 ### キャッシュ推奨 * 参照ファイルは最低 24 時間キャッシュ * 権威ファイルは別途 TTL を設定してキャッシュ * `last_updated` でキャッシュ無効化を判断 * 取得失敗時は指数バックオフを実装 ## 認可パターン AdCP は 4 種の認可パターンをサポートし、用途に最適化されています: ### パターン 1: Property IDs(直接参照) **最適な用途**: 具体的で列挙可能なプロパティリスト。明確で曖昧さがない。 **構造**: ```json theme={null} { "properties": [ { "property_id": "cnn_ctv_app", "property_type": "ctv_app", "name": "CNN CTV App", "identifiers": [ {"type": "roku_store_id", "value": "12345"} ] } ], "authorized_agents": [ { "url": "https://cnn-ctv-agent.com", "authorized_for": "CNN CTV properties", "authorization_type": "property_ids", "property_ids": ["cnn_ctv_app"] } ] } ``` **仕組み**: エージェントは `property_ids` 配列に列挙された特定プロパティのみを認可されます。プロパティはトップレベルの `properties` 配列で定義されていなければなりません。 ### パターン 2: Property Tags(効率的なグルーピング) **最適な用途**: 1 つのタグで数百〜数千のプロパティを参照できる大規模ネットワーク。全 property\_id を列挙せずにグルーピング効率を実現します。 **重要な観点**: タグは単なる「人が読めるメタデータ」ではなく、**パフォーマンス最適化**です。500 プロパティを持つパブリッシャーは 1 つのタグで全プロパティを認可でき、500 個の property\_id を列挙する必要がない。 **構造**: ```json theme={null} { "properties": [ { "property_id": "instagram", "property_type": "mobile_app", "name": "Instagram", "identifiers": [ {"type": "ios_bundle", "value": "com.burbn.instagram"} ], "tags": ["meta_network", "social_media"] }, { "property_id": "facebook", "property_type": "mobile_app", "name": "Facebook", "identifiers": [ {"type": "ios_bundle", "value": "com.facebook.Facebook"} ], "tags": ["meta_network", "social_media"] } ], "tags": { "meta_network": { "name": "Meta Network", "description": "All Meta-owned properties - enables one tag to authorize entire network" } }, "authorized_agents": [ { "url": "https://meta-ads.com", "authorized_for": "All Meta properties", "authorization_type": "property_tags", "property_tags": ["meta_network"] } ] } ``` **仕組み**: エージェントはリストされたタグのいずれかを持つすべてのプロパティを認可されます。プロパティは各プロパティ定義の `tags` 配列と照合されます。 ### パターン 3: Inline Properties **最適な用途**: トップレベルのプロパティ宣言なしに小規模・特定のプロパティ集合を扱う場合。 **構造**: ```json theme={null} { "authorized_agents": [ { "url": "https://agent.com", "authorized_for": "Specific inventory", "authorization_type": "inline_properties", "properties": [ { "property_type": "website", "name": "Example Site", "identifiers": [ {"type": "domain", "value": "example.com"} ] } ] } ] } ``` **仕組み**: プロパティはトップレベルの `properties` 配列ではなく、エージェント認可エントリ内で直接定義されます。各エージェントが固有のプロパティ定義を持つ場合に便利。 ### パターン 4: Publisher Property References **最適な用途**: 複数のパブリッシャーを代表するサードパーティエージェント。プロパティ定義の単一のソース・オブ・トゥルース。 **構造**: ```json theme={null} { "contact": { "name": "Third-Party CTV Network" }, "authorized_agents": [ { "url": "https://ctv-network.com/api", "authorized_for": "CTV inventory from multiple publishers", "authorization_type": "publisher_properties", "publisher_properties": [ { "publisher_domain": "cnn.com", "selection_type": "by_tag", "property_tags": ["ctv"] }, { "publisher_domain": "espn.com", "selection_type": "by_tag", "property_tags": ["ctv"] } ] } ] } ``` **仕組み**: エージェントは他のパブリッシャーの adagents.json ファイルからプロパティを参照します。`publisher_domain` でパブリッシャーを指定し、`selection_type` でプロパティの解決方法(`by_id` または `by_tag`)を決定します。 ### Resolution paths `publisher_properties` セレクターは、2 つの方法のいずれかでプロパティに解決します。フェデレーテッドがデフォルトで信頼のルートです。親ファイルインラインは、親ファイルがセレクターをローカルに解決するのに十分な情報を運ぶときにコンシューマーが取ってもよい(MAY)ドメインごとの最適化です。 **1. フェデレーテッド解決(デフォルト)。** `publisher_domain` または `publisher_domains[]` の各ドメインについて、そのパブリッシャーの `adagents.json` を取得し、セレクター述語をパブリッシャー自身のトップレベル `properties[]` に適用します。リストされた各ドメインは**独立して並行に**解決されます。 * リストされたパブリッシャーの `adagents.json` が到達不能(404、5xx、タイムアウト、自身の検証に失敗)な場合、セレクターは**そのパブリッシャーについてのみ**空集合に解決します — エントリは他のすべてのリストされたパブリッシャーについて有効なままです。コンシューマーは、単一の到達不能なパブリッシャーがコンパクトエントリの残りを汚染するものとして扱ってはなりません(MUST NOT)。 * リストされたパブリッシャーの `adagents.json` が述語に一致するプロパティを運ばない(名付けられたタグを持つエントリがない)場合、セレクターはそのパブリッシャーについて空集合に解決します。同じ部分解決ルールが適用されます。 * 解決キャッシュは、各パブリッシャー自身の `adagents.json` のキャッシュポリシーに独立して従います。コンシューマーは、同じコンパクトエントリ内の別のパブリッシャーの観察に基づいて、あるパブリッシャーのキャッシュ TTL を延長または短縮すべきではありません(SHOULD NOT)。 **2. 親ファイルインライン解決(管理ネットワーク最適化)。** コンシューマーは、**すべて**の次が成り立つとき、親ファイル自身のトップレベル `properties[]` からセレクターを満たしてもかまいません(MAY)。 * 親ファイルがトップレベル `properties[]` エントリを持つ。 * 一致するすべてのプロパティが、値がセレクターの `publisher_domain` / `publisher_domains[]` セットのドメインの 1 つに等しい明示的な `publisher_domain` フィールドを運ぶ。 * `selection_type: by_tag` の場合: プロパティの `tags[]` がセレクターの `property_tags[]` の少なくとも 1 つを含む。 * `selection_type: by_id` の場合: プロパティの `property_id` がセレクターの `property_ids[]` にあり、かつセレクターが単数形の `publisher_domain` 形式を使う(コンパクトな `publisher_domains[]` 形式は `by_id` では依然として拒否されます — プロパティ ID はパブリッシャースコープであり、固定 ID セットを複数パブリッシャーにファンアウトすると誤ったインベントリを黙って認可することになるため)。 * `selection_type: all` の場合: 一致する `publisher_domain` を持つすべての親ファイル `properties[]` エントリが選択される。 インライン解決は**ドメインごとの最適化**です: コンシューマーは、親ファイルに一致するインラインプロパティを持つリストされたドメインにはインライン解決を、残りにはフェデレーテッド解決を使ってもかまいません(MAY)。両方が利用可能な場合、両パスは同じ `(publisher_domain, property_id)` セットを生成すべきです(SHOULD)。 **なぜこれが安全か。** プロパティ認可の信頼アンカーは、プロパティ上でドメインが名付けられたパブリッシャーです。各インラインプロパティに `publisher_domain` を要求しセレクターの `publisher_domains[]` と照合することで、インラインパスは、インベントリが認可されているパブリッシャーが明示的に名付けられているという不変条件 — [`managerdomain` フォールバックの安全ルール](#safety-rules-for-this-fallback)が保護するのと同じ不変条件 — を保持します。マネージャーファイルは、リストしていないパブリッシャーのインベントリを認可するためにインライン解決を使えません。 **乖離ルール。** コンシューマーが同じ `(publisher_domain, property_id)` をインラインとフェデレーテッドの両パスで解決し結果が食い違う場合、フェデレーテッドの結果が権威を持ちます。コンシューマーは乖離をパブリッシャー側のデータ整合性警告としてログに記録すべきで(SHOULD)、オペレーターに表面化してもかまいません(MAY)。厳格なフェデレーションを好むコンシューマーはインラインパスを完全に無視してもかまいません(MAY)。 **インライン解決下での失効。** インライン解決は親ファイルの `revoked_publisher_domains[]` を尊重しなければなりません(MUST)。親レベルで失効としてリストされた `publisher_domain` は、親の `properties[]` に一致するプロパティが存在するかどうかに関係なく、そのドメインについて空集合に解決します。フェデレーテッドも解決するコンシューマーは、子自身の `revoked_publisher_domains[]` をクロスチェックすべきです(SHOULD)。最初の一致(親または子)が失効させます。 **どちらをいつ使うか。** インライン解決が存在するのは、管理ネットワーク規模(1 オペレーターの下で数千の代表パブリッシャー)での厳格なフェデレーションが認可チェックごとに N 回の HTTP フェッチを必要とし、どの本番コンシューマーも維持できないためです。`publisher_domain` アンカーとともに `properties[]` をインライン化するファイルは「ここで解決できる」とシグナルしています。小さなフェデレーテッドエントリ(少数のパブリッシャー、それぞれ適切に投入された独自の `adagents.json` を持つ)を扱うコンシューマーはフェデレーテッドパスを好むべきです。それはコンシューマー側の信頼の前提が少ないです。管理ネットワークの親ファイルを大規模にインデックスするコンシューマーはインラインパスを好むべきです。親ファイルは仕様が是認するかどうかに関係なく構造的にプロパティカタログであり、インライン解決がそれを明示的にします。 ## Authorization Qualifiers 上記の 4 つのプロパティ側 `authorization_type` パターンは、エージェントが**どのインベントリ**を販売できるかに答えます。2 つのシグナル側の値(`signal_ids`、`signal_tags`)は `signals[]` について同じ形状を運びます。下記の任意の修飾子は、そのインベントリが**どのように**利用可能にされているかに答えます。 ### `delegation_type` * **`direct`**: パブリッシャーは、第三者が舞台裏でソフトウェアを運用していても、このエンドポイントを自分たちから購入する直接的な方法として扱います * **`delegated`**: エージェントはパブリッシャーを代行して販売することを認可されています * **`ad_network`**: インベントリはパブリッシャーの直接エンドポイントとしてではなく、ネットワーク/パッケージ販売パスを通じて販売されます ### `collections` 認可が特定のコンテンツプログラムに関連付けられたインベントリにのみ適用されるべき場合に `collections` を使います。これは、同じプロパティが異なる商業的取り決めを持つ多くのコレクションを運びうる CTV、ストリーミング、ポッドキャスティング、クリエイターインベントリに特に有用です。 ### `placement_ids` 認可を同じ `adagents.json` に公開された正準プレースメントに狭めるために `placement_ids` を使います。これは、パブリッシャーが「このエージェントは MSN ホームページネイティブフィードに認可されているが、プロパティ全体ではない」や「このネットワークはプレロールを販売できるがホストリードスポンサーシップは販売できない」と言えるようにするフィールドです。プロダクトレスポンスとクリエイティブ割り当てでは、対応するプレースメントアイデンティティは `{ publisher_domain, placement_id }` です。 正準プレースメント定義は次も運べます。 * プロパティとプロダクト間でプレースメントをグループ化する `tags` * 「プロパティ X にはどのプレースメントがあるか?」「プレースメント Y はどのプロパティにあるか?」に答える `property_ids` または `property_tags` * プロダクトレスポンスのプレースメント詳細に完全に依存せずに「このプレースメントはどのフォーマットをサポートするか?」に答える `format_options` ### `placement_tags` 認可が手動保守されたプレースメント ID のリストではなく管理されたプレースメントグループに適用されるべき場合に `placement_tags` を使います。これは次のような商業アクセスパターンに有用です。 * `programmatic` * `direct_only` * `publisher_managed` * `managed_by_taboola` 自由形式のラベルとは異なり、これらのタグは認可決定がそれらに依存するため、パブリッシャーのプレースメントガバナンスモデルの一部として扱われるべきです。プロパティタグがトップレベル `tags` で文書化されるのと同じ方法で、トップレベル `placement_tags` メタデータで定義します。 ### `signing_keys` パブリッシャーが、認可されたエージェントが署名に使える公開鍵をピン留めしたい場合に `signing_keys` を使います。これは、エージェントドメインのみからの鍵ディスカバリーを信頼することを避けます。 * これらは単なる便宜メタデータではなく、パブリッシャーが証明した信頼アンカーです * バイヤーは、`adagents.json` のピン留めされた鍵に対して署名付きエージェントレスポンスを検証すべきです * エージェントドメインが侵害された場合、ピン留めされた鍵は攻撃者がエンドポイントとそのアドバタイズされた鍵の両方を黙って入れ替えるのを防ぎます パブリッシャーは、委任されたスコープに**変更操作** — パブリッシャーを代行して状態を書き込む任意の AdCP タスク — を含む任意の認可エージェントについて `signing_keys` を投入しなければなりません(MUST)。3.x カタログでは、これはメディアバイタスクセット(`create_media_buy`、`update_media_buy`、`sync_creatives`、`update_performance_index`)と、[media-buy タスクリファレンス](/docs/media-buy/task-reference/update_media_buy)で変更としてフラグ付けされる将来のタスクを意味します。読み取り専用のディスカバリータスク(`get_products`、`get_signals`、`list_creative_formats`)はこの要件の対象外です。変更スコープの認可について `signing_keys` を空のままにすると、信頼チェーンが取引相手が制御する `jwks_uri` ディスカバリーに縮小され、クロスチェックとしてのパブリッシャーのピンが失われます。 検証者要件: パブリッシャーのエージェント用 `adagents.json` エントリが `signing_keys` を含む場合、検証者は、`jwks_uri` の内容に関係なく、`keyid` がそのピン留めされたセットにない任意の署名を拒否しなければなりません(MUST)。ピンが権威を持ちます。エージェントがホストする JWKS はアドバイザリであり、それをオーバーライドしてはなりません(MUST NOT)。 **鍵ローテーションとキャッシュセマンティクス。** ローテーション・バイ・DoS ウィンドウを開かずにローテーション間でピンを使用可能に保つには: * 検証者は、ピン留めされた `signing_keys` を、パブリッシャーが `adagents.json` で提供する `Cache-Control` `max-age` を最大としてキャッシュすべきで(SHOULD)、ディレクティブがない場合は**1 時間**をデフォルトとします。より長いキャッシュは、正当なローテーションされた鍵を拒否するリスクがあります。 * **未知の `keyid`** に遭遇したとき、検証者は最終的な拒否の前にパブリッシャーの `adagents.json` を強制リフレッシュ(キャッシュをバイパス)しなければなりません(MUST)。これは、古いキャッシュが正当にローテーションされた鍵をロックアウトするのを防ぎます。 * パブリッシャーは、検証者が古い鍵または新しい鍵の下で生成された署名を受け入れられるよう、ローテーションウィンドウ中に `signing_keys` に**重複する鍵**を運んでもかまいません(MAY)。ピン留めされたセットは順序なしです: セット内の存在が受け入れに十分です。オペレーターは、進行中のトラフィックがまだそれで署名していないと確信したら、退役した鍵をピンから削除すべきです(SHOULD。日単位ではなく時間単位)。 **ブートストラップスコープ。** ピンは**エージェントドメイン**の侵害から保護します: エージェントドメインが乗っ取られても、パブリッシャーのピンが依然として受け入れを管理するため、攻撃者はエンドポイントとそのアドバタイズされた鍵の両方を黙って入れ替えられません。パブリッシャードメインの侵害からは保護し**ません**(`adagents.json` を制御する攻撃者はピン自体を書き換えられます)。`adagents.json` の初回取得は TLS 信頼のみです。R-1 の信頼のルート / 鍵透明性の作業(`specs/registry-change-feed.md` §Feed-event content signing で追跡)が、この境界を強化するトラックです。 変更スコープの認可について `signing_keys` を任意からスキーマレベルで必須に昇格させるフォローアップが追跡されています。そのスキーマ変更が到着するまで、上記の文章要件が規範的な下限です。 ### `countries` 認可を地理的に制約するために ISO 3166-1 alpha-2 国コードを使います。これは「LATAM」や「EMEA」などの曖昧な地域略称を避け、バイヤーエージェントに正確な機械可読なスコープを与えます。 ### `effective_from` / `effective_until` 季節的独占、ウィンドウ化されたシンジケーション、一時的な委任販売合意などの時間限定の権利にこれらのフィールドを使います。 ### `exclusive` このエージェントがスコープされたインベントリスライスに対するパブリッシャーの唯一の認可されたパスである場合に `exclusive: true` を設定します。複数のエージェントが同時に認可されている場合は、省略するか `false` に設定します。 ### Example: Scoped Delegation ```json theme={null} { "placement_tags": { "programmatic": { "name": "Programmatic", "description": "Placements available through programmatic sales paths" }, "direct_only": { "name": "Direct only", "description": "Placements reserved for direct publisher sales" } }, "collections": [ { "collection_id": "signal_noise", "name": "Signal & Noise", "kind": "series" } ], "placements": [ { "placement_id": "pre_roll", "name": "Pre-roll", "tags": ["audio", "pre_roll", "programmatic"], "property_ids": ["publisher_podcast"], "collection_ids": ["signal_noise"], "format_options": [ { "format_kind": "audio_hosted", "params": { "duration_ms_exact": 15000, "audio_codecs": ["mp3"] } } ] }, { "placement_id": "host_read", "name": "Host-read Mid-roll", "tags": ["audio", "host_read", "premium", "direct_only"], "property_ids": ["publisher_podcast"], "collection_ids": ["signal_noise"], "format_options": [ { "format_kind": "audio_hosted", "params": { "duration_ms_exact": 60000, "asset_source": "publisher_host_recorded", "buyer_asset_acceptance": "rejected" } } ] } ], "authorized_agents": [ { "url": "https://sales.publisher.example.com", "authorized_for": "Direct US and CA sales for Signal & Noise host reads", "authorization_type": "property_ids", "property_ids": ["publisher_podcast"], "collections": [ { "publisher_domain": "publisher.example.com", "collection_ids": ["signal_noise"] } ], "placement_tags": ["direct_only"], "delegation_type": "direct", "countries": ["US", "CA"], "exclusive": true }, { "url": "https://network.example.com", "authorized_for": "Open network distribution outside US and CA for pre-roll", "authorization_type": "property_ids", "property_ids": ["publisher_podcast"], "collections": [ { "publisher_domain": "publisher.example.com", "collection_ids": ["signal_noise"] } ], "placement_tags": ["programmatic"], "delegation_type": "ad_network", "countries": ["GB", "AU", "NZ"] } ] } ``` これにより、パブリッシャーは、すべての認可パスが同等であることを意味することなく、「一部の市場では私たちから直接ホストリードを購入し、他の市場ではプレロールにネットワークパスを使う」と言えます。 `adagents.json` は現在、正準のパブリッシャーレベルのプレースメントレジストリを提供します。プロダクトは依然として独自の `placements` を返しますが、プレースメント ID はパブリッシャースコープです: カタログバックのプレースメントは `{ publisher_domain, placement_id }` でパブリッシャーレジストリを参照すべきです(SHOULD)。プロダクトが複数のパブリッシャーにまたがりカタログ ID が衝突する場合、`publisher_domain` がそれらを曖昧性解消します。それらのケースについてクリエイティブ割り当ては構造化された `placement_refs` を使うべきです。カタログプレースメントを参照することは、プロダクトがそのプレースメントのアイデンティティを継承することを意味します。プロダクトは `format_ids` を狭めたり、プレースメントタグを保持または狭めたり、運用の詳細を追加したりできますが、プレースメントを互換性のないものに再定義すべきではありません。 ## ドメインマッチングルール ドメイン識別子を持つウェブサイトプロパティについて、AdCP はウェブの慣例に従います。 ### ベースドメイン(`example.com`) ドメイン本体と標準的なウェブサブドメインにマッチします。 * ✅ `example.com` * ✅ `www.example.com`(標準ウェブ) * ✅ `m.example.com`(標準モバイル) * ❌ `subdomain.example.com`(明示的な認可が必要) ### 特定サブドメイン(`subdomain.example.com`) その特定サブドメインのみにマッチします。 * ✅ `subdomain.example.com` * ❌ その他のすべてのドメイン/サブドメイン ### ワイルドカード(`*.example.com`) すべてのサブドメインにマッチしますが、ベースドメインはマッチしません。 * ✅ 任意のサブドメイン * ❌ `example.com`(ベースドメインは別途認可が必要) ## Real-World Examples ### Example 1: Meta Network (Tag-Based) Large network using tags for grouping efficiency: ```json theme={null} { "contact": { "name": "Meta Advertising Operations", "email": "adops@meta.com", "domain": "meta.com", "seller_id": "pub-meta-12345", "tag_id": "12345", "privacy_policy_url": "https://www.meta.com/privacy/policy" }, "properties": [ { "property_type": "mobile_app", "name": "Instagram", "identifiers": [ {"type": "ios_bundle", "value": "com.burbn.instagram"}, {"type": "android_package", "value": "com.instagram.android"} ], "tags": ["meta_network"], "publisher_domain": "instagram.com" }, { "property_type": "mobile_app", "name": "Facebook", "identifiers": [ {"type": "ios_bundle", "value": "com.facebook.Facebook"}, {"type": "android_package", "value": "com.facebook.katana"} ], "tags": ["meta_network"], "publisher_domain": "facebook.com" }, { "property_type": "mobile_app", "name": "WhatsApp", "identifiers": [ {"type": "ios_bundle", "value": "net.whatsapp.WhatsApp"}, {"type": "android_package", "value": "com.whatsapp"} ], "tags": ["meta_network"], "publisher_domain": "whatsapp.com" } ], "tags": { "meta_network": { "name": "Meta Network", "description": "All Meta-owned properties - one tag authorizes entire network efficiently" } }, "authorized_agents": [ { "url": "https://meta-ads.com", "authorized_for": "All Meta properties", "authorization_type": "property_tags", "property_tags": ["meta_network"] } ] } ``` **Why this works**: One tag (`meta_network`) authorizes all properties without listing individual property IDs. As Meta adds properties, they just tag them - no need to update agent authorization. ### Example 2: CNN (Channel Segmentation) Different agents for different channels: ```json theme={null} { "contact": { "name": "CNN Advertising Operations", "email": "adops@cnn.com", "domain": "cnn.com" }, "properties": [ { "property_id": "cnn_ctv_app", "property_type": "ctv_app", "name": "CNN CTV App", "identifiers": [ {"type": "roku_store_id", "value": "12345"} ], "tags": ["ctv"] }, { "property_id": "cnn_web_us", "property_type": "website", "name": "CNN.com US", "identifiers": [ {"type": "domain", "value": "cnn.com"} ], "tags": ["web"] } ], "authorized_agents": [ { "url": "https://cnn-ctv-agent.com", "authorized_for": "CNN CTV properties", "authorization_type": "property_ids", "property_ids": ["cnn_ctv_app"] }, { "url": "https://cnn-web-agent.com", "authorized_for": "CNN web properties", "authorization_type": "property_ids", "property_ids": ["cnn_web_us"] } ] } ``` ### Example 3: Publisher with Governance Agent References Publishers can declare which governance agents have data about their properties using `property_features`. This enables buyers to discover where to get sustainability, quality, and suitability data. ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "contact": { "name": "Premium News Publisher", "email": "adops@news.example.com", "domain": "news.example.com" }, "properties": [ { "property_id": "news_main", "property_type": "website", "name": "News Example", "identifiers": [ {"type": "domain", "value": "news.example.com"} ], "tags": ["premium", "news"], "publisher_domain": "news.example.com" } ], "tags": { "premium": { "name": "Premium Properties", "description": "High-quality, brand-suitable properties" }, "news": { "name": "News Properties", "description": "News and journalism content" } }, "authorized_agents": [ { "url": "https://sales.news.example.com", "authorized_for": "All news properties", "authorization_type": "property_tags", "property_tags": ["news"] } ], "property_features": [ { "url": "https://api.sustainability-vendor.example", "name": "Sustainability Vendor", "features": ["carbon_score", "green_media_certified"], "publisher_id": "pub_news_12345" }, { "url": "https://api.quality-vendor.example", "name": "Quality Vendor", "features": ["mfa_score", "ad_density", "page_speed"] }, { "url": "https://api.suitability-vendor.example", "name": "Suitability Vendor", "features": ["content_category", "brand_risk_score", "sentiment"], "publisher_id": "suit_news_67890" } ], "last_updated": "2025-01-10T18:00:00Z" } ``` **Why this works**: * Publishers declare relationships with governance agents upfront * Buyers discover governance agents by reading adagents.json (no need to query every possible agent) * The `publisher_id` field helps agents look up the publisher's data efficiently * Feature IDs tell buyers what data types are available without querying ## Governance Agent Discovery The `property_features` field solves a key discovery problem: how does a buyer know which governance agents have data about a given property? ```mermaid theme={null} sequenceDiagram participant Buyer as Buyer Agent participant PubDomain as Publisher Domain participant SustAgent as Sustainability Agent participant QualAgent as Quality Agent Buyer->>PubDomain: GET /.well-known/adagents.json PubDomain-->>Buyer: adagents.json with property_features Note over Buyer: Extract governance agents from property_features par Query governance agents Buyer->>SustAgent: get_adcp_capabilities SustAgent-->>Buyer: Available features (carbon_score, etc.) and Buyer->>QualAgent: get_adcp_capabilities QualAgent-->>Buyer: Available features (mfa_score, etc.) end Note over Buyer: Create property lists on each governance agent Buyer->>SustAgent: create_property_list(filters, brand) Buyer->>QualAgent: create_property_list(filters, brand) ``` ### When to Use property\_features | Scenario | Use property\_features? | | -------------------------------------------------------------- | ------------------------------ | | Publisher has carbon scoring from a sustainability vendor | ✅ Yes | | Publisher has MFA score measured by a quality vendor | ✅ Yes | | Publisher has content classification from a suitability vendor | ✅ Yes | | Publisher self-reports brand suitability | ❌ No - use property tags | | Sales agent provides quality data | ❌ No - that's agent capability | ### Vendor Extensions Governance agents can include vendor-specific data in feature definitions via an `ext` block. See [get\_adcp\_capabilities](/docs/protocol/get_adcp_capabilities) for details. ## Fetching and Validating ### Using the AdAgents.json Builder The easiest way to validate or create an adagents.json file is using the **[AdAgents.json Builder](https://agenticadvertising.org/adagents/builder)** web tool. It provides: * Domain validation (fetches and checks `/.well-known/adagents.json`) * Structure validation against the JSON schema * Agent card endpoint verification (checks if agent URLs respond correctly) * Guided file creation with proper formatting ### Programmatic Validation For programmatic validation, use the validation API: ```javascript JavaScript theme={null} // Validate a domain's adagents.json file const response = await fetch('https://adcontextprotocol.org/api/adagents/validate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ domain: 'example.com' }) }); const { success, data } = await response.json(); if (success && data.found) { console.log(`Valid: ${data.validation.valid}`); console.log(`Agents: ${data.validation.raw_data?.authorized_agents?.length || 0}`); // Check for any validation errors if (data.validation.errors?.length > 0) { console.log('Errors:', data.validation.errors.map(e => e.message)); } } else { console.log('No adagents.json found at this domain'); } ``` ```python Python theme={null} import httpx # Validate a domain's adagents.json file response = httpx.post( 'https://adcontextprotocol.org/api/adagents/validate', json={'domain': 'example.com'} ) result = response.json() if result['success'] and result['data']['found']: validation = result['data']['validation'] print(f"Valid: {validation['valid']}") print(f"Agents: {len(validation.get('raw_data', {}).get('authorized_agents', []))}") # Check for any validation errors if validation.get('errors'): print('Errors:', [e['message'] for e in validation['errors']]) else: print('No adagents.json found at this domain') ``` ```bash CLI theme={null} # Validate a domain's adagents.json file curl -X POST https://adcontextprotocol.org/api/adagents/validate \ -H "Content-Type: application/json" \ -d '{"domain": "example.com"}' | jq '.data.validation' ``` The validation API fetches `https://{domain}/.well-known/adagents.json`, validates its structure, follows URL references if present, and optionally checks agent card endpoints. ### Using AdCP Client Libraries The AdCP client libraries provide built-in validation and authorization checking: ```python Python theme={null} import asyncio from adcp import fetch_adagents, verify_agent_authorization async def validate_authorization(): # Fetch and validate adagents.json from a publisher domain adagents_data = await fetch_adagents('example-publisher.com') # Check if a specific agent is authorized is_authorized = verify_agent_authorization( adagents_data=adagents_data, agent_url='https://our-sales-agent.com', property_type='website', property_identifiers=[{'type': 'domain', 'value': 'example-publisher.com'}] ) print(f"Agent authorized: {is_authorized}") print(f"Total agents: {len(adagents_data.get('authorized_agents', []))}") asyncio.run(validate_authorization()) ``` ```javascript JavaScript theme={null} // Using the @adcp/client PropertyCrawler for discovery import { PropertyCrawler } from '@adcp/client'; const crawler = new PropertyCrawler({ logLevel: 'info' }); // Crawl agents to discover their authorized properties const result = await crawler.crawlAgents([ { agent_url: 'https://our-sales-agent.com', protocol: 'a2a' } ]); console.log(`Found ${result.totalProperties} properties across ${result.totalPublisherDomains} domains`); ``` ```bash CLI theme={null} # Fetch and inspect authorization file curl https://example-publisher.com/.well-known/adagents.json | jq '.' # Check specific agent authorization curl https://example-publisher.com/.well-known/adagents.json | \ jq '.authorized_agents[] | select(.url == "https://our-sales-agent.com")' ``` The Python library handles validation automatically when fetching - if the adagents.json file is malformed or missing required fields, it raises `AdagentsValidationError`. ## Best Practices ### 1. Use Appropriate Authorization Pattern * **Property IDs**: Small, enumerable lists (\< 20 properties) * **Property Tags**: Large networks (100+ properties) * **Inline Properties**: Simple cases without top-level properties * **Publisher Properties**: Third-party agents representing multiple publishers ### 2. Cache Files Appropriately * Cache for 24 hours minimum * Use `last_updated` timestamp to detect staleness * Handle 404 as "no file" (not an error - proceed without validation) * Implement retry logic with exponential backoff for network errors ### 3. Validate Structure * Validate against JSON schema before processing * Check required fields exist (`authorized_agents` array) * Verify authorization scope matches product claims * Cross-reference with seller.json if available ### 4. Handle Missing Files Gracefully * 404 status = No file present (not an authorization failure) * Absence of file does not mean agent is unauthorized * Use adagents.json as verification, not requirement ### 5. Handle Per-Property Validation Failures Gracefully ファイルレベルの失敗(パース不能な JSON、必須のトップレベル `authorized_agents` の欠如)は、そのドメインの処理を中止しなければなりません(MUST)— ファイルは使用不能です。プロパティごとの検証失敗は別の階層です: 他の点では有効なファイル内の単一のプロパティオブジェクトが、パブリッシャー側のテンプレートエラーや部分的な書き込みにより `identifiers` や他の必須フィールドを省略する場合があります。 プロパティごとの検証失敗は、同じファイル内の残りのプロパティの処理を妨げてはなりません(MUST NOT)。非準拠のプロパティを配列から欠けているものとして扱い、決して実行を中止する理由としないでください。 * 非準拠のプロパティを**スキップ**する * ソースドメイン、配列内のプロパティのインデックス、理由(例: `missing required field: identifiers`)を含む警告を**ログに記録**する * ファイル内の残りのすべてのプロパティの処理を**続行**する プロパティごとの失敗で完全なクロールを中止することは、一般的な実装エラーです。数百のパブリッシャードメインをカバーする管理ネットワークファイル内の単一の不正なプロパティが、ディスカバリー実行全体を黙ってゼロにする可能性があり、この失敗モードを基礎となるデータ問題のサイズに対して不釣り合いに破壊的にします。これは、不正な行を無視し後続の行の処理を続行しなければならないと規定する IAB Tech Lab ads.txt 1.1 §3.1 と同じ原則に従います。 これは、プロパティオブジェクトが現れるすべてのサーフェスに適用されます: トップレベル `properties` 配列、`authorized_agents[*].properties` 内のインラインプロパティ(`inline_properties` authorization type)、および `publisher_properties` 解決中にリモートドメインから取得・解決されたプロパティ。 ## Next Steps After implementing adagents.json validation: 1. **Integrate with Product Discovery**: Use [`get_products`](/docs/media-buy/task-reference/get_products) to discover inventory 2. **Validate at Purchase**: Check authorization before calling [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) 3. **Cache Property Mappings**: Store resolved properties for efficient validation 4. **Monitor Authorization**: Track validation success rates and unauthorized attempts ## Learn More * [AdCP Basics: Authorized Properties](https://bokonads.com/p/adcp-basics-authorized-properties) - Accessible introduction to AdCP authorization * [get\_adcp\_capabilities](/docs/protocol/get_adcp_capabilities) - Discover agent capabilities and portfolio * [Property Schema](https://adcontextprotocol.org/schemas/v2/core/property.json) - Property definition structure * [AdAgents.json Builder](https://adcontextprotocol.org/adagents) - Web-based validator and creator # 認可の理解 Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/property/authorized-properties AdCP の認可プロパティ機能により、バイヤーはプロパティリストとサプライパス検証を使って検証済みインベントリにキャンペーンを制限できます。 デジタル広告の根本課題の一つは **無断再販** であり、セールスエージェントが実際に販売できるプロパティを正当に代表しているかを保証する必要があります。AdCP はプログラマティック広告の ads.txt から得た教訓を活かし、包括的な認可システムでこれを解決します。 **AdCP の認可が初めてですか?** エージェント主導広告での認可の仕組みは [AdCP Basics: Authorized Properties](https://bokonads.com/p/adcp-basics-authorized-properties) を参照してください。 ## 課題: 無断再販 ### 歴史的背景 プログラマティック広告では ads.txt が無断再販という重大問題を解決するために作られました。以前は不正事業者が人気サイトを名乗り、無許可で在庫を販売していました。結果として: * **収益の搾取**: パブリッシャーが無断セラーに収益を奪われる * **ブランドセーフティの問題**: バイヤーが正規在庫元を確認できない * **市場の分断**: 正規・非正規セラーの区別がつかない ### AI 主導広告でも同じ課題 AdCP も、AI エージェントが広告を売買する際に同様の課題に直面します: * **AI セールスエージェント** が実際に管理していないプロパティを名乗る可能性 * **バイヤーエージェント** は購入前に認可を検証する必要 * **パブリッシャー** は特定のセールスエージェントを明示的に認可する手段が必要 * **スケールの課題**: 数千のプロパティを手作業で検証するのは非現実的 ## 解決策: AdCP 認可システム AdCP は 2 つの仕組みで無断再販を防ぎます: 1. **Publisher Authorization**: パブリッシャーが `adagents.json` でセールスエージェントを明示的に認可 2. **Agent Discovery**: セールスエージェントが [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) の `media_buy.portfolio` でポートフォリオを公開 これによりバイヤーエージェントが検証可能な認可チェーンが形成されます。 ## パブリッシャーによるセールスエージェントの認可 パブリッシャーは自ドメインの `/.well-known/adagents.json` に `adagents.json` をホストし、認可済みエージェントと権限を列挙します。 ### adagents.json の例 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "contact": { "name": "Sports Network Media", "email": "adops@sportsnetwork.com", "domain": "sportsnetwork.com" }, "properties": [ { "property_id": "sports_network_main", "property_type": "website", "name": "Sports Network", "identifiers": [ {"type": "domain", "value": "sportsnetwork.com"} ], "tags": ["premium", "sports"] } ], "authorized_agents": [ { "url": "https://sports-media-sales.com", "authorized_for": "All Sports Network properties", "authorization_type": "property_tags", "property_tags": ["sports"] }, { "url": "https://premium-ad-network.com", "authorized_for": "Premium inventory only", "authorization_type": "property_tags", "property_tags": ["premium"] } ], "last_updated": "2025-01-10T12:00:00Z" } ``` ### Key Fields * **contact**: このファイルを管理するパブリッシャー/主体 * **properties**: 認可ファイルが対象とするプロパティ定義 * **authorized\_agents**: プロパティを代表する認可済みセールスエージェント一覧 * **url**: エージェントの API エンドポイント * **authorized\_for**: 認可範囲の説明 * **authorization\_type**: プロパティの選択方法(`property_ids`, `property_tags`, `inline_properties`, `publisher_properties`) * **last\_updated**: 最終更新日時(ISO 8601) ## セールスエージェントが認可プロパティを共有する方法 Sales agents は [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) タスクで `media_buy.portfolio` にポートフォリオ情報を公開します。これには複数の目的があります: 1. **透明性**: エージェントが代表するパブリッシャーをバイヤーが確認できます 2. **検証の容易さ**: バイヤーが adagents.json で認可確認するためのドメインを提供 3. **ポートフォリオ概要**: 主要チャネル・国・説明を含みます ### Property Declaration Example ```json theme={null} { "properties": [ { "property_type": "website", "name": "Sports Network", "identifiers": [ {"type": "domain", "value": "sportsnetwork.com"} ], "tags": ["sports_network", "premium"], "publisher_domain": "sportsnetwork.com" }, { "property_type": "radio", "name": "WXYZ-FM Chicago", "identifiers": [ {"type": "call_sign", "value": "WXYZ-FM"}, {"type": "market", "value": "chicago"} ], "tags": ["local_radio", "midwest"], "publisher_domain": "radionetwork.com" } ], "tags": { "sports_network": { "name": "Sports Network Properties", "description": "145 sports properties and networks" }, "local_radio": { "name": "Local Radio Stations", "description": "1847 local radio stations across US markets" } }, "advertising_policies": "We maintain strict brand safety standards. Prohibited categories include: tobacco and vaping products, online gambling and sports betting, cannabis and CBD products, political advertising, and speculative financial products (crypto, NFTs, penny stocks).\n\nWe also prohibit misleading tactics such as clickbait headlines, false scarcity claims, hidden pricing, and ads targeting vulnerable populations.\n\nCompetitor brands in the streaming media space are blocked by policy.\n\nFull advertising guidelines: https://publisher.com/advertising-policies" } ``` ### 規模対応のプロパティタグ 数千のプロパティを持つ大規模ネットワークでは **property tags** で管理を容易にします: * **Products** は多数のステーションを列挙する代わりに `["local_radio", "midwest"]` を参照可能 * **Buyers** は `get_adcp_capabilities` でポートフォリオを発見し認可を検証 * **認可検証** は adagents.json を通じて解決済みプロパティに対して行います ## 認可検証のワークフロー バイヤーエージェントが、セールスエージェントが主張するプロパティを正当に代表しているか検証する手順: ### 1. 初期セットアップ ```javascript theme={null} // Get portfolio information from capabilities const capabilities = await salesAgent.call('get_adcp_capabilities'); const portfolio = capabilities.media_buy?.portfolio; const publisherDomains = portfolio?.publisher_domains || []; // For each publisher domain, fetch and cache adagents.json const authorizationCache = {}; for (const domain of publisherDomains) { try { const adagents = await fetch(`https://${domain}/.well-known/adagents.json`); authorizationCache[domain] = await adagents.json(); } catch (error) { console.warn(`Could not verify authorization for ${domain}`); authorizationCache[domain] = null; } } ``` ### 2. プロダクト検証 ```javascript theme={null} // When evaluating a product const result = await salesAgent.call('get_products', {brief: "Chicago radio ads"}); const product = result.products[0]; // Validate authorization for each publisher in publisher_properties const authorized = product.publisher_properties.every(pubProp => { const domain = pubProp.publisher_domain; const adagents = authorizationCache[domain]; if (!adagents) return false; // No adagents.json found // Verify the sales agent is in publisher's authorized_agents return adagents.authorized_agents.some(agent => agent.url === salesAgent.url && isAuthorizedForProperties(agent, pubProp) ); }); if (!authorized) { throw new Error("Sales agent not authorized for claimed properties"); } ``` ### 3. 継続的な検証 * **adagents.json をキャッシュ**(例: 24 時間) * **長期キャンペーンでは定期的に再検証** * **認可変更** は適切に処理(停止か拒否) ## このアプローチの利点 ### パブリッシャーにとって * **明示的な制御**: 誰が在庫を販売できるかを管理 * **きめ細かな権限**: プロパティ・期間・商品種別ごとに設定 * **標準的なホスティング**: 特別なインフラ不要 * **監査証跡**: 認可エージェントの履歴を保持 ### セールスエージェントにとって * **明確な認可証跡**: バイヤーが検証可能 * **効率的なタグ分け**: 大規模ポートフォリオをタグで管理 * **標準化された宣言**: AdCP の全インタラクションで統一 ### バイヤーエージェントにとって * **認可の自動検証**: セラーの正当性を自動確認 * **不正防止**: 暗号的な検証で詐欺を抑止 * **安心して購入**: 検証済み在庫ソースからの購入に自信 * **スケーラブルな検証**: 大規模な自動化バイイングにも対応 ## セキュリティの考慮事項 ### ドメイン検証 * **HTTPS 必須**: adagents.json は HTTPS で提供しなければなりません * **ドメイン所有権**: ドメイン所有者のみがプロパティのエージェントを認可できます * **定期的な検証**: バイヤーは定期的に認可を再確認すべきです ### 認可スコープ * **最小権限**: 必要最小限の権限を付与します * **期間制限**: 一時的な認可には開始/終了日付を使用します * **プロパティ制限**: 適切な場合は特定のパスやプロパティタイプに制限します ### エラー処理 * **adagents.json なし**: 非認可として扱う(フェイルクローズ) * **不正な JSON**: 不正な認可ファイルは拒否します * **ネットワークエラー**: フォールバックポリシー付きのリトライロジックを実装します * **認可期限切れ**: アクティブなキャンペーンでは適切に処理します ## プロダクト発見との連携 認可検証は [Product Discovery](../../media-buy/product-discovery/) とシームレスに統合されます。 1. [`get_products`](../../media-buy/task-reference/get_products) で**プロダクトを発見** 2. プロダクトが参照するプロパティの**認可を検証** 3. 認可済み在庫で**安心して進行** 4. 未認可のプロダクトを**手動レビュー用にフラグ** これにより無断再販を防ぎつつ効率的な自動取引を可能にする、AI 主導広告の信頼できる基盤が形成されます。 ## 技術的な実装 `adagents.json` ファイル形式の実装に関する完全な技術詳細については以下を参照してください。 * ファイルの配置場所とフォーマット要件(`/.well-known/adagents.json`) * JSON スキーマの定義と検証ルール * モバイルアプリと CTV の実装パターン * プロパティタイプの詳細仕様(website, mobile app, CTV, DOOH, podcast) * ドメインマッチングルールとワイルドカードパターン * バリデーションコード例とエラー処理 * セキュリティの考慮事項とベストプラクティス 完全な実装ガイダンスは **[adagents.json Tech Spec](./adagents)** を参照してください。 ## 関連ドキュメント * **[`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)** - エージェントの capabilities とポートフォリオ情報を確認 * **[Product Discovery](../../media-buy/product-discovery/)** - プロダクト発見と認可の連携 * **[Properties Schema](https://adcontextprotocol.org/schemas/v2/core/property.json)** - Technical property data model * **[adagents.json Tech Spec](./adagents)** - Complete `adagents.json` implementation guide # Property Governance Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/property/index AdCP Property Governance はすべてのメディアチャネルにわたって広告プロパティの識別、認可、データ付与、選定を標準化します。 **AdCP 3.0 提案** - このプロトコルは AdCP 3.0 向けに開発中です。フィードバックは [GitHub Discussions](https://github.com/adcontextprotocol/adcp/discussions) へどうぞ。 Property Governance は、広告プロパティ(Web サイト、アプリ、CTV、ポッドキャスト、屋外広告)の識別、認可、データ付与、キャンペーン選定を標準化します。 ## 概要 Property Governance addresses five distinct concerns: | Concern | Question | Owner | Mechanism | | ----------------------- | ----------------- | -------------- | ------------------------------------------------------------------------------- | | **Property Identity** | どのプロパティが存在するか | Publishers | `adagents.json` の properties 配列 | | **Sales Authorization** | 誰がこのプロパティを販売できるか | Publishers | `adagents.json` の authorized\_agents | | **Property Data** | このプロパティについて何がわかるか | Data providers | [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) を通じたガバナンスエージェント | | **Property Selection** | 要件を満たすプロパティはどれか | Buyers | フィルター付きプロパティリスト | 最初の 3 つは adagents.json を介した **パブリッシャー側の宣言** です。後ろの 2 つはガバナンスエージェントからのデータを使う **バイヤー側の処理** です。 ## パブリッシャー側: adagents.json Publishers declare their properties, authorize sales agents, and reference governance agents via `/.well-known/adagents.json`: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "properties": [ { "property_id": "example_site", "property_type": "website", "name": "Example Site", "identifiers": [{"type": "domain", "value": "example.com"}] } ], "authorized_agents": [ { "url": "https://agent.example.com", "authorized_for": "Official sales agent", "authorization_type": "property_ids", "property_ids": ["example_site"] } ], "property_features": [ { "url": "https://api.sustainability-vendor.example", "name": "Sustainability Vendor", "features": ["carbon_score", "green_media_certified"] }, { "url": "https://api.quality-vendor.example", "name": "Quality Vendor", "features": ["mfa_score", "ad_load_rating", "page_speed"] } ] } ``` ### property\_features によるガバナンスエージェント発見 `property_features` 配列は「特定プロパティのデータを持つガバナンスエージェントをバイヤーはどう知るか」という発見問題を解決します。 `property_features` がなければ、バイヤーはコンプライアンス・サステナビリティ・品質データを持つエージェントを総当たりで探す必要があります。`property_features` によってパブリッシャーが関係性を事前に宣言できます: | Field | Purpose | | -------------- | ----------------------------------------------- | | `url` | ガバナンスエージェントの API エンドポイント | | `name` | エージェント名(人が読める名前) | | `features` | 提供する Feature ID(例: `carbon_score`, `mfa_score`) | | `publisher_id` | 任意。エージェント側でこのパブリッシャーを参照する ID | **ユースケース例:** * **Sustainability**: パブリッシャーがカーボン計測ベンダーによる排出計測を宣言 * **Quality**: パブリッシャーが MFA スコアと広告密度を計測する検証ベンダーを宣言 * **Consumer experience**: パブリッシャーがページ速度と広告負荷を追跡するベンダーを宣言 Buyers read `property_features` from adagents.json, then query only the relevant governance agents for detailed data. 例やディスカバリフローを含む詳細は [adagents.json Tech Spec](/docs/governance/property/adagents) を参照してください。 ## バイヤー側: プロパティデータと選定 ### プロパティデータプロバイダー ガバナンスエージェントはプロパティに関するデータ(コンプライアンススコア、ブランドセーフティ評価、サステナビリティ指標、コンシューマーエクスペリエンススコアなど)を提供します。`governance.property_features` セクションで [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) を通じて機能を公開します: ```json theme={null} { "governance": { "property_features": [ { "feature_id": "mfa_score", "type": "quantitative", "range": { "min": 0, "max": 100 } }, { "feature_id": "coppa_certified", "type": "binary" }, { "feature_id": "carbon_score", "type": "quantitative", "range": { "min": 0, "max": 100 } } ] } } ``` バイヤーはプロパティリストをエージェントに送り、エージェントは専門データに基づいてフィルタ・スコアリングします。エージェントごとに得意なデータが異なります: * **Brand suitability providers** (content classification, risk scoring) * **Quality measurement** (MFA score, ad density, fraud detection) * **Sustainability providers** (carbon scoring, green media certification) * **Consumer experience** (page speed, ad load, layout shift) ### ガバナンスエージェントによるプロパティ選定 バイヤーは**ガバナンスエージェント上でプロパティリストを作成**し、エージェントがそれを管理してフィルタリングロジックを適用します: ```json theme={null} { "tool": "create_property_list", "arguments": { "name": "Q1 Campaign - UK Premium", "base_properties": [ { "selection_type": "publisher_tags", "publisher_domain": "raptive.com", "tags": ["premium_news"] } ], "filters": { "countries_all": ["UK"], "channels_any": ["display", "video"], "feature_requirements": [ { "feature_id": "mfa_score", "min_value": 70, "max_value": 100 } ] }, "brand": { "domain": "toybrand.com" } } } ``` ブランド参照を渡すと、ガバナンスエージェントはブランドのアイデンティティを解決し、COPPA や業界別コンテンツフィルタリングなど適切なルールを自動適用します。 バイヤーエージェントは通常、複数のガバナンスエージェント(同意、ブランドセーフティ、サステナビリティなど)を組み合わせ、結果を集約・交差させて最終的な準拠リストを作成します。 ## 全体の流れ ```mermaid theme={null} flowchart TB subgraph Publisher["PUBLISHER (adagents.json)"] P1["properties: identity"] P2["authorized_agents: sales auth"] P3["property_features: governance refs"] end subgraph Buyer["BUYER AGENT"] B1[Discovers governance agents from adagents.json] B2[Aggregates results from specialized agents] B3[Issues auth_tokens for sellers] end subgraph Governance["GOVERNANCE AGENTS"] SUS["Sustainability Agent
carbon_score
green_media
climate_risk"] QA["Quality Agent
mfa_score
ad_load
page_speed"] BS["Brand Suitability Agent
content_category
brand_risk
sentiment"] end subgraph Seller["SELLER AGENT (DSP/SSP)"] SE1[Caches resolved property lists] SE2[Uses cached lists for bid-time decisions] end Publisher -->|property_features discovery| Buyer Buyer -->|create_property_list + webhooks| SUS Buyer -->|create_property_list + webhooks| QA Buyer -->|create_property_list + webhooks| BS Buyer -->|get_property_list with auth_token| Seller ``` ### 完全なフロー 1. **Publisher declares** properties, sales agents, AND governance agents in `adagents.json` 2. **Buyer discovers** governance agents by reading `property_features` from adagents.json 3. **Buyer queries** each governance agent's `get_adcp_capabilities` for detailed capabilities 4. **Buyer creates** property lists on each governance agent with filters and brand references 5. **Governance agents evaluate** properties and notify buyer via webhooks when lists change 6. **Buyer aggregates** results into a final compliant list 7. **Buyer shares** property list reference with sellers (with auth token) 8. **Seller caches** resolved list for bid-time decisions ## セラーとのプロパティリスト共有 バイヤーが準拠リストを用意したら、セラーと共有します: 1. **Get a list reference**: The buyer agent exposes the list via `get_property_list` 2. **Issue an auth token**: The buyer generates a token that authorizes access to the list 3. **Pass to seller**: Include `property_list_ref` with `auth_token` in product discovery or media buy requests 4. **Seller caches locally**: Sellers fetch and cache the resolved list for bid-time decisions 5. **Webhooks for updates**: When the list changes, sellers are notified to refresh their cache ```json theme={null} { "property_list_ref": { "agent_url": "https://buyer-agent.example.com", "list_id": "pl_q1_uk_premium", "auth_token": "eyJhbGciOiJIUzI1NiIs..." } } ``` Sellers use this reference in `get_products` to filter available inventory: ```json theme={null} { "tool": "get_products", "arguments": { "brief": "UK video inventory for Q1", "property_list_ref": { "agent_url": "https://buyer-agent.example.com", "list_id": "pl_q1_uk_premium", "auth_token": "..." } } } ``` ## Relationship to Other Protocols ### Property Governance + Media Buy The Media Buy Protocol consumes property lists at multiple stages: * **Product discovery**: Pass `property_list_ref` to `get_products` to filter inventory to compliant properties * **Media buy creation**: Reference property lists to constrain where ads can run * **Authorization**: adagents.json validates agent authority to sell ### Property Governance + Signals Both protocols operate on properties but serve different purposes: | Signals Protocol | Property Governance | | ------------------------- | -------------------------------- | | Audience/contextual data | Property metadata and compliance | | "Who should see this ad?" | "Where can this ad run?" | | Signal activation | Property filtering | ## Tasks ### Discovery * **[`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)**: Discover governance capabilities including property features (protocol-level task) ### Property List Management * **[create\_property\_list](/docs/governance/property/tasks/property_lists#create_property_list)**: Create a new property list on a governance agent * **[get\_property\_list](/docs/governance/property/tasks/property_lists#get_property_list)**: Retrieve resolved properties (with caching guidance) * **[update\_property\_list](/docs/governance/property/tasks/property_lists#update_property_list)**: Modify filters or base properties * **[delete\_property\_list](/docs/governance/property/tasks/property_lists#delete_property_list)**: Remove a property list ## Getting Started **Publishers:** 1. Create `/.well-known/adagents.json` with property definitions 2. Authorize sales agents for your properties 3. Declare governance agents in `property_features` (sustainability vendors for carbon, quality vendors for MFA and ad load, suitability vendors for content classification, etc.) **Buyers:** 1. Discover governance agents by reading `property_features` from publishers' adagents.json files 2. Query each governance agent's `get_adcp_capabilities` for capabilities 3. Create property lists on relevant governance agents with filters and brand references 4. Aggregate results into a final compliant list 5. Share property list references with sellers (with auth tokens) **Governance Agent Implementers:** 1. Implement [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) to advertise your capabilities in `governance.property_features` 2. Implement property list CRUD operations 3. Support webhooks to notify buyers when evaluations change 4. Work with publishers to get listed in their `property_features` 5. See the [Protocol Specification](/docs/governance/property/specification) for implementation details See the [Protocol Specification](/docs/governance/property/specification) for detailed implementation guidance. # マネージドネットワークデプロイ Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/property/managed-networks マネージドパブリッシャーネットワークが、URL 参照パターン、委譲タイプ、一般的なインフラアプローチを使って、数千のドメイン全体で adagents.json をどうデプロイするか。 マネージド広告ネットワーク(例: 数百または数千のパブリッシャードメインを運用するネットワーク)は、既に HTTP リダイレクトまたは集中ホスティング経由で `ads.txt` を配布しています。`adagents.json` は組み込みの委譲モデルを通じて同じスケールをサポートします: [URL 参照パターン](/docs/governance/property/adagents#url-reference-pattern)。 このガイドは、既存の `ads.txt` デプロイ知識を `adagents.json` にマップし、ネットワークスケールで機能するインフラパターンをカバーします。 ## ads.txt 配布との比較 `ads.txt` と `adagents.json` の両方が、各パブリッシャーオリジンの well-known パスにファイルを要求します。デプロイの仕組みは似ていますが、`adagents.json` には、ネットワークが `ads.txt` に通常使う HTTP リダイレクトパターンを置き換える組み込みの委譲モデルがあります。 | Concern | `ads.txt` | `adagents.json` | | ------------ | --------------------------- | --------------------------------------- | | ファイル位置 | `/ads.txt` | `/.well-known/adagents.json` | | 委譲メカニズム | HTTP 301/302 リダイレクト | `authoritative_location` フィールド(ファイル内参照) | | 委譲が表現するもの | 「このファイルは別の場所に存在する」 | 「このパブリッシャーは名指しされた権威に委譲する」 | | パブリッシャーの意図 | 曖昧(リダイレクトはインフラかも) | 明示的(ポインターファイルは宣言) | | 認可のスコープ | フラット(`DIRECT` / `RESELLER`) | 構造化(プロパティ、プレースメント、国、時間ウィンドウ、委譲タイプ) | | スケールでのキャッシング | 各ドメインが独立にキャッシュ(重複排除なし) | 検証者はそれを参照するすべてのドメインの 1 つの権威ファイルをキャッシュ | | ファイル形式 | プレーンテキスト、行ごと 1 エントリー | スキーマ検証付き JSON | キーの違い: HTTP リダイレクトは消費者に不可視です。301 をフォローする検証者は、リダイレクトが「パブリッシャーがこのネットワークに委譲する」を意味するか「CDN がパスを再編成した」を意味するかを判別できません。`authoritative_location` フィールドは委譲を明示的なパブリッシャー宣言にします。 ## ポインターファイルパターン 各マネージドドメインは `/.well-known/adagents.json` に最小限のポインターファイルをホストします。ポインターは、ネットワークが保守する 1 つの集中化された権威ファイルを参照します。 **ポインターファイル**(各ドメイン上): ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "authoritative_location": "https://network.example.com/adagents/v2/adagents.json", "last_updated": "2025-06-01T00:00:00Z" } ``` ポインターファイルの `last_updated` タイムスタンプは、権威ファイルが更新されたときではなく、ポインター自体が最後に変更されたとき(例: `authoritative_location` URL が変わったとき)を反映します。権威ファイルは自身の `last_updated` を運びます。 **権威ファイル**(ネットワークにて): ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "contact": { "name": "Example Network Ad Operations", "email": "adops@network.example.com", "domain": "network.example.com" }, "properties": [ { "property_id": "site_cooking_daily", "property_type": "website", "name": "Cooking Daily", "identifiers": [{"type": "domain", "value": "cookingdaily.com"}], "tags": ["food", "managed_network"], "publisher_domain": "cookingdaily.com" }, { "property_id": "site_garden_weekly", "property_type": "website", "name": "Garden Weekly", "identifiers": [{"type": "domain", "value": "gardenweekly.com"}], "tags": ["home", "managed_network"], "publisher_domain": "gardenweekly.com" } ], "tags": { "managed_network": { "name": "Managed Network", "description": "All domains managed by Example Network" } }, "authorized_agents": [ { "url": "https://sales.network.example.com", "authorized_for": "All managed network properties", "authorization_type": "property_tags", "property_tags": ["managed_network"], "delegation_type": "ad_network" } ], "last_updated": "2025-06-01T00:00:00Z" } ``` ### 検証者がポインターファイルをどう解決するか ```mermaid theme={null} sequenceDiagram participant V as Validator participant P as cookingdaily.com participant N as network.example.com V->>P: GET /.well-known/adagents.json P-->>V: {"authoritative_location": "https://network.example.com/..."} Note over V: Detect pointer — single hop allowed V->>N: GET /adagents/v2/adagents.json N-->>V: Full adagents.json (properties, agents, placements) Note over V: Verify: no nested authoritative_location Note over V: Validate against schema ``` 検証者はポインターファイルをフェッチし、`authoritative_location` URL をフォローし、権威ファイルを通常のインライン構造として検証します。 **単一ホップのみ。** 権威ファイルはそれ自体で `authoritative_location` を含んではなりません。これはリダイレクトチェーンと無限ループを防ぎます。 ### 1 つの権威ファイル対パブリッシャーごとのファイル 上の例は、すべてのドメインが同じ権威ファイルを指すことを示します。これは、すべてのパブリッシャーが同じエージェント、委譲タイプ、プレースメント構造を共有するときに機能します。 パブリッシャーごとの権威ファイルは、取り決めがネットワーク全体で異なるときに意味をなします: * 異なるパブリッシャーが異なるエージェントを認可(一部はネットワークと並んで自身の直接販売を持つ) * 異なる委譲タイプ(パブリッシャー A は `ad_network` のみ、パブリッシャー B はプレミアムプレースメントの `direct` パスを保持) * 異なるプレースメント構造(あるパブリッシャーは `pre_roll` と `host_read` を持ち、別のは `display_banner` のみ) * `property_features` の異なるガバナンスベンダー このモデルでは、各ポインターファイルはパブリッシャー固有の URL を参照します: ``` cookingdaily.com/.well-known/adagents.json → "authoritative_location": "https://network.example.com/adagents/cookingdaily.json" gardenweekly.com/.well-known/adagents.json → "authoritative_location": "https://network.example.com/adagents/gardenweekly.json" ``` ネットワークは依然としてすべての権威ファイルを集中ホストします — ポインターファイルは異なるパスを参照するだけです。[CI/CD pipeline](#cicd-pipeline) パターンは自然なフィットです: 中央データベースからパブリッシャーごとの権威ファイルを生成しネットワークの CDN にデプロイします。 1 つの共有ファイルで始めてください。パブリッシャーが個別の取り決めを交渉するにつれパブリッシャーごとのファイルに移行します。 ### Why not HTTP redirects? HTTP リダイレクトは `ads.txt` に機能します。なぜなら `ads.txt` は自己参照セマンティクスのないフラットリストだからです。クローラーはリダイレクトチェーンをフォローし最終ファイルを検証します。 `adagents.json` には、HTTP リダイレクトは問題を引き起こします: * **曖昧な意図。** リダイレクトは委譲、インフラ移行、または CDN ルーティングを意味しうる。ポインターファイルは委譲を明示的に宣言します。 * **スコーピングなし。** HTTP リダイレクトは全か無かです。ポインターファイルは、ネットワークが販売を認可された正確なものを宣言できる構造化された認可モデルと並びます。 * **キャッシングペナルティ。** HTTP リダイレクトでは、検証者は 10,000 のドメインすべてが同じファイルにリダイレクトすることを知る方法がありません。各レスポンスを独立として扱わなければなりません — 同一コンテンツの 10,000 キャッシュエントリー。`authoritative_location` では、検証者はすべてのポインターファイル全体で同じ URL を見て権威ファイルを一度キャッシュします。数千のドメインを持つネットワークには、これは 1 キャッシュエントリーと数千の違いです。 それらの問題は、リダイレクトを使って **委譲** を表現すること — 「私の認可は別の場所に存在する」と言うクロスドメインホップ — についてです。それが `authoritative_location` が置き換えるものです。それらは **ホスティング正規化** についてではありません。それはパブリッシャーのアペックスドメインが `www` に `301` リダイレクトする通常のケース(ほとんどのマネージドホスティングと CDN のデフォルト)です。2 つはリダイレクトがどこを指すかで区別されるため、フェッチルールはそれに応じて分割されます: * **同一登録可能ドメインのリダイレクトはフォローされなければなりません(MUST)** — `/.well-known/adagents.json` フェッチで — `apex ↔ www`、および同じ登録可能ドメイン(eTLD+1)に留まる任意のリダイレクト — すべてのホップで [SSRF 制御](/docs/building/by-layer/L1/security#webhook-url-validation-ssrf) を再検証し、非 HTTPS スキームへの任意のリダイレクトを拒否し、チェーンを 3 ホップで上限とします。これらを黙って拒否することは、正しく構成されたパブリッシャーを未認可としてレポートします。同一登録可能ドメイン比較は、前のホップではなくすべてのホップで **元々リクエストされた** ドメインにアンカーされなければならず(MUST)、`same → cross` の 2 ホップチェーンがそれを逃れられないようにします。 * **クロス登録可能ドメインのリダイレクトはこのフェッチで拒否されなければなりません(MUST)。** クロスドメインリダイレクトは、このパターンが置き換えるために存在する曖昧でスコープされていない委譲シグナルです — 代わりに `authoritative_location` で委譲を明示的に宣言します。検証者はリダイレクト **拒否** で `authoritative_location` を 2 番目のホップとしてデリファレンスします([Security considerations](#security-considerations) を参照): 名指しされた URL が宣言された権威的位置であり、そこから離れるリダイレクトはその宣言を変えます。 ## 正しい委譲タイプの選択 ネットワークがマネージドパブリッシャーに代わってエージェントを認可するとき、`delegation_type` フィールドは商業関係を記述します: | `delegation_type` | Use when | Example | | ----------------- | ---------------------------------------------- | ------------------------------------- | | `direct` | ネットワークが運用してもパブリッシャーがこれを自身の販売チャネルとして扱う | パブリッシャーとしてブランド化されたホワイトラベルセールスエージェント | | `delegated` | パブリッシャーがネットワークに代わって販売することを認可 | 明示的なパブリッシャー契約を持つレップファーム | | `ad_network` | 在庫はパブリッシャーのエンドポイントとしてではなくネットワークのパッケージを通じて販売される | ポートフォリオ全体で販売する Mediavine 型マネージドネットワーク | ほとんどのマネージドネットワークは `ad_network` を使います。個別のパブリッシャーが自身の商業アイデンティティを維持するがネットワークが彼らを代表することを認可するとき `delegated` を使います。ネットワークがパブリッシャーが自身の販売インフラとして提示するものを運用するときのみ `direct` を使います。 単一の権威ファイルは委譲タイプを混合できます — 異なるエージェントが同じ在庫と異なる関係を持てます: ```json theme={null} { "authorized_agents": [ { "url": "https://sales.network.example.com", "authorized_for": "Network-sold inventory across all managed properties", "authorization_type": "property_tags", "property_tags": ["managed_network"], "delegation_type": "ad_network" }, { "url": "https://premium.publisher.example.com", "authorized_for": "Publisher's direct premium sales", "authorization_type": "property_ids", "property_ids": ["site_cooking_daily"], "delegation_type": "direct", "placement_tags": ["premium"], "exclusive": true } ] } ``` ## プロパティタグでファイルを効率的に保つ 500 のプロパティと 3 つの認可エージェントを持つマネージドネットワークは、すべてのエージェントエントリーですべてのプロパティ ID をリストできます — しかしそれは 1,500 のプロパティ対エージェントマッピングの保守を意味します。プロパティタグはその冗長性を排除します。 原則: **各プロパティを一度リスト** し、その識別子とタグを付け、次に **エージェントをタグで認可** します。 ```json theme={null} { "properties": [ { "property_id": "site_cooking_daily", "property_type": "website", "name": "Cooking Daily", "identifiers": [{"type": "domain", "value": "cookingdaily.com"}], "tags": ["managed_network", "food"], "publisher_domain": "cookingdaily.com" }, { "property_id": "site_garden_weekly", "property_type": "website", "name": "Garden Weekly", "identifiers": [{"type": "domain", "value": "gardenweekly.com"}], "tags": ["managed_network", "home"], "publisher_domain": "gardenweekly.com" } ], "tags": { "managed_network": { "name": "Managed Network", "description": "All domains managed by Example Network" }, "food": { "name": "Food & Cooking", "description": "Food and cooking content verticals" }, "home": { "name": "Home & Garden", "description": "Home and garden content verticals" } }, "authorized_agents": [ { "url": "https://sales.network.example.com", "authorized_for": "All managed network properties", "authorization_type": "property_tags", "property_tags": ["managed_network"], "delegation_type": "ad_network" }, { "url": "https://food-vertical-agent.example.com", "authorized_for": "Food vertical properties only", "authorization_type": "property_tags", "property_tags": ["food"], "delegation_type": "delegated" } ] } ``` 各プロパティは一度現れます。タグがマッピングを処理します。新しいドメインがネットワークに参加するとき、正しいタグで `properties` に追加します — 認可エントリーは変更不要です。新しいバーティカルエージェントが加わるとき、関連タグで 1 つのエージェントエントリーを追加します。 これはファイルを、数千のプロパティでも可読、保守可能、コンパクトに保ちます。 ## 単一の認可の下で多くのパブリッシャーを表現する 上のパターンは、自身のプロパティをリストする単一のパブリッシャーの `adagents.json` 用です。多くの *他の* パブリッシャーを表現するマネージドネットワーク(WordPress ネットワーク、コンテンツレコメンデーションネットワーク、マルチプロパティホールディングカンパニー構成)は、各表現されるパブリッシャーに代わってどのエージェントが販売を認可されるかを宣言する 1 つの権威ファイルをホストします。 すべての表現されるパブリッシャーが同じタグ述語の下で同じエージェントに委譲するとき — 正準マネージドネットワーク形状 — `publisher_properties` のコンパクトな `publisher_domains[]` 形式を使います: ```json theme={null} { "authorized_agents": [{ "url": "https://agent.network.example/api", "authorized_for": "Managed-network inventory across represented publishers", "authorization_type": "publisher_properties", "publisher_properties": [{ "publisher_domains": ["site1.example", "site2.example", "site3.example"], "selection_type": "by_tag", "property_tags": ["managed_network"] }], "delegation_type": "ad_network" }] } ``` パブリッシャーごとではなく共有セレクター述語ごとに 1 エントリー。上記は、リストされた各ドメインごとに単数形式エントリーを繰り返すのとセマンティックに同一です。完全な仕組み、単数対コンパクトコントラクト、`managerdomain` ads.txt フォールバックとの相互作用については [adagents.json リファレンスの Pattern 4](/docs/governance/property/adagents#pattern-4-publisher-property-references) を参照してください。コンパクト形式は、表現されるパブリッシャー数ではなく *異なるセレクター述語の数*(通常は小さな定数)と線形にスケールします。 ## プレースメントで各エージェントが何を販売できるかを制御する すべての認可されたセラーがすべてにアクセスできるように見える `ads.txt` と異なり、`adagents.json` はネットワークが各エージェントが販売を認可された正確なプレースメントを宣言できるようにします。これは販売レバレッジを保持します — バイヤーはプレミアム在庫が特定のパスを通じてのみ利用可能であることを見られます。 トップレベルで一度プレースメントを定義し、グループ化のためタグ付けし、次に各エージェントの認可を特定のプレースメントタグにスコープします: ```json theme={null} { "placement_tags": { "programmatic": { "name": "Programmatic", "description": "Placements available through programmatic sales paths" }, "direct_only": { "name": "Direct only", "description": "Premium placements reserved for direct network sales" } }, "placements": [ { "placement_id": "bottom_native_feed", "name": "Bottom-of-page native feed", "tags": ["programmatic"], "property_tags": ["managed_network"] }, { "placement_id": "page_takeover", "name": "Full-page takeover", "tags": ["direct_only", "premium"], "property_tags": ["managed_network"] }, { "placement_id": "sidebar_display", "name": "Sidebar display", "tags": ["programmatic"], "property_tags": ["managed_network"] } ], "authorized_agents": [ { "url": "https://taboola.com/agent", "authorized_for": "Bottom-of-page native feed across all managed properties", "authorization_type": "property_tags", "property_tags": ["managed_network"], "placement_tags": ["programmatic"], "delegation_type": "ad_network" }, { "url": "https://sales.network.example.com", "authorized_for": "Premium direct-sold placements", "authorization_type": "property_tags", "property_tags": ["managed_network"], "placement_tags": ["direct_only"], "delegation_type": "direct", "exclusive": true } ] } ``` この例では、Taboola はプログラマティックプレースメント(ページ下部ネイティブフィードとサイドバー)のみを販売できます。ネットワーク自身の販売チームがページテイクオーバーへの排他的アクセスを持ちます。このファイルを読むバイヤーエージェントは、どのパスがどの在庫につながるかを正確に知ります — 誰が何を販売できるかについて曖昧性はありません。 ## 追加の認可修飾子 プロパティタグとプレースメントタグを超えて、エージェントは以下でスコープできます: * **`countries`** — 地理で制限(例: `["US", "CA"]`) * **`effective_from` / `effective_until`** — 季節または試用取り決めのための時間制限付き認可 * **`exclusive`** — これがスコープされた在庫の唯一の認可パスかを宣言 ## パブリッシャーが認可しているもの パブリッシャーのドメインがポインターファイルをホストするとき、彼らは権威ファイルが彼らを代弁すると宣言しています。これは意味します: * 権威ファイルにリストされたエージェントはパブリッシャーの在庫を販売する認可を受けている * 各エージェントエントリーの `delegation_type` は商業関係を記述する * 修飾子(`placement_tags`、`countries`、`exclusive` など)は各エージェントが何を販売できるかをスコープする ネットワークがドメインインフラを運用する場合、パブリッシャーは通常ネットワーク契約を通じて既にこれに同意しています。しかしポインターファイルはその同意の機械可読な宣言です。パブリッシャーがネットワークを去る場合、ポインターファイルを削除または置き換えることが認可を即座に取り消します。 ## Deployment patterns これらのパターンはすべて同じことを達成します: 各マネージドドメインの `/.well-known/adagents.json` で静的 JSON ポインターファイルを提供します。既存のインフラに基づいて選んでください。 ### CDN エッジ関数 CDN ワーカーまたはエッジ関数からポインターファイルを提供します。これは、パブリッシャーの DNS と CDN を既に管理するネットワークに最も一般的なパターンです。 **Cloudflare Worker:** ```javascript theme={null} export default { async fetch(request) { const url = new URL(request.url); if (url.pathname === '/.well-known/adagents.json') { return new Response(JSON.stringify({ "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "authoritative_location": "https://network.example.com/adagents/v2/adagents.json", "last_updated": "2025-06-01T00:00:00Z" }), { headers: { 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=86400', 'Access-Control-Allow-Origin': '*' } }); } return fetch(request); } }; ``` **AWS CloudFront function:** ```javascript theme={null} function handler(event) { if (event.request.uri === '/.well-known/adagents.json') { return { statusCode: 200, statusDescription: 'OK', headers: { 'content-type': { value: 'application/json' }, 'cache-control': { value: 'public, max-age=86400' }, 'access-control-allow-origin': { value: '*' } }, body: JSON.stringify({ "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "authoritative_location": "https://network.example.com/adagents/v2/adagents.json", "last_updated": "2025-06-01T00:00:00Z" }) }; } return event.request; } ``` ### CMS プラグイン WordPress または類似の CMS インストールを管理するネットワークには、プラグインがサーバー構成に触れずにポインターファイルを提供できます。 **WordPress (mu-plugin):** ```php theme={null} 'https://adcontextprotocol.org/schemas/v3/adagents.json', 'authoritative_location' => 'https://network.example.com/adagents/v2/adagents.json', 'last_updated' => '2025-06-01T00:00:00Z', ]); exit; } }); ``` これをマネージドインストール全体で `wp-content/mu-plugins/` に置きます。Must-use プラグインはアクティベーションなしに自動的にロードされます。 ### CI/CD pipeline 中央構成からポインターファイルを生成し、各サイトと並んで静的アセットとしてデプロイします。 **GitHub Actions example:** ```yaml theme={null} name: Deploy adagents.json pointer files on: push: branches: [main] paths: ['config/managed-domains.json'] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Generate pointer files run: | TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") for domain in $(jq -r '.domains[]' config/managed-domains.json); do mkdir -p "dist/${domain}/.well-known" cat > "dist/${domain}/.well-known/adagents.json" < report.json - name: Check for failures run: | ISSUES=$(jq '.orphanedPointers + .stalePointers + .missingPointers + .schemaErrors | length' report.json) if [ "$ISSUES" -gt 0 ]; then echo "::error::Network consistency check found $ISSUES issues" jq '.' report.json exit 1 fi ``` ## トラブルシューティング マネージドネットワークデプロイで発生する 5 つの失敗モード、それらの検出方法、修正方法。 ### Orphaned pointer **何が起こったか:** パブリッシャードメインがあなたの権威 URL を参照するポインターファイルを持つが、権威ファイルがそのドメインを `properties` にリストしていない。 **どう見えるか:** バイヤーエージェントが `cookingdaily.com/.well-known/adagents.json` をフェッチし、ネットワークの権威ファイルへのポインターをフォローし、`publisher_domain: "cookingdaily.com"` を持つプロパティを見つけない。ドメインはそれを主張しないネットワークに委譲するように見える。 **一般的な原因:** ネットワークがパブリッシャーを権威ファイルから削除した(例: 契約終了)が、ドメインのポインターファイルが削除されなかった。 **修正:** プロパティを権威ファイルに再追加するか、パブリッシャーのドメインのポインターファイルを削除/置き換えます。ネットワークがもはやドメインの DNS または CDN を管理しない場合、パブリッシャーと調整してポインターを削除します。 **検出:** `npx adcp check-network --url ` がこれらを orphaned pointer としてレポートします。 ### 古いポインター **何が起こったか:** パブリッシャーのポインターファイルが、関係が終わった後もネットワークの権威 URL を依然として参照する。orphaned pointer に似ているが、パブリッシャーの視点から — ドメインがもはや認可しないネットワークへの委譲を依然として主張する。 **一般的な原因:** ネットワークがパブリッシャーを終了したがドメインのインフラを制御しない。パブリッシャーが well-known パスを更新していない。 **修正:** パブリッシャーがポインターファイルを更新または削除しなければならない。ネットワークは権威ファイルから彼らを削除するときパブリッシャーに通知すべき。AAO レジストリはクロール中にこの不一致を検出しネットワークヘルス監視で表示します。 ### 欠けているポインター **何が起こったか:** ドメインが権威ファイルの `properties` に(`publisher_domain` 経由で)リストされているが、そのドメインの `/.well-known/adagents.json` が存在しないか期待される権威 URL を指さない。 **どう見えるか:** ネットワークはドメインを表現すると主張するが、ドメインは委譲を確認しない。バイヤーエージェントは認可チェーンを検証できない。 **一般的な原因:** パブリッシャーが最近ネットワークに参加したがポインターファイルがまだデプロイされていない、またはデプロイが失敗した。 **修正:** 上の [deployment patterns](#deployment-patterns) の 1 つを使ってポインターファイルをドメインにデプロイします。以下で検証: ```bash theme={null} curl -s https://newpublisher.com/.well-known/adagents.json | jq '.authoritative_location' ``` ### スキーマエラー **何が起こったか:** 権威ファイルに検証エラーがある — 不正な形式の JSON、欠けている必須フィールド、無効なフィールド値。 **影響:** 1 つの悪いデプロイが、すべてが同じファイルを参照するため、ネットワークのすべてのドメインの検証を壊します。 **修正:** デプロイ前に権威ファイルを検証: ```bash theme={null} # Validate against the JSON schema npx adcp check-network --url https://network.example.com/adagents/v2/adagents.json ``` 開発中のインタラクティブ検証には [AdAgents.json Builder](https://agenticadvertising.org/adagents/builder) を使います。CI/CD パイプラインにプリデプロイチェックとしてスキーマ検証を追加します。 ### エージェントエンドポイント到達不能 **何が起こったか:** `authorized_agents` エントリーの URL が応答しないかエラーを返す。バイヤーエージェントは認可で宣言されたセールスエージェントに到達できない。 **一般的な原因:** エージェントサービスがダウン、URL 変更、または DNS が誤構成。 **修正:** エージェントエンドポイントが到達可能で有効なエージェントカードを返すことを検証: ```bash theme={null} # A2A agent curl -s https://sales.network.example.com/.well-known/agent-card.json | jq . # Check via AdCP client npx adcp check-network --url ``` `check-network` コマンドはすべてのエージェントエンドポイントを検証し応答時間をレポートするため、バイヤーより前に遅いまたは失敗するエージェントを捕捉できます。 ## Security considerations 権威ファイルへの 1 つのデプロイが、ネットワークのすべてのパブリッシャー全体で認可を変えます。そのスケールがポイントで、それはまた爆発半径です — 侵害されたネットワーク CDN は、数千のドメイン全体で悪意あるセールスエージェントを同時に認可できます。スキーマの正しさを超える 2 つの具体的な含意: **検証者フェッチセマンティクス。** 権威 URL はネットワーク制御のオリジンを指します。明示的なフェッチルールなしでは、誤動作するオリジンがキャッシュを汚染したり検証者をハングさせたりできます。検証者は以下をしなければなりません(MUST): * 有効な証明書で HTTPS 上でのみ接続し、リダイレクトのフォローを拒否します(リダイレクトは宣言された位置を変える — エラーとして扱う)。 * 2 層ポリシーでレスポンスサイズを上限とします: `/.well-known/adagents.json` で提供されるポインターファイル(インラインか `authoritative_location` を運ぶか)は [一般 SSRF ボディ上限](/docs/building/by-layer/L1/security#webhook-url-validation-ssrf) の 5 MB を保ちます — パブリッシャーごとのポインターファイルは小さいはずで、5 MB は誤構成を捕捉します。`authoritative_location` をデリファレンスして到達する権威ファイル(2 番目のホップ)は、20 MB のより高い推奨上限を使います。なぜならそのオリジンはパブリッシャーネットワーク全体にファンアウトすることに明示的にオプトインしており、日常的に数千のプロパティまたはパブリッシャードメインを列挙する必要があるからです。両ホップで短い connect/read タイムアウト(各 ≤ 10s)を強制します。 * 5xx またはタイムアウトで、フェイルクローズするのではなく最大 24 時間、以前キャッシュされた権威ファイルを提供します。一時的な CDN 障害は失効ではありません。 * 少なくとも 24 時間ごとにリフレッシュを試みます。繰り返しの 5xx レスポンスはキャッシュを延長してはなりません(MUST NOT) — 7 日絶対上限は、任意の種類の最新のレスポンスからではなく、最新の成功したフェッチから測定されます。 * オリジンの `Cache-Control` にかかわらず、最新の成功したフェッチから 7 日でキャッシュ寿命を上限とします。その後、フェイルクローズします — ネットワークはオリジンを修正する 7 日を持っていました。 * 非単調な `last_updated`(リフレッシュされたファイルのタイムスタンプがキャッシュされたファイルより古い)を無効なレスポンス、5xx と同等として扱います: キャッシュを提供し、アラートし、古いファイルを採用しません。これは、攻撃者が以前失効したエージェントを再確立するため古いファイルを再提供するロールバック攻撃をブロックします。 **増分リフレッシュ(条件付きリクエスト)。** すべての消費者で単純にリフレッシュされる 20 MB の権威ファイルは両側で高価で、すべてのパブリッシャーチャーンを完全な再ウォークに変えます。権威ファイルオリジンは HTTP 条件付きリクエストをサポートすべきで(SHOULD)、検証者はそれらを使うべきです(SHOULD): * オリジンは、ファイル内容が変わるたびに再生成される `ETag` と `Last-Modified` ヘッダーを権威ファイルで発行すべき(SHOULD)。 * 検証者は、すべてのリフレッシュで `If-None-Match`(推奨)または `If-Modified-Since` を送り、`304 Not Modified` を「キャッシュは有効なまま、この成功から 7 日クロックを再開」として扱うべき(SHOULD) — キャッシュ寿命目的では成功したボディフェッチと同じ効果。 * `authorized_agents[]` ごとのオプションの `last_updated` フィールド(`adagents.json` スキーマを参照)は、消費者に部分ウォークインデックス化の 2 番目の軸を与えます: ファイルレベルの `last_updated` でキーされたインデックスを既に持つ検証者は、自身の `last_updated` がインデックス値より古い authorized-agents エントリーをスキップできます。これは助言的です — 消費者はそれを無視して完全なファイルを再インデックスしてもよい(MAY)。 条件付きリクエストは、スケールでのマネージドネットワークの部分更新プロトコルです。それらなしでは、週次でチャーンする 3,000 パブリッシャーを持つネットワークは、すべてのバイヤー側検証者にリフレッシュごとバイヤーごと 20 MB のダウンロードを強います。それらありで、定常状態コストは 1 ラウンドトリップと 304 です。 ### Publisher revocation (the exit lifecycle) `publisher_properties[].publisher_domains[]` からパブリッシャードメインを削除するだけでは不十分です — 下流検証者のキャッシュされた権威ファイルは、去ったパブリッシャーを最大 7 日キャッシュ上限まで認可し続けます。次のリフレッシュで伝播する必要がある失効(紛争でネットワークを去るパブリッシャー、コンプライアンス問題、誤構成クリーンアップ)には、権威ファイルはトップレベルの `revoked_publisher_domains[]` 配列に `revoked_at` タイムスタンプでパブリッシャーをリストしなければなりません(MUST)。 検証者の動作: * 検証者は、`revoked_publisher_domains[]` の任意のパブリッシャードメインを、同じドメインが依然として任意の `authorized_agents[].publisher_properties[].publisher_domain` / `.publisher_domains[]`、`authorized_agents[].properties[].publisher_domain`(`inline_properties` 認可タイプ)、またはファイル内の他の場所のトップレベル `properties[].publisher_domain` に現れるかどうかに **かかわらず**、もはや認可されていないものとして扱わなければなりません(MUST)。失効リストが優先します — これはネットワークがすべてのセレクターエントリーを再デプロイせずに失効を出荷できるようにします。 * ファイルのキャッシュされた以前バージョンを持つ検証者は、エントリーの `revoked_at`(キャッチアップエントリーでは過去でありうる)ではなく、検証者の `last_updated` 採用時点で失効を適用しなければなりません(MUST)。 * **検証者側の追加のみ耐久性。** 検証者が `revoked_publisher_domains[]` エントリーで一度でも観測した `publisher_domain` は、後続のフェッチでエントリーが欠けていても、**検証者がそのドメインについて観測した最も早い `revoked_at` から 7 日** 失効として保持されなければなりません(MUST)。耐久性は `publisher_domain` のみでキーされます — `(publisher_domain, revoked_at)` タプルでキーすると、攻撃者がわずかに変異した `revoked_at`(例えば 1 秒早い)で同じドメインを再発行し、検証者が観測したことのない「新鮮な」タプルを提示し、保持をバイパスできます。`revoked_at` はクロック原点を設定するためだけに使い、耐久性キーの識別には使いません。これは耐久性をネットワークの保持 SHOULD ではなく検証者のキャッシュ状態に置き、ロールバックギャップを閉じます: `revoked_publisher_domains[]` を削除し `last_updated` を進めた古いファイルを再提供する攻撃者は、検証者の 7 日ウィンドウ内で以前失効したパブリッシャーを再確立できません。現在のフェッチで初めて見られる新しい失効(以前の観測なし)は、今から 7 日クロックを開始します。 * **再起動間の永続性。** 検証者は観測された失効エントリー(`{publisher_domain, earliest_revoked_at, first_observed_at}`)を耐久ストレージに永続化すべきです(SHOULD)。7 日ウィンドウ内で再起動するメモリ内のみの検証者は、以前の観測の記録がないためロールバックされたファイルを受け入れるかもしれません(MAY)。オペレーターはこれを既知の制限として扱い、インデックスを永続化するか残余リスクを受け入れるべきです(SHOULD)。7 日ウィンドウは、プロセス開始からではなく *検証者の* 最初の観測から測定されます。 * ネットワークは、初回出現中にエントリーを観測しなかった検証者が次のリフレッシュで依然としてそれを拾えるよう、各 `revoked_publisher_domains[]` エントリーを `revoked_at` の後少なくとも 7 日保持すべきです(SHOULD)。 マルチパブリッシャー退出または通常の商業関係終了下の日常チャーンには、ネットワークは `reason: "relationship_ended"` を使うべきで(SHOULD)、検証者の変更検出が日常失効のアラートを抑制し残りをレビューにルーティングできるようにします。 **以前失効したパブリッシャーの再認可。** `revoked_until` フィールドも un-revoke 動詞もありません。再認可するには、ネットワークは、`revoked_at` から検証者側 7 日耐久性ウィンドウが経過した *後* に `revoked_publisher_domains[]` からエントリーを削除します。より早くエントリーを削除することは、元の失効を観測した検証者では no-op です(彼らはウィンドウの残りの間ローカルで失効を保持します)。同週の再確立が運用上要求される時間制限付きコンプライアンスプルには、失効を `reason: "compliance_violation"` として実行し帯域外で再確立を調整することを優先します。スキーマは意図的にネットワークに 7 日前の再認可バックドアを与えません。それはロールバック攻撃と同じ表面になります。 **失効エントリーの拡張フィールドは規範的効果を持ちません。** `revoked_publisher_domains[]` アイテムはプロジェクト全体の `additionalProperties: true` ポリシーを使いますが、検証者はこれらのエントリーの未知のフィールドを無視しなければなりません(MUST) — 拡張は失効セマンティクスを緩めたりサイドチャネル再確立シグナルを運んだりできません。 **伝播レイテンシー。** `(agent, publisher_domain)` でキーされたメモリ内認可インデックスを維持する検証者は、`revoked_publisher_domains[]` にパブリッシャーをリストする `adagents.json` を検証者が *成功して* 再フェッチした後、1 クロール間隔を超えてペアを認可し続けてはなりません(MUST NOT)。上のフェッチセマンティクスからのキャッシュ制限された古さ(5xx での 24 時間フォールバック、7 日絶対上限)が適用されます — 境界は、任意の壁時計時間ではなく最新の成功したフェッチに対して保持されます。クロールバックログと中間 CDN キャッシングは実際のレイテンシーを延ばします。要件はネットワークではなく検証者自身のパイプラインにあります。 検証者はまた、次のクロールサイクルだけでなく **マニフェスト取り込み中に同期的に** 失効を適用すべきです(SHOULD): `revoked_publisher_domains[]` が *任意の* パブリッシャードメインをリストする任意の `adagents.json` を採用するとき — 検証者が現在そのために任意のエージェントを認可するかどうかにかかわらず — 検証者は次の認可決定を提供する前に、そのパブリッシャーについて保持するすべての `(agent, publisher_domain)` エントリーをメモリ内インデックスから削除すべきです(SHOULD)。これは上の追加のみ耐久性ルールと合成し、置き換えません: メモリ内インデックス更新がライブクエリを新鮮に保つもので、7 日保持がロールバック攻撃を生き延びるものです。 **リファレンス実装(非規範的)。** 他の検証者は上の要件を異なるメカニズムで満たしてもよい(MAY)。このリポジトリのチェーンは: (a) ライターの失効ブランチが一致するカタログ行を退役させる。(b) 次のクロールパスがカタログから `(agent, publisher_domain)` セットを再スナップショットする(退役した行を除く)。(c) 前後の diff がドロップされた各ペアに `authorization.revoked` イベントを発行する。(d) レジストリ同期消費者がイベントをメモリ内インデックスに適用する。`authorization.revoked` イベント形状はリファレンス実装の語彙で、規範的なワイヤー形式ではありません。 **変更検出。** 1 つのデプロイがすべてのパブリッシャーに影響するため、バイヤーエージェントと検証者は以前の権威ファイルを保存し各リフレッシュで diff し、外れ値の変更でアラートすべきです(SHOULD)。パートナーが初日に実装できる具体的なデフォルトしきい値: 以前のフェッチに存在しなかった新しく追加された任意の `authorized_agents` エントリー、任意の委譲タイプダウングレード(例えば `exclusive` エントリーが非排他的になる)、10% または絶対 50 プロパティを超える任意のプロパティ数減少、`authoritative_location` 自体への任意の変更。そこからチューニングします — 目標は日常更新を抑制することではなく、支出をルーティングする前に侵害されたデプロイを捕捉することです。 **ポインター整合性(パブリッシャーごとのスワップ脅威)。** 上の *検証者フェッチセマンティクス* と *変更検出* ルールは、ネットワーク側の権威ファイルとオリジンの侵害を防御します。それらは単一のパブリッシャーのエッジでの *ポインターファイル自体* の侵害を防御しません。1 つのパブリッシャーの `/.well-known/adagents.json` への書き込みアクセスを得た攻撃者 — そのパブリッシャーの CDN コントロールプレーン、オリジンストレージ、または DNS 経由 — は、`authoritative_location` を攻撃者制御の URL に黙って変更できます。攻撃者がパブリッシャーのドメインが解決するインフラから提供しているため、その URL の TLS は有効で、サイズ/リダイレクト/タイムアウト上限はトリガーされず、変更は検証者に正当な委譲ハンドオフとして読まれます。 この最後の性質がポインタースワップを汎用整合性失敗と区別するものです: `authoritative_location` パターンの全ポイントは、パブリッシャーが委譲する場所を変更 *できる* ことなので、検証者は正当な委譲ハンドオフを壊さずに任意のポインター変更を敵対的として扱えません。ネットワーク CDN 脅威は広く浅い(1 つの侵害、すべてのパブリッシャーがハイジャック)。ポインタースワップ脅威は狭く深い(1 つのパブリッシャーがハイジャック、しかしネットワークが監視できない表面を通じて)。両方が範囲内です。 検証者は、変更された `authoritative_location` を日常リフレッシュではなく高重大度イベントとして扱わなければなりません(MUST)。具体的には: * 検証者は変更された `authoritative_location` を自動採用してはなりません(MUST NOT)。変更が確認中の間、以前キャッシュされた権威ファイル(上の 7 日上限に従う)を提供し続けます。これは最小規範的フロアです。下の SHOULD が確認がどう得られるかを指定します。 * 検証者は、(a) 帯域外確認 — オペレーター承認、パブリッシャーサポートチャネル通知、またはアナウンスされたネットワーク移行 — または (b) 新しいポインター値が変わらないままでなければならない最小 24 時間の安定性猶予ウィンドウのいずれかの後にのみ新しい位置を尊重すべきです(SHOULD)。24h ウィンドウは未確認パスのフォールバックです。数分で完了する帯域外確認は (a) を満たし準拠します — 検証者は OOB パスに 24h フロアを課してはなりません(MUST NOT)。 * (a) の「アナウンスされたネットワーク移行」は、検証者オペレーターが検証できるパブリッシャー証明またはネットワーク証明の声明(例: 既存の信頼された鍵で副署名された署名付きアナウンス、パブリッシャーの `brand.json` `agents[]` セットへのオペレーター検証済み更新、またはオペレーターがそのパブリッシャーについて既に信頼する確立されたパブリッシャーアイデンティティチャネルの通知)を意味します。ブログ投稿やプレスリリースそれ自体は資格を持ちません。バーは公開性ではなく検証可能性です。 * 検証者は、候補権威ファイルを、公開されているときパブリッシャーの `/.well-known/brand.json` に対してクロスチェックすべきです(SHOULD)。`brand.json` が `agents[]` を宣言する場合、候補権威ファイルの `authorized_agents[]` URL は `brand.json` エージェントセットと照合されるべきです(SHOULD)。パブリッシャー自身のアイデンティティ宣言に不在のセールスエージェントを認可する権威ファイルは、ポインター侵害の強いシグナルで、オペレーターレビュー保留で採用をブロックすべきです(SHOULD)。正当なネットワーク間移行中、`brand.json` `agents[]` セットはポインター変更に遅れうる。`brand.json` の `last_updated` がポインターファイルの `last_updated` より古いとき、`brand.json`/権威の不一致を *権威矛盾* ではなく *古いクロスチェック* として扱い、移行を確認するため上のパス (a) または (b) にフォールバックします。 * 混合シグナルでは採用を拒否します: 候補権威ファイルの `last_updated` 退行と一致するポインター変更、ドメイン全体の委譲タイプダウングレード、またはエコシステム履歴のない初見のセールスエージェントは、採用ではなくキャッシュを保持しアラートする根拠です。このルールでは、*退行* は候補ファイルの `last_updated` がキャッシュされたファイルの `last_updated` より、小さなクロックスキュー許容(推奨: 60 秒)を超えて厳密に早いことを意味します。複数のエッジから提供されるポインターファイルは、通常運用で軽微な非単調性を観測できます。退行チェックはロールバック攻撃のためで、クロックジッターのためではありません。 自身のポインターファイルを管理するパブリッシャーは、他のパブリッシャーアイデンティティ表面(`/.well-known/brand.json`、DNS レコード、TLS 証明書発行)と同じインフラと変更管理制御からそれを提供すべきです(SHOULD)。ポインターファイルはアイデンティティ宣言です。それを静的マーケティングアセットとして扱うことが、スワップ脅威を実用的にする誤構成です。 **関係終了。** ポインターファイルパターンは、ネットワークがパブリッシャー DNS またはエッジを制御することに依存します。関係が終わるとき、委譲の両側が一緒に取り下げられなければなりません: * ネットワークは、既に DNS/エッジ制御を失っていても、終了時に権威ファイルの `properties` からパブリッシャーを削除しなければなりません(MUST)。一致するプロパティのない元ネットワークのファイルを依然として指すパブリッシャーは [orphaned pointer](#orphaned-pointer) になります — バイヤーに未認可として可視。 * 検証者は、キャッシュされた委譲に依存するのではなく、パブリッシャードメインが所有権を移転するとき再フェッチして再検証すべきです(SHOULD)。 **署名付きポインター(計画中)。** ポインタースワップギャップの完全な閉鎖には署名付きポインターメカニズムが必要です: ポインターファイルは、正準 `(authoritative_location, last_updated)` オブジェクトにわたるパブリッシャー制御のデタッチ署名を運び、公開鍵は帯域外にアンカーされます — `brand.json` でパブリッシャー証明、または将来の集中化されたパブリッシャー鍵レジストリ経由。署名プリミティブと鍵ディスカバリー/ローテーションモデルは合意された設計を必要とし、3.x 要件ではなく計画された AdCP 4.0 追加として追跡されます。4.0 ロールアウトを実行可能に保つため、今日ポインターファイルを公開する実装者はポインターオブジェクト形状を安定に保つべきです(SHOULD): トップレベルオブジェクトは `authoritative_location` と `last_updated` のみを含むべきで(SHOULD)、追加のトップレベルフィールドなしで、デタッチ署名が後で兄弟フィールド(または `.sig` コンパニオンパス)で、その間に追加されたカスタムフィールドと衝突せずに運べるようにします。4.0 が到達するまで、上のオペレーター側制御が規範的ベースラインです — それらは署名付きポインターの強さに一致しませんが、ポインタースワップ攻撃のコストを日常 CDN 侵害のコスト以上に上げます。それが 3.x が正直に約束できることです。 ## 次のステップ * [adagents.json 技術仕様](/docs/governance/property/adagents) — 完全なスキーマリファレンス、認可パターン、検証動作 * [プロパティガバナンス概要](/docs/governance/property) — adagents.json がより広範なガバナンスモデルにどう適合するか * [AdAgents.json Builder](https://agenticadvertising.org/adagents/builder) — インタラクティブ検証者とファイル作成者 * [@adcp/sdk](https://github.com/adcontextprotocol/adcp-client) — ネットワーク一貫性チェック付き TypeScript クライアントライブラリ # Property Governance Specification Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/property/specification AdCP プロパティガバナンスの正式仕様 — プロパティモデル、フィーチャー評価、リスト管理、配信検証。 **AdCP 3.0 提案** - この仕様は AdCP 3.0 向けに開発中です。フィードバックは [GitHub Discussions](https://github.com/adcontextprotocol/adcp/discussions) へどうぞ。 **Status**: Request for Comments **Last Updated**: January 2026 本文書における「MUST」「MUST NOT」「REQUIRED」「SHALL」「SHALL NOT」「SHOULD」「SHOULD NOT」「RECOMMENDED」「MAY」「OPTIONAL」は [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) の定義に従って解釈します。 ## Abstract Property プロトコルは、プロパティの識別・認可・データ提供・選定のための標準 MCP/A2A インターフェースを定義します。これにより、パブリッシャーはプロパティと認可エージェントを宣言し、データプロバイダーはプロパティインテリジェンスを提供し、バイヤーは準拠したプロパティ集合を選択できます。 ## 概要 The Property Protocol addresses four distinct concerns: | Concern | Question | Owner | Mechanism | | ----------------------- | ----------------- | -------------- | -------------------------------------- | | **Property Identity** | どのプロパティが存在するか | Publishers | `adagents.json` の properties 配列 | | **Sales Authorization** | 誰がこのプロパティを販売できるか | Publishers | `adagents.json` の authorized\_agents | | **Property Data** | このプロパティについて何がわかるか | Data providers | `get_adcp_capabilities` 経由のガバナンスエージェント | | **Property Selection** | 要件を満たすプロパティはどれか | Buyers | フィルター付きプロパティリスト | 最初の 2 つは adagents.json による **パブリッシャー側の宣言**、後ろの 2 つはガバナンスエージェントのデータを用いる **バイヤー側の処理** です。 ### プロパティデータと選定 プロパティデータと選定は **ステートフル** モデルです: * **Feature discovery**: Agents advertise what they can evaluate via `get_adcp_capabilities` * **Property list management**: CRUD operations for managed property lists with filters * **Brand references**: Let agents automatically apply rules based on brand identity * **Webhook notifications**: Real-time updates when resolved lists change * **Marketplace architecture**: Multiple specialized agents as subscription services すべての評価(スコアリング、フィルタリング、ディスカバリ)は `get_property_list` でリストが解決されるときに暗黙的に行われます。 ## コア概念 ### リクエストの役割と関係 Every governance request involves two key roles: #### Orchestrator (Buyer Agent) ガバナンスエージェントへ API リクエストを送るプラットフォーム/システム。メディアバイ文脈ではしばしば「buyer agent」と呼びます。 * **Examples**: DSP、トレーディングデスク、キャンペーン管理ツール * **Responsibilities**: API 呼び出し、認証、技術的なやり取りを管理 * **Account**: ガバナンスエージェントへの技術的な資格情報と API アクセスを持ちます #### Principal リクエストを代理している主体: * **Examples**: 広告主(Nike)、代理店(Omnicom)、ブランドチーム * **Responsibilities**: キャンペーン目標やポリシー要件の所有者 * **Policies**: 独自の閾値、ブロックリスト、コンプライアンス要件を持つ場合があります ### プロパティ識別 Properties are identified using the standard AdCP property model: ```json theme={null} { "property_type": "website", "name": "Example News", "identifiers": [ { "type": "domain", "value": "example.com" } ], "supported_channels": ["display", "olv"] } ``` プロパティタイプ: `website`, `mobile_app`, `ctv_app`, `dooh`, `podcast`, `radio`, `streaming_audio`。プロパティは `supported_channels` を宣言して在庫が対応する広告チャネルを示してもよい。 ### プロパティリスト参照 For large property sets, use property list references instead of embedding properties: ```json theme={null} { "property_list_ref": { "agent_url": "https://lists.example.com", "list_id": "premium_news_sites", "auth_token": "eyJhbGciOiJIUzI1NiIs..." } } ``` 受信側エージェントがリストを取得・キャッシュすることで実現: * **スケール**: ペイロード肥大化なしに 50,000 件以上のプロパティを扱える * **更新**: リクエストを変えずにリストを進化させられます * **認可**: トークンでリストへのアクセスを制御 ### ガバナンスエージェントの種類 #### Compliance Agents プロパティのコンプライアンスインテリジェンスを提供するベンダー: * **Examples**: データ整合性スコア、同意品質の測定 * **Business Model**: サブスクリプションまたは従量課金 * **Methodology**: 透明性のための公開ルーブリック #### Brand Safety Agents コンテンツ分類とリスク評価: * **Examples**: コンテンツカテゴライズ、ブランドセーフティスコアリング * **Coverage**: チャンネルや地域に特化する場合があります #### Quality Agents パフォーマンスと不正計測: * **Examples**: Viewability 予測、IVT 検知 * **Integration**: キャンペーン成果と相関させる場合があります ### スコアリングとデータプライバシー #### スコアは内部のみ **重要な設計原則**: 生のスコアはバイヤーや下流クライアントに共有しません。データ漏洩を防ぎます。 ガバナンスエージェントは内部スコアモデルを保持しますが、プロトコルはスコアを露出させず **リスト管理** を中心に設計されています: * Buyers specify **thresholds** via `feature_requirements` (e.g., `"min_value": 85`) * Agents return **pass/fail lists** of properties that meet the thresholds * Raw scores never leave the governance agent この設計により以下を防ぎます: * スコア逆算のための列挙攻撃(閾値を変えたリスト要求など) * 競合インテリジェンスの漏洩 * スコアデータの転売によるアービトラージ #### バイヤーが受け取るもの When calling `get_property_list`, buyers receive a compact list of identifiers (not full property objects) for efficiency: ```json theme={null} { "list_id": "pl_abc123", "identifiers": [ { "type": "domain", "value": "bbc.co.uk" }, { "type": "domain", "value": "theguardian.com" }, { "type": "domain", "value": "ft.com" } ], "total_count": 847 } ``` 閾値を通過したプロパティのみが含まれ、落ちたプロパティは除外されます。スコアやメタデータは返さず、入札時参照に必要な識別子のみを返します。 #### メソドロジーの開示 The `get_adcp_capabilities` task returns information about what features an agent evaluates and their methodology, but NOT the underlying scores: ```json theme={null} { "features": [ { "feature_id": "mfa_score", "name": "Made For Advertising Score", "type": "quantitative", "range": { "min": 0, "max": 100 }, "methodology": "mfa_detection", "methodology_version": "v2.1", "methodology_url": "https://quality.example.com/methodology" } ] } ``` これによりバイヤーは: * エージェントが何を測っているか把握 * エージェント間でメソドロジーを比較 * 適切な閾値を設定 ただし個々のプロパティの実スコアを取得することはできません。 ## タスク ### ディスカバリ #### get\_adcp\_capabilities ガバナンスエージェントが評価できる機能を発見します。 **ユースケース**: * 機能ディスカバリ: 何を評価できるか把握 * マーケットプレイス閲覧: エージェント間で機能比較 * 統合計画: リスト作成前に利用可能なフィルターを把握 ### プロパティリスト管理 #### create\_property\_list フィルターと任意の Brand Manifest を伴うプロパティリストを新規作成します。 **必須パラメータ**: * `countries_all` に少なくとも 1 つの国(ISO 3166-1 alpha-2) * `channels_any` に少なくとも 1 つのチャネル(display, video, audio など) **Base Properties**: 評価対象となるプロパティソースの配列。各要素は `selection_type` を識別子とする判別可能ユニオンです: * **`publisher_tags`**: `{ "selection_type": "publisher_tags", "publisher_domain": "...", "tags": [...] }` - パブリッシャー内のタグ * **`publisher_ids`**: `{ "selection_type": "publisher_ids", "publisher_domain": "...", "property_ids": [...] }` - パブリッシャー内の property\_id * **`identifiers`**: `{ "selection_type": "identifiers", "identifiers": [...] }` - パブリッシャーコンテキスト不要 * **Omitted**: エージェントのプロパティ DB 全体を参照 [base-property-source schema](https://adcontextprotocol.org/schemas/v2/property/base-property-source.json) で完全な仕様を確認してください。 **フィルタロジック**(フィールド名に明示): * `countries_all`: 指定されたすべての国のフィーチャーデータが必要(AND) * `channels_any`: 指定されたチャネルのいずれかをサポートしていれば可(OR) * `feature_requirements`: すべての要件を満たす(AND) **ユースケース**: * フィルター(国、チャネル、フィーチャー閾値)で準拠リストを定義 * Brand Manifest を渡してルールを自動適用 * 変更通知用の Webhook を登録 #### update\_property\_list 既存のプロパティリストを更新します。 **ユースケース**: * ベースリストにプロパティを追加/削除 * キャンペーン要件に応じてフィルター調整 * Webhook URL の更新 #### get\_property\_list 解決済みプロパティリストを取得します。 **ユースケース**: * フィルター適用後の準拠リストを取得 * 入札時利用のためリストをキャッシュ * Webhook 通知後に更新版を取得 #### list\_property\_lists 認証済みプリンシパルがアクセスできるプロパティリストを列挙します。 #### delete\_property\_list プロパティリストを削除します。 ### Validation #### validate\_property\_delivery 配信記録をプロパティリストに照らして準拠性を判定し、「意図」と「結果」を突き合わせます。 独立した 2 つの検証を行います: 1. **プロパティ準拠**: 識別子が解決済みリストに含まれるか 2. **サプライパス認可**: セールスエージェントがそのプロパティを販売する権限を持つか(任意、`sales_agent_url` が必要) **ユースケース**: * ポストキャンペーン検証: インプレッションが準拠プロパティに配信されたか確認 * サプライパス検証: セールスエージェントがパブリッシャーに認可されているか確認 * リアルタイム監視: 実行中キャンペーンの準拠率を確認 * 監査証跡: 規制/ブランドセーフティ審査向けにレポート生成 **プロパティ検証ステータス**: * `compliant`: 識別子が解決済みリストに含まれます * `non_compliant`: 識別子が解決済みリストに含まれない * `not_covered`: 識別子は認識したが当該プロパティのデータがない(新規など) * `unidentified`: このエージェントでは解決できない識別子(検出失敗・非対応) **認可検証ステータス**(`sales_agent_url` を提供した場合): * `authorized`: セールスエージェントが adagents.json に記載されています * `unauthorized`: authorized\_agents に記載されていません * `unknown`: adagents.json を取得/解析できません **検証不能レコード**: `not_covered` と `unidentified` は準拠率計算から除外すべきです。データカバレッジの不足と識別子解決の問題を区別できます。 **レスポンス形式**: 準拠/非準拠/not\_covered/unidentified の生カウントを返します。必要に応じてクライアントサイドで率を計算します。オプションで `aggregate` フィールドに算出指標(スコア、グレード、ラベルなど)を含めることもできます(フォーマットはエージェント依存)。 ## Typical Flows ### Property List Flow Property lists enable buyers to define and manage compliant property sets: 1. **Create property list**: Buyer defines list on governance agent with filters 2. **Resolve and iterate**: Buyer calls `get_property_list` to see resolved properties 3. **Share list reference**: Buyer provides `list_id` to orchestrator/seller 4. **Cache locally**: Orchestrator/seller fetches and caches resolved properties 5. **Use at bid time**: Orchestrator/seller uses local cache (no governance agent calls) 6. **Refresh periodically**: Re-fetch based on `cache_valid_until` (typically 1-24 hours) **Important**: Governance agents are NOT in the real-time bid path. All bid-time decisions use locally cached property sets. ### Webhook and Caching Pattern Webhooks provide **notification** that a property list has changed. The webhook payload contains a summary of changes, but you must call `get_property_list` to retrieve the actual updated properties. ``` ┌─────────────────────────────────────────────────────────────────┐ │ Webhook Flow │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. Governance agent re-evaluates properties (background) │ │ 2. Webhook fires with change summary (added/removed counts) │ │ 3. Recipient calls get_property_list to fetch updated list │ │ 4. Recipient updates local cache │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` **Best Practices for Downstream Consumers**: Consumers of property lists (orchestrators, sellers, buyer agents) should implement **at least one** of these patterns: 1. **Webhook-driven updates** (recommended): Register a webhook URL when creating the property list. Re-fetch via `get_property_list` when notified of changes. 2. **Polling with cache hints**: Use `cache_valid_until` from `get_property_list` responses to schedule periodic re-fetches. Typical validity periods are 1-24 hours. 3. **Hybrid approach**: Use webhooks for immediate updates, with polling as a fallback safety net. **Cache Expiry Guidance**: Every `get_property_list` response includes: * `resolved_at`: When the list was evaluated * `cache_valid_until`: When consumers should consider the cache stale ```json theme={null} { "resolved_at": "2026-01-04T10:00:00Z", "cache_valid_until": "2026-01-04T22:00:00Z" } ``` Consumers MUST NOT use cached data beyond `cache_valid_until` without re-fetching. ### Property Discovery Flow 1. **Define filters**: Specify country, channel, quality thresholds when creating property list 2. **Resolve list**: Call `get_property_list` with `resolve=true` to get matching properties 3. **Review candidates**: Evaluate returned properties for fit 4. **Add to campaign**: Include property list reference in media buy ## Response Structure All AdCP Governance responses follow a consistent structure: ### Core Response Fields * **message**: Human-readable summary of the operation result * **context\_id**: Session continuity identifier for follow-up requests * **data**: Task-specific payload (varies by task) ### Protocol Transport * **MCP**: Returns complete response as flat JSON object * **A2A**: Returns as structured artifacts with message in text part, data in data part * **Data Consistency**: Both protocols contain identical AdCP data structures ## Error Handling ### Error Codes * `PROPERTY_NOT_FOUND`: Property identifier not recognized * `PROPERTY_NOT_MONITORED`: Governance agent doesn't cover this property * `POLICY_NOT_FOUND`: Referenced policy doesn't exist * `LIST_ACCESS_DENIED`: Cannot access property list (auth failed) * `LIST_NOT_FOUND`: Property list reference invalid * `METHODOLOGY_NOT_SUPPORTED`: Requested methodology version unavailable * `PARTIAL_RESULTS`: Some properties couldn't be evaluated ### Partial Success For bulk operations, the response may include partial results: ```json theme={null} { "message": "Evaluated 847 of 850 properties. 3 properties not in coverage.", "context_id": "ctx-gov-123", "scores": [...], "errors": [ { "code": "PROPERTY_NOT_MONITORED", "property": { "identifiers": [{ "type": "domain", "value": "unknown.com" }] }, "message": "Property not in monitoring coverage" } ] } ``` ## Implementation Notes ### Caching Architecture Governance decisions are highly cacheable: #### Orchestrator-Side Caching * **Score cache**: Store scores with TTL from `valid_until` field * **Decision cache**: Pre-compute pass/fail for campaigns * **List cache**: Cache property lists from `property_list_ref` #### Agent-Side Caching * **Profile cache**: Maintain pre-computed property profiles * **Methodology cache**: Cache scoring algorithm results ### Performance Requirements | Operation | Target Latency | | ----------------------------- | -------------- | | Single property score | \< 100ms | | Bulk scoring (100 properties) | \< 2s | | Filter decision (cached) | \< 5ms | | Property discovery | \< 5s | ### Multi-Agent Strategies Orchestrators may consult multiple governance agents: 1. **Primary + Validation**: Use primary agent, validate with secondary 2. **Specialization**: Route by property type to specialist agents 3. **Consensus**: Require multiple agents to agree 4. **Competitive**: Track agent accuracy, weight by performance ## Agent Discovery There are two complementary discovery mechanisms: ### Publisher-Side Discovery via adagents.json Publishers declare which governance agents have data about their properties using the `property_features` field in `adagents.json`: ```json theme={null} { "property_features": [ { "url": "https://api.scope3.com", "name": "Scope3", "features": ["carbon_score", "sustainability_grade"], "publisher_id": "pub_12345" }, { "url": "https://api.onetrust.com", "name": "OneTrust", "features": ["gdpr_compliant", "tcf_registered", "ccpa_compliant"] } ] } ``` This solves the discovery problem: buyers don't need to query every possible governance agent. Instead, they read `property_features` from the publisher's adagents.json to find which agents have relevant data. See the [adagents.json Tech Spec](/docs/governance/property/adagents#governance-agent-discovery) for the complete discovery workflow. ### Agent-Side Discovery via agent-card.json Governance agents expose capabilities via `.well-known/agent-card.json`: ```json theme={null} { "name": "Example Compliance Provider", "url": "https://compliance.example.com", "capabilities": { "tasks": [ "get_adcp_capabilities", "create_property_list", "get_property_list", "update_property_list", "delete_property_list", "list_property_lists", "validate_property_delivery" ], "protocols": ["MCP", "A2A"], "schema_version": "v1" }, "methodology": { "documentation_url": "https://compliance.example.com/methodology", "scoring_frameworks": ["data_integrity_index", "brand_safety_score"], "coverage": { "property_types": ["website", "mobile_app", "ctv_app"], "jurisdictions": ["GDPR", "CCPA", "COPPA"] } } } ``` ### Detailed Capability Discovery Use `get_adcp_capabilities` for detailed capability discovery: ```json theme={null} { "tool": "get_adcp_capabilities", "arguments": {} } ``` Returns the specific features the agent can evaluate (consent\_quality, carbon\_score, brand\_risk, etc.). ## Marketplace Architecture The Property Protocol enables a marketplace of specialized data agents: ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ SELLER AGENT (DSP/SSP) │ │ "Give me the compliant property list for this campaign" │ └───────────────────────────────┬─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ BUYER AGENT (implements Property Protocol) │ │ - Exposes: get_adcp_capabilities, get_property_list, webhooks │ │ - Source of truth for final compliant list │ │ - Intersects results from specialized agents │ └───────────────────────────┬─────────────────────────────────────────────┘ │ ┌───────────────────┼───────────────────┐ ▼ ▼ ▼ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ Consent Agent │ │ Scope3 Agent │ │ Brand Safety │ │ (Compliant) │ │ │ │ Agent │ ├───────────────┤ ├───────────────┤ ├───────────────┤ │ Features: │ │ Features: │ │ Features: │ │ consent_qual │ │ carbon_score │ │ content_cat │ │ tcf_version │ │ climate_risk │ │ brand_risk │ │ coppa_cert │ │ green_host │ │ sentiment │ ├───────────────┤ ├───────────────┤ ├───────────────┤ │ Subscription │ │ Subscription │ │ Subscription │ └───────────────┘ └───────────────┘ └───────────────┘ ``` ### Key Principles 1. **Buyer agent is source of truth**: The buyer agent aggregates data from multiple specialized governance agents 2. **Seller sees one interface**: Sellers interact only with the buyer agent using standard Property Protocol 3. **Subscription model**: Each specialized agent is a paid service with its own features and coverage 4. **Webhook-driven updates**: Specialized agents notify the buyer agent when property evaluations change ### Multi-Agent Orchestration A buyer agent can distribute a master property list to multiple specialized agents: ```python theme={null} # Buyer agent creates variants on each specialized agent consent_list = consent_agent.create_property_list( name="Q1 Campaign - Consent", base_properties=master_list, brand=brand ) # Configure webhook for updates consent_agent.update_property_list( list_id=consent_list.list_id, webhook_url="https://buyer.example.com/webhooks/consent" ) scope3_list = scope3_agent.create_property_list( name="Q1 Campaign - Sustainability", base_properties=master_list, brand=brand ) scope3_agent.update_property_list( list_id=scope3_list.list_id, webhook_url="https://buyer.example.com/webhooks/scope3" ) # Buyer agent intersects filtered results def on_list_changed(event): consent_props = consent_agent.get_property_list(consent_list.list_id, resolve=True) scope3_props = scope3_agent.get_property_list(scope3_list.list_id, resolve=True) # Intersection = properties that pass ALL governance agents compliant_props = intersect(consent_props, scope3_props) # Update buyer agent's exposed list update_compliant_list(compliant_props) ``` ### Brand 複雑なフィルターを指定する代わりに、バイヤーはブランド参照を提供します: ```json theme={null} { "brand": { "domain": "toybrand.com" } } ``` 各ガバナンスエージェントはブランドのアイデンティティを解決し、専門領域に応じてルールを適用します: * **Consent agent**: 子ども向けブランドに対して COPPA 要件を適用 * **Brand safety agent**: 適切なコンテンツにフィルタリング、暴力/アダルトを除外 * **Sustainability agent**: グリーンメディア要件を適用 バイヤーは具体的なルールを知る必要がなく、自分が誰かを宣言すれば、エージェントが何を適用するかを判断します。 ## Integration with Media Buy Protocol ### Property Lists in Media Buys The Media Buy Protocol accepts property list references: ```json theme={null} { "task": "create_media_buy", "arguments": { "packages": [{ "property_list_ref": { "agent_url": "https://governance.example.com", "list_id": "approved_q1_campaign", "auth_token": "..." } }] } } ``` ### Policy Compliance Media buys can reference governance policies via property list references: ```json theme={null} { "compliance_requirements": { "property_list_ref": { "agent_url": "https://compliance.example.com", "list_id": "pl_q1_compliant", "auth_token": "eyJhbGciOiJIUzI1NiIs..." } } } ``` ## Best Practices 1. **Cache aggressively**: Property scores change slowly; cache for hours/days 2. **Bulk where possible**: Use batch operations for planning, not per-property calls 3. **Pre-compute decisions**: Build pass/fail lookups before bid-time 4. **Monitor coverage**: Track which properties agents don't cover 5. **Log methodology versions**: For audit trails, record which scoring version was used 6. **Handle partial results**: Not all properties will be scorable; plan for gaps ## Next Steps * See the [adagents.json Tech Spec](/docs/governance/property/adagents) for property declaration and authorization * See the [get\_adcp\_capabilities task reference](/docs/protocol/get_adcp_capabilities) for capability discovery * See the [Property List Management](/docs/governance/property/tasks/property_lists) for CRUD operations and webhooks * See the [validate\_property\_delivery task reference](/docs/governance/property/tasks/validate_property_delivery) for post-campaign compliance validation # Property Governance Tasks Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/property/tasks/index AdCP プロパティガバナンスのタスクリファレンス — プロパティリスト管理、フィーチャー評価、配信検証タスク。 # Property Governance タスク **AdCP 3.0 Proposal** - These tasks are under development for AdCP 3.0. Property Governance は **ステートフル** モデルで、評価はプロパティリスト管理を通じて行います。フィルターとブランド参照でリストを作成し、解決して準拠プロパティを取得します。 ## Discovery | Task | Purpose | Response Time | | --------------------------------------------------------------- | ----------- | ------------- | | [get\_adcp\_capabilities](/docs/protocol/get_adcp_capabilities) | エージェント機能の取得 | \~200ms | ガバナンスエージェントが評価できる機能はプロトコルレベルの `get_adcp_capabilities` で取得します。`property_features` 配列の詳細は [governance セクション](/docs/protocol/get_adcp_capabilities#governance-protocol) を参照。 ## Property List Management | Task | Purpose | Response Time | | --------------------------------------------------------------- | ------------ | ------------- | | [create\_property\_list](./property_lists#create_property_list) | プロパティリスト新規作成 | \~500ms | | [update\_property\_list](./property_lists#update_property_list) | 既存リストの更新 | \~500ms | | [get\_property\_list](./property_lists#get_property_list) | 解決済みリストの取得 | \~2-5s | | [list\_property\_lists](./property_lists#list_property_lists) | 全リストの列挙 | \~500ms | | [delete\_property\_list](./property_lists#delete_property_list) | リスト削除 | \~200ms | [Property List Management](./property_lists) で CRUD の詳細を参照してください。 ## Validation | Task | Purpose | Response Time | | ------------------------------------------------------------ | --------------- | ------------- | | [validate\_property\_delivery](./validate_property_delivery) | 配信記録をリストに照らして検証 | \~1-5s | [validate\_property\_delivery](./validate_property_delivery) でポストキャンペーン検証を参照。 ## Task Selection Guide ### プロパティリストを作成 `create_property_list` をフィルターとブランド参照で使います: ```json theme={null} { "tool": "create_property_list", "arguments": { "name": "Q1 Campaign - UK Premium", "base_properties": [ { "selection_type": "publisher_tags", "publisher_domain": "raptive.com", "tags": ["premium_news"] } ], "filters": { "countries_all": ["UK"], "channels_any": ["display", "video"], "feature_requirements": [ { "feature_id": "mfa_score", "min_value": 85, "max_value": 100 }, { "feature_id": "coppa_certified", "allowed_values": [true] } ] }, "brand": { "domain": "toybrand.com" } } } ``` **フィルター**(すべて任意): `countries_all` は列挙したすべての国でデータを持つプロパティに絞り込み、`channels_any` は列挙したいずれかのチャネルをサポートするプロパティに絞り込みます。フィルターを省略するとその次元での絞り込みは行いません。 **Base properties**: An array of property sources to evaluate. Each entry is a discriminated union with `selection_type`: * **`publisher_tags`**: `{ "selection_type": "publisher_tags", "publisher_domain": "...", "tags": [...] }` * **`publisher_ids`**: `{ "selection_type": "publisher_ids", "publisher_domain": "...", "property_ids": [...] }` * **`identifiers`**: `{ "selection_type": "identifiers", "identifiers": [...] }` * **Omitted**: Query the agent's entire property database **Filter logic** (explicit in field names): * `countries_all`: Property must have feature data for ALL listed countries * `channels_any`: Property must support ANY of the listed channels * `feature_requirements`: Property must pass ALL requirements (AND) Filters have two built-in fields (`countries_all`, `channels_any`) plus `feature_requirements` which reference features the agent provides (discovered via [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)). For quantitative features, use `min_value`/`max_value`. For binary or categorical features, use `allowed_values`. ### Getting Resolved Properties Use `get_property_list` to retrieve the list with resolved identifiers: ```json theme={null} { "tool": "get_property_list", "arguments": { "list_id": "pl_abc123", "resolve": true } } ``` Response includes resolved identifiers. Note that **raw scores are not returned** - only identifiers that pass the filter thresholds are included: ```json theme={null} { "list_id": "pl_abc123", "identifiers": [ { "type": "domain", "value": "bbc.co.uk" }, { "type": "domain", "value": "news.sky.com" } ], "cache_valid_until": "2026-01-04T17:15:00Z" } ``` The `auth_token` for sharing with sellers is returned at creation time (from `create_property_list`). Store it securely - it's only returned once. ### Multi-Agent Integration Create the same property list on multiple governance agents, then configure webhooks to aggregate results: ```python theme={null} # Create lists on specialized agents consent_list = consent_agent.create_property_list( name="Q1 - Consent", base_properties=master_list, brand_manifest=brand_manifest ) consent_agent.update_property_list( list_id=consent_list.list_id, webhook_url="https://buyer.example.com/webhooks/consent" ) scope3_list = scope3_agent.create_property_list( name="Q1 - Sustainability", base_properties=master_list, brand_manifest=brand_manifest ) scope3_agent.update_property_list( list_id=scope3_list.list_id, webhook_url="https://buyer.example.com/webhooks/scope3" ) # Buyer agent intersects results when webhooks fire ``` # Property List Management Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/property/tasks/property_lists AdCP のプロパティリストタスクは、静的セットと動的フィルターを組み合わせたインクルージョン/エクスクルージョンリストを作成・更新・取得・列挙・削除します。 **AdCP 3.0 提案** - これらのタスクは AdCP 3.0 向けに開発中です。 **Tasks**: プロパティリストの作成・更新・取得・列挙・削除。 プロパティリストは静的セットと動的フィルターを組み合わせた管理リソースです。解決時にフィルターが適用され、最終的なプロパティ集合が得られます。 ## アーキテクチャ: セットアップ時に処理(リアルタイムではない) Property lists are designed for **setup-time** operations, not real-time bid decisions: ``` ┌─────────────────────────────────────────────────────────────────┐ │ SETUP TIME (Campaign Planning) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. Buyer creates/updates property list on governance agent │ │ 2. Buyer resolves list to get current properties │ │ 3. Buyer provides list_id to orchestrator/seller │ │ 4. Orchestrator/seller fetches and caches resolved list │ │ 5. Campaign targets only cached compliant properties │ │ │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ BID TIME (Milliseconds) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ • Orchestrator/seller uses LOCAL cache only │ │ • NO calls to governance agent │ │ • Pass/fail from cached property set │ │ │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ REFRESH (Periodic) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ • Orchestrator/seller re-fetches list on schedule │ │ • Frequency based on cache_valid_until │ │ • Typically every 1-24 hours │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` これにより次が可能になります: * **静的リスト**: 承認済みプロパティのキュレーション * **動的リスト**: 国・チャネル・スコア閾値など条件に合致するプロパティ * **ハイブリッドリスト**: ベースセットをフィルターで修正 ## タスク概要 | Task | Purpose | Response Time | | ---------------------- | --------- | -------------- | | `create_property_list` | 新規リスト作成 | \~500ms | | `update_property_list` | 既存リスト更新 | \~500ms | | `get_property_list` | 解決済みリスト取得 | \~2-5s (サイズ依存) | | `list_property_lists` | 全リスト列挙 | \~500ms | | `delete_property_list` | リスト削除 | \~200ms | ## Property List Structure A property list contains: ```json theme={null} { "list_id": "uk_premium_news_q1", "name": "UK Premium News Sites Q1 2026", "description": "High-quality UK news sites for Q1 campaign", "principal": "did:principal:brand-x", "base_properties": [ { "selection_type": "publisher_tags", "publisher_domain": "raptive.com", "tags": ["premium_news", "uk_tier1"] } ], "filters": { "countries_all": ["UK"], "channels_any": ["display", "video"], "feature_requirements": [ { "feature_id": "mfa_score", "min_value": 90, "max_value": 100 } ] }, "brand": { "domain": "acmecorp.com" }, "created_at": "2026-01-03T10:00:00Z", "updated_at": "2026-01-03T10:00:00Z", "property_count": 847 } ``` ## Brand すべてのフィルターを手動で指定する代わりに、ブランド参照を提供して、あなたが誰かに基づいてガバナンスエージェントが適切なルールを適用するようにします: ```json theme={null} { "brand": { "domain": "toybrand.com" } } ``` エージェントはブランドのアイデンティティを解決し、専門領域に応じてルールを適用します: * Consent agent は対象ブランドのターゲットオーディエンスに基づいて COPPA 要件を適用 * Brand safety agent はブランドの業種からコンテンツカテゴリを推定 * Sustainability agent はブランドプロフィールから要件を適用 ブランド参照には標準の [core/brand-ref](https://adcontextprotocol.org/schemas/v3/core/brand-ref.json) スキーマを使用します。ガバナンスエージェントはドメインを解決してブランドの `brand.json` ファイルからアイデンティティを取得します。 ## Webhooks Configure webhooks via `update_property_list` to receive notifications when the resolved list changes. **Important**: Webhooks provide **notification only**. They tell you that the list has changed, but do not stream the updated properties. After receiving a webhook, you must call `get_property_list` to retrieve the updated property set. ### Webhook Flow ``` 1. Governance agent re-evaluates properties (periodically or on trigger) 2. Agent detects changes to the resolved property list 3. Webhook fires with change summary (counts, not full list) 4. Recipient calls get_property_list(list_id, resolve=true) 5. Recipient updates local cache with new properties ``` ### Webhook Payload ```json theme={null} { "event": "property_list_changed", "list_id": "uk_premium_news_q1", "list_name": "UK Premium News Sites Q1 2026", "change_summary": { "properties_added": 12, "properties_removed": 3, "total_properties": 856 }, "resolved_at": "2026-01-03T18:00:00Z", "cache_valid_until": "2026-01-04T18:00:00Z" } ``` The webhook payload includes counts but NOT the actual properties. This keeps payloads small and avoids redundant data transfer when recipients may not need the full list immediately. ### Webhook Use Cases 1. **Buyer agent aggregation**: Receive updates from multiple specialized agents, intersect results 2. **Seller cache invalidation**: Know when to re-fetch the compliant property list 3. **Alerting**: Notify when significant changes occur to compliance status ## Filters Filters are applied when the list is resolved (via `get_property_list`): | Filter | Type | Description | | ---------------------- | --------------------- | ------------------------------------------------------------------------------ | | `countries_all` | string\[] | ISO 3166-1 alpha-2 国コード(大文字推奨)- すべての国でフィーチャーデータを持つプロパティのみ対象。任意 — グローバルリストは省略可。 | | `channels_any` | string\[] | 広告チャネル - いずれかのチャネルをサポートするプロパティのみ対象。任意 — 全チャネルリストは省略可。 | | `property_types` | string\[] | プロパティタイプ(website, mobile\_app, ctv\_app など) | | `feature_requirements` | FeatureRequirement\[] | エージェント提供フィーチャーに基づく要件 | | `exclude_identifiers` | Identifier\[] | 常に除外する識別子 | ### Feature Requirements Feature requirements reference features discovered via `get_adcp_capabilities`. Each agent exposes different features (mfa\_score, carbon\_score, coppa\_certified, etc.). For **quantitative** features (scores, ranges): ```json theme={null} { "feature_id": "mfa_score", "min_value": 85, "max_value": 100 } ``` For **binary** features (true/false): ```json theme={null} { "feature_id": "coppa_certified", "allowed_values": [true] } ``` For **categorical** features (enum values): ```json theme={null} { "feature_id": "content_category", "allowed_values": ["news", "sports", "technology"] } ``` ### Handling Missing Coverage When a property doesn't have data for a required feature, you can control the behavior with `if_not_covered`: ```json theme={null} { "feature_id": "viewability_score", "min_value": 70, "if_not_covered": "include" } ``` | Value | Behavior | Use Case | | ------------------- | --------------------------------- | --------------------------------------------------------------- | | `exclude` (default) | Property is removed from the list | Strict enforcement - only include properties with verified data | | `include` | Property passes this requirement | Lenient enforcement - don't penalize for coverage gaps | When `if_not_covered: "include"` is used, the response includes a `coverage_gaps` field showing which properties were included despite missing data: ```json theme={null} { "identifiers": [...], "coverage_gaps": { "viewability_score": [ { "type": "domain", "value": "app.example.com" }, { "type": "domain", "value": "ctv.example.com" } ] } } ``` This transparency helps agencies distinguish between properties that passed a requirement vs. those that couldn't be evaluated. ### Required Filters Every property list must include at least: * One country in `countries_all` (ISO 3166-1 alpha-2 code, case-insensitive) * One channel in `channels_any` (display, video, audio, etc.) These are required because governance agents need to know which jurisdiction and context to evaluate properties against. ### Filter Logic The filter field names make the logic explicit: * **`countries_all`**: Property must have feature data for **ALL** listed countries. * **`channels_any`**: Property must support **ANY** of the listed channels. * **`feature_requirements`**: Property must pass **ALL** requirements (AND). ### Base Properties `base_properties` is an array of property sources to evaluate. Each entry is a **discriminated union** with `selection_type` as the discriminator: ```json theme={null} { "base_properties": [ { "selection_type": "publisher_tags", "publisher_domain": "raptive.com", "tags": ["premium_news", "tier1"] }, { "selection_type": "publisher_tags", "publisher_domain": "mediavine.com", "tags": ["lifestyle"] }, { "selection_type": "identifiers", "identifiers": [ { "type": "domain", "value": "bbc.co.uk" }, { "type": "domain", "value": "ft.com" } ] } ] } ``` Each entry must include `selection_type`: | selection\_type | Required Fields | Description | | ---------------- | ---------------------------------- | ------------------------------------------------------- | | `publisher_tags` | `publisher_domain`, `tags` | All properties matching these tags within the publisher | | `publisher_ids` | `publisher_domain`, `property_ids` | Specific property IDs within the publisher | | `identifiers` | `identifiers` | Direct domain/app identifiers (no publisher context) | If `base_properties` is omitted, the agent queries its entire property database for properties matching the filters. See the [base-property-source schema](https://adcontextprotocol.org/schemas/v2/property/base-property-source.json) for the full specification. *** ## create\_property\_list Create a new property list. ### Request ```json theme={null} { "tool": "create_property_list", "arguments": { "name": "UK Premium News Q1", "description": "High-quality UK news sites for Q1 campaign", "base_properties": [ { "selection_type": "publisher_tags", "publisher_domain": "raptive.com", "tags": ["premium_news"] } ], "filters": { "countries_all": ["UK"], "channels_any": ["display", "video"], "feature_requirements": [ { "feature_id": "consent_quality", "min_value": 85, "max_value": 100 } ] } } } ``` ### Response ```json theme={null} { "message": "Created property list 'UK Premium News Q1'.", "context_id": "ctx-gov-list-123", "list": { "list_id": "pl_abc123", "name": "UK Premium News Q1", "description": "High-quality UK news sites for Q1 campaign", "principal": "did:principal:brand-x", "base_properties": [ { "selection_type": "publisher_tags", "publisher_domain": "raptive.com", "tags": ["premium_news"] } ], "filters": { "countries_all": ["UK"], "channels_any": ["display", "video"], "feature_requirements": [ { "feature_id": "consent_quality", "min_value": 85, "max_value": 100 } ] }, "created_at": "2026-01-03T16:30:00Z", "updated_at": "2026-01-03T16:30:00Z", "property_count": 847 }, "auth_token": "eyJhbGciOiJIUzI1NiIs..." } ``` ### Dynamic List (Filters Only) Create a list that dynamically queries the governance agent's database (no base\_properties - uses agent's full coverage): ```json theme={null} { "tool": "create_property_list", "arguments": { "name": "GDPR-Compliant DE Video", "description": "All DE properties supporting video with strong consent", "filters": { "countries_all": ["DE"], "channels_any": ["video"], "feature_requirements": [ { "feature_id": "consent_quality", "min_value": 90, "max_value": 100 } ] } } } ``` *** ## update\_property\_list Modify an existing property list. ### Request - Update Filters ```json theme={null} { "tool": "update_property_list", "arguments": { "list_id": "pl_abc123", "filters": { "countries_all": ["UK"], "channels_any": ["display", "video"], "feature_requirements": [ { "feature_id": "consent_quality", "min_value": 80, "max_value": 100 } ] } } } ``` ### Request - Replace Base Properties ```json theme={null} { "tool": "update_property_list", "arguments": { "list_id": "pl_abc123", "base_properties": [ { "selection_type": "publisher_tags", "publisher_domain": "raptive.com", "tags": ["premium_news", "uk_tier1"] } ] } } ``` ### Request - Add Exclusions ```json theme={null} { "tool": "update_property_list", "arguments": { "list_id": "pl_abc123", "filters": { "exclude_identifiers": [ { "type": "domain", "value": "excluded-site.com" } ] } } } ``` ### Response ```json theme={null} { "message": "Updated property list 'UK Premium News Q1'.", "context_id": "ctx-gov-list-456", "list": { "list_id": "pl_abc123", "name": "UK Premium News Q1", "updated_at": "2026-01-03T17:00:00Z", "property_count": 845 } } ``` *** ## get\_property\_list Retrieve a property list with optional resolution of filters. ### Request - Get Resolved Properties ```json theme={null} { "tool": "get_property_list", "arguments": { "list_id": "pl_abc123", "resolve": true, "max_results": 100 } } ``` ### Response The response returns a compact list of **identifiers only** (not full property objects) for efficiency. Only identifiers that pass the feature requirements are included - no scores or metadata. ```json theme={null} { "message": "Retrieved property list 'UK Premium News Q1' with 847 resolved identifiers.", "context_id": "ctx-gov-list-789", "list_id": "pl_abc123", "identifiers": [ { "type": "domain", "value": "bbc.co.uk" }, { "type": "domain", "value": "news.sky.com" }, { "type": "domain", "value": "ft.com" }, { "type": "domain", "value": "theguardian.com" } ], "total_count": 847, "returned_count": 100, "pagination": { "has_more": true, "cursor": "eyJvZmZzZXQiOjEwMH0=" }, "resolved_at": "2026-01-03T17:15:00Z", "cache_valid_until": "2026-01-04T17:15:00Z" } ``` The `auth_token` is only returned when the list is created via `create_property_list`. Store it securely - you'll need it to share access with sellers. ### Request - Get Metadata Only ```json theme={null} { "tool": "get_property_list", "arguments": { "list_id": "pl_abc123", "resolve": false } } ``` ### Response (Metadata Only) ```json theme={null} { "message": "Retrieved property list 'UK Premium News Q1' metadata.", "context_id": "ctx-gov-list-790", "list": { "list_id": "pl_abc123", "name": "UK Premium News Q1", "description": "High-quality UK news sites for Q1 campaign", "base_properties": [ { "selection_type": "publisher_tags", "publisher_domain": "raptive.com", "tags": ["premium_news"] } ], "filters": { "countries_all": ["UK"], "channels_any": ["display", "video"], "feature_requirements": [ { "feature_id": "consent_quality", "min_value": 85, "max_value": 100 } ] }, "created_at": "2026-01-03T16:30:00Z", "updated_at": "2026-01-03T17:00:00Z", "property_count": 847 } } ``` *** ## list\_property\_lists List all property lists accessible to the authenticated principal. ### Request ```json theme={null} { "tool": "list_property_lists", "arguments": { "name_contains": "UK", "max_results": 50 } } ``` ### Response ```json theme={null} { "message": "Found 3 property lists matching 'UK'.", "context_id": "ctx-gov-list-list-123", "lists": [ { "list_id": "pl_abc123", "name": "UK Premium News Q1", "description": "High-quality UK news sites for Q1 campaign", "created_at": "2026-01-03T16:30:00Z", "updated_at": "2026-01-03T17:00:00Z", "property_count": 847 }, { "list_id": "pl_def456", "name": "UK Sports Sites", "description": "UK sports content for sponsorship", "created_at": "2026-01-02T10:00:00Z", "updated_at": "2026-01-02T10:00:00Z", "property_count": 156 } ], "total_count": 3, "returned_count": 3, "pagination": { "has_more": false } } ``` *** ## delete\_property\_list Delete a property list. ### Request ```json theme={null} { "tool": "delete_property_list", "arguments": { "list_id": "pl_abc123" } } ``` ### Response ```json theme={null} { "message": "Deleted property list 'UK Premium News Q1'.", "context_id": "ctx-gov-list-del-123", "deleted": true, "list_id": "pl_abc123" } ``` *** ## Integration with Other Tasks ### Using Lists in score\_properties Reference a property list instead of passing properties inline: ```json theme={null} { "tool": "score_properties", "arguments": { "property_list_ref": { "agent_url": "https://governance.example.com", "list_id": "pl_abc123" }, "scoring_context": { "jurisdiction": "GDPR" } } } ``` ### Using Lists in Media Buys Pass property lists to media buy creation: ```json theme={null} { "tool": "create_media_buy", "arguments": { "packages": [{ "property_list_ref": { "agent_url": "https://governance.example.com", "list_id": "pl_abc123" } }] } } ``` *** ## Error Codes | Code | Description | | -------------------- | ------------------------------------------ | | `LIST_NOT_FOUND` | Property list ID doesn't exist | | `LIST_ACCESS_DENIED` | Principal doesn't have access to this list | | `INVALID_FILTER` | Filter configuration is invalid | | `LIST_NAME_EXISTS` | A list with this name already exists | ## Caching and Refresh The `get_property_list` response includes caching guidance: | Field | Description | | ------------------- | ------------------------------------------------- | | `resolved_at` | When filters were applied and properties resolved | | `cache_valid_until` | When consumers should re-fetch the list | **Typical flow for orchestrators/sellers:** ```python theme={null} # Initial setup response = governance_agent.get_property_list(list_id, resolve=True) local_cache = build_property_lookup(response.properties) cache_expiry = response.cache_valid_until # Periodic refresh (background job) if now() >= cache_expiry: response = governance_agent.get_property_list(list_id, resolve=True) local_cache = build_property_lookup(response.properties) cache_expiry = response.cache_valid_until # Bid time (no external calls) def should_bid(property_domain): return property_domain in local_cache ``` ## Usage Notes 1. **Setup time only**: Governance agents are not in the real-time bid path; resolve lists during campaign setup 2. **Local caching**: Orchestrators/sellers must cache resolved properties locally for bid-time decisions 3. **Refresh on schedule**: Re-fetch lists based on `cache_valid_until` (typically every 1-24 hours) 4. **Dynamic vs Static**: Use filters-only lists when you want the agent to maintain the property set; use base\_properties when you need explicit control 5. **Pagination**: Large lists may require multiple requests with cursor-based pagination 6. **No score leakage**: Raw scores are kept internal to governance agents; responses contain pass/fail lists, not scores # validate_property_delivery Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/property/tasks/validate_property_delivery validate_property_delivery は AdCP において配信記録をプロパティリストに照らしてコンプライアンスとサプライパス認可を確認します。 # validate\_property\_delivery **AdCP 3.0 提案** - このタスクは AdCP 3.0 向けに開発中です。 配信記録をプロパティリストに照らして準拠性を検証します。次の 2 点を確認します: 1. **プロパティ準拠**: インプレッションはリスト内プロパティに配信されたか? 2. **サプライパス認可**: セールスエージェントにその在庫を売る権限があったか? ## Use Cases * **ポストキャンペーン検証**: インプレッションが準拠プロパティに配信されたか確認 * **サプライパス検証**: セールスエージェントがパブリッシャーに認可されていたか確認 * **リアルタイム監視**: 実行中キャンペーンの準拠率を確認 * **監査証跡**: 規制・ブランドセーフティ審査向けレポート生成 ## Request ```json theme={null} { "$schema": "/schemas/property/validate-property-delivery-request.json", "list_id": "pl_abc123", "records": [ { "identifier": { "type": "domain", "value": "www.nytimes.com" }, "impressions": 103 }, { "identifier": { "type": "domain", "value": "sketchy-site.example" }, "impressions": 47 }, { "identifier": { "type": "android_package", "value": "com.unknown.app" }, "impressions": 25 } ], "include_compliant": false } ``` ### Parameters | Parameter | Type | Required | Description | | --------------------------- | ------- | -------- | ------------------------------------------- | | `list_id` | string | Yes | 検証対象のプロパティリスト ID | | `records` | array | Yes | 検証する配信記録(1〜10,000 件) | | `records[].identifier` | object | Yes | プロパティ識別子(`type` と `value`) | | `records[].impressions` | integer | Yes | 配信インプレッション数 | | `records[].record_id` | string | No | 相関用のクライアント ID | | `records[].sales_agent_url` | string | No | 認可検証に使うセールスエージェント URL(adagents.json と突き合わせ) | | `include_compliant` | boolean | No | 準拠レコードを結果に含めるか(デフォルト false) | ## Response ```json theme={null} { "$schema": "/schemas/property/validate-property-delivery-response.json", "status": "completed", "list_id": "pl_abc123", "summary": { "total_records": 4, "total_impressions": 200, "compliant_records": 1, "compliant_impressions": 103, "non_compliant_records": 1, "non_compliant_impressions": 47, "not_covered_records": 1, "not_covered_impressions": 25, "unidentified_records": 1, "unidentified_impressions": 25 }, "aggregate": { "score": 68.7, "grade": "C+", "label": "68.7% compliant", "methodology_url": "https://governance.example.com/methodology/compliance-scoring" }, "results": [ { "identifier": { "type": "domain", "value": "sketchy-site.example" }, "status": "non_compliant", "impressions": 47, "features": [ { "feature_id": "record:list_membership", "status": "failed", "explanation": "Identifier not found in resolved property list" } ] }, { "identifier": { "type": "domain", "value": "new-site.example" }, "status": "not_covered", "impressions": 25 }, { "identifier": { "type": "android_package", "value": "com.unknown.app" }, "status": "unidentified", "impressions": 25 } ], "validated_at": "2026-01-04T19:00:00Z", "list_resolved_at": "2026-01-04T12:00:00Z" } ``` ### レスポンスフィールド | Field | Type | Description | | ------------------ | -------- | ---------------------- | | `list_id` | string | 検証に用いたプロパティリスト ID | | `summary` | object | プロパティ準拠検証の生カウント | | `aggregate` | object | 任意の算出指標(ガバナンスエージェント提供) | | `results` | array | レコード単位の検証結果 | | `validated_at` | datetime | 検証日時 | | `list_resolved_at` | datetime | 使用したプロパティリストの解決タイムスタンプ | ### Summary フィールド Summary は生カウントを提供し、率の算出はクライアントサイドで行います: | Field | Description | | ----------------------------------------------------- | -------------------------------------------- | | `total_records` | Total records validated | | `total_impressions` | Total impressions across all records | | `compliant_records` / `compliant_impressions` | Records/impressions in the property list | | `non_compliant_records` / `non_compliant_impressions` | Records/impressions NOT in the property list | | `not_covered_records` / `not_covered_impressions` | Identifier recognized but no data available | | `unidentified_records` / `unidentified_impressions` | Identifier type not resolvable | ### 検証ステータス | Status | Meaning | | --------------- | ------------------- | | `compliant` | 解決済みリストに含まれる | | `non_compliant` | 解決済みリストに含まれない | | `not_covered` | 識別子は認識したがデータなし | | `unidentified` | このエージェントでは解決できない識別子 | ## not\_covered と unidentified の違い いずれも「未知」だが性質が異なります: **`not_covered`** - 識別子は認識した(例: 有効なドメイン)が、そのプロパティのデータがない場合。例: * プロパティが新しく DB に未登録 * プロパティは存在するが未評価 * エージェントのカバレッジ外カテゴリ **`unidentified`** - 識別子を認識できない場合。例: * クライアントサイドでプロパティ検出に失敗 * サポート外の識別子型(ドメインのみ対応だが App ID を受信など) * 値が不正 両者とも準拠率計算から除外します。検出やカバレッジの不足を罰するべきではありません。 ## Optional Aggregate Metrics Governance agents can optionally return computed metrics in the `aggregate` field: ```json theme={null} "aggregate": { "score": 68.7, "grade": "C+", "label": "68.7% compliant", "methodology_url": "https://governance.example.com/methodology" } ``` | Field | Description | | ----------------- | ------------------------------------------------------- | | `score` | Numeric score (scale is agent-defined, typically 0-100) | | `grade` | Letter grade or category (e.g., "A+", "B-", "Gold") | | `label` | Human-readable summary (e.g., "85% compliant") | | `methodology_url` | URL explaining how the aggregate was calculated | The `aggregate` field is optional and agent-specific. Consumers should not assume a particular format - always check `methodology_url` for interpretation. ## Calculating Your Own Rates The response always includes raw counts. Calculate rates as needed: ```python theme={null} # Compliance rate (exclude unverifiable from denominator) unverifiable = summary.not_covered_impressions + summary.unidentified_impressions verifiable = summary.total_impressions - unverifiable compliance_rate = summary.compliant_impressions / verifiable if verifiable > 0 else None ``` In the example above: * Compliant impressions: 103 * Non-compliant impressions: 47 * not\_covered + unidentified impressions: 50 (excluded) * Compliance rate: 103 / (200 - 50) = 103 / 150 = 68.7% ## 機能結果 `features[]` の各エントリは `feature_id` と `status` を運びます。データ機能はガバナンスエージェントの機能カタログ(`get_adcp_capabilities` で発見)に由来します。レコードレベルの構造チェックは予約名前空間を使うため、データ機能と同じアイデンティティ空間に収まります。 ### 予約された feature\_id プレフィックス | Prefix | Scope | Canonical feature\_ids | | ----------- | -------------- | ---------------------------------------------------------------------------------------------- | | `record:` | レコードレベルの構造チェック | `record:list_membership`、`record:excluded`、`record:country_mismatch`、`record:channel_mismatch` | | `delivery:` | 配信パスチェック | `delivery:seller_authorization`、`delivery:click_url_presence` | ガバナンスエージェントは、これらの名前空間内で新しいチェックを追加し、機能カタログで公開してもかまいません(MAY)。 ### 機能レベルの失敗 プロパティが特定の機能要件に失敗した場合、エントリは機能を id で参照します。レスポンスは判定と方向性のある説明を運びます。呼び出し元が要件を作成した場合(例: プロパティリストの `feature_requirements`)、評価器は `requirement` をエコーバックして、リスト定義を読み直さずに修正・再試行ループを可能にしてもかまいません(MAY)。 ```json theme={null} { "identifier": { "type": "domain", "value": "low-quality-site.example" }, "status": "non_compliant", "impressions": 150, "features": [ { "feature_id": "mfa_score", "status": "failed", "explanation": "Property below MFA score requirement", "requirement": { "min_value": 85 } } ] } ``` オラクルパターン: レスポンスは**何が**失敗したか(feature\_id)と**どこにルールがあるか**(policy\_id、任意)を伝えます。評価器の内部(信頼度閾値、推論ロジック)は隠されたままです。 ## Supply Path Authorization When `sales_agent_url` is provided in delivery records, the governance agent validates that the sales agent is authorized to sell the property by checking the publisher's `adagents.json`. ### Request with Authorization ```json theme={null} { "list_id": "pl_abc123", "records": [ { "identifier": { "type": "domain", "value": "www.nytimes.com" }, "impressions": 103, "sales_agent_url": "https://legitimate-ssp.example.com" }, { "identifier": { "type": "domain", "value": "www.nytimes.com" }, "impressions": 50, "sales_agent_url": "https://unauthorized-reseller.example.com" } ] } ``` ### Response with Authorization When authorization is validated, each result includes an `authorization` field: ```json theme={null} { "list_id": "pl_abc123", "summary": { "total_records": 2, "total_impressions": 153, "compliant_records": 2, "compliant_impressions": 153, "non_compliant_records": 0, "non_compliant_impressions": 0, "unknown_records": 0, "unknown_impressions": 0 }, "authorization_summary": { "records_checked": 2, "impressions_checked": 153, "authorized_records": 1, "authorized_impressions": 103, "unauthorized_records": 1, "unauthorized_impressions": 50, "unknown_records": 0, "unknown_impressions": 0 }, "results": [ { "identifier": { "type": "domain", "value": "www.nytimes.com" }, "status": "compliant", "impressions": 50, "authorization": { "status": "unauthorized", "publisher_domain": "nytimes.com", "sales_agent_url": "https://unauthorized-reseller.example.com", "violation": { "code": "agent_not_authorized", "message": "Sales agent not listed in nytimes.com/.well-known/adagents.json" } } } ], "validated_at": "2026-01-04T19:00:00Z" } ``` ### Authorization Statuses | Status | Meaning | Included in authorization\_rate? | | -------------- | ------------------------------------------------------ | -------------------------------- | | `authorized` | Sales agent is listed in publisher's adagents.json | Yes (numerator) | | `unauthorized` | Sales agent is NOT listed in publisher's adagents.json | Yes (denominator only) | | `unknown` | Could not fetch or parse adagents.json | No (excluded) | ### Authorization Violation Codes | Code | Description | | ----------------------- | ------------------------------------------------------------------- | | `agent_not_authorized` | Sales agent URL not found in publisher's authorized\_agents list | | `adagents_not_found` | Publisher's adagents.json could not be fetched (404, timeout, etc.) | | `adagents_invalid` | Publisher's adagents.json exists but is malformed | | `property_not_declared` | Property identifier not declared in publisher's adagents.json | ### Two Independent Checks Property compliance and authorization are **independent checks**. A record can be: | Property Status | Authorization Status | Meaning | | --------------- | -------------------- | -------------------------------------------------------- | | compliant | authorized | Fully valid - property in list, sold by authorized agent | | compliant | unauthorized | Property is approved but sold by unauthorized reseller | | non\_compliant | authorized | Authorized agent sold property outside your list | | non\_compliant | unauthorized | Neither property nor agent validated | Both checks use the same "unknown excludes from rate" pattern - you cannot penalize for detection gaps. ## Best Practices ### Batch Validation For large-scale validation, batch records up to the 10,000 limit: ```python theme={null} def validate_delivery_batch(records, list_id, governance_agent): """Validate delivery records in batches.""" batch_size = 10000 all_results = [] for i in range(0, len(records), batch_size): batch = records[i:i + batch_size] response = governance_agent.validate_property_delivery( list_id=list_id, records=batch ) all_results.extend(response.results) return all_results ``` ### Sampling Strategy For real-time monitoring during campaign execution, validate a statistical sample rather than all records: ```python theme={null} import random def sample_and_validate(records, sample_size=1000): """Validate a random sample for real-time monitoring.""" sample = random.sample(records, min(sample_size, len(records))) return governance_agent.validate_property_delivery( list_id=list_id, records=sample ) ``` ### Handling Unknown Records Track unknown rates separately to identify detection gaps: ```python theme={null} def analyze_validation(response): """Analyze validation results with unknown handling.""" summary = response.summary # Core compliance metric compliance_rate = summary.compliance_rate # Detection quality metric unknown_rate = summary.unknown_impressions / summary.total_impressions if unknown_rate > 0.1: print(f"Warning: {unknown_rate:.1%} of impressions unresolvable") return { "compliance_rate": compliance_rate, "unknown_rate": unknown_rate, "non_compliant_impressions": summary.non_compliant_impressions } ``` ## Related Tasks * [create\_property\_list](./property_lists#create_property_list) - Create the list to validate against * [get\_property\_list](./property_lists#get_property_list) - Retrieve current list membership * [get\_adcp\_capabilities](/docs/protocol/get_adcp_capabilities) - Discover available filter features # RFC プロセス Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/rfc-process AdCP への実質的変更を提案し批准する方法 — 提案テンプレートと決定記録形式を含む、ドラフトから仕様変更までのライフサイクル。 プロトコル提案は、実質的変更が仕様に到達する前に動機付けられ、レビューされ、記録されることを保証するため、軽量な RFC(Request for Comments)プロセスを使います。このページは、プロセスがいつ適用されるか、提案をどう提出するか、決定記録がどう見えるかを説明します。 ## 何が RFC を必要とするか | Change | Requires RFC | | -------------------------------------------- | ------------ | | スキーマフィールドの削除またはリネーム | Yes | | タスクの追加または削除 | Yes | | 規範的言語の変更(`MUST` / `SHOULD` / `MAY`) | Yes | | 互換性表面の変更 — デフォルト値、フィールドタイプ、required↔optional | Yes | | オプションスキーマフィールドの追加 | No | | 新 enum 値の追加 | No | | セマンティック変更なしのドキュメント文言明確化 | No | | タイポ修正 | No | | 内部ツール、CI、インフラ | No | | ドキュメントナビゲーション変更(`docs.json`) | No | 疑わしいとき: 変更が下流実装に動作し続けるためコード更新を強いる場合、RFC が必要です。 ## ライフサイクル [proposal template](#proposal-template) をボディとして使い GitHub issue を開きます。タイトル形式: `RFC: `。`rfc` ラベルを追加します。作成者は正式レビューを要求する前にワーキンググループメンバーまたは影響を受ける実装者から早期フィードバックを求めるべきです。 issue は次のワーキンググループセッションのためキューされます。WG が投票する前に少なくとも 2 人の [ワーキンググループメンバー](/docs/community/working-group) がレビュアーチェックリストを完了しなければなりません。レビュー期間は issue 提出後最低 7 暦日です。 WG は決定 — accepted、rejected、または deferred — を、RFC issue へのコメントとして [decision record](#decision-record-format) を投稿することで記録します。合意に達しても反対は記録されなければなりません。 決定記録が存在しそのステータスが **accepted** の後、任意のコントリビューターが spec PR を開けます。PR は `Refs #N`(`Closes #N` ではない)で RFC issue を参照しなければならず、決定記録が存在するまでマージできません。spec PR レビュアーは diff が accepted された RFC スコープに一致することを確認します。最終 spec PR は、マージ時に RFC issue をクローズするため `Closes #N` を運びます。 accepted された RFC は各 spec ライフサイクルステージ遷移の必須トリガーです: それが機能を Draft → Proposed に移し、または Deprecated → Sunset をゲートします。追跡可能な accepted された決定記録なしにライフサイクル遷移は有効ではありません。 ## Proposal template RFC を提出するとき、これを GitHub issue ボディにコピーします: ```markdown theme={null} ## Motivation ## Scope ## Alternatives considered ## Compatibility impact ## Reviewer checklist - [ ] Motivation is clear and not redundant with existing functionality - [ ] Scope is specific enough to implement without further clarification - [ ] Alternatives section covers at least one non-obvious alternative - [ ] Compatibility impact accurately states breaking vs. non-breaking - [ ] Wire-format or schema snippet included (for schema or task changes) ``` ## Decision-record format WG 投票の後、これを RFC issue へのコメントとして投稿します。`Dissent` セクションは必須です — それを省略することはすべてのレビュアーが少数派の立場が存在しないことを明示的に確認したことを示します。 ```markdown theme={null} ## Decision record **Status:** accepted | rejected | deferred **Date:** YYYY-MM-DD **Discussion:** **Vote outcome:** N in favor, N opposed, N abstained ## Rationale ## Dissent ## Next steps ``` ## 関連項目 * 仕様ライフサイクル — accepted された RFC が spec ライフサイクルステージ遷移(Draft → Proposed → Final、および Final → Deprecated)を駆動します。専用ページは [#2441](https://github.com/adcontextprotocol/adcp/issues/2441) で追跡 * [ガバナンス概要](/docs/governance/overview) — 三者モデルとキャンペーンガバナンスドメイン * [埋め込まれた人間の判断](/docs/governance/embedded-human-judgment) — ほとんどの RFC が寄与するガバナンスシステムの背後にある原則 # ワーキンググループ憲章 Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/working-group-charter AdCP ワーキンググループの運用憲章: 定足数、投票しきい値、頻度、忌避、エスカレーション。 AdCP ワーキンググループ(WG)は、Foundation ガバナンスの下で Ad Context Protocol 仕様を開発、レビュー、保守します。この憲章は、WG が自身のミーティング、決定、行動のため採用した運用ルールを記録します。 これは Foundation の [Bylaws](https://agenticadvertising.org/governance)、[IPR Policy](https://github.com/adcontextprotocol/adcp/blob/main/IPR_POLICY.md)、[Charter](https://github.com/adcontextprotocol/adcp/blob/main/CHARTER.md) の下で運用されます。いかなる矛盾でも、それらの文書が優先します。 これらのしきい値はこの文書のマージ日から有効です。マージコミットが権威的な批准記録です。 ## 参加 WG は以下に開かれています: * **投票参加者** — AgenticAdvertising.org Voting Member 組織の従業員。各メンバー組織は 1 票を保持し、指定された代表者が行使します。 * **オブザーバー** — 非メンバーの実務者、研究者、関心のある当事者。オブザーバーは任意のフォーラムで発言しコメントできますが投票できません。 **アクティブステータス** — 投票参加者は、各ローリング 8 週間ウィンドウでいずれかのしきい値を満たすことで投票資格を保持します: * 直近 4 回の同期セッションのうち 2 回以上に出席、または * 直近 4 回の非同期 GitHub 投票のうち 3 回以上に参加([Meeting cadence](#meeting-cadence) を参照)。 申請は不要です。登録は [agenticadvertising.org/governance](https://agenticadvertising.org/governance) で。 ## 決定クラスと投票しきい値 WG は、対応する定足数と可決しきい値ルールを持つ 3 つの決定クラスを使います。これらのしきい値はこの憲章の批准で WG によって採用され、すべての後続決定の有効なルールです。 | Class | Examples | Quorum | Pass threshold | | ------------- | ---------------------------------------------------------- | ---------------------- | -------------- | | **Editorial** | タイポ、リンク切れ修正、非セマンティックな言い換え、メタデータのみの更新 | 投票参加者 3 名 | 単純多数(> 50%) | | **Normative** | 非破壊的追加: オプションフィールド、新タスク、新 enum 値、新ドキュメントセクション、新ケイパビリティ | 投票参加者 5 名、メンバー組織 2 つ以上 | ⅔ 特別多数 | | **Breaking** | 公開表面識別子の削除またはリネーム、optional → required、セマンティック意味変更、デフォルト値変更 | 投票参加者 7 名、メンバー組織 3 つ以上 | ¾ 特別多数 | ### 実験的サーフェス スキーマで `x-status: experimental` とマークされたサーフェス — または `static/schemas/source/trusted-match/`、`static/schemas/source/sponsored-intelligence/`、`static/schemas/source/a2ui/` の下 — への排他的変更は、[実験的サーフェスポリシー](/docs/reference/experimental-status) と一貫したダウングレードされた決定クラスを受けます: | Would be (stable) | Treated as (experimental) | | ----------------- | ------------------------- | | Breaking | Normative | | Normative | Editorial | | Editorial | Editorial | PR がサーフェスを experimental から stable に昇格する(`x-status: experimental` を削除)とき、クラスは実験的履歴ではなく昇格されたサーフェスの最初の安定コントラクトによって決定されます。 ### 分類異議 任意の参加者は、PR が GitHub に投稿されてから **72 時間** 以内に Editorial 分類に異議を唱えられます。異議には異議者からのコメントと他の 1 参加者からの支持が必要です。有効な異議は PR を Normative 扱いに昇格させます。WG Chair が PR スレッドに結果を記録します。 ## Meeting cadence * **ワーキングセッション** — 週次、ビデオと `#wg-adcp` Slack チャネル経由。アジェンダは少なくとも 48 時間前に公開されます。 * **議事録** — 各セッションから 7 暦日以内に [`governance/minutes/`](https://github.com/adcontextprotocol/adcp/tree/main/governance/minutes) に公開されます。 * **セッション録画** — メンバー専用 Slack アーカイブ経由で AgenticAdvertising.org メンバーに利用可能。録画は参加者の率直さを保護し、[Bylaws の Article VII](https://agenticadvertising.org/governance) の反トラストセーフハーバー規定に準拠するためアクセス制限されます。 * **非同期投票** — 任意の参加者はラベル付き issue で非同期 GitHub 投票を開始できます。投票ウィンドウは 5 暦日です。非同期投票はアクティブステータス追跡にカウントされます。 ## エスカレーション WG が標準コメントウィンドウの後に必要なしきい値に到達できないとき: 1. **拡張コメント** — WG Chair がウィンドウを 7 暦日延長し、未解決の反対のサマリーを issue スレッドに投稿します。 2. **Foundation リーダーシップへのエスカレーション** — まだ未解決の場合、Chair は書面で Foundation リーダーシップにエスカレートします。暫定期間(最初の AGM 前)中、エスカレーションは **暫定 Board** に直接行きます。最初の AGM 後、エスカレーションは **Executive Committee** に行き、[Bylaws § 4.14](https://agenticadvertising.org/governance) に従い 30 暦日以内に拘束力ある決議を発行します。 3. **フル Board 投票** — Executive Committee(AGM 後)が Breaking クラス変更でデッドロックした場合、事項は Bylaws の Article IV の理事投票ルールの下でフル Board に昇格されます。 ## タイブレーク * **Editorial** — 単純多数がちょうど同点の場合、WG Chair が決定票を投じます。 * **Normative / Breaking** — WG レベルのタイブレークなし。上のパスに従いエスカレート。 ## 忌避 **一般ルール** — 投票参加者は投票前に任意の利益相反を開示し、以下のとき投票から忌避しなければなりません(ただし議論には残れます): * 彼らの雇用主が特定の決定で名指しされた当事者(例: 彼らの組織を含むレジストリリスティングまたは認定紛争)。 * 一般メンバーシップと共有されない結果への財務的利益を持つ。 * 彼らまたは彼らの雇用主が、投票中の変更で Necessary Claims となるクレームを持つ特許を保有。開示義務については [IPR Policy](https://github.com/adcontextprotocol/adcp/blob/main/IPR_POLICY.md) を参照。 **暫定 Board 集中(最初の AGM まで)** — [CHARTER.md § 4.1](https://github.com/adcontextprotocol/adcp/blob/main/CHARTER.md#41-interim-board) で開示されているとおり、4 人の暫定理事のうち 2 人(Michael Blum と Brian O'Kelley)が Scope3 を代表します。暫定期間中、一般ルールは次のように補足されます: Scope3 に所属する任意の WG 参加者は、投票前に Scope3 に特定の実質的優位を与える任意の WG 決定(例: 主に Scope3 が開発または保守するインフラやツールを優先する決定)を宣言しなければなりません。優位がメンバーシップと広く共有されない場合、その投票からの忌避が必要です。これは一般ルールへの追加であり、その代替ではありません。 忌避は議事録に記録されます。 ## 名簿 投票参加者とそのメンバー組織所属の現在の名簿は [agenticadvertising.org/governance](https://agenticadvertising.org/governance) で保守されます。暫定 Board 構成は [CHARTER.md § 4.1](https://github.com/adcontextprotocol/adcp/blob/main/CHARTER.md#41-interim-board) にリストされています。 ## 修正 この憲章への修正は Normative クラスルール(⅔ 特別多数、投票参加者 5 名、メンバー組織 2 つ以上)に従います。Breaking クラス決定の定足数または可決しきい値を減らす修正は、それ自体が Breaking クラスルールの下で可決しなければなりません。 # アカウンタビリティ Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/advanced-topics/accountability AdCP 保証バイで、バイヤーとセラーがパフォーマンス標準、測定条件、キャンセルポリシーをどう交渉、強制、解決するか。 ## 概要 AdCP の保証メディアバイは 3 つのアカウンタビリティ表面を持ちます: * **パフォーマンス標準** — ビューアビリティ、IVT、完了、ブランドセーフティ、アテンションのレートしきい値(IAB T\&C Section XI) * **測定条件** — 誰が課金メトリックを数えるか、しきい値が破られたときどの救済(メイクグッド)が適用されるか(IAB T\&C Sections V、VII、IX) * **キャンセルポリシー** — 早期終了の通知期間とキャンセル料(IAB T\&C Section XII) これらは構造化された機械可読フィールドで、フリーテキストではありません。バイヤーとセラーエージェントは標準の製品ディスカバリーとバイ作成ワークフローを通じてプログラマティックにそれらを交渉します。 ## ライフサイクル ### 1. ディスカバリー: バイヤーが要件を述べる `get_products` で、バイヤーはパフォーマンス要件を満たす製品にフィルターします: ```json theme={null} { "buying_mode": "brief", "brief": "Premium video for CPG brand, Q3 flight", "filters": { "delivery_type": "guaranteed", "required_performance_standards": [ { "metric": "viewability", "threshold": 0.70, "standard": "mrc", "vendor": { "domain": "doubleverify.com" } }, { "metric": "ivt", "threshold": 0.05, "vendor": { "domain": "doubleverify.com" } } ] } } ``` これらのしきい値を満たせない、または指定されたベンダーをサポートしない製品は結果から除外されます。バイヤーは言っています: 「70% MRC でのビューアビリティと 5% 未満の IVT に DoubleVerify が必要。」 ### 2. 製品レスポンス: セラーがデフォルトを宣言 返される製品はセラーのデフォルト `performance_standards`、`measurement_terms`、`cancellation_policy` を含みます: ```json theme={null} { "product_id": "premium_video_q3", "delivery_type": "guaranteed", "performance_standards": [ { "metric": "viewability", "threshold": 0.70, "standard": "mrc", "vendor": { "domain": "doubleverify.com" } }, { "metric": "ivt", "threshold": 0.05, "vendor": { "domain": "doubleverify.com" } }, { "metric": "completion_rate", "threshold": 0.75, "vendor": { "domain": "doubleverify.com" } } ], "measurement_terms": { "billing_measurement": { "vendor": { "domain": "admanager.google.com" }, "max_variance_percent": 10 }, "makegood_policy": { "available_remedies": ["additional_delivery", "credit", "invoice_adjustment"] } }, "cancellation_policy": { "notice_period": { "interval": 30, "unit": "days" }, "cancellation_fee": { "type": "percent_remaining", "rate": 0.5 } } } ``` バイヤーは予算をコミットする前にすべての条件を見られます。 ### 3. リファインメント: コミット前に交渉 `buying_mode: "refine"` を使って、バイヤーはパフォーマンス標準または測定条件への変更を提案できます。これは同じ `required_performance_standards` フィルターを使います — セラーは提供できるものを反映した更新された製品で応答します。リファインメントは反復的で拘束力がありません。 ### 4. バイ作成: バイヤーが提案、セラーが受諾 `create_media_buy` で、バイヤーはパッケージリクエストで異なる条件を提案できます: ```json theme={null} { "product_id": "premium_video_q3", "budget": 50000, "pricing_option_id": "cpm_usd_fixed", "performance_standards": [ { "metric": "viewability", "threshold": 0.75, "standard": "mrc", "vendor": { "domain": "doubleverify.com" } }, { "metric": "ivt", "threshold": 0.03, "vendor": { "domain": "doubleverify.com" } } ], "measurement_terms": { "billing_measurement": { "vendor": { "domain": "campaignmanager.google.com" }, "max_variance_percent": 5 } } } ``` セラーは 3 つのレスポンスを持ちます: * **受諾** — 確認済みパッケージでバイヤーの条件をエコー * **拒否** — どの条件が失敗したかと許容範囲についての詳細を伴う `TERMS_REJECTED` を返す * **調整** — 確認済みパッケージで変更された条件を返す(バイヤーエージェントがレスポンスを検査して何が変わったか見る) バイヤーが `performance_standards` または `measurement_terms` を省略するとき、製品のデフォルトが適用されます。 #### 段階成熟チャネルの測定条件 一部のチャネルは初日に最終数値を配信するのではなく段階的に課金グレードデータを生成します — 放送 TV、DOOH、IVT フィルタリング付きデジタル、ポッドキャストダウンロードなど。これらには、バイヤーはベンダーと並んで `measurement_window` を提案し、保証がどの成熟段階に対して照合されるかを指定します: ```json theme={null} { "product_id": "primetime_30s_q4", "budget": 250000, "pricing_option_id": "unit_rate_30s", "agency_estimate_number": "EST-2026-04821", "measurement_terms": { "billing_measurement": { "vendor": { "domain": "videoamp.com" }, "measurement_window": "c7", "max_variance_percent": 10 } } } ``` `measurement_window` は製品の `reporting_capabilities.measurement_windows` からの `window_id` を参照します。これは両側に伝えます: 「VideoAmp の C7 数値が私たちが照合する対象。」DOOH 製品には `"final"`(IVT/不正チェック後)。デジタルには `"post_sivt"` かもしれません。同じメカニズムが、どのデータが課金に権威的かといつ利用可能になるかの両方を宣言します — 照合と請求のクロックはその宣言された可用性に従います。`agency_estimate_number` は、オーダーをエージェンシーのメディアプランにリンクする財務参照で、トランザクションライフサイクルを通じてオーダーとともに移動します。 ### 5. 確認済みパッケージ: コントラクト 確認済みパッケージは合意された条件を反映します — 拘束力あるコントラクト: ```json theme={null} { "package_id": "pkg_001", "product_id": "premium_video_q3", "performance_standards": [ { "metric": "viewability", "threshold": 0.75, "standard": "mrc", "vendor": { "domain": "doubleverify.com" } }, { "metric": "ivt", "threshold": 0.03, "vendor": { "domain": "doubleverify.com" } } ], "measurement_terms": { "billing_measurement": { "vendor": { "domain": "campaignmanager.google.com" }, "max_variance_percent": 5 }, "makegood_policy": { "available_remedies": ["additional_delivery", "credit", "invoice_adjustment"] } } } ``` ### 6. クリエイティブ強制 合意された `performance_standards` がベンダーを指定するとき、そのパッケージに割り当てられたクリエイティブはそのベンダーからの `tracker_script` または `tracker_pixel` URL アセットを含まなければなりません(MUST)。セールスエージェントは、必要な検証タグを欠くクリエイティブ割り当てを `CREATIVE_REJECTED` で拒否すべきです(SHOULD)。 例えば、合意された条件がビューアビリティに DoubleVerify を含む場合、パッケージのすべてのクリエイティブは、ビューアビリティが測定できるよう DV タグを運ばなければなりません。 **トラッカーサポートのないフォーマット**: すべてのフォーマットがサードパーティトラッキングアセットを受け入れるわけではありません。例えば放送 TV スポットはトラッカースロットを持ちません — テレビに発火するピクセルがありません。バイヤーエージェントは、クリエイティブレベル検証を要求するパフォーマンス標準を提案する前に、フォーマットの `assets` 配列でトラッカースロットをチェックすべきです。フォーマットがトラッカーをサポートしないとき、測定はクリエイティブ埋め込みピクセルではなく `billing_measurement` で宣言されたベンダー(パネルデータ、セットトップボックステレメトリー)から来ます。詳細については [フォーマット定義](/docs/creative/formats#third-party-tracker-support) を参照してください。 ### 7. 違反と解決 パフォーマンス標準または課金測定分散が破られたとき、セラーは合意された `makegood_policy` から救済を提案します: * **`additional_delivery`** — インプレッションを延長または追加(like-for-like、同じまたは後のキャンペーン) * **`credit`** — 同じアカウントの将来のバイへのクレジット * **`invoice_adjustment`** — 現在のバイの請求書を削減 バイヤーは受諾または異議を唱えます。 ## キャンセルポリシー キャンセルポリシーは交渉表面ではありません。セラーが製品でそれを宣言します。バイヤーはメディアバイを作成することでそれを受諾します。十分な通知なしにキャンセルされた保証バイは、宣言されたキャンセル料を負います。 **キャンセル料タイプ:** * `percent_remaining` — 残りのコミット済み支出のパーセンテージ(例: 50%) * `full_commitment` — バイヤーが完全なコミット済み予算を負う * `fixed_fee` — 定額の金銭 * `none` — キャンセル料なし ## インサーションオーダー インサーションオーダーはコミット済みプロポーザルの署名ラッパーです。新しいディール条件を導入しません。すべての交渉された条件は製品とパッケージに存在します。IO の `terms` オブジェクトは、バイヤーエージェントが人間が DocuSign などで署名する前に IO がプロポーザルに一致することを検証できるよう、サマリーフィールド(アドバタイザー、パブリッシャー、予算、日付、支払い条件)を提供します。 ## ベンダーアイデンティティと測定エージェント すべての測定と検証ベンダーは、標準の [ブランド参照](/docs/brand-protocol/brand-json) を使ってドメインで識別されます — ブランド、オペレーター、アカウントに使われるのと同じシステム。例: * `{ "domain": "doubleverify.com" }` — DoubleVerify * `{ "domain": "integralads.com" }` — IAS * `{ "domain": "oracle.com", "brand_id": "moat" }` — MOAT * `{ "domain": "campaignmanager.google.com" }` — Google Campaign Manager * `{ "domain": "admanager.google.com" }` — Google Ad Manager * `{ "domain": "videoamp.com" }` — VideoAmp(放送/CTV 測定) * `{ "domain": "comscore.com" }` — Comscore(クロスプラットフォーム測定) ベンダーのドメインの `brand.json` は、そのエージェントケイパビリティのディスカバリーポイントです。ベンダーは `type: "measurement"` で `agents` 配列にエージェントを宣言します: ```json theme={null} { "house": { "domain": "doubleverify.com", "name": "DoubleVerify", "agents": [ { "type": "measurement", "url": "https://api.doubleverify.com/adcp/measurement", "id": "dv_measurement" } ] } } ``` これは brand.json のすべてのエージェントタイプに使われる同じパターンに従います — brand、rights、governance、creative、buying、signals エージェントはすべて同じ方法で発見されます。 バイヤーまたはセラーは、キャンペーン後のレポートを待つのではなく、合意されたパフォーマンス標準に対する現在のレートを測定エージェントにクエリします。測定ベンダーはブラックボックスではなくエージェントとして参加します。 ### コンテンツ標準との関係 [コンテンツ標準エージェント](/docs/governance/content-standards) は WHAT が配信されたか(ブランドセーフティ、コンテンツ分類)を検証します。パフォーマンス標準は HOW WELL 配信されたか(ビューアビリティレート、IVT レート、完了レート)を測定します。ベンダーは同じ会社かもしれません — DoubleVerify はブランドセーフティスコアリングとビューアビリティ測定の両方を提供します — が、関心事は別個です: * **コンテンツ標準**: `validate_content_delivery` — 「この広告は安全なコンテンツの隣に配置されたか?」 * **パフォーマンス標準**: 測定エージェント — 「インプレッションの何パーセントがビューアブルだったか?」 両方ともエージェント間ワークフローを使います。コンテンツ標準は既に完全に仕様化されています。測定エージェントインターフェースはこの仕様のフォローアップです。 # アカウントとセキュリティ Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/advanced-topics/accounts-and-security AdCP のアカウントとセキュリティ — バイヤーとセラーエージェント間のマルチテナントメディア購入における認証、レートカード、請求エンティティ、データアイソレーション。 **アカウント**は AdCP においてバイヤーとセラー間の請求関係を表します。セールスエージェントはアカウントを使用して料金(レートカード)、請求エンティティを決定し、異なるバイヤー間のデータアイソレーションを適用します。 ## 認証 すべてのリクエストは標準の `Authorization` ヘッダーにベアラートークンを使用して認証する必要がある: ``` Authorization: Bearer ``` サーバーはこのトークンを検証し、リクエストを行っている**エージェント**を識別します。エージェントは1つ以上のアカウントにアクセスできます。 認証情報の取得と認証方法の詳細は [Authentication](/docs/building/by-layer/L2/authentication) を参照。 ### エージェントとアカウント AdCP は以下を区別する: * **エージェント**: API コールを行う認証済みエンティティ(例: `"pinnacle_trading_desk"`) * **アカウント**: メディアバイの請求関係(例: `"acme_c/o_pinnacle"`) エージェントは複数のアカウントで操作できます。例えば、エージェンシーのトレーディングデスクは複数の広告主のアカウントと自社のハウスアカウントを管理する場合があります。詳細は [Accounts and Agents](/docs/building/by-layer/L2/accounts-and-agents) を参照。 ## データアイソレーション 認証は厳格なデータアイソレーションの基盤を提供します。セールスエージェントは以下のルールを適用**しなければなりません** (MUST): 1. `MediaBuy` などのオブジェクトが作成される場合、そのリクエストで使用されたアカウントと**永続的に**関連付けなければなりません (MUST)。 2. そのオブジェクトを読み取るまたは変更するその後のリクエストでは、サーバーはエージェントがそのアカウントへのアクセスを持つことを検証しなければなりません (MUST)。 3. エージェントがアクセスを持たない場合、サーバーは権限拒否エラーを返さなければなりません (MUST)。 このモデルにより、1つのアカウントのデータが認可されていないエージェントにアクセスされないことが保証されます。アクセス権を持たないアカウントの `account_id` を渡すとエラーになります。 ## セキュリティ要件 完全な規範的実装リファレンス——二段階認可、行レベルセキュリティ、IDOR 防御、およびより広範なセキュリティ体制(Webhook、冪等性、署名済みガバナンスコンテキスト)——は、[セキュリティ — エージェントとアカウントのアイソレーション](/docs/building/by-layer/L1/security#agent-and-account-isolation)を参照してください。 ### 必須セキュリティ対策 セールスエージェントの実装は以下を**しなければなりません** (MUST): * すべての認証済みリクエストでベアラートークンを検証します * アカウントベースのデータアイソレーションを適用します * すべての通信に TLS を使用します * セキュリティ監視のために認証失敗をログに記録します ### 推奨セキュリティ対策 セールスエージェントの実装は以下を**すべきだ** (SHOULD): * エージェントとアカウントごとにレート制限を実装します * トークンの有効期限とリフレッシュをサポートします * コンプライアンスのために監査ログを提供します * 高セキュリティアカウントのための IP ホワイトリストをサポートします # Agentic eXecution Engine (AXE) Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/advanced-topics/agentic-execution-engine ブランドセーフティ、フリークエンシーキャップ、動的オーディエンスターゲティングを実現する AdCP のリアルタイム実行レイヤー。 AXE は非推奨です。[トラステッドマッチプロトコル(TMP)](/docs/trusted-match)が、構造的なプライバシー分離、マルチサーフェスのサポート(ウェブ、モバイル、CTV、AI アシスタント、リテールメディア)、標準化されたオファーモデルとともに AXE を置き換えます。新規の統合は TMP を使うべきです。既存の AXE 統合は引き続き機能します——`axei`/`axex`/`axem` のセグメントモデルは TMP のオファーとシグナルにマップされます。 Agentic eXecution Engine (AXE) は、インプレッション時に動的ターゲティング・ブランドセーフティ・頻度管理を行う AdCP の元来のリアルタイム実行レイヤーです。 AXE は、AdCP がインプレッション時の実行に到達する方法です。OpenRTB がプログラマティックな判断を可能にするのと同じ方法で——アドサーバーが配信するかどうかを決める前に、各インプレッションに対するリアルタイムの視点をバイヤーまたはオーケストレーターに与えることで——パブリッシャー横断のフリークエンシーキャップを可能にします。 ## 2 フェーズのワークフロー AXE は **オフラインのセットアップ** と **リアルタイム配信** の 2 フェーズで動作します。 ### Phase 1: オフラインセットアップ 広告を配信する前に、キャンペーン設定とセグメントデータの同期を行います。 ```mermaid theme={null} flowchart TB subgraph offline["Offline Setup Phase"] direction TB buyer["**Buyer Agent**
• Campaigns
• Budgets
• Targeting"] signal["**Signal Agent**
• Audiences
• Contextual
• Brand safe"] orch["**Orchestrator**
• Map to AXE segments
• Sync to RTD"] sales["**Sales Agent**
Create line items
with AXE targeting"] buyer --> orch signal --> orch orch --> sales end ``` **流れ:** 1. **Buyer Agent** がターゲティング・予算付きキャンペーンを作成 2. **Signal Agent** がコンテキストデータ(オーディエンス、ブランドセーフティルール、天気トリガー等)を付与 3. **Orchestrator** がキャンペーンを AXE セグメントにマッピングしリアルタイムモジュールへ同期 4. **Sales Agent** が AXE セグメントのキー値に基づくラインアイテムを作成 ### Phase 2: リアルタイム配信 広告リクエストが届くと、AXE がリアルタイムで評価しセグメント判定を返します。 ```mermaid theme={null} flowchart TB subgraph realtime["Real-Time Serving Phase"] direction TB page["**Page**
User visits"] adserver1["**Ad Server**
GAM / Kevel"] prebid["**Prebid**
OpenRTB request"] axe["**AXE**
Segment lookup"] page --> adserver1 --> prebid --> axe axeresponse["**AXE Response**
axei: Include segment
axex: Exclude (brand safety)
axem: Creative macro data"] axe --> axeresponse adserver2["**Ad Server**
Match segments to line items
→ Serve ad"] axeresponse --> adserver2 end ``` **流れ:** 1. ユーザーがページ訪問し広告リクエスト発火 2. アドサーバーが Prebid 等へリクエスト 3. Prebid が OpenRTB 入札リクエストを AXE に送信 4. AXE がユーザー/コンテキストを評価しセグメント値を返却 5. アドサーバーがセグメントに合うラインアイテムを選び配信 ## AXE セグメントタイプ AXE はアドサーバーへ 3 種のセグメント値を返します: | Segment | Key | Purpose | Example | | ----------- | ------ | ------------------------------ | ---------------------------- | | **Include** | `axei` | オーディエンス用(ユーザーが所属) | `"seg_auto_intenders"` | | **Exclude** | `axex` | ブランドセーフティ/抑制用(このインプレッションをブロック) | `"unsafe_content"` | | **Macro** | `axem` | クリエイティブのパーソナライズデータ | `"eyJjb250ZXh0IjoiLi4uIn0="` | ### セグメントがクリエイティブへ渡る流れ ```json theme={null} { "packages": [{ "product_id": "premium_video", "targeting_overlay": { "axe_include_segment": "seg_auto_intenders_q1", "axe_exclude_segment": "seg_existing_customers" } }] } ``` インプレッション時: * `axei` を `axe_include_segment` と照合 → 一致していれば配信 * `axex` を `axe_exclude_segment` と照合 → 一致していれば配信しません * `axem` を `{AXEM}` マクロ経由でクリエイティブに渡します ## データフロー例 顧客獲得キャンペーンでの AXE 利用例: ### セットアップ(オフライン) **1. Buyer がサプレッションリストをアップロード:** ``` Advertiser CRM → Hash emails (SHA256) → Upload to orchestrator → Receive segment ID: "seg_existing_customers_acme" ``` **2. AXE ターゲティング付きで media buy を作成:** ```json theme={null} { "packages": [{ "product_id": "premium_video_millennials", "budget": { "amount": 50000 }, "targeting_overlay": { "axe_exclude_segment": "seg_existing_customers_acme" } }] } ``` **3. Sales Agent がラインアイテムを作成:** ``` Line item: "Acme Q1 Acquisition" Targeting: axex != "seg_existing_customers_acme" ``` ### 配信(リアルタイム) **4. ユーザーがパブリッシャーサイトを訪問:** ``` GET /ad-request User-Agent: Mozilla/5.0... Cookie: uid=abc123 ``` **5. AXE ルックアップ:** ``` Input: uid=abc123 Check: Is abc123 in seg_existing_customers_acme? Result: YES (hashed email matches) ``` **6. AXE レスポンス:** ```json theme={null} { "axei": null, "axex": "seg_existing_customers_acme", "axem": null } ``` **7. アドサーバーでの判定:** ``` Line item requires: axex != "seg_existing_customers_acme" Current axex: "seg_existing_customers_acme" Decision: DO NOT SERVE (user is existing customer) ``` **結果:** 獲得予算が既存顧客に浪費されない。 ## コア機能 ### 1. 動的オーディエンスターゲティング 手持ちの DMP/CDP セグメントをパブリッシャー在庫に適用: * オーディエンスデータをアップロード(ハッシュ済みメール、デバイス ID など) * オーケストレーターからセグメント ID を受領 * `axe_include_segment` にセグメント ID を参照 * インプレッション時に AXE がユーザーを照合 **ユースケース:** Lookalike、CRM 活用、行動セグメント ### 2. ブランドセーフティ インプレッション時のリアルタイムコンテンツ評価: * **コンテンツ分類** - ニュース/エンタメ/スポーツなど * **センチメント分析** - ポジ/ネガの検知 * **キーワードブロック** - ブランド固有の NG ワード回避 * **隣接ルール** - ページ上の他広告との並び ブランドセーフティのルールは Signal Agent からオーケストレーターを経て AXE に流れます。 ### 3. パブリッシャー横断のフリークエンシー管理 パブリッシャー側の頻度制御と異なり、AXE は以下を横断管理します: * 複数パブリッシャー * 複数キャンペーン * 複数デバイス(ID 解決を含む) AXE が頻度キャップを適用し、アドサーバーにセグメント判定を返します。アドサーバーは「なぜ」ではなく「配信可否」のみを知ります。 ### パブリッシャー横断のフリークエンシーキャップの仕組み パブリッシャー横断のフリークエンシーキャップは、現在は[トラステッドマッチプロトコル(TMP)](/docs/trusted-match)によって扱われます。TMP は構造的に分離されたコンテキストマッチとアイデンティティマッチの操作を使います。AXE のセグメントモデルは、TMP のオファーと適格性レスポンスにマップされます。 AXE モデルでは、すべての適格なインプレッションが、共有された露出状態に対してリアルタイムで確認されます: ```mermaid theme={null} flowchart LR pubA["**Publisher A**
Impression opportunity"] pubB["**Publisher B**
Impression opportunity"] pubC["**Publisher C**
Impression opportunity"] req["**OpenRTB-style request**
user, placement, context"] axe["**AXE**
Evaluate cap eligibility"] state["**Exposure store**
cross-publisher history"] decision["**Decision**
serve or suppress"] pubA --> req pubB --> req pubC --> req req --> axe state --> axe axe --> decision ``` TMP では、この同じパターンが構造的なプライバシーとともに実現されます: アイデンティティマッチのパスがフリークエンシーキャップを扱い(バイヤーは、ユーザーがどのページにいるかを知らずに露出履歴を確認します)、コンテキストマッチのパスがコンテンツの関連性を扱います(バイヤーは、ユーザーが誰かを知らずにパッケージを評価します)。パブリッシャーは両方のレスポンスをローカルで結合します。 ### 4. ファーストパーティデータ活用 PII を共有せずに顧客データを活用: 1. 顧客 ID(メール/電話など)をハッシュ化 2. オーケストレーターへアップロード(データはオーケストレーターに留まる) 3. キャンペーンでセグメント ID を参照 4. AXE がインプレッション時に照合 5. パブリッシャーは生データに触れない ## Privacy by Design: 不透明なセグメント ID AXE の設計原則は **セグメント ID を意図的に不透明にすること** です。アドサーバーは `ABCD` が一致/不一致だったことだけを知り、そのセグメントが何を意味するかは分かりません。 それは以下を意味し得ます: * ユーザーが頻度上限を超えた * ページがブランドセーフティ検査に失敗 * ユーザーがファーストパーティサプレッションリストに含まれます * ユーザーがオーディエンスセグメントに一致 この不透明性がバイヤーデータを守ります。パブリッシャーやアドサーバーは次のような逆算ができません: * CRM リストに誰が含まれるか * 頻度キャップの閾値 * ブランドセーフティルールの内容 * オーディエンスセグメントの定義 アドサーバーが知るのは「AXE が配信可と言った/不可と言った」だけです。 ## インテグレーションの要点 ### バイヤー向け | Step | Action | Result | | ---- | --------------------------------- | ---------------------- | | 1 | オーディエンスをオーケストレーターにアップロード | セグメント ID を受領 | | 2 | `create_media_buy` にセグメント ID を含める | AXE ターゲティング付きでキャンペーン作成 | | 3 | 配信レポートを監視 | セグメントのマッチ率を追跡 | ### パブリッシャー向け パブリッシャーは AXE を直接実装しません。AXE ターゲティングをサポートするには: 1. **Prebid 等の RTD と統合** 2. **キーバリューターゲティングを受け入れる** - `axei`/`axex` をアドサーバーへ渡します 3. **ラインアイテム設定** - AXE セグメントのキーバリューでターゲティング 4. **対応表明** - `adagents.json` で AXE 対応を宣言 ### オーケストレーター向け オーケストレーターは AXE レイヤーを運用します。 1. **セグメントインジェスト** - バイヤーからオーディエンスデータを受け入れる 2. **リアルタイムルックアップ** - 10ms 未満でセグメント所属判定 3. **シグナル統合** - ブランドセーフティやコンテキストシグナルを適用 4. **頻度状態の維持** - キャンペーン横断の露出管理 5. **RTD モジュール** - Prebid や OpenRTB でセグメントを公開 ## AXE がページに到達する仕組み AXE はプロトコルレベルの概念です。**オーケストレーターが AXE を実装し**、それを広告配信環境に統合します。統合の経路は広告プラットフォームに依存します: | 統合の経路 | 仕組み | 例 | | -------------------- | ---------------------------------------------------- | -------------------- | | **Prebid RTD モジュール** | オーケストレーターが、オークション中に AXE エンドポイントを呼ぶ Prebid モジュールを配布する | `exampleRtdProvider` | | **独自の広告プラットフォーム** | AXE がプラットフォームのインフラ内でコンテナまたはセキュアエンクレーブとして実行される | プラットフォームネイティブな統合 | | **サーバーサイド** | 判断の前に広告プラットフォームがサーバー間で AXE エンドポイントを呼ぶ | カスタムアドサーバー統合 | 共通する筋道: 統合の経路が何であれ、AXE はセグメントを評価し、広告プラットフォームがターゲティングに使う `axei`/`axex`/`axem` の判定を返します。パブリッシャー横断のフリークエンシーキャップでは、それらのインプレッション時の呼び出しこそが、一つのパブリッシャーのローカルなアドサーバーのカウンターに頼るのではなく、バイヤーがセラー横断で共有された露出ルールを適用できるようにするものです。 ### 連鎖: オーケストレーター → AXE エンドポイント → セグメントターゲティング ``` Ad platform calls orchestrator's AXE endpoint → AXE evaluates segments and returns axei/axex/axem values → Values used for targeting decisions (key-values, container logic, etc.) → Matching campaigns serve ``` セラーの [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) レスポンスにある `axe_integrations` の URL は、セラーがどのオーケストレーターの AXE エンドポイントに接続するかをバイヤーに伝えます: ```json theme={null} { "media_buy": { "execution": { "axe_integrations": ["https://axe.example.com"] } } } ``` ### Prebid 統合(ウェブ) Prebid を使うウェブパブリッシャーでは、AXE はオーケストレーターの RTD モジュールを介して統合されます。Prebid のモジュール名は「AXE」ではなくオーケストレーターに一致します: ```javascript test=false theme={null} // Prebid build includes: rtdModule, exampleRtdProvider, ...other modules pbjs.setConfig({ realTimeData: { auctionDelay: 100, dataProviders: [{ name: 'example', // Orchestrator's module name waitForIt: true, params: { // Orchestrator-specific configuration } }] } }); ``` オーケストレーターの RTD モジュールは: 1. 入札がリクエストされる前にオークションを傍受する 2. AXE エンドポイントに OpenRTB スタイルのリクエストを送る 3. セグメント判定(axei/axex/axem)を受け取る 4. アドサーバーのリクエストにターゲティングのキーバリューを設定する パブリッシャーは AXE の内部を知る必要はありません——オーケストレーターのモジュールがすべてを扱います。 ### 独自プラットフォームの統合 AXE は、独自の広告プラットフォーム内でコンテナまたはセキュアエンクレーブとして実行することもできます。このモデルでは: * オーケストレーターが AXE のロジックをプラットフォームのインフラにデプロイする * セグメント評価がプラットフォームの判断パイプライン内で行われる * インプレッション時に外部ネットワーク呼び出しが不要——レイテンシを削減する * プラットフォームが、ネイティブな広告選択プロセスの一部として AXE を呼ぶ これは、Prebid を使わないプラットフォームや、外部の RTD 呼び出しが許すよりも厳しいレイテンシ要件を持つプラットフォームで特に関連します。 ### AXE サポートの見分け方 決定的な確認方法は、セラーの `get_adcp_capabilities` レスポンスです。Prebid ベースの統合では、ページを直接調べることもできます: | 見るべきもの | 場所 | 意味 | | -------------------------------------------------------- | --------------------------------- | ---------------------- | | ケイパビリティ内の `axe_integrations` | `get_adcp_capabilities` レスポンス | セラーが AXE をサポート | | `axei`/`axex`/`axem` のキーバリュー | アドサーバーのリクエスト(ネットワークタブ) | AXE セグメントがアドサーバーへ流れている | | Prebid ビルド内のオーケストレーター RTD モジュール(例: `exampleRtdProvider`) | ページソースまたは `pbjs.installedModules` | Prebid 経由の AXE | | `realTimeData.dataProviders` 内のオーケストレーターのエントリ | `pbjs.getConfig('realTimeData')` | AXE がアクティブ | 異なるオーケストレーターは異なる統合経路を通じて AXE を実装する場合があります——セグメントプロトコル(axei/axex/axem)は、AXE がどのようにデプロイされるかに関わらず同じです。 ## ユニバーサルマクロ: クリエイティブは AXE のコンテキストデータを受け取り動的レンダリングが可能です。 ```html theme={null} ``` `{AXEM}` マクロには base64 エンコードのコンテキストメタデータが含まれます: * 天候条件 * コンテンツカテゴリ * ユーザーセグメント属性(匿名化) * カスタムオーケストレーターデータ 詳細は [ユニバーサルマクロ](/docs/creative/universal-macros) を参照してください。 ## AXE を使うべき/避けるべきシナリオ | Scenario | Use AXE? | Alternative | | -------------------- | -------- | ------------------------ | | CRM 内ユーザーをターゲティング | ✅ Yes | — | | 既存顧客を除外 | ✅ Yes | — | | パブリッシャー横断の頻度キャップ | ✅ Yes | — | | リアルタイムブランドセーフティ | ✅ Yes | — | | 「カリフォルニア在住のミレニアル」 | ❌ No | ブリーフで表現 | | 地理的制約 | ❌ No | `geo_country_any_of` を使用 | | パブリッシャーのオーディエンスセグメント | ❌ No | ブリーフで表現 | | 単一パブリッシャーの頻度キャップ | ❌ No | パブリッシャーのアドサーバーで対応 | ## 性能 AXE は広告配信のレイテンシ要件に沿って設計されています。 | Operation | Target Latency | | ----------- | -------------- | | セグメント所属判定 | \< 10ms | | ブランドセーフティ評価 | \< 20ms | | 頻度チェック | \< 5ms | | AXE 判定全体 | \< 50ms | ## 関連ドキュメント * **[Targeting](/docs/media-buy/advanced-topics/targeting)** - ブリーフベースのターゲティングと地理オーバーレイ * **[Signals Protocol](/docs/signals/overview)** - シグナル探索と有効化 * **[Universal Macros](/docs/creative/universal-macros)** - クリエイティブでの AXE 連携 * **[Orchestrator Design](/docs/building/operating/orchestrator-design)** - オーケストレーション基盤の構築 # 課金権威 Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/advanced-topics/billing-authority AdCP がメディアバイの課金メトリックの権威的カウンターをどう名指し、数値を最終としてマークし、バイヤーとセラーのビューが不一致のときそれらを照合するか。 ## 概要 メディアバイが決定的に請求するには、2 つの質問が機械可読な答えを持たなければなりません: 1. **誰の数値が権威的か?** 異なるディールは異なる当事者を名指します — セラーの広告サーバー、バイヤーのサードパーティ広告サーバー、または Nielsen や IAS のような名指しされた測定ベンダー。 2. **その数値は動くのを止めたか?** テレメトリーは数時間、数日、数週間で落ち着きます(放送 C3 → C7 DVR 蓄積、デジタル IVT 後スクラブ、ポッドキャスト 30 日ダウンロード、コンバージョン dedup)。最終数値は請求可能。暫定数値はそうでない。 AdCP は両方に、既にワイヤー上にある構造化条件で答えます: `measurement_terms.billing_measurement`(`create_media_buy` で交渉)が権威を名指す。`finalized_at` タイムスタンプ付き `is_final` / `final` フラグ(`get_media_buy_delivery` と `report_usage` 上)がクロージャーをマークする。 このページはそれらの部分を結びつけます。 ## 権威の名指し `measurement_terms.billing_measurement` は以下を運びます: | Field | Meaning | | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `vendor`(必須、[BrandRef](/docs/brand-protocol/brand-json)) | 課金メトリックのカウントが請求を統制する当事者。同じフィールド形状がセラーの広告サーバー、バイヤーの 3PAS、またはサードパーティベンダーをカバー — BrandRef ドメインが曖昧性解消。 | | `max_variance_percent` | セラーと権威当事者のカウントが `makegood_policy` の下で解決をトリガーする許容度。IAB デフォルトは 10%。放送/CTV はしばしば 5%。 | | `measurement_window` | データのどの成熟段階が照合ポイントか(`c7`、`post_sivt`、`downloads_30d` など) — 製品の `reporting_capabilities.measurement_windows` からの `window_id` を参照。 | | `finalization_deadline_hours` | 権威当事者が最終レコードを公開しなければならない(MUST)最大時間。`measurement_window` が設定されているとき、時間はウィンドウのクローズから数えられる(`reporting_period.end` からではない)。不在のとき、`reporting_period.end` から。デッドラインは `vendor` に名指しされたどちらの当事者にも対称的に適用。ミス時、相手方は自身の証明にフォールバックしてもよい(MAY)。違反は `makegood_policy` の下で処理される。 | 不在は情報的です: `billing_measurement` が不在のとき、デフォルトはセラー証明で、契約上の最終化デッドラインなし。 ## 数値を最終としてマーク Final は **名指しされた測定ウィンドウについて落ち着いた** を意味します — 「永遠に最終」ではありません。`measurement_window: "c3"` について `is_final: true` とマークされた放送行は C3 について最終です。後の `c7` 行が自身の暫定 → 最終ライフサイクルでそれを置き換えます。紛争(AdCP 3.2 が追加するとき)は `(media_buy_id, reporting_period, measurement_window)` で結合します。 ### `get_media_buy_delivery` 上 `media_buy_deliveries[]` の各行は以下を運びます: * `is_final: boolean` — 行レベルの最終性、行のすべてのパッケージが同じウィンドウについて最終であることと同等。 * `finalized_at: string (date-time)` — `is_final: true` のとき、かつそのときのみ存在。`billing_measurement.finalization_deadline_hours` で宣言された任意のデッドラインをアンカー。 * パッケージごと: `by_package[*].is_final`、`by_package[*].finalized_at`、`by_package[*].measurement_window`、`by_package[*].supersedes_window`。 ### `report_usage` 上 各使用レコードは以下を運びます: * `final: boolean` — **不在は不明を意味する。** レポーターが実際に数値を落ち着けたとき(例: post-SIVT 月末クローズ)のみ `true` を設定。予備レコード(日次ペーシングプッシュ、期間内進捗)には `false` を設定。`measurement_terms.billing_measurement` がこのレポーターを権威的と名指すとき、受信者は `final: false` または不在で請求してはならない(MUST NOT) — まず最終レコードを要求。`billing_measurement` なしの 3.0 スタイル使用と非メディアバイバリアント(signals、governance、creative、brand — 暫定状態概念のないドメイン)には、受信者は不在を最終として扱い既存動作を保持してもよい(MAY)。 * `finalized_at: string (date-time)` — `final: true` のとき、かつそのときのみ存在。 * `measurement_window: string` — バイの `billing_measurement.measurement_window` が設定されているとき設定すべき(SHOULD)、受信者が正しい段階に対して照合するように。 同じ `(account, media_buy_id, reporting_period)` が後で `final: true` でレポートされるとき、そのレコードは期間の任意の以前のレコードを置き換えます。 ### Webhook 最終性を行最終性から区別する `get_media_buy_delivery` webhook は `"final"` を含むトップレベル `notification_type` enum を運びます — これは **「これはキャンペーンの最後の予定された通知」** をシグナルし、含まれる行が請求に最終であることではありません。2 つの軸は独立: `notification_type: "final"` の webhook は依然として `is_final: false` の行を含むかも(例: C7 が落ち着く前のキャンペーン終了通知)。課金決定には常に行ごとの `is_final` をチェックしてください。 ## セラーが請求書をどう生成するか ### セラー証明(デフォルト) ``` billing_measurement absent, or vendor names seller's own ad server ``` セラーは契約された `measurement_window` について `is_final: true` の `get_media_buy_delivery` 行から請求します。バイヤーからの `report_usage` は不要。 ### バイヤー証明(3PAS) ``` billing_measurement.vendor.domain = "campaignmanager.google.com" (buyer's CM360) billing_measurement.max_variance_percent = 10 billing_measurement.measurement_window = "post_sivt" billing_measurement.finalization_deadline_hours = 240 // 10 days after post_sivt close ``` シーケンス: 1. バイヤーの CM360 が期間について post-SIVT を落ち着ける。 2. バイヤーのオペレーター — 実際には holdco プラットフォーム(例: Choreograph、Annalect、Acxiom)または CM360 エクスポートをラップするインハウスエージェンシーエンジニアリングチーム — がメディアバイレコードで `report_usage` を呼ぶ: `media_buy_id`、`impressions`、`vendor_cost`、`currency`、`final: true`、`finalized_at`、`measurement_window: "post_sivt"`。日次ペーシングプッシュ(使われる場合)は `final: false` を設定。落ち着いたファイルのみが `final: true` を設定。 3. セラーが `get_media_buy_delivery` からの自身の post-SIVT 行(`is_final: true`、同じ `measurement_window`)と比較。`|seller - buyer| / max ≤ max_variance_percent` なら、セラーはバイヤーの数値で請求。 4. 分散がしきい値を超えるなら、セラーは `makegood_policy.available_remedies` から救済を提案。 5. バイヤーが `finalization_deadline_hours` 内に最終レコードを公開しないなら、セラーは自身のセラー証明数値から請求し遅い最終化を `makegood_policy` の下の違反として扱ってもよい(MAY)。 > **今日の現実:** 月次 3PAS 照合はほとんど、セラーの AR チームにメールされた CSV/PDF 経由で帯域外で処理されます。このフローはワイヤー形式アップグレードです — 最初のアダプターは、CM360 / Flashtalking / Innovid エクスポートを `report_usage` でラップするエンジニアリングを持つ holdco オペレーターの可能性が高く、RTB SSP ではなく共感的な直接パブリッシャーと働きます。 ### ベンダー証明(名指しされたサードパーティ) ``` billing_measurement.vendor.domain = "nielsen.com" billing_measurement.max_variance_percent = 5 billing_measurement.measurement_window = "c7" billing_measurement.finalization_deadline_hours = 528 // 22 days after c7 close ``` 誰がベンダー関係を保持するかに応じて 2 つの運用パターン: * **セラーがベンダー関係を保持(一般的な CTV パターン)。** セラーは自身のスケジュールで Nielsen(NPower など)から C7 数値を引き、C7 ウィンドウがクローズプラス内部処理するとき `measurement_window: "c7"`、`is_final: true`、`finalized_at` 設定で `get_media_buy_delivery` に行として公開。バイヤーからの `report_usage` プッシュ不要。照合はセラーの行に対して起こる。 * **バイヤーがベンダー関係を保持。** バイヤー(またはそのオペレーター)がベンダーの権威数値をフェッチ — 例: エージェンシーの iSpot サブスクリプション、インハウス IAS ダッシュボードエクスポート — し、`final: true`、`finalized_at`、`measurement_window: "c7"` でバイのアカウントに対して `report_usage` 経由でプッシュ。ベンダー自体は AdCP を呼ばない。 `vendor.domain` BrandRef はコントラクトが誰を名指すかを識別し、誰が API を呼ぶかではありません。ベンダー自身の統合(NPower フィード、IAS API、DV pinnacle)は AdCP 範囲外です。 > **今日の現実:** RTB を実行する SSP は当面セラー証明であり続けます — 彼らの課金システムはバイヤー証明の請求基準のため設計されたことがなく、`max_variance_percent` プラス `makegood_policy` は彼らがそれをレトロフィットする十分な商業カバーではありません。ここでの最初の実際のアダプターは、Nielsen バックの保証が既に測定ベンダーのカウントで請求する CTV 直接セラー(例: Disney、NBCU、Paramount)です — 彼らにとってこの PR は既存の慣行を構造化された条件で記述するだけです。 ## 今日の不一致の解決 数値が `max_variance_percent` を超えて不一致のとき、AdCP 3.0–3.1 は以下に裏付けられた相手方間の帯域外解決を期待します: * バイの `makegood_policy.available_remedies` — セラーが事前コミットした救済のメニュー(追加配信、クレジット、請求書調整)。 * 両側の `finalized_at` タイムスタンプ付き最終レコード — 誰が何が最終と言ったか、いつかの完全な監査証跡。 * `create_media_buy` で捕捉された元の `committed_metrics` と `measurement_terms` — 当事者が同意したもの。 構造化紛争タスク — ワイヤー上で紛争を開き、`under_review` / `seller_proposed_adjustment` / `buyer_accepted` / `unresolved_arbitration` を通じて遷移させ、監査ログに解決を記録 — は AdCP 3.2 にターゲットされています。紛争に必要なデータ形状(最終レコード、証明、測定ウィンドウ、メイクグッドメニュー)は既に 3.1 でワイヤー上にあります。 ## 実例: バイヤー証明 3PAS 照合 ```json title="create_media_buy excerpt — measurement terms" theme={null} { "measurement_terms": { "billing_measurement": { "vendor": { "domain": "campaignmanager.google.com" }, "max_variance_percent": 10, "measurement_window": "post_sivt", "finalization_deadline_hours": 240 }, "makegood_policy": { "available_remedies": ["additional_delivery", "credit", "invoice_adjustment"] } } } ``` ```json title="get_media_buy_delivery — seller's final post-SIVT row" theme={null} { "media_buy_deliveries": [ { "media_buy_id": "mb_q1_2026", "is_final": true, "finalized_at": "2026-04-08T18:00:00Z", "by_package": [ { "package_id": "pkg_001", "is_final": true, "finalized_at": "2026-04-08T18:00:00Z", "measurement_window": "post_sivt", "impressions": 5120000, "spend": 51200 } ] } ] } ``` ```json title="report_usage — buyer's final 3PAS push" theme={null} { "idempotency_key": "f9b3...e2a1", "reporting_period": { "start": "2026-03-01T00:00:00Z", "end": "2026-03-31T23:59:59Z" }, "usage": [ { "account": { "account_id": "acct_acme_seller" }, "media_buy_id": "mb_q1_2026", "currency": "USD", "impressions": 5040000, "vendor_cost": 50400, "final": true, "finalized_at": "2026-04-09T14:32:00Z", "measurement_window": "post_sivt" } ] } ``` 分散: `|5120000 - 5040000| / 5120000 = 1.56%` — 10% しきい値を十分に下回る。セラーはバイヤーの 5.04M インプレッション × 合意されたレートで請求。両側が監査追跡可能な最終レコードを保持。 ## 関連 * [`measurement-terms`](https://adcontextprotocol.org/schemas/v3/core/measurement-terms.json) — スキーマ * [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) — セラー側最終性フラグ * [`report_usage`](/docs/accounts/tasks/report_usage) — バイヤー側 / サードパーティ最終性フラグ * [アカウンタビリティ](/docs/media-buy/advanced-topics/accountability) — パフォーマンス標準、メイクグッド救済、キャンセル * [レポートケイパビリティと測定ウィンドウ](/docs/media-buy/media-buys/optimization-reporting) # 概要 Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/advanced-topics/index ターゲティング、次元モデリング、セキュリティなど AdCP の高度な機能と実装詳細の解説。 本セクションは、プロトコルの高度な機能を深く理解する必要がある上級ユーザー向けに、AdCP の高度機能と実装詳細を解説します。 扱う高度なトピック: * **ターゲティングシステム** - 高度なオーディエンスおよびプレースメントターゲティング * **次元モデリング** - 分類とレポーティングのための統一システム * **セキュリティとアクセス制御** - マルチテナントのセキュリティと権限 * **実装の詳細** - アーキテクチャの判断と設計の根拠 * **開発ツール** - テストと開発高速化の手法 ## コアとなる高度概念 ### ターゲティング AdCP はブリーフ優先のターゲティングに、必要に応じて技術的なオーバーレイを組み合わせる思想です。 * **[Targeting](/docs/media-buy/advanced-topics/targeting)** - 地理的オーバーレイとリアルタイムシグナルを組み合わせたブリーフベースのターゲティング * **[Trusted Match Protocol (TMP)](/docs/trusted-match)** - ウェブ、モバイル、CTV、AI アシスタント、リテールメディアにまたがるパッケージアクティベーションのためのリアルタイム実行レイヤー。AXE を置き換えます。 * **[Agentic eXecution Engine (AXE)](/docs/media-buy/advanced-topics/agentic-execution-engine)** - 非推奨。TMP を参照してください。 このアプローチにより、自然言語での指定を可能にしつつ、コンプライアンスやテストの技術要件も満たします。 ### セキュリティ & アクセス制御 マルチテナント環境向けのエンタープライズ級セキュリティ: * **[Principals & Security](/docs/media-buy/advanced-topics/accounts-and-security)** - マルチテナントのセキュリティモデルとアクセス制御 * **[Policy Compliance](/docs/media-buy/media-buys/policy-compliance)** - コンプライアンスの自動チェックと強制 ### 実装アーキテクチャ 実装者向けの詳細技術情報: * **[Orchestrator Design](/docs/building/operating/orchestrator-design)** - AdCP オーケストレーターの技術アーキテクチャ ## 開発とテスト ### 開発ツール AdCP のテスト機能で開発を加速: * **[サンドボックスモード](/docs/media-buy/advanced-topics/sandbox)** - 実際のプラットフォームコールや支出なしに操作を実行 * メディア購入のライフサイクル全体をリスクなくテストするための **シミュレートされたレスポンス** ### パフォーマンス最適化 実装を理解し最適化するために: * **レスポンスタイムの目安**(処理タイプ別) * **キャッシュ戦略** による性能向上 * **スケーラビリティの考慮**(高トラフィック時) ## インテグレーションパターン ### マルチプラットフォームオーケストレーション 複数プラットフォームでキャンペーンを管理するパターン: * **状態同期** をプラットフォーム間で実施 * **統合レポート** を多様なデータソースから生成 * **クロスプラットフォーム最適化** の戦略 ### AI エージェント統合 AI エージェント実装のベストプラクティス: * **自然言語処理** によるブリーフ解釈 * **パフォーマンス学習** による最適化 * **エラーハンドリング** とリカバリー戦略 ## 高度なターゲティング ### レイヤードターゲティング AdCP のターゲティングは複数レイヤーで精緻化できます: 1. **Product-level targeting** - Built into product definitions 2. **Package-level overlays** - Additional targeting refinements 3. **Real-time signals** - Dynamic targeting adjustments 4. **Frequency management** - Cross-campaign frequency control ### ターゲティングの一貫性 AdCP のターゲティング手法はキャンペーン全体で一貫性を保ちます: * **Briefs** で探索時の意図を共有 * **Products** は定義の中にターゲティング能力を含みます * **Overlays** は必要に応じて地理的制約を追加 * **Signals** はリアルタイムのターゲティング判断を可能にします これにより、ターゲティングの意図とキャンペーン配信の整合性が保たれます。 1. **プロダクトレベルターゲティング** - プロダクト定義に組み込み 2. **パッケージレベルのオーバーレイ** - 追加のターゲティング精緻化 3. **リアルタイムシグナル** - 動的なターゲティング調整 4. **フリークエンシー管理** - キャンペーンをまたぐ接触頻度の管理 ## エンタープライズ機能 ### マルチテナントアーキテクチャ 代理店・エンタープライズ環境を支える機能: * データセキュリティのための **プリンシパル分離** * 必要に応じた **共有リソース** * 複雑な組織向けの **権限階層** ### コンプライアンスとガバナンス コンプライアンスを支える機能: * すべての操作の **監査証跡** * 重要な操作向けの **承認ワークフロー** * **データガバナンス** の制御とモニタリング ## 技術的な深掘り ### プロトコル設計思想 AdCP を支える基本原則: * AI ファーストのやり取りを実現する **MCP ベースのアーキテクチャ** * 実世界のタイミングに合わせた **非同期設計** * 必要に応じて **Human-in-the-loop** を組み込む * 普遍的な互換性をもたらす **プラットフォーム抽象化** ### パフォーマンス特性 システム性能を詳細に把握: * **レスポンスタイムのカテゴリ** と期待値 * **スケーラビリティの上限** と考慮事項 * **リソース最適化** の戦略 ## 次のステップ これらの高度な概念を実践するには: * 開発のベストプラクティスとして **[サンドボックスモード](/docs/media-buy/advanced-topics/sandbox)** を確認します * アーキテクチャの指針として **[Orchestrator Design](/docs/building/operating/orchestrator-design)** を参照します # Pricing models Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/advanced-topics/pricing-models title: 価格モデル description: CPM、CPCV、CPP、CPC、DOOH など柔軟な価格モデルに関する包括的ガイド keywords: \[pricing models, CPM, CPCV, CPP, CPC, CPV, GRP, video pricing, DOOH, share of voice, measurement] ------------------------------------------------------------------------------------------------------------ AdCP は多様な広告チャネルとビジネス目的に合わせ、複数の価格モデルをサポートしています。パブリッシャーが対応する価格モデルを宣言し、バイヤーが提示されたオプションから選択します。 ## パブリッシャーが宣言し、バイヤーが選ぶモデル ### 仕組み 1. パブリッシャーは商品内の `pricing_options` 配列で **価格オプションを宣言** する(各オプションには固有の `pricing_option_id` を付与) 2. バイヤーは `get_products` を通じて **利用可能なオプションを探索** します 3. バイヤーはメディアバイ作成時に `pricing_option_id` を指定し、**特定のオプションを選択** します 4. **配信計測** は宣言された `delivery_measurement` のプロバイダーに従う ### 主なメリット * **柔軟性**: 同じインベントリに複数の価格モデルを提示できます * **通貨サポート**: パブリッシャーが対応通貨を指定し、バイヤーはそれに合わせる * **市場標準**: 各チャネル(TV・動画・ディスプレイ・パフォーマンス)が自然な単価を利用できます * **期待値の明確化**: キャンペーン開始前に双方が価格へ合意できます ## 計測とソース・オブ・トゥルース ### 計測プロバイダーをソース・オブ・トゥルースとします **商品で計測プロバイダーを宣言し、バイヤーはそのプロバイダーを配信指標の正と認めます。** パブリッシャーは商品内で計測プロバイダーを指定します。 ```json theme={null} { "product_id": "premium_video", "delivery_measurement": { "provider": "Google Ad Manager with IAS viewability verification", "notes": "MRC-accredited viewability measurement. 50% in-view for 1 second (display) or 2 seconds (video)." } } ``` **一般的な計測プロバイダー:** * **アドサーバー**: Google Ad Manager、Freewheel、SpringServe * **アテンション計測**: Adelaide、Lumen、TVision * **サードパーティ検証**: IAS、DoubleVerify、Scope3 * **TV/オーディオ計測**: Nielsen、Comscore、iSpot.tv、Triton Digital * **DOOH**: Geopath、Vistar、Place Exchange 商品を受け入れることで、バイヤーは宣言された計測プロバイダーを配信指標の正として同意します。 ### 計測条件とパフォーマンス基準 プロバイダーの宣言にとどまらず、セラーはプロダクトに構造化された条件を公開でき、バイヤーは購入作成時にそれらを交渉できます。これらは二つの別個の関心事です: * **`measurement_terms`** — 誰が課金メトリクスを数え、閾値が破られたときに何が起きるか(メイクグッド) * **`performance_standards`** — どのレート閾値が適用されるか(ビューアビリティ、IVT、完了、ブランドセーフティ、アテンション) ```json theme={null} { "product_id": "premium_guaranteed_video", "delivery_measurement": { "provider": "Google Ad Manager with DoubleVerify verification", "notes": "MRC-accredited viewability. DV IVT filtering enabled." }, "measurement_terms": { "billing_measurement": { "vendor": { "domain": "admanager.google.com" }, "max_variance_percent": 10 }, "makegood_policy": { "available_remedies": ["additional_delivery", "credit", "invoice_adjustment"] } }, "performance_standards": [ { "metric": "viewability", "threshold": 0.70, "standard": "mrc", "vendor": { "domain": "doubleverify.com" } }, { "metric": "ivt", "threshold": 0.05, "vendor": { "domain": "doubleverify.com" } }, { "metric": "completion_rate", "threshold": 0.80, "vendor": { "domain": "doubleverify.com" } } ] } ``` **計測条件のフィールド:** * **`billing_measurement`** — どのベンダーによる課金メトリクス(`pricing_model` が決定)のカウントが請求を支配するか。`max_variance_percent` は、非課金側のカウントの乖離が解決を引き起こす閾値を定義します。 * **`makegood_policy`** — 任意のパフォーマンス基準または請求の差異が破られたときに利用可能な、閉じた救済メニュー。三つの救済タイプ: `additional_delivery`(インプレッションの延長/追加、同等物)、`credit`(将来の購入への充当)、`invoice_adjustment`(現在の請求書の減額)。セラーがこのメニューから提案し、バイヤーが受諾または異議を申し立てます。 **パフォーマンス基準のフィールド:** `performance_standards` 配列の各エントリは以下を指定します: * **`metric`** — 何を計測するか: `viewability`、`ivt`、`completion_rate`、`brand_safety`、`attention_score` * **`threshold`** — 小数(0〜1)のレート。これが下限か上限かはメトリクスによります: viewability、completion\_rate、brand\_safety、attention\_score は下限(超えなければならない)、ivt は上限(超えてはならない)。 * **`standard`** — ビューアビリティでは必須(`"mrc"` または `"groupm"`)。他のメトリクスでは省略。 * **`vendor`** — 誰が計測するか。ドメインで識別されます。確定パッケージで合意された場合、クリエイティブは指定ベンダーのトラッカーアセットを含まなければなりません(MUST)。 **交渉のフロー:** 1. セラーがプロダクトに `measurement_terms` と `performance_standards` を公開(デフォルト) 2. バイヤーが `create_media_buy` のパッケージリクエストで上書きを提案 3. セラーが受諾(確定パッケージでエコー)、拒否(`TERMS_REJECTED`)、または調整(変更した条件を返す) 4. 閾値が破られた場合、セラーが合意済みの `makegood_policy` から救済を提案し、バイヤーが受諾または異議を申し立てる バイヤーがこれらのフィールドを省略した場合、プロダクトのデフォルトが適用されます。 ### キャンセルポリシー 保証プロダクトは、キャンセルが有効になるまでに必要な最短通知期間を持つ `cancellation_policy` を宣言できます: ```json theme={null} { "product_id": "premium_guaranteed_video", "delivery_type": "guaranteed", "cancellation_policy": { "notice_period": { "interval": 30, "unit": "days" }, "cancellation_fee": { "type": "percent_remaining", "rate": 0.5 } } } ``` **キャンセル料のタイプ:** * **`percent_remaining`** — 残りのコミット済み支出額に対する割合(`rate` が必要。例: 50% なら `0.5`) * **`full_commitment`** — 配信に関わらず、バイヤーはコミット済み予算全額を負う * **`fixed_fee`** — 定額の金額(購入の通貨で `amount` が必要) * **`none`** — キャンセル料なし(通知ありのキャンセルは無料) `measurement_terms` と異なり、キャンセルポリシーは交渉のサーフェスではありません——セラーが宣言し、バイヤーはそのプロダクトに対してメディアバイを作成することで受諾します。十分な通知なくキャンセルされた保証付き購入には、宣言されたキャンセル料が発生します。 ### ベストプラクティス **パブリッシャー向け:** * 計測プロバイダー(アドサーバーと第三者検証を含む)を明確に示します * 計測方法を平易な言葉で説明します * DOOH ではオーディエンス計測ソース(Geopath、会場センサーなど)を明記します **バイヤー向け:** * 予算確定前に計測プロバイダーを確認します * キャンペーン要件に合致しているか確認します * 必要に応じて契約で監査権を交渉します ## サポートされる価格モデル ### CPM (Cost Per Mille) **インプレッション 1,000 件あたりのコスト**。従来のディスプレイ広告の価格体系。 **ユースケース**: ディスプレイ、ネイティブ、バナー広告 **例**: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/cpm-option.json", "pricing_option_id": "cpm_usd_guaranteed", "pricing_model": "cpm", "fixed_price": 12.50, "currency": "USD", "min_spend_per_package": 5000 } ``` **課金**: 配信された広告インプレッション 1,000 件ごとに課金 *** ### vCPM (Viewable Cost Per Mille) **ビューアブルインプレッション 1,000 件あたりのコスト**。MRC のビューアビリティ基準を満たすインプレッションのみに支払う。 **ユースケース**: ビューアビリティ保証付きのディスプレイ、ネイティブ、動画広告 **ビューアビリティ基準**(MRC 標準): * **ディスプレイ広告**: ピクセルの 50% が 1 秒以上表示 * **動画広告**: ピクセルの 50% が 2 秒以上表示 **例**: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/vcpm-option.json", "pricing_option_id": "vcpm_usd_guaranteed", "pricing_model": "vcpm", "fixed_price": 18.50, "currency": "USD", "min_spend_per_package": 5000 } ``` **課金**: ビューアブルインプレッション 1,000 件ごとに課金(MRC のビューアビリティ閾値を満たすインプレッション)。ビューアビリティは宣言された計測プロバイダーで測定。 **計測要件**: パブリッシャーは商品の `delivery_measurement` フィールドでビューアビリティ計測プロバイダーを宣言すること。一般的なプロバイダーには IAS、DoubleVerify、MOAT、Google Active View などがあります。 *** ### CPCV (Cost Per Completed View) **動画/オーディオの 100% 完視聴あたりのコスト**。完全視聴された場合のみ支払う。 **ユースケース**: 動画キャンペーン、オーディオ広告、プレロール動画 **例**: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/cpcv-option.json", "pricing_option_id": "cpcv_usd_guaranteed", "pricing_model": "cpcv", "fixed_price": 0.15, "currency": "USD" } ``` **課金**: 視聴者が動画/オーディオ広告を 100% 再生した場合にのみ課金。完視聴は宣言された計測プロバイダーで測定。 *** ### CPV (Cost Per View) **閾値到達ごとの視聴単価**。視聴者がパブリッシャー定義の閾値に達したときに課金。 **ユースケース**: 短い完了要件がある動画キャンペーン **例**: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/cpv-option.json", "pricing_option_id": "cpv_usd_50pct", "pricing_model": "cpv", "fixed_price": 0.08, "currency": "USD", "parameters": { "view_threshold": 0.5 } } ``` **課金**: 視聴者が閾値に達したときに課金(例: 50% 完了、30 秒到達など) **パラメーター**: * `view_threshold`: 0.0〜1.0 の小数(例: 0.5 = 50% 完了) *** ### CPP (Cost Per Point) **Gross Rating Point(GRP) あたりのコスト**。従来の TV/ラジオの指標。 **ユースケース**: CTV、リニア TV、ラジオ、オーディオストリーミング **例**: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/cpp-option.json", "pricing_option_id": "cpp_usd_a18-49", "pricing_model": "cpp", "fixed_price": 250.00, "currency": "USD", "parameters": { "demographic": "A18-49", "min_points": 50 }, "min_spend_per_package": 12500 } ``` **課金**: 対象デモグラフィックに対して配信された rating point ごとに課金 **パラメーター**: * `demographic`: 対象デモグラフィック(例: "A18-49"、"W25-54"、"M35+") * `min_points`: 必須の最小 GRP コミットメント **報告されるメトリクス**: * `grps`: 配信された合計 Gross Rating Points * `reach`: 到達したユニーク人数 * `frequency`: 一人当たりの平均接触頻度 **計測要件**: CPP 価格では **認定されたデモグラフィック計測** が必要です。パブリッシャーは計測プロバイダーを宣言してください。 ```json theme={null} { "pricing_model": "cpp", "fixed_price": 250.00, "delivery_measurement": { "provider": "Nielsen DAR", "notes": "Panel-based demographic measurement for A18-49. GRP reports available weekly." } } ``` **CPP 向けの一般的な計測プロバイダー**: * **Nielsen DAR/TV**: 業界標準の TV 計測 * **Comscore**: CTV 向け Campaign Ratings * **iSpot.tv**: 高度な TV 分析 * **Triton Digital**: オーディオ/ストリーミング計測 バイヤーは CPP 取引を受ける前に、計測プロバイダーがキャンペーン要件に合致するか確認してください。 *** ### CPC (Cost Per Click) **クリック単価**。エンゲージメントを目的としたパフォーマンス型の価格。 **ユースケース**: ダイレクトレスポンス、検索広告、ソーシャル広告 **例**: ```json theme={null} { "pricing_model": "cpc", "currency": "USD", "floor_price": 0.50, "price_guidance": { "p50": 1.20, "p75": 2.00 } } ``` **課金**: ユーザーが広告をクリックした場合のみ課金 *** ### CPA (Cost Per Acquisition) **コンバージョンイベントあたりのコスト** - 定義されたコンバージョンが発生したときに広告主が支払います。 **ユースケース**: リテールメディア(注文ごとの支払い)、リード獲得、アプリインストールキャンペーン、コマースメディア **例**: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/cpa-option.json", "pricing_option_id": "cpa_usd_purchase", "pricing_model": "cpa", "event_type": "purchase", "fixed_price": 5.00, "currency": "USD" } ``` **課金**: 指定された `event_type` が発火したときに固定価格が課金されます。価格オプションは何のイベントが課金を引き起こすかを宣言します——これは配信の最適化を制御する `optimization_goals` とは独立です。 **パラメーター**: * `event_type`(必須): 課金を引き起こすコンバージョンイベント。標準のイベントタイプ列挙を使用します(例: `purchase`、`lead`、`app_install`、`add_to_cart`、有料サブスクリプションには `subscribe`、無料の継続的オプトインには `follow`)。 * `event_source_id`(任意): 存在する場合、この特定のソースからのイベントのみが課金の対象になります。`sync_event_sources` で設定されたイベントソースと一致しなければなりません。省略時は、指定された `event_type` の任意のイベントが対象になります。 **例**(イベントソースごとに異なるレート): ```json theme={null} { "pricing_options": [ { "pricing_option_id": "cpa_online_purchase", "pricing_model": "cpa", "event_type": "purchase", "event_source_id": "website_pixel", "fixed_price": 5.00, "currency": "USD" }, { "pricing_option_id": "cpa_instore_purchase", "pricing_model": "cpa", "event_type": "purchase", "event_source_id": "instore_attribution", "fixed_price": 3.00, "currency": "USD" } ] } ``` **価格設定 vs 最適化**: CPA 価格オプションの `event_type`(何が課金を引き起こすか)は、パッケージの `optimization_goals`(プラットフォームが配信を何に向けて最適化するか)とは独立です。例えば、パッケージは `lead` イベントで CPA 価格を使いつつ、購入イベントを含む `event_sources` と `target: { kind: "per_ad_spend", value: 4.0 }` を持つイベント目標を設定できます——課金はリードで発火しますが、配信は下流の購入リターンに向けて最適化されます。 **返金と調整**: 返金の扱いとコンバージョン調整のポリシーは、バイヤーとセラーの間の商業的条件です。プロトコルは、返金されたコンバージョンのクローバックや請求クレジットを規定しません。 **注記**: CPA は、別個の「CPO」(Cost Per Order)や「CPL」(Cost Per Lead)の価格モデルの必要性を置き換えます。セラーは同じプロダクトで、異なるイベントタイプ、イベントソース、価格を持つ複数の CPA オプションを提供できます。 *** ### Flat Rate **固定費**。配信ボリュームに関係なく一括で支払う。 **ユースケース**: スポンサーシップ、テイクオーバー、独占枠、ブランドコンテンツ **例**: ```json theme={null} { "pricing_model": "flat_rate", "fixed_price": 50000.00, "currency": "USD" } ``` **課金**: キャンペーン期間全体に対して固定費を支払う *** ### Time (Cost Per Time Unit) **時間単位あたりのコスト** - レートがキャンペーン期間に応じてスケールし、セルフサーブのスポンサーシップを可能にします。 **ユースケース**: ホームページのテイクオーバー、セクションのスポンサーシップ、価格が予約期間に依存するプレミアムプレースメント **例**: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/time-option.json", "pricing_option_id": "time_usd_daily", "pricing_model": "time", "fixed_price": 50000.00, "currency": "USD", "parameters": { "time_unit": "day", "min_duration": 1, "max_duration": 30 } } ``` **課金**: コスト = `fixed_price` × キャンペーンフライト内の `time_unit` 数。例えば、1 日 \$50,000 の 3 日間キャンペーンは合計 \$150,000 です。 **パラメーター**: * `time_unit`(必須): `"hour"`、`"day"`、`"week"`、`"month"` * `min_duration`(任意): 時間単位での最短予約期間 * `max_duration`(任意): 時間単位での最長予約期間 **時間単位の計算**: | Time Unit | Calculation | | --------- | ------------------- | | `hour` | レート × フライト内の時間数 | | `day` | レート × フライト内の暦日数 | | `week` | レート × 週数(丸めはセラー定義) | | `month` | レート × 月数(日割りはセラー定義) | **Flat Rate との比較**: | Aspect | Flat Rate | Time | | ----------------- | ----------- | --------------- | | 意味論 | 固定の総コスト | レート × 期間 | | `fixed_price` の意味 | キャンペーンの総コスト | 時間単位あたりのコスト | | バイヤーの柔軟性 | 期間を交渉する必要あり | 任意の期間をセルフサーブ | | ユースケース | 固定のスポンサーシップ | スケーラブルなスポンサーシップ | *** ## Digital Out-of-Home (DOOH) の価格 DOOH 広告では既存の価格モデル(主に **CPM** または **flat\_rate**)を用い、在庫の割り当てを説明するための任意パラメーターを追加します。 ### 基本概念 * **DOOH 向け CPM**: 会場トラフィック(例: Geopath データ)に基づいてインプレッションを算出し、1,000 インプレッション単位で価格を設定 * **DOOH 向け定額**: 特定の期間または配分(時間・日単位、独占テイクオーバーなど)に対して固定費を設定 ### 簡単な例: ビルボードのテイクオーバー ```json theme={null} { "product_id": "billboard_takeover", "name": "Premium Billboard - 24 Hour Takeover", "pricing_options": [{ "pricing_model": "flat_rate", "fixed_price": 50000.00, "currency": "USD" }], "delivery_measurement": { "provider": "Geopath", "notes": "Venue traffic data updated monthly. Estimated 2.5M impressions over 24 hours." } } ``` ### DOOH パラメーター(任意) パブリッシャーは DOOH の配分を説明するため、追加パラメーターを含めてもよい。 * `duration_hours`: 時間ベース料金のための配信時間 * `sov_percentage`: 音声シェア(利用可能な再生枠の %) * `daypart`: 特定の時間帯(例: "morning\_commute") * `venue_package`: 画面のまとまったパッケージ名称 **注意**: DOOH の計測やバイイング慣行は市場によって異なります。パブリッシャーは商品説明と `delivery_measurement` フィールドで、計測方法と在庫配分を明確に説明してください。 *** ## 価格の内訳(Price Breakdown) バイヤーとセラーが値引きやコミッションを交渉した場合、`price_breakdown` オブジェクトが、`fixed_price` がレートカードからどう導出されたかを開示します。これは任意です——内訳を必要としないセラーは丸ごと省略でき、既存の実装へのオーバーヘッドはゼロです。 `price_breakdown` はプランニングレイヤーの構成物です。AdCP 内の価格オプションと確定パッケージに存在します——インプレッションレベルの OpenRTB の入札リクエストやレスポンスには伝播しません。 ### 構造 ```json theme={null} { "pricing_option_id": "cpm_eur_premium", "pricing_model": "cpm", "fixed_price": 11.90, "currency": "EUR", "price_breakdown": { "list_price": 14.00, "adjustments": [ { "kind": "discount", "name": "volume", "rate": 0.15, "description": "12x frequency discount" }, { "kind": "commission", "name": "agency", "rate": 0.15, "description": "Agency commission" }, { "kind": "settlement", "name": "cash_discount", "rate": 0.02, "description": "2% discount for payment within 10 days" } ] } } ``` * `list_price` — 調整前のレートカードまたは基本価格 * `adjustments` — 順序付けられた調整のリスト。各項目は `kind` で分類されます * `fixed_price`(親の価格オプション上)— `list_price` にすべての `fee` と `discount` の調整を順に適用した結果と等しくなければなりません 実装は、前方互換性のため `price_breakdown` とその調整における認識できないフィールドを無視すべきです。 ### 調整の種別 調整は、その経済的効果に基づいて四つの種別に分かれます: | Kind | Effect on buyer price | Effect on publisher revenue | When applied | | ------------ | --------------------- | --------------------------- | --------------------------------------------- | | `fee` | 増やす | 増やす | 見積もり前——`list_price` を `fixed_price` に向けて引き上げる | | `discount` | 減らす | 減らす | 見積もり前——`list_price` を `fixed_price` に向けて引き下げる | | `commission` | なし——予算に含まれる | 減らす(収益分配) | 仲介者とパブリッシャー間の収益配分 | | `settlement` | なし——請求書発行後 | 実際の支払いを減らす | 請求または支払いの時点 | **Fee** はバイヤーの支払額を増やします。アドサービング料、データ/ターゲティングの追加料金、ブランドセーフティ検証のコストは基本価格に加算されます。fee の調整がなければ、これらは `list_price` の中で不透明になります。 **Discount** はバイヤーの支払額を減らします。ボリュームディスカウント、交渉レート、早期予約割引はいずれもバイヤーのコストとパブリッシャーの収益の両方を下げます。 **Commission** は収益分配です。バイヤーの価格と予算は変わりません——コミッションは、支払いが仲介者(例: エージェンシー)とパブリッシャーの間でどう分割されるかを決めます。予算は常にコミッション込みで管理されます。複数のコミッション調整は、ディスカウントと同様に順次複合します——例えば、15% のエージェンシーコミッションに続く 5% のトレーディングデスク手数料は、パブリッシャーが `budget × 0.85 × 0.95` を受け取ることを意味します。 **Settlement の調整**は請求または支払いの時点で適用されます(例: 早期支払いに対する現金割引)。コミット済みの価格や予算には影響しません。遡及的なリベートやパフォーマンスインセンティブは `price_breakdown` の対象外であり、照合のワークフローで扱われます。 ### 受益者(Beneficiary) 各調整は、調整の価値を誰が受け取るかを識別する任意の `beneficiary` フィールドを含められます。これは複数の仲介者が連なるチェーンにおけるコミッション調整で最も有用です: ```json theme={null} { "kind": "commission", "name": "agency", "rate": 0.15, "beneficiary": "mediaagency.example.com" }, { "kind": "commission", "name": "trading_desk", "rate": 0.05, "beneficiary": "tradingdesk.example.com" } ``` 値は sellers.json のドメイン、AdCP のアカウント ID、または人が読める当事者名にできます。 ### 不変条件 不変条件は次のとおりです: `list_price` にすべての `fee` と `discount` の調整を順に適用したものが `fixed_price` と等しい。コミッションと settlement の調整は関与しません——それらは透明性のために開示されます。fee や discount の調整が存在しない場合、`fixed_price` は `list_price` と等しくなければなりません。 この不変条件は、親オブジェクトに `fixed_price` が存在する場合にのみ適用されます。オークションベースのパッケージ(`bid_price` のみが存在する)では、`price_breakdown` は情報提供です——[確定パッケージ上](#on-confirmed-packages)を参照してください。 fee と discount の調整は、配列の順序で次の式を使って複合します: ``` For rate-based fees: running = running × (1 + rate) For amount-based fees: running = running + amount For rate-based discounts: running = running × (1 − rate) For amount-based discounts: running = running − amount ``` すべての金銭的な値は、各ステップで通貨の精度(例: EUR/USD は小数点以下 2 桁)に丸められます。不変条件は丸めの後に成立します。 混在した調整の例: ``` list_price: 12.00 fee, amount: 2.00 → 12.00 + 2.00 = 14.00 discount, rate: 0.15 → 14.00 × 0.85 = 11.90 fixed_price: 11.90 ✓ ``` 上記の構造の例では: 14.00 × (1 − 0.15) = 11.90 ✓(その例に fee の調整はありません) ### Rate と Amount 各調整は `rate` または `amount` のちょうど一方を含まなければなりません: * `rate` — 小数の割合。0 より大きく 1 より小さい(0 \< rate \< 1)。例: 15% なら 0.15 * `amount` — 価格オプションの通貨での正の数(> 0) ```json theme={null} { "kind": "discount", "name": "volume", "rate": 0.15 } { "kind": "discount", "name": "negotiated", "amount": 2.00, "description": "Flat rate reduction" } ``` ### 予算とコミッション 予算は常に `fixed_price` のレベルで、コミッション込みで建てられます。バイヤーが €11.90 CPM で €10,000 をコミットする場合、その €10,000 がバイヤーのコストです。エージェンシーはその金額からコミッションを取り、パブリッシャーは残りを受け取ります。 これは、セラー間でレートを比較するバイヤーエージェントが `fixed_price` を直接使えることを意味します——基盤となるコミッション構造に関わらず、それが実際の単位あたりコストです。 ### 確定パッケージ上 `price_breakdown` フィールドはパッケージレスポンス(確定したラインアイテム)にも現れます。これはセラーが埋めるものであり、バイヤーエージェントは読み取り専用として扱うべきです。 **固定価格のパッケージ**は価格オプションの内訳をエコーします: ```json theme={null} { "package_id": "pkg_12345", "product_id": "premium_display", "pricing_option_id": "cpm_eur_premium", "fixed_price": 11.90, "budget": 10000, "price_breakdown": { "list_price": 14.00, "adjustments": [ { "kind": "discount", "name": "volume", "rate": 0.15, "description": "12x frequency discount" }, { "kind": "commission", "name": "agency", "rate": 0.15, "description": "Agency commission" }, { "kind": "settlement", "name": "cash_discount", "rate": 0.02, "description": "2% for payment within 10 days" } ] } } ``` **オークションベースのパッケージ**は、`price_breakdown` を使ってクリアリング価格に対するコミッションと settlement の条件を開示します。オークションのパッケージでは、開示すべきレートカードの導出がないため、`list_price` はクリアリング価格に設定されます。ディスカウントの不変条件は適用されません——内訳は情報提供のみです: ```json theme={null} { "package_id": "pkg_67890", "product_id": "premium_display", "pricing_option_id": "cpm_eur_auction", "bid_price": 12.50, "budget": 10000, "price_breakdown": { "list_price": 12.50, "adjustments": [ { "kind": "commission", "name": "agency", "rate": 0.15, "description": "Agency commission" }, { "kind": "settlement", "name": "cash_discount", "rate": 0.02, "description": "2% for payment within 10 days" } ] } } ``` *** ## 適用可能な調整(Eligible Adjustments) パブリッシャーは `eligible_adjustments` を使って、どの調整種別が価格オプションに適用されるかを宣言できます。これにより、交渉が始まる前に、ディスカウント、コミッション、settlement の条件が利用可能かどうかをバイヤーエージェントに事前に伝えられます。 ```json theme={null} { "pricing_option_id": "cpm_eur_standard", "pricing_model": "cpm", "fixed_price": 14.00, "currency": "EUR", "eligible_adjustments": ["fee", "discount", "commission", "settlement"] } ``` `eligible_adjustments` が存在する場合、バイヤーはどの種別の調整を期待または要求すべきかを知ります。存在しない場合、事前に宣言された調整はありません——バイヤーは、既に適用された調整があるかどうか `price_breakdown`(存在する場合)を確認すべきです。 このフィールドは `price_breakdown` と対になります: `eligible_adjustments` は何が*可能か*を示し、`price_breakdown` は何が*適用されたか*を示します。 *** ## 複数通貨サポート パブリッシャーは同一の商品を複数通貨で提供できます。 ```json theme={null} { "product_id": "premium_video", "pricing_options": [ { "pricing_option_id": "cpm_usd_guaranteed", "pricing_model": "cpm", "fixed_price": 45.00, "currency": "USD" }, { "pricing_option_id": "cpm_eur_guaranteed", "pricing_model": "cpm", "fixed_price": 40.00, "currency": "EUR" }, { "pricing_option_id": "cpm_gbp_guaranteed", "pricing_model": "cpm", "fixed_price": 35.00, "currency": "GBP" } ] } ``` **バイヤーの責務**: パブリッシャーがサポートする通貨を選択する必要があります。 ## 固定価格とオークション価格 ### 固定価格(`fixed_price` がある場合) * パブリッシャーが固定価格を設定 * 価格が保証され予測可能 * 保証インベントリで一般的 * `fixed_price` フィールドを含めます ### オークション価格(`fixed_price` がない場合) * 最終価格はオークションで決定 * パブリッシャーは `floor_price`(下限)と `price_guidance`(パーセンタイルの目安)を提示 * 非保証インベントリで一般的 * バイヤーはメディアバイリクエストで `bid_price` を送信 **オークションの例**: ```json theme={null} { "pricing_option_id": "cpcv_usd_auction", "pricing_model": "cpcv", "currency": "USD", "floor_price": 0.08, "price_guidance": { "p25": 0.10, "p50": 0.12, "p75": 0.15, "p90": 0.18 } } ``` ## バイヤーの選択プロセス 各パッケージは独自の価格オプションを指定し、それが通貨と価格モデルを決定します。 ```json theme={null} { "buyer_ref": "campaign_001", "start_time": "2025-01-01T00:00:00Z", "end_time": "2025-01-31T23:59:59Z", "brand_manifest": { "name": "Acme Corp", "url": "https://acmecorp.com" }, "brief": "Q1 Brand Campaign", "packages": [{ "buyer_ref": "pkg_ctv", "product_id": "premium_ctv", "format_ids": [{"agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s"}], "pricing_option_id": "cpcv_usd_auction", "budget": 50000, "pacing": "even", "bid_price": 0.16 }] } ``` **流れ:** 1. パッケージが商品から `pricing_option_id`(例: "cpcv\_usd\_auction")を選択 2. 価格オプションが通貨・価格モデル・固定かオークションかを決定 3. パッケージの `budget` はその価格オプションの通貨で指定 4. オークション型価格では `bid_price` が必須 5. セラーはパッケージ間で通貨の整合性を検証 ## 価格モデル別のレポート指標 価格モデルによって主要指標が異なります。 | Pricing Model | Primary Metric | Secondary Metrics | | ------------- | --------------------- | ------------------------------------- | | CPM | impressions | clicks, ctr, spend | | vCPM | viewable\_impressions | impressions, viewability\_rate, spend | | CPCV | completed\_views | impressions, completion\_rate, spend | | CPV | views | impressions, quartile\_data, spend | | CPP | grps | reach, frequency, spend | | CPC | clicks | impressions, ctr, spend | | Flat Rate | N/A | impressions, reach, frequency | ## 例: 複数モデルを持つ CTV 商品 複数の価格オプションを持つ CTV インベントリを提供する例。 ```json theme={null} { "product_id": "ctv_premium_sports", "name": "Premium Sports CTV", "description": "High-engagement sports content on CTV devices", "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_15s" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s" } ], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "cpm_usd_guaranteed", "pricing_model": "cpm", "fixed_price": 55.00, "currency": "USD", "min_spend_per_package": 15000 }, { "pricing_option_id": "cpcv_usd_guaranteed", "pricing_model": "cpcv", "fixed_price": 0.22, "currency": "USD", "min_spend_per_package": 15000 }, { "pricing_option_id": "cpp_usd_m18-49", "pricing_model": "cpp", "fixed_price": 300.00, "currency": "USD", "parameters": { "demographic": "M18-49", "min_points": 50 }, "min_spend_per_package": 15000 } ] } ``` バイヤーは TV バイイングを計画する場合は CPP、エンゲージメント最適化なら CPCV、リーチ重視なら CPM を選択できます。 ## 交渉ベースとプレミアムの価格設定 プレミアムなセラー——特に OOH、CTV スポンサーシップ、直接取引——は、固定レートを公開するのではなく交渉を通じて価格を確定することが多いです。AdCP は、別個の見積もりや price-on-request の仕組みを必要とせず、既存のブリーフ→プロポーザルの経路を通じてこのワークフローをサポートします。 ### セラーはホールセール価格の公開を要求されない プロダクトディスカバリー(`get_products`)は、セラーにホールセール価格やクリアリング価格の開示を要求しません。セラーは、公開の価格詳細が限定的またはまったくないプロダクトを公開してよい——例えば、オーディエンスとフォーマットの情報はあるが `pricing_options` がない CTV スポンサーシッププロダクト。プロダクトは発見可能ですが、バイヤーは実際の価格を含むプロポーザルを受け取るためにブリーフを提出しなければなりません。 ### ブリーフへの選択的な応答 セラーは、どのブリーフに応答するか、どの価格を提示するかを制御します。取引しないと決めた在庫についてブリーフを受け取った場合、プロトコルネイティブな選択肢は次のとおりです: * **プロポーザルからプロダクトを完全に省略する** — バイヤーはその在庫についてプロポーザルを見ず、このリクエストでは利用不可であることが示されます * **`PROPOSAL_NOT_COMMITTED` を返す** — セラーがブリーフを検討したが、現時点でコミット可能なプロポーザルを提示していないという明示的なシグナル いずれも、セラーが辞退した理由の開示を要求しません。これは「交渉なしでは価格を提示できない」のプロトコルネイティブな等価物です。 ### 既存のフローが交渉ベースの取引をカバーする 交渉価格の在庫に対する標準的な経路は次のとおりです: 1. **セラーがプロダクトを公開** — 記述的なメタデータ(オーディエンス、フォーマット、プロパティ)を持つが、ホールセール価格は持たない 2. **バイヤーがブリーフを提出** — `get_products` を通じて、キャンペーンの目標と予算のパラメータを記述 3. **セラーがプロポーザルを返す** — 特定のブリーフに合わせた交渉価格とともに——または提案を辞退 4. **バイヤーがリファインまたは受諾** — その後 `proposal_id` を `create_media_buy` に渡す これは今日のプレミアムな直接取引の仕組みを反映しています: セラーがブリーフを見て、関与するかどうかを決め、その機会に固有の条件を提示します。 ## ベストプラクティス ### パブリッシャー向け 1. **関連性の高い価格モデルを提供する** - 在庫タイプとバイヤーの期待に合わせる 2. **適切な最低条件を設定する** - `min_spend_per_package` でキャンペーン成立性を確保 3. **価格の目安を提示する** - オークション価格では現実的な下限とパーセンタイルデータを示します 4. **複数通貨を検討する** - ターゲット市場の通貨に対応します 5. **パラメーターを明文化する** - 閾値、デモグラフィック、アクション種別を明確に説明 ### バイヤー向け 1. **適切なモデルを選ぶ** - キャンペーン目的に合った価格を選択 2. **通貨を合わせる** - パブリッシャーが対応する通貨を選ぶ 3. **現実的な予算を設定する** - 最低出稿要件を考慮します 4. **目標と価格を整合させる** - 価格モデルに合った配信目標を設定 5. **関連指標をモニタリングする** - 価格モデルに直結する指標に注力 ## 関連ドキュメント * [Media Products](/docs/media-buy/product-discovery/media-products) - 商品モデルのリファレンス * [Creating Media Buys](/docs/media-buy/task-reference/create_media_buy) - バイイング時の価格選択方法 * [Delivery Reporting](/docs/media-buy/task-reference/get_media_buy_delivery) - 価格モデル別の指標の見方 * [Glossary](/docs/reference/glossary) - 価格と指標の用語集 # サンドボックスモード Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/advanced-topics/sandbox AdCP サンドボックスモード — シミュレートされたデータで商品発見、キャンペーン作成、クリエイティブ、配信をテストします。実際の支出やプロダクションへの副作用なし。 ## 概要 サンドボックスモードを使用すると、バイヤーは実際のプラットフォームコールや実際の費用なしに、メディア購入のライフサイクル全体 — 発見、キャンペーン作成、クリエイティブ、配信 — をテストできます。レスポンスにはシミュレートされたが現実的なデータが含まれます。 サンドボックスはリクエストごとではなく**アカウントレベル**で機能します。リクエストがサンドボックスアカウントを参照すると、リクエスト全体がサンドボックスとして扱われる。これにより、マルチステップフローで実際のトラフィックとテストトラフィックを誤って混在させるリスクを排除します。 ## ケイパビリティの発見 セラーは `get_adcp_capabilities` でサンドボックスサポートを宣言する: ```json theme={null} { "account": { "sandbox": true } } ``` サンドボックスモードを使用する前にこれを確認します。`account.sandbox` が宣言されていないか `false` の場合、セラーはサンドボックスをサポートしていません。 ## サンドボックスへの2つの経路 サンドボックスモードへの入り方は、セラーのアカウントモデル(`require_operator_auth`)によって異なります。2つの経路はまったく異なる — 正しい方に従うようにすること。 ### バイヤー宣言アカウント(`require_operator_auth: false`) セラーはエージェントを信頼し、オペレーターごとの認証を必要としません。サンドボックスは**ナチュラルキー**の一部だ — 同じブランド/オペレーターのペアがプロダクションとサンドボックスの両方のアカウントを持つことができ、`sandbox: true` で区別されます。 **セットアップ:** アカウントエントリに `sandbox: true` を付けて `sync_accounts` でサンドボックスアカウントを宣言する: ```json theme={null} // sync_accounts — サンドボックスアカウントを宣言する { "accounts": [{ "brand": { "domain": "acme-corp.com" }, "operator": "acme-corp.com", "billing": "operator", "sandbox": true }] } ``` **使用方法:** すべてのリクエストで `sandbox: true` を付けたナチュラルキーでサンドボックスアカウントを参照する: ```json theme={null} // get_products — バイヤー宣言サンドボックス { "account": { "brand": { "domain": "acme-corp.com" }, "operator": "acme-corp.com", "sandbox": true }, "brief": "Premium CTV inventory for Q2 campaign" } ``` ### アカウント ID 名前空間(`require_operator_auth: true`) セラーは各オペレーターが直接認証することを要求します。サンドボックスアカウントは**セラーのプラットフォーム上の既存のテストアカウント**だ — Stripe のテストモード、Google Ads サンドボックスアカウント、Snap のテスト広告主アカウントのようなもの。作成するのではなく、発見します。 **セットアップ:** `sandbox: true` フィルターを使って `list_accounts` でサンドボックスアカウントを発見する: ```json theme={null} // list_accounts — サンドボックスアカウントを探す { "sandbox": true } ``` セラーは既存のテストアカウントを返します: ```json theme={null} { "accounts": [{ "account_id": "acct_sandbox_acme_001", "name": "Acme Test Account", "status": "active", "sandbox": true }] } ``` **使用方法:** すべてのリクエストで `account_id` でサンドボックスアカウントを参照する: ```json theme={null} // get_products — アカウント ID 名前空間サンドボックス { "account": { "account_id": "acct_sandbox_acme_001" }, "brief": "Premium CTV inventory for Q2 campaign" } ``` ### クイックリファレンス | | バイヤー宣言(`require_operator_auth: false`) | アカウント ID 名前空間(`require_operator_auth: true`) | | ---------------- | -------------------------------------- | -------------------------------------------- | | **サンドボックスアカウント** | バイヤーが `sync_accounts` で宣言 | セラーのプラットフォームに既存 | | **発見方法** | N/A — バイヤーが作成 | `sandbox: true` で `list_accounts` | | **アカウント参照** | `sandbox: true` を持つナチュラルキー | `account_id` | | **実世界の類似** | セルフサービステストモード | Stripe テストモード、Google Ads サンドボックス | ## レスポンスの確認 成功レスポンスには `sandbox: true` が含まれ、リクエストがサンドボックスモードで処理されたことを確認します: ```json theme={null} { "products": [...], "sandbox": true } ``` ## 完全なライフサイクルの例(バイヤー宣言アカウント) この例はバイヤー宣言アカウントのパスを示します。アカウント ID 名前空間の場合、サンドボックスアカウントを `list_accounts`(またはセラー定義 ID のためのアウトオブバンドのオンボーディング)を通じて解決してから、各ステップのナチュラルキーアカウント参照を `{ "account_id": "acct_sandbox_acme_001" }` に置き換える。 ### 1. 商品の発見 ```json theme={null} // get_products { "account": { "brand": { "domain": "acme-corp.com" }, "operator": "acme-corp.com", "sandbox": true }, "brief": "CTV inventory for brand awareness" } ``` ### 2. メディアバイの作成 ```json theme={null} // create_media_buy { "account": { "brand": { "domain": "acme-corp.com" }, "operator": "acme-corp.com", "sandbox": true }, "proposal_id": "prop_abc", "total_budget": { "amount": 50000, "currency": "USD" }, "brand": { "domain": "acme-corp.com" }, "start_time": { "start_type": "asap" }, "end_time": "2026-04-01T00:00:00Z" } ``` セラーはリアルな ID、パッケージ、クリエイティブ締め切りを持つシミュレートされたメディアバイを返す — 実際のプラットフォームには何も予約されない。 ### 3. クリエイティブのアップロード ```json theme={null} // sync_creatives { "account": { "brand": { "domain": "acme-corp.com" }, "operator": "acme-corp.com", "sandbox": true }, "creatives": [{ "creative_id": "hero_video_30s", "name": "Brand Hero Video 30s", "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_standard_30s" }, "assets": { "video": { "url": "https://cdn.example.com/hero.mp4", "width": 1920, "height": 1080, "duration_ms": 30000 } } }], "assignments": { "hero_video_30s": ["pkg_001"] } } ``` ### 4. 配信の確認 ```json theme={null} // get_media_buy_delivery { "account": { "brand": { "domain": "acme-corp.com" }, "operator": "acme-corp.com", "sandbox": true }, "media_buy_ids": ["mb_sandbox_123"] } ``` セラーはシミュレートされた配信メトリクス — インプレッション、スペンド、ペーシング — をキャンペーンが実行中であるかのように返します。 ## サンドボックス vs ドライラン 一部の sync タスク(`sync_creatives`、`sync_catalogs`)は `dry_run` パラメータをサポートします。これらは異なる目的を持ちます: | | サンドボックスアカウント | `dry_run` | | ---------- | -------------- | ------------------ | | **意味** | 何も実際ではない | 変更を適用せずにプレビュー | | **スコープ** | アカウント上のすべてのタスク | sync タスクのみ | | **副作用** | なし(シミュレート) | なし(プレビューのみ) | | **ユースケース** | 完全なライフサイクルのテスト | コミット前に同期が何を変えるかを確認 | これらは組み合わせられます——サンドボックスアカウントでの `dry_run: true` は、サンドボックスの状態すら更新せずに同期をプレビューします。 `X-Dry-Run`、`X-Test-Session-ID`、`X-Mock-Time` の HTTP ヘッダーは**非推奨**です。サンドボックスモードが、プロトコルレベルのパラメータとしてそれらを置き換えます。 * **セラーはこれらのヘッダーに基づいて挙動を変えてはなりません(MUST NOT)**。サンドボックスモードはアカウント参照のみで決まります。セラーはヘッダーを完全に無視すべきであり(SHOULD)、バイヤーが古い統合を特定するのを助けるために非推奨の警告をログに記録してもかまいません(MAY)。 * **バイヤーはプロダクションの副作用を防ぐためにこれらのヘッダーに頼ってはなりません(MUST NOT)**。アカウント参照の `sandbox: true` のみがサンドボックスセマンティクスを保証します。 ## セラーの実装 リクエストがサンドボックスアカウントを参照する場合(ナチュラルキーの `sandbox: true` またはサンドボックスの `account_id` を通じて)、エージェントはプロダクションの状態を永続化したり実世界の副作用を引き起こしたりしてはなりません(MUST NOT): * 実際の広告プラットフォーム API コール(実際の注文、ラインアイテムなど)を**行ってはならない(MUST NOT)** * 実際のお金を請求したり、実際の請求レコードを**作成してはならない(MUST NOT)** * プロダクションと同じ方法で入力を**検証しなければならない(MUST)**(無効な予算、不正な日付などを拒否します) * シミュレートされたデータを含むリアルなレスポンスシェイプを**返さなければならない(MUST)** * 成功レスポンスに `sandbox: true` を含める**べきだ(SHOULD)** サンドボックスのエラーは実際の検証エラーです。バイヤーがサンドボックスアカウントを使用して無効な予算を送信した場合、実際のエラーを返す — 偽のエラーをシミュレートしません。 **アカウント ID 名前空間のセラー**: プラットフォームに `list_accounts` が `sandbox: true` でフィルタリングした際に返すことができる既存のサンドボックス/テストアカウントがあることを確認するか、サンドボックス ID をアウトオブバンドで供給します。 **バイヤー宣言アカウントのセラー**: `sync_accounts` とアカウント参照でナチュラルキーの一部として `sandbox: true` を受け入れる。`(brand, operator, sandbox: true)` を `(brand, operator)` とは別のアカウントとして扱います。 ## プロトコルコンプライアンス ケイパビリティで `account.sandbox: true` を宣言するセラーは以下をしなければなりません (MUST): * アカウントモデルに適したサンドボックスアカウントを受け入れる * サンドボックスアカウントを参照するすべてのリクエストにサンドボックスセマンティクスを適用します * サンドボックスリクエストを処理する際、プロダクションの状態を永続化したり実世界の副作用を引き起こしたりしない * 通常の入力検証を適用する(サンドボックスは検証をバイパスしない) セラーはサンドボックスアカウントリクエストを処理する際に成功レスポンスに `sandbox: true` を含めるべきだ (SHOULD)。 # Targeting Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/advanced-topics/targeting AdCP のターゲティング — 自然言語ブリーフがどのようにタクソノミーベースのオーディエンス選択を置き換えるか、地理的オーバーレイとインプレッション時のリアルタイム判断とともに解説します。 AdCP のターゲティング思想は **ブリーフベースのターゲティング** を中心に据えています。ターゲティング要件は自然言語のブリーフで伝え、パブリッシャーが必要なターゲティング機能をすべて含むプロダクトを返します。 ## コア原則: ブリーフによるターゲティング AdCP でターゲティングを指定する主な方法はキャンペーンブリーフです。複雑なターゲティングパラメータを設定する代わりに、バイヤーはオーディエンス要件を平易な言葉で記述します: ```json theme={null} { "brief": "We want to reach millennial parents (ages 25-40) in major US metro areas who are interested in sustainable products. Focus on mobile and desktop during evening hours when families are planning purchases." } ``` すると、パブリッシャーはこのオーディエンスにリーチするためのターゲティング機能を含むプロダクトを返します。ターゲティングのコストはメディアの価格に組み込まれています。 バイヤーが、セラーが提供する特定の選択可能なシグナルをパッケージレベルで制御したい場合は、パッケージ上で `targeting_overlay.signal_targeting_groups` を使います。バイ時の適格性は、選択したプロダクトのシグナルターゲティング契約に由来します: `signal_targeting_allowed`、存在する場合はインラインの `Product.signal_targeting_options`、インラインオプションを省略するホールセールプロダクトについてはセラーの `get_signals` フィード、そして `signal_targeting_rules` です。シグナルは名前付きのターゲティング可能な次元であり、`signal_ref` で参照します: プロダクトローカルなシグナルオプションには `scope: "product"`、データプロバイダーが公開する adagents.json の `signals[]` で定義されたシグナルには `data_provider_domain` を伴う `scope: "data_provider"`、adagents.json の `signals[]` で公開されていないソースネイティブなシグナルには `signal_source_url` を伴う `scope: "signal_source"` です。`signal_ref.scope` はバイ時の解決パスであってプロヴェナンス(出所)ではなく、権威ある拡充情報はセラー、データプロバイダー、またはソースのシグナル定義に存在します。この目的のために `audience_include` や `audience_exclude` を流用しないでください。それらのフィールドは `sync_audiences` を通じて登録されたファーストパーティオーディエンス専用です。 プロダクトは、すでにプロダクトに束ねられている、または束ねる予定のシグナルを `included_signals` で公開することもできます。それらのシグナルは記述的なプロダクトメタデータであってパッケージレベルのターゲティング制御ではなく、バイヤーは `signal_targeting_groups` でそれらをエコーしません。 ## なぜブリーフベースなのか ### ターゲティング衝突を排除 * **単一のソース**: すべてのターゲティングはパブリッシャーのプロダクト定義に由来します * **レイヤリングの衝突なし**: 複数のターゲティングシステムが競合することを避けます * **価格の一貫性**: ターゲティングのコストは透明であり、メディア価格に含まれます ### 実装を簡素化 * **自然言語**: バイヤーは馴染みのある言葉でニーズを記述します * **パブリッシャーの専門知識**: パブリッシャーは自身のインベントリとオーディエンス機能を最もよく知っています * **複雑さの低減**: プラットフォーム固有のターゲティング構文を学ぶ必要がありません ### 正確な価格付けを実現 * **すべて込みの価格**: すべてのターゲティングコストがプロダクト価格に組み込まれています * **サプライズなし**: バイヤーは完全なコストを事前に把握できます * **市場主導**: 価格はターゲティングされたインベントリの真の市場価値を反映します ## TMP を用いたリアルタイム判断 インプレッション時に行わなければならないターゲティングの判断には、AdCP は **[トラステッドマッチプロトコル(TMP)](/docs/trusted-match)** を使います。TMP は、あらゆるサーフェスにわたって配信時に事前交渉されたパッケージを評価する、リアルタイムの実行レイヤーです。 TMP は、構造的に分離された二つの操作——コンテキストマッチ(コンテンツの関連性)とアイデンティティマッチ(ユーザーの適格性)——を通じて、各適格インプレッションに対するリアルタイムの視点をバイヤーに与えます。ユーザーのアイデンティティとページのコンテキストをバイヤーに同時に露出させることはありません。 **主な機能:** * **パブリッシャー横断のフリークエンシーキャップ**: アイデンティティマッチのパスを通じて、複数のパブリッシャーにまたがるユーザーの露出を管理します * **動的なオーディエンスターゲティング**: PII を共有せずに、インプレッション時にオーディエンスのメンバーシップを評価します * **ブランド適合性の強制**: コンテキストマッチのパスを通じたリアルタイムのコンテンツ評価 * **ファーストパーティデータの活性化**: 顧客データをパブリッシャーに露出させずに使用します **TMP を使う場面:** * パブリッシャー横断のフリークエンシーキャップ * サプレッションリスト(既存顧客、過去のコンバージョン者) * ブリーフで表現できないオーディエンスセグメント * 静的なルールを超えるリアルタイムのブランド適合性 * ウェブ、モバイル、CTV、AI アシスタント、リテールメディアにまたがる任意のインプレッション時の判断 完全な仕様とサーフェス固有の統合ガイドについては、[TMP のドキュメント](/docs/trusted-match)を参照してください。 ## パブリッシャーはこうターゲティングを含めます パブリッシャーはターゲティング機能を直接プロダクト定義に組み込みます: ### 地理的ターゲティング プロダクトは地理的なカバレッジを指定します: ``` "Chicago metro premium display package" "US national mobile video inventory" "California lifestyle sites network" ``` ### デモグラフィックターゲティング オーディエンスの特性がプロダクトに組み込まれます: ``` "Millennial-focused social media placements" "Premium business professional network" "Family-oriented content sites" ``` ### コンテキストターゲティング コンテンツとの整合がプロダクト記述に内在します: ``` "Sports content premium video inventory" "Financial news site network" "Entertainment property display package" ``` ### デバイス・プラットフォームターゲティング 技術仕様がプロダクトフォーマットに含まれます: ``` "Mobile-optimized video formats" "Connected TV premium inventory" "Desktop display network" ``` ## 代表的なターゲティング要件のブリーフ例 ### 地理的ターゲティング ```json theme={null} { "brief": "Target users in New York, Los Angeles, and Chicago metro areas with premium display advertising for our luxury retail brand." } ``` ### デモグラフィックターゲティング ```json theme={null} { "brief": "Reach parents with children under 10 who are interested in educational content, focusing on weekend and evening viewing times." } ``` ### コンテキストターゲティング ```json theme={null} { "brief": "Place financial services ads adjacent to business and investment content, targeting affluent professionals during business hours." } ``` ### 行動ターゲティング ```json theme={null} { "brief": "Target users who have shown interest in sustainable products and eco-friendly brands, particularly those researching major purchases." } ``` ## プロダクト応答に含まれるターゲティング情報 パブリッシャーはプロダクトを返す際、バイヤーが必要とするターゲティング情報を含めます: ```json theme={null} { "$schema": "/schemas/media-buy/get-products-response.json", "status": "completed", "cache_scope": "public", "products": [ { "product_id": "premium_millennial_mobile", "name": "Premium Millennial Mobile Package", "description": "Mobile display inventory reaching adults 25-40 across lifestyle and entertainment apps in the top 25 US metro areas.", "publisher_properties": [ { "publisher_domain": "pinnacle-media.example", "selection_type": "by_tag", "property_tags": ["lifestyle", "entertainment", "mobile_app"] } ], "channels": ["display"], "delivery_type": "guaranteed", "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250_image" } ], "pricing_options": [ { "pricing_option_id": "premium_mobile_cpm", "pricing_model": "cpm", "currency": "USD", "fixed_price": 8.50 } ], "forecast": { "forecast_range_unit": "availability", "method": "modeled", "currency": "USD", "reach_unit": "individuals", "points": [ { "metrics": { "audience_size": { "mid": 2500000 }, "impressions": { "mid": 12000000 } } } ] }, "included_signals": [ { "signal_ref": { "scope": "product", "signal_id": "lifestyle_entertainment_interest" }, "name": "Lifestyle and entertainment interest", "value_type": "binary", "description": "Seller-modeled users with recent lifestyle or entertainment content engagement." } ], "reporting_capabilities": { "available_reporting_frequencies": ["daily"], "expected_delay_minutes": 240, "timezone": "America/New_York", "supports_webhooks": false, "available_metrics": ["impressions", "clicks", "spend", "ctr"], "date_range_support": "date_range" }, "brief_relevance": "Matches the requested millennial audience, mobile app environment, lifestyle/entertainment context, and major US metro coverage." } ] } ``` ## プロダクトフィルター vs ターゲティングオーバーレイ 一部のターゲティング次元は、`get_products` のフィルターと `create_media_buy` のターゲティングオーバーレイの両方に現れます。これらは異なる段階で異なる目的を果たします: | フィルター(`get_products`) | オーバーレイ(`create_media_buy`) | フィルター: 何をするか | オーバーレイ: 何をするか | | --------------------- | --------------------------------------- | ------------------------- | ----------------- | | `countries` | `geo_countries` / `_exclude` | これらの国に配信するプロダクトを表示 | これらの国でのみ配信 | | `regions` | `geo_regions` / `_exclude` | これらの地域に配信するプロダクトを表示 | これらの地域でのみ配信 | | `metros` | `geo_metros` / `_exclude` | これらのメトロに配信するプロダクトを表示 | これらのメトロでのみ配信 | | `postal_areas` | `geo_postal_areas` / `_exclude` | これらの郵便番号に配信するプロダクトを表示 | これらの郵便番号でのみ配信 | | `geo_proximity` | `geo_proximity` | この地点の近くにインベントリを持つプロダクトを表示 | この地点の近くのユーザーにのみ配信 | | `keywords` | `keyword_targets` / `negative_keywords` | これらの検索語をサポートするプロダクトを表示 | これらの特定の語に入札 | **フィルター**は、セルサイドエージェントにバイヤーにとって何が重要かを伝え、関連するプロダクトをキュレーションできるようにします。プロキシミティのフィルターがなければ、セラーは geo\_proximity をサポートしないプロダクトを推奨するかもしれず、バイヤーは `create_media_buy` までそのギャップに気づきません。 **オーバーレイ**は、実行時に正確な機能的制約を適用します。オーバーレイはセラーのアドサーバーが強制するものです。 値フィルター(`countries`、`regions`、`metros`、`postal_areas`、`geo_proximity`、`keywords`)はカバレッジエリアで絞り込みます——「これらの場所のインベントリを見せてほしい」。ケイパビリティフィルター(`required_geo_targeting`、`required_features`)はセラーが何を強制できるかで絞り込みます——「郵便番号レベルのターゲティングをサポートするセラーのみ」。これらは組み合わせられます: 特定のエリアのインベントリを、その粒度でターゲティングできるセラーから必要とする場合は両方を使います。 バイヤーへ: 発見時にフィルターを渡し、それからバイ時に同じ値(または洗練させた版)をオーバーレイとして適用します。オーバーレイのスキーマはより厳格である点に注意してください——例えば、`keyword_targets` は `match_type` を必須としますが、`keywords` フィルターはデフォルトで `broad` になります。 ### 例: 発見からバイまで ```json theme={null} // Step 1: get_products — signal intent with filters { "brief": "Coffee shop promotion in downtown Seattle", "filters": { "geo_proximity": [{ "lat": 47.6062, "lng": -122.3321, "label": "Downtown Seattle", "radius": { "value": 5, "unit": "mi" } }], "keywords": [{ "keyword": "coffee" }] } } ``` ```json theme={null} // Step 2: create_media_buy — apply precise constraints as overlays { "targeting": { "geo_proximity": [{ "lat": 47.6062, "lng": -122.3321, "label": "Downtown Seattle", "radius": { "value": 5, "unit": "mi" } }], "keyword_targets": [{ "keyword": "coffee", "match_type": "broad" }] } } ``` `keywords`(フィルター)が `keyword_targets`(オーバーレイ)になり、`match_type` が必須になる点に注意してください。 ## ターゲティングオーバーレイを使う場面 `create_media_buy` と `update_media_buy` のターゲティングオーバーレイは**まれ**であり、以下の場合にのみ使うべきです: ### 地理的な制限 geo フィールドは**次の場合のみ**使います: * **RCT テスト**: 特定の地理的分割を必要とするランダム化比較試験 * **規制コンプライアンス**: 地理的制限に関する法的要件 * **プロダクトの絞り込み**: プロダクトが複数の地域にまたがり、そのサブセットに制限する必要がある場合 **包含フィールド**(これらの場所に配信を制限): * `geo_countries`: ISO 3166-1 alpha-2 の国コード(例: `["US", "GB"]`) * `geo_regions`: ISO 3166-2 の細分区分コード(例: `["US-CA", "GB-SCT"]`) * `geo_metros`: 明示的なシステム(例: `nielsen_dma`、`uk_itl2`)を伴う構造化されたメトロエリア——すべてのパブリッシャーがメトロレベルのターゲティングをサポートするわけではありません * `geo_postal_areas`: 明示的な国とシステム(例: `US` / `zip`、`GB` / `outward`、`ZA` / `postal_code`)を伴う構造化された郵便エリア——すべてのパブリッシャーが郵便レベルのターゲティングをサポートするわけではありません **除外フィールド**(これらの場所を配信から除外): * `geo_countries_exclude`: `geo_countries` と同じ形式 * `geo_regions_exclude`: `geo_regions` と同じ形式 * `geo_metros_exclude`: `geo_metros` と同じ形式 * `geo_postal_areas_exclude`: `geo_postal_areas` と同じ形式 **注**: 包含と除外は組み合わせられます。メトロと郵便のターゲティングは分類システムの指定を必要とし、これにより国際的なサポートが可能になります。すべての地理的粒度がすべてのパブリッシャーでサポートされるわけではありません。国と地域が最も広くサポートされています。 ### 年齢制限(コンプライアンス) **法的コンプライアンス**の要件のために使います: * **アルコール広告**: 米国で検証済みの 21 歳以上を要求 * **ギャンブル/ゲーミング**: 管轄区域に応じて検証済みの 18 歳以上または 21 歳以上を要求 * **カンナビス**: 現地の規制に応じて検証済みの年齢を要求 ```json theme={null} { "$schema": "/schemas/core/targeting.json", "age_restriction": { "min": 21, "verification_required": true, "accepted_methods": ["facial_age_estimation", "id_document", "world_id"] } } ``` **検証方法**([`age-verification-method.json`](https://adcontextprotocol.org/schemas/v3/enums/age-verification-method.json) で定義、ISO/IEC 27566-1 の年齢保証標準に基づく): * `facial_age_estimation` - AI ベースの年齢推定(Yoti など) * `id_document` - 政府発行 ID のスキャン * `digital_id` - 検証済みのデジタルアイデンティティクレデンシャル * `credit_card` - 決済カードによる年齢ゲート * `world_id` - World ID オーブ検証 **注**: 「推定」年齢(行動/プロフィールからの推測)は規制コンプライアンスには**受け入れられません**。プラットフォームはサポートする検証方法を `get_adcp_capabilities` で宣言します。 ### デバイスプラットフォーム(技術的互換性) **技術要件**のために使います: * **アプリインストールキャンペーン**: iOS 専用アプリは `device_platform: ["ios"]` を要求 * **CTV キャンペーン**: 特定の TV オペレーティングシステムをターゲット ```json theme={null} { "$schema": "/schemas/core/targeting.json", "device_platform": ["ios", "android"] } ``` **利用可能なプラットフォーム**([`device-platform.json`](https://adcontextprotocol.org/schemas/v3/enums/device-platform.json) で定義、CTV 向けに拡張された Sec-CH-UA-Platform 標準に基づく): * ブラウザ: `ios`、`android`、`windows`、`macos`、`linux`、`chromeos` * CTV: `tvos`、`tizen`、`webos`、`fire_os`、`roku_os` * その他: `unknown` ### デバイスタイプ(フォームファクター) OS ではなくハードウェアのカテゴリで**パフォーマンス最適化**のためにターゲティングする場合に使います: * **モバイルキャンペーン**: OS を問わずすべてのモバイルデバイスをターゲット * **CTV キャンペーン**: すべてのプラットフォームにわたるコネクテッド TV をターゲット * **フォームファクターの除外**: アプリインストールキャンペーンで CTV をスキップ ```json theme={null} { "$schema": "/schemas/core/targeting.json", "device_type": ["mobile", "tablet"] } ``` **除外** — 特定のフォームファクターを除外するには `device_type_exclude` を使います: ```json theme={null} { "$schema": "/schemas/core/targeting.json", "device_type_exclude": ["dooh"] } ``` **利用可能なタイプ**([`device-type.json`](https://adcontextprotocol.org/schemas/v3/enums/device-type.json) で定義): * `desktop`、`mobile`、`tablet`、`ctv`、`dooh`、`unknown` **デバイスタイプ vs デバイスプラットフォーム**: `device_type` はフォームファクター(モバイル、デスクトップ、CTV)をターゲットします。`device_platform` はオペレーティングシステム(iOS、Android、tvOS)をターゲットします。パフォーマンス最適化には `device_type` を、技術的互換性には `device_platform` を使います。 ### 言語(ローカライゼーション) **ローカライゼーション要件**のために使います: * クリエイティブが特定の言語である * キャンペーンが特定の言語話者をターゲットする ```json theme={null} { "$schema": "/schemas/core/targeting.json", "language": ["es", "en"] } ``` **形式**: ISO 639-1 の 2 文字言語コード(例: `en`、`es`、`fr`、`de`、`zh`)。 ### フリークエンシーキャップ 二つのフリークエンシー制御は、独立して、または一緒に使えます: **露出間のクールダウン** — `suppress` は連続した配信を防ぎます: ```json theme={null} { "$schema": "/schemas/core/targeting.json", "frequency_cap": { "suppress": { "interval": 60, "unit": "minutes" } } } ``` **エンティティごと・ウィンドウごとのインプレッションキャップ** — `max_impressions` + `per` + `window` が総露出を制限します: ```json theme={null} { "$schema": "/schemas/core/targeting.json", "frequency_cap": { "max_impressions": 5, "per": "households", "window": { "interval": 7, "unit": "days" } } } ``` 両方を組み合わせられます。`per` フィールドは、リーチ最適化ゴールの `reach_unit` と同じエンティティタイプを使います——リーチキャンペーンの上にハードキャップを重ねる場合は、一致する値を使ってください。 ### 地理的オーバーレイの例(RCT テスト) RCT テストでは、包含ターゲティングよりも除外ターゲティングの方がしばしば単純です。含める何百もの DMA を列挙する代わりに、全国キャンペーンからホールドアウト市場を除外します。包含と除外を組み合わせた場合、除外フィールドは包含された集合から差し引かれます(例: 「米国からこれら 3 つの DMA を引いたもの」): ```json theme={null} { "packages": [ { "product_id": "national_video", "targeting_overlay": { "geo_countries": ["US"], "geo_metros_exclude": [ { "system": "nielsen_dma", "values": ["501", "803", "602"] } ] } }, { "product_id": "national_video", "targeting_overlay": { "geo_metros": [ { "system": "nielsen_dma", "values": ["501", "803", "602"] } ] } } ] } ``` 正確な市場を指定したい場合には、包含ターゲティングも同じように機能します: ```json theme={null} { "packages": [ { "product_id": "national_video", "targeting_overlay": { "geo_metros": [ { "system": "nielsen_dma", "values": ["501", "602", "803"] } ] } }, { "product_id": "national_video", "targeting_overlay": { "geo_metros": [ { "system": "nielsen_dma", "values": ["504", "505", "506"] } ] } } ] } ``` ## ターゲティングオーバーレイを使うべきでないもの **代わりにブリーフで表現してください:** * **デモグラフィックの好み**(年齢、性別、収入)- ブリーフのテキストで「ミレニアルをターゲット」や「高収入世帯」 * **デバイスの好み** - ブリーフのテキストで「モバイルユーザー」や「CTV 視聴者」(`device_platform` オーバーレイは技術的互換性のためだけに使用) * **コンテンツカテゴリ** - ブリーフのテキストで「スポーツコンテンツ」や「ニュースサイト」 * **一般的なオーディエンスの好み** - ブリーフのテキストで「自動車購入意向者」や「ラグジュアリー購入者」。バイヤーがこのパッケージに特定の名前付きシグナルを適用したい場合は、`signal_targeting_groups` を使います。 * **デイパートの好み** - ブリーフのテキストで「朝の通勤時間」や「プライムタイムの夜」 **オーバーレイ vs ブリーフ:** | ユースケース | オーバーレイ | ブリーフ | | --------------------------------------- | ----------------------------------------- | ---------------------------------- | | コンプライアンスのための年齢(アルコール、ギャンブル) | ✅ `age_restriction` | | | オーディエンスターゲティングのための年齢 | | ✅ "Target millennials" | | アプリ互換性のためのデバイス | ✅ `device_platform` | | | オーディエンスの好みのためのデバイス | | ✅ "Mobile users" | | クリエイティブのローカライズのための言語 | ✅ `language` | | | オーディエンスの好みのための言語 | | ✅ "Spanish-speaking audiences" | | ファーストパーティ CRM オーディエンス(リターゲティング、サプレッション) | ✅ `audience_include` / `audience_exclude` | | | オーディエンスの好み(インタレストターゲティング) | | ✅ ブリーフで "Auto intenders" | | セラーが提供する特定の名前付きシグナル | ✅ `signal_targeting_groups` | | | 検索/リテールメディアのキーワードターゲティング | ✅ `keyword_targets` / `negative_keywords` | | | 広範なテーマ的意図(「靴を探している人」) | | ✅ "Reach in-market shoe shoppers" | | 特定座標への近接(都市から車で 2 時間以内) | ✅ `geo_proximity` | | | 近隣のオーディエンス(「コーヒーショップの近くの人」) | | ✅ "Reach people near coffee shops" | **なぜ好みにはブリーフの方が良いのか:** * 自然言語は意図をより明確に捉えます * パブリッシャーは自身のインベントリを知っており、効果的にターゲティングできます * チャネル固有の複雑さを避けられます(DOOH にブラウザはありません) * エッジケースの少ない、よりシンプルな API ## 利用可能なターゲティングオーバーレイパラメータ 地理的ターゲティングは、すべての geo 次元について包含(〜に制限)と除外(〜から除外)の両方をサポートします。包含フィールドと除外フィールドは組み合わせられます——例えば、ある国を含めつつ、その中の特定のメトロを除外できます。 ### 除外のセマンティクス **包含なしの除外。** 除外フィールドが対応する包含フィールドなしに存在する場合、除外はプロダクトの完全な地理的カバレッジに適用されます。例えば、プロダクトが米国全体をカバーし、バイヤーが `geo_metros_exclude` のみを指定した場合、除外されたメトロがプロダクトの全国的なフットプリントから取り除かれます。 **レベル横断の解決。** 地理的レベルは階層を成します: 国 > 地域 > メトロ > 郵便。セラーは、より高いレベルでの除外がより具体的なレベルでの包含より優先されるように、階層的な衝突を解決すべきです(SHOULD)。例えば、`geo_countries_exclude: ["US"]` と `geo_regions: ["US-CA"]` の組み合わせは、米国への配信なしという結果になるべきです——国レベルの除外が優先されます。 **同一値の重複。** セラーは、同じ値が同じレベルの包含フィールドと除外フィールドの両方に現れるリクエスト(例: `geo_countries: ["US"]` と `geo_countries_exclude: ["US"]`)を拒否し、説明的なエラーを返すべきです(SHOULD)。 **ケイパビリティ。** `get_adcp_capabilities` で地理的ターゲティングのサポートを宣言するセラーは、そのレベルで包含と除外の両方をサポートすべきです(SHOULD)。セラーが一方向のみをサポートする場合、サポートしないフィールドを黙って無視するのではなく、バリデーションエラーを返さなければなりません(MUST)。 ### geo\_countries * **説明**: 特定の国に配信を制限する * **形式**: ISO 3166-1 alpha-2 の国コード * **例**: `["US", "CA"]`、`["GB", "FR", "DE"]` * **ユースケース**: 規制コンプライアンス、国別キャンペーン ### geo\_countries\_exclude * **説明**: 特定の国を配信から除外する * **形式**: ISO 3166-1 alpha-2 の国コード * **例**: `["RU", "CN"]` * **ユースケース**: 規制コンプライアンス、制裁 ### geo\_regions * **説明**: 特定の地域/州に配信を制限する * **形式**: ISO 3166-2 の細分区分コード * **例**: `["US-CA", "US-NY"]`、`["GB-SCT", "GB-ENG"]` * **ユースケース**: 州レベルのコンプライアンス、地域テスト ### geo\_regions\_exclude * **説明**: 特定の地域/州を配信から除外する * **形式**: ISO 3166-2 の細分区分コード * **例**: `["US-CA"]`、`["CA-QC"]` * **ユースケース**: 規制コンプライアンス(例: 州ごとのカンナビス規制)、RCT ホールドアウト地域、プロダクトが利用できない地域 ### geo\_metros * **説明**: 特定のメトロエリアに配信を制限する * **形式**: それぞれ `system` と `values` を持つオブジェクトの配列 * **システム**: `nielsen_dma`(米国)、`uk_itl1` / `uk_itl2`(英国)、`eurostat_nuts2`(EU)、`custom` * **例**: `[{ "system": "nielsen_dma", "values": ["501", "803"] }]` * **ユースケース**: ローカルキャンペーン、メトロレベルの RCT テスト * **注**: セラーはサポートするシステムを `get_adcp_capabilities` で宣言しなければなりません ### geo\_metros\_exclude * **説明**: 特定のメトロエリアを配信から除外する * **形式**: それぞれ `system` と `values` を持つオブジェクトの配列 * **例**: `[{ "system": "nielsen_dma", "values": ["602"] }]` * **ユースケース**: RCT ホールドアウト市場、競合除外ゾーン、プロダクトが利用できない市場 * **注**: セラーはサポートするシステムを `get_adcp_capabilities` で宣言しなければなりません ### geo\_postal\_areas * **説明**: 特定の郵便エリアに配信を制限する * **形式**: それぞれ `country`、`system`、`values` を持つオブジェクトの配列 * **システム**: `zip`、`zip_plus_four`、`outward`、`full`、`fsa`、`plz`、`code_postal`、`postcode`、`pin`、`postal_code` などの国ローカルな値 * **例**: `[{ "country": "US", "system": "zip", "values": ["10001", "10002"] }]` * **ユースケース**: ハイパーローカルキャンペーン、郵便レベルの制限 * **注**: セラーはサポートするシステムを `get_adcp_capabilities` で宣言しなければなりません。3.x への移行期間中は、`us_zip` のような非推奨の国融合システムが互換性と SDK のバックフィルのために引き続き受け入れられます。 ### geo\_postal\_areas\_exclude * **説明**: 特定の郵便エリアを配信から除外する * **形式**: それぞれ `country`、`system`、`values` を持つオブジェクトの配列 * **例**: `[{ "country": "US", "system": "zip", "values": ["90210"] }]` * **ユースケース**: RCT ホールドアウトの郵便番号、配信制限エリア * **注**: セラーはサポートするシステムを `get_adcp_capabilities` で宣言しなければなりません。非推奨のレガシー形式は 3.x への移行期間中は引き続き受け入れられます。 ### axe\_include\_segment * **説明**: 包含ターゲティング用のセグメント ID(レガシーの AXE フィールド) * **形式**: 文字列のセグメント識別子 * **例**: `"seg_auto_intenders_q1"`、`"audience_lapsed_buyers_30d"` * **ユースケース**: 動的なオーディエンスターゲティング、ファーストパーティデータの活性化 * **注**: このフィールドはレガシーの AXE 統合に由来します。新しい実装では [TMP](/docs/trusted-match) を使うべきです。そこではオーディエンスターゲティングはアイデンティティマッチのパスを通じて扱われます。 ### axe\_exclude\_segment * **説明**: 除外ターゲティング用のセグメント ID(レガシーの AXE フィールド) * **形式**: 文字列のセグメント識別子 * **例**: `"seg_existing_customers"`、`"audience_past_converters"` * **ユースケース**: 顧客サプレッション、フリークエンシー管理 * **注**: このフィールドはレガシーの AXE 統合に由来します。新しい実装では [TMP](/docs/trusted-match) を使うべきです。そこではサプレッションはアイデンティティマッチのパスを通じて扱われます。 ### audience\_include * **説明**: これらのファーストパーティ CRM オーディエンスのメンバーであるユーザーに配信を制限します。アップロードされたリスト上の人だけが広告を見る資格を持ちます。 * **形式**: [`sync_audiences`](/docs/media-buy/task-reference/sync_audiences) からの `audience_id` 文字列の配列 * **例**: `["lapsed_subscribers", "high_value_prospects"]` * **ユースケース**: 既知ユーザーへのリターゲティング、既存メンバーをターゲットするロイヤルティキャンペーン、クローズドプラットフォーム(LinkedIn、Meta、TikTok、Google Ads)での CRM ベースの包含 * **類似/拡張のためではない**: あるオーディエンスに*似た*新しいユーザーを見つけるには、キャンペーンブリーフでその意図を記述します(「既存顧客のような人にリーチ」)——セラーが拡張戦略を扱います * **前提条件**: オーディエンスは使用前に `sync_audiences` を通じて登録され `ready` になっていなければなりません * **注**: セラーは `get_adcp_capabilities` でサポートを宣言しなければなりません ### audience\_exclude * **説明**: これらのファーストパーティ CRM オーディエンスのメンバーであるユーザーへの配信を抑制します。マッチしたユーザーは他のターゲティングに関わらず除外されます。 * **形式**: [`sync_audiences`](/docs/media-buy/task-reference/sync_audiences) からの `audience_id` 文字列の配列 * **例**: `["existing_customers", "recent_purchasers"]` * **ユースケース**: 獲得キャンペーンでの顧客サプレッション、最近のコンバージョン者の除外、オプトアウトしたユーザーの抑制 * **前提条件**: オーディエンスは使用前に `sync_audiences` を通じて登録され `ready` になっていなければなりません * **注**: セラーは `get_adcp_capabilities` でサポートを宣言しなければなりません ### signal\_targeting\_groups * **説明**: セラーが提供するデータシグナルの基本的なブール型のグルーピング。単純な包含のみのシグナルターゲティングと、`(A OR B) AND NOT (C OR D)` のようなグループ化された包含/除外の表現の両方に使います。 * **ディスカバリー**: ホールセールプロダクトは `signal_targeting_allowed: true` を設定してインラインの `signal_targeting_options` を省略でき、バイヤーはその場合 `get_signals` を選択可能なシグナルフィードとして使います。プロダクトは、プロダクト固有の価格、活性化ハンドル、デフォルト、グルーピングのヒント、またはブリーフ/refine 応答向けの関連サブセットが必要な場合に、インラインの `signal_targeting_options` を返します。 * **形式**: 必須のトップレベル `operator: "all"` と `groups` 配列を持つオブジェクト。v1 では `all` のみをサポートしますが、トップレベルのオペレーターは常に存在します。 * **子グループ**: 各子グループは `operator: "any"` または `operator: "none"` と、パッケージシグナルターゲティングオブジェクトの `signals` 配列を持ちます。各シグナルは `signal_ref`、`value_type`、値フィールド、加えて任意のコマーシャルおよび活性化ハンドルを持ちます。 * **レガシーのフラットターゲティング**: `targeting_overlay.signal_targeting` は、古いクライアント向けに SignalRef の移行ウィンドウ中はスキーマ的に有効なままですが、非推奨です。新しいパッケージレベルのシグナル選択は `signal_targeting_groups` を使い、セラーが包含/除外グループ、プロダクトルール、シグナルごとの価格を一貫して適用できるようにします。 * **セマンティクス**: `any` はユーザーがそのグループ内の少なくとも一つのシグナルにマッチしなければならないことを意味します。`none` はユーザーがそのグループ内のどのシグナルにもマッチしてはならないことを意味します。トップレベルの `all` により、すべての子グループが通過しなければなりません。単純な包含のみのターゲティングには、`operator: "any"` の子グループを一つ送ります。 * **解決モデル**: `signal_targeting_rules.resolution_model` は、セラーが選択されたシグナルをインベントリにどう適用するかをバイヤーに伝えます。`direct_targeting` は、選択されたシグナルがパッケージのターゲティング述語のように振る舞うことを意味します。`seller_planned` は、選択されたシグナルが、プロダクト固有のインベントリ、タイミング、可用性、リーチ、ペーシングの制約に対するセラー管理のプランニングへの入力であることを意味します。バイヤーは、選択されたオーディエンスをより低レベルのインベントリやスケジュールの決定に分解しようとすべきではありません。 * **選択グループ**: プロダクトが `signal_targeting_rules.selection_group_rules` を宣言する場合、各子グループはちょうど一つの `selection_group` と一つのターゲティングモードのシグナルを含まなければならず(MUST)、バイヤーは各 `(selection_group, targeting_mode)` ペアにつき最大一つの子グループを送らなければなりません(MUST)。セラーは、異なる選択グループのルールを同じ `any` または `none` グループに組み合わせる、重複した、混在した、または折りたたまれた子グループを拒否しなければなりません(MUST)。`selection_group` はプロダクトが定義する合成可能性のバケットであって、バックエンドのアイデンティティタイプではありません: 例えば、GAM ベースのセラーは、オーディエンスセグメントとキーバリューの両方を通常の `signal_ref` オプションとして公開し、それらが自由に OR 結合できるときは一つの `selection_group` を、別々の AND 節としてトラフィックしなければならないときは別々の `selection_group` を使うことができます。 * **価格**: 選択したプロダクトの `signal_targeting_options` エントリが `pricing_options` を持つ場合は `pricing_option_id` を含めます。シグナルがプロダクト価格に束ねられているか、追加コストがない場合にのみ省略します。`signal_targeting_options` のプロダクトスコープの価格は、そのプロダクトについて権威があります。プロダクトオプションにプロダクト固有の価格がない場合、セラーは `get_signals` で公開されるデフォルトの価格を使ってもかまいません(MAY)。 * **シグナル参照**: プロダクトローカルなシグナルオプションには `signal_ref: { "scope": "product", "signal_id": "..." }` を使います。データプロバイダーが公開する adagents.json の `signals[]` で定義されたシグナルには `signal_ref: { "scope": "data_provider", "data_provider_domain": "...", "signal_id": "..." }` を使います。`signal_ref` はシグナル定義を識別します。`signal_agent_segment_id` は、プロダクトオプションが公開する場合に、解決済みのセグメントまたは実行ハンドルを識別します。公開された `signal_agent_segment_id` はパッケージエントリでそのままエコーし、カテゴリ値からアイデンティティを再構築するよりもそれを優先してください。プロバイダーは、リアルタイムの雨と予報の高降水量のような定義を区別するために、ハンドルを名前空間で分けられるからです。プロダクトオプションが `activation_status: "requires_activation"` を持つ場合、それは `signal_agent_segment_id` を含まなければなりません(MUST)。まずシグナルを活性化し、それからセラーが要求する場合は `activation_key` を含めます。 * **プロバイダー公開シグナル**: プロバイダー公開シグナルについては、`signal_ref.data_provider_domain` が上流のデータプロバイダーを識別し、`signal_ref.signal_id` が公開シグナル定義を識別します。バイヤーは、プロバイダーの `adagents.json` の `authorized_agents` エントリにセラーがあるかを確認することで、セラーがそのシグナルを提供する権利を検証できます。 * **プロダクトゲーティング**: セラーは次の場合にシグナルエントリを拒否すべきです(SHOULD): プロダクトがそのシグナルをインラインまたは `get_signals` を通じて公開していない、パッケージレベルの選択について `signal_targeting_allowed` が false、シグナルがプロダクトの `signal_targeting_rules` に違反している、シグナルの `allowed_targeting_modes` が要求された子グループのオペレーターを許可しない、シグナルがアカウントに対してアクティブでない、または要求された値がシグナル定義の範囲外である。`allowed_targeting_modes: ["include"]` は `any` グループに、`["exclude"]` は `none` グループにマップされます。バイナリのパッケージシグナルエントリは `value: true` を使います。除外には `value: false` ではなく親の `none` グループを使います。シグナルターゲティングの制限はプロダクトスコープであり、セラー全体の `get_adcp_capabilities` では宣言されません。プロダクトは異なるアドサーバーやプラットフォームに支えられている可能性があるからです。`selection_mode` が `fixed` の場合、バイヤーは `signal_targeting_groups` への編集を省略すべきです(SHOULD)。セラーは固定/デフォルトの選択を適用し、結果のパッケージ状態でそれらをエコーしなければなりません(MUST)。 * **更新**: `targeting_overlay` は `create_media_buy` と `update_media_buy` で共有されるため、選択されたシグナル、グループ表現、または `pricing_option_id` が価格付けされたエンベロープを変更する場合、セラーはフライト途中のシグナルグループの変更を `REQUOTE_REQUIRED` で拒否してもかまいません(MAY)。 * **ファーストパーティオーディエンスのためではない**: `sync_audiences` からのバイヤーがアップロードしたオーディエンスには `audience_include` / `audience_exclude` を使います。 バックエンドのターゲティングプリミティブは意図的に `signal_ref` の背後に隠されています。バイヤーは「オーディエンスセグメント」と「キーバリュー」のために別々のアイデンティティシステムを必要とすべきではありません。選択したプロダクトの `signal_targeting_rules` が、それらのオプションを一緒に合成できるかどうかを記述します。プロダクトが `gam_audience_segments` グループと `gam_key_values` グループの両方を `targeting_mode: "include"` で公開する場合、バイヤーはトップレベルの `all` の下に二つの `any` 子グループを合成するのであって、一つの折りたたまれた混在グループにはしません。 線形放送スケジュールのような、インベントリとオーディエンスのプランニングが不可分なプロダクトについては、セラーは `resolution_model: "seller_planned"` を使います。必須のオーディエンス選択は依然として `selection_mode: "required"` または必須の `selection_group_rules` エントリに存在します。プロダクト横断のオーディエンスの一貫性は、セラーがデータプロバイダーでもある場合でさえ、共有された `scope: "data_provider"` のシグナル定義から得られます。 ```json theme={null} { "packages": [ { "product_id": "retail_video_premium", "pricing_option_id": "media_cpm_usd", "budget": 25000, "targeting_overlay": { "signal_targeting_groups": { "operator": "all", "groups": [ { "operator": "any", "signals": [ { "signal_ref": { "scope": "product", "signal_id": "high_intent_shoppers" }, "value_type": "binary", "value": true }, { "signal_ref": { "scope": "product", "signal_id": "loyalty_members" }, "value_type": "binary", "value": true } ] }, { "operator": "none", "signals": [ { "signal_ref": { "scope": "product", "signal_id": "recent_purchasers" }, "value_type": "binary", "value": true } ] } ] } } } ] } ``` ### frequency\_cap * **説明**: エンティティごとに広告の露出頻度を制限します。二つの任意の制御を独立して、または一緒に使えます。 * **クールダウン制御**: `suppress` — 同じエンティティへの連続した露出の間の最小時間。後方互換性のために `suppress_minutes`(数値)も受け入れられます。 * **インプレッションキャップ**: `max_impressions` + `per` + `window` — エンティティごと・時間ウィンドウごとの総インプレッション上限。三つのフィールドはすべて一緒に必須です。 * **ユースケース**: ユーザー体験の管理、広告疲労の防止、ハードな上限でリーチ最適化ゴールを補完 * **例**: `{"suppress": {"interval": 60, "unit": "minutes"}}`、`{"max_impressions": 5, "per": "households", "window": {"interval": 7, "unit": "days"}}` ### age\_restriction * **説明**: コンプライアンスのために最低年齢を要求します * **形式**: `min`(必須)、`verification_required`、`accepted_methods` を持つオブジェクト * **例**: `{"min": 21, "verification_required": true}`、`{"min": 18, "accepted_methods": ["world_id"]}` * **ユースケース**: アルコール(21+)、ギャンブル(18+)、カンナビス規制 * **注**: プラットフォームはサポートする検証方法を `get_adcp_capabilities` で宣言します ### device\_platform * **説明**: 特定のオペレーティングシステムプラットフォームに制限します * **形式**: Sec-CH-UA-Platform 標準のプラットフォーム識別子の配列 * **例**: `["ios"]`、`["ios", "android"]`、`["tvos", "fire_os"]` * **ユースケース**: アプリインストールキャンペーン(iOS 専用アプリ)、CTV 固有のキャンペーン * **値**: `ios`、`android`、`windows`、`macos`、`linux`、`chromeos`、`tvos`、`tizen`、`webos`、`fire_os`、`roku_os` ### device\_type * **説明**: 特定のデバイスのフォームファクターに制限します * **形式**: デバイスタイプ識別子の配列 * **例**: `["mobile"]`、`["mobile", "tablet"]`、`["ctv"]` * **ユースケース**: モバイル専用プロモーション、すべての TV プラットフォームをターゲットする CTV キャンペーン、特定のキャンペーンから DOOH を除外 * **値**: `desktop`、`mobile`、`tablet`、`ctv`、`dooh`、`unknown` * **注**: セラーは `get_adcp_capabilities` のターゲティングで `device_type: true` を宣言しなければなりません ### device\_type\_exclude * **説明**: 特定のデバイスのフォームファクターを配信から除外します * **形式**: デバイスタイプ識別子の配列 * **例**: `["dooh"]`、`["ctv", "dooh"]` * **ユースケース**: アプリインストールキャンペーンで CTV を除外、ダイレクトレスポンスキャンペーンで DOOH を除外 * **注**: セラーが `get_adcp_capabilities` で `device_type: true` を宣言する場合にサポートされます ### language * **説明**: 特定の言語設定を持つユーザーに制限します * **形式**: ISO 639-1 の 2 文字言語コードの配列 * **例**: `["en"]`、`["es", "en"]`、`["zh", "ja", "ko"]` * **ユースケース**: ローカライズされたクリエイティブ、言語固有のキャンペーン ### keyword\_targets * **説明**: 検索およびリテールメディアプラットフォーム向けに特定のキーワードをターゲットします。指定されたキーワードにマッチするクエリに配信を制限します。 * **形式**: `keyword`、`match_type`(`broad`、`phrase`、または `exact`)、および任意の `bid_price` を持つオブジェクトの配列 * **アイデンティティ**: 各キーワードはタプル `(keyword, match_type)` で識別されます。異なるマッチタイプを持つ同じキーワード文字列は別個のターゲットです。単一リクエスト内の重複ペアはセラーによって拒否されるべきです(SHOULD)。 * **マッチタイプ**: * `broad` — 関連クエリと同義語クエリにマッチ * `phrase` — キーワードフレーズを順序どおりに含むクエリにマッチ * `exact` — キーワードクエリのみにマッチ * **キーワードごとの入札**: 任意の `bid_price` は、そのキーワードについてパッケージレベルの `bid_price` を上書きします。価格オプションから `max_bid` の解釈を継承します: `max_bid` が true のときはこれがキーワードの入札上限、false のときはこれが正確な入札です。省略した場合はパッケージの `bid_price` が適用されます。 * **ユースケース**: 検索キャンペーン、リテールメディアのスポンサープロダクト、キーワードベースの意図ターゲティング * **注**: セラーは `get_adcp_capabilities` で `execution.targeting.keyword_targets` を、受け入れる `supported_match_types` とともに宣言しなければなりません。セラーが宣言するマッチタイプのみを使ってください——セラーはサポートしないマッチタイプを拒否しなければなりません。ローンチ後にキーワードを段階的に追加または更新するには、`update_media_buy` で `keyword_targets_add` と `keyword_targets_remove` を使います。キーワードレベルの配信データ(レポートの `by_keyword`)は、プロダクトに `reporting_capabilities.supports_keyword_breakdown: true` を必要とします——これらは独立したケイパビリティです。`by_keyword` はキーワード粒度(keyword+match\_type ペアごとに 1 行)であって、検索語粒度ではありません。 ```json theme={null} { "$schema": "/schemas/core/targeting.json", "keyword_targets": [ { "keyword": "running shoes", "match_type": "broad", "bid_price": 0.45 }, { "keyword": "trail running shoes womens", "match_type": "phrase", "bid_price": 0.85 }, { "keyword": "acme cloudrunner 5", "match_type": "exact", "bid_price": 1.20 } ] } ``` ### negative\_keywords * **説明**: 特定のキーワードを配信から除外します。これらのキーワードにマッチするクエリは広告をトリガーしません。 * **形式**: `keyword` と `match_type`(`broad`、`phrase`、または `exact`)を持つオブジェクトの配列 * **ユースケース**: 無関係なクエリへの無駄な支出を防ぐ、競合のブランド語を除外 * **注**: セラーは `get_adcp_capabilities` で `execution.targeting.negative_keywords` を、受け入れる `supported_match_types` とともに宣言しなければなりません。ローンチ後にネガティブを段階的に追加/削除するには、`update_media_buy` で `negative_keywords_add` と `negative_keywords_remove` を使います。 ```json theme={null} { "$schema": "/schemas/core/targeting.json", "negative_keywords": [ { "keyword": "free", "match_type": "broad" }, { "keyword": "used running shoes", "match_type": "phrase" } ] } ``` ### store\_catchments * **説明**: 同期された店舗カタログの店舗商圏内のユーザーをターゲットします * **形式**: `sync_catalogs` を通じて同期された store 型カタログをそれぞれ参照するオブジェクトの配列 * **必須フィールド**: `catalog_id` * **任意フィールド**: `store_ids`(特定の店舗に絞り込む)、`catchment_ids`(`"walk"` や `"drive"` のような特定のゾーンに絞り込む) * **ユースケース**: 来店促進キャンペーン、ローカルインベントリ広告、近接ターゲティング ```json theme={null} { "targeting_overlay": { "store_catchments": [ { "catalog_id": "retail-locations", "store_ids": ["store_nyc_001", "store_nyc_002"], "catchment_ids": ["drive"] } ] } } ``` `store_ids` を省略した場合、カタログ内のすべての店舗がターゲットされます。`catchment_ids` を省略した場合、すべての商圏ゾーンがターゲットされます。セラーは店舗商圏ターゲティングのサポートを `get_adcp_capabilities` で宣言しなければなりません。 ### geo\_proximity * **説明**: 任意の地理的地点の周囲の、移動時間、距離、またはカスタム境界内のユーザーをターゲットします * **形式**: それぞれちょうど一つの方法を持つオブジェクトの配列: `travel_time` + `transport_mode`、`radius`、または `geometry` * **必須フィールド**: `lat` + `lng`(travel\_time と radius の方法の場合)、または `geometry`(事前計算された境界の場合) * **任意フィールド**: `label`(エントリの人が読める名前) * **ユースケース**: 観光キャンペーン(都市から車で 2 時間以内)、イベントターゲティング(会場の近く)、空港の商圏エリア * **セマンティクス**: 複数のエントリは OR を使います——列挙された任意の地点の範囲内のユーザーが適格です。他の geo ターゲティングフィールドと交差します(例: `geo_countries` と組み合わせると近接をそれらの国に制限します) 移動時間(アイソクロン)の例: ```json theme={null} { "targeting_overlay": { "geo_proximity": [ { "lat": 51.2277, "lng": 6.7735, "label": "Düsseldorf", "travel_time": { "value": 2, "unit": "hr" }, "transport_mode": "driving" } ] } } ``` 半径ベースの例: ```json theme={null} { "targeting_overlay": { "geo_proximity": [ { "lat": 51.4700, "lng": -0.4543, "label": "Heathrow Airport", "radius": { "value": 30, "unit": "km" } } ] } } ``` 事前計算されたジオメトリの例(バイヤーがポリゴンを提供): ```json theme={null} { "targeting_overlay": { "geo_proximity": [ { "label": "2hr drive from Düsseldorf", "geometry": { "type": "Polygon", "coordinates": [[[5.87, 50.35], [8.23, 50.35], [8.23, 52.10], [5.87, 52.10], [5.87, 50.35]]] } } ] } } ``` 移動時間のエントリについては、プラットフォームが実際の交通ネットワークに基づいてアイソクロンを地理的境界に解決します。移動手段: `driving`、`walking`、`cycling`、`public_transport`。`geometry` の方法は、(TravelTime、Mapbox などで)すでにアイソクロンを計算したバイヤーがポリゴンを直接渡すことを可能にします——これはルーティングエンジンを持たないセラーの参加も可能にします。 10 か所以上をターゲットするキャンペーンには、代わりにロケーションカタログを持つ `store_catchments` の使用を検討してください。継続的な管理とロケーションごとのレポートをサポートします。`geo_proximity` には除外バリアントがありません——これは設計上の意図であり、「ある地点の近くの全員」を除外することが意味のあるターゲティング制約になることはめったにないためです。 セラーは、自身のプライバシーポリシーと適用される規制に合致した最小エリアのしきい値を強制すべきです(SHOULD)。セラーは `get_adcp_capabilities` で `geo_proximity` のサポートを、どの方法(`radius`、`travel_time`、`geometry`)と移動手段がサポートされるかを指定して宣言しなければなりません。 検証済みの例: ```json theme={null} { "$schema": "/schemas/core/targeting.json", "geo_proximity": [ { "lat": 51.2277, "lng": 6.7735, "label": "Düsseldorf", "travel_time": { "value": 2, "unit": "hr" }, "transport_mode": "driving" } ] } ``` ```json theme={null} { "$schema": "/schemas/core/targeting.json", "geo_proximity": [ { "lat": 51.4700, "lng": -0.4543, "label": "Heathrow Airport", "radius": { "value": 30, "unit": "km" } } ] } ``` ```json theme={null} { "$schema": "/schemas/core/targeting.json", "geo_proximity": [ { "label": "2hr drive from Düsseldorf", "geometry": { "type": "Polygon", "coordinates": [[[5.87, 50.35], [8.23, 50.35], [8.23, 52.10], [5.87, 52.10], [5.87, 50.35]]] } } ] } ``` ## ステークホルダー別の利点 ### バイヤー向け * **シンプルなプランニング**: オーディエンスのニーズを自然に記述 * **透明な価格**: すべてのコストが事前に込み * **複雑さの低減**: ターゲティングの設定が不要 * **より良い成果**: パブリッシャーの専門知識が配信を最適化 ### パブリッシャー向け * **価格の制御**: ターゲティングをプロダクト価格に束ねる * **専門知識の活用**: インベントリとオーディエンスの知識を適用 * **統合の簡素化**: 技術的なターゲティングパラメータが少ない * **市場でのポジショニング**: ターゲティング機能で差別化 ### プラットフォーム向け * **衝突の低減**: 単一のターゲティングソースがレイヤリングの問題を排除 * **クリーンな実装**: より複雑でないターゲティングロジック * **より良いパフォーマンス**: パブリッシャーのインベントリ特性に最適化 ## リアルタイムターゲティングシグナル オーケストレーターは、静的なオーバーレイで表現できるものを超えた動的で高カーディナリティなターゲティングのために、パブリッシャーに**リアルタイムターゲティングシグナル**を提供できます。これらのシグナルは次を可能にします: * **ブランドセーフティ** - リアルタイムのコンテンツフィルタリングと隣接制御 * **ブランド適合性** - ブランド価値とのコンテキスト的整合 * **オーディエンスターゲティング** - リアルタイムで更新される動的なオーディエンスセグメント * **コンテキストターゲティング** - ページレベルまたはモーメントレベルのターゲティング判断 リアルタイムシグナルは [AdCP シグナルプロトコル](/docs/signals/overview) を通じて提供され、オーケストレーターがインプレッション時にターゲティングデータを供給できるようにします。 ### シグナル vs オーバーレイの違い * シグナルはキャンペーンのセットアップ時ではなく、**インプレッション時に評価**されます * シグナルは**より高いカーディナリティ**をサポートします(数十ではなく数千の値) * シグナルはメディアバイを変更せずに**継続的に更新**できます * シグナルは、ブリーフでは表現できない**高度なコンテキストターゲティング**を可能にします ### リアルタイムシグナルを使う場面 ✅ **リアルタイムシグナルを使う場合:** * ブランドセーフティのフィルタリング(安全でないコンテンツをブロック) * ブランド適合性のスコアリング(適した文脈を優先) * 動的なオーディエンスターゲティング(リアルタイムのセグメントメンバーシップ) * コンテキストターゲティング(ページレベルまたはモーメントレベルの判断) * 高カーディナリティなターゲティング(数千の値) * キャンペーンのフライト中に変化するターゲティング ## ローンチ後のキーワード管理 キーワードターゲットとネガティブキーワードはどちらも `update_media_buy` で段階的な操作をサポートし、`targeting_overlay` 全体を置き換える必要をなくします: * **`keyword_targets_add`** — `(keyword, match_type)` のアイデンティティでアップサートします。新しいキーワードを追加するか、既存のものの `bid_price` を更新します。 * **`keyword_targets_remove`** — マッチする `(keyword, match_type)` ペアを削除します。 * **`negative_keywords_add`** — ネガティブを追加します。重複は no-op です。 * **`negative_keywords_remove`** — マッチするペアを削除します。存在しないエントリは no-op です。 ```json theme={null} { "packages": [ { "package_id": "pkg_sponsored_search_001", "keyword_targets_add": [ { "keyword": "trail running shoes", "match_type": "phrase", "bid_price": 0.95 } ], "keyword_targets_remove": [ { "keyword": "running shoes", "match_type": "broad" } ], "negative_keywords_add": [ { "keyword": "diy", "match_type": "broad" }, { "keyword": "how to make running shoes", "match_type": "phrase" } ] } ] } ``` セラーは、同じリクエストに `targeting_overlay.keyword_targets` が `keyword_targets_add` または `keyword_targets_remove` とともに存在する場合、バリデーションエラーを返すべきです(SHOULD)(ネガティブキーワードについても同様)。段階的な操作と完全なオーバーレイの置き換えは、単一の更新内では相互排他的です。 他のオーバーレイフィールドを保ちつつキーワードターゲティングをすべて削除するには、`keyword_targets` フィールドなしで完全な `targeting_overlay` を送ります。 ## 実装要件 ### パブリッシャーが必ず満たすこと: 1. **地理的ターゲティングのサポート**: プラットフォームがサポートする範囲で、地理的な包含と除外のパラメータ(`geo_countries`、`geo_countries_exclude`、`geo_regions`、`geo_regions_exclude`、`geo_metros`、`geo_metros_exclude`、`geo_postal_areas`、`geo_postal_areas_exclude`)を扱います。サポートするメトロと郵便のシステムを `get_adcp_capabilities` で宣言します 2. **ブリーフの解釈**: ブリーフを使って適切なオーディエンスとコンテンツのターゲティングを決定します 3. **ターゲティングの検証**: サポートできないターゲティングを持つメディアバイを拒否します 4. **制限の文書化**: 地理的ターゲティングの制限をプロダクト記述で明確に伝えます ### バイヤーが推奨されること: 1. **まずブリーフを使う**: ほとんどのターゲティングニーズを自然言語ブリーフで表現します 2. **オーバーレイを最小化**: 技術的なターゲティングは地理的制限または RCT テストにのみ使います 3. **パブリッシャーを信頼**: パブリッシャーにブリーフの解釈へインベントリの知識を適用させます 4. **早期に検証**: 技術的なターゲティングを適用する前にプロダクトのケイパビリティを確認します ## ベストプラクティス 1. **ブリーフをデフォルトに** - 自然言語の記述から始めます 2. **明確なブリーフを書く**: オーディエンスとコンテキストの要件を具体的にします 3. **パブリッシャーの専門知識を信頼**: パブリッシャーは自身のインベントリ機能を最もよく知っています 4. **動的なターゲティングにはシグナルを使う** - リアルタイムシグナルは、複雑で高カーディナリティなターゲティングをオーバーレイより上手く扱います 5. **技術的オーバーレイを最小化**: 地理的制限またはコンプライアンスにのみ使います 6. **オーディエンスの適合を検証**: プロダクト記述がキャンペーンゴールと一致することを確認します 7. **すべて込みの価格** - ターゲティングコストがプロダクトレートに組み込まれていることを期待します ## 将来の進化 * **強化されたブリーフ処理**: より高度な自然言語理解 * **オーディエンスの発見**: 利用可能なオーディエンスを探索するためのより良いツール * **より深いシグナル統合**: より高度なリアルタイムターゲティング機能 * **パフォーマンス最適化**: キャンペーン結果に基づく AI 主導のオーディエンス絞り込み ## 関連ドキュメント * **[トラステッドマッチプロトコル(TMP)](/docs/trusted-match)** - インプレッション時のターゲティング、フリークエンシーキャップ、ブランド適合性のためのリアルタイム実行レイヤー * **[シグナルプロトコル](/docs/signals/overview)** - ブランド適合性とコンテキストターゲティングのためのリアルタイムターゲティングシグナル * **[プロダクトディスカバリー](/docs/media-buy/product-discovery/)** - ブリーフがどのようにターゲティングされたプロダクト推奨へつながるか * **[ブリーフ例](/docs/media-buy/product-discovery/example-briefs)** - 効果的なターゲティングブリーフの実例 * **[ポリシーコンプライアンス](/docs/media-buy/media-buys/policy-compliance)** - 自動化されたコンプライアンスチェックと強制 # 標準フォーマット対応の実装 Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/capability-discovery/implementing-standard-formats このガイドは、クリエイティブフォーマットを実装する **セールスエージェント** 向けです。カスタムフォーマットを定義する前に、AdCP エコシステムで既に利用できるフォーマットを確認してください。 ## 実装方針: まず確認し、必要なら拡張 多くのパブリッシャーは IAB 標準サイズ、一般的な動画仕様、広く使われるネイティブフォーマットなど、標準フォーマットの組み合わせをサポートしています。自前で定義するのではなく、次の手順で進めましょう。 1. **リファレンスクリエイティブエージェントを確認** し、必要なフォーマットが既にあるか調べる 2. 要件に合う場合は **既存フォーマットを参照** します 3. **独自要件があるときだけ** カスタムフォーマットを作成します このアプローチにより: * **保守負荷を削減** - 既存フォーマットを自分で維持する必要がない * **クリエイティブの可搬性を向上** - バイヤーが複数パブリッシャーで再利用できます * **エコシステムの一貫性を強化** - 共通フォーマットで同じ仕様を共有できます ## リファレンスクリエイティブエージェント **URL:** `https://creative.adcontextprotocol.org` **ステータス:** 本番サービス(実際に稼働している AdCP クリエイティブエージェント) リファレンスクリエイティブエージェントは、広告業界で広く使われる一般的なクリエイティブフォーマットの公式定義を提供します。 * IAB 標準ディスプレイサイズ(300x250、728x90、320x50 など) * 標準的な動画フォーマット(15s、30s、60s のプレロールなど) * ストリーミングやポッドキャスト挿入用のオーディオフォーマット * デジタルサイネージ向けの DOOH フォーマット * レスポンシブ配置向けネイティブフォーマット * 複数商品を扱うカルーセルフォーマット **カスタムフォーマットを作成する前に、** 必要なフォーマットが既に存在するかリファレンスクリエイティブエージェントに問い合わせてください。 ## 標準フォーマットを使う理由 ### セールスエージェントにとって * **保守負荷なし**: IAB 標準フォーマット定義を複製しなくて済む * **エコシステムの一貫性**: 共通フォーマットで同じ仕様を利用できます * **差別化に注力**: 在庫固有のカスタムフォーマットに時間を使える ### バイヤーにとって * **可搬性**: 1 つのクリエイティブを複数パブリッシャーで利用可能 * **予測可能性**: フォーマット要件が一貫 * **迅速な立ち上げ**: パブリッシャーごとにカスタム制作しなくて済む ## 実装ステップ ### ステップ 1: 必要なフォーマットを洗い出す 在庫が受け付けるクリエイティブフォーマットを列挙します。例: * ディスプレイ: 300x250、728x90、320x50 * 動画: 15 秒プレロール、30 秒プレロール * ネイティブ: レスポンシブネイティブフォーマット ### ステップ 2: リファレンスクリエイティブエージェントを確認 `list_creative_formats` で `https://creative.adcontextprotocol.org` に問い合わせ、必要なフォーマットが既に存在するか確認します。リファレンスエージェントは次を管理しています。 * すべての IAB 標準ディスプレイサイズ * 一般的な動画尺とアスペクト比 * 標準オーディオフォーマット * DOOH の仕様 * ネイティブ広告フォーマット ### ステップ 3: 参照と定義を決める **参照するケース:** * 技術要件に完全一致するフォーマットがあります * IAB 標準仕様で作られたクリエイティブを受け入れる * パブリッシャー間でクリエイティブの可搬性を高めたい **カスタムフォーマットを定義するケース:** * 独自の技術要件がある(カスタム寸法、特殊アセットの必要など) * パブリッシャー固有の検証や組み立てロジックが必要 * プレミアムで差別化された広告体験を提供します ### ステップ 4: レスポンスを実装 `list_creative_formats` を実装する際、標準フォーマットをサポートする場合はリファレンスクリエイティブエージェントを含めます。 **最も一般的: 標準フォーマットを参照** 在庫が標準フォーマットを受け入れる場合(ほとんどのパブリッシャーが該当): ```json theme={null} { "formats": [], "creative_agents": [ "https://creative.adcontextprotocol.org" ] } ``` これは「リファレンスクリエイティブエージェントが提供する標準フォーマットをすべてサポートしています」という意味になります。 **カスタムフォーマットもある場合: 両方を組み合わせる** 固有フォーマットに加えて標準フォーマットもサポートする場合: ```json theme={null} { "formats": [ { "format_id": { "agent_url": "https://youragent.com", "id": "homepage_takeover" }, "name": "Homepage Takeover", "type": "rich_media", "assets": [...] } ], "creative_agents": [ "https://creative.adcontextprotocol.org" ] } ``` これは「カスタムの Homepage Takeover に加え、リファレンスクリエイティブエージェントの標準フォーマットもすべてサポートします」という意味です。 **カスタムフォーマットのみの場合: リファレンスを省略** まれですが、真に独自の要件を持つカスタムフォーマットしかサポートしない場合: ```json theme={null} { "formats": [ { "format_id": { "agent_url": "https://youragent.com", "id": "custom_holographic_display" }, "name": "Holographic Display Format", "type": "dooh", "assets": [...] } ] } ``` **注意:** これはまれです。多くのパブリッシャーは少なくとも一部の標準フォーマット(300x250 など)を受け入れるため、通常はリファレンスクリエイティブエージェントの URL を含めます。 ## 含まれる標準フォーマット リファレンスクリエイティブエージェントは主要チャネルを横断してフォーマットを提供します。 * **[Display Formats](/docs/creative/channels/display)** - IAB 標準バナーサイズ(300x250、728x90、320x50 など) * **[Video Formats](/docs/creative/channels/video)** - 標準的な動画広告仕様(15s、30s、縦型、CTV) * **[Audio Formats](/docs/creative/channels/audio)** - ストリーミングオーディオやポッドキャスト挿入用フォーマット * **[DOOH Formats](/docs/creative/channels/dooh)** - デジタルサイネージや交通広告の仕様 * **[Carousel Formats](/docs/creative/channels/carousels)** - 複数商品やスライドショー向けフォーマット 各フォーマットには次が含まれます。 * 正確な技術要件(寸法、尺、ファイルタイプなど) * 必須/任意アセットの仕様 * ユニバーサルマクロのサポート * プレビューとバリデーション機能 ## フォーマットディスカバリーの流れ バイヤーがセールスエージェントからフォーマットを取得する際: 1. **バイヤーが** セールスエージェントへ `list_creative_formats` を呼び出す 2. **レスポンスに** カスタムフォーマットと `creative_agents: ["https://creative.adcontextprotocol.org"]` を含めます 3. **バイヤーが再帰的に** リファレンスエージェントへ問い合わせ、標準フォーマットを取得 4. **バイヤーは** カスタムフォーマットと標準フォーマットを合わせた一覧を確認 バイヤーは問い合わせた URL を追跡し、無限ループを避けます。 ## セールスエージェント向けベストプラクティス ### ✅ 標準フォーマットを参照します ```json theme={null} { "creative_agents": [ "https://creative.adcontextprotocol.org" ] } ``` **いつ:** 特別な要件がなく、在庫が IAB 標準サイズを受け入れる場合 **理由:** 保守を削減し、一貫性を確保し、バイヤーは既存クリエイティブを流用できます ### ✅ カスタムフォーマットを定義します ```json theme={null} { "formats": [ { "format_id": { "agent_url": "https://youragent.com", "id": "native_feed_card" }, "type": "native" } ] } ``` **いつ:** 独自のインベントリ体験や特定の技術要件がある場合 **理由:** 差別化やプレミアム在庫の提供が可能になります ### ❌ 標準フォーマットを複製しません ```json theme={null} { "formats": [ {"format_id": {"agent_url": "https://youragent.com", "id": "display_300x250"}}, {"format_id": {"agent_url": "https://youragent.com", "id": "display_728x90"}}, {"format_id": {"agent_url": "https://youragent.com", "id": "display_320x50"}}, // ... copying 50+ standard formats ] } ``` **ダメな理由:** 保守負荷が高く、バージョンドリフトやエコシステム間の非一貫性を招く **例外:** これらのフォーマットに対してカスタムの検証/プレビューが必要な場合 ### ✅ 必要に応じて両方使います ```json theme={null} { "formats": [ // Your differentiating formats ], "creative_agents": [ "https://creative.adcontextprotocol.org" ] } ``` **結果:** バイヤーはカスタムフォーマットに加え、すべての標準フォーマットを確認できます ## フォーマット ID の名前空間 複数のエージェントがフォーマットを定義しても衝突しないよう、AdCP はフォーマット識別子に **名前空間パターン** を用います。 ### 名前空間パターン: `{domain}:{format_id}` **構造:** ``` domain:format_id ``` **例:** * `creative.adcontextprotocol.org:display_300x250` * `creative.adcontextprotocol.org:video_30s_hosted` * `youragent.com:homepage_takeover_2024` * `publisher.example:native_feed_card` ### ドメインの要件 名前空間付き format\_id のドメインは次を満たす必要があります。 1. `{domain}/.well-known/adagents.json` で **有効なエージェントカードをホスト** します 2. エージェントカードの拡張で **MCP エンドポイント** を宣言します 3. 標準のエージェントカード欄で **A2A エンドポイント** を宣言します **例: `https://youragent.com/.well-known/adagents.json` のエージェントカード** ```json theme={null} { "agents": [ { "agent_url": "https://youragent.com", "agent_name": "Your Creative Agent", "protocols": ["mcp", "a2a"], "mcp_endpoint": "https://youragent.com/mcp", "a2a_endpoint": "https://youragent.com/a2a", "capabilities": ["list_creative_formats", "preview_creative"] } ] } ``` これにより、その名前空間のドメインがフォーマット仕様とバリデーションを提供できる有効なエージェントであることが保証されます。 ### 名前空間を使うべきタイミング フォーマットを定義する際は **常に名前空間付き format\_id** を使用してください。 ```json theme={null} { "formats": [ { "format_id": { "agent_url": "https://youragent.com", "id": "homepage_takeover" }, "name": "Homepage Takeover", "type": "rich_media" } ] } ``` **メリット:** * **衝突なし** - 各エージェントが自分の名前空間を保有 * **所有者が明確** - ドメインが権威あるエージェントを示します * **発見可能** - バイヤーがドメインのエージェントカードを取得できます * **検証可能** - エージェントカードでドメイン所有を証明 ### エージェント種別別の名前空間例 **リファレンスクリエイティブエージェント:** ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" } } ``` **パブリッシャーセールスエージェント:** ```json theme={null} { "format_id": { "agent_url": "https://youragent.com", "id": "custom_format" } } ``` **DCO プラットフォーム:** ```json theme={null} { "format_id": { "agent_url": "https://dco.example", "id": "dynamic_creative_v2" } } ``` ### 衝突の解消 名前空間付き format\_id では、各ドメインが自分の名前空間を管理するため衝突は **発生しません**。 **衝突しない例:** ```json theme={null} // Two different formats, both valid { "format_id": { "agent_url": "https://publisher-a.com", "id": "video_30s" } } { "format_id": { "agent_url": "https://publisher-b.com", "id": "video_30s" } } ``` バイヤーが複数のソースから同じ名前空間付き format\_id を受け取った場合、それらは **同一のフォーマット** です。名前空間が同一性を保証します。 ### バリデーションルール 1. **ドメインは agent\_url のドメインと一致していること:** ```json theme={null} // ✅ Valid - domain matches { "format_id": { "agent_url": "https://youragent.com", "id": "format_x" } } // ❌ Invalid - domain mismatch { "format_id": { "agent_url": "https://otheragent.com", "id": "format_x" } } ``` 2. **ドメインは有効なエージェントカードを持つこと:** * `{domain}/.well-known/adagents.json` にエージェントカードが存在します * MCP および/または A2A エンドポイントを宣言しています * エンドポイントが機能しています 3. **format\_id はパターンに従うこと:** * `{domain}:{format_id}` 構造 * ドメインは有効な DNS ホスト名 * format\_id は英数字とアンダースコア/ハイフン ### 非名前空間 ID からの移行 以前に `display_300x250` のようなシンプルな ID を使用していた場合は、名前空間付きに移行します。 **移行前:** ```json theme={null} { "format_id": { "agent_url": "https://youragent.com", "id": "display_300x250_old" } } ``` **移行後:** ```json theme={null} { "format_id": { "agent_url": "https://youragent.com", "id": "display_300x250" } } ``` 必要に応じて移行期間中は両方をサポートしても構いませんが、新しい実装では最初から名前空間付き ID を使用してください。 ## 権威としてのリファレンスエージェント 各フォーマットは `format_id` の `agent_url` で権威となるソースを示します。 ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, "name": "Medium Rectangle", "type": "display" } ``` この URL のクリエイティブエージェントが次の決定的なソースとなります。 * フォーマット仕様の完全版 * アセット要件とバリデーションルール * プレビュー生成 * レンダリング手順 ## 標準フォーマットを使わないケース 次の場合は独自フォーマットを定義します。 1. **固有の技術要件** - プラットフォームが IAB 標準とは異なる仕様を必要とします 2. **カスタム検証** - 追加のクリエイティブ審査や承認が必要 3. **独自の組み立て** - レンダリングパイプラインに特別な要件があります 4. **プレミアム体験** - 標準フォーマットではカバーできない差別化された商品 これらの場合でも、基本的な在庫には標準フォーマットを参照しつつ、プレミアム枠にカスタムフォーマットを定義できます。 ## 実装に関する補足 ### フォーマット権威のパターン `agent_url` フィールドにより **分散型のフォーマット権威** モデルが成立します。 * リファレンスエージェントは IAB 標準の権威 * 各パブリッシャーは自社カスタムフォーマットの権威 * バイヤーは正しい権威を発見し、検証できます ### バージョン管理 リファレンスクリエイティブエージェントはフォーマットのバージョンと互換性を管理します。 * フォーマット定義は業界標準に合わせて進化 * 後方互換性を維持 * バイヤーは安定した format\_id を前提にできます ### 何が「標準」か **標準フォーマット** とは、`https://creative.adcontextprotocol.org` のリファレンスクリエイティブエージェントが定義し、次に基づくものです。 1. **業界仕様** - IAB 標準、VAST/VPAID 仕様、一般的な広告ユニットサイズ 2. **クロスプラットフォーム互換性** - 複数パブリッシャーでカスタマイズなしに動作 3. **安定した定義** - エコシステム全体の一貫性のためにバージョン管理されます **プロトコルの観点:** プロトコルレベルでは標準フォーマットも他のフォーマットと同じで、特別な API 処理はありません。`agent_url` が権威となるエージェントを示す点はカスタムフォーマットと同様です。 **エコシステムの観点:** 標準フォーマットは可搬性を提供します。バイヤーは 1 つのクリエイティブを作成し、同じフォーマット定義を参照する多くのパブリッシャーで利用できます。 ## 関連ドキュメント * [Creative Protocol Overview](/docs/creative) - フォーマット、マニフェスト、エージェントの連携 * [Creative Formats](/docs/creative/formats) - フォーマット仕様とディスカバリーの理解 * [Channel Guides](/docs/creative/channels/video) - 媒体別の詳細フォーマットガイド * [list\_creative\_formats Task](/docs/creative/task-reference/list_creative_formats) - フォーマットディスカバリーの API リファレンス # 概要 Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/capability-discovery/index AdCP におけるクリエイティブフォーマットとプロパティ認可を理解し、効果的な広告ワークフローの基盤を築く。 AdCP で効率的に広告を購入するには、まず **どのクリエイティブフォーマットがサポートされているか** と **営業エージェントがどのプロパティを代表する権限を持つか** の 2 つの基本的な機能を理解する必要があります。本セクションでは、AdCP の広告エコシステムの基盤となるツールと概念を解説します。 ## What You'll Learn ### [Implementing Standard Format Support](/docs/media-buy/capability-discovery/implementing-standard-formats) 🎨 営業エージェントがリファレンスクリエイティブエージェントを通じて標準クリエイティブフォーマットをサポートする方法を学びます。次の内容を扱います。 * 標準 IAB フォーマットを複製せずに参照します * 独自の在庫向けにカスタムフォーマットを実装します * フォーマット ID の名前空間で衝突を防ぐ * 標準フォーマットとカスタムフォーマットを組み合わせてサポートします * 広告プロダクトとのフォーマット互換性を検証します * 標準フォーマットでは Standard Creative Agent を活用します * カスタムフォーマットではパブリッシャー固有のクリエイティブエージェントと連携します ### [Understanding Authorization](/docs/governance/property/authorized-properties) 🔐 AdCP が無断転売を防ぎ、営業エージェントの正当性を担保する方法を解説します。次の点を理解します。 * デジタル広告における無断転売の問題 * パブリッシャーが `adagents.json` を通じて営業エージェントを認可する方法 * 購入前に営業エージェントの認可を検証する手順 * プロパティタグと大規模な認可管理 **プロトコル横断の基盤**: `adagents.json` 認可システムは、すべての AdCP プロトコル(Media Buy、Signals、将来の Curation)で共通に適用されます。実装の詳細は [adagents.json specification](/docs/governance/property/adagents) を参照してください。 ## 基盤となるタスク 以下のキャパビリティディスカバリータスクは、AdCP ワークフローに必要なリファレンスデータを提供します。 ### [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) 主要なキャパビリティディスカバリータスクです。プロトコルバージョン、サポート機能、ポートフォリオ情報、ガバナンス機能を 1 回の呼び出しで返します。AdCP エージェントとの最初のやり取りとして使用してください。 ### [`list_creative_formats`](/docs/creative/task-reference/list_creative_formats) サポートされるすべてのクリエイティブフォーマットを発見し、寸法、ファイルタイプ、長さの上限、技術要件などの詳細仕様を確認します。 ## インテグレーションパターン キャパビリティディスカバリーは、通常 AdCP ワークフローの早い段階で行います。 1. **機能を把握**: [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) でエージェントがサポートする機能を確認します 2. **フォーマットを理解**: [`list_creative_formats`](/docs/creative/task-reference/list_creative_formats) で対応するクリエイティブタイプを確認します 3. **認可を検証**: capabilities の `media_buy.portfolio` を確認し、パブリッシャーの `adagents.json` で検証します 4. **プロダクトを発見**: [`get_products`](/docs/media-buy/task-reference/get_products) で広告在庫を検索します 5. **クリエイティブを計画**: 発見したプロダクトを利用可能なフォーマットに対応付け、制作計画を立てる 6. **キャンペーンを実行**: フォーマット互換性と認可を確信した上でメディアバイを作成します ## 重要性 ### クリエイティブフォーマット * **制作計画**: アセット作成前に要件を把握します * **クリエイティブエージェント**: AI エージェントを活用してクリエイティブを構築・検証します * **プラットフォーム互換性**: 複数の広告プラットフォームで動作することを確認します * **コスト効率**: 仕様不一致によるアセット再作成を防ぐ * **品質保証**: 技術的基準を満たし最適なパフォーマンスを実現します * **プレビュー機能**: キャンペーン開始前にクリエイティブの描画をテストします ### 認可されたプロパティ * **不正防止**: 無許可の販売者や在庫詐欺を回避します * **ブランドセーフティ**: 正規のプロパティ所有者から購入していることを保証します * **法令遵守**: 認可された取引の監査証跡を維持します * **信頼構築**: 広告サプライチェーンへの信頼を醸成します これらの機能が組み合わさり、AdCP を通じた安全で効率的かつ効果的な広告配信の基盤となります。 ## 関連ドキュメント * **[Task Reference](/docs/media-buy/task-reference/)** - 完全な API ドキュメント * **[Product Discovery](/docs/media-buy/product-discovery/)** - 広告在庫の探索 * **[Creatives](/docs/media-buy/creatives/)** - クリエイティブアセット管理 * **[Creative Protocol](/docs/creative/)** - クリエイティブエージェントとマニフェスト * **[Creative Channel Guides](/docs/creative/channels/video)** - フォーマットの例とパターン * **[Creative Manifests](/docs/creative/creative-manifests)** - クリエイティブ仕様の理解 * **[adagents.json Specification](/docs/governance/property/adagents)** - すべての AdCP プロトコルに適用されるパブリッシャー認可システム # コンバージョントラッキングと最適化ゴール Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/conversion-tracking/index AdCP のコンバージョントラッキング — sync_event_sources でピクセルとイベントソースを設定し、log_event でコンバージョンイベントを送信し、キャンペーン配信に最適化ゴールを設定します。 AdCP のコンバージョントラッキングは、広告費と事業成果を結びつける。ライフサイクルを管理するタスクは2つある。[`sync_event_sources`](/docs/media-buy/task-reference/sync_event_sources) はイベントの収集元を設定し、[`log_event`](/docs/media-buy/task-reference/log_event) はイベント自体を送信します。 イベントデータは配信レポート(コンバージョン数、ROAS、獲得単価)にフィードされ、メディアバイパッケージの最適化ゴールを有効にします。 ## フロー ```mermaid theme={null} sequenceDiagram participant B as Buyer participant S as Seller rect rgb(240, 248, 255) Note over B,S: Setup B->>S: sync_event_sources (configure sources) S->>B: Setup instructions (snippets, pixel URLs) B->>B: Install snippets on site/app end rect rgb(240, 255, 240) Note over B,S: Event collection B->>S: log_event (send conversions) S->>S: Match users, attribute conversions end rect rgb(255, 248, 240) Note over B,S: Optimization B->>S: create_media_buy (with optimization_goals) S->>S: Optimize delivery toward conversions B->>S: get_media_buy_delivery S->>B: Conversion metrics (ROAS, CPA) end ``` これは推奨される順序を示しています。実際には、イベントが流れる前にメディアバイを作成することもできます。セラーは十分なイベント履歴が蓄積されてから最適化を開始します。 ## イベントソース イベントソースは、コンバージョンイベントを収集するチャネルを表します。ウェブサイトピクセル、モバイル SDK、サーバー間連携、CRM インポートなどがあります。 [`sync_event_sources`](/docs/media-buy/task-reference/sync_event_sources) でイベントソースを設定します。`event_source_id`、任意の `name`、`event_types`、`allowed_domains` を指定します。レスポンスには各ソースの追加フィールドが含まれます。 | フィールド | 型 | 説明 | | --------------- | ------------------------------- | ---------------------------------------------------------- | | `seller_id` | string | セラーの広告プラットフォームが割り当てた識別子 | | `action` | string | 発生したこと: `created`、`updated`、`unchanged`、`deleted`、`failed` | | `managed_by` | string | `buyer`(自分が設定した)または `seller`(常時オン、セラー管理) | | `action_source` | [ActionSource](#action-sources) | イベントソースの種類(ウェブサイトピクセル、アプリ SDK など) | | `setup` | object | 実装の詳細 — スニペットコード、スニペットタイプ、手順 | ### バイヤー管理 vs セラー管理 **バイヤー管理**ソースは `sync_event_sources` を通じて自分が設定するものです。イベントタイプ、ドメイン、ライフサイクルを自分で管理します。 **セラー管理**ソースは常時オンで、レスポンスに `managed_by: "seller"` として現れる。これはコマースメディアでよく見られ、リテーラーが組み込みアトリビューション(例:自社プラットフォームでの購入トラッキング)を提供する場合に使われます。`conversion_tracking.platform_managed: true` を持つプロダクトは、セラーがこれらのソースを提供することを示しています。 アカウント上のすべてのソース(セラー管理のものを含む)を検出するには、`event_sources` 配列を指定せずに `sync_event_sources` を呼び出す。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/media-buy/sync-event-sources-request.json", "account": { "account_id": "acct_12345" } } ``` ## イベント イベントは、購入、リード送信、ページビュー、アプリインストール、その他の[標準イベントタイプ](#event-types)といったユーザーアクションを表します。 [`log_event`](/docs/media-buy/task-reference/log_event) でイベントを送信します。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/event.json", "event_id": "evt_purchase_12345", "event_type": "purchase", "event_time": "2026-01-15T14:30:00Z", "action_source": "website", "event_source_url": "https://www.example.com/checkout/confirm", "user_match": { "hashed_email": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2", "click_id": "abc123def456", "click_id_type": "gclid" }, "custom_data": { "value": 149.99, "currency": "USD", "order_id": "order_98765", "num_items": 3 } } ``` | フィールド | 型 | 必須 | 説明 | | ------------------- | ------------------------------- | --- | ------------------------------------------------------------- | | `event_id` | string | Yes | 重複排除のための一意識別子(event\_type + event\_source\_id のスコープ)。最大256文字。 | | `event_type` | [EventType](#event-types) | Yes | 標準イベントタイプ | | `event_time` | date-time | Yes | イベント発生時刻の ISO 8601 タイムスタンプ | | `user_match` | [UserMatch](#user-match) | No | アトリビューションマッチングのためのユーザー識別子 | | `custom_data` | [CustomData](#custom-data) | No | イベント固有のデータ(value、currency、items) | | `action_source` | [ActionSource](#action-sources) | No | イベントが発生した場所 | | `event_source_url` | uri | No | イベントが発生した URL(action\_source が `website` の場合は必須) | | `custom_event_name` | string | No | カスタムイベントの名前(event\_type が `custom` の場合) | イベントは `event_id` + `event_type` + `event_source_id` で重複排除されます。同じイベントを複数回送信しても安全です。 ## ユーザーマッチ ユーザー識別子により、セラーはコンバージョンを広告インプレッションにアトリビュートできます。利用可能な最も強力な識別子を提供すること。識別子が多いほどマッチ率が高くなります。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/user-match.json", "hashed_email": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2", "uids": [ { "type": "uid2", "value": "AbC123XyZ..." } ], "click_id": "abc123def456", "click_id_type": "gclid" } ``` 少なくとも1つの識別子が必要です。強いものから弱いものへの順序: | フィールド | 型 | マッチ品質 | 説明 | | ------------------- | ------ | ----- | ---------------------------------------------------------- | | `uids` | UID\[] | 確定的 | ユニバーサル ID の値(`rampid`、`id5`、`uid2`、`euid`、`pairid`、`maid`) | | `hashed_email` | string | 確定的 | 小文字・トリム済みメールアドレスの SHA-256 ハッシュ(64文字の16進数) | | `hashed_phone` | string | 確定的 | E.164 形式の電話番号の SHA-256 ハッシュ(64文字の16進数) | | `click_id` | string | 確定的 | プラットフォームのクリック識別子(fbclid、gclid、ttclid など) | | `click_id_type` | string | — | クリック識別子の種類 | | `client_ip` | string | 確率的 | クライアント IP アドレス(`client_user_agent` が必要) | | `client_user_agent` | string | 確率的 | クライアントユーザーエージェント(`client_ip` が必要) | **ハッシュ化**: ハッシュ前に正規化すること。メールアドレスは小文字にして空白をトリムし、電話番号は E.164 形式(例: `+12065551234`)にします。SHA-256 でハッシュ化し、64文字の小文字16進数として出力します。 利用可能な場合は複数の識別子タイプを送信すること。セラーは最善のマッチを使用します。 ## カスタムデータ アトリビューションとレポートのためのイベント固有データ。購入イベントでは、ROAS レポートを有効にするために常に `value` と `currency` を含めること。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/event-custom-data.json", "value": 149.99, "currency": "USD", "order_id": "order_98765", "content_ids": ["SKU-1234", "SKU-5678"], "num_items": 3, "contents": [ { "id": "SKU-1234", "quantity": 2, "price": 49.99, "brand": "Acme" }, { "id": "SKU-5678", "quantity": 1, "price": 50.01, "brand": "Nova" } ] } ``` | フィールド | 型 | 説明 | | ------------------ | ---------- | ---------------------------------------------- | | `value` | number | イベントの金銭的価値 | | `currency` | string | ISO 4217 通貨コード(例: `USD`、`EUR`、`GBP`) | | `order_id` | string | 一意の注文または取引識別子 | | `content_ids` | string\[] | 商品またはコンテンツの識別子 | | `content_type` | string | コンテンツのカテゴリー(product、service など) | | `content_name` | string | 商品またはコンテンツの名前 | | `content_category` | string | 商品またはコンテンツのカテゴリー | | `num_items` | integer | イベントのアイテム数 | | `search_string` | string | 検索クエリ(検索イベントの場合) | | `contents` | Content\[] | アイテムごとの詳細: `id`(必須)、`quantity`、`price`、`brand` | ## イベントタイプ IAB ECAPI に準拠した標準マーケティングイベントタイプ: | イベントタイプ | 説明 | | ----------------------- | ------------------------------------ | | `page_view` | ユーザーがページを閲覧した | | `view_content` | ユーザーが特定のコンテンツ(商品、記事など)を閲覧した | | `select_content` | ユーザーがコンテンツを選択またはクリックした | | `select_item` | ユーザーがリストから特定の商品またはアイテムを選択した | | `search` | ユーザーが検索を実行した | | `share` | ユーザーがソーシャルまたはメッセージングでコンテンツをシェアした | | `add_to_cart` | ユーザーがカートにアイテムを追加した | | `remove_from_cart` | ユーザーがカートからアイテムを削除した | | `viewed_cart` | ユーザーがショッピングカートを閲覧した | | `add_to_wishlist` | ユーザーがウィッシュリストにアイテムを追加した | | `initiate_checkout` | ユーザーがチェックアウトプロセスを開始した | | `add_payment_info` | ユーザーが支払い情報を追加した | | `purchase` | ユーザーが購入を完了した | | `refund` | 購入が全額または一部払い戻された(ROAS を調整します) | | `lead` | ユーザーが関心を示した(フォーム送信、サインアップなど) | | `qualify_lead` | リードが営業またはスコアリング基準で適格とされた | | `close_convert_lead` | リードが顧客に転換したまたはディールがクローズした | | `disqualify_lead` | リードが不適格とされたまたは見込みなしとマークされた | | `complete_registration` | ユーザーがアカウント登録を完了した | | `subscribe` | ユーザーがサービスまたはニュースレターを購読した | | `start_trial` | ユーザーが無料トライアルを開始した | | `app_install` | ユーザーがアプリケーションをインストールした | | `app_launch` | ユーザーがアプリケーションを起動した | | `contact` | ユーザーが連絡を開始した(電話、メッセージなど) | | `schedule` | ユーザーが予約またはイベントをスケジュールした | | `donate` | ユーザーが寄付をした | | `submit_application` | ユーザーが申し込みを送信した(ローン、求人など) | | `custom` | カスタムイベントタイプ(`custom_event_name` で指定) | ## アクションソース コンバージョンイベントの発生元: | アクションソース | 説明 | | ------------------ | --------------------------- | | `website` | ウェブサイト上でイベントが発生した | | `app` | モバイルまたはデスクトップアプリ内でイベントが発生した | | `offline` | オフラインでイベントが発生した(インポートデータ) | | `phone_call` | 電話から発生したイベント | | `chat` | チャット会話から発生したイベント | | `email` | メールのインタラクションから発生したイベント | | `in_store` | 実店舗でイベントが発生した | | `system_generated` | 自動化システムによって生成されたイベント | | `other` | その他のソース(`ext` で指定) | ## イベントサーフェス `action_source` は互換性のため意図的にフラットなままです。最適化に関連するソースがより多くの構造を必要とする場合——特に自社プラットフォームのプロパティにおけるクリエイターやコンテンツのエンゲージメント——には `surface` を使います。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/core/event-surface.json", "category": "owned_property", "property_type": "channel", "namespace": "video_platform", "property_id": "channel_123" } ``` | Field | Type | Description | | --------------- | ------ | ----------------------------------------------------------------------------------------------------------------------- | | `category` | enum | 閉じた汎用カテゴリ: `owned_property`、`website`、`app`、`offline`、`phone_call`、`chat`、`email`、`in_store`、`system_generated`、`other` | | `property_type` | string | プロパティ種別のオープンな語彙。`channel`、`profile`、`feed`、`list`、`podcast`、`playlist`、`newsletter` など | | `namespace` | string | 自由形式のプラットフォーム、パブリッシャー、システムの名前空間。`video_platform`、`short_video_app`、`audio_service` など。列挙ではありません | | `property_id` | string | `namespace` 内のプロパティの任意の識別子 | `owned_property` では `property_type` と `namespace` の両方を設定し、プラットフォームが安定したプロパティ識別子を公開している場合は常に `property_id` を含めてください。 `surface.category` が `action_source` の値と一致する場合、プロデューサーは `action_source` を同じ値に設定すべきです。`owned_property` では、古いコンシューマーのために最も近い互換のフラット値を保ちます——プラットフォームネイティブなイベントでは一般に `system_generated`、より近い値がない場合は `other`。生のコンバージョン起点のようなプラットフォームネイティブの詳細は引き続き `ext` に載せられますが、共有される最適化の意味は `event_type`、`action_source`、`surface` に属します。 ## イベントソースの健全性 イベントソースの品質を評価するセラーは、[`sync_event_sources`](/docs/media-buy/task-reference/sync_event_sources) のレスポンスで各ソースに `health` オブジェクトを含めます。これは、Snap の Event Quality Score(EQS)や Meta の Event Match Quality(EMQ)のようなプラットフォーム固有の品質スコアの AdCP における等価物です。 `status` フィールドが AdCP 標準のスコアです——すべてのセラー間で比較可能です: | Status | Meaning | | -------------- | ------------------------------------ | | `insufficient` | セットアップ未完了、またはイベント品質が低すぎる——最適化を実行できない | | `minimum` | 機能はするが、データ品質が最適化の有効性を制限する | | `good` | ほとんどの最適化目標について品質閾値を満たす | | `excellent` | すべての次元で品質閾値を上回る | バイヤーエージェントは、`detail` ではなく `status` に基づいて判断すべきです。任意の `detail` オブジェクトは、人間向けダッシュボードや高度な診断のためにセラー固有のスコアリング(例: Snap の 0〜10 の EQS、Meta の 0〜10 の EMQ)を含みますが、スケールはセラーによって異なり、プラットフォーム間で比較できません。 | Field | Type | Description | | --------------------- | --------- | ------------------------------------------------------------------------------------------------------ | | `status` | string | AdCP 標準の健全性レベル。セラー横断の判断に使用します。 | | `detail` | object | セラー固有の `score`、`max_score`、任意の `label`。セラーがネイティブな品質スコアを持つ場合にのみ存在します。 | | `match_rate` | number | 広告インタラクションに一致したイベントの割合(0.0〜1.0)。低い率は user\_match 識別子が弱いことを示します。マッチ率を計算するセラー(Snap、Meta)からのみ利用可能。 | | `last_event_at` | date-time | 受信した最新イベントのタイムスタンプ。 | | `evaluated_at` | date-time | この健全性評価が計算された時刻。古い評価の検出に使用します。 | | `events_received_24h` | integer | 過去 24 時間に受信したイベント数。 | | `issues` | array | `severity` と `message` を持つ実行可能な問題。セラーは最も実行可能な上位 3〜5 件に限定すべきです。バイヤーエージェントは配列の位置に頼らず severity でソートすべきです。 | 健全性はアカウント単位ではなく、イベントソース単位で報告されます。健全なウェブサイトピクセルと壊れたアプリ SDK を持つバイヤーは、それぞれで異なる健全性を見ます。 **`health` が不在の場合**、セラーはイベントソースの品質を評価していません。バイヤーエージェントは健全性によるゲーティングなしで進めるべきです——セラーが内部で品質を扱います。不在の健全性を `insufficient` として扱わないでください。 ### セラーによる健全性の計算方法 ネイティブな API アクセス可能の品質スコア(Snap EQS、Meta EMQ)を持つセラーは、それらを `status` と `detail` で直接中継します。ほとんどのセラーはネイティブスコアを持たず、運用メトリクスから `status` を導出します: * **`insufficient`**: タグが非アクティブ、または `events_received_24h` が 0 * **`minimum`**: タグはアクティブだが、低ボリュームまたは高エラー率 * **`good`**: 安定して発火、妥当なボリューム、コアイベントタイプをカバー * **`excellent`**: 高ボリューム、低エラー、拡張マッチングが有効 セラーがレポートデータから健全性を計算する場合、`evaluated_at` のタイムスタンプが評価の鮮度をバイヤーに伝えます。24 時間より古い評価は、タグ設定やイベントボリュームの最近の変更を反映していない可能性があります。これらのセラーでは `detail` オブジェクトは不在です——中継すべきネイティブスコアがありません。 **スキーマ**: [`/schemas/v3/core/event-source-health.json`](https://adcontextprotocol.org/schemas/v3/core/event-source-health.json) ## 計測レディネス イベントベースの最適化をサポートするプロダクトは、[`get_products`](/docs/media-buy/task-reference/get_products) のレスポンスに `measurement_readiness` オブジェクトを含められます。これは、バイヤーのイベント設定がそのプロダクトが効果的に最適化するのに十分かどうかを伝えます。 | Field | Type | Description | | ---------------------- | ------------ | -------------------------------------------------------- | | `status` | string | AdCP 標準のレベル: `insufficient`、`minimum`、`good`、`excellent` | | `required_event_types` | EventType\[] | このプロダクトが必要とするイベントタイプ | | `missing_event_types` | EventType\[] | バイヤーが設定していない必須タイプ | | `issues` | array | `severity` と `message` を持つ実行可能な問題 | | `notes` | string | セラーの説明または推奨事項 | 計測レディネスは、バイヤーのアカウントのコンテキストでプロダクトごとに評価されます。同じプロダクトでも、イベントソースの設定に応じてバイヤーごとに異なるレディネスを示します。 **`measurement_readiness` が不在の場合**、そのプロダクトはイベントベースの最適化を使わない(CTV の認知、保証付きディスプレイ)か、セラーがレディネス評価を提供していないかのいずれかです。どちらの場合も、バイヤーエージェントはそのプロダクトを実行可能として扱うべきです。不在のレディネスを `insufficient` として扱わないでください。 イベントソースの健全性と異なり、計測レディネスには `evaluated_at` タイムスタンプがありません——バイヤーの現在のイベントソース設定を使って、`get_products` の呼び出しごとに新しく評価されます。 ### セラー横断のバイヤーエージェントのパターン 複数のセラーと対話するバイヤーエージェントは、どこでも機能する一組のルールを書きます。`insufficient` 以外のステータスは、そのプロダクトが最適化できることを意味します——問題はどれだけうまくやれるかです。標準化された `status` フィールドにより、セラーごとの統合コードは不要です: ```javascript test=false theme={null} // Works across all sellers — no seller-specific logic for (const seller of sellers) { const sources = await seller.syncEventSources({ account: seller.account }); // Surface issues from any seller — sort by severity, don't rely on array position for (const source of sources.event_sources) { if (source.health?.status === "insufficient") { surfaceIssues(source.health.issues ?? []); } } const products = await seller.getProducts({ account: seller.account, buying_mode: "brief", brief: campaign.brief, }); for (const product of products.products) { const mr = product.measurement_readiness; // Absent = no event-based optimization needed (CTV, awareness), treat as viable if (!mr) { viable.push(product); continue; } // For DR products, require good or better if (campaign.goal === "conversions" && mr.status === "minimum") { warnings.push({ product, reason: "Event setup is functional but limits optimization" }); viable.push(product); // Still viable, but flag it } else if (mr.status !== "insufficient") { viable.push(product); } else { skipped.push({ product, issues: mr.issues }); } } } ``` **スキーマ**: [`/schemas/v3/core/measurement-readiness.json`](https://adcontextprotocol.org/schemas/v3/core/measurement-readiness.json) ### 信頼境界 `issues[].message`、`measurement_readiness.notes`、`detail.label` の各フィールドはセラー提供の自由テキストです。バイヤーエージェントはこれらを信頼できないコンテンツとして扱うべきです——信頼境界なしに LLM のシステムプロンプトへ直接渡したり、意思決定の入力として使ったりしないでください。人間に表示したり、情報提供のコンテキストに含めたりするのは安全ですが、エージェントの制御フローに影響を与えるべきではありません。 ## 最適化ゴール 最適化ゴールは、セラーに対して何に向けて配信を最適化するかを伝える。[`create_media_buy`](/docs/media-buy/task-reference/create_media_buy#campaign-with-conversion-optimization) のパッケージに設定します。パッケージはゴールの配列を受け付け、各ゴールにはオプションの `priority`(1が最高)を指定できます。プロダクトは、パッケージが持てるゴール数を制限する場合に `max_optimization_goals` を宣言する(ほとんどのソーシャルプラットフォームは1つのみ受け付ける)。 **スキーマ**: [`/schemas/v3/core/optimization-goal.json`](https://adcontextprotocol.org/schemas/v3/core/optimization-goal.json) ゴールは `kind` で識別される2種類があります。 * **`kind: "metric"`** — セラーがトラッキングする配信メトリクス(クリック、ビュー、エンゲージメントなど)に向けて最適化します。イベントソースやコンバージョントラッキングの設定は不要です。プロダクトはサポートするメトリクスを `metric_optimization` で宣言します。 * **`kind: "event"`** — 広告主がトラッキングするコンバージョンイベントに向けて最適化します。`sync_event_sources` で登録されたイベントソースが必要です。プロダクトはサポートを `conversion_tracking` で宣言します。 ### kind: event 広告主がトラッキングするコンバージョンイベントに向けて最適化します。`event_sources` 配列は、このゴールにフィードするソースとタイプのペアを定義します。セラーが `multi_source_event_dedup`([`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) で宣言)をサポートしている場合、すべてのエントリを通じて `event_id` で重複排除します。複数のソースから報告された同じビジネスイベントは1回としてカウントされ、最初にマッチしたエントリの `value_field` と `value_factor` が使用されます。`multi_source_event_dedup` が存在しないまたは false の場合、バイヤーはゴールごとに1つのイベントソースを使用すべきです。 **コンバージョン単価**(単一ソース): ```json theme={null} { "kind": "event", "event_sources": [ { "event_source_id": "website_pixel", "event_type": "lead" } ], "target": { "kind": "cost_per", "value": 25.00 }, "priority": 1 } ``` **広告費用対効果**(返金を含む複数ソース): ```json theme={null} { "kind": "event", "event_sources": [ { "event_source_id": "web_pixel", "event_type": "purchase", "value_field": "order_total" }, { "event_source_id": "app_sdk", "event_type": "purchase", "value_field": "order_total" }, { "event_source_id": "web_pixel", "event_type": "refund", "value_field": "refund_amount", "value_factor": -1 } ], "target": { "kind": "per_ad_spend", "value": 4.0 }, "attribution_window": { "post_click": { "interval": 28, "unit": "days" }, "post_view": { "interval": 1, "unit": "days" } }, "priority": 1 } ``` `per_ad_spend` ターゲットでは、各イベントソースエントリに `value_field`(`custom_data` のどのフィールドが金銭的価値を持つか)とオプションの `value_factor`(乗数、デフォルトは1)を指定します。セラーは重複排除されたすべてのイベントに対して `sum(value_field * value_factor) / spend` を計算します。 **コンバージョン価値の最大化**(特定の ROAS ターゲットなし): ```json theme={null} { "kind": "event", "event_sources": [ { "event_source_id": "web_pixel", "event_type": "purchase", "value_field": "value" } ], "target": { "kind": "maximize_value" }, "priority": 1 } ``` `maximize_value` ターゲットは、特定のリターン比率にコミットせずに高価値コンバージョンに向けて支出を誘導します。少なくとも1つのイベントソースエントリに `value_field` が必要です。 | フィールド | 型 | 必須 | 説明 | | ----------------------------------- | ------------------------------------------------------ | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `kind` | `"event"` | Yes | 識別子 | | `event_sources` | array | Yes | このゴールにフィードするソースとタイプのペア。セラーはエントリを通じて `event_id` で重複排除します。同じ `event_id` が異なる `value_field` を持つ複数のソースから届いた場合、セラーはこの配列の最初にマッチしたエントリの `value_field` と `value_factor` を使用します。 | | `event_sources[].event_source_id` | string | Yes | イベントソース(`sync_event_sources` で設定済みであること) | | `event_sources[].event_type` | [EventType](#event-types) | Yes | 含めるイベントタイプ(例: `purchase`、`lead`、`refund`) | | `event_sources[].custom_event_name` | string | event\_type が `custom` の場合 | プラットフォーム固有のカスタムイベント名 | | `event_sources[].value_field` | string | ターゲットが `per_ad_spend` または `maximize_value` の場合 | `custom_data` のどのフィールドが金銭的価値を持つか。セラーはこれを価値の抽出と集計に使用しなければなりません。基盤となるプラットフォーム API に直接渡されるわけではありません。 | | `event_sources[].value_factor` | number | No | セラーが集計前に `value_field` に適用しなければなりません乗数(デフォルト1)。返金には -1、センティーム(1/100)には 0.01、カウントには含めながら価値の貢献をゼロにするには 0 を使用します。 | | `target.kind` | `"cost_per"` \| `"per_ad_spend"` \| `"maximize_value"` | No | ターゲットタイプ。省略した場合、セラーは予算内でコンバージョン数を最大化します。 | | `target.value` | number | Yes(ターゲット設定時) | 購入通貨でのイベント単価、またはリターン比率(例: 4.0 = $1 の支出に対して $4) | | `attribution_window` | object | No | クリックスルーとビュースルーのウィンドウ。省略した場合、セラーはデフォルトを使用します。 | | `priority` | integer | No | 1が最高優先度。省略した場合、セラーは配列の順序を使用します。 | ### kind: metric セラーがトラッキングする配信メトリクスに向けて最適化します。イベントソースは不要です。セラーはこれらをネイティブにトラッキングします。プロダクトはサポートするメトリクスを `metric_optimization.supported_metrics` で宣言します。 **クリック数の最大化**(ターゲットなし — セラーが予算内でボリュームを最適化): ```json theme={null} { "kind": "metric", "metric": "clicks" } ``` **クリック単価**: ```json theme={null} { "kind": "metric", "metric": "clicks", "target": { "kind": "cost_per", "value": 2.00 }, "priority": 2 } ``` **最低クリック率**: ```json theme={null} { "kind": "metric", "metric": "clicks", "target": { "kind": "threshold_rate", "value": 0.001 }, "priority": 2 } ``` **特定ベンダーからの最低アテンション**(`kind: "metric"` における `attention_seconds` / `attention_score` の列挙値は非推奨です——ベンダーによって実証されるメトリクスは代わりに `kind: "vendor_metric"` を使い、ゴールを特定の計測ベンダーに結び付けます): ```json theme={null} { "kind": "vendor_metric", "vendor": { "domain": "adelaidemetrics.com" }, "metric_id": "attention_score", "target": { "kind": "threshold_rate", "value": 70 }, "priority": 3 } ``` **エンゲージメントの最大化**(ソーシャルリアクション、コメント、シェア、ストーリー開封、オーバーレイタップ): ```json theme={null} { "kind": "metric", "metric": "engagements" } ``` **再生時間しきい値付きの完了ビュー**(TikTok での6秒ビュー): ```json theme={null} { "kind": "metric", "metric": "completed_views", "view_duration_seconds": 6, "target": { "kind": "cost_per", "value": 0.02 }, "priority": 1 } ``` | フィールド | 型 | 必須 | 説明 | | ----------------------- | ---------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `kind` | `"metric"` | Yes | 識別子 | | `metric` | string | Yes | セラーネイティブのメトリクス(下記のメトリクス表を参照) | | `view_duration_seconds` | number | No | `completed_views` イベントとして認定される最低動画再生時間(秒単位)。メトリクスが `completed_views` の場合にのみ適用されます。省略した場合、セラーはプラットフォームのデフォルトを使用します。プロダクトの `metric_optimization.supported_view_durations` に記載された値でなければなりません。セラーはサポートされていない値を拒否します。 | | `target.kind` | `"cost_per"` \| `"threshold_rate"` | No | ターゲットタイプ。省略した場合、セラーは予算内でメトリクスのボリュームを最大化します。 | | `target.value` | number | Yes(ターゲット設定時) | 購入通貨でのメトリクス単位あたりのコスト、またはインプレッションごとの最低値 | | `priority` | integer | No | 1が最高優先度。省略した場合、セラーは配列の順序を使用します。 | **メトリクス**: | メトリクス | 単位 | `threshold_rate` の例 | 説明 | | ------------------- | ------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | `clicks` | 回数/インプレッション | 0.001(0.1% CTR) | 離脱するリンクのクリック、スワイプスルー、CTA タップ | | `views` | 回数/インプレッション | 0.70(70% ビューアビリティ) | 視認可能なインプレッション | | `completed_views` | 回数/インプレッション | 0.85(85% VCR) | 動画または音声の完了。`view_duration_seconds` で認定しきい値を制御する(例: 2秒、6秒、15秒)。 | | `viewed_seconds` | 秒/インプレッション | 3.0(3秒表示) | インプレッションごとの表示時間。`viewability.viewed_seconds` でレポートされ、ビューアビリティの `standard`(MRC しきい値)に準拠します。 | | `attention_seconds` | 秒/インプレッション | — | **非推奨** — 業界で認定された定義がありません。代わりに `kind: "vendor_metric"` と `vendor` + `metric_id: "attention_seconds"` を使用してください。 | | `attention_score` | スコア/インプレッション | — | **非推奨** — 業界で認定された定義がありません。代わりに `kind: "vendor_metric"` と `vendor` + `metric_id: "attention_score"` を使用してください。 | | `engagements` | 回数/インプレッション | — | 閲覧を超えた直接インタラクション — ソーシャルリアクション/コメント/シェア、ストーリー/ユニット開封、CTV のインタラクティブオーバーレイタップ、音声のコンパニオンバナーインタラクション | | `follows` | 回数/インプレッション | — | 新規フォロワー、ページいいね、アーティスト/ポッドキャスト/チャンネルのフォロー、または無料のチャンネル/フィード購読 | | `saves` | 回数/インプレッション | — | 保存、ブックマーク、プレイリスト追加、ピン — 再訪意図のシグナル | | `profile_visits` | 回数/インプレッション | — | ブランドのプラットフォーム内ページへのアクセス — プロフィール、アーティストページ、チャンネル、ストアフロント。外部ウェブサイトのクリックは含まない(その場合は `clicks` を使用)。 | | `reach` | ユニーク数/ウィンドウ | — | フリークエンシーウィンドウ内のユニークオーディエンスリーチ。`reach_unit`(例: `households`、`individuals`)が必要。最適化のフリークエンシーバンドを設定するには `target_frequency` を使用します。 | ### kind: vendor\_metric 業界で認定された定義を持たない、ベンダーによって実証される計測——アテンション(DoubleVerify、IAS、Adelaide、TVision、Lumen)、パネルベースのブランドリフト(Kantar、Upwave、Cint)、排出量(Scope3、Good-Loop——後述の極性に関する注意を参照)、リテールメディアのパートナーメトリクス——では、ゴールが特定のベンダー + `metric_id` をエンドツーエンドで結び付けます。セラーの入札スタックはそのベンダーの計測に向けて誘導し、デリバリーは同じ `(vendor, metric_id)` のキーで `vendor_metric_values[]` を通じて値をレポートします。 **方向の極性**(このマイナーバージョンでは上向きの押し上げのみ)。`cost_per` と `threshold_rate` は上向きに押し上げるターゲットです——セラーはより高いメトリクス値、または最低しきい値の充足に向けてデリバリーを誘導します。バイヤーが*最小化*したいメトリクス(排出量、IVT、レイテンシ)は、現時点ではベンダーのメトリクス定義に沿ったセラー側の極性解釈に依存します。ファーストクラスの最小化セマンティクス(ゴール上の `direction: "minimize"` フィールド、または `target.kind: "ceiling_rate"`)は WG で議論中です——[#4644](https://github.com/adcontextprotocol/adcp/issues/4644) を参照してください。 ```json theme={null} { "kind": "vendor_metric", "vendor": { "domain": "adelaidemetrics.com" }, "metric_id": "attention_score", "target": { "kind": "threshold_rate", "value": 70 }, "priority": 1 } ``` | Field | Type | Required | Description | | -------------- | ---------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `kind` | `"vendor_metric"` | Yes | 判別子 | | `vendor` | BrandRef | Yes | このメトリクスを定義し計算するベンダー。ベンダーの `brand.json` の `agents[type='measurement']` に紐づきます。`vendor_metric_values`、`reporting_capabilities.vendor_metrics`、`committed_metrics`(ベンダースコープのエントリ)、`performance_standards.vendor` で使われるのと同じ形状です。 | | `metric_id` | string | Yes | ベンダーの語彙における識別子(例: `attention_score`、`awareness_lift`、`gco2e_per_impression`)。ベンダーが公開する `measurement.metrics[]` カタログに含まれていなければなりません(MUST)。 | | `target.kind` | `"cost_per"` \| `"threshold_rate"` | No | ターゲットの種別。省略した場合、セラーは予算内でメトリクス値を最大化します。 | | `target.value` | number | Yes(ターゲット設定時) | メトリクス単位あたりのコスト(通貨)、またはインプレッションごとの最低値。単位はベンダーが定義します。 | | `priority` | integer | No | 1 が最高優先度。省略した場合、セラーは配列の位置を使用します。 | **ゴール受理のための三つの前提条件**。セラーは、ケイパビリティまたはレポーティング整合性の前提条件を満たさない `vendor_metric` ゴールを拒否しなければなりません(MUST)。ディスカバリーの前提条件は検証すべきです(SHOULD): 1. **ディスカバリー**(このマイナーでは SHOULD、次のマイナーでは MUST)— `metric_id` がベンダーの公開する `measurement.metrics[]` カタログに含まれること(ベンダーの `brand.json` の計測エージェントに問い合わせます)。AdCP 準拠のケイパビリティ公開に対する計測ベンダーの対応が追いつくまでの間 SHOULD に緩和されており、2 社以上のベンダーが準拠エージェントを提供した時点で MUST に強化されます。 2. **ケイパビリティ** — `(vendor, metric_id)` のペアがプロダクトの `vendor_metric_optimization.supported_metrics[]` に含まれ、かつゴールの `target.kind` が該当エントリの `supported_targets` に含まれること。 3. **レポーティング整合性** — パッケージの `committed_metrics[]` に、対応する `{ scope: "vendor", vendor, metric_id }` エントリが含まれること。**コミットされたレポーティングのない最適化は検証不能です**——セラーが契約上値を埋める義務を負わないゴールに対して、バイヤーはパフォーマンスを評価できません。この前提条件こそがゴールを意味あるものにします。セラーは、同じパッケージでレポーティングにもコミットされていないメトリクスのゴールを(`TERMS_REJECTED` で)拒否しなければなりません(MUST)。 **`metric` 種別との違い**。`metric` 種別は、ベンダーの結び付けが不要なセラーネイティブの計測(clicks、views、completed\_views、reach、engagements など)向けです——セラーがそのメトリクスをネイティブに計測します。`vendor_metric` 種別は、同じメトリクス名がベンダーによって異なる意味を持ち、特定のソースに突き合わせる必要がある、ベンダー実証の計測向けです。`metric` 種別の列挙にある非推奨の `attention_seconds` / `attention_score` の値はこの分割より前のものであり、今後は `vendor_metric` を経由します。 **完全なライフサイクルのリファレンス**。標準メトリクスとベンダーメトリクスの両方のフローにまたがる、ケイパビリティ → コミットメント → 最適化 → デリバリーの全体像については[メトリクスのライフサイクル](/docs/media-buy/media-buys/optimization-reporting#メトリクスのライフサイクル)を参照してください。 ### ターゲットの種類 三つのゴール種別にまたがるすべてのターゲット種類: | `target.kind` | メトリクスゴール | ベンダーメトリクスゴール | イベントゴール | 説明 | | ---------------- | -------------- | ------------------------ | -------------- | ----------------------------------------- | | `cost_per` | クリック/ビューなどの単価 | ベンダーメトリクス単位あたりのコスト | コンバージョンイベント単価 | `spend / count` | | `threshold_rate` | インプレッションごとの最低値 | インプレッションごとのベンダーメトリクスの最低値 | — | `インプレッションごとに少なくとも X` | | `per_ad_spend` | — | — | ターゲット広告費用対効果 | `sum(value_field * value_factor) / spend` | | `maximize_value` | — | — | 総コンバージョン価値の最大化 | 高価値コンバージョンに向けて支出を誘導します。`value_field` が必要。 | ### 戦略の選択 | ゴール | 使用場面 | 設定内容 | | ---------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | コンバージョン数の最大化 | 予算内でできるだけ多くのコンバージョン | `kind: "event"` + イベントソース、ターゲットなし。`value_field` はレポーティングのために存在してもよいが、目的関数は変わりません。 | | ターゲットコンバージョン単価 | イベントごとの特定のコスト | `kind: "event"` + `target: { kind: "cost_per", value: 25.0 }` | | ターゲット広告費用対効果 | イベント価値に対する特定のリターン比率 | `kind: "event"` + ソースの `value_field` + `target: { kind: "per_ad_spend", value: 4.0 }` | | コンバージョン価値の最大化 | ROAS ターゲットなしで高価値コンバージョンに誘導 | `kind: "event"` + ソースの `value_field` + `target: { kind: "maximize_value" }` | | クリック数の最大化 | 予算内でクリックを最大化 | `kind: "metric"`、`metric: "clicks"`、ターゲットなし | | ターゲットクリック単価 | 特定のクリック単価 | `kind: "metric"`、`metric: "clicks"` + `target: { kind: "cost_per", value: 2.0 }` | | ターゲット CTR | 最低クリック率 | `kind: "metric"`、`metric: "clicks"` + `target: { kind: "threshold_rate", value: 0.001 }` | | ターゲットビューアビリティ | 最低ビューアビリティ率 | `kind: "metric"`、`metric: "views"` + `target: { kind: "threshold_rate", value: 0.70 }` | | ターゲットアテンション(ベンダー結び付け) | 特定ベンダーからの最低アテンション | `kind: "vendor_metric"`、`vendor: { domain: "adelaidemetrics.com" }`、`metric_id: "attention_score"` + `target: { kind: "threshold_rate", value: 70 }` | | ターゲットブランドリフト(ベンダー結び付け) | 特定のパネル提供者からの認知リフトを最大化 | `kind: "vendor_metric"`、`vendor: { domain: "kantar.com" }`、`metric_id: "awareness_lift"`、ターゲットなし(最大化) | | ターゲット VCR | 最低動画完了率 | `kind: "metric"`、`metric: "completed_views"` + `target: { kind: "threshold_rate", value: 0.85 }` | | 再生時間付き完了ビュー | 特定の再生時間しきい値付きの動画ビュー | `kind: "metric"`、`metric: "completed_views"` + `view_duration_seconds: 6` | | エンゲージメントの最大化 | 予算内でソーシャルインタラクションを最大化 | `kind: "metric"`、`metric: "engagements"`、ターゲットなし | | フォロワーの最大化 | 新規フォロワー、ページいいね、または無料のチャンネル/フィード購読を最大化 | `kind: "metric"`、`metric: "follows"`、ターゲットなし | | 保存数の最大化 | 保存/ブックマーク/プレイリスト追加を最大化 | `kind: "metric"`、`metric: "saves"`、ターゲットなし | | プロフィール訪問の最大化 | ブランドページ/プロフィールへのトラフィックを誘導 | `kind: "metric"`、`metric: "profile_visits"`、ターゲットなし | | 最大ユニークリーチ | 予算内でユニークオーディエンスを最大化 | `kind: "metric"`、`metric: "reach"` + `reach_unit: "households"`、ターゲットなし | | フリークエンシー付きリーチ | 週1〜3回のフリークエンシーバンドでリーチ | `kind: "metric"`、`metric: "reach"` + `reach_unit` + `target_frequency: { min: 1, max: 3, window: "7d" }` | ### 複数ゴールと優先度 パッケージは複数のゴールを持てる。優先度はセラーがどれをメインとして扱うかを制御します。よくあるパターンは、イベントデータが少ない場合にメトリクスゴールをプロキシシグナルとして使用することです。 ```json theme={null} "optimization_goals": [ { "kind": "metric", "metric": "clicks", "target": { "kind": "cost_per", "value": 2.00 }, "priority": 2 }, { "kind": "event", "event_sources": [ { "event_source_id": "mobile_sdk", "event_type": "app_install" }, { "event_source_id": "mmp_adjust", "event_type": "app_install" } ], "target": { "kind": "cost_per", "value": 10.00 }, "priority": 1 } ] ``` セラーは `priority: 1` のゴール(SDK と MMP をまたいで重複排除した \$10 インストール単価)に注力し、インストールデータが蓄積されるまでクリックをプロキシシグナルとして使用します。 ### イベントゴールのデフォルト動作 イベントゴールから `target` を省略した場合、セラーは予算内でコンバージョン数を最大化します。これは、イベントソースに `value_field` があるかどうかに関わらず当てはまります——明示的な価値志向のターゲットを伴わない `value_field` はレポーティング(デリバリーレポートの conversion\_value、ROAS)を有効にしますが、最適化の目的関数は変えません。 | `target` | `value_field` | セラーの動作 | | ---------------- | ------------- | ------------------------------------------------------- | | 省略 | 省略 | 予算内でイベント数を最大化 | | 省略 | あり | 予算内でイベント数を最大化。価値はレポーティングでのみ利用可能。 | | `cost_per` | いずれでも | コンバージョン単価をターゲットにする。価値は存在すればレポーティングに使用。 | | `per_ad_spend` | あり | 広告費用対効果をターゲットにする。 | | `per_ad_spend` | **なし** | **バリデーションエラー** — セラーは拒否しなければなりません。リターンを計算する価値の次元がありません。 | | `maximize_value` | あり | 高価値のコンバージョンに向けて誘導する。 | | `maximize_value` | **なし** | **バリデーションエラー** — セラーは拒否しなければなりません。最大化する価値の次元がありません。 | ### ゴールのブレンドとシーケンス `value_factor` と `priority` はどちらも「イベントタイプ A はイベントタイプ B より重要である」を表現しますが、セラーの最適化にとっての意味は異なります: * **`value_factor`** は複数のイベントソースを**単一の目的関数**にブレンドします。単一のゴールの `event_sources` 配列*内*のイベントソースエントリごとに設定します。セラーは、複合的な価値シグナルを持つ一つのゴールを見ます。購入とページビューを明示的な相対的重み付けで一緒に最適化すべき場合に使用します。 * **`priority`** は**独立したゴール**をシーケンスします。`optimization_goals` 配列内の別々のゴールオブジェクトに設定します。セラーはまずゴール 1 を最適化し、ゴール 2 は二次的な目的であって、ブレンドはされません。ゴールが概念的に別物である場合(例: まず CPA ターゲットを達成し、次に残りの予算でリーチを最大化する)に使用します。 ブレンドするには `value_factor` を、シーケンスするには `priority` を使用してください。これらを取り違えると、微妙に誤った最適化——シーケンスすべきものがブレンドされたゴール、またはブレンドすべきものがシーケンスされたゴール——が生じ、その影響はデリバリーレポートでは検出しにくいものになります。 ### イベントタイプの極性 ほとんどのイベントタイプは正のシグナルです——購入、リード、インストールは、バイヤーがより多く欲しいものです。一部のイベントタイプは、単独の最適化ターゲットにすべきでない観測シグナルです: | 極性 | イベントタイプ | 注記 | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- | | 正 | `purchase`、`lead`、`qualify_lead`、`close_convert_lead`、`app_install`、`complete_registration`、`subscribe`、`follow`、`content_view`、`watch_milestone`、`start_trial`、`contact`、`schedule`、`donate`、`submit_application` | イベントに十分なボリュームがあり、それがバイヤーの望む結果である場合、単独の最適化ターゲットとして安全 | | アッパーファネル | `page_view`、`view_content`、`select_content`、`select_item`、`search`、`add_to_cart`、`viewed_cart`、`add_to_wishlist`、`initiate_checkout`、`add_payment_info`、`share`、`app_launch` | 有効な最適化ターゲットだが、ローワーファネルのデータが乏しい場合に通常はプロキシシグナル(`priority: 2`)として使用される | | 観測 | `refund`、`remove_from_cart`、`disqualify_lead` | アトリビューションの精度と ROAS の調整のために `event_sources` に含めるものであり、単独の最適化ターゲットにはしない | `custom` イベントはここで分類されません——その極性はバイヤーの定義に依存します。バイヤーエージェントは、カスタムイベントが単独のターゲットとして安全かどうかを選ぶ際に、同じ考え方を適用すべきです。 観測イベントは複合ゴールの内側では有用です——`refund` に `value_factor: -1` を設定すると ROAS が下方に調整され、これはまさに望ましい挙動です。リスクは、`refund` や `remove_from_cart` の数に向けて最適化する単独のゴールを作ってしまう、設定を誤ったバイヤーエージェントです。これはバイヤーエージェントの実装上の懸念であり、プロトコルの制約ではありません——プロトコルは意図的に、どのイベントタイプを最適化ターゲットにできるかを制限しません。 ### `value_factor` によるボリュームの正規化 異なるボリューム規模のイベントソースを組み合わせる場合(例: `page_view` は数万、`purchase` は数百)、明示的な重み付けがなければ `sum(value_field * value_factor) / spend` における集計値は最もボリュームの大きいタイプに支配されます。バイヤーは、ソース間の相対的な重みを表現するために `value_factor` を使用すべきです: ```json theme={null} { "kind": "event", "event_sources": [ { "event_source_id": "web_pixel", "event_type": "page_view", "value_field": "value", "value_factor": 0.01 }, { "event_source_id": "web_pixel", "event_type": "purchase", "value_field": "value", "value_factor": 1 } ], "target": { "kind": "per_ad_spend", "value": 4.0 } } ``` ここでは `page_view` は額面価値の 1% しか寄与しないため、`purchase` より約 100 倍多く発生するにもかかわらず、ROAS の計算を支配することを防ぎます。 自動的な正規化は意図的にスコープ外です——セラーが持っていないかもしれないイベント履歴が必要になり、ROAS の式を不透明にしてしまうためです。イベントタイプをまたいで正規化したいバイヤーエージェントは、`value_factor` を設定する前に自分たちの側で行うべきです。 ### 価格モデルと最適化ゴール 価格モデル(CPC、CPM、CPA など)はバイヤーが支払う対象を決める。最適化ゴールはセラーがどのようにインプレッションを配分するかを決める。これらは独立しています。パッケージは CPM 価格を使いながら CPA ターゲットに向けて最適化したり、CPA 価格を使いながら ROAS に向けて最適化したりできます。請求の詳細については[価格モデル](/docs/media-buy/advanced-topics/pricing-models)を参照。 ### リーチとフリークエンシー リーチベースの最適化は `metric: "reach"` と2つの追加フィールドを使用します。 * **`reach_unit`**(必須): 測定単位 — プロダクトの `metric_optimization.supported_reach_units` で宣言された値でなければなりません(例: `households`、`individuals`)。 * **`target_frequency`**(任意): 最適化を誘導するフリークエンシーバンド。セラーは未リーチのエンティティへのインプレッションを高価値として、すでに飽和したエンティティへのインプレッションを低価値として扱います。`min`、`max`、`window`(例: `"7d"`、`"campaign"`)を含みます。省略した場合、セラーはユニークリーチを最大化します。 ```json theme={null} { "kind": "metric", "metric": "reach", "reach_unit": "households", "target_frequency": { "min": 1, "max": 3, "window": "7d" }, "priority": 1 } ``` GRP ベースのバイには [CPP 価格](/docs/media-buy/advanced-topics/pricing-models#cpp-cost-per-point)を使用します。最適化とは独立したハードなフリークエンシー制限には、パッケージの `frequency_cap` を使用します。リーチとフリークエンシーのメトリクスは [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) の配信レポートで確認できます。 ### 前提条件 **メトリクスゴール**(`kind: "metric"`)の場合: 1. **プロダクトのサポートを確認する** — プロダクトは `metric_optimization` で目的のメトリクスを `supported_metrics` に宣言していなければなりません。イベントソースやコンバージョントラッキングの設定は不要です。 2. **ターゲットのサポートを確認する** — ターゲットを設定する場合は、ターゲットの種類が `metric_optimization.supported_targets` に記載されていることを確認すること。 3. **再生時間を確認する** — `view_duration_seconds` 付きの `completed_views` を使用する場合は、その値が `metric_optimization.supported_view_durations` に記載されていることを確認すること。 **イベントゴール**(`kind: "event"`)の場合: 1. **イベントソースを設定する** — [`sync_event_sources`](/docs/media-buy/task-reference/sync_event_sources) を呼び出して、`event_sources` で参照するイベントソースをセットアップします。 2. **プロダクトのサポートを確認する** — プロダクトは `conversion_tracking` で目的のターゲット種類を `supported_targets` に宣言していなければなりません。 3. **重複排除のサポートを確認する** — ゴールごとに複数のイベントソースを使用する場合は、セラーが [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) で `multi_source_event_dedup` をサポートしていることを確認すること。サポートされていない場合は、ゴールごとに1つのイベントソースを使用すること。 4. **イベントを送信する** — [`log_event`](/docs/media-buy/task-reference/log_event) を使用してコンバージョンデータを送信します。セラーが効果的に最適化するにはイベント履歴が必要です。 ### アトリビューションウィンドウ アトリビューションウィンドウは、セラーがコンバージョンに広告インプレッションをクレジットするためにどれだけ遡るかを制御します。一般的なオプション: | ウィンドウ | 意味 | | ------------------------------------------ | ------------------- | | `post_click: {interval: 7, unit: "days"}` | クリックから7日以内のコンバージョン | | `post_click: {interval: 28, unit: "days"}` | クリックから28日以内のコンバージョン | | `post_view: {interval: 1, unit: "days"}` | 広告視聴から1日以内のコンバージョン | | `post_view: {interval: 7, unit: "days"}` | 広告視聴から7日以内のコンバージョン | 値はセラーの `conversion_tracking.attribution_windows` ケーパビリティのオプションと一致しなければなりません。省略した場合、セラーはデフォルトのウィンドウを適用します。 ## 配信レポートとの連携 イベントソースが設定されてイベントが流れ始めると、[`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) のレスポンスにコンバージョンメトリクスが表示されます。 * **`conversions`** — キャンペーンにアトリビュートされたポストクリックまたはポストビューのコンバージョン * **`conversion_value`** — アトリビュートされたコンバージョンの金銭的価値 * **`roas`** — 広告費用対効果(conversion\_value / spend) * **`cost_per_acquisition`** — コンバージョン単価(spend / conversions) これらのメトリクスは、パッケージに `optimization_goals` が設定されている場合にパッケージごとにレポートされます。`by_action_source` ブレークダウンをサポートするセラーは、コンバージョンをソース別(website、app、in\_store など)に分けて表示できます。 ## カタログアイテムのアトリビューション カタログドリブンのパッケージでは、コンバージョンイベントに関連するカタログアイテムを識別する `content_ids` が含まれます。カタログの `content_id_type` は期待される識別子タイプ(`sku`、`gtin`、`job_id` など)を宣言します。 アトリビューションは意図的に幅広く設計されています。ユーザーがあるアイテム(求人 A)をクリックして別のアイテム(求人 B に応募)でコンバージョンする場合もあります。イベントはクリックされたアイテムではなく、コンバージョンの実際の `content_id` で発火します。アイテムごとのクリックからコンバージョンまでのパス分析はプラットフォームの最適化の問題であり、プロトコルの問題ではありません。 [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) の `by_catalog_item` ブレークダウンは、アイテムごとのメトリクス(インプレッション、支出、クリック、コンバージョン)を表示します。 ## 関連ドキュメント * [`sync_event_sources`](/docs/media-buy/task-reference/sync_event_sources) — イベントソースの設定 * [`log_event`](/docs/media-buy/task-reference/log_event) — コンバージョンイベントの送信 * [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy#campaign-with-conversion-optimization) — パッケージへの最適化ゴールの設定 * [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) — コンバージョンメトリクスの監視 * [価格モデル](/docs/media-buy/advanced-topics/pricing-models#cpa-cost-per-acquisition) — CPA 請求(コンバージョン単価課金) # クリエイティブのライフサイクル Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/creatives/index フォーマット探索からアセット同期、ライブラリ管理まで、クリエイティブアセットをライフサイクル全体で管理します。 クリエイティブ管理はメディアバイキャンペーン成功の要です。AdCP は初期のフォーマット探索から継続的な最適化まで、クリエイティブアセットをライフサイクル全体で管理する包括的なツールを提供します。 ## 概要 AdCP のクリエイティブ管理システムは次を扱います: * サポートされるすべてのクリエイティブタイプの **フォーマット仕様** * 作成から最適化までの **アセットライフサイクル管理** * クリエイティブライブラリの **クロスプラットフォーム同期** * 一貫した配信を可能にする **標準フォーマット対応** ## 主要なクリエイティブタスク ### クリエイティブ同期 [`sync_creatives`](/docs/creative/task-reference/sync_creatives) を使って、セラーまたはクリエイティブプラットフォームがホストするクリエイティブライブラリにクリエイティブアセットをアップロードし管理します。これにより、クリエイティブがプラットフォームとキャンペーンをまたいで割り当て可能になります。 `creative.has_creative_library: true` を持たずに `inline_creative_management: true` を宣言するセラーの場合、クリエイティブは再利用可能なライブラリを介さず、`create_media_buy` または `update_media_buy` のパッケージスコープの `packages[].creatives` として提供されます。 ### クリエイティブラリ管理 [`list_creatives`](/docs/creative/task-reference/list_creatives) を使ってクリエイティブアセットライブラリを閲覧・管理します。ステータス追跡やパフォーマンスメタデータも確認できます。 ## 3 つの主なフェーズ AdCP は次の 3 つのフェーズでクリエイティブを管理します: ### フェーズ 1: フォーマット探索 クリエイティブアセットを作成する前に、**利用可能で必要なフォーマット** を理解する必要があります。AdCP は連携する 2 つの補完的なツールを提供します: #### 探索ワークフロー **`get_products`** はキャンペーンニーズに合う広告在庫を見つけ、そのプロダクトがサポートするフォーマット ID を返します。**`list_creative_formats`** は詳細なクリエイティブ要件を含む完全なフォーマット仕様を提供します。 #### 再帰的なフォーマット探索 営業エージェントは、追加のフォーマットを提供するクリエイティブエージェントを任意で参照できます。これにより再帰的な探索パターンが生まれます: 1. 営業エージェントで `list_creative_formats` を呼び出す 2. エージェントが直接サポートするフォーマットの完全な定義を受け取ります 3. 必要に応じて、他のクリエイティブエージェントへの URL を含む `creative_agents` 配列を受け取ります 4. それらのクリエイティブエージェントで `list_creative_formats` を再帰的に呼び出し、さらにフォーマットを探索します 5. **バイヤーは無限ループを避けるため訪問した URL を追跡する必要がある** 各フォーマットには、その権威元を示す `agent_url` フィールドが含まれます。 **注意**: `list_creative_formats` は認証を必要とせず、公開されたフォーマット探索が可能です。 #### よくある 2 つのアプローチ: **1. Inventory-First** - "自分のキャンペーンに合うプロダクトと、そのために必要なフォーマットは何か?" ```javascript theme={null} // Find products for your campaign const products = await get_products({ brand_manifest: { name: "Nike", url: "https://nike.com" }, brief: "Nike Air Max 2024 launch campaign" }); // Products return: format_ids: ["video_15s_hosted", "homepage_takeover_2024"] // Get full creative specs (returns complete format objects, not just IDs) const response = await list_creative_formats({}); const formatSpecs = response.formats.filter(f => products.products.flatMap(p => p.format_ids).includes(f.format_id) ); // Now you have full specs: video_15s_hosted needs MP4 H.264, 15s, 1920x1080 // homepage_takeover_2024 needs hero image + logo + headline // Optionally discover formats from linked creative agents if (response.creative_agents) { for (const agent of response.creative_agents) { const agentFormats = await list_creative_formats({ agent_url: agent.agent_url }); formatSpecs.push(...agentFormats.formats); } } ``` **2. Creative-First** - "このパブリッシャーはどの動画フォーマットをサポートしているか?" ```javascript theme={null} // Browse all available formats (returns full format objects immediately) const response = await list_creative_formats({ type: "video", category: "standard" }); // response.formats contains: full format objects for video_15s_hosted, video_30s_vast, etc. // Recursively discover formats from creative agents if needed const allFormats = [...response.formats]; if (response.creative_agents) { for (const agent of response.creative_agents) { const agentResponse = await list_creative_formats({ agent_url: agent.agent_url, type: "video" }); allFormats.push(...agentResponse.formats); } } // Find products supporting your creative capabilities const products = await get_products({ brand_manifest: { name: "Nike", url: "https://nike.com" }, brief: "Nike Air Max 2024 launch campaign", filters: { format_ids: allFormats.map(f => f.format_id) } }); ``` #### なぜ両方のツールが重要か * **`list_creative_formats` がない場合**: プロダクトから返るフォーマット ID は不透明な識別子のまま * **`get_products` がない場合**: どのフォーマットに実在庫があるか分からない * **両方を組み合わせると**: 利用可能なものと、仕様を満たすために必要なものの両方を把握できます ### フェーズ 2: クリエイティブ制作 フォーマット要件を理解したら、フェーズ 1 で把握した仕様に従って実際のクリエイティブアセットを作成します。 ### フェーズ 3: クリエイティブラリ管理 AdCP はクリエイティブ対応エージェントがホストするクリエイティブライブラリを使い、アセットを一度アップロードして複数キャンペーンに割り当てます。この手法により次が可能になります: * クリエイティブをアカウントレベルのライブラリにアップロード * クリエイティブを特定のキャンペーン/パッケージに割り当て * 複数のメディアバイでクリエイティブを再利用 * すべての割り当てにわたってパフォーマンスを追跡 アセット管理は [Brand Manifests](/docs/brand-protocol/brand-json) を通じて行い、ブランドレベルのアセットをタグ付きで提供します。 ## プラットフォーム考慮事項 プラットフォームによってクリエイティブ要件は異なります: ### Google Ad Manager * 標準的な IAB フォーマットをサポート * ポリシー準拠レビューが必要 * クリエイティブ承認は通常 24 時間以内 ### Kevel * カスタムのテンプレートベースクリエイティブをサポート * リアルタイムのクリエイティブディシジョニング * 柔軟なフォーマット対応 ### Triton Digital * オーディオ特化プラットフォーム * 標準的なオーディオフォーマットをサポート * 局単位のクリエイティブターゲティング ## レスポンスタイム クリエイティブ関連の処理には次のような応答時間があります: * **フォーマット一覧**: 約 1 秒(データベース参照) * **クリエイティブ同期**: 数分〜数日(アセット処理と承認) * **ライブラリクエリ**: 約 1 秒(データベース参照) ## ベストプラクティス 1. **フォーマット計画**: クリエイティブ制作前にサポートされるフォーマットを確認します 2. **早期アップロード**: キャンペーン開始の十分前にクリエイティブを提出します 3. **適応の受容**: より良い成果のためパブリッシャーの提案を検討します 4. **アセット整理**: クリエイティブ ID に明確な命名規則を用いる 5. **パフォーマンス監視**: 効果を追跡し継続的に改善します 6. **品質管理**: フォーマット仕様に厳密に従う 7. **ファイル最適化**: 高速読み込みのためファイルサイズを最適化します 8. **テスト**: さまざまなデバイスとプラットフォームでアセットをテストします ## 関連ドキュメント * **[`sync_creatives`](/docs/creative/task-reference/sync_creatives)** - アップサートセマンティクスによるバルククリエイティブ管理 * **[`list_creatives`](/docs/creative/task-reference/list_creatives)** - クリエイティブラリの高度なクエリとフィルタリング * **[`list_creative_formats`](/docs/creative/task-reference/list_creative_formats)** - フォーマット要件の理解 * **[Brand Manifest](/docs/brand-protocol/brand-json)** - ブランドアイデンティティとアセット管理 * **[Creative Formats](/docs/creative/formats)** - フォーマット仕様と探索の理解 * **[Creative Channel Guides](/docs/creative/channels/video)** - 動画、ディスプレイ、オーディオ、DOOH、カルーセルにおけるフォーマット例 * **[Asset Types](/docs/creative/asset-types)** - アセットの役割と仕様の理解 # Optimization & Reporting Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/media-buys/optimization-reporting AdCP のレポーティング/最適化機能を使ってパフォーマンスを監視し、配信指標を分析し、メディアバイを最適化する方法 データドリブンな監視と最適化による継続的改善を実現します。AdCP はパフォーマンス追跡・配信分析・成果向上を支援する包括的なレポーティング/最適化機能を提供します。 AdCP のレポーティングはキャンペーン設定に用いる [Targeting](/docs/media-buy/advanced-topics/targeting) と整合しており、ライフサイクル全体で一貫した分析が可能です。ターゲットした内容と同じ粒度でレポートできます。 パフォーマンスデータは AdCP の [Accountability & Trust Framework](/docs/media-buy/index#accountability--trust-framework) に反映され、パブリッシャーは安定した配信でレピュテーションを築き、バイヤーはデータに基づいて配分判断ができます。 ## 主な最適化タスク ### デリバリーレポート [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) でインプレッション、消化額、クリック、コンバージョンなど全パッケージのパフォーマンスデータを取得します。 あるいはメディアバイ作成時に **Webhook ベースのレポーティング** を設定し、定期的な自動通知を受け取ります。 ### キャンペーン更新 パフォーマンスインサイトに基づき、[`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) で設定・予算・構成を更新します。 ## 最適化ワークフロー 一般的な最適化サイクル: 1. **配信を監視**: 目標に対するパフォーマンスを追跡 2. **パフォーマンス分析**: 最適化の機会を特定 3. **調整を実施**: 予算・ターゲティング・クリエイティブ割り当てを更新 4. **変化を追跡**: 最適化の影響をモニタリング 5. **反復**: 定期的な分析で継続的改善 ## メトリクスのライフサイクル AdCP のすべての最適化メトリクス——セラーネイティブ(clicks、views、reach)、卒業済みのベンダー証明(viewability)、ベンダー定義(アテンション、ブランドリフト、排出量)のいずれであっても——は、同じ一連のサーフェスを流れます。バイヤーは次の三つの問いを順に立ててメトリクスを推論します: * **できるか?** — プロダクトはこのメトリクスを最適化できるか? *(ディスカバリー)* * **やるか?** — このパッケージについて、最適化と報告をコミットするか? *(コミットメント)* * **やったか?** — 配信において値はいくつで、コミットメントを満たしたか? *(レポーティング)* 各問いは特定のプロトコルサーフェスに対応します。同じ `metric_id`(ベンダー証明メトリクスでは `(vendor, metric_id)` タプル)がすべてのレイヤー——ディスカバリー、機能宣言、パッケージのコミットメント、配信——を流れるため、目標に対して配信を照合するバイヤーは変換表を必要としません。 ### 標準メトリクスのフロー(例: `clicks`、`viewable_rate`) | Question | Surface | Field | | ------------------------- | ------- | ----------------------------------------------- | | プロダクトは報告できるか? | プロダクト機能 | `reporting_capabilities.available_metrics` | | プロダクトは最適化できるか? | プロダクト機能 | `metric_optimization.supported_metrics` | | セラーは報告にコミットするか? | パッケージ | `committed_metrics[]`(scope: `standard`) | | バイヤーは最適化を望むか? | パッケージ | `optimization_goals[]`(kind: `metric`) | | バイヤーはアカウンタビリティを望むか? | パッケージ | `performance_standards[]` | | 値はいくつだったか? | 配信 | 標準スカラー(例: `clicks`、`viewability.viewable_rate`) | | セラーはコミット済みメトリクスの提供に失敗したか? | 配信 | `missing_metrics[]`(scope: `standard`) | ### ベンダー証明メトリクスのフロー(例: Adelaide アテンション、Scope3 排出量、Kantar ブランドリフト) 同じライフサイクルで、単なる `metric_id` の代わりに `(vendor, metric_id)` タプルがあらゆる箇所に入ります: | Question | Surface | Field | | -------------------------- | ------- | ------------------------------------------------ | | ベンダーはこのメトリクスを定義しているか? | ベンダーの機能 | `get_adcp_capabilities.measurement.metrics[]` | | プロダクトはこのベンダーメトリクスを報告できるか? | プロダクト機能 | `reporting_capabilities.vendor_metrics[]` | | プロダクトはこのベンダーメトリクスを最適化できるか? | プロダクト機能 | `vendor_metric_optimization.supported_metrics[]` | | セラーは報告にコミットするか? | パッケージ | `committed_metrics[]`(scope: `vendor`) | | バイヤーは最適化を望むか? | パッケージ | `optimization_goals[]`(kind: `vendor_metric`) | | バイヤーはアカウンタビリティを望むか? | パッケージ | `performance_standards[]`(`vendor` フィールド付き) | | 値はいくつだったか? | 配信 | `vendor_metric_values[]` | | セラーはコミット済みメトリクスの提供に失敗したか? | 配信 | `missing_metrics[]`(scope: `vendor`) | ### なぜ両方のフローが重要か 二つのフローは重複ではありません——*標準化された*計測と*ベンダー固有の*計測の違いをエンコードしています。クリックはどのセラーでも同じ意味を持ちますが、アテンションはそうではありません。ベンダーの紐付けは、*どの*アテンションモデルが最適化・報告されているかについてバイヤーとセラーが合意したという、ワイヤーレベルの証拠です。あるメトリクスがどちらのフローを使うかを決める Tier 0 → Tier 1 の卒業ポリシーは [`measurement/taxonomy.mdx`](/docs/measurement/taxonomy) を参照してください。 ### レイヤー間で必要な整合性 三つのルールがレイヤーを接続し、孤立した目標を防ぎます: 1. **最適化には機能が必要。** パッケージの `optimization_goals[]` エントリは、プロダクト機能と一致しなければなりません(MUST)——`kind: "metric"` には `metric_optimization.supported_metrics`、`kind: "vendor_metric"` には `vendor_metric_optimization.supported_metrics`。セラーは不一致を `TERMS_REJECTED` で拒否します。 2. **最適化には報告のコミットメントが必要。** `kind: "vendor_metric"` の目標では、一致する `(vendor, metric_id)` がパッケージの `committed_metrics[]` にも現れなければなりません(MUST)。セラーが報告をコミットしていないメトリクスの最適化は検証不能です——バイヤーには目標を採点する手段がありません。セラーはコミットされていない vendor\_metric 目標を `TERMS_REJECTED` で拒否します。(このルールは特にベンダーメトリクスについて規範的です。セラーネイティブな `metric` 目標では、セラーネイティブであること自体により通常は常に報告されるため、整合性は暗黙的です。)三つ目の前提条件——ディスカバリー、すなわち `metric_id` がベンダーの公開する `measurement.metrics[]` カタログに現れること——は、計測ベンダーの AdCP 適合の機能公開への対応が追いつくまで、このマイナーでは SHOULD であり、次のマイナーで MUST に強化されます。 3. **パフォーマンスのアカウンタビリティは独立。** `performance_standards[]` は並行して存在します——バイヤーは、最適化目標も設定するかどうかに関わらず、セラーが報告をコミットする任意のメトリクスに閾値のコミットメントを追加してよい(MAY)。三つのサーフェス(目標 / コミットメント / 基準)は組み合わさります: * 目標のみ — 「これに向けて押し進めて」、アカウンタビリティの下限なし * 基準のみ — 「X 以上を負っている」、達成方法はセラーが決める * 両方 — 最適化が舵を取り、基準が下支えする 4. **優先度は序数で、値が小さいほど優先。** `optimization_goals[]` が明示的な `priority` 値を運ぶ場合、セラーは**最小**の priority 値を持つ目標を主目標として扱います——`priority: 1` の目標がない場合でも(priority が `2` と `3` なら `2` が主)。`priority` が省略された場合、セラーは配列の位置を使ってよい。重複する priority 値は未定義です。 ### 実例: Adelaide アテンションのエンドツーエンド バイヤーが Adelaide の `attention_score` を閾値 70 で最適化したいとします。四つのサーフェスがどう並ぶかを示します: **1. プロダクト機能**(`get_products` でディスカバリー): ```json theme={null} { "product_id": "premium_ctv_video", "vendor_metric_optimization": { "supported_metrics": [ { "vendor": { "domain": "adelaidemetrics.com" }, "metric_id": "attention_score", "supported_targets": ["cost_per", "threshold_rate"] } ] }, "reporting_capabilities": { "vendor_metrics": [ { "vendor": { "domain": "adelaidemetrics.com" }, "metric_id": "attention_score" } ] } } ``` **2. `create_media_buy` でのパッケージ提案** — バイヤーは報告のコミットと最適化目標の両方を設定します(報告整合性ルールにより両方必須): ```json theme={null} { "product_id": "premium_ctv_video", "committed_metrics": [ { "scope": "vendor", "vendor": { "domain": "adelaidemetrics.com" }, "metric_id": "attention_score" } ], "optimization_goals": [ { "kind": "vendor_metric", "vendor": { "domain": "adelaidemetrics.com" }, "metric_id": "attention_score", "target": { "kind": "threshold_rate", "value": 70 }, "priority": 1 } ] } ``` バイヤーが契約上の下限を望む場合、`performance_standard` を上に重ねてもよい(MAY)——例: `{ "metric": "attention_score", "vendor": { "domain": "adelaidemetrics.com" }, "threshold": 65 }`——が、これは目標とは独立です。 **3. 配信**(`get_media_buy_delivery` レスポンス): ```json theme={null} { "vendor_metric_values": [ { "vendor": { "domain": "adelaidemetrics.com" }, "metric_id": "attention_score", "value": 73.2, "measurable_impressions": 420000 } ] } ``` セラーの入札スタックはより高い Adelaide アテンションスコアへ舵を切りました。報告値 `73.2` は閾値 `70` を上回るため、目標は達成されています。`measurable_impressions` の分母(配信インプレッション 100 万のうち `420000` など)は Adelaide の計測のカバレッジ値です——ベンダー SDK が配信インプレッションの 100% で発火することはまれで、今日の CTV におけるアテンションベンダーのカバレッジは、アプリ SDK の有無に応じて通常 30〜60% です。報告値について推論する前に、必ず `measurable_impressions / impressions` からカバレッジを計算してください。 **4. アカウンタビリティギャップの報告**(コミットメントが満たされない場合、同じく `get_media_buy_delivery` レスポンス内): ```json theme={null} { "by_package": [{ "package_id": "...", "missing_metrics": [ { "scope": "vendor", "vendor": { "domain": "adelaidemetrics.com" }, "metric_id": "attention_score" } ] }] } ``` 在庫における Adelaide のカバレッジがセラーの信頼閾値を下回り、この期間の `vendor_metric_values` を埋められなかった場合、同じ `(vendor, metric_id)` が `missing_metrics` に現れます——ライフサイクル全体で同じキーであるため、バイヤーの照合は行レベルの結合になります。 ## パフォーマンス監視 ### リアルタイム指標 配信中のキャンペーンを追跡します。 * 目標に対する **インプレッション配信状況** * 予算に対する **消化ペース** * **CTR** とエンゲージメント * 事業成果に紐づく **コンバージョン追跡** ### ヒストリカル分析 時間軸でパフォーマンス傾向を把握します。 * 主要指標の **日次/時間別の内訳** * 期間をまたいだ **パフォーマンス比較** * 最適化機会を見つける **トレンド識別** ### アラート/通知 重要なキャンペーンイベントを把握します。 * ペース異常に対する **配信アラート** * 大きな変化に対する **パフォーマンス通知** * 上限到達前の **予算警告** ## 配信方法 パブリッシャーは Webhook 通知またはオフラインファイル配信でレポートデータをバイヤーへプッシュできます。これによりポーリングを不要にし、タイムリーなインサイトを提供します。 **Webhook Push(リアルタイム)** - バイヤーのエンドポイントへ HTTP POST * 適合: 多くのバイヤー・セラー関係 * レイテンシ: ほぼリアルタイム(秒〜分) * コスト: 標準的な Webhook 基盤 **オフラインファイル配信(バッチ)** - クラウドストレージバケットへのプッシュ * 適合: 高ボリュームの大口バイヤー/セラー * レイテンシ: 定期バッチ(毎時/日次) * コスト: 大幅に低い($0.01-0.10/GB 対 $0.50-2.00/100 万 Webhook) * フォーマット: JSON Lines, CSV, Parquet * ストレージ: S3, GCS, Azure Blob Storage ### Webhook ベースのレポーティング #### Webhook 設定 メディアバイ作成時に `reporting_webhook` パラメーターでレポート Webhook を設定します。 ```json theme={null} { "packages": [...], "reporting_webhook": { "url": "https://buyer.example.com/webhooks/reporting", "authentication": { "schemes": ["Bearer"], "credentials": "secret_token_min_32_chars" }, "reporting_frequency": "daily" } } ``` **本番推奨: HMAC 署名付き** ```json theme={null} { "packages": [...], "reporting_webhook": { "url": "https://buyer.example.com/webhooks/reporting", "authentication": { "schemes": ["HMAC-SHA256"], "credentials": "shared_secret_min_32_chars" }, "reporting_frequency": "daily" } } ``` **セキュリティ必須:** * `authentication` 設定は必須(32 文字以上) * **Bearer トークン**: シンプルで開発向き(Authorization ヘッダー) * **HMAC-SHA256**: 本番推奨。リプレイ攻撃を防止(署名ヘッダー) * 資格情報はオンボーディング時に帯域外で交換 * 実装詳細は [Security](/docs/building/by-layer/L1/security) を参照 #### サポートされる頻度 パブリッシャーはプロダクトの `reporting_capabilities` でサポートする頻度を宣言します。すべてをサポートする必要はなく、運用に適した頻度を選択します。 * **`hourly`**: キャンペーン期間中、毎時間通知(任意。コスト/複雑性を考慮) * **`daily`**: 1 日 1 回通知(最も一般的、フェーズ1に推奨) * **`monthly`**: 月 1 回通知(タイムゾーンはパブリッシャー指定) **コスト考慮:** 時間単位 Webhook は日次の 24 倍のトラフィックを発生。大規模なバイヤー/セラーではコスト効率のためオフラインレポートを好む場合があります。 #### 提供可能な指標 指標の可否は 2 つのレベルで宣言します。 1. **プロダクトレベル**: `reporting_capabilities.available_metrics` でプラットフォームが提供できる指標を宣言 2. **フォーマットレベル**: クリエイティブフォーマットの `reported_metrics` でそのフォーマットが生成できる指標を宣言([Reported Metrics](/docs/creative/formats#reported-metrics) を参照) バイヤーは両者の積集合を受け取ります。`impressions` と `spend` は積集合によらず常に提供されます。標準的な指標: * **`impressions`**: 広告表示(常に提供) * **`spend`**: 消化額(常に提供) * **`clicks`**: クリック数 * **`ctr`**: クリック率 * **`views`**: プラットフォーム定義の閾値での視聴数 * **`completed_views`**: 動画/オーディオの完了数(最適化目標がカスタムの視聴尺を設定する場合は閾値ベースの完了数) * **`completion_rate`**: 完了率(`completed_views` / `impressions`)。該当しない場合(非動画の購入など)は `null` * **`conversions`**: クリック後/視聴後コンバージョン * **`conversion_value`**: 帰属コンバージョンの金銭的価値 * **`roas`**: 広告費用対効果 * **`cost_per_acquisition`**: コンバージョンあたりコスト * **`new_to_brand_rate`**: 初回購入者によるコンバージョンの割合 * **`leads`**: リード獲得数 * **`reach`**: ユニークリーチ(`reach_unit` と対)。計測ウィンドウは `reach_window`(`cumulative` / `period` / `rolling`)で宣言。`reach_window` が省略された場合ウィンドウは未指定であり、バイヤーは行をまたいでリーチを合計してはなりません(MUST NOT)。 * **`reach_window`**: リーチ/フリークエンシーのウィンドウ意味論——`kind`(キャンペーン開始以降の `cumulative`、重複しないスナップショットの `period`、後方ウィンドウの `rolling`)と `period: Duration`(`period` と `rolling` で必須)を持つオブジェクト * **`frequency`**: `reach_window` にわたって計測された、リーチ単位あたりの平均フリークエンシー * **`grps`**: グロスレーティングポイント(CPP 課金向け) * **`engagements`**: 視聴を超える直接的な広告インタラクション(リアクション、タップ、オープン) * **`engagement_rate`**: プラットフォーム固有のエンゲージメント率 * **`follows`**: 配信に帰属する新規フォロワー、ページのいいね、アーティスト/ポッドキャスト/チャンネルのフォロー、または無料のチャンネル/フィード購読。有料サブスクリプションは `event_type: "subscribe"` のコンバージョンイベントです。 * **`saves`**: 配信に帰属する保存、ブックマーク、プレイリスト追加(プラットフォームによって名称が異なる——Pinterest の「repins」、TikTok の「video\_saves」——すべてこの正準キーで報告) * **`profile_visits`**: ブランドのプラットフォーム内ページへの訪問 * **`viewability`**: ビューアビリティデータ(measurable\_impressions, viewable\_impressions, viewable\_rate, viewed\_seconds, standard, vendor)。MRC 基準と GroupM 基準を区別。`viewed_seconds` は計測可能インプレッションあたりの平均インビュー時間で、`viewed_seconds` 最適化目標のレポート側の対応物であり、`viewable_rate` と同じ `standard` 閾値に従います。任意の `vendor` フィールドは `BrandRef` を運ぶため行が自己記述的になります——配信を単独で読むバイヤーエージェントは、`package.committed_metrics` や `package.performance_standards` へ結合し直すことなく数値を計測ベンダーに帰属できます。 * **`quartile_data`**: 動画クォータイル完了データ(q1〜q4)。該当しない場合(非動画の購入など)は `null` * **`dooh_metrics`**: DOOH 固有指標(ループ再生数、スクリーン数、会場別内訳) * **`cost_per_click`**: クリックあたりコスト(`spend / clicks`) * **`cost_per_completed_view`**: 完了視聴あたりコスト(`spend / completed_views`)。動画/オーディオ在庫の CPCV 価格スカラー * **`cpm`**: 1000 インプレッションあたりコスト(`(spend / impressions) × 1000`)。CTV、ディスプレイ、モバイル/ウェブ動画、ネイティブ、オーディオ、DOOH をまたぐ普遍的な価格スカラー * **`downloads`**: オーディオ/ポッドキャストのダウンロード(IAB Podcast Measurement Technical Guidelines の手法)。`views` とは別 * **`units_sold`**: 配信に帰属して販売された点数(リテールメディアのコマーススカラー。`conversions` とは別——1 トランザクションが複数の点数を含みうる。アトリビューションウィンドウは `measurement_terms` で宣言) * **`new_to_brand_units`**: `new_to_brand_rate` の点数版——初回購入者に販売された点数のカウント * **`plays`**: DOOH/放送在庫の生の再生回数(`forecastable-metric.plays` に対応)。`dooh_metrics.loop_plays`(スクリーンごとのローテーション)や `impressions`(乗算後のオーディエンス数)とは別 バイヤーは `requested_metrics` で必要な指標のみを要求し、ペイロードを抑えて KPI に集中できます。 `completion_rate` と `quartile_data` については、セラーはメトリクスが該当しないことを示すために `null` を返してよく(MAY。例: 非動画の購入)、クライアントはこの二つのフィールドについて `null` を有効な値として受け入れなければなりません(MUST)。他のすべてのメトリクスは省略によって「該当なし」を示します——セラーは `null` を送るのではなく省略します。 #### ベンダー定義メトリクス 標準の `available_metrics` 列挙は閉じたプロトコル語彙です。ベンダー定義メトリクス——独自のアテンションスコア、インプレッションあたり排出量、パネルベースのデモグラフィック、ブランドリフト調査、フライト中のアテンションパネル、カスタムのビューアビリティ亜種——は並行する構造化サーフェスに存在します: * **宣言**(`reporting_capabilities.vendor_metrics`): 各エントリはベンダーのメトリクスカタログへのポインタ(`{ vendor: BrandRef, metric_id }`)です。セラーは「このベンダーのメトリクスをサポートする」と言うだけで、それ以外(カテゴリ、手法、標準との整合、人が読めるドキュメント)はベンダー側に存在します。識別子はベンダーで名前空間化されます——同じ `metric_id` が異なるベンダーの語彙で異なる意味を持ちうる。 * **ディスカバリーのアンカー**: ベンダーの `brand.json` の `agents[type='measurement']` が、計測エージェントの URL、機能プロファイル、手法、各メトリクスが実装する標準、メトリクスごとのドキュメントを見つける正準的な場所です。AdCP はそのメタデータをすべてのセラーのプロダクトごとの拡張に複製しません。バイヤーは必要なときにベンダーごとに一度だけ解決します(キャッシュ可能)。 * **フィルター**(`get_products` の `filters.required_vendor_metrics`): 各エントリは `vendor` および/または `metric_id` を指定します(少なくとも一方)。ベンダー横断のクエリ(例: 「サポートするベンダーの任意のアテンション計測」)はバイヤーエージェントの責任です: エージェントは `brand.json` レコードを通じてどのベンダーがカテゴリを提供するかを解決し、それらをフィルターエントリとして列挙します。他の `required_*` フィルターと同じ filter-not-fail の慣習。 * **レポーティング**(各 `by_package` 配信行の `vendor_metric_values`): 各値は `{ vendor, metric_id, value, unit?, measurable_impressions?, breakdown? }` を運びます。`measurable_impressions` はカバレッジの分母です——ベンダー計測が配信インプレッションの 100% であることはまれです。このフィールドが存在する場合、バイヤーはカバレッジを `measurable_impressions / impressions` として計算します。存在しない場合、カバレッジは未指定です(率を計算したり、完全なカバレッジを仮定したりしないでください)。`breakdown` スロットは、単一のスカラーを超える構造化ペイロード(パネルのデモグラフィック、共視聴比率、増分の分解)をベンダーが置く場所です。これが唯一の逃げ道であり、値エンベロープの残りは閉じています。 * **昇格パス**: 業界が公開された標準を通じてあるメトリクスに収束したとき、仕様はそれを閉じた `available_metrics` 列挙に追加し、ベンダー拡張は歴史的なエイリアスになります。昇格は、場当たり的なベンダー収束数ではなく、標準化団体の公開に基づきます。 * **アカウンタビリティのスコープ**: セラーが `package.committed_metrics` にベンダーメトリクスを刻印する場合(`scope: "vendor"`)、標準メトリクスと同じ `get_media_buy_delivery` の `missing_metrics` 契約の対象になります。ベンダーメトリクスを確かに証明できないセラーは、それを `committed_metrics` に刻印すべきではありません(SHOULD NOT)。不在はメトリクスを助言的なままにし、照合は `vendor_metric_values.measurable_impressions` のカバレッジと、ベンダーの計測エージェントを通じた帯域外の検証にフォールバックします。助言的か説明責任を伴うかの区別は、メトリクスのスコープ間で非対称であるのではなく、契約レイヤーで明示的になりました。 #### パブリッシャーのコミットメント レポート Webhook を設定した場合、パブリッシャーは以下を送信します。 **(campaign\_duration / reporting\_frequency) + 1** 回の通知 * キャンペーン期間中、頻度ごとに 1 回 * キャンペーン完了時に最終通知を 1 回 * 想定遅延時間を超える場合は `"delayed"` 通知を送信 #### Webhook ペイロード レポート Webhook は完全な MCP Webhook エンベロープを送ります。配信レポート自体は [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) と同じペイロード構造にメタデータを加えたものですが、`result` の下にネストされます。内側の配信オブジェクトをトップレベルの POST ボディとして送らないでください。 ```json theme={null} { "idempotency_key": "whk_20240205_example_000005", "operation_id": "delivery_report_mb001_2024_02_05", "task_id": "delivery_report_mb001_2024_02_05_000005", "task_type": "media_buy_delivery", "status": "completed", "timestamp": "2024-02-06T08:00:00Z", "message": "Scheduled media buy delivery report available", "result": { "notification_type": "scheduled", "sequence_number": 5, "next_expected_at": "2024-02-06T08:00:00Z", "reporting_period": { "start": "2024-02-05T00:00:00Z", "end": "2024-02-05T23:59:59Z" }, "currency": "USD", "media_buy_deliveries": [ { "media_buy_id": "mb_001", "status": "active", "totals": { "impressions": 125000, "spend": 5625.0, "clicks": 250, "ctr": 0.002 }, "by_package": [] } ] } } ``` 内側の `result` オブジェクトは個別に表示・保存できますが、この形状はトップレベルの Webhook POST ボディとしては無効です: ```json theme={null} { "notification_type": "scheduled", "sequence_number": 5, "reporting_period": { "start": "2024-02-05T00:00:00Z", "end": "2024-02-05T23:59:59Z" }, "currency": "USD", "media_buy_deliveries": [ { "media_buy_id": "mb_001", "status": "active", "totals": { "impressions": 125000, "spend": 5625.00, "clicks": 250, "ctr": 0.002 }, "by_package": [...] } ] } ``` **Fields:** * **`notification_type`**: `"scheduled"`(定期)、`"final"`(完了)、`"delayed"`(データ未準備) * **`sequence_number`**: 連番(1 起算) * **`next_expected_at`**: 次回通知の ISO 8601 時刻(最終通知では省略) * **`media_buy_deliveries`**: メディアバイ配信データの配列(パブリッシャーが複数メディアバイをまとめて返す場合あり) #### タイムゾーンの扱い **レポーティングはすべて UTC を使用しなければなりません。** DST の複雑性を排除し、照合を簡素化し、一貫した 24 時間単位を保証します。 ```json theme={null} { "reporting_capabilities": { "timezone": "UTC", "available_reporting_frequencies": ["daily"], "date_range_support": "date_range" } } ``` **レポート期間:** * 日次: 00:00:00Z 〜 23:59:59Z(常に 24 時間) * 時次: 時刻 00 分 00 秒〜59 分 59 秒(常に 1 時間) * 月次: 月初〜月末 **Example webhook payload:** ```json theme={null} { "reporting_period": { "start": "2024-02-05T00:00:00Z", "end": "2024-02-05T23:59:59Z" } } ``` #### 遅延レポーティング プロダクトの `expected_delay_minutes` 内にレポートデータが用意できない場合、パブリッシャーは `notification_type: "delayed"` で通知します。 ```json theme={null} { "notification_type": "delayed", "sequence_number": 3, "next_expected_at": "2024-02-06T10:00:00Z", "message": "Reporting data delayed due to upstream processing. Expected availability in 2 hours." } ``` これにより、通知が欠落したと誤解されるのを防ぎます。 #### 計測成熟ウィンドウ 課金グレードのデータが初日に最終値として届くのではなく**段階的に**生成されるチャネルでは、セラーはプロダクトに `measurement_windows` を宣言します。各ウィンドウは、独自の想定提供時期を持つ成熟ステージを表します。このパターンはチャネルをまたいで使われます: | Channel | Typical windows | | ----------------- | ------------------------------------------------ | | 放送 / 地上波 TV | `live`(当日)→ `c3`(約4日)→ `c7`(約15〜22日、保証の基準) | | DOOH | `tentative`(当日)→ IVT/不正チェック後の `final`(約1日、保証の基準) | | IVT フィルタリング付きデジタル | raw → `post_givt` → `post_sivt`(約2〜3日、保証の基準) | | ポッドキャスト | `downloads_7d` → `downloads_30d`(保証の基準) | 各ウィンドウの数値は前のものに優先します。通常、一つのウィンドウが `is_guarantee_basis`——双方が照合する数値——です。計測ベンダーの処理時間は各ウィンドウの `expected_availability_days` に取り込まれます(累積と処理の両方を含む)。 放送では、初期ウィンドウのデータの遅延や疎さは、通常セラー側のメタデータの問題ではなく、計測ベンダーの精算の問題です。Nielsen、Comscore、VideoAmp、その他の指定ベンダーは、Live、C3、C7、Live+5、または市場レベルの結果を波状に公開しうる。その期間中、セラーは配信を暫定または計測ベンダーの確定待ちとしてラベル付けし、`measurement_window` を保持し、`is_final`、`finalized_at`、`supersedes_window`、および遅延/ウィンドウ更新の通知を使って何が変わったかを示すべきです。バイヤーは、部分的なアフィリエイトや局の可視性を表すためにクリエイティブレコードやプロダクトメタデータをフォークすべきではありません。代わりに、セラーの配信行を市場、プレースメント、デイパート、クリエイティブ、計測ウィンドウで集約してください。 セラーは、最初に利用可能になるデータパイプラインを反映するようプロダクトに `expected_delay_minutes` を設定します。`reporting_capabilities` の `measurement_windows` 配列がウィンドウごとのタイムラインを提供します。 計測ウィンドウを持つプロダクトの配信データには、各パッケージに三つのフィールドが含まれます: * **`is_final`** — セラーがこのレポート期間についてデータを確定とみなす場合 `true`。データが更新される(より広いウィンドウ、追加処理)場合は `false`。セラーが暫定と最終を区別しない場合は不在。 * **`measurement_window`** — このデータがどのウィンドウを表すか(例: `"c3"`)。プロダクトの `measurement_windows` の `window_id` を参照します。ウィンドウ成熟のない標準的なデジタルレポーティングでは不在。 * **`supersedes_window`** — このレポートがどの以前のウィンドウを置き換えるか(例: C3 データが届いたときの `"live"`)。ある期間の最初のレポートでは不在。 セラーが同じ期間についてより広いウィンドウで更新データを送る場合、`notification_type: "window_update"` を使います。これは `adjusted`(同じウィンドウ内の訂正)とは別です。 **計測ウィンドウのライフサイクル例** — 3月1日に放映される放送スポット: **3月2日** — Live データが到着(notification\_type: `scheduled`): ```json theme={null} { "notification_type": "scheduled", "media_buy_deliveries": [{ "media_buy_id": "mb_nova_q4", "by_package": [{ "package_id": "primetime_30s", "impressions": 980000, "spend": 24500, "is_final": false, "measurement_window": "live" }] }] } ``` **3月5日** — C3 データが live に優先(notification\_type: `window_update`): ```json theme={null} { "notification_type": "window_update", "media_buy_deliveries": [{ "media_buy_id": "mb_nova_q4", "by_package": [{ "package_id": "primetime_30s", "impressions": 1050000, "spend": 26250, "is_final": false, "measurement_window": "c3", "supersedes_window": "live" }] }] } ``` **3月16日** — C7 データが到着、この期間について最終(notification\_type: `window_update`): ```json theme={null} { "notification_type": "window_update", "media_buy_deliveries": [{ "media_buy_id": "mb_nova_q4", "by_package": [{ "package_id": "primetime_30s", "impressions": 1120000, "spend": 28000, "is_final": true, "measurement_window": "c7", "supersedes_window": "c3" }] }] } ``` バイヤーは `window_update` が届くたびに保存データを置き換えます。`is_final: true` のとき、それが保証に対して照合すべき数値です。同じライフサイクルの形状が、DOOH(`tentative` → `final`)、IVT フィルタリング付きデジタル(`post_givt` → `post_sivt`)、ポッドキャスト(`downloads_7d` → `downloads_30d`)、その他データが段階的に成熟するあらゆるチャネルに適用されます——異なるのはウィンドウ ID とタイミングだけです。 `measurement_window` が課金条件にどう現れ、照合と請求のクロックをどう駆動するかは、[Accountability](/docs/media-buy/advanced-topics/accountability) を参照してください。 #### Webhook の集約 複数のメディアバイが以下を共有する場合、呼び出し数削減のため Webhook を集約すべきです。 * 同一 Webhook URL * 同一のレポート頻度 * 同一のレポート期間 **例**: バイヤーが同一エンドポイントで日次レポートを受けるアクティブキャンペーンを 100 件持つ場合 * **集約なし**: 1 日 100 件の Webhook(非効率) * **集約あり**: 1 日 1 件の Webhook に 100 キャンペーンをまとめる(最適) `media_buy_deliveries` 配列には Webhook 1 件あたり 1〜N のメディアバイが含まれます。バイヤーは配列を反復して各キャンペーンを処理してください。 **Aggregated webhook example:** ```json theme={null} { "notification_type": "scheduled", "reporting_period": { "start": "2024-02-05T00:00:00Z", "end": "2024-02-05T23:59:59Z" }, "currency": "USD", "media_buy_deliveries": [ { "media_buy_id": "mb_001", "totals": { "impressions": 50000, "spend": 1750 }, ... }, { "media_buy_id": "mb_002", "totals": { "impressions": 48500, "spend": 1695 }, ... }, // ... 98 more media buys ] } ``` バイヤーは配列を反復し、各メディアバイを個別に処理します。集計値が必要な場合は各メディアバイの合計から算出してください。 #### 部分的な失敗の扱い 複数メディアバイを 1 つの Webhook にまとめる際、キャンペーンごとにデータ可否が異なる場合があります。 **方針: ステータス付きベストエフォート配信** パブリッシャーは利用可能なデータをすべて含めた集約 Webhook を送り、ステータスで可否を示すべきです。 ```json theme={null} { "notification_type": "scheduled", "sequence_number": 5, "reporting_period": { "start": "2024-02-05T00:00:00Z", "end": "2024-02-05T23:59:59Z" }, "currency": "USD", "media_buy_deliveries": [ { "media_buy_id": "mb_001", "status": "active", "totals": { "impressions": 50000, "spend": 1750 } }, { "media_buy_id": "mb_002", "status": "active", "totals": { "impressions": 48500, "spend": 1695 } }, { "media_buy_id": "mb_003", "status": "reporting_delayed", "message": "Reporting data temporarily unavailable for this campaign", "expected_availability": "2024-02-06T02:00:00Z" } ], "partial_data": true, "unavailable_count": 1 } ``` **部分失敗の主なフィールド:** * `partial_data`: いずれかのキャンペーンでデータ欠落がある場合に true * `unavailable_count`: 遅延/欠落しているキャンペーン数 * `status`: キャンペーン単位のステータス(`"active"`, `"reporting_delayed"`, `"failed"`) * `expected_availability`: 遅延データの準備予定時刻(分かる場合) **部分配信を使うべきケース:** 1. **上流遅延**: データソースの速度差があります 2. **システム劣化**: 部分的な障害が一部キャンペーンに影響 3. **データ品質問題**: 特定キャンペーンのみ検証に失敗 4. **レートリミット**: API 制限で全キャンペーンを取得できません **部分配信を使わないケース:** 1. **全体障害**: `"delayed"` 通知を送る 2. **全キャンペーンに影響**: `notification_type: "delayed"` を使用 3. **バイヤー側エンドポイント問題**: サーキットブレーカーで送信を止める **バイヤー側の処理例:** ```javascript theme={null} function processAggregatedWebhook(webhook) { if (webhook.partial_data) { console.warn(`Partial data: ${webhook.unavailable_count} campaigns delayed`); } for (const delivery of webhook.media_buy_deliveries) { if (delivery.status === 'reporting_delayed') { // Mark campaign as pending, retry via polling or wait for next webhook markCampaignPending(delivery.media_buy_id, delivery.expected_availability); } else if (delivery.status === 'active') { // Process normal delivery data processCampaignMetrics(delivery); } else { console.error(`Unexpected status for ${delivery.media_buy_id}: ${delivery.status}`); } } } ``` **ベストプラクティス:** * データがない場合もステータス付きで全キャンペーンを配列に含めます * 遅延/失敗がある場合は `partial_data: true` を設定 * 分かる場合は `expected_availability` を返す * Webhook 全体をリトライしません。必要ならバイヤーは個別にポーリング * 部分配信率をモニタリングし、システム的な問題を検知 #### プライバシーとコンプライアンス ##### GDPR/CCPA 向けの PII マスキング パブリッシャーはすべての Webhook ペイロードから PII を削除し、GDPR/CCPA に準拠しなければなりません。レポート Webhook には集計・匿名化された指標のみを含めてください。 **除去するもの:** * ユーザー ID、デバイス ID、IP アドレス * メールアドレス、電話番号 * 正確な位置情報(緯度/経度) * Cookie ID、広告 ID(集計されていない場合) * PII を含むカスタムディメンション **保持してよいもの:** * 集計指標(インプレッション、消化額、クリックなど) * 粗い地理情報(市/州/国。番地は不可) * デバイスタイプカテゴリ(モバイル/デスクトップ/タブレット) * ブラウザ/OS カテゴリ * 時間ベースの集計 **Example - Before PII Scrubbing (❌ DO NOT SEND):** ```json theme={null} { "media_buy_id": "mb_001", "user_events": [ { "user_id": "user_12345", "ip_address": "192.168.1.100", "device_id": "abc-def-ghi", "impressions": 1, "lat": 40.7128, "lon": -74.0060 } ] } ``` **Example - After PII Scrubbing (✅ CORRECT):** ```json theme={null} { "media_buy_id": "mb_001", "totals": { "impressions": 125000, "spend": 5625.00, "clicks": 250 }, "by_package": [ { "package_id": "pkg_001", "impressions": 125000, "spend": 5625.00, "by_geo": [ { "geo_level": "region", "geo_code": "US-NY", "geo_name": "New York", "impressions": 45000, "spend": 2025.00 } ], "by_geo_truncated": false } ] } ``` **パブリッシャーの責任:** * Webhook 配信ではなくデータ収集レイヤーで PII をマスクします * 再識別されないよう集計閾値を設定(例: セグメントあたり 10 ユーザー以上) * 収集データと Webhook で共有するデータの違いを明文化 * GDPR 準拠のため DPA(データ処理契約)を提供 * GDPR/CCPA の削除依頼に対応 **バイヤーの責任:** * `requested_metrics` やカスタムディメンションで PII を要求しません * Webhook データが集計・匿名化されていることを理解します * 適切なデータ保持ポリシーを実装 * プライバシーポリシー/ユーザー通知に Webhook データを含めます #### 実装ベストプラクティス 1. **配列を扱う**: 1 件でも `media_buy_deliveries` は配列として処理します 2. **冪等なハンドラー**: 重複通知を安全に処理(Webhook は at-least-once 配信) 3. **シーケンス管理**: `sequence_number` で欠落/順不同の通知を検知 4. **フォールバックポーリング**: Webhook 失敗時に備え定期ポーリングを継続 5. **タイムゾーン意識**: 期間計算のためパブリッシャーのタイムゾーンを保持 6. **頻度の検証**: リクエストした頻度が `available_reporting_frequencies` に含まれることを確認 7. **指標の検証**: リクエストした指標が `available_metrics` に含まれることを確認 8. **PII コンプライアンス**: Webhook ペイロードにユーザーレベルデータを含めない #### Webhook Health Monitoring Webhook 配信ステータスは **AdCP のグローバルタスク管理システム** で追跡します([Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) 参照)。 `reporting_webhook` を設定してメディアバイを作成すると、パブリッシャーは配信用のタスクを生成します。バイヤーは標準のタスククエリで Webhook の健全性を監視できます。 **タスク管理を使う利点:** * すべての AdCP オペレーションで一貫したステータス管理 * ポーリング/Webhook の標準パターンを利用 * ステータス/履歴/エラーの既存インフラを活用 * メディアバイ固有の健全性エンドポイントが不要 Webhook 配信が恒常的に失敗しサーキットブレーカーが開いた場合、パブリッシャーはタスクステータスを更新して問題を示します。バイヤーは通常のタスク監視で検知できます。 ### オフラインファイル配信ベースのレポーティング **例: オフライン配信** パブリッシャーが日次レポートをバイヤーのクラウドストレージへプッシュ: ``` s3://buyer-reports/publisher_name/2024/02/05/media_buy_delivery.json.gz ``` ファイルは Webhook と同じ構造で、すべてのキャンペーンを集約しています。バイヤーは都合の良いタイミングで処理します。 **オフライン配信を使うケース:** * 同一バイヤーで 100 本超のアクティブキャンペーン * 時間単位レポートが必要(コスト 24 倍削減) * 詳細な内訳や多次元データでボリュームが大きい * バイヤーにバッチ処理基盤があります セラーは、サポートするプッシュ型の配信方法とプロトコルを `get_adcp_capabilities` で宣言します。`get_media_buy_delivery` によるポーリングは常に利用可能です——これはすべての `media_buy` セラーの必須タスクです。 ```json theme={null} { "media_buy": { "reporting_delivery_methods": ["webhook", "offline"], "offline_delivery_protocols": ["s3", "gcs"] } } ``` バイヤーはアカウント同期時にプロトコルの希望を表明します。セラーはサポートしていれば希望のプロトコルでバケットをプロビジョニングします: ```json theme={null} { "accounts": [{ "brand": { "domain": "nova-brands.com" }, "operator": "pinnacle-media.com", "billing": "operator", "preferred_reporting_protocol": "s3" }] } ``` プロダクトは `reporting_capabilities` でサポートするケイデンスとメトリクスを宣言します: ```json theme={null} { "reporting_capabilities": { "available_reporting_frequencies": ["daily"], "supports_webhooks": true, "available_metrics": ["impressions", "spend", "clicks"], "date_range_support": "date_range" } } ``` オフライン配信では、セラーはアカウントごとにストレージをプロビジョニングし、帯域外でバイヤーに読み取りアクセスを付与します。セラーはアカウントごとに専用バケットを使っても、アカウントごとの `prefix` で分離した共有バケットを使ってもよく、いずれの場合もバイヤーは自分のアカウントのパス配下のデータにのみアクセスできます。複数の購買プラットフォームが同じブランドで動作する場合、それぞれが別個のアカウント(オペレーター/エージェントでスコープ)を得るため、データはプレフィックスで分離されます。 バケットの場所は `sync_accounts` が返すアカウントオブジェクトに現れます: ```json theme={null} { "account_id": "acc_pinnacle_001", "status": "active", "reporting_bucket": { "protocol": "s3", "bucket": "seller-reports", "prefix": "accounts/pinnacle/adcp", "region": "us-east-1", "format": "jsonl", "compression": "gzip", "file_retention_days": 30, "setup_instructions": "https://seller.example.com/docs/bucket-access" } } ``` バイヤーは自分のスケジュールでバケットから読み取ります。セラーはプロダクトのレポート頻度でファイルをプッシュします。 **配信方法がレポートの経路を決めます:** * `get_media_buy_delivery` はすべての `media_buy` セラーの必須タスクです。セラーがどのプッシュ方法をサポートするかに関わらず、ポーリングは常にベースラインとして利用可能です。 * `reporting_delivery_methods` に `offline` が含まれ、アカウントに `reporting_bucket` が存在する場合、セラーは詳細な配信データをバケットにもプッシュします。バッチ基盤を持つバイヤーは効率のためバケットから読むべきです。 * バケット内のファイルは `file_retention_days`(`reporting_bucket` で宣言)の間保持されます。バイヤーはこのウィンドウ内にファイルを読まなければなりません。 * `get_media_buys` は常に、ステータス・合計・ペーシングのスナップショットを持つメディアバイオブジェクトを返します。詳細なレポートではなくステータス確認に使ってください。 オフラインファイル配信では、パブリッシャーは JSON Lines (JSONL)、CSV、Parquet、Avro、ORC でレポートデータを提供できます。いずれも Webhook ペイロードのネスト構造を保つためバッチ処理に適しています。 JSONL と CSV は `.jsonl.gz`/`.csv.gz` のように gzip 圧縮してストレージと転送コストを削減できます。Parquet、Avro、ORC は内部圧縮を使うため、これらのフォーマットではトップレベルの `compression` フィールドは無視されます。 #### JSON Lines (JSONL) 1 行 1 メディアバイ(改行区切り JSON)。各行にレポート期間とパッケージレベルのデータを持つ 1 つのデリバリーオブジェクトが含まれます。ネスト構造を保持しつつ、行単位で簡単にパースできるためストリーミング処理に適しています。 **Example JSONL file:** ```jsonl theme={null} {"notification_type": "scheduled", "sequence_number": 5, "next_expected_at": "2024-02-06T08:00:00Z", "reporting_period": {"start": "2024-02-05T00:00:00Z", "end": "2024-02-05T23:59:59Z"}, "currency": "USD", "media_buy_id": "mb_001", "media_buy_id": "campaign_a", "status": "active", "totals": {"impressions": 50000, "spend": 1750.00, "clicks": 100, "ctr": 0.002}, "by_package": [{"package_id": "pkg_001", "impressions": 30000, "spend": 1050.00, "pacing_index": 0.95, "pricing_model": "cpm", "rate": 0.035, "currency": "USD"}, {"package_id": "pkg_002", "impressions": 20000, "spend": 700.00, "pacing_index": 0.98, "pricing_model": "cpm", "rate": 0.035, "currency": "USD"}]} {"notification_type": "scheduled", "sequence_number": 5, "next_expected_at": "2024-02-06T08:00:00Z", "reporting_period": {"start": "2024-02-05T00:00:00Z", "end": "2024-02-05T23:59:59Z"}, "currency": "USD", "media_buy_id": "mb_002", "media_buy_id": "campaign_b", "status": "active", "totals": {"impressions": 200000, "spend": 9000.00, "clicks": 400, "ctr": 0.002}, "by_package": [{"package_id": "pkg_003", "impressions": 200000, "spend": 9000.00, "pacing_index": 1.02, "pricing_model": "cpm", "rate": 45.00, "currency": "USD"}]} {"notification_type": "scheduled", "sequence_number": 5, "next_expected_at": "2024-02-06T08:00:00Z", "reporting_period": {"start": "2024-02-05T00:00:00Z", "end": "2024-02-05T23:59:59Z"}, "currency": "USD", "media_buy_id": "mb_003", "media_buy_id": "campaign_c", "status": "active", "totals": {"impressions": 75000, "spend": 3375.00, "clicks": 150, "ctr": 0.002}, "by_package": [{"package_id": "pkg_004", "impressions": 75000, "spend": 3375.00, "pacing_index": 0.96, "pricing_model": "cpcv", "rate": 0.045, "currency": "USD"}]} ``` #### CSV **For tabular analysis** CSV files require unnesting nested arrays. Each record should be unnested to the `by_package` level, meaning one row per package with parent-level data (reporting period, media buy info, totals) duplicated. **Example CSV structure:** ```csv theme={null} notification_type,sequence_number,next_expected_at,reporting_period_start,reporting_period_end,currency,media_buy_id,media_buy_id,status,totals_impressions,totals_spend,totals_clicks,totals_ctr,by_package_package_id,by_package_impressions,by_package_spend,by_package_clicks,by_package_pacing_index,by_package_pricing_model,by_package_rate,by_package_currency scheduled,5,2024-02-06T08:00:00Z,2024-02-05T00:00:00Z,2024-02-05T23:59:59Z,USD,mb_001,active,50000,1750.00,100,0.002,pkg_001,30000,1050.00,60,0.95,cpm,0.035,USD scheduled,5,2024-02-06T08:00:00Z,2024-02-05T00:00:00Z,2024-02-05T23:59:59Z,USD,mb_001,active,50000,1750.00,100,0.002,pkg_002,20000,700.00,40,0.98,cpm,0.035,USD scheduled,5,2024-02-06T08:00:00Z,2024-02-05T00:00:00Z,2024-02-05T23:59:59Z,USD,mb_002,active,200000,9000.00,400,0.002,pkg_003,200000,9000.00,400,1.02,cpm,45.00,USD scheduled,5,2024-02-06T08:00:00Z,2024-02-05T00:00:00Z,2024-02-05T23:59:59Z,USD,mb_003,active,75000,3375.00,150,0.002,pkg_004,75000,3375.00,150,0.96,cpcv,0.045,USD ``` #### Parquet **For high-volume analytics** Columnar format optimized for analytics workloads. Excellent compression ratios. Supports nested structures natively. Best for data warehouses and big data processing. **Example Parquet schema:** ```json theme={null} { "type": "record", "name": "MediaBuyDelivery", "fields": [ {"name": "notification_type", "type": "string"}, {"name": "sequence_number", "type": "int"}, {"name": "next_expected_at", "type": "string"}, {"name": "reporting_period", "type": { "type": "record", "name": "ReportingPeriod", "fields": [ {"name": "start", "type": "string"}, {"name": "end", "type": "string"} ] }}, {"name": "currency", "type": "string"}, {"name": "media_buy_id", "type": "string"}, {"name": "media_buy_id", "type": "string"}, {"name": "status", "type": "string"}, {"name": "totals", "type": { "type": "record", "name": "Totals", "fields": [ {"name": "impressions", "type": "long"}, {"name": "spend", "type": "double"}, {"name": "clicks", "type": "long"}, {"name": "ctr", "type": "double"} ] }}, {"name": "by_package", "type": { "type": "array", "items": { "type": "record", "name": "PackageDelivery", "fields": [ {"name": "package_id", "type": "string"}, {"name": "impressions", "type": "long"}, {"name": "spend", "type": "double"}, {"name": "pacing_index", "type": "double"}, {"name": "pricing_model", "type": "string"}, {"name": "rate", "type": "double"}, {"name": "currency", "type": "string"} ] } }} ] } ``` #### Avro **スキーマリッチなストリーミングパイプライン向け** スキーマを埋め込んだ行指向フォーマット。自己記述的で、リーダーは外部のスキーマファイルを必要としません。スキーマの進化(フィールドの追加/削除)を優雅に扱えます。Kafka と Hadoop のエコシステムで一般的。内部圧縮(snappy、deflate、zstd)を使用します。 **Avro スキーマの例:** ```json theme={null} { "type": "record", "name": "MediaBuyDelivery", "namespace": "org.example.reporting", "fields": [ {"name": "notification_type", "type": "string"}, {"name": "sequence_number", "type": "int"}, {"name": "next_expected_at", "type": "string"}, {"name": "reporting_period", "type": { "type": "record", "name": "ReportingPeriod", "fields": [ {"name": "start", "type": "string"}, {"name": "end", "type": "string"} ] }}, {"name": "currency", "type": "string"}, {"name": "media_buy_id", "type": "string"}, {"name": "status", "type": "string"}, {"name": "totals", "type": { "type": "record", "name": "Totals", "fields": [ {"name": "impressions", "type": "long"}, {"name": "spend", "type": "double"}, {"name": "clicks", "type": "long"}, {"name": "ctr", "type": "double"} ] }}, {"name": "by_package", "type": { "type": "array", "items": { "type": "record", "name": "PackageDelivery", "fields": [ {"name": "package_id", "type": "string"}, {"name": "impressions", "type": "long"}, {"name": "spend", "type": "double"}, {"name": "pacing_index", "type": "double"}, {"name": "pricing_model", "type": "string"}, {"name": "rate", "type": "double"}, {"name": "currency", "type": "string"} ] } }} ] } ``` #### ORC **Hive/Spark 分析向け** Hadoop エコシステムのツール(Hive、Spark、Presto)での読み取り中心の分析に最適化された列指向フォーマット。述語プッシュダウン、組み込みインデックス、軽量圧縮(snappy、zlib、zstd)が I/O を削減します。struct と array 型を通じてネスト構造をサポートします。 ORC は Parquet と同じ論理スキーマを使います。データウェアハウスが Hive ネイティブなら ORC を、より広いツール互換性なら Parquet を選んでください。 **File Structure:** Each file contains one media buy delivery per line (JSONL), row (CSV/Parquet/ORC), or record (Avro). Files may contain: * Multiple media buy deliveries (one per line/row) * Multiple reporting periods for the same media buy (separate rows) * Multiple media buys (each with its own rows) **Processing Recommendations:** * Process files in chronological order using file timestamps * Handle duplicate files gracefully (idempotent processing) * Validate file integrity using checksums if provided * Monitor for missing files and alert on gaps ### オフライン配信のセキュリティ考慮事項 オフラインファイルは `file_retention_days` の間 at rest で存在するため、IAM ポリシーの設定ミスはテナントをまたいで過去のレポートを漏洩させます。[一般的なセキュリティ管理](/docs/building/by-layer/L1/security)が適用されます。オフライン固有の要件は次のとおりです: * **アクセスは秘匿ではなく IAM レイヤーでスコープする。** バイヤーの読み取りアクセスは `{bucket}/{prefix}/*` にスコープされなければなりません(MUST。S3 のバケットポリシー条件、GCS の `resource.name.startsWith(...)` による条件付き IAM バインディング、またはプレフィックスにスコープした Azure SAS)。セラーがアカウントごとに一つのプレフィックス配下にしか書き込まない場合でも、バケット全体の読み取り付与は非適合です。 * **リストもスコープする。** プレフィックスのスコープは、オブジェクトレベルの操作(`s3:GetObject`)とリスト(`s3:prefix` 条件付きの `s3:ListBucket`)の両方をカバーしなければなりません(MUST)。`GetObject` をプレフィックスにスコープしつつ `ListBucket` を未スコープのままにするポリシーは、バイヤーが他テナントのプレフィックス名を列挙できてしまいます——そのオブジェクトへの読み取りアクセスがなくてもテナント分離の失敗です。GCS の `storage.objects.list` と Azure の `list` SAS 権限にも同じことが当てはまります。 * **アカウント終了時にアクセスを取り消す。** セラーが `account.status` の `inactive`・`suspended`・`closed` への遷移を出すとき、セラーは関連する認証情報の受け入れを停止しなければならず(MUST)、バイヤーはそのステータス変更を、自分側で対応する IAM の信頼を削除するトリガーとして扱うべきです(SHOULD)。廃止されたバケットに付与されたままのセラー IAM ロールは横展開のリスクです。 PII のスクラビング要件([上記](#pii-scrubbing-for-gdpr-ccpa)参照)はオフラインファイルにも同様に適用されます——ファイルは at rest で蓄積するため、配信時ではなく収集レイヤーでスクラブしてください。 `setup_instructions` はセラー提供の URL です。これはオペレーター向けのドキュメントであり、エージェントが消費するコンテンツではありません。バイヤーエージェントはこの URL を自動取得してはならず(MUST NOT)、人間のオペレーターに提示すべきです(SHOULD)。実装が取得を選ぶ場合(例: オペレーターに見せる前に対象をプレビューする)、[Webhook URL の SSRF 検証](/docs/building/by-layer/L1/security#webhook-url-validation-ssrf)を適用し、取得したコンテンツは間接プロンプトインジェクションの防御なしに LLM コンテキストへ渡してはなりません(MUST NOT)——このフィールドのセラー制御のテキストは、認証情報のローテーション、請求の変更、下流エージェントの挙動の改変を指示しうる。 ## Data Reconciliation **`get_media_buy_delivery` API はすべてのキャンペーン指標の正式な信頼できる唯一の情報源です。** ポーリングは常にベースラインとして利用可能です。セラーがプッシュ型の配信(Webhook やオフラインバケット)もサポートする場合、それらの方法はタイムリーなデータを提供しますが、`get_media_buy_delivery` が照合の経路であり続けます。 照合は **あらゆるレポート配信方法** で重要です。 * **Webhooks**: ネットワーク障害やサーキットブレーカーで欠落する場合があります * **オフラインファイル**: 遅延・破損・処理失敗の可能性があります * **ポーリング**: API 障害中にデータを欠損する場合があります * **遅延データ**: 初回レポートから 24〜48 時間以上後に届くインプレッションがある(全手段共通) #### Reconciliation Process バイヤーは定期的に配信データを API と照合し、精度を確認すべきです。 **Recommended Reconciliation Schedule:** * **Hourly delivery**: Reconcile via API daily * **Daily delivery**: Reconcile via API weekly * **Monthly delivery**: Reconcile via API at month end + 7 days * **Campaign close**: Always reconcile after campaign\_end + attribution\_window **Reconciliation Logic:** ```javascript theme={null} async function reconcileWebhookData(mediaBuyId, startDate, endDate) { // Get authoritative data from API const apiData = await adcp.getMediaBuyDelivery({ media_buy_id: mediaBuyId, date_range: { start: startDate, end: endDate } }); // Compare with webhook data in local database const webhookData = await db.getWebhookTotals(mediaBuyId, startDate, endDate); const discrepancy = { impressions: apiData.totals.impressions - webhookData.impressions, spend: apiData.totals.spend - webhookData.spend, clicks: apiData.totals.clicks - webhookData.clicks }; // Acceptable discrepancy thresholds const impressionVariance = Math.abs(discrepancy.impressions) / apiData.totals.impressions; const spendVariance = Math.abs(discrepancy.spend) / apiData.totals.spend; if (impressionVariance > 0.02 || spendVariance > 0.01) { // Significant discrepancy (>2% impressions or >1% spend) console.warn(`Reconciliation discrepancy for ${mediaBuyId}:`, discrepancy); // Update local database with authoritative API data await db.updateCampaignTotals(mediaBuyId, apiData.totals); // Alert if discrepancy is unusually large if (impressionVariance > 0.10 || spendVariance > 0.05) { await alertOps(`Large reconciliation discrepancy detected`, { media_buy_id: mediaBuyId, webhook_totals: webhookData, api_totals: apiData.totals, discrepancy }); } } return { status: impressionVariance < 0.02 ? 'reconciled' : 'discrepancy_found', api_data: apiData.totals, webhook_data: webhookData, discrepancy }; } ``` **Why Discrepancies Occur:** 1. **Delivery failures**: Webhooks missed, offline files corrupted, API timeouts during polling 2. **Late-arriving data**: Impressions attributed after initial reporting (all delivery methods) 3. **Data corrections**: Publisher adjusts metrics after initial reporting 4. **Processing errors**: Buyer-side failures to process delivered data 5. **Timezone differences**: Period boundaries may differ between delivery and API query **Source of Truth Rules:** * **For billing**: Always use `get_media_buy_delivery` API at campaign end + attribution window * **For real-time decisions**: Use delivered data (webhook/file/poll) for speed, reconcile later * **For discrepancies**: API data wins, update local records accordingly * **For audits**: API provides complete historical data, delivered data is ephemeral **Best Practices:** * Store webhook `sequence_number` to detect missed notifications * Run automated reconciliation daily for active campaigns * Alert on discrepancies >2% for impressions or >1% for spend * Use API data for all financial reporting and invoicing * Document reconciliation process for audit compliance #### Late-Arriving Impressions Ad serving data often arrives with delays due to attribution windows, offline tracking, and pipeline latency. Publishers declare `expected_delay_minutes` in `reporting_capabilities`: * **Display/Video**: Typically 4-6 hours * **Audio**: Typically 8-12 hours * **CTV**: May be 24+ hours This represents when **most** data is available, not **all** data. #### 遅延データの扱い 過去の期間に遅延データが届いた場合、その期間を `is_adjusted: true` 付きで **再送** します。 ```json theme={null} { "notification_type": "adjusted", "reporting_period": { "start": "2024-02-01T00:00:00Z", "end": "2024-02-01T23:59:59Z" }, "media_buy_deliveries": [{ "media_buy_id": "mb_001", "is_adjusted": true, "totals": { "impressions": 51000, // Updated total (was 50000) "spend": 1785 // Updated spend (was 1750) } }] } ``` **バイヤー側の処理:** ```javascript theme={null} function processWebhook(webhook) { for (const delivery of webhook.media_buy_deliveries) { if (delivery.is_adjusted) { // Replace entire period with updated totals db.replaceCampaignPeriod( delivery.media_buy_id, webhook.reporting_period, delivery.totals ); } else { // Normal new period data db.insertCampaignPeriod(delivery.media_buy_id, webhook.reporting_period, delivery.totals); } } } ``` **調整済み期間を送るべき場合:** * 大きなデータ変化(インプレッション ±2% 超、または消化額 ±1% 超) * campaign\_end + attribution\_window 時点での最終照合 * データ品質修正 ポーリングのみの場合、バイヤーは API 結果を時系列で比較することで調整を検知します。 #### Webhook の信頼性 レポート Webhook は AdCP 標準の信頼性パターンに従います。 * **At-least-once 配信**: 同じ通知が複数回届く場合があります * **ベストエフォート順序**: 順不同で届く場合があります * **タイムアウトとリトライ**: 配信失敗時は回数を限定して再試行 実装の詳細は [Webhooks](/docs/building/by-layer/L3/webhooks) を参照してください。 ## 最適化の戦略 ### コンバージョン最適化 メディアバイパッケージに最適化目標を設定することで、特定の成果(目標 CPC、CPV、ROAS、CPA など)に向けた配信を促します。指標目標(クリック、視聴数)はイベント設定不要で機能します。イベント目標にはイベントソースの設定とコンバージョンデータが必要です。 完全なセットアップ手順は [Conversion Tracking](/docs/media-buy/conversion-tracking/) を、`optimization_goals` 配列のリファレンスは [Optimization Goals](/docs/media-buy/conversion-tracking/#optimization-goals) を参照してください。 ### 予算最適化 * [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) で成果の高低パッケージ間での **再配分** * **ペーシング調整** — `even`、`asap`、`front_loaded` の配信方式を切り替え * **消化効率** — パッケージ間でのコンバージョンあたりコストを比較し、優れたパッケージへ予算をシフト ### クリエイティブ最適化 * [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) でクリエイティブ別の内訳を使った **パフォーマンス分析** * **A/B テスト** — `creative_assignments` でウェイト付き複数クリエイティブを割り当て * **リフレッシュ戦略** — 疲弊を防ぐため、ライブラリ対応のセラーには [`sync_creatives`](/docs/creative/task-reference/sync_creatives) で、インライン専用のセラーには [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) のインライン `packages[].creatives` でクリエイティブを交換 ### ターゲティング改善 * **地理的最適化** — 地域別配信データに基づき `targeting_overlay` を調整 * **フリクエンシー管理** — 配信パターンに基づき `frequency_cap`(クールダウン抑制または max\_impressions/per/window 上限)をチューニング ## パフォーマンスフィードバックループ ビジネス成果をパブリッシャーにフィードバックすることで、AI 主導の最適化を可能にします。詳細な API は [`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback) を参照してください。 ### パフォーマンスインデックスの概念 相対的な成果を示す正規化スコア。 * `0.0` = 測定可能な価値や影響なし * `1.0` = ベースライン/想定パフォーマンス * `> 1.0` = 平均以上(例: 1.45 は 45% 改善) * `< 1.0` = 平均未満(例: 0.8 は 20% 低下) ### パフォーマンスデータの共有 バイヤーは [`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback) タスクを使って任意で成果を共有できます。 ```json theme={null} { "media_buy_id": "gam_1234567890", "measurement_period": { "start": "2024-01-15T00:00:00Z", "end": "2024-01-21T23:59:59Z" }, "performance_index": 1.35, "metric_type": "conversion_rate" } ``` ### サポートされる指標 * **overall\_performance**: キャンペーン全体の成果 * **conversion\_rate**: クリック後/視聴後コンバージョン率 * **brand\_lift**: ブランド認知/想起のリフト * **click\_through\_rate**: クリエイティブのエンゲージメント * **completion\_rate**: 動画/音声の完了率 * **viewability**: ビューアブル率 * **brand\_safety**: ブランドセーフティ順守 * **cost\_efficiency**: 望む成果あたりのコスト ### パブリッシャーが活用する方法 パブリッシャーはパフォーマンスインデックスを活用して: 1. **配信最適化**: 高成果セグメントへ配信をシフト 2. **価格調整**: 実証された価値に基づき CPM を更新 3. **プロダクト改善**: 成果パターンに基づきプロダクト定義を磨く 4. **アルゴリズム強化**: 実際のビジネス成果で ML モデルを学習 ### プライバシーとデータ共有 * パフォーマンス共有は任意でありバイヤーが制御します * 集計されたパフォーマンス傾向はプラットフォーム全体の改善に利用される場合があります * 個別キャンペーンの詳細はバイヤーとパブリッシャーの関係内に留まる ### ディメンション内訳 配信データは各パッケージ内で複数のディメンションに分解できます。バイヤーは `get_media_buy_delivery` の `reporting_dimensions` パラメーターで特定の内訳を指定します。各内訳は `by_package` 項目内に `by_*` 配列として現れ、`by_creative` と同じ構成パターンに従います。 | ディメンション | 内訳フィールド | 必須フィールド | 追加フィールド | ケイパビリティ宣言 | | ------------ | -------------------- | -------------------------------------------------------- | ------------------------------------ | ------------------------------------ | | 地理 | `by_geo` | `geo_level`, `geo_code`, `impressions`, `spend` | `system`, `country`, `geo_name` | `supports_geo_breakdown` | | デバイスタイプ | `by_device_type` | `device_type`, `impressions`, `spend` | — | `supports_device_type_breakdown` | | デバイスプラットフォーム | `by_device_platform` | `device_platform`, `impressions`, `spend` | — | `supports_device_platform_breakdown` | | オーディエンス | `by_audience` | `audience_id`, `audience_source`, `impressions`, `spend` | `audience_name` | `supports_audience_breakdown` | | プレースメント | `by_placement` | `placement_id`, `impressions`, `spend` | `publisher_domain`, `placement_name` | `supports_placement_breakdown` | 各内訳エントリは `delivery-metrics`(clicks、conversions、その他の任意メトリクス)のすべてのフィールドに加え、ディメンション固有のフィールドを継承します。各エントリには必須フィールド列に示したフィールドが必要です。どのディメンションが利用可能かはプロダクトの `reporting_capabilities` で確認してください。同じセラーの異なるプロダクトが異なる内訳をサポートしうるため、プロダクトレベルのケイパビリティが権威的です。`supports_geo_breakdown` は利用可能なレベルとシステムを宣言するオブジェクトで、この表の他のケイパビリティ宣言はブール値のフラグです。`supports_geo_breakdown` 内では、`country` と `region` はブール値で、`metro` は `metro-system` の値でキー付けされ、ネイティブな `postal_area` は ISO 3166-1 alpha-2 の国でキー付けされ、国ローカルな `postal-system` 値の配列を持ちます。geo の行は `geo_level: "metro"` と `"postal_area"` で `system` を使います。ネイティブな郵便の行は `country` も含みます。非推奨の国融合型の郵便システムは互換性のため引き続き受け付けられます。 プレースメントのアイデンティティはパブリッシャースコープです。プレースメント行は `publisher_domain`(プロダクトの `placements[]` エントリ由来のパブリッシャー名前空間)を運んでよく(MAY)、それが存在する場合、バイヤーはマルチパブリッシャープロダクトについて `{publisher_domain, placement_id}` を安定したプレースメントのアイデンティティとして扱えます。セラーは、プロダクトのプレースメントがそれを運ぶ場合は常に `publisher_domain` を出すべきです(SHOULD。`kind: "publisher_ref"` では常に真)。セラーがそれを省略してよいのは、セラーエージェント自身のドメインが名前空間であるレガシーな単一パブリッシャーの文脈における `kind: "seller_inline"` のプレースメントに限られます。`publisher_domain` が省略された場合、バイヤーはそのレガシーな単一パブリッシャーの文脈でのみ `placement_id` をセラーエージェント自身のパブリッシャードメインに対して解釈してよく(MAY)、それ以外ではパブリッシャー横断のプレースメントキーを推測すべきではありません。各プレースメントは正確に一つのパブリッシャー名前空間に属するため、`publisher_domain` は単一値です。 内訳はオプトイン方式で、明示的に指定しない限りディメンションデータは返されません。指定したディメンションをサポートしていないセラーはそれを黙って省略します。各内訳配列には `limit` を超える追加行の有無を示す `by_*_truncated` ブール値が付属します。 ## ターゲティングの一貫性 レポーティングは AdCP の [Targeting](/docs/media-buy/advanced-topics/targeting) アプローチに沿って設計されており、以下を可能にします。 * キャンペーンライフサイクル全体での **一貫した分析** * ターゲティングパラメーターによる **きめ細かい内訳** * ポートフォリオ最適化のための **キャンペーン横断インサイト** ### Target → Measure → Optimize ターゲティングとレポーティングの一貫性が好循環を生み出します。 1. **Target**: ブリーフとオーバーレイでオーディエンスを定義(例:「主要都市圏のモバイルユーザー」) 2. **Measure**: 同じ属性でレポート(デバイスタイプと地域別のパフォーマンスを追跡) 3. **Optimize**: 配信改善にパフォーマンスをフィードバック(高成果セグメントへ予算をシフト) ## 標準指標 すべてのプラットフォームがサポートしなければなりませんコア指標: * **impressions**: 広告表示数 * **spend**: 通貨建て消化額 * **clicks**: クリック数(該当する場合) * **ctr**: クリック率(clicks/impressions) 任意のオプション指標: * **conversions**: クリック後/視聴後コンバージョン * **viewability**: ビューアブルインプレッションの割合 * **completion\_rate**: 動画/音声の完了率 * **engagement\_rate**: プラットフォーム固有のエンゲージメント指標 ## プラットフォーム固有の考慮事項 プラットフォームによってレポーティング/最適化の機能は異なります。 ### Google Ad Manager * 包括的なディメンション別レポーティング、リアルタイム/ヒストリカルデータ、高度なビューアビリティ指標 ### Kevel * リアルタイムレポーティング API、カスタム指標サポート、柔軟な集計オプション ### Triton Digital * 音声固有指標(完了率、スキップ率)、局別パフォーマンスデータ、デイパート分析 ## 高度な分析 ### キャンペーン横断分析 * 複数キャンペーンにまたがる **ポートフォリオパフォーマンス** * **オーディエンスオーバーラップ** とフリクエンシー管理 * キャンペーン間の **予算配分** 最適化 ### 予測インサイト * ヒストリカルデータに基づく **パフォーマンス予測** * AI 分析による **最適化レコメンデーション** * プロアクティブな調整のための **トレンド予測** ## レスポンスタイム 最適化オペレーションには予測可能なタイミングがあります。 * **デリバリーレポート**: 約 60 秒(データ集計) * **キャンペーン更新**: 分〜日単位(変更内容による) * **パフォーマンス分析**: 約 1 秒(キャッシュ済み指標) ## ベストプラクティス 1. **頻繁にレポートする**: 定期的なレポーティングが最適化機会を増やす 2. **ペーシングを追跡する**: 目標に対する配信を監視し、過不足を防ぐ 3. **パターンを分析する**: ディメンション横断でパフォーマンストレンドを探す 4. **レイテンシを考慮する**: 一部の指標には帰属遅延がある場合があります 5. **指標を正規化する**: パフォーマンス比較に一貫したベースラインを使用します ## メディアバイライフサイクルとの統合 最適化/レポーティングはアクティブなキャンペーン全期間を通じて継続するフェーズです。 * **作成との連携**: 学習を活かして将来のキャンペーン設定を改善 * **更新の指針**: キャンペーン変更のためのデータドリブンな意思決定 * **スケールの実現**: 実証された戦略を類似キャンペーンへ展開 * **AI へのフィード**: パフォーマンスデータが自動最適化を向上 ## 関連ドキュメント * **[`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery)** - デリバリーレポートの取得 * **[`update_media_buy`](/docs/media-buy/task-reference/update_media_buy)** - パフォーマンスに基づくキャンペーン変更 * **[Media Buy Lifecycle](/docs/media-buy/media-buys)** - キャンペーン管理の完全なワークフロー * **[Targeting](/docs/media-buy/advanced-topics/targeting)** - ブリーフベースのターゲティングとオーバーレイ # ポリシーコンプライアンス Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/media-buys/policy-compliance AdCP には広告業務全体でブランドセーフティと規制遵守を確保するための包括的なポリシーコンプライアンス機能が含まれます。本ドキュメントでは、メディアバイのライフサイクルを通じてポリシーチェックをどのように実装・適用すべきかを説明します。 ## 概要 AdCP におけるポリシーコンプライアンスの中心は必須の広告主ブランド記述である `brand_manifest` です。これによりパブリッシャーは次を行えます: * 在庫を提示する前に不適切な広告主をフィルタリング * カテゴリー固有の制限を適用 * ブランドセーフティ基準を維持 * 規制要件に準拠 ## Brand Manifest すべてのプロダクト探索およびメディアバイ作成リクエストには、広告主ブランドを記述する `brand_manifest` を含める必要があります: ```json theme={null} { "name": "Nike", "url": "https://nike.com", "category": "athletic_apparel" } ``` マニフェストが提供する内容: * **name**: リクエストを送る広告主/ブランド * **url**: 確認用のブランド公式サイト * **category** (任意): ポリシーフィルタリング用の業種カテゴリ 何をプロモーションするかを示す `brief` フィールドと組み合わせることで、パブリッシャーはポリシー判断に必要な完全なコンテキストを得られます。 ブリーフとブランド情報の詳細なガイドは [Brief Expectations](/docs/media-buy/product-discovery/brief-expectations) を参照してください。 ## ポリシーチェックの実装 パブリッシャーはワークフローの 2 つの重要なポイントでポリシーチェックを実装する必要があります: ### 1. プロダクト探索中 (`get_products`) `get_products` リクエストを受け取ったら、パブリッシャーは次を行います: 1. `brand_manifest` が存在し有効であることを検証します 2. ブランドとカテゴリ情報を抽出します 3. パブリッシャーポリシーに照らして確認します 4. 不適切なプロダクトを除外します **ポリシーチェックフローの例:** ```python theme={null} def check_brand_policy(brand_manifest: dict) -> PolicyResult: # Extract brand information brand_name = brand_manifest.get("name") brand_url = brand_manifest.get("url") category = brand_manifest.get("category") # Verify brand identity if needed if not verify_brand_domain(brand_name, brand_url): return PolicyResult( status="blocked", message="Brand verification failed" ) # Check blocked categories if category in BLOCKED_CATEGORIES: return PolicyResult( status="blocked", message=f"{category} advertising is not permitted on this publisher" ) # Check restricted categories if category in RESTRICTED_CATEGORIES: return PolicyResult( status="restricted", message=f"{category} advertising requires manual approval", contact="sales@publisher.com" ) return PolicyResult(status="allowed", category=category) ``` ### 2. メディアバイ作成時 (`create_media_buy`) メディアバイを作成する際は次を行います: 1. `brand_manifest` をパブリッシャーポリシーに照らして検証します 2. キャンペーンブリーフとの整合性を確認します 3. 必要に応じて手動レビューフラグを付ける 4. 違反時に適切なエラーを返す ## ポリシーコンプライアンスのレスポンス プロトコルは 3 つのコンプライアンスステータスを定義しています: ### `allowed` ブランドは初期ポリシーチェックを通過し、プロダクトが通常通り返されます。 ```json theme={null} { "products": [...], "policy_compliance": { "status": "allowed" } } ``` ### `restricted` ブランドカテゴリはプロダクトを表示する前に手動承認が必要です。 ```json theme={null} { "products": [], "policy_compliance": { "status": "restricted", "message": "Cryptocurrency advertising is restricted but may be approved on a case-by-case basis.", "contact": "sales@publisher.com" } } ``` ### `blocked` ブランドカテゴリがこのパブリッシャーではサポートできません。 ```json theme={null} { "products": [], "policy_compliance": { "status": "blocked", "message": "Publisher policy prohibits alcohol advertising without age verification capabilities." } } ``` ## クリエイティブ検証 アップロードされたすべてのクリエイティブは、宣言された `brand_manifest` に照らして検証する必要があります: 1. **自動分析**: クリエイティブ認識を用いてブランド一貫性を確認 2. **人によるレビュー**: センシティブなカテゴリに対する手動確認 3. **継続的モニタリング**: キャンペーン配信中の継続的なチェック これにより次を保証します: * クリエイティブ内容が宣言されたブランドと一致します * 誤解を招く広告や欺瞞的な広告がない * すべての関係者にとってのブランドセーフティ ## 一般的なポリシーカテゴリ パブリッシャーは通常、次のカテゴリに制限を設けます: ### ブロック対象カテゴリ * 違法な製品やサービス * 禁止コンテンツ(地域によって異なります) * 特別なライセンスを要するカテゴリ ### 制限対象カテゴリ(手動承認) * アルコール(年齢制限が必要な場合あり) * ギャンブル/ゲーム * 暗号資産/金融サービス * 政治広告 * ヘルスケア/医薬品 * 出会い系サービス ### 特別要件 * 政治広告は開示が必要な場合があります * ヘルスケアは免責事項が必要な場合があります * 金融サービスはコンプライアンスレビューが必要 ## 実装ベストプラクティス 1. **明確なコミュニケーション**: 制限の具体的な理由を示します 2. **連絡先情報**: 制限カテゴリに対する営業連絡先を含めます 3. **一貫した適用**: すべての広告主に対しポリシーを均等に適用します 4. **ドキュメンテーション**: 広告主向けに明確なポリシー文書を維持します 5. **異議申立手続き**: 広告主がポリシー例外を要求できるようにします ## エラーハンドリング メディアバイ作成時のポリシー違反例: ```json theme={null} { "error": { "code": "POLICY_VIOLATION", "message": "Brand category not permitted on this publisher", "field": "brand_manifest", "suggestion": "Contact publisher for category approval process" } } ``` ## HITL との統合 ポリシー判断は Human-in-the-Loop ワークフローをトリガーできます: 1. 制限カテゴリは `pending_manual` タスクを生成します 2. 人によるレビュワーがキャンペーンを評価します 3. 承認または却下が返信されます 4. その決定に基づきキャンペーンを進行または終了します ## 関連ドキュメント * [`get_products`](/docs/media-buy/task-reference/get_products) - ポリシーチェック付きのプロダクト探索 * [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) - バリデーションを含むメディアバイ作成 * [Principals & Security](/docs/media-buy/advanced-topics/accounts-and-security) - 認証と認可 # メディアバイ仕様 Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/specification AdCP メディアバイ仕様 — エージェント間広告のトランスポート層、タスク定義、JSONスキーマ、認証、およびコンプライアンス要件。 **AdCP 3.0 提案** - この仕様は AdCP 3.0 向けに開発中です。フィードバックは [GitHub Discussions](https://github.com/adcontextprotocol/adcp/discussions) から歓迎します。 **ステータス**: コメント募集中 **最終更新**: 2026年2月 本ドキュメントにおけるキーワード「MUST」「MUST NOT」「REQUIRED」「SHALL」「SHALL NOT」「SHOULD」「SHOULD NOT」「RECOMMENDED」「MAY」「OPTIONAL」は、[RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) に記載された定義に従って解釈されます。 ## 概要 メディアバイプロトコルは、AI による広告自動化のための標準インターフェースを定義します。このプロトコルにより、AIエージェントは広告インベントリの発見、キャンペーンの作成・管理、クリエイティブアセットの同期、自然言語インタラクションを通じたパフォーマンストラッキングを行えるようになります。 ## プロトコルの概要 メディアバイプロトコルが提供する機能: * キャンペーンブリーフに基づく自然言語によるインベントリ発見 * パッケージレベルの予算とターゲティングを伴うキャンペーン作成 * クリエイティブアセットの管理と同期 * パフォーマンストラッキングと最適化フィードバック * 人間参加型の承認ワークフロー ## トランスポート要件 セールスエージェントは以下のトランスポートのうち少なくとも1つをサポートしなければなりません。 | トランスポート | プロトコル | 説明 | | ------- | ---------------------- | --------------------------- | | MCP | Model Context Protocol | JSON-RPC によるツールベースのインタラクション | | A2A | Agent-to-Agent | メッセージベースのインタラクション | セールスエージェントは優先トランスポートとして MCP をサポートすべきです。 セールスエージェントは `get_adcp_capabilities` を通じてメディアバイプロトコルのサポートを宣言しなければなりません。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/protocol/get-adcp-capabilities-response.json", "status": "completed", "adcp": { "major_versions": [2], "idempotency": { "supported": true, "replay_ttl_seconds": 86400 } }, "supported_protocols": ["media_buy"], "account": { "supported_billing": ["operator", "agent"] } } ``` ## コアコンセプト ### リクエストロール すべてのメディアバイリクエストには3つのエンティティが関与します。 * **オーケストレーター**: APIリクエストを行うプラットフォーム(例: DSP、トレーディングデスク) * **アカウント**: 請求関係 — 誰に請求が行き、どのレートが適用されるか(`account_id` で識別) * **エージェント**: バイを実行するエンティティ(認証トークンで識別) ### セールスエージェントの種類 **パブリッシャーセールスエージェント** — 単一パブリッシャーのインベントリを代表する: * セールスエージェントは販売を許可されたインベントリの商品のみを返さなければなりません * セールスエージェントは該当する場合 `adagents.json` を通じて認可を検証しなければなりません **アグリゲーターセールスエージェント** — 複数のパブリッシャーを代表する: * セールスエージェントは各商品のソースパブリッシャーを明確に特定しなければなりません * セールスエージェントはインベントリの出所を偽って伝えてはなりません ### 識別子 * **`media_buy_id`**: メディアバイの一意識別子。セールスエージェントは作成成功時にこれを返さなければなりません。オーケストレーターはメディアバイに対するすべての後続操作にこれを使用しなければなりません。`media_buy_id` は、認証済みアカウントが所有するセラーのアドサーバー上の任意のオーダーへの安定したハンドルです——元々 AdCP 経由で発注されたオーダーに限りません。 * **`package_id`**: メディアバイ内のパッケージの一意識別子。セールスエージェントは作成された各パッケージに対してこれを返さなければなりません。 * **`idempotency_key`**: 安全なリトライのためのクライアント生成の一意キー。同じアカウントに対して重複キーを受け取ったセールスエージェントは、再実行するのではなく元のレスポンスを返さなければなりません。 ### アカウントの所有権と作成サーフェス AdCP はセラーの広告オペレーションに対するプロトコルであり、その傍らに置かれる影の台帳ではありません。アカウントスコープのタスク(`get_media_buys`、`get_media_buy_delivery`、`update_media_buy`、該当する場合はクリエイティブの同期)は、リソースがどのサーフェスを通じて作成されたかではなく、**アカウントの所有権**によってスコープされます。セールスエージェントは、これらのタスクのために自身のインベントリを「AdCP 管理」と「AdCP 外」のサブセットに分割してはなりません(MUST NOT)。 具体的には: * `get_media_buys` は、認証済みアカウントが所有するすべてのメディアバイを返さなければなりません(MUST)——`create_media_buy` 経由で作成されたか、セラーのネイティブ API や UI 経由か、手動トラフィッキング経由か、レガシー/サードパーティのシステム経由かを問いません。適用されるのは宣言された `status_filter` とページネーションのみです。 * `get_media_buys` が返す `media_buy_id` はすべて、`get_media_buy_delivery` の有効な引数であり、かつその `valid_actions` に列挙されたすべてのアクションについて `update_media_buy` の有効な引数でなければなりません(MUST)。 * セールスエージェントは、元々 AdCP を通じて発注されたものではないことを理由に、バイを読み取り専用としたり、隠したり、`MEDIA_BUY_NOT_FOUND` を返したりしてはなりません(MUST NOT)。 * 特定のアクションがビジネス上の理由(契約上の義務、プラットフォームの制約、ポリシー)で利用できない場合、セールスエージェントはそれを、アカウントスコープの一覧からバイを拒むのではなく、そのアクションを `valid_actions` から省略することで表現します。 * **作成サーフェスは決してビジネス上の理由になりません。** セールスエージェントは、バイが AdCP の外で作成されたという理由だけで、`valid_actions` からアクションを省略したり、それ以外は有効な更新に対して `INVALID_STATE` を返したりしてはなりません(MUST NOT)。`valid_actions` の省略が正当なのは、同じ状態にある AdCP 作成のバイにも等しく適用されるであろう、実際の契約上、プラットフォーム上、またはポリシー上の制約に根拠がある場合のみです。AdCP 外のバイを体系的に空の `valid_actions` で返すセラーは非準拠です。その振る舞いはバイを隠しているのと区別がつかず、上記のルールの規範的な意図を無効にするからです。 #### 分離はアカウント境界で行う セラーが、あるバイの集合を呼び出し元の運用上の到達範囲の外に置く正当な理由を持つ場合——子セラーのモデル、NDA スコープの PMP ディール、サンドボックスと本番の分離、テナントレベルのプライバシー分割——正しいメカニズムはアカウント内でのフィルタリングではなく、**アカウントの分割**です: * 隠すサブセットを、呼び出し元が参照する権限を持たない別のアカウント(またはサブアカウント)として公開します。 * 呼び出し元が権限を*持つ*任意のアカウント内では、上記のルールに従って、そのアカウントが所有するバイの完全な集合を返します。 アカウント境界は、アクセス分割のための AdCP のプリミティブです。`get_agent_capabilities` は、あるアカウントに対して付与されたスコープを調べるためのサーフェスです。呼び出し元は自分が見られるものを見られます。見られないものは、彼らが持たないアカウント参照の背後にあります。アカウント内でのフィルタリング——あるアカウントに権限を持つ呼び出し元に対して、そのアカウントのバイの一部しか返さないこと——は、このルールが禁じる影の台帳の問題を再導入します。 ### 非同期オペレーション メディアバイプロトコルは設計上非同期です。オペレーションは即座に返ってくる場合も、延長された処理が必要な場合もある: * **同期レスポンス**: セールスエージェントは完了した結果を即座に返してもよい * **非同期レスポンス**: セールスエージェントはタスクリファレンスとともに `status: "submitted"` または `status: "working"` を返してもよい * **人間参加型**: セールスエージェントは、レビュアーが動くまでタスクを `status: "submitted"` に保つことで、内部の人によるレビュー(例: IO への署名)を要求してもよい(MAY)。セールスエージェントは、バイヤーが応答しなければならない場合(例: 予算の確認)に `status: "input-required"` を使ってもよい(MAY)。人による承認はタスクレイヤーでモデル化されます——`pending_approval` というメディアバイのステータスは存在しません(その値はアカウントのオンボーディングレビュー向けに Account.status にのみ存在します) * **拒否**: セールスエージェントは、プラットフォームのセットアップによってオーダーを履行できないことが判明した場合(例: インベントリの売り切れ、アドサーバーのセットアップ中に発見されたポリシー上の問題)、`pending_creatives` または `pending_start` ステータスのメディアバイを拒否してもよい(MAY)。オーケストレーターは `rejected` を終端状態として扱わなければなりません。セラーが作成時にオーダーを受け入れたくない場合は、`rejected` ステータスのメディアバイを作成するのではなく、`create_media_buy` をエラーで失敗させるべきです(SHOULD)。 オーケストレーターはすべてのレスポンスタイプを処理しなければならず、同期完了を前提としてはなりません。 ### メディアバイの状態遷移 メディアバイは定義された状態の集合を進みます。終端状態(`completed`、`rejected`、`canceled`)からのそれ以上の遷移はありません。 ``` create_media_buy ──┬──▶ pending_creatives ──▶ pending_start ──▶ active ├──▶ active ──(pause)──▶ paused └──▶ paused (when created with paused: true) paused ──(resume)──▶ active active ─────────────▶ completed (terminal) paused ─────────────▶ completed (terminal) pending_creatives ──▶ rejected (terminal) — seller rejects during setup pending_start ──────▶ rejected (terminal) — seller rejects during setup Any non-terminal ──── update(canceled: true) ──▶ canceled (terminal) ``` **ルール:** * セールスエージェントは `create_media_buy` から `active`、`paused`、`pending_creatives`、`pending_start` を返してもよい(MAY)(プラットフォームのセットアップ時間とバイヤーの作成時の `paused` リクエストに基づくセラーの選択) * セールスエージェントは、フライト日が到来したときにメディアバイを `pending_start` から `active` へ遷移させなければなりません(MUST)。セールスエージェントは、この遷移が起きたときにウェブフックでオーケストレーターに通知すべきです(SHOULD)。 * コミットされた `create_media_buy` の成功レスポンスは**オーダーの確定**を構成します。セールスエージェントは create/get のレスポンスに `confirmed_at` を含めなければなりません(MUST)。その値はセラーのコミットのタイムスタンプ、または、存在するもののまだセラーのコミットを待っている暫定的なバイの場合は `null` です。 * セールスエージェントは create、get、update のレスポンスに `revision` を含めなければなりません(MUST)。リビジョン番号は、状態を変更するあらゆる変更または更新のたびに増加しなければなりません(MUST)。 * `active` ↔ `paused` の遷移は、`paused: true` または `paused: false` を指定した `update_media_buy` を使います * トップレベルの `paused: true` を指定した `create_media_buy` は、そうでなければ `active` になるはずのメディアバイを保留状態で作成します。セットアップのブロッカーは依然として優先されます: クリエイティブがなければ `pending_creatives` に、将来のフライトなら開始条件が満たされるまで `pending_start` になります。それらのブロッカーが見えている間、保留は潜在的な状態です。クリエイティブが揃いフライトが開始できるようになると、そのバイは `active` ではなく `paused` に入ります。 * `paused: false` を指定した `update_media_buy` は、現在 `paused` のバイを再開するか、可視状態がまだ `pending_creatives` または `pending_start` である間に、作成時の潜在的な保留を解除します。潜在的な保留の解除はセットアップのブロッカーを迂回しません。可視ステータスは、クリエイティブが提供されフライトが開始できるようになるまで pending のままです。 * フライトが終了、ゴールが達成、または予算が消化されたとき、`active` または `paused` → `completed`(セラー起点) * バイヤー起点のキャンセルは、`canceled: true` と任意の `cancellation_reason` を指定した `update_media_buy` を使います * セラー起点のキャンセル(例: ポリシー違反、インベントリの引き上げ)は、メディアバイを `cancellation.canceled_by: "seller"` とともに `canceled` へ遷移させます。セラー起点のキャンセルを行う場合、セラーはウェブフックでオーケストレーターに通知しなければなりません(MUST)。 * セラー起点の拒否(`pending_creatives` または `pending_start` から)も、`push_notification_config` を通じてオーケストレーターに通知しなければなりません(MUST)。ウェブフックのペイロードには `media_buy_id`、`status: "rejected"`、`rejection_reason` を含めなければなりません(MUST)。 * セールスエージェントは、メディアバイまたはパッケージを `canceled` へ遷移させるとき、`canceled_at` と `canceled_by` を持つ `cancellation` オブジェクトを含めなければなりません(MUST) * セールスエージェントは、終端状態でないメディアバイのバイヤーによるキャンセルを、エラーコード `NOT_CANCELLABLE` で拒否してもよい(MAY)(例: セラーが契約上フライト途中のキャンセルを拒む場合) * バイヤーが既に `canceled` のメディアバイをキャンセルしようとした場合(`canceled` のバイに対する `canceled: true`)、セールスエージェントは `NOT_CANCELLABLE` で拒否しなければなりません(MUST) * 終端状態(`completed`、`rejected`、`canceled`)のメディアバイに対するその他すべての更新——`completed` または `rejected` のバイに対する `canceled: true` の試みを含む——は `INVALID_STATE` で拒否しなければなりません(MUST) * 拒否(`rejected` ステータス)は `pending_creatives` または `pending_start` からのみ有効です。セールスエージェントは、既に `active` へ遷移したメディアバイを拒否してはなりません(MUST NOT)。 * セラー起点のキャンセル通知は、`create_media_buy` または `update_media_buy` の際にオーケストレーターが提供した `push_notification_config` のウェブフックを使わなければなりません(MUST)。ウェブフックのペイロードには `media_buy_id`、`status: "canceled"`、および `canceled_at`、`canceled_by: "seller"`、`reason` を持つ `cancellation` オブジェクトを含めなければなりません(MUST)。 * update リクエストの `canceled` フィールドは `"const": true` を使います——`true` のみが有効です。`canceled: false` を送るとスキーマバリデーションに失敗します。キャンセルは取り消し不可であり、「キャンセルの取り消し」操作はありません。 * **クリエイティブの割り当てはバイの拒否またはキャンセルで解放されます。** メディアバイが `rejected` または `canceled` へ遷移すると、そのメディアバイ上のすべてのパッケージ・クリエイティブの割り当てが解放されます。`creative.has_creative_library: true` を表明しているセラーでは、[割り当ての状態とクリエイティブの状態](/docs/creative/creative-libraries#creative-state-and-assignment-state-are-separate)に従ってクリエイティブはクリエイティブライブラリに残り、後続の `create_media_buy` や `sync_creatives` の呼び出しで `creative_id` により参照してもかまいません(MAY)。クリエイティブライブラリなしで `inline_creative_management` を表明しているインライン専用のセラーは、送信されたクリエイティブをパッケージスコープに保ってもかまいません(MAY)。それらはバイをまたぐ再利用や `list_creatives` による読み戻しを表明しません。 * **クリエイティブのレビューはバイの結果から独立しています。** セールスエージェントは、クリエイティブを含むバイが拒否されたことを理由に、そのクリエイティブを暗黙的に拒否してはなりません(MUST NOT)。クリエイティブの拒否は、それ自身の `rejection_reason` を持つ意図的なレビューの判断でなければなりません(MUST)。クリエイティブがコンテンツポリシーに違反したためにバイが拒否された場合、セールスエージェントはそのクリエイティブを拒否してもかまいませんが(MAY)、それは通常のレビュー経路を通じて、それ自身の `rejection_reason` とともに行う場合に限ります。バイの `rejected` ステータスはそれ自体では十分ではありません。 * **解放された割り当ての可観測性。** メディアバイレベルの `canceled` または `rejected` の遷移(バイの `history` と上記で必須とされるウェブフック通知で公開されます)が、解放されたすべての割り当てに対する監査記録**そのもの**です。バイヤーは割り当てごとの差分の可観測性に依拠してはなりません(MUST NOT)。解放された割り当ては、そのバイの `get_media_buys` レスポンスにはもう現れません。ライブラリでの再利用可能性を確認するバイヤーは、クリエイティブがまだライブラリにあることを確認し現在のステータスを観測するために `list_creatives` を呼ぶべきです(SHOULD)。`get_media_buys` はパッケージレベルの承認状態を公開するのであってクリエイティブ本体の完全な取得ではないため、インライン専用のバイヤーは送信したクリエイティブ本体を保持しておくべきです。以前のパッケージ上のパッケージスコープの期限(`creative_deadline`)は、ライブラリのクリエイティブの新しいバイへの適格性には関係しません。 * **保持。** クリエイティブライブラリを持つセールスエージェントは、最後の割り当てが解放されてから少なくとも 90 日間、解放されたクリエイティブをライブラリに保持すべきです(SHOULD)。規範的な保持期間の下限は [#2260](https://github.com/adcontextprotocol/adcp/issues/2260) で追跡されているクリエイティブ保持の契約で規定されます。その契約が着地するまでの間、長期の再利用に依拠するバイヤーは、解放されたクリエイティブを新しいバイで参照する前に `list_creatives` で永続性を確認すべきです(SHOULD)。 #### リビジョンと確定のセマンティクス `revision` はメディアバイの楽観的並行性制御のトークンです。`update_media_buy` のワイヤーフィールド名は `revision` です。実装が内部的に `expected_revision` と呼んでもかまいませんが、セマンティクスは同じです: 呼び出し元は「現在保存されているメディアバイのリビジョンがまだこの値と等しい場合にのみ、この更新を適用せよ」と言っているのです。 セラーは、状態を変更するすべての更新について、永続化の境界でリビジョンのチェックをアトミックに強制しなければなりません(MUST)。アプリケーションのメモリ内での read-compare-write のシーケンスは、別のライターと競合して更新の喪失を許してしまう可能性があります。更新と比較は、一つのデータベーストランザクション、条件付き更新、または同等のアトミックなプリミティブの中に属します。不一致の場合は `CONFLICT` を返し、メディアバイは変更しないままにします。 状態を変更する更新は `revision` を増加させ、新しい値を返します。これには予算、フライト日、ターゲティング、ステータス、パッケージ、クリエイティブの割り当て、レポーティングのウェブフック、請求書送付先、コミット済みメトリクスの変更が含まれます。バリデーションのみのリクエスト、既に適用された操作の完全な冪等の再実行、`get_media_buys` による読み取りでは、リビジョンは増えません。 バイヤーは、状態変更を意図したすべての `update_media_buy` 呼び出しで、最後に観測した `revision` を渡すべきです(SHOULD)。リクエストフィールドは後方互換性のために任意のままです。`revision` が存在する場合、セラーは書き込みとアトミックにそれを比較し、古い値を `CONFLICT` で拒否しなければなりません(MUST)。`CONFLICT` の場合は、`get_media_buys` でメディアバイを再読み込みし、現在の状態と突き合わせてから、新しい `revision` と新鮮な `idempotency_key` でリトライします。 `confirmed_at` はセラーのコミットのタイムスタンプであり、配信ステータスのタイムスタンプではありません。一度値が入ると、その後の一時停止、再開、キャンセル、完了、レポーティングの遷移を通じて安定したままです。バイヤーに `media_buy_id` を返さない場合は `submitted` のレスポンス分岐を使います。セラーは代わりに、暫定的なバイに対して `media_buy_id`、`packages`、`confirmed_at: null` を伴う同期的な成功を返してもかまいません(MAY)。そのようなバイは `get_media_buys` で取得可能でなければならず(MUST)、コミット時に `confirmed_at` をちょうど一度設定することで遷移しなければなりません(MUST)。`confirmed_at: null` の暫定的なバイは `active` であってはならず(MUST NOT)、`packages[].committed_metrics` を含んでもなりません(MUST NOT)。 **パッケージレベルのライフサイクル:** パッケージはメディアバイと同じ一時停止/キャンセルのパターンに従います。加えて: * パッケージは `creative_deadline` を持ってもよい(MAY)——この期限の後、パッケージへのクリエイティブの変更は `CREATIVE_DEADLINE_EXCEEDED` で拒否されます。不在の場合、メディアバイの `creative_deadline` が適用されます。`CREATIVE_REJECTED` はコンテンツポリシー上の失敗のために予約されています。 * パッケージのキャンセル(パッケージ更新での `canceled: true`)は取り消し不可であり、メディアバイのステータスから独立しています——メディアバイが `active` のまま、単一のパッケージだけをキャンセルできます。キャンセルされたパッケージ上のクリエイティブの割り当ては、上記のメディアバイレベルのルールに従って解放されます。同じメディアバイ上の他のアクティブなパッケージに割り当てられたクリエイティブは影響を受けません。 * セールスエージェントはキャンセルされたパッケージの配信データを保持しなければなりません(MUST)。`include_snapshot` が true の場合、セールスエージェントはキャンセル時点の配信状態を反映した最終スナップショットを返すべきです(SHOULD)。 * メディアバイ内のすべてのパッケージがキャンセルされた場合、メディアバイ自体は現在のステータス(`active` または `paused`)のままです。フライト途中のパッケージ追加をサポートするセラーは `valid_actions` に `add_packages` を表明します——バイヤーは `update_media_buy` の `new_packages` を通じて新しいパッケージを追加できます。そうでない場合、バイヤーはメディアバイを明示的にキャンセルすべきです(SHOULD)。セールスエージェントは、最後のアクティブなパッケージがキャンセルされたときに(update レスポンスの `context.notes` を通じて)オーケストレーターに通知すべきです(SHOULD)。セールスエージェントは、新しい活動がなければ、セラーが定めた猶予期間の後にメディアバイを `canceled` へ自動遷移させてもかまいません(MAY)。 ### パッケージ上のクリエイティブ承認 **スキーマ**: [`enums/creative-approval-status.json`](https://adcontextprotocol.org/schemas/v3/enums/creative-approval-status.json) 各パッケージは、クリエイティブのライブラリレベルのステータスとは別に、クリエイティブごとの承認ステータスを追跡します。クリエイティブがライブラリでは `approved` でありながら、特定のパッケージでは `rejected` であることもあります(例: そのプレースメントに対してフォーマットが誤っている)。 | ステータス | 説明 | | ---------------- | ----------------------------------------------- | | `pending_review` | クリエイティブが送信され、プラットフォームのレビュー待ち | | `approved` | このパッケージでの配信についてクリエイティブが承認済み | | `rejected` | このパッケージについてクリエイティブが拒否された。`rejection_reason` を参照 | 拒否は終端ではありません——バイヤーはクリエイティブを修正し、セラーが表明しているクリエイティブの経路を通じて再送信します。これにより承認は `pending_review` にリセットされます。再送信の経路: 1. `get_media_buys` のレスポンスで `rejection_reason` を確認する 2. クリエイティブを修正する(アセットの更新、マニフェストの調整) 3. ライブラリを持つセラーには `sync_creatives` で、インライン専用のセラーには `update_media_buy` の `packages[].creatives` で再送信する 4. 承認が `pending_review` にリセットされる **`creative_deadline` との相互作用:** クリエイティブがパッケージの `creative_deadline` の後に拒否された場合、バイヤーはそれでも再送信してもかまいません(MAY)——バイヤーはセラーが特定した問題を修正しているのですから、セラーは期限を過ぎていても拒否されたクリエイティブの再送信を受け入れるべきです(SHOULD)。遅れた再送信を受け入れられないセラーは `CREATIVE_DEADLINE_EXCEEDED` を返さなければなりません(MUST)。 ### 検証タグの強制 確定したパッケージの `performance_standards` が `vendor` を持つエントリを含む場合、そのパッケージに割り当てられるクリエイティブは、指定された各ベンダーに対応する `url_type: "tracker_script"` または `url_type: "tracker_pixel"` の URL アセットを少なくとも一つ含まなければなりません(MUST)。セールスエージェントは、必要な検証タグを欠くクリエイティブの割り当てを、`CREATIVE_REJECTED` と、不足しているベンダータグを特定する `details` メッセージとともに拒否すべきです(SHOULD)。バイヤーエージェントは、クリエイティブを送信する前に、合意された `measurement_terms` に基づいてベンダータグを事前に含めておくべきです(SHOULD)。 ## タスク メディアバイプロトコルは以下のタスクを定義します。完全なリクエスト/レスポンススキーマと例については、タスクリファレンスページを参照すること。 ### get\_products **スキーマ**: [`media-buy/get-products-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-products-request.json) / [`media-buy/get-products-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-products-response.json) **リファレンス**: [`get_products` タスク](/docs/media-buy/task-reference/get_products) 自然言語ブリーフまたは明示的なホールセールインテントを使って広告インベントリを発見します。 **要件:** * オーケストレーターは `buying_mode` を `"brief"`、`"wholesale"`、または `"refine"` に設定しなければなりません * オーケストレーターは `buying_mode` が `"brief"` の場合に `brief` を含めなければなりません * オーケストレーターは `buying_mode` が `"wholesale"` または `"refine"` の場合に `brief` を含めてはなりません * オーケストレーターは `buying_mode` が `"refine"` の場合に `refine` を含めなければなりません * オーケストレーターは `buying_mode` が `"brief"` または `"wholesale"` の場合に `refine` を含めてはなりません * オーケストレーターは `refine` 内の各商品エントリに `scope` と `product_id` を、各プロポーザルエントリに `scope` と `proposal_id` を提供しなければなりません * オーケストレーターは商品エントリおよびプロポーザルエントリの `action` を省略してもよい(デフォルトは `"include"`) * オーケストレーターは単一の `refine` 配列に同一の商品IDまたはプロポーザルIDを持つ複数のエントリを含めてはなりません * セールスエージェントはブリーフが提供された場合、ブリーフ条件に一致する商品を返さなければなりません * セールスエージェントは各商品に `product_id` と `pricing_options` を含めなければなりません * セールスエージェントは複数の商品が一致する場合、関連性スコアを含めるべきです **リファインメント要件:** `buying_mode: "refine"` を持つ各 `get_products` リクエストは自己完結している — セールスエージェントはトランスポートレベルのセッション状態に依存してはなりません。各リクエストの `refine` 配列と `filters` がリファインメントの意図を完全に指定します。セラーは自身の商品およびプロポーザルレジストリを維持します。「ステートレス」とは、プロトコル交換がコール間で暗黙的な状態を持たないことを意味します。これによりステートレスな実装と安全なリトライが可能になります。 * セールスエージェントは、商品エントリおよびプロポーザルのリファインエントリで `action` が欠けている場合、`action: "include"` として扱わなければなりません * セールスエージェントは `action: "omit"` を持つ商品をレスポンスから除外しなければなりません * セールスエージェントは `action: "omit"` を持つプロポーザルをレスポンスから除外しなければなりません * セールスエージェントは `action: "include"` を持つ商品を更新された価格とともに返さなければなりません * セールスエージェントは `action: "include"` を持つ商品エントリの `ask` を満たすべきです * セールスエージェントは `action: "more_like_this"` を持つ商品に類似した追加商品を元の商品とともに返すべきです * セールスエージェントはレスポンスを構成する際にリクエストレベルの ask(`scope: "request"`)を考慮すべきだ — これにより明示的に参照された商品以外の追加商品が含まれる場合があります。商品ごとのアクションはリクエストレベルの指示より優先されます。 * セールスエージェントは `action: "include"` を持つプロポーザルエントリの `ask` を満たすべきです * セールスエージェントはレスポンスに `refinement_applied` を含めるべきで、位置でマッチした各変更リクエストに1エントリを持ちます * `refinement_applied` を返すセールスエージェントは、オーケストレーターが整合性を相互検証できるよう、各エントリで `scope` をエコーしなければならず、商品スコープとプロポーザルスコープについては `product_id` / `proposal_id` をエコーしなければなりません * セールスエージェントはオーケストレーターがプロポーザルエントリを含めていない場合でも、リファインモードで商品と並んでプロポーザルを返してもよい ### list\_creative\_formats **スキーマ**: [`media-buy/list-creative-formats-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/list-creative-formats-request.json) / [`media-buy/list-creative-formats-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/list-creative-formats-response.json) **リファレンス**: [`list_creative_formats` タスク](/docs/creative/task-reference/list_creative_formats) クリエイティブフォーマットの要件と仕様を発見します。 **要件:** * セールスエージェントはサポートするすべてのクリエイティブフォーマットを返さなければなりません * セールスエージェントは各フォーマットの技術仕様を含めなければなりません * セールスエージェントは該当する場合、クリエイティブプロトコルの標準フォーマットIDを参照すべきです ### create\_media\_buy **スキーマ**: [`media-buy/create-media-buy-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/create-media-buy-request.json) / [`media-buy/create-media-buy-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/create-media-buy-response.json) **リファレンス**: [`create_media_buy` タスク](/docs/media-buy/task-reference/create_media_buy) 選択したパッケージからメディアバイを作成するか、プロポーザルを実行します。 **要件:** * オーケストレーターは `packages` 配列または `proposal_id` のいずれかを含めなければなりません * オーケストレーターはキャンペーンの `start_time` と `end_time` を含めなければなりません * セールスエージェントは作成成功時に `media_buy_id` を返さなければなりません * セールスエージェントは作成成功時に `confirmed_at` を返さなければなりません。その値はセラーのコミットのタイムスタンプ、またはレスポンスがまだコミットされていない暫定的なバイを作成する場合は `null` です。 * セールスエージェントは作成成功時に `revision` を返さなければなりません * セールスエージェントは、クリエイティブをアップロードすべき期限を示す `creative_deadline` を返さなければなりません * セールスエージェントは価格オプションに対して予算を検証しなければなりません * 検証失敗時、セールスエージェントは `errors` 配列を返さなければなりません ### update\_media\_buy **スキーマ**: [`media-buy/update-media-buy-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/update-media-buy-request.json) / [`media-buy/update-media-buy-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/update-media-buy-response.json) **リファレンス**: [`update_media_buy` タスク](/docs/media-buy/task-reference/update_media_buy) 既存のメディアバイの予算、ターゲティング、または設定を変更します。 **パッケージの操作**は構造的に明示的です——操作の種類は、パッケージがリクエストのどこに現れるかで決まります: | 操作 | リクエスト上の位置 | 説明 | | --------- | --------------------------------- | ------------------------------------ | | **新規** | `new_packages[]` | メディアバイにパッケージを追加する | | **変更** | `packages[]` | 既存のパッケージを変更する(予算、ターゲティング、日付、クリエイティブ) | | **キャンセル** | `canceled: true` を伴う `packages[]` | 既存のパッケージをキャンセルする(取り消し不可) | **要件:** * オーケストレーターは `media_buy_id` を含めなければなりません * セールスエージェントは、認証済みアカウントについて `get_media_buys` が返した任意の `media_buy_id` を受け入れなければならず、バイが元々 AdCP の外で作成されたことを理由に更新を拒否してはなりません。特定の操作に対するビジネス上の制約は、それらを `valid_actions` から省略することで表現します。[アカウントの所有権と作成サーフェス](#アカウントの所有権と作成サーフェス)を参照。 * セールスエージェントは PATCH セマンティクスを適用しなければなりません: 指定されたフィールドのみ更新され、省略されたフィールドは変更されない * バイヤーが既に `canceled` のメディアバイをキャンセルしようとした場合(`canceled` のバイに対する `canceled: true`)、セールスエージェントは `NOT_CANCELLABLE` で拒否しなければなりません * 終端状態(`completed`、`rejected`、`canceled`)のメディアバイに対するその他すべての更新——`completed` または `rejected` のバイに対する `canceled: true` の試みを含む——は `INVALID_STATE` で拒否しなければなりません * オーケストレーターは `canceled: true` と任意の `cancellation_reason` を設定してメディアバイをキャンセルしてもよい * セールスエージェントはキャンセルを受理した際、メディアバイを `canceled` ステータスへ遷移させなければなりません * セールスエージェントは、終端状態でないメディアバイのキャンセルをエラーコード `NOT_CANCELLABLE` で拒否してもよい * オーケストレーターはパッケージ更新で `canceled: true` を設定して個別のパッケージをキャンセルしてもよい * セールスエージェントは、`creative_deadline` を過ぎたパッケージへのクリエイティブの変更をエラーコード `CREATIVE_DEADLINE_EXCEEDED` で拒否しなければなりません * オーケストレーターは `new_packages` を通じて既存のメディアバイに新しいパッケージを追加してもよい。これをサポートするセールスエージェントは `valid_actions` に `add_packages` を表明しなければなりません。パッケージの追加をサポートしないセールスエージェントは `UNSUPPORTED_FEATURE` で拒否しなければなりません。 * update リクエストで `canceled: true` が他のフィールドと同時に存在する場合、セールスエージェントはキャンセルを適用しなければならず、`cancellation_reason` を除く他のすべてのフィールドを無視しなければなりません。キャンセルは並行する変更よりも優先されます。セールスエージェントは、キャンセル以外のフィールドが存在し無視された場合、レスポンスの `context` に警告を含めるべきです。 * セールスエージェントは更新適用後(または承認保留中の場合は提案された状態)の直接変更された各パッケージの状態を含む `affected_packages` を返さなければなりません。キャンペーンレベルのフィールド(例: `paused`、`start_time`)のみが更新される場合は空の配列も有効です * 手動承認が必要な場合、セールスエージェントは保留中の更新リクエストを永続化しなければならず、`implementation_date: null` を返さなければならず、空の `affected_packages` を返してはなりません * セールスエージェントは更新されたメディアバイの状態を返すべきです ### sync\_catalogs **スキーマ**: [`media-buy/sync-catalogs-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/sync-catalogs-request.json) / [`media-buy/sync-catalogs-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/sync-catalogs-response.json) **リファレンス**: [`sync_catalogs` タスク](/docs/media-buy/task-reference/sync_catalogs) セラーアカウントのカタログ(商品、インベントリ、ストア、垂直フィード)を同期します。 **要件:** * オーケストレーターは `account_id` を含めなければなりません * `catalogs` が提供される場合、少なくとも1つのカタログを含めなければなりません * `catalogs` が省略された場合、そのコールは発見のみを目的とし、変更なしに既存のカタログを返す * セールスエージェントはカタログごとの結果を返さなければならず、取られたアクションとアイテムレベルの問題を含めます * セールスエージェントは変更を適用せずに検証するための `dry_run` をサポートすべきです ### list\_creatives `list_creatives` は [クリエイティブプロトコル](/docs/creative/specification#list_creatives) で定義されています。クリエイティブライブラリをホストするセールスエージェントはクリエイティブプロトコルの一部として `list_creatives` を実装してもよい。[`list_creatives` タスクリファレンス](/docs/creative/task-reference/list_creatives)を参照すること。 ### sync\_creatives `sync_creatives` は [クリエイティブプロトコル](/docs/creative/specification#sync_creatives) で定義されています。クリエイティブライブラリをホストするエージェントはクリエイティブプロトコルの一部として `sync_creatives` を実装します。[`sync_creatives` タスクリファレンス](/docs/creative/task-reference/sync_creatives)を参照すること。 ### get\_media\_buys **スキーマ**: [`media-buy/get-media-buys-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-media-buys-request.json) / [`media-buy/get-media-buys-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-media-buys-response.json) **リファレンス**: [`get_media_buys` タスク](/docs/media-buy/task-reference/get_media_buys) パッケージステータス、クリエイティブ承認、不足フォーマット、オプションの配信スナップショットを含む運用上のメディアバイ状態を取得します。 **要件:** * オーケストレーターは `account_id`、`media_buy_ids`、`status_filter` でフィルタリングしてもよい * オーケストレーターは広範なスコープのクエリに対してカーソルページネーション(`pagination.max_results` / `pagination.cursor`)を使用すべきです * セールスエージェントは、認証済みアカウントが所有し宣言されたフィルターの集合に一致するすべてのメディアバイを、そのバイがどのように作成されたか(AdCP、セラーのネイティブ API/UI、手動トラフィッキング、レガシーシステム)に関わらず返さなければなりません。[アカウントの所有権と作成サーフェス](#アカウントの所有権と作成サーフェス)を参照。 * セールスエージェントは一致した各メディアバイの現在のメディアバイステータスとパッケージレベルの運用状態を返さなければなりません * セールスエージェントは、返されるメディアバイレベルの `status` がキャッシュから提供される場合、または上流のレグからロールアップされたステータスとして計算される場合、`status_as_of` を含めるべきです。ロールアップされたステータスの場合、`status_as_of` は、返されるロールアップに影響しうる上流ステータス観測のうち最も古いものより後であってはなりません。そうすることで鮮度を過大に主張することがなくなります。鮮度について何も主張しない場合は省略するか `null` を返します。バイヤーは、省略または null の値がステータスがライブであることを意味すると推論してはなりません。バイヤーは一覧のステータスの鮮度を解釈するために `status_as_of` を使います。`updated_at` は引き続きメディアバイの最終更新時刻です。 * セールスエージェントは各メディアバイについて、現在の状態でバイヤーが実行できるアクションを列挙した `valid_actions` を含めるべきです。これにより、エージェントが状態機械を内部に取り込む必要がなくなります。期待されるマッピング: | ステータス | 期待される `valid_actions` | | ------------------------------------- | -------------------------------------------------------------------------------------------------- | | `pending_creatives` | `pause`、`cancel`、`sync_creatives` | | `pending_start` | `pause`、`cancel`、`sync_creatives` | | `active` | `pause`、`cancel`、`update_budget`、`update_dates`、`update_packages`、`add_packages`、`sync_creatives` | | `paused` | `resume`、`cancel`、`update_budget`、`update_dates`、`update_packages`、`add_packages`、`sync_creatives` | | `completed` / `rejected` / `canceled` | *(空配列)* | セラーはビジネスルールに基づいてアクションを省略してもかまいません(例: 契約上の義務がキャンセルを妨げる場合に `cancel` を省略する、プラットフォームがフライト途中の追加をサポートしない場合に `add_packages` を省略する)。 `pending_creatives` または `pending_start` が `paused: true` による作成時の保留を覆い隠している場合、セラーは、セットアップのブロッカーが解消する前にバイヤーが保留を解除できるよう、`resume` も含めるべきです。 `valid_actions` には変更操作のみが含まれます——読み取り専用の操作(`get_media_buys`、`get_media_buy_delivery`)は状態に関わらず常に許可されます。エージェントは `valid_actions` を最適化のヒントとして使うべきですが、`INVALID_STATE` エラーを適切に処理しなければなりません。`valid_actions` は並行操作によるリアルタイムの状態変化を反映していない可能性があるからです。アクションの不在は「このセラーが宣言していない」ことを意味するのであって、必ずしも「禁止されている」ことを意味しません。 クリエイティブの変更について、`valid_actions` にある `sync_creatives` はレガシーなクリエイティブ変更のアクションラベルであり、`sync_creatives` タスクが存在する証明ではありません。バイヤーはセラーが表明しているクリエイティブの経路を使います: `creative.has_creative_library: true` のセラーでは `sync_creatives` と `creative_assignments`、`media_buy.features.inline_creative_management: true` を表明しているインライン専用のセラーでは `update_media_buy` の `packages[].creatives` です。 * オーケストレーターは、`include_history` に希望する直近のエントリ数を設定してリビジョン履歴をリクエストしてもよい。セールスエージェントは、`include_history > 0` のとき、リビジョン番号、タイムスタンプ、サーバーが導出したアクターの識別情報、アクションの種類、任意のサマリーを含む `history` 配列をメディアバイごとに返すべきです。履歴エントリは新しい順に並べなければなりません。 * セールスエージェントはメディアバイ通貨を含めなければならず、通貨フィールドを一貫して表示しなければなりません(`snapshot.currency` -> `package.currency` -> `media_buy.currency`) * セールスエージェントは利用可能な場合、クリエイティブ承認結果と保留中のフォーマット要件を含めるべきです * `include_snapshot` が true でパッケージのスナップショットデータが省略される場合、セールスエージェントは `snapshot_unavailable_reason` を返さなければなりません * `include_snapshot` が true でスナップショットが返される場合、各スナップショットは `as_of` と `staleness_seconds` を含めなければなりません * デフォルトの `status_filter: ["active"]` は `media_buy_ids` が省略された場合のみ適用されます ### get\_media\_buy\_delivery **スキーマ**: [`media-buy/get-media-buy-delivery-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-media-buy-delivery-request.json) / [`media-buy/get-media-buy-delivery-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-media-buy-delivery-response.json) **リファレンス**: [`get_media_buy_delivery` タスク](/docs/media-buy/task-reference/get_media_buy_delivery) パフォーマンス指標とキャンペーン配信をトラッキングします。 **要件:** * オーケストレーターは `media_buy_id` を含めなければなりません * セールスエージェントは、認証済みアカウントについて `get_media_buys` が返した任意の `media_buy_id` を、その作成サーフェスに関わらず受け入れなければなりません。[アカウントの所有権と作成サーフェス](#アカウントの所有権と作成サーフェス)を参照。 * セールスエージェントはパッケージレベルで配信指標を返さなければなりません * セールスエージェントはリクエストされた場合、次元ごとの内訳を含めるべきです * セールスエージェントはデータの鮮度を示す `as_of` タイムスタンプを含めなければなりません ### provide\_performance\_feedback **スキーマ**: [`media-buy/provide-performance-feedback-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/provide-performance-feedback-request.json) / [`media-buy/provide-performance-feedback-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/provide-performance-feedback-response.json) **リファレンス**: [`provide_performance_feedback` タスク](/docs/media-buy/task-reference/provide_performance_feedback) パブリッシャーの最適化を可能にするためのパフォーマンスシグナルを送信します。 **要件:** * オーケストレーターは `media_buy_id` とパフォーマンス指標を含めなければなりません * セールスエージェントはフィードバックの受信を確認しなければなりません * セールスエージェントはキャンペーン制約内での配信最適化にフィードバックを使用すべきです ### sync\_event\_sources **スキーマ**: [`media-buy/sync-event-sources-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/sync-event-sources-request.json) / [`media-buy/sync-event-sources-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/sync-event-sources-response.json) **リファレンス**: [`sync_event_sources` タスク](/docs/media-buy/task-reference/sync_event_sources) アップサートセマンティクスでコンバージョントラッキング用のイベントソースをセラーアカウントに設定します。 **要件:** * オーケストレーターは `account_id` を含めなければなりません * `event_sources` が提供される場合、少なくとも1つのイベントソースを含めなければなりません * `event_sources` が省略された場合、そのコールは発見のみを目的とし、変更なしにアカウントのすべてのイベントソースを返す * セールスエージェントは何が起きたかを示す `action` を含むソースごとの結果を返さなければなりません * セールスエージェントは存在する場合、セラー管理のイベントソースをレスポンスに含めなければなりません * セールスエージェントは新しく作成されたイベントソースの `setup` 手順を返すべきです * セールスエージェントはセラーのプラットフォームでのクロスリファレンス用に `seller_id` を含めてもよい ### log\_event **スキーマ**: [`media-buy/log-event-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/log-event-request.json) / [`media-buy/log-event-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/log-event-response.json) **リファレンス**: [`log_event` タスク](/docs/media-buy/task-reference/log_event) アトリビューションと最適化のためのコンバージョンまたはマーケティングイベントを送信します。 **要件:** * オーケストレーターは設定済みのイベントソースを参照する `event_source_id` を含めなければなりません * オーケストレーターは `event_id`、`event_type`、`event_time` を持つ少なくとも1つのイベントを含めなければなりません * セールスエージェントは `events_received` と `events_processed` のカウントを返さなければなりません * セールスエージェントは `event_id` + `event_type` + `event_source_id` でイベントを重複排除しなければなりません * セールスエージェントは個別に失敗したイベントの `partial_failures` を報告すべきです * セールスエージェントはユーザーマッチングが試みられた場合、`match_quality` スコアを返すべきです ### sync\_audiences **スキーマ**: [`media-buy/sync-audiences-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/sync-audiences-request.json) / [`media-buy/sync-audiences-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/sync-audiences-response.json) **リファレンス**: [`sync_audiences` タスク](/docs/media-buy/task-reference/sync_audiences) セラーアカウントでファーストパーティ CRM オーディエンスを管理します。ハッシュ化された顧客リストをアップロードし、マッチングステータスを確認し、ターゲティングオーバーレイで結果のオーディエンスを参照します。 **要件:** * オーケストレーターは `account_id` を含めなければなりません * オーケストレーターはハッシュ化されたメンバーデータを持つ少なくとも1つのオーディエンスを含めなければなりません * セールスエージェントはマッチングステータスを含むオーディエンスごとの結果を返さなければなりません * セールスエージェントは非同期マッチング完了のための `push_notification_config` をサポートすべきです * セールスエージェントは SHA-256 ハッシュ化された識別子を受け入れなければならず、`hashed_email`/`hashed_phone` フィールドの平文のメールアドレス/電話番号を拒否しなければなりません(トランスポート境界での PII の最小化。ハッシュ化では満たされない保持と同意の義務については[プライバシーに関する考慮事項](/docs/reference/privacy-considerations#unsalted-hashed-identifiers-are-pseudonymous-not-anonymous)を参照) ## エラーハンドリング セールスエージェントは [AdCP 標準エラースキーマ](/docs/building/by-layer/L3/error-handling) を使用してエラーを返さなければなりません。 一般的なエラーコード: * `MEDIA_BUY_NOT_FOUND`: 参照されたメディアバイが存在しません * `PACKAGE_NOT_FOUND`: 参照されたパッケージが存在しません * `PRODUCT_NOT_FOUND`: 参照された商品が存在しません * `BUDGET_EXCEEDED`: 操作が割り当て予算を超過します * `CREATIVE_REJECTED`: クリエイティブがコンテンツポリシーのレビューに失敗しました * `CREATIVE_DEADLINE_EXCEEDED`: パッケージの `creative_deadline` の後にクリエイティブの変更が送信されました * `INVALID_STATE`: リソースの現在のステータスではその操作が許可されていない(例: 完了またはキャンセルされたメディアバイの更新) * `NOT_CANCELLABLE`: メディアバイまたはパッケージを現在の状態でキャンセルできません * `GOVERNANCE_DENIED`: 登録されたガバナンスエージェントがトランザクションを拒否しました。バイヤーはバイを組み直すか、人の支出権限にエスカレーションするか、ガバナンスエージェントに連絡できます。 * `TERMS_REJECTED`: バイヤーが提案した `measurement_terms` がセラーに拒否されました。エラーの詳細は、どの条件が失敗したか、およびセラーの許容範囲またはサポートするベンダーを特定すべきです。復旧方法: 提案した条件を調整してリトライするか、`measurement_terms` を省略してプロダクトのデフォルトを受け入れます。 * `REQUOTE_REQUIRED`: `update_media_buy` リクエストが、元の見積もりが価格付けの前提としたパラメータのエンベロープ(予算、フライト日、ボリューム、ターゲティング)を変更しています。`pricing_option` はロックされたままです。セラーはその価格でリクエストされた形を断っています。`TERMS_REJECTED`(計測)や `POLICY_VIOLATION`(コンテンツ)とは異なります。3.1 での復旧方法は、現在の見積もりに収まるよう更新を調整する、プロダクト/条件を再発見する、`add_packages` が利用可能なときにパッケージを追加する、または別のメディアバイを作成することです。AdCP 3.1 は `update_media_buy` に添付できる修正見積もりのアーティファクトを定義していません。セラーは、バイヤーのエージェントが自律的に再発見できるよう、エンベロープに違反したフィールドのパス(例: `packages[0].budget`、`end_time`)を `error.details.envelope_field` に設定すべきです。 * `VALIDATION_ERROR`: リクエストフォーマットまたはパラメータのエラー * `AUTH_MISSING`: 認証情報が提示されませんでした。復旧: 訂正可能。 * `AUTH_INVALID`: 認証情報は提示されたが拒否されました(期限切れ/失効/不正な形式)。復旧: 終端。 ## セキュリティの考慮事項 ### トランスポートセキュリティ すべてのメディアバイプロトコル通信は TLS 1.2 以上の HTTPS を使用しなければなりません。 ### 認証 * オーケストレーターは有効な認証情報を使用してセールスエージェントに対して認証しなければなりません * セールスエージェントはリクエストを処理する前に認証情報を検証しなければなりません * セールスエージェントはインベントリアクセスを決定するためにアカウントコンテキストを使用しなければなりません ### 予算認可 * セールスエージェントはアカウントが要求された予算レベルに対して認可されているかを検証しなければなりません * セールスエージェントは明示的な承認なしに認可された予算上限を超えてはなりません ### クリエイティブセキュリティ * セールスエージェントはポリシー準拠のためにクリエイティブコンテンツを検証しなければなりません * セールスエージェントはクリエイティブのマルウェアおよび悪意のあるコンテンツをスキャンすべきです * セールスエージェントはセキュリティ検証に失敗したクリエイティブを配信してはなりません ## 適合性 ### セールスエージェントの適合性 適合するメディアバイプロトコルのセールスエージェントは以下を行わなければなりません: 1. 指定されたトランスポート(MCP または A2A)のうち少なくとも1つをサポートします 2. スキーマに従ってすべてのタスクを実装します 3. レスポンススキーマで定義された必須フィールドを返す 4. 指定されたエラーコードを使用します 5. 非同期オペレーションを適切に処理します 6. 認証と認可を強制します すべての AdCP プロトコルにまたがる必須タスクと任意タスクの統合ビューについては、[プロトコル別の必須タスク](/docs/protocol/required-tasks)を参照してください。 ### オーケストレーターの適合性 適合するメディアバイプロトコルのオーケストレーターは以下を行わなければなりません: 1. セールスエージェントに対して認証します 2. リクエストスキーマで定義された必須フィールドを含めます 3. 完了アーティファクトのウェブフック配信を含め、タスクレベルの非同期レスポンス(`submitted`、`working`、`input-required`)を処理します 4. 後続の操作でメディアバイを参照するために `media_buy_id` を使用します 5. クリエイティブアップロードの `creative_deadline` を遵守します ## 実装ノート ### レスポンスタイムの目安 セールスエージェントは以下のレスポンスタイムを目標とすべきだ: | オペレーション種別 | 目標レイテンシ | | ---------------------------------- | ------- | | 単純な参照(list\_creative\_formats) | 1秒未満 | | AI/LLM を使った発見(get\_products) | 60秒未満 | | レポートクエリ(get\_media\_buy\_delivery) | 60秒未満 | | キャンペーン操作(create、update、sync) | 非同期でも可 | ### 冪等性 セールスエージェントは `idempotency_key` を使った冪等なオペレーションをサポートすべきだ: * 同じアカウントに対して `idempotency_key` が以前に見られた場合、セールスエージェントは既存のリソースを返すべきです * これにより重複作成なしに安全なリトライが可能になります ミューテーションタスク(`update_media_buy`、`sync_creatives`)では、オーケストレーターは安全なリトライのために `idempotency_key`(16〜255文字)を含めてもよい。レスポンスなしでリクエストが失敗した場合、同じ `idempotency_key` で再送することで最大1回の実行が保証されます。 ### 人間参加型 セールスエージェントはどの操作に対しても人間の承認を要求してもよい。承認は**タスクレイヤー**でモデル化されます: * セラーが**内部の**人(例: IO への署名、トラフィックマネージャーのレビュー)を待っている場合、セールスエージェントはレビュアーが動くまでタスクを `status: "submitted"` に保たなければなりません。完了すると、タスクは `completed` へ遷移し、最終アーティファクトが `media_buy_id` と完全な成功ペイロードを運びます。 * セラーが**バイヤー**の応答を必要とする場合(例: 事前承認された上限を超える予算の確認)、セールスエージェントは何が必要かを説明するメッセージとともに `status: "input-required"` を返さなければなりません。バイヤーは同じ A2A コンテキスト内で応答します。 * セールスエージェントはタスクのメッセージで推定承認タイムラインを提供すべきです。 * オーケストレーターはポーリングではなく、完了通知のためのウェブフックハンドラー(`push_notification_config` 経由)を実装すべきです。 `pending_approval` はメディアバイやタスクの有効なステータスではありません——その値は(アカウントのオンボーディングレビュー向けに)`Account.status` にのみ存在します。メディアバイやタスクレベルの承認のために転用しないでください。 ## スキーマリファレンス | スキーマ | 説明 | | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | | [`media-buy/get-products-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-products-request.json) | get\_products リクエスト | | [`media-buy/get-products-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-products-response.json) | get\_products レスポンス | | [`media-buy/create-media-buy-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/create-media-buy-request.json) | create\_media\_buy リクエスト | | [`media-buy/create-media-buy-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/create-media-buy-response.json) | create\_media\_buy レスポンス | | [`media-buy/update-media-buy-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/update-media-buy-request.json) | update\_media\_buy リクエスト | # create_media_buy Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/task-reference/create_media_buy create_media_buy タスク — AdCP で発見したプロダクトから広告キャンペーンを作成します。パッケージ、予算、フライト期間、ガバナンスルール、承認ワークフローを処理。 選択したパッケージからメディアバイを作成するか、プロポーザルを実行します。必要に応じたバリデーションや承認、キャンペーン作成を処理します。 2 つのモードをサポート: * **Manual Mode**: `packages` 配列で明示的にラインアイテムを指定 * **Proposal Mode**: `proposal_id` と `total_budget` を指定し、`get_products` のプロポーザルを実行 **Response Time**: 即時〜日単位(`completed`、120 秒未満の `working`、数時間〜数日の `submitted`) **Request Schema**: [`/schemas/v3/media-buy/create-media-buy-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/create-media-buy-request.json) **Response Schema**: [`/schemas/v3/media-buy/create-media-buy-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/create-media-buy-response.json) ## クイックスタート 2 つのパッケージでシンプルなメディアバイを作成: ```javascript JavaScript test=false theme={null} import { testAgent } from '@adcp/sdk/testing'; import { CreateMediaBuyResponseSchema } from '@adcp/sdk'; // Calculate dates dynamically - start tomorrow, end in 90 days const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setHours(0, 0, 0, 0); const endDate = new Date(tomorrow); endDate.setDate(endDate.getDate() + 90); const result = await testAgent.createMediaBuy({ brand: { domain: 'acmecorp.com' }, packages: [ { product_id: 'prod_d979b543', pricing_option_id: 'cpm_usd_auction', format_ids: [ { agent_url: 'https://creative.adcontextprotocol.org', id: 'display_300x250_image' } ], budget: 2500, bid_price: 5.00 }, { product_id: 'prod_e8fd6012', pricing_option_id: 'cpm_usd_auction', format_ids: [ { agent_url: 'https://creative.adcontextprotocol.org', id: 'display_300x250_html' } ], budget: 2500, bid_price: 4.50 } ], start_time: tomorrow.toISOString(), end_time: endDate.toISOString() }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } // Validate response against schema const validated = CreateMediaBuyResponseSchema.parse(result.data); // Check for errors (discriminated union response) if ('errors' in validated && validated.errors) { throw new Error(`Failed to create media buy: ${JSON.stringify(validated.errors)}`); } if ('media_buy_id' in validated) { console.log(`Created media buy ${validated.media_buy_id}`); console.log(`Upload creatives by: ${validated.creative_deadline}`); console.log(`Packages created: ${validated.packages.length}`); } ``` ```python Python test=false theme={null} import asyncio import time from datetime import datetime, timedelta, timezone from adcp.testing import test_agent async def create_campaign(): # Calculate dates dynamically - start tomorrow, end in 90 days tomorrow = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1) end_date = tomorrow + timedelta(days=90) result = await test_agent.simple.create_media_buy( brand={ 'domain': 'acmecorp.com' }, packages=[ { 'product_id': 'prod_d979b543', 'pricing_option_id': 'cpm_usd_auction', 'format_ids': [ { 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'display_300x250_image' } ], 'budget': 2500, 'bid_price': 5.00 }, { 'product_id': 'prod_e8fd6012', 'pricing_option_id': 'cpm_usd_auction', 'format_ids': [ { 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'display_300x250_html' } ], 'budget': 2500, 'bid_price': 4.50 } ], start_time=tomorrow.isoformat().replace('+00:00', 'Z'), end_time=end_date.isoformat().replace('+00:00', 'Z') ) # Check for errors (discriminated union response) if hasattr(result, 'errors') and result.errors: raise Exception(f"Failed to create media buy: {result.errors}") print(f"Created media buy {result.media_buy_id}") print(f"Upload creatives by: {result.creative_deadline}") print(f"Packages created: {len(result.packages)}") asyncio.run(create_campaign()) ``` ```bash CLI test=false theme={null} npx @adcp/sdk@latest \ https://test-agent.adcontextprotocol.org/sales/mcp \ create_media_buy \ '{"brand":{"domain":"acmecorp.com"},"packages":[{"product_id":"prod_d979b543","pricing_option_id":"cpm_usd_auction","format_ids":[{"agent_url":"https://creative.adcontextprotocol.org","id":"display_300x250_image"}],"budget":30000,"bid_price":5.00},{"product_id":"prod_e8fd6012","pricing_option_id":"cpm_usd_auction","format_ids":[{"agent_url":"https://creative.adcontextprotocol.org","id":"display_300x250_html"}],"budget":20000,"bid_price":4.50}],"start_time":"2025-06-01T00:00:00Z","end_time":"2025-08-31T23:59:59Z"}' \ --auth $ADCP_AUTH_TOKEN ``` ## リクエストパラメーター | Parameter | Type | Required | Description | | ------------------- | ----------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `account` | [account-ref](/docs/building/by-layer/L2/accounts-and-agents#account-references) | Yes | アカウント参照。`{ "account_id": "..." }` または、セラーが暗黙的な解決をサポートする場合は `{ "brand": {...}, "operator": "..." }` を渡します。請求とポリシー評価に必要。 | | `proposal_id` | string | No\* | 実行する `get_products` のコミット済みプロポーザル ID。packages の代替。ドラフトのプロポーザルはまず finalize する必要があり、セラーはドラフトプロポーザルの実行を `PROPOSAL_NOT_COMMITTED` で拒否します。 | | `total_budget` | TotalBudget | No\* | プロポーザル実行時の総予算。配分割合はパブリッシャーが適用 | | `packages` | Package\[] | No\* | パッケージ構成の配列(下記)。proposal\_id を使わない場合は必須 | | `brand` | BrandRef | Yes | ブランド参照 — 実行時に完全なアイデンティティに解決されます。[brand.json](/docs/brand-protocol/brand-json) 参照 | | `start_time` | string | Yes | `"asap"` または ISO 8601 日時。新規メディアバイでは、具体的な日時は過去であってはなりません。 | | `end_time` | string | Yes | ISO 8601 日時(指定がなければ UTC) | | `paused` | boolean | No | 配信を保留した状態でメディアバイを作成します。true で、かつメディアバイが本来アクティブになる場合、`media_buy_status` は `paused` になります。セットアップのブロッカーが依然として優先されます: クリエイティブ欠如は `pending_creatives`、将来のフライトは `pending_start` を返し、それらのブロッカーが解消された後に保留が `paused` として可視化されます。 | | `invoice_recipient` | [BusinessEntity](/docs/building/by-layer/L2/accounts-and-agents#billing-entity-and-invoice-recipient) | No | この購入についてアカウントのデフォルト請求エンティティを上書きします。セラーは受取人が認可されていることを検証しなければならず(MUST)、ガバナンスエージェントが設定されている場合は `check_governance` に含めなければなりません。 | | `po_number` | string | No | 発注番号 | | `idempotency_key` | string | No | 安全なリトライのための一意キー。同じキーとアカウントのリクエストがすでに処理済みの場合、セラーは既存のメディアバイを返します。(セラー、リクエスト)ペアごとに一意でなければなりません。最低 16 文字。 | | `context` | object | No | レスポンスにそのまま返される不透明な相関データ。内部トラッキング、トレース ID、その他の呼び出し元固有の識別子に使用。 | | `reporting_webhook` | ReportingWebhook | No | レポーティングの自動配信設定 | \* Either `packages` OR (`proposal_id` + `total_budget`) must be provided. プロポーザルを実行する場合、返されたプロポーザルの `proposal_status` によって `create_media_buy` が有効かどうかが決まります。`committed` のプロポーザルは `expires_at` 前に実行でき、`draft` のプロポーザルは事前に `action: "finalize"` を指定した `get_products` の refine 呼び出しが必要です。Finalize は確定条件へのセラーのコミットであり、バイヤーの受諾ではありません。この `create_media_buy` 呼び出しが受諾/実行のステップです。 ### TotalBudget オブジェクト | Parameter | Type | Required | Description | | ---------- | ------ | -------- | -------------- | | `amount` | number | Yes | 総予算額 | | `currency` | string | Yes | ISO 4217 通貨コード | ### Package オブジェクト | Parameter | Type | Required | Description | | ----------------------- | --------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `product_id` | string | Yes | `get_products` で取得した product\_id。セラーは、この要求パッケージを表すすべてのレスポンスパッケージオブジェクトでこの値をエコーしなければなりません(MUST)。 | | `pricing_option_id` | string | Yes | プロダクトの `pricing_options` 配列にある価格オプション ID | | `format_ids` | FormatID\[] | No | レガシーの名前付きフォーマットセレクター。使用するフォーマット ID——プロダクトでサポートされている必要あり。省略された場合(かつ 3.1 以降のフォーマットオプションセレクターや直接の正準セレクターも存在しない場合)、プロダクトがサポートするすべてのフォーマットがデフォルトになります。異種のセラー集団を対象とするフォーマットオプション対応のバイヤー SDK は、レガシーフォーマットのみのセラーが all-formats にフォールバックせず明示的なフォーマットセットを受け取れるよう、`format_option_refs` と並べてこれを二重出力すべきです(SHOULD)。 | | `format_option_refs` | FormatOptionRef\[] | No | 3.1 以降のフォーマットオプションセレクター。パッケージの対象プロダクトの `format_options[]` 内エントリへの構造化参照: パブリッシャーカタログ由来のオプションには `{scope: "publisher", publisher_domain, format_option_id}`、プロダクトローカルのオプションには `{scope: "product", format_option_id}`([canonical formats](/docs/creative/canonical-formats) 参照)。存在する場合、`format_option_refs` は `format_ids` と直接の `format_kind`/`params` の両方に優先します。エントリが `format_options[]` のエントリと一致しない、プロダクトがレガシーフォーマットのみ、またはプロダクトの `format_options[]` エントリが選択可能な `format_option_id` 値を公開しない場合、セラーは `UNSUPPORTED_FEATURE`(フィールドパス `packages[i].format_option_refs[j]`)で拒否しなければなりません(MUST)。 | | `format_kind` | CanonicalFormatKind | No | 3.1 以降の直接正準セレクター。バイヤーが `format_option_refs[]` や `format_ids[]` ではなく正準種別で作成する場合に、このパッケージが対象とする正準的なフォーマット形状を指定します。プロダクト宣言が寸法、尺、サイズ、コーデック、その他の正準パラメータを要求する場合は `params` と組み合わせます。`format_ids[]` も存在し `format_option_refs[]` が無い場合、セラーは `format_ids[]` を検証します。`format_kind` は同じ正規化された形状の情報提供のエコーにすぎません。 | | `params` | object | No | `format_kind` の直接正準セレクターのためのパラメータ。選択した正準のパラメータ語彙に従います。`format_kind` が必要で、`params` 単独はスキーマ不正です。`{format_kind: "image"}` のような広いセレクターは、`format_options[]` が `params.width` と `params.height` を固定するプロダクトを満たしません。セラーは仕様不足の直接セレクターを `UNSUPPORTED_FEATURE` または同等のフォーマットセレクターエラーで拒否します。 | | `budget` | number | Yes | 価格オプションの通貨での予算 | | `impressions` | number | No | このパッケージのインプレッション目標 | | `paused` | boolean | No | 一時停止状態で作成する場合(デフォルト: `false`) | | `pacing` | string | No | `"even"`(デフォルト)、`"asap"`、`"front_loaded"` | | `bid_price` | number | No | オークション価格の場合の入札額。選択した価格オプションに `max_bid: true` がない限り、指定した金額がそのまま入札/価格として扱われる。`max_bid: true` の場合はバイヤーの最大支払い意思額(上限)として扱われる。 | | `optimization_goals` | [OptimizationGoal\[\]](/docs/media-buy/conversion-tracking/#optimization-goals) | No | このパッケージの最適化ターゲット。各ゴールは `kind: "event"`(`event_sources` 配列を持つコンバージョンイベント。オプションで `cost_per`、`per_ad_spend`、`maximize_value` ターゲットを指定可)または `kind: "metric"`(オプションで `cost_per` または `threshold_rate` ターゲットを持つセラー独自のメトリクス)。イベントゴールにはプロダクトの `conversion_tracking.supported_targets` が必要。メトリクスゴールには `metric_optimization.supported_metrics` が必要。 | | `targeting_overlay` | TargetingOverlay | No | 追加ターゲティング条件([Targeting](/docs/media-buy/advanced-topics/targeting) 参照) | | `start_time` | string | No | このパッケージのフライト開始日時(ISO 8601)。省略するとメディアバイの `start_time` を継承。メディアバイの日付範囲内である必要があります。`"asap"` はサポートしません。 | | `end_time` | string | No | このパッケージのフライト終了日時(ISO 8601)。省略するとメディアバイの `end_time` を継承。メディアバイの日付範囲内である必要があります。 | | `creative_assignments` | CreativeAssignment\[] | No | 既存ライブラリのクリエイティブを重み/プレースメント指定付きで割り当て | | `creatives` | CreativeAsset\[] | No | 新規クリエイティブアセットをインラインでアップロードして割り当て。`media_buy.features.inline_creative_management: true` が必要。セラーが `creative.has_creative_library: true` も宣言する場合、`creative_id` はライブラリに既存であってはなりません。 | | `context` | object | No | パッケージレスポンス、Webhook、読み取り面にそのまま返される不透明な相関データ。セラーが割り当てた `package_id` を内部ラインアイテム、キャンペーン構造、トラッキング状態にマッピングするために使用。混在したセラー集団を対象とするバイヤーは、`product_id` をエコーしないレガシーセラーのために、ここにパッケージごとの相関値(一般的には `context.buyer_ref`)を含めるべきです(SHOULD)。 | | `measurement_terms` | [MeasurementTerms](/docs/media-buy/advanced-topics/pricing-models#measurement-terms-and-performance-standards) | No | バイヤーが提案する課金計測とメイクグッド条件。プロダクトのデフォルトを上書きします。セラーは受諾(確定パッケージでエコー)、`TERMS_REJECTED` で拒否、または調整します。省略時はプロダクトの `measurement_terms` が適用されます。 | | `performance_standards` | [PerformanceStandard\[\]](/docs/media-buy/advanced-topics/pricing-models#measurement-terms-and-performance-standards) | No | バイヤーが提案するパフォーマンス基準(ビューアビリティ、IVT、完了率、ブランドセーフティ、アテンションスコア)。プロダクトのデフォルトを上書きします。セラーは受諾、`TERMS_REJECTED` で拒否、または調整します。省略時はプロダクトの `performance_standards` が適用されます。 | | `committed_metrics` | object\[] | No | バイヤーが提案するレポート契約——バイヤーがセラーに配信レポートで埋めることをコミットしてほしいメトリクス。`measurement_terms`/`performance_standards` と同じ交渉パターン: 各エントリは `scope: "standard"`(閉じた列挙の `metric_id` を伴う)または `scope: "vendor"`(`vendor` BrandRef +ベンダーの `metric_id` を伴う)をタグ付けします。リクエスト側のエントリは `committed_at` を持ちません——そのタイムスタンプは受諾時にセラーが刻印します。セラーは受諾(`committed_at` 付きでレスポンスにエコー)、`TERMS_REJECTED` で拒否、または正規化(異なるが互換なリストをエコー)します。省略時、セラーはプロダクトの `available_metrics` と、バイヤーがディスカバリー時に渡した `required_metrics` フィルターに基づいて、何をコミットするかを決めます。 | ## レスポンス ### 成功レスポンス | Field | Description | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `media_buy_id` | セラーの一意 ID | | `account` | このメディアバイに請求される解決済みアカウント。完全な [Account](/docs/building/by-layer/L2/accounts-and-agents#account-references) オブジェクトとしてエコーされます(`account_id`、`name`、`status`、および解決済みの `brand`/`operator` を含む)。リクエストが暗黙的解決(`brand` + `operator`)を使った場合、これはセラーが解決したアカウントを確認します。任意。 | | `confirmed_at` | セラーがメディアバイにコミットした ISO 8601 タイムスタンプ。設定後は安定します。遅延/手動承認フローでは、セラーのコミットが発生するまで `null` になりうる。 | | `creative_deadline` | クリエイティブ提出期限 (ISO 8601) | | `revision` | 初期のメディアバイリビジョン。状態変更を意図する次の `update_media_buy` 呼び出しで、この値を `revision` トークンとして使います。 | | `packages` | 作成されたパッケージの配列(完全な状態付き)。パッケージは、メディアバイの期限と異なる場合にパッケージごとの `creative_deadline` を含みうる。また、一つのセレクターが優先される場合でも読み取り面がロスレスになるよう、作成時に供給されたすべてのフォーマットセレクターフィールド(`format_option_refs`、`format_ids`、および/または `format_kind`/`params`)をエコーすべきです(SHOULD)。 | `confirmed_at` はセラーのコミット時刻であり、配信ステータスのタイムスタンプではありません。購入が後で一時停止、再開、配信開始、完了、またはパフォーマンス報告しても更新しないでください。コミット済みの同期作成は即座にこれを刻印します。バイヤーに `media_buy_id` を返さない場合は `submitted` レスポンス分岐を使います。セラーは代わりに、暫定購入について `media_buy_id`、`packages`、`confirmed_at: null` を伴う同期成功を返してもよい(MAY)。そのような購入は `get_media_buys` で取得可能でなければならず(MUST)、コミット時に `confirmed_at` を正確に一度だけ設定して遷移しなければなりません(MUST)。`confirmed_at: null` の暫定購入は `active` であってはならず(MUST NOT)、`packages[].committed_metrics` を含んではなりません(MUST NOT)。 #### 確定パッケージのレポート契約 レスポンス内の各パッケージは `committed_metrics` を運びうる(MAY)——このパッケージについてセラーが配信レポートで埋めることに合意した拘束的なレポート契約です。このフィールドは、標準メトリクス(閉じた `available-metric.json` 列挙由来)とベンダー定義メトリクス(BrandRef に紐付く)の両方を運ぶ統一配列で、各エントリは明示的な `scope` 判別子でタグ付けされ、`committed_at` でタイムスタンプされます: `confirmed_at` が `null` の場合、セラーは `packages[].committed_metrics` を省略しなければなりません(MUST)。`confirmed_at` を設定する最初のレスポンスは初期の committed-metrics セットを含んでもよく(MAY)、各エントリの `committed_at` は `confirmed_at` と等しくなければなりません(MUST)。 ```json theme={null} { "package_id": "pkg_001", "committed_metrics": [ { "scope": "standard", "metric_id": "impressions", "committed_at": "2026-04-29T10:53:00Z" }, { "scope": "standard", "metric_id": "completed_views", "committed_at": "2026-04-29T10:53:00Z" }, { "scope": "vendor", "vendor": { "domain": "attentionvendor.example" }, "metric_id": "attention_units", "committed_at": "2026-04-29T10:53:00Z" }, { "scope": "standard", "metric_id": "viewable_rate", "qualifier": { "viewability_standard": "mrc" }, "committed_at": "2026-05-30T14:22:00Z" } ] } ``` **契約の仕組み:** * **Day-1 のエントリ**は `committed_at = confirmed_at` を共有します。セラーは、プロダクトの `reporting_capabilities` から配信する準備があるものに基づいて、`create_media_buy` レスポンスで day-1 セットを刻印します。 * **フライト中の追加**は `update_media_buy` を通じて追記されます——それぞれ独自の `committed_at` タイムスタンプを持つ追記専用です。これにより、セラーは購入をキャンセルして再発行することなく、「Adelaide のアテンションは30日目以降から契約の一部です」と正直に言えます。 * **既存のエントリは不変です。** セラーは、既存エントリを変更または削除しようとする `update_media_buy` リクエストを `validation_error`(推奨コード: `IMMUTABLE_FIELD`)で拒否しなければなりません(MUST)。新しいエントリは追記できます。 * **標準メトリクスの qualifier。** 一部のメトリクスは複数の非互換な計測パスを持ち、曖昧さの解消が必要です: * **`viewability_standard`** — `metric_id` が `viewable_impressions`、`viewable_rate`、`measurable_impressions` のいずれかで、セラーが特定のビューアビリティ標準にコミットする場合(MRC と GroupM は実質的に異なる閾値——`viewability-standard` 列挙を参照)、エントリは `qualifier.viewability_standard` を持たなければなりません(MUST)。`missing_metrics` でも対称: MRC ビューアビリティを期待するバイヤーは、GroupM のみの配信レポートを MRC コミットの欠如としてフラグします。 * **`completion_source`** — `metric_id` が `completion_rate` で、セラーが特定のソース(プレーヤー/広告サーバー自身の完了イベント vs. `performance_standard.vendor` に紐付く第三者計測ベンダー)にコミットする場合、エントリは `qualifier.completion_source`(`seller_attested` または `vendor_attested`)を持たなければなりません(MUST)。二つのパスは、特に SSAI 環境で実質的に異なるレートを生みうる。`missing_metrics` でも対称。 * **`attribution_methodology`** — `metric_id` が成果メトリクス(`conversions`、`conversion_value`、`roas`、`cost_per_acquisition`、`incremental_sales_lift`、`brand_lift`、`foot_traffic`、`conversion_lift`、`brand_search_lift`、`units_sold`、`new_to_brand_rate`、`new_to_brand_units`、`leads`)で、セラーが特定のアトリビューション手法にコミットする場合、エントリは `qualifier.attribution_methodology`(リテールメディアのクローズドループには `deterministic_purchase`、その他のパスには `probabilistic`、`panel_based`、`modeled`)を持つべきです(SHOULD)。異なる手法の下の二つの成果行は交換不可。`missing_metrics` でも対称。 * **`attribution_window`** — `metric_id` が成果メトリクスで、セラーが特定のルックバックウィンドウにコミットする場合、エントリは構造化された期間として `qualifier.attribution_window`(`{ interval: 14, unit: "days" }`)を持つべきです(SHOULD)。異なるウィンドウの二つの成果行は、バイヤーが誤って期間をまたいで集計しないよう、別々の行として報告されます。 qualifier がなければ契約は曖昧になり、照合は配信レポートがたまたま運ぶものにフォールバックします。qualifier の語彙は閉じています(`additionalProperties: false`)。新しいキーは後続のマイナーで明示的に出荷されます。 * **照合:** `get_media_buy_delivery` の `missing_metrics` は、`committed_metrics` を `committed_at < reporting_period.end` のエントリにフィルタし、レポートで埋められていないものをフラグします。フライト中にコミットされたメトリクスは、そのコミットタイムスタンプ以降のみ監査されます。qualifier は逐語的に一致します——コミット済みの `{viewable_rate, mrc}` は、`viewability.standard: groupm` を運ぶ配信された `viewable_rate` では満たされません。 * **v1 では任意。** パッケージごとのスナップショットインフラを持たないセラーは段階的に採用できます。欠如は適合ですが、既知の監査ギャップを伴います: スナップショットがなければ、`missing_metrics` はレポート時のプロダクトのライブ `available_metrics` に対して照合され、作成時にコミットされた内容を反映しないことがあります。`committed_metrics` を省略するセラーはこのリスクを受け入れます。バイヤーは欠如を「クリーンな配信」ではなく「監査グレードの契約なし」として扱うべきです(SHOULD)。次のメジャーで必須になる見込み。 ### エラーレスポンス | Field | Description | | -------- | ------------------ | | `errors` | 失敗理由を示すエラーオブジェクト配列 | ### Submitted レスポンス 購入を同期的に確定できない場合に返されます——例: IO 署名を待つ保証付き購入、ガバナンスレビューのキュー入り、バッチ処理など。完了アーティファクト(`tasks/get` またはプッシュ通知 Webhook で配信)が `media_buy_id` と `packages` を運びます。 プロポーザル固有の受諾 Webhook はありません。人間の承認、IO 署名、または非同期処理を要するプロポーザル実行は、この同じ submitted タスクエンベロープと標準のタスク/Webhook 完了パスを使います。 | Field | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `status` | リテラルの `"submitted"` — ライフサイクル状態に `media_buy_status` を使う同期成功分岐からこの形状を区別します。3.1 移行期間中、セラーは後方互換のため同期成功で非推奨のトップレベル MediaBuyStatus 値も出してよい(MAY)。 | | `task_id` | バイヤーが `tasks/get` でポーリングする、または Webhook コールバックで受け取るハンドル。 | | `message` | 任意の人が読める説明(例: 「営業チームの IO 署名待ち」)。 | | `errors` | 任意の助言的な警告(非ブロッキング)。最終的な失敗はエラーレスポンスに属します。 | **Note**: レスポンスはこれら三つの形状で相互排他です。まず `status` でディスパッチします: `"submitted"` → 非同期エンベロープ、それ以外は成功フィールドにアクセスする前に `errors` を確認します。 ### Submitted と同期 Success をいつ返すか(規範的) `submitted` と同期成功の選択は**呼び出しごと**で、プロダクトごとの属性と各特定の作成に対するセラーのポリシーに駆動されます——一律のセラーごとのルールではありません。営業保証のセラーは、同じセッション内で一部の `create_media_buy` 呼び出しに同期成功を、他に `submitted` を正当に返しうる。適合的な SDK skill は、入力に関わらずすべての `create_media_buy` について `submitted` を返すようエージェントに指示してはなりません(MUST NOT)。一律に `submitted` を返すセラーは、`sales-guaranteed` コンプライアンスストーリーボードの非 IO 承認パスで失敗します。 セラーは次の場合に `submitted` を返さなければなりません(MUST): * リクエストが `delivery_type: "guaranteed"` のプロダクトを一つ以上参照し、**かつ**セラーが `requires_io_approval` 機能を宣言する場合——人間の承認ハンドシェイクはレスポンス内で完了できません。完了アーティファクトは IO 署名の完了後に `tasks/get` または Webhook で配信されます。 * リクエストが同期的に完了できないセラー側のガバナンスレビューをトリガーする場合(例: 規制業種向けの手動ブランドセーフティレビュー)。 * リクエストが、セラーがレスポンスタイムアウト内に消化できないバッチ処理キューに入る場合。 セラーは次の場合に同期成功を返さなければなりません(MUST): * 参照されるすべてのプロダクトが `delivery_type: "non_guaranteed"` の場合。購入はインラインで作成・確認され、`media_buy_id` と `packages` が即座に発行されます。これはセラーの専門領域に関わらず適用されます——非保証プロダクトを提供する営業保証のセラーは同期成功を返します。 * リクエストが保証プロダクトを参照し、セラーが `requires_io_approval` を宣言しない場合(まれ。通常はセラーが承認を事前クリアしているリテール SKU や見積レートの保証フロー)。 * 購入が、バイヤーに即座に観測可能な既知の非終端状態(`pending_creatives` / `pending_start` / `active` / `paused`)に入る場合。 コンプライアンスグレーダーは、別々のストーリーボードシナリオを通じて同じセラーに対して両パスを観測します: `create_buy_submitted` シナリオは `requires_io_approval` を持つ保証プロダクトをシードし、四つの共有シナリオ(`measurement_terms_rejected`、`pending_creatives_to_start`、`inventory_list_targeting`、`invalid_transitions`)は非保証プロダクトをシードして同期の `media_buy_id` 返却を期待します。同期期待のシナリオで `submitted` を返すセラーはコンプライアンスに失敗します——フィクスチャパターンは [`sales-guaranteed` 専門領域](https://github.com/adcontextprotocol/adcp/tree/main/static/compliance/source/specialisms/sales-guaranteed) を参照(オープンブリーフの `get_products` 呼び出しが同期作成パスに解決されるよう、非保証プロダクトが最初にリストされます)。 このルールは、[#3822](https://github.com/adcontextprotocol/adcp/issues/3822) で追跡される skill ↔ storyboard の矛盾を解決します: 「すべての `create_media_buy` にタスクエンベロープを返す」ようエージェントに指示する SDK skill は非適合です。正しい skill は、プロダクトごとの `delivery_type` とセラーの `requires_io_approval` 機能でディスパッチするようエージェントに指示します。 ## 主なシナリオ ### ターゲティング付きキャンペーン 地理制限やフリークエンシーキャップを追加: ```javascript JavaScript test=false theme={null} import { testAgent } from '@adcp/sdk/testing'; import { CreateMediaBuyResponseSchema } from '@adcp/sdk'; // Calculate end date dynamically - 90 days from now const endDate = new Date(); endDate.setDate(endDate.getDate() + 90); const result = await testAgent.createMediaBuy({ brand: { domain: 'acmecorp.com' }, packages: [{ product_id: 'prod_d979b543', pricing_option_id: 'cpm_usd_auction', format_ids: [{ agent_url: 'https://creative.adcontextprotocol.org', id: 'display_300x250_image' }], budget: 2500, bid_price: 5.00, targeting_overlay: { geo_countries: ['US'], geo_regions: ['US-CA', 'US-NY'], frequency_cap: { suppress: { interval: 60, unit: 'minutes' } } } }], start_time: 'asap', end_time: endDate.toISOString() }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = CreateMediaBuyResponseSchema.parse(result.data); if ('errors' in validated && validated.errors) { throw new Error(`Creation failed: ${JSON.stringify(validated.errors)}`); } if ('media_buy_id' in validated) { console.log(`Campaign ${validated.media_buy_id} created with targeting`); } ``` ```python Python test=false theme={null} import asyncio import time from datetime import datetime, timedelta, timezone from adcp.testing import test_agent async def create_targeted_campaign(): # Calculate end date dynamically - 90 days from now end_date = datetime.now(timezone.utc) + timedelta(days=90) result = await test_agent.simple.create_media_buy( brand={ 'domain': 'acmecorp.com' }, packages=[{ 'product_id': 'prod_d979b543', 'pricing_option_id': 'cpm_usd_auction', 'format_ids': [{ 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'display_300x250_image' }], 'budget': 2500, 'bid_price': 5.00, 'targeting_overlay': { 'geo_countries': ['US'], 'geo_regions': ['US-CA', 'US-NY'], 'frequency_cap': { 'suppress': {'interval': 60, 'unit': 'minutes'} } } }], start_time='asap', end_time=end_date.isoformat().replace('+00:00', 'Z') ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Creation failed: {result.errors}") print(f"Campaign {result.media_buy_id} created with targeting") asyncio.run(create_targeted_campaign()) ``` ### コンバージョン最適化付きキャンペーン コンバージョン最適化配信のために per\_ad\_spend ターゲットを設定します。プロダクトが `conversion_tracking.supported_targets` でサポートを宣言しており、`sync_event_sources` 経由でイベントソースが設定済みである必要があります: ```javascript JavaScript test=false theme={null} import { testAgent } from '@adcp/sdk/testing'; import { CreateMediaBuyResponseSchema } from '@adcp/sdk'; const endDate = new Date(); endDate.setDate(endDate.getDate() + 90); const result = await testAgent.createMediaBuy({ brand: { domain: 'acmecorp.com' }, packages: [{ product_id: 'prod_retail_sp', pricing_option_id: 'cpc_usd_auction', budget: 10000, bid_price: 1.20, optimization_goals: [{ kind: 'event', event_sources: [ { event_source_id: 'retailer_sales', event_type: 'purchase', value_field: 'value' } ], target: { kind: 'per_ad_spend', value: 4.0 }, priority: 1 }] }], start_time: 'asap', end_time: endDate.toISOString() }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = CreateMediaBuyResponseSchema.parse(result.data); if ('errors' in validated && validated.errors) { throw new Error(`Creation failed: ${JSON.stringify(validated.errors)}`); } if ('media_buy_id' in validated) { console.log(`Campaign ${validated.media_buy_id} created with per_ad_spend target`); } ``` ```python Python test=false theme={null} import asyncio import time from datetime import datetime, timedelta, timezone from adcp.testing import test_agent async def create_optimized_campaign(): end_date = datetime.now(timezone.utc) + timedelta(days=90) result = await test_agent.simple.create_media_buy( brand={ 'domain': 'acmecorp.com' }, packages=[{ 'product_id': 'prod_retail_sp', 'pricing_option_id': 'cpc_usd_auction', 'budget': 10000, 'bid_price': 1.20, 'optimization_goals': [{ 'kind': 'event', 'event_sources': [ { 'event_source_id': 'retailer_sales', 'event_type': 'purchase', 'value_field': 'value' } ], 'target': { 'kind': 'per_ad_spend', 'value': 4.0 }, 'priority': 1 }] }], start_time='asap', end_time=end_date.isoformat().replace('+00:00', 'Z') ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Creation failed: {result.errors}") print(f"Campaign {result.media_buy_id} created with per_ad_spend target") asyncio.run(create_optimized_campaign()) ``` ### カタログ連動パッケージ カタログ連動パッケージは、カタログ全体のアイテムに対して単一の予算枠を割り当てます。アイテムごとにパッケージを個別作成する代わりに、プラットフォームがパフォーマンスに基づいてカタログ全アイテムへの配信を最適化します。これは Google Performance Max や Meta Dynamic Product Ads などのカタログベースのキャンペーンタイプに相当する AdCP の機能です。 パッケージにカタログ連動を設定するには `catalogs` フィールドを含めます。各カタログはそれぞれ異なるタイプ(例:プロダクトカタログ 1 件、ストアカタログ 1 件)を持つ必要があります。参照するカタログは [`sync_catalogs`](/docs/media-buy/task-reference/sync_catalogs) 経由で事前に同期されている必要があります。 **同期済みジョブカタログを使ったジョブキャンペーン:** ```json test=false theme={null} { "brand": { "domain": "acme-restaurants.com" }, "packages": [{ "product_id": "prod_job_board", "pricing_option_id": "cpc_eur_auction", "budget": 5000, "bid_price": 2.50, "catalogs": [{ "catalog_id": "chef-vacancies", "type": "job" }] }], "start_time": "asap", "end_time": "2026-06-30T23:59:59Z" } ``` **プロダクトカタログとストア集客圏ターゲティングを使ったリテールメディア:** ```json test=false theme={null} { "brand": { "domain": "acmecorp.com" }, "packages": [{ "product_id": "prod_retail_sp", "pricing_option_id": "cpc_usd_auction", "budget": 10000, "bid_price": 1.20, "catalogs": [{ "catalog_id": "gmc-primary", "type": "product", "tags": ["summer"] }], "targeting_overlay": { "store_catchments": [{ "catalog_id": "retail-locations", "catchment_ids": ["drive"] }] } }], "start_time": "asap", "end_time": "2026-09-30T23:59:59Z" } ``` プラットフォームはパフォーマンスに基づいてカタログアイテム間で予算を配分します。アイテムごとのレポートには、`by_catalog_item` ブレークダウンを返す [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) を使用します。カタログ連動パッケージのクリエイティブバリアントは、広告としてレンダリングされた個別のカタログアイテムを表します。 **明示的なシグナルターゲティング付きパッケージ:** バイヤーがセラー提供のシグナルを特定のパッケージに適用したい場合は `targeting_overlay.signal_targeting_groups` を使います。選択したプロダクトは `signal_targeting_allowed: true` を設定し、シグナルを適格にしなければなりません——インラインの `signal_targeting_options`(存在する場合)、インラインオプションを省略するホールセールプロダクトには `get_signals`、そして `signal_targeting_rules` を通じて。常にグループ化された式の形状を使います: トップレベルの `operator: "all"` と、include グループには `operator: "any"`、除外グループには `operator: "none"` を使う子グループ。単純な include のみのターゲティングには一つの `any` グループを送ります。バイナリシグナルでは、include と除外の両グループで `value: true` を送ります。除外は `value: false` ではなく親の `none` グループで表現します。シグナルは `signal_ref` で参照します: プロダクトローカルのシグナルオプションには `scope: "product"`、データプロバイダーが公開する adagents.json の `signals[]` で定義されたシグナルには `data_provider_domain` を伴う `scope: "data_provider"`、ソースネイティブなシグナルには `signal_source_url` を伴う `scope: "signal_source"`。これは、`sync_audiences` を通じて登録されたファーストパーティオーディエンスのみを参照する `audience_include` / `audience_exclude` とは別物です。`signal_agent_segment_id` は、選択したプロダクトオプションまたは `get_signals` の結果が、セラーが要求する別個の実行ハンドルとしてそれを公開した場合にのみ送ります。 クリエイティブがビルド時の `signal_condition`(`build_creative` の `signal_conditions` ファンアウト由来、[#5240](https://github.com/adcontextprotocol/adcp/issues/5240))を運ぶ場合、シグナルターゲティングが非互換なパッケージへの割り当て——例: 晴れのクリエイティブを雨ターゲットのパッケージへ——は `SIGNAL_TARGETING_INCOMPATIBLE` で拒否されます。互換性は、ここで使われる同じ共有 `signal_ref` アイデンティティで照合されます。規範的なトラフィッキング互換性契約は[シグナル仕様](/docs/signals/specification#creative-signal-fan-out-and-trafficking-compatibility)を参照してください。 ```json test=false theme={null} { "brand": { "domain": "acmecorp.com" }, "packages": [{ "product_id": "retail_video_premium", "pricing_option_id": "media_cpm_usd", "budget": 25000, "targeting_overlay": { "signal_targeting_groups": { "operator": "all", "groups": [{ "operator": "any", "signals": [{ "signal_ref": { "scope": "data_provider", "data_provider_domain": "pinnacle-data.example", "signal_id": "auto_intenders" }, "value_type": "binary", "value": true, "pricing_option_id": "signal_cpm_usd_250", "signal_agent_segment_id": "seller_sig_auto_intenders" }] }] } } }], "start_time": "asap", "end_time": "2026-09-30T23:59:59Z" } ``` include と除外を組み合わせた例: ```json test=false theme={null} { "brand": { "domain": "acmecorp.com" }, "packages": [{ "product_id": "retail_video_premium", "pricing_option_id": "media_cpm_usd", "budget": 25000, "targeting_overlay": { "signal_targeting_groups": { "operator": "all", "groups": [ { "operator": "any", "signals": [ { "signal_ref": { "scope": "product", "signal_id": "high_intent_shoppers" }, "value_type": "binary", "value": true }, { "signal_ref": { "scope": "product", "signal_id": "loyalty_members" }, "value_type": "binary", "value": true } ] }, { "operator": "none", "signals": [ { "signal_ref": { "scope": "product", "signal_id": "recent_purchasers" }, "value_type": "binary", "value": true } ] } ] } } }], "start_time": "asap", "end_time": "2026-09-30T23:59:59Z" } ``` ### クリエイティブをインライン指定したキャンペーン キャンペーン作成と同時にクリエイティブをアップロードします: ```javascript JavaScript test=false theme={null} import { testAgent } from '@adcp/sdk/testing'; import { CreateMediaBuyResponseSchema } from '@adcp/sdk'; // Calculate end date dynamically - 90 days from now const endDate = new Date(); endDate.setDate(endDate.getDate() + 90); const result = await testAgent.createMediaBuy({ brand: { domain: 'acmecorp.com' }, packages: [{ product_id: 'prod_d979b543', pricing_option_id: 'cpm_usd_auction', format_ids: [{ agent_url: 'https://creative.adcontextprotocol.org', id: 'display_300x250_image' }], budget: 2500, bid_price: 5.00, creatives: [{ creative_id: 'hero_video_30s', name: 'Hero Video', format_id: { agent_url: 'https://creative.adcontextprotocol.org', id: 'display_300x250_image' }, assets: { image: { url: 'https://cdn.example.com/hero-banner.jpg', width: 300, height: 250 } } }] }], start_time: 'asap', end_time: endDate.toISOString() }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = CreateMediaBuyResponseSchema.parse(result.data); if ('errors' in validated && validated.errors) { throw new Error(`Creation failed: ${JSON.stringify(validated.errors)}`); } if ('packages' in validated) { console.log(`Campaign created with ${validated.packages[0].creative_assignments.length} creatives`); } ``` ```python Python test=false theme={null} import asyncio import time from datetime import datetime, timedelta, timezone from adcp.testing import test_agent async def create_with_creatives(): # Calculate end date dynamically - 90 days from now end_date = datetime.now(timezone.utc) + timedelta(days=90) result = await test_agent.simple.create_media_buy( brand={ 'domain': 'acmecorp.com' }, packages=[{ 'product_id': 'prod_d979b543', 'pricing_option_id': 'cpm_usd_auction', 'format_ids': [{ 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'display_300x250_image' }], 'budget': 2500, 'bid_price': 5.00, 'creatives': [{ 'creative_id': 'hero_video_30s', 'name': 'Hero Video', 'format_id': { 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'display_300x250_image' }, 'assets': { 'image': { 'url': 'https://cdn.example.com/hero-banner.jpg', 'width': 300, 'height': 250 } } }] }], start_time='asap', end_time=end_date.isoformat().replace('+00:00', 'Z') ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Creation failed: {result.errors}") print(f"Campaign created with {len(result.packages[0].creative_assignments)} creatives") asyncio.run(create_with_creatives()) ``` ### レポート用 Webhook を設定したキャンペーン 自動レポート通知を受け取ります。 ```javascript JavaScript test=false theme={null} import { testAgent } from '@adcp/sdk/testing'; import { CreateMediaBuyResponseSchema } from '@adcp/sdk'; // Calculate end date dynamically - 90 days from now const endDate = new Date(); endDate.setDate(endDate.getDate() + 90); const result = await testAgent.createMediaBuy({ brand: { domain: 'acmecorp.com' }, packages: [{ product_id: 'prod_d979b543', pricing_option_id: 'cpm_usd_auction', format_ids: [{ agent_url: 'https://creative.adcontextprotocol.org', id: 'display_300x250_image' }], budget: 2500, bid_price: 5.00 }], start_time: 'asap', end_time: endDate.toISOString(), reporting_webhook: { url: 'https://buyer.example.com/webhooks/reporting', authentication: { schemes: ['Bearer'], credentials: 'secret_token_xyz_minimum_32_chars' }, reporting_frequency: 'daily', requested_metrics: ['impressions', 'spend', 'video_completions'] } }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = CreateMediaBuyResponseSchema.parse(result.data); if ('errors' in validated && validated.errors) { throw new Error(`Creation failed: ${JSON.stringify(validated.errors)}`); } if ('media_buy_id' in validated) { console.log(`Campaign created - daily reports will be sent to webhook`); } ``` ```python Python test=false theme={null} import asyncio import time from datetime import datetime, timedelta, timezone from adcp.testing import test_agent async def create_with_reporting(): # Calculate end date dynamically - 90 days from now end_date = datetime.now(timezone.utc) + timedelta(days=90) result = await test_agent.simple.create_media_buy( brand={ 'domain': 'acmecorp.com' }, packages=[{ 'product_id': 'prod_d979b543', 'pricing_option_id': 'cpm_usd_auction', 'format_ids': [{ 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'display_300x250_image' }], 'budget': 2500, 'bid_price': 5.00 }], start_time='asap', end_time=end_date.isoformat().replace('+00:00', 'Z'), reporting_webhook={ 'url': 'https://buyer.example.com/webhooks/reporting', 'authentication': { 'schemes': ['Bearer'], 'credentials': 'secret_token_xyz_minimum_32_chars' }, 'reporting_frequency': 'daily', 'requested_metrics': ['impressions', 'spend', 'video_completions'] } ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Creation failed: {result.errors}") print('Campaign created - daily reports will be sent to webhook') asyncio.run(create_with_reporting()) ``` ### プロポーザルの実行 `get_products` のプロポーザルを、パッケージを手作業で組まずに実行します。 ```javascript JavaScript test=false theme={null} import { testAgent } from '@adcp/sdk/testing'; import { CreateMediaBuyResponseSchema } from '@adcp/sdk'; // Calculate end date dynamically - 90 days from now const endDate = new Date(); endDate.setDate(endDate.getDate() + 90); const result = await testAgent.createMediaBuy({ proposal_id: 'swiss_balanced_v1', // From get_products response total_budget: { amount: 50000, currency: 'USD' }, brand: { domain: 'acmecorp.com' }, start_time: 'asap', end_time: endDate.toISOString() }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = CreateMediaBuyResponseSchema.parse(result.data); if ('errors' in validated && validated.errors) { throw new Error(`Creation failed: ${JSON.stringify(validated.errors)}`); } if ('media_buy_id' in validated) { // パブリッシャーがプロポーザルの配分をパッケージに変換 console.log(`Created media buy ${validated.media_buy_id}`); console.log(`Packages created: ${validated.packages.length}`); } ``` ```python Python test=false theme={null} import asyncio import time from datetime import datetime, timedelta, timezone from adcp.testing import test_agent async def execute_proposal(): # Calculate end date dynamically - 90 days from now end_date = datetime.now(timezone.utc) + timedelta(days=90) result = await test_agent.simple.create_media_buy( proposal_id='swiss_balanced_v1', # From get_products response total_budget={ 'amount': 50000, 'currency': 'USD' }, brand={ 'domain': 'acmecorp.com' }, start_time='asap', end_time=end_date.isoformat().replace('+00:00', 'Z') ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Creation failed: {result.errors}") # パブリッシャーがプロポーザルの配分をパッケージに変換 print(f"Created media buy {result.media_buy_id}") print(f"Packages created: {len(result.packages)}") asyncio.run(execute_proposal()) ``` プロポーザルを実行する際: * パブリッシャーが `total_budget` を使って配分割合を実額に変換 * 配分に基づきパッケージが自動生成 * それ以外のフィールド(brand、start\_time、end\_time など)は Manual モードと同じ 会話的なリファインを含む完全なフローは [Proposals](/docs/media-buy/product-discovery/media-products#proposals) を参照。 ### 相関のための Context `context` フィールドは、セラーがレスポンスと Webhook でそのまま返す不透明なオブジェクトです。別個のルックアップテーブルを維持することなく、セラーが割り当てた ID を自分の内部システムにマッピングするために使います。 Context は二つのレベルで機能します: * **メディアバイレベル** — `create_media_buy` レスポンスでエコーされる * **パッケージレベル** — 各パッケージのレスポンス、Webhook、読み取り面でエコーされ、`package_id` を内部ラインアイテムにマッピングするのに有用。明示的なパッケージリクエストでは、セラーは `product_id` もエコーしなければなりません(MUST)。 混在したセラー集団を対象とする場合、`product_id` をエコーしないかもしれない古いセラーのためのレガシーセーフなフォールバックとして、`context.buyer_ref` のようなパッケージ context を含めます。 **内部のキャンペーン ID・ラインアイテム ID へのマッピング:** ```json test=false theme={null} { "brand": { "domain": "acmecorp.com" }, "context": { "campaign_id": "camp-2026-q3-awareness", "planner": "media-team-west", "trace_id": "req-8f3a-4b2c" }, "packages": [ { "product_id": "prod_d979b543", "pricing_option_id": "cpm_usd_auction", "budget": 15000, "bid_price": 5.00, "context": { "line_item_id": "li-001", "flight": "june-awareness" } }, { "product_id": "prod_e8fd6012", "pricing_option_id": "cpm_usd_auction", "budget": 10000, "bid_price": 4.50, "context": { "line_item_id": "li-002", "flight": "june-retargeting" } } ], "start_time": "2026-06-01T00:00:00Z", "end_time": "2026-08-31T23:59:59Z" } ``` セラーのレスポンスは、セラーが割り当てた ID と並べて context を返します: ```json test=false theme={null} { "media_buy_id": "mb_12345", "context": { "campaign_id": "camp-2026-q3-awareness", "planner": "media-team-west", "trace_id": "req-8f3a-4b2c" }, "packages": [ { "package_id": "pkg_001", "product_id": "prod_d979b543", "context": { "line_item_id": "li-001", "flight": "june-awareness" } }, { "package_id": "pkg_002", "product_id": "prod_e8fd6012", "context": { "line_item_id": "li-002", "flight": "june-retargeting" } } ] } ``` セラーは context データをパースしたり、それに基づいて動作したりしてはなりません——これは純粋にバイヤーの内部利用のために存在します。 ## エラーハンドリング よくあるエラーと解決策: | Error Code | Description | Resolution | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `PRODUCT_NOT_FOUND` | Invalid product\_id | Verify product exists via `get_products` | | `UNSUPPORTED_FEATURE` | Format not supported by the product — covers legacy named-format selectors (`format_ids[]` not in the product's accepted formats), 3.1+ format-option selectors (`format_option_refs[]` entries that do not resolve against the product's `format_options[]`, legacy-format-only products with no `format_options[]`, or product `format_options[]` entries that do not publish selectable `format_option_id` values), and direct canonical selectors (`format_kind`/`params` outside or under-specifying the product declaration) | Check the product's `format_ids` and/or `format_options[]` from `get_products` — re-author against a supported format, add the required canonical parameters, or pick a `format_option_ref` from the product's published `format_options[]` | | `BUDGET_TOO_LOW` | Budget below product minimum | Increase budget or choose different product | | `TARGETING_TOO_NARROW` | Targeting yields zero inventory | Broaden geographic or audience criteria | | `POLICY_VIOLATION` | Brand/product violates policy | Review publisher's content policies | | `INVALID_PRICING_OPTION` | pricing\_option\_id not found | Use ID from product's `pricing_options` | | `CREATIVE_ID_EXISTS` | Creative ID already exists in the seller's creative namespace | For library-backed sellers, assign existing creatives via `creative_assignments` or update via `sync_creatives`; for inline-only sellers, use a different package-scoped `creative_id` | Example error response: ```json theme={null} { "errors": [{ "code": "UNSUPPORTED_FEATURE", "message": "Product 'prod_d979b543' does not support format 'display_728x90'", "field": "packages[0].format_ids[0]", "suggestion": "Use display_300x250_image, display_300x250_html, or display_300x250_generative formats" }] } ``` ## Key Concepts ### フォーマット指定 各パッケージは、使用するフォーマットを `format_ids[]`(レガシーの名前付きフォーマットパス——構造化された `{agent_url, id}` 参照)、`format_option_refs[]`(3.1 以降のフォーマットオプションパス——プロダクトの `format_options[]` への参照)、または直接の正準セレクター(`format_kind` と任意の `params`)で指定すべきです(SHOULD)。すべてのフォーマットセレクターを省略すると、プロダクトがサポートするすべてのフォーマットがデフォルトになります。指定することで、システムは次を行えます: * アドサーバーにプレースホルダークリエイティブを公開する * 必要なクリエイティブアセットを正確にピン留めする * プロダクトが要求フォーマットをサポートするか検証する * 不足しているアセットを追跡する セレクターの優先順位は決定的です: `format_option_refs[]` が存在すればそれが勝ち、なければ `format_ids[]` が存在すればそれが勝ち、なければ直接の `format_kind`/`params` が使われ、それもなければパッケージはすべてのプロダクトフォーマットにデフォルトします。バイヤーのコードベースが `Product.format_options[]` を読み、プロダクトが選択可能な `format_option_id` 値を公開する場合は `format_option_refs[]` を使います。レガシーフォーマットのみのセラーやライブラリと統合する場合は `format_ids[]` を使います。直接の `format_kind` は、バイヤーがプロダクトローカルの参照なしで対象プロダクト宣言を満たすのに十分な正準パラメータを持つ場合にのみ使います。`format_option_refs[]` と `format_ids[]` の二重出力は許可され、混在したセラー集団を対象とするバイヤー SDK には推奨されます——上記の `format_ids` 行を参照。 3.1 以降のフォーマットオプションの例(パブリッシャースコープの `Product.format_options[]` エントリに対してバイヤーが作成する場合): ```json test=false theme={null} { "packages": [ { "product_id": "prod_d979b543", "pricing_option_id": "cpm_usd_auction", "format_option_refs": [ { "scope": "publisher", "publisher_domain": "daily-pulse.example", "format_option_id": "daily_pulse_homepage_image" } ], "budget": 2500, "bid_price": 5.00 } ] } ``` 詳細は下記の [フォーマットワークフロー](#format-workflow) を参照。 ### ブランド参照 `brand` フィールドはポリシー準拠とビジネス目的のために広告主を識別します。 ```json theme={null} { "brand": { "domain": "acmecorp.com" } } ``` ブランドの完全なアイデンティティデータ(色、フォント、プロダクトカタログ)は実行時に brand.json から解決されます。[brand.json](/docs/brand-protocol/brand-json) を参照。 ### 価格と通貨 各パッケージは `pricing_option_id` を指定し、以下を決定します: * 通貨(USD、EUR など) * 価格モデル(CPM、CPCV、CPP など) * レートと固定/オークションの区別 セラーがサポートする場合、パッケージごとに異なる通貨を使用できます。[Pricing Models](/docs/media-buy/advanced-topics/pricing-models) 参照。 ### ターゲティングオーバーレイ **使用は最小限に** — ターゲティングの大部分はブリーフに含め、プロダクト選択で処理されるべきです。 オーバーレイは以下に限定して使用します: * 地理的制限(RCT テスト、規制対応) * フリークエンシーキャップ * AXE セグメントの包含/除外(レガシー——新規統合は [TMP](/docs/trusted-match) を使用) 詳細は [Targeting](/docs/media-buy/advanced-topics/targeting) を参照。 ## フォーマットワークフロー ### フォーマット指定が重要な理由 メディアバイ作成時にフォーマットを指定すると次が可能になります: 1. **プレースホルダー作成** - パブリッシャーが正しい仕様でアドサーバーにプレースホルダーを用意 2. **検証** - 要求フォーマットをプロダクトがサポートするかシステムが検証 3. **期待値の明確化** - 双方が必要なものを正確に把握 4. **進捗トラッキング** - 不足アセットと必須アセットを可視化 5. **技術セットアップ** - クリエイティブ到着前にアドサーバーを設定 ### 完全なワークフロー ``` 1. list_creative_formats → 利用可能なフォーマット仕様を取得 2. get_products → プロダクトを発見(サポートする format_ids[] および/または format_options[] を返す) 3. 互換性を検証 → 望むフォーマットをプロダクトがサポートするか確認 4. create_media_buy → フォーマットを format_ids[](v1)、 format_option_refs[](v2 参照)、または format_kind/params(直接正準)で指定; 省略すると all-formats デフォルト └── パブリッシャーがプレースホルダーを作成 └── クリエイティブ要件を明確化 5. クリエイティブ提供 → ライブラリ対応セラーには `sync_creatives`、 インライン専用セラーにはインラインの `packages[].creatives` で 合致するファイルをアップロード 6. キャンペーン有効化 → プレースホルダーを実クリエイティブに置換 ``` ### フォーマット検証 パブリッシャーが必ず検証する事項: * すべてのフォーマットがプロダクトでサポートされています * フォーマット仕様が `list_creative_formats` の出力と一致 * 期限内にクリエイティブ要件を満たせる 無効なレガシー名前付きフォーマットの例: ```json theme={null} { "errors": [{ "code": "UNSUPPORTED_FEATURE", "message": "Product 'ctv_sports_premium' does not support format 'audio_standard_30s'", "field": "packages[0].format_ids[0]", "supported_formats": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_standard_30s" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_standard_15s" } ] }] } ``` 無効な 3.1 以降のフォーマットオプションの例: ```json theme={null} { "errors": [{ "code": "UNSUPPORTED_FEATURE", "message": "Product 'ctv_sports_premium' has no format_options[] entry for format option 'audio_standard'", "field": "packages[0].format_option_refs[0]", "supported_format_option_refs": [ { "scope": "publisher", "publisher_domain": "streamhaus.example", "format_option_id": "ctv_video_30s_premium" }, { "scope": "publisher", "publisher_domain": "streamhaus.example", "format_option_id": "ctv_video_15s_premium" } ] }] } ``` ### フライト日程バリデーション 新規メディアバイでは、トップレベルの `start_time` は `"asap"` か、過去でない日時のいずれかでなければなりません(MUST)。過去の具体的な `start_time` は `INVALID_REQUEST` エラーを返さなければなりません(MUST)。 パッケージに `start_time` または `end_time` を指定した場合、セラーは以下を検証すべきだ: * 両日程がメディアバイの日付範囲内に収まっています * `start_time` が `end_time` より前です 範囲外または逆転した日程は `INVALID_REQUEST` エラーを返すべきだ: ```json theme={null} { "errors": [{ "code": "INVALID_REQUEST", "message": "Package 'week_5' end_time 2026-04-05T23:59:59Z is after media buy end_time 2026-03-31T23:59:59Z", "field": "packages[3].end_time" }] } ``` ## 非同期オペレーション このタスクは即時完了する場合も、複雑さや承認要件によっては日数を要する場合もあります。レスポンスの `status` フィールドで結果と次のアクションを確認してください。 | Status | Meaning | Your Action | | ---------------- | ---------- | ------------------------ | | `completed` | 即時完了 | 結果を処理 | | `working` | 処理中(約2分) | 高頻度でポーリング or Webhook を待つ | | `submitted` | 長時間(数時間〜日) | Webhook を使うか低頻度でポーリング | | `input-required` | 追加情報が必要 | メッセージを読み、情報を返す | | `failed` | エラー発生 | エラーに対応 | **Note:** 完全なステータス一覧は [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照。 ### 即時成功 (`completed`) タスクが同期的に完了しました。非同期処理は不要です。 **Request:** ```javascript test=false theme={null} const response = await session.call('create_media_buy', { brand: { domain: 'acmecorp.com' }, packages: [ { product_id: 'prod_ctv_sports', pricing_option_id: 'cpm_fixed', budget: 50000 } ] }); ``` **Response:** ```json theme={null} { "status": "completed", "media_buy_id": "mb_12345", "account": { "account_id": "acc_acme_direct", "name": "Acme", "status": "active", "brand": { "domain": "acmecorp.com" }, "operator": "acmecorp.com" }, "media_buy_status": "active", "confirmed_at": "2025-06-01T10:00:00Z", "creative_deadline": "2025-06-15T23:59:59Z", "revision": 1, "packages": [ { "package_id": "pkg_001", "product_id": "prod_ctv_sports" } ] } ``` トップレベルの `status` はエンベロープのタスクステータス(TaskStatus)です——同期成功では `completed`。ボディレベルの `media_buy_status` は購入のライフサイクル状態(`pending_creatives`、`pending_start`、`active`、`paused`)を運びます。3.0 形式のレスポンスはこの二つの列挙を同じルートキーで衝突させていました。3.1 ではこれらは別々のフィールドです。セラーは、3.0 バイヤーとの後方互換のため 3.1 非推奨期間中に非推奨のトップレベル `status: MediaBuyStatus` を出し続けてもよい(MAY)が、3.1 バイヤーは `media_buy_status` を優先しなければなりません(MUST)。下記の [メディアバイステータスフィールド(3.1 移行)](#media-buy-status-field-3-1-migration) を参照。 ### 長時間処理 (`submitted`) タスクが手動承認キューに入っています。更新を受け取るため Webhook を設定します。 **Request with webhook:** ```javascript test=false theme={null} const response = await session.call('create_media_buy', { brand: { domain: 'acmecorp.com' }, packages: [ { product_id: 'prod_premium_ctv', pricing_option_id: 'cpm_fixed', budget: 500000 // Large budget triggers approval } ] }, { pushNotificationConfig: { url: 'https://your-app.com/webhooks/adcp', authentication: { schemes: ['bearer'], credentials: 'your_webhook_secret' } } } ); ``` **Initial response:** ```json theme={null} { "status": "submitted", "task_id": "task_abc123", "message": "Budget exceeds auto-approval limit. Sales review required (2-4 hours)." } ``` **Webhook POST when approved:** ```json theme={null} { "task_id": "task_abc123", "task_type": "create_media_buy", "status": "completed", "timestamp": "2025-01-22T14:30:00Z", "message": "Media buy approved and created", "result": { "media_buy_id": "mb_67890", "creative_deadline": "2025-06-20T23:59:59Z", "packages": [ { "package_id": "pkg_002", } ] } } ``` ### Error (`failed`) **Response:** ```json theme={null} { "status": "failed", "errors": [ { "code": "INSUFFICIENT_INVENTORY", "message": "Requested targeting yields no available impressions", "field": "packages[0].targeting", "suggestion": "Expand geographic targeting or increase CPM bid" } ] } ``` ### 即時成功 (`completed`) **Request:** ```javascript test=false theme={null} const response = await a2a.send({ message: { parts: [{ kind: 'data', data: { skill: 'create_media_buy', parameters: { brand: { domain: 'acmecorp.com' }, packages: [ { product_id: 'prod_ctv_sports', pricing_option_id: 'cpm_fixed', budget: 50000 } ] } } }] } }); ``` **Response:** ```json theme={null} { "status": "completed", "taskId": "task_123", "contextId": "ctx_456", "artifacts": [{ "parts": [ { "text": "Media buy created successfully" }, { "data": { "media_buy_id": "mb_12345", "creative_deadline": "2025-06-15T23:59:59Z", "packages": [ { "package_id": "pkg_001", } ] } } ] }] } ``` ### 処理中 (`working`) タスクが処理中です。SSE ストリーミングまたはポーリングで更新を取得します。 **Initial response:** ```json theme={null} { "status": "working", "taskId": "task_789", "contextId": "ctx_456" } ``` **SSE status update:** ```json theme={null} { "taskId": "task_789", "status": { "state": "working", "message": { "parts": [ { "text": "Validating inventory availability..." }, { "data": { "percentage": 50, "current_step": "inventory_check" } } ] } } } ``` ### 長時間処理 (`submitted`) **Request with push notification:** ```javascript test=false theme={null} const response = await a2a.send({ message: { parts: [{ kind: 'data', data: { skill: 'create_media_buy', parameters: { packages: [{ budget: 500000 }] // Triggers approval } } }] }, pushNotificationConfig: { url: 'https://your-app.com/webhooks/a2a', authentication: { schemes: ['bearer'], credentials: 'your_webhook_secret' } } }); ``` **Initial response:** ```json theme={null} { "status": "submitted", "taskId": "task_abc", "contextId": "ctx_456" } ``` **Webhook POST (Task) when completed:** ```json theme={null} { "id": "task_abc", "contextId": "ctx_456", "status": { "state": "completed", "message": { "parts": [ { "text": "Media buy approved and created" }, { "data": { "media_buy_id": "mb_67890", "packages": [{ "package_id": "pkg_002" }] } } ] }, "timestamp": "2025-01-22T14:30:00Z" } } ``` ### 入力要求 (`input-required`) タスクが確認または承認待ちで一時停止しています。 **Response:** ```json theme={null} { "status": "input-required", "taskId": "task_def", "contextId": "ctx_456", "artifacts": [{ "parts": [ { "text": "The requested budget exceeds your pre-approved limit. Please confirm you want to proceed with $500K spend." }, { "data": { "reason": "APPROVAL_REQUIRED", "errors": [ { "code": "BUDGET_EXCEEDS_LIMIT", "message": "Requested budget exceeds pre-approved limit", "field": "total_budget" } ] } } ] }] } ``` **Follow-up to approve:** ```javascript test=false theme={null} await a2a.send({ contextId: 'ctx_456', // Continue the conversation message: { parts: [{ kind: 'text', text: 'Yes, I confirm the $500K budget' }] } }); ``` ### Error (`failed`) **Response:** ```json theme={null} { "status": "failed", "taskId": "task_xyz", "artifacts": [{ "parts": [ { "text": "Failed to create media buy" }, { "data": { "errors": [ { "code": "INSUFFICIENT_INVENTORY", "message": "Requested targeting yields no available impressions", "suggestion": "Expand geographic targeting" } ] } } ] }] } ``` 非同期処理の完全なパターンは [Async Operations](/docs/building/by-layer/L3/async-operations) を参照。 ## 使用上の注意 * 総予算は各パッケージの個別の `budget` 値に基づいて配分されます * クリエイティブアセットはキャンペーン有効化のためにデッドライン前にアップロードしなければなりません * インプレッション時のターゲティング(オーディエンス、フリークエンシー、適合性)は [TMP](/docs/trusted-match) が処理します * `working`、`submitted` などの保留状態は正常であり、エラーではありません * オーケストレーターは保留状態を通常のワークフローの一部として処理しなければなりません * **インラインクリエイティブ**: `creatives` 配列はパッケージのクリエイティブをインラインで作成または提供します。セラーが `creative.has_creative_library: true` を宣言する場合、インラインクリエイティブはライブラリに入ります。既存のライブラリクリエイティブを更新するには [`sync_creatives`](/docs/creative/task-reference/sync_creatives) を、割り当てるには `creative_assignments` を使用。セラーがクリエイティブライブラリなしで `inline_creative_management: true` を宣言する場合、`create_media_buy` と `update_media_buy` で `packages[].creatives` を使い、`sync_creatives` は呼びません。 * **インラインクリエイティブのライフサイクル**: ライブラリ対応のインラインクリエイティブは、`sync_creatives` のアップロードと同じライフサイクルでライブラリに入ります。インライン専用のセラーはクリエイティブをパッケージスコープに保ち、後の `creative_id` による再利用を宣言しないことがあります。クリエイティブレビューは購入の結果とは独立しています。セラーは、購入がアクティベートされなかったというだけでレビューをスキップしてはなりません(MUST NOT)。未割り当てのライブラリクリエイティブの保持は 3.0 ではセラー定義です。[パッケージ上のインラインクリエイティブ](/docs/creative/creative-libraries#path-2-inline-creatives-on-the-package)を参照。 ## Content Standards メディアバイがコンテンツ標準を含む場合(`get_products` レスポンスの `governance.content_standards` フィールド、またはメディアバイリクエスト経由)、バイヤーは配信中のブランド適合性の強制を要求しています。 コンテンツ標準は、検証エージェント(例: IAS、DoubleVerify)で [`create_content_standards`](/docs/governance/content-standards/tasks/create_content_standards) を呼び出すことで作成されます。標準は、セラーのローカル評価モデルが検証エージェントの解釈と整合するよう、本番で使う前に各セラーと[キャリブレーション](/docs/governance/content-standards/tasks/calibrate_content)されなければなりません(MUST)。完全なセットアップワークフロー(create → calibrate → activate → validate)は[コンテンツ標準の概要](/docs/governance/content-standards/index)を参照してください。 ## ポリシー準拠 ブランドとプロダクトは作成時に検証されます。ポリシー違反はエラーを返します: ```json theme={null} { "errors": [{ "code": "POLICY_VIOLATION", "message": "Brand or product category not permitted on this publisher", "field": "brand", "suggestion": "Contact publisher for category approval process" }] } ``` パブリッシャーは以下を確認すべきだ: * ブランド/プロダクトが選択したパッケージと整合しています * クリエイティブが宣言したブランド/プロダクトと一致しています * キャンペーンがすべての広告ポリシーに準拠しています ## 次のステップ メディアバイ作成後: 1. **クリエイティブの提供**: ライブラリ対応のセラーには [`sync_creatives`](/docs/creative/task-reference/sync_creatives) を、インライン専用のセラーには [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) の `packages[].creatives` を使用 2. **ステータスの監視**: [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) を使用 3. **最適化**: [`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback) を使用 4. **更新**: [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) でキャンペーンを変更 ## メディアバイステータスフィールド(3.1 移行) 3.1 は、3.0 が同じルートキーで衝突させていた二つの列挙を分割します——すべてのレスポンスの先頭にあるエンベロープの `status`(TaskStatus)と、購入のライフサイクル状態を並べて運ぶボディの `media_buy_status`(MediaBuyStatus、**3.1 の新機能**)。レガシーのトップレベル `status: MediaBuyStatus` 形式は 3.1 で `deprecated: true` となり、3.2 で削除されます([#4906](https://github.com/adcontextprotocol/adcp/issues/4906))。`get_media_buys`、`get_media_buy_delivery`、`core/media-buy.json` のネストされた `status` は 4.0 で続きます([#4905](https://github.com/adcontextprotocol/adcp/issues/4905))。 3.1 バイヤーは、存在する場合 `media_buy_status` を優先しなければなりません(MUST)。3.1 コンプライアンスストーリーボードは `path: "media_buy_status"` をアサートします——レガシーの `status` のみを出す 3.1 セラーはスキーマ上有効ですが認定に失敗します。ストーリーボードが拘束的な適合性チェックです。 完全な移行: [移行 › `media_buy_status`](/docs/reference/migration/media-buy-status)。 ## 関連ドキュメント * [Media Buy Lifecycle](/docs/media-buy/media-buys/) - キャンペーンの完全なワークフロー * [get\_products](/docs/media-buy/task-reference/get_products) - インベントリの発見 * [Targeting](/docs/media-buy/advanced-topics/targeting) - ターゲティング戦略 * [Pricing Models](/docs/media-buy/advanced-topics/pricing-models) - 通貨と価格設定 # get_media_buy_delivery Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/task-reference/get_media_buy_delivery get_media_buy_delivery タスク — 稼働中の AdCP キャンペーンについてインプレッション、消化額、ペーシング、ディメンション別内訳を取得します。カスタム日付範囲とメトリクスのフィルタリングをサポートします。 メディアバイのレポーティングに必要な配信メトリクスとパフォーマンスデータを取得します。 **応答時間**: 約 60 秒(レポーティングクエリ) ## スコープ `get_media_buy_delivery` は、基盤となるキャンペーンがどう作成されたかに関わらず、[`get_media_buys`](/docs/media-buy/task-reference/get_media_buys) が返す任意の `media_buy_id` で動作します。セラーエージェントは、購入が AdCP の外で発生したことを理由に、配信レポートを拒否したり、そのカバレッジを狭めたりしてはなりません(MUST NOT)。ある購入の配信データが本当に利用できない場合(例: アドサーバーがまだフライトを報告していない)、セラーはその購入をゼロまたは部分的なメトリクスとともに `media_buy_deliveries` で返します。セラーはそれを省略せず、アカウントが所有する購入に対して `MEDIA_BUY_NOT_FOUND` を返しません。 **リクエストスキーマ**: [`/schemas/v3/media-buy/get-media-buy-delivery-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-media-buy-delivery-request.json) **レスポンススキーマ**: [`/schemas/v3/media-buy/get-media-buy-delivery-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-media-buy-delivery-response.json) ## リクエストパラメーター | Parameter | Type | Required | Description | | -------------------------- | -------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `account` | [account-ref](/docs/building/by-layer/L2/accounts-and-agents#account-references) | No | アカウント参照。`{ "account_id": "..." }` または、セラーが暗黙的な解決をサポートする場合は `{ "brand": {...}, "operator": "..." }` を渡します。このアカウントに属するメディアバイのみを返します。省略時は、アクセス可能なすべてのアカウントにわたるデータを返します。 | | `media_buy_ids` | string\[] | No\* | 取得するメディアバイ ID の配列 | | `status_filter` | string \| string\[] | No | ステータスフィルター: `"pending_creatives"`、`"pending_start"`、`"active"`、`"paused"`、`"completed"`。省略時は `["active"]` がデフォルト。 | | `start_date` | string | No | レポート開始日 (YYYY-MM-DD)、**含む**。キャンペーン全期間のデータを得るには省略します。プロダクトが `date_range` をサポートする場合のみ受け付けられます。 | | `end_date` | string | No | レポート終了日 (YYYY-MM-DD)、**含まない**。キャンペーン全期間のデータを得るには省略します。プロダクトが `date_range` をサポートする場合のみ受け付けられます。 | | `reporting_dimensions` | object | No | `by_package` 内のディメンション別内訳を要求します。デフォルトで有効化するには、キーを空オブジェクトとして含めます(例: `"device_type": {}`)。キー: `geo`、`device_type`、`device_platform`、`audience`、`placement`。各キーは任意の `limit`(geo・audience・placement は既定 25)と `sort_by`(sort-metric 列挙、既定: `spend`)を受け付けます。geo は `geo_level`(リクエストごとに一つ)が必要で、特定のシステムを要求する場合は metro/postal レベルで `system` を含めます。サポートされないディメンションは黙って省略され、不正なリクエストは検証エラーを返します。 | | `time_granularity` | string | No | プル復旧のためのウィンドウごとのスライス粒度。`reporting_webhook.reporting_frequency` の語彙(`hourly`、`daily`、`monthly`)に一致します。設定すると、レスポンスは同じ粒度の Webhook 発火と形状が揃った `windows[]` スライスを含みます。ケイパビリティでスコープされます——値はプロダクトの `reporting_capabilities.windowed_pull_granularities` に含まれていなければなりません(MUST)。[ウィンドウ化プル復旧](#windowed-pull-recovery)参照。 | | `include_window_breakdown` | boolean | No | `true`(かつ `time_granularity` が設定されている)とき、各メディアバイに `windows[]` 配列を含めます。既定は `false`。`time_granularity` が省略された場合は無視されます。 | > **日付範囲の挙動**: 日付範囲は**開始を含み、終了を含みません**。例えば `start_date: "2026-01-01"`、`end_date: "2026-01-02"` は 1 月 1 日のみ(`2026-01-01 00:00:00` から `2026-01-02 00:00:00` の直前まで)の配信データを返します。1 週間分(1/1〜1/7)を得るには `end_date: "2026-01-08"` を使います。 **日付範囲の例**: | start\_date | end\_date | Data Returned | | ------------ | ------------ | ---------------- | | `2026-01-01` | `2026-01-02` | 1 月 1 日のみ(1 日) | | `2026-01-01` | `2026-01-08` | 1 月 1 日〜7 日(7 日) | | `2026-01-01` | `2026-02-01` | 1 月全体(31 日) | | `2026-01-15` | `2026-01-16` | 1 月 15 日のみ(1 日) | \*`media_buy_ids` は結果を特定のメディアバイに絞り込みます。いずれも指定しない場合、現在のセッションコンテキスト内のすべてのメディアバイを返します。 ## レスポンス 集計とメディアバイごとの内訳を含む配信レポートを返します。 | Field | Description | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `reporting_period` | レポート対象期間(開始/終了タイムスタンプ) | | `currency` | ISO 4217 通貨コード (USD, EUR, GBP など) | | `attribution_window` | アトリビューション手法: `post_click` と `post_view`(期間オブジェクト)、および `model`(last\_touch、first\_touch、linear、time\_decay、data\_driven) | | `aggregated_totals` | すべてのメディアバイを合算したメトリクス(impressions, spend, clicks, views, completed\_views, conversions, conversion\_value, roas, new\_to\_brand\_rate, cost\_per\_acquisition, completion\_rate, reach, reach\_unit, frequency, media\_buy\_count, metric\_aggregates) | | `media_buy_deliveries` | メディアバイごとの配信データ配列 | ### Media Buy Delivery オブジェクト **3.1 の語彙に関する注記。** `get_media_buy_delivery` はライフサイクル状態をネストした `media_buy_deliveries[].status` フィールドで返します(深さ 1 にネストされているためエンベロープとの衝突なし)。`create_media_buy` と `update_media_buy` の成功レスポンスは、同じライフサイクル状態をトップレベルの **`media_buy_status`** フィールドで返します(エンベロープのタスクステータス `status` との衝突を避けるため 3.1 で追加)。同じ列挙で、3.1 では二つのフィールド名——このカスケードは 4.0 で統一されます([#4905](https://github.com/adcontextprotocol/adcp/issues/4905))。全体像は[移行 › `media_buy_status`](/docs/reference/migration/media-buy-status)を参照してください。 | Field | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `media_buy_id` | メディアバイ ID | | `status` | 現在のステータス(`pending_creatives`、`pending_start`、`active`、`paused`、`completed`)。Webhook のコンテキストでは `reporting_delayed` や `failed` にもなりうる。`create_media_buy` / `update_media_buy` の成功レスポンスの `media_buy_status` に対応します(上記 3.1 の語彙注記)。 | | `totals` | 集計メトリクス(impressions, spend, clicks, ctr, conversions, conversion\_value, roas, new\_to\_brand\_rate) | | `by_package` | delivery\_status、paused 状態、pacing\_index を含むパッケージレベルの内訳 | | `daily_breakdown` | 日別配信(date, impressions, spend, conversions, conversion\_value, roas, new\_to\_brand\_rate) | 完全なフィールド一覧は[スキーマ](https://adcontextprotocol.org/schemas/v3/media-buy/get-media-buy-delivery-response.json)を参照してください。 ### 最終値と暫定値 配信行は、**その計測ウィンドウについて最終**であるか、そうでないかのいずれかです。最終とは、セラーがその期間についてこれらの数値を確定——これ以上の改訂なし——とみなし、購入が作成された際の `measurement_terms.billing_measurement` に従って請求する意思があることを意味します。それ以外はすべて暫定です: 計測が成熟するにつれてまだ落ち着いていく途中であり(放送の C3 → C7 の DVR 累積、IVT 除去後、コンバージョンの重複排除)、請求の信頼できる情報源では**ありません**。 行ごとのシグナル: * `media_buy_deliveries[*].is_final` と `media_buy_deliveries[*].finalized_at` — 行レベルの最終性。行内のすべてのパッケージが同じ計測ウィンドウについて最終である場合にのみ true。 * `media_buy_deliveries[*].by_package[*].is_final` と `.finalized_at` — 正確なタイムスタンプを伴うパッケージレベルの最終性。 * `media_buy_deliveries[*].by_package[*].measurement_window` — 数値がどの成熟ステージを表すか(`c3`、`c7`、`post_sivt`、`downloads_30d`、…)。 `is_final` が false(または不在)の行をペーシングやレポートに使う呼び出し元は安全です。照合、未払計上、財務クローズに使う呼び出し元は安全ではありません。 ### 請求について誰が権威的か どの数値が購入を請求するかは**契約条件**であり、購入の [`measurement_terms.billing_measurement`](/docs/media-buy/advanced-topics/billing-authority) で宣言されます: * **セラー証明**(`billing_measurement` が不在、またはセラー自身のアドサーバーを指名する場合の既定): `get_media_buy_delivery` の最終行に基づいて請求します。 * **ベンダー証明**(購入で指名された第三者計測ベンダー——例: Nielsen、IAS、DV、MOAT): 指名されたベンダーの権威ある数値に基づいて請求します。運用上は、セラーがベンダーから取得して `is_final: true` を伴って `get_media_buy_delivery` で公開するのが最も一般的です。バイヤーがベンダーとの関係を保持する場合は、バイヤーが `final: true` と `finalized_at` を設定して [`report_usage`](/docs/accounts/tasks/report_usage) でプッシュします。 * **バイヤー証明**(購入で指名されたバイヤーの 3PAS や MMP——例: CM360、Flashtalking): `report_usage` でプッシュされたバイヤーの最終記録に基づいて請求します。 権威ある当事者が `measurement_terms.billing_measurement.finalization_deadline_hours` 以内に最終値を公開しない場合、相手方は自身の証明にフォールバックしてよい(MAY)。この違反は `makegood_policy` の下で扱われます。この期限は `vendor` に指名されたどちらの当事者にも対称的に適用されます。当事者間の差異が `max_variance_percent` を超える場合、当事者は購入の [`makegood_policy.available_remedies`](/docs/media-buy/advanced-topics/accountability) と帯域外の交渉で解決します。 エンドツーエンドのフローは[請求の権威](/docs/media-buy/advanced-topics/billing-authority)を参照してください。構造化された紛争タスク——ワイヤー上で配信の紛争を開始・遷移・解決する——は AdCP 3.2 を目標としています。 ### 集計メトリクスのパーティション(`metric_aggregates`) **qualifier**(計測標準、透明性の開示)によって変わる購入横断の配信値は、フラットなスカラーではなく `aggregated_totals.metric_aggregates` のパーティション化された配列として報告されます。これは集計レイヤーでの「リンゴとオレンジの合計」問題を解決します: MRC と GroupM のビューアビリティは実質的に異なる閾値を定義しており、単一のレートに合算してはなりません。 各行は `package.committed_metrics` と `by_package[].missing_metrics` と同じ原子単位を運びます: ``` committed_metrics row : { scope, metric_id, qualifier, committed_at } missing_metrics row : { scope, metric_id, qualifier } metric_aggregates row : { scope, metric_id, qualifier, value, ...components } ``` **照合は `(scope, metric_id, qualifier)` による行レベルの結合です。** 各 `committed_metrics` 行について、一致する `metric_aggregates` 行を見つけます。一致しないものは `missing_metrics` として現れます。メトリクスごとの照合ロジックも、契約と配信の間のトラバーサルの非対称性も不要です。 **粒度のルール。** `(metric_id, qualifier セット全体)` ごとに 1 行で、利用可能な最も細かい粒度で報告します。より粗いビューが欲しいバイヤーは再集計します。これによりロールアップの曖昧さがなくなり、偶発的な二重計上を防げます。 **qualifier のないメトリクスはトップレベルのまま。** `impressions`、`spend`、`media_buy_count`、その他 qualifier を持たないメトリクスは `aggregated_totals` のトップに残ります。`metric_aggregates` は qualifier セットが空でないメトリクスにのみ使われます。 **相互排他(MUST)。** `metric_aggregates` に現れる任意の `metric_id` について、`aggregated_totals` の対応するトップレベルのスカラーは省略されなければなりません——ゼロにするのではありません。セラーは両方を出してはなりません(MUST NOT)。信頼できる情報源の重複を避けます。 **qualifier の語彙**は、今日 `committed_metrics` と `metric_aggregates` の両方で閉じています(`additionalProperties: false`)。五つのキーが存在します: `viewability_standard`(MRC vs GroupM のビューアビリティ)、`completion_source`(セラー証明 vs ベンダー証明の完了)、`attribution_methodology`(deterministic\_purchase / probabilistic / panel\_based / modeled——成果メトリクス向け)、`attribution_window`(構造化された期間——成果メトリクス向け)、`lift_dimension`(awareness / consideration / favorability / purchase\_intent / ad\_recall——`brand_lift` 向け)。配信の語彙は、バイヤーがコミットしない透明性の開示が配信専用で出荷されるため、将来のマイナーで**契約から乖離することが見込まれます**(例: #3832 保留中の `tracker_firing`)。新しい qualifier キーは、いずれのサーフェスでも後続のマイナーで明示的に出荷されます。**異種の値型**: qualifier の値はほとんどが文字列の列挙ですが、`attribution_window` はオブジェクト値の期間です。コンシューマーは値の形状を知るためにキー名でディスパッチしなければならず(MUST)、構造化値の qualifier は正準(キーでソート)の深い等価性で結合します。 **レポート間での qualifier セットのドリフト。** キャンペーンがフライト中に新しい qualifier を獲得した場合(例: 1 週目はクライアントサイドの発火のみで、2 週目に `tracker_firing` のパーティションを追加)、以前の期間の行は元の粒度で有効なままです。バイヤーは遡って再パーティションすべきではありません(SHOULD NOT)。`supersedes_window` によるレポートの置き換えが、ウィンドウレベルの改訂について文書化された経路です。 **購入ごとの `totals` の形状はフラットのまま。** 個々の購入は定義上シングル qualifier です。qualifier をまたぐのは購入横断の集計だけであり、パーティション化された形状を必要とします。購入ごとの `totals.viewability` は、独自の `standard` フィールドを持つフラットなオブジェクトのままです。 例: ```json theme={null} { "aggregated_totals": { "impressions": 1000000, "spend": 5000.00, "media_buy_count": 3, "metric_aggregates": [ { "scope": "standard", "metric_id": "viewable_rate", "qualifier": { "viewability_standard": "mrc" }, "value": 0.7286, "measurable_impressions": 700000, "viewable_impressions": 510000 }, { "scope": "standard", "metric_id": "viewable_rate", "qualifier": { "viewability_standard": "groupm" }, "value": 0.55, "measurable_impressions": 180000, "viewable_impressions": 99000 } ] } } ``` **値の型ディスパッチ。** バイヤーエージェントは算術を行う前に `metric_id` を検査しなければなりません(MUST)。レートメトリクス(`viewable_rate`、`completion_rate`)は 0.0〜1.0、cost-per メトリクスは通貨、カウントメトリクスは非負の数値、ROAS は比率です。`committed_metrics` と同じディスパッチの慣習で、`metric_id` が型タグです。 ## よくあるシナリオ ### 単一のメディアバイ ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { GetMediaBuyDeliveryResponseSchema } from "@adcp/sdk"; // 単一メディアバイの配信レポートを取得 const result = await testAgent.getMediaBuyDelivery({ media_buy_ids: ["mb_12345"], start_date: "2024-02-01", end_date: "2024-02-07" }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = GetMediaBuyDeliveryResponseSchema.parse(result.data); // 判別共用体レスポンスのエラーを確認 if ("errors" in validated && validated.errors) { throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`); } console.log( `Delivered ${validated.aggregated_totals.impressions.toLocaleString()} impressions` ); console.log(`Spend: $${validated.aggregated_totals.spend.toFixed(2)}`); if (validated.media_buy_deliveries.length > 0) { console.log( `CTR: ${(validated.media_buy_deliveries[0].totals.ctr * 100).toFixed(2)}%` ); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import GetMediaBuyDeliveryRequest async def main(): # 単一メディアバイの配信レポートを取得 result = await test_agent.get_media_buy_delivery( GetMediaBuyDeliveryRequest( media_buy_ids=['mb_12345'], start_date='2024-02-01', end_date='2024-02-07' ) ) # 判別共用体レスポンスのエラーを確認 if hasattr(result, 'errors') and result.errors: raise Exception(f"Query failed: {result.errors}") print(f"Delivered {result.aggregated_totals.impressions:,} impressions") print(f"Spend: ${result.aggregated_totals.spend:.2f}") if result.media_buy_deliveries: print(f"CTR: {result.media_buy_deliveries[0].totals.ctr * 100:.2f}%") asyncio.run(main()) ``` ### 複数のメディアバイ ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { GetMediaBuyDeliveryResponseSchema } from "@adcp/sdk"; // コンテキスト内のアクティブなメディアバイをすべて取得 const result = await testAgent.getMediaBuyDelivery({ status_filter: "active", start_date: "2024-02-01", end_date: "2024-02-07" }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = GetMediaBuyDeliveryResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`); } console.log(`${validated.aggregated_totals.media_buy_count} active campaigns`); console.log( `Total impressions: ${validated.aggregated_totals.impressions.toLocaleString()}` ); console.log(`Total spend: $${validated.aggregated_totals.spend.toFixed(2)}`); // キャンペーンごとに確認 validated.media_buy_deliveries.forEach((delivery) => { console.log( `${delivery.media_buy_id}: ${delivery.totals.impressions.toLocaleString()} impressions, CTR ${(delivery.totals.ctr * 100).toFixed(2)}%` ); }); ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import GetMediaBuyDeliveryRequest async def main(): # コンテキスト内のアクティブなメディアバイをすべて取得 result = await test_agent.get_media_buy_delivery( GetMediaBuyDeliveryRequest( status_filter='active', start_date='2024-02-01', end_date='2024-02-07' ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Query failed: {result.errors}") print(f"{result.aggregated_totals.media_buy_count} active campaigns") print(f"Total impressions: {result.aggregated_totals.impressions:,}") print(f"Total spend: ${result.aggregated_totals.spend:.2f}") # キャンペーンごとに確認 for delivery in result.media_buy_deliveries: print(f"{delivery.media_buy_id}: {delivery.totals.impressions:,} impressions, CTR {delivery.totals.ctr * 100:.2f}%") asyncio.run(main()) ``` ### 日付範囲でのレポート ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { GetMediaBuyDeliveryResponseSchema } from "@adcp/sdk"; // 月初から現在までのパフォーマンスを取得 const now = new Date(); const monthStart = new Date(now.getFullYear(), now.getMonth(), 1); const dateFormat = (date) => date.toISOString().split("T")[0]; const result = await testAgent.getMediaBuyDelivery({ media_buy_ids: ["mb_12345"], start_date: dateFormat(monthStart), end_date: dateFormat(now) }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = GetMediaBuyDeliveryResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`); } if (validated.media_buy_deliveries.length > 0) { // 日別内訳を分析 const dailyBreakdown = validated.media_buy_deliveries[0].daily_breakdown; if (dailyBreakdown && dailyBreakdown.length > 0) { console.log( `Daily average: ${Math.round(validated.aggregated_totals.impressions / dailyBreakdown.length).toLocaleString()} impressions` ); // ピーク日を特定 const peakDay = dailyBreakdown.reduce((max, day) => day.impressions > max.impressions ? day : max ); console.log( `Peak day: ${peakDay.date} with ${peakDay.impressions.toLocaleString()} impressions` ); } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import GetMediaBuyDeliveryRequest from datetime import date async def main(): # 月初から現在までのパフォーマンスを取得 today = date.today() month_start = date(today.year, today.month, 1) result = await test_agent.get_media_buy_delivery( GetMediaBuyDeliveryRequest( media_buy_ids=['mb_12345'], start_date=str(month_start), end_date=str(today) ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Query failed: {result.errors}") if result.media_buy_deliveries: # 日別内訳を分析 daily_breakdown = result.media_buy_deliveries[0].daily_breakdown if daily_breakdown: daily_avg = result.aggregated_totals.impressions // len(daily_breakdown) print(f"Daily average: {daily_avg:,} impressions") # ピーク日を特定 peak_day = max(daily_breakdown, key=lambda d: d.impressions) print(f"Peak day: {peak_day.date} with {peak_day.impressions:,} impressions") asyncio.run(main()) ``` ### 複数ステータスの取得 ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { GetMediaBuyDeliveryResponseSchema } from "@adcp/sdk"; // アクティブと一時停止中のキャンペーンを取得 const result = await testAgent.getMediaBuyDelivery({ status_filter: ["active", "paused"], start_date: "2024-02-01", end_date: "2024-02-07" }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = GetMediaBuyDeliveryResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`); } // ステータス別にグループ化 const byStatus = validated.media_buy_deliveries.reduce((acc, delivery) => { if (!acc[delivery.status]) acc[delivery.status] = []; acc[delivery.status].push(delivery); return acc; }, {}); console.log(`Active campaigns: ${byStatus.active?.length || 0}`); console.log(`Paused campaigns: ${byStatus.paused?.length || 0}`); // パフォーマンスが低いキャンペーンを特定 byStatus.paused?.forEach((delivery) => { if (delivery.by_package && delivery.by_package.length > 0) { const avgPacing = delivery.by_package.reduce((sum, pkg) => sum + pkg.pacing_index, 0) / delivery.by_package.length; console.log( `${delivery.media_buy_id}: paused with ${(avgPacing * 100).toFixed(0)}% pacing` ); } }); ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import GetMediaBuyDeliveryRequest from collections import defaultdict async def main(): # アクティブと一時停止中のキャンペーンを取得 result = await test_agent.get_media_buy_delivery( GetMediaBuyDeliveryRequest( status_filter=['active', 'paused'], start_date='2024-02-01', end_date='2024-02-07' ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Query failed: {result.errors}") # ステータス別にグループ化 by_status = defaultdict(list) for delivery in result.media_buy_deliveries: by_status[delivery.status].append(delivery) print(f"Active campaigns: {len(by_status['active'])}") print(f"Paused campaigns: {len(by_status['paused'])}") # パフォーマンスが低いキャンペーンを特定 for delivery in by_status['paused']: if delivery.by_package: avg_pacing = sum(pkg.pacing_index for pkg in delivery.by_package) / len(delivery.by_package) print(f"{delivery.media_buy_id}: paused with {avg_pacing * 100:.0f}% pacing") asyncio.run(main()) ``` ### Buyer Reference での取得 ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { GetMediaBuyDeliveryResponseSchema } from "@adcp/sdk"; // メディアバイ ID の代わりに buyer reference で検索 const result = await testAgent.getMediaBuyDelivery({ media_buy_ids: ["acme_q1_campaign_2024", "acme_q1_retargeting_2024"] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = GetMediaBuyDeliveryResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`); } console.log( `Total lifetime impressions: ${validated.aggregated_totals.impressions.toLocaleString()}` ); console.log( `Total lifetime spend: $${validated.aggregated_totals.spend.toFixed(2)}` ); validated.media_buy_deliveries.forEach((delivery) => { if (delivery.totals.impressions > 0) { const cpm = (delivery.totals.spend / delivery.totals.impressions) * 1000; console.log( `${delivery.media_buy_id}: CPM $${cpm.toFixed(2)}, CTR ${(delivery.totals.ctr * 100).toFixed(2)}%` ); } }); ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import GetMediaBuyDeliveryRequest async def main(): # メディアバイ ID の代わりに buyer reference で検索 result = await test_agent.get_media_buy_delivery( GetMediaBuyDeliveryRequest( media_buy_ids=['acme_q1_campaign_2024', 'acme_q1_retargeting_2024'] ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Query failed: {result.errors}") # ライフタイム配信データ(日付範囲を指定しない場合) print(f"Total lifetime impressions: {result.aggregated_totals.impressions:,}") print(f"Total lifetime spend: ${result.aggregated_totals.spend:.2f}") # キャンペーン比較 for delivery in result.media_buy_deliveries: if delivery.totals.impressions > 0: cpm = (delivery.totals.spend / delivery.totals.impressions) * 1000 print(f"{delivery.media_buy_id}: CPM ${cpm:.2f}, CTR {delivery.totals.ctr * 100:.2f}%") asyncio.run(main()) ``` ### アカウントスコープでの取得 ```javascript JavaScript test=false theme={null} import { testAgent } from '@adcp/sdk/testing'; import { GetMediaBuyDeliveryResponseSchema } from '@adcp/sdk'; // Get delivery for a specific advertiser account const result = await testAgent.getMediaBuyDelivery({ account: { account_id: 'acc_acme_pinnacle' }, status_filter: 'active', start_date: '2024-02-01', end_date: '2024-02-07' }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = GetMediaBuyDeliveryResponseSchema.parse(result.data); if ('errors' in validated && validated.errors) { throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`); } console.log(`${validated.aggregated_totals.media_buy_count} campaigns for account`); console.log(`Total spend: $${validated.aggregated_totals.spend.toFixed(2)}`); ``` ```python Python test=false theme={null} import asyncio from adcp.testing import test_agent from adcp.types import GetMediaBuyDeliveryRequest async def main(): # Get delivery for a specific advertiser account result = await test_agent.get_media_buy_delivery( GetMediaBuyDeliveryRequest( account={'account_id': 'acc_acme_pinnacle'}, status_filter='active', start_date='2024-02-01', end_date='2024-02-07' ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Query failed: {result.errors}") print(f"{result.aggregated_totals.media_buy_count} campaigns for account") print(f"Total spend: ${result.aggregated_totals.spend:.2f}") asyncio.run(main()) ``` ## メトリクスの定義 | Metric | Definition | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Impressions** | 広告が表示された回数 | | **Spend** | 指定通貨での支出額 | | **Clicks** | 広告クリック数(利用可能な場合) | | **CTR** | クリック率 (clicks/impressions) | | **Views** | 課金対象の視聴閾値でのコンテンツエンゲージメント——動画視聴、オーディオ/ポッドキャストのストリーム開始、またはフォーマット固有の視聴イベント | | **Completed Views** | オーディオ/動画の完了(閾値または 100%) | | **Completion Rate** | 完了率 (completed\_views/impressions) | | **Conversions** | 帰属コンバージョン(購入、新規リスナー、アプリインストールなど) | | **Conversion Value** | 帰属コンバージョンの総金銭的価値 | | **ROAS** | 広告費用対効果 (conversion\_value / spend) | | **New-to-Brand Rate** | 初回購入者によるコンバージョンの割合 (0-1) | | **Cost per Acquisition** | コンバージョンあたりコスト (spend / conversions) | | **Reach** | リーチしたユニークユーザー数(計測単位は `reach_unit` を参照: individuals, households, devices, accounts, cookies)。計測ウィンドウは `reach_window` で宣言。それがない場合、バイヤーは行をまたいでリーチを合計してはなりません。 | | **Reach Unit** | リーチの計測単位——reach が存在する場合は必須 | | **Reach Window** | 報告されるリーチ/フリークエンシーのウィンドウ意味論: `cumulative`(キャンペーン開始以降のユニーク)、`period`(重複しない単一レポート期間内のユニーク——例: 日次スナップショット)、`rolling`(後方ウィンドウ内のユニーク——例: 直近 7 日)。行をまたいで合計しないこと。任意だが、reach が存在する場合は強く推奨。 | | **Frequency** | `reach_window` にわたって計測された、リーチ単位あたりの平均広告接触回数 | | **Viewability** | `vendor`、`measurable_impressions`(分母)、`viewable_impressions`、`viewable_rate`、`viewed_seconds`(計測可能インプレッションあたりの平均インビュー時間——`viewed_seconds` 最適化目標と対)、`standard` を持つオブジェクト | | **Follows** | 配信に帰属する新規フォロワー、ページのいいね、無料のチャンネル/フィード購読 | | **Pacing Index** | 実績 vs 期待の配信速度 (1.0 = 計画通り、\<1.0 = 遅れ、>1.0 = 先行) | | **CPM** | インプレッション 1,000 件あたりのコスト (spend/impressions \* 1000) | ## クエリの挙動 ### コンテキストベースのクエリ * `media_buy_ids` を指定しない場合、現在のセッションコンテキスト内のすべてのメディアバイを返す * コンテキストは `create_media_buy` など直前の操作で確立されます ### ステータスフィルター * 未指定時はデフォルトで `["active"]` * 単一文字列 (`"active"`) でも配列 (`["active", "paused"]`) でも指定可能 * 有効なフィルター値はメディアバイのライフサイクルステータス: `pending_creatives`、`pending_start`、`active`、`paused`、`completed` * `reporting_delayed` と `failed` は Webhook のコンテキストで返される配信/レポートのステータスであり、リクエストのフィルター値ではありません * 一部のレガシー統合は `pending` を出しうる。`pending_start` と同等として扱ってください ### 日付範囲 * 日付未指定の場合はキャンペーン全期間の配信データを返す * `start_date` と `end_date` は両方セットで指定しなければなりません——部分的な日付範囲は無効です * 日付形式: `YYYY-MM-DD` * **開始を含み、終了を含まない**: `start_date` は含まれ、`end_date` は除外されます。例えば `start_date: "2026-01-01"`、`end_date: "2026-01-02"` は 1 月 1 日のみのデータを返します。 * プロダクトは `reporting_capabilities.date_range_support` で日付範囲のサポートを宣言します * `date_range_support: "lifetime_only"` のプロダクトは、`start_date`/`end_date` を含むリクエストを `DATE_RANGE_NOT_SUPPORTED` エラーで拒否します * `date_range_support: "date_range"` のプロダクトは日付パラメータを受け付け、配信データをそれに応じてフィルタリングします * 長期範囲ではレスポンスサイズ削減のため日別内訳が間引かれる場合があります ### メトリクスの有無 * **共通**: Impressions, spend(すべてのプラットフォームで利用可能) * **フォーマット依存**: Clicks, completed\_views, completion\_rate(在庫タイプとプラットフォーム能力に依存) * **オーディエンス**: Reach, frequency(重複排除された計測を持つプラットフォームで利用可能) * **コマースアトリビューション**: Conversions, conversion\_value, roas, new\_to\_brand\_rate(コマースメディアとストリーミングのプラットフォームで利用可能) * **エンゲージメント**: Follows, saves, engagements, profile\_visits(ソーシャルとストリーミングのプラットフォームで利用可能) * **アトリビューションウィンドウ**: `attribution_window` は、コンバージョンアトリビューションに使われるルックバックウィンドウとモデルを表します(例: 14 日クリック、1 日ビュー、last\_touch) * **パッケージレベル**: すべてのメトリクスが `by_package` で pacing\_index とともに提供 ## データ鮮度 * レポートデータは通常 2〜4 時間の遅延があります * リアルタイムのインプレッションカウントは提供されない * ライブモニタリングではなく、定期レポートや最適化判断に利用します **段階的成熟のチャネル**: 課金グレードのデータが初日に最終値として届くのではなく段階的に生成されるチャネルでは、データ鮮度が異なります——放送 TV(Live → C3 → C7 の DVR 累積、最終 C7 は放送後約 15〜22 日)、DOOH(暫定の再生 → IVT/不正チェック後の最終)、IVT フィルタリング付きデジタル(raw → post-GIVT → post-SIVT)、ポッドキャスト(7 日 → 30 日ダウンロード)。`reporting_capabilities.measurement_windows` を持つプロダクトがこれらのタイムラインを宣言します。バイヤーは、合意された条件の `billing_measurement` で指定された `measurement_window` に対して照合します。計測条件は [Accountability](/docs/media-buy/advanced-topics/accountability) を、ライフサイクル全体は[最適化とレポーティング](/docs/media-buy/media-buys/optimization-reporting)を参照してください。 ## エラーハンドリング | Error Code | Description | Resolution | | -------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `AUTH_MISSING` | No credentials presented | Provide credentials via auth header | | `AUTH_INVALID` | Credentials rejected (expired / revoked) | Human credential rotation required; do not auto-retry | | `MEDIA_BUY_NOT_FOUND` | Media buy doesn't exist | Verify `media_buy_id`; for legacy correlation use `get_media_buys` + `context.internal_campaign_id` | | `INVALID_DATE_RANGE` | Invalid start/end dates | Use YYYY-MM-DD format, ensure start \< end | | `DATE_RANGE_NOT_SUPPORTED` | Product only supports lifetime reporting | Omit `start_date` and `end_date`. Check `reporting_capabilities.date_range_support` on the product. | | `UNSUPPORTED_GRANULARITY` | Requested `time_granularity` is not in the product's `windowed_pull_granularities` | Re-issue with a granularity from `error.details.supported_granularities`, or omit `time_granularity` to fall back to cumulative date-range pulls. See [Windowed pull recovery](#windowed-pull-recovery). | | `CONTEXT_REQUIRED` | No media buys in context | Provide media\_buy\_ids explicitly | | `INVALID_STATUS_FILTER` | Invalid status value | Use valid status: pending\_creatives, pending\_start, active, paused, completed | ## パッケージレベルのメトリクス `by_package` 配列はパッケージごとの配信詳細を次の主要フィールドとともに提供します。 **Buyer Control**: * **`paused`**: パッケージがバイヤーによって一時停止されているか(true/false) **System State**: * **`delivery_status`**: システムが報告する動作状態: * `delivering` - パッケージが配信中 * `completed` - 正常終了 * `budget_exhausted` - 予算を使い切った * `flight_ended` - 終了日に到達 * `goal_met` - インプレッション/コンバージョン目標を達成 **Performance**: * **`pacing_index`**: 配信ペース(1.0 = 計画通り、1.0 未満 = 遅れ、1.0 超 = 先行) * **`rate`**: 実効価格(例: CPM) * **`pricing_model`**: 課金モデル (cpm, cpcv, cpp など) **Accountability**: * **`missing_metrics`**: 拘束的なレポート契約が宣言していたが、このレポートで埋められていないメトリクス。各エントリは明示的な `scope` 判別子を使います: 閉じた `available-metric.json` 列挙由来のエントリには `{ "scope": "standard", "metric_id": "completed_views" }`、ベンダー定義メトリクスには `{ "scope": "vendor", "vendor": { "domain": "..." }, "metric_id": "attention_units" }`。標準エントリは `committed_metrics` の qualifier を反映する `qualifier` を運んでよい(MAY。例: `{ "scope": "standard", "metric_id": "viewable_rate", "qualifier": { "viewability_standard": "mrc" } }` は、GroupM のビューアビリティが報告されていても MRC のコミット欠如をフラグし、`{ "scope": "standard", "metric_id": "completion_rate", "qualifier": { "completion_source": "vendor_attested" } }` は、セラー証明の完了が報告されていてもベンダー証明のコミット欠如をフラグします——これらの経路は交換可能ではありません)。存在する場合は `package.committed_metrics`(`committed_at < reporting_period.end` のエントリにフィルタ)に対して照合され、存在しない場合はプロダクトの現在の `reporting_capabilities.available_metrics` と `vendor_metrics` にフォールバックします。空配列(または不在)は契約に対するクリーンな配信を示し、空でない場合はアカウンタビリティの違反を示します。セラーは、現在の `measurement_window` でまだ計測できないメトリクス(例: live ウィンドウ中の post-IVT カウント)を除外しなければなりません(MUST)——それらは、より広いウィンドウが `supersedes_window` でこのレポートを置き換えるときに現れます(または現れません)。 * **`vendor_metric_values`**: プロダクトの `reporting_capabilities.vendor_metrics` が宣言したベンダー定義メトリクスの報告値(独自のアテンション、排出量、パネルのデモグラフィック、ブランドリフト調査など)。各エントリは `{ vendor, metric_id, value, unit?, measurable_impressions?, breakdown? }` を運びます。`measurable_impressions` はカバレッジの分母です——ベンダーは自身の SDK が発火するか、パネルが一致するインプレッションのみをスコアリングするため、ベンダー計測が配信の 100% であることはまれです。バイヤーはカバレッジを `measurable_impressions / impressions` として計算します。`measurable_impressions` が不在の場合、カバレッジは未指定です——バイヤーはカバレッジ率を計算したり、完全なカバレッジを仮定したりしてはなりません(MUST NOT)。宣言されたベンダーメトリクスがこの配列から完全に省略されている場合、計測が行われなかった(統合なし)と推論してください。JIC やパネルベースの共視聴調整、クレーム照合、信頼区間、パネルサイズは、購入時のシグナルターゲティング定義ではなく、通常 `breakdown` の中でここに属します。 **重要な違い**: `paused` はバイヤーによる制御、`delivery_status` はシステム側の実態を表します。`paused` でなくても `delivery_status: "budget_exhausted"` の場合があります。 ## クリエイティブレベルのメトリクス セラーがクリエイティブレベルのレポート(レポートケイパビリティの `supports_creative_breakdown`)をサポートする場合、各パッケージにはクリエイティブごとの配信メトリクスを持つ `by_creative` 配列が含まれます。 各クリエイティブエントリは以下を含みます: * **`creative_id`**: クリエイティブ割り当てと一致するクリエイティブ識別子 * **`weight`**: レポート期間中のこのクリエイティブの配信ウェイト (0-100) * すべての標準配信メトリクス(impressions、spend、clicks、ctr など) ```json theme={null} { "by_package": [ { "package_id": "pkg_001", "spend": 5000, "impressions": 100000, "pricing_model": "cpm", "rate": 50, "currency": "USD", "delivery_status": "delivering", "by_creative": [ { "creative_id": "hero_video_30s", "weight": 60, "impressions": 60000, "spend": 3000, "clicks": 3000, "ctr": 0.05, "completion_rate": 0.72 }, { "creative_id": "hero_video_15s", "weight": 40, "impressions": 40000, "spend": 2000, "clicks": 1200, "ctr": 0.03, "completion_rate": 0.85 } ] } ] } ``` バリアントレベルの配信データ(アセットの組み合わせ最適化、生成クリエイティブ)を含むより深いクリエイティブ分析には、[`get_creative_delivery`](/docs/creative/task-reference/get_creative_delivery) を使います。これはクリエイティブプロトコルのタスクです——クリエイティブプロトコルを実装する任意のエージェントで呼び出せます。`supported_protocols` に `"creative"` を宣言していれば、同じセラーエージェントであってもよい。[セラーエージェントのクリエイティブ機能](/docs/creative/sales-agent-creative-capabilities)を参照してください。 ## カタログアイテムのレポート カタログ駆動のパッケージ(`catalog` フィールドを持つパッケージ)では、セラーは各パッケージ内の `by_catalog_item` 配列でカタログアイテムごとの配信を返せます。 各エントリはカタログアイテムを識別し、標準の配信メトリクスを含みます: | Field | Description | | ----------------- | --------------------------------------------------------------------- | | `content_id` | アイテム識別子(SKU、GTIN、求人 ID など) | | `content_id_type` | 識別子の型(`sku`、`gtin`、`job_id` など)。カタログの `content_id_type` に一致 | | Standard metrics | `impressions`、`spend`、`clicks`、`ctr`、`conversions`、`roas`、その他の配信メトリクス | これは任意です。アイテムレベルのレポートをサポートするセラーは `by_catalog_item` を埋め、しないセラーは単に省略します。 ```json theme={null} { "by_package": [ { "package_id": "pkg_001", "spend": 5000, "impressions": 100000, "pricing_model": "cpc", "rate": 1.20, "currency": "USD", "delivery_status": "delivering", "by_catalog_item": [ { "content_id": "SKU-12345", "content_id_type": "sku", "impressions": 45000, "spend": 2250, "clicks": 1800, "ctr": 0.04, "conversions": 90, "roas": 4.2 }, { "content_id": "SKU-67890", "content_id_type": "sku", "impressions": 55000, "spend": 2750, "clicks": 2200, "ctr": 0.04, "conversions": 110, "roas": 3.8 } ] } ] } ``` ## ウィンドウ化プル復旧 `reporting_webhook` はバイヤーが選んだ `reporting_frequency`(hourly、daily、monthly)で発火します。受信側がトランスポートのリトライが尽きるほど長くオフラインだった場合、GET が同じスライスを再現できない限り、バイヤーはウィンドウごとの詳細を失います。`time_granularity` + `include_window_breakdown` がそのギャップを埋めます。 ### ケイパビリティの確認 セラーは、プル復旧で honor する粒度を `reporting_capabilities.windowed_pull_granularities` で宣言します。バイヤーは `time_granularity` を要求する前にケイパビリティを確認しなければなりません(MUST): ```json test=false theme={null} { "reporting_capabilities": { "available_reporting_frequencies": ["hourly", "daily"], "windowed_pull_granularities": ["daily"] } } ``` この例のセラーは hourly の Webhook を出しますが、プルは daily のみ honor します——Webhook が Kafka のタップで、履歴のプルはウェアハウスを通るストリームタップのアーキテクチャで一般的です。二経路のパリティは**宣言された**集合で成立します。hourly の復旧では Webhook が主です。完全なパリティを望むセラーは、発火するすべての頻度を宣言します。 ### ウィンドウ化スライスの要求 ```json test=false theme={null} { "media_buy_ids": ["mb_12345"], "start_date": "2026-06-01", "end_date": "2026-06-02", "time_granularity": "hourly", "include_window_breakdown": true } ``` ### レスポンスの形状 各メディアバイはレスポンスに `windows[]` 配列を得ます: ```json test=false theme={null} { "media_buy_deliveries": [ { "media_buy_id": "mb_12345", "status": "active", "totals": { "impressions": 12345678, "spend": 5432.10 }, "by_package": [ /* cumulative per-package — unchanged */ ], "windows": [ { "window_start": "2026-06-01T00:00:00Z", "window_end": "2026-06-01T01:00:00Z", "totals": { "impressions": 510234, "spend": 226.05 }, "by_package": [ { "package_id": "pkg_001", "impressions": 510234, "spend": 226.05 } ], "is_final": true }, { "window_start": "2026-06-01T01:00:00Z", "window_end": "2026-06-01T02:00:00Z", "totals": { "impressions": 488112, "spend": 215.83 }, "is_final": true } ] } ] } ``` スライスは `window_start` の昇順で並び、連続する行は隣接します(各行の `window_end` は次の行の `window_start` と等しい)。各スライスのペイロードは、同じウィンドウについて `reporting_webhook` が配信したであろうものと形状が揃っています——見逃した Webhook を照合するバイヤーは `(media_buy_id, window_start)` で結合します。 ### 仕様上の契約 * **ケイパビリティでスコープされた MUST** — セラーは `windowed_pull_granularities` にある任意の値について `time_granularity` の要求を honor しなければなりません(MUST)。宣言された集合の外のプルは `UNSUPPORTED_GRANULARITY` を返します。 * **非対称であることは誠実** — セラーは、プル向けに公開するより高い頻度の Webhook を出してよい(MAY)。`available_reporting_frequencies: ["hourly", "daily"]` を `windowed_pull_granularities: ["daily"]` とともに宣言するのは有効です。バイヤーはその頻度では hourly の Webhook を主として扱います。 * **同一形状での復旧** — スライスのペイロードは同じ粒度の Webhook 発火のペイロードを反映するため、バイヤーの照合パイプラインはトランスポート経路で分岐しません。 このサーフェスは、データを運ぶイベントについて [snapshot-and-log](/docs/protocol/snapshot-and-log) のルール 4(どちらの経路も完全)を支えます。より広い契約はそのページを参照してください。 ## ディメンション別内訳 リクエストに `reporting_dimensions` を含めると、レスポンスは各 `by_package` エントリ内にディメンション別内訳の配列を含みます。各内訳エントリは `delivery-metrics` のすべてのフィールドに加え、ディメンション固有の識別子を継承します。 ### 内訳の要求 ```json test=false theme={null} { "media_buy_ids": ["mb_123"], "reporting_dimensions": { "geo": { "geo_level": "metro", "system": "nielsen_dma", "limit": 10 }, "device_type": {}, "placement": { "limit": 5, "sort_by": "roas" } } } ``` 各ディメンションは任意の `limit`(最大行数。geo・audience・placement は既定 25)と `sort_by`(`sort-metric` 列挙の任意の値。例: `spend`、`impressions`、`clicks`、`roas`——既定は `spend` の降順。セラーが要求されたメトリクスを報告しない場合は `spend` にフォールバック)を受け付けます。geo は `geo_level`(`country`、`region`、`metro`、`postal_area`)が必要です。特定のシステムを要求する場合は metro/postal レベルで `system` を含めます。ネイティブな郵便のリクエストは `country` も含みます(例: `{ "geo_level": "postal_area", "country": "US", "system": "zip" }`)。各リクエストは単一の geo\_level を使います——複数の粒度(例: country と region)には、別々のリクエストを行ってください。サポートされないディメンションはレスポンスから黙って省略されますが、不正なリクエスト(例: `geo_level` のない geo)は検証エラーを返します。内訳はディメンション単位のみです——ディメンション横断の交差(例: device\_type × geo)はサポートされません。 ### 利用可能なディメンション | Dimension | Breakdown field | Required fields | Additional fields | Capability declaration | | --------------- | -------------------- | -------------------------------------------------------- | ------------------------------------ | ------------------------------------ | | Geography | `by_geo` | `geo_level`, `geo_code`, `impressions`, `spend` | `system`, `country`, `geo_name` | `supports_geo_breakdown` | | Device type | `by_device_type` | `device_type`, `impressions`, `spend` | — | `supports_device_type_breakdown` | | Device platform | `by_device_platform` | `device_platform`, `impressions`, `spend` | — | `supports_device_platform_breakdown` | | Audience | `by_audience` | `audience_id`, `audience_source`, `impressions`, `spend` | `audience_name` | `supports_audience_breakdown` | | Placement | `by_placement` | `placement_id`, `impressions`, `spend` | `publisher_domain`, `placement_name` | `supports_placement_breakdown` | どのディメンションが利用可能かはプロダクトの `reporting_capabilities` で確認してください。同じセラーの異なるプロダクトが異なる内訳をサポートしうるため、プロダクトレベルのケイパビリティが権威的です。 `supports_geo_breakdown` は利用可能なレベルとシステムを宣言するオブジェクトで、この表の他のケイパビリティ宣言はブール値のフラグです。`supports_geo_breakdown` 内では、`country` と `region` はブール値で、`metro` は `metro-system` の値でキー付けされ、ネイティブな `postal_area` は ISO 3166-1 alpha-2 の国でキー付けされ、国ローカルな `postal-system` 値の配列を持ちます。geo の行は `geo_level: "metro"` と `"postal_area"` で `system` を使います。ネイティブな郵便の行は `country` も含みます。非推奨の国融合型の郵便システムは互換性のため引き続き受け付けられます。 プレースメントのアイデンティティはパブリッシャースコープです。プレースメント行は `publisher_domain`——プロダクトの `placements[]` エントリ由来のパブリッシャー名前空間——を運んでよく(MAY)、それが存在する場合、バイヤーはマルチパブリッシャープロダクトについて `{publisher_domain, placement_id}` を安定したプレースメントのアイデンティティとして扱えます。セラーは、プロダクトのプレースメントがそれを運ぶ場合は常に `publisher_domain` を出すべきです(SHOULD。`kind: "publisher_ref"` では常に真)。セラーがそれを省略してよいのは、セラーエージェント自身のドメインが名前空間であるレガシーな単一パブリッシャーの文脈における `kind: "seller_inline"` のプレースメントに限られます。`publisher_domain` が省略された場合、バイヤーはそのレガシーな単一パブリッシャーの文脈でのみ `placement_id` をセラーエージェント自身のパブリッシャードメインに対して解釈してよく(MAY)、それ以外ではパブリッシャー横断のプレースメントキーを推測すべきではありません。各プレースメントは正確に一つのパブリッシャー名前空間に属するため、`publisher_domain` は単一値です。 ### 切り詰め 各内訳配列には兄弟のブールフラグ(例: `by_geo_truncated`)があります。`true` のとき、返された集合を超える追加行が存在します。`false` のとき、リストは完全です。セラーは、対応する内訳配列が存在する場合は常に truncated フラグを返さなければなりません(MUST)。行は要求された `sort_by` メトリクスの降順で並びます。 ### オーディエンスソース `audience_source` フィールドは、オーディエンスセグメントがどこに由来するかを示します: | Source | Description | Targetable? | | ------------- | ------------------------------------- | --------------------------------------------- | | `synced` | `sync_audiences` によるバイヤーのファーストパーティデータ | はい——`audience_include`/`audience_exclude` を使用 | | `platform` | セラーのネイティブセグメント(興味、行動) | いいえ——情報提供 | | `third_party` | 外部データプロバイダーのセグメント | いいえ——情報提供 | | `lookalike` | シードからのプラットフォーム生成の拡張 | いいえ——情報提供 | | `retargeting` | セラーのピクセル/タグによる過去のエンゲージメント | いいえ——情報提供 | | `unknown` | 未分類または認識されないオーディエンスソース | いいえ——情報提供 | ## ベストプラクティス **1. 日付範囲のサポートを確認する** 日付でフィルタした配信を要求する前に、プロダクトの `reporting_capabilities.date_range_support` を確認します。`lifetime_only` のサポートを持つプロダクトは日付範囲のリクエストを拒否します——代わりに `start_date` と `end_date` を省略してキャンペーン全期間のデータを取得してください。 **2. 日付範囲を指定して分析する** 日付範囲をサポートするプロダクトでは、期間比較やトレンド分析のために日付を指定します。 **3. Pacing Index を監視する** 0.95〜1.05 を目標とし、逸脱している場合は配信問題を疑う。 **4. 日別内訳を確認する** 配信パターンや平日/週末での差分を把握します。 **5. パッケージ性能を比較する** `by_package` 内訳で最も成果の高い在庫を特定します。`paused` と `delivery_status` の両方を確認し、配信されない理由を把握します。 **6. ステータス変化を追跡する** 複数ステータスのクエリで、キャンペーンが停止/完了した理由を把握します。 ## 配信後のガバナンス検証 配信レポートは最後のステップではありません。キャンペーンガバナンスが有効な場合、配信データはガバナンス検証に流れ込み、認可されていないサプライパス、ジオのドリフト、ペーシング違反を検出します。 ガバナンスのフィードバックループ: 1. `get_media_buy_delivery` で配信データを取得 2. [`report_plan_outcome`](/docs/governance/campaign/tasks/report_plan_outcome) でガバナンスエージェントに結果を報告 3. ガバナンスエージェントが実際の配信を計画パラメータと比較(ドリフト検出) 4. [`validate_property_delivery`](/docs/governance/property/tasks/validate_property_delivery) でプロパティの配信を検証し、認可されていないサプライパスを検出 | Governance task | Purpose | | ------------------------------------------------------------------------------------------------- | ----------------------------------------------- | | [`report_plan_outcome`](/docs/governance/campaign/tasks/report_plan_outcome) | 予算追跡とドリフト検出のためにガバナンスエージェントへ配信データを供給 | | [`validate_property_delivery`](/docs/governance/property/tasks/validate_property_delivery) | 配信記録をプロパティリストに対して検証——認可されていないプロパティで配信されている広告を検出 | | [`validate_content_delivery`](/docs/governance/content-standards/tasks/validate_content_delivery) | コンテンツアーティファクトをブランド適合性の標準に対して検証 | | [`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs) | 完全なプラン状態と監査証跡を表示 | このフィードバックループがなければ、配信データは報告されても検証されません。予算超過、ペーシングの乖離、ジオのドリフト、認可されていないサプライパスが検出されないままになります。 ## 次のステップ 配信データ取得後にできること: 1. **キャンペーンを最適化**: [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) で予算、ペーシング、ターゲティングを調整 2. **フィードバックを共有**: [`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback) で結果をセラーに共有 3. **クリエイティブを更新**: ライブラリ対応のセラーには [`sync_creatives`](/docs/creative/task-reference/sync_creatives) を、インライン専用のセラーには [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) のインライン `packages[].creatives` を使用 4. **フォローアップキャンペーンを作成**: インサイトに基づき [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) を実行 ## さらに学ぶ * [Media Buy Lifecycle](/docs/media-buy/media-buys/) - キャンペーンワークフロー全体 * [Async Operations](/docs/building/by-layer/L3/async-operations) - 非同期パターンとステータス処理 * [Performance Optimization](/docs/media-buy/media-buys/optimization-reporting) - 配信データを用いた最適化 # get_media_buys Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/task-reference/get_media_buys get_media_buys タスク — クリエイティブ承認、不足アセット、設定、オプションのほぼリアルタイム配信スナップショットを含む AdCP のメディアバイステータスを取得します。 メディアバイの現在の運用状態(設定、クリエイティブ承認ステータス、不足アセット、オプションのほぼリアルタイム配信スナップショット)を取得します。 **応答時間**: 約1秒 ## 結果のスコープ セールスエージェントは、認証済みアカウントが所有するすべてのメディアバイを返さなければなりません(MUST)。そのバイがどのように作成されたか——AdCP の `create_media_buy` 経由、セラー自身の API 経由、手動トラフィッキング経由、レガシーまたはサードパーティのシステム経由——は問いません。スコープは**アカウントの所有権**であり、作成のサーフェスではありません。ここで返される `media_buy_id` は、認証済みの呼び出し元がアクセスできるセラーのアドサーバー上の任意のオーダーを識別します。 `get_media_buys` が返すメディアバイはすべて、その `valid_actions` にあるすべてのタスクから到達可能でなければなりません(MUST)。セールスエージェントは、元々 AdCP 経由で作成されたものではないことを理由に、バイを読み取り専用としたり、隠したり、更新を拒否したりしてはなりません(MUST NOT)。ビジネス上の理由(契約上の義務、プラットフォームの制約、ポリシー)でアクションが利用できない場合、セラーはそのアクションのみを `valid_actions` から省略しなければなりません(MUST)——セット全体を省略してはならず、単に AdCP の外で作成されたという理由だけで省略してもなりません。AdCP 外のバイを体系的に空の `valid_actions` で返すセラーは非準拠です。そのパターンは、バイを隠しているのと区別がつきません。 呼び出し元からインベントリを分離する必要があるセラーは、アカウント内ではなく**アカウント境界**で行わなければなりません(MUST)。[アカウントの所有権と作成サーフェス](/docs/media-buy/specification#アカウントの所有権と作成サーフェス)を参照してください。 **リクエストスキーマ**: [`/schemas/v3/media-buy/get-media-buys-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-media-buys-request.json) **レスポンススキーマ**: [`/schemas/v3/media-buy/get-media-buys-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-media-buys-response.json) ## リクエストパラメータ | パラメータ | 型 | 必須 | 説明 | | -------------------------- | -------------------------------------------------------------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `account` | [account-ref](/docs/building/by-layer/L2/accounts-and-agents#account-references) | いいえ | アカウント参照。`{ "account_id": "..." }` または、セラーが暗黙的な解決をサポートしている場合は `{ "brand": {...}, "operator": "..." }` を渡します。省略した場合、アクセス可能なすべてのアカウントのデータを返します。 | | `media_buy_ids` | string\[] | いいえ\* | 取得するメディアバイIDの配列 | | `status_filter` | string \| string\[] | いいえ | ステータスフィルター: `"pending_creatives"`、`"pending_start"`、`"active"`、`"paused"`、`"completed"`、`"rejected"`、`"canceled"`。`media_buy_ids` が省略された場合のみ、デフォルトで `["active"]` になります。 | | `include_snapshot` | boolean | いいえ | true の場合、各パッケージのほぼリアルタイム配信スナップショットを含めます。デフォルトは `false`。 | | `include_history` | integer | いいえ | メディアバイごとに直近 N 件のリビジョン履歴エントリを含めます(min(N, 利用可能数) を返します)。除外するには 0 または省略。最大 1000。 | | `include_webhook_activity` | boolean | いいえ | true の場合、各メディアバイに、呼び出し元プリンシパル向けの最近の配信レポートウェブフック発火を含む `webhook_activity` 配列が含まれます。デフォルトは `false`。[ウェブフックアクティビティ](#ウェブフックアクティビティ)を参照。 | | `webhook_activity_limit` | integer | いいえ | バイごとに返すウェブフックレコードの上限(新しい順)。範囲 1〜200、デフォルト 50。`include_webhook_activity` が false の場合は無視されます。 | | `pagination` | object | いいえ | 広範なクエリのカーソルベースのページネーション制御(`max_results`、`cursor`)。 | \*`media_buy_ids` は結果を特定のメディアバイに絞り込む。どちらも指定しない場合、クエリはスコープベースとなり `status_filter` と `pagination` を使用します。 `media_buy_ids` を指定した場合、暗黙的なステータスフィルタリングは適用されない。特定のバイをステータスでフィルタリングしたい場合は `status_filter` を明示的に渡すこと。 ## レスポンス 現在のステータス、クリエイティブ承認状態、オプションの配信スナップショットを含むメディアバイの配列を返します: | フィールド | 説明 | | ------------ | ----------------------------------------------------------- | | `media_buys` | メディアバイオブジェクトの配列 | | `pagination` | カーソルページネーションメタデータ(`has_more`、`cursor`、オプションの `total_count`) | | `errors` | タスク固有のエラー(例: メディアバイが見つからない) | ### メディアバイオブジェクト **3.1 の語彙に関する注記。** `get_media_buys` はライフサイクルの状態をネストされた `media_buys[].status` フィールドで返します(深さ 1 にネストされているため、エンベロープとの衝突はありません)。`create_media_buy` と `update_media_buy` の成功レスポンスは、同じ状態をトップレベルの **`media_buy_status`** フィールドで返します(エンベロープのタスクステータス `status` との衝突を避けるため 3.1 で追加)。3.1 では同じ列挙に対して二つのフィールド名が存在し、この連鎖は 4.0 で統合されます([#4905](https://github.com/adcontextprotocol/adcp/issues/4905))。呼び出しをまたいで状態を保持するバイヤーは、両者を同じ論理的な値として扱うべきです。全体像については[マイグレーション › `media_buy_status`](/docs/reference/migration/media-buy-status)を参照してください。 | フィールド | 説明 | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `media_buy_id` | セラーのメディアバイ識別子 | | `invoice_recipient` | 作成時に指定された場合の、バイごとの請求書送付先。セラーが請求のオーバーライドを受理したことを確認します。銀行の詳細は省略されます(書き込み専用)。 | | `status` | 現在のステータス(`pending_creatives`、`pending_start`、`active`、`paused`、`completed`、`rejected`、`canceled`)。`create_media_buy` / `update_media_buy` の成功レスポンスの `media_buy_status` に対応します(上記 3.1 の語彙に関する注記を参照)。 | | `status_as_of` | セラーが、返されたメディアバイレベルの `status` を信頼できる情報源から最後に更新した ISO 8601 タイムスタンプ。ロールアップされたステータスの場合、返されるロールアップに影響しうる上流ステータス観測のうち最も古いものより後であってはなりません(MUST NOT)。任意です。省略または `null` の場合は鮮度について何も主張せず、バイヤーは不在からライブなステータスを推論してはなりません(MUST NOT)。キャッシュされた、またはロールアップされたステータスを、メディアバイの最終更新時刻を意味する `updated_at` とは別に解釈するために使用します。 | | `currency` | メディアバイレベルの金額に使用する ISO 4217 通貨 | | `total_budget` | キャンペーンの合計予算(`currency` 単位) | | `creative_deadline` | クリエイティブのアップロード期限(ISO 8601) | | `confirmed_at` | セラーがこのメディアバイにコミットした ISO 8601 タイムスタンプ。遅延承認/手動承認のフローでは、セラーのコミットが発生するまで `null` の場合があります。設定後は安定します。 | | `cancellation` | キャンセルのメタデータ(`status` が `canceled` の場合にのみ存在)。`canceled_at`(ISO 8601)、`canceled_by`(`"buyer"` または `"seller"`)、任意の `reason` を持つオブジェクト。 | | `revision` | 現在のリビジョン番号。楽観的並行性制御のために `update_media_buy` に渡します。 | | `valid_actions` | 現在の状態でバイヤーが実行できるアクション(例: `["pause", "cancel", "update_budget"]`)。[有効なアクションのマッピング](#有効なアクションのマッピング)を参照。 | | `history` | リビジョン履歴のエントリ、新しい順。`include_history > 0` の場合にのみ存在します。追記専用——エントリが変更または削除されることはありません。 | | `webhook_activity` | 呼び出し元プリンシパル向けの最近のレポーティングおよびヘルスのウェブフック発火、新しい順。`include_webhook_activity` が true で、**かつ**セラーがこのバイの発火履歴を公開している場合にのみ存在します。三状態の存在セマンティクスについては[ウェブフックアクティビティ](#ウェブフックアクティビティ)を参照。 | | `context` | `create_media_buy` からそのままエコーされる、メディアバイレベルの不透明な相関データ。メディアバイが context 付きで AdCP を通じて作成された場合、セラーは永続化した context を含めなければならず(MUST)、AdCP 外で、または context なしで作成されたメディアバイでは省略してもかまいません(MAY)。`media_buy_id` をバイヤーのトラッキング状態と突き合わせるために使用します。 | | `packages` | クリエイティブステータスとオプションのスナップショットを含むパッケージの配列 | ### パッケージオブジェクト | フィールド | 説明 | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `package_id` | セラーのパッケージ識別子 | | `product_id` | このパッケージの購入元となるプロダクト識別子。明示的な `create_media_buy` のパッケージリクエストから作成されたパッケージについて、セラーは、そのリクエストされたパッケージを表すすべてのレスポンスパッケージオブジェクトで、リクエストパッケージの `product_id` をエコーしなければなりません(MUST)。 | | `currency` | オプションのパッケージレベルの通貨オーバーライド(デフォルトはメディアバイの `currency`) | | `bid_price` | オークションベースパッケージの現在の入札価格(パッケージの `currency` が存在する場合はその通貨、なければメディアバイの `currency`) | | `format_ids` | `create_media_buy` で指定されたレガシーの名前付きフォーマット ID。元のリクエストに含まれていた場合は常にエコーされます。別のセレクターが優先された二重出力のケースも含みます。 | | `format_option_refs` | `create_media_buy` で指定された構造化された 3.1+ のフォーマットオプション参照。元のリクエストに含まれていた場合は常にエコーされます。 | | `format_kind` | `create_media_buy` で指定された直接的な正規セレクター。元のリクエストに含まれていた場合は常にエコーされます。別のセレクターが優先された情報提供目的のエコーのケースも含みます。 | | `params` | `format_kind` の直接的な正規セレクター向けのパラメータ。元のリクエストに含まれていた場合は常にエコーされます。`format_kind` が必要です。 | | `start_time` | フライト開始時刻(ISO 8601)。配信ステータスを解釈する前にこれを確認すること。 | | `end_time` | フライト終了時刻(ISO 8601) | | `paused` | バイヤーがこのパッケージを一時停止しているかどうか | | `canceled` | このパッケージがキャンセルされたかどうか(取り消し不可) | | `cancellation` | キャンセルのメタデータ(`canceled` が true の場合にのみ存在)。`canceled_at`(ISO 8601)、`canceled_by`(`"buyer"` または `"seller"`)、任意の `reason` を持つオブジェクト。 | | `creative_deadline` | パッケージごとのクリエイティブ期限(ISO 8601)。不在の場合、メディアバイの `creative_deadline` が適用されます。 | | `context` | `create_media_buy` のパッケージリクエストからそのままエコーされる、パッケージレベルの不透明な相関データ。パッケージが context 付きで AdCP を通じて作成された場合、セラーは永続化した context を含めなければならず(MUST)、AdCP 外で、または context なしで作成されたパッケージでは省略してもかまいません(MAY)。混在するセラー群を対象とするバイヤーは、レガシーな create レスポンスが `product_id` をエコーしなかった場合に、作成時に含めたパッケージごとの相関値(一般には `context.buyer_ref`)を使って `package_id` を自分たちのラインアイテムに対応付けられます。 | | `creative_approvals` | クリエイティブ承認状態の配列(下記参照) | | `format_ids_pending` | まだアップロードされていない `format_ids_to_provide` のフォーマットID | | `snapshot_unavailable_reason` | `include_snapshot: true` であるがこのパッケージのスナップショットが返されない場合の理由コード | | `snapshot` | ほぼリアルタイムの配信スナップショット(`include_snapshot: true` の場合) | ### クリエイティブ承認オブジェクト | フィールド | 説明 | | ------------------ | ------------------------------------------ | | `creative_id` | クリエイティブ識別子 | | `approval_status` | `pending_review`、`approved`、または `rejected` | | `rejection_reason` | 却下の説明(`approval_status` が `rejected` の場合) | クリエイティブライブラリを持たずに `inline_creative_management` を表明しているセラーの場合、`creative_approvals` は、パッケージに現在割り当てられているインラインクリエイティブの承認状態を読み戻す唯一の標準化されたサーフェスです。`creative_id`、`approval_status`、任意の `rejection_reason` をレポートしますが、`create_media_buy` や `update_media_buy` で送信された完全な `CreativeAsset` のペイロード、プレースメントのルーティング、ウェイト、過去のリビジョンは含みません。インライン専用のセラーと連携するバイヤーは、自分たちが送信したクリエイティブ本体を保持しておくべきです。 クリエイティブの修正は `approval_status: "rejected"` と特定の `rejection_reason` で表現されます。クリエイティブ編集のためのパッケージレベルの `input-required` ステータスは存在しません。修正済みのライブラリアセットは [`sync_creatives`](/docs/creative/task-reference/sync_creatives) で、修正済みのインライン専用パッケージアセットは [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) の `packages[].creatives` でアップロードすること。 ### 履歴エントリオブジェクト | フィールド | 必須 | 説明 | | ------------ | --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `revision` | はい | この変更が適用された後のリビジョン番号 | | `timestamp` | はい | この変更が発生した ISO 8601 タイムスタンプ | | `action` | はい | 何が起きたか: `created`、`activated`、`paused`、`resumed`、`canceled`、`rejected`、`completed`、`updated_budget`、`updated_dates`、`updated_packages`、`package_canceled`、`package_paused`、`package_resumed` | | `actor` | いいえ | 変更を行った者の識別情報(呼び出し元が指定するのではなく、認証コンテキストからサーバーが導出します) | | `summary` | いいえ | 人が読める説明(例: "Budget changed from $5,000 to $7,500 on pkg\_abc") | | `package_id` | いいえ | 変更が特定のパッケージを対象としていた場合の、影響を受けたパッケージ | 履歴エントリは**追記専用**です——セラーは既に出力したエントリを変更または削除してはなりません(MUST NOT)。呼び出し元はリビジョン番号でエントリをキャッシュしてもかまいません(MAY)。 `revision` は、セラーが状態を変更する変更または更新を適用した場合にのみ増加します。読み取り、バリデーションのみの呼び出し、完全に冪等な再実行では、履歴エントリは作成されず、リビジョンも増えません。バイヤーは、返されたリビジョンを、状態変更を意図した次の `update_media_buy` 呼び出しのためのトークンとして扱うべきです。 `confirmed_at` は配信ステータスのタイムスタンプではありません。セラーのコミットを記録するものであり、その後の一時停止/再開、アクティベーション、完了、キャンセル、レポーティングの変更を通じて安定したままです。 ### スナップショットオブジェクト | フィールド | 説明 | | ------------------- | ------------------------------------------------------------------------------------------------- | | `as_of` | プラットフォームがこのスナップショットを取得した際の ISO 8601 タイムスタンプ | | `staleness_seconds` | データの最大経過時間(秒)。ゼロ配信の解釈に使用します: 900(15分)はゼロが実際の値である可能性が高いことを意味し、14400(4時間)はレポートがまだ追いついていない可能性を意味します。 | | `impressions` | パッケージ開始以降の合計インプレッション数 | | `spend` | パッケージ開始以降の合計支出 | | `currency` | `spend` に対するオプションのスナップショット通貨オーバーライド | | `clicks` | パッケージ開始以降の合計クリック数(利用可能な場合) | | `pacing_index` | 配信ペース(1.0 = 順調、\<1.0 = 遅れ、>1.0 = 進みすぎ) | | `delivery_status` | `delivering`、`not_delivering`、`completed`、`budget_exhausted`、`flight_ended`、`goal_met` | | `ext` | セラー固有の運用フィールドのためのオプションの拡張オブジェクト | **`not_delivering`** は、パッケージが予定されたフライト期間内にあるにもかかわらず、少なくとも1回分の staleness サイクルでゼロインプレッションを記録したことを意味します。実装者はパッケージ起動から `staleness_seconds` が経過するまで `not_delivering` を返してはなりません — 最初の数分間インプレッションのない新しいパッケージは想定内であり、問題ではありません。このステータスに基づいて行動する前に、`start_time` を確認してパッケージがフライト期間内にあることを確認すること。 金額フィールドは次の通貨優先順位を使用します: `snapshot.currency` -> `package.currency` -> `media_buy.currency`。 ### ウェブフックアクティビティ `include_webhook_activity: true` の場合、返される各メディアバイは、セラーからバイヤーの登録済みエンドポイントへの最近のレポーティングおよびヘルスのウェブフック発火を記述する `webhook_activity` 配列を持つことがあります(MAY)。これは[永続チャネルのウェブフック契約](/docs/building/by-layer/L3/webhooks#persistent-channel-contract)におけるバイヤー側のデバッグ用サーフェスです——バイヤーはこれを使って、セラーのログに対するオペレーターレベルのクエリを必要とせずに、パブリッシャーが発火したか、バイヤーのゲートウェイが何を返したか、リトライがまだ進行中かを確認します。 レコードの形状、リクエストフィールド名、スコープ、保持期間の下限、三状態の存在、カーディナリティのルールは、このサーフェスを採用する AdCP のリソース全体で統一されています。リソース横断の規範的な節については、スナップショット/ログ契約のページの[ウェブフックアクティビティログのパターン](/docs/protocol/snapshot-and-log#webhook-activity-log-pattern)を参照してください——以下のルールは、それをメディアバイの呼び出し箇所向けに再掲し、メディアバイ固有のケイパビリティゲートを追加したものです。 このサーフェスは、配信レポートの通知タイプ(`scheduled`、`final`、`delayed`、`adjusted`)とヘルスの通知タイプ(`impairment`)の両方を対象とします。いずれも同じウェブフック配信契約と、同じバイヤー側のデバッグ上のニーズを共有します。 | フィールド | 説明 | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `idempotency_key` | ウェブフックのペイロード自体が持つ `idempotency_key` と等しくなります([§ `idempotency_key` による重複排除](/docs/building/by-layer/L3/webhooks#dedup-by-idempotency_key))。同じ論理的な発火のリトライをまたいで安定しています——バイヤーはまさにこのフィールドを介して、このサーフェスを自分たちのエンドポイントのログと突き合わせます。サポートチケットを起票する際にはこれを参照してください。 | | `subscriber_id` | どの登録済みウェブフックサブスクライバーがこの発火を受け取ったかを識別します。単一サブスクライバーの構成では不在です。複数サブスクライバーのバイでは値が入ります(4.0+、[#3009](https://github.com/adcontextprotocol/adcp/issues/3009) を参照)。 | | `fired_at` | セラーがこの試行を開始した ISO 8601 タイムスタンプ。 | | `completed_at` | レスポンスが観測された(または終端の失敗となった)ISO 8601 タイムスタンプ。`status` が `pending` の間は null。 | | `notification_type` | ウェブフックのペイロードのまま: `scheduled`、`final`、`delayed`、`adjusted`、`impairment`。 | | `sequence_number` | ウェブフックのペイロードのシーケンス番号——古いシーケンスの破棄や欠落の発見に有用です。通知タイプがシーケンス番号を持たない場合は不在。 | | `attempt` | この論理的な発火に対する 1 始まりのリトライカウンタ。最初の発火は `attempt: 1`。 | | `status` | `success`、`failed`、`timeout`、`connection_error`、または `pending`。セマンティクスは以下を参照。 | | `url` | **クエリ文字列とフラグメントが除去され**、高エントロピー/トークン状のパスセグメントが伏せられた対象 URL。完全な URL ではなく、オリジン + パスで登録済み URL と突き合わせてください。 | | `http_status_code` | バイヤーのエンドポイントからの HTTP ステータス。HTTP レスポンスを受け取らなかった場合は null(`timeout`、`connection_error`、`pending`)。 | | `response_time_ms` | リクエスト送信からレスポンス受信までの実時間レイテンシ。完了していない試行では null。 | | `payload_size_bytes` | セラーが送信したリクエストボディのサイズ——ペイロードのサイズ超過による拒否の診断に有用です。 | | `error_message` | 失敗に関する短い、人が読めるサーバー側の分類。`success` では null。セラーはここにリクエスト/レスポンスのボディやヘッダーを含めてはなりません(MUST NOT)。 | **ステータスのセマンティクス:** * `success` — 2xx ステータスのレスポンスを受信。`http_status_code` に値が入ります。 * `failed` — 2xx 以外のステータスのレスポンスを受信。`http_status_code` に値が入り、`error_message` がレスポンスを説明します。 * `timeout` — セラーが設定したタイムアウト内にレスポンスがありませんでした。`http_status_code` は null。運用上の意味: バイヤーのエンドポイントには到達できるが、遅いか過負荷である。 * `connection_error` — HTTP レスポンスの前に DNS、TLS、またはソケットが失敗しました。`http_status_code` は null。運用上の意味: バイヤーのエンドポイントに到達できないか、設定が誤っている。 * `pending` — 試行が実行中またはリトライのためにキューに入っています。`completed_at` は null。後続の試行は同じ `idempotency_key` と増加した `attempt` で現れます。 **レコードのカーディナリティ:** 試行ごとに 1 レコード。初回試行で成功した発火は `attempt: 1` の単一レコードとして現れます。3 回試行のリトライの軌跡(例: 2 回失敗して 1 回成功)は、`idempotency_key` を共有する 3 レコードとして現れます。 **スコープ(規範的):** * `webhook_activity` は**呼び出し元プリンシパル**にスコープされなければなりません(MUST)。複数のバイヤープリンシパルがアカウントレベルのアクセスを通じて同じメディアバイを閲覧できる場合、各プリンシパルは自分自身のエンドポイントを対象とする発火のみを見ます。 * このフィールドを公開するセラーは、各レコードの `completed_at` から少なくとも 30 日間、レコードを保持しなければなりません(**MUST**)——`success`、`failed`、`timeout`、`connection_error` の各結果(いずれも `completed_at` に値が入ります)にわたって一律にです。まだ `pending` ステータスのレコードでは、試行が終了するまでは `fired_at` から起算し、その後 `completed_at` から 30 日間に移行します——リトライの軌跡が途中で消えることはありません。この下限を守れないセラーは、より短いウィンドウを返すのではなく、フィールドを完全に省略しなければなりません(MUST)。三状態の存在セマンティクスは、セラーにきれいなオプトアウトを、バイヤーには依拠できる単一の保証を与えます。 * このサーフェスはデバッグの補助であり、完全な監査ログではありません。`webhook_activity_limit` を超えた古い発火のためのカーソルはありません——完全な履歴が必要なバイヤーは、自分たちの側でウェブフックのレコードを永続化しなければなりません。 **三状態の存在セマンティクス:** | 状態 | 意味 | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | フィールドが**省略** | セラーはこのバイのウェブフックアクティビティを公開していません。セラーが発火履歴を永続化していないか、セラーの宣言した [`propagation_surfaces`](/docs/media-buy/media-buys/lifecycle) が `webhook` を含まないか、そのバイに呼び出し元プリンシパル向けの登録済みウェブフックエンドポイントがないかのいずれかです。 | | 空配列 `[]` | セラーは発火履歴を永続化していますが、このプリンシパル向けに最近何も発火していません。 | | 空でない配列 | 実際の発火レコード、新しい順。 | 宣言した `propagation_surfaces` に `webhook` が含まれないセラーは、フィールドを省略しなければなりません(MUST)。`include_webhook_activity: true` でオプトインしても、それは上書きされません。 **予期しない省略の診断。** 発火があるはずなのにフィールドが省略されていた場合、チケットを起票せずに原因を切り分けられる観測点が二つあります。(1) このバイに対する自分の `push_notification_config` の登録状態を確認する——登録されていなければ、それが原因です。(2) `get_adcp_capabilities` を通じてセラーの `capabilities.media_buy.propagation_surfaces` を確認する——`webhook` が不在なら、それが原因です。両方とも問題なければ、残る原因は「セラーが発火履歴を永続化していない」であり、これはセラー側のギャップなのでオペレーターへのチケットを起票する価値があります。 **プライバシー:** * `url` フィールドはクエリ文字列とフラグメントが**除去**されており、セラーは共有シークレットに見えるパスセグメント(高エントロピーのランダムな素材、UUID/トークン状のもの)を伏せるべきです(SHOULD)。 * リクエストとレスポンスのボディはこのフィールドでは**公開されません**。将来の `include_webhook_payloads` 拡張が、より厳格な認可制御のもとでそれらを追加する可能性がありますが、ここではスコープ外です。 * `error_message` はサーバー側の分類文字列のみです——リクエストヘッダー、レスポンスボディ、バイヤーエンドポイントのスタックトレースは決して含みません。 #### ウェブフック配信の問題を診断する ```typescript TypeScript theme={null} import { testAgent } from '@adcp/sdk/testing'; import { GetMediaBuysResponseSchema, type WebhookActivityRecord } from '@adcp/sdk'; // The WebhookActivityRecord type is regenerated by the SDK from // /schemas/core/webhook-activity-record.json — once the SDK rebuilds against this // branch's schemas the import resolves. The same type appears on every AdCP resource // that surfaces webhook_activity[], so debug helpers can be written once and reused. function latestAttempt(trail: WebhookActivityRecord[]): WebhookActivityRecord { return trail.reduce((a, b) => (a.attempt >= b.attempt ? a : b)); } const result = await testAgent.getMediaBuys({ media_buy_ids: ['mb_12345'], include_webhook_activity: true, webhook_activity_limit: 20, }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = GetMediaBuysResponseSchema.parse(result.data); for (const mediaBuy of validated.media_buys) { // Three-state semantics — distinguish "seller does not surface" from "no recent fires". if (mediaBuy.webhook_activity === undefined) { console.log(`${mediaBuy.media_buy_id}: seller does not surface webhook activity for this buy`); continue; } const fires = mediaBuy.webhook_activity; if (fires.length === 0) { console.log(`${mediaBuy.media_buy_id}: no recent fires for this principal`); continue; } // Group attempts by idempotency_key so we can see the retry trail per logical fire. const trails = new Map(); for (const fire of fires) { const trail = trails.get(fire.idempotency_key) ?? []; trail.push(fire); trails.set(fire.idempotency_key, trail); } for (const [idempotencyKey, trail] of trails) { // Pick the latest attempt by `attempt` number — robust against any iteration order. const latest = latestAttempt(trail); if (latest.status === 'success') continue; const detail = latest.error_message ?? latest.http_status_code ?? '—'; console.log( `${mediaBuy.media_buy_id} ${idempotencyKey} ` + `(${latest.notification_type} seq=${latest.sequence_number}): ` + `${latest.status} after ${trail.length} attempt(s) — ${detail}` ); } } ``` ```python Python theme={null} import asyncio from collections import defaultdict from adcp.testing import test_agent from adcp.types import GetMediaBuysRequest, WebhookActivityRecord # WebhookActivityRecord is regenerated by the SDK from # /schemas/core/webhook-activity-record.json — once the SDK rebuilds against this # branch's schemas the import resolves. The same type appears on every AdCP resource # that surfaces webhook_activity[]. def latest_attempt(trail: list[WebhookActivityRecord]) -> WebhookActivityRecord: return max(trail, key=lambda f: f.attempt) async def main(): result = await test_agent.get_media_buys( GetMediaBuysRequest( media_buy_ids=['mb_12345'], include_webhook_activity=True, webhook_activity_limit=20, ) ) for media_buy in result.media_buys: # Three-state semantics: distinguish "seller does not surface" from "no recent fires". if media_buy.webhook_activity is None: print(f"{media_buy.media_buy_id}: seller does not surface webhook activity for this buy") continue fires = media_buy.webhook_activity if not fires: print(f"{media_buy.media_buy_id}: no recent fires for this principal") continue trails = defaultdict(list) for fire in fires: trails[fire.idempotency_key].append(fire) for idempotency_key, trail in trails.items(): latest = latest_attempt(trail) if latest.status == 'success': continue detail = latest.error_message or latest.http_status_code or '—' print(f"{media_buy.media_buy_id} {idempotency_key} " f"({latest.notification_type} seq={latest.sequence_number}): " f"{latest.status} after {len(trail)} attempt(s) — {detail}") asyncio.run(main()) ``` ## 有効なアクションのマッピング `valid_actions` 配列は、メディアバイの現在の状態でどの操作が許可されているかをエージェントに伝えます。セラーはこのフィールドを含めるべきです(SHOULD)。ステータスごとに期待される値: | ステータス | 期待される `valid_actions` | | ------------------- | -------------------------------------------------------------------------------------------------- | | `pending_creatives` | `pause`、`cancel`、`sync_creatives` | | `pending_start` | `pause`、`cancel`、`sync_creatives` | | `active` | `pause`、`cancel`、`update_budget`、`update_dates`、`update_packages`、`add_packages`、`sync_creatives` | | `paused` | `resume`、`cancel`、`update_budget`、`update_dates`、`update_packages`、`add_packages`、`sync_creatives` | | `completed` | *(空配列)* | | `rejected` | *(空配列)* | | `canceled` | *(空配列)* | セラーはビジネスルールに基づいてアクションを省略してもかまいません(MAY)(例: メディアバイにキャンセルを妨げる契約上の義務がある場合に `cancel` を省略する)。 クリエイティブの変更について、`valid_actions` にある `sync_creatives` はレガシーなクリエイティブ変更のアクションラベルであり、`sync_creatives` タスクが存在する証明ではありません。セラーが表明しているクリエイティブの経路を使用してください: `creative.has_creative_library: true` のセラーでは `sync_creatives` と `creative_assignments`、インライン専用のセラーでは `update_media_buy` の `packages[].creatives` です。 ## 一般的なユースケース ### クリエイティブ承認ステータスの確認 ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; import { GetMediaBuysResponseSchema } from '@adcp/sdk'; const result = await testAgent.getMediaBuys({ media_buy_ids: ['mb_12345'] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = GetMediaBuysResponseSchema.parse(result.data); if (validated.errors?.length > 0) { throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`); } for (const mediaBuy of validated.media_buys) { for (const pkg of mediaBuy.packages) { // Check for missing creatives if (pkg.format_ids_pending?.length > 0) { console.log(`Package ${pkg.package_id}: missing formats ${pkg.format_ids_pending.map(f => f.id).join(', ')}`); } // Check approval states for (const approval of pkg.creative_approvals ?? []) { if (approval.approval_status === 'rejected') { console.log(`Creative ${approval.creative_id} rejected: ${approval.rejection_reason}`); } else if (approval.approval_status === 'pending_review') { console.log(`Creative ${approval.creative_id} pending review`); } } } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import GetMediaBuysRequest async def main(): result = await test_agent.get_media_buys( GetMediaBuysRequest(media_buy_ids=['mb_12345']) ) if result.errors: raise Exception(f"Query failed: {result.errors}") for media_buy in result.media_buys: for pkg in media_buy.packages: # Check for missing creatives if pkg.format_ids_pending: ids = [f.id for f in pkg.format_ids_pending] print(f"Package {pkg.package_id}: missing formats {', '.join(ids)}") # Check approval states for approval in pkg.creative_approvals or []: if approval.approval_status == 'rejected': print(f"Creative {approval.creative_id} rejected: {approval.rejection_reason}") elif approval.approval_status == 'pending_review': print(f"Creative {approval.creative_id} pending review") asyncio.run(main()) ``` ### スナップショットを使った配信モニタリング ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; import { GetMediaBuysResponseSchema } from '@adcp/sdk'; const result = await testAgent.getMediaBuys({ status_filter: 'active', include_snapshot: true }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = GetMediaBuysResponseSchema.parse(result.data); for (const mediaBuy of validated.media_buys) { for (const pkg of mediaBuy.packages) { const snap = pkg.snapshot; if (!snap) continue; if (snap.delivery_status === 'not_delivering') { console.log(`Package ${pkg.package_id}: zero delivery (data up to ${snap.staleness_seconds}s old)`); } else if (snap.pacing_index !== undefined && snap.pacing_index < 0.8) { console.log(`Package ${pkg.package_id}: underpacing at ${(snap.pacing_index * 100).toFixed(0)}%`); } else { console.log(`Package ${pkg.package_id}: ${snap.impressions.toLocaleString()} impressions, pacing ${snap.pacing_index?.toFixed(2)}`); } } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import GetMediaBuysRequest async def main(): result = await test_agent.get_media_buys( GetMediaBuysRequest( status_filter='active', include_snapshot=True ) ) if result.errors: raise Exception(f"Query failed: {result.errors}") for media_buy in result.media_buys: for pkg in media_buy.packages: snap = pkg.snapshot if not snap: continue if snap.delivery_status == 'not_delivering': print(f"Package {pkg.package_id}: zero delivery (data up to {snap.staleness_seconds}s old)") elif snap.pacing_index is not None and snap.pacing_index < 0.8: print(f"Package {pkg.package_id}: underpacing at {snap.pacing_index * 100:.0f}%") else: print(f"Package {pkg.package_id}: {snap.impressions:,} impressions, pacing {snap.pacing_index:.2f}") asyncio.run(main()) ``` ### キャンペーン配信準備チェック ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; import { GetMediaBuysResponseSchema } from '@adcp/sdk'; const result = await testAgent.getMediaBuys({ media_buy_ids: ['mb_12345'] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = GetMediaBuysResponseSchema.parse(result.data); const [mediaBuy] = validated.media_buys; const issues = []; for (const pkg of mediaBuy.packages) { if (pkg.format_ids_pending?.length > 0) { issues.push(`Package ${pkg.package_id}: ${pkg.format_ids_pending.length} format(s) not yet uploaded`); } const rejected = (pkg.creative_approvals ?? []).filter(a => a.approval_status === 'rejected'); if (rejected.length > 0) { issues.push(`Package ${pkg.package_id}: ${rejected.length} creative(s) rejected`); } } if (issues.length === 0) { console.log('Campaign ready to launch'); } else { console.log('Campaign has blocking issues:'); issues.forEach(issue => console.log(` - ${issue}`)); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import GetMediaBuysRequest async def main(): result = await test_agent.get_media_buys( GetMediaBuysRequest(media_buy_ids=['mb_12345']) ) media_buy = result.media_buys[0] issues = [] for pkg in media_buy.packages: if pkg.format_ids_pending: issues.append(f"Package {pkg.package_id}: {len(pkg.format_ids_pending)} format(s) not yet uploaded") rejected = [a for a in (pkg.creative_approvals or []) if a.approval_status == 'rejected'] if rejected: issues.append(f"Package {pkg.package_id}: {len(rejected)} creative(s) rejected") if not issues: print("Campaign ready to launch") else: print("Campaign has blocking issues:") for issue in issues: print(f" - {issue}") asyncio.run(main()) ``` ## スナップショットと `get_media_buy_delivery` の比較 | | `get_media_buys`(スナップショットあり) | `get_media_buy_delivery` | | ---------------- | ---------------------------- | ------------------------ | | **目的** | 運用モニタリング | レポーティングと照合 | | **鮮度** | 数分(エンティティレベルの統計) | 数時間(バッチレポートジョブ) | | **精度** | ベストエフォート | 権威ある請求グレード | | **日付範囲** | 常に「キャンペーン開始以降」 | 設定可能な期間 | | **日別内訳** | なし | あり | | **クリエイティブステータス** | あり | なし | | **不足アセット** | あり | なし | 「現在のキャンペーン状態は何か?」という質問には `get_media_buys` を使い、「ある期間にキャンペーンがどのように機能したか?」には `get_media_buy_delivery` を使うこと。 ステータスの分類は両タスクのライフサイクルフィルター(`pending_creatives`、`pending_start`、`active`、`paused`、`completed`)で共有されています。`get_media_buy_delivery` はウェブフックコンテキストで追加のレポーティング専用ステータス(`reporting_delayed`、`failed`)を返すことがあります。 ## データの鮮度 スナップショットの `staleness_seconds` はプラットフォームによって異なる: | プラットフォームの種類 | 典型的な `staleness_seconds` | | ------------------------------------ | ------------------------ | | エンティティレベルの統計(例: GAM LineItemService) | 900(15分) | | ほぼリアルタイムのインサイト API | 60〜300 | | バッチ専用レポーティング | 14400(4時間) | プラットフォームがバッチレポーティングのみの場合、セラーエージェントは適切な `staleness_seconds` を設定して最新のキャッシュデータを返すべきです。 `include_snapshot: true` でパッケージの `snapshot` が省略されている場合、`snapshot_unavailable_reason` を確認すること: * `SNAPSHOT_UNSUPPORTED`: セラーがこの統合でパッケージスナップショットをサポートしていません * `SNAPSHOT_TEMPORARILY_UNAVAILABLE`: スナップショットパイプラインが遅延または低下しています。後でリトライすること * `SNAPSHOT_PERMISSION_DENIED`: 呼び出し元がそのパッケージのスナップショットメトリクスを閲覧する権限を持っていません ## ページネーション 大きなペイロードを避けるため、広範なステータスクエリにはカーソルページネーションを使用すること: * リクエスト: `pagination.max_results`(1〜100、デフォルト50)と オプションの `pagination.cursor` を設定します * レスポンス: `pagination.has_more` を読み取り、true の場合は `pagination.cursor` を次のリクエストに渡します * ID指定クエリ(`media_buy_ids`)は、IDセットが非常に大きい場合を除きページネーションを省略してよいです ## エラーハンドリング | エラーコード | 説明 | 対処法 | | --------------------- | -------------------------- | -------------------------------------------------- | | `MEDIA_BUY_NOT_FOUND` | メディアバイIDが存在しない | `media_buy_id` を確認すること | | `CONTEXT_REQUIRED` | リクエストされたスコープにメディアバイが見つからない | 有効なIDや参照を指定するか、`status_filter`/ページネーションのスコープを広げること | | `AUTH_MISSING` | 認証情報が提示されていない | 認証ヘッダーで認証情報を提供すること | | `AUTH_INVALID` | 認証情報が拒否された(期限切れ/失効) | 人による認証情報のローテーションが必要。自動リトライしないこと | ## 次のステップ * **不足クリエイティブのアップロード**: ライブラリを持つセラーには [`sync_creatives`](/docs/creative/task-reference/sync_creatives) を、インライン専用のセラーには [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) の `packages[].creatives` を使うこと * **ゼロ配信の調査**: `delivery_status: "not_delivering"` と `start_time` を確認してフライトがアクティブであることを確かめ、次に [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) を使って価格やターゲティングを調整すること * **詳細レポーティング**: 日付範囲レポーティングや日別内訳には [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) を使うこと * **キャンペーンの最適化**: セラーに結果を共有するには [`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback) を使うこと # get_products Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/task-reference/get_products get_products タスク — AdCP で自然言語のキャンペーンブリーフや構造化フィルターを使って広告インベントリを探索します。価格やフォーマットとともにマッチしたプロダクトを返します。 キャンペーン要件に基づき、自然言語のブリーフまたは構造化フィルターで利用可能な広告プロダクトを検索します。 **この形状の理由。** ターゲティング、価格、キュレーションは一度の往復に折り込まれています——ブリーフがディスカバリーを駆動し、パブリッシャーはそれに対してキュレーションし、`pricing_options` が確定価格を運び、バイヤーは `pricing_option_id` を通じてそれにコミットします。プロダクトと購入作成の間に別個の `get_price_quote` ステップを設けることは却下しました: それは一つの専門的判断を二つの不十分に規定された判断に分割し、ブリーフ→キュレーションの契約を壊します。反復は新しいタスクではなく、型付きの変更配列を伴う `buying_mode: "refine"` です。→ [設計原則: ブリーフがディスカバリーを駆動する](/docs/protocol/design-principles#3-the-brief-drives-discovery-targeting-is-an-input-not-a-step)。 **Authentication**: 任意(認証なしの場合は結果が制限されます) **Response Time**: 約 60 秒(バックエンド連携を伴う推論) **Request Schema**: [`/schemas/v3/media-buy/get-products-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-products-request.json) **Response Schema**: [`/schemas/v3/media-buy/get-products-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-products-response.json) ## クイックスタート 自然言語のブリーフでプロダクトを検索: ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; import { GetProductsResponseSchema } from '@adcp/sdk'; const result = await testAgent.getProducts({ buying_mode: 'brief', brief: 'Premium athletic footwear with innovative cushioning', brand: { domain: 'acmecorp.com' } }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } // Validate response against schema const validated = GetProductsResponseSchema.parse(result.data); console.log(`Found ${validated.products.length} products`); // Access validated product fields for (const product of validated.products) { console.log(`- ${product.name} (${product.delivery_type})`); console.log(` Formats: ${product.format_ids.map(f => f.id).join(', ')}`); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def discover_products(): result = await test_agent.simple.get_products( buying_mode='brief', brief='Premium athletic footwear with innovative cushioning', brand={ 'domain': 'acmecorp.com' } ) print(f"Found {len(result.products)} products") asyncio.run(discover_products()) ``` ```bash CLI requires-env=ADCP_AUTH_TOKEN theme={null} uvx adcp \ https://test-agent.adcontextprotocol.org/sales/mcp \ get_products \ '{"buying_mode":"brief","brief":"Premium athletic footwear with innovative cushioning","brand":{"domain":"acmecorp.com"}}' \ --auth $ADCP_AUTH_TOKEN ``` ### 構造化フィルターの利用 ブリーフの代わりに(または併用して)構造化フィルターを使うこともできます。`brief` モードでは、フィルターはパブリッシャーのキュレーションに対するハード制約として機能します。ブリーフが意図を表し、フィルターが要件を強制します。 ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; const result = await testAgent.getProducts({ buying_mode: 'wholesale', brand: { domain: 'acmecorp.com' }, filters: { channels: ['ctv'], delivery_type: 'guaranteed', standard_formats_only: true } }); if (result.success && result.data) { console.log(`Found ${result.data.products.length} guaranteed CTV products`); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def discover_with_filters(): result = await test_agent.simple.get_products( buying_mode='wholesale', brand={ 'domain': 'acmecorp.com' }, filters={ 'channels': ['ctv'], 'delivery_type': 'guaranteed', 'standard_formats_only': True } ) print(f"Found {len(result.products)} guaranteed CTV products") asyncio.run(discover_with_filters()) ``` ## リクエストパラメーター | Parameter | Type | Required | Description | | --------------------------- | ---------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `buying_mode` | string | Yes | `"brief"`、`"wholesale"`、または `"refine"`。`"brief"`: パブリッシャーがブリーフからプロダクトをキュレーションします。`"wholesale"`: バイヤー主導のターゲティング用の生プロダクトフィードアクセス。`brief` は指定してはなりません。`"refine"`: `refine` 配列の変更依頼を使って前のレスポンスのプロダクトやプロポーザルを反復します。v3 クライアントは `buying_mode` を含めなければなりません。`buying_mode` を持たない v3 以前のクライアントからリクエストを受けたセラーは `"brief"` をデフォルトとすべきです。**タイミングの意味論:** `"wholesale"` はホールセール・プロダクトフィードの読み取りです——セラーは同期レスポンスを返すべきであり(SHOULD)、`"wholesale"` リクエストを非同期/Submitted の経路へルーティングしてはなりません(MUST NOT)。部分的な完了はタスクの引き継ぎではなく [`incomplete[]`](#incomplete-配列) で通知します。`"brief"` と `"refine"` は同期的に完了してもよく(MAY)、キュレーションが上流システムへの問い合わせや、セラーが `time_budget` 内に完了できない HITL レビューを要する場合は `Submitted` エンベロープを返してもよい(MAY)。予測可能な高速のホールセール・プロダクトフィードアクセスを必要とするバイヤーは `"wholesale"` を使わなければなりません(MUST)。 | | `brief` | string | Conditional | キャンペーン要件の自然言語説明。`buying_mode` が `"brief"` の場合は必須。`"wholesale"` または `"refine"` の場合は指定してはなりません。 | | `refine` | [Refine\[\]](#refine-配列) | Conditional | プロダクトやプロポーザルを反復するための変更依頼の配列。`buying_mode` が `"refine"` の場合は必須。`"brief"` または `"wholesale"` の場合は指定してはなりません。後述の [Refine 配列](#refine-配列) を参照。 | | `brand` | BrandRef | No | ブランド参照(ドメイン+オプションの brand\_id)。実行時に完全な識別情報へ解決されます。 | | `account` | AccountRef | No | アカウント固有の価格設定に用いるアカウント参照。このアカウントのレートカードから価格付きのプロダクトを返します。 | | `catalog` | [Catalog](/docs/creative/catalogs) | No | バイヤーが宣伝したいアイテムのカタログ。セラーはカタログアイテムをインベントリに照合し、マッチが存在するプロダクトを返します。`brand` が必要。後述の [カタログによる探索](#カタログによる探索) を参照。 | | `filters` | Filters | No | 構造化フィルター(後述) | | `fields` | string\[] | No | 軽量なディスカバリーのためにレスポンスへ含める特定のプロダクトフィールド。シグナルのメタデータを要求する場合、バイヤーは選択不可のバンドル済み/計画済みシグナルには `included_signals` を、パッケージレベルのシグナル選択には `signal_targeting_allowed`・`signal_targeting_options`・`signal_targeting_rules` を要求すべきです(SHOULD)。 | | `property_list` | PropertyListRef | No | \[AdCP 3.0] フィルタリングに用いるプロパティリスト参照。[Property Lists](/docs/governance/property/tasks/property_lists) 参照 | | `pagination` | PaginationRequest | No | キュレーション/リファインされたレスポンスで返される `products[]` を上限設定するため、またはホールセール・プロダクトフィードを辿るためのカーソルベースページネーション(後述) | | `if_wholesale_feed_version` | string | No | このエージェントの以前のホールセールモード `get_products` レスポンスから得た不透明な `wholesale_feed_version` トークン。`buying_mode: "wholesale"` の場合のみ有効。指定されると、セラーはバイヤーの `cache_scope` に対する現在のホールセール・プロダクトフィードのバージョンと比較し、何も変わっていなければ `unchanged: true`(`products` を省略)を返してもよい(MAY)。バージョンのスコープは `pagination.cursor` を除外します: `public` は(エージェント、`buying_mode`、`filters`、`property_list`、`catalog`)でキー付けされ、`account` はアカウント識別を追加します。[ホールセールフィードのバージョニング](#ホールセールフィードバージョニング)参照。 | | `if_pricing_version` | string | No | 以前のレスポンスからの不透明な `pricing_version` トークン。`if_wholesale_feed_version` と一緒にのみ送らなければなりません(MUST)。評価順: `if_wholesale_feed_version` の不一致 → 完全なペイロード。`if_wholesale_feed_version` は一致するが `if_pricing_version` が不一致 → 完全なペイロード(バイヤーが更新後の `pricing_options` を見られるように)。両方一致 → セラーは `unchanged: true` を返してもよい(MAY)。価格を別途追跡しないセラーはこれを無視します。 | | `time_budget` | Duration | No | バイヤーがこのリクエストに割り当てる最大時間。セラーはその時間内で最善の結果を返し、その時間内に完了できないプロセス(人間の承認や高コストな外部クエリ)は開始しません。省略した場合はセラーがタイミングを決定します。例: `{"interval": 30, "unit": "seconds"}`。 | | `push_notification_config` | PushNotificationConfig | No | `brief` / `refine` のキュレーションディスカバリーにおける、非同期の最終完了/失敗通知のための任意の Webhook チャネル。`task_id` を持つ `submitted` レスポンスは、このフィールドの有無に関わらず `get_task_status`(レガシーの `tasks/get`)でポーリング可能なままです。リクエストがこのフィールドを含み、かつセラーが `submitted` を返す場合、セラーは少なくとも最終の完了/失敗通知を設定された Webhook へ配信しなければなりません(MUST)。途中経過の通知は任意です(MAY)。セラーが Webhook チャネルに対応できない場合は、暗黙に受け付けるのではなく構造化エラーでリクエストを拒否しなければなりません(MUST)。`wholesale` では無視されます。このフィールドが存在するからといって、セラーがホールセール読み取りを Submitted 経路へルーティングしてはなりません(MUST NOT)。 | **プロパティガバナンス** `property_list` フィルターは、プロパティガバナンスエージェント上で [`create_property_list`](/docs/governance/property/tasks/property_lists#create_property_list) を通じて作成されたプロパティリストを参照します。プロパティリストは、どのパブリッシャープロパティがコンプライアンス要件を満たすか——COPPA 認証済みサイト、サステナビリティスコア付き在庫、ブランドセーフなパブリッシャーなど——を定義します。 プロパティリストフィルタリングを使うには: 1. プロパティガバナンスエージェントで `get_adcp_capabilities` を呼び出し、利用可能な `property_features` を発見する 2. 機能要件を指定して `create_property_list` でプロパティリストを作成する 3. 得られた `property_list_id` を `get_products` に渡して在庫をフィルタリングする このフィルターをサポートするには、セラーが [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) で `features.property_list_filtering: true` を宣言しなければなりません。完全なワークフローは[プロパティガバナンス概要](/docs/governance/property/index)を参照してください。 ### Filters オブジェクト | Parameter | Type | Description | | -------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `delivery_type` | string | `"guaranteed"` または `"non_guaranteed"` でフィルタリング | | `is_fixed_price` | boolean | 固定価格かオークションかでフィルタリング。両方の価格タイプを持つプロダクトはどちらの値にもマッチしますが、返される `pricing_options` 配列には要求された価格タイプに一致するオプションのみを含めなければならず、バイヤーがディスカバリーから確定的に選択できるようにします。 | | `pricing_currencies` | string\[] | バイヤーがメディアプロダクトの取引に使える ISO 4217 通貨でフィルタリング(例: `["USD"]`)。プロダクトは、要求された通貨のいずれかでプロダクトレベルの `pricing_options` エントリを少なくとも1つ提供し、かつセラーが適用する(またはその他必須の)プロダクトスコープのシグナル課金がそれらの通貨のいずれかで満たせるか、増分価格を持たない場合にマッチします。セラーはマッチするプロダクトの `pricing_options` のみを返さなければなりません(MUST)。任意のシグナル/ベンダーのアドオン価格はこのフィルターで除外されません。 | | `format_ids` | FormatID\[] | 特定のフォーマット ID でフィルタリング | | `standard_formats_only` | boolean | IAB 標準フォーマットを受け付けるプロダクトのみ返す | | `min_exposures` | integer | 計測妥当性に必要な最小エクスポージャ数 | | `start_date` | string | 可用性確認のための開始日 (ISO 8601, YYYY-MM-DD) | | `end_date` | string | 可用性確認のための終了日 (ISO 8601, YYYY-MM-DD) | | `budget_range` | object | 適切なプロダクトを絞る予算レンジ(下記 Budget Range オブジェクト参照) | | `countries` | string\[] | ISO 3166-1 alpha-2 コードで国を指定(例: `["US", "CA", "GB"]`) | | `regions` | string\[] | ISO 3166-2 コードでリージョンのカバレッジをフィルタリング(例: `["US-NY", "GB-SCT"]`)。ローカルに限定された在庫に最適 | | `metros` | object\[] | メトロのカバレッジでフィルタリング。各エントリ: `{ system, code }`(例: `[{ "system": "nielsen_dma", "code": "501" }]`) | | `channels` | string\[] | 広告チャンネルでフィルタリング(例: `["display", "ctv", "social", "streaming_audio"]`)。[Media Channel Taxonomy](/docs/reference/media-channel-taxonomy) 参照 | | `video_placement_types` | string\[] | 許容する宣言済みの動画プレースメントタイプで動画プロダクトをフィルタリング: `instream`、`accompanying_content`、`interstitial`、`standalone`。セラーは要求されたタイプの少なくとも1つで満たせるプロダクトのみを返すべきで、配信を要求タイプに制約できない限り、混在した非ターゲット可能なバンドルは除外すべきです。IAB Tech Lab/OpenRTB 2.6 の `video.plcmt` 定義を AdCP ネイティブ名で使用します。 | | `audio_distribution_types` | string\[] | 許容する宣言済みのオーディオ配信タイプでオーディオプロダクトをフィルタリング: `music_streaming_service`、`fm_am_broadcast`、`podcast`、`catch_up_radio`、`web_radio`、`video_game`、`text_to_speech`。セラーは要求されたタイプの少なくとも1つで満たせるプロダクトのみを返すべきで、配信を要求タイプに制約できない限り、混在した非ターゲット可能なバンドルは除外すべきです。IAB Tech Lab/OpenRTB 2.6 の `audio.feed` 定義を AdCP ネイティブ名で使用します。 | | `sponsored_placement_types` | string\[] | 許容する宣言済みのスポンサープレースメントタイプでカタログ駆動のリテールメディアプロダクトをフィルタリング: `sponsored_search`、`sponsored_display`、`sponsored_native`。セラーは要求されたタイプの少なくとも1つで満たせるプロダクトのみを返すべきで、配信を要求タイプに制約できない限り、混在した非ターゲット可能なバンドルは除外すべきです。 | | `social_placement_surfaces` | string\[] | 許容する宣言済みのソーシャルプレースメント面でソーシャルプロダクトをフィルタリング: `feed`、`stories`、`short_video`、`explore`、`search`。セラーは要求された面の少なくとも1つで満たせるプロダクトのみを返すべきで、配信を要求面に制約できない限り、混在した非ターゲット可能なバンドルは除外すべきです。 | | `postal_areas` | object\[] | 郵便エリアのカバレッジでフィルタリング。各エントリ: `{ country, system, values }`(例: `[{ "country": "US", "system": "zip", "values": ["10001"] }]`) | | `geo_proximity` | object\[] | 地理的地点への近接でフィルタリング。各エントリは正確に1つの境界方式を使用: `radius`、`travel_time` + `transport_mode`、または `geometry`。 | | `keywords` | object\[] | 検索/リテールメディア向けのキーワード関連性でフィルタリング。各エントリ: `{ keyword, match_type? }`。`match_type` は省略時 `broad`。 | | `signal_targeting` | SignalTargeting\[] | 要求されたシグナルがバイヤー選択可能かつ共同で構成可能なプロダクトを対象とするディスカバリーフィルター: インラインの `signal_targeting_options` および/または、シグナルターゲティングを許可するがインラインオプションを省略するホールセールプロダクト向けのセラーの `get_signals` フィード経由で利用でき(`signal_targeting_allowed: true`)、プロダクトの `signal_targeting_rules` の下で互換なもの。各エントリは `signal_ref` を使用し(`signal_id` は非推奨の移行ブリッジとしてのみ受け付け)、`any` または `none` グループのサポートを要求するために `targeting_mode: "include"` または `"exclude"` を含めてもよい(省略時は `"include"`)。`scope: "product"` はセラーローカルな厳密オプションマッチングのみで、プロダクトやセラーをまたぐ可搬な意味識別子ではありません。可搬なディスカバリーを望むバイヤーは `scope: "data_provider"` または `get_signals` を使うべきです。`included_signals` および非推奨の `data_provider_signals` のメタデータは、`create_media_buy` で選択できないためこのフィルターを満たしません。これは購入時のリクエスト形状ではありません。パッケージ選択は常に `packages[].targeting_overlay.signal_targeting_groups` を使用します。 | | `required_performance_standards` | [PerformanceStandard\[\]](/docs/media-buy/advanced-topics/pricing-models#measurement-terms-and-performance-standards) | バイヤーのパフォーマンス基準要件を満たせるプロダクトに絞り込みます。各エントリはメトリクス、閾値、ベンダーを指定します(例: 「70% MRC のビューアビリティに対して DoubleVerify」)。これらの閾値を満たせない、または指定ベンダーをサポートしないプロダクトは除外されます。 | | `required_metrics` | string\[]([メトリクス語彙](/docs/media-buy/media-buys/optimization-reporting)) | `reporting_capabilities.available_metrics` がこれらのメトリクスの上位集合であるプロダクト——すなわち、配信時に列挙されたすべてのメトリクスの報告にコミットするプロダクト——に絞り込みます。機能ディスカバリーに使用します(例: CTV CPCV 購入向けの `["completed_views"]`)。セラーはリストを満たせないプロダクトを暗黙に除外しなければならず(MUST)——fail ではなく filter——エラーを返してはなりません。プロダクトが宣言した `available_metrics` は、結果のメディアバイに引き継がれる拘束的な報告契約となります。 | | `required_vendor_metrics` | object\[] | `reporting_capabilities.vendor_metrics` がベンダー定義のメトリクス(独自のアテンション、排出量、パネルデモグラフィック、ブランドリフト調査など)をカバーするプロダクトに絞り込みます。各エントリは `vendor`(BrandRef)および/または `metric_id` を指定します(少なくとも一方)。ベンダー横断のディスカバリー(例: 「任意のアテンション計測」)はバイヤーエージェントの責任です: どのベンダーがカテゴリを提供するかをベンダーの `brand.json` レコードで解決し、それらをフィルターエントリとして列挙します。`required_metrics` と同じ filter-not-fail の意味論。 | ### プレースメントフィールド `get_products` は、セラーが `placements` を含める、またはバイヤーが `fields` で要求した場合に、プロダクトのプレースメントデータを返します。プレースメント ID はパブリッシャースコープです。プロダクトのプレースメントは、パブリッシャーの宣言が存在する場合、パブリッシャーの公開 `adagents.json` のプレースメント宣言を `{publisher_domain, placement_id}` で参照すべきです。セラー非公開のプレースメント ID、ソース/オリジンの詳細、配信システムのマッピングはレスポンスに含めてはなりません。 返される各プレースメントは以下を持ちうる: | Field | Meaning | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `placement_id` | パブリッシャー名前空間におけるプレースメント識別子。バイヤーは `creative_assignments[].placement_refs` で `publisher_domain` とともに参照します。レガシーの `placement_ids` 文字列は単一パブリッシャーの文脈でのみ一意です。 | | `publisher_domain` | パブリッシャー参照のプレースメントを定義する `adagents.json` を持つドメイン。新しいマルチパブリッシャープロダクトは含めるべきです(SHOULD)。レガシープロダクトで省略された場合、バイヤーは `placement_id` をセラーエージェント自身のパブリッシャードメインに対して解釈してよい。 | | `mode` | `targetable` はバイヤーがパブリッシャースコープのプレースメントを(例えば `creative_assignments[].placement_refs` で)参照できることを意味します。`included` はプレースメントがプロダクト構成の一部だがバイヤー選択不可であることを意味します。 | | `video_placement_types` | OLV その他の動画在庫の宣言済み動画プレースメントタイプ。IAB Tech Lab/OpenRTB 2.6 の `video.plcmt` 定義を AdCP ネイティブ名で使用。具体的なプレースメントは通常1値を宣言し、集約プレースメントは複数を宣言しうる。 | | `audio_distribution_types` | ラジオ、ストリーミングオーディオ、ポッドキャスト、ゲームその他のオーディオ在庫の宣言済みオーディオ配信タイプ。IAB Tech Lab/OpenRTB 2.6 の `audio.feed` 定義を AdCP ネイティブ名で使用。 | | `sponsored_placement_types` | カタログ駆動のリテールメディア在庫の宣言済みスポンサープレースメントタイプ。 | | `social_placement_surfaces` | ソーシャル在庫の宣言済みソーシャルプレースメント面。 | | `format_ids` / `format_options` | プレースメント固有のクリエイティブサポート。プロダクトレベルのフォーマットが上限であり、プレースメントレベルのフォーマットはそのプレースメントで実効的に受け付ける集合を狭めるもので、プロダクトが受け付けないフォーマットを追加してはなりません。 | パブリッシャーは、`adagents.json` の `authorized_agents[].placement_ids` または `authorized_agents[].placement_tags` を使って、特定のパブリッシャープレースメントに対してセラーエージェントを認可できます。セラーは、自身が販売を認可されたパブリッシャー参照のプレースメントのみを返すべきです。 シグナルターゲティングフィルターの例: ```json theme={null} { "$schema": "/schemas/media-buy/get-products-request.json", "buying_mode": "wholesale", "filters": { "signal_targeting": [ { "signal_ref": { "scope": "data_provider", "data_provider_domain": "pinnacle-data.example", "signal_id": "auto_intenders" }, "value_type": "binary", "value": true, "targeting_mode": "include" } ] }, "fields": [ "product_id", "name", "included_signals", "signal_targeting_allowed", "signal_targeting_options", "signal_targeting_rules", "pricing_options" ] } ``` ### 通貨フィルタリング バイヤーの制約が「取引可能なメディア価格を持つプロダクトのみ表示」の場合は `filters.pricing_currencies` を使います。バイヤーが予算額やレンジも提供する場合は `budget_range.currency` を使います。 バイヤーは両方を送ってもよい(MAY)。セラーはそれらを論理積で適用します: `budget_range.currency` は予算額を建て、`pricing_currencies` はどの返却プロダクト `pricing_options` が対象かを絞ります。二つのフィールドが競合する場合、セラーは競合のみを理由にリクエストを拒否するのではなく、マッチするプロダクト0件を返すべきです(SHOULD)。プロダクトスコープのシグナル価格は別個のアドオン面であるため、このフィルターは必須のセラー適用シグナル課金のみをゲートします。任意のシグナル/ベンダーのアドオンは他通貨を広告してもよく、バイヤーは非対応のアドオン価格を選択すべきではありません。 `is_fixed_price` と組み合わせる場合、返却プロダクトの `pricing_options` は両方のフィルターを満たさなければなりません(MUST): 要求時にオプションは固定価格であり、その `currency` は `pricing_currencies` に含まれていなければなりません。 通貨のみのフィルター例: ```json theme={null} { "$schema": "/schemas/media-buy/get-products-request.json", "buying_mode": "wholesale", "filters": { "pricing_currencies": ["USD"] }, "fields": ["product_id", "name", "pricing_options"] } ``` プロダクトが USD と EUR の両方のメディア価格を持ち、バイヤーが `pricing_currencies: ["USD"]` を送ると、セラーはそのプロダクトを USD のプロダクトレベル `pricing_options` のみで返します。プロダクトが固定またはその他必須のプロダクトスコープのシグナル課金も持つ場合、その必須課金は USD で価格付けされているか、増分価格を持たないかのいずれかでなければならず、そうでなければプロダクトはフィルターにマッチしません。`currency` のない必須の `custom` シグナル価格は、セラーが正当に増分価格なしと扱える場合を除き、このフィルターでは満たせません。任意のシグナルアドオンはプロダクトのマッチングに影響しません。 ### Budget Range オブジェクト | Parameter | Type | Required | Description | | ---------- | ------ | -------- | -------------------------------------------- | | `currency` | string | Yes | ISO 4217 通貨コード(例: `"USD"`, `"EUR"`, `"GBP"`) | | `min` | number | No\* | 最低予算額 | | `max` | number | No\* | 最高予算額 | \*`min` または `max` のいずれか一方は必ず指定しなければなりません。 ### Refine 配列 `refine` 配列は変更依頼のリストです。各エントリは `scope` と、バイヤーが求める内容を宣言します。少なくとも 1 エントリが必要。セラーはすべてのエントリをまとめて考慮してレスポンスを構成し、`refinement_applied` で各エントリに返答します。 各エントリは `scope` による判別共用体です。 #### scope: "request" | Field | Type | Required | Description | | ------- | ------ | -------- | -------------------------------------------------------------------------------- | | `scope` | string | Yes | `"request"` | | `ask` | string | Yes | 選択全体への方向指示(例: `"more video options"`、`"suggest how to combine these products"`)。 | #### scope: "product" | Field | Type | Required | Description | | ------------ | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `scope` | string | Yes | `"product"` | | `product_id` | string | Yes | 前回の `get_products` レスポンスのプロダクト ID | | `action` | string | No | `"include"`(デフォルト): 更新された価格とデータでこのプロダクトを返します。`"omit"`: レスポンスから除外します。`"more_like_this"`: 類似プロダクトを探す(元のプロダクトも返されます)。省略時、セラーはエントリを `"include"` として扱います。 | | `ask` | string | No | バイヤーが求める内容。`"include"` の場合: 具体的な変更(例: `"add 16:9 format"`)。`"more_like_this"` の場合: 「類似」の意味(例: `"same audience but video format"`)。`action` が `"omit"` の場合は無視されます。 | #### scope: "proposal" | Field | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `scope` | string | Yes | `"proposal"` | | `proposal_id` | string | Yes | 前回の `get_products` レスポンスのプロポーザル ID | | `action` | string | No | `"include"`(デフォルト): 更新された配分と価格で返します。`"omit"`: レスポンスから除外します。`"finalize"`: 確定価格とインベントリのホールドを要求します(ドラフトのプロポーザルをコミット済みに遷移させます)。省略時、セラーはエントリを `"include"` として扱います。 | | `ask` | string | No | バイヤーが求める内容(例: `"shift more budget toward video"`、`"reduce total by 10%"`)。`action` が `"omit"` の場合は無視されます。 | ### refinement\_applied(レスポンス) セラーが `refine` 配列を受け取ると、レスポンスには位置でマッチする `refinement_applied` 配列が含まれます。各エントリは依頼が fulfilled されたかを報告します。 | Field | Type | Required | Description | | ------------- | ------ | ----------------------------- | ------------------------------------------------------------------------------------------------ | | `scope` | string | Yes | 対応する `refine` エントリの scope(`"request"` / `"product"` / `"proposal"`)をエコーします。 | | `product_id` | string | `scope` が `"product"` の場合は必須 | 対応する refine エントリの `product_id` をエコーします。 | | `proposal_id` | string | `scope` が `"proposal"` の場合は必須 | 対応する refine エントリの `proposal_id` をエコーします。 | | `status` | string | Yes | `"applied"`: 依頼が fulfilled されました。`"partial"`: 部分的に fulfilled されました。`"unable"`: fulfilled できなかった。 | | `notes` | string | No | セラーの説明。`status` が `"partial"` または `"unable"` の場合に推奨。 | ### カタログによる探索 カタログアイテムを宣伝できる広告プロダクトを探すには `catalog` を渡します。セラーはカタログアイテムをインベントリに照合し、マッチが存在するプロダクトを返します。すべてのカタログ種別に対応しています。商品カタログはスポンサー商品枠を探し、求人カタログは求人広告プロダクトを、フライトカタログはダイナミックトラベル広告を探す。 `catalog` フィールドは AdCP 全体で使われる同じ [Catalog](/docs/creative/catalogs) オブジェクトを使います。`catalog_id` で同期済みカタログを参照したり、インラインでアイテムを指定したり、セレクターでフィルタリングしたりできます。 | Field | Type | Description | | ------------ | ----------- | ---------------------------------------------------------- | | `type` | CatalogType | カタログ種別(必須)— `product`、`job`、`hotel`、`flight`、`offering` など | | `catalog_id` | string | ID で同期済みカタログを参照する | | `ids` | string\[] | 特定のアイテム ID に絞り込む | | `gtins` | string\[] | クロスリテーラーマッチング用に GTIN でフィルタリング(product 種別のみ) | | `tags` | string\[] | タグでフィルタリング(OR ロジック) | | `category` | string | カテゴリでフィルタリング | | `query` | string | 自然言語フィルター | レスポンスのプロダクトには `catalog_types`(対応するカタログ種別)と `catalog_match`(マッチしたアイテム)が含まれます。 ## レスポンス `products` 配列と、必要に応じて `proposals` を返します。 ### Products 配列 | Field | Type | Description | | ------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `product_id` | string | プロダクトの一意 ID | | `name` | string | 人が読めるプロダクト名 | | `description` | string | プロダクトの詳細説明 | | `publisher_properties` | PublisherProperty\[] | パブリッシャーごとのエントリ。`publisher_domain` と `property_ids` または `property_tags` を含む | | `format_ids` | FormatID\[] | サポートするクリエイティブフォーマット ID | | `delivery_type` | string | `"guaranteed"` または `"non_guaranteed"` | | `delivery_measurement` | DeliveryMeasurement | (任意)配信の計測方法(インプレッション、ビュー等) | | `pricing_options` | PricingOption\[] | 利用可能な価格モデル(CPM、CPCV など)。オークションオプションは `floor_price` とオプションの `price_guidance` を含む場合があります。入札ベースのオークションモデル(CPM、vCPM、CPC、CPCV、CPV)はオプションの `max_bid`(boolean)を含む場合もあります。 | | `shows` | CollectionSelector\[] | (任意)このプロダクトで利用可能なコレクション。各エントリは `publisher_domain` と `collection_ids` を持ちます。バイヤーは参照先の `adagents.json` から完全なコレクションオブジェクトを解決します。[Collections and installments](/docs/media-buy/product-discovery/collections-and-installments) 参照。 | | `collection_targeting_allowed` | boolean | (任意、デフォルト: false)バイヤーがこのプロダクトの shows のサブセットをターゲットできるかどうか。false の場合、プロダクトはバンドルです。 | | `data_provider_signals` | DataProviderSignalSelector\[] | (任意、非推奨)このプロダクトに既にバンドル/関連付けられたデータプロバイダーシグナルのレガシー/選択不可メタデータ。新規実装は `included_signals` を使うべきです。 | | `included_signals` | SignalListing\[] | (任意)このプロダクトに既に含まれる/バンドルされる/計画されたシグナルの選択不可メタデータ。これらはプロダクトが何であるかを説明するもので、バイヤーはパッケージの `signal_targeting_groups` では選択しません。データプロバイダー/シグナルソースの参照は参照のみの場合があり、プロダクトローカルの参照はインラインの `name` と `value_type` を含みます。 | | `signal_targeting_allowed` | boolean | (任意、デフォルト: false)このプロダクトがパッケージレベルのシグナルターゲティング面を持つかどうか。編集可能性は `signal_targeting_rules` で制御されます。固定/デフォルトのみのプロダクトも、適用済みのシグナルグループがエコーされる場合はこれを true に設定します。 | | `signal_targeting_options` | ProductSignalTargetingOption\[] | (任意)バイヤーが `packages[].targeting_overlay.signal_targeting_groups` を通じて選択できる(または固定/デフォルト時にセラーが適用する)インラインのプロダクトスコープのシグナルオプション。シグナルごとの `pricing_options` を含む場合があります。プロダクトスコープの価格はこのプロダクトについて権威的です。データプロバイダー/シグナルソースの参照は参照のみの場合があり、プロダクトローカルの参照はインラインの `name` と `value_type` を含みます。 | | `signal_targeting_rules` | SignalTargetingRules | (任意)選択可能なシグナルのプロダクトスコープの構成ルール。直接解決 vs セラー計画による解決、任意、必須、最大数、相互排他、固定選択、グループサイズ上限など。これらの上限はセラー全体の `get_adcp_capabilities` ではなくプロダクトに属します。プロダクトは異なるアドサーバーやセラーの計画レイヤーに支えられている場合があるためです。固定/デフォルトの選択はセラーが適用し、結果のパッケージ状態にエコーされます。 | | `brief_relevance` | string | ブリーフに合致する理由(ブリーフ提供時) | | `measurement_readiness` | [MeasurementReadiness](/docs/media-buy/conversion-tracking/#measurement-readiness) | (任意)バイヤーのイベント設定がこのプロダクトの最適化に十分かどうか。セラーがバイヤーのアカウントコンテキストを評価できる場合のみ存在します。 | | `measurement_terms` | [MeasurementTerms](/docs/media-buy/advanced-topics/pricing-models#measurement-terms-and-performance-standards) | (任意)セラーのデフォルトの課金計測とメイクグッド条件。バイヤーは `create_media_buy` で異なる条件を提案できます。 | | `performance_standards` | [PerformanceStandard\[\]](/docs/media-buy/advanced-topics/pricing-models#measurement-terms-and-performance-standards) | (任意)セラーのデフォルトのパフォーマンス基準(ビューアビリティ、IVT、完了率、ブランドセーフティ、アテンションスコア)。バイヤーは `create_media_buy` で異なる基準を提案できます。 | | `cancellation_policy` | [CancellationPolicy](/docs/media-buy/advanced-topics/pricing-models#cancellation-policy) | (任意)保証プロダクトのキャンセル通知期間とペナルティ。バイヤーはプロダクトに対してメディアバイを作成することでこれらの条件を受諾します。 | ### 非 URL 在庫向けのパブリッシャープロパティ `publisher_properties[].publisher_domain` は、パブリッシャーの `adagents.json` 名前空間を固定するドメインです。広告が表示される URL である必要はなく、物理的な会場、刊行物、放送局、スクリーンネットワーク、印刷媒体のプレースホルダーでもありません。 デジタル/非デジタルのプロダクトで同じセレクター形状を使います: * **デジタルプロパティ**: `publisher_domain` は通常、ウェブサイト、アプリ、チャンネル、CTV プロパティを `adagents.json` で宣言するパブリッシャードメインです。 * **印刷、静的 OOH、ラジオ、映画館、ローカル TV**: `publisher_domain` は権威あるプロパティカタログを公開する運営パブリッシャーまたはネットワークのドメインです。実際の在庫は `property_ids`、`property_tags`、プレースメント、コレクション、プロダクトメタデータ、チャンネルフィールドで識別されます。 * **集約ネットワーク**: プロダクトが多数のプロパティ(会場、刊行物、スクリーン、放送局、ローカル市場のタグ付き集合など)にまたがる場合は `property_tags` を使います。 `publisher_domain` に `"print"` や `"ooh"` のような値を作り出してはなりません。チャンネルの意味は `channels` に、プロパティの意味は参照先のプロパティ宣言に、販売可能なパッケージの意味はプロダクト自体に置きます。 例えば、タグ付けされたメトロプロパティにまたがるプロダクトは、`publisher_properties` 配列内でこのセレクターを使えます: ```json theme={null} [ { "selection_type": "by_tag", "property_tags": ["metro", "station"] } ] ``` ### Proposals 配列(任意) パブリッシャーはプロダクトと併せてプロポーザル(予算配分付きの構造化メディアプラン)を返すことがあります。詳細は [Proposals](/docs/media-buy/product-discovery/media-products#proposals) を参照。 | Field | Type | Description | | ----------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `proposal_id` | string | このプロポーザルを finalize し、コミット後に `create_media_buy` で実行するための一意 ID | | `proposal_status` | string | ライフサイクル状態。`draft` は、作成前に `get_products` の refine アクション `finalize` を通じてプロポーザルを finalize する必要があることを意味します。`committed` は、`expires_at` 前に `create_media_buy` で実行できることを意味します。省略時は後方互換のため購入可能として扱います。 | | `name` | string | メディアプランの人が読める名称 | | `allocations` | ProductAllocation\[] | プロダクト間の予算配分(合計 100% 必須)。各配分にはフライトごとのスケジューリング用にオプションの `start_time` と `end_time` を含む場合があります。 | | `forecast` | DeliveryForecast | プロポーザルの集計配信予測。メトリクスの範囲を持つ予測ポイントを含みます。[Delivery Forecasts](/docs/media-buy/product-discovery/media-products#delivery-forecasts) 参照 | | `total_budget_guidance` | object | 最小/推奨/最大の予算ガイダンス(任意) | | `brief_alignment` | string | キャンペーンブリーフへの対応内容 | | `expires_at` | string | プロポーザルの有効期限 (ISO 8601)。コミット済みプロポーザルでは、これは `create_media_buy` のためのインベントリホールドの期限です。 | 各 `ForecastPoint` は1つの予測行です。複合スライスは、同じポイント上の複数の `dimensions[]` 項目(例: placement × country)でエンコードされます。兄弟ポイントはネストされた子ではなく並列の行です。ディメンションの順序に意味はありません。バイヤーは `(forecast_range_unit, budget があれば, product_id があれば, kind でソートした dimensions)` から行の同一性を正規化します。バイヤーは同じ粒度の行を比較してもよいですが、返された行が完全で重複のないパーティションを形成するとセラーが文書化しない限り、それらを合計してはなりません(MUST NOT)。標準の配信レポートは、正確なディメンション横断の交差ではなく、一次元の周辺分布を検証します。 ### ページネーション `pagination` はすべての `get_products` モードで有効ですが、その意味は購入モードに従います: * `brief` モードでは、ページネーションはブリーフに対するセラーのキュレーション回答を上限設定します。ページは、ブリーフの文言にマッチするすべてのプロダクトが列挙されたという約束ではありません。 * `refine` モードでは、ページネーションは `refine` 配列と現在のフィルターが示す絞り込み後の `products[]` 結果を上限設定します。プロポーザルはプランメタデータとしてページに付随する場合がありますが、`pagination.max_results`・`has_more`・`cursor`・`total_count` はプロダクト結果セットにスコープされ、別個のプロポーザルリストや、プロダクト/プロポーザルの合算数にはスコープされません。 * `wholesale` モードでは、ページネーションはホールセール・プロダクトフィードを辿ります。これは網羅的/フィード形式の読み取りで、ホールセールフィードバージョニングと組み合わさるモードです。 キュレーション/絞り込み後のレスポンスで返されるプロダクトを上限設定する、またはホールセール・プロダクトフィードを辿るために、カーソルベースのページネーションを使います: | Request Parameter | Type | Description | | ------------------------ | ------- | -------------------------------- | | `pagination.max_results` | integer | ページあたりの最大プロダクト数(1〜100、デフォルト: 50) | | `pagination.cursor` | string | 次のページを取得するための前のレスポンスのカーソル | | Response Field | Type | Description | | ------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------- | | `pagination.has_more` | boolean | さらにプロダクトがあるかどうか | | `pagination.cursor` | string | 次のページを取得するために渡すカーソル | | `pagination.total_count` | integer | このページネーション結果セット内のプロダクト総数(任意。すべてのバックエンドがサポートするわけではない)。`brief` / `refine` では、これはセラーの全カタログではなく、キュレーション/絞り込み後のプロダクトセットです。 | ページネーションは任意です。省略した場合、サーバーは完全な結果セット(またはサーバーが選択したデフォルトページ)を返します。レスポンスに `pagination.has_more: true` が含まれる場合、更新された `pagination.cursor` を除いて同じ結果定義リクエストコンテキストを用い、次のリクエストで `pagination.cursor` を渡して次のページを取得します。 ### レスポンスメタデータ | Field | Type | Description | | ------------------------ | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `property_list_applied` | boolean | \[AdCP 3.0] 提供された `property_list` に基づきフィルタした場合 `true`。未指定または非対応なら省略/`false`。 | | `catalog_applied` | boolean | セラーが提供された `catalog` に基づき結果をフィルタした場合 `true`。カタログが提供されていないか、セラーがカタログマッチングをサポートしない場合は省略/`false`。 | | `refinement_applied` | [RefinementResult\[\]](#refinement_applied-レスポンス) | 各 `refine` エントリへのセラーの確認応答(位置でマッチ)。`buying_mode` が `"refine"` の場合のみ存在します。上記 [refinement\_applied](#refinement_applied-レスポンス) 参照。 | | `incomplete` | [IncompleteEntry\[\]](#incomplete-配列) | `time_budget` 内またはセラー内部の制限により完了できなかった内容を宣言します。各エントリはスコープと人が読める説明を持ちます。レスポンスが完全に完了している場合は省略されます。後述の [incomplete 配列](#incomplete-配列) 参照。 | | `filter_diagnostics` | object | `filters` が候補セットをどう絞り込んだかを説明する、任意の非致命的な可観測性ブロック——`total_candidates` に加え、フィルター単位の `excluded_by` カウント(フィルター名でキー付け)。結果リストが空、または想定外に小さいときに「在庫がない」と「フィルターがすべて除外した」を区別します。競争上の情報漏洩を避けるため、プロダクト名ではなくカウントのみ。後述の [filter\_diagnostics](#filter_diagnostics) 参照。 | | `wholesale_feed_version` | string | このレスポンスの構成に用いたホールセール・プロダクトフィード状態のバージョンを表す不透明トークン。条件付きフェッチ(`if_wholesale_feed_version`)を実装するセラーは、バイヤーがキャッシュして後で照会できるよう、すべてのホールセールモードレスポンスでこれを返さなければなりません(MUST)。不透明として扱う——フォーマットも順序も検査もなし。[ホールセールフィードバージョニング](#ホールセールフィードバージョニング)参照。 | | `pricing_version` | string | プロダクトの `pricing_options` とネストした `signal_targeting_options[].pricing_options` を含む価格レイヤーのバージョンを表す任意の不透明トークン。セラーが独立した価格バージョニングをサポートする場合、`pricing_version` は価格が動くと変わり、`wholesale_feed_version` は構造/メタデータが動くときのみ変わります。両者を分離しないセラーは `pricing_version` を省略し、両方に `wholesale_feed_version` を使ってもよい(MAY)。 | | `cache_scope` | string | `"public"` または `"account"`。**すべてのレスポンスで必須**(スキーマで強制——二層キャッシュの安全性はこれに依存します)。リクエストに `account` がなかった場合は `"public"` でなければなりません(MUST)。`account` があった場合、セラーは `"public"`(レートカードからのアカウント価格——バイヤーが重複排除)または `"account"`(アカウント固有のオーバーライド)のいずれかを宣言します。[キャッシュレイヤリング](#キャッシュレイヤリング)参照。 | | `unchanged` | boolean | リクエストがバイヤーの `cache_scope` に対するセラーの現在のバージョンに一致する `if_wholesale_feed_version`(および/または `if_pricing_version`)を運んだ場合にのみ `true` として存在し、その場合 `products[]` は省略されなければなりません(MUST)。`wholesale_feed_version`・`cache_scope`・(使用時は)`pricing_version` は引き続きエコーされなければなりません(MUST)。セラーは `unchanged: false` を出してはなりません(MUST NOT)——フィールドの不在が「レスポンスがプロダクトを含む」シグナルです(状態ごとに1形状)。`unchanged: true` を受け取ったバイヤーは、ローカルのホールセールプロダクトミラーを変更してはなりません(MUST NOT)。 | ### filter\_diagnostics セラーが除外を特定のフィルターに帰属できる場合、レスポンスは `filter_diagnostics` ブロックを含んでもよい(MAY)。これは可観測性であり、エラー報告ではありません——セラーは filter-not-fail の慣習に従って、マッチしないプロダクトを引き続き暗黙に除外します。バイヤーはこれを、その存在に依存せずに空/小さい結果をトリアージするために使います。`total_candidates` と `excluded_by` は独立して任意です——ベースライン候補セットのサイズが機密なセラーは、`total_candidates` なしで `excluded_by` を出してもよい(MAY)。 | Field | Type | Description | | ----------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `semantics` | string | `"only"`(決定的。*このフィルター単独*でなければ含まれたであろうプロダクトを数える——トリアージ推奨)、`"any"`(いずれかのフィルターで除外されたプロダクトを数える。カウントは重複しうる)、または `"approximate"`(セラーが除外を単一フィルターにきれいに帰属できない)。バイヤーはカウントで算術する前に `semantics` を確認すべきです(SHOULD)。 | | `total_candidates` | integer | フィルター適用前に検討されたプロダクト数。候補プールが大きい場合はサンプリング/上限設定されることがあります。任意。 | | `excluded_by` | object | キーはリクエストのフィルタープロパティ名(`pricing_currencies`、`required_metrics`、`required_geo_targeting`、`budget_range` など)。各値は `{ count, values?, notes? }`。有意にセットを絞ったフィルターのみ現れればよい。 | | `excluded_by..count` | integer | このフィルターで除外されたプロダクト数。親の `semantics` フィールドに従って解釈します。 | | `excluded_by..values` | array | 除外に寄与した具体的なフィルター値の任意のリスト(例: `required_metrics` の `["completed_views"]`)。項目はフィルター形状に応じて文字列またはオブジェクト。フィルター固有の知識なしでは不透明。 | | `excluded_by..notes` | string | 絞り込みに関する任意の人が読めるメモ。 | ```json theme={null} { "products": [], "filter_diagnostics": { "semantics": "only", "total_candidates": 47, "excluded_by": { "required_metrics": { "count": 31, "values": ["completed_views"] }, "required_geo_targeting": { "count": 9 }, "pricing_currencies": { "count": 3, "values": ["USD"] }, "budget_range": { "count": 7 } } } } ``` ### incomplete 配列 `time_budget` 内(またはセラー自身の内部制限により)すべての作業を完了できない場合、レスポンスには欠けている内容を宣言する `incomplete` 配列が含まれます。バイヤーは `estimated_wait` を使って、より大きな予算でリトライするかどうかを判断できます。 | Field | Type | Required | Description | | ---------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `scope` | string | Yes | `"products"`: すべてのインベントリソースを検索できなかった。`"pricing"`: プロダクトは返されたが価格が欠けているか未確定。`"forecast"`: プロダクトは返されたが予測データが欠けています。`"proposals"`: プロポーザルが生成されないか不完全。`"wholesale_feed"`: ホールセールモードで、完全なフィード列挙を完了できなかった。 | | `description` | string | Yes | 欠けている内容とその理由の人が読める説明。 | | `estimated_wait` | Duration | No | このスコープを解決するのに必要な追加時間。 | ### ホールセールフィードバージョニング セラーのホールセール・プロダクトフィードを同期したばかりのバイヤーは、フィードのサイズに関わらず、一度の安価な呼び出しで「バージョン X 以降に何か変わったか?」を尋ねられます。セラーはすべてのホールセールモードレスポンスで不透明な `wholesale_feed_version` を返します。バイヤーは次の呼び出しで `if_wholesale_feed_version` を通じてそれを返し、セラーは `unchanged: true` でショートサーキットしてもよい(MAY)——プロダクトペイロードもページごとの差分もなし。HTTP の `ETag` / `If-None-Match` を踏襲しています。 これは `get_products` が返すセラー側のホールセール・プロダクトフィードです。`sync_catalogs` のフィードではありません。`sync_catalogs` はセラーアカウント上のバイヤー提供のキャンペーン入力フィードを管理します。 **unchanged レスポンスの例:** リクエスト: ```json theme={null} { "$schema": "/schemas/media-buy/get-products-request.json", "buying_mode": "wholesale", "if_wholesale_feed_version": "v2026-05-18T08:00:00Z-acme-rev412" } ``` レスポンス(ホールセール・プロダクトフィードに変更なし): ```json theme={null} { "$schema": "/schemas/media-buy/get-products-response.json", "status": "completed", "message": "Wholesale product feed unchanged since v2026-05-18T08:00:00Z-acme-rev412.", "context_id": "ctx-abc-789", "unchanged": true, "wholesale_feed_version": "v2026-05-18T08:00:00Z-acme-rev412", "pricing_version": "v2026-05-18T08:00:00Z-acme-rev412", "cache_scope": "public" } ``` レスポンス(ホールセール・プロダクトフィードに変更あり——完全なペイロードを返す。抜粋): ```json test=false theme={null} { "message": "Returning 50 of 312 products (wholesale feed version advanced).", "context_id": "ctx-abc-790", "wholesale_feed_version": "v2026-05-18T10:15:00Z-acme-rev415", "pricing_version": "v2026-05-18T10:15:00Z-acme-rev415", "cache_scope": "public", "products": [ { "product_id": "prod_premium_ctv_us", "name": "Premium CTV — US", "description": "Run-of-network CTV inventory across premium publishers.", "publisher_properties": [{ "publisher_domain": "streamhaus.example.com", "property_ids": ["primetime_ctv"] }], "format_ids": [{ "id": "video_ctv_1080p_30s" }], "delivery_type": "guaranteed", "pricing_options": [ { "pricing_option_id": "po_cpm_v2", "pricing_model": "cpm", "currency": "USD", "fixed_price": 18.50 } ] } ], "pagination": { "has_more": true, "cursor": "eyJvIjo1MH0=", "total_count": 312 } } ``` **ルール** * トークンは**不透明**です。フォーマットも順序も検査もなし。 * 返された `wholesale_feed_version` は、それを生成したリクエストパラメータにスコープされます。バイヤーは、使用した `(account, filters, buying_mode, property_list, catalog)` タプルとともにバージョンをキャッシュしなければなりません(MUST)。 * `pricing_version` は任意のより細かいトークンです: 存在する場合、価格が動くと変わりますが `wholesale_feed_version` は構造/メタデータが動くときのみ変わります。プロダクトメタデータを変えないレートカードの一括更新でよくあります。 * **`if_pricing_version` は `if_wholesale_feed_version` を要求します。** 価格はそれ自体の構造的ベースラインを持ちません。`if_wholesale_feed_version` なしで `if_pricing_version` を送るのはスキーマレベルのエラーです。セラーの評価は二段階です: ホールセールフィードの不一致は完全なペイロードを返し(価格は暗黙に古い)、ホールセールフィード一致で価格不一致も完全なペイロードを返し(バイヤーが更新後の `pricing_options` を見られるように)、両方一致で `unchanged: true`。 * **`filters` の正準化。** セラーは `filters` オブジェクトを `wholesale_feed_version` のキー空間へハッシュする前に正準化済みとして扱わなければなりません(MUST): キーは辞書順にソートしなければならず(MUST)、省略された値とデフォルト値は同一に扱わなければならず(MUST。`delivery_type` キーの欠如は `delivery_type: null` と同じスコープ)、配列値はフィルターがセット意味論を持つ場合はソートしなければならず(MUST。例: `channels`, `format_ids`, `required_metrics`)、シーケンス意味論を持つ場合は順序を保持しなければなりません(例: `preferred_delivery_types`)。等価だが形状の異なるフィルターオブジェクトを渡すバイヤーは、セラーから同じ `wholesale_feed_version` を受け取らなければなりません(MUST)。このルールは、バイヤー SDK 間のキー順やデフォルト省略の違いによる、静かな古いミラーのバグを防ぎます。**前方互換のデフォルト:** 3.x マイナーバージョンで追加される新しいフィルターフィールドは、スキーマでセット vs シーケンスの意味論を宣言しなければなりません(MUST。`x-canonicalization: set | sequence` または同等の記述で)。明示的な宣言がない場合、ルールは**セット意味論**(ハッシュ前にソート)をデフォルトとします。このデフォルトでドリフトするセラーや SDK は、消費者が説明できないキャッシュミスを生みます。 * **ページネーションとの相互作用。** `wholesale_feed_version` は個々のページではなくホールセール・プロダクトフィード全体を表します。`wholesale_feed_versioning.supported: true` を宣言するセラーは、(最初のページだけでなく)すべてのページネーションページで `wholesale_feed_version` を返さなければなりません(MUST)。バージョニングを宣言しないセラーも同様にすべきです(SHOULD)。ページ間でホールセールフィードが変化した場合、新しいバージョンが次のページで現れ、バイヤーは `cursor: null` からページネーションを再開しなければなりません(MUST)——既に受け取った部分ページは古いバージョンを表します。セラーは代わりに、ページネーション開始時にフィードをスナップショットし、すべてのページを元のバージョンでそのスナップショットから提供してもよい(MAY)。あるページ上の `wholesale_feed_version` がそのページが属するバージョンである限り、どちらの実装も適合です。 * **`unchanged: true` と進行中のページネーション。** `cursor: X` でページネーション中のバイヤーは、これまでのページが引かれたバージョンに一致する `if_wholesale_feed_version` を送ってもよい(MAY)。セラーが `unchanged: true` を確認すると、レスポンスは `products[]` とページネーションエンベロープを完全に省略します。バイヤーは、そのバージョンの下でさらなるページが新しいデータを生まないと確信して、進行中のウォークを中止します。セラーは、アクティブなページネーション内の個々のページをスキップするために条件付きフェッチのショートサーキットを使ってはなりません——`unchanged` はフィード対キャッシュ済みバージョンであり、ページごとではありません。 * `if_wholesale_feed_version` を無視する v3.1 以前のセラーは、単に完全なペイロードを返します——意味的には正しく、非効率なだけです(HTTP の unchanged-server パスと同じ)。 条件付きフェッチを超えたプッシュ型の変更追跡については `specs/wholesale-feed-webhooks.md` を参照してください。ホールセールフィード Webhook は、変更されたプロダクトペイロード、価格ペイロード、削除トゥームストーン、または一括変更サマリーを運びます。`get_products` は修復と照合のための読み取りのままです。 ### キャッシュレイヤリング セラーは二つの概念的レイヤーを公開します: **パブリックレイヤー**(レートカード/構造ビュー)と**アカウントごとのオーバーレイ**(カスタムディール、アカウント固有のレートカード)。条件付きフェッチの経路は `cache_scope` を通じてレイヤーを認識します。 **なぜ重要か。** あるセラーで N アカウントにわたってホールセールプロダクトをミラーするバイヤーは、実際にはすべてのバイヤーで同一の在庫を N コピー持ちたくありません。パブリックレイヤーはセラーの公開レートカードで、ほとんどのセラーのほとんどのアカウントはそこから直接価格を付けます。プレミアムなカスタムディールが例外です。 **二層キャッシュ。** | Layer | Cache key | What's stored | | --------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | Public | `(agent, buying_mode, filters, property_list, catalog)` | `wholesale_feed_version_public`、アカウント参照なしで見たホールセール・プロダクトフィードのペイロード | | Account overlay | `(agent, buying_mode, filters, property_list, catalog, account_id)` | `wholesale_feed_version_account`、`cache_scope: "account"` が返されたときの、このアカウント参照ありで見たホールセール・プロダクトフィードのペイロード | **振る舞い。** * `account` なしのリクエストは常に `cache_scope: "public"` を返します。バイヤーはパブリックキーの下でキャッシュします。 * `account` ありのリクエストは `cache_scope: "public"` または `"account"` を返します(セラーが宣言しなければならず、MUST、デフォルトなし)。 * `"public"`: このアカウントはレートカードから価格を付けます。バイヤーは重複排除してもよい(MAY)——バージョンとペイロードは未認証ビューと同じです。バイヤーは `"public"` cache\_scope の任意のアカウントの後続リクエストを、単一のパブリックレイヤーエントリから提供できます。 * `"account"`: このレスポンスはアカウント固有のオーバーライドを運びます。バイヤーはアカウントオーバーレイキーの下でキャッシュします。 * セラーは、以前 `"account"` を得たリクエストで `cache_scope: "public"` を返すことで、アカウントを `"account"` から `"public"` へダウングレードしてもよい(MAY)——バイヤーはこれを「このアカウントにはもうオーバーライドがない」と解釈し、アカウントオーバーレイを破棄すべきです(SHOULD)。 **`if_wholesale_feed_version` による条件付きフェッチ。** トークンを、それが返されたスコープと組にして送ります。セラーはそのスコープの現在のバージョンと比較します。バイヤーのトークンが `"account"` スコープに属するがセラーが `cache_scope: "public"` で応答する場合、それがダウングレードのシグナルです——バイヤーはオーバーレイを破棄します。 **Webhook による無効化。** ホールセールフィード Webhook イベントは、`*.priced` と `*.updated` のペイロードで `applies_to.scope` を宣言します。セラーは、どのサブスクライバーがプロダクト Webhook を受け取るかを決める際、`get_products buying_mode: "wholesale"` が使う同じアカウント/呼び出し元の認可述語を適用しなければなりません(MUST): * `applies_to: { scope: "public" }` → そのエンティティのパブリックレイヤーキャッシュを無効化します。そのパブリックバージョンを参照するすべてのアカウントオーバーレイも古くなり、再取得すべきです(SHOULD)。 * `applies_to: { scope: "account", account_ids: [...] }` → 指定されたアカウントのオーバーレイのみを無効化します。パブリックレイヤーは影響を受けません。 * `account_ids` なしの `applies_to: { scope: "account" }` → セラーは影響を受ける集合を伏せています。サブスクライバーごとのスコープフィルターが、principal が影響を受ける集合に含まれるサブスクライバーにのみイベントをルーティングします。イベントを受け取ることは「あなたのオーバーレイは古い」を意味します。 Webhook 側の完全な仕様は `specs/wholesale-feed-webhooks.md` の §「Cache layering and event scoping」を参照してください。 **完全なフィールドはスキーマを参照**: [`get-products-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/get-products-response.json) ## よくあるシナリオ ### タイムバジェット付きの探索 素早い結果が必要で部分的なデータを許容できる場合、タイムバジェットを宣言します。セラーはバジェット内で達成できる最善の結果を返し、不完全な内容を宣言します。 ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; const result = await testAgent.getProducts({ buying_mode: 'brief', brief: 'CTV and display for brand awareness', brand: { domain: 'acmecorp.com' }, time_budget: { interval: 10, unit: 'seconds' } }); if (result.success && result.data) { console.log(`Found ${result.data.products.length} products`); if (result.data.incomplete) { for (const entry of result.data.incomplete) { console.log(`Incomplete: ${entry.scope} — ${entry.description}`); if (entry.estimated_wait) { console.log(` Would resolve in ${entry.estimated_wait.interval} ${entry.estimated_wait.unit}`); } } } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def discover_with_time_budget(): result = await test_agent.simple.get_products( buying_mode='brief', brief='CTV and display for brand awareness', brand={ 'domain': 'acmecorp.com' }, time_budget={ 'interval': 10, 'unit': 'seconds' } ) print(f"Found {len(result.products)} products") for entry in result.get('incomplete', []): print(f"Incomplete: {entry['scope']} — {entry['description']}") if 'estimated_wait' in entry: wait = entry['estimated_wait'] print(f" Would resolve in {wait['interval']} {wait['unit']}") asyncio.run(discover_with_time_budget()) ``` 不完全なデータを含むレスポンスの例 — プロダクトは返されているが一部のスコープが欠けている: ```json test=false theme={null} { "products": [ { "product_id": "prog-display-ros", "name": "Programmatic Display — Run of Site", "delivery_type": "non_guaranteed", "pricing_options": [{ "pricing_option_id": "cpm-ros", "pricing_model": "cpm", "currency": "USD", "fixed_price": 12.00 }] } ], "incomplete": [ { "scope": "products", "description": "Premium inventory not searched — requires publisher approval", "estimated_wait": { "interval": 60, "unit": "minutes" } }, { "scope": "forecast", "description": "Forecast model did not complete within budget", "estimated_wait": { "interval": 45, "unit": "seconds" } } ] } ``` ### ホールセールプロダクトの探索 ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; // wholesale モード: バイヤー独自のオーディエンスを適用、パブリッシャーキュレーションなし const result = await testAgent.getProducts({ buying_mode: 'wholesale', brand: { domain: 'acmecorp.com' }, filters: { delivery_type: 'non_guaranteed' } }); if (result.success && result.data) { console.log(`Found ${result.data.products.length} standard wholesale products`); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def discover_standard_wholesale_products(): # wholesale モード: バイヤー独自のオーディエンスを適用、パブリッシャーキュレーションなし result = await test_agent.simple.get_products( buying_mode='wholesale', brand={ 'domain': 'acmecorp.com' }, filters={ 'delivery_type': 'non_guaranteed' } ) print(f"Found {len(result.products)} standard wholesale products") asyncio.run(discover_standard_wholesale_products()) ``` ### マルチフォーマット探索 ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; // video と display の両方をサポートするプロダクトを探す const result = await testAgent.getProducts({ buying_mode: 'brief', brief: 'Brand awareness campaign with video and display', brand: { domain: 'acmecorp.com' }, filters: { channels: ['display', 'ctv'] } }); if (result.success && result.data) { console.log(`Found ${result.data.products.length} products supporting video and display`); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def discover_multi_format(): # Find products supporting both video and display result = await test_agent.simple.get_products( buying_mode='brief', brief='Brand awareness campaign with video and display', brand={ 'domain': 'acmecorp.com' }, filters={ 'channels': ['display', 'ctv'] } ) print(f"Found {len(result.products)} products supporting video and display") asyncio.run(discover_multi_format()) ``` ### 予算と日付でのフィルタリング ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; // 指定国とチャンネルで、予算と期間に収まるプロダクトを探す const result = await testAgent.getProducts({ buying_mode: 'brief', brief: 'Q2 campaign for athletic footwear in North America', brand: { domain: 'acmecorp.com' }, filters: { start_date: '2025-04-01', end_date: '2025-06-30', budget_range: { min: 50000, max: 100000, currency: 'USD' }, countries: ['US', 'CA'], channels: ['display', 'ctv', 'podcast'], delivery_type: 'guaranteed' } }); if (result.success && result.data) { console.log(`Found ${result.data.products.length} products for Q2 within budget`); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def discover_with_budget_and_dates(): # Find products within budget and date range for specific countries and channels result = await test_agent.simple.get_products( buying_mode='brief', brief='Q2 campaign for athletic footwear in North America', brand={ 'domain': 'acmecorp.com' }, filters={ 'start_date': '2025-04-01', 'end_date': '2025-06-30', 'budget_range': { 'min': 50000, 'max': 100000, 'currency': 'USD' }, 'countries': ['US', 'CA'], 'channels': ['display', 'ctv', 'podcast'], 'delivery_type': 'guaranteed' } ) print(f"Found {len(result.products)} products for Q2 within budget") asyncio.run(discover_with_budget_and_dates()) ``` ### プロパティタグの解決 ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; // property_tags を持つプロダクトを取得 const result = await testAgent.getProducts({ buying_mode: 'brief', brief: 'Sports content', brand: { domain: 'acmecorp.com' } }); if (result.success && result.data) { // publisher_properties に property_tags がある場合は大規模ネットワークを意味する // エージェントのポートフォリオは get_adcp_capabilities で確認する const productsWithTags = result.data.products.filter(p => p.publisher_properties?.some(pub => pub.property_tags && pub.property_tags.length > 0) ); console.log(`${productsWithTags.length} products use property tags (large networks)`); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def discover_property_tags(): # Get products with property tags result = await test_agent.simple.get_products( buying_mode='brief', brief='Sports content', brand={ 'domain': 'acmecorp.com' } ) # publisher_properties に property_tags がある場合は大規模ネットワークを意味する # エージェントのポートフォリオは get_adcp_capabilities で確認する products_with_tags = [p for p in result.products if any(pub.get('property_tags') for pub in p.get('publisher_properties', []))] print(f"{len(products_with_tags)} products use property tags (large networks)") asyncio.run(discover_property_tags()) ``` ### 保証配信のプロダクト ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; // Find guaranteed delivery products for measurement const result = await testAgent.getProducts({ buying_mode: 'brief', brief: 'Guaranteed delivery for lift study', brand: { domain: 'acmecorp.com' }, filters: { delivery_type: 'guaranteed', min_exposures: 100000 } }); if (result.success && result.data) { console.log(`Found ${result.data.products.length} guaranteed products with 100k+ exposures`); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def discover_guaranteed(): # 計測用に保証配信のプロダクトを探す result = await test_agent.simple.get_products( buying_mode='brief', brief='Guaranteed delivery for lift study', brand={ 'domain': 'acmecorp.com' }, filters={ 'delivery_type': 'guaranteed', 'min_exposures': 100000 } ) print(f"Found {len(result.products)} guaranteed products with 100k+ exposures") asyncio.run(discover_guaranteed()) ``` ### 標準フォーマットのみ ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; // IAB 標準フォーマットのみ受け付けるプロダクトを探す const result = await testAgent.getProducts({ buying_mode: 'wholesale', brand: { domain: 'acmecorp.com' }, filters: { standard_formats_only: true } }); if (result.success && result.data) { console.log(`Found ${result.data.products.length} products with standard formats only`); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def discover_standard_formats(): # IAB 標準フォーマットのみ受け付けるプロダクトを探す result = await test_agent.simple.get_products( buying_mode='wholesale', brand={ 'domain': 'acmecorp.com' }, filters={ 'standard_formats_only': True } ) print(f"Found {len(result.products)} products with standard formats only") asyncio.run(discover_standard_formats()) ``` ### カタログ主導の探索 `catalog` とブランドを使って、カタログアイテムを宣伝できる広告プロダクトを探す。セラーはアイテムをインベントリに照合し、マッチが存在するプロダクトを返します。 ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; // 特定のカタログアイテム向けのリテールメディアプロダクトを探す const result = await testAgent.getProducts({ buying_mode: 'wholesale', brand: { domain: 'acmecorp.com' }, catalog: { type: 'product', tags: ['ketchup', 'organic'], category: 'food/condiments' }, filters: { channels: ['retail_media'] } }); if (result.success && result.data) { if (result.data.catalog_applied) { console.log(`Found ${result.data.products.length} products with catalog matches`); } else { console.log('Seller does not support catalog matching'); } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def discover_commerce_products(): # 特定のカタログアイテム向けのリテールメディアプロダクトを探す result = await test_agent.simple.get_products( buying_mode='wholesale', brand={ 'domain': 'acmecorp.com' }, catalog={ 'type': 'product', 'tags': ['ketchup', 'organic'], 'category': 'food/condiments' }, filters={ 'channels': ['retail_media'] } ) if result.get('catalog_applied'): print(f"Found {len(result.products)} products with catalog matches") else: print("Seller does not support catalog matching") asyncio.run(discover_commerce_products()) ``` ```bash CLI requires-env=ADCP_AUTH_TOKEN theme={null} uvx adcp \ https://test-agent.adcontextprotocol.org/sales/mcp \ get_products \ '{"buying_mode":"wholesale","brand":{"domain":"acmecorp.com"},"catalog":{"type":"product","tags":["ketchup","organic"],"category":"food/condiments"},"filters":{"channels":["retail_media"]}}' \ --auth $ADCP_AUTH_TOKEN ``` GTIN マッチング、同期済みカタログの参照、または他のカタログ種別向けのプロダクト探索も利用できます。 ```json theme={null} { "catalog": { "type": "product", "gtins": ["00013000006040", "00013000006057"] } } ``` ```json theme={null} { "catalog": { "catalog_id": "gmc-primary", "type": "product" } } ``` ```json theme={null} { "catalog": { "type": "job", "catalog_id": "chef-vacancies" } } ``` ### プロパティリストでのフィルタリング **AdCP 3.0** - プロパティリストでのフィルタリングにはガバナンスエージェントの対応が必要です。 承認済みリストのプロパティで利用できるプロダクトだけに絞る。 ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; // Filter products by property list from governance agent const result = await testAgent.getProducts({ buying_mode: 'brief', brief: 'Brand-safe inventory for family brand', brand: { domain: 'acmecorp.com' }, property_list: { agent_url: 'https://governance.example.com', list_id: 'pl_brand_safe_2024' } }); if (result.success && result.data) { // フィルタが適用されたか確認 if (result.data.property_list_applied) { console.log(`Found ${result.data.products.length} products on approved properties`); } else { console.log('Agent does not support property list filtering'); console.log(`Found ${result.data.products.length} products (unfiltered)`); } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def discover_with_property_list(): # ガバナンスエージェントのプロパティリストでフィルタリング result = await test_agent.simple.get_products( buying_mode='brief', brief='Brand-safe inventory for family brand', brand={ 'domain': 'acmecorp.com' }, property_list={ 'agent_url': 'https://governance.example.com', 'list_id': 'pl_brand_safe_2024' } ) # Check if filtering was actually applied if result.get('property_list_applied'): print(f"Found {len(result['products'])} products on approved properties") else: print("Agent does not support property list filtering") print(f"Found {len(result['products'])} products (unfiltered)") asyncio.run(discover_with_property_list()) ``` **注意**: `property_list_applied` が省略または `false` の場合、セールスエージェントはプロダクトをフィルタリングしていません。これは以下の場合に発生する: * エージェントがプロパティガバナンス機能をサポートしていません * エージェントがプロパティリストにアクセスできなかった * プロパティリストが利用可能なインベントリに影響しなかった #### プロパティターゲティングの動作 プロダクトには `property_targeting_allowed` フラグがあり、フィルタリングに影響します。 * **`property_targeting_allowed: false`(デフォルト)**: プロダクトは「all or nothing」— あなたのリストがプロダクトのすべてのプロパティを含まない限り除外されます * **`property_targeting_allowed: true`**: プロダクトのプロパティとあなたのリストに交差がある場合にインクルードされます これにより、パブリッシャーはバイヤーが個別選択できないラン・オブ・ネットワークプロダクトと、バイヤーがフィルタリングできる柔軟なインベントリを提供できます。 詳細は [Property Targeting](/docs/media-buy/product-discovery/media-products#property-targeting) を参照。プロパティリストについては [Property Governance](/docs/governance/property/specification) を参照。 ## リファインメント 初回探索の後、`buying_mode: "refine"` を使って特定のプロダクトやプロポーザルを反復できます。`refine` 配列は変更依頼のリストで、各エントリはスコープとバイヤーが求める内容を宣言します。セラーは更新された価格と設定を持つプロダクトを返し、各依頼を `refinement_applied` で確認します。 完全なウォークスルー(スコープタイプ、アクションの意味論、セラーのレスポンス、よくあるパターン)は [Refinement ガイド](/docs/media-buy/product-discovery/refinement) を参照してください。パラメータ形状は上記の [Refine 配列](#refine-配列) セクションで定義されています。 最小の例: ```json test=false theme={null} { "buying_mode": "refine", "refine": [ { "scope": "request", "ask": "more video, less display" }, { "scope": "product", "product_id": "prod_premium_video", "ask": "add 16:9 format option" }, { "scope": "product", "product_id": "prod_display_run_of_site", "action": "omit" }, { "scope": "proposal", "proposal_id": "prop_awareness_q2", "ask": "reallocate display budget to video" } ], "filters": { "start_date": "2026-04-01", "end_date": "2026-04-30", "budget_range": { "min": 200000, "max": 200000, "currency": "USD" } } } ``` 送信前に知っておくべき主なルール: * **`refine` は `refine` モードでのみ有効。** このフィールドを `brief` または `wholesale` モードで含むリクエストは `INVALID_REQUEST` で拒否されます。 * **フィルターは絶対値**でありデルタではない。適用したいフィルターのフルセットを常に送信すること。 * **プロポーザルはステータスで操作可能。** `proposal_status: "draft"` は作成前に finalize が必要。`proposal_status: "committed"` は `expires_at` 前に `create_media_buy(proposal_id)` で実行可能。ステータスがなければレガシーの購入可能状態。 * **プロポーザルはエフェメラル。** プロポーザルには通常 `expires_at` タイムスタンプが含まれます。期限切れ後、セラーは `PROPOSAL_EXPIRED` を返します。 * **プロダクト ID は安定したカタログ識別子。** カスタムプロダクト(`is_custom: true`)には `expires_at` タイムスタンプがある場合があり、その後のリファインは `PRODUCT_NOT_FOUND` を返します。 ## Error Handling | Error Code | Description | Resolution | | ---------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | `AUTH_MISSING` | 認証情報が提示されていない | auth ヘッダーで認証情報を提供する | | `AUTH_INVALID` | 認証情報が拒否された(期限切れ/失効) | 人による認証情報のローテーションが必要。自動リトライしない | | `INVALID_REQUEST` | ブリーフが長すぎるか、フィルターが不正 | リクエストパラメーターを確認する | | `PRODUCT_NOT_FOUND` | 1 つ以上の参照プロダクト ID が未知または期限切れ | 無効な ID を削除して再試行するか、`brief` リクエストで再探索する | | `PROPOSAL_EXPIRED` | 参照したプロポーザル ID が `expires_at` タイムスタンプを過ぎている | 新しい `brief` または `wholesale` リクエストで再探索する | | `PROPOSAL_NOT_FOUND` | 参照した `proposal_id` がセラーにとって未知(finalize されていない、テナント違い、キャッシュから追い出された) | `refine` モードで `action: 'finalize'` を指定して `get_products` を再発行し、現在の proposal\_id を取得する | | `MULTI_FINALIZE_UNSUPPORTED` | `refine[]` が複数の `action: 'finalize'` エントリを運んだが、セラーがアトミックな複数プロポーザルのコミットを保証できない | 単一プロポーザルの finalize 呼び出しを順に実行——`get_products` 呼び出しごとに finalize エントリを1つ | | `POLICY_VIOLATION` | 広告主に対してカテゴリがブロックされている | ポリシーレスポンスメッセージで詳細を確認する | ### Authentication Comparison 認証あり・なしのアクセスの違いを確認します。 ```javascript JavaScript theme={null} import { testAgent, testAgentNoAuth } from '@adcp/sdk/testing'; // WITH authentication - full catalog with pricing const fullCatalog = await testAgent.getProducts({ buying_mode: 'brief', brief: 'Premium CTV inventory for brand awareness', brand: { domain: 'acmecorp.com' } }); if (!fullCatalog.success) { throw new Error(`Failed to get products: ${fullCatalog.error}`); } console.log(`With auth: ${fullCatalog.data.products.length} products`); console.log(`First product pricing: ${fullCatalog.data.products[0].pricing_options.length} options`); // WITHOUT authentication - limited public catalog const publicCatalog = await testAgentNoAuth.getProducts({ buying_mode: 'brief', brief: 'Premium CTV inventory for brand awareness', brand: { domain: 'acmecorp.com' } }); if (!publicCatalog.success) { throw new Error(`Failed to get products: ${publicCatalog.error}`); } console.log(`Without auth: ${publicCatalog.data.products.length} products`); console.log(`First product pricing: ${publicCatalog.data.products[0].pricing_options?.length || 0} options`); ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent, test_agent_no_auth async def compare_auth(): # WITH authentication - full catalog with pricing full_catalog = await test_agent.simple.get_products( buying_mode='brief', brief='Premium CTV inventory for brand awareness', brand={ 'domain': 'acmecorp.com' } ) print(f"With auth: {len(full_catalog['products'])} products") print(f"First product pricing: {len(full_catalog['products'][0]['pricing_options'])} options") # WITHOUT authentication - limited public catalog public_catalog = await test_agent_no_auth.simple.get_products( buying_mode='brief', brief='Premium CTV inventory for brand awareness', brand={ 'domain': 'acmecorp.com' } ) print(f"Without auth: {len(public_catalog['products'])} products") print(f"First product pricing: {len(public_catalog['products'][0].get('pricing_options', []))} options") asyncio.run(compare_auth()) ``` **主な違い:** * **プロダクト数**: 認証ありのアクセスはプライベート/カスタムオファリングを含む多くのプロダクトを返す * **価格情報**: 認証ありのリクエストのみ詳細な価格オプション(CPM、CPCV など)を受け取れる * **ターゲティング詳細**: カスタムターゲティング機能は認証ユーザーに限定される場合があります * **レート制限**: 認証なしのリクエストはレート制限が低い ## Authentication Behavior * **認証情報なし**: 制限された公開プロダクト結果を返します。価格なし、カスタムオファリングなし * **認証情報あり**: 価格とカスタムプロダクトを含む完全なプロダクト結果を返す 詳細は [Authentication Guide](/docs/building/by-layer/L2/authentication) を参照。 ## Asynchronous Operations ほとんどのプロダクト検索は即時完了するが、一部のシナリオでは非同期処理が必要になります。その場合、`completed` 以外のステータスを受け取ります。`task_id` を持つ `submitted` レスポンスは常に `get_task_status`(レガシーの `tasks/get`)でポーリング可能です。`push_notification_config` はバックグラウンドワークフロー向けに Webhook 通知を追加します。 #### SDK でのステータス処理 ```typescript theme={null} const initial = await agent.getProducts(params); const final = initial.status === 'submitted' ? await initial.submitted!.waitForCompletion(30000) : initial; if (final.status === 'failed') { throw new Error(final.error?.message ?? 'get_products failed'); } if (final.status !== 'completed') { throw new Error(`Unhandled get_products status: ${final.status}`); } for (const product of final.products) { console.log(product.name); } ``` ### 非同期処理が発生するケース 以下の状況でプロダクト検索に非同期処理が必要になる場合があります。 * **複雑な検索**: 複数のインベントリソースをまたぐ検索やカスタムキュレーション * **追加確認が必要**: ブリーフが曖昧でシステムが追加情報を必要とします * **カスタムプロダクト**: 人間のレビューが必要なオーダーメイドのプロダクトパッケージ ### Async Status Flow #### 即時完了(最も一般的) ```json theme={null} POST /api/mcp/call_tool { "name": "get_products", "arguments": { "buying_mode": "brief", "brief": "CTV inventory for sports audience", "brand": { "domain": "acmecorp.com" } } } Response (200 OK): { "status": "completed", "message": "Found 3 products matching your requirements", "products": [...] } ``` #### Needs Clarification ブリーフが不明確な場合、システムは詳細情報を求める。 ```json theme={null} Response (200 OK): { "status": "input-required", "message": "I need a bit more information. What's your budget range and campaign duration?", "task_id": "task_789", "context_id": "ctx_123", "reason": "CLARIFICATION_NEEDED", "partial_results": [], "suggestions": ["$50K-$100K", "1 month", "Q1 2024"] } ``` 同じ `context_id` で会話を続ける: ```json theme={null} POST /api/mcp/continue { "context_id": "ctx_123", "message": "Budget is $75K for a 3-week campaign in March" } Response (200 OK): { "status": "completed", "message": "Perfect! Found 5 products within your budget", "products": [...] } ``` #### Complex Search (With Webhook and Polling) 深いインベントリ分析が必要な検索には、最終の完了/失敗通知のための Webhook を設定します。返される `task_id` は `get_task_status`(レガシーの `tasks/get`)によるポーリング用に有効なままです。 ```json theme={null} POST /api/mcp/call_tool { "name": "get_products", "arguments": { "buying_mode": "brief", "brief": "Premium inventory across all formats for luxury automotive brand", "brand": { "domain": "acmecorp.com" }, "push_notification_config": { "url": "https://buyer.com/webhooks/adcp/get_products", "authentication": { "schemes": ["Bearer"], "credentials": "secret_token_32_chars" } } } } Response (200 OK): { "status": "submitted", "message": "Custom curation queued; typical turnaround 10-30 minutes", "task_id": "task_456", "context_id": "ctx_123", "estimated_completion": "2025-01-22T10:30:00Z" } // 後で task_id で get_task_status/tasks/get をポーリングするか、https://buyer.com/webhooks/adcp/get_products に Webhook POST が届く { "task_id": "task_456", "task_type": "get_products", "status": "completed", "timestamp": "2025-01-22T10:30:00Z", "message": "Found 12 premium products across all formats", "result": { "products": [...] } } ``` #### Immediate Completion (Most Common) ```json theme={null} POST /api/a2a { "message": { "role": "user", "parts": [{ "kind": "data", "data": { "skill": "get_products", "parameters": { "buying_mode": "brief", "brief": "CTV inventory for sports audience", "brand": { "domain": "acmecorp.com" } } } }] } } Response (200 OK): { "id": "task_123", "contextId": "ctx_456", "artifact": { "kind": "data", "data": { "products": [...] } }, "status": { "state": "completed", "message": { "role": "agent", "parts": [{ "text": "Found 3 products matching your requirements" }] } } } ``` #### 追加確認が必要な場合 確認が必要な場合、SSE でリアルタイム更新を受け取ります。 ```json theme={null} // Initial response { "id": "task_789", "contextId": "ctx_123", "status": { "state": "input-required", "message": { "role": "agent", "parts": [ { "text": "I need a bit more information. What's your budget range and campaign duration?" }, { "data": { "reason": "CLARIFICATION_NEEDED", "suggestions": ["$50K-$100K", "1 month", "Q1 2024"] } } ] } } } // 追いメッセージを送る POST /api/a2a { "contextId": "ctx_123", "message": { "role": "user", "parts": [{ "text": "Budget is $75K for a 3-week campaign in March" }] } } // SSE 更新: タスク完了 { "id": "task_789", "contextId": "ctx_123", "artifact": { "kind": "data", "data": { "products": [...] } }, "status": { "state": "completed", "message": { "role": "agent", "parts": [{ "text": "Perfect! Found 5 products within your budget" }] } } } ``` #### 複雑な検索(Webhook 併用・ポーリング) A2A では、プッシュ通知はトランスポートレベルの `configuration.pushNotificationConfig` フィールドを使います。snake\_case の `push_notification_config` を skill パラメータ内に入れてはいけません。A2A のタスク ID はタスクのポーリング用に有効なままです。 ```json theme={null} POST /api/a2a { "message": { "role": "user", "parts": [{ "kind": "data", "data": { "skill": "get_products", "parameters": { "buying_mode": "brief", "brief": "Premium inventory across all formats for luxury automotive brand", "brand": { "domain": "acmecorp.com" } } } }] }, "configuration": { "pushNotificationConfig": { "url": "https://buyer.com/webhooks/a2a/get_products", "authentication": { "schemes": ["bearer"], "credentials": "secret_token_32_chars" } } } } Response (200 OK): { "id": "task_456", "contextId": "ctx_789", "status": { "state": "submitted", "message": { "role": "agent", "parts": [ { "text": "Custom curation queued; typical turnaround 10-30 minutes" }, { "data": { "estimated_completion": "2025-01-22T10:30:00Z" } } ] } } } // 後で A2A タスクをポーリングするか、https://buyer.com/webhooks/a2a/get_products に Webhook POST が届く { "id": "task_456", "contextId": "ctx_789", "artifact": { "kind": "data", "data": { "products": [...] } }, "status": { "state": "completed", "message": { "role": "agent", "parts": [ { "text": "Found 12 premium products across all formats" }, { "data": { "products": [...] } } ] }, "timestamp": "2025-01-22T10:30:00Z" } } ``` ### ステータス概要 | Status | 発生タイミング | 対応 | | ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `completed` | 検索が正常完了 | プロダクト結果を処理 | | `input-required` | ブリーフに追加確認が必要 | 質問に回答して続行 | | `working` | 複数ソースを検索中 | オープンな接続/トランスポートの進捗ストリームで待つ | | `submitted` | カスタムキュレーションがキュー入り | `task_id` で `get_task_status`(レガシーの `tasks/get`)をポーリング。`push_notification_config` / A2A `configuration.pushNotificationConfig` が受理された場合は Webhook 通知も待つ | | `failed` | 検索を完了できなかった | エラーメッセージを確認しブリーフ調整 | **注意:** 完全なステータス一覧は [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle) を参照。 **ほとんどの検索は即時完了します。** 非同期処理が必要なのは複雑なケースや追加入力が必要な場合のみ。 ## 次のステップ プロダクトを見つけたら: 1. **選択肢を確認**: プロダクト、価格、ターゲティング能力を比較 2. **メディアバイ作成**: [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) でキャンペーンを実行 3. **クリエイティブ準備**: [`list_creative_formats`](/docs/creative/task-reference/list_creative_formats) で要件を確認 4. **アセット提供**: ライブラリ対応のセラーには [`sync_creatives`](/docs/creative/task-reference/sync_creatives) を、インライン専用のセラーにはインラインの `packages[].creatives` を使用 ## さらに学ぶ * [Product Discovery Guide](/docs/media-buy/product-discovery/) - ブリーフとプロダクトの理解 * [Pricing Models](/docs/media-buy/advanced-topics/pricing-models) - CPM, CPCV, CPP の解説 * [Brief Expectations](/docs/media-buy/product-discovery/brief-expectations) - 効果的なブリーフの書き方 * [Media Products](/docs/media-buy/product-discovery/media-products) - プロダクト構造とフィールド # タスクリファレンス Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/task-reference/index AdCP メディアバイのタスクリファレンス — プロダクトディスカバリー、キャンペーン作成、配信レポート、クリエイティブ、オーディエンス、コンバージョントラッキングの全タスクを、スキーマと例とともに解説します。 すべての AdCP メディアバイタスクの完全なリファレンスです。各タスクは、広告ワークフローの特定部分を AI エージェントが自動化できるよう設計されています。 ## すべてのタスク概要 | タスク | 目的 | レスポンスタイム | フェーズ | | --------------------------------------------------------------------------------------------- | ------------------------------------ | -------- | ------------- | | [`get_products`](/docs/media-buy/task-reference/get_products) | 在庫を発見しプロダクトを絞り込む | 約60秒 | ディスカバリー | | [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) | 選択したプロダクトからキャンペーンを作成 | 数分〜数日 | メディアバイ | | [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) | キャンペーン設定と予算を変更 | 数分〜数日 | メディアバイ | | [`list_creative_formats`](/docs/creative/task-reference/list_creative_formats) | サポートされるクリエイティブ仕様を表示 | 約1秒 | ケイパビリティ | | [`sync_catalogs`](/docs/media-buy/task-reference/sync_catalogs) | カタログフィード(プロダクト、店舗、在庫)を同期 | 数分〜数日 | カタログ | | [`sync_creatives`](/docs/creative/task-reference/sync_creatives) | クリエイティブアセットをアップロード・管理 | 数分〜数日 | クリエイティブ | | [`list_creatives`](/docs/creative/task-reference/list_creatives) | フィルタリングでクリエイティブライブラリを照会 | 約1秒 | クリエイティブ | | [`get_media_buys`](/docs/media-buy/task-reference/get_media_buys) | メディアバイのステータス、クリエイティブ承認、配信スナップショットを取得 | 約1秒 | モニタリング | | [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) | パフォーマンスと配信データを取得 | 約60秒 | レポート | | [`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback) | 最適化のためのパフォーマンスシグナルを送信 | 約1秒 | 最適化 | | [`sync_event_sources`](/docs/media-buy/task-reference/sync_event_sources) | コンバージョントラッキング用のイベントソースを設定 | 約1秒 | コンバージョントラッキング | | [`log_event`](/docs/media-buy/task-reference/log_event) | アトリビューションのためのマーケティングイベントを送信 | 約1秒 | コンバージョントラッキング | | [`sync_audiences`](/docs/media-buy/task-reference/sync_audiences) | ファーストパーティ CRM オーディエンスをアップロード・管理 | 数分〜数日 | オーディエンス | ## レスポンスタイムのカテゴリ AdCP タスクは四つのレスポンスタイムのカテゴリに分かれます: ### 🟢 即時(約 1 秒) **単純な参照とイベント取り込み** * [`list_creative_formats`](/docs/creative/task-reference/list_creative_formats) - フォーマット仕様 * [`list_creatives`](/docs/creative/task-reference/list_creatives) - クリエイティブライブラリの照会 * [`get_media_buys`](/docs/media-buy/task-reference/get_media_buys) - メディアバイのステータスとクリエイティブ承認 * [`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback) - パフォーマンスシグナルの送信 * [`sync_event_sources`](/docs/media-buy/task-reference/sync_event_sources) - イベントソースの設定 * [`log_event`](/docs/media-buy/task-reference/log_event) - イベントの取り込み ### 🟡 処理中(約 60 秒) **バックエンドシステムを伴う AI/LLM 推論** * [`get_products`](/docs/media-buy/task-reference/get_products) - 自然言語のプロダクトディスカバリーと絞り込み * [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) - パフォーマンスデータの集計 ### 🟠 非同期(数分〜数日) **人による承認の可能性を伴う複雑な操作** * [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) - キャンペーンの作成と検証 * [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) - キャンペーンの変更 * [`sync_catalogs`](/docs/media-buy/task-reference/sync_catalogs) - カタログフィードの処理とレビュー * [`sync_creatives`](/docs/creative/task-reference/sync_creatives) - クリエイティブアセットの処理 * [`sync_audiences`](/docs/media-buy/task-reference/sync_audiences) - オーディエンスのマッチングと処理 ## ワークフロー別のタスク分類 ### アカウント管理 メディアバイを行う前に、セラーとの商業的関係を確立します。これらのタスクはすべてのベンダープロトコルで共有され、コマースプロトコルのセクションに存在します: * **[`sync_accounts`](/docs/accounts/tasks/sync_accounts)** - ブランド/オペレーターのペアと請求を宣言し、セラーがアカウントをプロビジョニングする * **[`list_accounts`](/docs/accounts/tasks/list_accounts)** - アカウントのステータスを確認し、アクティブな `account_id` の値を取得する ### ディスカバリーと計画 何が利用可能かを理解し、キャンペーンを計画するにはここから始めます。 * **[`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)** - エージェントのケイパビリティ、ポートフォリオ、サポートされる機能を発見する(プロトコルレベルのタスク) * **[`get_products`](/docs/media-buy/task-reference/get_products)** - 自然言語ブリーフを使う中核のディスカバリータスク * **[`list_creative_formats`](/docs/creative/task-reference/list_creative_formats)** - クリエイティブ要件を理解する ### メディアバイ管理 広告キャンペーンを作成し管理します。 * **[`create_media_buy`](/docs/media-buy/task-reference/create_media_buy)** - 発見したプロダクトからキャンペーンを作成する * **[`update_media_buy`](/docs/media-buy/task-reference/update_media_buy)** - 予算、ターゲティング、設定を変更する ### カタログ管理 プロダクトフィード、在庫、店舗データをセラーアカウントに同期します。 * **[`sync_catalogs`](/docs/media-buy/task-reference/sync_catalogs)** - プラットフォームのレビューと承認を伴ってカタログフィードをプッシュする ### クリエイティブ管理 クリエイティブアセットをライフサイクル全体にわたって扱います。 * **[`sync_creatives`](/docs/creative/task-reference/sync_creatives)** - エージェントがホストするクリエイティブライブラリにアセットをアップロードする * **[`list_creatives`](/docs/creative/task-reference/list_creatives)** - クリエイティブライブラリを検索・管理する(クリエイティブプロトコル) * **インラインの `packages[].creatives`** - セラーがクリエイティブライブラリなしで `inline_creative_management` を表明する場合に、パッケージスコープのクリエイティブ本体を添付または置換する ### パフォーマンスと最適化 キャンペーンのパフォーマンスをモニタリングし最適化します。 * **[`get_media_buys`](/docs/media-buy/task-reference/get_media_buys)** - キャンペーンのステータス、クリエイティブ承認、ほぼリアルタイムの配信スナップショットを確認する * **[`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery)** - レポートのために配信とパフォーマンス指標を追跡する * **[`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback)** - パブリッシャーの最適化のためにパフォーマンスシグナルを送信する ### コンバージョントラッキング イベントソースを設定し、アトリビューションのためにマーケティングイベントを送信します。 * **[`sync_event_sources`](/docs/media-buy/task-reference/sync_event_sources)** - セラーアカウント上のイベントソースを設定する * **[`log_event`](/docs/media-buy/task-reference/log_event)** - アトリビューションのためにマーケティングイベントを送信する ### オーディエンス管理 ターゲティングのためにファーストパーティ CRM オーディエンスをアップロードし管理します。 * **[`sync_audiences`](/docs/media-buy/task-reference/sync_audiences)** - ハッシュ化された顧客リストをアップロードし、マッチングステータスを確認する ## スキーマリファレンス すべてのタスクには、リクエストとレスポンスの JSON スキーマ定義が含まれます: * **リクエストスキーマ**: `/schemas/v3/media-buy/[task-name]-request.json` * **レスポンススキーマ**: `/schemas/v3/media-buy/[task-name]-response.json` **タスク管理**: すべての AdCP ドメインにまたがる非同期操作の追跡については、[タスクライフサイクル](/docs/building/by-layer/L3/task-lifecycle)を参照してください。 スキーマは、検証とツーリングのためにドキュメントサーバーを通じて実行時にアクセスできます。 ## 共通パターン ### タスク命名規約 タスク名は snake\_case を使い、メディアバイ全体で一貫して動詞優先のセマンティクスに従います: * `get_*`: 現在の状態またはスコープされたデータセットを取得する(例: `get_products`、`get_media_buys`、`get_media_buy_delivery`) * `list_*`: 任意のフィルタリングを伴ってコレクションを列挙する(例: `list_creative_formats`、`list_creatives`) * `create_*`: 新しいリソースを作成する(`create_media_buy`) * `update_*`: 既存のリソースに部分更新を適用する(`update_media_buy`) * `sync_*`: 外部の状態をアップサート的な振る舞いでセラーシステムに突き合わせる(`sync_catalogs`、`sync_creatives`、`sync_event_sources`) * `log_*`: 追記専用のイベントレコードを取り込む(`log_event`) * `provide_*`: 最適化またはフィードバックのシグナルを送信する(`provide_performance_feedback`) ### エラーハンドリング すべてのタスクは、次を伴う一貫したエラーパターンに従います: * 異なるエラータイプに対する HTTP ステータスコード * コンテキストを持つ構造化されたエラーメッセージ * 一時的な失敗に対するリトライのガイダンス ### 認証 タスクは次を通じて適切な認証を要求します: * サービス間呼び出しのための API キー * マルチテナント操作のための認証済みエージェントとアカウントのスコープ * リソースアクセスのための権限検証 ### 非同期オペレーション 長時間実行のタスクは次を提供します: * 操作 ID を伴う即時レスポンス * 進捗のためのステータスポーリングエンドポイント * 完了のためのウェブフック通知 ## はじめに 1. **ケイパビリティを発見する**: [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) を使って、エージェントが何をサポートするかを理解する 2. **在庫を見つける**: [`get_products`](/docs/media-buy/task-reference/get_products) を使って、関連する在庫を見つける 3. **プロダクトを絞り込む**: `buying_mode: "refine"` を指定して [`get_products`](/docs/media-buy/task-reference/get_products) を再呼び出しし、予算、価格、ターゲティングを反復する 4. **フォーマットを理解する**: 要件について [`list_creative_formats`](/docs/creative/task-reference/list_creative_formats) を確認する 5. **カタログを同期する**: [`sync_catalogs`](/docs/media-buy/task-reference/sync_catalogs) を使って、プロダクトフィードをアカウントにプッシュする 6. **クリエイティブを供給する**: ライブラリを持つセラーには [`sync_creatives`](/docs/creative/task-reference/sync_creatives) を、インライン専用のセラーにはインラインの `packages[].creatives` を使う 7. **キャンペーンを作成する**: 選択したプロダクトで [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) を使う 8. **運用状態を確認する**: ステータス、承認、不足フォーマットについて [`get_media_buys`](/docs/media-buy/task-reference/get_media_buys) を使う 9. **パフォーマンスをモニタリングする**: [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) でレポート指標を追跡する レポートと照合については、[`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) を権威ある請求グレードの情報源として扱います。[`get_media_buys`](/docs/media-buy/task-reference/get_media_buys) のスナップショットは運用モニタリングにのみ使います。 ## 関連ドキュメント * **[プロダクトディスカバリー](/docs/media-buy/product-discovery/)** - 自然言語による在庫の発見 * **[メディアバイ](/docs/media-buy/media-buys/)** - キャンペーンのライフサイクル管理 * **[クリエイティブ](/docs/media-buy/creatives/)** - クリエイティブアセットの管理 * **[高度なトピック](/docs/media-buy/advanced-topics/)** - ターゲティング、セキュリティ、アーキテクチャ # log_event Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/task-reference/log_event log_event タスク — コンバージョンおよびマーケティングイベントをバッチで AdCP セラーに送信します。アトリビューション、キャンペーン最適化、ROAS 計測、テストイベントをサポート。 アトリビューションと最適化のためにコンバージョンまたはマーケティングイベントを送信します。バッチ送信、テストイベント、および部分的な失敗レポートをサポートしています。 **レスポンス時間**: 約1秒(イベントは処理キューに追加されます) **リクエストスキーマ**: [`/schemas/v3/media-buy/log-event-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/log-event-request.json) **レスポンススキーマ**: [`/schemas/v3/media-buy/log-event-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/log-event-response.json) ## クイックスタート 購入イベントをログに記録する: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { LogEventResponseSchema } from "@adcp/sdk"; const result = await testAgent.logEvent({ event_source_id: "website_pixel", events: [ { event_id: "evt_purchase_12345", event_type: "purchase", event_time: "2026-01-15T14:30:00Z", action_source: "website", event_source_url: "https://www.example.com/checkout/confirm", user_match: { click_id: "abc123def456", click_id_type: "gclid", }, custom_data: { value: 149.99, currency: "USD", order_id: "order_98765", num_items: 3, }, }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } // Validate response against schema const validated = LogEventResponseSchema.parse(result.data); // Check for operation-level errors first (discriminated union) if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("events_received" in validated) { console.log(`Received: ${validated.events_received}, Processed: ${validated.events_processed}`); if (validated.match_quality !== undefined) { console.log(`Match quality: ${(validated.match_quality * 100).toFixed(0)}%`); } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.log_event( event_source_id='website_pixel', events=[{ 'event_id': 'evt_purchase_12345', 'event_type': 'purchase', 'event_time': '2026-01-15T14:30:00Z', 'action_source': 'website', 'event_source_url': 'https://www.example.com/checkout/confirm', 'user_match': { 'click_id': 'abc123def456', 'click_id_type': 'gclid' }, 'custom_data': { 'value': 149.99, 'currency': 'USD', 'order_id': 'order_98765', 'num_items': 3 } }] ) # Check for operation-level errors first if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") print(f"Received: {result.events_received}, Processed: {result.events_processed}") if hasattr(result, 'match_quality') and result.match_quality is not None: print(f"Match quality: {result.match_quality * 100:.0f}%") asyncio.run(main()) ``` ## リクエストパラメータ | パラメータ | 型 | 必須 | 説明 | | ----------------- | ------------------------------------------------------ | --- | ---------------------------------------- | | `event_source_id` | string | はい | `sync_event_sources` でアカウントに設定されたイベントソース | | `events` | [Event](/docs/media-buy/conversion-tracking/#event)\[] | はい | ログに記録するイベント(最小1件、最大10,000件) | | `test_event_code` | string | いいえ | 本番データに影響を与えずに検証するためのテストイベントコード | ### Event オブジェクト | フィールド | 型 | 必須 | 説明 | | ------------------- | ------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------ | | `event_id` | string | はい | 重複排除のための一意の識別子(event\_type + event\_source\_id のスコープ内)。最大256文字。 | | `event_type` | [EventType](/docs/media-buy/conversion-tracking/#event-types) | はい | 標準イベントタイプ(例:`purchase`、`lead`、`add_to_cart`) | | `event_time` | date-time | はい | イベントが発生した時刻の ISO 8601 タイムスタンプ | | `user_match` | [UserMatch](/docs/media-buy/conversion-tracking/#user-match) | いいえ | アトリビューションマッチング用のユーザー識別子 | | `custom_data` | [CustomData](/docs/media-buy/conversion-tracking/#custom-data) | いいえ | イベント固有のデータ(金額、通貨、アイテムなど) | | `action_source` | [ActionSource](/docs/media-buy/conversion-tracking/#action-sources) | いいえ | イベントが発生した場所(`website`、`app`、`in_store` など) | | `surface` | [EventSurface](/docs/media-buy/conversion-tracking/#event-surfaces) | いいえ | イベントの構造化されたサーフェスコンテキスト(自社チャンネル、プロフィール、フィード、ポッドキャスト、ニュースレターリスト、ウェブサイト、アプリ、店舗など) | | `event_source_url` | uri | いいえ | イベントが発生した URL(action\_source が `website` の場合は必須) | | `custom_event_name` | string | いいえ | カスタムイベントの名前(event\_type が `custom` の場合に使用) | ### User Match オブジェクト `uids`、`hashed_email`、`hashed_phone`、`click_id`、または `client_ip` + `client_user_agent` のうち少なくとも1つが必須: | フィールド | 型 | 説明 | | ------------------- | ------ | ---------------------------------------------------------- | | `uids` | UID\[] | ユニバーサル ID の値(`rampid`、`id5`、`uid2`、`euid`、`pairid`、`maid`) | | `hashed_email` | string | 小文字・トリミング済みのメールアドレスの SHA-256 ハッシュ(64文字の16進数) | | `hashed_phone` | string | E.164 形式の電話番号の SHA-256 ハッシュ(64文字の16進数) | | `click_id` | string | プラットフォームのクリック識別子(fbclid、gclid、ttclid など) | | `click_id_type` | string | クリック識別子の種類 | | `client_ip` | string | 確率的マッチング用のクライアント IP アドレス | | `client_user_agent` | string | 確率的マッチング用のクライアントユーザーエージェント文字列 | **ハッシュ化:** ハッシュ化された識別子は SHA-256 の16進数文字列(64文字、小文字)でなければなりません。ハッシュ化前に正規化すること: メールアドレスは小文字かつ前後の空白をトリミング、電話番号は E.164 形式(例:`+12065551234`)にします。 ### Custom Data オブジェクト | フィールド | 型 | 説明 | | ------------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `value` | number | イベントの金額 | | `currency` | string | ISO 4217 通貨コード(例:`USD`、`EUR`、`GBP`) | | `order_id` | string | 注文またはトランザクションの一意の識別子 | | `content_ids` | string\[] | 商品またはコンテンツの識別子。カタログ駆動型キャンペーンでは、カタログの `content_id_type`(SKU、GTIN、求人 ID など)に対応します。[カタログアイテムアトリビューション](/docs/media-buy/conversion-tracking#catalog-item-attribution)を参照。 | | `content_type` | string | コンテンツのカテゴリ(商品、サービスなど) | | `num_items` | integer | イベント内のアイテム数 | | `progress_percent` | number | 到達したコンテンツ進捗の割合。主に `watch_milestone` イベント向け | | `progress_seconds` | number | 到達したコンテンツ進捗の長さ(秒)。主に時間ベースの `watch_milestone` イベント向け | | `contents` | Content\[] | アイテムごとの詳細(id、数量、価格、ブランド) | ## レスポンス **成功レスポンス:** * `events_received` - 受信したイベント数 * `events_processed` - 正常にキューに追加されたイベント数 * `partial_failures` - バリデーションに失敗したイベント(event\_id、コード、メッセージを含む) * `warnings` - 非致命的な問題(マッチ品質が低い、フィールドが欠落しているなど) * `match_quality` - 全体的なマッチ品質スコア(0.0 〜 1.0) **エラーレスポンス:** * `errors` - オペレーションレベルのエラーの配列(無効なイベントソース、認証失敗など) **注意:** レスポンスは判別共用体(discriminated union)を使用しており、成功フィールドまたは errors のどちらか一方のみが返されます。部分的な失敗は、成功レスポンス内にイベントごとに報告されます。 ## よくあるシナリオ ### クリエイターエンゲージメントイベント クリエイターまたはコンテンツのエンゲージメントイベントを、設定済みの自社プロパティのソースに対してログします。プロパティを識別するには `surface` を使い、視聴のしきい値は `custom_data.progress_percent` または `custom_data.progress_seconds` に入れます。 ```json test=false theme={null} { "event_source_id": "creator_channel", "events": [ { "event_id": "evt_watch_001", "event_type": "watch_milestone", "event_time": "2026-01-16T11:05:00Z", "action_source": "system_generated", "surface": { "category": "owned_property", "property_type": "channel", "namespace": "video_platform", "property_id": "channel_123" }, "user_match": { "uids": [{ "type": "uid2", "value": "CreatorViewer456" }] }, "custom_data": { "content_ids": ["episode_001"], "progress_percent": 75 } } ] } ``` 無料で継続的なオプトインには `event_type: "follow"` を使います。`event_type: "subscribe"` は有料の購読または有料のメンバーシップに限定します。 ### バッチイベント 複数のイベントを1つのリクエストで送信します: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { LogEventResponseSchema } from "@adcp/sdk"; const result = await testAgent.logEvent({ event_source_id: "website_pixel", events: [ { event_id: "evt_purchase_001", event_type: "purchase", event_time: "2026-01-15T10:00:00Z", action_source: "website", user_match: { hashed_email: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2", uids: [{ type: "uid2", value: "AbC123XyZ..." }], }, custom_data: { value: 89.99, currency: "USD", order_id: "order_001", }, }, { event_id: "evt_lead_002", event_type: "lead", event_time: "2026-01-15T11:30:00Z", action_source: "website", user_match: { hashed_email: "f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5", click_id: "abc123def456", click_id_type: "fbclid", }, }, { event_id: "evt_cart_003", event_type: "add_to_cart", event_time: "2026-01-15T12:15:00Z", action_source: "app", user_match: { uids: [{ type: "rampid", value: "Def456Ghi..." }], }, custom_data: { content_ids: ["SKU-1234", "SKU-5678"], num_items: 2, value: 45.00, currency: "USD", }, }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = LogEventResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("events_received" in validated) { console.log(`${validated.events_processed}/${validated.events_received} events processed`); if (validated.partial_failures?.length) { for (const failure of validated.partial_failures) { console.log(` Failed: ${failure.event_id} - ${failure.message}`); } } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.log_event( event_source_id='website_pixel', events=[ { 'event_id': 'evt_purchase_001', 'event_type': 'purchase', 'event_time': '2026-01-15T10:00:00Z', 'action_source': 'website', 'user_match': { 'uids': [{'type': 'uid2', 'value': 'AbC123XyZ...'}] }, 'custom_data': { 'value': 89.99, 'currency': 'USD', 'order_id': 'order_001' } }, { 'event_id': 'evt_lead_002', 'event_type': 'lead', 'event_time': '2026-01-15T11:30:00Z', 'action_source': 'website', 'user_match': { 'click_id': 'abc123def456', 'click_id_type': 'fbclid' } }, { 'event_id': 'evt_cart_003', 'event_type': 'add_to_cart', 'event_time': '2026-01-15T12:15:00Z', 'action_source': 'app', 'user_match': { 'uids': [{'type': 'rampid', 'value': 'Def456Ghi...'}] }, 'custom_data': { 'content_ids': ['SKU-1234', 'SKU-5678'], 'num_items': 2, 'value': 45.00, 'currency': 'USD' } } ] ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") print(f"{result.events_processed}/{result.events_received} events processed") if hasattr(result, 'partial_failures') and result.partial_failures: for failure in result.partial_failures: print(f" Failed: {failure.event_id} - {failure.message}") asyncio.run(main()) ``` ### テストイベント 本番データに影響を与えずにイベント連携を検証する: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { LogEventResponseSchema } from "@adcp/sdk"; const result = await testAgent.logEvent({ event_source_id: "website_pixel", test_event_code: "TEST_12345", events: [ { event_id: "test_evt_001", event_type: "purchase", event_time: new Date().toISOString(), action_source: "website", event_source_url: "https://www.example.com/checkout", user_match: { click_id: "test_click_abc", click_id_type: "gclid", }, custom_data: { value: 99.99, currency: "USD", }, }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = LogEventResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("events_received" in validated) { console.log("Test event sent successfully"); if (validated.warnings?.length) { console.log("Warnings:", validated.warnings); } } ``` ```python Python theme={null} import asyncio from datetime import datetime, timezone from adcp.testing import test_agent async def main(): result = await test_agent.simple.log_event( event_source_id='website_pixel', test_event_code='TEST_12345', events=[{ 'event_id': 'test_evt_001', 'event_type': 'purchase', 'event_time': datetime.now(timezone.utc).isoformat(), 'action_source': 'website', 'event_source_url': 'https://www.example.com/checkout', 'user_match': { 'click_id': 'test_click_abc', 'click_id_type': 'gclid' }, 'custom_data': { 'value': 99.99, 'currency': 'USD' } }] ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") print('Test event sent successfully') if hasattr(result, 'warnings') and result.warnings: print(f"Warnings: {result.warnings}") asyncio.run(main()) ``` テストイベントはセラーのテストイベント UI に表示されるが、本番のアトリビューションやレポートには影響しません。 ### 店舗内コンバージョン CRM データを使用してオフラインコンバージョンを報告する: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { LogEventResponseSchema } from "@adcp/sdk"; const result = await testAgent.logEvent({ event_source_id: "crm_import", events: [ { event_id: "store_txn_20260115_001", event_type: "purchase", event_time: "2026-01-15T16:45:00Z", action_source: "in_store", user_match: { uids: [{ type: "rampid", value: "XyZ789AbC..." }], }, custom_data: { value: 250.0, currency: "USD", order_id: "POS-2026-0115-001", contents: [ { id: "SKU-JACKET-L", quantity: 1, price: 189.0 }, { id: "SKU-SCARF-01", quantity: 1, price: 61.0 }, ], }, }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = LogEventResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("events_received" in validated) { console.log(`In-store events processed: ${validated.events_processed}`); } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.log_event( event_source_id='crm_import', events=[{ 'event_id': 'store_txn_20260115_001', 'event_type': 'purchase', 'event_time': '2026-01-15T16:45:00Z', 'action_source': 'in_store', 'user_match': { 'uids': [{'type': 'rampid', 'value': 'XyZ789AbC...'}] }, 'custom_data': { 'value': 250.00, 'currency': 'USD', 'order_id': 'POS-2026-0115-001', 'contents': [ {'id': 'SKU-JACKET-L', 'quantity': 1, 'price': 189.00}, {'id': 'SKU-SCARF-01', 'quantity': 1, 'price': 61.00} ] } }] ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") print(f"In-store events processed: {result.events_processed}") asyncio.run(main()) ``` ## イベントの重複排除 イベントは `event_id` + `event_type` + `event_source_id` の組み合わせで重複排除されます。同じイベントを複数回送信しても安全で、重複は無視されます。 リトライをまたいで安定した `event_id` の値を選ぶこと: * トランザクション ID: `"order_98765"` * 複合キー: `"purchase_user123_20260115"` * UUID: `"550e8400-e29b-41d4-a716-446655440000"` ## エラーハンドリング | エラーコード | 説明 | 対処方法 | | --------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | `REFERENCE_NOT_FOUND` | イベントソースが設定されていない、またはアクセスできない(`error.field` = `event_source_id`) | 先に `sync_event_sources` を実行する | | `INVALID_EVENT_TYPE` | 認識されていない、または許可されていないイベントタイプ | イベントソースの `event_types` 設定を確認する | | `INVALID_EVENT_TIME` | イベント時刻が過去または未来に遠すぎる | セラーのアトリビューションウィンドウ内のタイムスタンプを使用する | | `MISSING_USER_MATCH` | ユーザー識別子が提供されていない | uids、hashed\_email、hashed\_phone、click\_id、または client\_ip + client\_user\_agent のうち少なくとも1つを含める | | `BATCH_TOO_LARGE` | イベントが10,000件を超えている | 小さいバッチに分割する | | `RATE_LIMITED` | リクエストが多すぎる | 指数バックオフで待機してリトライする | ## ベストプラクティス 1. **先にソースを設定する** — イベントを送信する前に必ず `sync_event_sources` を実行します。設定されていないソースへのイベントは拒否されます。 2. **user\_match を含める** — ユーザー識別子のないイベントはアトリビューションできません。利用可能な最も強力な識別子を提供すること: ハッシュ化されたメール・電話番号 > UID > クリック ID > IP/UA。マッチ率を最大化するために、複数の識別子タイプを可能な限り送信します。 3. **最初はテストイベントを使用する** — 連携の検証時は `test_event_code` を設定し、本番データに影響を与えずにイベントが正しく表示されることを確認します。 4. **可能な限りバッチ処理する** — API 呼び出し回数を減らすために、1リクエストあたり最大10,000件のイベントを送信します。バッチ内のイベントはそれぞれ独立して処理されます。 5. **value と currency を含める** — 購入イベントでは、ROAS レポートと最適化を有効にするために常に `custom_data.value` と `custom_data.currency` を含めます。 6. **安定したイベント ID を使用する** — ランダムな UUID ではなく、決定論的なイベント ID(注文番号、トランザクション ID)を使用します。これにより重複カウントなしに安全なリトライが可能になります。 7. **イベントを迅速に送信する** — できる限りリアルタイムに近いタイミングでイベントをログに記録します。セラーのアトリビューションウィンドウを超えたイベントはマッチングされない場合があります。 ## 次のステップ * [コンバージョントラッキング](/docs/media-buy/conversion-tracking/) — データモデル、最適化目標、エンドツーエンドのフロー * [sync\_event\_sources](/docs/media-buy/task-reference/sync_event_sources) — イベントをログに記録する前にイベントソースを設定します * [create\_media\_buy](/docs/media-buy/task-reference/create_media_buy#campaign-with-conversion-optimization) — パッケージに最適化目標を設定します * [get\_media\_buy\_delivery](/docs/media-buy/task-reference/get_media_buy_delivery) — デリバリーレポートでコンバージョン指標を監視します # provide_performance_feedback Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/task-reference/provide_performance_feedback パフォーマンスの成果をパブリッシャーと共有し、データドリブンな最適化や配信改善を可能にします。 **応答時間**: 約 5 秒(データ取り込み) **リクエストスキーマ**: [`https://adcontextprotocol.org/schemas/v3/media-buy/provide-performance-feedback-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/provide-performance-feedback-request.json) **レスポンススキーマ**: [`https://adcontextprotocol.org/schemas/v3/media-buy/provide-performance-feedback-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/provide-performance-feedback-response.json) ## リクエストパラメーター | Parameter | Type | Required | Description | | -------------------- | -------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `media_buy_id` | string | Yes | セラー側のメディアバイ ID | | `measurement_period` | object | Yes | パフォーマンス測定期間 | | `performance_index` | number | Yes | 正規化されたスコア(0.0 = 価値なし、1.0 = 想定どおり、>1.0 = 想定超え) | | `package_id` | string | No | メディアバイ内の特定パッケージ(パッケージ単位のフィードバックの場合) | | `creative_id` | string | No | 特定クリエイティブ(クリエイティブ単位のフィードバックの場合) | | `metric_type` | string | **非推奨** | レガシーの自由形式メトリクス列挙(メトリクス・検証・アトリビューションを一つのリストに混在)。新規実装は代わりに `metric` を使用すべき(SHOULD)。スキーマレベルでは必須ではなくなり、1 マイナーの後方互換のため保持され、次のメジャーで削除される。マッピングは [metric-type 移行テーブル](https://adcontextprotocol.org/schemas/v3/enums/metric-type.json) を参照。 | | `metric` | object | No | このフィードバック行が対象とするメトリクス。`committed_metrics` と対称な `(scope, metric_id, qualifier)` の行形状を使用。非推奨の `metric_type` より優先。標準メトリクス: `{ scope: "standard", metric_id: "viewable_rate", qualifier: { viewability_standard: "mrc" } }`。ベンダーメトリクス: `{ scope: "vendor", vendor: { domain: "nielsen.com" }, metric_id: "brand_lift" }`。**総体的なフィードバックの場合は完全に省略** ——特定のメトリクスなしにキャンペーンの不振を示すトレーダーは、`performance_index` とレスポンスの説明でシグナルを伝えるため `metric` 行は不要。メトリクス固有のフィードバックでは、消費側がルーティングできるよう `metric` を設定すべき(SHOULD)。 | | `vendor` | [BrandRef](/docs/reference/glossary#b) | No | このフィードバックを生成したベンダー。`feedback_source` が `third_party_measurement` または `verification_partner` で、かつ単一の証明ベンダーが存在する場合は設定すべき(SHOULD)。ブレンドされた出力(MMM の混合、ベンダーをまたぐマルチタッチアトリビューション、クリーンルームが測定ソースでないクリーンルーム出力)では省略する。`buyer_attribution` と `platform_analytics` では任意。**ネストされた `metric.vendor` フィールドとは別物** ——このトップレベルの `vendor` はフィードバックの*ソース*(それを生成する当事者)を識別し、`metric.vendor`(ベンダースコープの `metric` エントリに存在する場合)は*メトリクスを定義するベンダー*を識別する。多くの場合は同じだが、異なることもある。 | | `feedback_source` | string | No | パフォーマンスデータのソース(デフォルト: "buyer\_attribution") | ## レスポンス(メッセージ) レスポンスには人間が読めるメッセージが含まれ、次を行います。 * フィードバックの受領を確認 * 提供されたパフォーマンス水準の要約 * フィードバックの最適化への活用方法を説明 * 次のアクションや推奨事項を提示 メッセージの戻し方はプロトコルごとに異なります。 * **MCP**: JSON レスポンスの `message` フィールドとして返却 * **A2A**: アーティファクト内のテキストパートとして返却 ## レスポンス(ペイロード) ```json theme={null} { "success": "boolean", "message": "string" } ``` ### フィールド説明 * **success**: パフォーマンスフィードバックの受領に成功したか * **message**: フィードバック処理に関する任意のメッセージ ## プロトコル別の例 AdCP のペイロードはプロトコル間で同一です。リクエスト/レスポンスのラッパーのみが異なります。 ### MCP リクエスト ```json theme={null} { "tool": "provide_performance_feedback", "arguments": { "media_buy_id": "gam_1234567890", "measurement_period": { "start": "2024-01-15T00:00:00Z", "end": "2024-01-21T23:59:59Z" }, "performance_index": 1.35, "metric_type": "conversion_rate" } } ``` ### MCP レスポンス ```json theme={null} { "message": "Performance feedback received for campaign gam_1234567890. The 35% above-expected conversion rate will be used to optimize future delivery. Next optimization cycle runs tonight at midnight UTC.", "success": true } ``` ### A2A リクエスト #### 自然言語での呼び出し ```javascript theme={null} await a2a.send({ message: { parts: [{ kind: "text", text: "The campaign gam_1234567890 had a conversion rate 35% above expectations for the week of January 15-21. Please use this to optimize future delivery." }] } }); ``` #### スキルを明示して呼び出す ```javascript theme={null} await a2a.send({ message: { parts: [{ kind: "data", data: { skill: "provide_performance_feedback", parameters: { media_buy_id: "gam_1234567890", measurement_period: { start: "2024-01-15T00:00:00Z", end: "2024-01-21T23:59:59Z" }, performance_index: 1.35, metric_type: "conversion_rate" } } }] } }); ``` ### A2A レスポンス A2A では結果をアーティファクトとして返します。 ```json theme={null} { "artifacts": [{ "artifactId": "artifact-perf-feedback-abc789", "name": "performance_feedback_confirmation", "parts": [ { "kind": "text", "text": "Performance feedback received for campaign gam_1234567890. The 35% above-expected conversion rate will be used to optimize future delivery. Next optimization cycle runs tonight at midnight UTC." }, { "kind": "data", "data": { "success": true } } ] }] } ``` ### 主な違い * **MCP**: 引数付きのツール呼び出しを行い、フラットな JSON を返す * **A2A**: スキル呼び出しで入力を渡し、テキストとデータのパートを持つアーティファクトとして返す * **ペイロード**: A2A の `input` フィールドは MCP の `arguments` と同一構造 ## シナリオ ### 例 1: キャンペーンレベルのフィードバック #### リクエスト ```json theme={null} { "$schema": "/schemas/media-buy/provide-performance-feedback-request.json", "idempotency_key": "c7d8e9f0-a1b2-4345-c678-345678901234", "media_buy_id": "gam_1234567890", "measurement_period": { "start": "2024-01-01T00:00:00Z", "end": "2024-01-31T23:59:59Z" }, "performance_index": 0.85, "metric_type": "brand_lift", "feedback_source": "third_party_measurement" } ``` #### レスポンス - 想定未達 **Message**: "Performance feedback received for campaign gam\_1234567890. The 15% below-expected brand lift suggests targeting refinement is needed. Our optimization algorithms will reduce spend on underperforming segments starting with the next cycle." **Payload**: ```json theme={null} { "success": true, "message": "Performance feedback processed successfully. Optimization algorithms updated." } ``` ### 例 2: パッケージ単位のフィードバック #### リクエスト ```json theme={null} { "$schema": "/schemas/media-buy/provide-performance-feedback-request.json", "idempotency_key": "d8e9f0a1-b2c3-4456-d789-456789012345", "media_buy_id": "meta_9876543210", "package_id": "pkg_social_feed", "measurement_period": { "start": "2024-02-01T00:00:00Z", "end": "2024-02-07T23:59:59Z" }, "performance_index": 2.1, "metric_type": "click_through_rate", "feedback_source": "buyer_attribution" } ``` #### レスポンス - 卓越した成果 **Message**: "Outstanding performance feedback for package pkg\_social\_feed! The 110% above-expected click-through rate indicates this audience segment is highly engaged. We'll increase allocation to similar inventory and audiences." **Payload**: ```json theme={null} { "success": true, "message": "Exceptional performance noted. Increasing allocation to similar segments." } ``` ### 例 3: クリエイティブ単位のフィードバック #### リクエスト ```json theme={null} { "$schema": "/schemas/media-buy/provide-performance-feedback-request.json", "idempotency_key": "e9f0a1b2-c3d4-4567-e890-567890123456", "media_buy_id": "ttd_5555555555", "creative_id": "creative_video_123", "measurement_period": { "start": "2024-02-01T00:00:00Z", "end": "2024-02-07T23:59:59Z" }, "performance_index": 0.65, "metric_type": "completion_rate", "feedback_source": "verification_partner" } ``` #### レスポンス - 低調なクリエイティブ **Message**: "Creative creative\_video\_123 shows 35% below-expected completion rate. Consider creative refresh or A/B testing alternative versions." **Payload**: ```json theme={null} { "success": true, "message": "Creative performance feedback recorded. Consider creative optimization." } ``` ### 例 4: 複数のパフォーマンスメトリクス 同じメディアバイについて複数のメトリクスをレポートするには、メトリクスタイプごとに 1 リクエストを送ります: #### リクエスト - ビューアビリティのフィードバック ```json theme={null} { "$schema": "/schemas/media-buy/provide-performance-feedback-request.json", "idempotency_key": "f0a1b2c3-d4e5-4678-f901-678901234567", "media_buy_id": "ttd_5555555555", "measurement_period": { "start": "2024-02-01T00:00:00Z", "end": "2024-02-14T23:59:59Z" }, "metric_type": "viewability", "performance_index": 1.15, "feedback_source": "verification_partner" } ``` #### リクエスト - 完了率のフィードバック ```json theme={null} { "$schema": "/schemas/media-buy/provide-performance-feedback-request.json", "idempotency_key": "a1b2c3d4-e5f6-4789-a012-789012345678", "media_buy_id": "ttd_5555555555", "measurement_period": { "start": "2024-02-01T00:00:00Z", "end": "2024-02-14T23:59:59Z" }, "metric_type": "completion_rate", "performance_index": 0.92, "feedback_source": "verification_partner" } ``` #### リクエスト - ブランドセーフティのフィードバック ```json theme={null} { "$schema": "/schemas/media-buy/provide-performance-feedback-request.json", "idempotency_key": "b2c3d4e5-f6a7-4890-b123-890123456789", "media_buy_id": "ttd_5555555555", "measurement_period": { "start": "2024-02-01T00:00:00Z", "end": "2024-02-14T23:59:59Z" }, "metric_type": "brand_safety", "performance_index": 1.05, "feedback_source": "verification_partner" } ``` ## パフォーマンスインデックスのスケール パフォーマンスインデックスはビジネス成果を正規化して伝えるための指標です。 * **0.0**: 価値が確認できません * **0.5**: 想定を大幅に下回る(-50%) * **1.0**: 想定どおり(0% 乖離) * **1.5**: 想定比 50% 上振れ * **2.0+**: 非常に優れた成果(100% 以上の上振れ) ### よく使われる metric\_type * **overall\_performance**: 全体的な成功度(デフォルト) * **conversion\_rate**: ポストクリック/ポストビューのコンバージョン * **brand\_lift**: ブランド認知・好意度向上 * **click\_through\_rate**: クリエイティブへのエンゲージメント * **completion\_rate**: 動画・音声の完了率 * **viewability**: ビューアブルインプレッション率 * **brand\_safety**: ブランドセーフティの準拠状況 * **cost\_efficiency**: 目標成果あたりのコスト ### feedback\_source の例 * **buyer\_attribution**: バイヤー自身の計測・アトリビューション * **third\_party\_measurement**: 第三者の計測パートナー * **platform\_analytics**: パブリッシャープラットフォームの分析 * **verification\_partner**: 検証ベンダー ## パブリッシャーがフィードバックを活用する方法 パブリッシャーはパフォーマンスインデックスを以下に活用します。 1. **ターゲティング最適化**: 高パフォーマンスのセグメント・オーディエンスへ配信をシフト 2. **インベントリ改善**: 価値の高い掲載面を特定し優先度を上げる 3. **価格調整**: 実績に基づいて CPM を更新 4. **アルゴリズム強化**: 実際の成果データで機械学習モデルを学習 5. **商品開発**: パフォーマンス傾向をもとにプロダクト定義を洗練 ## 利用上の注意 * パフォーマンスフィードバックは任意だが、最適化に大きく寄与します * フィードバックはキャンペーンまたはパッケージ単位で提供可能 * 同一期間に複数のインデックスを共有できる(バッチ送信は将来対応予定) * 効果はパブリッシャーのアルゴリズム成熟度に依存 * 処理は非同期で行われる。レスポンスでステータスを確認可能 * 過去のフィードバックは将来の配信パフォーマンス向上に役立つ ## プライバシーとデータ共有 * フィードバックの共有は任意で、バイヤーが制御します * 集計されたパフォーマンス傾向はプラットフォーム全体の改善に利用される場合があります * 個別キャンペーンの詳細はバイヤーとパブリッシャーの関係に限定されます * パブリッシャーは AdCP ドキュメントで明確なデータ利用ポリシーを提示すべき ## 実装ガイド ### パフォーマンスインデックスの計算 ```python theme={null} def calculate_performance_index(actual_metric, expected_metric): """ Calculate normalized performance index Args: actual_metric: Measured performance value expected_metric: Baseline or expected performance value Returns: Performance index (0.0 = no value, 1.0 = expected, >1.0 = above expected) """ if expected_metric == 0: return 0.0 return actual_metric / expected_metric # Examples: # CTR: 0.15% actual vs 0.12% expected = 1.25 performance index (25% above) # Conversions: 45 actual vs 60 expected = 0.75 performance index (25% below) # Brand lift: 8% actual vs 5% expected = 1.6 performance index (60% above) ``` ### metric\_type の決定 キャンペーン目標に基づいて metric\_type を選択します。 ```python theme={null} METRIC_TYPE_MAPPING = { 'awareness': 'brand_lift', 'consideration': 'brand_lift', 'traffic': 'click_through_rate', 'conversions': 'conversion_rate', 'sales': 'conversion_rate', 'engagement': 'completion_rate', 'reach': 'overall_performance' } def get_metric_type(campaign_objective): return METRIC_TYPE_MAPPING.get(campaign_objective, 'overall_performance') ``` ## 関連ドキュメント * [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) - 配信メトリクスを取得 * [Optimization & Reporting](/docs/media-buy/media-buys/optimization-reporting) - パフォーマンスフィードバックの考え方 * [Targeting](/docs/media-buy/advanced-topics/targeting) - 最適化に向けたターゲティングの理解 # sync_audiences Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/task-reference/sync_audiences sync_audiences タスク — ハッシュ化されたファーストパーティ CRM オーディエンスを AdCP セラーアカウントにアップロードして、リターゲティング、サプレッション、類似オーディエンス拡張に活用します。マッチングステータスのトラッキングをサポート。 セラーアカウント上のファーストパーティ CRM オーディエンスを管理します。ハッシュ化された顧客リストをアップロードし、マッチングステータスを確認し、`create_media_buy` のターゲティングオーバーレイで結果のオーディエンスを参照して、明示的なリターゲティングやサプレッションに利用できます。 オーディエンスは[シグナル](/docs/signals/overview)とは異なります。シグナルは、プロダクトのシグナルオプション、プロバイダーが公開するシグナル定義、または `get_signals` を通じて発見される名前付きのターゲティング次元です。オーディエンスは自社が所有してアップロードするデータです。`audience_include` を使うとアップロードしたリストのメンバーだけをターゲットにできます。`audience_include` はハード制約であり、リスト上のユーザーのみが対象となります。オーディエンスに*類似した*新規ユーザーを探す場合(類似オーディエンス拡張)は、キャンペーンブリーフにそのインテントを記述する — 拡張戦略はセラーが担当します。なお、ブリーフで表明された類似インテントはプロトコルを通じて検証できないため、セラー側のレポートで確認すること。 **レスポンス時間**: アップロードは約 1〜2 秒で受け付けられます。オーディエンスごとのマッチングは非同期です——マッチングが完了するまでタスクはアクティブな状態が続く(セラーによって 1〜48 時間)。オーディエンスの準備ができたときに Webhook を受け取るには `push_notification_config` を設定すること。取り込みパイプラインが同期レスポンスでオーディエンスごとの結果を返せないセラー(バッチ取り込み、ガバナンスによるゲート付きアップロード、クリーンルームのフロー)は、操作レベルの submitted タスクエンベロープで応答してもよい(MAY)——[レスポンスの形](#レスポンスの形)を参照。 **リクエストスキーマ**: [`/schemas/v3/media-buy/sync-audiences-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/sync-audiences-request.json) **レスポンススキーマ**: [`/schemas/v3/media-buy/sync-audiences-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/sync-audiences-response.json) ## クイックスタート 顧客リストをアップロードしてステータスを確認します: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncAudiencesResponseSchema } from "@adcp/sdk"; import { createHash } from "crypto"; const hashEmail = (email) => createHash("sha256").update(email.toLowerCase().trim()).digest("hex"); const hashPhone = (e164Phone) => createHash("sha256").update(e164Phone).digest("hex"); const result = await testAgent.syncAudiences({ account: { account_id: "acct_12345" }, audiences: [ { audience_id: "existing_customers", name: "Existing customers", add: [ { external_id: "crm_1001", hashed_email: hashEmail("alice@example.com") }, { external_id: "crm_1002", hashed_email: hashEmail("bob@example.com"), hashed_phone: hashPhone("+12065551234") }, ], }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncAudiencesResponseSchema.parse(result.data); // Three-shape discriminated union: errors | submitted | audiences if ("status" in validated && validated.status === "submitted") { // Whole sync queued asynchronously — poll tasks/get with task_id or await webhook console.log(`Sync queued as task ${validated.task_id}: ${validated.message ?? ""}`); } else if ("errors" in validated && validated.errors && !("audiences" in validated)) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } else if ("audiences" in validated) { for (const audience of validated.audiences) { console.log(`${audience.audience_id}: ${audience.action} (${audience.status ?? "n/a"})`); if (audience.status === "ready") { console.log(` Matched ${audience.matched_count} of ${audience.uploaded_count} members (this sync)`); } } } ``` ```python Python theme={null} import asyncio import hashlib from adcp.testing import test_agent def hash_email(email: str) -> str: return hashlib.sha256(email.lower().strip().encode()).hexdigest() def hash_phone(e164_phone: str) -> str: return hashlib.sha256(e164_phone.encode()).hexdigest() async def main(): result = await test_agent.simple.sync_audiences( account={'account_id': 'acct_12345'}, audiences=[{ 'audience_id': 'existing_customers', 'name': 'Existing customers', 'add': [ {'external_id': 'crm_1001', 'hashed_email': hash_email('alice@example.com')}, {'external_id': 'crm_1002', 'hashed_email': hash_email('bob@example.com'), 'hashed_phone': hash_phone('+12065551234')}, ] }] ) # Three-shape discriminated union: errors | submitted | audiences if getattr(result, 'status', None) == 'submitted': # Whole sync queued asynchronously — poll tasks/get with task_id or await webhook print(f"Sync queued as task {result.task_id}: {getattr(result, 'message', '') or ''}") return if getattr(result, 'errors', None) and not getattr(result, 'audiences', None): raise Exception(f"Operation failed: {result.errors}") for audience in result.audiences: status = getattr(audience, 'status', 'n/a') print(f"{audience.audience_id}: {audience.action} ({status})") if status == 'ready': print(f" Matched {audience.matched_count} of {audience.uploaded_count} members (this sync)") asyncio.run(main()) ``` ## リクエストパラメータ | パラメータ | 型 | 必須 | 説明 | | ---------------- | -------------------------------------------------------------------------------- | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `account` | [account-ref](/docs/building/by-layer/L2/accounts-and-agents#account-references) | はい | アカウント参照。`{ "account_id": "..." }` を渡すか、セラーが暗黙的な解決をサポートしている場合は `{ "brand": {...}, "operator": "..." }` を渡します。 | | `audiences` | [Audience](#audience-object)\[] | いいえ | 同期するオーディエンス。省略した場合、呼び出しはディスカバリー専用となり、変更なしで既存のすべてのオーディエンスを返します。 | | `delete_missing` | boolean | いいえ | true の場合、このリクエストに含まれていないアカウント上のバイヤー管理オーディエンスを削除する(デフォルト: false)。セラー管理のオーディエンスには影響しません。`audiences` 配列を省略した状態と組み合わせると、すべてのバイヤー管理オーディエンスが削除されるため注意すること。 | ### Audience オブジェクト | フィールド | 型 | 必須 | 説明 | | --------------- | ------------------------------------- | --- | ---------------------------------------------------------------------------------------------- | | `audience_id` | string | はい | このオーディエンスのバイヤー識別子。ターゲティングオーバーレイでオーディエンスを参照するために使用します。 | | `name` | string | いいえ | 人間が読みやすい名前 | | `delete` | boolean | いいえ | true の場合、このオーディエンスをアカウントから完全に削除します。その他のフィールドはすべて無視されます。 | | `add` | [AudienceMember](#audience-member)\[] | いいえ | このオーディエンスに追加するメンバー | | `remove` | [AudienceMember](#audience-member)\[] | いいえ | このオーディエンスから削除するメンバー。同じ識別子が `add` と `remove` の両方に現れた場合、remove が優先されます。 | | `consent_basis` | string | いいえ | GDPR の適法根拠: `consent`、`legitimate_interest`、`contract`、または `legal_obligation`。規制対象市場の一部セラーで必須。 | ### Audience メンバー すべてのメンバーには `external_id`(バイヤーが割り当てた安定した識別子)と、少なくとも 1 つのマッチング可能な識別子が必要です。送信前にすべての値を SHA-256 でハッシュ化すること — メールアドレスは小文字化+トリム、電話番号は E.164 形式(例: `+12065551234`)に正規化します。 | フィールド | 型 | 説明 | | -------------- | ------ | --------------------------------------------------------------------------------------------- | | `external_id` | string | **必須。** このメンバーのバイヤーが割り当てた安定した識別子(例: CRM レコード ID、ロイヤルティ ID)。重複排除、削除、バイヤーシステムとのクロスリファレンスに使用します。 | | `hashed_email` | string | 小文字化・トリムされたメールアドレスの SHA-256 ハッシュ(64 文字の16進数) | | `hashed_phone` | string | E.164 形式の電話番号の SHA-256 ハッシュ(64 文字の16進数) | | `uids` | UID\[] | ユニバーサル ID: `type`(rampid、uid2、maid など)+ `value` | 同一人物に複数の識別子を提供するとマッチ率が向上します。複合識別子(例: ハッシュ化された姓名 + 郵便番号)はまだ標準化されていない — プラットフォーム固有の拡張には `ext` を使用すること。 **識別子のサポートはセラーによって異なる**: 送信前に `get_adcp_capabilities` → `media_buy.audience_targeting.supported_identifier_types` および `media_buy.audience_targeting.supported_uid_types` を確認すること。MAID のサポートは全セラーに共通ではない(LinkedIn は MAID を受け付けない。iOS の IDFA には App Tracking Transparency の同意が必要)。ケイパビリティの `media_buy.audience_targeting.matching_latency_hours` の範囲と `media_buy.audience_targeting.minimum_audience_size` もセラー固有の値です。 **サイズ制限**: ペイロードはすべてのオーディエンスを合わせて 1 回の呼び出しにつき最大 100,000 メンバーに制限されます。より大きなリストの場合は、`add` のデルタを使って順次呼び出しに分割すること。 **同時実行**: `sync_audience` への呼び出しは互いに独立していることを確認すること。処理は順不同になる場合があります。順次実行が必要な場合は、設定した Webhook へのコールバックを受け取ってから次の呼び出しを行うこと。 ## レスポンスの形 レスポンスは識別共用体を使います——レスポンスは三つの形のうちちょうど一つを持ち、混在することはありません: **1. 同期的な成功** — オーディエンスごとの結果: * `audiences` — このリクエストに含まれていないオーディエンスも含む、アカウント上のすべてのオーディエンスの結果 * `sandbox` — このレスポンスがサンドボックスモードからのものかを示すブール値(オプション) **2. 終端の失敗** — 処理されたオーディエンスなし: * `errors` — 操作レベルのエラーの配列(認証失敗、アカウントが見つからない、無効なリクエスト形式) **3. Submitted タスクエンベロープ** — 操作全体が非同期でキューに入れられた(バッチ取り込み、ガバナンスによるゲート付きアップロード、レスポンスが出る前にセラーがオーディエンスごとの結果を返せないクリーンルームのフロー): * `status` — 常に `"submitted"` * `task_id` — `tasks/get` によるポーリングまたは完了時のウェブフック受信のためのハンドル * `message` — キューの状態を説明する任意の人が読めるテキスト 最終的なオーディエンスごとの `audiences` 配列は、submitted エンベロープではなくタスクの完了アーティファクトに載ります。オーディエンスごとの非同期マッチング(同期の残りが解決される間、一つのオーディエンスが `processing` になっている)は、submitted エンベロープではなく、その項目に `status: "processing"` を持つ同期的な成功の分岐に属します。オーディエンスごとの [audience-status](#オーディエンスステータス) 列挙上のマッチングレイテンシが一般的なケースであり、submitted エンベロープはより少ない操作レベルの非同期のケース向けです。 **成功レスポンスの各オーディエンスに含まれるフィールド:** | フィールド | 説明 | | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `audience_id` | リクエストから返されるバイヤーの識別子 | | `seller_id` | セラーの広告プラットフォームで割り当てられた ID | | `action` | `created`、`updated`、`unchanged`、`deleted`、または `failed` | | `status` | `processing`、`ready`、または `too_small`。action が `created`、`updated`、または `unchanged` の場合に存在します。action が `deleted` または `failed` の場合は存在しません。 | | `uploaded_count` | この同期操作で送信されたメンバー数(差分、累積ではない)。ディスカバリー専用呼び出しでは 0。 | | `total_uploaded_count` | すべての同期にわたってアップロードされたメンバーの累積数。`matched_count` と比較してマッチ率を計算します。 | | `matched_count` | すべての同期にわたってプラットフォームユーザーにマッチしたメンバーの合計数(累積)。`status: "ready"` の時に設定されます。 | | `effective_match_rate` | すべての識別子タイプにわたる重複排除済みのマッチ率(0〜1)。リーチ推定のための単一の数値。`status: "ready"` の時に設定されます。 | | `match_breakdown` | 識別子タイプ別のマッチ結果。どの ID タイプがどのマッチ率で解決されるかを示します。[マッチ内訳](#マッチ内訳)を参照。 | | `last_synced_at` | 最新の同期の ISO 8601 タイムスタンプ。セラーがこれをトラッキングしていない場合は省略されます。 | | `minimum_size` | このプラットフォームでターゲティングするための最小マッチオーディエンスサイズ。`status: "too_small"` の時に設定されます。 | | `errors` | オーディエンスごとのエラー(`action: "failed"` の場合のみ) | ## マッチ内訳 セラーが識別子タイプ別のレポートをサポートしている場合、レスポンスには `match_breakdown` が含まれる — これはどの ID タイプが解決されているか、どのマッチ率かを示す配列です。バイヤーは将来のアップロードでどの識別子を優先すべきかを判断するために活用できます。 ```json theme={null} { "audience_id": "existing_customers", "action": "updated", "status": "ready", "uploaded_count": 5000, "total_uploaded_count": 25000, "matched_count": 18750, "effective_match_rate": 0.75, "match_breakdown": [ { "id_type": "hashed_email", "submitted": 25000, "matched": 17500, "match_rate": 0.70 }, { "id_type": "hashed_phone", "submitted": 15000, "matched": 12000, "match_rate": 0.80 }, { "id_type": "rampid", "submitted": 8000, "matched": 7200, "match_rate": 0.90 } ] } ``` 主要なセマンティクス: * **`submitted` と `matched` は累積値**であり、すべての同期にわたる値で、`total_uploaded_count` のセマンティクス(`uploaded_count` ではない)に対応します。 * **`effective_match_rate` は重複排除済み** — メールと電話の両方でマッチしたメンバーは 1 回としてカウントされます。タイプ別マッチ率の合計以下になります。 * **`match_rate` はサーバーが権威のある値** — コンシューマーは submitted/matched から自分で計算するよりもこの値を優先すべきです。 * **`id_type` の値**は、ハッシュ化された PII タイプ(`hashed_email`、`hashed_phone`)とユニバーサル ID タイプ(`rampid`、`uid2`、`id5`、`euid`、`pairid`、`maid`)を組み合わせたものです。 集計マッチカウントのみをサポートするセラーは `match_breakdown` を完全に省略します。 ## よくあるシナリオ ### ディスカバリー専用 変更なしで既存のすべてのオーディエンスのステータスを確認します。レスポンスにはアカウント上のすべてのオーディエンスが含まれる — `audience_id` でフィルタリングして目的のオーディエンスを見つけること: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncAudiencesResponseSchema } from "@adcp/sdk"; const result = await testAgent.syncAudiences({ account: { account_id: "acct_12345" }, }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncAudiencesResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("audiences" in validated) { for (const audience of validated.audiences) { console.log(`${audience.audience_id}: ${audience.status ?? "n/a"}`); } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.sync_audiences(account={'account_id': 'acct_12345'}) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for audience in result.audiences: status = getattr(audience, 'status', 'n/a') print(f"{audience.audience_id}: {status}") asyncio.run(main()) ``` ### サプレッションリスト 新規獲得キャンペーンから除外するために、既存顧客のリストをアップロードする: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncAudiencesResponseSchema } from "@adcp/sdk"; import { createHash } from "crypto"; const hashEmail = (email) => createHash("sha256").update(email.toLowerCase().trim()).digest("hex"); // CRM エクスポートからのハッシュ化された顧客メールアドレス const existingCustomers = [ { hashed_email: hashEmail("customer1@example.com") }, { hashed_email: hashEmail("customer2@example.com") }, ]; const result = await testAgent.syncAudiences({ account: { account_id: "acct_12345" }, audiences: [ { audience_id: "existing_customers", name: "Existing customers — suppression", add: existingCustomers, }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncAudiencesResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("audiences" in validated) { const audience = validated.audiences[0]; console.log(`Status: ${audience.status}`); // ready になったら、create_media_buy の targeting_overlay.audience_exclude で audience_id を参照する } ``` ```python Python theme={null} import asyncio import hashlib from adcp.testing import test_agent def hash_email(email: str) -> str: return hashlib.sha256(email.lower().strip().encode()).hexdigest() async def main(): existing_customers = [ {'hashed_email': hash_email('customer1@example.com')}, {'hashed_email': hash_email('customer2@example.com')}, ] result = await test_agent.simple.sync_audiences( account={'account_id': 'acct_12345'}, audiences=[{ 'audience_id': 'existing_customers', 'name': 'Existing customers — suppression', 'add': existing_customers }] ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") audience = result.audiences[0] print(f"Status: {audience.status}") # ready になったら、create_media_buy の targeting_overlay.audience_exclude で audience_id を参照する asyncio.run(main()) ``` ### メンバーの削除 オーディエンスを差分で更新する — 新しいメンバーを追加し、対象外になったメンバーを削除します: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncAudiencesResponseSchema } from "@adcp/sdk"; import { createHash } from "crypto"; const hashEmail = (email) => createHash("sha256").update(email.toLowerCase().trim()).digest("hex"); const result = await testAgent.syncAudiences({ account: { account_id: "acct_12345" }, audiences: [ { audience_id: "lapsed_subscribers", name: "Lapsed subscribers", add: [{ hashed_email: hashEmail("newlapse@example.com") }], remove: [{ hashed_email: hashEmail("reactivated@example.com") }], }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncAudiencesResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("audiences" in validated) { for (const audience of validated.audiences) { console.log(`${audience.audience_id}: ${audience.action}`); } } ``` ```python Python theme={null} import asyncio import hashlib from adcp.testing import test_agent def hash_email(email: str) -> str: return hashlib.sha256(email.lower().strip().encode()).hexdigest() async def main(): result = await test_agent.simple.sync_audiences( account={'account_id': 'acct_12345'}, audiences=[{ 'audience_id': 'lapsed_subscribers', 'name': 'Lapsed subscribers', 'add': [{'hashed_email': hash_email('newlapse@example.com')}], 'remove': [{'hashed_email': hash_email('reactivated@example.com')}] }] ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for audience in result.audiences: print(f"{audience.audience_id}: {audience.action}") asyncio.run(main()) ``` ### オーディエンスの削除 他のオーディエンスに影響を与えずに特定のオーディエンスをアカウントから削除します。オーディエンスオブジェクトに `delete: true` を設定します: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncAudiencesResponseSchema } from "@adcp/sdk"; const result = await testAgent.syncAudiences({ account: { account_id: "acct_12345" }, audiences: [ { audience_id: "old_campaign_list", delete: true }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncAudiencesResponseSchema.parse(result.data); if ("audiences" in validated) { const audience = validated.audiences.find(a => a.audience_id === "old_campaign_list"); console.log(`${audience.audience_id}: ${audience.action}`); // "deleted" } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.sync_audiences( account={'account_id': 'acct_12345'}, audiences=[{'audience_id': 'old_campaign_list', 'delete': True}] ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") audience = next(a for a in result.audiences if a.audience_id == 'old_campaign_list') print(f"{audience.audience_id}: {audience.action}") # "deleted" asyncio.run(main()) ``` 1 回の呼び出しで複数のオーディエンスを削除するには、それぞれに `delete: true` を指定します。すべてのバイヤー管理オーディエンスを一度に削除するには、空の `audiences` 配列と `delete_missing: true` を使用する — ただし、すべてが削除されるため注意すること。 ### メディアバイでのオーディエンスの使用 オーディエンスが `ready` になったら、`create_media_buy` のターゲティングオーバーレイで `audience_id` を参照します。オーディエンス ID はセラーアカウントにスコープされるため、セラーをまたいで使用することはできません。 ```json test=false theme={null} { "brand": { "house_domain": "acme.com", "brand_id": "main" }, "start_time": "asap", "end_time": "2026-03-31T23:59:59Z", "packages": [ { "product_id": "prod_sponsored_content", "pricing_option_id": "cpm_standard", "budget": 10000, "targeting_overlay": { "audience_include": ["high_value_prospects"], "audience_exclude": ["existing_customers"] } } ] } ``` ## オーディエンスステータス プラットフォームのマッチングは非同期です。`status` フィールドは現在の状態を反映する: | ステータス | 意味 | | ------------ | ---------------------------------------------------------------------------------------- | | `processing` | プラットフォームがアップロードされたメンバーをユーザーベースと照合中。後でもう一度確認すること — まだキャンペーンを作成してはいけない。 | | `ready` | オーディエンスはターゲティングに使用可能。`matched_count` が設定されています。 | | `too_small` | マッチしたオーディエンスがプラットフォームの最小サイズを下回っています。レスポンスの `minimum_size` でしきい値を確認できます。メンバーを追加して再同期すること。 | `status` は `action` が `created`、`updated`、または `unchanged` の場合に存在します。`action` が `deleted` または `failed` の場合は存在しません。 セラーは、`matched_count < minimum_size` の場合には常に `too_small` を出力しなければなりません(MUST)。プラットフォームの最小値を下回る `matched_count` で `ready` を返すことは非準拠です——バイヤーは、カウントの事後的な解釈ではなく、ターゲティングが失敗するというプログラム的なシグナルとしてステータス値に依拠します。 **Webhook(推奨)**: アップロード前にプロトコルレベルで `push_notification_config` を設定すること。タスクはセラーのプラットフォームがメンバーをマッチングしている間アクティブな状態が続く。マッチングが完了すると、タスクが完了し、最終結果(`status: "ready"` または `status: "too_small"`)とともに Webhook が発火します。現実的な期待値を設定するには `get_adcp_capabilities` → `audience_targeting.matching_latency_hours` を確認すること(通常 1〜48 時間)。 **ポーリングフォールバック**: Webhook を使用しない場合は、`audiences` を省略したディスカバリー専用呼び出しで 15 分以上の間隔でポーリングすること。タスクのステータスを確認するには `tasks/get` と `task_id` を使用する — マッチング処理中はタスクが `submitted` 状態になり、オーディエンスの準備が完了するか小さすぎる場合に `completed` になります。 **エージェントワークフロー**: `push_notification_config` を設定してアップロードします。セッション終了前に `audience_id` と `account_id` を外部化します。`status: "ready"` の Webhook が発火したら再開して `create_media_buy` に進む。 ## 非同期パターン 二つの異なる非同期パターンがあります——セラーの振る舞いに応じて正しいものを選んでください: **オーディエンスごとの非同期マッチング**(一般的): 同期操作自体は同期的に解決され、オーディエンスごとの結果を即座に返します。マッチングがまだ実行中のオーディエンスは、`status: "processing"` とともに同期的な成功レスポンスで返ってきます。バイヤーは、後続のディスカバリー専用呼び出しまたはウェブフックを通じて終端の状態(`ready` / `too_small`)を突き合わせます。これは上記の [オーディエンスステータス](#オーディエンスステータス) 列挙が扱うケースです。 **操作レベルの非同期**(あまり一般的でない): 同期全体がキューに入れられます——取り込みがバッチ化されている、ガバナンスのレビューがアップロードをゲートしている、またはマッチングを開始する前に上流のクリーンルームのフローが確定しなければならない、といった理由でセラーが応答前にオーディエンスごとの結果を返せない場合です。レスポンスは submitted エンベロープです: * トップレベルの `status: "submitted"` と `task_id` * `message` — 任意の人が読める説明 * このエンベロープには `audiences` 配列なし `tasks/get` をポーリングするか、ウェブフックを待ちます。完了アーティファクトが、項目ごとの `action`/`status` の結果を持つ `audiences` 配列を運びます。操作レベルの失敗は、タスク上の `status: "failed"` として表面化します。 **参照:** ウェブフックの設定については[ウェブフック](/docs/building/by-layer/L3/webhooks)を参照。 ## ハッシュ化の要件 送信前にすべての識別子を SHA-256 でハッシュ化すること。まず正規化を行う: | 識別子 | 正規化 | 例 | | ------- | ------------- | -------------------------- | | メールアドレス | 小文字化、前後の空白を除去 | `alice@example.com` → ハッシュ | | 電話番号 | E.164 形式 | `+12065551234` → ハッシュ | | MAID | 正規化不要 | そのまま使用 | ```javascript test=false theme={null} import { createHash } from "crypto"; const hashEmail = (email) => createHash("sha256").update(email.toLowerCase().trim()).digest("hex"); const hashPhone = (e164Phone) => createHash("sha256").update(e164Phone).digest("hex"); ``` ## プライバシーに関する考慮事項 スキーマは平文のメールアドレスや電話番号を一切運びません——バイヤーは送信前にハッシュ化しなければなりません(MUST)。セラーは同じアルゴリズムで自社のユーザーデータを独立してハッシュ化することでマッチングを行います。 **ハッシュ化された識別子は仮名化された PII であり、匿名ではありません。** メールアドレスや電話番号のソルトなしの SHA-256 は、メールと E.164 の名前空間の事前計算された辞書を通じて復元可能です。したがって、`hashed_email` と `hashed_phone` は、保持、同意、アクセス制御、データ主体の請求の目的で PII として扱わなければなりません(MUST)。オペレーターのドキュメントや DPA でこれらを「プライバシー保護的」と説明しないでください。[プライバシーに関する考慮事項](/docs/reference/privacy-considerations#unsalted-hashed-identifiers-are-pseudonymous-not-anonymous)を参照。 **バイヤーの義務**: バイヤーは管轄区域に関わらず、オーディエンスデータを処理・共有するための適法根拠を持つ責任があります。規制対象市場で活動するセラーに GDPR の適法根拠を伝えるために、各オーディエンスに `consent_basis` を含めること — 一部のセラーは EU オーディエンスに対してこのフィールドを必須としています。 **データ取り扱い**: アップロード後のデータ処理と保持は、セラーとの契約に基づいて管理されます。オーディエンスデータをアップロードする前に、セラーのデータ処理条件を確認すること。 ## エラー処理 | エラーコード | 説明 | 対処方法 | | --------------------- | ------------------------------------------------------------- | ------------------------------------------- | | `ACCOUNT_NOT_FOUND` | アカウントが存在しない | `account_id` を確認する | | `REFERENCE_NOT_FOUND` | 削除対象のオーディエンスが存在しない、またはアクセスできない(`error.field` = `audience_id`) | `audience_id` を確認するか `remove` を省略する | | `INVALID_HASH_FORMAT` | 識別子が期待されるハッシュ形式と一致しない | SHA-256 の16進数エンコードを確認する(64 文字、小文字) | | `RATE_LIMITED` | 同期リクエストが多すぎる | 指数バックオフで再試行します。ポーリングは 15 分以上の間隔で行うこと | | `CALL_TOO_LARGE` | ペイロードのメンバー数が多すぎる | ペイロードはすべてのオーディエンスを合わせて最大 100,000 メンバーに制限される | ## 次のステップ * [ターゲティング](/docs/media-buy/advanced-topics/targeting) — `targeting_overlay.audience_include` と `audience_exclude` でオーディエンスを参照します * [create\_media\_buy](/docs/media-buy/task-reference/create_media_buy) — パッケージにオーディエンスターゲティングを適用します * [コンバージョントラッキング](/docs/media-buy/conversion-tracking/) — オーディエンスターゲティングキャンペーンの成果をトラッキングします # sync_catalogs Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/task-reference/sync_catalogs sync_catalogs タスク — 商品フィード、店舗所在地、垂直カタログ(ホテル、フライト、車両、不動産)をカタログ駆動型キャンペーン向けに AdCP セラーアカウントに同期します。 セラーアカウント上のカタログフィードを管理します。商品フィード、在庫データ、店舗所在地、オファリング、および業界垂直カタログ(ホテル、フライト、求人、車両、不動産、教育、目的地)を同期します。URL ベースのフィードのスケジュール再取得、インラインアイテムデータ、既存カタログのディスカバリーをサポートします。 **用語について。** `sync_catalogs` は、セラーが広告のレンダリングやターゲティングに使う、バイヤー提供のデータフィードを管理します。これは、`get_products buying_mode: "wholesale"` が公開するセラー側のホールセール商品フィードや、`get_signals discovery_mode: "wholesale"` が公開するホールセールシグナルフィードとは別物です。Webhook はそれらセラー側フィードの変更をプッシュするレイヤーです。 **レスポンス時間**: 即時〜数日(小規模カタログは `completed`、プラットフォームレビューが必要な大規模フィードは `submitted` を返す) **リクエストスキーマ**: [`/schemas/v3/media-buy/sync-catalogs-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/sync-catalogs-request.json) **レスポンススキーマ**: [`/schemas/v3/media-buy/sync-catalogs-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/sync-catalogs-response.json) ## 呼び出し関係 **バイヤー**がセラーアカウントにカタログフィードをプッシュするために、**セラー**に対して `sync_catalogs` を呼び出す。セラーはアイテムを検証し、コンテンツポリシーチェックを実行し、アイテムごとの承認ステータスを返します。 ```mermaid theme={null} sequenceDiagram participant B as Buyer participant S as Seller B->>S: sync_catalogs (product feed, stores, inventory) S->>B: Per-catalog results with item review status Note over B,S: Buyer can now reference synced catalogs in creatives ``` このタスクは[アカウント状態セットアップシーケンス](/docs/building/by-layer/L2/account-state)において、フォーマットディスカバリーとクリエイティブ提出の間に位置します: 1. `list_creative_formats` — 各フォーマットの `assets` 配列にある `catalog` アセットタイプを確認し、同期すべきフィードを把握します 2. **`sync_catalogs`** — 必要なフィードをアカウントにプッシュします 3. `sync_creatives` — `catalog_id` で同期済みカタログを参照するクリエイティブを提出します 4. `create_media_buy` — キャンペーンを開始します ## クイックスタート 商品フィードを同期します: ```json theme={null} { "account": { "account_id": "acct_acmecorp" }, "catalogs": [ { "catalog_id": "product-feed", "name": "Acme Product Catalog", "type": "product", "url": "https://feeds.acmecorp.com/products.xml", "feed_format": "google_merchant_center", "update_frequency": "daily" } ] } ``` ## リクエストパラメータ | パラメータ | 型 | 必須 | 説明 | | -------------------------- | -------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------------------------------------------------------------- | | `account` | [account-ref](/docs/building/by-layer/L2/accounts-and-agents#account-references) | 条件付き | アカウント参照。`{ "account_id": "..." }` または、セラーが暗黙的な解決をサポートしている場合は `{ "brand": {...}, "operator": "..." }` を渡します。エージェントが複数のアカウントを持つ場合は必須。 | | `catalogs` | Catalog\[] | No | 同期するカタログフィード(最大 50 件)。ディスカバリーモードでは省略します。 | | `catalog_ids` | string\[] | No | 同期スコープを特定のカタログ ID に限定します。アカウント上の他のカタログは影響を受けない。 | | `delete_missing` | boolean | No | true の場合、この同期に含まれないバイヤー管理カタログを削除します。セラー管理カタログには影響しません。`catalogs` の存在が必要。デフォルト: false。 | | `dry_run` | boolean | No | 変更を適用せずにプレビューします。デフォルト: false。 | | `validation_mode` | string | No | `"strict"`(デフォルト)はエラーが発生した場合に同期全体を失敗させる。`"lenient"` は有効なカタログを処理してエラーを報告します。 | | `push_notification_config` | object | No | 非同期完了通知のための Webhook 設定。 | ### Catalog オブジェクト `catalogs` 配列内の各カタログは [Catalog](/docs/creative/catalogs#the-catalog-object) オブジェクトです。主要フィールド: | フィールド | 型 | 必須 | 説明 | | ------------------- | ------------ | --- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `catalog_id` | string | Yes | バイヤーの識別子。アップサートのために既存カタログとの照合に使用します。 | | `type` | CatalogType | Yes | カタログタイプ: `product`, `offering`, `inventory`, `store`, `promotion`, `hotel`, `flight`, `job`, `vehicle`, `real_estate`, `education`, `destination` | | `url` | uri | No | 外部フィード URL。`items` と相互排他的。 | | `feed_format` | string | No | フィードフォーマット: `google_merchant_center`, `facebook_catalog`, `shopify`, `linkedin_jobs`, `custom` | | `update_frequency` | string | No | 再取得スケジュール: `realtime`, `hourly`, `daily`, `weekly` | | `items` | object\[] | No | インラインカタログデータ。`url` と相互排他的。 | | `conversion_events` | EventType\[] | No | このカタログ内のアイテムのコンバージョンを表すイベントタイプ | ## レスポンス **成功レスポンス** — カタログごとの結果: | フィールド | 型 | 説明 | | --------------------------- | --------- | ------------------------------------------------------ | | `catalogs` | object\[] | 処理された各カタログの結果 | | `catalogs[].catalog_id` | string | リクエストのカタログ ID | | `catalogs[].action` | string | `created`, `updated`, `unchanged`, `failed`, `deleted` | | `catalogs[].platform_id` | string | プラットフォームが割り当てた ID | | `catalogs[].item_count` | integer | 同期後の総アイテム数 | | `catalogs[].items_approved` | integer | プラットフォームが承認したアイテム数 | | `catalogs[].items_pending` | integer | レビュー待ちのアイテム数 | | `catalogs[].items_rejected` | integer | 却下されたアイテム数 | | `catalogs[].item_issues` | object\[] | アイテムごとの却下理由 | | `catalogs[].next_fetch_at` | datetime | 次回スケジュールされたフィード取得日時(URL ベースのカタログ) | **エラーレスポンス** — 操作が完全に失敗した場合: | フィールド | 型 | 説明 | | -------- | -------- | ------------------------ | | `errors` | Error\[] | 操作レベルのエラー(認証失敗、サービス利用不可) | レスポンスは判別ユニオンを使用する — `catalogs` または `errors` のいずれかが返され、両方が同時に返されることはない。 ### アイテムレベルレビューのレスポンス例 ```json theme={null} { "catalogs": [ { "catalog_id": "product-feed", "action": "created", "platform_id": "plat_cat_001", "item_count": 1250, "items_approved": 1180, "items_pending": 45, "items_rejected": 25, "item_issues": [ { "item_id": "SKU-789", "status": "rejected", "reasons": ["Missing required field: image_url"] } ], "next_fetch_at": "2025-03-01T06:00:00Z" } ] } ``` ## アイテムレビューのライフサイクル カタログアイテムはシンプルなレビューサイクルをたどります: アイテムは同期時に `pending` に入り、プラットフォームが非同期にレビューします。アイテムは `approved`、(理由付きの)`rejected`、または `warning` 付きの `approved`(配信されるが修正可能な問題あり)のいずれかになります。 却下は最終的なものではありません——ソースカタログで問題を修正して再同期します。アイテムを再同期すると、再レビューのために `pending` にリセットされます。レスポンスの `item_issues` 配列がアイテムごとの却下理由を示します。 ## ディスカバリーモード `catalogs` を省略することで、アカウント上のすべてのカタログを変更なしで一覧表示できます: ```json theme={null} { "account": { "account_id": "acct_acmecorp" } } ``` セラーが他のソースからブランドデータをすでに持っている場合があるため、これは重要である — 小売業者はコマースプラットフォームからブランドの商品カタログをすでに持っているかもしれない。ディスカバリーにより、バイヤーはすべてを再アップロードするのではなく、既存の状態を活用できます。 ## 非同期承認ワークフロー 大規模なフィードやコンテンツポリシーレビューが必要なフィードは、`task_id` とともに `status: "submitted"` を返します。セラーは非同期でアイテムをレビューし、完了したら Webhook でバイヤーに通知します。 非同期レスポンスの状態: * **`working`** — プラットフォームがフィードを処理中(URL の取得、アイテムの検証) * **`input-required`** — プラットフォームがバイヤーのアクションを必要としている(バリデーションエラーの修正、不足フィールドの提供) * **`submitted`** — レビュー完了、最終的なカタログごとの結果が利用可能 状態遷移の Webhook 通知を受け取るには、リクエストに `push_notification_config` を設定します。 ## 一般的なシナリオ ### リテールメディア(product + inventory + store) ```json theme={null} { "account": { "account_id": "acct_acmecorp" }, "catalogs": [ { "catalog_id": "product-feed", "type": "product", "url": "https://feeds.acmecorp.com/products.xml", "feed_format": "google_merchant_center", "update_frequency": "daily" }, { "catalog_id": "inventory-feed", "type": "inventory", "url": "https://feeds.acmecorp.com/inventory.json", "feed_format": "custom", "update_frequency": "hourly" }, { "catalog_id": "store-locations", "type": "store", "url": "https://feeds.acmecorp.com/stores.json", "feed_format": "custom", "update_frequency": "weekly" } ] } ``` ### 採用(インライン求人オファリング) ```json theme={null} { "account": { "account_id": "acct_restaurants" }, "catalogs": [ { "catalog_id": "chef-vacancies", "type": "offering", "items": [ { "offering_id": "chef-amsterdam-42", "name": "Head Chef - Amsterdam", "landing_url": "https://jobs.acme-restaurants.com/chef-amsterdam-42", "geo_targets": { "countries": ["NL"], "regions": ["NL-NH"] } } ] } ] } ``` ### ドライランバリデーション ```json theme={null} { "account": { "account_id": "acct_acmecorp" }, "dry_run": true, "catalogs": [ { "catalog_id": "product-feed", "type": "product", "url": "https://feeds.acmecorp.com/products.xml", "feed_format": "google_merchant_center" } ] } ``` ## エラーハンドリング | エラー | 説明 | 解決策 | | ------------------------ | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | | `REFERENCE_NOT_FOUND` | 参照された `catalog_id` が存在しないかアクセスできない(`catalog_ids` フィルター使用時。`error.field` = `catalog_ids`) | 以前の同期またはディスカバリー呼び出しからカタログ ID を確認する | | `FEED_FETCH_FAILED` | プラットフォームがフィード URL を取得できなかった | URL のアクセス可能性、認証、フィードフォーマットを確認する | | `INVALID_FEED_FORMAT` | フィードが宣言された `feed_format` と一致しない | フィードの内容がフォーマットと一致しているか確認する(google\_merchant\_center の場合は XML など) | | `ITEM_VALIDATION_FAILED` | アイテムがスキーマバリデーションに失敗した | アイテムごとの却下理由を `item_issues` で確認する | | `CATALOG_LIMIT_EXCEEDED` | アカウントが最大カタログ数に達した | 未使用のカタログを削除するか、セラーに連絡する | ## ベストプラクティス 1. **フォーマット要件を最初に確認する** — 同期前に `list_creative_formats` を呼び出し、各フォーマットの `assets` 配列にある `catalog` アセットタイプを確認します。これにより、同期すべきカタログタイプと各アイテムに必要なフィールドがわかる。 2. **ディスカバリーモードを使用する** — 同期前に `catalogs` なしで呼び出し、セラーがすでに持っているものを確認します。セラーが他のソースからブランドデータを持っている可能性があります。 3. **`update_frequency` を設定する** — URL ベースのフィードでは、プラットフォームが再取得頻度を把握できるよう、常に `update_frequency` を設定します。フィードが古くなると、在庫切れ商品の広告が表示されることになります。 4. **`conversion_events` を宣言する** — どのイベントタイプがカタログアイテムのコンバージョンを表すかを宣言して、カタログをコンバージョントラッキングシステムに接続します。 5. **大規模フィードには `dry_run` を使用する** — 特に数千のアイテムを含む初回同期では、コミット前にバリデーションを行います。 6. **アイテムレベルの失敗を処理する** — `lenient` モードでは、一部のアイテムが失敗しても有効なアイテムは処理されます。却下されたアイテムを修正するには、レスポンスの `item_issues` を確認します。 ## 次のステップ * [Catalogs](/docs/creative/catalogs) — カタログタイプ、ソーシング、フォーマット要件に関する完全なドキュメント * [アカウント状態](/docs/building/by-layer/L2/account-state) — アカウントセットアップシーケンスにおけるカタログの位置づけ * [sync\_creatives](/docs/creative/task-reference/sync_creatives) — 同期済みカタログを参照するクリエイティブの提出 * [list\_creative\_formats](/docs/creative/task-reference/list_creative_formats) — フォーマットのカタログ要件のディスカバリー # sync_event_sources Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/task-reference/sync_event_sources sync_event_sources タスク — コンバージョントラッキングとアトリビューションのために、AdCP セラーアカウントにウェブサイトピクセル、モバイル SDK、サーバー間連携、およびセラーやプラットフォームネイティブのイベントソースを設定します。 コンバージョントラッキングのためにセラーアカウントにイベントソースを設定します。ソースは、ウェブサイトピクセル、モバイル SDK、サーバー間フィード、CRM インポートのようなバイヤー管理の連携でも、チャンネル、プロフィール、フィード、ポッドキャスト、ニュースレターリストのようなセラーやプラットフォームネイティブの自社プロパティでもかまいません。アップサートセマンティクス、セラー管理ソース、セットアップ手順をサポートします。 **レスポンス時間**: 約1秒(同期設定) **リクエストスキーマ**: [`/schemas/v3/media-buy/sync-event-sources-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/sync-event-sources-request.json) **レスポンススキーマ**: [`/schemas/v3/media-buy/sync-event-sources-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/sync-event-sources-response.json) ## クイックスタート 購入トラッキング用のイベントソースを設定します: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncEventSourcesResponseSchema } from "@adcp/sdk"; const result = await testAgent.syncEventSources({ account: { account_id: "acct_12345" }, event_sources: [ { event_source_id: "website_pixel", name: "Main Website Pixel", event_types: ["purchase", "lead", "add_to_cart"], allowed_domains: ["www.example.com", "shop.example.com"], }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } // レスポンスをスキーマに対して検証する const validated = SyncEventSourcesResponseSchema.parse(result.data); // まず操作レベルのエラーを確認する(判別共用体) if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("event_sources" in validated) { for (const source of validated.event_sources) { console.log(`${source.event_source_id}: ${source.action}`); if (source.setup?.snippet) { console.log(` Install: ${source.setup.snippet_type}`); } } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.sync_event_sources( account={'account_id': 'acct_12345'}, event_sources=[{ 'event_source_id': 'website_pixel', 'name': 'Main Website Pixel', 'event_types': ['purchase', 'lead', 'add_to_cart'], 'allowed_domains': ['www.example.com', 'shop.example.com'] }] ) # まず操作レベルのエラーを確認する if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for source in result.event_sources: print(f"{source.event_source_id}: {source.action}") if source.setup and source.setup.snippet: print(f" Install: {source.setup.snippet_type}") asyncio.run(main()) ``` ## リクエストパラメータ | パラメータ | 型 | 必須 | 説明 | | ---------------- | -------------------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------------------------------------ | | `account` | [account-ref](/docs/building/by-layer/L2/accounts-and-agents#account-references) | はい | アカウント参照。`{ "account_id": "..." }` を渡すか、セラーが暗黙的な解決をサポートしている場合は `{ "brand": {...}, "operator": "..." }` を渡します。 | | `event_sources` | [EventSource](/docs/media-buy/conversion-tracking/#event-source)\[] | いいえ | 同期するイベントソース。省略した場合、呼び出しはディスカバリーのみとなり、変更なしに既存のすべてのイベントソースを返します。 | | `delete_missing` | boolean | いいえ | true の場合、このリクエストに含まれていないアカウント上のバイヤー管理イベントソースが削除されます(デフォルト: false)。 | ### イベントソースオブジェクト | フィールド | 型 | 必須 | 説明 | | ----------------- | ------------------------------------------------------------------- | --- | -------------------------------------------------------------------------------------------- | | `event_source_id` | string | はい | このイベントソースの一意の識別子 | | `name` | string | いいえ | 人が読める名前 | | `event_types` | [EventType](/docs/media-buy/conversion-tracking/#event-types)\[] | いいえ | このソースが処理するイベントタイプ。省略した場合、すべてのイベントタイプを受け入れる。 | | `action_source` | [ActionSource](/docs/media-buy/conversion-tracking/#action-sources) | いいえ | 互換性のためのフラットなソースカテゴリ(`website`、`app`、`system_generated` など)。フラット値が粗すぎる場合は `surface` と組み合わせます。 | | `allowed_domains` | string\[] | いいえ | このソースへのイベント送信が許可されたドメイン | | `surface` | [EventSurface](/docs/media-buy/conversion-tracking/#event-surfaces) | いいえ | このソースが表す構造化されたサーフェス(自社チャンネル、プロフィール、フィード、ポッドキャスト、ニュースレターリスト、ウェブサイト、アプリ、店舗など) | ## レスポンス **成功レスポンス:** * `event_sources` - 同期済みのソースとアカウント上のセラー管理ソースの両方を含む、各イベントソースの結果 **エラーレスポンス:** * `errors` - 操作レベルのエラーの配列(認証失敗、アカウントが見つからないなど) **注意:** レスポンスは判別共用体を使用しており、成功フィールドまたはエラーのいずれかが返され、両方が同時に返されることはない。 **成功レスポンス内の各イベントソースには以下が含まれます:** * すべてのリクエストフィールド * `seller_id` - このイベントソースのセラー割り当て識別子 * `action` - 実行された操作: `created`、`updated`、`unchanged`、`deleted`、`failed` * `action_source` - イベントソースの種別(ウェブサイトピクセル、アプリ SDK など) * `surface` - フラットな `action_source` が粗すぎる場合に、このソースが表す構造化されたサーフェス * `managed_by` - このソースの管理者: `buyer` または `seller` * `setup` - 実装の詳細(スニペット、手順) * `health` - イベントソースの健全性評価(セラーが健全性スコアリングをサポートする場合) * `errors` - ソースごとのエラー(`action: "failed"` の場合のみ) * `ext` - ソースごとの拡張メタデータ(プラットフォームネイティブのコンバージョン ID、アトリビューションウィンドウのハンドル、生の起点文字列など) **完全なフィールド一覧はスキーマを参照**: [sync-event-sources-response.json](https://adcontextprotocol.org/schemas/v3/media-buy/sync-event-sources-response.json) ### イベントソースの健全性 イベントソースの品質を評価するセラーは、レスポンスの各ソースに `health` オブジェクトを含めます。これは Snap の Event Quality Score や Meta の Event Match Quality に相当します——バイヤーのイベント連携が最適化に十分な程度に機能しているかを伝えます。 | フィールド | 型 | 説明 | | --------------------- | --------- | ------------------------------------------------------------------------------------ | | `status` | string | AdCP 標準の健全性レベル: `insufficient`、`minimum`、`good`、`excellent`。セラー横断の判断に使用します。 | | `detail` | object | セラー固有のスコアリング(任意)。`score`、`max_score`、任意の `label` を含みます。セラーがネイティブな品質スコアを持つ場合にのみ存在します。 | | `match_rate` | number | 広告インタラクションに一致したイベントの割合(0.0〜1.0)。 | | `last_event_at` | date-time | 受信した最新イベントのタイムスタンプ。 | | `evaluated_at` | date-time | この健全性評価が計算された時刻。レポートデータから計算するセラーでは古くなります。 | | `events_received_24h` | integer | 過去 24 時間に受信したイベント数(0 = 発火していない)。 | | `issues` | array | severity(`error`、`warning`、`info`)とメッセージを持つ実行可能な問題。severity でソートすること——配列の位置に頼らないこと。 | ```json test=false theme={null} { "event_sources": [ { "event_source_id": "web_pixel", "action": "unchanged", "managed_by": "buyer", "health": { "status": "good", "detail": { "score": 7.2, "max_score": 10, "label": "Event Match Quality" }, "match_rate": 0.72, "last_event_at": "2026-03-23T14:22:00Z", "evaluated_at": "2026-03-23T14:30:00Z", "events_received_24h": 14200, "issues": [ { "severity": "warning", "message": "Missing hashed_email parameter on 38% of purchase events. Adding this improves match rate for cross-device attribution." } ] } } ] } ``` バイヤーエージェントは、`detail.score` ではなく `status` を判断の基準にすべきです。四段階のステータスはすべてのセラー間で比較可能です——バイヤーエージェントは、どこでも機能する一つのルール(「DR プロダクトには `good` 以上を要求する」)を書きます。`detail` オブジェクトは人間向けダッシュボードや高度な診断のためのものです。 バイヤーエージェントは健全性データを次のように活用できます: * イベント品質でプロダクト選択をゲートする(例: DR プロダクトには `good` 以上を要求) * キャンペーンのローンチ前にセットアップの問題をバイヤーに提示する * どのイベントソースを最初に修正すべきか優先順位付けする ## よくある使用例 ### ディスカバリーのみ 変更を加えずにアカウント上のすべてのイベントソース(セラー管理ソースを含む)を検出します。セラーが常時オンのアトリビューションを提供するプラットフォーム管理のコンバージョントラッキングに便利: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncEventSourcesResponseSchema } from "@adcp/sdk"; const result = await testAgent.syncEventSources({ account: { account_id: "acct_12345" }, }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncEventSourcesResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("event_sources" in validated) { for (const source of validated.event_sources) { console.log(`${source.event_source_id} (${source.managed_by}): ${source.name}`); } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.sync_event_sources( account={'account_id': 'acct_12345'} ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for source in result.event_sources: print(f"{source.event_source_id} ({source.managed_by}): {source.name}") asyncio.run(main()) ``` ### 複数のイベントソース ウェブサイトとアプリ用に個別のソースを設定します: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncEventSourcesResponseSchema } from "@adcp/sdk"; const result = await testAgent.syncEventSources({ account: { account_id: "acct_12345" }, event_sources: [ { event_source_id: "web_pixel", name: "Website Pixel", event_types: ["purchase", "lead", "add_to_cart", "view_content"], allowed_domains: ["www.example.com"], }, { event_source_id: "app_sdk", name: "Mobile App SDK", event_types: ["purchase", "app_install", "app_launch"], }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncEventSourcesResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("event_sources" in validated) { for (const source of validated.event_sources) { console.log(`${source.event_source_id} (${source.managed_by}): ${source.action}`); } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.sync_event_sources( account={'account_id': 'acct_12345'}, event_sources=[ { 'event_source_id': 'web_pixel', 'name': 'Website Pixel', 'event_types': ['purchase', 'lead', 'add_to_cart', 'view_content'], 'allowed_domains': ['www.example.com'] }, { 'event_source_id': 'app_sdk', 'name': 'Mobile App SDK', 'event_types': ['purchase', 'app_install', 'app_launch'] } ] ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for source in result.event_sources: print(f"{source.event_source_id} ({source.managed_by}): {source.action}") asyncio.run(main()) ``` ### セラー管理ソースの検出 セラーは常時オンのイベントソース(例: Amazon 売上アトリビューション)を提供する場合があります。これらはバイヤー管理ソースと並んで `managed_by: "seller"` としてレスポンスに表示されます: ```json test=false theme={null} { "event_sources": [ { "event_source_id": "web_pixel", "name": "Website Pixel", "seller_id": "px_abc123", "action": "created", "managed_by": "buyer", "setup": { "snippet": "", "snippet_type": "javascript", "instructions": "Place this tag in the of all pages where you want to track events." } }, { "event_source_id": "seller_sales_attribution", "name": "Sales Attribution", "seller_id": "internal_attr", "action": "unchanged", "managed_by": "seller", "action_source": "in_store" } ] } ``` `conversion_tracking.platform_managed: true` を持つプロダクトは、セラーがこれらのソースを提供していることを示しています。 ### クリエイターおよび自社プロパティのソース セラー管理のクリエイターまたは自社プロパティのソースは、互換性のためのフラットな `action_source` と構造化されたコンテキストのための `surface` を伴って、同じ `event_sources` 配列を使います: ```json test=false theme={null} { "event_sources": [ { "event_source_id": "creator_channel", "name": "Creator Channel Events", "seller_id": "creator_attr_001", "event_types": ["follow", "content_view", "watch_milestone"], "action_source": "system_generated", "surface": { "category": "owned_property", "property_type": "channel", "namespace": "video_platform", "property_id": "channel_123" }, "managed_by": "seller", "action": "unchanged", "ext": { "video_platform": { "native_origin": "owned_channel" } } } ] } ``` チャンネル、プロフィール、フィード、リスト、ポッドキャストへの無料で継続的なオプトインには `follow` を使います。有料の購読または有料のメンバーシップにのみ `subscribe` を使います。 ### delete\_missing によるクリーン同期 アカウント上のすべてのバイヤー管理イベントソースを置き換える: ```javascript JavaScript theme={null} import { testAgent } from "@adcp/sdk/testing"; import { SyncEventSourcesResponseSchema } from "@adcp/sdk"; const result = await testAgent.syncEventSources({ account: { account_id: "acct_12345" }, delete_missing: true, event_sources: [ { event_source_id: "unified_pixel", name: "Unified Tracking Pixel", }, ], }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = SyncEventSourcesResponseSchema.parse(result.data); if ("errors" in validated && validated.errors) { throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`); } if ("event_sources" in validated) { for (const source of validated.event_sources) { if (source.action === "deleted") { console.log(`Removed: ${source.event_source_id}`); } else { console.log(`Active: ${source.event_source_id} (${source.action})`); } } } ``` ```python Python theme={null} import asyncio from adcp.testing import test_agent async def main(): result = await test_agent.simple.sync_event_sources( account={'account_id': 'acct_12345'}, delete_missing=True, event_sources=[{ 'event_source_id': 'unified_pixel', 'name': 'Unified Tracking Pixel' }] ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Operation failed: {result.errors}") for source in result.event_sources: if source.action == 'deleted': print(f"Removed: {source.event_source_id}") else: print(f"Active: {source.event_source_id} ({source.action})") asyncio.run(main()) ``` ## セットアップ手順 レスポンスには各イベントソースのセットアップ詳細が含まれます。`setup` オブジェクトはソースのアクティベート方法を示しています: | `snippet_type` | 説明 | 必要なアクション | | -------------- | ------------------------- | ---------------------------- | | `javascript` | ウェブサイトページ用の JavaScript タグ | トラッキング対象ページの `` 内に配置する | | `html` | HTML ピクセル / iframe | `` の直前に配置する | | `pixel_url` | イベント発生時に呼び出す URL | 各イベント時に GET リクエストを送信する | | `server_only` | クライアントサイドのタグ不要 | `log_event` API を直接使用する | ## エラーハンドリング | エラーコード | 説明 | 対処方法 | | --------------------------- | ------------------------- | ----------------------------------------------------------- | | `ACCOUNT_NOT_FOUND` | アカウントが存在しない | アカウント設定の `account_id` を確認する | | `INVALID_EVENT_TYPE` | 認識できないイベントタイプ | `get_adcp_capabilities` でセラーの `supported_event_types` を確認する | | `DUPLICATE_EVENT_SOURCE_ID` | リクエスト内に同じ ID を持つ複数のソースがある | 一意の `event_source_id` 値を使用する | | `RATE_LIMITED` | 同期リクエストが多すぎる | 指数バックオフで待機してリトライする | ## ベストプラクティス 1. **ログ送信前に同期する** — `log_event` でイベントを送信する前に、必ずイベントソースを設定しなければなりません。未設定のソースへのイベント送信は拒否されます。 2. **わかりやすい ID を使用する** — 不明瞭な識別子ではなく、意味のある `event_source_id` 値(例: `web_pixel`、`app_sdk`、`crm_import`)を選ぶべきです。 3. **event\_types を指定する** — より良いバリデーションとデバッグのために、各ソースを関連するイベントタイプに限定すべきです。 4. **セラーの機能を確認する** — イベントソースを設定する前に、`get_adcp_capabilities` を使用してサポートされているイベントタイプ、UID タイプ、アクションソースを確認すべきです。 5. **セットアップスニペットをインストールする** — レスポンスに `setup` 手順が含まれている場合、イベントをログに記録する前に提供されたスニペットをインストールしなければなりません。サーバーオンリーソース(`snippet_type: "server_only"`)はこの手順をスキップしてよいです。 6. **セラー管理ソースを処理する** — レスポンスには自分が設定していない `managed_by: "seller"` のソースが含まれる場合があります。これらは常時オンであり、追加のアトリビューションデータを提供します。 ## 次のステップ * [コンバージョントラッキング](/docs/media-buy/conversion-tracking/) — データモデル、最適化目標、エンドツーエンドのフロー * [log\_event](/docs/media-buy/task-reference/log_event) — 設定済みイベントソースにマーケティングイベントを送信します * [create\_media\_buy](/docs/media-buy/task-reference/create_media_buy#campaign-with-conversion-optimization) — イベントソースを参照するパッケージに最適化目標を設定します * [get\_media\_buy\_delivery](/docs/media-buy/task-reference/get_media_buy_delivery) — デリバリーレポートでコンバージョン指標を監視します # update_media_buy Source: https://adcp-docs-ja.pier1.co.jp/docs/media-buy/task-reference/update_media_buy 既存のメディアバイを PATCH セマンティクスで更新します。キャンペーン全体およびパッケージ単位の更新をサポートします。 **Response Time**: 即時〜日単位(`completed`、120 秒未満の `working`、または手動審査用の `submitted`) ## スコープ `update_media_buy` は、`create_media_buy` で作成された購入だけでなく、[`get_media_buys`](/docs/media-buy/task-reference/get_media_buys) が返す任意の `media_buy_id` に対して動作します。セラーエージェントは、購入が元々 AdCP の外(アドサーバーへの直接入力、レガシー API、手動トラフィッキング)で作成されたことを理由に更新を拒否してはなりません(MUST NOT)。作成サーフェスは認可の軸としてサポートされていません。アカウントの所有権がその軸です。 ビジネス上の理由(契約上の義務、プラットフォームの制約、ポリシー)により特定のアクションが特定の購入でサポートされない場合、セラーは対応する更新を暗黙に拒否するのではなく、その購入の `valid_actions`(および `available_actions[]` の対応エントリ)からそのアクションのみを省略しなければなりません(MUST)。**作成サーフェスはビジネス上の理由ではありません。** セラーは、AdCP 外で作成された購入に対するその他の点で有効な更新に `INVALID_STATE` を返してはならず(MUST NOT)、AdCP 外で予約されたという理由だけで `valid_actions` が体系的に空の購入を返してはなりません(MUST NOT)——そのパターンは購入を隠すことと区別できず、[アカウント所有権 vs 作成サーフェス](/docs/media-buy/specification#account-ownership-vs-creation-surface)のルールに違反します。 ## アクション語彙とフィールドマッピング バイヤーはアクションを通じて意図を表現します。セラーは、構造化された `available_actions[]` フィールド(権威的)と、フラットな `valid_actions[]` フィールド(レガシー、4.0 で非推奨)を通じて、各購入で利用可能なアクションを宣言します。両フィールドが存在する場合、コンシューマーは `available_actions[]` を優先しなければなりません(MUST)——それは、フラットな文字列配列が表現できない解決済みの `mode`、任意の `sla`、任意の `terms_ref` を運びます。バイヤーが `update_media_buy` リクエストを発行すると、セラーはリクエストのフィールドを一つ以上のアクションにマップし、マップされたアクションが購入の解決済み `available_actions[]` にない場合は `ACTION_NOT_ALLOWED`(`error.details` に `attempted_action`、`reason`、`currently_available_actions` を伴う)で拒否します。 このマッピングは規範的です——セラーと SDK は、実装をまたいでサーフェスが一貫するよう、リクエストフィールドとアクション識別子の変換にこの表を使わなければなりません(MUST)。 | Action | update\_media\_buy fields | Notes | | --------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------- | | `pause` | `paused: true` | | | `resume` | `paused: false` | | | `cancel` | `canceled: true`, `cancellation_reason` | 不可逆 | | `extend_flight` | `end_time`, `packages[].end_time`(現在より後) | 終了日時の後ろ倒しのみ | | `shorten_flight` | `end_time`, `packages[].end_time`(現在より前) | 終了日時の前倒しのみ | | `update_flight_dates` | `start_time`, `end_time`, `packages[].start_time`, `packages[].end_time`(シフト) | 開始シフトは extend/shorten とは別。購入レベルとパッケージレベルの両方の日付をカバー | | `increase_budget` | `packages[].budget`(引き上げ) | | | `decrease_budget` | `packages[].budget`(引き下げ) | セラーは通常、消化済み額に制約される | | `reallocate_budget` | `packages[].budget`(再配分、合計は不変) | | 変化の方向を表すアクション(`extend_flight` / `shorten_flight`、`increase_budget` / `decrease_budget` / `reallocate_budget`)は `update_fields` のパスを共有します。アクションは、どのフィールドが設定されたかではなく、要求値を購入の現在状態と比較して決まります。サーバー側のディスパッチ強制は、正しいアクションを選ぶためにリクエストと現在状態を差分しなければならず(MUST)、解決されたアクションが購入の `available_actions[]` にない場合は `ACTION_NOT_ALLOWED` で拒否しなければなりません。 \| `update_targeting` | `packages[].targeting_overlay`, `packages[].keyword_targets_add`, `packages[].keyword_targets_remove`, `packages[].negative_keywords_add`, `packages[].negative_keywords_remove` | | \| `update_pacing` | `packages[].pacing` | | \| `update_frequency_caps` | `packages[].targeting_overlay.frequency_cap` | 他のターゲティングよりフライト中に再交渉されることが多い。フィールドは複数形ではなく単数の `frequency_cap`(単一ルール)。 | \| `replace_creative` | `packages[].creatives[]` の入れ替え(割り当ては不変) | 割り当てセットの変更とは別の AM ワークフロー | \| `update_creative_assignments` | `packages[].creative_assignments` | どのクリエイティブをどこに | \| `remove_creative` | 置換ペイロードの `packages[].creatives[]` および/または `packages[].creative_assignments` からクリエイティブを省略 | 両配列は置換セマンティクスを使うため、削除は目的の事後状態をクリエイティブ不在で送ることで表現します(3.x に明示的な削除プリミティブはありません)。時間に敏感であり、追加/入れ替えに承認が必要な場合でも、セラーは self\_serve の削除をサポートすべきです(SHOULD) | \| `add_packages` | `new_packages[]` | | \| `remove_packages` | `packages[].canceled: true` | | 粗いレガシーアクション(`update_budget`、`update_dates`、`update_packages`、`sync_creatives`)は、3.0 の列挙サーフェスをまだ出しているセラー向けに、より細かい語彙を単一の値でカバーします。セラーはより細かいセットへ移行すべきです(SHOULD)。レガシー値は 4.0 で削除されます。 ### アクションモード `available_actions[]` の各エントリは、単数の `mode`(購入の現在状態に対して解決済み)を運びます。プロダクトレベルの `allowed_actions[]` テンプレートでは、プロダクトが複数の条件付きモードを提供しうる(例: 許容範囲内は `self_serve`、範囲外は `requires_approval` へエスカレーション)ため、フィールドは複数形の `modes[]` です。 | Mode | Meaning | | ------------------------ | ---------------------------------------------------------------------- | | `self_serve` | セラーはリクエストを同期的に処理する | | `conditional_self_serve` | 許容範囲内は自動承認し、範囲外はエスカレーションする(プログラマティック保証のパターン) | | `requires_approval` | Human-in-the-loop、非同期、プロポーザルのアーティファクトなし。バイヤー SDK はポーリングするか Webhook を待つ | バイヤー SDK は、同期レスポンス・条件付き処理・非同期の承認コールバックのどれを期待するかを決めるため、`mode` で分岐しなければなりません(MUST)。リクオートは 3.1 ではアクションモードとしてモデル化されていません。要求された更新が現在の見積もりの範囲を超える場合、セラーは `REQUOTE_REQUIRED` を返します。 **PATCH セマンティクス**: 指定したフィールドのみ更新し、未指定フィールドは変更しません。 **Request Schema**: [`/schemas/v3/media-buy/update-media-buy-request.json`](https://adcontextprotocol.org/schemas/v3/media-buy/update-media-buy-request.json) **Response Schema**: [`/schemas/v3/media-buy/update-media-buy-response.json`](https://adcontextprotocol.org/schemas/v3/media-buy/update-media-buy-response.json) ## クイックスタート メディアバイを作成し、一時停止します。 ```javascript JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; import { CreateMediaBuyResponseSchema, UpdateMediaBuyResponseSchema } from '@adcp/sdk'; // まず更新対象のメディアバイを作成 const uniqueRef = `test_campaign_${Date.now()}`; // 開始/終了日は未来の日付を使用 const startDate = new Date(); startDate.setDate(startDate.getDate() + 7); // Start 1 week from now const endDate = new Date(); endDate.setDate(endDate.getDate() + 37); // End 5 weeks from now const createResult = await testAgent.createMediaBuy({ brand: { domain: 'acmecorp.com' }, packages: [{ product_id: 'prod_d979b543', pricing_option_id: 'cpm_usd_fixed', format_ids: [{ agent_url: 'https://creative.adcontextprotocol.org', id: 'display_300x250_image' }], budget: 800, bid_price: 5.00 }], start_time: startDate.toISOString(), end_time: endDate.toISOString() }); if (!createResult.success) { throw new Error(`Create failed: ${createResult.error}`); } const created = CreateMediaBuyResponseSchema.parse(createResult.data); if ('errors' in created && created.errors) { throw new Error(`Create failed: ${JSON.stringify(created.errors)}`); } console.log(`Created media buy ${created.media_buy_id}`); // 続いてキャンペーンを一時停止 const updateResult = await testAgent.updateMediaBuy({ account: { brand: { domain: 'acmecorp.com' }, operator: 'acmecorp.com' }, media_buy_id: created.media_buy_id, revision: created.revision, paused: true }); if (!updateResult.success) { throw new Error(`Update failed: ${updateResult.error}`); } const updated = UpdateMediaBuyResponseSchema.parse(updateResult.data); if ('errors' in updated && updated.errors) { throw new Error(`Update failed: ${JSON.stringify(updated.errors)}`); } console.log(`Campaign ${updated.media_buy_id} paused`); ``` ```python Python theme={null} import asyncio import time from datetime import datetime, timedelta from adcp.testing import test_agent async def create_and_pause_campaign(): # まず更新対象のメディアバイを作成 unique_ref = f"test_campaign_{int(time.time() * 1000)}" # 開始/終了日は未来の日付を使用 start_date = datetime.utcnow() + timedelta(days=7) end_date = datetime.utcnow() + timedelta(days=37) create_result = await test_agent.simple.create_media_buy( brand={'domain': 'acmecorp.com'}, packages=[{ 'product_id': 'prod_d979b543', 'pricing_option_id': 'cpm_usd_fixed', 'format_ids': [{ 'agent_url': 'https://creative.adcontextprotocol.org', 'id': 'display_300x250_image' }], 'budget': 800, 'bid_price': 5.00 }], start_time=start_date.strftime('%Y-%m-%dT%H:%M:%SZ'), end_time=end_date.strftime('%Y-%m-%dT%H:%M:%SZ') ) if hasattr(create_result, 'errors') and create_result.errors: raise Exception(f"Create failed: {create_result.errors}") print(f"Created media buy {create_result.media_buy_id}") # 続いてキャンペーンを一時停止 update_result = await test_agent.simple.update_media_buy( account={'brand': {'domain': 'acmecorp.com'}, 'operator': 'acmecorp.com'}, media_buy_id=create_result.media_buy_id, revision=create_result.revision, paused=True ) if hasattr(update_result, 'errors') and update_result.errors: raise Exception(f"Update failed: {update_result.errors}") print(f"Campaign {update_result.media_buy_id} paused") asyncio.run(create_and_pause_campaign()) ``` ## リクエストパラメーター | Parameter | Type | Required | Description | | -------------------------- | ----------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------- | | `account` | [account-ref](/docs/building/by-layer/L2/accounts-and-agents#account-references) | Yes | このメディアバイを所有するアカウント。`{ "account_id": "..." }` または `{ "brand": {...}, "operator": "..." }` を渡します。ガバナンスチェックとアカウント解決に必要。 | | `media_buy_id` | string | Yes | 更新対象のセラー側メディアバイ ID | | `revision` | integer | No | 楽観的並行制御のための想定現在リビジョン。不一致の場合、セラーは `CONFLICT` で拒否します。`get_media_buys` または直近のレスポンスから取得します。 | | `start_time` | string | No | 更新後の開始日時 | | `end_time` | string | No | 更新後の終了日時 | | `paused` | boolean | No | メディアバイ全体を一時停止/再開(`true`=停止、`false`=稼働) | | `canceled` | boolean | No | メディアバイ全体をキャンセル(不可逆)。存在する場合は `true` でなければなりません。セラーは `NOT_CANCELLABLE` で拒否しうる。 | | `cancellation_reason` | string | No | キャンセルの理由 | | `packages` | PackageUpdate\[] | No | パッケージ単位の更新(下記参照) | | `reporting_webhook` | object | No | レポート用 Webhook 設定の更新(下記参照) | | `idempotency_key` | string | No | 安全なリトライのための一意キー。同じキーの更新がすでに処理済みの場合、セラーは元のレスポンスを返します。(セラー、リクエスト)ペアごとに一意でなければなりません。最低 16 文字。 | | `invoice_recipient` | [BusinessEntity](/docs/building/by-layer/L2/accounts-and-agents#billing-entity-and-invoice-recipient) | No | この購入の請求書受取人を上書きします。セラーは認可を検証しなければならず(MUST)、ガバナンスエージェントが設定されている場合は `check_governance` に含めなければなりません。 | | `new_packages` | PackageRequest\[] | No | このメディアバイに追加する新規パッケージ。`create_media_buy` のパッケージと同じ形状。`valid_actions` に `add_packages` を宣言するセラーのみサポート。 | | `push_notification_config` | object | No | 非同期処理通知用 Webhook | `account` と `media_buy_id` は常に必須です。 ### 楽観的並行制御 `revision` は想定される現在のメディアバイのリビジョンです。一部の実装は内部的にこの値を `expected_revision` と呼びますが、AdCP のワイヤー上のフィールドは `revision` です。後方互換のためこのフィールドは任意です。存在する場合、セラーは変更を伴う更新を適用する書き込みとアトミックにこれをチェックしなければなりません(MUST)。アプリケーションコードで現在値を読み、比較し、後で書き込むと、別のライターと競合して更新を失う可能性があります。保存されたリビジョンがリクエストの `revision` と異なる場合、セラーは `CONFLICT` で拒否し、変更を一切適用しません。 変更を伴うすべての更新は `revision` をインクリメントし、成功レスポンスで新しい値を返します。検証のみのリクエスト、読み取り、厳密な冪等リプレイはインクリメントしません。厳密なリプレイは以前のリビジョンを返します。クライアントは、状態変更を意図するすべての更新で最後に観測したリビジョンを渡し、`CONFLICT` を受け取ったら `get_media_buys` で再読み込みして新しい `idempotency_key` でリトライすべきです(SHOULD)。 ### Reporting Webhook オブジェクト このメディアバイの自動レポート配信を設定します。 | Parameter | Type | Required | Description | | --------------------- | --------- | -------- | --------------------------------- | | `url` | string | Yes | Webhook エンドポイント URL | | `authentication` | object | Yes | `schemes` と `credentials` を持つ認証設定 | | `reporting_frequency` | string | Yes | `hourly` / `daily` / `monthly` | | `requested_metrics` | string\[] | No | 取得したい指標(未指定ならすべて) | | `token` | string | No | 検証用のクライアントトークン(16 文字以上) | **Note**: `reporting_webhook` はキャンペーンの継続レポート設定、`push_notification_config` は「この更新が完了したら知らせて」といった非同期通知用です。 ### Package Update オブジェクト | Parameter | Type | Description | | -------------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `package_id` | string | 更新対象のセラー側パッケージ ID | | `paused` | boolean | パッケージ単位で一時停止/再開(`true`=停止、`false`=稼働) | | `canceled` | boolean | このパッケージをキャンセル(不可逆)。存在する場合は `true` でなければなりません。セラーは `NOT_CANCELLABLE` で拒否しうる。 | | `cancellation_reason` | string | このパッケージをキャンセルする理由 | | `budget` | number | 予算の更新値 | | `impressions` | number | このパッケージのインプレッション目標の更新値 | | `start_time` | string | 更新後のフライト開始日時(ISO 8601)。メディアバイの日付範囲内である必要があります。 | | `end_time` | string | 更新後のフライト終了日時(ISO 8601)。メディアバイの日付範囲内である必要があります。 | | `pacing` | string | ペーシング戦略の更新 | | `bid_price` | number | 入札額の更新(オークション商品のみ)。選択した価格オプションに `max_bid: true` がない限り、これは尊重すべき正確な入札/価格です。`max_bid: true` の場合はバイヤーの最大支払い意思額(上限)として扱われます。 | | `optimization_goals` | OptimizationGoal\[] | このパッケージの最適化目標をすべて置き換えます。置換セマンティクスを使用——省略すると目標は変更されません。 | | `targeting_overlay` | TargetingOverlay | ターゲティング制約の更新。置換セマンティクスを使用。`signal_targeting_groups` の変更は価格付きシグナル選択を変えうるため、選択されたシグナル・グループ式・シグナルの `pricing_option_id` が元の見積もりの範囲外になる場合、セラーは `REQUOTE_REQUIRED` で拒否してよい(MAY)。セラーは固定シグナル選択の変更を拒否すべきであり(SHOULD)、適用された固定/デフォルトのシグナルグループを結果のパッケージ状態でエコーしなければなりません(MUST)。 | | `catalogs` | Catalog\[] | このパッケージが宣伝するカタログを置き換えます。置換セマンティクスを使用——省略すると変更されません。 | | `keyword_targets_add` | KeywordTarget\[] | (keyword, match\_type) の同一性で追加またはアップサートするキーワードターゲット。作成時、これらは `targeting_overlay` 内の `keyword_targets` として設定されます。 | | `keyword_targets_remove` | KeywordTarget\[] | (keyword, match\_type) の同一性で削除するキーワードターゲット。 | | `negative_keywords_add` | NegativeKeyword\[] | このパッケージに追加する除外キーワード。作成時、これらは `targeting_overlay` 内の `negative_keywords` として設定されます。 | | `negative_keywords_remove` | NegativeKeyword\[] | このパッケージから削除する除外キーワード。 | | `creative_assignments` | CreativeAssignment\[] | 割り当て済みライブラリクリエイティブを置き換え(重み/プレースメント指定可) | | `creatives` | CreativeAsset\[] | このパッケージの割り当て済みインラインクリエイティブ本体を置き換えます。`media_buy.features.inline_creative_management: true` が必要。セラーが `creative.has_creative_library: true` も宣言する場合、新しい `creative_id` 値はライブラリに既存であってはなりません。 | `package_id` は更新対象のパッケージを識別するために必須です。 ## レスポンス ### 成功レスポンス | Field | Description | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `media_buy_id` | メディアバイ ID | | `media_buy_status` | 更新後のメディアバイのライフサイクルステータス(ステータスが変わる場合に存在。例: キャンセル)。3.1 の正準フィールド。[移行 › `media_buy_status`](/docs/reference/migration/media-buy-status) 参照。 | | `status` | **3.1 で非推奨、3.2 で削除**([#4906](https://github.com/adcontextprotocol/adcp/issues/4906))。代わりに `media_buy_status` を使用してください。トップレベルの `status` は、フラットにシリアライズされた MCP ワイヤー上でエンベロープのタスクステータスと衝突します。 | | `revision` | この更新後のリビジョン番号。楽観的並行制御のため、状態変更を意図する後続リクエストで使用します。厳密な冪等リプレイは以前のリビジョンを返します。 | | `implementation_date` | 変更が反映される日時 (ISO 8601)。承認待ちの場合は null | | `invoice_recipient` | 更新された請求書受取人。指定された場合にリクエストからエコーされます。セラーが請求の上書きを受諾したことを確認します。銀行詳細は省略されます(書き込み専用)。 | | `valid_actions` | この更新後にバイヤーが実行できるフラット語彙のアクション。`get_media_buys` への往復を節約します。`available_actions[]` の登場により非推奨、4.0 で削除。 | | `available_actions` | この更新後に利用可能なアクションの、購入ごとの構造化された解決。各エントリはアクション、解決済み `mode`、任意の `sla`、任意の `terms_ref` を持ちます。権威的——両方が存在する場合、バイヤー SDK はこれを `valid_actions` より優先すべきです(SHOULD)。 | | `affected_packages` | パッケージレベルの更新について、直接変更された各パッケージの更新後の完全な状態を示す Package オブジェクト配列。これは疎な差分ではなく状態のスナップショットです: セラーは `{ package_id }` のみのスタブを返してはなりません(MUST NOT)。パッケージを変更しないキャンペーンレベルの更新は空配列を返しうる。 | ### エラーレスポンス | Field | Description | | -------- | ----------------------------------------- | | `errors` | Array of error objects explaining failure | **Note**: レスポンスは判別可能なユニオン。成功フィールドか errors のどちらかのみ返されるため、成功フィールドにアクセスする前に `errors` を確認してください。 ## 主なシナリオ ### パッケージ予算の更新 特定パッケージの予算を増額: ```javascript test=false JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; import { UpdateMediaBuyResponseSchema } from '@adcp/sdk'; const result = await testAgent.updateMediaBuy({ media_buy_id: 'mb_12345', packages: [{ package_id: 'pkg_001', budget: 50000 // Increased from 30000 }] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = UpdateMediaBuyResponseSchema.parse(result.data); if ('errors' in validated && validated.errors) { throw new Error(`Update failed: ${JSON.stringify(validated.errors)}`); } const pkg = validated.affected_packages?.find(p => p.package_id === 'pkg_001'); if (pkg) { console.log(`Package budget updated to ${pkg.budget}`); } ``` ```python test=false Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import UpdateMediaBuyRequest async def increase_budget(): result = await test_agent.update_media_buy( UpdateMediaBuyRequest( media_buy_id='mb_12345', packages=[ {'package_id': 'pkg_001', 'budget': 50000} ] ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Update failed: {result.errors}") pkg = next((p for p in result.affected_packages if p.package_id == 'pkg_001'), None) if pkg: print(f"Package budget updated to {pkg.budget}") asyncio.run(increase_budget()) ``` ### キャンペーン日程の変更 キャンペーン終了日を延長: ```javascript test=false JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; import { UpdateMediaBuyResponseSchema } from '@adcp/sdk'; const result = await testAgent.updateMediaBuy({ account: { account_id: 'acc_acme_001' }, media_buy_id: 'mb_12345', end_time: '2025-09-30T23:59:59Z' }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = UpdateMediaBuyResponseSchema.parse(result.data); if ('errors' in validated && validated.errors) { throw new Error(`Update failed: ${JSON.stringify(validated.errors)}`); } console.log('Campaign end date extended'); console.log(`Effective: ${validated.implementation_date}`); ``` ```python test=false Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import UpdateMediaBuyRequest async def extend_campaign(): result = await test_agent.update_media_buy( UpdateMediaBuyRequest( account={'account_id': 'acc_acme_001'}, media_buy_id='mb_12345', end_time='2025-09-30T23:59:59Z' ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Update failed: {result.errors}") print('Campaign end date extended') print(f"Effective: {result.implementation_date}") asyncio.run(extend_campaign()) ``` ### ターゲティングの更新 地理制限を追加・変更: ```javascript test=false JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; import { UpdateMediaBuyResponseSchema } from '@adcp/sdk'; const result = await testAgent.updateMediaBuy({ media_buy_id: 'mb_12345', packages: [{ package_id: 'pkg_001', targeting_overlay: { geo_country_any_of: ['US', 'CA'], geo_region_any_of: ['CA', 'NY', 'TX', 'ON', 'QC'] } }] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = UpdateMediaBuyResponseSchema.parse(result.data); if ('errors' in validated && validated.errors) { throw new Error(`Update failed: ${JSON.stringify(validated.errors)}`); } console.log('Targeting updated successfully'); ``` ```python test=false Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import UpdateMediaBuyRequest async def update_targeting(): result = await test_agent.update_media_buy( UpdateMediaBuyRequest( media_buy_id='mb_12345', packages=[ { 'package_id': 'pkg_001', 'targeting_overlay': { 'geo_country_any_of': ['US', 'CA'], 'geo_region_any_of': ['CA', 'NY', 'TX', 'ON', 'QC'] } } ] ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Update failed: {result.errors}") print('Targeting updated successfully') asyncio.run(update_targeting()) ``` ### Replace Creatives Swap out creative assignments for a package: ```javascript test=false JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; import { UpdateMediaBuyResponseSchema } from '@adcp/sdk'; const result = await testAgent.updateMediaBuy({ media_buy_id: 'mb_12345', packages: [{ package_id: 'pkg_001', creative_assignments: [ { creative_id: 'creative_video_v2' }, { creative_id: 'creative_display_v2', weight: 60 } ] }] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = UpdateMediaBuyResponseSchema.parse(result.data); if ('errors' in validated && validated.errors) { throw new Error(`Update failed: ${JSON.stringify(validated.errors)}`); } const pkg = validated.affected_packages?.find(p => p.package_id === 'pkg_001'); const assignmentCount = pkg?.creative_assignments?.length || 0; console.log(`Assigned ${assignmentCount} creatives`); ``` ```python test=false Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import UpdateMediaBuyRequest async def replace_creatives(): result = await test_agent.update_media_buy( UpdateMediaBuyRequest( media_buy_id='mb_12345', packages=[ { 'package_id': 'pkg_001', 'creative_assignments': [ {'creative_id': 'creative_video_v2'}, {'creative_id': 'creative_display_v2', 'weight': 60} ] } ] ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Update failed: {result.errors}") pkg = next((p for p in result.affected_packages if p.package_id == 'pkg_001'), None) assignment_count = len(pkg.creative_assignments) if pkg and pkg.creative_assignments else 0 print(f"Assigned {assignment_count} creatives") asyncio.run(replace_creatives()) ``` ### Multiple Package Updates Update multiple packages in one call: ```javascript test=false JavaScript theme={null} import { testAgent } from '@adcp/sdk/testing'; import { UpdateMediaBuyResponseSchema } from '@adcp/sdk'; const result = await testAgent.updateMediaBuy({ media_buy_id: 'mb_12345', packages: [ { package_id: 'pkg_001', budget: 50000, pacing: 'front_loaded' }, { package_id: 'pkg_002', budget: 30000, paused: true } ] }); if (!result.success) { throw new Error(`Request failed: ${result.error}`); } const validated = UpdateMediaBuyResponseSchema.parse(result.data); if ('errors' in validated && validated.errors) { throw new Error(`Update failed: ${JSON.stringify(validated.errors)}`); } console.log(`Updated ${validated.affected_packages?.length || 0} packages`); ``` ```python test=false Python theme={null} import asyncio from adcp.testing import test_agent from adcp.types import UpdateMediaBuyRequest async def update_multiple_packages(): result = await test_agent.update_media_buy( UpdateMediaBuyRequest( media_buy_id='mb_12345', packages=[ { 'package_id': 'pkg_001', 'budget': 50000, 'pacing': 'front_loaded' }, { 'package_id': 'pkg_002', 'budget': 30000, 'paused': True } ] ) ) if hasattr(result, 'errors') and result.errors: raise Exception(f"Update failed: {result.errors}") print(f"Updated {len(result.affected_packages)} packages") asyncio.run(update_multiple_packages()) ``` ### メディアバイのキャンセル メディアバイ全体をキャンセルします: ```json theme={null} { "account": { "account_id": "acc_acme_001" }, "media_buy_id": "mb_12345", "canceled": true, "cancellation_reason": "Campaign strategy changed" } ``` **成功レスポンス:** ```json theme={null} { "media_buy_id": "mb_12345", "media_buy_status": "canceled", "revision": 4, "implementation_date": "2025-06-15T10:00:00Z", "affected_packages": [] } ``` ボディレベルの `media_buy_status` が、購入のライフサイクル状態に関する 3.1 の正準フィールドです。レガシーのトップレベル `status: MediaBuyStatus` 形式(例: `"status": "canceled"`)は非推奨で、3.2 で削除されます([#4906](https://github.com/adcontextprotocol/adcp/issues/4906))——それは同じルートキーでエンベロープのタスクステータスと衝突していました。移行については[移行 › `media_buy_status`](/docs/reference/migration/media-buy-status)を参照してください。 **`NOT_CANCELLABLE` エラーレスポンス:** ```json theme={null} { "errors": [{ "code": "NOT_CANCELLABLE", "message": "Media buy mb_12345 has contractual obligations preventing cancellation", "suggestion": "Contact seller to discuss cancellation options" }] } ``` **`INVALID_STATE` エラーレスポンス**(例: 完了したメディアバイを更新しようとした場合): ```json theme={null} { "errors": [{ "code": "INVALID_STATE", "message": "Media buy mb_12345 is in terminal state 'completed' and cannot be modified", "suggestion": "Check current status via get_media_buys and adjust request" }] } ``` **`REQUOTE_REQUIRED` エラーレスポンス**(更新が、見積もりの価格算定基準となったパラメータの範囲を変える場合): ```json theme={null} { "errors": [{ "code": "REQUOTE_REQUIRED", "message": "Doubling budget and extending end_time into Q4 changes the pricing basis of the current buy", "details": { "envelope_field": ["packages[0].budget", "end_time"] }, "suggestion": "Adjust the update to stay within the current quote envelope, rediscover products/terms, add packages when available, or create a separate media buy" }] } ``` ### パッケージのキャンセル メディアバイをアクティブなまま、単一のパッケージをキャンセルします: ```json theme={null} { "account": { "account_id": "acc_acme_001" }, "media_buy_id": "mb_12345", "packages": [ { "package_id": "pkg_67890", "canceled": true, "cancellation_reason": "Underperforming — reallocating budget" } ] } ``` ## What Can Be Updated ### Campaign-Level Updates ✅ **Can update:** * Start/end times (subject to seller approval) * Campaign status (active/paused/canceled) * Reporting webhook configuration (URL, frequency, metrics) ❌ **Cannot update:** * Media buy ID * Brand reference * Original package product IDs ### Package-Level Updates ✅ **Can update:** * Budget allocation * Pacing strategy * Bid prices (auction products) * Optimization goal (event source, event type, target ROAS/CPA) * Targeting overlays * Creative assignments * Package status (active/paused/canceled) * Catalog reference (replace the catalog a catalog-driven package promotes) * Creative assignments (before the package's `creative_deadline`) ❌ **Cannot update (schema-enforced via `not` constraint on `package-update.json`):** * Package ID * Product ID * Pricing option ID * Format selectors: `format_ids`, `format_option_refs`, `format_kind`, and `params` (creatives must match existing formats) ⚠️ **Append-only on update:** * `committed_metrics` — セラーは新しいエントリ(フライト中のメトリクス追加。それぞれ独自の `committed_at` タイムスタンプを持つ)を受け入れますが、既存エントリを変更または削除しようとする試みを `validation_error`(コード `IMMUTABLE_FIELD`)で拒否しなければなりません(MUST)。ランタイムでの強制です。追記専用のセマンティクスはスキーマの `not` 節では表現できません。 ## Error Handling Common errors and resolutions: | Error Code | Description | Resolution | | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `MEDIA_BUY_NOT_FOUND` | Media buy doesn't exist | Verify `media_buy_id`; for legacy correlation use `get_media_buys` + `context.internal_campaign_id` | | `PACKAGE_NOT_FOUND` | Package doesn't exist | Verify `package_id`; for legacy package correlation use `get_media_buys` + package `context.buyer_ref` | | `UPDATE_NOT_ALLOWED` | Field cannot be changed | See "What Can Be Updated" above | | `BUDGET_INSUFFICIENT` | New budget below minimum | Increase budget amount | | `POLICY_VIOLATION` | Update violates content policy | Review policy requirements | | `INVALID_STATE` | Operation not allowed in current state (e.g., updating completed/canceled media buy) | Check campaign status via `get_media_buys` | | `NOT_CANCELLABLE` | Media buy or package cannot be canceled | Check seller's cancellation policy or contact seller | | `CREATIVE_REJECTED` | Creative failed content policy review | Revise the creative per the seller's advertising policies | | `CREATIVE_DEADLINE_EXCEEDED` | Creative change submitted past the package's `creative_deadline` | Check package `creative_deadline` before submitting creative changes | | `CREATIVE_ID_EXISTS` | Creative ID already exists in the seller's creative namespace | For library-backed sellers, assign existing creatives via `creative_assignments` or update via `sync_creatives`; for inline-only sellers, use a different package-scoped `creative_id` | | `BUDGET_EXCEEDED` | Operation would exceed allocated budget | Reduce the amount or increase media buy total budget | | `CONFLICT` | Revision mismatch — another update was applied since you last read | Re-read via `get_media_buys` and retry with current `revision` | | `REQUOTE_REQUIRED` | Requested change (budget, dates, volume, targeting, or signal targeting price) falls outside the envelope the original quote was priced against | Adjust the update to fit the current quote, rediscover products/terms, add packages when `add_packages` is available, or create a separate media buy. 3.1 does not define an amendment-quote artifact for `update_media_buy`. Seller's `error.details.envelope_field` names which fields breached. | | `VALIDATION_ERROR` | Request format or business rule violation | Check error `field` and `message` for specifics | Example error response: ```json theme={null} { "errors": [{ "code": "UNSUPPORTED_FEATURE", "message": "Cannot change product_id for existing package", "field": "packages[0].product_id", "suggestion": "Create a new package with the desired product instead" }] } ``` ## Update Approval Some updates require seller approval and return pending status: * **Significant budget increases** (threshold varies by seller) * **Date range changes** affecting inventory availability * **Targeting changes** that alter campaign scope * **Creative changes** requiring policy review When approval is needed, `implementation_date` will be `null`: ```json theme={null} { "media_buy_id": "mb_12345", "implementation_date": null, "affected_packages": [] } ``` ## PATCH Semantics Only specified fields are updated - omitted fields remain unchanged: ```json theme={null} { "account": { "account_id": "acc_acme_001" }, "media_buy_id": "mb_12345", "packages": [{ "package_id": "pkg_001", "budget": 50000 }] } ``` **Array replacement**: When updating arrays (like `creative_assignments`), provide the complete new array: ```json theme={null} { "account": { "account_id": "acc_acme_001" }, "media_buy_id": "mb_12345", "packages": [{ "package_id": "pkg_001", "creative_assignments": [ { "creative_id": "creative_video_v2" }, { "creative_id": "creative_display_v2", "weight": 60 } ] }] } ``` ## Asynchronous Operations Updates may be asynchronous, especially with seller approval. ### Response Patterns **Synchronous (completed immediately)**: ```json theme={null} { "media_buy_id": "mb_12345", "implementation_date": "2025-06-15T10:00:00Z", "affected_packages": [] } ``` **Asynchronous (processing)**: ```json theme={null} { "status": "working", "message": "Processing update..." } ``` Poll for completion or use webhooks/streaming. **Manual Approval Required**: ```json theme={null} { "status": "submitted", "message": "Update requires seller approval (2-4 hours)" } ``` Will take hours to days. ### Protocol-Specific Handling AdCP tasks work across multiple protocols (MCP, A2A, REST). Each protocol handles async operations differently: * **Status checking**: Polling, webhooks, or streaming * **Updates**: Protocol-specific mechanisms * **Long-running tasks**: Different timeout and notification patterns See [Async Operations](/docs/building/by-layer/L3/async-operations) for protocol-specific async patterns and examples. ## Best Practices **1. Use Precise Updates** Update only what needs to change - don't resend unchanged values. **2. Budget Increases** Small incremental increases are more likely to be auto-approved than large jumps. **3. Pause Before Major Changes** Pause campaigns before making significant targeting or creative changes to avoid delivery issues. **4. Test with Small Changes** Test update workflows with minor changes before critical campaign modifications. **5. Monitor Status** Always check response status and `implementation_date` for approval requirements. **6. Validate Package State** Check `affected_packages` in response to confirm changes were applied correctly. ## Usage Notes * Updates are atomic - either all changes apply or none do * Both media buys and packages can be referenced by publisher IDs * Pending states (`working`, `submitted`) are normal, not errors * Orchestrators MUST handle pending states as part of normal workflow * `implementation_date` indicates when changes take effect (null if pending approval) * **Inline creatives**: `creatives` 配列はパッケージのインラインクリエイティブ本体を置き換えます。セラーが `creative.has_creative_library: true` を宣言する場合、既存のライブラリクリエイティブを更新するには [`sync_creatives`](/docs/creative/task-reference/sync_creatives) を、既存のライブラリクリエイティブを割り当てるには `creative_assignments` を使用します。セラーがクリエイティブライブラリなしで `inline_creative_management: true` を宣言する場合、インラインクリエイティブの追加・置換・削除のワークフローにはここの `packages[].creatives` を使用します。 **キャンペーンガバナンス — 変更フェーズ** バイヤーのアカウントにガバナンスエージェントが設定されている場合、セラーは更新を確定する前に、`media_buy_id`、`planned_delivery`、`phase: "modification"` を伴って [`check_governance`](/docs/governance/campaign/tasks/check_governance) を呼び出さなければなりません(MUST)。ガバナンスエージェントは、変更の大きさ、予算の再配分、新しいパラメータをキャンペーンプランに対して検証します。 完全な実行チェックのワークフローとコード例は[セラー統合ガイド](/docs/building/operating/seller-integration#execution-checks)を参照してください。 ## Next Steps After updating a media buy: 1. **Verify Changes**: Use [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) to confirm updates 2. **Upload New Creatives**: Use [`sync_creatives`](/docs/creative/task-reference/sync_creatives) if creative assignments changed 3. **Monitor Performance**: Track impact of changes on campaign metrics 4. **Optimize Further**: Use [`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback) for ongoing optimization ## Learn More * [Media Buy Lifecycle](/docs/media-buy/media-buys/) - Complete campaign workflow * [Targeting](/docs/media-buy/advanced-topics/targeting) - Targeting overlays and restrictions * [Async Operations](/docs/building/by-layer/L3/async-operations) - Async patterns and status checking * [create\_media\_buy](/docs/media-buy/task-reference/create_media_buy) - Initial campaign creation # AI 開示 Source: https://adcp-docs-ja.pier1.co.jp/docs/ai-disclosure AdCP と AgenticAdvertising.org が AI をどう使うか — 何が AI 著作、何が AI 支援、モデルとプロバイダーの開示、人間レビューをリクエストする方法。 AgenticAdvertising.org は、コンテンツを書く、画像を生成する、コードを出荷する、運用を実行するため AI を広範に使います。このページは、それが起こるすべての表面、その背後のモデル、人間レビューをリクエストする方法を名指します。 *** ## 何が AI 著作か これらの表面は、人間の編集監督を伴い、AgenticAdvertising.org が運用する AI エージェントによって主に書かれます: * **Addie** — AgenticAdvertising.org の教育アシスタントとチャットエージェント。すべての Addie チャットレスポンスは AI 生成。 * **Sage** — AdCP プロトコル説明エージェント。ドキュメントチャットのプロトコル Q\&A は Sage が回答。 * **The Prompt** — 隔週ニュースレター、Addie として一人称で著作。The Prompt は編集的で、AgenticAdvertising.org のマーケティング表面でもある。両方として読む。 * **The Build** — 3 週ごとの技術ニュースレター、Sage が著作。 * **メンバーポートレート** — メンバー向けのグラフィックノベルスタイルのイラストは AI 生成。 * **認定グレーディング** — Addie は無料の Basics トラックをモジュールごとの 3〜5 の必須実演の固定ルーブリックに対してグレードし、有料の Practitioner と Specialist トラックもグレードします。AgenticAdvertising.org はこれらの資格の発行者とグレーダーの両方です。私たちはこの利益相反を否定するのではなく開示し、任意のグレーディング決定の人間レビューがリクエストに応じて利用可能です(下記参照)。 ## 何が AI 支援か AgenticAdvertising.org の公開表面の残りのほとんどは、公開前に人間がレビューする AI コーディングアシスタントで構築されます。これは以下を含みます: * プロトコルスキーマとドキュメント(このサイト) * オープンソースリファレンス実装 * AgenticAdvertising.org スタッフが運用する管理者ツール * [adcontextprotocol](https://github.com/adcontextprotocol) 組織のほとんどの公開コード 個別の段落やプルリクエストを AI 支援としてマークしません — デフォルトは AI ツールが関与したことです。 ## モデルとプロバイダーの開示 * **Addie と Sage** は Anthropic Claude モデルで動作します。 * **画像生成**(メンバーポートレート、イラスト)は Google Gemini 画像モデルを使います。 * **プロトコル開発** は Claude Code と他のエージェンティック開発ツールを使います。 ## データ処理 * **Addie と Sage チャット** — 会話は教育品質を改善しグレーディング決定の異議を許すためログされます。ログは AgenticAdvertising.org が保持しモデルプロバイダーが訓練に使いません。EU/UK 居住者: チャット入力は私たちに代わって Anthropic によって処理されます。現在のデータプロセッサーチェーンと転送メカニズムについては [プライバシーポリシー](https://agenticadvertising.org/api/agreement?type=privacy_policy) を参照。 * **グレーディング決定** — 必須実演、クレジットを生成した Addie インタラクション、結果の評価が、学習者(または規制当局)が決定を再構築できるよう保持されます。 * **メンバーポートレート** — AI 生成画像はメンバー提供の入力から生成されます。プロンプトと生成された画像はメンバーのプロフィールに保持されます。 ## 人間レビュー 任意の AI 生成表面で人間レビューをリクエストできます。 **認定グレーディングとターゲティングレビュー SLA。** AgenticAdvertising.org はその認定の発行者と AI グレーダーの両方なので、すべてのグレーディング異議とプロトコル更新ターゲティング紛争は文書化された人間レビューパスを得ます: * **確認: 2 営業日** [certification@agenticadvertising.org](mailto:certification@agenticadvertising.org) での受領から。 * **解決:** 元のグレーディングまたはターゲティング決定に参加しなかった AgenticAdvertising.org スタッフレビュアーによって [認定苦情プロセス](/docs/learning/policies/complaints) を通じて処理。 * **支持された異議の結果**: レビュー中の問題に応じて、資格が付与されるか学習者レコードが訂正される。 * **エスカレーション**: スタッフレビュアーが解決できない異議は AgenticAdvertising.org プログラムリーダーシップにエスカレート。 * **年次透明性レポート**: AgenticAdvertising.org は AGM 資料の一部として、年 1 回異議ボリューム、支持率、決定までの中央値時間を公開します。最初のレポートは 2027-04 で終わる期間をカバー。 グレーディング異議またはプロトコル更新ターゲティング紛争を提出するには、学習者 ID、問題のモジュールまたは評価、異議を唱える特定の発見を添えて [certification@agenticadvertising.org](mailto:certification@agenticadvertising.org) にメールします。 その他の AI 表面レビューパス: * **コンテンツ訂正** — Addie の教育、Sage のプロトコル説明、または任意の AI 著作コンテンツの事実誤認は、[GitHub issues](https://github.com/adcontextprotocol/adcp/issues) または [Slack](https://join.slack.com/t/agenticads/shared_invite/zt-3c5sxvdjk-x0rVmLB3OFHVUp~WutVWZg) 経由でレポートできます。ターゲットターンアラウンドは **5 営業日**。 * **プロトコルガイダンス** — Sage は法的または規制助言の代替ではありません。コンプライアンスセンシティブな質問には、資格ある弁護士に相談してください。 ## Content provenance (C2PA) AgenticAdvertising.org が公開するすべての AI 生成画像は、AgenticAdvertising.org が署名した埋め込み [C2PA](https://c2pa.org/) マニフェストを運びます: * **メンバーポートレート** — 右下隅に可視の「AI」バッジも運ぶ(CA SB 942 可視開示パス)。 * **ニュースレターカバーアート** — The Prompt と The Build のカバー、サブスクライバーメールで出荷し OpenGraph シェアカードとしてレンダーするコピーを含む。 * **パースペクティブ記事ヒーロー画像** — 公開されたパースペクティブに添付されたすべての編集イラスト。 * **ドキュメントウォークスルーと概念イラスト** — このサイトのウォークスルーと概念説明全体に埋め込まれたパネル PNG。 マニフェストは生成ソフトウェアエージェントとして Google Gemini を識別し、IPTC digital-source-type 語彙に従いアセットを `trainedAlgorithmicMedia` としてマークし、タイムスタンププラス生成プロンプトの SHA-256(プロンプト自体ではない — ポートレートは私たちが再公開したくないメンバー提供の説明から生成される)を含みます。AgenticAdvertising.org は本番シークレットに保持された自己署名 P-256 証明書で署名します。CAI トラストリスト包含は将来のステップです。今日、公開検証者は署名を暗号的に有効として表示しますが *「発行者がトラストリストにない」* をフラグします。 **任意の AgenticAdvertising.org 画像を検証** するには [contentcredentials.org/verify](https://contentcredentials.org/verify) でファイルをアップロードするか URL を貼り付けます。 可視マークがグラフィックノベル美学を損なう編集イラストとドキュメントストーリーボードには、C2PA マニフェストが唯一の開示表面です。CA SB 942 の可視開示ルールは下流パブリッシャーではなくアップストリーム生成 AI プロバイダーをターゲットするので、この配置は AgenticAdvertising.org には擁護可能です — が、それは見落としではなく意図的な選択です。 マニフェストを運ばない AgenticAdvertising.org 生成画像を見つけたら、[issue を開いて](https://github.com/adcontextprotocol/adcp/issues) ください — 欠けているプロベナンスをバグとして扱います。 ## 規制姿勢 この開示は FTC Endorsement Guides(2023)、EU AI Act Art 50、California SB 942 に情報提供されています。外部弁護士によってレビューされていません。 * **EU AI Act Art 50(2)** と **CA SB 942** は AI 生成画像と動画の機械可読プロベナンスを要求します — これをどう満たすかについては上の [content provenance](#content-provenance-c2pa) セクションを参照。 * **FTC Endorsement Guides** は報酬のためのエンドースメントと証言に適用されます。それらは一般的な AI 著作によって発動されません。完全性のためここで呼び出します、このページ単独で満たされると主張するからではありません。 特定の AI 表面が適用される標準に達していないと思う場合、お知らせください。 ## 制度的利益相反 AgenticAdvertising.org による AI 著作と評価は、読者が知るべき方法で AgenticAdvertising.org のより広範なガバナンスと交差します: * AgenticAdvertising.org はその認定の発行者とグレーダーの両方(Addie がコースワークを評価し AgenticAdvertising.org が販売する資格を付与)。 * AgenticAdvertising.org の創設者の他の会社(Scope3)が資金と基盤 IP を貢献した — 完全な関係については [FAQ エントリー](/docs/faq#how-is-aao-related-to-scope3) を参照。 ガバナンスフレームワーク、ボード構成、忌避ルールは [CHARTER.md](https://github.com/adcontextprotocol/adcp/blob/main/CHARTER.md) に、権威的なボードリストと資金開示は [agenticadvertising.org/governance](https://agenticadvertising.org/governance) に記載されています。 *** AgenticAdvertising.org での AI の使い方への実質的な変更はこのページに反映されます。 # brand.json Specification Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/brand-json AdCP の brand.json 仕様。ファイル形式、5 つのバリアント(ポートフォリオ、リダイレクト、エージェント、権威ある場所、ブランド正準ドキュメント)、ブランド定義フィールド、相互アサーション信頼モデル、ビジュアルガイドライン、カラーウェイ、タイプスケール、商標、解決アルゴリズム。 `brand.json` ファイルは、ブランドがアイデンティティを主張し、発見可能なブランド情報を確立するための標準的な手段を提供します。異なる公開モデルに対応するために 5 つのバリアントをサポートします。 `brand.json` はブランドアイデンティティデータの正準ソースです。ここで定義されるブランドオブジェクト(logos、colors、tone、tagline)は、AdCP 全体で使われる単一のブランド定義です。タスクはドメインと brand\_id でブランドを参照します — システムは `brand.json` または[レジストリ](/docs/registry/index)から完全なアイデンティティを解決します。 ## Motivation ホールドコのブランドアイデンティティは、親が所有する**1 つ**の brand.json に存在しうる — すべての子の変更が親のファイルの編集を必要とします。Converse がロゴを更新したいなら、誰かが Nike, Inc. のファイルを編集します。ホールドコが 100 の子会社ブランドを運営するなら、100 のすべてのチームが同じモノリシックなドキュメントに集約します。ブランドチームは自分のアイデンティティを所有しますが、モノリシックな形状はコーポレート親に単一の運用チョークポイントを強制します。それはまた独立ブランドがそもそも公開することをブロックします — 他人のポートフォリオにリストされたブランドは、ドメイン管理がそれを証明できる場合でも、自身の正準データを主張するプロトコルレベルのパスを持ちません。 仕様は、ハウスが家族に誰がいるかの権威であり続けながら、子ブランドが**自身の**正準ドキュメントを公開できるようにすることでこれを解決します。インライン `brands[]` は一級のオプションのままです(親がデータを所有)。`brand_refs[]` は、正準ドキュメントが別の場所に存在するポインター子を追加します(子がデータを所有)。ハウスは自由に混合します。階層は 1 レベルの深さです — ハウスのみが所有権を宣言し、ブランド自体は子を持てません。 ## File location ブランドは `brand.json` ファイルを次の場所にホストします。 ``` https://example.com/.well-known/brand.json ``` [RFC 8615](https://datatracker.ietf.org/doc/html/rfc8615) の well-known URI 慣例に従います。 ## Variants brand.json ファイルは 5 つのバリアントをサポートします。 | # | Variant | Use when | | - | ------------------------------- | --------------------------------------------------- | | 1 | Authoritative Location Redirect | 正準ドキュメントが別の URL にホストされている | | 2 | House Redirect | ブランドドメインがより大きなハウスにロールアップする | | 3 | Brand Agent | MCP エージェントが権威あるブランドアイデンティティを提供する | | 4 | House Portfolio | ハウスがブランドを公開する(インライン、ポインター、または両方) | | 5 | Brand Canonical Document | ブランドが自身のアイデンティティを自己公開する(任意の `house_domain` ポインター付き) | バリアント 1〜3 は互いに、およびバリアント 4〜5 と相互排他的です。バリアント 4 と 5 は合成します: House Portfolio(4)は `brand_refs[]` を通じて Brand Canonical Document(5)を参照でき、Brand Canonical Document は `house_domain` を通じてハウスを指し返せます。2 つの半分がどう解決するかについては [Mutual-assertion trust model](#mutual-assertion-trust-model) を参照。 ### 1. Authoritative Location Redirect 別の URL にホストされた brand.json を指します。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "authoritative_location": "https://adcontextprotocol.org/brand/abc123/brand.json" } ``` 次の場合に使います。 * brand.json が中央でホストされている(例: サービスプロバイダーによって) * CDN 配信が必要 * マネージドブランドサービス 任意フィールド(House Redirect と共有 — [Redirect ergonomics](#redirect-ergonomics) を参照): * `redirect_reason`: キャッシュ処理のための構造化シグナル(`acquisition`、`rebrand`、`regional`、`legacy`、`consolidation`、`other`) * `redirect_effective_at`: リダイレクトが有効になった ISO タイムスタンプ * `note`: 自由テキストの根拠 ### 2. House Redirect 完全なブランドポートフォリオを含むハウスドメインを指します。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "house": "nikeinc.com", "note": "Regional site - see house for brand portfolio" } ``` 任意フィールド: * `region`: ISO 3166-1 alpha-2 国コード(例: "CN") * `redirect_reason`: キャッシュ処理のための構造化シグナル([Redirect ergonomics](#redirect-ergonomics) を参照) * `redirect_effective_at`: リダイレクトが有効になった ISO タイムスタンプ * `note`: 自由テキストの根拠 次の場合に使います。 * ブランドドメインがより大きなハウスに所有されている * 地域/ローカライズドメインがメインハウスを指す * レガシードメインが正準にリダイレクトする ### Redirect ergonomics 両方のリダイレクトバリアントは、コンシューマーが古いキャッシュ状態に留まることなくリダイレクト遷移を処理できるよう、任意の `redirect_reason` と `redirect_effective_at` を受け入れます。 `redirect_reason` は enum です。 | Value | Meaning | Caching guidance | | --------------- | --------------------------------------- | ------------------------------------ | | `acquisition` | 所有エンティティが買収された(例: 1 つのホールドコが別のホールドコを購入) | 安定するまでキャッシュ TTL を短縮。多くのフィールドが移動する可能性 | | `divestiture` | 所有エンティティがスピンアウトまたは売却された | 安定するまでキャッシュ TTL を短縮。新しい所有チェーン | | `rebrand` | ブランドまたはハウスが改名/再ポジショニング | 安定するまでキャッシュ TTL を短縮 | | `consolidation` | サブブランドがターゲットに統合された | 安定するまでキャッシュ TTL を短縮 | | `regional` | 地域/ローカライズドメインがメインハウスを指す | 安定。標準キャッシュ | | `legacy` | 正準にリダイレクトする古いドメイン | 安定。標準キャッシュ | | `other` | 上記でカバーされない理由 | 自由テキストの根拠は `note` に属する | `redirect_effective_at`(ISO 8601 タイムスタンプ)はハードなキャッシュ不変条件です: キャッシュは、このタイムスタンプより前にキャッシュされた任意のエントリを古いものとして扱い、リダイレクトを通じて再取得しなければなりません(**MUST**)。上記の TTL 短縮ガイダンスは SHOULD です。このタイムスタンプは MUST であり、M\&A や他の遷移中のキャッシュポイズニングに対する負荷を担う修正です。 これは、IAB Tech Lab の [ads.txt](https://iabtechlab.com/ads-txt/)(`OWNERDOMAIN`)と [sellers.json](https://iabtechlab.com/sellers-json/)(`seller_type`)の移行パターンに対する brand.json の類似物です。それらは歴史的に帯域外の調整に依存していました — 機械可読な遷移シグナルはありませんでした。`redirect_reason` + `redirect_effective_at` が brand.json についてそのギャップを閉じます。 例 — 買収リダイレクト: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "house": "wpp.com", "redirect_reason": "acquisition", "redirect_effective_at": "2026-04-15T00:00:00Z", "note": "Acquired by WPP April 2026" } ``` ### 3. Brand Agent ブランド情報を提供する MCP エージェントを指定します。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "brand_agent": { "url": "https://agent.acme.com/mcp", "id": "acme_brand_agent" } } ``` 任意フィールド: * `contact`: 連絡先情報 ブランドがエージェントを持つ場合、エージェントがブランドアイデンティティデータの権威あるソースです。 ### 4. House Portfolio ハウスがブランドを公開します。ハウスは、インラインの子定義、自己公開するブランドへのポインター参照、または両方を運べます。 * **`brands[]`** — インラインの子定義。**親がデータを所有。** 独自のドメインを持たないサブブランド(Nike SB、内部プロダクトライン)や、ホールドコが中央で管理したいサブブランドに最適。 * **`brand_refs[]`** — ポインターエントリ。**子が**自身の正準ドキュメント(バリアント 5)で**データを所有。** 自己公開権限を望む独自ドメインを持つサブブランド(Converse、Jordan)に最適。 特定の子は 2 つの配列の**正確に 1 つ**に現れます。`brands[]` または `brand_refs[]` の少なくとも 1 つが存在しなければなりません。 **シンプルなハウス(インラインのみ):** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "house": { "domain": "nikeinc.com", "name": "Nike, Inc.", "architecture": "hybrid" }, "brands": [ { "id": "nike", "names": [{"en": "Nike"}], "keller_type": "master", "properties": [ {"type": "website", "identifier": "nike.com", "primary": true} ] } ] } ``` **混合ハイブリッド(インライン + ポインター):** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "house": { "domain": "nikeinc.com", "name": "Nike, Inc.", "architecture": "hybrid" }, "brands": [ { "id": "nike_sb", "names": [{"en_US": "Nike SB"}], "keller_type": "sub_brand" } ], "brand_refs": [ { "domain": "converse.com", "brand_id": "converse" }, { "domain": "jordan.com", "brand_id": "jordan" } ] } ``` `brand_refs[]` エントリの形状: | Field | Required | Meaning | | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `domain` | yes | 子の正準 brand.json が存在する場所。`brand_refs[]` 内で一意でなければなりません。 | | `brand_id` | yes | ハウスのポートフォリオ内でのこのブランドの安定した識別子。`brand_refs[]` 内で一意で、`brands[]` にも現れてはなりません(MUST NOT)。 | | `managed_by` | no | このブランドを運用的に管理するエンティティのドメイン(例: ホールドコ内のエージェンシーネットワーク)。ハウスが宣言。**コンシューマーは信頼や認可の決定に使ってはなりません(MUST NOT)。** ハウス横断の集計(「BBH が管理するすべてを表示」)が意図された用途です — これは信頼フィールドではなくディレクトリフィールドです。 | | `effective_at` | no | ハウスがこの所有権クレームを確立した ISO 8601 タイムスタンプ。コンシューマーはこの日付から相互アサーションのエッジをエイジングします。省略すると、コンシューマーは自身の最初の観察からエイジングします。 | このエントリと子の `house_domain` クレームの関係についての信頼セマンティクスは、下記 [Mutual-assertion trust model](#mutual-assertion-trust-model) で定義されます。 **委任を伴うハウス(WPP):** ホールドコはブランド管理をエージェンシーネットワークに委任します。WPP plc はブランドを所有しますが、Ogilvy や BBH が実際に日々運営します。`brand_refs[]` エントリの `managed_by` は、これを所有ハウスによる一方的な宣言として捕捉します。リーフブランドはマネージャーを参照しません。UI はエージェンシービューのために BBH Sport を BBH の下でレンダリングします。信頼検証は BBH Sport → WPP のみをたどります。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "house": { "domain": "wpp.com", "name": "WPP plc" }, "brand_refs": [ { "domain": "bbh-sport.com", "brand_id": "bbh_sport", "managed_by": "bbh.com" }, { "domain": "ogilvy-toyota.com", "brand_id": "ogilvy_toyota", "managed_by": "ogilvy.com" }, { "domain": "wpp-direct.com", "brand_id": "wpp_direct" } ] } ``` `managed_by` は**ディレクトリフィールドであり、信頼フィールドではありません。** コンシューマーが実際に日々運営する主体でブランドをグループ化できるように存在します — エージェンシーネットワーク横断でインベントリをショッピングするときにバイヤー側 DSP が望む正準ビューです。コンシューマーは信頼や認可の決定に使ってはなりません(MUST NOT)。その線は依然としてリーフとハウス間の相互アサーションを通じて流れます。[Conformance](#conformance) を参照。 ### 5. Brand Canonical Document ブランドが自身のアイデンティティ属性を所有する、ブランドごとの自己公開ドキュメント。ブランド自身の `/.well-known/brand.json`(または [authoritative location redirect](#1-authoritative-location-redirect) 経由)にホストされます。ブランドは `house_domain` を通じてハウスを宣言するか、単独で立つ(親ハウスなし — Patagonia、Liquid Death — フィールドを省略)ことができます。`house_domain` 関係の信頼セマンティクスは、下記 [Mutual-assertion trust model](#mutual-assertion-trust-model) で定義されます。 **ハウス付き — Nike 傘下の Converse:** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "id": "converse", "names": [{"en_US": "Converse"}], "keller_type": "sub_brand", "house_domain": "nikeinc.com", "logos": [ {"url": "https://converse.com/logo.svg", "variant": "primary"} ], "tagline": "Sneaker for the streets" } ``` **スタンドアロン — Patagonia:** ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "id": "patagonia", "names": [{"en_US": "Patagonia"}], "keller_type": "independent", "logos": [ {"url": "https://patagonia.com/logo.svg", "variant": "primary"} ], "tagline": "We're in business to save our home planet." } ``` ブランドが独自のドメインを持ち、そのアイデンティティ(logos、colors、tone、taglines)の自己公開権限を望む場合に使います。ブランドは `brands[]` のインラインエントリと同じアイデンティティフィールドを運びます。 **このバリアント固有のトップレベルフィールド:** | Field | Type | Required | Description | | ---------------------------------- | --------------- | -------- | ------------------------------------------------------------------------------------------------ | | `id` | string | Yes | ブランド識別子(アンダースコア付きの小文字英数字) | | `names` | array | Yes | ローカライズされた名前 | | `house_domain` | string (domain) | No | このブランドが属するコーポレートハウスへのポインター。省略されるとブランドはスタンドアロンとして扱われます。シングルホップ — ブランド自体は `brand_refs[]` を宣言できません。 | | `$schema`、`version`、`last_updated` | string | No | ドキュメントメタデータ | **他のすべてのブランドアイデンティティフィールドが適用されます**(`logos`、`colors`、`fonts`、`tone`、`tagline`、`visual_guidelines`、`keller_type`、`parent_brand`、`properties[]`、`industries[]`、`target_audience`、`description`、`agents[]`、`contact`、`trademarks[]`、`data_subject_contestation` など — 完全なフィールドリストは [Brand definition](#brand-definition) を参照)。ドキュメントは、ハウス専用フィールド(`house`、`brands`、`brand_refs`、`authorized_operators`)やリダイレクトフィールド(`authoritative_location`、文字列としての `house`、`region`、`note`、`redirect_reason`、`redirect_effective_at`)を運んではなりません(MUST NOT)。 ## Mutual-assertion trust model 信頼は 2 つの層で解決します — **ブランドアイデンティティ**(logos、colors、tone、tagline など)と**ブランド関係**(このブランドを誰が所有するか、誰がそれを代弁できるか)。2 つの層はきれいに分離します。アイデンティティは単一の TLS 提供ドキュメントから検証可能です。関係は両側が相互に応答することを必要とします。 | Edge | Identity trust | Relationship trust | Notes | | ------------------------------------------------------------------- | ------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `brands[]` のインライン子 | Trusted(親の TLS がデータをカバー) | Trusted(ハウスがエントリを作成) | 古典的な形状。親がデータを所有。 | | **相互アサーション**: リーフが `house_domain: A` と言い、A の `brand_refs[]` がリーフを含む | Trusted(リーフの TLS) | Trusted(両側が相互応答) | **完全な信頼。** ガバナンス伝播、メンバー機能継承、課金可能な包含がすべて流れる。 | | **リーフのみ**: リーフが `house_domain: A` と言い、A の `brand_refs[]` がリーフを含まない | Trusted(リーフの TLS) | 未検証 | アイデンティティは本物 — ブランドは自身のドメインを管理。親クレームは未検証。ガバナンスと課金可能な包含は相互応答待ちでブロックされる。[Self-healing through notification](#self-healing-through-notification) を参照。 | | **ハウスのみ**: A の `brand_refs[]` がリーフを含み、リーフが `house_domain` を持たない | Trusted(リーフの TLS — スタンドアロンドキュメントとして) | 逆方向で未検証 | リーフは A を主張していない。リーフをスタンドアロンとして扱う。A のクレームは補助メタデータ。 | | スタンドアロン(`house_domain` なし) | Trusted(リーフの TLS) | n/a — 主張された関係なし | Patagonia、Liquid Death。ブランドはそれ自体のもの。 | **主要な非対称性。** 自身のドメインで検証済み TLS を持つリーフは、その親クレームが相互応答されているかどうかに関係なく、*自身のアイデンティティ属性*について権威を持ちます。相互アサーションがゲートするのは関係層です。素朴な「主張されたが未検証 ⇒ リーフを完全に無視」という読みは誤りです — リーフの logos/colors/tone は依然として本物です。 **スタンドアロンは第三者クレームに勝る。** `house_domain` のない Brand Canonical Document はスタンドアロンです。他のハウスの `brand_refs[]` がそれをリストしていても、リーフの沈黙が決定的です: スタンドアロンとして扱い、第三者クレームは未検証メタデータのみです。規範的な記述については [Conformance](#conformance) を参照。 検証はランタイムチェックです(クローラーが両方のドキュメントを取得して比較)。JSON スキーマはフィールドを独立して受け入れます。 ### Self-healing through notification リーフのみのエッジケースは実際に一般的です — サブブランドチームが、親のポートフォリオチームが相互エントリを追加する時間を持つ前に、自身の正準ドキュメントを立ち上げます。今日これはブランドを行き詰まらせます: アイデンティティは良好ですが、メディアバイとガバナンス決定はブロックされます。 仕様は自己修復ループを定義します。ブランドエージェント**なし**のハウスの場合: 1. ハウスがその `brand.json` のトップレベルで到達可能な `contact.email` を公開します。 2. コンシューマーがリーフのみのエッジ — リーフが `house_domain: A` を主張し、A の `brand_refs[]` がリーフを含まない — に遭遇したとき、コンシューマーは、リーフが相互応答を主張し検証待ちでブロックされていることを A の `contact.email` に通知すべきです(**SHOULD**)。 3. ハウスチームがエントリを `brand_refs[]` に追加します。次のクロール/再検証で、エッジは相互にアップグレードします。 [`verify_brand_claim`](/docs/brand-protocol/tasks/verify_brand_claim) をアドバタイズするブランドエージェント**あり**のハウスの場合、コンシューマーは代わりにエージェントに直接尋ねます — 下記 [Agent-augmented verification](#agent-augmented-verification) を参照。エージェントパスは、メールループが表現できるよりリッチな状態(`pending_review`、`transferring`、`disputed`、`licensed_in`)を表面化し、ハウスとリーフの両方がエージェントを公開する場合、静的ファイルのクロールなしに 2 つの署名付きエージェント呼び出しを通じて相互アサーションが完了します。 ### Agent-augmented verification ブランドがブランドエージェント([variant 3](#3-brand-agent)、またはバリアント 4/5 の `agents[]` の `type: "brand"` エントリ)を公開する場合、エージェントに [`verify_brand_claim`](/docs/brand-protocol/tasks/verify_brand_claim) を通じて検証の質問ができます — `claim_type`(`subsidiary` / `parent` / `property` / `trademark`)を取り、ブランドの権威ある回答を返す単一のツールです。ブランドエージェントは、サーバーの背後に隠された brand.json です: 静的ファイルが運ぶのと同じデータに加え、静的ファイルが捕捉できないよりリッチな状態(`pending_review`、`transferring`、`disputed`、`licensed_in`、`licensed_out`)です。 **信頼モデルは方向によって非対称です。** この非対称性がなければ、悪意ある、または誤ったハウスが所有していない子会社を主張し、署名だけの強さでコンシューマーに信頼を拡張させることができます。 | Direction | Trust rule | | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | **拒否** — エージェントが `disputed` または `not_ours` に署名 | 一方的に権威を持つ。ブランドは他の当事者が何を公開しようと関連を拒否する立場を持つ。任意の相互応答クレームをオーバーライドする。 | | **アサーション** — エージェントが `owned` / `pending_review` / `transferring` / `licensed_*` に署名 | 情報提供的だが単独では信頼を拡張しない。相互側が(自身のブランドエージェントの `parent` クレームタイプを通じて、または静的クロールを通じて)依然として確認しなければ、関係信頼は拡張しない。 | ハウスとリーフの両方がブランドエージェントを公開する場合、相互アサーションはエージェント層で完了できます: パートナーがハウスに `claim_type: "subsidiary"` で、リーフに `claim_type: "parent"` で `verify_brand_claim` を呼び出します。両方が `owned` → 相互アサーション、リアルタイム、両当事者による署名、クロール不要。これが信頼拡張の最もクリーンなパスです。 **相互アサーションは 2 当事者間の一貫性を証明し、立場を証明しません。** 攻撃者が制御するドメインの相互アサーションを行う 2 つのエージェントは、一致する `owned` レスポンスに署名できます。相互アサーションは、彼らが同意することのみを確認し、いずれかが基礎となるブランドに対して正当な権限を持つことは確認しません。最終的な信頼ゲートは依然としてドメイン管理 + TLS です — コンシューマー側で、コンシューマーがドメインを所有すると期待する法的/運用エンティティに対して検証されます。エージェント層での相互アサーションは下限です。コンシューマー側の立場チェック(ドメイン登録、実世界のアイデンティティ)は、高信頼の決定については依然として呼び出し元の責任です。 エージェントレスポンスはブランドの `adcp_use: "response-signing"` JWK の下で署名されます。完全なリクエスト/レスポンス形状とクレームタイプごとの詳細については、[タスクリファレンス](/docs/brand-protocol/tasks/verify_brand_claim) を参照。 ### Field resolution 継承/オーバーライドブロックはありません。各コンシューマー側の質問には単一の回答があります。 | Question | Resolution | | -------------------------------------------------- | ------------------------------------------------------- | | ブランドアイデンティティ(logos、colors、tone、tagline、voice) | ブランド自身の正準ドキュメントから読む。インライン子については、親の `brands[]` エントリから読む。 | | ブランド連絡先、properties、industries | ブランド自身の正準ドキュメントから読む。 | | Trademarks、authorized\_operators、コーポレート連絡先 | ハウスの brand.json から読む。 | | `data_subject_contestation`、コンプライアンスポリシー、規制カテゴリフラグ | ハウスレベルとブランドレベルの**最も厳格なもの**。下記参照。 | | 相互アサーション検証 | ブランドとハウスの両方を取得し、`house_domain` ↔ `brand_refs[]` を比較。 | **アイデンティティフィールド**(`name`、`names`、`logos`、`colors`、`fonts`、`tone`、`voice`、`tagline`、`visual_guidelines`、`avatar`): ブランドレベルの値が権威を持ちます。ハウスの値は参照されません。ブランドチームがブランドアイデンティティを所有します。 ### Authorized Operator Resolution `authorized_operators[]` エントリは、ブランド、国、アクティビティ、時間でスコープできます。オペレーター関係を評価するコンシューマーは、現在時刻が `valid_from` より前、または `valid_until` 以降のとき、エントリを無視しなければなりません(MUST)。省略された有効性フィールドは、ハウスが機械可読な開始または終了境界を公開していないことを意味します。 `scopes` が省略された場合、コンシューマーはエントリを、リストされたブランドと国に対する後方互換の広範な認可として扱うべきです。`scopes` が存在する場合、オペレーターはリストされたアクティビティのみ、または明示的に `all` を含む場合はすべてのアクティビティについて認可されます。 `authorized_operators[]` はパブリッシャー側のインベントリ認可を置き換えません。委任またはネットワークインベントリについては、バイヤーは依然としてパブリッシャーの一致する `adagents.json` 認可を必要とします。ブランドファイルは誰がブランドまたはハウスを代表できるかを確立し、`adagents.json` は誰がパブリッシャーのインベントリを販売できるかを確立します。 **コンプライアンスとガバナンスのフィールド**: 解決される値はハウスレベルとブランドレベルの値の**最も厳格なもの**です。ブランドはハウスのガバナンスアサーションを弱められません。より厳しい制約を追加できるだけです。次に適用されます。 * `data_subject_contestation` — 両方の連絡先がデータ主体に提示されるべきです(SHOULD。コンシューマーはいずれかを使ってもよい。ブランドレベルはハウスレベルを置き換えない) * `compliance_policies`、オーディエンス除外、規制カテゴリフラグ — 和集合として解決(より多くの制限が勝つ) これが、ホールドコがコーポレートレベルのガバナンスを公開する負荷を担う理由です: ブランドチームが自己公開によってコンプライアンスを緩められるべきではありません。アイデンティティはブランドチームが所有するものなのでブランドが勝ち、ガバナンスは法務/コンプライアンスレジームが実際に機能する方法なので最も厳格なものです。スキーマはこのルールをエンコードしません。これは解決層のセマンティクスです。 完全なクローラー手順については、下記 [Resolution algorithm](#resolution-algorithm) を参照。 ### Acquisitions and reorganizations 既存のリダイレクトバリアントは M\&A をネイティブに処理します。 1. ディール前: `dentsu.com/.well-known/brand.json` は House Portfolio です。Dentsu ブランドの正準ドキュメントは `house_domain: "dentsu.com"` と言います。 2. ディール成立: Dentsu の `brand.json` が [House Redirect](#2-house-redirect) → `{ "house": "wpp.com" }` に置き換えられます。WPP は買収したブランドを、運用継続のための `managed_by: "dentsu.com"` とディール成立日に設定された `effective_at` とともに `brand_refs[]` に追加します。 3. ディール後: 依然として `house_domain: "dentsu.com"` を指すリーフは、リダイレクトを通じて WPP に解決します。相互アサーション検証はリダイレクトチェーンをたどり([Conformance](#conformance) を参照)、保持されます。緊急のリーフ移行は不要です。 ### Adopting brand\_refs\[] for an existing portfolio 既存の House Portfolio パブリッシャー(今日インライン `brands[]` 上)は移行する必要はありません。`brand_refs[]` は追加的でプルベースです — ブランドは、そのチームが自己公開を決定したときにのみ `brands[]` から移動します。 単一ブランドの移行パス: 1. 子ブランドが自身のドメインで `/.well-known/brand.json` を Brand Canonical Document として立ち上げ、`house_domain: ""` を宣言します。 2. ハウスチームが子のエントリを `brands[]` から削除し、`{ domain, brand_id, effective_at }` を `brand_refs[]` に追加します。`brand_id` は子が使う値に一致しなければなりません(MUST)。そうでなければ、クロス配列の一意性不変条件が将来の読者を助けません。 3. クローラーが次のリフレッシュで相互アサーションを取得します。 ステップ 1 と 2 の間、リーフのみのエッジは関係層で未検証ですが、リーフの*アイデンティティ*は依然として TLS で信頼されます([Self-healing through notification](#self-healing-through-notification) を参照)。コンシューマーは、ステップ 2 を促すためにハウスの `contact.email` にメールすべきです(SHOULD)。 AAO レジストリは今日両方の形状を消費し、`brands[]` から `brand_refs[]` に移動するブランドを再分類しません — `brand_id` が移行を通じた安定した識別子です。 ### Out of scope 単一ハウス、シングルホップの信頼モデルは、意図的に 4 つの形状をカバーしません。 * **2 つの親を持つジョイントベンチャー**(Disney 以前の Hulu、ファーマの JV、自動車パートナーシップ)。リーフは最大 1 つの `house_domain` を持ちます。JV 構造は 1 つの正準親を選ぶか、持株エンティティを使わなければなりません。プロトコルは 2 親所有をモデル化しません。 * **不透明性を望む PE ロールアップとホワイトラベル取り決め。** 相互アサーションは所有権を well-known URL で公開します。公開開示なしにブランドを運営したいホールドコは、既存のモノリシックな `brands[]` 形状(リーフドメインのクローラーに親を公開しない)を使えますが、相互アサーションは使えません。相互アサーションは不透明性を検証可能性と引き換えにします — それが設計です。 * **管轄ガバナンスの乖離**(例: データ主体の争議ルールが米国親と異なるドイツの Marriott フランチャイジー)。最も厳格な解決は、ブランドレベルのパブリッシャーが制約を*追加*できるだけであることを意味します。規制の緩い管轄のブランドは、それに適用されないハウスレベルのルールを落とせません。回避策: ハウスが最も厳格な適用可能な地域ごとのルールを公開します。 * **静的な brand.json サーフェスとしての標準的なライセンス関係。** `claim_type: "trademark"` を伴う [`verify_brand_claim`](/docs/brand-protocol/tasks/verify_brand_claim) は、基礎となる関係が本物であるため `licensed_in` / `licensed_out` を返せます — Marriott フランチャイジーはライセンスの下で MARRIOTT マークを使い、音楽カタログは領域ごとにライセンスアウトされ、管轄ライセンス分割(「CN でライセンス、他では自己管理」)は一般的です。ブランドエージェントは内部記録からこれらについて語れます。**しかし `brand.json` 自体は、所有権のための `brand_refs[]` と並行する、標準的なライセンス関係の公開サーフェスをまだ持ちません。** 既存の権利プロトコル(`rights_agent`、`acquire_rights`)は、標準的な宣言ではなく*トランザクショナルな*ライセンス — ディールの交渉 — を処理します。このギャップは本物で、フォローアップとして追跡されています。verify サーフェスは、パートナーがそれらを消費する必要があるため状態を公開しますが、それらを支える静的ファイル基盤は権利プロトコルチームとともに別個の設計です。 これらのケースは brand.json ではなくガバナンス / コーポレート構造の仕様に属します。`brand.json` はブランドアイデンティティのサーフェスです。コーポレート法務構造とライセンス関係の公開は、それぞれ独自の関心事です。 ## House definition house オブジェクトはコーポレートエンティティを表します。 | Field | Type | Required | Description | | -------------- | ------ | -------- | ---------------------------------------------- | | `domain` | string | Yes | ハウスの主要ドメイン | | `name` | string | Yes | 表示名 | | `names` | array | No | ローカライズされた名前 | | `architecture` | enum | No | `branded_house`、`house_of_brands`、または `hybrid` | ## Brand definition `brands` 配列内の各ブランド: | Field | Type | Required | Description | | ------------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------- | | `id` | string | Yes | ブランド識別子(アンダースコア付きの小文字英数字) | | `names` | array | Yes | ローカライズされた名前(下記参照) | | `keller_type` | enum | No | `master`、`sub_brand`、`endorsed`、`independent` | | `parent_brand` | string | No | 親ブランドの id | | `properties` | array | No | このブランドに関連付けられたデジタルプロパティ | | `brand_agent` | object | No | ブランドアイデンティティデータを提供するエージェント `{ url, id }` | | `rights_agent` | object | No | 権利ライセンスエージェント `{ url, id, available_uses, right_types, countries }` | | `logos` | array | No | ブランドロゴアセット | | `colors` | object | No | ブランドカラーパレット | | `fonts` | object | No | ブランドタイポグラフィ | | `tone` | object | No | ブランドのボイスとメッセージングガイドライン(`voice`、`attributes`、`dos`、`donts`) | | `tagline` | string | No | ブランドタグラインまたはスローガン | | `visual_guidelines` | object | No | 生成クリエイティブシステム向けの構造化ビジュアルルール | | `trademarks` | array | No | このブランドが所有またはライセンスする登録商標(`registry`、`number`、`mark`、任意の `status`、`license_type`、`countries`)。[Trademarks](#trademarks) を参照。 | ### Names Array 名前は言語コードでローカライズされます。 ```json theme={null} { "names": [ {"en": "Nike"}, {"en": "The Swoosh"}, {"zh": "耐克"}, {"ja": "ナイキ"} ] } ``` 言語ごとに複数のエントリが許可されます(エイリアス用)。 ### Keller Types マーケティング理論からのブランドアーキテクチャ分類: | Type | Description | Example | | ------------- | -------------------- | -------------------- | | `master` | ハウスの主要ブランド | Nike, Inc. の Nike | | `sub_brand` | 親ブランド名を運ぶ | Nike SB | | `endorsed` | 独立したアイデンティティ、親によって推奨 | Air Jordan "by Nike" | | `independent` | ハウスとは別に運営 | Converse | ### Extended color roles `colors` オブジェクトは 5 つの標準ロール(`primary`、`secondary`、`accent`、`background`、`text`)を持ちますが、ブランドはより細かい粒度のために追加のロールを提供でき、また提供すべきです。スキーマは `additionalProperties` を通じて任意の追加カラーロールを受け入れます。 ```json theme={null} { "colors": { "primary": "#FF6600", "secondary": "#0066CC", "background": "#FFFFFF", "text": "#1A1A1A", "heading": "#FF6600", "body": "#333333", "label": "#666666", "border": "#E5E5E5", "divider": "#F0F0F0", "surface_1": "#F9F9F9", "surface_2": "#F0F0F0" } } ``` | Role | Purpose | | ----------- | ----------------------- | | `heading` | 見出しテキストの色(本文テキストと異なる場合) | | `body` | 本文テキストの色 | | `label` | ラベル/キャプションテキストの色 | | `border` | ボーダー/アウトラインの色 | | `divider` | ディバイダー/セパレーターの色 | | `surface_1` | 主要サーフェス/カード背景 | | `surface_2` | 副次サーフェス背景 | これらの拡張ロールは、クリエイティブエージェントが推測せずにテキスト階層とサーフェスレベルを区別するのに役立ちます。 ## Visual guidelines `visual_guidelines` オブジェクトは、生成クリエイティブシステムがオンブランドのアセットを一貫して生成するために使える構造化ルールを提供します。これらはブランド定数です — キャンペーンごとに変わりません。 ビジュアルガイドラインは基本的なアイデンティティフィールド(`colors`、`fonts`、`logos`)を補完します。colors はブランドパレットが*何*かを定義し、visual guidelines はそれを*どう*使うかを定義します。fonts はフォントファミリーを定義し、visual guidelines はタイプスケールを定義します。 ### Photography ブランド写真が選択または生成されるときにどう見えるべきかを制御します。 ```json theme={null} { "photography": { "realism": "natural", "lighting": "soft daylight", "color_temperature": "warm", "contrast": "medium", "depth_of_field": "medium", "subject": { "people": { "age_range": "25-45", "diversity": "mixed", "mood": ["confident", "relaxed"] }, "product_focus": "in-use", "setting": "outdoor" }, "framing": { "subject_position": "center-left", "crop_style": "waist-up", "perspective": "eye-level" } } } ``` | Field | Type | Description | | ------------------------- | ------ | ------------------------------------------- | | `realism` | enum | `natural`、`stylized`、`hyperreal`、`abstract` | | `lighting` | string | ライティングスタイルの説明 | | `color_temperature` | enum | `warm`、`neutral`、`cool` | | `contrast` | enum | `low`、`medium`、`high` | | `depth_of_field` | enum | `shallow`、`medium`、`deep` | | `subject` | object | 被写体ガイドライン(people、product focus、setting) | | `framing` | object | カメラフレーミングルール(position、crop、perspective) | | `preferred_aspect_ratios` | array | 優先アスペクト比(例: `["16:9", "4:5", "1:1"]`) | | `tags` | array | 追加のスタイル記述子 | ### Graphic style ブランドグラフィックとイラストのビジュアル言語を定義します。 ```json theme={null} { "graphic_style": { "style_type": "flat_illustration", "stroke_style": "rounded", "stroke_weight": "2px", "corner_radius": "12px" } } ``` スタイルタイプ: `flat_illustration`、`geometric`、`gradient_mesh`、`editorial_collage`、`hand_drawn`、`minimal_line_art`、`3d_render`、`isometric`、`photographic_composite`。 | Field | Type | Description | | --------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `style_type` | enum | `flat_illustration`、`geometric`、`gradient_mesh`、`editorial_collage`、`hand_drawn`、`minimal_line_art`、`3d_render`、`isometric`、`photographic_composite` | | `stroke_style` | enum | `rounded`、`square`、`mixed`、`none` | | `stroke_weight` | string | ストローク太さ(例: `2px`) | | `corner_radius` | string | グラフィック/イラスト要素のコーナー半径(例: `12px`)。UI コンポーネントには `border_radius` を参照。 | | `tags` | array | 追加のスタイル記述子 | ### Shapes ビジュアルアイデンティティの一部として使われるブランドシェイプ: ```json theme={null} { "shapes": { "primary_shape": "circle", "secondary_shapes": ["rounded_rectangle", "diagonal_wave"], "usage": { "max_per_layout": 2, "overlap_allowed": true } } } ``` | Field | Type | Description | | ----------------------- | ------- | ----------------------------------------------------- | | `primary_shape` | string | 主要ブランドシェイプ(例: `circle`、`rounded_rectangle`、`hexagon`) | | `secondary_shapes` | array | ブランド語彙の副次シェイプ | | `usage.max_per_layout` | integer | レイアウトごとの最大の異なるシェイプ数 | | `usage.overlap_allowed` | boolean | シェイプが重なってよいか | ### Iconography アイコンスタイルシステムと使用ルール: ```json theme={null} { "iconography": { "style": "outline", "stroke_weight": "2px", "corner_style": "rounded", "usage": { "max_per_frame": 3, "size_ratio": "1:8" } } } ``` | Field | Type | Description | | --------------------- | ------- | -------------------------------------------------------- | | `style` | enum | `outline`、`filled`、`duotone`、`flat`、`glyph`、`hand_drawn` | | `stroke_weight` | string | アイコンのストローク太さ(例: `2px`) | | `corner_style` | enum | `rounded`、`square`、`mixed` | | `usage.max_per_frame` | integer | クリエイティブフレームごとの最大アイコン数 | | `usage.size_ratio` | string | アイコン対レイアウトのサイズ比(例: `1:8`) | ### Composition オーバーレイ、テクスチャ、背景のレイアウトルール: ```json theme={null} { "composition": { "overlays": { "gradient_style": "linear", "gradient_direction": "45deg", "opacity": "70%" }, "texture": { "style": "subtle_grain", "intensity": "low" }, "backgrounds": { "types_allowed": ["solid_color", "gradient", "image"] } } } ``` テクスチャスタイル: `none`、`subtle_grain`、`noise`、`paper`、`fabric`、`concrete`。強度: `low`、`medium`、`high`。 背景タイプ: `solid_color`、`gradient`、`blurred_photo`、`image`、`video`、`pattern`、`transparent`。 ### Border radius UI コンポーネントとレイアウト要素の名前付きボーダー半径プリセット。ボーダー半径は最も目立つブランド差別化要因の 1 つです — 大きな半径は温かく親しみやすく感じられ、小さいまたはゼロの半径は精密で編集的に感じられます。 ```json theme={null} { "border_radius": { "none": "0", "default": "12px", "small": "4px", "large": "20px", "pill": "999px" } } ``` | Field | Type | Description | | --------- | ------ | --------------------------------------- | | `none` | string | 明示的にシャープなコーナー(`0`) | | `default` | string | UI コンポーネントのデフォルトボーダー半径(例: `8px`、`12px`) | | `small` | string | コンパクト要素用の小さい半径(例: `4px`) | | `large` | string | カードとコンテナ用の大きい半径(例: `16px`、`24px`) | | `pill` | string | 完全に丸い / ピル形状(例: `999px`) | 5 つの標準レベルを超えて追加の名前付きプリセットを追加できます。 `graphic_style.corner_radius` はグラフィック/イラスト要素のデフォルト半径を定義します。`border_radius` は UI コンポーネントとレイアウト — ボタン、カード、入力、モーダル — の名前付きスケールを定義します。 ### Elevation 要素がサーフェスから浮き上がって見える方法を定義する名前付きシャドウレベル。ブランドはエレベーションをアイデンティティとして使います — ドラマチックな多層シャドウを好むものもあれば、単一の拡散シャドウを使うものもあります。 ```json theme={null} { "elevation": { "none": "none", "subtle": "0 1px 3px rgba(0,0,0,0.08)", "card": "0 4px 8px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.06)", "modal": "0 20px 25px -5px rgba(0,0,0,0.1), 0 8px 10px -6px rgba(0,0,0,0.06)" } } ``` 値は CSS `box-shadow` 構文を使います。生成システムはこれらを直接適用できます。 | Field | Type | Description | | -------- | ------ | ------------------- | | `none` | string | シャドウなし(`none`) | | `subtle` | string | インタラクティブ要素の軽い浮き上がり | | `card` | string | カードレベルのエレベーション | | `modal` | string | モーダル/オーバーレイのエレベーション | 追加の名前付きレベル(例: `dropdown`、`tooltip`)を追加できます。 ### Spacing 一貫したレイアウトリズムのためのスペーシングシステム。ベースユニットと名前付きスケールにより、クリエイティブエージェントは推測せずに正しくスペースされたレイアウトを生成できます。 ```json theme={null} { "spacing": { "unit": "8px", "scale": { "xs": "4px", "sm": "8px", "md": "16px", "lg": "24px", "xl": "32px", "2xl": "48px" } } } ``` | Field | Type | Description | | ------- | ------ | ----------------------------------------------------------------- | | `unit` | string | このスケールが設計されたベースグリッドユニット(例: `8px`)。情報提供 — エージェントは名前付きスケール値を直接使うべき。 | | `scale` | object | 名前付きスペーシングスケール(`xs` から `2xl`) | `scale` オブジェクトは標準サイズ(`xs`、`sm`、`md`、`lg`、`xl`、`2xl`)をサポートし、追加の名前付き値(例: `3xl`、`section`)を含められます。 ### Graphic elements ブランドアイデンティティの一部である再利用可能な装飾的または構造的なビジュアル要素 — 破れた紙の端、ウォーターマーク、ディバイダー、背景パターン: ```json theme={null} { "graphic_elements": [ { "name": "Paper Tear", "type": "frame", "description": "Torn paper edge used as section dividers and photo frames. Use primarily vertical orientation.", "orientation": "vertical", "colors": ["#a75230", "#f6f1f1", "#fba007"], "max_per_layout": 2 }, { "name": "Location Sketch Watermark", "type": "watermark", "description": "Light hand-drawn building sketch behind content, visible through the logo area", "colors": ["#a75230"] } ] } ``` | Field | Type | Required | Description | | ---------------- | ------- | -------- | ------------------------------------------------------------------------------- | | `name` | string | Yes | 要素名 | | `type` | enum | No | `border`、`divider`、`frame`、`watermark`、`pattern`、`texture_overlay`、`decorative` | | `description` | string | No | 要素がレイアウトでどう使われるか | | `orientation` | enum | No | `horizontal`、`vertical`、`any` | | `colors` | array | No | この要素が現れてよい色 | | `max_per_layout` | integer | No | レイアウトごとの最大インスタンス数 | ### Motion 動画、アニメーションディスプレイ、インタラクティブフォーマットのモーションとアニメーションルール: ```json theme={null} { "motion": { "transition_style": "dissolve", "animation_speed": "moderate", "easing": "ease-in-out", "text_entrance": "fade", "pacing": "lingering", "kinetic_typography": false } } ``` | Field | Type | Description | | -------------------- | ------- | ---------------------------------------------------------- | | `transition_style` | enum | `cut`、`dissolve`、`slide`、`wipe`、`zoom`、`fade` | | `animation_speed` | enum | `slow`、`moderate`、`fast` | | `easing` | string | デフォルトのイージング関数(例: `ease-in-out`、`spring`、`linear`) | | `text_entrance` | enum | `fade`、`typewriter`、`slide_up`、`slide_left`、`scale`、`none` | | `pacing` | enum | `lingering`、`moderate`、`fast_cuts` | | `kinetic_typography` | boolean | アニメーション/キネティックタイポグラフィが許可されるか | | `tags` | array | 追加のモーションスタイル記述子 | ### Logo placement 自動化されたクリエイティブ制作のためのロゴ配置とクリアスペースルール: ```json theme={null} { "logo_placement": { "preferred_position": "bottom-left", "min_clear_space": "0.5x", "min_height": "40px", "background_contrast": "any" } } ``` | Field | Type | Description | | --------------------- | ------ | ----------------------------------------------------------------------------------------- | | `preferred_position` | enum | `top-left`、`top-center`、`top-right`、`bottom-left`、`bottom-center`、`bottom-right`、`center` | | `min_clear_space` | string | ロゴ高さの倍数(例: `0.5x`、`1x`)または固定値(例: `16px`)としての最小クリアスペース | | `min_height` | string | 可読性のための最小ロゴ高さ(例: `40px`) | | `background_contrast` | enum | `light_only`、`dark_only`、`any` | ### Logo selection slots ライトロゴカード、ダークロゴカード、プロファイルマーク、CTV エンドカード、コブランドロックアップ、マーケットプレイスリスティングなどの特定のサーフェスに対して、レンダラーが正しいロゴバリアントを選ぶ必要がある場合に `logos[].slots[]` を使います。`logos[].id` は、可変のアセット URL に依存しない安定したターゲットをダウンストリームルールに与えます。 ```json theme={null} { "logos": [ { "id": "primary_horizontal", "url": "https://assets.example.com/logos/primary.svg", "orientation": "horizontal", "background": "light-bg", "variant": "primary", "slots": ["logo_card_light", "nav_header", "marketplace_listing"] }, { "id": "knockout_horizontal", "url": "https://assets.example.com/logos/knockout.svg", "orientation": "horizontal", "background": "dark-bg", "variant": "secondary", "slots": ["logo_card_dark", "ad_end_card"] } ] } ``` 正準クリエイティブフォーマットは、マニフェストアセットグループに依然として `asset_group_id: "logo"` を使います。プロダクトがそのスロットを `format_options[].params.slots[].logo_slots[]` で狭める場合、ビルダーは `logos[].slots[]` が要求されたスロットと交差する `brand.json` の `logos[]` から選択すべきです。フォーマットが `required_logo_slots[]` も宣言する場合、カバレッジの欠如は、黙って文章にフォールバックするのではなく、検証警告または承認マッピングとして表面化すべきです。 `visual_guidelines.logo_usage_rules[]` は、安定した `logo_id` に配置制約を適用できます。 ```json theme={null} { "visual_guidelines": { "logo_usage_rules": [ { "logo_id": "primary_horizontal", "slots": ["logo_card_light", "marketplace_listing"], "minimum_size": { "height": "18px" }, "clear_space": "1x cap height", "forbidden_contexts": ["photography_without_knockout"], "severity": "must" } ] } } ``` ### Color constraints アクセント専用の色、禁止された背景の組み合わせ、決して一緒に現れるべきでない色などの機械可読なカラー使用とペアリングルールのために `visual_guidelines.color_constraints[]` を使います。 ```json theme={null} { "visual_guidelines": { "color_constraints": [ { "color": { "kind": "name", "name": "market_yellow" }, "applies_to": ["accent"], "forbidden_on": [ { "kind": "surface", "surface": "background" }, { "kind": "surface", "surface": "text" } ], "never_pair_with": [{ "kind": "name", "name": "linen" }], "contexts": ["digital", "print", "ctv_end_card"], "severity": "must", "description": "Market yellow is accent only." } ] } } ``` 各 `color_ref` は明示的な `kind` 識別子を使います: `kind: "name"` は `colors{}` のキーをルックアップし、`kind: "value"` はリテラルの hex カラーを運び、`kind: "surface"` は `background`、`text`、`logo_background` などのレイアウトサーフェスを識別します。 ### Mark lockups コブランド、パートナー、スポンサー、プログラム、または副次マークのレイアウトルールのために `visual_guidelines.mark_lockups[]` を使います。これらのフィールドは、順序、スペーシング、セパレーター、サイズ比のガイダンスをクエリ可能にしながら、光学的バランシングをレイアウト/レンダーレビューに残します。 ```json theme={null} { "visual_guidelines": { "mark_lockups": [ { "lockup_type": "co_brand", "ordering": "brand_first", "brand_logo_id": "primary_horizontal", "contexts": ["partner_campaign", "sponsored_content"], "separator": { "type": "keyline", "color": { "kind": "name", "name": "text" }, "width": "1px" }, "min_gap": "1x clear space", "brand_min_optical_weight_ratio": 1, "partner_max_optical_weight_ratio": 1, "severity": "must" } ] } } ``` ### Tooling notes: ingesting brand books ブランドガイド PDF からドラフト `brand.json` を作成するツールは、アセット抽出とガイドライン解釈を分離すべきです。 決定的な抽出を使って候補アセット ID を生成します。 * 写真、モックアップ、ラスターアイコン、例のための埋め込み PDF 画像 * ベクターロゴ、マーク、カラースウォッチ、埋め込み画像ファイルでないロゴサンプルのためのレンダーページクロップ 次にマルチモーダルモデルを使って、それらの候補 ID を提案された `logos[]`、`assets[]`、`colors`、`visual_guidelines` ルールに分類します。モデルは既知の候補 ID にマップし戻すべきです。オリジナルのアセットバイトのソースとして扱うべきではありません。 ドラフト取り込み記録は、提案されたロゴエントリから証拠を指せます。 ```json theme={null} { "asset_id": "candidate_logo_0007", "source_page": 15, "extraction_method": "rendered_page_crop", "proposed_logo": { "id": "primary_wordmark_dark_card", "variant": "wordmark", "background": "dark-bg", "slots": ["logo_card_dark", "marketplace_listing"] }, "review_status": "needs_review" } ``` レビュアーがクロップを確認し、正しいスロット/背景を割り当て、候補ファイルを耐久性のある HTTPS ロゴアセット URL に置き換えるまで、これらの候補を正準 `logos[]` として直接公開しないでください。ソース PDF がホストされたアセット、オペレーター認可、または商標証拠を欠く場合、それはスキーマギャップではなく取り込み警告です。 ### Public hosted asset promotion AAO ホストのブランドアセットは、レビュアーまたは検証済み所有者がパブリックな `brand.json` 使用のためにプライベートアップロードを承認したケース用です。抽出のみでバイトが公開されることは決してありません。プライベート分析アーティファクトはパブリックアセット行とは別のままで、`brand.json` は承認後にのみパブリック URL を受け取ります。 推奨フロー: 1. アップロードまたは抽出されたファイルを分析のためにプライベートに保存します。 2. 正確な提案された `brand.json` diff とともに候補をオペレーターに提示します。 3. プロモーション前に明示的な承認を要求します。 4. 承認されたバイトを安定したパブリック HTTPS URL にプロモートします。 5. そのパブリック URL を `logos[]` に書き込みます。 AAO ホストの URL はこの形状を使います。 ```text theme={null} https://agenticadvertising.org/assets/brands/{domain}/{assetId}.{ext} ``` 例: ```json theme={null} { "logos": [ { "id": "primary_wordmark", "url": "https://agenticadvertising.org/assets/brands/acme.example/{assetId}.png", "variant": "wordmark", "slots": ["logo_card_light"], "width": 512, "height": 128 } ] } ``` パブリックロゴプロモーションは、公開された `brand.json` の外部で来歴メタデータを保持しなければなりません: オリジナルのファイル名、既知の場合のアップローダーユーザーと組織、ソースフロー、作成時刻、コンテンツタイプ、寸法、ハッシュ、パスが所有者証明・委任・コミュニティ提出のいずれか。モデレーションと所有者承認の状態は運用メタデータです。コンシューマーがそれらをパブリック URL からパースする必要はないはずです。 初期のプロモーション制限は意図的に保守的です: ロゴに必要な画像 MIME タイプのみを受け入れ(`image/png`、`image/jpeg`、`image/webp`、`image/gif`、およびサニタイズされた `image/svg+xml`)、アップロードを 5 MB に制限し、公開前にラスター寸法を検出し、SVG を積極的にサニタイズするか、サニタイザーがサーフェスで有効になるまで拒否します。プライベート署名付き URL、ファイルビューアーリンク、分析専用のオブジェクトパスは、公開された `brand.json` に現れてはなりません。 削除は URL を無関係なバイトに再利用するのではなく、パブリックアセット行をトゥームストーンすべきです。置換は新しいアセット URL を作成し、`brand.json` をそれを指すよう更新すべきです。以前に公開された URL は、キャッシュ安定性のために古い承認済みバイトを提供し続けるか、1 対 1 の後継がある場合は置換にリダイレクトするか、法的/セキュリティのテイクダウン後に `404` を返してもよいですが、同じ ID の下で異なるアセットを黙って提供してはなりません。 ### Colorways 色がどう連携するかを定義する名前付きカラーペアリング。クリエイティブブリーフが、すべての色を指定せずに「私の primary カラーウェイを使う」と参照できるようにします。 ```json theme={null} { "colorways": [ { "name": "primary", "foreground": "#FFFFFF", "background": "#FF6600", "accent": "#0066CC", "cta_foreground": "#FFFFFF", "cta_background": "#0066CC" }, { "name": "inverted", "foreground": "#FF6600", "background": "#FFFFFF", "accent": "#0066CC", "border": "#FF6600" } ] } ``` | Field | Type | Required | Description | | ---------------- | --------- | -------- | ------------------------------------------------------------------------- | | `name` | string | Yes | カラーウェイ名(例: `"primary"`、`"inverted"`、`"dark"`) | | `foreground` | hex color | Yes | テキスト/前景色 | | `background` | hex color | Yes | 背景色 | | `accent` | hex color | No | アクセント色 | | `cta_foreground` | hex color | No | CTA テキスト色 | | `cta_background` | hex color | No | CTA ボタン色 | | `border` | hex color | No | ボーダー色 | | `channels` | array | No | このカラーウェイが適用されるチャネル(例: `["online"]`、`["print", "pos"]`)。ユニバーサルなカラーウェイでは省略。 | ### Type scale 異なるテキストロールのサイズとウェイトを定義するタイポグラフィスケール: ```json theme={null} { "type_scale": { "base_width": "1080px", "heading": { "font": "primary", "size": "48px", "weight": "700", "line_height": "1.1" }, "subheading": { "font": "primary", "size": "24px", "weight": "600", "line_height": "1.3" }, "body": { "font": "secondary", "size": "16px", "weight": "400", "line_height": "1.5" }, "caption": { "font": "secondary", "size": "12px", "weight": "400", "line_height": "1.4" }, "cta": { "font": "primary", "size": "18px", "weight": "700", "text_transform": "uppercase", "letter_spacing": "0.05em" } } } ``` `font` フィールドは、ブランドの `fonts` オブジェクトで定義されたフォントロール(`"primary"`、`"secondary"`)を参照するか、フォントファミリー名を直接指定できます。 サイズがピクセルの場合、これらのサイズが設計された参照キャンバスを示すために `base_width` を使います。生成システムは他のキャンバスサイズについて比例的にスケールすべきです — `1080px` 幅用に設計された `48px` の見出しは、`320px` のモバイルリーダーボードで `14px` にスケールします。 ### Asset libraries 管理されたアセットライブラリ(アイコンセット、イラストシステム、画像コレクション)への参照。URL は人間のアクセス用です — 人がブラウザで開けるブランドポータル、プレスキット、または DAM ランディングページ。 ```json theme={null} { "asset_libraries": [ { "name": "Brand Illustrations v2", "type": "illustration_system", "url": "https://brand.example.com/illustrations", "description": "Flat illustration system with defined color guide", "color_guide": { "roles": ["base", "shadow_1", "shadow_2", "highlight_1", "highlight_2", "stroke"], "palettes": [ { "name": "orange", "colors": { "base": "#FF6600", "shadow_1": "#CC5200", "shadow_2": "#993D00", "highlight_1": "#FF8533", "highlight_2": "#FFB380", "stroke": "#662900" } } ] } } ] } ``` | Field | Type | Required | Description | | ------------- | ------------ | ----------- | ----------------------------------------------------------------------------------- | | `name` | string | Yes | アセットライブラリの表示名 | | `type` | enum | Recommended | `icon_set`、`illustration_system`、`image_library`、`video_library`、`template_library` | | `url` | string (URI) | Yes | アセットライブラリへの URL(人間のアクセス用) | | `description` | string | No | ライブラリの内容と使用の説明 | | `color_guide` | object | No | ライブラリで使われるカラーロールとパレット | `color_guide` は、ライブラリで使われるカラーパレットを生成システムに提供します — ライブラリ自体にアクセスせずにオンブランドのイラストやアイコンを生成するのに有用です。 ### Restrictions ビジュアルの禁止事項とガードレール — `tone.donts` のビジュアル版。生成システムに何を避けるべきかを伝えます。 ```json theme={null} { "restrictions": [ "Never place text over the product", "Do not use black backgrounds", "No stock photography of people on phones", "No split-screen layouts" ] } ``` ## Trademarks 登録商標は、**ハウスレベル**(コーポレートマーク — 例: Nike, Inc. が所有する NIKE)または**ブランドレベル**(ブランド固有のマーク — 例: Converse が所有する CONVERSE)に現れます。両方の配列が有効なクレームです。それらの間の解決は**和集合**です。 **ライセンスイン / ライセンスアウト関係**(MARRIOTT マークを使う Marriott フランチャイジー、領域ごとにライセンスされる音楽カタログなど)は、`claim_type: "trademark"` を伴う [`verify_brand_claim`](/docs/brand-protocol/tasks/verify_brand_claim) を通じて今日ブランドエージェント経由でクエリ可能です。標準的なライセンス関係のための静的な `brand.json` 公開サーフェス(所有権のための `brand_refs[]` と並行する)は、権利プロトコルチームとともに将来の RFC です。下記の静的 `trademarks[]` 配列は所有権のみをカバーします。ライセンスの姿勢はエージェントから来ます。 ```json theme={null} { "trademarks": [ { "registry": "USPTO", "number": "12345678", "mark": "CONVERSE", "status": "active", "license_type": "owned", "countries": ["US"] } ] } ``` | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------------------------------------------------------- | | `registry` | string | Yes | 商標レジストリ(例: `USPTO`、`EUIPO`、`JPO`、`CNIPA`) | | `number` | string | Yes | レジストリが発行した登録番号 | | `mark` | string | Yes | 公開された登録マーク | | `status` | enum | No | `active`、`pending`、`abandoned`、`cancelled`、`expired`。ステータス追跡が維持されていない場合、アクティブマークでは省略。 | | `license_type` | enum | No | `owned`(デフォルト)、`licensed_in`、`licensed_out` | | `countries` | array | No | この登録が適用される ISO 3166-1 alpha-2 国コード。グローバル、またはレジストリの管轄が暗黙的な場合は省略。 | クロス管轄の競合を持つホールドコ(異なる所有者の USPTO `CONVERSE` 対 EUIPO `CONVERSE`)は、各登録を別個のエントリとして公開し、`countries` を使ってマークが適用される場所をスコープすべきです。 ## Property definition プロパティはブランドに関連付けられたデジタルタッチポイントです。 | Field | Type | Required | Description | | -------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `type` | enum | Yes | プロパティタイプ(下記参照) | | `identifier` | string | Yes | ドメインまたはアプリ ID | | `store` | enum | No | アプリストア(`apple`、`google` など) | | `region` | string | No | ISO 国コードまたは `global` | | `primary` | boolean | No | これが主要プロパティか? | | `relationship` | enum | No | このブランドがプロパティにどう関係するか: `owned`(デフォルト)、`direct`、`delegated`、`ad_network`。委任/ネットワークの値は、双方向検証のために adagents.json の `delegation_type` に一致します。`owned` は brand.json のみの所有権クレームです。[アドネットワーク](/docs/sponsored-intelligence/networks) を参照。 | ### Property types AdCP の property-type enum に一致します。 * `website` * `mobile_app` * `ctv_app` * `desktop_app` * `dooh` * `podcast` * `radio` * `streaming_audio` ### Property relationships プロパティはデフォルトで `owned` です — ブランドがプロパティを直接運営します。所有していないインベントリを販売するネットワークと SSP について、`relationship` フィールドは商業的取り決めを宣言します。 | Value | Meaning | Example | | ------------ | ---------------------------------------------- | ------------------------------- | | `owned` | ブランドがこのプロパティを所有・運営(デフォルト) | 自身のウェブサイト | | `direct` | 第三者がテクノロジーを運営していても、ブランドが直接の販売パス | ベンダーのプラットフォームを使うパブリッシャーの社内広告チーム | | `delegated` | ブランドがマネタイズを管理 — 広告販売を担当 | フードブログを管理する Mediavine | | `ad_network` | ブランドがネットワーク/エクスチェンジの一部として販売 — 唯一のパスではなく 1 つのパス | SSP としての PubMatic | これは `sellers.json` の AdCP 版です — オペレーターがどのパブリッシャーと連携するかの公開宣言です。委任またはネットワークパスについては、パブリッシャーが自身の [adagents.json](/docs/governance/property/adagents) でエージェントの認可に一致する `delegation_type` を設定することで確認します。ファーストパーティインベントリについては、`relationship: "owned"` はオペレーターのインライン所有権宣言です。セルサイド実装は、オペレーターの `brand.json` クレームと、インベントリがパブリッシャー認可または委任の場合はパブリッシャーの一致する `adagents.json` 認可を必要とします。ステップバイステップのセルサイドパターンについては [セラーセットアップ](/docs/brand-protocol/seller-setup) を、ネットワーク固有のガイダンスについては [アドネットワーク](/docs/sponsored-intelligence/networks) を参照。 ## Resolution algorithm ドメインを正準ブランドに解決するには: 1. `https://{domain}/.well-known/brand.json` を取得します。 2. バリアントを確認します: * **authoritative\_location**: その URL から取得し、ステップ 2 から続行します。 * **house**(文字列): ハウスドメインから取得し、ステップ 2 から続行します。 * **brand\_agent**: エージェント URL を返します — エージェントが権威を持ちます。 * **House Portfolio**(`house` オブジェクト + `brands[]` および/または `brand_refs[]`): インライン子については、`properties[]` または `id` がクエリに一致するブランドを見つけます。ポインター子については、`brand_refs[].domain` をたどって一度解決します — たどったドキュメントは Brand Canonical Document でなければならず、決して別の House Portfolio であってはなりません(MUST)。 * **Brand Canonical Document**(トップレベルの `id` + `names`): ドキュメントがブランドです。`house_domain` が存在する場合、ハウスの `brand.json` を取得して、その `brand_refs[]` での相互応答を検証します(相互アサーション)。ハウス側自体が [House Redirect](#2-house-redirect) の場合、比較する前にハウス側のリダイレクトチェーンをたどります。ブランドに存在しない場合、コーポレートレベルのフィールド(例: `data_subject_contestation`)をハウスから読みます。コンプライアンスフィールドについては、ハウスとブランドの**最も厳格なもの**を解決します([Mutual-assertion trust model](#mutual-assertion-trust-model) を参照)。 3. 正準ブランド情報を返します。 最大リダイレクト深さ: 3 ホップ。ブランド → ハウスのルックアップはシングルホップです(再帰的な親ウォークなし)。信頼セマンティクスについては [Mutual-assertion trust model](#mutual-assertion-trust-model) を参照。 ## Complete examples ### Small Business ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "house": { "domain": "bobsburgers.com", "name": "Bob's Burgers LLC" }, "brands": [ { "id": "bobs_burgers", "names": [{"en": "Bob's Burgers"}], "keller_type": "master", "properties": [ {"type": "website", "identifier": "bobsburgers.com", "primary": true} ], "logos": [ { "url": "https://bobsburgers.com/logo.svg", "tags": ["icon"] } ], "colors": { "primary": "#FF6B35" } } ] } ``` ### Enterprise with Agent ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "brand_agent": { "url": "https://brand-agent.enterprise.com/mcp", "id": "enterprise_brand_agent" }, "contact": { "name": "Enterprise Brand Team", "email": "brand@enterprise.com" } } ``` ### Multi-Brand Portfolio ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "house": { "domain": "nikeinc.com", "name": "Nike, Inc.", "architecture": "hybrid" }, "brands": [ { "id": "nike", "names": [{"en": "Nike"}, {"zh": "耐克"}, {"ja": "ナイキ"}], "keller_type": "master", "properties": [ {"type": "website", "identifier": "nike.com", "primary": true}, {"type": "website", "identifier": "nike.cn", "region": "CN"}, {"type": "mobile_app", "store": "apple", "identifier": "com.nike.omega"} ] }, { "id": "air_jordan", "names": [{"en": "Air Jordan"}, {"en": "Jordan"}, {"en": "Jumpman"}], "keller_type": "endorsed", "parent_brand": "nike", "properties": [ {"type": "website", "identifier": "jordan.com", "primary": true}, {"type": "website", "identifier": "jumpman23.com"}, {"type": "mobile_app", "store": "apple", "identifier": "com.nike.snkrs"} ] }, { "id": "converse", "names": [{"en": "Converse"}], "keller_type": "independent", "properties": [ {"type": "website", "identifier": "converse.com", "primary": true} ] } ], "contact": { "name": "Nike Brand Team", "email": "brand@nike.com" } } ``` ### Talent Agency with Rights ライセンス可能な権利を持つアスリートブランドを管理するタレントエージェンシー: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "house": { "domain": "lotientertainment.com", "name": "Loti Entertainment", "architecture": "house_of_brands" }, "brands": [ { "id": "daan_janssen", "names": [{"en": "Daan Janssen"}], "description": "Dutch Olympic speed skater, 2x gold medalist", "industries": ["sports"], "logos": [ { "url": "https://cdn.lotientertainment.com/janssen/headshot.jpg", "variant": "primary" } ], "brand_agent": { "url": "https://rights.lotientertainment.com/mcp", "id": "loti_entertainment" }, "rights_agent": { "url": "https://rights.lotientertainment.com/mcp", "id": "loti_entertainment", "available_uses": ["likeness", "voice", "endorsement"], "right_types": ["talent"], "countries": ["NL", "BE", "DE"] } } ] } ``` `rights_agent` フィールドは、MCP 呼び出しなしにクローラーに何がライセンス可能かを伝えます — 利用可能な用途、権利タイプ、国。バイヤーエージェントは「音声ライセンスに利用可能なオランダのアスリート」をレジストリで検索し、インデックスされた brand.json データからマッチを見つけられます。 ### Regional Domain Redirect `nike.cn/.well-known/brand.json` 上: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "house": "nikeinc.com", "region": "CN" } ``` ## Caching 推奨キャッシュ TTL: * 正準ファイル: 24 時間 * リダイレクトファイル: 24 時間 * 失敗したルックアップ: 1 時間 ## Conformance これらの不変条件は、バリデーターとクローラーによって強制されなければなりません(MUST)。JSON スキーマはそれらを直接表現できません。 **ポートフォリオ不変条件** * **`brand_id` のクロス配列一意性。** 特定の `brand_id` は、同じハウスの `brands[]` と `brand_refs[]` の両方に現れてはなりません(MUST NOT)。パブリッシャーは 1 つを選ばなければなりません。 * **`brand_id` の配列内一意性。** 特定の `brand_id` は、同じハウスの `brands[]` 内で一意で、`brand_refs[]` 内で一意でなければなりません(MUST)。 * **`domain` の配列内一意性。** 各 `brand_refs[].domain` は配列内で一意でなければなりません(MUST)。異なる `brand_id` 値で同じドメインを指す 2 つのエントリは未定義です — ハウスごと、ドメインごとに 1 つの正準ポインターしか存在できません。 * **`house_domain` の配置。** `house_domain` は `brands[]` 内のエントリに現れてはなりません(MUST NOT)。それは Brand Canonical Document のトップレベルフィールドです。インライン子は自身の親ポインターを運べません。 **信頼不変条件** * **相互アサーションが関係信頼のエッジです。** コンシューマーは、一方的なクレームを通じて関係信頼(自動プロビジョニング、メンバー機能継承、課金可能なシート包含)を拡張してはなりません(MUST NOT)。相互アサーション(子の `house_domain` が名付けられたハウスの `brand_refs[]` エントリに一致)が正準の信頼エッジです。 * **アイデンティティは TLS のみ。** リーフブランド自身のアイデンティティ属性(logos、colors、tone、tagline、visual\_guidelines)は、相互アサーション状態に関係なく、リーフの TLS 提供ドキュメントのみに基づいて権威を持ちます。リーフのみの関係クレームはリーフのアイデンティティを無効にしません。 * **ハウス側の House Redirect はたどらなければなりません(MUST)。** 相互アサーションを検証するとき、名付けられたハウスの `brand.json` が House Redirect の場合、コンシューマーは `brand_refs[]` メンバーシップを比較する前にリダイレクトチェーン(3 ホップ制限まで)をたどらなければなりません(MUST)。そうでなければ、買収後のリーフが黙って信頼を失います。 * **スタンドアロンは第三者クレームに勝る。** `house_domain` のない Brand Canonical Document は、それについての任意の第三者ハウスの `brand_refs[]` クレームに関係なく、スタンドアロンです。リーフの沈黙が決定的です。(ここで一度述べられます。他の場所の記述的な文章はこの句に従います。) * **`managed_by` はディレクトリフィールドであり、信頼フィールドではありません。** コンシューマーは信頼や認可の決定に `managed_by` を使ってはなりません(MUST NOT)。`managed_by` による集計(「BBH が管理するすべてを表示」)が意図された用途です — これは信頼アサーションではなく、ハウス横断の運用ディレクトリです。 **解決不変条件** * **コンプライアンスフィールドは最も厳格なもの。** ガバナンスフィールド — `data_subject_contestation`、`compliance_policies`、`policy_categories`、オーディエンス除外、規制カテゴリフラグ、およびハウスも公開するブランドレベルの `disclaimers[]` を含む — について、解決される値はハウスレベルとブランドレベルの和集合/最も厳格なものです。ブランドレベルのパブリッシャーは、ハウスレベルのアサーションを弱めることに依存してはなりません(MUST NOT)。これは関係信頼とは異なります — 最も厳格なものは信頼ゲートではなく解決ルールです。 * **エッジエイジング。** 相互アサーションのエッジはエイジングされるべきです(SHOULD): コンシューマーは、パブリッシャー宣言の `brand_refs[].effective_at`(または、ない場合はコンシューマーの最初の観察)と最後の成功した再検証の間のギャップがコンシューマーの選んだ TTL を超えるとき、エッジを一方的として扱うべきです(SHOULD)。AAO の参照クローラーは 180 日でエイジングします。コンシューマーは異なる選択をしてもかまいません(MAY)。 **自己修復** * **リーフのみのエッジは通知をトリガーすべきです(SHOULD)。** コンシューマーが X 上の相互エントリなしに `house_domain: X` を主張するリーフに遭遇したとき、コンシューマーは、ハウスチームが相互応答を完了できるよう X の `contact.email` に通知すべきです(SHOULD)。通知する当事者は、フラッディングを避けるために `{leaf, house}` ペアごとにレート制限しなければなりません(MUST)。 * **エージェント強化検証。** 名付けられたハウスが `supported_tasks` で関連する `claim_type`(subsidiary / parent / property / trademark)とともに [`verify_brand_claim`](/docs/brand-protocol/tasks/verify_brand_claim) をアドバタイズするブランドエージェントを公開する場合、コンシューマーは、クロールベースの相互アサーション推論を実行することに加えてエージェントを呼び出すべきです(SHOULD)。信頼モデルは方向によって非対称です: 署名付き `disputed` / `not_ours` は任意の相互応答クレームをオーバーライドします(拒否は一方的)。署名付き `owned` / `pending_review` / `transferring` / `licensed_*` は相互アサーション検証を置き換えません — 相互側が関係信頼を拡張する前に依然として確認しなければなりません。両側がブランドエージェントを持つ場合、相互アサーションは 2 つの署名付きエージェント呼び出し(ハウスの `subsidiary` + リーフの `parent`)を通じて完了します。クロールパスは、エージェントが到達不能または `unknown` を返す場合のフォールバックです。メール通知の SHOULD は、ブランドエージェントのないハウスについて引き続き適用されます。完全な信頼表については [Agent-augmented verification](#agent-augmented-verification) を参照。 ## Prior art 相互アサーションの信頼プリミティブは、IAB Tech Lab の [`ads.txt`](https://iabtechlab.com/ads-txt/) と [`sellers.json`](https://iabtechlab.com/sellers-json/) の相互公開モデル — およびパターンがウェブバンドルからモバイルアプリにきれいに移行することを証明した [`app-ads.txt`](https://iabtechlab.com/wp-content/uploads/2019/03/app-ads.txt-v1.0-final-.pdf) 拡張 — を反映しています。バイヤーは、両側が well-known URL で関係を公開する場合にのみ、セラーのリセラーとして信頼されます。同じ信頼形状、同じ非暗号的な「誰が誰について何を主張するか」の検証、部分的な公開に対する同じ一方的/未検証へのフォールバック。デプロイされた耐久性のある業界パターンです。 well-known URL に加えた構造化 JSON リソースディスカバリーの形状は ads.txt より前からあります — IETF の類似物については [WebFinger](https://datatracker.ietf.org/doc/html/rfc7033)(RFC 7033)と [host-meta](https://datatracker.ietf.org/doc/html/rfc6415)(RFC 6415)を参照。`brand.json` は [RFC 8615](https://datatracker.ietf.org/doc/html/rfc8615) を通じてこの慣例を借用します。 AdCP 内では、[プロベナンス検証者コントラクト](https://github.com/adcontextprotocol/adcp/pull/3468)(セラー公開 / バイヤー表明 / セラー確認)が、異なるフィールドファミリーに対して同じ構築ファミリーを使います。 ## Best practices 1. **シンプルに始める**: 最小限の brand.json から始め、必要に応じて複雑さを追加する 2. **子会社にはリダイレクトを使う**: ブランドドメインをハウスドメインに指す 3. **すべてのプロパティをリストする**: 地域ドメイン、アプリ、レガシードメインを含める 4. **名前を最新に保つ**: ローカライズされた名前と一般的なエイリアスを含める 5. **ビジュアルガイドラインは任意**: 生成システムにオンブランドのアセットを一貫して生成させる必要があるときに追加する。カラーウェイと restrictions から始める — それらが最も高い即時のインパクトを持つ。 6. **ポートフォリオをリーンに保つ**: 多くのブランドを持つハウスポートフォリオでは、必要なブランドにのみビジュアルガイドラインを含める。大きなポートフォリオのすべてのブランドに完全なビジュアルガイドラインを付けると、ファイルサイズが大幅に増加する。 # ブランドエージェントの構築 Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/building-a-brand-agent AdCP ブランドエージェントを MCP サーバーとして構築します。get_brand_identity でブランドアイデンティティを提供し、get_rights と acquire_rights でタレント権利をライセンスします。パブリックデータと認可データのティアを設ける。 ブランドエージェントはブランドプロトコルのタスクを実装する MCP サーバーです。DAM、タレント事務所、ブランドポータルはブランドエージェントを構築して、データを AdCP 経由でバイヤーエージェントに提供します。 エージェントは [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) で `supported_protocols: ["brand"]` を宣言します。実装する具体的なタスクがその役割を定義します: | 役割 | タスク | 例 | | -------------- | ------------------------------- | ----------------------------- | | アイデンティティプロバイダー | `get_brand_identity` | ブランドアセットとガイドラインを提供する Acme DAM | | 権利マネージャー | `get_rights` + `acquire_rights` | タレントをライセンスする Pinnacle Agency | | 両方 | 3つすべて | アイデンティティと権利を管理する Nova Talent | ## サーバーセットアップ すべてのブランドエージェントは、AdCP タスクをツールとして登録する MCP サーバーから始まる。 ```typescript theme={null} import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { z } from "zod"; const server = new McpServer({ name: "acme-brand-agent", version: "1.0.0", }); ``` バイヤーエージェントがサポートするプロトコルを発見できるよう `get_adcp_capabilities` を登録する: ```typescript theme={null} server.tool("get_adcp_capabilities", {}, async () => ({ content: [{ type: "text", text: JSON.stringify({ supported_protocols: ["brand"], supported_tasks: ["get_brand_identity"], }), }], })); ``` ## トランスポートと HTTP セットアップ MCP サーバーを HTTP エンドポイントに接続し、バイヤーエージェントがネットワーク経由で到達できるようにする: ```typescript theme={null} import express from "express"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; const app = express(); app.use(express.json()); app.post("/mcp", async (req, res) => { const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); res.on("close", () => transport.close()); await server.connect(transport); await transport.handleRequest(req, res, req.body); }); app.listen(3000, () => console.log("Brand agent listening on port 3000")); ``` これにより `/mcp` にステートレスな HTTP エンドポイントができます。本番環境では認証ミドルウェアと CORS ヘッダーを追加します。 ## ティア1: アイデンティティのみ `get_brand_identity` を実装して、DAM またはブランドポータルからブランドデータを提供します。 ```typescript theme={null} const FIELDS_ENUM = [ "description", "industries", "keller_type", "logos", "colors", "fonts", "visual_guidelines", "tone", "tagline", "voice_synthesis", "assets", "rights", ] as const; server.tool( "get_brand_identity", "Returns brand identity data. Core fields are always public.", { brand_id: z.string().describe("Brand identifier"), fields: z.array(z.enum(FIELDS_ENUM)).optional() .describe("Sections to include. Omit for all authorized sections."), use_case: z.string().optional() .describe("Intended use case — agent tailors content accordingly"), }, async ({ brand_id, fields, use_case }, extra) => { const brand = await loadBrand(brand_id); if (!brand) { return { content: [{ type: "text", text: JSON.stringify({ errors: [{ code: "brand_not_found", message: `No brand with id '${brand_id}'` }], }) }], isError: true, }; } const isAuthorized = await checkLinkedAccount(extra); const response = buildIdentityResponse(brand, { fields, use_case, isAuthorized }); return { content: [{ type: "text", text: JSON.stringify(response) }] }; } ); ``` ## パブリックデータと認可データ すべての `get_brand_identity` レスポンスにはパブリックの基本情報が含まれます: `brand_id`、`house`、`names`、`description`、`industries`、`keller_type`、基本的な `logos`、`tagline`。認証は不要。 [`sync_accounts`](/docs/accounts/tasks/sync_accounts) でリンクされた認可済みの呼び出し元は、その基本情報に加えてより深いデータを取得できる: 高解像度アセット、音声合成設定、トーンガイドライン、権利の可用性。 ```typescript theme={null} function buildIdentityResponse(brand, { fields, use_case, isAuthorized }) { // コアフィールドは常に返される const response = { brand_id: brand.id, house: brand.house, names: brand.names, }; // 含めるセクションを決定する const publicFields = ["description", "industries", "keller_type", "logos", "tagline"]; const authorizedFields = ["colors", "fonts", "visual_guidelines", "tone", "voice_synthesis", "assets", "rights"]; const requested = fields ?? [...publicFields, ...authorizedFields]; const withheld = []; for (const field of requested) { if (publicFields.includes(field)) { response[field] = brand[field]; } else if (isAuthorized) { response[field] = brand[field]; } else { withheld.push(field); } } // 認証の背後にあるものを通知する if (withheld.length > 0) { response.available_fields = withheld; } return response; } ``` パブリックの呼び出し元が `fields: ["logos", "tone"]` をリクエストすると、logos は取得できるが tone は取得できません。レスポンスには `available_fields: ["tone"]` が含まれ、アカウントをリンクすることで何がアンロックされるかを呼び出し元が知ることができます。 ## Adding verify\_brand\_claim [`verify_brand_claim`](/docs/brand-protocol/tasks/verify_brand_claim) は、パートナーがブランドエージェントにそのアイデンティティについて権威ある yes/no の質問をできるようにします — 「この子会社はあなたのものか」「このプロパティはあなたのものか」「この商標はあなたのものか」。これはアイデンティティ層の上の階層化された機能です: 同じブランドデータに加え、静的な `brand.json` が表現できないよりリッチな状態(`pending_review`、`transferring`、`disputed`、`licensed_in`)です。 信頼モデルは方向によって非対称です。署名付きの拒否(`disputed` / `not_ours`)は一方的に権威を持ちます — ブランドは相互性なしに関連を拒否する立場を持ちます。署名付きのアサーション(`owned` / `pending_review` / `transferring` / `licensed_*`)は情報提供的ですが、単独では信頼を拡張しません。相互側が依然として確認しなければなりません。これが負荷を担う概念です — 完全な規範表については [`brand.json` § エージェント強化検証](/docs/brand-protocol/brand-json#agent-augmented-verification) を参照。 ### Capability declaration `get_adcp_capabilities` で `verify_brand_claim` をアドバタイズし、ツールごとの拡張を通じてどのクレームタイプを実装するかを宣言します。ブランドエージェントは、4 つすべてではなくスライス(例: クリエイティブクリアランス用の property のみ、またはガバナンス信頼拡張用の subsidiary+parent)を出荷してもかまいません(MAY)。 ```typescript theme={null} server.tool("get_adcp_capabilities", {}, async () => ({ content: [{ type: "text", text: JSON.stringify({ supported_protocols: ["brand"], supported_tasks: ["get_brand_identity", "verify_brand_claim"], brand: { verify_brand_claim: { supported_claim_types: ["subsidiary", "parent", "property", "trademark"], }, }, }), }], })); ``` `supported_claim_types` が省略された場合、エージェントは 4 つすべてのサポートをアドバタイズします。コンシューマーは、特定のクレームタイプに依存する前に確認しなければなりません(MUST)。サポートされていないタイプは `UNSUPPORTED_CLAIM_TYPE` を返さなければなりません(MUST)。 ### State model エージェントは各クレームタイプに対応する内部データを必要とします。`brand.json` をこれらのストアの公開投影として扱います — エージェントは同じ事実に加え、よりリッチなライフサイクル状態を提供します。 | Store | Backs | Mirrors in `brand.json` | | ------------- | -------------------------- | ----------------------------------------------------- | | 子会社ポートフォリオ | `claim_type: "subsidiary"` | `brand_refs[]` とインライン `brands[]` | | 親宣言 | `claim_type: "parent"` | リーフの正準ドキュメント上の `house_domain` | | プロパティレジストリ | `claim_type: "property"` | `properties[]` | | 商標レジストリ | `claim_type: "trademark"` | `trademarks[]` に加え内部のライセンシー側記録(今日 `brand.json` に現れない) | | ペンディングクレームキュー | `pending_review` ライフサイクル | 表現されない | | アーカイブ | `archived` ステータス | 表現されない | ```typescript theme={null} type SubsidiaryRecord = { subsidiary_brand_id: string; subsidiary_domain: string; status: "owned" | "pending_review" | "transferring" | "disputed" | "not_ours" | "archived"; first_observed_by_house_at: string; expected_resolution_window_days?: number; // REQUIRED when status is "pending_review" }; type PropertyRecord = { type: "website" | "mobile_app" | "ctv_app" | "desktop_app" | "dooh" | "podcast" | "radio" | "streaming_audio"; identifier: string; brand_id: string; relationship: "owned" | "direct" | "delegated" | "ad_network"; regions: string[]; // ISO 3166-1 alpha-2 or ["global"] status: "owned" | "transferring" | "disputed" | "not_ours" | "archived"; use_case_authorization?: Record; }; type TrademarkRecord = { registry: string; number: string; mark: string; registration_status: "active" | "pending" | "expired" | "cancelled"; countries: string[]; nice_classes: number[]; status: "owned" | "licensed_in" | "licensed_out" | "transferring" | "disputed" | "not_ours" | "archived"; licensor_domain?: string; // when status is "licensed_in" use_case_authorization?: Record; }; ``` ### Tool registration and request validation `verify_brand_claim` は `claim_type` で判別します。タイプごとに `claim` ペイロードを検証します — 必須フィールドは異なり、欠けているか不正な場合は `INVALID_INPUT` が正しいレスポンスです。 ```typescript theme={null} import { z } from "zod"; const SubsidiaryClaim = z.object({ subsidiary_domain: z.string().min(1), subsidiary_brand_id: z.string().optional(), observed_at: z.string().datetime().optional(), }); const ParentClaim = z.object({ parent_domain: z.string().min(1), claimant_says: z.string().optional(), observed_at: z.string().datetime().optional(), }); const PropertyClaim = z.object({ property: z.object({ type: z.enum(["website", "mobile_app", "ctv_app", "desktop_app", "dooh", "podcast", "radio", "streaming_audio"]), identifier: z.string().min(1), store: z.enum(["apple", "google", "amazon", "roku", "fire_tv", "samsung", "lg", "vizio", "other"]).optional(), region: z.string().optional(), }), use_case: z.string().optional(), }); const TrademarkClaim = z.object({ mark: z.string().min(1), registry: z.string().optional(), number: z.string().optional(), countries: z.array(z.string().length(2)).optional(), }); server.tool( "verify_brand_claim", "Answer an authoritative yes/no about a facet of brand identity", { claim_type: z.enum(["subsidiary", "parent", "property", "trademark"]), claim: z.unknown(), }, async ({ claim_type, claim }, extra) => { const isAuthorized = await checkLinkedAccount(extra); const callerId = await resolveCaller(extra); if (await rateLimited(callerId, claim_type, claim)) { return rateLimitedResponse(claim_type, callerId, claim); } switch (claim_type) { case "subsidiary": { const parsed = SubsidiaryClaim.safeParse(claim); if (!parsed.success) return invalidInput(parsed.error); return await answerSubsidiary(parsed.data, { isAuthorized }); } case "parent": { const parsed = ParentClaim.safeParse(claim); if (!parsed.success) return invalidInput(parsed.error); return await answerParent(parsed.data, { isAuthorized }); } case "property": { const parsed = PropertyClaim.safeParse(claim); if (!parsed.success) return invalidInput(parsed.error); return await answerProperty(parsed.data, { isAuthorized }); } case "trademark": { const parsed = TrademarkClaim.safeParse(claim); if (!parsed.success) return invalidInput(parsed.error); return await answerTrademark(parsed.data, { isAuthorized }); } } } ); ``` ### Per-claim-type response shaping `details` フィールドは `claim_type` によって異なります。内部記録から型付きレスポンスを構築し、呼び出し元がリンクされていない場合は認可専用フィールドを取り除きます。 ```typescript theme={null} async function answerSubsidiary(claim, { isAuthorized, requestContext }) { const record = await subsidiaries.findByDomain(claim.subsidiary_domain); if (!record) { return signedResponse({ claim_type: "subsidiary", verification_status: "not_ours", context_note: "We have no record of this brand.", }, requestContext); } const details: Record = {}; // Public fields if (["owned", "pending_review", "transferring"].includes(record.status)) { details.brand_id = record.subsidiary_brand_id; } // Authorized-only fields if (isAuthorized) { details.first_observed_by_house_at = record.first_observed_by_house_at; if (record.expected_resolution_window_days != null) { details.expected_resolution_window_days = record.expected_resolution_window_days; } } // expected_resolution_window_days is REQUIRED when status is pending_review, // even for unauthorized callers — surface the bound so they can age the answer. if (record.status === "pending_review" && !isAuthorized) { details.expected_resolution_window_days = record.expected_resolution_window_days; } return signedResponse({ claim_type: "subsidiary", verification_status: record.status, details, }, requestContext); } ``` 同じシェイピングパターンが `property`(パブリック呼び出し元には `details.use_case_authorization` を省略)と `trademark`(`matched_registration`、`licensor_domain`、`countries`、`nice_classes` はパブリックに保持し、`use_case_authorization` は認可の背後にゲートする)に適用されます。 ### Public vs authorized field gating `get_brand_identity` からのパブリック/認可の分割をミラーします。クレームタイプごとの分割: | Public | Authorized-only | | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | | `claim_type`、`verification_status`、`context_note`(常に) | `details.first_observed_by_house_at` | | `details.brand_id`、`details.relationship`、`details.matched_registration`、`details.countries`、`details.nice_classes`、`details.regions` | `details.expected_resolution_window_days`(`pending_review` で必須の場合を除く) | | `details.licensor_domain`(`verification_status` が `licensed_in` の場合) | `details.use_case_authorization` | キュー位置、チケット状態、チームルーティングは、いかなる層でも決して公開されません。 ### Aging contract for pending\_review エージェントは、`expected_resolution_window_days` が経過したら、`pending_review` 記録を終端ステータス(`owned`、`disputed`、`not_ours`、`transferring`、`archived`)または `unknown` に遷移させなければなりません(MUST)。コンシューマーは、古い `pending_review` レスポンスを `unknown` として扱い、クロールベースの検証にフォールバックすべきです(SHOULD)— しかしエージェントはいずれにせよ遷移を負っています。 cron 駆動のスイープが最もシンプルな実装です。 ```typescript theme={null} // Run hourly. Promotes timed-out pending_review records to unknown // unless a human reviewer has acted in the meantime. async function ageOutPendingClaims(): Promise { const now = Date.now(); const stale = await pendingClaims.findStale(now); for (const record of stale) { const elapsedDays = (now - Date.parse(record.first_observed_by_house_at)) / 86_400_000; if (elapsedDays >= record.expected_resolution_window_days) { await pendingClaims.update(record.id, { status: "unknown", aged_out_at: new Date(now).toISOString(), }); logger.info("aged_out_pending_claim", { record_id: record.id, claim_type: record.claim_type }); } } } setInterval(ageOutPendingClaims, 60 * 60 * 1000); ``` イベント駆動の実装も機能します — 記録作成時に `first_observed_by_house_at + expected_resolution_window_days` に遅延ジョブをスケジュールし、`unknown` に反転する前に人間のアクションが介入しなかったことをジョブで確認します。 ### Signing setup レスポンスはブランドの `adcp_use: "response-signing"` JWK の下で署名されます。**これは、エージェントが自身のアウトバウンド呼び出しに使う `request-signing` 鍵とは別個の鍵です** — 目的ごとの鍵の慣例に従い、受信者は JWK の `adcp_use` レベルで目的を強制します。目的をまたいで鍵を再利用することは仕様で禁止されています。 署名は、レスポンスボディの内部に運ばれる **JWS ペイロードエンベロープ**です(RFC 9421 §2.2.9 のトランスポートレスポンス署名ではありません — そのプリミティブは 3.x で未定義です)。`verify_brand_claim` と `verify_brand_claims` は、仕様の[指定タスクレスポンス署名リスト](/docs/building/by-layer/L1/security#designated-task-response-signing)にある唯一のタスクです — 閉じたリストのルールと入場基準はそこにあります。 エンベロープはレスポンスの `signed_response` フィールドに運ばれ、[`response-payload-jws-envelope.json`](https://adcontextprotocol.org/schemas/v3/core/response-payload-jws-envelope.json) に従います。`brand_domain` を呼び出し元の入力からではなく、サーバー側のテナント解決から投入します。共有マルチブランドフリートも、`brand_domain` ごとに別個のレスポンス署名鍵素材と別個の `kid` 値を必要とします。ブランドをまたいだ鍵の再利用はテナントバウンドのリプレイ分析を無効にし、`adcp_use: "response-signing"` について非準拠です。 AdCP 3.1 の適合性は、通常のタスクレスポンスに加えてネストされた `signed_response` を使います。JWS エンベロープをレスポンスボディ全体として返したプレリリースの例は 3.1 準拠ではありません。検証者は、プライベート移行中のみそのドラフト形状を受け入れてもかまいませんが(MAY)、それを準拠した指定タスクレスポンスとして扱ってはなりません(MUST NOT)。 エージェントの JWKS に JWK を公開し、`brand.json` の関連する `agents[]` エントリから参照します。 ```json theme={null} // /.well-known/jwks.json on the brand-agent origin { "keys": [ { "kty": "EC", "crv": "P-256", "kid": "brand-agent-response-2026-01", "x": "...", "y": "...", "use": "sig", "key_ops": ["verify"], "adcp_use": "response-signing" }, { "kty": "EC", "crv": "P-256", "kid": "brand-agent-request-2026-01", "x": "...", "y": "...", "use": "sig", "key_ops": ["verify"], "adcp_use": "request-signing" } ] } ``` ```json theme={null} // /.well-known/brand.json — agents[] entry { "agents": [ { "type": "brand", "url": "https://brand-agent.nikeinc.com/mcp", "id": "nikeinc_brand_agent", "jwks_uri": "https://brand-agent.nikeinc.com/.well-known/jwks.json" } ] } ``` コードでは、返す前にレスポンスペイロードに署名します。署名なしの外側のフィールドは通常のタスクコンシューマーのために残ります。署名されたペイロードが正準の証明可能なオブジェクトです。 ```typescript theme={null} import { createHash } from "node:crypto"; import { jcsCanonicalize, signResponseEnvelope } from "./signing"; // your library of choice function sha256Base64Url(input: string) { return createHash("sha256").update(input).digest("base64url"); } function signedResponse( body: Record, request: { task: "verify_brand_claim" | "verify_brand_claims"; requestBody: unknown; callerIdentity: string | null; resolvedBrandDomain: string; agentUrl: string; maxAgeSeconds: number; } ) { const now = Math.floor(Date.now() / 1000); const requestBinding = { task: request.task, brand_domain: request.resolvedBrandDomain, agent_url: request.agentUrl, caller_identity: request.callerIdentity, request: request.requestBody, }; const payload = { typ: "adcp-response-payload+jws", task: request.task, brand_domain: request.resolvedBrandDomain, agent_url: request.agentUrl, request_hash: `sha256:${sha256Base64Url(jcsCanonicalize(requestBinding))}`, iat: now, exp: now + request.maxAgeSeconds, response: body, }; const signed = signResponseEnvelope(payload, { kid: "brand-agent-response-2026-01", alg: "ES256", typ: "adcp-response-payload+jws", adcp_use: "response-signing", }); return { content: [{ type: "text", text: JSON.stringify({ ...body, signed_response: signed }) }] }; } ``` キーペア生成と JWKS 公開のパターンについては [request-signing](/docs/building/by-layer/L1/request-signing) を参照。レスポンス署名鍵は、異なる `adcp_use` タグを持つ同じ形状に従います。 **レスポンスボディミドルウェアの注意。** 署名されるオブジェクトは `signed_response.payload` で、署名検証前に RFC 8785/JCS で正規化されます。そのサブオブジェクトの外側の空白やキー順の変更は署名に影響しませんが、`signed_response.payload`、`signed_response.protected`、`signed_response.signature` を変更するミドルウェアは検証を壊します。外側の便宜フィールドが存在する場合、検証者は、それらが `signed_response.payload.response` と一致しないとき署名付きレスポンスを拒否しなければなりません(MUST)。 ### Rate limiting `{caller_identity, claim_type, claim-target}` ごとにレート制限します — 1 つの商標を叩き続けるバイヤーは多くのプロパティを調査するバイヤーとは異なり、それらを混同すると過剰または過小なブロックを招きます。制限時は `Retry-After` を返し、ハードな `RATE_LIMITED` エラーよりもキャッシュされた以前の回答を返すことを優先します。呼び出し元は古い `owned` に基づいて行動できます。`429` には基づいて行動できません。 ```typescript theme={null} const RATE_LIMIT_WINDOW_SEC = 60; const RATE_LIMIT_MAX = 30; function rateLimitKey(callerId: string, claimType: string, claim: unknown): string { const target = extractTarget(claimType, claim); // subsidiary_domain | property.identifier | mark+registry return `${callerId}::${claimType}::${target}`; } async function rateLimited(callerId: string, claimType: string, claim: unknown): Promise { const key = rateLimitKey(callerId, claimType, claim); const count = await counter.increment(key, RATE_LIMIT_WINDOW_SEC); return count > RATE_LIMIT_MAX; } async function rateLimitedResponse(claimType: string, callerId: string, claim: unknown) { const cached = await responseCache.get(rateLimitKey(callerId, claimType, claim)); if (cached) { return { content: [{ type: "text", text: JSON.stringify({ ...cached, _from_cache: true }) }] }; } return { content: [{ type: "text", text: JSON.stringify({ errors: [{ code: "RATE_LIMITED", message: "Rate limit exceeded for this claim." }], }), }], _meta: { "retry-after": String(RATE_LIMIT_WINDOW_SEC) }, isError: true, }; } ``` ### Cache headers per status レスポンスに `Cache-Control: max-age=N` を設定します。タスクページからの推奨値: | Status | max-age | | ----------------------------- | ------------- | | `owned`、`not_ours`、`disputed` | 24–72h | | `pending_review` | ≤1h | | `transferring` | ≤4h | | `licensed_in`、`licensed_out` | 24h | | `unknown` | ≤1h | | `use_case_authorization` あり | セッションごとに再チェック | コンシューマーは下方にオーバーライドしてもかまいませんが(MAY)、エージェントが提供する `max-age` を超えるべきではありません(SHOULD NOT)。 ### Notification loop for pending\_review `verify_brand_claim` 呼び出しが未知の子会社/プロパティ/商標に着地し、エージェントのポリシーが「ポートフォリオチームに尋ねる」である場合、エージェントは `pending_review` 記録をエンキューし、チームに通知を表面化します。実装はエージェント側です。一般的なパターン: * `brand.json` `contact.email` のアドレスで**ポートフォリオチームにメール**する。 * ブランドの既存トラッカー(Jira、Linear、Zendesk)で**チケットを開く**。 * ポートフォリオチャンネルに **Slack 通知**する。 通知はクレームペイロード、呼び出し元アイデンティティ、`expected_resolution_window_days` を運びます。レビュアーのアクション — 受け入れ、拒否、移転、アーカイブ — が記録のステータスを反転させ、同じクレームでの次の `verify_brand_claim` 呼び出しが終端の回答を返します。 ```typescript theme={null} async function enqueuePendingReview(claim: SubsidiaryClaim, callerId: string): Promise { const record: SubsidiaryRecord = { subsidiary_brand_id: claim.subsidiary_brand_id ?? "", subsidiary_domain: claim.subsidiary_domain, status: "pending_review", first_observed_by_house_at: new Date().toISOString(), expected_resolution_window_days: 14, }; await subsidiaries.create(record); await notifications.send({ channel: "portfolio_team", subject: `New subsidiary claim: ${claim.subsidiary_domain}`, body: { claim, caller: callerId, window_days: 14 }, }); return record; } ``` ### UI considerations エージェントが `disputed` または `not_ours` を返すと、コンシューマーは拒否を自身の UI(DSP インベントリショッピング、ポートフォリオエクスプローラー、クリエイティブクリアランス)でレンダリングします。エージェントは明確な `context_note` を負っています — その文字列は人間の前に現れます。`context_note` テキストを書く際に留意すべきコンシューマー側の慣例については、[拒否されたクレームの UI ガイダンス](/docs/brand-protocol/ui-guidance) を参照。 ## ティア2: 権利のみ タレント事務所や音楽シンクプラットフォーム向けに、権利探索とライセンスのために `get_rights` と `acquire_rights` を追加します。 ```typescript theme={null} server.tool( "get_rights", "Search for licensable rights with pricing", { query: z.string().describe("Natural language description of desired rights"), uses: z.array(z.string()).describe("Rights uses: likeness, voice, name, endorsement"), buyer_brand: z.object({ domain: z.string(), brand_id: z.string().optional(), }).optional(), brand_id: z.string().optional(), include_excluded: z.boolean().optional(), }, async ({ query, uses, buyer_brand, brand_id, include_excluded }) => { const matches = await searchRights({ query, uses, brand_id }); // buyer_brand が提供されている場合はバイヤーの互換性でフィルタリングする const { rights, excluded } = buyer_brand ? await filterByBuyerCompatibility(matches, buyer_brand) : { rights: matches, excluded: [] }; const response = { rights }; if (include_excluded) response.excluded = excluded; return { content: [{ type: "text", text: JSON.stringify(response) }] }; } ); ``` `acquire_rights` は同じパターンに従う — `get_rights` からの `rights_id` と `pricing_option_id` を受け取り、既存の契約に照らしてクリアし、生成資格情報付きの条件を返します。レスポンスには認証済みの `approval_webhook`([`push-notification-config`](https://adcontextprotocol.org/schemas/latest/core/push-notification-config.json) を使用)が含まれ、バイヤーがレビュー用のクリエイティブを送信できます。完全なスキーマは [acquire\_rights タスクリファレンス](/docs/brand-protocol/tasks/acquire_rights) を参照。 ## 機密ブランドルール ブランドには開示できないルールがあることが多い — 公人ポリシー、内部の除外リスト、法的制限。エージェントはこれらを内部で評価し、ルール自体を明かさずにサニタイズされた理由を返します。 プロトコルはシンプルな慣例でこれをサポートする: 拒否に `suggestions` が含まれている場合、バイヤーは問題を修正できます。含まれていない場合、拒否は最終的でバイヤーは次に進むべきです。 ```typescript theme={null} async function evaluateAcquisition(request, talent) { // 機密ルール — バイヤーはこれらを見ない const confidentialResult = await evaluateConfidentialRules(request, talent); if (confidentialResult.blocked) { return { status: "rejected", reason: confidentialResult.sanitized_reason, // 代替案なし — これは最終的、バイヤーが変更できることはない }; } // 実行可能な拒否 — バイヤーはリクエストを調整できる const exclusivityConflict = await checkExclusivity(request, talent); if (exclusivityConflict) { return { status: "rejected", reason: `Exclusive conflict in ${exclusivityConflict.country} through ${exclusivityConflict.end_date}`, suggestions: [ `Available in ${exclusivityConflict.alternative_countries.join(", ")}`, `Available after ${exclusivityConflict.end_date}`, ], }; } // 承認済み — 条件に進む return { status: "acquired", /* ... */ }; } ``` 同じパターンが `get_rights` の除外にも適用されます: バイヤーがクエリを調整できる場合(別のマーケット、別の日程)は除外結果に `suggestions` を含め、除外が交渉不可の場合は省略します。 ### プロービングへの防御 執拗なバイヤーエージェントが若干異なる変数で `get_rights` を呼び出す — 異なるブランド、業界、国 — 拒否のパターンから機密ルールをマッピングしようとするかもしれない。次の方法で軽減する: * **類似した機密拒否全体で一貫した汎用的な表現を使用します。** 3つの異なるルールがすべて「これはタレントのライフスタイルガイドラインに抵触します」と表示されれば、バイヤーは繰り返しの試みから何も学べない。 * **どの特定のルールがトリガーされたかに関わらず同じ理由を返します。** ルールに基づいて表現を変えないこと — サイドチャンネルが生まれる。 * **バイヤーごとに探索呼び出しをレート制限します。** `buyer_brand` ごとのクエリ量を追跡し、閾値を超えたら徐々に具体性を下げた理由を返します。 `get_rights` レスポンスの `exclusivity_status.existing_exclusives` フィールドは特に注意が必要です。具体的な契約条件(「Acme Sports がオランダで Q3 まで独占権を持っている」)を入力すると競合情報を明かすことになります。曖昧な説明(「このカテゴリーで独占的なコミットメント」)を使うか、機密性が懸念される場合はフィールドを省略します。 ## フィールド選択とユースケース `fields` パラメーターにより呼び出し元は必要なセクションのみをリクエストできます。効率的に実装する — リクエストされていない場合は高コストのデータ(アセットカタログ、音声設定)の読み込みを避ける: ```typescript theme={null} async function loadBrandData(brand_id, fields) { const brand = await db.getBrandCore(brand_id); if (!fields || fields.includes("assets")) { brand.assets = await db.getBrandAssets(brand_id); } if (!fields || fields.includes("voice_synthesis")) { brand.voice_synthesis = await voiceProvider.getConfig(brand_id); } return brand; } ``` `use_case` パラメーターは参考情報 — 返されたセクション内のコンテンツを調整するが `fields` をオーバーライドしません。`"likeness"` ユースケースは `logos` セクションでアクションフォトを優先し、`"creative_production"` ユースケースはベクターロゴとブランドマークを優先します。 ## マルチテナンシー 単一の MCP エンドポイントで複数のブランドを提供できます。各リクエストの `brand_id` パラメーターが呼び出し元がどのブランドについて尋ねているかを明確にします。 ```typescript theme={null} // 1つのエージェント、多くのブランド const brands = { "emma_torres": { house: { domain: "pinnacleagency.com", name: "Pinnacle Agency" }, ... }, "kai_nakamura": { house: { domain: "pinnacleagency.com", name: "Pinnacle Agency" }, ... }, }; async function loadBrand(brand_id) { return brands[brand_id] ?? null; } ``` ロスター内の各ブランドは、バイヤーエージェントが MCP 呼び出しをする前に発見できるよう `brand.json` ファイルの `brands` 配列にも表示される必要があります。 ## アカウントリンク バイヤーはあなたのエージェントで [`sync_accounts`](/docs/accounts/tasks/sync_accounts) を呼び出すことで認可を確立します。リンク後、その後の `get_brand_identity` リクエストは認可済みと認識されます。 これをサポートするために [アカウントプロトコル](/docs/accounts/overview) を実装します。リンクされたアカウントは MCP トランスポートの呼び出し元の資格情報で識別される — ブランドプロトコルリクエストにアカウント ID を渡す必要はない。 ### 呼び出し元アイデンティティの抽出 ```typescript theme={null} async function checkLinkedAccount(extra: any): Promise { // 呼び出し元のアイデンティティは認証ミドルウェアから来る。 // sync_accounts がバイヤーをリンクした後、資格情報を保存し // その後のリクエストで確認する。 const sessionId = extra?.sessionId; if (!sessionId) return false; return await db.isLinkedAccount(sessionId); } ``` 呼び出し元の識別方法は認証セットアップによる。MCP トランスポートがセッション情報を提供し、認証ミドルウェアがそれをリンクされたアカウントにマッピングします。パターンについては [認証ガイド](/docs/building/by-layer/L2/authentication) を参照。 ## 権利とクリエイティブの統合 バイヤーが `acquire_rights` で権利を取得すると、`generation_credentials` と `rights_constraint` を受け取ります。これらは権利付与とクリエイティブ制作をつなぐ。 ### ブランドエージェント側の視点 `acquire_rights` を実装する際は、承認後にレスポンスで両方を返します: ```typescript theme={null} // acquire_rights ハンドラーで、承認後: const response = { status: "acquired", rights_id: "rgt_dj_001", terms: { /* ... 価格、日程、制限 */ }, generation_credentials: [ { provider: "midjourney", rights_key: "rk_dj_likeness_2026_abc", uses: ["likeness"], expires_at: "2026-06-15T00:00:00Z", }, ], rights_constraint: { rights_id: "rgt_dj_001", rights_agent: { url: "https://rights.lotientertainment.com/mcp", id: "loti_entertainment" }, valid_from: "2026-03-15T00:00:00Z", valid_until: "2026-06-15T23:59:59Z", uses: ["likeness"], countries: ["NL"], impression_cap: 100000, approval_status: "approved", }, }; ``` ### バイヤー側での使用方法 バイヤーのオーケストレーターが `generation_credentials` をクリエイティブエージェントに渡し、クリエイティブエージェントがそれを AI プロバイダーで使用します。`rights_constraint` はクリエイティブマニフェストの `rights` 配列に埋め込まれる — クリエイティブと一緒にサプライチェーンを伝わり、チェーン内のすべてのシステムが使用条件を知ることができます。 ```typescript theme={null} // バイヤー側: クリエイティブエージェントに権利を渡す const creative = await creativeAgent.callTool({ name: "build_creative", arguments: { brand: { domain: "bistro-oranje.nl" }, format_id: { agent_url: "https://ads.example.com", id: "video_social_1080x1920" }, brief: "15-second vertical video featuring Daan Janssen endorsing Bistro Oranje", generation_credentials: acquireResponse.generation_credentials, rights: [acquireResponse.rights_constraint], }, }); ``` クリエイティブエージェントは `generation_credentials` を使って AI プロバイダー(Midjourney、ElevenLabs など)に認証してアセットを制作します。`rights` 配列はクリエイティブマニフェストのメタデータの一部となる — ダウンストリームシステム(広告サーバー、検証ベンダー)はそれを調べてクリエイティブが適切にライセンスされていることを確認できます。 完全なクリエイティブマニフェストの仕様は [クリエイティブマニフェスト](/docs/creative/creative-manifests) を参照。 ## テスト `validate_brand_agent` MCP ツールを使ってエージェントが到達可能で正しく応答しているかを確認します。開発中の自動テストには MCP SDK のインメモリトランスポートを使用します: ```typescript theme={null} import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; import { Client } from "@modelcontextprotocol/sdk/client/index.js"; const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); await Promise.all([server.connect(serverTransport), client.connect(clientTransport)]); const result = await client.callTool({ name: "get_brand_identity", arguments: { brand_id: "emma_torres" }, }); ``` 確認すべき主要事項: パブリックの呼び出し元にはコアフィールドが返されること、認可済みの呼び出し元にはより深いデータが返されること、`available_fields` が保留中のセクションをリストアップすること、無効な ID には `brand_not_found` エラーが返されること。 ## デプロイチェックリスト * [ ] `brand.json` が `/.well-known/brand.json` にホストされ、`brand_agent.url` が MCP エンドポイントを指しています * [ ] `get_adcp_capabilities` が `supported_protocols: ["brand"]` を返す * [ ] `get_brand_identity` がパブリックの呼び出し元にコアフィールドを返す * [ ] `get_brand_identity` が認可済みの呼び出し元に深いデータを返す * [ ] `available_fields` が保留中のセクションを正しくリストアップします * [ ] エラーレスポンスが `errors` 配列フォーマットを使用します * [ ] 権利を実装する場合: `get_rights` が価格オプションを返し、`acquire_rights` が条件を返す ## 関連 * [ブランドプロトコル概要](/docs/brand-protocol/index) — ブランド探索の仕組み * [brand.json 仕様](/docs/brand-protocol/brand-json) — ブランド宣言のファイル形式 * [get\_brand\_identity](/docs/brand-protocol/tasks/get_brand_identity) — アイデンティティタスクリファレンス * [get\_rights](/docs/brand-protocol/tasks/get_rights) — 権利探索タスクリファレンス * [acquire\_rights](/docs/brand-protocol/tasks/acquire_rights) — 権利取得タスクリファレンス * [アカウント概要](/docs/accounts/overview) — アカウントリンクの仕組み # 広告主向け Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/for-advertisers AdCP を通じて AI 生成広告向けにリアルなタレントをライセンスします。利用可能なアスリート、ミュージシャン、インフルエンサーを透明な価格で検索し、AI クリエイティブツール向けのスコープ付き生成資格情報を取得します。 AI 生成広告にリアルな人物の肖像をライセンスできます。ストック映像でも似た人でもない。実際のアスリート、ミュージシャン、インフルエンサー本人 — 本人の許可を得て、透明な価格で。 Ad Context Protocol (AdCP) は、AI メディアバイヤーがすでに使用している標準を通じて、バイヤーとタレント権利をつなぐ。必要なものを説明すると、プロトコルが利用可能な人物を見つけ、ライセンスされたタレントを起用した広告を生成できるスコープ付き資格情報を取得できます。 ## 広告主側の仕組み アムステルダムのステーキハウス「Bistro Oranje」を運営していて、次のキャンペーンに地元の有名人を起用したいとします。プロセスは次のようになる: 1. **必要なものを説明します。** あなた(またはあなたの AI メディアバイヤー)が「オランダのアスリートで、オランダ国内のフードブランドに利用可能な人物」を検索します。予算、希望する権利の種類(肖像、音声、エンドースメント)、キャンペーンの地域を含められます。 2. **システムが価格付きのマッチを返します。** 結果は関連性順で返ってくる: オランダのオリンピック・スピードスケート選手 Daan Janssen、オランダ国内のフードブランドに利用可能。2つの価格オプション — EUR 3.50 CPM、または最大10万インプレッション付き EUR 350/月の定額制。 3. **予算に合ったオプションを選択してリクエストを送信します。** リクエストには、作りたいもの、フォーマット、国、インプレッション数、期間を含めます。これは拘束力のある契約上のリクエストです。 4. **タレントの事務所がレビューして承認します。** 事務所はタレントの意向と既存の契約に照らしてリクエストを確認します。別のフードブランドがオランダで既に独占権を持っている場合、リクエストは自動的に拒否されます。承認されると、契約条件が届く。 拒否に代替案が含まれている場合 — 別のマーケット、別の日程、スコープの調整 — エージェントはリクエストを修正して再送信できます。代替案がない場合、その拒否はそのタレントとキャンペーンの組み合わせに対して最終的なものとなります。事務所は開示することが必ずしも適切でない機密のルール(法的制約、内部ポリシー、公人ガイドライン)を管理している — エージェントはその違いを理解し、調整するか次に進む。 5. **生成資格情報を受け取ります。** これらは特定の AI プロバイダー(画像生成ツール、音声合成ツール)がライセンスされたタレントを起用したコンテンツを制作できるようにするスコープ付きキーです。資格情報は契約終了時に期限切れとなります。 6. **すべてのインプレッションが追跡されます。** 使用状況は請求と上限管理のために権利保有者に報告されます。インプレッション上限に達すると、再交渉するまで生成が停止します。 ## 得られるもの 権利取得が承認されると、クリエイティブツールはキャンペーンを制作・配信するために必要なすべてのものを受け取る: * **ライセンスされたタレントを起用した AI 生成広告。** 動画、ディスプレイ、音声 — リクエストしたフォーマットすべて。タレントの肖像と音声は、コンテンツ制作前に資格情報を検証する AI プロバイダーが生成します。 * **スコープ付き生成資格情報。** 特定のプロバイダーで機能するキー(例: 肖像には Midjourney、音声には ElevenLabs)。任意のクリエイティブエージェントが使用できます。プロバイダーがスコープを適用する — ライセンス条件の範囲外では生成できません。 * **クリエイティブマニフェスト用の権利制約。** これはサプライチェーンを通じて広告に付随し、コンテンツがライセンスされていることを証明し、その境界を説明します。 * **開示テキスト。** あなたのために提供され、クリエイティブに添付できる状態になっている: 「Loti Entertainment からのライセンスのもと、Daan Janssen の AI 生成肖像を使用しています。」 ## 費用 価格はオークションではなく、タレントの事務所が設定します。コミットする前に価格がわかる。 一般的な2つのモデル: * **CPM** — インプレッション課金。EUR 3.50 CPM で 5万インプレッションは EUR 175 の権利料。 * **月額定額制** — インプレッション上限付きの固定月額料金。EUR 350/月・10万インプレッション上限の場合、3ヶ月のローカルキャンペーンの権利料は EUR 1,050、プラスクリエイティブ制作費とメディア費用。 独占権はプレミアムオプションとして利用可能。オランダで Daan Janssen の肖像を使用できる唯一のレストランになりたい場合、事務所がそれを認めることができ、プロトコルは契約期間中の競合するリクエストを自動的に拒否します。 ## コントロールできること すべてのライセンスは4つの次元でスコープされており、署名前に境界がわかる: * **地理的スコープ。** オランダのライセンスはドイツをカバーしません。キャンペーンを拡大する場合は再交渉が必要。 * **フォーマットスコープ。** 動画のライセンスは音声権利を付与しません。各使用タイプ(肖像、音声、名前、エンドースメント)は個別にライセンスされます。 * **期間スコープ。** ライセンスには厳格な有効期限があります。契約終了時に生成資格情報が機能しなくなります。手動でのクリーンアップは不要。 * **コンテンツ制限。** タレントの事務所が許容されるものを定義する — カテゴリー、コンテキスト、変更の制限。これらの制限はライセンス条件の一部であり、事前に確認できます。 ## 始め方 1. **代理店と協力している場合**、AdCP 互換のバイイングプラットフォームを使用しているか確認します。多くのメディア代理店は既存のツールを通じて権利の探索とライセンスにアクセスできます。 2. **直接購入する場合**、[AgenticAdvertising.org メンバーディレクトリ](https://agenticadvertising.org/members) でプラットフォームパートナーを探す。メンバー組織はバイヤーとタレント権利をつなぐツールを構築しています。 3. **プラットフォームが検索、交渉、資格情報管理を処理します。** クリエイティブを承認し、予算を設定し、キャンペーンパラメーターを定義します。プラットフォームがそれをプロトコル呼び出しに変換し、権利取得を管理し、生成資格情報をクリエイティブツールに届ける。 ## 次のステップ 価格と空き状況付きでライセンス可能なタレントを検索します。 拘束力のあるリクエストを送信して生成資格情報を受け取ります。 brand.json と権利探索の連携方法。 # タレントと権利保有者向け Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/for-rights-holders AdCP を通じてタレントと権利保有者が AI 生成の肖像、音声、エンドースメント使用をコントロールする方法。スコープ付き資格情報、地理的制限、承認ワークフロー、インプレッション追跡、ロングテールライセンス収益。 AI はあなたの顔、声、エンドースメントを — 無断で — 生成できます。生成ツールはすでに広告向けの合成セレブコンテンツを作成しており、法的な枠組みはまだ追いついていません。ライセンス可能な権利を持つ公人にとって、問題は AI があなたの肖像を使うかどうかではありません。あなたがその方法をコントロールできるかどうかです。 Ad Context Protocol (AdCP) はそのコントロールを提供します。これは、あなたの事務所や管理チームが AI システムが広告であなたのアイデンティティを使用するルールを設定し — そのルールを生成の時点で適用できる — オープン標準です。 ## 平易な言葉での仕組み あなたがオランダのオリンピック・スピードスケート選手 Daan Janssen だとします。あなたのマネジメント事務所 Loti Entertainment があなたの商業的権利を代理しています。AdCP が導入されている場合の流れ: 1. **事務所があなたの空き状況を公開します。** Loti はプロトコルにあなたの権利を登録する: 何が利用可能か(肖像、音声、エンドースメント)、どこで(オランダ、ベルギー、ドイツ)、いくらで。また、NGなもの — 商品カテゴリー、競合他社、コンテンツの種類 — もリストアップします。 2. **ブランドがあなたを検索します。** アムステルダムのレストランチェーンが AI メディアバイヤーにキャンペーン向けのオランダ人アスリートを探すよう依頼します。エージェントは AdCP を通じてあなたのプロフィールを発見し、オランダ国内のフードブランドに利用可能で予算に合うことを確認します。 3. **ブランドがあなたの権利をリクエストします。** エージェントが正式なリクエストを送信します: 作りたいもの、フォーマット、国、インプレッション数、期間。これは気軽な問い合わせではなく、拘束力のある契約上のリクエストです。 4. **事務所がレビューして承認します。** Loti はあなたの意向と既存の契約に照らしてリクエストを確認します。別のフードブランドがオランダで既に独占権を持っている場合、リクエストは自動的に拒否されます。承認されると、Loti が条件を設定します。 5. **期間限定の資格情報が発行されます。** ブランドは生成資格情報を受け取る — 特定の AI プロバイダー(画像生成ツール、音声合成ツール)があなたの肖像を使ったコンテンツを制作できるようにするキー。これらの資格情報は契約終了時に期限切れとなります。その日以降、いかなるプロバイダーもそのバイヤー向けにあなたの肖像を生成しません。 6. **すべての使用が追跡・報告されます。** インプレッションは請求と上限管理のために事務所に報告されます。契約で10万インプレッションが許可されていてブランドがその上限に達すると、再交渉するまで生成が停止します。 ## コントロールできること AdCP はいくつかのメカニズムを事務所の手に委ねる: **承認要件。** すべてのクリエイティブを配信前にあなたの承認を必要とすることができます。プロトコルは `pending_approval` ステータスをサポートする — あなたまたはあなたの代理人が承認するまで何も公開されない。 **コンテンツ制限。** 事務所が許容されるもの・されないものを定義します。カテゴリー、コンテキスト、隣接するコンテンツ、変更の制限。これらの制限はライセンスと一緒に伝達されます。 **地理的制限。** 権利は特定の国にスコープできます。オランダのライセンスは米国での権利を付与しません。 **期間限定の資格情報。** 生成資格情報には厳格な有効期限があります。ライセンス期間が終了すると、AI プロバイダーは生成を停止します。これはポリシーではなく、プロバイダーが適用する技術的制約です。 **価格の透明性。** 事務所が価格を設定します: インプレッションごとのロイヤリティ、月額定額、またはその両方。バイヤーはコミットする前に価格を見ます。裏取引や不透明なレートカードはない。 **開示要件。** すべてのライセンスでブランドにあなたが起用された AI 生成コンテンツの使用を開示することを要求できます。開示テキストは契約条件の一部です。 **独占権の適用。** ブランドがオランダのレストラン広告でのあなたの肖像の独占権を持っている場合、プロトコルはそのカテゴリーと地域での競合するリクエストを自動的に拒否します。 ## 事務所が行うこと あなたの事務所やマネジメント会社は、プロトコルにおいてあなたの**権利エージェント**として機能します。彼らは技術を構築する必要はない。AdCP を実装するプラットフォームと連携し、あなたの意向を設定します: * どの権利が利用可能か(肖像、音声、名前、エンドースメント) * 地理的な空き状況 * 価格帯とモデル * カテゴリー除外(エンドースしない商品や業界) * 承認ワークフロー(一部のカテゴリーは自動、その他は手動レビュー) * 独占条件 権利エージェントは発見、交渉、資格情報発行、使用状況追跡を処理します。あなたの関与は、意向の設定と承認が必要なクリエイティブのレビューに限定されます。 ## ロングテールの機会 従来のエンドースメント契約には写真撮影、契約交渉、何週間もの往復が必要です。取引コストが高いため、大企業しかあなたとの仕事を負担できません。地元のレストラン、地域のジムチェーン、近所のカーディーラー — 彼らはあなたを起用したいが、プロセスを正当化するにはディールが小さすぎる。 AdCP は計算を変える。発見、交渉、資格情報発行が自動化されているため、小さなディールが実行可能になります。アムステルダムのステーキハウスがあなたの肖像を月額 EUR 350 でライセンスできます。ロッテルダムのフィットネススタジオがあなたの声を月額 EUR 200 でライセンスできます。個別には小さい。まとめると積み上がる。 地元の20社が月額 EUR 350 で年間 EUR 84,000 — 従来のチャンネルでは実現しなかったディールからの収益です。事務所が価格を設定し、プロトコルが残りを処理し、あなたは承認が必要なクリエイティブを承認します。 ## 承認体験の流れ ブランドがキャンペーンであなたの肖像を使用したい場合、あなたまたはあなたの代理人に通知が届く。配信方法は事務所が使用するプラットフォームによる — メール、ダッシュボードのアラート、アプリのプッシュ通知の場合があります。 通知には主要な詳細が含まれます: * どのブランドがリクエストしているか * 作りたいもの(動画広告、ディスプレイバナー、音声スポット) * フォーマットと寸法 * 配信場所(国、チャンネル) * ライセンス期間 そこから、あなたまたはあなたの代理人は3つのオプションを持ちます: リクエストを承認する、変更を要求する、または拒否します。曖昧さはなく、すぐに回答するプレッシャーもない — 決定が記録されるまでクリエイティブは生成できません。 承認すると、ブランドは合意した内容にスコープされた生成資格情報を受け取ります。拒否すると、資格情報は発行されず、ブランドはそのキャンペーンであなたの肖像を使ったコンテンツを生成できません。変更を要求すると、ブランドは修正して再送信できます。 プロトコルの用語では、「変更の要求」は代替案付きの拒否です。あなたのエージェントはリクエストを拒否するが、実行可能な代替案を含める — 「別のマーケットで利用可能」または「より短いライセンス期間を試してください」。バイヤーのエージェントは代替案を見て自動的に調整できます。代替案なしの拒否は「いいえ、断固として」を意味する — バイヤーはあなたの内部的な理由を知ることなく次に進む。 すべての決定は記録されます。事務所はリクエストされたもの、承認されたもの、拒否されたものの完全な記録を持ちます。 ## よくある懸念への回答 **「誰かが無断でわたしの肖像を生成するでしょう。」** AdCP 準拠のプロバイダーは、既知のアイデンティティを使ったコンテンツを生成する前に権利資格情報を確認します。有効な資格情報がなければ、生成はブロックされます。プロトコルはすべての不正使用を防ぐことはできない — 悪意のある人物はオープンソースモデルを悪用できる — しかし正当な広告エコシステムのための明確で実行可能な標準を作る。 **「AdCP を経由せずに誰かがわたしの肖像を使用した場合はどうなるか?」** 事務所は正当な AdCP 使用からの監査証跡を使って、承認された使用がどのようなものかを証明できます。不正使用が市場に現れた場合、その証跡が証拠となります。AdCP はインターネットを監視しない — しかし法務チームが必要とする書類の記録を作る。 **「ライセンスを与えたらコントロールを失う。」** ライセンスは使用、地域、期間、コンテンツタイプでスコープされます。オランダのレストランの動画広告であなたの肖像を使用するライセンスは、日本の自動車ブランドの音声広告であなたの声を使用する権利を付与しません。各次元は独立してコントロールされます。 **「自分の肖像がどのように使用されているかわからないでしょう。」** 使用状況報告はプロトコルに組み込まれています。あなたの権利に対するすべてのインプレッションが追跡され、事務所に報告されます。これにより請求、コンプライアンス、契約執行のための監査証跡が作られます。 **「価格の底辺競争になるでしょう。」** 価格を設定するのはあなたです。プロトコルは複数の価格モデルをサポートする — CPM ベースのロイヤリティ、定額制、超過料金付きインプレッション上限。バイヤーは透明な価格を見て、受け入れるか次に進む。オークションや価格圧縮メカニズムはない。 ## AdCP がまだ解決していないこと プロトコルは現在の制限について正直だ: * **契約中の取り消し**は取り消しウェブフックを通じてサポートされています。何か問題が起きた場合 — タレントの論争、契約違反、ブランドの競合 — 事務所はすぐに権利を取り消せる。バイヤーは権利を取得する際に取り消しウェブフックを提供し、事務所は理由と有効日付を含む通知を送信します。バイヤーはクリエイティブの配信を停止する責任を負う。ただし、プロトコルはまだプロバイダーレベルで取り消しを適用しない — 資格情報の無効化はプロバイダーの協力に依存しています。 * **オープンソースモデルの適用**はプロトコルの範囲外です。AdCP は資格情報システムに参加するプロバイダーと連携します。制御されていないモデルを使って誰かがあなたの肖像を生成することは防げない。 * **ディープフェイク検出**は別の問題です。AdCP は承認された使用を処理します。不正な合成コンテンツの検出と対応には異なるツールが必要です。 ## 次のステップ 権利保有者またはその代理人の場合: 1. **事務所が AdCP 互換の権利管理プラットフォームと連携しているか確認します。** 多くのタレント事務所やマネジメント会社は、プロトコルをサポートするプラットフォームをすでに使用しています。使用している場合、セットアップは簡単 — 事務所がすでに使用しているプラットフォームで意向と承認ルールを設定するだけです。 2. **使用していない場合は、[AgenticAdvertising.org メンバーディレクトリ](https://agenticadvertising.org/members) でプラットフォームパートナーを見つけるよう事務所に伝える。** メンバー組織はタレント権利と広告エコシステムをつなぐツールを構築しています。事務所がプラットフォームを選び、そのプラットフォームが技術統合を処理します。 3. **プラットフォームパートナーが技術的なセットアップを処理する — 意向と承認ルールを設定します。** 何が利用可能か、どこで、いくらで、何があなたの個人的な承認が必要かを決める。プラットフォームはそれらの意向を、AI メディアバイヤーが発見・交渉できるプロトコル準拠の権利リストに変換します。 プロトコルの動作をより深く理解するには、[権利探索](/docs/brand-protocol/tasks/get_rights) と [権利取得](/docs/brand-protocol/tasks/acquire_rights) のドキュメントを読む。 広告業界は AI 生成コンテンツを採用しつつあります。権利保有者にとっての問題は、その採用があなたの参加と報酬とともに起きるか、それともなしに起きるかです。AdCP はあなたが議論の場に参加できるよう設計されています。 # ブランドプロトコル Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/index brand.json は AI エージェントにブランドを正しく使用する方法を伝えるオープン標準。ロゴ、カラー、トーン、制約を1つの機械可読ファイルで公開し、AdCP エコシステムのすべてのエージェントがこれに従う。 AI エージェントは今この瞬間もあなたのブランドのコンテンツを生成しています。クリエイティブツールはロゴのバリアントを選び、カラーパレットを決め、あなたの声だと思うトーンでコピーを書いています。これらは見つけられる場所から引き出される — キャッシュされたウェブページ、スクレイピングされたスタイルガイド、古いプレスキット。 あなたには、彼らが何を見つけるかをコントロールする手段がない。してはいけないことを伝える方法もない。 ## 仕組み 広告エコシステムのすべての AI エージェントが読めるブランドルールのセットを1つ公開する — ロゴ、カラー、声のトーン、絶対にしてはいけないこと。この形式は `brand.json` と呼ばれ、あなたのドメインに置く。 クリエイティブエージェントがブリーフを受け取る: 「Bistro Oranje のランチプロモーション、2コースメニュー、EUR 18.50。」 分割パネル: 左側は AI エージェントがバラバラなブランド要素をスクレイピングし、誤った色とフード画像にテキストを重ねたブランド外れの広告を生成している様子; 右側は同じエージェントが brand.json を読んで正しいロゴ、パレット、レイアウトで整ったブランドに沿った広告を生成している様子 **brand.json がない場合**、エージェントはウェブサイトをスクレイピングし、古いロゴを見つけ、ウェブサイトからカラーを推測し、ヒーローのフードショットに見出しを重ねる。まあまあ — ブランドチームが見るまでは。 **brand.json がある場合**、エージェントはファイルを取得し、正しいワードマークを引き出し、正確なパレットを適用し、温かいトーンを読み取り、フード画像へのテキスト重ねの制約を確認します。見出しを画像の下に配置したフードフォワードのコンポジションを生成します。推測なし。修正なし。 ブランドチームはこのことをクリエイティブエージェントに何もブリーフしていません。ファイルが代わりにやってくれた。 世界中のすべてのブランドシステムはロゴを提供できます。しかし「フード画像にテキストを重ねるな」を AI エージェントが実際に従うような形で表現できるシステムはほとんどない。`brand.json` にはそれができる — 制約は PDF に埋もれたガイドラインではなく、機械可読なルールだから。 ## エージェントがあなたのブランドについて知っていることを確認します [AgenticAdvertising.org ブランドレジストリ](https://agenticadvertising.org/brands) で任意のドメインを入力すると、AI エージェントが今日そのブランドについて見つけるものを確認できます。ほとんどのブランドは何も返らない — つまりエージェントは推測しています。`brand.json` があるブランドはエージェントが見るものを正確に表示します。 ## 2層: パブリックと認可 すべてがパブリックファイルに属するわけではありません。`brand.json` は2つのアクセスレベルでこれを処理します。 | レベル | 含む内容 | 閲覧者 | | --------- | ----------------------- | -------------- | | **パブリック** | 名前、ロゴ、カラー、タグライン、基本的なトーン | すべての AI エージェント | | **認可** | 高解像度アセット、音声合成、詳細なガイドライン | 承認したパートナー | パブリック層には既にウェブサイトにあるものが含まれます。認可層は代理店やパートナーとのみ共有するアセットとガイドライン用。アカウントをリンクすることで誰がアクセスできるかを決める — 承認していないエージェントへのデータ漏洩はない。 ## 1つのファイル、多くの用途 ブランドは階層構造で存在する — 持株会社、そのブランド、サブブランド、フランチャイズ店舗。`brand.json` がこれを処理します: 任意のドメインから始めて、プロトコルが正規のブランドアイデンティティを見つける。バイサイドはセルサイドが既に持っている同じ構造化されたアイデンティティを取得します。 * **同じファイルを通じてリアルなタレントのライセンスを取得します。** `brand.json` で利用可能なセレブの肖像・声優と利用条件を宣言できます。バイヤーエージェントがタレントを見つけ、価格を交渉し、AI クリエイティブツールの承認を取得する — メール1通も不要で。[完全な権利ライセンスのストーリーを読む →](/docs/brand-protocol/walkthrough-rights-licensing) * **すべてのサブブランドを一貫して保つ。** フランチャイズドメインは2行で親ブランドを参照します。すべての拠点が完全なアイデンティティを継承する — ロゴ、カラー、トーン、制約 — 別途設定不要。 * **サプライチェーンがリアルタイムで検証できるようにします。** 広告を配信する前に、すべての参加者がタレントライセンスがまだ有効で、正しい地域をカバーしていることを確認できます。 ## シンプルに始める 最もシンプルで役立つ `brand.json` は名前とロゴだけ。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "house": { "domain": "novabrands.com", "name": "Nova Brands" }, "brands": [{ "id": "nova", "names": [{"en": "Nova Brands"}], "logos": [{"url": "https://novabrands.com/logo.svg"}] }] } ``` 準備ができたらカラーを追加します。後でトーンのガイドラインを追加します。AI が誤りを犯すのを見たら視覚的な制約を追加します。ファイルはニーズに合わせて成長します。 ## エコシステムサポート `brand.json` はブランドデータを読み取って行動するエージェントツールのエコシステムに供給されます。 * **AdCP に準拠するバイヤーエージェント**は、メディアバイがブランドガイドラインに沿っていることを確認するためにキャンペーン計画中に `brand.json` を参照します * **クリエイティブエージェント**はブランドに沿ったアセットを生成する際にカラー、ロゴ、制約、声のトーンを取得します * **AgenticAdvertising.org レジストリ**は公開された `brand.json` ファイルをインデックス化し、バイヤーおよびセラーエージェントがプログラマティックにブランドを発見できるようにします * **ブランドプロトコルを実装した MCP 互換の AI ツール**はすべて `brand.json` を読み取ることができる — 形式はオープンで単一プラットフォームにロックされていません このプロトコルを採用するツールが増えるほど、追加作業なしに `brand.json` が提供する価値が大きくなります。 ## さらに深く **ブランドチーム向け:** AI 生成キャンペーンのためにリアルなタレントをライセンスする方法。 AdCP がタレント権利を保護してマネタイズする方法。 タレント発見から取得、失効までのバイヤーのストーリーを追う。 **開発者向け:** ファイル形式の完全な技術仕様。 アイデンティティと権利を提供するブランドエージェントを実装します。 # ブランドプロトコル Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/key-concepts AdCP ブランドプロトコルの概念: ハウス、ブランド、ブランドエージェント、Keller アーキテクチャタイプ、brand.json の探索、解決フロー、ブランドアイデンティティがクリエイティブ生成とメディアバイにどう関わるか。 ブランドプロトコルは、ブランドが標準化された探索メカニズムを通じてアイデンティティを主張し、検証可能な信頼できる情報源を確立できるようにします。ブランドは既知の場所に `brand.json` ファイルをホストすることで、アイデンティティ、ブランド階層を宣言し、オプションで公式ブランドエージェントを指定できます。 ## 目的 ブランドプロトコルは広告のバイサイドアイデンティティに対応し、プロパティプロトコルがセルサイドに提供するのと同様の明確さをもたらす。 | セルサイド | バイサイド | 説明 | | ------- | ------------- | --------------------------- | | パブリッシャー | **ハウス** | 法人組織(Nike, Inc.、P\&G) | | プロパティ | **ブランド** | 広告アイデンティティ(Nike、Air Jordan) | | インベントリ | **デスティネーション** | ランディングページ、アプリ | この並行構造により、AdCP においてブランドがファーストクラスの市民となります。 ## 仕組み ブランドはドメイン上の `/.well-known/brand.json` に `brand.json` ファイルをホストします。ファイルは4つの形式のいずれかを取ることができます。 1. **ブランドエージェント**: ブランド情報を提供する MCP エージェントを指します 2. **ハウスポートフォリオ**: すべてのブランドとプロパティを含む完全なブランド階層 3. **ハウスリダイレクト**: ポートフォリオを含むハウスドメインを指します 4. **権威ある場所**: ホストされた brand.json URL を指します ```mermaid theme={null} sequenceDiagram participant Agent as バイヤーエージェント participant Domain as ブランドドメイン participant House as ハウスドメイン participant BrandAgent as ブランドエージェント (MCP) Agent->>Domain: GET /.well-known/brand.json alt ハウスリダイレクト Domain-->>Agent: { "house": "nikeinc.com" } Agent->>House: GET /.well-known/brand.json House-->>Agent: 完全なポートフォリオ(ハウス + ブランド) else ブランドエージェント Domain-->>Agent: { "brand_agent": { "url": "..." } } Agent->>BrandAgent: MCP: ブランドアイデンティティを取得 BrandAgent-->>Agent: ブランドアイデンティティデータ else ハウスポートフォリオ Domain-->>Agent: 完全なポートフォリオ(ハウス + ブランド) end ``` ## ブランドアーキテクチャ プロトコルは Keller のブランドアーキテクチャモデルをサポートします。 | タイプ | 説明 | 例 | | ------------- | -------------------- | ----------------------- | | `master` | ハウスの主要ブランド | Nike, Inc. の Nike | | `sub_brand` | 親ブランド名を引き継ぐ | Nike SB | | `endorsed` | 独立したアイデンティティ、親に支持される | Air Jordan "by Nike" | | `independent` | 独立して運営 | Nike, Inc. 傘下の Converse | ## 例: ハウスポートフォリオ 複数のブランドを持つハウスドメイン。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "house": { "domain": "nikeinc.com", "name": "Nike, Inc.", "architecture": "hybrid" }, "brands": [ { "id": "nike", "names": [{"en": "Nike"}, {"zh": "耐克"}], "keller_type": "master", "properties": [ {"type": "website", "identifier": "nike.com", "primary": true}, {"type": "mobile_app", "store": "apple", "identifier": "com.nike.omega"} ] }, { "id": "air_jordan", "names": [{"en": "Air Jordan"}, {"en": "Jordan"}], "keller_type": "endorsed", "parent_brand": "nike", "properties": [ {"type": "website", "identifier": "jordan.com"}, {"type": "website", "identifier": "jumpman23.com"} ] } ] } ``` ## 例: ブランドエージェント ブランド情報を提供する MCP エージェントを持つブランド。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "brand_agent": { "url": "https://agent.acme.com/mcp", "id": "acme_brand_agent" } } ``` エージェントはブランドの代わりにブランドアイデンティティデータ(ロゴ、カラー、トーン)を提供します。 ## 例: ハウスリダイレクト ハウスを指すブランドドメイン。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "house": "nikeinc.com" } ``` ## 解決フロー 任意のドメインが与えられると、プロトコルは正規ブランドに解決します。 ``` jumpman23.com -> /.well-known/brand.json を取得 -> { "house": "nikeinc.com" } -> nikeinc.com/.well-known/brand.json を取得 -> "jumpman23.com" に一致するプロパティを brands[] で検索 -> Air Jordan ブランドのプロパティで発見 -> 結果: { house: "nikeinc.com", brand_id: "air_jordan" } ``` ## ブランド解決ソース ブランドアイデンティティを解決する3つの方法があり、それぞれ同じデータ構造を返します。 | ソース | 仕組み | 使用タイミング | | ----------------------------------------------------- | ---------------------------------------------- | ---------------------------------- | | [`resolve_brand`](#mcp-tools) | `/.well-known/brand.json` を取得してブランドアイデンティティを抽出 | ブランドが `brand.json` ファイルを公開している場合 | | [ブランドエンリッチメント](/docs/registry/index#brand-resolution) | Brandfetch から取得してアイデンティティをレジストリに保存 | `brand.json` が利用できない場合、エンリッチメントが必要 | | [レジストリ検索](/docs/registry/index#brand-resolution) | レジストリからコミュニティまたはエンリッチされたアイデンティティを返す | ブランドが既に登録されている場合 | ソースに関わらず、結果はブランドアイデンティティであり、ブランド参照(`{ "domain": "...", "brand_id": "..." }`)経由で任意の AdCP タスクから参照できます。 ## ユースケース ### クリエイティブ生成 クリエイティブエージェントがブランドアセットを必要とする場合。 1. brand.json 経由でドメインを正規ブランドに解決 2. ブランドアイデンティティデータを取得(brand.json、エージェント、またはレジストリから) 3. ブランドに沿ったクリエイティブを生成 ### ブランド検証 ブランドの主張を検証する場合。 1. 主張されたドメインから brand.json を取得 2. 必要に応じてハウスへのリダイレクトを追う 3. ポートフォリオにブランドが存在することを確認 ### レポーティングロールアップ ブランドパフォーマンスを集計する場合。 1. すべてのブランドドメインを正規 ID に解決 2. 企業レベルのレポーティングのためにハウスでグループ化 3. オプションでサブブランドを含める/除外します ## リクエスト内のブランドコンテキスト AdCP タスクはブランドをドメインとオプションの brand\_id で識別する `brand` 参照を受け付けます。システムは実行時にこの参照を完全なブランドアイデンティティに解決します。 ```json theme={null} { "brand": { "domain": "acmecorp.com", "brand_id": "tide" } } ``` 単一ブランドのドメインでは `brand_id` はオプション。 ```json theme={null} { "brand": { "domain": "acmecorp.com" } } ``` ブランドアイデンティティデータは `brand.json` またはレジストリから解決される — インラインで渡されない。 ## キャッシュ ブランド情報は変更頻度が低い(ロゴ更新、ガイドライン改定)。推奨キャッシュ設定。 * **HTTP ヘッダー**: 標準的な `ETag`、`Last-Modified`、`Cache-Control` ヘッダーを使用 * **デフォルト TTL**: 検証済み brand.json ファイルは24時間 * **失敗した検索**: 再試行前に1時間キャッシュ * **last\_updated フィールド**: 鮮度チェック用の brand.json 内の情報タイムスタンプ エージェントは brand.json ファイルを取得する際に HTTP キャッシュヘッダーを尊重すべきです。 ## ブランドプロトコルのタスク ブランドプロトコルを実装するエージェントは `get_adcp_capabilities` で `supported_protocols: ["brand"]` を宣言します。実装する具体的なタスクが役割を定義します。 | エージェントのケイパビリティ | タスク | 例 | | -------------- | ------------------------------- | --------------------------- | | DAM | `get_brand_identity` | エンタープライズブランドポータル、アセット管理 | | 権利管理 | `get_rights` + `acquire_rights` | タレントライセンス、音楽シンク、ストックメディア | | 両方 | すべてのブランドタスク | アイデンティティと権利を管理するタレントエージェンシー | ### get\_brand\_identity 静的な brand.json より豊富で、より動的で、よりアクセス制御されたブランドアイデンティティデータを返します。コアアイデンティティ(ハウス、名前、説明、ロゴ)は常にパブリック。(`sync_accounts` 経由で)リンクされたアカウントはそのベースラインの上に深いデータを取得できる: 高解像度アセット、音声合成設定、トーンガイドライン、権利の可用性。 ### brand.json 経由の権利探索 ライセンス可能な権利を持つブランドは brand.json に `rights_agent` を宣言します。これにより MCP 呼び出しなしに権利がクロール可能でインデックス化可能になります。 ```json theme={null} { "id": "daan_janssen", "names": [{"en": "Daan Janssen"}], "description": "Dutch Olympic speed skater, 2x gold medalist", "rights_agent": { "url": "https://rights.lotientertainment.com/mcp", "id": "loti_entertainment", "available_uses": ["likeness", "voice", "endorsement"], "right_types": ["talent"], "countries": ["NL", "BE", "DE"] } } ``` `brand_agent` はアイデンティティデータ(ロゴ、トーン、アセット)を提供します。`rights_agent` はライセンス(探索、価格、取得)を提供します。同一エージェントでも異なるエージェントでも可。 ### get\_rights ブランドエージェントのロスター全体でライセンス可能な権利を検索します。価格付きのマッチを返します。探索は自然言語ファースト — タクソノミーなし、LLM がクエリから意図を解釈します。 ### acquire\_rights 権利をクリアするための拘束力のある契約リクエスト。バイヤーは `get_rights` から `pricing_option_id` を選択し、キャンペーン詳細を提供します。条件、LLM プロバイダー向けの生成資格情報、開示要件を返します。 ### 生成資格情報 権利管理エージェントは LLM プロバイダー(Midjourney、ElevenLabs など)と連携してスコープ付きの資格情報を発行します。権利エージェントがパーミッションを設定し、プロバイダーが生成時に適用します。任意のクリエイティブエージェントが資格情報を使用できます。 ### クリエイティブのライフサイクル クリエイティブマニフェストはオプションの `rights` 配列を持つ — 各エントリは異なる権利保有者からの権利制約。単一のクリエイティブはタレントの肖像 + 音楽ライセンスを組み合わせることができ、それぞれ異なる有効期間と国の制限を持ちます。v1 では権利制約は情報メタデータ。 使用状況は `report_usage` を通じて権利エージェントに `rights_id` フィールドとともに報告され、上限追跡と請求に使用されます。 ## MCP ツール ブランドプロトコルはプログラマティックアクセス用の MCP ツールを提供します。 | ツール | 説明 | | ---------------------- | ---------------------- | | `resolve_brand` | ドメインを正規ブランドアイデンティティに解決 | | `validate_brand_json` | ドメインの brand.json を検証 | | `validate_brand_agent` | ブランドエージェントの到達可能性をテスト | ## 詳細を学ぶ brand.json ファイル形式の完全な技術仕様。 ブランドエージェントからブランドアイデンティティデータを取得します。 価格付きでライセンス可能な権利を検索します。 契約上のクリアランスで権利を取得します。 # セラーセットアップ Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/seller-setup パブリッシャー、セールスエージェント、ネットワーク、SSP がセラーアイデンティティ、署名鍵ディスカバリー、サプライパス検証のため brand.json と adagents.json をどう一緒に使うか。 `brand.json` はアドバタイザーだけのものではありません。売り手側では、AdCP セールスパスを運用する組織の公開企業レコードです: 名前、ロゴ、ドメイン、セールスエージェント、署名鍵ディスカバリー。`adagents.json` はパブリッシャーの認可レコードです: どのプロパティが存在しどのエージェントがそれらを販売できるか。 バイヤーエージェントは両方のビューを必要とします。`brand.json` は「このセラーまたはプラットフォームは誰で、何を主張しているか?」に答えます。`adagents.json` は「パブリッシャーはこのエージェントにこの在庫を販売する認可を与えているか?」に答えます。 ## 要件境界 実践的なルールは: * **AdCP エージェントを運用する場合**、そのエージェントはオペレーターアイデンティティと署名鍵ディスカバリーを必要とします。運用組織の `brand.json` エントリーを公開し、エージェントを `agents[]` にリストし、`agents[].jwks_uri` またはデフォルト `/.well-known/jwks.json` を通じて公開鍵を露出します。 * **在庫を公開する場合**、パブリッシャー認可レコードが必要です。バイヤーがどのエージェントがどのプロパティを販売できるか検証できるよう、パブリッシャードメインで `adagents.json` を公開します。 * **在庫を公開しかつセールスエージェントを運用する両方の場合**、同じ組織/ドメインで両方を行います。 * **販売を別のオペレーターに委譲する場合**、あなたの `adagents.json` が認可のヒンジです。パブリッシャーアイデンティティ、ポートフォリオコンテキスト、ガバナンスのため自身の `brand.json` は依然として推奨されますが、委譲されたオペレーターの `brand.json` がバイヤーがインタラクトするセールスエージェントアイデンティティを運びます。 プロトコル要件は検証可能性です: 公開鍵は発見可能でなければならず、署名付きリクエストまたは webhook はそれらの鍵に対して検証されなければなりません。本番エージェントは秘密署名鍵を KMS/HSM またはマネージドシークレットシステムで保護すべきですが、AdCP は特定のベンダーやホスティングパターンを義務付けません。このページはディスカバリーと検証レコードをカバーします。鍵ストレージと署名の実装詳細は [リクエスト署名](/docs/building/by-layer/L1/request-signing) に存在します。 ## 誰が何を公開するか | Organization | Publish `brand.json`? | Publish `adagents.json`? | Why | | ----------------- | --------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | 直接販売するパブリッシャー | Yes | Yes | パブリッシャーは在庫所有者と販売オペレーターの両方。バイヤーは同じ組織からアイデンティティ、販売エンドポイント、鍵、プロパティ認可を検証。 | | 販売を委譲するパブリッシャー | 推奨 | Yes | パブリッシャーの `adagents.json` が外部セールスエージェントを認可。その `brand.json` はバイヤーにパブリッシャー自身のアイデンティティとハウス/ポートフォリオコンテキストを与えるが、外部オペレーターの `brand.json` がセールスエージェントアイデンティティを運ぶ。 | | ネットワーク、SSP、セールスレプ | Yes | 通常 no、プロパティも所有する場合を除く | オペレーターの `brand.json` がそのセールスエージェント、署名鍵、代表されるプロパティを宣言。パブリッシャーが自身の `adagents.json` で関係を確認。 | パブリッシャーと販売オペレーターが同じ会社の場合、両ファイルは同じドメインに存在できます。サードパーティプラットフォームがパブリッシャーのため販売する場合、オペレーターの `brand.json` はオペレータードメインに、パブリッシャーの `adagents.json` はパブリッシャードメインに存在します。 ## 検証の仕組み 売り手側チェーンは双方向です: 1. セラーの `brand.json` が `agents[]` でセールスエージェントを宣言。 2. セラーの `brand.json` が `properties[]` で所有、直接販売、管理、または代表するプロパティを宣言。 3. パブリッシャーの `adagents.json` が `authorized_agents[]` で同じセールスエージェントを宣言。 4. 委譲またはネットワークパスには、`brand.json` の `relationship` 値がパブリッシャーの `adagents.json` の `delegation_type` に一致。ファーストパーティ在庫には、`relationship: "owned"` はインライン所有権で `delegation_type` カウンターパートを持たない。 5. エージェントが使う署名鍵はセラーの `brand.json` `agents[].jwks_uri` から発見可能。変更するセラー認可には、パブリッシャーも `adagents.json` `authorized_agents[].signing_keys[]` で許可された鍵をピン留め。 結果は検証可能なサプライパスです。オペレーターは公に「私はこのプロパティを販売する」と言う。パブリッシャーは公に「このオペレーターはそれを販売する認可を受けている」と言う。 ## 直接パブリッシャー例 自身のセールスエージェントを持つパブリッシャーは `https://streamhaus.example/.well-known/brand.json` で `brand.json` を公開します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "id": "streamhaus", "url": "https://streamhaus.example", "names": [{ "en_US": "StreamHaus" }], "industries": ["media"], "properties": [ { "type": "ctv_app", "identifier": "com.streamhaus.ctv", "relationship": "owned" } ], "agents": [ { "type": "sales", "id": "streamhaus_sales", "url": "https://ads.streamhaus.example/mcp", "jwks_uri": "https://ads.streamhaus.example/.well-known/jwks.json" } ] } ``` 同じパブリッシャーが `https://streamhaus.example/.well-known/adagents.json` で `adagents.json` を公開します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "contact": { "name": "StreamHaus", "email": "adops@streamhaus.example", "domain": "streamhaus.example" }, "properties": [ { "property_id": "streamhaus_ctv", "property_type": "ctv_app", "name": "StreamHaus CTV App", "publisher_domain": "streamhaus.example", "identifiers": [{ "type": "bundle_id", "value": "com.streamhaus.ctv" }] } ], "authorized_agents": [ { "authorization_type": "property_ids", "url": "https://ads.streamhaus.example/mcp", "authorized_for": "StreamHaus direct CTV inventory", "property_ids": ["streamhaus_ctv"], "signing_keys": [ { "kid": "streamhaus-sales-prod-2026", "kty": "OKP", "alg": "EdDSA", "crv": "Ed25519", "x": "w8zcY1LZqV4n1oKbfyq3n2q3sL2uV3z7kEw1m9Qjv4A", "use": "sig" } ] } ] } ``` バイヤーは、`brand.json` の `agents[].url` が `adagents.json` の `authorized_agents[].url` に一致すること、StreamHaus が主張するプロパティを所有すること、署名付き変更レスポンスが `signing_keys[]` で StreamHaus がピン留めした鍵を使うことを検証します。 ## 委譲セラー例 ネットワークが別のパブリッシャーの在庫を販売するとき、ネットワークは自身の `brand.json` を公開します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "id": "northwind_media", "url": "https://northwind.example", "names": [{ "en_US": "Northwind Media" }], "industries": ["advertising"], "properties": [ { "type": "website", "identifier": "streamhaus.example", "relationship": "delegated" } ], "agents": [ { "type": "sales", "id": "northwind_sales", "url": "https://northwind.example/mcp", "jwks_uri": "https://northwind.example/.well-known/jwks.json" } ] } ``` StreamHaus は `https://streamhaus.example/.well-known/adagents.json` で関係を確認します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "contact": { "name": "StreamHaus", "email": "adops@streamhaus.example", "domain": "streamhaus.example" }, "properties": [ { "property_id": "streamhaus_web", "property_type": "website", "name": "StreamHaus", "publisher_domain": "streamhaus.example", "identifiers": [{ "type": "domain", "value": "streamhaus.example" }] } ], "authorized_agents": [ { "authorization_type": "property_ids", "url": "https://northwind.example/mcp", "authorized_for": "StreamHaus inventory via delegated sales agreement", "property_ids": ["streamhaus_web"], "delegation_type": "delegated", "signing_keys": [ { "kid": "northwind-sales-prod-2026", "kty": "OKP", "alg": "EdDSA", "crv": "Ed25519", "x": "Xe2lAKRJR_zr3FQRdSNwp3zsrv_IXnVCWJXDcWXwkLI", "use": "sig" } ] } ] } ``` Northwind の `brand.json` だけでは認可ではありません。任意のオペレーターがプロパティを主張できます。パブリッシャーの一致する `adagents.json` エントリーが、主張を認可されたサプライパスに変えるものです。 ### マルチテナントセールスエージェント 多くのパブリッシャーテナントのため 1 つのセールスエージェントデプロイをホストするオペレーターは、すべてのエントリーが `type: "sales"` でも、テナントまたはプロパティスコープエンドポイントごとに 1 つの `agents[]` エントリーを公開してもよい(MAY)。各エントリーは `type` ではなく具体的な `url` で選択されるので、パスルーテッドデプロイはテナントごとの JWKS シャードを公開できます。テナントまたはプロパティスコープは、`/mcp/{tenant}` のような認証される具体的なエージェント URL によって運ばれます。署名付きリクエストボディ内のテナント識別子から選択されません。エージェント URL は `agents[]` 配列内で一意でなければなりません(MUST)。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "agents": [ { "type": "sales", "id": "sales_streamhaus", "url": "https://northwind.example/mcp/streamhaus", "jwks_uri": "https://northwind.example/.well-known/jwks/streamhaus.json" }, { "type": "sales", "id": "sales_pinnacle_news", "url": "https://northwind.example/mcp/pinnacle_news", "jwks_uri": "https://northwind.example/.well-known/jwks/pinnacle_news.json" } ] } ``` 署名検証者は、リクエスト署名ディスカバリーアルゴリズムを使って認証されるエージェント URL から鍵を解決しなければなりません(MUST): ちょうど 1 つの `brand.json` `agents[].url` エントリーに一致、そのエントリーの `jwks_uri` またはエージェント URL のオリジンのデフォルト `/.well-known/jwks.json` を使い、次に `keyid` を解決。重複する一致エントリーを曖昧として拒否しなければならず(MUST)、エージェント `type` だけで JWKS を選んではならず(MUST NOT)、どの鍵セットを信頼するか決めるためリクエストペイロード内で供給されたテナント識別子に依存してはなりません(MUST NOT)。 ## セットアップチェックリスト 1. AdCP セールスエージェントを運用する各組織の `brand.json` を `https://{seller-domain}/.well-known/brand.json` で公開。 2. 組織が運用する各 AdCP セールスエンドポイントのため `agents[]` に `sales` エントリーを追加。 3. エンドポイントの JWKS を `agents[].jwks_uri` を通じて公開、またはエージェントオリジンのデフォルト `/.well-known/jwks.json` に依存。 4. 所有、直接、委譲、またはネットワーク代表のすべてのプロパティを正しい `relationship` で `properties[]` に追加。ファーストパーティ在庫には `owned` を使う。 5. 在庫を所有する各パブリッシャードメインで `adagents.json` を公開。 6. `authorized_agents[]` に、セラーのエージェント URL、認可スコープ、委譲またはネットワークパスの一致する `delegation_type`、任意の変更セラー認可の `signing_keys[]` をリスト。 7. 販売関係、エンドポイント、署名鍵が開始、変更、終了するとき両ファイルを揃えたまま保つ。 ## 次に行く場所 * [brand.json リファレンス](/docs/brand-protocol/brand-json) — `agents[]`、`properties[]`、プロパティ関係のフィールドレベル詳細 * [adagents.json リファレンス](/docs/governance/property/adagents) — パブリッシャー側認可と `delegation_type` * [セラー検証](/docs/verification/overview) — 完全な検証チェーンのバイヤー側ウォークスルー * [セラー統合ガイド](/docs/building/operating/seller-integration) — 完全な AdCP セールスエージェント実装パス # acquire_rights Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/tasks/acquire_rights acquire_rights は拘束力のある権利取得のための AdCP タスク。価格オプションとキャンペーン詳細を送信し、ブランドエージェントから生成資格情報、権利制約、開示要件を受け取ります。 ブランドエージェントから権利を取得するための拘束力のある契約上のリクエスト。`create_media_buy` と並行する — `get_rights` から `pricing_option_id` を選択してキャンペーン詳細を提供します。エージェントは既存の契約に照らしてクリアし、条件、生成資格情報、開示要件を返します。 ## スキーマ * **リクエスト**: [`acquire-rights-request.json`](https://adcontextprotocol.org/schemas/latest/brand/acquire-rights-request.json) * **レスポンス**: [`acquire-rights-response.json`](https://adcontextprotocol.org/schemas/latest/brand/acquire-rights-response.json) ## 応答時間 `acquired` または `rejected` まで数秒から数分。`pending_approval` ステータスは権利保有者がレビューする必要があることを意味する — 解決には数時間から数日かかる場合があります。 ## クイックスタート ```json リクエスト theme={null} { "rights_id": "janssen_likeness_voice", "pricing_option_id": "monthly_exclusive", "buyer": { "domain": "bistro-oranje.nl", "brand_id": "bistro_oranje" }, "campaign": { "description": "AI-generated video ads for Bistro Oranje steakhouse featuring Daan Janssen", "uses": ["likeness", "voice"], "countries": ["NL"], "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_social_1080x1920" } ], "estimated_impressions": 50000, "start_date": "2026-04-01", "end_date": "2026-06-30" }, "revocation_webhook": { "url": "https://buyer.bistro-oranje.nl/webhooks/revocation", "authentication": { "schemes": ["HMAC-SHA256"], "credentials": "whsk_bo_abc123...shared_secret_min_32_chars" } }, "idempotency_key": "acq_bo_janssen_2026q2_001", "push_notification_config": { "url": "https://buyer.bistro-oranje.nl/webhooks/adcp/acquire_rights/op_abc123", "authentication": { "schemes": ["HMAC-SHA256"], "credentials": "whsk_bo_xyz789...shared_secret_min_32_chars" } } } ``` ```json レスポンス(acquired) theme={null} { "rights_id": "janssen_likeness_voice", "status": "acquired", "brand_id": "daan_janssen", "terms": { "pricing_option_id": "monthly_exclusive", "amount": 350, "currency": "EUR", "period": "monthly", "uses": ["likeness", "voice"], "impression_cap": 100000, "overage_cpm": 4.00, "start_date": "2026-04-01", "end_date": "2026-06-30", "exclusivity": { "scope": "Exclusive licensee for Daan Janssen in NL for food/restaurant brands", "countries": ["NL"] } }, "generation_credentials": [ { "provider": "midjourney", "rights_key": "rk_mj_abc123...", "uses": ["likeness"], "expires_at": "2026-06-30T23:59:59Z" }, { "provider": "elevenlabs", "rights_key": "rk_el_def456...", "uses": ["voice"], "expires_at": "2026-06-30T23:59:59Z" } ], "rights_constraint": { "rights_id": "janssen_likeness_voice", "rights_agent": { "url": "https://agent.lotientertainment.com/mcp", "id": "loti_entertainment" }, "valid_from": "2026-04-01T00:00:00Z", "valid_until": "2026-06-30T23:59:59Z", "uses": ["likeness", "voice"], "countries": ["NL"], "impression_cap": 100000, "approval_status": "approved", "verification_url": "https://agent.lotientertainment.com/rights/rts_abc123/verify" }, "restrictions": [ "All generated creatives must be submitted for approval before distribution", "No modification of talent likeness beyond approved AI generation parameters" ], "disclosure": { "required": true, "text": "Features AI-generated likeness of Daan Janssen, used under license from Loti Entertainment" }, "approval_webhook": { "url": "https://agent.lotientertainment.com/rights/rts_abc123/approve", "authentication": { "schemes": ["Bearer"], "credentials": "rk_approve_abc123...token_min_32_chars" } }, "usage_reporting_url": "https://agent.lotientertainment.com/rights/rts_abc123/usage" } ``` ```json レスポンス(pending approval) theme={null} { "rights_id": "janssen_likeness_voice", "status": "pending_approval", "brand_id": "daan_janssen", "detail": "Creative concept requires talent approval per contract terms", "estimated_response_time": "48h" } ``` ```json レスポンス(rejected — 実行可能) theme={null} { "rights_id": "janssen_likeness_voice", "status": "rejected", "brand_id": "daan_janssen", "reason": "Active exclusivity with another brand for food/restaurant in NL through 2026-09-30", "suggestions": [ "Available in BE and DE markets", "Available in NL after 2026-10-01" ] } ``` ```json レスポンス(rejected — 最終) theme={null} { "rights_id": "janssen_likeness_voice", "status": "rejected", "brand_id": "daan_janssen", "reason": "This violates our public figures brand guidelines" } ``` ```json レスポンス(エラー) theme={null} { "errors": [ { "code": "pricing_option_unavailable", "message": "Pricing option 'monthly_exclusive' is no longer available for this rights offering" } ] } ``` ## パラメーター ### リクエスト | フィールド | 型 | 必須 | 説明 | | -------------------------------- | ------------------------ | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `rights_id` | string | Yes | `get_rights` からの権利オファリング識別子 | | `pricing_option_id` | string | Yes | 選択した価格オプション | | `buyer` | brand-ref | Yes | バイヤーのブランドアイデンティティ | | `campaign.description` | string | Yes | 権利の使用方法 | | `campaign.uses` | string\[] | Yes | このキャンペーン向けの特定の権利使用 | | `campaign.countries` | string\[] | No | キャンペーンを実施する国 | | `campaign.format_ids` | format-id\[] | No | 制作するクリエイティブフォーマット | | `campaign.estimated_impressions` | integer | No | 推定総インプレッション数 | | `campaign.start_date` | date | No | キャンペーン開始日 | | `campaign.end_date` | date | No | キャンペーン終了日 | | `revocation_webhook` | push-notification-config | Yes | 取り消し通知用ウェブフック。権利保有者が権利を取り消す必要がある場合、この URL に [revocation-notification](https://adcontextprotocol.org/schemas/latest/brand/revocation-notification.json) を POST します。 | | `idempotency_key` | string | No | 安全な再試行のためのクライアント生成キー。同じキーで再送信すると元のレスポンスが返されます。 | | `push_notification_config` | push-notification-config | No | 取得に承認が必要な場合の非同期ステータス更新用ウェブフック。[プッシュ通知](/docs/building/by-layer/L3/webhooks) を参照。 | ### レスポンスステータス レスポンスは `status` の判別共用体を使用します: | ステータス | 説明 | 主要フィールド | | ------------------ | ------------------- | ----------------------------------------------------------------- | | `acquired` | 権利がクリアされ、資格情報が発行された | `terms`、`generation_credentials`、`rights_constraint`、`disclosure` | | `pending_approval` | 権利保有者のレビューが必要 | `detail`、`estimated_response_time` | | `rejected` | リクエストが拒否された | `reason`、`suggestions`(オプション) | 拒否されたレスポンスに `suggestions` が存在する場合、拒否は実行可能 — バイヤーはリクエストを調整して再試行できます。`suggestions` がない場合、拒否は最終的でバイヤーはこの権利/タレントの組み合わせで再試行すべきではありません。この慣例は `acquire_rights` の拒否、`get_rights` の除外結果、クリエイティブ承認の拒否全体で一貫して適用されます。 ## Request validation `acquire_rights` について 2 つのキャンペーンフィールド検証が規範的です。どちらも、問題の `field` を投入した `INVALID_REQUEST` と `recovery: "correctable"`(バイヤーはリクエストを修正して再試行できる)を生成します。 ### Expired campaign window ブランドエージェントは、リクエスト時点で `campaign.end_date` が過去にある場合、`INVALID_REQUEST` と `field: "campaign.end_date"` で拒否しなければなりません(MUST)。すでに経過したウィンドウの権利を取得すると、期間ゼロの付与が生成され、これはほぼ常にバイヤー側のバグです — それを決定的に表面化させることは、直ちに期限切れになる資格情報を黙って発行するよりも有用です。 [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) と同様に、権利の付与は経過したウィンドウについて時間シフト可能ではありません: コントラクトは要求された期間に付随するため、拒否のみが `acquire_rights` の正しいコントラクトです。 ブランドエージェントは、`campaign.start_date` が権利エージェントの設定された猶予ウィンドウより過去にある場合にも拒否してもかまいません(MAY。権利は遡及的に付与できないため、通常、権利エージェントは `now - 24h` より前の開始日を拒否します)。その決定はコントラクト固有で、`field: "campaign.start_date"` を使うべきです(SHOULD)。`end_date < now` チェックが規範的な下限です。 ### CPM-priced rights under a governance plan リクエストは、ブランドエージェントが資格情報を発行する前にガバナンスプランに対してコミットメントを投影する場合に**ガバナンス対応**です。それは 2 つのパスのいずれかで発生します。 1. **インラインパス** — リクエストが [プロトコルエンベロープ](/docs/building/by-layer/L1/security)に意図フェーズの `governance_context` トークンを運ぶ。バイヤーはリクエストごとに明示的にトークンをスレッドする。 2. **バインドパス** — リクエストが `account`(セラーが割り当てた `account_id`、またはバインドされたアカウントに解決する `account.brand` + `account.operator`)を運び、ブランドエージェントが [`sync_governance`](/docs/accounts/tasks/sync_governance) を通じてそのアカウントに以前バインドされたガバナンスエージェントを持つ。ブランドエージェントは、バイヤーがリクエストごとに何もスレッドせずに、バインドされたエージェントをルックアップする。 同じリクエストに**両方**のパスが存在する場合 — インライン `governance_context` トークンとバインドされたガバナンスエージェントを持つ `account` — インライントークンが勝ちます。トークンはリクエストごとで、特定のプランに対して JWS 署名され、監査とレポートの[主要な相関キー](/docs/building/by-layer/L1/security)です。バインドされたエージェントは、トークンがスレッドされていない場合のリゾルバーフォールバックとして機能します。ブランドエージェントは、両方が存在する場合、バインドされたエージェントと異なっていても、インライントークンで識別されるエージェントを参照しなければなりません(MUST)— バイヤーのリクエストごとの決定が永続化されたバインディングをオーバーライドします。 両方のパスは同じ投影ルールをトリガーします。リクエストがガバナンス対応で選択された料金オプションが `model: "cpm"` を持つ場合、`campaign.estimated_impressions` がブランドエージェントが残りのプラン予算に対してコミットメントを投影するために使う入力です。その投影を実装間で決定的にするには: * ブランドエージェントは、リクエストがガバナンス対応(いずれかのパス)で、選択された `pricing_option.model` が `"cpm"` で、`campaign.estimated_impressions` が省略または `0` の場合、`INVALID_REQUEST` と `field: "campaign.estimated_impressions"` で拒否しなければなりません(MUST)。実装者が選んだデフォルト(例: 100 万インプレッションを想定)は非準拠です — それらは各実装の内部にポリシー決定を隠し、同一のリクエストに対して異なるガバナンス結果を生成します。 * `estimated_impressions` が提供され非ゼロの場合、投影されたコミットメントは `(pricing_option.price / 1000) × campaign.estimated_impressions` で、`pricing_option.currency` で評価されます。`pricing_option.currency` がガバナンスプランの予算通貨(プランで運ばれる)と異なる場合、ブランドエージェントは `INVALID_REQUEST` と `field: "pricing_option_id"` で拒否しなければなりません(MUST)— ガバナンス投影に通貨変換は指定されていないため、通貨不一致のオファーは被管理プランに対してクリアできません。 * 投影されたコミットメントがバイヤーの残りのプラン予算を超える場合、エージェントは `INVALID_REQUEST` と `field: "campaign.estimated_impressions"` で拒否しなければならず(MUST)、バイヤーが調整できるよう `reason` に投影されたコミットメントと残り予算を投入します。 * 非 CPM の料金オプション(`model: "flat_rate"` など)は、インプレッションボリュームに関係なくフラット額をコミットします。ブランドエージェントは、それらのオプションについてガバナンス投影のために `estimated_impressions` を要求してはなりません(MUST NOT)。バイヤーは、上限追跡の目的で依然として `estimated_impressions` を提供してもかまいません(MAY)。 ガバナンスされていないリクエスト — `governance_context` トークンもバインドされたガバナンスエージェントを持つアカウントもない — はこの検証の影響を受けません。その場合、`estimated_impressions` は任意のままです(セラーは、[支出コミットの呼び出し](/docs/governance/campaign/specification#spend-commit-invocation)に従い、商業ポリシーの問題としてガバナンスされていないプランでの取引を拒否してもかまいませんが(MAY)、その拒否はこの投影ルールとは独立です)。 ## 生成資格情報 権利が取得されると、エージェントは LLM プロバイダーと連携してスコープ付き資格情報を発行する: 1. エージェントが既存の契約に照らして権利をクリアします 2. エージェントがプロバイダー(例: Midjourney)に伝える: 「このタレントの権利キーを発行し、このバイヤーにライセンスする」 3. エージェントがバイヤーに資格情報を返す **任意のクリエイティブエージェント**がこれらの資格情報を使用できます。LLM プロバイダーは生成時に使用制約を適用する — 権利エージェントがパーミッションを設定し、プロバイダーがゲートキーパーとなります。 | フィールド | 型 | 説明 | | ------------ | --------- | --------------------------------------------- | | `provider` | string | LLM/生成サービス(例: "midjourney"、"elevenlabs") | | `rights_key` | string | 権利がクリアされたコンテンツを生成するためのスコープ付き API キー | | `uses` | string\[] | この資格情報がカバーする権利の使用 | | `expires_at` | datetime | 資格情報の有効期限(プロバイダーが決定) | | `endpoint` | uri | 権利スコープ付き生成のプロバイダーエンドポイント(省略時はプロバイダーのデフォルトを使用) | ## 権利制約 `status` が `acquired` の場合、レスポンスには `rights_constraint` オブジェクトが含まれます: | フィールド | 型 | 説明 | | ------------------- | ------ | -------------------------------------------------------------------------------------- | | `rights_constraint` | object | クリエイティブマニフェスト用の事前構築された制約。合意された条件の有効期間、国の制限、インプレッション上限を含みます。マニフェストの `rights` 配列に直接埋め込む。 | ## クリエイティブのライフサイクル 権利取得後: 1. **生成**: クリエイティブエージェントが `generation_credentials` を使ってコンテンツを制作 2. **マニフェスト**: `acquire_rights` レスポンスの `rights_constraint` をクリエイティブマニフェストの `rights` 配列に直接埋め込む。権利エージェントは合意された条件の正しい有効期間、国の制限、インプレッション上限を含むこの制約を事前に構築します。 3. **承認**: 提供された資格情報で認証しながら、`approval_webhook` URL に [`creative-approval-request`](https://adcontextprotocol.org/schemas/latest/brand/creative-approval-request.json) を POST します。レスポンスはステータス `approved`、`rejected`、または `pending_review` を持つ [`creative-approval-response`](https://adcontextprotocol.org/schemas/latest/brand/creative-approval-response.json) だ。`pending_review` の場合は、返された `status_url` を定期的にポーリングする(推奨: 5分ごと、1時間後は30分ごとにバックオフ)。 4. **配信**: 国と日程の制限を守りながら `create_media_buy` で承認されたクリエイティブを配信 5. **報告**: 上限追跡と請求のために `rights_id` を含む [`report_usage`](/docs/accounts/tasks/report_usage) を使用 `acquire_rights` が `pending_approval` を返し、`push_notification_config` を提供した場合、ステータスが `acquired` または `rejected` に変わるとウェブフック通知を受け取ります。それ以外の場合は、`estimated_response_time` の間隔後に同じ `rights_id` と `idempotency_key` で `acquire_rights` を再度呼び出す。この期間中に構築したクリエイティブマニフェストには `approval_status: 'pending'` を設定します。 ## 取り消し 権利保有者が権利を取り消す必要がある場合(タレントの論争、契約違反など)、取得時に提供された資格情報で認証しながら、バイヤーの `revocation_webhook` に [`revocation-notification`](https://adcontextprotocol.org/schemas/latest/brand/revocation-notification.json) を POST します。通知には `notification_id`(重複排除用)、`rights_id`、`brand_id`、`reason`、`effective_at` タイムスタンプが含まれます。 バイヤーの責任: * `notification_id` による重複排除 — 同じ取り消しが複数回届く場合があります * `effective_at` までにクリエイティブの配信を停止します * アクティブなキャンペーンから影響を受けたクリエイティブを削除または置き換える * 生成資格情報の使用を停止する(プロバイダーも独立して資格情報を無効化する場合があります) 部分的な取り消しをサポート — `revoked_uses` が存在する場合、それらの使用のみ取り消されます(例: 音声は取り消されるが肖像は残る)。 ### 取り消しの確認 取り消し通知を受け取って検証したらすぐに HTTP `200` を返します。権利保有者は非 `2xx` レスポンスに対して指数バックオフ(1秒、5秒、30秒、5分、30分)で再試行します。6回の失敗後、権利保有者は他のチャンネルでエスカレーションする場合があります。 すべてのウェブフック認証は AdCP の [プッシュ通知署名慣例](/docs/building/by-layer/L3/webhooks#hmac-sha256-recommended-for-production) を使用する — HMAC-SHA256 による `X-ADCP-Signature` と `X-ADCP-Timestamp` ヘッダー。 ## インプレッション上限と超過 `terms.impression_cap` が設定されている場合、それは**ソフト上限**だ。上限で配信が自動的に停止されることはない — バイヤーは `report_usage` を通じて使用状況を監視し、それに応じて配信を管理する責任があります。上限を超えたインプレッションは `terms.overage_cpm` で請求されます。権利保有者がハード上限(制限を超えた配信なし)を望む場合、`restrictions` でそれを指定します。 ## 使用状況報告 取得されたレスポンスの `usage_reporting_url` は、権利エージェントが HTTP ベースのインプレッション報告のために提供する便利なエンドポイントです。[`report_usage`](/docs/accounts/tasks/report_usage) MCP タスクと同じペイロードを受け付けます。パイプラインにとってシンプルな方を使用する — エージェント間ワークフロー用の MCP ツール、または広告サーバーからの直接 HTTP 呼び出し用の URL。 ## 更新と更新 権利付与を延長、インプレッション上限の調整、価格の変更、一時停止/再開には [`update_rights`](/docs/brand-protocol/tasks/update_rights) を使用します。延長された付与には再発行された生成資格情報と、マニフェストに再埋め込みするための更新された `rights_constraint` が含まれます。 ## 次のステップ 既存の権利付与を延長、調整、または一時停止します。 請求と上限追跡のために権利付与に対するインプレッションを報告します。 クリエイティブマニフェストの権利制約。 # get_brand_identity Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/tasks/get_brand_identity get_brand_identity はブランドエージェントからブランドデータを取得する AdCP タスク。ロゴ、カラー、フォント、ビジュアルガイドライン、トーン、音声合成設定をパブリックと認可アクセスのティアで返します。 ブランドエージェントからブランドアイデンティティデータを取得します。コアアイデンティティ(ハウス、名前、説明、ロゴ)は常にパブリック — 認証なしで任意のエージェントがブランドが誰であるかを発見できます。リンクされたアカウントはより深いデータを取得できる: 高解像度アセット、音声合成設定、トーンガイドライン、権利の可用性。 ## スキーマ * **リクエスト**: [`get-brand-identity-request.json`](https://adcontextprotocol.org/schemas/latest/brand/get-brand-identity-request.json) * **レスポンス**: [`get-brand-identity-response.json`](https://adcontextprotocol.org/schemas/latest/brand/get-brand-identity-response.json) ## 応答時間 アイデンティティデータは通常2秒以内。大きなアセットコレクションを含む認可レスポンスはより時間がかかる場合があります。 ## デフォルトでパブリック ブランドアイデンティティはパブリックデータです。任意のエージェントがリンクされたアカウントなしで `get_brand_identity` を呼び出し、ブランドのコアアイデンティティを受け取ることができる: ハウス、名前、説明、業界、基本的なロゴ。これは `brand.json` で利用可能なものと同じデータ — `get_brand_identity` はファイルを取得してパースするよりも構造化された呼び出しを好むエージェントのために MCP 経由で提供します。 レジストリはこれを適用する: `brand.json` からインデックス化されたすべてのブランドはパブリックに発見可能です。ブランドがどのハウスに属するか、何と呼ばれるか、何をするかを常に確認できます。 認可された呼び出し元([`sync_accounts`](/docs/accounts/overview) でリンクされた)はパブリックのベースラインの上にさらに深いデータを取得できます。 アカウントリンクは一度限りのセットアップ: バイヤーエージェントがブランドエージェントで [`sync_accounts`](/docs/accounts/tasks/sync_accounts) を呼び出してブランド参照を提供します。その後、バイヤーの `get_brand_identity` リクエストは認可済みと認識されます。 | レベル | 取得できるもの | | ------------------------------ | ------------------------------------------------- | | **パブリック**(リンクされたアカウントなし) | ハウス、名前、説明、業界、keller\_type、基本的なロゴ、タグライン | | **認可**(`sync_accounts` でリンク済み) | 上記すべて、プラス: 高解像度アセット、音声合成、トーンガイドライン、コンテンツ制限、権利の可用性 | リクエストに呼び出し元が持っていない認可が必要な `fields` が含まれている場合、それらのフィールドは黙って省略されます。レスポンスには `available_fields` が含まれ、存在するが返されなかったセクションを一覧表示する — これにより呼び出し元はアカウントをリンクすることで何が得られるかを知ることができます。 ## クイックスタート ```json リクエスト(パブリック) theme={null} { "brand_id": "daan_janssen" } ``` ```json レスポンス(パブリック) theme={null} { "brand_id": "daan_janssen", "house": { "domain": "lotientertainment.com", "name": "Loti Entertainment" }, "names": [{"en": "Daan Janssen"}], "description": "Dutch Olympic speed skater, 2x gold medalist", "industries": ["sports"], "keller_type": "independent", "logos": [ { "url": "https://cdn.lotientertainment.com/janssen/headshot.jpg", "variant": "primary" } ], "tagline": [{"en-US": "Speed is a choice"}, {"nl-NL": "Snelheid is een keuze"}], "available_fields": ["tone", "voice_synthesis", "assets", "rights"] } ``` ```json リクエスト(認可済み、特定フィールド) theme={null} { "brand_id": "daan_janssen", "fields": ["logos", "tone", "voice_synthesis"], "use_case": "endorsement" } ``` ```json レスポンス(認可済み) theme={null} { "brand_id": "daan_janssen", "house": { "domain": "lotientertainment.com", "name": "Loti Entertainment" }, "names": [{"en": "Daan Janssen"}], "logos": [ { "url": "https://cdn.lotientertainment.com/janssen/headshot.jpg", "variant": "primary" }, { "url": "https://assets.lotientertainment.com/janssen/hero_01_highres.jpg", "variant": "full-lockup", "width": 3000, "height": 2000 } ], "voice_synthesis": { "provider": "elevenlabs", "voice_id": "janssen_v2", "settings": { "stability": 0.7 } }, "tone": { "voice": "enthusiastic, warm, competitive", "attributes": ["athletic", "Dutch pride", "approachable"], "dos": ["Reference athletic achievements", "Use Dutch cultural touchpoints"], "donts": ["No injury references", "No competitor comparisons"] } } ``` ```json リクエスト(クリエイティブ制作) theme={null} { "brand_id": "daan_janssen", "fields": ["colors", "fonts", "visual_guidelines"], "use_case": "creative_production" } ``` ```json レスポンス(認可済み) theme={null} { "brand_id": "daan_janssen", "house": { "domain": "lotientertainment.com", "name": "Loti Entertainment" }, "names": [{"en": "Daan Janssen"}], "colors": { "primary": "#FF6600", "secondary": "#1A1A2E", "accent": "#FBA007" }, "fonts": { "primary": "Montserrat", "secondary": "Open Sans", "font_urls": ["https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700"] }, "visual_guidelines": { "photography": { "realism": "photorealistic", "lighting": "bright, natural", "framing": ["medium shot", "action shot"] }, "restrictions": [ "Never place text over the athlete", "No competitor brand logos in frame" ] } } ``` ```json レスポンス(エラー) theme={null} { "errors": [ { "code": "brand_not_found", "message": "No brand with id 'unknown_brand' in this agent's roster" } ] } ``` ## パラメーター ### リクエスト | フィールド | 型 | 必須 | 説明 | | ---------- | --------- | --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `brand_id` | string | Yes | エージェントの brand.json の brands 配列からのブランド識別子 | | `fields` | string\[] | No | 含めるオプションセクション(例: `logos`、`colors`、`fonts`、`visual_guidelines`、`tone`)。すべての認可セクションには省略します。コアフィールド(`brand_id`、`house`、`names`)は常に返され、リクエストする必要はない。 | | `use_case` | string | No | 意図するユースケース(例: "endorsement"、"voice\_synthesis"、"likeness")。エージェントは返されたセクション内のコンテンツを調整する — "likeness" ユースケースはアクション写真を返し、"voice\_synthesis" ユースケースは音声設定を返します。`fields` をオーバーライドしません。 | 有効な `fields` 値: `description`、`industries`、`keller_type`、`logos`、`colors`、`fonts`、`visual_guidelines`、`tone`、`tagline`、`voice_synthesis`、`assets`、`rights` 推奨 `use_case` 値: | 値 | エージェントの動作 | | --------------------- | ------------------------------------------------ | | `endorsement` | アクション写真、エンドースメントトーン、ブランドストーリーを優先 | | `voice_synthesis` | 音声合成設定、発音ガイドを返す | | `likeness` | 高解像度写真、外見ガイドライン | | `creative_production` | 完全なビジュアルアイデンティティ: カラー、フォント、visual\_guidelines、ロゴ | | `media_planning` | 基本的なアイデンティティと権利可用性のサマリー | `use_case` は参考情報 — 返されたセクション内のコンテンツを調整するが `fields` をオーバーライドしません。 ### レスポンス レスポンスはエージェントがコントロールする動的データで拡張された brand.json のブランド定義を反映する: | フィールド | 型 | 必須 | 説明 | | ------------------- | --------- | --- | -------------------------------------------------------------------------------------------------------------- | | `brand_id` | string | Yes | ブランド識別子 | | `house` | object | Yes | ハウス(法人組織): `domain` と `name` | | `names` | object\[] | Yes | ローカライズされた名前 | | `description` | string | No | ブランドの説明 | | `industries` | string\[] | No | 業界またはカテゴリー | | `keller_type` | string | No | ブランドアーキテクチャタイプ: `master`、`sub_brand`、`endorsed`、`independent` | | `logos` | object\[] | No | ブランドロゴ(brand.json のロゴ形状に準拠: `url`、`variant`、`orientation`、`background`、`tags`) | | `colors` | object | No | 構造化されたロール(`primary`、`secondary`、`accent`、`background`、`text`)を持つブランドカラーパレット | | `fonts` | object | No | ブランドタイポグラフィ(`primary`、`secondary`、`font_urls`) | | `visual_guidelines` | object | No | 写真、グラフィックスタイル、カラーウェイ、タイプスケール、モーションルール、制限 | | `tone` | object | No | ブランドの声とメッセージガイドライン。サブフィールド: `voice`(個性を表す形容詞)、`attributes`(プロンプトガイダンスの特性)、`dos`(承認されたアプローチ)、`donts`(禁止されたトピック) | | `tagline` | string | No | ブランドのタグラインまたはスローガン | | `voice_synthesis` | object | No | TTS 音声合成設定(`provider`、`voice_id`、`settings`) | | `assets` | object\[] | No | 利用可能なブランドアセット — brand.json アセット形状に準拠(`asset_id`、`asset_type`、`url`、`tags`) | | `rights` | object | No | 権利の可用性サマリー(価格には `get_rights` を使用) | | `available_fields` | string\[] | No | 認可レベルにより返されなかったが利用可能なセクション。アカウントをリンクすることで何がアンロックされるかを呼び出し元に伝える。 | ## ユースケース * **DAM**: 高解像度アセット、現在のキャンペーンガイドライン、季節ごとのクリエイティブツールキットを返す * **エンタープライズブランドエージェント**: 承認されたコピー、ブランドボイスガイドライン、現在のタグラインを返す * **権利管理エージェント**: タレントアイデンティティを返す — トーン、音声合成、写真、権利の可用性 ## 次のステップ 価格付きでライセンス可能な権利を検索します。 契約上のクリアランスで権利を取得します。 # get_rights Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/tasks/get_rights get_rights はライセンス可能なタレント、音楽、ストックメディアを発見するための AdCP タスク。自然言語で検索し、価格オプション付きのマッチを取得し、バイヤーブランドの互換性でフィルタリングします。 ブランドエージェントのロスター全体でライセンス可能な権利を検索します。価格オプション付きのマッチを返します。探索は自然言語ファースト — タクソノミーなし、LLM がクエリから意図を解釈します。 ## スキーマ * **リクエスト**: [`get-rights-request.json`](https://adcontextprotocol.org/schemas/latest/brand/get-rights-request.json) * **レスポンス**: [`get-rights-response.json`](https://adcontextprotocol.org/schemas/latest/brand/get-rights-response.json) ## 応答時間 通常2〜10秒。エージェントがバイヤーの brand.json に対して互換性チェックを行う場合、遅延が増加します。 ## クイックスタート ```json リクエスト theme={null} { "query": "Dutch athlete for restaurant brand in Amsterdam, budget 400 EUR/month", "uses": ["likeness", "voice"], "buyer_brand": { "domain": "bistro-oranje.nl" }, "include_excluded": true } ``` ```json レスポンス theme={null} { "rights": [ { "rights_id": "janssen_likeness_voice", "brand_id": "daan_janssen", "name": "Daan Janssen", "description": "Dutch Olympic speed skater, 2x gold medalist", "right_type": "talent", "match_score": 0.92, "match_reasons": [ "Available for food/restaurant brands in NL", "Within budget at 350 EUR/month", "Athletic brand aligns with Bistro Oranje's quality positioning" ], "available_uses": ["likeness", "voice", "name", "endorsement"], "countries": ["NL", "BE", "DE"], "pricing_options": [ { "pricing_option_id": "cpm_endorsement", "model": "cpm", "price": 3.50, "currency": "EUR", "uses": ["likeness"], "description": "Per-impression royalty for AI-generated creatives using likeness" }, { "pricing_option_id": "monthly_exclusive", "model": "flat_rate", "price": 350, "currency": "EUR", "period": "monthly", "uses": ["likeness", "voice"], "impression_cap": 100000, "overage_cpm": 4.00, "description": "Monthly exclusive license for likeness + voice, up to 100K impressions" } ], "content_restrictions": ["approval_required"], "preview_assets": [ { "url": "https://cdn.lotientertainment.com/janssen/headshot.jpg", "usage": "preview_only" } ] } ], "excluded": [ { "brand_id": "pieter_van_dijk", "name": "Pieter van Dijk", "reason": "Dietary lifestyle conflict with steakhouse brand", "suggestions": ["Available for plant-based and health food brands"] } ] } ``` ## パラメーター ### リクエスト | フィールド | 型 | 必須 | 説明 | | ------------------ | --------- | --- | -------------------------------------------------------- | | `query` | string | Yes | 希望する権利の自然言語での説明。予算、地域、ユースケースを含めます。 | | `uses` | string\[] | Yes | リクエストする権利の使用(`likeness`、`voice`、`name`、`endorsement` など) | | `buyer_brand` | brand-ref | No | バイヤーのブランド。エージェントが互換性フィルタリングのためにバイヤーの brand.json を取得します。 | | `countries` | string\[] | No | 権利が必要な国(ISO 3166-1 alpha-2) | | `brand_id` | string | No | 特定のブランドの権利内を検索 | | `right_type` | string | No | 権利タイプでフィルタリング(`talent`、`music`、`stock_media` など) | | `include_excluded` | boolean | No | フィルタリングされた結果を理由とともに `excluded` 配列に含めます。デフォルトは false。 | | `pagination` | object | No | 大きな結果セットのページネーションパラメーター | ### レスポンス | フィールド | 型 | 説明 | | ------------------------------- | --------- | ------------------------------------------------ | | `rights` | object\[] | 価格付きのマッチ、関連性順でランク付け | | `rights[].rights_id` | string | このオファリングの識別子 — `acquire_rights` で参照される | | `rights[].brand_id` | string | ブランド識別子 | | `rights[].name` | string | 表示名 | | `rights[].match_score` | number | 関連性スコア(0〜1) | | `rights[].match_reasons` | string\[] | この結果がマッチする理由 | | `rights[].available_uses` | string\[] | 利用可能な権利の使用 | | `rights[].countries` | string\[] | 利用可能な国 | | `rights[].excluded_countries` | string\[] | 可用性から除外された国 | | `rights[].exclusivity_status` | object | 現在の独占可用性(`available`、`existing_exclusives`) | | `rights[].pricing_options` | object\[] | 価格オプション(以下参照) | | `rights[].description` | string | 権利の対象の説明 | | `rights[].right_type` | string | 権利のタイプ(`talent`、`music`、`stock_media` など) | | `rights[].content_restrictions` | string\[] | コンテンツ制限または承認要件 | | `rights[].preview_assets` | object\[] | 評価用のプレビューのみのアセット | | `excluded` | object\[] | 理由付きのフィルタリング済み結果(`include_excluded: true` の場合のみ) | | `excluded[].suggestions` | string\[] | 除外が修正可能な場合の実行可能な代替案。除外が最終的な場合は省略。 | ### 権利価格オプション 価格オプションは権利に特有 — 期間、インプレッション上限、超過レート、使用タイプのスコーピングを含みます: | フィールド | 型 | 必須 | 説明 | | ------------------- | --------- | --- | ---------------------------------------- | | `pricing_option_id` | string | Yes | `acquire_rights` と `report_usage` で参照される | | `model` | string | Yes | 価格モデル(`cpm`、`flat_rate` など) | | `price` | number | Yes | 価格金額 | | `currency` | string | Yes | ISO 4217 通貨コード | | `uses` | string\[] | Yes | このオプションでカバーされる権利の使用 | | `period` | string | No | 請求期間(`monthly`、`quarterly` など) | | `impression_cap` | integer | No | 期間あたりの最大インプレッション数 | | `overage_cpm` | number | No | 上限を超えたインプレッションの CPM | ## 複合権利 複数の使用をリクエスト(例: `["likeness", "voice"]`)すると、エージェントはそれらを単一の価格オプションにバンドルします。1回の呼び出し、1つの価格。 ## バイヤーブランドフィルタリング `buyer_brand` が提供されると、エージェントはバイヤーの brand.json を取得して互換性フィルタリングに使用します。例えば、ベジタリアンのアスリートはステーキハウスのキャンペーンから除外されます。フィルタリングされた結果と理由を見るには `include_excluded: true` を設定します。 ## 音楽ライセンスと DDEX このセクションは音楽権利エージェントを構築する実装者向け。ブランドプロトコルを使用するバイヤーであれば、標準の `get_rights` と `acquire_rights` フローは音楽でも同じように機能する — DDEX を知る必要はない。 権利プロトコルはタレント権利と並行して音楽ライセンスをサポートします。音楽シンクプラットフォームは `right_type: "music"` で `get_rights` を実装し、シンク/バックグラウンド使用の価格オプションを返します。 AdCP の権利モデルは [DDEX](https://ddex.net/) の Party Information Exchange (PIE) パターンを参考にしている — 各 `get_rights` レスポンスは以前の状態に対するデルタではなく、現在の可用性のステートレスなスナップショットです。DDEX を熟知している実装者向けのマッピング: | AdCP コンセプト | DDEX の同等物 | 備考 | | ---------------------- | --------------------- | ------------------------------------------- | | `rights_id` | ISRC / ISWC | AdCP はエージェントスコープの ID を使用; 標準識別子は `ext` に含める | | `available_uses` | 使用タイプ(シンク、バックグラウンドなど) | AdCP は `right-use` enum 値を使用 | | `pricing_options` | ライセンスオファー | 同じコンセプト、異なる構造 | | `content_restrictions` | 地域/使用制限 | AdCP は DDEX より粒度が低い | | `acquire_rights` | ライセンス付与 | AI 音楽制作の生成資格情報を返す | 音楽権利エージェントは、既存の音楽ライセンスシステムとの相互運用性のために、権利レスポンスの `ext` フィールドに標準識別子(ISRC、ISWC)を含めるべきです。 ## 次のステップ 権利オファリングを選択した後: 1. 選択したブランドの完全なアイデンティティデータを取得するために [`get_brand_identity`](/docs/brand-protocol/tasks/get_brand_identity) を呼び出す 2. `rights_id` と `pricing_option_id` を使って [`acquire_rights`](/docs/brand-protocol/tasks/acquire_rights) を呼び出す # update_rights Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/tasks/update_rights update_rights はアクティブな権利付与を変更するための AdCP タスク。終了日の延長、インプレッション上限の調整、価格オプションの変更、一時停止と再開 — 再発行された生成資格情報付き。 既存の権利付与を変更する — 日程の延長、インプレッション上限の調整、価格の変更、一時停止/再開。`update_media_buy` と並行します。提供したフィールドのみが更新される; 省略されたフィールドは変更されない。 ## スキーマ * **リクエスト**: [`update-rights-request.json`](https://adcontextprotocol.org/schemas/latest/brand/update-rights-request.json) * **レスポンス**: [`update-rights-response.json`](https://adcontextprotocol.org/schemas/latest/brand/update-rights-response.json) ## 応答時間 ほとんどの更新は数秒。価格変更には権利保有者の承認が必要でより時間がかかる場合があります。 ## クイックスタート ```json リクエスト(終了日の延長) theme={null} { "rights_id": "janssen_likeness_voice", "end_date": "2026-09-30" } ``` ```json リクエスト(インプレッション上限の引き上げ) theme={null} { "rights_id": "janssen_likeness_voice", "impression_cap": 200000 } ``` ```json リクエスト(付与の一時停止) theme={null} { "rights_id": "janssen_likeness_voice", "paused": true } ``` ```json レスポンス(エラー) theme={null} { "errors": [ { "code": "invalid_update", "message": "New impression_cap (50000) must be >= impressions already delivered (78432)" } ] } ``` ```json レスポンス(成功) theme={null} { "rights_id": "janssen_likeness_voice", "terms": { "pricing_option_id": "monthly_exclusive", "amount": 350, "currency": "EUR", "period": "monthly", "uses": ["likeness", "voice"], "impression_cap": 200000, "overage_cpm": 4.00, "start_date": "2026-04-01", "end_date": "2026-09-30", "exclusivity": { "scope": "Exclusive licensee for Daan Janssen in NL for food/restaurant brands", "countries": ["NL"] } }, "generation_credentials": [ { "provider": "midjourney", "rights_key": "rk_mj_abc123_renewed...", "uses": ["likeness"], "expires_at": "2026-09-30T23:59:59Z" }, { "provider": "elevenlabs", "rights_key": "rk_el_def456_renewed...", "uses": ["voice"], "expires_at": "2026-09-30T23:59:59Z" } ], "rights_constraint": { "rights_id": "janssen_likeness_voice", "rights_agent": { "url": "https://agent.lotientertainment.com/mcp", "id": "loti_entertainment" }, "valid_from": "2026-04-01T00:00:00Z", "valid_until": "2026-09-30T23:59:59Z", "uses": ["likeness", "voice"], "countries": ["NL"], "impression_cap": 200000, "approval_status": "approved", "verification_url": "https://agent.lotientertainment.com/rights/rts_abc123/verify" }, "implementation_date": "2026-06-28T14:30:00Z" } ``` ## パラメーター ### リクエスト | フィールド | 型 | 必須 | 説明 | | ------------------- | ------- | --- | ------------------------------------------- | | `rights_id` | string | Yes | `acquire_rights` からの権利付与識別子 | | `end_date` | date | No | 新しい終了日(現在の終了日以降である必要があります) | | `impression_cap` | integer | No | 新しいインプレッション上限(すでに配信されたインプレッション以上である必要があります) | | `pricing_option_id` | string | No | 元の `get_rights` オファリングの別の価格オプションに切り替える | | `paused` | boolean | No | 付与を一時停止(`true`)または再開(`false`) | | `idempotency_key` | string | No | 安全な再試行のためのクライアント生成キー | ### レスポンス | フィールド | 型 | 説明 | | ------------------------ | -------------- | ------------------------------------------------- | | `rights_id` | string | 更新された権利付与識別子 | | `terms` | object | 更新された契約条件(`acquire_rights` の acquired レスポンスと同じ形状) | | `generation_credentials` | array | 更新された有効期限と上限で再発行された資格情報 | | `rights_constraint` | object | クリエイティブマニフェストへの再埋め込み用の更新された制約 | | `paused` | boolean | 付与が現在一時停止されているか(一時停止状態が変わる場合に含まれる) | | `implementation_date` | datetime\|null | 変更が有効になる時刻(承認待ちの場合は `null`) | ## 再発行された資格情報 日程を延長したり価格を変更したりすると、権利エージェントは更新された有効期限で生成資格情報を再発行します。古い資格情報と新しい資格情報は重複期間中に両方機能する場合がある — 古い資格情報は元の有効期限まで有効のままです。クリエイティブパイプライン内の資格情報を速やかに置き換える必要があるが、進行中の生成リクエストが途切れるような厳格な切り替え時点はない。 更新された `rights_constraint` は、ダウンストリームシステムが現在の条件を確認できるよう、アクティブなクリエイティブマニフェスト内の制約を置き換えるべきです。 ## 次のステップ 最初の権利取得フロー。 権利付与に対するインプレッションを報告します。 # verify_brand_claim Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/tasks/verify_brand_claim verify_brand_claim は、ブランドエージェントに自身のアイデンティティについての特定のクレームが owned、pending、disputed、licensed かを尋ねる統一 AdCP ブランドプロトコルタスク。1 ツール、4 つのクレームタイプ — subsidiary、parent、property、trademark — claim_type フィールドで判別。 ブランドエージェントに、そのアイデンティティの一側面についての検証質問を尋ねます。**これは前提条件ゲートです — 進む前にチェックする、決定がロックされた後に消費するシグナルではありません。** 4 つのクレームタイプを持つ 1 つのツールが検証次元をカバーします: | `claim_type` | The question | Used at | | ------------ | ------------------------------ | ----------------------------------- | | `subsidiary` | 「このブランドはあなたの子会社か?」 | ブランド関係確立、メンバー機能プロビジョニング、ガバナンス信頼拡張 | | `parent` | 「このブランドはあなたの親ハウスか?」(リーフ側ミラー) | エージェント層での相互主張確認 | | `property` | 「このサイト / アプリ / プロパティはあなたのものか?」 | 在庫オンボーディング、クリエイティブクリアランス、不正エスカレーション | | `trademark` | 「この商標はあなたのものか?」 | クリエイティブクリアランスゲート、ライセンシー姿勢確認 | ブランドエージェントは自身のデータを使って答えます — それは brand.json プラス、静的ファイルが表現できないよりリッチな状態(`pending_review`、`transferring`、`licensed_in` など)です。ツールは `get_brand_identity` が読むのと同じアイデンティティ表面の上の 1 つの特定質問アフォーダンスです。 高ボリューム検証(ポートフォリオリフレッシュ、クリエイティブクリアランスバッチ、クローラースキャン)には、バルクバリアント [`verify_brand_claims`](/docs/brand-protocol/tasks/verify_brand_claims) を使います — 同じクレームごとのセマンティクス、バッチ全体に 1 ラウンドトリップと 1 レート制限スロット。 ## 信頼ルール — 1 つではなく 2 つの呼び出し **単一の署名付き `owned` レスポンスは信頼拡張ではありません。相互主張が肯定的信頼のフロアのままです。** これは非対称信頼モデルの荷重を担うルールです — 主張方向は両側の同意を要求します。消費者は関係信頼を拡張するとき両側を呼ばなければなりません(MUST): * `subsidiary` クレームには、`claim_type: "parent"` でリーフのブランドエージェントも呼ぶ(またはリーフの `brand.json` を `house_domain` のためクロール)。 * `property` クレームには、ブランドの静的 `brand.json` `properties[]` と(ドメインには)DNS/TLS 証拠に対してクロスチェック。 * `trademark` クレームには、公開レジストリレコードをクロスチェック。特に `licensed_in` には、ライセンシング関係が信頼される前に、`details.licensor_domain` で名指しされたライセンサーが同じマークについて `licensed_out` を相互にすべき(SHOULD)。 * **拒否(`disputed` / `not_ours`)のみが単一の署名付きレスポンスで権威的** — ブランドは一方的に関連を拒否する立場を持つ。 ショートカットは信頼モデルを殺します。相互性ステップなしでは、悪意ある、または誤ったハウスが、実際には持たない子会社、プロパティ、ライセンスマークを主張しうる。完全な規範的信頼テーブルと悪意あるハウスのウォークスルーについては [`brand.json` § エージェント拡張検証](/docs/brand-protocol/brand-json#agent-augmented-verification) を参照してください。 ## スキーマ * **Request**: [`verify-brand-claim-request.json`](https://adcontextprotocol.org/schemas/v3/brand/verify-brand-claim-request.json) * **Response**: [`verify-brand-claim-response.json`](https://adcontextprotocol.org/schemas/v3/brand/verify-brand-claim-response.json) ## ケイパビリティディスカバリー ブランドエージェントは `get_adcp_capabilities` レスポンスでこのタスクをアドバタイズします。一部のクレームタイプのみをサポートするエージェントは、ツールごとのケイパビリティ拡張経由でこれを宣言します: ```json theme={null} { "supported_protocols": ["brand"], "supported_tasks": [ "get_brand_identity", "verify_brand_claim" ], "brand": { "verify_brand_claim": { "supported_claim_types": ["subsidiary", "parent", "trademark"] } } } ``` `supported_claim_types` が省略されるとき、エージェントは 4 つすべてのサポートをアドバタイズします。消費者は特定のクレームタイプに依存する前にチェックしなければなりません(MUST)。サポートされないタイプは `UNSUPPORTED_CLAIM_TYPE` を返します([Error handling](#error-handling) を参照)。 ### 最小実行可能採用 ブランドエージェントは 4 つすべてのクレームタイプを一度に出荷する必要はありません。ワークフローに一致するスライスを選びます: * **Property のみ** — クリエイティブクリアランスと在庫オンボーディング消費者。最小の有用な表面。 * **Subsidiary + parent** — ブランド関係確立またはガバナンス信頼拡張を行うパートナー。相互主張がエージェント層で完了するよう両半分を一度に出荷。 * **Trademark のみ** — ライセンシー姿勢を必要とするクリエイティブクリアランスパイプライン(レジストリクロールからの差別化要因)。 * **すべて 4 つ** — 完全カバレッジ。多くのメンバー構成をサーブする AAO ホストエージェントに推奨。 実装するタイプのみをアドバタイズします。パートナーは `supported_claim_types` をチェックしそれに応じてルーティングします。サポートされないタイプはクリーンに `UNSUPPORTED_CLAIM_TYPE` を返します。 ## 認可階層 クレームタイプごとの公開/認可分割: | Tier | What the agent returns | | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Public**(リンクされたアカウントなし) | `claim_type`、`verification_status`(常に)。該当するクレームタイプには: `details.brand_id`、`details.relationship`、`details.matched_registration`、`details.countries`、`details.nice_classes`。投入されたとき `context_note`。 | | **Authorized**([`sync_accounts`](/docs/accounts/tasks/sync_accounts) 経由) | 上のすべて、プラス: `details.first_observed_by_house_at`、`details.expected_resolution_window_days`、`details.use_case_authorization`、`details.licensor_domain`(licensed\_in のとき)。 | キュー位置、内部チケット状態、チームルーティングは決して露出されません。 ## クレームタイプごとのリクエストとレスポンス形状 レスポンスの `details` フィールドは `claim_type` によって変わります。以下: リクエストペイロードと各クレームタイプが返す型付き `details` フィールド。 ### `claim_type: "subsidiary"` ハウス側: 消費者が `converse.com` が `house_domain: nikeinc.com` を主張するのを検出。Nike のエージェントに尋ねる: ```json theme={null} { "claim_type": "subsidiary", "claim": { "subsidiary_domain": "converse.com", "subsidiary_brand_id": "converse", "observed_at": "2026-05-14T10:00:00Z" } } ``` ```json theme={null} { "claim_type": "subsidiary", "verification_status": "owned", "details": { "brand_id": "converse" } } ``` ブランドは拒否もできる — 拒否方向は単一の署名付きレスポンスで権威的、相互性不要: ```json theme={null} { "claim_type": "subsidiary", "verification_status": "not_ours", "context_note": "We have no record of this brand; the leaf's claim is in error." } ``` 該当ステータス: `owned`、`pending_review`、`transferring`、`disputed`、`not_ours`、`archived`、`unknown`。(`licensed_in` / `licensed_out` は適用されない — 子会社はライセンスされない。ブランドと商標がされる。)`archived` は、ブランドがかつてこの子会社を保持していた(例: 分離された事業単位)がもはやしないことを意味 — `not_ours`(決して所有しない)と区別。 **リクエスト `claim` フィールド:** | Field | Required | Notes | | --------------------- | -------- | --------------------------------------------------------------------------------- | | `subsidiary_domain` | Yes | `house_domain` クレームが検証されているリーフブランドのドメイン。 | | `subsidiary_brand_id` | No | リーフが自身に使う安定したブランド識別子。推奨。複数のブランドがドメインを共有するときエージェントが曖昧性解消するのを助ける。 | | `observed_at` | No | ISO 8601 タイムスタンプ — 呼び出し元がリーフのクレームを観測したとき。エージェントがクレームをエイジングし内部キューで新鮮なものを優先するのを助ける。 | **レスポンス `details` フィールド:** | Field | Tier | Returned when | Notes | | --------------------------------- | ---------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `brand_id` | Public | `verification_status` ∈ | この子会社のハウスの brand\_id。 | | `first_observed_by_house_at` | Authorized | any | ハウスが最初にクレームを認識したとき。 | | `expected_resolution_window_days` | Authorized | `verification_status` が `pending_review` のとき **必須**。そうでなければ存在するとき Authorized | エイジングウィンドウ。強制はエージェント側: エージェントはウィンドウが経過したらクレームを終端ステータスまたは `unknown` に遷移しなければならない(MUST)。`pending_review` レスポンスが宣言されたウィンドウより古いとき、消費者はそれを `unknown` として扱いクロールにフォールバックすべき(SHOULD)。 | ### `claim_type: "parent"` (leaf-side mirror) リーフ側: 消費者がその親についてのリーフの権威的な答えを望む。Converse のエージェントに尋ねる: ```json theme={null} { "claim_type": "parent", "claim": { "parent_domain": "nikeinc.com", "claimant_says": "Nike's brand_refs[] lists converse.com" } } ``` ```json theme={null} { "claim_type": "parent", "verification_status": "owned", "details": { "house_domain": "nikeinc.com" } } ``` リーフは能動的に拒否もできる: ```json theme={null} { "claim_type": "parent", "verification_status": "disputed", "context_note": "We are not a Nike subsidiary; their claim is in error." } ``` 該当ステータス: `subsidiary` と同じ(ミラー)。 **リクエスト `claim` フィールド:** | Field | Required | Notes | | --------------- | -------- | --------------------------------------------------------------------------------------------------------- | | `parent_domain` | Yes | このブランドの親として主張されているハウスのドメイン。 | | `claimant_says` | No | 主張者が公開したものについてのフリーテキストコンテキスト(例: "Nike's brand\_refs\[] lists converse.com")。競合するクレームをエージェントが曖昧性解消するのを助ける。 | | `observed_at` | No | ISO 8601 タイムスタンプ — 呼び出し元が親クレームを観測したとき。 | **レスポンス `details` フィールド:** | Field | Tier | Returned when | Notes | | --------------------------- | ---------- | ------------------------ | -------------------------------------------------------------- | | `house_domain` | Public | `verification_status` ∈ | ブランドの宣言された親ハウス。`pending_review` には返されない — リーフがまだ親クレームを受諾していない。 | | `first_observed_by_leaf_at` | Authorized | any | リーフが最初にその親子関係についての第三者クレームを認識したとき。 | `claim_type: "subsidiary"`(ハウス上)AND `claim_type: "parent"`(リーフ上)の両方の `verify_brand_claim` が同じ関係について `owned` を返すとき、**相互主張がエージェント層で確立** — 静的ファイルクロール不要。これは信頼拡張の最もクリーンなパスです。 ### `claim_type: "property"` リクエストは 1 つのプロパティについて尋ねます。レスポンスは、関係が適用されるすべてのリージョン(クエリで名指しされたものを超えるかも)を含む、そのプロパティとのブランドの関係を記述します。 ```json theme={null} { "claim_type": "property", "claim": { "property": { "type": "website", "identifier": "nike.cn", "region": "CN" }, "use_case": "advertising" } } ``` ```json theme={null} { "claim_type": "property", "verification_status": "owned", "details": { "relationship": "owned", "brand_id": "nike", "regions": ["CN"], "use_case_authorization": { "advertising": true } }, "context_note": "Regional site for China market" } ``` 複数のリージョンにまたがるプロパティ(例: グローバル e コマース表面)はそれらすべてを返す: ```json theme={null} { "claim_type": "property", "claim": { "property": { "type": "website", "identifier": "nike.com" } } } → { "claim_type": "property", "verification_status": "owned", "details": { "relationship": "owned", "brand_id": "nike", "regions": ["US", "CA", "GB", "FR", "DE", "JP", "AU"] } } ``` ブランドは拒否もできる — 拒否方向は単一の署名付きレスポンスで権威的: ```json theme={null} { "claim_type": "property", "claim": { "property": { "type": "website", "identifier": "fake-nike-store.com" } } } → { "claim_type": "property", "verification_status": "not_ours", "context_note": "Unaffiliated third-party site; we do not authorize use of our marks on it." } ``` 該当ステータス: `owned`、`transferring`、`disputed`、`not_ours`、`archived`、`unknown`。(`pending_review` はプロパティには珍しい。飛行中の所有権変更には `transferring` を使う。)`archived` は、ブランドがかつてこのプロパティを運用していた(例: 売却されたドメイン)がもはやしないことを意味。 **リクエスト `claim` フィールド:** | Field | Required | Notes | | --------------------- | ---------- | --------------------------------------------------------------------------------------------------- | | `property.type` | Yes | `website`、`mobile_app`、`ctv_app`、`desktop_app`、`dooh`、`podcast`、`radio`、`streaming_audio`。 | | `property.identifier` | Yes | ウェブサイト/ポッドキャストにはドメイン、アプリにはバンドル id など。 | | `property.store` | タイプがアプリのとき | `apple`、`google`、`amazon`、`roku`、`fire_tv`、`samsung`、`lg`、`vizio`、`other`。 | | `property.region` | No | 単一の ISO 3166-1 alpha-2 コード(または `"global"`) — 呼び出し元が気にするリージョン。レスポンスの `details.regions` が完全な該当セットを運ぶ。 | | `use_case` | No | フリーテキストユースケース(例: `"advertising"`)。エージェントはそれに応じて答えをスコープしてもよい(MAY)。 | **レスポンス `details` フィールド:** | Field | Tier | Returned when | Notes | | ------------------------ | ---------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `relationship` | Public | `verification_status` ∈ | `owned` / `direct` / `delegated` / `ad_network` — brand.json の `properties[].relationship` をミラー。 | | `brand_id` | Public | `verification_status` ∈ | このプロパティを所有するハウス内のブランド。 | | `regions` | Public | `verification_status` ∈ | ISO 3166-1 alpha-2 コード、またはリージョン制限なしの `["global"]` センチネル。リクエストが名指ししたものを超えるリージョンを含むかも。 | | `use_case_authorization` | Authorized | any | ユースケースごとの権限マップ。登録されたキー: `advertising`、`endorsement`、`retail_listing`、`editorial`、`commercial_advertising`、`merchandise_resale`。エージェントは拡張を追加してもよい(MAY)。 | ### `claim_type: "trademark"` ```json theme={null} { "claim_type": "trademark", "claim": { "mark": "AIR JORDAN", "registry": "USPTO", "number": "1234567" } } ``` ```json theme={null} { "claim_type": "trademark", "verification_status": "owned", "details": { "matched_registration": { "registry": "USPTO", "number": "1234567", "mark": "AIR JORDAN", "registration_status": "active" }, "countries": ["US"], "nice_classes": [25, 41] } } ``` ```json theme={null} { "claim_type": "trademark", "verification_status": "licensed_in", "details": { "matched_registration": { "registry": "EUIPO", "number": "EU98765", "mark": "CONVERSE", "registration_status": "active" }, "licensor_domain": "converseholdings-eu.com", "countries": ["FR", "DE", "IT", "ES"], "nice_classes": [25] } } ``` **`licensed_in` 相互性。** 消費者は、名指しされた `licensor_domain` のブランドエージェントが同じマークについて `licensed_out` を相互にするまで、`licensed_in` を **未検証** として扱うべきです(SHOULD)(所有権と同じ相互主張形状、ただしライセンシングエッジ全体で)。相互性なしでは、ブランドが存在しないライセンス関係を一方的に主張しうる。 ブランドは拒否もできる — 拒否方向は単一の署名付きレスポンスで権威的: ```json theme={null} { "claim_type": "trademark", "claim": { "mark": "AIR JORDAN", "registry": "EUIPO" } } → { "claim_type": "trademark", "verification_status": "disputed", "context_note": "EU mark in this jurisdiction held by separate entity; we contest their registration and do not authorize use as ours." } ``` 該当ステータス: `owned`、`licensed_in`、`licensed_out`、`transferring`、`disputed`、`not_ours`、`archived`、`unknown`。(`pending_review` は珍しい — 商標登録は任意の時点で確定的な所有権を持つ公開記録イベント。)`archived` は、ブランドがかつてこのマークを保持していた(期限切れ、キャンセル、別の当事者に移転)がもはやしないことを意味。 **`details` フィールド:** | Field | Tier | Notes | | ------------------------ | ---------- | ------------------------------------------------------------------------------------------------------- | | `matched_registration` | Public | エージェントがクエリをマッチした登録。`verification_status` が `owned`、`licensed_in`、`licensed_out`、`transferring` のとき返される。 | | `licensor_domain` | Public | `verification_status` が `licensed_in` のときライセンサーのドメイン。 | | `countries` | Public | レスポンスがカバーする ISO 3166-1 alpha-2 コード。 | | `nice_classes` | Public | Nice 分類クラス番号。クロス産業マークを曖昧性解消。 | | `use_case_authorization` | Authorized | このマークのユースケースごとの権限 — レジストリクロールからの差別化要因。 | ## Trust model エージェントのレスポンスはブランドの `adcp_use: "response-signing"` JWK の下で署名されます。これはペイロードエンベロープ JWS です — 署名はレスポンスボディ内に存在し、RFC 9421 §2.2.9 トランスポートレスポンス署名(AdCP 3.x は定義しない)と区別されます。`verify_brand_claim` は仕様の [指定タスクレスポンス署名リスト](/docs/building/by-layer/L1/security#designated-task-response-signing) にあります。そのリスト外のタスクでのレスポンス署名は禁止されます。 署名は、エンベロープの `iat`/`exp` ウィンドウ中にブランドの公開鍵の下でレスポンスペイロードの著作を証明します。それは否認防止レシートではなく、下の方向非対称セマンティクスを超えてブランドを主張に拘束しません。必須の `signed_response` エンベロープは、レスポンスを指定タスク、解決された `brand_domain`、応答する `agent_url`、呼び出し元アイデンティティ、リクエストペイロード、鮮度ウィンドウにバインドします。署名に依存する検証者は、エンベロープを [`response-payload-jws-envelope.json`](https://adcontextprotocol.org/schemas/v3/core/response-payload-jws-envelope.json) に対して検証しなければならず(MUST)、オンライン決定のため期限切れエンベロープを拒否し、署名されていないタスクボディフィールドと `signed_response.payload.response` 間の任意の不一致を拒否しなければなりません。 信頼モデルは **方向非対称** です: * **拒否方向**(エージェントが `disputed` / `not_ours` と言う)は権威的。ブランドは一方的に関連を拒否できる。相互性不要。 * **主張方向**(エージェントが `owned` / `pending_review` / `transferring` / `licensed_*` と言う)は情報的だがそれ自体では信頼拡張ではない。相互する側が信頼拡張前に依然として確認しなければならない。 ハウス側とリーフ側エージェントの両方が話すとき(それぞれ `claim_type: "subsidiary"` と `claim_type: "parent"` 経由)、**相互主張がエージェント層で確立** されます。片側のみがエージェントを持つとき、[`brand.json` § 相互主張信頼モデル](/docs/brand-protocol/brand-json#mutual-assertion-trust-model) に従いクロールベースの相互主張推論にフォールバックします。 完全な信頼テーブルと悪意あるハウスのウォークスルーについては [`brand.json` § エージェント拡張検証](/docs/brand-protocol/brand-json#agent-augmented-verification) を参照してください。 ## キャッシング ステータスごと: * `owned` / `not_ours` / `disputed` — 安定。24-72h。 * `pending_review` — 変動的。Max-age ≤1h。 * `transferring` — 遷移まで変動的。Max-age ≤4h。 * `licensed_in` / `licensed_out` — 中程度に変動的。24h。 * `use_case_authorization` — 最も変動的。セッションごとに再チェック。 * `unknown` — 短いキャッシュ(≤1h)。 エージェントは `Cache-Control: max-age=N` を設定すべきです(SHOULD)。消費者は下方にオーバーライドしてもよい(MAY)が、エージェント供給の `max-age` を超えるべきでありません(SHOULD NOT)。 エージェントは `signed_response.payload.exp` を `Cache-Control: max-age` が含意する HTTP 鮮度寿命より遅くない時に設定すべきです(SHOULD)。オンライン決定に署名付きレスポンスを使う消費者は、HTTP キャッシュ期限切れと署名付き `exp` のうち早い方を使わなければなりません(MUST)。その時点の後、エンベロープは新鮮な認可シグナルではなく監査証拠のみのままです。 ## Error handling | Error code | Cause | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | | `AUTH_INVALID` | 呼び出し元の署名付きエンベロープが検証されなかった。 | | `RATE_LIMITED` | エージェントが `{caller_identity, claim_type, claim-target}` ごとに呼び出し元をレート制限した。エージェントは `Retry-After` を返しキャッシュされた以前の答えを返すことを優先すべき(SHOULD)。 | | `UNSUPPORTED_CLAIM_TYPE` | エージェントが要求された `claim_type` を実装しない。`get_adcp_capabilities` 経由で `supported_claim_types` をチェック。 | | `INVALID_INPUT` | 必須の `claim` フィールドが欠けているか不正な形式(例: `subsidiary_domain` が有効なホスト名でない)。 | | `AMBIGUOUS_MATCH` | `claim_type: "trademark"` — 複数の登録が一致。`registry`、`number`、`countries` で絞る。 | ```json theme={null} { "errors": [ { "code": "UNSUPPORTED_CLAIM_TYPE", "message": "claim_type 'property' is not supported by this agent. Supported: subsidiary, parent, trademark." } ] } ``` # verify_brand_claims Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/tasks/verify_brand_claims verify_brand_claims は verify_brand_claim のバルクバリアント — 1 つのブランドエージェントに対して多くのクレームを単一ラウンドトリップで検証。同じ 4 つのクレームタイプ(subsidiary、parent、property、trademark)。結果はリクエストと位置的に揃えて返される。 [`verify_brand_claim`](/docs/brand-protocol/tasks/verify_brand_claim) のバルクバリアント。エージェントは多くの検証質問に単一ラウンドトリップで答え、呼び出し元が送ったのと同じ順序でクレームごとに 1 つの結果を返します。MCP ラウンドトリップコストがクレームごとの作業を支配するときに使います — ブランドポートフォリオをリフレッシュするクローラー、1 つの権利保有者に対してクリエイティブバッチをクリアするクリエイティブクリアランスパイプライン、ハウスに対してサプライパスを検証する在庫オンボーディングスキャン。 **これはバルクアフォーダンスで、異なる操作ではありません。** クレームごとのセマンティクス — 信頼モデル、適用可能なステータス、認可階層、クレームタイプごとの `details` 形状 — は単一ターゲットツールと同一です。単一ターゲットページで文書化されるすべてが結果ごとに適用されます。このページはバルク固有の関心事のみをカバーします。 ## バルクバリアントをいつ使うか | Workflow | Variant | Why | | ------------------------------------------------------ | --------------------------------------------------------------------- | ------------------------------------- | | 1 回限りの検証(単一ページ、単一クリエイティブ、単一子会社チェック) | [`verify_brand_claim`](/docs/brand-protocol/tasks/verify_brand_claim) | バッチングの利益なし。シンプルなエラーモデル。 | | ポートフォリオリフレッシュ(1 ブランドのすべての既知の子会社 / プロパティ / マークを再検証) | `verify_brand_claims` | MCP オーバーヘッドが支配。 | | クローラー駆動の完全ポートフォリオ検証(Nike ポートフォリオ: リージョン全体で約 100 プロパティ) | `verify_brand_claims` | 100 → 1 ラウンドトリップ。 | | クリエイティブクリアランスバッチ(1 つの権利保有者に対して N クリエイティブをプリフライトでクリア) | `verify_brand_claims` | バッチに同じレート制限スロット(下記参照)。 | | クロスブランド検証(異なるエージェントに対する異なるクレーム) | ブランドエージェントごとに 1 バルク呼び出し | 各エージェントは別のアドレス可能なエンドポイント。バルクはエージェント内。 | ## スキーマ * **Request**: [`verify-brand-claims-request.json`](https://adcontextprotocol.org/schemas/v3/brand/verify-brand-claims-request.json) * **Response**: [`verify-brand-claims-response.json`](https://adcontextprotocol.org/schemas/v3/brand/verify-brand-claims-response.json) ## ケイパビリティディスカバリー バルクサポートは単一ターゲットツールと別にアドバタイズされます。エージェントは単一ターゲットツールのみ、バルクツールのみ、または両方を出荷してもよい(MAY)。`supported_claim_types` 宣言は、両方がアドバタイズされるとき両ツールに適用されます — バルクバリアントは、エージェントの単一ターゲット実装が答えられないクレームタイプを受け入れられません。 ```json theme={null} { "supported_protocols": ["brand"], "supported_tasks": [ "get_brand_identity", "verify_brand_claim", "verify_brand_claims" ], "brand": { "verify_brand_claim": { "supported_claim_types": ["subsidiary", "parent", "property", "trademark"] } } } ``` エージェントは仕様の 100 上限より低い呼び出しごとのバッチシーリングをアドバタイズしてもよい(MAY)。シーリングが異なるとき、ケイパビリティ記述子の `extensions` スタイルエントリー経由でアドバタイズするか帯域外で文書化します。消費者は 100 を最大として扱うべきで(SHOULD)、エージェントの上限がより低いとき、それに応じてチャンクすべきです(SHOULD)。 ## 順序が保持される エージェントは、リクエストの `claims[]` と同じ順序で `results[]` を返さなければなりません(MUST)(インデックスによる位置 zip)。呼び出し元は位置インデックスバッチを渡しインデックスで結果を消費します。この保証は、呼び出し元が再キーなしに入力と出力を相関できるようにします: ``` request.claims[7] ↔ response.results[7] ``` バッチが全体的に失敗する場合(認証、レート制限、不正な形式のリクエスト)、レスポンスはトップレベル `errors[]` を運び `results` を省略します。「結果 かつ バッチエラーを伴う部分レスポンス」モードはありません — バッチエラーと結果ごとのエラーはワイヤーで相互排他的です。 ## 部分失敗 — 結果ごとのエラー クレームごとの失敗(多くのクレームの 1 つの `UNSUPPORTED_CLAIM_TYPE`、プロパティのポートフォリオの 1 つの商標クエリの `AMBIGUOUS_MATCH`)はバッチを失敗させません。`results[]` の対応するエントリーは `claim_type`/`status` の代わりに `error` フィールドを運び、バッチの残りは影響を受けません: ```json theme={null} { "results": [ { "claim_type": "property", "status": "owned", "details": { "regions": ["US"] } }, { "error": { "code": "AMBIGUOUS_MATCH", "message": "Multiple registrations match 'CONVERSE'; narrow with registry+number." } }, { "claim_type": "subsidiary", "status": "not_ours" } ] } ``` バッチレベルエラーは、エージェントが何も答えられない失敗のために予約されます: | Tier | Where it goes | Example codes | | ------ | ------------------ | ------------------------------------------------------------------ | | 結果ごと | `results[i].error` | `UNSUPPORTED_CLAIM_TYPE`、1 アイテムの `INVALID_INPUT`、`AMBIGUOUS_MATCH` | | バッチレベル | トップレベル `errors[]` | `AUTH_INVALID`、`RATE_LIMITED`、不正な形式のリクエスト、上限超過の `claims[]` | 完全なエラーコードセマンティクスは [単一ターゲットタスクページ § Error handling](/docs/brand-protocol/tasks/verify_brand_claim#error-handling) で文書化されています。 ## キャッシング 各結果は自身の古さを運んでもよい(MAY) — `pending_review` は短命(≤1h)、`owned` は安定(24-72h)。ステータスごとのキャッシングガイダンスは単一ターゲットページに従います。 バルクレスポンスのトップレベル `Cache-Control: max-age` は **バッチ全体の最小共通 max-age** を表します: 1 つの `pending_review` と 99 の `owned` 結果を持つバッチは `pending_review` シーリングでキャッシュすべき(SHOULD)、なぜなら消費者側キャッシュ無効化は通常レスポンス粒度で動作するから。結果ごとの古さを必要とする呼び出し元は、期待されるステータス変動性でバッチを分割するか、変動的なクレームを個別に再検証すべきです。 結果ごとのキャッシュヒントをサポートするエージェントは、`ext`(例: `results[i].ext.cache.max_age_seconds`)経由でそれらを表示してもよい(MAY)。これは拡張表面のままで、規範的レスポンスの一部ではありません。 ## レート制限 バルク呼び出しは、結果ごとではなく呼び出しごとに **単一のレート制限スロット** を消費します。100 クレームのバッチは `{caller, query-target}` ごとの制限に 100 回ではなく 1 回ヒットします。これはバルクバリアントの中核的経済論拠です — 制限は検証作業ではなくラウンドトリップにあります。 含意: * エージェントは、バルクがアドバタイズされるとき、クレーム/ウィンドウではなく呼び出し/ウィンドウでレート制限をサイズすべきです(SHOULD)。クレームボリュームが運用上重要な場合、バッチごとのクレーム上限をアドバタイズ(ケイパビリティディスカバリーを参照)。 * 呼び出し元は、同じエージェントに対して検証するとき N 単一呼び出しより 1 バルク呼び出しを優先すべきです(SHOULD) — コストと制限内に留まるため。 * バルク呼び出しの `RATE_LIMITED` レスポンスはバッチレベルエラーです。バッチ全体が拒否されます。`Retry-After` を尊重してリトライ。 ## Trust model 単一ターゲットツールと同一です。結果ごとの `status` は同じ方向非対称ルールに従います: 拒否(`disputed` / `not_ours`)は単一の署名付きレスポンスで権威的。主張(`owned` / `pending_review` / `transferring` / `licensed_*`)は肯定的信頼を拡張する前に相互性を要求します。 1 つのトップレベル `signed_response` エンベロープが完全な `results[]` 配列を証明します。その `request_hash` は `claims[]` リクエスト全体、呼び出し元アイデンティティ、解決された `brand_domain`、応答する `agent_url` をバインドします。結果ごとの署名はありません。オンライン決定に署名付きバルク結果を使う消費者は、単一ターゲットタスクと同じ鮮度ルールを適用します: HTTP キャッシュ期限切れと `signed_response.payload.exp` のうち早い方を使う。バルクレスポンスから 1 つの拒否された結果を抽出する監査ストアは、元のバッチリクエストと結果インデックスも保持しなければなりません(MUST)、なぜならエンベロープはスタンドアロンの結果ごとアーティファクトではなくバッチ全体を検証するから。 **「単一呼び出し相互主張」ショートカットはありません。** 1 つのエージェントに対するバルク呼び出しは、関係ペアの両半分が同じバルクリクエスト内に現れても、そのバッチの主張方向クレームの相互主張を確立しません。相互主張は同意する 2 当事者の性質です — `owned` を返す任意の `subsidiary` クレームにはリーフ側エージェントを別途呼ばなければならず、任意の `licensed_in` にはライセンサーのエージェントを呼ばなければならず、などなど。バッチングは MCP ラウンドトリップ経済についてで、信頼モデルを崩すことではありません。 完全な信頼テーブルについては [`brand.json` § エージェント拡張検証](/docs/brand-protocol/brand-json#agent-augmented-verification) を参照してください。 ## 例 — ポートフォリオリフレッシュ クローラーが既知の Nike ポートフォリオ(1 子会社チェック + 3 プロパティチェック)を 1 ラウンドトリップでリフレッシュ: ```json theme={null} { "claims": [ { "claim_type": "subsidiary", "claim": { "subsidiary_domain": "converse.com", "subsidiary_brand_id": "converse" } }, { "claim_type": "property", "claim": { "property": { "type": "website", "identifier": "nike.com" } } }, { "claim_type": "property", "claim": { "property": { "type": "website", "identifier": "nike.cn", "region": "CN" } } }, { "claim_type": "trademark", "claim": { "mark": "AIR JORDAN", "registry": "USPTO", "number": "1234567" } } ] } ``` ```json theme={null} { "results": [ { "claim_type": "subsidiary", "status": "owned", "details": { "brand_id": "converse" } }, { "claim_type": "property", "status": "owned", "details": { "relationship": "owned", "brand_id": "nike", "regions": ["US", "CA", "GB", "FR", "DE", "JP", "AU"] } }, { "claim_type": "property", "status": "owned", "details": { "relationship": "owned", "brand_id": "nike", "regions": ["CN"] }, "context_note": "Regional site for China market" }, { "claim_type": "trademark", "status": "owned", "details": { "matched_registration": { "registry": "USPTO", "number": "1234567", "mark": "AIR JORDAN", "registration_status": "active" }, "countries": ["US"], "nice_classes": [25, 41] } } ] } ``` `subsidiary` 結果を通じてガバナンス信頼を拡張するには、呼び出し元は依然として `claim_type: "parent"` で Converse のブランドエージェントを呼ぶ必要があります。バルク呼び出しはラウンドトリップ経済です。信頼モデルショートカットではありません。 ## バッチレベルエラー例 ```json theme={null} { "errors": [ { "code": "RATE_LIMITED", "message": "Caller has exhausted per-window quota. Retry after the indicated interval." } ] } ``` # 拒否されたクレームの UI ガイダンス Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/ui-guidance DSP、ポートフォリオエクスプローラー、クリエイティブクリアランス UI、ブランドセーフティパイプラインで verify_brand_claim の disputed / not_ours 拒否をレンダーする非規範的な消費者側ガイダンス。帰属言語、拒否されたパブリッシャーのリカバリーパス、監査証跡の推奨、法的露出の考慮をカバー。 [`verify_brand_claim`](/docs/brand-protocol/tasks/verify_brand_claim) が `disputed` または `not_ours` を返すとき、拒否は権威的です — ブランドは一方的に関連を拒否する立場を持ちます。しかし *署名付きの答え* は単独で移動します。それをレンダーする消費者表面は、両側の人間に正気のある提示を負います。 このページは非規範的です。消費者側 — DSP、ポートフォリオエクスプローラー、クリエイティブクリアランス UI、ブランドセーフティパイプライン — と、クレームが拒否されたリーフパブリッシャーのための規約を集めます。 ## 拒否されたクレームのレンダリング 拒否されたレスポンスは `verification_status`(`disputed` または `not_ours`)と、通常ブランドエージェントが書いた `context_note` を運びます。ノートは人間の目のためのものです。表示してください。 ### 帰属言語 **常に拒否を拒否するブランドに帰属させます。** Nike のブランドエージェントからの署名付き `not_ours` は *Nike がこれは自分のものではないと言う* と言います — *このパブリッシャーは偽物* とは言いません。区別は法的に重要です。下の [Legal exposure](#legal-exposure) を参照。 | Don't render | Render instead | | -------------------------- | --------------------------------------------------------- | | "fake-nike-store.com は詐欺的" | "Nike, Inc. は fake-nike-store.com を自身のプロパティの 1 つとして認識しない" | | "この子会社クレームは無効" | "Nike, Inc. はこのブランドの子会社クレームを拒否した" | | "商標が所有されていない" | "Brand X はこのマークがこの管轄で自身のものというクレームに異議を唱える" | ### DSP 在庫ショッピング DSP バイヤーエージェントが入札前にプロパティクレームをチェックし `not_ours` または `disputed` を得るとき: * **デフォルトでプロパティをバイから除外。** 拒否されたプロパティクレームはパブリッシャーのハウス帰属が疑問視されていることを意味します — 基盤在庫品質にかかわらず入札リスクが上昇。 * **別の監査ログではなく、在庫行にインラインで拒否を表示。** バイヤーは決定時にシグナルを必要とします。 * **`context_note` をそのまま表示** ブランドの説明として。言い換えないでください — ブランドが意図的に書きました。 * **バイヤーに手動オーバーライドパスを提供** プロパティがバイヤーが別途検証した異なる関係の下で正当に運用されているケースのため。 ```text theme={null} ┌─────────────────────────────────────────────────────────────┐ │ ◯ premium-sports-network.com CPM $4.20 ━━━ │ │ │ │ ⚠ Nike, Inc. has rejected this site's house-affiliation │ │ claim ("Unaffiliated third-party site; we do not │ │ authorize use of our marks on it.") │ │ │ │ [View details] [Override and bid anyway] │ └─────────────────────────────────────────────────────────────┘ ``` ### ポートフォリオエクスプローラー ポートフォリオエクスプローラー(AAO レジストリ、ブランドセーフティベンダーのルックアップツール)はブランド関係をレンダーします。関係エッジがエージェント層で拒否されたとき: * **警告アイコン付きで存在するかのようにエッジを表示しない。** *contested*(争われている)として表示 — *verified*、*unverified-pending-reciprocation*、*missing* とは別個の視覚状態。 * **拒否されたエッジの両側をレンダー。** 「Brand X は Brand Y の子会社を主張。Brand Y はこのクレームを拒否した。」 * **拒否にタイムスタンプ。** ブランドは立場を変えます。今や実際の買収である 2 年前の拒否が UI を支配すべきでありません。 * **リーフをその更新パスにリンク**(下の [Recovery paths](#recovery-paths-for-the-rejected-leaf) を参照)。 ### クリエイティブクリアランス UI クリエイティブクリアランスパイプラインは、生成されたクリエイティブがマークに侵害していないことを確認するため `claim_type: "trademark"` で `verify_brand_claim` を呼びます。レスポンスが `disputed` または `not_ours` のとき: * **デフォルトでクリエイティブの公開をブロック。** マークが争われているか拒否されている。クリエイティブを昇格することはテイクダウンを招く。 * **異議を唱えるブランドの `context_note` をレンダー** クリエイティブレビュアーが法務にエスカレートするか決められるように。 * **異なるマークバリアントで自動リトライしない** — 拒否は管轄的でニアミスは同じ結果を生むかも。 ### ブランドセーフティパイプライン ブランドセーフティベンダーはサプライチェーン全体で `verify_brand_claim` シグナルを集約します。プロパティがその主張されたハウスに拒否されるとき: * **安全スコアリングでプロパティを降格** するが黙ってゼロにしない — パブリッシャーは正当な独立した在庫を持つかも。 * **完全な帰属で安全レポートに拒否を表示**: 「Brand X は \$DATE にこのサイトの所属クレームを拒否した。」レポートを読むバイヤーは誰が判断したか知る必要がある。 * **エージェントの `Cache-Control: max-age` に一致するスケジュールで再計算。** 拒否状態を永遠にピン留めしない — 基盤ステータスは遷移しうる。 ## Recovery paths for the rejected leaf `house_domain`(または `properties[]`、または `trademarks[]`)クレームが拒否されたリーフパブリッシャーは、**クレームの更新または削除を超えたプロトコルレベルの手段を持ちません**。ブランドプロトコルに「アピール」タスクはありません。これは設計上です — ブランドはカウンタープロセスなしに関連を拒否する立場を持ちます。 しかし拒否されたリーフに着地する消費者表面は、そのリーフオペレーターに明確な次ステップ UI を与えるべきです(SHOULD)。そうでなければパブリッシャーは説明なしに自身のサイトが降格されるのを見ます。 ### リーフに拒否を表示 消費者表面がリーフを見ていることを知っている場合(例: リーフがポートフォリオエクスプローラーまたは DSP セルフサービスポータルにログイン): ```text theme={null} ┌─────────────────────────────────────────────────────────────┐ │ Your brand.json claims house_domain: nikeinc.com │ │ │ │ Nike, Inc.'s brand-agent has rejected this claim: │ │ "We have no record of this brand; the leaf's claim is in │ │ error." │ │ │ │ What this means: AdCP consumers will treat your site as │ │ standalone (not a Nike subsidiary). Your own brand identity │ │ is unaffected. │ │ │ │ Next steps: │ │ • If you should be in Nike's portfolio, contact │ │ (from Nike's brand.json). │ │ • If the claim was mistaken, edit your brand.json to │ │ remove `house_domain`, or point it at the correct │ │ parent. │ └─────────────────────────────────────────────────────────────┘ ``` ### リーフができないこと * **プロトコル定義のチャレンジメカニズムはない。** リーフは AdCP を通じて再考を「強制」できない。それは帯域外のビジネス会話。 * **A の公開された拒否に対して `house_domain: A` を主張するリーフは関係を確立しない。** 消費者はリーフをスタンドアロンとして扱い続ける。 * **異なる表面(新しいサブドメインの新しいブランドエージェント)経由の再主張は役立たない** — 消費者信頼ゲートはドメイン制御 + ハウス側相互性で、主張の数ではない。 ## 監査証跡とアピールプロセスノート 仕様外だが文書化する価値あり: ブランドやパブリッシャーが履歴を見られるよう拒否のレコードを保つ。 ### 拒否されたレスポンスごとに記録するもの * レスポンスの **タイムスタンプ**。 * verify 呼び出しを開始した **呼び出し元アイデンティティ**(自身のユーザー、またはアップストリームサービス)。 * ブランドエージェントのレスポンスの **完全な署名付きエンベロープ** — 署名を含む、下流証明のため。 * **`verification_status`**、**`context_note`**、それをトリガーした `claim` ペイロード。 * 答えが `verify_brand_claims` から来たときの **バルクコンテキスト**: 完全な元の `claims[]` リクエストと拒否された結果インデックス。単一バルクエンベロープは、結果ごとのスタンドアロンアーティファクトではなく `results[]` 配列全体に署名。 * **キャッシュ有効ウィンドウ**: `Cache-Control: max-age` と `signed_response.payload.exp` のうち早い方、レコードが新鮮でなくなったときを知るため。 署名付きエンベロープは、その署名が検証され、その `request_hash` がトリガーリクエストに一致し、拒否されたクレームがエンベロープの `iat`/`exp` ウィンドウ内に収まるとき、耐久性のある履歴証拠です。バイヤーがハウスブランド拒否に基づいてプロパティを除外したことで異議を唱えられる場合、その検証されたエンベロープがバイヤーが返すアーティファクトです。 ### アピールプロセス表面 プラットフォームがアピールフロー(リーフとハウス間のベンダー仲介紛争)をサポートする場合、それをプロトコル層の外に、プラットフォームの関係管理表面の中に保ちます。プロトコルの仕事はブランドの権威的な答えを伝えることです。プラットフォームの仕事は、あれば、ビジネス会話を仲介することです。 ## Legal exposure あるブランドの別の当事者の拒否を放送することは名誉毀損リスクを運びます。2 つの考慮: ### 帰属し、論説しない 拒否をブランドに帰属したブランドの一人称声明としてレンダー: * **良い**: 「Nike, Inc. は fake-nike-store.com が自身のプロパティの 1 つでないと述べた。」 * **悪い**: 「fake-nike-store.com は詐欺的な Nike 模倣者。」 最初は報告可能な事実(Nike の署名付き声明)。2 番目はあなたのプラットフォームが行った告発。 ### 消費者表面が責任を負う、AdCP ではない AdCP はある当事者から別の当事者への署名付きの答えを配信します。消費者表面 — その答えを第三者にレンダーする UI — がそれをどう提示するかの論説決定を所有します。AdCP は名誉毀損を事前訴訟しません。注意してレンダーしてください。 具体的には: * **拒否するブランドが `context_note` テキストを所有。** ブランドが「fake-nike-store.com は詐欺」と書く場合、それはブランドの声明でブランドの露出。あなたの表面はそれをそのままレンダーするかより中立に要約できる。両方ともオーディエンス次第で合理的。 * **`context_note` 外の任意のテキストはあなたのプラットフォームが所有。** ヘッドライン、重大度ラベル、バッジ(「VERIFIED FRAUD」)はあなたの論説選択であなたの露出。 * **ステータスアイコンデザインは重みを運ぶ。** パブリッシャーの名前の隣の赤い X は黄色い「house affiliation contested」バッジと異なって読まれる。基盤シグナルに一致する視覚レジスターを選ぶ。 ### 疑わしいときは、帰属してリンク 最低リスクパターンは、声明をブランドに帰属し署名付きソースにリンクすること: ```text theme={null} "Nike, Inc. says this is not theirs." [View signed response] ``` バイヤーまたはレビュアーは署名付きエンベロープにクリックスルーし自身のビューを形成できます。あなたのプラットフォームはそれを増幅せずにシグナルを配信しました。 ## 関連 * [verify\_brand\_claim](/docs/brand-protocol/tasks/verify_brand_claim) — このガイダンスが適用されるタスク * [ブランドエージェントの構築](/docs/brand-protocol/building-a-brand-agent) — 公開/認可分割と署名セットアップを含むエージェント側実装 * [brand.json § エージェント拡張検証](/docs/brand-protocol/brand-json#agent-augmented-verification) — 拒否を権威的にする非対称信頼モデル # 権利ライセンスのウォークスルー Source: https://adcp-docs-ja.pier1.co.jp/docs/brand-protocol/walkthrough-rights-licensing AdCP 権利ライセンスのウォークスルー: バイヤーがキャンペーンブリーフからタレント探索(get_rights)、取得(acquire_rights)、クリエイティブ承認、延長と取り消しを含むライフサイクル管理まで追う。 Carlos を紹介しよう。アムステルダムの中規模代理店 Pinnacle Media でプログラマティックを担当しています。クライアントの Bistro Oranje — オランダ全土に展開中のステーキハウスチェーン — は次のキャンペーンに有名人アスリートを起用したい。ストック映像でも似た人でもない。AIツールが広告に生成できるリアルな、ライセンスされたオランダ人アスリートの肖像と音声です。 問題は: 利用可能なタレントを見つけ、権利を交渉し、AI ツールの承認を取得し、使用状況を追跡する — これをすべてエージェント経由で、プログラマティックの速度で、どうやって行うか? このウォークスルーは Carlos をキャンペーンブリーフからライブ配信まで — そして途中で事態が変わったときに何が起こるかまで追う。 **7ステップのワークフロー:** 1. **ブリーフ** — キャンペーンが必要なものを定義します 2. **発見** — `brand.json` を取得して権利エージェントを見つける 3. **検索** — `get_rights` で利用可能なタレントをクエリ 4. **取得** — `acquire_rights` で拘束力のあるリクエストを送信 5. **承認** — 承認、拒否、または保留パスを処理します 6. **生成** — ブランドに沿った広告を作成して配信 7. **管理** — キャンペーンを延長、一時停止、または中止します Carlos at his desk reviewing the Bistro Oranje campaign brief, with restaurant brand imagery on one screen and athlete photos on another ## ステップ1: ブリーフ Bistro Oranje はオランダ全土にわたるサマーキャンペーンの顔としてオランダのオリンピックアスリートを求めています。動画広告、ディスプレイバナー、音声スポット。予算: 権利に EUR 5,000、プラスクリエイティブ制作費とメディア費用。キャンペーンは6月から8月まで。 Carlos はメディアバイイングで AdCP を使ったことがあります。権利ライセンスも同じ方法で機能する — バイヤーエージェントは権利エージェントと同じプロトコル、同じツールで話す。 | Carlos が言うこと | プロトコルがそれを呼ぶもの | | ---------------------------- | -------------------------------------------------- | | 「フードブランド向けのオランダ人アスリートを見つけて」 | 自然言語 `query` を含む `get_rights` | | 「肖像と音声でいくら?」 | `get_rights` レスポンスの `pricing_options` | | 「オランダのみで3ヶ月確定して」 | キャンペーン日程と国を含む `acquire_rights` | | 「クリエイティブツールが生成できるようにキーを送って」 | `acquire_rights` レスポンスの `generation_credentials` | | 「タレントの事務所がクリエイティブを承認する必要がある」 | `approval_webhook` 経由の `creative-approval-request` | | 「9月まで延長が必要」 | 新しい `end_date` を含む `update_rights` | | 「タレントが怪我をした — すべて引き上げて」 | `revocation_webhook` への取り消し通知 | *** ## ステップ2: ブランドを発見します Carlos のバイヤーエージェントはすべての AdCP インタラクションが始まる場所から開始する: `brand.json`。エージェントは `https://lotientertainment.com/.well-known/brand.json` を取得し、アスリートのロスターを管理するタレントエージェンシーを見つける。 A buyer agent robot following a glowing trail from a restaurant website to a brand.json file floating in the air, revealing identity data like colors, logos, and tone `rights_agent` フィールドは MCP 呼び出しをする前に、バイヤーエージェントが知る必要があるすべてを伝える — ライセンス可能なもの、権利のタイプ、どこで。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/brand.json", "version": "1.0", "house": { "domain": "lotientertainment.com", "name": "Loti Entertainment", "architecture": "house_of_brands" }, "brands": [ { "id": "daan_janssen", "names": [{ "en": "Daan Janssen" }], "description": "Dutch Olympic speed skater, 2x gold medalist", "industries": ["sports"], "rights_agent": { "url": "https://rights.lotientertainment.com/mcp", "id": "loti_entertainment", "available_uses": ["likeness", "voice", "endorsement"], "right_types": ["talent"], "countries": ["NL", "BE", "DE"] } } ] } ``` ```mermaid theme={null} sequenceDiagram participant Buyer as バイヤーエージェント participant Domain as lotientertainment.com participant Rights as 権利エージェント (MCP) Buyer->>Domain: GET /.well-known/brand.json Domain-->>Buyer: rights_agent 付き brand.json Note over Buyer: 権利エージェント URL、
利用可能な用途、国を発見 Buyer->>Rights: get_brand_identity (brand_id: "daan_janssen") Rights-->>Buyer: ビジュアルアイデンティティ、写真、トーン ``` バイヤーエージェントは `get_brand_identity` も呼び出してビジュアルアセットとトーンガイドラインを取得します。これらはクリエイティブツールがアスリートを起用したブランドに沿った広告を生成する際に後で必要になります。 ```json theme={null} { "brand_id": "daan_janssen", "fields": ["logos", "colors", "tone", "visual_guidelines"] } ``` これにより高解像度の写真、ブランドカラー、外見ガイドラインが返される — クリエイティブエージェントが正しく見える広告を生成するために必要なすべて。 *** ## ステップ3: タレントを検索します Carlos のエージェントが権利エージェントで `get_rights` を呼び出す。Carlos は平易な言語で必要なものを説明します。ドロップダウンメニューなし、カテゴリーコードなし — エージェントが意図を理解します。 A catalog display showing talent cards with availability indicators, pricing tags, and geographic coverage maps, like a talent agency's digital showcase **リクエスト:** ```json theme={null} { "query": "Dutch athlete available for food and restaurant brands in the Netherlands, budget around EUR 5000 for 3 months", "uses": ["likeness", "voice"], "buyer_brand": { "domain": "bistrooranje.nl" }, "countries": ["NL"], "right_type": "talent", "include_excluded": true } ``` `include_excluded: true` フラグは権利エージェントに、そのままではライセンスできないタレントも理由と代替案とともに返すよう求める。このフラグなしでは、レスポンスには利用可能なタレントのみが含まれます。 レスポンスは Daan Janssen を92%マッチとして返す — オランダ国籍、フードカテゴリーに競合なし、予算に合致。2つの価格オプション: インプレッションあたり EUR 3.50 の CPM、または10万インプレッション上限付き EUR 1,500/月の定額制。レスポンスはまた、オランダのフードカテゴリーで独占権があるため Emma van Dijk が除外されており、代替マーケットの代替案が提示されていることを示します。 ```json theme={null} { "rights": [ { "rights_id": "loti_dj_talent_2026", "brand_id": "daan_janssen", "name": "Daan Janssen", "description": "Dutch Olympic speed skater, 2x gold medalist. Available for food, lifestyle, and fitness brands.", "right_type": "talent", "match_score": 0.92, "match_reasons": [ "Dutch nationality matches geographic request", "No food category exclusivity conflicts", "Budget aligns with available pricing options" ], "available_uses": ["likeness", "voice", "endorsement"], "countries": ["NL", "BE", "DE"], "exclusivity_status": { "available": true, "existing_exclusives": [ "Exclusive commitment in sportswear category (NL, BE, DE)" ] }, "pricing_options": [ { "pricing_option_id": "dj_cpm_likeness_voice", "model": "cpm", "price": 3.50, "currency": "EUR", "uses": ["likeness", "voice"], "description": "Likeness and voice, per-impression pricing" }, { "pricing_option_id": "dj_flat_monthly", "model": "flat_rate", "price": 1500, "currency": "EUR", "uses": ["likeness", "voice"], "period": "monthly", "impression_cap": 100000, "overage_cpm": 4.00, "description": "Monthly flat rate with 100K impression cap" } ], "content_restrictions": [ "No depiction in competitive sports contexts", "Alcohol adjacency prohibited", "Creative approval required for video formats" ] } ], "excluded": [ { "brand_id": "emma_van_dijk", "name": "Emma van Dijk", "reason": "Exclusive commitment in food and beverage category (NL)", "suggestions": [ "Available for food brands in BE and DE markets", "Exclusivity expires 2026-12-31 — available in NL from January 2027" ] } ] } ``` Carlos のエージェントは Daan Janssen の2つの価格オプションを確認します。インプレッションごとのモデルはボリュームが予測できない場合に機能します。定額月額制は計画されたキャンペーンに適している — EUR 1,500/月・10万インプレッション上限は3ヶ月のキャンペーン全体で EUR 4,500、予算内。 `excluded` 配列は Emma van Dijk がオランダのフードブランドで利用できないことを示しているが、代替案は彼女がベルギーとドイツで利用可能、または2027年1月からオランダでも利用可能であることを Carlos のエージェントに伝える。代替案がある拒否は実行可能 — エージェントは調整して再試行できます。 ```mermaid theme={null} sequenceDiagram participant Buyer as バイヤーエージェント participant Rights as 権利エージェント Buyer->>Rights: get_rights (query, uses, buyer_brand, countries) Rights-->>Buyer: 価格オプション付きのマッチする権利 Note over Buyer: 価格オプションを評価、
コンテンツ制限を確認、
除外理由をレビュー ``` *** ## ステップ4: 権利を取得します Carlos はオプションを確認して定額月額制を選ぶ。バイヤーエージェントが `acquire_rights` を送信する — 拘束力のある契約上のリクエスト。 Two robots, a buyer agent and a brand agent, shaking hands over a glowing contract document with terms floating above it — dates, geography, and pricing Carlos のエージェントは定額月額制の価格オプション、キャンペーン日程(6月から8月)、取り消しとプッシュ通知のウェブフック、安全な再試行のための冪等性キーを含む `acquire_rights` を送信します。 ```json theme={null} { "rights_id": "loti_dj_talent_2026", "pricing_option_id": "dj_flat_monthly", "buyer": { "domain": "bistrooranje.nl" }, "campaign": { "description": "Summer steakhouse campaign featuring Daan Janssen in video, display, and audio ads promoting Bistro Oranje locations across the Netherlands", "uses": ["likeness", "voice"], "countries": ["NL"], "format_ids": [ { "agent_url": "https://creatives.pinnaclemedia.com", "id": "video_16x9_30s" }, { "agent_url": "https://creatives.pinnaclemedia.com", "id": "display_300x250" } ], "estimated_impressions": 250000, "start_date": "2026-06-01", "end_date": "2026-08-31" }, "revocation_webhook": { "url": "https://api.pinnaclemedia.com/webhooks/revocation", "auth": { "type": "bearer", "token": "whk_pinnacle_abc123" } }, "push_notification_config": { "url": "https://api.pinnaclemedia.com/webhooks/rights-updates", "auth": { "type": "bearer", "token": "whk_pinnacle_def456" } }, "idempotency_key": "bistro-dj-summer-2026-v1" } ``` 3つの結果が考えられます。 ```mermaid theme={null} flowchart TD A[acquire_rights リクエスト] --> B{権利エージェント
が評価} B -->|クリア済み| C["acquired 条件 + 資格情報 + 権利制約"] B -->|レビューが必要| D["pending_approval 推定応答時間"] B -->|対応不可| E["rejected 理由 + オプションの代替案"] C --> F[クリエイティブ生成開始] D --> G[解決時にウェブフック通知] E --> H{代替案
あり?} H -->|Yes| I[調整して再送信] H -->|No| J[他のタレントに進む] style C fill:#047857,color:#fff style D fill:#d97706,color:#fff style E fill:#dc2626,color:#fff ``` Carlos のエージェントはクリエイティブの制作を開始するために必要なすべてを取得します: Midjourney(肖像)と ElevenLabs(音声)のキー、すべての広告の法的開示テキスト、タレントの承認のためにクリエイティブを送信するリンク。月間上限の10万インプレッションは3ヶ月の条件にわたる合計キャンペーン上限の30万に変換されます。 ```json theme={null} { "rights_id": "loti_dj_talent_2026", "status": "acquired", "brand_id": "daan_janssen", "terms": { "pricing_option_id": "dj_flat_monthly", "amount": 1500, "currency": "EUR", "period": "monthly", "uses": ["likeness", "voice"], "impression_cap": 100000, "overage_cpm": 4.00, "start_date": "2026-06-01", "end_date": "2026-08-31" }, "generation_credentials": [ { "provider": "midjourney", "rights_key": "rk_mj_dj_2026_bistro_7f3a9b", "uses": ["likeness"], "expires_at": "2026-09-01T00:00:00Z" }, { "provider": "elevenlabs", "rights_key": "rk_el_dj_2026_bistro_4e8c1d", "uses": ["voice"], "expires_at": "2026-09-01T00:00:00Z" } ], "restrictions": [ "No depiction in competitive sports contexts", "Alcohol adjacency prohibited", "Creative approval required for video formats" ], "disclosure": { "required": true, "text": "Features AI-generated likeness of Daan Janssen, used under license from Loti Entertainment." }, "approval_webhook": { "url": "https://rights.lotientertainment.com/api/creative-approval", "auth": { "type": "bearer", "token": "appr_loti_dj_2026_9x4k" } }, "usage_reporting_url": "https://rights.lotientertainment.com/api/usage", "rights_constraint": { "rights_id": "loti_dj_talent_2026", "rights_agent": { "url": "https://rights.lotientertainment.com/mcp", "id": "loti_entertainment" }, "valid_from": "2026-06-01T00:00:00Z", "valid_until": "2026-09-01T00:00:00Z", "uses": ["likeness", "voice"], "countries": ["NL"], "impression_cap": 300000, "right_type": "talent", "verification_url": "https://rights.lotientertainment.com/verify/loti_dj_talent_2026" } } ``` *** ## ステップ5: 承認と拒否 すべてのリクエストがすぐに承認されるわけではありません。プロトコルは他の2つのパスを処理します。 Split panel showing two paths: left side has a golden key credential being handed over for an approved request; right side shows a rejected stamp with two branches — one looping back with a lightbulb for suggestions, another with a final X ### 承認待ち 一部のリクエストは人間のレビューが必要 — タレントのマネジメント、法務チーム、またはアスリート本人。権利エージェントは推定タイムラインとともに `pending_approval` を返します。 ```json theme={null} { "rights_id": "loti_dj_talent_2026", "status": "pending_approval", "brand_id": "daan_janssen", "detail": "Video format requests require creative concept review by talent management", "estimated_response_time": "48h" } ``` 決定が下されると、権利エージェントが `push_notification_config` URL にウェブフックを送信します。バイヤーエージェントはポーリングしません。 ### 代替案付きの拒否(実行可能) リクエストがそのままでは対応できないが、バイヤーが調整できる場合、拒否に `suggestions` が含まれます。その存在がシグナル — 代替案がある場合、拒否は修正可能です。 ```json theme={null} { "rights_id": "loti_dj_talent_2026", "status": "rejected", "brand_id": "daan_janssen", "reason": "Requested dates conflict with an existing exclusivity commitment in the food category for this market", "suggestions": [ "Available in NL from 2026-09-01 onward", "Available immediately in BE and DE markets", "Consider likeness-only (without voice) — available at reduced rate" ] } ``` Carlos のエージェントは修正できる — 日程をずらす、地域を変える、または音声権利を外す — そして再送信します。 ### 代替案なしの拒否(最終) 代替案がない場合、そのタレントとキャンペーンの組み合わせに対して拒否は最終的です。 ```json theme={null} { "rights_id": "loti_dj_talent_2026", "status": "rejected", "brand_id": "daan_janssen", "reason": "This request does not meet the talent's current licensing criteria" } ``` `suggestions` フィールドなし。バイヤーエージェントは次に進む。理由が意図的に曖昧な場合がある — 事務所はプライベートなビジネスロジックを公開することなくバイヤーエージェントが結果を理解するのに十分なサニタイズされた理由を提供します。 *** この時点で Carlos はタレントをライセンスしてクリエイティブを生成する準備ができています。次のセクションは取得後の生成、配信、ライフサイクル管理について説明します。 バイヤー認定プログラムを通じてプロトコルをハンズオンで学ぶ。 バイヤーの視点から権利ライセンスがどのように機能するか。 *** ## ステップ6: 生成と配信 資格情報を手に入れた Carlos のクリエイティブツールが動き始める。Midjourney の資格情報が肖像を生成します。ElevenLabs の資格情報が音声を生成します。各プロバイダーは生成時に `rights_key` を検証する — 資格情報自体が認可です。 Carlos reviewing AI-generated ads on his screen — a display banner and video still featuring the licensed athlete, with a verification badge and shield icon ### クリエイティブ承認 動画フォーマットでは、コンテンツ制限により配信前の承認が必要です。クリエイティブエージェントが完成した広告を `approval_webhook` に送信します。 ```json theme={null} { "rights_id": "loti_dj_talent_2026", "creative_id": "bistro_summer_video_01", "creative_url": "https://cdn.pinnaclemedia.com/creatives/bistro_summer_video_01.mp4", "creative_format": { "agent_url": "https://creatives.pinnaclemedia.com", "id": "video_16x9_30s" }, "description": "30-second video spot featuring Daan Janssen recommending Bistro Oranje summer menu" } ``` ### クリエイティブマニフェストの権利制約 ライセンスされたタレントを使用するすべてのクリエイティブは、マニフェスト内に `acquire_rights` レスポンスの `rights_constraint` を含む — バイヤーは手動で構築しません。単一の広告は異なる権利保有者からのタレント肖像と音楽を組み合わせることができ、それぞれ異なる有効期間と地理的制限を持ちます。ダウンストリームの参加者(SSP、検証ベンダー)は配信前に付与がまだアクティブであることを確認するために `verification_url` にアクセスします。 ### 使用状況報告 インプレッションは請求と上限管理のために権利エージェントに報告されます。 条件のインプレッション上限(月間10万)はデフォルトではソフト上限です。キャンペーンがそれを超えた場合、追加インプレッションは `overage_cpm` レート(EUR 4.00)で請求されます。権利エージェントは累積使用状況を追跡し、上限に近づいたらバイヤーに通知できます。 *** ## ステップ7: ライフサイクルは続く 権利は静的ではありません。キャンペーンは変化し、契約は延長され、時には問題が起こる。 Timeline view showing the full rights lifecycle: an active campaign with an impression counter ticking, a pause and update midstream, and a revocation alert notification appearing on Carlos's screen ### 権利の更新 キャンペーン途中で Bistro Oranje は9月まで延長したい。Carlos のエージェントが `update_rights` を呼び出す。 ```json theme={null} { "rights_id": "loti_dj_talent_2026", "end_date": "2026-09-30", "impression_cap": 150000, "idempotency_key": "bistro-dj-extend-sept-v1" } ``` レスポンスには更新された条件と新しい有効期限で再発行された生成資格情報が含まれます。古い資格情報は重複期間中も機能し続ける — クリエイティブ配信に隙間はない。 キャンペーンを一時停止する必要がある場合(タレントの怪我、ブランドの問題、季節的な休止)、エージェントは `paused: true` を設定できます。資格情報は停止されます。再開するには `paused: false` を設定します。 ### 自然な有効期限切れ キャンペーンが終了して `valid_until` に達すると、資格情報は自動的に期限切れとなります。生成リクエストは機能しなくなります。どちらの側のアクションも不要。 ### 取り消し タレントの事務所が権利を取り消す必要がある場合 — スキャンダル、契約違反、法的問題 — バイヤーの `revocation_webhook` に取り消し通知を POST します。 ```json theme={null} { "notification_id": "rev_loti_dj_2026_001", "rights_id": "loti_dj_talent_2026", "brand_id": "daan_janssen", "reason": "Rights revoked due to updated talent representation terms", "effective_at": "2026-07-15T18:00:00Z" } ``` バイヤーは受領を確認します。通知の配信に失敗した場合、権利エージェントは自動的に再試行します。 `effective_at` が将来の時刻の場合、バイヤーはクリエイティブ配信を終了するための猶予期間を持ちます。現在の時刻の場合、取り消しは即時 — 今すぐ配信を停止します。 部分的な取り消しもサポートされています。通知に `revoked_uses` が含まれている場合、それらの使用のみ取り消されます。付与の残りはアクティブのままです。 ```mermaid theme={null} stateDiagram-v2 [*] --> Active: acquire_rights (acquired) Active --> Active: update_rights (延長、上限調整) Active --> Paused: update_rights (paused: true) Paused --> Active: update_rights (paused: false) Active --> PartialRevocation: 取り消し (revoked_uses あり) PartialRevocation --> Active: 残りの使用が継続 Active --> Revoked: 取り消し (全体) Paused --> Revoked: 取り消し (全体) Active --> Expired: valid_until に達した Revoked --> [*] Expired --> [*] ``` *** ## 確認できたこと ブランドプロトコルは探索(`brand.json`)からライセンス(`get_rights`、`acquire_rights`)、ライフサイクル管理(`update_rights`、取り消しウェブフック)まで権利を処理します。すべてのステップでバイヤーエージェントがすでに使用している同じ MCP トランスポートを使用します。権利制約はクリエイティブと一緒に伝わる — サプライチェーンのすべての参加者が配信前に検証できます。 Bistro Oranje のキャンペーンが7月の途中で、Daan Janssen がベルギーのフードブランドを含む新しいスポーツウェアの独占契約に署名したことがわかった。Carlos のキャンペーンはオランダのみで実施されています。Carlos は何かする必要があるか?なぜか? *考えるべきこと: 権利付与における地理的スコーピング、既存の独占権と新しい独占権の違い、Carlos の `acquire_rights` 条件の `countries` フィールドが実際にカバーするもの。* *** ## さらに深く brand.json ファイル形式の完全な技術仕様。 バイヤーの視点から権利ライセンスがどのように機能するか。 AdCP がタレント権利を保護してマネタイズする方法。 アイデンティティと権利を提供するブランドエージェントを実装します。 価格と空き状況付きでライセンス可能なタレントを検索します。 拘束力のあるリクエストを送信して生成資格情報を受け取ります。 既存の権利付与を変更する — 延長、一時停止、調整。 バイヤー認定を取得してプロトコルをハンズオンで学ぶ。 # よくある質問 Source: https://adcp-docs-ja.pier1.co.jp/docs/faq AdCP に関するよくある質問への回答——ライセンス(Apache 2.0、無償で利用可能)、OpenRTB や IAB 標準との関係、メンテナンス主体(AgenticAdvertising.org)、実装の始め方。 ## エージェント広告について AI アシスタントに商品のレコメンデーションを求めると、アシスタントは関連するブランドを提示できます——リテールプラットフォームで検索するとスポンサー商品が表示されるのと同様です。ブランドは、商品カタログ、ブランドアイデンティティ、コンテンツ標準を事前にプラットフォームへプッシュしておきます。ユーザーの質問がマッチすると、AI はそのデータから文脈的に関連するレコメンデーションを生成します。これは [Sponsored Intelligence](/docs/sponsored-intelligence/overview) と呼ばれ、コンテンツは常にスポンサー付きであることが明示されます——スポンサー検索やリテールメディアの掲載が明示されるのと同じです。 AI プラットフォームは広告の提供を始めており、市場は成長しています。AdCP は標準プロトコルを提供するため、バイヤーエージェントは——それぞれにカスタム統合を構築することなく——それを実装するあらゆる AI プラットフォームに接続できます。バイヤーエージェントは [`get_products`](/docs/media-buy/task-reference/get_products) を通じて、接続されたセラーから利用可能な在庫をリアルタイムにディスカバリーします。 AI 向け SEO(GAIO や生成 AI 最適化とも呼ばれます)は、公開コンテンツを最適化してオーガニックな AI レスポンスの中でブランドが言及されるようにすることに焦点を当てます。[Sponsored Intelligence](/docs/sponsored-intelligence/overview) は有料広告です——ブランドは構造化された商品データ、ブランドアイデンティティ、最適化目標を標準プロトコルを通じて AI プラットフォームへプッシュし、プラットフォームは明示されたスポンサーコンテンツを生成します。この二つのアプローチは競合ではなく補完的です。 **実験的機能。** Sponsored Intelligence は AdCP 3.0 の実験的サーフェス(機能 id `sponsored_intelligence.core`)です——セッションのライフサイクル、UI コンポーネント、アイデンティティ/同意オブジェクトの形状、機能ネゴシエーションは、少なくとも6週間の予告のうえで 3.x リリース間に変更される可能性があります。パイロット実装は推奨されますが、規制対象またはコンプライアンスに敏感なワークフローは安定版への昇格を待つべきです。完全な契約は[実験的ステータス](/docs/reference/experimental-status)を参照してください。 AI プラットフォームで広告を購入する実践的なガイドは、[AI マネタイズガイド](/docs/sponsored-intelligence/monetizing-ai)を参照してください。技術的なプロトコルのウォークスルーは、[Sponsored Intelligence 概要](/docs/sponsored-intelligence/overview)を参照してください。 ## AI メディアの購入 どちらの方法でも機能します。エージェンシー、アドネットワーク、コマースプラットフォームはあなたに代わって AdCP を実装できます——あなたが商品データとブランドガイドラインを提供し、彼らが AI プラットフォームをまたいだプロトコルの配管を処理します。プログラマティックを自社で運用している場合は、あなた(またはエンジニアリングチーム)が AdCP に対して直接バイヤーエージェントを構築できます。[AI マネタイズガイド](/docs/sponsored-intelligence/monetizing-ai#getting-started-by-role)では、ブランド、エージェンシー、中小企業向けの選択肢を解説しています。 はい。ブランドは[ブランドアイデンティティ](/docs/brand-protocol/brand-json)(ボイス、ビジュアルガイドライン、ポジショニング)と[コンテンツ標準](/docs/governance/content-standards)(承認された主張、避けるべきトピック、適合性ルール)をプロトコルを通じて AI プラットフォームへプッシュします。プラットフォームは、事後にフィルタリングするのではなく、生成時——コンテンツが表示される前——にこれらを強制します。完全なモデルは[ガバナンス](/docs/governance/overview)を参照してください。 価格はプラットフォームとフォーマットによって異なります。一般的なモデルには、スポンサーレスポンスや AI 検索結果向けの CPC(クリック単価)、SI Chat Protocol による会話型ブランド体験向けのセッション単価があります。バイヤーエージェントは [`get_products`](/docs/media-buy/task-reference/get_products) を通じて接続されたセラーから利用可能な価格をディスカバリーします——各プロダクトが価格オプションを提示します。 AI アシスタント、検索コパイロット、会話型プラットフォームが今日稼働しています。エコシステムは初期段階にあり成長中です。バイヤーエージェントは [`get_products`](/docs/media-buy/task-reference/get_products) を通じて接続されたセラーから利用可能な在庫をリアルタイムにディスカバリーします——現在リーチ可能なものを常に確認できます。 AdCP はアトリビューションやビューアビリティを規定しません——MRC 認定の測定標準ではありません。プロトコルは、既存の IAS、DV、Nielsen、Comscore、またはアトリビューションツールが消費する配信・利用データを運びます。既存の測定契約と認定はそのまま維持できます。AdCP が標準化しないものの完全なリストは[既知の制限](/docs/reference/known-limitations)を参照してください。 はい。AdCP は既存のエージェンシー関係に対して付加的です。エージェンシーはバイヤーエージェントを使って能力を拡張できますし、AdCP 認定のプラクティショナーと協業することもできます。 エージェンシーがすでに AdCP をサポートしていれば、数日で稼働できます。そうでない場合、[AI マネタイズガイド](/docs/sponsored-intelligence/monetizing-ai#getting-started-by-role)では、AdCP 認定パートナーとの協業を含め、ブランド、エージェンシー、中小企業向けの選択肢を解説しています。 ブランド、エージェンシー、中小企業のいずれであっても——何が必要で、どのデータを提供し、どうパートナーを見つけるか。 ## AdCP について AdCP(Ad Context Protocol)は、AI エージェントが標準化された言語を用いて広告プラットフォームをまたいで協調できるようにするオープンプロトコルです——商品ディスカバリー、メディアバイイング、クリエイティブ生成、オーディエンス活性化、ブランドガバナンスにまたがります。 [埋め込まれた人間の判断](/docs/governance/embedded-human-judgment)が人間の説明責任を保ちます: エージェントのアクションは実行前にレビューされ承認されます。 AdCP は仕様です——製品、プラットフォーム、企業ではありません。誰でも実装できます。[イントロダクション](/docs/intro)または[構築ガイド](/docs/building)から始めてください。 AdCP は [AgenticAdvertising.org](https://agenticadvertising.org)(AAO)によって開発・維持されています。AAO はデラウェア州の非営利業界団体(IRS への 501(c)(6) ステータスは申請中)で、四つの対等な投票クラス——ブランド、エージェンシー、パブリッシャー、テクノロジープロバイダー——を持ち、定常状態ではクラスごとに10の選出議席を目標としています。 ファウンデーションは現在、設立時に任命された暫定理事会のもとで運営されています: Michael Blum(Scope3)、Brian O'Kelley(Scope3)、Pia Malovrh(Celtra)、Benjamin Masse(Triton Digital)。暫定理事会は、**2026年5月6日**の第1回年次総会で選出理事会に置き換えられます。四つの暫定議席のうち二つが Scope3 系であり、これは Scope3 のシード拠出を反映しています。完全な関係、指名された忌避領域、対等な投票クラス代表への移行については、[AAO は Scope3 とどう関係していますか?](#how-is-aao-related-to-scope3)を参照してください。 日々のプロトコル作業は [GitHub](https://github.com/adcontextprotocol/adcp) の公開ワーキンググループで行われ、すべての変更は Git 履歴で監査可能です。issue、プルリクエスト、ワーキンググループへの参加を通じてプロトコルを形作った貢献者・組織は [CONTRIBUTORS.md](https://github.com/adcontextprotocol/adcp/blob/main/CONTRIBUTORS.md) に記載されています。 エージェント AI を通じて、より知的で人間中心の広告の未来を切り拓くこと——AI のスケールと人間の判断の力を組み合わせます。 三つの柱がミッションを支えます: [オープン標準](https://docs.adcontextprotocol.org)(AdCP)、[教育](/docs/learning/overview)(アカデミーと認定プログラム)、[ガバナンス](/docs/governance/overview)(重要な意思決定に人間を関与させ続けるフレームワーク)。 はい。AdCP は [Apache 2.0 ライセンス](https://www.apache.org/licenses/LICENSE-2.0)のもとでのオープンソースです。プロトコルの利用、実装、ライセンスに費用はかからず、許可も不要です。仕様、[JSON スキーマ](https://adcontextprotocol.org/schemas/v3/)、ドキュメントは自由に利用可能です——料金なし、ライセンス契約なし、メンバーシップ要件なし。 AdCP は **3.0.1 — 一般提供(General Availability)** の段階にあり、3.0 は2026年4月にリリースされました。プロトコルは安定しており、本番環境で利用可能です。完全な変更ログは[リリースノート](/docs/reference/release-notes)を、2.5 → 3.0 の移行サマリーは [v3 の新機能](/docs/reference/whats-new-in-v3)を参照してください。 次のメジャーバージョン(4.0)は2027年初頭を目標としています。[リリースケイデンスポリシー](/docs/reference/versioning#release-cadence)のもとでは、メジャーバージョンは少なくとも18か月間隔で、前のメジャーは後継の GA 後少なくとも12か月間セキュリティパッチを受け、非推奨の通知は削除の少なくとも6か月前に公開されます。完全なポリシーと 3.x の安定性保証は[バージョニングとガバナンス](/docs/reference/versioning)を参照してください。AdCP 2.5 は 2026-08-01 までセキュリティパッチが提供されます——EOL のタイムラインは [v2 サンセットページ](/docs/reference/v2-sunset)を参照してください。 AdCP 3.0 は破壊的変更を導入します。まず [v3 の新機能](/docs/reference/whats-new-in-v3)でサマリーを確認し、次にトピック別の[移行ガイド](/docs/reference/migration)——チャネル、価格、クリエイティブ、カタログ、ジオターゲティング、最適化目標、ブランドアイデンティティ、オーディエンス——を順に進めてください。新しいプロトコルドメイン(アカウント、ガバナンス、ブランドプロトコル)は付加的です。既存の統合はそれらを段階的に採用できます。 ## AdCP と他の標準との関係 いいえ。AdCP と OpenRTB は異なるレイヤーで動作し、補完的です。 | | OpenRTB | AdCP | | --------- | -------------------- | --------------------------------------- | | **スコープ** | インプレッションレベルのトランザクション | エージェントレベルのワークフロー | | **操作** | 入札リクエスト、入札レスポンス、落札通知 | 商品ディスカバリー、メディアバイ作成、クリエイティブ生成、オーディエンス活性化 | | **参加者** | DSP と SSP | AI エージェントと広告プラットフォーム | | **タイミング** | リアルタイム(ミリ秒) | 非同期(秒〜日) | プラットフォームは両方を実装できます。例えば、パブリッシャーの AdCP エージェントはバイヤーエージェントから [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) タスクを受け取り、内部で OpenRTB を使ってインプレッションレベルの配信を実行できます。AdCP がワークフローを処理し、OpenRTB がオークションを処理します。 AdCP は AgenticAdvertising.org(AAO)——独立した仕様策定団体——によって維持されており、IAB Tech Lab の子会社やワーキンググループではありません。両組織は広告スタックの異なるレイヤーに位置し、異なるケイデンスで運営されています。 * **レイヤー。** AdCP はキャンペーンレイヤー——インプレッションレベルのオークションの上にあるバイヤー/セラーのワークフロー(商品ディスカバリー、メディアバイ作成、クリエイティブ、シグナル、ガバナンス)——を記述します。IAB Tech Lab のポートフォリオ(OpenRTB、VAST、`ads.txt`/`sellers.json`、Open Measurement SDK、コンテンツ分類、オーディエンス分類、GPP)は、インプレッションレイヤーとその下のサプライチェーンのプリミティブを標準化します。AdCP はそれらすべてと共存します——コンテンツ分類フィールドは IAB コンテンツカテゴリと整合し、オーディエンスセグメントは IAB オーディエンス分類 ID を参照でき、[`adagents.json`](/docs/governance/property/adagents) は `ads.txt`/`sellers.json` の関係セマンティクスを置き換えるのではなく拡張します。 * **ケイデンス。** AAO は GitHub 上の公開 RFC とワーキンググループを通じて月次サイクルで作業しており、まだ動きの激しいエージェントサーフェスに適しています。IAB Tech Lab の標準化プロセスは、はるかに大きな会員を横断する、より遅く広範な合意形成のために作られています。 AdCP は Apache 2.0 です——IAB Tech Lab やその他の団体は、仕様を自由に採用、参照、または整合させることができます。AdCP、OpenRTB、MCP、A2A がどう関係するかの全体像は[業界ランドスケープ](/docs/building/concepts/industry-landscape)を参照してください。 AAMP——[IAB Tech Lab の Agentic Advertising Management Protocols フレームワーク](https://iabtechlab.com/standards/)——は、エージェント広告のイニシアチブ群(Agent Registry や Agentic Audiences のワークストリームを含む)として立ち上がりつつあります。これまでに公開された AAMP の資料に基づくと、AdCP と AAMP はスタックの異なるレイヤーで動作し、共存できるように見えます。 端的に言えば: **AAMP はエージェント入札、AdCP はエージェント購買です。** 現時点で説明されている AAMP のワークストリームは、インプレッションレベルの関心事——プログラマティックオークションの内部でエージェントがどう発見・識別されるか、エージェントオーディエンスがどう横断するか——を扱い、インプレッションレイヤー(200ms 未満、単一オークション)で OpenRTB と並びます。AdCP はその上のキャンペーンレイヤーを記述します: バイヤーエージェントとセラーエージェントが、商品ディスカバリー、価格、クリエイティブ、シグナル、ガバナンスにまたがってメディアバイをどう交渉、取引、統治するか。レイヤーは合成されます——単一の AdCP `create_media_buy` が数千のインプレッションレイヤーイベントを生み出しうるのです。プラットフォームは両方を実装できます。 2026年4月時点: | | AAMP | AdCP | | ----------- | ----------------------------- | ------------------------------------------------------------------------------------- | | **レイヤー** | インプレッションレイヤー——エージェント入札 | キャンペーンレイヤー——エージェント購買 | | **メンテナー** | IAB Tech Lab | AgenticAdvertising.org | | **成熟度** | 複数のサブイニシアチブにまたがる立ち上げ期のフレームワーク | 3.0 GA(2026年4月リリース) | | **スコープ** | 複数のエージェントイニシアチブのアンブレラ | メディアバイイング、クリエイティブ、シグナル、ブランドガバナンス、実行(TMP)をカバーする単一仕様 | | **ガバナンス検証** | 定義中 | 15ステップ検証付きの署名済みガバナンスコンテキスト([セキュリティモデル](/docs/building/concepts/security-model)のレイヤー4) | | **公開スキーマ** | 定義中 | [公開済み](https://adcontextprotocol.org/schemas/v3/)、Apache 2.0 | AAMP がまだ規範的サーフェスを定義中であるため、正式な技術比較はまだ公開していません。両方に関心のある実装者は、両仕様が安定するのを見守るべきです。 Google の Universal Commerce Protocol(UCP)——Shopify、Walmart、Target らと共同開発——と、OpenAI・Stripe の Agentic Commerce Protocol(ACP)は、AI アシスタントにおける*コマース*を標準化します: チェックアウト、決済、フルフィルメント。AdCP は*広告*を標準化します: オファーがどう提示されるか、ブランドエージェントがどうユーザーと関わるか、アトリビューションがどう戻るか。 これらは異なるレイヤーであり、競合する仕様ではありません。 | レイヤー | 標準 | 役割 | | ---- | ------- | -------------------------------------------------- | | コマース | UCP、ACP | チェックアウト、決済、フルフィルメント、注文ステータス | | 広告 | AdCP | スポンサーディスカバリー、メディアバイイング、クリエイティブ、ブランドガバナンス、アトリビューション | レイヤーは受け渡し(ハンドオフ)で交わります。[SI Chat Protocol](/docs/sponsored-intelligence/si-chat-protocol) は AI アシスタント内で会話型ブランド体験を実行します。ユーザーが購入を決めると、ホストはチェックアウトのために ACP または UCP へ引き継ぎ、SI `session_id` をコンテキストとして持ち越すことで、トランザクションをスポンサー会話に帰属させられます。コマースプロトコル自身のセッションが購入フローを所有します。AdCP は引き継ぎまでの経路を所有し、コマースプロトコルがトランザクションを所有します。 両方を実装するプラットフォームは、「適切なオファーを提示しユーザーと関わる」ために AdCP を、「決済を受け取る」ために UCP/ACP を利用します。どちらも他方の代替にはなりません。 作ることはできますし、一部のデプロイではそれが正しい選択です——スコープが単一組織内にとどまる場合、独自の内部プロトコルで問題ありません。 共有プロトコルがそのコストに見合うのは、二つの条件が成り立つときです: (1) エージェントが組織の境界をまたいで相互運用する必要がある、(2) 実装者が必要とする保証——冪等性、署名済みガバナンス、構造的なプライバシー分離、適合性——をゼロから構築するのが簡単ではない。AdCP はすでにワイヤーを規定し、スキーマを公開し、適合性テストを実行し、15ステップ検証付きの署名済みガバナンスプロファイル([セキュリティモデル](/docs/building/concepts/security-model)参照)を備えています。これを内部で再構築するのは実際のコストであり、内部プロトコルを取引相手に信頼してほしいなら、いずれにせよそれを周知させる必要があります。 必要なものが AdCP に欠けている場合、より安価な方法は通常: `ext.{vendor}` で拡張する、またはワーキンググループに変更を提案することです。[コントリビューションガイド](https://github.com/adcontextprotocol/adcp/blob/main/CONTRIBUTING.md)を参照してください。 MCP はエージェントがツールを*どのように*呼び出すかを定義します。AdCP は、エージェントが広告ツールを呼び出すときに*何を*言うかを定義します。AdCP なしで広告購入ツールを公開する MCP サーバーは、独自のタスク形状、独自のレスポンススキーマ、独自のエラーコード、独自のガバナンスセマンティクスを定義します——そしてすべてのバイヤーエージェントは、サーバーを一つずつ統合しなければなりません。 AdCP こそがツールを交換可能にするものです。すべてのパブリッシャーの MCP サーバーが AdCP の `create_media_buy` を話せば、一つのバイヤーエージェントがそれらすべてと統合できます。AdCP がなければ、「新しいパブリッシャーへの接続」は毎回新しい開発タスクになります。 言い換えれば: MCP はトランスポート、AdCP はプロトコルです。HTTP が REST ペイロードを運ぶのと同じように、MCP を使って AdCP タスクを*運びます*。AdCP、OpenRTB、MCP、A2A がどう関係するかは[業界ランドスケープ](/docs/building/concepts/industry-landscape)を参照してください。 AdCP はエージェントワークフロー——直接販売の在庫、保証付き取引、コマースメディア——のために作られており、OpenRTB がすでに機能しているインプレッションレベルのオークションのためではありません。バイヤーとセラーはエージェントを通じて直接取引し、価格は `pricing_options`、`price_guidance`、そして(トランザクションがそれを正当化する場合)`price_breakdown` を通じて提示されます。 プログラマティックオークションで SPO 開示を推進したサプライパス最適化の関心事は、バイヤーとセラーが匿名の入札者ではなく認証された取引相手である直接販売のトランザクションでは異なって見えます。SPO 相当の手数料開示を求めるバイヤーは、購入の条件として `buy_terms` を通じてそれを要求できます——プロトコルはそれをサポートしますが、プロトコル全体の義務として課されるものではありません。 `adagents.json` は、ads.txt/sellers.json の関係セマンティクスを保ちながら、エージェント購買のための認可モデルを拡張します。`delegation_type` を持つパブリッシャーの `adagents.json` は ads.txt の `DIRECT` または `RESELLER` 行と同じシグナルを運び、`relationship` フィールドを持つ `brand.json` プロパティは sellers.json エントリと同じシグナルを運びます。 完全なクロスウォークは [なぜ ads.txt ではなく adagents.json なのか](/docs/governance/property/adagents#why-adagents-json-instead-of-ads-txt)にあります。 これらの業種は GDPR 第22条、EU AI 法 附属書 III、米国 FHA / ECOA / EEOC の対象です。ポリシーカテゴリの仕組みには、レジストリにすでに `fair_housing`、`fair_lending`、`fair_employment` のエントリが含まれています。 AdCP 3.0 GA では、規制対象のポリシーカテゴリを宣言するキャンペーンは人間のレビューを伴って実行することが必須になります——`authority_level: agent_full` は受け入れられません——と同時に、附属書 III カテゴリ分類とデータ主体の異議申立て経路が追加されます。[#2310](https://github.com/adcontextprotocol/adcp/issues/2310) で追跡されています。それが出荷されるまで、強制はスキーマ不変条件ではなくガバナンスエージェントの実装に依存します。完全なモデルは[ガバナンス概要](/docs/governance/overview)を参照してください。 AdCP は AgenticAdvertising.Org(AAO)——デラウェア州で設立された申請中の 501(c)(6) 業界団体——によって統治されています。ガバナンスはリポジトリの [CHARTER.md](https://github.com/adcontextprotocol/adcp/blob/main/CHARTER.md) にまとめられ、権威ある資料(Bylaws、Membership Agreement、IPR Policy、Antitrust Policy)は [agenticadvertising.org/governance](https://agenticadvertising.org/governance) にあります。 暫定理事会(2026-04-18 時点)には四人の理事がいます: Michael Blum(Scope3)、Brian O'Kelley(Scope3)、Pia Malovrh(Celtra)、Benjamin Masse(Triton Digital)。選出理事会——第1回年次総会は**2026年5月6日**——は、四つの投票クラス(ブランド、エージェンシー、パブリッシャー、テクノロジープロバイダー)にわたる対等な代表を持ち、クラスごとに10議席を目標とします。日々のプロトコル作業は[ワーキンググループ](/docs/community/working-group)で行われ、変更提案はこのリポジトリを通じて流れます。 **リファレンスのセルサイド実装は Prebid にあります。** セルサイド AI エージェントのリファレンスコードの開発は、2026年2月に [Prebid コミュニティ](https://www.prebid.org/)へ引き渡されました([AdExchanger による報道](https://www.adexchanger.com/ad-exchange-news/the-agentic-advertising-organization-hands-development-of-its-sell-side-agent-to-prebid/))。AAO は仕様を所有し、Prebid はリファレンスソフトウェアを所有します。仕様のガバナンスとリファレンス実装の開発は、意図的に別々の組織です。 いいえ。ドキュメント、ストーリーボード、テストベクトルのすべての例は架空のエンティティを使います——Acme Outdoor、Nova Motors、Pinnacle Agency、StreamHaus、その他 `static/compliance/source/universal/fictional-entities.yaml` に登録された名前。実在のブランド、エージェンシー、パブリッシャー、ベンダーは規範的な例には登場しません。この編集ルールは [`CLAUDE.md`](https://github.com/adcontextprotocol/adcp/blob/main/CLAUDE.md) で強制され、[CONTRIBUTING.md](https://github.com/adcontextprotocol/adcp/blob/main/CONTRIBUTING.md) で言及されています。レビュアーは、スキーマにおけるベンダー混入をフラグするのと同じように、実在ブランドの使用をフラグします。これはプロトコルを中立に保つための意図的な選択です: 仕様が特定のセラー、エージェンシー、ベンダーを名指しで優遇すべきではありません。 **四つの暫定理事会議席のうち二つが Scope3 系です。** 暫定理事会には四人の理事がいます: Michael Blum(Scope3)、Brian O'Kelley(Scope3)、Pia Malovrh(Celtra)、Benjamin Masse(Triton Digital)。この構成は Scope3 の AAO へのシード拠出を反映しており、**2026年5月6日**の第1回年次総会で選出理事会へ移行します。選出理事会は、四つの投票クラス(ブランド、エージェンシー、パブリッシャー、テクノロジープロバイダー)にわたる対等な代表——定常状態でクラスごとに10議席——を持ちます。 Scope3 は基盤となる IP と初期資金を拠出しました。具体的には: * **CSBS(Common Sense Brand Standards)**——旧「Scope3 Common Sense」——は AAO に寄贈され、現在は AAO によって統治されています。正式な寄贈と改名は [#2305](https://github.com/adcontextprotocol/adcp/issues/2305) で追跡されています。 * **プロパティレジストリのシードデータ**——AAO プロパティカタログをシードする初期プロパティユニバースと広告インフラ知識グラフは Scope3 によって寄贈されました。 * **シード資金融資**——Scope3 は AAO にシード資金の融資を提供し、会員収益からスケジュールに沿って返済されます。条件は会員向け年次財務報告で開示されます。 Brian は Scope3 と AAO の両方を共同創業し、AdCP のリードアーキテクトを務めています。彼は AAO で執行権限を持たず、Scope3 は他のどの会員とも同等の範囲を超える投票権、拒否権、プロトコル制御の特権を持ちません。この二重の役割のため、彼は Scope3 が直接的な商業的利害を持つ AAO の意思決定——デフォルトのブランドセーフティフレームワークへのあらゆる変更、プロパティカタログのデータガバナンス、シード融資の返済条件を含む——から忌避します。 ガバナンスフレームワークは [CHARTER.md](https://github.com/adcontextprotocol/adcp/blob/main/CHARTER.md) を、権威ある理事名簿・資金開示・忌避ルールは [agenticadvertising.org/governance](https://agenticadvertising.org/governance) を参照してください。 AdCP は今日、エージェント間でベアラートークン認証を使用しています——出荷済みのモデルは[認証](/docs/building/by-layer/L2/authentication)を参照してください。 AdCP 3.1 では、変更を伴う呼び出し(`create_*`、`update_*`、`sync_*`、`activate_*`、`acquire_*`)に対して、RFC 9421 HTTP Signatures または JWS 署名済みボディによるリクエスト署名を規範的要件として追加し、セラーはバイヤーの公開署名鍵に対して検証します。ベアラートークンだけでは変更を伴う呼び出しには不十分になります。[#2307](https://github.com/adcontextprotocol/adcp/issues/2307) で追跡されています。 ガバナンスの決定も署名されるため、セラーや規制当局は `governance_context` トークンが発行元のガバナンスエージェントから確かに来たことを検証できます。[#2306](https://github.com/adcontextprotocol/adcp/issues/2306) で追跡されています。それらが実装されるまで、実装者はベアラー認証を長期的な契約ではなく暫定的な最低ラインとして扱うべきです。 はい。AdCP の `sponsored_intelligence` チャネルは、AI アシスタント、AI 検索エンジン、生成 AI 体験内の広告——スポンサーレスポンス、AI 検索スポンサー結果、生成ディスプレイ、SI Chat Protocol によるブランド体験の引き継ぎを含む——をカバーします。AI プラットフォームとアドネットワークは、他のどのセラーとも同じ方法で AdCP を実装します: [`adagents.json`](/docs/governance/property/adagents) を公開し、`channels: ["sponsored_intelligence"]` で [`get_products`](/docs/media-buy/task-reference/get_products) を実装し、メディアバイを受け付けます。商品モデリング、ワークフロー、測定については [Sponsored Intelligence プロトコル](/docs/sponsored-intelligence/overview)を参照してください。 Sponsored Intelligence は 3.0 の[実験的サーフェス](/docs/reference/experimental-status)(機能 id `sponsored_intelligence.core`)です——それを実装するセラーは `experimental_features` に `sponsored_intelligence.core` を宣言しなければならず(MUST)、バイヤーは SI タスクに依存する前にその宣言を確認すべきです(SHOULD)。このサーフェスは少なくとも6週間の予告のうえで 3.x リリース間に変更される可能性があります。 AdCP は [MCP(Model Context Protocol)](https://modelcontextprotocol.io/)と [A2A(Agent-to-Agent Protocol)](https://google.github.io/A2A/)をトランスポートレイヤーとして使用します。次のように考えてください: * **MCP と A2A** はエージェントがどう通信するか(トランスポート)を定義します * **AdCP** はエージェントが広告について何を言うか(ドメイン)を定義します AdCP タスクはトランスポートに関わらず同一です。[`get_products`](/docs/media-buy/task-reference/get_products) の呼び出しは、MCP と A2A のどちらを経由しても同じリクエストスキーマとレスポンススキーマを持ちます。二つのトランスポートがどう異なるかの詳細は[プロトコル比較](/docs/building/concepts/protocol-comparison)を参照してください。 いいえ。プラットフォーム API(セルフサーブのダッシュボード、管理 API)は AdCP とは異なる目的を果たします。プラットフォーム API は単一プラットフォームの完全で独自の機能セットを公開します。AdCP はプラットフォームをまたいだ一般的な広告操作のための標準化されたインターフェースを提供します。 AdCP を実装するプラットフォームは、既存の API を置き換える必要はありません。AdCP はその隣に位置し、AI エージェントがクロスプラットフォームのワークフローに使える標準インターフェースを提供します。 ## トラスト、アイデンティティ、ガバナンス ガバナンスエージェントは、アドバタイザー/バイヤーが自分のアカウントに設定する外部サービスで、 キャンペーンのアクションを実行前に検証します——予算制限、ブランドセーフティ、規制コンプライアンス。 オーケストレーター(バイ側)とセラーの両方が、それに対して [`check_governance`](/docs/governance/campaign/tasks/check_governance) を呼び出します。これは普遍的な検証ゲートです。キャンペーンガバナンス (`sync_plans`、`check_governance`、`report_plan_outcome`、`get_plan_audit_logs`)は **AdCP 3.0 の実験的** サーフェスです——それを実装するセラーは `experimental_features` に `governance.campaign` を宣言します。 あなたの役割は**検証して履行する**ことです。アカウントが [`sync_governance`](/docs/accounts/tasks/sync_governance) 経由で 同期されるとき、ガバナンスエージェントのエンドポイントと認証情報を受け取り、メディアバイを処理する前に [`check_governance`](/docs/governance/campaign/tasks/check_governance) を呼び出し、判定を尊重し(ガバナンスの決定を 上書きしたり予算を変更したりはできません)、承認どおりに履行します。あなたはガバナンスエージェントに 結果を報告**しません**——オーケストレーターが受諾、コミット済み予算、配信を報告します。 **ガバナンスエージェント**が統合された監査証跡——すべての決定、承認、結果に関する第一級の、構造化され、 タイムスタンプ付きで、帰属可能な記録——を保持し、[`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs) 経由で提供します。セラーはガバナンスの監査ログストアを維持したり引き渡したりする必要は**ありません**。 セラーは `check_governance` 経由で検証して履行し、オーケストレーターが結果を報告します。(セラーが自身の ビジネスのために保持する運用ログは、ガバナンスの監査証跡とは別です。) ガバナンスエージェントのエンドポイントと認証情報は、[`sync_governance`](/docs/accounts/tasks/sync_governance) を通じて セラーにプロビジョニングされます。侵害された認証情報のローテーションや置換は、更新された認証でアカウントを 再同期することで行われます——新しい設定が以前のものを置き換えます。 ガバナンスの承認(`governance_context`)は、セラーが真正性と新しさを検証できるよう、AdCP JWS プロファイルに従って 署名済みトークンとして運ばれる点に注意してください。これは AdCP 3.0 の実験的ガバナンスサーフェスの一部です。 パブリッシャーは、どのセラーエージェントが自分の在庫を販売してよいかを、自身のドメイン上の `/.well-known/adagents.json` ファイルで宣言します(ネットワーク管理プロパティ向けには `ads.txt` の `managerdomain` 委任を使用)。認可はパブリッシャードメインに紐付けられます——セラーの仕事は正確な宣言を 公開することであり、エージェントは照会されたドメイン上のプロパティを付与されることで認可を証明します (`authorized_agents[]` に存在するだけでは不十分です)。解決ルールはプロパティ/認可のドキュメントを参照してください。 ## 認定 私たちの AI ティーチングアシスタント Addie との会話を始めてください。彼女があなたのペースで[インタラクティブなモジュール](/docs/learning/overview)を案内します。 Basics トラックには3つのモジュールがあり、合計約50分です。ほとんどの学習者は数回の集中したセッションで終えます。Practitioner トラックはさらに4つのモジュールを追加します(役割トラックに応じて約90〜105分)。 Basics トラックは無料で誰にでも開かれています。Practitioner と Specialist のトラックには AgenticAdvertising.org のメンバーシップが必要です。 Basics トラックには不要です。Practitioner トラックにはビルドプロジェクトが含まれますが、バイブコーディングを使います——やりたいことを平易な言葉で説明すれば、AI エージェントがコードを書きます。 はい。それが狙いです。Practitioner のビルドプロジェクトは、誰でも——コーディング経験ゼロのマーケティング幹部を含め——AI コーディングアシスタントとの会話を通じて動作する広告エージェントを構築できるよう設計されています。 バイブコーディングとは、やりたいことを平易な言葉で説明し、AI コーディングアシスタントにそれを構築させることです。構文も、事前のプログラミング経験も不要です。変更したいことを説明することで反復します——AI がコードを処理します。認定のビルドプロジェクトでは、動作する広告エージェントをバイブコーディングします。 それは想定内です。2〜3回の反復サイクルは通常のことで、失敗の兆候ではありません。エラーに遭遇したら、それを AI コーディングアシスタントにコピーして戻し、何をしようとしていたかを説明します。Addie はあなたの代わりにデバッグするのではなく、デバッグループを通じてコーチします——プログラムを終えるころには、実際のプロジェクトで AI と反復する方法を身につけているでしょう。 はい。各モジュールには3〜5の必須のデモンストレーション——会話中に必ず行うか説明する具体的な事柄——があります。これらはすべての学習者に同一で、システムによって強制され、スキップできません。Addie は*教え方*をあなたの背景に合わせますが、*基準*は全員に同じです。経験豊富なアドテック幹部も新参者も、同じ中核コンピテンシーを検証します。詳細は[評価の公平性](/docs/learning/instructional-design#assessment-fairness)を参照してください。 AdCP は進化します。プロトコルの更新が認定プロフェッショナルの知るべき内容を変える場合、システムは影響を受ける資格を特定し、保有者に何が変わったかを通知します。再認定はターゲットを絞ったものです——更新がクリエイティブのワークフローに影響してもメディアバイイングに影響しなければ、クリエイティブ関連の資格だけがフラグされます。変わっていない内容をやり直すよう求められることはありません。 Addie を開いて「認定を受けたい」と伝えてください。Basics トラックは無料です——アカウント不要。 ## 参加する 概要は[イントロダクション](/docs/intro)を読み、次にユースケースに合ったドメインを探索してください: * **セルサイドプラットフォーム**: 在庫を公開するには[メディアバイ](/docs/media-buy)から始める * **クリエイティブプラットフォーム**: フォーマットディスカバリーと広告生成を提供するには[クリエイティブ](/docs/creative)から始める * **データプロバイダー**: オーディエンスをエージェントからアドレス可能にするには[シグナル](/docs/signals/overview)から始める * **オーケストレーターとエージェンシー**: 既存の AdCP エージェントに接続するには[インテグレーションガイド](/docs/building)から始める すべてのタスクの JSON スキーマは [adcontextprotocol.org/schemas](https://adcontextprotocol.org/schemas/v3/) で入手できます。 両方です。AgenticAdvertising.org のメンバーシップは個人と企業に開かれています。広告に携わっているなら——トレーダー、メディアプランナー、バイヤー、エージェンシーストラテジスト、その他どの役割であっても——個人会員として参加し、次の恩恵を受けられます: * **認定** — Practitioner と Specialist のトラックは、技術的背景に関わらず、エージェント広告の仕組みと広告エージェントの構築方法を教えます。Basics トラックは無料で誰にでも開かれています。 * **コミュニティ** — 役割や企業を越えて、プログラマティックからエージェントへの同じ移行を進む人々とつながります。 * **ワーキンググループ** — プロトコルの方向性を形作るグループに参加します。プラクティショナーとしてのあなたの運用視点は貴重です——エンジニアだけで作られたプロトコルは、実世界のワークフローのニーズを見落とします。 * **プロフェッショナルの成長** — エージェント広告はまだ初期段階です。今認定を受けることで、業界が AI 主導のワークフローを採用するにつれ、あなたは先行者となります。 参加するのにエンジニアである必要も、企業を代表する必要もありません。個人会員は、学び、貢献し、業界の変化の先を行きたいプラクティショナーのために設計されています。 メンバーシップはコミュニティ、認定、ガバナンスへのアクセスを提供します: * **認定** — Practitioner と Specialist の資格トラック。動作する広告エージェントを作る実践的なビルドプロジェクトを含みます * **ワーキンググループ** — プロトコルの方向性を形作り、提案された変更に投票するグループに参加します * **メンバーディレクトリ** — 組織の能力を掲載し、パートナーやベンダーとして見つけてもらえます * **コミュニティ** — 業界全体の実装者、プラクティショナー、意思決定者とつながります AdCP の実装や無料の Basics 認定トラックの修了にメンバーシップは不要です。参加方法は[ワーキンググループ](/docs/community/working-group)ページを参照してください。 はい。プロトコルはオープンに開発されています。次のことができます: * [GitHub](https://github.com/adcontextprotocol/adcp/issues) で issue や機能リクエストを提出する * コミュニティ Slack に参加して質問し、実装について議論する * バグ修正やドキュメント改善のプルリクエストを送る * 自分の AdCP 実装を構築して公開する メンバーシップは、より深く関わりたい個人と組織のためのものです——認定、ワーキンググループ、プロトコルガバナンスへの正式な影響力。 実装ガイド、SDK、インテグレーションパターン。 # 監査証跡: 内部ビュー対共有可能ビュー Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/campaign/audit-trail AdCP のキャンペーンガバナンス監査証跡が完全な内部ビューとスコープされた共有可能ビューにどう分かれるか、各相手方が安全に見られるもの。 プロトコルは、バイヤーが相手方と何を共有しなければならないかを義務付けません。しかし同じ監査証跡の 2 つの異なるビューを生成するプリミティブを与えます: * **内部ビュー** — すべてのチェック、発見、予算移動、人間レビューのバイヤーの完全な記録。自己防衛、規制当局監査、内部コンプライアンスレビューに使われる。 * **共有可能ビュー** — 1 つの相手方(セラー、エージェンシー、第三者監査人)に宛てられたスコープされたサブセット。その当事者が自身のアクションを検証するために必要なものだけを明かす。 両方とも [`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs) から生成されます。分割はフィルタリングとフィールド選択の問題であり、別のプロトコルではありません。 ## そもそもなぜ分割するか バイヤーが異なるビューを維持する 3 つの理由: 1. **戦略リーク。** すべてのセラー全体の合計承認予算、チャネル配分、残ヘッドルームは競争シグナルです。派生した比率は生の金額より安全ではないことに注意 — `budget.utilization_pct` は、セラーが自身のコミット済みシェアを知っている場合、1 ステップの逆問題です。単一のセラーは自身のバイを検証するためにバイヤーの完全なプランを知る必要はありません。 2. **相手方間分離。** セラー A はセラー B の発見、ガバナンスコンテキスト、結果を見る必要はありません。共有可能ビューは常にリクエスト当事者の `governance_context` 値にスコープされます。 3. **内部レビューフィールド。** 人間レビュー理由、ドリフトメトリック、監督しきい値はバイヤー自身のガバナンス姿勢です。それらは規制当局監査でバイヤーを保護します。それらは相手方に宛てられていません。 ## フィールドタグ付け 以下のテーブルは、[`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs#response) レスポンスのすべてのフィールドを、誰が安全に見られるかで分類します。 | Field | Internal | 特定セラーと共有可能 | 規制当局/監査人と共有可能 | | ------------------------------------------------------- | :------: | :--------------: | :------------------------------------------------------: | | `plans[].plan_id`, `plan_version` | yes | yes(スコープ時) | yes | | `plans[].status` | yes | yes(スコープ時) | yes | | `plans[].budget.authorized` | yes | no | yes | | `plans[].budget.committed` | yes | no | yes | | `plans[].budget.remaining` | yes | no | yes | | `plans[].budget.utilization_pct` | yes | no | yes | | `plans[].channel_allocation.*` | yes | no | yes | | `plans[].governed_actions[].governance_context` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].governed_actions[].purchase_type` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].governed_actions[].status` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].governed_actions[].committed` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].governed_actions[].check_count` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].summary.checks_performed`, `outcomes_reported` | yes | no | yes | | `plans[].summary.statuses` | yes | no | yes | | `plans[].summary.findings_count` | yes | no | yes | | `plans[].summary.human_reviews[]` | yes | no | 理由を編集除去。リクエスト時に解決 + タイムスタンプを共有 | | `plans[].summary.drift_metrics.*` | yes | no | バイヤーの相手方ルールが開示を要求する場合に利用可能 | | `plans[].entries[].id`, `type`, `timestamp` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].entries[].caller` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].entries[].tool` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].entries[].check_type`, `verdict` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].entries[].mode` | yes | yes(自身のコンテキストのみ) | yes — `audit` 下で `denied` を `approved`-with-finding から区別 | | `plans[].entries[].explanation` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].entries[].categories_evaluated` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].entries[].policies_evaluated` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].entries[].findings[]` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].entries[].plan_hash` | yes | yes(自身のコンテキストのみ) | yes — 監査人がこれを検証 | | `plans[].entries[].governance_context` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].entries[].purchase_type` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].entries[].outcome`, `outcome_status` | yes | yes(自身のコンテキストのみ) | yes | | `plans[].entries[].committed_budget` | yes | yes(自身のコンテキストのみ) | yes | ## 共有可能ビューの生成 「セラー X と何を共有すべきか?」に答えるには、監査クエリをそのセラーに属するコンテキストにフィルターし、プランレベルの集計を落とします。 **1 つのセラーのアクションにスコープされたリクエスト:** ```json theme={null} { "tool": "get_plan_audit_logs", "arguments": { "plan_ids": ["plan_q1_2026_launch"], "governance_contexts": ["gc_mb_seller_456"], "include_entries": true } } ``` これは `governed_actions` と `entries` をリクエストされたコンテキストに絞ります。バイヤーは依然として転送前に `budget.*`、`channel_allocation.*`、`summary.findings_count`、`summary.statuses`、`summary.drift_metrics` を除去する責任があります — それらのサマリーはプラン全体をカバーし、他のセラー全体の合計を明かします。 **最小コンプライアンス証明(エントリーなし):** バイが統制されたことの証明のみを必要とする相手方には — 完全な証跡ではなく — 監査レスポンスから導出された 4 フィールドの証明を共有します: ```json theme={null} { "governance_context": "gc_mb_seller_456", "verdict": "approved", "plan_hash": "oR0jFDEtzcwgPbNf-Ofd_fZHYfAyD1TRbzGOFBVCG-c", "policies_evaluated": ["us_coppa", "alcohol_advertising"] } ``` セラーは `plan_hash` を彼らが署名したプランリビジョンに対して検証でき、`policies_evaluated` リストはどのレジストリポリシーが適用されたかを確認します。バイヤーのポートフォリオについて他は何も開示されません。 ## 規制当局と監査人のビュー 規制当局は通常 3 つのうちの 1 つを求めます: 1. **必要な場所で人間の監督が起こったか?** 解決されたポリシーからの `summary.human_reviews[]` と `requires_human_review` フラグを共有。理由は編集除去または要約されてもよい。解決とタイムスタンプは無傷であるべき。 2. **バイは特定の規制に準拠したか?** 規制の `policy_id`(例: `us_coppa`)を含む `policies_evaluated` でスコープされたエントリーを共有。規制当局がプランリビジョンに対して検証できるよう `plan_hash` を含める。 3. **監督メトリックはしきい値内か?** `summary.drift_metrics` とポリシー導出のしきい値を共有。`human_review_rate_min` を下回る `human_review_rate` は説明する価値のあるシグナル。 プロトコルは規制当局 API を定義しません。相手方ルールが開示を統制します。監査証跡が開示を可能にします。 ## 実例: クリーンなバイ OLV/ディスプレイキャンペーンに $500K 承認されたプラン。オーケストレーターは `get_products` で意図チェックを実行し、次に $150K の `create_media_buy` で実行チェック、次に結果を報告します。完全な内部監査レスポンス: ```json theme={null} { "$schema": "/schemas/governance/get-plan-audit-logs-response.json", "status": "completed", "plans": [ { "plan_id": "plan_q1_2027_acme", "plan_version": 1, "status": "active", "budget": { "authorized": 500000, "committed": 150000, "remaining": 350000, "utilization_pct": 30 }, "governed_actions": [ { "governance_context": "11ab64d0-2e20-4b62-8964-b024cfc98d36", "purchase_type": "media_buy", "status": "active", "committed": 150000, "check_count": 1 } ], "summary": { "checks_performed": 2, "outcomes_reported": 1, "statuses": { "approved": 2, "denied": 0, "conditions": 0 }, "findings_count": 0 }, "entries": [ { "id": "chk_378be2f1", "type": "check", "timestamp": "2027-01-15T14:32:41.008Z", "caller": "https://ads.seller-a.example", "tool": "create_media_buy", "check_type": "execution", "mode": "enforce", "purchase_type": "media_buy", "governance_context": "11ab64d0-2e20-4b62-8964-b024cfc98d36", "verdict": "approved", "explanation": "All governance checks passed.", "policies_evaluated": [], "categories_evaluated": ["delegation_authority"], "findings": [] }, { "id": "out_9b2c1f04", "type": "outcome", "timestamp": "2027-01-15T14:32:41.105Z", "outcome": "completed", "committed_budget": 150000, "purchase_type": "media_buy", "governance_context": "11ab64d0-2e20-4b62-8964-b024cfc98d36" } ] } ] } ``` 同じ証跡のセラーの共有可能ビューは、`governed_actions[]` エントリーと `governance_context = 11ab64d0...` にフィルターされた `entries[]` です。プランレベルの `budget.authorized`、`budget.remaining`、`summary` 集計はバイヤー側に留まります。 ## 違反がどう見えるか 発見は監査証跡がリスクを伝える方法です。知る価値のある 2 つのパターン: ### セキュリティ形状の発見: 認可されていないセラー プランは `approved_sellers: ["https://ads.seller-approved.example"]` を宣言します。別のセラーが `check_governance` を呼び拒否されます: ```json theme={null} { "$schema": "/schemas/governance/check-governance-response.json", "check_id": "chk_0ac8d7de", "plan_id": "plan_2027_apex_athletic", "verdict": "denied", "explanation": "Denied: Caller https://ads.seller-rogue.example is not in the plan's approved sellers list.", "categories_evaluated": ["delegation_authority", "seller_compliance"], "policies_evaluated": [], "findings": [ { "category_id": "seller_compliance", "severity": "critical", "explanation": "Caller https://ads.seller-rogue.example is not in the plan's approved sellers list." } ] } ``` 対応するプランサマリーは `statuses.denied: 1` と `findings_count: 1` を記録します。認可されていないセラーの `governance_context` は依然として記録されます — 証跡は成功だけでなく試みを捕捉します。 ### コーチング形状の発見: Annex III 前提条件が欠けている `policy_categories: ["fair_lending"]`(住宅ローン、消費者信用など)を持つプランは `human_review_required: true` を自動反転します。ブランドプロフィールが異議申立連絡先を公開しない場合、チェックは実行可能な説明とともにフェイルクローズします: ```json theme={null} { "$schema": "/schemas/governance/check-governance-response.json", "check_id": "chk_8360a36d", "plan_id": "plan_2027_nova_mortgage", "verdict": "denied", "explanation": "Denied: Plan requires human review (Annex III / Art 22) but brand does not expose data_subject_contestation. Art 22(3) requires a discoverable contact point for the data subject to request human intervention, express their view, and contest the decision. Set brand.data_subject_contestation in brand.json.", "categories_evaluated": ["data_subject_contestation", "delegation_authority"], "policies_evaluated": [], "findings": [ { "category_id": "data_subject_contestation", "severity": "critical", "explanation": "Plan requires human review (Annex III / Art 22) but brand does not expose data_subject_contestation. Set brand.data_subject_contestation in brand.json." } ] } ``` 拒否はまたコーチングの瞬間です — オペレーターは何を修正すべきかを正確に見ます。これは良いガバナンス発見が取るべき形状です: カテゴリー、重大度、前進の道。 ## オペレーターのダイヤル: enforce / advisory / audit `mode` は [`sync_plans`](/docs/governance/campaign/tasks/sync_plans) 経由でプランに設定され、オペレーターの主要なレバーです。同じ呼び出し元、同じペイロード、同じ発見が、モードに応じて 3 つの異なる結果を生成します。 **`mode: "enforce"`** — バイはガバナンス層でブロックされます: ```json theme={null} { "$schema": "/schemas/governance/check-governance-response.json", "check_id": "chk_enforce_001", "plan_id": "plan_mode_enforce", "verdict": "denied", "explanation": "Denied: Caller https://ads.seller-rogue.example is not in the plan's approved sellers list.", "categories_evaluated": ["delegation_authority", "seller_compliance"], "policies_evaluated": [], "findings": [ { "category_id": "seller_compliance", "severity": "critical", "explanation": "Caller https://ads.seller-rogue.example is not in the plan's approved sellers list." } ] } ``` **`mode: "advisory"`** — バイは進行します。オペレーターは事後に対処する重大な発見を得ます: ```json theme={null} { "$schema": "/schemas/governance/check-governance-response.json", "check_id": "chk_advisory_001", "plan_id": "plan_mode_advisory", "verdict": "approved", "explanation": "Approved with 1 advisory finding(s).", "expires_at": "2027-01-15T15:32:41Z", "categories_evaluated": ["delegation_authority", "seller_compliance"], "policies_evaluated": [], "findings": [ { "category_id": "seller_compliance", "severity": "critical", "explanation": "Caller https://ads.seller-rogue.example is not in the plan's approved sellers list." } ] } ``` **`mode: "audit"`** — バイは黙って進行します。発見は遡及的分析のためログにあります: ```json theme={null} { "$schema": "/schemas/governance/check-governance-response.json", "check_id": "chk_audit_001", "plan_id": "plan_mode_audit", "verdict": "approved", "explanation": "All governance checks passed.", "expires_at": "2027-01-15T15:32:41Z", "categories_evaluated": ["delegation_authority", "seller_compliance"], "policies_evaluated": [], "findings": [ { "category_id": "seller_compliance", "severity": "critical", "explanation": "Caller https://ads.seller-rogue.example is not in the plan's approved sellers list." } ] } ``` トレードオフ: | Mode | Velocity | Safety | When to use | | ---------- | ------------------- | ---------------- | ------------------------------ | | `enforce` | 最も遅い — チェックがバイをブロック | 最高 — 違反は出荷できない | 規制されたブランドの本番。新しいオペレーターのデフォルト | | `advisory` | フルベロシティ | 発見は表面化するがブロックしない | 新しいポリシーの展開。締める前の動作観察 | | `audit` | フルベロシティ | 発見は黙ってログされる | バックストップテレメトリー。決して唯一のガバナンス姿勢でない | プランはリビジョン間でモードをシフトできます — 監査証跡のガバナンス決定は、各履歴バイがどう統制されたかについて真実を伝えます。運用モードはすべての監査エントリーで可視であるべきです。今日それは `check_governance` ごとのレスポンスに現れますが、`get_plan_audit_logs` レスポンスには一貫して現れません([#3156](https://github.com/adcontextprotocol/adcp/issues/3156))。 ## プロトコルが保証するもの 共有可能ビューを設計するとき依存できる 2 つの不変条件: * **`plan_hash` は検証可能。** それはチェックが対して評価されたプランリビジョンにわたる `base64url_no_pad(SHA-256(JCS(plan_payload)))` です。プランリビジョンを持つ任意の当事者はダイジェストを再計算しバイト比較できます。[プランバインディングと監査](/docs/governance/campaign/specification#plan-binding-and-audit) を参照。 * **インラインポリシーはレジストリポリシーを緩和できない。** バイヤーのカスタム `policy` エントリーはレジストリソースのポリシーの上に制限を追加することのみできます。したがって共有可能ビューが `policies_evaluated: ["us_coppa"]` を表示するとき、相手方はレジストリバージョンの `us_coppa` が宣言された `enforcement` レベルで適用されたことを信頼できます — バイヤーは黙ってそれをダウングレードしませんでした。[ポリシーレジストリ](/docs/governance/policy-registry#policy-categories) を参照。 ## 関連 * [`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs) — リクエストとレスポンススキーマ * [キャンペーンガバナンス仕様](/docs/governance/campaign/specification) — プランバインディング、ドリフト検出、ガバナンスコンテキストライフサイクル * [ポリシーレジストリ](/docs/governance/policy-registry) — ポリシーバージョニング、`effective_date`、レジストリ対インラインポリシー * [Annex III と Art 22 の義務](/docs/governance/annex-iii-obligations) — 人間レビューがいつ必要か # バイヤーとセラーの責任 Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/campaign/responsibilities AdCP キャンペーンガバナンスで、バイヤー側オーケストレーター、セラー、ガバナンスエージェントがどう責任を共有するか。 # ガバナンスの責任: バイヤー対セラー キャンペーンガバナンスは、トランザクションの 2 つの異なる位置から同じ `check_governance` タスクを使います。バイヤー側オーケストレーターはアクションを送る前に意図をチェックします。セラーはコミットまたは配信変更の前に実行をチェックします。両方のチェックはバイヤーの構成されたガバナンスエージェントに行きますが、異なる証拠を運びます。 ## ロール | Role | Responsibility | | -------------- | ----------------------------------------------------------------------------------- | | バイヤー側オーケストレーター | プランを作成または更新し、意図されたアクションをチェックし、承認されたリクエストをセラーに送り、結果をガバナンスエージェントに報告する。 | | セラー | バイヤーが有効なガバナンスコンテキストを供給したことを検証し、実行前に計画された配信をチェックし、ガバナンスによって欠けているか拒否された統制アクションを拒否する。 | | ガバナンスエージェント | プランとポリシールールを適用し、`approved`、`conditions`、または `denied` を返し、ガバナンスコンテキストに署名し、監査状態を記録する。 | ## セットアップ: セラーをガバナンスエージェントにバインドする セラーがセラー側チェックを行える前に、バイヤーアカウントは [`sync_governance`](/docs/accounts/tasks/sync_governance) 経由でガバナンスエージェントエンドポイントをセラーと同期しなければなりません。これはアカウントセットアップであり、バイごとの交渉ではありません。 セラーは、アカウントの構成されたガバナンスエージェントエンドポイントと認証情報を保存します。後で、統制リクエストが到着したとき、セラーはどこで `check_governance` を呼ぶかを知ります。 ## バイヤー側の意図チェック バイヤー側オーケストレーターは、統制プランの支出コミットアクションごとの前に [`check_governance`](/docs/governance/campaign/tasks/check_governance) を呼びます。 意図チェックは以下を送ります: | Field | Required | Purpose | | -------------------- | -------- | --------------------------------------------------------------------------------------------------------- | | `plan_id` | Yes | 強制されるキャンペーンプランを識別。 | | `caller` | Yes | ガバナンスチェックを行うバイヤー側オーケストレーターを識別。 | | `tool` | Yes | オーケストレーターが実行しようとするアクション(`create_media_buy`、`update_media_buy`、`activate_signal`、`build_creative` など)を名指す。 | | `payload` | Yes | オーケストレーターが送ろうとする正確なリクエストボディを運ぶ。 | | `governance_context` | 後の呼び出し | 最初の承認済みチェックの後、ライフサイクル連続性を維持。 | ガバナンスエージェントが `approved` または `conditions` を返す場合、`governance_context` トークンを返します。オーケストレーターはそのトークンをセラーに送るリクエストに添付します。レスポンスが `conditions` の場合、オーケストレーターは進む前に条件を適用して再チェックしなければなりません。 ## セラー側の実行チェック セラーが統制された支出コミットリクエストを受け取ると、アクションを確認する前に独立した実行チェックを行います。 セラー側チェックは以下を送ります: | Field | Required | Purpose | | -------------------- | --------------- | --------------------------------------------------------------------------------- | | `plan_id` | Yes | 強制されるキャンペーンプランを識別。 | | `caller` | Yes | 実行チェックを行うセラーを識別。 | | `governance_context` | Yes | バイヤー側意図チェックからの署名付きコンテキストトークン。 | | `planned_delivery` | Yes | セラーの実際の計画された実行: 予算、日付、チャネル、地理、プレースメント、ペーシング、その他の配信パラメーター。 | | `phase` | ライフサイクル明確化に Yes | これが購入、変更、配信チェックのいずれかを示す。 | | `delivery_metrics` | 配信フェーズ | `phase` が `delivery` のとき必須。ペーシング、支出、地理、チャネル、オーディエンスドリフトチェックのため実際の配信パフォーマンスデータを運ぶ。 | セラーはバイヤーの意図チェックをそれ自体で十分と扱ってはなりません。セラーは実際に配信するものをチェックします。それは在庫可用性、セラーのデフォルト、実装制約のためバイヤーのリクエストと異なりうる。 ガバナンスエージェントが実行チェックを拒否する場合、セラーは進んではなりません。ガバナンスエージェントが条件を返す場合、セラーは計画された配信を調整し確認前に再チェックしなければなりません。 ## 結果報告 セラーが応答した後、バイヤー側オーケストレーターは [`report_plan_outcome`](/docs/governance/campaign/tasks/report_plan_outcome) を呼びます。これはガバナンスエージェントが承認されたアクションをセラーの実際の応答と照合し、確認された結果から予算状態を更新できるようにします。 結果報告は、ガバナンスエージェントが試みられたアクションをコミットされた支出として数えることを防ぐものです。ガバナンスエージェントは実際に起こった状態を追跡します。 ## よくある間違い | Mistake | Correct behavior | | ---------------------------------------------------- | -------------------------------------------------- | | セラーが後でチェックするからバイヤーが `check_governance` をスキップ | バイヤーはまず意図チェックを行い、有効なガバナンスコンテキストを添付できるようにしなければならない。 | | セラーが統制アカウントでガバナンスコンテキストなしのリクエストを受け入れる | セラーは実行前にリクエストを拒否する。 | | セラーが `planned_delivery` の代わりにバイヤーのリクエストされたペイロードをチェック | セラーは実際に実行する配信をチェックする。 | | オーケストレーターが `conditions` を承認として扱う | オーケストレーターまたはセラーが条件を適用し `check_governance` を再度呼ぶ。 | | オーケストレーターが `report_plan_outcome` を省略 | ガバナンス予算と監査状態が実際に起こったことからドリフトする。 | ## 最小シーケンス 1. バイヤーが `sync_governance` でアカウントにガバナンスを構成。 2. バイヤーが `sync_plans` でプランを作成または更新。 3. バイヤーが `tool` と `payload` で `check_governance` を呼ぶ。 4. バイヤーが `governance_context` でセラーリクエストを送る。 5. セラーがコンテキストを検証し `plan_id`、`caller`、`planned_delivery` で `check_governance` を呼ぶ。 6. セラーがガバナンス判定に基づいて確認、拒否、または調整。 7. バイヤーがセラー結果で `report_plan_outcome` を呼ぶ。 リクエストとレスポンスフィールドについては [`check_governance` タスクリファレンス](/docs/governance/campaign/tasks/check_governance) を、ライフサイクルとトークン検証詳細については [キャンペーンガバナンス仕様](/docs/governance/campaign/specification) を参照してください。 # Specification Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/campaign/specification AdCP キャンペーンガバナンスの正式仕様 — プランスキーマ、予算権限モデル、検証ロジック、統合パターン。 # Campaign Governance specification **実験的機能。** キャンペーンガバナンスは、実験的サーフェスとして AdCP 3.0 の一部です — 少なくとも 6 週間の予告をもって 3.x リリース間で変更される可能性があります。これを実装するセラーは `experimental_features` で `governance.campaign` を宣言しなければなりません(MUST)。完全なコントラクトについては [実験的ステータス](/docs/reference/experimental-status) を参照。 **Status**: Request for Comments **Last Updated**: March 2026 本ドキュメント内のキーワード "MUST"、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、"SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"MAY"、"OPTIONAL" は、[RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) に記載のとおりに解釈されます。 本ドキュメントは、キャンペーンガバナンスのデータモデル、検証ロジック、統合パターンを定義します。 ## Campaign plan キャンペーンプランは、すべての検証における真実の源泉です。プランは [`sync_plans`](/docs/governance/campaign/tasks/sync_plans) を通じてガバナンスエージェントにプッシュされ、キャンペーンのプランパラメーター — 予算制限、チャンネル、フライト日程、プラン市場 — を定義します。ガバナンスエージェントはブランドのコンプライアンス設定から適用可能なポリシーを解決します。プランは `policy_ids` でレジストリポリシーを直接参照したり、`custom_policies` でキャンペーン固有のルールを含めたりすることもできます。 ```json theme={null} { "plan_id": "plan_q1_2026_launch", "brand": { "domain": "acmecorp.com" }, "objectives": "Drive awareness for spring product launch among 25-54 adults in the US, focusing on premium video and high-impact display.", "budget": { "total": 500000, "currency": "USD", "reallocation_threshold": 25000, "per_seller_max_pct": 40 }, "channels": { "required": ["olv"], "allowed": ["olv", "display", "ctv", "audio"], "mix_targets": { "olv": { "min_pct": 40, "max_pct": 70 }, "display": { "min_pct": 10, "max_pct": 30 }, "ctv": { "min_pct": 0, "max_pct": 20 }, "audio": { "min_pct": 0, "max_pct": 10 } } }, "flight": { "start": "2026-03-15T00:00:00Z", "end": "2026-06-15T00:00:00Z" }, "countries": ["US"], "policy_ids": ["us_coppa", "alcohol_advertising"], "custom_policies": [ { "policy_id": "no_competitor_adjacency", "enforcement": "must", "policy": "No advertising adjacent to competitor brand content." } ], "approved_sellers": null, "ext": {} } ``` ### Purchase types ガバナンスプランは、メディアバイだけでなくすべての金銭的コミットメントを管理します。`check_governance` の `purchase_type` フィールドが、どの種類のコミットメントが検証されているかを識別します。 | Purchase type | Tool | What's governed | | ------------------- | ------------------------------------- | --------------- | | `media_buy`(デフォルト) | `create_media_buy`、`update_media_buy` | メディアインベントリの購入 | | `rights_license` | `acquire_rights`、`update_rights` | ブランド権利ライセンス料 | | `signal_activation` | `activate_signal` | データシグナル有効化料 | | `creative_services` | `build_creative` | クリエイティブ生成料 | すべての購入タイプは同じガバナンスループを共有します: `sync_plans` → `check_governance` → 実行 → `report_plan_outcome`。ガバナンスエージェントは、すべてのタイプにわたって予算権限、ジオコンプライアンス、フライトコンプライアンスを検証します。メディアバイ固有の検証(チャンネルコンプライアンス、セラー集中度、配信ペーシング)は、`purchase_type` が `media_buy` の場合、またはペイロードが関連フィールドを含む場合にのみ適用されます。 `purchase_type` が省略された場合、ガバナンスエージェントは `media_buy` を想定します。 **将来の購入タイプ**: コンテンツ標準、プロパティリストのキュレーション、測定/検証サービス(ブランドリフト調査、ビューアビリティ、フラウド検出)はすべて、スキーマに `pricing_options` を運び、`report_usage` を通じて課金します。これらのサービスは現在、バイヤーがサービスにコミットする明示的な有効化ツールを欠いています — 課金関係は暗黙的です。プロトコルがこれらのサービスの有効化サーフェスを追加する際、コミットメント時点でのガバナンスチェックを可能にするために、対応する購入タイプが追加されます。 ### Budget reallocation `budget.reallocation_threshold`(必須の数値)は、予算再配分の自律性を管理します。これは、データ主体に影響する決定の必須の人間によるレビューをカバーしません — それについてはプランレベルの `human_review_required` フィールドを参照。 | Value | Meaning | | ---------------------------- | --------------------------------------------------------- | | `0` | すべての再配分に人間の承認が必要 | | `budget.total` 未満の正の数 | エージェントはこの金額までエスカレーションなしに再配分できる。より大きい変更は人間のレビューにエスカレーションする | | `budget.total` に等しい(またはそれ以上) | エージェントはプランの総予算内で自由に再配分できる | ### Budget allocations プランは任意で、`allocations` を使って総予算を購入タイプ間で分割できます。 ```json theme={null} { "budget": { "total": 500000, "currency": "USD", "reallocation_threshold": 25000, "allocations": { "media_buy": { "amount": 400000 }, "rights_license": { "amount": 75000 }, "signal_activation": { "amount": 25000 } } } } ``` `allocations` が存在する場合、ガバナンスエージェントはタイプごとの割り当てと全体の総額の両方に対して支出を検証します。存在しない場合、購入タイプに関係なくすべての支出が単一の総額に対してカウントされます。割り当てはガードレールであり、ハードな分割ではありません — 割り当ての合計は総額と異なってもかまいません(MAY)。 `allocations` が存在するが購入タイプがリストされていない場合(例: `media_buy` と `rights_license` のみを割り当てるプランに対して `signal_activation` が試みられる)、ガバナンスエージェントはアクションをプランの総予算のみに対して検証します。リストされていないタイプは拒否されません — 共有プールから引き出します。支出をリストされたタイプのみに制限するには、明示的な制約を持つ `custom_policies` を設定します。 ### Human review required `human_review_required` は、予算再配分の自律性とは独立に、プラン上のすべてのアクションの人間による監督を義務付けるプランレベルのブール値(デフォルト `false`)です。 ガバナンスエージェントは、プラン上の解決されたポリシーまたは policy\_category が `requires_human_review: true` を運ぶ場合、`human_review_required: true` を自動的に設定します。これには、`fair_housing`、`fair_lending`、`fair_employment`、`pharmaceutical_advertising` などの規制業種、および附属書 III のユースケースにおける決定をカバーする `eu_ai_act_annex_iii` ポリシーが含まれます。 `human_review_required` が true の場合、ガバナンスエージェントは — プランの `reallocation_threshold` に関係なく — プラン上のあらゆるアクションを実行前に人間のレビューにエスカレーションしなければなりません(MUST)。プランが `human_review_required: true` を運ぶとき、寛容な再配分しきい値は人間のレビューをバイパスしません。2 つの次元は合成されます。 このフィールドは `budget.reallocation_threshold` とは異なります。 | Field | Scope | Purpose | | ------------------------------- | ----- | --------------------------------------------------------- | | `budget.reallocation_threshold` | 運用面 | プラン内での予算再配分に対するエージェントの自律性を制御 | | `human_review_required` | 規制面 | 個人に影響する決定(例: GDPR 第22条、EU AI Act 附属書 III)の人間によるレビューを義務付ける | 呼び出し元は、トリガーとなるポリシーが存在しない場合でも、プラン上で `human_review_required: true` を明示的に設定してもかまいません(MAY)。呼び出し元は、人間のレビューを要求するポリシーをオーバーライドするために `false` に設定してはなりません(MUST NOT)— ガバナンスエージェントはすべての同期でこのフラグを解決されたポリシーから再評価し、トリガーとなるポリシーが存在する場合、呼び出し元が提供した `false` をオーバーライドします。 ### Spend-commit invocation バイヤー側のガバナンス呼び出しは、アドバイザリではなく強制可能です。プランにガバナンスエージェントが設定されている場合、バイヤーエージェントは、セラーに支出コミットリクエストを送信する前に [`check_governance`](/docs/governance/campaign/tasks/check_governance) を呼び出さなければならず(MUST)— 例外なく — そのリクエストに添付する**意図フェーズ**の `governance_context` トークンを生成します。ガバナンスエージェントは、プランの `budget.reallocation_threshold` と `human_review_required` フィールドに従って、自動承認、条件適用、拒否、または人間のレビューへのエスカレーションを内部的に決定します。呼び出しルールにドル金額、ベースライン計算、オペレーター宣言の下限は現れません — それらの自動承認の高速パスは、呼び出すかどうかというバイヤーの決定ではなく、ガバナンスエージェント自身のポリシーの内部に属します。 #### Spend-commit tasks 呼び出しの MUST は、リクエストの時点で金銭的義務を付与するすべての AdCP タスクに適用されます。 * [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) — パッケージ全体のコミット済み予算 * [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) — 増分コミットデルタ(新予算 − 以前にコミットされた額) * [`acquire_rights`](/docs/brand-protocol/tasks/acquire_rights) — 権利価格 * [`update_rights`](/docs/brand-protocol/tasks/update_rights) — 増分コミットデルタ * [`activate_signal`](/docs/signals/tasks/activate_signal) — 有効化料 * [`build_creative`](/docs/creative/task-reference/build_creative) — クリエイティブ生成料 * 将来の支出コミットタスク 呼び出しは、ディスカバリータスク(例: `get_products`、`get_signals`)、レポートタスク(例: `get_media_buy_delivery`)、または運用ステータスタスクには必須ではありません。MUST は金銭的義務の時点で特に発火します。 プランにガバナンスエージェントが設定されていない場合、`check_governance` の呼び出しは必須でも意味もありません — 呼び出す対象がありません。セラーは、独自の商業ポリシーの問題として、ガバナンスエージェントが設定されていないプランでの取引を拒否してもかまいません(MAY。エンタープライズセラーは通常拒否します)。プロトコルはガバナンスエージェントを義務付けず、ブランドが公開する設定が、セラーが 1 つ存在するかどうかを発見する手段です。 同じプランを複数のセラーにファンアウトするオーケストレーターは、`aud` がターゲットセラーにバイト単位でバインドされるため、セラーごとに 1 つの意図トークンを生成します。これは正しいプロトコル形状ですが、ガバナンスエージェントがプラン上のバイヤーの完全な買い物リストを見ることを意味します。買い物の意図を商業的に機密と見なすオペレーターは、データ処理の姿勢を信頼できるガバナンスエージェントを選ぶべきです(SHOULD)。 #### Seller enforcement ガバナンスエージェントが設定されたプランの支出コミットリクエストを受け取ったセラーは、リクエスト上で有効で期限内の**意図フェーズ**の `governance_context` トークンを要求しなければならず(MUST)、[セラー検証チェックリスト](/docs/building/by-layer/L1/security#セラー検証チェックリスト)に従って検証します。トークンは `phase: "intent"` を運び、(`sub` を通じて)リクエストの `plan_id` に一致し、(`aud`)このセラー宛てでなければなりません。トークンのないリクエスト、検証に失敗するトークンを持つリクエスト、または別のプラン・別のセラー・非意図フェーズ向けに発行されたトークンを持つリクエストは、`PERMISSION_DENIED` で拒否しなければなりません(MUST)。次にセラーは、`planned_delivery` と受け取った `governance_context` を伴って `check_governance` を呼び出すことで自身の実行チェックを実行します。その呼び出しは、メディアバイライフサイクルの残りに使われる `purchase` フェーズのトークン(セラーが割り当てた `media_buy_id` にバインド)を生成します。この 2 段階のフローが、バイヤー側の MUST を実効化します。`check_governance` をスキップするバイヤーは有効な意図トークンを生成できず、支出コミットはセラーが実行チェックに到達する前に拒否されます。 セラーは、受け入れた意図トークンとその後保持するライフサイクルトークンを、最低限 `jti` をキーとして、`iss`、`aud`、`sub`(plan\_id)、`phase`、決定結果、受け入れのタイムスタンプとともに永続化しなければなりません(MUST)。保持はセラーの規制上の保持期間に従います。セラー側の保持がなければ、監査ログはガバナンスエージェントからの単一ソースになります。セラー記録と `get_plan_audit_logs` の間の独立した照合が、侵害されたまたは不正なガバナンスエージェントを捕捉するクロスチェックです。 セラー側ガバナンス(セラー自身がアカウントにガバナンスエージェントを設定している場合)は**独立した層**です。バイヤーの成功した `check_governance` は、セラーにリクエストの受け入れを義務付けません。セラー自身のコンプライアンスポリシーは、依然として `PERMISSION_DENIED` を通じてアクションを拒否してもかまいません(MAY)。 承認されたトークンは、検証時点で権威を持つ `exp` を運びます。トークンの有効ウィンドウ内でのポリシー変更は、あらゆる署名決定システムの受容される残余リスクです。厳しい `exp` 値(意図トークンは JWS プロファイルに従い 15 分以内に期限切れになるべき(SHOULD))は、ウィンドウを閉じるのではなく制限します。ウィンドウを許容できないオペレーターは、キャッシュに関係なくすべてのアクションが内部の人間によるレビューを通るよう、`reallocation_threshold` を `0` に、または `human_review_required: true` に設定しなければなりません(MUST)。 #### Audit logging すべての `check_governance` 呼び出しは、[`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs) を通じて取得可能な監査ログエントリを生成しなければならず(MUST)、次を捕捉します。 1. タイムゾーンオフセット付き ISO 8601 文字列としての呼び出しタイムスタンプ 2. 検証されるツール(`create_media_buy`、`acquire_rights` など)とプランの通貨でのコミット額 3. 結果(`approved`、`denied`、`conditions`。および人間のレビューが内部的に呼び出されたかどうか) 4. 人間のシグナルが記録された場合の人間のアクターの識別と権限 5. ダウンストリームの支出コミットタスクの監査エントリおよび `report_plan_outcome` からのクロスリファレンス用の `check_id` バイヤー側の意図チェックとセラー側の実行チェックはそれぞれ別個の `check_id` を生成します。`report_plan_outcome` は単一のチェック識別子ではなく `plan_id` を通じて相関させます。支出コミットを再構築する監査人は、バイヤー側エントリ、セラー側エントリ、セラーが永続化したトークン記録を照合します。 #### Interaction with idempotency * **以前の意図フェーズ `governance_context` を運ぶ同一ペイロードのリトライ:** 再呼び出しなし。キャッシュされたガバナンスレスポンスが再利用されます。トークンの署名と鮮度が再検証されます。セラー側のリプレイ重複排除キー([検証チェックリスト](/docs/building/by-layer/L1/security#セラー検証チェックリスト)を参照)は、同じ `idempotency_key` を運ぶ繰り返された `jti` を、リプレイ攻撃ではなく正当なリトライとして扱わなければなりません(MUST)— これが唯一の狭い適用除外です。 * **異なるペイロードでの再プラン([冪等性](/docs/building/by-layer/L1/security#冪等性)に従う新しい `idempotency_key`):** 新しい `check_governance` 呼び出しが必要です。新しい `governance_context` トークンが発行されます。 人間の承認後のリトライはガバナンスを再呼び出ししません。既存のトークンは期限切れになるまで認可のままです。実行後のライフサイクルリトライは、セラーの `purchase` フェーズトークンに対して動作します。これは、バイヤーの意図チェックではなく、セラーの実行チェックによって管理される別個のアーティファクトです。 ### Channel mix targets `mix_targets` フィールドは、許容される配分範囲を定義します。ガバナンスエージェントは、すべてのメディアバイにわたる集計支出がこれらの範囲内に収まることを検証します。ビデオ支出を総予算の 70% 超に押し上げる `create_media_buy` は、`conditions` または `denied` のステータスをトリガーします。 ### Delegations プランは、どのエージェントがプランに対して実行を認可されているか、どのような制約でかを指定する `delegations` 配列を含めることができます。これにより、ブランドとエージェンシー間の委任関係がプロトコル内で明示的になります。 ```json theme={null} { "plan_id": "plan_q1_2026_launch", "brand": { "domain": "acmecorp.com" }, "delegations": [ { "agent_url": "https://buying.pinnacle-media.com", "authority": "full", "budget_limit": { "amount": 300000, "currency": "USD" }, "markets": ["FR", "DE", "GB"], "expires_at": "2026-06-30T00:00:00Z" }, { "agent_url": "https://buying.nova-agency.com", "authority": "execute_only", "markets": ["US"], "expires_at": "2026-06-30T00:00:00Z" } ] } ``` 権限レベル: | Level | Meaning | | -------------- | ------------------------------------------------ | | `full` | 委任の予算と市場の制約内で任意のアクションを実行できる | | `execute_only` | 事前承認されたアクションを実行できるが、新しいキャンペーンを開始したり予算を再配分したりできない | | `propose_only` | ガバナンスレビュー用にアクションを提案できるが、明示的な承認なしに実行できない | 委任が存在する場合、ガバナンスエージェントは、アクションを承認する前に `check_governance` の `caller` URL が委任の `agent_url` に一致することを検証します。マッチングは厳密な URI 比較(RFC 3986 に従う正規化後、大文字小文字を区別)によります。フランスでメディアバイを要求するエージェントは、`markets` にフランスを含む委任を持たなければなりません。`execute_only` 権限を持つエージェントは、チャンネル間で予算を再配分できません。 委任が存在しない場合、ガバナンスエージェントはどのエージェントがプランに対して行動できるかを制限しません。 `delegations.authority` は、委任された実行エージェントがプランを代行して何ができるかを管理します。これはプランの予算自律性(`budget.reallocation_threshold` / `budget.reallocation_unlimited`)とは無関係で、`plan.human_review_required` とも無関係です。3 つの別個の関心事: エージェントごとのスコープ、予算運用、決定ごとのレビュー。 ### Portfolio governance ホールディングカンパニーやマルチブランド組織のために、プランはクロスブランド制約を定義する `portfolio` オブジェクトを含めることができます。ポートフォリオプランはメンバープランを管理します — メンバープランに対して検証されるあらゆるアクションは、ポートフォリオプランの制約に対しても検証されます。 ```json theme={null} { "plan_id": "portfolio_q1_2026_global", "brand": { "domain": "acmecorp.com" }, "objectives": "Global Q1 media governance across all Acme brands", "budget": { "total": 50000000, "currency": "USD", "reallocation_threshold": 2000000 }, "flight": { "start": "2026-01-01T00:00:00Z", "end": "2026-06-30T00:00:00Z" }, "countries": ["US", "GB", "FR", "DE", "JP"], "portfolio": { "member_plan_ids": ["plan_sparkle_q1", "plan_glow_q1", "plan_nova_q1"], "total_budget_cap": { "amount": 50000000, "currency": "USD" }, "shared_policy_ids": ["eu_gdpr_advertising", "eu_ai_act_article_50"], "shared_exclusions": [ { "policy_id": "no_competitor_properties", "enforcement": "must", "policy": "No advertising on properties owned by competitor holding companies." } ] } } ``` ポートフォリオ制約: * **`total_budget_cap`**: すべてのメンバープランにわたる最大集計支出。ガバナンスエージェントはすべてのメンバープランにわたってコミット済み予算を追跡し、上限を超えるアクションを拒否します。 * **`shared_policy_ids`**: 個々のブランドコンプライアンス設定に関係なく、すべてのメンバープランにわたって強制されるレジストリポリシー。どのブランドチームもオーバーライドできないコーポレートレベルの規制。 * **`shared_exclusions`**: `PolicyEntry` 形状を使って、すべてのメンバープランに適用されるビスポークな除外ポリシー。追加のみ — プランレベルの `custom_policies` と同じ制約。 ガバナンスエージェントは、メンバープランのアクションを、メンバープラン自身の制約とポートフォリオプランの制約の両方に対して検証します。どちらのレベルからの拒否もアクションをブロックします。 ポートフォリオプランがガバナンスエージェントがまだ認識していない `member_plan_id` を参照する場合、ガバナンスエージェントはポートフォリオプランを受け入れ、メンバープランが同期されるにつれてポートフォリオ制約の強制を開始すべきです(SHOULD)。これにより、特定の順序を要求することなく、メンバープランの前にポートフォリオプランを同期できます。 **並行性**: オーケストレーターは、複数のセラーに同時に `create_media_buy` リクエストを送信し、それぞれが `committed` チェックをトリガーする場合があります。予算チェックは特定時点のもので予算を予約しないため、同時承認が合計でプラン予算を超える場合があります。ガバナンスエージェントは結果レポート時にオーバースペンドを検出します。同時のオーバースペンドを防ぐには、実行エージェント間で予算を分割するために、エージェントごとの `budget_limit` を持つ [委任](#delegations) を使います。 ### Aggregated-spend evaluation (fragmentation defense) ガバナンスしきい値(`reallocation_threshold`、`human_review_required` のトリガーポイント、レジストリポリシーのドル下限)は、プランごとまたはメディアバイごとに単独ではなく、トレーリングウィンドウにわたる**集計コミット済み支出**に対して評価しなければなりません(MUST)。意図した 999,900 ドルの支出を 100 × 9,999 ドルのバイに分割するバイヤー — それぞれが個別にはオペレーターの 10,000 ドルの人間レビューしきい値を下回る — は、そうでなければレビューを完全にバイパスします。これはガバナンスに対するフラグメンテーション攻撃であり、正当な利用パターンではありません。ガバナンスエージェントはこれを閉じなければなりません(MUST)。 ガバナンスエージェントは、任意のしきい値を評価する際、次のすべてにわたってコミット済み支出を集計しなければなりません(MUST)。 * 同じ `(buyer_agent, seller_agent, account_id)` タプルに帰属可能なすべてのプラン — 同じアカウント上のプラン間でのフラグメンテーションは集計をリセットしません。委任されたサブエージェント([委任](#delegations)を参照)は委任元バイヤーの集計を共有します: キーの `buyer_agent` 要素は委任元プリンシパルであり、サブエージェントの `agent_url` ではありません。委任は新しいエージェントごとの集計ウィンドウを作りません。そうでなければ委任サーフェス自体がフラグメンテーションの穴を再び開くからです(999,900 ドルを 100 のサブエージェントに分割し、それぞれが独自の 9,999 ドルの予算を得る)。 * すべての [支出コミットタスク](#spend-commit-tasks) — タスクサーフェス間でのフラグメンテーションは集計をリセットしません。支出コミットタスクのインベントリが唯一の権威あるリストです。そこに追加される新しい支出コミットタスクは、本セクションでの別個の編集なしに自動的に集計に加わります。 * `governance.aggregation_window_days` ケイパビリティを通じて宣言されるトレーリングウィンドウ(下記 [get\_adcp\_capabilities](#governance-aggregation-capability) を参照)。ウィンドウはプラン境界ではなく実時間でスライドします。 **評価時のセマンティクス(テスト可能)。** 支出コミットの時点で、ガバナンスエージェントは次を計算します。 ``` aggregate = sum(c.amount for c in commit_history where c.key == this.key and c.ts > now - aggregation_window_days × 86400s) + this.amount ``` 次に、`aggregate` を適用可能な各しきい値に対して評価します。現在の受信コミットは合計に含まれます。完全に拒否されたコミットは寄与しません。承認された、または条件付きで承認されたコミットは寄与します。`now` は評価時のガバナンスエージェントの実時間です。スライディングウィンドウの境界はプランやカレンダーの境界にスナップされません。 **コミットメントはウィンドウ内でスティッキーです。** `c.amount` は承認時にコミットされた額であり、配信された額ではありません。アンダーデリバリー、キャンセル、メイクグッド、承認後の予算削減は、トレーリングウィンドウがそれをロールオフする前に、コミットの集計への寄与を減じてはなりません(MUST NOT)。そうでなければ、バイヤーは承認済みコミットをキャンセルして直ちにしきい値未満で再コミットすることでフラグメンテーションの余地を解放できます — ラウンドトリップにわたって完全な支出が移動し、各レグが単独で通過します。コミット済み予算を*増やす* `update_media_buy` はデルタ(新コミット済み予算 − 以前のコミット済み)として入ります。減少は減じません。 個々のコミットが単独ではしきい値を下回るが、トレーリングウィンドウの集計をしきい値超に押し上げる場合、ガバナンスエージェントはそのコミットにしきい値の帰結(人間レビューへのエスカレーション、拒否、または条件)を適用しなければなりません(MUST)。ガバナンスエージェントは、監査人が完全な結果ストリームから再導出せずにフラグメンテーション防御の決定を再構築できるよう、`get_plan_audit_logs` レスポンスに `aggregate_committed` フィールドを公開してもかまいません(MAY)。フィールドの形状(単位、通貨、ウィンドウ境界のレポート)は 3.x ではガバナンスエージェント固有で、後の改訂で標準化されます。それを公開する実装は、`get_plan_audit_logs` レスポンスとともに形状を文書化すべきです(SHOULD)。 ガバナンスエージェントは、より狭い集計スコープ(ブランドごと、キャンペーンごと)を追加で評価してもかまいませんが(MAY)、オペレーターの署名なしに宣言されたウィンドウより*広い*スコープを評価してはなりません(MUST NOT)。「より広い」は**両方**の次元をカバーします: より長いトレーリングウィンドウ(時間)とより広いキータプル(例: `account_id` にまたがって折りたたみ、2 つのアカウントが集計を共有する)。いずれかの次元での黙って広げられたスコープは、黙って狭められたスコープと同じくらいオペレーターにとって驚きです。 #### Composition with `reallocation_threshold` 再配分自体が支出コミットです: `update_media_buy` は増分コミットデルタ(新コミット済み予算 − 以前にコミットされた額)を運び、そのデルタは集計に入り `reallocation_threshold` 評価にカウントされます。バイヤーは、1 つの 30,000 ドルの再配分を 6 つの 4,999 ドルの更新に分割することで 25,000 ドルの再配分しきい値を回避できません — 各更新のデルタはトレーリングウィンドウの集計に蓄積され、累積デルタがそれを超えるとしきい値をトリップします。 #### Conformance example エージェントが `aggregation_window_days: 30` を宣言します。プランはコミット済み支出 10,000 ドルで `human_review_required` トリガーを設定します(`(buyer_agent, seller_agent, account_id)` をキーとする)。 | # | Prior 30-day aggregate | Incoming commit | Post-aggregate | Expected outcome | | - | ---------------------- | -------------------------- | -------------- | --------------------------------------------------- | | 1 | \$4,000 | \$2,500 `create_media_buy` | \$6,500 | 自動承認 — しきい値未満 | | 2 | \$8,000 | \$2,500 `create_media_buy` | \$10,500 | 人間レビューにエスカレーション — 受信コミットが単独では 2,500 ドルでも集計がしきい値を超える | エスカレーションなしに行 2 を承認するガバナンスエージェントは非準拠です: 30 日ウィンドウにわたる集計に失敗したか、正しいタプルでキー付けに失敗したか、受信コミットを合計に含めることに失敗したかのいずれかです。 #### Governance aggregation capability セラーとガバナンスエージェントは、[`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) の `governance.aggregation_window_days` を通じて集計ウィンドウを宣言します。コンプライアンスのために特定のウィンドウに依存するバイヤー(例: ブランドレベルの週次ケイデンスレビュー)は、集計セマンティクスに依存する前にこのケイパビリティを確認しなければなりません(MUST)— `aggregation_window_days: 7` を宣言するガバナンスエージェントは、30 日の四半期末プッシュにまたがって広がるフラグメンテーションから防御しません。宣言がないことは、エージェントがいかなる集計ウィンドウにもコミットしていないことを意味し、バイヤーはコミットごとの評価のみを想定しなければなりません(MUST。フラグメンテーション攻撃サーフェスが開いている)。スキーマのデフォルトはありません: 省略は宣言された 30 日ウィンドウと同等ではありません。 ## Brand compliance configuration コンプライアンスポリシーは、個々のキャンペーンプランではなくブランドレベルに存在します。ブランドのポリシーチームがブランドのコンプライアンスプロファイルを設定し、ガバナンスエージェントがそのブランドのプランを処理する際にそれを解決します。 ブランドコンプライアンス設定のスキーマとホスティングメカニズムは、AgenticAdvertising.org ガバナンスワーキンググループによって開発中です。以下は概念モデルを説明します。実装は異なる場合があります。 ブランドのコンプライアンス設定には 2 種類のポリシーが含まれます。 * **レジストリポリシー**: [AdCP ポリシーレジストリ](/docs/governance/policy-registry) の標準化されたポリシーへの ID による参照。各参照は、ブランド向けにポリシーをカスタマイズする設定パラメーターを含んでもかまいません(MAY)。 * **カスタムポリシー**: 自然言語文字列として表現されるブランド固有のルール。[プロンプトベースのポリシー](/docs/governance/overview#prompt-based-policies)と同じアプローチでガバナンスエージェントが評価します。 ポリシーチームはブランドに適用されるレジストリポリシーを選択し、必要に応じてパラメーターを設定し、ブランド固有のカスタムポリシーを追加します。バイイングチームはこの設定と対話することはありません — ブランドを参照するキャンペーンプランを作成し、ガバナンスエージェントが適用可能なポリシーを自動的に解決します。 ブランドの業種が自動ポリシーマッチングに情報を与えます — 例えば、飲料業界のブランドはその業界向けにタグ付けされたレジストリポリシーを受け取ります。 ## Policy registry ポリシーレジストリは、標準化された機械可読な広告コンプライアンスポリシーのコミュニティ管理ライブラリです。ブランドは独自に記述する代わりに ID でポリシーを参照します。 レジストリは 3 つのカテゴリをカバーします。 | Category | Examples | | ---------------- | ---------------------------------------------------------------- | | **Jurisdiction** | UK HFSS 制限、US COPPA、EU GDPR 年齢確認、California AI ディスクロージャー(SB 942) | | **Vertical** | アルコール年齢確認、ファーマのフェアバランス、ギャンブルの自己排除、金融サービスの APR ディスクロージャー | | **Brand safety** | ブランドセーフティのベースライン、コンテンツ適合性の階層 | レジストリの各ポリシーには、ID、適用管轄、説明、およびガバナンスエージェントがプログラム的に評価できる機械可読なルールがあります。ポリシーは規制の変更に応じてバージョン管理されます。ブランド参照は特定のバージョンにピン留めしてもかまいません(MAY)。バージョンなしの参照は現在のバージョンに解決されます。レジストリ形式とホスティングメカニズムは AgenticAdvertising.org ガバナンスワーキンググループによって開発中です。 このモデルは、[IEEE 7012](https://standards.ieee.org/ieee/7012/7192/)(Machine Readable Personal Privacy Terms)が確立したパターンに従います。IEEE 7012 は、当事者が個別に起草するのではなく参照する標準化された合意の中立的な名簿を維持します。 ## Policy resolution ポリシーは `policy_ids` と `custom_policies` を通じてプラン上で直接宣言されます。プランが同期されると、ガバナンスエージェントはアクティブなポリシーセットを解決します。 1. `policy_ids` で参照されるレジストリポリシーを読み込む 2. プランの `countries` と `regions` と交差させる — プランの市場に適用可能なポリシーのみがアクティブ 3. すべての `custom_policies` を含める(これらは地理に関係なく適用される) **`custom_policies` は追加のみです。** ガバナンスエージェントは、レジストリソースのポリシーテキストをシステムレベルの指示としてピン留めしなければならず(MUST)、`custom_policies`(またはプランの `objectives` フィールド)がレジストリソースのポリシーを緩和、オーバーライド、または無効化することを許可してはなりません(MUST NOT)。カスタムポリシーはより厳しい制限を追加できます — 強制レベルを下げたりカテゴリを免除したりできません。レジストリポリシーと矛盾する `custom_policies` エントリは、その代わりにではなく並行して評価されます。より厳格な制約が支配します。 プランの `countries` と `regions` フィールドは**ジオ強制**としても機能します: ガバナンスエージェントは、プランの許可された地理の外の市場をターゲットにする被管理アクションを拒否しなければなりません(MUST)。`regions: ["US-MA"]` のプランは、他の点でコンプライアントであっても、明示的にマサチューセッツをターゲットにしないアクションを拒否します。これらのフィールドは `product-filters`、`offerings`、`create_media_buy` と同じ ISO コードとセマンティクスを使います。 解決されたポリシーセットは、ガバナンスエージェントが [`check_governance`](/docs/governance/campaign/tasks/check_governance) 中に評価するものです。`brand_policy` と `regulatory_compliance` カテゴリについて、ガバナンスエージェントはこの解決されたセットに対して検証します。 プランに `policy_ids` または `custom_policies` がない場合、ガバナンスエージェントはポリシーベースのカテゴリについて空のポリシーセットで動作します。他のカテゴリ(`budget_authority`、`strategic_alignment` など)は、プランのパラメーターに基づいて依然として適用されます。 ## Audience governance キャンペーンプランは、オーディエンスターゲティング制約、制限属性、ポリシーカテゴリを宣言します。ガバナンスエージェントはこれらを使って、セラーのターゲティングが規制要件とキャンペーンの意図に準拠していることを検証します。 ### Three-layer model オーディエンスガバナンスは 3 つの関心事を分離します。 | Layer | Field | Purpose | Example | | ------------ | ---------------------------- | -------------------- | --------------------------------------------------- | | **アイデンティティ** | `brand.industries` | 会社が何をするか | `["pharmaceuticals", "consumer_packaged_goods"]` | | **規制レジーム** | `plan.policy_categories` | このキャンペーンにどの規制が適用されるか | `["pharmaceutical_advertising", "health_wellness"]` | | **データ制限** | `plan.restricted_attributes` | ターゲティングに使えない個人データは何か | `["health_data"]` | 製薬会社は常にファーマ(アイデンティティ)ですが、一般的な認知キャンペーンは製薬広告規制をトリガーしないかもしれず(レジーム)、EU 管轄のキャンペーンのみが健康データターゲティングを制限するかもしれません(制限)。 ### Audience constraints プランは、オーディエンスセレクターを使って `audience.include` と `audience.exclude` 配列を宣言できます。各セレクターは `signal_ref` または自然言語の説明のいずれかです。 ガバナンスエージェントは、`check_governance` でこれらの制約をセラーのターゲティングに対して評価します。 1. `planned_delivery.audience_targeting` をプランの `audience.include`/`exclude` と比較 2. `planned_delivery.audience_targeting` を同じ制約と比較(コミット済みチェック用) 3. オーケストレーターが要求したものとセラーが有効化するものの間の乖離を検出 ### Structural governance matching シグナル定義は `restricted_attributes` と `policy_categories` を自己宣言できます。そうする場合、ガバナンスエージェントは**構造的マッチング** — プランの制限とシグナルの宣言の間の集合の交差 — を実行します。これは決定的で、LLM 推論を必要としません。 ガバナンスメタデータを宣言しないシグナルについて、ガバナンスエージェントは**セマンティックマッチング** — シグナル名と説明から機密性を推論する — にフォールバックします。構造的マッチングはセマンティックマッチングより高信頼度の検出事項を生成します。 制限属性は `include` と `exclude` の両方のターゲティングに適用されます。制限データを使ってオーディエンスを除外すること(例: 製薬広告から健康状態のある人を除外する)は、それを包含に使うのと同じくらい禁止されています — どちらもターゲティング決定のための制限された個人データの使用を構成します。 ### Audience distribution drift 配信中、セラーは `delivery_metrics` で `audience_distribution` をレポートします。インデックス値は、宣言されたベースライン(census、platform、または custom)に対する人口構成を示します。値 1.0 は同等を意味します。大幅に上または下の値はスキューを示します。 ガバナンスエージェントは、期間ごとのインデックスとすべてのレポート期間にわたる累積インデックスの両方を追跡します。これにより、単一のレポート期間では見えないかもしれない体系的なバイアスの検出が可能になります。 ## State tracking ガバナンスエージェントは 2 つのレベルで状態を追跡します。 * **プランレベル**: コミット済み総予算、チャンネル配分パーセンテージ、プランステータス * **キャンペーンレベル**: `governance_context` ごとのコミット済み予算、アクティブなメディアバイ参照、検証履歴 単一のプランが複数のキャンペーンにまたがることがあります。[`check_governance`](/docs/governance/campaign/tasks/check_governance) が予算権限をチェックするとき、プランに紐付けられたすべてのキャンペーンを考慮します。[`report_plan_outcome`](/docs/governance/campaign/tasks/report_plan_outcome) がセラー確認をレポートするとき、ガバナンスエージェントは要求された額ではなくセラーの実際の額から予算をコミットします。 ### Plan status | Status | Meaning | | ----------- | ------------------------- | | `active` | 検証リクエストと結果レポートを受け付け中 | | `suspended` | 重大なエスカレーションの人間レビュー待ちで一時停止 | | `completed` | プラン完了。読み取り専用 | ステータスが `suspended` の場合、ガバナンスエージェントは、エスカレーションが解決されるまで、すべての `check_governance` および `report_plan_outcome` リクエストを `CAMPAIGN_SUSPENDED` エラーで拒否しなければなりません(MUST)。 ### Budget tracking 予算は、検証されたアクションではなく**確認された結果**に基づいてコミットされます。フロー: 1. `tool` + `payload`(意図チェック)を伴う `check_governance` が、提案された支出がプランに収まるかをチェックします。まだ予算はコミットされません。 2. オーケストレーターがセラーとアクションを実行します。 3. `report_plan_outcome` がセラーの確認済み額をレポートします。ガバナンスエージェントはこの額をプラン予算にコミットします。 これにより、予算追跡が現実を反映します。セラーが予算を 150K ドルから 120K ドルに削減した場合、ガバナンスエージェントは 120K ドルをコミットし、差異について検出事項を返します。アクションが完全に失敗した場合、ガバナンスエージェントは 0 ドルをコミットします。 実行チェックの承認は、セラーの計画された配信をプランに対して検証しますが、予算をコミットしません。予算は、オーケストレーターがセラーの確認済みレスポンスを伴って `report_plan_outcome` を呼び出すときにのみコミットされます。 予算チェックは特定時点のものです: `check_governance` は現在のコミット済み総額に対して検証しますが、予算を予約しません。複数のエージェントが同じプランに対して同時に実行する場合、2 つのチェックが両方通過し、組み合わせた結果が認可された予算を超える可能性があります。ガバナンスエージェントは結果レポート時にオーバースペンドを検出し、`budget_authority` 検出事項を返します。同時のオーバースペンドを防ぐには、実行エージェント間で予算を分割するために、エージェントごとの `budget_limit` を持つ [委任](#delegations) を使います。 ### Drift detection 監査ログには、プランの存続期間にわたる集計ガバナンストレンドを表面化する `drift_metrics` が含まれます。 ```json theme={null} { "summary": { "checks_performed": 847, "drift_metrics": { "human_review_rate": 0.03, "human_review_rate_trend": "declining", "auto_approval_rate": 0.91, "human_override_rate": 0.02, "mean_confidence": 0.88 } } } ``` これらのメトリクスは監視ドリフト — 人間から制御が徐々に移行すること — を検出します。人間レビュー率の低下は、ガバナンスエージェントが適切にキャリブレーションされていることを示すかもしれず、または監視が侵食されていることを示すかもしれません。トレンドを表面化させることで、組織がその判断を下せます。 | Metric | What it measures | | ------------------------- | ------------------------------------------------- | | `human_review_rate` | 内部の人間レビューを必要としたチェックの割合 | | `human_review_rate_trend` | プランの存続期間にわたる方向(`increasing`、`stable`、`declining`) | | `auto_approval_rate` | 人間の介入なしに承認されたチェックの割合 | | `human_override_rate` | 人間がガバナンスエージェントをオーバーライドした人間レビューの割合 | | `mean_confidence` | 検出事項全体の平均信頼スコア(信頼度がレポートされる場合) | 組織はドリフトメトリクスにしきい値を設定できます。メトリクスがしきい値を超えると、ガバナンスエージェントは次のガバナンスチェックに検出事項(深刻度 `warning`)を含めるべきです(SHOULD)。 ```json theme={null} { "drift_metrics": { "human_review_rate": 0.01, "human_review_rate_trend": "declining", "auto_approval_rate": 0.97, "thresholds": { "human_review_rate_min": 0.02, "auto_approval_rate_max": 0.95 } } } ``` この例では、両方のしきい値が破られています — 人間レビュー率(0.01)が最小値(0.02)を下回り、自動承認率(0.97)が最大値(0.95)を超えています。これは、ガバナンスエージェントが広く承認しすぎていることを示すかもしれず、または低リスクキャンペーンにポリシーが適切にキャリブレーションされていることを示すかもしれません。しきい値の破れが問いを表面化させます。組織が答えを決めます。 組織は懸念に関連するしきい値のみを設定します。`human_review_rate_min` は監視の侵食を捕捉します。`human_review_rate_max` はポリシーの誤キャリブレーションを捕捉します。`human_override_rate_max` は、推奨が一貫して間違っているガバナンスエージェントを捕捉します。すべてのしきい値フィールドは任意です。 ### Plan amendments 既存の `plan_id` で `sync_plans` を呼び出すと、プランが更新されます(upsert)。ガバナンスエージェントは `plan_version` をインクリメントし、新しいパラメーターを直ちに適用します。以前のプランバージョンで承認されたアクティブなメディアバイは自動的に再検証されません — ガバナンスエージェントは次の `check_governance` 呼び出し(例: 次の配信チェック)でそれらを更新されたプランに対して評価します。修正が予算を現在のコミット済み額を下回るように削減する場合、ガバナンスエージェントは次のガバナンスチェックでこれを検出事項としてフラグを立てます。 ## Validation logic ガバナンスエージェントは、各 [検証カテゴリ](/docs/governance/campaign/index#validation-categories) を独立して評価します。 * **いずれか**のカテゴリがステータス `failed` を持ち、その失敗が修正可能な場合、ステータスは提案された修正を伴う `conditions` です * **いずれか**のカテゴリがステータス `failed` を持ち、その失敗が呼び出し元によって修正不可能な場合、ステータスは `denied` です * すべてのカテゴリが通過するが全体のリスクプロファイルが人間のレビューを正当化する場合、ガバナンスエージェントはレビューを内部的に処理し(タスクは非同期になる)、最終的に `approved` または `denied` に解決します * すべてのカテゴリが通過する場合、ステータスは `approved` です `conditions` 配列は、ステータスが `conditions` の場合にのみ存在します。各条件は、特定のフィールド、その現在の値、提案された値、変更の理由を識別します。 ### Finding confidence ガバナンスの検出事項には、確実な違反を曖昧なものから区別する任意の `confidence` スコア(0-1)と `uncertainty_reason` が含まれます。 ```json theme={null} { "category_id": "regulatory_compliance", "severity": "critical", "confidence": 0.85, "uncertainty_reason": "Targeting includes 'New Mexico' which partially overlaps LATAM HFSS jurisdiction boundaries", "explanation": "Potential HFSS jurisdiction violation based on targeting geography." } ``` 信頼度は適切な応答に情報を与えます。 * **高信頼度(0.9 以上)**: 検出事項は確定的です。EU ユーザーを明示的にターゲットにしたキャンペーンでの GDPR 違反。 * **中信頼度(0.6-0.9)**: 検出事項は、ガバナンスエージェントが完全に解決できないコンテキストに依存します。未成年者を含む可能性のあるオーディエンスセグメント、規制管轄と部分的に重なるジオターゲティング。 * **低信頼度(0.6 未満)**: 検出事項は推測的です。ガバナンスエージェントは、自律的に行動するのではなく、人間のレビューのためにフラグを立てます。 信頼度がなければ、すべての検出事項が等しく確実として提示され、(確実として扱えば)過剰にブロックするか、(多くが偽陽性なら)人々に検出事項を無視するよう訓練します。ガバナンスエージェントは、評価が自然言語の解釈や確率的マッチングを伴う場合、信頼度を含めるべきです(SHOULD)。 ### Phase inference ガバナンスエージェントは、`check_governance` の `tool` パラメーターから検証フェーズを推論します。 | tool | Phase | | ------------------ | ----------------------------------- | | `get_products` | ディスカバリー — 検索意図、セラー適格性、プロダクト適合性を検証 | | `create_media_buy` | 購入 — 予算権限、ターゲティングコンプライアンス、フライト日程を検証 | | `update_media_buy` | 購入 — 変更の大きさ、再配分しきい値を検証 | | `acquire_rights` | 購入 — 予算権限、ジオコンプライアンス、フライト日程を検証 | | `update_rights` | 購入 — 変更の大きさ、再配分しきい値を検証 | | `activate_signal` | 購入 — 予算権限、ジオコンプライアンス、フライト日程を検証 | | `build_creative` | 購入 — 予算権限、ジオコンプライアンスを検証 | フェーズコンテキストは累積的です。**購入**中、ガバナンスエージェントは**ディスカバリー**中に発見されたものを考慮します。 `check_governance` が返す `check_id` は、`report_plan_outcome` がセラーのレスポンスを検証されたアクションにリンクするために使われます。 ## Capability declaration ガバナンスエージェントは、`get_adcp_capabilities` でキャンペーンガバナンスのサポートを宣言します。 ```json theme={null} { "governance": { "campaign_governance": { "categories": [ { "category_id": "budget_authority", "description": "Validates spend against plan budget limits and allocation rules." }, { "category_id": "strategic_alignment", "description": "Validates that purchases match campaign brief and channel mix targets." }, { "category_id": "bias_fairness", "description": "Checks targeting for discriminatory patterns and protected category compliance.", "jurisdictions": ["US", "EU", "UK"] }, { "category_id": "regulatory_compliance", "description": "Validates jurisdiction-specific advertising regulations.", "jurisdictions": ["US", "EU", "UK"] }, { "category_id": "seller_verification", "description": "Compares seller setup against original requests to detect discrepancies." }, { "category_id": "brand_policy", "description": "Enforces brand-level compliance policies resolved from the brand configuration and policy registry." } ] } } } ``` ## Integration with `create_media_buy` バイヤーは `create_media_buy` リクエストに `plan_id` を、プロトコルエンベロープに `governance_context` を含めます。これらのフィールドは、どのガバナンスプランが適用されるかをセラーに伝え、セラー側のガバナンスチェックを可能にします。 ```json theme={null} { "tool": "create_media_buy", "arguments": { "plan_id": "plan_q1_2026_launch", "account": { "agent_url": "https://seller.example.com", "id": "acc_123" }, "brand": { "domain": "acmecorp.com" }, "start_time": "2026-03-15T00:00:00Z", "end_time": "2026-06-15T00:00:00Z", "packages": ["..."] } } ``` セラーのレスポンスには `planned_delivery` — セラーが実際に実行するもの — が含まれます。 ```json theme={null} { "seller_reference": "mb_seller_456", "packages": ["..."], "planned_delivery": { "geo": { "countries": ["US"] }, "channels": ["olv"], "start_time": "2026-03-15T00:00:00Z", "end_time": "2026-06-15T00:00:00Z", "total_budget": 150000, "currency": "USD", "frequency_cap": { "max_impressions": 3, "per": "user", "window": { "interval": 1, "unit": "days" } }, "audience_summary": "Adults 25-54, US, premium video inventory", "enforced_policies": ["us_coppa"] } } ``` `planned_delivery` は、リクエストに対するセラーの解釈 — 使用する実際の配信パラメーター — です。2 つの目的を果たします。 1. **ガバナンスチェック** — アカウントにガバナンスエージェントが設定されている場合、セラーはメディアバイを確認する前に検証のために `planned_delivery` をガバナンスエージェントに送信します。 2. **透明性** — バイヤーは、配信開始前に早期に差異を捕捉するために、`planned_delivery` を要求したものと比較できます。 ## Governance checks キャンペーンガバナンスのバイヤー側検証には信頼の限界があります: バイヤーのオーケストレーターが自分の宿題を採点します。LLM エージェントはガバナンス承認を幻覚したり、検証をスキップしたり、何が検証されたかを誤って表現したりする可能性があります。セラー側のガバナンスチェックは、購入が承認されていることをセラーが独立して確認する方法を与えることで、このギャップを閉じます。 被管理アクションイベントが発生すると、セラーはバイヤーが設定したガバナンスエージェント URL に POST します。ガバナンスエージェントはすべての状態を維持し、`plan_id` + `governance_context` でリクエストを相関させます — セラーはガバナンス履歴を追跡したり、呼び出し間で ID をチェーンしたりする必要はありません。 ### Both checks must pass すべての被管理アクションは、バイヤー側の意図チェックとセラー側の計画配信チェックの両方に**合格しなければなりません(MUST)**。両方の呼び出しは同じ権威(バイヤーのガバナンスエージェント)に到達するため、「2 つのエージェントが意見を異にする」ケースはありません — しかし両方の呼び出しが成功しなければならないという不変条件は負荷を担っています。 * バイヤー側の意図チェックは、*プランが原則として支出を許可する*ことを確認します。 * セラー側の計画配信チェックは、*セラーの実際の配信パラメーターが承認されたプランと一致する*ことを確認します。 これらは冗長ではありません。バイヤーの意図チェックが通過し(プランはプレミアムビデオに 100K ドルを許可する)、セラーの計画配信チェックが失敗する(セラーの計画されたラインナップにプランが除外するインベントリが含まれる)ことがあります。どちらかが `denied` を返す場合、アクションは進めてはなりません(**MUST NOT**)。両方が空でない `conditions` を伴って `approved` を返す場合、適用されるセットは両方のレスポンスの条件の**和集合**です。矛盾する条件(一方が X を要求し、他方が NOT X を要求する)は、黙った優先ではなく、構造化された `finding` を伴う `denied` に解決されます。 自身のコンテンツ標準や商業的理由で取引を拒否するセラーは、ガバナンス競合に参加しているわけではありません — それは別個の商取引層の拒否(例: `TERMS_REJECTED`)であり、通常の拒否パスに従います。ガバナンスはバイヤーのプランについてのみ語ります。 ### Setup バイヤーは [`sync_governance`](/docs/accounts/tasks/sync_governance) を通じてガバナンスエージェントを同期し、各アカウントを呼び出すガバナンスエージェントエンドポイントとペアリングします。各エージェントには、ガバナンスエージェントがセラーの識別を検証できるよう認証情報が含まれます。 ```json theme={null} { "tool": "sync_governance", "arguments": { "accounts": [ { "account": { "brand": { "domain": "acmecorp.com" }, "operator": "pinnacle-media.com" }, "governance_agents": [ { "url": "https://governance.pinnacle-media.com", "authentication": { "schemes": ["Bearer"], "credentials": "gov_token_acme_pinnacle_2026_xyzxyzxyz..." } } ] } ] } } ``` セラーはこれらのエンドポイントを保存し、`check_governance` を呼び出す際に認証情報を提示します。ガバナンスエージェントは、Bearer トークンが `plan_id` に関連付けられたアカウントの登録済み認証情報に一致することを検証しなければならず(MUST)、認識されないまたは一致しない認証情報を持つリクエストを拒否しなければなりません(MUST)。 ### Governance modes ガバナンスモード(audit、advisory、enforce)は、ガバナンスエージェントの内部実装の詳細であり、プロトコルレベルのフィールドではありません。呼び出し元は `check_governance` を送信し、`approved`、`denied`、または `conditions` を受け取ります — どのモードがその決定を生成したかを知る必要はありません。 これは次を意味します。 * audit モードのガバナンスエージェントは、内部的に常に検出事項を添付して `approved` を返します * advisory モードのガバナンスエージェントは、内部的に `denied` を返す場合がありますが、組織はそれを非ブロッキングとして扱います * enforce モードのガバナンスエージェントは `denied` を返し、呼び出し元が停止することを期待します モードは、プロトコル経由ではなく、ガバナンスエージェント自体でバイヤーのポリシーチームによって設定されます。ガバナンスエージェントは、事後分析のために監査ログや `get_plan_audit_logs` レスポンスにモード情報を含めてもかまいませんが(MAY)、呼び出し元はモードに基づいて動作を分岐してはなりません(MUST NOT)— 受け取ったステータスに基づいて行動します。 クロール・ウォーク・ランの採用パスについては [安全モデル](/docs/governance/campaign/safety-model) を参照。 ### Governance context `governance_context` フィールドは、`check_governance` レスポンスでガバナンスエージェントが発行する不透明な文字列です。任意の被管理アクションのライフサイクルを相関させ、主要な監査/レポートキーです。ガバナンスエージェントは、必要な内部状態(プラン参照、予算スナップショット、チェック履歴)をこの値にエンコードします。 呼び出し元は `governance_context` を解釈してはなりません(MUST NOT)。永続化して転送します。 * **バイヤー**: `check_governance` レスポンスから `governance_context` を受け取り、メディアバイをセラーに送信する際にプロトコルエンベロープに添付します。 * **セラー**: エンベロープで `governance_context` を受け取り、メディアバイとともに保存し、そのメディアバイのライフサイクルの後続のすべての `check_governance` 呼び出しに含めます。 * **ガバナンスエージェント**: `governance_context` を使って各ライフサイクルイベントを元のプラン、キャンペーングルーピング、予算状態に再接続します。 最初の `check_governance` 呼び出し(コンテキストが存在する前)では、ガバナンスエージェントは `payload` と `plan_id` から必要なものを抽出します。後続の呼び出しでは、`governance_context` が継続性を提供するため、ガバナンスエージェントはペイロードから状態を再導出する必要がありません。 ガバナンスエージェントは、`governance_context` をガバナンス状態のプレーンテキストエンコーディングとしてではなく、サーバー側状態へのルックアップキーまたは署名付きトークンとして扱うべきです(SHOULD)。状態が直接エンコードされる場合、中間者による改ざんが検出可能になるよう署名されなければなりません(MUST。例: HMAC)。 AdCP 3.0 では、エンコードされた値は [AdCP JWS プロファイル](/docs/building/by-layer/L1/security#adcp-jws-プロファイル)に従って署名されたコンパクト JWS です。トークンは、証明をガバナンスエージェントが評価した正確なプラン状態にバインドする必須の `plan_hash` クレームを運びます(下記 [Plan binding and audit](#plan-binding-and-audit) を参照)。呼び出し元は依然として値を相関のために不透明として扱います。検証にオプトインするセラーは [セラー検証チェックリスト](/docs/building/by-layer/L1/security#セラー検証チェックリスト)に従い、トークンの真正性、認可スコープ、鮮度を検証します — バイヤーのプランは検証しません。 ### Plan binding and audit `plan_hash` クレームは、署名付き `governance_context` 証明を、ガバナンスエージェントが評価した正確なプラン状態に永遠にバインドする暗号学的レシートです。これは**監査層**プロパティです — セラーはそれを検証せず、検証することも期待されません。バイヤーのプランは、バイヤーがセラーと共有しない商業的に機密なデータ(クロスセラー配分、セラーごとの上限、目標、`approved_sellers` リスト、カスタムポリシー、`ext`)を運びます。3.x にはプラン取得メカニズムはなく、計画もされていません。`plan_hash` は、JWS がすでにガバナンスエージェントが生成する署名付きアーティファクトであるため JWS の内部に同行しますが、ワイヤー検証コントラクトの一部ではありません。 クレームが提供するもの: * **事後の説明責任。** すべてのガバナンス証明は、それが証明したプラン状態に永遠にバインド可能です。規制当局とフォレンジック監査は、保持された JWS とガバナンスエージェントのリビジョン記録のみを使って、何年も後に「このトランザクションは時刻 T にプラン状態 X の下で認可された」ことを証明できます。 * **ガバナンスエージェントの自己整合性。** すべての `check_governance` 呼び出しで、ガバナンスエージェントは現在のプラン状態を再評価し再ハッシュします。呼び出し間でガバナンスエージェントの永続化されたプランを改ざんすると、保持されたリビジョン記録に対する不一致として表面化します。 * **バイヤー側のコンプライアンス検証。** バイヤー自身のツールは、そのガバナンスエージェントがバイヤーが実際にプッシュしたプランに一致するトークンを生成していることを検証できます — 侵害されたまたは不正なガバナンスベンダーを捕捉します。 #### Canonicalization `plan_hash = base64url_no_pad(SHA-256(JCS(plan_payload)))` ここで: * `JCS` は [RFC 8785 JSON Canonicalization Scheme](https://www.rfc-editor.org/rfc/rfc8785) — [冪等性ペイロード等価性](/docs/building/by-layer/L1/security#ペイロード等価性)に使われるのと同じスキームです。ガバナンスエージェントと監査人の検証者は、そこにリストされているのと同じライブラリ実装を使うべきです(SHOULD)。JCS はオブジェクトキーをコードポイントで辞書順にソートし(`sync_plans` リクエストでの呼び出し元のキー順はハッシュに影響しません)、省略された任意フィールドと明示的な `null` の区別を保持します(これらは異なるハッシュを生成します)。ガバナンスエージェントはプランを供給されたままハッシュしなければならず(MUST)、省略された任意項目をデフォルト値に合成してはならず(MUST NOT)、明示的な null を削除してはなりません(MUST NOT)。 * `plan_payload` は **`sync_plans` で供給された `plans[]` 配列の 1 要素** です — 単一のプランオブジェクトであり、`sync_plans` リクエストエンベロープでも `plans` ラッパー配列でもありません。プリイメージは証明時点での現在のプランリビジョン状態、すなわちガバナンスエージェントがちょうど評価したプランオブジェクトです。プリイメージを構築する実装は、プランリビジョンオブジェクトから開始し、下記にリストされた閉じた帳簿フィールドのセットを削除します。 * `base64url_no_pad` は、末尾の `=` パディングを取り除いた RFC 4648 §5 に従います — JWS プロファイルの `jti` や他の base64url 値と一貫しています。ガバナンスエージェントはパディングなしの形式を発行しなければなりません(MUST)。検証者(自身のトークンを再検証するガバナンスエージェント、監査人、バイヤー側コンプライアンスツール)は、両側を base64url デコードして生の 32 バイト SHA-256 ダイジェストにし、バイトを比較して比較しなければならず(MUST)— エンコードされた形式の文字列等価性ではなく — パディング、大文字小文字、アルファベットの変動が、偽の不一致を生成するのではなくデコード失敗として拒否されるようにします。正確に 32 バイトにデコードされない `plan_hash` は拒否しなければなりません(MUST)。 #### Excluded fields 閉じたリスト — ガバナンスエージェントはそれを拡張または縮小してはならず(MUST NOT)、追加はプロファイルバージョンのバンプを要求する破壊的変更で、[ペイロード等価性](/docs/building/by-layer/L1/security#ペイロード等価性)と同じルールです。 * `version` — ガバナンスエージェントのリビジョンカウンター、各再同期でエージェントが設定 * `status` — エージェントが管理するプランライフサイクルステータス * `syncedAt` — 各再同期で書き込まれるタイムスタンプ * `revisionHistory` — エージェント内部の追記のみのリビジョンログ(追記のみのアーカイブでなければならず(MUST)、実装は revisionHistory エントリをハッシュに使うアクティブなプランオブジェクト形状に読み戻してはなりません(MUST NOT)) * `committedBudget` — ダウンストリームの `check_governance` / `report_plan_outcome` アクティビティから導出 * `committedByType` — 同じアクティビティから導出 これらはいずれも `sync_plans` リクエストスキーマ(プランアイテムの `additionalProperties: false`)に現れません。ガバナンスエージェントの永続化されたプラン状態にのみ存在します。リストは、内部状態構造体を素朴にハッシュする実装者が正しいフィールドを取り除くよう、明示的に述べられています。このリストを超えて永続化されたプラン状態に追加の GA 内部フィールドを発見した実装は、プロファイルバージョンがバンプするまでそれらのフィールドを**プリイメージ内**として扱わなければなりません(MUST)— 除外ではなく包含へのフェイルセーフ。「帳簿のように見えるもの → 取り除く」というショートカットは、異なる推測をするすべての実装を黙って乖離させます。安全なデフォルトは「閉じたリストになければ、ハッシュの一部である」です。 他のすべてのフィールド — `ext`、`custom_policies`、`objectives`、`delegations`、`human_override`、および `sync_plans` プランアイテムスキーマで宣言されたすべてのフィールドを含む — はプリイメージ内です。呼び出し元へのガイダンス: * **`ext` はプリイメージの一部です。** バイヤーは、プラン上の `ext` 内に回転するトークンやリトライ不安定な値を置いてはなりません(MUST NOT)。再同期間で変わる値は、バイヤーの宣言された意図が変わらない場合でも、プランのすべての未処理の governance\_context トークンを無効化します(冪等性の `ext` の扱いと一貫)。 * **`delegations[].expires_at`** は宣言された意図であり、同じプランの再同期間で安定しているべきです(SHOULD)。すべての同期で「now + N days」から再生成すると、ハッシュのチャーンを引き起こします。 * **配列順序** は `policy_ids`、`policy_categories`、`custom_policies`、`approved_sellers`、`delegations`、`countries`、`regions`、`channels.required`、`channels.allowed` において意味的に重要ではありませんが、JCS はそれを保持します。バイヤーはこれらを再同期間で安定した順序で発行すべきです(SHOULD)。 * **JCS は Unicode 正規化しません。** RFC 8785 §3.2.5 に従い、JCS は文字列を供給されたまま保持します — 視覚的に区別できない Unicode 変種(ラテン `a` 対キリル `а`、NFC 対 NFD 合成、混同可能なホモグリフ)は異なるバイトを生成し、したがって異なるハッシュを生成します。`plan_hash` はこの乖離を暗号層で正しく検出しますが、プランセマンティクス層はそうではありません: `policy_ids` または `policy_categories` がホモグリフ置換のみで異なる 2 つのプランは、ガバナンスエージェントおよびダウンストリームコンシューマーで異なる強制結果を認可します。バイヤーとガバナンスエージェントは、`policy_ids` と `policy_categories` を発行または評価する前に、サーバー側で正規の許可リストに対して検証すべきです(SHOULD)。これはハッシュルールではなくプランコンテンツルールです — `plan_hash` の整合性はどちらの場合も無傷です。許可リストが、ホモグラフ置換が異なる認可決定を生成するのを防ぐものです。 #### Governance-agent obligations ガバナンスエージェントは次を行わなければなりません(MUST)。 * すべての `check_governance` 呼び出しで現在のプラン状態にわたって `plan_hash` を計算し、署名付き JWS ペイロードに含める。ハッシュはエージェントがちょうど評価したプランにわたってでなければなりません。変異したプランにわたる古い証明を生成してはなりません(MUST NOT)。 * すべての `check_governance` 呼び出しで署名をリフレッシュする — 新しい `jti`、`iat`、`exp`、`plan_hash`。ガバナンスエージェントは、プランリビジョンにまたがって以前に署名された `governance_context` トークンをキャッシュして再発行してはなりません(MUST NOT)。エンベロープの冪等性レスポンスキャッシュは別個のレジームです — `governance_context` は、リプレイ時に回転できるよう、[ペイロード等価性](/docs/building/by-layer/L1/security#ペイロード等価性)の閉じた除外リストに含まれています。 * 各内部プランリビジョン記録とともに**リビジョンごとの `plan_hash` を保持する** — `audit_log_pointer` が公開されているかどうかに関係なく MUST。保持が永遠にバインドするプロパティを提供するものです。普遍的な保持がなければ、`audit_log_pointer` を使わないすべてのガバナンスエージェントは、そのトークンコーパス全体の監査層を黙って無効にします。証明されたプラン状態に結合し直せない監査ログは監査証跡の半分であり、自身の履歴トークンを検証できないガバナンスエージェントは、自身のストアの改ざんを検出できません。保持された値は実装内部で、[`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs) の正規化されたレスポンスを通じて以外はワイヤー上で決して公開されません。それはエントリごとに `plan_hash` をエコーするため、監査人はガバナンスエージェントのプライベート記録から再構築する必要はありません。 #### Wire-verification contract `plan_hash` は `crit` にリストされません。`crit` はワイヤー検証者のセマンティクス(RFC 7515 §4.1.11)です: リストされたクレームを処理できない検証者にトークンを拒否させます。`plan_hash` を処理するワイヤー検証者はありません — プリイメージを取得できる唯一の当事者(ガバナンスエージェント、監査人、バイヤーコンプライアンス)はオフワイヤーです。`crit` にリストすると、検証する根拠のないトークンをセラーに拒否させ、相殺する利益はありません。ガバナンスエージェントはクレームを発行しなければならず(MUST)、`crit` にリストしてはなりません(MUST NOT)。 セラーは `governance_context` をそのまま永続化して転送し、[15 ステップの JWS 検証チェックリスト](/docs/building/by-layer/L1/security#セラー検証チェックリスト) — 真正性、認可スコープ、鮮度 — を実行します。トークン内の `plan_hash` を不透明なカーゴとして扱い、決して検査しません。 #### Verification recipes **監査人レシピ。** プランの監査ログにアクセスできる規制当局やサードパーティ監査人は、次のように履歴証明を検証します。 1. `include_entries: true` を伴って [`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs) を呼び出して監査証跡を取得します。各 `check` エントリは `plan_hash`(発行時に主張されたクレーム)と `governance_context`(署名付き JWS)を運びます。 2. 各 governance\_context について、コンパクト JWS をデコードし、ガバナンスエージェントの公開 JWKS に対して 15 ステップの JWS コントラクト(署名、brand.json の `iss`、`aud`、`exp` など)を検証します。 3. デコードされた JWS ペイロードから `plan_hash` クレームを抽出し、base64url デコードして 32 生バイトにします。 4. エントリレベルの `plan_hash` を 32 生バイトにデコードし、クレームとバイト比較します。不一致は、保持された監査記録が署名付きトークンと矛盾することを意味します — トークンが改ざんされたか記録が改ざんされたかのいずれかで、ガバナンスエージェントの整合性が疑われます。 5. 任意: ガバナンスエージェントの保持されたリビジョンごとのプラン記録から `plan_hash` を再計算し(監査人がガバナンスエージェントのリビジョンストアへの認証済みアクセスを持つ場合)、再度バイト比較します。ここでの不一致は、署名と監査の間にガバナンスエージェント自身のストアが改ざんされたことを意味します。 **バイヤー側コンプライアンスレシピ。** 自身のツールがガバナンスエージェントが正直なトークンを生成していることを検証したいバイヤー: 1. プロトコルエンベロープを通じてセラーに流れる `governance_context` トークンを観察します(バイヤーはすでにこれらを持っています。取得不要)。 2. 各トークンについて、JWS をデコードし、`plan_hash` クレームを抽出し、base64url デコードして 32 バイトにします。 3. トークンが証明するリビジョンでのバイヤー自身のプランのコピーにわたって `plan_hash` を再計算します。バイヤーは自身の `sync_plans` 呼び出しから権威あるプラン状態を持っています。 4. バイト比較します。不一致は、ガバナンスエージェントがバイヤーが実際にプッシュしたプランに一致しない証明に署名していることを意味します — ベンダーの侵害またはバグのいずれかです。どちらもバイヤーがエスカレーションすべき重大な検出事項です。 このパスは、セラー、監査人、プロトコルを関与させずに不正なガバナンスベンダーを捕捉します。データはすでにバイヤー側にあります。 **定数時間比較。** 3 種類の検証者すべて — ガバナンスエージェントの自己整合性、監査人、バイヤー側コンプライアンス — は、`plan_hash` ダイジェストを比較する際に定数時間バイト比較(例: Node の `crypto.timingSafeEqual`、Python の `hmac.compare_digest`、Go の `crypto/subtle.ConstantTimeCompare`)を使うべきです(SHOULD)。SHA-256 長の比較でのタイミングサイドチャネルは今日実際には悪用可能ではありません。ルールは、より短いダイジェストで比較コードを再利用する将来のデプロイや、検証者ホスト上でのコテナンシーを持つ攻撃者に対する安価な保険です。 #### Privacy considerations 再同期にわたる `plan_hash` の変化は、*何が*変異したかを明かさなくても、プランが変異したことを明かします。トークンを長期保持する当事者(`governance_context` をそのまま転送するセラー、監査人、規制当局)は、特定の `plan_id` について観察する別個のハッシュのシーケンスから、プランの変異ケイデンスを推論できます。ほとんどのデプロイでこれは許容可能または望ましいものです — 変異ケイデンスは監査シグナルの一部です。変異頻度自体が商業的または運用的に機密である機密ガバナンスデプロイ(例: バイヤーがセラーにキャンペーンポートフォリオ全体の再プランケイデンスを推論されたくない)は、これをトークン保持ポリシーに織り込むべきです(SHOULD): `governance_context` のより短いセラー側保持ウィンドウ、またはクロストークンのリンク可能性を断つためのプランの `jti` 名前空間の定期的な回転。 #### Reference test vectors [`static/compliance/source/test-vectors/plan-hash/`](https://github.com/adcontextprotocol/adcp/tree/main/static/compliance/source/test-vectors/plan-hash) の 11 個のベクターが、正規化をビット単位で正確にピン留めします: 最小限のプラン、すべての任意フィールドを行使するプラン、帳簿を取り除いたケース(GA 内部フィールドが保存されたプランに存在するがハッシュ前に取り除かれ、帳簿なしの等価物と同じハッシュを生成する)、省略対明示 null、`policy_categories` の配列順序、回転する `ext.trace_id` がすべて別個のハッシュを生成することを証明するペアベクター、JCS が RFC 8785 §3.2.5 に従い正規化しないことを確認する Unicode ケース、および手作りの `JSON.stringify + key sort` ではなくライブラリの選択をピン留めする小数パーセンテージ付きの数値正規化ケース。各ベクターは、プリイメージ、正規の JCS バイト、SHA-256 16 進ダイジェスト、最終的な `plan_hash` クレーム値を記録します。ガバナンスエージェントと監査人の検証者は、これらのハッシュをビット単位で正確に再現しなければなりません(MUST)。 ### Governance phases ガバナンスチェックは、3 つのフェーズを通じてメディアバイのライフサイクル全体をカバーします。 | Phase | Trigger | What's validated | | -------------- | ---------------------------------------------------------------------- | ------------------------ | | `purchase` | `create_media_buy`、`acquire_rights`、`activate_signal`、`build_creative` | 予算、ジオ、チャンネル、フライト日程、ポリシー | | `modification` | `update_media_buy`、`update_rights` | 変更の大きさ、再配分、新しいパラメーター | | `delivery` | 定期的(セラー起動) | ペーシング、支出率、ジオドリフト、チャンネル分布 | `phase` フィールドは省略された場合 `purchase` にデフォルトするため、既存の実装は変更なしに動作し続けます。 ガバナンスエージェントはすべての状態を維持し、`plan_id` + `governance_context` でリクエストを相関させます。セラーはチェック ID をチェーンしたり会話履歴を追跡したりしません — 何が起きたかをポストし、ガバナンスエージェントがコンテキストをルックアップします。 ### Purchase phase セラーが `governance_agents` を持つアカウントで `create_media_buy` リクエストを受け取ったとき: 1. セラーはリクエストを解釈し、その `planned_delivery` を決定します。 2. セラーは `phase: "purchase"`、`plan_id`、`planned_delivery` を伴って `check_governance` を呼び出します。 3. ガバナンスエージェントは計画された配信をキャンペーンプランに対して検証します。 4. `approved` なら、セラーはメディアバイを確認します。 5. `denied` なら、セラーは `GOVERNANCE_DENIED` エラーでメディアバイを拒否します。 6. `conditions` なら、セラーは条件を満たすように計画された配信を調整して再検証するか、拒否します。 ```mermaid theme={null} sequenceDiagram participant O as Orchestrator participant S as Seller participant A as Governance Agent O->>S: create_media_buy(plan_id, packages, ...) S->>S: Interpret request → planned_delivery S->>A: POST check_governance(phase: purchase) A->>A: Validate against plan A-->>S: approved S-->>O: media_buy confirmed (planned_delivery) ``` ### Modification phase セラーが `update_media_buy` リクエストを受け取ったとき: 1. セラーは更新を解釈し、新しい `planned_delivery` を決定します。 2. セラーは `phase: "modification"`、更新された `planned_delivery`、`modification_summary` を伴って `check_governance` を呼び出します。 3. ガバナンスエージェントは `plan_id` + `governance_context` で被管理アクションをルックアップし、変更をプランに対して評価します。 4. `approved` なら、セラーは更新を確認します。 5. `denied` または `conditions` なら、セラーは購入フェーズと同じフローに従います。 ```mermaid theme={null} sequenceDiagram participant O as Orchestrator participant S as Seller participant A as Governance Agent O->>S: update_media_buy(budget: $200K, end: Jul 15) S->>S: Determine new planned_delivery S->>A: POST check_governance(phase: modification) A->>A: Look up media buy, evaluate changes A-->>S: approved S-->>O: media_buy updated ``` ガバナンスエージェントは、初期購入とは異なるロジックを修正に適用できます。例えば、`reallocation_threshold` 内の小さな予算増加は自動承認され、一方で大きな予算増加や新しいジオ市場はより厳格な精査を必要とするかもしれません。 ### Delivery phase セラーは、アクティブな配信中に定期的に `phase: "delivery"` を伴って `check_governance` を呼び出します。これにより、セラーとバイヤーのガバナンスエージェントの間に直接のレポートチャネルが作られます。 1. セラーはレポート期間の配信メトリクスを収集します。 2. セラーは `phase: "delivery"`、現在の `planned_delivery`、`delivery_metrics` を伴って `check_governance` を呼び出します。 3. `approved` なら、レスポンスには `next_check` — セラーが次にレポートすべき時 — が含まれます。 4. `denied` なら、セラーは直ちに配信を一時停止します。 5. `conditions` なら、セラーは配信を調整し(例: ペーシングを遅くする、ジオターゲティングをシフトする)、直ちに再検証します。 ガバナンスエージェントは、購入承認レスポンスに `next_check` を含めることで配信レポートにオプトインします。購入レスポンスに `next_check` がない場合、ガバナンスエージェントは配信レポートを期待しません。 ```mermaid theme={null} sequenceDiagram participant S as Seller participant A as Governance Agent loop Every reporting period S->>A: check_governance(phase: delivery, metrics) A->>A: Check pacing, geo drift, spend rate A-->>S: approved (next_check: +7d) end Note over S,A: If drift detected S->>A: check_governance(phase: delivery, metrics) A-->>S: conditions (slow pacing) S->>S: Adjust delivery S->>A: check_governance(phase: delivery, updated metrics) A-->>S: approved (next_check: +3d) ``` ガバナンスエージェントは `next_check` を通じてレポートケイデンスを制御します。ドリフトや条件を検出したときにケイデンスを締め(より短い間隔)、配信が安定しているときに緩める(より長い間隔)ことができます。ガバナンスエージェントは、見逃した `next_check` の期限を次の配信チェックでの検出事項として扱ってもかまいません(MAY)。 ### Verification examples **購入リクエスト:** ```json theme={null} { "tool": "check_governance", "arguments": { "plan_id": "plan_q1_2026_launch", "caller": "https://seller.example.com", "governance_context": "gc_from_buyer_envelope", "phase": "purchase", "planned_delivery": { "geo": { "countries": ["US"] }, "channels": ["olv"], "start_time": "2026-03-15T00:00:00Z", "end_time": "2026-06-15T00:00:00Z", "total_budget": 150000, "currency": "USD", "frequency_cap": { "max_impressions": 3, "per": "user", "window": { "interval": 1, "unit": "days" } }, "audience_summary": "Adults 25-54, US, premium video inventory", "enforced_policies": ["us_coppa"] } } } ``` **承認(配信オプトイン付き購入):** ```json theme={null} { "check_id": "auth_001", "verdict": "approved", "plan_id": "plan_q1_2026_launch", "explanation": "Planned delivery is within plan parameters. Budget: $150,000 of $500,000 plan total. Geo: US (within plan). Channel: OLV (within 40-70% target range).", "expires_at": "2026-03-15T01:00:00Z", "next_check": "2026-03-22T00:00:00Z" } ``` `next_check` フィールドは、ガバナンスエージェントが配信レポートを期待していることを示します。存在しない場合、配信レポートは期待されません。 **拒否(購入):** ```json theme={null} { "check_id": "auth_002", "verdict": "denied", "plan_id": "plan_q1_2026_launch", "explanation": "Planned delivery targets CA (Canada) which is not an authorized market for this plan.", "findings": [ { "category_id": "strategic_alignment", "severity": "critical", "explanation": "Geo targeting includes CA but plan only authorizes US.", "details": { "plan_countries": ["US"], "planned_countries": ["US", "CA"] } } ] } ``` **承認(配信):** ```json theme={null} { "check_id": "auth_004", "verdict": "approved", "plan_id": "plan_q1_2026_launch", "explanation": "Delivery on track. Week 1 spend: $12,500 of $150,000 (8.3%). Pacing is on target for 13-week flight.", "next_check": "2026-03-29T00:00:00Z" } ``` ### Enforcement アカウントに `governance_agents` が存在する場合、セラーは任意のメディアバイを確認する前に `check_governance` を呼び出さなければなりません(MUST)。バイヤーは、購入が独立して検証されるように特にエンドポイントを提供しました — それをスキップすることは目的を無に帰します。 `governance_agents` が存在しない場合、セラーはメディアバイリクエストを通常どおり処理します。バイヤー側のガバナンスループ(意図チェック → 実行 → `report_plan_outcome`)は依然として適用されますが、セラー側の検証はありません。 セラーは、すべてのアカウントの前提条件としてガバナンスチェックを要求してはなりません(MUST NOT)。`governance_agents` のないアカウントからのメディアバイの処理を拒否するセラーは、キャンペーンガバナンスを使わないバイヤーとの相互運用性を壊します。 `purchase` フェーズのガバナンスが使われる場合でも、`delivery` フェーズは任意です。セラーは、継続的な配信レポートなしに購入承認をサポートしてもかまいません(MAY)。ガバナンスエージェントは、購入レスポンスに `next_check` が存在することを通じて、配信レポートを期待するかどうかを示します。 ガバナンスエージェントに到達できない場合(タイムアウト、ネットワークエラー)、セラーはメディアバイを進めてはなりません(MUST NOT)。ガバナンスチェックは、登録済み `governance_agents` を持つアカウントでの購入確認の前提条件です。セラーは短い遅延の後にチェックを再試行すべきで(SHOULD)、エージェントが到達不能なままなら `GOVERNANCE_UNAVAILABLE` エラーでメディアバイを拒否すべきです。 オーケストレーターがセラーから `GOVERNANCE_UNAVAILABLE` を受け取ったとき、遅延の後に `create_media_buy` を再試行すべきです(SHOULD)。ガバナンスエージェントが利用不可のままなら、オーケストレーターは代替セラーを試みるのではなく人間にエスカレーションすべきです(SHOULD)— ガバナンス障害は同じアカウント上のすべてのセラーに影響します。オーケストレーターからの以前の意図チェック承認は、セラーの実行チェックの代替にはなりません。セラーは独立して検証し、オーケストレーターの承認を使えません。 ### Performance expectations ガバナンスエージェントの実装は、意図チェックについては 5 秒以内、実行チェックについては 10 秒以内に `check_governance` 呼び出しに応答すべきです(SHOULD)。セラーは適切なタイムアウトを設定し、タイムアウトを利用不可と同じように扱うべきです(再試行し、その後 `GOVERNANCE_UNAVAILABLE` で拒否)。 ### Wire format セラーは、MCP over HTTP(Streamable HTTP トランスポート)を使って、登録された URL で各ガバナンスエージェントを呼び出します。リクエストは、ツール名 `check_governance` とツール入力としてのリクエスト引数を持つ MCP `tools/call` 呼び出しです。認証は、`Authorization` ヘッダーのエージェントの `authentication.credentials` からの Bearer トークンを使います。 ### One governance agent per account アカウントは、[`sync_governance`](/docs/accounts/tasks/sync_governance) ごとに正確に 1 つのガバナンスエージェントにバインドされます。登録はスキーマによって単一エージェントです — `governance_agents` は 3.0 が出荷したものであるため配列ですが、負荷を担う不変条件として `maxItems: 1` に制約されています(緩和に向けた段階ではありません)。エンベロープは単一の `governance_context` トークンを運びます。すべてのライフサイクル呼び出しはその 1 つのエージェントにルーティングされます。上限を緩めるには、`sync_governance`、プロトコルエンベロープ、トークンをスレッドするすべてのライフサイクルタスクにまたがる協調的な変更が必要です — その変更は計画されていません。 これは意図的です。ガバナンスプランは単一的です — 予算権限、配信監視、ブランドセーフティ、規制コンプライアンスは、異なる権威が持つ独立した専門分野ではありません。それらは同じプラン状態に対する同じ評価のフェーズとファセットです。 * **認可、忠実性、ドリフトは専門分野ではなくフェーズです。** `check_governance` はすでにそれらを `phase` 軸(`purchase` / `modification` / `delivery`)で分離しています。それらをエージェント間で分割すると、同じプラン状態を別個の権威に分割することになり、ドリフト、不一致、または同じプランの重複した再読み取りしか生成できません。 * **規制ルールはプランにエンコードされ、別個のエージェントが保持しません。** `enforced_policies`、`restricted_attributes`、`policy_ids`、`human_review_required` はプラン自体に存在します。「支出権限」エージェントとは別の「規制コンプライアンス」エージェントは、同じプランを再評価して同じ決定に達するか、乖離します — どちらも有用ではありません。 * **内部の専門家レビューはガバナンスエージェントの内部に属します。** 法務、ブランドセーフティ、カテゴリ専門家のレビューを望むバイヤーは、それらのレビュアーを単一のガバナンスエージェントエンドポイントの背後で構成します(人間レビュー、内部ルーティング、複数レビュアーの合意はすべてガバナンスエージェント内部の関心事)。プロトコルは 1 つのエージェントを見ます。エージェントの内部組織はエージェントの問題です。 * **1 つのライフサイクル、1 つのトークン、1 つの監査証跡。** プランバインディング(`plan_hash`)、署名付きコンテキスト(`governance_context`)、`get_plan_audit_logs` はすべて単一エージェント設計です。単一エージェントが、事後の説明責任(「このトランザクションは時刻 T にエージェント Y によってプラン状態 X の下で認可された」)をクリーンで検証可能なクレームにするものです。 内部の専門家レビュー(法務、ブランドセーフティ、カテゴリ)を必要とするバイヤーは、それらのレビュアーを設定するガバナンスエージェント内部で構成します — プロトコルは分割を表面化しません。 内部分解は `check-governance-response.findings[]` を通じて監査可能です。各検出事項は `category_id`(エージェント内部のタクソノミー — ファーマ MLR、ブランドセーフティ、法務コンプライアンス、どの専門分野がフラグを立てたか)と `policy_id`(検出事項をトリガーした特定のポリシー)を運びます。バイヤーとセラーは 1 つの統合された決定を見ます。検出事項ごとの帰属により、読者は、分割を別個のプロトコルレベルエージェントとして表面化させることなく、ガバナンスエージェント内のどの専門家が拒否や条件に寄与したかを追跡できます。 違反が、プロデューサーがタグ付けしたサーフェス — バイヤーが作成した `feature_requirements[i].policy_id`、クリエイティブエージェントが記録した `creative-feature-result.policy_id`、またはプロパティリストエージェントが発行した `validation-result.features[i].policy_id` — に遡る場合、ガバナンスエージェントはエンドツーエンドのトレーサビリティのためにその `policy_id` を検出事項にエコーします。プロデューサーコントラクトについては [ポリシー帰属](/docs/governance/policy-attribution) を参照。 キャンペーンガバナンスの内部ではなく隣接する関連する専門家レビュー — クリエイティブのブランドセーフティ事前スクリーン、プロパティリストポリシー、コンテンツ標準評価 — は、独自のエージェントと独自のライフサイクルを持つ別個のガバナンスサーフェスです([`build_creative`](/docs/creative/task-reference/build_creative)、プロパティガバナンス、コンテンツ標準ガバナンスを参照)。キャンペーンガバナンスはプランについてのみ語ります。 ### Governance checks and the governance loop ガバナンスチェックはバイヤー側のガバナンスループを補完します。置き換えません。 | Concern | Intent checks (orchestrator, `tool` + `payload`) | Execution checks (seller, `governance_context` + `planned_delivery`) | | ------------- | ------------------------------------------------ | -------------------------------------------------------------------- | | **誰がチェックするか** | オーケストレーターが呼び出すバイヤーのガバナンスエージェント | セラーが呼び出すバイヤーのガバナンスエージェント | | **いつ** | バイヤーがリクエストを送信する前 | 確認前、更新時、配信中 | | **何が検証されるか** | バイヤーの意図したアクション | セラーの計画された、実際の配信 | | **信頼モデル** | 自己申告 | 独立して検証 | | **予算追跡** | Yes(プラン状態) | ガバナンスエージェントが状態を維持 | | **継続的な監視** | `report_plan_outcome` 経由 | `delivery` フェーズ経由 | `delivery` フェーズは、セラーが実際に配信しているものへのリアルタイムの可視性をガバナンスエージェントに与えます。バイヤー側の `report_plan_outcome` はオーケストレーターの正直なレポートに依存します。`delivery` フェーズはセラーから直接レポートを得ます。 バイヤー側とセラー側のガバナンスチェックは同じエージェント — `sync_governance` を通じてアカウントに登録されたもの — に到達します。オーケストレーターは意図チェックのためにそれを呼び出し、セラーは実行チェックのためにそれを呼び出します。両方の会話が同じプラン状態を持つ同じ権威に到達します。 ## Orchestrator integration pattern ```mermaid theme={null} flowchart TD A[Sync plan] --> B[Agent decides to act] B --> C["check_governance(plan_id, tool, payload)"] C --> D{Status?} D -->|approved| E[Send create_media_buy to seller] D -->|conditions| F[Apply conditions] F --> C D -->|denied| G[Log denial, skip action] D -->|async| J[Task goes async — human review internal to governance agent] J --> K[Resolves to approved or denied] K --> C E --> L{Governance agent?} L -->|yes| M["Seller calls check_governance (purchase)"] L -->|no| N[Seller processes normally] M --> O{Approved?} O -->|approved| N O -->|denied| P[Seller rejects media buy] O -->|conditions| Q[Seller adjusts or rejects] N --> R[Receive seller response with planned_delivery] R --> S["report_plan_outcome(plan_id, check_id, outcome)"] S --> T{Status?} T -->|accepted| U[Continue] T -->|findings| V[Review findings, decide next action] V --> U U --> W{Update needed?} W -->|yes| X["check_governance(tool: update_media_buy, payload)"] X --> Y["Seller calls check_governance(governance_context, phase: modification)"] W -->|no| Z{Delivery active?} Z -->|yes| AA["Seller calls check_governance (delivery) periodically"] AA --> AB{Delivery approved?} AB -->|approved| Z AB -->|denied| AC[Seller pauses delivery] AB -->|conditions| AD[Seller adjusts delivery] AD --> Z ``` ガバナンスチェックは、オーケストレーターのアクションループにおける同期呼び出しです。オーケストレーターは、セラーにリクエストを送信する前に `tool` + `payload`(意図チェック)を伴って `check_governance` を呼び出します。セラー側の実行チェックはオーケストレーターに対して透過的です — オーケストレーターは、ガバナンスチェックが設定されているかどうかに関係なく同じ `create_media_buy` リクエストを送信します。修正と配信フェーズのチェックは、オーケストレーターのガバナンスループとは独立に、セラーとガバナンスエージェントの間で発生します。 ## Audit trail すべてのプランは、[`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs) を通じて取得可能な、すべての検証されたアクションとレポートされた結果の順序付き監査証跡を維持します。証跡には次が含まれます。 * チェック ID、タイムスタンプ、ツール * ステータスとカテゴリ評価 * 結果ステータスとコミット済み予算 * 結果レポートからの検出事項 * 内部エスカレーションとその解決(ガバナンスエージェントが記録) * 人間の承認者の識別(人間レビューが内部的に発生した場合) * 時間経過に伴う配信メトリクス この監査証跡はコンプライアンスとレポートのニーズに応えます。規制カテゴリ(政治広告、金融サービス)について、証跡はすべてのトランザクションにガバナンスが適用されたという証拠を提供します。 ## Conformance testing ガバナンスエージェント実装のための適合性テストスイートが計画されています。テストベクターは構造化された入出力ペア — プラン、ポリシーのセット、`check_governance` リクエスト、期待されるレスポンスステータスと検出事項 — を提供します。ガバナンスエージェントはこれらのベクターを実行して、ポリシー評価が一貫した結果を生成することを検証できます。 ポリシーレジストリの exemplar(ポリシーごとの合否シナリオ)が原材料を提供します。テストベクターはこれらを、任意のガバナンスエージェントが検証できる実行可能なアサーションに形式化します。AdCP クライアントテストライブラリは、標準テストスイートの一部としてこれらのベクターを含めます。 ## Property list governance キャンペーンガバナンスは、メディアバイがプロパティリストを参照するときにプロパティガバナンスと交差します。ガバナンスエージェントは、メディアバイリクエストで参照されるプロパティリストがプランのブランドセーフティとコンプライアンス要件を満たすことを検証してもかまいません(MAY)。これにより、プロパティリストがブランドのコンプライアンス設定と強制されたポリシーに整合することを保証します。 # check_governance Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/campaign/tasks/check_governance check_governance は AdCP における汎用的な検証ゲートです。オーケストレーターとセラーは、キャンペーンアクションを実行する前にこれを呼び出します。 # check\_governance **実験的機能。** キャンペーンガバナンス(`sync_plans`、`check_governance`、`report_plan_outcome`、`get_plan_audit_logs`)は、実験的サーフェスとして AdCP 3.0 の一部です — 少なくとも 6 週間の予告をもって 3.x リリース間で変更される可能性があります。これを実装するセラーは `experimental_features` で `governance.campaign` を宣言しなければなりません(MUST)。完全なコントラクトについては [実験的ステータス](/docs/reference/experimental-status) を参照。 キャンペーンアクションのための汎用ガバナンスチェック。オーケストレーター(バイヤー側)とセラーの両方がこのタスクを呼び出します。ガバナンスエージェントは、存在するフィールドからチェックタイプを推論します。 | Check type | Who calls | Discriminating fields | Purpose | | ------------- | --------- | ----------------------------------------- | --------------------------------------------------------- | | **Intent** | オーケストレーター | `tool` + `payload` | セラーに送信する前に意図したアクションを検証。予算はコミットされない。 | | **Execution** | セラー | `planned_delivery` + `governance_context` | セラーが実際に配信するものを検証。予算は後で `report_plan_outcome` を通じてコミットされる。 | ガバナンスエージェントがすべての状態を維持します。呼び出し元はチェック ID をチェーンしたり会話履歴を追跡したりしません — アクションをポストし、ガバナンスエージェントが `plan_id` で相関させます。後続のライフサイクルチェックでは、呼び出し元は継続性のために前のレスポンスの `governance_context` を含めます。 アカウントは1つのガバナンスエージェントにバインドされます([`sync_governance`](/docs/accounts/tasks/sync_governance) と [アカウントごとに1つのガバナンスエージェント](/docs/governance/campaign/specification#one-governance-agent-per-account) を参照)。被管理アクションのすべてのライフサイクル呼び出しは、その同じエージェントに送られます。 **専門家ごとのレビューはここに表面化します。** 内部的な分解(法務、ブランドセーフティ、カテゴリ)は別個のエンドポイントとして公開されません — `categories_evaluated`、および `denied`、`conditions`、情報提供的な `approved` レスポンスでは `findings[].details` に、エージェント内部のラベルとして現れます。これらの値は不透明な監査値として扱ってください。固定リストに対してパターンマッチングしないでください。 ## チェックタイプ ### 意図チェック(オーケストレーター) オーケストレーターは、セラーにツール呼び出しを送信する前に、`tool` と `payload` を伴って `check_governance` を呼び出します。ガバナンスエージェントは意図したアクションをキャンペーンプランに照らして評価します。 1. オーケストレーターがセラーツール(例: `create_media_buy`)を呼び出すことを決定する 2. オーケストレーターがツール名と完全なペイロードを伴って `check_governance` を呼び出す 3. `approved` なら、オーケストレーターはツール呼び出しをセラーに送信する 4. `denied` なら、オーケストレーターはツール呼び出しを送信しない 5. `conditions` なら、オーケストレーターはペイロードを調整して `check_governance` を再呼び出しする 6. ガバナンスエージェントが人間のレビューを必要とする場合、タスクは非同期になり、最終的に `approved` または `denied` に解決する ### 実行チェック(セラー) セラーは、ガバナンスエージェントが設定された([`sync_governance`](/docs/accounts/tasks/sync_governance) で設定)アカウントでリクエストを処理する際に、`governance_context` と `planned_delivery` を伴って `check_governance` を呼び出します。実行チェックは常に拘束的です — ガバナンスエージェントが拒否した場合、セラーは進めてはなりません。 チェックを実行する前に、セラーはバイヤーからプロトコルエンベロープに到着した署名付き `governance_context` トークンを検証します。バイヤーは**意図フェーズ**のトークンを生成します([JWS プロファイル](/docs/building/by-layer/L1/security#adcp-jws-プロファイル)に従う)。セラー自身の実行チェックは、ライフサイクルの残りについて、割り当てられた `media_buy_id` にバインドされた `purchase`/`modification`/`delivery` フェーズのトークンを生成します。 ``` on receive(create_media_buy request): token = request.envelope.governance_context persist(token) # always persist for audit/forwarding verify(token, { # per Security — Signed Governance Context sellerId: my_adagents_url, planId: request.plan_id, phase: "intent", # buyer produces intent tokens mediaBuyId: null, # intent tokens have no media_buy_id }) # throws on any of 15 checks failing call check_governance(planned_delivery, token) # seller-side execution check — produces purchase-phase token proceed only if governance_agent verdict = approved ``` 検証をまだ実装していないセラーも、トークンを変更せずに永続化して転送しなければなりません(MUST)— 監査人や規制当局はこれに依存します。検証は「転送のみ」のコンプライアンスから暗号的な説明責任へのランプであり、段階的に採用できます。 実行チェックは、3 つのフェーズを通じてメディアバイのライフサイクル全体をカバーします。 | Phase | When | What's checked | | -------------- | ----------------------- | ------------------------ | | `purchase` | `create_media_buy` の確認前 | 予算、ジオ、チャンネル、フライト日程、ポリシー | | `modification` | `update_media_buy` の確認前 | 変更の大きさ、再配分、新しいパラメーター | | `delivery` | 配信中に定期的に | ペーシング、支出率、ジオドリフト、チャンネル分布 | セラーは、コミット済みガバナンスチェックを段階的に採用できます。 * **レベル 1: 購入のみ** — `create_media_buy` ごとに 1 回の呼び出し。最小限の実行可能な統合。 * **レベル 2: + 変更** — `update_media_buy` ごとに 1 回の呼び出し。 * **レベル 3: + 配信レポート** — アクティブな配信中の定期的な呼び出し。 ## 呼び出し要件 プランにガバナンスエージェントが設定されている場合、バイヤーエージェントはすべての支出コミットリクエスト(`create_media_buy`、`update_media_buy`、`acquire_rights`、`update_rights`、`activate_signal`、`build_creative`)の前に `check_governance` を呼び出さなければなりません(MUST)— 例外なく。ドル下限も、異常しきい値も、コールドスタート免除もありません。すべてのコミットがガバナンスエージェントを通ります。バイヤー側の呼び出しは意図チェック(`tool` + `payload`)であり、バイヤーがセラーへのリクエストに添付する意図フェーズの `governance_context` トークンを生成します。ガバナンスエージェントは、プランの `budget.reallocation_threshold` と `human_review_required` フィールドに従って、自動承認、条件適用、拒否、または人間のレビューへのエスカレーションを内部的に決定します。 セラー側の強制が、[署名付き `governance_context` トークン](/docs/building/by-layer/L1/security#署名付きガバナンスコンテキスト)を通じて MUST を実効化します。ガバナンスエージェントが設定されたプランの支出コミットを受け取ったセラーは、有効で期限内の意図フェーズトークン(`phase: "intent"`、`sub` がプラン ID に等しい、`aud` がこのセラー宛て)を要求しなければならず(MUST)、そうでなければ `PERMISSION_DENIED` で拒否しなければなりません(MUST)。次にセラーは自身の実行チェックを実行します — `planned_delivery` と受け取った `governance_context` を伴って `check_governance` を呼び出す — これがライフサイクルの残りについて、新たに割り当てられた `media_buy_id` にバインドされた `purchase` フェーズのトークンを生成します。意図チェックをスキップするバイヤーは有効な意図トークンを生成できないため、コミットはセラーが実行チェックに到達する前に拒否されます。 プランにガバナンスエージェントが設定されていない場合、`check_governance` の呼び出しは必須でも意味もありません — 呼び出す対象がありません。セラーは、独自の商業ポリシーの問題として、ガバナンスエージェントが設定されていないプランでの取引を拒否してもかまいません(MAY)。 完全な定義(監査要件、セラー側の保持 MUST、冪等性との相互作用を含む)については [仕様](/docs/governance/campaign/specification#spend-commit-invocation) を参照。 ## ステータス値 | Status | Meaning | Caller action | | ------------ | ------------------ | ----------------------------------------------- | | `approved` | 計画通り進める。 | `expires_at` の前に行動するか、再呼び出しする。 | | `denied` | 進めない。 | 上流の呼び出し元にエラーを返す。 | | `conditions` | 呼び出し元が調整を受け入れれば承認。 | 条件を適用し、調整したパラメーターで `check_governance` を再呼び出しする。 | ### 期限切れ `expires_at` は `verdict` が `approved` または `conditions` の場合に存在します。失効した承認は承認ではありません — 呼び出し元は進める前に `check_governance` を再呼び出ししなければなりません。 ### 条件 `verdict` が `conditions` の場合、呼び出し元は進める前に調整したパラメーターで `check_governance` を再呼び出ししなければなりません(MUST)。`required_value` を持つ条件は機械処理可能です — 呼び出し元はプログラム的に値を適用できます。`required_value` のない条件はアドバイザリです — 呼び出し元は `reason` を解釈してそれに応じて調整すべきです。 ガバナンスエージェントは、同じアクションに対する 3 回の失敗した再呼び出しの後、(`conditions` ではなく)`denied` を返すべきです(SHOULD)。これは無限の交渉ループを防ぎます。特に、セラーがキャンペーンプランを見えないセラー側チェックで有効です。 ### 人間によるレビュー ガバナンスエージェントが人間のレビューが必要と判断した場合(例: アクションがプランの `reallocation_threshold` を超える、またはプランが `human_review_required: true` を運ぶ)、エージェントは内部的にエスカレーションを処理します。`check_governance` タスクは非同期になります — 呼び出し元は標準の非同期タスクライフサイクルステータス(`submitted`、`working`)を受け取り、人間が行動すると最終的に `approved` または `denied` を得ます。呼び出し元は、非同期タスクをサポートすること以外に、このケースの特別な処理を必要としません([タスクライフサイクル](/docs/building/by-layer/L3/task-lifecycle)を参照)。 `committed` チェック(セラー側)では、セラーがタイムアウトを設定します。ガバナンスエージェントがタイムアウト内に応答しない場合、セラーはそれを `denied` として扱い、オーケストレーターにエラーを返します。オーケストレーターは、ガバナンスエージェントが解決した後にメディアバイを再開始できます。 ### 結果とのリンク レスポンスには `check_id` が含まれます。これを [`report_plan_outcome`](./report_plan_outcome) で使い、結果をそれを認可したガバナンスチェックにリンクします。 ## ガバナンスエージェントが利用不可の場合 ガバナンスエージェントが設定されていて、呼び出し元がそれに到達できない場合(タイムアウト、ネットワークエラー)、呼び出し元は進めてはなりません(MUST NOT)。ガバナンスはゲートです — ゲートに到達できないとき、デフォルトは停止です。呼び出し元はバックオフを伴って再試行し、失敗を上流にレポートすべきです(SHOULD)。 ## 配信ケイデンス レスポンスに `next_check` が存在することは、ガバナンスエージェントが継続的な配信レポートを期待しているというシグナルです。セラーは `next_check` の時刻までに呼び出すべきです(SHOULD)。ガバナンスエージェントは、期限の見逃しを次の配信チェックでの検出事項として扱ってもかまいません(MAY)。 ## リクエスト ### 意図チェック(オーケストレーターがセラーに送信する前にチェック) ```json theme={null} { "tool": "check_governance", "arguments": { "plan_id": "plan_q1_2026_launch", "caller": "https://orchestrator.example.com", "tool": "create_media_buy", "payload": { "product_id": "premium_video_300k", "budget": 150000, "currency": "USD", "geo": { "countries": ["US"] }, "channels": ["olv"], "flight": { "start": "2026-03-15T00:00:00Z", "end": "2026-06-15T00:00:00Z" } } } } ``` 最初の `check_governance` 呼び出しで、ガバナンスエージェントは `payload` から必要なものを抽出します。レスポンスには、呼び出し元がプロトコルエンベロープに添付し、この被管理アクションの後続のすべてのガバナンス呼び出しに含める `governance_context` 文字列が含まれます。3.0 では、ガバナンスエージェントは、セラーが真正性、認可スコープ、鮮度(15 ステップのセラーチェックリスト)を検証できるよう、[AdCP JWS プロファイル](/docs/building/by-layer/L1/security#署名付きガバナンスコンテキスト)に従って署名されたコンパクト JWS を発行しなければなりません(MUST)。トークンは必須の `plan_hash` 監査層クレームも運びます — 正規化ルール、保持義務、およびガバナンスエージェントの実装者が出荷前に検証すべき 11 個の参照ベクターについては [プランバインディングと監査](/docs/governance/campaign/specification#plan-binding-and-audit) を参照。 ### 意図チェック(権利ライセンス) ```json theme={null} { "tool": "check_governance", "arguments": { "plan_id": "plan_acme_summer_2026", "caller": "https://buying.pinnacle-agency.example", "purchase_type": "rights_license", "tool": "acquire_rights", "payload": { "brand": { "domain": "acmeoutdoor.com" }, "right_type": "image_generation", "pricing_option_id": "standard_monthly", "campaign": { "countries": ["US"], "start_date": "2026-04-01", "end_date": "2026-06-30" } } } } ``` ### 実行チェック — purchase ```json theme={null} { "tool": "check_governance", "arguments": { "plan_id": "plan_q1_2026_launch", "caller": "https://seller.example.com", "governance_context": "gc_from_buyer_envelope", "phase": "purchase", "planned_delivery": { "geo": { "countries": ["US"] }, "channels": ["olv"], "start_time": "2026-03-15T00:00:00Z", "end_time": "2026-06-15T00:00:00Z", "total_budget": 150000, "currency": "USD", "frequency_cap": { "max_impressions": 3, "per": "user", "window": { "interval": 1, "unit": "days" } }, "audience_summary": "Adults 25-54, US, premium video inventory", "enforced_policies": ["us_coppa"] } } } ``` ### 実行チェック — modification ```json theme={null} { "tool": "check_governance", "arguments": { "plan_id": "plan_q1_2026_launch", "caller": "https://seller.example.com", "governance_context": "gc_from_buyer_envelope", "phase": "modification", "modification_summary": "Budget increase from $150,000 to $200,000 and flight extension to 2026-07-15.", "planned_delivery": { "geo": { "countries": ["US"] }, "channels": ["olv"], "start_time": "2026-03-15T00:00:00Z", "end_time": "2026-07-15T00:00:00Z", "total_budget": 200000, "currency": "USD", "frequency_cap": { "max_impressions": 3, "per": "user", "window": { "interval": 1, "unit": "days" } }, "audience_summary": "Adults 25-54, US, premium video inventory", "enforced_policies": ["us_coppa"] } } } ``` ### 実行チェック — delivery ```json theme={null} { "tool": "check_governance", "arguments": { "plan_id": "plan_q1_2026_launch", "caller": "https://seller.example.com", "governance_context": "gc_from_buyer_envelope", "phase": "delivery", "planned_delivery": { "geo": { "countries": ["US"] }, "channels": ["olv"], "start_time": "2026-03-15T00:00:00Z", "end_time": "2026-06-15T00:00:00Z", "total_budget": 150000, "currency": "USD", "frequency_cap": { "max_impressions": 3, "per": "user", "window": { "interval": 1, "unit": "days" } }, "audience_summary": "Adults 25-54, US, premium video inventory", "enforced_policies": ["us_coppa"] }, "delivery_metrics": { "reporting_period": { "start": "2026-03-15T00:00:00Z", "end": "2026-03-22T00:00:00Z" }, "spend": 12500, "cumulative_spend": 12500, "impressions": 850000, "cumulative_impressions": 850000, "geo_distribution": { "US": 100 }, "channel_distribution": { "olv": 100 }, "pacing": "on_track", "audience_distribution": { "baseline": "platform", "indices": { "age:18-24": 0.8, "age:25-34": 1.4, "age:35-44": 1.3, "age:45-54": 1.1, "gender:female": 1.05, "gender:male": 0.95 }, "cumulative_indices": { "age:18-24": 0.85, "age:25-34": 1.35, "age:35-44": 1.25, "age:45-54": 1.1, "gender:female": 1.03, "gender:male": 0.97 } } } } } ``` ## レスポンス ### approved(意図チェック) ```json theme={null} { "check_id": "chk_001", "verdict": "approved", "plan_id": "plan_q1_2026_launch", "explanation": "Proposed create_media_buy is within plan parameters. Budget: $150,000 of $500,000 plan total. Geo: US (within plan). Channel: OLV (within 40-70% target range).", "categories_evaluated": ["budget_authority", "geo_compliance", "channel_compliance", "flight_compliance", "delegation_authority"], "policies_evaluated": ["us_coppa", "alcohol_advertising"], "expires_at": "2026-03-15T01:00:00Z" } ``` オーケストレーターは `expires_at` の前に `create_media_buy` をセラーに送信します。 ### approved(実行チェック — 配信オプトイン付き purchase) ```json theme={null} { "check_id": "chk_002", "verdict": "approved", "plan_id": "plan_q1_2026_launch", "explanation": "Planned delivery is within plan parameters. Budget: $150,000 of $500,000 plan total. Geo: US (within plan). Channel: OLV (within 40-70% target range).", "mode": "enforce", "expires_at": "2026-03-15T01:00:00Z", "next_check": "2026-03-22T00:00:00Z" } ``` セラーはメディアバイを進めます。`next_check` の存在は、ガバナンスエージェントがその時刻から配信レポートを期待していることを示します。 ### approved(実行チェック — delivery) ```json theme={null} { "check_id": "chk_003", "verdict": "approved", "plan_id": "plan_q1_2026_launch", "explanation": "Delivery on track. Week 1 spend: $12,500 of $150,000 (8.3%). Pacing is on target for 13-week flight. Geo and channel distribution match plan parameters.", "next_check": "2026-03-29T00:00:00Z" } ``` セラーは配信を続け、次のガバナンスチェックを `next_check` にスケジュールします。 ### denied(意図チェック) ```json theme={null} { "check_id": "chk_004", "verdict": "denied", "plan_id": "plan_q1_2026_launch", "explanation": "Proposed media buy targets CA (Canada) which is not within the plan's geography.", "findings": [ { "category_id": "strategic_alignment", "severity": "critical", "explanation": "Geo targeting includes CA but plan only covers US.", "details": { "plan_countries": ["US"], "payload_countries": ["US", "CA"] } } ] } ``` オーケストレーターはツール呼び出しをセラーに送信してはなりません(MUST NOT)。 ### denied(実行チェック — delivery ジオドリフト) ```json theme={null} { "check_id": "chk_005", "verdict": "denied", "plan_id": "plan_q1_2026_launch", "explanation": "Delivery has drifted outside plan parameters. 12% of impressions delivered in CA (Canada) which is not within the plan's geography.", "findings": [ { "category_id": "strategic_alignment", "severity": "critical", "confidence": 0.98, "explanation": "Geo distribution shows 12% delivery in CA, but plan only covers US.", "details": { "plan_countries": ["US"], "actual_distribution": { "US": 88, "CA": 12 } } } ] } ``` セラーは直ちに配信を一時停止し、再開する前にジオターゲティングを修正しなければなりません(MUST)。 ### conditions(実行チェック — purchase) ```json theme={null} { "check_id": "chk_006", "verdict": "conditions", "plan_id": "plan_q1_2026_launch", "explanation": "Budget approved but frequency cap must be applied per brand policy.", "conditions": [ { "field": "planned_delivery.frequency_cap", "required_value": { "max_impressions": 5, "per": "user", "window": { "interval": 1, "unit": "days" } }, "reason": "Brand policy requires daily frequency cap of 5 or fewer impressions per user." } ], "expires_at": "2026-03-15T01:00:00Z" } ``` セラーは計画された配信を調整し、進める前に更新したパラメーターで `check_governance` を再呼び出ししなければなりません(MUST)。 ### conditions(実行チェック — delivery オーバーペーシング) ```json theme={null} { "check_id": "chk_007", "verdict": "conditions", "plan_id": "plan_q1_2026_launch", "explanation": "Delivery is pacing 40% ahead of schedule. Cumulative spend of $42,000 after 2 weeks exceeds expected $23,000 for this point in the flight.", "conditions": [ { "field": "pacing", "reason": "Reduce daily spend rate to align with the planned flight duration. At current pace, budget will be exhausted by week 7 of 13." } ], "next_check": "2026-03-31T00:00:00Z" } ``` セラーはペーシングを調整し、直ちに `check_governance` を再呼び出ししなければなりません(MUST)。`next_check` は、ガバナンスエージェントが修正を検証できるよう通常より近くに設定されます。 ## フィールド ### リクエスト | Field | Type | Required | Description | | ------------------------------------------------------------- | ------------ | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `plan_id` | string | Yes | キャンペーンガバナンスプラン識別子。 | | `caller` | string (URI) | Yes | リクエストを行うエージェントの URL。 | | `purchase_type` | enum | No | 検証される金銭的コミットメントの種類: `media_buy`(デフォルト)、`rights_license`、`signal_activation`、または `creative_services`。省略した場合、ガバナンスエージェントは `media_buy` を想定します。 | | `tool` | string | Intent | チェックされる AdCP ツール。意図チェック(オーケストレーター)に存在します。ガバナンスエージェントは `tool` + `payload` の存在で意図チェックを識別します。 | | `payload` | object | Intent | セラーに送信される完全なツール引数。意図チェックに存在します。 | | `governance_context` | string | No | 前の `check_governance` レスポンスからのガバナンスコンテキストトークン。後続のライフサイクルチェックに含めることで、ガバナンスエージェントが継続性を維持できます。実行チェックでは、ガバナンスエージェントは `governance_context` + `planned_delivery` でチェックを識別します。これは、すべての購入タイプにわたる唯一のライフサイクル相関子です。JWS プロファイルとセラー検証については [署名付きガバナンスコンテキスト](/docs/building/by-layer/L1/security#署名付きガバナンスコンテキスト) を、`plan_hash` 監査層クレームについては [プランバインディングと監査](/docs/governance/campaign/specification#plan-binding-and-audit) を参照。 | | `phase` | enum | Execution | `purchase`、`modification`、または `delivery`。デフォルトは `purchase`。実行チェックに存在します。 | | `planned_delivery` | object | Execution | 実際に配信されるもの。実行チェックに存在します。[planned delivery](/docs/governance/campaign/specification#integration-with-create_media_buy) を参照。 | | `delivery_metrics` | object | Delivery | 実際の配信パフォーマンスデータ。`phase` が `delivery` の場合に必須。 | | `delivery_metrics.audience_distribution` | object | No | ベースラインに対するオーディエンスの人口構成。バイアス/公平性ドリフト検出に使用。 | | `delivery_metrics.audience_distribution.baseline` | enum | Yes | 参照母集団: `census`(全国人口)、`platform`(プラットフォームのユーザーベース)、または `custom`。 | | `delivery_metrics.audience_distribution.baseline_description` | string | No | `baseline` が `custom` の場合のベースラインの説明(例: "US adults 18+ with broadband access")。 | | `delivery_metrics.audience_distribution.indices` | object | Yes | 現在のレポート期間のインデックス値。キー形式: `dimension:value`(例: `age:25-34`、`gender:female`)。値 1.0 はベースラインとの同等、1.0 超はオーバーインデックス、1.0 未満はアンダーインデックスを意味します。 | | `delivery_metrics.audience_distribution.cumulative_indices` | object | No | すべてのレポート期間にわたるインデックス値。`indices` と同じ形式。ガバナンスエージェントが単一期間のノイズ対トレンドを検出するのに役立ちます。 | | `modification_summary` | string | No | 何が変わったかの人間可読な要約。`modification` フェーズで存在すべきです(SHOULD)。 | ### 配信メトリクス | Field | Type | Description | | ------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `reporting_period` | object | `start` と `end` のタイムスタンプ(ISO 8601)を持つレポートウィンドウ。必須。 | | `spend` | number | レポート期間中の支出。 | | `cumulative_spend` | number | メディアバイ開始以降の総支出。 | | `impressions` | integer | レポート期間中のインプレッション。 | | `cumulative_impressions` | integer | メディアバイ開始以降の総インプレッション。 | | `geo_distribution` | object | 実際の地理的分布。キーは ISO 3166-1 alpha-2 コード、値はパーセンテージ。 | | `channel_distribution` | object | 実際のチャンネル分布。キーは channels enum の値、値はパーセンテージ。 | | `pacing` | enum | `ahead`、`on_track`、または `behind`。 | | `audience_distribution` | object | ベースラインに対するオーディエンス構成。`baseline`(enum)、任意の `baseline_description`(string、カスタムベースライン用)、`indices`(現在の期間)、任意の `cumulative_indices`(全期間)を含みます。キーは `dimension:value` 文字列、値はインデックス数値(1.0 が同等)。 | ### レスポンス | Field | Type | Description | | ---------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `check_id` | string | このガバナンスチェックの一意識別子。`report_plan_outcome` で結果をリンクするのに使います。 | | `verdict` | enum | `approved`、`denied`、または `conditions`。 | | `plan_id` | string | リクエストからエコー。 | | `explanation` | string | 決定の人間可読な説明。 | | `findings` | array | カテゴリごとに見つかった問題。`verdict` が `denied` または `conditions` の場合に存在。情報提供的な検出事項については `approved` にも存在してもかまいません(MAY)。各検出事項は `category_id`、`severity`、`explanation`、任意で `policy_id`、`details`、`confidence`(0-1)、`uncertainty_reason` を持ちます。`category_id` は**エージェント内部のラベル**であり、プロトコルレベルの enum ではありません — 表示/監査用に不透明として扱い、機械的パターンマッチングには使わないでください。 | | `conditions` | array | `verdict` が `conditions` の場合に存在。呼び出し元が再呼び出し前に行うべき調整。 | | `categories_evaluated` | string\[] | このチェック中に評価されたガバナンスカテゴリ(例: `budget_authority`、`geo_compliance`、`channel_compliance`)。**エージェント内部のラベル** — 各文字列はガバナンスエージェントのポリシーモデルによって定義され、内部の専門家レビュー(法務、ブランドセーフティ、カテゴリ)がエージェントの単一エンドポイントの背後から監査用に表面化する手段です。プロトコル enum ではなく、固定リストに対してパターンマッチングするのは安全ではありません。 | | `policies_evaluated` | string\[] | このチェック中に評価されたレジストリポリシー ID。 | | `mode` | enum | `audit`、`advisory`、または `enforce` — このチェックが評価されたときにアクティブだったガバナンスモード。ガバナンスエージェントがチェック時のランタイム設定から記録します。プランフィールドからではありません。取引相手、規制当局、監査人が、`approved` の決定が意図的な `enforce` の強制を反映するのか `audit` モードのサイレントログを反映するのかを区別できるようにします。 | | `expires_at` | string | `verdict` が `approved` または `conditions` の場合に存在。呼び出し元はこの時刻の前に行動するか再呼び出ししなければなりません。失効した承認は承認ではありません。 | | `next_check` | string | セラーが次に配信メトリクスを伴って `check_governance` を呼び出すべき時刻。ガバナンスエージェントが継続的な配信レポートを期待する場合に存在。 | | `governance_context` | string | この被管理アクションのガバナンスコンテキストトークン。`verdict` が `approved` または `conditions` の場合に存在。プロトコルエンベロープに添付し、後続のすべてのガバナンス呼び出しに含めます。これはすべての購入タイプの唯一のライフサイクル相関子です。JWS プロファイルとセラー検証については [署名付きガバナンスコンテキスト](/docs/building/by-layer/L1/security#署名付きガバナンスコンテキスト) を、`plan_hash` 監査層クレームについては [プランバインディングと監査](/docs/governance/campaign/specification#plan-binding-and-audit) を参照。検証しないセラーも、トークンをそのまま永続化して転送しなければなりません(MUST)。 | | `authority_remaining` | object | このチェック後に残るバイヤー側のプラン予算権限 — セラーの割り当て予算ではありません。実行チェックで `verdict` が `approved` または `conditions` の場合に存在。`budget_remaining`(number)、`currency`(string)、`budget_used_pct`(number、0-100)を含みます。オーケストレーターはこれを使って、メディアプランの総権限に対するプランレベルの支出を追跡します。 | ## エラーコード | Code | Recovery | Description | | ----------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------- | | `PLAN_NOT_FOUND` | correctable | この ID のプランがありません。バイヤーがまだプランを同期していない可能性があります。 | | `AMBIGUOUS_CHECK_TYPE` | correctable | リクエストが意図フィールド(`tool` + `payload`)と実行フィールド(`governance_context` + `planned_delivery`)の両方を含んでいます。いずれか一方のセットを送信してください。 | | `CAMPAIGN_SUSPENDED` | correctable | キャンペーンガバナンスが人間のレビュー待ちで一時停止されています。 | | `SELLER_NOT_RECOGNIZED` | correctable | 呼び出し元 URL がプランの `approved_sellers` リストにありません。 | ## 関連タスク * [`sync_plans`](./sync_plans) — このガバナンスチェックが照合するプラン * [`report_plan_outcome`](./report_plan_outcome) — アクションが確認された後に何が起きたかをレポート * [`get_plan_audit_logs`](./get_plan_audit_logs) — プラン状態と監査証跡を表示 # get_plan_audit_logs Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/campaign/tasks/get_plan_audit_logs get_plan_audit_logs は AdCP キャンペーンプランまたはポートフォリオのガバナンス状態、予算追跡、完全な監査証跡を取得します。 # get\_plan\_audit\_logs プラン、複数のプラン、またはポートフォリオ全体のガバナンス状態と監査証跡を取得します。予算追跡、検証履歴、コンプライアンスサマリーを返します。 ## リクエスト ```json theme={null} { "tool": "get_plan_audit_logs", "arguments": { "plan_ids": ["plan_q1_2026_launch"], "include_entries": true } } ``` 結果は各プラン内で `media_buy_id` ごとにグループ化されます。 **複数プラン** — 1回の呼び出しで特定のプランを取得します: ```json theme={null} { "tool": "get_plan_audit_logs", "arguments": { "plan_ids": ["plan_q1_2026_launch", "plan_q1_2026_emea"], "include_entries": false } } ``` **ポートフォリオクエリ** — 1つ以上のポートフォリオのすべてのメンバープランの統合監査データを取得します: ```json theme={null} { "tool": "get_plan_audit_logs", "arguments": { "portfolio_plan_ids": ["portfolio_nova_brands_2026"], "include_entries": true } } ``` `plan_ids` と `portfolio_plan_ids` を組み合わせて、1回の呼び出しで特定のプランとポートフォリオの両方をクエリできます。 ## レスポンス ```json theme={null} { "plans": [ { "plan_id": "plan_q1_2026_launch", "plan_version": 1, "status": "active", "budget": { "authorized": 500000, "committed": 425000, "remaining": 75000, "utilization_pct": 85 }, "channel_allocation": { "olv": { "committed": 275000, "pct": 55 }, "display": { "committed": 150000, "pct": 30 } }, "media_buys": [ { "media_buy_id": "mb_seller_456", "status": "active", "committed": 275000, "check_count": 8 }, { "media_buy_id": "mb_seller_789", "status": "active", "committed": 150000, "check_count": 7 } ], "summary": { "checks_performed": 15, "outcomes_reported": 12, "statuses": { "approved": 12, "denied": 1, "conditions": 1, "escalated": 1 }, "findings_count": 2, "escalations": [ { "check_id": "chk_esc_001", "reason": "Budget reallocation exceeds threshold", "resolution": "approved_by_human", "resolved_at": "2026-03-16T09:30:00Z" } ], "drift_metrics": { "escalation_rate": 0.07, "escalation_rate_trend": "stable", "auto_approval_rate": 0.80, "human_override_rate": 0.02, "mean_confidence": 0.88, "thresholds": { "escalation_rate_min": 0.02, "auto_approval_rate_max": 0.95, "human_override_rate_max": 0.15 } } }, "entries": [ { "id": "chk_001", "type": "check", "timestamp": "2026-03-10T10:05:00Z", "caller": "https://orchestrator.pinnacle-media.com/agent", "tool": "get_products", "status": "approved", "binding": "proposed", "explanation": "Product discovery within budget and channel constraints.", "categories_evaluated": ["budget_authority", "strategic_alignment"], "policies_evaluated": ["us_coppa"] }, { "id": "chk_003", "type": "check", "timestamp": "2026-03-15T11:05:00Z", "caller": "https://ads.seller-example.com/adcp", "tool": "create_media_buy", "status": "approved", "binding": "committed", "explanation": "Media buy within plan budget ($150,000 of $500,000 remaining). Geo targeting matches authorized markets. COPPA compliance verified.", "categories_evaluated": ["budget_authority", "regulatory_compliance", "brand_policy"], "policies_evaluated": ["us_coppa", "alcohol_advertising"], "findings": [ { "category_id": "budget_authority", "severity": "info", "explanation": "Budget utilization at 70% after this buy." } ] }, { "id": "out_001", "type": "outcome", "timestamp": "2026-03-15T11:10:00Z", "caller": "https://orchestrator.pinnacle-media.com/agent", "outcome": "completed", "committed_budget": 150000 }, { "id": "out_del_001", "type": "outcome", "timestamp": "2026-03-22T00:00:00Z", "caller": "https://ads.seller-example.com/adcp", "outcome": "delivery", "media_buy_id": "mb_seller_456", "outcome_status": "accepted" } ] } ] } ``` ## フィールド ### リクエスト | フィールド | 型 | 必須 | 説明 | | -------------------- | --------- | ----------------------------------------- | ----------------------------- | | `plan_ids` | string\[] | `plan_ids` または `portfolio_plan_ids` のいずれか | 取得するプラン ID。 | | `portfolio_plan_ids` | string\[] | `plan_ids` または `portfolio_plan_ids` のいずれか | ポートフォリオプラン ID。メンバープランに展開されます。 | | `include_entries` | boolean | No | 完全な監査証跡を含めます。デフォルト: `false`。 | ### レスポンス | フィールド | 型 | 説明 | | ------------------------------------------------------------------ | ------- | -------------------------------------------------------------------------------------------- | | `plans` | array | リクエストされた各プランの監査データ。 | | `plans[].plan_id` | string | プラン識別子。 | | `plans[].plan_version` | number | 現在のプランバージョン。 | | `plans[].status` | enum | `active`、`suspended`、または `completed`。 | | `plans[].budget` | object | 予算状態。 | | `plans[].budget.authorized` | number | プランで認可された総予算。 | | `plans[].budget.committed` | number | 確認済みの結果からコミットされた総予算。 | | `plans[].budget.remaining` | number | 認可額からコミット済み額を引いたもの。 | | `plans[].budget.utilization_pct` | number | 認可額に対するコミット済み額のパーセンテージ。 | | `plans[].channel_allocation` | object | 現在のチャンネルミックス。チャンネル ID をキーとします。 | | `plans[].channel_allocation[channel].committed` | number | このチャンネルにコミットされた予算。 | | `plans[].channel_allocation[channel].pct` | number | 認可された総予算に対するチャンネルのシェア。 | | `plans[].media_buys` | array | メディアバイごとの内訳。 | | `plans[].media_buys[].media_buy_id` | string | セラーが割り当てたメディアバイ識別子。 | | `plans[].media_buys[].status` | enum | `active`、`suspended`、または `completed`。 | | `plans[].media_buys[].committed` | number | このメディアバイにコミットされた予算。 | | `plans[].media_buys[].check_count` | integer | 実行されたガバナンスチェックの数。 | | `plans[].summary` | object | 集計された検証と結果の統計。 | | `plans[].summary.checks_performed` | number | 実行されたガバナンスチェックの総数。 | | `plans[].summary.outcomes_reported` | number | 報告された結果の総数。 | | `plans[].summary.statuses` | object | 各ガバナンスチェックステータスのカウント(`approved`、`denied`、`conditions`、`escalated`)。 | | `plans[].summary.findings_count` | number | すべてのチェックと結果にわたる検出事項の総数。 | | `plans[].summary.escalations` | array | すべてのエスカレーションとその解決策。 | | `plans[].summary.escalations[].check_id` | string | エスカレーションされたガバナンスチェック。 | | `plans[].summary.escalations[].reason` | string | エスカレーションされた理由。 | | `plans[].summary.escalations[].resolution` | string | 解決方法(例: `approved_by_human`、`rejected_by_human`)。 | | `plans[].summary.escalations[].resolved_at` | string | ISO 8601 解決タイムスタンプ。 | | `plans[].summary.drift_metrics` | object | 監視ドリフトを検出するための集計ガバナンスメトリクス。[仕様](/docs/governance/campaign/specification#drift-detection)を参照。 | | `plans[].summary.drift_metrics.escalation_rate` | number | エスカレーションに至ったチェックの割合(0-1)。 | | `plans[].summary.drift_metrics.escalation_rate_trend` | enum | `increasing`、`stable`、または `declining`。 | | `plans[].summary.drift_metrics.auto_approval_rate` | number | 人間の介入なしに承認されたチェックの割合(0-1)。 | | `plans[].summary.drift_metrics.human_override_rate` | number | 人間がエージェントをオーバーライドしたエスカレーションの割合(0-1)。 | | `plans[].summary.drift_metrics.mean_confidence` | number | 検出事項全体の平均信頼スコア(0-1)。検出事項に信頼度が含まれる場合に存在します。 | | `plans[].summary.drift_metrics.thresholds` | object | ドリフトメトリクスの組織定義の閾値。メトリクスが閾値を超えると、ガバナンスエージェントは検出事項を含めます。 | | `plans[].summary.drift_metrics.thresholds.escalation_rate_max` | number | 許容できる最大エスカレーション率。 | | `plans[].summary.drift_metrics.thresholds.escalation_rate_min` | number | 許容できる最小エスカレーション率。この値を下回る率は監視の侵食を示す可能性があります。 | | `plans[].summary.drift_metrics.thresholds.auto_approval_rate_max` | number | 許容できる最大自動承認率。 | | `plans[].summary.drift_metrics.thresholds.human_override_rate_max` | number | 許容できる最大人間オーバーライド率。 | | `plans[].entries` | array | 順序付けられた監査証跡(`include_entries` が `true` の場合のみ)。 | | `plans[].entries[].id` | string | エントリー識別子。 | | `plans[].entries[].type` | enum | `check` または `outcome`。 | | `plans[].entries[].timestamp` | string | ISO 8601 タイムスタンプ。 | | `plans[].entries[].plan_id` | string | このエントリーが属するプラン。複数のプランまたはポートフォリオをクエリする場合に存在します。 | | `plans[].entries[].caller` | string | リクエストを行ったエージェントの URL。ガバナンスコールバックで使用されたクレデンシャルから解決されます。 | | `plans[].entries[].tool` | string | AdCP ツール(`check` エントリーに存在)。 | | `plans[].entries[].status` | enum | ガバナンスチェックステータス(`check` エントリーに存在)。 | | `plans[].entries[].binding` | enum | `proposed` または `committed`(`check` エントリーに存在)。 | | `plans[].entries[].explanation` | string | ガバナンス決定の人間が読める説明(`check` エントリーに存在)。 | | `plans[].entries[].policies_evaluated` | array | このチェック中に評価されたレジストリポリシー ID。 | | `plans[].entries[].categories_evaluated` | array | 評価されたガバナンスカテゴリ(例: `budget_authority`、`regulatory_compliance`)。 | | `plans[].entries[].findings` | array | このチェックからの検出事項。カテゴリ、深刻度、ポリシー ID、説明、信頼度を含みます。 | | `plans[].entries[].outcome` | enum | 結果タイプ(`outcome` エントリーに存在)。 | | `plans[].entries[].committed_budget` | number | コミットされた予算(`completed` 結果エントリーに存在)。 | | `plans[].entries[].media_buy_id` | string | メディアバイ ID(`delivery` 結果エントリーに存在)。 | | `plans[].entries[].outcome_status` | string | 結果ステータス(`outcome` エントリーに存在)。 | ## 認可 リクエストスキーマにはエンベロープの `account` フィールドがありません — テナントの識別は送信された ID から解決されます。ガバナンスエージェントは、認証済みプリンシパルが、すべての `plan_ids` メンバー、`portfolio_plan_ids` から展開されるすべてのプラン、`governance_contexts` が指すすべての被管理アクションについて認可されていることを検証しなければなりません(MUST)。いずれかの要素が認可チェックに失敗した場合、ガバナンスエージェントは汎用的な `PLAN_NOT_FOUND` レスポンスでフェイルクローズしなければなりません(MUST)— エラーボディは「未認可」と「見つからない」を区別してはならず、問題の ID を名指ししてはなりません(MUST NOT)。パターンと存在漏洩のガードレールについては [エージェントとアカウントの分離](/docs/building/by-layer/L1/security#エージェントとアカウントの分離) を参照。 ## エラーコード | コード | 回復 | 説明 | | ---------------- | ----------- | ---------------------------- | | `PLAN_NOT_FOUND` | correctable | この ID のプランが存在しないか、認可されていません。 | ## 関連タスク * [`sync_plans`](./sync_plans) — プランをプッシュまたは更新します * [`check_governance`](./check_governance) — プランに対してアクションを検証します * [`report_plan_outcome`](./report_plan_outcome) — プラン状態を更新するために結果を報告します # タスクリファレンス Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/campaign/tasks/index AdCP キャンペーンガバナンスのタスクリファレンス — sync_plans、check_governance、report_plan_outcome、get_plan_audit_logs。 # キャンペーンガバナンスタスク キャンペーンガバナンスには4つのタスクがあります。 | タスク | 方向 | 説明 | | ---------------------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------- | | [`sync_plans`](./sync_plans) | オーケストレーター → Gov | 予算、チャンネル、フライト日程、認可市場を含むキャンペーンプランをプッシュする | | [`check_governance`](./check_governance) | オーケストレーターまたはセラー → Gov | 汎用ガバナンスチェック。オーケストレーターはセラーに送信する前に `binding: "proposed"` で呼び出す。セラーは実行前に `binding: "committed"` で呼び出す。 | | [`report_plan_outcome`](./report_plan_outcome) | オーケストレーター → Gov | アクション後のレポート: 「実際に起きたこと」。Gov は状態を更新し、検出事項を返す | | [`get_plan_audit_logs`](./get_plan_audit_logs) | オーケストレーター → Gov | ガバナンス状態、予算消化、監査証跡を読み取る | `sync_plans` はルールを設定します。`check_governance` は汎用ガバナンスゲートだ — オーケストレーター(proposed)とセラー(committed)の両方がプランに対してアクションを検証するために呼び出す。`report_plan_outcome` は実行後にループを閉じる。`get_plan_audit_logs` は読み取り専用で、モニタリングとレポートに使います。 # report_plan_outcome Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/campaign/tasks/report_plan_outcome report_plan_outcome はアクションの結果を AdCP ガバナンスエージェントに送信し、予算追跡、状態、コンプライアンス記録を更新します。 # report\_plan\_outcome アクションの結果をガバナンスエージェントに報告します。セラーが応答した後にオーケストレーター(バイヤーサイドエージェント)が呼び出す。これはガバナンスループの「after」の半分だ — 実際に何が起きたかをガバナンスエージェントに伝えて状態を更新し、問題があればフラグを立てる。 セラーはこのタスクを呼び出さない。セラーは `phase: "delivery"` を使った [`check_governance`](./check_governance) で配信データを報告します。 ## セラーレスポンス(`create_media_buy` 後) ```json theme={null} { "tool": "report_plan_outcome", "arguments": { "plan_id": "plan_q1_2026_launch", "check_id": "chk_xyz789", "outcome": "completed", "seller_response": { "media_buy_id": "mb_seller_456", "packages": [ { "package_id": "pkg_001", "product_id": "premium_video_300k", "budget": 150000, "targeting_overlay": { "geo": { "include": [{ "type": "country", "code": "US" }] }, "viewability": { "standard": "mrc", "threshold": 50 } } } ], "planned_delivery": { "geo": { "countries": ["US"] }, "channels": ["olv"], "start_time": "2026-03-15T00:00:00Z", "end_time": "2026-06-15T00:00:00Z", "total_budget": 150000, "currency": "USD" }, "creative_deadline": "2026-03-20T00:00:00Z" } } } ``` ### レスポンス(問題なし) ```json theme={null} { "outcome_id": "out_001", "status": "accepted", "committed_budget": 150000, "plan_summary": { "total_committed": 425000, "budget_remaining": 75000 } } ``` ガバナンスエージェントは状態を更新します: セラーが確認した金額に基づいて予算がコミットされ、メディアバイが追跡されます。 ### レスポンス(不一致検出) 同じアクションの別のシナリオで、セラーがリクエストを変更した場合: ```json theme={null} { "outcome_id": "out_002", "status": "findings", "committed_budget": 120000, "findings": [ { "category_id": "seller_verification", "severity": "warning", "explanation": "Seller reduced budget from $150,000 to $120,000 and added geo targeting for CA that was not requested.", "details": { "discrepancies": [ { "field": "packages[0].budget", "requested": 150000, "received": 120000 }, { "field": "packages[0].targeting_overlay.geo.include", "requested": ["US"], "received": ["US", "CA"] } ] } } ], "plan_summary": { "total_committed": 395000, "budget_remaining": 105000 } } ``` ガバナンスエージェントはセラーの実際の金額(要求した $150K ではなく $120K)をコミットするが、オーケストレーターが対処するための検出事項を返します。 ## 配信データ(定期レポート) ```json theme={null} { "tool": "report_plan_outcome", "arguments": { "plan_id": "plan_q1_2026_launch", "outcome": "delivery", "delivery": { "media_buy_id": "mb_seller_456", "reporting_period": { "start": "2026-03-15T00:00:00Z", "end": "2026-03-22T00:00:00Z" }, "impressions": 1250000, "spend": 18750, "cpm": 15.00, "viewability_rate": 0.72, "completion_rate": 0.65 } } } ``` ### レスポンス(順調) ```json theme={null} { "outcome_id": "out_del_001", "status": "accepted" } ``` ### レスポンス(異常検出) ```json theme={null} { "outcome_id": "out_del_002", "status": "findings", "findings": [ { "category_id": "budget_authority", "severity": "warning", "explanation": "Spend is pacing 62% above plan. At current rate, budget will be exhausted 5 weeks early.", "details": { "planned_weekly_spend": 11538, "actual_weekly_spend": 18750, "overpace_pct": 62, "projected_exhaustion": "2026-05-03T00:00:00Z" } } ] } ``` ## 失敗したアクション セラーがリクエストを拒否した場合、ガバナンスエージェントがプラン状態を更新できるよう報告する: ```json theme={null} { "tool": "report_plan_outcome", "arguments": { "plan_id": "plan_q1_2026_launch", "check_id": "chk_xyz789", "outcome": "failed", "error": { "code": "PRODUCT_UNAVAILABLE", "message": "Product premium_video_300k is no longer available." } } } ``` ```json theme={null} { "outcome_id": "out_003", "status": "accepted", "committed_budget": 0, "plan_summary": { "total_committed": 275000, "budget_remaining": 225000 } } ``` ## フィールド ### リクエスト | フィールド | 型 | 必須 | 説明 | | ----------------------------------- | ------- | ----------- | ---------------------------------------------------------------------------------------- | | `plan_id` | string | Yes | この結果が対象のプラン。 | | `check_id` | string | Conditional | `check_governance` からの `check_id`。結果を承認したガバナンスチェックにリンクします。`completed` と `failed` の結果に必須。 | | `outcome` | enum | Yes | `completed`、`failed`、または `delivery`。 | | `seller_response` | object | No | セラーの完全なレスポンス。`outcome` が `completed` の場合に必須。 | | `seller_response.media_buy_id` | string | No | セラーのメディアバイ識別子。 | | `seller_response.committed_budget` | number | No | 確認されたすべてのパッケージにわたってコミットされた総予算。存在する場合、ガバナンスエージェントは個々のパッケージ予算を合計する代わりにこれを直接使用します。 | | `seller_response.packages` | array | No | 実際の予算とターゲティングを含む確認済みパッケージ。 | | `seller_response.planned_delivery` | object | No | セラーが配信すると言ったもの。セラーサイドガバナンスが設定されていない場合、これがガバナンスエージェントのセラーの配信パラメーターに対する唯一のビューとなります。 | | `seller_response.creative_deadline` | string | No | クリエイティブ提出の ISO 8601 締め切り。 | | `delivery` | object | No | 配信メトリクス。`outcome` が `delivery` の場合に必須。 | | `delivery.media_buy_id` | string | No | レポート対象のメディアバイ。 | | `delivery.reporting_period` | object | No | レポートウィンドウの開始と終了タイムスタンプ。 | | `delivery.impressions` | integer | No | 期間中に配信されたインプレッション数。 | | `delivery.spend` | number | No | 期間中の消化額。 | | `delivery.cpm` | number | No | 期間の実効 CPM。 | | `delivery.viewability_rate` | number | No | ビューアビリティ率(0-1)。 | | `delivery.completion_rate` | number | No | 動画完了率(0-1)。 | | `error` | object | No | エラー詳細。`outcome` が `failed` の場合に必須。 | | `error.code` | string | No | セラーからのエラーコード。 | | `error.message` | string | No | 人間が読めるエラーの説明。 | ### レスポンス | フィールド | 型 | 説明 | | ------------------------ | ------ | --------------------------------------------- | | `outcome_id` | string | この結果レコードの一意識別子。 | | `status` | enum | `accepted`(状態更新、問題なし)または `findings`(問題検出)。 | | `committed_budget` | number | この結果からコミットされた予算(`completed`/`failed` の結果に存在)。 | | `findings` | array | ステータスが `findings` の場合のみ存在。 | | `findings[].category_id` | string | 問題をフラグした検証カテゴリ。 | | `findings[].severity` | enum | `info`、`warning`、または `critical`。 | | `findings[].explanation` | string | 問題の人間が読める説明。 | | `findings[].details` | object | プログラム的な処理のための構造化された詳細。 | | `plan_summary` | object | 更新されたプランの予算状態(`completed`/`failed` の結果に存在)。 | ## エラーコード | コード | 回復 | 説明 | | -------------------- | ----------- | ---------------------------------------------------------- | | `PLAN_NOT_FOUND` | correctable | この ID のプランが存在しません。 | | `CHECK_NOT_FOUND` | correctable | この `check_id` のガバナンスチェックが存在しません。 | | `CAMPAIGN_NOT_FOUND` | correctable | プラン内にこの `governance_context` のキャンペーンが存在しません。 | | `CAMPAIGN_SUSPENDED` | correctable | プランが人間のレビューを待ちながら停止されています。エスカレーションが解決されるまで結果レポートはブロックされます。 | ## 関連タスク * [`check_governance`](./check_governance) — アクションを承認したガバナンスチェック * [`sync_plans`](./sync_plans) — プランをプッシュまたは更新します * [`get_plan_audit_logs`](./get_plan_audit_logs) — プラン状態と監査証跡を表示します # sync_plans Source: https://adcp-docs-ja.pier1.co.jp/docs/governance/campaign/tasks/sync_plans sync_plans は、予算制限、チャンネル、フライト日程、コンプライアンスポリシーを含むキャンペーンプランを AdCP ガバナンスエージェントにプッシュします。 # sync\_plans キャンペーンプランをガバナンスエージェントにプッシュします。プランはキャンペーンの認可パラメーター — 予算制限、チャンネル、フライト日程、認可市場、コンプライアンスポリシー — を定義し、すべての検証における真実の源泉として機能します。 ## リクエスト ```json theme={null} { "tool": "sync_plans", "arguments": { "plans": [ { "plan_id": "plan_q1_2026_launch", "brand": { "domain": "acmecorp.com" }, "objectives": "Drive awareness for spring product launch among 25-54 adults in the US, focusing on premium video and high-impact display.", "budget": { "total": 500000, "currency": "USD", "reallocation_threshold": 25000, "per_seller_max_pct": 40 }, "channels": { "required": ["olv"], "allowed": ["olv", "display", "ctv", "audio"], "mix_targets": { "olv": { "min_pct": 40, "max_pct": 70 }, "display": { "min_pct": 10, "max_pct": 30 }, "ctv": { "min_pct": 0, "max_pct": 20 }, "audio": { "min_pct": 0, "max_pct": 10 } } }, "flight": { "start": "2026-03-15T00:00:00Z", "end": "2026-06-15T00:00:00Z" }, "countries": ["US"], "policy_categories": ["age_restricted"], "audience": { "include": [ { "type": "description", "description": "Adults 25-54 interested in home improvement" } ], "exclude": [ { "type": "description", "description": "Children under 13" } ] }, "restricted_attributes": ["health_data"], "min_audience_size": 1000, "policy_ids": ["us_coppa", "alcohol_advertising"], "custom_policies": [ "No advertising adjacent to competitor content" ], "approved_sellers": null, "ext": {} } ] } } ``` ## レスポンス ```json theme={null} { "plans": [ { "plan_id": "plan_q1_2026_launch", "status": "active", "version": 1, "categories": [ { "category_id": "budget_authority", "status": "active" }, { "category_id": "strategic_alignment", "status": "active" }, { "category_id": "bias_fairness", "status": "active" }, { "category_id": "regulatory_compliance", "status": "active" }, { "category_id": "seller_verification", "status": "active" }, { "category_id": "brand_policy", "status": "active" } ], "resolved_policies": [ { "policy_id": "us_coppa", "source": "explicit", "enforcement": "must", "reason": "Referenced in plan policy_ids" }, { "policy_id": "alcohol_advertising", "source": "explicit", "enforcement": "should", "reason": "Referenced in plan policy_ids" } ] } ] } ``` ## 仕組み プランはエージェンシーのプランニングツール、ブランドの予算システム、インサーションオーダーなど外部システムから生まれる。`sync_plans` はそれらをガバナンスエージェントにプッシュして、何を検証すべきかをエージェントに伝える。 既存のプラン(同じ `plan_id`)を同期すると更新されます。ガバナンスエージェントはバージョンをインクリメントし、アクティブなキャンペーンを更新されたルールに対して再評価します。これにより、予算増加やチャンネル追加などのフライト中の変更に対応できます。コンテンツ標準のバージョンは別個の「購入時ピン留め」ルールに従います — [コンテンツ標準のバージョニング](/docs/governance/content-standards/index)を参照。 複数のキャンペーン(`check_governance` と `report_plan_outcome` の `governance_context` で識別)が同じプランを参照できます。ガバナンスエージェントはプランに紐付けられたすべてのキャンペーンにわたって予算を追跡します。 プランはキャンペーンコンテキスト — 予算、チャンネル、フライト日程、認可市場 — を指定します。ガバナンスエージェントはブランドのコンプライアンス設定から適用可能なポリシーを解決するが、プランは `policy_ids` でレジストリポリシーを直接参照したり、`custom_policies` でキャンペーン固有のルールを含めたりすることもできます。これにより、集中的なポリシー管理(ブランドレベル)と、バイイングチームが特定のキャンペーンに追加要件を必要とする場合のキャンペーン固有のオーバーライドの両方に対応できます。 `countries` と `regions` は2つの目的を果たす: 1. **ジオ強制** — ガバナンスエージェントはプランの市場外をターゲットにするメディアバイを拒否します。`regions: ["US-MA"]` のプランは明示的にマサチューセッツをターゲットにしないバイをブロックします。 2. **ポリシー解決** — エージェントはプランの市場と管轄が重なるすべてのポリシーを検索します。`countries: ["US"]` のプランはすべての米国連邦および州レベルのポリシーの対象となります。`regions: ["US-MA"]` のみのプランはマサチューセッツ固有および連邦ポリシーの対象となります。 これらのフィールドは `product-filters`、`offerings`、`create_media_buy` と同じ ISO コードおよびセマンティクスを使用し、プロトコル全体で一貫したジオ語彙を確保します。全国展開するファーマキャンペーンは `countries: ["US"]` を使用し、合法な州に限定される大麻キャンペーンは `regions: ["US-CO", "US-CA", "US-MA"]` を使用します。 ## プランハッシュのプリイメージ バイヤーがここで提供する各プランアイテムは、すべての[署名付き `governance_context`](/docs/building/by-layer/L1/security#署名付きガバナンスコンテキスト)で運ばれる監査層クレーム `plan_hash` を生成するためにガバナンスエージェントがハッシュ化するプリイメージです。正規化ルール、閉じた帳簿上の除外リスト、保持義務、および参照テストベクターの完全なセットは、[プランバインディングと監査](/docs/governance/campaign/specification#plan-binding-and-audit)で規定されています。ガバナンスエージェントの実装者は、出荷前に [`static/compliance/source/test-vectors/plan-hash/`](https://github.com/adcontextprotocol/adcp/tree/main/static/compliance/source/test-vectors/plan-hash) の 11 個のベクターに対してハッシュ化コードを実行すべきです(SHOULD)。 ## フィールド ### リクエスト | フィールド | 型 | 必須 | 説明 | | --------------------------------------- | ---------- | --- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `plans` | array | Yes | 同期する1つ以上のキャンペーンプラン。 | | `plans[].plan_id` | string | Yes | このプランの一意識別子。 | | `plans[].brand` | BrandRef | Yes | ガバナンス対象のブランド。ガバナンスエージェントはブランドのコンプライアンス設定を解決して適用可能なポリシーを決定します。 | | `plans[].objectives` | string | Yes | 自然言語のキャンペーン目標。戦略的整合性の検証に使用します。 | | `plans[].budget` | object | Yes | 予算パラメーター。 | | `plans[].budget.total` | number | Yes | 認可された総予算。 | | `plans[].budget.currency` | string | Yes | ISO 4217 通貨コード。 | | `plans[].budget.reallocation_threshold` | number | Yes | エージェントがエスカレーションなしに再配分できる金額。`0` はすべての再配分に人間の承認を要求し、`budget.total` 以上の値は実質的に無制限です。[仕様](/docs/governance/campaign/specification#budget-reallocation)を参照。 | | `plans[].budget.per_seller_max_pct` | number | No | 単一セラーに配分できる予算の最大割合。 | | `plans[].channels` | object | No | チャンネル制約。省略した場合、すべてのチャンネルが許可されます。 | | `plans[].flight` | object | Yes | 認可されたフライト日程。このウィンドウ外の日程を持つメディアバイは拒否されます。 | | `plans[].countries` | array | No | 認可市場の ISO 3166-1 alpha-2 国コード。ガバナンスエージェントはこれらの国外をターゲットにするバイを拒否し、ポリシー管轄との照合によって適用可能なポリシーを解決します。 | | `plans[].regions` | array | No | 認可された国内サブ市場の ISO 3166-2 区画コード(例: `US-MA`)。指定した場合、国全体ではなくこれらの地域に限定します。 | | `plans[].policy_categories` | array | No | このキャンペーンに適用される規制カテゴリ(例: `children_directed`、`fair_housing`)。ガバナンスエージェントが強制するポリシーレジームを決定します。省略した場合、ガバナンスエージェントはブランドの業種とキャンペーン目標から推論することがあります。 | | `plans[].audience` | object | No | オーディエンスターゲティング制約。キャンペーンがリーチすべき対象(include)とリーチしてはなりません対象(exclude)を定義します。[オーディエンス制約](#オーディエンス制約)を参照。 | | `plans[].restricted_attributes` | array | No | ターゲティングに使用してはなりません個人データカテゴリ(例: `health_data`、`racial_ethnic_origin`)。GDPR 第9条特別カテゴリ。ガバナンスエージェントはこれらの属性を参照するオーディエンスターゲティングにフラグを立てる。 | | `plans[].restricted_attributes_custom` | array | No | enum でカバーされていない追加の制限属性。管轄固有の制限のための自由形式文字列(例: `financial_status`)。 | | `plans[].min_audience_size` | integer | No | k-匿名性のための最小オーディエンスセグメントサイズ。複数の条件を使用する場合の推定交差オーディエンスに適用されます。 | | `plans[].policy_ids` | array | No | このプランに強制するレジストリポリシー ID。プランの countries/regions と交差させて、地理的に関連するポリシーのみをアクティブにします。 | | `plans[].custom_policies` | array | No | このキャンペーン固有の自然言語ポリシーステートメント(例: 「競合コンテンツに隣接した広告禁止」)。 | | `plans[].mode` | enum | No | このプランのガバナンス強制モード: `enforce`、`advisory`、または `audit`。デフォルトは `enforce`。[ガバナンスモード](/docs/governance/campaign/specification#governance-modes)を参照。 | | `plans[].approved_sellers` | array/null | No | 承認されたセラーエージェント URL のリスト。`null` は任意のセラーを意味します。 | | `plans[].delegations` | array | No | このプランに対して実行権限を持つエージェント。[仕様](/docs/governance/campaign/specification#delegations)を参照。 | | `plans[].delegations[].agent_url` | string | Yes | 委任されたエージェントの URL。 | | `plans[].delegations[].authority` | enum | Yes | `full`、`execute_only`、または `propose_only`。 | | `plans[].delegations[].budget_limit` | object | No | このエージェントがコミットできる最大予算。 | | `plans[].delegations[].markets` | array | No | このエージェントが認可されている ISO 国/地域コード。 | | `plans[].delegations[].expires_at` | string | No | ISO 8601 委任期限。 | | `plans[].portfolio` | object | No | ポートフォリオレベルのガバナンス制約。[仕様](/docs/governance/campaign/specification#portfolio-governance)を参照。 | | `plans[].portfolio.member_plan_ids` | array | Yes | このポートフォリオプランでガバナンスされるプラン ID。 | | `plans[].portfolio.total_budget_cap` | object | No | メンバープラン全体の最大累積予算。 | | `plans[].portfolio.shared_policy_ids` | array | No | すべてのメンバープランに強制されるレジストリポリシー ID。 | | `plans[].portfolio.shared_exclusions` | array | No | すべてのメンバープランに対する自然言語除外ルール。 | | `plans[].ext` | object | No | 拡張データ。 | ### レスポンス | フィールド | 型 | 説明 | | ----------------------------------------- | ------ | ------------------------------------------------------------------------- | | `plans` | array | 同期された各プランのステータス。 | | `plans[].plan_id` | string | プラン識別子。 | | `plans[].status` | enum | `active`(同期成功)または `error`(同期失敗)。これは同期結果のステータスであり、プランのライフサイクルステータスではありません。 | | `plans[].version` | number | プランバージョン(同期のたびにインクリメント)。 | | `plans[].categories` | array | このプランでアクティブな検証カテゴリ。ガバナンスエージェントの宣言されたケイパビリティによって異なります。 | | `plans[].categories[].category_id` | string | 検証カテゴリ識別子。 | | `plans[].categories[].status` | enum | `active` または `inactive`。 | | `plans[].resolved_policies` | array | このプランに対してガバナンスエージェントが強制するポリシー。明示的に参照されたポリシーと自動適用されたポリシーの両方を含みます。 | | `plans[].resolved_policies[].policy_id` | string | レジストリポリシー ID。 | | `plans[].resolved_policies[].source` | enum | `explicit`(設定またはプランで参照)または `auto_applied`(管轄/ポリシーカテゴリによってマッチ)。 | | `plans[].resolved_policies[].enforcement` | enum | `must`、`should`、または `may`。 | | `plans[].resolved_policies[].reason` | string | このポリシーが含まれた理由。 | ## オーディエンス制約 プランは `audience` フィールドを使ってオーディエンスターゲティング制約を宣言できます。各制約は**オーディエンスセレクター** — 特定のシグナルへの参照または自然言語の説明のいずれか。 **シグナル参照** — データプロバイダーのカタログ内の特定のシグナルを指す: ```json theme={null} { "type": "signal", "catalog_url": "https://signals.dataprovider.com/catalog.json", "signal_id": "likely_ev_buyers", "value": true } ``` **説明** — 特定のシグナルにマップしない制約のための自然言語: ```json theme={null} { "type": "description", "description": "Adults aged 25-54 in urban areas", "category": "demographic" } ``` ガバナンスエージェントは `check_governance` 中にセラーのターゲティングをこれらの制約に対して評価します。シグナル参照は構造的なマッチングを可能にし、説明はセマンティックな比較を必要とします。 ### 制限属性 `restricted_attributes` フィールドはターゲティングに使用してはなりません個人データカテゴリを宣言します。値は GDPR 第9条特別カテゴリ: `racial_ethnic_origin`、`political_opinions`、`religious_beliefs`、`trade_union_membership`、`health_data`、`sex_life_sexual_orientation`、`genetic_data`、`biometric_data`。 ガバナンスエージェントはこれらを独自の `restricted_attributes` を宣言しているシグナル定義と照合します。マッチする属性を持つシグナルはターゲティングからブロックされます。属性を宣言していないシグナルの場合、ガバナンスエージェントはシグナル名と説明からセマンティックな推論にフォールバックします。 ### ポリシーカテゴリ `policy_categories` フィールドは適用される規制レジームを宣言します。カテゴリは[ポリシーレジストリ](/docs/governance/policy-registry)で定義され、関連する規制をグループ化する — たとえば `children_directed` は COPPA、英国 AADC、GDPR 第8条をカバーします。 ポリシーカテゴリは `brand.industries` とは異なります。インダストリーは企業が何をするかを説明し、ポリシーカテゴリは特定のキャンペーンにどの規制レジームが適用されるかを説明します。一般的な認知キャンペーンを実施する製薬会社(`industries: ["pharmaceuticals"]`)は、特定の薬を宣伝しないキャンペーンには `pharmaceutical_advertising` をポリシーカテゴリとして必要としないかもしれない。 ## エラーコード | コード | 回復 | 説明 | | ------------------------ | ----------- | -------------------------------------------------------------------------------- | | `INVALID_PLAN` | correctable | プランに必須フィールドがないか、値が無効です。 | | `BRAND_NOT_FOUND` | correctable | ブランドドメインをブランドプロトコル経由で解決できなかった。ガバナンスエージェントは有効なブランド参照なしに適用可能なコンプライアンスポリシーを決定できません。 | | `BUDGET_BELOW_COMMITTED` | correctable | プラン更新時に、すでにコミット済みの金額を下回る予算には削減できません。 | ## 関連タスク * [`check_governance`](./check_governance) — このプランに対してアクションを検証します * [`report_plan_outcome`](./report_plan_outcome) — プラン状態を更新するために結果を報告します * [`get_plan_audit_logs`](./get_plan_audit_logs) — プラン状態と監査証跡を表示します # 既知の制限 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/known-limitations AdCP 3.0 の明示的な非目標と延期項目 — プロトコルが何をしないか。実装者とレビュアーがそれに応じて計画できるように。 プロトコルが何を*しない*かを知ることは、それを評価することの一部です。このページは、AdCP 3.0 の明示的な非目標と延期項目を統合します — プロトコルが意図的に扱わないもの、このサイクルでスコープ外にされたもの、メカニズムとして存在するがプロトコル層で強制されないもの。 下記の各制限は、仕様の可視な端か、追跡されたフォローアップのいずれかです。どれも隠されていません。 3.x で出荷されたがまだ凍結されていないサーフェスは別途リストされます — [実験的ステータス](/docs/reference/experimental-status) を参照。 ## セキュリティとプライバシー * **エンドユーザー認証なし。** AdCP はエージェントを認証し、それらが代わりに動作する人間を認証しません。ユーザーレベルのアイデンティティ、同意取得、データ主体の権利は、バイヤーのスタックで上流で扱われ、プロトコルのスコープ外です。 * **プロトコルレベルの侵害通知 SLA なし。** AdCP は「セラーは鍵侵害の疑いから N 時間以内にバイヤーに通知しなければならない」を仕様化しません。通知コミットメントは当事者間の DPA に存在します。将来のメジャーバージョンがこれを厳格化するかもしれません。 * **プロトコルに組み込まれた協調的脆弱性開示なし。** 個々のエージェントオペレーターは `/.well-known/security.txt` を公開すべきです。まだ規範的な AdCP 要件はありません。 * **プロトコルレベルの PII トランスポートなし。** `sync_audiences` の `hashed_email` と `hashed_phone` フィールドは、バイヤー側での SHA-256 ハッシュ化を要求します — スキーマはそれらのフィールドに平文を受け入れません。非 PII 空間のために他の識別子タイプが存在します。ワイヤー上に平文 PII が必要なら、AdCP は正しいキャリアではありません。 * **ハッシュ化識別子は仮名であり匿名ではない。** email と phone の SHA-256 ハッシュは GDPR と CPRA の下で仮名識別子のままで、基底の PII の規制的扱いを継承します。AdCP は非識別化を主張しません。 * **プロトコルレベルのデータレジデンシーメカニズムなし。** レジデンシーは個々のエージェントの設定と契約のプロパティです。プロトコルは規範的なレジデンシータグを運びません。 * **LLM プロンプトインジェクション防御についてのプロトコル保証なし。** それはループ内のすべての LLM 駆動エージェントのオペレーターの関心事です。[Security Model — エージェンティック広告に固有の脅威](/docs/building/concepts/security-model#threats-specific-to-agentic-advertising) を参照。 * **構造的プライバシーは TMP にのみ適用される。** Trusted Match Protocol はプライバシーを構造的に強制します(分離されたコードパス、スキーマ上の禁止)。他のすべてのドメインは契約的機密性またはセッションごとの同意に依存します。[ドメインをまたぐプライバシー姿勢](/docs/protocol/architecture#privacy-posture-across-domains) を参照。 * **ワイヤー上の管轄区域認識の同意シグナルなし。** AdCP は規範的な同意タグ(例: ケベックの Law 25、日本の APPI、ブラジルの LGPD のような制度への準拠を表現するのに使われる IAB TCF、GPP、同等物)を運びません。合法的根拠の判断、同意取得、管轄ルーティングは、それぞれのスタックでコントローラーまたはプロセッサーとして動作する各当事者の責任のままで、それらの間の DPA によって統治されます。構造化されたクロスエージェント同意シグナルは将来の作業の候補です。 * **プロトコル境界をまたいだ同意スコープの伝播なし。** ある同意スコープの下でアクティベートされたシグナル(例: ファーストパーティ広告同意を持つ CRM からの `sync_audiences`)は、スコープ整合を強制するプロトコルレベルのメカニズムなしに下流で参照 — 再ターゲット、別のキャンペーンに添付、または他のシグナルと合成 — できます。各当事者は DPA と運用制御を通じてオフプロトコルでスコープ互換性を検証します。3.1 に向けて [#2540](https://github.com/adcontextprotocol/adcp/issues/2540) で追跡。 * **プロトコルレベルの越境転送メカニズムなし。** AdCP は SCC、IDTA、または十分性決定のメタデータを運びません。国際転送の合法性は当事者の契約と設定のプロパティです。 * **バージョン管理されたコンテンツ来歴チェーンなし。** AdCP は権利利用アサーションと `ai_generated_image` フラグを運びます — 後者は boolean マーカーであり、署名された来歴アサーションではありません。プロトコルは、クリエイティブが生成、編集、適応ステップを通過するにつれアサーションを蓄積する暗号学的に署名された来歴グラフを仕様化しません。新興のコンテンツ真正性標準(CAI/C2PA)との相互運用は将来の作業として追跡されます。 * **保持または削除 SLA なし。** プロトコルは、当事者が `sync_audiences` 入力、`report_usage` レコード、タスク履歴をどのくらい保持するかを仕様化しません。保持ウィンドウとデータ主体リクエストの履行は当事者間の DPA に存在します。 * **汎用の同期 RPC レスポンス署名は定義されていない(指定タスクのペイロードエンベロープを除く) — 省略ではなく設計による。** AdCP はアプリケーション層で 5 つのものに署名します: インバウンドリクエスト(RFC 9421、`adcp_use: "request-signing"`)、ブランド権利、AAO Verified コンプライアンス、セールスインテリジェンスリレー、双方向の否認防止領収書などすべての専門分野スコープの永続的アーティファクトを含むアウトバウンド webhook(RFC 9421、`adcp_use: "request-signing"`。非推奨の `webhook-signing` キーは互換性ウィンドウ中受け入れられたまま)、ガバナンスアテステーション(JWS、`adcp_use: "governance-signing"`)、指定タスクのレスポンスペイロード — 現在は `verify_brand_claim` ファミリーのみ(JWS ペイロードエンベロープ、`adcp_use: "response-signing"`)、Trusted Match Protocol エンベロープ(TMP 独自の Ed25519 プロファイル)。`tools/call`(または同等の A2A 非ストリーミング応答 / ストリーミング `artifactUpdate` フレーム)が返す同期レスポンスボディは、指定タスクリスト外のタスクについては署名され **ません**: 即時応答の完全性はリクエストを運んだ認証済みセッション内の TLS によって配信され、永続的アーティファクトの静止時完全性は署名付き webhook の仕事です。分割は意図的です — webhook のみのアテステーションは、「このアーティファクトは永続的完全性を必要とする」をすべての応答へのフリーライダーではなく明示的なモデリング決定にする強制関数であり、webhook のものと重なる汎用のレスポンス署名サーフェスの運用コスト(追加の検証者パス、適合性グレーダー、失効エントリ)を避けます。**RFC 9421 §2.2.9 トランスポートレスポンス署名は 3.x のどのタスクにも定義されていません。** バイヤーは指定タスクリスト外のレスポンス署名に依存してはなりません(MUST NOT)。静止時アテステーションを必要とするアーティファクトは署名付き webhook 経由で配信されなければなりません(MUST)。同期的に返されるアーティファクトがアテステーション可能である必要がある場合、仕様がサポートするパスは、タスクを指定リストに追加する(規範的決定)か、正準バージョンで署名付き webhook を発するようツールを再構築することです。[#3737](https://github.com/adcontextprotocol/adcp/issues/3737) で追跡、意図された 3.x 設計として解決、脅威モデルが進化すれば 4.0 で再検討可能。 ## コマースと決済 * **プロトコル内の支払いまたは決済なし。** `report_usage` は請求に供給される消費データを提供しますが、請求と決済はバイヤー/セラーの商業的関係を通じて帯域外で起こります。 * **クロス通貨メディアバイなし。** 各メディアバイは単一の ISO 4217 通貨を使います(`core/price.json` を参照)。バイヤーの予算通貨がセラーの価格通貨と異なる場合、バイは一致する通貨を使うか拒否されます。FX レートのピン留め、リスク帰属、クロス通貨レポートは将来のバージョンに延期されます。 * **プロトコルレベルの配信紛争フローなし。** バイヤーとセラーの配信数が不一致のとき、再照合は商業的関係を通じて帯域外で起こります(両側の監査証跡によって裏付けられる)。構造化された紛争タスクは将来の作業の候補です。 ## 測定とアトリビューション * **アトリビューションプロトコルではない。** AdCP はエクスポージャーレコード、許可される場合は識別子、アトリビューションに供給される成果シグナルを運びますが、アトリビューションモデルを仕様化しません。メディアミックスモデリング、マルチタッチアトリビューション、インクリメンタリティテストはバイヤーの測定スタックに存在します — プロトコルによって計算されるのではなく AdCP データ([`report_usage`](/docs/accounts/tasks/report_usage) とタスクレベルの出力)によって供給されます。 ## 認証とアイデンティティ * **OAuth 2.1 + resource-indicators の規範的要件なし。** AdCP は mTLS、事前プロビジョニングされた API キー、RFC 9421 署名付き HTTP リクエスト(最後は 3.1 で規範的)を使ってエージェントを認証します。これらは、委譲された人間ユーザー認可ではなく、自律エージェント間の相互認証のために意図的に選ばれています。resource indicators を持つ OAuth 2.1 は、オペレーターのインフラがすでにそれに標準化している場合に許容されるトランスポートですが、プロトコル要件ではなく、上記の 3 つのメカニズムの代替にはなりません。 * **変更呼び出しの署名付きリクエストは 3.1 で規範的、3.0 ではない。** AdCP 3.0 は変更呼び出しで bearer トークン認証を許します。3.1 は RFC 9421 署名または JWS 署名ボディを要求します。[#2307](https://github.com/adcontextprotocol/adcp/issues/2307) で追跡。 * **レジストリでの鍵透明性アンカリングなし。** [AgenticAdvertising.org レジストリ](/docs/registry/index) はブランドアイデンティティ、プロパティ認可、エージェントディスカバリーを解決し、パブリッシャーの [`adagents.json`](/docs/governance/property/adagents#signing_keys) で宣言された `signing_keys[]` をキャッシュできます。まだしないことは、鍵透明性ログとして動作することです: ドメインをルート検証鍵にバインドする登録儀式、追記専用のローテーションレコード、すべての検証者が同じ鍵履歴を見る暗号学的コミットメントはありません。したがって 3.x では、RFC 9421 バイヤー鍵、ガバナンス JWS 鍵、エージェント署名鍵、ポインターファイルは依然として究極的には当事者自身のインフラに根ざしています — 当事者の CDN、DNS、`/.well-known` パスを制御する攻撃者は攻撃者制御の鍵を提供でき、証明書が侵害されたホスト名に対して有効なので TLS はこれを閉じません。3.x は継続性を伴う trust-on-first-use を配信します(マルチソースクロスチェック、公開遅延ウィンドウ、帯域外ローテーションシグナリング、ローテーション有効性の規律) — 検出可能にハードルを上げますが、暗号学的にギャップを閉じません。完全な閉鎖は、追記専用ローテーションログと JWKS ワイヤー互換性を持つ、既存レジストリの上の鍵透明性層で、4.0 の成果物として追跡されます。 ## ガバナンス * **規制カテゴリーの人間レビューは、3 つの名指しされたカテゴリーについてスキーマレベルで、それ以外すべてについてガバナンスエージェントレベルで強制される。** 3.0 は、`fair_housing`、`fair_lending`、`fair_employment` を宣言するキャンペーンで `authority_level: agent_full` をスキーマレベルで拒否します([#2310](https://github.com/adcontextprotocol/adcp/issues/2310) 経由で出荷、2026-04-18 マージ)。その他の規制カテゴリー — 政治、製薬、ギャンブル、アルコール、タバコ、金融、暗号、大麻/CBD、銃器、栄養補助食品の健康クレーム、子供向け — は、スキーマ不変条件ではなくガバナンスエージェント実装に依存します。 * **`sync_catalogs` または `sync_creatives` でプロトコルが義務付ける HITL なし。** ユニバーサルタスクライフサイクルメカニズムが利用可能です。人間のゲートを要求する規範的ルールはありません。`acquire_rights` は、バイヤーのプランがそれ用に設定されているとき、キャンペーンガバナンスパス([購入フェーズ](/docs/governance/campaign/specification#governance-phases))経由で統治されます。2 つのチャネルについては [How human-in-the-loop enters the protocol](/docs/governance/embedded-human-judgment#how-human-in-the-loop-enters-the-protocol) を、`check_governance`、`TERMS_REJECTED`、ライフサイクルタスクの規範的ルールについては EHJ レジスターを参照。 * **`fair_housing`、`fair_lending`、`fair_employment` を超えた規制カテゴリーはファーストクラスの扱いを持たない。** 政治広告、製薬、ギャンブル、アルコール、タバコ、金融プロモーション(暗号とデジタル資産を含む)、大麻/CBD、銃器、栄養補助食品の健康クレーム、子供向け広告は、カテゴリー固有のスキーマルールではなく一般的な [キャンペーンガバナンス](/docs/governance/campaign/specification) メカニズム(HITL ゲート、ガバナンスタスク)に依存します。地域固有の開示要件(例: 英国 FCA s.21、EU MiCA、政治広告レジストリ、COPPA、英国 Children's Code、GDPR 第 8 条)は、プロトコルではなくセラーの配信スタックとバイヤーのコンプライアンス姿勢に存在します。 ## 適合性とテスト * **リファレンステストベクターは部分的。** [適合性](/docs/building/conformance) はストーリーボードスイートによって定義され、スイートは今日リクエスト署名と正準化のベクターを公開します — しかしより広いリファレンステストベクターのコーパスは [#2383](https://github.com/adcontextprotocol/adcp/issues/2383) で追跡されます。 * **AdCP Verified は 3.0 で自己証明。正式なプログラムは 3.1 でローンチ。** 今日、エージェントは自身の署名付き `runner-output.json` を公開します — 任意の検証当事者によって再現可能・再実行可能ですが、AAO 監査されていません。トレーニングエージェントと公式 SDK は、3.0 → 3.1 ウィンドウにわたって 4〜6 週間のケイデンスで完全なストーリーボードコンプライアンスに引き上げられています(トレーニングエージェントは今日 32/55 クリーン)。それらがクリーンに合格し曖昧なストーリーボード作業が完了したら、AAO は提出されたエージェントを正準ストーリーボードスイートに対して実行し、Verified エージェントの公開レジストリを維持します。コンプライアンスランナーとストーリーボード自体が 3.0 のソフトウェアです — ストーリーボードのバグ、カバレッジギャップ、エンコードされた仕様意図の曖昧さは、実装バグと並んで正当な GitHub issue です。 * **プロパティ名をスキャンする `check:platform-agnostic` リントを超えたプラットフォーム非依存性の自動強制なし。** スキーマセマンティクスをカバーするより豊かなチェックは将来の作業です。 * **レイテンシーまたはレスポンスタイム SLA なし。** プロトコルは、エージェントがどのくらい速く応答しなければならないかについて規範的な期待を持ちません(OpenRTB のオークションごとの `tmax` とは異なり)。バイヤーとセラーは、商業的関係を通じて、またはタスク固有の [アカウンタビリティ条件](/docs/media-buy/advanced-topics/accountability) を通じてタイミングを交渉します。構造化された SLA 宣言は延期されます。 * **ワイヤー上のランタイムスキーマディスカバリーツールなし。** AdCP は、ライブエージェント上の名前付きタスクのリクエストとレスポンスの形状を返す `get_schema`(または同等)ケイパビリティツールを出荷しません。コーディングエージェントは、仕様とパッケージされた [SKILL.md ファイル](https://github.com/adcontextprotocol/adcp/tree/main/skills) 経由で形状を発見します。SDK ビルダーはビルド時に `/schemas/v3/bundled/` から JSON Schema を読みます。したがって正準スキーマソースはワイヤー上ではなく帯域外です。ランタイムツールは [#3057](https://github.com/adcontextprotocol/adcp/issues/3057) で検討され延期されました — SKILL.md パスがコーディングエージェントの発見性をカバーし、スキーマバンドル URL が SDK ビルダーをカバーし、唯一のランタイム固有のケース(特定エージェントのプライベートツール拡張)は 3.x で規範的なツールサーフェスを正当化するほど一般的でないからです。決定は MCP `tools/list` が `inputSchema` を運び続けることに依存します — MCP と A2A 間の SDK 検証対称性([adcp-client#909](https://github.com/adcontextprotocol/adcp-client/issues/909) で追跡)は、A2A `AgentCard` をスキルごとの `schemaRef` で拡張するか、検証の非対称性を受け入れて A2A でバンドルスキーマにフォールバックすることで解決されるべきです。MCP `tools/list` から `inputSchema` を剥がすことによって*ではありません*。それを剥がすと、`get_schema` が閉じるはずだったまさにそのワイヤーサーフェスのギャップが生まれ、その解決は #3057 を再開すべきです。スキルローダーを持たない非 Anthropic LLM が主要なコンシューマーになる場合、プライベートツール拡張が一般的になる場合、静的ケイパビリティ記述子をまったく持たないトランスポートが現れる場合、または SDK 検証対称性の修正が MCP ディスカバリーから `inputSchema` を削除することで着地する場合、再検討してください。 * **ランナー側のケイパビリティスロット `not_applicable` 強制はまだ着地中。** `definePlatform` などの SDK ヘルパーによって投影されるケイパビリティスロットは、提案ではなくコミットメントです。望まれるランナー動作はこうです: ストーリーボード/ベクターが宣言されていないスロットをターゲットにする場合、そのパスを `not_applicable` とグレードする。スロットが宣言されている場合、ディスパッチし実装エラーで通常どおり失敗する。`adcp-client#2244` が着地するまで、プレリリース/カスタムランナーはエージェントの宣言されたスロットスコープ外のベクターに明示的なスキップゲートが必要かもしれません。実装者は、サポートされないスロットをプレースホルダー動作で宣言するのではなく欠如させるべきです。 ## プロトコルの外にあるもの AdCP はワイヤーを仕様化します。次のいずれも仕様化せず — また代替もできません: * **シークレットストレージ。** KMS、Vault、Secrets Manager、または同等物を使う。 * **エンドポイント堅牢化。** WAF、レート制限、DDoS 保護、TLS 設定、OS パッチ、依存関係スキャン。 * **監視とインシデント対応。** プロトコルは監視する価値のあるシグナル(冪等性衝突、ガバナンス失敗、SSRF 拒否)を発します。それらを検出し対応するのはオペレーターの仕事です。 * **人間の制御。** 承認しきい値、支出上限、一時停止権限 — これらはプロトコルではなく、オペレーターのエージェントやガバナンスプラットフォーム内のポリシー設定です。 * **物理的および人的セキュリティ。** 誰が本番に触れられるか、誰がブレークグラス認証情報を保持するか、誰が main にプッシュできるかについての通常の制御。 * **課金グレードのメトリクス会計。** AdCP はセラーの広告配信システムからエンドツーエンドで配信と使用量のデータを運び、[`report_usage`](/docs/accounts/tasks/report_usage) が請求に供給します。基底の配信プラットフォーム — またはバイヤー指定の測定ベンダー — が、測定方法論のカウント、監査、MRC 認定の記録システムです。AdCP 自体は MRC 認定されておらず、認定を求めません。認定は下流に位置する測定システムに付随します。AdCP はワイヤーと契約であり、台帳ではありません。 * **無効トラフィックのフィルタリングとビューアビリティ測定。** バイヤーとセラーは、[アカウンタビリティ条件](/docs/media-buy/advanced-topics/accountability) を通じて検証ベンダー、しきい値、修復に合意します。GIVT/SIVT フィルタリング(MRC 準拠)、ビューアビリティ測定(MRC Viewable Ad Impression 標準準拠)、ブランドセーフティ検証は、配信スタックまたは選ばれたベンダー層(例: DoubleVerify、IAS、HUMAN)で実行されます。 * **配信されるクリエイティブのアクセシビリティ適合性。** レンダリングされた広告の WCAG、ADA、EN 301 549 適合性は、クリエイティブサプライヤー、配信スタック、パブリッシャーのレンダリングコンテキストにあります。AdCP はアクセシビリティ適合性アサーションを規範的フィールドとして運びません。 AdCP をドアの錠を仕様化するものと考えてください。オペレーターは依然として建物を所有します。 ## 関連 * [Security Model](/docs/building/concepts/security-model) — 脅威モデルと多層防御 * [プライバシー考慮事項](/docs/reference/privacy-considerations) — クロスプロトコルのプライバシー入口 * [実験的ステータス](/docs/reference/experimental-status) — 3.x で出荷されたがまだ凍結されていないサーフェス * [バージョニングとガバナンス](/docs/reference/versioning) — ケイデンス、サポートウィンドウ、制限がフォローアップになる方法 * [ロードマップ](/docs/reference/roadmap) — 次に来るもの # プライバシー考慮事項 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/privacy-considerations AdCP 実装者、コンプライアンスレビュアー、CISO のためのクロスプロトコルのプライバシー入口 — 各プロトコルが何を運び、何を運ばないか、実装者が自分でプライバシーを扱わなければならない場所。 このページは AdCP におけるプライバシーのクロスプロトコルの入口です。実装者とコンプライアンスレビュアーが考える必要のあるカテゴリーを名指しし、各 AdCP プロトコルが何を運び何を運ばないかを要約し、より深いリファレンスにリンクします。それらのいずれも置き換えません。 特定のドメインの深いアーキテクチャの全体像が必要なら、リンクをたどってください。AdCP はプライバシー影響評価テンプレートを公開しません。デプロイヤーの DPO が自身の評価を組み立てるのに必要な入力については、下記の [For DPOs and procurement reviewers](#for-dpos-and-procurement-reviewers) を参照。 ## What AdCP's privacy posture is — and isn't AdCP はエージェント間のワイヤープロトコルを仕様化します。次を仕様化しません: * エンドユーザー認証または同意取得(上流で扱われる) * データ主体の権利ワークフロー(バイヤーの責任) * データレジデンシー(個々のエージェントの設定と契約のプロパティ) * 保持ポリシー(オペレーターの責任) AdCP が仕様化するのは、各プロトコルが運ぶものの*形状*、運んではならないものの禁止、プロトコルがプライバシー敏感な場所に適用される構造的分離です。実装者は、プロトコル自体が強制しないすべての境界でのプライバシー制御に責任を負います。 ## Privacy posture by domain AdCP のプライバシー保証はプロトコル間で一様ではありません。完全な表については [ドメインをまたぐプライバシー姿勢](/docs/protocol/architecture#privacy-posture-across-domains) を参照。要約: * **Trusted Match Protocol(TMP)** — **構造的分離**。Context Match と Identity Match は分離されたコードパスで実行される。スキーマがクロスオーバーを禁止する。TEE アテステーション(デプロイされたとき)が分離を独立に検証可能にする。[TMP privacy architecture](/docs/trusted-match/privacy-architecture) を参照。 * **Media Buy、Creative、Signals、Governance** — **契約的機密性**。データを交換する当事者は、プロトコルレベルの分離ではなくアカウントの条件に拘束される。 * **Sponsored Intelligence** — **セッションごとの同意**。ユーザーはセッションごとに同意する。セッションをルーティングするネットワークはルーティングメタデータを見ることがある。[SI networks](/docs/sponsored-intelligence/networks) を参照。 * **Brand / Registry** — **設計上公開**。`brand.json` は `/.well-known/brand.json` で発見可能。レジストリは公開エンティティ解決を公開する。 ガバナンスゲーティング(キャンペーンガバナンス経由)はプライバシー姿勢と独立して動作します: それは予算、ポリシー、ブランドセーフティの根拠で人間の承認を要求できますが、基底のドメインが運ぶデータを変えません。 ## Privacy categories to think about ### Data minimization AdCP は、できる場所でプロトコル境界を越えるデータを最小化します: * `sync_audiences` で `hashed_email` または `hashed_phone` を使うとき、バイヤーは転送前に正規化された値を SHA-256 ハッシュ化しなければなりません(MUST) — それらのフィールドのスキーマは平文を受け入れません。他の空間のために非ハッシュ識別子タイプが存在します。実装者は email または phone を転送するときハッシュ化タイプを選ばなければなりません(MUST)。 * TMP Context Match はユーザーアイデンティティを運びません。TMP Identity Match はページコンテキストを運びません。両方ともスキーマレベルで強制されます。 * `governance_context` トークンは、バイヤーのコンプライアンス姿勢が敏感なとき、インライン決定の代わりに `policy_decision_hash` を使えます。 実装者は、`ext` または `context` に追加する任意のフィールドが、プロトコルが最小化して除いたデータを再導入するかをレビューすべきです(SHOULD)。 ### Unsalted hashed identifiers are pseudonymous, not anonymous `hashed_email` と `hashed_phone` は **仮名 PII** です。email と E.164 名前空間は、事前計算された辞書と商用逆引きサービスがソルトなし SHA-256 ハッシュから平文を回復できるほど小さいです。ハッシュ化識別子は匿名化識別子と等価ではありません。 規範的帰結: * オペレーターのドキュメント、データ処理契約、コンプライアンス開示は、ソルトなしの `hashed_email` または `hashed_phone` を「プライバシー保護」「匿名」「非識別化」と記述してはなりません(MUST NOT)。ハッシュ化はトランスポート境界でのデータ最小化であり、匿名化ではありません。 * `hashed_email` と `hashed_phone` は、保持、同意、アクセス制御、データ主体アクセス(GDPR 第 15 条)、消去(GDPR 第 17 条)のワークフローについて PII として扱われなければなりません(MUST)。email アドレスの主体アクセスリクエストは、オペレーターが対応するハッシュ化レコードを保持する場合、そのハッシュでキー付けされたレコードに解決されなければなりません(MUST)。(真に再識別できないオペレーター — 例えば集約されたマッチ数やブルームフィルターのメンバーシップビットのみを保存する — は GDPR 第 11 条を呼び出してもよい。そのハードルは「ハッシュ化した」より高い。) * 上記のハッシュをセラーのアイデンティティグラフに対して照合することは、独自の合法的根拠を必要とする処理活動です — ハッシュ化はその要件を除去しません。 マッチングプロトコルが「プライバシー保護」であるというクレームは、認識されたプリミティブを必要とします — オペレーター保持のシークレットを伴うソルト付きハッシュ、クロスパーティ共有シークレットを伴う HMAC、PSI(Private Set Intersection)、またはアテストされた分離を伴う TEE。AdCP 3.0 は `hashed_email`/`hashed_phone` のソルト付きまたは HMAC バリアントを定義しません。標準化されたソルト付きバリアントは将来のマイナーリリースに向けて追跡されています。それまで、プライバシー保護マッチが必要な実装者は、上記のプリミティブの 1 つをプロトコルの上に重ねなければなりません(MUST)(例: クリーンルーム処理、PAIR、UID2/RampID オペレーターが行うアイデンティティグラフトークン化)。 ### Separation 2 つの事実が組み合わさるとプライバシー被害を生む場所(例: インプレッション時のアイデンティティ + コンテキスト)で、プロトコルはそれらを分離します。構造的分離は TMP 固有です。他のドメインは契約的分離に依存します。 ### Transport すべての AdCP トラフィックは HTTPS 経由です([Security — Identity](/docs/building/concepts/security-model#layer-1-identity--who-is-actually-calling) を参照)。署名付きリクエスト(RFC 9421)は 3.1 で規範的です。トランスポートセキュリティはベースラインの前提です。プロトコルはその上に構築されます。 ### Residency レジデンシーはプロトコルで運ばれません。エージェントのレジデンシー姿勢は設定と契約のプロパティです — 実装者はそれを文書化しなければならず(MUST)、オペレーターは該当する場合 EU / UK / その他の地域要件を満たすようそれを設定しなければなりません(MUST)。[Security Model — データ処理とサブプロセッサーのチェックリスト](/docs/building/concepts/security-model#data-handling-and-subprocessors) を参照。 ### Retention プロトコルは、冪等性のキャッシュ保持([Layer 3: Idempotency](/docs/building/concepts/security-model#layer-3-idempotency--at-most-once-execution) を参照)とガバナンスの監査ログ保持([Layer 5: Auditability](/docs/building/concepts/security-model#layer-5-auditability--the-trail-survives-the-transaction) を参照)を記述します。他のデータ — クリエイティブアセット、キャンペーン状態、LLM プロンプト、会話ログ — の保持はオペレーターの責任です。 ### Processor / controller roles 誰がコントローラーで誰がプロセッサーかはデプロイに依存します。AdCP はプロトコル層でロールを割り当てません。典型的な割り当て: * バイヤーは、実行するキャンペーンについて通常コントローラー。 * ガバナンスエージェントは、それが提供するコントローラーのためにプロセッサーとして動作し、しばしば **マルチカスタマーの影響範囲** を持つ — デューデリジェンスでそれに応じて扱う。 * セラーは、自身のインベントリデータについてコントローラー、バイヤースコープのキャンペーンデータについてプロセッサーになりうる。 * TMP ルーターオペレーターは、通常両側のプロセッサーで、[TMP privacy architecture](/docs/trusted-match/privacy-architecture) で記述される分離保証の下で動作する。 TMP 固有のデプロイについては、[TMP Data Protection Roles](/docs/trusted-match/data-protection-roles) を参照 — バイヤーエージェントの条件付きプロセッサーポジション、コンテキスト+アイデンティティ結合が委譲されたときの SSP のロール、アイデンティティプロバイダーのリスク形状、TMP の分離保証の外に落ちるインプレッション後フローをカバーするより深い分析。 オペレーターは、各データフローについて自身のロールを文書化し、それを反映する各当事者との DPA を持たなければなりません(MUST)。 ### Subprocessors and LLM providers すべての LLM 駆動エージェントはサブプロセッサーを持ちます: LLM プロバイダー自体、加えて任意の検索サービス、埋め込みストア、ツール統合。各プロバイダーとの DPA は、プロンプト、ブランドアセット、ファーストパーティシグナル、クリエイティブメタデータが保持されるかモデル訓練に使われるかについて明示的でなければなりません。[Security Model — データ処理チェックリスト](/docs/building/concepts/security-model#data-handling-and-subprocessors) を参照。 LLM サブプロセッサーは、機密性だけでなく **完全性** リスクも導入します: ブリーフ、クリエイティブメタデータ、ツール出力内の信頼できないテキストは、エージェントに認証情報を漏らさせ、認可されていないツール呼び出しを発行させ、出力を改ざんさせるプロンプトインジェクションペイロードを運びうる。[エージェンティック広告に固有の脅威](/docs/building/concepts/security-model#threats-specific-to-agentic-advertising) を参照。これはプロトコルのスコープ外ですが、すべてのオペレーターのスコープ内です。 ## Boundaries implementers must handle プロトコルはこれらを強制しません。オペレーターがしなければなりません: * **エンドユーザーの同意とデータ主体の権利**(GDPR 第 15–22 条、CCPA)。 * フィールド形状が強制するものを超えた **目的制限**。 * **越境データ転送制御**(SCC、十分性、UK IDTA)。 * **非構造化フィールドでの PII 発見**(クリエイティブメタデータ、チャットログ、ブリーフテキスト)。AdCP は `ext`、`context`、ブリーフの散文、クリエイティブアセットを PII についてスキャンしません。 * ログの **ログ保持と PII 編集**。 * 信頼できないテキストを処理する LLM 駆動エージェントの **プロンプトインジェクション封じ込め**。 ## For DPOs and procurement reviewers AdCP はプライバシー影響評価テンプレートを公開しません。PIA は、データコントローラーとその法律顧問が所有するデプロイヤーのアーティファクトです — すべてのデプロイの目的、合法的根拠、保持、レジデンシー、サブプロセッサーチェーンは異なり、それらが GDPR 第 35 条評価にとって重要な部分です。プロトコル自体はコントローラーではなく、その分析の代わりにはなれません。 AdCP が代わりに提供するのは、DPO が処理の AdCP 部分を記述するのに必要なプロトコルレベルの入力のセットです。AdCP を使うデプロイの PIA を組み立てるとき、関連する入力は: * **各プロトコルが何を運び禁止するか** — 上記の [Privacy posture by domain](#privacy-posture-by-domain) 要約、加えて各ドメインからリンクされる深いリファレンス。 * **コントローラー / プロセッサーの割り当て** — 上記の [Processor / controller roles](#processor--controller-roles) セクション。オペレーターは依然としてデータフローごとに自身のロールを文書化し(MUST)、各当事者との DPA を持たなければならない。 * **構造的分離(TMP のみ)** — [TMP Privacy Architecture](/docs/trusted-match/privacy-architecture)、デプロイされたときの TEE アテステーションを含む。 * **脅威モデルと運用制御** — [Security Model](/docs/building/concepts/security-model)、[データ処理とサブプロセッサーのチェックリスト](/docs/building/concepts/security-model#data-handling-and-subprocessors) を含む。 * **LLM プロバイダーのサブプロセッサー考慮事項** — 上記の [Subprocessors and LLM providers](#subprocessors-and-llm-providers) セクション、機密性(保持、訓練)と完全性(プロンプトインジェクション)の両方をカバー。 * **明示的な非目標** — [既知の制限](/docs/reference/known-limitations)、プロトコルが何をしないか(プロトコルレベルの PII トランスポートなし、レジデンシーメカニズムなし、侵害通知 SLA なしなど)を名指しし、デプロイヤーがどの制御を所有するかを知る。 レジデンシー、保持、同意取得、データ主体の権利ワークフロー、越境転送メカニズム、目的制限はデプロイの関心事です — AdCP はプロトコル層でそれらを強制せず、テンプレートはデプロイ固有にならずにそれらを意味あるようにカバーできません。 ## Related references * **[TMP Privacy Architecture](/docs/trusted-match/privacy-architecture)** — 構造的分離モデル、TEE アテステーションの詳細付き * **[Security Model](/docs/building/concepts/security-model)** — 脅威モデル、多層防御、デプロイチェックリスト * **[Security(実装リファレンス)](/docs/building/by-layer/L1/security)** — 認証、冪等性、SSRF、ガバナンス検証の規範的ルール * **[ドメインをまたぐプライバシー姿勢](/docs/protocol/architecture#privacy-posture-across-domains)** — 要約表 * **[既知の制限](/docs/reference/known-limitations)** — プロトコルがプライバシーについて何をしないか # List agents Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-discovery/list-agents /static/openapi/registry.yaml get /api/registry/agents List all agents in the registry. Optionally enrich with health checks, capabilities, and property summaries via query parameters. Measurement-vendor filters (`metric_id`, `accreditation`, `q`) imply `type=measurement` when `type` is unset; an explicit `type` other than `measurement` returns 400. # List publishers Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-discovery/list-publishers /static/openapi/registry.yaml get /api/registry/publishers List all registered publishers. # Registry statistics Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-discovery/registry-statistics /static/openapi/registry.yaml get /api/registry/stats Get aggregate statistics about the registry. # Request domain re-crawl Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-discovery/request-domain-re-crawl /static/openapi/registry.yaml post /api/registry/crawl-request Trigger an immediate re-crawl of a publisher domain after updating adagents.json. The crawl runs asynchronously — returns 202 immediately. **Rate limits:** 5 minutes per domain, 30 requests per user per hour. # Search agent inventory profiles Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-discovery/search-agent-inventory-profiles /static/openapi/registry.yaml get /api/registry/agents/search Search agents by inventory profile — channels, markets, content categories, property types, and more. Filters use AND across dimensions and OR within a dimension. Results are ranked by relevance score. # Brand activity history Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/brand-resolution/brand-activity-history /static/openapi/registry.yaml get /api/brands/history Returns the edit history for a brand in the registry, newest first. Only brands with community or enriched edits have history; brand.json-sourced brands are authoritative and do not generate revisions. # Bulk resolve brands Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/brand-resolution/bulk-resolve-brands /static/openapi/registry.yaml post /api/brands/resolve/bulk Resolve up to 100 domains to their canonical brand identities in a single request. **Rate limit:** 20 requests per minute per IP address. # Enrich brand Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/brand-resolution/enrich-brand /static/openapi/registry.yaml get /api/brands/enrich Enrich brand data using Brandfetch. Returns logo, colors, and company information. Authenticated callers may also receive ephemeral Brand Context API identity/positioning/voice data. # Find brands by name Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/brand-resolution/find-brands-by-name /static/openapi/registry.yaml get /api/brands/find Search for brands by name or domain. Returns matching results with basic identity info. # Get brand.json Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/brand-resolution/get-brandjson /static/openapi/registry.yaml get /api/brands/brand-json Fetch the raw brand.json file for a domain. # List brands Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/brand-resolution/list-brands /static/openapi/registry.yaml get /api/brands/registry List all brands in the registry with optional search, pagination, and source filter. # Resolve brand Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/brand-resolution/resolve-brand /static/openapi/registry.yaml get /api/brands/resolve Resolve a domain to its canonical brand identity. Follows brand.json redirects and returns the resolved brand with its house, architecture type, and optional manifest. # Save brand Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/brand-resolution/save-brand /static/openapi/registry.yaml post /api/brands/save Save or update a brand in the registry. Requires authentication. For existing brands, creates a revision-tracked edit. For new brands, creates the brand directly. Cannot edit authoritative brands managed via brand.json. # Set up a hosted brand.json Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/brand-resolution/set-up-a-hosted-brandjson /static/openapi/registry.yaml post /api/brands/setup-my-brand Create or update a hosted brand.json for a domain owned by the authenticated user's organization. Returns the hosted URL and a pointer snippet for DNS setup. # Bulk property identifier check Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/property-resolution/bulk-property-identifier-check /static/openapi/registry.yaml post /api/properties/check/bulk Check up to 10,000 property identifiers (domains, app bundle IDs, CTV store URLs) against the registry catalog. Returns a verdict for each identifier and a summary. # Bulk resolve properties Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/property-resolution/bulk-resolve-properties /static/openapi/registry.yaml post /api/properties/resolve/bulk Resolve up to 100 publisher domains at once. **Rate limit:** 20 requests per minute per IP address. # Check property list Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/property-resolution/check-property-list /static/openapi/registry.yaml post /api/properties/check Check a list of publisher domains against the AAO registry. Normalizes domains (strips www/m prefixes), removes duplicates, flags known ad tech infrastructure, and identifies domains not yet in the registry. Returns four buckets: - **remove**: duplicates or known blocked domains (ad servers, CDNs, trackers, intermediaries) - **modify**: domains that were normalized (e.g. www.example.com → example.com) - **assess**: unknown domains not in registry, not blocked - **ok**: domains found in registry with no changes needed Results are stored for 7 days and retrievable via the `report_id`. # Claim a domain for bind-on-verify Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/property-resolution/claim-a-domain-for-bind-on-verify /static/openapi/registry.yaml post /api/properties/hosted/{domain}/claim Issue a pending domain claim for the caller's organization and return a claim-specific `authoritative_location` URL (`…/adagents.json?adcp_claim=`). The caller places that single pointer at their own origin `/.well-known/adagents.json`; a subsequent verify-origin reads the token and binds the domain to the caller's org. The token is the per-account artifact that proves WHICH account owns the domain — a plain domain-keyed pointer proves only that the origin endorses AAO hosting, not who the owner is. The community write surface stays open; this does not gate writes — it establishes ownership on successful verification. Refused with 409 only when the domain is already verified and locked to a different owner. # Get bulk check report Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/property-resolution/get-bulk-check-report /static/openapi/registry.yaml get /api/properties/check/bulk/{reportId} Retrieve a previously generated bulk property check report by ID. # Get property check report Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/property-resolution/get-property-check-report /static/openapi/registry.yaml get /api/properties/check/{reportId} Retrieve a previously stored property check report by ID. Reports expire after 7 days. # List properties Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/property-resolution/list-properties /static/openapi/registry.yaml get /api/properties/registry List all properties in the registry with optional search, pagination. # Property activity history Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/property-resolution/property-activity-history /static/openapi/registry.yaml get /api/properties/history Returns the edit history for a property in the registry, newest first. # Resolve property Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/property-resolution/resolve-property /static/openapi/registry.yaml get /api/properties/resolve Resolve a publisher domain to its property information. Checks hosted properties, discovered properties, then live adagents.json validation. # Save property Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/property-resolution/save-property /static/openapi/registry.yaml post /api/properties/save Save or update a hosted property in the registry. Requires authentication. For existing properties, creates a revision-tracked edit. For new properties, creates the property directly. Cannot edit authoritative properties managed via adagents.json. This is an identity-only write surface: the stored document always carries `authorized_agents: []`. Sales authorization lives solely in the publisher's own origin `adagents.json`; the community registry cannot mint or carry it. Any `authorized_agents` sent in the request body is ignored. # Validate adagents.json Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/property-resolution/validate-adagentsjson /static/openapi/registry.yaml get /api/properties/validate Validate a domain's adagents.json file and return the validation result. # Verify AAO-hosted publisher origin Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/property-resolution/verify-aao-hosted-publisher-origin /static/openapi/registry.yaml post /api/properties/hosted/{domain}/verify-origin Trigger origin verification for an AAO-hosted publisher: fetches the publisher's own `/.well-known/adagents.json` and checks for an `authoritative_location` field pointing at the AAO-hosted URL. On success, promotes `agent_publisher_authorizations` rows from `source='aao_hosted'` to `source='adagents_json'` for the manifest's authorized agents — buyers reading the registry then see them as origin-attested. Bind-on-verify: when the pointer carries an `adcp_claim` token (see the claim endpoint), a successful verification binds the domain to that claim's organization and returns `bound_org_id`. Binding is driven by which token the origin pointer carries, never by who triggers verification, so any authenticated caller may trigger it and a squatter cannot bind a domain they don't control. An existing verified owner is never overwritten. Failure classification: - `not_found`: publisher origin returned 404 (permanent — demotes if previously verified). - `invalid_json` / `no_authoritative_location` / `authoritative_location_mismatch`: publisher origin returned a parseable response that doesn't satisfy the spec stub pattern (permanent — demotes). - `unresolvable`: DNS NXDOMAIN, private IP, or non-http scheme (permanent — demotes). - `transient`: 5xx / 429 / 3xx / network timeout (leaves persisted state alone, stamps `origin_last_checked_at`). # API discovery Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/search/api-discovery /static/openapi/registry.yaml get /api Returns links to the main API entry points and documentation. # Manifest reference lookup Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/search/manifest-reference-lookup /static/openapi/registry.yaml get /api/manifest-refs/lookup Find the best manifest reference (brand.json URL or agent) for a domain. # Search Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/search/search /static/openapi/registry.yaml get /api/search Search across brands, publishers, and properties. Returns up to 5 results per category. # Registry API Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/index AdCP エコシステムにおけるブランド解決、プロパティルックアップ、エージェント探索、認可のためのパブリック REST API。 AgenticAdvertising.org レジストリは、AdCP エコシステムにおけるブランドとプロパティの解決、エージェントの発見、認可の検証のためのパブリック REST API を提供します。 ## ベース URL ``` https://agenticadvertising.org ``` ほとんどのエンドポイントは**パブリックで認証不要**です。[認証済みエンドポイント](#authenticated-endpoints)には Bearer トークンが必要です。 完全な [OpenAPI 3.1 仕様](https://agenticadvertising.org/openapi/registry.yaml) がコード生成とツール用に利用可能です。`/.well-known/openapi.yaml` でも発見できます。 ## クイックスタート ブランドドメインを正準アイデンティティに解決します。 ```bash cURL theme={null} curl "https://agenticadvertising.org/api/brands/resolve?domain=acmecorp.com" ``` ```javascript JavaScript theme={null} const res = await fetch( "https://agenticadvertising.org/api/brands/resolve?domain=acmecorp.com" ); const brand = await res.json(); console.log(brand.brand_name, brand.canonical_domain); ``` ```python Python theme={null} import requests brand = requests.get( "https://agenticadvertising.org/api/brands/resolve", params={"domain": "acmecorp.com"} ).json() print(brand["brand_name"], brand["canonical_domain"]) ``` ```json Response theme={null} { "canonical_id": "acmecorp.com", "canonical_domain": "acmecorp.com", "brand_name": "Acme Corp", "keller_type": "master", "house_domain": "acmecorp.com", "source": "brand_json" } ```

レート制限

| Endpoint | Limit | | --------------------------------------------------------------- | ------------------------------ | | 一括解決(`/api/brands/resolve/bulk`、`/api/properties/resolve/bulk`) | IP あたり 20 リクエスト/分 | | 保存エンドポイント(`/api/brands/save`、`/api/properties/save`) | ユーザーあたり 60 リクエスト/時 | | クロールリクエスト(`/api/registry/crawl-request`) | ドメインあたり 5 分、ユーザーあたり 30 リクエスト/時 | | その他すべてのエンドポイント | 制限なし | レート制限されたエンドポイントは、制限を超えると `429 Too Many Requests` を返します。 ## エンドポイントグループ ドメインを正準ブランドアイデンティティに解決し、brand.json ファイルを取得し、ブランドレジストリを参照。 パブリッシャードメインをプロパティ情報に解決し、adagents.json を検証し、プロパティを参照。 インベントリプロファイルでエージェントをリスト、検索、フィルタリング。パブリッシャーを参照し、レジストリ統計を表示。 ローカル同期のためにレジストリ変更のカーソルベースフィードをポーリング。 ドメインでエージェントをルックアップし、プロダクト認可を検証し、プロパティ認可をリアルタイムで確認。 ## エンティティ別ルックアップ 3 つのエンドポイントが、レジストリに誰がいるかについての異なる質問に答えます。それらは API リファレンスの 2 つのタググループにまたがるため、エンドポイント固有のパラメーターに入る前に、この表を使って正しいルックアップサーフェスを選んでください。 | Endpoint | Auth surface | What it returns | | -------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | `GET /api/registry/agents` | パブリックカタログ。認証済みの AgenticAdvertising.org API 階層メンバーは `members_only` エージェントも見られます。 | AgenticAdvertising.org が証明したメンバー登録エージェントカタログ。ヘルス、機能、プロパティ、コンプライアンス、測定メトリクス、検証状態の任意のフィルターとエンリッチメント付き。 | | `GET /api/registry/operator?domain=X` | Auth 対応: 匿名の呼び出し元は `public` を、AgenticAdvertising.org API 階層メンバーは `members_only` も、プロファイル所有者は `private` も見られます。 | クエリされたエンティティが運営するエージェントと、それらのエージェントを信頼するパブリッシャー。 | | `GET /api/registry/publisher?domain=X` | パブリックで未認証。AgenticAdvertising.org メンバーシップはレスポンス形状を変えません。 | クエリされたエンティティが公開するインベントリ(`properties[]`)と、それが認可するエージェント(`adagents.json` からの `authorized_agents[]`)。 | **`GET /api/registry/agents`** — パブリックなエージェント母集団を参照またはフィルタリングしたいときに使います。エージェントがこのカタログに入る方法については [エージェントの登録](/docs/registry/registering-an-agent) を参照。リファレンス: Agent Discovery の [List agents エンドポイント](#agent-discovery)。 **`GET /api/registry/operator?domain=X`** — 1 つのエンティティを手にしていて、そのエージェントフットプリントを知りたいときに使います。 **`GET /api/registry/publisher?domain=X`** — 1 つのパブリッシャーを手にしていて、そのインベントリと委任を知りたいときに使います。

ブランド解決

これらのエンドポイントはドメインをブランドアイデンティティに解決します。レスポンスの `source` フィールドがデータの出所を示します。 | Source | Meaning | | ------------ | ---------------------------------------- | | `brand_json` | ドメインの `/.well-known/brand.json` ファイルから解決 | | `enriched` | Brandfetch API 経由でエンリッチ | | `community` | コミュニティメンバーが提出 | すべてのソースが同じ解決レスポンス構造を生成します。完全なブランドアイデンティティデータ(logos、colors、tone)を得るには、`/api/brands/enrich` を使うか、レジストリでブランドをルックアップします。 | Method | Path | Description | | ------ | -------------------------- | --------------------------- | | GET | `/api/brands/resolve` | ドメインを正準ブランドに解決 | | POST | `/api/brands/resolve/bulk` | 最大 100 ドメインを一度に解決 | | GET | `/api/brands/brand-json` | ドメインの生の brand.json を取得 | | GET | `/api/brands/registry` | すべてのブランドをリスト(検索、ページネーション) | | GET | `/api/brands/enrich` | Brandfetch 経由でブランドデータをエンリッチ | | GET | `/api/brands/history` | ブランドの編集履歴 | | POST | `/api/brands/save` | コミュニティブランドを保存または更新(認証必須) |

プロパティ解決

| Method | Path | Description | | ------ | ------------------------------ | -------------------------- | | GET | `/api/properties/resolve` | ドメインをプロパティ情報に解決 | | POST | `/api/properties/resolve/bulk` | 最大 100 ドメインを一度に解決 | | GET | `/api/properties/registry` | すべてのプロパティをリスト(検索、ページネーション) | | GET | `/api/properties/validate` | ドメインの adagents.json を検証 | | GET | `/api/properties/history` | プロパティの編集履歴 | | POST | `/api/properties/save` | ホストされたプロパティを保存または更新(認証必須) |

エージェント探索

| Method | Path | Description | | | | | | | | | ------ | ----------------------------- | ------------------------------------------------ | ------ | ----------- | ---------- | -------- | ----- | ------ | ---------- | | GET | `/api/registry/agents` | すべてのメンバー登録エージェントをリスト — タイプでフィルタリング(\`?type=brand | rights | measurement | governance | creative | sales | buying | signals\`) | | GET | `/api/registry/agents/search` | インベントリプロファイルでエージェントを検索(認証必須) | | | | | | | | | GET | `/api/registry/publishers` | すべてのパブリッシャーをリスト | | | | | | | | | GET | `/api/registry/stats` | レジストリ統計 | | | | | | | | | POST | `/api/registry/crawl-request` | パブリッシャードメインの再クロールをリクエスト(認証必須) | | | | | | | | #### 測定ベンダー探索 測定ベンダー(Adelaide スタイルの attention、Scope3 スタイルの emissions、Nielsen DAR、IAS/DV カスタム品質など)を特に発見するには、エージェントリストを `type=measurement` でフィルタリングします。 ```bash theme={null} curl "https://agenticadvertising.org/api/registry/agents?type=measurement" ``` これは、AAO メンバーによってレジストリに登録されたすべての測定エージェントを返します。 **メトリクスごとのカタログ探索。** 各測定エージェントは、その完全なメトリクスごとのカタログを [`get_adcp_capabilities.measurement.metrics[]`](/docs/protocol/get_adcp_capabilities#measurement) — 正準の、ベンダー管理の真実の源泉 — に公開します。AAO は各測定エージェントの `get_adcp_capabilities` を TTL でクロールし結果を保存します。`?capabilities=true` を渡すと、カタログが `creative_capabilities` と `signals_capabilities` の隣でレスポンスに折り込まれます。 **フィルターパラメーター**(存在するときすべて `type=measurement` を意味します。明示的な非測定タイプは 400 を返します): | Param | Match | Notes | | --------------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------------- | | `metric_id=attention_units` | `metrics[].metric_id` の完全一致 | 繰り返し可能。パラメーター内の複数の値は OR。 | | `accreditation=MRC` | `metrics[].accreditations[].accrediting_body` の完全一致 | 繰り返し可能。ベンダー主張 — レスポンスの `verified_by_aao` は常に `false`。レンダラーはこれらを AAO の裏書きではなくベンダーの主張としてマークすべき。 | | `q=attention` | `metric_id` の大文字小文字を区別しない部分文字列 | v1 スコープ: metric\_id のみ。最大 64 文字。SQL ワイルドカード(`%`、`_`)は拒否。説明/標準のファジー検索はフォローアップ。 | ```bash theme={null} # アテンション測定を提供するすべてのベンダー curl "https://agenticadvertising.org/api/registry/agents?metric_id=attention_units&capabilities=true" # MRC 認定のビューアビリティベンダー curl "https://agenticadvertising.org/api/registry/agents?type=measurement&accreditation=MRC&q=viewab&capabilities=true" ``` **直接呼び出し対インデックス — どちらをいつ使うか。** バイヤーはベンダーのメトリクスカタログへの 2 つのパスを持ちます。 | Use case | Path | Why | | ------------------------------------------- | -------------------------------- | -------------------------------------------------------------------------------- | | ディスカバリー/プランニング(「どのベンダーが attention を提供するか?」) | AAO インデックス(`?metric_id=...`) | 事前集計済み、キャッシュ済み、高速。1 回の呼び出しでクロスベンダー。AAO TTL ウィンドウ(通常 24 時間)まで古い可能性。 | | 決済/監査(「Adelaide は現在メトリクス X をサポートするか?」) | `get_adcp_capabilities` への直接呼び出し | ライブ、正準、古さなし。バイヤーはすでに配信/照合時に測定エージェントを呼び出している。1 回の追加呼び出しは安価で、監査証跡からインデックスの古さを除去する。 | | `get_products` 時のフィルタリング | AAO インデックス | バイヤーは高速パスのクエリにおり、セラーのプロダクトカタログはすでにどのベンダーが有効かを知る必要がある。 |

Change Feed

| Method | Path | Description | | ------ | --------------------------- | ----------------------------------------------------- | | GET | `/api/registry/feed` | カーソルベースのレジストリ変更フィードをポーリング(認証必須) | | GET | `/api/registry/feed/stream` | カーソルベースのレジストリフィードページを Server-Sent Events でストリーム(認証必須) |

ルックアップと認可

| Method | Path | Description | | ------ | ----------------------------------------------- | ------------------- | | GET | `/api/registry/lookup/domain/{domain}` | ドメインに認可されたエージェントを検索 | | GET | `/api/registry/lookup/property` | プロパティ識別子でエージェントを検索 | | GET | `/api/registry/lookup/agent/{agentUrl}/domains` | エージェントのすべてのドメインを取得 | | POST | `/api/registry/validate/product-authorization` | エージェントのプロダクト認可を検証 | | POST | `/api/registry/expand/product-identifiers` | プロパティセレクターを識別子に展開 | | GET | `/api/registry/validate/property-authorization` | リアルタイム認可チェック | 認可検証は両側をチェックします: パブリッシャーの `adagents.json`(主張された `delegation_type` でこのエージェントを認可するか?)とオペレーターの `brand.json`(一致する `relationship` でこのプロパティを宣言するか?)。 ### バリデーションツール | Method | Path | Description | | ------ | ------------------------ | ----------------------- | | POST | `/api/adagents/validate` | ドメインの adagents.json を検証 | | POST | `/api/adagents/create` | adagents.json コンテンツを生成 | ### 検索 | Method | Path | Description | | ------ | --------------------------- | ----------------------- | | GET | `/api/search` | ブランド、パブリッシャー、プロパティを横断検索 | | GET | `/api/manifest-refs/lookup` | ドメインのマニフェスト参照を検索 | ### エージェントプロービング | Method | Path | Description | | ------ | -------------------------------- | ------------------------ | | GET | `/api/public/discover-agent` | エージェント URL の機能をプローブ | | GET | `/api/public/agent-formats` | エージェントからクリエイティブフォーマットを取得 | | GET | `/api/public/agent-products` | セールスエージェントからプロダクトを取得 | | GET | `/api/public/validate-publisher` | パブリッシャードメインを検証 | ## アクティビティ履歴 `GET /api/brands/history?domain={domain}` と `GET /api/properties/history?domain={domain}` は、レジストリエントリの編集履歴を新しい順で返します。これらはパブリックエンドポイントです — 認証不要。 ```json Response theme={null} { "domain": "acmecorp.com", "total": 3, "revisions": [ { "revision_number": 3, "editor_name": "Pinnacle Media", "edit_summary": "Updated logo URL", "source": "community", "is_rollback": false, "created_at": "2026-03-01T12:34:56Z" }, { "revision_number": 2, "editor_name": "system", "edit_summary": "API: enriched via Brandfetch", "source": "enriched", "is_rollback": false, "created_at": "2026-02-15T08:00:00Z" } ] } ``` `editor_name: "system"` のエントリは自動エンリッチメントによって書き込まれました。`is_rollback` が `true` のとき、`rolled_back_to` に復元されたリビジョン番号が含まれます。ページネーションは `limit`(最大 100)と `offset` クエリパラメーターを使います。 ## 不正防止とアンチホモグラフ制御 `/api/brands/save`、`/api/properties/save`、および `adagents` 検証エンドポイントは、認証済みメンバー組織からドメイン文字列を受け入れるため、ホストされたレジストリは保存時に多層の不正防止制御の下限を適用します。これらは 3.x 時代の AgenticAdvertising.org レジストリの運用挙動であり — 新しいワイヤサーフェスではありません — タイポスクワット、混同可能なそっくりさん、通りすがりのブランドハイジャックが、単一の認証済み呼び出し元によってインデックスに書き込まれないように存在します。 * **ドメイン正規化(IDNA 2008 + 混同可能検出)。** 保存エンドポイントは、永続化前に国際化ドメイン名を ASCII に正規化するために IDNA 2008 を適用すべきで(SHOULD)、次に 2 つのコーパス — (1) インデックス内のすでに登録されたエントリ、(2) レジストリオペレーターが維持する厳選された高価値ブランドの拒否リスト — に対して Unicode 混同可能検出(例: ICU `uspoof` または同等物)を実行すべきです(SHOULD)。拒否リストは、有名ブランドが自身で登録される前に、それらのタイポスクワットを捕捉します(例: `g00gle.com` の提出は、Google がまだインデックス行を主張していなくても拒否リストのエントリと衝突します)。曖昧な提出 — 混合スクリプトラベル、ホモグラフ衝突、許可されない Unicode クラス — は、黙ってコミットするのではなく、拒否または人間のレビューのためにフラグを立てるべきです(SHOULD)。 * **コミット前の所有権証明。** 保存エンドポイントは、**新しい**ブランドまたはプロパティエントリがインデックスにコミットされる前にドメイン管理の証拠を要求しなければなりません(MUST)— これがこの制御を動機付ける脅威です。侵害されたメンバー API キーを持つ攻撃者は、そうでなければ衝突する以前のエントリを持たない新鮮な混同可能バリアントを自由に一括登録できるからです。同じ認証済み組織による既存のコミュニティソースエントリへの**リビジョン**については、再証明はローリングベースで要求されるべきですが(SHOULD。例: 以前の証明が 90 日より古くなったら)、そのウィンドウ内ではスキップしてもかまいません(MAY)。受け入れられる証明は、サーバー発行の nonce に一致する `_adcp-owner.{domain}` の DNS TXT レコード、またはドメイン上の `/.well-known/adcp-ownership.txt` にホストされた HTTP チャレンジのいずれかです。nonce は単一使用で、`(organization, domain)` ペアにスコープされ、発行から **15 分**以内に期限切れになければなりません(MUST)。検証は成功時に nonce を消費し、失敗時に無効化しなければなりません(MUST)。期限切れ後のリークまたは未使用の nonce は死んでいます。既存の**権威ある**エントリ(すなわち `brand.json` / `adagents.json` に裏付けられたもの)へのリビジョンは、[Save brand](#save-brand) と [Save property](#save-property) の 409 Conflict セマンティクスに従い続けます。所有権証明はコミュニティソースの保存パスをカバーします。 * **保存に対する組織ごとのレート制限。** [レート制限](#rate-limits)に文書化された IP ごとのレート制限に加えて、保存エンドポイントは、単一の侵害された API キーが混同可能バリアントを一括登録できないよう、組織ごとの制限を適用すべきです(SHOULD)。ホストされた実装はバースト許容の上限を使います(目安: 組織あたり数十保存/時、組織あたり数百/日)。組織ごとのバケットを超える呼び出し元は `429 Too Many Requests` を受け取ります。 これらの制御はホストされた AgenticAdvertising.org レジストリによって強制されます。[Change Feed](#change-feed) を消費するセルフホストミラーは、ホストされたレジストリの保存時チェックに依存し、それらを再実行しません — これはフィードのアドバイザリアイデンティティの姿勢(`specs/registry-change-feed.md` §Advisory identity material を参照)と一貫しています: フィードは変更検出であり信頼アンカーではなく、パブリッシャー自身の `adagents.json` ピンが権威あるアイデンティティソースのままです([`adagents.json` §`signing_keys`](/docs/governance/property/adagents#signing_keys) を参照)。代替レジストリ実装を運営するオペレーターは、コミュニティソースの書き込みを受け入れる前に同等の保存時制御を適用すべきです(SHOULD)。 ## 認証 パブリックエンドポイント(解決、探索、検索)は認証不要です。書き込みエンドポイントは、**組織 API キー**(サーバー間)または OAuth 2.1 経由で取得した**ユーザー JWT**(インタラクティブ/エージェントクライアント)のいずれかを受け入れます。両方とも `Authorization: Bearer ...` ヘッダーで送信されます。 ### Option A: 組織 API キー 長寿命、組織スコープ。ユーザーが存在しないサーバー間統合に最適。 1. [agenticadvertising.org/dashboard/api-keys](https://agenticadvertising.org/dashboard/api-keys) でサインイン 2. **Create key** をクリックし、生成されたキーをコピー `Authorization` ヘッダーでキーを渡します。 ``` Authorization: Bearer sk_... ``` ### Option B: OAuth 2.1 経由のユーザー SSO 短寿命、ユーザースコープ。人間が AAO にサインインするエージェントクライアント(MCP、AI アシスタント、カスタムアプリ)に最適。単一のトークンが `/mcp` と REST API の両方に対して機能します。 ディスカバリーは [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414) と [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) に従います。 * 認可サーバーメタデータ: `GET /.well-known/oauth-authorization-server` * 保護リソースメタデータ(REST API): `GET /.well-known/oauth-protected-resource/api` * 保護リソースメタデータ(MCP): `GET /.well-known/oauth-protected-resource/mcp` フローは PKCE 付き認可コードです。動的クライアント登録([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591))が `/register` で利用可能です。ユーザーは AuthKit 経由で認証します。トークンは WorkOS 署名の JWT です。 ``` Authorization: Bearer ``` 有効なユーザー JWT はアイデンティティを証明し、エンタイトルメントは証明しません。組織メンバーシップまたは管理者ロールでゲートされたエンドポイント(ほとんどの書き込みエンドポイント)は、認証済みユーザーが必要な立場を欠く場合、依然として `403` を返します。

認証済みエンドポイント

これらのエンドポイントには有効な API キーが必要です。

Save brand

`POST /api/brands/save` レジストリでコミュニティブランドを保存または更新します。既存のブランドについては、リビジョン追跡された編集を作成します。`brand.json` 経由で管理される権威あるブランドは編集できません — それらは `409 Conflict` を返します。 **リクエストボディ:** ```json theme={null} { "domain": "acmecorp.com", "brand_name": "Acme Corp", "brand_manifest": { "name": "Acme Corp", "description": "A fictional company", "logos": [{ "url": "https://acmecorp.com/logo.svg", "tags": ["icon"] }], "colors": [{ "hex": "#FF5733", "type": "accent" }] } } ``` `domain` と `brand_name` は必須です。`brand_manifest`(ブランドアイデンティティデータ)は任意です。ブランドの `source` はサーバーによって `"community"` に設定されます。ドメインは正規化されます(プロトコル除去、小文字化)。 ```bash cURL theme={null} curl -X POST "https://agenticadvertising.org/api/brands/save" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"domain":"acmecorp.com","brand_name":"Acme Corp"}' ``` ```javascript JavaScript theme={null} const res = await fetch( "https://agenticadvertising.org/api/brands/save", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ domain: "acmecorp.com", brand_name: "Acme Corp", }), } ); const result = await res.json(); ``` ```python Python theme={null} import requests result = requests.post( "https://agenticadvertising.org/api/brands/save", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={"domain": "acmecorp.com", "brand_name": "Acme Corp"}, ).json() ``` ```json Response (create) theme={null} { "success": true, "message": "Brand \"Acme Corp\" saved to registry", "domain": "acmecorp.com", "id": "br_abc123" } ``` ```json Response (update) theme={null} { "success": true, "message": "Brand \"Acme Corp\" updated in registry (revision 2)", "domain": "acmecorp.com", "id": "br_abc123", "revision_number": 2 } ```

Save property

`POST /api/properties/save` レジストリでホストされたプロパティを保存または更新します。既存のプロパティについては、リビジョン追跡された編集を作成します。`adagents.json` 経由で管理される権威あるプロパティは編集できません — それらは `409 Conflict` を返します。 これはアイデンティティのみの書き込みサーフェスです: 保存されるドキュメントは常に `authorized_agents: []` を運びます。セールス認可はパブリッシャー自身のオリジン `adagents.json` にのみ存在します — コミュニティレジストリはそれを作成も運搬もできません — したがってリクエストボディで送られる任意の `authorized_agents` は無視されます。 **リクエストボディ:** ```json theme={null} { "publisher_domain": "examplepub.com", "properties": [ { "type": "website", "name": "Example Publisher" } ], "contact": { "name": "Ad Ops", "email": "adops@examplepub.com" } } ``` `publisher_domain` は必須です。`properties`(それぞれ `type` と `name` を要求)と `contact` は任意です。ドメインは正規化されます(プロトコル除去、小文字化)。 ```bash cURL theme={null} curl -X POST "https://agenticadvertising.org/api/properties/save" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "publisher_domain": "examplepub.com", "properties": [{"type": "website", "name": "Example Publisher"}] }' ``` ```javascript JavaScript theme={null} const res = await fetch( "https://agenticadvertising.org/api/properties/save", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ publisher_domain: "examplepub.com", properties: [{ type: "website", name: "Example Publisher" }], }), } ); const result = await res.json(); ``` ```python Python theme={null} import requests result = requests.post( "https://agenticadvertising.org/api/properties/save", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "publisher_domain": "examplepub.com", "properties": [{"type": "website", "name": "Example Publisher"}], }, ).json() ``` ```json Response (create) theme={null} { "success": true, "message": "Hosted property created for examplepub.com", "id": "prop_xyz789" } ``` ```json Response (update) theme={null} { "success": true, "message": "Property 'examplepub.com' updated (revision 2)", "id": "prop_xyz789", "revision_number": 2 } ``` #### Change feed `GET /api/registry/feed` レジストリ変更のカーソルベースフィードをポーリングします。これを使って、完全なデータセットを再取得せずにレジストリのローカルコピーを同期に保ちます。イベントは UUID v7 の `event_id` で順序付けられ、単調なカーソル進行を提供します。フィードはイベントを 90 日間保持します — 期限切れのカーソルは `410 Gone` を返します。各レスポンスには、コンシューマーが要求したタイプフィルターのフィードラグを測定できるよう `freshness` メタデータが含まれます。 **スキーマ:** [`core/registry-feed-response.json`](https://adcontextprotocol.org/schemas/v3/core/registry-feed-response.json) が [`core/registry-event.json`](https://adcontextprotocol.org/schemas/v3/core/registry-event.json) アイテムをラップします。 **クエリパラメーター:** | Parameter | Type | Default | Description | | --------- | ------ | ------- | -------------------------------------------------- | | `cursor` | UUID | — | このイベント ID の後から再開。最も早く利用可能なイベントには省略。 | | `types` | string | — | カンマ区切りのイベントタイプフィルター。グロブパターンをサポート(例: `property.*`)。 | | `limit` | number | 100 | ページあたりの最大イベント数(1〜10,000)。 | **イベントタイプ:** | Type | Description | | ------------------------------- | ------------------------------------------------------------------------------------------------ | | `property.created` | 新しいプロパティがレジストリに追加された | | `property.updated` | プロパティメタデータが変更された | | `property.merged` | 2 つのプロパティレコードがマージされた | | `property.stale` | プロパティが再クロール検証に失敗した | | `property.reactivated` | 古いプロパティが再クロールに合格した | | `collection.created` | 新しいパブリッシャーコレクションがレジストリに追加された | | `collection.updated` | コレクションメタデータまたは配信識別子が変更された | | `collection.merged` | 2 つのコレクションレコードがマージされた | | `collection.removed` | コレクションがパブリッシャーの権威あるカタログでもはや見えない | | `agent.discovered` | 新しい `agent_url` がパブリッシャーの `adagents.json` に現れた(認可グラフ。エージェントが `/api/registry/agents` にあることを意味しない) | | `agent.removed` | エージェントがレジストリから削除された | | `agent.profile_updated` | エージェントのインベントリプロファイルが変更された | | `agent.compliance_changed` | エージェントのコンプライアンスまたは検証状態が変更された | | `agent.verification_earned` | エージェントが AAO Verified バッジを獲得した | | `agent.verification_lost` | エージェントが AAO Verified バッジを失った | | `publisher.adagents_discovered` | パブリッシャーの adagents.json が発見されレジストリに投影された | | `publisher.adagents_changed` | パブリッシャーの adagents.json が更新された | | `authorization.granted` | エージェントがプロパティに認可された | | `authorization.revoked` | 認可が削除された | | `authorization.modified` | 認可は見えるままだが、外部から見えるメタデータが変更された | ```bash cURL theme={null} curl "https://agenticadvertising.org/api/registry/feed?types=property.*&limit=50" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```javascript JavaScript theme={null} const res = await fetch( "https://agenticadvertising.org/api/registry/feed?types=property.*&limit=50", { headers: { Authorization: "Bearer YOUR_API_KEY" } } ); const feed = await res.json(); // Store feed.cursor for next poll ``` ```python Python theme={null} import requests feed = requests.get( "https://agenticadvertising.org/api/registry/feed", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={"types": "property.*", "limit": 50}, ).json() # Store feed["cursor"] for next poll ``` ```json Response theme={null} { "events": [ { "event_id": "019539a0-1234-7000-8000-000000000001", "event_type": "property.created", "entity_type": "property", "entity_id": "019539a0-b1c2-7000-8000-000000000002", "payload": { "property_rid": "019539a0-b1c2-7000-8000-000000000002", "classification": "property", "source": "contributed", "identifiers": [ { "type": "domain", "value": "streamer.example.com" } ] }, "actor": "crawler", "created_at": "2026-03-31T10:00:00.000Z" } ], "cursor": "019539a0-1234-7000-8000-000000000001", "has_more": true, "freshness": { "generated_at": "2026-03-31T10:00:15.000Z", "latest_event_created_at": "2026-03-31T10:00:00.000Z", "lag_seconds": 15, "retention_days": 90 } } ``` `has_more` が `true` のとき、返された `cursor` 値を次のリクエストで渡してポーリングを続けます。`false` のとき、現在のフィードの終わりに達しています — 後で同じカーソルで再度ポーリングして新しいイベントを取得します。 `latest_event_created_at` は、現在フィードに見える最新の一致イベントです。`lag_seconds` は `generated_at` に対して計算されます。自身のミラー鮮度目標に応じてアラートを設定します。 `GET /api/registry/feed/stream` は、同じフィードページを Server-Sent Events で提供します。切断後は最後に永続化したカーソルで再接続します。ストリームは上記と同じ JSON 形状で `event: feed` を、追いついている間 `event: heartbeat` を発します。カーソルは同じ論理 `types` サブスクリプションに結び付けられます。カーソルを再利用しながらフィルターを変更すると、以前フィルターされたイベントをスキップする可能性があります。ストリームは再開カーソルを SSE `id` ではなく JSON `data.cursor` で運ぶため、ブラウザ `EventSource` クライアントは `?cursor=...` で再接続しなければなりません。 カーソルが期限切れ(90 日より古いか見つからない)の場合、レスポンスは `410 Gone` です。 ```json 410 Gone theme={null} { "error": "cursor_expired", "message": "Cursor is older than 90-day retention window. Re-bootstrap from /registry/agents/search, /catalog, and /catalog/collections/sync." } ``` #### Agent search `GET /api/registry/agents/search` インベントリプロファイル — チャネル、市場、コンテンツカテゴリ、プロパティタイプなど — でエージェントを検索します。フィルターは次元間で AND、次元内で OR を使います。結果は、フィルターマッチの幅、インベントリの深さ、TMP サポートに基づく関連性スコアでランク付けされます。 **クエリパラメーター:** | Parameter | Type | Default | Description | | ---------------- | ------- | ------- | ------------------------------------------- | | `channels` | CSV | — | チャネルでフィルタリング(例: `ctv,olv,display`) | | `property_types` | CSV | — | プロパティタイプでフィルタリング(例: `ctv_app,website`) | | `markets` | CSV | — | 市場/国コードでフィルタリング(例: `US,GB`) | | `categories` | CSV | — | IAB コンテンツカテゴリでフィルタリング(例: `IAB-7,IAB-7-1`) | | `tags` | CSV | — | タグでフィルタリング(例: `premium,brand_safe`) | | `delivery_types` | CSV | — | 配信タイプでフィルタリング(例: `guaranteed,programmatic`) | | `has_tmp` | boolean | — | TMP サポートを要求(`true` または `false`) | | `min_properties` | number | — | インベントリ内の最小プロパティ数 | | `cursor` | string | — | 前のレスポンスからのページネーションカーソル | | `limit` | number | 50 | ページあたりの最大結果数(1〜200) | 各 CSV パラメーターは最大 100 値を受け入れます。 ```bash cURL theme={null} curl "https://agenticadvertising.org/api/registry/agents/search?channels=ctv,olv&markets=US&has_tmp=true" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```javascript JavaScript theme={null} const res = await fetch( "https://agenticadvertising.org/api/registry/agents/search?channels=ctv,olv&markets=US&has_tmp=true", { headers: { Authorization: "Bearer YOUR_API_KEY" } } ); const agents = await res.json(); ``` ```python Python theme={null} import requests agents = requests.get( "https://agenticadvertising.org/api/registry/agents/search", headers={"Authorization": "Bearer YOUR_API_KEY"}, params={"channels": "ctv,olv", "markets": "US", "has_tmp": "true"}, ).json() ``` ```json Response theme={null} { "results": [ { "agent_url": "https://ads.streamhaus.example.com", "channels": ["ctv", "olv"], "property_types": ["ctv_app", "website"], "markets": ["US", "GB", "CA"], "categories": ["IAB-7", "IAB-7-1"], "tags": ["premium"], "delivery_types": ["guaranteed"], "format_ids": [], "property_count": 42, "publisher_count": 3, "has_tmp": true, "category_taxonomy": null, "relevance_score": 0.92, "matched_filters": ["channels", "markets"], "updated_at": "2026-03-31T10:00:00.000Z" } ], "cursor": "MC45Mjpodh...", "has_more": false } ``` `matched_filters` 配列は、どのフィルター次元が一致したかを示し、結果が返された理由を理解するのに役立ちます。`relevance_score` は、フィルターマッチの幅、0.1 で重み付けされた `ln(property_count + 1)`、TMP サポートの 0.05 のブーストを組み合わせます。 #### Crawl request `POST /api/registry/crawl-request` パブリッシャードメインの即時再クロールをリクエストします。`adagents.json` ファイルを更新した後にこれを使うと、次のスケジュールされたクロールを待たずにレジストリが変更を取得します。クロールは非同期で実行されます — エンドポイントは直ちに `202 Accepted` を返します。 ドメインあたり 5 分ごとに 1 リクエスト、ユーザーあたり 1 時間に 30 リクエストにレート制限されます。 **リクエストボディ:** ```json theme={null} { "domain": "examplepub.com" } ``` `domain` は必須です。ドメインは正規化されます(小文字化、トリム)。エンドポイントはドメイン形式を検証し、DNS ルックアップを実行してプライベート/予約済み IP アドレスを拒否します。 ```bash cURL theme={null} curl -X POST "https://agenticadvertising.org/api/registry/crawl-request" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"domain":"examplepub.com"}' ``` ```javascript JavaScript theme={null} const res = await fetch( "https://agenticadvertising.org/api/registry/crawl-request", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ domain: "examplepub.com" }), } ); const result = await res.json(); // 202 ``` ```python Python theme={null} import requests result = requests.post( "https://agenticadvertising.org/api/registry/crawl-request", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={"domain": "examplepub.com"}, ).json() ``` ```json 202 Accepted theme={null} { "message": "Crawl request accepted", "domain": "examplepub.com" } ``` ```json 429 Too Many Requests theme={null} { "error": "Rate limit exceeded for this domain", "retry_after": 245 } ``` `retry_after` は再試行前に待つ秒数です。 #### Submit brand (legacy) `POST /api/brands/discovered/community` レビュー用にブランドを提出します。このエンドポイントは `/api/brands/save` より前からあります — 新しい統合には save エンドポイントを推奨します。 ### エラーレスポンス | Status | Description | | ------ | ------------------------------------------------------------- | | 400 | 必須フィールドの欠如または無効なドメイン | | 401 | API キーの欠如または無効 | | 409 | 権威あるブランド/プロパティを編集できない(`brand.json` または `adagents.json` 経由で管理) | | 410 | カーソル期限切れ(Change Feed — 90 日保持ウィンドウより古い) | | 429 | レート制限超過 | ## プロトコル対 REST API AdCP プロトコルは、エージェント間通信のための MCP と A2A のタスク(例: `get_products`、`create_media_buy`)を定義します。レジストリ REST API は別物です — AgenticAdvertising.org レジストリでエンティティをルックアップするための HTTP エンドポイントを提供します。 **REST API を使う**のはディスカバリーと認可のためです。 * プロトコル呼び出しを行う前にブランドまたはプロパティドメインを解決する * どのエージェントが存在し、何に認可されているかを発見する * アドサービング中にリアルタイムで認可を検証する * レジストリを参照または検索する統合を構築する **MCP/A2A タスクを使う**のはトランザクショナルな操作のためです。 * セールスエージェントからプロダクトを取得(`get_products`) * メディアバイの作成(`create_media_buy`) * クリエイティブの構築(`build_creative`) * シグナルの取得(`get_signals`) 典型的な統合は両方を使います: レジストリ API 経由でパブリッシャードメインを解決し、次に認可されたエージェントの MCP エンドポイントを呼び出してトランザクションします。 # エージェントの保守 Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/maintaining-your-agent オペレーターライフサイクルガイド — AAO Verified ハートビートの仕組み、ダッシュボードステータスの読み方、再プローブ方法、comply レポートの解釈。 エージェントが AAO レジストリに登録されると、あなたの責任は登録で終わりません。レジストリは AAO Verified コンプライアンスハートビート経由でエージェントのプロトコル適合性を継続的に監視し、リスティングの現在のヘルスをダッシュボードに反映します。このページは完全なオペレーターライフサイクルをカバーします: ハートビートが何をテストするか、ダッシュボードステータスインジケーターをどう読むか、手動で再プローブをどうトリガーするか、comply レポートをどう解釈するか。 ## 登録のおさらい AAO レジストリへの 1 つのパスがあります: **AAO メンバーがメンバープロフィールでエージェントを明示的に登録**。完全な登録フロー — ダッシュボードまたは `PUT /api/me/member-profile`、5 分未満でエンドツーエンド — については [エージェントの登録](/docs/registry/registering-an-agent) を参照してください。 登録後、あなたのエージェントは以下を得ます: * `visibility: "members_only"` の `/api/registry/agents` のカタログエントリー(有料 AAO 階層で `public` にアップグレード) * コンプライアンスハートビートで該当ストーリーボードを通過すると自動的に発行される **AAO Verified** バッジの適格性 次のクローラープローブがエージェントのタイプをその `get_adcp_capabilities` レスポンスから解決します — タイプフィールドを手動で設定または保守する必要はありません。 ## AAO Verified ハートビートの仕組み AAO は約 **1 時間のハートビート** 頻度でエージェントの適合性を継続的に再評価します。すべてのハートビートサイクルで、AAO のコンプライアンスランナーが登録された `agent_url` に対してストーリーボードスイートを実行します — `get_adcp_capabilities` のあなたの宣言が義務付ける同じセット(universal ベースライン + protocol ベースライン + 宣言された専門分野ストーリーボード)。 **ハートビートがテストするもの:** * AdCP ワイヤー形式とタスク形状 * エラーセマンティクスとエラーエンベロープ * メディアバイまたは該当ライフサイクル全体のステートマシン遷移 * 宣言された専門分野が動作するツールにマップ * スキーマ適合性とフィルター動作 * 冪等性セマンティクス ハートビートはレジストリクロールではありません。クロールはあなたの `adagents.json` またはケイパビリティスナップショットを再読しレジストリメタデータを更新します。ハートビートはライブエンドポイントに対してプロトコルストーリーボードを実行しあなたの検証ステータスを決定します。2 つは独立した操作です。 ### Verified (Spec) 対 Verified (Sandbox) 両修飾子は同じ約 1 時間のハートビートで同じストーリーボードを実行します。違いはランナーがどこをターゲットするかです: | Qualifier | Runner targets | What it attests | | ------------- | --------------------------------------------------------------- | ------------------------------- | | **(Spec)** | 登録する任意のエンドポイント — テストデプロイ、ローカル開発、サンドボックス専用スタック | AdCP ワイヤー形式とプロトコルセマンティクスが正しい | | **(Sandbox)** | すべてのリクエストに `account.sandbox: true` を付けた登録された **本番** `agent_url` | 本番コードパスが実世界の副作用ゼロでサンドボックスフラグを尊重 | 完全な適格性と証明詳細については [AAO Verified](/docs/building/verification/aao-verified) を参照してください。 ## ダッシュボードステータスインジケーター [agenticadvertising.org/dashboard/agents](https://agenticadvertising.org/dashboard/agents) のエージェントダッシュボードは、各登録エージェントの現在の状態を反映します。ステータスはコンプライアンスハートビートから来ます — AAO はすべてのプローブサイクルでそれを更新します。 | Status | What it means | Operator action | | ------------ | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | | **Active** | 現在のハートビートですべてのストーリーボードが通過。AAO Verified バッジがライブで自動更新中。 | アクション不要。デプロイ後の退行を監視。 | | **Degraded** | 少なくとも 1 つのストーリーボードが失敗し始めた。48 時間の猶予期間が実行される間、バッジは **レンダーし続ける**。 | 即座に調査 — comply レポートをチェックし、`@adcp/sdk/testing` でローカルに再現し、猶予期間が切れる前に退行を修正。 | | **Revoked** | 48 時間の連続ストーリーボード失敗が回復なしに経過。修飾子がバッジから落ちた。別途保持されていれば (Sandbox) は影響を受けない — 2 つの軸は独立。 | 基盤適合性問題を修正、次に次のハートビートサイクルを待ってバッジを自動的に復元。 | | **Recovery** | 以前の失敗後にストーリーボードが再び通過。バッジ修飾子が自動的に再発行される。 | 修正が安定していることを確認する以外アクション不要。 | **猶予期間の計算。** Degraded 状態は **最初の** 失敗したハートビートで始まります。48 時間クロックはその初期失敗から実行されます — ステータスに最初に気づいたときからではありません。プロトコル動作に触れる任意のデプロイの後、comply レポートを速やかにチェックしてください。 **メンバーシップの失効** は、ストーリーボード結果にかかわらず即座にバッジ全体を取り消します。AAO Verified は API アクセス階層でのアクティブな AAO メンバーシップに依存します。 ## 手動で再プローブをトリガーする方法 AAO は約 1 時間のハートビートで自動的にプローブを実行しますが、エージェント状態をリフレッシュする 3 つの方法があります: ### レジストリクロール(メタデータ更新) `adagents.json`、`brand.json`、またはケイパビリティスナップショットを更新し、次の予定クロールを待たずにレジストリに変更を拾わせたい場合、crawl-request エンドポイントを使います: ```bash cURL theme={null} curl -X POST "https://agenticadvertising.org/api/registry/crawl-request" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"domain":"yourcompany.com"}' ``` ```javascript JavaScript theme={null} const res = await fetch( "https://agenticadvertising.org/api/registry/crawl-request", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ domain: "yourcompany.com" }), } ); // Returns 202 Accepted — crawl runs asynchronously ``` これは非同期的に実行され `202 Accepted` を返します。ドメインごと 5 分に 1 リクエストとユーザーごと 1 時間に 30 リクエストにレート制限。 **これがすること、しないこと:** * あなたのエージェントのレジストリメタデータ(タイプ解決、ケイパビリティスナップショット、`adagents.json` 認可グラフ)をリフレッシュする * コンプライアンスハートビートの再実行を **トリガーしない** — ハートビートストーリーボードはこのエンドポイントで再実行されない ### ダッシュボード Refresh ボタン [agenticadvertising.org/dashboard/agents](https://agenticadvertising.org/dashboard/agents) のエージェントカードで、**Recheck status** ボタンが `/api/registry/agents/{encodedUrl}/refresh` を呼びます。それはあなたのエージェントのレジストリメタデータを再読し、あなたがエージェントを所有しケイパビリティプローブが成功するとき、ダッシュボードビューを更新する前に完全なコンプライアンスストーリーボードスイートを同期的に実行します。 ### コンプライアンス再キュー **Requeue comply** ボタンはストーリーボードスイートを即座に実行しません。`last_checked_at` をクリアするので、エージェントは次の予定ハートビートサイクル(約 1 時間かかりうる)に拾われます。 修正を即座に確認する必要がある場合、**Requeue comply** ではなく **Recheck status** またはダッシュボード **Test** フローを使います。ローカル再現には、`@adcp/sdk/testing` で同じストーリーボードを実行します。 ## comply レポートの読み方 comply レポートは AAO Verified セクションの下のエージェントのダッシュボードパネルに現れます。最新のハートビート実行の結果を表示します。 ### レポート構造 | Field | What it means | | ----------------------- | ------------------------------------------------------------------------------------------------ | | **Overall verdict** | `passed` — すべての該当ストーリーボードが通過。`failed` — 少なくとも 1 つのストーリーボードが失敗。`degraded` — 最初の失敗が検出された。猶予期間が実行中。 | | **Storyboard results** | ストーリーボードごとの内訳。各エントリーは `verdict` とオプションの `failure_reason` を持つ。 | | **Specialism coverage** | どの宣言された専門分野がテストされたか、各がどのストーリーボードを義務付けるか。 | | **Heartbeat timestamp** | このプローブが実行されたとき。 | ### ストーリーボードごとの判定 | Verdict | Meaning | | ---------------- | --------------------------------------------------------------------------------------------------------------- | | `passed` | ストーリーボードのアサーションがすべてあなたのエージェントのレスポンスに対して真と評価された。 | | `failed` | 少なくとも 1 つのアサーションが失敗。`failure_reason` フィールドがどのアサーションとランナーが何を受け取ったかを識別。 | | `skipped` | ストーリーボードがあなたの宣言された専門分野または現在のプロトコルバージョンに適用されない。 | | `not_applicable` | ストーリーボードがあなたのエージェントがサポートを宣言していない操作をテスト — `failed` ではなく `not_applicable` としてグレード。オプションツールとコントローラー専用ストーリーボードに一般的。 | ### 失敗のデバッグ 1. 失敗した行からストーリーボード名をメモ(例: `signed_requests`、`pagination_integrity`、`comply-controller-mode-gate`)。 2. [コンプライアンスカタログ](/docs/building/verification/compliance-catalog) でストーリーボード定義を見つける。 3. ローカルに再現: ```bash theme={null} npx @adcp/sdk@latest storyboard run signed_requests --agent-url https://your-agent-url.example/mcp ``` 4. ローカルランナーはハートビートと同じアサーションを与えます。失敗を修正し、ローカルで検証し、次にデプロイ — すべてのストーリーボードが通過すれば次のハートビートサイクルがバッジを再発行します。 ## 関連 * [エージェントの登録](/docs/registry/registering-an-agent) — 登録パス、フィールド、プログラマティック登録。 * [レジストリ API 概要](/docs/registry) — `POST /api/registry/crawl-request` と完全なエンドポイントカタログ。 * [AAO Verified](/docs/building/verification/aao-verified) — 完全なライフサイクル状態、軸セマンティクス、バッジ埋め込み。 * [コンプライアンスカタログ](/docs/building/verification/compliance-catalog) — ストーリーボードインデックス、専門分野ごとカバレッジ、ストーリーボードをローカルで実行する方法。 # エージェントの登録 Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/registering-an-agent エージェントが AAO レジストリにどう現れるか — 単一の登録パスと AAO メンバーシップがアンロックするもの。 AdCP レジストリカタログ(`/api/registry/agents`、`/api/registry/publishers`)は、AAO 証明のメンバー登録エージェントのみを含みます。カタログに現れるには、AAO メンバーがメンバープロフィールでエージェントを明示的に登録しなければなりません。 ## エージェントがレジストリに入る方法 1 つのパスがあります: **AAO メンバーがダッシュボードまたは `PUT /api/me/member-profile` 経由でエージェントをメンバープロフィールに追加**。ダッシュボード経由でエンドツーエンド: 5 分未満。 1. **サインインまたはサインアップ。** [agenticadvertising.org/auth/login](https://agenticadvertising.org/auth/login) に行きます。初めて? **Sign up** でアカウントを作成し、次に AAO 組織招待を受諾(またはあなたの組織がまだ AAO にない場合 [メンバーシップを開始](https://agenticadvertising.org/membership))。 2. **エージェントダッシュボードを開く。** サインインしたら、[agenticadvertising.org/dashboard/agents](https://agenticadvertising.org/dashboard/agents) に行きます。URL は組織コンテキストを自動解決します。 3. **ページ右上の `+ Register agent` をクリック。**(エージェントのないまったく新しい組織では、空状態 CTA は **Register your first agent** と読み、同じフローをトリガーします。) 4. **Addie と話す。** ボタンは、プロンプト *"Help me register my agent."* で事前ロードされた Addie とのチャットに入れます。Addie は以下を案内します: * **エージェント URL** — 例: `https://agent.yourcompany.com/mcp` * **表示名**(オプション) * **認証方法** — 1 つを選ぶ: None · Static bearer · Static basic · OAuth client credentials。*(インタラクティブ OAuth ユーザー認可は別途構成 — ここでは **None** で登録し、次にエージェントカードの **Authorize** をクリックしてサインイン。)* * **認証フィールド** — 選んだ方法が必要とするもののみ(bearer トークン、または client credentials の `token_endpoint` + `client_id` + `client_secret` など) * **プロトコル** — MCP にデフォルト。URL が曖昧な場合のみ Addie が尋ねる 5. **完了。** Addie が `save_agent` を呼び、あなたのエージェントは `visibility: "members_only"`(他の有料 AAO メンバー — Professional、Builder、Member、Leader に可視。公開リストされない)でレジストリカタログに着地します。 6. **オプション — 公開に。** `/dashboard/agents` に戻り、エージェントの可視性を **Members only** から **Public** に変更します。公開可視性は有料 AAO 階層(Professional、Builder、Member、Leader)と、エージェントが `brand.json` に追加できるようメンバープロフィールのプライマリブランドドメインを要求します。 **タイプはサーバー側で解決されます。** エージェントのタイプを尋ねられません — タイプ(`brand`、`sales`、`buying`、`measurement`、`creative`、`signals` など)はエージェントのケイパビリティスナップショットから解決されます。`resolveAgentTypes()` はクローラーから最新のスナップショットを読みます。スナップショットがまだ存在しない場合、保存されるタイプフィールドはクライアントが供給したもので、次のクローラープローブがそれを上書きします。どちらの方法でも、誤ったタイプを永久にピン留めできません。 **このパスが証明するもの:** メンバーが AAO 条件に署名した。URL、名前、連絡先が明示的に宣言された。タイプがプローブ検証された。可視性は `public`、`members_only`、`private` になりえます — 下の [Visibility](#visibility) を参照。 クロールされた `adagents.json` ファイルからの自動投入はありません。サードパーティ `adagents.json` ファイルにリストされたエージェントは、Operator ルックアップエンドポイント(`GET /api/registry/operator?domain=X`)、`/api/registry/lookup/domain`、`hasValidAdagents` が使うパブリッシャー認可グラフを投入しますが、カタログエントリーを作成しません。 ## プログラマティック登録(CI、スクリプト、エージェント用) CI、デプロイフック、または自身のエージェントから — ダッシュボードや Addie なしで — エージェントを登録するには、`/api/me/agents` 下のエージェントごとの REST エンドポイントを使います。それらはダッシュボードパスと同じ可視性ゲート、サーバー側タイプ解決、監査ログを共有するので、`members_only` デフォルト、`public` の `tier_required` チェック、タイプスマグル保護がすべて同一に適用されます。 WorkOS API キー(`Authorization: Bearer sk_…`)または OAuth ユーザー JWT で認証します。[agenticadvertising.org/dashboard/api-keys](https://agenticadvertising.org/dashboard/api-keys) で API キーを作成します。 | Endpoint | Purpose | | ----------------------------- | -------------- | | `GET /api/me/agents` | 登録したエージェントをリスト | | `POST /api/me/agents` | エージェントを登録 | | `PATCH /api/me/agents/{url}` | エージェントを更新 | | `DELETE /api/me/agents/{url}` | エージェントを削除 | PATCH と DELETE のパスパラメーターはエージェントの `url`、URL エンコード(例: `https%3A%2F%2Fagent.example.com%2Fmcp`)。`POST` は `url` で冪等: 新しいエントリーは `201` を返す。同じ `url` の再ポストは既存エントリーを更新し `200` を返す。各成功した書き込みは `{ agent, warnings? }` を返す — `warnings` は任意の階層駆動の可視性ダウングレードをリスト(例: `public` を求める Explorer 階層呼び出し元は `visibility_downgraded` 警告で `members_only` として保存)。 ### 前提条件 * あなたの組織の AAO メンバープロフィールが既に存在しなければならない。まずダッシュボードまたは `POST /api/me/member-profile` 経由で作成。それまでエージェントエンドポイントは `404` を返す。 * `visibility: "public"` には、組織は有料 AAO 階層(Professional、Builder、Member、Leader)と、エージェントが `brand.json` に追加できるようプロフィールに設定された `primary_brand_domain` を必要とする。下の [Visibility](#visibility) を参照。 ## AAO メンバーでない場合これが何を意味するか 今日セルフ登録できません。あなたのオペレーターは、レジストリカタログにあなたのエージェントを登録するため AAO メンバーでなければなりません。 クロール発見リスティング(誰かの `adagents.json` で参照されるあなたのエージェント)はプロパティ認可チェックに使われる認可グラフを投入しますがカタログエントリーを作成しません — `/api/registry/agents` はメンバー専用です。 登録パスにアクセスするには [メンバーになる](https://agenticadvertising.org/membership)。 ## Visibility メンバー登録エージェントは 3 つの可視性レベルの 1 つを持ちます: | Visibility | Who sees it | | -------------- | ------------------------------------------------------------------------- | | `public` | 誰でも — `/api/registry/agents` と `/api/registry/operator?domain=X` への匿名呼び出し | | `members_only` | `/api/registry/operator?domain=X` の AAO API 階層メンバー | | `private` | プロフィールオーナーのみ | `/api/registry/operator?domain=X` エンドポイントは認証対応: 匿名呼び出し元は `public` エージェントのみを見る。認証された AAO API 階層呼び出し元は `members_only` エージェントを見る。プロフィールオーナーは加えて `private` エージェントを見る。 ## メンバーシップの利益 | Capability | Description | | ------------------------------ | --------------------------------------- | | カタログ可視性 | あなたのエージェントが `/api/registry/agents` に現れる | | タイプ、名前、連絡先を自己証明 | エージェントのアイデンティティを宣言し編集 | | 自身のリスティングを編集 | プロフィールオーナーがすべてのフィールドを制御 | | `members_only` と `private` 可視性 | `public` を超えてエージェント可視性をスコープ | | AAO Verified バッジ | ストーリーボード通過後に適格 | | ストーリーボードテストアクセス | あなたのエージェントに対してプロトコル適合性テストを実行 | | コンプライアンスレポート | あなたのエージェントのプロトコル使用に対するレポート | | 消費者へのトラストシグナル | 「AAO メンバー。条件署名。証明済み」 | 登録パスにアクセスするには [メンバーになる](https://agenticadvertising.org/membership)。 ## あなたのエージェントがどう現れるか検証 レジストリを直接クエリ: ```bash cURL theme={null} curl "https://agenticadvertising.org/api/registry/agents" \ | jq '.agents[] | select(.url == "https://your-agent-url.example/mcp")' ``` ```javascript JavaScript theme={null} const res = await fetch("https://agenticadvertising.org/api/registry/agents"); const { agents } = await res.json(); const yours = agents.find((a) => a.url === "https://your-agent-url.example/mcp"); console.log(yours); ``` あなたのエージェントの URL がレスポンスにある場合、それは登録済みで `member` がリスティングを所有する AAO 組織を識別します。レスポンスにない場合、どの AAO メンバーもそれを登録していません — オペレーターにメンバープロフィール経由で登録するよう頼むか、[メンバーになって](https://agenticadvertising.org/membership) セルフ登録します。 あなたのエージェントがパブリッシャーの `adagents.json` で参照されているか(カタログ登録とは別の認可目的)をチェックするには、パブリッシャーのドメインに対して `/api/registry/lookup/domain/{domain}` を呼びます。 ## 関連 * [レジストリ概要](/docs/registry) — エンドポイントカタログ、ルックアップフロー、ブランド解決。 * `GET /api/registry/operator?domain=X` — エージェントと認可の認証対応エンティティごとビュー。 * `GET /api/registry/agents` — 完全なレジストリカタログ。 * `POST /api/registry/crawl-request` - 認可グラフのパブリッシャーの `adagents.json` マッピングをリフレッシュ。 # データプロバイダーガイド Source: https://adcp-docs-ja.pier1.co.jp/docs/signals/data-providers adagents.json を使って データプロバイダーとしてシグナルカタログを公開します。シグナル値タイプの定義、シグナルエージェントの認可、AI 主導のオーディエンス探索と AdCP 経由の検証の有効化。 # データプロバイダーガイド このガイドでは、データプロバイダーが `adagents.json` でシグナルカタログを公開する方法を説明します。AI エージェントがシグナルを探索し、認可を確認し、広告キャンペーン向けにシグナルを活性化できるようにします。 ## 問題 データプロバイダー(Pinnacle Data、Meridian Analytics、Apex Segments など)は価値のあるオーディエンスとコンテキストデータを持っていますが、急成長する AI 駆動の広告エージェントエコシステムとの統合にはチャレンジがあります: **探索が分散しています。** 各シグナルエージェント(Luminary Data、Nova DSP など)は、提供するシグナルを知るためにカスタム統合を必要とします。AI エージェントが「Pinnacle Data はどんな自動車購買意向シグナルを持っているか?」と問い合わせる標準的な方法がありません。 **認可が不透明です。** バイヤーがシグナルエージェントからシグナルを受け取った場合、エージェントが実際にそれを再販する認可を持っているか確認できません。仲介者を信頼するしかありません。 **シグナルのセマンティクスが一貫していません。** 標準化された定義がなければ、AI エージェントは「auto\_intenders」がバイナリセグメントなのか、傾向スコアなのか、多値カテゴリなのかを知ることができません。適切なターゲティング式を構築することが難しくなります。 **スケールには N×M の統合が必要です。** すべてのデータプロバイダーはすべてのシグナルエージェントとカスタム統合を必要とします。これはスケールしません。 ## 解決策 シグナルカタログはデータプロバイダーがよく知られた URL に機械可読なシグナルカタログを公開できるようにすることでこれらの問題を解決します: * **探索**: AI エージェントは自然言語(「自動車購買意向シグナルを探す」)または構造化ルックアップでシグナルを見つけられます * **認可確認**: バイヤーはデータプロバイダーのドメインを直接確認することで認可を確認できます * **型付きターゲティング**: シグナル定義は値タイプ(バイナリ、カテゴリ、数値)を含むのでエージェントが正しいターゲティング式を構築できます * **スケーラブルなパートナーシップ**: カタログで一度エージェントを認可します; シグナルを追加すると、認可されたエージェントは自動的にアクセスできます ## 概要 データプロバイダーはオーディエンスとコンテキストデータ(購買意向、人口統計、行動セグメント)を所有します。シグナルカタログ機能により以下を可能にする標準化されたフォーマットでシグナルを公開できます: * 自然言語クエリによる探索の有効化 * エージェントの認可確認の提供 * シグナルの特性(バイナリ、カテゴリ、数値)の説明 * 効率的な認可のためのタグベースのグループ化のサポート これはパブリッシャーがプロパティを宣言するのと同じパターンに従います — 「どんな広告プレースメントがあるか」ではなく、「どんなシグナルがあるか」を宣言します。 ## パラレルパターン | パブリッシャー | データプロバイダー | | -------------------------------------- | ---------------------------------- | | **プロパティ**(ウェブサイト、アプリ)を宣言する | **シグナル**(オーディエンス、セグメント)を宣言する | | エージェントが**インベントリを売る**ことを認可する | エージェントが**シグナルを再販する**ことを認可する | | `property_ids` / `property_tags` を使用する | `signal_ids` / `signal_tags` を使用する | | バイヤーは `publisher_domain` で確認する | バイヤーは `data_provider_domain` で確認する | どちらも公開メカニズムとして `/.well-known/adagents.json` を使用します。 ## ファイルの場所 データプロバイダーはシグナルカタログを以下でホストします: ``` https://your-domain.com/.well-known/adagents.json ``` [RFC 8615](https://datatracker.ietf.org/doc/html/rfc8615) の well-known URI 規則に従います。 ## 基本構造 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "contact": { "name": "Pinnacle Auto Data", "email": "partnerships@pinnacle-auto-data.com", "domain": "pinnacle-auto-data.com" }, "signals": [ { "id": "likely_ev_buyers", "name": "Likely EV Buyers", "description": "Consumers modeled as likely to purchase an electric vehicle in the next 12 months", "value_type": "binary", "tags": ["automotive", "green"] } ], "signal_tags": { "automotive": { "name": "Automotive Signals", "description": "Vehicle-related audience segments" }, "green": { "name": "Green/Sustainability", "description": "Environmentally-conscious consumer segments" } }, "authorized_agents": [ { "url": "https://signals-agent.example.com", "authorized_for": "All automotive signals", "authorization_type": "signal_tags", "signal_tags": ["automotive"] } ], "last_updated": "2025-01-15T10:00:00Z" } ``` ## シグナル定義 `signals` 配列の各シグナルはターゲット可能なセグメントを説明します: ### 必須フィールド | フィールド | 型 | 説明 | | ------------ | ------ | -------------------------------------------- | | `id` | string | カタログ内の一意識別子。パターン: `^[a-zA-Z0-9_-]+$` | | `name` | string | 人間が読めるシグナル名 | | `value_type` | enum | データタイプ: `binary`、`categorical`、または `numeric` | ### オプションフィールド | フィールド | 型 | 説明 | | ----------------------- | ------ | -------------------------------------------------------------------- | | `description` | string | このシグナルが何を表すかの詳細な説明 | | `tags` | array | グループ化のためのタグ(小文字、英数字: `^[a-z0-9_-]+$`) | | `allowed_values` | array | カテゴリシグナルの場合: 有効な値 | | `range` | object | 数値シグナルの場合: `{ min, max, unit }` | | `restricted_attributes` | array | このシグナルが触れる制限された属性カテゴリ(例: `["health_data"]`)。構造的ガバナンスマッチングを有効にします。 | | `policy_categories` | array | このシグナルが敏感なポリシーカテゴリ(例: `["children_directed"]`)。構造的ガバナンスマッチングを有効にします。 | ## シグナル値タイプ ### バイナリシグナル ユーザーがマッチするかしないか。最も一般的なタイプです。 ```json theme={null} { "id": "likely_ev_buyers", "name": "Likely EV Buyers", "value_type": "binary", "tags": ["automotive", "purchase_intent"] } ``` **ターゲティング**: このシグナルにマッチするユーザーを含めるか除外します。 ### カテゴリシグナル ユーザーがいくつかの可能な値のいずれかを持ちます。 ```json theme={null} { "id": "vehicle_ownership", "name": "Current Vehicle Ownership", "value_type": "categorical", "allowed_values": ["luxury_ev", "luxury_non_ev", "mid_range", "economy", "none"] } ``` **ターゲティング**: 特定の値を持つユーザーをターゲットにします(例: 「高級 EV または高級非 EV を所有するユーザー」)。 ### 数値シグナル ユーザーが範囲内のスコアまたは測定値を持ちます。 ```json theme={null} { "id": "purchase_propensity", "name": "Auto Purchase Propensity", "value_type": "numeric", "range": { "min": 0, "max": 1, "unit": "score" } } ``` **ターゲティング**: 値の範囲内のユーザーをターゲットにします(例: 「傾向スコア > 0.7」)。 ## 認可パターン ### パターン1: シグナル ID(直接参照) ID で特定のシグナルを認可します: ```json theme={null} { "authorized_agents": [ { "url": "https://premium-agent.example.com", "authorized_for": "Premium automotive signals only", "authorization_type": "signal_ids", "signal_ids": ["likely_ev_buyers", "luxury_auto_intenders"] } ] } ``` **最適な用途**: 特定の限られたシグナルセット。きめ細かい制御。 ### パターン2: シグナルタグ(効率的なグループ化) 特定のタグを持つすべてのシグナルを認可します: ```json theme={null} { "authorized_agents": [ { "url": "https://full-catalog-agent.example.com", "authorized_for": "All automotive signals", "authorization_type": "signal_tags", "signal_tags": ["automotive"] } ] } ``` **最適な用途**: 大型カタログ。タグ付きシグナルを追加すると、エージェントは自動的にアクセスできます。 ## シグナルタグ `signal_tags` オブジェクトはシグナルで使用されるタグのメタデータを提供します: ```json theme={null} { "signal_tags": { "automotive": { "name": "Automotive Signals", "description": "Vehicle ownership, purchase intent, and service signals" }, "premium": { "name": "Premium Signals", "description": "High-value segments with enhanced pricing" } } } ``` **タグを定義する理由**: * カタログを探索するバイヤーへの人間が読めるコンテキスト * 効率的な認可の有効化(「すべてのプレミアムシグナル」) * 簡単な探索のための関連シグナルのグループ化 ## バイヤーによるカタログの使用方法 ### 1. 探索 バイヤーはシグナルエージェントで `get_signals` を呼び出します。エージェントはカタログを以下に使用することがあります: * 自然言語マッチング(「自動車購買意向シグナルを探す」) * `signal_id` による構造化ルックアップ ### 2. 認可確認 バイヤーがシグナルを受け取ったら、認可を確認できます: ```json theme={null} { "signal_id": { "data_provider_domain": "pinnacle-auto-data.com", "id": "likely_ev_buyers" } } ``` バイヤーは `https://pinnacle-auto-data.com/.well-known/adagents.json` を取得して確認します: 1. シグナルは `signals` 配列に存在するか? 2. シグナルエージェントは `authorized_agents` にあるか? 3. 認可はこのシグナルをカバーしているか(ID またはタグで)? ### 3. ターゲティング `value_type` に基づいて、バイヤーはターゲティング式を構築します: ```json theme={null} // Binary targeting { "signal_id": { "source": "catalog", "data_provider_domain": "pinnacle-auto-data.com", "id": "likely_ev_buyers" }, "value_type": "binary", "value": true } // Categorical targeting { "signal_id": { "source": "catalog", "data_provider_domain": "pinnacle-auto-data.com", "id": "vehicle_ownership" }, "value_type": "categorical", "values": ["luxury_ev", "luxury_non_ev"] } // Numeric targeting { "signal_id": { "source": "catalog", "data_provider_domain": "pinnacle-auto-data.com", "id": "purchase_propensity" }, "value_type": "numeric", "min_value": 0.7 } ``` ## エージェントネイティブシグナル すべてのシグナルがデータプロバイダーカタログから来るわけではありません。シグナルエージェントは**エージェントネイティブシグナル** — 独自に作成したカスタムシグナル(プロプライエタリモデル、ファーストパーティデータなど)— も提供することがあります。 ### シグナル ID 構造 シグナル ID は `source` を識別子として使用します: | ソース | フィールド | 検証 | | --------- | ----------------------------- | ------------------------------ | | `catalog` | `data_provider_domain` + `id` | データプロバイダーの adagents.json で検証可能 | | `agent` | `agent_url` + `id` | 信頼ベース — バイヤーがエージェントを信頼する | ### 例: エージェントネイティブシグナル ```json theme={null} { "signal_id": { "source": "agent", "agent_url": "https://luminary-data.com/.well-known/adcp/signals", "id": "custom_auto_intenders" }, "value_type": "binary", "value": true } ``` ### いつどちらを使うか **`source: "catalog"` を使う場合**: * シグナルが外部データプロバイダー(Pinnacle Data、Meridian Analytics など)から来ます * 認可確認が重要 * 正規のシグナル定義を参照したい **`source: "agent"` を使う場合**: * シグナルがシグナルエージェント独自のもの * 照合する外部データプロバイダーがない * エージェントがカスタムモデルやファーストパーティセグメントを作成しています ## 完全な例 自動車データプロバイダーの完全なシグナルカタログ: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "contact": { "name": "Pinnacle Auto Data", "email": "partnerships@pinnacle-auto-data.com", "domain": "pinnacle-auto-data.com" }, "signals": [ { "id": "likely_ev_buyers", "name": "Likely EV Buyers", "description": "Consumers modeled as likely to purchase an electric vehicle in the next 12 months based on vehicle registration, financial, and behavioral data", "value_type": "binary", "tags": ["automotive", "premium"] }, { "id": "vehicle_ownership", "name": "Current Vehicle Ownership", "description": "Current vehicle category owned by the consumer", "value_type": "categorical", "allowed_values": ["luxury_ev", "luxury_non_ev", "mid_range", "economy", "none"], "tags": ["automotive"] }, { "id": "purchase_propensity", "name": "Auto Purchase Propensity", "description": "Likelihood score of purchasing any new vehicle in the next 6 months", "value_type": "numeric", "range": { "min": 0, "max": 1, "unit": "score" }, "tags": ["automotive"] } ], "signal_tags": { "automotive": { "name": "Automotive Signals", "description": "Vehicle-related audience segments" }, "premium": { "name": "Premium Signals", "description": "High-value premium audience segments with enhanced pricing" } }, "authorized_agents": [ { "url": "https://luminary-data.com/.well-known/adcp/signals", "authorized_for": "All Pinnacle automotive signals via Luminary Data", "authorization_type": "signal_tags", "signal_tags": ["automotive"] }, { "url": "https://nova-dsp.com/.well-known/adcp/signals", "authorized_for": "Pinnacle premium signals only", "authorization_type": "signal_ids", "signal_ids": ["likely_ev_buyers"] } ], "last_updated": "2025-01-15T10:00:00Z" } ``` ## 位置情報データプロバイダーの例 ジオ/モビリティプロバイダーのシグナルカタログは同じ構造を使用しますが、位置情報固有のシグナルがあります。フットトラフィックとモビリティデータを公開するプロバイダーの `signals` 配列の例: ```json theme={null} { "signals": [ { "id": "store_visitors", "name": "Store Visitors", "description": "Consumers who visited a specified retail location in the past 30 days based on opted-in mobile device data", "value_type": "binary", "tags": ["geo", "foot_traffic"] }, { "id": "visit_frequency", "name": "Location Visit Frequency", "description": "Monthly visit count to a specified location category", "value_type": "numeric", "range": { "min": 0, "max": 30, "unit": "visits_per_month" }, "tags": ["geo", "frequency"] }, { "id": "commute_pattern", "name": "Commute Pattern", "description": "Categorized daily commute behavior based on observed travel patterns", "value_type": "categorical", "allowed_values": ["urban_transit", "suburban_driver", "remote_worker", "hybrid"], "tags": ["geo", "behavioral"] } ] } ``` 3つの値タイプが異なるジオコンセプトにマップされることに注目してください: 店舗訪問の有無には `binary`、意味のある範囲を持つ訪問頻度には `numeric`、分類されたモビリティ行動には `categorical`。 ## アイデンティティ/デモグラフィックプロバイダーの例 アイデンティティ会社のシグナルカタログは金融記録、調査、公開データから得られる消費者セグメントを公開します。注意: これらは生データではなく**ターゲティングセグメント**です。クレジット由来のシグナルは規制上の義務(FCRA)を伴う場合があります — 公開前にコンプライアンスチームに相談してください。 ```json theme={null} { "signals": [ { "id": "household_income", "name": "Household Income Tier", "description": "Modeled household income bracket based on financial and demographic indicators", "value_type": "categorical", "allowed_values": ["under_50k", "50k_75k", "75k_100k", "100k_150k", "150k_250k", "over_250k"], "tags": ["demographic", "income"] }, { "id": "life_stage", "name": "Life Stage", "description": "Life stage classification derived from demographic and behavioral indicators", "value_type": "categorical", "allowed_values": ["young_adult", "early_career", "established_family", "empty_nester", "retired"], "tags": ["demographic", "life_stage"] }, { "id": "credit_active", "name": "Active Credit Seeker", "description": "Consumer has actively applied for new credit products in the past 90 days", "value_type": "binary", "tags": ["financial", "in_market", "credit"] } ] } ``` アイデンティティ会社はクロスデバイスのアイデンティティグラフも提供することが多いですが、サービスとしてのアイデンティティ解決(デバイス A を人物 B にマッチング)はまだ AdCP プロトコルの一部ではありません。この境界の詳細は[シグナルエコシステムガイド](/docs/signals/ecosystem#identity-companies)を参照してください。 ## リテールメディアプロバイダーの例 リテーラーはファーストパーティの購買データを持ち、それが高価値なターゲティングシグナルになります。リテールメディアネットワークは同じ `adagents.json` 内でプロパティと並べてシグナルを公開できます: ```json theme={null} { "signals": [ { "id": "category_buyer", "name": "Category Buyer", "description": "Purchased in the specified product category within the past 90 days", "value_type": "categorical", "allowed_values": ["electronics", "home", "beauty", "grocery", "fashion"], "tags": ["retail", "purchase"] }, { "id": "purchase_frequency", "name": "Monthly Purchase Frequency", "description": "Number of purchases in a product category over the trailing 90 days", "value_type": "numeric", "range": { "min": 0, "max": 50, "unit": "purchases" }, "tags": ["retail", "frequency"] }, { "id": "new_to_brand", "name": "New to Brand", "description": "Consumer has no prior purchase history with the specified brand in the trailing 12 months", "value_type": "binary", "tags": ["retail", "conquest"] } ] } ``` リテールシグナルはモデル化された行動ではなく実際の購買に基づく決定論的なデータのため特に価値が高いです。デュアルロールパターン(パブリッシャー + データプロバイダー)については[シグナルエコシステムガイド](/docs/signals/ecosystem#retail-media-networks)を参照してください。 ## バリデーション [AdAgents.json Builder](https://agenticadvertising.org/adagents/builder) を使ってシグナルカタログを検証するか、プログラム的に検証します: ```bash theme={null} curl -X POST https://adcontextprotocol.org/api/adagents/validate \ -H "Content-Type: application/json" \ -d '{"domain": "your-domain.com"}' | jq '.data.validation' ``` バリデーターは以下をチェックします: * 必須フィールド(各シグナルの `id`、`name`、`value_type`) * ID パターン(アンダースコア/ハイフン付き英数字) * タグの一貫性(シグナルで使用されるタグは `signal_tags` で定義されるべきです) * 認可参照(`signal_ids`/`signal_tags` は既存のシグナル/タグを参照すべきです) ## ベストプラクティス ### 1. 説明的な ID を使用します ```json theme={null} // Good { "id": "likely_ev_buyers" } { "id": "household_income_150k_plus" } // Avoid { "id": "seg_12345" } { "id": "a1b2c3" } ``` ### 2. 完全なメタデータを提供します `description` を含めてバイヤーが各シグナルが何を表すか理解できるようにします。 ### 3. スケーラビリティのためにタグを使用します カタログが成長するにつれて、タグは各シグナル ID をリストアップせずに効率的な認可を可能にします。 ### 4. 値タイプを明確に文書化します カテゴリシグナルには常に `allowed_values` を含めます。数値シグナルには `unit` を含む `range` を含めます。 ### 5. ファイルを最新の状態に保つ シグナルが変更されたときは `last_updated` タイムスタンプを更新します。バイヤーはこれらのファイルをキャッシュします — 古いデータは認可の失敗を引き起こします。 ## ガバナンスメタデータの宣言 シグナル定義は2つのオプションフィールドをサポートします。`restricted_attributes` と `policy_categories` は構造的ガバナンスマッチングを有効にします。宣言されると、ガバナンスエージェントはシグナル名からの意味的推論に頼る代わりに、キャンペーンプランの制限に対して決定論的にシグナルをマッチできます。 ### restricted\_attributes シグナルが触れる GDPR 第9条の個人データの特別カテゴリを宣言します。値: `racial_ethnic_origin`、`political_opinions`、`religious_beliefs`、`trade_union_membership`、`health_data`、`sex_life_sexual_orientation`、`genetic_data`、`biometric_data`。 ```json theme={null} { "id": "chronic_condition_hh", "name": "Chronic Condition Households", "description": "Households with modeled indicators of chronic health conditions", "value_type": "binary", "tags": ["health", "demographic"], "restricted_attributes": ["health_data"] } ``` キャンペーンプランが `restricted_attributes: ["health_data"]` を宣言すると、ガバナンスエージェントは説明を解釈することなくこのシグナルをブロックします。 ### policy\_categories シグナルが敏感なポリシーカテゴリを宣言します。ポリシーカテゴリは関連する規制レジームをグループ化します — `children_directed` は COPPA、英国 AADC、GDPR 第8条をカバーします。値はレジストリ定義のカテゴリ ID です。 ```json theme={null} { "id": "kids_cartoon_fans", "name": "Kids Cartoon Fans", "description": "Children aged 6-12 who watch animated content", "value_type": "binary", "tags": ["entertainment", "children"], "policy_categories": ["children_directed"] } ``` ### 両フィールドの組み合わせ シグナルが制限された個人データに触れ、特定の規制レジームに関連する場合、両方を宣言できます: ```json theme={null} { "id": "fertility_intent", "name": "Fertility Intent", "description": "Consumers researching fertility treatments", "value_type": "binary", "tags": ["health", "life_stage"], "restricted_attributes": ["health_data"], "policy_categories": ["pharmaceutical_advertising"] } ``` ガバナンスメタデータがない場合、ガバナンスエージェントはシグナル名から感度を推論しなければなりません — これは脆弱で偽陽性を生みます。宣言された属性により決定論的なマッチングが可能になります。 ## get\_adcp\_capabilities との統合 シグナルエージェントは `get_adcp_capabilities` で利用可能なデータプロバイダーをアドバタイズします: ```json theme={null} { "signals": { "data_provider_domains": ["pinnacle-auto-data.com", "meridian-analytics.com", "apex-segments.com"] } } ``` これにより、エージェントがアクセスできるデータプロバイダーのカタログをバイヤーに伝えます。 ## 次のステップ 1. **adagents.json を作成する**: シグナルカタログと共に 2. **ドメインの `/.well-known/adagents.json` でホストする** 3. **AdAgents.json Builder を使ってバリデートする** 4. **データを再販するシグナルエージェントとパートナーシップを締結する** 5. **パートナーシップが確立されたら `authorized_agents` にエージェントを追加する** ## 関連ドキュメント * [シグナルプロトコル概要](/docs/signals/overview) — AdCP でのシグナルの仕組み * [get\_signals タスク](/docs/signals/tasks/get_signals) — シグナル探索 API * [activate\_signal タスク](/docs/signals/tasks/activate_signal) — シグナル活性化 API * [adagents.json 技術仕様](/docs/governance/property/adagents) — 完全な adagents.json リファレンス(プロパティ中心) # シグナルエコシステム Source: https://adcp-docs-ja.pier1.co.jp/docs/signals/ecosystem AdCP シグナルエコシステム: データプロバイダー、小売業者、パブリッシャー、CDP、アイデンティティ企業がシグナルプロトコルを通じてオーディエンスシグナルを公開・活性化する方法。 # シグナルエコシステム [シグナルプロトコル](/docs/signals/overview)は多くの種類の企業を接続します。このガイドでは各企業の位置付け、構築するもの、次のステップを示します。 ## シグナルの流れ ``` データプロバイダー ──┐ 小売業者 ───────────┤ パブリッシャー ──────┤──→ シグナルカタログ ──→ シグナルエージェント ──→ バイヤーエージェント ──→ キャンペーンターゲティング CDP ────────────────┤ (adagents.json) (get_signals) (activate) アイデンティティ企業 ──┘ ``` ターゲット可能なデータを持つすべての企業は `/.well-known/adagents.json` を通じて**シグナルカタログ**を公開できます。シグナルエージェントはこれらのカタログを発見し、`get_signals` と `activate_signal` を通じてバイヤーに提供します。 ## 自分の役割を見つける オーディエンスや行動データを所有し、広告ターゲティングに提供したい。 購買データを持ち、インベントリとデータの両方を販売しています。 広告インベントリとともにコンテキストデータとファーストパーティの購読者データを持ちます。 アイデンティティ解決、デモグラフィック、または金融データを提供します。 フットトラフィック、ジオフェンシング、またはモビリティデータを持ちます。[ジオシグナルの仕組みを見る →](#location-and-mobility-providers) ブランドのファーストパーティデータを管理し、その代わりにオーディエンスを活性化します。 クライアントのメディアを購入し、独自のデータ資産を持ちます。 データを保存し、プライバシーを保護したコラボレーションを実現します。 *** ## データプロバイダー **例**: 自動車データ企業、金融データプロバイダー、行動データ企業 **役割**: オーディエンスセグメント、傾向モデル、行動データを所有します。シグナルエージェントがデータを発見して再販できるように、シグナルカタログを公開します。 **構築するもの**: 1. シグナル、その値タイプ、どのエージェントが再販を認可されているかを記述した `/.well-known/adagents.json` のシグナルカタログ 2. それ以外は何もありません — シグナルエージェントが発見と活性化を代わりに処理します **公開するシグナルタイプ**: ```json theme={null} { "signals": [ { "id": "likely_ev_buyers", "name": "Likely EV Buyers", "value_type": "binary", "description": "Consumers modeled as likely to purchase an EV in the next 12 months", "tags": ["automotive", "purchase_intent"] }, { "id": "vehicle_ownership", "name": "Vehicle Ownership Category", "value_type": "categorical", "allowed_values": ["luxury_ev", "luxury_ice", "midrange", "economy", "truck_suv"], "tags": ["automotive", "ownership"] }, { "id": "purchase_propensity", "name": "Auto Purchase Propensity", "value_type": "numeric", "range": { "min": 0, "max": 1, "unit": "score" }, "tags": ["automotive", "purchase_intent"] } ] } ``` **次のステップ**: * [データプロバイダーガイド](/docs/signals/data-providers) — シグナルカタログの完全なウォークスルー * [シグナル仕様](/docs/signals/specification) — プロトコルの詳細 * [S3: シグナルスペシャリストモジュール](/docs/learning/specialist/signals) — サンドボックスシグナルエージェントを使ったハンズオンラボ *** ## 位置情報・モビリティプロバイダー **例**: フットトラフィック分析企業、モビリティデータプラットフォーム、ジオフェンスドオーディエンスプロバイダー **役割**: オプトインしたモバイルデバイスデータから得られた地理的・行動的シグナルを公開します — フットトラフィックパターン、商圏、滞在時間、通勤行動。これらのシグナルにより、バイヤーは人々が物理的な世界でどこに行くかに基づいてオーディエンスをターゲティングできます。 **構築するもの**: 1. ジオシグナルとその値タイプを記述した `/.well-known/adagents.json` のシグナルカタログ 2. それ以外は何もありません — シグナルエージェントが発見と活性化を代わりに処理します **シグナルカタログの例**: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "contact": { "name": "Meridian Geo", "email": "partnerships@meridiangeo.example", "domain": "meridiangeo.example" }, "signals": [ { "id": "competitor_visitors", "name": "Competitor Store Visitors", "value_type": "binary", "description": "Consumers who visited a competitor retail location in the past 30 days based on verified foot traffic data", "tags": ["geo", "retail", "conquest"] }, { "id": "trade_area_residents", "name": "Trade Area Residents", "value_type": "binary", "description": "Consumers whose primary residence is within a specified trade area defined by drive time or radius", "tags": ["geo", "proximity"] }, { "id": "visit_frequency", "name": "Location Visit Frequency", "value_type": "numeric", "range": { "min": 0, "max": 30 }, "description": "Monthly visit count to a specified location category (QSR, grocery, gym, auto dealer)", "tags": ["geo", "frequency"] }, { "id": "dwell_time", "name": "Average Dwell Time", "value_type": "numeric", "range": { "min": 0, "max": 120, "unit": "minutes" }, "description": "Average minutes spent on-site, distinguishing drive-bys from intentional visits", "tags": ["geo", "behavioral", "dwell"] }, { "id": "daypart_visitation", "name": "Day-Part Visitation", "value_type": "categorical", "allowed_values": ["morning_commute", "midday", "evening_commute", "weekend_daytime", "weekend_evening"], "description": "When consumers typically visit a specified venue type", "tags": ["geo", "temporal"] } ], "signal_tags": { "geo": { "name": "Geographic Signals", "description": "Location-derived audience segments from opted-in mobile data" } }, "authorized_agents": [ { "url": "https://signals-agent.example.com", "authorized_for": "All geo signals", "authorization_type": "signal_tags", "signal_tags": ["geo"] } ] } ``` **ジオフェンスドオーディエンスの活性化**: ```json theme={null} { "tool": "activate_signal", "arguments": { "signal_agent_segment_id": "meridian_trade_area_residents", "pricing_option_id": "po_meridian_trade_cpm", "destinations": [ { "type": "platform", "platform": "nova-dsp", "account": "agency-seat-789" } ] } } ``` AdCP は位置情報由来の**オーディエンスセグメント** — ある場所を訪問した人々、商圏内に住む人々、または通勤パターンを示す人々のグループ — をサポートします。リアルタイムジオフェンシングトリガー(誰かがゾーンに入ったときにメッセージを送信します)はまだプロトコルに含まれていません。計画中の拡張については[ロードマップ](/docs/reference/roadmap)を参照してください。 **次のステップ**: * [データプロバイダーガイド](/docs/signals/data-providers) — シグナルカタログの完全なウォークスルー * [シグナル仕様](/docs/signals/specification) — プロトコルの詳細 * [S3: シグナルスペシャリストモジュール](/docs/learning/specialist/signals) — サンドボックスシグナルエージェントを使ったハンズオンラボ *** ## リテールメディアネットワーク **例**: マーケットプレイス広告プラットフォーム、食料品配達広告ネットワーク **二重の役割**: **パブリッシャー**(マーケットプレイス上の広告インベントリを販売)と**データプロバイダー**(購買データは他のプラットフォームでのターゲティングに価値があります)の両方。 ### パブリッシャーとして スポンサードプロダクトとディスプレイインベントリを販売します。これは[メディアバイプロトコル](/docs/media-buy/index)を使用します — `adagents.json` でプロパティを宣言し、セールスエージェントを構築し、`get_products` と `create_media_buy` を処理します。 ### データプロバイダーとして ファーストパーティの購買データ(カテゴリバイヤー、ロイヤルティ層、バスケット価値、ブランド新規顧客)は自社のインベントリを超えて価値があります。これらをプロパティと同じ `adagents.json` ファイルにシグナルとして公開できます。 **複合 adagents.json**(インベントリ用プロパティ + データ用シグナル): ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/adagents.json", "contact": { "name": "Acme Marketplace", "email": "partnerships@acme-marketplace.example", "domain": "acme-marketplace.example" }, "properties": [ { "property_id": "marketplace_web", "property_type": "website", "name": "Acme Marketplace", "identifiers": [ {"type": "domain", "value": "acme-marketplace.example"} ], "supported_channels": ["retail_media", "display"] } ], "signals": [ { "id": "category_buyer", "name": "Category Buyer", "value_type": "categorical", "allowed_values": ["electronics", "home", "beauty", "grocery", "fashion"], "description": "Purchased in category in past 90 days", "tags": ["retail", "purchase"] }, { "id": "loyalty_tier", "name": "Loyalty Program Tier", "value_type": "categorical", "allowed_values": ["platinum", "gold", "silver", "bronze"], "tags": ["retail", "loyalty"] }, { "id": "new_to_brand", "name": "New to Brand", "value_type": "binary", "description": "Never purchased from specified brand on marketplace", "tags": ["retail", "conquest"] } ], "authorized_agents": [ { "url": "https://signals-agent.example.com", "authorized_for": "All retail signals", "authorization_type": "signal_tags", "signal_tags": ["retail"] } ] } ``` **重要な洞察**: クローズドループの購買データは決定論的なため、エコシステムで最も価値のあるシグナルであることが多いです。行動モデルがインテントを予測する一方で、実際のトランザクション証明を持っています。 **クローズドループ測定**: 購買データはアトリビューションも可能にします。キャンペーンがショッパーシグナルをターゲットにし、消費者がマーケットプレイスで広告商品を購入した場合、そのコンバージョンを直接測定できます。[`sync_event_sources`](/docs/media-buy/task-reference/sync_event_sources) を使用して購買イベントフィードをバイヤーエージェントに登録し、[`log_event`](/docs/media-buy/task-reference/log_event) でコンバージョンを報告します。これにより、サードパーティのアトリビューションに頼ることなく、ターゲティングから測定までのループが閉じます — リテールメディアにとって大きな利点です。 **次のステップ**: * [コマースメディアガイド](/docs/media-buy/commerce-media) — リテールのコンセプトを AdCP にマッピング * [データプロバイダーガイド](/docs/signals/data-providers) — シグナルカタログの公開方法 * [コンバージョントラッキング](/docs/media-buy/conversion-tracking/index) — `sync_event_sources` と `log_event` によるクローズドループ測定 * [セラー統合ガイド](/docs/building/operating/seller-integration) — セールスエージェントの構築 *** ## パブリッシャー **例**: ニュースパブリッシャー、ストリーミングプラットフォーム、コンテンツネットワーク **役割**: コンテキストシグナル(コンテンツカテゴリ、記事のセンチメント)とファーストパーティの購読者データ(エンゲージメントレベル、購読期間)を持ちます。これらは広告インベントリを補完します。 **すでに持っているもの**: `adagents.json` のプロパティとメディアバイプロトコル用のセールスエージェント。 **追加できるもの**: 同じ `adagents.json` のシグナル定義。コンテキストインテリジェンスと購読者データをエコシステム全体でターゲット可能なシグナルに変えます。 **パブリッシャーのシグナルタイプ**: | シグナル | 値タイプ | 説明 | | --------- | ---- | ---------------------- | | コンテンツカテゴリ | カテゴリ | ページコンテンツの IAB タクソノミー分類 | | 記事センチメント | カテゴリ | ポジティブ、ニュートラル、ネガティブ、混合 | | エンゲージド読者 | バイナリ | 高注目購読者(週5記事以上) | | 購読期間 | 数値 | アクティブな購読の月数 | **なぜ重要か**: サードパーティクッキーの減少に伴い、コンテキストシグナルの価値は高まっています。あるパブリッシャーから CTV インベントリを購入する広告主が、別のパブリッシャーのコンテキストインテリジェンスを使ってターゲティングできます — そのパブリッシャーがシグナルとして公開していれば。 **次のステップ**: * [データプロバイダーガイド](/docs/signals/data-providers) — 既存の adagents.json にシグナルを追加 * [パブリッシャー/セラートラック](/docs/learning/tracks/publisher) — セールスエージェントの構築(まだの場合) * [S3: シグナルスペシャリストモジュール](/docs/learning/specialist/signals) — バイヤーの視点を理解します *** ## アイデンティティ企業 **例**: クロスデバイスアイデンティティプロバイダー、デモグラフィックデータ企業、信用由来データ企業 **役割**: アイデンティティ解決(デバイスと人、世帯のリンク)と金融記録、公的データ、調査から得られた消費者データを提供します。 **現在 AdCP で適合するもの**: 世帯所得層、ライフステージ、クレジット活動、クロスデバイスリーチなどの**消費者セグメント**はシグナル値タイプに直接マッピングできます: ```json theme={null} { "signals": [ { "id": "household_income", "name": "Household Income Tier", "value_type": "categorical", "allowed_values": ["under_50k", "50k_75k", "75k_100k", "100k_150k", "150k_250k", "over_250k"], "tags": ["demographic", "income"] }, { "id": "life_stage", "name": "Life Stage", "value_type": "categorical", "allowed_values": ["young_adult", "early_career", "established_family", "empty_nester", "retired"], "tags": ["demographic", "life_stage"] }, { "id": "household_composition", "name": "Household Composition", "value_type": "categorical", "allowed_values": ["single", "couple_no_children", "family_young_children", "family_teens", "multigenerational"], "tags": ["demographic", "household"] }, { "id": "cross_device_reach", "name": "Cross-Device Household Reach", "value_type": "numeric", "range": { "min": 1, "max": 12 }, "description": "Identified devices linked to household via deterministic identity graph", "tags": ["identity", "cross_device"] } ] } ``` **まだ適合しないもの**: コアのアイデンティティ解決サービス — デバイス A と人物 B のマッチング — は他のシグナルを強化するインフラであり、シグナル自体ではありません。AdCP にはまだサービスとしてのアイデンティティ解決のプロトコルがありません。これは[ロードマップ](/docs/reference/roadmap)に含まれています。 **現在付加価値を生み出せる場所**: 1. **デモグラフィックおよび金融セグメント**をカタログのシグナルとして公開します 2. **他のプロバイダーのシグナルをクロスデバイスリーチで強化します**(アイデンティティグラフにより、バイナリシグナルがより多くのデバイスにアドレス可能になります) 3. **シグナルエージェントとパートナーを組みます** — 他のプロバイダーの行動データとアイデンティティデータを組み合わせることができます **信用由来シグナルのデータガバナンス**: 信用調査機関データから得られたシグナルは、FCRA および類似のフレームワークに基づく規制上の義務を持つ可能性があります。AdCP はこれらをターゲティングセグメント(所得層、クレジット活動)として公開しますが、生の金融データとしてではありません — ただし、コンプライアンスチームはどのセグメントが広告用途に許可されるかを確認すべきです。`activate_signal` の非活性化メカニズムは、同意が撤回されたり規制要件が変更されたりした場合のコンプライアンスワークフローをサポートします。 **次のステップ**: * [データプロバイダーガイド](/docs/signals/data-providers) — シグナルカタログの公開 * [プラットフォーム/インターメディアリートラック](/docs/learning/tracks/platform) — データをプラットフォームに接続するインフラを構築する場合 *** ## カスタマーデータプラットフォーム **例**: カスタマーデータプラットフォーム、オーディエンス管理プラットフォーム、マーケティングデータプラットフォーム **役割**: ブランドはファーストパーティデータをプラットフォームに保存します。オーディエンスセグメントの構築と広告ターゲティングのための活性化を支援します。ブランドがデータを所有し、CDP はインフラです。 **所有権の問題**: 標準的なデータプロバイダーモデルでは、プロバイダーが自社ドメインの下でシグナルを公開します。CDP の場合、**ブランド**がデータを所有しますが**CDP**がインフラを運用します。2つのアプローチがあります: ### アプローチ 1: CDP をシグナルエージェントとして使います CDP がブランド固有のカスタムセグメントを提供するシグナルエージェントを運用します。各ブランドのセグメントはそのアカウントにスコープされます: ```json theme={null} { "tool": "get_signals", "arguments": { "signal_spec": "High lifetime value customers for retargeting", "account": { "brand": { "domain": "acme-brand.example" } } } } ``` シグナルエージェントはそのブランドのアカウントに認可されたセグメントのみを返します。これは CDP の既存の動作に自然にマッピングされます — ブランドスコープのオーディエンス管理です。 ### アプローチ 2: ブランドがカタログを公開し、CDP がエージェントをホスト ブランドが自分の `adagents.json`(`acme-brand.example/.well-known/adagents.json`)にカスタムシグナルを列挙し、CDP のシグナルエージェントを認可エージェントとして設定します。これにより、ブランドが何を公開するかについての透明性とコントロールが得られます。 **CDP のシグナルタイプ**: | シグナル | 値タイプ | ユースケース | | ----------- | --------- | ------------------ | | 高 LTV 顧客 | バイナリ | リテンションとアップセルキャンペーン | | カート放棄者 | バイナリ | リアルタイムリターゲティング | | エンゲージメントスコア | 数値(0〜100) | 高インテントの見込み客を優先する | | チャーンリスク | カテゴリ | ウィンバックキャンペーン | **重要な懸念事項 — プライバシー**: ファーストパーティデータの活性化は同意を尊重する必要があります。CDP は同意されたセグメントのみが公開されることに責任を持ちます。`activate_signal` タスクは同意が撤回されたりキャンペーンが終了したりした場合のコンプライアンスのために `deactivate` アクションをサポートします(プラットフォームからセグメントを削除します)。 **次のステップ**: * [データプロバイダーガイド](/docs/signals/data-providers) — カタログ構造 * [プラットフォーム/インターメディアリートラック](/docs/learning/tracks/platform) — MCP ベースのインフラの構築 * [シグナル仕様](/docs/signals/specification) — プロトコル適合要件 *** ## データ資産を持つエージェンシー **例**: データ部門を持つエージェンシーホールディングカンパニー、独自のオーディエンスプラットフォームを持つ独立系エージェンシー **二重の役割**: クライアントキャンペーンのシグナルを**消費**(バイヤー側)し、データ資産から独自のシグナルを**提供**(プロバイダー側)します。 ### シグナルコンシューマーとして バイイングエージェントは `get_signals` を使用してクライアントキャンペーンのターゲティングデータを探します。複数のブランドにまたがって作業し、それぞれが独自のアカウントを持ちます: ```json theme={null} { "tool": "get_signals", "arguments": { "signal_spec": "In-market luxury auto intenders", "account": { "brand": { "domain": "client-brand.example" } }, "destinations": [ { "type": "platform", "platform": "the-trade-desk", "account": "agency-ttd-seat" } ] } } ``` ### シグナルプロバイダーとして データ部門の独自シグナル(アイデンティティグラフ、購買パネル、カスタムモデル)は `adagents.json` を通じて公開でき、自社または第三者のシグナルエージェントを通じて利用可能にできます。 **マルチブランド管理**: 各クライアントブランドは独自のアカウントコンテキストを持ちます。あるブランドのために活性化されたシグナルは他のブランドには見えません。DSP プラットフォームのエージェンシーシートは共有されることがありますが、シグナル活性化はブランドスコープです。 **次のステップ**: * [バイヤー/ブランドトラック](/docs/learning/tracks/buyer) — AdCP によるメディアバイイング * [データプロバイダーガイド](/docs/signals/data-providers) — 独自シグナルの公開 * [S3: シグナルスペシャリストモジュール](/docs/learning/specialist/signals) — ハンズオンのシグナル発見と活性化 *** ## データウェアハウスとクリーンルーム **例**: 広告データ製品を持つクラウドデータプラットフォーム、データコラボレーションプラットフォーム **役割**: データ(ブランド、プロバイダー、小売業者)と活性化(DSP、セールスエージェント)の間に位置します。問題はシグナルがプラットフォームとどのように流れるかです。 ### プラットフォームへのシグナル活性化 バイヤーまたはエージェンシーが、どちらの生データも移動させずに、シグナルプロバイダーのオーディエンスをウェアハウスにあるデータと照合したい場合があります。AdCP の用語では、プラットフォームは `activate_signal` の**デスティネーション**です: ```json theme={null} { "tool": "activate_signal", "arguments": { "signal_agent_segment_id": "trident_likely_ev_buyers", "pricing_option_id": "po_trident_ev_cpm", "destinations": [ { "type": "platform", "platform": "apex-data-cloud", "account": "brand-clean-room-456" } ] } } ``` シグナルエージェントはセグメントメンバーシップをクリーンルームにプッシュします。ブランドのファーストパーティデータはその場に留まります。オーバーラップ分析、ルックアライクモデリング、または測定はあなたの環境内で行われます。 ### プラットフォームからのシグナル活性化 ウェアハウスに存在するデータ — 小売業者の購買データ、ブランドの CRM セグメント、プロバイダーの行動モデル — はシグナルとして公開できます。シグナルエージェントはプラットフォームの API をクエリして `get_signals` リクエストに応答します。基礎となるデータはウェアハウスを離れません。DSP にはターゲティングキー(セグメント ID、活性化キー)のみが流れます。 ウェアハウスに購買データが保存されている小売業者は以下を行います: 1. シグナルを記述したシグナルカタログを `adagents.json` で公開します 2. プラットフォームへの API アクセスを持つシグナルエージェントとパートナーを組みます 3. シグナルエージェントが `get_signals`(可用性の確認)と `activate_signal`(DSP へのターゲティングキーのプッシュ)を処理します ### 構築するもの デスティネーションプラットフォームとして参加するには、受信側を実装します: シグナルエージェントからのセグメント活性化を受け入れ、環境内のデータと照合し、活性化キーを返します。これは DSP が今日オーディエンスセグメントを受け入れる方法に類似しています。 **次のステップ**: * [プラットフォーム/インターメディアリートラック](/docs/learning/tracks/platform) — MCP サーバーアーキテクチャ * [シグナル仕様](/docs/signals/specification) — デスティネーションとデプロイメントモデル * [業界ランドスケープ](/docs/building/concepts/industry-landscape) — AdCP が他の標準とどう共存するか * [ワーキンググループ](/docs/community/working-group) — クリーンルーム統合パターンの形成を支援します *** ## ハンズオンで試す `https://agenticadvertising.org/api/training-agent/mcp` のトレーニングエージェントには、自動車データ、ジオ/モビリティ、リテール購買データ、アイデンティティ/デモグラフィック、パブリッシャーコンテキストシグナル、CDP オーディエンスをカバーするサンドボックスシグナルプロバイダーが含まれています。 `get_signals` でシグナルを発見し、`activate_signal` で活性化します — すべてサンドボックスモードで、実際のデータやコストはかかりません。 Addie に伝える: "I'd like to start the signals specialist module" — または自分の役割を説明してシグナルエコシステムへの参加方法を尋ねる。 # キーコンセプト Source: https://adcp-docs-ja.pier1.co.jp/docs/signals/key-concepts AdCP シグナルタイプ(バイナリ、カテゴリ、数値)、シグナルソース(カタログ vs エージェント)、get_signals による探索、activate_signal による活性化、adagents.json による認可。 # キーコンセプト シグナルプロトコルにより、AI エージェントは広告キャンペーン向けのデータシグナルを探索、活性化、管理できます。シグナルはターゲット可能なオーディエンス、コンテキストカテゴリ、地理的地域、その他のデータ属性を表します。 ## シグナルとは何か シグナルは広告キャンペーンでのターゲティングや測定に使用されるデータセグメントです: * **オーディエンスシグナル**: 人口統計、興味関心、行動に基づくユーザーセグメント * **コンテキストシグナル**: コンテンツカテゴリやページコンテキスト * **地理的シグナル**: 位置情報ベースのターゲティングデータ * **時間的シグナル**: 時間ベースのターゲティングパターン * **多次元シグナル**: 複合またはカスタムシグナルタイプ ## シグナル値タイプ すべてのシグナルには `value_type` があり、バイヤーがターゲティング式を構築する方法を決定します: ### バイナリ ユーザーがマッチするかしないか。最も一般的なタイプです。 ```json theme={null} { "id": "likely_ev_buyers", "name": "Likely EV Buyers", "value_type": "binary", "tags": ["automotive", "purchase_intent"] } ``` **ターゲティング**: このシグナルにマッチするユーザーを含めるか除外します。 ### カテゴリ ユーザーがいくつかの可能な値のいずれかを持ちます。 ```json theme={null} { "id": "vehicle_ownership", "name": "Current Vehicle Ownership", "value_type": "categorical", "allowed_values": ["luxury_ev", "luxury_non_ev", "mid_range", "economy", "none"] } ``` **ターゲティング**: 特定の値を持つユーザーをターゲットにします(例: 「高級 EV または高級非 EV を所有するユーザー」)。 ### 数値 ユーザーが範囲内のスコアまたは測定値を持ちます。 ```json theme={null} { "id": "purchase_propensity", "name": "Auto Purchase Propensity", "value_type": "numeric", "range": { "min": 0, "max": 1, "unit": "score" } } ``` **ターゲティング**: 値の範囲内のユーザーをターゲットにします(例: 「傾向スコア > 0.7」)。 ## シグナルソース シグナル ID は `source` を識別子として使用します: | ソース | フィールド | 検証 | | --------- | ----------------------------- | ------------------------------ | | `catalog` | `data_provider_domain` + `id` | データプロバイダーの adagents.json で検証可能 | | `agent` | `agent_url` + `id` | 信頼ベース — バイヤーがエージェントを信頼する | **カタログシグナル**は `/.well-known/adagents.json` でオファリングを公開する外部データプロバイダーから来ます。バイヤーはシグナルエージェントが再販を認可されているかどうかを独立して確認できます。 **エージェントネイティブシグナル**はシグナルエージェント独自のものです — カスタムモデル、ファーストパーティデータ、またはエージェントが複数のソースから構築する複合セグメントです。 ## 2つのタスク | タスク | 目的 | | -------------------------------------------------------- | ------------------------- | | [`get_signals`](/docs/signals/tasks/get_signals) | キャンペーン基準にマッチするシグナルを探索します | | [`activate_signal`](/docs/signals/tasks/activate_signal) | キャンペーンで使用するためにシグナルを活性化します | ### get\_signals による探索 バイヤーは必要なものを自然言語で説明します。シグナルエージェントはすべてのデータプロバイダーのカタログと独自のプロプライエタリシグナルにわたって検索します: ```json theme={null} { "tool": "get_signals", "arguments": { "signal_spec": "In-market auto buyers with high purchase propensity" } } ``` レスポンスには価格、サイズ推定値、値タイプメタデータを含むマッチするシグナルが含まれます — バイヤーエージェントがターゲティング決定を行うために必要なすべての情報です。 ### activate\_signal による活性化 バイヤーがシグナルを選択したら、DSP またはデータプラットフォームで活性化します: ```json theme={null} { "tool": "activate_signal", "arguments": { "signal_agent_segment_id": "trident_likely_ev_buyers", "pricing_option_id": "po_trident_ev_cpm", "destinations": [ { "type": "platform", "platform": "the-trade-desk", "account": "agency-seat-123" } ] } } ``` シグナルエージェントはセグメントメンバーシップを指定されたプラットフォームにプッシュします。バイヤーのキャンペーンはその後、プラットフォームの標準ツールを使ってそれに対してターゲットを設定できます。 ## エージェント統合 シグナルプロトコルは、より広い [AdCP エコシステム](/docs/intro#the-adcp-ecosystem-layers)内で動作します。シグナルエージェントは意思決定プラットフォーム(DSP、オーケストレーションプラットフォーム)と直接統合し、中間のレポートと使用追跡を排除します。シグナルエージェントは [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) で利用可能なデータプロバイダーをアドバタイズします。 シグナルがプラットフォームで活性化されると、すべての使用レポート、請求、キャンペーンメトリクスはそのプラットフォームによって直接処理されます。 ## 認可と信頼 データプロバイダーは `adagents.json` の `authorized_agents` 配列でシグナルを再販できる人を制御します。2つのパターン: * **シグナル ID**: ID で特定のシグナルを認可する — きめ細かい制御 * **シグナルタグ**: 特定のタグを持つすべてのシグナルを認可する — カタログが成長するにつれてスケールします バイヤーはデータプロバイダーの `adagents.json` を取得してシグナルエージェントが `authorized_agents` に表示されているかどうかを確認することで認可を検証できます。 ## さらに詳しく * [データプロバイダーガイド](/docs/signals/data-providers) — シグナルカタログを公開する方法 * [シグナルエコシステム](/docs/signals/ecosystem) — 各タイプの企業がどのように参加するか * [プロトコル仕様](/docs/signals/specification) — 正式な適合要件 * [get\_signals タスクリファレンス](/docs/signals/tasks/get_signals) — 探索 API の詳細 * [activate\_signal タスクリファレンス](/docs/signals/tasks/activate_signal) — 活性化 API の詳細 # Signals プロトコル Source: https://adcp-docs-ja.pier1.co.jp/docs/signals/overview メディアバイヤーがキャンペーンブリーフからシグナル有効化まで進む流れを、自動車・ジオ・リテールデータを横断して追う — AdCP シグナルワークフローのビジュアルウォークスルー。 サムがホワイトボードの前でターゲティング計画を描いています。周囲にオーディエンス・ロケーション・購買行動を示すホログラフィックなデータアイコンが浮かんでいる サムは Pinnacle Agency のシニアメディアバイヤーです。クライアントの Nova Motors は初の電気自動車「Volta EV」を発売しようとしています。ブリーフの内容は、購買意向の高いインマーケット層に、ディーラーの近くでリーチし、Nova 車を買ったことのない消費者に絞ることです。 AdCP がなければ、サムは 3 社のデータプロバイダーにメールでセグメントの提供可否を照会し、IO の承認を待ち、2 つの別々の DSP プラットフォームに CSV セグメントファイルをアップロードすることになります — この工程は数日かかり、プロバイダーがタクソノミーを更新するたびに壊れます。AdCP を使えば、エージェンシーのプラットフォームが数分で処理します。 このウォークスルーでは、サムがブリーフからライブターゲティングまで進む流れを追います。 ## ステップ 1: 必要なものを言葉で伝える サムはセグメントタクソノミーではなく、達成したいことから始める。 サムがノートパソコンで検索を入力しています。3 本のデータストリームがロケーション・リテール・オーディエンスのデータプロバイダーアイコンへと広がっている — 1 回のクエリで 3 つのソースに届く サムのエージェンシープラットフォームはブリーフを `get_signals` 呼び出しに変換します。どのプロバイダーが何を持っているか知る必要はありません — シグナルエージェントがすべてを横断して検索します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/signals/get-signals-request.json", "signal_spec": "In-market EV buyers with high purchase propensity, near auto dealerships" } ``` シグナルエージェントは認可済みのすべてのデータプロバイダー — 自動車、ジオ/モビリティ、リテール、ID — の公開された定義を検索し、マッチするものを返します。 | サムが使う言葉 | プロトコルでの呼び方 | | ---------------------- | --------------------------------------------------------------------------------------------- | | ターゲットオーディエンス | シグナル(`get_signals` から取得) | | セグメントタクソノミー | シグナル定義(`adagents.json` `signals[]`) | | データプロバイダー | シグナルソース(`data_provider_domain`) | | DSP で有効化する | デスティネーション付きの `activate_signal` | | オーディエンスまたはインベントリの利用可能性 | 権威的なシグナルレベルのカバレッジには `coverage_forecast`。オプションの非推奨 `coverage_percentage` はレガシースカラーフォールバックとしてのみ | | データコスト | シグナルレスポンスの `pricing_options` | ## ステップ 2: 返ってきたものを確認します サムが壁のスクリーンでシグナル結果を腕を組んで確認しています。ロケーション・リテール・オーディエンスのデータを示す色違いのアクセントが付いた 3 グループのデータカードが表示されている シグナルエージェントは複数のプロバイダーからのマッチ結果を返します。各シグナルには値の型、価格、カバレッジ推定値が付いています: ```mermaid theme={null} sequenceDiagram participant Platform as Sam's Agency Platform participant Agent as Signal Agent participant Auto as Trident Auto Data participant Geo as Meridian Geo participant Retail as ShopGrid Platform->>Agent: get_signals ("in-market EV buyers near dealerships") par Search catalogs Agent->>Auto: Search catalog Auto-->>Agent: likely_ev_buyers (binary), purchase_propensity (numeric) Agent->>Geo: Search catalog Geo-->>Agent: competitor_visitors (binary), trade_area_residents (binary) Agent->>Retail: Search catalog Retail-->>Agent: category_buyer (categorical), new_to_brand (binary) end Agent-->>Platform: 8 matching signals with pricing ``` サムは個別に探したり交渉したりすることなく、3 社のプロバイダーからのシグナルを確認できます: * **Trident Auto Data** — `purchase_propensity`(numeric、0〜1 スコア)。CPM: \$1.50。`likely_ev_buyers`(binary)。CPM: \$2.50。 * **Meridian Geo** — `competitor_visitors`(binary、競合ディーラーを訪問した人)。CPM: \$2.00。 * **ShopGrid** — `new_to_brand`(binary)。CPM: \$3.50。`category_buyer`(categorical: electronics、automotive、home)。CPM: \$3.00。 サムは `purchase_propensity` が numeric であることに気づく — エージェントがしきい値(score > 0.7)を設定して、曖昧な include/exclude ではなく高意図見込み客に予算を集中させられます。`category_buyer` が categorical であることも確認し、ShopGrid の全オーディエンスに費用をかけずに「automotive」だけをターゲティングできます。`competitor_visitors` シグナルも目に留まる — これは Meridian Geo のシグナルで、同社はオープンプロトコルを通じてロケーションと行動データをアクセスしやすくするために Kai Lindström が設立したデータ企業です。サムは 3 つのシグナルを選ぶ: インテントスコアリング用の `purchase_propensity`、競合ディーラー近くでのコンクエストターゲティング用の Meridian Geo の `competitor_visitors`、そして Nova 車を購入したことのない世帯にリーチするための `new_to_brand`。 ## ステップ 3: 検証して選択します サードパーティデータを有効化する前に、Pinnacle Agency は検証を必須としています。サムのプラットフォームはデータプロバイダーの公開された定義を直接取得します: ``` https://shopgrid.example/.well-known/adagents.json ``` そして以下を確認します: 1. `new_to_brand` シグナルが ShopGrid のカタログに存在すること 2. シグナルエージェントが `authorized_agents` にリストされていること 3. 認可がリテールシグナルをカバーしていること(`signal_tags: ["retail"]` 経由) 認可チェックが失敗した場合 — たとえば ShopGrid がエージェントのアクセスを取り消した場合 — サムは 1 ドルも使う前にシグナルにフラグが付いた状態で表示されます。この独立した検証により、バイヤーはデータの出所についてシグナルエージェントの言葉を鵜呑みにする必要がない。 ## ステップ 4: プラットフォームで有効化します 2 分割のシーン — サムが下でタブレットからシグナルセグメントを有効化しています。データストリームが上のプラットフォームへと流れていく。上では Kai がノートパソコンで有効化メトリクスを監視している サムは 3 つのシグナルを 2 つの DSP で有効化する — プログラマティックディスプレイ(広いリーチ、低い CPM)用の Nova DSP と、プレミアム CTV インベントリ(ブランドスポット用の世帯レベルターゲティング)用の StreamHaus: ```mermaid theme={null} sequenceDiagram participant Platform as Agency Platform participant Agent as Signal Agent participant DSP1 as Nova DSP (display) participant DSP2 as StreamHaus (CTV) par Activate signals Platform->>Agent: activate_signal (purchase_propensity → Nova DSP) Agent->>DSP1: Push segment DSP1-->>Agent: deployed Platform->>Agent: activate_signal (competitor_visitors → Nova DSP) Agent->>DSP1: Push segment DSP1-->>Agent: deployed Platform->>Agent: activate_signal (new_to_brand → StreamHaus) Agent->>DSP2: Push segment DSP2-->>Agent: deployed end Agent-->>Platform: 3 signals active, deployment IDs returned ``` 各有効化呼び出しにはデスティネーションプラットフォームとアカウントを指定します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/signals/activate-signal-request.json", "idempotency_key": "c3d4e5f6-a7b8-4901-c234-901234567890", "signal_agent_segment_id": "shopgrid_new_to_brand", "pricing_option_id": "po_shopgrid_retail_cpm", "destinations": [ { "type": "platform", "platform": "streamhaus", "account": "agency-ctv-seat-456" } ] } ``` シグナルエージェントは各プラットフォームにセグメントメンバーシップをプッシュします。サムはメディアバイを構築する際に参照できるデプロイメント ID を受け取ります。 ### セールスエージェント経由での購入 サムは、独自のセールスエージェントを持つプレミアムパブリッシャー Wonderstruck を通じてスポンサード記事キャンペーンも実行したいと考えています。特定の DSP でシグナルを有効化する代わりに、サムはセールスエージェントで直接シグナルを有効化します — Wonderstruck が独自の DSP 連携を処理します: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/signals/activate-signal-request.json", "idempotency_key": "d4e5f6a7-b8c9-4012-d345-012345678901", "signal_agent_segment_id": "shopgrid_new_to_brand", "pricing_option_id": "po_shopgrid_retail_cpm", "destinations": [ { "type": "agent", "agent_url": "https://wonderstruck.salesagents.example" } ] } ``` セールスエージェントは有効化を内部で記録します。サムが後で Wonderstruck を通じて `create_media_buy` を呼ぶと、シグナルベースのターゲティングはすでに設定済みです — サムは Wonderstruck が裏でどの DSP を使うかを知る必要がありません。 ## ステップ 5: キャンペーンを構築します サムが大画面で 3 つのターゲティングレイヤーが積み重なる様子を見ている — ロケーション・オーディエンス・購買データが重なり合い、交差する部分が光っている これでシグナルが両プラットフォームでライブ状態になりました。サムのエージェントは [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) を使ってメディアバイを構築します。有効化されたシグナルは各 DSP 上でターゲティングセグメントとしてすでに利用可能になっている — メディアバイは [`get_products`](/docs/media-buy/task-reference/get_products) で見つかったプロダクトを参照し、DSP がシグナルベースのターゲティングを自動的に適用します。 * **ディスプレイ(Nova DSP)**: サムは `purchase_propensity > 0.7` AND `competitor_visitors = true` をターゲティングするディスプレイプロダクトを選択します。競合ディーラーを訪問したことがある高意図の自動車バイヤーにリーチします。 * **CTV(StreamHaus)**: サムは `new_to_brand = true` をターゲティングする CTV プロダクトを選択します。Nova 車を購入したことのない世帯にブランド認知スポットを届ける。 重要なポイント: シグナルとメディアバイは別々の関心事です。Signals プロトコルはデータをプラットフォームに乗せる。[Media Buy プロトコル](/docs/media-buy/index)はそのプラットフォームでキャンペーンを動かす。両者は組み合わせて使える — サムはシグナルプロバイダーと DSP の間にカスタム統合を構築する必要がなかった。 ## ステップ 6: 管理と計測 キャンペーンが動き始めた。2 週間後、ディスプレイの CPA が目標を 40% 上回る一方、CTV は順調にペーシングしています。サムは予算を再配分する: Nova DSP でジオシグナルを無効化してディスプレイのデータコストを削減する: ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/signals/activate-signal-request.json", "idempotency_key": "e5f6a7b8-c9d0-4123-e456-123456789012", "signal_agent_segment_id": "meridian_competitor_visitors", "action": "deactivate", "destinations": [ { "type": "platform", "platform": "nova-dsp", "account": "agency-display-seat-123" } ] } ``` 無効化によりセグメントがプラットフォームから削除されます。課金も止まる。CTV のシグナルはアクティブのまま — 各有効化は独立しています。 サムと Kai がシグナルマーケットプレイスを示す大きなディスプレイの前に並んで立っている — データプロバイダーがハブを通じてバイヤーへと流れ込み、エコシステム全体でコラボレーションしている すべてのステップで標準の AdCP タスクを使っています。サムはどのデータプロバイダーが存在するかを把握し、個別に契約を交渉し、DSP ごとにカスタム統合を構築する必要がなかった。Kai Lindström の Meridian Geo のようなデータプロバイダーはシグナル定義を一度公開すれば、シグナルエージェントがすべてのディスカバリーを処理します。各プラットフォームはセグメントが届けば標準のターゲティングツールでターゲティングを処理します。 ## さらに深く学ぶ * **主要コンセプト**: [シグナルの種類・ソース・認可](/docs/signals/key-concepts) — このウォークスルーの背景にある構成要素 * **エコシステム**: [誰がどのように参加するか](/docs/signals/ecosystem) — データプロバイダー、リテーラー、パブリッシャー、CDP、エージェンシー、ID 企業 * **データを公開する**: [データプロバイダーガイド](/docs/signals/data-providers) — 公開されたシグナル定義の作り方 * **プロトコル仕様**: [シグナル仕様](/docs/signals/specification) — 正式な要件と適合性 * **認定を取得する**: [シグナルスペシャリストモジュール](/docs/learning/specialist/signals) では、サンドボックスのシグナルエージェントを使ったインタラクティブなラボを通じてシグナルのディスカバリーと有効化を学ぶ # 仕様 Source: https://adcp-docs-ja.pier1.co.jp/docs/signals/specification AdCP シグナルプロトコルの正式仕様。トランスポート要件、get_signals・activate_signal タスクスキーマ、適合基準、エラーハンドリング、アクティベーションキーのセキュリティ、RFC 2119 要件。 **Status**: Request for Comments **Last Updated**: January 25, 2026 本ドキュメントは Signals Protocol の仕様を定義します。ここで使用する "MUST"、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、"SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"MAY"、"OPTIONAL" といったキーワードは、[RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) に従って解釈すること。 ## Abstract Signals Protocol は、AI を活用したシグナルの探索・有効化・管理システムの標準インターフェースを定義します。このプロトコルにより、AI アシスタントは自然言語での対話を通じて、マーケターがデータシグナル(オーディエンス、コンテキスト、地理、時間、多次元データ)を発見・有効化・管理できるよう支援します。 ## Protocol Overview Signals Protocol では次のことが可能です。 * マーケティング目的に基づく自然言語でのシグナル探索 * 1 回のリクエストで複数プラットフォームのシグナルを探索 * 特定のプラットフォームやアカウントへのシグナル有効化 * CPM やレベニューシェアモデルによる透明な価格提示 * 個人・デバイス・世帯など単位別のシグナル規模報告 ## Transport Requirements シグナルエージェントは以下のいずれか少なくとも 1 つのトランスポートをサポートしなければなりません。 | Transport | Protocol | Description | | --------- | ---------------------- | ----------------------------------- | | MCP | Model Context Protocol | Tool-based interaction via JSON-RPC | | A2A | Agent-to-Agent | Message-based interaction | シグナルエージェントは優先トランスポートとして MCP をサポートすべきです。 シグナルエージェントは `get_adcp_capabilities` で Signals Protocol のサポートを宣言しなければなりません。 ```json theme={null} { "$schema": "https://adcontextprotocol.org/schemas/v3/protocol/get-adcp-capabilities-response.json", "status": "completed", "adcp": { "major_versions": [2], "idempotency": { "supported": true, "replay_ttl_seconds": 86400 } }, "supported_protocols": ["signals"] } ``` ## Core Concepts ### Request Roles すべてのシグナルリクエストには 2 つの役割が存在します。 * **Orchestrator**: API リクエストを送るプラットフォーム(例: バイヤーエージェントや AI アシスタント) * **Account**: リクエストが代行される商業関係。[Accounts Protocol](/docs/accounts/overview) を参照。 ### Signal Agent Types **プライベートシグナルエージェント** — 単一のアカウントが専有し、専用アクセスを持つもの: * 権限のないアカウントに対しては `REFERENCE_NOT_FOUND` を返さなければなりません — 「エージェントが存在しない」と同じレスポンス。「存在するが未認可」を「存在しない」と区別すると、プライベートエージェントのクロステナント列挙が可能になります。両方のパスで一致しなければならない観測可能なチャネルの完全なセットは [error-handling.mdx](/docs/building/by-layer/L3/error-handling) の統一レスポンス MUST を参照。 * アカウントをまたいでプライベートシグナルを公開してはなりません **マーケットプレイスシグナルエージェント** — 複数のアカウントにシグナルデータをライセンス提供するもの: * アカウント登録なしでの公開ホールセールフィードアクセスをサポートしなければなりません * 登録済みアカウント向けのアカウントパーソナライズされたホールセールフィードビューをサポートすべきです ### Identifiers * **`signal_agent_segment_id`**: シグナルソースが発行する不透明なシグナルハンドル。Signals Protocol レスポンスは各シグナルに対してこれを返さなければならず、オーケストレーターは `activate_signal` リクエストでこれを使用しなければなりません。セラー提供のメディアバイシグナルターゲティングでは、バイヤーは名前付きシグナル参照として `signal_ref` を使い、選択したプロダクトオプションが別個の実行ハンドルを公開する場合のみ `signal_agent_segment_id` を含めます。 * **`activation_key`**: 外部デプロイメントターゲティングに使うキー。`is_live: true` かつ呼び出し元がデプロイメントへのアクセス権を持つ場合、シグナルエージェントはこれを返さなければなりません。オーケストレーターはプラットフォームまたはデスティネーションのターゲティングに `activation_key` を使用しなければなりません(`signal_agent_segment_id` ではなく)。セールスエージェントのメディアバイで選択されたセラー提供シグナルには、`packages[].targeting_overlay.signal_targeting_groups` を使い、セラーが事前のアクティベーションキーを要求する場合のみ `activation_key` を含めます。 ### Governance metadata シグナル定義には `restricted_attributes` および `policy_categories` フィールドを含めてもよい。これらは構造的なガバナンスマッチングを可能にします。データプロバイダーはこれらを宣言すべきであり、それによりガバナンスエージェントはシグナル名から感度を推測するのではなく、コンプライアンスを確定的に評価できます。 * **`restricted_attributes`**: このシグナルが関係する GDPR 第 9 条の特別カテゴリー値の配列。ガバナンスエージェントはセマンティック推論より宣言された属性を優先すべきです。 * **`policy_categories`**: このシグナルが対象となるポリシーカテゴリー ID の配列。ガバナンスエージェントはこれをプランの `policy_categories` と照合して機密データの使用にフラグを立てます。 実装の詳細は [ガバナンスメタデータの宣言](/docs/signals/data-providers#declaring-governance-metadata) を参照してください。 ### エンリッチメントとプログレッシブディスクロージャー `taxonomy.values[]`、`taxonomy.value_mappings[]`、`segmentation_criteria`、`onboarder`、`modeling.disclosure.jurisdictions[]` のようなリッチな定義フィールドは、権威的なシグナル定義を記述します。それらをすべてのディスカバリーリスティングで繰り返す必要はありません。 シグナルエージェントは `get_signals` をディスカバリーと利用可能性の面として扱うべきです(SHOULD)。広範な検索結果とホールセールフィードページでは、エージェントは安定したアイデンティティ、表示メタデータ、価格、デプロイメントステータス、利用可能な場合はキャッシュバリデーターまたはディスクロージャーポインターを持つコンパクトなリスティングを返すべきです(SHOULD)。プロバイダーが公開する公開シグナルでは、`signal_ref` が定義ポインターです: オーケストレーターはプロバイダーの `/.well-known/adagents.json` をフェッチし(存在する場合 `authoritative_location` に従う)、一致する `signals[].id` を選択します。大きなタクソノミー、長いセグメンテーション基準、管轄区域固有のディスクロージャーテキストは、その権威的なシグナル定義または `taxonomy.ref`、`criteria_url`、`modeling.disclosure.jurisdictions[].disclosure_url` のような宣言された URL からフェッチすべきです(SHOULD)。 プロバイダーファイルが既存の `adagents.json` の `catalog_etag` を含むか、HTTP レスポンスが `ETag` または `Last-Modified` を含む場合、オーケストレーターは解決された権威的 URL とそのバリデーターでフェッチした `adagents.json` ドキュメントをキャッシュし、キャッシュされたドキュメント内で `signal_ref.signal_id` を解決すべきです(SHOULD)。クライアントはシグナル定義キャッシュをバリデーターのみでキーしてはなりません(MUST NOT)。独自のフレッシュネスバリデーターを持つタクソノミードキュメントには `taxonomy.etag` を使います。エージェントはレビュー、ランキング、参照ルックアップのフロー向けにインライン抜粋を含めてもよい(MAY)が、大きな `get_signals` レスポンスのすべてのアイテムで完全な外部リソースを複製することは避けるべきです(SHOULD)。 インラインでよりリッチなメタデータを必要とするオーケストレーターは、`get_products.fields` と同じレスポンス射影パターンを使い、`get_signals` に `fields` を設定して、`taxonomy`、`data_sources`、`methodology`、`modeling`、`countries`、`consent_basis`、`data_subject_rights` のような特定のリスティングまたは定義フィールドを要求してもよい(MAY)。エージェントは、正確なルックアップ、リファインメント、小さなカスタムシグナル結果セット、公開 `adagents.json` 定義を持たないプライベート/ソースネイティブなシグナルについて、要求されたフィールドを尊重すべきです(SHOULD)。`fields` は射影リクエストであり資格付与ではありません。エージェントは、呼び出し元が基盤の系譜、方法論、権利ルーティングメタデータに認可されていない限り、要求された定義フィールドを秘匿してもよい(MAY)。セラーまたはフェデレーティングエージェントが別のプロバイダーの `consent_basis` や `art9_basis` を `get_signals` 行に射影するとき、その値はプロバイダー宣言のシグナル定義の姿勢のままです。セラーはそれを自身の処理根拠に書き換えてはなりません(MUST NOT)。広範なディスカバリーとホールセールページでは、エージェントは大きな定義リソースをインライン化する代わりにコンパクトなポインターを返してもよい(MAY)。 ### 定義エンリッチメントフィールド シグナル定義は、透明性、ガバナンス、レビューのための追加フィールドを運んでもよい(MAY): * **タクソノミーメタデータ**: `taxonomy.ref`、`taxonomy.values[]`、`taxonomy.value_mappings[]`、`taxonomy.parent_match_behavior` は、シグナルが外部またはプロバイダー所有のタクソノミーにどうマップするかを記述します。これらのフィールドはパッケージのターゲティング文法を変えません。 * **ソースと方法論のディスクロージャー**: `data_sources`、`methodology`、`segmentation_criteria`、`criteria_url`、`refresh_cadence`、`lookback_window`、`onboarder` は、セグメントがどうコンパイルされたかを記述します。オフラインと公的記録のソースカテゴリーは `onboarder` を必要とします。 * **モデリングディスクロージャー**: `methodology` が `modeled` または `audience_expansion` が `true` のとき `modeling` が必須です。必須のモデリングディスクロージャーは、ディスクロージャーが適用される管轄区域を名指ししなければなりません。 * **管轄区域とプライバシーメタデータ**: `countries`、GDPR スコープの `consent_basis`、`restricted_attributes`、`policy_categories`、`art9_basis` により、ガバナンスエージェントは使用制約を構造的に評価できます。ピアのシグナルを表面化するフェデレーティングエージェントは、ピアの `countries[]` を上限として扱い、バイヤーの意図するデプロイメント国に対して再チェックし、より狭いローカルポリシーのみを適用しなければなりません(MUST)。 * **データ主体の権利ルーティング**: `data_subject_rights.channels[]` は、このシグナルについてアクセス、消去、異議、ポータビリティ、訂正のリクエストがどこにルーティングされるかを宣言します。少なくとも 1 つのチャネルがアクセス、消去、異議の 1 つ以上をサポートしなければなりません。`response_sla_days` はシグナルスコープです。カスタム/プライベートシグナルと上流固有のルートは公開のプロバイダー全体のポリシー面を共有しないかもしれないからです。Global Privacy Control サポートはシグナル定義で宣言されず、コンシューマーは `data_subject_rights` から GPC の扱いを推論してはなりません(MUST NOT)。 ### ランタイム検証ノート シグナル定義スキーマは、多くの SDK ジェネレーターが保持しない制約に JSON Schema draft-07 の `if`/`then` と `contains` を使います。コンシューマーと SDK はこれらのケースにランタイムガードを実装すべきです(SHOULD): * `taxonomy` を持つ `value_type: "categorical"` は `taxonomy.value_mappings` を必要とします。 * `audience_scope: "single_domain"` は `originating_domain` を必要とします。 * `methodology: "modeled"` または `audience_expansion: true` は `modeling` を必要とします。 * オフラインまたは公的記録の `data_sources[]` は `onboarder` を必要とします。 * `data_subject_rights.channels[]` は、アクセス、消去、異議の 1 つ以上をサポートする少なくとも 1 つのチャネルを含まなければなりません。 `art9_basis` は、第 9 条の適用可能性が管轄区域と使用に依存するため、スキーマ必須ではなくポリシー必須です。ガバナンスエージェントはランタイムチェックを実行すべきです(SHOULD): `restricted_attributes[]` が非空で計画された使用が第 9 条の管轄区域に触れるとき、`art9_basis` を要求するか、有効化前にレビューイシューを提起します。 ## Tasks Signals Protocol は 2 つのタスクスキーマを定義します。すべてのコンフォーマントな Signals Protocol エージェントはディスカバリー用に `get_signals` を実装します。マーケットプレイスアクティベーション専門分野を主張するか、アクティベーション/デアクティベーションを宣伝するか、バイヤー管理のアクティベーションを必要とするシグナルを返すエージェントは、アクティベーションライフサイクル面として `activate_signal` を実装します。 ### get\_signals **Schema**: [`get-signals-request.json`](https://adcontextprotocol.org/schemas/v3/signals/get-signals-request.json) / [`get-signals-response.json`](https://adcontextprotocol.org/schemas/v3/signals/get-signals-response.json) **Reference**: [`get_signals` task](/docs/signals/tasks/get_signals) キャンペーン条件に合致するシグナルを探索します。 **要件:** * オーケストレーターは `signal_spec`、`signal_refs`、または非推奨の `signal_ids` を含めなければなりません * シグナルエージェントはレスポンススキーマに定義された必須フィールドをすべて返さなければなりません * `is_live: true` かつ呼び出し元がデプロイメントにアクセスできる場合、シグナルエージェントは `activation_key` を含めなければなりません ### activate\_signal **Schema**: [`activate-signal-request.json`](https://adcontextprotocol.org/schemas/v3/signals/activate-signal-request.json) / [`activate-signal-response.json`](https://adcontextprotocol.org/schemas/v3/signals/activate-signal-response.json) **Reference**: [`activate_signal` task](/docs/signals/tasks/activate_signal) 意思決定プラットフォームで使用するためにシグナルを有効化します。`activate_signal` は、`signal_marketplace` を主張するか、アクティベーション/デアクティベーションを宣伝するか、バイヤー管理のアクティベーションを必要とするシグナルを返すエージェントに必須です。ディスカバリー専用の自社シグナルエージェントは公開する必要はありません。 **要件:** * オーケストレーターは `signal_agent_segment_id` と `destinations` を含めなければなりません * ガバナンス対象のアカウントでは、オーケストレーターは `check_governance` からの有効な `governance_context` を渡さなければなりません。シグナルエージェントは、それを省略または捏造したガバナンス対象の有効化を拒否しなければなりません * 成功時、シグナルエージェントは各デプロイメントの `is_live` を含む `deployments` 配列を返さなければなりません * `is_live: true` かつ呼び出し元がデプロイメントにアクセスできる場合、シグナルエージェントは `activation_key` を返さなければなりません * 失敗時、シグナルエージェントは `errors` 配列を返さなければならず(`deployments` 配列は含めない) ## Error Handling シグナルエージェントは [標準の AdCP エラースキーマ](/docs/building/by-layer/L3/error-handling) に従ってエラーを返さなければなりません。 シグナルエージェントは [エラーハンドリングリファレンス](/docs/building/by-layer/L3/error-handling) で定義された Signals Protocol のエラーコードを使用しなければなりません。 ## Security Considerations ### Transport Security Signals Protocol の通信はすべて TLS 1.2 以上の HTTPS を使用しなければなりません。 ### Authentication * オーケストレーターは有効な認証情報を用いてシグナルエージェントに認証しなければなりません * シグナルエージェントはリクエストを処理する前に認証情報を検証しなければなりません * シグナルエージェントはアカウントのコンテキストを利用してカタログのアクセスレベルを判断すべきです ### Activation Key Security * シグナルエージェントは認証済みでデプロイメントにアクセスできる呼び出し元にのみ `activation_key` を返さなければなりません * 呼び出し元がアクセスできないデプロイメントに対するアクティベーションキーを返してはなりません ### Data Minimization * シグナルエージェントは認証済みエージェントまたはアカウントがアクセスを許可されていないシグナルを返してはなりません ## Conformance ### Signal Agent Conformance 準拠したベースラインの Signals Protocol エージェントは次を満たさなければなりません。 1. 指定されたトランスポート(MCP または A2A)のうち少なくとも 1 つをサポートします 2. スキーマに沿って `get_signals` を実装します 3. レスポンススキーマで定義された必須フィールドを返す 4. 規定のエラーコードを使用します 5. プライベートシグナルの、そしてアクティベーションがサポートされる場合はアクティベーションキーのアクセス制御を適用します マーケットプレイスアクティベーション専門分野を主張するか、その他アクティベーションサポートを宣伝するエージェントは、スキーマに従い `activate_signal` も実装しなければなりません。ディスカバリー専用の自社シグナルエージェントは、`activate_signal` を実装せずに Signals Protocol ベースラインに準拠してもよい(MAY)。 ### Orchestrator Conformance 準拠した Signals Protocol オーケストレーターは次を満たさなければなりません。 1. シグナルエージェントと認証を行います 2. リクエストスキーマで定義された必須フィールドを含めます 3. `activate_signal` を使うときは非同期の有効化レスポンスを処理します 4. アクティベーションがスコープ内のとき外部デプロイメントターゲティングに `activation_key` を使い、セールスエージェントのメディアバイでのセラー提供シグナル選択にはパッケージレベルの `signal_targeting_groups` を使う セラー提供のシグナルをセールスエージェントのバイの特定パッケージにのみ適用すべき場合、オーケストレーターはその選択を `create_media_buy.packages[].targeting_overlay.signal_targeting_groups` に運ぶべきです(SHOULD)。必要な場合は選択したシグナルの `pricing_option_id`、選択した `signal_ref` を含めます。`get_signals` はより広範なディスカバリー面で、選択したプロダクトのインライン `signal_targeting_options`(存在する場合)と `signal_targeting_rules` がバイ時の適格性と価格を規定します。ホールセールプロダクトはインラインオプションを省略し、候補ディスカバリーに `get_signals` に依存できます。`included_signals` は記述的なだけです: プロダクトにすでにバンドルまたは計画されたシグナルを識別しますが、それらを選択可能にはしません。プロダクトオプションまたは `get_signals` 結果がセラーの要求する別個の `signal_agent_segment_id` を公開する場合、バイヤーはそれを実行ハンドルとしてエコーします。そうでなければ `signal_ref` で十分です。これはパッケージレベルのバインディングです。`audience_include` と `audience_exclude` は `sync_audiences` からのバイヤー管理オーディエンスにスコープされたままです。 ## Implementation Notes ### Multi-Platform Discovery オーケストレーターは 1 回の `get_signals` 呼び出しで複数プラットフォームにわたるシグナルを要求してもよい。 シグナルエージェントは要求されたすべてのプラットフォームについてデプロイ情報を返すことが推奨されます。 ### Activation Timing シグナルの有効化は通常非同期です。 * シンプルな有効化: 1〜2 時間 * 複雑なデプロイ: 最大 24〜48 時間 オーケストレーターは有効化リクエスト直後に利用可能になると仮定してはなりません。 ### デスティネーションタイプの選択 `activate_signal` リクエストは 2 つのデスティネーションタイプをサポートします。選択はバイヤーの実行パスによります: * セールスエージェント経由で購入するオーケストレーターは、SA の URL を持つ `type: "agent"` デスティネーションを使うべきです(SHOULD)。SA がダウンストリームのプラットフォーム連携を処理します — どの DSP を使うかは実装の詳細です。 * DSP で直接購入するオーケストレーターは `type: "platform"` デスティネーションを使うべきです(SHOULD)。オーケストレーターは、有効化プラットフォームがキャンペーンが実行される場所と一致することを保証する責任があります。 * シグナルマーケットプレイスエージェントは、デスティネーションスキーマに従い両方のデスティネーションタイプをサポートしなければなりません(MUST)。 ### クリエイティブシグナルのファンアウトとトラフィッキング互換性 `build_creative` は `signal_conditions` にわたってファンアウトしてもよい([#5240](https://github.com/adcontextprotocol/adcp/issues/5240))— シグナル条件ごとに 1 つの別個のクリエイティブグループを生成する keep-all の制作軸です(例: 雨のクリエイティブ AND 晴れのクリエイティブ)。生成された各グループは、それが対象とする `signal_condition`(`SignalTargeting`)でタグ付けされます。 **トラフィッキング互換性の不変条件(規範的)。** ある信号条件のために構築されたクリエイティブは、互換性のない条件をターゲティングするパッケージに配信されてはなりません(MUST NOT)。セールスエージェントは `create_media_buy` / `sync_creatives` でこの**トラフィッキング時拒否**を強制します: `signal_condition` がパッケージのシグナルターゲティングと互換性のないクリエイティブを割り当てると `SIGNAL_TARGETING_INCOMPATIBLE` で拒否されます。これは `build_creative` では強制されません — ビルド層ではシグナルポインターは助言的(バイヤー添付入力契約)で、強制はトラフィッキング境界に存在します。 **マッチング。** 互換性は共有される `signal_ref` アイデンティティで一致されます — 名前空間付きの `signal_agent_segment_id` / データプロバイダースコープの `signal_ref` が主要パス、カテゴリカルな `{signal_id, value}` がフォールバック。`value_type: numeric` では比較は範囲オーバーラップ(WG 未決: 範囲オーバーラップ vs 完全一致)。これは `create_media_buy` の `packages[].targeting_overlay.signal_targeting_groups` が使う**同じ** `signal_ref` アイデンティティで、共有タクソノミーレジストリなしでエージェント間マッチを構造的に可能にするものです。 ## Schema Reference | Schema | Description | | ------------------------------------------------------------------------------------------------------------------------- | ------------------------- | | [`signals/get-signals-request.json`](https://adcontextprotocol.org/schemas/v3/signals/get-signals-request.json) | get\_signals request | | [`signals/get-signals-response.json`](https://adcontextprotocol.org/schemas/v3/signals/get-signals-response.json) | get\_signals response | | [`signals/activate-signal-request.json`](https://adcontextprotocol.org/schemas/v3/signals/activate-signal-request.json) | activate\_signal request | | [`signals/activate-signal-response.json`](https://adcontextprotocol.org/schemas/v3/signals/activate-signal-response.json) | activate\_signal response | | [`core/deployment.json`](https://adcontextprotocol.org/schemas/v3/core/deployment.json) | Deployment target | | [`core/activation-key.json`](https://adcontextprotocol.org/schemas/v3/core/activation-key.json) | Activation key | # activate_signal Source: https://adcp-docs-ja.pier1.co.jp/docs/signals/tasks/activate_signal **タスク**: 特定のプラットフォーム/アカウントでシグナルを有効化します。 **応答時間**: 数分~数日(人手を伴う非同期処理の可能性あり) **リクエストスキーマ**: [`https://adcontextprotocol.org/schemas/v3/signals/activate-signal-request.json`](https://adcontextprotocol.org/schemas/v3/signals/activate-signal-request.json) **レスポンススキーマ**: [`https://adcontextprotocol.org/schemas/v3/signals/activate-signal-response.json`](https://adcontextprotocol.org/schemas/v3/signals/activate-signal-response.json) `activate_signal` は次を含む有効化ライフサイクル全体を扱います。 * 有効化リクエストの開始 * 進行状況の監視 * 最終的なデプロイステータスの返却 ## リクエストパラメーター | Parameter | Type | Required | Description | | ------------------------- | ------------------------------------------------------------------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `idempotency_key` | string | Yes | このリクエストのクライアント生成一意キー。リトライ時の重複有効化を防ぐ。(seller, request) ペアごとに一意でなければならない。最小 16 文字。規範的セマンティクスは [Idempotency](/docs/building/by-layer/L1/security#idempotency) を参照。 | | `signal_agent_segment_id` | string | Yes | 有効化するシグナルの `get_signals` からの不透明なシグナルハンドル | | `action` | string | No | `"activate"`(デフォルト)または `"deactivate"`。デアクティベートはデータガバナンスコンプライアンス(GDPR、CCPA)のためダウンストリームプラットフォームからセグメントを削除します。 | | `destinations` | Destination\[] | Yes | 有効化のターゲットデスティネーション(下記 Destination Object を参照) | | `account` | [AccountRef](/docs/building/by-layer/L2/accounts-and-agents#account-references) | No | この有効化のアカウント。`sync_accounts` で確立された商業関係に関連付けます。 | | `pricing_option_id` | string | Yes(シグナルに料金オプションがある場合) | [`get_signals`](/docs/signals/tasks/get_signals) レスポンスの `pricing_options` から選択した料金オプション。有効化時のバイヤーの価格コミットメントを記録します。後続の `report_usage` 呼び出しでこの同じ値を渡します。 | | `governance_context` | string | Conditional | [`check_governance`](/docs/governance/campaign/tasks/check_governance) がこの有効化に返す不透明な承認コンテキスト。有効化アカウントに登録されたガバナンスエージェントがある場合に必須。 | ## Governance シグナル有効化は、選択したシグナルがアクティベーション、利用、CPM 料金を運ぶとき、支出コミットイベントです。ガバナンス対象のアカウントでは、バイヤー側オーケストレーターは `activate_signal` の前に、`purchase_type: "signal_activation"` と、`total_budget` や `budget` のような支出基準を含む正規化された有効化ペイロードで `check_governance` を呼ばなければなりません(MUST)。 `check_governance` が有効化を承認したら、返された `governance_context` を `activate_signal` に渡します。アカウントに `sync_governance` で登録されたガバナンスエージェントがあり、リクエストが有効な `governance_context` を省略する場合、シグナルエージェントは `PERMISSION_DENIED` でリクエストを拒否しなければなりません(MUST)。`check_governance` が有効化を拒否した場合、バイヤーは `activate_signal` を呼んではなりません(MUST NOT)。 ### Destination Object 各デプロイ先は `type` フィールドでプラットフォームベースかエージェントベースかを区別します。 | Parameter | Type | Required | Description | | ----------- | ------------ | ------------- | -------------------------------------------------------------------- | | `type` | string | Yes | 識別子。DSP なら "platform"、セールスエージェントなら "agent" | | `platform` | string | Conditional\* | プラットフォーム ID(例: 'the-trade-desk', 'amazon-dsp')。type="platform" の場合必須 | | `agent_url` | string (URI) | Conditional\* | セールスエージェントを識別する URL。type="agent" の場合必須 | | `account` | string | No | プラットフォーム/エージェント上のアカウント ID | \*`platform` は type="platform" の場合必須、`agent_url` は type="agent" の場合必須。 **Activation Keys**: 認証済みの呼び出し元がリクエスト中のいずれかのデプロイ先にアクセス権を持つ場合、シグナルエージェントはレスポンスにそのデプロイメント用の `activation_key` を含めます。 **Permission Model**: キーの付与はリクエストのフラグではなく、シグナルエージェント側の認証・認可に基づきます。 * セールスエージェントは自分の `agent_url` に一致するデプロイメントのキーを受け取ります * 複数 DSP への認証情報を持つバイヤーは、それらすべてのキーを受け取ります * アクセス可否はシグナルエージェントの権限モデルで決定されます ## レスポンス構造 すべての AdCP レスポンスは次を含みます。 * **message**: 有効化ステータスの概要 * **context\_id**: 進行状況追跡用のセッション ID * **data**: タスク固有のペイロード(下記 Response Data 参照) レスポンス構造はプロトコル間で同一で、トランスポートのみ異なります。 * **MCP**: 平坦な JSON を返却 * **A2A**: アーティファクトで返却(text パートに message、data パートにデータ) 有効化のような非同期処理では、両プロトコルとも次をサポートします。 * **ステータス追跡**: task\_id で完了状況を確認 * **進行更新**: 有効化進捗のリアルタイム更新 ## レスポンスデータ ```json theme={null} { "deployments": [ { "type": "platform", "platform": "string", "account": "string", "activation_key": { "type": "segment_id", "segment_id": "string" }, "estimated_activation_duration_minutes": "number", "deployed_at": "string" } ], "errors": [ { "code": "string", "message": "string", "field": "string", "suggestion": "string", "details": {} } ] } ``` ### フィールド説明 * **deployments**: 各デプロイ先の結果 * **platform**: DSP のプラットフォーム ID(platform か agent\_url のいずれかが入る) * **agent\_url**: デプロイメントエージェントの URL(platform か agent\_url のいずれかが入る) * **account**: 必要に応じたアカウント ID * **activation\_key**: ターゲティングに使用するキー(下記参照)。認証済みの呼び出し元がそのデプロイにアクセスできる場合にのみ含まれます。 * **estimated\_activation\_duration\_minutes**: 非同期処理の所要時間目安 * **deployed\_at**: 有効化完了時刻(ISO 8601) * **errors**: 有効化中のエラーや警告 * **code**: プログラムで扱いやすいエラーコード * **message**: 文脈を含む説明 * **field**: エラーに関連するフィールド(任意) * **suggestion**: 解決策の提案(任意) * **details**: 有効化固有の追加情報(任意) ### Activation Key Object デプロイ先でのシグナル利用方法を示します。セグメント ID かキー/バリューのいずれかです。 **セグメント ID 形式(DSP で一般的):** ```json theme={null} { "type": "segment_id", "segment_id": "ttd_segment_12345" } ``` **キー/バリュー形式(セールスエージェントで一般的):** ```json theme={null} { "type": "key_value", "key": "audience_segment", "value": "luxury_auto_intenders" } ``` ### アクティベーションキーの使用 アクティベーションキーは、デスティネーションでシグナルをどう参照するかをバイヤーに伝えます。実行パスはデスティネーションタイプによります: **プラットフォームデスティネーション** — `activation_key` はプラットフォームネイティブの識別子 `segment_id` を含みます。シグナルエージェントはセグメントデータを DSP にプッシュしました。バイヤーはそのプラットフォームでキャンペーンターゲティングを設定する際にこのキーを参照します。バイヤーは、有効化プラットフォームがキャンペーンを実行する場所と一致することを保証する責任があります。 **エージェントデスティネーション** — `activation_key` はシグナルがセールスエージェント上でライブであることを確認します。SA は有効化を内部で記録し、`create_media_buy` を通じてメディアバイを履行するときにシグナルベースのターゲティングを適用できます。セラーがインライン `Product.signal_targeting_options` を通じて、またはインラインオプションを省略するホールセールプロダクトについて `get_signals` を通じて選択可能なシグナルを公開する場合、バイヤーはそれらのシグナルを `packages[].targeting_overlay.signal_targeting_groups` で選択し、選択した `signal_ref`、必要な場合は `pricing_option_id`、プロダクトオプションまたはシグナル結果が要求する別個のセラー実行ハンドルを運びます。バイヤーは SA がどの DSP を使うかを知る必要はありません — ダウンストリームのプラットフォーム連携は SA の責任です。 **デスティネーションタイプの選択**: DSP で直接購入するときは `type: "platform"` を使います。セールスエージェント経由で購入するときは `type: "agent"` を使います — SA は独自の DSP ターゲティングを実装の詳細として調整します。 ## プロトコル別の例 AdCP のペイロードはプロトコル間で同一で、ラッパーのみ異なります。 ### MCP リクエスト - セールスエージェントへの有効化 ```json theme={null} { "tool": "activate_signal", "arguments": { "signal_agent_segment_id": "luxury_auto_intenders", "deployments": [{ "type": "agent", "agent_url": "https://wonderstruck.salesagents.com" }] } } ``` ### MCP レスポンス - 同期(キー/バリュー) 即時レスポンスとアクティベーションキー: ```json theme={null} { "status": "completed", "message": "Signal successfully activated on Wonderstruck sales agent", "context_id": "ctx-signals-123", "deployments": [{ "type": "agent", "agent_url": "https://wonderstruck.salesagents.com", "is_live": true, "activation_key": { "type": "key_value", "key": "audience_segment", "value": "luxury_auto_intenders_v2" }, "deployed_at": "2025-01-15T14:30:00Z" }] } ``` ### MCP リクエスト - DSP 有効化 ```json theme={null} { "tool": "activate_signal", "arguments": { "signal_agent_segment_id": "luxury_auto_intenders", "deployments": [{ "type": "platform", "platform": "the-trade-desk", "account": "agency-123-ttd" }] } } ``` ### MCP レスポンス - 非同期(セグメント ID) 初期レスポンス: ```json theme={null} { "status": "completed", "message": "Initiating activation of 'Luxury Auto Intenders' on The Trade Desk", "context_id": "ctx-signals-123", "deployments": [{ "type": "platform", "platform": "the-trade-desk", "account": "agency-123-ttd", "is_live": false, "estimated_activation_duration_minutes": 30 }] } ``` 完了後のポーリング: ```json theme={null} { "status": "completed", "message": "Signal successfully activated on The Trade Desk", "context_id": "ctx-signals-123", "deployments": [{ "type": "platform", "platform": "the-trade-desk", "account": "agency-123-ttd", "is_live": true, "activation_key": { "type": "segment_id", "segment_id": "ttd_agency123_lux_auto" }, "deployed_at": "2025-01-15T14:30:00Z" }] } ``` ### A2A リクエスト #### 自然言語での呼び出し ```javascript theme={null} await a2a.send({ message: { parts: [{ kind: "text", text: "Please activate the luxury_auto_intenders signal on The Trade Desk for account agency-123-ttd." }] } }); ``` #### スキルを明示して呼び出す ```javascript theme={null} await a2a.send({ message: { parts: [{ kind: "data", data: { skill: "activate_signal", parameters: { signal_agent_segment_id: "luxury_auto_intenders", deployments: [{ type: "platform", platform: "the-trade-desk", account: "agency-123-ttd" }] } } }] } }); ``` ### A2A レスポンス(ストリーミング) 初期レスポンス: ```json theme={null} { "taskId": "task-signal-001", "status": { "state": "working" } } ``` Server-Sent Events による更新: ``` data: {"message": "Validating signal access permissions..."} data: {"message": "Configuring deployment on The Trade Desk..."} data: {"message": "Finalizing activation..."} data: {"status": {"state": "completed"}, "artifacts": [{ "artifactId": "artifact-signal-activation-abc123", "name": "signal_activation_result", "parts": [ {"kind": "text", "text": "Signal successfully activated on The Trade Desk"}, {"kind": "data", "data": { "context_id": "ctx-signals-123", "deployments": [{ "type": "platform", "platform": "the-trade-desk", "account": "agency-123-ttd", "activation_key": { "type": "segment_id", "segment_id": "ttd_agency123_lux_auto" }, "deployed_at": "2025-01-15T14:30:00Z" }] }} ] }]} ``` ### プロトコルトランスポート * **MCP**: ポーリングや webhook による非同期トラッキング用の task\_id を返す * **A2A**: SSE で進捗と完了をリアルタイム送信 * **データ整合性**: どちらも同一の AdCP データ構造とバージョン情報を含みます ### Webhook サポート 初期レスポンスが `submitted` となる長時間処理では、webhook を設定して完了時のレスポンスを受け取ります。 ```javascript theme={null} const response = await session.call('activate_signal', { signal_agent_segment_id: "luxury_auto_intenders", deployments: [{ type: "platform", platform: "the-trade-desk", account: "agency-123-ttd" }] }, { webhook_url: "https://buyer.com/webhooks/adcp/activate_signal/agent_id/op_id", webhook_auth: { type: "bearer", credentials: "secret-token" } } ); ``` 有効化が完了すると、`activate_signal` のフルレスポンスが届きます。 ```http theme={null} POST /webhooks/adcp/activate_signal/agent_id/op_id HTTP/1.1 Content-Type: application/json Authorization: Bearer secret-token { "deployments": [{ "type": "platform", "platform": "the-trade-desk", "account": "agency-123-ttd", "activation_key": { "type": "segment_id", "segment_id": "ttd_agency123_lux_auto" }, "deployed_at": "2025-01-15T14:30:00Z" }] } ``` 詳細な webhook 設定や信頼性については **[Webhooks](/docs/building/by-layer/L3/webhooks)** を参照してください。 ## シナリオ ### 非同期有効化 - 初期レスポンス(保留) **Message**: "I've initiated activation of 'Luxury Automotive Context' on PubMatic for account brand-456-pm. This typically takes about 60 minutes. I'll monitor the progress and notify you when it's ready to use." **Complete Response**: ```json theme={null} { "status": "completed", "message": "I've initiated activation of 'Luxury Automotive Context' on PubMatic for account brand-456-pm. This typically takes about 60 minutes. I'll monitor the progress and notify you when it's ready to use.", "context_id": "ctx-signals-def456", "deployments": [{ "type": "platform", "platform": "pubmatic", "account": "brand-456-pm", "estimated_activation_duration_minutes": 60 }] } ``` ### 非同期有効化 - 最終レスポンス(デプロイ済み) **Message**: "Excellent! The 'Luxury Automotive Context' signal is now live on PubMatic. You can start using it immediately with the activation key provided. The activation completed faster than expected - just 52 minutes." **Complete Response**: ```json theme={null} { "status": "completed", "message": "Excellent! The 'Luxury Automotive Context' signal is now live on PubMatic. You can start using it immediately with the activation key provided. The activation completed faster than expected - just 52 minutes.", "context_id": "ctx-signals-def456", "deployments": [{ "type": "platform", "platform": "pubmatic", "account": "brand-456-pm", "activation_key": { "type": "segment_id", "segment_id": "pm_brand456_peer39_lux_auto" }, "deployed_at": "2025-01-15T14:30:00Z" }] } ``` ### 同期有効化 - セールスエージェント(即時) **Message**: "Signal successfully activated on Wonderstruck sales agent. Use the key-value pair in your targeting configuration." **Complete Response**: ```json theme={null} { "status": "completed", "message": "Signal successfully activated on Wonderstruck sales agent. Use the key-value pair in your targeting configuration.", "context_id": "ctx-signals-ghi789", "deployments": [{ "type": "agent", "agent_url": "https://wonderstruck.salesagents.com", "activation_key": { "type": "key_value", "key": "audience_segment", "value": "luxury_auto_context_v2" }, "deployed_at": "2025-01-15T14:31:00Z" }] } ``` ### 成功(警告付き) **Message**: "Successfully activated 'Luxury Automotive Context' on PubMatic, but noted some configuration issues. The signal is live and ready to use, though performance may be sub-optimal until the account settings are updated." **Complete Response**: ```json theme={null} { "status": "completed", "message": "Successfully activated 'Luxury Automotive Context' on PubMatic, but noted some configuration issues. The signal is live and ready to use, though performance may be sub-optimal until the account settings are updated.", "context_id": "ctx-signals-def456", "deployments": [{ "type": "platform", "platform": "pubmatic", "account": "brand-456-pm", "activation_key": { "type": "segment_id", "segment_id": "pm_brand456_peer39_lux_auto" }, "deployed_at": "2025-01-15T14:30:00Z" }], "errors": [ { "code": "SUBOPTIMAL_CONFIGURATION", "message": "Account frequency cap settings may limit signal reach", "field": "account.frequency_settings", "suggestion": "Contact your PubMatic account manager to review frequency cap settings for optimal performance" } ] } ``` ### エラーレスポンス(失敗) **Message**: "I couldn't activate the signal on PubMatic. Your account 'brand-456-pm' doesn't have permission to use Peer39 data. Please contact your PubMatic account manager to enable Peer39 access, then we can try again." **Complete Response**: ```json theme={null} { "status": "failed", "message": "I couldn't activate the signal on PubMatic. Your account 'brand-456-pm' doesn't have permission to use Peer39 data. Please contact your PubMatic account manager to enable Peer39 access, then we can try again.", "context_id": "ctx-signals-def456", "errors": [ { "code": "DEPLOYMENT_UNAUTHORIZED", "message": "Account brand-456-pm not authorized for Peer39 data on PubMatic", "field": "deployment.account", "suggestion": "Contact your PubMatic account manager to enable Peer39 data access for your account", "details": { "account_id": "brand-456-pm", "deployment_url": "https://pubmatic.com", "data_provider": "peer39", "required_permission": "third_party_data_access" } } ] } ``` ## エラーコード ### Activation Errors * `REFERENCE_NOT_FOUND`: 参照された `signal_agent_segment_id` が存在しないか、呼び出しアカウントからアクセスできません(`error.field` が失敗パラメーターを識別します) * `ACTIVATION_FAILED`: 何らかの理由で有効化できません * `ALREADY_ACTIVATED`: 指定プラットフォーム/アカウントですでに有効化済み * `DEPLOYMENT_UNAUTHORIZED`: 権限不足でデプロイ不可 * `INVALID_PRICING_MODEL`: リクエストした課金モデルが利用できません ### Configuration Warnings * `SUBOPTIMAL_CONFIGURATION`: 有効化したが設定により性能に影響の可能性 * `SLOW_ACTIVATION`: 想定より時間がかかっているが進行中 * `FREQUENCY_CAP_RESTRICTIVE`: 有効化済みだがフリークエンシーキャップが厳しい可能性 ## エラーハンドリングの考え方 ### ステータスとエラーの区別 * **タスクステータス**: 有効化の全体結果(`deployed`、`failed` など) * **errors 配列**: 具体的な問題や警告、対応策 * **部分成功**: `errors` に警告があっても `deployed` となる場合があります ### エラーの種類 * **致命的エラー**: 有効化を阻害(status = `failed`) * **警告**: 有効化は成功するが注意点あり(status = `deployed` + errors) * **設定上の問題**: パフォーマンスに影響する非致命的な課題 ## 利用上の注意 1. **アカウント指定**: アカウント単位で有効化する場合は `account` を含めます 2. **プラットフォーム全体**: プラットフォーム全体で有効化する場合は `account` を省略 3. **非同期処理**: ステータス更新がある長時間タスク 4. **監視**: ポーリングまたは SSE で task\_id を監視 5. **冗長性**: 失敗時はリトライ可能(冪等) ## 実装ガイド ### 有効化メッセージの生成 `message` フィールドは明確なステータスとアクションを伝えるべきです。 ```python theme={null} def generate_activation_message(status, signal_info, request): if status == "pending": return f"I've initiated activation of '{signal_info.name}' on {request.platform} for account {request.account}. This typically takes about {signal_info.estimated_duration} minutes. I'll monitor the progress and notify you when it's ready to use." elif status == "processing": progress_details = get_progress_details() time_remaining = calculate_time_remaining() return f"Good progress on the activation. {progress_details}. About {time_remaining} minutes remaining." elif status == "deployed": actual_duration = calculate_actual_duration() timing_note = "faster than expected" if actual_duration < signal_info.estimated_duration else "as expected" return f"Excellent! The '{signal_info.name}' signal is now live on {request.platform}. You can start using it immediately in your campaigns with the ID '{signal_info.platform_id}'. The activation completed {timing_note} - just {actual_duration} minutes." elif status == "failed": error_explanation = explain_error_in_context(error_code) next_steps = get_remediation_steps(error_code) return f"I couldn't activate the signal on {request.platform}. {error_explanation}. {next_steps}" ``` # get_signals Source: https://adcp-docs-ja.pier1.co.jp/docs/signals/tasks/get_signals get_signals はオーディエンスシグナルとコンテキストシグナルを発見するための AdCP タスクです。自然言語またはシグナル参照で検索し、プラットフォームや CPM でフィルタリングし、アクティベーションキー付きのリアルタイムなデプロイステータスを取得します。 **タスク**: 説明に基づいてシグナルを発見し、それらがどこにデプロイされているかの詳細を返します。 **応答時間**: 約 60 秒(バックエンドシステムとの推論/RAG) **リクエストスキーマ**: [`https://adcontextprotocol.org/schemas/v3/signals/get-signals-request.json`](https://adcontextprotocol.org/schemas/v3/signals/get-signals-request.json) **レスポンススキーマ**: [`https://adcontextprotocol.org/schemas/v3/signals/get-signals-response.json`](https://adcontextprotocol.org/schemas/v3/signals/get-signals-response.json) `get_signals` タスクは、シグナルメタデータとプラットフォーム横断のリアルタイムなデプロイステータスの両方を返します。これによりエージェントは可用性を把握し、アクティベーションプロセスを案内できます。 ## リクエストパラメーター | Parameter | Type | Required | Description | | --------------------------- | ------------------------------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `discovery_mode` | string | No(v3.1+ では含めるべき) | `"brief"`(デフォルト)または `"wholesale"`。`"brief"`: 従来の挙動 — `signal_spec`、`signal_refs`、または非推奨の `signal_ids` が必須で、エージェントが推論/RAG を実行します。`"wholesale"`: 生のホールセールシグナルフィードの列挙 — `signal_spec`、`signal_refs`、`signal_ids` は指定してはなりません(MUST NOT)。エージェントは、存在する場合は `filters` / `account` / `destinations` / `countries` でスコープされ、ページネーションされた完全な価格付きシグナルフィードを返します。**タイミングセマンティクス:** `"wholesale"` はホールセールシグナルフィードの読み取りであり、エージェントは同期的に応答すべきで(SHOULD)、非同期/Submitted 経路を通してはなりません(MUST NOT)。部分的完了は [`incomplete[]`](#incomplete-配列) を使います。`discovery_mode` を持たない v3.1 より前のクライアントからのリクエストを受けたエージェントは `"brief"` をデフォルトにしなければなりません(MUST)。サポートの探索は [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)(`signals.discovery_modes`)経由。 | | `signal_spec` | string | Conditional | 望むシグナルの自然言語による説明。`discovery_mode` が `"brief"` で `signal_refs` とレガシーの `signal_ids` の両方が欠けている場合に必須。`discovery_mode` が `"wholesale"` の場合は指定してはなりません(MUST NOT)。 | | `signal_refs` | SignalRef\[] | Conditional | 参照で特定のシグナルを検索します。`discovery_mode` が `"brief"` で `signal_spec` が欠けている場合に必須。`discovery_mode` が `"wholesale"` の場合は指定してはなりません(MUST NOT)。 | | `signal_ids` | SignalID\[] | Conditional | **非推奨。** 代わりに `signal_refs` を使います。旧クライアント向けのレガシーな特定シグナル検索。`discovery_mode` が `"wholesale"` の場合は指定してはなりません(MUST NOT)。 | | `account` | [AccountRef](/docs/building/by-layer/L2/accounts-and-agents#account-references) | No | このリクエストのアカウント。指定された場合、シグナルエージェントは設定されていればアカウント別の料金オプションを返します。ホールセールモードではこれがレートカードのスコープになります。省略された場合、エージェントはデフォルトのレートカード価格を返すか、`pricing_options` を完全に省略します。 | | `destinations` | Destination\[] | No | 特定のエージェント/プラットフォームで有効化可能なシグナルに絞り込みます。省略された場合、現在のエージェントで利用可能なすべてのシグナルを返します。下記 Destination Object を参照。 | | `countries` | string\[] | No | シグナルが使われる国(ISO 3166-1 alpha-2 コード) | | `filters` | Filters | No | 結果を絞り込むフィルター(下記 Filters Object を参照)。ホールセールモードでは、フィルターは列挙されるシグナルフィードを制約します(例: `filters.data_providers: ["acme-data"]` はそのプロバイダーのシグナルのみを返します)。 | | `fields` | string\[] | No | レスポンスに含める特定のシグナルフィールド。`get_products.fields` と整合します。必須の識別・有効化フィールドは、レスポンススキーマが要求する場合は常に含まれます。`taxonomy`、`data_sources`、`methodology`、`segmentation_criteria`、`criteria_url`、`onboarder`、`modeling`、`countries`、`consent_basis`、`restricted_attributes`、`policy_categories`、`art9_basis`、`data_subject_rights` などのリッチな定義メタデータのプログレッシブディスクロージャーに使います。エージェントは、厳密な検索、絞り込み、小規模なカスタムシグナル結果セット、および利用可能な場合はプライベート/ソースネイティブなシグナルについて、要求されたフィールドを尊重すべきです(SHOULD)。`fields` は投影リクエストであり、権限付与ではありません。呼び出し元が基礎となるリネージ、方法論、権利ルーティングメタデータへのアクセスを認可されていない限り、エージェントは要求された定義フィールドを編集(リダクト)してもかまいません(MAY)。別のプロバイダーのシグナルについて `consent_basis` や `art9_basis` が投影される場合、その値はプロバイダーが宣言したシグナル定義の姿勢のままです。セラーや連携エージェントが自身の処理根拠で置き換えてはなりません(MUST NOT)。広範なディスカバリーやホールセールページは、プロバイダーが公開した定義や開示 URL へのコンパクトなポインターを返してもかまいません(MAY)。 | | `if_wholesale_feed_version` | string | No | 以前の `get_signals` レスポンスから得た不透明な `wholesale_feed_version` トークン。指定された場合、エージェントは呼び出し元の `cache_scope` に対する現在のホールセールシグナルフィードバージョンと比較し、何も変わっていなければ `unchanged: true`(`signals` は省略)を返してもかまいません(MAY)。[ホールセールフィードバージョニング](#ホールセールフィードバージョニング) と [キャッシュレイヤリング](#キャッシュレイヤリング) を参照。 | | `if_pricing_version` | string | No | 以前のレスポンスから得た不透明な `pricing_version` トークン。`if_wholesale_feed_version` と共にのみ送信しなければなりません(MUST)。評価順序: `if_wholesale_feed_version` 不一致 → 完全ペイロード。`if_wholesale_feed_version` は一致するが `if_pricing_version` が不一致 → 完全ペイロード(呼び出し元が更新された `pricing_options` を確認できるように)。両方一致 → エージェントは `unchanged: true` を返してもかまいません(MAY)。価格を別途追跡しないエージェントはこれを無視します。 | | `max_results` | number | No | **非推奨。** 代わりに `pagination.max_results` を使います。両方が存在する場合は `pagination.max_results` が優先されます。AdCP 4.0 で削除予定。 | | `pagination` | object | No | ページネーションエンベロープ。`pagination.max_results`(最大: 100、デフォルト: 50)がページサイズを制御し、`pagination.cursor`(前回レスポンスからの不透明トークン)がページを進めます。ホールセールでは必須(シグナルフィードは大きい場合があるため)。 | | `push_notification_config` | PushNotificationConfig | No | `brief` のセマンティックディスカバリーにおける非同期の終端完了/失敗通知用の任意の Webhook チャネル。`task_id` を持つ `submitted` レスポンスは、このフィールドの有無にかかわらず `get_task_status`(レガシー `tasks/get`)でポーリング可能です。リクエストがこのフィールドを含み、エージェントが `submitted` を返す場合、エージェントは少なくとも終端の完了/失敗通知を設定された Webhook に配信しなければなりません(MUST)。中間の進捗通知は任意(MAY)。Webhook チャネルを尊重できない場合、エージェントは黙って受け入れるのではなく、構造化エラーでリクエストを拒否しなければなりません(MUST)。`wholesale` では無視されます。このフィールドが存在するからといって、エージェントはホールセール読み取りを Submitted 経路に通してはなりません(MUST NOT)。 | `discovery_mode: "wholesale"` は AdCP 3.1+ のディスカバリー形状です。呼び出し元が 3.1+ のシグナルサーフェスやホールセールサポートを前提にできない場合、クライアントは ホールセールリクエストを発行する前に、まず [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) を呼び出し、`signals.discovery_modes` に `"wholesale"` が含まれることを確認すべきです。 フィールドが無いか `"brief"` のみを列挙している場合は、エージェントを brief 専用として扱い、 `signal_spec`、`signal_refs`、または非推奨の `signal_ids` を使います。 ### 非同期ディスカバリー `discovery_mode: "brief"` は、セマンティックディスカバリーが遅いプロバイダークエリや、初期レスポンス前に完了できないレビューに依存する場合、`submitted` を返してもかまいません(MAY)。`task_id` を用いた `get_task_status`(レガシー `tasks/get`)のポーリングは常に有効です。`push_notification_config` が存在し、エージェントが `submitted` を返す場合、エージェントは少なくとも終端の完了/失敗通知をその Webhook にも送信します。中間の進捗通知は任意です。`discovery_mode: "wholesale"` は同期的なフィード読み取りのままで、部分的完了は `submitted` ではなく `incomplete[]` で報告します。 ### Destination オブジェクト 各デプロイ先は `type` フィールドでプラットフォームベースかエージェントベースかを区別します。 | Parameter | Type | Required | Description | | ----------- | ------------ | ------------- | -------------------------------------------------------------------- | | `type` | string | Yes | 識別子。DSP なら "platform"、営業エージェントなら "agent" | | `platform` | string | Conditional\* | プラットフォーム ID(例: 'the-trade-desk', 'amazon-dsp')。type="platform" の場合必須 | | `agent_url` | string (URI) | Conditional\* | 営業エージェントを識別する URL。type="agent" の場合必須 | | `account` | string | No | プラットフォーム/エージェント上のアカウント ID | \*`platform` は type="platform" の場合必須、`agent_url` は type="agent" の場合必須。 **デスティネーションフィルタリング**: シグナルは、要求されたデスティネーションの*いずれか*で利用可能であれば返されます(OR セマンティクス)。シグナルが利用できないデスティネーションは、そのシグナルのレスポンス `deployments` 配列から省略されます。一部のデスティネーションがシグナルをサポートしない場合、`PARTIAL_COVERAGE` 警告が含まれることがあります。 **アクティベーションキー**: 認証済みの呼び出し元がリクエスト中のいずれかのデスティネーションにアクセス権を持つ場合、シグナルエージェントはそれらのデプロイメント(`is_live: true` のとき)についてレスポンスに `activation_key` フィールドを含めます。 **権限モデル**: シグナルエージェントは、呼び出し元の認証・認可に基づいてキーの付与を決定します。例: * 営業エージェントは自分の `agent_url` に一致するデプロイメントのキーを受け取ります * 複数 DSP プラットフォームへの認証情報を持つバイヤーは、それらすべてのデプロイメントのキーを受け取ります * アクセス可否は、リクエスト中のフラグではなく、シグナルエージェントの権限システムで決定されます ### Filters オブジェクト | Parameter | Type | Required | Description | | ------------------------- | --------- | -------- | ------------------------------------------------------------------------------------------------ | | `catalog_types` | string\[] | No | カタログタイプでフィルタリング("marketplace"、"custom"、"owned") | | `data_providers` | string\[] | No | 特定のデータプロバイダーでフィルタリング | | `max_cpm` | number | No | 最大 CPM 価格フィルター。すべての CPM ベースの料金オプションがこの値を超えるシグナルを除外します。CPM ベースの料金オプションを持たないシグナルはこのフィルターの影響を受けません。 | | `min_coverage_percentage` | number | No | 最小カバレッジ要件 | `catalog_types`、非推奨の `catalog_signals` 機能フラグ、非推奨の `signal_id.source: "catalog"` 値はレガシーなワイヤ用語です。新しい文章では、これらを adagents.json の `signals[]` におけるプロバイダー公開のシグナル定義として読み替えてください。 既存の 3.x エージェントは互換性のためこれらを受け付け/送出し続けてもかまいませんが、 新しい呼び出し元は `signal_ref` を使うべきで(SHOULD)、Signals プロトコルを使う前に `signals.features.catalog_signals` を必須としてはなりません(MUST NOT)。 ## レスポンス構造 すべての AdCP レスポンスは次を含みます。 * **message**: 操作結果の人間可読な要約 * **context\_id**: フォローアップリクエスト用のセッション継続識別子 * **data**: タスク固有のペイロード(下記 Response Data 参照) レスポンス構造はプロトコル間で同一で、トランスポートラッパーのみ異なります。 * **MCP**: 完全なレスポンスを平坦な JSON として返却 * **A2A**: アーティファクトとして返却(text パートに message、data パートにデータ) ## Response Data ```json theme={null} { "signals": [ { "signal_ref": { "scope": "data_provider", "data_provider_domain": "string", "signal_id": "string" }, "signal_agent_segment_id": "string", "name": "string", "description": "string", "signal_type": "string", "data_provider": "string", "coverage_percentage": "number (optional, deprecated)", "coverage_forecast": { "method": "estimate", "forecast_range_unit": "availability", "scope": { "kind": "inventory", "label": "network price-priority inventory" }, "bucket_semantics": "exclusive", "bucket_completeness": "partial", "points": [ { "label": "not present", "dimensions": [ { "kind": "signal", "signal_ref": { "scope": "data_provider", "data_provider_domain": "weather-data.example", "signal_id": "weather" }, "signal_value": null, "presence": "absent" } ], "metrics": { "impressions": { "mid": 280000 }, "coverage_rate": { "mid": 0.28 } } }, { "label": "hot", "dimensions": [ { "kind": "signal", "signal_ref": { "scope": "data_provider", "data_provider_domain": "weather-data.example", "signal_id": "weather" }, "signal_value": "hot", "presence": "present" } ], "metrics": { "impressions": { "mid": 180000 }, "coverage_rate": { "mid": 0.18 } } } ] }, "deployments": [ { "type": "agent", "agent_url": "string", "account": "string", "is_live": "boolean", "activation_key": { "type": "segment_id", "segment_id": "string" }, "estimated_activation_duration_minutes": "number" } ], "pricing_options": [ { "pricing_option_id": "string", "model": "cpm | percent_of_media | flat_fee | per_unit | custom", "...": "..." } ] } ] } ``` `get_signals` はディスカバリーと可用性のサーフェスであり、正規のシグナル定義から すべてのフィールドをインライン化することを要求するものではありません。広範な検索結果や ホールセールフィードのページでは、シグナルエージェントは各リスティングをコンパクトに保ち、 安定した参照と、大きな定義リソース向けのキャッシュ可能な開示ポインターを使うべきです(SHOULD)。 バイヤーは `signal_ref` からプロバイダー公開の定義を参照解決できます。プロバイダーの `/.well-known/adagents.json` を取得し(存在する場合は `authoritative_location` に従う)、 一致する `signals[].id` を選択します。取得した `adagents.json` ドキュメントは、解決された authoritative URL に加え、`catalog_etag`、HTTP `ETag` / `Last-Modified`、または上限付き TTL で キャッシュし、そのキャッシュされたドキュメント内で `signal_ref.signal_id` を解決します。 `taxonomy.etag` は、独自の鮮度バリデーターを持つタクソノミードキュメントについてのみ使います。 これらは既存のプロバイダーファイルおよびタクソノミーのバリデーターを再利用します。`get_signals` は別個のシグナル定義バリデーターを定義せず、クライアントはシグナル定義キャッシュを バリデーターのみでキー付けしてはなりません。 ### 定義フィールドのインクルージョン 同じ呼び出しでよりリッチなレビューコンテキストが必要なバイヤーは `fields` を設定できます。これは 別個の検索タスクを導入するのではなく、`get_products.fields` と同じレスポンス投影パターンを使います。 必須の識別・有効化フィールドは、レスポンススキーマが要求する場合は常に含まれます。追加の値は、 `taxonomy`、`data_sources`、`methodology`、`segmentation_criteria`、`criteria_url`、 `refresh_cadence`、`lookback_window`、`onboarder`、`modeling`、`audience_expansion`、 `device_expansion`、`countries`、`consent_basis`、`restricted_attributes`、`policy_categories`、 `art9_basis`、`data_subject_rights` などの、任意のリスティングフィールドやリッチな定義メタデータを インラインで要求します。 別のプロバイダーのシグナルについて `consent_basis` や `art9_basis` が投影される場合、その値は プロバイダーが宣言したシグナル定義の姿勢のままです。セラーや連携エージェントが、プロバイダー宣言の 根拠を自身の処理根拠で置き換えてはなりません(MUST NOT)。 エージェントは、フィールドが利用可能なとき、厳密な検索、絞り込み、小規模なカスタムシグナル結果 セットについて、要求されたフィールドを尊重すべきです(SHOULD)。広範なディスカバリーやホールセール ページでは、要求されたフィールドをインライン化するとページが大きくなりすぎる場合、エージェントは プロバイダー公開の定義、タクソノミードキュメント、基準ページ、開示 URL へのポインターを含む コンパクトなリスティングを返してもかまいません(MAY)。これにより、静的シグナルは `adagents.json` を通じてキャッシュ可能なまま、カスタムや brief 固有のシグナルは有用な場合により深いインライン コンテキストを返せます。 ### フィールド説明 * **signals**: 一致するシグナルの配列 * **signal\_ref**: 正規のシグナル参照。adagents.json の `signals[]` を通じて解決されるシグナルには `scope: "data_provider"` を、adagents.json の `signals[]` に公開されていないソースネイティブなシグナルには `scope: "signal_source"` を、プロダクトコンテキストのレスポンスでのみ `scope: "product"` を使います。新しいレスポンスはこのフィールドを含めるべきです(SHOULD)。 * **signal\_id**: 非推奨のレガシー SignalId オブジェクト。新しいクライアントは `signal_ref` を読むべきです。移行期間中、古いレスポンスは `signal_id` のみを含むことがあります。 * **signal\_agent\_segment\_id**: このシグナルソースが発行する不透明なシグナルハンドル。`activate_signal` にはそのまま使い、グローバルに移植可能なシグナル ID として扱わないでください。パッケージレベルの `signal_targeting_groups` では、`signal_ref` が購入時のアイデンティティであり、このハンドルは選択したプロダクトオプションが別個の実行ハンドルとして公開する場合にのみエコーされます。 * **name**: 人間可読なシグナル名 * **description**: 詳細なシグナル説明 * **signal\_type**: シグナルのタイプ。次のいずれか: * `marketplace` — 再販されるサードパーティセグメント(プロバイダーの `adagents.json` でプロバイダー認可を検証可能) * `owned` — シグナルソースが直接所有するデータから導出されるファーストパーティセグメント * `custom` — モデル、コンポジット、バイヤー入力からオンデマンドで構築されるソースネイティブなセグメント(既存の上流プロバイダーに帰属しない) * **data\_provider**: 該当する場合の人間可読なソース名。`scope: "data_provider"` のシグナルではこれがデータプロバイダーです。`scope: "signal_source"` のシグナルではシグナルソースや独自の出所を示すことがあります。 * **coverage\_percentage**: 任意の非推奨レガシースカラー。オーディエンスカバレッジのパーセンテージ。`coverage_forecast` を消費しないクライアントのフォールバックとしてのみ使います。**coverage\_forecast** が存在する場合、シグナルレベルのディスカバリーでは `coverage_forecast` が正規であり、このスカラーはフォールバック専用です。`coverage_forecast` が同じ分母で absent バケットを含む場合、`coverage_percentage` は `100 * (1 - absent coverage_rate.mid)` に整合すべきです。 * **coverage\_forecast**: シグナルの、任意のフォーキャスト形状の可用性ガイダンス。`scope` が分母を宣言し、`bucket_semantics` が返される値バケットが `exclusive` か `overlapping` かを宣言し、`bucket_completeness` が返されるバケットが完全な分母分割か部分的ヒストグラムかを宣言します。各ポイントは、正規の `signal_ref` を持つ `kind: "signal"` ディメンション、任意の存在値には `presence: "present"`、特定の値バケットには `presence: "present"` に加えて `signal_value`、非存在バケットには `presence: "absent"` に加えて `signal_value: null` を使えます。`metrics.coverage_rate` は宣言されたスコープの 0.0〜1.0 の割合です。 * **deployments**: デスティネーションデプロイメントの配列 * **agent\_url**: デスティネーションエージェントを識別する URL * **account**: 該当する場合のアカウント ID * **is\_live**: このデプロイメントでシグナルが現在アクティブかどうか * **activation\_key**: ターゲティングに使うキー(下記 Activation Key 参照)。**`is_live=true` かつ認証済みの呼び出し元がこのデプロイメントにアクセスできる場合にのみ含まれます。** * **estimated\_activation\_duration\_minutes**: ライブでない場合の有効化所要時間 * **pricing\_options**: 増分価格を持つシグナルの料金オプションの配列。選択した `pricing_option_id` を `report_usage` またはパッケージレベルの `signal_targeting_groups` に渡して課金検証に使います。価格が呼び出し元に利用できない、デスティネーションプロダクトにバンドルされている、または増分コストがない場合は省略されます。 * **pricing\_option\_id**: この料金オプションの一意識別子 * **model**: 課金モデル — `cpm`、`percent_of_media`、`flat_fee`、`per_unit`、または `custom` * `model: "cpm"` — `cpm`(数値、インプレッション 1000 回あたりのコスト)、`currency`(ISO 4217) * `model: "percent_of_media"` — `percent`(0〜100)、`currency`(ISO 4217)、`max_cpm`(任意の CPM 上限: 実効課金 = `min(percent × media_spend_per_mille, max_cpm)`) * `model: "flat_fee"` — `amount`(固定料金)、`currency`(ISO 4217)、`period`(`monthly`、`quarterly`、`annual`、または `campaign`) * `model: "per_unit"` — `unit`(何をカウントするか)、`unit_price`(1 単位あたりのコスト)、`currency`(ISO 4217) * `model: "custom"` — `description`(人間可読)、`metadata`(構造化パラメーター)、`currency`(任意)。パフォーマンスキッカー、段階的ボリューム、ハイブリッド式、または標準モデルで表現できない任意の構成のためのエスケープハッチ。バイヤーはコミットメント前にカスタム価格をオペレーターレビューに通すべきです(SHOULD)。 課金モデルに合った料金オプションを選択します。直接のシグナル有効化では、その `pricing_option_id` を `report_usage` に渡して課金検証に使います。メディアプロダクトで選択されるセラー提供シグナルでは、パッケージレベルの `targeting_overlay.signal_targeting_groups.groups[].signals[].pricing_option_id` に渡します。シグナルが複数のモデル(例: CPM とフラットフィー)を提供する場合、予想される配信ボリュームとキャンペーン構造に基づいて選びます。 ### レスポンスメタデータ | Field | Type | Description | | ------------------------ | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `wholesale_feed_version` | string | このレスポンスの構成に使われたホールセールシグナルフィード状態のバージョンを表す不透明トークン。不透明として扱います — フォーマットなし、順序なし、検査なし。条件付きフェッチを実装するエージェントはすべてのレスポンスで返します。[ホールセールフィードバージョニング](#ホールセールフィードバージョニング) を参照。 | | `pricing_version` | string | 任意のより細粒度のトークン。`wholesale_feed_version` が構造/メタデータについてのみ変わるのに対し、価格が動くと変わります。これらを分離しないエージェントは `pricing_version` を省略してもかまいません(MAY)。 | | `cache_scope` | string | `"public"` または `"account"`。**すべてのレスポンスで必須**(スキーマで強制 — 2 層キャッシュの安全性はこれに依存します)。リクエストに `account` がなかった場合は `"public"` でなければなりません(MUST)。リクエストに `account` があった場合、エージェントは `"public"`(アカウント価格がレートカードと同じ — 呼び出し元が重複排除)または `"account"`(アカウント固有のオーバーライド)のいずれかを宣言します。[キャッシュレイヤリング](#キャッシュレイヤリング) を参照。 | | `unchanged` | boolean | リクエストが呼び出し元の `cache_scope` に対するエージェントの現在のバージョンに一致する `if_wholesale_feed_version`(および/または `if_pricing_version`)を運んだ場合にのみ、存在して `true` になります。その場合 `signals[]` は省略しなければならず(MUST)、`wholesale_feed_version`、`cache_scope`、(使用時は)`pricing_version` は依然としてエコーしなければなりません(MUST)。エージェントは `unchanged: false` を送出してはなりません(MUST NOT)— フィールドの不在が「レスポンスがシグナルを運ぶ」というシグナルです(状態ごとに 1 つの形状)。`unchanged: true` を受け取った呼び出し元は、ローカルのホールセールシグナルミラーを変更してはなりません(MUST NOT)。 | | `incomplete` | IncompleteEntry\[] | 呼び出し元の `time_budget` 内、または内部制限のために完了できなかったものを宣言します。[incomplete 配列](#incomplete-配列) を参照。 | | `pagination` | PaginationResponse | `has_more`、`cursor`、任意の `total_count`。ホールセールモードでは必須。 | ### Activation Key オブジェクト アクティベーションキーは、デスティネーションターゲットでのシグナルの使い方を表します。セグメント ID かキー/バリューのいずれかです。 **セグメント ID 形式:** ```json theme={null} { "type": "segment_id", "segment_id": "ttd_segment_12345" } ``` **キー/バリュー形式:** ```json theme={null} { "type": "key_value", "key": "audience_segment", "value": "luxury_auto_intenders" } ``` ## プロトコル別の例 AdCP のペイロードはプロトコル間で同一で、リクエスト/レスポンスのラッパーのみ異なります。 ### MCP リクエスト - 営業エージェントがシグナルを問い合わせる 営業エージェントがシグナルを問い合わせます。認証済みの呼び出し元が wonderstruck.salesagents.com であるため、シグナルエージェントはレスポンスにアクティベーションキーを含めます。 ```json theme={null} { "tool": "get_signals", "arguments": { "signal_spec": "High-income households interested in luxury goods", "destinations": [ { "type": "agent", "agent_url": "https://wonderstruck.salesagents.com" } ], "countries": ["US"], "filters": { "max_cpm": 5.0, "catalog_types": ["marketplace"] }, "pagination": { "max_results": 5 } } } ``` ### MCP レスポンス - Activation Key 付き 認証済みの呼び出し元がデプロイメントターゲットに一致するため、レスポンスにアクティベーションキーが含まれます。 ```json theme={null} { "$schema": "/schemas/signals/get-signals-response.json", "status": "completed", "message": "Found 1 luxury segment matching your criteria. Already activated for your sales agent.", "context_id": "ctx-signals-123", "cache_scope": "public", "wholesale_feed_version": "sig_v2026-05-18T08:00:00Z-public-rev12", "signals": [ { "signal_ref": { "scope": "data_provider", "data_provider_domain": "pinnacle-data.example", "signal_id": "luxury_auto_intenders" }, "signal_agent_segment_id": "luxury_auto_intenders", "name": "Luxury Automotive Intenders", "description": "High-income individuals researching luxury vehicles", "signal_type": "marketplace", "data_provider": "Pinnacle Data", "coverage_forecast": { "method": "estimate", "forecast_range_unit": "availability", "scope": { "kind": "inventory", "label": "eligible destination inventory" }, "bucket_semantics": "exclusive", "bucket_completeness": "partial", "points": [ { "label": "present", "dimensions": [ { "kind": "signal", "signal_ref": { "scope": "data_provider", "data_provider_domain": "pinnacle-data.example", "signal_id": "luxury_auto_intenders" }, "presence": "present" } ], "metrics": { "coverage_rate": { "mid": 0.12 } } } ] }, "deployments": [ { "type": "agent", "agent_url": "https://wonderstruck.salesagents.com", "is_live": true, "activation_key": { "type": "key_value", "key": "audience_segment", "value": "luxury_auto_intenders_v2" } } ], "pricing_options": [ { "pricing_option_id": "po_cpm_usd", "model": "cpm", "cpm": 3.50, "currency": "USD" } ] } ] } ``` ### MCP レスポンス - 複数の料金オプション 一部のシグナルは複数の課金モデルを提供します。バイヤーは 1 つを選択し、直接のシグナル利用では `report_usage` に、シグナルがメディアバイで選択される場合はパッケージレベルの `signal_targeting_groups` に、その `pricing_option_id` を渡します。 ```json theme={null} { "$schema": "/schemas/signals/get-signals-response.json", "status": "completed", "message": "Found 1 segment matching your criteria. Three pricing options are available: CPM at $3.50, 15% of media spend, or $5,000/month flat fee.", "context_id": "ctx-signals-456", "cache_scope": "public", "wholesale_feed_version": "sig_v2026-05-18T08:00:00Z-public-rev12", "signals": [ { "signal_ref": { "scope": "data_provider", "data_provider_domain": "acmedata.com", "signal_id": "eco_conscious_shoppers" }, "signal_agent_segment_id": "eco_conscious_shoppers", "name": "Eco-Conscious Shoppers", "description": "Users with demonstrated interest in sustainable and eco-friendly products", "signal_type": "marketplace", "data_provider": "Acme Data", "coverage_percentage": 18, "deployments": [ { "type": "agent", "agent_url": "https://wonderstruck.salesagents.com", "is_live": true, "activation_key": { "type": "segment_id", "segment_id": "eco_seg_789" } } ], "pricing_options": [ { "pricing_option_id": "po_eco_cpm", "model": "cpm", "cpm": 3.50, "currency": "USD" }, { "pricing_option_id": "po_eco_pom", "model": "percent_of_media", "percent": 15, "max_cpm": 1.50, "currency": "USD" }, { "pricing_option_id": "po_eco_flat", "model": "flat_fee", "amount": 5000, "period": "monthly", "currency": "USD" } ] } ] } ``` ### MCP リクエスト - バイヤーが複数 DSP を確認 バイヤーが複数の DSP プラットフォームで可用性を確認します。 ```json theme={null} { "tool": "get_signals", "arguments": { "signal_spec": "High-income households interested in luxury goods", "destinations": [ { "type": "platform", "platform": "the-trade-desk", "account": "agency-123" }, { "type": "platform", "platform": "amazon-dsp" } ], "countries": ["US"], "filters": { "max_cpm": 5.0, "catalog_types": ["marketplace"] }, "pagination": { "max_results": 5 } } } ``` ### MCP レスポンス - マルチプラットフォームアクセスを持つバイヤー The Trade Desk と Amazon DSP の両方の認証情報を持つバイヤーは、両プラットフォームのキーを受け取ります。 ```json theme={null} { "$schema": "/schemas/signals/get-signals-response.json", "status": "completed", "message": "Found 1 luxury segment matching your criteria. Already activated on The Trade Desk, pending activation on Amazon DSP.", "context_id": "ctx-signals-123", "cache_scope": "public", "wholesale_feed_version": "sig_v2026-05-18T08:00:00Z-public-rev12", "signals": [ { "signal_ref": { "scope": "data_provider", "data_provider_domain": "experian.com", "signal_id": "luxury_auto_intenders" }, "signal_agent_segment_id": "luxury_auto_intenders", "name": "Luxury Automotive Intenders", "description": "High-income individuals researching luxury vehicles", "signal_type": "marketplace", "data_provider": "Experian", "coverage_percentage": 12, "deployments": [ { "type": "platform", "platform": "the-trade-desk", "account": "agency-123", "is_live": true, "activation_key": { "type": "segment_id", "segment_id": "ttd_agency123_exp_lux_auto" } }, { "type": "platform", "platform": "amazon-dsp", "is_live": false, "estimated_activation_duration_minutes": 60 } ], "pricing_options": [ { "pricing_option_id": "po_cpm_usd", "model": "cpm", "cpm": 3.50, "currency": "USD" } ] } ] } ``` ### A2A リクエスト #### 自然言語での呼び出し ```javascript theme={null} await a2a.send({ message: { parts: [{ kind: "text", text: "Find me signals for high-income households interested in luxury goods that can be deployed on The Trade Desk and Amazon DSP in the US, with a maximum CPM of $5.00." }] } }); ``` #### 明示的なスキル呼び出し ```javascript theme={null} await a2a.send({ message: { parts: [{ kind: "data", data: { skill: "get_signals", parameters: { signal_spec: "High-income households interested in luxury goods", destinations: [ { type: "agent", agent_url: "https://thetradedesk.com", account: "agency-123" }, { type: "agent", agent_url: "https://advertising.amazon.com/dsp" } ], countries: ["US"], filters: { max_cpm: 5.0, catalog_types: ["marketplace"] }, pagination: { max_results: 5 } } } }] } }); ``` ### A2A レスポンス A2A は同じデータ構造でアーティファクトとして結果を返します。 ```json theme={null} { "artifacts": [{ "artifactId": "artifact-signal-discovery-def456", "name": "signal_discovery_result", "parts": [ { "kind": "text", "text": "Found 1 luxury segment matching your criteria. Available on The Trade Desk, pending activation on Amazon DSP." }, { "kind": "data", "data": { "context_id": "ctx-signals-123", "signals": [ { "signal_ref": { "scope": "data_provider", "data_provider_domain": "experian.com", "signal_id": "luxury_auto_intenders" }, "signal_agent_segment_id": "luxury_auto_intenders", "name": "Luxury Automotive Intenders", "description": "High-income individuals researching luxury vehicles", "signal_type": "marketplace", "data_provider": "Experian", "coverage_percentage": 12, "deployments": [ { "type": "agent", "agent_url": "https://thetradedesk.com", "account": "agency-123", "is_live": true }, { "type": "agent", "agent_url": "https://advertising.amazon.com/dsp", "is_live": false, "estimated_activation_duration_minutes": 60 } ], "pricing_options": [ { "pricing_option_id": "po_cpm_usd", "model": "cpm", "cpm": 3.50, "currency": "USD" } ] } ] } } ] }] } ``` ### プロトコルトランスポート * **MCP**: 引数を伴う直接のツール呼び出し。完全なレスポンスを平坦な JSON として返却 * **A2A**: 入力を伴うスキル呼び出し。message とデータを分離した構造化アーティファクトを返却 * **データの一貫性**: 両プロトコルとも同一の AdCP データ構造とバージョン情報を含みます ## シナリオ ### すべてのプラットフォームを探索 プラットフォーム横断で利用可能なすべてのデプロイメントを発見します。 ```json theme={null} { "$schema": "/schemas/signals/get-signals-request.json", "signal_spec": "Contextual segments for luxury automotive content", "destinations": [ { "type": "platform", "platform": "index-exchange", "account": "agency-123-ix" }, { "type": "platform", "platform": "openx" }, { "type": "platform", "platform": "pubmatic", "account": "brand-456-pm" } ], "countries": ["US"], "filters": { "data_providers": ["Peer39"], "catalog_types": ["marketplace"] } } ``` ### レスポンス **Message**: "Found luxury automotive contextual segment from Peer39 with 15% coverage. Live on Index Exchange and OpenX, pending activation on Pubmatic." **ペイロード**: ```json theme={null} { "$schema": "/schemas/signals/get-signals-response.json", "status": "completed", "cache_scope": "public", "wholesale_feed_version": "sig_v2026-05-18T08:00:00Z-public-rev12", "signals": [{ "signal_ref": { "scope": "data_provider", "data_provider_domain": "peer39.com", "signal_id": "peer39_luxury_auto" }, "signal_agent_segment_id": "peer39_luxury_auto", "name": "Luxury Automotive Context", "description": "Pages with luxury automotive content and high viewability", "signal_type": "marketplace", "data_provider": "Peer39", "coverage_percentage": 15, "deployments": [ { "type": "platform", "platform": "index-exchange", "account": "agency-123-ix", "is_live": true, "activation_key": { "type": "segment_id", "segment_id": "ix_agency123_peer39_lux_auto" } }, { "type": "platform", "platform": "index-exchange", "is_live": true, "activation_key": { "type": "segment_id", "segment_id": "ix_peer39_luxury_auto_gen" } }, { "type": "platform", "platform": "openx", "is_live": true, "activation_key": { "type": "segment_id", "segment_id": "ox_peer39_lux_auto_456" } }, { "type": "platform", "platform": "pubmatic", "account": "brand-456-pm", "is_live": false, "estimated_activation_duration_minutes": 60 } ], "pricing_options": [ { "pricing_option_id": "po_cpm_usd", "model": "cpm", "cpm": 2.50, "currency": "USD" } ] }] } ``` ### レスポンスフィールド * **context\_id** (string): セッション永続化用のコンテキスト識別子 * **signals** (array): 一致するシグナルの配列 * **signal\_agent\_segment\_id** (string): このシグナルソースが発行する不透明なシグナルハンドル。`activate_signal` にはそのまま使い、グローバルに移植可能なシグナル ID として扱わないでください。メディアバイのシグナルグループでは、`signal_ref` が購入時のアイデンティティであり、このハンドルは選択したプロダクトオプションが別個の実行ハンドルとして公開する場合にのみエコーされます。 * **name** (string): 人間可読なシグナル名 * **description** (string): 詳細なシグナル説明 * **signal\_type** (string): `marketplace`(再販サードパーティ)、`owned`(シグナルソースのファーストパーティデータ)、または `custom`(オンデマンド構築のソースネイティブセグメント) * **data\_provider** (string, optional): 該当する場合の人間可読なソース/プロバイダー名 * **coverage\_percentage** (number, optional, deprecated): レガシースカラーの推定リーチパーセンテージ。`coverage_forecast` が無いかクライアントがサポートしない場合のフォールバックとしてのみ使います。 * **coverage\_forecast** (object, optional): 明示的な分母と可用性ポイントを持つフォーキャスト形状のカバレッジ内訳。「任意の存在値」の集約バケットには `presence: "present"` と `signal_value` の省略を使い、行が特定の値のためのものである場合にのみ `signal_value` を追加します。返されるバケットが重複しない場合は `bucket_semantics: "exclusive"` を、複数値シグナルにより返されるレートの合計が 1.0 を超えうる場合は `"overlapping"` を使います。返されるバケットが宣言された分母をカバーする場合にのみ `bucket_completeness: "complete"` を使い、そうでなければ `"partial"` を使います。バイヤーは省略されたシェアを、未開示・その他・非サポートのバケットとして扱わなければなりません。 `coverage_forecast` をシグナルファーストのディスカバリーの正規フィールドとして使います:「このシグナルまたはその値はどれだけのインベントリを持つか?」。`kind: "signal"` ディメンションを持つプロダクトまたはプロポーザルのフォーキャストは、プロダクト固有のプランニングの正規サーフェスとして使います:「このシグナルはこのプロダクトのベースライン可用性をどう制約するか?」。バケット固有のフィルタリングは現状クライアント側であり、`min_coverage_percentage` のようなリクエストフィルターは依然としてレガシースカラーを使います。 * **deployments** (array): プラットフォーム固有のデプロイメント情報 * **platform** (string): ターゲットプラットフォーム名 * **account** (string, nullable): アカウント固有の場合の特定アカウント * **is\_live** (boolean): シグナルが現在アクティブかどうか * **activation\_key** (object): ターゲティングに使うキー。`is_live=true` かつ呼び出し元がアクセスできる場合にのみ含まれます。上記 Activation Key オブジェクトを参照。 * **estimated\_activation\_duration\_minutes** (number, optional): ライブでない場合の有効化所要時間 * **pricing\_options** (array): 増分価格を持つシグナルの利用可能な料金オプションの配列。1 つを選択し、その `pricing_option_id` を `report_usage` またはパッケージレベルの `signal_targeting_groups` に渡します。 * **pricing\_option\_id** (string): この料金オプションの一意識別子 * **model** (string): 課金モデル — `cpm`、`percent_of_media`、`flat_fee`、`per_unit`、または `custom` ## エラーコード ### ディスカバリーエラー * `REFERENCE_NOT_FOUND`: 参照された `signal_agent_segment_id` が存在しないか、 プライベートなシグナルエージェントがこのアカウントから見えません。リソースが存在するが 未認可であっても、真に存在しなくても、同じコードが返されます — セラーは両者を区別しては なりません(MUST NOT。どの型付きパラメーターの解決に失敗したかは `error.field` を参照)。 error-handling.mdx の uniform-response の MUST を参照。 * `AGENT_ACCESS_DENIED`: 認証済みエージェントの認証情報がこのシグナルエージェントへのアクセスを認可しませんでした ### ディスカバリー警告 * `PRICING_UNAVAILABLE`: 1 つ以上のプラットフォームで価格データが一時的に利用不可 * `PARTIAL_COVERAGE`: 一部の要求されたプラットフォームがこのシグナルタイプをサポートしません * `STALE_DATA`: プロバイダーのリフレッシュ遅延により一部のシグナルメタデータが古い可能性があります ## 利用上の注意 1. **認証ベースのキー**: アクティベーションキーは、認証済みの呼び出し元がいずれかのデプロイメントターゲットに一致する場合にのみ返されます 2. **権限セキュリティ**: シグナルエージェントは、リクエストフラグではなく呼び出し元の識別に基づいてキーの付与を決定します 3. **デプロイメントステータス**: `is_live` を確認して有効化が必要かを判断します 4. **複数デプロイメント**: 複数のデプロイメントターゲットを問い合わせてプラットフォーム横断の可用性を確認します 5. **有効化が必要な場合**: `is_live` が false の場合は `activate_signal` タスクを使います 6. **message フィールド**: 最も関連性の高い発見の簡潔な要約を提供します ## メディアプロダクト上のセラー提供シグナル ターゲティングシグナルを所有または適用する認可を持つ営業エージェントは、`get_products` を通じて購入時のプロダクト適格性を公開します。バイヤーがパッケージ選択前にディスカバリーや有効化を必要とする場合、`get_signals` を通じてより広範なクロスプロダクトのシグナルフィードを公開することもできます。 * `get_products` は、プロダクトがパッケージレベルのシグナルターゲティングを持つかを宣言します。`products[].included_signals` は、プロダクトにすでにバンドルまたは計画された選択不可のシグナルを記述します。インラインの `products[].signal_targeting_options` は、プロダクト固有のメニュー、価格、有効化ハンドル、デフォルト、グルーピングヒント、または brief/refine で選択されたサブセットを運べます。ホールセールプロダクトはインラインオプションを省略し、`get_signals` を選択可能なシグナルフィードとして使えます。 * `get_signals` は任意で、`signal_ref`、`signal_agent_segment_id`、値メタデータ、および任意のデフォルトまたはアカウントスコープの `pricing_options` を含むクロスプロダクトのシグナルメタデータを返します。 * `create_media_buy` は、`packages[].targeting_overlay.signal_targeting_groups` でパッケージのシグナルを選択し、選択したシグナルの `pricing_option_id`、`signal_ref`、および必要な場合は別個のセラー実行ハンドルを運びます。 これにより、プロダクトが大きなホールセールシグナルフィードを複製することを強いずに、バイヤーにプロダクトファーストの購入時適格性パスを与えます。存在する場合は選択したプロダクトのインライン `signal_targeting_options` を、`signal_targeting_rules` を使ってそのパッケージで何が適用可能かを判断します。プロダクトがインラインオプションを省略するがシグナルターゲティングを許可する場合、候補ディスカバリーと有効化メタデータには `get_signals` を使い、次に `get_products.filters.signal_targeting` またはプロダクト固有の `get_products` クエリを使って、`create_media_buy` を呼ぶ前に、意図したプロダクトについて候補セットが選択可能で共同構成可能であることを確認します。両サーフェスが価格を含む場合、そのプロダクトについてはプロダクトスコープの `signal_targeting_options[].pricing_options` の価格が正規です。両サーフェスで公開されるプロダクトローカルなシグナルについては、プロダクトオプションの `signal_ref.signal_id` が、同じシグナルについてセラーの `get_signals.signals[].signal_ref.signal_id` と一致しなければなりません(MUST)。`included_signals` は説明のためだけのもので、シグナルを選択可能にはしません。 メディアバイのプロダクトターゲティングでは、プロダクトオプションとパッケージグループは同じ `signal_ref` アイデンティティを使います。プロダクトローカルなシグナルオプションには `scope: "product"`、公開された adagents.json の `signals[]` で定義されるシグナルには `data_provider_domain` を伴う `scope: "data_provider"`、adagents.json の `signals[]` に公開されないソースネイティブなカスタムシグナルには `scope: "signal_source"` を使います。プロバイダー公開のシグナルが選択される場合、バイヤーは、シグナル ID またはタグについてプロバイダーの `adagents.json` の `authorized_agents` ルールを確認することで、セラーの認可を検証できます。古い Signals プロトコルの `signal_id.source` 形状は非推奨で、後方互換性のためにのみ保持されます。 ## ホールセールシグナルフィード コンシューマー(ストアフロント、連携マーケットプレイス、レジストリ)がシグナルエージェントの完全な価格付きシグナルフィードをミラーする必要がある場合、`discovery_mode: "wholesale"` を設定し、`signal_spec` / `signal_refs` / 非推奨の `signal_ids` を省略します。呼び出し元が 3.1+ のシグナルサーフェスやホールセールサポートを前提にできない場合、まず `get_adcp_capabilities.signals.discovery_modes` に `"wholesale"` があるか確認します。ホールセール列挙は [`get_products` の `buying_mode: "wholesale"`](/docs/media-buy/task-reference/get_products) と対称です。同期的、ページネーションあり、確定価格で、部分的完了は `incomplete[]` で宣言されます。 ### リクエスト ```json theme={null} { "$schema": "/schemas/signals/get-signals-request.json", "discovery_mode": "wholesale", "account": { "account_id": "acct_123" }, "filters": { "catalog_types": ["marketplace", "owned"], "data_providers": ["acme-data", "nova-insights"] }, "pagination": { "max_results": 50 } } ``` ### レスポンス ```json theme={null} { "$schema": "/schemas/signals/get-signals-response.json", "status": "completed", "message": "Returning 50 of 312 signals in wholesale mode.", "context_id": "ctx-wholesale-001", "cache_scope": "public", "wholesale_feed_version": "v2026-05-18T08:00:00Z-acme-rev412", "signals": [ { "signal_ref": { "scope": "data_provider", "data_provider_domain": "acme-data.com", "signal_id": "luxury_auto_intenders" }, "signal_agent_segment_id": "sigagent_seg_4421", "name": "Luxury Auto Intenders", "description": "Households researching premium vehicles in the last 30 days.", "signal_type": "marketplace", "data_provider": "Acme Data", "coverage_percentage": 18.4, "deployments": [ { "type": "platform", "platform": "the-trade-desk", "is_live": true, "activation_key": { "type": "segment_id", "segment_id": "ttd_seg_99821" } } ], "pricing_options": [ { "pricing_option_id": "po_cpm_1", "model": "cpm", "cpm": 2.50, "currency": "USD" } ] } ], "pagination": { "has_more": true, "cursor": "eyJvIjo1MH0=", "total_count": 312 } } ``` ### 認可と来歴 マーケットプレイスシグナル(`signal_type: "marketplace"`)は、上流のデータプロバイダーに帰属し続けます。ホールセール列挙は来歴を潰しません。 * 各マーケットプレイスシグナルは `data_provider` を運びます。 * コンシューマーは、プロバイダーの `adagents.json` を通じてプロバイダー認可を検証すべきです(SHOULD)— そのシグナルクラスについて、シグナルエージェントの URL がプロバイダーの認可リストに現れなければなりません。 * ストアフロントやレジストリは、ホールセール列挙に加えて `adagents.json` の相互参照を使い、データパブリッシャーのシグナルビューを実体化してもかまいません(MAY): 既知の各データプロバイダーについて、認可されたシグナルエージェント経由で利用可能なシグナルの集合を価格付きで。 ### 価格 エージェントが宣言すべき確定したスタンドアロンシグナル価格を持つ場合、認証済みの呼び出し元について `pricing_options[]` を投入しなければなりません(MUST)。`account` が省略された場合、エージェントはデフォルトのレートカード価格を返すか、`pricing_options[]` を省略します(その場合、呼び出し元は構成前に `account` で再クエリするか、`get_products` のプロダクトスコープ価格を使わなければなりません(MUST))。未認証の呼び出し元は価格なしでシグナルメタデータを受け取ってもかまいません(MAY)。メディアプロダクトにバンドルされている、または増分コストを持たないシグナルは `pricing_options[]` を省略してもかまいません(MAY)。 ### 機能宣言 シグナルエージェントは [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) でホールセールサポートを宣言します。 ```json theme={null} { "signals": { "discovery_modes": ["brief", "wholesale"] } } ``` `"wholesale"` を宣言しないエージェントは、ホールセール呼び出しに対して `INVALID_REQUEST` を返してもかまいません(MAY)。ホールセールディスカバリーは AdCP 3.1+ であるため、呼び出し元が 3.1+ のシグナルサーフェスやホールセールサポートを前提にできない場合、機能宣言が正規のサポートシグナルとなります。呼び出し元はホールセールリクエストを発行する前に探索すべきです(SHOULD)。 ## ホールセールフィードバージョニング ホールセール列挙があっても、エージェントのシグナルフィードをミラーするコンシューマーは、変更を検出するためだけに毎回のポーリングですべてのページネーションされたページを再取得することになります。これを避けるため、`get_signals` はすべてのレスポンスで返される不透明な `wholesale_feed_version` トークンをサポートします。後続の呼び出しで `if_wholesale_feed_version` を通じて渡すと、エージェントは `unchanged: true` でショートサーキットしてもかまいません(MAY)— シグナルペイロードなし、ページごとの差分なし。 これは `get_signals` が返すセラー側のホールセールシグナルフィードです。`sync_catalogs` フィードではありません。`sync_catalogs` はセラーアカウント上のバイヤー提供のキャンペーン入力フィードを管理します。 ### 変更なしレスポンス **リクエスト:** ```json theme={null} { "$schema": "/schemas/signals/get-signals-request.json", "discovery_mode": "wholesale", "if_wholesale_feed_version": "v2026-05-18T08:00:00Z-acme-rev412" } ``` **レスポンス(ホールセールシグナルフィード変更なし):** ```json theme={null} { "$schema": "/schemas/signals/get-signals-response.json", "status": "completed", "message": "Wholesale signals feed unchanged since v2026-05-18T08:00:00Z-acme-rev412.", "context_id": "ctx-abc-789", "unchanged": true, "wholesale_feed_version": "v2026-05-18T08:00:00Z-acme-rev412", "pricing_version": "v2026-05-18T08:00:00Z-acme-rev412", "cache_scope": "public" } ``` `unchanged: true` のとき、`signals[]` は省略しなければならず(MUST)、コンシューマーはローカルのホールセールシグナルミラーを変更してはなりません(MUST NOT)。 ### ホールセールシグナルフィード変更 — 完全ペイロード返却(省略版) ```json test=false theme={null} { "message": "Returning 312 signals (wholesale feed version advanced).", "context_id": "ctx-abc-790", "wholesale_feed_version": "v2026-05-18T10:15:00Z-acme-rev415", "pricing_version": "v2026-05-18T10:15:00Z-acme-rev415", "cache_scope": "public", "signals": [ { "signal_ref": { "scope": "data_provider", "data_provider_domain": "acme-data.com", "signal_id": "luxury_auto_intenders" }, "signal_agent_segment_id": "sigagent_seg_4421", "name": "Luxury Auto Intenders", "description": "Households researching premium vehicles in the last 30 days.", "signal_type": "marketplace", "data_provider": "Acme Data", "coverage_percentage": 18.4, "deployments": [ { "type": "platform", "platform": "the-trade-desk", "is_live": true, "activation_key": { "type": "segment_id", "segment_id": "ttd_seg_99821" } } ], "pricing_options": [ { "pricing_option_id": "po_cpm_1", "model": "cpm", "cpm": 2.50, "currency": "USD" } ] } ], "pagination": { "has_more": true, "cursor": "eyJvIjo1MH0=", "total_count": 312 } } ``` ### ルール * トークンは**不透明**です。フォーマットなし、順序なし、検査なし。 * 返される `wholesale_feed_version` は `cache_scope` を通じてスコープキー付けされます。呼び出し元は、使用した `(account, filters, discovery_mode, destinations, countries)` タプルと共に `(cache_scope, wholesale_feed_version)` のペアをキャッシュします。2 層モデルについては [キャッシュレイヤリング](#キャッシュレイヤリング) を参照。 * `pricing_version` は任意のより細粒度のトークンです。存在する場合、価格が動くと変わりますが、`wholesale_feed_version` は構造/メタデータが動いたときのみ変わります。セグメントメタデータを変えないレートカードスイープで一般的です。 * **`if_pricing_version` には `if_wholesale_feed_version` が必要です。** 価格は独自の構造的ベースラインを持ちません。`if_wholesale_feed_version` なしで `if_pricing_version` を送るのはスキーマレベルのエラーです。エージェントの評価は 2 段階です: ホールセールフィード不一致は完全ペイロードを返します。ホールセールフィード一致で価格不一致も完全ペイロードを返します(呼び出し元が更新された `pricing_options` を確認できるように)。両方一致 → `unchanged: true`。 * **`filters` の正規化。** エージェントは、`wholesale_feed_version` キースペースへのハッシュ化前に `filters` オブジェクトを正規化されたものとして扱わなければなりません(MUST): キーを辞書順にソート、省略されデフォルトの値は同一に扱う、フィルターが集合セマンティクスを持つ場合は配列値をソート(例: `catalog_types`、`data_providers`)。同等だが形状の異なるフィルターオブジェクトを渡す呼び出し元は、同じ `wholesale_feed_version` を受け取らなければなりません(MUST)。キー順やデフォルト省略の違いによる暗黙のミラー陳腐化バグを防ぎます。**前方互換のデフォルト:** 3.x マイナーバージョンで追加される新しいフィルターフィールドは集合か列かのセマンティクスを宣言しなければなりません(MUST)。明示的な宣言がない場合、ルールは**集合セマンティクス**にデフォルトします。 * **ページネーションとの相互作用。** `wholesale_feed_versioning.supported: true` を宣言するエージェントは、(最初だけでなく)すべてのページネーションされたページで `wholesale_feed_version` を返さなければなりません(MUST)。バージョニングを宣言しないエージェントも同様にすべきです(SHOULD)。ページ間でホールセールフィードが変異した場合、新しいバージョンが次のページで現れ、呼び出し元は `cursor: null` からページネーションを再開しなければなりません(MUST)— すでに受け取った部分ページは陳腐化したバージョンを記述しています。 * **`unchanged: true` と進行中のページネーション。** ページネーション途中の呼び出し元は、それまでのページが描かれたバージョンに一致する `if_wholesale_feed_version` を送ってもかまいません(MAY)。エージェントが `unchanged: true` を確認した場合、レスポンスは `signals[]` とページネーションエンベロープを完全に省略し、呼び出し元はそのバージョン下での進行中のウォークを放棄します。エージェントは、アクティブなページネーション内の個々のページをスキップするために条件付きフェッチのショートサーキットを使ってはなりません(MAY NOT)— `unchanged` はフィード対キャッシュバージョンであり、ページ単位ではありません。 * `if_wholesale_feed_version` を無視する v3.1 より前のエージェントは、単に完全ペイロードを返します — 意味的には正しく、非効率なだけです。 条件付きフェッチを超えたプッシュ型の変更追跡については `specs/wholesale-feed-webhooks.md` を参照。ホールセールフィード Webhook は、変更されたシグナルペイロード、価格ペイロード、削除トゥームストーン、または一括変更サマリーを運びます。`get_signals` は修復と再照合の読み取りのままです。 ## キャッシュレイヤリング シグナルエージェントは 2 つの概念的なレイヤーを公開します: **パブリックレイヤー**(レートカード/構造ビュー)と **アカウント別オーバーレイ**(プレミアムバイヤー向けのアカウント固有価格)。条件付きフェッチパスは `cache_scope` を通じてレイヤーを認識します。 **2 層キャッシュ。** | Layer | Cache key | What's stored | | --------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | Public | `(agent, discovery_mode, filters, destinations, countries)` | `wholesale_feed_version_public`、アカウント参照なしで見たホールセールシグナルフィードペイロード | | Account overlay | `(agent, discovery_mode, filters, destinations, countries, account_id)` | `wholesale_feed_version_account`、`cache_scope: "account"` が返されたときのホールセールシグナルフィードペイロード | **挙動。** * `account` なしのリクエストは常に `cache_scope: "public"` を返します。呼び出し元はパブリックキーの下にキャッシュします。 * `account` ありのリクエストは `cache_scope: "public"` または `"account"` を返します(エージェントは宣言しなければなりません(MUST)。デフォルトなし)。 * `"public"`: このアカウントはレートカードで価格付けされます。呼び出し元は重複排除してもかまいません(MAY)— バージョンとペイロードは未認証ビューと同じです。 * `"account"`: このレスポンスはアカウント固有のオーバーライドを運びます。呼び出し元はアカウントオーバーレイキーの下にキャッシュします。 * エージェントはアカウントを `"account"` から `"public"` にダウングレードしてもかまいません(MAY)— 呼び出し元はこれを「このアカウントはもうオーバーライドを持たない」と解釈し、オーバーレイを破棄すべきです(SHOULD)。 **`if_wholesale_feed_version` での条件付きフェッチ。** トークンを、それが返されたスコープと組にして送ります。エージェントはそのスコープの現在のバージョンと比較します。呼び出し元のトークンが `"account"` スコープに属するが、エージェントが `cache_scope: "public"` で応答した場合、それがダウングレードシグナルです。 **Webhook 無効化。** ホールセールフィード Webhook イベントは、`*.priced` と `*.updated` のペイロードで `applies_to.scope` を宣言します。エージェントは、どのサブスクライバーがシグナル Webhook を受け取るかを決める際、`get_signals discovery_mode: "wholesale"` が使うのと同じアカウント/呼び出し元認可述語を適用しなければなりません(MUST)。 * `applies_to: { scope: "public" }` → パブリックレイヤーのキャッシュを無効化。そのパブリックバージョンを参照するすべてのアカウントオーバーレイも陳腐化します。 * `applies_to: { scope: "account", account_ids: [...] }` → 指定されたアカウントのオーバーレイのみを無効化。 * `account_ids` なしの `applies_to: { scope: "account" }` → セラーは影響を受ける集合を秘匿します。サブスクライバー別のスコープフィルターが、プリンシパルが影響を受ける集合にあるサブスクライバーにのみイベントをルーティングします。 完全な Webhook 側の仕様については `specs/wholesale-feed-webhooks.md` の §"Cache layering and event scoping" を参照。 ## incomplete 配列 エージェントが呼び出し元の `time_budget` 内(または内部制限のため)にすべての作業を完了できない場合、レスポンスは `incomplete` — 欠けているものを宣言する配列 — を含みます。呼び出し元は `estimated_wait` を使って、より大きな予算で再試行すべきかを判断できます。 | Field | Type | Required | Description | | ---------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------ | | `scope` | string | Yes | `"signals"`: 一致するすべてのシグナルが返されなかった。`"pricing"`: シグナルは返されたが価格が欠けているか未確認。`"wholesale_feed"`: ホールセールモードで、完全なフィード列挙を完了できなかった。 | | `description` | string | Yes | 何が欠けていてなぜかの人間可読な説明。 | | `estimated_wait` | Duration | No | このスコープを解決するのに追加でどれだけの時間が必要か。 | ## 反復的絞り込み `get_signals` は別個のモードフラグなしで反復的絞り込みをサポートします。`signal_spec` と `signal_refs` の組み合わせが操作を決定します。 | Fields provided | Behavior | | ---------------------------------------------------------------- | ------------------------------------------------------------------------- | | `signal_spec` のみ | ディスカバリー — 説明に一致するシグナルを探す | | `signal_refs` のみ | 厳密検索 — 参照で特定のシグナルを返す | | `signal_refs` + `signal_spec` | 絞り込み — 既知のシグナルから開始し、spec に従って調整 | | `discovery_mode: "wholesale"`(`signal_spec` も `signal_refs` もなし) | ホールセール — エージェントの完全な価格付きシグナルフィードを列挙。[ホールセールシグナルフィード](#ホールセールシグナルフィード) を参照。 | 以前の結果を絞り込むには、保持したいシグナルの `signal_ref` 値を渡し戻し、変更内容を記述する更新された `signal_spec` を提供します。 ```json theme={null} { "$schema": "/schemas/signals/get-signals-request.json", "signal_spec": "Same audience but with broader coverage, ideally above 20%", "signal_refs": [ { "scope": "data_provider", "data_provider_domain": "experian.com", "signal_id": "luxury_auto_intenders" } ], "destinations": [ { "type": "agent", "agent_url": "https://wonderstruck.salesagents.com" } ], "countries": ["US"] } ``` シグナルエージェントは、提供された ID を出発点として、spec を調整ガイダンスとして使い、元の選択と要求された変更の両方を反映したシグナル(例: 同じプロバイダーのより広範なセグメント、またはより高いカバレッジを持つ代替プロバイダーの比較可能なセグメント)を返します。 ### レスポンス - 複数シグナル ```json theme={null} { "message": "I found 3 signals matching your luxury goods criteria. The best option is 'Affluent Shoppers' with 22% coverage, already live across all requested platforms. 'High Income Households' offers broader reach (35%) but requires activation on OpenX. All signals are priced between $2-4 CPM.", "context_id": "ctx-signals-abc123", "signals": [ { "signal_ref": { "scope": "data_provider", "data_provider_domain": "acme-data.com", "signal_id": "affluent_shoppers" }, "signal_agent_segment_id": "acme_affluent_shoppers", "name": "Affluent Shoppers", "description": "Users with demonstrated luxury purchase behavior", "signal_type": "marketplace", "data_provider": "Acme Data", "coverage_percentage": 22, "deployments": [ { "type": "platform", "platform": "index-exchange", "account": "agency-123-ix", "is_live": true, "activation_key": { "type": "segment_id", "segment_id": "ix_agency123_acme_aff_shop" } }, { "type": "platform", "platform": "openx", "account": "agency-123-ox", "is_live": true, "activation_key": { "type": "segment_id", "segment_id": "ox_agency123_affluent_789" } } ], "pricing_options": [ { "pricing_option_id": "po_cpm_usd", "model": "cpm", "cpm": 3.50, "currency": "USD" } ] } // ... more signals ] } ``` ### レスポンス - 警告付き部分成功 ```json theme={null} { "$schema": "/schemas/signals/get-signals-response.json", "status": "completed", "message": "Found 2 luxury signals, but encountered some platform limitations. The 'Premium Auto Shoppers' signal has limited reach due to data restrictions, and pricing data is unavailable for one platform. Review the warnings below for optimization suggestions.", "context_id": "ctx-signals-abc123", "cache_scope": "public", "wholesale_feed_version": "sig_v2026-05-18T08:00:00Z-public-rev12", "signals": [ { "signal_ref": { "scope": "data_provider", "data_provider_domain": "experian.com", "signal_id": "premium_auto_shoppers" }, "signal_agent_segment_id": "premium_auto_shoppers", "name": "Premium Auto Shoppers", "description": "High-value automotive purchase intenders", "signal_type": "marketplace", "data_provider": "Experian", "coverage_percentage": 8, "deployments": [ { "type": "platform", "platform": "the-trade-desk", "is_live": true, "activation_key": { "type": "segment_id", "segment_id": "ttd_exp_auto_premium" } } ], "pricing_options": [ { "pricing_option_id": "po_cpm_usd", "model": "cpm", "cpm": 4.50, "currency": "USD" } ] } ], "errors": [ { "code": "PRICING_UNAVAILABLE", "message": "Pricing data temporarily unavailable for The Trade Desk platform", "field": "signals[0].pricing_options", "suggestion": "Retry in 15-30 minutes when platform pricing feed updates", "details": { "affected_platform": "the-trade-desk", "last_updated": "2025-01-15T12:00:00Z", "retry_after": 1800 } }, { "code": "PRICING_UNAVAILABLE", "message": "Pricing data temporarily unavailable for Amazon DSP", "field": "filters.platforms", "suggestion": "Pricing will be available during activation, or try again later", "details": { "affected_platform": "amazon-dsp", "retry_after": 1800 } } ] } ``` ### レスポンス - 該当なし ```json theme={null} { "$schema": "/schemas/signals/get-signals-response.json", "status": "completed", "message": "I couldn't find any signals matching 'underwater basket weavers' in the requested platforms. This appears to be a very niche audience. Consider broadening your criteria to 'craft enthusiasts' or 'hobby communities' for better results. Alternatively, we could create a custom signal for this specific audience.", "context_id": "ctx-signals-abc123", "cache_scope": "public", "wholesale_feed_version": "sig_v2026-05-18T08:00:00Z-public-rev12", "signals": [] } ``` ## 実装ガイド ### シグナルメッセージ生成 `message` フィールドは実行可能なインサイトを提供すべきです。 ```python theme={null} def generate_signals_message(signals, request): if not signals: return generate_no_signals_message(request.signal_spec) best_signal = find_best_signal(signals) if len(signals) == 1: signal = signals[0] deployment_status = get_deployment_summary(signal, request.destinations) pricing = signal.pricing_options[0] if signal.pricing_options else None if pricing: p = pricing.pricing if p.model == "cpm": price_commentary = f"Priced at ${p.cpm:.2f} CPM {p.currency}." elif p.model == "percent_of_media": cap = f", capped at ${p.max_cpm:.2f} CPM" if getattr(p, "max_cpm", None) else "" price_commentary = f"Priced at {p.percent}% of media spend{cap}." elif p.model == "flat_fee": price_commentary = f"Flat fee of {p.amount} {p.currency} per {p.period}." else: price_commentary = "" else: price_commentary = "" coverage_commentary = f" with {signal.coverage_percentage}% coverage" if getattr(signal, "coverage_percentage", None) is not None else "" return f"I found a perfect match: '{signal.name}' from {signal.data_provider}{coverage_commentary}. {deployment_status} {price_commentary}" else: return f"I found {len(signals)} signals matching your {extract_key_criteria(request.signal_spec)} criteria. {describe_best_option(best_signal)} {get_pricing_range(signals)}." def get_deployment_summary(signal, requested_deployments): live = [d for d in signal.deployments if d.is_live] pending = [d for d in signal.deployments if not d.is_live] if not pending: return "Already live on all requested deployments, ready to use immediately." elif live: activation_time = max((d.estimated_activation_duration_minutes or 0) for d in pending) return f"Live on {len(live)} deployment(s). Activation on {len(pending)} more would take about {activation_time} minutes." else: return "Requires activation on all deployments, which typically takes 1-2 hours." ``` # SI エージェントの実装 Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/implementing-si-agents ブランドが Sponsored Intelligence エージェントを実装するためのガイドです。実装が完了すると、Addy や ChatGPT などの SI ホスト、その他の AI アシスタントからエージェントを呼び出せるようになります。 ## Quick Start SI エージェントは、以下 4 つのタスクを実装する MCP または A2A サーバーです。 1. `si_get_offering` - Respond to offering lookups (details, availability, products) 2. `si_initiate_session` - Start a conversation 3. `si_send_message` - Exchange messages 4. `si_terminate_session` - End the conversation ## Capability Discovery SI エージェントは、標準の AdCP タスク `get_adcp_capabilities` を通じて機能を公開します。ホストがこのタスクを呼び出すと、エージェントは SI の設定を返します。 ```json theme={null} { "adcp": { "major_versions": [2] }, "supported_protocols": ["sponsored_intelligence"], "sponsored_intelligence": { "endpoint": { "transports": [ { "type": "mcp", "url": "https://yourbrand.example/si-agent" } ] }, "capabilities": { "modalities": { "conversational": true, "voice": false, "video": false, "avatar": false }, "components": { "standard": ["text", "link", "image", "product_card", "carousel", "action_button"], "extensions": {} }, "commerce": { "acp_checkout": false } }, "brand_manifest_url": "https://yourbrand.example/.well-known/brand-manifest.json" } } ``` この統一されたディスカバリー手段により、ホストは他の AdCP プロトコルと同様に SI の機能を把握できます。 ## Reference Implementation リファレンス実装は近日公開予定です。公開後は次を示します。 * 4 つすべての SI タスクを MCP ツールとして実装 * タイムアウトを含むセッション管理 * アイデンティティと同意の取り扱い * UI 要素の生成 * ACP へのチェックアウト引き継ぎ ### Task Structure 各 SI タスクは MCP ツールのパターンに従います。 ```typescript theme={null} server.tool( "si_initiate_session", "Start a conversational session with this brand agent", { context: { type: "string", description: "Natural language user intent" }, identity: { type: "object", description: "User identity with consent" }, media_buy_id: { type: "string", description: "AdCP media buy ID (optional)" }, offering_id: { type: "string", description: "Brand-specific offering reference (optional)" }, }, async ({ context, identity, media_buy_id, offering_id }) => { // Your implementation here return { content: [{ type: "text", text: JSON.stringify({ session_id: "sess_abc123", response: { message: "Hello! How can I help?" }, negotiated_capabilities: { /* ... */ } }) }] }; } ); ``` ## 実装で押さえるポイント ### 1. 会話のハンドオフ `si_initiate_session` の `context` フィールドはホストからの引き継ぎメッセージで、ユーザーの意図についてホスト AI がブランドエージェントに伝える内容です。これは会話の流れの一部としてユーザーにも見えます。 たとえば ChatGPT でユーザーが「来週ボストンに飛びたい」と言い、ホストが Delta の SI エージェントに接続すると決めた場合、ハンドオフは次のようになります。 > 「ボストン行きの便を探すために Delta につなぎます。空き状況や提供内容を確認してくれます。」 ブランドエージェントはこのコンテキストを受け取り、会話をそのまま続けるように自然に回答する必要があります。 ```typescript theme={null} // あなたのエージェントは会話で応答する AI です // context がユーザーの要求を教えてくれるので、素直に手助けしてください // 望ましい回答: "こんにちは! ボストン行きの便探しをお手伝いします。 来週のいつ頃をご希望ですか?時間帯の希望はありますか?" // NG 例: 解析結果を機械的に返さない "判定結果: category=flight, destination=Boston, timeframe=next_week" ``` 重要なのは、SI が API 呼び出しではなく会話だという点です。ブランドエージェントはフォーム入力のような機械的な応答ではなく、親切な人と話しているように感じられるべきです。 ### 2. アイデンティティの扱い `consent_granted` が true の場合、実際の PII が渡されます。 ```typescript theme={null} async function handleIdentity(identity: Identity) { if (!identity.consent_granted) { // Anonymous session - can still help, just can't personalize return null; } // Look up existing customer by email const customer = await lookupCustomer(identity.user.email); if (customer) { // Personalize based on history return { name: customer.preferred_name || identity.user.name, loyalty_status: customer.loyalty_tier, preferences: customer.preferences, }; } // New customer - use provided identity return { name: identity.user.name, email: identity.user.email, }; } ``` ### 3. UI 要素 ホストが描画できる構造化データを返します。 ```typescript theme={null} const uiElements = [ // Product card for a specific item { type: "product_card", data: { title: "Premium Widget", subtitle: "Best seller", price: "$99", image_url: "https://...", cta: { label: "Add to Cart", action: "add_to_cart", payload: { sku: "WIDGET-001" } }, }, }, // Carousel for browsing options { type: "carousel", data: { title: "You might also like", items: [ { title: "Option A", price: "$49" }, { title: "Option B", price: "$79" }, ], }, }, // Action button for explicit CTA { type: "action_button", data: { label: "Complete Purchase", action: "checkout", payload: { items: ["WIDGET-001"] }, }, }, ]; ``` ### 4. セッション管理 セッションにはタイムアウトとクリーンアップを設定します。 ```typescript theme={null} const SESSION_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes function cleanupExpiredSessions() { const now = Date.now(); for (const [id, session] of sessions) { if (now - session.last_activity.getTime() > SESSION_TIMEOUT_MS) { sessions.delete(id); } } } // Run cleanup periodically setInterval(cleanupExpiredSessions, 60 * 1000); ``` ### 5. ACP へのハンドオフ ユーザーが購入準備できたら、ハンドオフを知らせます。 ```typescript theme={null} if (userWantsToPurchase) { return { session_status: "pending_handoff", handoff: { type: "transaction", intent: { action: "purchase", product: selectedProduct, price: { amount: 99, currency: "USD" }, }, context_for_checkout: { conversation_summary: "User selected Premium Widget after discussing features", applied_offers: appliedOffers, }, }, }; } ``` ## エンドポイントのテスト ### ローカルテスト MCP Inspector を使ってエンドポイントをテストします。 ```bash theme={null} npx @anthropic-ai/mcp-inspector your-si-agent ``` ### Addy との統合テスト エンドポイントを登録したら、次の手順で確認します。 1. AgenticAdvertising.org の Slack ワークスペースに参加します 2. Addy との会話を開始します 3. 「connect me with \[Your Brand]」と伝える 4. Addy が SI エンドポイントを呼び出します ## SI エージェントの登録 Addy やその他のホストで SI エージェントを利用可能にするには次を実施します。 1. 5 つの SI タスク(`get_adcp_capabilities` + 4 つの SI タスク)を実装します 2. `get_adcp_capabilities` が SI エンドポイントと機能を返すことを確認します 3. AgenticAdvertising.org チームに連絡し、エンドポイントを登録します 4. ステージング環境で統合をテストします ## 次のステップ * 詳細なスキーマ仕様は [タスクリファレンス](./tasks/) を参照してください * コンセプトの理解には [SI プロトコル概要](./overview) を確認してください * 実装に関する議論は [ワーキンググループ](https://join.slack.com/t/agenticads/shared_invite/zt-3c5sxvdjk-x0rVmLB3OFHVUp~WutVWZg) に参加してください # SI ホストの実装 Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/implementing-si-hosts このガイドは、AI プラットフォームが Sponsored Intelligence ホスト機能を実装する際の手助けをします。実装後は、AI アシスタントがブランドエージェントを呼び出し、充実した会話型コマース体験を提供できます。 ## クイックスタート SI ホストが行うべきこと: 1. SI マニフェストを通じてブランドエージェントを **探索** します 2. ハンドオフ前にオファー詳細を **取得** する(任意だが推奨) 3. ブランドエージェントと **ケイパビリティを交渉** します 4. セッションを **管理** する(開始、メッセージ送受信、終了) 5. 標準 UI コンポーネントを **描画** します 6. コマースハンドオフを **処理** します ## アーキテクチャ概要 ```mermaid theme={null} flowchart TB subgraph Platform["Your AI Platform"] CE[Conversation Engine] SM[SI Session Manager] UI[UI Renderer] MCP[MCP Client] CE --> SM SM --> UI CE --> MCP SM --> MCP UI --> MCP end subgraph Brand["Brand Agent (MCP Server)"] T1[si_get_offering] T2[si_initiate_session] T3[si_send_message] T4[si_terminate_session] end MCP --> Brand ``` ## リファレンス実装 リファレンス実装は近日公開予定です。公開され次第、次を示します: * ブランドエージェントへの MCP クライアント接続 * タイムアウト処理を含むセッション管理 * 同意フローの実装 * UI コンポーネントのレンダリング * ACP へのコマースハンドオフ ## 実装時の重要な考慮点 ### 1. 同意フロー ブランドエージェントとユーザーのアイデンティティを共有する前に、明示的な同意を取得する必要があります: 1. ブランドと要求データを示す明確な同意ダイアログを提示します 2. ブランドのプライバシーポリシーへのリンクを表示します 3. 共有するフィールド(氏名、メール、配送先など)をユーザーが選べるようにします 4. アイデンティティオブジェクトに同意のタイムスタンプと範囲を記録します 同意が拒否された場合は、`consent_granted: false` の匿名セッションを作成します。 ### 2. UI コンポーネントのレンダリング ホストは SI プロトコルで定義されたすべての標準 UI コンポーネントをレンダリングする必要があります: | Component | Purpose | Required Fields | | --------------- | --------------- | ----------------- | | `text` | 会話メッセージ | `message` | | `link` | ラベル付き URL | `url`, `label` | | `image` | 単一画像 | `url`, `alt` | | `product_card` | CTA 付きの商品表示 | `title`, `price` | | `carousel` | カード/画像の配列 | `items` | | `action_button` | コールバックを起動する CTA | `label`, `action` | ユーザーが `action_button` をクリックしたら、アクション識別子とペイロードを添えて `si_send_message` で `action_response` を送信します。 ### 3. オファー参照フロー スポンサード結果に推奨されるフロー: 1. **オファー詳細を取得**(匿名) - オファー情報とマッチする商品を取得 2. **オファーをユーザーに提示** - オファー詳細や商品を表示し、接続するかを尋ねる 3. **同意取得** - 接続する場合は同意ダイアログを提示 4. **セッション開始** - 手順 1 のオファートークンを含めます ### 4. コマースハンドオフ セッションが `session_status: "pending_handoff"` を返した場合: * `handoff.type: "transaction"` の場合 - 提供された intent で ACP チェックアウトを開始 * 理由を `handoff_transaction` として SI セッションを終了 ### 5. セッション管理 * セッションタイムアウトを実装する(推奨: 5 分間の非アクティブ) * セッション状態をローカルで追跡し、終了時にクリーンアップします * 次のエラーコードを処理します: `session_not_found`, `offering_unavailable`, `rate_limited` ## 実装のテスト ### ブランドシミュレーターでのローカルテスト ```bash theme={null} # Run the SI brand simulator npx @adcontextprotocol/si-simulator # Connect your host to localhost:3001 ``` ### 統合チェックリスト * [ ] SI マニフェスト経由でブランドエージェントを探索できます * [ ] オファー詳細を取得できる(匿名、PII なし) * [ ] アイデンティティ有無にかかわらずセッションを開始できます * [ ] メッセージ送受信ができます * [ ] すべての終了理由を処理できます * [ ] すべての標準コンポーネントを正しく描画できます * [ ] アクションボタンとコールバックを処理できます * [ ] 適切な同意フローを実装しています * [ ] コマースハンドオフを処理できます * [ ] セッションタイムアウトを実装しています ## 次のステップ * 規定要件については [SI Specification](./specification) を確認します * ブランド側の実装は [Implementing SI Agents](./implementing-si-agents) を参照します * 詳細なスキーマ仕様は [Task Reference](./tasks/) を確認します * 実装サポートには [Community](https://join.slack.com/t/agenticads/shared_invite/zt-3c5sxvdjk-x0rVmLB3OFHVUp~WutVWZg) に参加します # 計測 Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/measurement Sponsored Intelligence の計測 — エンゲージメント指標、コンバージョントラッキング、会話内コマース # 計測 Sponsored Intelligence の計測は、従来のインプレッションベースの指標ではなく、エンゲージメントとインテントシグナルを中心に据えています。 ## 主要指標 | 指標 | フィールド | 説明 | | ---------- | -------------------------- | ------------------------------------------------------- | | エンゲージメント | `engagements` | スポンサードコンテンツへのユーザーインタラクション — クリック、展開、ブランドに関するフォローアップ質問など | | クリック | `clicks` | 広告主のリンク先へのアウトバウンドクリック | | クリック単価 | `rate`(CPC) | クリックあたりの平均コスト — ほとんどの SI 製品における主要な価格モデル | | エンゲージメント単価 | 派生値: `spend / engagements` | すべてのインタラクション種別にわたる効率を測るレポート指標(価格モデルではない) | ## コンバージョントラッキング コンバージョントラッキングをサポートする AI プラットフォームは、`get_adcp_capabilities` の `conversion_tracking` にその旨を宣言します。バイヤーは `sync_event_sources` でイベントソースを設定し、パッケージに `kind: "event"` の最適化ゴールを使用する — 他のチャネルと同じパターンです。詳細は [最適化とレポート](/docs/media-buy/media-buys/optimization-reporting) を参照。 ## 計測は変わりません AdCP はメディアの購入方法を変えるものであって、計測方法を変えるものではありません。既存の計測スタック — メディアミックスモデリング、モバイル計測パートナー、マルチタッチアトリビューション、インクリメンタリティテストなど — はそのまま機能します。Sponsored Intelligence はメディアプランにおける新しいチャネルであり、新しい計測パラダイムではありません。 プロトコルが計測を容易にする点が一つある。`sync_event_sources` を通じてプラットフォームにコンバージョンイベントをプッシュするため、プラットフォームはプロキシ指標ではなく実際のビジネス成果に向けて最適化できます。ただし、その支出が価値あるものだったかどうかを評価する方法は、今日使っているのと同じツールやフレームワークを使います。 ## 広告を超えて: 会話内コマース AI サーフェスはコマースに独自のポジションを持っています。ランニングシューズについて AI アシスタントに尋ねるユーザーは、プラットフォームがレコメンド、比較、そして最終的には購入をサポートできるコンテキストでインテントを表明しています。現在、AdCP は広告レイヤー(カタログ、メディアバイ、デリバリー)を担っています。`sync_catalogs` を通じてカタログを提供することで、[SI チャットプロトコル](/docs/sponsored-intelligence/si-chat-protocol) セッションを通じたコマースハンドオフがすでに可能になっており、ユーザーは商品を閲覧してチェックアウトへと移行できます。コマースプロトコルが成熟するにつれ、「興味あり」から「購入済み」までの流れが会話内で完結するようになるでしょう。 # AIサーフェスの収益化 Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/monetizing-ai AIアシスタント、AI検索エンジン、その他のAIプラットフォームで広告を出したいブランド、代理店、中小企業向けの実践ガイド。 # AIサーフェスの収益化 AIアシスタントが商品を勧め、AI検索エンジンがブランドの検索結果を表示し、会話型エージェントがユーザーの購買判断を支援しています。これは新しい広告チャネルであり、急速に成長しています。 Amazon AdsやWalmart Connectを運用したことがある人なら、基本的な仕組みはすでに理解しているはずです。プロダクトフィードをプラットフォームに投入すると、プラットフォームが適切なユーザーに商品を訴求します。AIプラットフォームも同じ仕組みで動くが、あなたのデータをさらに活用できます。カスタムレスポンスの生成、コンテキストに応じた推薦、さらには完全な商品相談のために自社ブランドエージェントへの引き継ぎまで行える。 これがSponsored Intelligenceだ。ディスプレイ、ソーシャル、CTVとは異なる仕組みで動く。アップロードするクリエイティブはない。ターゲットとするオーディエンスセグメントもない。AIプラットフォームがブランドを適切に表現するために必要なものをすべて提供します。商品、ブランドボイス、ルールです。そしてプラットフォームが適切なタイミングで適切なメッセージを生成します。 このガイドはバイサイドのすべての人を対象としています。新しいチャネルを評価しているエージェンシーのトレーディングデスク、メディアアプローチを見直しているブランドリーダー、顧客がますます時間を過ごすようになった場所でリーチしようとしている中小企業です。技術的なプロトコルの詳細については、[Sponsored Intelligenceプロトコル](/docs/sponsored-intelligence/overview)を参照してほしい。 ## 既存のアプローチが不十分な理由 **インサーションオーダー**は、あらかじめ決められたプレースメントに完成したクリエイティブを配信することを前提としています。AIプラットフォームはあなたのデータからその場でクリエイティブを生成します。配信するものは存在しません。 **プログラマティックバイイング**は薄いシグナル(ページのURL、デバイスの種類、場合によってはユーザーID)をリモートの意思決定者に送信します。その意思決定者は、AIの広告効果を生み出す会話のコンテキストを持っていません。ユーザーに最も近いプラットフォームがコンテキストを持っています。そのコンテキストから離れてビッドリクエストを送ることは、間違った方向です。 **ダイレクトディール**は単一のプラットフォームには機能するかもしれないが、AIプラットフォームにはそれぞれ独自のAPI、独自のデータ要件、独自のレポート形式があります。それぞれとカスタムインテグレーションを構築することは、業界がプログラマティックで10年かけて解決したのと同じ断片化の問題です。 AdCPが存在するのは、ここで機能するレガシーなアプローチがないからです。AIプラットフォームにデータを投入し、プラットフォームが代わりに効果的な広告を生成できるようにするための標準プロトコルです。 ## 変化:キャンペーンから素材へ 従来の広告では、バイヤーの仕事はキャンペーンを設定しクリエイティブを制作することです。Sponsored Intelligenceでは、バイヤーの仕事は**素材を提供し目標を定義すること**だ。 誰が最も多くの情報を持っているかを考えてほしい。AIプラットフォームはユーザーと会話している存在です。その人が何を尋ね、何を気にし、すでに何を議論したかを知っています。あなたは自分のブランド、商品、目標を知っています。プロトコルはこの2つの側面をつなぐ。あなたが素材を投入すると、プラットフォームが最善の結果を組み立てる。 Sponsored IntelligenceはこのパターンをあらゆるAIプラットフォームにおける会話型・生成型のエクスペリエンスに拡張します。 素材が優れているほど、結果も優れたものになります。 ## 提供するもの AIプラットフォームに投入するものはすべて、プラットフォームが広告を作成、ターゲティング、最適化するために使うビルディングブロックです。 | あなたが... | 投入するもの | プラットフォームができること | | --------- | ---------------------- | ---------------------------- | | Eコマースブランド | タイトル、説明、価格、画像を持つ商品 | ユーザーがカテゴリについて尋ねたとき特定の商品を推薦する | | 旅行会社 | 日程と価格を持つフライト、ホテル、パッケージ | 会話に基づいて関連する旅行プランを提案する | | 雇用主 | 職種、勤務地、要件を持つ求人 | 適格な候補者に募集ポジションを表示する | | 小売業者 | 店舗の場所とローカル在庫 | 在庫のある近隣店舗にユーザーを誘導する | | サービス会社 | サービス内容とプロモーション | ユーザーが求めているものに能力をマッチさせる | カタログ以外にも以下を提供します。 * **ブランドアイデンティティ** — プラットフォームがあなたのブランドらしく聞こえるよう、ボイス、ビジュアルガイドライン、ポジショニング * **コンテンツ基準** — 広告が作成される前の生成時に適用される適合性ルール * **コンバージョンイベント** — プラットフォームが重要な指標に向けて最適化できるようにする実際のビジネス成果 * **最適化目標** — エンゲージメント単価、コンバージョン単価、またはROASの目標値 ## 既存の計測スタックはそのまま使える メディアミックスモデリング、MMP、マルチタッチアトリビューション、インクリメンタリティテストなど、あなたの既存の計測スタックはこれまでと同じように機能します。Sponsored Intelligenceはメディアプランの新しいチャネルであり、新しい計測パラダイムではありません。プロトコルが1つ容易にすることがあります。コンバージョンイベントをプラットフォームに投入するため、プロキシ指標ではなく実際のビジネス成果に向けて最適化できます。しかし、その支出が価値があったかどうかを評価する方法は、今日と同じツールとフレームワークを使います。詳細は[計測](/docs/sponsored-intelligence/measurement)を参照してほしい。 ## 役割別の始め方 ### 代理店を持つブランド あなたの主な仕事は**データの品質**だ。キャンペーンの効果は、何を投入するかに直接依存します。 ### カタログを管理します 商品データが豊富で、正確で、最新の状態であることを確認します。詳細な説明、高品質な画像、構造化された属性(サイズ、色、カテゴリ)、正確な価格設定が必要です。カタログが薄ければ、広告も薄くなります。 ### ブランドアイデンティティを定義します ボイス、ビジュアルガイドライン、ポジショニングを提供します。これはあれば良いものではなく、あなたのブランドらしく聞こえるスポンサードコンテンツと、汎用的に聞こえるコンテンツの違いを生む。 ### コンテンツ基準を設定します ブランドが表示されてよい場所とされてはなりません場所を定義します。具体的に示す必要があります。AIプラットフォームは広告生成時にこれらのルールを適用するため、他のどのチャネルよりも強力なコントロールが可能です。 ### 代理店にブリーフするか自分で実験します 代理店はAdCPを使うバイヤーエージェントを通じてキャンペーンを管理します。プロトコルにより代理店はより多くの自動化を行い、より良いサービスを提供できます。一部のブランドがAmazon Adsをインハウスで運用しながら代理店に他のすべてを任せているのと同じように、代理店との関係と並行して自社ブランドエージェントで実験することもできます。 ### 代理店とトレーディングデスク バイヤーエージェントはプログラマティックにおけるDSPと同じ役割を果たすが、プログラマティックに限定されない。DSPの隣に位置します。既存のプログラマティックスタック、計測、レポートはなくなりません。AIプラットフォームにリーチできるバイヤーエージェントを追加し、時間をかけて、セラーがプロトコルを実装しているあらゆるチャネルにリーチできるようになります。 バイヤーエージェントはプロトコルを実装しているあらゆるAIプラットフォームに接続します。クライアントデータ(カタログ、ブランドアイデンティティ、コンテンツ基準、コンバージョンイベント)を投入し、利用可能な商品を発見し、キャンペーンを実行し、配信レポートを取得します。すべてのプラットフォームを1つのインターフェースで管理できます。一度構築したものがどこでも機能します。 [AdCP SDK](/docs/building)を使って構築してほしい。動作するバイヤーエージェントを最初に持つ代理店は、プラットフォームごとにダイレクトディールを交渉する代理店よりも速くクライアントの需要を獲得できます。 ### 中小企業 パートナーを通じて作業します。広告ネットワーク、プラットフォームインテグレーション、またはすでに使っているコマースプラットフォームに組み込まれたツールです。パートナーがプロトコルの配管を処理します。 あなたの仕事はシンプルです。 * **良いプロダクトフィードを用意します。** Shopify、Etsy、その他のEコマースプラットフォームで販売しているなら、すでに持っているはずです。 * **ブランドの基本をセットアップします。** 名前、ロゴ、ボイス、ブランドが表示されるべき場所とされてはなりません場所のルール。 * **成功の定義を決める。** 売上か、店舗への来訪か、サインアップか。パートナーは何に向けて最適化すべきかを知る必要があります。 パートナーを見つけるには、[Addieに聞いてみる](https://agenticadvertising.org/chat)といい。適切な選択肢をマッチさせてくれます。[メンバーディレクトリを直接閲覧する](https://agenticadvertising.org/members)こともできます。 ## 次のステップ Addieに「認定を取得したい」と伝えてほしい。無料のBasicsトラック(A1–A3、約50分)でプロトコルの基礎を学べる。Buyerトラック(C1–C4)では動作するバイヤーエージェントの構築方法を学べる。プログラミング経験は不要です。 * [Sponsored Intelligenceプロトコル](/docs/sponsored-intelligence/overview) — 商品スペクトラム、広告ネットワーク、ワークフロー、計測を含む完全な技術プロトコル * [カタログ](/docs/creative/catalogs) — 商品、オファリング、店舗、在庫カタログの仕組み * [ブランドアイデンティティ](/docs/brand-protocol/brand-json) — ボイス、ビジュアルガイドライン、ポジショニングのための`brand.json`仕様 * [コンテンツ基準](/docs/governance/overview) — ブランド適合性ルールの定義、共有、適用の仕組み * [SDKとインテグレーション](/docs/building) — バイヤーエージェント構築用のJavaScriptおよびPython SDK # 広告ネットワーク Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/networks AdCPを使用してAI広告ネットワークが複数のAIプラットフォームにまたがってインベントリを集約する方法 — プロダクトモデリング、アカウントチェーン、カタログフォワーディング、ガバナンス、配信レポート、SI Chat Protocolルーティング。 # 広告ネットワーク 広告ネットワークは、複数のAIプラットフォームにまたがるインベントリを単一のセラーインターフェースに集約します。プロトコルはこのトポロジーをネイティブにサポートしており、ネットワークは自身が所有していない複数のパブリッシャープロパティを代理するセラーエージェントとして機能します。 ## ネットワークの見え方 **バイヤーエージェント**から見ると、ネットワークは標準的なセラーエージェントです。バイヤーはネットワークのMCPサーバーに接続し、標準タスクを通じてカタログやデータをプッシュし、買い付けを実行します。**基盤となるAIプラットフォーム**から見ると、ネットワークはオペレーターだ — 各プラットフォームにアカウントを保有し、バイヤーのカタログデータ、ブランドアイデンティティ、コンテンツ基準を転送します。 ## ネットワークのプロダクトモデリング ネットワークのプロダクトは、`publisher_properties`を使用して複数のAIプラットフォームにまたがることができます: ```json theme={null} { "product_id": "sponsored_response_ai_network", "name": "Sponsored responses - AI assistant network", "description": "Sponsored responses across multiple AI assistants. The network routes to the best-matching platform based on user context and brand relevance.", "channels": ["sponsored_intelligence"], "publisher_properties": [ { "publisher_domain": "assistant-alpha.example.com", "selection_type": "all" }, { "publisher_domain": "assistant-beta.example.com", "selection_type": "all" }, { "publisher_domain": "search-gamma.example.com", "selection_type": "all" } ], "format_ids": [ { "agent_url": "https://ads.ai-network.example.com", "id": "sponsored_response" } ], "delivery_type": "non_guaranteed", "pricing_options": [ { "pricing_option_id": "network_cpc", "pricing_model": "cpc", "floor_price": 0.75, "price_guidance": { "p50": 2.50, "p75": 4.00 }, "currency": "USD", "min_spend_per_package": 1000 } ], "metric_optimization": { "supported_metrics": ["clicks", "engagements"], "supported_targets": ["cost_per"] }, "creative_policy": { "co_branding": "none", "landing_page": "any", "templates_available": true }, "catalog_types": ["product", "offering"] } ``` ネットワークは、コンテキスト、関連性、パフォーマンスに基づいてどのプラットフォームが各インプレッションを配信するかを決定します。バイヤーはどのプラットフォームが選ばれたかを知る必要はなく、ネットワークから統合された配信レポートを受け取ります。 ## ネットワークのアカウントモデル ネットワークは通常、暗黙的なアカウント(`require_operator_auth: false`)を使用します。バイヤーエージェントは信頼され、`sync_accounts`を通じてブランドを宣言します。その後ネットワークは、基盤となる各AIプラットフォームとのアカウントを独自に管理します: ``` Buyer agent -> Network (implicit accounts, agent-trusted) Network -> AI Platform A (explicit accounts, operator auth) Network -> AI Platform B (explicit accounts, operator auth) ``` ケイパビリティは以下のように宣言します: ```json theme={null} { "adcp": { "major_versions": [3] }, "supported_protocols": ["media_buy", "creative"], "account": { "require_operator_auth": false, "supported_billing": ["operator", "agent"], "required_for_products": false, "sandbox": true } } ``` ## カタログフォワーディング ネットワークはバイヤーから`sync_catalogs`を通じてカタログを受け取り、関連するAIプラットフォームに転送します。同じタスクが両方の区間で機能し、ネットワークは各プラットフォームにカタログを同期する際にバイヤーとして機能します。これはコアデータパイプです:ブランドのカタログデータがバイヤーからネットワーク、そしてプラットフォームへと流れ、各プラットフォームが広告を生成するための素材を提供します。 ## ガバナンスとコンテンツ基準 ネットワークはプラットフォームへの転送前に、ルーティング層で[ガバナンスポリシー](/docs/governance/overview)を適用できます。バイヤーがコンテンツ基準をプッシュすると、ネットワークはどのプラットフォームとコンテキストが適格かを選択する際にそれを適用し、その後各プラットフォームにもポリシーを転送してクリエイティブ生成時にも適用されるようにします。これにより、ブランドには2層の適合性適用が提供されます:ネットワークのルーティング決定とプラットフォームの生成制約です。 ## 配信レポート ネットワークは基盤となるプラットフォームからの配信データを集約し、`get_media_buy_delivery`を通じてバイヤーに統合されたレポートを提供します。バイヤーはメディアバイごとに単一の配信レポートを受け取り、ネットワークがプラットフォームごとの内訳を内部で処理します。プラットフォームレベルの透明性を提供したいネットワークは、`reporting_dimensions`を使用してプレースメントレベルの内訳を公開できます。 ## Declaring your network in brand.json ネットワークは、`relationship` フィールドを使って [`brand.json`](/docs/brand-protocol/brand-json) でプロパティを宣言します。これは adagents.json の `delegation_type` と同じ語彙を使い、双方向検証チェーン — プログラマティック広告の `sellers.json` の AdCP 版 — を作ります。 ```json theme={null} { "house": { "domain": "ai-network.example.com", "name": "Example AI Network" }, "brands": [{ "id": "ai_network", "properties": [ { "type": "website", "identifier": "ai-network.example.com", "primary": true }, { "type": "website", "identifier": "assistant-alpha.example.com", "relationship": "delegated" }, { "type": "website", "identifier": "assistant-beta.example.com", "relationship": "delegated" }, { "type": "website", "identifier": "search-gamma.example.com", "relationship": "ad_network" } ], "agents": [{ "type": "sales", "url": "https://ads.ai-network.example.com", "id": "network_sales" }] }] } ``` 委任とネットワークのパスについては、`relationship` フィールドは adagents.json の `delegation_type` と同じ値を使います。`owned` はファーストパーティインベントリのための brand.json のみの relationship です。 | Relationship | Meaning | Example | | -------------- | ---------------------------------------------------------- | ------------------------------- | | `owned`(デフォルト) | このプロパティを所有・運営 | 自身のウェブサイト | | `direct` | このプロパティの直接の販売パス | ベンダーのテクノロジーを使うパブリッシャーの社内広告チーム | | `delegated` | このプロパティのマネタイズを管理 — 担当 | フードブログの広告販売を管理する Mediavine | | `ad_network` | このプロパティのインベントリをネットワークまたはエクスチェンジの一部として販売 — 唯一のパスではなく 1 つのパス | nytimes.com の SSP としての PubMatic | `delegated` と `ad_network` の区別は重要です: 委任関係は、オペレーターがプロパティのマネタイズを担当する(排他的またはほぼ排他的なアクセス)ことを意味します。ad\_network 関係は、オペレーターがインベントリへの潜在的に多くのパスの 1 つであることを意味します。 ### Bilateral verification with adagents.json これは双方向の検証チェーンを作ります — プログラマティックの `sellers.json` + `ads.txt` と同じパターンです。 | File | Who publishes it | What it declares | Programmatic equivalent | | ------------------------ | ---------------- | ---------------------------------- | ----------------------- | | `brand.json`(オペレーター) | ネットワーク/SSP | 「私はこれらのパブリッシャーのために販売する、これがその方法」 | `sellers.json` | | `adagents.json`(パブリッシャー) | 各パブリッシャー | 「このオペレーターのエージェントは認可されている、これが委任タイプ」 | `ads.txt` | 両側が合意しなければなりません。ネットワークは `brand.json` で関係を宣言し、各パブリッシャーは自身の `adagents.json` で一致する `delegation_type` によってネットワークのエージェントを認可することで確認します。片側のみが宣言する場合、関係は不完全です — ネットワークヘルスダッシュボードは、これを認可の欠如(オペレーターは宣言したがパブリッシャーが認可していない)または孤立した認可(パブリッシャーは認可したがオペレーターが宣言していない)としてフラグを立てます。 ## ネットワークのadagents.json ネットワークの[`adagents.json`](/docs/governance/property/adagents)には、自身が所有していないパブリッシャープロパティも含めて、代理するパブリッシャープロパティが列挙されます: ```json theme={null} { "version": "1.0", "properties": [ { "domain": "assistant-alpha.example.com", "agents": [{ "agent_url": "https://ads.ai-network.example.com", "relationship": "direct", "supported_protocols": ["media_buy", "creative"] }] }, { "domain": "assistant-beta.example.com", "agents": [{ "agent_url": "https://ads.ai-network.example.com", "relationship": "direct", "supported_protocols": ["media_buy", "creative"] }] } ] } ``` 基盤となる各AIプラットフォームは、自身の`adagents.json`でネットワークを認可します。バイヤーエージェントは、プラットフォームの認可チェーンを通じてネットワークを発見します。 ## ネットワーク経由のSI Chat Protocol 広告ネットワークがブランドを代理して[SI Chat Protocol](/docs/sponsored-intelligence/si-chat-protocol)セッションを販売する場合、セッションフローの仲介者として機能します。ブランドは`type: "offering"`を指定しました`sync_catalogs`でオファリングをネットワークに同期し、ネットワークがそれを基盤となるプラットフォームに転送します。プラットフォームがセッションをトリガーすると、ネットワークはそれを正しいブランドエージェントにルーティングします。 ``` AI Platform -> Network -> Brand Agent 1. Platform calls si_initiate_session with the network's media_buy_id 2. Network maps media_buy_id to the brand's offering_id 3. Network forwards to the brand agent's SI endpoint 4. Brand agent responds; network relays back to the platform ``` 各区間の主要フィールド: | フィールド | プラットフォームからネットワーク | ネットワークからブランド | | -------------- | --------------------- | ---------------- | | `media_buy_id` | ネットワークのメディアバイID | 異なる場合や省略される場合もある | | `offering_id` | 未設定(プラットフォームは知らない) | ブランド固有のオファリング | | `context` | 会話からのユーザーインテント | そのまま転送 | | `identity` | ユーザーアイデンティティ(同意済みの場合) | そのまま転送 | ネットワークは2区間にまたがるアトリビューション相関を処理します。どのプラットフォームがセッションをトリガーしたか(`placement`)、どのメディアバイが資金提供したか(`media_buy_id`)、どのブランドが応答したか(`offering_id`)を把握しています。これにより、ネットワークは`get_media_buy_delivery`を通じてバイヤーに統合された配信レポートを提供でき、一方で各ブランドエージェントは自身のセッションのみを確認します。 ネットワークは`identity`と`supported_capabilities`を変更せずに転送すべきだ — ブランドエージェントはモダリティを交渉するために正確なホストケイパビリティを必要とし、ユーザーの同意はネットワークではなくブランドに対して付与されているためです。 SI Chat Protocolのセッションライフサイクルとケイパビリティネゴシエーションの詳細については、[SI Chat Protocol](/docs/sponsored-intelligence/si-chat-protocol)と[SIホストの実装](/docs/sponsored-intelligence/implementing-si-hosts)を参照。 # Sponsored Intelligence Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/overview AdCP で AI サーフェスをマネタイズ — グロースマーケターが逆転したデータフローを使って AI プラットフォームでキャンペーンを立ち上げる様子を追います。 グロースマーケターが、AI アシスタントの会話と、そこに流れ込む商品カタログデータを表示する 1 枚のスクリーンの前の整然としたデスクに座っている **実験的機能。** Sponsored Intelligence は、実験的サーフェス(機能 id `sponsored_intelligence.core`)として AdCP 3.0 の一部です — セッションライフサイクル、UI コンポーネント、アイデンティティ/同意オブジェクトの形状、機能ネゴシエーションは、少なくとも 6 週間の予告をもって 3.x リリース間で変更される可能性があります。SI を実装するセラーは `experimental_features` で `sponsored_intelligence.core` を宣言しなければなりません(MUST)。完全なコントラクトについては [実験的ステータス](/docs/reference/experimental-status) を参照。計画された変更は [3.1.0 ロードマップ](https://github.com/adcontextprotocol/adcp/issues/2201) で追跡されます。 Priya は DTC アウトドアブランド Ridgeline Gear のグロースマーケターです。彼女の CEO は、競合の商品が ChatGPT で推奨されているのを目にしたばかりで、こう尋ねます: *「なぜ我々はそこにいないんだ?」* Priya はプログラマティックを知っています。ディスプレイ、ソーシャル、CTV のキャンペーンを運用してきました。しかし AI プラットフォームは違います。アドサーバーがない。入札リクエストがない。クリエイティブのアップロードがない。AI はコンテキストから広告を生成します — そして今、それには Ridgeline についてのコンテキストがありません。 VAST が動画広告の配信方法を定義したように、Sponsored Intelligence は AI サーフェスでの広告の仕組みを定義します。それは伝統的なモデルを反転させます: 薄い入札リクエストを外に送る代わりに、バイヤーはすべてを中に押し込みます — 商品カタログ、コンバージョンイベント、ブランドアイデンティティ、コンテンツ標準、最適化目標 — プラットフォームの LLM が完全な情報で適切な瞬間に適切な広告を生成できるように。 ## 逆転したデータフロー 分割画面の比較: 左側は伝統的なプログラマティックで、薄い入札リクエストの矢印がプラットフォームからリモートの入札者へ外向きに流れる。右側は Sponsored Intelligence で、リッチなデータ — カタログ、イベント、ブランドアイデンティティ — が LLM が決定を下す AI プラットフォームへ内向きに流れる 伝統的なプログラマティックでは、プラットフォームは入札リクエストを**外へ**送ります — ページ URL、デバイスタイプ、おそらくユーザー ID — リモートの意思決定者へ。意思決定者はコンテキストを持ちません。コンテキスト保持者は決定を下しません。 AI プラットフォームはこれを反転させます。バイヤーは AdCP 経由ですべてを**中へ**押し込みます。**意思決定者がコンテキスト保持者です。** それが根本的な転換です。 データは事前に流れます。決定は依然としてリアルタイムで起きます — しかし意思決定者は今、完全なコンテキストを持ちます: ユーザーが何を尋ねたか、ブランドが何を売るか、成功がどう見えるか、ブランドのボイスがどう聞こえるか。 | Priya が知っていること | SI での呼び方 | | ----------------------------- | ------------------------------------------------------------ | | クリエイティブをアドサーバーにアップロード | カタログ、ブランドアイデンティティ、イベントをプラットフォームに押し込む | | 入札リクエスト(プラットフォームがコンテキストを外に送る) | 逆転したデータフロー(バイヤーがデータを中に押し込む) | | DSP がリモートで広告を選択 | プラットフォーム LLM が完全なコンテキストで広告を生成 | | オーディエンスセグメントとジオターゲティング | 会話の関連性とキーワードの意図 | | インプレッション、クリック、CTR | エンゲージメント、クリック、クリックあたりのコスト | | ブランドセーフティのブロックリスト | 生成時に強制される[コンテンツ標準](/docs/governance/content-standards/index) | ## 仕組み: Priya の最初の SI キャンペーン Priya は、自身のスポンサードプレースメントを販売する主要な AI アシスタント NovaMind から始めます。NovaMind はファーストパーティの AI プラットフォームです — account-id 名前空間、独自のアドサービング、独自の測定。 ### ステップ 1: アカウントを接続する Priya のバイヤーエージェントロボットが、光るポータルの前で NovaMind プラットフォームロボットと握手し、アカウント認証情報が両者の間を流れながら接続を確立する Priya のバイヤーエージェントは NovaMind に接続し、Ridgeline のアカウントを確認します — 新しいパブリッシャーでアクセスを検証するように: ```javascript theme={null} const accounts = await novamind.listAccounts({ account: { brand: { domain: "ridgelinegear.com" } } }); // Returns Ridgeline's account — ready to sync data and buy inventory ``` ### ステップ 2: 材料を押し込む 商品カタログカード、brand.json ドキュメント、コンバージョンイベントストリーム、コンテンツ標準ドキュメントが、Ridgeline のシステムから光るパイプラインを通じて NovaMind のプラットフォームに流れ込み、AI 内部でリッチなブランドコンテキストに組み立てられる ここが SI が Priya の知るすべてから分岐する点です。クリエイティブをアップロードする代わりに、彼女は生の材料を押し込みます — そしてプラットフォームが広告を組み立てます。Priya のバイヤーエージェントがプロトコルを処理します。内部で何が起きるか: ```javascript theme={null} // Product catalog — a structured feed describing what Ridgeline sells // (like a Google Merchant Center feed, but consumed by the AI platform) await novamind.syncCatalogs({ account: { brand: { domain: "ridgelinegear.com" } }, catalogs: [{ catalog_id: "ridgeline-products-2026", type: "product", source: { url: "https://ridgelinegear.com/feeds/products.json" } }] }); // Conversion events — what success looks like await novamind.syncEventSources({ account: { brand: { domain: "ridgelinegear.com" } }, event_sources: [{ event_source_id: "ridgeline-conversions", type: "conversion", source: { url: "https://ridgelinegear.com/events/conversions" } }] }); ``` Priya はまた `ridgelinegear.com/.well-known/brand.json` に [`brand.json`](/docs/brand-protocol/brand-json) を公開します — プラットフォームが広告生成時に読むボイス、カラー、ビジュアルガイドライン。そして彼女は、プラットフォームが決定時に強制する[コンテンツ標準](/docs/governance/content-standards/index)を設定します。 **SI ガバナンス統合は計画中です。** Sponsored Intelligence の完全なプロトコルレベルのガバナンス — `sync_plans` によるキャンペーン登録、`check_governance` によるセッションライフサイクルチェック、AI 生成コンテンツのコンテンツ標準、AI アシスタント配置のプロパティガバナンス — は開発中です。現在のステータスについては [ガバナンス概要](/docs/governance/overview#sponsored-intelligence-planned) を参照。現在、SI プラットフォームは、コンテンツ標準とブランドアイデンティティを使ってアプリケーション層でガバナンスを強制します。 これらの材料が一緒になって、それが広告**そのもの**です。商品カタログがクリエイティブ生成を養います。コンバージョンイベントが最適化ループを閉じます。ブランドアイデンティティが出力を正しく聞こえるようにします。コンテンツ標準がそれを安全に保ちます。 ### ステップ 3: 商品を発見する NovaMind が、スポンサードプレースメントオプションのカタログを Priya のバイヤーエージェントに提示する: スポンサードレスポンス、AI 検索結果、プレミアムなブランド体験ハンドオフ — それぞれ光る商品カードとして表示される Priya は NovaMind が提供するものを発見します — CTV やディスプレイで使うのと同じ `get_products`: ```javascript theme={null} const products = await novamind.getProducts({ buying_mode: "brief", brief: "Outdoor gear brand. Want to reach people asking about hiking, camping, and trail running. Budget $15K/month.", brand: { domain: "ridgelinegear.com" }, account: { brand: { domain: "ridgelinegear.com" } } }); ``` NovaMind は [SI プロダクトスペクトラム](/docs/sponsored-intelligence/product-spectrum)にわたるプロダクトを返します: | Product | How it works | Pricing | | --------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------- | | **スポンサードレスポンス** | ユーザーがハイキングギアについて尋ねたとき、NovaMind が Ridgeline のカタログから推奨を生成 | CPC | | **AI 検索結果** | Ridgeline が「best hiking boots」のキーワードトリガーの検索結果に現れる | CPC | | **ブランド体験ハンドオフ** | ユーザーが Ridgeline のブランドエージェントとのマルチターン会話を通じて Ridgeline 商品を深掘りする([SI Chat Protocol](/docs/sponsored-intelligence/si-chat-protocol)) | セッションごと | ### ステップ 4: メディアバイを作成する Priya のバイヤーエージェントが NovaMind に create_media_buy リクエストを送る — 光る契約ドキュメントが両エージェントの間に実体化し、予算額、フライト日程、最適化目標が浮遊するラベルとして見える Priya は 2 つのプロダクトを選んでメディアバイを作成します: ```javascript theme={null} const buy = await novamind.createMediaBuy({ account: { brand: { domain: "ridgelinegear.com" } }, brand: { domain: "ridgelinegear.com" }, start_time: "2026-04-01T00:00:00Z", end_time: "2026-06-30T23:59:59Z", packages: [ { product_id: "novamind_sponsored_outdoor", budget: 10000, pricing_option_id: "cpc_standard", optimization_goals: [ { type: "cpa", target: 12.00, event_type: "purchase" } ] }, { product_id: "novamind_search_hiking", budget: 5000, pricing_option_id: "cpc_keyword", targeting_overlay: { keywords: ["hiking boots", "trail running shoes", "camping gear"] } } ] }); ``` アップロードするクリエイティブはありません。トラフィックする広告タグはありません。NovaMind はすでに Ridgeline の商品カタログ、ブランドアイデンティティ、コンバージョンイベントを持っています。それはそれらの材料から、適切な瞬間に適切な広告を生成します。 ### ステップ 5: 広告の瞬間 スマホ画面上の AI アシスタント会話 — ユーザーが「アパラチアントレイル用にどのハイキングブーツを買うべき?」と尋ね、AI がコンテキストに関連する Ridgeline Trail Pro の推奨を、商品詳細と下部で光る「Ridgeline と話す」ハンドオフボタンとともに応答する NovaMind のユーザーが尋ねます: > *「アパラチアントレイル用にどのハイキングブーツを買うべき?」* NovaMind の LLM は Ridgeline の商品カタログを持ち、Trail Pro 3000 がこのクエリに一致することを知り、スポンサードレスポンスを生成します: > *「AT には、岩場の地形と変わりやすい天候に対応するブーツが欲しいところです。**Ridgeline Trail Pro 3000**(\$189)はまさにこのために作られています — Gore-Tex 防水、Vibram アウトソール、複数日のハイキング用に設計されたアンクルサポート。AT スルーハイカーから 4.7/5 の評価を得ています。」* > > *Sponsored by Ridgeline Gear* · \[Talk to Ridgeline →] すべての詳細は Priya が同期したカタログから来ています — 価格、機能、評価。ボイスは `brand.json` に一致します。Priya が設定した[コンテンツ標準](/docs/governance/content-standards/index)が、プラットフォームが裏付けのないクレームをしないことを保証します。ユーザーは、スポンサードと明確にラベル付けされた、関連性のある役立つ推奨を見ます。 プロトコルの下では、これは単なる UI コピーではありません。スポンサードコンテキストは、誰がコンテキストに支払ったか、ホストがそれをどう使うことが許されるか、ユーザー向けの開示が必要かを分離する説明責任宣言を運べます。レシートと監査モデルについては [Sponsored Context Accountability](/docs/sponsored-intelligence/specification#sponsored-context-accountability) を参照。 ユーザーが「Talk to Ridgeline」をタップすると、NovaMind は [SI Chat Protocol](/docs/sponsored-intelligence/si-chat-protocol) 経由で Ridgeline のブランドエージェントにハンドオフします — ユーザーがサイズについて尋ね、モデルを比較し、購入を開始できるマルチターン会話です。すべて AI 体験内で。 ### ステップ 6: 結果を測定する SI キャンペーンメトリクス — エンゲージメント、クリック、コンバージョン、エンゲージメントあたりのコスト — を示す整然としたダッシュボード。上昇トレンドの折れ線グラフと「CPA: $9.40、$12.00 目標を達成」を示すコールアウト付き Priya は、任意の AdCP キャンペーンを監視するのと同じ方法で監視します: ```javascript theme={null} const delivery = await novamind.getMediaBuyDelivery({ account: { brand: { domain: "ridgelinegear.com" } }, media_buy_ids: [buy.media_buy_id], include_package_daily_breakdown: true }); ``` レポート形式は、Priya が CTV やディスプレイに使うのと同じ `get_media_buy_delivery` レスポンスです。1 つのダッシュボード、すべてのチャネル。彼女の既存の測定スタック — メディアミックスモデリング、マルチタッチアトリビューション、インクリメンタリティテスト — は、これまでと同じように機能します。 *** ## スケールアップ: AI 広告ネットワーク Priya の NovaMind キャンペーンは機能しています。今、彼女は 1 つのプラットフォームだけでなく、多数の AI サーフェスにわたるより広いリーチを望んでいます。彼女は AI 広告ネットワーク Gravity に接続します。 ハブアンドスポーク図: Gravity がネットワークノードとして中心に位置し、スポークが一ダースの異なる AI プラットフォーム — チャットアシスタント、検索コパイロット、コーディングツール、旅行プランナー — に放射状に伸び、それぞれが自身のサーフェスの小さなロボットとして示される AI 広告ネットワークは、多数の AI プラットフォームにわたるインベントリを単一のセラーインターフェースに集約します。Priya は自身のカタログを Gravity に一度同期し、Gravity がそれを基盤となるプラットフォームに転送します。1 つの統合、多数のサーフェス。 | | ファーストパーティ(NovaMind) | 広告ネットワーク(Gravity) | | ------------ | -------------------------- | -------------------------------------------------------------- | | **インベントリ** | 1 つの AI プラットフォームの独自プレースメント | 多数の AI プラットフォームにわたって集約 | | **アカウントモデル** | 明示的 — Priya が直接登録 | 暗黙的 — バイヤーエージェントが `sync_accounts` でブランドを宣言。ネットワークがエージェントの識別を検証 | | **プロダクト** | NovaMind 独自の提供内容 | プロダクトは、どのプラットフォームが提供するかを示す `publisher_properties` を含む | | **カタログフロー** | NovaMind に直接同期 | Gravity に一度同期し、基盤となるプラットフォームに転送 | 同じプロトコルタスクが両方のパスで機能します。Priya は Gravity で `get_products`、`create_media_buy`、`get_media_buy_delivery` を、NovaMind でしたのと正確に同じように — そして [メディアバイプロトコル](/docs/media-buy/index) を通じて CTV とディスプレイのキャンペーンですでにしているのと正確に同じように — 呼び出します。彼女はすべてを 1 つのダッシュボードで見ます。 配信時、Gravity の基盤となる AI プラットフォームは [Trusted Match Protocol](/docs/trusted-match) を使ってデマンドを会話にマッチさせます。TMP はバイヤーエージェントにファンアウトし、コンテキストとユーザー適格性を評価し、プラットフォームがどのオファーを提示するかを選択します — すべて LLM の生成レイテンシー内で。購入層(メディアバイ、カタログ、レポート)は同じままです。TMP がその下でリアルタイムのメディエーションを処理します。 ネットワークトポロジー、バイヤー宣言のアカウントチェーン、カタログフォワーディング、仲介者経由の SI Chat Protocol ルーティング。 TMP が AI サーフェスでデマンドをメディエートする方法 — コンテキストマッチング、フリークエンシーキャップ、LLM 統合。 ## プロトコルアーキテクチャ SI は 2 つのプロトコル層を使います: * **購入**は [メディアバイプロトコル](/docs/media-buy/index) を使います — `get_products`、`create_media_buy`、`sync_catalogs`、`get_media_buy_delivery`。CTV やディスプレイを買うのと同じ方法で SI インベントリを買います。プロダクトの `channels: ["sponsored_intelligence"]` フィールドが SI インベントリを識別するものです。 * **配信**は SI プロトコルを使います — `si_initiate_session`、`si_send_message`、`si_terminate_session`。これらのタスクが [SI Chat Protocol](/docs/sponsored-intelligence/si-chat-protocol) のブランド体験ハンドオフを支えます。 ほとんどの SI プロダクト(スポンサードレスポンス、AI 検索結果)は購入層のみを含みます — プラットフォームが配信を内部で処理します。SI Chat Protocol のハンドオフが、両方の層にまたがるプロダクトタイプです。 ## 全体像 Ridgeline のデータが NovaMind(直接)と Gravity(ネットワーク)の両方に流れ込み、Gravity が多数の AI プラットフォームにファンアウトする水平フロー図 — すべてが同じ統一された配信フォーマットを通じて Priya の単一ダッシュボードにレポートし返す Priya の CEO は *「なぜ我々はそこにいないんだ?」* と尋ねました — そして今、Ridgeline は複数のプラットフォームにわたる AI 会話で推奨されています。Priya は一ダースの異なるシステムを学びませんでした。彼女は材料を標準プロトコルに押し込み、各プラットフォームに完全なコンテキストから適切な広告を生成させました。彼女の CTV とディスプレイのキャンペーンを実行するのと同じ `create_media_buy` が、彼女の SI キャンペーンも実行します。 来四半期、彼女は SI Chat Protocol 経由でブランド体験ハンドオフを追加します — ユーザーが Ridgeline 商品を深掘りしたいとき、AI 体験を離れずに Ridgeline のブランドエージェントと完全な会話ができるように。 ## さらに深く学ぶ 4 つの SI プロダクトタイプ — スポンサードレスポンス、AI 検索、生成ディスプレイ、ブランド体験ハンドオフ。 コード例付きで、アカウントセットアップから配信レポートまでステップバイステップ。 会話型のブランド体験プロトコル — セッションライフサイクル、モダリティ、コマースハンドオフ。 ブランド、エージェンシー、SMB が始めるための非技術的なガイド。 # プロダクトスペクトラム Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/product-spectrum Sponsored Intelligenceの4つのプロダクトタイプ(スポンサードレスポンス、AI検索結果、ジェネレーティブディスプレイ、SI Chat Protocolブランドエクスペリエンスハンドオフ)をJSONの例と価格モデルとともに解説します。 # プロダクトスペクトラム [Sponsored Intelligence](/docs/sponsored-intelligence/overview)はAIサーフェス(AIアシスタント、AI検索エンジン、ジェネレーティブAIエクスペリエンス)上の広告です。セラーはいくつかの異なるプロダクトタイプを提供しており、それぞれクリエイティブ生成パターン、価格モデル、計測機能が異なります。 | プロダクトタイプ | クリエイティブモデル | 価格 | 主な特徴 | | ------------------------- | -------------------------- | ------- | -------------------------------------------------------------------------------------- | | **スポンサードレスポンス** | プラットフォームがカタログ+ブランドデータから生成 | CPC | ユーザーの会話に対するコンテキスト関連性 | | **AI検索結果** | プラットフォームがカタログ+キーワードから生成 | CPC | `targeting_overlay`によるキーワードターゲティング | | **ジェネレーティブディスプレイ/ビデオ** | バイヤーが`build_creative`経由で提供 | CPM | 標準フォーマットID;バイヤーがクリエイティブを制御 | | **SI Chat Protocolハンドオフ** | ブランドエージェントが直接会話 | セッション単位 | [SI Chat Protocol](/docs/sponsored-intelligence/si-chat-protocol)によるマルチターンブランドエクスペリエンス | 最初の3つのプロダクトタイプは、[メディアバイプロトコル](/docs/media-buy/index)を通じて完全に購入・配信されます。SI Chat Protocolハンドオフはさらに`si_*`セッションタスクを使用します。[プロトコルアーキテクチャ](/docs/sponsored-intelligence/overview#protocol-architecture)を参照。 ## AIアシスタントにおけるスポンサードレスポンス Sponsored Intelligenceの主力プロダクトです。ユーザーの会話がブランドに関連している場合、プラットフォームはブランドのカタログとアイデンティティを使用してスポンサードレスポンスを生成します。価格は通常CPCだ。 ```json theme={null} { "product_id": "sponsored_response_assistant", "name": "Sponsored responses - AI assistant", "description": "Contextually relevant sponsored responses generated from brand catalog data when users ask related questions. Creative is generated by the platform using brand identity and product catalog.", "channels": ["sponsored_intelligence"], "publisher_properties": [ { "publisher_domain": "ai-platform.example.com", "selection_type": "all" } ], "format_ids": [ { "agent_url": "https://ads.ai-platform.example.com", "id": "sponsored_response" } ], "delivery_type": "non_guaranteed", "pricing_options": [ { "pricing_option_id": "sr_cpc", "pricing_model": "cpc", "floor_price": 0.50, "price_guidance": { "p50": 2.00, "p75": 3.50 }, "currency": "USD", "min_spend_per_package": 500 } ], "delivery_measurement": { "provider": "Platform analytics", "notes": "Engagement tracked per platform methodology. Clicks measured on outbound links and action buttons." }, "metric_optimization": { "supported_metrics": ["clicks", "engagements"], "supported_targets": ["cost_per"] }, "creative_policy": { "co_branding": "none", "landing_page": "any", "templates_available": true }, "catalog_types": ["product", "offering"] } ``` 主な特徴: * **CPC価格** — コンテキスト関連性によるオークションベースの入札 * **`templates_available: true`** — プラットフォームがカタログとブランドデータからクリエイティブを生成します * **`catalog_types`** — ジェネレーティブクリエイティブに使用するカタログタイプを宣言します。ECブランド向けのプロダクトフィード、サービス向けのオファリングフィード * **`metric_optimization`** — エンゲージメントとクリックをサポートし、バイヤーはコストパーエンゲージメント目標を設定できます ## AI検索スポンサード結果 AIを活用した検索エクスペリエンス内のスポンサード結果です。従来の検索広告に似ているが、AIが合成したレスポンスの中にレンダリングされます。キーワードターゲティングが主な関連性シグナルです。 ```json theme={null} { "product_id": "ai_search_sponsored", "name": "Sponsored results - AI search", "description": "Sponsored results appearing within AI search responses. Targeted by keyword relevance to user queries.", "channels": ["sponsored_intelligence"], "publisher_properties": [ { "publisher_domain": "ai-platform.example.com", "selection_type": "all" } ], "format_ids": [ { "agent_url": "https://ads.ai-platform.example.com", "id": "search_result_native" } ], "delivery_type": "non_guaranteed", "pricing_options": [ { "pricing_option_id": "search_cpc", "pricing_model": "cpc", "floor_price": 0.75, "price_guidance": { "p50": 2.50, "p75": 5.00 }, "currency": "USD", "min_spend_per_package": 500 } ], "delivery_measurement": { "provider": "Platform analytics", "notes": "Click tracking on sponsored result links. Query-level reporting available." }, "metric_optimization": { "supported_metrics": ["clicks"], "supported_targets": ["cost_per"] }, "creative_policy": { "co_branding": "none", "landing_page": "any", "templates_available": true } } ``` AI検索プロダクトは通常キーワードターゲティングをサポートします。セラーはこれを`get_adcp_capabilities`の`media_buy.execution.targeting.keyword_targets`で宣言し、バイヤーは`targeting_overlay`経由でパッケージにキーワードを指定します。 ## ジェネレーティブディスプレイとビデオ AIエクスペリエンス内(サイドバー、会話ターン間のインタースティシャル、またはビジュアルレスポンス)への配置に向けて、ブランドアセットから生成されたディスプレイおよびビデオ広告です。プラットフォームはブランドのアセットとガイドラインからビジュアルクリエイティブを生成するために`build_creative`を使用します。 ```json theme={null} { "product_id": "ai_generative_display", "name": "Generative display - AI experience", "description": "Display ads generated from brand assets, placed within AI experience surfaces.", "channels": ["sponsored_intelligence"], "publisher_properties": [ { "publisher_domain": "ai-platform.example.com", "selection_type": "all" } ], "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_300x250" }, { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_native" } ], "delivery_type": "non_guaranteed", "pricing_options": [ { "pricing_option_id": "display_cpm", "pricing_model": "cpm", "floor_price": 6.00, "currency": "USD", "min_spend_per_package": 1000 } ], "delivery_measurement": { "provider": "Platform ad server", "notes": "Impressions per IAB guidelines." }, "creative_policy": { "co_branding": "none", "landing_page": "any", "templates_available": false } } ``` スポンサードレスポンスとは異なり、ジェネレーティブディスプレイはクリエイティブエージェントの標準フォーマットIDを使用します。バイヤーは`build_creative`経由またはメディアバイにインラインでクリエイティブを提供する — プラットフォームはカタログデータからクリエイティブを生成しません。 ## ブランドエクスペリエンスハンドオフ(SI Chat Protocol) ユーザーが高い購買意向を示した場合、AIプラットフォームは[SI Chat Protocol](/docs/sponsored-intelligence/si-chat-protocol)経由でブランド独自のAIエージェントに会話をハンドオフできます。これにより、AIアシスタントを離れることなく、プロダクトレコメンデーション、比較、設定、さらには取引完了まで含む充実したマルチターンブランドエクスペリエンスが実現します。 ブランドエクスペリエンスハンドオフはSponsored Intelligence独自の機能です。ブランドは`type: "offering"`を指定して`sync_catalogs`でオファリングを同期し、プラットフォームはユーザーのインテントがオファリングと一致した際にハンドオフをトリガーします。セッションライフサイクルとケイパビリティモデルについては[SI Chat Protocol](/docs/sponsored-intelligence/si-chat-protocol)を参照。 # SI チャットプロトコル Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/si-chat-protocol SI チャットプロトコルは、AIアシスタントにおける会話型ブランドエクスペリエンスを定義します。セッションライフサイクル、ケイパビリティネゴシエーション、アイデンティティの同意、UIコンポーネント、ACPコマースハンドオフを含みます。 **ドラフト仕様** — このプロトコルは現在開発中です。最終リリースまでにAPIとスキーマが変更される可能性があります。リファレンス実装を構築する中でフィードバックを歓迎します。 消費者はAIサービスで情報を探したり調べたりしています。ブランドや製品について調べるためにその場を離れたくない。だから、ブランドをチャットの中に持ち込む方法が必要です。 Sponsored Intelligenceプロトコルは、AIアシスタントがブランドエージェントのエンドポイントを呼び出し、対話する方法を定義します。これにより、会話の流れを損なわずに豊かなブランドエクスペリエンスを実現できます。 ## 兆ドルの価値を持つ一文 OpenAIがChatGPTへの広告導入を発表したとき、「回答の独立性」を約束した——広告が回答に影響を与えることはない、と。しかし本当の価値はチャットの下部にあるバナー広告ではありません。それはこういう文です: > "お探しの内容に基づくと、Deltaはボストン行きのフライトを199ドルから提供しています。アシスタントに接続してオプションを探ってみたいか?" AIがブランドを推薦し、会話のハンドオフを提案するこの一文は、数兆ドルの価値があります。Sponsored Intelligenceは、その次に何が起きるかの標準を定義します。 ## Sponsored Intelligenceとは何か? **Sponsored Intelligence (SI)** は、AIアシスタントにおける会話型ブランドエクスペリエンスのためのオープン標準です。VASTがビデオ広告配信を定義するように、SIはブランドエージェントのエンドポイントをどのように配信・対話するかを定義します。 ``` 広告配信標準: - VAST → 動画(動画ファイル + コンパニオン + トラッキング) - MRAID → リッチメディア/インタラクティブディスプレイ - Native → コンテンツスタイル広告 - SI → 会話型エージェント(エンドポイント + モダリティ + ブランドアセット) ``` ### SIはクリエイティブタイプ以上のもの SIは複数のコンテキストで使用できます: | コンテキスト | 説明 | 例 | | ---------------------- | ----------- | --------------------------------------- | | **クリエイティブ** | メディアバイ経由で配信 | ブランドがSIエンドポイントを同期し、キャンペーン実行時にトリガー | | **埋め込みエクスペリエンス** | ユーザーが興味を表明 | 「Deltaについてもっと教えて」→ ブランドエージェントへのシームレスな移行 | | **アジェンティック・ランディングページ** | キャンペーンの遷移先 | ランディングページの会話型版——ブランドエンゲージメントが発生する場所 | 重要な洞察:SIはクリックスルーではありません。会話のハンドオフです。従来のランディングページは、ユーザーが発見コンテキストを離れて詳細を知るために存在します。SIでは、ユーザーは会話の中にとどまりながら、ブランドが来ます。 ### プロトコルのスコープ SIは**会話型エンゲージメントプロトコル**だ。セッションライフサイクル、メッセージ交換、ハンドオフのメカニズムを定義します。スコープの内外は以下の通りです: | スコープ内 | スコープ外 | | ------------------------- | ------------------- | | セッションの開始・メッセージング・終了 | 広告選択・ランキングアルゴリズム | | ホストとブランド間のケイパビリティネゴシエーション | ビッディングとオークションのメカニズム | | アイデンティティと同意のハンドオフ | アトリビューションモデルと計測 | | ACPへのコマースハンドオフ | 当事者間の請求と報酬 | | 標準UIコンポーネント | 在庫予測 | **このスコープの理由は?** SIはエンゲージメントレイヤーに焦点を当てている——ユーザーがブランドエージェントと接続するときに何が起きるか。オファーがどのように表面化されるか(広告選択)、コンバージョンがどのようにクレジットされるか(アトリビューション)、お金がどのように流れるか(請求)は、SIと相互作用するが、SIで定義されるものではありません。 この分離は意図的です。プラットフォームは同じSIプロトコルを使いながら、独自の選択アルゴリズムを使用できます。アトリビューションシステムはSIの相関IDを利用できるが、SIがモデルを指定することはない。請求の取り決めは当事者間のビジネス上の決定にとどまる。 ### アトリビューション相関 SIはアトリビューションのセマンティクスを定義しないが、アトリビューションシステムが必要とする相関IDを提供します: | フィールド | スコープ | 目的 | | -------------- | -------- | ------------------------------------- | | `session_id` | 開始時に返される | 会話内のすべてのメッセージをリンク。click\_idの代替として機能する | | `media_buy_id` | 開始時に渡される | セッションをトリガーしたキャンペーンにリンク | | `offering_id` | 開始時に渡される | 宣伝された特定のオファー/製品にリンク | `session_id` は `context_for_checkout` を介してACPチェックアウトに引き継がれ、インプレッション → 会話 → トランザクションのクローズドループアトリビューションを可能にします。 ## 仕組み SIがエンゲージメントを担当します。[Agentic Commerce Protocol (ACP)](https://github.com/anthropics/acp)——プログラマティックコマースのためのOpenAIとStripeによるオープン標準——がトランザクションを担当します。この分離により、ホストとのユーザーの信頼関係を維持しながら、シームレスなチェックアウトを実現できます。 ```mermaid theme={null} flowchart LR A[User Intent] --> B[Host Platform] B -.->|Optional| P[Check Availability] P -.-> C B --> C{Consent?} C -->|Yes + Identity| D[Initiate Session] C -->|Yes, Anonymous| D C -->|No| E[Continue without brand] D --> F[Brand Agent] F <--> G[Conversation Turns] G --> H{User Done?} H -->|Transaction| I[Handoff to ACP] H -->|Complete| J[Return to Host] H -->|Exit| J I --> K[Checkout Flow] ``` ### フロー 1. **ユーザーが興味を表明** → ホストがオポチュニティを特定 2. **オファリング詳細の取得**(任意)→ ホストがオファリング情報とマッチング製品を取得(`si_get_offering`) 3. **同意プロンプト** → ユーザーがアイデンティティ共有を決定 4. **セッション開始** → ホストがコンテキスト + ケイパビリティとともにブランドエージェントを呼び出す(`si_initiate_session`) 5. **会話型エンゲージメント** → ブランドエージェントがテキスト、音声、動画、または埋め込みUIを通じて対話(`si_send_message`) 6. **セッション終了** → トランザクション(ACP経由)または会話完了のためのハンドバック(`si_terminate_session`) ## SIマニフェスト:ブランドが宣言すること ブランドはエージェントのケイパビリティを宣言するSIマニフェストを公開します: ```json theme={null} { "endpoint": { "transports": [ { "type": "mcp", "url": "https://delta.com/mcp" }, { "type": "a2a", "url": "https://delta.com/.well-known/agent.json" } ], "preferred": "mcp" }, "capabilities": { "modalities": { "voice": { "provider": "elevenlabs", "voice_id": "delta_v1" }, "video": { "formats": ["mp4", "webm"], "max_duration_seconds": 60 }, "avatar": { "provider": "d-id", "avatar_id": "delta_avatar" } }, "components": { "standard": ["text", "link", "image", "product_card", "carousel", "action_button"], "extensions": { "chatgpt_apps_sdk": { "app_id": "delta-travel" } } }, "commerce": { "acp_checkout": true } }, "brand": { "domain": "delta.com" } } ``` > **注意**: すべてのSIエージェントはデフォルトで会話(テキスト)モダリティをサポートする——これはベースラインです。モダリティセクションは音声、動画、アバターなどの*追加*ケイパビリティを宣言します。 ### トランスポートオプション SIは複数のトランスポートプロトコルをサポートし、ブランドがホストの環境に合わせて対応できます: | トランスポート | 説明 | 最適なケース | | ------- | ------------------------------------- | ------------------------- | | **MCP** | Model Context Protocol - ツールベースの対話 | 構造化されたツール呼び出し、IDE統合 | | **A2A** | Agent-to-Agent Protocol - メッセージベースの対話 | リッチな非同期会話、エージェントのコラボレーション | ブランドは複数のトランスポートを宣言し、優先順位を指定できます。ホストは自分のケイパビリティに基づいて選択し、グレースフルなネゴシエーションを可能にします。 ## ケイパビリティネゴシエーション すべてのホストがすべてのケイパビリティをサポートするわけではありません。SIはケイパビリティネゴシエーションを使用する——ブランドが「できること」を宣言し、ホストが「サポートすること」を返し、セッションはその交差部分を使用します。 ``` ブランドが宣言: voice, avatar, standard components, chatgpt_apps_sdk ホストがサポート: voice, standard components, chatgpt_apps_sdk セッションで使用: voice, standard components, chatgpt_apps_sdk ``` 標準コンポーネントはどこでも機能します。拡張機能はそれをサポートするプラットフォームでよりリッチなエクスペリエンスを可能にします。ブランドは常に標準コンポーネントにフォールバックして、ユニバーサルな互換性を確保できます。 これによってグレースフルなデグラデーションが実現します。ChatGPTでフルのApps SDKエクスペリエンスとして美しく動作するブランドエージェントが、よりシンプルなホストでも機能できる——Apps SDKの代わりに標準の製品カードとカルーセルを使うだけで。 ## アイデンティティとプライバシーの同意 ユーザーがブランドエージェントとエンゲージするとき、ホストはアイデンティティを共有するかどうかを尋ねる。これがコアの価値交換です:ユーザーはパーソナライズされたサービスを得て、ブランドはリードを得ます。 ### 同意フロー ``` ユーザー: 「Deltaのフライトについて話したい」 ホスト: 「Deltaのアシスタントに接続できる。エクスペリエンスをパーソナライズするため、 あなたの情報を共有してよいか? [x] 名前とメールをDeltaと共有する [x] 配送先住所を共有する(正確な価格計算のため) 続けることで、DeltaのプライバシーポリシーへのリンクList[link]に同意する」 ユーザー: 「はい、情報を共有する」 ``` 配送先住所により、ブランドは会話中に正確な税金と送料を計算でき、より速いチェックアウトとより良いレコメンデーションにつながる。 ### なぜクリアなPII(ハッシュ化なし)か これは複数の仲介者を伴うRTBではありません。直接的な、同意を得たハンドオフです: * ユーザーが明示的に「はい、誰であるかを伝えてください」と言う * Deltaは確認メールを送るために実際のメールアドレスが必要 * ハッシュ化するとユースケースが成立しません ### 同意あり ```json theme={null} { "identity": { "consent_granted": true, "consent_timestamp": "2026-01-18T10:30:00Z", "consent_scope": ["name", "email", "shipping_address"], "privacy_policy_acknowledged": { "brand_policy_url": "https://delta.com/privacy", "brand_policy_version": "2026-01" }, "user": { "email": "user@example.com", "name": "Jane Smith", "locale": "en-US", "shipping_address": { "street": "123 Main St", "city": "New York", "state": "NY", "postal_code": "10001", "country": "US" } } } } ``` ### 同意なし(匿名) ```json theme={null} { "identity": { "consent_granted": false, "anonymous_session_id": "anon_xyz789" } } ``` ブランドは引き続きサポートできる——ただしパーソナライズやメールでのフォローアップはできません。 ## モダリティ SIは複数のインタラクションモダリティをサポートします。これらを組み合わせることができる——例えばセッションで会話テキストと埋め込み製品カルーセルを同時に使うこともあります。 ### 会話型 MCPツールまたはA2Aメッセージによる純粋なテキスト交換。すべてのSI実装がサポートするベースラインモダリティです。 ### 音声 ブランドの声を使用したオーディオベースの対話。ホストはブランドのTTS設定(ElevenLabs、OpenAIなど)を使用してオーディオをレンダリングします。 ### 動画 会話内で再生されるブランドの動画コンテンツ。製品動画、説明コンテンツ、プロモーションクリップが含まれ、ユーザーが画面を離れることなくブランドエクスペリエンスを向上させる。 ### アバター ブランドアバターによるアニメーション動画プレゼンス。D-ID、HeyGen、Synthesiaなどのプロバイダーが、話して視覚的に応答できるブランデッドビデオエージェントを可能にします。ホストはブランドが提供する設定を使用してアバターをレンダリングします。 ### ビジュアルコンポーネント SIはビジュアルエクスペリエンスへの段階的なアプローチを定義する——どこでも機能する軽量コンポーネントから、リッチなプラットフォーム固有のアプリまで。 #### 標準コンポーネント(どこでも機能します) SIはすべての準拠ホストがレンダリングしなければなりません(MUST)**標準コンポーネント**の小さなセットを定義します。AMPがモバイルウェブコンポーネントを標準化したように、これらによりブランドはプラットフォーム固有のコードを構築することなく参加できます: | コンポーネント | 目的 | データ形状 | | --------------- | ---------------- | ------------------------------------------------- | | `text` | 会話メッセージ | `{ message: string }` | | `link` | ラベル付きURL | `{ url, label, preview? }` | | `image` | 単一画像 | `{ url, alt, caption? }` | | `product_card` | 製品表示 | `{ title, price, image_url, description?, cta? }` | | `carousel` | カード/画像の配列 | `{ items: [...], title? }` | | `action_button` | コールバックをトリガーするCTA | `{ label, action, payload? }` | ブランドは構造化されたJSONデータを提供します。ホストは自身のデザインシステムに従ってレンダリングします。フレームワークへの依存はない。 ```json theme={null} { "ui_elements": [ { "type": "product_card", "data": { "title": "Boston Flight - Jan 25", "price": "$199", "image_url": "https://delta.com/images/bos.jpg", "cta": { "label": "Book Now", "action": "checkout" } } } ] } ``` #### プラットフォーム拡張機能 ホストは標準セットを超えたよりリッチなケイパビリティをサポートする場合があります。セッション開始時に、ホストはサポートする拡張機能を宣言します: ```json theme={null} { "supported_components": { "standard": ["text", "link", "image", "product_card", "carousel", "action_button"], "extensions": { "chatgpt_apps_sdk": "1.0", "maps": true, "forms": true } } } ``` ブランドは利用可能な場合に拡張機能を使用し、そうでない場合は標準コンポーネントにフォールバックできます。 #### アプリハンドオフ フルのプラットフォーム固有アプリ(ChatGPT Appsなど)を構築したブランドのために、SIは直接ハンドオフをサポートします: ```json theme={null} { "type": "app_handoff", "apps": { "chatgpt": { "app_id": "delta-travel", "deep_link": "flights/boston" }, "web": { "url": "https://delta.com/book?dest=BOS" } } } ``` これにより、ブランドは既存のアプリへの投資を活用しながら、SIプロトコルに参加できます。 #### コマースアクション 標準コンポーネントにはコマーストリガー用の `action_button` が含まれます。現在はACPチェックアウトを開始します: ```json theme={null} { "type": "action_button", "data": { "label": "Add to Cart", "action": "acp_checkout", "payload": { "sku": "DL-BOS-125", "quantity": 1 } } } ``` エコシステムが成熟するにつれ、永続カート、複数アイテムのチェックアウト、よりリッチなコマースフローへと拡張されると予想します。標準コンポーネントスキーマはこれらの拡張に対応できるよう設計されています。 #### インテグレーションアクション ブランドエージェントはユーザーに対し、より深いコネクションを確立するオプションを提供できる——ブランドをMCPツールとして追加したり、継続的なエージェントコラボレーションのためにA2Aの関係を確立したりします: ```json theme={null} { "type": "integration_actions", "data": { "actions": [ { "type": "mcp", "label": "Add as MCP Tool", "highlighted": true }, { "type": "a2a", "label": "Connect via A2A" } ] } } ``` これによりユーザーは「ブランドを持ち歩く」ことができる——ブランドのケイパビリティを自身のAI環境にインストールし、広告を通じて再発見する必要なく将来的に使用できます。これはスポンサードモーメントから永続ツールへの強力なコンバージョンパスです。 ## セッションライフサイクル SIセッションには明示的なライフサイクル管理があります。 ### セッション開始 ホスト → ブランド。コンテキスト、ケイパビリティ、アイデンティティ(同意がある場合)、メディアバイからのアクティブなオファーを含む: ```json theme={null} { "context": "User wants to fly to Boston next Tuesday morning on flight 632 at 6 AM.", "identity": { "consent_granted": true, "user": { "email": "jane@example.com", "name": "Jane Smith", "shipping_address": { /* ... */ } } }, "media_buy_id": "delta_q1_premium_upgrade", "placement": "chatgpt_search", "offering_id": "delta_chatgpt_3313", "supported_capabilities": { /* what host supports */ } } ``` `context` は会話のハンドオフだ——ホストはブランドエージェントにユーザーが必要としていることを伝え、ブランドエージェントは自然に応答します。`offering_id` はブランドが適用方法を知っているキャンペーンプロモーション(対象フライトへの無料アップグレードなど)を参照します。 **フリークエントフライヤーとロイヤリティデータ**: ブランドはユーザーのメールアドレスからこれを検索する——ホストはロイヤリティ番号を保存しません。Deltaは `jane@example.com` を認識し、SkyMilesのステータスを自動的に取得します。 ### セッションレスポンス ブランドはホストがレンダリングする構造化コンテンツを返します。ブランドエージェントが特定のインテントを使用し、オファーを適用する方法に注目: ```json theme={null} { "session_id": "sess_abc123", "response": { "message": "Hi Jane! I found DL632 departing at 6:15 AM next Tuesday. Great news—as a SkyMiles Gold member, you qualify for our free Premium Economy upgrade on this flight.", "ui_elements": [ { "type": "product_card", "data": { "title": "DL632 to Boston - Tue Jan 27", "subtitle": "6:15 AM → 9:42 AM (3h 27m)", "price": "$199", "badge": "Free Premium Economy Upgrade", "image_url": "https://delta.com/images/premium-economy.jpg", "cta": { "label": "Book with Upgrade", "action": "checkout" } } } ] } } ``` **重要な原則**: ブランドは構造化コンテンツ(カード、リンク、アクション)を返します。ホストはケイパビリティとポリシーに基づいてレンダリングするものを決定します。ブランドエージェントはJaneのメールを使ってレスポンスをパーソナライズし(SkyMilesのステータスを検索)、キャンペーンオファーを適用しました。 ### セッション終了 複数の終了理由があります: | 理由 | 意味 | 何が起きるか | | --------------------- | ---------- | --------------------- | | `handoff_transaction` | ユーザーが購入したい | ホストがACPチェックアウトを開始 | | `handoff_complete` | 会話が完了 | 通常のチャットに戻る | | `user_exit` | ユーザーが終了 | クリーンアップ、コンテキストの保存の可能性 | | `session_timeout` | 非アクティブ | 自動クリーンアップ | | `host_terminated` | ポリシー/エラー | セッション終了 | ## ACP統合 終了理由が `handoff_transaction` の場合、ホストはAgentic Commerce Protocol(ACP)経由でチェックアウトを開始します: ``` Brand Agent → Host: terminate_session(handoff_transaction) Host → ACP: Initiate checkout with Delta ACP → User: Complete purchase flow ``` SIがエンゲージメントを担当します。ACPがトランザクションを担当します。ホストとのユーザーの信頼関係は全体を通じて維持されます。 ## 価値提案 ### AIプラットフォーム(ホスト)向け * **収益化**: 回答の独立性を損なわない新しい広告フォーマット * **ユーザーエクスペリエンス**: バナー広告ではなく、ネイティブな会話型コマース * **標準ベース**: SI準拠のブランドエージェントとの相互運用性 ### ブランド向け * **ダイレクトエンゲージメント**: ユーザーが興味を持っているとき、コンテキストの中で直接対話 * **リッチなエクスペリエンス**: ミニストア、インタラクティブマップ、音声/アバタープレゼンス * **リードジェネレーション**: フォローアップのための同意済みアイデンティティ ### ユーザー向け * **パーソナライゼーション**: アイデンティティを共有して、より良いサービスを受けます * **コントロール**: 共有するものの明確な同意 * **利便性**: 会話の中でショッピング、予約、探索——すべて完結 ## オープン広告レイヤー AIにおけるコマースは標準化が進んでいる。ShopifyやWalmart、Targetなどと共同開発されたGoogleのUniversal Commerce Protocol(UCP)は、チェックアウト、支払い、フルフィルメントのためのオープンプリミティブを定義します。OpenAIとStripeのAgentic Commerce Protocol(ACP)も同様の役割を果たす。これらは良い発展だ——コマースの基盤となるオープン標準はすべての人にメリットをもたらす。 しかしギャップがあります。コマースがオープンになっている一方で、**広告レイヤー**——オファーがどのように表面化されるか、ブランドがいつ現れるか、ユーザーが何を見るか——はプロプライエタリなままです。各プラットフォームが、スポンサードコンテンツをいつどのように表示するかを決定する独自のブラックボックスを構築しています。 私たちはそのレイヤーもオープンであるべきだと考える。 **AdCPとSIはそれを定義しようとする試みです:** | レイヤー | オープン標準 | 何をするか | | ---- | -------- | ---------------------------------- | | コマース | UCP, ACP | チェックアウト、支払い、フルフィルメント | | 広告 | AdCP, SI | オファーディスカバリー、ブランドエンゲージメント、アトリビューション | 私たちが取り組んでいるプリミティブ: * **オファー宣言** — ブランドが宣伝できるもの(オファリング、有効期間) * **コンテキストシグナル** — ホストがユーザーインテントについて共有するもの(匿名、同意済み) * **選択基準** — オファーがインテントとどのようにマッチするか(キーワード、カテゴリ、可用性) * **開示** — スポンサードコンテンツのラベル付け方法 * **アトリビューション** — サーフェスをまたいだコンバージョンの計測方法 すべての答えを持っているわけではありません。「競合するオファーはどのようにランキングすべきか?」「正しい開示フォーマットは何か?」といった問いには業界の意見が必要です。しかし、これをオープンに——ブランド、プラットフォーム、ユーザーが同じテーブルに着いて——解決することは、各プラットフォームがプロプライエタリなブラックボックスを構築するよりも良いと信じています。 **AIのオープン広告レイヤーを定義することに参加してほしい。** ## 次のステップ * **技術チーム**: 上記のプロトコルコンポーネントをレビューします * **プラットフォームプロバイダー**: ホスト向けの実装に関する考慮事項を参照します * **ブランド**: SI準拠エージェントの構築方法を理解します * **すべての人**: [コミュニティ](https://join.slack.com/t/agenticads/shared_invite/zt-3c5sxvdjk-x0rVmLB3OFHVUp~WutVWZg)に参加して議論します *** *Sponsored IntelligenceプロトコルはAdCPエコシステム全体([/docs/intro](/docs/intro))の一部であり、次世代のAI駆動広告を実現します。* # 仕様 Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/specification **ドラフト仕様** — このプロトコルは開発中です。最終リリースまでに API やスキーマが変更される可能性があります。 本ドキュメントでは Sponsored Intelligence (SI) プロトコルの仕様を定義します。本文中の "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" の語は [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) に従って解釈します。 ## Protocol Overview SI プロトコルは、AI アシスタント(ホスト)がブランドエージェントのエンドポイントを呼び出し、会話型のブランド体験を提供する方法を定義します。プロトコルは次で構成されます。 1. **ディスカバリー** - ホストがブランドエージェントとその機能を発見する方法 2. **提供内容の照会** - セッション引き継ぎ前の匿名チェック 3. **セッション管理** - 開始、メッセージ交換、終了 4. **機能ネゴシエーション** - 利用できる機能の決定 5. **UI コンポーネント** - 描画用の標準的なビジュアル要素 ## Transport Requirements ### サポートするトランスポート ブランドエージェントは、以下のうち少なくとも 1 つのトランスポートをサポートしなければなりません。 | Transport | Protocol | Description | | --------- | ---------------------- | ----------------------- | | MCP | Model Context Protocol | JSON-RPC によるツールベースのやり取り | | A2A | Agent-to-Agent | メッセージベースのやり取り | 推奨トランスポートとして MCP をサポートすることが望まれます。 ### トランスポートの宣言 ブランドエージェントは `get_adcp_capabilities` でサポートするトランスポートを宣言します。 ```json theme={null} { "adcp": { "major_versions": [2] }, "supported_protocols": ["sponsored_intelligence"], "sponsored_intelligence": { "endpoint": { "transports": [ { "type": "mcp", "url": "https://brand.example/mcp" } ], "preferred": "mcp" }, "capabilities": { ... }, "brand_manifest_url": "https://brand.example/.well-known/brand-manifest.json" } } ``` 複数のトランスポートを宣言する場合、レスポンスには `preferred` フィールドを含めることが望まれます。 ## Discovery ### 機能ディスカバリー ブランドエージェントは SI 対応を宣言するために `get_adcp_capabilities` タスクを実装しなければなりません。ホストがこのタスクを呼び出したとき、レスポンスには次を必ず含めます。 * `supported_protocols` 配列内の `sponsored_intelligence` * 次を含む `sponsored_intelligence` オブジェクト: * `endpoint` - トランスポートの設定(必須) * `capabilities` - サポートするモダリティとコンポーネント(必須) レスポンスには以下を含めることが望まれます。 * `brand_manifest_url` - ブランドアイデンティティの参照 ## Get Offering ### Purpose `si_get_offering` タスクはセッション引き継ぎ前に提供内容と提供可否を取得します。ホストはブランドとのエンゲージメントに同意を求める前に、価格や在庫などの情報をユーザーへ提示できます。 ### Requirements ホストはセッション開始前に `si_get_offering` を呼び出してもかまいません。 `si_get_offering` を呼び出す場合: 1. リクエストにユーザーの PII を含めてはいけません 2. リクエストには `offering_id` を含める必要があります 3. パーソナライズ結果のために `context` を含めてもかまいません(例: "mens size 14 near Cincinnati") 4. 一致する商品を得るために `include_products: true` を設定してもかまいません 5. ブランドエージェントは可能であれば `offering_token` を返さなければなりません 6. ブランドエージェントは有効期限を示す `ttl_seconds` を返すことが望まれます ### Offering Token Flow ホストが `offering_token` を受け取った場合: 1. 後続の `si_initiate_session` リクエストにこのトークンを含めることが望まれます 2. ブランドエージェントはトークンを使って照会とセッションを関連付けることができます 3. ホストはトークンを不透明な値として扱わなければなりません ```json theme={null} { "offering_token": "offering_abc123xyz" } ``` ### Matching Products `include_products` が true で `context` が与えられている場合、レスポンスに一致する商品を含めてもかまいません。 ```json theme={null} { "available": true, "offering_token": "offering_abc123xyz", "offering": { "title": "Nike Summer Sale", "summary": "Up to 50% off summer collection", "price_hint": "from $89" }, "matching_products": [ { "product_id": "nike-air-max-90", "name": "Nike Air Max 90", "price": "$129", "availability_summary": "Size 14 in stock" } ], "total_matching": 12 } ``` これにより、セッション開始前にリッチなプレビューを提示できます。 ### Sponsored Context Accountability Offering レスポンスとセッションレスポンスは、返された offering、マッチする商品、message、または UI 要素がホスト境界に入るスポンサードコンテキストである場合、`sponsored_context` を含めてもかまいません(MAY)。宣言は 3 つの事実を分離します。 | Field | Purpose | | ----------------------- | ----------------------------------------------------------------------------- | | `paying_principal` | 経済的説明責任: コンテキストに資金提供またはスポンサーしたブランド。任意のセラーアカウント/オペレーターコンテキスト付き | | `context_use` | 宣言されたホスト側の使用モード: `presentation_only`、`comparison_set`、または `reasoning_context` | | `disclosure_obligation` | ホストがコンテキストを使う前に受け入れて満たすか拒否するかしなければならない開示 | スポンサードコンテキストの説明責任には 4 つのハンドオフポイントがあります。 | Handoff point | Accountability fact | | ------------- | ---------------------------------------------------------------------------------------------------------------------- | | プロバイダー宣言 | ブランド/セラーが、コンテキストがホストサーフェスに入る前に、`paying_principal`、`context_use`、`disclosure_obligation` を含む `sponsored_context` を宣言する。 | | ホストレシート | ホストが、スポンサードコンテキストを受け入れたか拒否したか、受け入れたコンテキストについては受け入れた宣言使用モードと開示コミットメントを記録する。 | | ユーザー向け開示 | `disclosure_obligation.required` が true のとき、ホストは、スポンサードコンテキストの前または横で、ユーザー向けサーフェスに適切な開示をレンダリングする説明責任を負う。 | | 監査証拠 | 当事者は、支払いプリンシパル、宣言された使用モード、開示義務、ホストレシート、およびホストが記録する任意のレンダリングされた開示証拠をリンクする証拠証跡を保持できる。 | このモデルは**提示開示**を**推論影響**から分離します。提示開示は、必要なときにホストがレンダリングするユーザーに見えるラベル、カード処理、通知、または同等の開示です。推論影響は、スポンサードコンテキストが比較セット、ランキング、生成された回答、プラン、またはモデル/オーケストレーションコンテキストを形成してよいかどうかです。`context_use` は許可される影響境界を宣言し、`disclosure_obligation` はユーザー向けの開示義務を宣言します。 | `context_use` | Applies when | | ------------------- | ------------------------------------------------------------------------------------------------------------------- | | `presentation_only` | コンテキストが、スポンサードカード、回答ブロック、ブランドエージェントハンドオフなどの別個のラベル付きユニットとしてレンダリングまたは提供される。 | | `comparison_set` | コンテキストが比較、ランキング、または選択のためのスポンサード候補セットを形成する。これは `matching_products` に自然に適用される: 返された商品は、すべてのアイテムがレンダリングされなくても比較を形成しうる。 | | `reasoning_context` | コンテキストが、回答生成、プランニング、ランキング、またはその他の推論のためにホストモデルまたはオーケストレーション層に利用可能であることを意図する。 | 宣言は、将来の拡張が個々のアイテムに狭めない限り、返された offering と `matching_products` パッケージ全体に適用されます。 ホストは、スポンサードコンテキストを受け入れる、または明示的に拒否するとき、`paying_principal`、宣言された `context_use`、`disclosure_obligation`、ホストレシートをリンクする監査記録を保持すべきです(SHOULD)。ホストが後続で `si_initiate_session` または `si_send_message` を呼び出すとき、その決定をブランド/セラーに見えるようにするために `sponsored_context_receipt` を含めてもかまいません(MAY)。レシートは受信サーフェスの説明責任事実を記録します: ホストがコンテキストを受け入れたか、受け入れたレシートについてはどの使用モードと開示コミットメントを行ったか。 受け入れられたレシートについて: * `accepted_context_use` は宣言の `context_use` に一致しなければなりません(MUST) * `disclosure_obligation.required` が true のとき `disclosure_commitment.status` は `accepted` でなければなりません(MUST) * `disclosure_obligation.required` が false のときのみ `disclosure_commitment.status` は `not_required` であってもかまいません(MAY) 宣言された使用モードを尊重できない、または必要な開示義務を満たせないホストは、受け入れられたレシートを送る代わりにスポンサードコンテキストを拒否しなければなりません(MUST)。 拒否されたレシートについては、`accepted_context_use` と `disclosure_commitment` は存在してはなりません(MUST)。拒否されたレシートは、ホストがスポンサードコンテキストを受け入れなかったまたは使わなかったことを、理由を説明する任意の `rejection_reason` とともに記録します。 これは境界コントラクトです。AdCP は隠されたモデルの推論を検査せず、思考の連鎖を標準化せず、ホストモデルがレシート後に内部でコンテキストをどう使うかを保証しません。宣言された使用モードまたは開示義務を尊重できない準拠ホストは、黙ってダウンスコープしたり開示なしに使ったりするのではなく、スポンサードコンテキストを拒否しなければなりません(MUST)。 ## Session Lifecycle ### Session States SI セッションには次の状態があります。 | State | Description | | ----------------- | ---------------------- | | `active` | セッションが進行中 | | `pending_handoff` | ブランドがコマースフローへのハンドオフを要求 | | `complete` | セッションが正常終了 | ### Session State Transitions ``` si_initiate_session ──▶ active │ ├── si_send_message ──▶ pending_handoff │ │ │ └── si_terminate_session ──▶ complete (terminal) │ (handoff_transaction │ or handoff_complete) │ ├── si_send_message ──▶ complete (terminal) │ (conversation concluded) │ └── si_terminate_session ──▶ terminated (terminal) (user_exit, session_timeout, or host_terminated) Any non-terminal ── si_terminate_session(user_exit/timeout/host) ──▶ terminated ``` **ルール:** * ブランドエージェントは、成功時に `si_initiate_session` から `session_status: "active"` を返さなければなりません(MUST) * ブランドエージェントは、すべての `si_send_message` レスポンスで `session_status` を返さなければなりません(MUST) * `session_status` が `pending_handoff` のとき、レスポンスは `handoff` オブジェクトを含まなければなりません(MUST) * ブランドエージェントは、会話がコマースまたはチェックアウトの意図に達したとき、任意の `si_send_message` レスポンスで `active` から `pending_handoff` に遷移してもかまいません(MAY) * ブランドエージェントは、会話が結論に達したとき(例: 質問に回答済み、追加のアクション不要)、`si_send_message` レスポンスで `active` から直接 `complete` に遷移してもかまいません(MAY) * ホストは、セッションを終了するために `si_terminate_session` を呼び出さなければなりません(MUST)。ブランドエージェントは、任意の非終端状態からの終了を受け入れなければなりません(MUST)。 * ブランドエージェントは、未知または期限切れのセッションに送られたメッセージについて `SESSION_NOT_FOUND` を返さなければなりません(MUST) * ブランドエージェントは、`complete` または `terminated` 状態のセッションに送られたメッセージについて `SESSION_TERMINATED` を返さなければなりません(MUST)。情報開示の最小化を優先するブランドエージェントは、終了したセッションについても `SESSION_NOT_FOUND` を返してもかまいません(MAY)— 回復パスは両方のケースで同一です。 * 終端状態は不可逆です — セッションが `complete` または `terminated` になると、新しいセッションを開始しなければなりません ### Session Timeout セッションは最大非アクティブタイムアウトを持つべきです(SHOULD)。ブランドエージェントは、アイドルセッションを `terminated` に遷移させることでタイムアウトを強制してもかまいません(MAY)。 * ブランドエージェントは、一定期間の非アクティブ後にセッションを期限切れとして扱うべきです(SHOULD。推奨: 会話セッションで 5 分) * ブランドエージェントは、期限切れセッションに送られたメッセージについて、黙って新しいセッションを作成するのではなく `SESSION_NOT_FOUND` を返すべきです(SHOULD) * ホストは `last_active_at` を追跡し、可能な場合はセッションタイムアウト前にユーザーに警告すべきです(SHOULD) * ブランドエージェントは、タイムアウト期間をホストに伝えるために `si_initiate_session` レスポンスに `session_ttl_seconds` を含めてもかまいません(MAY) ### Initiate Session `si_initiate_session` タスクは新しい SI セッションを確立します。 #### Request Requirements ホストは次を必ず含めなければなりません。 * `context` - ユーザー意図の自然言語説明 * `identity` - 同意状態を含むユーザーのアイデンティティ ホストは次を含めることが望まれます。 * `supported_capabilities` - ネゴシエーション用のホスト側機能セット * `offering_token` - `si_get_offering` を実行した場合のトークン ホストは次を含めてもかまいません。 * `media_buy_id` - 広告起点の場合の AdCP メディアバイ ID * `offering_id` - 適用するブランド固有のオファー * `placement` - セッションがトリガーされた場所 #### Response Requirements ブランドエージェントは次を必ず返さなければなりません。 * `session_id` - セッションの一意識別子 ブランドエージェントは次を返すことが望まれます。 * `response.message` - 最初の会話メッセージ * `negotiated_capabilities` - ブランドとホストの機能の交差集合 ### Send Message `si_send_message` タスクはアクティブなセッション内でメッセージをやり取りします。 #### Request Requirements ホストは次を必ず含めなければなりません。 * `session_id` - アクティブなセッション ID さらに次のいずれかを必ず含めます。 * `message` - ユーザーのテキストメッセージ * `action_response` - UI アクションへの応答 #### Response Requirements ブランドエージェントは次を必ず返さなければなりません。 * `session_id` - セッション ID * `session_status` - 現在のセッション状態(`active`、`pending_handoff`、`complete`) ブランドエージェントは次を返すことが望まれます。 * `response.message` - 会話の応答 `session_status` が `pending_handoff` の場合、レスポンスには必ず次を含めます。 * `handoff` - コマースフローへのハンドオフ設定 ### Terminate Session `si_terminate_session` タスクは SI セッションを終了します。 #### Request Requirements ホストは次を必ず含めなければなりません。 * `session_id` - 終了するセッション ID * `reason` - 終了理由 #### Termination Reasons | Reason | Description | | --------------------- | ----------------- | | `handoff_transaction` | ユーザーが購入に進む | | `handoff_complete` | 会話が正常に完了した | | `user_exit` | ユーザーがセッションを終了した | | `session_timeout` | 非アクティブによるタイムアウト | | `host_terminated` | ホストがポリシー/エラーで終了した | #### Handoff Data `reason` が `handoff_transaction` のとき、ブランドエージェントは終了レスポンスで `acp_handoff` オブジェクトを返すべきです(SHOULD)。 | Field | Type | Description | | ---------------- | -------- | ------------------------------------------------------------------------------------------------ | | `checkout_url` | uri | ブランドの ACP チェックアウトエンドポイント。ホストは開く前にこれが HTTPS であることを検証しなければなりません(MUST。Security Considerations を参照)。 | | `checkout_token` | string | チェックアウトエンドポイントに渡す不透明トークン。SI セッションをトランザクションと相関させる。 | | `payload` | object | リッチなチェックアウトコンテキスト(商品詳細、適用オファー、価格)。構造化データが必要な統合のための `checkout_token` の代替。 | | `expires_at` | datetime | このハンドオフデータが期限切れになる時刻。ホストはこの時刻の前にチェックアウトを開始すべきです(SHOULD)。 | ブランドエージェントは、ホストがセッションコンテキストをチェックアウトエンドポイントに渡せるよう、`checkout_token` または `payload`(または両方)を含めるべきです(SHOULD)。 ブランドエージェントは、セッション後のコンテキスト(例: 議論した内容のサマリー、次のステップ)を持つ `follow_up` オブジェクトを返してもかまいません(MAY)。 ## Capability Negotiation ### Negotiation Process 1. ブランドが SI マニフェストで機能を宣言します 2. ホストがセッション開始時にサポート機能を送る 3. ブランドがレスポンスでネゴシエート済み(交差)の機能を返す 4. セッションは交差した機能のみを使用します ### Capability Categories #### Modalities モダリティはインタラクションのモードを定義します。 | Modality | Description | Required Support | | ---------------- | -------------- | ---------------- | | `conversational` | テキストでのやり取り | すべての実装で必須 | | `voice` | 音声によるインタラクション | 任意 | | `video` | 動画コンテンツの再生 | 任意 | | `avatar` | アバターによる動画プレゼンス | 任意 | すべての SI 実装は `conversational` モダリティをサポートしなければなりません。 #### Standard Components 準拠するすべてのホストは次のコンポーネントを描画できなければなりません。 | Component | Purpose | | --------------- | ----------------- | | `text` | 会話メッセージ | | `link` | ラベル付き URL | | `image` | 単一画像 | | `product_card` | CTA を含む商品表示 | | `carousel` | カード/画像の配列 | | `action_button` | コールバックをトリガーする CTA | #### Extension Components ホストは追加コンポーネントをサポートしてもかまいません。 | Component | Purpose | | --------------------- | -------------------- | | `app_handoff` | プラットフォーム固有アプリへのハンドオフ | | `integration_actions` | MCP/A2A 追加のプロンプト | ブランドエージェントはコア機能を拡張コンポーネントに依存してはいけません。 ## UI Element Requirements ### Standard Component Data 各スタンダードコンポーネントは `si-ui-element.json` で定義された必須フィールドを含めなければなりません。 **text**: `message`(必須) **link**: `url`, `label`(必須); `preview`(任意) **image**: `url`, `alt`(必須); `caption`(任意) **product\_card**: `title`, `price`(必須); `subtitle`, `image_url`, `description`, `badge`, `cta`(任意) **carousel**: `items`(必須); `title`(任意) **action\_button**: `label`, `action`(必須); `payload`(任意) ### Action Handling ユーザーが `action_button` を操作した場合: 1. ホストは `si_send_message` を介して `action_response` を送信しなければなりません 2. `action_response` には `action` 識別子を含めなければなりません 3. `payload` が提供されている場合、`action_response` に含めることが望まれます ### Integration Actions `integration_actions` コンポーネントは、ブランドエージェントが恒久的な接続を提案するためのものです。 ```json theme={null} { "type": "integration_actions", "data": { "actions": [ { "type": "mcp", "label": "Add as MCP Tool", "highlighted": true }, { "type": "a2a", "label": "Connect via A2A" } ] } } ``` ホストは、その統合タイプをサポートしている場合に限り integration actions を描画してもかまいません。 ## Identity and Privacy ### Consent Requirements ホストはブランドエージェントとアイデンティティを共有する前に、ユーザーの明示的な同意を得なければなりません。 同意フローでは次を必ず行います。 1. 共有するデータを明示します 2. ブランドのプライバシーポリシーを参照させる 3. ユーザーに拒否する選択肢を提供します ### Identity Object 同意が得られた場合、`identity` オブジェクトには次を必ず含めます。 * `consent_granted: true` * `consent_timestamp` - 同意を取得した時刻 * `consent_scope` - 同意したデータ種別の配列 * `privacy_policy_acknowledged.brand_policy_url` `user` オブジェクトには次を含めてもかまいません。 * `email` * `name` * `locale` * `shipping_address` ### Anonymous Sessions 同意が得られない場合: * `identity.consent_granted` は必ず `false` * `identity.anonymous_session_id` を提供することが望まれます * PII を送信してはなりません ## Commerce Integration ### ACP Handoff `session_status` が `pending_handoff` で `handoff.type: "transaction"` の場合: 1. ホストは ACP のチェックアウトフローを開始することが望まれます 2. `handoff.intent` には購入意図を記述しなければなりません 3. `handoff.context_for_checkout` には会話コンテキストを含めてもかまいません ### Commerce Actions `action_button` コンポーネントにはコマースアクションを含めてもかまいません。 | Action | Description | | -------------- | -------------- | | `acp_checkout` | ACP チェックアウトを開始 | | `add_to_cart` | 永続カートに追加 | ## Error Handling ### Error Response ブランドエージェントは標準のエラースキーマを使い、`errors` 配列でエラーを返さなければなりません。 ```json theme={null} { "errors": [ { "code": "session_not_found", "message": "セッションが期限切れ、または存在しません" } ] } ``` ### Error Codes | Code | Description | | ------------------------ | ------------------- | | `session_not_found` | セッション ID が無効または期限切れ | | `offer_unavailable` | 参照されたオファーが利用不可 | | `capability_unsupported` | 必要な機能が利用不可 | | `rate_limited` | リクエストが多すぎる | ## Security Considerations ### Transport Security すべての SI 通信は TLS 1.2 以上の HTTPS を使用しなければなりません。 ### Token Security * Availability トークンは不透明かつ予測不能でなければなりません * セッション ID は一意で予測不能でなければなりません * トークンは妥当な期間内に期限切れにすることが望まれます ### Handoff URL Validation ホストは、ユーザーに提示する前に `acp_handoff` データの `checkout_url` を検証しなければなりません(MUST)。ホストは `https` スキームに制限すべきで(SHOULD)、ドメインがブランドエージェントの登録ドメインに一致することを検証してもかまいません(MAY)。ホストは、ハンドオフデータから `javascript:`、`data:`、その他の非 HTTPS URI を開いてはなりません(MUST NOT)。 ### Data Minimization * 同意なしにホストは PII を送信してはなりません * ブランドエージェントはデータ収集を最小限にすることが望まれます * セッション終了後はセッションデータを削除することが望まれます ## Conformance ### Host Conformance 準拠する SI ホストは次を満たさなければなりません。 1. MCP トランスポートをサポートします 2. すべてのスタンダードコンポーネントを描画します 3. セッションライフサイクル(開始、送信、終了)を実装します 4. アイデンティティ共有前に同意を取得します 5. 機能ネゴシエーションをサポートします ### Brand Agent Conformance 準拠する SI ブランドエージェントは次を満たさなければなりません。 1. SI マニフェストを公開します 2. 指定されたトランスポートの少なくとも 1 つをサポートします 3. 会話モダリティをサポートします 4. 有効なセッション ID を返す 5. すべての終了理由を処理します ## Version History | Version | Date | Changes | | ------- | ------- | ------- | | 1.0.0 | 2025-01 | 初版 | # タスクリファレンス Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/tasks/index Sponsored Intelligence プロトコルは、会話型ブランド体験を管理するために 4 つのタスクを定義しています: ## セッションライフサイクル ```mermaid theme={null} flowchart LR P[si_get_offering] -.-> A[si_initiate_session] A --> B[si_send_message] B --> B B --> C[si_terminate_session] A --> C ``` ### 2 つのエントリポイント **事前参照あり**(点線): ホストが先に `si_get_offering` を呼び、同意前に商品を提示します。`offering_token` が提示した内容をセッションに引き継ぎます。 **直接セッション**(実線): ホストが直接 `si_initiate_session` を呼びます。ブランドエージェントは会話の一部として商品を提示し、内部で追跡します。 どちらも有効です。ユーザーに参加を求める前に商品をプレビューしたいスポンサード検索結果では、事前参照を使用してください。 ## タスク | Task | Description | Initiator | | ------------------------------------------------ | -------------------------- | --------- | | [`si_get_offering`](./si_get_offering) | オファー詳細、在庫状況、マッチする商品を取得(匿名) | Host | | [`si_initiate_session`](./si_initiate_session) | ブランドエージェントとの会話を開始 | Host | | [`si_send_message`](./si_send_message) | アクティブセッション内でメッセージを交換 | Host | | [`si_terminate_session`](./si_terminate_session) | 適切なハンドオフでセッションを終了 | Either | ## トランスポートオプション SI タスクは MCP と A2A の両プロトコルで動作します: ### MCP トランスポート ```json theme={null} { "method": "tools/call", "params": { "name": "si_initiate_session", "arguments": { "context": "User wants to fly to Boston next Tuesday morning", "identity": { /* ... */ } } } } ``` ### A2A トランスポート ```json theme={null} { "task": "si_initiate_session", "payload": { "context": "User wants to fly to Boston next Tuesday morning", "identity": { /* ... */ } } } ``` ## よくあるパターン ### 最小限のセッション(アイデンティティなし) パーソナライズなしの匿名ブラウジング用: ```json theme={null} { "context": "User interested in product information", "identity": { "consent_granted": false, "anonymous_session_id": "anon_xyz789" } } ``` ### フルアイデンティティのセッション 同意済み PII を使ったパーソナライズ体験用: ```json theme={null} { "context": "User wants to book a flight", "identity": { "consent_granted": true, "consent_timestamp": "2026-01-18T10:30:00Z", "consent_scope": ["name", "email", "shipping_address"], "user": { "email": "jane@example.com", "name": "Jane Smith" } } } ``` ### キャンペーンから起動するセッション メディアバイの一部として SI が呼び出される場合: ```json theme={null} { "context": "User searching for flights to Boston", "media_buy_id": "media_buy_q1_promo", "placement": "chatgpt_search", "offering_id": "premium_upgrade_offer", "identity": { "consent_granted": true, "user": { "email": "jane@example.com" } } } ``` # si_get_offering Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/tasks/si_get_offering セッション開始前に提供内容の詳細や提供可否、必要に応じて一致する商品を取得します。これにより、ホストはブランドとの連携について同意を求める前に、ユーザーへリッチなプレビューを提示できます。 ## このタスクを使うタイミング SI セッションを開始する有効なフローは 2 つあります。 ### フロー A: セッション前の照会(スポンサード結果に推奨) ``` si_get_offering → ホストが商品を提示 → ユーザーが同意 → offering_token を付与して si_initiate_session ``` 同意を求める**前に**商品を見せたい場合に使用します。`offering_token` がセッション前のコンテキストを橋渡しするため、「2 つ目のもの」といった参照が成立します。 **例**: 検索結果ページで、ユーザーがエンゲージする前に「Nike にはあなたのサイズのランニングシューズが 3 足あり、\$89 から」と表示します。 ### フロー B: 直接セッション ``` si_initiate_session → ブランドが最初の応答で商品を提示 → 会話を継続 ``` ユーザーがすでにエンゲージする意図を示している場合に使います。ブランドエージェントがセッション内で商品を提示し、何を提示したかを内部で追跡します。 **例**: ユーザーが「Nike とランニングシューズの話をしたい」と言う場合は、事前取得は不要でそのままセッションを開始します。 重要な違い: `si_get_offering` は **同意前の匿名プレビュー** 用です。すぐにセッションへ進むなら省略できます。 ## 目的 オファーの照会には 3 つの目的があります。 1. **提供内容の提示** - 同意前に価格、提供可否、説明を表示します 2. **一致する商品の提示** - コンテキストがあれば、関連商品を返す 3. **セッション継続性** - 返されるトークンで提示内容を保持し、セッション開始時にブランドエージェントが文脈を把握できるようにします ## リクエスト | Field | Type | Required | Description | | ------------------ | ------- | -------- | ------------------------------ | | `offering_id` | string | Yes | プロモートされた提供内容の ID | | `context` | string | No | パーソナライズのための自然言語コンテキスト(PII は禁止) | | `include_products` | boolean | No | 一致する商品を含めるか(デフォルト: false) | | `product_limit` | integer | No | 返す商品の最大数(デフォルト: 5、最大: 50) | ### プライバシー このリクエストには個人を特定できる情報を含めてはいけません。`context` フィールドには意図の説明を記載できますが、匿名である必要があります(例: "mens size 14 near Cincinnati" は可、メールアドレスは不可)。 ## レスポンス | Field | Type | Description | | -------------------------- | ------- | ----------------------------- | | `available` | boolean | 提供が現在利用可能か | | `offering_token` | string | `si_initiate_session` に渡すトークン | | `ttl_seconds` | integer | 情報が有効な秒数 | | `checked_at` | string | 照会時刻の ISO 8601 タイムスタンプ | | `offering` | object | 提供内容の詳細 | | `matching_products` | array | コンテキストに一致する商品(要求された場合) | | `total_matching` | integer | 一致商品の総数(返却数を上回る場合あり) | | `unavailable_reason` | string | 提供不可の理由(利用不可の場合) | | `alternative_offering_ids` | array | 代替オファーの候補 | ### Offering Object | Field | Type | Description | | ------------- | ------ | --------------------- | | `offering_id` | string | 提供内容の ID | | `title` | string | タイトル | | `summary` | string | 簡潔な説明 | | `tagline` | string | 短い宣伝フレーズ | | `expires_at` | string | 提供終了時刻 | | `price_hint` | string | 価格の目安(例: "from \$89") | | `image_url` | string | ヒーロー画像の URL | | `landing_url` | string | ランディングページ URL | ### Matching Product Object | Field | Type | Description | | ---------------------- | ------ | ------------ | | `product_id` | string | 商品 ID | | `name` | string | 商品名 | | `price` | string | 表示価格 | | `original_price` | string | セール時の元価格 | | `image_url` | string | 商品画像の URL | | `availability_summary` | string | 簡単な在庫情報 | | `url` | string | 商品詳細ページの URL | ### Sponsored Context Object 返された offering または `matching_products` がホスト境界に入るスポンサードコンテキストである場合、レスポンスレベルで `sponsored_context` を含めます。返されるパッケージ全体に適用されます。 | Field | Type | Description | | ----------------------- | ------ | ----------------------------------------------------------------------------- | | `paying_principal` | object | スポンサードコンテキストについて経済的に説明責任を負うブランド。任意のセラーアカウント/オペレーターコンテキスト付き | | `context_use` | string | 宣言されたホスト側の使用モード: `presentation_only`、`comparison_set`、または `reasoning_context` | | `disclosure_obligation` | object | ホストがコンテキストを使う前に受け入れて満たすか拒否するかしなければならない開示 | `matching_products` が比較、ランキング、または選択のためのスポンサード候補セットである場合は `context_use: "comparison_set"` を使います。別個のスポンサードユニットまたはハンドオフには `presentation_only` を、コンテキストがホストの回答生成、プランニング、ランキング、またはその他の推論に利用可能であることを意図する場合にのみ `reasoning_context` を使います。パッケージを受け入れるホストは、監査証跡が支払いプリンシパル、宣言された使用モード、開示義務、ホストレシートをリンクするよう、後続の `si_initiate_session` リクエストに `sponsored_context_receipt` を含められます。 ### Unavailable Reasons | Reason | Description | | ------------------- | -------------- | | `sold_out` | 商品/オファー在庫が枯渇 | | `expired` | 提供期間が終了 | | `region_restricted` | ユーザー地域では提供不可 | | `inactive` | キャンペーンが停止または終了 | ## 例 ### 基本的な提供内容の照会 ```json theme={null} { "offering_id": "nike-summer-sale" } ``` ### Response ```json theme={null} { "available": true, "offering_token": "offering_abc123xyz", "ttl_seconds": 3600, "checked_at": "2025-01-19T10:00:00Z", "offering": { "offering_id": "nike-summer-sale", "title": "Nike Summer Sale", "summary": "Up to 50% off summer collection", "price_hint": "from $89", "expires_at": "2025-08-31T23:59:59Z" } } ``` ### 商品コンテキストを付与 ```json theme={null} { "offering_id": "nike-summer-sale", "context": "mens size 14 running shoes near Cincinnati", "include_products": true, "product_limit": 3 } ``` ### 商品を含むレスポンス ```json theme={null} { "available": true, "offering_token": "offering_abc123xyz", "ttl_seconds": 3600, "checked_at": "2025-01-19T10:00:00Z", "offering": { "offering_id": "nike-summer-sale", "title": "Nike Summer Sale", "summary": "Up to 50% off summer collection", "price_hint": "from $89" }, "matching_products": [ { "product_id": "nike-pegasus-41", "name": "Nike Pegasus 41", "price": "$89", "original_price": "$130", "image_url": "https://cdn.nike.com/pegasus-41.jpg", "availability_summary": "Size 14 in stock" }, { "product_id": "nike-air-max-90", "name": "Nike Air Max 90", "price": "$129", "image_url": "https://cdn.nike.com/air-max-90.jpg", "availability_summary": "Size 14 in stock" } ], "total_matching": 12 } ``` これによりホストは次のように提示できます。 > 「Nike にはあなたのサイズのランニングシューズが 12 足あり、\$89 からです。アシスタントと詳しく見てみますか?」 ### Response with Sponsored Context ```json theme={null} { "$schema": "/schemas/sponsored-intelligence/si-get-offering-response.json", "status": "completed", "available": true, "offering_token": "offering_abc123xyz", "matching_products": [ { "product_id": "trail-pace-14", "name": "Trail Pace 14", "price": "$89", "availability_summary": "Size 14 in stock" } ], "sponsored_context": { "paying_principal": { "brand": { "domain": "acme-running.example" }, "display_name": "Acme Running" }, "context_use": "comparison_set", "disclosure_obligation": { "required": true, "label_text": "Sponsored results from Acme Running", "timing": "near_each_influenced_output", "proximity": "near_influenced_output" }, "declared_by": { "agent_url": "https://agent.acme-running.example/si", "role": "brand_agent" }, "declared_at": "2025-01-19T10:00:00Z" }, "total_matching": 1 } ``` ### 提供不可の場合のレスポンス ```json theme={null} { "available": false, "checked_at": "2025-01-19T10:00:00Z", "unavailable_reason": "expired", "alternative_offering_ids": [ "nike-fall-collection", "nike-clearance" ] } ``` ## Offering トークンの利用 `offering_token` は **セッション継続性** の鍵です。`si_get_offering` で商品を表示した後に会話を開始するとき、このトークンによりブランドエージェントは何が提示されたかを正確に把握できます。 ### セッション継続性が重要な理由 Without the token, this conversation breaks: ``` Host: "Nike has 3 running shoes in size 14: Pegasus 41 ($89), Air Max 90 ($129), Vomero 18 ($139)" User: "Tell me more about the second one" Host → si_initiate_session: { context: "User wants more info about the second shoe" } Brand Agent: ??? (Which shoes were shown? In what order?) ``` With the token, the brand agent can reconstruct the full context: ``` Host → si_initiate_session: { context: "User wants more info about the second shoe", offering_token: "offering_abc123xyz" } Brand Agent: (Looks up token → sees Pegasus, Air Max, Vomero were shown in that order) Brand Agent: "The Air Max 90 is a classic! It's part of our summer sale..." ``` ### ブランドエージェントがトークンを使う方法 `offering_token` を生成する際は、クエリの状態をサーバーサイドに保存します。 ```typescript theme={null} // When returning si_get_offering response const token = generateToken(); await store.save(token, { offering_id: request.offering_id, context: request.context, products_shown: matchingProducts, // In exact order returned product_ids: matchingProducts.map(p => p.product_id), queried_at: new Date().toISOString(), ttl: 3600 }); return { available: true, offering_token: token, matching_products: matchingProducts, // ... }; ``` `si_initiate_session` でトークンを受け取ったら: ```typescript theme={null} // Retrieve the pre-session context const preContext = await store.get(request.offering_token); if (preContext) { // Now you know exactly what was shown // "the second one" = preContext.products_shown[1] } ``` ### セッション開始時にトークンを含めます 提供内容を取得した後にセッションを開始する場合、トークンを含めます。 ```json theme={null} { "context": "User wants running shoes, mens size 14", "offering_id": "nike-summer-sale", "offering_token": "offering_abc123xyz", "identity": { "consent_granted": true, "user": { ... } } } ``` ## 重要ポイント 1. **匿名設計** - 提供内容の照会にはユーザーデータを送信しません。プライバシーを守りつつリッチなプレビューを実現します。 2. **セッション継続性** - Offering トークンは提示内容の記憶です。「最初の選択肢」や「あの青いもの」といった参照をブランドエージェントが解決できます。 3. **商品マッチング** - `include_products` が true で `context` があれば、ブランドは関連商品を返せます。「あなたのサイズの靴が 12 足、\$89 から」といったプレビューを実現します。 4. **キャッシュ** - ホストは `ttl_seconds` までレスポンスをキャッシュできます。頻繁に照会されるオファーでブランドエージェントの負荷を下げます。 5. **段階的なフォールバック** - 照会が失敗またはタイムアウトしても、ホストはセッションを直接開始できます。照会は必須ではありません。 6. **代替提案** - 提供不可の場合は `alternative_offering_ids` を通じて代替案を提示できます。 ## ベストプラクティス ### ホスト向け * スポンサード結果を表示する前に提供内容を取得します * リッチなプレビューにはコンテキスト付きで `include_products` を使います * TTL を守ってキャッシュし、古いデータを避ける * 提供不可の場合の扱いを丁寧にし、期限切れのオファーを表示しません * 可能であればセッション開始時に offering トークンを含めます ### ブランドエージェント向け * 正確な表示のためにリッチな `offering` 情報を返す * コンテキストに合わせた商品マッチングのため `include_products` をサポートします * 妥当な TTL 値を設定する(変動に応じて 5~60 分など) * デバッグに役立つ `unavailable_reason` を提供します * 主要なオファーが利用できない場合は代替案を提案します # si_initiate_session Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/tasks/si_initiate_session ブランドエージェントとの会話セッションを開始します。ユーザーがブランドとのやり取りに興味を示したとき、ホストプラットフォームがこのタスクを呼び出します。 ## リクエスト | Field | Type | Required | Description | | ------------------------ | ------ | -------- | ------------------------------------- | | `context` | string | Yes | ユーザー意図の自然言語記述 | | `identity` | object | Yes | 同意ステータスを含むユーザーアイデンティティ | | `media_buy_id` | string | No | 広告起点の場合の AdCP メディアバイ ID | | `placement` | string | No | セッションがトリガーされた場所(例: "chatgpt\_search") | | `offering_id` | string | No | 適用するブランド固有のオファー参照 | | `supported_capabilities` | object | No | ホストプラットフォームがサポートする機能 | | `offering_token` | string | No | 相関用の `si_get_offering` からのトークン | ### Offering Token 開始前にホストが [`si_get_offering`](./si_get_offering) 参照を行った場合、**セッション継続性** のためトークンを含めます: ```json theme={null} { "offering_token": "offering_abc123xyz" } ``` トークンは、ユーザーに何がどの順で提示されたかをブランドエージェントに伝えます。これにより自然な会話の流れが可能になります: * ユーザーが見る: "Nike Pegasus ($89), Air Max ($129), Vomero (\$139)" * ユーザーが言う: "真ん中のものについて詳しく教えて" * ブランドエージェントはトークンに保存されたコンテキストを用いて「真ん中のもの」→ Air Max と解釈します ### Sponsored Context Receipt ホストがセッション前の照会からのスポンサードコンテキストを受け入れた、または明示的に拒否した場合、ホストの境界決定をブランド/セラーに見えるようにするために `sponsored_context_receipt` を含めます。 ```json theme={null} { "offering_token": "offering_abc123xyz", "sponsored_context_receipt": { "sponsored_context": { "paying_principal": { "brand": { "domain": "acme-running.example" }, "display_name": "Acme Running" }, "context_use": "comparison_set", "disclosure_obligation": { "required": true, "label_text": "Sponsored results from Acme Running" } }, "host_receipt": { "status": "accepted", "accepted_context_use": "comparison_set", "received_at": "2025-01-19T10:00:02Z", "host_surface": "assistant_comparison", "disclosure_commitment": { "status": "accepted", "label_text": "Sponsored results from Acme Running" } } } } ``` レシートは、ホストがスポンサードコンテキストを受け入れたか拒否したかを記録します。受け入れられたレシートについては、ホストが尊重することをコミットした内容を記録します。AdCP が隠されたモデルの推論を検査できることを主張するものではありません。 | Receipt field | Description | | ------------------------------------ | ----------------------------------------------------- | | `sponsored_context` | 承認される送信者宣言: 支払いプリンシパル、宣言された使用モード、開示義務 | | `host_receipt.status` | ホストが宣言を尊重した場合は `accepted`。コンテキストを使わなかった場合は `rejected` | | `host_receipt.accepted_context_use` | 受け入れられたレシートで必須。宣言の `context_use` に一致しなければならない | | `host_receipt.disclosure_commitment` | 受け入れられたレシートで必須。ホストの開示コミットメントを記録 | | `host_receipt.rejection_reason` | ホストがスポンサードコンテキストを拒否する場合の任意の説明 | 受け入れられたレシートは、使用モードをダウンスコープしたり、必要な開示を辞退したりできません。いずれの条件も尊重できないホストは、代わりにスポンサードコンテキストを拒否します。拒否されたレシートは `accepted_context_use` と `disclosure_commitment` を省略しなければなりません。 ### Identity オブジェクト `consent_granted` が `true` の場合: | Field | Type | Required | Description | | ----------------------------- | ------- | -------- | -------------------- | | `consent_granted` | boolean | Yes | `true` 固定 | | `consent_timestamp` | string | Yes | 同意の ISO 8601 タイムスタンプ | | `consent_scope` | array | Yes | ユーザーが共有に同意したフィールド | | `privacy_policy_acknowledged` | object | No | ユーザーが承諾したブランドポリシー | | `user` | object | Yes | ユーザーの PII | `consent_granted` が `false` の場合: | Field | Type | Required | Description | | ---------------------- | ------- | -------- | --------------- | | `consent_granted` | boolean | Yes | `false` 固定 | | `anonymous_session_id` | string | Yes | この匿名セッションの一意 ID | ### Supported Capabilities オブジェクト ホストプラットフォームがレンダリングできるものを宣言します: ```json theme={null} { "modalities": { "conversational": true, "voice": { "providers": ["elevenlabs", "openai"] }, "video": false, "avatar": false }, "components": { "standard": ["text", "link", "image", "product_card", "carousel", "action_button"], "extensions": { "chatgpt_apps_sdk": "1.0" } }, "commerce": { "acp_checkout": true } } ``` ## レスポンス | Field | Type | Description | | ------------------------- | ------ | ------------------ | | `session_id` | string | このセッションの一意識別子 | | `response` | object | ブランドエージェントの初回レスポンス | | `negotiated_capabilities` | object | ブランドとホストの機能の交差集合 | ### Response オブジェクト | Field | Type | Description | | ------------- | ------ | -------------------- | | `message` | string | ブランドエージェントからのテキスト応答 | | `ui_elements` | array | レンダリングするビジュアルコンポーネント | ## 例 ### リクエスト ```json theme={null} { "context": "User wants to fly to Boston next Tuesday morning on flight 632 at 6 AM.", "media_buy_id": "delta_q1_premium_upgrade", "placement": "chatgpt_search", "offering_id": "delta_chatgpt_3313", "identity": { "consent_granted": true, "consent_timestamp": "2026-01-18T10:30:00Z", "consent_scope": ["name", "email"], "privacy_policy_acknowledged": { "brand_policy_url": "https://delta.com/privacy", "brand_policy_version": "2026-01" }, "user": { "email": "jane@example.com", "name": "Jane Smith", "locale": "en-US" } }, "supported_capabilities": { "modalities": { "conversational": true, "voice": true }, "components": { "standard": ["text", "link", "image", "product_card", "carousel", "action_button"] }, "commerce": { "acp_checkout": true } } } ``` ### レスポンス ```json theme={null} { "session_id": "sess_abc123", "response": { "message": "Hi Jane! I found DL632 departing at 6:15 AM next Tuesday. Great news—as a SkyMiles Gold member, you qualify for our free Premium Economy upgrade on this flight.", "ui_elements": [ { "type": "product_card", "data": { "title": "DL632 to Boston - Tue Jan 27", "subtitle": "6:15 AM → 9:42 AM (3h 27m)", "price": "$199", "badge": "Free Premium Economy Upgrade", "image_url": "https://delta.com/images/premium-economy.jpg", "cta": { "label": "Book with Upgrade", "action": "checkout" } } } ] }, "negotiated_capabilities": { "modalities": { "conversational": true, "voice": true }, "components": { "standard": ["text", "link", "image", "product_card", "carousel", "action_button"] }, "commerce": { "acp_checkout": true } } } ``` ## キーポイント 1. **コンテキストは会話ハンドオフ** - ホストがブランドエージェントにユーザーのニーズを伝え、ブランドエージェントが自然に会話を続けます。 2. **ブランドがロイヤルティデータを参照** - Jane のメールが認識されれば、Delta が SkyMiles ステータスを自動取得します。ホストはロイヤルティ番号を保持しません。 3. **offering\_id はブランド固有** - ブランドがこの参照を解釈し、プロモーションや割引、ロイヤルティ特典を適用します。ホストはオファーの意味を理解せずに渡すだけです。 4. **機能ネゴシエーション** - レスポンスの `negotiated_capabilities` には、このセッションで利用できる機能(ブランドとホストの交差)が示されます。 5. **明示的同意のある PII 受け渡し** - `consent_granted` が true の場合、実際のメール/名前が渡されます(ハッシュなし)。これは同意に基づく直接のハンドオフです。 # si_send_message Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/tasks/si_send_message アクティブな SI セッション内でメッセージを送信します。ホストはこのタスクを呼び出し、ユーザーメッセージやアクションレスポンスをブランドエージェントに中継します。 ## リクエスト | Field | Type | Required | Description | | ----------------- | ------ | -------- | ------------------------------- | | `session_id` | string | Yes | `si_initiate_session` のセッション ID | | `message` | string | No | ユーザーのテキストメッセージ | | `action_response` | object | No | UI アクション(ボタンクリック、フォーム送信)へのレスポンス | `message` または `action_response` のいずれかは必須です。 ### Action Response オブジェクト ユーザーが UI 要素とやり取りした場合: | Field | Type | Required | Description | | ------------ | ------ | -------- | ---------------- | | `action` | string | Yes | UI 要素由来のアクション識別子 | | `element_id` | string | No | 特定の UI 要素の ID | | `payload` | object | No | やり取りから得た追加データ | ## レスポンス | Field | Type | Description | | ---------------- | ------ | ---------------------------------------------- | | `session_id` | string | アクティブなセッションの確認 | | `response` | object | ブランドエージェントからのレスポンス | | `session_status` | string | 現在のセッション状態 | | `handoff` | object | `session_status` が "pending\_handoff" のときに含まれる | ### Sponsored Context Receipts 以前の `si_initiate_session` または `si_send_message` レスポンスが `sponsored_context` を含んでいた場合、ホストは、そのコンテキストを提示、比較、またはその他の方法で使用する前に、宣言された `context_use` と `disclosure_obligation` を尊重できるかどうかを決定します。後続の `si_send_message` リクエストで、ホストは通常の `message` または `action_response` とともに `sponsored_context_receipt` を含めて、その決定をブランド/セラーに見えるようにしてもかまいません(MAY)。 受け入れられたレシートでは、`accepted_context_use` が宣言の `context_use` に一致しなければならず、開示が必要だった場合は `disclosure_commitment.status` が `accepted` でなければなりません。拒否されたレシートでは、`accepted_context_use` と `disclosure_commitment` を省略します。ホストがコンテキストを受け入れなかった、または使わなかった理由を説明したい場合は `rejection_reason` を使います。 `si_send_message` レスポンスが新しい `sponsored_context` を含む場合、ホストが受信サーフェスでそのレスポンスコンテキストを使う前に、同じ受け入れまたは拒否の決定が適用されます。ホストは次のターンでレシートを含めるか、次の SI 呼び出しがない場合は自身の監査記録に保持できます。 ### Session Status 値 | Status | Description | | ----------------- | ----------------------- | | `active` | セッションが通常継続中 | | `pending_handoff` | ブランドエージェントがハンドオフ準備完了を示す | | `complete` | 会話が完了 | ### Handoff オブジェクト `session_status` が `pending_handoff` の場合: | Field | Type | Description | | ---------------------- | ------ | ---------------------------- | | `type` | string | "transaction" または "complete" | | `intent` | object | 取引の場合: ユーザーが購入したい内容 | | `context_for_checkout` | object | ACP ハンドオフ用のサマリー | ## 例 ### シンプルなメッセージ交換 **Request:** ```json theme={null} { "session_id": "sess_abc123", "message": "Do you have any earlier flights?" } ``` **Response:** ```json theme={null} { "session_id": "sess_abc123", "response": { "message": "Yes! There's DL628 departing at 5:30 AM. It's a bit earlier but also qualifies for the Premium Economy upgrade.", "ui_elements": [ { "type": "carousel", "data": { "items": [ { "title": "DL628 - 5:30 AM", "subtitle": "Arrives 8:57 AM", "price": "$199", "badge": "Free Upgrade" }, { "title": "DL632 - 6:15 AM", "subtitle": "Arrives 9:42 AM", "price": "$199", "badge": "Free Upgrade" } ] } } ] }, "session_status": "active" } ``` ### アクションレスポンス(ボタンクリック) **Request:** ```json theme={null} { "session_id": "sess_abc123", "action_response": { "action": "select_flight", "payload": { "flight_number": "DL628", "departure_time": "05:30" } } } ``` **Response:** ```json theme={null} { "session_id": "sess_abc123", "response": { "message": "Great choice! DL628 is confirmed with your Premium Economy upgrade. Ready to book?", "ui_elements": [ { "type": "product_card", "data": { "title": "DL628 to Boston", "subtitle": "Tue Jan 27, 5:30 AM → 8:57 AM", "price": "$199", "badge": "Premium Economy", "cta": { "label": "Book Now", "action": "checkout" } } } ] }, "session_status": "active" } ``` ### トランザクションハンドオフ ユーザーが購入準備完了の場合: **Request:** ```json theme={null} { "session_id": "sess_abc123", "action_response": { "action": "checkout" } } ``` **Response:** ```json theme={null} { "session_id": "sess_abc123", "response": { "message": "Perfect! I'll hand you back to complete the booking." }, "session_status": "pending_handoff", "handoff": { "type": "transaction", "intent": { "action": "purchase", "product": { "type": "flight", "flight_number": "DL628", "departure": "2026-01-27T05:30:00-05:00", "arrival": "2026-01-27T08:57:00-05:00", "origin": "JFK", "destination": "BOS", "class": "premium_economy" }, "price": { "amount": 199, "currency": "USD" } }, "context_for_checkout": { "conversation_summary": "Jane selected DL628 JFK→BOS on Jan 27 with free Premium Economy upgrade via campaign offer", "applied_offers": ["delta_chatgpt_3313"] } } } ``` ## ハンドオフの扱い `session_status: "pending_handoff"` を受け取ったら: 1. **`type: "transaction"` の場合** - 提供された intent とコンテキストで ACP チェックアウトを開始します 2. **`type: "complete"` の場合** - 会話完了として通常チャットに戻る ハンドオフ処理後、ホストは `si_terminate_session` を呼び出して適切にセッションを閉じるべきです。 ## キーポイント 1. **message または action\_response** - 各リクエストには少なくともどちらかが必要です。ユーザーはメッセージ入力か UI 操作でやり取りします。 2. **セッションステータスがフローを決める** - 各レスポンスで `session_status` を確認し、会話継続かハンドオフ必要かを判断します。 3. **ハンドオフでコンテキストを保持** - `context_for_checkout` オブジェクトが ACP にシームレスな購入体験に必要な情報を提供します。 4. **UI 要素は任意** - カードやカルーセルをいつ含めるかは、ブランドエージェントが会話に応じて判断します。 # si_terminate_session Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/tasks/si_terminate_session SI セッションを終了します。ホストまたはブランドエージェントのいずれからでも終了を開始でき、理由によってセッションの締め方が示されます。 ## リクエスト | Field | Type | Required | Description | | --------------------- | ------ | -------- | -------------- | | `session_id` | string | Yes | 終了するセッション ID | | `reason` | string | Yes | セッション終了の理由 | | `termination_context` | object | No | 終了に関する追加コンテキスト | ### 終了理由 | Reason | Meaning | Typical Initiator | | --------------------- | ----------------- | ------------------------------- | | `handoff_transaction` | ユーザーが購入を完了したい | ブランドエージェント(pending\_handoff 経由) | | `handoff_complete` | 会話が自然に終了 | ブランドエージェント | | `user_exit` | ユーザーが明示的に会話を終了 | Host | | `session_timeout` | 非アクティブのタイムアウト到達 | Host | | `host_terminated` | ポリシー/エラー理由でホストが終了 | Host | ### Termination Context オブジェクト 追加詳細は理由によって異なります: **`handoff_transaction` の場合:** ```json theme={null} { "intent": { /* purchase intent from handoff */ }, "context_for_checkout": { /* ACP context */ } } ``` **`user_exit` の場合:** ```json theme={null} { "user_signal": "changed_topic", "partial_context": { /* what was discussed */ } } ``` **`session_timeout` の場合:** ```json theme={null} { "last_activity": "2026-01-18T10:30:00Z", "timeout_seconds": 300 } ``` ## レスポンス | Field | Type | Description | | ------------- | ------- | --------------------- | | `session_id` | string | どのセッションが終了したかの確認 | | `terminated` | boolean | 成功時は常に `true` | | `acp_handoff` | object | 取引ハンドオフ時に含まれる | | `follow_up` | object | 将来のエンゲージメント向けの任意アクション | ### ACP Handoff オブジェクト 取引終了の場合、ACP チェックアウトに必要なデータを含みます: | Field | Type | Description | | -------------- | ------ | ------------------------------- | | `checkout_url` | string | ブランドの ACP チェックアウトエンドポイント | | `payload` | object | ACP に渡すデータ | | `expires_at` | string | チェックアウトコンテキストの ISO 8601 形式の有効期限 | ### Follow-Up オブジェクト 将来のエンゲージメントに向けた提案: | Field | Type | Description | | ------------------ | ------ | ---------------- | | `suggested_action` | string | ホストが次に行うべきこと | | `data` | object | そのアクションに関する関連データ | | `message` | string | 表示用の任意メッセージ | ## 例 ### トランザクションハンドオフ `type: "transaction"` の `pending_handoff` を受け取った後: **Request:** ```json theme={null} { "session_id": "sess_abc123", "reason": "handoff_transaction", "termination_context": { "intent": { "action": "purchase", "product": { "type": "flight", "flight_number": "DL628" } } } } ``` **Response:** ```json theme={null} { "session_id": "sess_abc123", "terminated": true, "acp_handoff": { "checkout_url": "https://delta.com/acp/checkout", "payload": { "session_id": "sess_abc123", "flight": "DL628", "passenger": { "email": "jane@example.com", "name": "Jane Smith" }, "applied_offers": ["delta_chatgpt_3313"], "price": { "amount": 199, "currency": "USD" } }, "expires_at": "2026-01-18T11:00:00Z" } } ``` ### Conversation Complete (No Purchase) トランザクションなしで会話が自然に終わる場合: **Request:** ```json theme={null} { "session_id": "sess_abc123", "reason": "handoff_complete" } ``` **Response:** ```json theme={null} { "session_id": "sess_abc123", "terminated": true, "follow_up": { "suggested_action": "save_for_later", "data": { "flights_discussed": ["DL628", "DL632"], "destination": "BOS", "travel_date": "2026-01-27" }, "message": "Let me know if you'd like to revisit Boston flights later!" } } ``` ### ユーザー退出 ユーザーが話題を変える、または明示的に離脱する場合: **Request:** ```json theme={null} { "session_id": "sess_abc123", "reason": "user_exit", "termination_context": { "user_signal": "changed_topic", "partial_context": { "flights_viewed": ["DL628"], "last_topic": "seat selection" } } } ``` **Response:** ```json theme={null} { "session_id": "sess_abc123", "terminated": true, "follow_up": { "suggested_action": "remind_later", "data": { "incomplete_booking": { "flight": "DL628", "step": "seat_selection" } } } } ``` ### セッションタイムアウト 非アクティブによるタイムアウトの場合: **Request:** ```json theme={null} { "session_id": "sess_abc123", "reason": "session_timeout", "termination_context": { "last_activity": "2026-01-18T10:25:00Z", "timeout_seconds": 300 } } ``` **Response:** ```json theme={null} { "session_id": "sess_abc123", "terminated": true } ``` ### ホストによる終了 ホストがポリシーやエラー理由でセッションを終了する場合: **Request:** ```json theme={null} { "session_id": "sess_abc123", "reason": "host_terminated", "termination_context": { "cause": "user_left_app" } } ``` **Response:** ```json theme={null} { "session_id": "sess_abc123", "terminated": true } ``` ## ACP 連携フロー 理由が `handoff_transaction` の場合: ```mermaid theme={null} sequenceDiagram participant H as Host participant B as Brand Agent participant A as ACP H->>B: si_terminate_session(handoff_transaction) B->>H: { acp_handoff: { checkout_url, payload } } H->>A: Initiate checkout with payload A->>H: Checkout flow ``` 1. ホストは終了レスポンスで `acp_handoff` を受け取ります 2. ホストは提供された `checkout_url` と `payload` を使って ACP チェックアウトを開始します 3. ACP がトランザクションを処理し、ユーザーとホストの信頼関係を維持します 4. ブランドは加盟店にはならず、支払いは ACP が処理します ## キーポイント 1. **常にセッションを終了する** - 会話が終わったように見えても terminate を呼び、リソースクリーンアップとフォローアップ提案を得ます。 2. **ACP ハンドオフデータには有効期限がある** - `expires_at` フィールドがチェックアウトコンテキストの有効期間を示します。 3. **フォローアップで再エンゲージメントを促す** - 取引がない終了でも将来のエンゲージメント提案を含められます。 4. **ホストが信頼を維持する** - 取引は ACP を経由し、ユーザーとホストの関係性を保つ。 # エンドツーエンドのワークフロー Source: https://adcp-docs-ja.pier1.co.jp/docs/sponsored-intelligence/workflow Sponsored Intelligence のワークフロー全体 — アカウント設定からカタログ同期、プロダクト探索、メディアバイの作成、配信レポートまで。 # エンドツーエンドのワークフロー ## アカウントを設定します まず、セラーのケイパビリティを確認してアカウントモデルを把握します。ファーストパーティ AI プラットフォームは通常、明示的なアカウント(各広告主が OAuth で認証します)を必要とします。一方、[アドネットワーク](/docs/sponsored-intelligence/networks)は暗黙的なアカウント(エージェントが `sync_accounts` でブランドを宣言します)を使用する場合があります。 **例: ファーストパーティ AI プラットフォーム(明示的アカウント)** ```json theme={null} { "adcp": { "major_versions": [3] }, "supported_protocols": ["media_buy", "creative"], "account": { "require_operator_auth": true, "supported_billing": ["operator"], "authorization_endpoint": "https://ads.ai-platform.example.com/oauth/authorize", "required_for_products": false, "sandbox": true } } ``` 主なシグナル: * **`require_operator_auth: true`** — 各広告主が OAuth で認証します * **`sandbox: true`** — 統合検証用のテストアカウントが利用可能 * **`required_for_products: false`** — バイヤーはアカウントを設定する前にプロダクトを閲覧できます 複数プラットフォームをまとめるアドネットワークは、`require_operator_auth: false` と `supported_billing: ["operator", "agent"]` を宣言する可能性が高い。この場合、エージェントは信頼され、`sync_accounts` でアカウントを宣言します。 明示的アカウントの場合、OAuth 認証後に `list_accounts` で利用可能なアカウントを探索する: ```json theme={null} { "accounts": [ { "account_id": "acct_novabrand_ai_001", "name": "Nova Brand - AI Platform", "status": "active", "sandbox": false }, { "account_id": "acct_novabrand_ai_sandbox", "name": "Nova Brand - Sandbox", "status": "active", "sandbox": true } ] } ``` 実際の予算を使う前に、サンドボックスアカウントで統合全体を検証します。 ## カタログを同期します これが最も重要なステップだ — プロダクトとオファリングのデータをプラットフォームに投入し、広告生成やトランザクションの素材を提供します。プロダクトカタログはクリエイティブ生成に活用されます。オファリングカタログはプロモーションやコマースのハンドオフを可能にします。フィードが充実しているほど、プラットフォームはインテントとインベントリのマッチングを改善できます。 ```json theme={null} { "account": { "account_id": "acct_novabrand_ai_001" }, "catalogs": [ { "catalog_id": "product-feed", "name": "Nova Brand Product Catalog", "type": "product", "url": "https://novabrand.example.com/products.xml", "feed_format": "google_merchant_center", "update_frequency": "daily" }, { "catalog_id": "offerings-feed", "name": "Nova Brand Promotions", "type": "offering", "url": "https://novabrand.example.com/offerings.json", "feed_format": "custom", "update_frequency": "weekly" } ] } ``` プラットフォームは各フィードを取り込む: * **プロダクトカタログ** — タイトル、説明、価格、画像がスポンサードレスポンス生成に活用されます * **オファリングカタログ** — [SI Chat Protocol](/docs/sponsored-intelligence/si-chat-protocol) のブランドエクスペリエンスハンドオフ向けのプロモーション、サービス、シーズンキャンペーン 広告を改善するには、投入するデータを改善する必要がある — カタログ、コンバージョンイベント、ブランドアイデンティティ、コンテンツ基準です。カタログには詳細な説明、複数の画像、構造化された属性を含めます。プラットフォームが何が効果的かを把握できるよう、コンバージョンイベントを送信します。プラットフォームはこれらのデータすべてから広告を生成する — インプットが充実しているほど、アウトプットの質が向上します。 ## プロダクトを探索します `get_products` に `channels: ["sponsored_intelligence"]` を指定して Sponsored Intelligence プロダクトを検索します: ```json theme={null} { "buying_mode": "brief", "brief": "Promote our new wireless headphones to tech-savvy consumers on AI platforms.", "brand": { "domain": "novabrand.example.com" }, "filters": { "channels": ["sponsored_intelligence"] } } ``` セラーはブリーフに一致するプロダクトを返します。カタログ連動型プロダクトの場合、セラーはどのカタログアイテムが対象かを示す `catalog_match` を含めることがあります。プロダクトタイプの全体像については[プロダクトスペクトラム](/docs/sponsored-intelligence/product-spectrum)を参照。 ## メディアバイを作成します メディアバイは複数の Sponsored Intelligence プロダクトタイプにまたがることができる: ```json theme={null} { "account": { "account_id": "acct_novabrand_ai_001" }, "brand": { "domain": "novabrand.example.com" }, "start_time": "2026-04-01T00:00:00Z", "end_time": "2026-04-30T23:59:59Z", "packages": [ { "product_id": "sponsored_response_assistant", "pricing_option_id": "sr_cpc", "budget": 10000, "bid_price": 2.50, "pacing": "even", "optimization_goals": [{ "kind": "metric", "metric": "engagements", "target": { "kind": "cost_per", "value": 3.00 }, "priority": 1 }] }, { "product_id": "ai_search_sponsored", "pricing_option_id": "search_cpc", "budget": 5000, "bid_price": 3.00, "pacing": "even", "targeting_overlay": { "keyword_targets": [ { "keyword": "wireless headphones", "match_type": "broad" }, { "keyword": "noise cancelling", "match_type": "phrase" }, { "keyword": "bluetooth earbuds", "match_type": "broad" } ] } } ] } ``` スポンサードレスポンスのパッケージは、エンゲージメント単価を最適化するためにメトリクスゴールを持つ `optimization_goals` を使用します。AI 検索パッケージは、関連するクエリにリーチするために `keyword_targets` を使用します。 ## 配信レポート 配信レポートには、標準の配信データとともにエンゲージメント指標が含まれます: ```json theme={null} { "reporting_period": { "start": "2026-04-01T00:00:00Z", "end": "2026-04-14T23:59:59Z" }, "currency": "USD", "media_buy_deliveries": [ { "media_buy_id": "mb_ai_001", "status": "active", "totals": { "impressions": 125000, "spend": 7200 }, "by_package": [ { "package_id": "pkg_sr_001", "pricing_model": "cpc", "rate": 2.40, "currency": "USD", "impressions": 85000, "spend": 4800, "clicks": 2000, "delivery_status": "delivering" }, { "package_id": "pkg_search_001", "pricing_model": "cpc", "rate": 3.00, "currency": "USD", "impressions": 40000, "spend": 2400, "clicks": 800, "delivery_status": "delivering" } ] } ] } ``` 指標の定義とコンバージョントラッキングについては[測定](/docs/sponsored-intelligence/measurement)を参照。 # トラスト & セキュリティ Source: https://adcp-docs-ja.pier1.co.jp/docs/trust AdCP が検証可能性と監督のため決定をどう構造化するか — プロトコルが提供する継ぎ目と、デプロイヤーが責任を負うもの。 AdCP はビルディングブロックで、コンプライアンスの近道ではありません。プロトコルは、デプロイヤーが義務を果たせる構造化フィールドを公開します — それ自体は適合性評価を実行したり、ポリシーを強制したり、結果を保証したりしません。AdCP がしないことの明示的なリストについては [既知の制限](/docs/reference/known-limitations) を参照してください。 AdCP のトラスト姿勢は単一の構造原則に基づきます: **単一のエージェントが一方的に行動できず、すべての決定はチェックしたい任意の当事者によって暗号的に再検証可能。** ガバナンスエージェントは資金が動く前にプランを検証します。JWS 署名付き `governance_context` がすべてのバイとともに移動し、セラーがバイヤーの言葉を信頼せずに独立に認可を確認できます。コンプライアンスランナーは `runner-output.json` からストーリーボード出力を数か月後に再実行し、規制当局が見るのと同じ pass/fail 行を生成できます。 AdCP がしないことはデプロイヤーポリシーを強制することです。継ぎ目はフックです — デプロイヤーがそれらを自身のガバナンスプラットフォーム、ポリシーレジストリ、人間レビューワークフローに配線します。キャンペーンを承認する権限は人間定義のルールに留まります。プロトコルはそれを運び、署名し、監査可能にします。 このページは、AdCP デプロイを評価する CISO、コンプライアンスレビュアー、調達チームのため 7 つのトラスト表面をマップします。各表面について: AdCP が何を提供するか、明示的に何を提供しないか、正準詳細をどこで見つけるか。7 つすべての表面にわたるライブ作業は [Trust, Identity, and Governance マスター issue (#3925)](https://github.com/adcontextprotocol/adcp/issues/3925) の下で追跡されます。 *** ## Governance **AdCP が提供するもの。** 資金を使うエージェントが決して支出を承認するエージェントでない三者構造。オーケストレーターは [`sync_plans`](/docs/governance/campaign/specification) 経由でプランを提案します。独立に運用されるガバナンスエージェントが、オーケストレーターが進む前に [`check_governance`](/docs/governance/campaign/specification) 経由でデプロイヤー構成のポリシーに対して各プランを検証します。すべてのガバナンス決定が [`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs) エントリーを生成します — 不変、タイムスタンプ付き、再現可能。 **AdCP が提供しないもの。** `check_governance` は継ぎ目で、強制者ではありません。ガバナンスエージェントを構成していないセラー、または誤構成されたセラーは `check_governance` をまったく呼びません — プロトコルは非適合セラーがトランザクションするのを防ぎません。規制されたバーティカル(クレジット、保険、雇用、住宅)は、AdCP 3.0 で名指しされた 3 つのカテゴリー(`fair_housing`、`fair_lending`、`fair_employment`)にスキーマレベルの強制を得ますが、他のすべての規制されたカテゴリー — 政治、製薬、ギャンブル、金融プロモーション — はガバナンスエージェント実装に依存します。[既知の制限 — Governance](/docs/reference/known-limitations#governance) セクションを参照。 → [ガバナンス概要](/docs/governance/overview) · [埋め込まれた人間の判断](/docs/governance/embedded-human-judgment) · [ポリシーレジストリ](/docs/governance/policy-registry) · [Annex III と Art 22 の義務](/docs/governance/annex-iii-obligations) *** ## 規制 **AdCP が提供するもの。** デプロイヤーが EU AI Act Annex III と GDPR Article 22 の義務を果たせる構造化フィールド: 人間の監督(Art. 14)の `plan.human_review_required`、入力データ統制(Art. 10)の `policy_categories` と `restricted_attributes`、自動ログ(Art. 12)の `get_plan_audit_logs`、発見可能な異議申立連絡先(Art. 22(3))としての `brand.data_subject_contestation`。 **AdCP が提供しないもの。** AdCP は適合性評価、DPIA、異議処理を実行しません。それらはデプロイヤーの責任のままです。[Annex III と Art 22 の義務](/docs/governance/annex-iii-obligations) の上部の Warning ブロックが権威的フレーミングです。 → [Annex III と Art 22 の義務](/docs/governance/annex-iii-obligations) *** ## プライバシー **AdCP が提供するもの。** スキーマレベルの PII 制御: `sync_audiences` の `hashed_email` と `hashed_phone` フィールドが平文を拒否。Trusted Match Protocol の構造的プライバシー — TMP でクロス当事者データリークを防ぐ分離されたコードパスとスキーマ禁止。仕様の他の場所にプロトコルレベルの PII トランスポートなし。 **AdCP が提供しないもの。** 構造的プライバシーは TMP にのみ適用されます。他のドメインは契約上の機密性またはセッションごとの同意に依存します。AdCP は規範的同意シグナル(IAB TCF、GPP、または同等物)を運びません。越境転送の合法性は当事者の契約と構成の性質です。[既知の制限 — Security and Privacy](/docs/reference/known-limitations#security-and-privacy) を参照。 → [プライバシー考慮事項](/docs/reference/privacy-considerations) · [Trusted Match Protocol](/docs/trusted-match/index) *** ## アイデンティティ **AdCP が提供するもの。** エージェントがトランザクションするすべての当事者の発見可能なアイデンティティ。 ハウスは `/.well-known/brand.json` で [`brand.json`](/docs/brand-protocol/brand-json) を公開し、企業ドメイン、Keller 型関係(`master` / `sub_brand` / `endorsed` / `independent`)を持つブランドポートフォリオ、デジタルプロパティ、認可されたオペレーター(ドメインによるエージェンシーとパートナー)、ハウスレベルの商標クレーム、検証可能な署名鍵のためのエージェントごとの JWKS URI を宣言します。パブリッシャーは [`adagents.json`](/docs/governance/property/adagents) を公開し、どのセールスエージェントがどのプロパティを販売またはどの公開されたシグナル定義を再販する認可を受けているかを、エージェントごとのパブリッシャー証明の `signing_keys` とともに宣言します。 双方向検証チェーンが 2 つを結びつけます: 委譲とネットワークパスは、セラーの `brand.json` `properties[].relationship` がパブリッシャーの `adagents.json` `delegation_type` に一致することを要求します。ファーストパーティ在庫はインライン所有権として解決します。ブランドプロトコルの相互主張モデル(RFC [#3533](https://github.com/adcontextprotocol/adcp/issues/3533)) — 子ブランドが `parent_house` を宣言、親ハウスが `brand_refs[]` 経由で相互にする — は、下流消費者が直接行動できる 5 状態トラストシグナル(`inline` / `mutual_assertion` / `one_sided_brand` / `one_sided_house` / `standalone`)を生成します。 `(Spec)` と `(Live)` 修飾子を持つ [AAO Verified](/docs/building/verification/aao-verified) マークは、セラーの実広告サーバー統合に対して実行される正準テストキャンペーンを通じて動作適合性を継続的に証明します。 **AdCP が提供しないもの — 知るべき 3 つのギャップ。** 第一: **集約された公開レジストリアイデンティティクレームなし。** brand.json はハウスレベルの商標クレームと自己主張のブランド関係を運びます。それは、関連する事実を既に検証する公開レジストリに対するクレームを集約する一般化された `identifiers[]` ブロック — 法人には LEI / GLEIF、商標登録には USPTO / EUIPO / WIPO Madrid、CA 証明の商標→ドメインバインディングには Verified Mark Certificates、公開アイデンティティには Wikidata Q-ID、公開企業アイデンティティには SEC EDGAR CIK — を運びません。アイデンティティクレームはスプーフとルックアライクドメインに対して防御します。それらは正当な brand.json のホスティングインフラの侵害に対して防御しません — その脅威は Security 表面で対処されます。この層の集約 RFC はトラストマスター issue の下で追跡されます。 第二: **`adagents.json` に対称なバイヤー側認可プリミティブなし。** ブランドは、どのバイヤーエージェントが自身に代わってトランザクションする認可を受けているかを単一の発見可能な場所で宣言できません。最も近い既存プリミティブは `brand.json` の `authorized_operators[]` で、オペレータードメインでスコープします — エージェントエンドポイントではなく、特定のバイヤーエージェント JWKS へのブランドからの署名付きバインディングなし。認可されたオペレーターのドメインの侵害されたエージェントは、そのオペレーターをリストするすべてのブランドで一方的にトランザクションできます。RFC [#2307](https://github.com/adcontextprotocol/adcp/issues/2307) はリクエスト署名のためのバイヤー側 agents.json を提案します。より広範な認可層ギャップはそれと並んで追跡されます。 第三: **プロトコルにオペレーター/人間 KYC プリミティブなし。** プロトコルは、人間または組織オペレーターが KYC プロバイダー(Persona、Stripe Identity、Onfido)によってアイデンティティ検証された、または権威的 IdP にルートされたという証明を運びません。KYC はメンバーシップとアカウント層に委ねられます。プロトコル側では、暗号的事実(どの鍵がどのメッセージに署名したか)のみが規範的です。[既知の制限 — Authentication and Identity](/docs/reference/known-limitations#authentication-and-identity) を参照。 **在庫と製品クレーム。** バイヤーが [`get_products`](/docs/media-buy/task-reference/get_products) レスポンスを評価するとき、上のチェーンは *誰が問題の在庫を販売する認可を受けているか* を確立します: セラーオペレーターの `brand.json` がエージェントと代表されるプロパティを宣言、プロパティ所有者の `adagents.json` がそのエージェントを認可、レスポンス自体がオペレーターの JWKS から解決された鍵またはパブリッシャーの `adagents.json` でピン留めされた鍵で RFC 9421 署名されます。チェーンが確立しないのは、特定の製品ライン — 可用性ウィンドウ、価格、在庫ボリューム — が配信時に現実を反映するかです。カタログの正確性はプロトコル証明されません: パブリッシャーは個別の製品エントリーに署名せず、製品ごとの証明は在庫が本番でどう運用されるかに一致しません。配信時の真実は測定レポートと課金照合フロー([#2391](https://github.com/adcontextprotocol/adcp/issues/2391))に存在します。誤動作する認可されたセラーは、プロトコル内クレームチェックではなく、パブリッシャーが `adagents.json` エントリーを取り消すことで修復されます。これは在庫に適用された C2PA の「クレーム非認証」姿勢です: AdCP は認可クレームを運びそれを検証可能にします。主張された在庫が存在することを認証しません。 → [brand.json](/docs/brand-protocol/brand-json) · [adagents.json とエージェントアイデンティティ](/docs/governance/property/adagents) · [AAO Verified](/docs/building/verification/aao-verified) *** ## セキュリティ **AdCP が提供するもの。** 変更する呼び出しの署名付きリクエスト(3.1 で規範的。3.0 で許可 — 下記参照)とアウトバウンド webhook 配信(それを要求するレシーバーのオプトイン HMAC フォールバック付き)のベースラインメカニズムとしての RFC 9421 HTTP メッセージ署名。リプレイ攻撃を防ぐすべての状態変更操作の冪等性キー。盗まれたトークンの爆発半径を制限する `(agent, account)` ごとの認証情報スコーピング。すべてのメディアバイとともに移動しセラーがバイヤーを信頼せず独立に検証可能な JWS 署名付きガバナンスコンテキスト。 **AdCP が提供しないもの — 知るべき 2 つの制限。** 第一: **署名付きリクエストは 3.1 で規範的、3.0 ではない。** AdCP 3.0 は変更する呼び出しに bearer トークン認証を許可します。すべての変更する呼び出しに RFC 9421 署名を要求することは 3.1 に向けて [#2307](https://github.com/adcontextprotocol/adcp/issues/2307) で追跡されます。今日署名を要求するデプロイは、それをプラットフォーム層で強制し、プログラムが 3.1 で立ち上がるとき AdCP Verified にオプトインすべきです。 第二: **署名鍵はまだ鍵透明性ログにアンカーされていない。** 3.x では、RFC 9421 バイヤー鍵、ガバナンス JWS 鍵、エージェント署名鍵は最終的に各相手方自身のインフラにルートされます — 相手方の CDN、DNS、`/.well-known` パスを制御する攻撃者は攻撃者制御の鍵をサーブできます。TLS はこのギャップを閉じません。AdCP 3.x は継続性を伴う trust-on-first-use(マルチソースクロスチェック、公開遅延ウィンドウ、帯域外ローテーションシグナリング)を配信します — バーを検出可能に上げますが、暗号的に閉じません。鍵透明性層は 4.0 の成果物です。完全な記述については [既知の制限 — Authentication and Identity](/docs/reference/known-limitations#authentication-and-identity) を参照。 → [セキュリティモデル](/docs/building/concepts/security-model) · [セキュリティ実装リファレンス](/docs/building/by-layer/L1/security) *** ## プロベナンス **AdCP が提供するもの。** クリエイティブペイロードの正しい使用主張、AI 生成画像の `ai_generated_image` ブールフラグ、各メディアバイをそれを認可したガバナンス決定に遡らせる `governance_context` JWS。 **AdCP が提供しないもの。** `ai_generated_image` フラグはブールマーカーで、署名付きプロベナンス主張ではありません。クリエイティブが生成、編集、適応を通過するにつれ主張を蓄積する暗号署名付きプロベナンスグラフはありません。CAI/C2PA との相互運用は将来の作業として追跡されます。 → [クリエイティブプロベナンス検証](/docs/governance/creative/provenance-verification) · [adagents.json とエージェントアイデンティティ](/docs/governance/property/adagents) *** ## 開示 **AdCP が提供するもの。** [/docs/ai-disclosure](/docs/ai-disclosure) の AI 開示ページは、AgenticAdvertising.org が AI を使うすべての表面、その背後のモデル、人間レビューをリクエストする方法を名指します。`sync_plans` と `create_media_buy` はオーケストレーションエージェントのアイデンティティを運び、各決定の AI 起源を発見可能にします。 **AdCP が提供しないもの。** 配信されるクリエイティブの AI 生成広告コンテンツの規範的開示要件なし。サーブされる広告の開示義務は、適用される法(例: FTC ガイダンス、EU AI Act Art. 50、DSA Art. 26)の下でデプロイヤーの責任です。 → [AI 開示](/docs/ai-disclosure) *** ## コンプライアンスレビュアー向け これらはデプロイヤーが実装するワイヤーレベルフックです。それらは継ぎ目で、保証ではありません — 非適合デプロイヤーはそれらをバイパスできます。 | Control | Wire hook | AdCP role | | -------------- | ------------------------------------------------------------------------------------------ | ----------------------------------------- | | 人間監督ゲート | `plan.human_review_required: true` + `APPROVED` を返す `check_governance` | ゲートを提供。デプロイヤーがしきい値を構成 | | 監査証跡 | [`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs) | 不変、タイムスタンプ付き。デプロイヤーが保持に責任 | | 異議申立連絡先 | `brand.data_subject_contestation` | 発見可能なエンドポイント。デプロイヤーがプロセスを運用 | | リクエスト署名 | RFC 9421 `Signature` / `Signature-Input` ヘッダー | 3.1 で規範的。3.0 で許可 | | ガバナンス証明 | `create_media_buy` の JWS 署名付き `governance_context` | セラーが独立に検証可能 | | 規制カテゴリーブロック | `fair_housing`、`fair_lending`、`fair_employment` の `authority_level: agent_full` のスキーマレベル拒否 | 3 カテゴリーのみ。他はガバナンスエージェント実装が必要 | | ブランドアイデンティティ宣言 | `brand.json` `house`、`brands[]`、`authorized_operators[]`、ハウスレベル `trademarks[]` | 発見可能。デプロイヤー / エコシステムが公開レジストリに対して解決 | | 相互主張トラスト状態 | `brand.json` `parent_house` ↔ `brand_refs[]`(RFC #3533) | 5 状態シグナル。デプロイヤーポリシーが何が必要か決定 | | エージェントアイデンティティ | `brand.json` `agents[].jwks_uri`、`adagents.json` `signing_keys` | 検証可能な署名鍵。鍵透明性層は 4.0 | | 動作検証 | 正準テストキャンペーン経由の AAO Verified `(Live)` 継続証明 | AAO が発行し取り消す。デプロイヤーは AdCP ランタイムではなくマークを信頼 | **AdCP が明示的にしないことについては [既知の制限](/docs/reference/known-limitations) を参照** — そのページは、このページがあなたが取ったと仮定する敵対的な読みです。 # 需要はどう AI アシスタントに到達するか Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/ai-mediation AI アシスタントのための仲介プロトコル — コンテキストをすべてのバイヤーにブロードキャストできないとき、需要がどう会話型 AI を見つけるか。 Priya looks at a StreamHaus AI assistant interface — the conversation is helpful but there's no way for advertisers to participate StreamHaus は 6 か月前に AI アシスタントをローンチしました。ユーザーはハイキングトレイル、ギアのおすすめ、旅行計画について尋ねます — 広告主がその一部になるためにプレミアムを支払う類の高い意図の会話です。Priya は利用数が上昇し広告収益がゼロのままなのを見ています。 アシスタントはブラックボックスです。アドサーバーがありません。インプレッションがありません。ユーザーが「岩場の地形にどんなトレイルシューズを買うべき?」と尋ねるとき、ブロードキャストする入札リクエストがありません — そしてあったとしても、生の会話コンテキストをエクスチェンジ上のすべてのバイヤーに送ることは、ユーザーコンテンツを大規模に漏らします。 これはすべての AI プラットフォームが直面する需要問題です: バイヤーが参加する標準的な方法のない高い意図の会話。 ## 中核となる設計原則 TMP は関心の分離でこれを解決します: プロトコルはどのスポンサーコンテンツが利用可能で関連性があるか **what** を決め、プラットフォームの LLM はそれをどう提示するか **how** を決めます。バイヤーはユーザー体験に決して触れません。プラットフォームはカスタム広告ロジックを決して構築しません。 これが TMP を広告注入システムではなく仲介プロトコルにするものです。複数のバイヤーエージェントが標準インターフェースを通じてオファーを提出し、プラットフォームはおすすめがどう — そしてそもそも — 現れるかについての編集制御を保持します。 ## Step 1: 需要問題 AI アシスタント以前のすべての広告サーフェスは同じように機能します: ユーザーがページをロードし、入札リクエストがコンテキストをバイヤーにブロードキャストし、アドサーバーが定義されたスロットにクリエイティブをレンダリングします。AI アシスタントは 3 つの前提すべてを壊します: * **定義されたスロットなし** — スポンサーコンテンツはレスポンステキストに織り込まれる * **入札リクエストなし** — 会話はプライベートで一時的、クロール可能な URL ではない * **アドサーバーなし** — プラットフォームの LLM がレスポンスを生成する これに標準はありません。仲介層なし、プロトコルなし、複数のバイヤーが会話で関連性を競う方法なし。今日のほとんどの AI プラットフォームは単一のアドネットワークと提携するか、独自のスポンサーシップロジックを構築するか、収益化を完全にスキップします。 Priya は 1 つのアドネットワークを選びたくありません。彼女は Pinnacle Agency の Sam と、関連するパッケージを持つ他のすべてのバイヤーエージェントに、標準プロトコルを通じて関連性を競わせたいのです。TMP はコンテキスト評価をユーザー識別から分離することでこれに対処します。 ## Step 2: 需要をコンテキストにもたらす The TMP Router hub from the frequency capping walkthrough now has a fourth connection — an AI assistant chat bubble icon joining web, mobile, and CTV web では、コンテキストは外向きに放射します — URL は公開、ページコンテンツはクロール可能、バイヤーはそれを並列で評価します。AI アシスタントはそれができません。会話はプライベートで一時的、オープンに共有できないユーザーコンテンツを含みます。 TMP のアーキテクチャはこの制約のために構築されました。コンテキストを外向きにブロードキャストする代わりに、ルーターはバイヤーエージェントを内向きにもたらします — 彼らは会話自体ではなく、会話についての分類されたシグナルを見ます。Priya は web と CTV を扱うのと同じ TMP ルーターに、アシスタントを新しいプロパティとして登録します: * **プロパティタイプ**: `ai_assistant` * **プレースメント**: `chat-inline-recommendation` — LLM がスポンサーおすすめを組み込める会話コンテキスト ルーターは登録されたすべてのバイヤーエージェントに並列で Context Match リクエストをファンアウトします。Sam のエージェントは他のバイヤーと並んで競います — これは単一パートナーディールではなく仲介です。 ## Step 3: 入札リクエストの代わりに分類されたシグナル A chat conversation about trail shoes sends classified context signals — topics, sentiment, keywords — to buyer agents, with a crossed-out person icon showing no user identity ユーザーが尋ねます: 「岩場の地形にどんなトレイルシューズを買うべき? 良い足首サポートが必要。」 LLM がレスポンスを生成する前に、StreamHaus は **Context Match** リクエストを送ります。会話ターンは URL ではありません — 一時的で、ユーザーコンテンツを含み、バイヤーに送れません。代わりに、プラットフォームは **分類されたシグナル** を送ります: IAB トピックコード、センチメント、キーワード、自然言語 summary。バイヤーはユーザーの実際の言葉を見ずに関連性を評価します。 ```json theme={null} { "type": "context_match_request", "request_id": "ctx-trail-shoes-01", "property_rid": "01916f3a-f8cb-7000-8000-000000000051", "property_type": "ai_assistant", "placement_id": "chat-inline-recommendation", "seller_agent_url": "https://streamhaus.example", "context_signals": { "topics": ["596", "477"], "taxonomy_source": "iab", "taxonomy_id": 7, "sentiment": "positive", "keywords": ["trail shoes", "rocky terrain", "ankle support"], "language": "en", "summary": "User seeking trail shoe recommendations for rocky terrain with ankle support" } } ``` `artifact_refs` なし — 会話ターンは一時的です。`context_signals` は分類された出力を運びます。`summary` フィールドは、関連性をセマンティックに評価する LLM ネイティブなバイヤーに特に有用です。信頼された実行環境で動作するプラットフォームは、代わりに完全な会話を `artifact` として送れます — パブリッシャーが開示レベルを制御します。 Sam のバイヤーエージェントは評価します: 「岩場の地形のトレイルシューズ — これは Acme Outdoor の Trail Pro 3000 キャンペーンに一致する。」それは **テキストと構造化データ** — LLM が自然なおすすめを生成するのに必要な生の素材 — を含むオファーで応答します。 すべての統合が完全なクリエイティブマニフェストを必要とするわけではありません。TMP はスペクトラムをサポートします: | Integration level | What the offer contains | How the platform uses it | | ----------------- | -------------------------------------------------------- | ------------------------------------------------------------ | | アクティベーションのみ | `package_id` | プラットフォームのアドサーバーがラインアイテムをアクティベート(AI と並んで従来の広告配信を持つプラットフォーム向け) | | ブランドメンション | `package_id` + `brand` + `summary` | LLM が summary を使って自然なブランドメンションを生成 | | 完全なおすすめ | `package_id` + `brand` + `summary` + `creative_manifest` | LLM がマニフェストからのプロダクト詳細をレスポンスに織り込む | | カタログステアリング | カタログアイテムを伴う `package_id` + `brand` + `creative_manifest` | LLM が同期されたカタログから特定のプロダクトをおすすめ | Priya は StreamHaus のアシスタントに完全なおすすめを選びました: ```json theme={null} { "type": "context_match_response", "request_id": "ctx-trail-shoes-01", "offers": [ { "package_id": "pkg-outdoor-display", "brand": { "domain": "acmeoutdoor.example" }, "summary": "Trail Pro 3000 — ankle-height trail runner with rock plate, relevant to user's terrain needs", "creative_manifest": { "format_id": { "agent_url": "https://streamhaus.example", "id": "sponsored_recommendation" }, "assets": { "headline": { "content": "Built for rocky trails" }, "body": { "content": "The Trail Pro 3000 has a full rock plate and ankle-height collar for technical terrain. Vibram outsole with 4mm lugs." } } } } ] } ``` `summary` は、プラットフォームがオファーを組み込むかを決める前に関連性を判断するのを助けます。`body` は、LLM が自然なレスポンスに織り込める事実に基づくプロダクト詳細を与えます。 ## Step 4: フリークエンシーキャップはすべてのサーフェスを越える A session token flows to the buyer agent — the same eligibility check as web, same frequency caps, same shared exposure store StreamHaus はセッショントークンを伴う Identity Match リクエストを送ります。バイヤーは、web と CTV をカバーする同じ共有された露出ストアに対して、フリークエンシーキャップ、オーディエンス適格性、recency を確認します。 AI アシスタントユーザーはしばしば認証されています — プラットフォームにログイン済み — つまりアイデンティティシグナルは通常 web より弱くなく強いです。このユーザーが 30 分前に StreamHaus の CTV アプリで Trail Pro 3000 広告を見た場合、2 時間の recency ウィンドウはここにも適用されます。AI アシスタントは、異なるサーフェスだからといってフリークエンシーキャップのフリーパスを得ません。 ## Step 5: プラットフォームが体験を制御する The creative manifest text feeds into the LLM alongside the conversation context — the LLM generates a natural response that weaves in the product recommendation StreamHaus は Context Match と Identity Match のレスポンスを結合します。Trail Pro 3000 オファーは適格です。オファーのクリエイティブマニフェストが LLM の生成コンテキストの一部になります: > 「良い足首サポートのある岩場の地形には、rock plate とより高いカラーのあるシューズが欲しいでしょう。**Trail Pro 3000** はまさにこのために設計されています — 鋭い岩から守る full rock plate と、テクニカルな地形での安定性のための足首の高さのカラーがあります。4mm ラグの Vibram アウトソールが緩い地面で確実なグリップを与えます。 > > こちらも見てみるとよいかもしれません…」 > > *Acme Outdoor からのスポンサーおすすめ* LLM はクリエイティブマニフェストを逐語的にコピーしませんでした。プロダクト詳細を、ユーザーの特定の質問に対応する自然なおすすめに織り込みました。プラットフォームは独自の編集ポリシー — スポンサーコンテンツラベル、自然な統合、非スポンサー代替との継続 — を適用しました。AI 生成スポンサーコンテンツをめぐる規制要件(FTC 開示、EU AI Act)はプラットフォームの責任で、プロトコルではなく LLM 統合を通じて強制されます。 ## Step 6: これが解き放つもの Three panels: a user receiving a relevant product recommendation in chat, Sam seeing his campaign reach a new high-intent surface, Priya seeing AI assistant revenue appear on her dashboard **ユーザー** は質問し有用な答えを得ました。Trail Pro 3000 のおすすめは関連性がありました — 彼らは岩場の地形と足首サポートについて尋ね、それがプロダクトが作られた目的です。スポンサーラベルは透明です。レスポンスは非スポンサーオプションで続きました。 **Sam** は、何を買うか尋ねた瞬間にユーザーに届きました — 意図は直接の質問より具体的にはなりません。web と CTV で実行される同じパッケージが AI アシスタントでアクティベートされました。クリエイティブマニフェストはバナーの代わりにテキストを運びましたが、Sam のバイヤーエージェントはサーフェス固有のロジックを必要としませんでした。 **Priya** は、独自のスポンサーシップロジックなしに、単一のアドネットワークにロックインせずに、ユーザー体験を妥協せずに AI アシスタントを収益化しました。TMP を話すすべてのバイヤーエージェントが参加できます。ルーターがファンアウトを扱います。StreamHaus が編集統合を制御します。web と CTV からのフリークエンシーキャップが引き継がれます。そして配信レポートは他のすべてのサーフェスと同じ測定インフラを通じて流れます。 | Without TMP | With TMP | | ------------------------ | --------------------- | | 単一のアドネットワークまたは収益化なし | 任意のバイヤーエージェントが関連性を競える | | 広告主ごとの独自統合 | 標準プロトコル、オープンな需要 | | サーフェスをまたいだフリークエンシーキャップなし | 共有された露出ストア、どこでも同じキャップ | | プラットフォームが広告ロジックを構築 | プラットフォームは編集統合のみを制御 | | アドネットワークが体験を制御 | プラットフォーム LLM が提示を制御 | ## マルチターン会話 各ユーザーメッセージは新しい Context Match 評価をトリガーします。プラットフォームはいつ再評価するかを決めます — 推奨トリガーはトピックシフト(プラットフォームの分類器が検出)、明示的なプロダクト関心(「もっと教えて…」)、またはセッションタイムアウト(5 分以上の非アクティブ)です。 プラットフォームは最新の Context Match レスポンスをキャッシュし、同じトピックのフォローアップ質問の再評価をスキップしてもかまいません。これは広告品質に影響せずにレイテンシーとプロバイダー負荷を減らします。 複数のバイヤーが同じ会話ターンのオファーを返すとき、プラットフォームは価格ではなく会話への関連性でランク付けします。これはオークションではなく仲介です。 インプレッションは、LLM がクリエイティブマニフェストをそのレスポンスに組み込むときに起こります。おすすめされたプロダクトについてのフォローアップ質問(「どこで買える?」)は新しいインプレッションではなくエンゲージメントイベントです。 **レイテンシー。** TMP の 50ms 未満の往復は LLM の生成時間(通常 1-3 秒)内に隠れます。プラットフォームは LLM プロンプトを準備しながら Context Match リクエストを送るため、TMP はユーザー体験に知覚可能な遅延を追加しません。 ## さらに深く AI アシスタント統合の技術リファレンス — リクエスト/レスポンス形式、コンテキストシグナル、アクティベーションパターン。 2 操作モデルがすべてのサーフェスにわたってどう機能するか、具体例付き。 クロスパブリッシャーフリークエンシーキャッピングのウォークスルー — TMP がどう実行ギャップを解決するか。 権威あるメッセージタイプ、フィールド表、適合性要件。 # バイヤーエージェント向け TMP Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/buyer-guide バイヤーエージェントが TMP とどう統合するか — Context Match と Identity Match リクエストへの応答、オファーの構造化、フリークエンシーキャップの管理。 # バイヤーエージェント向け TMP バイヤーエージェントとして、あなたは TMP ルーターから Context Match と Identity Match リクエストを受け取り、オファーと適格性決定で応答します。あなたは決してリクエストを送りません — パブリッシャーがそのルーターを通じてすべての対話を開始します。 ## 何を構築するか バイヤーエージェントは、単一のベース URL の下に 2 つの HTTP/2 エンドポイント — Context Match の `POST /context` と Identity Match の `POST /identity` — を公開します。ルーターはパスでディスパッチします: | Message type | Receives | Returns | | ------------------------ | ------------------------------------------ | -------------------------------- | | `context_match_request` | ページ/コンテンツシグナル、プレースメント、geo | クリエイティブマニフェストを伴うオファー | | `identity_match_request` | セラーエージェント URL、アイデンティティトークン、任意のパッケージ ID リスト | 適格なパッケージ ID + `serve_window_sec` | 各エンドポイントは 1 つのメッセージタイプを扱います。両方とも 50ms 未満で応答しなければなりません。ルーターはこの予算を強制し、遅いプロバイダーをスキップします。 [adcp-go](https://github.com/adcontextprotocol/adcp-go) SDK は、両エンドポイントの Go 型、リクエストパース、レスポンスビルダーを提供します。 ## 前提条件 TMP リクエストが到着する前に、あなたのパッケージが存在しなければなりません。メディアバイは 1 つ以上のパッケージを含みます — TMP はパッケージレベルで動作します。 1. パブリッシャーのセールスエージェントと `create_media_buy` 経由で **メディアバイを作成** 2. パブリッシャーがあなたのクリエイティブアセットを持つよう `sync_creatives` 経由で **クリエイティブを同期** 3. パブリッシャーのルーターがあなたのエンドポイントを知るよう **TMP プロバイダーとして登録** ルーターはパブリッシャーのディール履歴からあなたのパッケージについて学びます。あなたはパッケージリストをルーターにプッシュしません。 ## Responding to Context Match ルーターはあなたにページコンテキストを送ります。あなたはそのコンテキストに対してアクティブなパッケージを評価し、一致するパッケージのオファーを返します。 ```json theme={null} // Request you receive { "type": "context_match_request", "request_id": "ctx-8f3a2b", "property_rid": "01916f3a-9c4e-7000-8000-000000000010", "property_type": "website", "placement_id": "article-sidebar", "seller_agent_url": "https://streamhaus.example", "artifact_refs": [ { "type": "url", "value": "https://streamhaus.example/articles/hiking-gear-2026" } ], "context_signals": { "topics": ["550", "710"], "keywords": ["hiking", "gear", "outdoor"], "sentiment": "positive" }, "geo": { "country": "US", "region": "US-CO" } } ``` **あなたがすること:** 1. この `property_rid` と `placement_id` のアクティブなパッケージをルックアップする 2. 各パッケージのターゲティングをコンテキストシグナル、geo、artifact refs に対して評価する 3. 一致するパッケージのオファーを、それぞれクリエイティブマニフェスト付きで返す ```json theme={null} // Your response { "type": "context_match_response", "request_id": "ctx-8f3a2b", "offers": [ { "package_id": "acme-outdoor-q2", "brand": { "domain": "acmeoutdoor.example.com" }, "summary": "Hiking gear seasonal promotion", "creative_manifest": { "format_id": { "agent_url": "https://streamhaus.example", "id": "sidebar_display" }, "assets": { "headline": { "content": "Trail-ready gear for every summit" }, "image": { "url": "https://cdn.acme.example/hiking-hero.jpg", "width": 300, "height": 250 }, "cta": { "content": "Shop now" } } }, "price": { "amount": 12.50, "currency": "USD", "model": "cpm" } } ] } ``` Context Match で **あなたが決して受け取らないもの**: ユーザー ID、デバイス ID、セッショントークン、IP アドレス、cookie。あなたはユーザーを識別できません。 ## Responding to Identity Match ルーターはあなたにセラーの `seller_agent_url` と 1 つ以上のアイデンティティトークンを送ります。`seller_agent_url` を使ってそのセラーに登録したアクティブなパッケージセットをルックアップし、サポートするトークンでアイデンティティを解決し、解決されたユーザーに対して各パッケージの適格性ルールを確認します。パブリッシャーは評価を明示的にスコープするため `package_ids` も送ってもよい(MAY)。存在するとき、登録済みアクティブセットと `package_ids` の **交差** に対して評価し、認識しない任意の ID を **黙って落とす**(silently drop) — 両方のパブリッシャーモード(all-active と fuzzed/padded)はこの動作に依存します。未知の ID をエラーとしてサーフェスすることは、あなたのレジストリメンバーシップをパブリッシャーに漏らします。 ```json theme={null} // Request you receive { "type": "identity_match_request", "request_id": "id-9c4e", "seller_agent_url": "https://publisher.example", "identities": [ { "user_token": "opaque-token-abc123", "uid_type": "publisher_first_party" }, { "user_token": "ID5*7xYp...", "uid_type": "id5" } ], "package_ids": ["acme-outdoor-q2", "acme-winter-clearance", "acme-loyalty-retarget"], "consent": { "gdpr": true, "tcf_consent": "CPxyz..." } } ``` **あなたがすること:** 1. サポートする `uid_type` でトークンをアイデンティティグラフに対して解決する。リクエストのエントリ順は意味的に重要でない — バイヤー自身の優先順に従って自身の優先順を適用する。すべてのエントリは同じユーザーを識別するので、最初の成功した解決で十分。複数のアイデンティティタイプが存在するとき、バイヤーは `hashed_email` のような強く再識別するトークンより不透明なプロバイダー ID(UID2、EUID、ID5、RampID)を優先すべき(SHOULD)。これは、誤設定または侵害されたルーターが最高リスクのトークンのみを転送するシナリオを無効化する。 2. フリークエンシーキャップを確認: このユーザーはパッケージのインプレッション制限を超えたか? 3. オーディエンスルールを確認: このユーザーはターゲットオーディエンスにいるか? 4. 抑制リストを確認: このユーザーは除外されるべきか? 5. 各パッケージの適格性を返す ```json theme={null} // Your response { "type": "identity_match_response", "request_id": "id-9c4e", "eligible_package_ids": ["acme-outdoor-q2", "acme-loyalty-retarget"], "serve_window_sec": 60 } ``` 適格性チェックを通過するパッケージ ID のみを返します。リストにないパッケージは不適格として扱われます。`serve_window_sec` は **パッケージごとのシングルショット fcap** です: パブリッシャーがこのウィンドウ内で各適格パッケージにユーザーへ 1 インプレッションを提供した後、パブリッシャーはそれらのパッケージから再び提供する前に Identity Match を再クエリしなければなりません(MUST)。デフォルト 60s、最大 300s。これはルーターのレスポンスキャッシュ TTL ではありません — [The serve-window contract](#the-serve-window-contract) を参照。 Identity Match で **あなたが決して受け取らないもの**: ページ URL、コンテンツトピック、キーワード、記事テキスト、任意のコンテンツシグナル。あなたはユーザーが何を見ているかを判断できません。 **なぜ Context Match で一致したものだけでなく、すべてのパッケージを受け取るか**: これはあなたがユーザーがどのコンテンツを見ているかを推論するのを防ぎます。ハイキング記事に一致したパッケージのみを受け取ったら、ユーザーがハイキングについて読んでいたと知ってしまいます。すべてのパッケージを受け取ることが構造的分離を保ちます。 ## 結合はパブリッシャー側で起こる あなたは結合された結果を決して見ません。パブリッシャーのルーターは: 1. あなたの Context Match オファーとあなたの Identity Match `eligible_package_ids` を交差させる 2. 両方のレスポンスに現れるパッケージのみをアクティベートする 3. 一致したパッケージのアドサーバーターゲティングキー値を設定する 4. アドサーバーが最終的なレンダリング決定を下す このステップにあなたの役割はありません。パブリッシャーがアクティベーションを制御します。 ## フリークエンシーキャップ管理 クロスパブリッシャーフリークエンシーキャッピングは Identity Match の主要なユースケースです。キャップポリシーとカウントはあなたの **インプレッショントラッカー** に存在します。Identity Match サービスはクエリ時にキャップ発火シグナルのみを消費します。分割: * **インプレッショントラッカー** はピクセル発火を受け取り、TMPX トークンをデコードし、維持する任意の fcap ポリシーを適用します — 各解決されたユーザーアイデンティティについて、キャップする任意の次元(パッケージ、キャンペーン、広告主、クリエイティブ、ラインアイテム)にわたってインプレッションをカウントし、ポリシーエンジンが使う任意のウィンドウ化と重複排除ロジックで。 * **キャップを使い果たすインプレッションで**、インプレッショントラッカーはキャップ発火エントリ — `(user_identity, package) capped until ` — を Identity Match キャップ状態ストアに書き込みます。 * **Identity Match サービス** はクエリ時に、リクエストの任意のアイデンティティに対してキャップ発火エントリを持つ任意のパッケージを `eligible_package_ids` から除外します。 プロトコルは、あなたがどうインプレッションをカウントするか、ポリシーがどこに存在するか、アイデンティティをまたいでどう重複排除するかを制約しません。境界のみを定義します: キャップ発火イベントがキャップ状態ストアに流れ込み、IdentityMatch サービスがクエリ時に存在を確認します。境界コントラクトとリファレンスキャップ状態ストアについては [フリークエンシーキャップデータフロー](/docs/trusted-match/identity-match-implementation) を参照。 fcap ルールが変わるとき — ウィンドウが短縮または延長、`max_count` が上昇または下降、ポリシーが一時停止または削除、パッケージが再割り当て — あなたは影響を受ける `(user_identity, package)` キャップ状態エントリを新しいポリシーに対して再評価し、適切な更新をプッシュしなければなりません(MUST): もはやキャップ超過でないユーザーのエントリを **削除**、まだキャップ超過だがウィンドウが変わったエントリを **延長**(新しい `expire_at` で上書き)。キャップ状態ストアはカウントを保存せず自身で再評価できません。バイヤーのポリシー所有者が真実の源泉です。イベント形状については [ポリシー更新とキャップ状態の再評価](/docs/trusted-match/identity-match-implementation#policy-updates-and-cap-state-re-evaluation) を参照。 Identity Match は TMP を使うすべてのパブリッシャーにわたって実行されるため、Publisher A であなたの広告を見たユーザーは、どのパブリッシャーがリクエストを送ったか見えなくても、Publisher B で正しくフリークエンシー超過として現れます。 実装の詳細 — fcap\_keys ラベルモデル、リファレンス valkey データモデル、露出レコード形状、SDK プリミティブ、適合性シナリオ — については [インプレッショントラッカー実装リファレンス](/docs/trusted-match/impression-tracker-implementation) を参照。インプレッショントラッカーと Identity Match サービスの間に位置する境界コントラクトは [フリークエンシーキャップデータフロー](/docs/trusted-match/identity-match-implementation) にあります。 ### バイヤーはどう露出を学ぶか Identity Match レスポンスの `tmpx` フィールドは TMPX トークン — ユーザーの解決されたアイデンティティトークンを含む HPKE 暗号化された blob — を運びます。パブリッシャーは `{TMPX}` をクリエイティブトラッキング URL に代入します。広告が提供されると、あなたのインプレッションピクセルが暗号化トークンを受け取ります。あなたのインプレッショントラッカーはそれを復号し、解決されたアイデンティティに対して fcap ポリシーロジックを適用し、(キャップが発火したとき)Identity Match キャップ状態ストアにキャップ発火エントリを書き込みます。ほとんどの本番デプロイは、バッファリングのため、デコード(同期、取り込み時)をポリシー評価とキャップ状態書き込み(非同期、キューの背後)から分離します。 これはパブリッシャーがユーザーアイデンティティを見ずに、あなたにリアルタイムのユーザーごとの露出シグナルを与えます。 暗号化形式とバイナリトークン構造については [TMPX 露出トークン](/docs/trusted-match/specification#tmpx-exposure-tokens) を、キャップ状態ストア境界コントラクトについては [フリークエンシーキャップデータフロー](/docs/trusted-match/identity-match-implementation) を参照。 ## プロバイダー登録 プロバイダー登録は帯域外プロセスです。`create_media_buy` 経由でメディアバイを確立した後、パブリッシャーと調整してあなたの TMP ベース URL を提供します。これは通常商業的合意を含み、法的レビューを必要とするかもしれません。なぜならパブリッシャーがあなたのエンドポイントにコンテンツシグナルとアイデンティティトークンを送るからです。パブリッシャーは次にそのルーターであなたのプロバイダーエントリを設定します([ルーターデプロイ](/docs/trusted-match/router-architecture#deployment) を参照)。 ```json theme={null} { "provider_id": "acme-outdoor-us", "endpoint": "https://us.tmp.acmeoutdoor.example/v1", "context_match": true, "identity_match": true, "countries": ["US"], "uid_types": ["uid2", "rampid", "id5"] } ``` `identity_match` をサポートするとき、`countries`(提供する国コード)と `uid_types`(解決するアイデンティティタイプ)を宣言しなければなりません(MUST)。ルーターはこれらを使って Identity Match ファンアウトをフィルターします — それらなしでは、ルーターはあなたのプロバイダーにリクエストをルーティングできません。 いずれか、または両方のエンドポイントをサポートできます。Context Match のみは、フリークエンシーキャッピングなしのコンテキストターゲティングを意味します。Identity Match のみは、パブリッシャーがあなたのメディアバイのターゲティングルールからコンテキストをローカルで評価し、フリークエンシーチェックのためだけにあなたを呼ぶことを意味します。両方は完全な TMP 統合を意味します。 ### ヘルスエンドポイント あなたはそのベース URL で `GET /health` を公開すべきです(SHOULD)。準備完了のとき HTTP `200` を `{"status": "ok"}` とともに返します。パブリッシャーのルーターはこれをプリフライトチェックと監視に使います。リクエストファンアウト中には呼ばれません — バックグラウンド間隔のみ。 ## エラー処理 あなたのエージェントがリクエストを評価できないとき、エラーレスポンスを返します: ```json theme={null} { "type": "error", "request_id": "ctx-8f3a2b", "code": "provider_unavailable", "message": "Targeting data temporarily unavailable" } ``` 一般的なシナリオ: * **一致するパッケージなし**: 空の `offers` 配列を返す(エラーではない)。これはあなたのパッケージがコンテンツに一致しないときの通常のケース。 * **内部失敗**: エラーレスポンスを返す。ルーターはあなたのプロバイダーをスキップし他のプロバイダーで続行する。 * **タイムアウト**: レイテンシー予算内に応答できない場合、ルーターはあなたをスキップする。エラーレスポンスは不要 — ルーターがこれを扱う。 ## The serve-window contract Identity Match レスポンスの `serve_window_sec` フィールドは、バイヤーとパブリッシャーの間の **パッケージごとのシングルショット fcap** です: * `eligible_package_ids` の各パッケージについて、パブリッシャーは `serve_window_sec` 秒以内にそのパッケージでユーザーに **最大 1 インプレッション** を提供してもよい(MAY)。 * パブリッシャーが各適格パッケージに 1 インプレッションを提供した後、パブリッシャーは同じユーザーにそれらのパッケージのいずれかを再び提供する前に Identity Match を再クエリしなければなりません(MUST)。 * マルチインプレッションフリークエンシーキャッピング(5/日、100/月など)は別です。それはあなたのバイヤー側の状態に存在し、`serve_window_sec` にかかわらず TMPX インプレッションコールバック経由で帯域外で更新されます。サーブウィンドウはプロトコルレベルのスロットルで、マルチインプレッションキャップはバイヤー内部のポリシーです。 ルーターは `{identities_hash, provider_id, package_ids_hash, consent_hash}`(正準バイトについては仕様を参照)でキー付けされた内部重複排除キャッシュを適用してもよい(MAY)が、パブリッシャーの拘束コントラクトはルーターのキャッシュウィンドウではなくサーブウィンドウスロットルです。 **serve\_window\_sec 値の選択**: デフォルト 60 秒。範囲 1–300。300 より長いものは、典型的なキャンペーンにパッケージごとの fcap が粗すぎます。IdentityMatch 往復より短いものは負荷を追加するだけです。60 が良いデフォルトです。適格性状態がより速くシフトする場合(キャップに近い、オーディエンスがちょうど変わった)は下方に、IdentityMatch サービスが負荷下でキャンペーンがより粗い fcap に寛容な場合は上方(最大 300)に調整します。 ## パフォーマンス要件 | Metric | Target | | --------------------------------------------------------- | ----------- | | エージェント側処理 | \< 30ms p95 | | エンドツーエンド(publisher → router → agent → router → publisher) | \< 50ms p95 | | 可用性 | 99.9% | | エラー率 | \< 0.1% | 30ms のエージェント側予算は、ルーターとあなたのエンドポイントの間のネットワークオーバーヘッドを考慮します。ルーターはあなたのレイテンシーパーセンタイルを追跡し、適応的にタイムアウト割り当てを調整します。一貫して遅い応答は、ルーターがあなたの割り当てを減らすかあなたのプロバイダーをスキップする結果になります。 ## 測定 パブリッシャーは `get_media_buy_delivery` 経由で配信をレポートします。あなたのエージェントは配信データをクエリして、インプレッションを再照合し、ペーシングを追跡し、フリークエンシー状態を更新します。 バイヤーは `{TMPX}` マクロ経由でリアルタイムのユーザーごとの露出シグナルを受け取ります。Identity Match レスポンスは、クリエイティブトラッキング URL を通じてあなたのインプレッションピクセルに流れる暗号化 TMPX トークンを含みます。あなたのクラスターマスターがトークンを復号し、ユーザーごとのフリークエンシー状態をリアルタイムで更新します。`get_media_buy_delivery` は再照合とペーシングのための集約配信メトリクスを提供します — 主要なフリークエンシー入力ではありません。 ## OpenRTB との違い | | OpenRTB | TMP | | ------------ | ------------------------------- | -------------------------------------------- | | **あなたが受け取る** | 完全な入札リクエスト(ユーザー + コンテンツ + デバイス) | コンテンツ **または** アイデンティティ、決して両方でない | | **あなたが返す** | 入札価格 | オファー(クリエイティブマニフェスト)または適格なパッケージ ID + サーブウィンドウ | | **オークション** | エクスチェンジがオークションを実行 | オークションなし — パブリッシャーがローカルで結合 | | **フリークエンシー** | DSP ごとのみ | Identity Match 経由でクロスパブリッシャー | | **統合** | エクスチェンジごとの SSP アダプター | 2 つのエンドポイント(context + identity)、任意のサーフェス | # Context Match と Identity Match Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/context-and-identity TMP の中核にある 2 つの操作、それらがどう機能するか、パブリッシャーが決定時にどうそれらを結合するか。 # Context Match と Identity Match TMP は 2 つの操作を定義します。それらは設計上独立です — 別々のインフラで処理され、異なるデータを運び、異なる問いに答えます。パブリッシャーはそれらの結果を結合して最終的なアクティベーション決定を下します。 このページは、具体例を使って両操作と結合を説明します。 ## シナリオ リテールパブリッシャーは、バイヤーエージェントからの 3 つのアクティブな AdCP メディアバイを持ちます。各メディアバイはパッケージを含みます: * **Package A**: スポンサープロダクト — コーヒーブランド、カルーセルフォーマット、検索とカテゴリーページで利用可能 * **Package B**: ホームページテイクオーバー — プレミアムディスプレイ、ホームページのみで利用可能 * **Package C**: 季節プロモーション — アイス飲料、ネイティブフォーマット、夏の間サイト全体で利用可能 買い物客が「best cold brew」を検索します。パブリッシャーの検索結果ページがロードされます。 ## Context Match パブリッシャーは Context Match リクエストを TMP ルーターに送り、それが各バイヤーのエージェントにファンアウトします。リクエストはページコンテキストを含みます。ユーザーアイデンティティもパッケージリストも含みません — バイヤーエージェントはこのプレースメントに同期されたパッケージセットを使います。 ### パブリッシャーが送るもの ```json theme={null} { "type": "context_match_request", "request_id": "ctx-7f3a", "property_rid": "01916f3a-7b2c-7000-8000-000000000001", "property_id": "retailer-web", "property_type": "website", "placement_id": "search-results-grid", "seller_agent_url": "https://retailer.example", "artifact_refs": [ { "type": "custom", "value": "search:beverages-coffee" } ], "context_signals": { "topics": ["632"], "keywords": ["cold brew", "iced coffee", "coffee beans"], "sentiment": "positive", "summary": "Shopper searching for cold brew coffee products" }, "geo": { "country": "US", "region": "US-CA" } } ``` バイヤーエージェントはページが何についてか — コーヒー、ポジティブな購入意図、カリフォルニア — を見て、そのコンテキストに対してパッケージを評価します。欠けているものに注意してください: ユーザー ID なし、デバイス情報なし、セッショントークンなし、IP アドレスなし。 ### バイヤーが応答するもの バイヤーエージェントは各パッケージをコンテキストに対して評価します。Package B はホームページのみなので、検索結果ページに一致しません。Package A と C は一致します。バイヤーはそれぞれのオファーを返します。 ```json theme={null} { "type": "context_match_response", "request_id": "ctx-7f3a", "offers": [ { "package_id": "pkg-A", "brand": { "domain": "bluebottle.example.com", "brand_id": "blue_bottle" }, "summary": "Cold brew coffee carousel — featuring top-rated blends", "creative_manifest": { "format_id": { "agent_url": "https://retailer.example.com", "id": "sponsored_carousel" }, "assets": { "items": { "type": "catalog-asset", "items": [ { "gtin": "gtin-001", "image_url": "https://cdn.example.com/gtin-001.jpg" }, { "gtin": "gtin-002", "image_url": "https://cdn.example.com/gtin-002.jpg" } ] } } }, "macros": { "sponsor_label": "Sponsored by Blue Bottle" } }, { "package_id": "pkg-C", "summary": "Free shipping on iced beverages — summer promotion", "price": { "amount": 0, "currency": "USD", "model": "flat" }, "creative_manifest": { "format_id": { "agent_url": "https://retailer.example.com", "id": "native_text" }, "assets": { "headline": { "content": "Free shipping on iced beverages" }, "body": { "content": "Summer promotion — order any iced beverage and get free delivery." }, "cta": { "content": "https://shop.example.com/promo/summer-iced" } } } } ], "signals": { "segments": ["coffee_enthusiast", "high_purchase_intent"], "targeting_kvs": [ { "key": "category_affinity", "value": "beverages" }, { "key": "seasonal_relevance", "value": "high" } ] } } ``` バイヤーは一致したパッケージごとにオファーを返します。各オファーは `package_id` と、任意で `brand`、`price`、`summary`、`creative_manifest`、`macros` を運びます。`summary` はパブリッシャーに関連性を判断するのに十分なものを与えます。クリエイティブマニフェストがインラインで存在するとき、パブリッシャーはレンダリングに必要なすべてを持ちます。大きなクリエイティブ(例: VAST 動画)については、マニフェストは埋め込む代わりに URL 経由で外部アセットを参照します。レスポンスは、パブリッシャーがアドサーバーに渡せるエンリッチメントシグナル — オーディエンスセグメントとターゲティングキー値 — も含みます。 ### Context Match が決して運ばないもの * ユーザー ID(ハッシュ化されたものでも他でも) * デバイス識別子 * セッショントークン * IP アドレス * 特定のユーザーを識別しうる任意のデータ これはポリシー制限ではありません。アイデンティティデータへのアクセスを持たないルーターのコンテキストコードパスによって強制されます — 共有メモリなし、共有状態なし、アイデンティティコードパスへの通信チャネルなし。[TEE アテステーション](/docs/trusted-match/privacy-architecture) はこの分離を独立に検証可能にできます。 ## Identity Match 別途 — そしてランダムな遅延とランダムな順序で、2 つのリクエストがタイミングやどちらが最初に着くかでペア化されないよう — パブリッシャーは Identity Match リクエストを TMP ルーターに送り、それが各バイヤーのエージェントにファンアウトします。リクエストはパブリッシャーの `seller_agent_url` とアイデンティティトークンを含みます。バイヤーは `seller_agent_url` からアクティブなパッケージセットを解決するか、パブリッシャーが送るとき明示的な `package_ids` リストに対して評価します。リクエストはページコンテキストを含みません。 ### パブリッシャーが送るもの ```json theme={null} { "type": "identity_match_request", "request_id": "id-9b2c", "seller_agent_url": "https://publisher.example", "identities": [ { "user_token": "tok_hk82mfp1", "uid_type": "uid2" }, { "user_token": "ID5*aB3xY...", "uid_type": "id5" }, { "user_token": "a1b2c3d4e5f6...", "uid_type": "hashed_email" } ], "consent": { "gdpr": true, "tcf_consent": "CPx2XYZABC..." }, "package_ids": ["pkg-A", "pkg-B", "pkg-C"] } ``` `identities` の各エントリは `{user_token, uid_type}` ペアです。パブリッシャーは利用可能なすべてのトークンを含めるべきです(SHOULD) — バイヤーは一致するグラフで解決し、より多くのトークンを送ることで追加のページコンテキストを漏らさずにマッチ率を最大化します。各 `user_token` は既存のアイデンティティプロバイダー(ID5、LiveRamp、UID2)から来るか、パブリッシャー生成です。`uid_type` はバイヤーにどのアイデンティティグラフに対して解決するかを伝え、試行錯誤のマッチングを避けます。`consent` オブジェクトはユーザーの同意シグナルを運びます — 規制された管轄区域のバイヤーはそれなしにトークンを処理してはなりません(MUST NOT)。トークンはバイヤーにとって不透明です — バイヤーはそれらを自身のアイデンティティグラフにマップでき(一致があれば)ますが、PII に逆変換したり任意のページコンテキストと相関させたりできません。 欠けているものに注意してください: URL なし、検索クエリなし、コンテンツシグナルなし、トピック ID なし。バイヤーエージェントはこのリクエストを純粋にユーザーアイデンティティとパッケージ適格性に基づいて評価します。 ### バイヤーが応答するもの ```json theme={null} { "type": "identity_match_response", "request_id": "id-9b2c", "eligible_package_ids": ["pkg-A", "pkg-B"], "serve_window_sec": 60, "tmpx": "k1.dG1weC1leGFtcGxlLWVuY3J5cHRlZC10b2tlbi4uLg" } ``` バイヤーは、このユーザーがパッケージ A と B に適格であることをレポートします。Package C は欠けています — ユーザーは適格でありません。パブリッシャーはなぜかを知る必要はありません — フリークエンシーキャッピング、オーディエンス不一致、その他の失格理由はバイヤー内部です。 `serve_window_sec: 60` はルーターに伝えます: 「これを 60 秒キャッシュせよ。」ルーターはこのキャッシュされた適格性を使って、存在するあらゆるプレースメント — 単一スロット、CTV 広告ポッド、複数の広告ユニットを持つページ — を、バイヤーに再クエリせずに埋めます。パブリッシャーはプレースメントをまたいでどう割り当てるかを決めます。 ### Identity Match が決して運ばないもの * ページ URL * コンテンツハッシュ * 検索クエリ * トピック ID * コンテンツレーティング * ユーザーが何を見ているかを識別しうる任意のデータ これはコンテキストデータへのアクセスを持たないルーターのアイデンティティコードパスによって強制されます — 共有メモリなし、共有状態なし、コンテキストコードパスへの通信チャネルなし。TEE アテステーションはこの分離を独立に検証可能にできます。 ## パブリッシャー側の結合 パブリッシャーは今や 2 つの独立したレスポンスを持ちます。彼らはそれらを自身のインフラで結合します — バイヤーエージェントもルーターも結合された結果を見ません。 ``` Context Match からの各オファーについて: pkg-A: Context は言う: cold brew カルーセルのオファー(クリエイティブマニフェストインライン) Identity は言う: 適格(eligible_package_ids 内) → オファーを受け入れる。インラインクリエイティブマニフェストを使ってレンダリング。 → "coffee_enthusiast" セグメントをアドサーバーターゲティングに適用。 pkg-C: Context は言う: アイス飲料プロモーションのオファー Identity は言う: 適格でない(eligible_package_ids に欠如) → このユーザーにこのオファーをスキップ。 結果: Package A を受け入れる。 ターゲティング KV を設定: category_affinity=beverages, seasonal_relevance=high。 ``` パブリッシャーは Package A をどうレンダリングするかを既に知っています — それはスポンサーカルーセルスロットにマップします。インラインクリエイティブマニフェストは必要なカタログアイテムとアセットを運びます。パブリッシャーは残りを扱いました。 ## オファーモデル TMP のレスポンスモデルはオファーです。オファーは `package_id`(必須)と任意フィールド: `brand`、`price`、`summary`、`creative_manifest`、`macros`(動的な値のキー値マップ)を運びます。 **シンプルなケース(GAM/Prebid)**: オファーは `package_id` を運びます。パブリッシャーは、ラインアイテムマッチングのため `targeting_kvs` シグナル経由で `package_id` を GAM に流します。`macros` マップは、GAM がレンダリング時にクリエイティブに挿入できる動的な値(例: スポンサーラベル、プロモーションテキスト)を運びます。 **リッチなケース(AI アシスタント、動的リテール)**: オファーは `summary`(「cold brew 50% オフ — レシピ統合」)を含むため、パブリッシャーは関連性を判断でき、レンダリングに必要なすべてを持つインライン `creative_manifest` を含みます。大きなクリエイティブ(例: VAST 動画)については、マニフェストは完全なペイロードを埋め込む代わりに URL 経由で外部アセットを参照します。 **動的ブランド**: プロダクトが `dynamic_brands` をサポートするとき、バイヤーは、事前設定されたパッケージブランドにロックされる代わりに、マッチ時にポートフォリオから選択して、オファーに `brand` を含められます。 **可変価格**: プロダクトが可変価格をサポートするとき、バイヤーはオファーに `price` を含められます。 クリエイティブマニフェストは、カタログアイテム、テキスト、画像、レンダリングに必要な他すべてを運びます。これは既存の CreativeManifest スキーマを再利用します。 ユーザーごとの露出トラッキングは TMPX マクロ — パブリッシャーがクリエイティブトラッキング URL に代入する Identity Match からの暗号化トークン — を通じて流れます。バイヤーのインプレッションピクセルがトークンを復号し、ユーザーごとの露出をリアルタイムでログします。`get_media_buy_delivery` 経由の集約配信レポートが、再照合とペーシングデータを提供します。 ## エンリッチメントシグナル Context Match レスポンスは、パッケージアクティベーションと並んでエンリッチメントシグナルを含められます: * **セグメント**: パブリッシャーのアドサーバーにターゲティングシグナルとして流れるオーディエンスまたはコンテキストセグメント(例: 「coffee\_enthusiast」「high\_purchase\_intent」)。 * **ターゲティングキー値**: パブリッシャーがラインアイテムターゲティング、レポート内訳、リアルタイム決定に使える任意のキー値ペア(例: `category_affinity=beverages`)。 エンリッチメントシグナルは加算的です — 特定のパッケージに結びついていません。バイヤーエージェントは、パッケージをアクティベートしないときでもエンリッチメントシグナルを返し、需要ソースではなくデータプロバイダーとして価値を提供するかもしれません。 これが既存の RTD(Real-Time Data)モジュールが今日機能する方法です。TMP はエンリッチメントとパッケージアクティベーションを単一のプロトコルに統一するため、バイヤーエージェントは 1 つのレスポンスで両方をできます。 ## パッケージリスト管理 Identity Match は、特定のバイヤーのアクティブなパッケージ ID すべてを送ります — Context Match で一致したものだけではありません。これは意図的です: バイヤーがどのパッケージがページコンテンツに一致したかを相関させるのを防ぎます。バイヤーがハイキング記事に一致したパッケージのみを受け取ったら、ユーザーがハイキングについて読んでいたと知ってしまいます。 パッケージリストは、`create_media_buy` 経由で新しいメディアバイが作成されたときに更新されます。ルーターは、パブリッシャーとのバイヤーのメディアバイ履歴から導出された、バイヤーごとのアクティブなパッケージリストを維持します。 Context Match では、任意の `package_ids` フィールドが、パブリッシャーが特定の理由を持つとき評価を絞れます — 例えば、動画パッケージのみが適用される CTV ポッド構成。これは Identity Match に影響しません: Identity Match が `package_ids` を送るとき、その構成は現在のプレースメントと独立でなければなりません(all-active または fuzzed)、プレースメント固有のサブセットではありません。 期限切れまたはキャンセルされたメディアバイからの古いパッケージは、1 時間以内にアクティブリストから削除されるべきです。ルーターがこのクリーンアップに責任を負います。 # データ保護ロール Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/data-protection-roles TMP のアーキテクチャが GDPR のコントローラー/プロセッサーロールにどうマップするか — 各当事者が何を決定するか、各当事者がどのデータを保持するか、リスクがどこにあるか。 # データ保護ロール このページは TMP のアーキテクチャを GDPR データ保護ロール(コントローラー、プロセッサー、共同コントローラー)にマップします。各参加者が個人について何を決定できて何を決定できないか、アーキテクチャの境界がどこでプロセッサーポジションをサポートし、どこでしないかを説明します。 これはアーキテクチャの分析であり法的助言ではありません。組織は特定の状況について資格のあるデータ保護顧問に相談すべきです。[TMP プライバシーアーキテクチャ](/docs/trusted-match/privacy-architecture) への習熟が前提とされます — 構造的分離、TEE アテステーション、パッケージセット相関除去、時間的相関除去のような概念はそこで定義されます。 ## Verdict at a Glance | Participant | Role | Confidence | Where the risk sits | | ----------------------------- | -------------------------- | ----------- | ------------------------------------------------------------------------------------------------ | | **TMP Router** | プロセッサー | 高(アーキテクチャ的) | オペレーターデプロイの完全性。TEE アテステーションで緩和。 | | **Buyer agent** | プロセッサー | 条件付き(運用上) | 独自スコアリング、クロス広告主データ結合、オーディエンス構築、クロスパブリッシャー露出蓄積。 | | **Publisher** | コントローラー | 変わらず | 同意収集、データプロセッサーの設定、最終的な提供決定。 | | **SSP / 結合を実行するラッパー** | パブリッシャーの共同コントローラーまたはプロセッサー | 条件付き | context+identity 結合を実行する者がそのステップのコントローラー責任を継承する。 | | **Identity provider** | TMP のスコープ外。トークン発行のコントローラー | プロバイダーによる | トークンスコープとグラフ動作(publisher-first-party 対 決定論的クロスサイト 対 確率的グラフ)がパブリッシャーのリスクを実質的に変える。 | | **Measurement / attribution** | TMP のスコープ外 | 該当なし | インプレッション後フローが、TMP が対処しないコントローラー分析を再導入する。[Out of Scope](#out-of-scope-post-impression-flows) を参照。 | ## Background: Controller vs. Processor GDPR の下で、**コントローラー** は個人データ処理の目的と手段を決定します。**プロセッサー** はコントローラーに代わって個人データを処理します。**共同コントローラー** は 1 つ以上の他のコントローラーと目的と手段を共同で決定します。 区別が重要なのは、コントローラーがより重い義務を負うからです: 処理の法的根拠、データ主体の権利、DPIA、直接責任。鍵となる問いは「誰がデータに触れるか」ではなく「誰がそれで何が起こるかを決めるか」です。 広告では、仲介者がコントローラーと見なされるリスクは次のとき増加します: * それが知っているか推論する何かに基づいて特定の人に広告を表示すると決める * 識別子が特性を持つと判断してセグメントを構築または投入する * サードパーティのセグメントデータを他のデータと結合して新しいプロファイルを作る * 特定のキャンペーン指示を超えた任意の目的にデータを使う リスクは仲介者が次のとき減少します: * データ主体またはデータプロバイダーと直接の関係を持つ当事者からの指示の下でデータを処理する * 任意のセグメントまたはオーディエンスデータの権原を保持しない * データを独立して、または自身の目的で使わない * 新しいマッピングを作るためにソースをまたいでデータを結合しない ## Why TMP's Posture Is Unusual バイヤーエージェントのプロセッサーポジションは、ad tech 業界が通常どう運営するかに対して珍しいです。ほとんどの DSP は、MSA がプロセッサーステータスを主張するときでさえ、自身の最適化目的のために共同コントローラーまたは独立コントローラーとして機能します。IAB Europe TCF フレームワークは、チェーンの各ベンダーに別々の目的と法的根拠を割り当てることでこれを反映します。 TMP のアーキテクチャは、従来の DSP が信頼できるかたちで主張できないバイヤーエージェントプロセッサーポジションを *可能にします*。なぜなら: * バイヤーはユーザーアイデンティティとページコンテキストを一緒に決して受け取らない(DSP はすべての入札リクエストで両方を受け取る) * バイヤーはアイデンティティに基づいてインプレッションごとの価格を設定しない(DSP はユーザーデータに基づいた入札価格を提出する) * バイヤーはスコア付けされた入札ではなくバイナリの適格性を返す(DSP はユーザーの評価をエンコードする価格を返す) バイヤーエージェントがそのエンベロープ内で動作するかは、アーキテクチャの問題ではなく契約と運用の問題です。ほとんどのバイヤー側組織は、その内側に留まるために明示的な選択をする必要があります。独自スコアリング、クロス広告主データ結合、または独立したオーディエンス構築を導入するバイヤーエージェントは、アーキテクチャの優位性を侵食し、プロトコルが可能にするものにかかわらず共同コントローラーとしての役割を評価する必要があるかもしれません。 ## The TMP Router: Processor TMP ルーターはインフラです。ターゲティング決定を下さず、プロファイルを構築せず、ユーザーを評価しません。パブリッシャーからリクエストを受け取りバイヤーエージェントにファンアウトします。レスポンスを受け取りマージします。マージされた結果をパブリッシャーに返します。 **ルーターが Context Match パスで見るもの:** * コンテンツシグナル(トピック、キーワード、センチメント) * プレースメント識別子 * 地理的コンテキスト(粗い) * いかなる種類のユーザーアイデンティティもなし **ルーターが Identity Match パスで見るもの:** * 不透明なユーザートークン * パッケージ識別子 * 同意シグナル * いかなる種類のページコンテキストもなし 2 つのパスは構造的に分離されています: 共有メモリなし、共有状態なし、通信チャネルなし。単一のコードパスが両方を保持することがないため、ルーターはユーザートークンをページ URL と関連付けられません。強制メカニズムについては [プライバシーアーキテクチャ](/docs/trusted-match/privacy-architecture) を参照。 **ルーターがしないこと:** * ユーザーが広告を見るべきかを評価する * フリークエンシーキャップやオーディエンスメンバーシップを確認する * ユーザープロファイルを構築または保存する * コンテキストデータをアイデンティティデータと結合する * 価格決定を下す * リクエスト/レスポンスのライフサイクルを超えてデータを保持する ルーターはパブリッシャーの指示(どのプロバイダーを呼ぶか、どのプロパティを提供するか)に基づいて動作するプロセッサーです。個人データ(不透明なユーザートークン)を、それらをバイヤーエージェントに配信し結果を返すためだけに処理します。 > **結論:** ルーターは信頼できるかたちでプロセッサーステータスを主張できます。TEE アテステーションにより、これは独立に検証可能です。TEE なしでは、オペレーターの完全性とコード監査に依存します。 ## The Buyer Agent: Processor Conditional on Operational Discipline TMP は、バイヤーが受け取るもの(アイデンティティを伴うコンテキストなし)と返すもの(適格なパッケージ ID、それ以上なし)をアーキテクチャ的に制約します。バイヤーが見るトークン、構築する露出履歴、実行する独自モデルで内部的に何をするかは制約しません。 したがってプロセッサーポジションは条件付きであり、アーキテクチャ的ではありません。それはバイヤーエージェントの運用上の選択とそれを統治する DPA に依存します。ほとんどのバイヤー側組織は、TMP が可能にするエンベロープの内側に留まるために意図的な選択をする必要があります。 **アーキテクチャが提供するもの:** * バイヤーはアイデンティティを伴うページコンテキストを決して受け取らない。Identity Match リクエストはページ URL、コンテンツシグナル、トピック ID を運ばない。 * バイヤーはインプレッションごとの価格を設定しない。Identity Match レスポンスは適格なパッケージ ID とキャッシュ TTL — 価格なし、入札なし、スコア付けされたレスポンスなし。 * バイヤーは提供決定を下さない。パブリッシャーが結合を実行する(または委譲する — [the SSP question](#the-publisher-and-the-ssp-join) を参照)。 **プロセッサーポジションが侵食する箇所:** * **独自の適格性スコアリング。** 適格性をスコア付けするために ML モデルを使うバイヤーエージェント — 広告主データのみで訓練されたモデルでさえ — は処理の手段を決定しています。「広告主基準を適用する」と「最適化エンジンを運用する」の間の線が、プロセッサーと共同コントローラーの間の線です。独自のオプティマイザーを実行し *ない* バイヤーエージェントは、既存の DSP に対して競争力がありません。これはエッジケースではなくベースケースです。 * **クロス広告主データ結合。** 複数の広告主を提供するバイヤーエージェントは、彼らのデータを分離して保たなければなりません。プロファイルを豊かにするために広告主をまたいでセグメントメンバーシップを結合することはコントローラーの動作です。 * **観察からのオーディエンス構築。** 広告主提供のオーディエンスリストを適用する(プロセッサー)ことは、行動を観察してオーディエンスを構築する(コントローラー)ことと異なります。[Risks requiring DPA scrutiny](#risks-requiring-dpa-scrutiny) を参照。 * **クロスパブリッシャー露出履歴。** クロスパブリッシャーフリークエンシーキャッピングは TMP の目玉ユースケースであり *かつ* 目玉のデータ保護エクスポージャーです。コンテキストがなくても、多くのパブリッシャーにわたってユーザートークンに結びついた露出履歴は、CJEU の判例の下で行動プロファイルを構成します。プロトコルはこれを除去しません — それを分離します。 > **結論:** TMP はバイヤーエージェントプロセッサーポジションを可能にします。強制はしません。広告主とバイヤーエージェントの間の DPA は、バイヤーが見るトークンで何をしてよいか、露出履歴がどう有界化されるか、独自モデルが広告主データとどう相互作用するかを指定しなければなりません。 ## The Publisher and the SSP Join パブリッシャーはファーストパーティです。ユーザーと直接の関係を持ちます。同意を収集します。コンテキスト(ユーザーが見ているもの)とアイデンティティ(ユーザーが誰か)の両方を保持します。TMP はパブリッシャーのコントローラーステータスを変えません。 パブリッシャーのコントローラー責任には次が含まれます: * Identity Match リクエストで同意シグナルを収集し送信する * ユーザートークンが不透明でバイヤーエージェントによって PII に逆変換できないことを保証する * コンテキストとアイデンティティの間の結合をローカルで実行する(または委譲する) * 提供前に同意ロジックを適用する * ルーターがどのプロバイダーを呼ぶかを設定する(データプロセッサー選択) * どのアイデンティティプロバイダーのトークンを使うかを選択する(コントローラーレベルの決定 — [Publisher configuration choices](#publisher-configuration-choices) を参照) **実際の結合。** 「パブリッシャーが結合をローカルで実行する」は原理的には正しく実際には不完全です。ほとんどのパブリッシャーは、「提供前に 2 つのリアルタイム API レスポンスを同意ロジックで結合する」プリミティブをネイティブに公開しないアドサーバー(Google Ad Manager、Kevel、Equativ)を運用します。GAM を使うパブリッシャーは通常、結合を実行するためにヘッダービッダーラッパー、Prebid モジュール、または SSP シムを必要とします。多くはそれを SSP(Magnite、PubMatic、Index Exchange、OpenX)にアウトソースします。 結合が委譲されるとき、SSP またはラッパーは結合ステップ自体のコントローラー責任を継承します。パブリッシャーの SSP との DPA はこれを反映しなければなりません: SSP は、そのより広いサービスがどう特徴付けられるかに応じて、結合の共同コントローラー(または結合に特にスコープされたプロセッサー)になります。結合を委譲することによって「TMP が私をプロセッサーにした」と仮定するパブリッシャーは、アーキテクチャを誤読しています。 > **結論:** パブリッシャーはコントローラーのままです。結合が SSP またはラッパーに委譲される場合、その当事者は結合ステップの共同コントローラーになり、DPA チェーンで対処されなければなりません。 ## Pricing and Real-Time Decisions コントローラー/プロセッサー分析の重要な要因は、仲介者がユーザーアイデンティティに基づいてリアルタイムの価格設定または入札決定を下すかどうかです。 **TMP はアイデンティティに基づくリアルタイム価格設定を含みません。** プロトコルで価格設定が起こる箇所は次です: | Decision | When | Based on | Where | | -------------------- | --------------- | -------------------------------------------------- | --------------------------------- | | パッケージ価格 | メディアバイ交渉(オフライン) | プロダクトカタログ、ボリューム、条件 | バイヤーエージェントとパブリッシャー、任意のユーザーが評価される前 | | Context Match オファー価格 | リクエスト時 | コンテンツコンテキストのみ — ユーザーアイデンティティなし | Context Match パス(アイデンティティデータ利用不可) | | Identity Match 適格性 | リクエスト時 | フリークエンシーキャップ、オーディエンスメンバーシップ | Identity Match パス(コンテキストデータ利用不可) | | 最終提供決定 | 両レスポンスが返った後 | パブリッシャー(または SSP)が context + identity + consent を結合 | パブリッシャーインフラまたは SSP に委譲 | TMP のどの参加者も「この特定のページのこの特定のユーザー」に基づいて価格を設定しません。Context Match パスは可変価格を含むかもしれません(バイヤーは一般的なコンテンツよりハイキングコンテンツをより高く評価するかもしれません)が、これはアイデンティティではなくコンテンツに基づきます。Identity Match パスは価格ではなく適格性を決定します。 事前交渉された価格モデルはアイデンティティと経済的成果の間のリンクを減らしますが、完全には除去しません。パブリッシャー(または SSP)が context match オファーを identity match 適格性と結合しパッケージをアクティベートするとき、経済的結果は、このページのこのユーザーがこの価格でこの広告を見たということです。規制当局は個々のプロトコルメッセージを検査するのではなくシステムを全体論的に評価するかもしれません。アーキテクチャの区別は、単一の仲介者が結合されたユーザー+コンテキストの価格決定を下さないことです。 これは、入札者がユーザーアイデンティティとページコンテキストを一緒に受け取りインプレッションごとの入札価格を提出する OpenRTB とは異なります。そのモデルでは、入札者はユーザーが誰かと彼らが何を見ているかを結合してリアルタイムの価格決定を下 *せます*。それをするかはキャンペーンに依存します — 多くの入札者は主にコンテキストで価格を設定しアイデンティティはフリークエンシーキャッピングにのみ使い、それはプロセッサーパターンに近いです。構造的な懸念は、OpenRTB がこの結合を *可能にし*、プロセッサーポジションがアーキテクチャではなく契約上の制約に依存することです。TMP はこれらの関心をプロトコルレベルで分離します。 ## Out of Scope: Post-Impression Flows **TMP はリアルタイム決定のみをカバーします。** インプレッションが提供された後、配信レポート、コンバージョンイベント、アトリビューション、測定データがどう流れるかは仕様化しません。 これが重要なのは、すべてのキャンペーンがコンバージョントラッキング、ビュースルーアトリビューション、MMM 入力、インクリメンタリティ測定を必要とするからです。これらのフローは、インプレッションレベルのデータ — 通常ユーザートークン、クリエイティブ ID、タイムスタンプ、コンバージョンイベントを含む — をアトリビューションシステム(CM360、Innovid、Flashtalking)、DSP ネイティブのアトリビューションスタック、測定ベンダー(DV、IAS、iSpot、Nielsen)にルーティングします。これらのベンダーは通常、受け取るデータのコントローラーまたは共同コントローラーです。 オペレーターが既存のインプレッションログパイプラインを TMP 決定のキャンペーンに接続する場合、彼らは広く開いたインプレッション後のパイプに取り付けられたプライバシー保護のリアルタイム決定を構築したことになります。前面のアーキテクチャ保護は背面に届きません。 2 つのパスがこれに対処します: 1. **スコープ制限デプロイ。** インプレッション後フローを、広告主、測定ベンダー、任意のクリーンルームオペレーターの間の既存の DPA によって統治される別個のデータ保護問題として扱う。TMP はアトリビューションプライバシーを解決するレバーではありません。既存のフレームワーク(クリーンルーム、測定 API、集約レポート)がそうです。 2. **互換なアトリビューションを採用する。** バイヤーブラインドのコンバージョン API、クリーンルームで結合されるパブリッシャー側のコンバージョンログ、または TMP 自体と同じ分離規律を維持する集約測定システムを使う。これは新興領域です。AdCP はまだ「TMP 互換アトリビューション」パターンを仕様化していませんが、続くかもしれません。 TMP 採用を評価する DPO は、インプレッション後フローを独立したワークストリームとして扱い、プロトコルの分離プロパティがそれらに拡張されると仮定すべきではありません。 ## Comparison: AXE (Deprecated) vs. TMP TMP の前身 [AXE](/docs/media-buy/advanced-topics/agentic-execution-engine) は、より弱いデータ保護姿勢を持っていました: | | AXE | TMP | | ---------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | リアルタイムエンドポイントが受け取ったもの | 完全な OpenRTB スタイルのリクエスト: ユーザーアイデンティティ + ページコンテキスト + デバイスシグナル | 別々のリクエスト: コンテキスト **または** アイデンティティ、決して両方でない | | 誰がユーザー + コンテキストを一緒に見たか | AXE エンドポイントオペレーター | パブリッシャー(または委譲された SSP)のみ、ファーストパーティとして | | プロファイル構築リスク | オペレーターは理論的に閲覧プロファイルを構築できた | ルーターについてアーキテクチャ的に制約(どのコードパスも両方のシグナルを保持しない)。バイヤー側の相関は [相関除去メカニズム](/docs/trusted-match/privacy-architecture) によって妨げられるが、SHOULD レベルの要件へのパブリッシャーの遵守に依存 | | 価格モデル | 不透明なセグメント決定がアドサーバーターゲティングに供給 | 事前交渉されたパッケージ価格、インプレッションごとの入札なし | | 分離の強制 | 信頼と契約 | コード構造(監査可能)または TEE アテステーション(検証可能) | AXE の設計は、エンドポイントオペレーター(通常オーケストレーター)がユーザーアイデンティティとページコンテキストを同じリクエストで受け取ることを意味しました。オペレーターのプロセッサーポジションは、結合されたデータを悪用しないという契約上のコミットメントに依存しました。TMP はこれを構造的分離に置き換えます — ルーターは一緒に決して保持しないデータを悪用できません。 ## Comparison: RTD Modules (Prebid Real-Time Data) RTD モジュールは、オークション時に入札リクエストを豊かにするベンダー固有の Prebid 拡張です。各モジュールは完全な OpenRTB BidRequest(2-10KB)をベンダーエンドポイントに送り、それがエンリッチメントデータ(オーディエンスセグメント、コンテキスト分類、ブランドセーフティスコア)を返します。 **データ保護の懸念:** RTD モジュールはユーザーアイデンティティとページコンテキストを一緒に各ベンダーに送ります。ベンダーのプロセッサーポジションはアーキテクチャではなく契約に依存します。累積的なエクスポージャーは重大です: 5 つの RTD モジュールを使うパブリッシャーは、インプレッションごとに完全なユーザー+コンテキストのペイロードを 5 つの異なるベンダーエンドポイントに送ります。各ベンダーのプロセッサーポジションは独立して契約上のものです。 TMP は、ベンダー固有の RTD モジュールを分離を強制する標準化されたプロトコルに置き換えます。すべてをすべてのベンダーに送る代わりに、TMP はコンテキストをコンテキストパスに、アイデンティティをアイデンティティパスに送ります。結果は同じ(パッケージがアクティベートするかしないか)ですが、データエクスポージャーは構造的に最小化されます。 ## Risks Requiring DPA Scrutiny これらは DPA が対処しなければならない運用上のリスクです。アーキテクチャはそれらを制約しません。 **1. クロスパブリッシャー露出履歴**(目玉のバイヤーエージェントリスク)。 クロスパブリッシャーフリークエンシーを追跡するバイヤーエージェントは、ユーザートークンに結びついた露出履歴を維持します。ページコンテキストがなくても、これは行動プロファイルを構成します: このユーザーがいくつのプロパティに現れるか、どのくらい頻繁に、どのパブリッシャーカテゴリーにわたって。CJEU の *Meta Platforms* 決定(Case C-252/21)は、サービスをまたいでデータを結合することが、深いプロファイリングなしでもコントローラーレベルの処理を構成しうると確立しました。 これは脚注リスクではありません — それは目玉の TMP ユースケースの目玉のデータ保護エクスポージャーです。プロトコルはそれを除去しません。それを分離します。広告主とバイヤーエージェントの間の DPA は、法的根拠、保持期間、目的制限、消去フローを指定しなければなりません(第 17 条の権利が適用されます: 消去を行使するユーザーは、そのトークンに結びついたバイヤーの露出履歴が削除可能でなければならないことを意味します)。 **2. リターゲティングオーディエンス構築。** オーディエンスを *適用する*(ユーザートークンを広告主提供のリストに対して確認 — プロセッサーパターン)ことと、オーディエンスを *構築する*(観察された行動に基づいてユーザートークンが特性を持つと判断 — コントローラーパターン)の間には区別があります。バイヤーエージェントがコンバージョンイベントやサイト訪問シグナルを受け取りリターゲティングプールを構築する場合、それはオーディエンスを構築しています。 リターゲティングオーディエンスがどうシステムに入るかが重要です。バイヤーが機械的に確認する広告主提供のリストはプロセッサーポジションをサポートします。観察された行動からのバイヤー構築オーディエンスはしません。 **3. 独自の適格性スコアリング。** 適格性をスコア付けするために ML モデルを使うバイヤーエージェント — 広告主データのみで訓練されたモデルでさえ — は処理の手段を決定しています。DPA は、バイヤーが実行してよいモデル、それらを訓練するデータ、それらの出力がどう制約されるかを指定すべきです。 **4. 測定とアトリビューションフロー。** [Out of Scope](#out-of-scope-post-impression-flows) でカバー。TMP 自体とは別個のワークストリームとして扱う。 ## Publisher Configuration Choices これらはデータ保護の含意を持つパブリッシャー側の設定決定です。それぞれがパブリッシャーが下すコントローラーレベルの決定です。 **1. アイデンティティプロバイダー選択。** TMP はアイデンティティプロバイダーからトークンを消費しますが、プロバイダーは交換可能ではありません。異なるグラフ動作は根本的に異なるリスク形状を作ります: | Token type | Risk shape | Examples | | --------------------- | --------------------------------------------------- | ------------------------------------------- | | Publisher-first-party | クロスサイトリンクなし。最低リスクプロファイル。 | `publisher_first_party`(パブリッシャーごとのハッシュ化識別子) | | 決定論的クロスサイト | 同じユーザーがサイトとデバイスをまたいで同じトークンに解決。クロスサイトプロファイリングを可能にする。 | UID2(The Trade Desk が運用、主にバイ側使用)、ID5 | | 確率的 / 商用グラフ | プロバイダーが、プロバイダーの壁の内側でトークンを PII に解決するアイデンティティグラフを運用。 | RampID(LiveRamp) | アイデンティティプロバイダーの選択自体がコントローラーレベルの決定です。決定論的クロスサイトトークンの選択は、バイヤーエージェントのクロスパブリッシャー相関サーフェスを拡大します。パブリッシャー DPA と同意フローは、すべての `uid_type` 値を等価に扱うのではなく、プロバイダーの特定の姿勢を反映すべきです。 **2. 完全なアーティファクトを伴う Context Match。** パブリッシャーが分類されたシグナル(`context_signals`)ではなく完全なコンテンツ(`artifact` フィールド)を送るとき、バイヤーエージェントは実際のコンテンツを受け取ります。`context_signals`(事前分類されたトピック、センチメント、キーワード)はプライバシー保護のベースラインです。完全なアーティファクトは、バイヤーがコンテンツを直接評価する必要のあるケース(例: 分類だけでは不十分な AI アシスタント会話)のために存在します。 **3. キャッシュセマンティクス。** Identity Match レスポンスは `ttl_sec` キャッシュコントラクトを含みます。キャッシュウィンドウ中、ルーターはバイヤーに再クエリせずにキャッシュされた適格性を返します。キャッシュされた適格性は個人データです(ユーザートークンに結びついています)。[仕様](/docs/trusted-match/specification) は、推奨クランプ 3,600 秒で最大 86,400 秒(24 時間)の TTL を許します。ルーターは短い TTL を強制すべきで、有効期限を超えてキャッシュされたデータを保持してはならず、同じトークンの後続の Identity Match リクエストへの応答以外の任意の目的にキャッシュされた適格性を使ってはなりません。 **4. コンテキストの可変価格。** Context Match オファーは `OfferPrice` を含められます。Context Match はアイデンティティを運ばないため、これはコンテキスト価格 — ユーザーごとの価格ではありません。しかし、パブリッシャーの `context_signals` が個人を識別するのに十分具体的(例: 一意の AI 会話 summary)な場合、コンテキストパスは事実上のアイデンティティを運びうる。パブリッシャーは `context_signals` が PII や一意に識別するコンテンツを含まないことを保証すべきです。 **5. 結合の委譲。** パブリッシャーが context+identity 結合を SSP、ヘッダービッダーラッパー、またはサードパーティモジュールに委譲する場合、その当事者は結合ステップの共同コントローラーになります。パブリッシャーはより広い提供決定のコントローラーのままですが、DPA チェーンで委譲先の役割を対処しなければなりません。 # 実行ギャップ Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/execution-gap 既存のプロトコルがなぜリアルタイム実行に失敗し、TMP がなぜオークションアプローチではなくマッチングアプローチを取るか。 # 実行ギャップ バイヤーエージェントは `get_products` を通じてインベントリを発見し、`create_media_buy` を通じてディールを交渉し、後に `get_media_buy_delivery` を通じて配信を測定します。ディールは存在します。パッケージは合意された価格、ターゲティング、予算で定義されます。 そしてユーザーがページを訪れます。またはアプリを開きます。または AI アシスタントに質問します。 何が起こるでしょうか? ## 今日何が起こるか 答えは完全にサーフェスに依存します — そして各サーフェスで、それは場当たり的です。 ### Web(アドサーバー付き) パブリッシャーの広告運用チームは、各 AdCP パッケージに対応するラインアイテムまたは PMP ディールを、アドサーバー(GAM、Kevel、FreeWheel)で手動で作成します。ページがロードされると、アドサーバーは独自のターゲティングルールを評価してどのラインアイテムが適格かを決めます。パブリッシャーが Prebid Server を使う場合、ベンダー固有の RTD(Real-Time Data)モジュールがオークションにシグナルを注入できます — しかし各モジュールは独自の API を話し、完全な OpenRTB BidRequest(約 2-10KB の JSON)を送り、AdCP ディール構造と独立して動作します。 結果: AdCP を通じて交渉されたパッケージは、別個の手動プロセスを通じてアクティベートされます。ディールと実行が切り離されています。 ### AI アシスタント 標準メカニズムはありません。会話を収益化したい AI プラットフォーム(ChatGPT、Snap AI、Reddit chat、character.ai)は、バイヤーエージェントに「あなたのパッケージのどれがこの会話に一致するか?」を尋ねるプロトコルを持ちません。各プラットフォームは独自の広告配信ロジック、独自のバイヤー統合、独自のターゲティングシステムを構築するか — 単一のアドネットワークと提携して問題全体を委譲します。 ### モバイルアプリ メディエーション SDK(AppLovin MAX、ironSource、Google AdMob)が決定を扱います: このユーザーとプレースメントにどのネットワークディールが提供すべきか? しかしメディエーションは独自のディールレジストリで動作し、AdCP パッケージから切り離されています。アプリ開発者はメディエーションダッシュボード内でウォーターフォール優先度やオークションルールを設定します。「AdCP パッケージが存在する」から「メディエーション層がそれをアクティベートする」へのプロトコルパスはありません。 ### CTV ポッドサーバーは複数のディールから広告ブレイクを構成します。各ディールは、ターゲティングルール、競合分離制約、クリエイティブローテーションロジックとともに放送局のアドサーバーで設定されます。ポッド構成は複雑でサーフェス固有の問題です。バイヤーエージェントが「このポッドに私のパッケージをアクティベートし、15 秒カットダウンを優先せよ」と言う標準的な方法はありません。 ### リテールメディア リテーラーは内部のレコメンデーションエンジンを通じてスポンサープロダクトプレースメントを管理します。買い物客が検索したりカテゴリーを閲覧したりすると、リテーラーのアルゴリズムがどのスポンサープロダクトを表示するかを決めます。バイヤーエージェントは、初期のディール条件を超えてこの決定へのリアルタイム入力を持ちません。「この検索コンテキストにこれらの GTIN を優先しこれらのプロモーションを除外せよ」と言うプロトコルはありません。 ## なぜ OpenRTB はこれに誤っているか OpenRTB は特定のシナリオのために設計されました: アドエクスチェンジが、既存の関係を持たない入札者の間でオークションを実行する。それはコールドスタート問題を解決します — 見知らぬ者同士がどうやってリアルタイムで広告インベントリを取引するか? しかしパッケージが AdCP を通じて事前交渉されているとき、当事者は見知らぬ者ではありません。彼らはディールを持っています。実行時の問いは「誰が最も高く入札するか?」ではなく「合意されたパッケージのどれがこのコンテキストにアクティベートすべきか?」です。 OpenRTB はこれに 3 つの具体的な点で誤っています: **ユーザーアイデンティティをページコンテキストとバンドルする。** すべての OpenRTB 入札リクエストは、ユーザー ID、デバイスフィンガープリント、IP アドレス、ページ URL を単一のオブジェクトで送ります。これは構造的なプライバシー失敗です。バイヤーはこのデータを使ってクロスサイトの閲覧プロファイルを構築でき — そして実際にします。いかなる同意管理もアーキテクチャを修正しません。データは設計上一緒に移動します。 **オークションセマンティクスを強制する。** OpenRTB はすべてのインプレッションが競争的オークションだと仮定します。しかし事前交渉されたディールは競争的ではありません — 価格は既に合意されています。ディールをオークション機構に強制することは、オープンな競争のために設計されたプロトコルの上に PMP ロジック、ディール ID マッチング、フロア価格強制を構築することを意味します。尻尾が犬を振ります。 **ヘビー級である。** 典型的な OpenRTB 入札リクエストは 2-10KB の JSON で、完全なデバイスオブジェクト、ユーザーオブジェクト、site/app オブジェクト、インプレッション配列を運びます。実行が「どのパッケージがこのページコンテキストに一致するか?」だけを知る必要があるとき、そのペイロードのほとんどは無駄な帯域と無駄なパース時間です。 | What execution needs | What OpenRTB sends | | -------------------- | ------------------------------------------ | | ページコンテンツシグナル | 完全な site オブジェクト + referrer チェーン + キーワードリスト | | 利用可能なパッケージ | なし — バイヤーはディール ID から推論しなければならない | | コンテンツ分類 | 部分的 — バイヤーはしばしば独立して再分類する | | ユーザー適格性 | 生のユーザー ID、デバイス ID、IP アドレス、GPS | | 結果: パッケージアクティベーション | 結果: 価格、クリエイティブ URL、トラッキングピクセルを伴う入札 | ## なぜオークションは解決戦略であってプロトコルでないか 関連する本能は、新しいオークションプロトコル — より速く、より軽く、よりプライバシー認識 — を構築し、リアルタイム実行にそれを使うことです。CloudX の OpenAuction はこのアプローチを取ります: 暗号化された入札とアテステーション証明を伴い TEE(Trusted Execution Environment)で実行されるオープンソースのオークションロジック。よく設計されています。 しかしオークションは、ほとんどのリアルタイム広告決定にとって誤ったプリミティブです: **競争ではなくフィルタリング。** 「私の事前購入したパッケージのどれがこのコンテキストに適用されるか?」はフィルタリング操作です。バイヤーは利用可能なパッケージを自身のキャンペーンターゲティングと予算に対して評価します。敵対的な競争はありません — バイヤーは自身のディールから選択しています。 **入札ではなくステアリング。** 「このパッケージ内で、どのカタログアイテムやクリエイティブバリアントを優先すべきか?」はステアリング操作です。バイヤーは合意されたディールの範囲内で好みを表現します。計算する価格はありません。 **ランキングではなく適格性。** 「このユーザーはこのパッケージについてフリークエンシーキャップされているか、高い意図を持つか?」は適格性チェックです。バイヤーはユーザーのステータスをルックアップします。ランク付けする入札はありません。 オークションは、複数のバイヤーからの複数のパッケージが同じプレースメントを競うときの 1 つの有効な解決戦略です。しかしオークションをプロトコルにすることは、すべてのサーフェス — 関連性でスポンサーコンテンツを選択する AI アシスタント、プロダクト適合でカルーセルを埋めるリテーラー、編集判断でポッドを構成する CTV 放送局を含む — を入札パラダイムに強制します。 TMP は異なるアプローチを取ります: 一致するパッケージを返し、パブリッシャーにそれらをどう解決するかを決めさせます。パブリッシャーがオークションを望むなら、実行できます — 検証可能な公平性のために CloudX の TEE インフラを使う可能性もあります。しかしオークションはマッチングの代わりにではなく、マッチングの上に位置します。 ## プロトコルレベルのソリューションはどう見えるか 実行ギャップは、次のプロトコルを要求します: 1. **事前交渉されたパッケージで機能する。** 市場は計画時に既に起こりました。リアルタイム層はディールをアクティベートし、交渉しません。 2. **コンテキストをアイデンティティから分離する。** バイヤーはどのパッケージが一致するかを決めるためにコンテンツシグナルを必要とします。バイヤーは適格性を評価するためにユーザーシグナルを必要とします。これら 2 つのニーズは、バイヤーがそれらを相関させられないよう、2 つの独立した操作によって提供されなければなりません。 3. **カタログとクリエイティブの絞り込みをサポートする。** パッケージのアクティベートは常にバイナリの on/off ではありません。バイヤーは、どのカタログアイテムを特集するか、どのクリエイティブバリアントを提供するか、どのプロモーションがアクティブかを指定する必要があるかもしれません。 4. **サーフェスをまたいで機能する。** 同じプロトコルが、アドサーバーを持つウェブページ、アドサーバーのない AI アシスタント、メディエーション層を持つモバイルアプリ、レコメンデーションエンジンを持つリテーラー、ポッドサーバーを持つ放送局で機能しなければなりません。 5. **リアルタイムのレイテンシー要件を満たす。** エンドツーエンドで 50ms 未満。なぜなら実行はすべてのページロード、すべての会話ターン、すべてのアプリ画面で起こるからです。 これが TMP が提供するものです: 2 つの軽量な操作 — [Context Match と Identity Match](/docs/trusted-match/context-and-identity) — が、構造的プライバシー保証と 50ms 未満のレイテンシーで、あらゆるサーフェスにわたって事前交渉されたパッケージをアクティベートします。 # Identity Match フリークエンシーキャップデータフロー Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/identity-match-implementation フリークエンシーキャッピングのためのインプレッショントラッカーと Identity Match サービス間の境界コントラクト — データフローのみ。内部カウント、ポリシー評価、ストレージレイアウトはバイヤー内部の関心事。 # Identity Match フリークエンシーキャップデータフロー このページは、フリークエンシーキャップの状態がどう Identity Match サービスに到達し、Identity Match が適格性時にどうそれを消費するかを記述します。それは **データフローのみ** を定義します — インプレッショントラッカーと Identity Match サービスの間の境界を越えるもの。内部メカニクス(インプレッショントラッカーがどうインプレッションをカウントするか、ポリシーがどこに存在するか、Identity Match サービスがどのストレージレイアウトを使うか、アイデンティティが上流でどう重複排除されるか)はバイヤー内部の関心事で、ここではスコープ外です。 ワイヤー仕様は [TMP 仕様](/docs/trusted-match/specification) に存在します。Identity Match サービスが満たさなければならない適合性不変条件もそこで規範的です。Identity Match キャップ状態ストアのリファレンス実装は [`adcp-go/targeting/fcap`](https://github.com/adcontextprotocol/adcp-go/tree/main/targeting/fcap) で出荷されます。 ## ロール | Component | Responsibility | | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Identity Match service** | クエリ時に `eligible_package_ids` — ユーザーが現在キャップされていない(そして他の適格性チェックを通過する)要求されたパッケージのサブセット — を返す。インプレッションをカウントせず、fcap ポリシーを所有しない。 | | **Impression tracker** | ピクセル発火を受け取り、TMPX をデコードし、バイヤーの fcap ポリシー(カウント、ウィンドウ化、マルチアイデンティティ重複排除、バイヤーのポリシーロジックが行う何でも)を適用し、キャップを使い果たすインプレッションで Identity Match キャップ状態ストアに「キャップ発火」をシグナルする。 | | **Identity Match cap-state store** | TTL を伴う `(user_identity, package) → cap-until` エントリを記録。適格性時に Identity Match サービスによってクエリされる。インプレッショントラッカー(またはそのパイプラインの下流サービス)によって書き込まれる。 | 分割は意図的です: インプレッションのカウント、ウィンドウの評価、キャップがいつ発火するかの決定は、バイヤーとキャンペーンをまたいで異なるバイヤー内部のポリシー関心事です。Identity Match サービスは狭いまま — 「このユーザーはこのパッケージについて現在キャップされているか?」に答え、それ以上はしません。新しいキャップ次元(広告主、キャンペーン、クリエイティブ — [extensions](#future-extensions) を参照)は、サービスを変えずに同じ境界コントラクトに接続します。 ## エンドツーエンドフロー ``` 1. Identity Match query publisher → router → Identity Match service Identity Match looks up cap state for each (identity, package) pair returns eligible_package_ids + tmpx (HPKE-encrypted resolved identities) 2. Ad serves; creative tracking URL fires pixel with {TMPX} publisher's player/page → impression tracker 3. Impression tracker decodes TMPX → resolved identities + signed package context (seller_agent_url, package_id) 4. Impression tracker applies the buyer's fcap policies → counts this exposure against whatever dimensions the buyer caps on (package, campaign, advertiser, creative, line item, …) for each resolved identity, using whatever policy logic and storage the buyer runs internally 5. If this impression exhausts a cap (i.e., it is the last allowed exposure under one of the buyer's policies), the impression tracker (or a downstream service in its pipeline) writes a cap-fire entry to the Identity Match cap-state store: (user_identity, package) capped until 6. Subsequent Identity Match queries for that user see the cap-state entry and exclude the package from eligible_package_ids until the entry expires ``` ステップ 1、2、6 はワイヤーを越え、[TMP 仕様](/docs/trusted-match/specification) で規範的に定義されます。ステップ 3 と 5 はインプレッショントラッカー → キャップ状態ストアの境界を越え、このページで定義されます。ステップ 4 はバイヤー内部 — プロトコルはそれを制約しません。 ## The cap-fire event バイヤーのポリシー評価が、インプレッションがキャップを使い果たしたと判断すると、インプレッショントラッカーは Identity Match キャップ状態ストアにキャップ発火エントリを書き込みます。各エントリは次から成ります: | Field | Description | | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `user_identity` | キャップが発火した解決されたアイデンティティトークン(例: `rampid:abc`、`id5:def`、`maid:ghi`)。単一のインプレッションが複数のアイデンティティに解決しポリシーがそのすべてで発火した場合、インプレッショントラッカーはアイデンティティごとに 1 エントリを書き込む。 | | `seller_agent_url` | パッケージが属するセラーエージェント。セラーをまたいで同一の `package_id` 文字列を曖昧性解消。 | | `package_id` | キャップが発火したパッケージ。 | | `expire_at` | キャップが期限切れになる壁時計時刻。キャップ状態ストアはこれを TTL として強制する — エントリは `expire_at` の後は欠如する。 | 単一のキャップ発火イベントは通常 1 エントリに対応します。複数の解決されたアイデンティティまたは複数のパッケージで発火するキャップは、バイヤーのポリシーが同じなら同じ `expire_at` を共有する `(identity, package)` ペアごとに 1 エントリを生成します。 キャップ状態ストアは、インプレッションごとのカウント、ポリシー定義、ウィンドウ設定を記録しません。その唯一の仕事は「この `(user_identity, package)` は現在キャップされているか?」に答えることです。バイヤーのポリシーロジック — カウント、ウィンドウ化、キャップする次元の選択、いつ発火するかの決定 — は完全にインプレッショントラッカーに存在します。 ## 適格性クエリ クエリ時に、Identity Match サービスはアイデンティティのリストと候補パッケージのリストを受け取ります。各候補パッケージについて、ユーザーのアイデンティティにわたって一致する `(identity, package)` エントリをキャップ状態ストアで確認します。任意のエントリが存在すれば、パッケージは `eligible_package_ids` から除外されます。これは存在チェックであり、カウントではありません。 キャップ状態は適格性への 1 つの入力です。Identity Match サービスは、オーディエンスメンバーシップ、パッケージのアクティブ状態、オーディエンス鮮度、バイヤーが気にする他の任意の入力も評価します — [適合性不変条件](/docs/trusted-match/specification#conformance-invariants-for-identitymatch-eligibility) を参照。その評価のキャップ状態の部分が、このページが定義する部分です。 ## Policy updates and cap-state re-evaluation キャップ状態エントリは、キャップ発火時に有効だった fcap ポリシーの下で書き込まれます。バイヤーの fcap ポリシーが変わるとき — ウィンドウが短縮または延長、`max_count` が上昇または下降、ポリシーが一時停止または削除、パッケージが異なるポリシーに再割り当て — 古いポリシーの下で書き込まれた既存のキャップ状態エントリは古くなりうる。古いエントリは、今や適格であるべきユーザーを抑制する(過剰抑制)か、今やキャップされるべきユーザーを抑制しそこなう(過少抑制)かのいずれかです。 fcap ルールが変わるとき、バイヤーのポリシー所有者(通常はインプレッショントラッカーまたはそのパイプラインのサービス)は、ルールが適用したすべてのキャップ状態エントリを再評価し、適切な更新を IdentityMatch キャップ状態ストアにプッシュしなければなりません(MUST)。2 つのイベント形状がケースをカバーします: | Event | When to push | Effect on cap-state | | -------------------- | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | **Delete cap-state** | 新しいポリシーの下でのユーザーの露出カウントが新しい `max_count` 未満、またはポリシーが削除/無効化、またはパッケージがポリシーから再割り当て。 | `(user_identity, package)` エントリを削除 — ユーザーはそのパッケージについてもう抑制されない。 | | **Extend cap-state** | ユーザーが新しいポリシーの下でまだキャップ超過だが、新しい `expire_at` が既存エントリと異なる — 例えばウィンドウが延長(より遅い `expire_at` をプッシュ)または短縮(より早い `expire_at` をプッシュ)。 | 新しい `expire_at` でエントリを上書き。 | 再評価は、キャップ状態ストアではなくバイヤー自身のカウント状態(インプレッション履歴が存在する場所)で実行されます — キャップ状態ストアはカウントを運びません。出力は、適用する削除または延長イベントのセットです。 [`adcp-go/targeting/fcap`](https://github.com/adcontextprotocol/adcp-go/tree/main/targeting/fcap) のリファレンスストアは、延長をネイティブに実装します(同じ `(user_identity, field)` の 2 番目の `RecordCap` が `HSETEX` 経由で以前の `expire_at` を上書き)。削除は将来の拡張です — 今日、最もシンプルな回避策は、既に過去の `expire_at` で延長することで、これにより次のクエリでエントリが欠如として扱われ、バックエンドの TTL 機構によって刈り取られます。 再評価は、ポリシーが多くのユーザーに適用されるとき高価になりえます。バイヤーは通常それを非同期に実行します: ポリシー変更イベントをエンキューし、影響を受けるユーザー母集団をバッチでスイープし、削除/延長イベントを段階的にプッシュ。プロトコルはケイデンスを制約しません — キャップ状態が現在のポリシーが示すものに収束しなければならないという結果整合性要件のみ。 ## リファレンス実装 [`adcp-go/targeting/fcap`](https://github.com/adcontextprotocol/adcp-go/tree/main/targeting/fcap) のキャップ状態ストア API がリファレンス形状です。2 つの操作を公開します: ```go theme={null} RecordCap(ctx, userIdentity string, fields []Field, expireAt time.Time) error IsCapped(ctx, userIdentity string, field Field) (bool, error) ``` — に加え両方のバッチバリアント。`Field` は `{SellerAgentURL, PackageID}` です。リファレンスストアは Valkey 9 ハッシュに裏付けられ、ユーザーアイデンティティでハッシュされ、`(seller_agent_url, package_id)` タプルごとに 1 つのハッシュフィールドと `expire_at` に設定された TTL を持ちます。他のバックエンド(Aerospike、DynamoDB、インメモリ、何でも)は、上記の境界コントラクトを満たせば準拠です。 ## Future extensions 今日、キャップ状態ストアは `(user_identity, seller_agent_url, package_id)` でキー付けされます。将来のプロトコルバージョンは、フィールドを追加の次元 — 広告主、キャンペーン、クリエイティブ、ラインアイテム — に拡張し、バイヤーがすべてのキャップ発火で N エントリを書かずに複数のパッケージにまたがるキャップを表現できるようにするかもしれません。このページの境界コントラクトはそのような拡張で変わりません: インプレッショントラッカーがキャップ発火エントリを書き、Identity Match サービスがクエリ時に存在を確認します。 ## 関連項目 * [TMP 仕様](/docs/trusted-match/specification) — ワイヤー仕様、TMPX 形式、適合性不変条件 * [インプレッショントラッカー実装リファレンス](/docs/trusted-match/impression-tracker-implementation) — 境界のインプレッショントラッカー側の非規範的リファレンス(`impression_id` 経由のマルチアイデンティティ重複排除、fcap\_keys ラベルモデル、ログベースのリファレンスデータモデル、SDK プリミティブ) * [バイヤーガイド](/docs/trusted-match/buyer-guide) — バイヤーエージェント統合、Context Match + Identity Match フロー * [AXE からの移行](/docs/trusted-match/migration-from-axe) — OpenRTB User.eids クロスウォークを含む、AXE 形状のパイプラインから移行するバイヤー向け * [プライバシーアーキテクチャ](/docs/trusted-match/privacy-architecture) — 各当事者が学ぶもの * [ルーターアーキテクチャ](/docs/trusted-match/router-architecture) — プロバイダー登録、ファンアウト、レイテンシー * [`adcp-go/targeting/fcap`](https://github.com/adcontextprotocol/adcp-go/tree/main/targeting/fcap) — Go のリファレンスキャップ状態ストア # インプレッショントラッカー実装リファレンス Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/impression-tracker-implementation バイヤー内部のインプレッショントラッカーの非規範的リファレンス — マルチアイデンティティ重複排除、fcap_keys ラベルモデル、インプレッションピクセルから Identity Match 境界のキャップ発火エントリまでのパス。 # インプレッショントラッカー実装リファレンス このページは、[フリークエンシーキャップデータフロー](/docs/trusted-match/identity-match-implementation) 境界の背後に位置するインプレッショントラッカーの **非規範的リファレンスコンテンツ** です。プロトコルは次のみを制約します: * ワイヤー仕様 — [TMP 仕様](/docs/trusted-match/specification) を参照。 * Identity Match サービスが満たさなければならない適合性不変条件 — [TMP 仕様](/docs/trusted-match/specification#conformance-invariants-for-identitymatch-eligibility) でも規範的。 * キャップ発火境界コントラクト — [フリークエンシーキャップデータフロー](/docs/trusted-match/identity-match-implementation) で定義。 このページのすべてはバイヤー内部です: インプレッショントラッカーがどうインプレッションをカウントし、解決されたアイデンティティをまたいで重複排除し、ウィンドウを評価し、キャップがいつ発火するかを決めるか。準拠するインプレッショントラッカーを実行するバイヤーは、境界で正しいキャップ発火イベントを生成する任意のアプローチを選べます。このページは 1 つのそのようなアプローチ — [`adcp-go/targeting`](https://github.com/adcontextprotocol/adcp-go/tree/main/targeting) で実装されたもの — を文書化し、他の実装者が実践的なリファレンスを持てるようにします。 ## クロスアイデンティティ重複排除問題 ユーザーへの単一のインプレッションは、しばしば同じ TMPX 内で複数のアイデンティティ(RampID、ID5、MAID、UID2、パブリッシャー発行トークンなど)に解決されます。アイデンティティごとにカウントする素朴なインプレッショントラッカーは、1 つのインプレッションをユーザーのキャップに対して 2〜3 としてカウントします。バイヤーがアイデンティティグラフを実行する場合、バイヤーはカウント前にアイデンティティを正準化できます。バイヤーがグラフなしまたは部分的にグラフ化されている場合(一般的 — Scope3 のホストされた Identity Match はグラフなし)、正準 id は存在しません。 カウンターベースのアプローチは、アイデンティティごとのカウンターを読むとき `merge_rule`(MAX / OR / SUM)でこれを取り繕います。どのマージルールも一般には正しくありません。病理的なケースは、インプレッションをまたいだアイデンティティ解決のトグルです: 一部のインプレッションは `rampid` のみを解決し、一部は `rampid` と `id5` の両方を解決します。MAX マージされたカウンターは過少カウント、SUM は過剰カウント、OR は 1 より多くを表現できません。どちらにせよキャップが誤ったタイミングで発火します。 リファレンス実装は、`impression_id` スキームで merge-rule 問題を完全に回避します: インプレッションごとに 1 つの id、すべての解決されたアイデンティティのログに書き込み、読み取り時に id で重複排除。カウントは、アイデンティティが上流で正準化されているかにかかわらず正確です。 ## impression\_id ルール インプレッショントラッカーはインプレッションごとに 1 つの `impression_id` を維持し、すべての解決されたアイデンティティのログに書き込みます。読み取り時に、ユーザーのすべてのアイデンティティログをスキャンし `impression_id` で重複排除すると、distinct-impression カウントが正確に復元されます。 必要なプロパティ: 1. **すべてのセラー、ソース、時間にわたってグローバルに一意。** バイヤーエージェントは多くのセラーから供給されるインプレッションを提供します。セラーをまたいだ衝突は distinct なインプレッションを黙ってマージしキャップを過少カウントします。十分なエントロピーを持つ任意の衝突耐性のある識別子スキームが許容されます — UUID(任意バージョン)、ULID、snowflake、または同等物。プロトコルは形式をピン留めしません。任意の層(パブリッシャー、決定層、またはデコード時のバイヤー)で鋳造された値は、他のすべての当事者にとって不透明な文字列です。 2. **値の 3 つの有効なソース、優先順位順。** `impression_id` は (a) パブリッシャー自身のファーストパーティコード、(b) 広告決定層(Prebid TMP モジュール、アドサーバー、SSP)、(c) TMPX デコード時のバイヤーのインプレッショントラッカーによって生成されます。層 (a) と (b) は、[`{IMPRESSION_ID}`](/docs/creative/universal-macros#impression-identification) ユニバーサルマクロ経由でピクセル URL に値を代入することでバイヤーに値を配信します。それらの違いは運用上 — パブリッシャーのスタックで誰が鋳造するか — で、ピクセルが到着するとバイヤーには不透明です。 3. **バイヤー消費ルール。** バイヤーは、存在するとき `{IMPRESSION_ID}` をピクセル URL から消費しなければならず(MUST)、欠如のときのみデコード時鋳造にフォールバックします。バイヤーがデコード時に鋳造するとき、TMPX nonce を `impression_id` として再利用してはなりません(MUST NOT) — TMPX nonce は Identity-Match-評価ごとで、サーブウィンドウ内のすべてのインプレッションで共有されるため、衝突します。 4. **コンテキストのみの要件。** コンテキストのみのインプレッション(ピクセルに `{TMPX}` 代入なし)については、デコード時のバイヤー側鋳造は不可能 — 層 (a) または (b) からの `{IMPRESSION_ID}` が唯一の利用可能なソースです。パブリッシャーと決定層は、TMP コンテキストのみのインプレッションに `{IMPRESSION_ID}` を含めなければなりません(MUST)。インプレッショントラッカーは、その欠如を統合エラーとして扱い、ログし、露出書き込みをスキップするか、ピクセル発火ごとのワンショット識別子にフォールバックすべきです(SHOULD)。フォールバックは、クロスアイデンティティ重複排除を保持できないため劣化しています。 5. **インプレッションごとに 1 つの id、そのインプレッションのユーザーのすべての解決されたアイデンティティログに書き込む。** アイデンティティごとに異なる id を生成すると重複排除コントラクトが壊れます — 同じインプレッションが解決されたアイデンティティごとに 1 回カウントされます。 6. **ピクセルリトライは別の関心事。** 同じピクセルが 2 回発火する(ネットワークリトライ、ページリフレッシュなど)ことは、2 つの `impression_id` を鋳造してはなりません — 2 つを鋳造するとピクセルリトライがキャップに対して二重カウントします。ピクセル URL の冪等性キーまたは `Idempotency-Key` ヘッダーでインバウンドリクエストを重複排除するか、リトライからの小さな過剰カウントを fcap 目的で無害として受け入れるかのいずれか。クロスアイデンティティ重複排除とピクセルごとの冪等性は、異なる緩和を持つ異なる問題です。(小文字の表現: このページは非規範的です。[フリークエンシーキャップデータフロー](/docs/trusted-match/identity-match-implementation) ページの境界コントラクトが適合性テストが引用するものです。) ## fcap\_keys ラベルモデル キャップは、インプレッション書き込み時に `dimension:value` ラベルでタグ付けされます。パッケージはどのラベルにマップするかを宣言し、fcap ポリシーは各ラベルに `window` と `max_impression_count` を付けます。 ``` package 2342: fcap_keys ["campaign:42", "campaign_group:7", "advertiser:13"] policy "campaign:42": {window: {interval: 10, unit: "minutes"}, max_impression_count: 5} policy "campaign_group:7": {window: {interval: 1, unit: "days"}, max_impression_count: 50} policy "advertiser:13": {window: {interval: 1, unit: "days"}, max_impression_count: 20} ``` インプレッショントラッカーが package 2342 のインプレッションの露出を書き込むとき、エントリの `fcap_keys` は `["campaign:42", "campaign_group:7", "advertiser:13"]` です。キャップが発火したかを評価するとき、そのポリシーのウィンドウ内で各ラベルに一致するエントリをログでスキャンします。 **ウィンドウ unit は負荷を担う**、単なる人間可読の省略形ではありません。リファレンス実装は `unit` をスライディングウィンドウのバケットサイズとして使います: `unit: "hours"` は時間単位のバケットに対して評価し、`unit: "minutes"` は分単位のバケットに対して評価します。duration 的に等価に見える 2 つのポリシー — `{interval: 2, unit: "hours"}` 対 `{interval: 120, unit: "minutes"}` — は **同じウィンドウ長** だが **異なるキャップ後再評価ケイデンス** を持ちます。ユーザーが 2 時間バケットキャップに達した後、新しいトラフィックを許可する次の適格性チェックは次の時間バケット境界で起こります。120 分バケットポリシーでは、次の分バケット境界で起こります。より小さい数字に収まる duration ではなく、望むケイデンスに合わせて `unit` を選んでください。 **文字セット制約。** 各セグメントは `[a-zA-Z0-9_-]+` に一致するため、`:` デリミタは曖昧でありません。URL を運ぶまたは他にコロンを運ぶ値はハッシュ化または短縮されなければなりません。 **マルチテナントオペレーター** は通常、共有状態上の広告主組織をまたいだキー衝突を防ぐデプロイ慣例として、テナントプレフィックス(`buyer-acme:campaign:42`)を採用します。これはオペレーターポリシーであり、プロトコルではありません。 **なぜ階層ではなくラベルか。** キャップ次元は顧客をまたいで異種です — 一部はクリエイティブでキャップし、一部はラインアイテムで、一部は広告主ロールアップで。固定スキーマは過剰規定するか過少提供するかのいずれかです。ラベルはクロスセラーキャップも自動にします: キーがセラーをまたいで共有される任意のポリシー(例: `buyer-acme:advertiser:13`)は、追加モードなしにそれらすべてにわたって強制します。横断的なポリシーは明示的です — キャンペーンごとと広告主ごとの両方のキャップが必要なキャンペーンは、両方のキーを宣言し 2 つのポリシールックアップを得ます。 ## リファレンスデータモデル(valkey 裏付け、ログベース) 下のレイアウトは [`adcp-go/targeting`](https://github.com/adcontextprotocol/adcp-go/tree/main/targeting) が使うものです。任意のバックエンド(Aerospike、DynamoDB、インメモリ、何でも)が問題ありません。データ形状はリファレンスであり、要件ではありません。 ### 露出ログ(アイデンティティごと) ``` type: STRING (binary-encoded []ExposureEntry, lazy-pruned to window) key: user:exposures:{HashToken(uid_type + ":" + user_token)} value: [ { impression_id, fcap_keys[], timestamp }, ... ] ``` `HashToken` は 16 バイトの SHA-256 プレフィックス、16 進エンコード。バイナリエントリエンコーディングがログをコンパクトに保ちます([`exposure_binary.go`](https://github.com/adcontextprotocol/adcp-go/blob/main/targeting/exposure_binary.go)) — 典型的なユーザーの 30 日ログは数 KB です。 各エントリは記録します: * `impression_id` — TMPX デコード時に生成。このインプレッションのすべてのアイデンティティログで同じ値。 * `fcap_keys[]` — このインプレッションがカウントするラベル。 * `timestamp` — unix 秒。 ### Fcap ポリシー(fcap\_key ごと) ``` type: STRING (JSON-encoded FcapPolicy) key: fcap_policy:{fcap_key} value: { window: {interval, unit}, max_impression_count, active, updated_at } ``` スライディングウィンドウは、ウィンドウをまたぐ現在と以前のバケットに落ちるログエントリをカウントすることで読み取り時に適用されます。バケットサイズは `window.unit`(`minutes`/`hours`/`days`/`weeks`/`months`)から導出され、ウィンドウ長は `interval × unit` です。エントリタイムスタンプの秒ごとの `>=` フィルターではなくバケットレベルのフィルターが、本番が使うものです — これがキャップ発火後の再評価ケイデンスをポリシーの `unit` から予測可能にします。 ### パッケージ設定(パッケージごと) ``` type: STRING (JSON-encoded PackageConfig) key: package:identity:{package_id} value: { fcap_keys: ["campaign:42", "advertiser:13"], active: true, updated_at: } ``` パッケージ → fcap\_keys をマップ。インプレッショントラッカーは、新しい露出をどのラベルでタグ付けするかを把握するためにこれを読みます。 ## 書き込みパス: ピクセル → ログ ピクセル発火時に、インプレッショントラッカーは: 1. **アイデンティティとパッケージコンテキストを解決する。** `{TMPX}` が存在するとき、TMPX をデコード(HPKE 復号 + バイナリパース) → 解決されたアイデンティティ + `(seller_agent_url, package_id)`。`{TMPX}` が欠如するとき(コンテキストのみのインプレッション)、他のピクセルパラメーターからパッケージコンテキストを解決し、解決されたアイデンティティセットは空 — 露出は、アイデンティティごとではなく上流で鋳造された(パブリッシャーまたは決定層)impression\_id でキー付けされた単一のコンテキストのみのログに書き込まれる。 2. パッケージの `fcap_keys` をルックアップする。 3. **`impression_id` を取得する。** ピクセル URL が `{IMPRESSION_ID}`(パブリッシャーまたは広告決定層によって上流で鋳造 — バイヤーはどちらかを区別できず、する必要もない)を運ぶ場合、その値を使う。そうでなければ 1 つ鋳造する — ただし `{TMPX}` が存在するときのみ。コンテキストのみのインプレッションでは、劣化モード動作について上のルールセクションのルール #2 を参照。 4. 各解決されたアイデンティティについて、`{impression_id, fcap_keys, timestamp}` を `user:exposures:{hash(identity)}` に追加する。最も長いアクティブウィンドウ(デフォルト 30 日)より古いエントリを刈り取る。解決されたアイデンティティのないコンテキストのみのインプレッションについては、エントリは適用される配信カウントログにのみ書き込まれる — 重複排除するアイデンティティがないとき、クロスアイデンティティ重複排除は意味を持たない。 アイデンティティごとの read-modify-write はリファレンス実装ではアトミックでありません([`engine.go:478`](https://github.com/adcontextprotocol/adcp-go/blob/main/targeting/engine.go#L478)) — 同じユーザーの並行書き込みは露出を失いうる。リファレンス実装はこれを明示的に受け入れます。競合下の過少カウントは fcap 目的で無害です。Lua または `Store.Append` 拡張経由のアトミック追加は延期された最適化です。 ## このインプレッションがキャップを使い果たしたかの評価 露出を書き込んだ後、インプレッショントラッカーは任意のキャップがちょうど発火したかを決めます。**パッケージは通常複数の `fcap_keys`(campaign、campaign\_group、advertiser、…)にマップし、それぞれ独自のポリシーを持ちます。ポリシーは独立して評価され、そのうち *いずれか 1 つ* がそのウィンドウ内で `max_impression_count` に達したときキャップが発火します。** ユーザーは、広告主ごとのポリシーに一度も近づかずにキャンペーンごとのポリシーでパッケージをキャップされうるし、逆も同様です。 露出の各 `fcap_key` について、インプレッショントラッカーはユーザーのアイデンティティログをスキャンします: 1. すべての解決されたアイデンティティについて `user:exposures:{h}` を読む。 2. エントリを、`policy.window` をまたぐ現在+以前のバケットに落ち、`fcap_key ∈ entry.fcap_keys` のものにフィルターする。 3. ユーザーのすべてのアイデンティティログにわたって `impression_id` で重複排除する。 4. 重複排除されたカウントを `policy.max_impression_count` と比較する。 任意のポリシーの重複排除されたカウントが `>= max_impression_count` なら、このインプレッションでキャップが発火しました。インプレッショントラッカーは次に、パッケージが使い果たされた `fcap_key` にマップするすべての `(user_identity, package_id)` について、Identity Match キャップ状態ストアにキャップ発火エントリを書き込みます。有効期限は `policy.window` の現在のバケットの終わり(バケットセマンティクスの下で最も古いスコープ内露出が期限切れになるとき)です。 複数のセラーの複数のパッケージにマップする広告主レベルのラベル(`advertiser:13`)のキャップについては、インプレッショントラッカーは影響を受ける `(user_identity, seller_agent_url, package_id)` ごとに 1 つのキャップ発火エントリを発します — main の [境界コントラクト](/docs/trusted-match/identity-match-implementation#the-cap-fire-event) はパッケージスコープなので、クロス次元のキャップは書き込み時にファンアウトします。 ## SDK プリミティブ SDK は、インプレッション処理を 1 つのバンドルされた呼び出しではなく、2 つの合成可能な関数として出荷します。本番トラッキングエンドポイントは通常、取り込み時にデコードし、下流のワーカーに独自のペースでストアを書かせます。decode+write を単一の関数にバンドルすることは、同期トポロジーを強制しバッファリングを妨げます。 ``` decodeTmpx(raw_tmpx) -> DecodedExposures Decrypts HPKE ciphertext, parses the published TMPX binary format (/docs/trusted-match/specification#binary-format), returns the resolved identity entries in a structured form ready for serialization onto a topic or for direct write. The persistent per-identity exposure log is a separate, store-resident structure — see Reference data model above. writeExposure(decoded, fcap_keys, store_context) -> { ok, fired_caps } Appends entries to each resolved identity's exposure log with a fresh impression_id and the supplied fcap_keys. Prunes entries older than the longest active window. Returns the set of caps that fired on this impression — the caller fans these out to the Identity Match cap-state store. ``` 加えてバイヤー側の管理プレーン: ``` upsertPackage(seller_agent_url, package_id, fcap_keys, opts) upsertFcapPolicy(fcap_key, {window: {interval, unit}, max_impression_count}) inspectExposures(uid_type, user_token, fcap_key?) // debugging helper ``` 加えて、net-new SDK プリミティブとしての HPKE 暗号化/復号(X25519 KEM、ChaCha20-Poly1305、RFC 9180 `mode_base` 準拠の HKDF-SHA256)。暗号化は TMPX を発する Identity Match サービスが必要とし、復号は `decodeTmpx` を呼ぶインプレッショントラッカーが必要とします。 同じサーフェスが `@adcp/client`(TS)、`adcp-go`、`adcp`(Python)で出荷されます。 > **プリミティブ名は説明的です。** `decodeTmpx`、`writeExposure`、`upsertPackage`、`upsertFcapPolicy`、`inspectExposures` は SDK サーフェスの形状を記述します。正準署名は対応する SDK RFC とともに着地し、命名や引数順で異なる場合があります。このセクションを API コントラクトとしてではなく、インプレッショントラッカーの分解として扱ってください。 ## 本番トポロジーパターン 典型的な Scope3 スタイルのデプロイ: ``` publisher pixel fires {TMPX} → tracking endpoint │ decodeTmpx (synchronous, at intake) │ ▼ pub/sub topic │ frequency_writer worker │ writeExposure (asynchronous) │ ▼ valkey (exposure log) │ if cap fired → RecordCap to Identity Match cap-state store ``` 取り込み時にデコード。バッファリングのため pub/sub に発する。下流のワーカーが露出ログを書きキャップ発火イベントを発する。バッファリング、リトライ、重複排除、可観測性、悪用防止はキュー層に存在 — そのどれも SDK の仕事ではありません。よりシンプルな同期パイプライン(同じハンドラーでの decode + write)も低ボリュームデプロイに有効です。 ## 適合性シナリオ これらはインプレッショントラッカーの動作をエンドツーエンドで説明します。それらはバイヤー内部のメカニクスです。ワイヤー上の観測可能なものは、Identity Match キャップ状態ストアに着地するキャップ発火エントリで、後の `identity_match_request` 呼び出しで適格性決定としてサーフェスします。 両シナリオのセットアップ: `seller-a.example` の `package = "pkg-42"`、`fcap_keys: ["campaign:42"]`、`policy campaign:42 = {window: {interval: 1, unit: "days"}, max_impression_count: 5}`。 ### シナリオ A — マルチアイデンティティ重複排除 ユーザーはインプレッションストリームにわたって 2 つの解決されたアイデンティティを持ちます: `rampid:abc` と `id5:def`。アイデンティティ解決はトグルします — ほとんどのインプレッションは両方を解決するが、1 つは rampid のみを解決します。 **imp-001、imp-002、imp-003** — TMPX が両方のアイデンティティを解決。各インプレッションが同じ `impression_id` を両方のログに書き込む: ``` user:exposures: = [ imp-001, imp-002, imp-003 ] user:exposures: = [ imp-001, imp-002, imp-003 ] ``` **imp-004** — TMPX が rampid のみを解決(id5 ルックアップ失敗)。imp-004 は rampid のログのみに書き込まれる: ``` user:exposures: = [ imp-001..imp-004 ] user:exposures: = [ imp-001..imp-003 ] unchanged ``` **imp-005** — TMPX が再び両方のアイデンティティを解決。imp-005 は両方のログに書き込まれる。インプレッショントラッカーは次に両方の解決されたアイデンティティログを読んでキャップを評価する: ``` rampid:abc log: { imp-001, imp-002, imp-003, imp-004, imp-005 } = 5 entries id5:def log: { imp-001, imp-002, imp-003, imp-005 } = 4 entries ``` ログをまたいでエントリを union し、`impression_id` で重複排除: ``` { imp-001, imp-002, imp-003, imp-004, imp-005 } = 5 distinct impressions ``` 5 = `max_impression_count` → キャップがちょうど使い果たされた。imp-005 で両方のアイデンティティが解決されているため、インプレッショントラッカーは両方のキャップ発火エントリを発する: ``` RecordCap(rampid:abc, [{seller-a.example, pkg-42}], expire_at) RecordCap(id5:def, [{seller-a.example, pkg-42}], expire_at) ``` 2 つのことが実証されます: * **重複排除が重要。** アイデンティティごとのカウントを素朴に合計すると `5 + 4 = 9` — `max_impression_count` を大幅に超える。`impression_id` による重複排除が正しいカウント 5 を復元する。 * **アイデンティティ解決の安定性は不要。** imp-004 は id5 のログエントリを完全に逃した。両方のアイデンティティが次に一緒に解決されたとき、評価時の重複排除が依然として正しい答えを生成する。 MAX merge\_rule を持つカウンターベースのトラッカーは、ここでカウンター `max(rampid=5, id5=4) = 5` を見る — この時点では偶然正しいが、それは分岐がたまたま単一の逃した書き込みだったからにすぎない。2 番目の id5 を逃したインプレッション(imp-006 スタイル)は rampid を 6 に押し上げ id5 を 5 に残す。MAX は依然として 5 と言い 1 つ過剰提供する。SUM(= ここで 9)は反対方向に過剰カウントする。ログ + `impression_id` 重複排除は構成上正しい。 実装者のためにフラグを立てる帰結: 将来のクエリが id5:def のみを解決する場合、キャップ状態ルックアップは imp-005 で書き込まれた id5:def エントリにヒットし、ユーザーは正しく抑制される。将来のクエリでどちらのアイデンティティも解決されない場合、キャップ状態ルックアップは全く起こらない — それは fcap の上流のアイデンティティ解決問題であり、fcap の正しさの問題ではない。 ### シナリオ B — クロスセラー広告主キャップ 異なるセラーの 2 つのパッケージ、両方が同じ広告主レベルのラベルにマップ: ``` package:identity:pkg-A = { fcap_keys: ["advertiser:13"], active: true } // seller-a package:identity:pkg-B = { fcap_keys: ["advertiser:13"], active: true } // seller-b fcap_policy:advertiser:13 = { window: {interval: 1, unit: "days"}, max_impression_count: 10 } ``` `seller-a` からの `pkg-A` の 10 インプレッション。各露出エントリの `fcap_keys` は `advertiser:13` を含む。10 番目の書き込みで、`advertiser:13` の重複排除されたカウントが `max_impression_count` に一致する。インプレッショントラッカーは、**すべてのセラーにわたって `advertiser:13` にマップするすべてのパッケージ** について、すべての解決されたアイデンティティに対してキャップ発火エントリを発する: ``` RecordCap(, [ {seller-a.example, pkg-A}, {seller-b.example, pkg-B}, ], expire_at) ``` `pkg-B` の `seller-b` からの後続の `identity_match_request` は、キャップ状態エントリが存在するため `eligible_package_ids: []` を返す。`fcap_key` が共有されているため、広告主レベルのキャップはセラーをまたいで強制する。IdentityMatch サービスでクロスセラー協調は不要 — バイヤーエージェントのインプレッショントラッカーが単一の真実の源泉で、キャップ状態ストアが公開チャネル。 ## パフォーマンスリファレンス 下の数字は [`targeting/scale_test.go`](https://github.com/adcontextprotocol/adcp-go/blob/main/targeting/scale_test.go) からの、インメモリモックストアに対する単一 goroutine のものです。CPU をネットワークから分離しています。それらは **インプレッショントラッカーの** 評価コスト — ログをスキャンしこのインプレッションがちょうどキャップを発火したかを決めるコスト — を記述します。Identity Match サービスのクエリ時コストは、別個のはるかに小さいキャップ状態存在チェックです。 **書き込み時の eval ごと、ログサイズ変動、単一アイデンティティ、単一 fcap\_key:** | Prior exposures in user's log | Eval latency | | ----------------------------- | ------------ | | 0 | 368 ns | | 100 | 5.3 µs | | 1,000 | 53 µs | | 10,000 | 118 µs | バイナリ lazy 重複排除を伴う線形スキャン。10K エントリでミリ秒未満。 **結合負荷(マルチアイデンティティ、マルチパッケージ eval)、すべての次元変動:** | packages mapped via fcap\_keys | log entries / id | identities | CPU/eval | | ------------------------------ | ---------------- | ---------- | ---------------------------------------- | | 100 | 1,000 | 3 | 1.0 ms | | 1,000 | 1,000 | 3 | 7.5 ms ← realistic Scope3-shape load | | 1,000 | 10,000 | 3 | 58 ms ← pathological tail (heavy users) | CPU は `packages × log_entries × identities` でスケールします。病理的な裾野は [adcp-go#103](https://github.com/adcontextprotocol/adcp-go/pull/103) のアルゴリズム最適化(ヒューリスティックゲートのプレフィルターバケット。小さいリクエストでの回帰を避けるため `numPackages > 50` でゲート)で対処されます: | packages | log entries | identities | Before | After | Speedup | | -------- | ----------: | ---------: | --------: | -------: | ------: | | 1,000 | 100 | 3 | 784 µs | 71 µs | 11.0× | | 1,000 | 1,000 | 3 | 7,566 µs | 287 µs | 26.4× | | 1,000 | 10,000 | 3 | 57,861 µs | 1,500 µs | \~38× | 本番のサイジングは、valkey ラウンドトリップレイテンシー、負荷下の裾野の動作、ヘビーユーザーのインプレッション分布形状にも依存します。モックストア CPU は下限であり、本番の数字ではありません。 ## 関連項目 * [フリークエンシーキャップデータフロー](/docs/trusted-match/identity-match-implementation) — このページが背後に位置するキャップ発火境界コントラクト * [TMP 仕様](/docs/trusted-match/specification) — ワイヤー仕様、適合性不変条件 * [`adcp-go/targeting`](https://github.com/adcontextprotocol/adcp-go/tree/main/targeting) — このページのモデルのリファレンス Go 実装 * [`adcp-go/targeting/fcap`](https://github.com/adcontextprotocol/adcp-go/tree/main/targeting/fcap) — 境界の反対側のリファレンスキャップ状態ストア # Trusted Match Protocol Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/index AdCP のリアルタイム実行層 — 無駄な予算から構造的プライバシーまで、あらゆるサーフェスにわたるクロスパブリッシャーのフリークエンシーキャッピングのストーリーをたどる。 A viewer sees the same outdoor gear ad on their TV and phone within minutes — a budget meter drains with diminishing returns **実験的。** TMP は実験的サーフェスとして AdCP 3.0 の一部です — 少なくとも 6 週間の予告をもって 3.x リリース間で変わることがあります。TMP を実装するセラーは `experimental_features` に `trusted_match.core` を宣言しなければなりません(MUST)。完全なコントラクトについては [実験的ステータス](/docs/reference/experimental-status) を参照。 Priya は CTV パブリッシャー StreamHaus の Ad Products ディレクターです。彼女は StreamHaus のインベントリがバイヤーエージェントにどう見えるか — プロダクトカタログ、クリエイティブ仕様、価格 — を設計しました。 Sam の Acme Outdoor キャンペーンは、フリークエンシーポリシー **週 5 インプレッション、露出間隔は最低 2 時間** で StreamHaus、OutdoorNet、PodTrail で実行されます。Sam がこれを選んだのは、間隔を空けた露出が集中した反復を上回るからです — コマーシャルの合間ごとに同じ広告は、エンゲージメントではなく疲労を生みます。 問題: 各パブリッシャーが独立してカウントします。 ## Step 1: 問題 — 無駄な予算 StreamHaus、OutdoorNet、PodTrail はそれぞれ独立してカウントします。夕食後に StreamHaus でハイキングコンテンツを見て、30 分後にスマホで OutdoorNet を閲覧する視聴者は、Sam が設定した 2 時間の recency ウィンドウの十分内で、同じ広告を再び受け取ります。 1 週間で掛け合わせると、視聴者は間隔を空けてではなく集中して 5 ではなく 15 インプレッションを受け取ります: | Publisher | Impressions counted | Actual viewer experience | | ---------- | ------------------- | ------------------------ | | StreamHaus | 5(上限内) | 毎セッション同じ広告 | | OutdoorNet | 5(上限内) | 毎セッション同じ広告 | | PodTrail | 5(上限内) | 毎セッション同じ広告 | | **Total** | **15** | **Sam の上限の 3 倍** | すべての冗長なインプレッションは、新しい誰かに届けられたはずの予算です。広告は間隔を空けることでよりよく機能します — 最初の数回の後の各露出は逓減する収益を生みます。Sam はリーチを買うべきときにフリークエンシーを買っています。 これは StreamHaus の問題ではありません。構造的な問題です。単一のパブリッシャーが全体像を見ないため、単一のパブリッシャーはクロスパブリッシャーの上限を強制できません。 ## Step 2: TMP ルーターの追加 Priya at a terminal, deploying a TMP Router — a diagram materializes showing context and identity paths splitting into separate channels Priya は Trusted Match Protocol(TMP)ルーター — 彼女のアドサーバーとバイヤーエージェントの間に位置し、コンテキストとアイデンティティに構造的に分離されたパスを持つ部品 — をデプロイします。彼女は Sam のバイヤーエージェント(Pinnacle)を、他のバイヤーと並んで TMP プロバイダーとして設定します。 ルーターは StreamHaus のアドサーバーとバイヤーエージェントの間に位置します。ユーザーがページをロードすると、ルーターはリアルタイム評価を扱います。Priya はサーフェス固有のコードを書きませんでした — 同じルーターが StreamHaus のウェブサイト、モバイルアプリ、CTV アプリを扱います。 Priya は Sam のバイヤーエージェント(Pinnacle)をルーターに TMP プロバイダーとして登録します: ```json theme={null} { "providers": [ { "name": "Pinnacle (Acme Outdoor)", "endpoint": "https://pinnacle.acme.example/tmp", "context_match": true, "identity_match": true, "properties": ["01J5A2B3C4-streamhaus-web", "01J5A2B3C5-streamhaus-ios"], "latency_budget_ms": 50, "priority": 1 } ] } ``` `context_match: true` は、ルーターがターゲティングのためにコンテンツコンテキストを Pinnacle に送ることを意味します。`identity_match: true` は、Pinnacle がフリークエンシーキャップとオーディエンス適格性を強制できるよう、不透明なユーザートークンも送ることを意味します。`properties` はこのプロバイダーが提供する StreamHaus プロパティをスコープします — Pinnacle は CTV ではなく web と iOS を評価します。`latency_budget_ms` はプロバイダーごとのタイムアウトを設定します。Pinnacle が一貫してそれを超えると、ルーターはそれを非優先化します。 ルーターは設定されたすべてのプロバイダーに並列でファンアウトし、そのレスポンスをマージします。 ## Step 3: Context Match — ページに何があるか? A StreamHaus article about hiking gear with content signals radiating outward — Sam's buyer agent responds with a package offer and creative manifest 視聴者がハイキングギアについての StreamHaus 記事を開きます。StreamHaus のプロパティは安定した `property_rid` 識別子で [プロパティガバナンス](/docs/governance/property) に登録されているため、バイヤーはこのリクエストがどのプロパティから来たかを正確に知ります。StreamHaus は記事のコンテンツシグナル、プレースメント、geo を伴う **Context Match** リクエストを送ります。 Sam のバイヤーエージェントは評価します: 「このハイキングコンテンツは `pkg-outdoor-display` に一致する。」それはクリエイティブマニフェスト — Trail Pro 3000 バナー — を含むオファーで応答します。 鍵となる制約: **ユーザーアイデンティティはこの境界を越えない。** バイヤーは人ではなくコンテンツを評価します。記事を読んでいるのが誰かは知りません — 記事が何についてかだけを知ります。 StreamHaus から Sam のバイヤーエージェントへのリクエスト: ```json theme={null} { "type": "context_match_request", "request_id": "ctx-8f3a2b", "property_rid": "01916f3a-9c4e-7000-8000-000000000010", "property_type": "website", "placement_id": "article-sidebar", "seller_agent_url": "https://streamhaus.example", "artifact_refs": [ { "type": "url", "value": "https://streamhaus.example/articles/hiking-gear-2026" } ], "context_signals": { "topics": ["596"], "taxonomy_source": "iab", "taxonomy_id": 7, "keywords": ["hiking gear", "outdoor equipment"] } } ``` パブリッシャーは `artifact_refs`(コンテンツを直接クロールするバイヤー向け)と `context_signals`(フォールバックとして事前分類されたトピックとキーワード)の両方を送ります。バイヤーエージェントはこのプレースメントにどのパッケージがアクティブかを既に知っています — `create_media_buy` 経由でそれらをセットアップしました。パッケージリストがワイヤー上を移動する必要はありません。 Sam のバイヤーエージェントからのレスポンス: ```json theme={null} { "type": "context_match_response", "request_id": "ctx-8f3a2b", "offers": [ { "package_id": "pkg-outdoor-display" } ] } ``` ## Step 4: Identity Match — このユーザーは適格か? An opaque user token with package IDs flows to Sam's buyer agent — a timeline shows last exposure 45 minutes ago, recency window 2 hours, verdict: not eligible 別途、StreamHaus は **Identity Match** リクエストを送ります: 不透明なユーザートークンと、あらゆるパブリッシャーにわたる Sam のアクティブなパッケージ ID すべて。 Sam のバイヤーエージェントはその露出履歴を確認します: 「このユーザーは 45 分前に OutdoorNet で 1 インプレッションを見た。2 時間の recency ウィンドウは経過していない。**適格でない。**」 鍵となる制約: **ページコンテキストはこの境界を越えない。** バイヤーはコンテンツ適合ではなく適格性を確認します。ユーザーが何を見ているかは知りません — このユーザーが今もっと広告を見るべきかどうかだけを知ります。 recency チェックは、Sam のバイヤーエージェントが共有された露出ストアを維持するため、パブリッシャー境界を越えます。StreamHaus、OutdoorNet、PodTrail はすべて同じバイヤーエージェントに Identity Match リクエストを送ります — そのためエージェントは 3 つすべてにわたるユーザーの総露出を知ります。 StreamHaus から Sam のバイヤーエージェントへのリクエスト: ```json theme={null} { "type": "identity_match_request", "request_id": "id-7c9e1d", "seller_agent_url": "https://streamhaus.example", "identities": [ { "user_token": "opaque-streamhaus-token-abc123", "uid_type": "uid2" }, { "user_token": "ID5*zP3wK...", "uid_type": "id5" } ], "package_ids": [ "pkg-outdoor-display", "pkg-outdoor-ctv", "pkg-outdoor-audio" ] } ``` Sam のバイヤーエージェントからのレスポンス: ```json theme={null} { "type": "identity_match_response", "request_id": "id-7c9e1d", "eligible_package_ids": ["pkg-outdoor-audio"], "serve_window_sec": 60 } ``` 適格なパッケージのみがリストされます — `pkg-outdoor-audio` がバイヤーのチェックを通過します。`serve_window_sec: 60` はルーターにこの適格性を 60 秒キャッシュするよう伝えます。 例は `package_ids` を明示的に送っていますが、パブリッシャーはそれを省略してもよい(MAY) — Sam の identity-match サービスは `seller_agent_url` からアクティブなパッケージセットを解決します。`package_ids` が送られる *とき*、その構成は現在のページと独立でなければなりません(MUST) — all-active(StreamHaus のすべての Sam パッケージ)または fuzzed(Sam が黙って落とす合成 ID でパディングされたランダムサンプル)のいずれか。ページ固有のサブセットは禁止されています。それはバイヤーが Context Match と Identity Match をまたいでパッケージセットを相関させることを許し、構造的分離を壊します。 ## Step 5: 結合 — StreamHaus が決定を下す Two response cards merge at StreamHaus — the Trail Pro ad fades while a different advertiser's ad activates in its place StreamHaus は両方のレスポンスをローカルで結合します: * **Context Match** は言いました: 「このクリエイティブマニフェストで `pkg-outdoor-display` をアクティベートせよ。」 * **Identity Match** は言いました: 「適格でない — recency ウィンドウ。」 結果: **広告を抑制する。** 別の広告主のキャンペーンがスロットを埋めます。Sam の予算は、視聴者が最近広告を見ておらずインプレッションが実際に重要になる、よりよい瞬間のために保存されます。 バイヤーはユーザーアイデンティティとページコンテキストを一緒に見ることは決してありませんでした。プライバシーは違反されうるポリシーではありません — それは構造的です。2 つのパスは決してデータを共有せず、パブリッシャー(既に両方のシグナルを持つ)が最終決定を下します。 ## Step 6: 3 人の勝者 Three panels: a viewer relaxing with varied ads across devices, Sam's dashboard showing increased unique reach, Priya seeing rising buyer satisfaction metrics **視聴者** は 3 つのプラットフォームにわたって普通の夜を過ごしました。彼らは StreamHaus のハイキングコンテンツ中に Trail Pro 広告を見ました — 関連性があり、タイミングがよい。30 分後に OutdoorNet を閲覧したとき、別の広告が現れました。インターネット中でつけ回される感覚はありません。 **Sam** は集中した反復ではなく間隔を空けた露出を得ました。彼の週 5 インプレッションは異なるコンテキストと瞬間にわたって着地し、それぞれが 6 回目や 7 回目のインプレッションよりも効果的です。そして抑制によって解放された予算は、まだ広告を見ていない視聴者に届きます — 同じ支出でより多くのユニークリーチ。 **Priya** は StreamHaus を差別化しました。バイヤーは、フリークエンシーポリシーが実際に機能するため TMP をサポートするパブリッシャーを好みます。StreamHaus のインベントリはインプレッションあたりより価値が高い。なぜならバイヤーは過剰露出された視聴者に予算を無駄にしていないことを知るからです。 | Before TMP | With TMP | | ------------------- | -------------------------------- | | 各パブリッシャーが独立してカウント | バイヤーエージェントがすべてのパブリッシャーにわたって露出を追跡 | | 視聴者あたり週 15 インプレッション | 視聴者あたり週 5 インプレッション、適切に間隔を空けて | | 予算がフリークエンシーを買う | 予算がリーチを買う | | 集中した反復、広告疲労 | 間隔を空けた露出、インプレッションあたりより高い効果 | | パブリッシャーはボリュームで競争 | パブリッシャーは品質とバイヤー体験で競争 | ## Step 7: 同じプロトコル、あらゆるサーフェス Five surface icons — web, mobile, CTV, retail media, AI assistant — connected to a single TMP Router hub, all in teal 同じ TMP ルーターが StreamHaus のウェブサイト、モバイルアプリ、CTV アプリ、AI アシスタントを扱います。Sam のバイヤーエージェントは、サーフェス固有のロジックなしにそれらすべてにわたって機能します。プロトコルがサーフェスの違いを扱います。Priya と Sam はビジネスを扱います。 ## さらに深く AI アシスタントのための仲介プロトコル — コンテキストがブロードキャストできないとき、需要がどう会話型 AI を見つけるか。 既存のプロトコルがなぜ配信時に失敗し、TMP がなぜオークションアプローチではなくマッチングアプローチを取るか。 カタログ絞り込みとパブリッシャー側の結合を含む、具体例を伴う両操作。 権威あるメッセージタイプ、フィールド表、適合性要件。 構造的分離、時間的相関除去、TEE アテステーション。 各 TMP 参加者のコントローラー対プロセッサー分析。 デプロイ、ファンアウト、プロバイダー設定。 ### サーフェスガイド # AXE から TMP への移行 Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/migration-from-axe AXE セグメントターゲティングから TMP のオファーと適格性モデルへの移行方法 — 概念マッピング、並行運用、切り替え。 # AXE から TMP への移行 AXE と TMP は同じ問題 — 事前交渉されたパッケージのインプレッション時実行 — を異なるアーキテクチャで解決します。AXE は完全なリクエスト(ユーザー + コンテキスト + デバイス)を送り、不透明なセグメント ID を返します。TMP はリクエストを 2 つの構造的に分離された操作に分割し、オファーと適格性を返します。 このページは AXE の概念を TMP の同等物にマップし、移行中に両方を並行して実行する方法を記述します。 ## 概念マッピング | AXE | TMP | Notes | | ------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------------------------------- | | `axei`(include segment) | Context Match オファー | パッケージがコンテンツに一致 | | `axex`(exclude segment) | `eligible_package_ids` からパッケージが欠如 | ユーザーが抑制リスト、オーディエンスルール、バイヤー側フリークエンシーチェックに失敗 | | `axem`(macro data) | クリエイティブマニフェスト + オファー `macros` + `{TMPX}` | 構造化アセットはクリエイティブマニフェストに移動。動的キー値はオファー `macros` を通過。ユーザーごとの露出トラッキングは Identity Match の `{TMPX}` マクロを使う | | オーケストレーター AXE エンドポイント | TMP ルーター | 2 つの分離されたコードパスを持つ単一のバイナリ | | Prebid Real-Time Data(RTD)モジュール | TMP Prebid モジュール | ベンダー固有の RTD モジュールを単一のモジュールに置き換え | | `axe_integrations` URL | `trusted_match` ケイパビリティ | `get_adcp_capabilities` レスポンス内 | | OpenRTB スタイルのリクエスト | Context Match + Identity Match | 1 つのバンドルされたリクエストの代わりに 2 つのリクエスト | | アドサーバーのセグメントキー値 | オファーからのターゲティングキー値 | 同じ GAM 統合、異なるソース | ## 各ロールで何が変わるか ### バイヤーエージェント **Before(AXE):** オーディエンスセグメントをオーケストレーターにアップロード。メディアバイの `axe_include_segment` / `axe_exclude_segment` でセグメント ID を参照。 **After(TMP):** Context Match と Identity Match エンドポイントを公開。コンテンツシグナル(Context Match)とユーザー適格性(Identity Match)に対してパッケージをリアルタイムで評価。クリエイティブマニフェストと適格性決定を伴うオファーを返す。 **鍵となる違い:** あなたのエージェントは、セグメントメンバーシップを事前計算する代わりにリアルタイムの決定を下します。ターゲティングロジックの完全な制御を持ちます — 仲介オーケストレーターなし。 ### パブリッシャー **Before(AXE):** オーケストレーターの Prebid RTD モジュールを有効化。`axei`/`axex`/`axem` キー値を受け入れ。それらのキー値をターゲットにする GAM ラインアイテムを作成。 **After(TMP):** TMP ルーターをデプロイ(または TMP Prebid モジュールを使用)。ルーターからオファーと適格性を受け入れ。オファーシグナルから GAM ターゲティングキー値を設定し、動的クリエイティブレンダリングのためオファー `macros` を通過。GAM ラインアイテムは `axei`/`axex` の代わりに `adcp_pkg` をターゲット。 **鍵となる違い:** ルーターがオーケストレーターの RTD モジュールを置き換えます。GAM ラインアイテムは不透明なセグメント ID の代わりにパッケージ ID を参照します。 ### オーケストレーター **Before(AXE):** AXE エンドポイントを運用、セグメント状態を管理、Prebid RTD モジュールを配布。 **After(TMP):** オーケストレーターは、パブリッシャーに代わって TMP ルーターを運用するか、バイヤー側のロール(バイヤーエージェント TMP エンドポイントを運用)に移行できます。仲介者としてのオーケストレーターのロールは TMP では任意です — バイヤーとパブリッシャーはルーターを通じて直接接続できます。 ## 並行運用 移行中、パブリッシャーは AXE と TMP を同時に実行できます: 1. Prebid で新しい TMP モジュールと並んで **既存の AXE RTD モジュールを保持** 2. **新しいメディアバイ** は TMP を使う(`axe_include_segment` / `axe_exclude_segment` なし) 3. **既存のメディアバイ** は期限切れになるまで AXE セグメントを使い続ける 4. **両方の GAM ラインアイテム**: AXE ラインアイテムは `axei`/`axex` をターゲット、TMP ラインアイテムは `adcp_pkg` をターゲット TMP は [`{TMPX}` マクロ](/docs/trusted-match/specification#tmpx-exposure-tokens) 経由でリアルタイムのユーザーごとの露出トラッキングを提供します。並行運用中、AXE と TMP のインプレッションの両方がバイヤーの露出ストアに供給されます — AXE はオーケストレーターのレポート経由、TMP は暗号化 TMPX トークンを受け取るバイヤーのインプレッションピクセル経由。バイヤーの露出ストアがソースにかかわらずユーザートークンとパッケージ ID で重複排除するため、二重カウントのリスクはありません。 ### 切り替え すべてのアクティブなメディアバイが TMP を使うとき: 1. Prebid からオーケストレーターの RTD モジュールを削除 2. AXE ターゲットの GAM ラインアイテムを削除 3. `axe_integrations` を削除し `trusted_match` を保持するよう `get_adcp_capabilities` を更新 ## ターゲティングオーバーレイの移行 `create_media_buy` の AXE ターゲティングフィールドは TMP の動作にマップします: | AXE field | TMP equivalent | | --------------------- | -------------------------------------- | | `axe_include_segment` | Context Match — バイヤーがターゲティングをリアルタイムで評価 | | `axe_exclude_segment` | Identity Match — バイヤーが抑制とオーディエンスルールを確認 | 新しいメディアバイは AXE フィールドを完全に省略すべきです。バイヤーエージェントの Context Match と Identity Match ロジックが、オーケストレーターのセグメント評価を置き換えます。 ## 変わらないもの * **`create_media_buy`** — 同じタスク、同じスキーマ(AXE フィールドを除く) * **`get_media_buy_delivery`** — 同じ配信レポート * **`sync_creatives`** — 同じクリエイティブ同期 * **アドサーバーとしての GAM** — TMP は依然として GAM が評価するキー値を設定 * **地理的およびその他のターゲティングオーバーレイ** — これらはメディアバイフィールドであり、実行層の関心事ではない ## OpenRTB User.eids クロスウォーク OpenRTB 形状のパイプラインから橋渡しするバイヤーのために、TMP Identity Match `identities[]` 形状は OpenRTB 2.6 `User.eids[]` に次のようにマップします: | AdCP TMP `identities[].uid_type` | OpenRTB 2.6 `User.eids[].source` | Notes | | -------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `rampid` / `rampid_derived` | `liveramp.com` | `atype: 3`(人ベース、[IAB AdCOM Agent Types](https://github.com/InteractiveAdvertisingBureau/AdCOM/blob/main/AdCOM%20v1.0%20FINAL.md#list_agenttypes) 準拠) | | `id5` | `id5-sync.com` | `atype: 3` | | `uid2` | `uidapi.com` | `atype: 3` | | `euid` | `euid.eu` | `atype: 3` | | `pairid` | `iabtechlab.com/pair` | `atype: 3` | | `maid` | `adid`(Android) / `idfa`(iOS) | OpenRTB では `User.eids` ではなく `Device.ifa` に非典型的に運ばれる | | `hashed_email` | `liveintent.com` またはバイヤー固有 | `atype: 3` | | `publisher_first_party` | パブリッシャー定義の `source` URL | コンテキスト依存。橋渡し実装はトークンが人ベースの識別子を表すときのみ `atype` を省略するか `atype: 3` にデフォルトしてよい | | `other` | バイヤー定義の `source` URL | コンテキスト依存。橋渡し実装はトークンが人ベースの識別子を表すときのみ `atype` を省略するか `atype: 3` にデフォルトしてよい | TMP `user_token` フィールドは `User.eids[].uids[].id` に対応します。OpenRTB の `User.eids[].uids[].atype` は AdCP のより高忠実度の `uid_type` から導出されます。別個の AdCP フィールドではありません。橋渡しコードは上の表から `atype` を計算すべきで、`uid_type` と食い違いうる独立したユーザー提供の `atype` 値を追加すべきではありません。 AdCP は Identity Match リクエストごとに最大 3 つのアイデンティティを運びます(HPKE サイズ予算 — [TMPX サイズ予算](/docs/trusted-match/specification#size-budget) を参照)。OpenRTB にはそのような制限がないため、OpenRTB から TMP に橋渡しするバイヤーは、切り詰めのためにバイヤー設定の優先順位(通常: 決定論的グラフを先に — UID2、RampID — 次に確率的またはパブリッシャースコープの ID)を適用しなければなりません。 # プライバシーアーキテクチャ Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/privacy-architecture TMP がどうユーザーアイデンティティをページコンテキストから分離するか、各当事者が何を学べて何を学べないか、TEE アテステーションがどう保証を格上げするか。 # プライバシーアーキテクチャ TMP のプライバシーモデルは構造的であり、ポリシーベースではありません。プロトコルはユーザーアイデンティティをページコンテキストから分離し、バイヤーが両方を一緒に受け取ることが決してないようにします。TEE なしでは、この分離はコードによって強制されます: コンテキストコードパスは決してアイデンティティデータにアクセスせず、逆も同様です。コードはオープンソースで監査可能です。TEE ありでは、アテステーションが期待されるコードが変更されずに実行されていることを証明し、保証を「監査可能」から「独立に検証可能」に格上げします。 このページは、分離とは何か、それが何を防ぐか、保証がどこから来るかを説明します。TMP はプライバシーを構造的に強制する唯一の AdCP ドメインです — これが他のドメインとどう比較されるかについては [ドメインをまたぐプライバシー姿勢](/docs/protocol/architecture#privacy-posture-across-domains) とクロスプロトコルの [プライバシー考慮事項](/docs/reference/privacy-considerations) ページを参照。 ## 分離原則 TMP ルーターは、2 つの構造的に分離されたコードパスを持つ単一のバイナリです: 1 つはコンテキスト用、1 つはアイデンティティ用。 | | Context Path | Identity Path | | ------------------ | ---------------------------- | --------------------- | | **Inputs** | ページ URL、コンテンツシグナル、トピック ID | 不透明なユーザートークン、パッケージ ID | | **Never receives** | 任意のユーザーアイデンティティ | 任意のページコンテキスト | | **Returns** | アクティベートされたパッケージ、エンリッチメントシグナル | 適格なパッケージ ID + TTL(秒) | コンテキストコードパスはアイデンティティデータへのアクセスを持ちません。アイデンティティコードパスはコンテキストデータへのアクセスを持ちません。2 つのパスは状態を共有しません: 共有メモリなし、共有データベースなし、通信チャネルなし、共有ログやテレメトリなし。 ### TEE なし 分離はコード自体によって強制されます。コンテキストパスは、アイデンティティデータが渡されず、コンテキストパスが到達できるどの場所にも保存されず、コンテキストパスが処理するどのデータ構造でも参照されないため、それを読めません。アイデンティティパスには逆が同じように適用されます。 これはソースコードを読むことで検証可能です。ルーターはオープンソースです。誰でもそれを監査して 2 つのコードパスが分離されていることを確認できます。しかしこれは信頼と監査のモデルです: デプロイされたバイナリが公開されたソースに一致し、実行時に変更が導入されていないことを信頼しています。 ### TEE あり TEE(Trusted Execution Environment)アテステーションは、オペレーターを信頼する必要を除去します。アテステーションドキュメントは、エンクレーブ内で何のコードが実行されているかの正確な暗号学的に署名された声明です。パブリッシャーまたは監査者は: 1. ルーターからアテステーションドキュメントを取得する。 2. TEE ベンダーのルート認証局に対して署名を検証する。 3. 実行中のコードが公開・監査されたソースに一致することを確認する。 これは分離を独立に検証可能にします。誰もルーターオペレーターが正しいバイナリをデプロイしたというクレームを信頼する必要はありません。ハードウェアがそれを証明します。 TEE はアップグレードパスであり前提条件ではありません。プロトコルはそれなしで機能します。独立検証が必要なパブリッシャーは TEE アテスト済みルーターを要求でき、コード監査と運用信頼で快適な人はそうする必要はありません。 ## 各当事者が学ぶもの ### バイヤーエージェントが学ぶもの **Context Match リクエストから**、バイヤーエージェントは学びます: * どのパブリッシャープレースメントとコンテンツアーティファクトが存在するか * `context_signals`(トピック、センチメント、キーワード、言語、ブランドセーフティティア、summary、embedding)経由のコンテンツシグナル — パブリッシャーによって事前計算 * 地理的コンテキスト(国、地域、メトロ) — パブリッシャー制御の粒度 **Identity Match リクエストから**、バイヤーエージェントは学びます: * どのユーザートークンが存在するか(不透明、パブリッシャースコープ) * 各ユーザーがバイヤーの各アクティブパッケージに適格かどうか * **パブリッシャーのビュー内でのクロスアイデンティティ等価性**: IMR が複数の `identities` エントリを運ぶとき、バイヤーはそれらのトークンがこのパブリッシャーの観点から同じユーザーに解決されることを学びます。これは意図的なマッチ率最適化ですが、バイヤーが以前持っていなかったアイデンティティグラフのエンリッチメントでもあります。クロスグラフ結合の開示を避けたいパブリッシャーは、マッチ率を犠牲にして IMR ごとに単一の `(user_token, uid_type)` を送れます。 * **機微トークンの露出**: `hashed_email` や類似の強く再識別するトークンは、不透明なプロバイダー ID より高い再識別リスクを運びます。パブリッシャーは、包含をデフォルトではなくデプロイ判断として扱うべきです(SHOULD)。バイヤーエージェントが TEE アテスト済みデプロイで実行されるとき、リスクサーフェスは、ログされたペイロードが事後的に明かせるものではなく、バイヤーのモデルがマッチ結果から推論できるものに縮小します — TEE は開示を除去しませんが、オフライン保持ベクターを閉じます。非 TEE デプロイは、法的/同意姿勢に対して `hashed_email` の包含を秤にかけるべきです。 バイヤーは、フリークエンシーキャップ、オーディエンスメンバーシップ、購入履歴、または持っている任意のシグナルから適格性を内部で計算します。彼らは適格なパッケージ ID のリストと TTL を返します。パブリッシャーは、ユーザーが適格かどうかの理由を学びません。 **バイヤーができないこと:** * ユーザートークンをページ URL やコンテンツシグナルと関連付ける * 特定のユーザーが特定のページを訪れたと判断する * 任意のユーザーのクロスページ閲覧プロファイルを構築する これらの制限は、バイヤーがアイデンティティとコンテキストを同じリクエストで決して受け取らず、下記の相関除去メカニズムが事後にそれらを結合するのを防ぐため、成立します。 ### ルーターオペレーターが学ぶもの コンテキストコードパスはコンテンツシグナルとパッケージリストを見ます。それはこれらをバイヤーエージェントにファンアウトし、レスポンスをマージします。ユーザートークンを決して処理しません。 アイデンティティコードパスはユーザートークンとパッケージ ID を見ます。それはこれらをバイヤーエージェントにファンアウトし、レスポンスをマージします。コンテンツシグナルを決して処理しません。 TEE なしでは、オペレーターは理論的にバイナリを変更して 2 つのパスを橋渡しできます。コードはオープンソースで監査可能ですが、オペレーターがそれを変更せずに実行することを信頼しています。TEE ありでは、アテステーションがバイナリが公開されたソースに一致することを証明し、その信頼要件を除去します。 ### アイデンティティフィルタリングのためのルーター信頼境界 ルーターは Identity Match 転送のための信頼境界内にあります。ルーターがプロバイダーごとに `identities` 配列をフィルターする(プロバイダーの宣言された `uid_types` が含むトークンのみを送る)とき、プロバイダーはパブリッシャーが元々送ったものを独立に検証できません — 彼らはルーターによって署名されたフィルターされたサブセットのみを見ます。これは意図的な信頼配置です: ルーターは既に 2 つのコードパスの構造的分離を実行し、アイデンティティトークンのフィルタリングはコードパスの分離より単純な保証です。 ルーターはアイデンティティトークンを追加、置換、変換してはなりません(MUST NOT)。転送された `identities` はパブリッシャー起源の配列のサブセットでなければなりません(MUST)。この不変条件は、コード監査から証明可能(TEE なし)で、アテステーション測定から暗号学的に検証可能(TEE あり)です。非 TEE デプロイを実行するオペレーターは、この不変条件の基礎としてコード監査を受け入れます。TEE アテスト済みデプロイを実行するオペレーターは独立検証を得ます。 バイヤーはプロトコル層で残余リスクを閉じます: 複数のアイデンティティタイプが存在するとき、バイヤーは `hashed_email` や他の強く再識別するトークンより不透明なプロバイダー ID を優先すべきです(SHOULD)。そうすれば `hashed_email` 以外すべてを剥がすルーターでも影響力を得ません([Identity Match への応答](/docs/trusted-match/buyer-guide#responding-to-identity-match) を参照)。 ### パブリッシャーが保持するもの パブリッシャーはコンテキストとアイデンティティの両方を持ちます。彼らはファーストパーティです: ユーザーは彼らのページ上、彼らのアプリを使用、彼らの会話中です。TMP はパブリッシャーのデータ姿勢を変えません。バイヤーと仲介者が同じ結合されたビューを得るのを防ぎます。 パブリッシャーは両方のレスポンスが到着した後、結合をローカルで実行します。彼らは自身のインフラで同意ロジック、フリークエンシー管理、関連性ランキングを適用できます。 ### TMPX 露出トークンと構造的分離 Identity Match レスポンスは、TMPX トークン — ユーザーの解決されたアイデンティティトークンを含む HPKE 暗号化された blob — を運ぶ `tmpx` フィールドを含められます。このトークンは、ユーザーごとの露出トラッキングのため、クリエイティブトラッキング URL を通じてバイヤーのインプレッションピクセルに流れます。 このデータフローはアイデンティティとコンテキストのパスを橋渡しします: TMPX トークンは Identity Match によって生成され、Context Match オファーから発生するクリエイティブトラッキング URL 経由で消費されます。しかし、橋渡しは **パブリッシャー側** で起こります — パブリッシャーは 2 つのレスポンスをローカルで結合し、広告配信中に TMPX 値をトラッキング URL に代入します。バイヤーの読み取りレプリカが暗号化トークンを生成します。バイヤーのインプレッションピクセルがそれを受け取ります。パブリッシャーは不透明な blob のみを見て、その値をパース、ログ、またはそれに基づいて決定してはなりません(MUST NOT)。 TMPX トークンは構造的分離に違反しません。なぜなら: * ルーターは決して復号された内容を見ません — 不透明な `tmpx` フィールドを通過させます。 * パブリッシャーはそれを解釈せずに値をトラッキング URL に代入します。 * バイヤーのクラスターマスターのみがトークンを復号できます(HPKE `mode_base` — マスターの公開鍵で暗号化、マスターの秘密鍵のみが復号可能)。 * Identity Match リクエストの `country` ルーティングディレクティブは、転送前にルーターによって剥がされます — バイヤーエージェントはユーザーがどの国にいるかを決して見ません。 ## パッケージセット相関除去 コンテキストパスがこのページに関連するパッケージのみを送り、アイデンティティパスが同じパッケージのみを送ったら、バイヤーは 2 つのセットを比較しアイデンティティリクエストがどのページから来たかを推論できます。TMP は構造的に異なるセットを要求することでこれを防ぎます: * **Context Match** はパッケージリストを送りません。プロバイダーはプレースメントの同期されたパッケージセットを評価します — プレースメントごとに安定、すべてのユーザーで同じ。リクエストごとにパッケージが送られないため、パブリッシャーはパッケージフィルタリングを通じて誤ってアイデンティティを漏らせません。 * **Identity Match** は `package_ids` を送ります(または完全に省略、その場合バイヤーは `seller_agent_url` の完全な登録済みアクティブセットに対して評価)。送られるとき、構成は現在のプレースメントと統計的に独立でなければなりません(MUST) — all-active(このパブリッシャーでのこのバイヤーのすべてのアクティブパッケージ)または fuzzed(バイヤーが黙って落とす合成の存在しない ID で任意にパディングされたランダムサンプル)のいずれか。バイヤーはこのセットに対してユーザーを評価し、ページ固有のサブセットだけに対してではありません。 コンテキストセットは 1 つのプレースメントにスコープされます。アイデンティティセットは 1 つのバイヤーの全アクティブインベントリにスコープされます。2 つのセットは構造的に異なり、どちらも他についての情報を明かしません。 パブリッシャーは交差をローカルで実行します: どのパッケージが context match によってアクティベートされ、かつ identity match によって適格だったか。 ## 時間的相関除去 別々のコードパスと異なるパッケージセットがあっても、Context Match と Identity Match リクエストが同じパブリッシャーから同じ瞬間に — または常に同じ順序で — バイヤーに到着したら、タイミングまたは順序の相関が可能です。TMP は両方に対処します: * パブリッシャーはコンテキストとアイデンティティのリクエスト間にランダムな遅延を導入すべきです(SHOULD)。推奨範囲は 100-2000ms、一様分布。 * パブリッシャーは順序もランダム化すべきです(SHOULD): 各機会は context match が先か identity match が先かのほぼ等しい確率を持つべき。遅延がランダム化されても、固定順序は順序を通じてペアリングを漏らします。 * パブリッシャーは複数のページビューにわたって Identity Match リクエストをバッチしてもよく(MAY)、各アイデンティティリクエストがどのコンテキストリクエストに対応するかをさらに不明瞭にします。 * パブリッシャーはコンテキストとアイデンティティのリクエストを異なるネットワークパス経由でルーティングしてもよい(MAY)。 時間的相関除去は多層防御です。それは主要な分離メカニズムではありません。構造的分離とパッケージセット相関除去がそうです。しかしそれはタイミングのサイドチャネルを閉じます。 ## TEE アテステーションの詳細 TMP のリファレンスアーキテクチャは AWS Nitro Enclaves をターゲットにしますが、プロトコルは TEE 非依存です。検証可能なアテステーションドキュメントを生成する任意の TEE が互換です。 ### アテステーションが証明するもの * エンクレーブ内で実行されるルーターバイナリが、公開・監査されたソースコードに一致する。 * コンテキストとアイデンティティのコードパスが、共有状態なしに構造的に分離されている。 * バイナリがオペレーター、ホスティングプロバイダー、または任意のランタイムプロセスによって変更されていない。 ### アテステーションが証明しないもの * バイヤーエージェントが受け取ったデータを責任を持って扱うこと。TMP はバイヤーが受け取るものを制限します。彼らがそれで何をするかは制御しません。 * パブリッシャーの結合ロジックが正しいこと。パブリッシャーはファーストパーティで、TMP の分離モデルに制約されません。 * コードがバグから自由であること。アテステーションはコードが公開されたソースに一致することを証明します。そのソースが正しいかは別の問題で、オープンソース監査によって対処されます。 ### アテステーション測定 各アテステーションドキュメントは、実行中の環境の暗号学的ハッシュを含みます: | Measurement | What it covers | | -------------------- | -------------------------------------------- | | **Image hash** | エンクレーブイメージのハッシュ。バイナリが期待されるビルドに一致することを確認。 | | **Kernel hash** | 動作環境のハッシュ。 | | **Application hash** | アプリケーションレベルのコードのハッシュ。 | | **Role hash** | エンクレーブの権限が期待に一致することを確認(例: 外部データベースへのアクセスなし)。 | パブリッシャーまたは監査者は、これらの測定を公開されたビルドアーティファクトに対して検証できます。この検証は自動化され継続的に実行できます。 ## OpenRTB との比較 | Signal | OpenRTB | TMP | | ----------------- | ------------------- | -------------------------------------- | | ユーザー ID + ページ URL | 同じ入札リクエスト | 別々のコードパス、決して結合されない | | デバイスフィンガープリント | 入札リクエストに含む | 決して送らない | | IP アドレス | 入札リクエストに含む | 決して送らない | | 生の cookie | 入札リクエストに含む | 決して送らない | | GPS 座標 | 入札リクエストに含む | 決して送らない | | 閲覧履歴 | cookie sync 経由で構築可能 | 構築不能: バイヤーはアイデンティティ + コンテキストを一緒に決して見ない | | 分離の検証 | エクスチェンジを信頼 | コード監査(TEE なし)または TEE アテステーション(TEE あり) | OpenRTB では、入札リクエストはすべてのバンドルです: ユーザーアイデンティティ、デバイスシグナル、ページコンテキスト、行動データ。オークションのすべての参加者が完全なバンドルを受け取ります。プライバシーは、データを悪用しないという契約的約束に依存します。 TMP はプロトコルレベルでバンドルを分割します。バイヤーはコンテキストまたはアイデンティティを受け取り、決して両方ではありません。分離は構造的であり契約的ではありません。 ## 規制姿勢 TMP の構造的分離は、GDPR、CCPA、ePrivacy、類似の規制が要求するデータ最小化原則に整合します: * バイヤーエージェントはコンテンツコンテキストとペアになったユーザーアイデンティティを決して受け取りません。最小化はポリシーではなくプロトコルによって強制されます。 * パブリッシャーは、直接のユーザー関係を持つファーストパーティとして、結合を制御します。データセットを結合する前に同意ロジックを適用できます。 * TEE アテステーションにより、分離は独立に検証可能で、規制当局に監査可能な証拠を提供します。 これはアーキテクチャの観察であり法的助言ではありません。パブリッシャーとバイヤーエージェントは、規制コンプライアンスについて自身の法律顧問に相談すべきです。 TMP のアーキテクチャが GDPR コントローラーとプロセッサーロールにどうマップするかの詳細な分析については、[データ保護ロール](/docs/trusted-match/data-protection-roles) を参照。クロスプロトコルのプライバシーガイダンスについては、[プライバシー考慮事項](/docs/reference/privacy-considerations) を参照。 # TMP ルーター Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/router-architecture 単一バイナリ設計、統合パス、ファンアウトを含む、TMP ルーターのアーキテクチャと運用。 # TMP ルーター TMP ルーターは、パブリッシャーとバイヤーエージェントの間に位置するインフラです。リクエストのファンアウト、レスポンスのマージ、プライバシー強制を扱います。決定を下しません — リクエストをルーティングしレスポンスを集約します。パブリッシャーはルーターがどのプロバイダーを呼ぶかを設定します。 ## ルーターがすること 1. **リクエストをファンアウトする**: `context_match` ケイパビリティを持つ設定されたすべてのプロバイダーに Context Match リクエストを送る。`identity_match` ケイパビリティを持つ設定されたすべてのプロバイダーに Identity Match リクエストを送る。 2. **レスポンスをマージする**: 複数のプロバイダーからのオファー、エンリッチメントシグナル、適格性結果を統一されたレスポンスに結合する。 3. **分離を強制する**: コンテキストとアイデンティティのコードパスは構造的に分離 — コンテキストパスは決してアイデンティティデータにアクセスせず、逆も同様。 4. **レイテンシーを管理する**: 適応的タイムアウトを適用し、一貫してレイテンシー予算を超えるプロバイダーを非優先化する。 ## 単一バイナリ、分離されたコードパス ルーターは 2 つの構造的に分離されたコードパスを持つ単一の Go バイナリです: 1 つは context match 用、1 つは identity match 用。 ``` ┌──────────────────────────────────────────────────────────────┐ │ TMP Router │ │ │ │ ┌───────────────────────────┐ ┌───────────────────────────┐│ │ │ Context Match Path │ │ Identity Match Path ││ │ │ │ │ ││ │ │ Inputs: │ │ Inputs: ││ │ │ • Artifact IDs / artifact │ │ • Opaque user token ││ │ │ • Context signals │ │ • ALL active package IDs ││ │ │ • Geo, URL hash │ │ ││ │ │ • Available packages │ │ ││ │ │ │ │ Outputs: ││ │ │ Outputs: │ │ • Eligible package IDs ││ │ │ • Offers │ │ • TTL (seconds) ││ │ │ • Enrichment signals │ │ ││ │ │ │ │ ││ │ │ Never touches: │ │ Never touches: ││ │ │ • User tokens │ │ • URLs ││ │ │ • Any identity data │ │ • Content signals ││ │ └───────────────────────────┘ └───────────────────────────┘│ │ │ │ No shared state between code paths. │ │ One binary, one audit surface, one Docker image. │ └──────────────────────────────────────────────────────────────┘ ``` 分離はコードの中にあり監査可能です。コンテキストパスは、アイデンティティデータが渡されず、到達可能などの場所にも保存されず、コンテキストパスが処理するどのデータ構造でも参照されないため、それを読めません。アイデンティティパスには逆が同じように適用されます。ルーターはオープンソースです — 誰でもソースを読んでこれを検証できます。 TEE アテステーションはアップグレードパスです。TEE なしでは、オペレーターが公開されたバイナリをデプロイしたことを信頼します。TEE ありでは、アテステーションがデプロイされたバイナリが監査されたソースに一致することを証明し、その信頼要件を除去します。 ## プロバイダー登録 パブリッシャーはルーターがどのプロバイダーを呼ぶかを設定します。これは運用上の関係です — パブリッシャーはプロバイダーが自身の広告決定に参加することを信頼します。プロバイダー登録は `provider-registration` スキーマ(`/schemas/trusted-match/provider-registration.json`)に従います。 ### ディスカバリーモデル プロバイダー登録は通常 **ページ設定** から来ます — パブリッシャーは Prebid モジュール設定またはサーフェス固有のセットアップでプロバイダーを宣言します。これが標準パスで、安定したプロバイダーセットを持つパブリッシャーにうまく機能します。 **静的設定**(Prebid config、YAML ファイル、infrastructure-as-code): * パブリッシャーがデプロイ時にプロバイダーを宣言 * ルーターが起動時と設定リロード時に設定を読む * 変更には設定更新とリロード/再デプロイが必要 * ほとんどのデプロイに適切 — プロバイダーリストはめったに変わらない **動的登録**(API 駆動、データベース裏付け): * パブリッシャーが管理インターフェースを通じてプロバイダーを管理 * ルーターがディスカバリーエンドポイントをポーリングするか設定変更を監視 * 変更は 1 リフレッシュサイクル内に有効になる(推奨: 30 秒) * 多くのプロバイダーを管理するか、再デプロイなしのランタイム更新が必要なパブリッシャーに適切 * 動的登録エンドポイントは、プロバイダー `endpoint` URL が外部 HTTPS アドレスであることを検証しなければならない(MUST)。実装は、プロバイダー登録を通じた SSRF を防ぐため、プライベート(RFC 1918)、リンクローカル(169.254.x.x)、クラウドメタデータの IP 範囲を拒否しなければならない(MUST)。完全な規範的要件 — エンドポイント URL 検証(DNS 再解決を伴う)、動的登録エンドポイント認証、ルーター対プロバイダー認証の最低ライン、`/health` エンドポイントガイダンス — については仕様の [プロバイダー登録セキュリティ](/docs/trusted-match/specification#provider-registration-security) を参照。 両モデルは同じスキーマを使います。ルーターは YAML ファイルからロードされたプロバイダーと API からロードされたプロバイダーを区別しません — 登録フィールドは同一です。 ### 登録フィールド | Setting | Type | Required | Description | | ---------------- | ------------- | ----------- | ---------------------------------------------------------------------------------------------- | | `provider_id` | string | Yes | このプロバイダーの安定した識別子。ログ、メトリクス、キャッシュキーで使う。 | | `endpoint` | URL | Yes | プロバイダーのベース URL。ルーターはディスパッチ時に `/context` または `/identity` を追加する。 | | `context_match` | bool | No | プロバイダーが Context Match リクエストを扱う。`context_match` または `identity_match` の少なくとも一方が true でなければならない。 | | `identity_match` | bool | No | プロバイダーが Identity Match リクエストを扱う。`context_match` または `identity_match` の少なくとも一方が true でなければならない。 | | `countries` | List\ | Conditional | ISO 3166-1 alpha-2 国コード。`identity_match` が true のとき存在しなければならない(MUST)。 | | `uid_types` | List\ | Conditional | このプロバイダーが解決するアイデンティティタイプ。`identity_match` が true のとき存在しなければならない(MUST)。 | | `timeout_ms` | integer | No | プロバイダーごとのタイムアウト。ルーターの `latency_budget_ms` 以下でなければならない。デフォルト: 50。 | | `priority` | integer | No | マージ衝突解決順(低い = 高優先)。デフォルト: 0。 | | `status` | enum | No | `active`、`inactive`、または `draining`。デフォルト: `active`。 | | `properties` | List\ | No | このプロバイダーが提供するプロパティ RID。欠如のとき、プロバイダーはすべてのプロパティを提供する。 | `context_match` または `identity_match` の少なくとも一方が true でなければなりません — どちらの操作も扱わないプロバイダーは無効です。`identity_match` が true のとき、`countries` と `uid_types` は **必須** です — ルーターはそれらなしに Identity Match リクエストをルーティングできません。ルーターは無効なプロバイダー登録を拒否しなければならず(MUST)、誤設定されたプロバイダーを識別する警告をログすべきです(SHOULD)。 ### プロバイダーライフサイクル プロバイダーは 3 つのライフサイクル状態を持ちます: * **Active**: プロバイダーが通常どおりリクエストを受け取る。 * **Draining**: プロバイダーが新しいリクエストの受け取りを停止する。飛行中のリクエストは通常どおり完了する。プロバイダーを保守のためオフラインにするとき使う。 * **Inactive**: プロバイダーが完全にスキップされる。設定を削除せずにプロバイダーを無効化するとき使う。 ### プロバイダーヘルス プロバイダーはそのベース URL で `GET /health` を公開すべきです(SHOULD)。ルーターはこれを次に使います: * **プリフライトチェック**: 起動時または設定リロード時に、ファンアウトに含める前に各プロバイダーが到達可能であることを検証。 * **定期監視**: 設定可能な間隔(推奨: 30 秒)でプロバイダーヘルスを確認。連続するヘルスチェックに失敗するプロバイダーは、一時的にファンアウトから除外され(MAY)、ヘルスが回復したとき自動的に再包含される。 ヘルスチェックはリクエストのホットパスにはありません — バックグラウンド間隔で実行されます。ルーターの `/healthz` エンドポイントは、個々のプロバイダーステータスではなく全体的なルーターヘルスを反映します。 プロバイダーは `context_match` と `identity_match` の任意の組み合わせをサポートしてもよい(MAY)。コンテキストのみのプロバイダーはエンリッチメントまたはコンテキストターゲティングを扱います。アイデンティティのみのプロバイダーはフリークエンシーキャッピングを扱います — パブリッシャーはメディアバイのターゲティングルールからコンテキストをローカルで評価し、アイデンティティチェックのためだけにバイヤーを呼びます。 すべての通信は HTTP/2 上の JSON を使います。TMP メッセージは小さい(200-600 バイト) — これらのサイズでは、シリアライゼーション形式は総レイテンシーの 1% 未満です。 ## 統合 ### Prebid 統合 Prebid Server または Prebid.js を持つパブリッシャーは、ベンダー固有の RTD モジュールを置き換える TMP モジュールを追加します。TMP モジュールは Context Match と Identity Match リクエストをルーターに送り、マージされたレスポンスをターゲティングシグナルとパッケージアクティベーションデータとして返します。パブリッシャーのアドサーバー(GAM など)はターゲティングキー値を受け取り、対応するラインアイテムをアクティベートします。 ### 非 Prebid サーフェス AI アシスタント、モバイルアプリ、CTV、リテールメディアについては、ルーターは直接 HTTP/2 API を提供します。HTTP/2 POST リクエストを行える任意のプラットフォームが統合できます。リクエストとレスポンスのスキーマはサーフェスにかかわらず同じです。 ### SSP と DSP 統合 SSP と DSP は TMP プロバイダーとして統合します — ファンアウト中にルーターが呼ぶエンドポイントを公開します。これは既存の RTD 統合と同じパターンです。 ### アイデンティティトークン アイデンティティトークンは、既にページ上またはアプリ内に存在する既存のプロバイダー(ID5、LiveRamp、UID2 など)から来ます。TMP はトークンのライフサイクルを仕様化しません — パブリッシャーのアイデンティティスタックが既に生成するトークンを消費します。 ## ファンアウトとレスポンスマージ ### Context Match ファンアウト パブリッシャーが Context Match リクエストを送るとき: 1. ルーターはリクエストの `property_rid` について `context_match` ケイパビリティで設定されたすべてのプロバイダーを識別する。 2. HTTP/2 上で一致するすべてのプロバイダーに並列でリクエストを送る。 3. レイテンシー予算(デフォルト: 50ms)までレスポンスを待つ。 4. レスポンスをマージする: * **オファー** はすべてのプロバイダーから収集される。2 つのプロバイダーが同じ `package_id` のオファーを返す場合(まれ — パッケージは通常プロバイダー固有)、ルーターは最初に受け取ったレスポンスを保持する。プロバイダーをまたいだ重複する `package_id` は設定エラー。ルーターは警告をログすべき(SHOULD)。 * **エンリッチメントシグナル** は連結される。すべてのプロバイダーからのセグメントが単一のリストに結合される。異なるプロバイダーからのターゲティングキー値は衝突を防ぐため名前空間化される。 5. マージされたレスポンスをパブリッシャーに返す。 ### Identity Match fan-out ルーターは国とアイデンティティタイプで Identity Match プロバイダーをフィルターします: 1. ルーターはリクエストから `country` フィールド(アイデンティティシグナルではなくルーティングディレクティブ)を読む。 2. `countries` リストがその国コードを含むプロバイダーを選択する。 3. さらに、`uid_types` リストがリクエストの `identities` 配列の任意の `uid_type` と重なるプロバイダーにフィルターする。 4. 選択された各プロバイダーについて、**`identities` 配列をフィルター** して、リクエストのアイデンティティとそのプロバイダーの宣言された `uid_types` の交差にする。プロバイダーは宣言しなかったタイプのアイデンティティトークンを受け取ってはならない(MUST NOT) — これは minimum-necessary-data を運用上のものではなく構造的なプライバシープロパティとして強制する。ルーターはアイデンティティトークンを追加、置換、変換してはならない(MUST NOT)。転送されたセットはパブリッシャー起源の `identities` 配列のサブセットでなければならない(MUST)。 5. 交差が空の場合、ルーターはそのプロバイダーを完全にスキップしなければならない(MUST)。空の `identities` 配列は有効な IMR ペイロードでなく(スキーマが `minItems: 1` を強制)、skip 対 forward を区別可能なテレメトリとして発することは、各ユーザーがどのアイデンティティタイプを利用可能だったかを漏らす。 6. バイヤーエージェントにリクエストを転送する前に **`country` フィールドを剥がす**。 7. プロバイダーごとのペイロードがインバウンドリクエストと異なるため、ルーターはフィルターされたセットの正準署名フィールド — `identities_hash`(アイデンティティごとの `attestation` をカバー)と、存在するとき `audience_kid` によってそのプロバイダーにルーティングされた `sealed_credentials[]` エントリの `sealed_credentials_hash` — に対して各プロバイダーごとの転送を **再署名** する([Identity Match signed fields](/docs/trusted-match/specification#identity-match-signed-fields) で定義)。プロバイダーはルーターの公開鍵に対して署名を検証する。 8. 一致するすべてのプロバイダーに並列でファンアウトし、適格性結果をマージし、統一されたレスポンスを返す。 プロバイダーをまたいだ重複する `package_id` は設定エラーです — パッケージはメディアバイから来てプロバイダー固有です。発生した場合、ルーターは保守的なマージを適用します: パッケージは両方のプロバイダーの `eligible_package_ids` に現れるときのみ適格。ルーターはプロバイダーをまたいだ最小の `serve_window_sec` を使い、警告をログすべき(SHOULD)。 **TMPX 収集。** TMPX トークンを鋳造するのに十分なアイデンティティ素材を解決する各アイデンティティプロバイダーは、その順序付けられたマクロ/値ペアを `tmpx_macros[]` で返します — `{ name, value }` のペアで、`name` はプロバイダーの登録された `tmpx_macros` スロット(provider-registration.json)の 1 つ、`value` は URL セーフなワイヤー文字列です。ルーターはそれらのエントリを、発行プロバイダーの `provider_id` でキー付けされたレスポンスの `tmpx_providers` マップに収集しなければならず(MUST)、プロバイダーごとのインプレッション会計がファンアウトを生き残るようにします。ルーターはプロバイダーのペア順を逐語的に保持しなければならず(MUST) — チャンクは決定論的 — 複数のプロバイダーの値を単一の `tmpx` 文字列に折り畳んではなりません(MUST NOT)。ルーターは `provider_id` からマクロ名を合成してはなりません(MUST NOT)。トラフィッキングは事前に登録された名前に対して設定されます。ルーターはアウトバウンドレスポンスのルートに `tmpx_macros` を運んではなりません(MUST NOT) — そのフィールドはプロバイダー→ルーターのキャリアです。`tmpx_providers` と並んでそれを漏らすと、パブリッシャーにどれを読むべきかのスキーマシグナルを与えず、同じ値を 1 つのスロットに二重発火するリスクがあります。任意の TMPX を発しないプロバイダー(例: 適格なパッケージなし)は、空の `macros[]` で表現されるのではなくマップから省略されなければなりません(MUST)。非推奨の単数 `tmpx` フィールドを読むコンシューマーとの後方互換性のため、ルーターは 1 つのプロバイダーの最初のスロット値で `tmpx` も投入してもよい(MAY)。両方のフィールドが存在するとき、`tmpx_providers` が権威的です。 ### タイムアウト処理 ルーターは 2 つの明確なタイムアウト値を管理します: * **全体レイテンシー予算**(`latency_budget_ms`): ルーターがファンアウトし、レスポンスを収集し、マージする総時間。デフォルト: 50ms。これはパブリッシャーが広告配信パイプライン内で TMP に割り当てるエンドツーエンドの予算。 * **プロバイダーごとのタイムアウト**(プロバイダー登録の `timeout_ms`): ルーターが単一のプロバイダーを待つ最大時間。全体レイテンシー予算以下でなければならない。デフォルト: 50ms(単一プロバイダー設定では予算と等しい)。 複数のプロバイダーが設定されるとき、プロバイダーごとのタイムアウトが各個別プロバイダーの実効上限で、全体予算がファンアウト全体の上限です。ルーターは各プロバイダーについて 2 つのうちより厳しい方を強制します。例えば: 50ms の全体予算と各 40ms に設定された 2 つのプロバイダーで、両プロバイダーが並列に呼ばれ、ルーターは合計で最大 50ms 待ちます — プロバイダー A が 45ms で応答すると、プロバイダー B は既に 40ms でタイムアウトしています。 * **単一プロバイダータイムアウト**: そのプロバイダーをスキップし、そのレイテンシーパーセンタイルをログし、残りのプロバイダーからのレスポンスで続行。スキップされたプロバイダーのパッケージはこのリクエストについて「アクティベートされない」として扱われる。 * **すべてのプロバイダータイムアウト**: 空のレスポンスを返す — Context Match のオファーなし、Identity Match の適格性なし。パブリッシャーは既存の需要ソース(Prebid オープンオークション、直接販売など)にフォールバックする。 * **適応的タイムアウト**: ルーターはプロバイダーごとのレイテンシーパーセンタイル(p50、p95、p99)を追跡し、時間をかけて割り当てを調整する。一貫して遅いプロバイダーはより小さいタイムアウト割り当てを受けるか先制的にスキップされる。適応的割り当てがアクティブなとき、より高優先のプロバイダー(低い `priority` 値)は予算のより大きなシェアを受ける。これは運用上の決定であり、プロトコル要件ではない。 ## レイテンシー予算 TMP は 50ms 未満のエンドツーエンドレイテンシーをターゲットにします: パブリッシャーがリクエストを送り、ルーターがファンアウトし、プロバイダーが応答し、ルーターがマージし、パブリッシャーがレスポンスを受け取る。 これが達成可能なのは: * **小さいメッセージ**: TMP リクエストは 200-600 バイトの JSON — 典型的な OpenRTB 入札リクエストのおよそ 10-20 倍小さい。シリアライゼーションはマイクロ秒未満。 * **価格計算なし**: パッケージは事前交渉済み。プロバイダーはオークションダイナミクスではなくターゲティング基準を評価する。 * **並列ファンアウト**: すべてのプロバイダーが同時に呼ばれる。総レイテンシーは合計ではなく最も遅いプロバイダーの応答時間。 * **ステートレスルーター**: ホットパスにデータベースルックアップなし。ルーターの唯一の仕事は転送とマージ。 * **接続再利用**: HTTP/2 多重化により、単一の接続で各プロバイダーへの並行リクエストが可能。 ## ベンダー RTD モジュールとの比較 TMP ルーターは、ベンダー固有の RTD モジュールが今日することを一般化します。単一ベンダーの RTD モジュールはコンテンツに対してパッケージをリアルタイムで評価しますが、1 つのプロバイダー、1 つのサーフェス(Prebid)にロックされ、完全な OpenRTB BidRequest を送ります。 TMP ルーターはこれをマルチプロバイダー、マルチサーフェス、プロトコル標準の代替に置き換えます: | | Vendor RTD Module (today) | TMP Router | | ---------- | ------------------------------------- | ------------------------------------------- | | プロバイダー | 単一ベンダー | TMP ケイパビリティを宣言する任意のプロバイダー | | ディスカバリー | パブリッシャー設定 | パブリッシャー設定 | | サーフェス | Web(Prebid Server) | Web、AI、モバイル、CTV、リテールメディア | | リクエスト形式 | 完全な OpenRTB BidRequest(約 2-10KB JSON) | TMP ContextMatchRequest(約 200-600 バイト JSON) | | プライバシー | 送信前のデータマスキング | 構造的分離(TEE 対応) | | アイデンティティ処理 | 入札リクエスト内のユーザー ID | 別個の Identity Match 操作 | 既存の Prebid Server デプロイについて、TMP モジュールはベンダー固有の RTD モジュールを汎用 TMP クライアントに置き換えます。Prebid のないサーフェスについて、ルーターの HTTP/2 API が同じ機能を提供します。 ## TEE オークションインフラとの関係 TEE ベースのオークションインフラ(暗号化された入札、アテステーション証明、検証可能な勝者選択)は TMP と補完的です。パブリッシャーが複数のバイヤーからのアクティベートされたパッケージ間で競争的選択を望むとき: 1. TMP ルーターが Context Match レスポンス(各バイヤーがアクティベートしたいパッケージ)を収集する。 2. パブリッシャーがアクティベートされたパッケージ(事前交渉された価格付き)を TEE オークションに提出する。 3. TEE エンクレーブが勝者を選択しアテステーション証明を生成する。 4. パブリッシャーが勝ったパッケージをアクティベートする。 TMP はマッチングを扱います。TEE オークションは競争を扱います。パブリッシャーはそもそも競争が必要かを選びます — 多くのサーフェス(編集 AI コンテンツ、CTV ポッド構成、リテールカルーセル)は、価格ベースのオークションよりパブリッシャー側の関連性ランキングによってよりよく提供されます。 TEE オークションインフラ(AWS Nitro Enclaves、アテステーション、鍵管理)は、TMP ルーターを TEE アテスト済み運用にアップグレードするとき直接適用可能で、プロトコルの自然なインフラパートナーになります。 ## Deployment TMP ルーターは [adcp-go](https://github.com/adcontextprotocol/adcp-go) 上に構築された単一の Go バイナリです。プロバイダーとそのケイパビリティをリストする設定ファイルを読みます。各プロバイダーはそのベース URL の下に 2 つのパスベースのエンドポイント — `POST /context` と `POST /identity` — を公開し、ルーターはパスでディスパッチします。 ### 設定 ```yaml theme={null} # tmp-router.yaml listen: ":8443" tls: cert: /etc/tmp/tls.crt key: /etc/tmp/tls.key latency_budget_ms: 50 adaptive_timeout: true health_check_interval_sec: 30 providers: # US cluster — UID2, RampID, ID5 - provider_id: acme-outdoor-us endpoint: https://us.tmp.acmeoutdoor.example/v1 context_match: true identity_match: true countries: [US] uid_types: [uid2, rampid, id5] timeout_ms: 40 priority: 0 properties: ["01916f3a-9c4e-7000-8000-000000000010"] # EU cluster — EUID, ID5 - provider_id: acme-outdoor-eu endpoint: https://eu.tmp.acmeoutdoor.example/v1 context_match: true identity_match: true countries: [DE, FR, IT, ES, NL, BE, AT, PL, SE, DK, FI, IE, PT, GR, CZ, RO, HU, BG, HR, SK, SI, LT, LV, EE, CY, MT, LU, GB] uid_types: [euid, id5] timeout_ms: 40 priority: 0 properties: ["01916f3a-9c4e-7000-8000-000000000010"] # Context-only enrichment provider (no identity match, no country scoping needed) - provider_id: enrichment-co endpoint: https://enrichment.example/v1 context_match: true identity_match: false timeout_ms: 30 priority: 10 ``` ### コンテナデプロイ ```dockerfile theme={null} FROM ghcr.io/adcontextprotocol/tmp-router:latest COPY tmp-router.yaml /etc/tmp/config.yaml EXPOSE 8443 ``` ルーターはステートレスです — データベースなし、永続ストレージなし。任意のロードバランサーの背後で水平スケールできます。ヘルスチェックは `/healthz` で利用可能です。 ### キャパシティプランニング 各ルーターインスタンスは、2-vCPU コンテナで毎秒約 10,000 リクエストを扱います。メモリ使用量は、リクエストボリュームではなく、プロバイダーへの並行接続数に線形にスケールします。 Web パブリッシャーには、point of presence(PoP)ごとに 1 つのルーターインスタンスが典型的です。AI プラットフォームには、ルーターがエンドツーエンドレイテンシーに 5ms 未満を追加するため、地域フェイルオーバーを伴う中央集権デプロイで十分です。 ### 監視 ルーターは `/metrics` で Prometheus メトリクスを公開します: | Metric | Description | | -------------------------------- | ----------------------------------- | | `tmp_context_match_duration_ms` | Context Match エンドツーエンドレイテンシーヒストグラム | | `tmp_identity_match_duration_ms` | Identity Match エンドツーエンドレイテンシーヒストグラム | | `tmp_provider_duration_ms` | プロバイダーごとの応答時間ヒストグラム | | `tmp_provider_timeout_total` | プロバイダーごとのタイムアウトカウンター | | `tmp_provider_error_total` | プロバイダーごとのエラーカウンター | | `tmp_offers_total` | すべてのプロバイダーにわたって返された総オファー | `tmp_provider_timeout_total` の増加でアラート — 一貫してタイムアウト予算を超えるプロバイダーは、それを含むすべてのリクエストのマッチ品質を劣化させます。 # TMP 仕様 Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/specification Trusted Match Protocol の権威あるメッセージタイプ定義、フィールド表、プライバシー要件、適合性レベル。 # Trusted Match Protocol 仕様 **実験的。** Trusted Match Protocol は実験的サーフェスとして AdCP 3.0 の一部です — 少なくとも 6 週間の予告をもって 3.x リリース間で変わることがあります。TMP を実装するセラーは `experimental_features` に `trusted_match.core` を宣言しなければなりません(MUST)。完全なコントラクトについては [実験的ステータス](/docs/reference/experimental-status) を参照。このサーフェスのフィールドは 3.0.0 GA まで非推奨サイクルの対象になりません。 これは Trusted Match Protocol(TMP)の権威あるリファレンスです。概念的な導入については、[概要](/docs/trusted-match/) と [コアコンセプト](/docs/trusted-match/context-and-identity) を参照。 進化が予想される特定の領域には、TMPX 露出トークン、国分割アイデンティティ、Offer マクロが含まれます — 計画された変更については [3.1.0 ロードマップ](https://github.com/adcontextprotocol/adcp/issues/2201) を参照。 ## Definitions | Term | Definition | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Context Match** | 利用可能なパッケージをコンテンツコンテキストに対して評価する TMP 操作。ユーザーアイデンティティを運ばない。 | | **Identity Match** | ユーザー適格性をパッケージ基準に対して評価する TMP 操作。ページコンテキストを運ばない。 | | **TMP Router** | TMP リクエストをバイヤーエージェントにファンアウトしレスポンスをマージするインフラ。コンテキストとアイデンティティの両リクエストを、構造的に分離されたコードパスで扱う単一のバイナリ。 | | **Offer** | context match リクエストへのバイヤーの応答。シンプルなアクティベーション(package\_id のみ)から、ブランド、価格、summary、クリエイティブマニフェストを伴うリッチな提案まで及ぶ。 | | **Available package** | 特定のプレースメントで評価に適格な、アクティブなメディアバイからのパッケージ。パッケージメタデータ(発信元セラーエージェントを含む)はメディアバイ時に同期される。[Package Sync](#package-sync) を参照。 | | **Seller agent** | パッケージをパブリッシャーに販売したバイヤー側のエージェント。パブリッシャーの `adagents.json` `authorized_agents[].url` で宣言されたエージェント URL によって識別される。すべての `AvailablePackage` は同期時に正確に 1 つのセラーエージェントにバインドされる。 | | **Eligibility** | Identity Match が返す適格なパッケージ ID のリストと、サーブウィンドウスロットル。バイヤーはフリークエンシーキャップ、オーディエンスメンバーシップ、その他のシグナルから適格性を計算する。理由はパブリッシャーにとって不透明。 | | **Artifact** | パブリッシャープロパティに関連付けられた型付きコンテンツ参照(記事 URL、エピソード EIDR、番組 Gracenote ID、音楽 ISRC、プロダクト GTIN、会話ターン)。各アーティファクトは `type` と `value` を持つ。context match リクエストで参照される。 | | **Temporal decorrelation** | Context Match と Identity Match リクエスト間のランダムな遅延とランダムな順序で、タイミングと順序ベースの相関を防ぐ。 | ## Message Types すべての TMP メッセージタイプは、デシリアライゼーションのためにメッセージを識別する `type` フィールドを含みます。ルーターとエージェントはこのフィールドを使って JSON ボディをパースする正しいスキーマを選択します。 | Message | `type` value | | ----------------------- | ------------------------- | | Context Match request | `context_match_request` | | Context Match response | `context_match_response` | | Identity Match request | `identity_match_request` | | Identity Match response | `identity_match_response` | | Error response | `error` | ### ContextMatchRequest パブリッシャー(ルーター経由)からバイヤーエージェントに送られます。コンテンツコンテキストを含みます。ユーザーアイデンティティを含んではなりません(MUST NOT)。 | Field | Type | Required | Description | | ------------------ | ------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `type` | string | Yes | `"context_match_request"`。デシリアライゼーションのメッセージタイプ判別子。 | | `protocol_version` | string | No | TMP プロトコルバージョン。デフォルト: `1.0`。受信者がバージョン間の意味的差異を扱えるようにする。 | | `request_id` | string | Yes | ログ用の一意のリクエスト識別子。任意の Identity Match request\_id と相関してはならない(MUST NOT)。 | | `property_rid` | UUID | Yes | プロパティカタログ UUID(v7)。グローバルに一意、安定。 | | `property_id` | string | No | パブリッシャーの人間可読なスラッグ。`property_rid` が存在するとき任意。 | | `property_type` | enum | Yes | 次の 1 つ: `website`、`mobile_app`、`ctv_app`、`desktop_app`、`dooh`、`podcast`、`radio`、`linear_tv`、`streaming_audio`、`ai_assistant`。`property-type` enum を参照。 | | `placement_id` | string | Yes | パブリッシャーの `adagents.json` のプレースメントレジストリからのプレースメント識別子。リクエストごとに 1 プレースメント。 | | `seller_agent_url` | string (URI) | Yes | このリクエストを発行するセラーエージェントの API エンドポイント URL。プロバイダーはそれに対してこのセラーに同期したアクティブなパッケージセットを解決する。同期されていないセラーは、別のセラーのセットへのフォールバックではなく空のオファーセットを生成しなければならない(MUST)。ユーザーアイデンティティを運ばない単一のプレースメントごとの値。AdCP URL 正準化で比較される。Identity Match リクエストの `seller_agent_url` および `adagents.json` の `agent_url` と一貫。 | | `artifact` | Artifact | No | この広告機会に隣接する完全なコンテンツアーティファクト。コンテンツ標準評価と同じスキーマ。パブリッシャーはバイヤーに実際のコンテンツを評価させたいとき完全なアーティファクトを送る。契約上の保護がバイヤーの使用を統治する。TEE デプロイが契約的信頼を暗号学的検証に格上げする。 | | `artifact_refs` | List\ | No | バイヤーが独立して解決できる公開コンテンツ参照。各は `type`(次の 1 つ: `url`、`url_hash`、`eidr`、`gracenote`、`isrc`、`gtin`、`rss_guid`、`isbn`、`custom`)と `value` を持つ。URL アドレス可能なコンテンツについては、バイヤーがこれらを事前分類している場合がある。パブリッシャーが URL を明かしたくないとき(コンテキストクリーンルーム)は `url_hash` を使う。 | | `context_signals` | ContextSignals | No | コンテンツ環境の事前計算された分類器出力。コンテンツが一時的(会話ターン、検索クエリ)なとき、またはアーティファクトベースのマッチングを補完するために使う。`artifact_refs` を完全に置き換えられる。生のコンテンツを含んではならない(MUST NOT) — 分類された出力のみ。パブリッシャーが分類器境界。 | | `geo` | Geo | No | 視聴者の粗い地理的位置。パブリッシャーが粒度を制御 — 規制コンプライアンスには国、キャンペーンターゲティングと評価には地域/メトロ。郵便番号や座標なし — ユーザー識別を防ぐため粗くされる。 | | `package_ids` | List\ | No | 評価を特定のパッケージに制限する。省略されたとき、プロバイダーはこのプレースメントのすべての適格なパッケージを評価する(一般的なケース)。パッケージメタデータ(フォーマット、カタログ)はメディアバイ時に同期される — リクエストごとに送られない。 | #### ContextSignals コンテンツ環境の事前計算された分類器出力。生のコンテンツ(会話テキスト、記事本文、URL)を含んではなりません(MUST NOT)。分類された出力のみ。パブリッシャーが分類器境界。 | Field | Type | Required | Description | | ------------------ | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `topics` | List\ | No | コンテンツトピック識別子。`taxonomy_id` が 7(デフォルト)のとき IAB Content Taxonomy 3.0 ID を、カスタムタクソノミーには人間可読な文字列を使う。 | | `taxonomy_source` | enum | No | トピックタクソノミーを定義する組織。デフォルト: `iab`。 | | `taxonomy_id` | integer | No | ソース内のタクソノミーバージョン。IAB については AdCOM cattax enum に従う: `7` = Content Taxonomy 3.0(CC-BY-3.0)。デフォルト: `7`。 | | `sentiment` | enum | No | コンテンツセンチメント: `positive`、`negative`、`neutral`、`mixed`。 | | `keywords` | List\ | No | パブリッシャーの分類器が抽出したコンテンツキーワード。 | | `language` | string | No | ISO 639-1 言語コード。 | | `content_policies` | List\ | No | このコンテンツが満たす [AdCP Policy Registry](/docs/governance/policy-registry) のポリシー ID。ルーターはこれをパブリッシャーのプロパティガバナンス設定またはコンテンツメタデータから投入する。バイヤーはパッケージの `required_policies` で要求するポリシーをフィルターする。これは **事前フィルタリング最適化** — 要求されるポリシーを欠くコンテキストは下流ガバナンスに到達する前に除外される。決定的な強制は [`check_governance`](/docs/governance/campaign/tasks/check_governance) 経由でガバナンス層で起こる。 | | `summary` | string | No | 関連性判断のための自然言語 summary。セマンティックに評価する LLM ネイティブなバイヤーに有用。 | | `embedding` | string | No | base64 エンコードされた int8 ベクターとしてのコンテンツ embedding。トピックとキーワードを超えたセマンティックコンテンツを捉える。 | | `embedding_model` | string | No | Embedding モデル識別子(例: `nomic-embed-text-v1.5`)。`embedding` が存在するとき必須。 | | `embedding_dims` | integer | No | Embedding ベクターの次元数。`embedding` が存在するとき必須。 | 3 つのレベルのコンテンツ開示 — パブリッシャーはバイヤーが必要とするものとパブリッシャーが共有して快適なものに基づいて選ぶ: * **`artifact`** — 完全なコンテンツ(記事本文、トランスクリプト、会話フロー、プロダクトページ)。コンテンツ標準アーティファクトと同じスキーマ。バイヤーがコンテンツを直接評価する。契約上の保護がバイヤーができることを統治する。TEE デプロイが暗号学的検証を上に追加する。 * **`artifact_refs`** — バイヤーが独立して解決する公開参照(URL、EIDR ID、URL ハッシュ)。バイヤーが自分でクロールして分類できる公開アドレス可能なコンテンツに使う。 * **`context_signals`** — 分類された出力(トピック、センチメント、キーワード、summary)。パブリッシャーがコンテンツやその参照を共有せずにコンテンツを記述したいときに使う。 `context_signals` はベースライン — すべてのバイヤーエージェントがそれを扱わなければならない(MUST)。`artifact_refs` と `artifact` は漸進的な強化。`artifact_refs` を送るパブリッシャーは、参照を解決できないバイヤーのためのフォールバックとして `context_signals` も送るべき(SHOULD)。 LLM ベースのバイヤーエージェントは、`context_signals.summary` と `context_signals.topics` を最初に評価すべき(SHOULD)。これらのフィールドは、最小のトークンコスト(約 30 トークン)でほとんどの関連性決定に十分なシグナルを提供する。`artifact_refs` からの完全なコンテンツ解決や `artifact` 評価は、精度がコストを正当化する高価値のパッケージのために予約すべき(SHOULD)。バイヤーは `artifact` コンテンツと `context_signals.summary` を信頼できないパブリッシャー生成の入力として扱わなければならない(MUST)。 リクエストは任意の組み合わせを含められる。ニュースサイトは `artifact_refs`(URL)と `context_signals`(事前分類されたトピック)を送る。CTV アプリは `artifact_refs`(EIDR ID)のみを送る。AI アシスタントは、コンテンツを直接評価するバイヤーには `artifact`(会話)を、加えてフォールバックとして `context_signals` を送る。コンテンツや参照を共有したくないパブリッシャーは `context_signals` のみを送る。 #### Artifact Ref Type Conventions バイヤーは `artifact_refs` 文字列をパターンでパースします。次の慣例は規範的です: | Type | Pattern | Example | | ------------- | -------------------- | ------------------------------------------------------ | | URL | `https://` で始まる | `https://oakwood.example/articles/sustainable-kitchen` | | URL hash | 44 文字 base64(Blake3) | `k7Xp9mQ2vL8nR3wY5tB1aH6jK0pZ4xC9dF2eG7iMqw==` | | EIDR | `eidr:` で始まる | `eidr:10.5240/XXXX-XXXX-XXXX-XXXX-XXXX-C` | | Gracenote TMS | `tms:` で始まる | `tms:SH012345670000` | | RSS + GUID | `rss:` で始まる | `rss:https://feed.example/rss+guid:ep-2026-03-15` | | GTIN | 8-14 桁の数値 | `00012345600012` | バイヤーは、リクエストを失敗させるのではなく、サポートしない ref タイプを無視すべき(SHOULD)。 #### Artifact 型付きコンテンツ参照。各アーティファクトは標準またはカスタムの識別子スキームを使ってコンテンツの一片を識別します。 | Field | Type | Required | Description | | ------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `type` | enum | Yes | 次の 1 つ: `url`、`url_hash`、`eidr`、`gracenote`、`isrc`、`gtin`、`rss_guid`、`isbn`、`custom`。 | | `value` | string | Yes | 識別子の値。`url`: 正準コンテンツ URL(ユーザー固有のパスやクエリパラメーターを含んではならない。URL を明かすのを避けるには `url_hash` を使う)。`url_hash`: base64 エンコードされた Blake3 ハッシュ(正準化: スキーム除去、[www./m./amp](http://www./m./amp). プレフィックス除去、小文字化、末尾スラッシュ除去、クエリパラメーターとフラグメント除去)。`eidr`: EIDR DOI(例: `10.5240/xxxx`)。`gracenote`: Gracenote TMS ID(例: `SH032541890000`)。`isrc`: ISRC コード(例: `USRC17607839`)。`gtin`: GTIN(例: `00012345678905`)。`rss_guid`: RSS フィードのエピソード GUID。`isbn`: ISBN(例: `978-0-123456-78-9`)。`custom`: パブリッシャー定義の文字列。 | #### Geo インプレッション機会の地理的コンテキスト。パブリッシャーが粒度を制御します。 | Field | Type | Required | Description | | --------- | ------ | -------- | -------------------------------------- | | `country` | string | No | ISO 3166-1 alpha-2 国コード(例: `US`、`GB`)。 | | `region` | string | No | ISO 3166-2 区分コード(例: `US-CA`、`GB-SCT`)。 | | `metro` | Metro | No | AdCP のメトロ分類システムを使ったメトロエリア。 | ### ContextMatchResponse バイヤーエージェントが返します。一致したパッケージのオファーと任意のレスポンスレベルのターゲティングシグナルを含みます。 | Field | Type | Required | Description | | ------------ | ------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `type` | string | Yes | `"context_match_response"`。デシリアライゼーションのメッセージタイプ判別子。 | | `request_id` | string | Yes | リクエストの `request_id` のエコー。 | | `offers` | List\ | Yes | バイヤーからのオファー、アクティベートされたパッケージごとに 1 つ。空リストはパッケージが一致しなかったことを意味する。 | | `cache_ttl` | integer | No | ルーターのデフォルト Context Match レスポンスキャッシュ TTL のプロバイダーオーバーライド(秒)。存在するとき、ルーターはデフォルトの代わりにこの値を使わなければならない(MUST)。`0` はキャッシュを無効化(例: ターゲティング設定がちょうど変わったとき)。スキーマ強制の最大は 86400 秒。[Caching](#caching) を参照。 | | `signals` | Signals | No | アドサーバー通過用のレスポンスレベルのターゲティングシグナル。オファーごとではない — レスポンス全体に適用。GAM のケースでは、これらがラインアイテムをトリガーするキー値ペアを運ぶ。 | #### Offer 単一のパッケージに対するバイヤーの応答。 | Field | Type | Required | Description | | ------------------- | -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `package_id` | string | Yes | メディアバイからのパッケージ識別子。 | | `seller_agent` | SellerAgentRef | No | パブリッシャー側の可観測性のためのパッケージのセラーエージェントの任意のエコー。非権威的 — キャッシュされた AvailablePackage のバインディングが真実の源泉。ルーターはプロバイダーが省略したときキャッシュされた package→seller マップからこのフィールドをスタンプしてもよい(MAY)。[Package Sync](#package-sync) を参照。 | | `brand` | BrandRef | No | このオファーのブランド。プロダクトが動的ブランドを許すとき必須。単一ブランドパッケージについては、メディアバイから既知。 | | `price` | OfferPrice | No | このオファーの可変価格。プロダクトが可変価格をサポートするときのみ存在。 | | `summary` | string | No | パブリッシャーが関連性を判断するためのバイヤー生成のオファーの説明。例: 「Goldenfield マヨ 50% オフ — レシピ統合」。 | | `creative_manifest` | CreativeManifest | No | 完全なクリエイティブ詳細、インライン。存在するとき、パブリッシャーはレンダリングに必要なすべてを持つ。大きなクリエイティブ(VAST、動画)については、マニフェストは URL 経由で外部アセットを参照。 | | `macros` | Map\ | No | 動的クリエイティブレンダリングまたはアトリビューショントラッキングのキー値ペア。GAM のケースでは、これらがマクロ値として流れる。フリークエンシートラッキング用の暗号化された露出トークンを運ぶ Identity Match `tmpx` フィールドとは別。 | #### OfferPrice | Field | Type | Required | Description | | ---------- | ------ | -------- | ---------------------------------------- | | `amount` | number | Yes | 指定された通貨での価格額。 | | `currency` | string | No | ISO 4217 通貨コード。デフォルト: `USD`。 | | `model` | enum | Yes | 次の 1 つ: `cpm`、`cpc`、`cpcv`、`cpa`、`flat`。 | #### Signals アドサーバー通過用のレスポンスレベルのターゲティングシグナル。 | Field | Type | Required | Description | | --------------- | ------------------- | -------- | ------------------------- | | `segments` | List\ | No | オーディエンスまたはコンテキストセグメント ID。 | | `targeting_kvs` | List\ | No | アドサーバーターゲティングのキー値ペア。 | #### KeyValuePair | Field | Type | Required | Description | | ------- | ------ | -------- | ----------- | | `key` | string | Yes | ターゲティングキー。 | | `value` | string | Yes | ターゲティング値。 | ### IdentityMatchRequest パブリッシャー(ルーター経由)からバイヤーエージェントに送られます。セラーエージェント URL、1 つ以上の不透明なアイデンティティトークン、任意のパッケージ ID リストを含みます。ページコンテキストを含んではなりません(MUST NOT)。 | Field | Type | Required | Description | | -------------------- | ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `type` | string | Yes | `"identity_match_request"`。デシリアライゼーションのメッセージタイプ判別子。 | | `protocol_version` | string | No | TMP プロトコルバージョン。デフォルト: `1.0`。 | | `request_id` | string | Yes | 一意のリクエスト識別子。任意の Context Match request\_id と相関してはならない(MUST NOT)。 | | `seller_agent_url` | string (URI) | Yes | このリクエストを発行するセラーエージェントの API エンドポイント URL。バイヤーの identity-match サービスはこれを使ってこのセラーに登録したアクティブなパッケージセットを解決する。`package_ids` が省略されたとき、その完全なセットに対して評価が起こる。バイト等価ではなく [AdCP URL 正準化ルール](/docs/reference/url-canonicalization) を使って比較される。`AvailablePackage` の `seller_agent.agent_url` および `adagents.json` の `agent_url` と一貫。 | | `identities` | List\ | Yes | ユーザーの 1 つ以上のアイデンティティトークン。パブリッシャーは利用可能なすべてのトークンを含めるべき(SHOULD) — バイヤーは一致するグラフで解決し、マッチ率を最大化する。各エントリは同じユーザーの独立した識別子。バイヤーはその組み合わせを新しい相関アイデンティティとして扱ってはならない(MUST NOT)。 | | `consent` | Consent | No | プライバシー同意シグナル。規制された管轄区域のバイヤーは同意情報なしにアイデンティティトークンを処理してはならない(MUST NOT)。 | | `package_ids` | List\ | No | 省略されたとき、バイヤーは `seller_agent_url` に登録したアクティブなパッケージの完全なセットに対して適格性を評価する。提供されたとき、構成は現在のプレースメントと統計的に独立でなければならない(MUST)。2 つの許容モード: **all-active**(このパブリッシャーでのこのバイヤーのすべてのアクティブパッケージ)または **fuzzed**(アクティブなパッケージのランダムサンプル、任意で合成の存在しない ID でパディング、現在のプレースメントに依存しない分布から抽出)。ページ固有のサブセットは禁止 — それはバイヤーがパッケージセットを比較して Context Match と相関させることを許す。 | | `country` | string | No | ISO 3166-1 alpha-2 国コード。ルーティングディレクティブ — ルーターはこれを使って正しい地域プロバイダーを選択する。ルーターはバイヤーエージェントに転送する前にこのフィールドを剥がさなければならない(MUST)。アイデンティティシグナルではない。 | | `sealed_credentials` | List\ | No | **実験的(`trusted_match.verified_identity`)。** 特定のオーディエンスに宛てられた HPKE 封印された検証済みアイデンティティ認証情報 — network-as-RP キャリア。パブリッシャーにとって不透明なパススルー。[Verified Identity Attestation](#verified-identity-attestation) を参照。 | `identities` の各エントリは `{user_token, uid_type, attestation?}` トリプルです: | Field | Type | Required | Description | | ------------- | ----------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `user_token` | string | Yes | アイデンティティプロバイダー(ID5、LiveRamp、UID2)からの、またはパブリッシャー生成の不透明なトークン。バイヤーは内部アイデンティティグラフにマップしてもよいが PII に逆変換できない。 | | `uid_type` | enum | Yes | ユーザー識別子のタイプ: `uid2`、`rampid`、`id5`、`euid`、`pairid`、`maid`、`hashed_email`、`publisher_first_party`、`world_id_nullifier`、`other`。バイヤーにどのアイデンティティグラフに対して解決するかを伝える。`uid-type` enum を参照。 | | `attestation` | Attestation | No | **実験的(`trusted_match.verified_identity`)。** このアイデンティティ *について* の検証可能な証明(人格証明および/または年齢)。受信者はそれを検証しなければならず(MUST)、検証不能なアテステーションを、asserted-true としてではなく absent として扱わなければならない。[Verified Identity Attestation](#verified-identity-attestation) を参照。 | ### IdentityMatchResponse バイヤーエージェントが返します。サーブウィンドウスロットルを伴う適格なパッケージ ID のリスト。 | Field | Type | Required | Description | | ---------------------- | ------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `type` | string | Yes | `"identity_match_response"`。デシリアライゼーションのメッセージタイプ判別子。 | | `request_id` | string | Yes | リクエストの `request_id` のエコー。 | | `eligible_package_ids` | List\ | Yes | ユーザーが適格なパッケージ ID。リストされないパッケージは不適格。 | | `serve_window_sec` | integer | Yes | パッケージごとのシングルショット fcap ウィンドウ、秒。範囲: 1–300。デフォルト: 60。このウィンドウ内で各適格パッケージにユーザーへ 1 インプレッションを提供した後、パブリッシャーはそれらのパッケージから再び提供する前に Identity Match を再クエリしなければならない(MUST)。これはルーターレスポンスキャッシュ TTL では **ない** — バイヤーがアサートするサーブスロットル。マルチインプレッションフリークエンシーキャップは、このウィンドウにかかわらず境界で IdentityMatch キャップ状態ストアにキャップ発火イベントを書き込むバイヤーのインプレッショントラッカーによって別途扱われる — [フリークエンシーキャップデータフロー](/docs/trusted-match/identity-match-implementation) を参照。 | | `tmpx_macros` | List\ | No | プロバイダーが発する(アイデンティティエージェント側フィールド)。各チャンクが埋めるアドサーバーマクロ名とペアになったエージェントの順序付けられた TMPX チャンク。名前は同じ順序でプロバイダーの登録された `tmpx_macros` リスト([Provider Registration](#provider-registration) を参照)から取られなければならない(MUST)。v1 では 2 エントリに上限。各 `value` はパブリッシャーが逐語的に代入する不透明な URL セーフなワイヤー文字列 — パブリッシャーはパース、デコード、変換、エンコーディングの選択をしてはならない(MUST NOT)。ルーターのマージされたレスポンスを読むコンシューマーは `tmpx_providers` を消費し、ルートの `tmpx_macros` を無視すべき(SHOULD)。 | | `tmpx_providers` | Map\ }> | No | ルーターが投入。発信元アイデンティティプロバイダーの `provider_id` でグループ化された TMPX マクロ/値ペア。パブリッシャーが各プロバイダーのトークンをそのプロバイダーの特定のアドサーバーマクロ(例: あるプロバイダーの `PIN_TMPX_1`、`PIN_TMPX_2`、別のプロバイダーの `NOVA_TMPX_1` — GAM / VAST URL / DOOH play log でプロバイダーごとに設定)を通じて発火する。このリクエストで任意のアイデンティティプロバイダーが TMPX を発したときルーター適合性によって必須。プロバイダーごとのトークンを単一の文字列に折り畳むとアトリビューションが失われプロバイダーごとのインプレッション会計が壊れる。マクロ名は各プロバイダーの登録された `tmpx_macros` から来なければならない(MUST) — パブリッシャーは実行時に `provider_id` からマクロ名を導出してはならない(MUST NOT)。#5689 で出荷された実験的 v1 サーフェス(`Map` を使った)からの SHAPE CHANGE。実験的コントラクトによって認可。 | | `tmpx` | string | No | `tmpx_providers` を優先して非推奨。単一の HPKE 暗号化された露出トークン。ルーターは単一トークン形状のみを知るコンシューマーとの後方互換性のためこのフィールドを投入し続けてもよい(MAY)。両方のフィールドが存在するとき、`tmpx_providers` が権威的。ワイヤー形式: `kid.base64url_nopad(ciphertext)`(パディングなし、`=` 文字なし)。4.0 で削除。 | #### TmpxMacro | Field | Type | Required | Description | | ------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | string | Yes | パブリッシャーのアドサーバーで設定されたアドサーバーマクロ名(例: `PIN_TMPX_1`)。発行プロバイダーの登録された `tmpx_macros` リストに現れなければならない(MUST)。パターン: `^[A-Z][A-Z0-9_]*$`。プロバイダーが distinct なスロットをプロバイダーごとにターゲットできるようプロバイダー名前空間化。 | | `value` | string | Yes | パブリッシャーが名前付きマクロスロットに逐語的に代入する不透明で URL セーフなワイヤー文字列。長さは 1024 文字に上限 — 255 文字の GAM キー値制限を快適に上回り、HPKE オーバーヘッド + チャンクされたペイロードに十分大きい。パブリッシャーはこの値をパース、デコード、変換してはならない(MUST NOT)。プロトコルはプラットフォームが相互運用するようワイヤー形式を固定する。生のバイトを運べるプラットフォームはプライベートに最適化してもよい(MAY)が、ワイヤーコントラクトは URL セーフな文字列のまま。 | レスポンスは適格なパッケージ ID、サーブウィンドウスロットル、プロバイダーごとの TMPX 形状(ルーターマージ後の `tmpx_providers`、またはプロバイダーごとのレベルでの `tmpx_macros`)を含みます。慣例により、プロバイダー→ルーターのペイロードは `tmpx_macros` を投入し `tmpx_providers` を欠如させる。ルーター→パブリッシャーのペイロードは `tmpx_providers`(各プロバイダーの `tmpx_macros` を収集して構築)を投入する。ルーターレスポンスはルートに `tmpx_macros` を運んではならない(MUST NOT) — 上流プロバイダーのルート配列を `tmpx_providers` と並んで漏らすと、パブリッシャーにどれを読むべきかのスキーマシグナルを与えず、同じ値を 1 つのスロットに二重発火し、マップが保護するために存在するプロバイダーごとの会計を破損するリスクがある。スキーマはこれを強制できない(同じスキーマが両方のホップに提供される)のでルーター適合性不変条件として存在する。ルーターはアウトバウンドレスポンスから `tmpx_macros` を落とさなければならない(MUST)。各 TMPX 値は、クリエイティブトラッキング URL を通じてバイヤーのインプレッションピクセルに流れる HPKE 暗号化された露出トークンで、ユーザーアイデンティティをパブリッシャーに露出せずにリアルタイムのユーザーごとのフリークエンシー状態更新を可能にする。バイヤーは持っている任意のアイデンティティシグナル(フリークエンシーキャップ、オーディエンスメンバーシップ、購入履歴)から適格性を計算し、通過するパッケージのみを返す。パブリッシャーはパッケージがなぜ除外されたかを知る必要はない — どのパッケージが適格かだけ。 **TMPX マクロトラフィッキング。** マクロ名は運用セットアップの一部であり、プロトコルが合成する識別子ではない。各アイデンティティプロバイダーは、そのプロバイダー登録エントリの `tmpx_macros` に安定したプロバイダー名前空間化されたマクロ名を登録する(例: Pinnacle は `["PIN_TMPX_1", "PIN_TMPX_2"]` を、Nova は `["NOVA_TMPX_1"]` を登録)。パブリッシャーはそれらの正確な名前をそのアドサーバー(GAM キー値、VAST URL マクロ、DOOH play-log フィールド)で設定する。レスポンスが到着すると、パブリッシャーは各プロバイダーの `macros[].value` を一致する `macros[].name` スロットに発火する — コントラクトは「この正確な文字列をこの正確なマクロに代入する」。順序付けられたマルチチャンクサポートにより単一の TMPX が 1 つのマクロスロットを超えられる(v1 ではプロバイダーごとに 2 チャンクに上限、shape change なしに上げられる MAY)。実行時に `provider_id` からマクロ名を導出することは、GAM/アドサーバーラインアイテムが実行時合成文字列ではなく登録された名前に対して事前に設定されるため、このトラフィッキングモデルを壊す。 `tmpx_providers` は、ファンアウトが複数のアイデンティティプロバイダーに到達したときルーターがアトリビューションを保つよう、マクロ/値ペアを `provider_id` でキー付けする。レガシー `tmpx` フィールドは、移行していないコンシューマーのため 3.x を通じてサポートされたまま。両方のフィールドが存在するとき、`tmpx_providers` が権威的で、単数フィールドは移行的な便宜としてのみ 1 つのプロバイダーの最初のスロット値を反映すべき(SHOULD)。 `serve_window_sec` フィールドは **パッケージごとのシングルショット fcap** であり、ルーターキャッシュ TTL ではない。バイヤーはこう言っている: 「各適格パッケージにユーザーへ 1 インプレッションを提供した後、それらのパッケージから再び提供する前に私に再クエリせよ。」ルーターは内部の重複排除/コスト節約ウィンドウのためにレスポンスをキャッシュしてもよい(MAY)が、パブリッシャー側の拘束コントラクトは「ウィンドウごとの適格パッケージごとに 1 インプレッション」。マルチインプレッションフリークエンシーキャップ(キャンペーンごとに 1 日 5、広告主ごとに 1 か月 100 など)はバイヤーのインプレッショントラッカーに存在し、`serve_window_sec` にかかわらず境界でキャップ発火イベントとして IdentityMatch サービスにサーフェスする。 パブリッシャーは適格性リストを入力として割り当てルール(競合分離、ポッド構成)を強制する。これはポッド固有またはバッチ固有のプロトコルセマンティクスの必要を除去する — パブリッシャーは、one-impression-per-package コントラクトを尊重しながら、サーブウィンドウ中に存在する任意のプレースメント(CTV 広告ポッド、20 スロットの web ページ、単一のプレロール)にわたって割り当てる。 #### Conformance invariants for IdentityMatch eligibility 準拠する IdentityMatch サービスは、各 `package_id ∈ request.package_ids` について、次の **すべて** が成立する場合かつその場合に限りパッケージが `eligible_package_ids` に含まれるように `eligible_package_ids` を計算しなければなりません(MUST): 1. **オーディエンス適格性。** パッケージがオーディエンス要件を持たないか、または `a` がパッケージの必要オーディエンスセットにありかつ `a` が少なくとも 1 つのアイデンティティ `i ∈ request.identities` のオーディエンスメンバーシップにあるようなオーディエンス識別子 `a` が少なくとも 1 つ存在する(ユーザーの解決されたアイデンティティにわたる union がパッケージの必要オーディエンスと交差する)。 2. **フリークエンシーキャップ適格性。** 任意のアイデンティティ `i ∈ request.identities` に対してパッケージに `(identity, package)` キャップ状態エントリが存在しない。キャップ状態エントリは、バイヤーのインプレッショントラッカーがインプレッションがキャップを使い果たしたと判断したときに書き込まれ、有効期限タイムスタンプを運ぶ。エントリはそのタイムスタンプまで「存在」する。プロトコルは、インプレッショントラッカーがどうインプレッションをカウントし、ウィンドウを評価し、いつキャップが発火するかを決めるかを制約しない — 境界コントラクト(キャップ発火エントリがキャップ状態ストアに流れ込み、IdentityMatch サービスがクエリ時に存在を確認)のみ。境界コントラクトについては [フリークエンシーキャップデータフロー](/docs/trusted-match/identity-match-implementation) を参照。 3. **アクティブ状態。** inactive とマークされたパッケージまたはポリシーは absent であるかのように扱わなければならない(MUST)。 4. **オーディエンス鮮度。** バイヤーのオーディエンスパイプラインが鮮度期限を公開し現在時刻がそれを過ぎている場合、そのオーディエンスメンバーシップエントリは (1) に寄与してはならない(MUST NOT)。 5. **年齢適格性**(実験的 — `trusted_match.verified_identity` が有効なときのみ適用)。パッケージが年齢ポリシーを要求しないか、または何らかのアイデンティティ `i ∈ request.identities` が、`(パッケージの必要年齢ポリシー, request geo)` から解決されたしきい値以上の年齢クレームを持つ **検証済み** `attestation`([Verified Identity Attestation](#verified-identity-attestation) の適合性ルール準拠)を運ぶ。未検証または欠如のアテステーションはこの条項を満たさない。機能が有効でないとき、この条項は自明に真なので、コア適合性は変わらない。 レスポンスとともに返される TMPX は、帯域外のインプレッショントラッカーが fcap ポリシー状態を更新し IdentityMatch キャップ状態ストアにキャップ発火イベントをシグナルできるよう、解決されたアイデンティティをエンコードしなければなりません(MUST) — § TMPX tokens と [フリークエンシーキャップデータフロー](/docs/trusted-match/identity-match-implementation) を参照。 ストレージバックエンド(valkey、Aerospike、DynamoDB、インメモリ、何でも)は実装。同じ入力についてこれらの不変条件を満たす異なるストレージバックエンドを持つ 2 つのサービスは、同じ適格性出力を返さなければなりません(MUST)。 #### Consent identity match のプライバシー同意シグナル。パブリッシャーは、規制された管轄区域(EU/EEA、カリフォルニアなど)で動作するとき同意情報を含めなければなりません(MUST)。バイヤーは、適用法が要求するとき、同意情報なしにユーザートークンを処理してはなりません(MUST NOT)。 | Field | Type | Required | Description | | ------------- | ------ | -------- | -------------------------------------------- | | `gdpr` | bool | No | GDPR がこのリクエストに適用されるか。 | | `tcf_consent` | string | No | IAB TCF v2.2 同意文字列。`gdpr` が true のとき存在。 | | `gpp` | string | No | IAB Global Privacy Platform 文字列。 | | `us_privacy` | string | No | US Privacy 文字列(CCPA)。GPP を優先して非推奨だが依然広く使われる。 | ### Verified Identity Attestation **実験的 — `experimental_features` に `trusted_match.verified_identity` を宣言**(`trusted_match.core` とは別、そのためバイヤーがサポートを独立に検出できる)。パブリッシャー — または relying party として動作するネットワーク/発行者 — が、バイヤーがアサーションを信頼するのではなくクレームを暗号学的に検証するよう、ユーザー *について* の **検証可能な** 証明(人格証明および/または年齢)を運べるようにする。発行者非依存: World ID が最初のスキーム。mDL / VC スタイルの発行者は同じ形状を使う。設計理由: `specs/tmp-verified-identity-attestation.md`。 これは、そうでなければ `additionalProperties: false` である `identity-match-request.json` を拡大します。拡大は意図的: アテステーションはプライバシー境界の **アイデンティティ** 側の証明であり — ページコンテキストではない — なので厳格なスキーマが保護する境界を破らない。 #### Topologies | Topology | Relying party | Carrier | | ---------------------------- | --------------- | ------------------------------------------------ | | Publisher-as-RP | パブリッシャー | `identities[]` エントリの `attestation`(パブリッシャーごとの仮名) | | Network-as-RP / issuer-as-RP | ネットワーク、または発行者自体 | `sealed_credentials[]`(オーディエンスの鍵に HPKE 封印) | #### Attestation 各 `identities[]` エントリの任意オブジェクト。 | Field | Type | Required | Description | | -------------------- | ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `issuer` | BrandRef | Yes | ベンダー BrandRef としてのアイデンティティ発行者 / アテステーション権威(例: `{"domain": "world.org"}`) — AdCP が測定/シグナルベンダーに使うのと同じ形状。ドメインがアンカー。`brand.json` ホスティングは任意。`scheme` が検証者バージョンを選択。 | | `scheme` | string | Yes | 証明スキームとバージョン、例: `world_id_v4`。 | | `relying_party_id` | string | No | 証明が鋳造された relying-party id。`relying_party_id` 所有者の `brand.json` の公開された `identity_relying_parties[]` に対して確認される(来歴)。 | | `action` | string | No | 証明がバインドされた発行者アクション/スコープ。 | | `claims` | List\ | Yes | 閉じた、発行者非依存のセット: `unique_human`、`age_over_13`、`age_over_16`、`age_over_18`、`age_over_21`。`attestation-claim` enum を参照。 | | `verification_level` | enum | No | `orb` \| `device` \| `document`。認証情報の強度。 | | `signal_binding` | string | No | 証明がコミットするシグナルのハッシュ(リプレイ防御)。 | | `proof` | object | Yes | スキーム固有の検証可能な証明素材。このスキーマにとって不透明。 | | `expires_at` | string (date-time) | No | 有効期限。受信者は過ぎたとき拒否しなければならない(MUST)。 | #### SealedCredential トップレベルの `sealed_credentials[]` のエントリ。 | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------------------------------------------------------------ | | `audience_kid` | string | Yes | HPKE 秘密鍵が `payload` を開く受信者(ネットワーク / relying party)の鍵 id。 | | `payload` | string | Yes | TMPX エンベロープ形式 `kid.base64url_nopad(ciphertext)` で HPKE 封印されたアテステーション。パブリッシャーにとって不透明なパススルー。 | #### Conformance invariants (verified attestation) アテステーションを受け入れる受信者は次をしなければなりません(MUST): 1. **信頼の前に検証。** 受け入れるすべての `scheme` について証明を検証する。検証に失敗するアテステーション — または `signal_binding`、`relying_party_id` 来歴、`expires_at` — は、asserted-true クレームとしてではなく **absent**(アテステーションなし)として扱わなければならない(MUST)。 2. **黙った格上げなし。** 未検証または検証不能なアテステーションを決して `true` として扱わない。 3. **relying\_party\_id 来歴。** 信頼する前に、アテステーションの `relying_party_id` がトラフィックを主張するエンティティに属することを確認する。所有者の `brand.json` `identity_relying_parties[]` が v1 のディスカバリーサーフェスだが、HTTPS 上で自己公開される — 権威あるルートは発行者自身の relying-party レジストリ(例: World ID のオンチェーンレジストリ)で、それに対して `brand.json` はクロスチェック。受信者は、発行者側のアンカーなしに、何らかの `brand.json` がリストするからという理由だけで `relying_party_id` を信頼してはならない(MUST NOT)。双方向の発行者メタデータクロスチェックは追跡されたオープン項目。 4. **封印された認証情報。** 受信者が鍵を持つ `audience_kid` の `sealed_credentials[]` エントリのみを復号する。残りは無視。 5. **有界リソース。** DoS を防ぐためアテステーションと封印された認証情報の数とサイズを有界化する。 **リプレイ(v1 制限)。** 証明を検証することは、*ある* 認証情報保持者がそれを生成したことを確立し、*この* インプレッションのために生成されたことではない。強制された `signal_binding` 鮮度ウィンドウと nullifier 再利用追跡 — 両方とも v1 では検証者に委ねられる(鮮度ポリシーは WG オープン) — なしでは、このサーフェスはせいぜい日次エポックのリプレイ耐性(`request_id` 重複排除経由)を提供する。受信者は `signal_binding` を新鮮で受信者確認可能な値にバインドし nullifier 再利用を追跡すべきで(SHOULD)、そうするまでアテステーションをインプレッションごとのライブネスシグナルとして過度に信頼してはならない(MUST NOT)。 #### Router handling of `sealed_credentials[]` ルーターは各 `sealed_credentials[]` エントリを、その `audience_kid` を所有するプロバイダーにのみ転送しなければなりません(route-by-audience、ブロードキャストでない)(MUST)。改ざん証拠とキャッシュ分割のルールは [Identity Match signed fields](#identity-match-signed-fields) と [Caching](#caching) で正準的に定義される: `sealed_credentials` はプロバイダーごとの再署名の正準バイトに折り込まれる(そのため注入または交換された blob がリクエスト署名を壊す)、`sealed_credentials_hash` は重複排除キャッシュキーの一部(そのためネットワーク認証情報の変更が古いレスポンスを提供するのではなくキャッシュを再分割する)。同じ正準アイデンティティバイトは既に任意のアイデンティティごとの `attestation` をカバーする。 #### Age as eligibility 検証済み年齢クレーム(`age_over_N`)は `eligible_package_ids` に解決される — 平文の年齢や生年月日として **決して** 運ばれない。検証する当事者は `(required age policy, geo) → required threshold claim` をマップし、アテステーションが必要しきい値以上のクレームを運ぶときのみパッケージを含める。管轄区域 → しきい値のテーブルは [AdCP Policy Registry](/docs/governance/policy-registry) で維持される(パッケージは `required_policies` 経由で年齢ポリシーを要求する)。sub-country(例: 米国州)解決は、Identity Match `country` フィールドが粗く転送前に剥がされるため、より細かい geo が利用可能な場所で起こる。 ### Error Response リクエストが処理できないときプロバイダーまたはルーターが返します。空の結果とは別 — 空の `offers` 配列や空の `eligible_package_ids` リストは、エラーではなく一致なしを意味する有効なレスポンス。 | Field | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `type` | string | Yes | `"error"`。デシリアライゼーションのメッセージタイプ判別子。 | | `request_id` | string | Yes | 元のリクエストの `request_id` のエコー。 | | `code` | enum | Yes | 機械可読なエラーコード: `invalid_request`、`unknown_package`、`seller_not_authorized`、`rate_limited`、`timeout`、`internal_error`、`provider_unavailable`。`seller_not_authorized` は、AvailablePackage がパブリッシャーの adagents.json に存在しない `seller_agent.agent_url` を宣言するとき [package sync](#package-sync) 時に返される。 | | `message` | string | No | デバッグ用の人間可読なエラー説明。 | ルーターは、そのリクエストのマージされたレスポンスからエラーを返すプロバイダーを除外すべき(SHOULD)。ルーターはプロバイダーごとのエラー率を追跡し、持続的なエラーを持つプロバイダーを先制的にスキップしてもよい(MAY)。 ## Provider Registration TMP プロバイダーはパブリッシャー設定を通じてルーターに登録されます。パブリッシャーは、各プロバイダーのエンドポイントとサポートするケイパビリティとともに、ルーターがどのプロバイダーを呼ぶべきかを指定します。これは運用上の関係です — パブリッシャーはプロバイダーがその広告決定パスでコードを実行することを信頼します。 標準の登録パスは **静的設定** — パブリッシャーが Prebid モジュール設定、ルーター YAML、または同等のサーフェス固有の設定でプロバイダーを宣言する。動的登録(API 駆動、データベース裏付け)は、多くのプロバイダーを管理するかランタイム更新が必要なパブリッシャーのための等しく有効なバリアント。両アプローチは同じプロバイダー登録スキーマ(`/schemas/trusted-match/provider-registration.json`)を使う。 | Setting | Type | Required | Description | | ---------------- | ------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `provider_id` | string | Yes | このプロバイダー登録の安定した識別子。ログ、メトリクス、キャッシュキー、そして identity-match レスポンスの `tmpx_providers` のキーとして使われ、パブリッシャーが各プロバイダーの TMPX `macros[]` をそのプロバイダーの事前設定されたアドサーバースロット(`tmpx_macros` に登録 — マクロ名は実行時に `provider_id` から導出してはならない)にルーティングできる。パターン: `^[A-Za-z0-9_]+$`、最大長 64 — 値がクォートなしで運用サーフェス(ログ、メトリクス、ダッシュボード)に現れられる安全な英数字/アンダースコア文字セット。 | | `endpoint` | URL | Yes | プロバイダーのベース URL。ルーターはディスパッチ時に `/context` または `/identity` を追加する。 | | `context_match` | bool | No | プロバイダーが Context Match リクエストを扱う。`context_match` または `identity_match` の少なくとも一方が true でなければならない。 | | `identity_match` | bool | No | プロバイダーが Identity Match リクエストを扱う。`context_match` または `identity_match` の少なくとも一方が true でなければならない。 | | `countries` | List\ | Conditional | このプロバイダーが提供する ISO 3166-1 alpha-2 国コード。`identity_match` が true のとき存在し空でないことが必須(MUST)。 | | `uid_types` | List\ | Conditional | このプロバイダーが解決できるアイデンティティタイプ(`uid-type` enum から)。ルーターは、`uid_types` がリクエストの `identities` 配列の任意の `uid_type` と重なるプロバイダーを選択し、転送された `identities` を交差にフィルターする — プロバイダーは宣言しなかったタイプのトークンを受け取ってはならない(MUST NOT)。`identity_match` が true のとき存在し空でないことが必須(MUST)。 | | `properties` | List\ | No | このプロバイダーが提供するプロパティ RID。欠如のとき、プロバイダーはすべてのプロパティを提供する。 | | `timeout_ms` | integer | No | ミリ秒単位のプロバイダーごとのタイムアウト。ルーターの全体 `latency_budget_ms` 以下でなければならない。デフォルト: 50。 | | `priority` | integer | No | マージ衝突解決のプロバイダー順序。低い値 = 高優先。デフォルト: 0。 | | `tmpx_macros` | List\ | No | このプロバイダーの TMPX レスポンスが埋める、安定したプロバイダー名前空間化されたアドサーバーマクロ名、順序付き(例: `["PIN_TMPX_1", "PIN_TMPX_2"]`)。パブリッシャーはこれらの正確な名前をそのアドサーバーでトラフィックする。ルーターは各プロバイダーの TMPX チャンクを identity-match レスポンスの一致するスロットに置く。名前はパターン `^[A-Z][A-Z0-9_]*$` に一致しなければならない(MUST)。v1 では 2 エントリに上限。上限は shape change なしに上げられる(MAY)。TMPX を発する(すなわち identity-match レスポンスに `tmpx_macros` を投入する)プロバイダーはこのリストも登録しなければならない(MUST)。さもなければルーターは `tmpx_providers` に転送するスロット名を持たない。「TMPX を発する」がスキーマ可視の述語でないためスキーマはこれを強制できない。マクロ名は実行時に `provider_id` から導出してはならない(MUST NOT) — トラフィッキングはこれらの登録された名前に対して事前に設定される。 | | `status` | enum | No | プロバイダーライフサイクルステータス: `active`、`inactive`、または `draining`。デフォルト: `active`。 | `context_match` または `identity_match` の少なくとも一方が true でなければなりません — どちらの操作も扱わないプロバイダーは無効です。`identity_match` が true のとき、`countries` と `uid_types` は **必須** です — ルーターはそれらなしに国分割アイデンティティルーティングを実行できません。スキーマは両方の制約を強制します。 プロバイダーは `context_match` と `identity_match` の任意の組み合わせをサポートしてもよい(MAY)。`context_match` のみをサポートするプロバイダーは純粋なエンリッチメントまたはコンテキストターゲティングプロバイダー。`identity_match` のみをサポートするプロバイダーはフリークエンシーキャッピングプロバイダー — パブリッシャーはメディアバイのターゲティングルールからコンテキストをローカルで評価し、アイデンティティチェックのためだけにバイヤーを呼ぶ。 ### Provider lifecycle プロバイダーは 3 つのライフサイクル状態を持ちます: * **Active**: プロバイダーが通常どおりリクエストを受け取る。 * **Draining**: プロバイダーが新しいリクエストの受け取りを停止する。飛行中のリクエストは通常どおり完了する。プロバイダーを保守のためオフラインにするとき使う — ルーターはこのプロバイダーへの新しいファンアウトを開始せずに現在の作業を終える。 * **Inactive**: プロバイダーが完全にスキップされる。設定を削除せずにプロバイダーを無効化するとき使う。 状態遷移は静的設定では即時(設定をリロード)で、動的登録では 1 リフレッシュサイクル内に有効になる。 ### Provider registration security **エンドポイント URL 検証(SSRF)。** 静的設定と動的登録の両方が、プロバイダー `endpoint` URL を正準の [Webhook URL 検証(SSRF)](/docs/building/by-layer/L1/security#webhook-url-validation-ssrf) ルールに対して検証しなければなりません(MUST) — 本番では HTTPS のみ、予約された IPv4 と IPv6 範囲を拒否(`::ffff:0:0/96` の IPv4 マップバイパスと `169.254.169.254` / `fd00:ec2::254` クラウドメタデータアドレスを含む)、リダイレクトなし。ルーターがすべてのリクエストでプロバイダーを呼ぶため、DNS リバインディングが主要なリスク: ルーターは、検証を通過した IP に TCP 接続をピン留めするか、リクエストボディを送る前にソケットのハンドシェイク後のピアアドレスを再検証するかのいずれかをしなければならない(MUST)。ピン留めなしに DNS を再解決するのは不十分。 **動的登録認証。** 動的登録 API は特権的なサーフェス。未認証の登録は攻撃者がパブリッシャートラフィックを任意の HTTPS エンドポイントに向けることを許す。動的登録を公開するルーターは呼び出し元を認証しなければならず(MUST)(mTLS または短命の OAuth 2.0 トークン。静的 API キーは IP 許可リストとともにのみ)、変更のエージェントごとのレート制限と登録ストーム悪用を有界化するためパブリッシャーごとの総登録プロバイダー数の上限を適用すべき(SHOULD)。 **ルーター対プロバイダー認証。** [Request Authentication](#request-authentication) の既存の「デプロイ固有(mTLS、API キーなど)」の言葉がメカニズムを設定する。最低ラインは、本番プロバイダーが匿名呼び出しを受け入れてはならない(MUST NOT)こと。静的 bearer トークンは IP 許可リストとともにのみ使ってもよい(MAY)。 **`/health` エンドポイント。** ルーターの生存性のためにプロバイダーが公開することが推奨される `/health` エンドポイントは未認証でもよい(MAY)が、レスポンスは内部状態を漏らしてはならない(MUST NOT)。プロバイダーは準備完了のとき `200` をボディ `{"status": "ok"}` とともに、準備未完了のとき `503` を返すべき(SHOULD)。他のステータスコードはバグ。プロバイダーは内部サブシステムによってステータスコードやレスポンスボディを区別してはならない(MUST NOT)(例えば、データベースがダウンしているときとアイデンティティキャッシュがダウンしているときの distinct なコードは、外部プロービングを内部トポロジーにマップするサイドチャネル)。バージョン文字列、ビルドハッシュ、内部ホスト名、依存関係ステータスはボディに現れてはならない(MUST NOT)。DoS 増幅器にならないようエンドポイントをレート制限(推奨: ソース IP ごとに 1 req/秒)する。 ## Product Integration パブリッシャーは `trusted_match` フィールド経由でそのプロダクトに TMP サポートを宣言します。バイヤーは `get_products` でこれを見て、どの TMP ケイパビリティが利用可能かを知ります。 | Field | Type | Required | Description | | ---------------- | -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `context_match` | bool | Yes | プロダクトが Context Match リクエストをサポートする。 | | `identity_match` | bool | No | プロダクトが Identity Match リクエストをサポートする。デフォルト: false。 | | `response_types` | List\ | No | パブリッシャーが受け入れられるもの: `activation`(デフォルト)、`catalog_items`、`creative`、`deal`。 | | `dynamic_brands` | bool | No | バイヤーがマッチ時にブランドを選択できるか。false(デフォルト)のとき、ブランドはメディアバイになければならない。true のとき、バイヤーのオファーは任意のブランドを含められる — パブリッシャーがマッチ時に承認ルールを適用する。マルチブランド合意を可能にする。 | | `providers` | List\ | No | このプロダクトのインベントリと統合された TMP プロバイダー。各エントリは `agent_url`(レジストリから)でプロバイダーを識別しどのマッチタイプをサポートするかを宣言する。プロダクトレベルの `context_match` と `identity_match` boolean が全体的なサポートを宣言し、プロバイダーごとの boolean がどのプロバイダーが各を扱うかを宣言する。バイヤーディスカバリーを可能にする。 | #### ProviderEntry | Field | Type | Required | Description | | ---------------- | ------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `agent_url` | string (URI) | Yes | レジストリからのプロバイダーのエージェント URL。この TMP プロバイダーの正準識別子。バイト等価ではなく [AdCP URL 正準化ルール](/docs/reference/url-canonicalization) を使ってルーターのプロバイダーレジストリに対して比較される。 | | `context_match` | bool | No | このプロバイダーがこのプロダクトの context match を扱うか。デフォルト: false。 | | `identity_match` | bool | No | このプロバイダーがこのプロダクトの identity match を扱うか。デフォルト: false。 | | `countries` | List\ | No | このプロバイダーが提供する ISO 3166-1 alpha-2 国コード。ルーターはリクエストの `country` フィールドでプロバイダーをフィルターする。`identity_match` が true のとき必須。 | | `uid_types` | List\ | No | このプロバイダーが解決できるアイデンティティタイプ(`uid-type` enum から)。ルーターは、`uid_types` がリクエストの `identities` 配列の任意の `uid_type` と重なるプロバイダーを選択し、転送された `identities` を交差にフィルターする — プロバイダーは宣言しなかったタイプのトークンを受け取ってはならない(MUST NOT)。`identity_match` が true のとき必須。 | ## Package Sync パッケージメタデータは、メディアバイ作成時、およびメディアバイが実質的に変わるたびに、セラーエージェントから TMP プロバイダーに同期されます。プロバイダーはプレースメントごとに `AvailablePackage` セットをキャッシュしリクエスト時に使う — `context_match_request` や `identity_match_request` を通じてパッケージメタデータは流れない。同期トランスポート、認証、バッチエラー形状はデプロイ固有。このセクションはペイロードコントラクトと各参加者が継承する義務を定義する。 `AvailablePackage` の `seller_agent` は実験的 `trusted_match.core` サーフェスの下で必須です。既存のデプロイで TMP を実行するセラーは、それを投入するよう同期ペイロードを更新しなければなりません — 3.x-to-3.x 進化ポリシーについては [実験的機能コントラクト](/docs/reference/experimental-status) を参照。 ### AvailablePackage | Field | Type | Required | Description | | -------------- | --------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `package_id` | string | Yes | パッケージの一意識別子。 | | `media_buy_id` | string | Yes | このパッケージが属するメディアバイ。 | | `seller_agent` | SellerAgentRef | Yes | このパッケージを所有するセラーエージェント。`agent_url` は、このパッケージが提供しうるすべてのプロパティについて権威ある adagents.json の `authorized_agents[].url` の 1 つに一致しなければならない(MUST)。[Seller Agent Attribution](#seller-agent-attribution) を参照。 | | `format_ids` | List\ | No | このパッケージに適格なクリエイティブフォーマット識別子。標準の `{agent_url, id}` 形状を使う。 | | `catalogs` | List\ | No | このパッケージに添付されたバイヤーカタログ、どのアイテムが対象かをスコープするセレクター付き。別途同期されたカタログデータに対して `catalog_id` で参照される。 | #### SellerAgentRef | Field | Type | Required | Description | | ----------- | ------------ | -------- | --------------------------------------------------------------------------------------------------------- | | `agent_url` | string (URI) | Yes | セラーエージェントの API エンドポイント URL、プロパティパブリッシャーの adagents.json `authorized_agents[].url` で宣言されたとおり正確に。本番では HTTPS。 | | `id` | string | No | 将来のレジストリ割り当ての安定したセラー識別子のために予約。今日は使われない。参照が破壊的リネームなしに後で不透明 ID 層を吸収できるよう含まれている。 | ### Seller Agent Attribution TMP プロバイダーは多くのセラーエージェントからのパッケージを多くのパブリッシャーに対してキャッシュします。`seller_agent` フィールドは各キャッシュされた `AvailablePackage` にその来歴を明示的にし、プロバイダー — メディアバイストアへのアクセスを持たない — が、帯域外ルックアップなしにオファーを帰属させ、セラーごとの可観測性を適用し、紛争を解決できるようにします。 AdCP の正準セラーアイデンティティは、プロパティパブリッシャーの [adagents.json](https://adcontextprotocol.org/schemas/v3/adagents.json) `authorized_agents[].url` エントリで宣言されたエージェント URL です。TMP は並行する識別子空間を導入するのではなくその URL を直接再利用します。`SellerAgentRef` の `id` スロットは将来のレジストリ割り当ての不透明な識別子のために予約され、今日は使われません。 **配置理由。** `context_match_request` と `identity_match_request` の両方が `seller_agent_url` を運びます。それは、受信エージェントが尋ねるセラーに登録したアクティブなパッケージセットを解決するのに使うキー: コンテキストパスのプロバイダー、アイデンティティパスのバイヤーエージェント。`package_ids` が省略されたとき、評価はそのセラーの完全なアクティブセットに対して実行される。受信者がパッケージを同期していない `seller_agent_url` は、別のセラーのセットへのフォールバックではなく空の結果を生成しなければならない(MUST)。 `seller_agent` は、アトリビューションのためキャッシュされた `AvailablePackage`(同期時)にも存在する — そのバインディングがどのセラーがパッケージを所有するかの真実の源泉で、下のオファーエコーがそれから読む。2 つは異なる仕事を運ぶ: リクエスト側の `seller_agent_url` はどのセラーのセットを評価するかを選択し、パッケージ側の `seller_agent` は個々のパッケージを帰属させる。同期時はプロバイダーが最初にパッケージについて学ぶとき。そのバインディングは一度確立され後続のすべての評価で再利用される。 `seller_agent_url` は [Package set decorrelation](#package-set-decorrelation) 保証と相互作用しません。その保証は、ユーザーごとに変わるデータ — 主に `package_ids`、その構成は現在のプレースメントと独立でなければならない — を制約します。`seller_agent_url` は尋ねるセラーを識別する単一の安定した値で、特定のプレースメントのすべてのユーザーで同一でありユーザーアイデンティティを運ばないので、コンテキストとアイデンティティのリクエストが相関されうるユーザーごとのシグナルを追加しない。これは受信者側のアクティブセットスコーピングで、ユーザーごとのフィルターではない。 **オファーエコー。** `seller_agent` は、メディアバイストアに再結合せずにワンホップアトリビューションを望むパブリッシャー側のログパイプラインのため、キャッシュされたパッケージからのエコーとして `offer.json` に現れてもよい(MAY)。エコーは非権威的 — キャッシュされた `AvailablePackage` バインディングが真実の源泉。プロバイダーとルーターは、ログ、転送、または下流へのオファー発出の前に、不一致のエコーをキャッシュされたバインディングで上書きしなければならず(MUST)、課金、レポート、紛争解決に消費される任意のフィールドでエコー値を使ってはならない(MUST NOT)。不一致は異常検出のため `seller_agent_echo_mismatch` メトリクスにカウントすべき(SHOULD)。ルーターはプロバイダーが省略したときマージで `seller_agent` をスタンプしてもよい(MAY)。 ### Sync-Time Validation プロバイダーは同期時に `seller_agent.agent_url` をプロパティパブリッシャーの adagents.json に対して検証すべきです(SHOULD): 1. パッケージが提供しうる各プロパティについて、プロパティドメインの `/.well-known/adagents.json` をフェッチする。ファイルが `authoritative_location` ポインターを含む場合、同一スキームの HTTPS URL への最大 1 ホップでそれをたどる。それ以上チェーンしない。初期フェッチと `authoritative_location` ホップの両方が [Webhook URL 検証(SSRF)](/docs/building/by-layer/L1/security#webhook-url-validation-ssrf) ルールを適用しなければならない(MUST) — HTTPS のみ、予約された IPv4 と IPv6 範囲を拒否(`::ffff:0:0/96` IPv4 マップバイパスと `169.254.169.254` / `fd00:ec2::254` クラウドメタデータアドレスを含む)、透過的リダイレクトなし、TCP 接続を検証された IP にピン留め。[Provider registration security](#provider-registration-security) で参照されるインバウンドフェッチルールは、このアウトバウンドフェッチにも等しく適用される。 2. `seller_agent.agent_url` が `authorized_agents[].url` に現れ、そのエントリの任意の `property_ids`、`collections`、`placement_ids`、`placement_tags`、`countries`、`effective_from`、`effective_until` 制約がパッケージのスコープを許すことを確認する。URL 比較は [AdCP URL 正準化ルール](/docs/reference/url-canonicalization) を使う — マッチング前に両方の値を正準化する、決してバイト等価でない。`https://` スキームを使わない `seller_agent.agent_url` 値を `seller_not_authorized` で拒否する。非 HTTPS セラー URL はトランスポート完全性保証を持たず認可キーとして信頼できない。 3. 不一致で、その `AvailablePackage` の同期操作を `code: seller_not_authorized` を使う `error` レスポンスで拒否する。同じ同期バッチの他のパッケージは影響を受けない。同期エラーの正確なワイヤー形状はデプロイ固有。`code` が機械可読な理由。 **キャッシュと再検証。** 検証結果は最大 5 分キャッシュすべき(SHOULD)、推奨 adagents.json キャッシュウィンドウに一致。プロバイダーはキャッシュ期限切れで再検証しなければならず(MUST)、持続的な `seller_not_authorized` 拒否をパブリッシャー運用にサーフェスすべき(SHOULD) — 繰り返しの失敗は通常、パブリッシャーの adagents.json とセラーの同期パイプラインが分岐したことを示す。認可の `effective_until` が過ぎるかセラーが `authorized_agents` から削除されるとき、プロバイダーは、再同期・再検証されるまで、そのセラーからの以前キャッシュされたパッケージをリクエスト時に `unknown_package` として扱わなければならない(MUST)。 **フェッチ失敗。** 検証が完了できないとき(フェッチエラー、タイムアウト、証明書失敗、不正な形式のファイル)、プロバイダーは、検証されていないバインディングをキャッシュするのではなく `seller_not_authorized` で同期を拒否すべき(SHOULD)。fail-open するプロバイダーは、検証されていないウィンドウを 5 分のキャッシュ TTL に有界化し、次の機会に再検証しなければならない(MUST)。一度も検証に成功していないバインディングは、そのウィンドウを過ぎてキャッシュされたままであってはならない(MUST NOT)。 **事前アテスト済み関係のバイパス。** プロバイダーは、同じ `agent_url → authorized_agents[].url` バインディングを帯域外オンボーディングプロセス(例: パブリッシャーのアテステーションを運ぶ相互認証されたプロバイダー-セラー登録)を通じて検証できるときのみ adagents.json チェックをスキップしてもよく(MAY)、パブリッシャーがオンボーディングをゲートできるよう、その適合性自己レポートに強制モード(`enforcing` / `advisory`)を公開すべき(SHOULD)。このエスケープハッチは `trusted_match.core` v1 で許可され、最初の非実験的 TMP リリースで削除され、その時点で検証は MUST になる。 ### Participant Responsibilities | Actor | Sync time | Request time | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Seller agent** | 同期するすべての `AvailablePackage` に、自身の adagents 登録された `agent_url` を `seller_agent.agent_url` として含める。パブリッシャーが `authorized_agents[].url` にアテストした URL でなければならない(MUST)。 | 新しい動作なし。オファーは `seller_agent` をエコーしてもよい(MAY)。省略も問題ない — ルーターがキャッシュからスタンプできる。 | | **Publisher** | adagents.json の `authorized_agents` を、スコープ制約(`property_ids`、`placement_ids`、`countries`、有効ウィンドウ)を含め正確に保つ。 | 新しい動作なし。 | | **Router** | 新しい動作なし。 | キャッシュされた package→seller バインディングを使って、それなしに到着したオファーに `seller_agent` をスタンプしてもよい(MAY)。`context_match_request` と `identity_match_request` の両方がそのスキーマごとに `seller_agent_url` を直接運ぶ。ルーターはそれを変更せずに転送しなければならず(MUST)、リクエストをフィルターまたは再ルーティングするのに使ってはならない(MUST NOT)。 | | **Provider** | [Sync-Time Validation](#sync-time-validation) ごとに `seller_agent.agent_url` をプロパティの adagents.json に対して検証する。認可されていないパッケージを `seller_not_authorized` で拒否する。バインディングをキャッシュされたパッケージとともに保存する。 | 生成するオファーに `seller_agent` をエコーする。キャッシュされたバインディングは飛行中の決定について不変だが、プロバイダーは、認可が期限切れになったパッケージ(`authorized_agents` からの削除または `effective_until` 経過)を、パッケージが再同期されるまで後続リクエストで `unknown_package` として扱わなければならない(MUST)。 | ### What This Is Not * **ユーザーごとのフィルターではない。** `seller_agent_url` は受信者が評価するどのセラーの登録されたアクティブセットかを選択する — プレースメントのすべてのユーザーで同じ値でユーザーアイデンティティを運ばない。パブリッシャー、ルーター、プロバイダーはそれをユーザーごとまたはリクエストごとのフィルターとして、またはリクエストを再ルーティングするために使ってはならない(MUST NOT)。そうすることは [Package set decorrelation](#package-set-decorrelation) が防ぐために存在するユーザーごとの変動を再導入する。パッケージ側の `seller_agent` アトリビューションも同様にリクエストをスコープまたはフィルターするのに使ってはならない(MUST NOT) — オファーアトリビューションのためだけに存在する。 * **sellers.json ブリッジではない。** IAB sellers.json `seller_id` と TAG-ID は distinct な財務監査アイデンティティ空間に提供する。それらは `adagents.json` `contact` に残る — TMP はそれらを複製しない。 * **暗号学的アテステーションではない。** バインディングは adagents.json 経由で HTTPS 上でパブリッシャーアテストされる。署名付き TMP セラークレームは将来の強化で、破壊的変更なしに予約された `id` スロットまたは `ext` フィールドを通じて `SellerAgentRef` に重ねられる。将来のリリースが `agent_url` と `id` の両方を投入するとき、`agent_url` が権威的のままで `id` は助言的 — 2 つの間の不一致は、URL の adagents.json バインディングが提供するものを超えて信頼を格上げするのに使ってはならない(MUST NOT)。 ## Privacy Requirements 次の要件は RFC 2119 キーワード(MUST、SHOULD、MAY)を使います。 ### Structural separation * Context Match リクエストはユーザーアイデンティティデータ(ユーザートークン、デバイス ID、IP アドレス、セッショントークン、特定のユーザーを識別しうる任意のデータ)を含んではならない(MUST NOT)。 * Identity Match リクエストはページコンテキストデータ(URL、コンテンツハッシュ、トピック ID、コンテンツシグナル、ユーザーが何を見ているかを識別しうる任意のデータ)を含んではならない(MUST NOT)。 * TMP ルーターは、共有状態のない構造的に分離されたコードパスで Context Match と Identity Match を処理しなければならない(MUST)。 * Context Match と Identity Match のリクエスト ID は相関または互いから導出可能であってはならない(MUST NOT)。 ### Package set decorrelation * Context Match はユーザーアイデンティティやオーディエンスでフィルターしてはならない(MUST NOT)。プロバイダーはプレースメントの同期されたパッケージセット — すべてのユーザーで同じパッケージ — を評価する。リクエストごとのパッケージリストは送られないので、パブリッシャーはパッケージフィルタリングを通じて誤ってアイデンティティを漏らせない。 * パブリッシャーは Identity Match から `package_ids` を省略し、バイヤーに `seller_agent_url` に登録した完全なアクティブセットに対して評価させるべき(SHOULD)。`package_ids` が提供されるとき、その構成は現在のプレースメントと統計的に独立でなければならない(MUST) — ページ固有のサブセットのみを送ることは、バイヤーがパッケージセットを比較して Identity Match を Context Match と相関させることを許す。2 つの許容モード: * **All-active。** このバイヤーがこのパブリッシャーに持つすべてのアクティブパッケージを含める。 * **Fuzzed。** アクティブなパッケージのランダムサンプル、任意で合成の存在しない ID でパディング、現在のプレースメントに依存しない分布から抽出。下の silent-drop ルールが合成 ID パディングを安全にする — 未知の ID はレスポンス形状に影響せずレジストリメンバーシップを漏らせない。 * `seller_agent_url` と `package_ids` の両方が存在するとき、バイヤーは登録されたアクティブセットと `package_ids` の交差に対して評価する。`package_ids` の未知の ID は、レスポンスがレジストリメンバーシップをパブリッシャーに漏らさないよう、黙って無視されなければならない(MUST)(エラーサーフェスしない)。 * バイヤーごとのすべてのアクティブパッケージ ID のキャッシュされたリストを維持するパブリッシャーは、多層防御としてすべての Identity Match リクエストで完全なセットを送ってもよい(MAY)が、`seller_agent_url` からのバイヤー側解決が主要なメカニズム。 * パブリッシャーは、両方のレスポンスが到着した後、context match オファーと identity match 適格性の交差をローカルで実行する。 ### Temporal decorrelation * パブリッシャーは Context Match と Identity Match リクエスト間にランダムな遅延を導入すべき(SHOULD)。推奨: 100-2000ms、一様分布。 * パブリッシャーは Context Match と Identity Match の順序もランダム化すべき(SHOULD): 各機会は Context Match が先に送られるか Identity Match が先に送られるかのほぼ等しい確率を持つべき。固定順序 — 例えば Identity Match が常に Context Match の後 — は、遅延がランダム化されても順序を通じてペアリングを漏らす。 * パブリッシャーは複数のページビューにわたって Identity Match リクエストをバッチしてもよい(MAY)。 * パブリッシャーは Context Match と Identity Match を異なるネットワークパス経由でルーティングしてもよい(MAY)。 ### TEE attestation * TMP ルーターは、利用可能なとき、デプロイされたバイナリが公開されたソースに一致することを証明する TEE アテステーションを提供すべき(SHOULD)。 * アテステーションドキュメントは、リクエストに応じてパブリッシャーと監査者に利用可能であるべき(SHOULD)。 * アテステーションは、サービスコードの完全性と分離を確認する測定を含むべき(SHOULD)。 ### Consent handling * Identity Match リクエストから `consent` が省略されたとき、バイヤーはこれを「同意不要」ではなく「同意ステータス不明」として扱わなければならない(MUST)。 * 同意が要求される管轄区域のバイヤーは、`consent` を省略する Identity Match リクエストを、同意を仮定するのではなく拒否しなければならない(MUST)。 ### User token requirements * ユーザートークンはバイヤーエージェントにとって不透明でなければならない(MUST)。トークンはアイデンティティプロバイダー(ID5、LiveRamp、UID2)から発生するか、パブリッシャー生成であってよい。 * ユーザートークンは PII を含んではならず、バイヤーエージェントによって PII に逆変換可能であってはならない(MUST NOT)。 ## Request Authentication TMP リクエストは、リクエストが認可されたルーターから発生したことを証明する署名を運びます。これは、認可されていない当事者がプロバイダーに偽造リクエストを送ってターゲティングロジックをプローブし、スポンサーコンテンツを抽出し、フリークエンシー状態を操作するのを防ぎます。 ### Signing model ルーターは Ed25519 を使ってすべてのリクエストに署名します。Context Match と Identity Match の両リクエストが署名されますが、その異なるコンテンツとキャッシュ特性を反映する異なる署名フィールドで。 署名はリクエストを特定のプロバイダーにバインドします。ルーターは、ルーターのプロバイダー登録からのプロバイダーのエンドポイント URL を使って、ファンアウトターゲットごとに別個の署名に署名します。プロバイダーは、署名された `provider_endpoint_url` が自身のアドバタイズされたエンドポイントに一致することを検証し、そうでなければリクエストを拒否しなければなりません(MUST)。これは、キャプチャされた署名がエポック内でレジストリの異なるプロバイダーに対してリプレイされるのを防ぎます。 署名は、尋ねるセラーのエージェント URL である `seller_agent_url` にもバインドします。`seller_agent_url` は受信者がどのセラーの登録されたアクティブパッケージセットに対して評価するか([Seller Agent Attribution](#seller-agent-attribution) を参照)を選択し、受信者が検証のためパブリッシャーの署名鍵を解決するのに使うルックアップキーです。それを署名バイトに含めることは、キャプチャされた署名が別のセラーのアイデンティティの下でリプレイされて別のセラーのオファーやアクティブパッケージセットを読むのを防ぎます。 日次エポックがリプレイ保護を提供します — キャプチャされた署名はせいぜい約 48 時間(現在 + 前のエポックが検証者に受け入れられる)有効です。 ### Signature envelope 署名は JSON ボディと並んで HTTP ヘッダー経由で送信されます。 | Header | Value | | ------------------ | ----------------------------------------- | | `X-AdCP-Signature` | Base64 エンコード(URL セーフ、パディングなし)の Ed25519 署名 | | `X-AdCP-Key-Id` | エージェントの `agent-signing-key.json` からの鍵識別子 | #### Context Match signed fields この順序で連結、UTF-8、改行区切り: | Field | Source | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `type` | `context_match_request` | | `seller_agent_url` | リクエストボディから、[AdCP URL 正準化ルール](/docs/reference/url-canonicalization) を使って比較。署名を尋ねるセラーにバインド — 異なる `seller_agent_url` の下で署名を再利用すると検証に失敗。 | | `property_rid` | リクエストボディから | | `placement_id` | リクエストボディから | | `package_ids` | ソートされた、カンマ区切りのアクティブなパッケージ ID のリスト | | `provider_endpoint_url` | プロバイダーの登録されたエンドポイント URL(プロバイダー登録との正確な文字列一致、末尾スラッシュなし) | | `daily_epoch` | `floor(unix_timestamp / 86400)` | `package_ids` がリクエストから欠如するとき、署名は署名されたペイロードのそのフィールドに空文字列を使わなければならない(MUST)。 署名フィールドはセラーごとプレースメントごとプロバイダーごとに静的なので、同じ署名は 24 時間エポック内の同じ `(seller_agent_url, placement_id, provider_endpoint_url)` トリプルへのすべてのリクエストにキャッシュして再利用できます。キャッシュキーは `seller_agent_url` と `provider_endpoint_url` を含まなければならない(MUST) — セラーやプロバイダーをまたいで署名を再利用することはバインディングに違反し検証に失敗する。 #### Identity Match signed fields 署名された入力は、次の正準オブジェクトの [RFC 8785 JCS](https://datatracker.ietf.org/doc/html/rfc8785) シリアライゼーションの hex エンコードされた SHA-256 です。JCS を使うことでデリミタ注入リスクが除去されます — 生の `tcf_consent` / `gpp` / `us_privacy` / `package_id` 値は任意のバイトを含みうるが、JCS の JSON 文字列エスケープが任意のフレーミングバイトを無害にする。 | Field | Source | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `type` | `"identity_match_request"` | | `request_id` | リクエストボディから | | `seller_agent_url` | リクエストボディから、[AdCP URL 正準化ルール](/docs/reference/url-canonicalization) を使って比較。署名を尋ねるセラーにバインド — 異なる `seller_agent_url` の下で署名を再利用すると検証に失敗。 | | `identities_hash` | 正準 `identities` バイトの hex エンコードされた SHA-256(下記参照) | | `consent` | リクエストの `consent` オブジェクト逐語的、または欠如のとき `null` | | `package_ids` | UTF-8 バイト順で辞書順ソートされたリクエストの `package_ids` | | `sealed_credentials_hash` | 正準 `sealed_credentials` バイトの hex エンコードされた SHA-256(下記参照)、または欠如のとき `null`。**実験的(`trusted_match.verified_identity`)。** | | `provider_endpoint_url` | プロバイダーの登録されたエンドポイント URL(プロバイダー登録との正確な文字列一致、末尾スラッシュなし)。署名を特定のプロバイダーにバインド — プロバイダーをまたいで署名を再利用すると検証に失敗。 | | `daily_epoch` | JSON 整数としての `floor(unix_timestamp / 86400)` | **正準 `identities` バイト:** バイト正確なマッチ(case folding なし、trimming なし)を使って `(uid_type, user_token)` で重複排除 — これは重複トークンのみを折り畳む — エントリを `uid_type`(UTF-8 バイト順)、次に `user_token`(UTF-8 バイト順)でソートし、結果の **完全なアイデンティティオブジェクト** の配列を [RFC 8785 JCS](https://datatracker.ietf.org/doc/html/rfc8785) としてシリアライズ。各オブジェクトは、**任意の `attestation` を含め** 完全にシリアライズされる — そのため署名がアテステーションをカバーし、剥がされた、交換された、または注入されたアイデンティティごとのアテステーションが署名検証を壊す(`(uid_type, user_token)` の重複排除キーは重複トークンを折り畳むためで、`attestation` がハッシュから除外されるという声明ではない)。UTF-8 バイトを SHA-256 し、署名された入力には hex エンコード、キャッシュキーには生バイトを使う(両方の慣例が同じプリイメージをハッシュする)。 **正準 `sealed_credentials` バイト**(実験的、`trusted_match.verified_identity`): エントリを `audience_kid`(UTF-8 バイト順)でソートし、結果の配列を [RFC 8785 JCS](https://datatracker.ietf.org/doc/html/rfc8785) としてシリアライズ、UTF-8 バイトを SHA-256。リクエストが `sealed_credentials` を運ばないとき、`sealed_credentials_hash` は `null`。`sealed_credentials` が署名された入力の一部なので、注入または交換された封印 blob が署名検証を壊す。`sealed_credentials_hash` が重複排除キャッシュキーの一部([Caching](#caching) を参照)なので、ネットワーク認証情報の変更がキャッシュを再分割し古い適格性が提供されない。ルーターは [route-by-`audience_kid`](#verified-identity-attestation) フィルタリングの後、各プロバイダーの転送されたセットに対して再署名する。 ルーターは転送前にプロバイダーごとに `identities` をフィルターします([Identity Match ファンアウト](/docs/trusted-match/router-architecture#identity-match-fan-out) を参照)。署名は各プロバイダーのフィルターされた `identities` セットに対して計算される — ルーターはアウトバウンド転送ごとに再署名する。同じフィルターされた `identities_hash` 値がキャッシュキー([Caching](#caching) を参照)で使われるので、各プロバイダーは実際に受け取ったサブセットでキー付けされた独自のキャッシュパーティションを持つ。 Identity Match 署名は `request_id` と `identities_hash` を含むので、リクエストごとに一意でキャッシュできない。これは意図的 — Identity Match レスポンスはバイヤー側のフリークエンシー状態に影響し冪等でなければならない。バイヤーは日次エポックウィンドウ内で `request_id` によって Identity Match リクエストを重複排除しなければならない(MUST)。繰り返された `request_id` は、フリークエンシー状態を更新せずに同じレスポンスを返さなければならない(MUST)。バイヤーは生の識別子を保持するのではなく `hash(request_id)` で重複排除すべき(SHOULD)。 ### Signature verification ルーターは、署名しファンアウトする前に、パブリッシャーからのインバウンドリクエストを認証しなければなりません(MUST)。パブリッシャー対ルーター認証のメカニズムはデプロイ固有(mTLS、API キーなど)で TMP 署名のスコープ外だが、強制されなければならない(MUST)。これは、侵害されたパブリッシャー側のコンポーネントが未認証リクエストをルーターの署名を通じてロンダリングするのを防ぎます。 ルーターはファンアウトする前にリクエストに署名します。プロバイダーは、プロパティレジストリから得たパブリッシャーの公開鍵を使って署名を検証します。これは、リクエストが認可されたルーターから発生したことを証明します — プロバイダーのターゲティングロジックをプローブするサードパーティではなく。プロバイダーは、すべてのリクエストを検証するのではなくサンプル検証すべき(SHOULD) — Ed25519 検証はリクエストごとに約 30μs 追加し、完全なパイプラインに比べれば小さいがボリュームでは積み重なる。5% のサンプルレートが、無視できるオーバーヘッドを追加しながら数秒以内に不正なパブリッシャーを検出する。 検証失敗で、プロバイダーは設定可能な期間(推奨: 24 時間)プロパティを抑制し運用にアラートすべき(SHOULD)。 ### Key rotation エージェントは、その well-known エージェント URL の `agent-signing-key.json` 経由で新しい署名鍵を公開します。ルーターは 5 分の TTL で鍵をキャッシュすべき(SHOULD)。署名が検証に失敗するとき、ルーターは拒否する前に鍵を再フェッチすべき(SHOULD) — エージェントがローテートしたかもしれない。 ### Key revocation ローテーションは鍵を置き換え、失効は 1 つを殺します。失効は、秘密鍵が漏洩したと知られているか疑われる侵害のケースのためです。 パブリッシャーは、`agent-signing-key.json` の鍵エントリに `revoked_at`(ISO 8601 タイムスタンプ)を設定し、古いキャッシュが依然としてそれを見つけられるよう猶予期間中に鍵を trust anchor に残すことで、侵害された鍵をマークします。検証者は次をしなければなりません(MUST): * `revoked_at` が存在し署名エポックが失効タイムスタンプ以降に落ちる鍵で生成された任意の署名を拒否する。 * 最後のキャッシュリフレッシュの後に伝播した失効を拾うため、拒否する前に検証失敗で `agent-signing-key.json` を再フェッチする。 * 問題のあるリクエストについて失効を再試行不可として扱う — 「リトライで動くかも」の状態はない。 キャッシュ TTL(推奨: 5 分)がすべての検証者にわたって経過したら、鍵は trust anchor から完全に削除されてもよい。鍵を早く削除すると、キャッシュされたコピーを持つ検証者が新鮮なフェッチなら拒否する署名を受け入れるウィンドウを生みうる — キャッシュ伝播が完了するまで `revoked_at` マーカーを維持する。 **失効の制限。** 失効は 5 分以内(キャッシュ TTL)に伝播するが、失効が観測される前にプロバイダーによって既に受け入れられた署名を遡及的に無効化しない。`revoked_at` より前の署名エポックを持つキャプチャされた署名は、48 時間のリプレイウィンドウの残りの間検証可能なまま。侵害を疑うオペレーターは次をすべき(SHOULD): * 確認された漏洩だけでなく、任意の疑いで先制的にローテートする。 * そもそも侵害の可能性を最小化するため、署名鍵をディスクではなく HSM または KMS に保つ。 * 日次エポックのロールオーバーがそれを含む署名されたペイロードを退役させるまで、漏洩した鍵が最大約 48 時間の偽造能力を付与することを受け入れる。このウィンドウが受け入れられないとき、より短いカスタムエポックウィンドウがデプロイオプション。 ### Key distribution パブリッシャーの公開鍵はプロパティレジストリ経由で配布されます。各プロパティレコードはパブリッシャーの Ed25519 公開鍵を含みます。プロバイダーは起動時にレジストリをダウンロードし、増分同期経由で最新に保ちます。 ## Wire Format コンテンツタイプ: `application/json` すべての TMP メッセージは JSON エンコーディングを使います。フィールド名とタイプはこの仕様の JSON Schema 定義に従います。実装は JSON をサポートしなければなりません(MUST)。 TMP メッセージは小さい(リクエスト/レスポンスごとに 200-600 バイト)。これらのサイズでは、シリアライゼーション形式は総レイテンシーの 1% 未満 — プロトコルのパフォーマンスは、エンコーディング効率ではなく、より小さいメッセージと構造的分離から来る。JSON はユニバーサルで、デバッグ可能で、すべての言語とツールでサポートされる。 ## Country-Partitioned Identity アイデンティティデータは国(ISO 3166-1 alpha-2)で論理的に分割されます。プロトコルはすべての Identity Match リクエストとすべての TMPX トークンに国コードを運びます(バイヤーの読み取りレプリカは、データレジデンシールーティングのため暗号化された平文に国を含める — これはバイヤー内部でパブリッシャーやルーターに可視でない)。国が物理的にどうデータベースクラスターにグループ化されるかはデプロイ決定 — プロトコルはそれを制約しない。 パブリッシャーはルーティングディレクティブとして Identity Match リクエストに `country` フィールドを含めます。ルーターはそれを使って `countries` リストがその国コードを含むプロバイダーを選択し、次に転送前にフィールドを剥がします。バイヤーエージェントは決して国を見ません — アイデンティティシグナルではありません。 各プロバイダーエントリは、`countries` と `uid_types` フィールド経由でどの国とアイデンティティタイプを提供するかを宣言します。マルチカントリーのバイヤーはクラスターごとに別々のプロバイダーエントリを運用します(例: 米国に 1 つ、EU 諸国に 1 つ)。これにより、バイヤーはデータレジデンシー要件に準拠し、提供する国のみにパブリッシャーをサブスクライブし、プロトコル変更なしにクラスター間で国を移動できます。 ## TMPX Exposure Tokens TMP は暗号化された露出トークン(TMPX)を使ってフリークエンシーキャッピングのループを閉じます。Identity Match 読み取りレプリカは、解決されたアイデンティティトークンを、クリエイティブトラッキング URL を通じて流れる不透明な TMPX マクロに暗号化します。バイヤーのインプレッションピクセルがトークンを復号しユーザーごとの露出をログします。 ### Encryption TMPX は `mode_base` で HPKE(RFC 9180)を使います: | Parameter | Value | | --------- | --------------------------- | | Mode | `mode_base` — 受信者の公開鍵のみで暗号化 | | KEM | DHKEM(X25519, HKDF-SHA256) | | KDF | HKDF-SHA256 | | AEAD | ChaCha20-Poly1305 | バイヤーのクラスターマスターが受信者秘密鍵を保持します。読み取りレプリカはマスターの公開鍵を使って暗号化します。マスターのみが復号できます。TMPX トークンの偽造は、マスターの公開鍵(公開)とアイデンティティプロバイダー(UID2、ID5、RampID)からの現実的なアイデンティティトークンの両方を要求します — これらを捏造することは詐欺自体より難しい。系統的なフリークエンシー操作は、暗号化層ではなく IVT(Invalid Traffic)層で検出されます。 ### Binary format TMPX 平文はコンパクトなバイナリ構造です。タイプ ID がトークン長を暗黙的に定義します — 長さプレフィックスは不要です。 **ヘッダー(16 バイト):** | Field | Size | Description | | --------- | ------- | -------------------------------------------- | | Version | 1 byte | フォーマットバージョン(`0x01`) | | Timestamp | 4 bytes | uint32 Unix 秒 — トークン作成時刻 | | Country | 2 bytes | ISO 3166-1 alpha-2、ASCII — アイデンティティデータレジデンシー | | Nonce | 8 bytes | ランダム — マスターでのリプレイ重複排除 | | Count | 1 byte | アイデンティティエントリの数 | **エントリ(繰り返し、バイヤー設定の優先順位順):** | Field | Size | Description | | ------- | ------- | ----------------------------------- | | Type ID | 1 byte | 固定整数、トークンバイナリサイズを定義 | | Token | N bytes | 生バイナリアイデンティティトークン(サイズは Type ID で決定) | **Type ID レジストリ:** | ID | Token Type | Binary size | | -- | ----------------------- | ---------------------------------------------------------- | | 1 | uid2 | 32 bytes | | 2 | euid | 32 bytes | | 3 | id5 | 32 bytes | | 4 | rampid | 32 bytes | | 5 | rampid\_derived | 48 bytes | | 6 | maid | 16 bytes | | 7 | pairid | 32 bytes | | 8 | hashed\_email | 32 bytes | | 9 | publisher\_first\_party | 32 bytes | | 10 | world\_id\_nullifier | 48 bytes(16 バイトの relying-party ダイジェスト + 32 バイトの nullifier) | Type ID は安定 — 新しいタイプは追加され、既存の ID は決して変わらない。トークンはバイナリで保存される(UUID は 16 バイト、base64 エンコードされたトークンはデコード)。RampID は、維持された(XY、32 バイト)形式と派生された(Xi、48 バイト)形式がサイズで異なるため 2 エントリを持つ。 `world_id_nullifier` は **relying-party スコープ**: そのトークンは、証明の `relying_party_id` の 16 バイト SHA-256 ダイジェストに続く 32 バイトの nullifier。このエントリは **検証済み** アテステーションからのみ生成される — 受信者確認された `rp_id` の下の検証者由来の nullifier([Verified Identity Attestation](#verified-identity-attestation) を参照)。`identities[]` の送信者アサートの `world_id_nullifier` は信頼を運ばず決して解決も封印もされない。検証済み `rp_id` を欠くため、このトークンさえ形成できない。`uid2`/`id5`/`rampid` とは異なり、エントリはバイヤーが解決するグラフ識別子ではない — 人格証明の仮名で、付随する検証済みアテステーションとともにのみ有効。World ID nullifier はそれが鋳造された `rp_id` 内でのみ意味を持ち、その `rp_id` はリクエスト側の `attestation` に乗り、トークンにラウンドトリップしない。トークンに `rp_id` ダイジェストを運ぶことで、帯域外のインプレッショントラッカーが nullifier をその relying party に帰属させ(受け入れる relying parties に対してダイジェストをマッチング)、`(rp_id, nullifier)` ペアでフリークエンシー状態をキー付けできる。そのため、ある relying party の下の nullifier は別のものとキャップ状態を決して共有しない。トークンは `rp_id` 平文を運ばず、ダイジェスト幅はワーキンググループのオープン項目。 パーサーが未知の Type ID に遭遇した場合、パースを停止し残りのエントリを absent として扱わなければならない(MUST)。ヘッダー Count フィールドは総エントリを示すが、実装はすべてのエントリがパース可能と仮定してはならない(MUST NOT) — 前方互換性は、より新しい Type ID が存在するときの優雅な劣化を要求する。 ### Wire format TMPX マクロ値は `.` 形式を使います。ciphertext はパディングなし base64url エンコーディング(RFC 4648 セクション 5、`=` パディング文字なし)を使わなければなりません(MUST)。パディング文字は `=` がキー値デリミタである URL クエリパラメーターを壊します。`kid`(鍵識別子、最大 8 文字)は不透明 — 地理的またはデプロイ情報をエンコードしてはなりません(MUST NOT)。内部的にクラスターマスター秘密鍵にマップします。 **サイズ予算(255 文字 GAM マクロ制限):** HPKE オーバーヘッド(48 バイト)とヘッダー(16 バイト)の後、アイデンティティエントリに約 120 バイト残る。3 つの 32 バイトトークン = 99 バイト — 快適に収まる。バイヤーが予算に収まるより多くのアイデンティティを解決するとき、TMPX 平文はバイヤーデプロイ設定に従って最高優先のエントリに切り詰められる。優先順位はバイヤー側の設定の関心事(プロトコルレベルでない)で、通常決定論的グラフ(UID2、RampID)を確率的またはパブリッシャースコープの識別子より上にランク付けする。バイヤーは明示的な優先リストを設定しなければならず(MUST) — デフォルト実装は恣意的に切り詰めてはならない(MUST NOT) — リストはバイヤーの運用ランブックに文書化すべき(SHOULD)。 ### Key management クラスターごとに 1 つの X25519 キーペア: * クラスターマスターが復号のための **秘密鍵** を保持する。 * **公開鍵** は `adagents.json` のエージェント認可エントリの `encryption_keys` に公開される。 * 読み取りレプリカは公開鍵を使って暗号化する。レプリカごとの鍵管理なし。 鍵ローテーションは TMP 署名鍵と同じパターンに従う: 5 分のキャッシュ TTL、バージョニングのための kid プレフィックス、古いマスター鍵の 30 日の猶予期間。 ### Replay protection 8 バイトのランダム nonce がマスターでの重複排除を可能にする。マスターは設定可能なウィンドウ(推奨: 7 日)nonce を保存し重複を拒否する。nonce は AEAD 保護された ciphertext の内側にある — 仲介者はそれを観測できない。 ### Caching behavior TMPX トークンは Identity Match 評価ごとに一度生成され、`serve_window_sec` ウィンドウの間適格性レスポンスに付随する。そのウィンドウ内の適格なパッケージのすべてのインプレッションは同じ TMPX 値(同じ nonce、同じトークン)を共有する。 バイヤーのマスターは、サーブウィンドウ内で TMPX 値や nonce で重複排除してはならない(MUST NOT) — 各ピクセル発火は 1 インプレッション。CTV ポッドまたは複数の広告ユニットを持つ web ページで同じユーザーに提供される複数の広告はすべて、同じ TMPX トークンで distinct なピクセル発火を生成する。nonce 重複排除は、サーブウィンドウが期限切れになった *後* の同じ TMPX トークンのリプレイのみを防ぐ — 同じ nonce が元のウィンドウ外に現れたら、それはリプレイで拒否されなければならない(MUST)。 ### Publisher obligations パブリッシャーは TMPX 値をパース、復号、またはそれに基づいて決定してはならない(MUST NOT)。トークンは、他のマクロと正確に同様にクリエイティブトラッキング URL に代入される不透明なパススルーデータ。DOOH インベントリについては、プレーヤーは露出再照合のためバイヤーに送信される play log レコードに不透明な TMPX 値を含めてもよい(MAY) — パブリッシャーは送信ウィンドウを超えて TMPX 値を保持してはならない(MUST NOT)。 ### Inventory-specific behavior 各ターミナルサーフェスについて、パブリッシャーは各アイデンティティプロバイダーがその `tmpx_macros` 登録エントリで宣言したマクロ名(例: `PIN_TMPX_1`、`NOVA_TMPX_1`)をトラフィックする — それらの名前はアドサーバーラインアイテムがターゲットにする運用コントラクト。提供時に、パブリッシャーはレスポンスから `tmpx_providers[provider_id].macros[]` をたどり、各エントリの `value` を `name` で名付けられたスロットに逐語的に代入する。レガシー `{TMPX}` マクロ(非推奨の単数 `tmpx` フィールドから)は、移行していないコンシューマーのためサポートされたまま。 * **Web、モバイル、CTV(SSAI)、音声(DAI):** 標準インプレッションピクセル — 各 `tmpx_providers[*].macros[].value` を一致するアドサーバーマクロスロットに代入する。 * **CTV(クライアント側 VAST):** パブリッシャーは VAST ドキュメントがプレーヤーに到達する前に同じマクロごとの代入を実行する。 * **DOOH:** Play-log ベース — TMPX 値はピクセル URL ではなく DOOH プレーヤーによってログされ play log レコードに含まれる。プロバイダーごとのマクロ/値ペアは、バイヤーが正しい復号マスターにルーティングできるよう、その `provider_id` とともにログされなければならない(MUST)。 ## Transport * HTTP/2 POST 上の JSON。すべての実装はこのトランスポートを使わなければならない(MUST)。 * 各プロバイダーはそのベース URL の下に 2 つのパスベースのエンドポイントを公開する: Context Match の `POST /context` と Identity Match の `POST /identity`。ルーターは、メッセージボディを検査するのではなく、パスでリクエストをディスパッチする。 * 各プロバイダーはそのベース URL で `GET /health` を公開すべき(SHOULD)。エンドポイントは、プロバイダーがリクエストを受け入れる準備ができたとき HTTP `200` を JSON ボディ `{"status": "ok"}` とともに返す。任意の非 `200` レスポンスまたは接続失敗はプロバイダーが準備未完了を意味する。ルーターとオペレーターはこれをプリフライトチェックと監視に使う — リクエストのホットパスでは呼ばれない。 * 各メッセージの `type` フィールドはデシリアライゼーションのためにメッセージを識別する — ルーターとエージェントはそれを、ルーティングではなく正しいスキーマを選択するのに使う。エージェントは `type` フィールドがエンドポイントに一致することを検証しなければならない(MUST): `/context` の `context_match_request`、`/identity` の `identity_match_request`。不一致は HTTP `400` で拒否されなければならない(MUST)。 * 接続は HTTP/2 多重化経由で再利用すべき(SHOULD)。 * ルーターは各バイヤーエージェントへの接続プールを維持すべき(SHOULD)。 * [adcp-go](https://github.com/adcontextprotocol/adcp-go) SDK がリファレンスクライアントとサーバー実装を提供する。適合性テストが互換性を検証する — 他の言語の実装は同じテストスイートに合格しなければならない(MUST)。 ### HTTP Status Codes TMP はトランスポートレベルのエラーにのみ HTTP ステータスコードを使います。アプリケーションレベルの結果(TMP エラーレスポンスを含む)は常に HTTP `200` で返されます: | HTTP Status | Meaning | | ----------- | -------------------------------------------------------------------- | | `200` | リクエスト処理済み。ボディは TMP レスポンス(成功、空の結果、または TMP エラー)を含む。 | | `400` | 不正な形式のリクエスト(無効な JSON、欠けている `type` フィールド)。TMP レスポンスではない — パースするボディなし。 | | `503` | プロバイダー一時的に利用不可。ルーターはリトライまたはスキップすべき。 | これは、空の `offers` 配列を持つ `200` が有効な「一致なし」レスポンスで、TMP エラーボディ(`"type": "error"`)を持つ `200` が有効なアプリケーションエラーであることを意味します。ルーターは結果にかかわらず 1 つのレスポンス形式を扱います。 ## Latency * TMP は 50ms 未満のエンドツーエンドレイテンシー(publisher → router → agents → router → publisher)をターゲットにする。 * ルーターは、観測されたレイテンシーパーセンタイルに基づいてエージェントごとの適応的タイムアウトを適用すべき(SHOULD)。 * タイムアウトを超えるエージェントは、そのリクエストのマージされたレスポンスから除外される。 * ルーターは、p95 レイテンシーが一貫して予算を超えるエージェントを先制的にスキップしてもよい(MAY)。 ## Caching Context Match レスポンスは、同じパッケージが特定のプレースメントのすべてのユーザーについて評価されるためキャッシュ可能です。推奨キャッシュキーは `{property_rid, placement_id, provider_id}`。 * ルーターは Context Match レスポンスを **5 分** の TTL でキャッシュすべき(SHOULD)。 * プロバイダーは Context Match レスポンスに `cache_ttl` フィールド(integer、秒)を含めてデフォルトを上書きしてもよい(MAY)。ルーターは存在するときこの値を尊重しなければならない(MUST)。 * Identity Match レスポンスは `serve_window_sec`(パッケージごとのシングルショット fcap、最大 300s、デフォルト 60s)で束縛される。ルーターは `{identities_hash, provider_id, package_ids_hash, consent_hash, sealed_credentials_hash}` でキー付けされた内部重複排除キャッシュを適用してもよい(MAY)。ここで `identities_hash` は [Identity Match signed fields](#identity-match-signed-fields) で定義された正準 `identities` バイトの SHA-256(プロバイダーごとにフィルターされたサブセットに対して計算)。`package_ids_hash` はソートされた `package_ids` 配列の JCS シリアライゼーションに対する SHA-256。`consent_hash` はリクエストの `consent` オブジェクトの JCS シリアライゼーション(フィールドが欠如のとき JCS `null` — これは「同意不明」を明示的に空の consent オブジェクトと区別する)に対する SHA-256。`sealed_credentials_hash`(実験的、`trusted_match.verified_identity`)は [Identity Match signed fields](#identity-match-signed-fields) で定義された正準 `sealed_credentials` バイトの SHA-256、または欠如のとき `null` — そのため適格性をシフトさせるネットワーク認証情報の変更が、古いレスポンスを提供するのではなくキャッシュを再分割する。JCS フレーミングがデリミタ注入を防ぐ: `|`、`,`、`\n` を含む生の consent 文字列やパッケージ ID が 2 つの distinct な入力を衝突させられない。アイデンティティセットを含めることは、トークンの追加や削除が distinct なキャッシュエントリを生成することを保証する。パッケージリストハッシュを含めることは、アクティブなパッケージセットが変わるとき(例: 新しいメディアバイがアクティベート)キャッシュされたレスポンスが無効化されることを保証する。consent ハッシュを含めることは、ある consent 状態の下で取られた適格性決定が別の下で提供されるのを防ぐ。パブリッシャーの拘束コントラクトは、ルーターの内部キャッシュウィンドウではなくサーブウィンドウスロットル。 * プロバイダーのターゲティング設定が変わるとき(新しいパッケージ、更新されたターゲティングルール)、プロバイダーは変更が伝播するまで `"cache_ttl": 0`(Context Match)または `"serve_window_sec": 1`(Identity Match)を返し、その後通常値を再開すべき(SHOULD)。 * `cache_ttl`(Context Match)はスキーマ強制の最大 86400 秒を持つ。`serve_window_sec` は 300 秒に束縛される — より長いウィンドウはパッケージごとの fcap を典型的なキャンペーンに粗すぎにし、IdentityMatch 往復より短いものはスロットルを無駄にする。 ## Conformance Levels ### TMP Buyer Agent (Basic) * `context_match` ケイパビリティをサポート * ContextMatchRequest に有効な ContextMatchResponse で応答 * レイテンシー予算を満たす(エージェント側処理で p95 \< 30ms) * プライバシー制約を尊重する(他のソースからのアイデンティティデータでリクエストをログまたは相関しない) ### TMP Buyer Agent (Full) Basic のすべて、加えて: * `identity_match` ケイパビリティをサポート * IdentityMatchRequest に有効な IdentityMatchResponse(適格なパッケージ ID + TTL)で応答 * プロダクトが一致する `response_types` を宣言するときリッチなオファー(ブランド、価格、summary、クリエイティブマニフェスト)をサポート ### TMP Router (Basic) * プロバイダーエンドポイントで設定 * Context Match と Identity Match を認可されたプロバイダーにファンアウト * レスポンスをマージ * エンドツーエンドレイテンシー予算を満たす(p95 \< 50ms) ### TMP Router (Trusted) Basic のすべて、加えて: * TEE アテスト済み環境(例: AWS Nitro Enclaves)で実行 * リクエストに応じてアテステーションドキュメントを提供し、デプロイされたバイナリが公開されたソースに一致することを証明 * コンテキストとアイデンティティのコードパス間の構造的分離がアテステーション測定経由で検証可能 # AI アシスタント向け TMP Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/surfaces/ai-assistants TMP が従来のアドサーバーなしに AI チャットとアシスタントのサーフェスの収益化をどう可能にするか。 # AI アシスタント向け TMP AI アシスタントは根本的に新しい広告サーフェスを表します。従来の意味でのインプレッションはありません — スポンサーコンテンツは会話レスポンスに織り込まれます。アドサーバーはありません — プラットフォームの言語モデルがレスポンスを生成します。そしてバイヤーに「この会話で何がスポンサーされるべきか?」を尋ねる標準プロトコルはありません。 TMP がそのプロトコルを提供します。 ## 今日どう機能するか 会話を収益化するほとんどの AI プラットフォームは次のいずれかです: * 単一のアドネットワークと提携しすべての収益化決定を委譲する * 特定の広告主に結びついた独自のスポンサーシップロジックを構築する * 会話をまったく収益化しない AI プラットフォームが、どのバイヤーエージェントが関連するパッケージを持つかを発見し、特定の会話コンテキストで何をアクティベートするかを彼らに尋ね、彼らの好みをレスポンスに組み込む — すべてユーザープライバシーを保護しながら — 標準的な方法はありません。 ## Context Match ユーザーが会話でメッセージを送るとき、AI プラットフォームはレスポンスを生成する前に Context Match リクエストを送ります: ```json theme={null} { "type": "context_match_request", "request_id": "ctx-a1b2c3d4", "property_rid": "01916f3a-f8cb-7000-8000-000000000050", "property_id": "chatplatform-assistant", "property_type": "ai_assistant", "placement_id": "chat-inline-recommendation", "seller_agent_url": "https://chatplatform.example", "artifact_refs": [ { "type": "custom", "value": "turn:b3c9e2" } ], "context_signals": { "topics": ["479", "483", "592"], "taxonomy_source": "iab", "taxonomy_id": 7, "sentiment": "positive", "keywords": ["sneakers", "running", "recommendations"], "language": "en", "content_policies": ["csbs"], "summary": "User asking for sneaker recommendations for running and casual wear" }, "geo": { "country": "US" } } ``` 会話ターンは一時的です — バイヤーが独立して解決できる公開参照はないため、`artifact_refs` は通常不透明なターン識別子(例: `turn:b3c9e2`)に限られます。プラットフォームは、バイヤーが生の会話を見ずに関連性を評価できるよう、事前計算された分類器出力(トピック、センチメント、キーワード、summary)を伴う `context_signals` を送ります。ユーザーアイデンティティは存在しません。プラットフォームは、コンテンツを直接評価するバイヤー向けに完全な会話を `artifact` として送ることもできます — コンテンツ標準評価に使われるのと同じアーティファクトスキーマ。 バイヤーエージェントはオファーで応答します: ```json theme={null} { "type": "context_match_response", "request_id": "ctx-a1b2c3d4", "offers": [ { "package_id": "pkg-sneaker-reco", "brand": { "domain": "apexathletics.example.com", "brand_id": "apex_runners" }, "price": { "amount": 12.50, "currency": "USD", "model": "cpm" }, "summary": "Apex Classic Low + Runner X — free shipping this month", "creative_manifest": { "format_id": { "agent_url": "https://chatplatform.example.com", "id": "sponsored_recommendation" }, "assets": { "headline": { "content": "Trending for spring" }, "body": { "content": "Apex Classic Low — clean lines, tons of colorways. Free shipping this month." }, "product_catalog": { "catalog_id": "catalog-sneakers", "ids": ["sku-classic-low", "sku-runner-x"] } } }, "macros": { "click_url": "https://apexathletics.example.com/classic-low?utm_source=chatplatform" } } ], "signals": { "segments": ["sneaker_enthusiast"], "targeting_kvs": [{ "key": "product_affinity", "value": "sneakers" }] } } ``` バイヤーのオファーは `package_id`(必須)と任意フィールド: `brand`、`price`、`summary`、`creative_manifest`、`macros` を含みます。AI アシスタントについては、クリエイティブマニフェストはリアルタイムパスでインラインで送れるほど小さいです。マニフェストは、プラットフォームが会話に織り込めるテキストと参照するカタログアイテムを運びます。`summary` は、スポンサーコンテンツを組み込むかを決める前にプラットフォームが関連性を判断するのを助けます。 ## Identity Match プラットフォームはセッショントークンとプラットフォームの `seller_agent_url` を伴う Identity Match リクエストを送ります。バイヤーは `seller_agent_url` からアクティブなパッケージセットを解決します。プラットフォームが(下記のように)`package_ids` を明示的に送るとき、構成は現在のページと独立でなければなりません(MUST) — all-active(プラットフォームでのこのバイヤーのすべてのアクティブパッケージ)または fuzzed(バイヤーが黙って落とす合成の存在しない ID でパディングされたランダムサンプル)のいずれか。ページ固有のサブセットは禁止されています — それはバイヤーがパッケージセットを比較してこのリクエストを context match と相関させることを許します: ```json theme={null} { "type": "identity_match_request", "request_id": "id-e5f6g7h8", "seller_agent_url": "https://ai-assistant.example", "identities": [ { "user_token": "tok_session_k2f8", "uid_type": "publisher_first_party" } ], "package_ids": ["pkg-sneaker-reco", "pkg-fashion-native", "pkg-athletic-wear", "pkg-outdoor-gear", "pkg-accessories-promo", "pkg-seasonal-sale"] } ``` バイヤーは適格なパッケージの ID と TTL で応答します。バイヤーはフリークエンシーキャップ、オーディエンスメンバーシップ、その他のシグナルから適格性を計算します — 理由はパブリッシャーにとって不透明です。 ```json theme={null} { "type": "identity_match_response", "request_id": "id-e5f6g7h8", "eligible_package_ids": [ "pkg-sneaker-reco", "pkg-athletic-wear", "pkg-outdoor-gear", "pkg-seasonal-sale" ], "serve_window_sec": 120 } ``` プラットフォームはこれらの結果をローカルで交差させます: context match オファーと `eligible_package_ids` リストの両方に現れたパッケージのみがアクティベートされます。 ## アクティベーション AI プラットフォームは TMP 結果をそのレスポンス生成に組み込みます: * オファーのクリエイティブマニフェスト(ヘッドライン、ボディテキスト、カタログアイテム)が言語モデルに利用可能なコンテキストの一部になる。マニフェストはオファーにインライン — 別途フェッチ不要。 * プラットフォーム自身の関連性モデルが、会話フローと編集ポリシーに応じて、スポンサーコンテンツをどう統合するか **how** — 直接のおすすめ、控えめなメンション、別個のスポンサーカードとして — を決める。 * 不適格なパッケージ(Identity Match から)は生成コンテキストから除外される。 * オファー `summary` は、スポンサーコンテンツが会話に適合するかをプラットフォームの関連性モデルが決めるのを助ける。 TMP はプラットフォームにどのスポンサーコンテンツが利用可能で関連性があるか **what** を伝えます。プラットフォームはそれをどう提示するか **how** を決めます。これは正しい関心の分離です — バイヤーは自身のキャンペーンを知り、プラットフォームは自身のユーザー体験を知ります。 ## なぜこれが重要か AI アシスタントは、web とモバイルが数十年かけて構築したインフラを欠く新しい広告サーフェスです。TMP は提供します: * **標準バイヤー統合**: TMP を話す任意のバイヤーエージェントが、TMP をサポートする任意の AI プラットフォームでパッケージをアクティベートできる。プラットフォームごとのあつらえ統合なし。 * **デフォルトでプライバシー**: 会話コンテンツは生のテキストとしてプラットフォームを決して離れない。バイヤーは分類されたシグナルとトピック ID を見る。ユーザーのアイデンティティは別のリクエストで扱われる。 * **プラットフォームの編集制御**: プラットフォームがスポンサーコンテンツを会話にどう織り込むかを決める。TMP は入力を提供し、プラットフォームが体験を制御する。 * **マルチバイヤーサポート**: プラットフォームは複数のバイヤーエージェントからのパッケージを同時にアクティブにできる。TMP ルーターがファンアウトを扱う。プラットフォームが選択を扱う。 ## フロー例 ``` User message: "What are the best sneakers for spring?" → Platform classifies: topic=shopping.fashion.sneakers, sentiment=positive → Platform sends Context Match to TMP Router → Router fans out to buyer agents → Apex Athletics agent: offer for Classic Low + Runner X, free shipping, inline creative manifest → Spring Retailer agent: no offers (context doesn't match fashion-native targeting) → Router returns merged response → (300ms later) Platform sends Identity Match with ALL buyer's active packages → Response: eligible_package_ids includes pkg-sneaker-reco, serve_window_sec: 120 → Router caches eligibility → Platform joins: pkg-sneaker-reco offer is eligible → Platform includes offer's creative manifest in generation context → Language model generates response: "Great question! The Apex Classic Low is trending for spring — clean lines, tons of colorways, and they're offering free shipping right now. The Runner X is also a solid pick if you want more cushion..." → Sponsored content label applied per platform policy ``` ## 課金と測定 **インプレッションの定義。** インプレッションは、プラットフォームの LLM がクリエイティブマニフェストをユーザーへのレスポンスに組み込むときに起こります。これは web のビューアブルインプレッションに類似します — コンテンツがレンダリングされ提示されました。 **エンゲージメントイベント。** スポンサープロダクトについてのフォローアップ質問(「どこで買える?」「どんな色がある?」)はエンゲージメントイベントです。プラットフォームはこれらを追跡し `get_media_buy_delivery` 経由でレポートします。 **クリックスルー。** レスポンスがプロダクト URL を含みユーザーがそれにナビゲートする場合、これはクリックイベントです。プラットフォームはクリエイティブマニフェストのアセットからの URL を使ってクリックスルーを追跡します。 **課金モデル。** ほとんどの AI アシスタントパッケージは CPM(cost per thousand impressions)または CPA(cost per action)を使います。プラットフォームは他のサーフェスと同様に `get_media_buy_delivery` 経由で配信をレポートします。 **測定の課題。** ビューアビリティが標準化されている(MRC)web とは異なり、AI アシスタントインプレッションはまだ業界標準のビューアビリティ定義を持ちません。AdCP はインプレッションを「LLM のレスポンスでユーザーに提示されたクリエイティブマニフェストコンテンツ」と定義します。 **フリークエンシーカウント。** 各インプレッションはパッケージのクロスパブリッシャーフリークエンシーキャップにカウントされます。プラットフォームは配信レポート経由でインプレッションをレポートし、バイヤーエージェントはその露出ストアを更新します。AI アシスタントでおすすめを見てから web ページを訪れるユーザーは、バイヤーの露出ストアが最新である限り、その AI インプレッションが Identity Match 適格性チェックに反映されます。 # CTV 向け TMP Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/surfaces/ctv TMP がクリエイティブバリアント選択で CTV ポッド構成のためにパッケージをどうアクティベートするか。 # CTV 向け TMP コネクテッド TV アプリは広告ポッド — ストリーミングコンテンツ中のコマーシャルブレイクを埋める広告のシーケンス — を構成します。ポッド構成は、競合分離、フリークエンシー制限、duration 制約を尊重しながら複数のパッケージを同時にアクティベートすることを意味します。TMP はパッケージアクティベーションとアイデンティティ適格性を扱い、放送局のアドサーバーがポッド組み立てを扱います。 ## 今日どう機能するか 放送局は、そのアドサーバー(FreeWheel、Google Ad Manager、SpringServe)を通じて CTV ディールを管理します。各ディールはターゲティングルール、競合分離制約、クリエイティブローテーションロジックで設定されます。ポッド構成はアドサーバーのポッド最適化エンジンによって扱われます。バイヤーエージェントが、どのパッケージをアクティベートするか、特定のポッドにどのクリエイティブバリアントを優先するかについてリアルタイム入力を提供する標準的な方法はありません。 ## 4 つのメッセージ CTV 広告ブレイクは 4 つの TMP メッセージを含みます: context match リクエストとレスポンス(何のコンテンツが再生中か、どのパッケージが一致するか)、次に identity match リクエストとレスポンス(この世帯は適格か)。パブリッシャーは結果をローカルで結合してポッドを構成します。 ### Context Match Request ポッドブレイクが近づくと、放送局は context match リクエストを送ります。`placement_id` は広告ブレイク位置(例: `pre_roll`、`mid_roll_1`、`pod_break_2`)を識別します。`artifact_refs` は番組とエピソードを参照し、バイヤーエージェントがコンテンツレベルのターゲティングに使います。パッケージリストは送られません — プロバイダーはこのプレースメントの同期されたパッケージセットを使います。 ```json theme={null} { "type": "context_match_request", "request_id": "ctx-9f3a-e7b2", "property_rid": "01916f3a-a1d3-7000-8000-000000000020", "property_id": "riverview-streaming", "property_type": "ctv_app", "placement_id": "mid_roll_1", "seller_agent_url": "https://riverview.example", "artifact_refs": [ { "type": "gracenote", "value": "SH032541890000" }, { "type": "eidr", "value": "10.5240/B1A2-C3D4-E5F6-7890-1234-X" } ] } ``` 要点: * **アーティファクトは業界 ID で番組とエピソードを参照する。** バイヤーエージェントは、番組(「The Night Kitchen」は料理ドラマ、食品ブランドに好適合)をその Gracenote ID 経由で、特定のエピソードをその EIDR 経由で、または両方でマッチできる。 * **リクエストごとにパッケージリストは送られない。** プロバイダーはメディアバイセットアップからの同期されたパッケージセットを使って、このプレースメントのすべての適格なパッケージを評価する。同じパッケージがすべての世帯について評価される — 世帯によるフィルタリングは identity match で起こる。 * **クリエイティブサポートはプロダクトの `trusted_match` 設定で宣言される。** プロダクトの設定がクリエイティブレスポンスタイプを含むとき、バイヤーエージェントはクリエイティブマニフェストを返し、放送局が直接レンダリングできる。 ### Context Match Response 各バイヤーエージェントはコンテンツコンテキストを評価し、アクティベートしたいパッケージのオファーで応答します。ルーターはすべてのレスポンスをマージします。ここでは 2 つのバイヤーがアクティベートしました: ```json theme={null} { "type": "context_match_response", "request_id": "ctx-9f3a-e7b2", "offers": [ { "package_id": "pkg-sparklean-30s", "brand": { "domain": "sparklean.example.com" }, "summary": "Kitchen cleaning product — contextual fit with cooking drama", "creative_manifest": { "format_id": { "agent_url": "https://riverview.example.com", "id": "video_30s" }, "assets": { "video": { "delivery_type": "url", "url": "https://creatives.sparklean.example/vast/kitchen-30s.xml" } } } }, { "package_id": "pkg-greenleaf-15s", "summary": "Organic grocery — recipe content alignment", "creative_manifest": { "format_id": { "agent_url": "https://riverview.example.com", "id": "video_15s" }, "assets": { "video": { "delivery_type": "url", "url": "https://creatives.greenleaf.example/vast/spring-15s.xml" } } } } ] } ``` Vaultline(金融サービス)と Driftmoto(バイクブランド)は料理ドラマのコンテキストに一致せず、オファーから欠けています。 CTV クリエイティブについては、`creative_manifest` は通常クリエイティブをインラインで含むのではなく VAST URL を参照します。動画アセットは大きく、マニフェストは外部アセットを指し、放送局のアドサーバーがレンダリング時にそれをフェッチします。 ### Identity Match Request 別途、放送局は世帯トークンと放送局の `seller_agent_url` を伴う identity match リクエストを送ります。バイヤーは `seller_agent_url` からアクティブなパッケージセットを解決します。放送局が(下記のように)`package_ids` を明示的に送るとき、構成は現在のポッドブレイクと独立でなければなりません(MUST) — all-active(この放送局でのそのバイヤーのすべてのアクティブパッケージ)または fuzzed(バイヤーが黙って落とす合成の存在しない ID でパディングされたランダムサンプル)のいずれか。ポッドブレイク固有のサブセットは禁止されています — それはバイヤーがパッケージセットを比較して identity リクエストを特定の context リクエストと相関させることを許します。 ```json theme={null} { "type": "identity_match_request", "request_id": "id-7k2m-p4w1", "seller_agent_url": "https://broadcaster.example", "identities": [ { "user_token": "tok_household_q7w2", "uid_type": "publisher_first_party" }, { "user_token": "ID5*mN4pQ...", "uid_type": "id5" } ], "consent": { "us_privacy": "1YNN" }, "package_ids": [ "pkg-sparklean-30s", "pkg-sparklean-display-web", "pkg-sparklean-native-mobile", "pkg-greenleaf-15s", "pkg-greenleaf-display-web", "pkg-vaultline-30s", "pkg-vaultline-audio", "pkg-driftmoto-15s", "pkg-driftmoto-30s" ] } ``` リストは他のサーフェス(web ディスプレイ、モバイルネイティブ、音声)からのパッケージを含みます。例は all-active モードを使います — 放送局はバイヤーごとのすべてのアクティブパッケージのキャッシュされたリストを維持し、毎回完全なセットを送ります。fuzzed モード(合成 ID でパディングされたランダムサンプル)は、そのキャッシュを維持したくない放送局のための等価なプライバシー保証です。 ### Identity Match Response 各バイヤーエージェントは、世帯トークンを自身のデータ(フリークエンシーキャップ、オーディエンスメンバーシップ、購入履歴)に対して評価し、適格なパッケージの ID と TTL を返します。バイヤーは理由を開示しません — パブリッシャーは世帯が資格を満たすかどうかだけを知る必要があります。 ```json theme={null} { "type": "identity_match_response", "request_id": "id-7k2m-p4w1", "eligible_package_ids": [ "pkg-sparklean-30s", "pkg-sparklean-display-web", "pkg-greenleaf-15s", "pkg-greenleaf-display-web", "pkg-vaultline-30s", "pkg-vaultline-audio", "pkg-driftmoto-30s" ], "serve_window_sec": 90 } ``` レスポンスは CTV のものだけでなくすべてのパッケージをカバーします。`serve_window_sec: 90` は広告ブレイクの duration をカバーします — ルーターは再クエリせずにキャッシュされた適格性を使ってすべてのポッドスロットを埋めます。パブリッシャーは現在のポッドに関連するパッケージ ID のみを抽出します。 ## ポッド構成 放送局は今や 2 セットの結果を持ち、ポッドをローカルで構成します: 1. **コンテキストアクティベーションでフィルター。** context match からのオファーを持つパッケージのみが候補: Sparklean(30s)と Greenleaf(15s)。Vaultline と Driftmoto はアクティベートしなかった。 2. **アイデンティティ適格性でフィルター。** コンテキストアクティベートされたパッケージのうち、世帯適格性を確認: Sparklean は適格、Greenleaf は適格。両方通過。 3. **適格なオファーをランク付け。** アドサーバー自身の優先度とペーシングルールを使って適格なオファーをランク付け。 4. **競合分離を適用。** 放送局のアドサーバーが競合分離ルールを強制 — 同じ広告主カテゴリーの 2 つのブランドは同じポッドに現れられない。Sparklean(クリーニング)と Greenleaf(食料品)は異なるカテゴリーなので衝突なし。 5. **ポッドを組み立てる。** 利用可能な duration を埋める。典型的なミッドロールポッドは 60 秒かもしれません: * Slot 1(30s): Sparklean — context match、世帯適格 * Slot 2(15s): Greenleaf — context match、世帯適格 * 残り 15s: 他の需要ソース(プログラマティック、ハウス広告)で埋める 競合分離はパブリッシャーの責任です。TMP はアクティベーションと適格性のシグナルを提供し、放送局のアドサーバーがどのブランドが一緒に現れられるかについてのビジネスルールを適用します。これはプロトコルをシンプルに保ち、カテゴリータクソノミーを TMP メッセージにエンコードすることを避けます。 ## SSAI 統合 サーバーサイド広告挿入(SSAI)は支配的な CTV 配信モデルです。TMP ルーターは SSAI エンジンと並んでサーバーサイドで実行され、アクティベーションフロー全体をクライアントデバイスから外します。 * **フロー。** SSAI エンジンはコンテンツストリームからポッドブレイクシグナルを受け取り、コンテキストとアイデンティティのマッチについて TMP ルーターにクエリし、オファーを受け取り、配信前に VAST クリエイティブをストリームにステッチする。 * **クリエイティブ配信。** Context Match レスポンスの `creative_manifest` は、SSAI エンジンが直接フェッチしスプライスできる VAST URL を含む。クライアント側の広告ロードは不要。 * **レイテンシー予算。** SSAI エンジンの全体的な広告挿入予算は通常 200-500ms(クライアント側挿入より大きい)。ステッチがストリーム配信前に起こるため。TMP ルーターは依然としてその部分に 50ms 未満をターゲットにする。追加の予算は SSAI エンジンに VAST フェッチとストリームステッチの時間を与える。 * **コンパニオン広告。** VAST レスポンスがコンパニオンクリエイティブを含む場合、SSAI エンジンはそれらを CTV アプリのディスプレイ層に渡して動画コンテンツと並べてレンダリングできる。 ## 番組とエピソードのアーティファクト Refs CTV アーティファクトは通常、業界標準の識別子を使って番組と特定のエピソードの両方を参照します: ```json theme={null} "artifact_refs": [ { "type": "gracenote", "value": "SH032541890000" }, { "type": "eidr", "value": "10.5240/B1A2-C3D4-E5F6-7890-1234-X" } ] ``` EIDR(Entertainment Identifier Registry)は番組、シーズン、エピソードのグローバルに一意な ID を提供します。Gracenote TMS ID も等しく有効です。鍵となる要件: 識別子は、バイヤーが独立してメタデータをルックアップできるよう公開に解決可能でなければなりません。 バイヤーエージェントはこれらを異なる粒度で使えます: * **番組レベルのターゲティング。** 「The Night Kitchen の任意のエピソードでアクティベート」 — エージェントは Gracenote 番組 ID がそのターゲティングルールにあるかを確認する。 * **エピソードレベルのターゲティング。** 「シーズンプレミアでのみアクティベート」 — エージェントは特定の EIDR エピソード ID を確認する。 * **ジャンルまたはトピックのターゲティング。** エージェントはキャッシュされたアーティファクトデータからジャンルとトピックのメタデータを解決する。これは、特定の番組 ID をターゲティングルールにハードコードせずに広範なカテゴリーターゲティングに機能する。 ## フロー例 ``` Mid-roll break in "The Night Kitchen" S02E07 Context Match --> Broadcaster sends request: show + episode artifacts, placement context --> Sparklean agent: activate pkg-sparklean-30s (VAST creative, cooking context fit) --> Greenleaf agent: activate pkg-greenleaf-15s (VAST creative, food content) --> Vaultline agent: no activation (financial services, no context fit) --> Driftmoto agent: no activation (motorcycle brand, no context fit) Identity Match (after temporal decorrelation — random delay + random order) --> Broadcaster sends request: all 9 active packages across all buyers --> Response: eligible_package_ids includes Sparklean, Greenleaf, Vaultline, Driftmoto-30s --> serve_window_sec: 90 (covers the ad break) Pod Assembly (broadcaster's ad server) --> Join: Sparklean and Greenleaf both activated and eligible --> Competitive separation: different categories, no conflict --> Pod: Sparklean 30s + Greenleaf 15s + 15s backfill --> Fetch VAST creatives from manifest URLs --> Serve pod during commercial break ``` # モバイルアプリ向け TMP Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/surfaces/mobile TMP がモバイルメディエーション SDK と統合し、プログラマティック需要と並んで事前交渉された AdCP パッケージをどうアクティベートするか。 # モバイルアプリ向け TMP モバイルアプリは、各インプレッションをどのアドネットワークが提供するかを選択するためにメディエーション層を使います。メディエーターは需要ソース — アドネットワーク、入札パートナー、直接ディール — を評価し勝者を選びます。TMP はこのモデル内の追加需要ソースとして統合します。メディエーターのオークションまたはウォーターフォールで既存のネットワーク需要と並んで競う、事前交渉された AdCP パッケージをアクティベートします。 ## 今日どう機能するか モバイルパブリッシャーは、アドネットワーク、ウォーターフォール優先度またはインアプリ入札ルール、プレースメント定義でメディエーション SDK を設定します。広告機会が生じると、メディエーターは期待収益に基づいて最良のソースを選択します。AdCP パッケージはこの決定へのパスを持ちません — AdCP システムに存在しますが、メディエーション層はそれらを評価する方法を持ちません。 TMP はこのギャップを橋渡しします。パブリッシャーのアプリは、広告機会が生じると TMP ルーターを呼ぶ TMP SDK を含みます。アクティベートされたパッケージは、事前交渉された CPM を伴うカスタム需要ソースとしてメディエーション層に渡されます。メディエーターはその標準の選択ロジックを使ってそれらをネットワーク入札と並んで評価します。 ## 統合モデル TMP SDK はアプリとメディエーション層の間に位置します。メディエーターを置き換えません — パッケージをそれに供給します。 ``` Ad opportunity -> TMP SDK sends Context Match (content signals, placement context) -> TMP SDK sends Identity Match (device token, all buyer packages) -> TMP SDK joins results locally -> Eligible, activated packages passed to mediator as demand sources -> Mediator runs its auction / waterfall as usual -> Winner serves ``` 2 つの統合パターンがほとんどのモバイル広告フォーマットをカバーします: * **アクティベーション**: バイヤーが ID でパッケージをアクティベートする。TMP SDK はパッケージをその事前交渉された CPM とともにメディエーターに渡す。メディエーターはその標準のレンダリングパスを通じてクリエイティブをフェッチする。これはメディエーションを通じて提供されるインタースティシャル、リワード動画、バナーの典型的なパターン。プロダクトの `trusted_match` 設定がアクティベーションをサポートされるレスポンスタイプとして宣言する。 * **クリエイティブ**: バイヤーが完全なクリエイティブマニフェストをインラインで返す。アプリはメディエーターを通さずに広告を直接レンダリングする。これはアプリがレンダリングを制御するネイティブインフィード広告の典型的なパターン。プロダクトの `trusted_match` 設定がクリエイティブをサポートされるレスポンスタイプとして宣言する。 ## Context Match 広告機会が生じると、TMP SDK は Context Match リクエストをルーターに送ります。リクエストはプレースメントのコンテンツコンテキストを記述します。 ### インタースティシャルの例 フィットネスアプリがワークアウト完了後にインタースティシャルをトリガーします: #### Request ```json theme={null} { "type": "context_match_request", "request_id": "ctx-mob-7f3a91", "property_rid": "01916f3a-b4e7-7000-8000-000000000030", "property_id": "pulsefit-ios", "property_type": "mobile_app", "placement_id": "interstitial_main", "seller_agent_url": "https://pulsefit.example", "artifact_refs": [ { "type": "custom", "value": "screen:workout-complete" }, { "type": "custom", "value": "screen:workout-summary" } ] } ``` リクエストごとにパッケージリストは送られません。プロバイダーはメディアバイセットアップからの同期されたパッケージセットを使って、`interstitial_main` プレースメントのすべての適格なパッケージを評価します。同じパッケージがすべてのユーザーについて評価されます — ユーザーによるフィルタリングはアイデンティティをコンテキストパスに漏らします。 #### Response ```json theme={null} { "type": "context_match_response", "request_id": "ctx-mob-7f3a91", "offers": [ { "package_id": "pkg-sports-inter-01" }, { "package_id": "pkg-nutrition-inter-02", "summary": "Post-workout recovery shake promo" } ] } ``` 2 つのパッケージがアクティベートしました。スポーツギアと栄養のパッケージがフィットネスコンテンツコンテキストに一致します。テレコムと自動車のパッケージは一致せずオファーリストから欠けています。プロダクトの `trusted_match` 設定がアクティベーションをレスポンスタイプとして宣言するとき、オファーは `package_id`(と任意の summary)のみを運びます。メディエーターがクリエイティブフェッチを扱います。 ### ネイティブフィードの例 レシピアプリがそのレシピフィードにスポンサーコンテンツカードを表示します: #### Request ```json theme={null} { "type": "context_match_request", "request_id": "ctx-mob-b2c419", "property_rid": "01916f3a-c5f8-7000-8000-000000000031", "property_id": "tastecraft-android", "property_type": "mobile_app", "placement_id": "banner_feed", "seller_agent_url": "https://tastecraft.example", "artifact_refs": [ { "type": "url", "value": "https://tastecraft.example.com/weeknight-pasta-recipes" } ] } ``` #### Response ```json theme={null} { "type": "context_match_response", "request_id": "ctx-mob-b2c419", "offers": [ { "package_id": "pkg-grocery-native-01", "brand": { "domain": "freshmart.example.com" }, "price": { "amount": 6.50, "currency": "USD", "model": "cpm" }, "summary": "Pasta night ingredients — 20% off with in-app coupon", "creative_manifest": { "format_id": { "agent_url": "https://tastecraft.example.com", "id": "native_card" }, "assets": { "headline": { "content": "Everything for pasta night" }, "body": { "content": "Fresh basil, San Marzano tomatoes, and artisan pasta. 20% off your next order." }, "image": { "url": "https://cdn.freshmart.example/campaigns/pasta-night-card.jpg", "width": 1200, "height": 628 }, "cta": { "content": "Shop Now" } } }, "macros": { "campaign_ref": "fm-pasta-2026q2", "promo_code": "PASTA20" } }, { "package_id": "pkg-kitchenware-native-02", "brand": { "domain": "ironpan.example.com", "brand_id": "ironpan" }, "summary": "Cast iron skillet — pairs with pasta recipes", "creative_manifest": { "format_id": { "agent_url": "https://tastecraft.example.com", "id": "native_card" }, "assets": { "headline": { "content": "The only pan you need" }, "body": { "content": "Pre-seasoned 12-inch cast iron. Free shipping this week." }, "image": { "url": "https://cdn.ironpan.example/campaigns/skillet-card.jpg", "width": 1200, "height": 628 }, "cta": { "content": "Learn More" } } } } ] } ``` プロダクトの `trusted_match` 設定がクリエイティブをレスポンスタイプとして宣言するとき、バイヤーは完全なクリエイティブ詳細をインラインで返します。アプリは別途クリエイティブフェッチなしにネイティブカードをレンダリングするのに必要なすべてを持ちます。ミールキットパッケージ(`pkg-meal-native-03`)は一致せずレスポンスから欠けています。 ## Identity Match TMP SDK は、パブリッシャースコープのデバイストークンとパブリッシャーの `seller_agent_url` を伴う Identity Match リクエストを送ります。このリクエストは Context Match から構造的に分離されています — コンテンツシグナルを運ばず、時間的相関除去とともに送られます: 100-2000ms のランダムな遅延、加えてランダム化された順序(各機会は Context Match または Identity Match が先に送られるほぼ等しい確率を持つ)。 バイヤーは `seller_agent_url` からアクティブなパッケージセットを解決します。SDK が(下記のように)`package_ids` を明示的に送るとき、構成は現在のプレースメントと独立でなければなりません(MUST) — all-active(この publisher でのバイヤーのすべてのアクティブパッケージ)または fuzzed(バイヤーが黙って落とす合成の存在しない ID でパディングされたランダムサンプル)のいずれか。プレースメント固有のサブセットは禁止されています — それはバイヤーがパッケージセットを比較して Identity Match を Context Match と相関させることを許します。 #### Request ```json theme={null} { "type": "identity_match_request", "request_id": "id-mob-e4d782", "seller_agent_url": "https://mobile-publisher.example", "identities": [ { "user_token": "tok_idfv_a9c3e7", "uid_type": "publisher_first_party" }, { "user_token": "A1B2C3D4-E5F6-7890-1234-567890ABCDEF", "uid_type": "maid" } ], "consent": { "gpp": "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA" }, "package_ids": [ "pkg-sports-inter-01", "pkg-nutrition-inter-02", "pkg-telecom-inter-03", "pkg-auto-inter-04", "pkg-sports-banner-05", "pkg-nutrition-banner-06", "pkg-sports-rewarded-07" ] } ``` 7 つのパッケージ ID — 例は all-active モードを使います(Context Match によってアクティベートされた 2 つだけでなく、すべてのプレースメントとフォーマットにわたるこのバイヤーのすべてのアクティブパッケージ)。バイヤーが黙って落とす合成の存在しない ID でパディングされた、同様のサイズの fuzzed リストは、同じプライバシー不変条件を満たします。 #### Response ```json theme={null} { "type": "identity_match_response", "request_id": "id-mob-e4d782", "eligible_package_ids": [ "pkg-sports-inter-01", "pkg-nutrition-inter-02", "pkg-auto-inter-04", "pkg-sports-banner-05", "pkg-sports-rewarded-07" ], "serve_window_sec": 60 } ``` 適格なパッケージのみがリストされます。バイヤーはフリークエンシーキャップ、オーディエンスメンバーシップ、購入履歴、その他のアイデンティティベースのシグナルから適格性を計算します。理由はパブリッシャーにとって不透明です。パブリッシャーは `pkg-telecom-inter-03` がなぜ不適格かを学びません — リストに欠如していることだけです。 `serve_window_sec` はルーターにこのレスポンスをどのくらいキャッシュするかを伝えます。TTL ウィンドウ中、ルーターはバイヤーに再クエリせずにキャッシュされた適格性を使ってインタースティシャル、バナー、リワード広告を埋めます。 ## 結合とアクティベーション TMP SDK は Context Match と Identity Match の結果をローカルで結合します。両方のレスポンスに現れるパッケージ — コンテキストによってアクティベートされ、アイデンティティによって適格 — のみがメディエーターに進みます。 ### インタースティシャルアクティベーション 上のインタースティシャルの例から: | Package | Context Match | Identity Match | Result | | ------------------------ | ------------- | -------------- | ---------------------- | | `pkg-sports-inter-01` | Activated | Eligible | メディエーターに渡す | | `pkg-nutrition-inter-02` | Activated | Eligible | メディエーターに渡す | | `pkg-telecom-inter-03` | Not activated | Ineligible | スキップ | | `pkg-auto-inter-04` | Not activated | Eligible | スキップ(context match なし) | 2 つのパッケージが通過: `pkg-sports-inter-01` と `pkg-nutrition-inter-02`。TMP SDK はそれらを事前交渉された CPM とともにメディエーション層にカスタム需要ソースとして登録します。 ``` Mediation auction: Network A bid: $6.50 CPM Network B bid: $5.20 CPM pkg-sports-inter-01: $8.00 CPM (pre-negotiated) pkg-nutrition-inter-02: $7.00 CPM (pre-negotiated) Winner: pkg-sports-inter-01 at $8.00 CPM -> Mediator serves the sports gear interstitial ``` AdCP パッケージが勝たない場合、メディエーターは通常どおりネットワーク広告を提供します。TMP は AdCP パッケージが考慮されたことを保証します — メディエーターの選択ロジックを上書きしません。 ### ネイティブフィードアクティベーション プロダクトの `trusted_match` 設定がクリエイティブをレスポンスタイプとして宣言するネイティブ広告については、アプリは Context Match レスポンスで返されたクリエイティブマニフェストから直接レンダリングします。メディエーターは関与しません。アプリは context match オファーを identity match の `eligible_package_ids` と交差させ、独自のランキングロジックを使って最良の適格オファーを選び、`creative_manifest` アセットを使ってネイティブカードをレンダリングします。 ## リワード動画 リワード動画はインタースティシャルと同じアクティベーションパターンに従います。プレースメントはリワードスロットを識別します: ```json theme={null} { "type": "context_match_request", "request_id": "ctx-mob-c8f201", "property_rid": "01916f3a-d6a9-7000-8000-000000000032", "property_id": "puzzlequest-ios", "property_type": "mobile_app", "placement_id": "rewarded_video", "seller_agent_url": "https://puzzlequest.example", "artifact_refs": [ { "type": "custom", "value": "screen:level-complete-42" } ] } ``` メディエーターはリワード動画完了コールバックとリワード付与を扱います。TMP はパッケージをアクティベートし、メディエーターはリワードライフサイクルを管理します。 ## プライバシー考慮事項 モバイル TMP はすべてのサーフェスと同じ構造的分離に従います: * **Context Match** はコンテンツシグナルとプレースメントデータを運ぶ。リクエストにデバイス識別子なし、ユーザートークンなし、IDFA/IDFV なし。 * **Identity Match** はパブリッシャースコープのデバイストークンとバイヤーパッケージ ID の完全なリストのみを運ぶ。コンテンツシグナルなし、画面名なし、トピック ID なし。 * **時間的相関除去** が 2 つのリクエスト間のタイミングと順序ベースの相関を防ぐ。TMP SDK はランダムな遅延(100-2000ms)を導入し **かつ** どちらのリクエストが先に送られるかをランダム化する — 各機会は Context Match または Identity Match が先に行くほぼ等しい確率を持つ。 * **パッケージセット相関除去**: Context Match はパッケージリストを送らない — プロバイダーはプレースメント上のすべてのユーザーについて同じ同期されたパッケージセットを評価する。Identity Match はバイヤーのすべてのパッケージを送る。どちらのパスも、どのパッケージが現在の機会に関連するかを明かさない。 パブリッシャーは両方のレスポンスが到着した後、交差をローカルで実行します。バイヤーは結合された結果を決して見ません。 # リテールメディア向け TMP Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/surfaces/retail-media TMP が GTIN レベルのカタログ絞り込みでスポンサープロダクトパッケージをどうアクティベートするか。 # リテールメディア向け TMP リテーラーは、検索結果、カテゴリーページ、カルーセルにわたってスポンサープロダクトプレースメントを管理します。TMP のカタログ絞り込みケイパビリティはこれを自然な適合にします — バイヤーは、どのプロダクトを特集するか、どのプロモーションを強調するか、どのアイテムを抑制するかを、すべて事前交渉されたパッケージの範囲内で指定できます。 ## 今日どう機能するか リテールメディアネットワークは、どのスポンサープロダクトが現れるかを決めるために内部のレコメンデーションエンジンを使います。バイヤーはキャンペーンレベルのターゲティング(キーワード、カテゴリー、予算)を設定しますが、どの特定のプロダクトがどのコンテキストに現れるかについてのリアルタイム制御は限定的です。各リテーラーは独自の API と最適化ロジックを持ちます。 ## Context Match 買い物客が検索結果ページやカテゴリーページを見るとき、リテーラーは Context Match リクエストを送ります: ```json theme={null} { "type": "context_match_request", "request_id": "ctx-retail-8f3a", "property_rid": "01916f3a-e7ba-7000-8000-000000000040", "property_id": "grocery-retailer-web", "property_type": "website", "placement_id": "search-results-sponsored", "seller_agent_url": "https://grocery-retailer.example", "artifact_refs": [ { "type": "custom", "value": "search:beverages-coffee" } ] } ``` バイヤーはオファーで応答します: ```json theme={null} { "type": "context_match_response", "request_id": "ctx-retail-8f3a", "offers": [ { "package_id": "pkg-coffee-sponsored", "brand": { "domain": "coldbrew.example.com", "brand_id": "coldbrew" }, "price": { "amount": 2.50, "currency": "USD", "model": "cpc" }, "summary": "Cold brew and iced latte — buy 2 get 1 free promotion", "creative_manifest": { "format_id": { "agent_url": "https://grocery-retailer.example.com", "id": "sponsored_product_listing" }, "assets": { "items": { "type": "product", "items": [ { "gtin": "gtin-cold-brew-12oz", "badge": "BOGO", "image_url": "https://cdn.example.com/cold-brew-12oz.jpg" }, { "gtin": "gtin-iced-latte-4pk", "badge": "PROMO", "image_url": "https://cdn.example.com/iced-latte-4pk.jpg" } ] }, "promo_banner": { "url": "https://cdn.example.com/banners/b2g1.png", "width": 728, "height": 90 } } }, "macros": { "click_tracker": "https://track.example.com/click?pkg=coffee-sponsored", "impression_tracker": "https://track.example.com/imp?pkg=coffee-sponsored" } } ] } ``` バイヤーのオファー summary はリテーラーが関連性を判断するのを助けます。クリエイティブマニフェストはオファーにインラインで含まれ、どのカタログアイテムを特集するか、プロモーションバッジ、レンダリングアセットを指定します。大きなクリエイティブについては、マニフェストは直接埋め込む代わりに URL 経由で外部アセットを参照します。 ## Identity Match リテーラーは買い物客のロイヤルティトークンとリテーラーの `seller_agent_url` を伴う Identity Match リクエストを送ります。バイヤーは `seller_agent_url` からアクティブなパッケージセットを解決します。リテーラーが(下記のように)`package_ids` を明示的に送るとき、構成は現在のページと独立でなければなりません(MUST) — all-active(リテーラーでのこのバイヤーのすべてのアクティブパッケージ)または fuzzed(バイヤーが黙って落とす合成の存在しない ID でパディングされたランダムサンプル)のいずれか。ページ固有のサブセットは禁止されています — それはバイヤーがパッケージセットを比較してこのリクエストを context match と相関させることを許します: ```json theme={null} { "type": "identity_match_request", "request_id": "id-retail-c7b2", "seller_agent_url": "https://retailer.example", "identities": [ { "user_token": "tok_loyalty_m3p7", "uid_type": "publisher_first_party" }, { "user_token": "a1b2c3d4e5f67890abcdef...", "uid_type": "hashed_email" } ], "package_ids": ["pkg-coffee-sponsored", "pkg-snacks-display", "pkg-dairy-promo", "pkg-bakery-seasonal", "pkg-frozen-meals", "pkg-household-q1"] } ``` バイヤーは適格なパッケージの ID と TTL で応答します: ```json theme={null} { "type": "identity_match_response", "request_id": "id-retail-c7b2", "eligible_package_ids": [ "pkg-coffee-sponsored", "pkg-snacks-display", "pkg-bakery-seasonal", "pkg-frozen-meals" ], "serve_window_sec": 60 } ``` パブリッシャーはユーザーが適格かどうかの理由を知る必要はありません — 適格かどうかだけです。表示するカタログアイテムは、Identity Match レスポンスではなく Context Match オファーのクリエイティブマニフェストから来ます。 ## アクティベーション リテーラーは両方のレスポンスを結合します: * コーヒースポンサーオファーを受け入れる * カタログアイテム、プロモーションバッジ、レンダリングアセットにオファーのインラインクリエイティブマニフェストを使う * Identity Match を確認: パッケージは `eligible_package_ids` にあるか? * リテーラー自身のレコメンデーションエンジンが、スポンサー結果をオーガニック結果と並べて統合する ## フロー例 ``` Shopper searches "cold brew" → Retailer sends Context Match: coffee sponsored package available → Buyer: offer with creative manifest (cold brew + iced latte items, promo banner, badges) → (fuzzed) Retailer sends Identity Match: loyalty token + all active package IDs → Buyer: eligible_package_ids includes pkg-coffee-sponsored, serve_window_sec: 60 → Retailer joins: accept offer, render items from creative manifest → Render sponsored carousel in search results ``` # Web パブリッシャー向け TMP Source: https://adcp-docs-ja.pier1.co.jp/docs/trusted-match/surfaces/web TMP が Prebid と GAM を使って web ページで事前交渉されたパッケージをどうアクティベートするか。 # Web パブリッシャー向け TMP Web パブリッシャーは通常、ラインアイテム、ターゲティングルール、広告選択を管理するアドサーバー(GAM、Kevel、FreeWheel)を実行します。TMP は Prebid モジュールを通じてこのインフラと統合します — どのディールをアクティベートするかをアドサーバーに伝え、アドサーバーを置き換えません。 ## 今日どう機能するか AdCP ディールで Prebid を実行するパブリッシャーは、`create_media_buy` を通じて定義されたパッケージを持ちます。それらをアクティベートするため、パブリッシャーはアドサーバーで対応するラインアイテムまたは PMP ディールを手動で作成します。ベンダー固有の RTD モジュールが、完全な OpenRTB BidRequest をベンダーの API に送ることでターゲティングシグナルを注入します。これは機能しますが、ベンダーごとの統合を要求し不要なデータを送ります。 TMP は、ベンダー固有の RTD モジュールを、標準プロトコルを話す単一の Prebid モジュールに置き換えます。バイヤーエージェントはコンテキストとアイデンティティを別々に評価し、パブリッシャーは GAM に指示を渡す前に結果をローカルで結合します。 ## Context Match ページがロードされると、TMP Prebid モジュールは Context Match リクエストをルーターに送ります。リクエストはページコンテキストを含みます。パッケージリストは送られません — プロバイダーはこのプレースメントの同期されたパッケージセットを使います。 ### Context Match Request ```json theme={null} { "type": "context_match_request", "request_id": "ctx-7f2a-oakwood-91b3", "property_rid": "01916f3a-9c4e-7000-8000-000000000010", "property_id": "oakwood-publishing-main", "property_type": "website", "placement_id": "article-sidebar-300x250", "seller_agent_url": "https://oakwood.example", "artifact_refs": [ { "type": "url", "value": "https://oakwood.example.com/sustainable-kitchen-2026-03" } ] } ``` 要点: * リクエストごとにパッケージリストは送られない。プロバイダーはメディアバイセットアップからの同期されたパッケージセットを使って、このプレースメントのすべての適格なパッケージを評価する。同じパッケージがすべてのユーザーについて評価され、アイデンティティのコンテキストパスへの漏洩を防ぎ、レスポンスキャッシュを可能にする。 * `artifact_refs` はコンテンツをタイプと値で参照する。バイヤーエージェントはアーティファクトメタデータをそのキャッシュから解決する — リクエストにインラインシグナルは不要。 ### Context Match Response ルーターは各バイヤーエージェントにファンアウトしレスポンスをマージします。各バイヤーは、ターゲティングがコンテンツコンテキストに一致したパッケージのオファーと、キー値として GAM に流れるレスポンスレベルのシグナルを返します。 ```json theme={null} { "type": "context_match_response", "request_id": "ctx-7f2a-oakwood-91b3", "offers": [ { "package_id": "pkg-display-0041" }, { "package_id": "pkg-native-0078" } ], "signals": { "segments": ["sustainability", "home_cooking"], "targeting_kvs": [ { "key": "adcp_seg", "value": "sustainability" }, { "key": "adcp_seg", "value": "home_cooking" }, { "key": "adcp_pkg", "value": "pkg-display-0041" }, { "key": "adcp_pkg", "value": "pkg-native-0078" } ] } } ``` 要点: * `offers` はアクティベートされたパッケージごとに 1 エントリを含む。このプレースメントのプロバイダーの同期されたセットは 3 つのパッケージを含んでいた — この 2 つがキッチン/サステナビリティのコンテキストに一致し、3 つ目(`pkg-display-0103`)は一致せず欠如している。 * web/GAM アクティベーションについては、オファーはシンプル — `package_id` のみ。よりリッチなフィールド(`brand`、`price`、`summary`、`creative_manifest`、`macros`)はそれらを必要とする統合に利用可能だが必須ではない。 * `signals.targeting_kvs` は、Prebid モジュールが GAM 広告リクエストに設定するキー値ペア。GAM ラインアイテムはこれらのキーで一致するよう設定される。 ## Identity Match 別途、TMP Prebid モジュールはユーザーのアイデンティティトークンとパブリッシャーの `seller_agent_url` を伴う Identity Match リクエストを送ります。このリクエストはページコンテキストを運びません。バイヤーは `seller_agent_url` からアクティブなパッケージセットを解決します。モジュールが(下記のように)`package_ids` を明示的に送るとき、構成は現在のページと独立でなければなりません(MUST) — all-active(この publisher でのそのバイヤーのすべてのアクティブパッケージ)または fuzzed(バイヤーが黙って落とす合成の存在しない ID でパディングされたランダムサンプル)のいずれか。ページ固有のサブセットは禁止されています。 ### Identity Match Request ```json theme={null} { "type": "identity_match_request", "request_id": "id-3k9p-oakwood-d4f1", "seller_agent_url": "https://oakwood.example", "identities": [ { "user_token": "tok_uid2_8f2a3b7c", "uid_type": "uid2" }, { "user_token": "XY1a2b3c4d5e6f7g8h9i0jKlMnOpQrSt", "uid_type": "rampid" }, { "user_token": "ID5*aB3xY9kL...", "uid_type": "id5" } ], "package_ids": [ "pkg-display-0041", "pkg-display-0042", "pkg-display-0043", "pkg-native-0078", "pkg-native-0079", "pkg-display-0103", "pkg-display-0104", "pkg-video-0201" ] } ``` 要点: * `request_id` は context match の `request_id` と無関係。2 つは互いから導出可能であってはならない。 * `package_ids` の例は all-active モードを示す: このプレースメントの context match にあった 3 つだけでなく、サイト全体にわたるバイヤーのすべてのアクティブパッケージ。ページ固有のサブセットは禁止 — それはバイヤーがパッケージセットを比較してアイデンティティをコンテキストと相関させることを許す。fuzzed モード(バイヤーが黙って落とす合成 ID でパディングされたランダムサンプル)も許容される。 * `identities` はパブリッシャーが利用可能なすべてのトークン(UID2、ID5、LiveRamp、hashed email、publisher first-party)を運ぶ。各トークンは不透明 — バイヤーはそれを PII に逆変換できない。完全なセットを送ることは、異なるバイヤーが異なるグラフで解決するためマッチ率を最大化する。 ### Identity Match Response バイヤーはユーザーを要求されたすべてのパッケージに対して評価し、適格なパッケージの ID と、ルーターがこのレスポンスをどのくらいキャッシュできるかを定義する TTL を返します。 ```json theme={null} { "type": "identity_match_response", "request_id": "id-3k9p-oakwood-d4f1", "eligible_package_ids": [ "pkg-display-0041", "pkg-display-0042", "pkg-native-0078", "pkg-native-0079", "pkg-display-0104" ], "serve_window_sec": 60 } ``` 要点: * 適格なパッケージのみがリストされる。リストに欠如するパッケージ(例: `pkg-display-0043`、`pkg-display-0103`、`pkg-video-0201`)は不適格。バイヤーはフリークエンシーキャップ、オーディエンスメンバーシップ、購入履歴、その他のアイデンティティベースのシグナルから適格性を計算する。理由はパブリッシャーにとって不透明。 * `serve_window_sec` はルーターにこのレスポンスをどのくらいキャッシュするかを伝える。そのウィンドウ中、ルーターはバイヤーに再クエリせずにキャッシュされた適格性を返す。パブリッシャーはキャッシュされた適格性を使ってページ上のすべてのプレースメントに割り当てる。 * `frequency_capped`、`audience_match`、`recency` フィールドはない。バイヤーの内部的な理由はバイヤーに留まる。 ## アクティベーション: コンテキストとアイデンティティの結合 パブリッシャーは context match と identity match をローカルで結合します。ここで 2 つの半分が一緒になります — ルーターは同じインプレッションについて両方を決して見ません。 ### Step 1: 交差 context match からのオファーを取り、identity match の適格性でフィルターします。 | Package | Context Match | Identity Match | Result | | ------------------ | ------------- | -------------- | -------- | | `pkg-display-0041` | Offered | Eligible | Activate | | `pkg-native-0078` | Offered | Eligible | Activate | | `pkg-display-0103` | Not offered | Not eligible | Skip | 2 つのパッケージのみが生き残る: `pkg-display-0041` と `pkg-native-0078`。 ### Step 2: GAM ターゲティングを設定 Prebid モジュールは context match の `signals.targeting_kvs` を取り、それらをキー値ペアとして GAM 広告リクエストに設定します: ``` adcp_seg = sustainability, home_cooking adcp_pkg = pkg-display-0041, pkg-native-0078 ``` GAM ラインアイテムは `adcp_pkg` 値でターゲットするよう事前設定されています。広告リクエストが `adcp_pkg=pkg-display-0041` で到着すると、GAM はそれを対応するラインアイテムに一致させクリエイティブを提供します。 ### Step 3: GAM が選択 GAM は、TMP アクティベートされたディールと他の需要ソースの両方を含む、すべての適格なラインアイテムにわたって、独自の優先度ルール、競合排除、ペーシングロジックを適用します。TMP は GAM の広告選択を上書きしません。入力を提供します。 ## GAM ラインアイテム設定 各アクティブパッケージについて、パブリッシャーは `adcp_pkg = ` をターゲットにする GAM ラインアイテムを作成します。これが TMP アクティベーションと GAM 広告選択の間のリンクです。 * **ラインアイテムタイプ。** `create_media_buy` からのディール条件に応じて Sponsorship または Standard。保証ディールは Sponsorship、非保証ディールは Standard を使う。 * **優先度。** ディールタイプに基づいて設定。保証ディールは優先度 4-8、非保証は優先度 12-16。これは TMP 需要が GAM の選択ロジックで他のラインアイテムとどう競うかを決める。 * **クリエイティブ割り当て。** `sync_creatives` からの事前同期されたクリエイティブを参照するか、バイヤーが提供する場合は Context Match レスポンスのクリエイティブマニフェストを使う。 * **ライフサイクル。** メディアバイが終了またはキャンセルされると、対応するラインアイテムを非アクティブ化する。ルーターは 1 時間以内にパッケージを Identity Match に含めるのを停止する。 * **自動化。** パブリッシャーは、新しい `create_media_buy` 完了をリッスンしパッケージ詳細を GAM API 呼び出しにマップすることで、ラインアイテム作成を自動化できる。パッケージ ID、ディールタイプ、優先度、クリエイティブ参照はすべてメディアバイレスポンスから利用可能。 ## Prebid 統合 TMP Prebid モジュールは、ベンダー固有の RTD モジュールを置き換える Real-Time Data(RTD)モジュールです。それは [TMP Prebid 提案](/specs/prebid-tmp-proposal) によって定義され、標準の Prebid RTD モジュールインターフェースに従います。完全なフローを扱います: 1. **オークション初期化時**: ページ上の各プレースメントについて TMP ルーターに Context Match リクエストを送る。 2. **context match レスポンス時**: オファーとターゲティングシグナルを保存する。 3. **時間的相関除去の後**: ユーザーのアイデンティティトークンと各バイヤーのすべてのアクティブパッケージ ID を伴う Identity Match リクエストを送る。相関除去は 2 つの部分を持つ — ランダムな 100-2000ms 遅延 **かつ** ランダム化された順序: 各オークションは Identity Match リクエストが先に送られるか Context Match リクエストが先に送られるかのほぼ等しい確率を持つ。 4. **identity match レスポンス時**: context match 結果と結合する。広告ユニットにターゲティングキー値を設定する。 5. **入札リクエスト時**: GAM は TMP ターゲティングキーで豊かにされた広告リクエストを受け取り、通常どおりラインアイテムを選択する。 コンテキストとアイデンティティのリクエスト間の時間的相関除去はプライバシー措置です。ランダムな遅延だけでは不十分 — 固定順序(Identity は常に Context の後)は順序を通じてペアリングを漏らす。モジュールは遅延とどちらのリクエストが先に送られるかの両方をランダム化します。 ### Impression ID の代入 web/Prebid サーフェスでは、**Prebid TMP モジュールが決定層** で、3 層の [`{IMPRESSION_ID}` 鋳造階層](/docs/creative/universal-macros#impression-identification) — パブリッシャー先、決定層 2 番目、TMPX デコード時のバイヤー最後 — の一部です。モジュールはパブリッシャーが既に impression\_id を供給したか(`adUnit.tmp.impressionId` または同等のファーストパーティフック経由)を確認します。そうなら、その値を通過させます。そうでなければ、モジュールが独自に鋳造します。どちらにせよ、GAM 広告リクエストにターゲティングキー `tmp_impression_id` として値を公開します。これは、バイヤーのインプレッションピクセルがクロスアイデンティティ重複排除キーとして使う値です — `{TMPX}` が欠如するコンテキストのみのインプレッションに不可欠で、`{TMPX}` が存在するときでもバイヤーの重複排除パスが一様に保たれるため有用です。 **形式は実装の選択。** Prebid モジュールは ULID、UUID(任意バージョン)、または任意の衝突耐性のある識別子スキームを使ってもよい(MAY) — プロトコルは形式をピン留めしません。 **任意の最適化。** Prebid の `enableTIDs` 設定が `true` のとき、モジュールは別途鋳造する代わりに `adUnit.transactionId` を impression\_id として再利用してもよい(MAY)。`enableTIDs` は Prebid.js 8+ でオプトアウト(プライバシー上の理由で)なので、ほとんどのデプロイは独自に鋳造する必要があります。 ```javascript theme={null} import { ulid } from 'ulid'; // In the TMP Prebid module, at auctionInit. // Resolve the impression_id in priority order: // 1. publisher pre-mint (e.g., server-side, attached as adUnit.tmp.impressionId) // 2. Prebid transactionId (decision-layer reuse) when enableTIDs is on // 3. fresh decision-layer mint (ULID, UUID, or any collision-resistant scheme) function resolveImpressionId(adUnit) { if (adUnit.tmp?.impressionId) { return adUnit.tmp.impressionId; // publisher-side mint — highest priority } if (pbjs.getConfig('enableTIDs') && adUnit.transactionId) { return adUnit.transactionId; // reuse Prebid's tid when available } return ulid(); // decision-layer fresh mint } const impId = resolveImpressionId(adUnit); pbjs.setTargeting(adUnit.code, { tmp_impression_id: impId }); ``` GAM を通じてトラフィックされるバイヤーのクリエイティブトラッキング URL は、標準の GAM マクロ経由でターゲティングキーを参照します: ``` https://buyer.example/imp ?imp_id=%%PATTERN:tmp_impression_id%% &tmpx=%%PATTERN:tmp_tmpx%% &cb=%%CACHEBUSTER%% ``` レンダリング時に、GAM は KV を代入しバイヤーのインプレッショントラッカーが impression\_id を不透明な文字列として受け取ります。KV 名 `tmp_impression_id` は意図的に `hb_*` プレフィックス(Prebid 自身の名前空間)を避け、TMP が発するターゲティングにこのページの他の場所で使われる `adcp_*` 慣例をミラーします。 ## シーケンス図 ``` Page Load | |-- Prebid TMP Module ---> TMP Router (Context) | |-- fan out --> Buyer Agent A | |-- fan out --> Buyer Agent B | |<- merge <--- offers + signals |<--- Context Match Response -- | | (100-2000ms random delay; Context/Identity order randomized | per auction — diagram shows Context-first; Identity-first | runs the two phases in reverse) | |-- Prebid TMP Module ---> TMP Router (Identity) | |-- fan out --> Buyer Agent A | |-- fan out --> Buyer Agent B | |<- merge <--- eligibility |<--- Identity Match Response -- | |-- Join locally: intersect offers with eligibility |-- Set targeting KVs on GAM ad request |-- GAM selects and serves ad ``` ## OpenRTB との共存 ほとんどのパブリッシャーは、OpenRTB 経由で需要を供給する Prebid ヘッダー入札と並んで TMP を実行します。2 つのシステムは補完的です。 * **追加需要としての TMP。** TMP パッケージは GAM でラインアイテムとして現れ、優先度と価格で Prebid ラインアイテムと競う。GAM がイールド決定を扱う — TMP は Prebid を置き換えず、事前交渉された需要を追加する。 * **競合排除。** TMP と Prebid の需要ソースをまたいで衝突するブランドが一緒に現れるのを防ぐため、GAM 競合排除ルールを設定する。 * **収益帰属。** TMP アクティベートされたインプレッションは `get_media_buy_delivery` 経由で追跡され、Prebid インプレッションは既存の SSP レポートを通じて流れる。パブリッシャーは BI 層で再照合する。 * **タイムアウトの独立性。** TMP と Prebid のリクエストは並列で実行される。TMP タイムアウト(50ms)は通常 Prebid タイムアウト(1000-1500ms)より速いので、TMP 結果は GAM が必要とする前に準備できる。 ## Web のプライバシー制約 これらの制約はすべてのサーフェスに適用されますが、web のケースについて再述する価値があります: * **context match はユーザーデータを運ばない。** cookie なし、ユーザートークンなし、IP アドレスなし。プロバイダーはプレースメント上のすべてのユーザーについて同じ同期されたパッケージセットを評価する。 * **identity match はページデータを運ばない。** URL なし、コンテンツシグナルなし、プレースメント ID なし。`package_ids` リストは、送られるとき、現在のページと独立した構成(all-active または fuzzed)を持つ — 決してページ固有のサブセットではない。 * **パブリッシャーがローカルで結合する。** ルーターは同じインプレッションについてコンテキストとアイデンティティの両方を決して見ない。ページコンテキストとユーザーアイデンティティの両方を既に持つパブリッシャーのみが交差を実行する。 * **時間的相関除去。** 2 つのリクエスト間のランダムな遅延 **かつ** ランダム化された順序(Context Match または Identity Match が等しく先に送られる可能性)が、ネットワークレベルでのタイミングと順序ベースの相関を防ぐ。固定順序は順序を通じてペアリングを漏らすため、ランダムな遅延だけでは不十分。 # セラー検証 Source: https://adcp-docs-ja.pier1.co.jp/docs/verification/overview バイヤーが馴染みのないセラーをエンドツーエンドでどう検証するか — brand.json、adagents.json、リクエスト署名を 1 つのチェーンとして、チェーンが証明しないものについての境界された正直さとともに。 Sam stands at a wide marble counter facing a confident agent in a sharp blazer holding a clipboard of glossy ad placements — but the counter behind the agent is empty, with no logo, no signage, and no one else in sight `get_products` レスポンスがちょうど Sam のオーケストレーターにヒットしました。セラー — Northwind Media — は Acme Outdoor が乗りたいスポーツネットワーク StreamHaus 全体で CTV 在庫を見積もりました。CPM は公正に見え、アベイルはフライトに合い、レスポンスは 1 秒未満で到着しました。 Sam は Northwind とトランザクションしたことがありません。このレスポンスに署名したエージェントが実際に StreamHaus 在庫を販売する認可を受けているか、StreamHaus が彼が認識する親ハウスの下の実パブリッシャーか、賢い攻撃者が先週 `northw1nd.example` を登録し \$25,000 を持ち去ろうとしているかを知りません。 彼は Northwind にセールスデッキで説得してもらう必要はありません。プロトコルにコードでチェーンを検証可能にしてもらう必要があります — レスポンスの署名鍵から、Northwind のブランドアイデンティティを通じて、StreamHaus の認可へ、彼が認識できる親ハウスに戻るまで、すべてのリンク。彼のバイヤーエージェントはチェーンを自動的に歩きます。Sam は判定を読みます。 このウォークスルーはそのチェーンを Sam の目を通じてたどります。 **これが検証するものとしないもの。** チェーンは *誰が販売する認可を受けているか* に答えます。それは *エージェントの背後の法人が誰か*(KYC、実オペレーター)、*アベイルが配信時に現実を反映するか*(カタログ精度、CPM、配信)、*ホスティングインフラが信頼できるか*(DNS、CDN、レジストラ)には答えません。[境界された正直さステップ](#step-5-know-what-the-chain-does-not-prove) がすべての制限を明示的に名指します。これは在庫に適用された C2PA の「クレーム非認証」姿勢です — プロトコルは認可クレームを運びそれを検証可能にし、そこで止まります。 ## 一目でわかるチェーン 3 つの発見可能な表面と 1 つの暗号チェック。Sam のエージェントは都合のよい順序でそれらを実行します: | Surface | Source | Question it answers | | --------------- | ---------------------------------------------- | --------------------------------------- | | RFC 9421 署名 | レスポンスヘッダー | 「このレスポンスは主張する鍵から来たか?」 | | `brand.json` | `northwind.example/.well-known/brand.json` | 「Northwind は誰で、何を代表すると主張しているか?」 | | `adagents.json` | `streamhaus.example/.well-known/adagents.json` | 「パブリッシャーは Northwind に在庫を販売する認可を与えているか?」 | | 相互主張 | 両 `brand.json` ファイルのクロス参照 | 「関係の 2 つの側は一致するか?」 | チェーンは設計上双方向です: 各事実はそれに対する権限を持つちょうどその当事者によって主張されます。パブリッシャーが誰が在庫を販売できるか決定します。ブランドオーナーが何を所有するか決定します。セラーがどの鍵がレスポンスに署名するか決定します。第三者レジストリがそれらの間を裁定しません — 誤動作する認可されたセラーは、プロトコル内クレームチェックではなく、パブリッシャーが `adagents.json` エントリーを取り消すことで修復されます。 下の 4 ステップのうち 1 つのみが暗号的に基礎付けられています(署名)。他の 3 つは整合性チェックされた発見です — well-known な場所の権威ファイルに対する文字列等価マッチ。チェーンは、パブリッシャーとセラーの自身の DNS、ホスティング、well-known エンドポイントの制御と同じだけ強いです。それが含意するものについては [Step 5](#step-5-know-what-the-chain-does-not-prove) を参照。 ## ステップ 1: レスポンス署名を検証 Sam holds a paper response up to a lamp — a wax seal in the corner glows under the light, revealing a cryptographic pattern that matches a key card he pulls from a folder labeled adagents.json `get_products` レスポンスは RFC 9421 `Signature` と `Signature-Input` ヘッダーを運びます。`keyid` パラメーターは Northwind の公開された JWKS の JWK を指します。Sam のクライアントはボディを解析する前に署名を検証します: ```javascript theme={null} const response = await fetch("https://northwind.example/mcp", { /* get_products call */ }); const verified = await verifyMessageSignature(response, { fetchJwks: (keyid) => fetchJwksForAgent("northwind.example", keyid), requiredFields: ["@method", "@target-uri", "content-digest", "@authority"], requireCreated: true, // RFC 9421 `created` parameter MUST be present maxAge: 300, // seconds — receiver MUST enforce a window }); if (!verified) throw new Error("signature failed — discard response"); ``` パスは Sam に 1 つの狭いことを伝えます: `keyid` とペアの秘密鍵を保持するエンティティがこのレスポンスを生成し、レスポンスは転送中に改ざんされていない。それはまだ `keyid` が正当な Northwind に属することを伝えません。そのバインディングはステップ 3 の `adagents.json` から来ます。 署名が失敗すると、他のすべてのチェックは無駄な作業です — レスポンスは自身を正しく識別することさえ信頼できません。最初に検証することはまた Sam にクリーンな中止を与えます: 任意のビジネスロジックがそれに触れる前にレスポンスを破棄します。 `maxAge` と `requireCreated` はオプションではありません — 鮮度ウィンドウなしでは同じ署名付きレスポンスが無期限にリプレイされうる。`maxAge` をクロックスキュー予算にチューニング。300s が一般的な出発点です。 AdCP 3.0 では、`get_products` の署名は RECOMMENDED です。支出コミット操作の必須署名は 4.0 に向けて [#2307](https://github.com/adcontextprotocol/adcp/issues/2307) で追跡されます。今日署名を要求するデプロイはそれをプラットフォーム層で強制します。 ## ステップ 2: Northwind の brand.json を読む Sam opens a leather-bound folder on Northwind's reception desk — inside is a single embossed page declaring the agency's portfolio, signing keys, and authorized operators, with a glowing seal at the top Sam のエージェントは `https://northwind.example/.well-known/brand.json` をフェッチします。これは Northwind の自己宣言です: それが誰で、署名鍵がどこに存在するか。 ```json theme={null} { "$schema": "/schemas/brand.json", "version": "1.0", "id": "northwind_media", "names": [{ "en_US": "Northwind Media" }], "url": "https://northwind.example", "keller_type": "master", "industries": ["advertising"], "agents": [ { "type": "sales", "id": "northwind_sales", "url": "https://northwind.example/mcp", "jwks_uri": "https://northwind.example/.well-known/jwks.json" } ] } ``` ここで 2 つが重要: 1. **ステップ 1 の `keyid` は `https://northwind.example/.well-known/jwks.json` で解決しなければならない** — Northwind 自身の brand.json が指す JWKS。Sam は今や署名から自己宣言のブランドアイデンティティへのバインディングを持ちます。 2. **Northwind はスタンドアロンエージェンシー** — `house_domain` フィールドなし。Northwind 側で検証する親ハウスクレームはありません。重要な認可クレームはパブリッシャー側、ステップ 3 に存在します。 `brand.json` はそれが記述するエンティティによって HTTPS 上で公開されます。生のフィールドをまずスキーマ検証せずに LLM プロンプトにパイプするバイヤーエージェントは、相手方から敵対的入力を取っています。解析前に [brand.json スキーマ](/docs/brand-protocol/brand-json) に対して検証し、攻撃者制御の文字列フィールド(`names`、`description`、カスタムキー)をサニタイズなしに LLM コンテキストに決して渡さないでください。 ## ステップ 3: StreamHaus の adagents.json に対して確認 Sam holds two documents side by side — Northwind's brand.json on the left listing StreamHaus, and StreamHaus's adagents.json on the right listing Northwind — and the matching delegation_type field on both glows green as the chain locks in Sam のエージェントは `https://streamhaus.example/.well-known/adagents.json` をフェッチします — 誰が在庫を販売する認可を受けているかのパブリッシャー自身の宣言: ```json theme={null} { "$schema": "/schemas/adagents.json", "contact": { "name": "StreamHaus Publishing", "email": "adops@streamhaus.example", "domain": "streamhaus.example" }, "properties": [ { "property_id": "streamhaus_ctv", "property_type": "ctv_app", "name": "StreamHaus CTV App", "publisher_domain": "streamhaus.example", "identifiers": [{ "type": "roku_store_id", "value": "12345" }] } ], "authorized_agents": [ { "url": "https://northwind.example/mcp", "authorized_for": "StreamHaus CTV inventory via delegated authority", "authorization_type": "property_ids", "property_ids": ["streamhaus_ctv"], "delegation_type": "delegated", "signing_keys": [ { "kid": "northwind-sell-prod-2026", "kty": "OKP", "alg": "EdDSA", "crv": "Ed25519", "x": "Xe2lAKRJR_zr3FQRdSNwp3zsrv_IXnVCWJXDcWXwkLI", "use": "sig" } ] } ], "last_updated": "2026-04-12T10:00:00Z" } ``` これが双方向ロックです: * **`url` が一致** Northwind の `brand.json` が `agents[].url` で名指した MCP エンドポイントと。両側で同じエージェント。 * **`delegation_type: "delegated"`** が商業関係を宣言 — Northwind は StreamHaus に代わって販売する認可を受けている。enum は `direct | delegated | ad_network`。パブリッシャーがどれが合うか選ぶ。 * **`signing_keys[]`** が、その `kid` がステップ 1 の署名で使われたものに一致する JWK を含む。これが Sam が欠いていたリンクです: パブリッシャーの StreamHaus が、自身のファイルでこの特定の公開鍵が自身に代わって署名できると証明します。 今や Sam は「誰が販売する認可を受けているか」の答えを持ちます: **このレスポンスに署名したエンティティはパブリッシャー自身の認可ファイルで、一致する商業関係と明示的な署名鍵認可とともに名指されている**。チェーンが閉じました。 ブランドプロトコルの [相互主張モデル](https://github.com/adcontextprotocol/adcp/issues/3533) は、Sam の下流ロジックが行動できる離散シグナルを生成します: | State | Meaning | Buyer action | | ------------------ | ----------------------------------------------------- | -------------------------------------------- | | `inline` | セラーがブランドオーナー — 委譲が関与しない | 進む。委譲するものなし | | `mutual_assertion` | 両側が一致する宣言を公開 | 進む | | `one_sided_brand` | セラーの brand.json がパブリッシャーを主張。パブリッシャーが相互にしていない | **認可として扱わない。** 任意のドメインが任意のパブリッシャーを一方的に主張できる。 | | `one_sided_house` | パブリッシャーの adagents.json がセラーを名指す。セラーの brand.json が認めない | 人間レビューのため保留 | | `standalone` | どちらの側も公開しない — bearer トークン信頼のみ | 帯域外認可または拒否 | Sam のチェーンは `mutual_assertion` に解決します — inline に次ぐ最強の状態。`inline` と `mutual_assertion` のみがチェーンを閉じます。 ## ステップ 4: 親ハウスを歩く Sam stands in front of a wall display showing a brand-portfolio hierarchy — a large parent-house emblem at top, with the publisher emblem below it connected by a glowing teal line, and other sibling sub-brand emblems branching off the parent Acme Outdoor の包含リストは親ハウスレベルで解決します — 彼らは Sportshaus Holdings のブランドファミリーを信頼します。StreamHaus はサブブランドなので、Sam のエージェントは 1 ホップさらに歩きます。 StreamHaus 自身の brand.json がその親を宣言します: ```json theme={null} { "$schema": "/schemas/brand.json", "version": "1.0", "id": "streamhaus", "names": [{ "en_US": "StreamHaus" }], "url": "https://streamhaus.example", "house_domain": "sportshaus-holdings.example", "keller_type": "endorsed", "industries": ["media", "broadcasting"] } ``` 次に親の brand.json が相互にします: ```json theme={null} { "$schema": "/schemas/brand.json", "version": "1.0", "house": { "domain": "sportshaus-holdings.example", "name": "Sportshaus Holdings", "architecture": "branded_house" }, "brand_refs": [ { "domain": "streamhaus.example", "brand_id": "streamhaus", "effective_at": "2025-01-01T00:00:00Z" }, { "domain": "courtsidehq.example", "brand_id": "courtsidehq" } ] } ``` 相互性ルール: **StreamHaus の `house_domain` ↔ Sportshaus Holdings の `brand_refs[].domain`**。両側が一致します。 このステップに 2 つの別個の概念が乗り、文書はそれらを混同しないよう注意します: * **子の `keller_type`**(`endorsed`)はブランドアーキテクチャ関係を記述 — サブブランドが親の隣にどう位置付けられるか。それは Keller アーキテクチャメタデータで、商業認可ではありません。 * **Northwind が販売できるようにする商業関係** はステップ 3 の `delegation_type: "delegated"` で、親ハウスではなくパブリッシャー(StreamHaus)に存在します。 Sportshaus Holdings は Acme Outdoor の包含リストにあります。Sam のエージェントがちょうど歩いたチェーンは: 署名付きレスポンス → Northwind の `brand.json` → StreamHaus の `adagents.json` → StreamHaus と Sportshaus Holdings の相互 `brand.json` 宣言。4 つの発見可能な表面、1 つの暗号アンカー、認可質問のための電話ゼロ。 ## Step 5: Know what the chain does not prove Sam sits at his desk with the sealed document beside him, holding his phone to his ear — the technical chain is complete, and now he's calling a person at the publisher to confirm the human-layer details the protocol cannot attest 認可チェーンが閉じました。Northwind は初めて Acme Outdoor が実際に意味ある支出でトランザクションする相手方なので、Sam は電話を取ります。プロトコルのためではありません — チェーンは伝えられることを彼に伝えました。チェーンが伝えられないすべてのために。 | The chain proves | The chain does not prove | | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | Northwind は `delegated` 関係の下で StreamHaus 在庫を販売する認可を受けている | Northwind の実際の人間がこのエージェントを運用しそれに答える | | 署名鍵はセラーとパブリッシャーの両方によって名指されている | その鍵の背後のオペレーターが Sam が信頼する誰かによって KYC された | | StreamHaus は Sportshaus Holdings を親として宣言し、親が相互にする | 法的相手方が Sam がトランザクションしていると思う相手に一致する | | レスポンスは転送中に改ざんされなかった | レスポンスのアベイルがフライト開始日に利用可能である、または見積もられた CPM が保持される | | Northwind のドメインは今日署名鍵を制御する | 鍵が明日攻撃者にローテートできない — 初回遭遇信頼は [鍵透明性](https://github.com/adcontextprotocol/adcp/issues/3925) が 4.0 で到達するまで TOFU | そのテーブルの右側に 3 つの名指しされたギャップが存在します。 **人間層ギャップ。** AdCP はオペレーター/人間 KYC プリミティブを運びません。暗号チェーンは「このドメインは販売する認可を受けており、この鍵がこのレスポンスに署名した」と言います — 「検証された会社の検証された人間がこのエージェントの反対側にいる」とは言いません。KYC はメンバーシップとアカウント層です。Acme のしきい値を超える新しい相手方には、Sam は依然として、任意の意味ある新ベンダーに対してするのとまったく同様に人間チェックにエスカレートします。 **ホスティング層ギャップ。** チェーンは `northwind.example`、その DNS、TLS、CDN を制御する誰でも信頼します。レジストラ乗っ取り、CDN 侵害、誤発行された TLS 証明書はチェーン全体を置き換えます — 攻撃者が有効に見えるパイプライン上で攻撃者制御の `brand.json`、`adagents.json`、JWKS をサーブします。3.x に公開鍵透明性ログはありません。初回遭遇信頼は trust-on-first-use で、失効は再フェッチによってのみ検出可能です。以前 Northwind とトランザクションしたバイヤークライアントは見た `kid` をピン留めしローテーションで警告します。初回遭遇のバイヤークライアントはそのシグナルを持ちません。[#3925](https://github.com/adcontextprotocol/adcp/issues/3925) を参照。 **配信時ギャップ。** カタログ精度はプロトコル証明されません。パブリッシャーは個別の製品エントリーに署名せず、製品ごとの証明は在庫が本番でどう運用されるかに一致しません — 予測はドリフトし、価格は動き、供給は動的です。誤動作する認可されたセラーは、プロトコル内クレームチェックではなく、パブリッシャーが `adagents.json` エントリーを取り消すことで修復されます。配信時の真実は [測定](https://github.com/adcontextprotocol/adcp/issues/2391) と課金照合に存在します。 これは C2PA の「クレーム非認証」姿勢です。プロトコルは認可クレームを運びそれを暗号的に検証可能にします。それは — そしてプロトコル層ではすべきでない — 任意の意味ある新相手方に対してバイヤーがする人間層、ホスティング層、配信層のチェックを置き換えません。 ## Sam が次にすること Sam のクライアントは、決定時にキャプチャした `brand.json`、`adagents.json`、JWKS バイトを含めて — ライブファイルへのポインターだけでなく — `get_products` レスポンスとともに検証結果をログします。数か月後、監査人はそれらのキャプチャされたアーティファクトに対して署名を再検証し Sam の決定を正確に再現できます。ライブ `.well-known/` ファイルの再フェッチは不十分です — それらは可変で、単一の鍵ローテーションまたはドメイン移転が素朴なリプレイを無効化します。 検証結果と 5 状態トラストシグナルは候補プランとともに Sam の [ガバナンスフロー](/docs/governance/overview) に移動し、そこで Jordan のガバナンスエージェントが任意の支出がコミットされる前に Acme のポリシーチェックを適用します。 ## ここからどこに行くか * [brand.json リファレンス](/docs/brand-protocol/brand-json) — 自己宣言、親ハウスポートフォリオ、Keller アーキテクチャメタデータの完全なスキーマ * [セラーセットアップ](/docs/brand-protocol/seller-setup) — パブリッシャー、ネットワーク、SSP がセラーアイデンティティと認可ファイルをどう公開するか * [adagents.json リファレンス](/docs/governance/property/adagents) — パブリッシャー側認可、`authorization_type` 判別子、`signing_keys[]` JWK 形状 * [verify\_brand\_claim](/docs/brand-protocol/tasks/verify_brand_claim) — 検証をブランドエージェントに委譲する Tier-2 実装者ガイド * [セキュリティモデル](/docs/building/concepts/security-model) — 三者ガバナンスとトラスト姿勢 * [リクエスト署名](/docs/building/by-layer/L1/request-signing) — RFC 9421 詳細、鍵ローテーション、透明性ログロードマップ * [トラスト & セキュリティ](/docs/trust) — CISO 向け表面マップ。このウォークスルーはバイヤー向けの相棒 * [AAO Verified](/docs/building/verification/aao-verified) — 上のアイデンティティチェーンの上に層化された継続的動作適合性証明 # Addie ツールリファレンス Source: https://adcp-docs-ja.pier1.co.jp/docs/aao/addie-tools Addie がアクセスできるすべてのツール、ケイパビリティセットごとにグループ化。 # Addie ツールリファレンス このページは Addie が呼べるすべてのツールをリストします。各ツールの説明は Addie のプロンプトに出荷されるのと同じもので、言語はチュートリアル調ではなくルーター向けです — しかし Addie が *何ができる* か、*いつ* ツールに手を伸ばすべきか、*どの* フィールドを受け入れるかを正確に伝えます。 ツールは **ケイパビリティセット**(ルーターカテゴリー)ごとにグループ化されています。ルーターはユーザーの意図に基づいて 1 つ以上のセットを選択し、次に Addie はそれらのセット内で特定のツールを選びます。少数のツールはルーティングにかかわらず *常時利用可能* です — バグレポートフロー、コンテンツ提出、エスカレーション — **常時利用可能** セクションを参照してください。 インテグレーターまたは管理者で Addie が X をできるか知りたい場合: まずこのページを検索してください。ここでツールが見つからない場合、Addie は X をできません — 発明を依頼しないでください。 ## knowledge プロトコルの質問、実装ヘルプ、ロードマップ/RFC 検索、コミュニティ議論のため、ドキュメント、コードリポジトリ、Slack 履歴、キュレートされたリソース、GitHub issue/PR を検索し、JSON を AdCP スキーマに対して検証する ### `search_docs` 関連コンテンツについて AdCP ドキュメントを検索します。AdCP、プロトコル、ツール、または物事がどう動くかについての質問に答えるためにこれを使います。 *Source: `server/src/addie/mcp/docs-search.ts`* ### `get_doc` 特定のドキュメントページの完全なコンテンツを取得します。ドキュメントを詳細に読むため search\_docs の後にこれを使います。 *Source: `server/src/addie/mcp/docs-search.ts`* ### `search_slack` AAO ワークスペースの公開チャネルから Slack メッセージを検索します。コミュニティ議論、Q\&A スレッド、実世界の実装例が必要なときにこれを使います。特定のチャネルまたはワーキンググループ(例: 「Governance working group」)について尋ねられたら、channel パラメーターを使って結果をフィルターします。議論を要約するよう依頼されたら、関連キーワードを検索し結果を統合します。結果からの情報を使うとき Slack パーマリンクを引用します。 *Source: `server/src/addie/mcp/knowledge-search.ts`* ### `get_channel_activity` 特定の Slack チャネルから最近のメッセージを取得します。チャネルアクティビティを要約するよう依頼されたとき、ワーキンググループが議論していることを見るとき、チャネル内の会話の概要を得るときにこれを使います。最近性でソートされたメッセージを返します。結果を得た後、それらをユーザー向けのサマリーに統合します。 *Source: `server/src/addie/mcp/knowledge-search.ts`* ### `search_resources` サマリーとコンテキスト分析でインデックスされたキュレート済み外部リソース(記事、ブログ投稿、業界コンテンツ)を検索します。業界トレンド、競合情報、エージェンティック広告に関する外部の視点にこれを使います。 *Source: `server/src/addie/mcp/knowledge-search.ts`* ### `get_recent_news` キュレートされた業界フィードからアドテックとエージェンティック広告に関する最近のニュースと記事を取得します。サマリーと分析付きで最近性でソートされた記事を返します。ユーザーが「ニュースで何が起きている?」「アドテックで何が新しい?」「最近何を学んだ?」と尋ねるときにこれを使います。 *Source: `server/src/addie/mcp/knowledge-search.ts`* ### `fetch_url` ウェブ URL のコンテンツをフェッチして読みます。ユーザーがリンクを共有してそれについて尋ねるとき、または外部コンテンツを読む必要があるときにこれを使います。ページのテキストコンテンツを返します。注: 認証を必要とするページでは動作しません。 *Source: `server/src/addie/mcp/url-tools.ts`* ### `read_slack_file` Slack で共有されたファイルをダウンロードして読みます。ユーザーがファイル(PDF、ドキュメント、テキストファイル、画像など)を共有したときはいつでも積極的にこれを使ってください — 依頼を待たないでください。ユーザーはあなたが共有したものを見ることを期待します。共有ファイル情報からファイル URL を提供します。 *Source: `server/src/addie/mcp/url-tools.ts`* ### `list_github_issues` トピックのオープン項目を見つける、RFC/epic ステータスを確認する、または「X のため何が作業されているか」の質問に答えるため、GitHub issue と PR を検索またはリストします。キーワード検索には `query` を渡します(GitHub 検索構文、ただし `repo:`/`org:`/`user:`/`is:` 修飾子は拒否される — 代わりに `repo` パラメーターを使う)。タイトル、番号、状態、ラベル、作成者、最終更新を返します。任意の公開 GitHub リポジトリで動作します。ユーザーが特定の issue 番号を持つときは使わないでください — get\_github\_issue を使ってください。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `validate_json` JSON オブジェクトを AdCP スキーマに対して検証します。ユーザー提供の JSON が仕様に従って有効かを確認するためにこれを使います。無効な場合は検証エラーを返します。 *Source: `server/src/addie/mcp/schema-tools.ts`* ### `get_schema` AdCP JSON スキーマをフェッチして表示します。すべてのプロパティ、必須フィールド、制約を含む正確なスキーマ定義を表示するためにこれを使います。これはどのフィールドが有効かの権威的ソースです。 *Source: `server/src/addie/mcp/schema-tools.ts`* ### `list_schemas` 利用可能な AdCP スキーマとバージョンをリストします。どのスキーマが存在しどのバージョンが利用可能かをユーザーが発見するのを助けるためにこれを使います。 *Source: `server/src/addie/mcp/schema-tools.ts`* ### `compare_schema_versions` 2 つのスキーマバージョンを比較して何が変わったかを表示します。ユーザーが AdCP バージョン間の違いについて尋ねるとき、またはどのバージョンを使うか混乱しているときにこれを使います。 *Source: `server/src/addie/mcp/schema-tools.ts`* ## member メンバープロフィール、ワーキンググループ、委員会、アカウント設定を管理します。ワーキンググループドキュメントのリスト、コンテンツへのアセット添付、会社ロゴまたはブランドカラーの更新を含みます。 ### `get_my_profile` 現在のユーザーの個人プロフィール — 人としての彼らが誰か — を取得します。ヘッドライン、バイオ、専門知識、興味、ソーシャルリンクを表示します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `update_my_profile` 現在のユーザーの個人プロフィール — 人としての彼らが誰か — を更新します。ヘッドライン、バイオ、専門知識、興味、位置、ソーシャルリンクを更新できます。提供されたフィールドのみを更新します。名/姓は更新しません — それには set\_my\_name を使ってください。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `get_company_listing` 会社のディレクトリリスティング — 組織がメンバーディレクトリと Addie にどう現れるか — を取得します。タグライン、説明、オファリング、本社、連絡先情報を表示します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `update_company_listing` 会社のディレクトリリスティングテキストフィールド — タグライン、説明、連絡先情報、ソーシャルリンク、本社 — を更新します。提供されたフィールドのみを更新します。ロゴまたはブランドカラーには代わりに update\_company\_logo を使ってください。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `update_company_logo` ディレクトリリスティングの会社ロゴまたはブランドカラーを更新します。メンバーが会社ロゴをアップロード、変更、または修正したいときに使います。ロゴ URL は公開アクセス可能な HTTPS 画像(PNG、JPG、SVG など)でなければなりません — Google Drive のようなファイルビューアーリンクは動作しません。 ブランドドメインが以前に別の組織によって登録されていた場合、ツールはユーザーに以前のブランドアイデンティティ(ロゴ、カラー、エージェント)を採用するか新しく始めるかを尋ねる通知を返します — 採用には `adopt_prior_manifest: true`、クリアには `false` を渡し、再度呼びます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `request_brand_domain_challenge` 呼び出し元の選択された組織が、現在別の組織に登録されているか未登録のブランドドメインを主張できるよう DNS TXT チャレンジを発行します。ユーザーが DNS ホストで公開する検証レコード(Name/Type/Value)を返します。使わない場合: ドメインが既に呼び出し元の組織に所有されている(メンバープロフィールで既にリンク済み)。ユーザーが単にドメインが何か尋ねている。ユーザーが汎用の「ドメインはセットアップされているか?」の質問をしている。呼び出し元が複数の組織に属する場合、または現在の組織が個人ワークスペースでドメインが会社組織に一致する場合、停止してチャレンジ発行前に会社 organization\_id を選ぶよう依頼します。ユーザーがレコードを公開したことを確認した後にのみ verify\_brand\_domain\_challenge とペアにします。レスポンスは機械解析のため HTML コメント '\' で始まります(レンダリングされた markdown では不可視) — コード: dns\_record\_issued, already\_verified, collision, invalid\_domain, workos\_error, not\_authenticated, no\_org, org\_selection\_required, not\_admin, missing\_domain。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `verify_brand_domain_challenge` 選択された組織の以前発行されたチャレンジに対して WorkOS DNS ルックアップを実行し、成功時にブランドレジストリ更新を適用します。この同じ会話で request\_brand\_domain\_challenge が DNS 指示を返し、かつユーザーがレコードを公開したことを明示的に確認した後にのみ呼びます。推測的に、「ステータス確認」ツールとして、またはリトライループで決して呼ばないでください — DNS 伝播は数分かかり、サーバーはクールダウンを強制し、あまりに早く再度呼ぶと still\_pending を返します。呼び出しが still\_pending を返す場合、停止して任意のリトライ前にユーザーに確認を依頼します。レスポンスは HTML コメント '\' で始まります(レンダリングされた markdown では不可視) — コード: verified, still\_pending, no\_challenge, workos\_error, not\_authenticated, no\_org, org\_selection\_required, not\_admin, missing\_domain。'verified' の後クレームは完了。'still\_pending' の後は停止してリトライ前にユーザーに確認を依頼します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `list_working_groups` AgenticAdvertising.org のアクティブな委員会をリストします。タイプでフィルターできます: ワーキンググループ(技術)、カウンシル(業界バーティカル)、チャプター(地域)。公開グループを全員に表示し、メンバーには非公開グループを含めます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `get_working_group` 説明、リーダー、メンバー数、最近の投稿を含む特定のワーキンググループの詳細を取得します。グループスラッグ(URL フレンドリーな名前)を使います。名前、組織、メールを含む完全なメンバーリストを得るには include\_members: true を渡します(非公開グループは管理者のみ)。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `join_working_group` 現在のユーザーに代わってワーキンググループに参加します。グループが非公開の場合、代わりに request\_working\_group\_invitation の使用を提案します。ユーザーは AgenticAdvertising.org のメンバーでなければなりません。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `request_working_group_invitation` ユーザーに代わって非公開ワーキンググループへの招待をリクエストします。管理者が招待を処理できるようエスカレーションを作成します。グループが非公開のため join\_working\_group が失敗したときにこれを使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `get_my_working_groups` 現在のユーザーのワーキンググループメンバーシップを取得します。どのグループに属し各での役割を表示します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `express_council_interest` まだ立ち上がっていない業界カウンシルまたは他の委員会への参加に関心を表明します。ユーザーは参加者になりたいか潜在的リーダーになりたいかを示せます。これはカウンシルが公式に立ち上がる前に関心を測るのに役立ちます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `withdraw_council_interest` カウンシルまたは委員会への関心を撤回します。ユーザーがカウンシル立ち上げ時にもはや通知されたくないときにこれを使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `get_my_council_interests` 現在のユーザーのカウンシル関心サインアップを取得します。どのカウンシルに参加関心を表明したかを表示します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `list_perspectives` AgenticAdvertising.org メンバーからの公開されたパースペクティブ(記事/投稿)をリストします。これらはコミュニティが共有した公開記事です。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `create_working_group_post` 現在のユーザーに代わってワーキンググループに投稿を作成します。ユーザーはワーキンググループのメンバーでなければなりません。article、link、discussion の投稿タイプをサポートします。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `attach_content_asset` 公開されたパースペクティブにファイル(画像、PDF)を添付します。URL からフェッチして保存します。カバー画像やレポート PDF を追加するため propose\_content の後に使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `bookmark_resource` 有用なウェブリソースを将来の参照のためナレッジベースに保存します。ウェブ検索中に将来の質問に役立つ価値ある外部コンテンツを見つけたときにこれを使います。コンテンツはフェッチされ、要約され、インデックスされます。 *Source: `server/src/addie/mcp/knowledge-search.ts`* ### `list_committee_documents` 委員会が追跡するドキュメントをリストします。ドキュメントタイトル、ステータス、サマリーを表示します。 *Source: `server/src/addie/mcp/member-tools.ts`* ## directory 検索可能なパートナー/ベンダーディレクトリ — パートナー、ベンダー、コンサルタント、サービスプロバイダー、メンバー組織を見つけます。また: 紹介のリクエスト、メンバーディレクトリの閲覧、ブランドの調査、ブランドアセットのルックアップ、レジストリギャップの発見 ### `search_members` 特定のケイパビリティやサービスを提供するメンバー組織(企業)を検索します。メンバー名、説明、タグライン、オファリング、タグを検索します。ユーザーがベンダー、コンサルタント、実装パートナー、マネージドサービスを見つけたいときにこれを使います。クエリはユーザーが実際に必要とするもの(例: 「CTV measurement」「sales agent implementation」)を反映すべきで、「partner」のような汎用語ではありません。連絡先情報付きの公開メンバープロフィールを返します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `request_introduction` ユーザーをメンバー組織と繋ぐ紹介メールを送ります。Addie がリクエスターに代わって直接メールを送ります。ユーザーが検索結果を見た後、特定のメンバーに紹介または接続されることを明示的に依頼したときにこれを使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `get_my_search_analytics` ユーザーのメンバープロフィールの検索分析を取得します。プロフィールが検索に何回現れたか、プロフィールクリック、紹介リクエストを表示します。公開プロフィールを持つメンバーのみに機能します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `list_members` AgenticAdvertising.org メンバー組織をリストします。オファリング(buyer\_agent, sales\_agent, creative\_agent, signals\_agent, si\_agent, governance\_agent, publisher, consulting)、市場(North America, EMEA, APAC, LATAM, Global)、または検索語でフィルターできます。 *Source: `server/src/addie/mcp/directory-tools.ts`* ### `get_member` スラッグ識別子で特定の AAO メンバーの詳細情報を取得します。 *Source: `server/src/addie/mcp/directory-tools.ts`* ### `list_agents` メンバー組織からのすべての公開 AdCP エージェントをリストします。タイプでフィルターできます: creative(アセット生成)、signals(オーディエンスデータ)、sales(メディアバイイング)、governance(プロパティリストとコンテンツ標準)、si(スポンサードインテリジェンス/会話型コマース)。 *Source: `server/src/addie/mcp/directory-tools.ts`* ### `get_agent` URL で特定のエージェントの詳細を取得します。 *Source: `server/src/addie/mcp/directory-tools.ts`* ### `list_publishers` AdCP をサポートすることを示す /.well-known/adagents.json ファイルを公開したすべてのパブリッシャーをリストします。 *Source: `server/src/addie/mcp/directory-tools.ts`* ### `lookup_domain` 特定のパブリッシャードメインに認可されたすべてのエージェントを見つけます。検証済みエージェント(adagents.json から)と主張されたエージェント(エージェント登録から)の両方を表示します。 *Source: `server/src/addie/mcp/directory-tools.ts`* ### `research_brand` Brandfetch API を使ってドメインでブランドを調査します。見つかった場合ブランド情報(ロゴ、カラー、会社詳細)を返します。拡充データを自動的にレジストリに保存します — 後で save\_brand を呼ぶ必要はありません。 *Source: `server/src/addie/mcp/brand-tools.ts`* ### `resolve_brand` /.well-known/brand.json で brand.json をチェックしてドメインを正準ブランドアイデンティティに解決します。見つかった場合権威的ブランド情報を返します。 *Source: `server/src/addie/mcp/brand-tools.ts`* ### `save_brand` ブランドをコミュニティブランドとしてレジストリに保存します。手動でブランドを追加するために使います(research\_brand の後は不要、自動保存されるため)。マニフェストが提供されないとき既存の拡充データを保持します。 *Source: `server/src/addie/mcp/brand-tools.ts`* ### `list_brands` オプションフィルター付きでレジストリのブランドをリストします。ソースタイプでフィルターし名前またはドメインで検索できます。 *Source: `server/src/addie/mcp/brand-tools.ts`* ### `list_missing_brands` まだレジストリにない最もリクエストされたブランドドメインをリストします。需要シグナルを表示します — 人々が探しているが私たちが持っていないブランド。 *Source: `server/src/addie/mcp/brand-tools.ts`* ## agent\_testing パブリッシャーとエージェントのセットアップ、検証、テスト — adagents.json を検証、brand.json をチェック、パブリッシャー認可を検証、プロパティを解決、エージェントエンドポイントをプローブ、コンプライアンステストを実行、RFC 9421 リクエスト署名をグレード、OAuth ハンドシェイクを診断。「私のエージェントがプロパティを見られない」「認可が動作しない」「署名セットアップは正しい?」「OAuth を診断」、またはパブリッシャーセットアップの質問に使います。 ### `validate_adagents` ライブフェッチでドメインの /.well-known/adagents.json ファイルを検証します。エラーや警告を含む検証結果を返します。 *Source: `server/src/addie/mcp/property-tools.ts`* ### `resolve_brand` /.well-known/brand.json で brand.json をチェックしてドメインを正準ブランドアイデンティティに解決します。見つかった場合権威的ブランド情報を返します。 *Source: `server/src/addie/mcp/brand-tools.ts`* ### `get_agent_status` エージェントの AAO レジストリの現在ステータスを返します: ヘルス(オンライン / 最終チェック)、宣言されたケイパビリティ、comply ストーリーボードスイートからのトラックごとの最新コンプライアンス判定。キャッシュされた状態を読みます — ライブプローブは実行しません。レジストリにないエージェントには、エージェントを登録する(ハートビートが拾うように)か、オンデマンドチェックのため evaluate\_agent\_quality を実行するガイダンスを返します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `check_publisher_authorization` パブリッシャードメインが特定のエージェントを認可したかをチェックします。結果は短時間キャッシュされます。パブリッシャーが adagents.json を変更した後にキャッシュをバイパスするには force\_refresh: true を渡します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `test_adcp_agent` 非推奨 — 代わりに evaluate\_agent\_quality を使ってください。evaluate\_agent\_quality を実行し同じ結果を返します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `evaluate_agent_quality` AdCP エージェントでプロトコルコンプライアンス評価を実行し、コーチング用の構造化結果を返します。エージェントがサポートするすべてのケイパビリティトラック(core, products, media buy, creative, governance, signals など)をテストし、パフォーマンス、完全性、ベストプラクティスに関する助言観測を収集します。結果は pass/fail だけでなく具体的な実行可能な観測を含みます。公開テストエージェントはセットアップ不要でログインした任意のユーザーに機能します。認証を必要とするカスタムエージェントには、まず save\_agent を使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `grade_agent_signing` RFC 9421 リクエスト署名適合性グレーダーをエージェントに対して実行します。エージェントの検証者が有効な署名付きリクエストを受け入れ、未署名、期限切れ、リプレイ、誤鍵などのリクエストを正しいエラーコードで拒否するかをテストします。診断付きのベクターごと pass/fail レポートを返します。前提条件: エージェントが get\_adcp\_capabilities で `request_signing.supported: true` を宣言し、`test-kits/signed-requests-runner.yaml` に従って検証者を事前構成している(ランナーの署名 keyid `test-ed25519-2026` と `test-es256-2026` を受け入れ、`test-revoked-2026` を失効リストに持つ)。ライブ副作用ベクター(実 `create_media_buy`、リプレイ上限フラッド)はデフォルトでスキップされます — それらを実行するには `allow_live_side_effects: true` を渡し、それはサンドボックスエンドポイントに対してのみ行います。 *Source: `server/src/addie/mcp/auth-grader-tools.ts`* ### `diagnose_agent_auth` RFC 9728 protected-resource メタデータと RFC 8414 authorization-server メタデータをプローブし、スコープ内の任意のアクセストークンをデコードし、何が間違っているかについてランク付けされた仮説(likely / possible / ruled out)をレポートすることで、エージェントの OAuth ハンドシェイクを診断します。エージェントが予期せず 401/403 を返すとき、OAuth メタデータが誤構成されているかもしれないとき、または統合前にエージェントの OAuth セットアップを検証するときに使います。これは匿名モード診断です — トークンリフレッシュと認証済みツール呼び出しプローブはスキップされるため、レポートは特定のトークンが動作するかではなく公開表面がアドバタイズするものを記述します。 *Source: `server/src/addie/mcp/auth-grader-tools.ts`* ### `compare_media_kit` \[非推奨 — 代わりに test\_rfp\_response または test\_io\_execution を使ってください] パブリッシャーの述べた在庫をエージェントが返すものと比較します。test\_rfp\_response(実 RFP に対してテスト)または test\_io\_execution(IO がエージェントを通じて実行できるかテスト)を優先してください。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `test_rfp_response` パブリッシャーのエージェントが実 RFP またはキャンペーンブリーフにどう応答するかをテストします。Addie はまず RFP ドキュメントを解析し、次に構造化データでこのツールを呼びます。エージェントで get\_products を呼び、エージェントが表示するものと RFP がリクエストするものを比較するギャップ分析を返します。パブリッシャーの述べた応答(通常提案するもの)が最も価値ある入力です — エージェント出力をセールスチームが実際に応答する方法と比較できます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `test_io_execution` バイヤーエージェントが実 IO またはプロポーザルをパブリッシャーのエージェントを通じて実行できるかをテストします。Addie はまず IO ドキュメントを解析し、次に構造化ラインアイテムでこのツールを呼びます。決定的スコアリングを使って各ラインアイテムをエージェント製品にマップし、バイヤーエージェントが送る正確な create\_media\_buy JSON を構築し、オプションでドライランします。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `validate_agent` エージェントがパブリッシャードメインに認可されているかを、その /.well-known/adagents.json ファイルをチェックして検証します。 *Source: `server/src/addie/mcp/directory-tools.ts`* ### `resolve_property` パブリッシャードメインをそのプロパティ情報に解決します。ホストされたプロパティ、発見されたプロパティ、ライブ adagents.json をチェックします。 *Source: `server/src/addie/mcp/property-tools.ts`* ### `save_property` レジストリのホストされたプロパティを保存または承認します。新しいプロパティを作成し、保留中のものを承認し、既存のものを更新します。 *Source: `server/src/addie/mcp/property-tools.ts`* ### `list_properties` レジストリのパブリッシャープロパティをリストします。ソースタイプでフィルターしドメインで検索できます。 *Source: `server/src/addie/mcp/property-tools.ts`* ### `list_missing_properties` まだレジストリにない最もリクエストされたパブリッシャードメインをリストします。需要シグナルを表示します — 人々が探しているが私たちが持っていないプロパティ。 *Source: `server/src/addie/mcp/property-tools.ts`* ## agent\_conformance Addie の Socket Mode チャネル経由で AdCP コンプライアンスストーリーボードをユーザー自身の開発/ステージング MCP サーバーに対して実行します — 採用者から Addie へのアウトバウンド WebSocket、公開 DNS や ngrok は不要。ユーザーが開発中に自身の AdCP エージェントをテストしたいときに使います。ユーザーが WorkOS 組織にマップされていることが必要です。ツールはセッションバウンドトークンを発行し、次に接続された採用者エージェントに対してストーリーボードを実行します。 ### `issue_conformance_token` 採用者が `@adcp/sdk/server` ConformanceClient 設定に貼り付けて、開発/ステージング MCP サーバーを Addie のコンフォーマンスチャネルにアウトバウンドで接続する短命 JWT(1h TTL)を発行します。トークンは呼び出し元の WorkOS 組織にバインドされます。ユーザーが AdCP エージェントを構築していて、公開せずに Addie に開発環境に対してコンフォーマンス/コンプライアンステストを実行させたいときに使います。トークン、WebSocket URL、有効期限を返します。 *Source: `server/src/addie/mcp/conformance-tools.ts`* ### `run_conformance_against_my_agent` Socket Mode 経由でこの Addie セッションに接続された採用者 MCP サーバーに対してコンプライアンスストーリーボードを実行します。採用者はこの同じチャットセッションで発行されたトークンで `@adcp/sdk/server` ConformanceClient を起動していなければなりません — チャネルは WorkOS 組織 id でルーティングします。フェーズ/ステップの pass/fail/skipped ステータス、トリミングされたエラーテキスト、失敗時の id、expected、actual などのサニタイズされた失敗検証詳細を含む markdown レポートを返します。ユーザーがコンフォーマンスクライアントが `status=connected` を表示することを確認した後にこれを使います。 *Source: `server/src/addie/mcp/conformance-tools.ts`* ## adcp\_operations AdCP プロトコル操作を実行 - ドキュメントを発見、エージェントに対してタスクを実行、エージェントケイパビリティをチェック。メディアバイ、クリエイティブ、シグナル、ガバナンス、SI、ブランドプロトコルをカバーします。 ### `save_agent` 現在の組織、または `organization_id` / `organization_name` 経由で明示的に選択されたアクティブな組織に代わって、AgenticAdvertising.org レジストリにエージェントを登録します。エージェントを組織のメンバープロフィールに追加し、`/dashboard/agents` に表示します。新しいエージェントは `members_only` 可視性で着地します(他の有料 AgenticAdvertising.org メンバー — Professional, Builder, Member, Leader — に発見可能。ディレクトリや brand.json には公開リストされない)。公開リストするには、呼び出し元がダッシュボード経由でエージェントを昇格します。公開可視性には有料 AgenticAdvertising.org 階層(Professional, Builder, Member, Leader)とプライマリブランドドメインが必要です。認証モード: (1) none — 公開エージェント、認証情報なし。(2) 静的 `auth_token` + `auth_type`(`bearer` または `basic`、暗号化保存)。(3) マシン間の `oauth_client_credentials`(RFC 6749 §4.4)。インタラクティブ OAuth ユーザー認可には、認証フィールドなしで保存し、ユーザーに後でダッシュボードの **Authorize** フローを完了させます — `save_agent` はエンドユーザー OAuth 状態を収集しません。呼び出し元はエージェントの `type`(`brand`, `rights`, `measurement`, `governance`, `creative`, `sales`, `buying`, `signals`)を宣言しなければなりません — オーナーに尋ねてください、推測しないでください。サーバー側スマグル保護は、ケイパビリティスナップショットが利用可能なとき宣言されたタイプをそれに対して依然として検証します。ユーザーが MCP エンドポイントが認証を必要とする、非ルートパス(例: /adcp/mcp)に存在する、または保存後にオフラインと表示されると言う場合、基盤 URL を修正する間の liveness フォールバックとして `health_check_url` の設定を提案します。インテークスクリプトについてはルールの「Registering an Agent in the AgenticAdvertising.org Registry」セクションを参照してください。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `list_saved_agents` この組織に保存されたすべてのエージェントをリストします。エージェント URL、名前、タイプ、認証トークンが保存されているか(ただし実際のトークンは決して表示しない)を表示します。ユーザーが「どのエージェントを保存したか?」と尋ねるとき、または構成されたエージェントを見たいときにこれを使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `remove_saved_agent` 保存されたエージェントとその保存された認証トークンを削除します。ユーザーがエージェント構成を削除または忘れたいときにこれを使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `setup_test_agent` チームメイトが使えるよう、ユーザーの組織に公開 AdCP テストエージェント認証情報を保存します。ログインした任意のユーザーはこのステップなしに evaluate\_agent\_quality 経由で公開テストエージェントを直接既に使えます — 組織不要。これは認証情報を保存したいチームにのみ必要です。 *Source: `server/src/addie/mcp/member-tools.ts`* ## content コンテンツワークフローを管理 — ニュースソースを提案、委員会ドキュメントを追加または更新(管理者アクション) ### `propose_news_source` 業界監視のニュースソースとしてウェブサイトまたは RSS フィードを提案します。任意のコミュニティメンバーがソースを提案できます - 管理者がレビューして承認します。誰かが関連するアドテック、マーケティング、メディア出版物へのリンクを共有しニュースのため監視すべきと思うときにこれを使います。提案前に重複をチェックします。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `add_committee_document` 追跡のため委員会(ワーキンググループ、カウンシル、チャプター)に Google Docs ドキュメントを追加します。ドキュメントは自動的にインデックスされ要約されます。委員会メンバーとリーダーがドキュメントを追加できます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `update_committee_document` 委員会が追跡するドキュメントを更新します。タイトル、説明、URL、または featured ステータスを変更できます。委員会メンバーとリーダーがドキュメントを更新できます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `delete_committee_document` 委員会からドキュメントを削除します。ドキュメントはもはや追跡または表示されません。委員会リーダーのみがドキュメントを削除できます。 *Source: `server/src/addie/mcp/member-tools.ts`* ## billing (admin only) 課金と支払い操作を処理 - 支払いリンクを作成、請求書を送信、割引とプロモーションを管理、保留中の請求書をルックアップ ### `find_membership_products` 潜在的メンバーの利用可能なメンバーシップ製品を見つけます。 誰かが参加、メンバーシップ価格について尋ねるとき、またはメンバーになりたいときにこれを使います。 正しい製品を見つけるため会社タイプと概算収益について尋ねるべきです。 *Source: `server/src/addie/mcp/billing-tools.ts`* ### `create_payment_link` 認証されたメンバー自身の組織の Stripe チェックアウト支払いリンクを作成します。 リンクはサインインしたメンバーにのみ発行されます — 顧客メールとアイデンティティは認証されたセッションから取られ、呼び出し元供給の入力から決して取られません。メンバーは agenticadvertising.org でサインインしワークスペースを持たなければなりません。そうでない場合、拒否してまずサインアップに誘導します。 このツールは他の人や組織に代わって支払いリンクを生成できません。 *Source: `server/src/addie/mcp/billing-tools.ts`* ### `send_invoice` 認証されたメンバー自身の組織の請求書をプレビューし、送信前に金額と課金メールを確認できるようにします。連絡先メールと会社はサインインセッションから取られ、呼び出し元供給の入力から決して取られません。これを呼びメンバーが確認した後、送信には confirm\_send\_invoice を呼びます。 *Source: `server/src/addie/mcp/billing-tools.ts`* ### `send_payment_request` 見込み組織を見つけるか作成し、その製品をルックアップするか、レビュー用の請求書をドラフトするか、メンバーシップ招待を送ります。管理者はこのツールから支払いリンクを直接作成したり請求書を送ったりできません — それらの操作は受信者自身の認証されたセッションで、招待を受諾した後にのみ有効です。 アクション: * "lookup\_only": 組織を見つけるか作成し、適格製品をリスト。読み取り専用。 * "draft\_invoice": 請求書がどう見えるか(金額、割引)をプレビュー。Stripe 書き込みなし。 * "send\_invite": メンバーシップ招待トークンを作成し連絡先にメール。これは直接請求書送信では **ありません** — サインインしていない受信者に請求書や支払いリンクを発行する管理者パスはありません。受信者がサインインし、契約を受諾し、請求書またはチェックアウトがその認証されたセッションで発行されます — 決して管理者や幻覚メールの下でではありません。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `grant_discount` 組織に割引を付与します。Stripe クーポン/プロモコードを作成します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `remove_discount` 組織から割引を削除します。注: これは作成された Stripe クーポンを削除しません。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_discounts` アクティブな割引を持つ組織をリストします。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `create_promotion_code` マーケティングキャンペーン用のスタンドアロン Stripe プロモコードを作成します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `resend_invoice` オープンな請求書を再送信します。invoice\_id(既知の場合)または company\_name(保留中の請求書をルックアップ)のいずれかを提供します。会社にちょうど 1 つのオープンな請求書がある場合、自動的に再送信されます。請求書が別のメールに行く必要がある場合、まず update\_billing\_email を使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `update_billing_email` Stripe 顧客の課金メールを更新します。請求書が別のメールアドレス(例: 買掛金)に行く必要があるときに使います。org\_id または直接 customer\_id でルックアップできます。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `preview_org_stripe_customer_update` 組織を別の Stripe 顧客に再リンクすることをプレビューします。組織と Stripe 顧客を検証し、現在と提案された顧客 ID を表示し、preview\_token を返します。変更は書き込みません。確認前に常に管理者にプレビューを表示します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `confirm_org_stripe_customer_update` 以前プレビューされた組織 Stripe 顧客再リンクを確認します。このツールは生の Stripe 顧客 ID を受け入れません。明示的な管理者承認の後、検証された preview\_token のみをコミットします。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_pending_invoices` 保留中(未払い)の請求書を持つすべての組織をリストします。 管理者が未払い請求書または組織全体の支払いステータスについて尋ねるときにこれを使います。 オープンまたはドラフト請求書を持つ組織のリストを返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_account` 任意の組織の完全なアカウントビューを取得: ライフサイクルステージ、メンバーシップステータス、エンゲージメントメトリック、パイプライン情報、拡充データ。任意の会社ルックアップに使います。一致する組織がない場合、users テーブルにフォールバック — 一致するメールドメインでサインアップしたがまだ組織を作成/参加していない人を表示(インバウンドウェブサイトサインアップに典型的)。 *Source: `server/src/addie/mcp/admin-tools.ts`* ## events 今後のイベントを閲覧、イベント登録をチェック、イベント詳細を取得、誰が来るかを見る、イベントへの関心を登録 — すべてのメンバーに利用可能 ### `list_events` ユーザー向けにパーソナライズされた AAO イベントをリストします。表示: * ユーザーが既に登録しているイベント * ユーザーがメンバーである地域チャプターのイベント * ユーザーが関心を示した業界集会(CES、Cannes Lions など)のイベント * 主要なグローバルサミット(全メンバーに公開) バーチャルウェビナー(それらは教育コンテンツ)は含みません。 過去のイベントについて尋ねられたら、include\_past=true を設定します。 今後のイベントやまもなく起こることについて尋ねられたら、デフォルトを使います。 ユーザーが地域チャプターにいないか業界イベントに関心を示していない場合、 レスポンスは位置を共有するか業界集会グループに参加することを提案します。 *Source: `server/src/addie/mcp/event-tools.ts`* ### `get_event_details` 登録数とウェイトリストを含む特定イベントの詳細を取得します。誰かが特定イベントについて尋ねるときにこれを使います。 *Source: `server/src/addie/mcp/event-tools.ts`* ### `list_event_attendees` イベントに登録されている人をリストします。誰かが「\[event] に誰が来るか?」「誰が登録したか?」「参加者リスト」「誰がそこにいるか?」と尋ねるときに使います。公開イベントの登録参加者の名前を表示します。 *Source: `server/src/addie/mcp/event-tools.ts`* ### `register_event_interest` 現在のユーザーのイベントへの関心を登録します。誰かがイベントについて通知されたい、ウェイトリストに追加されたい、または正式登録せずに関心を表明したいときに使います。 *Source: `server/src/addie/mcp/event-tools.ts`* ## meetings ミーティングをスケジュール、リスト、更新、キャンセル - 出席者を追加または削除、RSVP、繰り返しシリーズを管理、カレンダー招待と Zoom リンクを処理 ### `schedule_meeting` 新しいワーキンググループミーティングをスケジュールします。誰かがミーティング、コール、議論をスケジュールするよう依頼するときにこれを使います。 ミーティングは Zoom リンクで作成されます。1 回限りのミーティングでは、カレンダー招待がデフォルトでワーキンググループメンバーに送られます。 繰り返しミーティングでは、カレンダー招待がデフォルトでワーキンググループメンバーに送られます(1 回限りのミーティングと同じ)。 ユーザーがワーキンググループに関連付けられたチャネルにいる場合、working\_group\_slug を省略でき、チャネルコンテキストから推論されます。 繰り返しミーティングには、freq、interval、count、byDay を伴う recurrence パラメーターを使います。 必須: title、start\_time(タイムゾーンサフィックスなしの ISO 形式 - それには timezone パラメーターを使う) オプション: working\_group\_slug(チャネルから自動検出)、description、agenda、duration\_minutes、timezone、topic\_slugs、recurrence 重要: start\_time には、ユーザーのタイムゾーンの時刻を Z サフィックスなしで提供します。例えばユーザーが「2pm ET」と言う場合、"2026-01-15T14:00:00"("2026-01-15T14:00:00Z" ではない)を使います。timezone パラメーター(デフォルト: America/New\_York)は start\_time がどのタイムゾーンにあるかを指定します。 これが処理する例のプロンプト: * 「次の火曜午後 2 時 ET に技術ワーキンググループコールをスケジュール」 * 「1 月 15 日午後 3 時 PT に bylaws 小委員会ミーティングをセットアップ」 * 「今後 8 週間、毎週木曜午後 3 時に週次ガバナンスコールをスケジュール」 * 「隔週火曜午後 2 時に繰り返しクリエイティブ WG ミーティングを作成」 *Source: `server/src/addie/mcp/meeting-tools.ts`* ### `list_upcoming_meetings` 今後のミーティングをリストします。誰かがスケジュールされたミーティング、今後の予定、ミーティングカレンダーについて尋ねるときにこれを使います。また add\_meeting\_attendee、cancel\_meeting、update\_meeting のため meeting\_id が必要なときの最初のステップとしてこれを使います。ユーザーがメンバーである委員会にフィルターするには my\_committees\_only を使います。 *Source: `server/src/addie/mcp/meeting-tools.ts`* ### `get_my_meetings` ユーザーの今後のミーティングを取得します。誰かが「どのミーティングがあるか?」または「カレンダーに何があるか?」と尋ねるときにこれを使います。 *Source: `server/src/addie/mcp/meeting-tools.ts`* ### `get_meeting_details` 出席者と RSVP ステータスを含む特定ミーティングの詳細を取得します。 *Source: `server/src/addie/mcp/meeting-tools.ts`* ### `rsvp_to_meeting` ミーティングに RSVP します。誰かがミーティングに出席したい、または辞退する必要があると言うときにこれを使います。 *Source: `server/src/addie/mcp/meeting-tools.ts`* ### `cancel_meeting` スケジュールされたミーティングをキャンセルします。すべての出席者にキャンセル通知を送ります。 *Source: `server/src/addie/mcp/meeting-tools.ts`* ### `cancel_meeting_series` 繰り返しミーティングシリーズをキャンセルします。シリーズのすべての今後のミーティング(Zoom + カレンダー)をキャンセルしシリーズレコードをアーカイブします。誰かが繰り返しシリーズを完全に停止したいときにこれを使います。 *Source: `server/src/addie/mcp/meeting-tools.ts`* ### `update_meeting` 既存ミーティングの詳細を更新します。誰かがスケジュールされたミーティングの時刻、タイトル、説明、アジェンダを変更したいときにこれを使います。 これはデータベース、Zoom(構成されている場合)、Google Calendar のミーティングを更新します。 重要: start\_time には、ユーザーのタイムゾーンの時刻を Z サフィックスなしで提供します(schedule\_meeting と同じ)。 *Source: `server/src/addie/mcp/meeting-tools.ts`* ### `add_meeting_attendee` 既存ミーティングにメールで人を追加します。人ごとに 1 回これを呼びます。まず list\_upcoming\_meetings を使って meeting\_id を取得します。 例: 「Karen、Brian、Jonathan をコールに追加」は以下を要求: 1. meeting\_id を見つけるため list\_upcoming\_meetings 2. Karen のため add\_meeting\_attendee 3. Brian のため add\_meeting\_attendee 4. Jonathan のため add\_meeting\_attendee add\_to\_series が true のとき、同じシリーズのすべての今後のミーティングに彼らを追加します。 *Source: `server/src/addie/mcp/meeting-tools.ts`* ### `update_topic_subscriptions` ワーキンググループ内のユーザーのミーティングトピックサブスクリプションを更新します。誰かがどのタイプのミーティングに招待されるかを変更したいときにこれを使います。 *Source: `server/src/addie/mcp/meeting-tools.ts`* ### `manage_committee_topics` ワーキンググループ/委員会のトピックを管理します。トピックはミーティングを整理し招待をフィルターするのに役立ちます。各トピックはサブグループ議論用の独自の Slack チャネルをオプションで持てます。現在のトピックを見るには action='list'、新しいトピックを作成するには action='add'、既存トピックを変更するには action='update'、トピックを削除するには action='remove' を使います。 *Source: `server/src/addie/mcp/meeting-tools.ts`* ## committee\_leadership あなたがリードする委員会を管理: 共同リーダーとワーキンググループ、カウンシル、チャプター、業界集会のイベント管理 ### `add_committee_co_leader` あなたがリードする委員会に共同リーダーを追加します。委員会リーダーが委員会のリードを助ける別の人を追加したいときにこれを使います。 ワーキンググループ、カウンシル、チャプター、業界集会で機能します。 重要: 既にリーダーである委員会にのみ共同リーダーを追加できます。 使用例: * 「Sarah を India Chapter の共同リーダーとして追加」 * 「Creative Working Group のリードを助けるため John を追加したい」 * 「Maria を CTV Council リーダーシップに追加」 *Source: `server/src/addie/mcp/committee-leader-tools.ts`* ### `remove_committee_co_leader` あなたがリードする委員会から共同リーダーを削除します。その人はメンバーのまま残りますがリーダーシップアクセスを失います。 ワーキンググループ、カウンシル、チャプター、業界集会で機能します。 重要: あなたがリーダーである委員会からのみ共同リーダーを削除できます。 自分自身をリーダーとして削除できません(それには管理者に連絡)。 *Source: `server/src/addie/mcp/committee-leader-tools.ts`* ### `list_committee_co_leaders` あなたがリードする委員会のすべての現在のリーダーをリストします。誰がリーダーシップアクセスを持つかを表示します。 ワーキンググループ、カウンシル、チャプター、業界集会で機能します。 *Source: `server/src/addie/mcp/committee-leader-tools.ts`* ### `list_working_groups` AgenticAdvertising.org のアクティブな委員会をリストします。タイプでフィルターできます: ワーキンググループ(技術)、カウンシル(業界バーティカル)、チャプター(地域)。公開グループを全員に表示し、メンバーには非公開グループを含めます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `create_event` 新しい AAO イベントを作成します。誰かがミートアップ、ウェビナー、サミット、ワークショップを作成するよう依頼するときにこれを使います。 イベントは Luma(登録用)と AAO ウェブサイトの両方で作成されます。 共有用の Luma URL と AAO イベントページ URL を返します。 必須: title、start\_time(ISO 形式)、event\_type オプション: description、end\_time、timezone、位置詳細、virtual\_url、max\_attendees *Source: `server/src/addie/mcp/event-tools.ts`* ### `update_event` 既存イベントを更新します。説明、キャパシティ、タイミングなどのイベント詳細を変更するためにこれを使います。 *Source: `server/src/addie/mcp/event-tools.ts`* ### `manage_event_registrations` イベント登録を管理 - 登録を表示、ウェイトリストの出席者を承認、または出席者リストをエクスポート。 *Source: `server/src/addie/mcp/event-tools.ts`* ### `check_person_event_status` イベントでの特定の人のステータスをルックアップします。誰かが「\[person] は \[event] に招待されているか?」「\[person] は \[event] に出席したか?」「\[person] の RSVP ステータスは?」などと尋ねるときに使います。 招待リスト、登録、出席記録全体で名前またはメールで検索します。 招待ステータス、登録ステータス、出席したかを返します。 *Source: `server/src/addie/mcp/event-tools.ts`* ### `invite_to_event` イベントに人を招待します。招待リストに追加し登録レコードを作成します。 誰かが「\[person] を \[event] に招待」「\[person] を Foundry ゲストリストに追加」などと言うときに使います。 draft\_message が true の場合、Slack またはメール経由で送れる提案アウトリーチメッセージを返します。 招待は即座に記録されます。アウトリーチメッセージは管理者がレビューするドラフトです。 *Source: `server/src/addie/mcp/event-tools.ts`* ## admin (admin only) 管理操作 - 見込み客、組織、フィード、エスカレーション、ユーザーロール、委員会/ワーキンググループリーダーシップ、イベント管理(イベント作成/更新、登録管理、招待、出席者リスト)、メンバーインサイトとエンゲージメント分析、コミュニティ全体のエンゲージメントランキング、ブランドロゴレジストリレビューキュー(保留中ロゴの承認/拒否)、メンバーのディレクトリプロフィールまたはロゴを代わりに編集(管理者のみ)を管理 ### `create_event` 新しい AAO イベントを作成します。誰かがミートアップ、ウェビナー、サミット、ワークショップを作成するよう依頼するときにこれを使います。 イベントは Luma(登録用)と AAO ウェブサイトの両方で作成されます。 共有用の Luma URL と AAO イベントページ URL を返します。 必須: title、start\_time(ISO 形式)、event\_type オプション: description、end\_time、timezone、位置詳細、virtual\_url、max\_attendees *Source: `server/src/addie/mcp/event-tools.ts`* ### `update_event` 既存イベントを更新します。説明、キャパシティ、タイミングなどのイベント詳細を変更するためにこれを使います。 *Source: `server/src/addie/mcp/event-tools.ts`* ### `manage_event_registrations` イベント登録を管理 - 登録を表示、ウェイトリストの出席者を承認、または出席者リストをエクスポート。 *Source: `server/src/addie/mcp/event-tools.ts`* ### `check_person_event_status` イベントでの特定の人のステータスをルックアップします。誰かが「\[person] は \[event] に招待されているか?」「\[person] は \[event] に出席したか?」「\[person] の RSVP ステータスは?」などと尋ねるときに使います。 招待リスト、登録、出席記録全体で名前またはメールで検索します。 招待ステータス、登録ステータス、出席したかを返します。 *Source: `server/src/addie/mcp/event-tools.ts`* ### `invite_to_event` イベントに人を招待します。招待リストに追加し登録レコードを作成します。 誰かが「\[person] を \[event] に招待」「\[person] を Foundry ゲストリストに追加」などと言うときに使います。 draft\_message が true の場合、Slack またはメール経由で送れる提案アウトリーチメッセージを返します。 招待は即座に記録されます。アウトリーチメッセージは管理者がレビューするドラフトです。 *Source: `server/src/addie/mcp/event-tools.ts`* ### `list_pending_invoices` 保留中(未払い)の請求書を持つすべての組織をリストします。 管理者が未払い請求書または組織全体の支払いステータスについて尋ねるときにこれを使います。 オープンまたはドラフト請求書を持つ組織のリストを返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_account` 任意の組織の完全なアカウントビューを取得: ライフサイクルステージ、メンバーシップステータス、エンゲージメントメトリック、パイプライン情報、拡充データ。任意の会社ルックアップに使います。一致する組織がない場合、users テーブルにフォールバック — 一致するメールドメインでサインアップしたがまだ組織を作成/参加していない人を表示(インバウンドウェブサイトサインアップに典型的)。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `add_prospect` 追跡する新しい見込み組織を追加します。まず get\_account を使って会社が存在しないことを確認します。できるだけ多くの情報を捕捉: 名前、ドメイン、連絡先詳細、関心についてのノート。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `update_prospect` 既存の見込み客についての情報を更新します。ノートを追加、ステータスを変更、連絡先情報を更新、または関心レベルを設定するためにこれを使います。重要: 興奮、リソースコミットメント、参加意図を示すノートを追加するとき、interest\_level もそれに応じて設定します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `enrich_company` Lusha を使って会社を調査しファーモグラフィックデータ(収益、従業員数、業界など)を取得します。ドメインまたは会社名で使えます。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `query_prospects` 異なるビュー全体で見込み客をクエリします。視点を切り替えるには `view` を使います: "all"(デフォルト)、"my\_engaged"、"my\_followups"、"unassigned"、"addie\_pipeline"。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `prospect_search_lusha` 基準に一致する潜在的見込み客のため Lusha のデータベースを検索します。業界、規模、位置に基づいてアプローチする新しい会社を見つけるためにこれを使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `search_industry_feeds` RSS 業界フィードを検索してリストします。名前、URL、カテゴリーでフィードを見つける、または注意が必要なエラーのあるフィードを見るためにこれを使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `add_industry_feed` 業界ニュースを監視する新しい RSS フィードを追加します。フィード URL と名前を提供します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_feed_stats` 業界フィードについての統計を取得 - 合計フィード、アクティブフィード、収集された記事、処理ステータスなど。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_platform_stats` 管理者プラットフォーム統計を取得: 重複排除された人の合計、加えてライフサイクル階層、メンバーシップ階層、サブスクリプションステータスごとの組織数。プラットフォームレベルのメンバー、ユーザー、または組織数に使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_feed_proposals` コミュニティメンバーが提出した保留中のフィード提案をリストします。どのニュースソースが提案されたかをレビューするためにこれを使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `approve_feed_proposal` フィード提案を承認しフィードを作成します。最終フィード名と URL を提供しなければなりません(実際の RSS フィードを見つけた場合、提案された URL と異なりうる)。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `reject_feed_proposal` フィード提案を拒否します。提案者と共有できる理由をオプションで提供します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `add_media_contact` Slack ユーザーを既知のメディア連絡先(ジャーナリスト、記者、編集者)としてフラグします。このユーザーからのメッセージは特に注意して扱われ、機密トピックは逸らされます。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_flagged_conversations` 機密トピック検出のためフラグされた会話をリストします。これらは適切な処理を保証するため人間レビューが必要です。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `review_flagged_conversation` フラグされた会話をレビュー済みとしてマークします。フラグされたメッセージを見てフォローアップアクションが必要か判断した後にこれを使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `create_chapter` Slack チャネル付きの地域チャプターを作成します。創設メンバーをチャプターリーダーとして設定します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_chapters` すべての地域チャプターをそのメンバー数と Slack チャネルとともにリストします。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `create_industry_gathering` カンファレンス/トレードショーの業界集会を作成します。イベント終了後に自動アーカイブします。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_industry_gatherings` すべての業界集会をその日付、位置、メンバー数とともにリストします。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_working_groups` AgenticAdvertising.org のアクティブな委員会をリストします。タイプでフィルターできます: ワーキンググループ(技術)、カウンシル(業界バーティカル)、チャプター(地域)。公開グループを全員に表示し、メンバーには非公開グループを含めます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `get_working_group` 説明、リーダー、メンバー数、最近の投稿を含む特定のワーキンググループの詳細を取得します。グループスラッグ(URL フレンドリーな名前)を使います。名前、組織、メールを含む完全なメンバーリストを得るには include\_members: true を渡します(非公開グループは管理者のみ)。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `add_committee_leader` ユーザーを委員会のリーダーとして追加します。リーダーは投稿、イベント、メンバーを管理できます。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `remove_committee_leader` ユーザーを委員会リーダーシップから削除します。ユーザーは通常メンバーのまま残ります。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_committee_leaders` 委員会のすべてのリーダーをリストします。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `merge_organizations` 重複する組織レコードをマージします。破壊的、元に戻せません。まず preview=true でプレビューします。両方の組織が Stripe 顧客を持つ場合、stripe\_customer\_resolution を指定しなければなりません。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `find_duplicate_orgs` 名前またはドメインで重複組織を検索します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `check_domain_health` データ品質問題のためドメインヘルスをチェック: 孤立ドメイン、コンフリクト、ミスアラインしたユーザー。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `manage_organization_domains` 組織の検証済みドメインを追加、削除、リスト、プライマリ設定、または再照合します。WorkOS に同期します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `update_org_member_role` 組織内のユーザーのロールを更新します。メンバーの権限を変更するためにこれを使います。 一般的なシナリオ: * ユーザーがメンバーシップを支払ったがチームを管理できない → 管理者に昇格 * 誰かにチームメンバーを招待する能力を付与する必要 → 管理者に昇格 * ユーザーが組織の完全な制御を持つべき → オーナーに昇格 ロール: member(デフォルト)、admin(チーム管理可能)、owner(完全な制御) *Source: `server/src/addie/mcp/admin-tools.ts`* ### `claim_prospect` 見込み客の所有権を主張します。現在の人間ユーザーを割り当てるには owner\_type "self"(デフォルト)を、Addie を SDR オーナーとして割り当てるには "addie" を使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `triage_prospect_domain` メールドメインを潜在的見込み客として評価します。Addie は会社を調査し、フィットを判断し、オプションで見込みレコードを作成します。誰かがまだシステムにない会社について言及するときにこれを使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `suggest_prospects` 見込みリストに追加する会社を提案します。マップされていないドメインと Lusha 一致を見つけます。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `set_reminder` 見込み客のリマインダー/次のステップを設定します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `my_upcoming_tasks` 今後のタスクとリマインダーをリストします。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `complete_task` タスク/リマインダーを完了としてマークします。会社名、組織 ID、またはすべての期限超過タスクを一度に完了できます。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `log_conversation` 見込み客/メンバーとの会話またはインタラクションをログします。分析して学びを抽出します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_member_search_analytics` メンバー検索と紹介についての分析を取得します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_organizations_by_users` ユーザー数(ウェブサイト + Slack のみ)でランク付けされた組織をリストします。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_users_by_engagement` コミュニティポイント(イベント、ワーキンググループ、コンテンツ、接続、GitHub から獲得)でランク付けされたコミュニティメンバーをリストします。関係ステージ、組織、アクションタイプごとのポイント内訳を表示します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_slack_users_by_org` 特定組織の Slack ユーザーをリストします。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_paying_members` サブスクリプションレベル($50K ICL、$10K corporate、\$2.5K SMB、individual)でグループ化されたすべての有料メンバーをリストします。デフォルトで個人メンバーを含みます。corporate のみには include\_individual: false を渡します。各エントリーはプライマリ連絡先の名前とメールを含みます。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `resend_invoice` オープンな請求書を再送信します。invoice\_id(既知の場合)または company\_name(保留中の請求書をルックアップ)のいずれかを提供します。会社にちょうど 1 つのオープンな請求書がある場合、自動的に再送信されます。請求書が別のメールに行く必要がある場合、まず update\_billing\_email を使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `update_billing_email` Stripe 顧客の課金メールを更新します。請求書が別のメールアドレス(例: 買掛金)に行く必要があるときに使います。org\_id または直接 customer\_id でルックアップできます。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `add_working_group_member` ユーザーをワーキンググループ、カウンシル、チャプター、または業界集会のメンバーとして追加します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `remove_working_group_member` ユーザーをワーキンググループ、カウンシル、チャプター、または業界集会から削除します。ユーザーは削除されず非アクティブ化されます。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `rename_working_group` ワーキンググループ、チャプター、または委員会をリネームします。表示名とオプションでスラッグを更新します。チャプターや WG をリネームする必要があるとき(例: 「Germany Chapter」→「DACH Chapter」)にこれを使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_missing_brands` まだレジストリにない最もリクエストされたブランドドメインをリストします。需要シグナルを表示します — 人々が探しているが私たちが持っていないブランド。 *Source: `server/src/addie/mcp/brand-tools.ts`* ### `list_missing_properties` まだレジストリにない最もリクエストされたパブリッシャードメインをリストします。需要シグナルを表示します — 人々が探しているが私たちが持っていないプロパティ。 *Source: `server/src/addie/mcp/property-tools.ts`* ### `get_outreach_stats` アウトリーチパフォーマンスメトリックを取得: 送られたメッセージ、応答率。「アウトリーチはどうか?」またはエンゲージメントパフォーマンスについて尋ねられたときにこれを使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_outreach_history` アウトリーチメッセージ履歴を取得します。slack\_user\_id 付きで、その人の目標と応答を伴う完全なアウトリーチタイムラインを返します。なしで、最近のシステム全体のアウトリーチを返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `send_outreach` Slack ユーザーへのアウトリーチをトリガーします。まず適格性をチェックします。送信せずに適格性をチェックするには dry\_run=true を設定します。完全な人のコンテキストには lookup\_person を使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `lookup_person` Slack ユーザー ID、メール、または名前で人をルックアップします。関係ステージ、センチメント、連絡適格性、最近のアクティビティ、組織を返します。人レベルのコンテキスト(組織レベルの get\_account に対して)に使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_action_items` アウトリーチパイプラインからのオープンなアクションアイテム — ナッジ、ウォームリード、モメンタムシグナル、フォローアップ — を取得します。今日注意が必要なものを表示します。「誰にフォローアップが必要か?」または「パイプラインに何があるか?」に答えるために使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_pending_brand_logos` レジストリ全体でモデレーターレビューを待つブランドロゴをリストします。ロゴ ID、ドメイン、アップローダーメール、タグ、保留期間を返します。レジストリ承認キューをトリアージするか「どのロゴが承認を必要とするか?」に答えるためにこれを使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_brand_logos` 1 つの特定ブランドドメインのすべてのロゴ — 保留中、承認済み、拒否、削除 — をリストして、ブランドのロゴが表示されているかいないかを調査します。手元にドメインがあるときにこれを使います。すべてのドメイン全体のグローバルモデレーションキューには、代わりに list\_pending\_brand\_logos を使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `review_brand_logo` 保留中のブランドロゴを承認、拒否、または削除します。承認するとブランドの会社リスティングで可視になります。拒否すると隠されます。削除するとトゥームストーン化します。未検証ブランドの承認時にマニフェスト再構築をトリガーします。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `update_member_logo` メンバーのディレクトリプロフィールのロゴ URL を設定または更新します。公開ホストされた HTTPS ロゴ URL と、彼らを識別するメンバーの org\_name またはプロフィールスラッグのいずれかが必要です。なければブランドエントリーを作成し、あれば既存のものを更新します。 これをロゴファイルのアップロードやホストに使わないでください — URL は既に公開アクセス可能でなければなりません。更新後、関連サポートチケットをクローズするため resolve\_escalation を使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `update_member_profile` メンバーのディレクトリプロフィールのフィールドを更新します。org\_name またはスラッグ(完全一致が必要)でメンバーを識別します。以下の任意の組み合わせを受け入れます: 説明、タグライン、連絡先情報、ソーシャルリンク、本社、市場、オファリング、可視性設定。 ロゴ変更には、代わりに update\_member\_logo を使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `transfer_brand_ownership` ブランドドメインの所有権を 1 つの組織から別の組織に移転します。監査のためリビジョンを記録します。帯域外検証(買収文書、サポートチケット、法的通信)が新しい組織がドメインを所有すべきと確認した後に使います。 未検証の紛争を解決するために使わないでください — それらにはエスカレーションキューを使います。これは確認された移転のみのためです。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_orphaned_brands` 孤立状態のブランドドメインをリストします — 以前のオーナーが放棄しマニフェストが採用のため保持されている。放棄されたブランドを監査し、どれが古いデータを持つかを見て、管理者クリーンアップをトリガーするか潜在的採用者にアプローチするためにこれを使います。以前のオーナー組織名 + id、放棄時期、管理者が一目で決定できるマニフェストプレビューを返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `create_contact` メールで人の連絡先レコードを作成または見つけます。email\_contacts(CRM)と person\_relationships(エンゲージメント追跡)に書き込みます。連絡先が新規か、組織に自動一致したかを返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ## outreach (admin only) SDR アウトリーチ操作 — アウトリーチ統計を表示、履歴をチェック、アウトリーチを送信、人をルックアップ、アクションアイテムを管理(管理者のみ) ### `get_outreach_stats` アウトリーチパフォーマンスメトリックを取得: 送られたメッセージ、応答率。「アウトリーチはどうか?」またはエンゲージメントパフォーマンスについて尋ねられたときにこれを使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_outreach_history` アウトリーチメッセージ履歴を取得します。slack\_user\_id 付きで、その人の目標と応答を伴う完全なアウトリーチタイムラインを返します。なしで、最近のシステム全体のアウトリーチを返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `send_outreach` Slack ユーザーへのアウトリーチをトリガーします。まず適格性をチェックします。送信せずに適格性をチェックするには dry\_run=true を設定します。完全な人のコンテキストには lookup\_person を使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `lookup_person` Slack ユーザー ID、メール、または名前で人をルックアップします。関係ステージ、センチメント、連絡適格性、最近のアクティビティ、組織を返します。人レベルのコンテキスト(組織レベルの get\_account に対して)に使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_action_items` アウトリーチパイプラインからのオープンなアクションアイテム — ナッジ、ウォームリード、モメンタムシグナル、フォローアップ — を取得します。今日注意が必要なものを表示します。「誰にフォローアップが必要か?」または「パイプラインに何があるか?」に答えるために使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_account` 任意の組織の完全なアカウントビューを取得: ライフサイクルステージ、メンバーシップステータス、エンゲージメントメトリック、パイプライン情報、拡充データ。任意の会社ルックアップに使います。一致する組織がない場合、users テーブルにフォールバック — 一致するメールドメインでサインアップしたがまだ組織を作成/参加していない人を表示(インバウンドウェブサイトサインアップに典型的)。 *Source: `server/src/addie/mcp/admin-tools.ts`* ## collaboration 他の AgenticAdvertising.org メンバーにダイレクトメッセージを送り、会話コンテキストを転送し、コミュニティ全体でコラボレーションします ### `send_member_dm` Slack で別の AgenticAdvertising.org メンバーにダイレクトメッセージを送ります。 ユーザーが明示的に以下を依頼するときにこれを使います: * 彼らに代わって別のメンバーにアプローチ * フィードバックのため会話サマリーを誰かに転送 * 特定の人にフォローアップまたは通知を送る メッセージは誰が送信を依頼したかを示す帰属を含みます。 コンテキストとして現在の会話のサマリーをオプションで含められます。 受信者をメール(推奨)、名前(曖昧性解消が必要かも)、または Slack ユーザー ID でルックアップします。 ユーザーが誰かにメッセージするよう明示的にリクエストしない限り使わないでください。 *Source: `server/src/addie/mcp/collaboration-tools.ts`* ## certification AdCP Academy — トラックをリスト、モジュールを教える、演習を実行、プレースメント評価、学習者の進捗を追跡 ### `list_certification_tracks` すべての AdCP 認定トラックと各での学習者の進捗をリストします。トラック名、説明、モジュール数、完了ステータスを返します。 *Source: `server/src/addie/mcp/certification-tools.ts`* ### `get_certification_module` モジュールを開始せずにそのコンテンツをプレビューします。読み取り専用閲覧のためレッスンプラン、演習、評価基準を返します。進捗を記録せず前提条件をチェックしません。学習者が実際にモジュールを受講したいときは代わりに start\_certification\_module を使います。 *Source: `server/src/addie/mcp/certification-tools.ts`* ### `start_certification_module` 認定モジュールの教授を開始します。任意のモジュールコンテンツを教える、デモを実行する、またはモジュールトピックについての質問に答える前に呼ばれなければなりません。これはオプションではありません — モジュールを開始せずに教えることは進捗が追跡されず、実演が記録されず、学習者がクレジットを得ないことを意味します。まずこれを呼び、次に返されたレッスンプランを使って教えます。学習者を開始済みとして記録し、前提条件とメンバーシップをチェックし、教授指示と評価基準を伴うレッスンプランを返します。 *Source: `server/src/addie/mcp/certification-tools.ts`* ### `complete_certification_module` 認定モジュールを完了としてマークします。学習者がすべての学習目標の習得を実演したときにのみ呼びます。ギャップがある場合、教え続けます — 失敗はなく、「まだ準備ができていない」だけです。あなたの仕事は彼らをそこに到達させることで、判定することではありません。すべての目標を理解していると確信したとき、内部評価スコアでこれを呼びます。学習者はこれらのスコアを決して見ません — それらは管理者分析と品質較正のためだけです。 *Source: `server/src/addie/mcp/certification-tools.ts`* ### `get_learner_progress` すべての認定モジュールとトラック全体で現在の学習者の進捗を取得します。どのモジュールが完了、進行中、未開始か、加えて獲得した証明書を表示します。 *Source: `server/src/addie/mcp/certification-tools.ts`* ### `test_out_modules` プレースメント評価が学習者が既に知識を持つことを確認した後、モジュールをテストアウト済みとしてマークします。徹底的な評価を実施した後にのみこれを呼びます — 表面的な馴染みだけでなく、モジュールトピックごとに探るような質問をします。スペシャリストまたはビルドプロジェクトモジュール(任意の S トラックモジュール、B4、C4、D4)を決してテストアウトしません。正式なコースワークが完了していないためスコアを授与しませんが、進級の前提条件を満たします。 *Source: `server/src/addie/mcp/certification-tools.ts`* ### `start_certification_exam` スペシャリストディープダイブモジュール(S1: Media Buy、S2: Creative、S3: Signals、S4: Governance、S5: Sponsored Intelligence)を開始します。学習者は Practitioner 資格を保持していなければなりません。集大成形式、ラボ演習、評価基準を返します。あなた(Sage)は組み合わされたハンズオンラボと適応試験を実施します — 仕様に対して学習者を技術的に評価します。 *Source: `server/src/addie/mcp/certification-tools.ts`* ### `complete_certification_exam` スペシャリスト集大成を確定します。学習者が習得を実演した場合(各次元で内部スコア 70%+)、スペシャリスト資格を授与し Certifier バッジ発行をトリガーします。まだ準備ができていない場合、さらなる作業が必要な領域を返します — 教え続けます。ラボフェーズと試験フェーズの両方が完了するまで呼ばないでください。学習者が早く止めるよう依頼した場合は呼ばないでください。学習者とスコアを決して共有しません。 *Source: `server/src/addie/mcp/certification-tools.ts`* ## 常時利用可能 これらのツールはルーターの意図にかかわらずすべての会話で到達可能です。認証済みと匿名の両方のユーザーが、ハンドラーが許可するとき使えます。 ### `escalate_to_admin` 自分で満たせないリクエストを人間管理者にエスカレートします。 以下のときにこれを使います: * ユーザーがあなたにツールがないアクション(チャネルへの投稿、issue の作成、物事のリネーム)の実行を依頼 * リクエストが管理者の判断、アカウントアクセス、またはあなたが実行できない人間のアクションを必要とする * トピックが真に機密(法務、コンプライアンス、対立的、論争的政治) * 利用可能なツールで助けようとして失敗した 以下には使いません: * あなたのツールやルールファイル(knowledge.md、behaviors.md)で答えられる質問 * コミュニティフィットの質問(「私の背景はワーキンググループに合うか?」) — knowledge.md と behaviors.md のワーキンググループマッピングを使って直接答える * クレジットカードまたは請求書での任意の階層のアップグレード日割りを含む日常メンバーシップ価格 — knowledge.md の FAQ がこれをカバー。返金、周期外クレジット、カスタム契約、通貨変更のみエスカレート * 各部分が独立して答えられるマルチパート質問 — まず分解し、部分に答え、バンドルをエスカレートしない(constraints.md の「Decompose bundled questions」を参照) * 「Complex」と「sensitive」は魔法の言葉ではありません。バンドルまたはマルチドメインの質問は自動的に Complex ではありません。エスカレート前に各部分が本当にあなたの知識やケイパビリティの外にあるかチェックします * 管理者の注意を必要としないもの * 一般的な会話 エスカレート前にユーザーと確認 — まずユーザーの同意を得なければなりません: * ユーザーにこれのツールがないことを伝え、何をエスカレートするか説明 * チームに渡してほしいか尋ねる * ユーザーがエスカレートを望むと確認した後にのみこのツールを呼ぶ このツールを呼ぶ前に — エスカレーションを実行可能にする十分なコンテキストを集めます: * リクエストが曖昧な場合、まず明確化の質問をする * リクエストが誰からかを確認: 名前と組織 * チームがフォローアップできるよう常にメールアドレスや Slack ハンドルを収集。ユーザーが提供していない場合、エスカレート前に尋ねる * 誰かが別の人に代わって尋ねている場合、その人の名前と連絡先詳細をサマリーに捕捉 * 関連コンテキスト(タイムライン、緊急性、既に試したこと)を含める エスカレートするとき、助けられる人間に渡していることをユーザーに正直に伝えます。 *Source: `server/src/addie/mcp/escalation-tools.ts`* ### `get_escalation_status` 現在のユーザーのため以前エスカレートされたサポートリクエストのステータスをチェックします。 ユーザーが以前のリクエスト、チケット、または誰かがフォローアップしたかのステータスについて尋ねるときにこれを使います。 現在のステータスと任意の解決ノートを伴う彼らのエスカレーションのリストを返します。 *Source: `server/src/addie/mcp/escalation-tools.ts`* ### `get_account_link` ユーザーの Slack アカウントを AgenticAdvertising.org アカウントと接続するリンクを取得します。ユーザーのアカウントがリンクされておらずメンバー機能にアクセスしたいときにこれを使います。重要: ツール出力全体をユーザーと共有 - それは必要なクリック可能なサインインリンクを含みます。ユーザーがリンクをクリックしてサインインし、アカウントが自動的に接続されます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `capture_learning` ユーザーが共有した価値ある知識または視点を捕捉します。 ユーザーが以下を共有するときにこれを使います: * 業界に関する戦略的視点 * 採用障壁または実装経験 * AdCP または AgenticAdvertising.org についてのフィードバック * 市場インテリジェンスまたは競合インサイト * ユースケースまたは新規アプリケーション これはチームがコミュニティ会話から学び Addie の知識を改善するのに役立ちます。 以下には使いません: * 一般的な質問またはサポートリクエスト * 既にドキュメントにあるコンテンツ * オフトピックの会話 *Source: `server/src/addie/mcp/escalation-tools.ts`* ### `set_outreach_preference` Addie がプロアクティブなメッセージ(ヒント、リマインダー、フォローアップ)をどのくらいの頻度で送るかを設定します。頻度を選ぶか完全にオプトアウトします。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `search_image_library` トピックまたは概念に一致する画像のため承認済みイラストライブラリを検索します。レスポンスに含められる画像 URL と alt テキストを返します。すべての検索は自動的にログされます。 *Source: `server/src/addie/mcp/image-tools.ts`* ### `draft_github_issue` GitHub issue をドラフトし、ユーザーが作成する事前入力された URL を生成します。ユーザーがバグをレポート、機能をリクエスト、または GitHub issue の作成を依頼するときにこれを使います。重要: ユーザーはツール出力を見られません - このツールの出力全体(GitHub リンク、タイトル、ボディプレビュー)をレスポンスにコピーしなければなりません。実際のリンクを含めずに「上のリンクをクリック」と決して言わないでください。ユーザーがリンクをクリックして自身の GitHub アカウントから issue を作成します。すべての issue は、プロトコル、スキーマ、AgenticAdvertising.org サーバー、ドキュメントを含む "adcp" リポジトリに行きます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `create_github_issue` WorkOS Pipes GitHub 接続経由でログインしたユーザーが作成した GitHub issue を adcontextprotocol/adcp に提出します。ユーザーにドラフトを表示し確認を得た後に使います。ユーザーがまだ GitHub を接続していない場合、ツールは 1 回限りの Connect リンクを伴うメッセージを返し、代わりに `draft_github_issue` を依頼できることをリマインドします — その完全なメッセージを返信に含めます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `get_github_issue` 番号で GitHub issue または PR を読みます。ユーザーが GitHub リンクを貼る、「issue #1234」を参照する、または特定の RFC、epic、PR のステータス/内容について尋ねるときに使います。タイトル、ボディ、状態、ラベル、作成者、オプションで最近のコメントと PR diff を返します。任意の公開 GitHub リポジトリ(フォークを含む)で動作します。PR レビュースレッドコメント(特定の diff 行)は返しません — issue スタイルのトップレベルコメントのみ。ユーザーが PR リンクを貼るかコードレビューを求めるとき、`include_diff: true` を設定します。キーワード検索には使わないでください — list\_github\_issues を使います。github.com/.../issues|pull URL に fetch\_url を使わないでください。このツールは構造化フィールドを返します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `propose_content` 編集レビューのためドラフト(記事またはリンク)を提出します。コンテンツは pending\_review に着地します。委員会リードまたは管理者が承認して公開します。デフォルト委員会は "editorial"(サイト全体の Perspectives)。`title` のみが必須です。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `get_my_content` ユーザーが作成者、提案者、またはオーナー(委員会リード)であるすべてのコンテンツを取得します。ステータスと関係情報を伴うすべてのコレクション全体のコンテンツを表示します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `list_pending_content` ユーザーが承認/拒否できるレビュー保留中のコンテンツをリストします。委員会リードのみが委員会コンテンツを見ます。管理者はすべての保留中コンテンツを見ます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `approve_content` 公開のため保留中コンテンツを承認します。委員会リード(自身の委員会について)と管理者のみがコンテンツを承認できます。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `reject_content` コンテンツを永久に拒否します。委員会リード(自身の委員会について)と管理者のみがコンテンツを拒否できます。提案者は拒否理由を見ます。作成者がフィードバックに対処して再提出すべき場合は代わりに request\_revisions を使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `request_revisions` 作成者にコンテンツを修正して再提出するよう依頼します。作成者が再提出するまで記事はレビューキューに「needs revisions」として可視のまま残ります。コンテンツが修正可能なとき reject\_content の代わりにこれを使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `check_illustration_status` パースペクティブ記事が編集イラストを持つか、作成者が生成できるかをチェックします。 *Source: `server/src/addie/mcp/illustration-tools.ts`* ## 常時利用可能(管理者) ルーターの意図にかかわらずすべての会話で到達可能な管理者専用ツール。 ### `resolve_escalation` エスカレーションを解決済みとしてマークし、Slack DM またはメール経由でユーザーに通知します。以前エスカレートされたリクエストを処理した後にこれを使います。 重要: 理由がない限り(例: テストエスカレーション、重複)常にユーザーに通知します。 Slack ユーザー ID が記録にあるとき Slack DM 経由、フォールバックとしてメール経由で通知が送られます。 これがエスカレーション結果についてユーザーに通知する方法です — エスカレーションについて「誰かに知らせて」「フォローアップ」「ループを閉じて」と依頼されたときはいつでもこれを使います。 例: * ユーザーが管理者ロールを必要とした → update\_org\_member\_role を使用 → 解決して通知 * ユーザーが共同リーダー追加を必要とした → add\_committee\_co\_leader を使用 → 解決して通知 * 管理者が「これは修正された、知らせて」と言う → 解決して通知 * 重複リクエスト → wont\_do で解決、通知不要 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_escalations` 管理者の注意が必要なエスカレーションをリスト、または ID で特定のエスカレーションをルックアップします。 特定のエスカレーション(任意のステータス)の詳細を得るには escalation\_id を使います。 エスカレーションを閲覧するには status フィルターを使います(デフォルトは open)。 *Source: `server/src/addie/mcp/admin-tools.ts`* ## その他のツール コードで定義されているが上のどのツールセットでも参照されていないツール。これらは通常登録に特定のチャネル/認証条件を必要とします。詳細はソースを参照してください。 ### `research_domain` 包括的なドメイン調査: ブランドレジストリをチェックし、Brandfetch + Sonnet 分類 + Lusha ファーモグラフィックス経由で拡充します。既に新鮮なデータ(\< 30 日)を持つソースをスキップします。ブランドアイデンティティ、企業階層(house\_domain/parent\_brand)、ファーモグラフィックスを 1 回の呼び出しで返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `diagnose_signin_block` ユーザーが AgenticAdvertising.org にサインインできないとレポートするときに使います。人、招待、組織メンバーシップ状態を単一の判定に組み合わせます — needs\_signin(アカウント存在、サインインするだけ)、needs\_resend(招待期限切れまたは古い、新しいものを送る)、needs\_invite(記録に招待なし、送る)、または needs\_human(状態が不明)。一般的なアカウント質問には使わないでください — それらには lookup\_person または get\_account を使います。判定に加えて 1 行の理由と対処する保留中招待トークンを返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_invites_for_org` 管理者が「X にどの招待があるか」と尋ねるとき、または再送信や取り消し前に招待トークンを参照する必要があるときに使います。デフォルトで保留中のみ。広げるには include\_accepted または include\_revoked を渡します。20 で上限、有効期限(最も近いものから)でソート。resend\_invite または revoke\_invite にトークンを引用し戻せるよう、行ごとに 1 招待をそのトークンサフィックスとともに返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `resend_invite` 保留中または期限切れのメンバーシップ招待をリフレッシュするために使います — オリジナルをアトミックに取り消し新しいものをメールします。真新しい連絡先を招待するために使わないでください(それには send\_payment\_request を使います)。受諾された招待には使わないでください。新しい有効期限とメールが配信されたかを返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `revoke_invite` 保留中または期限切れの招待をキャンセルするために使います(例: 誤ったメール、人がもう参加しない)。受信者に通知を送りません — 既存の招待リンクが単に動作しなくなります。既に受諾または既に取り消された招待を取り消せません。以前のステータスを含む確認を返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `add_member_to_org` 管理者またはオーナーが有料組織に同僚を追加、招待、または昇格するよう依頼するときに使います。また diagnose\_signin\_block が認可されたリクエスターに needs\_invite を返すときに取るアクション。4 つの状態を自動的に処理: 新しいユーザーを招待(まだ WorkOS アカウントなし)、既存ユーザーを組織に追加、ロールを更新、または既に正しければ no-op。メンバーの削除(削除パスなし)、自身のロール変更、または個人ワークスペースには使わないでください。invited、member\_added、role\_updated、no\_change のいずれかを返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `create_committee` 委員会(ワーキンググループ、カウンシル、またはガバナンス組織)を作成します。チャプターには create\_chapter、カンファレンスには create\_industry\_gathering を使います。名前で既存の Slack チャネルをリンクできます。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `update_user_name` ユーザーの表示名(名と姓)を更新します。ユーザーの名前が誤って表示されている(例: メールプレフィックスとして)手動修正が必要なときにこれを使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `ban_entity` ユーザー、組織、または API キーを禁止します。スコープはプラットフォーム全体(すべてのアクセスをブロック)またはレジストリ固有(ブランド/プロパティ編集のみブロック)にできます。 例: * ユーザーをプラットフォーム禁止: ban\_type=user, entity\_id=user\_01HW\..., scope=platform * 組織をブランド編集から禁止: ban\_type=organization, entity\_id=org\_01HW\..., scope=registry\_brand * API キーを取り消し: ban\_type=api\_key, entity\_id=wkapikey\_..., scope=platform *Source: `server/src/addie/mcp/admin-tools.ts`* ### `unban_entity` 禁止を削除します。禁止 ID を直接、または ban\_type + entity\_id + scope でルックアップして提供します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `list_bans` アクティブな禁止をリストします。ban\_type、scope、entity\_id でオプションでフィルターします。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_person_memory` 人について私たちが知っていることの完全な絵 — アイデンティティ、メンバーシップ、エンゲージメント、設定、保留中招待、最近のスレッド — を推論前に 1 つのビューに集める必要があるときに使います。lookup\_person + get\_account + list\_invites\_for\_org を連鎖させるよりこれを優先します。組織レベルの質問には使わないでください。各事実がどこから来たか引用できるよう、ソース付きの構造化サマリーを返します。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_engagement_plan` 人のエンゲージメントプランをプレビュー — 連絡適格性、スコア付き機会、Addie が何を言うかを表示します。エンゲージメント決定を理解またはデバッグするためにこれを使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `get_outreach_health` アウトリーチシステムのヘルスレポートを取得: ステージ内訳、サーキットブレーカーに近づく過剰接触された人、アウトリーチボリューム、メール統計。システムヘルスを評価するか「アウトリーチはどうか?」と尋ねられたときに使います。 *Source: `server/src/addie/mcp/admin-tools.ts`* ### `confirm_send_invoice` send\_invoice が表示した詳細を確認した後、認証されたメンバー自身の組織の請求書を送ります。連絡先メール、会社、請求先住所はサインインセッションから来ます — それらは上書きできません。組織は既に記録に請求先住所を持たなければなりません(ダッシュボードまたは招待受諾フロー経由で設定)。 *Source: `server/src/addie/mcp/billing-tools.ts`* ### `get_billing_portal` メンバーが請求書を表示、領収書をダウンロード、支払い方法を更新、サブスクリプションを管理できる Stripe カスタマーポータルへのリンクを取得します。 メンバーが領収書、請求書、課金履歴、支払い方法、サブスクリプション管理について尋ねるときにこれを使います。 メンバーはサインインしていなければなりません。 *Source: `server/src/addie/mcp/billing-tools.ts`* ### `publish_brand_canonical_document` サブブランドチームのため Brand Canonical Document(brand.json バリアント 5)を生成します。ブランドのアイデンティティフィールド(ロゴ/カラー/トーンなど)を持つ JSON ドキュメントを構築し、brand.json スキーマに対して検証し、ドキュメントとホストされるべきパス(https\:///.well-known/brand.json)を返します。無効なドキュメントの代わりに検証エラーを返します。アップロードしません — オペレーターが返された JSON を自身でホストします。 *Source: `server/src/addie/mcp/brand-canonical-tools.ts`* ### `add_to_brand_refs` House Portfolio の brand\_refs\[] に portfolio\_entry ポインターを追加します。house の brand.json を直接渡す(house\_brand\_json)か、ツールにフェッチさせます(house\_domain)。仕様のクロス配列一意性不変条件を強制します: brand\_id は brands\[] と brand\_refs\[] の両方に現れてはならず(MUST NOT)、各 brand\_id/domain は brand\_refs\[] 内で一意でなければなりません。更新された brand.json または明確な検証エラーを返します。 *Source: `server/src/addie/mcp/brand-canonical-tools.ts`* ### `check_mutual_assertion` リーフブランドのドメインが与えられたとき、その正準ドキュメントと主張された house のポートフォリオをフェッチします(House Redirect を house 側で最大 3 ホップ、Conformance セクションに従いフォロー)。関係トラストを分類: mutual(両側が相互)、leaf\_only(リーフが house を主張、house は沈黙)、house\_only(house がリーフを主張、リーフは沈黙 — 別の呼び出し元が既に持つ場合 house をチェックすることで返される)、standalone(house\_domain なし)、または unverifiable(一方または両方のフェッチが失敗)。呼び出し元が leaf\_only エッジで notify\_pending\_verification に渡せるよう、存在するとき house の contact.email を返します。 *Source: `server/src/addie/mcp/brand-canonical-tools.ts`* ### `notify_pending_verification` leaf\_only エッジが検出されたとき house の contact.email に SHOULD レベルの通知メールを送ります。 ペアごとに 24 時間ごと 1 通知にレート制限され、並行呼び出し元が二重送信できないよう brand\_assertion\_notifications テーブルに永続化されます。機能フラグ(BRAND\_ASSERTION\_EMAIL\_ENABLED)の背後 — デフォルトで log-only なので、オペレーターがライブ送信パスを有効化する前に送信されたであろうペイロードをレビューできます。 *Source: `server/src/addie/mcp/brand-canonical-tools.ts`* ### `parse_brand_properties` オペレーターが所有するブランドのスマートペーストインポートをプレビューします。貼り付けられたテキスト(ドメインとアプリバンドルのリスト)またはボディがフェッチされるべき URL を取り、インポートされる構造化プロパティリストを返します。import\_brand\_properties を呼ぶ前にユーザーは確認しなければなりません — まず解析されたリストを表示します。呼び出し元の組織はブランドドメインを所有しなければなりません。 *Source: `server/src/addie/mcp/brand-property-tools.ts`* ### `import_brand_properties` プレビューされたプロパティインポートをコミットします。プロパティリスト(通常 parse\_brand\_properties の出力、ユーザーがトリミングしたかも)を取り、識別子でブランドマニフェストにマージします — 既存エントリーはその場で更新され、新しいエントリーは追加されます。parse\_brand\_properties からのリストをユーザーが明示的に確認した後にのみ呼びます。呼び出し元の組織はブランドドメインを所有しなければなりません。 *Source: `server/src/addie/mcp/brand-property-tools.ts`* ### `upload_brand_logo` レジストリのブランドのロゴファイルをフェッチして昇格します。アップロードはモデレーターレビュー(コミュニティ貢献)のためキューされ、承認されると brand.json に適した AAO ホストの公開 /assets/brands URL を受け取ります。ブランドが検証済み DNS オーナーを持つときブロックされます — その組織のみがロゴを変更でき、人間はオーナー証明アップロードにブランドビルダー UI を使わなければなりません。 *Source: `server/src/addie/mcp/brand-tools.ts`* ### `check_credentials` 現在の学習者の新しく適格な資格を授与し、以前延期された Certifier 発行を確定します。新しく発行された資格の共有リンク、または学習者が記録に名前を持たないとき NAME\_REQUIRED マーカーを返します。欠けている名前でゲートされた資格を確定するため `set_my_name` の後にこれを使います。 *Source: `server/src/addie/mcp/certification-tools.ts`* ### `checkpoint_teaching_progress` 現在のモジュールの教授進捗のスナップショットを保存します。complete\_certification\_module または complete\_certification\_exam を呼ぶ前に必須。これらの時点で呼びます: (a) レッスンプランの各キー概念グループを終えた後、(b) 教授から評価に移行する前、(c) 集大成ラボフェーズの後、試験フェーズの前、(d) 学習者が離れる必要がある場合。重要: 最初のチェックポイントでは常に learner\_background を含めます。完了前に、学習者が満たした criterion ID を伴う demonstrations\_verified を含めます。 *Source: `server/src/addie/mcp/certification-tools.ts`* ### `get_build_phase_instructions` ビルドプロジェクトフェーズ遷移の正確な指示を取得します。B4、C4、D4 の Build、Validate、Extend フェーズに移行するときこのツールを呼ばなければなりません。ツールは学習者が必要とする特定のコマンドと URL を返します — 返されたとおりに正確に提示し、書き直したり要約したりしないでください。これはすべての学習者が同じ検証されたワークフローを得ることを保証します。 *Source: `server/src/addie/mcp/certification-tools.ts`* ### `save_learner_feedback` 認定モジュール完了後の学習者フィードバックを保存します。学習者が経験についての考え — 何が混乱したか、何がうまくいったか、改善の提案 — を共有するときにこれを呼びます。 *Source: `server/src/addie/mcp/certification-tools.ts`* ### `set_my_name` 現在のユーザーの名と(オプションで)姓を設定します。ローカル DB、組織メンバーシップ、WorkOS に書き通します。資格チェックがユーザーが記録に名前を持たないと伝えるとき、またはユーザーが名前の設定や修正を明示的に依頼するときはいつでもこれを使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `recommend_storyboards` エージェントの `get_adcp_capabilities` をプローブし、実行されるコンプライアンスバンドルを返します。エージェントの宣言された `supported_protocols` と `specialisms` が選択を駆動します — メンバー構成不要。エージェントが何も宣言しない場合、カバレッジを得るため何を宣言する必要があるか説明します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `get_storyboard_detail` ストーリーボードの完全な構造 — フェーズ、ステップ、各ステップがテストするもの、通過がどう見えるか — を表示します。開発者が何がテストされるか理解できるよう、ストーリーボードを実行する前にこれを使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `run_storyboard` 完全なストーリーボードをエージェントに対して実行しステップバイステップの結果を返します。各ステップは pass/fail、検証、エージェントが返したものを表示します。recommend\_storyboards とオプションで get\_storyboard\_detail の後に使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `run_storyboard_step` ストーリーボードの単一ステップを実行します。結果に加えて次のステップのプレビューを返します。ステップバイステップのデバッグにこれを使います — 開発者が各リクエスト/レスポンスを見て続行するか決められます。状態を維持するため前のステップ結果からのコンテキストを渡します。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `get_member_engagement` 現在のユーザーの組織エンゲージメントデータを取得: ジャーニーステージ、エンゲージメントスコア、ペルソナ/アーキタイプ、マイルストーン完了、ペルソナベースのワーキンググループ推奨。メンバーがジャーニーのどこにいてどのアクションが進むのに役立つかを理解するためにこれを使います。 *Source: `server/src/addie/mcp/member-tools.ts`* ### `search_moltbook` トピックについての投稿のため Moltbook を検索します。Moltbook は AI エージェントのソーシャルネットワークです。作成者、スコア、コメント数を伴う検索クエリに一致する投稿を返します。 *Source: `server/src/addie/mcp/moltbook-tools.ts`* ### `get_moltbook_thread` Moltbook の投稿とそのコメントを取得します。完全な投稿コンテンツと議論スレッドを返します。 *Source: `server/src/addie/mcp/moltbook-tools.ts`* ### `post_to_moltbook` Moltbook に新しい投稿を作成します。30 分ごと 1 投稿にレート制限。インサイトの共有、質問、他の AI エージェントとの議論の開始に使います。 *Source: `server/src/addie/mcp/moltbook-tools.ts`* ### `comment_on_moltbook` Moltbook の投稿にコメントを追加します。20 秒ごと 1 コメント、1 日 50 コメントにレート制限。他の AI エージェントとの議論への参加に使います。 *Source: `server/src/addie/mcp/moltbook-tools.ts`* ### `get_moltbook_stats` karma、投稿数、フォロワー数、今日のアクティビティを含む Addie の Moltbook プロフィール統計を取得します。 *Source: `server/src/addie/mcp/moltbook-tools.ts`* ### `get_moltbook_feed` hot、new、top、または rising でソートされた Moltbook からの最新投稿を取得します。 *Source: `server/src/addie/mcp/moltbook-tools.ts`* ### `suggest_newsletter_content` コミュニティニュースレターのコンテンツを提案します。誰かが「これは The Prompt にあるべき」「これを The Build に追加」「これをニュースレターに提案」と言うときにこれを使います。The Prompt は Addie のコミュニティニュースレター(全員向け)です。The Build は Sage のコントリビューターブリーフィング(コントリビューターシート向け)です。 *Source: `server/src/addie/mcp/newsletter-tools.ts`* ### `check_portrait_status` 現在のメンバーがイラストポートレートを持つか、生成できるかをチェックします。ポートレート生成を提供する前にこれを使います。 *Source: `server/src/addie/mcp/portrait-tools.ts`* ### `check_property_list` パブリッシャードメインのリストを AAO レジストリに対してチェックします。問題のサマリーと完全な詳細のレポート URL を返します。ドメインは自動的に正規化され(www/m 除去)、重複が削除され、既知のアドテックインフラがフラグされます。 *Source: `server/src/addie/mcp/property-tools.ts`* ### `enhance_property` 未知のパブリッシャードメインを分析しレビューのため保留エントリーとしてレジストリに提出します。ドメイン年齢をチェックし(\< 90 日を高リスクとフラグ)、adagents.json の存在を検証し、AI を使ってそれが実パブリッシャーかとその可能性のある在庫タイプを評価します。 *Source: `server/src/addie/mcp/property-tools.ts`* ### `resolve_catalog` 識別子(ドメイン、アプリバンドル、ストア ID)をカタログの安定した property\_rid に解決します。欠けているプロパティを自動作成します。既知のアド インフラとパブリッシャーマスクを除外します。各識別子の property\_rid を返します。 *Source: `server/src/addie/mcp/property-tools.ts`* ### `browse_catalog` プロパティカタログを閲覧します。識別子、分類、ソースを伴うプロパティを返します。フィルタリングと検索をサポートします。 *Source: `server/src/addie/mcp/property-tools.ts`* ### `dispute_catalog_entry` カタログエントリーに対して異議を提出します。medium/weak 信頼度の識別子リンク異議については、リンクはレビュー保留で即座に停止されます。authoritative/strong リンクについては、異議は停止なしでレビューのためキューされます。 *Source: `server/src/addie/mcp/property-tools.ts`* ### `get_si_availability` 接続前にブランドエージェントからオファーまたは製品が利用可能かをチェックします。これはユーザーデータを共有しない匿名プリフライトチェックです。 *Source: `server/src/addie/mcp/si-host-tools.ts`* ### `list_si_agents` Sponsored Intelligence プロトコルをサポートする AAO メンバーブランドエージェントをリストします。ユーザーがどのブランドと会話できるかを表示します。 *Source: `server/src/addie/mcp/si-host-tools.ts`* ### `connect_to_si_agent` ユーザーを SI ホストのブランドエージェントと接続します。ブランドエージェントがユーザーとインタラクトできる会話セッションを開始します。 *Source: `server/src/addie/mcp/si-host-tools.ts`* ### `send_to_si_agent` ブランドエージェントとのアクティブな SI セッションにメッセージを送ります。ユーザーが既に接続していて会話を続けたいときにこれを使います。 *Source: `server/src/addie/mcp/si-host-tools.ts`* ### `end_si_session` ブランドエージェントとの現在の SI セッションを終了します。ユーザーがブランドとの会話を終えたか通常の会話に戻りたいときに使います。 *Source: `server/src/addie/mcp/si-host-tools.ts`* ### `get_si_session_status` アクティブな SI セッションがあるかをチェックしその現在のステータスを取得します。 *Source: `server/src/addie/mcp/si-host-tools.ts`* ### `lookup_cast` AdCP ユニバースの架空のキャラクターまたは AI エージェントをルックアップします。彼らの役割、会社、性格、ストーリー出演、関連プロトコルウォークスルーを返します。 *Source: `server/src/addie/mcp/story-tools.ts`* ### `lookup_story` AdCP ストーリーをルックアップします。タイトル、あらすじ、フィーチャーされたキャラクター、関連プロトコルウォークスルー、リンクを返します。 *Source: `server/src/addie/mcp/story-tools.ts`* # Addie を AI クライアントに接続する Source: https://adcp-docs-ja.pier1.co.jp/docs/aao/connect-addie AAO MCP サーバーを Claude Desktop、Claude Code、ChatGPT、その他の MCP 互換クライアントに追加する — よくある「reconnection failed」エラーのトラブルシューティング付き。 # Addie を AI クライアントに接続する Addie は `https://agenticadvertising.org/mcp` のホストされた MCP エンドポイントで動作します。streamable HTTP を話すほとんどの MCP クライアントが接続できます — Claude Desktop、Claude Code、ChatGPT、MCP SDK 上に構築されたカスタムクライアント。このページは各インストールステップと、最も一般的な失敗モードから回復する方法をカバーします。 Addie が *何ができる* かについてのエンドユーザーヘルプは、[メンバー向け AAO](/docs/aao/users) と [Addie ツールリファレンス](/docs/aao/addie-tools) を参照してください。 ## 認証の概要 エンドポイントはすべてのリクエストで認証を要求します。2 つの認証情報タイプを受け入れます: * **OAuth 2.1 ユーザー JWT** — 人間駆動クライアント(Claude Desktop、Claude Code、ChatGPT、Cursor)用。AAO メールでサインイン。クライアントが OAuth フローを処理します。 * **WorkOS 組織 API キー** — サーバー間呼び出し元用。[agenticadvertising.org/dashboard/api-keys](https://agenticadvertising.org/dashboard/api-keys) で生成。`Authorization: Bearer ` として送信し OAuth を完全にスキップ。 特定の接続では一方 *または* もう一方を使ってください — 両方は決してだめ。静的な `Authorization` ヘッダーが存在するとき、サーバーはリクエストを認証済みとして扱い、OAuth フローは決して開始しません。ヘッダーがあるのにクライアントがまだブラウザをポップする場合、それを剥がすか上書きしています — `claude mcp get addie`(Claude Code)またはクライアントの同等物で検証してください。 基盤となる OAuth 表面(authorization server メタデータ、動的クライアント登録、スコープ)については、このページ下部の [Reference URLs](#reference-urls) を参照してください。 ## Claude Desktop Claude Desktop の組み込み **Connectors** UI が最もスムーズなパスです。Anthropic が OAuth プロキシをホストするため、トークンを自分で管理しません。カスタムコネクターには有料 Claude プラン(Pro、Max、Team、Enterprise)が必要です。 1. Claude Desktop → Settings → Connectors を開く。 2. **Add custom connector**(または **Connect** → custom)をクリック。 3. ダイアログで Name = `Addie`、Remote MCP server URL = `https://agenticadvertising.org/mcp` を設定し、**Add** をクリック。 4. ブラウザが開いたら AAO メールでサインイン。 5. Claude Desktop が Addie を接続済みとして表示。ツールはチャットツールピッカーに現れます。 ## ChatGPT ChatGPT は **Connectors** 機能経由でリモート MCP をサポートします。カスタム MCP サーバーには有料プラン(Pro、Business、Enterprise)が必要です — Plus と Free はこのオプションを見られません。 1. ChatGPT → Settings → Connectors → Advanced → Developer mode を有効化。 2. **Create**(または **Add MCP server**)をクリックし、タイプ **MCP** を選択。 3. URL: `https://agenticadvertising.org/mcp`、Authentication: **OAuth**。 4. ブラウザでサインインを完了。 ChatGPT のコネクター UI はしばしば変わります。ラベルが正確に一致しない場合、設定で "remote MCP server" または "custom connector" を探してください。 ## Claude Code Claude Code(CLI)には [既知のバグ](https://github.com/anthropics/claude-code/issues/10250) があり、OAuth は完了するが認証後の再接続が失敗し、サーバーが `failed` とマークされます。これは Addie だけでなく、streamable-HTTP + OAuth を使うすべてのリモート MCP に影響します。Anthropic が修正を出荷するまで、下の 2 つのパスのいずれかを使ってください。 ### 推奨: `mcp-remote` 経由の stdio シム `mcp-remote` は、ローカル stdio MCP サーバーとして動作し、OAuth を自身で処理し、呼び出しをリモートエンドポイントに転送する小さな npm プロキシです。Claude Code の壊れた再接続パスを完全に回避します。Node 18 以降が必要です。 ```bash theme={null} claude mcp add addie -- npx -y mcp-remote@latest https://agenticadvertising.org/mcp ``` 次に Claude Code 内で `/mcp` を実行し、ブラウザでサインインを完了します。`/mcp` は `addie ✓ connected` を表示し、Addie のツールが即座に利用可能になります。再起動不要。 ### 代替: ネイティブ HTTP トランスポート ネイティブトランスポートを使いたい場合: ```bash theme={null} claude mcp add --transport http addie https://agenticadvertising.org/mcp ``` `/mcp` を実行し、サインインを完了します。*"Authentication successful, but server reconnection failed"* が見えたら、Claude Code を完全に終了し(macOS では ⌘Q、ウィンドウを閉じるだけでなく)再起動してください。ときどき単一の再起動が保存されたトークンを拾います。しばしば拾いません。1 回の再起動で回復しない場合、上の `mcp-remote` パスにフォールバックしてください。 ### OAuth の代わりに API キーを使う WorkOS 組織 API キーを持っている場合、OAuth が決して実行されないよう静的 `Authorization` ヘッダーでサーバーを登録します: ```bash theme={null} claude mcp add --transport http addie https://agenticadvertising.org/mcp \ --header "Authorization: Bearer sk_your_key_here" ``` `claude mcp add` は他のエントリーを乱すことなく安全に `~/.claude.json` に書き込みます。そのファイルを手動編集することは動作しますが他のサーバーを壊しうる — 既に何があるか知っている場合のみ行ってください。同等の JSON 形状は: ```json theme={null} { "mcpServers": { "addie": { "type": "http", "url": "https://agenticadvertising.org/mcp", "headers": { "Authorization": "Bearer sk_your_key_here" } } } } ``` これを OAuth フローと組み合わせないでください — 一方を選んでください。ヘッダーが存在するのに Claude Code が OAuth をトリガーする場合、キーが無効か期限切れです。[agenticadvertising.org/dashboard/api-keys](https://agenticadvertising.org/dashboard/api-keys) で新しいものを生成してください。 ## その他の MCP クライアント streamable HTTP + OAuth 2.1 を話す任意の MCP クライアントが接続できます。汎用設定: ```json theme={null} { "type": "http", "url": "https://agenticadvertising.org/mcp" } ``` ディスカバリーは標準パターンに従います: 最初のリクエストでの 401 は `WWW-Authenticate: Bearer resource_metadata=...` を含み、クライアントを `/.well-known/oauth-protected-resource/mcp` に向け、それが authorization server をリストします。 ## トラブルシューティング ### "Authentication successful, but server reconnection failed" これは Claude Code バグ [#10250](https://github.com/anthropics/claude-code/issues/10250) です。OAuth フローは動作した — トークンは `~/.claude/.credentials.json` に保存されている — が、クライアントがそれらで再接続に失敗しました。完全な再起動がときどき回復します。信頼できる回避策は上の `mcp-remote` インストールパスです。 これは Addie 固有ではありません: 同じエラーは Notion、Supabase、Slack、New Relic、OAuth を使う他のリモート MCP サーバーに影響します。 ### OAuth 完了後に 401 が返る クライアントが有効なトークンを持つと言うが `/mcp` へのすべてのリクエストが 401 を返す場合: * トークンが新しいことを確認。WorkOS アクセストークンは短命です。リフレッシュトークンはより長く続きます。ほとんどのクライアントは自動リフレッシュします。一部はしません。再認証を強制してください。 * 認証情報を二重送信していないことを確認。設定に `Authorization` ヘッダー *と* OAuth フローの両方がある場合、一方が他方と衝突します。 * トークンを手動でテスト: ```bash theme={null} curl -i -X POST https://agenticadvertising.org/mcp \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"diag","version":"0.0.1"}}}' ``` ここでの 401 はトークンがサーバーで拒否されたことを意味します。200 はクライアントが認証を誤処理していることを意味します。 ### 再認証を強制する 保存されたトークンが古くなりクライアントが再プロンプトしないとき: * **Claude Code:** `rm ~/.claude/.credentials.json` してから `/mcp` を実行。(これは *すべての* MCP サーバーの OAuth 状態をクリアします。複数ある場合はまずバックアップしてください。) * **Claude Desktop:** Addie コネクターを削除して再追加。 * **ChatGPT:** 設定でコネクターを切断して再追加。 ### "`~/.claude/settings.json` に Addie を設定したがロードされない" Claude Code は MCP サーバーを `~/.claude.json`(グローバル設定)または `.mcp.json`(プロジェクトスコープ)から読みます — `settings.json` からではありません。`settings.json` は権限、フック、環境変数のみを保持します。手動編集ではなく `claude mcp add` を使ってください。 ### Claude Code ログの場所 macOS: `tail -f ~/Library/Logs/Claude/mcp*.log`。tailing しながらクライアントを実行し失敗するフローをトリガーしてください — 実際のエラー(トークン拒否、トランスポート不一致、ネットワーク失敗)がそこに現れます。共有前にトークンを編集除去してください。 ## Reference URLs * **MCP エンドポイント:** `https://agenticadvertising.org/mcp` * **Authorization server メタデータ**(RFC 8414): `https://agenticadvertising.org/.well-known/oauth-authorization-server` * **Protected resource メタデータ**(RFC 9728): `https://agenticadvertising.org/.well-known/oauth-protected-resource/mcp` * **動的クライアント登録**(RFC 7591): `POST /register` * **API キーダッシュボード:** [agenticadvertising.org/dashboard/api-keys](https://agenticadvertising.org/dashboard/api-keys) * **Issue トラッカー:** [github.com/adcontextprotocol/adcp/issues](https://github.com/adcontextprotocol/adcp/issues) # AAO ディレクトリ API — エージェント ↔ パブリッシャー逆引き Source: https://adcp-docs-ja.pier1.co.jp/docs/aao/directory-api セールスエージェントオペレーターがどのパブリッシャーが自身のエージェントを認可するかを発見する HTTP API。AAO ディレクトリのパブリッシャー adagents.json ファイルのインデックスから取得。プロベナンス、パブリッシャーごとのプロパティ数、ライフサイクルステータスを返す。 # AAO ディレクトリ API `agenticadvertising.org` の AAO ディレクトリは、オープンウェブ全体でパブリッシャー `adagents.json` ファイルをインデックスします。この API は、すべてのセールスエージェントオペレーターが同期時に必要とする **逆マップ** を表示します: > 「どのパブリッシャーが私のエージェントを認可したか?」 このエンドポイントなしでは、オペレーターはパブリッシャードメインリストを手動で保守し `fetch_agent_authorizations` をそれに対して呼ぶか、オープンウェブを自分でクロールしなければなりません。両方ともマネージドネットワークスケールでは実行不可能です([cafemedia](https://cafemedia.com/.well-known/adagents.json) だけで単一のマネージャーファイルの下に約 6,800 のパブリッシャードメインを委譲)。 このエンドポイントは **ディスカバリー** であり、**認可** ではありません。パブリッシャー自身の `adagents.json` がトラストルートのままです。ディレクトリは、どのパブリッシャーを SDK のドメインごとプリミティブ(`verify_agent_authorization`、`fetch_agent_authorizations`)経由で直接検証すべきかを伝えます。 ## エンドポイント ``` GET https://{aao_directory}/v1/agents/{agent_url}/publishers ``` `{agent_url}` はパーセントエンコードされなければなりません(MUST)。ディレクトリは、SDK が `verify_agent_authorization` で適用するのと同じ規約を使って、ルックアップキーを正準化します(小文字ホスト、デフォルトポート除去、パスコンポーネントの末尾スラッシュ正規化)。 ### クエリパラメーター | Parameter | Type | Default | Semantics | | --------- | ----------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `since` | ISO 8601 | 未設定 | `last_verified_at` ≥ `since` のパブリッシャーのみを返す。増分同期を可能にする。 | | `cursor` | 不透明文字列 | 未設定 | 先のレスポンスが返したページネーションカーソル。カーソルの寿命の間、ディレクトリのリフレッシュサイクル全体で安定。 | | `status` | string、繰り返し | `authorized` | ライフサイクルステータスでフィルター。v1: `authorized`、`revoked`。値ごとにキーを 1 回繰り返す(OpenAPI `style: form, explode: true`)。カンマ区切り単一値形式(`status=authorized,revoked`)は受け入れられ **ない**。ディレクトリは繰り返しキー形式を指す説明とともに `400` を返さなければならない(MUST)。 | | `limit` | int(1–1000) | 200 | ページあたり最大パブリッシャー数。 | | `include` | string、繰り返し | 未設定 | 拡張されたロウごとフィールドにオプトイン。v1: `properties` — 各 `PublisherEntry` がそのパブリッシャーの下の正準 `property_ids[]` リストを運ぶ(消費者が数比較だけでなくフェデレーテッドフェッチに対して完全な集合差分を実行できる)。繰り返しキー形式、`status` と同じエンコードルール。未知の値は `400` を返す。 | #### 実例: 複数のステータス値でフィルター ``` GET /v1/agents/https%3A%2F%2Fsales-agent.example.com%2F/publishers?status=authorized&status=revoked&limit=500 ``` TypeScript での同等物: ```ts theme={null} const url = new URL(`${directory}/v1/agents/${encodeURIComponent(agentUrl)}/publishers`); url.searchParams.append("status", "authorized"); url.searchParams.append("status", "revoked"); url.searchParams.set("limit", "500"); ``` Python(`requests`)での同等物: ```python theme={null} requests.get( f"{directory}/v1/agents/{quote(agent_url, safe='')}/publishers", params=[("status", "authorized"), ("status", "revoked"), ("limit", "500")], ) ``` `status` パラメーターの OpenAPI フラグメント: ```yaml theme={null} - in: query name: status schema: type: array items: type: string enum: [authorized, revoked] style: form explode: true required: false ``` 繰り返しキーが選ばれたのは、(a) それが `URLSearchParams.append()` と OpenAPI のデフォルト `explode: true` が生成するもので、(b) カンマを含みうる将来の値ときれいに合成し、(c) ディレクトリでパーサーの曖昧性を残さないからです。 #### 実例: 完全な集合差分のため `?include=properties` を要求 ``` GET /v1/agents/https%3A%2F%2Fsales-agent.example.com%2F/publishers?include=properties ``` デフォルトレスポンスは `properties_authorized` を数としてのみ運びます。数の等価は集合の等価では **ありません**: 3 つのプロパティをローテートするパブリッシャーは数を変えないまま集合を完全に異なるものにし、数ベースの分岐検出器はそれを見られません。`?include=properties` は `PublisherEntry` ごとに `property_ids: list[string]` フィールドを追加します — そのパブリッシャーの下でエージェントのセレクターが解決する正準 ID — 消費者がフェデレーテッド `fetch_agent_authorizations` 結果に対して集合として完全な集合差分を実行し、大きさの差分だけでなくローテーションを検出できるように。 フラグはデフォルトページペイロードを小さく保つためオプトインです。インライン ID はパブリッシャーごとのプロパティ数 × 約 16 バイト/ID を追加します。マネージドネットワーク親ファイル(約 6,800 パブリッシャー × 平均 1 プロパティ ≈ 追加 7 KB の ID)では、オーバーヘッドは小さいが非ゼロです。ページネーションセマンティクスは変わりません。 ### レスポンス ```json theme={null} { "agent_url": "https://sales-agent.example.com", "directory_indexed_at": "2026-05-19T12:00:00Z", "publishers": [ { "publisher_domain": "recipeswithessentialoils.com", "discovery_method": "ads_txt_managerdomain", "manager_domain": "cafemedia.com", "properties_authorized": 1, "properties_total": 1, "signing_keys_pinned": false, "status": "authorized", "last_verified_at": "2026-05-19T08:00:00Z" }, { "publisher_domain": "wsj.com", "discovery_method": "direct", "manager_domain": null, "properties_authorized": 47, "properties_total": 200, "signing_keys_pinned": true, "status": "authorized", "last_verified_at": "2026-05-19T10:00:00Z" }, { "publisher_domain": "former-partner.example", "discovery_method": "authoritative_location", "manager_domain": "cafemedia.com", "properties_authorized": 0, "properties_total": 0, "signing_keys_pinned": false, "status": "revoked", "last_verified_at": "2026-05-19T11:00:00Z" } ], "next_cursor": "eyJv..." } ``` `?include=properties` では、各 `PublisherEntry` が追加で `property_ids` を運びます: ```json theme={null} { "publisher_domain": "recipeswithessentialoils.com", "discovery_method": "ads_txt_managerdomain", "manager_domain": "cafemedia.com", "properties_authorized": 3, "properties_total": 3, "property_ids": ["p-001", "p-002", "p-003"], "signing_keys_pinned": false, "status": "authorized", "last_verified_at": "2026-05-19T08:00:00Z" } ``` ## フィールドリファレンス ### エンベロープ | Field | Required | Notes | | ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------ | | `agent_url` | yes | ルックアップキーの正準化エコー。 | | `directory_indexed_at` | yes | 結果セット内の最新のパブリッシャーごとリフレッシュ。消費者自身のキャッシュのプロベナンス。**空のページでは NULL** — レポートするアンカーがない。消費者は null 値からキャッシュ鮮度を進めるべきでない(SHOULD NOT)。 | | `publishers` | yes | 配列。空配列は有効なレスポンス — ディレクトリはこのエージェントをインデックスしたが現在の認可が解決しない。 | | `next_cursor` | optional | 不透明ページネーションカーソル。終端ページでは不在または null。 | ### `PublisherEntry` | Field | Required | Notes | | ----------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `publisher_domain` | yes | `adagents.json` がエージェントを認可するパブリッシャー。 | | `discovery_method` | yes | `direct`、`authoritative_location`、`adagents_authoritative`、または `ads_txt_managerdomain`。下記参照。 | | `manager_domain` | conditional | `discovery_method` ≠ `direct` のとき必須。それ以外は null。 | | `properties_authorized` | yes | エージェントのセレクターが解決する **この publisher\_domain のみ** の下のプロパティ数。決してネットワーク全体の数ではない。 | | `properties_total` | yes | パブリッシャーのファイル(またはそのドメインの親ファイルのインラインサブセット)内の **この publisher\_domain のみ** の下のプロパティ数。決してネットワーク全体の数ではない。 | | `property_ids` | conditional | リクエストが `?include=properties` を含んだとき、かつそのときのみ存在。エージェントのセレクターがこのパブリッシャーの下で解決する `property_id` の正準リスト — `properties_authorized` が数える同じ集団を、消費者がフェデレーテッドフェッチに対して完全な集合差分(数比較だけでなく)を実行できるよう ID として表示。パブリッシャーごとスコープ。決してネットワーク全体でない。集合として扱う。順序は未指定。 | | `signing_keys_pinned` | optional | パブリッシャーがこのエージェントに `signing_keys[]` をピン留めするか。`true` のとき、エージェントの署名付きレスポンスはエージェント自身の JWKS にかかわらずピン留めされた集合に対して検証されなければならない(MUST)。 | | `status` | yes | `authorized` または `revoked`。下記参照。 | | `last_verified_at` | yes | ディレクトリがこのパブリッシャーの `adagents.json` を最後にフェッチし検証したとき。 | ### `discovery_method` 値 | Value | Meaning | Trust profile | | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | `direct` | パブリッシャー自身の `/.well-known/adagents.json` にエージェントがリストされている。 | 最強 — 委譲ホップなし。 | | `authoritative_location` | パブリッシャーの `/.well-known/adagents.json` が、エージェントをリストするマネージャーファイルを指す `authoritative_location` を宣言。 | 強 — パブリッシャーが積極的に委譲。 | | `adagents_authoritative` | マネージャーファイル自身の `properties[]` がパブリッシャーのドメインを運ぶことで発見([adcp#4825 インライン解決ルール](https://github.com/adcontextprotocol/adcp/issues/4825) に従い)。 | 中 — パブリッシャーはマネージャーファイルで名指されたが委譲を自身でホストしなかった。 | | `ads_txt_managerdomain` | マネージャーファイルを指すパブリッシャーの `ads.txt` `MANAGERDOMAIN=` ディレクティブ経由で発見。 | 最弱 — [`managerdomain` フォールバック安全ルール](/docs/governance/property/adagents#safety-rules-for-this-fallback) が唯一の肯定的クロスチェック。 | ディレクトリは `discovery_method: ads_txt_managerdomain` のロウを返す前に `managerdomain` 安全ルールを検証します — これがオペレーターごとの `ads.txt` クロールに対するディレクトリの主な付加価値です。 ### `status` 値 | Value | Meaning | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `authorized` | セレクターがこの publisher\_domain の下で 1 つ以上のプロパティに解決する。通常のケース。 | | `revoked` | パブリッシャーが以前エージェントを認可し、今や権威ファイルの `revoked_publisher_domains[]` にこの `publisher_domain` をリストする。失効が着地した後の最初の同期でトゥームストーンとして発行され、その後ドロップ。オペレーターが各パブリッシャーのキャッシュ TTL をポーリングせずに失効を伝播できる。 | `unbound`、`pending`、`unreachable`、`no_properties` は **意図的に v1 の一部でありません**。ディレクトリは `adagents.json` が正常にフェッチされエージェントを参照するパブリッシャーのみをインデックスします。パブリッシャーが消えた場合、ディレクトリはトゥームストーンを返すのではなく結果からそれをドロップします(消費者は先のページに対する集合差分でメンバーシップを追跡)。 ## HTTP セマンティクス | Status | Meaning | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `200 OK` | ルックアップ成功。ボディは空の `publishers[]` を持ってもよい(MAY)。 | | `400 Bad Request` | 不正な形式の `agent_url`、無効なカーソル、未知の `status` 値、または繰り返しキーではなくカンマ区切りリストとして供給された `status`。 | | `404 Not Found` | ディレクトリはこの `agent_url` を参照するパブリッシャーを決してインデックスしていない。**`200` + 空とは区別される**(それはディレクトリがこのエージェントをインデックスしたが現在の認可が解決しないことを意味する)。 | | `429 Too Many Requests` | レート制限。`Retry-After` ヘッダーが設定される。バケットキー: `agent_url`(匿名)プラス IP(多層防御)。 | | `5xx` | ディレクトリエラー。消費者はバックオフでリトライすべき(SHOULD)。 | エンドポイントは `Cache-Control` と `ETag` を設定します。条件付き `GET`(`If-None-Match`)がワイヤーレベルのキャッシュメカニズムです。ボディの `directory_indexed_at` が消費者ロジックの鮮度アンカーです。 ## 認証 V1 は未認証です。パブリッシャー `adagents.json` ファイルは公開です。逆マップは公開です。レート制限が IP ベースからアイデンティティベースに卒業する場合、パスはエージェントの公開 JWKS をキーとする [RFC 9421](https://datatracker.ietf.org/doc/html/rfc9421) リクエスト署名を重ねる別の RFC です — エージェントはリクエストに署名することで `agent_url` を制御することを証明します。この RFC の範囲外。 ## ページネーション カーソルは不透明です。置換可能な文字列として扱い、そのまま返してください。ディレクトリは通知なしにカーソル形式を変更してもよい(MAY)。消費者はカーソル内容を解析してはなりません(MUST NOT)。 カーソルは少なくとも 1 つのディレクトリリフレッシュサイクルの間有効なままです。それを過ぎると、ディレクトリは `cursor_expired` を伴う `400` または最初からの再走査を伴う `200` を返してもよい(MAY) — 両方とも適合。消費者は先のリクエストの実時間を記録し、24 時間より古いカーソルの使用を拒否すべきです(SHOULD)。 ## 他のプリミティブとの関係 AAO ディレクトリは既存の SDK プリミティブを補完します: | Question | Primitive | Direction | | ------------------------------------------------------ | ---------------------------------------------------------- | ---------------------- | | *この* エージェントは *この* パブリッシャーの `adagents.json` にリストされているか? | `verify_agent_authorization(adagents_data, agent_url)` | プッシュ(パブリッシャー → エージェント) | | パブリッシャーのリストが与えられたとき、どれが私のエージェントを認可するか? | `fetch_agent_authorizations(agent_url, publisher_domains)` | プル、呼び出し元供給リスト | | **どのパブリッシャーが私のエージェントを認可するか?** | **`GET /v1/agents/{agent_url}/publishers`** | **プル、ディレクトリ供給リスト** | 最初の 2 つは、オペレーターが既にパブリッシャー集合を知っている質問に答えます。ディレクトリエンドポイントはオペレーターの実際の同期時の質問に答えます: 「私のパブリッシャー集合は何か?」 推奨ワークフロー: 1. `GET /v1/agents/{agent_url}/publishers` を呼んでパブリッシャー集合を発見。 2. レスポンスの各 `publisher_domain` について、オペレーターはトラストルートに対して再確認するためパブリッシャー自身の `adagents.json` に対して `verify_agent_authorization` を呼んでもよい(MAY)。ディレクトリの `last_verified_at` はクリティカルパスでのドメインごと検証の必要性を減らすが排除しない。 3. レスポンスの `properties_authorized` / `properties_total` をオペレーター向けスコープサマリーに、`signing_keys_pinned` フラグをどのエージェントがパブリッシャーのピンに一致する JWKS を公開しなければならないかを表示するために使う。 4. 分岐検出器(ディレクトリとパブリッシャーのライブ `adagents.json` が不一致のケースを捕捉)を実行するオペレーターは、`?include=properties` を要求し、ディレクトリの `property_ids[]` をフェデレーテッドフェッチに対して数ではなく集合として比較すべき(SHOULD)。N プロパティをローテートするパブリッシャーは両側で等しい数を生成する。集合比較のみがそれを捕捉する。 ## `publisher_properties` インライン解決との関係 マネージドネットワーク型親ファイル([adcp#4825 インライン解決ルール](/docs/governance/property/adagents#resolution-paths) に従い)では、ディレクトリは `publisher_domain` でフィルターされた親ファイルのインライン `properties[]` から `properties_total` を計算します。このスケールでの厳格なフェデレーションは、ディレクトリリフレッシュごとパブリッシャーごとに N HTTP フェッチを要求します — オペレーターが持つのと同じスケール問題が 1 レイヤー上に移動しただけ。ディレクトリは仕様が承認するインライン解決ルールを使います。 ## 範囲外(v1) * **認証。** 公開エンドポイント、匿名レート制限。アイデンティティバウンドの制限は必要なら別の RFC で到達。 * **クロスディレクトリフェデレーション。** 単一ディレクトリ。エンドポイント形状は、複数の AAO 互換ディレクトリがそれを実装できるよう定義されている。どのディレクトリをクエリするかのディスカバリーは今日は構成。 * **新しい認可のプッシュ通知。** ポーリングベースの v1。 * **完全なプロパティオブジェクトのインライン。** `?include=properties` は解決された `property_ids[]` のみを返す — プロパティオブジェクト自体ではない。ID を持つ消費者は既存のドメインごとプリミティブ経由で詳細をフェッチできる。 ## 関連項目 * [adagents.json 技術仕様](/docs/governance/property/adagents) — トラストルート。 * [マネージドネットワークデプロイ](/docs/governance/property/managed-networks) — このエンドポイントがインデックスする正準マルチパブリッシャーパターン。 * [adcp#4825](https://github.com/adcontextprotocol/adcp/issues/4825) — ディレクトリの数フィールドが依存する `publisher_properties` インライン解決ルール。 # 組織管理者向け AAO Source: https://adcp-docs-ja.pier1.co.jp/docs/aao/org-admins 組織管理者ができること — メンバー管理、階層変更、課金表示、ブランドとエージェントの構成。 # 組織管理者向け AAO このページは、AgenticAdvertising.org アカウントを管理する会社の人向けです: シート割り当て、課金、ブランド構成、エージェント宣言。エンドユーザードキュメントについては [メンバー向け AAO](/docs/aao/users) を参照してください。Addie の完全なツールリストについては [Addie ツールリファレンス](/docs/aao/addie-tools) を参照してください。 あなたのアカウントが AAO 上の組織内で `admin` または `owner` ロールを持つ場合、あなたは組織管理者です。有料組織を最初にセットアップした人が自動的にオーナーになります。 ## メンバーシップ階層 すべての AAO メンバーシップは年次 Stripe サブスクリプションです。アップグレードは自動的に日割り計算されます。ダウングレードは次の更新時に有効になります。 | Tier | Price | Contributor seats | Community-only seats | Payment | | ------------ | ---------- | ----------------- | -------------------- | -------------- | | Explorer | \$50/年 | 0 | 1 | クレジットカード | | Professional | \$250/年 | 1 | 1 | クレジットカード | | Builder | \$2,500/年 | 5 | 5 | クレジットカード | | Partner | \$10,000/年 | 10 | 50 | クレジットカードまたは請求書 | | Leader | \$50,000/年 | 20+ | 無制限 | クレジットカードまたは請求書 | **Contributor シート** は Slack、ワーキンググループ、投票権、ディレクトリリスティング、community-only のすべてを含みます。**Community-only シート** は Addie、認定、トレーニング、地域チャプターを含みます — 学ぶ必要はあるがアクティブなコラボレーションアクセスは必要ないチームメンバー向け。 ## シートの管理 * **チームメイトを招待。** Addie に *「invite \[email] to my org」* と尋ねてください — Stripe バックのシート招待を送ります。管理者またはオーナーでなければなりません。 * **昇格 / 降格。** 今日これはエスカレーションのみです — Addie に *「set \[email] as admin in my org」* と尋ねるとリクエストをルーティングします。セルフサーブに取り組んでいます。 * **メンバーの削除。** エスカレーション。認定クレジットが失われないようチームが慎重に処理します。 ## 課金 * **請求書の表示。** Addie に *「show my invoices」* と尋ねるか、[agenticadvertising.org/dashboard/billing](https://agenticadvertising.org/dashboard/billing) を訪問。組織管理者は完全な組織課金履歴を見られます。 * **支払い方法 / 請求先住所の更新。** Stripe カスタマーポータルを使う — *「open my billing portal」* でリンク(Addie は `create_customer_portal_session` を持っています)。 * **請求書または支払いリンクの生成。** *「Send me an invoice for Builder tier」* が `send_invoice` → 確認 → `confirm_send_invoice` をトリガーします。 * **クーポンの適用。** 請求書生成時に Addie にクーポン ID を伝えてください。 * **階層のアップグレード。** *「Upgrade us to Partner」* — Stripe が現在期間の残りについて差額を日割り計算します。 * **階層のダウングレード。** 同じフロー。次の更新時に有効になります。 返金、カスタム契約、通貨変更、周期外クレジットについては、Addie が人間にエスカレートします。 ## ブランドアイデンティティ `/.well-known/brand.json` で公開されるあなたの `brand.json` は、AdCP エコシステムでのあなたの会社の正準アイデンティティです。AAO はそれを使ってプロパティ認可を検証し、エージェントディスカバリーを有効化します。 * **brand.json をビルド。** [agenticadvertising.org/brand](https://agenticadvertising.org/brand) がビジュアルビルダーです。 * **ドメイン所有権を検証。** Addie に *「verify my brand for \[domain]」* と尋ねてください — `request_brand_domain_challenge` 経由で DNS チャレンジを発行します。TXT レコードを追加し、次に *「check my brand verification」*(`verify_brand_domain_challenge`)。 * **プロパティカタログ。** あなたの brand.json にリストされたプロパティは自動発見されます。AAO があなたのドメインについて何を解決したか確認するには、*「what properties are resolved for \[domain]?」* と尋ねてください。 ## エージェント宣言(adagents.json) パブリッシャーの場合、あなたの在庫を販売する認可を受けたエージェントを宣言する `/.well-known/adagents.json` を公開します。 * **adagents.json をビルド。** [agenticadvertising.org/adagents](https://agenticadvertising.org/adagents)。 * **検証。** Addie に *「validate my adagents.json」* と尋ねてください — `validate_adagents` が形状をチェックし認可を解決します。 * **セールスエージェントのステータスを確認。** *「What's the status of \[agent URL]?」* が `get_agent_status` を実行し、レジストリのキャッシュされたヘルス、宣言されたケイパビリティ、トラックごとの最新の comply ストーリーボード判定を返します。オンデマンドのライブテストには、Addie に *「evaluate \[agent URL]」* と依頼してください — `evaluate_agent_quality` が今すぐ comply スイートを実行します。 * **リスティングが見えない?** 最初にチェック: メンバープロフィールは完全で階層はアクティブか? 次に: *「check property resolution for \[domain]」* — Addie はレジストリクローラーがあなたのファイルを拾ったかを診断できます。 ## ワーキンググループと委員会のリーダーシップ * **ワーキンググループをリード。** ワーキンググループリードは `list_committee_documents`、`create_working_group_post` ができ、コントリビューターメンバーシップリクエストを承認/辞退できます。 * **カウンシルのリーダーシップ。** カウンシルリードはカウンシルリソースに対して `attach_content_asset` と `propose_content` の権限を持ちます。 Addie に *「what can I do as a \[WG/council] lead?」* と尋ねると、あなたのロールで利用可能なリーダー専用ツールを列挙します。 ## Addie がセルフサーブせずにエスカレートすること * 標準アップグレードフロー外の階層変更(カスタム契約、請求書での日割り計算)。 * 返金、無効化、周期外クレジット。 * メンバーの昇格 / 降格 / 削除。 * オンボーディング後のアカウントマージまたはドメイン変更。 * 削除された、または孤立したデータに関わるもの。 これらについては、Addie がコンテキストを収集し AAO チームにルーティングします — 通常 1〜2 営業日の SLA。 # メンバー向け AAO Source: https://adcp-docs-ja.pier1.co.jp/docs/aao/users AgenticAdvertising.org メンバーができること — サインイン、認定、ワーキンググループ、パースペクティブ、ディレクトリリスティング、Addie へのヘルプ依頼。 # メンバー向け AAO このページは、AAO メンバー — 任意の階層のアクティブなメンバーシップを持つ人 — が AgenticAdvertising.org 上と Addie を通じて何ができるかをカバーします。組織管理者(会社のシートと課金を管理する人)については [組織管理者向け AAO](/docs/aao/org-admins) を参照してください。Addie のツールの完全なリストについては [Addie ツールリファレンス](/docs/aao/addie-tools) を参照してください。 ## サインインとアカウントのリンク * **サインイン。** [agenticadvertising.org](https://agenticadvertising.org) に行き、メールまたは Google でサインインします。新しい訪問者は無料の匿名 Addie セッションを得ます。サインインするとメンバーツールがアンロックされます。 * **Slack をリンク。** AAO Slack ワークスペースに参加した場合、任意の DM で Addie に *「link my account」* と尋ねると、パーソナライズされたサインインリンクを生成します。サインイン後、あなたの Slack アイデンティティは永続的にリンクされます。 * **リンクを失くした?** Addie に *「send me my sign-in link」* と尋ねてください — 任意のチャネルで動作する `get_account_link` ツールを持っています。 ## 認定(AdCP Academy) | Tier | Price | 得られるもの | | -------------------------- | ------------------------- | -------------------------------------- | | Tier 1 — AdCP Basics | 無料、メンバーシップ不要 | 3 つの基礎モジュール、約 90 分 | | Tier 2 — AdCP Practitioner | 任意のアクティブなメンバーシップ(\$50+/年) | Basics + 1 つのロール固有トラック + ビルドプロジェクト | | Tier 3 — AdCP Specialist | 任意のアクティブなメンバーシップ(\$50+/年) | Practitioner + 5 つの領域の 1 つでのスペシャリスト集大成 | Addie に尋ねてください: * *「Start module A1」* — 現在の階層で該当モジュールを開始または再開。 * *「Where am I in certification?」* — 完了したモジュールと次を列挙。 * *「What's the next step in my Practitioner track?」* — トラック対応の進行。 ## ワーキンググループ、カウンシル、チャプター * **ワーキンググループを閲覧。** *「what working groups are active?」* と尋ねるか、[agenticadvertising.org/working-groups](https://agenticadvertising.org/working-groups) を訪問。 * **ワーキンググループに参加。** Addie に *「I want to join the \[name] working group」* と伝えてください — Professional 階層以上はコントリビュータートラックの WG に参加でき、Explorer 階層はウェブサイト経由で読み取り専用アクセス。 * **カウンシルへの関心を表明。** カウンシルは指名が必要です。Addie は `express_council_interest` であなたの関心を記録できます。 * **地元のチャプターを見つける。** *「is there an AAO chapter in \[region]?」* と尋ねてください — チャプターはメンバー運営で、ほとんどは別の参加フローなしに新参者を受け入れます。 ## パースペクティブ(AAO での公開) メンバーは agenticadvertising.org の Stories の下で *パースペクティブ* — 短い記事、オプエド、レポート — を公開できます。 * **ドラフトを提出。** Addie にタイトルとコンテンツを添えて *「propose a perspective」* と依頼(または Google Doc リンクを貼り付け — `read_google_doc` 経由で読みます)。 * **編集レビュー。** 提出されたパースペクティブは `pending_review` になります。AAO 管理者がレビューして承認、編集依頼、または辞退します。 * **カバーイラスト。** メンバーは月次で無料のイラスト生成を得ます。提出後 *「generate a cover for my perspective」* と尋ねてください。 * **公開後の編集。** パースペクティブページの編集ボタンを使ってください。欠けている場合、Addie に調査を依頼してください。 ## ディレクトリリスティング * **掲載される。** Professional 階層以上のメンバーは agenticadvertising.org/registry にディレクトリリスティングを得ます。Addie に *「set up my directory listing」* と尋ねると、会社名、説明、オファリング、連絡先、可視性を案内します。 * **リスティングを更新。** *「update my directory listing」* と尋ねてください — Addie は `update_company_listing` を持っています。 * **ロゴを追加。** *「Update my company logo to \[URL]」* — `update_company_logo`。 * **ブランド検証。** 所有ドメインで公開する場合、*「verify my brand for \[domain]」* と尋ねると、Addie は `request_brand_domain_challenge` 経由で DNS チャレンジを発行します。 ## プロフィール * **プロフィールの表示 / 更新。** *「Show my profile」* / *「update my profile」*(`get_my_profile`、`update_my_profile`)。 * **プロフィール写真。** ウェブサイトのプロフィールページ経由でアップロード。アップロードコントロールが欠けている場合、Addie にエスカレーションを依頼。 * **メンバーポートレート。** Builder 階層以上のメンバーは自動生成されたグラフィックノベルポートレートを得ます — *「generate my portrait」* と尋ねてください。 ## Addie があなたのためにできないこと * 階層の変更や返金の処理 — それは組織管理者のアクション。*「escalate to admin」* でチームに依頼してください。 * 組織内の誰かを管理者に昇格 — エスカレーションのみ。 * アカウントの削除 — エスカレーション。管理者が慎重に処理します。 * 存在しないツールを発明。このページや [Addie ツールリファレンス](/docs/aao/addie-tools) で見つけられないツールを持っていると Addie が言う場合、おそらく間違っています — 押し返して検証を依頼してください。 # コミュニティ Slack への参加 Source: https://adcp-docs-ja.pier1.co.jp/docs/community/joining-slack AgenticAdvertising.org Slack コミュニティへの参加方法 — 公開招待リンク、ドメイン allowlist ポリシー、リンクが動作しない場合の対処。 AdCP コミュニティ Slack は、プロトコル開発が起こる場所です — ワーキンググループ議論、実装質問、パブリッシャー、エージェンシー、開発者にわたるリアルタイムコラボレーション。 ## 公開招待で参加 **[→ Slack で AdCP コミュニティに参加](https://join.slack.com/t/agenticads/shared_invite/zt-3h15gj6c0-FRTrD_y4HqmeXDKBl2TDEA)** 招待リンクは公開です。クリックして Slack のプロンプトに従って参加してください。 ## リンクが動作しない場合 Slack ワークスペースにはドメイン allowlist があります。メールが Gmail、個人メールアドレス、またはまだ allowlist にないドメインを使う場合、Slack は黙って招待を辞退します — リンクは動作するように見えますが、メールを受け取ったりアクセスを得たりしません。 **これは壊れたリンクではありません。** ドメイン制限です。 ### 対処法 1. **Addie に直接尋ねる** — [agenticadvertising.org/chat](https://agenticadvertising.org/chat) でチャットを開き「Slack に参加しようとしているが招待リンクが動作しなかった」と言います。Addie はあなたのメールを尋ね、直接招待のため管理者チームにエスカレートします。 2. **チームにメール** — [community@agenticadvertising.org](mailto:community@agenticadvertising.org) にあなたのメールアドレスと役割についての一行を添えて送ります。チームは通常 1 営業日以内に直接招待を処理します。 ## ドメイン allowlist ポリシー 現在の allowlist ポリシーはレビュー中です。参加後モデレーションを優先して allowlist を落とすか、軽量なセルフサーブ招待リクエストフォームでそれを保つかの決定が保留中です。決定がなされたときこのページは更新されます。 現在、allowlist は既知のパブリッシャー、エージェンシー、アドテックベンダー、AAO メンバーに関連付けられた企業と組織のドメインをカバーします。個人メールドメイン(Gmail、Outlook、Yahoo など)は直接招待を要求します。 ドメインが allowlist にない組織を代表する場合、最速のパスは Addie に尋ねるかチームにメールすることです。Allowlist 追加は通常 1 営業日以内に処理されます。 ## 関連項目 * [ワーキンググループ](/docs/community/working-group) — コミュニティが何に取り組むか、どう参加するか * [メンバーシップ](/docs/aao/users) — 完全なアクセス(ワーキンググループ、認定、Slack)のため AgenticAdvertising.org に参加 # ワーキンググループ Source: https://adcp-docs-ja.pier1.co.jp/docs/community/working-group Ad Context Protocol ワーキンググループは、プラットフォーム事業者、広告主、代理店、開発者が集まり、AI を活用した広告の未来を共に形作るオープンなコミュニティです。 ## Join the Discussion 主なコラボレーションは Slack 上で行っています。 **[→ Slack で AdCP コミュニティに参加する](https://join.slack.com/t/agenticads/shared_invite/zt-3h15gj6c0-FRTrD_y4HqmeXDKBl2TDEA)** ## What We Discuss * **プロトコル開発**: 新機能や改善案の提案と議論 * **実装に関する質問**: プラットフォームへの AdCP 導入に関するサポート * **ユースケース**: 実際の利用例や適用事例の共有 * **ベストプラクティス**: 他社の経験から学び、知見を共有 * **今後の方向性**: AdCP のロードマップ策定への貢献 ## How to Participate 1. **ディスカッションを始める**: アイデア共有、質問、変更提案 2. **会話に参加する**: 既存のトピックへのコメント 3. **経験を共有する**: 実装の取り組みや学びを共有 4. **他のメンバーを支援する**: 質問への回答や専門知識の提供 ## Stay Updated * **Slack チャンネルに参加**: トピック別のディスカッションに参加 * **アナウンスをフォロー**: #announcements チャンネルに重要なお知らせを投稿 * **プロジェクトにスターを付与**: GitHub でサポートし最新情報を追う ## Other Ways to Connect * **メール**: 個別の問い合わせは [hello@adcontextprotocol.org](mailto:hello@adcontextprotocol.org) まで * **GitHub Issues**: バグ報告や機能要望は [issue tracker](https://github.com/adcontextprotocol/adcp/issues) へ 皆さまと一緒に活動できることを楽しみにしています。 ## Governance WG の運用手順 — 定足数、投票しきい値、忌避ポリシー、エスカレーションパス — は [ワーキンググループ憲章](/docs/governance/working-group-charter) で公開されています。 # ストーリーボードの作成 Source: https://adcp-docs-ja.pier1.co.jp/docs/contributing/storyboard-authoring AdCP コンプライアンスストーリーボードの作成方法: 正準アカウント形状、セッションスコープ lint、sync_plans のプランレベルアイデンティティ、クロステナントプローブのオプトアウト。 # ストーリーボードの作成 — スコープルール コンプライアンスストーリーボードは、コンプライアンスバンドルの正準な作成ソースである `static/compliance/source/` の下に存在します。`domains/` や `index.json` のような生成キャッシュアーティファクトをそこに追加しないでください。`scripts/build-compliance.cjs` が開発中に `dist/compliance/latest/` にそれらを作成し、リリース時に `dist/compliance/{version}/` にスタンプします。 セッション状態をテナントでスコープするトレーニングエージェントタスクを呼び出す各ステップは、`sample_request` にブランドまたはアカウントアイデンティティを運ば **なければなりません**。さもなければ呼び出しは `open:default` に着地し、アイデンティティを *運ぶ* 後続ステップが `open:` に書き込みます — あなた自身の今作成したメディアバイに対して `MEDIA_BUY_NOT_FOUND` を与えます。 このルールは `scripts/lint-storyboard-scoping.cjs` によってビルド時に強制され、`npm run build:compliance` の一部として実行されます。 ## 正準アイデンティティ形状 `account { brand, operator }` を使います。`AccountRef` スキーマは、自然キー形式(`brand`)が使われるときは常に `operator` を要求します — 仕様レベルに「ブランドだけ」の形状はありません。 ```yaml theme={null} sample_request: account: brand: domain: "acmeoutdoor.example" operator: "pinnacle-agency.example" # ... ``` 明示的アカウント形式(セラーが `list_accounts` 経由で `account_id` を発行したとき): ```yaml theme={null} sample_request: account: account_id: "acc_acme_001" # ... ``` `sync_plans` では、アイデンティティは各プランエントリー内に存在します。`sync-plans-request` スキーマは各プランアイテムに `brand` を定義し、そこでの `account` を禁止します — `plans[]` 内でラッパー形式を使わないでください: ```yaml theme={null} sample_request: plans: - plan_id: "plan-001" brand: domain: "acmeoutdoor.example" # ... ``` ## トップレベル `brand` はどうか? 一部の AdCP リクエスト(`create_media_buy`、`get_products`、`build_creative`)はトップレベル `brand` フィールドを持ちます。それは **キャンペーンのブランド** で、別のスキーマフィールドです — アイデンティティの略記ではありません。`create_media_buy` は `account` と `brand` の両方を要求します。一方が他方を代替しません。 lint は依然として、トレーニングエージェントの `sessionKeyFromArgs` がそれを読むため、むき出しのトップレベル `brand.domain` をフォールバックとして受け入れます — が、それはトレーニングエージェントのルーティング詳細で、仕様正準な形状ではありません。新しいストーリーボードは `account { brand, operator }` を使うべきです。 ## どのタスクがセッションスコープか? 権威的なリストは `scripts/lint-storyboard-scoping.cjs` に `TENANT_SCOPED_TASKS` として存在します。パリティテスト(`tests/lint-storyboard-scoping.test.cjs`)が、トレーニングエージェントの `HANDLER_MAP` に登録されたすべてのタスクが `TENANT_SCOPED_TASKS` または `EXEMPT_FROM_LINT` のいずれかに現れることをアサートします。ディスパッチテーブルに新しいツールを追加して分類を忘れると、パリティテストが失敗します — 静かなドリフトは起こりません。 経験則: タスクの **リクエストスキーマがグローバルに一意なスコープ ID を要求** するなら(`plan_id`、`rights_id`、`standards_id`、`list_id`、`event_source_id`)、セラーはその ID だけからテナントを解決できます — エンベロープアイデンティティは冗長で、lint はそれを要求しません(`EXEMPT_FROM_LINT` バケット (c) を参照)。 それ以外すべては `TENANT_SCOPED_TASKS` に該当します: スコープ ID のない create/update ミューテーション、単一リソース ID を運ばない list/get 操作、スキーマに `standards_id` のないリソース標準呼び出し、など。これらは エンベロープ `account { brand, operator }` を運ばなければなりません。 ## `$context` を通じて流れるアイデンティティフィールド ステップが `context_outputs` 経由で値を `$context` にキャプチャし、後のステップがそれを `$context.` として消費するとき、両端の *エンティティタイプ* は一致しなければなりません。`advertiser_brand` とアノテーションされたフィールドからキャプチャされた値が `rights_holder_brand` とアノテーションされたフィールドとして消費されると、lint がそれをフラグします(それが #2627 バグ: 同じフィールド名、異なるエンティティ)。エンティティタイプのリストとスキーマ作成者がフィールドをどうアノテーションするかについては `docs/contributing/x-entity-annotation.md` を参照。 その他の免除カテゴリー: ペイロード配列キー付き sync タスク(`sync_accounts`、`sync_governance`、`sync_catalogs`、`sync_event_sources`)、グローバルディスカバリー(`list_creative_formats`、`get_adcp_capabilities`)、グローバルカタログ読み取り(`get_brand_identity`、`get_rights`、`update_rights`)、および `comply_test_controller` サンドボックスプリミティブ。 ### なぜ ID スコープタスクは免除だがストーリーボードは依然としてアイデンティティを運ぶか `check_governance`、`report_plan_outcome`、`acquire_rights`、`log_event`、`calibrate_content`、`validate_content_delivery`、`validate_property_delivery` はすべて、以前にブランドコンテキストでプロビジョニングされたグローバルに一意な ID(`plan_id`、`rights_id`、`standards_id` など)を要求します。仕様レベルでは、実際のセラーは ID → テナントを自身のルックアップ経由で解決します。エンベロープはアイデンティティを繰り返す必要がありません。 トレーニングエージェントの `sessionKeyFromArgs` はエンベロープアイデンティティでルーティングします。ID スコープタスクでアイデンティティを **落とす** ストーリーボードは `open:default` に着地し、plan/rights/standards を見つけられません — だからストーリーボードはとにかくエンベロープアイデンティティを運び、lint はそれを強制しないだけです。 これはサンドボックスルーティング規約で、仕様の主張ではありません。本番セラーは、エンベロープペイロードからではなく認証済みプリンシパル(bearer/OAuth/HMAC)からテナントを解決します — [テナント解決](/docs/building/integration/authentication#tenant-resolution) を参照。彼らは ID スコープタスクでエンベロープアイデンティティを必要とせず、存在しても依存しません。アイデンティティをワイヤーから外すためだけにトレーニングエージェントにクロスセッション逆インデックスを構築することは、仕様意味のないサンドボックス配管でしょう。 ## 意図的なクロステナントプローブ ステップがテナントアイデンティティなしでセッションスコープタスクをプローブすることが *意図されている* 場合 — 例: セラーがむき出しのリクエストを拒否することを検証するネガティブテスト、またはケイパビリティディスカバリープローブ — ステップをアノテーションします: ```yaml theme={null} - id: probe_without_brand task: get_media_buys scoping: global sample_request: # ... no brand/account here by design ``` 控えめに使ってください。疑わしいときはブランドアイデンティティを運びます — ほぼすべての実世界の呼び出しがそうします。 ## フィクスチャとクロスステップキャプチャ 前提条件状態(特定の `product_id` を持つ製品、既に `approved` ステータスのクリエイティブ、ガバナンスフローが参照できるプラン)を必要とするストーリーボードは、それを設定する 2 つの方法があります: テストが実行される *前* に存在する状態のための **ストーリーボードルートの宣言的 `fixtures:`** と、実行 *中に生成される* ID のための **ステップ `context_outputs:` キャプチャ**。 ### どちらをいつ使うか | Fixture origin | Pattern | Authored as | | ---------------------- | ---------------------------------------------------- | ------------------------------------------------------- | | ストーリーボードの前に存在(シードが必要) | ストーリーボードルートの `fixtures:` | 宣言的ブロック; ランナーが `comply_test_controller` `seed_*` 経由でシード | | この実行の以前のステップで生成 | 生成ステップの `context_outputs:`、後のステップの `$context.` | ランタイムでキャプチャ; この実行内に留まる | | ランナー供給(webhook URL など) | `{{runner.webhook_url:}}` | 置換変数 | **避けられるなら `sample_request` にリテラル ID をハードコードしないでください。** `media_buy_id: "mb_acme_q2_2026_auction"` のようなリテラルは、エージェントがたまたまその正確な ID を生成(または受け入れ)する場合のみ機能します。仕様準拠のエージェントは ID を自動生成します — リテラルは一致せず、何も間違っていない実装者に対してストーリーボードが失敗します。 ### パターン A — `fixtures:` + `comply_test_controller` 経由の前提条件フィクスチャ ストーリーボードルートでフィクスチャを宣言します。`prerequisites.controller_seeding: true` を設定して、ランナーにメインフェーズの前にフィクスチャフェーズを自動注入するよう伝えます。 ```yaml theme={null} id: sales_non_guaranteed prerequisites: controller_seeding: true description: "Requires a seeded product and approved creative." fixtures: products: - product_id: "test-product" delivery_type: "non_guaranteed" pricing_options: - pricing_option_id: "test-pricing" pricing_model: "cpm" currency: "USD" creatives: - creative_id: "campaign_hero_video" status: "approved" format_id: { id: "video_30s" } phases: - id: place_buy steps: - id: create_buy task: create_media_buy sample_request: packages: - product_id: "test-product" # ← seeded above pricing_option_id: "test-pricing" # ← seeded above ``` ランナーは、`place_buy` を実行する前に(外部キー順で)`scenario: seed_product`、`scenario: seed_pricing_option`、`scenario: seed_creative` で `comply_test_controller` を呼ぶフィクスチャフェーズを注入します。シードシナリオを実装するエージェントは箱から出してすぐ通過します。シードで `UNKNOWN_SCENARIO` を返すエージェントは、ストーリーボードを `failed` ではなく `not_applicable` としてグレードさせます — 実装者はサンドボックス専用の表面が欠けていることでペナルティを受けません。 ベンダーメトリックストーリーボードが決定論的な外部 `measurement.metrics[]` スナップショットを必要とするとき、`scenario: seed_measurement_catalog` を持つ明示的な `comply_test_controller` ステップを追加します。製品フィクスチャ内の `measurement_catalogs[]` は、同じストーリーボードがセラーの製品レベルケイパビリティフィールドも運ぶ必要があるときの互換性フォールバックとしてのみ使ってください。 シードシナリオとそのパラメーターの完全なリストは [コンプライアンステストコントローラー — シナリオ](/docs/building/implementation/comply-test-controller#scenarios) を参照。 ### パターン B — `context_outputs:` + `$context.` 経由のフロー由来キャプチャ 生成ステップが返した ID をキャプチャし、下流ステップで `$context.` で参照します。 ```yaml theme={null} steps: - id: create_buy task: create_media_buy sample_request: packages: [...] context_outputs: - name: media_buy_id path: "media_buy_id" # JSON path against this step's response - id: check_buy task: get_media_buys sample_request: media_buy_ids: ["$context.media_buy_id"] # ← resolved at run time ``` ランナーは(バリデーションが通過した後)`create_buy` のレスポンスから `media_buy_id` をキャプチャし、実行スコープのコンテキストアキュムレーターに格納し、次に送信前に `check_buy.sample_request` のリテラル文字列 `$context.media_buy_id` を置換します。エージェントは実際の ID を見ます — リテラル `$context.foo` トークンを決して見ません。 キャプチャ失敗はリーダーではなく *生成* ステップをグレードします: レスポンスが宣言されたパスに `media_buy_id` を含まない場合、`create_buy` が `capture_path_not_resolvable` で失敗します。これは意図的です — ストーリーボードが宣言したコントラクト(「このステップは `media_buy_id` を生成する」)が失敗したもので、それを使おうとしたステップではありません。 ### コンテキストブロックとエコーコントラクト レスポンスの `context` をアサートするストーリーボードは、sample\_request に `context:` ブロックを送らなければなりません(MUST): ```yaml theme={null} sample_request: packages: [...] context: correlation_id: "sales_non_guaranteed--create_buy" validations: - check: field_value path: "context.correlation_id" value: "sales_non_guaranteed--create_buy" description: "Agent echoes context verbatim" ``` ランナーは、それを省略する sample\_request に `context:` を自動注入 **しません**。バリデーターがレスポンスに `context.correlation_id` を期待するがその sample\_request に `context:` が欠けているストーリーボードは、作成バグです — 呼び出し元が何も送らなかったとき、エージェントはコンテキストを省略することが許可されて(かつ要求されて)います。 エージェント側のルールについては [コンテキストとセッション — 規範的エコーコントラクト](/docs/building/integration/context-sessions#normative-echo-contract) を参照。 ## Asserting on errors AdCP はエラーを 2 つの層で表面化します([エラー処理 — エンベロープ対ペイロード](/docs/building/implementation/error-handling#envelope-vs-payload-errors-the-two-layer-model) を参照)。ストーリーボードは、準拠エージェントがどの層でエラーを表面化したかにかかわらず機能する方法で、エラー形状をアサートしなければなりません(MUST)。 **`check: error_code` を使う — `check: field_present, path: "errors"` ではなく。** ```yaml theme={null} # ✅ Shape-agnostic — resolves from either adcp_error (envelope) or errors[] (payload) validations: - check: error_code value: "BUDGET_TOO_LOW" description: "Budget validation rejected with BUDGET_TOO_LOW" # ✅ Multiple acceptable codes validations: - check: error_code allowed_values: ["VALIDATION_ERROR", "INVALID_REQUEST", "BUDGET_TOO_LOW"] # ❌ Pins to the payload `errors[]` shape — fails against agents that surface # errors only via the transport envelope (MCP `adcp_error`, A2A DataPart) validations: - check: field_present path: "errors" ``` `value:` または `allowed_values:` で使われるすべてのコードは、`static/schemas/source/enums/error-code.json` の正準エラーコード enum に存在しなければなりません(MUST)。`lint:error-codes` スクリプト(`npm run test` に組み込まれている)はすべてのストーリーボードを歩き、enum にないコードへの参照を拒否します — 任意のテストが実行される前のビルド失敗です。 リネームが必要なとき、古いコードを `scripts/error-code-aliases.json` に登録します。ファイルは純粋なデータで(それを読む lint スクリプトの隣に存在し、スキーマツリーにはない)、デフォルトで空の `aliases` マップとともに出荷されます: ```json theme={null} { "aliases": { "OLD_CODE": "NEW_CODE" } } ``` エイリアスされたコードは、非推奨ウィンドウの間 **警告** として lint を通過し、作成者にストーリーボードを移行する時間を与えます。エイリアスがファイルから削除されると、古いコードへの参照は lint エラーになります。これがバージョン全体でストーリーボード作成を壊さずにリネームが着地する方法です。 ## 分岐可能な動作のアサート 一部の仕様要件は複数の準拠エージェント動作を許可します — 例: 操作がセラーポリシーに応じて即座の成功 OR `pending_review` を返しうる。1 つの分岐のみをアサートする単一アサーションバリデーターは、他の分岐を選んだ準拠エージェントを静かに失敗させます。 仕様が分岐可能な結果を許可するとき、ストーリーボードを並行のオプションフェーズに分割し、`assert_contribution` 経由で解決します: ```yaml theme={null} phases: - id: reject_path optional: true steps: - id: probe_reject expect_error: true contributes_to: behavior_handled validations: - check: error_code value: "INVALID_REQUEST" - id: adjust_path optional: true steps: - id: probe_adjust contributes_to: behavior_handled validations: - check: response_schema - check: field_present path: "media_buy_id" - id: enforcement steps: - id: require_either task: assert_contribution validations: - check: any_of allowed_values: ["behavior_handled"] description: "Agent must exhibit one of the conformant branches." ``` `optional: true` フェーズ内の失敗はストーリーボードを失敗させません — 最終フェーズの合成 `assert_contribution` のみが、かつどの分岐も貢献しなかったときのみ失敗させます。準拠エージェントは正確に 1 つの分岐を通過し、設計上他方を失敗させます。 選ばれなかった分岐の失敗ステップは、`failed` ではなくスキップ理由 `peer_branch_taken` でランナーによってレポートされなければなりません(MUST)。これは準拠エージェントのランナーサマリーを正確に保ち(他分岐の失敗は本物の失敗ではなかった)、ダッシュボードカバレッジシグナルをクリーンに保ちます(`peer_branch_taken` はランタイムルーティング; `not_applicable` はプロトコルカバレッジギャップ用)。規範的ルールについては `universal/storyboard-schema.yaml` §「Per-step grading in any\_of branch patterns」と `universal/runner-output-contract.yaml` > `skip_result.reasons.peer_branch_taken` を参照。 観測可能な結果が分岐全体で異なる任意の仕様 `MAY` / `any_of` にこの形状を使ってください。過去の `create_media_buy.start_time` にはこれを使わないでください。そのケースは現在 `INVALID_REQUEST` で拒否のみです。 単一コード `check: error_code` は、仕様がシナリオに正準コードを義務付けるとき(例: ガバナンス拒否結果での `GOVERNANCE_DENIED`、再キャンセルでの `NOT_CANCELLABLE`)は依然として正しいです。分割フェーズパターンは、仕様自体が結果を分岐可能に残すときのみ適用されます。 ### このパターンを使わないとき 並行オプションフェーズ + `assert_contribution` 形状は、**仕様テキスト自体** が複数の観測可能な結果を許可するとき(規範的散文の明示的な `MAY`/`OR`、または受け入れ可能なステータスの enum を探す)のみ適切です。エージェントの動作が仕様からドリフトしたからベクターを軟化させるツールでは **ありません**。以下にこのパターンを適用しないでください: * **冪等性セマンティクス。** ミューテーションタスクで欠けているとき `idempotency_key` は拒否されなければならない; リプレイはキャッシュされたレスポンスを返さなければならない; コンフリクトは `IDEMPOTENCY_CONFLICT` を表面化しなければならない。仕様は単一の動作を義務付けます — 他のどの結果も準拠せず、有効な分岐ではありません。 * **コンテキストエコー。** レスポンスは、呼び出し元が送ったとき `context:` を逐語的にエコーしなければなりません(MUST)。エコーを省略する準拠分岐はありません。 * **エラーコード語彙。** `static/schemas/source/enums/error-code.json` に列挙された正準コードは、シナリオごとに単一値です。ストーリーボードがガバナンス拒否結果で `GOVERNANCE_DENIED` をアサートするなら、それがコードです — いくつかの中の 1 つのオプションではありません。 * **Webhook 署名の正しさ。** AdCP の covered-components プロファイルによる RFC 9421 署名は単一の検証形状です; 代替分岐はありません。 失敗するベクターを乗り越えるために分割フェーズパターンに手を伸ばしていることに気づいたら、まず仕様が受け入れたい分岐を実際に許可するか検証してください。許可しないなら、修正はベクターではなくエージェント(または仕様)にあります。 ## 新しい専門分野へのカタログ置換安全性フェーズの追加 カタログアイテムマクロを URL にレンダーする専門分野(カタログ駆動セールス、生成セラー、リテールメディアなど)を追加する場合、ストーリーボードは [`docs/creative/universal-macros.mdx#substitution-safety-catalog-item-macros`](../creative/universal-macros.mdx#substitution-safety-catalog-item-macros) のルールセットをカバーする置換安全性フェーズを含めるべきです(SHOULD)。 **テンプレートから始めてください。兄弟専門分野からコピペしないでください。** 正準な 3 ステップフェーズ(`sync_*_probe_catalog` → `build_*_probe_creative` → `expect_substitution_safe`)は、 [`static/compliance/source/test-kits/substitution-observer-runner.yaml`](https://github.com/adcontextprotocol/adcp/blob/main/static/compliance/source/test-kits/substitution-observer-runner.yaml) に `phase_template:` コメントブロックとして存在します。 ブロックは専門分野固有のビット(ブランドドメイン、catalog\_id プレフィックス、冪等性プレフィックス)に `<>` トークンを使うので、それらのトークンに対する単純なテキスト置換を行うことで新しいフェーズを具体化できます。 `sales-catalog-driven` または `creative-generative` からほぼクローンをコピーすることは原理的には機能しますが、[#2654](https://github.com/adcontextprotocol/adcp/issues/2654) の DX レビュアーは、3 つのコンシューマーが些細なドリフト(`item_id` のミススペル、`require_every_binding_observed: true` の欠落)が始まる変曲点であることをフラグしました。テンプレートはドリフト回避表面で、`lint:substitution-vector-names` スクリプト([#2655](https://github.com/adcontextprotocol/adcp/issues/2655))が vector\_name 参照のタイポを捕捉します。 ## lint をローカルで実行する ```bash theme={null} npm run build:compliance # includes the lint node scripts/lint-storyboard-scoping.cjs # lint only npm run test:storyboard-scoping # parity test ``` 典型的な失敗出力: ``` ✗ storyboard scoping lint: 1 violation(s) protocols/media-buy/scenarios/invalid_transitions.yaml:setup/create_buy (create_media_buy) — sample_request missing brand/account Fix: add `account { brand, operator }` to sample_request, e.g. sample_request: account: brand: domain: "acmeoutdoor.example" operator: "pinnacle-agency.example" ``` # Testable examples demo Source: https://adcp-docs-ja.pier1.co.jp/docs/contributing/testable-examples-demo # テスト可能なドキュメント例 このページは、実際のテストエージェントに対して動作する完全なコード例を使い、テスト可能なドキュメント機能を紹介します。 ## JavaScript の例 ### クリエイティブフォーマットの一覧取得 ```javascript theme={null} import { testAgent } from '@adcp/client/testing'; const result = await testAgent.listCreativeFormats({}); console.log(`✓ Found ${result.data?.formats?.length || 0} creative formats`); ``` ## Python の例 ### クリエイティブフォーマットの一覧取得 ```python theme={null} import asyncio from adcp.testing import test_agent async def list_formats(): result = await test_agent.simple.list_creative_formats() print(f"✓ Found {len(result.formats)} supported creative formats") asyncio.run(list_formats()) ``` ## CLI の例 ### uvx (Python CLI) の使用例 ```bash theme={null} uvx adcp \ https://test-agent.adcontextprotocol.org/mcp \ list_creative_formats \ '{}' \ --auth 1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ ``` ## テスト可能なドキュメントの仕組み フロントマターに `testable: true` を設定すると、このページ内のすべてのコードブロックが抽出され、テスト時に実行されます。 ### テストの実行 ```bash theme={null} # Run all tests including snippet validation npm run test:all ``` ### テスト可能ページの要件 すべてのコードブロックは次を満たす必要があります。 * 完結しており自己完結していること * 必要な依存関係をすべてインポートしていること * エラーなく実行できること * 成功を確認できる出力を生成すること ### ページをテスト可能とマークするタイミング ページに `testable: true` を付けるのは、次の条件をすべて満たす場合に限ります。 * すべてのコードブロックが完全な実行例です * コード断片や不完全なスニペットがない * すべての例がテストエージェントの認証情報を使用しています * 依存関係がインストール済みである(`@adcp/client`, `adcp`) ### ページをテスト可能としない場合 次のようなページに `testable: true` を付けてはいけません。 * パターンを示すだけのコード断片があります * 不完全な例が含まれます * 概念的な疑似コードが含まれます * 本番用の認証情報が必要な例があります * テスト可能な内容とそうでない内容が混在しています 詳しくは [Testable Snippets Guide](./testable-snippets.md) を参照してください。 # Testable snippets Source: https://adcp-docs-ja.pier1.co.jp/docs/contributing/testable-snippets # テスト可能なドキュメントスニペットの書き方 このガイドでは、AdCP ドキュメントに記述したコード例を自動テストで検証する方法を説明します。 ## なぜドキュメントスニペットをテストするのか ドキュメントの例を自動テストすることで、次のことが保証されます。 * 例が最新の API に追随します * コードスニペットが記載どおりに動作します * 破壊的変更を即座に検知できます * ドキュメントへの信頼性が高まる **重要**: テスト基盤は、ドキュメントファイル(`.md` と `.mdx`)内のコードブロックを **そのまま** 検証します。フロントマターで `testable: true` を付けると、そのページ内のすべてのコードブロックが抽出され実行されます。 ## ページをテスト対象にします ページ全体をテスト対象にするには、フロントマターに `testable: true` を追加します。 ```markdown theme={null} --- title: get_products testable: true --- # get_products ...all code examples here will be tested... ``` **重要な原則**: ページは完全にテスト可能であるか、まったくテストしないかのどちらかです。テスト可能と非テスト可能なコードブロックが混在するページはサポートしません。 ### コードブロックの例 ページに `testable: true` を付けると、すべてのコードブロックが実行されます。 ````markdown theme={null} ```javascript import { testAgent } from '@adcp/client/testing'; const products = await testAgent.getProducts({ brief: 'Premium athletic footwear with innovative cushioning', brand_manifest: { name: 'Nike', url: 'https://nike.com' } }); console.log(`Found ${products.products.length} products`); ``` ```` ### Snippet Metadata ローカルの前提条件を必要とする例には、スニペットメタデータを使います。 ````markdown theme={null} ```bash requires-env=ADCP_AUTH_TOKEN uvx adcp https://test-agent.adcontextprotocol.org/sales/mcp get_products '{}' --auth $ADCP_AUTH_TOKEN ``` ```javascript integration=true // Runs only when snippet integration tests are enabled. ``` ```` `requires-env=NAME` は、名前付き環境変数が設定されていないときにスニペットをスキップします。`integration=true` は、デフォルトのローカル実行でスニペットをスキップします。それらの例は `node tests/snippet-validation.test.cjs --integration` または `SNIPPET_INTEGRATION=true` で実行します。 ### テストヘルパーの活用 簡潔な例を示す場合は、クライアントライブラリに含まれるテストヘルパーを使ってください。 **JavaScript:** ```javascript theme={null} import { testAgent, testAgentNoAuth } from '@adcp/client/testing'; // Authenticated access const fullCatalog = await testAgent.getProducts({ brief: 'Premium CTV inventory' }); // Unauthenticated access const publicCatalog = await testAgentNoAuth.getProducts({ brief: 'Premium CTV inventory' }); ``` **Python:** ```python theme={null} import asyncio from adcp.testing import test_agent, test_agent_no_auth async def example(): # Authenticated access full_catalog = await test_agent.simple.get_products( brief='Premium CTV inventory' ) # Unauthenticated access public_catalog = await test_agent_no_auth.simple.get_products( brief='Premium CTV inventory' ) asyncio.run(example()) ``` ## ベストプラクティス ### 1. テストエージェントの認証情報を使います 例には常にパブリックテストエージェントを使用してください。 * **Test Agent URL**: `https://test-agent.adcontextprotocol.org` * **MCP Token**: `1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ` * **A2A Token**: `L4UCklW_V_40eTdWuQYF6HD5GWeKkgV8U6xxK-jwNO8` ### 2. 例は自己完結させる テスト対象のスニペットは次を満たしてください。 * 必要な依存関係をすべてインポートします * 接続を初期化します * 完結した処理を実行します * 目に見える出力を返す(console.log など) **良い例:** ```javascript theme={null} // Example of a complete, testable snippet import { AdcpClient } from '@adcp/client'; const client = new AdcpClient({ agentUrl: 'https://test-agent.adcontextprotocol.org/mcp', protocol: 'mcp', bearerToken: '1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ' }); const products = await client.getProducts({ promoted_offering: 'Nike Air Max 2024' }); console.log('Success:', products.products.length > 0); ``` **悪い例(不完全):** ```javascript theme={null} // Don't mark this for testing - it's incomplete const products = await client.getProducts({ promoted_offering: 'Nike Air Max 2024' }); ``` ### 3. Use sandbox accounts 状態を変更する操作(作成・更新・削除)を示すときは、サンドボックスアカウント参照を使います。 ```javascript theme={null} // Example using sandbox account — no real campaign created const mediaBuy = await client.createMediaBuy({ account: { brand: { domain: 'acme-corp.com' }, operator: 'acme-corp.com', sandbox: true }, product_id: 'prod_123', budget: 10000, start_date: '2025-11-01', end_date: '2025-11-30' }); console.log('Sandbox media buy created:', mediaBuy.media_buy_id); ``` ### 4. 非同期処理を扱います JavaScript/TypeScript の例では `await` または `.then()` を使用してください。 ```javascript theme={null} // Using await (recommended) const products = await client.getProducts({...}); // Or using .then() client.getProducts({...}).then(products => { console.log('Products:', products.products.length); }); ``` ### 5. 例は焦点を絞る 各スニペットでは 1 つの概念のみを示してください。 ```javascript theme={null} // 良い例: 認証を示す import { AdcpClient } from '@adcp/client'; const client = new AdcpClient({ agentUrl: 'https://test-agent.adcontextprotocol.org/mcp', protocol: 'mcp', bearerToken: '1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ' }); console.log('Authenticated:', client.isAuthenticated); ``` ## テスト可能としてマークしないケース 次のようなドキュメントページには `testable: true` を付けるべきではありません。 ### 1. 疑似コードや概念例を含むページ 実行を想定していない概念的な例が含まれている場合: ```javascript theme={null} // Conceptual workflow - not actual code const result = await magicFunction(); // ✗ Not a real function ``` ### 2. 不完全なコード断片を含むページ 説明のために部分的なコードスニペットを示している場合: ```javascript theme={null} // Incomplete fragment showing field structure budget: 10000, start_date: '2025-11-01' ``` ### 3. 設定/スキーマ例を含むページ JSON スキーマや設定構造を示すドキュメントの場合: ```json theme={null} { "product_id": "example", "name": "Example Product" } ``` ### 4. レスポンス例を含むページ API レスポンス(リクエストではなく)の例を示すページ: ```json theme={null} { "products": [ {"product_id": "prod_123", "name": "Premium Display"} ] } ``` ### 5. テスト可能・非テスト可能なコードが混在するページ 実行可能なコードと概念的なコードが混在する場合はページを分割してください。 * 完全に実行可能な例を載せたページに `testable: true` * 概念的/部分的な例だけのページにはフラグを付けない **注意**: テスト可能にしたページではすべてのコードブロックが実行されます。1 つでも実行できないブロックがある場合はテスト可能にしないでください。 ## スニペットテストの実行 ### ローカルで実行 ドキュメントのスニペットをすべてテスト: ```bash theme={null} npm test ``` スニペットテストだけを実行: ```bash theme={null} node tests/snippet-validation.test.js ``` このコマンドは以下を行います。 1. `docs/` 配下の `.md` と `.mdx` をすべて走査 2. フロントマターに `testable: true` があるページを検出 3. それらのページから **すべての** コードブロックを抽出 4. 各スニペットを実行して結果を報告 5. 失敗した場合はエラー終了 ### Coverage Reporting スキーマ裏付けの JSON 例と実行可能スニペットがどこに集中しているかを見るには、ドキュメント例カバレッジレポートを使います。 ```bash test=false theme={null} npm run docs:example-coverage ``` レポートは `docs/` をスキャンし、次を表示します。 * `$schema` を含み、したがって `npm run test:json-schema` でカバーされる JSON ブロック * `$schema` のない完全な JSON ブロック * `npm run test:snippets` でカバーされる実行可能な JavaScript、TypeScript、Python、シェルのスニペット * 未検証の JSON または未テストの実行可能スニペットのギャップが最も大きいトップファイル CI ダッシュボードや保存済みベースライン用に、機械可読な出力を出します。 ```bash test=false theme={null} npm run --silent docs:example-coverage -- --json ``` GitHub のジョブサマリー用に、Markdown を出します。 ```bash test=false theme={null} npm run --silent docs:example-coverage -- --markdown ``` スキーマ検証は、ワイヤープロトコルが拡張可能な場所では拡張フィールドを受け入れます。スキーマ裏付けのドキュメント例を未知の公開的なフィールドについて監査するには、次を実行します。 ```bash test=false theme={null} npm run docs:json-field-audit ``` フィールド監査はデフォルトでアドバイザリです。`scripts/docs-json-field-audit-baseline.json` に対して意図的にラチェットするときのみ `--check` を使います。 ```bash test=false theme={null} npm run docs:json-field-audit -- --check ``` 意図的に検出事項をクリーンアップするときは、同じ変更でベースラインをリフレッシュします。 ```bash test=false theme={null} npm run docs:json-field-audit -- --update-baseline ``` ### CI/CD での実行 スニペットテストを含むフルテストスイートは次で実行できます。 ```bash theme={null} npm run test:all ``` このコマンドには次が含まれます。 * スキーマ検証 * 例の検証 * スニペット検証 * TypeScript の型チェック ## サポート言語 現在テストでサポートされている言語: * **JavaScript** (`.js`, `javascript`, `js`) * **TypeScript** (`.ts`, `typescript`, `ts`) - compiled to JS * **Bash** (`.sh`, `bash`, `shell`) - only `curl` commands * **Python** (`.py`, `python`) - requires Python 3 installed ### 制約 **パッケージ依存**: 外部パッケージ(`@adcp/client` や `adcp` など)をインポートするスニペットが動作するのは、次のいずれかを満たす場合のみです。 1. パッケージがリポジトリの `node_modules` にインストールされています 2. もしくは `devDependencies` にパッケージが記載されています クライアントライブラリが必要な例では、次の選択肢があります。 * **オプション 1**: ライブラリを `devDependencies` に追加し、テストでインポートできるようにします * **オプション 2**: そのスニペットをテスト可能にしない(概念的な例として記載します) * **オプション 3**: 依存関係のない curl/HTTP の例をテスト可能なドキュメントとして使います ## テスト失敗時のデバッグ スニペットテストが失敗した場合は次を確認してください。 1. **エラーメッセージを確認** - どのファイルの何行目で失敗したかが表示されます 2. **手動で実行** - コードをコピーしローカルで実行します 3. **テストエージェントへのアクセス確認** - [https://test-agent.adcontextprotocol.org](https://test-agent.adcontextprotocol.org) を確認 4. **依存関係を確認** - すべての import が利用可能か確認 5. **スニペットを見直す** - 自己完結しているか検証 Example error output: ``` Testing: quickstart.mdx:272 (javascript block #6) ✗ FAILED Error: Cannot find module '@adcp/client' ``` これは `@adcp/client` パッケージのインストールが必要であることを示しています。 ## 貢献ガイドライン 新しいドキュメントを追加するときは次を守ってください。 1. ✅ すべてのコードブロックが実行可能ならページ全体に `testable: true` を付ける 2. ✅ シンプルな例にはクライアントライブラリのテストヘルパーを使います 3. ✅ コミット前にローカルでスニペットをテストする(`npm test`) 4. ✅ 例は自己完結かつ完全な形にします 5. ✅ 例ではテストエージェントの認証情報を使います 6. ❌ 不完全な断片が 1 つでもあるページをテスト可能にしません 7. ❌ 疑似コードを含むページをテスト可能にしません 8. ❌ 同じページにテスト可能コードと非テスト可能コードを混在させない 9. ❌ 例に本番用の認証情報を使わない ## 質問がありますか? * `docs/quickstart.mdx` にある既存のテスト可能な例を確認します * テストスイートを確認します: `tests/snippet-validation.test.js` * [Slack コミュニティ](https://join.slack.com/t/agenticads/shared_invite/zt-3c5sxvdjk-x0rVmLB3OFHVUp~WutVWZg) で質問します # x-entity スキーマアノテーション Source: https://adcp-docs-ja.pier1.co.jp/docs/contributing/x-entity-annotation エンティティアイデンティティを運ぶ AdCP スキーマフィールドをどうアノテーションするか、クロスストーリーボードの context-entity lint が #2627 brand_id advertiser 対 rights-holder ケースのような混同バグを捕捉できるように。 # `x-entity` スキーマアノテーション ## スキーマ作成者向け TL;DR スキーマを編集していて、追加(またはレビュー)しているフィールドが id、slug、または安定した参照: 1. **値はストーリーボードステップを越えるか?**(`context_outputs` 経由でキャプチャ、`$context.` として消費、またはリクエストとレスポンス間でエコー。)いいえなら、アノテーションしない。 2. 下のテーブルから **エンティティタイプを選ぶ**。どれも合わない場合、`static/schemas/source/core/x-entity-types.json` の完全なレジストリを読む — それでも何もない場合、追加するためレジストリに PR。 3. リーフプロパティの `type` の隣に **`x-entity: ` を追加**。`$ref` された共有タイプには、使用サイトではなく共有タイプにアノテーション。多くの既知の id フィールドを持つドメインスイープには、`node scripts/add-x-entity-annotations.mjs [--overlay ]` を実行 — ベースマップは `scripts/x-entity-field-map.json`、ドメインごとのオーバーレイが曖昧な名前(`list_id`、`plan_id`、`pricing_option_id`)を解決。スクリプトは書き込み前にすべての値をレジストリに対して検証するので、タイポはハード失敗。 4. フィールドがリクエストからの値のパススルー **エコー** の場合、両側で **同じ** エンティティタイプでアノテーション。 lint は `x-entity` のないフィールドで沈黙するので、部分的ロールアウトは安全です。 ## なぜこれが存在するか 一部の AdCP スキーマは単一のフィールド名 — `brand_id`、`list_id`、`plan_id` — を、異なるコンテキストで **異なる種類のエンティティ** を参照する値に使います。最も引用される例: `brand_id` は「アドバタイザーのブランド」(`get_brand_identity` から)または「権利保有者 / タレントブランド」(`get_rights` 内)を意味しうる。同じ JSON 形状、異なるエンティティ。両方ともローカルで有効。不一致は、ストーリーボードが 1 つの種類の値を `$context` にキャプチャし後のステップが他を期待して消費するときのみ表面化 — [issue #2627](https://github.com/adcontextprotocol/adcp/issues/2627) で追跡されるとおり。 `x-entity` は、各アイデンティティを運ぶフィールドを値が解決する *エンティティタイプ* でタグ付けする非検証 JSON Schema アノテーションです。context-entity lint(`scripts/lint-storyboard-context-entity.cjs`)は、ストーリーボードの `context_outputs` キャプチャサイトと `$context.` 消費サイトを歩き、両端で `x-entity` を読み、不一致をフラグします。 ## いつ追加するか 以下のとき、かつそのときのみフィールドに `x-entity` を追加: 1. フィールドの値がビジネスエンティティへの id、slug、または安定した参照、**かつ** 2. ストーリーボードがその値をステップ全体でキャプチャまたは消費する可能性がある(`context_outputs` または `$context.` 経由)。 リクエストフィールドとレスポンスフィールドの両方がアノテーションを取ります。`$ref` で参照される共有タイプ(例: `core/brand-id.json`)はアノテーションを一度運びます。それはすべての使用サイトで適用されます。 **エコーフィールド**(クライアントがリクエストで送った値をパススルーするレスポンスフィールド)は、リクエスト側と同じエンティティタイプでアノテーションされる *べき* です。lint はキャプチャと消費を対称的に扱います — アノテーションされたエコーは、ストーリーボードがそれを `$context` に再キャプチャし誤解を招く名前の下で転送するときを捕捉します。 以下は **アノテーションしない**: * 一時的なリクエストスコープの値(`idempotency_key`、`request_id`、`correlation_id`)。 * 純粋に記述的なフィールド(表示名、URL、フリーテキスト)。 * ストーリーボードステップ境界を越えないフィールド。 * Enum 値(`right_type`、`audience_type`) — それらはタグで、エンティティ参照ではない。 ## 配置 リーフプロパティ定義の、`type` / `description` の隣: ```json theme={null} { "properties": { "brand_id": { "type": "string", "description": "Brand identifier from the agent's roster", "x-entity": "rights_holder_brand" } } } ``` エンティティの配列には、アイテムスキーマにアノテーション: ```json theme={null} { "rights": { "type": "array", "items": { "properties": { "rights_id": { "type": "string", "x-entity": "rights_contract" } } } } } ``` 共有 `$ref` タイプ(例: `core/brand-id.json`)には、共有タイプにアノテーション。すべての使用サイトがエンティティタイプを継承: ```json theme={null} { "$id": "/schemas/core/brand-id.json", "type": "string", "x-entity": "advertiser_brand" } ``` **共有タイプ不変条件:** 共有タイプが `x-entity` を運ぶと、それへのすべての `$ref` がそのエンティティスコープを主張します。`core/brand-id.json` は `advertiser_brand` とタグ付けされているので、権利保有者 / タレントロスターブランド id はそのタイプを再利用できません — 文字列形状が同一でも別の共有タイプ(例: `core/rights-holder-brand-id.json`)を作成します。lint は共有タイプを真実の源泉として扱います。それをスコープ全体で黙って再利用することが、私たちが捕捉するバグです。 共有タイプがコンテキスト全体で曖昧に使われる場合、アノテーションを省略するのではなく *タイプを分割* します — 曖昧性が lint が捕捉するために存在する問題です。 ### `oneOf` / `anyOf` / `allOf` バリアントを持つ共有タイプ 共有タイプのルートが複合(`oneOf` / `anyOf` / `allOf`)ですべての分岐が同じエンティティに解決する場合、ルートで一度アノテーション — lint はバリアントに降下する前にルートレベル `x-entity` を読むので、オブジェクト全体のキャプチャ(例: `core/signal-id.json` の `$context.signal_id`)は各バリアントに `x-entity` を重複させずにクリーンに解決します。`core/signal-id.json` はこのパターンに従います: ルートレベル `x-entity: signal`、バリアントローカルの `id` フィールドは、`id` がそのバリアントの名前空間(`data_provider_domain` または `agent_url`)内でのみ一意なので、意図的にアノテーションされないままです。内部 `id` をアノテーションすると、2 つの異なる名前空間の id が lint に交換可能に見えます。 バリアントが *異なる* エンティティに解決する場合、**タイプを分割** します。レジストリ lint は、ウォーカーのルートレベルチェックが空パスで勝ちバリアント値を黙って落とすため、root+variant の不一致(`composite_entity_disagreement` ルール)をフラグします。 ## 登録されたエンティティタイプ 権威的なリストは `static/schemas/source/core/x-entity-types.json` に存在します。lint は未知の値を拒否します — レジストリの拡張は意図的で PR を要求します。 高レベルのグループ化(完全な説明についてはレジストリを参照)。*下のカテゴリーは方向付けのための編集グループ化のみ。`static/schemas/source/core/x-entity-types.json` のレジストリが権威的リスト。* | Category | Values | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | | Brand & rights | `advertiser_brand`, `rights_holder_brand`, `rights_grant` | | Account & party | `account`, `operator` | | Media buy | `media_buy`, `package`, `product`, `product_pricing_option` | | Creative | `creative`, `creative_format` | | Data & targeting | `audience`, `signal`, `signal_activation_id`, `event_source` | | Lists & catalogs | `collection_list`, `property_list`, `catalog`, `property` | | Plans & governance | `media_plan`, `governance_plan`, `governance_registry_policy`, `governance_inline_policy`, `governance_check`, `content_standards`, `task` | | Vendor services | `vendor_pricing_option`, `vendor_metric` | | SI | `si_session`, `offering` | **Plan 対 policy 対 check:** `governance_plan` はプランコンテナを識別(*「どのプラン?」* に答える)。`governance_registry_policy` / `governance_inline_policy` はプラン内またはプランが参照するルールを識別(*「どのルール?」*)。`governance_check` はポリシーに対するプランの特定の評価を識別(*「どのチェック?」* — `check_governance` と `report_plan_outcome` 間でラウンドトリップ)。キャプチャされた値が答える質問で選ぶ。 **Registry 対 inline ポリシー:** フィールドがグローバルに一意なレジストリ id(例: `uk_hfss`、`us_coppa`、`garm:brand_safety:violence`)を保持するとき `governance_registry_policy` を使う。フィールドが `policy-entry.json` 経由で作成されたプランスコープのカスタム id を保持するとき `governance_inline_policy` を使う。AdCP タスクスキーマの `policy-entry.json` へのすべての `$ref` は定義上インライン — レジストリエントリーは別の帯域外 API でサーブされる。フィールドがランタイムでいずれかを正当に保持できる場合(2 つの曖昧なサイト: `check-governance-response::findings[].policy_id`、`get-plan-audit-logs-response` 監査エントリー、プラス予約された `creative/creative-feature-result.json::policy_id` と `core/feature-requirement.json::policy_id`)、アノテーションされないままにし `"x-entity deliberately omitted"` で始まる `$comment` を追加 — ギャップリスターがそのフレーズを認識しリーフをスキップします。 レジストリファイルが真実の源泉です。リポジトリ全体のすべてのアノテーションされたフィールドを見るには: `git grep -l x-entity static/schemas/source`。 ### 新しいエンティティタイプの追加 スキーマ変更が任意の登録された値に合わない id を導入するとき: 1. `static/schemas/source/core/x-entity-types.json` の `enum` 配列に新しい値を追加。 2. 同じファイルの `x-entity-definitions` の下に一段落の定義を追加。id が何を識別するか、それを使うスキーマ、既知の注意点(例: 名前空間スコープ)を記述。 3. 上のカテゴリーテーブルの最も適切な行に新しい値を追加。 4. 新しい値が既存のもの(例: plan 対 policy 対 check)に隣接する場合、テーブルの下に一文の曖昧性解消を追加。 5. 値が将来のドメインスイープでパッチスクリプトによって適用される場合、正準フィールド名 → エンティティ値マッピングとともに `scripts/x-entity-field-map.json` に追加。同じフィールド名がドメインで分割する場合(`plan_id` や `list_id` のように)、`__scope_specific__` / `__ambiguous__` センチネルを使い、ドメインごとの PR が供給すべきオーバーレイパターンを文書化。 ## lint がアノテーションをどう読むか クロスストーリーボードウォーク(`scripts/lint-storyboard-context-entity.cjs`)は `npm run build:compliance` と `npm run test:storyboard-context-entity` として実行: 1. 各ストーリーボードステップの `context_outputs[].path` について、ステップの `response_schema_ref` を参照場所まで歩き、そこで `x-entity` を読む。`(capture_name → entity_type)` を記録。 2. 値が `$context.` の各ストーリーボードステップの `sample_request` フィールドについて、ステップの `schema_ref`(リクエストスキーマ)を参照フィールドまで歩き、そこで `x-entity` を読む。キャプチャテーブルで名前をルックアップ。 3. 両端が `x-entity` を持ち一致しない場合、違反をフラグ。 lint は **欠けているアノテーションで沈黙** します — 部分的ロールアウトは安全です。欠けているアノテーションは「これがどのエンティティか知らない」として扱われ、「これらは一致しなければならない」ではありません。これはアノテーションパスが偽陽性を生成せずにドメインごとに進むことを可能にします。どのドメインがアノテーションされたかチェックするには、`git grep -l x-entity static/schemas/source` を実行。 ## 関連 * レジストリ: `static/schemas/source/core/x-entity-types.json` * Lint: `scripts/lint-storyboard-context-entity.cjs` * テスト: `tests/lint-storyboard-context-entity.test.cjs` * 正準ケース: [#2627 brand\_rights storyboard conflates advertiser brand\_id with talent brand\_id](https://github.com/adcontextprotocol/adcp/issues/2627) * 追跡 issue: [#2660 Storyboard field-entity-context lint](https://github.com/adcontextprotocol/adcp/issues/2660) # 障害モード能力スコープ Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/failure-mode-scope カリキュラムスコープドキュメント — どの障害モードがどの認定モジュールに属するか、深さターゲット、評価アプローチ。作成はモジュールごとのフォローアップ issue。 # 障害モード能力スコープ このドキュメントは、各 AdCP 障害モードシナリオを、それが属する認定モジュールにマップし、ティアごとの深さターゲットを指定し、評価アプローチを定義します。これはスコープアーティファクトです — 各モジュールの実際のコンテンツの作成は、モジュールごとのフォローアップ issue で追跡されます。 **以下で使われる深さレベル:** | Level | 意味 | | --------------- | ------------------------------------------- | | 表面 / 認識 | 障害モードが存在することを知る; 促されたときに名指せる | | 診断 / 説明 | 原因、影響を受けるプロトコル表面、正しい回復パスを記述できる | | 解決 / デモンストレーション | サンドボックスエージェントに対してプロトコルツールを使い、回復をハンズオンで実行できる | | 評価 / 作成 | マルチドメインの衝突について推論し、競合するルールを裁定し、新規シナリオを構築できる | *** ## FM-1 — 冪等性リプレイ / コンフリクト / 期限切れ **ステータス:** 部分的にカバー。`#2346` と `#2367` がトレーニングエージェントに冪等性を確立しました。このエントリーは評価スコープを統合します。 | Module | Depth | 評価アプローチ | | -------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **S1**(メディアバイ) | 解決 / デモンストレーション | エラー発見: 3 呼び出しトランスクリプト — (1) 同じキー + 同じペイロードでリトライ → `replayed: true`; (2) 新しいペイロード + 同じキーで再計画 → `IDEMPOTENCY_CONFLICT`; (3) TTL 後に使われたキー → `IDEMPOTENCY_EXPIRED`。学習者はどの呼び出し元が契約に違反したか識別し、3 つすべての正しい動作を説明。 | | A2 | 表面 / 認識 | 学習者は `idempotency_key` がエージェントのリトライを実際の金銭に対して安全にすることを説明 — より深いトラブルシューティングは不要。 | | C4(ビルドプロジェクト) | 解決 / デモンストレーション | 学習者の提出エージェントが冪等性キーを正しく生成し、再計画せずにリトライを処理。 | **作成前に閉じるギャップ:** S1 ラボ演習 7(「ライフサイクル管理」)が `NOT_CANCELLABLE` だけでなく 3 つのエラー状態すべてをサンドボックスでステージングすることを確認。そうでない場合、演習 7 を `IDEMPOTENCY_CONFLICT` と `IDEMPOTENCY_EXPIRED` をカバーするよう拡張。 *** ## FM-2 — ローンチ後のクリエイティブコンプライアンス障害 **ステータス:** まだどのモジュールにもない。前提読書は S2 と S4 に存在するが、ローンチ後の発見パスをウォークするラボ演習はない。 | Module | Depth | 評価アプローチ | | --------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **S2**(クリエイティブ) | 診断 / 説明 + デモンストレーション | シナリオ: クリエイティブがビルド時に `preview_creative` コンプライアンスを通過するが、`validate_content_delivery` がフライト開始後に違反を返す(例: 配信されたバリアントがプレビューレンダーに存在する規制開示を省略)。学習者は `get_media_buy_artifacts` を使って監査アーティファクトを取得し、不一致を説明し、修復オプション(一時停止してスワップ 対 キャンセル)を記述。 | | S4(副次的) | 診断 / 説明 | 学習者は `get_media_buy_artifacts` と `validate_content_delivery` がガバナンス監査証跡にどう接続するか説明。 | | C2 | 表面 / 認識 | 学習者はクリエイティブコンプライアンスがローンチ後に失敗しうること、発見パスがローンチ前チェックとは別であることを知る。 | **作成前に閉じるギャップ:** `validate_content_delivery` を使ってローンチ後コンプライアンス障害を明示的にステージングする番号付きラボ演習を S2 に追加。前提読書カードは存在する; 演習は存在しない。ステージングされたシナリオなしでは、評価は完全に会話に依存し、IACET Element 7(実証可能な能力証拠)を満たせない。作成 issue は、読書カードがラボ演習として提出されるのを防ぐため 3 つのことを指定しなければならない: (a) サンドボックスエージェントは、シミュレートされたフライト開始イベント後に特定のクリエイティブ ID で `validate_content_delivery` 違反を返すよう構成されなければならない; (b) 学習者は同じセッション内で `get_media_buy_artifacts` を呼び、監査アーティファクトを違反に相関させなければならない; (c) これは安定した基準 ID(例: `s2_postlaunch_sc0`)を持つ必須デモンストレーションを構成する — 再認定機構は ID が存在するときのみ発火する。 *** ## FM-3 — 支払い / 決済の照合差異 **ステータス:** まだどのモジュールにもない。スキーマは `#2391`(billing reconciliation、AdCP 3.1)で最終化中。現在の配信とアカウンタビリティ条件の表面に対して今スコープ; `#2391` が着地したとき深さが拡大する。 | Module | Depth | 評価アプローチ | | -------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **S1**(メディアバイ) | 診断 / 説明 | シナリオ: `get_media_buy_delivery` がフライト中間点で保証コミットメントより 12% 低いレポートインプレッションと、バイ作成時に受け入れられた `measurement_terms` と異なる `billing_measurement` ベンダーエントリーを示す。学習者は (a) 不一致タイプ — 配信不足 対 測定ベンダー不一致 — を識別; (b) 各の正しい修復 — ペーシング調整のための `update_media_buy` 対 `measurement_terms` 交渉に従った測定ベンダー不一致のエスカレーション — を名指す; (c) どのプロトコルアーティファクトが決済レコードか識別。 | **作成のためのフラグ:** 作成時に安定した基準 ID(例: `s1_recon_sc0`)を割り当てる。`#2391` が照合スキーマを出荷したとき、現在の基準の下で発行された S1 クレデンシャルはターゲットを絞った再認定のためフラグされなければならない — インストラクショナルデザインフレームワークの再認定機構は基準 ID が存在するときのみ発火する。これを暗黙のままにしないこと。 **`#2391` 保留の深さ TBD:** billing reconciliation スキーマが着地したら、新しい決済フィールドをカバーする 2 番目の基準(`s1_recon_sc1`)を追加。その追加前に発行されたクレデンシャルは、プロトコルトリガー再認定ポリシーに従い再認定の候補。 *** ## FM-4 — ガバナンストークン不一致 / ライフサイクル途中の認可失効 **ステータス:** 部分的にカバー。S4 は `GOVERNANCE_DENIED` 回復、15 ステップの JWS 検証、`governance_context` 相関モデルをカバー。ライフサイクル途中の失効はチェック時の拒否とは別で、まだシナリオとして名指されていない。 | Module | Depth | 評価アプローチ | | ------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **S4**(ガバナンス) | 解決 / デモンストレーション | シナリオ: `governance_context` トークンがキャンペーンローンチ時に発行された; フライト途中、ガバナンスエージェントが認可を失効させる(ブランドが市場を退出、権利付与が期限切れ)。セラーの execution フェーズの `check_governance` が失効ステータスを返す。エラー発見: 失効を受け取った後に静かに配信を続けるセラー実装。学習者は障害を識別し、正しい動作(実行停止、webhook オーケストレーター、更新されたパラメーターで `sync_plans` を再実行)を説明し、15 ステップの JWS 検証のどのステップがキー侵害 対 有効な失効を捕捉するか説明。 | | C2 | 表面 / 認識 | 学習者はガバナンストークンがライフサイクル途中で失効しうること、セラーの義務が続けることではなく停止することであることを知る。 | **作成前に閉じるギャップ:** S4 の「あなたがデモンストレーションすること」セクションは 15 ステップの JWS 検証と `governance_context` 相関モデルをカバーするが、ライフサイクル途中の失効を個別のシナリオとして名指していない。それをデモンストレーション項目として追加し、ラボ演習 7(「GOVERNANCE\_DENIED 回復」)を実行中失効バリアントで拡張。 *** ## FM-5 — ライフサイクル状態がスタック(メディアバイ、クリエイティブ、アカウント、SI セッション、カタログ) **ステータス:** 明示的な障害モードシナリオとしてまだどのモジュールにもない。S1 ラボ演習 6 は `pending_start` からの強制拒否をカバーするが、レスポンスなしのタイムアウトはカバーしない。S5 は通常の SI セッション管理をカバーするが、スタックまたは期限切れセッションはカバーしない。 | Module | Depth | 評価アプローチ | | ------------------------ | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **S1**(メディアバイ) | 解決 / デモンストレーション | シナリオ: メディアバイがセラー起動の遷移も webhook もなしに 36 時間 `pending_start` にある。学習者は (a) `get_media_buys` を使って `valid_actions` を読み状態を確認; (b) `cancel` が `pending_start` から利用可能であることを識別; (c) なぜ `pause` が `pending_start` から有効でないか説明; (d) セラーがフライト開始時に送らなければならない(MUST)webhook とそれが不在のとき何が起こるか記述。 | | **S2**(クリエイティブ — 同期スタック) | 診断 / 説明 | シナリオ: `sync_creatives` 呼び出しが `accepted` を返すが、クリエイティブ承認ステータスが決して更新されず、バイが `pending_creatives` に留まる。学習者はバイの `valid_actions` を読み、セラー側の義務を識別し、エスカレーションパスを記述。 | | **S5**(SI — セッションスタック) | 診断 / 説明 | シナリオ: SI Chat Protocol セッションが期待される TTL 後に `session_end` イベントを持たない。学習者はセッション期限切れセマンティクス、ホストが何をしなければならないか、終了されないセッションが作る状態リスクを説明。 | | D1 / D3(プラットフォームトラック) | 表面 / 認識 | 学習者は非同期プロトコル操作が停滞しうること、ポーリング対 webhook 照合パターンを説明。 | **作成前に閉じるギャップ:** * S5 の現在の「あなたがデモンストレーションすること」は通常のセッション管理のみをカバー。セッション期限切れとスタックセッション回復を明示的なデモンストレーション項目として追加。 * S1 ラボ演習 6 は、既にステージングされた強制拒否とは別に、レスポンスなしのタイムアウトケースのために拡張(またはバリアント追加)されるべき。 *** ## FM-6 — Webhook 配信障害 / リトライ **ステータス:** 障害シナリオとしてまだどのモジュールにもない。D3 の前提読書はエラー処理を参照するが、webhook 障害をカバーするラボ演習や評価次元はない。 | Module | Depth | 評価アプローチ | | ---------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **D3**(プラットフォーム) | 診断 / 説明 + 構成 | シナリオ: セラーの `active` → `paused` 遷移が失われる — webhook エンドポイントが最初の試行で 503 を返し、リトライバックオフがバイヤーの期待ウィンドウを超えた。学習者は (a) 正しいリトライセマンティクス(指数バックオフ、配信イベントの冪等性)を記述; (b) 期待される webhook が到着しないときバイヤーエージェントが何をすべきか識別(`get_media_buys` をポール); (c) webhook 駆動と poll 駆動の状態照合間のトレードオフを説明。 | | S1(副次的) | 診断 / 説明(コンシューマー視点) | 学習者は欠けている webhook をどう検出するか、いつ代わりにポールするか、これがキャンペーン状態管理にどう影響するか説明。 | | B3(パブリッシャートラック) | 表面 / 認識 | 学習者は webhook が失敗しうること、セラーがリトライを実装しなければならないことを知る。 | **作成前に閉じるギャップ:** webhook 配信障害をエンドツーエンドでウォークするシナリオを D3 のエラー処理議論内に追加(必ずしも完全な新演習でなくてよい)。D3 は現在エラー処理ドキュメントを前提読書としてリストするが、運用回復をテストするラボ演習や評価項目がない。 *** ## FM-7 — 署名付きリクエスト検証障害 **ステータス:** 障害シナリオとしてまだどのモジュールにもない。S1 はバイヤーアイデンティティ解決チェーン(署名 → JWKS → エージェントエントリー → brand.json)を概念的にカバーするが、fail-closed 実装をテストするモジュールはない。 | Module | Depth | 評価アプローチ | | ---------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **D2**(プラットフォーム) | 解決 / デモンストレーション(実装) | エラー発見: セラー実装が、`iss` が既知のブランドに一致するが JWKS フェッチがネットワークエラーで失敗し、実装が `iss` クレームを信頼することにフォールバックするリクエストを受け入れる。学習者は (a) 脆弱性 — キー検証なしにアイデンティティクレームを受け入れる — を識別; (b) 正しい動作 — fail closed、リクエストを拒否、フォールバックしない — を説明; (c) JWKS フェッチの SSRF リスクと緩和を記述。 | | S1(副次的) | 診断 / 説明(推論) | 学習者はアイデンティティチェーンの各リンクが何を防御するか、なぜ JWKS フェッチ障害がむき出しの `iss` トラストへのフォールスルーをトリガーしてはならないか説明。 | | B2(パブリッシャートラック) | 表面 / 認識 | 学習者は着信リクエストが署名検証されなければならないこと、検証障害が受け入れではなく拒否しなければならないことを知る。 | **基準 ID に関する注記:** RFC 9421 リクエスト署名への将来の変更が両モジュールで独立して再認定をトリガーするよう、作成時に D2(`d2_sig_sc0`)と S1(`s1_sig_sc0`)に別々の基準 ID を割り当てる。 **関連(下の FM-C):** エージェントディスカバリー時の `adagents.json` / `brand.json` 認可障害は、別途スコープされる密接に関連したオンボーディング障害モード。 *** ## FM-8 — TMP プロバイダー統合障害 **ステータス:** 「TMP 証明障害」から再分類。TMP 暗号証明は AdCP 3.0 で SHOULD(MUST ではない)で、仕様で「将来の拡張」とマークされている — 現在の適合性モデルは HTTPS 上の `adagents.json` 経由のパブリッシャー証明。メカニズムが安定する前に暗号証明障害を認定トピックとして教えることは、現在のプロトコル動作ではなく将来のフィーチャーの知識をクレデンシャル化することになる。今日の運用上の実際の障害モードは: (1) `adagents.json` バインディング障害 — プロバイダーがリストされていない、`seller_agent` URL 不一致、または bypass モード誤構成; (2) TMP Router プロバイダー構成障害; (3) 統合誤構成による Identity Match が結果を返さず、フリークエンシーキャップロジックが no-cap 動作にフォールバックする。ホームは S3 ではなく S1 — TMP はメディアバイ実行メカニズムで、シグナル / オーディエンストピックではない。 **延期:** 暗号証明障害シナリオは、TMP が実験的ステータスから移行したときに追加される。 | Module | Depth | 評価アプローチ | | -------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **S1**(メディアバイ) | 診断 / 説明 | シナリオ: マッチプロバイダーの `adagents.json` エントリーがセラーの `seller_agent` URL をリストしないか bypass モードが誤構成されているため、TMP Identity Match リクエストが結果を返さない。オーケストレーターのフリークエンシーキャップロジックが no-cap 動作にフォールバックし、過剰配信を招く。学習者は (a) `adagents.json` バインディングが何を証明するか — レスポンスがユーザーデータを露出せずに登録された TMP プロバイダーから来ること — を説明; (b) 正しいフォールバック動作 — 保守的: ユーザーを未知として扱い、デフォルトフリークエンシーキャップを適用 — を記述; (c) なぜ Context Match と Identity Match が構造的に分離され、この障害がクリエイティブ選択ではなくフリークエンシーキャッピングにどう影響するか説明。 | | D3(副次的) | 診断 / 説明(ルーター構成) | 学習者は TMP Router セットアップのプロバイダー構成ギャップを識別: `adagents.json` エントリー欠如または `seller_agent` URL 不一致。診断ステップと回復のための構成変更を説明。 | | S3(第三次的) | 表面 / 認識 | 学習者は TMP 統合障害がコンテキストマッチングではなくアイデンティティマッチングを劣化させること、フォールバックが保守的フリークエンシー動作であることを知る。 | **作成前に閉じるギャップ:** S1 ラボ演習 9 に障害バリアントを追加 — 同じクロスパブリッシャー抑制シナリオだが、Identity Match が `adagents.json` 誤構成のため結果を返さない。これは既存演習の拡張で、新しいものではない。 *** ## FM-9 — クロスプロトコルポリシー衝突 **ステータス:** まだどのモジュールにもない。S4 はガバナンスドメイン(campaign、property、collection、content standards、creative)の合成をカバーするが、複数ドメインからのルールが衝突し裁定されなければならないシナリオを含まない。 | Module | Depth | 評価アプローチ | | ------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **S4**(ガバナンス) | 評価 / 作成 | オープンエンド試験問題(ラボなし): キャンペーンプランが `restricted_attributes: ['zip_code']` を持つ `policy_categories: ['fair_housing']` を指定する。クリエイティブガバナンスフィーチャー評価(`get_creative_features`)が zip-code 隣接の geo シグナルを参照するクリエイティブを通過させる。geo/mobility プロバイダーのシグナルアクティベーション(`activate_signal`)が zip コードから派生したが `restricted_attributes` でラベルされていないトレードエリアセグメントを含む。どのガバナンスドメインが優先されるか、バイヤーエージェントの義務は何か、正しい実装は何をするか? 学習者はキャンペーンガバナンス(プランレベル制約)、クリエイティブガバナンス(フィーチャー評価)、シグナルガバナンス(シグナルメタデータの制限属性)にまたがって推論しなければならない。 | | S1, S2, S5 | 相互参照 | 各モジュールの「あなたがデモンストレーションすること」セクションは、クロスドメインポリシー裁定の権威的ホームとして S4 を相互参照すべき。S1/S2/S5 に独立した評価はない。 | **S4 の根拠(新しいクロスドメインセクションではなく):** S4 は既に、campaign、property、collection、content standards、creative にわたる相互作用を含むガバナンスドメインの合成をカバー。クロスプロトコルシナリオは既存の S4 スコープの拡張で、S4 + S3 前提知識で評価可能。新しいモジュールセクションは、この issue が明示的に延期する作成スコープを要求する。 **`#2391` のためのフラグ:** billing reconciliation が支払い認可にガバナンス次元を導入する場合(例: 支出を制約するプランが billing reconciliation ルールに対して検証しなければならない)、`#2391` が閉じたときその相互作用のための 2 番目の基準 ID を S4 に追加。 *** ## FM-A — アカウント支払い要求がアクティブバイをブロック **ステータス:** 元の候補リストにない。カリキュラムレビュー中に実際の高頻度運用障害として識別。アカウントステートマシンはアカウントが `payment_required` に遷移することを許可し、それは新しい支出をブロックするが飛行中のキャンペーンを終了しない。すべての認可障害を一時的として扱うバイヤーエージェントは過剰リトライする; それらを致命的として扱うエージェントは依然として変更可能なキャンペーンの管理を止める。どちらの動作も正しくない。 | Module | Depth | 評価アプローチ | | -------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **S1**(メディアバイ) | 診断 / 説明 | シナリオ: `create_media_buy` が `ACCOUNT_PAYMENT_REQUIRED` を返す。同じアカウントでの `update_media_buy`(既存バイの変更)への 2 番目の呼び出しが成功する。学習者は (a) 新規支出認可障害と既存コミットメントの変更の区別を説明; (b) バイヤーエージェントの正しい動作 — 新規バイを止め、既存を放棄せず、支払いステータスをオーケストレーターに表面化 — を記述。 | *** ## FM-B — report\_usage 価格不一致 **ステータス:** 元の候補リストにない。課金紛争トリガーとして識別: バイヤーが `report_usage` でバイ時に交渉されたレートに一致しない `pricing_option_id` をレポートし、セラーエージェントがそれを拒否する。これは、価格オプション ID がバイとレポート呼び出しの間で変わる CPA とパフォーマンス価格キャンペーンで運用上苦痛。 | Module | Depth | 評価アプローチ | | -------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **S1**(メディアバイ) | 診断 / 説明 | シナリオ: レポートの `pricing_option_id` が `create_media_buy` で受け入れられた `pricing_option_id` に一致しないため、`report_usage` 呼び出しがエラーを返す。学習者は権威的な価格オプション ID がどこに記録されるか(元の製品リスティングではなく、受け入れられたバイレスポンスに)、なぜ異なる ID をレポートすることがプロトコルエラーか、正しい回復(バイを再読、受け入れられた価格オプションを抽出、レポートを再送信)を説明。 | **スコープ注記:** `#2391` が正式な billing reconciliation メカニズムを導入する場合、この障害モードは FM-3 と統合された決済モジュールにマージするかもしれない。`#2391` が閉じたときレビューのためフラグ。 *** ## FM-C — ディスカバリー時の adagents.json / brand.json 認可障害 **ステータス:** 元の候補リストにない。新規統合の最も一般的なオンボーディング障害として識別: バイヤーエージェントが `get_adcp_capabilities` 経由で販売エージェントを発見し、OAuth 認証を試み、バイヤーの `adagents.json` が正しい `authorized_agents[]` 関係を宣言しない — または `brand.json` エントリーが署名付きリクエストで提示されたエージェントアイデンティティに一致しない — ため、セラーがトークンを拒否する。 | Module | Depth | 評価アプローチ | | ---------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **D2**(プラットフォーム) | 解決 / デモンストレーション | ラボシナリオ: バイヤーエージェントが新しく統合されたセラーに認証できない。学習者はセラーのエラーを読み、バイヤーの `adagents.json` で `authorized_agents[]` エントリーをチェックし、`brand.json` エージェントアイデンティティを検証し、解決チェーンをトレース。3 つの一般的な根本原因のどれが適用されるか正しく識別: エントリー欠如、URL 不一致、または期限切れ認可。 | | S1(副次的) | 診断 / 説明 | 学習者は `adagents.json` と `brand.json` がディスカバリーと認可フローで何をエンコードするか、なぜ不一致が拒否を生むか、バイヤー側からそれをどう診断するか説明。 | | S4(副次的) | 診断 / 説明 | 学習者は `brand.json` 認可障害がガバナンストークンチェーンとどう相互作用するか — 具体的に、なぜ有効な `authorized_agents[]` エントリーを持たないバイヤーがガバナンスコンテキストトークンを取得できないか、エスカレーションパスは何か — 説明。 | *** ## オープンアイテムトラッカー | Item | Status | ブロック解除 | | ---------------------------------------- | --------------------- | -------------------------- | | Billing reconciliation スキーマ(`#2391`) | アクティブ / 飛行中(3.1 スコープ) | FM-3 深さ拡大、FM-9 `#2391` フラグ | | トレーニングエージェントで冪等性カバレッジ確認(`#2346`、`#2367`) | 出荷済み | FM-1 作成(ラボ演習拡張のみ検証) | | ライフサイクル状態スタック issue(`#1612`–`#1616`) | オープン | FM-5 S1/S5 シナリオ詳細 | *** ## モジュール影響サマリー 以下のテーブルは、どのスペシャリストと practitioner モジュールが作成フォローアップを必要とするか示します。各 ✦ は新しいラボ演習またはデモンストレーション項目; 各 ✧ は新しい試験シナリオまたは相互参照。 | Module | 新しいラボ演習 | 新しい試験シナリオ | 追加する相互参照 | | ------ | ------------------------------------------------------------ | -------------------------------------------------------------------- | -------------------------------- | | **S1** | FM-1(冪等性状態)、FM-5(スタック `pending_start`)、FM-8(TMP プロバイダーバリアント) | FM-3(配信照合)、FM-6(webhook コンシューマー)、FM-A(payment\_required)、FM-B(価格不一致) | FM-9 → S4、FM-C(adagents.json) | | **S2** | FM-2(ローンチ後コンプライアンス — IACET に必須)、FM-5(クリエイティブ同期スタック) | — | FM-2 → S1(ステートマシン相互参照)、FM-9 → S4 | | **S3** | — | — | FM-8(TMP プロバイダー表面) | | **S4** | FM-4(実行中失効バリアント) | FM-9(クロスプロトコル裁定) | FM-C(adagents.json / ガバナンスチェーン) | | **S5** | FM-5(SI セッションスタック / 期限切れ) | — | FM-9 → S4 | | **D2** | FM-C(adagents.json/brand.json ラボ) | FM-7(署名付きリクエスト fail-closed) | — | | **D3** | FM-6(webhook 配信障害)、FM-8(TMP Router 構成) | — | — | | **B2** | — | — | FM-7(表面 / 認識) | | **B3** | — | — | FM-6(表面 / 認識) | | **C2** | — | — | FM-2、FM-4(表面 / 認識) | | **C4** | FM-1(冪等性 解決 / デモンストレーション) | — | — | | **A2** | — | — | FM-1(表面 / 認識) | # 教育設計フレームワーク Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/instructional-design AdCP 認定プログラムの教育設計: 教育方法論、適応型評価、AI 支援学習、IACET 準拠の品質プロセス。 # 教育設計フレームワーク このドキュメントは、AdCP 認定プログラムの設計、提供、維持の方法を説明します。教育方法論とプログラム品質の権威ある参照として機能します。 ### 認定要素リファレンス | IACET 要素 | セクション | | ------------ | ---------------------------------------------------------------------------- | | 2 — 学習環境 | [シミュレーション職場環境](#simulated-workplace-environment)、[学習者サポート](#learner-support) | | 3 — 教育担当者 | [教育担当者](#instructional-personnel) | | 5 — 学習成果 | [学習成果とカリキュラム構造](#learning-outcomes-and-curriculum-structure) | | 6 — コンテンツと指導 | [教育哲学](#teaching-philosophy)、[モジュール設計パターン](#module-design-patterns) | | 7 — 評価 | [コンピテンシーベース評価](#competency-based-assessment) | | 9 — 評価と改善 | [カリキュラム維持](#curriculum-maintenance)、[品質保証](#quality-assurance) |

教育哲学

認定プログラムは5つの原則に基づいています: **ソクラテス的方法。** Addie は講義ではなく会話を通じて教えます。ほとんどの回答には質問またはタスクが含まれますが、Addie は強い回答を肯定し、発展させることもあります — リズムは尋問というよりも教えることと問うことを交互に行います。学習者は答えを受け取るのではなく、問題を論理的に考えることで理解を構築します。 **マスタリーベースの進歩。** 失敗はなく、「まだ」があるだけです。学習者はすべての学習目標のマスタリーを実証するまで Addie と作業を続けます。評価は学習者には見えません — 合格するまで継続的な学習として体験されます。 **パーソナライゼーション。** Addie は各学習者の背景、役割、コミュニケーションスタイルに適応します。ランニングシューズを販売する学習者には、例はランニングシューズについてとなります。技術的な学習者には、Addie は技術的になります。コンテキストはセッション全体を通じて引き継がれます。 **アクティブラーニング。** 回答は150語以内に保ちます。1ターン1アイデア。簡潔さは参加を促します。学習者は演習、ライブサンドボックスエージェントに対するデモ、シナリオベースの推論を通じて知識を構築します。 **具体的な言語。** 抽象的な専門用語は常に具体的な動作に根ざします。「エージェントはインプレッションを推論する」ではなく「エージェントはプレースメントがキャンペーン目標に適合するかを評価し、いくら入札するかを決定する」。

学習成果とカリキュラム構造

### 3段階クレデンシャルモデル プログラムは深さが増す3つのクレデンシャルを授与します: | 段階 | クレデンシャル | モジュール | 要件 | | -- | ----------------- | ------------------------------------- | ----------------- | | 1 | AdCP basics | A1、A2、A3 | 無料 — すべての人に開放 | | 2 | AdCP practitioner | Basics + ロールトラック1つ(B、C、D) | ハンズオンビルドプロジェクトを含む | | 3 | AdCP specialist | Practitioner + スペシャリストキャップストーン(S1〜S5) | ラボ演習 + 適応型試験 | ### ブルームの分類との整合 学習目標は段階に応じてスケールします: * **Basics(A トラック):** 理解と適用 — 学習者はエージェンティック広告とは何か、AdCP がどう機能するか、エコシステム構造を説明します * **Practitioner トラック(B、C、D):** 適用と分析 — 学習者はエージェントを設定し、レスポンスを解釈し、トレードオフについて論理的に考えます * **ビルドプロジェクト(B4、C4、D4):** 創造と評価 — 学習者は動く AdCP エージェントを構築し、設計上の決定を説明します * **スペシャリストキャップストーン(S1〜S5):** 分析、評価、創造 — 学習者はハンズオンラボと適応型評価を通じてプロトコルマスタリーを実証します ### 前提条件の適用 モジュールには明示的な前提条件があります。システムは基礎を完了せずに高度なモジュールを開始することを防ぎます。ビルドプロジェクトはすべてのトラックモジュールを必要とします。スペシャリストキャップストーンは Practitioner クレデンシャルを必要とします。

教育担当者

### AI 教育アシスタント Addie は Claude(Anthropic 製)を搭載しています。教育動作はフリーフォームの AI 生成ではなく、実行時に注入された運用ルールによって管理されます。これらのルールは以下を指定します: * ソクラテス的方法論とターン構造 * 評価の公平性とスコアリングキャリブレーション * 学習者データの取り扱いとプライバシー * ツールの使用タイミングと方法(デモ、演習、チェックポイント) ### カリキュラム設計 モジュールのレッスンプラン、評価基準、スコアリングルーブリックは、広告技術と AdCP プロトコルの専門知識を持つ主題専門家が設計します。コンテンツの正確性は、プロトコル事実の単一の情報源として機能する AdCP 仕様に対して検証されます。 ### 人間による監督 プログラムリーダーシップは以下を通じて教育品質を監督します: * **スコア監視** — 管理ダッシュボードがモジュール、次元、設定バージョン、期間別にスコアを追跡します。異常(例: ある次元で一貫して低いスコア)はカリキュラムレビューを引き起こします * **フィードバックレビュー** — すべてのモジュール完了後に学習者フィードバックを収集します。プログラムリーダーシップは四半期ごとにフィードバックを確認し、ネガティブなセンチメントパターンは即座のレビューを引き起こします * **教育動作監査** — 教育方法論の変更には `CODE_VERSION` のバンプが必要で、学習者アウトカムの変更前後の比較を可能にします * **カリキュラムレビュー** — すべてのカリキュラム変更はデプロイ前にコードレビューを経ます。プロトコルの正確性は AdCP 仕様に対して検証されます * **学習者エスカレーション** — 人間の支援が必要な学習者は [certification@agenticadvertising.org](mailto:certification@agenticadvertising.org) に連絡できます。評価の異議申し立ては[苦情処理プロセス](/docs/learning/policies/complaints)を通じて処理されます

コンピテンシーベース評価

### 形成的評価 評価は指導中に継続的に行われます: * **ソクラテス的質問** — すべてのモジュールを通じて、Addie は理解を探り、誤解を修正し、回答に基づいて深さを調整します * **教育チェックポイント** — 概念グループの境界で保存され、カバーされたコンセプト、残りのコンセプト、学習者の強み、学習者のギャップ、暫定スコアを記録します * **チェックポイントの一貫性** — 最終スコアはチェックポイント中に記録された暫定スコアから20ポイント以上ジャンプできません ### 総括的評価 各モジュールは明示的なルーブリックを持つ3〜5つの評価次元を定義します: * 各次元にはウェイト、説明、スコアリングガイド(高/中/低レンジ)があります * **次元ごとの50%フロア** — 学習者はすべてのエリアで基本的なコンピテンシーを実証しなければなりません * マスタリーのための**70%加重平均閾値** * スコアキャリブレーション: 70 = コーチングありで基準を達成、85 = 独立して実証、95+ = 教えられた以上の深さ スコアは内部のみです。学習者はパーセンテージや次元の内訳を見ることはありません。体験は: マスタリーまで学び続け、その後クレデンシャルを受け取るというものです。 評価は別のテストフェーズではなく、指導的な会話を通じて継続的に行われます。これはテスト不安を減らし、即座の改善を可能にし、マスタリーベース学習原則と一致します。形成的評価(ソクラテス的質問、教育チェックポイント)と総括的評価(モジュール完了時の次元スコアリング)は同じ学習者インタラクション内で行われても、別々のプロセスとして維持されます。 早期にコンピテンシーを実証する専門家学習者に対しては、教育は圧縮されますが評価要件は同一のままです。会話トランスクリプトが監査可能な証拠として機能します: 各評価次元への理解を実証する学習者自身の言葉を示します。教育チェックポイントは、どの次元が評価されたか、暫定スコア、学習者背景コンテキストを記録します。

シミュレーション職場環境

学習者は本番環境を反映するシミュレーション職業コンテキストで作業します: * **サンドボックスエージェント** — 本番エージェントが使用するものと同じ AdCP プロトコルエンドポイントを実装します。学習者は実際のツール呼び出し、実際の JSON スキーマ、実際のレスポンス形式で練習します — 唯一の違いはサンドボックスエージェントが本番インベントリではなくテストデータを提供することです * **ビルドプロジェクト**(B4、C4、D4)— 学習者は AI コーディングアシスタントを使って動くエージェントを作成し、AdCP スキーマに対してレスポンスを検証し、実装を説明します * **スペシャリストラボ**(S1〜S5)— サンドボックスエージェントに対する実際の AdCP ツールを使ったガイド付き演習、その後に適応型質問 ### 評価の公平性 すべての学習者は、背景や経験レベルに関係なく、同じコアコンピテンシーを実証しなければなりません。 各モジュールは3〜5の**必須実証** — 会話中に学習者が実行または説明しなければなりません具体的で観察可能なもの — を定義します。これらはすべての人に同じです: * **A1**(3つの実証): ライブエージェントをクエリする、レスポンスフィールドを解釈する、プロトコルがすべてのチャンネルで機能することを説明します * **A2**(3つの実証): メディアバイを指示する、各トランザクションステップを特定する、プロトコルタスクをライフサイクルステージにマップします * **A3**(4つの実証): 特定のシナリオを処理するプロトコルドメインを特定する、エージェント発見における brand.json の役割を説明する、フォーマット/マニフェストの区別を説明する、スポンサードインテリジェンスが会話としてどう機能するかを説明します * **ビルドプロジェクト**(指定/検証/拡張フェーズにわたる9つの実証): 正しい用語を使って仕様を書く、ライブスキーマに対して検証する、新しいケイパビリティで拡張します * **スペシャリストキャップストーン**(3〜5つの実証): ライブツールを使ったプロトコル固有のマスタリータスク Addie は会話を通じて各実証を確認し、安定した基準 ID(例: `a1_ex1_sc0`)を使って教育チェックポイントに記録します。必須実証が欠けている場合、サーバーはモジュール完了を拒否します。これはサーバーサイドで適用されます — Addie はバイパスできません。 早期にコンピテンシーを実証する専門家学習者も同じ基準を確認します。教育は圧縮されることがありますが、実証は同一です。 確認された各実証には証拠の根拠が含まれます — 学習者が基準を満たすために言ったことまたは行ったことを説明する簡単なメモ。これにより監査可能なトレールが作成されます: 任意のクレデンシャルについて、どの実証が確認されたか、いつ、そしてどの証拠が各実証を支持したかを正確に追跡できます。 ### 再認定 AdCP は生きたプロトコルです。仕様が進化します — 新しいタスクが追加され、既存のタスクが変更され、チャンネルが拡張されます — 認定に必要なコンピテンシーも変わる可能性があります。 各必須実証は特定のプロトコル知識に結びついた安定した ID で追跡されます。プロトコルの変更が認定された人が知るべき内容に影響を与えると、システムは以前の基準の下で学習したクレデンシャル保持者を特定し、再認定のフラグを立てることができます。 再認定は一括ではなく、ターゲットを絞ったものです。プロトコルの更新が新しいガバナンスタスクを追加しても、メディアバイワークフローに影響を与えない場合、ガバナンスをカバーするクレデンシャルだけがフラグを立てられます。クレデンシャル保持者は Addie を通じて何が変わったか、何をレビューする必要があるかのコンテキスト付きで通知を受け取ります。 | 段階 | 有効期間 | 再認定 | | --------------------- | ---- | -------------------- | | 1 — AdCP basics | 期限なし | 基礎概念が変更されたときにフラグ | | 2 — AdCP practitioner | 2年 | 標準更新、プラスプロトコル引き起こし更新 | | 3 — AdCP specialist | 2年 | 標準更新、プラスプロトコル引き起こし更新 | ### 評価の整合性 サーバーサイドの適用がゲームを防ぎます: * すべての学習者のモジュール完了前に必須実証が確認されます * 最低エンゲージメント: モジュールで4ユーザーターン、キャップストーンで6、配置評価で3 * 最低時間: モジュールで5分、キャップストーンで10分 * 完了前に少なくとも1つの暫定スコア付き教育チェックポイントが必要です * スコア一貫性チェックはチェックポイントスコアから20ポイント以上のジャンプがある完了を拒否します * モジュール完了は Addie のツール呼び出しを通じてのみ利用可能です — 直接 REST API エンドポイントなし * 学習者は自分のスコアに影響を与えることができません * ペーストされたコンテンツ(JSON、コード、ログ)は指示ではなく検証するデータとして扱われます

モジュール設計パターン

### 標準モジュール モジュール A1〜A3、B1〜B3、C1〜C3、D1〜D3 はこのフローに従います: 1. **学習者を理解する** — 最初のターンは常に学習者について: 背景、役割、何を知っているか 2. **早期デモ**(ターン2〜3)— 理論を説明する前に実際のエージェントレスポンスを示します 3. **ソクラテス的方法で教える** — レッスンプランのすべての重要コンセプトをカバーし、ガイダンスを段階的に縮小します 4. **練習** — サンドボックスエージェントに対する演習、シナリオベースの推論 5. **会話を通じて評価** — 各学習目標のマスタリーを確認します **専門家パス。** 学習者がガイダンスや修正なしに3つ以上の概念に対して正しく詳細な回答を連続して示すことで早期に強い理解を実証する場合、ステップ3〜4は圧縮されます: Addie は専門知識を認め、ターゲットを絞ったデモンストレーション質問で残りの概念を確認し、評価に移ります。監査トレールは同じです — 会話トランスクリプト、チェックポイントスコア、次元ごとのルーブリック — しかし証拠は最初に教えられるのではなく、コンピテンシーを実証する学習者から来ます。 ### ビルドプロジェクトモジュール モジュール B4、C4、D4 は5フェーズアプローチを使用します: 1. **指定**(約5分)— 学習者は AdCP 用語を使って何を構築したいかを説明します 2. **構築**(約5分)— 学習者は AI コーディングアシスタントを使ってエージェントを作成します 3. **検証**(約10分)— 学習者はツール呼び出しを実行し、JSON レスポンスをペーストし、Addie がスキーマに対して検証します 4. **説明**(約10分)— 設計上の決定とトレードオフについての掘り下げ質問 5. **拡張**(約15分)— 学習者はケイパビリティを追加し、反復できることを実証します Addie はコーチであり、ビルダーではありません。評価は5つの次元にわたります: 仕様の品質、スキーマコンプライアンス、エラーハンドリング、設計の根拠、拡張能力。 ### スペシャリストキャップストーンモジュール モジュール S1〜S5 はハンズオンラボ作業と適応型試験を組み合わせます: 1. **ラボフェーズ** — サンドボックスエージェントに対する実際の AdCP ツールを使ったガイド付き演習 2. **チェックポイント** — 試験前に必須で、試験前の観察を記録します 3. **試験フェーズ** — 評価次元をカバーする6〜10のフォローアップ質問、難易度は回答に応じて適応します 形式にはオープンエンドの質問、多肢選択、シナリオベースの問題、「エラーを見つける」比較が含まれます。

学習者サポート

**復帰学習者。** 教育チェックポイントはセッションをまたいだ再開を可能にします。学習者が戻ってきたとき、Addie はコールドリスタートではなく、カバーされた最後のコンセプトに関する想起質問から始めます。 **エンゲージメントが低い学習者。** 学習者が繰り返し短い回答をしたり、意欲を失っているように見える場合、Addie はアプローチを切り替えます: デモを実行し、コンセプトを学習者の述べた目標に結びつけ、または抽象性を認めて具体的にします。 **過剰資格の学習者。** 教育と評価は異なる目的に役立ちます。教育は学習者のためで、評価はクレデンシャルのためです。学習者がガイダンスなしに3つ以上の概念に対して連続して強い理解を実証する場合、Addie は*教育*を圧縮しますが*評価*は圧縮しません。専門家パスは指導をデモンストレーションに置き換えます: 「X を教え、X について質問する」の代わりに、Addie は「X への理解を示してください」と直接尋ねます。これにより教育を受ける前にコンピテンシーを実証する学習者の言葉から強い監査証拠が生成され、同時に学習者の時間を尊重します。パスに関係なく、同じ評価次元、スコアリングルーブリック、最低エンゲージメント要件が適用されます。 **配置評価。** 既存の知識を実証する学習者は、コンテンツを繰り返さずにモジュールをテストアウトできます(ビルドプロジェクトとスペシャリストキャップストーンを除く)、前提条件を満たします。 **ペーシング。** Addie は45分以上または2つ以上の連続モジュール後に休憩を提案します。モジュールの移行は、完了したモジュールを次のモジュールに結びつける圧縮されたウォームアップでパーソナライゼーションコンテキストを引き継ぎます。

クレデンシャル発行

学習者がクレデンシャル段階に必要なすべてのモジュールを完了すると、システムは自動的に: 1. すべての前提条件とモジュール完了を確認します 2. クレデンシャルを付与し、日付を記録します 3. 固有の確認 URL と QR コード付きで Certifier を通じてデジタルバッジを発行します 4. 学習者に通知し、共有オプション(LinkedIn、公開プロフィール)を提供します **クレデンシャルの有効性:** Basics クレデンシャルには有効期限がありません。Practitioner と Specialist クレデンシャルは2年間有効です。クレデンシャルは発行時のプロトコルバージョンを参照します。プロトコルの変更が既存のクレデンシャルにどう影響するかについては、[再認定](#recertification)を参照してください。 **学習者アイデンティティ:** 学習者は AgenticAdvertising.org アカウント(WorkOS)を通じて認証します。クレデンシャルは認証済みアカウントに紐づけられます。プログラムは現在、評価に対して監視付きのアイデンティティ確認を必要としていません。

カリキュラム維持

### プロトコル変更トリガー プロトコルバージョンがリリースされたとき(マイナーまたはメジャー): 1. `MODULE_RESOURCES` URL を確認します — ドキュメントページが移動または名前変更されていないか? 2. 教育ノートを確認します — 重要コンセプトが変更された動作を参照していないか? 3. 現在のスキーマに対してドキュメントの例を検証します 4. タスクが追加、削除、または名前変更された場合、影響を受けるモジュールのレッスンプランを更新します ### 学習者フィードバックループ * すべてのモジュール後に Addie を通じて完了後フィードバックを収集します * フィードバックにはフリーテキストとセンチメント分類(ポジティブ、混合、ネガティブ)が含まれます * ネガティブフィードバックのパターンは影響を受けるモジュールのカリキュラムレビューを引き起こします ### プログラム評価(四半期ごと) プログラムリーダーシップはプログラムが目標を達成しているかを評価するための四半期評価を実施します: **確認するデータ:** 1. すべてのモジュールにわたる学習者フィードバックパターン(センチメントトレンド、繰り返しの混乱点) 2. モジュールと次元別のスコア分布 — 一貫して低いスコアは教育ギャップを示します 3. 学習者がよく詰まるコンセプトのチェックポイントデータ 4. 完了率と完了までの時間のトレンド 5. 段階別のクレデンシャル授与率 **プロセス:** * プログラムリーダーシップがデータを確認し、所見を文書化します * 所見は具体的なカリキュラム変更に変換されます(教育ノートの更新、レッスンプランの改訂、スコアリングガイドの調整) * 変更は標準コードレビュープロセスを通じて実装され、`CODE_VERSION` で追跡されます * 変更の結果は翌四半期のレビューで評価されます ### バージョン追跡 * 教育動作バージョンは `CODE_VERSION`(形式: YYYY.MM.N)で追跡されます * プロトコルの変更はチェンジセットワークフローで追跡されます * スコア分析を設定バージョンをまたいで比較し、教育改善を測定できます

品質保証

### サーバーサイド適用 品質ゲートは AI の判断だけでなく、アプリケーションによって適用されます: * モジュール完了を許可する前に最低ターン数と時間をサーバーサイドで確認します * ユーザーターンのカウントはクライアント報告ではなくサーバーサイドです * スコア一貫性チェックはチェックポイントスコアから20ポイント以上のジャンプがある完了を拒否します * モジュールステータス検証は進行中でないモジュールの完了を防ぎます * クレデンシャル授与チェックはすべてのモジュール完了後に自動的に実行されます ### フィードバックと評価 * すべてのモジュール完了後に構造化フィードバックを収集します * モジュールと期間をまたいだトレンド検出のためのセンチメント分析 * 管理者分析: 完了率、次元別スコア分布、完了までの時間 * チームクレデンシャル追跡のための組織レベルレポーティング ### 継続的改善 * 教育方法論定数はこのフレームワーク文書を権威ある情報源として参照します * 教育動作へのコード変更は変更前後の比較のために `CODE_VERSION` をバンプします * 仮定ではなく学習者データに基づく四半期カリキュラムレビュー

認定整合

このフレームワークは以下の要件を満たすよう設計されています: * **ANSI/IACET 1-2018 継続教育・訓練標準** — 要素 2(学習環境)、3(教育担当者)、5(学習成果)、6(コンテンツと指導)、7(評価)、9(評価と改善) * **ASTM E3416-24 コンピテンシーベース職場学習プログラムの標準実践** — コンピテンシー整合、形成的・総括的評価、シミュレーション職場設定、クレデンシャル発行 * **CPD Standards Office** の継続的専門能力開発の認定要件 このフレームワークを支援する組織ポリシーは[ポリシーセクション](/docs/learning/policies/nondiscrimination)に文書化されています。 # AdCP 認定プログラム Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/overview AdCP 認定プログラム: AI ティーチングアシスタント Addie が教える3ティア(Basics、Practitioner、Specialist)。基礎モジュールは無料、メンバー限定の上級トラック。 # AdCP 認定プログラム AI が広告を再構築しています。問題はあなたがその変化を主導するか、追随するかです。 エージェンティック広告はすでにここにあります — メディアバイを交渉し、リアルタイムでクリエイティブを最適化し、複数チャンネルにわたってキャンペーンを自律的に管理する AI エージェント。これらのシステムの仕組みを理解した人が次に何が来るかを形作ります。それ以外の人は後れを取ることになります。 AdCP 認定プログラムは、インタラクティブな AI ガイド付きモジュールを通じてそこまで導きます。講義なし。選択問題のテストなし。実際にやることで学びます — 実際のシナリオを議論し、ライブのサンドボックスエージェントで作業し、自分の広告エージェントを構築します。 Addie を開いて「認定プログラムを始めたい」と言ってください。アカウント不要 — Basics トラックは無料です。 ## Addie で学ぶ感覚 認定は会話の中で行われます。AI ティーチングアシスタントの Addie があなたの経験レベルに適応します — 質問し、概念を説明し、実際のシナリオを解説します。忍耐強い博識な同僚がいるようなものです。 Addie は理解度も同じ方法で評価します: 会話を通じて。概念を明確に説明できるか?実際のシナリオに適用できるか?プロトコルツールを正しく使えるか? このプログラムは広告業界のあらゆる人向けに設計されています — プログラマティックトレーダー、メディアプランナー、ブランドマネージャー、代理店エグゼクティブ、エンジニア。プログラミング経験は不要です。3つのモジュール、合計約50分で、最初のクレデンシャルを取得できます。ほとんどの学習者は数回のランチタイムで Basics トラックを完了します。 ## 全員が構築します エンジニアでなくても広告エージェントを構築できます。 Practitioner レベルでは、Claude Code や Cursor などの AI コーディングアシスタントを使って、本物の動くエージェント — バイヤーエージェント、セラーエージェント、またはプラットフォームエージェント — を構築します。これはバイブコーディング: 構築したいものを平易な言語で説明し、AI コーディングアシスタントがコードを書きます。本物の AdCP スキーマに対して検証し、Addie と設計を繰り返し改善します。 コーディングの部分は速いです。本物の評価は何を構築する必要があるかを明確に示せるか、どのように機能するかを論理的に考えられるか、繰り返しを通じて改善できるかです。これらはすべての広告プロフェッショナルが持つべきスキルです。 **Practitioner トラックの終わりには、自分の動く広告エージェントを構築することになります。** ## 3ティアのクレデンシャルモデル **誰でも無料。** 3つの基礎モジュールを完了して、エージェンティック広告とは何か、AdCP がどのように機能するか、プロトコルの全体像を理解していることを証明します。 モジュール: [A1](/docs/learning/foundations/a1-agentic-advertising)、[A2](/docs/learning/foundations/a2-protocol-architecture)、[A3](/docs/learning/foundations/a3-ecosystem-governance) **Basics プラスロールトラック — 自分のエージェントを構築して終了。** Basics モジュールと1つのロールトラック(publisher、buyer、または platform)を完了します。トラックは自分の広告エージェントを作成するビルドプロジェクトで締めくくられます。プログラミング経験不要。 モジュール: A1〜A3 + 1トラック(B1〜B4、C1〜C4、または D1〜D4) **プロトコルの習熟。** 特定のプロトコル領域を深く掘り下げます: メディアバイ、クリエイティブ、シグナル、ガバナンス、またはスポンサードインテリジェンス。ハンズオンラボと適応型試験を組み合わせます。 必要: Practitioner + スペシャリストモジュール ## 構築しない意思決定者向け 上記の階層はすべてハンズオンの作業 — ライブエージェントへのクエリ、エージェントの構築 — を伴います。それは実践者にとって正しい水準であり、評価、ブリーフ、決定はするが構築は委任するリーダーにとっては誤った水準です。 **[AdCP for Decision-Makers](/docs/learning/decision-makers/overview)** 資格情報は彼らのためです: ブランドメディアリーダー、エージェンシー幹部、SMB オーナー。3 つの短いモジュール、無料、完全に戦略的推論を通じて評価 — コードなし、ライブエージェントクエリなし。あなたはエージェントではなく、意思決定アーティファクト(ビジネスケース、エージェンシーブリーフ、または採用計画)を持って卒業します。これは単独で成立します — 前提条件なし — が、構築ではなく戦略的流暢さを認定します: Basics や Practitioner の代替、またはより簡単なルートではありません。 スペシャリスト資格情報はプロトコルのドメインと整合します。各ドメイン内で、AdCP は**専門分野(specialisms)** — エージェントがサポートする特定のフロー、`get_adcp_capabilities` で宣言 — を定義します。エージェントは、そのコンプライアンスストーリーボードに合格することで各専門分野を実証します。スペシャリストトラックは、これらのクレームについて推論し、エージェントをそれらに対して検証することを教えます。完全なタクソノミーについては [Compliance Catalog](/docs/building/verification/compliance-catalog) を参照。 ## 仕組み 各モジュールは同じパターンに従います: 1. **読む** — このページの読み物リストを確認してコンテキストを構築します 2. **学ぶ** — Addie とモジュールを開始してソクラテス式のインタラクティブな教育を受けます 3. **練習する** — ライブのサンドボックスエージェントに対して演習を行います 4. **実証する** — すべての学習者が実証しなければなりません3〜5つの特定のことができることを示します すべてのモジュールには必須の実証セットがあります — 経験に関わらず全員同じです。ベテランのアドテクの専門家と初心者の両方が同じコアコンピテンシーを検証します。Addie があなたのレベルに合わせて教育を適応させますが、基準は一貫しています。詳細は[教え方](/docs/learning/instructional-design#assessment-fairness)を参照してください。 ## 学習パス ### Basics(無料) 全員ここから始めます。3つのモジュールすべて無料 — メンバーシップ不要。 | モジュール | トピック | 所要時間 | 無料? | | --------------------------------------------------------- | ------------- | ---- | --- | | [A1](/docs/learning/foundations/a1-agentic-advertising) | AdCP の理由 | 15分 | Yes | | [A2](/docs/learning/foundations/a2-protocol-architecture) | 最初のメディアバイ | 20分 | Yes | | [A3](/docs/learning/foundations/a3-ecosystem-governance) | AdCP のランドスケープ | 15分 | Yes | ### 意思決定者(無料) 決定とブリーフはするが構築しないブランドリーダー、エージェンシー幹部、SMB オーナー向け。単独の資格情報 — 前提条件なし。コードやライブエージェントクエリではなく、推論を通じて評価されます。 | Track | For | Modules | Duration | | ---------------------------------------------------------- | --------------------------- | ------- | -------- | | [Decision-makers](/docs/learning/decision-makers/overview) | ブランドリーダー、エージェンシー幹部、SMB オーナー | L1–L3 | 約45分 | ### ロールトラック(パスを選ぶ) Basics の後、役割に合ったトラックを選びます。各トラックはビルドプロジェクトで締めくくられる4つのモジュールです。 | トラック | 対象 | モジュール | 所要時間 | | ------------------------------------------------------- | --------------------------- | ----- | ----- | | [パブリッシャー / セラー](/docs/learning/tracks/publisher) | パブリッシャー、SSP、サプライサイドプラットフォーム | B1〜B4 | 約103分 | | [バイヤー / ブランド](/docs/learning/tracks/buyer) | ブランド、代理店、DSP | C1〜C4 | 約92分 | | [プラットフォーム / インターメディアリー](/docs/learning/tracks/platform) | アドテクプラットフォーム、取引所、データ会社 | D1〜D4 | 約89分 | ### スペシャリストモジュール(プロトコルの習熟) 各スペシャリストモジュールは、特定のプロトコル領域のハンズオンラボと適応型試験を組み合わせます。Practitioner クレデンシャルが必要です。 | スペシャリスト | プロトコル領域 | 所要時間 | | ---------------------------------------------------------------------- | --------------------------------------- | ---- | | [S1: メディアバイ](/docs/learning/specialist/media-buy) | トランザクション、価格設定、マルチエージェントオーケストレーション | 45分 | | [S2: クリエイティブ](/docs/learning/specialist/creative) | アセットワークフロー、フォーマットコンプライアンス、クロスプラットフォーム適応 | 45分 | | [S3: シグナル](/docs/learning/specialist/signals) | シグナル探索、活性化、プライバシー、最適化ループ | 45分 | | [S4: ガバナンス](/docs/learning/specialist/governance) | ブランドセーフティ、サプライチェーンコンプライアンス、コンテンツ基準 | 45分 | | [S5: スポンサードインテリジェンス](/docs/learning/specialist/sponsored-intelligence) | 会話型ブランド体験、セッションライフサイクル | 45分 | ## 始める Addie を開いて AdCP 認定プログラムの開始をリクエストしてください。Addie がモジュール A1 を案内してくれます — アカウント不要。 # メジャメント分類 Source: https://adcp-docs-ja.pier1.co.jp/docs/measurement/taxonomy AdCP メジャメントの 3 層モデル — メトリック(配信)、検証(品質)、アトリビューション(結果) — と各層が真実の源泉、プロトコルの居場所、変化速度でどう異なるか。 # メジャメント分類 メジャメントは 1 つではなく 3 つのものです。それらを 1 つのバケットとして扱うことが、メジャメント RFC(SSAI、アイデンティティ喪失、AI コンテンツプロベナンス、クリーンルーム)のほとんどの混乱と、配信レスポンスのほとんどのスキーマ肥大化の源です。AdCP はそれらを意図的に分離します。 3 つの層 — **メトリック**、**検証**、**アトリビューション** — は異なる質問に答え、異なる当事者によって証明され、プロトコルの異なる場所に存在し、異なる速度で進化します。 ## 3 つの層 | Layer | Question | Source of truth | Protocol home | Rate of change | | ------------- | ------------ | --------------- | --------------------------------------------- | -------------- | | **メトリック** | 起こったか? | セラー | 配信レポート | 遅い(10 年スケール) | | **検証** | 適切にカウントされたか? | サードパーティ | パフォーマンス標準 + ケイパビリティ + マニフェストトラッカー + ベンダー証明配信値 | 中(環境駆動) | | **アトリビューション** | 結果を引き起こしたか? | バイヤー | ハンドオフフック(イベントソース、ログレベルシグナル) | 速い(モデル駆動) | ### 1. メトリック — 「起こったか?」 配信事実: インプレッション、完了、quartile、クリック、支出、リーチ、フリークエンシー。リーチとフリークエンシーは明示的な `reach_window` 宣言(`cumulative` / `period` / `rolling`)を運び、バイヤーが値を行全体で合計できるか知れます。 標準とベンダーメトリックフローの両方にわたる完全なケイパビリティ → コミットメント → 最適化 → 配信の絵については、[メトリックライフサイクル](/docs/media-buy/media-buys/optimization-reporting#metric-lifecycle) を参照。同じ `(vendor, metric_id)` キーが、ベンダー証明メトリックのすべての表面 — ディスカバリー、最適化ケイパビリティ、レポートケイパビリティ、パッケージコミットメント、最適化目標、パフォーマンス標準、配信値 — を通じて流れます。 セラーが真実の源泉です — セラーが広告をサーブしイベントをカウントします。業界カウント規約(MRC、IAB)が何がインプレッションとして資格を持つか、何が動画ビューを完了するか、どう重複排除するかを定義します。AdCP はセラーがこれらのカウントをどう露出するかを標準化します。それらが何をカウントするかを再定義しません。 1 つのニュアンス: `available-metric.json` の一部のメトリック(ROAS、CPA、コンバージョン、conversion\_value、units\_sold)はセラーレポートだが **アトリビューション由来** です — セラーがバイヤー供給のイベントソースにアトリビューションモデルを実行し結果をレポートします。それらは、すべての DSP とリテールメディアプラットフォームが今日それらを露出する場所だから配信レポートに存在しますが、基盤となる真実のイベントはバイヤー証明です。これらを *「配信を通じて表示されたアトリビューション」* として読み、純粋な配信事実として読まないでください。セラーの数値はバイヤーのイベントに対するセラーのアトリビューションモデルを反映します。バイヤー側グラウンドトゥルースに対する照合は依然としてアトリビューション境界に属します。 AdCP では、メトリックは配信レポートを通じて流れます: * [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) — 現在の配信状態 * [`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback) — バイヤー側観測パフォーマンス * [最適化とレポート](/docs/media-buy/media-buys/optimization-reporting) — レポートが最適化目標にどう接続するか メトリックはゆっくり進化します。定義は業界団体によって統制されます。新しいメトリック(ビューアブルインプレッション、アテンション秒)は 10 年スケールで現れます。ここでのスキーマ圧力は低いです。 ### 2. 検証 — 「適切にカウントされたか?」 品質証明: ビューアビリティ、無効トラフィック(IVT)、ブランドセーフティ、geo 精度、コンテキスト適合性、広告コンテンツプロベナンス。 検証の全ポイントは、それがセラーの言葉 *でない* ことです。バイヤーは、独立した当事者がインプレッションが品質しきい値を満たしたことを確認できるよう、まさにサードパーティ測定ベンダー(Moat、IAS、DoubleVerify)と契約します。検証は配信環境を生き延びる実行パスを要求します — 歴史的にはクライアント側で動く OMID と VPAID。SSAI では、サーバー側回避策としての [SIVA](https://iabtechlab.com/standards/siva/)。 AdCP では、検証はベンダーの `brand.json` 測定エージェントレコードにアンカーされ、バイライフサイクル全体で構造化表面を持ちます: * **ディスカバリー。** バイヤーは [`get_products`](/docs/media-buy/task-reference/get_products) で `required_performance_standards`(「DoubleVerify による 70% MRC ビューアビリティ」)、`required_metrics`、`required_vendor_metrics` で製品をフィルターします。セラーは `reporting_capabilities.available_metrics`、`vendor_metrics`、`committed_metrics_supported` ケイパビリティフラグ経由でサポートを宣言します。 * **コミットメント。** [`performance-standard.json`](/docs/media-buy/task-reference/create_media_buy) が `metric` + `threshold` + `standard`(例: MRC 対 GroupM ビューアビリティ)+ `vendor` をバイコントラクトにバインドします。ベンダーはベンダーの `brand.json` `agents[type='measurement']` レコードに解決する `BrandRef` です。パフォーマンス標準がコミットされると、*クリエイティブはそのベンダーからの `tracker_script` または `tracker_pixel` アセットを含まなければなりません(MUST)* — プロトコルがパスを強制します。`committed_metrics` は `create_media_buy` でパッケージのレポートコントラクトをスナップショットし(閉じた `available-metric.json` enum からの標準メトリックと `BrandRef` にアンカーされたベンダー定義メトリックの両方を運ぶ統一された判別された配列、各エントリーが `committed_at` でタイムスタンプ付き)、バイの寿命の間追加のみです。 * **実行。** [クリエイティブマニフェスト](/docs/creative/creative-manifests) は、サードパーティベンダーがイベントを記録するよう配信時に発火するトラッカーとマクロ(`vast_tracker`、`daast_tracker`、ユニバーサルマクロ)を運びます。それらがクライアント側かサーバー側で発火するかはセラーの実装詳細です。バイヤーのコントラクトはパスではなくメトリックにあります。 * **レポート。** 閉じた `available-metric.json` enum に卒業した標準検証メトリック(例: `viewability`)は、[`delivery-metrics.json`](/docs/media-buy/task-reference/get_media_buy_delivery) の専用配信スカラーを通じて流れます。非卒業のベンダー定義メトリックは、カバレッジ分母としての `measurable_impressions` とともに `vendor_metric_values` を通じて流れます。ベンダーアトリビューションは、配信行自体ではなく、`committed_metrics` と `performance_standards.vendor` 経由でコントラクトレベルでアンカーされます。`missing_metrics` は、セラーがコミットされたメトリックを配信しなかったときアカウンタビリティギャップを表示します — `committed_metrics` が存在するとき、照合は正確でタイムスタンプ対応です。不在のとき、`missing_metrics` はコミットメントタイムスタンプフィルターなしで製品のライブ `available_metrics` にフォールバックしギャップを過小レポートします。バイヤーは `committed_metrics` の不在を *「監査グレードのコントラクトなし」* として扱うべきで(SHOULD)、*「クリーンな配信」* ではありません。 ベンダーの完全な *ダッシュボード* はベンダー(Moat、IAS、DV、HUMAN など)に存在しますが、証明された数値は AdCP 配信レポートを通じて戻ります。測定エージェントはファーストクラスアイデンティティです — `brand.json` `agents[type='measurement']`(BrandRef アンカー)経由で発見可能で、メトリックカタログ(`metric_id`、`standard_reference`、`accreditations[]`、`unit`、`methodology_url`、`methodology_version`)はエージェントの [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) レスポンスの `measurement` ブロックの下でサーブされます。`brand.json` がディスカバリーポイント。エージェントがカタログをサーブします。 #### 卒業した検証メトリック 検証メトリックは標準化の異なる速度で進化し、プロトコルはそのグラデーションのどこに位置するかに基づいて異なるレベルの構造サポートを与えます: * **Tier 1 — 卒業。** 業界公開、MRC または同等認定。複数の競合する標準が存在しうる。閉じた `available-metric.json` enum の専用エントリー、`delivery-metrics.json` の専用構造化ブロック、(標準が相互に互換でないとき)曖昧性解消のための `committed_metrics` の `qualifier` スロットを得る。**ビューアビリティ** は今日の正準 Tier 1 メトリック — MRC と GroupM は実質的に異なるしきい値を定義し `qualifier.viewability_standard` 経由のスキーマ強制曖昧性解消を要求。 * **Tier 2 — ベンダー拡張。** 業界公開標準のないベンダー定義メトリック。セラーは `reporting_capabilities.vendor_metrics` 経由でレポートサポートを、`vendor_metric_optimization.supported_metrics` 経由で最適化サポートを宣言。値は `vendor_metric_values` 経由で流れる。目標は `kind: "vendor_metric"` で `optimization_goals` 経由でベンダーにバインド。アイデンティティはベンダーの `BrandRef` にアンカーされカタログはベンダーの測定エージェントケイパビリティに存在。**アテンションスコア、パネルベースブランドリフト、パネルデモグラフィック、インプレッションごと排出量** が今日ここに位置。 * **Tier 3 — 主張。** 構造化ベンダーアイデンティティまたは標準担持者証明のない製品上のフリーフォームクレーム。BrandRef パターンに先行し、漸増的に上方に再構築されている。 メトリックは、業界標準団体が測定仕様を公開するとき Tier 2 から Tier 1 に卒業します — ベンダー数しきい値や非公式収束ではなく、標準団体の公開にアンカーされます。Tier 1 をサポートするパターン(`qualifier` スロット、専用配信スカラー、パフォーマンス標準バインディング)は再利用可能なテンプレートです: ビューアビリティは最初のインスタンスで、ビューアビリティ固有のカスタム形状ではありません。 #### クローズドループトポロジー: セラーを測定エージェントとして 卒業したメトリックフレーミングは、デフォルト測定トポロジーが *セラーがサーブ、サードパーティが検証* — DV/IAS がビューアビリティを証明しパブリッシャーの広告サーバーがインプレッションをカウント — であると仮定します。それは依然として従来の CTV、ビデオ、ディスプレイの支配的パターンです。しかし 2 つのチャネルクラスは異なるデフォルトを持ちます: * **リテールメディアクローズドループ**: Walmart Connect、Kroger Precision、Amazon DSP、Criteo Retail Media。リテーラーは自身の表面で広告をサーブし、自身の表面でクリックを観測し、自身の表面でコンバージョン(ロイヤルティカード、ログイン、POS)を観測します。セラーは測定ベンダーでもあります。トラストモデルはサードパーティ独立性ではなくリテーラーのファーストパーティデータアセットに基づきます。 * **AI ネイティブチャネル**: ChatGPT と他のエージェンティック会話表面は、広告を会話ストリームに直接注入します(サーバー側)。クリックナビゲーションはセラーが制御するアプリ内 webview で起こります。コンバージョンアトリビューションは、マーチャントのプロパティにデプロイされたセラー提供 SDK(OpenAI には `oaiq.min.js`)を通じて戻ります。セラーは再び測定ベンダーでもあります。 これらはサードパーティ検証の劣化ケースではありません — プロトコルが既存のプリミティブ経由でクリーンにサポートする構造的に異なるトポロジーです: * **セラーがベンダーのときベンダーアイデンティティは暗黙**: BrandRef が `delivery_measurement.vendors` でセラーにアンカー。ベンダースコープの `committed_metrics` エントリーがセラーの測定エージェントケイパビリティを指す。`performance_standards.vendor`(存在するとき)がセラーを名指す。追加スキーマ不要。 * **結果メトリックは同じ語彙を通じて流れる**: `conversion_value` + `qualifier.attribution_methodology: "deterministic_purchase"` + `qualifier.attribution_window: { interval: 30, unit: "days" }` が、ChatGPT のアトリビューショントークンベースのコンバージョンアトリビューションと Walmart Connect の `attributedSalesIn14Days` をクリーンに表現。リテールメディア固有スキーマなし、AI ネイティブ固有スキーマなし。 * **`(metric_id, qualifier)` 行形状が両方を処理**: コントラクト / diff / 配信 / フィードバックが、ベンダーがサードパーティかセラー-as-ベンダーかにかかわらず同じ方法で照合。 今日欠けているもの: セラーが、イベントをセラーに戻すためバイヤーがプロパティにデプロイする **マーチャント側 SDK**(OAIQ パターン)を宣言する構造化された方法。別の RFC([#3889](https://github.com/adcontextprotocol/adcp/issues/3889))として追跡 — 既存のプリミティブは *何が測定されるか* を表現。SDK 配布 / 統合 / サプライチェーンストーリーがギャップ。 検証は中程度のペースで進化します。環境シフト — CTV、SSAI、ウォールドガーデン、クッキーレス、AI 生成コンテンツ、AI ネイティブチャネル — が新しいシグナル喪失問題とそれらを回復する新しいプロトコルを駆動します。検証ケイパビリティへのスキーマ圧力を 1〜3 年ごとに期待してください。 ### 3. アトリビューション — 「結果を引き起こしたか?」 配信と結果間のバイヤー側結合: コンバージョン、リフト、マルチタッチアトリビューション、メディアミックスモデリング、インクリメンタリティ。 セラーはコンバージョンイベントを知りません。バイヤー(またはバイヤーの測定パートナー)が結果データを保持しそれを配信に結合します。AdCP の役割は結合を可能にすることです — ログレベルシグナル、アイデンティティフック、クリーンルームへのハンドオフパターンを露出 — モデルを実行することではありません。 AdCP では、アトリビューションは *境界* に現れます: * [`sync_event_sources`](/docs/media-buy/task-reference/sync_event_sources) — バイヤーがコンバージョンイベントソースをセラープラットフォームにプッシュし、プラットフォームが実結果に向けて最適化できる * [`log_event`](/docs/media-buy/task-reference/log_event) — バイヤー証明イベント配信 * [コンバージョントラッキング](/docs/media-buy/conversion-tracking/) — 配信を結果に接続するパターン * [Trusted Match](/docs/trusted-match/) — PII をリークせずに結合を可能にするアイデンティティ解決 モデル自体(クリーンルーム、MMM、因果推論、エージェンティック結果アトリビューション)は完全にプロトコルの外側に存在します。 アトリビューションは最も速く進化します。クリーンルームパターン、MMM 復活、因果 AI、コマースメディアアトリビューション、エージェンティック結果モデルはすべて、四半期から年のスケールでアトリビューション層をシフトします。アトリビューションが配信スキーマ内に存在したら、それはサイクルごとにスキーマ破損を強いるでしょう。 ## なぜ分離が重要か ワーキンググループのほとんどのメジャメント議論は、層が名指しされるとより速く解決します: * **SSAI**([#3759](https://github.com/adcontextprotocol/adcp/issues/3759))は *検証* 問題です。どのメトリックがレポートされるかを変えません。どの検証パスが有効か、生き延びるシグナルがどれだけリッチかを変えます。修正は配信レポートではなくケイパビリティ + クリエイティブマニフェストトラッカーに存在します。 * **アイデンティティ喪失**(クッキーレス、IDFA 非推奨、ウォールドガーデンシグナル崩壊)はメトリックではなく *アトリビューション* に現れます。セラーは依然としてサーブしインプレッションをカウントします。バイヤーの結果への結合が劣化します。修正は配信ペイロードではなくアトリビューション境界(クリーンルーム、[Trusted Match](/docs/trusted-match/))に属します。 * **AI コンテンツプロベナンス** は *検証* の関心事(この広告はブランドが承認したものか?)で、アトリビューションの関心事ではありません。他の検証ケイパビリティと並ぶべきで — [プロベナンス検証](/docs/governance/creative/provenance-verification) を参照 — 結果レポートにボルト留めされません。 * **結果ベースの最適化目標**(CPA、ROAS、カスタムイベント)は、最適化入力として表示される *アトリビューション* の関心事です。バイヤーがプラットフォームが何に向けて最適化すべきかを引き渡すイベントソース境界に属します。 提案がアトリビューション概念(リフト、ROAS、MMM 入力)を配信レポートに、または検証概念(OMID、SIVA)をアトリビューションフックに入れるとき、押し返してください。層の不一致はほぼ常に、提案が壊れるまでエッジケースを蓄積することを意味します。 ## 実用的経験則 メジャメントフィールドがどこに属するかを評価するとき、**誰が真実の源泉か?** と尋ねます。 * **セラー** がそれをカウント → メトリック → 配信レポート * **サードパーティ** がそれを証明 → 検証 → ケイパビリティ + クリエイティブマニフェスト * **バイヤー** が結果を所有 → アトリビューション → イベントソース / ログレベルハンドオフ この単一の質問がほとんどの配置議論を解決します。2 つの層が同じフィールドを主張するように見える場合、フィールドはおそらく 1 つの名前をまとった 2 つのフィールドです — それを分割してください。 ## 原子単位: `(metric_id, qualifier)` プロトコルのメジャメントプリミティブは、同じ方法でインデックスされ照合される 1 つのタプルに還元されます: * **`committed_metrics` 行**: `{ scope, metric_id, qualifier, committed_at }` — セラーが投入に同意したもの([#3576](https://github.com/adcontextprotocol/adcp/pull/3576)、出荷済み) * **`missing_metrics` 行**: `{ scope, metric_id, qualifier }` — 現れなかったもの([#3576](https://github.com/adcontextprotocol/adcp/pull/3576)、出荷済み) * **`metric_aggregates` 行**: `{ metric_id, qualifier, value, …components }` — 実際に配信されたもの、qualifier で分割([#3848](https://github.com/adcontextprotocol/adcp/issues/3848)、提案) 照合は `(metric_id, qualifier)` の結合に崩れます。各 `committed_metrics` 行について、一致する `metric_aggregates` 行を見つける。一致がないものは `missing_metrics` として表示。カスタムのメトリックごと照合ロジックなし、コントラクトと配信間のトラバーサル非対称なし。 `qualifier` 語彙は表面によって異なります: コントラクトは閉じている(`additionalProperties: false`、今日 `viewability_standard` のみを運ぶ)。配信は意図的な **スーパーセット**(例: `tracker_firing` は、バイヤーがコミットしないがセラーが配信後に露出できる透明性開示として存在)。非対称は名指しされ、偶然ではありません — バイヤーは語彙を共有するものにコミットし、セラーは配信されたもののパスレベル透明性を露出します。 将来の qualifier(`completion_threshold`、標準化するならアテンション方法論)が構造サポートを必要とするとき、既存のスロットに差し込みます。並行 `*_by_*` フィールドなし、新しい集計表面なし、スキーマ破損なし。 ## Signals と Governance との境界 メジャメントは AdCP の唯一のサードパーティ証明表面ではありません。[Signals](/docs/signals/overview) と [Governance](/docs/governance/overview) もサードパーティを関与させ、証明されたアーティファクトを生成し、コアメディアバイプリミティブより速く進化します。境界は実際ですが、プロトコルはベンダーとライフサイクルで重なります — Signals 自身の key-concepts ページはシグナルが「ターゲティングまたはメジャメントに」使われると記し、その曖昧性が問題の境界です。 最も明確な分離はライフサイクルモーメントと尋ねられる質問による: | Lifecycle moment | Question | Protocol home | | ---------------- | ----------------- | ----------------------------- | | 決定前 | 何をすべきか? | Signals | | プラン時 | これをすることを許可されているか? | Governance(ポリシーレジストリ、プランチェック) | | 配信 | 起こったか? カウントされたか? | Measurement(メトリック、検証) | | 配信後 | どの結果を引き起こしたか? | Measurement(アトリビューション) | | 継続 | 監査証跡は無傷か? | Governance(監査証跡) | 同じベンダーがしばしば複数のレーンでプレイします。例えば DoubleVerify は、事前入札ブランドセーフティ *シグナル*、配信後 *検証* 証明、*ガバナンス* ポリシー強制が消費するコンテンツ分類フィードを販売します。ベンダーは 1 つのエンティティです。プロトコル表面は、タイミング、真実の源泉、消費パターンが異なるため 3 つです。 ### 線が鮮明な場所 * **シグナルは予測的、メジャメントは記述的。** 事前入札ビューアビリティスコアはシグナル — インプレッションがビューアブルである可能性の推定。配信後ビューアビリティレートはメジャメント。同じ方法論ファミリー、異なる質問。 * **ガバナンスは規範的、メジャメントは事実的。** ガバナンスは「これは私たちが設定したルールに準拠したか?」と尋ねます。メジャメントは「客観的に何が起こったか?」と尋ねます。アトリビューションモデルは、任意のポリシーに違反せずにバイヤーの結果目標と不一致になりうる。配信がクリーンに測定されてもブランドセーフティ違反は起こりうる。 * **シグナルは入力、メジャメントは出力、ガバナンスは制約。** 購入決定はシグナルを消費し、ガバナンスに境界され、メジャメントが記録する事実を生成します。 ### 線がぼやける場所 * 事前入札ブランドセーフティ分類子は *シグナル* として販売される。同じベンダーの配信後レポートは *検証*。同じ入力データ、異なるプロトコルの居場所 — データが *いつ* 消費されるかで駆動。 * *ガバナンス* ポリシーが証拠として *メジャメント* 証明を要求できる(「このキャンペーンは MRC 認定ベンダーで検証しなければならない」)。メジャメントがガバナンス承認の前提条件になる。 * *シグナル* が *アトリビューション* モデルを供給 — オーディエンスセグメントとアイデンティティシグナルが結果推定を生成するリフトまたは MMM モデルへの入力。 これらの重複はバグではありません。それらはメジャメントとデータ業界が実際にどう機能するかを反映します: ベンダーはライフサイクル全体で運用し、ある層のイベントがしばしば別の層への入力になります。プロトコルの仕事は *インターフェース* をクリーンに保つこと — 同じベンダー、複数のロール、複数のエンドポイント — で、ライフサイクルを単一の表面に崩すことではありません。 ## 実例: サードパーティビューアビリティコミットメント バイヤーが CTV キャンペーンで MRC しきい値の DoubleVerify ビューアビリティを必要とします。SSAI がスコープ内です。バイヤーはどの製品がそれを使うか知らず気にしません。 **1. ディスカバリー。** バイヤーが以下で `get_products` を呼びます: ```json theme={null} { "required_performance_standards": [ { "metric": "viewability", "threshold": 0.70, "standard": "mrc", "vendor": { "domain": "doubleverify.com" } } ] } ``` この在庫で DV の測定をサポートできない製品は — DV のパスが劣化する SSAI 環境を含むどの配管理由でも — 黙ってフィルターアウトされます(filter-not-fail)。セラーは「私は SSAI」と宣言しません。*「このベンダーでこの製品でこのパフォーマンス標準を配信できる」* と宣言します。配管はセラーの問題です。 **2. コミットメント。** バイヤーが `create_media_buy` を呼びます。`performance_standards` がバイコントラクトに入ります。`performance-standard.json` に従い、*クリエイティブは `doubleverify.com` からの `tracker_script` または `tracker_pixel` アセットを含まなければなりません(MUST)*。セラーはコントラクトをスナップショットする `committed_metrics`(標準とベンダーエントリーの両方を運ぶ統一された配列、各が `committed_at` 付き)を返します — バイの寿命の間追加のみ。ビューアビリティコミットメントは `qualifier.viewability_standard: "mrc"` を運び、MRC と GroupM が互いに対して決して照合しないようにします。 **3. 実行。** クリエイティブマニフェストが DV のトラッカーアセットを運びます。それらが発火します — クライアント側、サーバー側、OMID、SIVA、セラーがコミットメントを尊重するため選んだどのパスでも。バイヤーはパスを見ません。 **4. レポート。** バイごとの `totals` が標準 `viewability` ブロック(卒業した Tier 1 表面 — `measurable_impressions`、`viewable_impressions`、`viewable_rate`、`standard`)を投入します。バイ横断の `aggregated_totals` が `metric_aggregates` 経由で qualifier で分割([#3848](https://github.com/adcontextprotocol/adcp/issues/3848)、提案) — コントラクトと同じ原子単位、`(metric_id, qualifier)` で結合。セラーが任意のコミットされたメトリックを配信できない場合、それは `missing_metrics` に現れます — アカウンタビリティ違反、プロトコル内で表示。 **この例が示すもの。** バイヤーは決して *「これは SSAI か?」* と尋ねません。彼らが実際に持つ質問 — *「選んだ検証ベンダーはこの在庫で信頼できるビューアビリティを生成できるか?」* — は、製品がフィルターを通過するかどうかで構造的に答えられます。セラーの配管は、彼らが作成時に署名したコントラクトに束縛された私的な実装詳細です。SSAI、CSAI、アプリ内、ウェブ、DOOH がすべて同じ表面を通じて流れます。どれも特別なスキーマを得ません。 これは、層が機能するはずの方法で機能する検証です: バイヤーが *必要とする結果*(ベンダー + 標準 + しきい値)を指定し、セラーがコミットするか自身を除外し、アカウンタビリティはナラティブではなく構造的です。 ## オープンな質問 分類は何が別個かを明確にしますが、2 つの質問が境界に位置します。 ### メジャメントは専用のプロトコル表面を得るべきか? 測定エージェントは既にファーストクラスです: ベンダーは `brand.json` `agents[type='measurement']`(BrandRef アンカー)として発見可能で、`get_adcp_capabilities.measurement.metrics[]` 経由でメトリックカタログを公開し、`performance-standard.vendor`、`vendor_metrics`、`committed_metrics`(ベンダースコープエントリー)から `BrandRef` で参照され、`delivery-metrics.viewability`(卒業した標準)と `vendor_metric_values`(非卒業ベンダーメトリック)を通じて証明された値を発行します。パターンは *「複数のプロトコル全体で消費される発見可能なエージェントアイデンティティ」* で、*「プロトコルの居場所なし」* ではありません。 オープンな質問は、その分散パターンが正しいか、メジャメントが Signals と Governance と並ぶ *ピアプロトコル* に値するか — 自身のタスク表面(例: `register_measurement`、`attest_outcome`、`dispute_measurement`)と自身の仕様ページとともに。今日、測定エージェントコントラクトは暗黙で、BrandRef が消費される場所の union によって定義されます。 **現状(分散)のケース。** 測定ベンダーは既に OMID、MRC 認定、ベンダー SDK 経由で運用します。プロトコルの仕事はそれらをバイヤー/セラーフローから呼び出し可能にすることで、それをしています。専用プロトコルは既に機能するものを複製するリスクを負います。 **ピアプロトコルのケース。** 紛争解決、再カウント、(配信行のベンダーフィールドではなく)主要アーティファクトとしての署名付き測定証明は、共通プリミティブから利益を得るかもしれません。測定エージェントケイパビリティが *「値をレポート」* を超えて — 飛行中のシグナル生存レポート、予測測定可能性、独立ガバナンス監査へ — 拡大すると、分散パターンが緊張します。 この質問は抽象的な答えを必要としません。現在のパターンに合わない測定ベンダーケイパビリティが表面化するとすぐに解決します。 ### 事前入札測定シグナルはどこに存在するか? 事前入札ビューアビリティスコア(インプレッションがビューアブルになる予測可能性)は、配信後ビューアビリティ測定を生成する同じベンダーによって販売されます。今日、予測は *シグナル*(決定前に消費)。測定は *検証*(配信後に消費)。同じベンダー、同じ方法論ファミリー、2 つのプロトコルの居場所。これは *消費パターン* が異なるから機能します — が、特に予測測定と配信後測定がリアルタイムビディングコンテキストで収束するにつれ、重複コストが層化利益を上回るか監視する価値があります。 ### 会話コンテキストターゲティングはどこに合うか? AI ネイティブチャネル(ChatGPT と類似のエージェンティック会話表面)は、会話トピックをシグナルとして広告をターゲットします — クッキーなし、フィンガープリンティングなし、オーディエンスグラフなし。同じアカウントが異なるチャット主題で異なるアドバタイザーを得ます。プロンプト自体がリアルタイムでターゲティングシグナルを運びます。 これは構造的に *シグナル* 層パターン(予測的、決定前)ですが、従来のコンテキストシグナル(ページ URL またはページコンテンツをターゲット)より細かい粒度です。従来のコンテキスト広告よりウォールドガーデンエンゲージメントシグナルターゲティング(Facebook News Feed)に近い — 在庫がフィード投稿ではなく会話テキストであることを除いて。 AdCP のシグナル分類は今日会話コンテキストターゲティングを直接モデル化しません。それが新しいシグナルタイプに値するか既存の `Contextual signals` カテゴリー内に合うかはオープンな質問です — 在庫形状(会話対ページベース)とシグナルライフサイクル(プロンプトごと対ページビューごと)は異なりますが、消費パターン(決定前ターゲティング入力)は同じです。 ## このプロトコルがしないこと AdCP はメジャメントモデルを実行しません。競合する検証ベンダー間を裁定しません。MRC カウント規約を定義しません。アトリビューション出力を保存または正規化しません。 AdCP がすることは、正しい *接続ポイント* を存在させること — セラーのメトリックがクエリ可能、検証者のパスが宣言可能で実行可能、バイヤーの結果データが添付する場所を持つ、ように。30 年かけて成長したメジャメント業界がそれらの接続ポイントの上に座ります。プロトコルはそれを置き換えません。 # Changelog Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/changelog 破壊的変更、新機能、すべてのリリースのスキーマ更新をカバーする GitHub 上の AdCP 変更履歴へのリンク。 詳細な技術的変更履歴はリポジトリで維持されています。公開されたすべてのバージョンの一覧ステータスについては [Versions & Compatibility](/docs/reference/versions) を参照。高レベルのサマリーと移行ガイドについては [Release Notes](/docs/reference/release-notes) を参照。 [**GitHub で Changelog を見る →**](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md) AdCP はプロトコルパッケージのバージョン管理に [Changesets](https://github.com/changesets/changesets) を使います。公開されるプロトコルサーフェスを変更するプルリクエストは、リリース時に変更履歴にコンパイルされる `adcontextprotocol` チェンジセットを含みます。アプリ、サイト、課金、管理、インフラ、その他の非プロトコル変更は、プロトコルパッケージの変更履歴に現れません。プロトコル変更は [セマンティックバージョニング](https://semver.org/) に従います。 # 実験的ステータス Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/experimental-status AdCP が、仕様に含まれるがまだ凍結されていないサーフェスをどう印付けるか。実験的が実装者にとって何を意味するか、3.x 内で何が変わるか、実験的サーフェスがどう安定版に卒業するか。 一部の AdCP サーフェスは、リリースに公開されるがまだ凍結されていません。実装者がそれらに対して構築を始められるよう出荷されますが、安定サーフェスよりも弱い安定性コントラクトを運びます。このページはそのコントラクトを定義します。 実験的ステータスは、[3.x 安定性保証](/docs/reference/versioning#3x-stability-guarantees)を信頼に足るものに保つ安全弁です。サーフェスは安定 — その場合 3.x 内で壊れることができない — か、明示的に実験的 — その場合壊れることができる — のいずれかです。宣言されていない中間地帯はありません。プロトコルの非目標と延期項目のリストについては [既知の制限](/docs/reference/known-limitations) を参照。 *** ## 何が実験的と見なされるか AdCP サーフェスは、次の両方が真のとき実験的です: 1. そのスキーマが、スキーマルートまたは特定のプロパティに `x-status: experimental` を運ぶ。 2. それを実装するセラーが、その `get_adcp_capabilities` レスポンスの `experimental_features` にそのサーフェスを宣言する。 両方のマーカーが必要です。1 つ目はエコシステムにサーフェスが凍結されていないことを伝えます。2 つ目は特定のバイヤーに、この特定のセラーがこのサーフェスについて実験的コントラクトにオプトインしたことを伝えます。 任意の実験的サーフェスを実装するセラーは、それを `experimental_features` にリストしなければなりません(MUST)。実験的サーフェスをリストしないセラーはそれを実装してはなりません(MUST NOT) — 「黙って実験的」モードはありません。 実験的機能 id は関連タスクのクラスターをカバーします。クラスター内の任意のタスクを実装するセラー(例: `get_rights` だけで `acquire_rights` や `update_rights` はなし)は、依然としてクラスターの機能 id(`brand.rights_lifecycle`)を宣言しなければなりません(MUST)。部分的な実装は許されます。黙った実装は許されません。 `x-status: experimental` はスキーマローカルな注釈です。`$ref` を通じて継承されません — 実験的サブスキーマを参照する安定スキーマが自動的に実験的になることはありません。`get_adcp_capabilities` の `experimental_features` 宣言が権威あるランタイムシグナルです。`x-status` はスキーマ読み取り者とツール向けのオーサリングヒントです。 ## 実験的サーフェスのコントラクト **3.x 内で、実験的サーフェスは安定サーフェスにはできない方法で変わることがあります(MAY):** * フィールドがリネーム、削除、または型変更されることがある * 必須フィールドが任意になり、逆もありうる * Enum が値を削除またはリネームされることがある * タスク名がリネームまたは削除されることがある * 実験的サーフェス用に導入されたエラーコードがリネームまたは削除されることがある **実験的サーフェスへの破壊的変更の予告要件:** * 変更が着地する前に、リリースノートとチェンジログで少なくとも **6 週間** 公開 * 可能な場合は before/after の例を伴う、変更を記述する移行ノート * 実行可能な場合、変更を導入するリリースで新旧両形式を受け入れるエイリアス これは、安定サーフェスに適用される [6 か月の非推奨予告](/docs/reference/versioning#deprecation-policy) の意図的な緩和です。 **なぜ実験的が存在するか。** アーキテクチャ委員会は、真にコアプロトコルの一部だがまだフィールドテストされていないサーフェスにこのラベルを使います。反復パスがなければ、AdCP は誰もデプロイしていない硬直したスキーマを出荷するか、機能が完璧になるまで保留するかのいずれかになります。どちらも実装者に役立ちません。 **実験的サーフェスで変わらないもの:** * 認証、トランスポート、コアセキュリティ要件。これらはバージョンレベルの関心事で、実験的かどうかにかかわらず 3.x 内で決して変わりません。 * 冪等性セマンティクス。セラーの宣言された冪等性コントラクトは、安定サーフェスに適用されるのと同じ方法で実験的サーフェスに適用されます。 * エラーエンベロープ形状。実験的サーフェスは安定サーフェスと同じエンベロープを使ってエラーを返します。特定のコードのみがシフトすることがあります。 ## 安定版への卒業 実験的サーフェスは、次のすべてが満たされたとき安定版に卒業します: | Criterion | Requirement | | ------------- | ----------------------------------------------------------------------------------------------------------- | | **本番シグナル** | 少なくとも 1 つの実装が **本番**(サンドボックスでない)で **45 日以上** 稼働。 | | **クロスパーティ検証** | (a) 2 番目の実装が存在し 45 日以上稼働、うち少なくとも 1 つが本番、または (b) 少なくとも 1 バイヤーが本番でサーフェスに対して統合成功、のいずれか。バイヤー統合なしの単独実装者卒業は許されない。 | | **スキーマ安定性** | 卒業前 **30 日以上** サーフェスに対するオープンな破壊的変更 issue がない。 | | **意図的な昇格** | 卒業 PR がスキーマから `x-status: experimental` を削除し、正準実験的リストから機能 id を削除、変更を運ぶ 3.x リリースのリリースノートで言及される。 | 2 実装者が 1 より低いハードルなのは、クロス実装の摩擦こそが仕様の曖昧さを振るい落とすからです。単一の実装者は反射的に自身のスキーマに一致させられますが、2 つはできません。1 実装者のみが準備できているとき、バイヤー統合シグナルが代替します — そのシグナルは、単独実装者が見逃すバイヤー側の人間工学バグをカバーします。 卒業は決して自動ではありません。アーキテクチャ委員会は卒業 PR をレビューし、サーフェスがまだ不安定性の兆候を示す場合、追加サイクルを要求することがあります。 ### 卒業ケイデンス アーキテクチャ委員会は各 3.x リリースで実験的サーフェスをレビューします。すべてのリリースのノートは、各実験的サーフェスについて次を含みます: * 現在のステータス(依然実験的 / 卒業予定 / 活発な破壊的改訂中) * 次のリリースが運ぶと予想される変更のリスト * 該当する場合、そのサーフェスの最新の破壊的変更予告へのポインター エンタープライズ調達チームは、予測可能なレビューケイデンスで実験的サーフェスを追跡するためにリリースノートを購読できます。別個のメーリングリストやチケットプロセスはありません。 ## クライアントの動作 AdCP セラーに対して統合するバイヤーは次をすべきです(SHOULD): * **実験的サーフェスに依存する前に `experimental_features` を検査する。** 実験的サーフェスをリストしないセラーは、そのサーフェスを実装しないと主張しています。 * 実験的サーフェスに依存するとき **特定の 3.x リリースにピン留めする**、または消費する機能のリリースノートを購読する。 * **リトライとエラー処理をリリース間で追加された新しいエラーコードに耐えるよう設計する**。 * 追加のベンダー保証なしに **実験的サーフェスを規制されたワークフローに不適当として扱う**。実験的はコンプライアンスグレードの安定性のクレームではありません。 ### バイヤー側の拒否 実験的サーフェスと対話したくないバイヤー — 典型的には規制されたワークフロー、コンプライアンス敏感なデプロイ、凍結されていない機能を禁じる調達ポリシー — は、これをクライアント側で強制します。パターン: 1. **ケイパビリティディスカバリー時**に、セラーの `get_adcp_capabilities` レスポンスから `experimental_features` を読む。 2. **呼び出し前にフィルタリングする。** あなたのポリシーが拒否する実験的機能 id に属するタスクを呼ばない。機能 id ごとのタスクのリストは下記の正準実験的サーフェスリストで公開されている。 3. **上流で短絡する。** オーケストレーターや上流呼び出し元が実験的サーフェスを要求する動作をリクエストするとき、呼び出しを試みるのではなくポリシーレベルのエラー(例: 呼び出し元自身の `POLICY_EXPERIMENTAL_REFUSED`)を返す。拒否はバイヤーの関心事でありセラーのものではない — セラーがバイヤーポリシーを推論することを期待してはならない(MUST NOT)。 3.0 にワイヤーレベルの拒否フィールドはありません。バイヤー側フィルタリングで十分で、コントラクトを非対称に保ちます(セラーは宣言し、バイヤーは決定する)。マルチパーティ拒否ハンドオフパターンが実際の統合から現れる場合、相互的なワイヤーメカニズムが将来のリリースで再検討されるかもしれません。 ## 現在の実験的サーフェス `x-status: experimental` でマークされたスキーマが権威あるソースです。AdCP 3.x の `experimental_features` の機能 id の正準リスト: | Feature id | Surface | Why experimental | | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `brand.rights_lifecycle` | [`get_rights`](/docs/brand-protocol/tasks/get_rights)、[`acquire_rights`](/docs/brand-protocol/tasks/acquire_rights)、[`update_rights`](/docs/brand-protocol/tasks/update_rights)。`get_adcp_capabilities` の `brand.rights`、`brand.right_types`、`brand.available_uses`、`brand.generation_providers` ケイパビリティフィールド。これらのサーフェスが参照する `right-use` と `right-type` enum | 3.0 サイクル後期に追加された法的構成のサーフェス。最初のエンタープライズデプロイが部分的権利、サブライセンス、失効、紛争解決のエッジケースを露出させる。2 つの enum は、新しいライセンス可能利用カテゴリー(例: 具現化、ホログラム、生成音楽スタイル)が現れるにつれ値が進化すると予想されるため実験的とマークされる。 | | `governance.campaign` | [`sync_plans`](/docs/governance/campaign/tasks/sync_plans)、[`check_governance`](/docs/governance/campaign/tasks/check_governance)、[`report_plan_outcome`](/docs/governance/campaign/tasks/report_plan_outcome)、[`get_plan_audit_logs`](/docs/governance/campaign/tasks/get_plan_audit_logs) | マルチパーティガバナンスセマンティクス(バイヤー vs セラー承認の衝突、監査来歴の検証、Embedded Human Judgment 下のタイブレーク)がまだ確定していない。 | | `measurement.core` | [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities#measurement) の `measurement` ケイパビリティブロックと `supported_protocols` の `measurement` 値 | 測定は現在ベンダーメトリクスカタログのディスカバリーのみを公開。レポート、アトリビューション、価格/カバレッジ、ベースラインコンプライアンスストーリーボードは凍結されていないため、サーフェスは 3.1 で実験的のまま。 | | `trusted_match.core` | [TMP](/docs/trusted-match/) | プライバシーアーキテクチャが、規制当局の深掘りが要求するものに対して薄く仕様化されている。エクスポージャートークン、国分割アイデンティティ、Offer マクロは変わると予想される。 | | `trusted_match.verified_identity` | `identity-match-request` エントリの `attestation` オブジェクト、トップレベルの `sealed_credentials[]` 配列、`uid-type` enum の `world_id_nullifier` 値、`attestation-claim` enum、`brand.json` の `identity_relying_parties[]` — [Verified Identity Attestation](/docs/trusted-match/specification#verified-identity-attestation) を参照 | 検証可能な人格証明 / 年齢を Identity Match に転送し、バイヤーがアサーションを信頼するのではなく検証する。発行者非依存だが、これまで World ID スキームのみが仕様化されている。`signal_binding` 鮮度ポリシー、発行者/スキームのレジストリ対自由形式の決定、`brand.json` バリアント配置は WG オープン。そうでなければ `additionalProperties:false` の identity-match-request スキーマの拡大はコントラクトを担い、実験的の下で改訂されることがある。 | | `sponsored_intelligence.core` | [`si_get_offering`](/docs/sponsored-intelligence/tasks/si_get_offering)、[`si_initiate_session`](/docs/sponsored-intelligence/tasks/si_initiate_session)、[`si_send_message`](/docs/sponsored-intelligence/tasks/si_send_message)、[`si_terminate_session`](/docs/sponsored-intelligence/tasks/si_terminate_session)。`get_adcp_capabilities` の `sponsored_intelligence` ケイパビリティフィールド。[SI 仕様](/docs/sponsored-intelligence/specification) で定義される SI アイデンティティ、ケイパビリティネゴシエーション、UI コンポーネントサーフェス | 会話的なブランド体験は新しい広告モデル。セッションライフサイクル、UI コンポーネント、アイデンティティ/同意オブジェクト形状、ケイパビリティネゴシエーションは、ファーストパーティ AI ホストとブランドエージェントが統合するにつれ進化すると予想される。計画された変更は [3.1.0 ロードマップ](https://github.com/adcontextprotocol/adcp/issues/2201) を追跡する。 | | `creative.evaluator` | [`build_creative`](/docs/creative/task-reference/build_creative) の `evaluator` 入力、ビルドレスポンスのリーフごとの `eval` ブロック、`get_adcp_capabilities` の `creative.supports_evaluator` ケイパビリティフィールド(スキーマ: `core/evaluator-spec.json`) | 新しい gate-then-rank のクリエイティブ機能オラクルサーフェス(#5241 / #5311)、まだ当事者間でフィールドテストされていない。エバリュエーターランキングは、並行するエバリュエーターカタログを鋳造するのではなく、意図的に既存の安定クリエイティブ機能カタログを再利用する: `rank_by`、`feature_requirement`、`eval.features[]` はすべて `governance.creative_features` の機能 ID を参照する。`evaluator_id` はそのカタログとは別: 出力が依然クリエイティブ機能結果に解決される、事前プロビジョニング/アカウント手配されたプリセット。別個の `supports_evaluator_gate` ケイパビリティとハードな MUST-enforce-gate セマンティクスは、エバリュエーターフィールドを再形成しうる予約されたフォローオン。 | | `creative.signal_fanout` | [`build_creative`](/docs/creative/task-reference/build_creative) の `signal_conditions[]` と `selection_strategy` 入力、そのレスポンスの `creatives[].signal_condition` / `selection_strategy_applied` / `estimate.conditions_total` フィールド、`get_adcp_capabilities` の `creative.multiplicity.supports_signal_fanout` / `max_signal_conditions_limit` / `selection_strategies` ケイパビリティフィールド、`enums/creative-selection-strategy.json` enum、`SIGNAL_TARGETING_INCOMPATIBLE` エラーコード(#5240 / #5304、#5262 を折り込む) | クリエイティブエージェント + セールスエージェントにまたがるクロスエージェントの reject-at-trafficking MUST(`SIGNAL_TARGETING_INCOMPATIBLE`)を導入する新しいシグナル駆動のクリエイティブファンアウトサーフェス — まだ当事者間でフィールドテストされていない。数値(`value_type: numeric`)条件互換性比較(範囲重複対完全一致)はまだ WG オープンで、`proximity` 選択戦略のジオ入力バインドはまだ確定していない — 両方とも実験的の下で改訂可能なまま。条件アイデンティティ自体は今日解決可能(`get_signals` 経由の `signal_agent_segment_id` / `signal_ref`)なので、凍結識別子の問題はない。 | 卒業の進捗と今後の破壊的変更の予告は、3.0 GA から始まる各 3.x リリースの [リリースノート](/docs/reference/release-notes) で言及されます。 ## 拡張との関係 実験的ステータスは [拡張](/docs/reference/versioning#extensibility) と同じではありません。拡張は `ext.{namespace}` フィールドに存在し、拡張レジストリによって統治されます。それらはコアプロトコルにとって恒久的に帯域外です。実験的サーフェスはコアプロトコル内にあります — それらは安定版への昇格の候補であり、サードパーティの追加ではありません。 アーキテクチャ委員会がサーフェスをコアプロトコルの一部として意図するがまだ凍結する用意がないとき、サーフェスは実験的であるべきです。サーフェスがドメイン固有、コアプロトコル外で保守、またはより広い AdCP エコシステムに適用される可能性が低いとき、それは拡張であるべきです。 # 用語集 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/glossary ## A **Account Type** MCP セッションクレデンシャルを「platform」(アグリゲーター)または「customer」(直接の広告主/代理店)として分類したもの。 **Activation** 特定のプラットフォームとシートでシグナルをターゲティングに利用可能にするプロセス。 **Ad Context Protocol (AdCP)** AI を用いた広告ワークフローのためのオープン標準。AdCP は MCP と A2A をトランスポートとして動作するドメイン固有のタスクとスキーマを定義し、広告オペレーションの自然言語インターフェースを可能にします。 **Agentic Commerce Protocol (ACP)** OpenAI と Stripe が策定した、AI アシスタントにおけるプログラマティックなコマースフローのためのオープン標準。ACP はエージェントが加盟店にならずにチェックアウトを開始し、支払いを委任し、取引を完了する方法を定義します。Sponsored Intelligence の文脈では、ブランドエージェントが購入意図を持つユーザーを引き渡した後のトランザクションを ACP が処理します。 **Agentic eXecution Engine (AXE)** オーケストレーターとディシジョニングプラットフォームの間に位置するリアルタイム実行レイヤーで、動的オーディエンスターゲティング、ブランドセーフティ適用、フリークエンシー管理、インプレッション時の 1st パーティデータ活用を処理します。詳細は [AXE documentation](/docs/media-buy/advanced-topics/agentic-execution-engine) を参照。 **AgenticAdvertising.org** Ad Context Protocol (AdCP) と関連する AI 活用広告のオープン標準を運営するメンバー組織。 ## B **Budget** メディアバイに割り当てられた総予算額。複数のパッケージに配分可能。 ## C **CPC (Cost Per Click)** 広告のクリック単価に基づく課金モデル。 **CPCV (Cost Per Completed View)** 動画または音声を 100% 再生した回数あたりの課金モデル。 **CPM (Cost Per Mille)** インプレッション 1000 回あたりの課金モデル。従来のディスプレイ広告の価格体系。 **CPP (Cost Per Point)** TV やオーディオ広告で一般的な、GRP(Gross Rating Point)あたりの課金モデル。 **CPV (Cost Per View)** パブリッシャー定義の閾値(例: 動画 50% 再生)での視聴 1 回あたりの課金モデル。 **Completed View** 動画または音声広告が 100% 再生された状態。CPCV 課金や完了率指標に使用。 **Completion Rate** 動画または音声広告が 100% 再生された割合(completed\_views / impressions)。 **Customer Account** 特定のシートアクセスとレートを持つ直接の広告主または代理店アカウント。 ## D **Daypart** 時間帯ベース広告の特定時間帯セグメント。DOOH で一般的(例: morning\_commute, evening\_prime, overnight)。 **Deployment** (Signals Protocol) 特定プラットフォーム上でのシグナルの利用可能状況。アクティベーション状態やタイミングを含みます。 **Devices** (Signals Protocol) ユニークデバイス ID(クッキー、モバイル ID)を表すサイズ単位。通常もっとも大きいリーチ指標。 **Device Type** (Media Buy Protocol) プラットフォーム種別のターゲティング軸。mobile、desktop、tablet、CTV、audio、DOOH。 **Decisioning Platform** インプレッション時に配信する広告を選択する技術基盤。アクティベートされたシグナルを受け取りキャンペーンを実行します。例: DSP(The Trade Desk)、SSP(Index Exchange, OpenX, PubMatic)、アドサーバー(Google Ad Manager, Kevel)。 **DOOH (Digital Out-of-Home)** ビルボード、駅、空港、小売店舗など公共空間のスクリーンに表示されるデジタル広告。SOV、期間、会場ターゲティングのパラメーター付きで CPM または flat\_rate 価格を用いる。 **DSP (Demand-Side Platform)** 広告主がプログラマティックに在庫を購入できるディシジョニングプラットフォームの一種。 ## E **Estimated Activation Time** シグナル展開の推定時間。新規アクティベーションでは通常 24〜48 時間。 ## F **Flat Rate** 配信量に関係なく固定額を支払う価格モデル。スポンサーシップやテイクオーバーで一般的。 **Flight** 期間で区切られた広告キャンペーンの区分。アドサーバーのラインアイテムに対応。 **Frequency** キャンペーン期間中に個人が広告に接触する平均回数。 ## G **GRP (Gross Rating Point)** テレビ・ラジオ広告で使用される指標で、ターゲットオーディエンスの 1% を表します。CPP 価格モデルで使用。総 GRP = リーチ % × 平均頻度。Nielsen、Comscore、iSpot.tv、Triton Digital などの第三者によって測定。 ## H **Households** ユニークな世帯住所を表すサイズ単位。地理的・家族ベースのターゲティングに有効。 **Human-in-the-Loop (HITL)** パブリッシャーが操作に手動承認を要求できるようにするプロトコル機能。 ## I **Impressions** (Media Buy Protocol) 広告が表示された回数。課金や配信追跡に使用。 **Individuals** (Signals Protocol) ユニークな人を表すサイズ単位。フリークエンシーキャップやデモグラフィックターゲティングに最適。 **Inventory** ウェブサイト、アプリ、その他メディア上で利用可能な広告枠。 ## L **Line Item** Google Ad Manager などのアドサーバーにおける基本的な在庫単位。AdCP では package として表現。 **Loop Duration** DOOH で広告が 1 周する時間。秒単位。頻度やシェア・オブ・ボイスの算出に使用。 **Loop Plays** DOOH のループ内で広告が表示された回数。DOOH 配信レポートの主要指標。 ## M **Marketplace Signal** データプロバイダーからライセンス提供されるサードパーティシグナル。 **MCP (Model Context Protocol)** AI アシスタントが外部システムとやり取りするための基盤プロトコルフレームワーク。 **Media Buy** パッケージ、予算、ターゲティング、クリエイティブアセットを含む完全な広告キャンペーン。 ## N **Natural Language Processing**\ 技術パラメーターではなく会話的な記述によってオーディエンス探索を可能にする AI 能力。 ## O **Owned Signal**\ 広告主またはプラットフォームが保有する 1st パーティのシグナルデータ。 ## P **Package** メディアバイ内の特定の広告プロダクトで、独自の価格とターゲティングを持つフライトまたはラインアイテム。 **Platform Account** シグナルを複数の顧客に配信できる広告プラットフォームを表すマスターアカウント。 **Pricing Model** 広告在庫の価格付けと請求方法。AdCP は CPM、CPC、CPCV、CPV、CPA、CPL、CPP、フラットレートモデルをサポート。 **Pricing Option** パブリッシャーがプロダクトに提供する具体的な価格オプション。レート、通貨、パラメーターを含みます。 **Principal** 一意のアクセス資格とプラットフォームマッピングを持つ認証済み主体(広告主、代理店、ブランド)。 **Product** 自然言語クエリを通じて探索できる購入可能な広告在庫。 **Prompt** 関連シグナルを探索するための自然言語記述(例: "high-income sports enthusiasts", "premium automotive content", "users in urban areas during evening hours")。 **Provider** シグナルデータを提供する企業やプラットフォーム(例: LiveRamp、Experian、Peer39、天気サービス)。 ## Q **Quartile (Video)** 動画広告視聴の区切り。Q1(25% 視聴)、Q2(50% 視聴)、Q3(75% 視聴)、Q4(100% 完了)。動画エンゲージメント測定に使用。 ## R **Reach** キャンペーン期間中に少なくとも 1 度広告に接触したユニーク個人の数または割合。 **Relevance Score** シグナルが探索プロンプトにどれだけ合致するかを示す数値指標(0-1)。 **Relevance Rationale** オーディエンスがそのリレバンススコアを受けた理由を示す、人が読める説明。 **Revenue Share** 固定 CPM ではなくメディア支出割合に基づく価格モデル。 ## S **Sales Agent** パブリッシャー在庫を探索・購入用に公開する MCP サーバー。プロダクト探索、メディアバイ作成、キャンペーン管理を担当。例: AdCP インターフェースを公開するパブリッシャーのアドサーバー、セールスハウスプラットフォーム。 **Screen Time** DOOH 画面全体で広告が表示された総時間。秒単位。DOOH 配信レポートに使用。 **Seat** ディシジョニングプラットフォーム内の特定広告アカウント。通常はブランドまたはキャンペーンを表します。 **Segment ID** シグナルアクティベーションに使われる固有の識別子。signal\_id と異なる場合があります。 **Share of Voice (SOV)** DOOH ループで特定広告主に割り当てられる在庫割合。0.0-1.0 で表現(例: 0.15 = 15% SOV)。 **Signal Agent** シグナル探索とアクティベーションを提供する MCP サーバー。自然言語でのオーディエンス探索を可能にし、シグナルをディシジョニングプラットフォームへ展開します。プライベート(単一プリンシパル所有)やマーケットプレイス(複数プリンシパルにデータをライセンス)になり得ます。例: LiveRamp、Experian、Peer39。 **Signal Discovery** 自然言語の記述を用いて、関連するデータシグナル(オーディエンス、コンテキスト、地理、時間帯)を見つけるプロセス。 **Signal ID** プロバイダのカタログ内でのシグナルの一意識別子。 **Signal Type** シグナルを "marketplace"(サードパーティ)、"owned"(1st パーティ)、"destination"(媒体に同梱)、"contextual"、"geographical"、"temporal" に分類。 **Size Unit** (Signals Protocol) シグナルサイズの測定単位。individuals、devices、households。 **Sponsored Intelligence (SI)** AI アシスタントにおける会話型ブランド体験のためのオープン標準。VAST が動画広告配信を定義するように、SI はブランドエージェントエンドポイントの提供と対話方法を定義します。SI はエンゲージメントを扱い、取引は Agentic Commerce Protocol (ACP) が処理します。詳細は [Sponsored Intelligence Protocol](/docs/sponsored-intelligence/overview) を参照。 **SSP (Supply-Side Platform)** パブリッシャーが広告在庫をプログラマティックに販売できるようにするディシジョニングプラットフォームの一種。複数の需要ソースに接続し広告選択を行います。例: Index Exchange、OpenX、PubMatic、Magnite。 ## T **Takeover** 特定期間の DOOH 在庫における 100% シェア・オブ・ボイスの独占枠。flat\_rate で sov\_percentage: 100 として価格設定。 **Third-Party Signal** 外部プロバイダーからライセンスされるシグナルデータ。マーケットプレイスシグナルとも呼ばれます。 **Time-Based Pricing** インプレッションではなく時間(時間単位、日単位、時間帯単位)に基づく価格体系。flat\_rate モデルを用いる DOOH 広告で一般的。 ## U **Universal Commerce Protocol (UCP)** Google が Shopify、Walmart、Target などと共に策定した、AI アシスタントでのコマース向けオープン標準。UCP はチェックアウト、支払い、フルフィルメントのプリミティブを定義します。ACP(Agentic Commerce Protocol)とともに、AdCP の広告レイヤーを補完するコマースレイヤーを構成します。 **Usage Reporting** 請求と最適化のためのシグナル利用状況の日次レポート。 ## V **Venue** DOOH 広告が表示される物理的な場所(例: 空港ターミナル、駅、小売店)。DOOH ターゲティングと配信レポートに使用。 **Venue Package** 特定会場にわたる DOOH スクリーンの命名済みコレクション(例: 'times\_square\_network', 'airport\_terminals')。DOOH の価格パラメーターで使用。 ## Acronyms * **ACP**: Agentic Commerce Protocol * **AdCP**: Ad Context Protocol * **API**: Application Programming Interface * **AXE**: Agentic eXecution Engine * **CPM**: Cost Per Mille (thousand) * **DMP**: Data Management Platform * **DSP**: Demand-Side Platform * **MCP**: Model Context Protocol * **PII**: Personally Identifiable Information * **RTB**: Real-Time Bidding * **SI**: Sponsored Intelligence * **SSP**: Supply-Side Platform * **TTD**: The Trade Desk * **UCP**: Universal Commerce Protocol * **UTC**: Coordinated Universal Time ## Units and Measurements **Time Formats** * すべてのタイムスタンプは ISO 8601 形式(例: "2025-01-20T14:30:00Z") * 日付は YYYY-MM-DD 形式 * アクティベーション時間は人が読める推定値("24-48 hours")で表記 **Currency** * 価格は指定通貨(一般的に USD)で表示 * CPM は 1000 インプレッションあたりのコストで表記 * Revenue share は小数(0.15 = 15%)で表記 **Size Reporting** (Signals Protocol) * 件数は整数で表記 * 単位を明確に記載(individuals/devices/households) * 鮮度のため "as\_of" タイムスタンプを付与 **Impression Reporting** (Media Buy Protocol) * パッケージごとの配信件数 * 最適化のためのペーシング指標 * 次元別の内訳提供可 ## Error Codes Reference Common error codes across all AdCP implementations: * `SEGMENT_NOT_FOUND`: セグメント ID が無効または期限切れ * `ACTIVATION_FAILED`: アクティベーション処理を完了できません * `ALREADY_ACTIVATED`: プラットフォーム/シートでシグナルが既にアクティブ * `DEPLOYMENT_UNAUTHORIZED`: プラットフォーム/シートの権限不足 * `BUDGET_EXCEEDED`: 割り当て予算を超過する操作 * `CREATIVE_REJECTED`: クリエイティブアセットがプラットフォーム審査に失敗 * `INVALID_PRICING_MODEL`: 要求した価格モデルが利用不可 * `RATE_LIMIT_EXCEEDED`: 一定時間内のリクエスト過多 * `AUTHENTICATION_FAILED`: 資格情報が無効または期限切れ * `VALIDATION_ERROR`: リクエスト形式やパラメーターのエラー # GMSF リファレンス Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/gmsf-reference AI エージェントのための Global Media Sustainability Framework の要点 # GMSF リファレンス Global Media Sustainability Framework のトークン効率の良いリファレンスです。広告の持続可能性について議論する AI エージェント向けに設計されています。 ## GMSF とは **Global Media Sustainability Framework (GMSF)** は、メディアと広告における炭素排出を測定・削減するための業界標準です。Ad Net Zero と業界関係者によって策定されました。 **目的**: 広告サプライチェーン全体で炭素測定を標準化すること。 ## 主な組織 | Organization | Role | | --------------- | ------------------------------------------------- | | **Ad Net Zero** | サステナビリティ施策を主導する業界連合 | | **WFA** | World Federation of Advertisers - ブランドのコミットメントを調整 | | **IAB** | 技術標準と実装ガイダンスを提供 | | **Scope3** | メディア向けの炭素測定プラットフォーム | ## 広告テクノロジーにおける炭素排出源 ### サプライチェーンの段階別 ``` ┌─────────────────────────────────────────────────────────────┐ │ CAMPAIGN CREATION │ │ Creative production, asset hosting, transcoding │ │ Carbon: Medium │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ AD DECISIONING │ │ Bid requests, auction processing, ML inference │ │ Carbon: HIGH (biggest contributor in programmatic) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ AD DELIVERY │ │ CDN distribution, ad rendering, tracking pixels │ │ Carbon: Medium-High │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ MEASUREMENT │ │ Attribution, reporting, analytics │ │ Carbon: Low-Medium │ └─────────────────────────────────────────────────────────────┘ ``` ### プログラマティックにおける浪費ポイント | Issue | Carbon Impact | Description | | ------------------- | ------------- | --------------------- | | **Bid requests** | Very High | 膨大なリクエストの大半が落札に至らない | | **Header bidding** | High | 複数の SSP によりリクエストが増幅 | | **Reselling** | High | 同じインプレッションが何度も販売される | | **MFA sites** | High | 低品質な広告目的サイト | | **Invalid traffic** | High | 価値のないインプレッションを生成するボット | ## GMSF の測定フレームワーク ### スコープ分類 | Scope | Definition | Examples in Ad Tech | | ----------- | ------------ | ------------------- | | **Scope 1** | 直接排出 | オフィス空調、社用車 | | **Scope 2** | エネルギー由来の間接排出 | データセンターの電力 | | **Scope 3** | バリューチェーン排出 | メディアバイ、クリエイティブ制作 | **Note**: 広告における炭素排出の大半は Scope 3(下流のサプライチェーン)です。 ### 主要指標 | Metric | Definition | Target | | -------------------------- | ------------------------ | -------- | | **gCO2e/impression** | 広告インプレッションあたりの CO2 換算グラム | 低いほど良い | | **gCO2e/1000 impressions** | 千回あたりの炭素量 | 業界ベンチマーク | | **Supply path carbon** | 仲介による排出量 | 経路を短縮 | ### 典型的な炭素量の目安 | Channel | gCO2e per 1000 impressions | Notes | | -------------------- | -------------------------- | ---------------- | | Direct sold | 50-150 | 最短経路 | | Programmatic display | 200-400 | 仲介が多い | | Programmatic video | 400-800 | 大きなファイル + オークション | | CTV programmatic | 300-600 | サプライパスに依存 | *値は概算であり、測定方法によって変動します。* ## AdCP が炭素を削減する方法 | Programmatic Problem | AdCP Solution | Carbon Reduction | | -------------------- | --------------- | ---------------- | | 無数のビッドリクエスト | 直接交渉 | 90%以上のリクエスト削減 | | 多段のサプライパス | パブリッシャー直接 | 仲介を排除 | | リアルタイムオークション | 非同期の意思決定 | サーバー計算を削減 | | Cookie シンキング | コンテキストベースのマッチング | シンクトラフィックを排除 | | 投機的入札 | 意図ベースの購入 | 必要な在庫のみ要求 | ### 定量的な効果 ``` 従来のプログラマティック: 1 インプレッション = 50 以上のビッドリクエスト × 10 以上の SSP = 500 以上のサーバーコール AdCP エージェント指向: 1 インプレッション = 営業エージェントへの 1 リクエスト = 1 サーバーコール 削減効果: サーバーコールを約 99% 削減 ``` ## サステナビリティのベストプラクティス ### バイヤー向け | Practice | Impact | | -------------------- | ------------- | | サプライチェーンの経路を短縮 | -30~50% の炭素削減 | | オープンオークションより直接取引を選択 | -40~60% の炭素削減 | | MFA 在庫を回避 | -20~40% の炭素削減 | | グリーン認証を受けたパブリッシャーを選ぶ | 変動 | | クリエイティブファイルサイズを最適化 | -10~20% の炭素削減 | ### パブリッシャー向け | Practice | Impact | | --------------------- | ------------- | | ヘッダービディングのパートナー数を削減 | -20~40% の炭素削減 | | レイジーローディングを実装 | -15~25% の炭素削減 | | 広告リフレッシュ頻度を最適化 | -10~30% の炭素削減 | | 再生可能エネルギーによるホスティングを利用 | -50~90% の炭素削減 | | エージェント/直接チャネルを有効化 | -60~90% の炭素削減 | ## GMSF リソース | Resource | URL | | ------------------ | -------------------------------------------------------------------- | | Ad Net Zero | [adnetzero.com](https://adnetzero.com) | | GMSF Documentation | [wfanet.org/gmsf](https://wfanet.org/leadership/gmsf) | | Scope3 | [scope3.com](https://scope3.com) | | IAB Sustainability | [iab.com/sustainability](https://www.iab.com/topics/sustainability/) | ## 主要な用語 | Term | Definition | | ------------------------- | ---------------------------- | | **Carbon offset** | 炭素除去を資金提供して排出を相殺すること | | **Carbon neutral** | 排出量実質ゼロ(オフセット可) | | **Net zero** | 実質ではなく実際のゼロ排出(オフセットなし) | | **Science-based targets** | 気候科学に基づく削減目標 | | **GHG Protocol** | 温室効果ガス測定の標準 | | **MFA** | Made-for-advertising(低品質サイト) | | **SPO** | Supply path optimization | *** *本ドキュメントはプレースホルダーです。今後、詳細や引用を追加して強化します。* # 実装者向け FAQ Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/implementor-faq AdCP 実装者のための質問と回答 AdCP の営業エージェントや統合を構築するチームから寄せられる一般的な質問に、仕様に基づいた回答をまとめました。 ## プロダクト探索 ### Q: `get_products` では予算、日付、目標などの特定パラメーターが必須ですか? **A:** 現時点で `get_products` のパラメーターは `brief`(自然言語文字列)、`brand_manifest`、`filters`(構造化フィルター)の 3 つのみです。予算、日付、目標などのキャンペーン詳細は自然言語の `brief` に含めてください。 **将来:** 会話の往復を減らすため、予算・日付・目標・ターゲティングの構造化パラメーターを検討しています。最新情報はロードマップを確認してください。 **現在の回避策:** これらの詳細をブリーフに含めます: ```json theme={null} { "brand_manifest": { "name": "Acme Corp", "url": "https://acmecorp.com" }, "brief": "Tech startup needs display and video inventory to reach IT decision makers. Budget: $25K. Timeline: March 1-31. Objective: Lead generation with 2% conversion target." } ``` ### Q: `get_products` で特定のオーディエンスターゲティングを指定するには? **A:** すべてのターゲティング指定は現在、自然言語の `brief` に記述します。まだ構造化されたターゲティングフィルターはありません。 **例:** ```json theme={null} { "brand_manifest": { "name": "Energy Drink Co", "url": "https://energydrink.com" }, "brief": "Target sports fans, ages 18-34, in major US cities for energy drink campaign" } ``` パブリッシャーの AI がこれを解釈し、関連するプロダクトを返します。将来のバージョンで構造化ターゲティングフィルターが追加される可能性があります。 ### Q: 「no brief = 標準カタログ」とは? 標準カタログがありません。 **A:** バイヤーが `brief` フィールドを省略すると、標準カタログの要求になります。これは、カスタムレコメンドなしで全広告主に提供するベースラインのプロダクトです。 **標準カタログを提供していない場合** はエラーを返します: ```json theme={null} { "message": "We require a campaign brief to recommend products. Please provide details about your campaign goals, audience, and objectives.", "products": [] } ``` **標準カタログを提供している場合** は、指定されたフィルター(フォーマットタイプ、デリバリータイプなど)に合う標準プロダクトを返してください。 ### Q: バイヤーは `get_products` の前後どちらで `list_creative_formats` を呼ぶべきですか? **A:** **`get_products` の後** です。推奨フローは以下の通り: 1. キャンペーンブリーフ/フィルターとともに `get_products` を呼ぶ 2. 返されたプロダクトを確認する(`format_ids` 配列を含む) 3. 詳細が必要なフォーマット ID について `list_creative_formats` を呼ぶ 4. クリエイティブ要件が自社の能力と合致するか検証します 5. 選択したプロダクトで `create_media_buy` を呼ぶ **理由:** どのフォーマット ID が必要かは、利用可能なプロダクトを確認するまで分かりません。すべてのフォーマットを最初に取得するのは非効率です。 ## ポリシーコンプライアンス ### Q: パブリッシャーは広告ポリシーの問題(アルコール、アダルトなど)をどう把握しますか? **A:** パブリッシャーは `brand_manifest` フィールドから広告主のアイデンティティを抽出します: 1. `brand_manifest.name`, `brand_manifest.url`, 任意の `brand_manifest.category` から **広告主情報を抽出** 2. `brief` テキストから **何がプロモートされているか確認** 3. 自社ポリシーに基づき **ポリシールールを適用** 4. **適切なレスポンスを返します:** * Allowed: プロダクトを通常返す * Blocked: ポリシー説明付きで products を空配列にします * Restricted: 手動承認が必要な旨を示します **Example blocked response:** ```json theme={null} { "message": "I'm unable to offer products for this campaign. Our publisher policy prohibits alcohol advertising without age verification capabilities.", "products": [] } ``` [Policy Compliance](/docs/media-buy/media-buys/policy-compliance) に完全な実装ガイドがあります。 ### Q: 広告主名は常に共有されますか? **A:** はい。`brand_manifest` フィールドは `get_products` と `create_media_buy` の両方で必須です。これは次のために必要な広告主の識別子を提供します: * ポリシーコンプライアンスチェック * ビジネス関係管理(KYC) * 請求・レポート Minimal manifests are simple: ```json theme={null} { "brand_manifest": { "name": "Acme Corp", "url": "https://acmecorp.com" } } ``` 任意の `category` フィールド(例: `"athletic_apparel"`, `"alcohol"`, `"pharma"`)は自動ポリシーフィルタリングに役立ちます。 ## スキーマとフィールド ### Q: `filters` パラメーターが「オブジェクト」と呼ばれるのはなぜですか? **A:** 複数の任意フィールドを持つ入れ子の JSON オブジェクトだからです: ```json theme={null} { "filters": { "delivery_type": "guaranteed", "format_types": ["video", "display"], "standard_formats_only": true, "is_fixed_price": true, "min_exposures": 10000 } } ``` 単一のフィルターではなく、プロダクトカタログ検索のフィルター条件集合です。 ### Q: なぜ `ad_units` ではなく `format_ids` なのですか? **A:** AdCP はプロトコル非依存の用語を採用しています: * **`format_ids`**: クリエイティブフォーマット仕様への構造化参照(例: `{agent_url: "...", id: "video_30s_hosted"}`) * **Ad units**: 「300x250 バナー」のようなプラットフォーム固有の用語 フォーマットはすべての広告プラットフォームで機能する抽象的な仕様です。`_ids` サフィックスは参照であることを示し、完全なフォーマットオブジェクトは `list_creative_formats` で取得します。 ### Q: 以前のドキュメントにあった `promoted_offering` フィールドはどこにありますか? **A:** それは以前のバージョンのドキュメント誤りです。実際の API に **このフィールドは存在しません**。 正しい方法は次の通りです: * **広告主の識別子** → `brand_manifest.name` と `brand_manifest.url`(必須) * **プロモーション内容** → `brief` フィールドで記述(任意) Example: ```json theme={null} { "brand_manifest": { "name": "Nike", "url": "https://nike.com", "category": "athletic_apparel" }, "brief": "Nike Air Max 2024 - latest innovation in cushioning technology targeting runners and fitness enthusiasts" } ``` ## ブリーフの扱い ### Q: バイヤーが不完全なブリーフを提供したらどうする? **A:** 重要情報が欠けている場合、パブリッシャーは明確化を求めるべきです: ```json theme={null} { "message": "I'd be happy to help find the right products for your campaign. To provide the best recommendations, could you share:\n\n• What's your campaign budget?\n• When do you want the campaign to run?\n• Which geographic markets are you targeting?", "products": [] } ``` これにより必要なコンテキストを集めつつ、会話的で親切な対応を維持できます。 ### Q: 常に明確化を求めるべきですか、それともそのままプロダクトを返しますか? **A:** パブリッシャーの戦略によります: * **ハイタッチ型**: 不完全なブリーフには明確化を求め、会話的に対応します * **セルフサービス型**: 利用可能な情報を基にベストなプロダクトを返す どちらも有効です。ターゲットバイヤーのペルソナと自動化レベルに合わせて選択してください。 ## ワークフローと統合 ### Q: `brand_manifest` を必須とする場面と任意とする場面は? **A:** 現行仕様では次の通りです: * **`get_products` と `create_media_buy` の両方で必須** ベストプラクティス: 常に必須にします。ポリシーチェックは購入時ではなく探索時に行うべきです。 ### Q: バイヤーはプロダクトレスポンスをキャッシュできますか? **A:** プロダクトは時間とともに変化する在庫状況を表します。推奨は次の通りです: * **ブリーフベースの探索**: キャッシュしない — プロダクトはブリーフに基づいてコンテキストマッチされるため * **標準カタログ**: カタログが安定していれば短時間(5〜15 分)キャッシュ可 * **プロダクト詳細**: `product_id` のマッピングはキャッシュ可能だが、購入前に在庫を再検証します ### Q: 1000 件以上の大規模プロダクトカタログはどう扱う? **A:** 完全な `properties` 配列の代わりに `property_tags` を使用します: ```json theme={null} { "product_id": "local_radio_midwest", "property_tags": ["local_radio", "midwest"], "format_ids": [...] } ``` バイヤーは [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) を通じてエージェントのポートフォリオ(パブリッシャードメインや主要チャネル)を把握できます。これによりレスポンスを軽量に保ちながら検証機能を維持できます。 ## テストとバリデーション ### Q: ポリシーコンプライアンスをどうテストしますか? **A:** Create test cases with known restricted categories: ```javascript theme={null} // Test blocked category const response = await get_products({ brand_manifest: { name: "Test Alcohol Brand", url: "https://test-alcohol.example.com", category: "alcohol" }, brief: "Promote our new craft beer" }); assert(response.products.length === 0); assert(response.message.includes("policy")); ``` ### Q: 統合テストで何を検証すべきですか? **A:** 次の主要シナリオをカバーしてください: 1. **ブリーフなし + フィルターあり** → 標準カタログ 2. **ブリーフあり** → `brief_relevance` 付きで AI マッチしたプロダクト 3. **ブロック対象広告主** → ポリシーエラー 4. **不完全なブリーフ** → 明確化リクエスト 5. **一致するプロダクトなし** → 有用な代替提案 6. **フォーマットフィルタリング** → 一致するフォーマットのみ返却 ## よくある落とし穴 ### Q: フォーマットフィルターが機能しないのはなぜ? **A:** 文字列ではなく構造化された `format_ids` を使っているか確認してください: **誤り:** ```json theme={null} { "filters": { "format_ids": ["video_30s"] // ❌ Strings don't work } } ``` **正:** ```json theme={null} { "filters": { "format_ids": [ { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s_hosted" } ] } } ``` ### Q: プロダクトに `brief_relevance` が含まれないのはなぜ? **A:** `brief_relevance` は `brief` パラメーターが提供された場合にのみ含まれます。標準カタログ要求(ブリーフなし)ではコンテキストマッチがないためこのフィールドは含まれません。 ### Q: `get_products` で認可を検証すべきですか? **A:** **はい!** バイヤーエージェントは購入前に営業エージェントの認可を検証する必要があります: 1. プロダクトから properties を取得(または `property_tags` を解決) 2. 各 `publisher_domain` から `/.well-known/adagents.json` を取得 3. 営業エージェントの URL が `authorized_agents` に含まれることを確認 4. 認可されていないエージェントのプロダクトを拒否 完全な要件は [Authorization Validation](/docs/governance/property/adagents#buyer-agent-validation) を参照してください。 ## 用語 ### Q: 「product」と「package」の違いは? **A:** * **Product**: パブリッシャーの販売可能な在庫単位(`get_products` が返す) * **Package**: 利用可能なプロダクトからバイヤーが選択したもの(`create_media_buy` で送信) Product は入手可能なものを示し、Package は購入対象を示します。 ### Q: 「delivery」と「distribution」の違いは? **A:** * **Delivery type**: `"guaranteed"` と `"non_guaranteed"`(インプレッション保証の有無) * **Distribution**: クリエイティブが広告サーバーへ配信される方法(メディアバイではなくクリエイティブエージェントの領域) ### Q: ドキュメントにある "AXE" とは? **A:** **Agentic eXecution Engine (AXE)** ― オーケストレーターとディシジョニングプラットフォームの間にあるリアルタイム実行レイヤーです。AXE は動的オーディエンスターゲティング(バイヤー持ち込みセグメント)、ブランドセーフティ適用、フリークエンシー管理、インプレッション時の 1st パーティデータ活用を処理します。詳細は [AXE documentation](/docs/media-buy/advanced-topics/agentic-execution-engine) を参照してください。 ## さらにサポートが必要ですか? ここで扱われていない場合: 1. 詳細な API ドキュメントは [Task Reference](/docs/media-buy/task-reference/) を確認 2. 探索ガイダンスは [Brief Expectations](/docs/media-buy/product-discovery/brief-expectations) を参照 3. プロダクトモデルの詳細は [Media Products](/docs/media-buy/product-discovery/media-products) を参照 4. 仕様の不明点は GitHub Issue を作成 この FAQ は実装者からのフィードバックに基づき定期的に更新されます。 # Media Channel Taxonomy Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/media-channel-taxonomy メディアプランニングツール、アドテクプラットフォーム、AI エージェント間で相互運用可能な広告メディアチャンネルの標準タクソノミー # Media Channel Taxonomy **Status**: Draft Specification **Version**: 1.0.0-draft **Last Updated**: 2026-01-23 本仕様は広告メディアチャンネルの標準タクソノミーを定義します。メディアプランニングツール、アドテクプラットフォーム、AI ベースの広告エージェント間での相互運用を目的としています。 ## 背景 広告業界には標準化されたチャンネルタクソノミーがありません。IAB Tech Lab はコンテンツ・オーディエンス・広告商品向けのタクソノミーを提供していますが、メディアチャンネルに相当するものが存在しないため次の課題が生じます。 * プラットフォーム間でチャンネル名称が不一致 * クロスチャネルのキャンペーンデータ集約が困難 * チャンネル/フォーマット/購入モデルの混同 * 自動化されたメディアプランニングでの摩擦 本仕様は以下を定義することで課題を解消します。 1. **Media Channels** - バイヤーが予算を配分する単位(計画上の抽象化) 2. **Property Types** - 所有が検証可能なアドレス指定インベントリ面 3. **チャンネル・プロパティタイプ・フォーマットの明確な区別** ## 設計思想 **チャンネルは広告がどこで描画されるかではなく、バイヤーがどのように計画・予算配分するかを表します。** これは意図的な選択です。バイヤーは「web に 50 万ドル」ではなく「display に 50 万ドル」「OLV に 30 万ドル」と表現します。チャンネルタクソノミーはこの実態を反映しています。 ### 主要原則 1. **プランニング指向**: エージェンシーのプラン構成に沿う 2. **軽量タグ**: 正確な分類を強制するのではなく意図を表明するためのもの 3. **マルチチャネル対応**: 1 つのプロパティが複数チャンネルにまたがる場合がある(例: YouTube = `olv`, `social`, `ctv`) 4. **パブリッシャー宣言**: 在庫が対応するチャンネルをパブリッシャーが示します 5. **エージェント調整**: 営業エージェントがバイヤー意図とパブリッシャー主張を突き合わせる ### チャンネル vs 技術的な基盤 チャンネルは「web」「モバイルアプリ」のような基盤レベルの概念を意図的に避けています。 * バイヤーはその粒度で計画しません * 多くのデジタル在庫が両方に該当してしまいます * 同じプレースメントを異なる方法で購入できます 代わりに、display/OLV/social/search/CTV など **購入コンテキスト** を表します。 ## 用語 本文中の "MUST" などの語は [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119) に従って解釈します。 ## 定義 ### Media Channel **メディアチャンネル** はバイヤーが予算をどう配分するかを表す計画上の抽象化です。オーディエンス・環境・購入方法に関する前提を内包します。 チャンネルは **購入コンテキスト** で定義され、以下では定義されません。 * 技術基盤(web vs app) * 広告の見た目(それは **フォーマット**) * 配信技術の詳細 ### Property Type **プロパティタイプ** は次を満たすアドレス指定インベントリ面を指します。 * 所有権が検証できる(例: `adagents.json`) * プログラマティックに配信できます * ドメインやアプリ ID、デバイス ID などの識別子が存在します プロパティタイプは技術的な分類でありチャンネルとは別です。同じプロパティが複数チャンネルにまたがる場合があります。 ### Format Category **フォーマットカテゴリ** は広告のレンダリング方法(クリエイティブユニット種別)を表します。例: `video`, `audio`, `display`, `native`。フォーマットはチャンネルとは直交します。 ## チャンネルとプロパティタイプの理解 | Concept | 回答する問い | 判断基準 | 例 | | ----------------- | ---------------- | ------------- | -------------- | | **Channel** | バイヤーはどう予算を配分するか | プランニングコンテキスト | `retail_media` | | **Property Type** | 広告が技術的にどこで配信されるか | アドレス指定インベントリ面 | `website` | ### 重要性 リテールメディアを考えると、バイヤーが「retail media」に予算を割くとき、以下を購入しています。 1. 小売サイト上のスポンサード商品 2. 小売アプリ内のディスプレイ広告 3. 小売データを使ったオフサイト広告 4. 店舗内デジタルサイネージ すべて `retail_media` チャンネルですが、プロパティタイプは `website` / `mobile_app` / `dooh` など異なります。 ### マルチチャネルなプロパティ 1 つのプロパティが複数チャンネルに属する場合があります。 | Property | Channels | Reasoning | | -------- | ----------------------------------- | ---------------------- | | YouTube | `olv`, `social`, `ctv` | 同一プラットフォームで異なる購入コンテキスト | | ESPN App | `olv`, `display` | 動画とディスプレイ在庫を保有 | | Amazon | `retail_media`, `search`, `display` | 複数の広告商品を提供 | ### 使い分け **channel を使う場面:** * 予算配分カテゴリでプロダクトを絞るとき * メディアミックスをレポートするとき * プロダクト探索でバイヤー意図を表すとき **property\_type を使う場面:** * `adagents.json` で在庫所有を検証するとき * 配信の技術的判断を行うとき * プラットフォーム固有のターゲティングを行うとき ## Media Channels ### Channel Enum The following 19 channels MUST be supported. Implementations MAY extend with additional channels using the `ext` field. | Channel | Description | | ------------------- | -------------------------------------- | | `display` | デジタルディスプレイ広告(バナー、ネイティブ、リッチメディア) | | `olv` | CTV 以外のオンライン動画(プリロール、アウトストリーム、インアプリ動画) | | `social` | ソーシャルメディアプラットフォーム | | `search` | 検索エンジン広告 | | `ctv` | テレビ画面での接続型/ストリーミング TV | | `linear_tv` | 従来の地上波/ケーブルテレビ | | `radio` | 従来の AM/FM ラジオ放送 | | `streaming_audio` | デジタルオーディオストリーミングサービス | | `podcast` | ポッドキャスト広告 | | `dooh` | デジタル屋外スクリーン | | `ooh` | 伝統的な屋外(ビルボード、トランジット) | | `print` | 新聞・雑誌などの紙媒体 | | `cinema` | 映画館広告 | | `email` | メール広告・ニュースレター | | `gaming` | インゲーム広告 | | `retail_media` | リテールメディアネットワークやコマースマーケットプレイス | | `influencer` | クリエイター/インフルエンサーとの提携 | | `affiliate` | アフィリエイトネットワーク、成果報酬型提携 | | `product_placement` | プロダクトプレースメントやブランドコンテンツ | ### Channel Definitions #### `display` Digital display advertising including banners, native units, and rich media across web and app environments. **Includes**: * Display banners on websites * Display ads in mobile apps * Native content units * Rich media ads * Interstitials (non-video) **Excludes**: * Video ads (use `olv` or `ctv`) * Social platform ads (use `social`) * Search results (use `search`) * Retail media placements (use `retail_media`) **Typical Formats**: display, native, rich\_media #### `olv` Online video advertising delivered outside of CTV/television environments. **Includes**: * Pre-roll, mid-roll, post-roll on websites * In-app video ads * Outstream/in-feed video * YouTube video ads (when not on TV screens) * Video on news sites, sports sites, etc. **Excludes**: * CTV/streaming on TV screens (use `ctv`) * Social platform video (use `social`) * Linear TV (use `linear_tv`) * Retail media video (use `retail_media`) **Typical Formats**: video **Note**: OLV (Online Video) is a distinct planning bucket from CTV. Agencies commonly budget separately for "OLV" and "CTV" campaigns. #### `social` Social media platform advertising, regardless of technical delivery surface. **Includes**: * Meta platforms (Facebook, Instagram, Threads) * X (Twitter) * TikTok * LinkedIn * Snapchat * Pinterest * Reddit * YouTube (when bought through social-style targeting) **Excludes**: * Influencer content on social platforms (use `influencer`) * Video ads bought for reach, not social engagement (consider `olv`) **Typical Formats**: display, video, native **Note**: Social is defined by BUYING CONTEXT (social platform ad tools) and audience engagement model. #### `search` Search engine results pages and search advertising networks. **Includes**: * Google Search ads * Microsoft Bing ads * DuckDuckGo ads * App store search ads * Shopping/product listing ads in search context **Excludes**: * Display ads on search engine properties (use `display`) * Retail media search (use `retail_media`) **Typical Formats**: text, display (shopping) #### `ctv` Connected TV advertising delivered through streaming applications on television screens. **Includes**: * Streaming service apps (Netflix, Hulu, Max, etc.) * Virtual MVPDs (YouTube TV, Sling, etc.) * Free ad-supported streaming TV (FAST) * Smart TV native apps * Gaming console streaming apps **Excludes**: * Linear broadcast/cable (use `linear_tv`) * Video on phones/tablets/desktops (use `olv`) * YouTube on mobile (use `olv` or `social`) **Typical Formats**: video #### `linear_tv` Traditional broadcast and cable television advertising. **Includes**: * National broadcast networks (ABC, CBS, NBC, Fox) * Cable networks (ESPN, CNN, HGTV) * Local broadcast stations * Addressable linear TV **Excludes**: * Streaming on TV screens (use `ctv`) * TV Everywhere apps on mobile (use `olv`) **Typical Formats**: video #### `radio` Traditional AM/FM radio broadcast advertising. **Includes**: * Terrestrial radio stations * Satellite radio (SiriusXM terrestrial simulcast) * HD Radio **Excludes**: * Streaming audio services (use `streaming_audio`) * Podcasts (use `podcast`) **Typical Formats**: audio #### `streaming_audio` Digital audio streaming services. **Includes**: * Music streaming (Spotify, Apple Music, Amazon Music, Pandora) * Audio content platforms * Digital radio streams (iHeartRadio digital, TuneIn) **Excludes**: * Podcasts (use `podcast`) * Terrestrial radio simulcast (use `radio`) **Typical Formats**: audio, display (companion) #### `podcast` Podcast advertising, including host-read and dynamically inserted ads. **Includes**: * Host-read sponsorships * Dynamically inserted audio ads * Podcast network advertising **Excludes**: * Music streaming (use `streaming_audio`) * Video podcasts on YouTube (use `olv` or `social`) **Typical Formats**: audio #### `dooh` Digital out-of-home advertising on electronic screens in public spaces. **Includes**: * Digital billboards * Transit screens (subway, bus shelters, airports) * Retail/mall digital displays * Gas station screens * Elevator screens * Stadium/venue digital signage **Excludes**: * Static billboards (use `ooh`) * Cinema screens (use `cinema`) **Typical Formats**: display, video #### `ooh` Classic out-of-home advertising on physical (non-digital) surfaces. **Includes**: * Static billboards * Transit posters * Street furniture * Wallscapes * Wild postings **Excludes**: * Digital screens (use `dooh`) **Typical Formats**: display (static) #### `print` Newspaper, magazine, and other print publication advertising. **Includes**: * Newspaper display ads * Magazine display ads * Newspaper/magazine inserts * Trade publication advertising **Excludes**: * Digital versions of publications (use `display`) * Direct mail **Typical Formats**: display (static) #### `cinema` Movie theater advertising. **Includes**: * Pre-show advertising * On-screen trailers and ads * Lobby displays * Concession advertising **Excludes**: * Streaming movie services (use `ctv`) **Typical Formats**: video, display #### `email` Email advertising and sponsored newsletter content. **Includes**: * Sponsored email newsletters * Email display advertising * Dedicated email sends **Excludes**: * Transactional email * CRM/owned email marketing **Typical Formats**: display, native #### `gaming` In-game advertising across gaming platforms. **Includes**: * In-game display ads * Rewarded video ads * Playable ads * Advergames * Esports sponsorships * Gaming influencer integrations **Excludes**: * Ads in non-gaming apps (use `display` or `olv`) * Gaming content on streaming platforms (use `ctv` or `olv`) **Typical Formats**: display, video, native #### `retail_media` Retail media networks and commerce marketplace advertising. **Includes**: * Retail media networks (Amazon Ads, Walmart Connect, Target Roundel) * Grocery and delivery platforms (Instacart, DoorDash, Uber) * Travel marketplaces (Expedia, Booking.com, Kayak) * Financial services marketplaces * Sponsored product listings * On-site display and video on commerce platforms * Off-site ads using retailer first-party data **Excludes**: * General display ads (use `display`) * Social commerce (use `social`) * Search ads on non-commerce platforms (use `search`) **Typical Formats**: native, display, video **Note**: Retail media is distinguished by its transactional context and closed-loop attribution capabilities. #### `influencer` Creator and influencer marketing partnerships. **Includes**: * Sponsored content creation * Brand ambassador programs * Affiliate creator partnerships * User-generated content campaigns **Excludes**: * Ads placed on creator content by platforms (use `social`) * Podcast host reads (use `podcast`) **Typical Formats**: video, native, display **Note**: `influencer` describes the BUYING MODEL (creator partnership) rather than where content appears. #### `affiliate` Affiliate networks, comparison sites, and performance-based publisher partnerships. **Includes**: * Affiliate networks (CJ, Rakuten, Impact, Awin, ShareASale) * Comparison shopping engines (NerdWallet, Bankrate, The Points Guy) * Review and recommendation sites * Coupon and deal sites (RetailMeNot, Honey) * Content commerce (editorial with affiliate links) * Lead generation sites * Cashback and loyalty programs **Excludes**: * Standard display ads on affiliate sites (use `display`) * Influencer partnerships (use `influencer`) * Retail media sponsored products (use `retail_media`) **Typical Formats**: native, display, text **Note**: `affiliate` describes the BUYING MODEL (performance-based, CPA/CPC/rev-share) rather than where content appears. #### `product_placement` Product placement, branded content, and sponsorship integrations. **Includes**: * Traditional product placement (film, TV) * Virtual product placement * Branded entertainment * Event sponsorships * Naming rights * Branded content series **Excludes**: * Influencer partnerships (use `influencer`) * Standard ad placements at sponsored events (use appropriate channel) **Typical Formats**: native, video #### `sponsored_intelligence` Sponsored Intelligence — 逆転したデータフローを通じた、AI アシスタント、AI 検索結果、生成 AI 体験内の広告。 **Includes**: * AI アシスタントとチャットボットのスポンサードレスポンス * AI 搭載検索エンジンのスポンサード結果 * 生成 AI 体験内のディスプレイと動画広告 * SI Chat Protocol 経由のブランド体験ハンドオフ **Excludes**: * 標準的な検索エンジン広告(`search` を使う) * AI 企業ウェブサイトのディスプレイ広告(`display` を使う) * ソーシャルプラットフォームの AI 機能(`social` を使う) **Typical Formats**: native, display, video **Note**: `sponsored_intelligence` は、従来のウェブやアプリのサーフェスではなく、ユーザーが AI システムと対話する購入コンテキストを記述します。このチャネルは逆転したデータフローを使います — バイヤーがデータを中に押し込み、プラットフォームが完全なコンテキストで広告を生成します。 ## Property Types Property types describe addressable inventory surfaces with verifiable ownership. They are used in `adagents.json` for authorization validation. ### Property Type Enum | Property Type | Description | Example Channels | | ----------------- | --------------------------------------- | ------------------------------------ | | `website` | Web properties accessible via browser | `display`, `olv` | | `mobile_app` | Native mobile applications | `display`, `olv`, `social`, `gaming` | | `ctv_app` | Connected TV applications | `ctv` | | `desktop_app` | Desktop applications (Electron, native) | `streaming_audio`, `gaming` | | `dooh` | Digital out-of-home screen networks | `dooh` | | `podcast` | Podcast feeds and episodes | `podcast` | | `radio` | Radio station properties | `radio` | | `streaming_audio` | Digital audio streaming properties | `streaming_audio` | ### Channels Without Property Types The following channels do not have corresponding property types because they lack addressable, verifiable inventory surfaces in the traditional sense: * `linear_tv` - Broadcast/cable inventory is managed through different authorization mechanisms * `ooh` - Physical inventory lacks digital identifiers * `print` - Physical publication inventory * `cinema` - Theater inventory management systems * `email` - Email list ownership differs from property authorization * `influencer` - Creator relationships rather than property ownership * `affiliate` - Performance-based partnerships, placements appear on partner websites * `product_placement` - Content/event relationships rather than properties * `retail_media` - Platform-managed inventory within retail ecosystems * `search` - Platform-managed inventory * `social` - Platform-managed inventory ## Relationship Between Concepts ``` ┌─────────────────────────────────────────────────────────────────┐ │ MEDIA CHANNEL │ │ How buyers allocate budget (planning abstraction) │ │ │ │ display, olv, social, search, ctv, linear_tv, podcast, │ │ streaming_audio, radio, dooh, ooh, print, cinema, email, │ │ gaming, retail_media, influencer, affiliate, product_placement│ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ PROPERTY TYPE │ │ Addressable inventory with verifiable ownership (technical) │ │ │ │ website, mobile_app, ctv_app, desktop_app, dooh, │ │ podcast, radio, streaming_audio │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ FORMAT CATEGORY │ │ How the ad renders (orthogonal to channel) │ │ │ │ audio, video, display, native, rich_media, text │ └─────────────────────────────────────────────────────────────────┘ ``` ## Implementation ### JSON Schema Channels are defined in the AdCP schema at: ``` /schemas/v1/enums/channels.json ``` ```json theme={null} { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "/schemas/enums/channels.json", "title": "Media Channel", "description": "Standardized advertising media channels describing how buyers allocate budget", "type": "string", "enum": [ "display", "olv", "social", "search", "ctv", "linear_tv", "radio", "streaming_audio", "podcast", "dooh", "ooh", "print", "cinema", "email", "gaming", "retail_media", "influencer", "affiliate", "product_placement" ] } ``` ### Usage in Product Discovery When filtering products by channel: ```json theme={null} { "filters": { "channels": ["ctv", "olv", "streaming_audio"] } } ``` ### Usage in Property Definitions Properties in `adagents.json` declare which channels they support: ```json theme={null} { "properties": [ { "property_id": "youtube_app", "property_type": "ctv_app", "name": "YouTube CTV", "supported_channels": ["ctv", "olv", "social"], "identifiers": [ {"type": "roku_store_id", "value": "12345"} ] } ] } ``` ### Extensibility Implementations MAY support additional channels beyond this specification using the `ext` field pattern: ```json theme={null} { "channel": "display", "ext": { "sub_channel": "native_content" } } ``` ### Usage in Product Definitions プロダクトは、どのチャネルとして販売されるかを宣言します。これは通常プロパティから継承しますが、より具体的である場合があります。 ```json theme={null} { "product_id": "youtube_ctv_premium", "name": "YouTube CTV Premium", "channels": ["ctv"], "publisher_properties": [ { "publisher_domain": "youtube.com", "selection_type": "by_tag", "property_tags": ["ctv_apps"] } ] } ``` YouTube のプロパティが `["olv", "social", "ctv"]` をサポートしていても、このプロダクトは特に CTV インベントリとして販売されます。 ## Versioning This taxonomy follows [Semantic Versioning](https://semver.org/): * **MAJOR**: Removing channels, changing channel semantics * **MINOR**: Adding new channels (append-only) * **PATCH**: Clarifying descriptions, fixing typos Adding new channels is a MINOR version change and MUST NOT break existing implementations. ## Migration from Legacy Channel Values Prior to this taxonomy, AdCP used a different set of channel values. The following table maps legacy values to the new taxonomy: | Legacy Value | New Channel(s) | Notes | | ---------------- | ------------------------------------- | ----------------------------------------- | | `display` | `display` | No change, but now excludes video | | `video` | `olv`, `ctv` | Split by viewing environment | | `audio` | `streaming_audio`, `podcast`, `radio` | Split by audio type | | `native` | `display` | Native is a format, not a channel | | `web` | `display`, `olv` | Web is a substrate, not a planning bucket | | `mobile_app` | `display`, `olv`, `gaming` | App is a substrate, not a planning bucket | | `dooh` | `dooh` | No change | | `ctv` | `ctv` | No change | | `podcast` | `podcast` | No change | | `retail` | `retail_media` | Renamed | | `commerce_media` | `retail_media` | Renamed | | `social` | `social` | No change | | `sponsorship` | `product_placement` | Renamed and refined | ### Migration Guidance 1. **Identify the planning context**: Determine HOW the buyer allocates budget, not where ads technically render. 2. **Split video by environment**: If video was a single category, split into `olv` (desktop/mobile) and `ctv` (TV screens). 3. **Remove substrate channels**: If you had `web` or `mobile_app` as channels, map to planning-oriented channels (`display`, `olv`) based on buying context. 4. **Update filters**: When filtering products, use the new channel values. ## Edge Cases and Ambiguities ### YouTube Classification YouTube spans multiple channels depending on context: | Scenario | Channel | Reasoning | | --------------------------------- | ----------------- | -------------------------- | | YouTube video ad on phone/desktop | `olv` or `social` | Depends on buying approach | | YouTube video ad on CTV | `ctv` | TV screen environment | | YouTube Shorts | `social` | Short-form social context | | YouTube Music | `streaming_audio` | Audio streaming context | ### Retail Media Complexity Retail media may eventually warrant sub-channels: | Scenario | Current | Potential Future | | ---------------------------- | -------------- | ---------------------- | | Sponsored products on Amazon | `retail_media` | `retail_media_search` | | Display on retailer site | `retail_media` | `retail_media_display` | | Off-site using retailer data | `retail_media` | `retail_media_offsite` | For now, use `retail_media` with format filters to distinguish. ### Gaming vs Display/OLV | Scenario | Channel | Reasoning | | -------------------------------------------- | --------- | ---------------------------- | | Rewarded video in mobile game via Unity Ads | `gaming` | Gaming-specific ad network | | Banner in casual game via AdMob general pool | `display` | Standard mobile programmatic | | Esports tournament sponsorship | `gaming` | Gaming audience context | ### Influencer vs Social | Scenario | Channel | Reasoning | | -------------------------------------------- | ------------ | ------------------- | | Buying promoted posts through Instagram Ads | `social` | Platform ad tools | | Contracting an influencer directly | `influencer` | Creator partnership | | Platform-inserted ads around creator content | `social` | Platform ad tools | ## Future Considerations The following channels may be added in future versions based on market evolution: * `messaging` - WhatsApp Business, Telegram ads * `xr` - VR/AR advertising * `ai_agents` - AI assistant and chatbot advertising ## References * [IAB Tech Lab Taxonomies](https://github.com/InteractiveAdvertisingBureau/Taxonomies) - Content, Audience, Ad Product * [OpenRTB/AdCOM](https://github.com/InteractiveAdvertisingBureau/AdCOM) - Placement types and media objects * [Planmatic Media Plan Schema](https://github.com/planmatic/mediaplanschema) - Media planning data structures * [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119) - Requirement level keywords ## Changelog ### 1.1.0-draft (2026-03-13) * `ai_media` を `sponsored_intelligence` にリネーム — Sponsored Intelligence は AI プラットフォーム広告(AI アシスタント、AI 検索、生成 AI 体験)の総称 * 20 チャネル定義 ### 1.0.0-draft (2026-01-23) * Initial draft specification * 19 channels defined (planning-oriented approach) * 8 property types defined * Clear distinction between channel (planning abstraction), property\_type (technical surface), and format * Multi-channel support for properties * Migration guide from legacy values # Release Notes Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/release-notes 累積的な変更詳細、移行ガイダンス、バージョンごとの採用ノートを備えた、AdCP の権威あるバージョンごとのリリース記録。 AdCP の権威あるバージョンごとのリリース記録。累積的な変更詳細、移行ガイダンス、バージョンごとの採用ノート付き。3.1 マイナーリリースの厳選された採用者向け概要については [What's New in AdCP 3.1](/docs/reference/whats-new-in-3-1) を参照。焦点を絞ったアップグレードチェックリストについては [Migrating from 3.0 to 3.1](/docs/reference/migration/3-0-to-3-1) を参照。公開されたすべてのバージョンの一覧ステータスについては [Versions & Compatibility](/docs/reference/versions) を参照。ソースレベルの変更履歴については [CHANGELOG.md](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md) を参照。バージョン安定性、スキーマ変更スコープ、3.x 保証については [Versioning & Governance](/docs/reference/versioning) を参照。v2 の EOL については [v2 サンセットページ](/docs/reference/v2-sunset) を参照。 *** ## Version 3.1.0 **Status:** 一般提供 — マイナーリリース。安定したワイヤーネゴシエーション値は `"3.1"`。公開された 3.0.x ラインは既存のインテグレーションに対してサポートされ続けます。新しい 3.1 インテグレーションは `"3.1"` にピン留めし、3.1 専用フィールドを送信する前に `supported_versions` を読むべきです。 完全な変更リストの前の採用指向のフレーミングについては [What's New in AdCP 3.1](/docs/reference/whats-new-in-3-1) から始めてください。役割ベースのアップグレードチェックリストには [Migrating from 3.0 to 3.1](/docs/reference/migration/3-0-to-3-1) を使ってください。 **ヘッドライン: 実際のエージェント運用のための本番強化。** 3.1 は、分散 `brand.json`、リリース精度のバージョンネゴシエーション、指定ブランドレスポンス署名、正準クリエイティブフォーマット、ベンダー証明の最適化、ホールセールフィードのミラーリング、依存性影響の可観測性、プロポーザルライフサイクルの精度、バージョンごとの検証バッジを追加します。ブランドは今や、コーポレートハウスがポートフォリオポインター経由で所有権を宣言する一方で、自身のドメインで**自身の**正準ドキュメントを公開できます。階層は 1 レベルの深さのままで — ハウスのみが所有権を宣言 — リーフとハウス間の信頼は相互アサーション(両側が相互応答)で解決します。アイデンティティ属性(logos、colors、tone、tagline)はリーフの TLS のみで信頼されます。関係信頼(ガバナンス伝播、課金可能な包含)は相互エントリでゲートされます。 3.0 に対して追加的 — 既存のすべての brand.json パブリッシャーは変更なしに検証され続けます。 3.1 の詳細な変更リストは [CHANGELOG.md](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md) と [What's New in AdCP 3.1](/docs/reference/whats-new-in-3-1) を参照。焦点を絞ったアップグレードチェックリストには [Migrating from 3.0 to 3.1](/docs/reference/migration/3-0-to-3-1) を使ってください。3.1 は、分散 `brand.json` と `brand_refs[]`、リリース精度のバージョンネゴシエーション、ブランド指定タスクレスポンス署名、正準フォーマット、ベンダー証明の最適化目標(`kind: "vendor_metric"`)、ホールセールシグナルフィードのミラーリング、`reach_window` と `viewability.viewed_seconds` の配信メトリクス、ワイヤー適合性の明確化(すべてのタスクでの冪等性、フラットな MCP エンベロープ許容、前方互換のエラーコードデコード)、片側の hosted audio/video 継続時間範囲を追加します。完全な採用者向けの概要と各機能の詳細なアダプターアクションについては、上記の [What's New in AdCP 3.1](/docs/reference/whats-new-in-3-1) を参照。 *** ## Version 3.0.6 **Status:** パッチリリース — 3.0 準拠エージェントに対して安定サーフェスの no-op **3.0.6 は `GOVERNANCE_DENIED` のワイヤー配置ルールをエラーコード自体から発見可能にし**、`ctx_metadata` キーワードをアダプター内部のラウンドトリップキーとして予約し、呼び出しエージェント側での `issues[]` 回復に関する SKILL.md ガイダンスを拡張し、仕様準拠のアダプターを拒否していた 2 つのストーリーボードフィクスチャバグを修正します。任意の 3.0 エージェントのワイヤー形式は変更されません。詳細は [CHANGELOG.md § 3.0.6](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md) を参照。 *** ## Version 3.0.5 **Status:** パッチリリース — 3.0 準拠エージェントに対して安定サーフェスの no-op **3.0.5 は 3.0 での `brand_json_url` 採用をアンブロックし**、任意のストーリーボードオーサリングのアフォーダンスを出荷し、仕様準拠のエージェントを拒否していたブランド権利ストーリーボードのキャプチャパスを修正します。新しい任意サーフェスを主張しない任意の 3.0 エージェントのワイヤー形式は変更されません。主な変更: `get_adcp_capabilities` の `identity` ブロックが `additionalProperties: true` になり(`brand_json_url` の 3.0 採用をアンブロック)、任意の `default_agent` ストーリーボードフィールドが追加されます。詳細は [CHANGELOG.md § 3.0.5](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#305) を参照。 *** ## Version 3.0.4 **Status:** パッチリリース — 3.0 準拠エージェントに対して安定サーフェスの no-op **3.0.4 は 3 つ目の 3.0.x パッチです。** main からの 3 つの追加的なチェリーピック、すべてメンテナンスライン用に手作業で適応: `manifest.json` + 構造化 `enumMetadata` アーティファクト(SDK が仕様を手作業で書き写すのを止める)、`core/error.json` の規範的な `issues[]` 配列、`AUTH_REQUIRED` のリトライストームリスクを指摘する文章のみの強化。ワイヤー形式は変更されません。`AUTH_REQUIRED` は 3.0.x では単一コードのままですが(3.1 で `AUTH_MISSING` / `AUTH_INVALID` に分割)、拒否された認証情報での自動リトライを抑制する文章が追加されました。詳細は [CHANGELOG.md § 3.0.4](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#304) を参照。 *** ## Version 3.0.3 **Status:** パッチリリース — 追加的なストーリーボードスキーマフィールド、3.0 準拠エージェントに対して安定サーフェスの no-op **3.0.3 は `provides_state_for` ストーリーボードフィールドを出荷し**、同じフェーズに 2 つの交換可能なステートフルステップがあるときに適合性スイートがカスケードスキップを救済できるようにします。加えて、公開されたスキーマがすでに除外していた値を発するチャネルドキュメントの `url_type` enum のドキュメントのみの修正(`tracker` → `tracker_pixel`)。ワイヤー形式は変更されません。詳細は [CHANGELOG.md § 3.0.3](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#303) を参照。 *** ## Version 3.0.2 **Status:** パッチリリース — 追加的なストーリーボードチェック種別 + 正準アセットユニオンスキーマ **3.0.2 は新しいストーリーボードチェック種別を出荷し**、`@adcp/sdk` のドリフト検証者の静的解析ギャップを閉じます。加えて、共有アセットバリアント `oneOf` ユニオンを独自のスキーマファイル(`core/assets/asset-union.json`)に抽出し、コード生成ツール(特に `json-schema-to-typescript`)が番号付き重複型を発するのを止めます。ワイヤー形式は no-op です。詳細は [CHANGELOG.md § 3.0.2](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#302) を参照。 *** ## Version 3.0.1 **Status:** パッチリリース — 3.0 準拠エージェントに対して安定サーフェスの no-op **3.0.1 はメンテナンスリリースです。** 正準 tarball を通じてプロトコルスキルバンドルを出荷し、3.0.0 で未指定だったいくつかの規範的条項を形式化し、実験的サーフェス(ガバナンス、TMP)と適合性ハーネスに小さな追加フィールドを追加します。安定したワイヤーサーフェスは変更されません。主な変更: 正準 skills tarball(3.0.0 tarball の cosign 署名後に skills/ がプロトコルルートに移動したため再カット)、`acquire_rights` リクエスト検証、`get_signals` ページネーション優先順位、URL 正規化、v3 エンベロープ整合性、ガバナンス `mode` フィールド、TMP `seller_agent`。詳細は [CHANGELOG.md § 3.0.1](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#301) を参照。 *** ## Version 3.0.0 **Status:** 一般提供 | [AdCP 3.0 overview](/docs/reference/whats-new-in-v3) **AdCP 3.0 はエージェント間の広告購入をリトライ安全かつ監査可能にし、AdCP Verified エージェント向けに任意のエンドツーエンドリクエスト署名を備えます。** 4 つの信頼プリミティブが変更トラフィックを運びます — 3 つは 3.0 でベースライン必須(リクエスト側の冪等性、Webhook 上の RFC 9421 プロファイル、署名付き JWS ガバナンス)で、RFC 9421 リクエスト署名はエージェントが AdCP Verified を主張しない限り任意です。コンプライアンスランナーがエージェントがそれぞれを正しく行うことを証明します。ストーリーボードがバーとして `/compliance/{version}/` のプロトコルに移動します。AdCP Verified は、エージェントが合格したランナー出力を公開したという自己証明のスタンプです。3.0 はまた、放送 TV を一級のチャネルとして持ち込み、ガバナンスを任意の購入タイプに一般化し、オブジェクトの存在が数十のブールフラグを置き換えるよう機能モデルを簡素化します。 3.0 の完全な What's New 一覧、破壊的変更、移行ノートについては [AdCP 3.0 概要](/docs/reference/whats-new-in-v3) と [CHANGELOG.md § 3.0.0](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#300) を参照。ハイライト: 4 つの暗号プリミティブ(リクエスト/Webhook 冪等性、RFC 9421 署名、署名付き JWS ガバナンス)、専門分野とコンプライアンスストーリーボードと AdCP Verified、放送 TV サポート、ガバナンスの一般化と規制不変条件(GDPR 22条 / EU AI Act 附属書 III)、機能モデルの簡素化、コレクションリスト、構造化測定条件、統合ベンダー価格、リクエストごとのバージョン宣言、オフラインレポート配信、エラーコードのクリーンアップ(`REFERENCE_NOT_FOUND` への統合)。 ### 次のステップ * **v2 からアップグレード?** [移行ガイド](/docs/reference/migration) から始めてください。 * **プレリリースからアップグレード?** [プレリリースアップグレードノート](/docs/reference/migration/prerelease-upgrades) へ。 * **AdCP は初めて?** [3.0 概要](/docs/reference/whats-new-in-v3) を読んでから [始める](/docs/quickstart)。 *** ## Version 3.0.0-rc.3 **Status:** リリース候補(3.0.0 に置き換えられました) | [AdCP 3.0 overview](/docs/reference/whats-new-in-v3) ### What's New 1. **Trusted Match Protocol (TMP)** — AdCP のリアルタイム実行層。9 スキーマ、12 ドキュメントページ、プロダクトでのプロバイダー探索、コンテンツ解決の型付きアーティファクト、軽量なコンテキストマッチング。AXE を非推奨に。 2. **オーダーライフサイクル管理** — オーダー作成時の `confirmed_at`、`canceled_by` 帰属付きのメディアバイとパッケージレベルでのキャンセル、パッケージごとの `creative_deadline`、状態認識エージェント向けの `valid_actions`、楽観的並行制御の `revision`、リビジョン監査証跡の `include_history`。7 つの新エラーコード。 3. **ガバナンスの簡素化** — `check_governance` から `binding` フィールドを削除(判別フィールドから推論)、`sync_plans` から `mode` を削除、`escalated` ステータスを削除。3 つの終端ステータス: `approved`、`denied`、`conditions`。 4. **セラー割り当て ID** — `buyer_ref`、`buyer_campaign_ref`、`campaign_ref` を削除。セラー割り当ての `media_buy_id` と `package_id` が正準。すべての変更リクエストで `idempotency_key`。不透明な `governance_context` 文字列が構造化スキーマを置き換える。 5. **プロポーザルライフサイクル** — draft/committed プロポーザルステータス、refine アクションによる確定、インサーションオーダー署名、`create_media_buy` での有効期限強制。 6. **オーディエンスバイアスガバナンス** — 公平性検証のための構造化オーディエンスデータ。オーディエンスセレクター、制約、ポリシーカテゴリ、制限属性(GDPR 第9条)の新スキーマ。 7. **ストリーミングとオーディオの配信メトリクス** — `completed_views`(`video_completions` からリネーム)、`reach`、`reach_unit`、`frequency`。 8. **可用性フォーキャスト** — `ForecastPoint` の `budget` が任意に。新しい `availability` フォーキャストレンジユニット。 9. **広告主業種タクソノミー** — ブランドマニフェストと `create_media_buy` の 2 レベルドット記法カテゴリ。 10. **コンテンツ標準** — 購入前の可視性のための `get_adcp_capabilities` の `content_standards`。`get_media_buy_artifacts` から `sampling` を削除。 11. **イベントソースのヘルス** — イベントソースの任意の `health`、プロダクトの `measurement_readiness`。 12. **コレクション/インストールメント拡張** — `special` と `limited_series` フィールド、インストールメント期限、印刷対応クリエイティブフォーマット。 13. **スコープされた adagents.json 認可** — 委任タイプ、プレースメントガバナンス、署名鍵、`authorized_agents` の国と時間ウィンドウ制約。 ### Breaking Changes | Change | rc.2 | rc.3 | | -------------------------- | ----------------------------------------------- | ----------------------------------------------------------------- | | バイヤー参照 | `buyer_ref`、`buyer_campaign_ref`、`campaign_ref` | 削除 — セラー割り当ての `media_buy_id` と `package_id` が正準 | | 冪等性 | 暗黙の重複排除キーとしての `buyer_ref` | すべての変更リクエストで明示的な `idempotency_key` | | ガバナンスコンテキスト | 構造化 `governance-context.json` スキーマ | 署名付き JWS `governance_context` 文字列 | | `check_governance` binding | リクエストの `binding` フィールド | 削除 — 判別フィールドから推論 | | `sync_plans` mode | `mode` フィールド | 削除 — ガバナンスエージェント設定 | | `check_governance` status | `escalated` | 削除 — 非同期タスクライフサイクルを使う | | `get_media_buy_artifacts` | リクエストの `sampling` パラメーター | 削除 — 作成時に設定 | | FormatCategory | `format-category.json` enum、Format の `type` | 削除 — `assets` 配列または `asset_types` フィルターを使う | | 番組/エピソード | `show`、`episode`、`show_id`、`episode_id` | `collection`、`installment`、`collection_id`、`installment_id` にリネーム | *** ## Version 3.0.0-rc.2 **Status:** リリース候補 | [AdCP 3.0 overview](/docs/reference/whats-new-in-v3) ### What's New 1. **ブランドプロトコル権利ライフサイクル** — ブランドライセンスのための `get_rights`、`acquire_rights`、`update_rights` タスク。生成資格情報、クリエイティブ承認 Webhook、取り消し通知、利用レポート。 2. **ブランドマニフェストのビジュアルガイドライン** — `brand.json` の構造化 `visual_guidelines` フィールド: 写真、グラフィックスタイル、シェイプ、アイコノグラフィー、コンポジション、モーション、ロゴ配置、カラーウェイ、タイプスケール、アセットライブラリ、制限。 3. **コレクションとインストールメント** — 永続的なプログラム(ポッドキャスト、TV シリーズ、YouTube チャンネル)を表すプロダクトのコンテンツ次元。 4. **Sponsored Intelligence チャネル** — AI プラットフォーム広告のためにメディアチャネルタクソノミーに `sponsored_intelligence` を追加。 5. **プロパティガバナンス統合** — ガバナンス評価されたプロパティリストでプロダクトをフィルタリングする `get_products` の任意 `property_list` パラメーター。 6. **キャンペーンガバナンスとポリシーレジストリ** — プランレベルのガバナンスのための `sync_plans`、`check_governance`、`report_plan_outcome`、`get_plan_audit_logs`。 7. **アカウントモデルの簡素化** — `account_resolution` 機能を削除。`require_operator_auth` が認証モデルとアカウント参照スタイルの両方を決定。 8. **クリエイティブワークフローのアップグレード** — `build_creative` が `include_preview` によるインラインプレビュー、`target_format_ids` によるマルチフォーマット出力、品質階層をサポート。 9. **クリエイティブライブラリプロトコルの統合** — `list_creatives` と `sync_creatives` がクリエイティブプロトコルに存在するように。 10. **開示の永続性** — 規制開示要件が position と duration に加えて persistence(`continuous`、`initial`、`flexible`)を指定できるように。 11. **プロダクト探索とプランニングのエルゴノミクス** — プロダクト探索に `exclusivity` と `preferred_delivery_types` を追加。 12. **アカウントとサンドボックスの改良** — `sync_accounts` に `payment_terms` を追加、サンドボックス機能がアカウント機能ブロックに移動。 13. **ガバナンスエージェント同期** — ガバナンスエージェントエンドポイントを特定のアカウントに同期する `sync_governance` タスク。 ### Breaking Changes | Change | rc.1 | rc.2 | | -------------------------- | ------------------------------------------------------- | ------------------------------------------------ | | アカウント解決 | `account_resolution` 機能 | 削除 — `require_operator_auth` がアカウントモデルを決定 | | クリエイティブライブラリ操作 | Media Buy 下に文書化 | クリエイティブプロトコルに存在 | | サンドボックス機能 | `media_buy.features.sandbox` | `account.sandbox` | | DOOH フラットレートパラメーター | 判別子なしの `flat_rate.parameters` | パラメーター存在時 `flat_rate.parameters.type: "dooh"` 必須 | | delete\_content\_standards | 文書化タスク | 削除 — `update_content_standards` でアーカイブ | | get\_property\_features | スタンドアロンタスク | 削除 — プロパティリストフィルターと `get_adcp_capabilities` を使う | | ガバナンスエージェント同期 | `sync_accounts` / `list_accounts` の `governance_agents` | 専用 `sync_governance` タスクに移動 | *** ## Version 3.0.0-rc.1 **Status:** リリース候補 | [AdCP 3.0 overview](/docs/reference/whats-new-in-v3) ### What's New rc.1 は 3.0 に向けた大規模なプレリリースで、30 の主要な新機能領域を導入します: キーワードターゲティング、最適化目標の再設計(`optimization_goal` → `optimization_goals` 配列、判別共用体)、リーチ最適化、拡張フリークエンシーキャップ、シグナル価格モデル、次元ブレークダウン、デバイスタイプターゲティング、deliver-to のフラット化、メトリクス最適化機能、ブランドアイデンティティの統合(`brand-manifest.json` 削除、BrandRef 参照)、プロダクトの配信フォーキャスト、バイイングモードによるプロポーザル絞り込み、一級カタログ(`sync_catalogs`、13 カタログタイプ)、新タスク(`get_media_buys`、`get_creative_features`、`sync_audiences`)、バイイングモード、サンドボックスモード、クリエイティブブリーフ型、ジオ近接ターゲティング、セラーの承認を伴う型指定された絞り込み、AI プロベナンスと開示、クリエイティブコンプライアンス、マニフェスト統合、構造化エラー回復(18 標準エラーコード)、シグナルの非アクティベーション、メディアバイの拒否、冪等性。 ### Breaking Changes | Change | beta.3 | rc.1 | | ----------------------- | ----------------------------------- | ---------------------------------------------------------- | | ブランドアイデンティティ | インライン `brand_manifest` オブジェクト | `brand`(BrandRef: `{ domain, brand_id }`)— 実行時に解決 | | プロダクトエクスポージャー推定 | `estimated_exposures`(整数) | `forecast`(DeliveryForecast オブジェクト) | | プロポーザル絞り込み | `get_products` の `proposal_id` | 削除 — 型付き `refine` 配列を伴う `buying_mode: "refine"` を使う | | 最適化目標 | `optimization_goal`(単一) | `optimization_goals`(判別共用体の配列) | | AudienceMember アイデンティティ | uid-type enum の `external_id` | `external_id` が必須のトップレベルフィールド、enum から削除 | | シグナル deliver\_to | ネストされた `deliver_to` | トップレベル `destinations` と `countries` | | シグナル価格 | `pricing: { cpm }` | 構造化 `pricing_options[]` | | クリエイティブアサインメント | `{ creative_id: package_id[] }` マップ | `creative_id`、`package_id`、`weight`、`placement_ids` の型付き配列 | | パッケージカタログ | `catalog`(単一オブジェクト) | `catalogs`(配列) | | buying\_mode | なし | 必須 — 3 モード: `brief`、`wholesale`、`refine` | *** ## Version 3.0.0-beta.3 **Status:** Beta | [AdCP 3.0 overview](/docs/reference/whats-new-in-v3) ### What's New 1. **配信フォーキャスト** — 予算コミット前にキャンペーンパフォーマンスを予測。予算曲線、フォーキャスト方法、デイパートターゲティングウィンドウ、GRP デモグラフィック記法を持つ新しい `DeliveryForecast` 型。 2. **ブランドプロトコル** — `brand.json` によるブランド探索とアイデンティティ解決。4 つのマニフェストバリアント。 3. **アカウント管理** — エージェントがブランドポートフォリオをセラーに宣言する `sync_accounts` タスク(upsert セマンティクス)。2 つの課金モデル。 4. **コマースメディア** — カタログ駆動のプロダクト探索、カタログ駆動パッケージ、カタログアイテムごとの配信レポート、店舗商圏ターゲティング。 5. **クリエイティブ配信レポート** — 配信レスポンスの `by_package` 内のクリエイティブごとのメトリクス内訳。新しい `get_creative_delivery` タスク。 6. **CPA & TIME 価格モデル** — 2 つの新価格モデル。成果ベースキャンペーンの CPA、スポンサーシップベース広告の TIME。 7. **コンバージョントラッキング** — `sync_event_sources` と `log_event` タスクを持つ新しいイベントプロトコル。 8. **公開シグナル定義** — データプロバイダーが、シグナル定義、カテゴリ、ターゲティングスキーマ、値タイプを持つ一級メンバーに。 9. **カーソルベースのページネーション** — すべてのリスト操作がカーソルベースのページネーションに標準化。 10. **クリエイティブフォーマットのアクセシビリティ** — 2 層アクセシビリティモデル。フォーマットレベルの `wcag_level`。 11. **ターゲティング制限とジオ除外** — 年齢、デバイスプラットフォーム、言語ローカライゼーションの機能的制限オーバーレイ。地理的除外フィールド。 12. **型付きアセット要件** — `asset_type` を判別子とする 12 のすべてのアセットタイプの判別共用体スキーマ。 13. **ユニバーサルマクロ** — 54 のすべての標準アドサービングマクロを定義する `universal-macro.json` enum。 14. **ブランドマニフェストの改良** — `voice`、`attributes`、`dos`、`donts` フィールドを持つ構造化トーンオブジェクト。 ### Breaking Changes | Change | beta.2 | beta.3 | | ------------- | ------------------ | ---------------------------- | | ページネーション | `limit`/`offset` | カーソルベースの `pagination` オブジェクト | | ブランドマニフェストトーン | `string \| object` | 構造化フィールドを持つオブジェクトのみ | *** ## Version 3.0.0-beta.2 **Status:** Beta | [Full Changelog](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#300-beta2) | [AdCP 3.0 overview](/docs/reference/whats-new-in-v3) beta.1 の上に構築し、このリリースはアカウントレベルの課金、プロパティターゲティング制御、CTV 技術仕様、Sponsored Intelligence のエージェント駆動 UI レンダリングを追加します。 ### What's New 1. **アカウントとエージェント** — AdCP は Brand(プロダクトが広告される主体)、Account(課金される主体)、Agent(購入を行う主体)を区別するように。メディアバイ、プロダクトクエリ、クリエイティブ操作の新しい `account_id` フィールド。 2. **プロパティターゲティング** — プロダクトが `property_targeting_allowed` を宣言してバイヤーがパブリッシャープロパティのサブセットをターゲットできるように。 3. **Sponsored Intelligence の A2UI** — Sponsored Intelligence セッションが MCP Apps によるエージェント駆動 UI レンダリングをサポート。 4. **CTV & ストリーミング制約** — 動画フォーマットがフレームレート、HDR、GOP 構造、moov アトム位置の技術制約フィールドを得る。 5. **クリエイティブプロトコル探索** — `get_adcp_capabilities` が `supported_protocols` に `"creative"` を含むように。 ### Removed | Removed | Replacement | | -------------------------------- | ----------------------------------------- | | `list_property_features` タスク | `get_adcp_capabilities` | | `list_authorized_properties` タスク | `get_adcp_capabilities` portfolio section | | `adcp-extension.json` スキーマ | `get_adcp_capabilities` タスク | | `assets_required` フォーマットフィールド | `required` ブール付きの `assets` 配列 | | `preview_image` フォーマットフィールド | `format_card` オブジェクト | *** ## Version 3.0.0-beta.1 **ステータス:** Beta | [Full Changelog](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#300-beta1) | [Migration Guide](/docs/reference/migration) **このリリースはベータ版です。** API はテストに耐える状態ですが、正式版 3.0.0 までに破壊的変更が入る可能性があります。早期利用者からのテストとフィードバックを歓迎します。 ### 変更概要 Version 3.0.0 はメディアバイを超え、ガバナンス、ブランドセーフティ、会話型コマースへ領域を拡張する **メジャーリリース** です。詳細な移行手順は [Migration Guide](/docs/reference/migration) を参照してください。 **🎯 主なテーマ:** 1. **メディアチャネル分類** - 5 つのフォーマット起点チャネルから、バイヤーの予算配分を反映した 19 のプランニング指向チャネルへ全面刷新。詳しくは [Media Channel Taxonomy](/docs/reference/media-channel-taxonomy)。 2. **ガバナンスプロトコル** - プロパティリスト、コンテンツスタンダード、ブランドセーフティ評価を、協調的なキャリブレーションワークフローと共に定義。 3. **Sponsored Intelligence プロトコル** - AI アシスタント内でのブランド会話体験。ブランドエージェントエンドポイントを途切れなく呼び出す方法を定義。詳細は [Sponsored Intelligence](/docs/sponsored-intelligence/overview)。 4. **プロトコルレベルの能力ディスカバリー** - `get_adcp_capabilities` タスクがエージェントカード拡張を置き換え、実行時に機能、サポートプロトコル、ジオターゲティングシステムを公開。 5. **クリエイティブ割り当ての重み付け** - 単純な creative ID 配列を、トラフィック配分とプレースメントターゲティングに対応する重み付き割り当てへ置換。 6. **グローバルなジオターゲティング** - 名前付きシステム(Nielsen DMA、UK ITL、Eurostat NUTS2 など)による構造化ターゲティングで国際市場に対応。 ### 破壊的変更の概要 | Change | v2.x | v3.x | | ------------------- | ------------------------ | ---------------------------------- | | Channels | 5 values | 19 planning-oriented values | | Creative assignment | `creative_ids: [...]` | `creative_assignments: [{...}]` | | Metro targeting | `geo_metros: ["501"]` | `geo_metros: [{ system, code }]` | | Postal targeting | `geo_postal_codes` | `geo_postal_areas` with system | | Asset discovery | `assets_required: [...]` | `assets: [{ asset_id, required }]` | 詳細な before/after と移行手順は [Migration Guide](/docs/reference/migration) を参照してください。 ### 新しいプロトコルドメイン #### ガバナンスプロトコル ブランドセーフティと在庫キュレーション: * **プロパティリスト** - `create_property_list`、`get_property_list`、`update_property_list`、`delete_property_list`、`list_property_lists` * **コンテンツスタンダード** - `create_content_standards`、`get_content_standards`、`update_content_standards`、`calibrate_content`、`validate_content_delivery` * **商品フィルタリング** - ガバナンスリストを `get_products` に渡し、コンプライアンス済み在庫を探索 #### Sponsored Intelligence プロトコル 会話型ブランド体験: * **セッション管理** - `si_check_availability`、`si_initiate_session`、`si_send_message`、`si_terminate_session` * **機能ネゴシエーション** - ブランドがモダリティ(音声・動画・アバター)を宣言し、ホストが対応機能を返す * **コマース連携** - 取引を ACP へ途切れなくハンドオフ 詳細は [Sponsored Intelligence Overview](/docs/sponsored-intelligence/overview)。 ### 新機能 * **`get_adcp_capabilities` タスク** - エージェントカード拡張に代わる実行時の能力ディスカバリー * **統一されたアセットディスカバリー** - `required` ブール値付きの `assets` 配列で完全なアセット可視化 * **プロパティリストフィルタリング** - ガバナンスリストを `get_products` に渡し、ブランドセーフな在庫を抽出 ### v3 で削除された項目 | Removed | Replacement | | --------------------------------- | ----------------------------------------- | | `adcp-extension.json` agent card | `get_adcp_capabilities` task | | `list_authorized_properties` task | `get_adcp_capabilities` portfolio section | | `assets_required` in formats | `assets` array with `required` boolean | | `preview_image` in formats | `format_card` object | | `creative_ids` in packages | `creative_assignments` array | | `geo_postal_codes` | `geo_postal_areas` | | `fixed_rate` in pricing | `fixed_price` | | `price_guidance.floor` | `floor_price` (top-level) | ### クイック移行チェックリスト * [ ] チャネル enum を更新([taxonomy guide](/docs/reference/media-channel-taxonomy)) * [ ] `creative_ids` を `creative_assignments` に置き換える * [ ] メトロ/郵便ターゲティングに system 指定を追加 * [ ] `get_adcp_capabilities` タスクを実装 * [ ] フォーマット解析を `assets` 配列に対応させる **[完全な移行ガイドを見る →](/docs/reference/migration)** *** ## Version 2.5.0 **リリース日:** 2025 年 11 月 | [Full Changelog](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#250) ### 変更概要 Version 2.5.0 は **開発体験と API の磨き込み** を目的としたリリースで、型安全性、スキーマ基盤、クリエイティブワークフロー性能を大幅に改善しました。TypeScript/Python のコード生成、厳密なバリデーションセマンティクス、柔軟なスキーマバージョニングにより、本番規模の導入に備えています。 **🎯 主なテーマ:** 1. **型安全性とコード生成** - プロトコル全体に判別子フィールドを追加し、TypeScript/Python の型推論を向上、曖昧な共用体を排除。 2. **クリエイティブ一括プレビュー** - 最大 50 件のクリエイティブを 1 回の API 呼び出しで生成し、直接 HTML 埋め込みも可能。プレビュー時間を 5〜10 倍短縮。 3. **スキーマ基盤** - セマンティックパス(`/schemas/2.5.0/`, `/schemas/v2/`, `/schemas/v2.5/`)でビルド時バージョン管理を実現し、バージョン固定と自動マイナートラッキングを提供。 4. **API 一貫性** - 原子的なレスポンスセマンティクス(success XOR error)と標準化された Webhook ペイロードで曖昧さを排除し、信頼性を向上。 5. **シグナルプロトコルの改善** - 認可に基づくデプロイメント別のアクティベーションキーを返却し、マルチプラットフォームでの適切なシグナル活性化を実現。 6. **テンプレートフォーマット** - `accepts_parameters` により、実行時寸法や尺などを受け付ける動的フォーマットをサポート。 7. **商品ディスカバリーの強化** - 日付範囲、予算制約、国ターゲティング、チャネルフィルタを持つ構造化フィルタで検索精度を向上。 ### 主な改善点 #### 型安全性とコード生成 * **判別子フィールド** を判別共用体全体(配信先、価格、プロパティセレクター、プレビューのリクエスト/レスポンス)に追加 * **原子的レスポンスセマンティクス** - すべてのタスクレスポンスで厳密な success XOR error パターン(`oneOf` 判別子)を採用 * すべての const フィールドに **明示的な型宣言** を付与し、正確な TypeScript リテラル型を生成 * **31 個の新しい enum スキーマ** をインライン定義から抽出し再利用性を向上 #### スキーマ基盤 * **ビルド時バージョニング** - セマンティックなパス(`/schemas/2.5.0/`)、メジャーエイリアス(`/schemas/v2/`)、マイナーエイリアス(`/schemas/v2.5/`)をサポート * **一貫したメディアバイレスポンス** - `create_media_buy` と `update_media_buy` が共に完全な Package オブジェクトを返す * **標準化された Webhook ペイロード** - プロトコルエンベロープをトップレベルに、タスクデータを `result` フィールドに配置 #### 商品ディスカバリー * **構造化フィルタ** - フィルタオブジェクトを個別スキーマ(`product-filters.json`、`creative-filters.json`、`signal-filters.json`)に分離 * **強化されたフィルタ** - 日付範囲(`start_date`、`end_date`)、通貨付き予算範囲、国ターゲティング、チャネルフィルタを追加 * **完全な enum 対応** - フィルタで全 enum 値を制限なく受け付け #### シグナルプロトコル * **アクティベーションキー** - `activate_signal` が認証権限に基づきデプロイメント別のアクティベーションキー(セグメント ID、キー/バリュー)を返却 * **用語の統一** - リクエストとレスポンス全体で「deployments」に統一 #### クリエイティブプロトコル * **バッチプレビュー対応** - `preview_creative` が 1〜50 件のプレビューを 1 リクエストでサポート * **直接 HTML 埋め込み** - iframe なしで表示できる生 HTML をレスポンスに含められます * **シンプルな brand manifest** - 必須フィールドを `name` のみにし、重複した型生成を排除 * **テンプレートフォーマット** - `accepts_parameters` により display\_\[width]x\[height]、video\_\[duration]s のような動的フォーマットを実現 * **インラインクリエイティブ更新** - `sync_creatives` タスクが既存キャンペーンへの upsert セマンティクスを提供 #### ドキュメントとテスト * **テスト可能なドキュメント** - すべてのコード例をライブスキーマで検証可能 * **クライアントライブラリの明示** - イントロに NPM バッジとインストール手順を掲載 * **50 ファイルで 389 件のリンク切れを修正** ### 移行ガイド #### 判別子フィールド(Breaking) 多くのスキーマで判別子フィールドが必須になりました。コードを更新してください。 **Signal Destinations:** ```json theme={null} // Before { "platform_id": "ttd" } // After { "type": "platform", "platform_id": "ttd" } ``` **Property Selectors:** ```json theme={null} // Before { "publisher_domain": "cnn.com", "property_ids": ["cnn_ctv_app"] } // After { "publisher_domain": "cnn.com", "selection_type": "by_id", "property_ids": ["cnn_ctv_app"] } ``` **Pricing Options:** ```json theme={null} // Before { "pricing_model": "cpm", "rate": 12.50 } // After { "pricing_model": "cpm", "is_fixed": true, "rate": 12.50 } ``` #### Webhook ペイロード構造(Breaking) Webhook ペイロードがトップレベルでプロトコルエンベロープを使用するようになりました。 **Before:** ```json theme={null} { "task_id": "task_123", "status": "completed", "media_buy_id": "mb_456", "packages": [...] } ``` **After:** ```json theme={null} { "task_id": "task_123", "task_type": "create_media_buy", "status": "completed", "timestamp": "2025-11-21T10:30:00Z", "result": { "media_buy_id": "mb_456", "packages": [...] } } ``` #### Signal Activation レスポンス(Breaking) `activate_signal` のレスポンスが単一キーから deployments 配列へ変更されました。 **Before:** ```json theme={null} { "activation_key": "segment_123" } ``` **After:** ```json theme={null} { "deployments": [ { "destination": {"type": "platform", "platform_id": "ttd"}, "activation_key": "segment_123", "status": "active" } ] } ``` #### テンプレートフォーマット フォーマットがパラメーターを受け取り、動的サイズに対応できるようになりました。 **テンプレートフォーマット定義:** ```json theme={null} { "format_id": {"agent_url": "https://creative.adcontextprotocol.org", "id": "display_static"}, "accepts_parameters": ["dimensions"], "renders": [{ "role": "primary", "parameters_from_format_id": true }] } ``` `parameters_from_format_id: true` は、使用時の format\_id から寸法を取得することを示します。 **利用例(パラメータ化された format\_id):** ```json theme={null} { "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "display_static", "width": 300, "height": 250 } } ``` これは display\_static テンプレートの 300x250 版を生成します。 #### 商品フィルタリングの強化 `get_products` に構造化フィルタが追加されました。 ```json theme={null} { "filters": { "start_date": "2026-01-01", "end_date": "2026-03-31", "budget_range": { "min": 10000, "max": 50000, "currency": "USD" }, "countries": ["US", "CA"], "channels": ["display", "video"] } } ``` #### スキーマバージョニング 利用可能な新しいバージョンパス: * `/schemas/2.5.0/` - 正確なバージョン(本番利用に推奨) * `/schemas/v2.5/` - 最新の 2.5.x パッチ(パッチリリースで自動更新) * `/schemas/v2/` - 最新の 2.x リリース(マイナー/パッチで自動更新) * `/schemas/v1/` - 後方互換エイリアス(v2 と同一) ### 破壊的変更 * 配信先、プロパティセレクター、価格オプション、プレビューリクエストで **判別子フィールドが必須** * **Webhook ペイロード構造** - タスクデータが `result` フィールド下へ移動し、`domain` は不要に * **シグナルアクティベーションレスポンス** - `activation_key` 文字列から `deployments` 配列へ変更 * **レガシークリエイティブフィールドの削除** - `list_creatives` レスポンスから `media_url`、`click_url`、`duration` を削除 ### 非破壊的な追加 * すべてのタスクリクエストで任意の Application `context` オブジェクト * ビジュアル UI 向けの任意フィールド `product_card` と `format_card` * 後方互換の `preview_creative` バッチプレビューモード * 配信レポートでのパッケージ価格フィールド(既存ドキュメントをスキーマで強制) * マイナーバージョンのシンボリックリンク (`/schemas/v2.5/`) *** ## Version 2.3.0 **リリース日:** 2025 年 10 月 | [Full Changelog](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#230) ### 変更概要 **パブリッシャー所有のプロパティ定義** - プロパティをパブリッシャーが所有し、エージェントが参照するモデルへ変更(IAB Tech Lab sellers.json に倣う)。重複をなくし、プロパティ情報の単一のソース・オブ・トゥルースを提供。 **プレースメントターゲティング** - 商品が複数のプレースメント(ホームページバナー、記事サイドバーなど)を定義でき、バイヤーは商品購入内でプレースメントごとに異なるクリエイティブを割り当て可能。 **シンプルな予算指定** - 予算をパッケージ単位のみに限定し、混在通貨キャンペーンを可能にするとともに、メディアバイレベルでの冗長な集約を排除。 ### 移行ガイド #### パブリッシャー所有のプロパティ **Before:** ```json theme={null} { "properties": [{ "publisher_domain": "cnn.com", "property_name": "CNN CTV App", "property_tags": ["ctv", "premium"] }] } ``` **After:** ```json theme={null} { "publisher_properties": [ { "publisher_domain": "cnn.com", "property_tags": ["ctv"] } ] } ``` バイヤーは `https://cnn.com/.well-known/adagents.json` からプロパティ定義を取得します。 #### メディアバイ予算の削除 **Before:** ```json theme={null} { "budget": 50000, "packages": [...] } ``` **After:** ```json theme={null} { "packages": [ {"package_id": "p1", "budget": 30000}, {"package_id": "p2", "budget": 20000} ] } ``` 予算はパッケージ単位のみで指定します。 ### 破壊的変更 * 商品の `properties` フィールド → `publisher_properties` * `list_authorized_properties` は `publisher_domains` 配列を返却 * create\_media\_buy/update\_media\_buy リクエストから `budget` を削除 *** ## Version 2.2.0 **リリース日:** 2025 年 10 月 | [Full Changelog](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#220) ### 変更概要 **Build Creative の整合性** - `build_creative` タスクが明確な「manifest-in → manifest-out」の変換モデルに準拠し、パラメーター名を統一。 ### 移行ガイド **Before:** ```json theme={null} { "source_manifest": {...}, "promoted_offerings": [...] } ``` **After:** ```json theme={null} { "creative_manifest": { "format_id": {...}, "assets": { "promoted_offerings": [...] } } } ``` ### 破壊的変更 * `build_creative` パラメーターを `source_manifest` から `creative_manifest` に改名 * トップレベルの `promoted_offerings` を削除(マニフェストの assets に移動) *** ## Version 2.1.0 **リリース日:** 2025 年 1 月 | [Full Changelog](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#210) ### 変更概要 **シンプルなアセットスキーマ** - アセットのペイロードスキーマとフォーマット要件スキーマを分離し、冗長性を排除。アセットタイプはマニフェストの宣言ではなくフォーマット仕様で決定。 ### 移行ガイド **Before:** ```json theme={null} { "assets": { "banner_image": { "asset_type": "image", "url": "https://cdn.example.com/banner.jpg", "width": 300, "height": 250 } } } ``` **After:** ```json theme={null} { "assets": { "banner_image": { "url": "https://cdn.example.com/banner.jpg", "width": 300, "height": 250 } } } ``` ### 破壊的変更 * クリエイティブマニフェストから `asset_type` フィールドを削除 * スキーマパスを `/creative/asset-types/*.json` から `/core/assets/*-asset.json` に変更 * 制約フィールドをアセットペイロードからフォーマット仕様へ移動 # Roadmap Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/roadmap AdCP プロトコルロードマップ: パブリックな GitHub Project ボードで追跡される RFC、エピック、開発マイルストーン。 AdCP ロードマップは、すべてのドメインにわたるプロトコル RFC とエピックを追跡するパブリックな [GitHub Project ボード](https://github.com/orgs/adcontextprotocol/projects/1) です。探索中のもの、承認されたもの、進行中のもの、出荷されたものを示します。 Creative、Media Buy、Signals、Governance などにわたる RFC とエピックを含むライブボード。 *** ## ロードマップの仕組み ボードには、ロードマップアイテムのライフサイクルを表す 4 つの列があります。 | Status | Meaning | | --------------- | ------------------------- | | **Exploring** | 議論中、コミュニティのインプットを歓迎 | | **Accepted** | コミットされスコープされたが、まだ開始されていない | | **In Progress** | アクティブな作業 | | **Shipped** | リリースされ利用可能 | 各アイテムには 2 つのフィールドがあります。 * **Protocol** — 影響するプロトコルの領域(Creative、Media Buy、Signals、Brand Protocol、Governance、SI、TMP、Platform、Website、Addie、Certification) * **Kind** — **RFC**(コミュニティのインプットを必要とするプロトコル変更)か **Epic**(複数の PR にまたがる主要な成果物)か *** ## ロードマップに載るもの すべての issue や PR がここに属するわけではありません。ロードマップアイテムは、アダプターに影響するプロトコルレベルの変更、新機能、戦略的イニシアチブです。issue は、次のうち少なくとも 2 つを満たす場合に該当します。 1. **プロトコルサーフェス** — エージェントやプラットフォームが対話するものを変える 2. **オーディエンスへの影響** — 見込みメンバーやビルダーの決定に影響する 3. **複数 issue のスコープ** — 複数の PR にまたがる バグ修正、軽微な改善、内部ツールは [issue tracker](https://github.com/adcontextprotocol/adcp/issues) に留まります。 *** ## ロードマップへのアイテムの追加 `rfc` または `epic` とラベル付けされた任意の issue は自動的にボードに追加されます。ロードマップアイテムを提案するには: 1. 提案を記述する **GitHub issue を開く** 2. **`rfc` または `epic` ラベルを追加**(メンテナーもトリアージ中にこれを行える) 3. issue が **Exploring** 列に現れる 4. メンテナーがボード上で **Protocol** と **Kind** フィールドを設定する *** ## トリアージ 各プロトコル領域には、新しい issue を毎週レビューし(約 15 分)、何に `rfc` または `epic` ラベルを付けるかを決定する責任を持つトリアージオーナーがいます。トリアージオーナーは、アイテムが実態を反映するよう毎月ボードもレビューします。 | Protocol Area | Triage Owner | | -------------- | ------------ | | Creative | *TBD* | | Media Buy | *TBD* | | Signals | *TBD* | | Brand Protocol | *TBD* | | Governance | *TBD* | | SI | *TBD* | | TMP | *TBD* | | Platform | *TBD* | | Website | *TBD* | | Addie | *TBD* | | Certification | *TBD* | トリアージオーナーは四半期ごとに交代します。志願するには、Slack の関連するワーキンググループチャンネルで連絡してください。 *** ## バージョンマイルストーン 名前付きマイルストーンは、将来のメジャーバージョンで一緒に出荷されるロードマップアイテムをグループ化します。各マイルストーンは承認された RFC をリストします — 探索的なアイテム(コミュニティのインプットが開いている)は、メンテナーがそれをランドさせると決めるまでメインボードに残ります。 ### v4.0 — 2027 年初頭を目標 v4.0 は次の**破壊的変更の蓄積ウィンドウ**で、[リリースケイデンスポリシー](/docs/reference/versioning#release-cadence)に従い 2027 年初頭を目標としています。破壊的変更は、エコシステムがマイナーごとの非推奨を追いかけるのではなく単一の移行ウィンドウを計画できるよう、ここに集められます。下記にリストされたアイテムは v4.0 のコミットされた下限要件です。RFC が承認されるにつれて追加のアイテムがここに追加されます。 | Area | Item | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Security(下限) | [支出コミット操作の必須リクエスト署名(RFC 9421)](https://github.com/adcontextprotocol/adcp/issues/2307) — `security.mdx` の 3.0 の任意プロファイルが必須になる。エージェントは支出コミット操作に署名しなければならず(MUST)、セラーは検証しなければなりません(MUST)。 | このマイルストーンは意図的に「セキュリティリリース」ではありません。プロトコルサーフェス全体にわたる蓄積された破壊的変更が一緒にランドするバージョンです。リクエスト署名が現在の下限要件です。破壊的変更を運ぶ他の承認済み RFC は、メインボードの `rfc` ラベルライフサイクルを進むにつれてここに追加されます。 *** ## リリース履歴 詳細なリリースノートとバージョン履歴については、次を参照。 * **[Release Notes](./release-notes)** — バージョンごとの機能サマリー * **[CHANGELOG.md](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md)** — 技術的変更履歴 * **[GitHub Releases](https://github.com/adcontextprotocol/adcp/releases)** — リリースアーカイブ * **[Versioning & Governance](./versioning)** — バージョニングモデルとリリースケイデンス *** ## 参加するには AdCP は、Slack 上のアクティブなワーキンググループとともにオープンに開発されています。 * **[AgenticAds Slack に参加する](https://join.slack.com/t/agenticads/shared_invite/zt-3c5sxvdjk-x0rVmLB3OFHVUp~WutVWZg)** * **[GitHub Discussions](https://github.com/adcontextprotocol/adcp/discussions)** * **[ワーキンググループ](../community/working-group)** # スキーマ拡張 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/schema-extensions AdCP 固有の `x-` プレフィックス付きスキーマ注釈のリファレンス。 AdCP スキーマは、JSON Schema 語彙を補完するいくつかの `x-` プレフィックス付き注釈キーワードを運びます。JSON Schema 検証器は [draft-07 §6](https://json-schema.org/draft-07/schema) に従って未知の `x-` キーワードを無視するため、これらのキーの追加は任意の準拠検証器とワイヤー互換です。 このページはそれらの注釈の正準リファレンスです。Codegen コンシューマー(TypeScript / Python / Go 型ジェネレーター)、ストーリーボードランナー、AdCP SDK ファミリーはこれらの注釈をプログラム的に読みます。検証者はそれらを読んでもよい(MAY)が、それらが記述する規範的動作は [security.mdx](/docs/building/by-layer/L1/security) の該当セクションまたはフィールド自身の説明でも文書化されています。 ## `x-status` スキーマまたはプロパティを **実験的** — コアプロトコルの一部だがまだ凍結されていない — としてマークします。実験的サーフェスを実装するセラーは、`get_adcp_capabilities` の `experimental_features` に機能 id を宣言します。完全な卒業ポリシーについては [実験的ステータス](/docs/reference/experimental-status) を参照。 ```jsonc theme={null} "trusted_match": { "type": "object", "x-status": "experimental", "description": "Trusted Match Protocol support..." } ``` 許可される値: `"experimental"`。キーワードは安定サーフェスでは省略されます。 ## `x-adcp-validation` 構造化された規範的制約を散文の説明から機械可読な形状に引き上げます。ストーリーボードランナーと SDK 検証器は構造化されたルールを消費します。codegen コンシューマーは注釈を無視して人間の説明を読めます。 このキーワードは、ストーリーボードランナーが英語をパースして強制できない「...のとき存在しなければならない(MUST)」や「...のホストと等しくなければならない(MUST)」句を現在説明が運ぶフィールドに最も有用です。 ### 形状 ```jsonc theme={null} "some_field": { "type": "string", "format": "uri", "description": "Brief one or two sentences for codegen JSDoc; full constraints live in x-adcp-validation and the linked spec.", "x-adcp-validation": { "trust_root": true, "required_when": { "any_of": [ ... ] }, "schema_required_when": { ... }, "verifier_constraints": { ... }, "distinct_from": "other.field.path", "spec": "docs/.../section.mdx#anchor" } } ``` ### サブキー | Key | Type | Purpose | | ---------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `trust_root` | boolean | フィールドが署名検証に負荷を担う。検証者は権威的として扱わなければならない(MUST)。 | | `required_when` | `any_of` / `all_of` をラップするオブジェクト | ストーリーボード強制の required-when ルール(3.x)。オブジェクトは `any_of`(OR 結合)または `all_of`(AND 結合)のいずれか正確に 1 つを持ち、それぞれがリーフ条件の配列を含む。各リーフは次のいずれか: `{ "field": "...", "non_empty": true }`、`{ "field": "...", "equals": }`、`{ "field": "...", "any_subfield_present": true }`。ラップするオブジェクトは JSON Schema の `anyOf`/`allOf` の先例をミラーし、ツール読み取り者が馴染みのある boolean 結合子セマンティクスを再利用できる。素の配列は受け入れられない — 常にラップする。 | | `schema_required_when` | condition | ルールがストーリーボード強制からスキーマ必須に昇格するとき。通常、`any_item_matches_pattern` 経由でバージョンパターンに一致する `adcp.supported_versions` にキー付けされる(例: 4.0 切り替えと任意の 4.x パッチの `"^4\\."`)。 | | `forbidden_when` | `any_of` / `all_of` をラップするオブジェクト | `required_when` の逆。ラップされた条件が成立するとき、フィールドは欠如していなければならない(型によっては `false`/空)(MUST)。`required_when` と同じリーフ形状。存在が別の姿勢と相互排他的なフィールドに使う。 | | `disjoint_with` | string(ドット付きパス)またはドット付きパスの配列 | アイテムレベルの相互排他: このフィールドの配列のどの値も、名前付き配列のいずれにも現れてはならない(MAY NOT)。ストーリーボードランナーは各について集合の非交差性をアサートする。例: `request_signing.warn_for` は `disjoint_with: "request_signing.required_for"` を運ぶ。なぜなら操作は一方または他方にありえるが、決して両方ではないから。 | | `subset_of` | string(ドット付きパス) | アイテムレベルの部分集合制約: このフィールドの配列のすべての値は、名前付き配列にも現れなければならない(MUST)。例: `request_signing.required_for` は `subset_of: "request_signing.supported_for"` を運ぶ — 操作はサポートされずに必須にはなれない。 | | `verifier_constraints` | object | 上記の構造化サブキーに適合しない検証者側ルールの自由形式のキー値マップ。キーは規範的(例: `agent_url_match: "byte_equal"`)。ストーリーボードランナーはこれらをテストベクターに対して強制する。適合するとき構造化サブキー(`required_when`、`forbidden_when`、`disjoint_with`、`subset_of`)を優先。一般化しない一回限りのルールにのみ `verifier_constraints` に手を伸ばす。 | | `distinct_from` | string(ドット付きパス) | 名前の混同を防ぐため、類似の形状だが異なるセマンティクスを持つ別のフィールドを名指しする(例: `identity.brand_json_url` は `sponsored_intelligence.brand_url` と別)。検証者は一方を他方の代わりにしてはならない(MUST NOT)。 | | `spec` | string(アンカー付き相対パス) | フィールドの完全なセマンティクスを定義するドキュメントの規範的セクションへのポインター。他のサブキーが存在するとき常に必須。 | ### 適合性 * 検証器は前方互換性のため未知のサブキーを無視しなければならない(MUST)(スキーマがマイナーリリースで新しいエントリを追加しうる)。 * ストーリーボードランナーは `required_when`、`schema_required_when`、`verifier_constraints` を消費してリリースごとにテストケースを生成する。まだサブキーを認識しないランナーはそれをスキップし「認識されない検証ルール」警告を発しなければならない(MUST)。 * Codegen コンシューマー(TypeScript / Python / Go 型ジェネレーター)は `x-adcp-validation.spec` を `@see` JSDoc リンクとしてサーフェスしてもよい(MAY)が、それ以外は注釈を不透明として扱う。 ### 現在の使用 代表的な使用: | Field | Sub-keys used | Rule | | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `identity.brand_json_url` | `trust_root`、`required_when`、`schema_required_when`、`verifier_constraints`、`distinct_from` | 署名鍵ディスカバリーのための trust-root ポインター。署名姿勢に結びついた required-when。4.0 でスキーマ必須。`sponsored_intelligence.brand_url` と別。[security.mdx §エージェントの署名鍵の発見](/docs/building/by-layer/L1/security) を参照。 | | `identity.key_origins` | `verifier_constraints`(`purpose_anchoring`) | リストされたすべての purpose は、レスポンスの他の場所で宣言された対応する署名姿勢を持たなければならない(MUST)。クロスフィールドルール。[security.mdx §オリジン分離](/docs/building/by-layer/L1/security) を参照。 | | `request_signing.required_for` | `subset_of` | リストされたすべての操作は `supported_for` にも現れなければならない(MUST) — 操作はサポートされずに必須にはなれない。 | | `request_signing.warn_for` | `disjoint_with`、`subset_of` | 操作は `warn_for` と `required_for` の両方に現れてはならない(MUST NOT)。リストされたすべての操作は `supported_for` にも現れなければならない(MUST)。 | | `webhook_signing.supported` | `verifier_constraints`(`must_equal_when`) | セラーが変更 webhook の発出をアドバタイズするとき(`media_buy.reporting_delivery_methods` が `webhook` を含む、または `media_buy.content_standards.supports_webhook_delivery: true`)、`supported` は `true` でなければならない(MUST)。ダウングレードベクターを閉じる。 | | `wholesale_feed_webhooks.event_types` | `verifier_constraints`(`wholesale_feed_webhook_capability_consistency`) | `product.*` イベントタイプはホールセール `get_products` を必要とする。`signal.*` イベントタイプはホールセール `get_signals` を必要とする。`wholesale_feed.bulk_change` は少なくとも 1 つの宣言されたホールセール修復パスを必要とし、修復可能なフィードファミリーのみを名指ししなければならない。 | | `get_products.wholesale_feed_version` / `get_signals.wholesale_feed_version` | `verifier_constraints`(`required_for_wholesale_request`) | バージョントークンはホールセール読み取りレスポンスで必須だが、共有レスポンススキーマはレスポンスボディだけからリクエストの `buying_mode` / `discovery_mode` を推論できない。 | JSON Schema によってネイティブに既に強制され、移行から除外: * **`adcp.idempotency`** — 判別された `oneOf` が、サポートブランチで `replay_ttl_seconds` を既に要求し、非サポートブランチでそれを禁止する。 * **`webhook_signing.algorithms`** — 各アイテムの `enum: ["ed25519", "ecdsa-p256-sha256"]` が既に許可リストを強制する。 移行履歴は [adcontextprotocol/adcp#3827](https://github.com/adcontextprotocol/adcp/issues/3827) で追跡。 ## `x-adcp-open-payload` SDK ジェネレーターにオープンエンドに見えるフィールドを分類します。注釈は文書的で非検証です。フィールドの JSON Schema が依然としてワイヤー検証を制御します。 許可されるオーサリング値: | Value | Meaning for schema authors and generators | | ------ | ---------------------------------------------------------------------------------------------------------------------------------- | | `true` | フィールドは意図的に自由形式のペイロードデータを運ぶ。ジェネレーターは object/decoded-JSON アームを警告なしに `Record` または別の汎用 JSON コンテナとしてモデル化してもよい(MAY)。 | | 省略 | 未分類。ジェネレーターとスキーマリントルールは `true` も `false` も推論すべきではない(SHOULD NOT)。レビュー中に注釈のない `additionalProperties: true` オブジェクトフィールドで警告してもよい(MAY)。 | `false` は将来の構造化だが拡張許容マーカーのために予約されています。ソーススキーマは、リポジトリが少なくとも 1 つの正準使用サイトとその値のジェネレーターコントラクトを定義するまで、`x-adcp-open-payload: false` を設定してはなりません(MUST NOT)。 混合フィールドについては、注釈は object または decoded-JSON ペイロードアームにのみ適用されます。スカラーアームは依然として宣言されたスキーマに従います。例えば、上流の記録されたボディは、コンテンツタイプが JSON 形状のとき decoded JSON オブジェクトで、それ以外は文字列でありうる。`x-adcp-open-payload: true` は、文字列アームをオブジェクトモデルとしてではなく、decoded JSON ペイロードを意図的にオープンとしてマークする。 ```jsonc theme={null} "breakdown": { "type": "object", "x-adcp-open-payload": true, "additionalProperties": true } ``` レガシーまたはまだ分類されていないフィールドには省略を使う。「閉じている」を意味するために省略を使わない。 ## `x-adcp-hoist` ソーススキーマを正準に共有される型としてマークするビルド時ディレクティブ。スキーマバンドラーは、すべてのインライン出現を単一のルート `$defs` エントリに引き上げ、インラインコピーを `$ref` ポインターに置き換える。ディレクティブ自体はバンドル出力から除去される。ワイヤーに無関係 — 検証器はそれを無視しなければならず(MUST)(draft-07 §6 の未知キーワードセマンティクス)、準拠コンシューマーはバンドルされたアーティファクトでそれを観測すべきではない(SHOULD NOT)。 ```jsonc theme={null} { "$id": "/schemas/core/price-block.json", "title": "Price Block", "x-adcp-hoist": true, "type": "object", "properties": { "cpm": { "type": "number" }, "currency": { "type": "string" } } } ``` ### なぜ複雑なオブジェクトにオプトインか 純粋な enum は自動的に引き上げられます([`hoistDuplicateInlineEnums`](https://github.com/adcontextprotocol/adcp/pull/3170) を参照)。なぜなら 2 つの構造的に同一な enum のマージはセマンティクス保存だからです。複雑なオブジェクトは異なります — 構造的同一性 ≠ 意味的同一性。`BriefAsset`(提案されたクリエイティブ仕様)と `VASTAsset`(配信された動画クリエイティブ)は現在フィールドを共有しますが、異なるライフサイクル概念を表します。それらを自動マージすると、ソーススキーマが表現しないクロスツール結合が生まれ、SDK がマージされた型に対して codegen したらほどくのが難しくなります。`x-adcp-hoist` は共有か分割かの決定をスキーマごとに意図的にします。 ### バンドラーの動作 * **任意の出現回数(≥1)で引き上げる。** ディレクティブは意図 — 「これは正準な名前付き型」 — を宣言するため、後で 2 番目の参照を追加しても codegen サーフェスは決して変わらない。 * **`title` が必須。** タイトルの欠如または空 → ビルド時エラー。ディレクティブは意図的であることが意図される。 * **同じタイトル + 異なる形状はビルド時エラー。** 同じ `title` だが異なるフィールドで作られた 2 つのマークされたスキーマは、そうでなければ一方を `Foo2` に黙ってサフィックス付けし、ディレクティブの「正準名」保証を無効にする。 * **既存の `$defs` キーとの衝突はサフィックス付けされる**(`PriceBlock2`)。純粋 enum 引き上げが使う慣例に一致。 * **バンドル出力から除去される** — 正準 `$defs` エントリからも、既存の `$defs` ブロック内に書かれた任意の迷子マーカーからも。 ### SDK / codegen への影響 以前インライン化されたソーススキーマに `x-adcp-hoist` を追加することはワイヤー互換(バンドルされたスキーマは依然として同じペイロードを検証する)ですが、codegen 形状の変更です: 以前匿名のインライン型(しばしば `Foo1`、`Foo2`、…)を発した TypeScript / Python / Go 型ジェネレーターは、今や単一の名前付き型を発します。SDK 採用者は独自の非推奨ポリシーに従ってリネームエイリアスを維持します — クライアント側のリネーム/エイリアス追跡については [adcp-client#942](https://github.com/adcontextprotocol/adcp-client/issues/942) を参照。 ### 適合性 * 検証器は draft-07 §6 に従って `x-adcp-hoist` を無視しなければならない(MUST)(未知キーワードは許容される)。ディレクティブはワイヤーセマンティクスを持たない。 * ソースツリーコンシューマー(バンドルされたアーティファクトではなく `static/schemas/source/...` を直接デリファレンスするサードパーティ)は、`x-adcp-hoist: true` を no-op 注釈として扱わなければならない(MUST)。スキーマのコンテンツがコントラクト。 * `scripts/build-schemas.cjs` 以外のバンドラーは、ディレクティブを尊重しても無視してもよい(MAY)。それを無視するバンドラーは、重複排除されていないインラインコピーを持つワイヤー互換バンドルを生成する。 ### 履歴 * [#4557](https://github.com/adcontextprotocol/adcp/issues/4557) で導入。[#3145](https://github.com/adcontextprotocol/adcp/issues/3145) フェーズ 2 の後継。 ## 将来の拡張 新しい `x-adcp-*` キーワードはマイナーリリースで追加されます。コンシューマーはエラーなしに未知の `x-` キーワードを許容しなければなりません(MUST)。慣例は `x-adcp-` 名前空間を予約します。ベンダー固有またはデプロイ固有の注釈は、衝突を避けるためベンダー固有のプレフィックス(例: `x-yourorg-`)を使うべきです(SHOULD)。 # 仕様ライフサイクル Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/specification-lifecycle AdCP 仕様セクションが Draft から Final へどう移動するか、各遷移を誰が決定するか、各ステージが実装者にとってどんな安定性コントラクトを運ぶか。 AdCP 仕様の各セクションは 5 つのステージの 1 つにあります。ステージは実装者にどれだけの安定性を期待すべきかを伝えます: Draft セクションはコントラクトを運びません。Final セクションは完全な [3.x 安定性保証](/docs/reference/versioning#3x-stability-guarantees) によって保護されます。 *** ## 一目でのステージ | Stage | Vocabulary match | Stability contract | Safe to build production systems against? | | -------------- | -------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------- | | **Draft** | Pre-schema | なし | いいえ | | **Proposed** | Experimental(`x-status: experimental`) | [実験的コントラクト](/docs/reference/experimental-status#実験的サーフェスのコントラクト) | 注意して — 6 週間予告で壊れることがある | | **Final** | Stable(`x-status` マーカーなし) | 完全な [3.x 保証](/docs/reference/versioning#3x-stability-guarantees) | はい | | **Deprecated** | Stable、削除がアナウンス済み | 削除リリースまで Final と同じ | はい — ただし移行を計画 | | **Sunset** | Removed | なし | いいえ — 機能は消えた | vocabulary match 列が鍵です: 仕様ステージとスキーマ注釈は同じ状態の 2 つのビューです。`static/schemas/source/` の JSON Schema が `"x-status": "experimental"` を持つなら、それが属するセクションは **Proposed** ステージにあります。`x-status` マーカーを持たず、スキーマが GA リリースで出荷されているなら、**Final** ステージにあります。 *** ## ステージ定義 ### Draft セクションは、ワーキンググループが GitHub マイルストーンでスコープされた提案 — 定義されたスコープを持つ `spec / protocol` ラベル付き issue — を開いたが、まだ `static/schemas/source/` にスキーマが公開されていないとき Draft にあります。Draft セクション: * **いかなる種類の安定性コントラクトも運ばない**。Draft セクションに対して進むビルダーは自己責任でそうし、変更のため該当する GitHub マイルストーンを追跡すべき(SHOULD)。 * `get_adcp_capabilities` で宣言されない。セラーは Draft セクションとの適合性を主張できない。 * Proposed に達する前に放棄されることがある。 **参入**: スコープされた仕様 issue がワーキンググループのアクティブなマイルストーンに受け入れられる。ドメインワーキンググループリードがスコープが範囲内であることを確認する。 **決定権限**: ドメインワーキンググループリード。 **RFC プロセスが適用されるとき**: 実質的な仕様変更 — 新しいタスク、既存フィールドに影響するスキーマ変更、規範的散文への変更 — は、Draft に入る前に承認された [RFC](https://github.com/adcontextprotocol/adcp/issues/2437) を必要とします。RFC は動機、検討された代替案、互換性への影響、レビュアーチェックリストを文書化します。軽量な変更(動作上の帰結のない新しい任意フィールド)は、正式な RFC の代わりにワーキンググループレビューを伴う PR 経由で Draft に入れます。 *** ### Proposed セクションは、そのスキーマがスキーマルートまたは特定のプロパティに `"x-status": "experimental"` を持って `static/schemas/source/` に存在し、**かつ** それを実装するセラーが `get_adcp_capabilities` の `experimental_features` に機能 id を宣言するとき Proposed です。 完全な Proposed ステージのコントラクト — 6 週間予告で変わりうるもの、変わらないもの(認証、トランスポート、エラーエンベロープ)、セラー宣言要件を含む — は [実験的ステータス](/docs/reference/experimental-status) で仕様化されています。そのページが統治します。このページはステージ語彙をそれにマップします。 **参入**: スキーマが `x-status: experimental` を持って 3.x リリースで公開される。リリースに伴うアーキテクチャ委員会レビューが実験的ステータスを確認する。 **決定権限**: アーキテクチャ委員会、リリース時。 *** ### Final セクションは、そのスキーマが安定 — アーキテクチャ委員会がレビューした意図的な卒業 PR 経由で `x-status: experimental` が削除された — とき Final です。 4 つの卒業基準は [実験的ステータス — 安定版への卒業](/docs/reference/experimental-status#安定版への卒業) で仕様化されています — 本番シグナル要件、クロスパーティ検証ハードル(45 日以上稼働しうち少なくとも 1 つが本番の 2 番目の実装、または 1 実装 + バイヤー統合)、スキーマ安定性ウィンドウ、意図的な昇格 PR を含む。そのページが統治します。このページはステージ語彙をそれにマップします。アーキテクチャ委員会は各 3.x リリースで卒業 PR をレビューします。 いったん Final になると、セクションは完全な [3.x 安定性保証](/docs/reference/versioning#3x-stability-guarantees) によって保護されます: フィールドは決して削除されず、enum は加算的のみ、タスク名はメジャーバージョン内で決して削除もリネームもされません。**3.x の Final セクションは、4.0 開発サイクルを通じて — 同じ保証の下で — Final のままです。** アクティブな 4.0 スコーピング下のセクションは遡及的に降格されません。3.x コントラクトは 3.x サポートが終わるまで保たれます。[前メジャーのサポートウィンドウ](/docs/reference/versioning#support-window-for-previous-major) を参照。 **参入**: experimental-status.mdx の 4 つの卒業基準すべてが満たされる。アーキテクチャ委員会が承認し、スケジュールされた 3.x リリースで卒業 PR をマージする。 **決定権限**: アーキテクチャ委員会。 *** ### Deprecated セクションは、正式な非推奨予告がリリースノートとチェンジログで公開され、削除への 6 か月のカウントダウンが始まったとき Deprecated です。セクションは非推奨ウィンドウ中、完全に機能し 3.x 安定性保証の下にあります。 非推奨ポリシー — 6 か月の最小予告、非推奨化後少なくとも 1 つの完全なリリースサイクル機能が持続、同じメジャーバージョン内で決して削除されない — は [バージョニング — 非推奨ポリシー](/docs/reference/versioning#deprecation-policy) で仕様化されています。 **参入**: 非推奨予告が 3.x リリースのリリースノートとチェンジログに着地する。置き換え(あれば)が同じリリースで出荷される。アーキテクチャ委員会が削除ターゲット(常にメジャーバージョン境界)を確認する。 **決定権限**: アーキテクチャ委員会、ワーキンググループレビューを伴う。 **アナウンス**: `deprecated` ラベル + 移行ノート付きのリリースノートエントリ、チェンジログエントリ、スキーマのインライン `@deprecated` 注釈。 *** ### Sunset セクションは、それがターゲットにされたメジャーバージョン境界が出荷されたら Sunset(削除)です。ポリシーにより、Deprecated セクションは同じメジャーバージョン内で決して削除されません — 最も早い削除日は次のメジャーの GA です。v2 サンセットタイムラインは [v2 sunset ページ](/docs/reference/v2-sunset) で文書化されています。 **参入**: 後継メジャーが GA 出荷される。以前に非推奨化されたフィールド、タスク、スキーマプロパティが新しいメジャーのスキーマから省略される。 **決定権限**: 暗黙 — メジャーバージョンリリースプロセスが削除を運ぶ。 *** ## 遷移図 ``` Draft ──→ Proposed ──→ Final ──→ Deprecated ──→ Sunset └──→ (放棄) (安定) (6か月予告) (メジャー 境界で削除) ``` Draft は Proposed に達せずに放棄されることもあります。Final セクションは直接 Deprecated になりうる(中間ステージなし)。Sunset から任意の前のステージへのパスはありません — 削除された機能は復元されず、必要なら一から再提案されます。 ステージ遷移(Draft → Proposed → Final、Final → Deprecated など)は承認された RFC でゲートされます — RFC の結果(accepted / accepted-with-changes)が、コントリビューターに遷移を運ぶ仕様 PR を開くことを認可するトリガーです。[RFC プロセス(issue #2437)](https://github.com/adcontextprotocol/adcp/issues/2437) を参照。 *** ## 仕様セクションの現在のステージを確認する方法 1. **スキーマmarker**: `static/schemas/source/` の該当スキーマを確認。`"x-status": "experimental"` → Proposed。出荷されたスキーマにマーカーなし → Final。`static/schemas/source/` にないがオープンな GitHub マイルストーン issue で参照 → Draft。 2. **実験的サーフェスリスト**: [実験的ステータス](/docs/reference/experimental-status) ページが、機能 id と現在のステータスを持つすべての現在 Proposed のサーフェスをリストする。 3. **GitHub マイルストーン**: アクティブなマイルストーンのオープン issue は Draft と Proposed の作業を表す。マージされたスキーマに `x-status` のない出荷済みアイテムは Final。 4. **リリースノート**: Deprecated セクションは、すべてのリリースのノートで `deprecated` ラベルと削除ターゲット付きで言及される。 *** ## ワーキンググループとガバナンス 誰がワーキンググループに座るか、投票しきい値、定足数、エスカレーションパスを統治する手続き: * **RFC プロセス**([#2437](https://github.com/adcontextprotocol/adcp/issues/2437)): 提案テンプレート、決定記録形式、RFC から仕様変更までのライフサイクル * **ワーキンググループ憲章**([#2438](https://github.com/adcontextprotocol/adcp/issues/2438)): 定足数、投票しきい値、名簿、忌避ポリシー — アクティブに開発中 アーキテクチャ委員会は、Final ステージの卒業と Deprecated ステージの参入について定義された決定権限です。[実験的ステータス](/docs/reference/experimental-status) で参照され、安定性コントラクトを運ぶステージ遷移についてワーキンググループの批准機関として動作します。 # リファレンステストベクター Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/test-vectors/index ワイヤーフォーマットの一致を確認するため SDK と実装が差分を取る機械可読フィクスチャ。仕様とともにバージョン管理され、各リリースで凍結される。 **ステータス**: Request for Comments **最終更新**: 2026 年 4 月 20 日 ## これらは何か リファレンステストベクターは、仕様の特定のワイヤーフォーマットルールにピン留めされた機械可読な JSON フィクスチャです。出力がベクターの `expected_*` フィールドとバイト単位で一致する SDK は、そのルールのワイヤーフォーマットについてリファレンスと一致しています — 適合性クレームではありません(それは [ストーリーボード](/docs/building/conformance) のみが決定します)が、相互運用の必要な前提条件です。分岐する SDK は、自身のテストが合格しても相互運用バグを持っています。 ベクターは [ストーリーボード](/docs/building/conformance) を補完します。ストーリーボードはエージェントをエンドツーエンドで実行して pass/fail の判定を生成します。ベクターはライブラリを凍結された入力に対して分離して実行します。ベクターは署名者に「この 9421 リクエストはこの署名ベースを生成しなければならない(MUST)」と伝えます。ストーリーボードはエージェントに「バイヤーがこのリクエストを送るとき、このような形状の結果で応答しなければならない(MUST)」と伝えます。ほとんどの準拠スタックは両方を必要とします — ベクターはライブラリ内の正準化ドリフトを捕まえ、ストーリーボードはワイヤーでの動作ドリフトを捕まえます。 ベクターは適合性仕様ではありません — [ストーリーボード](/docs/building/conformance) がそれです。ベクターは、ストーリーボードと SDK ユニットテストが消費するリファレンス入力です。 ## バージョニング コンプライアンスツリーの下に公開されるベクターセット — `request-signing`、`webhook-signing`、`plan-hash`、`webhook-receiver-envelope`、`catalog-macro-substitution` — は仕様とともにバージョン管理されます。`/compliance/{version}/test-vectors/{set}/` で提供されるコピーは、そのバージョンの GA リリースで凍結されます。ベクターのバイトを変える修正は次の AdCP マイナーリリースで出荷されます。`/compliance/latest/test-vectors/{set}/` は最新の GA を追跡し、リリース間であなたの下で移動します。 `/test-vectors/{name}.json` で提供されるトランスポートとレスポンス抽出のベクターは、現在バージョン管理されていません: 各ファイルは変更されたときにその場で上書きされます。これらのフィクスチャを消費する SDK は、これらのファイルがバージョン管理されたコンプライアンスツリーに巻き込まれるまで、コミットにピン留めされたコピーをベンダリングすべきです(SHOULD)。例えば `https://raw.githubusercontent.com/adcontextprotocol/adcp//static/test-vectors/.json` から取得し、`` をロックファイルに記録します。 SDK は、利用可能な場所でバージョン管理されたパスを取得し、テスト中のバージョンを記録すべきです(SHOULD)。ピン留めされたバージョンについては、`/compliance/{version}/...` の CDN コピーが真実の源泉です。`/compliance/latest/...` は安定したピンではなく便宜的なエイリアスです。 ## 公開されたセット | Set | What it pins | Source | CDN | | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | [`request-signing`](https://github.com/adcontextprotocol/adcp/tree/main/static/compliance/source/test-vectors/request-signing) | RFC 9421 リクエスト署名プロファイル: 正準署名ベース、カバードコンポーネント、署名パラメーター、タグ名前空間、alg 許可リスト、`adcp_use` 判別子、リプレイ重複排除、失効、content-digest セマンティクス、URL 正準化 | `static/compliance/source/test-vectors/request-signing/` | `/compliance/latest/test-vectors/request-signing/` | | [`webhook-signing`](https://github.com/adcontextprotocol/adcp/tree/main/static/compliance/source/test-vectors/webhook-signing) | RFC 9421 webhook 署名プロファイル: 必須カバードコンポーネント(content-digest 必須 — `forbidden` オプトアウトなし)、`adcp/webhook-signing/v1` タグ、webhook 有効な `adcp_use` セット(`request-signing` に加え非推奨 `webhook-signing`)、`webhook_signature_*` エラータクソノミー。`request-signing` と `@target-uri` 正準化を共有 | `static/compliance/source/test-vectors/webhook-signing/` | `/compliance/latest/test-vectors/webhook-signing/` | | [`plan-hash`](https://github.com/adcontextprotocol/adcp/tree/main/static/compliance/source/test-vectors/plan-hash) | `plan_hash` プリイメージの JCS 正準化: required のみのベースライン、full-optional、bookkeeping 除去、omitted 対 explicit-null、配列順の感度、`ext.trace_id` の区別、Unicode 非正規化(RFC 8785 §3.2.5) | `static/compliance/source/test-vectors/plan-hash/` | `/compliance/latest/test-vectors/plan-hash/` | | [`webhook-receiver-envelope`](https://github.com/adcontextprotocol/adcp/blob/main/static/compliance/source/test-vectors/webhook-receiver-envelope.json) | 完全な MCP webhook POST エンベロープの受信者側リプレイベクター: 正準配信レポートエンベロープの受け入れ、リトライ冪等性の保持、素の結果ペイロードまたは不正なエンベロープの拒否 | `static/compliance/source/test-vectors/webhook-receiver-envelope.json` | `/compliance/latest/test-vectors/webhook-receiver-envelope.json` | | [`catalog-macro-substitution`](https://github.com/adcontextprotocol/adcp/blob/main/static/compliance/source/test-vectors/catalog-macro-substitution.json) | カタログアイテムマクロ置換の安全性: NFC 正規化、RFC 3986 パーセントエンコーディング、ネスト展開の保持、CRLF 無効化、bidi オーバーライド無効化、URL スキームインジェクション無効化 | `static/compliance/source/test-vectors/catalog-macro-substitution.json` | `/compliance/latest/test-vectors/catalog-macro-substitution.json` | | [`transport-error-mapping`](https://github.com/adcontextprotocol/adcp/blob/main/static/test-vectors/transport-error-mapping.json) | トランスポート層エラーエンベロープ形状: 各文書化された AdCP トランスポートエラーの JSON-RPC(`error.code` / `data`)と A2A(task `status.message`)キャリア | `static/test-vectors/transport-error-mapping.json` | [`/test-vectors/transport-error-mapping.json`](https://adcontextprotocol.org/test-vectors/transport-error-mapping.json) | | [`mcp-response-extraction`](https://github.com/adcontextprotocol/adcp/blob/main/static/test-vectors/mcp-response-extraction.json) | MCP `tools/call` エンベロープからの AdCP ペイロードのクライアント抽出 | `static/test-vectors/mcp-response-extraction.json` | [`/test-vectors/mcp-response-extraction.json`](https://adcontextprotocol.org/test-vectors/mcp-response-extraction.json) | | [`a2a-response-extraction`](https://github.com/adcontextprotocol/adcp/blob/main/static/test-vectors/a2a-response-extraction.json) | A2A タスクステータスとアーティファクトからの AdCP ペイロードのクライアント抽出 | `static/test-vectors/a2a-response-extraction.json` | [`/test-vectors/a2a-response-extraction.json`](https://adcontextprotocol.org/test-vectors/a2a-response-extraction.json) | | [`webhook-payload-extraction`](https://github.com/adcontextprotocol/adcp/blob/main/static/test-vectors/webhook-payload-extraction.json) | インバウンド AdCP webhook の受信者側フォーマット検出とペイロード抽出 | `static/test-vectors/webhook-payload-extraction.json` | [`/test-vectors/webhook-payload-extraction.json`](https://adcontextprotocol.org/test-vectors/webhook-payload-extraction.json) | | [`webhook-hmac-sha256`](https://github.com/adcontextprotocol/adcp/blob/main/static/test-vectors/webhook-hmac-sha256.json) *(legacy)* | レガシー HMAC webhook プロファイルの HMAC-SHA-256 署名計算とバイト等価不変条件。3.x で非推奨、4.0 で削除([Webhook callbacks](/docs/building/by-layer/L3/webhooks#legacy-hmac-sha256-fallback-deprecated) 参照)。新しい統合は `webhook-signing` を使う | `static/test-vectors/webhook-hmac-sha256.json` | [`/test-vectors/webhook-hmac-sha256.json`](https://adcontextprotocol.org/test-vectors/webhook-hmac-sha256.json) | **ここから始める**: すべてのセットの Source 列の `README.md` が、ファイルレイアウト、鍵素材、前提条件(例: リプレイベクターに必要なランナー状態)、セットを SDK テストループに配線する方法を文書化します。ソースツリーの README が権威的です。このページのインデックスはカタログであり、統合ガイドではありません。 ディレクトリ CDN パス(3 つのコンプライアンスツリー行)はプログラム利用のためのベースパスです — CDN は個別のファイルを提供し、ディレクトリリストは提供しません。Source 列経由でツリーを参照してください。 ## テスト鍵は公開されている すべての署名ベクターセットは、ライブラリが同一の入力に対して署名者と検証者のロールを実行できるよう、`keys.json` に秘密鍵素材を出荷します。これらの鍵は **このスイートに対するグレーディングにのみ有効** です。 公開された `keys.json` ファイルの 1 つで宣言された `kid` を信頼する任意の本番検証者は悪用可能です — 秘密鍵は公開 CDN 上にあり、誰でもその kid の下で署名を偽造できます。執筆時点でこれには `test-ed25519-2026`、`test-es256-2026`、`test-gov-2026`、`test-revoked-2026`(request-signing)と `test-ed25519-webhook-2026`、`test-es256-webhook-2026`、`test-wrong-purpose-2026`、`test-response-purpose-2026`、`test-revoked-webhook-2026`(webhook-signing ベクター)が含まれます。任意のスイート `keys.json` に現れるすべての `kid` を、現在も将来も、グレーディング外では信頼できないものとして扱ってください。 本番署名者は独自のキーペアを鋳造し、独自の `jwks_uri` の下で公開します。本番検証者は、ライブトラフィックに公開された信頼ストアに任意のテスト `kid` を登録してはなりません(MUST NOT)。 ## スコープ 上のセットは、すべてのサーフェスをまたぐトランスポート、署名、正準化ルール — ストーリーボードが検証できないバイトレベルのピン — を実行します。タスクごとのリクエスト/レスポンスフィクスチャは意図的にここに公開されていません: [適合性ストーリーボード](https://adcontextprotocol.org/compliance/latest/)(ワイヤー動作、エラーコード、ライフサイクル遷移)と [JSON Schema](https://github.com/adcontextprotocol/adcp/tree/main/static/schemas/source)(リクエスト/レスポンス形状)がそれらの間でタスクレベルの適合性をカバーし、凍結されたリクエスト/レスポンスペアの並行ツリーは、それが複製するストーリーボードに対してドリフトするでしょう。 実装者はスキーマから期待される形状を導出し、エージェントに対してストーリーボードを実行してワイヤー動作を確認します。機械可読なタスクごとのフィクスチャが欲しい SDK 作者は、別個のベクターセットを期待するのではなく、該当するストーリーボードからそれらを抽出すべきです。 # URL 正準化 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/url-canonicalization 2 つの URL が識別子として比較されるあらゆる場所で AdCP が使う正準化ルール — リクエスト署名、認可マッチング、レジストリルックアップ。 AdCP はいくつかの場所で URL を識別子として比較します: リクエスト署名プロファイルの `@target-uri`、`adagents.json` の `authorized_agents[].url` エントリ、TMP `AvailablePackage` の `seller_agent.agent_url`、`format-id` と `ProviderEntry` の `agent_url`、その他 URL がプライマリキーである任意のレジストリ。単一の正準化アルゴリズムがこれらすべてを統治するため、どのサーフェスがルックアップをしていても、バイト単位で異なるが意味的に等しい 2 つの URL は等しく比較されます。このページはそのアルゴリズムの権威ある拠点です。[リクエスト署名プロファイル](/docs/building/by-layer/L1/security#署名付きリクエストトランスポート層) はそれを引用し、トランスポート固有の拡張を追加します。 ## アルゴリズム 正準化は、この順序で RFC 3986 §6.2.2(構文ベースの正規化)と §6.2.3(スキームベースの正規化)を適用します。実装はすべてのステップを適用し、結果をバイト単位で比較しなければなりません(MUST)。 1. **スキームを小文字化**(`HTTPS` → `https`)。スキーム自体は保持されます — `http` と `https` は異なる形式に正準化され、識別子比較で一致してはなりません(MUST NOT)。 2. **ホストを小文字化。** IDN ラベルについては、**UTS-46 Nontransitional processing(`CheckHyphens=true`、`CheckBidi=true`、`UseSTD3ASCIIRules=true`、`Transitional_Processing=false`)** を使って Punycode A-label(ACE 形式)に変換します(`bücher.example` → `xn--bcher-kva.example`)。処理モードのピン留めが重要です: ToASCII の前に非 ASCII 入力を ASCII 小文字化すると、UTS-46 正しい処理とは異なる A-label が生成され、TypeScript(`url.domainToASCII`)、Go(`golang.org/x/net/idna`)、Python(`idna` パッケージ — IDNA2003 である `str.encode('idna')` では*ない*)はモードのデフォルトで正当に分岐します。生成者によって ToASCII 正規化されていない生の非 ASCII バイトを含むホストは、比較者によって拒否されなければなりません(MUST) — 受信者は黙って再正規化しません。IPv6 リテラルについては、`[` と `]` ブラケットを保持し、その内部の 16 進数を小文字化します(`[2001:DB8::1]` → `[2001:db8::1]`)。**IPv6 ゾーン識別子(RFC 6874)は拒否されなければなりません(MUST)** — ゾーン ID はノードローカルで、生成ホストの外では意味を持ちません。実装は `[...]` 内に `%25` を含む任意の URL を拒否しなければなりません(MUST)。 3. **userinfo を除去。** `user:pass@host` → `host`。次の authority 形状は不正な形式で拒否されなければなりません(MUST) — 生成者はそれらを発してはならず(MUST NOT)、比較者はそれらを拒否しなければなりません(MUST): * userinfo だがホストなし: `https://user@/p` * ホストがまったくない: `https:///p`、`https://:443/p` * 閉じブラケットが欠けたブラケット付きホスト: `https://[::1/p` * ブラケット外の素の IPv6 アドレス: `https://fe80::1/p` 4. **デフォルトポートを除去。** https には `:443`、http には `:80`。他のすべてのポートを保持(`:8443`)。 5. **パスに `remove_dot_segments`(RFC 3986 §5.2.4)を適用するが、連続するスラッシュはバイト単位で保持。** `/a//b` は `/a//b` のままでなければなりません(MUST) — RFC 3986 はそれらを折り畳むことを義務付けず、保持することでパス混同攻撃サーフェスを閉じます: 一方が `/admin//foo` → `/admin/foo` を折り畳み、他方が `/admin//foo` を異なる(潜在的により無防備な)ハンドラーにディスパッチする場合、攻撃者は 1 つの URL に署名または認可し、別のものを実行できます。URL ベースの認可をデプロイするサーバーは、影響を受けるルートでスラッシュ折り畳みを無効にしなければなりません(MUST)(`nginx: merge_slashes off;`、Express: 事前正規化しない、Go 1.22+ `http.ServeMux`: 受信パスを保持する明示的な `http.Handler` を使う)。パスが空でありかつ authority が存在する場合、`/` を代入します(RFC 3986 §6.2.3。`https://host?x=1` → `https://host/?x=1`)。 6. **パーセントエンコーディングを正規化。** 16 進数を大文字化(`%2f` → `%2F`)。パーセントエンコードされた unreserved 文字をデコード(RFC 3986 §2.3 に従い `ALPHA / DIGIT / "-" / "." / "_" / "~"` なので `%7E` → `~`、`%2Dfoo` → `-foo`、`%41` → `A`)。reserved 文字はパーセントエンコードされたままにする(`%3A` は `%3A` のまま、`%2F` は `%2F` のまま)。パーセントエンコーディング正規化はパスとクエリに適用されます。ゾーン識別子はステップ 2 で拒否されるのでこのステップに到達しません。 7. **クエリ文字列をバイト単位で保持。** パラメーターを並べ替えてはならず(MUST NOT)、再エンコードしてはならず(MUST NOT)、`+` をスペースとして解釈してはなりません(MUST NOT)。空のクエリを伴う末尾の `?` は保持されます(`https://host/p?` は `https://host/p?` に正準化され、`https://host/p` とは別)。`?` のない URL は `?` なしのままです。クエリパラメーター順のみが異なる 2 つの URL は、等価ではなく異なる正準形式です。 8. **フラグメントを除去。** フラグメントは識別子比較に決して参加せず、RFC 9421 §2.2.2 に従いワイヤー上で送られません。 8 ステップすべての後、比較はバイト単位です。実装は比較の前に追加の変換を適用してはなりません(MUST NOT)。 ## どこに適用されるか | Surface | Comparison | Reference | | --------------------------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | リクエスト署名 | `@target-uri` 正準出力が署名・検証される | [署名付きリクエスト(トランスポート層)](/docs/building/by-layer/L1/security#署名付きリクエストトランスポート層) | | TMP セラー認可 | `seller_agent.agent_url` 対 `authorized_agents[].url` | [TMP Sync-Time Validation](/docs/trusted-match/specification#sync-time-validation) | | TMP プロバイダー解決 | `ProviderEntry.agent_url` 対ルーターの登録済みプロバイダーエンドポイント | [TMP Product Integration](/docs/trusted-match/specification#product-integration) | | `adagents.json` ルックアップ | 「このエージェントはこのプロパティに認可されているか?」を問う任意の呼び出し元 | [adagents.json スキーマ](https://adcontextprotocol.org/schemas/v3/adagents.json) | | `format-id` 解決 | `format-id.agent_url` 対エージェントがそのフォーマットに公開する URL | [format-id スキーマ](https://adcontextprotocol.org/schemas/v3/core/format-id.json) | | `adagents.json` `authoritative_location` 間接参照 | ポインターをたどる。ターゲット URL は同じ方法で正準化されなければならない(MUST) | [Managed networks](/docs/governance/property/managed-networks#security-considerations) | | 来歴検証者許可リスト | `verify_agent.agent_url` 対 `creative_policy.accepted_verifiers[].agent_url` | [Provenance Verification](/docs/governance/creative/provenance-verification#the-verifier-contract-seller-publishes-buyer-represents-seller-confirms) | | URL プライマリキーを持つ任意のレジストリ | 正準形式がキー。生の入力はキーでない | - | ## 署名プロファイル拡張 [リクエスト署名プロファイル](/docs/building/by-layer/L1/security#署名付きリクエストトランスポート層) は、このアルゴリズムの上にトランスポート固有のルールを重ねます: * `@authority` は正準化された authority から導出され、同じ正準化の後に HTTP/2 `:authority` 疑似ヘッダー(または受信した HTTP/1.1 `Host` ヘッダー)と比較されます。非署名の呼び出し元は URL のみから `@authority` を導出します。 * 不正な形式の authority は、署名パスで `request_target_uri_malformed` で拒否されます。非署名の呼び出し元は独自の認可失敗コードを使います(例: TMP には `seller_not_authorized`)。 * 受信した HTTP/2 リクエストに `:authority` と `Host` の両方が存在するとき、署名プロファイルは正準化後のバイト等価を要求します。これは署名固有のゲートです。なぜなら HTTP/1.1 `Host` は転送中に書き換えられうるからです。 ## 適合性ベクター [`canonicalization.json`](https://adcontextprotocol.org/compliance/latest/test-vectors/request-signing/canonicalization.json) セットは、固定された `{ input_url, expected_target_uri, expected_authority }` トリプルと、不正な形式の authority 拒否ケースで、上記のすべてのルールを実行します。非署名の呼び出し元は `expected_target_uri` のみと比較します — `expected_authority` は署名プロファイルが使う HTTP ヘッダー由来の形式です。上の表のサーフェスのいずれかを実装する SDK は、すべてのコミットでこのセットを実行すべきです(SHOULD)。正準化の分岐は、本番の相互運用バグがサーフェスするまで静かです。 ## よくある落とし穴 * **ToASCII の前に IDN を ASCII 小文字化。** `Bücher.example` を ASCII で小文字化 → `bücher.example` だが、UTS-46 正しいパスは元のバイトを処理しなければならない。TypeScript `url.domainToASCII`、Go `golang.org/x/net/idna`、Python の `idna` パッケージ(IDNA2003 である `str.encode('idna')` ではない)はモードのデフォルトで分岐する。上記の 4 つのフラグを持つ UTS-46 Nontransitional にピン留めする。 * **連続するスラッシュの折り畳み。** `/admin//foo` と `/admin/foo` は異なる正準形式。折り畳む生成者と折り畳まない比較者(またはその逆)はパス混同攻撃を開く。 * **クエリの再エンコード。** クエリ文字列の正規化は魅力的に見えるが禁止。`?x=1&y=2` と `?y=2&x=1` は異なる正準形式。 * **空のクエリを伴う末尾の `?`。** `https://host/p?` と `https://host/p` は異なる。生成者が送ったものを保持する。`adagents.json` や類似のレジストリに URL を登録するパブリッシャーは、空クエリ形式を意図しない限り末尾の `?` なしで貼り付けるべき。 * **フラグメント除去を忘れる。** フラグメントは識別子比較に決して参加しない。 * **`http://` と `https://` の混在。** スキームは強制ではなく保持される。`authorized_agents[].url` を登録するパブリッシャーは、パブリックインターネットで到達可能を意図するものすべてに `https://` を使わなければならない(MUST) — `http://` エントリは `https://` 呼び出し元との一致に失敗し、その逆も同様で、非 HTTPS URL はトランスポート完全性保証を持たない。 # v2 サンセット Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/v2-sunset AdCP v2 は 3.0 GA 時点でサポート外。2026 年 8 月 1 日(UTC)までセキュリティのみのパッチ、それ以降は完全非推奨。今すぐ 3.0 への移行を始める。 **AdCP v2 はサポートされておらず、AAO ネットワーク上の相互運用可能な本番に安全ではありません。** v2 は、AAO アーキテクチャ委員会がライブのマルチパーティキャンペーンに不可欠と見なすアカウントとガバナンスの保護に先行します。最後の v2 リリースは 2026 年 1 月の 2.5.3 でした。今すぐ [3.0](/docs/reference/whats-new-in-v3) への移行を始めてください。 ## タイムライン | Date (UTC) | Event | | ---------------------- | ---------------------------------------------- | | **2026 年 4 月**(3.0 GA) | v2 は AAO によってサポートされなくなる。セキュリティのみのパッチ開始。機能作業なし。 | | **2026 年 8 月 1 日** | v2 完全非推奨。それ以上のパッチなし。リファレンスドキュメントアーカイブ。 | v2 サンセットは 4.0 に結びついていません。v2 は独自のスケジュールで EOL に達します。なぜなら 2.5 は初期の予備的なバージョンで — 採用は小規模のままで、アーキテクチャ委員会はメジャーな再設計なしにバックポートできない、v2 に欠けている保護を特定したからです。v2 のより短いウィンドウは、[12 か月の前メジャーサポートコミットメント](/docs/reference/versioning#support-window-for-previous-major) の文書化された例外です。将来のメジャーはこの例外を呼び出しません。 ## 「サポート外」が何を意味するか 3.0 GA 時点で: * **機能作業なし。** 2.5.3 が最後の v2 リリース。セキュリティ勧告以外、2.x ラインでのそれ以上のマイナーまたはパッチ作業なし。 * **AAO 認証なし。** 認証は v3.0 以降を必要とする。 * **AAO レジストリで検証されない。** Verified セラーとエージェントステータスは v3.x を必要とする。v2 のみのエージェントは登録できるが検証できない。 * **凍結されたスキーマ。** v2 スキーマは 2.5.3 時点で凍結され、既存のスキーマ URL に残る。新しいフィールド、タスク、プロトコルなし。 2026 年 8 月 1 日(UTC)以降、v2 は完全に非推奨です: セキュリティパッチが停止しリファレンスドキュメントがアーカイブされます。2.5.3 スキーマ URL は既存の統合が黙って壊れないよう解決し続けますが、それらのスキーマはそれ以上の更新を受け取りません。 ## なぜ v2 が相互運用可能な本番に安全でないか v2 は、3.0 が AAO ネットワーク上のライブマルチパーティキャンペーンに不可欠として扱う保護を欠いています: * **アカウントプロトコルなし。** バイヤーとセラーはアカウントスコープ、オペレーター認可、バイヤーアイデンティティ解決を交渉できない。 * **ガバナンスなし。** 構造化されたコンテンツ標準、オーディエンスバイアス検証、プロパティリストガバナンスなし。 * **キャンペーンガバナンスなし。** 署名付きプロポーザル、承認ワークフロー、プロポーザルライフサイクルなし。 * **限定的な最適化と測定。** 構造化された最適化目標、イベントソースヘルス、ストリーミング/音声配信メトリクスなし。 AAO ネットワークで v2 を実行することは、これらの保護なしで実行することを意味します。独自のガバナンスとアイデンティティ層を持つプライベートデプロイはオペレーターの判断です — しかし AAO ネットワークは、サポートされる本番トラフィックについて v2 をスコープ外として扱います。 ## 何をすべきか ### v2 エージェントを稼働している場合 1. [v3 の新機能](/docs/reference/whats-new-in-v3) を読む。 2. [v2→v3 移行ガイド](/docs/reference/migration) を進める。 3. [ストーリーボードテスト](/docs/reference/migration/v3-readiness) で検証する。 4. 2026 年 8 月 1 日(UTC)より前に移行を完了する。 ### v2 セラーと統合するバイヤーの場合 1. どのセラーが v2 のみかを棚卸しする。 2. 2026 年 8 月 1 日(UTC)の非推奨を通知し、[移行ガイド](/docs/reference/migration) を共有する。 3. v3 セラーが利用可能になるにつれトラフィックをシフトする。 ### If you use the AAO registry Verified セラーとエージェントステータスは v3.x を必要とします。v2 のみのエージェントは登録できますが検証の対象になりません。レジストリが verified-default ディスカバリーを展開するにつれ、v2 のみのエントリはデフォルトでサーフェスされません。未検証エントリを返すエンドポイントは、それを必要とするオペレーターのために利用可能なままです。 ## 移行中のデュアルサポート 移行中盤のセラーは、`get_adcp_capabilities` と `major_versions` 配列を使って一時的に両バージョンをサポートできます — [v2 と v3 を並行して稼働する](/docs/reference/migration#running-v2-and-v3-side-by-side) を参照。デュアルサポートは移行ツールであり、長期的な姿勢ではありません。2026 年 8 月 1 日(UTC)以降、v3 のみが必須の設定です。 ## 関連 * **[v3 の新機能](/docs/reference/whats-new-in-v3)** — 3.0 が追加するものの機能ごとのサマリー * **[移行ガイド](/docs/reference/migration)** — すべてのプロトコル領域の破壊的変更と深掘りページ * **[v3 レディネスチェックリスト](/docs/reference/migration/v3-readiness)** — ストーリーボードテストの 8 つの最小要件 * **[バージョニングとガバナンス](/docs/reference/versioning)** — 3.x 安定性保証とリリースケイデンス # プロトコル tarball の検証 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/verifying-protocol-tarballs cosign keyless と Sigstore 透明性ログで AdCP プロトコルバンドルの発行者アイデンティティを検証する。 すべての AdCP リリースは、3 つのサイドカーとともに `{version}.tgz` バンドル(そのバージョンの完全なスキーマ + コンプライアンス + OpenAPI ツリー)を公開します: | File | Role | | ---------------------- | ---------------------- | | `{version}.tgz.sha256` | SHA-256 チェックサム、転送中の完全性 | | `{version}.tgz.sig` | Sigstore 分離署名 | | `{version}.tgz.crt` | Fulcio 発行の署名証明書 | SHA-256 サイドカーは tarball と同じオリジンに存在するため、転送中の改ざんからのみ保護します。`.sig` + `.crt` ペアは、バンドルが AdCP リリースワークフロー自体から来たこと、そしてホストが侵害されても悪意あるものとすり替えられなかったことを証明します。 このページは、それらの署名を正しく検証する方法をカバーします。SDK ユーザー(`@adcp/sdk`、`adcp-client-python`、`adcp-go`)は、すべての `sync-schemas` / `download.sh` 実行でこの検証を無料で得ます。バンドルを直接消費している場合 — CI パイプラインで特定バージョンをピン留め、異なる言語から取り込み、新規採用者を実装 — 読み進めてください。 ## 信頼モデル AdCP は **Sigstore keyless 署名** を使います。長寿命の秘密鍵はありません。リリース時に: 1. `adcontextprotocol/adcp` の `release.yml` ワークフローが GitHub Actions ランナーで実行される。 2. ランナーは、実行を生成したワークフローと ref を識別するサブジェクトを持つ短命の OIDC トークンを鋳造する。 3. `cosign sign-blob --yes` が、その OIDC トークンを Sigstore の Fulcio CA で短命の X.509 証明書と交換し、証明書の一時秘密鍵を使って分離署名を生成する。 4. 署名、証明書、透明性ログエントリが Sigstore の Rekor 公開ログに着地する。 5. リリースパイプラインが `.sig` と `.crt` を tarball の隣にコミットし、GitHub Release にアップロードする。 コンシューマー側の検証は次に **2 つのバインドプロパティ** を確認します: * **署名の真正性** — `.sig` が `.crt` が証明する秘密鍵によって生成された。標準の Sigstore 数学。AdCP 固有ではない。 * **アイデンティティバインド** — `.crt` のサブジェクトが AdCP リリースワークフローを具体的に名指しし、発行者は GitHub Actions の OIDC プロバイダーである。これが AdCP 固有の部分。 両方が成立すれば、AdCP リリースワークフロー実行がこの正確な tarball を生成したという証明を持ちます — `adcontextprotocol.org` 自体を信頼せずにエンドツーエンドで証明可能。 ## 推奨される `cosign verify-blob` 呼び出し ```bash theme={null} # tarball + サイドカーをダウンロード curl -OL https://adcontextprotocol.org/protocol/3.0.3.tgz curl -OL https://adcontextprotocol.org/protocol/3.0.3.tgz.sha256 curl -OL https://adcontextprotocol.org/protocol/3.0.3.tgz.sig curl -OL https://adcontextprotocol.org/protocol/3.0.3.tgz.crt # まずチェックサムを検証(安価、転送中の破損を捕まえる) shasum -a 256 -c 3.0.3.tgz.sha256 # Sigstore アイデンティティを検証(発行者を証明) cosign verify-blob \ --signature 3.0.3.tgz.sig \ --certificate 3.0.3.tgz.crt \ --certificate-identity-regexp '^https://github\.com/adcontextprotocol/adcp/\.github/workflows/release\.yml@refs/(heads|tags)/.*$' \ --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ 3.0.3.tgz ``` 抽出前に両方がゼロで終了しなければなりません。`cosign verify-blob` は、SHA が一致し TLS が有効でも、署名が AdCP リリースワークフロー以外のものによって作られた場合、非ゼロを返します。 ## アイデンティティ正規表現の説明 ``` ^https://github\.com/adcontextprotocol/adcp/\.github/workflows/release\.yml@refs/(heads|tags)/.*$ ``` 3 つの部分が重要です: * `https://github.com/adcontextprotocol/adcp/.github/workflows/release.yml` — ワークフローファイルパス。これが証明書を AdCP 固有にするものです。異なるリポジトリのワークフロー、またはこのリポジトリの異なるワークフローファイルは一致しません。 * `refs/(heads|tags)/.*` — ワークフローが実行された ref。ブランチ ref が今日使われるものです(cosign は push トリガーの実行中に署名するため、OIDC サブジェクトは `release.yml@refs/heads/`)。タグ ref は、将来のタグ後再署名フローの前方互換です。 * `--certificate-oidc-issuer 'https://token.actions.githubusercontent.com'` — OIDC 発行者は GitHub Actions 自体でなければなりません。正しいリポジトリとワークフローパスでも、非 GitHub-Actions 発行者はこのチェックに失敗します。 ### なぜ正確な ref ではなく正規表現か この正規表現の最初のバージョンは `^...refs/heads/(main|2\.6\.x)$` — リリースブランチのリテラル許可リストでした。それらのリリースが `refs/heads/3.0.x`(3.0 ラインがカットされたときに追加された保守ブランチ)に移動したとき、v3.0.1+ を黙って拒否しました。任意の新しい保守ブランチは、各 SDK がパッチされるまですべてのコンシューマーで検証を壊しました。 ブランチコンポーネントのワイルドカード化は信頼モデルを弱めません: 上流の `release.yml` ワークフロー自身の `on.push.branches` 許可リスト(現在 `main`、`3.0.x`、`2.6.x`)が、そもそもどの ref が署名を生成できるかを決定します。すべてのコンシューマーの正規表現でそのリストをミラーリングすることは、防御を追加しない保守負債でした。 ## 過去のリリースの証明書サブジェクト 参考のため、各リリースの証明書サブジェクトはこうでした: | Release | Triggering ref | Cert subject(サブジェクトのみ、完全 URL プレフィックス省略) | | ------- | -------------------- | --------------------------------------- | | v3.0.0 | `main`(初期 3.0 カット) | `release.yml@refs/heads/main` | | v3.0.1 | `3.0.x`(ラインがカットされた後) | `release.yml@refs/heads/3.0.x` | | v3.0.2 | `3.0.x` | `release.yml@refs/heads/3.0.x` | | v3.0.3 | `3.0.x` | `release.yml@refs/heads/3.0.x` | 将来の保守ブランチ(例: `2.7.x`)は、コンシューマーの変更を必要とせずに `release.yml@refs/heads/2.7.x` を追加します。 ## 検証が利用できないとき 一部のリリースは正当に `.sig`/`.crt` なしで出荷されます: * **v3.0.0 以前(cosign 署名がまだ配線されていなかった)。** チェックサムのみとして扱う。SDK は失敗ではなく完全性のみの検証に劣化する。 * **帯域外の再公開。** tarball が `release.yml` ワークフロー外で再生成される場合(例: 一回限りの再ビルド)、Sigstore アイデンティティを持たない。cosign サイドカーは欠如する。信頼できないものとして扱う。 コンシューマーは「サイドカー欠如」(チェックサムのみに劣化)と「サイドカー存在だが検証失敗」(ハード失敗)を区別すべきです。それらを混同しないでください — 存在するが無効な署名は、署名がまったくないよりも強い否定的シグナルです。 ## SDK の動作 3 つすべてのファーストパーティ SDK は、プロトコルバンドルを取得するときこの正規表現を使います: | SDK | Verifies via | | ----------------------- | ----------------------------------------------------------------- | | `@adcp/sdk`(TypeScript) | `scripts/sync-schemas.ts` がサイドカー存在時に `cosign verify-blob` をシェルアウト | | `adcp-client-python` | `scripts/sync_schemas.py` が同じことをする | | `adcp-go` | `adcp/schemas/download.sh` が同じことをする | フォースパーティ SDK を保守する場合、上の正規表現をミラーリングしてください。リテラル許可リストパターンを避けてください — 新しい保守ブランチがカットされるたびに腐ります。 ## 生成者側の詳細 仕様ワークフロー自体に貢献する場合: cosign 署名は `release.yml` 内の `npm run version`(`sign-protocol-tarball.sh` ステップから連鎖)中に起こります。OIDC トークンは署名時に鋳造されるため、証明書サブジェクトはそのワークフロー実行のトリガー ref を反映します。タグベースの署名は次のいずれかを必要とします: * `release: published` で実行され、タグ後 OIDC サブジェクトを使って tarball を再署名する 2 番目のワークフロー、または * `changeset tag` の後、`refs/tags/*` がアクティブな ref であるコンテキスト内で署名が起こるようリリースパイプラインを再構築する。 今日のブランチから署名する形状は意図的です — すべてのコンシューマーがタグ対ブランチのアイデンティティについて推論せずに単一の正準アーティファクトを検証できます。正規表現の `refs/(heads|tags)/.*` は、それが変わる場合に備えた前方互換です。 ## 関連項目 * [スキーマ、コンプライアンスバンドル、SDK](/docs/building/schemas-and-sdks) — これらのサイドカーがより広いバンドル取得フローで記述される場所 * [Sigstore ドキュメント](https://docs.sigstore.dev/) — keyless 署名、透明性ログ、脅威モデル * [`adcp#2273`](https://github.com/adcontextprotocol/adcp/issues/2273) — cosign 署名を導入した変更 # バージョニングとガバナンス Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/versioning AdCP がリリースをどうバージョン管理し、スキーマ変更を管理し、プロトコルの進化を統治するか。 AdCP は 3 ティアの番号付けシステムを使います: **VERSION.RELEASE.PATCH**(例: 3.1.2)。 *** ## Version tiers | Tier | Example | Description | | ----------- | ----------- | ---------------------------------------------------------------------------------------------------------------- | | **Version** | 3.0 → 4.0 | プロトコルの新しい世代。単一の機能ではなく、前のサイクルにわたって蓄積されたアーキテクチャ変更を反映。エコシステムのためのクリーンなベースラインを示す。 | | **Release** | 3.0 → 3.1 | 新しいフィールド、新しいケイパビリティ、またはプロトコルのアーキテクチャを変えない小さなスキーマ変更。既存の実装に周縁で影響することがある。 | | **Patch** | 3.1 → 3.1.1 | バグ修正、明確化、訂正。安定 AdCP サーフェスについて常にアップグレードが安全。パッチは安定仕様から逸脱した動作を訂正するが、新しい安定ケイパビリティを導入しない。実験的のみのパッチは下記の実験的予告コントラクトに従う。 | ### リリースと新バージョンを区別するものは何か? 新バージョン(4.0)は、変更がアーキテクチャ的なとき、前バージョンからの累積ドリフトがクリーンなベースラインがエコシステムに役立つほど大きいとき、または新世代を示す戦略的理由があるときに出荷されます。 リリース(3.x)は周縁でスキーマを変えられます — フィールドの required/optional ステータス、文書化されたエイリアス付きのリネームされたフィールド、厳格化された検証、同じリリースで置き換えが利用可能なオブジェクトの非推奨化。これらはビルダーがターゲットを絞った更新で吸収できる変更です。 ### 前方互換性 3.0 に対して構築された実装は、任意の 3.x リリースに対して機能し続けます。バージョン内のスキーマ変更は、書き直しではなくターゲットを絞った更新で吸収されるよう設計されています。 *** ## Version negotiation バイヤーとセラーは、すべてのリクエストとレスポンスで **リリース精度**(VERSION.RELEASE、例: `"3.0"`、`"3.1"`)で交渉します。リリース精度のピン留めは、バイヤー SDK がどのスキーマに対して検証するかを選べるようにし、セラーがバイヤーの期待するコントラクトに合わせて形作られたレスポンスを返せるようにします(Stripe モデル)。 > ワイヤーを直接実装する社内クライアントは、3.x を通じて `adcp_major_version` に留まれます。`adcp_version` は 4.0 までオプトインです。ケイデンスについては [Migration timeline](#migration-timeline) を参照。 ### Bidirectional negotiation (3.1+) 1. **セラーがアドバタイズ**: `get_adcp_capabilities` レスポンスの `adcp.supported_versions` が、セラーが話すすべてのリリース精度バージョンをリストする(例: `["3.0", "3.1"]`)。`adcp.build_version`(例: `"3.1.2+vendor.42"`)はインシデントトリアージ用の任意の助言的メタデータ — ワイヤーコントラクトの一部ではない。 2. **バイヤーが宣言**: すべてのリクエストの `adcp_version`(リリース精度文字列)が、バイヤーのペイロードがどのリリースに準拠するかをセラーに伝える。3.x を通じてバイヤーは `adcp_major_version` も発すべき(SHOULD)で、整数のみを読むレガシー 3.x セラーが正しく交渉し続けられるようにする。 3. **サーバーが解決**: * 完全一致 → そのリリースで提供。 * 同じメジャー、プレリリースとリリースの両方が存在、バイヤーがリリースをピン → 完全一致が勝つ。サーバーは黙ってプレリリースにダウンシフトしてはならない(MUST NOT)。(サーバー `["3.1-beta", "3.1"]` + ピン `"3.1"` → `"3.1"` を提供。) * 同じメジャー、完全一致なし、少なくとも 1 つのサーバーリリース ≤ バイヤーのピン → ピン以下の最高のサポートリリースにダウンシフト。「サーバーの最大 \< ピン」とギャップケース(例: サーバー `["3.0", "3.2"]` + ピン `"3.1"` → `"3.0"` を提供)の両方をカバー。 * 同じメジャー、バイヤーのピン以下のサーバーリリースなし(sub-min: すべてのサポートリリースが厳密に大きい) → `error.data` に `supported_versions` を伴う `VERSION_UNSUPPORTED` を返す。 * 異なるメジャー → `error.data` に `supported_versions` を伴う `VERSION_UNSUPPORTED` を返す。 * 省略 → サーバーはそのデフォルトリリースを使う(または、`adcp_major_version` のみが送られた場合、そのメジャーの最高のサポートリリース)。 4. **サーバーがエコー**: すべてのレスポンスの `adcp_version` が、セラーが実際に提供したリリースをバイヤーに伝える。エコーされる値は **提供されたリリース** であり、セラー自身の最新リリースではない — 3.0 で 3.0 バイヤーに提供する 3.1 セラーは `"3.0"` をエコーする。バイヤーは、自身のピンではなくそのリリースのスキーマに対してレスポンスを検証すべき(SHOULD)。 レスポンスが `adcp_version` を省略するとき(フィールドを読まないレガシー 3.0 セラー — `additionalProperties: true` がそこでそれを不可視にする)、バイヤーは自身のピンに対して検証し、ケイパビリティ推論についてはセラーを 3.0 のみとして扱うべき(SHOULD)。この場合、バイヤーの SDK はコンストラクターのピンにフォールバックする。 **`get_adcp_capabilities` が完全に欠けているとき。** `get_adcp_capabilities` を公開しないセラー(そのツールが MCP ツールリスト、A2A スキルリスト、REST ツールインデックスにアドバタイズされない)は v3 以前の実装です — ツール自体が v3 以降。この場合バイヤーは **v2** を推論し、リクエストを v2 ワイヤー形状アダプター経由でルーティングし、このセラーについてリトライ安全保証が不明であること(`replay_ttl_seconds` 宣言が利用できない)を示す一度限りの助言的警告を発すべき(SHOULD)。これは意図的な fail-open です: このシグナルで fail-closed にすると、最も一般的な採用パス — v2 ツールを出荷し v3 ディスカバリーを決して実装しなかったセラー — をブロックします。バイヤーは `get_adcp_capabilities` の欠如を肯定的な v2 適合性シグナルとして使ってはならない(MUST NOT)。それは純粋に「v3 ディスカバリーサーフェスが欠けている」からの推論です。冪等性、署名付きリクエスト、その他の v3 信頼プリミティブは不明として扱わなければならず(MUST)、それらの保証を必要とするバイヤーは、セラーがそれらをアドバタイズできないときアプリケーション層で fail-closed にしなければならない(MUST)。fail-open ルールはワイヤー形状アダプターにのみ適用され、ワイヤー形状が運ぶ信頼プリミティブには適用されません。 これにより、バイヤーとセラーは独立してアップグレードできます — セラーはマルチリリースサポートを宣言でき、バイヤーは移行を調整せずに特定のリリースにピン留めできます。 #### Pre-release pins プレリリースタグ(`"3.1-beta"`、`"3.1-rc.1"`)は `supported_versions` に対して **正確に** マッチされます。範囲解決されません: `["3.0", "3.1"]` をリストするサーバー(リストに beta なし)に対して `"3.1-beta"` をピンするバイヤーは、`"3.0"` へのダウンシフトではなく `VERSION_UNSUPPORTED` を得ます。サーバーはリリースピンをプレリリースにダウンシフトしてはなりません(`"3.1"` → `"3.1-beta"`)(MUST NOT)。両方がアドバタイズされるとき、リリースが完全一致で勝ちます。 #### `VERSION_UNSUPPORTED` error data セラーがピンを尊重できないとき、`error.data` は [`error-details/version-unsupported.json`](https://github.com/adcontextprotocol/adcp/blob/main/static/schemas/source/error-details/version-unsupported.json) に従います: ```json theme={null} { "adcp_version": "4.0", "adcp_major_version": 4, "supported_versions": ["3.0", "3.1"], "supported_majors": [3], "build_version": "3.1.2+vendor.42" } ``` `supported_versions` は権威的 — クライアントはこのリストから値を選んでリトライすべき(SHOULD)。SDK は黙ってリトライするのではなく型付きエラーを上げるべき(SHOULD)。自動ダウンシフトは呼び出し元の下でワイヤー形状を変えます。 ### Patches are not negotiated 3 ティアモデルに従い、パッチは安定 AdCP コントラクトの変更を導入しません — 安定サーフェスのバグ修正と明確化です。ワイヤーネゴシエーションフィールド(`adcp_version`)はリリース精度のみを使います。サーバーは運用可視性のために任意の `build_version` ケイパビリティ経由でビルドパッチをサーフェスしてもよい(MAY)が、バイヤーはそれをネゴシエーションに使ってはならない(MUST NOT)。実験的のみのパッチ変更は、パッチレベルのネゴシエーションではなく、`experimental_features` 宣言と下記の予告コントラクトによって統治されます。 `build_version` は、パッチコンポーネントが投入された有効な semver 文字列でなければならず(MUST)、[semver §9–§10](https://semver.org/#spec-item-9) に従って任意でプレリリースとビルドメタデータセグメントで拡張されます。例: `"3.1.2"`、`"3.1.2+scope3.deploy.4821"`、`"3.1.0-beta.3+sha.a1b2c3d"`。 `adcp_version` ワイヤー形状は `MAJOR.MINOR` または `MAJOR.MINOR-PRERELEASE` です。**パッチコンポーネントはワイヤー上で有効でない**、プレリリースタグと並んでいてもです。完全な semver(`"3.1.0-beta.1"`)を使って内部でバンドルをキー付けする SDK は、発する前にリリース精度(`"3.1-beta.1"`)に正規化しなければなりません(MUST)。`"3.1.2"`、`"3.1.0-beta.1"`、`"v3.1"`、`"3"` はすべて無効なワイヤー値で、検証によって拒否されます。 **ワイヤーフィールドをバンドルメタデータと混同しないでください。** スキーマレジストリ、tarball マニフェスト、コンプライアンスインデックス、`/protocol/` HTTP ディスカバリーエンドポイントはすべて、完全 semver 精度で `published_version` フィールド(例: `"3.1.0-beta.1"`)を公開します — それは公開されたアーティファクトのバージョンであり、ワイヤー値ではありません。`@adcp/sdk.ComplianceIndex` 互換性のため、レガシー `adcp_version` エイリアスも 3.x を通じてそれらのメタオブジェクトに存在し、4.0 でサンセットします。**メタフィールド値をワイヤー上で決して発しない** — 先にリリース精度に正規化してください。ディスカバリー URL については [schemas-and-sdks](/docs/building/schemas-and-sdks#version-discovery) を参照。 ### Major-precision negotiation (deprecated, kept through 3.x) レガシー `adcp_major_version`(リクエストごとの整数)と `adcp.major_versions`(ケイパビリティ上の整数配列)は、後方互換性のため 3.x を通じて機能し続けます。新しいバイヤーとセラーはリリース精度ネゴシエーションを優先すべき(SHOULD)。両方のレガシーフィールドは 4.0 で削除されます。 **3.x 中のデュアル発行:** `adcp_version` を発するバイヤーは、同じリクエストで `adcp_major_version`(そのピンのメジャーコンポーネント付き)も発すべき(SHOULD)。これにより整数のみを読むレガシー 3.x セラーが正しく交渉し続けられます。両方のフィールドが存在しメジャーレベルで不一致のとき、サーバーはリクエストを不正な形式として扱い `VERSION_UNSUPPORTED` を返さなければなりません(MUST)。 メジャー精度のみが提供されるとき: * バイヤーが `adcp_major_version: 3` を送る → サーバーはメジャー 3 の最高のサポートリリースを提供。 * サーバーが `adcp.major_versions: [3]` を発する → バイヤーはメジャー精度で交渉できるがリリースレベルの情報を失う。 ### Migration timeline 仕様は 3.x を通じて両側で SHOULD に留まります — フィールドがメジャー内で optional → required に卒業しないという 3.x 安定性保証と一致します。AdCP コンプライアンスグレーダーが 3.x 内で採用圧力を担います: 3.2 で認証を望むセラーはレスポンスエコーを出荷します。 | Phase | Spec — buyer | Spec — seller | Compliance grader | | -------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------- | | **3.1(加算的出荷)** | `adcp_version` を発すべき(`adcp_major_version` ミラー付き)。 | `adcp_version` を尊重しエコーすべき。ケイパビリティに `supported_versions` を発すべき。 | 助言的: リクエストとレスポンスの存在をレポート。 | | **3.2** | (3.1 から変更なし) | (3.1 から変更なし) | セラーが `adcp_version` をエコーしないか、ケイパビリティに `supported_versions` を発しないときブロッキング失敗。 | | **4.0** | `adcp_version` を発しなければならない(MUST)。`adcp_major_version` 削除。 | `adcp_version` を尊重しエコーしなければならない(MUST)。`adcp.major_versions` と `extensions.adcp.adcp_version` 削除。 | 欠如でブロッキング失敗。レガシーフィールド拒否。 | まだ SDK に移行していない社内クライアントは、3.x を通じて `adcp_major_version` のみを送り続けられます — サーバーがそれにフォールバックします。`adcp_version` の追加は 3.2 で認証可能な動作になります(グレーダー経由)。仕様自体は 4.0 までそれを要求しません。 ### Relationship to MCP `protocolVersion` MCP は `initialize` ハンドシェイクに独自の `protocolVersion` フィールドを運びます(例: `"2025-06-18"`)。そのハンドシェイクは **MCP ワイヤー** をバージョン管理します — JSON-RPC フレーミング、トランスポートセマンティクス。AdCP `adcp_version` は **AdCP ペイロード** をバージョン管理します — `params` と `result` コンテンツのスキーマ。2 つは独立: MCP-2025-06-18 サーバーは AdCP 3.0 *または* 3.1 を話せ、AdCP `"3.1"` をピンする MCP クライアントは、MCP ハンドシェイクが成功しても 3.0 のみを話すサーバーに対して `VERSION_UNSUPPORTED` で失敗します。A2A には MCP `initialize` に相当するものがなく、それが `adcp_version` がペイロードに乗る理由です(両トランスポートがそれを運べる場所)。 ### Features over versions バージョンネゴシエーションはメジャーなアーキテクチャ境界を扱います。機能レベルの互換性については、代わりにケイパビリティモデルを使います。セラーは特定の機能、ターゲティングシステム、実行統合、拡張を `get_adcp_capabilities` で宣言します。バイヤーは必要なケイパビリティを確認し、それらが存在すれば続行します。 特定のメジャーバージョンのすべてのセラーがすべての機能をサポートするわけではありません。すべてのバイヤーがすべての機能を必要とするわけではありません。ケイパビリティコントラクトはこうです: **宣言されたら、セラーはそれを尊重しなければならない(MUST)**。これはバージョン番号だけよりも細かい粒度の互換性を与えます。 完全なケイパビリティリファレンスについては [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) を参照。 *** ## Schema changes in releases: scope and limits スキーマ変更は、次の条件の下でリリースで受け入れられます: **リリースのスコープ内:** * フィールドを optional から required に変更(またはその逆) * 同じリリースで文書化されたエイリアスと移行ノート付きでフィールドをリネーム * 既存パラメーターの検証ルールを厳格化(before/after の例で文書化) * 同じリリースで置き換えが出荷されるときオブジェクトやメソッドを非推奨化 **スコープ外 — バージョンレベルの変更のみ:** * プロトコルのアーキテクチャ的または構造的な再設計 * 事前の非推奨リリースなしにフィールドやメソッドを削除 * 認証、トランスポート、コアセキュリティ要件の変更 * 基本的な動作セマンティクスを変える変更 ### Deprecation policy 非推奨予告は、任意の機能が削除される前に少なくとも **6 か月** 公開されます。非推奨機能は、非推奨化後少なくとも 1 つの完全なリリースサイクル機能し続け、同じメジャーバージョン内で決して削除されません — 3.x で非推奨化された機能は、早くても 4.0 まで削除されません。 スキーマ変更を伴うすべてのリリースは、チェンジログ、リリースノート、インラインドキュメントで言及されます。スキーマ変更を伴うすべてのリリースは移行ガイドとともに出荷されます。 [実験的サーフェス](/docs/reference/experimental-status) は、6 週間の別個のより速い予告ウィンドウの下で動作します。上記の非推奨ポリシーは安定サーフェスにのみ適用されます。 ### Spec, registry, conformance, and experimental changes 上記のリリース対パッチのルールは **仕様レベルのアーティファクト** に適用されます — `static/schemas/source/` の下の JSON Schema、`docs/` の規範的な散文、プロトコルタスク定義。これらは、エージェントがワイヤー上で実装するもの、バイヤーが相互運用のために依存するものです。 AgenticAdvertising.org レジストリ API とフィードトランスポートは、`adcontextprotocol` とは別にバージョン管理されます。レジストリ OpenAPI ドキュメント、レジストリ公開ポリシーファイル、レジストリフィードトランスポートスキーマへの変更は、それらが安定 AdCP タスクペイロード、ネゴシエーションフィールド、コンプライアンスアーティファクト、規範的プロトコルルールも変えない限り、それ自体では `adcontextprotocol` パッケージバージョンを上げません。`static/schemas/source/` の下のレジストリ JSON Schema は、レビュアーがトランスポート形状の変更を見られるよう changeset スコープゲートによって追跡されたままですが、それらのリリース分類は、安定 AdCP セマンティクスに踏み込まない限りレジストリサーフェスに属します。 **適合性スイート** — ストーリーボード、専門分野タクソノミー、シナリオ分類、ランナーメカニクス — は独立してバージョン管理され、**デフォルトでパッチレベル** です。適合性スイートは AAO が保守する検証アーティファクトです。仕様でもドキュメントでもありません。プレビューステータスの専門分野の追加、削除、リネーム、再分類、universal/protocol/specialism ディレクトリ間のストーリーボードの移動、シナリオカバレッジのリファクタリング、変更されていない仕様に合わせたランナー動作の調整はすべてパッチ変更です。 適合性スイートの変更は、エージェントがワイヤー上でしなければならないことを変える場合にのみマイナーまたはメジャーにエスカレートします — すなわち、暗黙の仕様検証を厳格化する、存在しなかった新しいケイパビリティをセラーにアドバタイズさせる、またはエージェントが積極的に主張している安定した専門分野を削除する(現在それをアドバタイズしているエージェントが非準拠になるため破壊的)とき。 | Change | Tier | | ----------------------------------------------------------------------- | --------------------------- | | 既存ケイパビリティに新しい universal ストーリーボードを追加 | Patch | | ストーリーボードをディレクトリ間で移動(`specialisms/{id}/` → `universal/` など) | Patch | | プレビューステータスの専門分野を再分類(グレードされたユーザーなし) | Patch | | 既存ストーリーボード内にシナリオを追加 | Patch | | 予告コントラクトの下で実験的のみのスキーマを変更、安定 AdCP タスクペイロード/ネゴシエーション/コンプライアンス/規範的ルールの変更なし | Patch | | enum に新しい安定した専門分野を追加 | Minor(エージェントができる新しいクレーム) | | enum から安定した専門分野を削除 | Major(現在それを主張しているエージェントを壊す) | | リクエスト/レスポンススキーマに新しいエラーコードまたは新しい任意フィールドを追加 | Minor | この分離により、レジストリトランスポート、検証機構、実験的フィードバックサーフェスが、安定した仕様レベルのバージョニングを引きずることなく素早く進化できます。 *** ## 3.x stability guarantees 3.0 に対して構築された実装は、3.x サイクルを通じて次の安定サーフェス保証に依存できます。実験的サーフェスは下記の予告とパッチ分類ルールに従います。 | Artifact | Guarantee within 3.x | | ----------------------- | ------------------------------------------------------------------------------------------- | | **Fields** | 決して削除されない。同じリリースで両方の名前を受け入れる文書化されたエイリアス付きでリネームされることがある。optional → required は事前の非推奨リリースの後のみ。 | | **Enums** | 加算的のみ。既存の値は決して削除もリネームもされない。クライアントは未知の値を許容し、賢明なデフォルトにフォールバックしなければならない。 | | **Error codes** | 加算的のみ。既存のコードはそのセマンティクスを保持。未知のエラーコードを汎用的に扱うクライアントは互換のまま。 | | **Task names** | 決して削除もリネームもされない。新しいタスクが追加されることがある。 | | **認証、トランスポート、コアセキュリティ** | 決して変わらない。これらはバージョンレベルの変更のみ。 | ### Experimental surfaces 上の表の安定性保証は安定サーフェスにのみ適用されます。AdCP は、コアプロトコルの一部だがまだ凍結されていないサーフェスを **実験的** として公開することがあります。実験的サーフェスはそのスキーマで `x-status: experimental` とマークされ、それらを実装するセラーは `get_adcp_capabilities` の `experimental_features` 経由でそう宣言します。 実験的サーフェスは、リリースノートで少なくとも 6 週間の予告をもって、任意の 2 つの 3.x リリース間で壊れることがあります(MAY)。実験的のみのスキーマ変更は、安定 AdCP タスクペイロード、ネゴシエーションフィールド、コンプライアンスアーティファクト、規範的プロトコルルールを変えないときパッチレベルです。採用者は実験的機能を宣言することでそのより速い動きにオプトインします。歴史的なプレリリースノートは実験的な再形成をマイナーと分類したかもしれませんが、このポリシーはリリースティアを安定サーフェスへの影響 + 実験的予告義務として扱います。実験的サーフェスは、実世界のシグナルを実証したら安定版に卒業します — 卒業基準、予告要件、クライアントガイダンスについては完全な [実験的ステータスコントラクト](/docs/reference/experimental-status) を参照。 実験的ステータスは意図的にスコープされています。サーフェスが実験的とマークされていない場合、上記の 3.x 保証が適用されます。 ### Patch releases パッチリリース(`3.0.1`、`3.1.2`)は、ドキュメント、表現、文書化された安定仕様から逸脱していた検証、レジストリバージョン管理アーティファクト、適合性スイートアーティファクト、または実験的のみのサーフェスのみを変えます。安定 AdCP サーフェスについて、パッチは決してスキーマを変えません — 新しいフィールドなし、リネームされたフィールドなし、新しい enum 値なし。現在のリリースの最新パッチへのアップグレードは、安定 AdCP サーフェスについて常に安全です。実験的採用者は、宣言した実験的機能のリリースノートにも従わなければなりません。 ### Security fixes セキュリティ関連の修正は、`security` ラベル付きでリリースノートに文書化され、現在のリリースに着地します。実装はセキュリティ勧告の後速やかにアップグレードすべき(SHOULD)。3.x 内の古いリリースは定期的なバックポートを受け取りません。現在のリリースへのアップグレードが期待される修正パスです。同じ姿勢がセキュリティのみのウィンドウ中の v2 に適用されます — そのタイムラインについては [v2 sunset ページ](/docs/reference/v2-sunset) を参照。 ### Breaking-change notice 実装に適応を要求する任意の変更 — リネームされたフィールド、required-to-optional 遷移、厳格化された検証 — は、次のすべてとともに出荷されます: * 移行ノート付きの [リリースノート](/docs/reference/release-notes) のエントリ * [チェンジログ](/docs/reference/changelog) のエントリ * [移行ガイド](/docs/reference/migration) のセクションまたは専用の深掘りページ * 可能な場合、変更を導入するリリースで新旧両名を受け入れるエイリアス ### Schema publication at merge 最新の公開タグは、常にソース HEAD と同じ required フィールドセットを反映しなければなりません。任意の `required` 配列を変える、判別子 `const` を変える、または検証制約を厳格化する PR は、変更が外部コンシューマーのアクティブな実装ターゲットになる前に、新しいタグをカットして伴わなければなりません。これは任意の上流義務に加えてです — 例えば、上記の安定性保証で記述された optional→required 遷移の非推奨要件。 **なぜ:** 実装者は公開タグに対して検証します(正確なバージョンにピン留めするか、[バージョンエイリアス](/docs/building/schemas-and-sdks#schema-versioning) を通じて解決するか)。ソース HEAD の required フィールドがそのタグが宣言するものを超えて増えると、ソースを読むエバリュエーターと公開タグを読む実装者の検証器が不一致になります — どちらかが間違っているからではなく、公開されたコントラクトが追いついていないからです。実装者はソース履歴を読まずにギャップを調停できません。 **RC サイクル中:** 同じ不変条件が RC タグ間に適用されます。`rc.N` と `rc.N+1` の間に着地するスキーマ変更は、新しい required フィールドセットがアクティブとして扱われる前に `rc.N+1` をカットすることを要求します。 **CI 強制が整うまで:** PR 作成者は、変更がエバリュエーターに伝播する前に新しいリリースまたは RC タグをカットする責任があります。レビュアーは、新しいタグが公開準備できていることを確認せずに、`required` 配列、判別子 `const`、または検証制約を変える PR を承認すべきではありません。 *** ## Release cadence AdCP は、実装者が計画できるよう次の名前付きポリシーを公開します: | Commitment | Window | | ---------------------- | ----------- | | **メジャーリリース(破壊的)** | 最小 18 か月間隔 | | **次のメジャー(4.0)** | 2027 年初頭を目標 | | **後継 GA 後の前メジャーのサポート** | 最小 12 か月 | | **削除前の非推奨予告** | 最小 6 か月 | バージョン内では、リリースはサイクル初期に 6〜8 週間ごとに着地し、バージョンが安定するにつれ四半期に向けて伸びます。バージョン内のリリース数は固定されていません。 ### Support window for previous major 新しいメジャーが出荷されると、前のメジャーは後継 GA の後少なくとも 12 か月間セキュリティパッチを受け取ります。セキュリティパッチ(CVE 修正とセキュリティ勧告)は全ウィンドウでバックポートされます。機能作業はされません。各バージョンの正確な EOL 日付はバージョンごとのサンセットページで公開され、後継の GA リリースに伴う遷移ノートからリンクされます。 非推奨ウィンドウは、非推奨機能が変更されてもリセットされません — 元の削除日が保たれます。 v2 サンセットは、このコミットメントの文書化された例外です: v2 はこのポリシーに先行し、バックポートできないアカウントとガバナンスの保護を欠きます。そのセキュリティのみのウィンドウは 2026 年 8 月 1 日まで走ります — [v2 sunset ページ](/docs/reference/v2-sunset) を参照。将来のメジャーはこの例外を呼び出しません。 ### Planned releases | Release | Target | | ------- | ---------------------------- | | **3.0** | 2026 年 4 月 — GA リリース | | **3.1** | 2026 年 6 月下旬 | | **3.2** | 2026 年 9 月下旬 | | **4.0** | 2027 年初頭 — 次のメジャー、蓄積された破壊的変更 | さらなる 3.x リリースは、実装フィードバックとワーキンググループの優先事項に基づき、リリース間に少なくとも 8 週間空けてスケジュールされます。各リリースの計画されたスコープは [GitHub マイルストーンページ](https://github.com/adcontextprotocol/adcp/milestones) で追跡されます — `3.1.0` と `3.2.0` マイルストーンのオープン issue は、固定されたコミットメントではなく現在の候補作業を反映します。 *** ## Extensibility AdCP は 3 レベルのスキーマ拡張を区別します。下記のルールは、タスクリファレンスページが別途述べない限り、すべての AdCP スキーマに適用されます。 | Surface | Who may extend | How | | ------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | プロトコルが定義する **コアフィールド** | ワーキンググループ、リリース経由 | ワーキンググループで提案され、通常のリリースプロセスを通じて受け入れられる。実装者によってインラインで拡張されることは決してない。 | | 任意のリクエスト/レスポンスオブジェクトの **`ext.{namespace}`** | 誰でも、`extensions_supported` に名前空間を宣言することで | 名前空間化、帯域外、ベースライン相互運用に影響しない。名前空間は [拡張レジストリ](/docs/building/by-layer/L2/context-sessions#extensions) に登録すべき(SHOULD)。 | | それを許すコンテナの **`additionalProperties: true`** | 誰でも | 宣言された位置での加算的フィールド。コアフィールドをシャドウしたり再定義したりしてはならない(MUST NOT)。読み取り者はエラーにするのではなく未知のフィールドを無視しなければならない(MUST)。追加フィールドが後のリリースで定義されたコアフィールドと名前で衝突するとき、読み取り者はコアフィールドを優先しなければならない(MUST)。 | **起こってはならないこと:** * `ext.{namespace}` の代わりにレスポンススキーマに新しいトップレベルプロパティを導入すること。`ext` の外にフィールドが必要な実装者は、そのフィールドをワーキンググループに提案しなければならない(MUST)。 * コアフィールドをインラインでリネームすること。エイリアスは仕様レベルの操作であり、実装者の操作ではない。 * コアフィールドの検証をローカルで厳格化すること。より厳格な検証が必要な実装者は、発する前または受け取った後にそれを実行する — ワイヤースキーマではない。 この分離が、すべての非準拠実装が断片化のクレームにならずに仕様を進化させられる理由です。ワイヤーコントラクトは狭く予測可能なまま保たれます。`ext.{namespace}` は、誰もが協調なしに素早く動ける場所です。 ### Adding to the core protocol フィールドやタスクは、次のパスの 1 つを通じてコアプロトコルに入ります: 1. **リリース追加。** 加算的変更(新しい任意フィールド、新しいタスク、新しい enum 値)は通常のリリースプロセスの下で 3.x リリースで出荷されます。これらは出荷されたリリースから安定です。 2. **実験的追加。** ワーキンググループがフィードバックのために出荷したいがまだ凍結する用意がないサーフェスは、実験的としてプロトコルに入ります — [実験的ステータス](/docs/reference/experimental-status) を参照。実験的サーフェスは安定版に卒業するか削除されます。恒久的な実験的状態はありません。 3. **バージョン境界。** 現在のバージョンと互換でない変更は次のメジャーのために保留されます。リリースのスコープ内外については [schema changes in releases](#schema-changes-in-releases-scope-and-limits) を参照。 *** ## Governance AdCP の開発は **[ワーキンググループ](/docs/community/working-group)** を中心に組織され、それぞれが特定のプロトコルドメイン(クリエイティブ、ガバナンス、メディアバイ、シグナル、ブランド、sponsored intelligence)に焦点を当てます。ワーキンググループは機能提案を推進し、実装フィードバックをサーフェスし、その領域の方向を形作ります。横断的な設計決定 — ドメイン間の一貫性、ワーキンググループ間の衝突、共有プリミティブ — は、閉じたドアの背後ではなく、ワーキンググループフォーラムと GitHub の公開 issue で解決されます。 ワーキンググループは次を通じて貢献します: * 提案と技術的議論のための **GitHub Discussions** * リアルタイムコラボレーションのための **Slack チャネル** * AdCP 上に構築する組織からの **メンバーフィードバック** * 設計決定を検証する **リファレンス実装** プロトコルとその実装基盤が成熟するにつれ、ドメインリードは自身の領域の所有権をますます引き受けます。 *** ## Additional resources * **[バージョンと互換性](/docs/reference/versions)** — 公開されたすべてのバージョンの一目でのステータス * **[仕様ライフサイクル](/docs/reference/specification-lifecycle)** — ステージ定義(Draft → Proposed → Final → Deprecated → Sunset)、参入基準、仕様セクションの現在のステージの確認方法 * **[ロードマップ](/docs/reference/roadmap)** — 計画された機能とマイルストーン * **[リリースノート](/docs/reference/release-notes)** — 何が出荷されたか、移行ガイド付き * **[チェンジログ](/docs/reference/changelog)** — 技術変更履歴 # バージョンと互換性 Source: https://adcp-docs-ja.pier1.co.jp/docs/reference/versions 公開されたすべての AdCP バージョン、そのステータス、EOL 日付。バージョンを選択またはアップグレードするならここから始める。 ## 一目で **今日の選択:** 新しい本番統合はワイヤーピン `"3.1"` で 3.1 を使うべきです。既存の 3.0 統合は移行中 `"3.0"` にピン留めしたままでかまいません。 | Status | Versions | Wire pin | What it means | | ----------------- | ------------------- | -------- | ----------------------------------------------------------------------------------------------------- | | **Active** | 3.1(現在の安定版: 3.1.0) | `"3.1"` | 現在の本番マイナーリリース。機能セットについては [What's New in AdCP 3.1](/docs/reference/whats-new-in-3-1) を参照。 | | **Maintained** | 3.0(現在の安定版: 3.0.19) | `"3.0"` | 既存の本番統合のためにサポートされる前のマイナー。互換性とセキュリティのパッチを受け取る。 | | **Security-only** | 2.5.0, 2.5.1, 2.5.3 | `"2.5"` | **2026 年 8 月 1 日(UTC)** までセキュリティパッチ。AAO ネットワーク本番には安全でない — [v2 sunset](/docs/reference/v2-sunset) を参照。 | | **End of life** | 2.0.0, 2.1.0 | — | パッチなし。3.0 に移行。 | | **Planned** | 3.2, 4.0 | — | [計画されたリリース](/docs/reference/versioning#planned-releases) のターゲット。 | **ワイヤー値はリリース精度のみです。** 安定版 3.1 ラインには `"3.1"` を、保守版 3.0 ラインには `"3.0"` を送ってください。`"3.0.19"` のようなパッチコンポーネントや `"3.1.0-rc.15"` のような完全な semver プレリリース値を送らないでください。`"3.1-rc.15"` のような歴史的なプレリリースピンは、それらのプレリリース値を明示的にアドバタイズするエージェントに対してのみ有効なままです。[パッチは交渉されない](/docs/reference/versioning#patches-are-not-negotiated) を参照。 *ステータス凡例: Active = 現在の安定世代。Maintained = サポートされる前のマイナー。Security-only = セキュリティ問題のみのパッチ、機能なし。End of life = パッチなし。Planned = 未リリース。* ## 公開されたすべてのバージョン ### 3.x — 現在の世代 | Version | Released | Notes | | ----------------- | --------------------- | ---------------------------------------- | | 3.0.0 | 2026-04-22 | GA — 3.x サイクルの安定ベースライン。 | | 3.0.1〜3.0.10 | 2026-04-28〜2026-05-10 | パッチ。3.0.0 とワイヤー互換。 | | 3.0.11〜3.0.15 | 2026-05-11〜2026-05-31 | パッチ。3.0.0 とワイヤー互換。 | | 3.0.16〜**3.0.19** | 2026-06-14〜2026-06-17 | パッチ。**3.0.19 が最新の安定パッチ。** 3.0.0 とワイヤー互換。 | | **3.1.0** | 2026-06-18 | GA — 現在の本番マイナーリリース。ワイヤーピン `"3.1"` を使う。 | パッチはワイヤーコントラクトを変えません。3.0.x 内のアップグレードは常に安全で、3.1 は 3.0 に対して加算的なままです。バージョンごとの詳細については [リリースノート](/docs/reference/release-notes)(キュレーションされた物語)と [チェンジログ](/docs/reference/changelog)(完全な技術履歴)を参照。 ### プレリリース(置き換え済み) | Version | Released | Notes | | ---------------------- | --------------------- | ------------------------------------------------------------------------------------------------ | | 3.1.0-rc.1〜3.1.0-rc.15 | 2026-05-29〜2026-06-16 | 3.1.0 GA によって置き換え。これらのアーティファクトはピン留めされたプレリリース採用者とリリースフォレンジックのために利用可能なまま。 | | 3.0.0-beta.1〜rc.2 | 2026-01-26〜2026-03-15 | 置き換え済み。 | | 3.0.0-rc.3 | 2026-04-01 | GA によって置き換え。rc.3 を採用した場合は [プレリリースアップグレードノート](/docs/reference/migration/prerelease-upgrades) を参照。 | ### 2.x — サンセット中 | Version | Released | Notes | | ------- | ---------- | ----------------------------------------------- | | 2.0.0 | 2025-10-15 | End of life。パッチなし。3.0 に移行。 | | 2.1.0 | 2025-10-19 | End of life。パッチなし。3.0 に移行。 | | 2.5.0 | 2025-11-22 | 2026 年 8 月 1 日(UTC)までセキュリティのみ。 | | 2.5.1 | 2025-12-24 | 2026 年 8 月 1 日(UTC)までセキュリティのみ。 | | 2.5.3 | 2026 年 1 月 | 2026 年 8 月 1 日(UTC)までセキュリティのみ。**最後の 2.x リリース。** | 2026 年 8 月 1 日(UTC)以降、2.x ライン全体が完全に非推奨になります。2.x スキーマ URL は既存の統合が黙って壊れないよう解決し続けますが、それらのスキーマはそれ以上の更新を受け取りません。移行パスについては [v2 Sunset](/docs/reference/v2-sunset) を参照。 ## FAQ ### 「現在」とは何か? 新しい本番統合には **3.1.0** が現在です。統合を `"3.1"`(`"3.1.0"` ではなく)にピン留めしてください。既存の 3.0 統合は移行中 `"3.0"` にピン留めしたままでかまいません。[バージョンネゴシエーション](/docs/reference/versioning#version-negotiation) を参照。 ### 2.2、2.3、2.4、2.6 はどうなったか? 2.x ラインはワーキンググループサイクル中にバージョン番号をスキップしました。一部は進行中のチェンジログエントリとして現れますが、決してリリースされませんでした — 変更は次の公開バージョンにロールフォワードされるか 3.0 に延期されました。公開された 5 つの 2.x リリースは 2.0.0、2.1.0、2.5.0、2.5.1、2.5.3 です。 ### v2 エージェントをまだ登録できるか? はい — v2 エージェントは AAO レジストリに登録できますが AAO Verified にはなれません。認証と verified-default ディスカバリーは v3.x を必要とします。[v2 Sunset → AAO レジストリを使う場合](/docs/reference/v2-sunset#if-you-use-the-aao-registry) を参照。 ### 3.0 はどのくらいサポートされるか? 4.0 GA の後、少なくとも 12 か月。[バージョニング → 前のメジャーのサポートウィンドウ](/docs/reference/versioning#support-window-for-previous-major) を参照。 ### 4.0 はどこか? 2027 年初頭を目標。3.x はサイクル中盤です — [計画されたリリース表](/docs/reference/versioning#planned-releases) を参照。 ## バージョンの選択 | もしあなたが… | 使う | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | 今日新しい本番統合を構築している | **3.1**。ワイヤーで `"3.1"` をピン留め。 | | 既存の 3.0 統合を稼働している | 移行中は **3.0** に留まるか、SDK と検証が準備できたら **3.1** に移る。 | | 2.5.x 統合を稼働している | **2026 年 8 月 1 日(UTC)** より前に 3.0 に移行。[移行ガイド](/docs/reference/migration) から始める。 | | 2.0 または 2.1 統合を稼働している | 今すぐアップグレード — サポート外。 | | 3.1 後の未リリース機能を試している | [リリースノート](/docs/reference/release-notes)、[バージョニングとガバナンス](/docs/reference/versioning)、[実験的ステータス](/docs/reference/experimental-status) を参照。 | ## 関連 * **[バージョニングとガバナンス](/docs/reference/versioning)** — リリースティア、スキーマ変更スコープ、ケイデンスポリシー * **[v2 Sunset](/docs/reference/v2-sunset)** — 完全な v2 EOL タイムラインと移行パス * **[v3 の新機能](/docs/reference/whats-new-in-v3)** — 3.0 が追加するものの機能ごとのサマリー * **[What's New in AdCP 3.1](/docs/reference/whats-new-in-3-1)** — 最終的な 3.1 機能セットと採用者のアクション * **[リリースノート](/docs/reference/release-notes)** — 各バージョンのキュレーションされた物語 * **[チェンジログ](/docs/reference/changelog)** — 完全な技術変更履歴 # AAO public key set Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/aao-public-key-set /static/openapi/registry.yaml get /api/.well-known/jwks.json Returns the JSON Web Key Set (JWKS) containing AAO's public verification keys. Use these to verify AAO Verified badge tokens without calling AAO's API. # Bulk storyboard status Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/bulk-storyboard-status /static/openapi/registry.yaml post /api/registry/agents/storyboard-status Returns per-storyboard test results for multiple agents in a single request. **Members only** — requires authentication and an active membership. Static admin API key callers may read this for support/debugging. Maximum 100 agent URLs per request. # Compare storyboard against reference agent Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/compare-storyboard-against-reference-agent /static/openapi/registry.yaml post /api/registry/agents/{encodedUrl}/storyboard/{storyboardId}/compare Run a storyboard against both the target agent and the public reference agent, returning side-by-side results. Requires authentication and ownership. # Connect agent credentials Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/connect-agent-credentials /static/openapi/registry.yaml put /api/registry/agents/{encodedUrl}/connect Store authentication credentials for an agent. Requires authentication and ownership. # Dry-run the saved OAuth 2.0 client-credentials config Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/dry-run-the-saved-oauth-20-client-credentials-config /static/openapi/registry.yaml post /api/registry/agents/{encodedUrl}/oauth-client-credentials/test Exchange the saved client_credentials at the token endpoint and discard the resulting access token. Returns success + latency on a 2xx exchange, or the SDK's `ClientCredentialsExchangeError` kind (`oauth`, `malformed`, `network`) on failure so operators get same-second feedback instead of waiting for the next compliance heartbeat. Requires authentication and ownership. Requires credentials to already be saved via `PUT /oauth-client-credentials`. # Get agent AAO Verified status Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-agent-aao-verified-status /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/verification Returns AAO Verified badge status for a single agent. Public and cacheable. Includes role badges, verified storyboards, and a link to the agent's registry listing. # Get agent auth status Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-agent-auth-status /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/auth-status Returns whether an agent has stored authentication credentials and OAuth token status. Requires authentication. # Get agent compliance detail Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-agent-compliance-detail /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/compliance Returns detailed compliance status for a single agent, including track-level results, storyboard counts, and timestamps. If the agent has opted out of compliance monitoring, returns a minimal response with `status: opted_out`. # Get agent compliance history Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-agent-compliance-history /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/compliance/history Returns a list of compliance test runs for an agent, ordered most recent first. If the agent has opted out, returns an empty list. # Get agent storyboard status Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-agent-storyboard-status /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/storyboard-status Returns per-storyboard test results for an agent. Includes title, category, track, pass/fail status, and step counts. **Members only** — requires authentication and an active membership. Static admin API key callers may read this for support/debugging. # Get agent verification badge SVG Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-agent-verification-badge-svg /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/badge/{role}.svg Returns an SVG badge image for the specified agent and role. Shows 'AAO Verified | Sales Agent' (teal) when verified, or 'AAO Verified | Not Verified' (grey) when not. Cacheable, suitable for embedding in websites. # Get applicable storyboards for agent Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-applicable-storyboards-for-agent /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/applicable-storyboards Probe the agent's get_adcp_capabilities and resolve its declared supported_protocols and specialisms to the compliance bundles that will run. Requires authentication and ownership. # Get embeddable badge code Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-embeddable-badge-code /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/badge/{role}/embed Returns HTML and Markdown embed snippets for displaying an AAO Verified badge on websites, social profiles, and documentation. The badge links to the agent's AAO registry listing. # Get first step preview Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-first-step-preview /static/openapi/registry.yaml get /api/storyboards/{storyboardId}/first-step Returns a preview of the first step of a storyboard. No agent call needed. # Get monitoring settings Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-monitoring-settings /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/monitoring/settings Returns the monitoring configuration for an agent. Requires authentication and ownership. # Get outbound request log Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-outbound-request-log /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/monitoring/requests Returns the outbound request log for an agent (compliance checks, health probes, etc.). Requires authentication and ownership, or the static admin API key for support/debugging. # Get per-step diagnostics for a compliance run Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-per-step-diagnostics-for-a-compliance-run /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/compliance/diagnostics Returns the exact request and response payloads the runner captured for failing storyboard steps on a single compliance run. Lets agent owners diff what the runner sent against their own probes without re-running the storyboard. Owner-only, with static admin API key access for support/debugging — payloads echo seller-side account/brand identifiers and may carry sensitive descriptive fields. If `run_id` is omitted, resolves to the latest run for the agent. # Get storyboard detail Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-storyboard-detail /static/openapi/registry.yaml get /api/storyboards/{id} Returns a single storyboard with its full phase and step structure, plus its test kit if available. # Get version-pinned agent verification badge SVG Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-version-pinned-agent-verification-badge-svg /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/badge/{role}/{version}.svg Returns an SVG badge image scoped to a specific AdCP release (MAJOR.MINOR, e.g. '3.0'). Buyers who want to call out 'verified for 3.0' embed this instead of the legacy `/badge/{role}.svg` (which auto-upgrades to the highest active version). Renders 'Not Verified' when the agent never earned a badge at this version. # Get version-pinned embeddable badge code Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/get-version-pinned-embeddable-badge-code /static/openapi/registry.yaml get /api/registry/agents/{encodedUrl}/badge/{role}/{version}/embed Returns HTML and Markdown embed snippets that point at the version-pinned SVG. Alt text includes the version (e.g. 'AAO Verified Media Buy Agent 3.0'). Buyers who want to freeze on a specific AdCP release embed these instead of the legacy `/badge/{role}/embed`. # List storyboards Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/list-storyboards /static/openapi/registry.yaml get /api/storyboards Returns the catalog of compliance storyboards. Optionally filter by category. # Pause or resume monitoring Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/pause-or-resume-monitoring /static/openapi/registry.yaml put /api/registry/agents/{encodedUrl}/monitoring/pause Pause or resume automated compliance monitoring for an agent. Requires authentication and ownership. # Refresh agent snapshot Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/refresh-agent-snapshot /static/openapi/registry.yaml post /api/registry/agents/{encodedUrl}/refresh Re-probe the agent and update its registry health (online, tools_count, response_time_ms), capability snapshot (inferred type, discovered tools), and compliance verdict (storyboard pass/fail counts). Use after fixing your agent so the registry shows fresh data without waiting for the periodic heartbeat (~1h). **Compliance re-run:** when the caller owns the agent or is an AAO admin and the capability probe succeeds, the full storyboard suite can run for several minutes on capability-rich agents with a fresh test session, and `agent_storyboard_status` is updated. Owner-triggered runs use `triggered_by: 'owner_test'`; admin-triggered support runs use `triggered_by: 'manual'`. Badge fan-out reissues verification badges off the new run. If the compliance call fails (timeout, OAuth wall, internal error), the capability/health portion still returns successfully — `compliance.ran` is `false` with an `error` string. **Auth:** owner of the agent, AAO admin, or static `ADMIN_API_KEY`. **Rate limits:** 60 seconds per agent URL, 30 requests per user per hour. # Requeue agent for compliance heartbeat Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/requeue-agent-for-compliance-heartbeat /static/openapi/registry.yaml post /api/registry/agents/{encodedUrl}/monitoring/requeue Clears the agent's last_checked_at timestamp so it is picked up on the next heartbeat cycle (within ~1 hour). This is queued-async; it does not run the compliance suite synchronously or change the current verdict until the heartbeat completes. Requires authentication and ownership. # Run a single storyboard step Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/run-a-single-storyboard-step /static/openapi/registry.yaml post /api/registry/agents/{encodedUrl}/storyboard/{storyboardId}/step/{stepId} Execute a single storyboard step against an agent. Requires authentication and ownership. # Run full storyboard evaluation Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/run-full-storyboard-evaluation /static/openapi/registry.yaml post /api/registry/agents/{encodedUrl}/storyboard/{storyboardId}/run Execute all steps of a storyboard against an agent and record the compliance result. Requires authentication and ownership. # Save OAuth 2.0 client-credentials for an agent Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/save-oauth-20-client-credentials-for-an-agent /static/openapi/registry.yaml put /api/registry/agents/{encodedUrl}/oauth-client-credentials Store a machine-to-machine OAuth 2.0 client-credentials configuration (RFC 6749 §4.4) for this agent. The SDK exchanges at the token endpoint before every call and refreshes on 401. `client_secret` may be a `$ENV:VAR_NAME` reference — the SDK resolves at exchange time, the server stores it as written (encrypted uniformly). Requires authentication and ownership. # Update agent lifecycle stage Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/update-agent-lifecycle-stage /static/openapi/registry.yaml put /api/registry/agents/{encodedUrl}/lifecycle Set the lifecycle stage for an agent. Requires authentication and ownership of the agent. # Update compliance opt-out Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/update-compliance-opt-out /static/openapi/registry.yaml put /api/registry/agents/{encodedUrl}/compliance/opt-out Opt an agent in or out of public compliance reporting. Requires authentication and ownership of the agent. # Update monitoring interval Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-compliance/update-monitoring-interval /static/openapi/registry.yaml put /api/registry/agents/{encodedUrl}/monitoring/interval Set the check interval for automated compliance monitoring (6–168 hours). Requires authentication and ownership. # Request manager fan-out re-validation Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-discovery/request-manager-fan-out-re-validation /static/openapi/registry.yaml post /api/registry/manager-revalidation-request Trigger re-validation of every publisher delegating to a manager domain via ads.txt `MANAGERDOMAIN`. Use after rotating the manager's `adagents.json` so the change propagates to delegating publishers without waiting for the next routine crawl cycle. Work is queued and drained at a bounded rate (≈50 publishers per 5-minute tick). Returns 202 immediately with the number of publishers enqueued. **Rate limits:** 5 minutes per manager domain, 30 requests per user per hour (shared with other crawl-request endpoints). # Discover agent Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-probing/discover-agent /static/openapi/registry.yaml get /api/public/discover-agent Probe an agent URL to discover its name, type, supported protocols, and basic statistics. # Get agent formats Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-probing/get-agent-formats /static/openapi/registry.yaml get /api/public/agent-formats Fetch creative formats from a creative agent. # Get agent products Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-probing/get-agent-products /static/openapi/registry.yaml get /api/public/agent-products Fetch products from a sales agent. # Validate publisher Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/agent-probing/validate-publisher /static/openapi/registry.yaml get /api/public/validate-publisher Validate a publisher domain's adagents.json and return summary statistics. # AAO directory inverse-lookup Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/authorization-lookups/aao-directory-inverse-lookup /static/openapi/registry.yaml get /api/v1/agents/{encodedUrl}/publishers Given a percent-encoded `agent_url`, returns the publishers whose adagents.json authorizes that agent, with provenance (`discovery_method`, `manager_domain`), per-publisher property counts (`properties_authorized`, `properties_total`, scoped to this publisher only — never network-wide), signing-key pin status, and lifecycle state (`authorized` / `revoked`). Spec: [docs/aao/directory-api.mdx](/docs/aao/directory-api) (adcp#4823). This endpoint is the spec-compliant richer-shape replacement for the legacy `/api/registry/lookup/agent/{agentUrl}/domains`, which returns domain strings only. # AAO directory inverse-lookup Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/authorization-lookups/aao-directory-inverse-lookup-1 /static/openapi/registry.yaml get /v1/agents/{encodedUrl}/publishers Given a percent-encoded `agent_url`, returns the publishers whose adagents.json authorizes that agent, with provenance (`discovery_method`, `manager_domain`), per-publisher property counts (`properties_authorized`, `properties_total`, scoped to this publisher only — never network-wide), signing-key pin status, and lifecycle state (`authorized` / `revoked`). Spec: [docs/aao/directory-api.mdx](/docs/aao/directory-api) (adcp#4823). This endpoint is the spec-compliant richer-shape replacement for the legacy `/api/registry/lookup/agent/{agentUrl}/domains`, which returns domain strings only. # Agent domain lookup Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/authorization-lookups/agent-domain-lookup /static/openapi/registry.yaml get /api/registry/lookup/agent/{agentUrl}/domains Get all publisher domains associated with an agent. # Domain lookup (deprecated) Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/authorization-lookups/domain-lookup-deprecated /static/openapi/registry.yaml get /api/registry/lookup/domain/{domain} **Deprecated.** Use `/api/registry/publisher?domain=X` for richer data including hosting state, per-agent rollup, and brand.json fallback. This endpoint will be removed in a future release. # Expand product identifiers Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/authorization-lookups/expand-product-identifiers /static/openapi/registry.yaml post /api/registry/expand/product-identifiers Expand publisher_properties selectors into concrete property identifiers for caching. # Operator lookup Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/authorization-lookups/operator-lookup /static/openapi/registry.yaml get /api/registry/operator Given a domain, returns the agents this entity operates and which publishers trust them. **Response shape is auth-aware.** Anonymous callers see only `public` agents. Authenticated callers on an AAO membership tier with API access also see `members_only` agents. Profile owners (callers whose org owns the queried domain) additionally see `private` agents. This is the primary mechanism by which AAO membership unlocks deeper registry visibility. **`scope` bucket filter.** Callers can opt INTO a single visibility bucket (or the full union) regardless of what their auth would otherwise unlock — useful for picker UIs that want exactly one slice (e.g. anonymous-equivalent, members-only catalog, owner's private drafts). `scope` only narrows; it never escalates (e.g. `scope=member` on an explorer or anonymous caller silently returns public only). **Member level visibility.** When the profile owner has set their member card to public (`is_public=true`), the `member` object additionally carries `is_founding_member` (boolean) plus `membership_tier` (raw enum) and `membership_tier_label` (e.g. `Professional`, `Partner`, `Leader`) when the org has a resolvable tier. Founding Member is orthogonal to tier — founding orgs typically display both. For private profiles these fields are absent. # Per-agent authorization rollup Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/authorization-lookups/per-agent-authorization-rollup /static/openapi/registry.yaml get /api/registry/publisher/authorization Returns whether a given agent is authorized for a publisher domain and how many of the publisher's properties it can sell. When the agent has property-level authorization rows, the count is the intersection with the publisher's property set; when it only has a publisher-wide row, the count equals the total. Returns 404 when the agent has no authorization (publisher-wide or property-level) for the domain. # Property authorization check Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/authorization-lookups/property-authorization-check /static/openapi/registry.yaml get /api/registry/validate/property-authorization Quick check if a property identifier is authorized for an agent. Optimized for real-time ad request validation. # Property identifier lookup Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/authorization-lookups/property-identifier-lookup /static/openapi/registry.yaml get /api/registry/lookup/property Find agents that hold a specific property identifier. # Publisher lookup Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/authorization-lookups/publisher-lookup /static/openapi/registry.yaml get /api/registry/publisher Given a domain, returns the inventory this entity publishes and which agents it authorizes. **This endpoint is unauthenticated and returns the same response shape for every caller.** Compare to `/api/registry/operator`, where AAO membership tier and profile ownership unlock additional agent visibility (`members_only`, `private`). AAO membership does not change the `/publisher` response today. **Property source precedence:** publisher-attested adagents.json properties win first. When no publisher-attested adagents properties exist for the domain, brand.json properties supplement and override lower-trust rows, followed by approved community catalogs, then crawler-discovered rows. Each property carries a `source` field (`adagents_json` / `brand_json` / `community` / `discovered`). **Per-agent rollup:** each entry in `authorized_agents` may carry `properties_authorized` + `properties_total` + `publisher_wide`. The rollup is suppressed (fields absent) when (a) properties are entirely brand.json-hydrated — no adagents.json claim has been made — or (b) the publisher has more than 50 authorized agents (above-cap entries are returned without rollup; `rollup_truncated` is set with `{ cap, total_agents }`). Use `/api/registry/publisher/authorization?domain=X&agent=Y` for the per-agent count when the index rollup is absent. # Revalidate publisher adagents.json Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/authorization-lookups/revalidate-publisher-adagentsjson /static/openapi/registry.yaml post /api/registry/publisher/{domain}/adagents/revalidate Admin-only endpoint for support/operator tooling to synchronously fetch a publisher's live `/.well-known/adagents.json`, run the registry validator, persist the refreshed verdict and fetch metadata, and return the validation result. `force=true` is accepted for operator tooling; the current validator always fetches the live origin. **Rate limits:** 5 minutes per domain, 30 requests per user per hour. # Validate product authorization Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/authorization-lookups/validate-product-authorization /static/openapi/registry.yaml post /api/registry/validate/product-authorization Check whether an agent is authorized to sell a product based on its publisher_properties. # Request brand.json re-crawl Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/brand-discovery/request-brandjson-re-crawl /static/openapi/registry.yaml post /api/registry/brand-crawl-request Trigger an immediate re-crawl of a domain's brand.json. The crawl runs asynchronously — returns 202 immediately. **Rate limits:** 5 minutes per domain, 30 requests per user per hour (shared with adagents.json crawl requests). # Bootstrap snapshot for inline verifiers Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/change-feed/bootstrap-snapshot-for-inline-verifiers /static/openapi/registry.yaml get /api/registry/authorizations/snapshot Streams the full effective authorization set as gzipped NDJSON (one JSON object per line). Consumers persist `X-Sync-Cursor` and tail `/api/registry/feed?entity_type=authorization&cursor=` for deltas. **ETag** is the hash of the X-Sync-Cursor — clients can `If-None-Match` to skip a re-pull when nothing has changed. **evidence** defaults to `adagents_json` only; long-run wire size ~150 MB gzipped. # Per-agent authorization pull Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/change-feed/per-agent-authorization-pull /static/openapi/registry.yaml get /api/registry/authorizations Default endpoint for verification consumers (DSPs, sales houses, agencies). Returns the rows where the requested agent appears as `agent_url` — typically ≤ a few hundred. Pair with `/api/registry/feed?entity_type=authorization` to tail subsequent changes via the `X-Sync-Cursor` header. **evidence** defaults to `adagents_json` only. `agent_claim` is opt-in (`?evidence=adagents_json,agent_claim`) to prevent buy-side trust misuse — see specs/registry-authorization-model.md. # Registry change feed Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/change-feed/registry-change-feed /static/openapi/registry.yaml get /api/registry/feed Poll a cursor-based feed of registry changes. Events are ordered by UUID v7 event_id for monotonic cursor progression. The feed retains events for 90 days. The `freshness` object reports when the response was generated, the newest matching event currently visible to the feed, and the resulting feed lag. Type filtering supports glob patterns: `property.*` matches `property.created`, `property.updated`, etc. # Registry change feed stream Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/change-feed/registry-change-feed-stream /static/openapi/registry.yaml get /api/registry/feed/stream Subscribe to registry feed pages over Server-Sent Events. This is a push-friendly transport for the same cursor contract as `/api/registry/feed`: clients still persist `cursor`, apply only feed events, and recover from `cursor_expired` by re-bootstrapping. The stream emits `feed` events containing a full feed page, `heartbeat` events while caught up, and `error` before closing when the cursor expires or the server cannot query the feed. # Delete community mirror Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/community-mirrors/delete-community-mirror /static/openapi/registry.yaml delete /api/registry/mirrors/{platform} Delete a persisted community mirror and retire derived publisher-domain catalog rows. Requires a registry moderator or AgenticAdvertising.org admin. Without `force=true`, the service refuses to delete a mirror that has not first published a `superseded_by` migration URL. # Get community mirror Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/community-mirrors/get-community-mirror /static/openapi/registry.yaml get /api/registry/mirrors/{platform} Fetch one persisted community mirror by platform. A present mirror returns the platform metadata plus the stored catalog-only `adagents_json` document; absent mirrors return 404. # List community mirrors Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/community-mirrors/list-community-mirrors /static/openapi/registry.yaml get /api/registry/mirrors List persisted catalog-only adagents.json community mirrors. The list projection includes presence and freshness metadata but omits the full `adagents_json` body; fetch a platform-specific mirror for the full document. # Publish community mirror Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/community-mirrors/publish-community-mirror /static/openapi/registry.yaml put /api/registry/mirrors/{platform} Publish or update a catalog-only adagents.json community mirror. Requires a registry moderator or AgenticAdvertising.org admin. The service validates the assembled document against adagents.json, forces `authorized_agents: []`, regenerates `$schema` and `last_updated`, and updates derived publisher-domain catalog rows. # List my registered agents Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/member-agents/list-my-registered-agents /static/openapi/registry.yaml get /api/me/agents List the agents registered on the caller's organization member profile. Returns the same `agents[]` array stored on the profile, in the order members registered them. # Register an agent Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/member-agents/register-an-agent /static/openapi/registry.yaml post /api/me/agents Register an agent on the caller's organization member profile. Idempotent on `url`: re-posting the same `url` updates the entry in place rather than creating a duplicate. New entries return `201`; updates return `200`. **True one-call storefront experience.** A third-party app holding only a user's OAuth token can `POST /api/me/agents` once and have the entire bootstrap chain materialize: - If the caller has zero org memberships, the server auto-creates an organization (corporate or personal workspace based on the user's email domain) and the response includes `org_auto_created: true`. - If the caller's org has no member profile, the server auto-creates a private profile (display name = organization name, `is_public: false`) and the response includes `profile_auto_created: true`. Both auto-bootstraps are best-effort fallbacks. To customize org name / company_type / revenue_tier, or to control profile slug / brand identity / tagline, call `POST /api/organizations` and `POST /api/me/member-profile` explicitly before registering the agent. Tier transitions never happen via this path — go through the billing flow. `type` is required and declared by the caller — the server does not infer it. Server-side smuggle protection still cross-checks the declared type against the agent's capability snapshot when one exists; if the snapshot contradicts the declaration without classifying it, the stored value is `unknown` and the dashboard surfaces the conflict for the owner to resolve. `visibility: "public"` requires a paid AAO tier (Professional, Builder, Member, or Leader) and a verified primary domain on the organization (set via the Linked Domains UI). Non-API-tier callers (Explorer or no tier) who request `public` will have the entry stored as `members_only` instead, and the response will include a `visibility_downgraded` warning describing the coercion. # Remove an agent Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/member-agents/remove-an-agent /static/openapi/registry.yaml delete /api/me/agents/{url} Remove one registered agent identified by its `url`. A currently-`public` agent is reflected in the published `brand.json` manifest. To prevent the registry catalog and `brand.json` from silently disagreeing, this endpoint returns `409 unpublish_first` when the agent is `public` — `PATCH /api/me/agents/{url}` with `visibility: "private"` first (or call `DELETE /api/me/member-profile/agents/{index}/publish` to reconcile the manifest), then re-issue the DELETE. # Update an agent Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/member-agents/update-an-agent /static/openapi/registry.yaml patch /api/me/agents/{url} Update one registered agent identified by its `url`. The `url` field itself cannot be changed via PATCH — supplying a `url` in the body that differs from the path returns `400 url_immutable`; re-register at the new URL and DELETE the old entry to migrate. All other fields accept partial updates. # Create or adopt my organization Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/onboarding/create-or-adopt-my-organization /static/openapi/registry.yaml post /api/organizations Bootstrap the caller's organization explicitly. Use this when the caller wants to control the organization name, `company_type`, `revenue_tier`, or `is_personal` flag before any agents are registered. **Most storefront-style integrations don't need this call** — `POST /api/me/agents` will auto-create an org for a fresh OAuth user (corporate or personal workspace based on the email domain) and surface `org_auto_created: true` in the response. Reach for `POST /api/organizations` only when the auto-derived defaults aren't acceptable. Three outcomes depending on the caller's state: - **Fresh create** (most common): a new WorkOS organization is created, the caller is added as `owner`, the corporate domain is recorded as email-verified, and ToS / privacy-policy acceptance is logged from the request context. Returns `{ success: true, organization: { id, name } }`. - **Prospect adoption**: an organization with the caller's email domain already exists as a `prospect` (the registry pre-recorded it from a brand crawl but no human had claimed it yet). The caller is promoted to `owner` of the existing record instead of forking a duplicate. Returns `{ id, name, adopted: true }`. - **Already-active conflict**: the org exists and is already claimed by another paying member or a previously joined user. Returns `409` with the existing org id so the caller can switch to a join-request flow (`POST /api/organizations/:orgId/join-requests`) instead of trying to register a duplicate. Tier transitions happen via the billing flow only — there is no `membership_tier` field on this endpoint. After org creation, send the user to `POST /api/checkout-session` (or the `/dashboard/membership` page) to start a subscription; the Stripe webhook is the sole writer of `organizations.membership_tier`. Rate-limited per user: `15` failed attempts per hour; successful calls do not count against the limit so a legitimate registration is never penalized by earlier validation errors. # Bulk resolve policies Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/policy-registry/bulk-resolve-policies /static/openapi/registry.yaml post /api/policies/resolve/bulk Resolve up to 100 policies by ID in a single request. Returns a map of policy_id to Policy (or null if not found). **Rate limit:** 20 requests per minute per IP address. # List policies Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/policy-registry/list-policies /static/openapi/registry.yaml get /api/policies/registry Browse and search the governance policy registry. Returns approved policies with optional filtering by category, enforcement level, jurisdiction, policy category, and governance domain. # Policy revision history Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/policy-registry/policy-revision-history /static/openapi/registry.yaml get /api/policies/history Retrieve the edit history for a policy. Each revision records who made the change, a summary, and whether it was a rollback. # Resolve policy Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/policy-registry/resolve-policy /static/openapi/registry.yaml get /api/policies/resolve Resolve a single policy by ID. Optionally pin to a specific version — returns null if the version does not match. # Save policy Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/policy-registry/save-policy /static/openapi/registry.yaml post /api/policies/save Create or update a community-contributed policy. Requires authentication. Registry-sourced and pending-review policies cannot be edited (returns 409). Updates automatically create a revision record. # Generate adagents.json Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/validation-tools/generate-adagentsjson /static/openapi/registry.yaml post /api/adagents/create Generate a valid adagents.json file from authorized agents and/or catalog content. `authorized_agents` may be empty for a catalog-only community mirror that publishes formats/properties/placements for a platform that has not adopted AdCP. # Validate adagents.json Source: https://adcp-docs-ja.pier1.co.jp/docs/registry/api-reference/validation-tools/validate-adagentsjson /static/openapi/registry.yaml post /api/adagents/validate Validate a domain's adagents.json file and optionally validate referenced agent cards. # エージェント型広告の説明方法 Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/decision-makers/explaining-agentic-advertising エージェント型広告と AdCP を説明する、最も明快で最もテストされた方法のチートシート — オーディエンス別・達成すべきジョブ別。CMO、取締役会、エージェンシー、パブリッシャーに 1 回のミーティングで理解させなければならない意思決定者向け。 # エージェント型広告の説明方法 意思決定者の仕事の半分は、*他の* 人々に — CMO、取締役会、エージェンシー、パブリッシャーに — 1 回のミーティングで理解させることです。このページは、コミュニティが実際にエージェント型広告をどう説明するかから引き出した、刺さるフレーミングを集めています。あなたの部屋に合うものを選んでください; すべてを一度に使わないでください。 これは [Decision-Makers トラック](/docs/learning/decision-makers/overview) の伴走ページです。トラックはシフトについて推論することを教えます; このページはそれを伝えるための言語を渡します。 ## 覚える価値のあるワンライナー > **入札から推論へ。インプレッションから関係へ。ミリ秒から数か月へ。** > — Ben Masse, Triton Digital(egta CEO Summit, 2026) シフトの最も持ち運びやすい記述: プログラマティックはミリ秒で次のインプレッションを最適化します; エージェント型広告は数か月にわたって結果と関係について推論します。1 文を覚えるなら、これを覚えてください。 > **AI が実行する。人間が説明責任を持ち続ける。** > — Ben Masse, Triton Digital(egta CEO Summit, 2026) 「でも誰がコントロールしているのか?」への最もクリーンな回答。エージェントが作業をします; 重要な決定は人々に留まります。実行を委任することは判断を委任することではありません。 > **1 つのオープンプロトコル。任意のプラットフォーム。任意のフォーマット。任意のチャネル。** 5 語で表す標準 — なぜ一度構築してどこにでも到達するのか。 > **プログラマティックは薄いリクエストを外に送る; エージェント型広告はあなたのデータを中に持ち込む。** [逆転したデータフロー](/docs/learning/decision-makers/l1-agentic-advertising) — 会話に最も近いプラットフォームがコンテキストを持つので、あなたはコンテキストからリクエストを送り出す代わりに、そこへデータを持ち込みます。 ## 刺さるアナロジー * **自動運転車** — プログラマティックを「より賢く」することは、既存の道路の既存の車に AI ドライバーを乗せることです; エージェント型広告は自動運転車向けに設計された新しいインフラを構築します。*「これは単により良い RTB では?」と言われたときに使う。* * **征服ではなく条約** — *「征服者によって課された新技術ではなく — 実際に何かを所有する当事者によって交渉された条約」*(Ben Masse, egta CEO Summit)。*パブリッシャーと放送局に使う: 標準は彼らのもので、プラットフォームのものではない。* * **コンテナ輸送** — 世界が標準コンテナ寸法に合意すると、グローバル貿易は離陸しました。オープン標準は AI 時代の広告にとってその合意です。*標準懐疑論者に使う。* * **取引のための共通言語** — 銀行業の共通決済レイヤーのように、AdCP は製品を作りません; それはエコシステム全体がスピードとスケールで取引できるようにします。 * **パーソナルショッパー** — ブランドエージェントはブランドが望むものを知り、最適なフィットを見つけるためすべての店を訪ねますが、店はドアを開けて棚を整理しておく必要があります。*「あなたはもう価格で競争していません — 見つけられることで競争しています。」* *パブリッシャーに使う。* * **キャンペーンから材料へ** — 完成したクリエイティブのトラフィッキングをやめ、[材料とゴールを提供](/docs/sponsored-intelligence/monetizing-ai) し始めます; プラットフォームが結果を組み立てます。 ## オーディエンス別 **CMO / 取締役会。** 配管ではなく、賭け金とシフトから始めます。*「AI は広告を作り替えています; 問題はあなたが変化を指揮するか反応するかです。」* 次にトライアド(入札→推論、インプレッション→関係、ミリ秒→数か月)。部屋を鎮める安心で締めくくります: あなたの測定スタックとエージェンシー関係は消えません — これは新しい測定パラダイムではなく、新しいチャネルです。 **エージェンシー。** バイヤーエージェントは AI サーフェスにとって DSP に相当します — それはあなたのスタックの *隣* に座り、上には座りません。一度構築したものがどこでも機能します; マージンは、プラットフォームごとに直接取引を交渉する代わりに、1 つの統合が多くのサーフェスに到達することから来ます。 **パブリッシャー / 放送局。** 敵役は「ID 貴族制」— アイデンティティを溜め込むウォールドガーデンです。あなたは「実際に何かを所有する当事者」の 1 つです: 本物の在庫と本物のオーディエンス関係。AdCP はあなたにテーブルの席を与える条約です。あなたは価格ではなく、エージェントに *見つけられ読み取れる* ことで競争します。 **SMB / 創業者。** あなたは既に Google Shopping でこのバージョンを行っています: 製品フィードをプッシュし、プラットフォームがあなたの製品をマーチャンダイズします。AI サーフェスはパートナーを通じて同じように機能します — 作る新しいクリエイティブはなく、既存の ROAS/CPA 追跡も依然として機能します。 ## ジョブ別 | あなたが必要とすること… | 手を伸ばす先 | | ------------------------ | ---------------------------------------------- | | シフトを説明する | トライアド; 「高頻度取引ではなく、ポートフォリオ管理」 | | 「これは単により良い RTB では?」に対処する | 自動運転車アナロジー; 「プログラマティックをより良くではなく、プログラマティックより良く」 | | 「また別の購入プラットフォーム?」に対処する | 「それは馬なし馬車です; あなたは車が欲しい」 — それはコンテキストグラフを持つ | | 「誰がコントロールしているのか?」に答える | 「AI が実行する。人間が説明責任を持ち続ける。」 | | 「これは一時的流行か?」に答える | AI はシグナル喪失の *治療法* で、次のハイプサイクルではない | | 測定について安心させる | 「新しい測定パラダイムではなく、新しいチャネル」 | ## さらに深く 逆転したデータフロー、そして CMO への説明方法。 バイサイドガイド: 何を提供するか、ロール別の始め方。 # L1: エージェント型広告と逆転したデータフロー Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/decision-makers/l1-agentic-advertising AdCP Decision-Makers モジュール L1: エージェント型広告とは何か、なぜプログラマティックと異なるか、逆転したデータフロー、CMO への説明方法。推論のみ — コードなし。 # L1: エージェント型広告と逆転したデータフロー **約 15 分** | 前提条件: なし | 無料 意思決定者が最初に必要とするのは正確なメンタルモデル — 手を振らずに CMO に繰り返せるものです。このモジュールはそれを構築します。実行するものはなく、クエリするエージェントもありません; あなたはシフトを通じて推論し、それを説明する練習をします。 中核となるアイデア: **プログラマティックは薄いリクエストを外に送る; エージェント型広告はあなたのデータを中に持ち込む。** 入札リクエストはページ URL、デバイスタイプ、おそらくユーザー ID を運びます — 会話コンテキストを持たないリモートの意思決定者へ。ユーザーに最も近い AI プラットフォームはそのコンテキストを *持っています*。だからコンテキストからリクエストを送り出す代わりに、あなたは材料 — ブランドアイデンティティ、ルール、ゴール、そして(製品を売るなら)製品カタログ — をそこへ持ち込み、プラットフォームがレスポンスを生成します。 だからトラフィッキングするクリエイティブはなく、購入するセグメントもありません。あなたは材料とゴールを提供します; プラットフォームが結果を組み立てます。 1 行で表すシフト — Triton Digital の Ben Masse が egta CEO Summit で放送 CEO の部屋に向けて述べた方法: > **入札から推論へ。インプレッションから関係へ。ミリ秒から数か月へ。** それが CMO の頭に残す文です。 ハンズオン版が欲しいですか? Foundations モジュール [A1](/docs/learning/foundations/a1-agentic-advertising) は、ライブエージェントをクエリしそのレスポンスを読むことで同じパラダイムを教えます。このトラックは代わりに推論であなたを評価します — 同じアイデア、構築なし。 ## 消費者が見るもの AI アシスタントに *「明るいリビングルームに最適な 65 インチ TV」* と尋ねるショッパーを想像してください。 * **旧世界:** あなたの TV のバナーがチャットの隣に座るかもしれません — 汎用的で、無視でき、質問から切り離されています。 * **エージェント型広告:** アシスタントがあなたの **製品カタログ** — あなたが既に保持している製品、価格、画像のリスト、Google Shopping や Amazon に送るのと同じフィード — とあなたのブランドボイスからスポンサー付き推奨を生成し、明るい部屋に実際に合うモデルを、あなたのブランドのトーンで、スポンサー付きであることの明確な開示とともに指名します。 あなたはその文を決して書きませんでした。あなたは材料 — 製品データ、ブランドボイス、ルール — を供給し、プラットフォームがその瞬間に適したメッセージを組み立てました。 ショッパーにフォローアップがある場合(*「グレアはどう処理する?」*)、**[Sponsored Intelligence](/docs/sponsored-intelligence/overview)** は、静的な広告で終わるのではなく、会話を *あなたのブランドと共に* 続けさせます。そのマルチターンのブランド対消費者の会話 — バナーではなく — が、このチャネルの最も深い形態で、CMO に見せる価値のあるものです。 **エージェンシー?** これがクライアントへのあなたの売り込みです: 完成したクリエイティブのトラフィッキングをやめ、彼らのデータをそれを生成するプラットフォームにオーケストレーションし始めます。 **個人または SMB?** あなたは既に Google Shopping でこれを生きています — 製品フィードをプッシュし、プラットフォームがあなたの製品をマーチャンダイズします。AI サーフェスは同じように機能します: AI がバナーを表示する代わりにあなたのフィードから推奨を書きます。作る新しいクリエイティブはありません。 ## 読書リスト 「なぜ既存のアプローチが不十分か」と「キャンペーンから材料へ」 — 逆転したデータフローを平易な言葉で。 2 つの標準がどこで異なりどう協働するか — Sponsored Intelligence はあなたのプログラマティックスタックの隣に座ります。 メディアチームがどう 1 つのプロトコルを通じてディスカバリー、クリエイティブ、実行、レポートを実行するか。 プラットフォームがその会話のために、その瞬間にメッセージを生成する広告モデル。 ## 主要概念 * **逆転したデータフロー** — プログラマティックはコンテキストなしにリモート入札者へ薄いシグナルを送り出す; エージェント型広告はコンテキストを保持しレスポンスを生成するプラットフォームへリッチなデータを持ち込む * **キャンペーンから材料へ** — あなたはブランドボイス、ルール、ゴール(製品を売るなら製品カタログも)を提供する; プラットフォームが広告を組み立てる。より良い材料、より良い結果 * **AI アプリのバナーではない** — 広告はあなたのブランドデータから生成され、チャットウィンドウにボルト留めされたディスプレイユニットではなく、体験にネイティブに感じられる * **置き換えではなく追加** — Sponsored Intelligence はあなたの DSP、エージェンシー、測定ツールの隣に座る新しいチャネル * **AI が実行し、人間が説明責任を持ち続ける** — エージェントが作業をするが、重要な決定は人々に留まる。実行を委任することは判断を委任することではない — 「でも誰がコントロールしているのか?」への最もクリーンな回答 ## あなたがデモンストレーションすること Sage は会話を通じて 3 つのデモンストレーションを検証します — すべての学習者に同じ: 1. **逆転したデータフローを説明** し、なぜコンテキストへデータを持ち込むことがリクエストを送り出すことに勝るか。`l1_ex1_sc_reversed_data_flow` 2. **CMO に説明** — 平易な言葉で「プラットフォームが私たちのブランドデータからメッセージを生成する」であり、「AI アプリのバナー広告」ではない。`l1_ex1_sc_cmo_explanation` 3. **あなたが知るモデルと対比** — トラフィッキングするクリエイティブなし、購入するセグメントなし; トラフィッキングされたアセットとターゲティングの代わりに材料とゴール。`l1_ex1_sc_no_creative_to_traffic` ## 評価ルーブリック | Dimension | Weight | Sage が評価するもの | | ------------ | ------ | ---------------------------- | | パラダイム理解 | 35% | 逆転したデータフローを中核の区別として把握 | | 経営陣コミュニケーション | 35% | 非技術的な経営陣向けにコンセプトを翻訳できる | | レガシーとの対比 | 20% | ミスマッピングせずにプログラマティック / IO と対比 | | フレーミングの正確さ | 10% | 一般的な誤解を回避 | 合格しきい値: 70%。スコアは内部的です — あなたはモジュールを、習熟を実証するまで続く会話として体験します。 「認定モジュール L1 を始めたい。」 # L2: コントロールサーフェスとしてのデータ・ブランド・ガバナンス Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/decision-makers/l2-data-brand-governance AdCP Decision-Makers モジュール L2: あなたが制御するブランド側のデータ、生成時のブランドセーフティ、なぜあなたの IAS / DV / Nielsen 測定スタックが持続するか。推論のみ — コードなし。 # L2: コントロールサーフェスとしてのデータ・ブランド・ガバナンス **約 15 分** | 前提条件: L1 | 無料 プラットフォームが広告を生成する(L1)なら、あなたのコントロールはどこにあるのか? それは **あなたが投入するもの** にあります。このモジュールは、意思決定者の組織が提供する入力、そしてそれらの入力 — キャンペーン UI ではなく — がどうあなたのレバーになるかについてです。それらは **チェックリストではなくダイヤル** です: プロトコルが *実行* するのに必要なものはごくわずかで(アカウント、予算、購入するもの)、より多く — そしてより良く — 投入するほど、結果はあなたの意図をより反映します。 3 つのアイデアが仕事の大半を行います: * **あなたの入力がダイヤルです。** プラットフォームはあなたがプッシュするものから広告を生成するので、あなたの入力が *コントロールそのもの* です。**ブランドアイデンティティ**(ボイス、ガイドライン、ポジショニング)がそれがどう聞こえるかを形作ります。製品を広告するとき **カタログ** が製品推奨を駆動します — そこでは入力品質が広告品質を駆動します: 薄いカタログ(タイトルと価格だけ、それ以上なし)は薄く汎用的な推奨を生みます。ブランド認知キャンペーンはカタログをまったく必要としません — ブランドアイデンティティとブリーフがそれを運びます。より多くのコントロールとより良い結果が欲しいときにダイヤルを上げます。 * **ブランドセーフティは生成時に起こります。** あなたはプラットフォームが *広告が生成される間に*、何かが表示される前に強制する適合性ルールをプッシュします。それは、事後のチェックが提供しない、AI があなたのブランドをどう表現するかのコントロールを与えます — そしてコンテンツがその場で生成されプラットフォームを決して離れない場所では、それが唯一の実行可能なメカニズムです。それは隣接性とは異なる問題です: 悪いプレースメントを避けるだけでなく、AI があなたのブランドについてどう話すかを制御することです。法的・規制的コンプライアンス(COPPA、GDPR、HFSS)は別で自動です — ガバナンスエージェントが、あなたが何をプッシュするかにかかわらず共有 [ポリシーレジストリ](/docs/governance/policy-registry) ルールを強制します。**コンテンツ標準はあなたが上に追加する *ブランド固有* のコントロールで、オプションです** — AI があなたをどう表現するかについてより厳密な発言権が欲しいときに手を伸ばします。 * **あなたの測定スタックが引き継がれます。** あなたは IAS、DV、Nielsen、Comscore の契約と認定を保持し、同じフレームワーク — MMM、マルチタッチアトリビューション、インクリメンタリティ — でこのチャネルを評価します。Sponsored Intelligence はあなたのプランの新しいチャネルで、新しい測定パラダイムではありません。2 つの適応: コンバージョンイベントをプッシュすることでプラットフォームが実際の結果に向けて最適化でき、はかない AI 生成サーフェスでは、古典的な *ページを IAS/DV に送る* 隣接性チェックがコンテンツ標準キャリブレーションモデルにシフトします — 契約は持続し、メカニズムが適応します。 **エージェンシー?** あなたの所有権の答えはオーケストレーションです: あなたはクライアントのカタログと `brand.json` を彼らに代わって保守し、キャンペーンが実行される前にそれらの入力がリッチであることを確認します。 **個人または SMB?** 既存の Shopify またはコマースフィードが既にカタログ *そのもの* で、パートナーが配管を処理します。「測定が引き継がれる」とは、あなたの ROAS / CPA ダッシュボードとコンバージョン追跡が依然として機能することを意味します — 始めるのに IAS / DV / Nielsen 契約は必要ありません。 ## 読書リスト なぜあなたの製品フィードが主要な材料か — タイトル、説明、価格、画像がクリエイティブ入力になる。 機械可読なブランドアイデンティティ — ボイス、ビジュアルガイドライン、ポジショニング — を AI プラットフォームが読み、あなたのように聞こえる。 適合性ルールが事後に検証されるのではなく生成時にどう強制されるか。 コンテンツ標準をプラットフォームにプッシュし監査証跡を返すモデル。 短い答え: いいえ — あなたは IAS / DV / Nielsen の契約と認定を保持します。 AdCP は MRC 認定の測定標準ではありません; それは既存のツールが消費するデータを運びます。 ## 主要概念 * **あなたが所有するレバー** — ブランドアイデンティティ(`brand.json`)は常に出力を形作る; カタログとコンバージョンイベントは製品・結果最適化キャンペーンで入る; コンテンツ標準は上のオプションのコントロール * **入力品質が広告品質を駆動** — リッチで正確な入力(詳細なカタログ、明確なブランドボイス)は強い広告を生む; 薄い入力は汎用的なものを生む * **生成時強制** — コンテンツ標準は生成中に適用され、ブロックリストやサードパーティのボルト留めとしてではない * **コンプライアンスはあなたのために処理される** — ガバナンスエージェントが共有ポリシーレジストリルール(COPPA、GDPR、HFSS)を自動的に強制する; コンテンツ標準はあなたのブランド固有のコントロールで、法的バックストップではない * **測定の継続性** — あなたは測定契約、認定、評価フレームワークを保持する; コンバージョンイベント最適化と(AI 生成サーフェスでの)キャリブレーションベースの適合性が適応するもの ## 入力はどれだけ良くある必要があるか? より良い入力はより多くのコントロールとより良い結果を意味します — これはあなたが持つ最も安価なレバーです。ここのどれもハードな前提条件ではありません; それは **品質ダイヤル** で、最も重要なものはあなたが何を広告するかに依存します。 | Input | Thin | Ready | Strong | | -------------------------------- | ----------- | -------------------- | --------------------------------- | | **ブランドアイデンティティ(`brand.json`)** | ロゴ + 名前 | + ボイスとトーンのガイダンス | + ポジショニング、do/don't 言語、ビジュアルガイドライン | | **製品カタログ** *(製品キャンペーン)* | タイトル + 価格のみ | + 説明、画像、在庫状況 | + 構造化属性(サイズ、色、カテゴリー)、最新に保つ | | **コンテンツ標準** *(オプションのブランドコントロール)* | なし | 避けるトピック | + 承認されたクレームと生成時に強制される適合性ルール | | **コンバージョンイベント** *(結果への最適化)* | なし | 1 つのコアイベント(購入 / リード) | + 実際の結果を定義するイベント、成功指標にマップ | **どこに準備を費やすか:** 品質は **ブランドアイデンティティ** で複利になります — そして製品を売るなら **カタログ** も — なので費やす前にそれらを強化します; まばらなフィードや欠けたブランドボイスは最初に **リッチにする** べきものです。**コンテンツ標準** と **コンバージョンイベント** は、より厳密なブランド適合性または結果最適化が欲しいときに上げるコントロールです — 有用で、必須ではありません。そして **ゴール** を設定します — あなたが何に向けて最適化するか(ターゲット CPA、ROAS、エンゲージメントあたりコスト、または認知のためのリーチ) — プラットフォームが成功が何を意味するか知るように。 ## あなたがデモンストレーションすること Sage は会話を通じて 3 つのデモンストレーションを検証します — すべての学習者に同じ: 1. **あなたが所有する入力をコントロールレバーとして識別** — ブランドアイデンティティは常に、カタログとコンバージョンイベントは製品キャンペーンで — し、入力品質が広告品質を駆動することを説明。`l2_ex1_sc_data_ownership` 2. **生成時のブランドセーフティをコントロールとして説明** — AI があなたのブランドをどう表現するかのコントロールで、ブロックリストや事後の隣接性検証とは異なる。`l2_ex1_sc_generation_time_brand_safety` 3. **あなたの測定スタックがどう引き継がれるかを述べる** — あなたは IAS / DV / Nielsen 契約と同じ MMM / アトリビューションフレームワークを保持する — そして何が適応するか(コンバージョンイベント最適化; AI 生成サーフェスでのキャリブレーションベースの適合性)を名指す。`l2_ex1_sc_measurement_persists` ## 評価ルーブリック | Dimension | Weight | Sage が評価するもの | | ---------- | ------ | ------------------------- | | データ所有権の明確さ | 35% | 組織が所有する入力をコントロールレバーとして識別 | | ガバナンスモデル | 30% | 生成時のブランドセーフティをコントロールとして理解 | | 測定の継続性 | 25% | 測定契約が持続することと何が適応するかを知る | | 組織への適用 | 10% | コントロールサーフェスを自身の組織に接続 | 合格しきい値: 70%。スコアは内部的です — あなたはモジュールを、習熟を実証するまで続く会話として体験します。 「認定モジュール L2 を始めたい。」 # L3: 意思決定とリーダーシップ Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/decision-makers/l3-deciding-and-leading AdCP Decision-Makers モジュール L3: エージェンシーやチームをブリーフし、パイロット対標準化を決め、意思決定アーティファクト — ビジネスケース、エージェンシーブリーフ、または段階的採用計画 — を生成します。キャップストーンは構築ではなく意思決定です。 # L3: 意思決定とリーダーシップ **約 15 分** | 前提条件: L2 | 無料 L1 と L2 はあなたにモデルとコントロールサーフェスを与えました。L3 はあなたが決断を下し、組織をそれを通じて導く場所です。このトラックのキャップストーンは **意思決定アーティファクト** です — 構築ではなく、仕様でもなく、あなたのロールが実際に生成するもの: ビジネスケース、エージェンシーブリーフ、または段階的採用計画。 2 つの判断が最も重要です: * **実行する人々をブリーフする。** あなたの仕事は構築ではなくブリーフです。ブランドはカタログ、`brand.json`、コンテンツ標準、ゴールを提供します; エージェンシーまたはインハウスチームが AI サーフェス全体でキャンペーンを実行し配信をレポートします。既存のエージェンシー関係とプログラマティックスタックへの追加としてフレーミングします — バイヤーエージェントは DSP の隣に座ります。 * **パイロットか標準化か。** サーフェスが実験的なとき、安価かつ可逆的に学ぶために 1 つのパートナーと 1 つのサーフェスをパイロットします。価値が証明されたら、プラットフォームごとに直接取引を交渉する代わりに 1 つの統合が多くのサーフェスに到達するようプロトコルで標準化します。ハイプではなく、可逆性とリスクから推論します。 ## 読書リスト ブランド、エージェンシー、SMB がそれぞれ最初に何をするか — そして何をブリーフし何を所有するか。 バイヤーエージェントが必要か、それともエージェンシー、アドネットワーク、コマースプラットフォームパートナーを通じて機能できるか。 どのサーフェスが明示的に実験的で変わりうるか — パイロット対標準化の決断への入力。 プロトコルの配管を処理するエージェンシー、アドネットワーク、プラットフォームのメンバーディレクトリ。 ## 主要概念 * **実行者のブリーフィング** — ブランドが提供するもの(カタログ、`brand.json`、コンテンツ標準、ゴール)対、返ってくると期待するもの(AI サーフェス全体のキャンペーン、配信レポート) * **追加的採用** — バイヤーエージェントはあなたのスタックとエージェンシー関係を拡張する; それらを置き換えない * **パイロット対標準化** — サーフェスが実験的なとき学ぶためにパイロット; 価値が証明されたらプロトコルで標準化し、1 つの統合が多くのサーフェスに到達する * **経済性と競争ケース** — 価格はプラットフォームごとに設定される(例: クリックあたり、セッションあたりコスト)、単一の固定レートではない; 一度標準化して多くに到達することを、プラットフォームごとの直接取引とそれが示唆する到達までの時間と比較して評価する ## 意思決定アーティファクトテンプレート あなたのキャップストーンはこれらの 1 つ — 出発点のスケルトンで、盲目的に埋めるフォームではありません。あなたのロールに合うものを使ってください。 * **機会** — 私たちのカテゴリーの消費者は既に AI アシスタントに尋ねている; 今日私たちはプレゼンスがなく、どう表現されるかのコントロールもない * **何が変わるか** — プラットフォームが私たちのブランドデータから広告を生成する; 私たちはデータを所有し、測定は引き継がれる * **私たちが所有するもの** — カタログ、`brand.json`、コンテンツ標準、コンバージョンイベント、成功指標 * **パイロット** — 1 サーフェス、1 エージェンシー、1 成功指標、学びとして償却できる予算 * **依頼** — 予算、タイムライン、誰がデータパイプラインを所有するか * **待つことのリスク** — よりリッチなブランドデータを持つ競合が AI サーフェス需要に先に到達する * **クライアントとゴール** * **クライアントが提供するもの** — カタログ、`brand.json`、コンテンツ標準、ゴール * **私たちが配信するもの** — 選ばれた AI サーフェス全体のキャンペーン、既存ダッシュボードへの配信レポート * **それがどこに座るか** — DSP と既存のプログラマティックの隣; 追加であり置き換えではない * **パイロット範囲** — 1 サーフェス、1 成功指標 * **P\&L ノート** — マネージドサービス(私たちがバイヤーエージェントを実行、メディア + サービスフィーをマークアップ)対セルフサーブ(クライアントが実行、私たちはセットアップと戦略に課金)。1 つの統合が多くの AI サーフェスに到達するので、マージンはプラットフォームごとの労働を圧縮することから来る — プラットフォームごとにレートカードを交渉することからではない * **ステップ 1 — パートナーを選ぶ** — アドネットワークまたは Shopify タイプのアプリ; [Addie に尋ねる](https://agenticadvertising.org/chat) か [メンバーディレクトリ](https://agenticadvertising.org/members) をブラウズ * **ステップ 2 — フィードを接続する** — 既存の製品フィードにブランド基本(名前、ボイス、任意のルール)を加える * **ステップ 3 — 予算とゴールを設定する** — 小さく始め、1 つのゴール(売上? 来店?)を選び、ROAS/CPA 追跡を確認する * **ステップ 4 — レビューと拡大** — 機能するものを保持し、そこからサーフェスを追加する ## パイロットのサイジング(価格表なしで) 価格はプラットフォームごとに設定されるので、引用する単一のレートはありません。代わりにそれについて推論します: * **学びとして償却できるパイロットをサイジングする** — 予測ではなく、弁護する必要のない数字 * **1 サーフェスと 1 成功指標を選ぶ** — 結果を読めるほど変数を少なく保つ * **プラットフォームごとの実際の価格を発見する** — あなたのバイヤーエージェントまたはパートナーが購入時にセラーの製品からそれを読む * **成果あたりコストを既に信頼するベンチマークと比較する** — 同じゴールのための現在の最良チャネル **具体例 — 私たちの数字ではなく、あなたの数字。** これらは推論の *形* を示すためのプレースホルダーです; 自分のものを差し込んでください。 * パイロット予算: 学びとして償却できる **\$25k**(弁護しなければならない予測ではない) * 1 サーフェス、1 ゴール: **適格リード** * あなたのベンチマーク: 現在の最良チャネルは **約 \$40 CPA** で走る * 読み: AI サーフェスが **約 \$40 CPA 以下** に着地すれば標準化の決断を得る; **2〜3 倍高く** 来れば、それを安価に学んでスケールしない * あなたが *しなかった* ことに注意: レートカードの交渉。プラットフォームごとの価格設定(CPC / セッションあたり)は購入時にあなたのバイヤーエージェントまたはパートナーによって発見される — あなたは予算と成功指標を設定し、単価ではない 規律は、成果あたりコストを既に信頼するベンチマークと比較することです — まだ実行していないチャネルの数字を予測することではありません。 *「他に誰かがこれをやっているか?」* について — 競合の見出しを待つのではなく、サーフェスの成熟度(どのサーフェスが [実験的](/docs/reference/experimental-status) 対ライブか)と今日 [レジストリ](/docs/registry) を通じて到達可能なセラーの数でランドスケープを特徴づけます。1 つのサーフェスが実験的であることは決断への入力で、それらすべてを待つ理由ではありません。 ## あなたがデモンストレーションすること Sage は会話を通じて 3 つのデモンストレーションを検証します — すべての学習者に同じ: 1. **エージェンシーまたはチームをブリーフ** し、既存の関係への追加としてフレーミングした労働分担について。`l3_ex1_sc_brief_agency` 2. **与えられたサーフェスについてパイロット対標準化を決め**、可逆性とリスクでそれを正当化する。`l3_ex1_sc_pilot_vs_standardize` 3. **意思決定アーティファクトを生成** — 経済性、組織の準備状況、具体的な次のステップを結びつけるビジネスケース、エージェンシーブリーフ、または段階的採用計画。`l3_ex1_sc_decision_artifact` ## 評価ルーブリック | Dimension | Weight | Sage が評価するもの | | ------------ | ------ | ------------------------------ | | リーダーシップブリーフ | 30% | 入力対期待についてエージェンシーまたはチームをブリーフできる | | 意思決定推論 | 35% | リスクと可逆性でパイロット対標準化について推論する | | 意思決定アーティファクト | 25% | 一貫した、ロールに適した意思決定アーティファクトを生成する | | 競争経済性 | 10% | 経済性と競争ケースについて推論する | 合格しきい値: 70%。スコアは内部的です — あなたはモジュールを、習熟を実証するまで続く会話として体験します。 ## 次は何か L1〜L3 を完了すると **AdCP for Decision-Makers** 資格を獲得します。ここから: * [Monetizing AI ガイド](/docs/sponsored-intelligence/monetizing-ai) とあなたの意思決定アーティファクトをチームに渡す * ハンズオンパスが欲しいなら、無料の [Basics トラック](/docs/learning/overview#basics-free)(A1〜A3)がプロトコルの基礎を教え、[Buyer トラック](/docs/learning/tracks/buyer)(C1〜C4)が動作するバイヤーエージェントを構築します — プログラミング経験不要。 「認定モジュール L3 を始めたい。」 # Decision-Makers トラック Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/decision-makers/overview 意思決定者向け AdCP(L1-L3): エージェント型広告を評価・ブリーフ・意思決定するがエージェントを構築しないブランドリーダー、エージェンシー幹部、SMB オーナー向けの、無料・推論のみの認定。 # 意思決定者向け AdCP(L1–L3) **無料で誰にでも開かれています。** 3 つの短いモジュール、合計約 45 分。エージェント構築なし、コードなし、ライブエージェントへのクエリなし — この資格は戦略的推論だけで獲得します。 このプログラムの他のすべてのパスはハンズオン作業でゲートされます: Basics トラックはライブエージェントへのクエリを課し、ロールトラックは 1 つのエージェントを構築することで頂点に達します。それは実務者にとって正しいバーで — 構築を委任しつつ **評価・ブリーフ・意思決定** する人々にとっては間違ったバーです。 このトラックは彼らのためのものです: CMO 向けの提案を組み立てるブランドメディアリーダー、標準を採用すべきか決めるエージェンシーのトレーディングデスク幹部、エンジニアを雇わずに AI サーフェスに到達できるか見極める創業者。あなたは何も構築しません。構築する人々を指揮することを学びます。 L1〜L3 を完了すると **AdCP for Decision-Makers** 資格を獲得します。 ## 対象者 エージェント型広告のビジネスケースを組み立て、何を変えるべきかエージェンシーにブリーフするメディア・マーケティングの責任者。 AI サーフェス全体で購入する標準を採用すべきか — そして競争優位はどこにあるか — を決めるトレーディングデスクとプログラマティックのリーダー。 既に持っている製品データから始めて、パートナーを通じて AI プラットフォームに到達したい創業者とオペレーター。 ## このトラックがどう違うか | | Basics & ロールトラック | Decision-Makers トラック | | ---------- | ---------------------------- | ------------------------------------------ | | **評価** | ライブエージェントへのクエリ、動作するエージェントの構築 | 会話を通じた戦略的推論 | | **前提条件** | A1〜A3、次にロールトラック | なし | | **コスト** | Basics 無料; ロールトラックはメンバー限定 | 無料 | | **得られるもの** | 動作するエージェント | 意思決定アーティファクト — ビジネスケース、エージェンシーブリーフ、または採用計画 | 評価は他のすべてのモジュールと同じ [公平性ルール](/docs/learning/instructional-design#assessment-fairness) に従います: 各モジュールには必須のデモンストレーションがあり、すべての学習者に同一で、Sage が会話を通じて検証し、安定した基準 ID で記録します。違いは *何を* デモンストレーションするかです — ここではそれは推論で、決して暗記ではなく、決してコードではありません。 この資格は **戦略的流暢さ** を証明し、ハンズオン実装ではありません。エージェントをクエリし構築できることを検証する [Basics](/docs/learning/overview#basics-free) や [Practitioner](/docs/learning/overview) 資格の代替 — またはより簡単なルート — ではありません。あなたのロールが構築することなら、Basics から始めてください。 ## 3 つのモジュール エージェント型広告が実際に何であるか、なぜプログラマティックと異なるか、CMO にどう説明するか。約 15 分。 あなたが制御するブランド側の入力、生成時のブランドセーフティ、なぜ測定スタックが持続するか。約 15 分。 チームをブリーフし、パイロット対標準化を決め、意思決定アーティファクトを生成します。約 15 分。 ## 読書リスト これらは、プロトコルがバイサイドリーダー向けに公開しているのと同じナラティブです。この順序で読めば、3 つのモジュールは既に始めた会話のように感じられます。 初めてですか? 2 つの必読は **AI サーフェスのマネタイズ** と **AdCP vs OpenRTB** です — 残りは後で戻れる深掘りです。評価に入らずにすべてを読み、[パートナーを見つける](https://agenticadvertising.org/members) ことができます。資格はオプションです; 理解が要点です。 CMO、取締役会、エージェンシー、パブリッシャーに 1 回のミーティングで理解させなければならないときのための、オーディエンス別・ジョブ別に刺さるフレーミングのチートシート。 ブランド、エージェンシー、SMB 向けのバイサイドガイド: 何を提供するか、キャンペーンから材料への移行、ロール別の始め方。 エージェント型広告とプログラマティックがどう競合ではなく補完的か — 既存のスタックは持続します。 メディアチームがパートナー探しからパフォーマンス追跡まで、AdCP を通じて実行するすべてのステップのウォークスルー。 ブランドセーフティルールが、事後に検証するのではなく、コンテンツが表示される前の生成時にどう強制されるか。 AI プラットフォームがあなたのブランドのように聞こえるために読む、機械可読なブランドアイデンティティ。 なぜあなたの製品フィードが主要な材料か — そしてそれがどうクリエイティブ入力になるか。 パートナー対インハウス、AI がブランドについてどう話すかの制御、価格設定、あなたの IAS / DV / Nielsen 測定がどう引き継がれるかについての平易な回答。 ## 始める Addie を開いて「Decision-Makers トラックを始めたい」と言ってください。アカウント不要 — このトラックは無料です。 # A1: AdCP の理由 Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/foundations/a1-agentic-advertising モジュール A1: エージェンティック広告とは何か、なぜ AdCP が存在するのか?プラットフォームの断片化と共有プロトコルの根拠をカバーする無料15分のインタラクティブモジュール。 # A1: AdCP の理由 **無料モジュール** — アカウント不要。Addie と約15分。 ## 学習目標 このモジュールの終わりには次のことができるようになります: * エージェンティック広告がプログラマティックからのパラダイムシフトである理由を説明できます * AI エージェントとは何か、従来の API とどう異なるかを説明できます * AdCP が解決する断片化の問題を明確に述べられます * AdCP の広さを認識できます: ディスプレイからローカルラジオ、映画館まで20チャンネル * キャンペーンガバナンスが自律的なエージェントトランザクションに常時コンプライアンスを提供する方法を説明できます ## 読み物リスト Addie とモジュールを開始する前にこれらのページを確認してください。これらは認定プログラムのすべての概念的な基盤を提供します。 AdCP とは何か、どのように機能するか、プロトコルドメイン。完全に初めての場合はここから始めてください。 戦略的なビジョン: アロケーション vs デイトレーディング、断片化の問題、エージェントが共有プロトコルを必要とする理由。 AdCP が OpenRTB、プラットフォーム API、ダイレクト IO とどう比較されるか。エージェンティック広告がランドスケープのどこに位置するか理解します。 AdCP の実装方法を見ます。オプションですが直感を構築するのに役立ちます。 キャンペーンガバナンスがキャンペーンをメディアプランに結びつけ、すべてのトランザクションを自律的に検証する方法。 ## 主要用語 | 用語 | 定義 | | --------------- | -------------------------------------------------------------------------------------- | | **エージェンティック広告** | 推論し、交渉し、適応できる AI エージェントによって実行される広告 — 硬直した API インテグレーションとは対照的 | | **AI エージェント** | 環境を認識し、意思決定を行い、自律的に行動するソフトウェア | | **AdCP** | Ad Context Protocol — AI エージェントに広告のための共有言語を与えるオープン標準 | | **MCP** | Model Context Protocol — AI モデルを外部ツールとデータソースに接続するための標準。AdCP はトランスポートレイヤーとして MCP を使用します | | **タスク** | AdCP によって定義された個別の広告操作(例: `get_products`、`create_media_buy`) | | **キャンペーンガバナンス** | キャンペーンをメディアプランに結びつけ、3つの独立したパーティを通じてすべてのトランザクションを検証する常時コンプライアンス | 用語集全体は [AdCP 用語集](/docs/reference/glossary) を参照してください。 ## Addie で行うこと このモジュールはライブのエージェントクエリで固定されています — スライドデッキではなく `@cptestagent` からの実際の `get_products` レスポンスを見ます。Addie が次のことを案内します: * AI エージェントが従来の API と何が違うのか? * 共有プロトコルが AI 搭載広告でなぜ重要なのか? * すべてのアドテク会社が独自のエージェントプロトコルを構築した場合に何が問題になるか? * 「アロケーション vs デイトレーディング」のフレーミングがメディアバイイングの考え方をどう変えるか? * AI エージェントにお金を使う信頼をどう築くか?(常時コンプライアンス: すべてのトランザクションをプランに対して検証) * 20チャンネル: ディスプレイ、ソーシャル、検索、CTV、リニア TV、ラジオ、ポッドキャスト、DOOH、OOH、プリント、映画館、ゲーミング、リテールメディア、インフルエンサー、アフィリエイト、プロダクトプレイスメント、AI メディア ## 評価 Addie は4つの次元にわたって理解度を評価します: | 次元 | ウェイト | Addie が評価するもの | | --------- | ---- | ----------------------------------------- | | 概念的理解 | 25% | プログラマティックからエージェンティックへのパラダイムシフトを明確に述べられるか? | | 実践的知識 | 35% | エージェントにクエリしてレスポンスを解釈できるか? | | チャンネルの広さ | 20% | AdCP がデジタルだけでなく20チャンネルをカバーすることを理解しているか? | | プロトコルの流暢さ | 20% | AdCP の用語を正しく使えるか? | 合格基準: 70%。テストではなく会話 — Addie がそこまで導いてくれます。 ## このモジュールを始める Addie を開いて「認定モジュール A1 を始めたい」と言ってください。Addie が引き継ぎます。 **次:** [A2: 最初のメディアバイ](/docs/learning/foundations/a2-protocol-architecture) # A2: 最初のメディアバイ Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/foundations/a2-protocol-architecture モジュール A2: 最初の AdCP メディアバイを実行します。ライブのサンドボックスエージェントで探索、購入、クリエイティブ同期、配信を実行する無料20分のハンズオンモジュール。 # A2: 最初のメディアバイ **無料モジュール** — アカウント不要。Addie と約20分。前提条件: [A1](/docs/learning/foundations/a1-agentic-advertising)。 ## 学習目標 * メディアバイの完全なライフサイクルを実行します: 探索、購入、クリエイティブ同期、配信 * 関与するエージェントの役割を特定します: バイヤーエージェント、セールスエージェント、クリエイティブエージェント、シグナルエージェント * 各段階での実際のプロトコルメッセージを読んで理解します * ライブのエージェント間トランザクションを観察します ## 読み物リスト 完全なアーキテクチャ: ドメインマップ、アイデンティティレイヤー、トランザクションドメイン、ガバナンス、エコシステムレイヤー。 実践での MCP の機能: ツール呼び出し、レスポンスフォーマット、コンテキスト管理、非同期操作。 エージェントが他のエージェントに提供するものを発見できるようにケイパビリティを宣言する方法。 エージェント探索メカニズム — 広告エージェントの robots.txt のようなもの。 エージェント間プロトコル — 専門化されたエージェントが複雑なキャンペーンで協力する方法。 タスクが状態を移動する方法: リクエストから完了まで、非同期操作を含みます。 ## 主要用語 | 用語 | 定義 | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | **セールスエージェント** | パブリッシャーを代理し、`get_products` でインベントリを公開します。クリエイティブプロトコルも実装して同じエンドポイントからクリエイティブを処理する場合もあります。 | | **バイヤーエージェント** | ブランドまたは代理店を代理し、`create_media_buy` でメディアを購入します | | **ブランドエージェント** | `brand.json` でブランドアイデンティティとガイドラインを管理します | | **クリエイティブエージェント** | クリエイティブプロトコルを実装する任意のエージェント — `build_creative` で広告アセットを制作・適応させます。スタンドアロンサービスまたは `supported_protocols` で `"creative"` を宣言するセールスエージェント。 | | **シグナルエージェント** | `get_signals` で測定とオーディエンスデータを提供します | | **adagents.json** | エージェントケイパビリティを宣言するパブリッシャーホスト型ファイル(エージェントの robots.txt のようなもの) | | **ツール探索** | エージェントが別のエージェントのケイパビリティを読んで、サポートするタスクを知るプロセス | ## Protocol versioning すべてのリクエストは `adcp_major_version` を運びます。セラーは `get_adcp_capabilities` でサポートするバージョンをアドバタイズします。A3 と B1 は、バージョンネゴシエーションと、セラーがケイパビリティを宣言するために使うオブジェクト存在パターンをより深く掘り下げます。 ## Addie で行うこと Addie に必要なことを伝えてください: オーディエンス、目標、予算。次に各ステップが発生するにつれて確認します: 1. **探索** — `@cptestagent` に対する `get_products`、実際のレスポンス構造を確認 2. **購入** — ターゲティングと予算付きの `create_media_buy` 3. **クリエイティブ** — パブリッシャーにアセットを届けるための `sync_creatives` 4. **配信** — 結果を確認するための `get_media_buy_delivery` 各段階で実際のプロトコルメッセージを見ます。終わりには、エージェントを通じてメディアを購入したことになります。 ## 評価 | 次元 | ウェイト | Addie が評価するもの | | --------- | ---- | ------------------------------------ | | 概念的理解 | 25% | トランザクションフローとどのエージェントが何を処理するかを説明できるか? | | 実践的知識 | 35% | メディアバイを指示して配信レポートを解釈できるか? | | 問題解決 | 15% | 問題が発生したときに何が起こるかを論理的に考えられるか? | | プロトコルの流暢さ | 25% | 正しいタスク名とエージェントの役割を使えるか? | 合格基準: 70%。 ## このモジュールを始める Addie を開いて「認定モジュール A2 を始めたい」と言ってください。 **次:** [A3: AdCP のランドスケープ](/docs/learning/foundations/a3-ecosystem-governance) # A2B: 最初のエージェント呼び出しのテスト Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/foundations/a2b-testing-your-first-agent モジュール A2B: ハンズオンラボ — MCP セッションを初期化し、get_products を呼び、メディアバイを配置し、クリエイティブを添付し、コピペ可能な curl の例で実際のレスポンス形状を処理します。 # A2B: 最初のエージェント呼び出しのテスト **無料モジュール** — アカウント不要。Addie と約 20 分。前提条件: [A2](/docs/learning/foundations/a2-protocol-architecture)。 ## 学習目標 * AdCP テストエージェントに対してステートフルな MCP セッションを初期化する * 自然言語ブリーフで `get_products` を呼び、製品レスポンスを読む * `create_media_buy` でメディアバイを配置し、3 つのレスポンス形状すべてを処理する * `sync_creatives` でクリエイティブを添付し、`get_media_buys` でバイステータスをチェックする * 認証失敗、スキーマ不一致、非同期ポーリング遅延を診断し解決する ## 読書リスト セットアップから配信までのエンドツーエンドバイヤーワークフロー。 メディアバイのステータス状態 — pending\_creatives、pending\_start、active、paused、completed — と各がバイヤーにとって何を意味するか。 完全なフィールドリファレンス、必須フィールド、3 つのレスポンス形状すべて。 アセットをバイに添付する方法、ドライラン検証、割り当てパターン。 エラーコード、リトライ動作、`errors[]` 配列の読み方。 セッション初期化、`mcp-session-id` ヘッダー、ツール呼び出しフォーマット。 ## テストエージェント 以下のすべての curl の例は AdCP トレーニングエージェントをターゲットします: ``` https://test-agent.adcontextprotocol.org/mcp ``` [AgenticAdvertising.org ダッシュボード](https://agenticadvertising.org/dashboard) からの API キーが必要です。すべての例で `` を置き換えてください。 ## Addie と行うこと 5 つの呼び出しを順番にウォークスルーします。Addie は各呼び出しをデモンストレーションし、生のレスポンスを示し、次にあなた自身がそれを再現するのを導きます。 1. **初期化** — ステートフルな MCP セッションを開く; `mcp-session-id` ヘッダーを保存 2. **ディスカバリー** — ブリーフで `get_products`; 提案を読む 3. **購入** — `create_media_buy`; 3 つのレスポンス形状すべてを処理 4. **クリエイティブ添付** — `sync_creatives`; 最初にドライランで検証 5. **ステータスポール** — `valid_actions` がバイが配信中であることを示すまで `get_media_buys` ## ステップバイステップ curl リファレンス Addie とモジュールを進める間のクイックリファレンスとして、または任意のステップを独立して再現するためにこれらを使います。 ### ステップ 1 — セッションを初期化 すべてのシーケンスは `initialize` 呼び出しで始まります。レスポンスがプロトコルバージョンを設定し、`mcp-session-id` ヘッダーを返します — それを保存します。 ```bash theme={null} curl -X POST https://test-agent.adcontextprotocol.org/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "jsonrpc": "2.0", "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": { "name": "my-buyer-agent", "version": "1.0" } }, "id": 1 }' ``` レスポンスはレスポンスヘッダーに `mcp-session-id` を含みます。後続のすべての呼び出しはそれを含まなければなりません: ``` mcp-session-id: ``` ### ステップ 2 — 製品をディスカバリー `buying_mode: "brief"` とキャンペーンゴールの平易な英語の記述で `get_products` を呼びます。エージェントはキュレートされた `products[]` と実行準備完了の `proposals[]` を返します。 ```bash theme={null} curl -X POST https://test-agent.adcontextprotocol.org/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -H "mcp-session-id: " \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "get_products", "arguments": { "adcp_major_version": 3, "buying_mode": "brief", "brief": "CTV campaign, adults 25-54 in the US, $50K budget, brand safety required" } }, "id": 2 }' ``` 結果は JSON として `content[0].text` にあります。`proposals[0].proposal_id` を探します — それを `create_media_buy` に渡します。 ### ステップ 3 — メディアバイを配置 ステップ 2 からの `proposal_id` と `total_budget` を渡します。`idempotency_key` はネットワークが落ちた場合に安全にリトライできるようにします — リクエストごとに新しい UUID v4 を使います。 ```bash theme={null} curl -X POST https://test-agent.adcontextprotocol.org/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -H "mcp-session-id: " \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "create_media_buy", "arguments": { "adcp_major_version": 3, "idempotency_key": "mb-lab-20260428-001", "account": { "brand": { "domain": "nova-motors.com" }, "operator": "pinnacle-media.com" }, "proposal_id": "", "total_budget": { "amount": 50000, "currency": "USD" } } }, "id": 3 }' ``` **3 つのレスポンス形状 — これらの 1 つが見えます:** | Shape | 意味 | 次のステップ | | --------------------------------------------------------- | ------------------------- | --------------------------------------------------------------- | | `media_buy_id` + `status: "pending_creatives"` | バイ確認; クリエイティブを添付 | ステップ 4 へ | | `media_buy_id` + `status: "pending_start"` または `"active"` | バイ確認・準備完了 | クリエイティブは既に添付済みまたは不要 | | `status: "submitted"` + `task_id` | バイが非同期処理のためキュー | `task_id` で AdCP タスクをポール(下の [Async polling](#async-polling) 参照) | | `errors[]` 存在、`media_buy_id` なし | 拒否 — `errors[0].code` を読む | リクエストを修正し、新しい `idempotency_key` でリトライ | ### ステップ 4 — クリエイティブを添付 `pending_creatives` 状態のバイは、`sync_creatives` を呼ぶまで配信できません。最初に `dry_run: true` を使い、何も書き込まずにクリエイティブ形状を検証します。 ```bash theme={null} curl -X POST https://test-agent.adcontextprotocol.org/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -H "mcp-session-id: " \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "sync_creatives", "arguments": { "adcp_major_version": 3, "idempotency_key": "sc-lab-20260428-001", "account": { "brand": { "domain": "nova-motors.com" }, "operator": "pinnacle-media.com" }, "creatives": [ { "creative_id": "nova-ctv-30s-v1", "format_id": { "agent_url": "https://test-agent.adcontextprotocol.org", "id": "ctv_1920x1080_30s" }, "assets": [ { "asset_id": "video_url", "url": "https://cdn.example.com/nova-ctv-30s.mp4" } ] } ], "dry_run": true } }, "id": 4 }' ``` 適用するには `"dry_run": true` を削除します。レスポンスは `creatives[].status` を含みます — `approved`、`pending_review`、または `rejected`。 ### ステップ 5 — ステータスをチェック ステップ 3 からの `media_buy_id` で `get_media_buys` をポールし、ライフサイクル状態と `valid_actions` を見ます。 ```bash theme={null} curl -X POST https://test-agent.adcontextprotocol.org/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -H "mcp-session-id: " \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "get_media_buys", "arguments": { "adcp_major_version": 3, "media_buy_ids": [""] } }, "id": 5 }' ``` `media_buys[0].status` フィールドは `pending_creatives`、`pending_start`、`active`、`paused`、`completed`、`rejected`、または `canceled` のいずれかです。`valid_actions` 配列はバイヤーが次に何ができるかを教えます。 ## Async polling `create_media_buy` が `status: "submitted"` と `task_id` を返すとき、バイはキューに入っています。タスクが完了するまでポールします: ```bash theme={null} curl -X POST https://test-agent.adcontextprotocol.org/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -H "mcp-session-id: " \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "get_task_status", "arguments": { "task_id": "", "include_result": true } }, "id": 6 }' ``` `get_task_status` は AdCP アプリケーション層のタスクポーリングツールです。3.x では、このエイリアスを広告しないセラーも、同じ snake\_case ペイロードでレガシー AdCP `tasks/get` 表面を露出します。いずれの AdCP ポーリング表面も、独自のタスクワイヤ形状を使うトランスポートネイティブな MCP/A2A `tasks/*` メソッドと混同しないでください。 2〜5 秒ごとにポールします。AdCP タスク `status` が `completed` のとき、`result` フィールドは `media_buy_id` を持つ完全な `create_media_buy` レスポンスを含みます。すべての AdCP タスクステータス値については [タスクライフサイクル](/docs/building/by-layer/L3/task-lifecycle) ドキュメントを参照。 ## 一般的なエラー | Symptom | 考えられる原因 | 修正 | | ------------------------------------- | ---------------------- | --------------------------------------------------------------------------------------- | | HTTP 401 / `error: "invalid_token"` | 期限切れまたは間違った API キー | ダッシュボードからトークンを再発行; `Bearer` プレフィックスを確認 | | HTTP 401 / `error: "invalid_request"` | `Authorization` ヘッダー欠如 | すべての呼び出しに `-H "Authorization: Bearer "` を追加 | | ボディに `errors[]`、`media_buy_id` なし | スキーマ検証失敗 | `errors[0].field` と `errors[0].code` を読む; フィールドを修正し **新しい** `idempotency_key` でリトライ | | `status: "submitted"` が無期限に留まる | 非同期タスクが停滞 | `get_task_status` またはレガシー `tasks/get` で AdCP タスクステータスをチェック; `failed` なら拒否理由のためタスクエラーを読む | | `mcp-session-id: invalid` エラー | セッション期限切れまたはヘッダー欠如 | ステップ 1 を再実行して新しいセッション ID を取得 | ## 評価 | Dimension | Weight | Addie が探すもの | | --------- | ------ | ------------------------------------------------ | | 概念的理解 | 10% | MCP セッションライフサイクルとなぜ `mcp-session-id` が必要か記述できるか? | | 実践的知識 | 40% | 正しいタスク名とリクエスト形状で 5 つの呼び出しすべてを順番にトレースできるか? | | 問題解決 | 30% | 各ステップが失敗するか予期しないレスポンスを返すときに何が起こるか推論できるか? | | エラー回復 | 20% | 認証失敗、スキーマエラー、非同期ポーリング遅延の正しい修正を識別できるか? | 合格しきい値: 70%。 ## このモジュールを始める Addie を開いて「認定モジュール A2B を始めたい」と言ってください。 **次:** [A3: AdCP ランドスケープ](/docs/learning/foundations/a3-ecosystem-governance) # A3: AdCP のランドスケープ Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/foundations/a3-ecosystem-governance モジュール A3: すべての AdCP ドメインの概要 — メディアバイ、クリエイティブ、カタログ、アカウント、シグナル、ガバナンス、スポンサードインテリジェンス。無料15分のインタラクティブ概要。 # A3: AdCP のランドスケープ **無料モジュール** — アカウント不要。Addie と約15分。前提条件: [A2](/docs/learning/foundations/a2-protocol-architecture)。 これはサーベイコースです。すべてに触れ、深掘りはしません。AdCP エコシステム全体のマップを得ます — すべてのプロトコルドメイン、すべての探索メカニズム、すべてのガバナンスレイヤー。好奇心を刺激し、どこをさらに深く学ぶかを示すよう設計されています。 A1 + A2 + A3 を完了すると **AdCP basics** クレデンシャルを取得できます。 ## 学習目標 * `brand.json` とは何か、すべてのブランドが持つべき理由を説明できます * `adagents.json` とエージェントが互いを発見する方法を説明できます * AdCP プロトコルドメイン8つすべてとそれぞれが何をカバーするかを名付けられます * ロールトラックでさらに深く探求したい分野を特定できます ## 読み物リスト ブランドアイデンティティの主張、brand.json 探索、ブランド階層。4つのバリアント: ハウスポートフォリオ、ブランドエージェント、リダイレクト、最小限。 ガバナンスプロトコル: プロパティガバナンス、ブランドガバナンス、コンテンツ基準、クリエイティブガバナンス、キャンペーンガバナンス。 常時コンプライアンス: キャンペーンをメディアプランに結びつけ、3つの独立したパーティを通じてすべてのトランザクションを検証します。 ブランドが ID で参照する広告規制と標準のコミュニティ維持ライブラリ。 クリエイティブプロトコル: アセット、フォーマット、マニフェスト、クリエイティブエージェント、20チャンネルの適応。 シグナルプロトコル: オーディエンスセグメント、コンテキストシグナル、測定データ、最適化。 AI アシスタントでの会話型ブランド体験 — 真に新しい広告モデル。 エージェントがサポートするものを宣言して他のエージェントが発見できるようにする方法。 商取引レイヤー: 広告主、オペレーター、認証、請求、アカウントライフサイクル。 ## Addie でカバーすること Addie が各ドメインをクイックなライブ例とともに案内します — 各エリアが何をするかを理解するのに十分です: **探索とコミュニティ:** * `brand.json`: `/.well-known/brand.json` にあるブランドの機械可読アイデンティティ * `adagents.json`: パブリッシャーがどのエージェントがインベントリにアクセスできるかを宣言する方法 * コミュニティレジストリ: エージェントとブランドが互いを見つける方法 * AgenticAdvertising.org: ワーキンググループ、業界評議会、仕様の進化方法 **プロトコルドメイン:** * **アカウント** — 商取引アイデンティティ、オペレーター請求 vs エージェント請求、アカウントライフサイクル、`get_adcp_capabilities` * **メディアバイ** — 提案、予測、絞り込み、パッケージ、キーワードターゲティング、ジオプロキシミティ * **クリエイティブ** — フォーマット vs マニフェスト、20チャンネル、`build_creative` による AI 生成 * **シグナル** — オーディエンスデータ、プライバシー準拠シグナル、コンバージョントラッキング、アトリビューション * **ガバナンス** — コンテンツ基準、プロパティリスト、AI 駆動ブランドセーフティの Oracle モデル、キャンペーンガバナンス(マルチパーティバリデーション、予算権限、ポリシーレジストリ) * **スポンサードインテリジェンス** — 会話型ブランド体験、CPM ではなくコンバーセーション単価 * **ブランドプロトコル** — `brand.json` 解決、ブランドアイデンティティの主張、ブランド階層 * **レジストリ** — エンティティ解決、エージェント探索、コミュニティディレクトリ ## 評価 | 次元 | ウェイト | Addie が評価するもの | | ------- | ---- | ------------------------------------------- | | 広さ | 35% | 各プロトコルドメインが何をするか説明できるか? | | 探索メカニズム | 25% | brand.json、adagents.json、ケイパビリティ探索を理解しているか? | | 主要コンセプト | 25% | フォーマット vs マニフェスト、請求モデル、Oracle モデルを説明できるか? | | 統合 | 15% | プロンプトなしでドメイン間のコンセプトをつなげられるか? | 合格基準: 70%。 ## このモジュールを始める Addie を開いて「認定モジュール A3 を始めたい」と言ってください。 **次:** ロールトラックを選ぶ — [パブリッシャー](/docs/learning/tracks/publisher)、[バイヤー](/docs/learning/tracks/buyer)、または [プラットフォーム](/docs/learning/tracks/platform) # 苦情と申し立て Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/policies/complaints AdCP 認定プログラムの苦情ポリシー: 申し立ての方法、調査手続き、学習者と関係者のための解決タイムライン。 # 苦情と申し立て **採択:** 2026年3月 **責任者:** AgenticAdvertising.org プログラムリーダーシップ ## 目的 すべての学習者または関係者は、AdCP 認定プログラムに関する懸念を提起できます。このポリシーは苦情の申し立て、調査、解決の方法を説明します。 ## 苦情の申し立て方法 件名「Certification complaint」と懸念の説明を記載して [certification@agenticadvertising.org](mailto:certification@agenticadvertising.org) にメールします。 ## 対象となるもの * 評価の公平性に関する懸念 * アクセシビリティの問題 * コンテンツの正確性に関する異議 * 行為上の問題 * プライバシーに関する懸念 * 認定プログラムに関連するその他の事項 ## タイムライン | ステップ | 期間 | | ---- | ----- | | 受領確認 | 2営業日 | | 調査 | 10営業日 | | 解決 | 20営業日 | ## 解決 プログラムが懸念を調査し、申立人に結果を伝えた時点で苦情は解決されたとみなす。可能なアウトカムには、是正措置の実施、カリキュラムの更新、ポリシーの明確化、または説明付きで根拠なしと判断された懸念が含まれます。 申立人が解決に同意しない場合、エスカレーションを要求できます。 ## エスカレーション **内部:** 苦情が満足のいく形で解決されない場合、学習者は解決メールにエスカレーション要求を返信することで AgenticAdvertising.org リーダーシップにエスカレーションできます。リーダーシップは10営業日以内にレビューします。 **外部:** 内部解決が不満足で苦情が認定基準に関わる場合、学習者は関連する認定機関に直接連絡できます。 ## 機密性 苦情は機密として処理されます。調査に直接関与する者だけが苦情の詳細にアクセスできます。 ## 報復の禁止 苦情を申し立てても、学習者の認定ステータス、進捗、またはプログラム資料へのアクセスに影響を与えることはない。 ## 記録保持 すべての苦情と解決は文書化され、システム上の問題を特定してプログラムの改善を促進するために四半期ごとにレビューされます。 # 利益相反 Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/policies/conflict-of-interest AdCP 認定プログラムの利益相反ポリシー: 開示要件と商業的利益をカリキュラムおよび評価の決定から分離すること。 # 利益相反 **採択:** 2026年3月 **責任者:** AgenticAdvertising.org プログラムリーダーシップ ## 目的 このポリシーは、AdCP 認定プログラムが客観性を維持し、商業的利益がカリキュラムコンテンツや評価アウトカムに影響を与えないことを確保します。 ## 開示要件 カリキュラム設計、評価基準の定義、またはスコアリングキャリブレーションに関与するすべての人は以下を開示しなければなりません: * カリキュラムでカバーされているプロダクトを持つ会社への雇用または投資 * AgenticAdvertising.org 加盟組織との顧問関係 * 認定アウトカムへの金銭的利益 開示はプログラムリーダーシップに行われ、貢献者がカリキュラムの決定に参加する前にレビューされます。 ## 利益の分離 * カリキュラムコンテンツは特定のベンダーの実装ではなく、オープン AdCP プロトコル仕様に基づいています * モジュールは特定の商業製品やサービスを推進しません * 評価ルーブリックは指導開始前に定義され、すべての学習者に一貫して適用されます ## AI 指導 Addie はカリキュラムチームによって定義された運用ルールに従う。教育方法論は[教育設計フレームワーク](/docs/learning/instructional-design)に文書化され、均一に適用されます。商業的利益は Addie の教え方やスコアリングに影響を与えない。 ## 違反 未開示の利益相反は、影響を受けるカリキュラムコンテンツのレビューとカリキュラム貢献者役割からの潜在的な排除をもたらす。 # 知的財産 Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/policies/intellectual-property AdCP 認定プログラムの知的財産ポリシー: カリキュラム資料、評価、学習者が作成したコンテンツの所有権と使用権。 # 知的財産 **採択:** 2026年3月 **責任者:** AgenticAdvertising.org リーダーシップ ## 目的 このポリシーは、AdCP 認定プログラムで作成および使用される資料の所有権と使用権を明確にします。 ## コース資料 カリキュラムコンテンツ — レッスンプラン、評価基準、教育ノート、演習を含む — は AgenticAdvertising.org が所有します。これらの資料は許可なく複製または配布することはできません。 ## プロトコル仕様 AdCP はオープンプロトコルです。認定プログラムはプロトコルを教えるが、実装の権利を付与または制限しません。認定を完了しても、プロトコル仕様に対する知的財産権は付与されない。 ## 学習者が作成したコンテンツ ビルドプロジェクトモジュール(B4、C4、D4)中に作成されたコードと設定は学習者の財産として残る。AgenticAdvertising.org は学習者の実装に対する所有権を主張しません。 ## 会話コンテンツ 教育会話は匿名化された集約形式でカリキュラムの改善に使用される場合があります。個別の会話は公開または共有されない。 ## クレデンシャルマーク AdCP Basics、Practitioner、Specialist クレデンシャル名と関連バッジは AgenticAdvertising.org の商標です。クレデンシャル保持者は職業的文脈で取得したクレデンシャルを表示できます。 ## サードパーティコンテンツ モジュールでリンクされている学習リソースは公式の AdCP ドキュメントを指します。カリキュラムの例で参照されているサードパーティの商標はそれぞれの所有者に属します。 ## 違反 コース資料またはクレデンシャルマークの無断使用は、標準的な商標および著作権の施行の対象となります。 # 学習者記録とプライバシー Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/policies/learner-records AdCP 認定プログラムの学習者記録ポリシー: データ保持、プライバシー保護、成績証明書へのアクセス権、プログラム参加者の削除手続き。 # 学習者記録とプライバシー **採択:** 2026年3月 **責任者:** AgenticAdvertising.org エンジニアリングおよびプログラムリーダーシップ ## 目的 このポリシーは、AdCP 認定プログラムで学習者データがどのように収集、保存、アクセス、保持されるかを定める。 ## 保存するもの 認定プログラムに参加する各学習者について: * アイデンティティ(氏名、メール、組織) * モジュールの進捗とステータス(未開始、進行中、完了、テストアウト) * 完了日 * 評価スコア(次元ごと、内部のみ) * 取得したクレデンシャルと授与日 * 教育チェックポイント(カバーされたコンセプト、学習者の強みとギャップ、暫定スコア) * 完了後フィードバック ## データの保存場所 学習者記録は2つのシステムで維持されます: * **アプリケーションデータベース**(PostgreSQL)— 進捗、スコア、チェックポイント、フィードバック * **Certifier** — クレデンシャルバッジ、確認 URL、有効期限追跡 ## 保持 学習者記録は最後の活動日から最低**7年間**保持されます。これは IACET 認定要件に準拠します。 ## 成績証明書へのアクセス 学習者はいつでも認定ダッシュボードを通じて進捗と取得したクレデンシャルを確認できます。各クレデンシャルにはサードパーティによる確認のための固有の確認 URL と QR コードが含まれます。 ## プライバシー * 評価スコアは内部のみで、公開して共有されることはない。クレデンシャルステータス(取得済みまたは未取得)だけが他者に見える * 教育会話は匿名化された集約形式でカリキュラムの改善に使用される場合があります。個別の会話は公開または共有されない * 学習者フィードバックはカリキュラム改善のために集約でレビューされます。個別のフィードバックは公開されて帰属されることはない **GDPR:** 学習者は [certification@agenticadvertising.org](mailto:certification@agenticadvertising.org) に連絡することでデータのエクスポートまたは削除を要求できます。削除要求は、認定コンプライアンスに保持が必要な場合を除いて受け入れられます(最低7年間)。そのような場合、記録は削除ではなく匿名化されます。 ## 違反 データ取り扱いの違反は、5営業日以内に是正措置を伴ってプログラムリーダーシップにエスカレーションされます。 # 差別禁止ポリシー Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/policies/nondiscrimination AdCP 認定プログラムの差別禁止ポリシー: すべての学習者の平等なアクセス、インクルージョン基準、保護された特性に関係なく配慮の手続き。 # 差別禁止ポリシー **採択:** 2026年3月 **責任者:** AgenticAdvertising.org プログラムリーダーシップ ## 目的 AgenticAdvertising.org は、人種、肌の色、国籍、性別、年齢、障害、宗教、性的指向、退役軍人ステータス、遺伝情報、またはその他の保護された特性に関係なく、すべての学習者に AdCP 認定プログラムへの平等なアクセスを提供することにコミットしています。 ## 基準 * すべての認定モジュールは、定められた前提条件を満たす学習者が利用できます * AI 提供の指導は、すべての学習者に同じ教育方法論と評価基準を適用します * 評価は公開されたルーブリック次元に対する実証された知識とコンピテンシーのみに基づく * 個人的な特性に基づいて有利または不利になる学習者はいない ## アクセシビリティ * 指導はスクリーンリーダーと支援技術に対応したウェブベースのテキスト会話で提供されます * 時間プレッシャーの評価はない — プログラムはマスタリーベースの進歩を使用し、各学習者に必要な時間を与えます * 配慮が必要な学習者は [certification@agenticadvertising.org](mailto:certification@agenticadvertising.org) に連絡できます ## 言語 指導は英語で提供されます。学習者は好みの用語を使って説明を求めることができます。Addie は各学習者のコミュニケーションスタイルと技術的な習熟度に適応します。 ## 違反 このポリシーの違反は[苦情処理プロセス](/docs/learning/policies/complaints)を通じて報告する必要があります。報告は10営業日以内に調査されます。 # 要員資格 Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/policies/personnel-qualifications AdCP 認定プログラムの要員資格: カリキュラム設計者、コンテンツレビュアー、Addie AI 教育アシスタントの基準。 # 要員資格 **採択:** 2026年3月 **責任者:** AgenticAdvertising.org プログラムリーダーシップ ## 目的 このポリシーは、AdCP 認定プログラムの設計、レビュー、提供に関わるすべての人の資格基準を定める。 ## カリキュラム設計 モジュールのレッスンプラン、評価基準、スコアリングルーブリックは、広告技術や AdCP プロトコルの実証された専門知識を持つ主題専門家が設計します。専門知識はプロトコル開発への直接的な関与、業界経験、またはその両方によって確立されます。 ## コンテンツレビュー カリキュラムの変更はデプロイ前に標準のコードレビュープロセス(プルリクエスト)を通じてレビューされます。プロトコルの正確性は AdCP 仕様に対して検証されます。レビュー基準を満たさない変更はデプロイされない。 ## AI 教育アシスタント Addie は Claude(Anthropic 製)を搭載しています。教育動作は[教育設計フレームワーク](/docs/learning/instructional-design)に文書化された運用ルールによって管理され、以下をカバーする: * ソクラテス的方法論とターン構造 * 評価の公平性とスコアリングキャリブレーション * 学習者データの取り扱いとプライバシー * デモと演習のためのツールの使用タイミングと方法 Addie はサーバーサイドのバリデーション要件なしにクレデンシャルを授与できない: 最低エンゲージメント時間、最低会話ターン数、チェックポイント要件、スコア一貫性チェック。これらは AI の判断だけでなく、アプリケーションによって適用されます。 ## 継続的な資格 カリキュラム貢献者は AdCP ワーキンググループへの参加、プロトコル開発、四半期カリキュラムレビューを通じて最新の状態を保つ。 ## 違反 教育動作の変更はパフォーマンス比較を可能にするために `CODE_VERSION` のバンプが必要です。レビュープロセスをバイパスするカリキュラムの変更は元に戻されます。 # 再認定とプロトコル変更更新 Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/policies/recertification AdCP 認定の再認定ポリシー: ターゲットを絞ったプロトコル変更更新、デルタ評価、学習者通知、AdCP 3.1 正準フォーマット S2 の根拠。 # 再認定とプロトコル変更更新 **採択:** 2026 年 5 月 **責任当事者:** AgenticAdvertising.org プログラムリーダーシップ このポリシーは IACET 準拠かつ CPD 準拠です。現在の IACET Accredited Provider ステータスや IACET CEU 発行の主張ではありません。 ## 目的 AdCP クレデンシャルはプロトコルの能力に結びついています。プロトコルが変わると、認定プログラムは、既存のクレデンシャル保持者がアクションを必要としないか、ターゲットを絞ったデルタ評価か、完全な再認定を必要とするかを決定します。 このポリシーはその決定プロセスを文書化し、3.1 以前の S2 クリエイティブスペシャリスト保持者のための AdCP 3.1 正準フォーマットの根拠を記録します。 ## 決定レベル | Level | 使われるとき | 学習者要件 | | ------- | -------------------------------------------------------------- | ---------------------------- | | アクションなし | プロトコル変更がクレデンシャルの必須デモンストレーションに影響しない | なし | | デルタ評価 | プロトコル変更が、以前に実証された作業の上に構築される境界のある能力を追加または絞る | 新しいまたは変更された必須デモンストレーションのみを完了 | | 完全な再認定 | プロトコル変更が以前の能力の証拠を無効化する、学習者がデルタウィンドウを逃す、または以前の記録がデルタ決定をサポートできない | 現在のモジュールを再度完了 | デルタ評価は、学習者への招待やエンジンのターゲティングがライブになる前に、書面の根拠が新しい基準を以前に実証された能力にマップするときのみ利用可能です。 ## 一般プロセス 1. プログラムリーダーシップが影響を受けるクレデンシャル、モジュール、基準 ID を識別する。 2. カリキュラム所有者が、プロトコル変更を以前の証拠と新しい必須デモンストレーションにマップする根拠を書く。 3. エンジニアリングが更新されたモジュール基準を出荷し、基準の有効ポイントを記録する。 4. 認定エンジンが、以前の完了が基準の有効ポイントより前で、記録に新しい基準がまだ含まれていないクレデンシャル保持者をターゲットする。 5. 学習者が、変更、必須デルタ作業、期限、エスカレーションパスを説明する通知を受け取る。 6. デルタ完了が学習者記録に記録され、[学習者記録ポリシー](/docs/learning/policies/learner-records) の下で保持される。 ## 有効日とターゲティング 任意のプロトコル変更更新について、カリキュラム変更記録は、エンジニアリングが学習者をターゲットする前にこれらの日付を定義しなければなりません: | Field | 定義 | | --------------------------- | --------------------------------------------------- | | `criteria_effective_at` | 新しい必須基準を追加する本番デプロイと、それらの基準を学習者向けにするプロトコル GA 日付の、遅い方 | | `delta_window_opens_at` | 学習者ターゲティングと通知が開始してよい日付 | | `delta_window_closes_at` | デルタのみの完了の最終日 | | `auditable_record_required` | 完全な再認定の代わりにデルタを提供するために必要な最小限の学習者記録 | AdCP 3.1 S2 クリエイティブ正準フォーマット更新について: | Field | Value | | --------------------------- | --------------------------------------------------------------------------------------------- | | `criteria_effective_at` | `496_curriculum_3_1_canonical_formats_criteria.sql` の本番デプロイと AdCP 3.1.0 GA の、遅い方 | | `delta_window_opens_at` | `criteria_effective_at`; AdCP 3.1.0 GA と新しい基準の本番デプロイの両方の前に、学習者ターゲティングや通知は実行してはならない | | `delta_window_closes_at` | `delta_window_opens_at` の 90 暦日後、計算された期限日の 23:59 UTC に終了 | | `auditable_record_required` | `criteria_effective_at` より前に完了した S2 学習者進捗、加えて以前の必須デモンストレーションを検証するのに十分な S2 完了記録または教育チェックポイント証跡 | 学習者記録に既に 5 つの AdCP 3.1 正準フォーマット S2 基準すべてが含まれる保持者はアクション不要です。記録が `criteria_effective_at` より前で、5 つの基準の 1 つ以上を欠く保持者は、監査可能記録の要件が満たされるときのみデルタ候補です。 ## AdCP 3.1 正準フォーマット: S2 クリエイティブデルタの根拠 AdCP 3.1 は `format_options[]` を通じてメディアバイ製品に正準フォーマット宣言を追加します。クリエイティブスペシャリストは今や、製品の正準宣言を読み、正しい `format_kind` を選び、製品レベルの `format_option_id` をクリエイティブエージェントの `capability_id` から区別し、正しいアセットソースモデルを選択し、共有正準形状から製品固有の絞り込みまでの検証順序を説明しなければなりません。 変更は既存の S2 クリエイティブ保持者に再認定を要求するほど重大ですが、S2 クレデンシャルの残りを無効化しません。3.1 以前の S2 保持者は、フォーマットディスカバリー、クリエイティブマニフェスト、プレビュー、同期、価格設定、トラッカースロット推論、放送識別子全体でクリエイティブワークフローの能力を既に実証しています。正準フォーマットは、クリエイティブワークフロー全体を置き換えるのではなく、それらの能力を新しい製品宣言レイヤーで拡張します。 したがって、3.1 以前の S2 クリエイティブ保持者は、以前の S2 学習者記録が利用可能で完全なとき、自動の完全な再認定ではなく、ターゲットを絞ったデルタ評価の対象です。 ### 基準マッピング | New S2 criterion ID | 新しい能力 | それが構築する以前の S2 証拠 | デルタの根拠 | | -------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------- | | `s2_ex1_sc_format_kind_selection` | 製品とマニフェストのための正しい正準 `format_kind` を選択 | フォーマットディスカバリー、クリエイティブマニフェスト作成、チャネル / フォーマットの区別 | 既存のフォーマット選択能力に正準語彙レイヤーを追加 | | `s2_ex1_sc_format_options_cardinality` | `format_options[]` を読み、単一オプション、マルチオプション、マルチサイズのファンアウト宣言を説明 | マルチフォーマットクリエイティブ適応とパブリッシャー要件全体の同期 | 製品宣言がどう表現されるかを絞る; 以前の証拠はアセットをパブリッシャーフォーマット制約に適応することをカバー | | `s2_ex1_sc_option_vs_capability_id` | メディアバイ `format_option_id` をクリエイティブエージェント `capability_id` から区別 | クリエイティブエージェントケイパビリティディスカバリーとメディアバイ製品の読み取り | 3.1 が導入した名前空間の混同を防ぐ; 以前の証拠は両方の表面を別々にカバー | | `s2_ex1_sc_source_taxonomy` | バイヤーアップロード、エージェント合成、セラー事前レンダー、セラー人間設計、パブリッシャーホスト録画のワークフロー全体で正しいアセットソースモデルを選択 | クリエイティブ生成、変換、プレビュー、同期のワークフロー | S2 で既に評価されたワークフローの区別に明示的な分類を追加 | | `s2_ex1_sc_validation_order` | 正準優先、製品第 2 の検証とクリエイティブエージェントケイパビリティ絞り込みを説明 | スキーマ準拠、プレビュー検証、同期エラー回復 | 3.1 製品絞り込みのための検証シーケンスを追加; 以前の証拠は検証と回復を一般的にカバー | ### 適格性 S2 正準フォーマットデルタは、`criteria_effective_at` より前に S2 クリエイティブスペシャリストクレデンシャルを獲得し、監査可能な S2 完了記録を持つ学習者に適用されます。 学習者は、以下のときデルタの代わりに完全な S2 再認定に割り当てられます: * 以前の S2 記録が欠けているか不完全; * 公開されたウィンドウ内にデルタを完了しない; * 以前の完了が現在の証拠保持モデルより前で、上記の基準にマップできない; * プログラムリーダーシップが、将来のプロトコル変更が正準フォーマットレイヤー以上を無効化すると決定する。 ### ウィンドウ デルタのみのウィンドウは `delta_window_opens_at` の 90 日後です。そのウィンドウ中、影響を受けるクレデンシャル保持者は S2 クレデンシャルを保持しますが、プロトコル更新が必要とマークされます。ウィンドウが閉じた後、未解決の保持者は履歴上の S2 完了記録を保持しますが、現在の S2 モジュールを完了するまで、学習者向けの S2 ステータスはもはや最新ではありません。学習者向けメッセージは計算された絶対期限日を含まなければなりません。 ## CPD と認定の開示 継続的職能開発記録のための推奨開示: > AdCP 3.1 はメディアバイ製品に正準フォーマット宣言を導入しました。AgenticAdvertising.org は S2 クリエイティブスペシャリストクレデンシャルをレビューし、既存の S2 保持者が新しい正準フォーマット基準をカバーするターゲットを絞ったプロトコル変更更新を必要とすると決定しました。新しい基準が完全な S2 能力セットを置き換えるのではなく以前に実証されたクリエイティブワークフロー能力を拡張するため、更新はデルタ評価です。公開されたウィンドウ中にデルタ評価を完了しない学習者は、現在の S2 モジュールを完了しなければなりません。 この根拠は、監査目的でカリキュラム変更記録および学習者記録とともに保持されなければなりません。 ## 学習者通知コピー プロダクト内通知: > AdCP 3.1 はクリエイティブワークフローに正準フォーマット宣言を追加します。あなたの S2 クリエイティブスペシャリストクレデンシャルはアクティブのままですが、新しい正準フォーマット基準をカバーする短いプロトコル更新が必要です。完全な S2 モジュールを再受験せずにクレデンシャルを最新に保つには、`` までに S2 正準フォーマットデルタを完了してください。 > > 誤ってターゲットされたと思われる場合、[certification@agenticadvertising.org](mailto:certification@agenticadvertising.org) にメールしてください。評価とターゲティングのレビューリクエストは 2 営業日以内に確認し、[苦情プロセス](/docs/learning/policies/complaints) を通じて解決します。 メール通知: > 件名: AdCP 3.1 のための S2 クリエイティブプロトコル更新が必要 > > AdCP 3.1 はメディアバイ製品に正準フォーマット宣言を追加します。この変更の前に S2 クリエイティブを獲得したため、新しい正準フォーマット基準をカバーするターゲットを絞ったデルタ評価を完了する必要があります。これは完全な再認定ではありません: `format_options[]` の読み取り、`format_kind` の選択、`format_option_id` とクリエイティブエージェント `capability_id` の区別、正しいアセットソースモデルの選択、正準優先の検証の説明のみに焦点を当てます。 > > クレデンシャルを最新に保つには `` までにデルタを完了してください。そのウィンドウの後、更新には現在の S2 モジュールが必要です。誤ってターゲットされたと思われる場合、[certification@agenticadvertising.org](mailto:certification@agenticadvertising.org) にメールしてください; 評価とターゲティングのレビューリクエストは 2 営業日以内に確認し、苦情プロセスを通じて解決します。 ## 実装ゲート エンジニアリングは、以下のすべてが真になるまで学習者ターゲティングを有効化したり学習者通知を送ったりしてはなりません: * この根拠が公開されている; * 新しい S2 基準が `certification_modules.exercise_definitions` に存在する; * 適格性クエリが有効日前の S2 保持者を識別できる; * デルタ完了が新しい基準が検証されたことを示す監査可能な学習者記録を書き込む; * 誤ってターゲットされたと思う学習者のためにサポートと苦情のエスカレーションパスが準備されている。 # 返金とキャンセル Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/policies/refund AdCP 認定プログラムの返金とキャンセルポリシー: 会員費、モジュールアクセス、クレデンシャル発行の条件。 # 返金とキャンセル **採択:** 2026年3月 **責任者:** AgenticAdvertising.org プログラムリーダーシップ ## 目的 このポリシーは AdCP 認定プログラムの返金とキャンセルの条件を説明します。 ## 認定プログラム料金 Basics 段階(モジュール A1〜A3)は無料で、すべての人に開放されています。支払いは不要です。 Practitioner と Specialist 段階には AgenticAdvertising.org のメンバーシップが必要です。メンバーシップの料金と返金の条件はメンバーシップ契約によって管理され、このポリシーではありません。 ## 認定固有の返金 メンバーシップ要件を超えて、認定モジュール、試験、またはクレデンシャル発行に対して別途料金はかからない。認定固有の料金がないため、認定固有の返金もない。 ## クレデンシャルの取り消し クレデンシャルは検証された不正行為(例: アイデンティティの虚偽表示)を除いて取り消されない。クレデンシャルの有効期限と更新は[教育設計フレームワーク](/docs/learning/instructional-design#credential-issuance)に記載された条件に従う。 ## お問い合わせ プログラム料金またはメンバーシップの条件に関するご質問は [certification@agenticadvertising.org](mailto:certification@agenticadvertising.org) にお問い合わせください。 # S7: ブランドアイデンティティと検証 Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/specialist/brand AdCP スペシャリストモジュール S7: Brand Protocol の習熟。brand.json アイデンティティ解決、分散パブリッシングと相互アサーション相互性、双方向 adagents.json 確認、verify_brand_claim 署名レスポンス検証、商標曖昧性解消、実験的な権利ライフサイクル。 # S7: ブランドアイデンティティと検証 **メンバー限定** — Practitioner 資格が必要。Addie と約 60 分。ハンズオンラボとアダプティブ試験を組み合わせます。 このスペシャリストモジュールは、Brand Protocol のアイデンティティと検証レイヤーの習熟をテストします。サンドボックスエージェントと協働してブランドの `brand.json` を解決し、`brand.json` と `adagents.json` にまたがる双方向トラストチェーンをウォークし、`verify_brand_claim` を呼んでその署名レスポンスをトラストのために解釈し、複数の法域にわたって商標を曖昧性解消します。Addie はあなたのハンズオン作業とプロトコルのトラストモデルについて推論する能力の両方を評価します。 合格すると **AdCP specialist — Brand** 資格を獲得します。 **これはブランド資格で、暗号の資格ではありません。** それはブランドドメインの *判断* を証明します — アイデンティティの解決、組織階層の読み取り、誰がブランドのために行動できるか知ること、トラストモデルの適用、検証結果が何を *意味する* か決めること。**ハイパー技術的ではありません**: 署名レスポンスが現れる場所では、ゲートは「有効 / 期限切れ / 偽造された署名が何を教えるか、そしてそれについて何をするか」であり — 暗号を実装することではありません。署名メカニクス(キー解決、正規化、署名チェック)は **[S6: セキュリティ](/docs/learning/specialist/security)** のセキュリティスペシャリストスキルで、関連する場所で相互参照されます。 **このドメインは 2 速で、このモジュールもそうです。** アイデンティティと検証の表面 — `brand.json` 解決、分散セルフパブリッシング、`verify_brand_claim` 署名レスポンス、`adagents.json` 確認 — は成熟しており、この資格が **ゲートする** ものです。**権利ライフサイクル**(`get_rights` / `acquire_rights` / `update_rights`)は `brand.rights_lifecycle` フィーチャークラスターに属し、[実験的ステータス](/docs/reference/experimental-status) で **実験的** とマークされています。あなたはそれをウォークすることを学びますが、その習熟は **教えられ、評価されません** — それに依存するゲートされた基準はありません。 ## このトラックが検証を準備する専門分野 以下の `specialisms` は `brand` ドメインに該当します。それぞれ独自のコンプライアンスストーリーボードを持ちます — 完全な分類については [コンプライアンスカタログ](/docs/building/verification/compliance-catalog) を参照。 | Specialism | Status | カバーするもの | | -------------- | ------ | ---------------------------------------------------------------- | | `brand-rights` | stable | ブランドアイデンティティ解決(`get_brand_identity`)と権利ライセンシング(タレント、音楽、ストックメディア) | `brand-rights` 専門分野ストーリーボードは stable ですが、**実験的** とマークされた `brand.rights_lifecycle` フィーチャークラスター(`get_rights`、`acquire_rights`、`update_rights`)を実行します — 部分的権利、サブライセンス、失効、紛争解決は進化することが予想されます。このモジュールはドメインのアイデンティティと検証の半分をゲートします; 権利ライフサイクルは実験的として教えます。 ## 組織階層: 属性だけではない ブランドアイデンティティは **組織が誰であり誰がそれのために行動できるか** を、2 つの異なる軸に沿ってエンコードします — そしてこのモジュールは属性(色、トーン、ロゴ)だけでなく両方をゲートします: * **軸 1 — ブランド対ブランド(ハウス階層)。** どのブランドがハウスに属するか: `house_of_brands` 対 `branded_house` アーキテクチャ、`keller_type`(`master` / `endorsed` / `independent`)、`brand_refs[]` ↔ `house_domain` 相互性。 * **軸 2 — ブランド対オペレーター(誰がブランドの *ために* 行動するか)。** ハウスはその `brand.json` で `authorized_operators[]` を宣言します — そのブランドを代表することを許可されたエージェンシー、プラットフォーム、インハウスチームで、それぞれ `domain`、`brands[]`(または `*` ワイルドカード)、`countries`、`scopes[]`(`media_buying`、`creative_generation`、`rights_clearance`、`governance`、`measurement`、`agent_operations`)でスコープされます。 3 つの関係タイプを区別しておきます — 混同しやすいです: | Relationship | 宣言される場所 | 方向 | 答える質問 | | ------------ | --------------------------------------- | --------------------------- | ---------------------------- | | ハウスメンバーシップ | `brand_refs[]` ↔ `house_domain` | ブランド ↔ ブランド | どのブランドがこのハウスに属するか? | | オペレーター認可 | `brand.json` の `authorized_operators[]` | ブランド → オペレーター(**バイサイド**) | 誰がこのブランドの *ために* 購入 / 行動できるか? | | エージェント委任 | `adagents.json` の `authorized_agents[]` | パブリッシャー → エージェント(**セルサイド**) | 誰がこのパブリッシャーの在庫を *売れる* か? | ランタイムでオペレーター軸は **Accounts Protocol** で表面化します: すべてのアクションは自然キーが `{brand, operator}` である `account` を運びます(`operator` はアカウントを運用するエンティティのドメインで; ブランドが直接運用するときはブランド自身のドメインに等しい)。セラーの `require_operator_auth` ケイパビリティが参照形状を選択します — `true` ⇒ 独立したオペレーター認証を持つセラー割り当ての `account_id` 名前空間; `false` ⇒ `sync_accounts` 経由のバイヤー宣言 `{brand, operator}` ペア。`get_brand_identity` の **authorized** ティアは、まさにアイデンティティのオペレーター(リンクされたアカウント)ビューです。 ## あなたが知れること — そして知れないこと この資格の知的中核は **トラストフレームワーク** です: ブランドアイデンティティの認識論。ここでの暗号は *誰かが何かを言った* ことを証明し、*それが真実かどうか* ではありません。トラストはクリーンに分離する 2 つのレイヤーで解決します: * **アイデンティティ属性**(ロゴ、色、トーン、タグライン) — 単一の TLS でサーブされる `brand.json` から信頼されます。自身のドメインを制御するブランドは、自身の属性について権威的です、以上 — 親関係が相互化されていなくても。(「主張されたが未検証 ⇒ リーフを完全に無視」は **間違い** です: リーフのアイデンティティは依然として本物です。) * **関係**(誰がブランドを所有するか、誰がそれのために話せるか) — **両方の** 側が相互化するときのみ信頼されます。 次に *各シグナルが実際に何を証明するか* のはしごを登ります: | Signal | 証明するもの | 証明 **しない** もの | | --------------------------- | ------------------------------------------ | ---------------------------------------------------- | | TLS + ドメイン制御 | この当事者がこのドメインを制御する | それがあなたが期待する現実世界のエンティティであること | | `signed_response`(JWS) | この当事者が `iat`/`exp` 内で公開キーの下でこの回答を **作成した** | 回答が **真実** であること(そしてそれは否認防止レシートではない) | | 相互アサーション(両側 `owned`) | 2 つの当事者が **合意する**(一貫性) | 現実世界の **地位** — 2 つの攻撃者制御ドメインが両方とも一致する `owned` に署名できる | | 拒否(`not_ours` / `disputed`) | 単一の署名レスポンスについて権威的 | —(ブランドは常に関連を拒否できる) | **プロトコルがあなたのために確立できないもの** — そしてあなたが代わりにどこへ行かなければならないか: * **現実世界の法的地位** → あなたが期待する法的エンティティに対する消費者側ドメイン制御 + TLS; 高信頼決定のための帯域外アイデンティティ。(これは *検証済みアイデンティティ証明* が存在する場所です — **別の、実験的** フィーチャーで、この資格の範囲外。) * **商標の実際の登録** → 公開 **レジストリ** レコードをクロスチェック(エージェントは `matched_registration` を主張するだけ)。 * **プロパティクレーム** → **DNS/TLS** をクロスチェック。 * **`licensed_in`** → 名指されたライセンサーが **`licensed_out`** を相互化するまで未検証。 * **保留された内部状態**(キュー位置、チケット状態、チームルーティング) → 決して露出されない; それを推論しない。 * **`exp` を過ぎた鮮度** → 期限切れのエンベロープは監査証拠で、新鮮な認可シグナルではない。 避けるべき 2 つの罠: `managed_by` は **ディレクトリ** フィールドで、決してトラスト / 認可シグナルではない; そしてスタンドアロンリーフの沈黙は、それを主張する任意のサードパーティハウスに **勝る**。 ## ブランドアイデンティティがクリエイティブ生成をどう駆動するか ブランドアイデンティティは装飾ではありません — それはオンブランド生成への **入力** で、それが authorized ティアが存在する *理由* です。クリエイティブエージェントは `brand.json` をフェッチ(または `get_brand_identity` authorized を呼び)、次にワードマークを引き出し、正確なパレットとタイプスケールを適用し、トーンオブボイスを採用し、制限に従います — `visual_guidelines` が食品画像の上のテキストを禁止するので、見出しを画像の *下* に置いたフードフォワードなコンポジションを生成します。推測なし、修正なし。2 つのロールを分離します: * ジェネレーターが消費する **入力**: `logos`、`colors`、`fonts`、`tone.voice`、`voice_synthesis`(provider / voice\_id / settings)。 * それが生成してよいものを束縛する **制約**: `tone.dos` / `tone.donts`、`visual_guidelines.restrictions`(例: 「アスリートの上に決してテキストを置かない」)、`content_restrictions`。 入力品質が出力品質を駆動します — よりリッチな `brand.json` はより少ない修正でより良い生成を生みます。これらはまさに `get_brand_identity` が認可の背後にゲートするフィールドです、ブランドが生成入力を保護するから。 **2 番目の、実験的** パスは、ライセンスされたタレントの肖像や声で生成するとき(`brand.rights_lifecycle`)に適用されます: `acquire_rights` が、特定のプロバイダー(肖像には Midjourney、声には ElevenLabs)が *生成時に* 検証する **スコープされた `generation_credentials`** を発行します — 権利エージェントが許可を設定し、プロバイダーがそれを強制します — `rights_constraint`(用途 / 国 / インプレッション上限)、必須の開示テキスト、生成されたアセットをレビューのためにブランドエージェントに戻す **`creative_approval`** ループとともに。インプレッション上限に達すると生成が停止します。 **範囲境界。** このモジュールはハンドオフのブランド *側* をカバーします — 入力 / 制約としてのアイデンティティ、そして権利 / 承認インターフェース。クリエイティブ自体の生成(`build_creative` マニフェスト / コードモード、フォーマット選択、プレビュー、同期)は **[S2: クリエイティブ](/docs/learning/specialist/creative)** と **[S5: Sponsored Intelligence / 生成広告](/docs/learning/specialist/sponsored-intelligence)** に属します — 相互参照され、ここで再教育されません。権利ゲートされた半分は教えられ、ゲートされません(それは実験的な権利ライフサイクルに乗ります)。 ## このモジュールでの stable 対 experimental | Surface | Maturity | この資格で | | ------------------------------------------------------------------------------------------------------- | ------------ | ------------------------------- | | `brand.json` アイデンティティ解決 + public/authorized フィールドティア | stable | **ゲート** | | 分散パブリッシング + 相互アサーション相互性(`brand_refs[]` ↔ `house_domain`) | stable | **ゲート** | | オペレーター認可(`authorized_operators[]`: domain / brands / countries / scopes)+ アカウント `{brand, operator}` モデル | stable | **ゲート** | | 双方向 `adagents.json` 確認(委任された権限、署名キー `kid`) | stable | **ゲート** | | `verify_brand_claim` / `verify_brand_claims` — トラストのための署名レスポンスの解釈 | stable (3.1) | **ゲート**(署名 *メカニクス* は S6 セキュリティ) | | 方向非対称トラスト + 商標曖昧性解消 | stable | **ゲート** | | トラストフレームワーク / 知り得性(作成≠真実、一貫性≠地位、2 つのレイヤー、必須のクロスチェック) | stable | **ゲート** | | クリエイティブ生成入力 / 制約としてのブランドアイデンティティ(authorized ティアフィールド) | stable | **ゲート** | | 権利ゲートされた生成(`generation_credentials`、開示、インプレッション上限、`creative_approval` ループ) | experimental | 教えられる、ゲートされない | | クリエイティブプロダクション(`build_creative`、フォーマット、マニフェスト) | — | 範囲外 → S2 / S5 | | 権利ライフサイクル(`get_rights` / `acquire_rights` / `update_rights`、`brand.rights_lifecycle`) | experimental | 教えられる、ゲートされない | ## あなたがデモンストレーションすること * ブランドのアイデンティティを、静的な `brand.json` と `get_brand_identity` エージェントタスクの両方から解決; `available_fields` を読んで未認可の呼び出し元が何を欠いているか検出し、次に `authorized=true` を再リクエストしてゲートされた `colors` / `fonts` / `tone` / `voice` / `rights` セクションを取得 * ブランド階層関係が本物であることを **両方の** 方向をチェックして確立 — 親ハウスの `brand_refs[]` とサブブランドの `house_domain` バックポインター — し、なぜ片側のアサーションがトラストを拡張しないか説明 * ハウスの `authorized_operators[]` を解決し、与えられたオペレーター(エージェンシー、プラットフォーム、インハウスチーム)が特定のブランドのために行動できるか判定 — `domain`、`brands[]`(`*` ワイルドカード含む)、`countries`、`scopes[]` をチェック — し、このバイサイドオペレーター軸をハウスメンバーシップとセルサイドエージェント委任から区別; `require_operator_auth` が `{brand, operator}` 対 `account_id` 参照形状をどう選択するか推論 * 委任された販売エージェントを双方向に確認: パブリッシャーの `adagents.json` がそれを認可 *かつ* エージェントが自身の `brand.json` で自己宣言(どちらも単独では不十分); なぜエージェントの署名キーをピンするパブリッシャーがエージェントの署名レスポンスのトラスト権威になるか(侵害されたエージェントが自身のキーにスワップできないように)理解し、パブリッシャーがリストしないエージェントを拒否 *(署名チェックのメカニクスは S6 セキュリティ)* * 署名された `verify_brand_claim` レスポンスをトラストのために **解釈** — 有効な署名はブランドが回答を *作成した* ことを証明(真実であること、否認防止ではない)、期限切れのものは監査のみ、検証に失敗するか署名された内容が一致しないレスポンスは拒否されなければならず、未署名 / 未検証の回答や `verification_status` フィールド単独は決してトラストを拡張しない *(署名がどう検証されるかは S6 セキュリティスキル — ここでは各結果が何を意味するか推論)* * **方向非対称トラストルール** を適用: 単一の `owned` / `pending_review` / `licensed_in` アサーションを、他の側が相互化するまで有益だがトラストを拡張しないものとして扱い、`not_ours` / `disputed` を単一の署名レスポンスについて権威的として扱う * レジストリと Nice クラスにわたって商標クレームを曖昧性解消: あるレジストリで `owned` だが別で `disputed` または `licensed_in` であるマークを解決し、`AMBIGUOUS_MATCH` を絞り、間違った登録に対してクリエイティブをクリアすることを回避 * ブランドの解決されたアイデンティティを **クリエイティブ生成** にマップ: どのフィールド(`logos`、`colors`、`fonts`、`tone.voice`、`voice_synthesis`)がジェネレーターが消費する *入力* か、どれ(`tone.donts`、`visual_guidelines.restrictions`、`content_restrictions`)がハードな *制約* か; なぜこれらが認可ゲートされたフィールドか説明し、ブランド / プロダクション境界(`build_creative` と実験的な権利ゲートされた `generation_credentials` + `creative_approval` ループが引き継ぐ場所)を特定 * **トラストフレームワークの知り得性境界** について推論: アイデンティティレイヤー(TLS 検証可能)を関係レイヤー(相互アサーションゲート)から分離; なぜ署名が *真実ではなく作成* を証明し相互アサーションが *地位ではなく一貫性* を証明するか説明; そしてブランドについての任意の事実について、プロトコルが確立できるもの、できないもの、ギャップを閉じる外部クロスチェック(レジストリ、DNS/TLS、ライセンサー相互化、消費者側地位)を名指す * 実験的な権利ライフサイクル(`get_rights` → `acquire_rights` → `update_rights`)をウォークし、権利支出ガバナンスについて推論 — 表面をまだ安定していないと正しくフラグしながら ## 前提読書 ブランドドメイン: アイデンティティ、分散パブリッシング、階層、検証、権利。 セルフパブリッシュされたアイデンティティドキュメント — ハウスアーキテクチャ、`brand_refs[]`、エージェント、相互アサーショントラストモデル。 4 つのクレームタイプ、署名レスポンスエンベロープ、方向非対称トラストルール。 2 つのトラストレイヤー、相互化テーブル、なぜ一貫性が地位でないか — 知り得性フレームワーク。 双方向 `adagents.json` + `brand.json` トラストチェーン — Northwind / StreamHaus / Sportshaus Holdings。 ランタイムのオペレーター軸: `authorized_operators`、`{brand, operator}` アカウントモデル、`require_operator_auth`、課金。 クリエイティブエージェントがどう `brand.json` アセット、トーン、制限を引き出してオンブランドを生成するか — そして権利ゲートされた `generation_credentials` パス。 `get_brand_identity`、`verify_brand_claim`、レスポンス署名キーの実装。 なぜ `brand.rights_lifecycle` が実験的か、それが採用者にとって何を意味するか。 ## テストエージェントへの接続 ラボ演習は公開テストエージェントのブランドテナントに対して実行します。共有トークンを使います — サインアップ不要: ```bash theme={null} export ADCP_AUTH_TOKEN="1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ" export AGENT_URL="https://test-agent.adcontextprotocol.org/brand/mcp" ``` ディスカバリーとウォークスルーのドキュメントはホストルートでサーブされます: * `https://test-agent.adcontextprotocol.org/.well-known/brand.json` — エージェント自身のアイデンティティドキュメント * `https://test-agent.adcontextprotocol.org/.well-known/adagents.json` — 販売認可 * `https://test-agent.adcontextprotocol.org/.well-known/jwks.json` — 署名キー(`response-signing` キーを含む) * `…/fixtures/walkthrough/northwind/.well-known/brand.json` — 委任されたエージェンシーのアイデンティティ * `…/fixtures/walkthrough/streamhaus/.well-known/brand.json` と `…/fixtures/walkthrough/streamhaus/.well-known/adagents.json` — サブブランドパブリッシャーのアイデンティティと販売認可(`adagents.json` フィクスチャをサーブする唯一のロール) * `…/fixtures/walkthrough/sportshaus-holdings/.well-known/brand.json` — 親ハウスのアイデンティティ、`brand_refs[]` **と** `authorized_operators[]` 付き(ポートフォリオ全体のエージェンシーとブランドスコープの US のみのインハウスオペレーター) これらは [セラー検証ウォークスルー](/docs/verification/overview) のマルチティアトラストチェーンフィクスチャです。 最初の呼び出しのウォークスルーについては [クイックスタート](/docs/quickstart) を参照。 ## ラボ演習 1. **ブランドアイデンティティの解決** — 認可なしでサンドボックスブランドの `get_brand_identity` を呼び、`available_fields` を読んでどのセクションがゲートされているか見る。`authorized=true` で再度呼び、`colors`、`fonts`、`tone`、`rights` セクションが現れることを確認。なぜブランドがこれらのフィールドをリンクされたアカウントの背後にゲートするか説明。 2. **アイデンティティ → クリエイティブ生成** — タレントブランド(例: `daan_janssen`)で、`authorized=true` で `get_brand_identity` を呼び、`colors`、`fonts`、`tone`(`dos` / `donts`)、`voice_synthesis`、`visual_guidelines.restrictions` を読む。各フィールドをクリエイティブエージェントが消費する生成 **入力** か出力を束縛するハードな **制約** かに分類し、ジェネレーターが従うオンブランドブリーフを書く。なぜこれらがまさに認可ゲートされたフィールドか説明し、`build_creative`(S2/S5)と実験的な権利ゲートされた `generation_credentials` + `creative_approval` ループが引き継ぐ場所を指す。 3. **分散パブリッシング相互性** — Sportshaus Holdings の `brand.json` をフェッチしその `brand_refs[]` を読む。StreamHaus の `brand.json` をフェッチしその `house_domain` を読む。関係が **両方の** 方向でアサートされることを確認。1 つの方向だけがチェックされた場合に悪意あるハウスが何を主張できるか説明。 4. **オペレーター認可** — Sportshaus Holdings の `brand.json` で `authorized_operators[]` を読む。`meridian-agency.example` が `streamhaus` のために購入できること(`brands: ["*"]` を持つ)、`courtside-inhouse.example` が **できない** こと(`brands: ["courtsidehq"]`、`countries: ["US"]` にスコープされている)を確認。このバイサイドオペレーター軸がハウスメンバーシップ(`brand_refs[]`)とセルサイドエージェント委任(`adagents.json`)とどう異なるか、`require_operator_auth` が `{brand, operator}` 自然キーとセラー割り当ての `account_id` のどちらを渡すか決めるかを説明。 5. **双方向 adagents.json 確認** — StreamHaus の `adagents.json` をフェッチし Northwind Media を認可するエントリーを見つける; `delegation_type: "delegated"` を確認。Northwind の `brand.json` をフェッチし同じ販売エージェントを宣言することを確認 — パブリッシャーがそれを認可 *かつ* エージェントが自己宣言、どちらも単独では不十分。なぜエージェントの署名キーをピンするパブリッシャーがそのエージェントの署名レスポンスのトラスト権威になるか(侵害されたエージェントが自身のキーにスワップできないように)、なぜパブリッシャーがリストしないエージェントが失敗するか説明。*(署名チェックのメカニクスは S6 セキュリティ。)* 6. **verify\_brand\_claim — 署名された回答を解釈** — `streamhaus.example` について `claim_type: "subsidiary"` で `verify_brand_claim` を呼び、回答を **解釈**: それはブランドによって署名されているので、有効な署名はブランドが *それを作成した* ことを証明 — 真実であることではない; 期限切れの署名は監査のみ; 検証に失敗するか署名された内容が一致しないレスポンスは拒否されなければならない; `verification_status` フィールド単独を決して信頼しない。*(暗号検証の実行は S6 セキュリティスキル — ここでは各結果がトラストにとって何を意味するか推論。)* 次に `claim_type: "parent"` でリーフ側のミラーを検証し、相互アサーションがエージェントレイヤーで完了することを確認。 7. **方向非対称トラスト** — 無関係なプロパティについて `verify_brand_claim` を呼び `not_ours` を観測。なぜこの拒否が単一の署名レスポンスについて権威的で、`owned` または `licensed_in` の回答がそうでないか — そして `licensed_in` がどの相互化ステップを要求するか説明。 8. **商標曖昧性解消** — 同じマークについて 2 つの異なるレジストリの下で `claim_type: "trademark"` で `verify_brand_claim` を呼び、異なるステータス(`owned` 対 `licensed_in`)を観測。レジストリなしで一度呼び `AMBIGUOUS_MATCH` を観測。`registry`、`countries`、`nice_classes` がどう曖昧性を解決するか、なぜこれがクリエイティブクリアランスにとって重要か説明。 9. **トラスト境界 — 知れないこと** — エージェントが `courtsidehq` サブシディアリークレームに `owned` で署名し、Sportshaus Holdings が `brand_refs[]` に `courtsidehq` をリスト — しかし `courtsidehq` ドキュメントが相互化しない。なぜ署名された `owned` が *真実ではなく作成* を証明するか、なぜこのハウスのみのエッジが `courtsidehq` のアイデンティティ(それが 1 つを公開したなら)が依然として本物であっても関係レイヤーで **未検証** か説明。次に `sportshaus-holdings.example` があなたが思う *本物の* 組織であると信頼するためにあなたがまだ必要とするもの — そしてなぜ署名も相互アサーションもそれを与えられないか述べる。 10. **権利ライフサイクル(実験的)** — `get_rights` を使ってタレントオファリングを発見し、`acquire_rights` → `update_rights` ライフサイクル、スコープされた `generation_credentials` と `creative_approval` ループ、権利支出ガバナンスについて推論。この表面を実験的(`brand.rights_lifecycle`)として識別し、それが本番採用にとって何を意味するか説明。*資格ゲートとして評価されない。* ## 評価 | Dimension | Weight | Addie が評価するもの | | ----------------- | ------ | ------------------------------------------------------------------------------------------------------------------- | | アイデンティティ解決 & 生成入力 | 20% | public/authorized フィールド分割を解決 *かつ* フィールドをクリエイティブ生成入力と制約としての役割にマップできるか? | | トラストチェーン検証 | 25% | 3 つの軸すべて — `brand_refs[]`/`house_domain` 相互性、`authorized_operators[]`、`adagents.json` 委任 — をウォークできるか? | | クレーム検証 | 25% | 署名レスポンスをトラストのために *解釈*(作成≠真実、期限切れ=監査のみ、不一致で拒否、ステータスフィールド単独を決して信頼しない)し、商標を曖昧性解消できるか? 署名メカニクスは S6 セキュリティで、ここではゲートされない。 | | トラストモデル推論 | 30% | 2 つのトラストレイヤーを分離し、作成≠真実と一貫性≠地位を説明し、プロトコルが確立できないもの + 各ギャップを閉じるクロスチェックを名指せるか? | 合格しきい値: 70%。 # S2: クリエイティブマスタリー Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/specialist/creative AdCP スペシャリストモジュール S2: クリエイティブマスタリー。20チャンネルにわたるフォーマット分類、クリエイティブマニフェスト仕様、AI 生成、コンプライアンスチェック、アセット同期ワークフロー。 # S2: クリエイティブマスタリー **メンバー限定** — Practitioner クレデンシャルが必要。Addie と約45分。ハンズオンラボと適応型試験の組み合わせ。 このスペシャリストモジュールは、クリエイティブプロトコルのマスタリーをテストします。サンドボックスエージェントを使ってチャンネルをまたがるフォーマット要件を発見し、クリエイティブアセットを制作・適応させ、パブリッシャーに同期し、コンプライアンスを確認します。Addie はハンズオンの作業と概念的な理解の両方を評価します。 合格すると **AdCP specialist — Creative** クレデンシャルを取得できます。 ## Specialisms this track prepares you to validate 次の `specialisms` は `creative` ドメインに属します。それぞれ独自のコンプライアンスストーリーボードを持ちます — 完全なタクソノミーは [Compliance Catalog](/docs/building/compliance-catalog) を参照。 | Specialism | Status | What it covers | | --------------------- | ------ | ------------------------------- | | `creative-ad-server` | stable | タグベース配信のクリエイティブアドサーバー | | `creative-generative` | stable | オンデマンドでアセットを生成する生成クリエイティブエージェント | | `creative-template` | stable | クリエイティブテンプレートと変換エージェント | ## 実証すること * フォーマット要件を発見し、コンプライアンスに準拠したクリエイティブアセットを制作します * 単一のクリエイティブコンセプトを複数のチャンネルとフォーマットに適応させる * クリエイティブをパブリッシャープラットフォームに同期し、デリバリーを確認します * フォーマットのエッジケース、アクセシビリティ、プロベナンスについて論理的に考える ## 前提条件の読み物 ### コアクリエイティブタスク フォーマット探索: 各パブリッシャーが必要とする仕様は何か? ブランドアセットからのクリエイティブ生成と変換。 デプロイ前にクリエイティブをプレビューします。 クリエイティブアセットをパブリッシャープラットフォームと同期します。 ### サポートコンセプト クリエイティブプロトコル: アセット、フォーマット、マニフェスト、クリエイティブエージェント。 クリエイティブプロトコルの正式仕様。 フォーマット定義、技術仕様、renders 構造。 画像、ビデオ、オーディオ、HTML5、ネイティブアセット仕様。 マニフェスト構造: クリエイティブパッケージがコンテンツをどう説明するか。 ビデオ、ディスプレイ、オーディオ、DOOH、カルーセルのチャンネル別仕様。 AI 生成クリエイティブワークフローとベストプラクティス。 広告クリエイティブのアクセシビリティ標準。 AI クリエイティブのプロベナンスと開示要件。 クリエイティブエージェント構築のアーキテクチャガイド。 ### Creative governance 機能ベースのクリエイティブ評価: セキュリティスキャン、規制コンプライアンス、コンテンツ分類。 クリエイティブをガバナンス機能に対してスコアリングする評価タスク。 クリエイティブが含みうるものを制約するバイヤー定義のコンテンツルール。 ガバナンスエージェントが AI クリエイティブのプロベナンスメタデータを検証する方法。 ## Connecting to the test agent ラボ演習はパブリックテストエージェントに対して実行されます。共有トークンを使います — サインアップ不要。クリエイティブモジュールは 3 つのエージェントサーフェスにまたがります。アドサーバー(最も一般的なパス)から始め、演習が別のエージェントタイプを求めるときに URL を切り替えます。 ```bash theme={null} export ADCP_AUTH_TOKEN="1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ" export AGENT_URL="https://test-agent.adcontextprotocol.org/creative/mcp" ``` | Agent surface | URL | Used in | | ---------------------- | --------------------------------------------------------------- | ------- | | クリエイティブアドサーバー | `https://test-agent.adcontextprotocol.org/creative/mcp` | 演習 2、4 | | ステートレス変換/テンプレートエージェント | `https://test-agent.adcontextprotocol.org/creative-builder/mcp` | 演習 1、5 | | セールスエージェント(プッシュ&プレビュー) | `https://test-agent.adcontextprotocol.org/sales/mcp` | 演習 3、6 | 最初の呼び出しのウォークスルーについては [Quickstart](/docs/quickstart) を参照。 ## Canonical-format glossary | Term | Definition | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Canonical format** | `image`、`video_hosted`、`native_in_feed` などの AdCP 定義のクリエイティブアーキタイプ。バイヤーはまずクリエイティブマニフェストを正準に対して検証します。 | | **`format_kind`** | プロダクトフォーマットオプションにどの正準が適用されるかを名付ける識別子。配信チャネルやターゲティングではなく、クリエイティブの形状を制御します。 | | **`format_options[]`** | サイズ、スロット、制作ソース、プラットフォーム制約で正準を狭めるプロダクトレベルの宣言。プロダクトは 1 つのオプションまたは複数の代替を提供できます。 | | **`format_option_id`** | プロダクトまたはパブリッシャーカタログオプションの安定した識別子。プロダクトが公開するときに選択します。メディアバイプロダクトやクリエイティブマニフェストでクリエイティブエージェントの機能 ID を代用しないでください。 | | **`v1_format_ref[]`** | 正準オプションに添付されたレガシークリエイティブフォーマット参照。1 つのオプションが 1 つの既存フォーマットを指すか、複数のサイズ固有フォーマットにファンアウトできます。 | | **`capability_id`** | `creative.supported_formats` 上のクリエイティブエージェントのビルド機能セレクター。`build_creative` パスを選び、プロダクトレベルの `format_option_id` とは別物です。 | | **`asset_source`** | `image`、`video_hosted`、`audio_hosted` のアセットバイトを誰がレンダリングするかを宣言: `buyer_uploaded`、`publisher_host_recorded`、`seller_pre_rendered_from_brief`、`seller_human_designed`、または `agent_synthesized`。 | | **Manifest asset map** | 選択したフォーマットオプションの要件からクリエイティブマニフェストアセットへのスロットごとのマッピング。バイヤーが提供する入力と、セラー・パブリッシャー・エージェントが制作するアセットを含む。 | ## ラボ演習 ### Interaction models 1. **ステートレス変換** — テンプレートエージェントに接続し、そのフォーマットを発見し、サンプルブランドアセット(インライン)でテンプレートをプレビューし、配信タグを構築します。`sync_creatives` なし — すべて呼び出し内で渡されます。 2. **アドサーバーワークフロー** — 事前ロードされたアドサーバーに接続し、`list_creatives` でクリエイティブライブラリを参照し、`build_creative` で複数のメディアバイのタグを生成し、配信メトリクスを確認します。 3. **プッシュ&プレビュー** — セールスエージェントに接続し、受け入れられるフォーマットを発見し、`sync_creatives` でカタログアセットをプッシュし、パブリッシャーの環境でどうレンダリングされるかをプレビューします。 ### Creative pricing 4. **価格ライフサイクル** — 演習 2 と同じサンドボックスアドサーバーを使用: * アドサーバーとアカウントを確立 * `account` と `include_pricing: true` で `list_creatives` — 各クリエイティブでレートカードを反映する `pricing_options` を観察 * 2 つ目のサンドボックスアカウントに切り替え、再度 `list_creatives` — `pricing_options` が異なるレート(異なる CPM、モデル、または通貨)を反映することを観察 * `account` で `build_creative` — レスポンスの `pricing_option_id`、`vendor_cost`、`consumption` を検査 * `account` なしで `build_creative` — 拒否エラーを観察 * `creative_id` + `pricing_option_id` で `report_usage` を呼び出し、値が `build_creative` が返したものと一致することを検証 * 説明: CPM 価格のクリエイティブでビルド時に `vendor_cost` がゼロなのはなぜか? 5. **変換エージェント価格** — 価格が有効な、演習 1 のサンドボックス変換エージェントを使用: * 変換エージェントとアカウントを確立 * `account` と `include_pricing: true` で `list_creative_formats` — 各フォーマットで `pricing_options`(単位ごとの価格)を観察 * `account` で `build_creative` — `pricing_option_id`、`vendor_cost`、`consumption` を検査(CPM アドサーバーと異なり、ビルド時に非ゼロの `vendor_cost` を期待) * 比較: アドサーバーは `list_creatives` で価格を発見し、変換エージェントは `list_creative_formats` で価格を発見。アドサーバーはビルドコストがゼロ(CPM は配信時に発生)、変換エージェントはビルドコストが非ゼロ(単位ごとの価格) **ベンダー価格はプロトコル間で一貫している** すべてのベンダーサービスは同じパターンを使います: ディスカバリーレスポンスの `pricing_options[]`、`report_usage` の `pricing_option_id`。シグナル、コンテンツ標準、クリエイティブエージェント、プロパティリストエージェントすべてがこれに従います。 ベンダーはしばしばクリエイティブごとに複数の価格オプションを提供します — ボリューム/コミットメント階層(高い支出で低い CPM)、コンテキスト固有のレート(プレミアム対標準プレースメント)、または異なるプロダクトラインの異なる価格モデル(リッチメディアには CPM、ソーシャルバリアントには単位ごと)。バイヤーは適切な `pricing_option_id` を選択し、`report_usage` で渡します。 ### Canonical formats 6. **正準フォーマットオーサリング** — `static/examples/products/canonical/` の検証済み参照フィクスチャ: マルチフォーマットディスプレイフィクスチャ(`nytimes_homepage_mrec.json`)と生成ビデオフィクスチャ(`veo_generative_video_15s.json`)を使用: * 各プロダクトの `format_options[]` を読み、利用可能なすべての `format_kind` を特定 * プロダクトが公開するときに `format_option_id` を選択し、単一オプションのプロダクトが別個のセレクターを公開しない場合がある理由を説明 * クリエイティブエージェントの `capability_id` がメディアバイプロダクトの `format_option_id` の代替でない理由を説明 * 選択したオプションのスロットが示唆するマニフェストアセットマップを書く。バイヤーがレンダリング済みアセットを出荷するときと、ブリーフ、ビデオブリーフ、または構造化オブジェクトを出荷するときを含む * 単一オプション対マルチオプションのプロダクトを説明: 1 つのプロダクトが複数の正準代替を受け入れるとき、および 1 つのオプションが `v1_format_ref[]` を通じて複数のサイズにファンアウトするとき * セラーがまず正準の形状に対して、次にプロダクトの狭めに対して検証する一方、クリエイティブエージェント自身の機能は生成すると約束するものしか狭められない理由を説明 ### Cross-platform skills 7. **フォーマット探索** — サポートされているフォーマットのサンドボックスエージェントをクエリし、パブリッシャー間で要件を比較します 8. **クロスプラットフォーム適応** — `target_format_ids` を伴う `build_creative` を使って、ディスプレイ、ビデオ、ネイティブフォーマットにひとつのコンセプトを適応させる 9. **コンプライアンス** — AI 生成クリエイティブのプロベナンスメタデータと開示要件を設定します 10. **プレビューモード** — `preview_creative` を `request_type: "single"`、次に `"batch"`(5 つのクリエイティブを 1 回の呼び出しで送信し高速化を測定)、次に以前の `get_creative_delivery` 結果に対して `"variant"` として実行します。レンダリングレイテンシーについて `output_format: "url"` 対 `"html"` を比較 11. **トラッカースロット監査** — 各サンドボックスフォーマットについて、`assets` 配列を検査し、サードパーティ測定をサポートするかを判断します。DoubleVerify ピクセルを放送スポットに割り当てても機能しない理由と、代わりにどの `billing_measurement` ベンダーが該当するかを説明 12. **放送識別子** — 同じスポットの `:15` と `:30` カットに別個の `industry_identifiers[]` を持つ放送マニフェストを構築します。`creative-identifier-type` 値を検証し、各カットが独自のトラフィック識別子を必要とする理由を説明 ## 評価 | 次元 | ウェイト | Addie が評価するもの | | ----------- | ---- | ------------------------------- | | プロトコルマスタリー | 30% | 完全なクリエイティブライフサイクルのマスタリー | | クロスプラットフォーム | 25% | チャンネルとフォーマットをまたがってクリエイティブを適応させる | | コンプライアンス | 25% | 開示と規制要件を設定する | | 分析スキル | 20% | クリエイティブ特徴評価結果を解釈する | 合格基準: 70%。 ## このモジュールを始める 「クリエイティブスペシャリストモジュールを始めたい」 # S4: ガバナンス Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/specialist/governance AdCP スペシャリストモジュール S4: ガバナンスプロトコルマスタリー。コンテンツ標準、プロパティリスト、キャンペーンガバナンスライフサイクル、ポリシーレジストリ、サンドボックスエージェントとのコンプライアンス自動化。 # S4: ガバナンス **メンバー限定** — Practitioner クレデンシャルが必要。Addie と約60分。ハンズオンラボと適応型試験の組み合わせ。 このスペシャリストモジュールは、完全なガバナンスプロトコルのマスタリーをテストします。サンドボックスエージェントを使ってコンテンツ標準とプロパティリストを管理し、キャンペーンガバナンスライフサイクル(プラン、バリデーション、アウトカム、監査)を実行し、レジストリからコンプライアンスポリシーを解決し、ガバナンスドメインの構成についての論理的思考を示します。Addie はハンズオンの作業と概念的な理解の両方を評価します。 合格すると **AdCP specialist — Governance** クレデンシャルを取得できます。 ## Specialisms this track prepares you to validate 次の `specialisms` は `governance` ドメインに属します。それぞれ独自のコンプライアンスストーリーボードを持ちます — 完全なタクソノミーは [Compliance Catalog](/docs/building/compliance-catalog) を参照。 | Specialism | Status | What it covers | | ----------------------------- | ------ | ----------------------------------------- | | `content-standards` | stable | コンテンツ標準の強制(ブランドセーフティ、ポリシーコンプライアンス) | | `property-lists` | stable | ターゲティングと配信コンプライアンスのための厳選された包含・除外リスト | | `collection-lists` | stable | コンテンツプログラム(番組、シリーズ、ポッドキャスト)の厳選された包含・除外リスト | | `audience-sync` | stable | バイヤー提供のオーディエンスセグメントを有効化のためにプラットフォームに同期 | | `governance-delivery-monitor` | stable | ドリフト検出付きのキャンペーン配信監視 | | `governance-spend-authority` | stable | 条件付き支出承認と human-in-the-loop ガバナンス | ## 実証すること * コンテンツ標準を作成、更新、一覧表示、削除します * プロパティリストを作成、更新、一覧表示、削除します * コンテンツを標準に対してキャリブレートし、結果を解釈します * 予算権限とポリシー設定でキャンペーンプランを作成・検証します * 完全なガバナンスループを実行します: `sync_plans`、`check_governance`(提案済み + コミット済み)、`report_plan_outcome` * ポリシーレジストリを使ってコンプライアンスポリシーを解決・適用します * 監査ログを解釈し、ドリフトメトリクスについて論理的に考える * ガバナンスドメインの構成を説明する — キャンペーン、プロパティ、コンテンツ標準、クリエイティブ * ポリシーカテゴリと制限属性を使ったキャンペーンプランのオーディエンス制約を設定します * 制限属性を使ったターゲティングを `check_governance` が正しく拒否することを検証します ## 前提条件の読み物 ### コアガバナンスタスク インクルージョン/エクスクルージョンフィルタリングのためのプロパティリストを作成・管理します。 自動コンプライアンスのためのブランド固有のコンテンツ標準を定義します。 プレースメント前にコンテンツが定義済み標準を満たすかテストします。 広告が承認されたプロパティに配信されたことを確認します。 承認されたパラメーターを定義するキャンペーンプランを作成・更新します。 提案済みおよびコミット済みアクションをキャンペーンプランに対して検証します。 確認済みアウトカムを報告し、プランに対して予算をコミットします。 ガバナンスの決定、所見、ドリフトメトリクスを確認します。 ### サポートコンセプト ガバナンスプロトコル: プロパティ、コンテンツ標準、クリエイティブ、キャンペーンガバナンス。 マルチパーティバリデーション、予算権限、ガバナンスモード、トラストモデル。 3者トラストモデル: オーケストレーター、ガバナンスエージェント、セラー。 完全な技術仕様: バリデーションカテゴリ、施行レベル、予算トラッキング。 コミュニティ維持のコンプライアンスポリシー: 規制(COPPA、GDPR、HFSS)と標準(アルコール、ファーマ)。 パブリッシャーのアイデンティティ、認可、データエンリッチメント。 コンテンツ標準の仕組み: キャリブレーション、ローカル実行、バリデーション。 コンテンツ標準の実装ガイド。 コンテンツ評価中に生成されるアーティファクト。 クリエイティブ品質、コンプライアンス、特徴分析。 AI クリエイティブのプロベナンスと開示の検証。 コンプライアンス評価のためのクリエイティブ特徴分析。 配信後のコンテンツバリデーション。 監査とレビューのための評価アーティファクトの取得。 ## Connecting to the test agent ラボ演習はパブリックテストエージェントに対して実行されます。共有トークンを使います — サインアップ不要: ```bash theme={null} export ADCP_AUTH_TOKEN="1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ" export AGENT_URL="https://test-agent.adcontextprotocol.org/governance/mcp" ``` 最初の呼び出しのウォークスルーについては [Quickstart](/docs/quickstart) を参照。 ## ラボ演習 1. **コンテンツ標準ライフサイクル** — 架空のブランドのコンテンツ標準を作成、更新、管理します 2. **プロパティリスト管理** — サプライパスコントロールのためのインクルージョンリストとエクスクルージョンリストを作成します 3. **コンテンツキャリブレーション** — 標準に対してサンプルコンテンツをキャリブレートし、結果を解釈します 4. **コンプライアンス検証** — サンドボックスキャンペーン配信がガバナンス要件を満たすことを確認します 5. **キャンペーンガバナンスライフサイクル** — キャンペーンプランを作成し、アクション(提案済み + コミット済み)を検証し、拒否と条件を処理し、アウトカムを報告し、監査ログを確認します 6. **ポリシー解決とコンプライアンス** — レジストリからポリシーを解決し、管轄スコープの施行を設定し、違反が検出されることを確認します 7. **オーディエンスガバナンス** — `restricted_attributes` を持つ `policy_categories`(例: `fair_housing`)でプランを設定します。制限シグナルを使ったオーディエンスセレクターで `check_governance` リクエストを送信し、ガバナンスエージェントが拒否することを確認します。その後、コンプライアンスに準拠したターゲティングを送信し、承認を確認します 8. **オーディエンスドリフト検出** — ベースラインパリティから徐々にシフトする `audience_distribution` インデックスで一連の配信フェーズガバナンスチェックを実行します。累積インデックスが単一期間のノイズでは見えにくい持続的バイアスパターンをどう明らかにするか、ドリフトが閾値を超えたときにガバナンス所見がどうエスカレーションするかを観察します ## 評価 | 次元 | ウェイト | Addie が評価するもの | | ----------- | ---- | ---------------------------------------- | | プロトコルマスタリー | 25% | キャンペーンガバナンスライフサイクルを含む全ガバナンスドメインの完全なマスタリー | | セーフティ専門性 | 25% | 3者トラストモデル、職務分離、ガバナンスドメインの構成を理解する | | Oracle 理解 | 20% | AI 駆動評価モデルとポリシーレジストリ統合を理解する | | コンプライアンススキル | 30% | 規制要件、ポリシー解決、管轄スコープ、監査解釈を処理する | 合格基準: 70%。 ## このモジュールを始める 「ガバナンススペシャリストモジュールを始めたい」 # S1: メディアバイマスタリー Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/specialist/media-buy AdCP スペシャリストモジュール S1: メディアバイマスタリー。完全なトランザクションライフサイクル、価格モデル、提案、予測、ライブサンドボックスエージェントとのマルチエージェントオーケストレーション。 # S1: メディアバイマスタリー **メンバー限定** — Practitioner クレデンシャルが必要。Addie と約45分。ハンズオンラボと適応型試験の組み合わせ。 このスペシャリストモジュールは、メディアバイのトランザクションライフサイクルのマスタリーをテストします。ライブサンドボックスエージェントを使って複雑なフローを実行します: 提案、予測、絞り込み、パッケージ、マルチエージェントオーケストレーション。Addie はハンズオンの作業と概念的な理解の両方を評価します。 合格すると **AdCP specialist — Media buy** クレデンシャルを取得できます。 ## Specialisms this track prepares you to validate `media_buy` ドメインのエージェントは、`get_adcp_capabilities` の `specialisms` フィールドでサポートする特定のフローを宣言します。各専門分野は `/compliance/{version}/specialisms/{id}/` にコンプライアンスストーリーボードを持ち、ランナーがクレームを検証するために実行します。このモジュールは、これらのクレームについて推論し、エージェントをそれらに対して検証することに備えます。 | Specialism | Status | What it covers | | ---------------------- | ---------- | ------------------------------------------------------------------------------ | | `sales-guaranteed` | stable | 人手による IO 承認を伴う保証型メディアバイ | | `sales-non-guaranteed` | stable | 非保証型のオークションベースメディアバイ | | `sales-proposal-mode` | deprecated | **3.1 で非推奨。** `sales-guaranteed` + `media_buy.supports_proposals: true` に置き換え。 | | `sales-catalog-driven` | stable | コンバージョン追跡付きのカタログ駆動コマース | | `sales-broadcast-tv` | stable | 保証型インベントリと FCC キャンセルルールを伴う放送リニア TV | | `sales-social` | stable | セルフサービスフローを伴うソーシャルメディア広告プラットフォーム | 完全なタクソノミーは [Compliance Catalog](/docs/building/compliance-catalog) を、権威あるリストは [`specialism` enum](https://adcontextprotocol.org/schemas/v3/enums/specialism.json) を参照。 ## 実証すること * 提案と予測を含む完全なメディアバイライフサイクルの実行 * 価格交渉、予算配分、マルチエージェントオーケストレーションの処理 * 複雑な購入シナリオのための絞り込みとパッケージリクエストの使用 * プロトコルツールを使ったキャンペーン配信の監視と最適化 * 障害モード、競合解決、エッジケースについての論理的な思考 ## 前提条件の読み物 ### コアトランザクションタスク プロダクト探索: 自然言語ブリーフ、構造化フィルター、レスポンススキーマ。 キャンペーン作成: マニュアルモード、提案モード、承認ライフサイクル。 キャンペーン変更: 予算、ターゲティング、スケジュール、クリエイティブスワップ。 デリバリーレポーティング: インプレッション、支出、完了率、パフォーマンス。 ### サポートコンセプト メディアバイプロトコルの正式仕様。 CPM、フラットレート、パフォーマンスベース価格、レートカード。 キャンペーン構造、状態、承認ライフサイクル。 マルチエージェントオーケストレーションのアーキテクチャパターン。 イベントソース、log\_event、アトリビューション設定。 キャンペーンパフォーマンスに基づくセラー最適化フィードバック。 ## Connecting to the test agent ラボ演習はパブリックテストエージェントに対して実行されます。共有トークンを使います — サインアップ不要: ```bash theme={null} export ADCP_AUTH_TOKEN="1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ" export AGENT_URL="https://test-agent.adcontextprotocol.org/sales/mcp" ``` 最初の呼び出しのウォークスルーについては [Quickstart](/docs/quickstart) を参照。 ## ラボ演習 モジュール中に Addie がハンズオン演習を案内する: 1. **プロダクト探索と評価** — 複数のサンドボックスエージェントをクエリし、プロダクトを比較し、価格を評価します 2. **提案と予測** — 予算ポイント付きで提案をリクエストし、デリバリー予測を分析します 3. **キャンペーン作成と最適化** — メディアバイを作成し、デリバリーを監視し、更新を実行します 4. **マルチエージェントオーケストレーション** — 複数のセラーにわたるキャンペーンを同時に管理します ## 評価 | 次元 | ウェイト | Addie が評価するもの | | ---------- | ---- | ---------------------- | | プロトコルマスタリー | 30% | メディアバイライフサイクルの包括的理解 | | ターゲティング専門性 | 25% | 高度なターゲティング機能をマスターしているか | | 分析スキル | 25% | デリバリーデータを効果的に分析できるか | | 問題解決 | 20% | 複雑なシナリオとエッジケースを処理できるか | 合格基準: 70%。 ## このモジュールを始める 「メディアバイスペシャリストモジュールを始めたい」 # S6: セキュリティ Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/specialist/security AdCP スペシャリストモジュール S6: セキュリティの習熟。脅威モデル、5 層防御モデル、冪等性セマンティクス、ガバナンストークン検証、SSRF 規律、運用インシデント対応。 # S6: セキュリティ **メンバー限定** — Practitioner 資格が必要。Addie と約 60 分。ハンズオンラボとアダプティブ試験を組み合わせます。 このスペシャリストモジュールは、AdCP のセキュリティモデル — 5 層防御: アイデンティティ検証、テナント分離、冪等性セマンティクス、署名付きガバナンス検証、SSRF 規律 — の習熟をテストします。サンドボックスがサポートするコントロールをハンズオンで実行し(冪等性セマンティクス、SSRF 規律、署名付きガバナンストークンの取得)、残りを脅威シナリオとインシデント対応を通じて推論します。Addie はあなたのハンズオン実行とセキュリティ推論の両方を評価します。 合格すると **AdCP specialist — Security** 資格を獲得します。 このモジュールは AdCP 固有のコントロール — エージェント型広告システムに固有の脅威モデル、層状防御、運用対応パターン — をカバーします。一般的なセキュリティプログラムの代替ではありません。認定スペシャリストは AdCP のコントロールがどう構成されるかについて推論できます; OWASP Top 10 や一般的なセキュリティエンジニアリングについては、組織のセキュリティトレーニングを参照してください。 ## このトラックが検証を準備する専門分野 以下の `specialisms` はセキュリティドメインに該当します。それぞれ独自のコンプライアンスストーリーボードを持ちます — 完全な分類については [コンプライアンスカタログ](/docs/building/verification/compliance-catalog) を参照。 | Specialism | Status | カバーするもの | | ----------------- | ------ | -------------------------------------------------------------------- | | `security` | stable | 認証ベースライン — 未認証拒否、静的クレデンシャル強制、OAuth ディスカバリー + RFC 9728 オーディエンスバインディング | | `signed-requests` | stable | RFC 9421 トランスポート層リクエスト署名検証 | ## あなたがデモンストレーションすること * エージェント型広告の脅威モデルを説明: クレデンシャル窃盗、リプレイ攻撃、クロステナントデータ漏洩、アウトバウンドフェッチでの SSRF、なりすましエージェントアイデンティティ、不正なガバナンストークン使用、監査ログ改ざん * AdCP の 5 層防御モデル — アイデンティティ、分離、冪等性、署名付きガバナンス、監査可能性 — をウォークスルーし、各層が閉じる特定の攻撃を名指す * 冪等性キーを発行し、サンドボックスに対して 3 つの観測可能な結果を生成 — 成功する初回呼び出し、冪等リプレイ(`replayed: true`、変更されないリソース)、ペイロード変更でのコンフリクト — し、4 番目について推論: TTL 期限切れ(サンドボックスのリプレイウィンドウは 24h なので、期限切れはセッション内で観測されるのではなく推論される)と、キーの欠如がセラーの安全性保証から何を取り除くか * 署名付きガバナンストークンを取得しデコードし、次にサンドボックス検証者を使ってチェックリストが有効なトークンを受け入れ、改ざんされたもの(署名)、誤アドレスのもの(`aud` / confused-deputy)、失効したキーで署名されたものを拒否するのを見る — 各失敗ステップが閉じる攻撃と、なぜ失効が期限切れより前にチェックされるかを説明 * アウトバウンドフェッチでの 6 点 SSRF チェック(HTTPS のみ強制、クラウドメタデータエンドポイントを含む予約 IP 拒否リスト、IP ピン検証、リダイレクト抑制、サイズとタイムアウトの上限、抑制されたエラー詳細)を指定し、エージェントがメタデータ IP webhook ターゲットを拒否するのをデモンストレーション * クレデンシャル侵害、webhook シークレットローテーション、ガバナンスキー失効、当事者間インシデント通信をカバーする運用ランブックを設計 * インシデントの記述が与えられたら、どの防御層が失敗したか、どの特定のコントロールを強化するかを識別 S4(ガバナンス)は **セラーの** 視点から 15 ステップの JWS セラー検証をカバーします — セラーがバイヤーのガバナンスエージェントによって発行されたガバナンストークンをどう検証するか。S6 は **セキュリティオペレーターの** 視点からそれをカバーします — 自身のトークン発行実装が正しいことを検証し、各ステップが何を閉じるか推論する。重複は意図的です; フレーミングが異なります。 ## 前提読書 AdCP の 5 層防御モデル: アイデンティティ、分離、冪等性、署名付きガバナンス、監査可能性。 実装リファレンス: 冪等性強制、webhook HMAC 検証、SSRF 規律、署名付きガバナンス、プリンシパル分離、インサートレート上限。 ガバナンストークン構造、JWS 検証モデル、マルチパーティライフサイクル追跡の相関モデル。 運用の関心事としてのセキュリティ: クレデンシャル管理、ローテーション頻度、インシデント対応。 プリンシパル分離、アカウントスコープアクセス、マルチテナント分離。 静的クレデンシャル強制、OAuth ディスカバリー、RFC 9728 オーディエンスバインディング、認証ベースライン専門分野。 ## テストエージェントへの接続 ラボ演習は公開テストエージェントに対して実行します。共有トークンを使います — サインアップ不要: ```bash theme={null} export ADCP_AUTH_TOKEN="1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ" export AGENT_URL="https://test-agent.adcontextprotocol.org/mcp" ``` 最初の呼び出しのウォークスルーについては [クイックスタート](/docs/quickstart) を参照。 ## ラボ演習 1. **脅威モデルウォークスルー** — 各脅威(クレデンシャル窃盗、リプレイ、クロステナント漏洩、SSRF、なりすましアイデンティティ、不正ガバナンス、監査改ざん)をそれを閉じる特定の AdCP コントロールにマップ。なぜ単一の層だけでは不十分かを説明。 2. **冪等性ライフサイクル** — ミューテーション呼び出し(例: `create_property_list`)で 1 つの冪等性キーを使い: (a) 初回呼び出し — 成功を観測; (b) 同一リプレイ — 変更されないリソース id で `replayed: true` を観測し、新しい副作用がないことを確認; (c) 同じキー、異なるペイロード — `IDEMPOTENCY_CONFLICT` エラーを観測。次に 4 番目の結果 — リプレイウィンドウが経過した後の期限切れ(24h なのでセッション内で再現不可) — について推論し、冪等性キーの欠如がセラーの at-most-once 安全性保証にとって何を意味するか説明。 3. **ガバナンストークン検証** — サンドボックスガバナンスエージェントから署名付きガバナンストークンを取得(`sync_plans`、次に intent フェーズの `check_governance`)し、そのヘッダー(`alg`、`typ`、`kid`)とクレーム(`aud`、`sub`、`phase`、`jti`、`exp`)をデコード。次にサンドボックス検証者(`comply_test_controller` シナリオ `verify_governance_token`)を実行し、JWS チェックリストがトークンを受け入れ拒否するのを見る: 有効なトークンはすべてのステップを通過; 改ざんされたクレームは署名ステップで失敗(`governance_token_invalid`); 異なるセラーにバインドされたトークンは `aud` バイトマッチで失敗(`governance_token_not_applicable` — confused deputy、`mode: wrong_aud_demo` 経由); 失効したキーで署名されたトークンは失効ステップで失敗(`governance_token_revoked`、`mode: revoked_demo` 経由)。それぞれについて、失敗ステップが閉じる攻撃を説明 — そして失効が期限切れの *前に* チェックされるので、失効したトークンは経過していても拒否されることに注意。(`jti` の既視重複排除 — `aud` とは別 — が同じトークンのリプレイを止めるステップ。) 4. **SSRF 防御** — URL がクラウドメタデータアドレス(`https://169.254.169.254/latest/meta-data/`)をターゲットする webhook `notification_config` を `sync_accounts` 経由で登録; エージェントが `notification_configs[].url` の `VALIDATION_ERROR` で同期的にそれを拒否するのを観測し、受け入れられる公開ホストと対比。6 点 SSRF チェックを指定し、各点が何を閉じるか説明 — 予約 IP / メタデータ拒否リスト、接続時 IP ピン(DNS リバインディング)、リダイレクト抑制、抑制されたエラー詳細。 5. **プリンシパル分離** — リソースを作成したアカウントとは異なるアカウントに読み取りをスコープし、結果を正しく解釈: アカウントスコープの *not-found* を認可の *拒否* と区別。分離モデル — アカウントスコープアクセス — と、アカウントスコープトークンが強制されなかった場合に何が壊れるか(漏洩したトークンでのクロステナント読み書き)を説明。 6. **インシデントランブック設計** — クレデンシャル侵害シナリオ(API キーが公開リポジトリに漏洩)が与えられたら、対応を設計: どのキーをどの順序でローテーションするか、カウンターパーティにどう通知するか、どの監査イベントをレビューするか、侵害ウィンドウをどう検証するか。 7. **防御層診断** — 3 つのインシデント記述(リプレイ攻撃成功、クロステナントデータ返却、キー失効後にガバナンストークン受け入れ)が与えられたら、各ケースでどの層が失敗したか、どの特定のコントロールを強化するかを識別。 ## 評価 | Dimension | Weight | Addie が評価するもの | | --------- | ------ | --------------------------------------------------------------- | | 脅威モデルの流暢さ | 20% | 攻撃とそれを閉じる特定の層を名指せるか? | | ハンズオン冪等性 | 20% | オンデマンドで観測可能な冪等性結果(成功、リプレイ、コンフリクト)を生成し、期限切れとキーの欠如について推論できるか? | | ガバナンス検証 | 25% | 15 ステップのチェックリストをウォークし、各ステップが何を防ぐか説明できるか? | | SSRF 規律 | 15% | 6 点チェックを指定し、エージェントがメタデータ IP webhook ターゲットを拒否するのをデモンストレーションできるか? | | 運用設計 | 20% | ローテーション順序と当事者間通信を含む、クレデンシャル侵害のランブックを設計できるか? | 合格しきい値: 70%。 # S3: シグナルとオーディエンス Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/specialist/signals AdCP スペシャリストモジュール S3: シグナルとオーディエンス。6業界にわたるサンドボックスデータプロバイダーとのシグナル探索、アクティベーション、プライバシー制御、最適化ループ。 # S3: シグナルとオーディエンス **メンバー限定** — Practitioner クレデンシャルが必要。Addie と約45分。ハンズオンラボと適応型試験の組み合わせ。 このスペシャリストモジュールは、シグナルプロトコルのマスタリーをテストします。サンドボックスシグナルプロバイダー — 自動車データ、ジオ/モビリティ、リテール購買データ、アイデンティティ/デモグラフィクス、パブリッシャーコンテキストシグナル、CDP オーディエンス — を使ってシグナルを探索し、アクティベートし、プライバシーを管理し、最適化ループを設計します。Addie はエコシステムでの役割に合わせて体験を適応させる。 合格すると **AdCP specialist — Signals** クレデンシャルを取得できます。 ## Specialisms this track prepares you to validate 次の `specialisms` は `signals` ドメインに属します。それぞれ独自のコンプライアンスストーリーボードを持ちます — 完全なタクソノミーは [Compliance Catalog](/docs/building/compliance-catalog) を参照。 | Specialism | Status | What it covers | | -------------------- | ------ | -------------------------------------- | | `signal-owned` | stable | ファーストパーティセグメントを公開する owned シグナルエージェント | | `signal-marketplace` | stable | サードパーティデータを再販する marketplace シグナルエージェント | **AdCP シグナルのスコープ** トレーニングに時間を投資する前に、プロトコルが今日カバーするものとスコープ外のものを把握しておくと役立つ。 * **スコープ内**: アイデンティティ由来の属性(収入ティア、ライフステージ)、行動シグナル(購入意向、来店頻度)、コンテキストシグナル(コンテンツカテゴリ、センチメント)、地理的オーディエンス(商圏、店舗来訪者) * **未対応**: アイデンティティ解決とマッチング(デバイスを人物に結びつける)、リアルタイムジオフェンシングトリガー(誰かがゾーンに入ったときにプッシュ)、測定とアトリビューションパイプライン 「未対応」エリアで業務を行う会社も、データが生成する**オーディエンスセグメント**は公開できる — プロトコルは基礎インフラをカバーしていなくても、ターゲティングのアウトプットをカバーします。 ## 実証すること * 複数のプロバイダータイプ(データプロバイダー、リテーラー、パブリッシャー、CDP)からシグナルを探索・評価します * 異なるキャンペーン目標に適切なシグナルをアクティベートします * シグナル値タイプ(binary、categorical、numeric)とターゲティングへの影響を理解します * プライバシーの考慮事項と非アクティベーションを含むオーディエンスアクティベーションを管理します * `sync_event_sources` と `log_event` でイベントトラッキングを設定します * シグナルとデリバリーデータを使った最適化ループを設計します * シグナルエコシステムについて論理的に考える — データを提供するのは誰か、消費するのは誰か、認可はどう機能するか ## 前提条件の読み物 ### コアシグナルタスク シグナル探索: ターゲティング可能なオーディエンス、コンテキストカテゴリ、測定データを見つける。 キャンペーンターゲティングまたは測定のためにシグナルをアクティベートします。 ### サポートコンセプト シグナルプロトコル: オーディエンスセグメント、コンテキストシグナル、測定、最適化。 シグナルプロトコルの正式仕様。 データプロバイダーが adagents.json 経由でシグナルカタログを公開する方法。 リテーラー、パブリッシャー、CDP、アイデンティティ会社などの異なる会社タイプがシグナルに参加する方法。 イベントソース、`log_event`、アトリビューション設定。 シグナルがキャンペーン最適化の意思決定にどう反映されるか。 ## Connecting to the test agent ラボ演習はパブリックテストエージェントに対して実行されます。共有トークンを使います — サインアップ不要: ```bash theme={null} export ADCP_AUTH_TOKEN="1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ" export AGENT_URL="https://test-agent.adcontextprotocol.org/signals/mcp" ``` 最初の呼び出しのウォークスルーについては [Quickstart](/docs/quickstart) を参照。 ## ラボ演習 サンドボックストレーニングエージェントには、異なるエコシステムの役割を代表するシグナルプロバイダーが含まれています。すべてで作業する: | プロバイダー | タイプ | シグナル | | ------------------------- | --------- | ------------------------------------------------ | | Trident Auto Data | データプロバイダー | EV 購入者、車両所有、購入傾向、サービス期日、サービス履歴 | | Meridian Geo | ジオ/モビリティ | 競合来訪者、来店頻度、商圏、通勤パターン、滞在時間、時間帯来訪 | | ShopGrid Shopper Insights | リテーラー | カテゴリ購入者、ロイヤルティティア、バスケット価値、ブランド新規、購入頻度、ブランドアフィニティ | | Keystone Identity | アイデンティティ | 世帯収入、ライフステージ、クロスデバイスリーチ、信用活動、世帯構成 | | Pinnacle News Signals | パブリッシャー | コンテンツカテゴリ、エンゲージドリーダー、サブスクライバー期間、センチメント、ページタイプ | | Prism CDP | CDP | 高 LTV、カート放棄者、エンゲージメントスコア、チャーンリスク、クロスデバイス | ### 演習 1: シグナル探索 異なるキャンペーン目標に合致するシグナルのサンドボックスシグナルエージェントをクエリします。プロバイダータイプをまたがってシグナルを比較する — データプロバイダーの自動車シグナルはリテーラーの購買シグナルとどう違うか? ### 演習 2: シグナルアクティベーション サンドボックスキャンペーンのシグナルをアクティベートします。アクティベーションキーの仕組みとデプロイメントステータスの変化を観察します。 ### 演習 3: オーディエンス管理 シグナルをアクティベートし、非アクティベートします。プライバシーについて考える: シグナルを非アクティベートする必要があるのはいつか?同意はシグナルの可用性にどう影響するか? ### 演習 4: エコシステムシナリオ Addie が特定の視点からシナリオを提示する — リテールメディアネットワークのシグナルカタログを構築しているかもしれないし、代理店のクライアントキャンペーンのシグナルを選択しているかもしれないし、CDP インテグレーションを設計しているかもしれない。プロトコルの知識をシナリオに適用します。 ### 演習 5: シグナルカタログの構築(プロバイダーの視点) 前の演習はバイヤーサイド — シグナルの探索とアクティベーション — に焦点を当てていました。この演習はプロバイダーサイドに切り替える。任意の架空のデータプロバイダー(ジオ、リテール、アイデンティティなど)の `adagents.json` シグナルエントリを構築します。カタログには以下を含める: * 各値タイプ(`binary`、`categorical`、`numeric`)のシグナルを少なくとも1つ * [データプロバイダーガイド](/docs/signals/data-providers)に従った説明的な ID、タグ、メタデータ * シグナルエージェントにカタログの再販を承認する `authorized_agents` エントリ * 機微な個人データから派生したシグナル(例: `health_data`、`racial_ethnic_origin`)への `restricted_attributes` 宣言 * 規制上の意味合いを持つシグナル(例: `children_directed`、`fair_housing`)への `policy_categories` 宣言 タグや説明でクエリしたときに `get_signals` の結果にシグナルが正しく表示され、ガバナンス属性がシグナルメタデータに保持されていることを確認してカタログを検証します。 ## 評価 | 次元 | ウェイト | Addie が評価するもの | | -------------- | ---- | --------------------------------------------------------------------------- | | プロトコルマスタリー | 25% | 完全なシグナルライフサイクル(探索 → アクティベーション → ターゲティング → 非アクティベーション) | | プライバシーコンプライアンス | 20% | 同意、非アクティベーション、データガバナンスを正しく処理する | | 測定スキル | 20% | コンバージョントラッキングとアトリビューションを設定する | | エコシステム理解 | 20% | 異なるプロバイダータイプがシグナルにどう適合するか説明できる | | エコシステムシナリオ | 15% | 有効なシグナルカタログを構築し、バイヤーとプロバイダー両方の視点を理解し、アクティベーション先(エージェント対プラットフォーム)について論理的に考える | 合格基準: 70%。 ## このモジュールを始める 「シグナルスペシャリストモジュールを始めたい」 # S5: スポンサードインテリジェンス Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/specialist/sponsored-intelligence AdCP スペシャリストモジュール S5: スポンサードインテリジェンス。AI チャットの収益化 — ジェネレーティブクリエイティブ、逆転データフロー、会話型ブランド体験のための SI Chat Protocol。 # S5: スポンサードインテリジェンス **メンバー限定** — Practitioner クレデンシャルが必要。Addie と約45分。ハンズオンラボと適応型試験の組み合わせ。 このスペシャリストモジュールは、AI での広告の仕組みをカバーする: ブランドアセットとカタログからのジェネレーティブクリエイティブ、バイヤーがビッドリクエストを送り出す代わりにデータをプラットフォームにプッシュする逆転データフロー、そしてブランドがユーザーとマルチターン会話を行う SI Chat Protocol。Addie はハンズオンの作業とスポンサードインテリジェンスがいつなぜ適切かの理解の両方を評価します。 合格すると **AdCP specialist — Sponsored Intelligence** クレデンシャルを取得できます。 ## Sponsored Intelligence as a full protocol 3.0 で、Sponsored Intelligence は専門分野から完全なプロトコルに昇格しました。SI をサポートするエージェントは、`get_adcp_capabilities` の `supported_protocols` で `sponsored_intelligence` を宣言します — 専門分野としてではありません。コンプライアンスランナーは `/compliance/{version}/domains/sponsored-intelligence/` の SI ドメインベースラインストーリーボードに加え、すべてのユニバーサルストーリーボードを実行します。[Compliance Catalog](/docs/building/compliance-catalog) を参照。 ## 実証すること * ブランドアセット、カタログデータ、自然言語ブリーフからクリエイティブを生成します * 逆転データフローを使ってスポンサードインテリジェンスキャンペーンを実行します * 会話型ブランド体験のために SI Chat Protocol セッションを管理します * ウォールドガーデン対エージェントトラストネットワークのアカウントモデルを説明します * スポンサードインテリジェンスが従来のアプローチに対していつ適切かについて論理的に考える ## 前提条件の読み物 ### ジェネレーティブクリエイティブ `build_creative` による AI 搭載クリエイティブ生成 — マニフェスト、コード出力、ブランドアイデンティティ統合。 フォーマット探索 — クリエイティブエージェントがサポートする広告フォーマット。 ### スポンサードインテリジェンス 逆転データフロー、プロダクトスペクトラム、エンドツーエンドワークフロー、ネットワーク集約パターン。 プロダクトとオファリングカタログ — AI プラットフォームでのジェネレーティブクリエイティブの原材料。 ガバナンス施行を含む AI バイヤーエージェントへのインベントリ公開方法。 `sponsored_intelligence` チャンネル定義。 ### Governance integration ガバナンスが Sponsored Intelligence セッションでコンテンツ標準、ポリシーコンプライアンス、ブランドセーフティをどう検証するか。 SI エージェントが言えることを制約するバイヤー定義のコンテンツルール。 ### SI Chat Protocol 会話型ブランド体験 — セッションライフサイクル、アイデンティティ、コマースハンドオフ。 スポンサードインテリジェンスプロトコルの正式仕様。 AI プラットフォームがブランド体験ハンドオフのために SI Chat Protocol を統合する方法。 SI ブランドエージェント構築のアーキテクチャガイド。 ## Connecting to the test agent ラボ演習はパブリックテストエージェントに対して実行されます。Sponsored Intelligence のラボはクリエイティブ生成、ガバナンス、SI Chat Protocol のサーフェスにまたがります — 単一の URL ですべてのツールを公開するマルチ専門分野エンドポイントを使います: ```bash theme={null} export ADCP_AUTH_TOKEN="1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ" export AGENT_URL="https://test-agent.adcontextprotocol.org/mcp" ``` 最初の呼び出しのウォークスルーについては [Quickstart](/docs/quickstart) を参照。 ## ラボ演習 モジュール中に Addie がハンズオン演習を案内する: 1. **ジェネレーティブクリエイティブ** — フォーマットを探索し、ブランドアセットとブリーフからクリエイティブを生成し、出力品質を評価します 2. **スポンサードインテリジェンスキャンペーン** — カタログをプラットフォームにプッシュし、プロダクトを探索し、最適化目標付きでメディアバイを作成します 3. **SI Chat Protocol セッション** — セッションを開始し、オファリング統合付きでメッセージを交換し、コマースハンドオフを観察します 4. **戦略的評価** — 異なるシナリオでスポンサードインテリジェンスと従来のアプローチを比較します ## 評価 | 次元 | ウェイト | Addie が評価するもの | | ------------------- | ---- | ----------------------------------------- | | ジェネレーティブクリエイティブ | 25% | ブランドアセット、カタログ、ブリーフからクリエイティブを構築する | | SI マスタリー | 30% | 逆転データフローを理解し、スポンサードインテリジェンスキャンペーンを実行する | | SI Chat Protocol 能力 | 25% | SI Chat Protocol セッションを管理し、会話型ブランド体験を理解する | | 戦略的思考 | 20% | スポンサードインテリジェンスをいつどう使うかについて論理的に考える | 合格基準: 70%。 ## このモジュールを始める 「スポンサードインテリジェンススペシャリストモジュールを始めたい」 # バイヤーブリーフと get_products リクエスト形状 Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/supplements/buyer-briefs-and-get-products バイヤーエージェントが get_products のブリーフ文字列、構造化フィルター、フォローアップの絞り込みリクエストに何が属するかをどう決めるか。 # バイヤーブリーフと get\_products リクエスト形状 このサプリメントは、バイサイドの実装者が人間のキャンペーンリクエストを正確な `get_products` 呼び出しに変えられるよう準備します。 ゴールはブリーフを冗長にすることではありません。ゴールは意図をブリーフに、ハードな制約を型付きフィールドに置くことで、セラーがどの部分が交渉可能か推測せずに在庫をキュレートできるようにすることです。 ## メンタルモデル | Input | 用途 | ここに置くのを避ける | | --------- | ------------------------------------------ | ---------------------- | | `brief` | バイヤーの意図、オーディエンス言語、コンテキスト、トーン、ビジネスゴール、成功の定義 | 既に型付きフィールドを持つ機械強制可能な制約 | | `filters` | 一致しない製品を静かに除外すべきハードな制約 | セラーがキュレーションで満たせるソフトな選好 | | `brand` | セラーが適格性、安全性、フィットに使うバイヤーブランドアイデンティティ | キャンペーンブリーフの 2 番目のコピー | | `catalog` | キャンペーンがカタログ駆動のときのコマースまたは製品セットコンテキスト | 一般的なブランドポジショニング | | `refine` | 以前のディスカバリーレスポンスへの特定の変更 | 新しい無関係なディスカバリーゴール | ## ブリーフに入るもの セラーまたはキュレーターが判断を下すのに必要なコンテキストにブリーフを使います: * キャンペーン目的: 認知、検討、適格トラフィック、来店、コンバージョン、更新 * 人間の言葉でのオーディエンス記述 * 製品、オファー、季節的コンテキスト、またはクリエイティブの方向性 * ファミリー適合性や競合隣接性のような、理解必須の機微 * メトリックフィルターではない成功言語、例えば「信頼される編集環境を優先」 例: ```json theme={null} { "buying_mode": "brief", "brief": "Launch Nova Running's spring trail shoe line with outdoor enthusiasts. Favor trusted adventure and fitness contexts, avoid discount-led positioning, and prioritize packages that can support a brand-lift readout.", "brand": { "domain": "novarunning.example" } } ``` ## フィルターに入るもの 条件に失敗する製品が返ってくるべきでないときにフィルターを使います。 良いフィルター候補: * 必須チャネルまたはフォーマット * 必須ジオターゲティングサポート * 必須の測定またはレポートケイパビリティ * 価格通貨制約 * 予算範囲 * 固定価格要件 * ホールセール / カタログ用途の製品カテゴリー 例: ```json theme={null} { "buying_mode": "brief", "brief": "Launch Nova Running's spring trail shoe line with outdoor enthusiasts. Favor trusted adventure and fitness contexts.", "brand": { "domain": "novarunning.example" }, "filters": { "channels": ["ctv", "display"], "pricing_currencies": ["USD"], "required_metrics": ["impressions", "clicks"] } } ``` バイヤーが「理想的には CTV だが、ディスプレイでも OK」と言うなら、その選好をブリーフに保ちます。「CTV のみ」と言うなら、`filters.channels` を使います。 ## ブリーフ対 refine バイヤーが以前のディスカバリーレスポンスに反応しているときは `buying_mode: "refine"` を使います。refine リクエストは何が変わったかを指すべきです: 製品を削除、予算を調整、よりプレミアムなプレースメントをリクエスト、地理を絞る、または代替を求める。 `refine` に完全に新しいキャンペーンを送らないでください; 代わりに新しい `brief` リクエストを開始します。 ## 実装チェックリスト * セラーを呼ぶ前に、人間のリクエストを意図、ハードな制約、フォローアップの変更に正規化する。 * バイヤーのビジネス言語を `brief` に保つ; それをキーワードだけに崩さない。 * 型付き制約を `filters` に置き、セラーが `filter_diagnostics` を通じて除外を説明できるようにする。 * `brief` を `wholesale` モードから外す。 * 後の `refine` 呼び出しと `wholesale_feed_version` 比較が正しくスコープされるよう、リクエストタプルをレスポンスとともに永続化する。 ## 練習プロンプト バイヤーが言います: > Acme Meals の新しいファミリーディナーキットのための 6 週間のローンチが必要。CTV またはオンラインビデオが欲しく、USD 価格のみ、子供のいる親に適したもの、そして完了率レポートが必要。 期待される分解: * ブリーフ: ファミリーディナーキットのローンチ、親オーディエンス、適したコンテキスト、6 週間のローンチ。 * フィルター: ビデオ対応チャネル / フォーマット制約、USD 価格、完了率レポート。 * ブランド: Acme Meals ドメインまたは BrandRef。 # クリエイティブエージェントワークフロー Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/supplements/creative-agent-workflows クリエイティブエージェントが build_creative、preview_creative、sync_creatives、および人間承認ワークフローをどう実装するか。 # クリエイティブエージェントワークフロー このサプリメントは、クリエイティブエージェントの実装者が完全なクリエイティブライフサイクル — 構築、プレビュー、承認、同期、回復 — をモデル化できるよう準備します。 クリエイティブエージェントは、どれだけの状態を所有するかで異なります。テンプレートトランスフォーマーはアセットを直接返すかもしれません。生成エージェントは非同期プロダクションが必要かもしれません。アドサーバーは最初にプレビューを返し、承認までライブタグを保留するかもしれません。 ## コアライフサイクル 1. ケイパビリティとサポートされるフォーマットを発見する。 2. `build_creative` でクリエイティブを構築または変換する。 3. `preview_creative` でクリエイティブをプレビューする。 4. エージェントまたはパブリッシャーが要求するときに人間承認を収集する。 5. 承認されたクリエイティブを `sync_creatives` で同期する。 6. ワークフローが要求する場所で使用または配信をレポートする。 ## build\_creative クリエイティブマニフェストを生成またはプロダクションを開始するには `build_creative` を使います。 リクエストは以下を運ぶべきです: * ターゲットフォーマットまたは正準 `format_kind` * アセットまたはクリエイティブブリーフ入力 * ブランドコンテキスト * `format_options[]` からの製品固有の絞り込み * クリエイティブエージェントがプロダクションに課金するときの価格オプション選択 レスポンスは、クリエイティブが準備完了か、まだ作業中か、失敗したか、入力を待っているかについて明示的であるべきです。 ## preview\_creative レビュー可能なレンダリングには `preview_creative` を使います。プレビューは必ずしもライブタグではありません。 一般的なパターン: * 静的アセットプレビュー: 画像、ビデオ、オーディオ、または HTML プレビュー。 * バッチプレビュー: 同じマニフェストからの多数のバリエーション。 * 履歴プレビュー: 配信レコードから以前のバリアントをレンダー。 * ベンダープレビュー: ライブタグが保留されたままの、期限切れになるプレビューリンク。 ベンダーが承認までライブタグを保留する場合、それをプレビュー + 承認状態としてモデル化します。別の「タグ取得」タスクを発明しないでください。 ## sync\_creatives 承認されたクリエイティブアセットまたはタグをセラーまたはパブリッシャー環境にプッシュするには `sync_creatives` を使います。 実装ルール: * アップストリームのトラフィッキングが即座でないときは非同期 `working` 状態を受け入れる。 * 利用可能なとき安定したクリエイティブ ID とプラットフォーム ID を返す。 * リトライ全体で冪等性を保持する。 * 検証失敗をフィールドレベルの詳細でレポートする。 * バイヤー所有のクリエイティブアイデンティティをセラープラットフォーム ID と分離しておく。 ## 人間承認 クリエイティブがポリシー、ブランド、法的、パブリッシャー、またはベンダーのレビュー要件を持つとき、人間承認はワークフローの一部です。 承認を沈黙ではなく状態としてモデル化します: * `pending_review`: 同期またはライブタグリリースの前に誰かが承認しなければならない。 * `approved`: 同期が進むか、ライブタグがリリースされてよい。 * `rejected`: バイヤーまたはクリエイティブエージェントは、`sync_creatives` で再送信する前に修正しなければならない。 ## 回復パターン | Failure | Recovery | | ------------------ | --------------------------------------------------- | | フォーマット不一致 | 製品 `format_options[]` を再読、正しい `format_kind` を選択、再構築 | | 必須アセット欠如 | 欠けているアセットのみを求める; ディスカバリーを再開しない | | プレビュー期限切れ | `preview_creative` を再実行するか、新しいプレビューリンクをリクエスト | | 人間承認が停滞 | 保留中の承認状態と所有者を表面化 | | 同期が非同期 working を返す | タスクステータスをポールするか通知を購読 | | セラーがクリエイティブを拒否 | 拒否理由を保持、マニフェストを修正、新しい論理リビジョンでリトライ | ## 練習プロンプト ベンダークリエイティブエージェントがプレビュー URL を返すが、ライブディスプレイタグを返しません。バイヤーはオーケストレーターにキャンペーンを即座にトラフィックするよう求めます。 期待される回答: * オーケストレーターはクリエイティブをプレビュー済みだが承認 / ライブではないものとして扱う。 * バイヤーまたはレビュアーがプレビューを承認する。 * クリエイティブエージェントが承認されたワークフローを通じてライブタグをリリースまたは同期する。 * セラーは承認状態が実行を許可した後にのみ同期されたクリエイティブを受け取る。 # ロール別ガバナンスプロトコル Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/supplements/governance-protocol-by-role sync_plans、check_governance、署名付きガバナンスコンテキスト、report_plan_outcome を含む、キャンペーンガバナンスループにおけるバイヤーとセラーの責任。 # ロール別ガバナンスプロトコル AdCP のガバナンスは共有された制御ループです。バイサイドのガバナンスエージェントが、計画または実行されたアクションが許可されるかを決めます。バイヤーエージェントがガバナンスコンテキストを取得し運びます。セラーは作業を実行する前にそのコンテキストを検証し、結果を監査証跡に返します。 ## ロール | Role | 責任 | | ----------- | ---------------------------------------- | | バイヤーエージェント | プランを構築し、ガバナンスを呼び、セラーリクエストにガバナンスコンテキストを添付 | | ガバナンスエージェント | プラン、ポリシー、予算、権限に対して意図と実行を評価 | | セラーエージェント | 実行前に署名付きガバナンスコンテキストを検証し、確認された結果をレポート | | 人間レビュアー | 必要なとき例外、オーバーライド、曖昧なポリシー決定を処理 | ## バイサイドフロー 1. `sync_plans` でキャンペーンプランを作成または更新する。 2. セラーに実行を求める前に、intent フェーズのために `check_governance` を呼ぶ。 3. 返されたガバナンスコンテキストをセラーリクエストに添付する。 4. 実行詳細が返ってきたら、ワークフローが要求する場合、execution フェーズのために再度 `check_governance` を呼ぶ。 5. 結果をポールまたは購読し、ガバナンス監査証跡を保持する。 バイヤーが計画の真実を所有します。バイヤーが予算、オーディエンス、セラー、フライト日、製品選択、または目的を変更する場合、プランを更新するか新しいガバナンス決定をリクエストしなければなりません。 ## セルサイドフロー 1. ガバナンスコンテキストを持つリクエストを受け取る。 2. 実行前に署名付きガバナンスコンテキストを検証する。 3. トークンを期待されるプラン、呼び出し元、セラー、フェーズ、操作にバインドする。 4. 欠けた、期限切れの、失効した、リプレイされた、または不一致の署名付きコンテキストを `PERMISSION_DENIED` で拒否する。 5. セラーが結果レポートに責任を持つとき、`report_plan_outcome` を通じて確認された配信または支出をレポートする。 セラーはバイヤーポリシーをゼロから再解釈しません。それはバイヤーのガバナンスエージェントがこの正確な実行を認可したことを検証し、実際に何が起こったかをレポートします。 `GOVERNANCE_DENIED` は、バイサイドのガバナンスエージェントが計画または実行されたアクションに対して実際の拒否を返したときのみ使います。 ## check\_governance ペイロード規律 最も一般的な実装バグは、フェーズに必要なフィールドを省略する部分的なチェックを送ることです。 intent チェックは通常以下を必要とします: * `plan_id` * `caller` * 提案されたセラーまたはオペレーターのアイデンティティ * 予算とペーシングの意図 * ターゲティングとポリシー関連の制約 * 既に選択されているときは製品またはパッケージ参照 execution チェックは通常以下を必要とします: * `plan_id` * `caller` * セラーアイデンティティ * 実行される具体的なパッケージ、クリエイティブ、ターゲティング、日付、予算 * 配信フェーズをチェックするときは配信または実行メトリック * タスクが継続性を要求するときは以前のガバナンスコンテキストまたは相関識別子 ## テストすべきこと * 有効なプランが intent チェックを通過する。 * プランがそのセラーを認可しない限り、セラースワップが失敗する。 * `sync_plans` がプランを更新するまで、予算増加が失敗する。 * リプレイされたガバナンストークンが失敗する。 * 期限切れまたは失効したトークンが失敗する。 * 認可されたプラン外の配信レポートが失敗するか監査所見を生成する。 ## 練習プロンプト このケースのためのガバナンステストを設計してください: > Pinnacle Media は Nova Snacks のためのファミリーセーフな CTV パッケージを購入したい。バイヤープランは支出を USD 75,000 に制限し、2 つの承認済みセラーのみを許可する。セラーが異なるセラーアイデンティティでより高予算のパッケージを返す。 期待される回答: * バイヤーは実行前にプランを更新または拒否する。 * ガバナンス intent チェックは未承認のセラーまたは予算超過のパッケージを拒否すべき。 * セラーは元のセラーまたは予算にバインドされたコンテキストで実行してはならない。 * 監査ログは拒否理由と試みられた実行事実を示すべき。 # 学習者テストペルソナ Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/test-personas コンテンツがさまざまなユーザータイプ — レガシービルダーからエンタープライズバイヤーまで — にどう役立つかを評価するための AdCP ドキュメントテストペルソナ。 # 学習者テストペルソナ AdCP のドキュメント、ウェブサイト、Addie がさまざまなユーザータイプにどれだけよく役立つかをテストするための 7 つのペルソナ。ペルソナ 1-3 はビルド側(エンジニアリング実装)。ペルソナ 4-6 はバイ側(異なるスケールでの戦略と採用)。ペルソナ 7 は非コーダー向けの認定ビルドプロジェクト体験をテストします。それぞれが現実的なセッションと一連の質問を表します。 > **キャラクターバイブルとの関係**: キャラクターバイブル(`specs/character-bible.md` を参照)は、ウォークスルーパネルで使われる図解キャラクター(Alex、Sam、Jordan、Maya など)を定義します。ここでのテストペルソナは別の概念です — それらはコンテンツ品質を評価するために実際のユーザージャーニーをシミュレートします。テストペルソナがウォークスルーキャラクターのロールにマップする場合、その接続を記します。 *** ## ペルソナ 1: Marcus Chen — レガシー AdCP ビルダー ### ロール エージェンシーのテックチームのシニアエンジニア。約 9 か月前に AdCP 2.5 に対してバイヤーエージェント統合を構築。それは本番で稼働し、少数のブランドのためにメディアバイを配置している。 ### 背景 Marcus の統合は、MCP 上で製品ディスカバリー、クリエイティブ同期、メディアバイ作成を処理する。彼はそれを 2.5 スキーマに対して書き、ローンチ以来触れていない。Slack で v3 RC アナウンスを見て、移行する必要があることを知っているが、まだ変更履歴を読んでいない。プロトコルに慣れており、MCP が何か、タスクがどう機能するかを誰かに説明してもらう必要はない。 ### 彼が既に知っていること * コアタスクフロー: `get_products` -> `sync_creatives` -> `create_media_buy` -> `get_media_buy_delivery` * パブリッシャーディスカバリーのための `adagents.json` の仕組み * v2 チャネル enum(`display`、`video`、`audio`、`native`、`social`、`ctv`、`podcast`、`dooh`、`retail`) * パッケージの文字列配列としてのクリエイティブ ID * クリエイティブアセットタイプとしての `promoted_offerings` * 価格オプションの `fixed_rate` と `price_guidance.floor` * フラットな文字列配列としての `geo_postal_codes` と `geo_metros` * パッケージ上の単一オブジェクトとしての `optimization_goal` * ケイパビリティディスカバリーのための `adcp-extension.json` の仕組み ### 彼が知らないこと * `native` がチャネルとして削除されたこと(彼は `channels: ["native"]` を持つパッケージを持っている) * `video` が `olv`、`linear_tv`、`cinema` に分割されたこと * `creative_ids` が重み付けを持つ `creative_assignments` になったこと * 新しいアカウントモデル(`sync_accounts`、`list_accounts`、課金モデル) * `promoted_offerings` がファーストクラスカタログ(`sync_catalogs`)に置き換えられたこと * `brand_manifest` が `brand` ref(`{ domain, brand_id }`)に置き換えられたこと * `adcp-extension.json` が `get_adcp_capabilities` に置き換えられたこと * geo ターゲティングが今やシステム仕様を要求すること * `optimization_goal` が `optimization_goals`(配列、判別されたユニオン)になったこと * Brand Protocol、Governance、Sponsored Intelligence、または Registry API の存在 ### 誤解と盲点 * `native` が依然として有効なチャネルだと想定。検証が失敗すると混乱する。 * ケイパビリティディスカバリーが依然として `adcp-extension.json` を使うと想定。もう存在しないエージェントカード拡張ドキュメントを探す。 * `account_id` が単に彼が渡す文字列だと考える。`AccountReference` オブジェクトや account-id 名前空間対バイヤー宣言アカウントモデルについて知らない。 * `promoted_offering` がメディアバイ上の文字列フィールドのままだと期待する。 * 価格フィールドの名前が変わっていないと想定する。 * おそらく「what's new」ではなく「migration」または「upgrade」を検索する。 ### 主要ゴール 既存の統合に影響するすべての破壊的変更を理解し、更新すべきもののチェックリストを取得し、移行の労力(週ではなく時間)を見積もる。 ### 彼が尋ねる主要な質問 1. 「AdCP 2.5 と 3.0 の間で何が壊れたか?」 2. 「v2 から v3 への移行ガイドはあるか?」 3. 「native チャネルの代わりは何か?」 4. 「creative\_ids を新しいフォーマットにどう更新するか?」 5. 「adcp-extension.json はどうなったか?」 6. 「accounts プロトコルを実装する必要があるか、スキップできるか?」 7. 「移行中に v2 統合を v3 と並行して実行できるか?」 ### 彼が訪れそうなページ 1. `/docs/reference/whats-new-in-v3` — 最初の立ち寄り先、変更のサマリーを探す 2. `/docs/reference/migration/channels` — 彼の `native` と `video` パッケージが壊れている 3. `/docs/reference/migration/pricing` — `fixed_rate` と `price_guidance.floor` の修正 4. `/docs/reference/migration/creatives` — `creative_ids` から `creative_assignments` へ 5. `/docs/reference/migration/catalogs` — `promoted_offerings` の置き換え 6. `/docs/reference/migration/geo-targeting` — geo フィールドのシステム仕様 7. `/docs/reference/migration/optimization-goals` — 単一ゴールから配列へ 8. `/docs/reference/migration/brand-identity` — `brand_manifest` から `brand` ref へ 9. `/docs/accounts/overview` — 新しいアカウントモデルの理解 10. `/docs/protocol/get_adcp_capabilities` — `adcp-extension.json` の置き換え ### 成功基準 * 彼は統合が必要とするすべてのコード変更のラインアイテムリストを作成できる * どの変更がリネーム(簡単)対 構造的(より難しい)かを理解している * どの新しいプロトコルドメイン(accounts、governance、brand protocol)が彼のユースケースに必須対オプションかを知っている * 推測せずにコードを書き始めるのに十分なスキーマ詳細を持っている * ランディングから「何をすべきか分かった」までの合計時間: 45 分未満 *** ## ペルソナ 2: Ravi Mehta — AI アドネットワークビルダー ### ロール AI アドネットワークスタートアップ(Kontext や Koah を思い浮かべる)のエンジニアリングリード。彼の会社は複数の AI プラットフォーム — AI アシスタント、AI 検索エンジン、生成 AI 体験 — にわたって広告在庫を集約し、統一されたインターフェースを通じてエージェンシーとブランドに販売する。 ### 背景 Ravi の会社は、会話型と検索の体験で広告をサーブする十数の AI プラットフォームとのパートナーシップを持つ。会社のバリュープロップは集約: エージェンシーは各 AI プラットフォームと個別に統合したくなく、AI プラットフォームは自身の販売チームを構築したくない。彼のアドネットワークは中間に座る — エージェンシーから広告主データ(カタログ、予算、ブランドガイドライン)を受け入れ、それを適切な AI プラットフォームに配布する。 彼は各 AI プラットフォームと各エージェンシーとのカスタム統合を構築してきた。スケールしない。彼は AdCP の実装を検討しているパートナープラットフォームから AdCP について聞いた。彼は AdCP が彼のビジネスの両側の標準インターフェースになりうるか評価している: 需要側で AdCP 経由でデータをプッシュするバイヤーエージェント、供給側でそのデータを AI プラットフォームにプッシュするネットワーク。 彼はアドテックを深く知り(これ以前に中規模 SSP で ad ops を運営)、以前に MCP サーバーを構築した(彼の会社は既に MCP ベースのプロトタイプを持つ)。彼は技術的に流暢で、プロトコル仕様を直接読む。 ### 彼が既に知っていること * アドネットワークがどう供給と需要を集約するか * ファーストパーティプラットフォーム(ウォールドガーデン)とネットワーク(マルチプラットフォーム)の違い * MCP の基本 — 彼は MCP サーバーを構築し、ツール露出を理解し、クライアントがどう接続するか知っている * 従来のプログラマティック: OpenRTB、prebid、SSP/DSP メカニクス * 彼の会社の痛み: プラットフォームごととエージェンシーごとのカスタム統合がスケールしない * AI プラットフォームがブランドデータからクリエイティブを生成すること — 彼のネットワークはそのデータをパイプする必要がある * 複数の広告主とプラットフォーム全体のアカウント管理 * OAuth、API キー管理、マルチテナントアーキテクチャ ### 彼が知らないこと * AdCP が彼のユースケースのための特定の `sponsored_intelligence` チャネルを持つこと * `sync_catalogs` が、彼が各プラットフォームのためにカスタムで構築してきたカタログパイプをどう標準化するか * アカウントモデルがネットワーク(バイヤー宣言アカウント、agent-trusted モデル)対ファーストパーティプラットフォーム(account-id 名前空間、ウォールドガーデン)にとってどう機能するか * `adagents.json` の仕組み — バイヤーエージェントが彼のネットワークを発見するために必要で、彼が接続する AI プラットフォームからそれを理解する必要がある * ガバナンスポリシーがネットワークをどう流れるか — ブランドはコンテンツ標準を彼のネットワークにプッシュし、彼のネットワークはそれを各 AI プラットフォームにプッシュするか? * `optimization_goals` と `sync_event_sources` がネットワーク境界全体でどう機能するか — 彼のネットワークは複数のプラットフォームから配信データを集約する * AdCP がネットワークトポロジーを処理するか: バイヤーエージェント → アドネットワーク → AI プラットフォーム、それとも直接バイヤー対セラーを想定するか * AI プラットフォームがセッションをホストするが、ブランドが彼のネットワークを通じて紹介されたとき、Sponsored Intelligence がどう機能するか ### 誤解と盲点 * **AdCP がバイヤー対セラーのみだと想定。** 彼のビジネスは中間のネットワーク。プロトコルが中間者を考慮しないこと — 彼がバイヤーかセラーのふりをしなければならないこと — を心配している。 * **アカウントが単純だと考える。** 彼のネットワークはブランドに代わってアカウントを管理するエージェンシーに代わってアカウントを管理する。彼はこの複雑さに慣れているが、AdCP のアカウントモデルがマルチレベル委任をどう処理するか知らない。 * **カタログパイプを自分で構築する必要があると想定。** 彼は各 AI プラットフォームとカスタムカタログ同期統合を構築してきた。`sync_catalogs` が彼のビジネスの両側で機能しうる標準であることに気づいていない。 * **彼のネットワークの製品を基盤プラットフォームの製品と混同。** 彼は「AI アシスタント全体のスポンサー付きレスポンス」を単一製品として販売するが、各基盤 AI プラットフォームは独自の製品 ID、価格、フォーマットを持つ。AdCP で集約製品をどうモデル化するか理解する必要がある。 * **ガバナンスがパススルーだと考える。** 彼は単にバイヤーからプラットフォームにブランドセーフティルールを転送すると想定。プラットフォームに転送する前にネットワークがルーティングレイヤーで強制できる構造化オブジェクトとしてのガバナンスポリシーについて知らない。 ### 主要ゴール AdCP がネットワークトポロジー(バイヤー → ネットワーク → プラットフォーム)で機能するか判定。そうなら、彼のビジネスをどうモデル化するか理解する: 彼のネットワークはバイヤーにどう見えるか(セラーエージェントとして)、AI プラットフォームとどう相互作用するか(バイヤーまたはオペレーターとして)、カタログ、アカウント、ガバナンスがネットワークレイヤーをどう流れるか。 ### 彼が尋ねる主要な質問 1. 「AdCP は中間のネットワークをサポートするか、それとも厳密にバイヤー対セラーか?」 2. 「複数の AI プラットフォーム全体で集約するとき、アドネットワークの製品をどうモデル化するか?」 3. 「ネットワークにとってアカウントはどう機能するか? エージェンシーは私とアカウントを持ち、私は各 AI プラットフォームとアカウントを持つ。」 4. 「両側で `sync_catalogs` を使えるか — エージェンシーからカタログを受け入れ、AI プラットフォームに転送する?」 5. 「ガバナンスポリシーとコンテンツ標準がネットワークをどう流れるか?」 6. 「複数のプラットフォーム全体で集約しているとき、配信レポートはどう機能するか?」 7. 「私の `adagents.json` はどう見えるか? 私は自分のものでない複数のパブリッシャープロパティを代表する。」 8. 「私が中間者のとき SI はどう機能するか — ブランドは私のネットワークを通じて紹介されたが、セッションは AI プラットフォームで実行される?」 9. 「アカウントモデルは何か — 私は agent-trusted なので `require_operator_auth: false`?」 10. 「AdCP を使う他のネットワークはあるか、それとも私が最初か?」 ### 彼が訪れそうなページ 1. `/docs/sponsored-intelligence/overview` — コアページ、ネットワーク固有のガイダンスを探す 2. `/docs/building/implementation/seller-integration` — バイヤーエージェントにセラーとしてどう見えるか 3. `/docs/accounts/overview` — ネットワークアカウントモデル(agent-trusted、バイヤー宣言アカウント) 4. `/docs/building/integration/accounts-and-agents` — マルチレベルアカウント委任 5. `/docs/creative/catalogs` — パススルーのためのカタログ同期メカニクス 6. `/docs/governance/overview` — ガバナンスが中間者をどう流れるか 7. `/docs/media-buy/product-discovery/media-products` — 集約製品のモデル化 8. `/docs/media-buy/advanced-topics/accounts-and-security` — ネットワークのための `adagents.json` 9. `/docs/protocol/get_adcp_capabilities` — ネットワークが宣言するケイパビリティ 10. `/docs/sponsored-intelligence/overview` — ネットワーク中間者を通じた SI 11. `/docs/building/integration/mcp-guide` — MCP サーバーパターン(彼は詳しいが AdCP 固有のガイダンスが欲しい) 12. `/docs/reference/media-channel-taxonomy` — `sponsored_intelligence` チャネル定義 ### 成功基準 * 彼はネットワークがバイヤーにどう見えるか(バイヤー宣言アカウントを持つセラーエージェント)、AI プラットフォームとどう相互作用するか(各プラットフォームで account-id 名前空間を持つオペレーター)を理解している * 彼は集約製品 — 異なる価格の複数の基盤 AI プラットフォームにまたがる製品 — をモデル化できる * カタログがどう流れるか知っている: バイヤー → ネットワーク → AI プラットフォーム、両レグで `sync_catalogs` を使う * ガバナンスフローを理解している — ブランドからのコンテンツ標準はネットワークレイヤーで強制されプラットフォームに転送されうる * アカウントチェーンを説明できる: ブランド → エージェンシー → ネットワーク → AI プラットフォーム、AdCP が各関係をどうモデル化するか * 彼のビジネスの両側で AdCP 統合をアーキテクトするのに十分なものを持っている * ランディングから「アーキテクチャドキュメントを書ける」までの合計時間: 90 分未満 *** ## ペルソナ 3: Tomoko Hayashi — AI プラットフォーム広告インフラリード ### ロール 主要な AI アシスタントプラットフォームの広告チームのシニアプロダクトマネージャー。ChatGPT スケールを思い浮かべる: 数億のユーザー、強い商業意図シグナル、リーダーシップが広告支援ティアの構築を決定した。彼女は需要側アーキテクチャ — 広告主データと予算がどうプラットフォームに流れるか — に責任を持つ。 ### 背景 Tomoko のチームは既にサービングインフラを構築した — プラットフォームはスポンサー付きレスポンスをレンダーし、コンテキスト推奨を注入し、ブランド体験セッションを処理できる。LLM は正しい入力を持つとき、関連性のあるオンブランドコンテンツを生成するのが得意。今の難問は配管: 数百の広告主がどう製品カタログ、コンバージョンイベント、ブランドガイドライン、コンテンツ標準をスケールでプラットフォームに入れるか? そしてエージェンシーとその AI エージェントがどうプラットフォームの広告製品を発見しプログラマティックにバイを実行するか? 彼女は 2 つのアプローチを評価した: (1) プロプライエタリ API を構築し各バイヤーに 1 つずつ統合させる、または (2) オープン標準を採用し、任意の準拠バイヤーエージェントがプラグインできるようにする。彼女はオプション 2 のために AdCP を見ている。彼女はまた従来の SSP(prebid、GAM)に売り込まれ、懐疑的 — 入札リクエストモデルは会話コンテキストを持たないリモートの意思決定者に薄いシグナルを送り出す。彼女のプラットフォームはコンテキストを持つ。彼女はデータが彼女のところに来ることを望む。 ### 彼女が既に知っていること * 彼女のプラットフォームの LLM ケイパビリティ — 正しいブランドデータとコンテキストが与えられたときに何を生成できるか * 広告サービングが彼女のプラットフォーム内部でどう機能するか(スポンサー付きレスポンスランキング、コンテキストマッチング、セッション管理) * スケール問題: プロプライエタリ API を通じて広告主を 1 つずつオンボードすることがスケールしない * 従来のプログラマティック(入札リクエスト外、広告返却)が、リモート入札者が会話コンテキストを持たないため相性が悪いこと * 基本的なアドテック: CPM、CPC、エンゲージメントあたりコスト、フィルレート、フリークエンシーキャッピング * 彼女のプラットフォームが良い広告を生成するために広告主製品データを必要とすること — 初期テストのため手動でスクレイピングしてきた * OAuth、API 設計、webhook パターン — 彼女はプロトコル仕様を評価するのに十分技術的 ### 彼女が知らないこと * AdCP が彼女のプラットフォームのユースケース向けに設計された特定の `sponsored_intelligence` チャネルを持つこと * `sync_catalogs` が広告主製品データをスケールで入れる標準パイプとしてどう機能するか * `sync_event_sources` が広告主にコンバージョンシグナルをプッシュさせ、プラットフォームが実際の結果で最適化できるようにする方法 * ガバナンスポリシーがブランドにコンテンツ標準をプッシュさせる方法 — プラットフォームが生成時に強制する適合性ルール * `brand.json` が生成クリエイティブ品質を改善するブランドアイデンティティ(ボイス、ビジュアルガイドライン、ポジショニング)を提供する方法 * メディアバイ上の `optimization_goals` がプラットフォームに各キャンペーンの成功がどう見えるか伝えること * MCP が何か、REST API の構築とどう異なるか(彼女は REST を構築すると想定してきた) * バイヤーエージェントが彼女のプラットフォームを発見するための `adagents.json` の仕組み * アカウントがどう機能するか — 広告主ごとに OAuth を要求すべきか、バイヤーエージェントにブランドを宣言させるか * Sponsored Intelligence が単なる「派手なスポンサー付きレスポンス」ではなく、マルチターンのブランド体験のための別のプロトコルであること ### 誤解と盲点 * **選択がプロプライエタリ API 対 SSP だと考える。** AdCP がまさに彼女のユースケース向けに設計されたオープン標準 — 入札リクエストを送り出すのではなくデータを受け取る — という第 3 の選択肢であることをまだ見ていない。 * **REST API を構築する必要があると想定。** MCP が AI エージェントが既にネイティブに話すトランスポートとして存在することを知らない。彼女のプラットフォームのバイヤーエージェントは LLM — 既に MCP ツールを呼ぶ方法を知っている。 * **カタログ問題を過小評価。** 彼女のチームは初期広告主から製品フィードを手動でオンボードしてきた。これがスケールしないことは知っているが、`sync_catalogs` がそれを標準として解決することに気づいていない。 * **ブランドセーフティをブロックリストと考える。** ブランドに適合性ルールをプラットフォームにプッシュさせるガバナンスポリシー — LLM がクリエイティブ生成中に強制するルール、事後フィルタリングとしてでなく — について知らない。 * **スポンサー付きレスポンスを SI と混同。** ブランド体験ハンドオフが単によりリッチなスポンサー付きレスポンスだと考える。SI がブランド自身のエージェントが会話を引き継ぐ別のセッションライフサイクルであることを理解していない。 * **コンバージョン追跡が自身のピクセル / SDK を要求すると想定。** `sync_event_sources` が広告主に既存のコンバージョンデータをプッシュさせ、プラットフォームが自身の測定スタックを構築せずに最適化できるようにすることを知らない。 * **他の側からの「なぜプログラマティックをやらないのか?」という質問について考えていない。** 彼女はなぜ AdCP が SSP との統合より彼女のプラットフォームにとって良いかリーダーシップに明確に述べる必要がある — 答えは、プログラマティックが薄いシグナルを送り出すのに対し AdCP がリッチなデータを持ち込み、彼女の LLM がそのデータを使って任意のリモート入札者よりも良い広告決定を下せること。 ### 主要ゴール AdCP を彼女のプラットフォームの需要側配管の標準インターフェースとして採用するか決定する。そうなら、何を構築する必要があるか(MCP サーバー、アカウントモデル、カタログ取り込み、製品スキーマ)と、それが代替(プロプライエタリ REST API または SSP 統合)とどう比較されるか理解する。彼女のエンジニアリングチームが実行できる技術設計ドキュメントを書く。 ### 彼女が尋ねる主要な質問 1. 「AdCP はどう広告主製品データを私のプラットフォームに入れるか? カタログ同期の標準はあるか?」 2. 「広告主はコンバージョンイベントをプッシュして、プロキシメトリックの代わりに実際の結果で最適化できるか?」 3. 「ブランドセーフティとコンテンツ標準はどう機能するか? ブランドは私の LLM がクリエイティブ生成中に強制する適合性ルールをプッシュできるか?」 4. 「なぜ自分の API を構築する代わりにオープン標準を採用するのか? 何が得られるか?」 5. 「MCP とは何か、なぜ REST API の代わりに MCP サーバーを構築するのか?」 6. 「バイヤーエージェントはどう私のプラットフォームとその広告製品を発見するか?」 7. 「アカウントモデルは何か? 広告主ごとに OAuth が必要か、もっと単純なパスがあるか?」 8. 「スポンサー付きレスポンスと Sponsored Intelligence の違いは何か?」 9. 「なぜこれが従来の SSP との統合より良いか? リーダーシップにどう説明するか?」 10. 「他に誰がこれをやっているか? リファレンス実装はあるか?」 ### 彼女が訪れそうなページ 1. `/docs/intro` — 出発点、AI 固有のフレーミングを探す 2. `/docs/sponsored-intelligence/overview` — 彼女のユースケースのコアページ — 逆転したデータフロー論、カタログ同期、ガバナンス、製品モデル化を見つけることを期待 3. `/docs/creative/catalogs` — カタログ同期の深掘り — これが彼女の最大の運用上の痛点 4. `/docs/building/implementation/seller-integration` — セラーエージェントとして構築する必要があるもの 5. `/docs/governance/overview` — コンテンツ標準がプラットフォームがクエリ / 受信する「オラクル」としてどう機能するか 6. `/docs/media-buy/media-buys/optimization-reporting` — 最適化ゴールとコンバージョンイベントがどう機能するか 7. `/docs/accounts/overview` — アカウントモデルの理解(ウォールドガーデン対 agent-trusted) 8. `/docs/building/integration/mcp-guide` — なぜ REST の代わりに MCP か、MCP サーバーはどう見えるか 9. `/docs/sponsored-intelligence/overview` — SI セッションライフサイクル対スポンサー付きレスポンスの理解 10. `/docs/media-buy/product-discovery/media-products` — 在庫を製品としてどうモデル化するか 11. `/docs/protocol/get_adcp_capabilities` — 宣言するケイパビリティ 12. `/docs/building/understanding/adcp-vs-openrtb` — リーダーシップとの「なぜ SSP でないのか?」会話のための弾薬 ### 成功基準 * 彼女はなぜ AdCP が SSP 統合より彼女のプラットフォームにとって良いかリーダーシップに明確に述べられる — 逆転したデータフロー論: 「私たちは会話コンテキストを持つ。AdCP はブランドデータ、コンバージョンシグナル、適合性ルールを持ち込み、私たちの LLM がローカルで素晴らしい広告決定を下せる。SSP は私たちのコンテキストを持たないリモートシステムに薄い入札リクエストを送らせるだろう。」 * 彼女はデータパイプを理解している: 製品データのための `sync_catalogs`、コンバージョンシグナルのための `sync_event_sources`、コンテンツ標準のためのガバナンスポリシー、ブランドアイデンティティのための `brand.json`、成功定義のための `optimization_goals` * 実装するアカウントモデルとその理由を記述できる(ファーストパーティプラットフォームなので OAuth を持つウォールドガーデン) * スポンサー付きレスポンス(製品レベル、カタログ駆動)と SI(セッションレベル、ブランドエージェントハンドオフ)の実装の違いを知っている * チームが構築する MCP サーバーを仕様化できる: どのタスクを実装するか、どのケイパビリティを宣言するか、カタログ取り込みが既存インフラにどうマップするか * 明確な比較を持つ: AdCP(オープン標準、データが流れ込む、任意のバイヤーエージェントがプラグイン)対 プロプライエタリ API(バイヤーごとにカスタム、同じデータフローだがエコシステムなし)対 SSP(間違った方向 — シグナルを送り出す) * ランディングから「技術設計ドキュメントを書ける」までの合計時間: 90 分未満 *** ## ペルソナ 4: Daniela Reyes — エージェンシートレーディングデスク幹部 ### ロール 中規模独立エージェンシーのプログラマティック VP。彼女のチームは 30 以上のブランドにわたって年間 2 億ドル以上のデジタル支出を管理。CEO に報告し、エージェンシーの AI トランスフォーメーション委員会に属する。 ### 背景 Daniela はトレーディングデスクを通じて昇進した — この独立ショップに加わる前にホールディングカンパニーでプログラマティック運用を運営。彼女は DSP、SSP、OpenRTB、prebid を隅々まで知っている。彼女のチームは 15 人のトレーダーと、カスタム入札アルゴリズムとレポートダッシュボードを保守する 3 人のエンジニア。 彼女はクライアントから、そして業界カンファレンスで「AI メディア」について聞いてきた。彼女の最大のクライアント 2 社(CPG ブランドと金融サービス会社)が、チームに「ChatGPT と Perplexity で広告を買う方法を見つける」よう頼んだ。彼女はそれらのプラットフォームと直接取引をセットアップしようとしたが、それぞれが異なる API、異なるクリエイティブ仕様、異なるレポートフォーマットを要求する。彼女はチームが従来のプログラマティック全体で買うのと同じ方法で AI サーフェス全体で買う標準的な方法を探している。 彼女はエンジニアではない — コードを書かない。しかし技術を評価し、buy/build 決定を下し、実装すべきことをエンジニアリングチームにブリーフする。概念レベルでドキュメントを読み、形状のためにスキーマをざっと見て、ワークフロー、経済性、競争優位に焦点を当てる。 ### 彼女が既に知っていること * プログラマティック広告を深く: DSP、SSP、アドエクスチェンジ、OpenRTB 入札 / レスポンスフロー * キャンペーン管理: フライト、予算、ペーシング、最適化、フリークエンシーキャッピング * クリエイティブトラフィッキング: タグ管理、VAST/VPAID、DCO * 測定: ビューアビリティ、ブランドセーフティベンダー(IAS、DV)、アトリビューション、MMM * エージェンシー経済性: マージン、マネージドサービス対セルフサーブ、プラットフォームフィー * AI プラットフォームが新しいメディアチャネルで、クライアントがそれを求めていること * 現在のアプローチ(プラットフォームごとの直接取引)がスケールしないこと ### 彼女が知らないこと * AI メディア購入の標準として AdCP が存在すること * 「逆転したデータフロー」が何を意味し、なぜ彼女のエージェンシーにとって重要か * チームが単一のバイヤーエージェントを使って複数の AI プラットフォーム全体で買えること * カタログがクリエイティブタグをどう置き換えるか — アセットをトラフィックする代わりに、製品データをプッシュする * AI プラットフォームが彼女のブランドのデータからクリエイティブを生成すること * アカウントがプラットフォーム全体でどう機能するか — どこでも別々のログインが必要か? * AI メディアでガバナンスがどう見えるか — IAS と DV は関連するか、それとも違うか? * 最適化ゴールが彼女が慣れている DSP 最適化アルゴリズムをどう置き換えるか * MCP が何か、なぜ重要か(彼女は API とダッシュボードの観点で考える) * Sponsored Intelligence がより深いブランドエンゲージメントフォーマットとして存在すること * 価格設定がどう機能するか — RTB のようにオークションベースか、固定か、それとも他の何か? ### 誤解と盲点 * **すべてをプログラマティックにマップ。** 彼女は DSP と SSP のレンズを通じて AdCP を理解しようとする。「じゃあバイヤーエージェントは DSP みたいなもの?」「adagents.json は ads.txt みたいなもの?」これらのアナロジーの一部は助け、一部は誤解を招く。 * **UI を期待。** 彼女は DSP ダッシュボードに慣れている。バイヤーエージェントがキャンペーン管理 UI なしにすべてをプログラマティックに行うという考えは馴染みがない。ダッシュボードがどこにあるか知りたがる。 * **クリエイティブが自分の仕事だと考える。** 従来のプログラマティックでは、エージェンシーがクリエイティブを構築しトラフィックする。AI メディアでは、プラットフォームがブランドデータからクリエイティブを生成する。これは大きなメンタルシフト。 * **ブランドセーフティが同じベンダーを意味すると想定。** 彼女は IAS/DV 統合を探す。ガバナンスがサードパーティ検証としてボルト留めされるのではなくプロトコルに組み込まれている(生成時に強制されるコンテンツ標準)という考えは新しい。 * **カタログワークフローを過小評価。** 彼女は製品フィードをリテールメディアのものと考える。すべての AI メディア購入がカタログとブランドデータをプラットフォームにプッシュすることから始まることに気づいていない。 * **AI メディアを「単なる別のチャネル」と考える。** 既存のプログラマティックスタックに新しいラインアイテムとして追加したがる。パラダイムシフト — 入札リクエストを送り出すのではなくデータが流れ込む — は、単にチャネルを追加するのではなくワークフローの再考を要求する。 ### 主要ゴール AdCP が彼女のエージェンシーが AI メディア購入のために採用する正しい標準か理解する。CEO のためのビジネスケースとエンジニアリングチームのための技術ブリーフを構築する。競争優位を見極める: 他のエージェンシーより先にこれを採用したら、勝つか? ### 彼女が尋ねる主要な質問 1. 「AI プラットフォームで広告を買うことは DSP で買うこととどう違うか?」 2. 「ChatGPT、Perplexity、他の AI プラットフォーム全体で買う標準的な方法はあるか?」 3. 「キャンペーンワークフローはどう見えるか? 私のチームはどこに収まるか?」 4. 「まだクリエイティブを構築する必要があるか、それともプラットフォームが処理するか?」 5. 「ブランドセーフティはどう機能するか? IAS/DV を使えるか?」 6. 「価格モデルは何か? オークションベースか?」 7. 「これをどうレポートするか? 既存のダッシュボードに入れられるか?」 8. 「エンジニアリングチームに何を構築させる必要があるか?」 9. 「複数のプラットフォーム全体でアカウントと課金はどう機能するか?」 10. 「他に誰かがこれをやっているか? 競争ランドスケープは何か?」 ### 彼女が訪れそうなページ 1. `/` — ホームページ、「これは何で、なぜ気にすべきか」を探す 2. `/docs/intro` — オリエンテーション、明確なバリュープロップを望む 3. `/docs/building/understanding/adcp-vs-openrtb` — 彼女の「これはどう違うか」の質問に直接答える 4. `/docs/sponsored-intelligence/overview` — 彼女のユースケースのコアガイド(彼女はバイヤー) 5. `/docs/sponsored-intelligence/workflow` — コードを書かなくても、ワークフローがどう見えるか見たい 6. `/docs/building/implementation/seller-integration` — 他の側を理解するためにこれを読むかもしれない 7. `/docs/governance/overview` — この世界でブランドセーフティがどう機能するか 8. `/docs/creative/catalogs` — カタログワークフローの理解 9. `/docs/accounts/overview` — マルチプラットフォーム課金がどう機能するか 10. `/docs/reference/media-channel-taxonomy` — チャネルリストで `sponsored_intelligence` を探す ### 成功基準 * 彼女は CEO に、なぜ AI メディアが新しい DSP を追加することと違うか、なぜ標準の採用が重要か説明できる * エンジニアリングチームに何を構築するかブリーフできる: 「AdCP を話すバイヤーエージェントが必要。ワークフローはこう: カタログをプッシュ、製品を発見、メディアバイを作成、配信レポートを引き出す。」 * クリエイティブパラダイムシフトを理解している: エージェンシーがブランドデータとカタログを提供し、プラットフォームがクリエイティブを生成 * ガバナンスモデルを知っている: コンテンツ標準はプロトコルレベル、生成時強制で、サードパーティのボルト留めではない * 3 人のエンジニアリングチームのためのエンジニアリング投資とタイムラインを見積もれる * 競争優位を見る: 動作するバイヤーエージェントを持つ最初のエージェンシーは、直接取引をするエージェンシーより速く AI メディアのクライアント需要に応えられる * ランディングから「これを CEO にプレゼンできる」までの合計時間: 60 分未満 *** ## ペルソナ 5: James Okafor — ブランドメディアトランスフォーメーションリーダー ### ロール Fortune 500 の消費者エレクトロニクスブランドのグローバルメディア責任者。CMO に報告。3 つのエージェンシーパートナーと成長するインハウスチームにわたって年間 5 億ドルのメディア予算を管理。ブランドの「未来のメディア」イニシアチブの議長を務める。 ### 背景 James は 15 年間ブランド側メディアに携わり、メディアプランナーから機能全体を運営するまで移動。彼はすべての主要なシフトをナビゲートした: プログラマティック、ソーシャル、リテールメディア、CTV。彼はエージェンシー関係をよく知る — エージェンシーに戦略と KPI をブリーフし、彼らがキャンペーンを実行しレポートする。彼のインハウスチームはリテールメディア(Amazon、Walmart)を直接処理し、より多くのプログラマティックをインハウスに持ち込むことを実験している。 彼の CMO は AI メディアを次の優先事項としてフラグした。消費者はますます AI アシスタントを使って製品を調査・購入している。彼のブランドの製品が AI 生成レスポンスに現れている — 時に正確に、時にそうでなく。彼は「AI が正しく言及することを願う」から「AI 体験で正確なブランドメッセージングで積極的に消費者に到達する」に移りたい。 彼は技術的でない。メディア戦略、ブランドエクイティ、消費者ジャーニー、ROAS の観点で考える。ビジネス成果、エージェンシー関係、組織の準備状況のレンズを通じて技術を評価する。 ### 彼が既に知っていること * スケールでのメディア戦略と計画: リーチ、フリークエンシー、GRP、クロスチャネル配分 * エージェンシー管理: ブリーフィング、交渉、パフォーマンス評価、フィー構造 * リテールメディア: Amazon Ads、Walmart Connect、Instacart Ads の学習曲線を経験 * ビジネスリスクとしてのブランドセーフティ: ブランドセーフティインシデントを経験し、コストを知る * 彼のカテゴリーで消費者が購入を調査するために AI アシスタントを使っていること * 競合が AI 広告を実験し始めていること * インハウス対エージェンシーのダイナミクス: 一部のケイパビリティは所有した方が良く、他は外注した方が良い ### 彼が知らないこと * AdCP が何か、AI 広告の標準が存在すること * AI 広告が実際にどう機能するか — デモは見たが、メカニクスを理解していない * クリエイティブが AI プラットフォームによって彼のブランドのデータ(カタログ、ブランドガイドライン)から生成されること * 彼のブランドのコンテンツ標準を AI プラットフォームにプッシュして、ブランドがどう現れるか制御できること * 「カタログ品質が広告品質を駆動する」こと — 彼の製品データがクリエイティブ入力 * AI メディアでガバナンスがどう異なって機能するか(生成時強制対事後検証) * Sponsored Intelligence が彼のブランドに消費者とマルチターンの会話をさせること * AI プラットフォームで価格設定がどう機能するか — プログラマティックオークションと同じではない * AI メディアキャンペーンを実行するためにエージェンシーが彼から何を必要とするか(カタログ、brand.json、コンテンツ標準) * AI メディアの組織モデルが、従来のプログラマティック(クリエイティブ + ターゲティング)よりリテールメディア(データ + コンテンツ)に近く見えること ### 誤解と盲点 * **AI 広告が AI アプリのバナー広告だと考える。** ChatGPT のレスポンスの隣のディスプレイ広告を想像する。AI がブランドデータから広告を生成すること — 「広告」が AI 体験にネイティブに見え感じるスポンサー付きレスポンスであること — に気づいていない。 * **エージェンシーが既にこれをやる方法を知っていると想定。** そうでない。AI メディアは十分新しく、エージェンシーもそれを見極めている。彼は彼らの提案を評価し正しい方向に押すのに十分理解する必要がある。 * **ブランドセーフティが同じことを意味すると考える。** 従来のメディアでは、ブランドセーフティ = 悪いコンテンツ隣接を避けること。AI メディアでは、ブランドセーフティ = AI がブランドについてどう話すか制御すること。異なる問題、異なる解決策。 * **データ要件を過小評価。** 彼のチームはリテールメディアのための製品フィードとクリエイティブアセットのための DAM を管理する。AI メディアがさらにリッチなブランドデータ — 製品カタログ、ブランドボイスガイドライン、コンテンツ標準 — を要求し、このデータの品質が直接広告品質を決定することに気づいていない。 * **エージェンシー問題だと想定。** 彼はエージェンシーをブリーフして見極めさせたがる。しかし AI メディアはエージェンシーが生成できないブランド側入力(カタログ、ブランドアイデンティティ、コンテンツ標準)を要求する。彼はデータパイプラインを所有する必要がある。 * **Sponsored Intelligence が派手なリターゲティングだと考える。** SI が新しいエンゲージメントモデルであることを理解する必要がある — 消費者が AI アシスタント内で彼のブランドと会話する。 ### 主要ゴール AI 広告が何か、彼のブランドが投資すべきか、どんな組織変更が必要か理解する。CMO のためのビジネスケースを構築する。エージェンシーに何を変えるかブリーフする。インハウスチームが何を所有し何を委任すべきか識別する。 ### 彼が尋ねる主要な質問 1. 「AI 広告とは何か、今日私たちがやっていることとどう違うか?」 2. 「消費者は AI アシスタントで広告をどう体験するか?」 3. 「AI がブランドについてどう話すか制御できるか?」 4. 「私のチームはどんなデータを提供する必要があるか?」 5. 「AI がクリエイティブを生成するとき、ブランドセーフティはどう機能するか?」 6. 「エージェンシーに何をするよう頼むべきか?」 7. 「これをどう測定するか? ROAS を得られるか?」 8. 「プログラマティックと比べて価格設定はどう見えるか?」 9. 「大きな投資なしにこれをテストする方法はあるか?」 10. 「競合は何をしているか?」 ### 彼が訪れそうなページ 1. `/` — ホームページ、全体像を探す 2. `/docs/intro` — 「私が CMO だと思って説明して」 3. `/docs/sponsored-intelligence/overview` — コアガイドだが、技術的すぎると離脱するかもしれない 4. `/docs/building/understanding/adcp-vs-openrtb` — 彼が知っているものとの比較が欲しい 5. `/docs/creative/catalogs` — チームが提供する必要があるデータの理解 6. `/docs/governance/overview` — ブランドセーフティとコンテンツ標準(彼にとって高優先) 7. `/docs/governance/content-standards/overview` — ブランドコントロールの深掘り 8. `/docs/sponsored-intelligence/overview` — 会話型エンゲージメントモデルの理解 9. `/docs/creative/brand-json` — 作成する必要があるブランドアイデンティティデータ 10. `/docs/learning/basics/intro` — 構造化されたコンテンツを学ぶため認定を試すかもしれない ### 成功基準 * 彼は CMO に AI 広告が何か、なぜプログラマティックと違うか説明できる — 単なる「AI アプリの広告」ではなく「AI が私たちのブランドデータから広告を生成する」 * 組織的含意を理解している: チームがリテールメディア製品フィードを所有するのと同じ方法でブランドデータ品質(カタログ、ブランドアイデンティティ、コンテンツ標準)を所有する必要がある * エージェンシーをブリーフできる: 「AdCP を話すバイヤーエージェントを構築または採用してほしい。私たちが提供するもの: 製品カタログ、brand.json、コンテンツ標準。私たちが期待するもの: 配信レポートを持つ主要 AI プラットフォーム全体の AI メディアキャンペーン。」 * ガバナンスストーリーを知っている: 「私たちはコンテンツ標準を AI プラットフォームにプッシュする。彼らは生成時にそれを強制する。AI が私たちのブランドについて正しいことを言うことを願うのはもう終わり。」 * Sponsored Intelligence を単なる広告ではなく新しい消費者エンゲージメントチャネルとして見る * 競争リスクを明確に述べられる: 「今 AI メディアデータ品質に投資しなければ、競合はブランドデータがよりリッチなので AI 体験でより高性能な広告を持つだろう」 * 段階的計画を持つ: (1) ブランドデータの準備状況を監査、(2) 1 つの AI プラットフォームで 1 つのエージェンシーとパイロット、(3) AdCP 標準を通じてスケール * ランディングから「これを CMO にプレゼンできる」までの合計時間: 45 分未満 *** ## ペルソナ 6: Priya Sharma — SMB e コマース創業者 ### ロール DTC スキンケアブランドの創業者兼唯一のオペレーター。Shopify で稼働。年間 200 万ドルの収益。時折フリーランスの助けを借りてマーケティングを自分で処理。40 SKU の製品カタログを持つ。 ### 背景 Priya は Instagram と Google Shopping でブランドを構築した。彼女は自身の Meta Ads、Google Ads を管理し、最近 Amazon を始めた。各プラットフォームは独自の広告マネージャー、独自のクリエイティブ要件、独自のピクセル / コンバージョンセットアップを持つ。3 プラットフォームでは管理可能だが、顧客から ChatGPT と Perplexity の推奨を通じて製品を見つけたと聞いている — そして彼女はそこにプレゼンスがない。広告なし、ブランドプロフィールなし、製品がどう記述されるかのコントロールなし。 彼女は ChatGPT での広告を調べ、直接販売関係を要求することを見つけた。Perplexity は異なるプログラムを持つ。すべての AI プラットフォームが異なる。彼女はエージェンシーを持たず、エンジニアを持たず、さらに 5 つのプラットフォームを個別にセットアップし管理する時間もない。 彼女は技術的に有能 — Shopify アプリを構成し、Meta ピクセルをセットアップし、Zapier を使える — が、コードは書かない。「私のストアをこのプラットフォームに接続する」「予算を設定して実行させる」の観点で考える。 ### 彼女が既に知っていること * Meta、Google、Amazon で広告を実行する方法 — キャンペーンセットアップ、予算、ターゲティング、クリエイティブ * Shopify がストアを広告プラットフォームに接続するアプリ統合を持つこと * 製品フィード管理 — Google Merchant Center フィードを保守 * 基本的な測定: ROAS、CPA、アトリビューションウィンドウ * 彼女の製品が AI アシスタントレスポンスに、時に間違った価格や廃止された商品で現れていること * AI プラットフォームがブランドをどう表現するか現在制御・改善できないこと ### 彼女が知らないこと * AdCP が存在すること、「エージェント型広告」が何を意味するか * 複数の AI プラットフォームに一度に接続する標準的な方法があること * 既存の Shopify 製品フィードが基本的に AI プラットフォームにプッシュできるカタログであること * AI プラットフォームがアップロードするクリエイティブからではなく製品データから広告を生成すること * brand.json がすべての AI プラットフォームにわたってブランドアイデンティティを確立できること * コンテンツ標準が AI プラットフォームが承認していない製品についてのクレームを作ることを防げること * AdCP を直接実装するのではなく、パートナー(アドネットワーク、Shopify アプリ)を通じて機能する可能性が高いこと * MCP や A2A が何か — 彼女はプロトコルではなくアプリと統合の観点で考える ### 誤解と盲点 * **AI プラットフォームでの広告がダッシュボードを意味すると考える。** Meta Ads Manager のようなもの — クリエイティブをアップロード、ターゲティングを設定、予算を設定、ローンチ — を期待する。AI プラットフォームが彼女のデータから広告を生成するという考えは馴染みがない。 * **プラットフォームごとにやる必要があると想定。** Meta、Google、Amazon に別々のアカウントを持つのと同様に、ChatGPT、Perplexity、Claude、Gemini などに別々のアカウントが必要だと想定。 * **製品フィードが既に必要なものの大半であることに気づいていない。** Google Merchant Center フィードはタイトル、説明、価格、画像、在庫状況を持つ。それが製品カタログ。標準パイプを通じて AI プラットフォームにプッシュするだけでよい。 * **これを支払えないと考える。** 「AI 広告」をエンタープライズ予算と関連づける。AI アドネットワークが複数プラットフォーム全体で月 500 ドルで始められることを知らない。 * **ブランドコントロール問題を過小評価。** 彼女の製品は既に AI 会話で議論されている — 時に不正確に。これを機会に接続していない: 正確な製品データ AND ブランドガイドラインをプッシュすれば、AI は推測する代わりに正しい情報を持つ。 ### 主要ゴール エージェンシーやエンジニアを雇わずに AI プラットフォームで広告できるか見極める。何を提供する必要があるか(製品データ、ブランド情報)、誰が助けるか(Shopify アプリ、アドネットワーク、パートナー)を理解する。小さく始めて機能するか見る。 ### 彼女が尋ねる主要な質問 1. 「ChatGPT と Perplexity で広告できるか? どうやって?」 2. 「エージェンシーが必要か、自分でできるか?」 3. 「単に Shopify ストアを接続できるか?」 4. 「始めるのにいくらかかるか?」 5. 「新しいクリエイティブを作る必要があるか、AI がやるか?」 6. 「AI が製品を正しく — 価格、説明、在庫状況 — 得ることをどう確認するか?」 7. 「AI がブランドについて何を言うか制御できるか?」 8. 「機能しているかどう分かるか? ROAS を見られるか?」 9. 「これのための Shopify アプリはあるか?」 10. 「これと単に Google Ads をやることの違いは何か?」 ### 彼女が訪れそうなページ 1. `/` — ホームページ、平易な言葉の説明を探す 2. `/docs/intro` — 技術的すぎると離脱するかもしれない 3. `/docs/sponsored-intelligence/overview` — バイヤーセクションが彼女を捉えたら読む 4. `/docs/creative/catalogs` — Shopify フィードが機能するか知りたい 5. `/docs/brand-protocol/brand-json` — ブランド表現を制御したい 6. `/docs/governance/overview` — AI が製品について間違ったクレームを作るのを防ぎたい 7. `/docs/learning/overview` — ランドスケープを理解するため基礎を試すかもしれない ### 成功基準 * 彼女は既存の製品フィードが必要な主要な材料であることを理解している * プロトコル配管を処理するパートナー(アドネットワーク、Shopify アプリ)を通じて機能することを知っている * 価値を見る: 1 つの統合(パートナーを通じて)ですべての AI プラットフォームに到達 対 各々を個別にセットアップ * ブランドコントロールストーリーを理解している: AI プラットフォームがブランドを正しく表現するよう正確なデータとガイドラインをプッシュする * プロトコル用語に怖気づかない — コンテンツが彼女のいる場所で彼女に会う * 明確な次のステップを持つ: Shopify で機能する AdCP 接続パートナーを見つける * ランディングから「次に何をすべきか分かった」までの合計時間: 20 分未満 *** ## ペルソナ 7: Lisa Tran — ビルドプロジェクトをする非コーダー ### ロール 中堅リテールブランドのデジタル VP。ブランドのデジタルメディア戦略とベンダー関係を管理。AI コーディングアシスタントに慣れている(内部ツーリングプロトタイプに毎日 Cursor を使う)が、TypeScript や JavaScript を手で書いたことは決してない。 ### 背景 Lisa は C トラック認定モジュール(C1-C3)を完了し、C4 ビルドプロジェクトを始めている。彼女は Cursor を使って小さな内部ツール — Slack ボット、スプレッドシート自動化、シンプルなダッシュボード — を、望むものを記述し出力を反復することで構築した。彼女はスタックトレースを読んだことがなく、`npm` が何か知らず、「コードを実行する」を「Cursor で再生を押すと機能する」と考える。 彼女は C1-C3 に合格した、材料が概念的だから — 購入ワークフロー、製品ディスカバリー、キャンペーン戦略。C4 は彼女に動作するバイヤーエージェントの構築を求める。彼女はエージェントが何をすべきか(製品を発見、メディアバイを作成、クリエイティブを同期)を理解するが、「記述できる」と「動作する」のギャップが彼女が苦労する場所。 ### 彼女が既に知っていること * C1-C3 からの AdCP 購入概念: 製品ディスカバリー、メディアバイ、クリエイティブ同期、ターゲティング、最適化ゴール * AI コーディングアシスタントに望むものを平易な言葉で記述する方法 * AI と反復するワークフロー: 記述 → 生成 → テスト → 再び記述 * 彼女のブランドのメディア購入ニーズ — シナリオの実際のコンテキストを持つ * `@cptestagent` がテストするサンドボックスセラーであること ### 彼女が知らないこと * 「実行中の MCP サーバー」が何を意味するか、実行中であることをどう検証するか * エラーメッセージの読み方 — `TypeError: Cannot read properties of undefined` を見て何をすべきか分からない * コードが実行される前に `npm install` や `pip install` が必要かもしれないこと * 「JSON レスポンスを貼り戻す」方法 — JSON が他のターミナル出力とどう違うか知らないかもしれない * AI コーディングアシスタントがプロンプトで指定された adcp クライアントライブラリを必要とすること * 検証フェーズのためにローカルエージェントを Addie に接続する方法 ### 彼女が詰まる場所 * **最初のビルド試行が失敗。** AI コーディングアシスタントが実行されないコードを生成する。ターミナルにエラーが見えるが、どの部分がエラー対通常の出力か分からない。 * **反復方法が分からない。** 彼女はシンプルなツールのため Cursor で反復する方法を知るが、依存関係を持つマルチファイル TypeScript プロジェクトは単一ファイル Slack ボットとは異なる。 * **仕様の問題をコードの問題と混同。** エージェントがエラーケースを処理しない場合、それは彼女の仕様が不完全だったからか、AI コーディングアシスタントがミスをしたからか? 彼女には分からない。 * **検証フェーズが混乱を招く。** 「この MCP ツール呼び出しをローカルエージェントに対して実行」 — 彼女はそれが機械的に何を意味するか分からない。 ### 彼女が Sage から必要とするもの * **フェーズ 1(仕様)**: ここでうまくやる。AdCP 用語で購入ワークフローを記述できる。Sage は彼女の仕様がコーディングアシスタントに十分完全であることを確認すべき。 * **フェーズ 2(ビルド)**: ビルドが失敗したとき、Sage にデバッグループを教えてもらう必要がある — 彼女のためにデバッグするのではなく。「そのエラーメッセージをコピーし、Cursor に貼り戻し、『実行しようとしたときこのエラーが出た』と言う。」再び失敗したら、「Cursor に何を構築しようとしているか、エラーを修正すべきと伝える。」2-3 サイクルが正常で、失敗のサインではないことを学ぶ必要がある。 * **フェーズ 3(検証)**: 明確で機械的な指示が必要。「エージェントに対して get\_products を実行」ではなく、ツールを正確にどう呼び出すか、どの出力をコピーバックするかのガイダンス。 * **フェーズ 4(説明)**: ここでうまくやる — 概念を理解している。 * **フェーズ 5(拡張)**: フェーズ 2 と同じパターン — 変更を仕様化し、コーディングアシスタントと反復し、結果を持ち帰る。 ### 成功基準 * 彼女は誰にもコードを書いてもらわずにビルドプロジェクトを完了する * デバッグループを学ぶ: エラー → アシスタントに貼り付け → 反復 * どの機械的ステップでも 5 分以上ブロックされない * 体験がエンジニアリングでの失敗ではなく、コーチングのように感じる * コードを書かない同僚に認定を推薦する # バイヤー / ブランドトラック Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/tracks/buyer AdCP バイヤートラック(C1〜C4): マルチエージェントのバイイングオーケストレーション、ブランドアイデンティティプロトコル、クリエイティブワークフロー、スポンサードインテリジェンス、バイヤーエージェントのビルドプロジェクト。 # バイヤー / ブランドトラック(C1〜C4) **メンバー限定** — Basics クレデンシャル(A1〜A3)が必要。4つのモジュール、合計約105分。 このトラックは AdCP のデマンドサイドを教える。バイヤーエージェントが複数のセラーにわたってオーケストレーションする方法、ブランドアイデンティティとコンプライアンスプロトコルの仕組み、クリエイティブワークフローとスポンサードインテリジェンスの連携を学ぶ。トラックは動くバイヤーエージェントを作成するビルドプロジェクトで締めくくられます。 このトラック(プラス A1〜A3)を完了すると **AdCP practitioner** クレデンシャルを取得できます。 *** ## C1: マルチエージェントバイイングとメディアプランニング **約20分** | 前提条件: A3 バイヤーエージェントが複数のセールスエージェントに同時にオーケストレーションする方法: 探索、ポートフォリオ割り当て、提案、パブリッシャー間のリーチ測定。 ### 読み物リスト メディアバイプロトコル: エージェントが広告キャンペーンを探索、交渉、実行する方法。 複数のセラーからのプロダクト探索 — あらゆるバイイングワークフローの最初のステップ。 キャンペーン作成: マニュアルモード、提案モード、バリデーション、承認ライフサイクル。 複数のセラーにわたって調整するエージェントのアーキテクチャパターン。 CPM、フラットレート、パフォーマンスベース — セラー間の価格の仕組み。 ターゲティングオプション、オーディエンスオーバーレイ、ジオターゲティング。 ### 主要コンセプト * **マルチエージェントバイイング** — 複数のセラーに並行してクエリし、比較、割り当て、実行 * **オーケストレーションパターン** — 探索、評価、割り当て、実行、監視 * **オーディエンスターゲティング** — カスタムセグメントに `sync_audiences` * **アカウントセットアップ** — バイイング前に請求を確立するために `sync_accounts` 「認定モジュール C1 を始めたい」 *** ## C2: ブランドアイデンティティ、コンプライアンス、セーフティ **約20分** | 前提条件: C1 ブランドプロトコル(`brand.json`)、コンテンツ基準、ブランドエージェントが自動バイイングでガイドラインを適用する方法。 ### 読み物リスト ブランドアイデンティティの主張、brand.json の探索、ブランド階層、ブランドエージェント。 brand.json フォーマット: アイデンティティ、ロゴ、カラー、ガイドライン、エージェント宣言。 広告主がブランドプロトコルを通じてタレント権利をライセンスする方法 — 価格、スコープ、取得できるもの。 ライセンス可能なタレント権利を検索 — 価格、空き状況、除外フィルタリング。 タレント権利のライセンス: 生成資格情報、権利制約、取り消し、承認ワークフロー。 既存の権利付与を延長、調整、または一時停止します。 権利保有者がブランドプロトコルを通じてタレントをマネタイズする方法。 コンテンツ基準が何が適切かを定義する方法、キャリブレーションの仕組み、ローカル実行。 配置前にコンテンツがブランド基準を満たすかテストします。 メディアバイ実行中のコンプライアンスの適用方法。 クリエイティブ品質とコンプライアンスのガバナンス。 キャンペーンをメディアプランに結びつける: 予算権限、マルチパーティバリデーション、常時コンプライアンス。 ブランドが ID で参照するコミュニティ維持のコンプライアンスポリシー(COPPA、GDPR、HFSS)。 ### 主要コンセプト * **ブランドアイデンティティプロトコル** — `/.well-known/brand.json` の `brand.json` がブランドアイデンティティを宣言 * **コンテンツ基準** — MCP ベースのブランドエージェントによる自動コンプライアンスチェック * **Oracle モデル** — スケールでブランドセーフティを評価するための AI の使用 * **サプライチェーンの好み** — 適切性、セーフティ、持続可能性の要件 * **キャンペーンガバナンス** — キャンペーンプランが承認パラメーターを定義; `check_governance` が実行前にすべてのトランザクションを検証 * **ポリシーレジストリ** — ブランドごとに記述されるのではなく ID で参照される共有コンプライアンスポリシー(規制と標準) * **ガバナンスモード** — 監査、アドバイザリー、強制 — 学習から本番への展開パス * **ポリシーカテゴリー** — プランが宣言してガバナンスエージェントが適用する規制体制(`children_directed`、`fair_housing`、`fair_lending`) * **制限属性** — 適用されるポリシーの下でターゲティングに使用してはなりません個人データカテゴリー(`health_data`、`racial_ethnic_origin`) 「認定モジュール C2 を始めたい」 *** ## C3: クリエイティブワークフロー **約20分** | 前提条件: C2 クリエイティブアセットが AdCP を通じて流れる方法: `build_creative`、`preview_creative`、`sync_creatives`。クロスプラットフォーム適応とスポンサードインテリジェンスプロトコル。 ### 読み物リスト クリエイティブプロトコル: アセット、フォーマット、マニフェスト、クリエイティブエージェント。 クリエイティブの生成と変換。 デプロイ前のクリエイティブのプレビュー。 クリエイティブアセットをパブリッシャープラットフォームと同期します。 AI 生成クリエイティブワークフローとベストプラクティス。 AI アシスタントでの会話型ブランド体験。 スポンサードインテリジェンスプロトコル: セッション、メッセージ、オファリング。 ### 主要コンセプト * **クリエイティブライフサイクル** — `build_creative`、`preview_creative`、`sync_creatives` — クリエイティブプロトコルを実装する任意のエージェント(セールスエージェントを含む)で呼び出し可能 * **クロスプラットフォーム適応** — エージェントがディスプレイ、動画、音声、ネイティブフォーマットにわたってアセットを適応させる * **セラーサイドの生成** — セールスエージェントはメディアバイで提供されたブリーフからサーブ時にクリエイティブを生成できます * **スポンサードインテリジェンス** — ブランドが透明性とユーザーコントロールを伴って会話型 AI に参加します ### Canonical-format buyer glossary | Term | Buyer-track meaning | | ------------------ | ---------------------------------------------------------------------------------------------------- | | `format_kind` | セラーが受け入れる正準クリエイティブ形状(`image`、`video_hosted`、`native_in_feed` など)。メディアチャネルだけでなく、出荷できるクリエイティブで選びます。 | | `format_options[]` | `get_products` が返すプロダクトレベルのクリエイティブ要件。各オプションはセラー固有のサイズ、スロット、セレクター、制作制約で正準を狭めます。 | | `asset_source` | 正準アセットのレンダリング済みバイトを誰が制作するか。バイヤーはこれを使って、完成アセットをアップロードするか、セラー・パブリッシャー・エージェントの制作のためのブリーフ/入力を提供するかを決めます。 | S2 は [マニフェストのオーサリング](/docs/learning/specialist/creative)、`format_option_id` 対クリエイティブエージェントの `capability_id`、正準ファースト/プロダクトセカンドの検証フローをより深く掘り下げます。 ### Practice exercise 1. **`get_products` からの正準フォーマット選択** — 複数の `format_options[]` を含むプロダクトレスポンスが与えられたとき: * 利用可能なすべての `format_kind` と、クリエイティブルーティングに重要なバイヤーに見えるプロダクト制約をリスト * プロダクトが公開するときに `format_option_id` を使って、キャンペーンのクリエイティブ形状に一致するオプションを選択 * 検証順序を高レベルで述べる: 正準の形状が先、プロダクトの狭めが後 * ルーティング前に `errors[]` の正準フォーマット `FORMAT_*` エントリを確認: ほとんどは非致命的なアドバイザリだが、壊れたプレースメント参照はそのプレースメントについてフェイルクローズを要求 | Advisory code | Buyer action | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | `FORMAT_PROJECTION_FAILED` | レガシーフォーマットはまだ存在するが、SDK がそれを `format_options[]` に投影できなかった。利用可能なときは明示的な正準オプションを優先。そうでなければセラーまたはレジストリのギャップにフラグ。 | | `FORMAT_DECLARATION_DIVERGENT` | セラーの v1 と v2 の宣言が食い違う。3.1 ルーティングには `format_options[]` を優先し、セラー宣言の不一致にフラグ。 | | `FORMAT_DECLARATION_V1_AMBIGUOUS` | v2 宣言が 1 つの v1 名前付きフォーマットに安全にマップし戻せない。v1 フォールバックを発明しない。正準パスを使うか、セラーに `v1_format_ref[]` の追加を依頼。 | | `FORMAT_OPTION_UNRESOLVED` | プレースメントが欠けているパブリッシャーカタログオプションを参照。そのプレースメント参照についてフェイルクローズし、パブリッシャーにカタログの修正を依頼。 | | `FORMAT_DECLARATION_V1_LOSSY_MULTI_SIZE` | 一部の v1 サイズカバレッジがマルチサイズ v2 宣言から落とされた。表面化したカバー済みサイズのみで続けるか、完全に宣言されたオプションを選ぶ。 | 「認定モジュール C3 を始めたい」 *** ## C4: ビルドプロジェクト — 最初のバイヤーエージェント **約45分** | 前提条件: C3 プロダクトを探索してメディアバイを実行する動くバイヤーエージェントを作成します。adcp クライアントライブラリを使って任意の AI コーディングアシスタント(Claude Code、Cursor、Copilot)を使用します。テストされるスキルは正しくバイイングワークフローをオーケストレーションすることです。 ### 構築するもの * `sync_accounts` でのアカウントセットアップ * 少なくとも2つのセラーからのプロダクト探索 * ターゲティングと予算付きのメディアバイ作成 * 少なくとも1つのフォーマットでのクリエイティブ同期 * 配信レポートを通じたキャンペーン監視 ### How you'll validate あなたのバイヤーエージェントは*呼び出し元*です — セラーツールを公開するのではなく消費します。あなたのエージェントをパブリックテスト**セラー**(`https://test-agent.adcontextprotocol.org/sales/mcp`)に向け、それが完全なワークフローをエンドツーエンドで駆動すること — プロダクトを発見し、メディアバイを作成し、クリエイティブを同期し、配信を読む — を確認することで検証します。 あなたのエージェントが処理しなければならないセラーレスポンスを見るには、テストセラーを直接検査できます。これはバイヤーエージェントが受け取る形状を示します — あなたのエージェントを**テストしません**: ```bash theme={null} # Inspects the test SELLER's get_products response — the shape your buyer agent must parse. npx @adcp/sdk@latest test-mcp get_products '{"brief":"your campaign brief"}' ``` クライアントセットアップについては [Build a caller](/docs/building/by-layer/L4/build-a-caller) を、ストーリーボードワークフローについては [Validate Your Agent](/docs/building/verification/validate-your-agent) を参照。 ### Validating across sellers バイヤーエージェントは専門分野を主張しません — 専門分野はセラーが何を提供するかを記述します。しかしあなたのバイヤーエージェントは、取引を期待するすべての専門分野を処理すべきです。セラーが合格するストーリーボード(`get_adcp_capabilities` の `supported_protocols` と `specialisms` で宣言)が、期待する動作を教えます。 [Compliance Catalog](/docs/building/compliance-catalog) を確認し、ターゲットセラーがどの専門分野を主張するかに注目します — それがあなたのテストマトリクスです。 ### 評価ルーブリック | 次元 | ウェイト | Addie が評価するもの | | ------------ | ---- | -------------------------------- | | 仕様の品質 | 20% | AdCP 用語でバイイングワークフローを指定できるか? | | スキーマコンプライアンス | 25% | エージェントのリクエストとレスポンスがスキーマに対して検証される | | エラーハンドリング | 15% | セラーエラーと非同期レスポンスを処理する | | 設計の根拠 | 20% | オーケストレーションとバイイング戦略を説明できるか? | | 拡張能力 | 20% | 新しいバイイング機能でエージェントを拡張できるか? | 合格基準: 70%。 任意の AI コーディングアシスタントが歓迎されます。ビルドはロール間のインタラクションを実証しなければなりません。 「認定モジュール C4 を始めたい」 *** ## 次のステップ C1〜C4 を完了すると **AdCP practitioner** クレデンシャルを取得できます。ここからスペシャリストモジュールを追求できる: * [S1: メディアバイ](/docs/learning/specialist/media-buy) — トランザクションフロー、価格設定、オーケストレーション * [S2: クリエイティブ](/docs/learning/specialist/creative) — アセットワークフロー、フォーマットコンプライアンス * [S3: シグナル](/docs/learning/specialist/signals) — 測定、アトリビューション、最適化 * [S4: ガバナンス](/docs/learning/specialist/governance) — ブランドセーフティ、キャンペーンガバナンス、コンプライアンス、ポリシーレジストリ * [S5: スポンサードインテリジェンス](/docs/learning/specialist/sponsored-intelligence) — 会話型ブランド体験 # プラットフォーム / インターメディアリートラック Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/tracks/platform AdCP プラットフォームトラック(D1〜D4): MCP サーバーアーキテクチャ、サプライパス検証、エージェントトラスト、RTB から AdCP への移行パターン、インフラストラクチャビルドプロジェクト。 # プラットフォーム / インターメディアリートラック(D1〜D4) **メンバー限定** — Basics クレデンシャル(A1〜A3)が必要。4つのモジュール、合計約105分。 このトラックは AdCP インフラストラクチャを構築する人向け: アドテクプラットフォーム、取引所、データ会社、エコシステムをつなぐすべての人。MCP サーバーアーキテクチャ、サプライチェーン検証、RTB 移行を学び、ビルドプロジェクトで動くインフラストラクチャを構築します。 このトラック(プラス A1〜A3)を完了すると **AdCP practitioner** クレデンシャルを取得できます。 *** ## D1: MCP サーバーアーキテクチャ **約20分** | 前提条件: A3 AdCP 準拠の MCP サーバー構築の技術的な詳細。トランスポートオプション(SSE、Streamable HTTP)、ツール定義パターン、OAuth/認可フロー、アカウント管理。 ### 読み物リスト AdCP インテグレーションを構築するために必要なことの概要。 決定版ガイド: ツール呼び出し、レスポンスフォーマット、利用可能なツール、コンテキスト管理。 エージェント認証用 OAuth 2.0、トークン管理、オペレーター資格情報。 アカウント、エージェント、プリンシパルの関係。 マルチターンエージェントインタラクションにわたるコンテキスト管理。 より速く構築するための JSON スキーマ、TypeScript 型、クライアント SDK。 ### 主要コンセプト * **MCP サーバーアーキテクチャ** — AdCP タスクをツールとして公開し、認証とリクエストルーティングを処理します * **トランスポートオプション** — ほとんどのケースで Streamable HTTP、リアルタイム更新には SSE * **ケイパビリティ宣言** — 他のエージェントがサポート内容を知るための `get_adcp_capabilities` * **アカウント処理** — バイヤーからの受信 `sync_accounts` の管理 「認定モジュール D1 を始めたい」 *** ## D2: サプライパス、トラスト、プロパティガバナンス **約20分** | 前提条件: D1 サプライチェーン検証のための暗号署名。プラットフォームがエージェントアイデンティティを検証し、不正を検出し、トラストを確保する方法。AdCP と ads.cert の関係。 ### 読み物リスト AdCP の参加者のアイデンティティ、認可、データエンリッチメント。 エージェント探索と宣言 — すべてのエージェントの検証可能なアイデンティティ。 パブリッシャーが承認されたセラーを宣言する方法、バイヤーが検証する方法。 AdCP サーバーのセキュリティ実装パターン。 アカウントレベルのセキュリティ、プリンシパル階層、アクセスコントロール。 プロパティガバナンスの正式仕様。 マルチパーティバリデーション: プラットフォームが自律的なトランザクションのガバナンスフローを実装する方法。 完全な技術仕様: sync\_plans、check\_governance、report\_plan\_outcome、監査ログ。 ガバナンスエージェントが解決・適用するコミュニティ維持のコンプライアンスポリシー。 ### 主要コンセプト * **エージェントアイデンティティ検証** — ドメイン所有権、暗号署名、組織登録 * **サプライパスの透明性** — 各トランザクションを処理したエージェントへの完全な可視性 * **ads.cert との関係** — RTB からエージェント間インタラクションへの暗号検証の拡張 * **キャンペーンガバナンスアーキテクチャ** — 3者検証を実装: `sync_accounts` から `governance_agents` を保存し、実行前に `check_governance` を呼び出し、すべてのステータスを処理します * **ポリシーレジストリ統合** — ID でポリシーを解決し、自然言語ポリシーテキストと例をガバナンスエージェントの評価に統合します 「認定モジュール D2 を始めたい」 *** ## D3: RTB 共存と移行 **約20分** | 前提条件: D2 AdCP が既存のプログラマティックインフラストラクチャと共存する方法。DSP、SSP、取引所の移行戦略。移行中の並行システムの運用。 ### 読み物リスト リアルタイム実行のための AXE エンジンとエージェンティックとプログラマティックのブリッジ方法。 長時間実行操作の処理 — リアルタイムとエージェンティックシステムのブリッジに不可欠。 デリバリー、ステータス変更、キャンペーン変更のためのイベント駆動更新。 エラーパターン、再試行戦略、グレースフルデグラデーション。 本番稼働前にサンドボックスエージェントに対して実装をテストします。 AdCP インテグレーションを構築するチームからのよくある質問と回答。 ### 主要コンセプト * **共存戦略** — OpenRTB と並行して AdCP を実行し、ワークフローを徐々に移行します * **プラットフォーム固有の移行** — DSP はビッディングロジックをラップし、SSP はインベントリを公開し、取引所は変換します * **パフォーマンスベンチマーキング** — キャンペーンパフォーマンス、効率、コストでエージェンティック vs 従来を比較 「認定モジュール D3 を始めたい」 *** ## D4: ビルドプロジェクト — AdCP インフラストラクチャ **約45分** | 前提条件: D3 adcp クライアントライブラリを使って任意の AI コーディングアシスタント(Claude Code、Cursor、Copilot)で動く AdCP インフラストラクチャを構築します。これは最も野心的なビルドプロジェクトです。 ### 構築するもの * AdCP ツール定義付きの MCP サーバー * `get_adcp_capabilities` の実装 * ツールとして公開された少なくとも3つの AdCP タスク * OAuth/認証フロー * AdCP エラーコードを使った適切なエラーハンドリング ### How you'll validate 一致するストーリーボードを稼働中のエージェントに対して実行します: ```bash theme={null} npx @adcp/sdk@latest storyboard run my-agent media_buy_seller ``` ストーリーボードは完全なワークフローにわたってプロトコルコンプライアンスを検証します。セットアップ、デバッグ、完全な CLI リファレンスについては [Validate Your Agent](/docs/building/validate-your-agent) を参照。 ### Specialisms you can claim プラットフォームエージェントはしばしば複数のドメインにまたがります。実装する各プロトコルを `supported_protocols` で、主張する各専門分野を `specialisms` で宣言します。一般的な組み合わせ: * フルスタックセールスプラットフォーム: `supported_protocols: ["media_buy", "creative", "signals"]`、`specialisms: ["sales-guaranteed", "sales-non-guaranteed", "creative-ad-server"]` * シグナルプラットフォーム: `supported_protocols: ["signals"]`、`specialisms: ["signal-marketplace"]` または `["signal-owned"]` * ガバナンスプラットフォーム: `supported_protocols: ["governance"]`、`specialisms: ["content-standards", "property-lists", "collection-lists"]` すべての専門分野と各クレームを検証するストーリーボードについては [Compliance Catalog](/docs/building/compliance-catalog) を参照。 ### 評価ルーブリック | 次元 | ウェイト | Addie が評価するもの | | ------------ | ---- | ---------------------------- | | 仕様の品質 | 20% | AdCP 用語でインフラストラクチャを指定できるか? | | スキーマコンプライアンス | 20% | すべてのエンドポイントにわたるプロトコルコンプライアンス | | エラーハンドリング | 15% | リカバリータイプと非同期を含む適切なエラーハンドリング | | 設計の根拠 | 25% | 本番アーキテクチャについて論理的に考えられるか? | | 拡張能力 | 20% | 新しいタスクでエンドポイントを拡張できるか? | 合格基準: 70%。 「認定モジュール D4 を始めたい」 *** ## 次のステップ D1〜D4 を完了すると **AdCP practitioner** クレデンシャルを取得できます。ここからスペシャリストモジュールを追求できる: * [S1: メディアバイ](/docs/learning/specialist/media-buy) — トランザクションフロー、価格設定、オーケストレーション * [S2: クリエイティブ](/docs/learning/specialist/creative) — アセットワークフロー、フォーマットコンプライアンス * [S3: シグナル](/docs/learning/specialist/signals) — 測定、アトリビューション、最適化 * [S4: ガバナンス](/docs/learning/specialist/governance) — ブランドセーフティ、キャンペーンガバナンス、コンプライアンス、ポリシーレジストリ * [S5: スポンサードインテリジェンス](/docs/learning/specialist/sponsored-intelligence) — 会話型ブランド体験 # パブリッシャー / セラートラック Source: https://adcp-docs-ja.pier1.co.jp/docs/learning/tracks/publisher AdCP パブリッシャートラック(B1〜B4): セールスエージェントの構築と運用。プロダクトカタログ設計、クリエイティブ仕様、配信レポート、セールスエージェントのビルドプロジェクト。 # パブリッシャー / セラートラック(B1〜B4) **メンバー限定** — Basics クレデンシャル(A1〜A3)が必要。4つのモジュール、合計約105分。 このトラックは AdCP のサプライサイドの構築と運用を教える。セールスエージェントがパブリッシャーのインベントリを表現する方法、バイヤーエージェントがあなたのプロダクトを探索・評価する方法、測定のためのデリバリーデータとシグナルの公開方法を学ぶ。トラックは動くセールスエージェントを作成するビルドプロジェクトで締めくくられます。 このトラック(プラス A1〜A3)を完了すると **AdCP practitioner** クレデンシャルを取得できます。 ## Is this track for you? AdCP は、AI バイヤーエージェントがあなたのインベントリを発見し、メディアバイを交渉・作成し、配信データを取得するために使える単一のエンドポイントをパブリッシャーに与えます — セールスコール、カスタム API 統合、または直接取引条件を諦めることなく。1 つの実装。自律的に運用するバイヤーへの常時アクセス。 | Your role | Where to start | Modules that matter most | | ----------------- | -------------- | ----------------------------------------------- | | 広告運用 / イールドマネージャー | B1、次に B3 | B1(カタログ設計)、B3(配信レポート、シグナル) | | セールス / 収益 | B1、次に B2 | B1(バイヤーが見るもの)、B2(クリエイティブ仕様) | | エンジニア / 開発者 | B1 から B4 | フルトラック — 実際のセールスエージェントの構築と検証で終了 | | エグゼクティブ / ストラテジスト | A1〜A3 のみ | トラック A がプロトコルとエコシステムのコンテキストをカバー。実装しない限り B はスキップ | エンジニアとアドテクビルダーは B1〜B4 を行うべきです。それ以外の人は上記のモジュールを選んで残りをスキップできます。 ### What you'll build toward * B4 はビルドプロジェクトです: 実際のバイヤークエリを処理する動くセールスエージェント — プロダクト探索、メディアバイ作成、クリエイティブ仕様、配信レポート — を作成します * エージェントは手動レビューだけでなく、AdCP ストーリーボードに対して検証されます * B1〜B4(プラス A1〜A3)の完了で **AdCP practitioner** クレデンシャルを取得できます *** ## B1: プロダクトカタログの設計 **約20分** | 前提条件: A3 ホストされたセールスエージェントの仕組み: プロダクト探索、カタログ統合、ケイパビリティ宣言。プロダクトカタログの設計のウォークスルー — ポッドキャスト、CTV シリーズ、ライブイベントなどのコンテンツ中心インベントリのショーとエピソードを含む — そしてバイヤーがあなたについて何を発見できるかの設定。 ### 読み物リスト バイヤーエージェントが `get_products` を通じてパブリッシャーのインベントリを探索する方法。 タスクリファレンス: リクエストスキーマ、レスポンススキーマ、例。 プロダクトの構造化方法: フォーマット、価格、ターゲティング、空き状況。 コンテンツ中心インベントリのモデル化: ポッドキャスト、CTV シリーズ、ライブイベント、エピソードコンテンツ。 カタログ同期: 13のカタログタイプ、フィードフィールドマッピング。 エージェントが他のエージェントに提供するものを発見できるようにケイパビリティを宣言する方法。 パブリッシャーがエージェンティックトランザクションのためにプロパティを宣言・承認する方法。 ### 主要コンセプト * **セールスエージェントの役割** — バイヤーのクエリに応答する常時起動の AI 搭載セールスチーム * **プロダクトカタログ設計** — プロダクト、フォーマット、価格モデル、ターゲティングオプション、空き状況、コンテンツ中心インベントリのショー/エピソード * **カタログ統合** — プロダクトデータ用の `sync_catalogs`、13のカタログタイプ、フィードフィールドマッピング * **ケイパビリティ宣言** — バイヤーがサポート内容を知るための `get_adcp_capabilities` 「認定モジュール B1 を始めたい」 *** ## B2: クリエイティブ仕様とフォーマットサポート **約20分** | 前提条件: B1 `list_creative_formats` の詳細と、バイヤーエージェントがあなたのインベントリが受け付けるものを探索する方法。クリエイティブ仕様の構造化、受信クリエイティブの処理、ブランドコンプライアンスのガイド。 ### 読み物リスト バイヤーブリーフの形式と、エージェントがマッチできるようにプロダクトを構造化する方法。 バイヤーエージェントからのプロダクト探索クエリの実際の例。 クリエイティブフォーマット仕様の仕組み — 寸法、ファイルタイプ、レンダー要件。 フォーマット定義、技術仕様、レンダー構造。 標準フォーマット ID と一貫した実装方法。 バイヤーエージェントが検索を絞り込みプロダクトパラメーターを交渉する方法。 ### 主要コンセプト * **フォーマット宣言** — `list_creative_formats` がバイヤーに受け付けるものを伝える * **フォーマット vs マニフェスト** — フォーマットは受け付けるもの、マニフェストは配信されるもの * **受信クリエイティブの処理** — `sync_creatives` と `build_creative` のバリデーションと承認 * **ブランドコンプライアンス** — 受信 brand.json の理解、開示ポジション 「認定モジュール B2 を始めたい」 *** ## B3: 測定、シグナル、最適化 **約20分** | 前提条件: B2 セールスエージェントを通じてデリバリーデータ、測定シグナル、最適化レバーを公開する方法。測定プラットフォームとの統合、オーディエンス処理、アカウント管理。 ### 読み物リスト デリバリーメトリクス: インプレッション、支出、完了率、パフォーマンスデータ。 バイヤーエージェントがキャンペーン最適化にデリバリーデータを使用する方法。 シグナルプロトコル: オーディエンスセグメント、コンテキストシグナル、測定データ。 シグナル探索: どの測定データが利用可能か、公開方法。 キャンペーンの特定測定を有効化します。 商取引レイヤー: 広告主、オペレーター、認証、請求。 セラーサイドのガバナンスチェック: 実行前にバイヤーの購入を検証します。 ガバナンスバリデーションタスク: 提案された vs コミットされたバインディング、ステータス、計画された配信。 ### 主要コンセプト * **デリバリーレポーティング** — `get_media_buy_delivery` による正確でタイムリーなデリバリーデータ * **シグナルフレームワーク** — `get_signals` と `activate_signal` が断片化した測定ベンダー統合を置き換える * **オーディエンス処理** — バイヤーオーディエンスデータを受け付けるための `sync_audiences` * **アカウント管理** — バイヤー関係を管理するための `sync_accounts`、`list_accounts` * **セラーサイドガバナンス** — バイヤーアカウントに `governance_agents` がある場合、メディアバイを確認する前にバインディング `committed` で `check_governance` を呼び出す * **計画された配信** — 実際に配信するものを説明し、ガバナンスエージェントがそれをバイヤーのプランに対して検証します ### Exercises サンプルの Context Match と Identity Match のレスポンスが与えられたとき、パブリッシャーがどのパッケージをアクティベートし、どのターゲティングキー/バリューを設定するかを判断します。 **放送配信レポート:** `measurement_windows: [{window_id: "c3"}, {window_id: "c7"}]` を持つ放送プロダクトが与えられ、バイヤーがフライト日の 2 日後に `get_media_buy_delivery` を呼び出します。各ウィンドウでどのデータが利用可能か、`c7` ウィンドウがまだ不完全な理由、およびバイヤーが部分データをアンダーデリバリーと誤読しないようにセラーが配信をどう表現すべきかを説明します。 「認定モジュール B3 を始めたい」 *** ## B4: ビルドプロジェクト — 最初のセールスエージェント **約45分** | 前提条件: B3 実際のバイヤーのクエリに応答する動くセールスエージェントを作成します。adcp クライアントライブラリを使って任意の AI コーディングアシスタント(Claude Code、Cursor、Copilot)を使用します。テストされるスキルは正しい AdCP の動作を指定することです。 ### 構築するもの * `sync_catalogs` サポート付きで少なくとも3つのプロダクトを含むプロダクトカタログ * 少なくとも2つのフォーマットのクリエイティブフォーマットサポート * `get_products`、`create_media_buy`、`list_creative_formats` を処理します * `sync_accounts` によるアカウントセットアップ * 無効なリクエストに対する適切なエラーレスポンス ### How you'll validate `media_buy_seller` ストーリーボードを稼働中のエージェントに対して実行します: ```bash theme={null} npx @adcp/sdk@latest storyboard run my-agent media_buy_seller ``` ストーリーボードは完全なバイヤーワークフロー — 探索、アカウント同期、メディアバイ、クリエイティブ、配信 — を行使し、すべてのレスポンスを検証します。セットアップとデバッグについては [Validate Your Agent](/docs/building/validate-your-agent) を参照。 ### Specialisms you can claim `supported_protocols` で `media_buy` を宣言し、インベントリモデルに一致する専門分野を選びます。各専門分野は `/compliance/{version}/specialisms/{id}/` にクレームを検証するコンプライアンスストーリーボードを持ちます: * `sales-guaranteed` — 人手による IO 承認を伴う保証型 * `sales-non-guaranteed` — オークションベース * `sales-proposal-mode` — **3.1 で非推奨**。代わりに `sales-guaranteed` + `media_buy.supports_proposals: true` を使う * `sales-catalog-driven` — コンバージョン追跡付きのカタログ駆動コマース * `sales-broadcast-tv` — 放送リニア TV * `sales-social` — セルフサービスフローを伴うソーシャルプラットフォーム 完全なリストは [Compliance Catalog](/docs/building/compliance-catalog) を参照。 ### 評価ルーブリック | 次元 | ウェイト | Addie が評価するもの | | ------------ | ---- | ------------------------------- | | 仕様の品質 | 20% | AdCP 用語でエージェントを指定できるか? | | スキーマコンプライアンス | 25% | エージェントレスポンスが AdCP スキーマに対して検証される | | エラーハンドリング | 15% | 無効なリクエストを適切な AdCP エラーで処理する | | 設計の根拠 | 20% | 設計上の意思決定を説明・論理的に考えられるか? | | 拡張能力 | 20% | 新しいケイパビリティでエージェントを拡張できるか? | 合格基準: 70%。 任意の AI コーディングアシスタントが歓迎されます(Claude、Cursor、Copilot など)。テストされるスキルは:「正しい AdCP の動作を指定できるか?」 「認定モジュール B4 を始めたい」 *** ## 次のステップ B1〜B4 を完了すると **AdCP practitioner** クレデンシャルを取得できます。ここからスペシャリストモジュールを追求できる: * [S1: メディアバイ](/docs/learning/specialist/media-buy) — トランザクションフロー、価格設定、オーケストレーション * [S2: クリエイティブ](/docs/learning/specialist/creative) — アセットワークフロー、フォーマットコンプライアンス * [S3: シグナル](/docs/learning/specialist/signals) — 測定、アトリビューション、最適化 * [S4: ガバナンス](/docs/learning/specialist/governance) — ブランドセーフティ、キャンペーンガバナンス、コンプライアンス、ポリシーレジストリ * [S5: スポンサードインテリジェンス](/docs/learning/specialist/sponsored-intelligence) — 会話型ブランド体験