# アカウントプロトコル 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 | セラーの説明または推奨事項 | 計測レディネスは、バイヤーのアカウントのコンテキストでプロダクトごとに評価されます。同じプロダクトでも、イベントソースの設定に応じてバイヤーごとに異なるレ