Skip to main content
Create a media buy from selected packages or execute a proposal. Handles validation, approval if needed, and campaign creation. Supports two modes:
  • Manual Mode: Provide packages array with explicit line item configurations
  • Proposal Mode: Provide proposal_id and total_budget to execute a proposal from get_products
Response Time: Instant to days (returns completed, working < 120s, or submitted for hours/days) Request Schema: /schemas/v3/media-buy/create-media-buy-request.json Response Schema: /schemas/v3/media-buy/create-media-buy-response.json

Quick Start

Create a simple media buy with two packages:
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({
  brand: {
    domain: 'acmecorp.com'
  },
  packages: [
    {
      product_id: 'prod_d979b543',
      pricing_option_id: 'cpm_usd_auction',
      format_ids: [
        {
          agent_url: 'https://creative.adcontextprotocol.org',
          id: 'display_300x250_image'
        }
      ],
      budget: 2500,
      bid_price: 5.00
    },
    {
      product_id: 'prod_e8fd6012',
      pricing_option_id: 'cpm_usd_auction',
      format_ids: [
        {
          agent_url: 'https://creative.adcontextprotocol.org',
          id: 'display_300x250_html'
        }
      ],
      budget: 2500,
      bid_price: 4.50
    }
  ],
  start_time: tomorrow.toISOString(),
  end_time: endDate.toISOString()
});

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

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

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

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

Request Parameters

ParameterTypeRequiredDescription
accountaccount-refYesAccount reference. Pass { "account_id": "..." } or { "brand": {...}, "operator": "..." } if the seller supports implicit resolution. Required for billing and policy evaluation.
proposal_idstringNo*ID of a proposal from get_products to execute. Alternative to providing packages.
total_budgetTotalBudgetNo*Total budget when executing a proposal. Publisher applies allocation percentages.
packagesPackage[]No*Array of package configurations (see below). Required when not using proposal_id.
brandBrandRefYesBrand reference — resolved to full identity at execution time. See brand.json
start_timestringYes"asap" or ISO 8601 date-time
end_timestringYesISO 8601 date-time (UTC unless timezone specified)
invoice_recipientBusinessEntityNoOverride the account’s default billing entity for this buy. The seller MUST validate the recipient is authorized and include it in check_governance when governance agents are configured.
po_numberstringNoPurchase order number
idempotency_keystringNoUnique key for safe retries. If a request with the same key and account has already been processed, the seller returns the existing media buy. MUST be unique per (seller, request) pair. Min 16 chars.
contextobjectNoOpaque correlation data echoed unchanged in the response. Use for internal tracking, trace IDs, or other caller-specific identifiers.
reporting_webhookReportingWebhookNoAutomated reporting delivery configuration
* Either packages OR (proposal_id + total_budget) must be provided.

TotalBudget Object

ParameterTypeRequiredDescription
amountnumberYesTotal budget amount
currencystringYesISO 4217 currency code

Package Object

ParameterTypeRequiredDescription
product_idstringYesProduct ID from get_products
pricing_option_idstringYesPricing option ID from product’s pricing_options array
format_idsFormatID[]YesFormat IDs that will be used - must be supported by product
budgetnumberYesBudget in currency specified by pricing option
impressionsnumberNoImpression goal for this package
pausedbooleanNoCreate package in paused state (default: false)
pacingstringNo"even" (default), "asap", or "front_loaded"
bid_pricenumberNoBid price for auction pricing. This is the exact bid/price to honor unless the selected pricing option has max_bid: true, in which case it is treated as the buyer’s maximum willingness to pay (ceiling).
optimization_goalsOptimizationGoal[]NoOptimization targets for this package. Each goal is either kind: "event" (conversion events with event_sources array, optional cost_per, per_ad_spend, or maximize_value target) or kind: "metric" (seller-native metric with optional cost_per or threshold_rate target). Event goals require conversion_tracking.supported_targets on the product; metric goals require metric_optimization.supported_metrics.
targeting_overlayTargetingOverlayNoAdditional targeting criteria (see Targeting)
start_timestringNoISO 8601 date-time for this package’s flight start. When omitted, inherits the media buy’s start_time. Must fall within the media buy’s date range. Does not support "asap".
end_timestringNoISO 8601 date-time for this package’s flight end. When omitted, inherits the media buy’s end_time. Must fall within the media buy’s date range.
creative_assignmentsCreativeAssignment[]NoAssign existing library creatives with optional weights and placement targeting
creativesCreativeAsset[]NoUpload new creative assets and assign (creative_id must not already exist in library)
contextobjectNoOpaque correlation data echoed unchanged in the package response. Use to map seller-assigned package_id back to your internal line items, campaign structure, or tracking state.

Response

Success Response

FieldDescription
media_buy_idSeller’s unique identifier
confirmed_atISO 8601 timestamp of order confirmation. A successful response constitutes confirmation.
creative_deadlineISO 8601 timestamp for creative upload deadline
packagesArray of created packages with complete state. Packages may include per-package creative_deadline when different from the media buy deadline.

Error Response

FieldDescription
errorsArray of error objects explaining failure
Note: Responses use discriminated unions - you get either success fields OR errors, never both. Always check for errors before accessing success fields.

Common Scenarios

Campaign with Targeting

Add geographic restrictions and frequency capping:
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({
  brand: {
    domain: 'acmecorp.com'
  },
  packages: [{
    product_id: 'prod_d979b543',
    pricing_option_id: 'cpm_usd_auction',
    format_ids: [{
      agent_url: 'https://creative.adcontextprotocol.org',
      id: 'display_300x250_image'
    }],
    budget: 2500,
    bid_price: 5.00,
    targeting_overlay: {
      geo_countries: ['US'],
      geo_regions: ['US-CA', 'US-NY'],
      frequency_cap: {
        suppress: { interval: 60, unit: 'minutes' }
      }
    }
  }],
  start_time: 'asap',
  end_time: endDate.toISOString()
});

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

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

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

Campaign with Conversion Optimization

Set a per_ad_spend target for conversion-optimized delivery. The product must declare support in conversion_tracking.supported_targets, and you must have an event source configured via sync_event_sources:
import { testAgent } from '@adcp/client/testing';
import { CreateMediaBuyResponseSchema } from '@adcp/client';

const endDate = new Date();
endDate.setDate(endDate.getDate() + 90);

const result = await testAgent.createMediaBuy({
  brand: {
    domain: 'acmecorp.com'
  },
  packages: [{
    product_id: 'prod_retail_sp',
    pricing_option_id: 'cpc_usd_auction',
    budget: 10000,
    bid_price: 1.20,
    optimization_goals: [{
      kind: 'event',
      event_sources: [
        { event_source_id: 'retailer_sales', event_type: 'purchase', value_field: 'value' }
      ],
      target: { kind: 'per_ad_spend', value: 4.0 },
      priority: 1
    }]
  }],
  start_time: 'asap',
  end_time: endDate.toISOString()
});

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

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

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

Catalog-driven packages

A catalog-driven package allocates a single budget envelope to an entire catalog of items. Instead of creating separate packages per item, the platform optimizes delivery across all catalog items based on performance. This is the AdCP equivalent of catalog-based campaign types such as Google Performance Max or Meta Dynamic Product Ads. Include the catalogs field in a package to make it catalog-driven. Each catalog should have a distinct type (e.g., one product catalog, one store catalog). The referenced catalogs must already be synced via sync_catalogs. Job campaign with synced job catalog:
test=false
{
  "brand": { "domain": "acme-restaurants.com" },
  "packages": [{
    "product_id": "prod_job_board",
    "pricing_option_id": "cpc_eur_auction",
    "budget": 5000,
    "bid_price": 2.50,
    "catalogs": [{
      "catalog_id": "chef-vacancies",
      "type": "job"
    }]
  }],
  "start_time": "asap",
  "end_time": "2026-06-30T23:59:59Z"
}
Retail media with product catalog and store catchment targeting:
test=false
{
  "brand": { "domain": "acmecorp.com" },
  "packages": [{
    "product_id": "prod_retail_sp",
    "pricing_option_id": "cpc_usd_auction",
    "budget": 10000,
    "bid_price": 1.20,
    "catalogs": [{
      "catalog_id": "gmc-primary",
      "type": "product",
      "tags": ["summer"]
    }],
    "targeting_overlay": {
      "store_catchments": [{
        "catalog_id": "retail-locations",
        "catchment_ids": ["drive"]
      }]
    }
  }],
  "start_time": "asap",
  "end_time": "2026-09-30T23:59:59Z"
}
The platform distributes budget across catalog items based on performance. For per-item reporting, use get_media_buy_delivery which returns by_catalog_item breakdowns. Creative variants for catalog-driven packages represent individual catalog items rendered as ads.

Campaign with Inline Creatives

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({
  brand: {
    domain: 'acmecorp.com'
  },
  packages: [{
    product_id: 'prod_d979b543',
    pricing_option_id: 'cpm_usd_auction',
    format_ids: [{
      agent_url: 'https://creative.adcontextprotocol.org',
      id: 'display_300x250_image'
    }],
    budget: 2500,
    bid_price: 5.00,
    creatives: [{
      creative_id: 'hero_video_30s',
      name: 'Hero Video',
      format_id: {
        agent_url: 'https://creative.adcontextprotocol.org',
        id: 'display_300x250_image'
      },
      assets: {
        image: {
          url: 'https://cdn.example.com/hero-banner.jpg',
          width: 300,
          height: 250
        }
      }
    }]
  }],
  start_time: 'asap',
  end_time: endDate.toISOString()
});

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

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

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

Campaign with Reporting Webhook

Receive automated reporting notifications:
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({
  brand: {
    domain: 'acmecorp.com'
  },
  packages: [{
    product_id: 'prod_d979b543',
    pricing_option_id: 'cpm_usd_auction',
    format_ids: [{
      agent_url: 'https://creative.adcontextprotocol.org',
      id: 'display_300x250_image'
    }],
    budget: 2500,
    bid_price: 5.00
  }],
  start_time: 'asap',
  end_time: endDate.toISOString(),
  reporting_webhook: {
    url: 'https://buyer.example.com/webhooks/reporting',
    authentication: {
      schemes: ['Bearer'],
      credentials: 'secret_token_xyz_minimum_32_chars'
    },
    reporting_frequency: 'daily',
    requested_metrics: ['impressions', 'spend', 'video_completions']
  }
});

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

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

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

Executing a Proposal

Execute a proposal from get_products without manually constructing packages:
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({
  proposal_id: 'swiss_balanced_v1',  // From get_products response
  total_budget: {
    amount: 50000,
    currency: 'USD'
  },
  brand: {
    domain: 'acmecorp.com'
  },
  start_time: 'asap',
  end_time: endDate.toISOString()
});

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

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

if ('media_buy_id' in validated) {
  // Publisher converted proposal allocations to packages
  console.log(`Created media buy ${validated.media_buy_id}`);
  console.log(`Packages created: ${validated.packages.length}`);
}
When executing a proposal:
  • The publisher converts allocation percentages to actual budgets using total_budget
  • Packages are created automatically based on the proposal’s allocations
  • All other fields (brand, start_time, end_time, etc.) work the same as manual mode
See Proposals for the complete workflow.

Context for Correlation

The context field is an opaque object that sellers echo unchanged in responses and webhooks. Use it to map seller-assigned IDs back to your internal systems without needing to maintain a separate lookup table. Context works at two levels:
  • Media buy level — echoed in the create_media_buy response
  • Package level — echoed in each package’s response, useful for mapping package_id back to your internal line items
Mapping to internal campaign and line item IDs:
test=false
{
  "brand": { "domain": "acmecorp.com" },
  "context": {
    "campaign_id": "camp-2026-q3-awareness",
    "planner": "media-team-west",
    "trace_id": "req-8f3a-4b2c"
  },
  "packages": [
    {
      "product_id": "prod_d979b543",
      "pricing_option_id": "cpm_usd_auction",
      "budget": 15000,
      "bid_price": 5.00,
      "context": {
        "line_item_id": "li-001",
        "flight": "june-awareness"
      }
    },
    {
      "product_id": "prod_e8fd6012",
      "pricing_option_id": "cpm_usd_auction",
      "budget": 10000,
      "bid_price": 4.50,
      "context": {
        "line_item_id": "li-002",
        "flight": "june-retargeting"
      }
    }
  ],
  "start_time": "2026-06-01T00:00:00Z",
  "end_time": "2026-08-31T23:59:59Z"
}
The seller’s response echoes your context back alongside the seller-assigned IDs:
test=false
{
  "media_buy_id": "mb_12345",
  "context": {
    "campaign_id": "camp-2026-q3-awareness",
    "planner": "media-team-west",
    "trace_id": "req-8f3a-4b2c"
  },
  "packages": [
    {
      "package_id": "pkg_001",
      "product_id": "prod_d979b543",
      "context": {
        "line_item_id": "li-001",
        "flight": "june-awareness"
      }
    },
    {
      "package_id": "pkg_002",
      "product_id": "prod_e8fd6012",
      "context": {
        "line_item_id": "li-002",
        "flight": "june-retargeting"
      }
    }
  ]
}
Sellers must never parse or act on context data — it exists purely for the buyer’s internal use.

Error Handling

Common errors and resolutions:
Error CodeDescriptionResolution
PRODUCT_NOT_FOUNDInvalid product_idVerify product exists via get_products
FORMAT_INCOMPATIBLEFormat not supported by productCheck product’s format_ids field
BUDGET_TOO_LOWBudget 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 reference

The brand field identifies the advertiser for policy compliance and business purposes.
{
  "brand": {
    "domain": "acmecorp.com"
  }
}
Full brand identity data (colors, fonts, product catalog) is resolved from brand.json at execution time. See brand.json.

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. Use overlays only for:
  • Geographic restrictions (RCT testing, regulatory compliance)
  • Frequency capping
  • AXE segment inclusion/exclusion (legacy — new integrations use TMP)
See Targeting for details.

Format Workflow

Why Format Specification Matters

When creating a media buy, format specification enables:
  1. Placeholder Creation - Publisher creates placeholders in ad server with correct specs
  2. Validation - System validates products support requested formats
  3. Clear Expectations - Both parties know exactly what’s needed
  4. Progress Tracking - Track which assets are missing vs. required
  5. Technical Setup - Ad server configured before creatives arrive

Complete Workflow

1. list_creative_formats → Get available format specifications
2. get_products → Find products (returns format_ids they support)
3. Validate compatibility → Ensure products support desired formats
4. create_media_buy → Specify formats (REQUIRED)
   └── Publisher creates placeholders
   └── Clear creative requirements established
5. sync_creatives → Upload actual files matching formats
6. Campaign activation → Replace placeholders with real creatives

Format Validation

Publishers MUST validate:
  • All formats are supported by the product
  • Format specifications match list_creative_formats output
  • Creative requirements can be fulfilled within timeline
Invalid format example:
{
  "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" }
    ]
  }]
}

Flight date validation

When a package specifies start_time or end_time, sellers SHOULD validate that:
  • Both dates fall within the media buy’s date range
  • start_time is before end_time
Out-of-range or inverted dates SHOULD return an INVALID_REQUEST error:
{
  "errors": [{
    "code": "INVALID_REQUEST",
    "message": "Package 'week_5' end_time 2026-04-05T23:59:59Z is after media buy end_time 2026-03-31T23:59:59Z",
    "field": "packages[3].end_time"
  }]
}

Asynchronous Operations

This task can complete instantly or take days depending on complexity and approval requirements. The response includes a status field that tells you what happened and what to do next.
StatusMeaningYour Action
completedDone immediatelyProcess the result
workingProcessing (~2 min)Poll frequently or wait for webhook
submittedLong-running (hours/days)Use webhooks or poll infrequently
input-requiredNeeds your inputRead message, respond with info
failedError occurredHandle the error
Note: For the complete status list see Task Lifecycle.

Immediate Success (completed)

The task completed synchronously. No async handling needed.Request:
test=false
const response = await session.call('create_media_buy', {
  brand: { domain: 'acmecorp.com' },
  packages: [
    {
      product_id: 'prod_ctv_sports',
      pricing_option_id: 'cpm_fixed',
      budget: 50000
    }
  ]
});
Response:
{
  "status": "completed",
  "media_buy_id": "mb_12345",
  "confirmed_at": "2025-06-01T10:00:00Z",
  "creative_deadline": "2025-06-15T23:59:59Z",
  "revision": 1,
  "packages": [
    {
      "package_id": "pkg_001",
      "product_id": "prod_ctv_sports"
    }
  ]
}

Long-Running (submitted)

The task is queued for manual approval. Configure a webhook to receive updates.Request with webhook:
test=false
const response = await session.call('create_media_buy',
  {
    brand: { domain: 'acmecorp.com' },
    packages: [
      {
        product_id: 'prod_premium_ctv',
        pricing_option_id: 'cpm_fixed',
        budget: 500000  // Large budget triggers approval
      }
    ]
  },
  {
    pushNotificationConfig: {
      url: 'https://your-app.com/webhooks/adcp',
      authentication: {
        schemes: ['bearer'],
        credentials: 'your_webhook_secret'
      }
    }
  }
);
Initial response:
{
  "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",
    "confirmed_at": "2025-01-22T14:30:00Z",
    "creative_deadline": "2025-06-20T23:59:59Z",
    "revision": 1,
    "packages": [
      {
        "package_id": "pkg_002",
      }
    ]
  }
}

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
  • Creative assets must be uploaded before deadline for campaign activation
  • Impression-time targeting (audience, frequency, suitability) is handled by TMP
  • 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.

Content Standards

When a media buy includes content standards (via the governance.content_standards field on get_products responses or the media buy request), the buyer is requesting brand suitability enforcement during delivery.
Content standards are created by calling create_content_standards on a verification agent (e.g., IAS, DoubleVerify). Standards MUST be calibrated with each seller before use in production to ensure the seller’s local evaluation model aligns with the verification agent’s interpretation. See the Content Standards overview for the full setup workflow: create → calibrate → activate → validate.

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",
    "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