Skip to main content
選択したパッケージからメディアバイを作成するか、プロポーザルを実行します。必要に応じたバリデーションや承認、キャンペーン作成を処理します。 2 つのモードをサポート:
  • Manual Mode: packages 配列で明示的にラインアイテムを指定
  • Proposal Mode: proposal_idtotal_budget を指定し、get_products のプロポーザルを実行
Response Time: 即時〜日単位(completed、120 秒未満の working、数時間〜数日の submitted Request Schema: /schemas/v2/media-buy/create-media-buy-request.json Response Schema: /schemas/v2/media-buy/create-media-buy-response.json

クイックスタート

2 つのパッケージでシンプルなメディアバイを作成:
import { testAgent } from '@adcp/client/testing';
import { CreateMediaBuyResponseSchema } from '@adcp/client';

// Calculate dates dynamically - start tomorrow, end in 90 days
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(0, 0, 0, 0);
const endDate = new Date(tomorrow);
endDate.setDate(endDate.getDate() + 90);

const result = await testAgent.createMediaBuy({
  buyer_ref: 'summer_campaign_' + Date.now(),
  brand_manifest: {
    name: 'Nike',
    url: 'https://nike.com'
  },
  packages: [
    {
      buyer_ref: 'display_package_1',
      product_id: 'prod_d979b543',
      pricing_option_id: 'cpm_usd_fixed',
      format_ids: [
        {
          agent_url: 'https://creative.adcontextprotocol.org',
          id: 'display_300x250_image'
        }
      ],
      budget: 2500,
      bid_price: 5.00
    },
    {
      buyer_ref: 'display_package_2',
      product_id: 'prod_e8fd6012',
      pricing_option_id: 'cpm_usd_fixed',
      format_ids: [
        {
          agent_url: 'https://creative.adcontextprotocol.org',
          id: 'display_300x250_html'
        }
      ],
      budget: 2500,
      bid_price: 4.50
    }
  ],
  start_time: tomorrow.toISOString(),
  end_time: endDate.toISOString()
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

// Validate response against schema
const validated = CreateMediaBuyResponseSchema.parse(result.data);

// Check for errors (discriminated union response)
if ('errors' in validated && validated.errors) {
  throw new Error(`Failed to create media buy: ${JSON.stringify(validated.errors)}`);
}

if ('media_buy_id' in validated) {
  console.log(`Created media buy ${validated.media_buy_id}`);
  console.log(`Upload creatives by: ${validated.creative_deadline}`);
  console.log(`Packages created: ${validated.packages.length}`);
}

リクエストパラメーター

ParameterTypeRequiredDescription
buyer_refstringYesこのメディアバイの参照 ID
proposal_idstringNo*get_products のプロポーザル ID。packages の代替
total_budgetTotalBudgetNo*プロポーザル実行時の総予算。配分割合はパブリッシャーが適用
packagesPackage[]No*パッケージ構成の配列(下記)。proposal_id を使わない場合は必須
brand_manifestBrandManifestRefYesブランド情報。営業エージェントでは名前/URL の最小構成で可。Brand Manifest 参照
start_timestringYes"asap" または ISO 8601 日時
end_timestringYesISO 8601 日時(指定がなければ UTC)
po_numberstringNo発注番号
reporting_webhookReportingWebhookNoレポーティングの自動配信設定
* Either packages OR (proposal_id + total_budget) must be provided.

TotalBudget オブジェクト

ParameterTypeRequiredDescription
amountnumberYes総予算額
currencystringYesISO 4217 通貨コード

Package オブジェクト

ParameterTypeRequiredDescription
buyer_refstringYesこのパッケージの参照 ID
product_idstringYesget_products で取得した product_id
pricing_option_idstringYesプロダクトの pricing_options 配列にある価格オプション ID
format_idsFormatID[]Yes使用するフォーマット ID(プロダクトでサポートされている必要あり)
budgetnumberYes価格オプションの通貨での予算
impressionsnumberNoこのパッケージのインプレッション目標
pausedbooleanNo一時停止状態で作成する場合(デフォルト: false
pacingstringNo"even"(デフォルト)、"asap""front_loaded"
bid_pricenumberNoオークション価格の場合の入札額(fixed_price がない価格オプションでは必須)
targeting_overlayTargetingOverlayNo追加ターゲティング条件(Targeting 参照)
creative_assignmentsCreativeAssignment[]No既存ライブラリのクリエイティブを重み/プレースメント指定付きで割り当て
creativesCreativeAsset[]No新規アセットをアップロードして割り当て(creative_id は未使用であること)

レスポンス

成功レスポンス

FieldDescription
media_buy_idパブリッシャーの一意 ID
buyer_refバイヤー側の参照 ID
creative_deadlineクリエイティブ提出期限 (ISO 8601)
packages作成されたパッケージの配列(完全な状態付き)

エラーレスポンス

FieldDescription
errors失敗理由を示すエラーオブジェクト配列
Note: レスポンスは判別可能なユニオン。成功フィールドか errors のどちらかのみ返されるため、成功フィールドにアクセスする前に常に errors を確認してください。

主なシナリオ

ターゲティング付きキャンペーン

地理制限やフリークエンシーキャップを追加:
import { testAgent } from '@adcp/client/testing';
import { CreateMediaBuyResponseSchema } from '@adcp/client';

// Calculate end date dynamically - 90 days from now
const endDate = new Date();
endDate.setDate(endDate.getDate() + 90);

const result = await testAgent.createMediaBuy({
  buyer_ref: 'regional_campaign_' + Date.now(),
  brand_manifest: {
    name: 'Regional Retailer',
    url: 'https://example-retailer.com'
  },
  packages: [{
    buyer_ref: 'ctv_regional',
    product_id: 'prod_d979b543',
    pricing_option_id: 'cpm_usd_fixed',
    format_ids: [{
      agent_url: 'https://creative.adcontextprotocol.org',
      id: 'display_300x250_image'
    }],
    budget: 2500,
    bid_price: 5.00,
    targeting_overlay: {
      geo_country_any_of: ['US'],
      geo_region_any_of: ['CA', 'NY'],
      frequency_cap: {
        suppress_minutes: 60
      }
    }
  }],
  start_time: 'asap',
  end_time: endDate.toISOString()
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

const validated = CreateMediaBuyResponseSchema.parse(result.data);
if ('errors' in validated && validated.errors) {
  throw new Error(`Creation failed: ${JSON.stringify(validated.errors)}`);
}

if ('media_buy_id' in validated) {
  console.log(`Campaign ${validated.media_buy_id} created with targeting`);
}

クリエイティブをインライン指定したキャンペーン

Upload creatives at the same time as creating the campaign:
import { testAgent } from '@adcp/client/testing';
import { CreateMediaBuyResponseSchema } from '@adcp/client';

// Calculate end date dynamically - 90 days from now
const endDate = new Date();
endDate.setDate(endDate.getDate() + 90);

const result = await testAgent.createMediaBuy({
  buyer_ref: 'campaign_with_creatives_' + Date.now(),
  brand_manifest: {
    name: 'Nike',
    url: 'https://nike.com'
  },
  packages: [{
    buyer_ref: 'ctv_package',
    product_id: 'prod_d979b543',
    pricing_option_id: 'cpm_usd_fixed',
    format_ids: [{
      agent_url: 'https://creative.adcontextprotocol.org',
      id: 'display_300x250_image'
    }],
    budget: 2500,
    bid_price: 5.00,
    creatives: [{
      creative_id: 'hero_video_30s',
      name: 'Hero Video',
      format_id: {
        agent_url: 'https://creative.adcontextprotocol.org',
        id: 'display_300x250_image'
      },
      assets: {
        image: {
          url: 'https://cdn.example.com/hero-banner.jpg',
          width: 300,
          height: 250
        }
      }
    }]
  }],
  start_time: 'asap',
  end_time: endDate.toISOString()
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

const validated = CreateMediaBuyResponseSchema.parse(result.data);
if ('errors' in validated && validated.errors) {
  throw new Error(`Creation failed: ${JSON.stringify(validated.errors)}`);
}

if ('packages' in validated) {
  console.log(`Campaign created with ${validated.packages[0].creative_assignments.length} creatives`);
}

レポート用 Webhook を設定したキャンペーン

自動レポート通知を受け取ります。
import { testAgent } from '@adcp/client/testing';
import { CreateMediaBuyResponseSchema } from '@adcp/client';

// Calculate end date dynamically - 90 days from now
const endDate = new Date();
endDate.setDate(endDate.getDate() + 90);

const result = await testAgent.createMediaBuy({
  buyer_ref: 'campaign_with_reporting_' + Date.now(),
  brand_manifest: {
    name: 'Nike',
    url: 'https://nike.com'
  },
  packages: [{
    buyer_ref: 'ctv_package',
    product_id: 'prod_d979b543',
    pricing_option_id: 'cpm_usd_fixed',
    format_ids: [{
      agent_url: 'https://creative.adcontextprotocol.org',
      id: 'display_300x250_image'
    }],
    budget: 2500,
    bid_price: 5.00
  }],
  start_time: 'asap',
  end_time: endDate.toISOString(),
  reporting_webhook: {
    url: 'https://buyer.example.com/webhooks/reporting',
    authentication: {
      schemes: ['Bearer'],
      credentials: 'secret_token_xyz_minimum_32_chars'
    },
    reporting_frequency: 'daily',
    requested_metrics: ['impressions', 'spend', 'video_completions']
  }
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

const validated = CreateMediaBuyResponseSchema.parse(result.data);
if ('errors' in validated && validated.errors) {
  throw new Error(`Creation failed: ${JSON.stringify(validated.errors)}`);
}

if ('media_buy_id' in validated) {
  console.log(`Campaign created - daily reports will be sent to webhook`);
}

プロポーザルの実行

get_products のプロポーザルを、パッケージを手作業で組まずに実行します。
import { testAgent } from '@adcp/client/testing';
import { CreateMediaBuyResponseSchema } from '@adcp/client';

// Calculate end date dynamically - 90 days from now
const endDate = new Date();
endDate.setDate(endDate.getDate() + 90);

const result = await testAgent.createMediaBuy({
  buyer_ref: 'swiss_campaign_' + Date.now(),
  proposal_id: 'swiss_balanced_v2',  // From get_products response
  total_budget: {
    amount: 50000,
    currency: 'USD'
  },
  brand_manifest: {
    name: 'Swiss Brand',
    url: 'https://example-swiss.com'
  },
  start_time: 'asap',
  end_time: endDate.toISOString()
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

const validated = CreateMediaBuyResponseSchema.parse(result.data);
if ('errors' in validated && validated.errors) {
  throw new Error(`Creation failed: ${JSON.stringify(validated.errors)}`);
}

if ('media_buy_id' in validated) {
  // パブリッシャーがプロポーザルの配分をパッケージに変換
  console.log(`Created media buy ${validated.media_buy_id}`);
  console.log(`Packages created: ${validated.packages.length}`);
}
プロポーザルを実行する際:
  • パブリッシャーが total_budget を使って配分割合を実額に変換
  • 配分に基づきパッケージが自動生成
  • それ以外のフィールド(brand_manifest、start_time、end_time など)は Manual モードと同じ
会話的なリファインを含む完全なフローは Proposals を参照。

エラーハンドリング

よくあるエラーと解決策:
Error CodeDescriptionResolution
PRODUCT_NOT_FOUNDInvalid product_idVerify product exists via get_products
FORMAT_INCOMPATIBLEFormat not supported by productCheck product’s format_ids field
BUDGET_INSUFFICIENTBudget below product minimumIncrease budget or choose different product
TARGETING_TOO_NARROWTargeting yields zero inventoryBroaden geographic or audience criteria
POLICY_VIOLATIONBrand/product violates policyReview publisher’s content policies
INVALID_PRICING_OPTIONpricing_option_id not foundUse ID from product’s pricing_options
CREATIVE_ID_EXISTSCreative ID already exists in libraryUse a different creative_id, assign existing creatives via creative_assignments, or update via sync_creatives
Example error response:
{
  "errors": [{
    "code": "FORMAT_INCOMPATIBLE",
    "message": "Product 'prod_d979b543' does not support format 'display_728x90'",
    "field": "packages[0].format_ids",
    "suggestion": "Use display_300x250_image, display_300x250_html, or display_300x250_generative formats"
  }]
}

Key Concepts

Format Specification

Format IDs are required for each package because:
  • Publishers create placeholder creatives in ad servers
  • Both parties know exactly what creative assets are needed
  • Validation ensures products support requested formats
  • Progress tracking shows which assets are missing
See Format Workflow below for complete details.

Brand Manifest

The brand_manifest field identifies the advertiser for policy compliance and business purposes. Minimal manifests are acceptable for sales agents:
{
  "brand_manifest": {
    "name": "ACME Corporation",
    "url": "https://acmecorp.com"
  }
}
Full manifests with colors, fonts, and product catalogs are only needed for creative generation. See Brand Manifest.

Pricing & Currency

Each package specifies its pricing_option_id, which determines:
  • Currency (USD, EUR, etc.)
  • Pricing model (CPM, CPCV, CPP, etc.)
  • Rate and whether it’s fixed or auction-based
Packages can use different currencies when sellers support it. See Pricing Models.

Targeting Overlays

Use sparingly - most targeting should be in your brief and handled through product selection. オーバーレイは以下に限定して使用します。
  • 地理的制限(RCT テスト、規制対応)
  • フリークエンシーキャップ
  • AXE セグメントの包含/除外
詳細は Targeting を参照。

フォーマットワークフロー

フォーマット指定が重要な理由

メディアバイ作成時にフォーマットを指定すると次が可能になります。
  1. プレースホルダー作成 - パブリッシャーが正しい仕様でアドサーバーにプレースホルダーを用意
  2. 検証 - 要求フォーマットをプロダクトがサポートするかシステムが検証
  3. 期待値の明確化 - 双方が必要なものを正確に把握
  4. 進捗トラッキング - 不足アセットと必須アセットを可視化
  5. 技術セットアップ - クリエイティブ到着前にアドサーバーを設定

完全なワークフロー

1. list_creative_formats → 利用可能なフォーマット仕様を取得
2. get_products → プロダクトを発見(サポートする format_ids を返す)
3. 互換性を検証 → 望むフォーマットをプロダクトがサポートするか確認
4. create_media_buy → フォーマットを指定(必須)
   └── パブリッシャーがプレースホルダーを作成
   └── クリエイティブ要件を明確化
5. sync_creatives → フォーマットに合致する実ファイルをアップロード
6. キャンペーン有効化 → プレースホルダーを実クリエイティブに置換

フォーマット検証

パブリッシャーが必ず検証する事項:
  • すべてのフォーマットがプロダクトでサポートされている
  • フォーマット仕様が list_creative_formats の出力と一致
  • 期限内にクリエイティブ要件を満たせる
無効なフォーマットの例:
{
  "errors": [{
    "code": "FORMAT_INCOMPATIBLE",
    "message": "Product 'ctv_sports_premium' does not support format 'audio_standard_30s'",
    "field": "packages[0].format_ids",
    "supported_formats": [
      { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_standard_30s" },
      { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_standard_15s" }
    ]
  }]
}

非同期オペレーション

このタスクは即時完了する場合も、複雑さや承認要件によっては日数を要する場合もあります。レスポンスの status フィールドで結果と次のアクションを確認してください。
StatusMeaningYour Action
completed即時完了結果を処理
working処理中(約2分)高頻度でポーリング or Webhook を待つ
submitted長時間(数時間〜日)Webhook を使うか低頻度でポーリング
input-required追加情報が必要メッセージを読み、情報を返す
failedエラー発生エラーに対応
Note: 完全なステータス一覧は Task Lifecycle を参照。

即時成功 (completed)

タスクが同期的に完了しました。非同期処理は不要です。Request:
test=false
const response = await session.call('create_media_buy', {
  buyer_ref: 'summer_campaign_2025',
  brand_manifest: { name: 'Nike', url: 'https://nike.com' },
  packages: [
    {
      buyer_ref: 'ctv_package',
      product_id: 'prod_ctv_sports',
      pricing_option_id: 'cpm_fixed',
      budget: 50000
    }
  ]
});
Response:
{
  "status": "completed",
  "media_buy_id": "mb_12345",
  "buyer_ref": "summer_campaign_2025",
  "creative_deadline": "2025-06-15T23:59:59Z",
  "packages": [
    {
      "package_id": "pkg_001",
      "buyer_ref": "ctv_package",
      "product_id": "prod_ctv_sports"
    }
  ]
}

長時間処理 (submitted)

タスクが手動承認キューに入っています。更新を受け取るため Webhook を設定します。Request with webhook:
test=false
const response = await session.call('create_media_buy',
  {
    buyer_ref: 'enterprise_campaign',
    brand_manifest: { name: 'Nike', url: 'https://nike.com' },
    packages: [
      {
        buyer_ref: 'premium_package',
        product_id: 'prod_premium_ctv',
        pricing_option_id: 'cpm_fixed',
        budget: 500000  // Large budget triggers approval
      }
    ]
  },
  {
    pushNotificationConfig: {
      url: 'https://your-app.com/webhooks/adcp',
      authentication: {
        schemes: ['bearer'],
        credentials: 'your_webhook_secret'
      }
    }
  }
);
Initial response:
{
  "status": "submitted",
  "task_id": "task_abc123",
  "message": "Budget exceeds auto-approval limit. Sales review required (2-4 hours)."
}
Webhook POST when approved:
{
  "task_id": "task_abc123",
  "task_type": "create_media_buy",
  "status": "completed",
  "timestamp": "2025-01-22T14:30:00Z",
  "message": "Media buy approved and created",
  "result": {
    "media_buy_id": "mb_67890",
    "buyer_ref": "enterprise_campaign",
    "creative_deadline": "2025-06-20T23:59:59Z",
    "packages": [
      {
        "package_id": "pkg_002",
        "buyer_ref": "premium_package"
      }
    ]
  }
}

Error (failed)

Response:
{
  "status": "failed",
  "errors": [
    {
      "code": "INSUFFICIENT_INVENTORY",
      "message": "Requested targeting yields no available impressions",
      "field": "packages[0].targeting",
      "suggestion": "Expand geographic targeting or increase CPM bid"
    }
  ]
}
For complete async handling patterns, see Async Operations.

Usage Notes

  • Total budget is distributed across packages based on individual budget values
  • Both media buys and packages have buyer_ref fields for tracking
  • Creative assets must be uploaded before deadline for campaign activation
  • AXE segments enable advanced audience targeting
  • Pending states (working, submitted) are normal, not errors
  • Orchestrators MUST handle pending states as part of normal workflow
  • Inline creatives: The creatives array creates NEW creatives only. To update existing creatives, use sync_creatives. To assign existing library creatives, use creative_assignments instead.

Policy Compliance

Brand and products are validated during creation. Policy violations return errors:
{
  "errors": [{
    "code": "POLICY_VIOLATION",
    "message": "Brand or product category not permitted on this publisher",
    "field": "brand_manifest",
    "suggestion": "Contact publisher for category approval process"
  }]
}
Publishers should ensure:
  • Brand/products align with selected packages
  • Creatives match declared brand/products
  • Campaign complies with all advertising policies

Next Steps

After creating a media buy:
  1. Upload Creatives: Use sync_creatives before deadline
  2. Monitor Status: Use get_media_buy_delivery
  3. Optimize: Use provide_performance_feedback
  4. Update: Use update_media_buy to modify campaign

Learn More