Skip to main content

Documentation Index

Fetch the complete documentation index at: https://agenticadvertisingorg-changeset-release-main.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Retrieve the current operational state of media buys: configuration, creative approval status, missing assets, and optional near-real-time delivery snapshots. Response Time: ~1 second

Scope of Results

Sales agents MUST return every media buy owned by the authenticated account, regardless of how the buy was created — via AdCP create_media_buy, via the seller’s own APIs, via manual trafficking, via legacy or third-party systems. Scope is account ownership, not creation surface. A media_buy_id returned here identifies any order in the seller’s ad server accessible to the authenticated caller. Any media buy returned by get_media_buys MUST be reachable by every task in its valid_actions. Sales agents MUST NOT mark a buy read-only, hide it, or refuse updates on the basis that it was not originally created via AdCP. When an action is unavailable for business reasons (contractual obligations, platform constraints, policy), the seller MUST omit only that action from valid_actions — never the whole set, and never merely because the buy was created outside AdCP. A seller that returns non-AdCP buys with a systematically empty valid_actions is non-conformant; that pattern is indistinguishable from hiding the buy. Sellers that need to partition inventory away from a caller MUST do so at the account boundary, not within-account. See Account Ownership vs. Creation Surface. Request Schema: /schemas/v3/media-buy/get-media-buys-request.json Response Schema: /schemas/v3/media-buy/get-media-buys-response.json

Request Parameters

ParameterTypeRequiredDescription
accountaccount-refNoAccount reference. Pass { "account_id": "..." } or { "brand": {...}, "operator": "..." } if the seller supports implicit resolution. When omitted, returns data across all accessible accounts.
media_buy_idsstring[]No*Array of media buy IDs to retrieve
status_filterstring | string[]NoStatus filter: "pending_creatives", "pending_start", "active", "paused", "completed", "rejected", "canceled". Defaults to ["active"] only when media_buy_ids is omitted.
include_snapshotbooleanNoWhen true, include near-real-time delivery snapshots for each package. Defaults to false.
include_historyintegerNoInclude the last N revision history entries per media buy (returns min(N, available)). 0 or omit to exclude. Max 1000.
include_webhook_activitybooleanNoWhen true, each media buy includes a webhook_activity array with recent delivery-report webhook fires for the calling principal. Defaults to false. See Webhook activity.
webhook_activity_limitintegerNoPer-buy cap on returned webhook records (most-recent first). Range 1–200, default 50. Ignored when include_webhook_activity is false.
paginationobjectNoCursor-based pagination controls (max_results, cursor) for broad queries.
*media_buy_ids filters results to specific media buys. If neither is provided, the query is scope-based and uses status_filter + pagination. When media_buy_ids are provided, no implicit status filtering is applied. Pass status_filter explicitly if you want to filter identified buys by status.

Response

Returns an array of media buys with current status, creative approval state, and optionally delivery snapshots:
FieldDescription
media_buysArray of media buy objects
paginationCursor pagination metadata (has_more, cursor, optional total_count)
errorsTask-specific errors (e.g., media buy not found)

Media Buy Object

3.1 vocabulary note. get_media_buys returns the lifecycle state on a nested media_buys[].status field (no envelope collision — nested at depth 1). create_media_buy and update_media_buy success responses return the same state on a top-level media_buy_status field (added in 3.1 to avoid colliding with the envelope task-status status). Same enum, two field names in 3.1 — the cascade unifies in 4.0 (#4905). Buyers maintaining cross-call state should treat the two as the same logical value. See Migration › media_buy_status for the full picture.
FieldDescription
media_buy_idSeller’s media buy identifier
invoice_recipientPer-buy invoice recipient when provided at creation. Confirms the seller accepted the billing override. Bank details are omitted (write-only).
statusCurrent status (pending_creatives, pending_start, active, paused, completed, rejected, canceled). Maps to media_buy_status on create_media_buy / update_media_buy success responses (3.1 vocabulary note above).
currencyISO 4217 currency for media-buy-level monetary values
total_budgetTotal campaign budget (in currency)
creative_deadlineCreative upload deadline (ISO 8601)
confirmed_atISO 8601 timestamp when the seller confirmed this media buy
cancellationCancellation metadata (present only when status is canceled). Object with canceled_at (ISO 8601), canceled_by ("buyer" or "seller"), and optional reason.
revisionCurrent revision number. Pass in update_media_buy for optimistic concurrency.
valid_actionsActions the buyer can perform in the current state (e.g., ["pause", "cancel", "update_budget"]). See valid actions mapping.
historyRevision history entries, most recent first. Only present when include_history > 0. Append-only — entries are never modified or deleted.
webhook_activityRecent reporting and health webhook fires for the calling principal, most-recent first. Only present when include_webhook_activity is true AND the seller surfaces fire history for this buy. See Webhook activity for three-state presence semantics.
packagesArray of packages with creative status and optional snapshots

Package Object

FieldDescription
package_idSeller’s package identifier
currencyOptional package-level currency override (defaults to media buy currency)
bid_priceCurrent bid price for auction-based packages (in package currency if present, otherwise media buy currency)
start_timeFlight start time (ISO 8601). Check this before interpreting delivery status.
end_timeFlight end time (ISO 8601)
pausedWhether buyer has paused this package
canceledWhether this package has been canceled (irreversible)
cancellationCancellation metadata (present only when canceled is true). Object with canceled_at (ISO 8601), canceled_by ("buyer" or "seller"), and optional reason.
creative_deadlinePer-package creative deadline (ISO 8601). When absent, the media buy’s creative_deadline applies.
creative_approvalsArray of creative approval states (see below)
format_ids_pendingFormat IDs from format_ids_to_provide not yet uploaded
snapshot_unavailable_reasonReason code when include_snapshot: true but no snapshot is returned for this package
snapshotNear-real-time delivery snapshot (when include_snapshot: true)

Creative Approval Object

FieldDescription
creative_idCreative identifier
approval_statuspending_review, approved, or rejected
rejection_reasonExplanation of rejection (when approval_status is rejected)
Creative revisions are represented as approval_status: "rejected" with a specific rejection_reason. There is no package-level input-required status for creative edits; upload corrected assets via sync_creatives.

History Entry Object

FieldRequiredDescription
revisionYesRevision number after this change was applied
timestampYesISO 8601 timestamp when this change occurred
actionYesWhat happened: created, activated, paused, resumed, canceled, rejected, completed, updated_budget, updated_dates, updated_packages, package_canceled, package_paused, package_resumed
actorNoIdentity of who made the change (server-derived from auth context, not caller-provided)
summaryNoHuman-readable description (e.g., “Budget changed from 5,000to5,000 to 7,500 on pkg_abc”)
package_idNoPackage affected, when the change targeted a specific package
History entries are append-only — sellers MUST NOT modify or delete previously emitted entries. Callers MAY cache entries by revision number.

Snapshot Object

FieldDescription
as_ofISO 8601 timestamp when the platform captured this snapshot
staleness_secondsMaximum data age in seconds. Use this to interpret zero delivery: 900 (15 min) means zero is likely real; 14400 (4 hr) means reporting may still be catching up.
impressionsTotal impressions delivered since package start
spendTotal spend since package start
currencyOptional snapshot currency override for spend
clicksTotal clicks since package start (when available)
pacing_indexDelivery pace (1.0 = on track, <1.0 = behind, >1.0 = ahead)
delivery_statusdelivering, not_delivering, completed, budget_exhausted, flight_ended, goal_met
extOptional extension object for seller-specific operational fields
not_delivering means the package is within its scheduled flight but has delivered zero impressions for at least one full staleness cycle. Implementers must not return not_delivering until staleness_seconds have elapsed since package activation — a new package with no impressions in its first minutes is expected, not a problem. Check start_time to confirm the package is within its flight before acting on this status. Money fields use this currency precedence: snapshot.currency -> package.currency -> media_buy.currency.

Webhook Activity

When include_webhook_activity: true, each returned media buy MAY carry a webhook_activity array describing recent reporting and health webhook fires from the seller to the buyer’s registered endpoint. This is the buyer-side debug surface for the persistent-channel webhook contract — buyers use it to verify that the publisher fired, what the buyer’s gateway returned, and whether retries are still in flight, without needing an operator-level query against the seller’s logs. The record shape, request-field names, scoping, retention floor, three-state presence, and cardinality rules are uniform across AdCP resources that adopt this surface. See Webhook activity log pattern on the snapshot/log contract page for the cross-resource normative section — the rules below restate it for the media-buy call site and add the media-buy-specific capability gate. The surface covers both delivery-report notification types (scheduled, final, delayed, adjusted) and health notification types (impairment). All share the same webhook delivery contract and the same buyer-side debug need.
FieldDescription
idempotency_keyEquals the idempotency_key carried in the webhook payload itself (§ Dedup by idempotency_key). Stable across retries of the same logical fire — buyers correlate this surface with their own endpoint logs via this exact field. Reference it when filing support tickets.
subscriber_idIdentifies which registered webhook subscriber received this fire. Absent in single-subscriber configurations; populated under multi-subscriber buys (4.0+, see #3009).
fired_atISO 8601 timestamp when the seller initiated this attempt.
completed_atISO 8601 timestamp when the response was observed (or terminal failure). Null while status is pending.
notification_typeVerbatim from the webhook payload: scheduled, final, delayed, adjusted, impairment.
sequence_numberSequence number from the webhook payload — useful for spotting stale-sequence drops or gaps. Absent when the notification type does not carry one.
attempt1-indexed retry counter for this logical fire. Initial fire is attempt: 1.
statussuccess, failed, timeout, connection_error, or pending. See semantics below.
urlTarget URL with query string and fragment stripped, and high-entropy / token-shaped path segments redacted. Match this against your registered URL by origin + path, not full URL.
http_status_codeHTTP status from the buyer’s endpoint. Null when no HTTP response was received (timeout, connection_error, pending).
response_time_msWall-clock latency between request send and response receipt. Null for non-completed attempts.
payload_size_bytesSize of the request body the seller sent — useful for diagnosing oversized-payload rejections.
error_messageShort human-readable server-side classification of failure. Null for success. Sellers MUST NOT include request / response bodies or headers here.
Status semantics:
  • success — response received with a 2xx status. http_status_code populated.
  • failed — response received with a non-2xx status. http_status_code populated; error_message describes the response.
  • timeout — no response within the seller’s configured timeout. http_status_code null. Operationally: the buyer’s endpoint is reachable but slow / overloaded.
  • connection_error — DNS, TLS, or socket failure before any HTTP response. http_status_code null. Operationally: the buyer’s endpoint is unreachable or misconfigured.
  • pending — attempt is in flight or queued for retry. completed_at is null; subsequent attempts appear with the same idempotency_key and incremented attempt.
Record cardinality: one record per attempt. A successful first-attempt fire appears as a single record with attempt: 1. A 3-attempt retry trail (e.g., two failures then a success) appears as three records sharing idempotency_key. Scoping (normative):
  • webhook_activity MUST be scoped to the calling principal. When multiple buyer principals share visibility into the same media buy via account-level access, each principal sees only fires targeting its own endpoint.
  • Sellers that surface this field MUST retain records for at least 30 days from each record’s completed_at — uniformly across success, failed, timeout, and connection_error outcomes (all of which populate completed_at). For records still in pending status, the clock runs from fired_at until the attempt terminates and then transitions to 30 days from completed_at — retry trails do not age out mid-flight. Sellers that cannot honor this floor MUST omit the field entirely rather than return a shorter window; the three-state presence semantics give them a clean opt-out and buyers a single guarantee they can build against.
  • This surface is a debug aid, not a full audit log. There is no cursor for older fires beyond webhook_activity_limit — buyers needing full history must persist webhook records on their own side.
Three-state presence semantics:
StateMeaning
Field omittedSeller does not surface webhook activity for this buy. Either the seller does not persist fire history, the seller’s declared propagation_surfaces excludes webhook, or the buy has no registered webhook endpoint for the calling principal.
Empty array []Seller persists fire history but has fired nothing recent for this principal.
Non-empty arrayActual fire records, most-recent first.
Sellers whose declared propagation_surfaces does not include webhook MUST omit the field; opting in via include_webhook_activity: true does not override that. Diagnosing an unexpected omission. When you expected fires but got an omitted field, two observables let you discriminate the cause without filing a ticket: (1) check your own push_notification_config registration state for this buy — if not registered, that’s the cause; (2) check the seller’s capabilities.media_buy.propagation_surfaces via get_adcp_capabilities — if webhook is absent, that’s the cause. When both check out, “seller does not persist fire history” is the remaining cause; that’s a seller-side gap and warrants an operator ticket. Privacy:
  • The url field has its query string and fragment stripped, and sellers SHOULD redact path segments resembling shared secrets (high-entropy random material, UUID / token shapes).
  • Request and response bodies are not surfaced by this field. A future include_webhook_payloads extension may add them under stricter authorization controls — out of scope here.
  • error_message is a server-side classification string only — never request headers, never response bodies, never buyer-endpoint stack traces.

Diagnose a webhook delivery problem

import { testAgent } from '@adcp/sdk/testing';
import { GetMediaBuysResponseSchema, type WebhookActivityRecord } from '@adcp/sdk';

// The WebhookActivityRecord type is regenerated by the SDK from
// /schemas/core/webhook-activity-record.json — once the SDK rebuilds against this
// branch's schemas the import resolves. The same type appears on every AdCP resource
// that surfaces webhook_activity[], so debug helpers can be written once and reused.
function latestAttempt(trail: WebhookActivityRecord[]): WebhookActivityRecord {
  return trail.reduce((a, b) => (a.attempt >= b.attempt ? a : b));
}

const result = await testAgent.getMediaBuys({
  media_buy_ids: ['mb_12345'],
  include_webhook_activity: true,
  webhook_activity_limit: 20,
});

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

const validated = GetMediaBuysResponseSchema.parse(result.data);

for (const mediaBuy of validated.media_buys) {
  // Three-state semantics — distinguish "seller does not surface" from "no recent fires".
  if (mediaBuy.webhook_activity === undefined) {
    console.log(`${mediaBuy.media_buy_id}: seller does not surface webhook activity for this buy`);
    continue;
  }

  const fires = mediaBuy.webhook_activity;
  if (fires.length === 0) {
    console.log(`${mediaBuy.media_buy_id}: no recent fires for this principal`);
    continue;
  }

  // Group attempts by idempotency_key so we can see the retry trail per logical fire.
  const trails = new Map();
  for (const fire of fires) {
    const trail = trails.get(fire.idempotency_key) ?? [];
    trail.push(fire);
    trails.set(fire.idempotency_key, trail);
  }

  for (const [idempotencyKey, trail] of trails) {
    // Pick the latest attempt by `attempt` number — robust against any iteration order.
    const latest = latestAttempt(trail);
    if (latest.status === 'success') continue;

    const detail = latest.error_message ?? latest.http_status_code ?? '—';
    console.log(
      `${mediaBuy.media_buy_id} ${idempotencyKey} ` +
      `(${latest.notification_type} seq=${latest.sequence_number}): ` +
      `${latest.status} after ${trail.length} attempt(s) — ${detail}`
    );
  }
}

Valid Actions Mapping

The valid_actions array tells agents what operations are permitted on a media buy in its current state. Sellers SHOULD include this field. Expected values by status:
StatusExpected valid_actions
pending_creativescancel, sync_creatives
pending_startcancel, sync_creatives
activepause, cancel, update_budget, update_dates, update_packages, add_packages, sync_creatives
pausedresume, cancel, update_budget, update_dates, update_packages, add_packages, sync_creatives
completed(empty array)
rejected(empty array)
canceled(empty array)
Sellers MAY omit actions based on business rules (e.g., omit cancel when the media buy has contractual obligations that prevent cancellation).

Common Scenarios

Check creative approval status

import { testAgent } from '@adcp/sdk/testing';
import { GetMediaBuysResponseSchema } from '@adcp/sdk';

const result = await testAgent.getMediaBuys({
  media_buy_ids: ['mb_12345']
});

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

const validated = GetMediaBuysResponseSchema.parse(result.data);

if (validated.errors?.length > 0) {
  throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`);
}

for (const mediaBuy of validated.media_buys) {
  for (const pkg of mediaBuy.packages) {
    // Check for missing creatives
    if (pkg.format_ids_pending?.length > 0) {
      console.log(`Package ${pkg.package_id}: missing formats ${pkg.format_ids_pending.map(f => f.id).join(', ')}`);
    }

    // Check approval states
    for (const approval of pkg.creative_approvals ?? []) {
      if (approval.approval_status === 'rejected') {
        console.log(`Creative ${approval.creative_id} rejected: ${approval.rejection_reason}`);
      } else if (approval.approval_status === 'pending_review') {
        console.log(`Creative ${approval.creative_id} pending review`);
      }
    }
  }
}

Monitor delivery with snapshots

import { testAgent } from '@adcp/sdk/testing';
import { GetMediaBuysResponseSchema } from '@adcp/sdk';

const result = await testAgent.getMediaBuys({
  status_filter: 'active',
  include_snapshot: true
});

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

const validated = GetMediaBuysResponseSchema.parse(result.data);

for (const mediaBuy of validated.media_buys) {
  for (const pkg of mediaBuy.packages) {
    const snap = pkg.snapshot;
    if (!snap) continue;

    if (snap.delivery_status === 'not_delivering') {
      console.log(`Package ${pkg.package_id}: zero delivery (data up to ${snap.staleness_seconds}s old)`);
    } else if (snap.pacing_index !== undefined && snap.pacing_index < 0.8) {
      console.log(`Package ${pkg.package_id}: underpacing at ${(snap.pacing_index * 100).toFixed(0)}%`);
    } else {
      console.log(`Package ${pkg.package_id}: ${snap.impressions.toLocaleString()} impressions, pacing ${snap.pacing_index?.toFixed(2)}`);
    }
  }
}

Campaign readiness check

import { testAgent } from '@adcp/sdk/testing';
import { GetMediaBuysResponseSchema } from '@adcp/sdk';

const result = await testAgent.getMediaBuys({
  media_buy_ids: ['mb_12345']
});

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

const validated = GetMediaBuysResponseSchema.parse(result.data);
const [mediaBuy] = validated.media_buys;
const issues = [];

for (const pkg of mediaBuy.packages) {
  if (pkg.format_ids_pending?.length > 0) {
    issues.push(`Package ${pkg.package_id}: ${pkg.format_ids_pending.length} format(s) not yet uploaded`);
  }

  const rejected = (pkg.creative_approvals ?? []).filter(a => a.approval_status === 'rejected');
  if (rejected.length > 0) {
    issues.push(`Package ${pkg.package_id}: ${rejected.length} creative(s) rejected`);
  }
}

if (issues.length === 0) {
  console.log('Campaign ready to launch');
} else {
  console.log('Campaign has blocking issues:');
  issues.forEach(issue => console.log(`  - ${issue}`));
}

Snapshot vs. get_media_buy_delivery

get_media_buys (with snapshot)get_media_buy_delivery
PurposeOperational monitoringReporting and reconciliation
FreshnessMinutes (entity-level stats)Hours (batch report jobs)
AccuracyBest-effortAuthoritative, billing-grade
Date rangeAlways “since campaign start”Configurable period
Daily breakdownNoYes
Creative statusYesNo
Missing assetsYesNo
Use get_media_buys to answer “what is the current state of my campaigns?” and get_media_buy_delivery for “how did my campaigns perform over a period?” Status taxonomy is shared for lifecycle filters across both tasks (pending_creatives, pending_start, active, paused, completed). get_media_buy_delivery may additionally return reporting-only statuses (reporting_delayed, failed) in webhook contexts.

Data Freshness

Snapshot staleness_seconds varies by platform:
Platform typeTypical staleness_seconds
Entity-level stats (e.g., GAM LineItemService)900 (15 min)
Near-real-time insights API60–300
Batch-only reporting14400 (4 hr)
When the platform only has batch reporting, the seller agent should return the most recent cached data with the appropriate staleness_seconds. If include_snapshot: true and snapshot is omitted for a package, check snapshot_unavailable_reason:
  • SNAPSHOT_UNSUPPORTED: the seller does not support package snapshots for this integration
  • SNAPSHOT_TEMPORARILY_UNAVAILABLE: snapshot pipeline is delayed or degraded; retry later
  • SNAPSHOT_PERMISSION_DENIED: caller lacks permission to view snapshot metrics for that package

Pagination

Use cursor pagination for broad status queries to avoid large payloads:
  • Request: set pagination.max_results (1-100, default 50) and optional pagination.cursor
  • Response: read pagination.has_more; when true, pass pagination.cursor into the next request
  • ID-targeted queries (media_buy_ids) can omit pagination unless the ID set is very large

Error Handling

Error CodeDescriptionResolution
MEDIA_BUY_NOT_FOUNDMedia buy ID does not existVerify media_buy_id
CONTEXT_REQUIREDNo media buys found for the requested scopeProvide valid IDs/refs or broaden status_filter/pagination scope
AUTH_MISSINGNo credentials presentedProvide credentials via auth header
AUTH_INVALIDCredentials rejected (expired / revoked)Human credential rotation required; do not auto-retry

Next Steps

  • Upload missing creatives: Use sync_creatives for formats in format_ids_pending
  • Investigate zero delivery: Check delivery_status: "not_delivering" and start_time to confirm the flight is active, then use update_media_buy to adjust pricing or targeting
  • Detailed reporting: Use get_media_buy_delivery for date-range reporting and daily breakdowns
  • Optimize campaigns: Use provide_performance_feedback to share results with the seller