キャンペーン要件に基づき、自然言語のブリーフまたは構造化フィルターで利用可能な広告プロダクトを検索します。
Authentication: 任意(認証なしの場合は結果が制限されます)
Response Time: 約 60 秒(バックエンド連携を伴う推論)
Request Schema: /schemas/v3/media-buy/get-products-request.json
Response Schema: /schemas/v3/media-buy/get-products-response.json
クイックスタート
自然言語のブリーフでプロダクトを検索:
import { testAgent } from '@adcp/client/testing';
import { GetProductsResponseSchema } from '@adcp/client';
const result = await testAgent.getProducts({
buying_mode: 'brief',
brief: 'Premium athletic footwear with innovative cushioning',
brand: {
domain: 'acmecorp.com'
}
});
if (!result.success) {
throw new Error(`Request failed: ${result.error}`);
}
// Validate response against schema
const validated = GetProductsResponseSchema.parse(result.data);
console.log(`Found ${validated.products.length} products`);
// Access validated product fields
for (const product of validated.products) {
console.log(`- ${product.name} (${product.delivery_type})`);
console.log(` Formats: ${product.format_ids.map(f => f.id).join(', ')}`);
}
構造化フィルターの利用
ブリーフの代わりに(または併用して)構造化フィルターを使うこともできます。brief モードでは、フィルターはパブリッシャーのキュレーションに対するハード制約として機能します。ブリーフが意図を表し、フィルターが要件を強制します。
import { testAgent } from '@adcp/client/testing';
const result = await testAgent.getProducts({
buying_mode: 'wholesale',
brand: {
domain: 'acmecorp.com'
},
filters: {
format_types: ['video'],
delivery_type: 'guaranteed',
standard_formats_only: true
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} guaranteed video products`);
}
リクエストパラメーター
| Parameter | Type | Required | Description |
|---|
buying_mode | string | Yes | "brief"、"wholesale"、または "refine"。"brief": パブリッシャーがブリーフからプロダクトをキュレーションします。"wholesale": バイヤー主導のターゲティング用の生インベントリ。brief は指定してはなりません。"refine": refine 配列の変更依頼を使って前のレスポンスのプロダクトやプロポーザルを反復します。v3 クライアントは buying_mode を含めなければなりません。buying_mode を持たない v3 以前のクライアントからリクエストを受けたセラーは "brief" をデフォルトとすべきです。 |
brief | string | Conditional | キャンペーン要件の自然言語説明。buying_mode が "brief" の場合は必須。"wholesale" または "refine" の場合は指定してはなりません。 |
refine | Refine[] | Conditional | プロダクトやプロポーザルを反復するための変更依頼の配列。buying_mode が "refine" の場合は必須。"brief" または "wholesale" の場合は指定してはなりません。後述の Refine 配列 を参照。 |
brand | BrandRef | No | ブランド参照(ドメイン+オプションの brand_id)。実行時に完全な識別情報へ解決されます。 |
account | AccountRef | No | アカウント固有の価格設定に用いるアカウント参照。このアカウントのレートカードから価格付きのプロダクトを返します。 |
catalog | Catalog | No | バイヤーが宣伝したいアイテムのカタログ。セラーはカタログアイテムをインベントリに照合し、マッチが存在するプロダクトを返します。brand が必要。後述の カタログによる探索 を参照。 |
filters | Filters | No | 構造化フィルター(後述) |
property_list | PropertyListRef | No | [AdCP 3.0] フィルタリングに用いるプロパティリスト参照。Property Lists 参照 |
pagination | PaginationRequest | No | 大規模プロダクトカタログ向けのカーソルベースページネーション(後述) |
time_budget | Duration | No | バイヤーがこのリクエストに割り当てる最大時間。セラーはその時間内で最善の結果を返し、その時間内に完了できないプロセス(人間の承認や高コストな外部クエリ)は開始しません。省略した場合はセラーがタイミングを決定します。例: {"interval": 30, "unit": "seconds"}。 |
Filters オブジェクト
| Parameter | Type | Description |
|---|
delivery_type | string | "guaranteed" または "non_guaranteed" でフィルタリング |
is_fixed_price | boolean | 固定価格かオークションかでフィルタリング |
format_types | string[] | (非推奨の FormatCategory を使用) フォーマット種別でフィルタリング(例: ["video", "display"])。正確なマッチングには代わりに format_ids でのフィルタリングを推奨。 |
format_ids | FormatID[] | 特定のフォーマット ID でフィルタリング。format_types より推奨。 |
standard_formats_only | boolean | IAB 標準フォーマットを受け付けるプロダクトのみ返す |
min_exposures | integer | 計測妥当性に必要な最小エクスポージャ数 |
start_date | string | 可用性確認のための開始日 (ISO 8601, YYYY-MM-DD) |
end_date | string | 可用性確認のための終了日 (ISO 8601, YYYY-MM-DD) |
budget_range | object | 適切なプロダクトを絞る予算レンジ(下記 Budget Range オブジェクト参照) |
countries | string[] | ISO 3166-1 alpha-2 コードで国を指定(大文字/小文字は不問。例: ["US", "CA", "GB"]) |
channels | string[] | 広告チャンネルでフィルタリング(例: ["display", "ctv", "social", "streaming_audio"])。Media Channel Taxonomy 参照 |
Budget Range オブジェクト
| Parameter | Type | Required | Description |
|---|
currency | string | Yes | ISO 4217 通貨コード(例: "USD", "EUR", "GBP") |
min | number | No* | 最低予算額 |
max | number | No* | 最高予算額 |
*min または max のいずれか一方は必ず指定しなければなりません。
Refine 配列
refine 配列は変更依頼のリストです。各エントリは scope と、バイヤーが求める内容を宣言します。少なくとも 1 エントリが必要。セラーはすべてのエントリをまとめて考慮してレスポンスを構成し、refinement_applied で各エントリに返答します。
各エントリは scope による判別共用体です。
scope: “request”
| Field | Type | Required | Description |
|---|
scope | string | Yes | "request" |
ask | string | Yes | 選択全体への方向指示(例: "more video options"、"suggest how to combine these products")。 |
scope: “product”
| Field | Type | Required | Description |
|---|
scope | string | Yes | "product" |
id | string | Yes | 前回の get_products レスポンスのプロダクト ID |
action | string | Yes | "include": 更新された価格とデータでこのプロダクトを返します。"omit": レスポンスから除外します。"more_like_this": 類似プロダクトを探す(元のプロダクトも返されます)。 |
ask | string | No | バイヤーが求める内容。"include" の場合: 具体的な変更(例: "add 16:9 format")。"more_like_this" の場合: 「類似」の意味(例: "same audience but video format")。action が "omit" の場合は無視されます。 |
scope: “proposal”
| Field | Type | Required | Description |
|---|
scope | string | Yes | "proposal" |
id | string | Yes | 前回の get_products レスポンスのプロポーザル ID |
action | string | Yes | "include": 更新された配分と価格で返します。"omit": レスポンスから除外します。 |
ask | string | No | バイヤーが求める内容(例: "shift more budget toward video"、"reduce total by 10%")。action が "omit" の場合は無視されます。 |
refinement_applied(レスポンス)
セラーが refine 配列を受け取ると、レスポンスには位置でマッチする refinement_applied 配列が含まれます。各エントリは依頼が fulfilled されたかを報告します。
| Field | Type | Required | Description |
|---|
scope | string | No | 相互検証用に対応する refine エントリの scope をエコーします。 |
id | string | No | 対応する refine エントリの id をエコーする(product・proposal スコープの場合)。 |
status | string | Yes | "applied": 依頼が fulfilled されました。"partial": 部分的に fulfilled されました。"unable": fulfilled できなかった。 |
notes | string | No | セラーの説明。status が "partial" または "unable" の場合に推奨。 |
カタログによる探索
カタログアイテムを宣伝できる広告プロダクトを探すには catalog を渡します。セラーはカタログアイテムをインベントリに照合し、マッチが存在するプロダクトを返します。すべてのカタログ種別に対応しています。商品カタログはスポンサー商品枠を探し、求人カタログは求人広告プロダクトを、フライトカタログはダイナミックトラベル広告を探す。
catalog フィールドは AdCP 全体で使われる同じ Catalog オブジェクトを使います。catalog_id で同期済みカタログを参照したり、インラインでアイテムを指定したり、セレクターでフィルタリングしたりできます。
| Field | Type | Description |
|---|
type | CatalogType | カタログ種別(必須)— product、job、hotel、flight、offering など |
catalog_id | string | ID で同期済みカタログを参照する |
ids | string[] | 特定のアイテム ID に絞り込む |
gtins | string[] | クロスリテーラーマッチング用に GTIN でフィルタリング(product 種別のみ) |
tags | string[] | タグでフィルタリング(OR ロジック) |
category | string | カテゴリでフィルタリング |
query | string | 自然言語フィルター |
レスポンスのプロダクトには catalog_types(対応するカタログ種別)と catalog_match(マッチしたアイテム)が含まれます。
レスポンス
products 配列と、必要に応じて proposals を返します。
Products 配列
| Field | Type | Description |
|---|
product_id | string | プロダクトの一意 ID |
name | string | 人が読めるプロダクト名 |
description | string | プロダクトの詳細説明 |
publisher_properties | PublisherProperty[] | パブリッシャーごとのエントリ。publisher_domain と property_ids または property_tags を含む |
format_ids | FormatID[] | サポートするクリエイティブフォーマット ID |
delivery_type | string | "guaranteed" または "non_guaranteed" |
delivery_measurement | DeliveryMeasurement | (任意)配信の計測方法(インプレッション、ビュー等) |
pricing_options | PricingOption[] | 利用可能な価格モデル(CPM、CPCV など)。オークションオプションは floor_price とオプションの price_guidance を含む場合があります。入札ベースのオークションモデル(CPM、vCPM、CPC、CPCV、CPV)はオプションの max_bid(boolean)を含む場合もあります。 |
shows | ShowSelector[] | (任意)このプロダクトで利用可能な番組。各エントリは publisher_domain と show_ids を持ちます。バイヤーは参照先の adagents.json から完全な番組オブジェクトを解決します。Shows and episodes 参照。 |
show_targeting_allowed | boolean | (任意、デフォルト: false)バイヤーがこのプロダクトの番組のサブセットをターゲットできるかどうか。false の場合、プロダクトはバンドルです。 |
brief_relevance | string | ブリーフに合致する理由(ブリーフ提供時) |
Proposals 配列(任意)
パブリッシャーはプロダクトと併せてプロポーザル(予算配分付きの構造化メディアプラン)を返すことがあります。詳細は Proposals を参照。
| Field | Type | Description |
|---|
proposal_id | string | create_media_buy でこのプロポーザルを実行するための一意 ID |
name | string | メディアプランの人が読める名称 |
allocations | ProductAllocation[] | プロダクト間の予算配分(合計 100% 必須)。各配分にはフライトごとのスケジューリング用にオプションの start_time と end_time を含む場合があります。 |
forecast | DeliveryForecast | プロポーザルの集計配信予測。メトリクスの範囲を持つ予測ポイントを含みます。Delivery Forecasts 参照 |
total_budget_guidance | object | 最小/推奨/最大の予算ガイダンス(任意) |
brief_alignment | string | キャンペーンブリーフへの対応内容 |
expires_at | string | プロポーザルの有効期限 (ISO 8601) |
ページネーション
大規模プロダクトカタログにはカーソルベースのページネーションを使います。
| Request Parameter | Type | Description |
|---|
pagination.max_results | integer | ページあたりの最大プロダクト数(1〜100、デフォルト: 50) |
pagination.cursor | string | 次のページを取得するための前のレスポンスのカーソル |
| Response Field | Type | Description |
|---|
pagination.has_more | boolean | さらにプロダクトがあるかどうか |
pagination.cursor | string | 次のページを取得するために渡すカーソル |
pagination.total_count | integer | マッチするプロダクトの合計数(任意。すべてのバックエンドがサポートするわけではない) |
ページネーションは任意です。省略した場合、サーバーはすべての結果(またはサーバーが選択したデフォルトページ)を返します。レスポンスに pagination.has_more: true が含まれる場合、次のリクエストで pagination.cursor を渡して次のページを取得します。
レスポンスメタデータ
| Field | Type | Description |
|---|
property_list_applied | boolean | [AdCP 3.0] 提供された property_list に基づきフィルタした場合 true。未指定または非対応なら省略/false。 |
catalog_applied | boolean | セラーが提供された catalog に基づき結果をフィルタした場合 true。カタログが提供されていないか、セラーがカタログマッチングをサポートしない場合は省略/false。 |
refinement_applied | RefinementResult[] | 各 refine エントリへのセラーの確認応答(位置でマッチ)。buying_mode が "refine" の場合のみ存在します。上記 refinement_applied 参照。 |
incomplete | IncompleteEntry[] | time_budget 内またはセラー内部の制限により完了できなかった内容を宣言します。各エントリはスコープと人が読める説明を持ちます。レスポンスが完全に完了している場合は省略されます。後述の incomplete 配列 参照。 |
incomplete 配列
time_budget 内(またはセラー自身の内部制限により)すべての作業を完了できない場合、レスポンスには欠けている内容を宣言する incomplete 配列が含まれます。バイヤーは estimated_wait を使って、より大きな予算でリトライするかどうかを判断できます。
| Field | Type | Required | Description |
|---|
scope | string | Yes | "products": すべてのインベントリソースを検索できなかった。"pricing": プロダクトは返されたが価格が欠けているか未確定。"forecast": プロダクトは返されたが予測データが欠けています。"proposals": プロポーザルが生成されないか不完全。 |
description | string | Yes | 欠けている内容とその理由の人が読める説明。 |
estimated_wait | Duration | No | このスコープを解決するのに必要な追加時間。 |
完全なフィールドはスキーマを参照: get-products-response.json
よくあるシナリオ
タイムバジェット付きの探索
素早い結果が必要で部分的なデータを許容できる場合、タイムバジェットを宣言します。セラーはバジェット内で達成できる最善の結果を返し、不完全な内容を宣言します。
import { testAgent } from '@adcp/client/testing';
const result = await testAgent.getProducts({
buying_mode: 'brief',
brief: 'CTV and display for brand awareness',
brand: {
domain: 'acmecorp.com'
},
time_budget: {
interval: 10,
unit: 'seconds'
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} products`);
if (result.data.incomplete) {
for (const entry of result.data.incomplete) {
console.log(`Incomplete: ${entry.scope} — ${entry.description}`);
if (entry.estimated_wait) {
console.log(` Would resolve in ${entry.estimated_wait.interval} ${entry.estimated_wait.unit}`);
}
}
}
}
不完全なデータを含むレスポンスの例 — プロダクトは返されているが一部のスコープが欠けている:
{
"products": [
{
"product_id": "prog-display-ros",
"name": "Programmatic Display — Run of Site",
"delivery_type": "non_guaranteed",
"pricing_options": [{ "pricing_option_id": "cpm-ros", "pricing_model": "cpm", "currency": "USD", "fixed_price": 12.00 }]
}
],
"incomplete": [
{
"scope": "products",
"description": "Premium inventory not searched — requires publisher approval",
"estimated_wait": { "interval": 60, "unit": "minutes" }
},
{
"scope": "forecast",
"description": "Forecast model did not complete within budget",
"estimated_wait": { "interval": 45, "unit": "seconds" }
}
]
}
スタンダードカタログの探索
import { testAgent } from '@adcp/client/testing';
// wholesale モード: バイヤー独自のオーディエンスを適用、パブリッシャーキュレーションなし
const result = await testAgent.getProducts({
buying_mode: 'wholesale',
brand: {
domain: 'acmecorp.com'
},
filters: {
delivery_type: 'non_guaranteed'
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} standard catalog products`);
}
マルチフォーマット探索
import { testAgent } from '@adcp/client/testing';
// video と display の両方をサポートするプロダクトを探す
const result = await testAgent.getProducts({
buying_mode: 'brief',
brief: 'Brand awareness campaign with video and display',
brand: {
domain: 'acmecorp.com'
},
filters: {
format_types: ['video', 'display']
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} products supporting video and display`);
}
予算と日付でのフィルタリング
import { testAgent } from '@adcp/client/testing';
// 指定国とチャンネルで、予算と期間に収まるプロダクトを探す
const result = await testAgent.getProducts({
buying_mode: 'brief',
brief: 'Q2 campaign for athletic footwear in North America',
brand: {
domain: 'acmecorp.com'
},
filters: {
start_date: '2025-04-01',
end_date: '2025-06-30',
budget_range: {
min: 50000,
max: 100000,
currency: 'USD'
},
countries: ['US', 'CA'],
channels: ['display', 'ctv', 'podcast'],
delivery_type: 'guaranteed'
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} products for Q2 within budget`);
}
プロパティタグの解決
import { testAgent } from '@adcp/client/testing';
// property_tags を持つプロダクトを取得
const result = await testAgent.getProducts({
buying_mode: 'brief',
brief: 'Sports content',
brand: {
domain: 'acmecorp.com'
}
});
if (result.success && result.data) {
// publisher_properties に property_tags がある場合は大規模ネットワークを意味する
// エージェントのポートフォリオは get_adcp_capabilities で確認する
const productsWithTags = result.data.products.filter(p =>
p.publisher_properties?.some(pub => pub.property_tags && pub.property_tags.length > 0)
);
console.log(`${productsWithTags.length} products use property tags (large networks)`);
}
保証配信のプロダクト
import { testAgent } from '@adcp/client/testing';
// Find guaranteed delivery products for measurement
const result = await testAgent.getProducts({
buying_mode: 'brief',
brief: 'Guaranteed delivery for lift study',
brand: {
domain: 'acmecorp.com'
},
filters: {
delivery_type: 'guaranteed',
min_exposures: 100000
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} guaranteed products with 100k+ exposures`);
}
標準フォーマットのみ
import { testAgent } from '@adcp/client/testing';
// IAB 標準フォーマットのみ受け付けるプロダクトを探す
const result = await testAgent.getProducts({
buying_mode: 'wholesale',
brand: {
domain: 'acmecorp.com'
},
filters: {
standard_formats_only: true
}
});
if (result.success && result.data) {
console.log(`Found ${result.data.products.length} products with standard formats only`);
}
カタログ主導の探索
catalog とブランドを使って、カタログアイテムを宣伝できる広告プロダクトを探す。セラーはアイテムをインベントリに照合し、マッチが存在するプロダクトを返します。
import { testAgent } from '@adcp/client/testing';
// 特定のカタログアイテム向けのリテールメディアプロダクトを探す
const result = await testAgent.getProducts({
buying_mode: 'wholesale',
brand: {
domain: 'acmecorp.com'
},
catalog: {
type: 'product',
tags: ['ketchup', 'organic'],
category: 'food/condiments'
},
filters: {
channels: ['retail_media']
}
});
if (result.success && result.data) {
if (result.data.catalog_applied) {
console.log(`Found ${result.data.products.length} products with catalog matches`);
} else {
console.log('Seller does not support catalog matching');
}
}
GTIN マッチング、同期済みカタログの参照、または他のカタログ種別向けのプロダクト探索も利用できます。
{
"catalog": {
"type": "product",
"gtins": ["00013000006040", "00013000006057"]
}
}
{
"catalog": {
"catalog_id": "gmc-primary",
"type": "product"
}
}
{
"catalog": {
"type": "job",
"catalog_id": "chef-vacancies"
}
}
プロパティリストでのフィルタリング
AdCP 3.0 - プロパティリストでのフィルタリングにはガバナンスエージェントの対応が必要です。
承認済みリストのプロパティで利用できるプロダクトだけに絞る。
import { testAgent } from '@adcp/client/testing';
// Filter products by property list from governance agent
const result = await testAgent.getProducts({
buying_mode: 'brief',
brief: 'Brand-safe inventory for family brand',
brand: {
domain: 'acmecorp.com'
},
property_list: {
agent_url: 'https://governance.example.com',
list_id: 'pl_brand_safe_2024'
}
});
if (result.success && result.data) {
// フィルタが適用されたか確認
if (result.data.property_list_applied) {
console.log(`Found ${result.data.products.length} products on approved properties`);
} else {
console.log('Agent does not support property list filtering');
console.log(`Found ${result.data.products.length} products (unfiltered)`);
}
}
注意: property_list_applied が省略または false の場合、セールスエージェントはプロダクトをフィルタリングしていません。これは以下の場合に発生する:
- エージェントがプロパティガバナンス機能をサポートしていません
- エージェントがプロパティリストにアクセスできなかった
- プロパティリストが利用可能なインベントリに影響しなかった
プロパティターゲティングの動作
プロダクトには property_targeting_allowed フラグがあり、フィルタリングに影響します。
property_targeting_allowed: false(デフォルト): プロダクトは「all or nothing」— あなたのリストがプロダクトのすべてのプロパティを含まない限り除外されます
property_targeting_allowed: true: プロダクトのプロパティとあなたのリストに交差がある場合にインクルードされます
これにより、パブリッシャーはバイヤーが個別選択できないラン・オブ・ネットワークプロダクトと、バイヤーがフィルタリングできる柔軟なインベントリを提供できます。
詳細は Property Targeting を参照。プロパティリストについては Property Governance を参照。
リファインメント
初回探索の後、buying_mode: "refine" を使って特定のプロダクトやプロポーザルを反復できます。refine 配列は変更依頼のリストで、各エントリはスコープとバイヤーが求める内容を宣言します。セラーは更新された価格と設定を持つプロダクトを返し、各依頼を refinement_applied で確認します。完全なガイドは Refinement を参照。
プロダクトのリファイン
各プロダクトエントリは明示的なアクションを宣言します。セットレベルの方向指示には scope: "request" を使います。フィルターは絶対値(デルタではない)を含むため、budget_range は新しいターゲット予算を意味します。
{
"buying_mode": "refine",
"refine": [
{ "scope": "request", "ask": "good selection but I want more video options and less display" },
{ "scope": "product", "id": "prod_premium_video", "action": "include", "ask": "add 16:9 format option" },
{ "scope": "product", "id": "prod_display_run_of_site", "action": "omit" },
{ "scope": "product", "id": "prod_native_feed", "action": "more_like_this", "ask": "same audience but video format" }
],
"filters": {
"start_date": "2026-04-01",
"end_date": "2026-04-30",
"budget_range": { "min": 200000, "max": 200000, "currency": "USD" }
}
}
プロポーザルのリファイン
プロポーザルを ID で参照して調整を依頼します。
{
"buying_mode": "refine",
"refine": [
{ "scope": "product", "id": "prod_premium_video", "action": "include" },
{ "scope": "product", "id": "prod_display_run_of_site", "action": "include" },
{ "scope": "proposal", "id": "prop_awareness_q2", "action": "include", "ask": "shift more budget toward video, reduce display allocation" }
],
"filters": {
"start_date": "2026-04-01",
"end_date": "2026-05-31",
"budget_range": { "min": 300000, "max": 300000, "currency": "USD" }
}
}
類似プロダクトの探索
more_like_this を使って気に入ったプロダクトと類似したプロダクトを探す。セラーは元のプロダクトと追加オプションを返します。
{
"buying_mode": "refine",
"refine": [
{ "scope": "product", "id": "prod_premium_video", "action": "more_like_this", "ask": "same premium audience but different formats" }
]
}
リファインモードでのプロポーザル
セラーはバイヤーがプロポーザルエントリを含めなかった場合でも、リファインされたプロダクトとともにプロポーザルを返してもよい。例えば、3 つのプロダクトをリファインしているバイヤーが、それらのプロダクトを更新された価格とともに受け取り、それらを組み合わせる方法を提案するプロポーザルも受け取ることがあります。
重要ポイント:
- プロポーザルは保証されない。 セラーはリファインモードでプロポーザルを生成する義務はない。配分とキャンペーン最適化は主にオーケストレーター(バイヤー側エージェント)の責任です。
- リクエストレベルの ask でプロポーザルへの関心を示します。
{ "scope": "request", "ask": "suggest how to combine these products" } を含めることで、プロポーザルを歓迎することを示せる。
- 未依頼のプロポーザルはリファインまたは無視できます。 セラーが依頼していないプロポーザルを返した場合、後続の呼び出しでリファインするか、単に無視して
create_media_buy でパッケージを手動で構築できます。
注意事項
refine は refine モードでのみ有効。 このフィールドを brief または wholesale モードで含むリクエストは INVALID_REQUEST で拒否されます。
- プロポーザルはエフェメラル。 プロポーザルには通常
expires_at タイムスタンプが含まれます。期限切れ後、セラーは PROPOSAL_EXPIRED を返します。新しい brief リクエストで再探索して新しいプロポーザルを取得します。expires_at のないプロポーザルはセラーが無効化するまで有効。
- プロダクト ID は安定したカタログ識別子。 セラーのインベントリを参照し、リクエスト時に検証されます。前回の呼び出しのプロダクト(別のブリーフのものも含む)をリファインでき、セラーは現在のリクエストコンテキストに対して適格性を確認します。カスタムプロダクト(
is_custom: true)には expires_at タイムスタンプがある場合があり、その後のリファインは PRODUCT_NOT_FOUND を返します。
レスポンスは探索と同じフォーマットを使い、各依頼を確認する refinement_applied が追加されます。各プロダクトは更新されたパラメーター(refine エントリとフィルターに基づいてセラーが推奨する設定変更と改訂された価格オプション)を反映します。create_media_buy にコミットする前に何度でもリファインを呼び出せる。
クライアントバリデーション
リファインメントワークフローを構築するオーケストレーターは送信前にリクエストを検証すべきです。
- refine は空にできません。
refine 配列には少なくとも 1 エントリが必要。空の [] はスキーマバリデーションで拒否されます。
- 有効なエントリ。 各プロダクトエントリには
scope、id、および action(include、omit、または more_like_this)が必要。各プロポーザルエントリには scope、id、および action(include または omit)が必要。リクエストレベルのエントリには scope と ask が必要。
- フィルターは絶対値。 リファイン時、
filters は前のリクエストからのデルタではなく、適用したい完全なターゲット状態を表します。適用したいフィルターのフルセットを常に送信すること。
クライアント実装は送信前にリファインメントリクエストを リクエストスキーマ に対してバリデーションすべきです。
Error Handling
| Error Code | Description | Resolution |
|---|
AUTH_REQUIRED | 完全なカタログには認証が必要 | auth ヘッダーで認証情報を提供する |
INVALID_REQUEST | ブリーフが長すぎるか、フィルターが不正 | リクエストパラメーターを確認する |
PRODUCT_NOT_FOUND | 1 つ以上のプロダクト ID が未知または期限切れ | 無効な ID を削除して再試行するか、brief リクエストで再探索する |
PROPOSAL_EXPIRED | 参照したプロポーザル ID が expires_at タイムスタンプを過ぎている | 新しい brief または wholesale リクエストで再探索する |
POLICY_VIOLATION | 広告主に対してカテゴリがブロックされている | ポリシーレスポンスメッセージで詳細を確認する |
Authentication Comparison
認証あり・なしのアクセスの違いを確認します。
import { testAgent, testAgentNoAuth } from '@adcp/client/testing';
// WITH authentication - full catalog with pricing
const fullCatalog = await testAgent.getProducts({
buying_mode: 'brief',
brief: 'Premium CTV inventory for brand awareness',
brand: {
domain: 'acmecorp.com'
}
});
if (!fullCatalog.success) {
throw new Error(`Failed to get products: ${fullCatalog.error}`);
}
console.log(`With auth: ${fullCatalog.data.products.length} products`);
console.log(`First product pricing: ${fullCatalog.data.products[0].pricing_options.length} options`);
// WITHOUT authentication - limited public catalog
const publicCatalog = await testAgentNoAuth.getProducts({
buying_mode: 'brief',
brief: 'Premium CTV inventory for brand awareness',
brand: {
domain: 'acmecorp.com'
}
});
if (!publicCatalog.success) {
throw new Error(`Failed to get products: ${publicCatalog.error}`);
}
console.log(`Without auth: ${publicCatalog.data.products.length} products`);
console.log(`First product pricing: ${publicCatalog.data.products[0].pricing_options?.length || 0} options`);
主な違い:
- プロダクト数: 認証ありのアクセスはプライベート/カスタムオファリングを含む多くのプロダクトを返す
- 価格情報: 認証ありのリクエストのみ詳細な価格オプション(CPM、CPCV など)を受け取れる
- ターゲティング詳細: カスタムターゲティング機能は認証ユーザーに限定される場合があります
- レート制限: 認証なしのリクエストはレート制限が低い
Authentication Behavior
- 認証情報なし: 制限されたカタログ(標準カタログプロダクト)を返します。価格なし、カスタムオファリングなし
- 認証情報あり: 価格とカスタムプロダクトを含む完全なカタログを返す
詳細は Authentication Guide を参照。
Asynchronous Operations
ほとんどのプロダクト検索は即時完了するが、一部のシナリオでは非同期処理が必要になります。その場合、completed 以外のステータスを受け取り、Webhook またはポーリングで進捗を追跡できます。
非同期処理が発生するケース
以下の状況でプロダクト検索に非同期処理が必要になる場合があります。
- 複雑な検索: 複数のインベントリソースをまたぐ検索やカスタムキュレーション
- 追加確認が必要: ブリーフが曖昧でシステムが追加情報を必要とします
- カスタムプロダクト: 人間のレビューが必要なオーダーメイドのプロダクトパッケージ
Async Status Flow
即時完了(最も一般的)
POST /api/mcp/call_tool
{
"name": "get_products",
"arguments": {
"buying_mode": "brief",
"brief": "CTV inventory for sports audience",
"brand": { "domain": "acmecorp.com" }
}
}
Response (200 OK):
{
"status": "completed",
"message": "Found 3 products matching your requirements",
"products": [...]
}
Needs Clarification
ブリーフが不明確な場合、システムは詳細情報を求める。Response (200 OK):
{
"status": "input-required",
"message": "I need a bit more information. What's your budget range and campaign duration?",
"task_id": "task_789",
"context_id": "ctx_123",
"reason": "CLARIFICATION_NEEDED",
"partial_results": [],
"suggestions": ["$50K-$100K", "1 month", "Q1 2024"]
}
同じ context_id で会話を続ける:POST /api/mcp/continue
{
"context_id": "ctx_123",
"message": "Budget is $75K for a 3-week campaign in March"
}
Response (200 OK):
{
"status": "completed",
"message": "Perfect! Found 5 products within your budget",
"products": [...]
}
Complex Search (With Webhook)
深いインベントリ分析が必要な検索には Webhook を設定します。POST /api/mcp/call_tool
{
"name": "get_products",
"arguments": {
"buying_mode": "brief",
"brief": "Premium inventory across all formats for luxury automotive brand",
"brand": { "domain": "acmecorp.com" },
"pushNotificationConfig": {
"url": "https://buyer.com/webhooks/adcp/get_products",
"authentication": {
"schemes": ["Bearer"],
"credentials": "secret_token_32_chars"
}
}
}
}
Response (200 OK):
{
"status": "working",
"message": "Searching premium inventory across display, video, and audio",
"task_id": "task_456",
"context_id": "ctx_123",
"percentage": 10,
"current_step": "searching_inventory"
}
// 後で https://buyer.com/webhooks/adcp/get_products に Webhook POST が届く
{
"task_id": "task_456",
"task_type": "get_products",
"status": "completed",
"timestamp": "2025-01-22T10:30:00Z",
"message": "Found 12 premium products across all formats",
"result": {
"products": [...]
}
}
POST /api/a2a
{
"message": {
"role": "user",
"parts": [{
"kind": "data",
"data": {
"skill": "get_products",
"parameters": {
"buying_mode": "brief",
"brief": "CTV inventory for sports audience",
"brand": { "domain": "acmecorp.com" }
}
}
}]
}
}
Response (200 OK):
{
"id": "task_123",
"contextId": "ctx_456",
"artifact": {
"kind": "data",
"data": {
"products": [...]
}
},
"status": {
"state": "completed",
"message": {
"role": "agent",
"parts": [{ "text": "Found 3 products matching your requirements" }]
}
}
}
追加確認が必要な場合
確認が必要な場合、SSE でリアルタイム更新を受け取ります。// Initial response
{
"id": "task_789",
"contextId": "ctx_123",
"status": {
"state": "input-required",
"message": {
"role": "agent",
"parts": [
{ "text": "I need a bit more information. What's your budget range and campaign duration?" },
{
"data": {
"reason": "CLARIFICATION_NEEDED",
"suggestions": ["$50K-$100K", "1 month", "Q1 2024"]
}
}
]
}
}
}
// 追いメッセージを送る
POST /api/a2a
{
"contextId": "ctx_123",
"message": {
"role": "user",
"parts": [{ "text": "Budget is $75K for a 3-week campaign in March" }]
}
}
// SSE 更新: タスク完了
{
"id": "task_789",
"contextId": "ctx_123",
"artifact": {
"kind": "data",
"data": { "products": [...] }
},
"status": {
"state": "completed",
"message": {
"role": "agent",
"parts": [{ "text": "Perfect! Found 5 products within your budget" }]
}
}
}
複雑な検索(Webhook 併用)
長時間かかる検索ではプッシュ通知を設定します。POST /api/a2a
{
"message": {
"role": "user",
"parts": [{
"kind": "data",
"data": {
"skill": "get_products",
"parameters": {
"buying_mode": "brief",
"brief": "Premium inventory across all formats for luxury automotive brand",
"brand": { "domain": "acmecorp.com" }
}
}
}]
},
"pushNotificationConfig": {
"url": "https://buyer.com/webhooks/a2a/get_products",
"authentication": {
"schemes": ["bearer"],
"credentials": "secret_token_32_chars"
}
}
}
Response (200 OK):
{
"id": "task_456",
"contextId": "ctx_789",
"status": {
"state": "working",
"message": {
"role": "agent",
"parts": [
{ "text": "Searching premium inventory across display, video, and audio" },
{
"data": {
"percentage": 10,
"current_step": "searching_inventory"
}
}
]
}
}
}
// 後で https://buyer.com/webhooks/a2a/get_products に Webhook POST が届く
{
"id": "task_456",
"contextId": "ctx_789",
"artifact": {
"kind": "data",
"data": {
"products": [...]
}
},
"status": {
"state": "completed",
"message": {
"role": "agent",
"parts": [
{ "text": "Found 12 premium products across all formats" },
{
"data": {
"products": [...]
}
}
]
},
"timestamp": "2025-01-22T10:30:00Z"
}
}
ステータス概要
| Status | 発生タイミング | 対応 |
|---|
completed | 検索が正常完了 | プロダクト結果を処理 |
input-required | ブリーフに追加確認が必要 | 質問に回答して続行 |
working | 複数ソースを検索中 | Webhook を待つかポーリング |
submitted | カスタムキュレーションがキュー入り | Webhook 通知を待つ |
failed | 検索を完了できなかった | エラーメッセージを確認しブリーフ調整 |
注意: 完全なステータス一覧は Task Lifecycle を参照。
ほとんどの検索は即時完了します。 非同期処理が必要なのは複雑なケースや追加入力が必要な場合のみ。
次のステップ
プロダクトを見つけたら:
- 選択肢を確認: プロダクト、価格、ターゲティング能力を比較
- メディアバイ作成:
create_media_buy でキャンペーンを実行
- クリエイティブ準備:
list_creative_formats で要件を確認
- アセットアップロード:
sync_creatives でアセットを提供
さらに学ぶ