Continuous improvement through data-driven monitoring and optimization. AdCP provides comprehensive reporting tools and optimization features to help you track performance, analyze delivery, and improve campaign outcomes. Reporting in AdCP aligns with the Targeting system used for campaign setup, enabling consistent analysis across the campaign lifecycle. This unified approach means you can report on exactly what you targeted. Performance data feeds into AdCP’s Accountability & Trust Framework, enabling publishers to build reputation through consistent delivery and helping buyers make data-driven allocation decisions.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.
Key Optimization Tasks
Delivery Reporting
Useget_media_buy_delivery to retrieve comprehensive performance data including impressions, spend, clicks, and conversions across all campaign packages.
Alternatively, configure webhook-based reporting during media buy creation to receive automated delivery notifications at regular intervals.
Campaign Updates
Useupdate_media_buy to modify campaign settings, budgets, and configurations based on performance insights.
Optimization Workflow
The typical optimization cycle follows this pattern:- Monitor Delivery: Track campaign performance against targets
- Analyze Performance: Identify optimization opportunities
- Make Adjustments: Update budgets, targeting, or creative assignments
- Track Changes: Monitor impact of optimizations
- Iterate: Continuous improvement through regular analysis
Metric lifecycle
Every optimization metric in AdCP — whether seller-native (clicks, views, reach), graduated vendor-attested (viewability), or vendor-defined (attention, brand lift, emissions) — flows through the same set of surfaces. Buyers reason about a metric by asking three questions in order:- Can you? — Is the product capable of optimizing for this metric? (Discovery)
- Will you? — For this package, are you committing to optimize and report it? (Commitment)
- Did you? — In delivery, what was the value, and did you meet the commitment? (Reporting)
metric_id (or (vendor, metric_id) tuple for vendor-attested metrics) flows through every layer — discovery, capability declarations, package commitments, and delivery — so buyers reconciling delivery against a goal never need a translation table.
Standard metric flow (e.g., clicks, viewable_rate)
| Question | Surface | Field |
|---|---|---|
| Can the product report it? | Product capability | reporting_capabilities.available_metrics |
| Can the product optimize for it? | Product capability | metric_optimization.supported_metrics |
| Will the seller commit to reporting it? | Package | committed_metrics[] (scope: standard) |
| Does the buyer want it optimized? | Package | optimization_goals[] (kind: metric) |
| Does the buyer want accountability? | Package | performance_standards[] |
| What was the value? | Delivery | Standard scalar (e.g., clicks, viewability.viewable_rate) |
| Did the seller fail to deliver a committed metric? | Delivery | missing_metrics[] (scope: standard) |
Vendor-attested metric flow (e.g., Adelaide attention, Scope3 emissions, Kantar brand lift)
Same lifecycle, with the(vendor, metric_id) tuple replacing the plain metric_id everywhere:
| Question | Surface | Field |
|---|---|---|
| Does the vendor define this metric? | Vendor’s capabilities | get_adcp_capabilities.measurement.metrics[] |
| Can the product report this vendor metric? | Product capability | reporting_capabilities.vendor_metrics[] |
| Can the product optimize for this vendor metric? | Product capability | vendor_metric_optimization.supported_metrics[] |
| Will the seller commit to reporting it? | Package | committed_metrics[] (scope: vendor) |
| Does the buyer want it optimized? | Package | optimization_goals[] (kind: vendor_metric) |
| Does the buyer want accountability? | Package | performance_standards[] (with vendor field) |
| What was the value? | Delivery | vendor_metric_values[] |
| Did the seller fail to deliver a committed metric? | Delivery | missing_metrics[] (scope: vendor) |
Why both flows matter
The two flows aren’t a duplication — they encode the difference between standardized and vendor-specific measurement. Clicks mean the same thing across every seller; attention does not. Vendor binding is the wire-level evidence that the buyer and seller agreed on which attention model is being optimized and reported. Seemeasurement/taxonomy.mdx for the Tier 0 → Tier 1 graduation policy that decides which flow a given metric uses.
Required coherence between layers
Three rules connect the layers and prevent orphaned goals:-
Optimization requires capability. A package’s
optimization_goals[]entry MUST match a product capability —metric_optimization.supported_metricsforkind: "metric", orvendor_metric_optimization.supported_metricsforkind: "vendor_metric". Sellers reject mismatches withTERMS_REJECTED. -
Optimization requires reporting commitment. For
kind: "vendor_metric"goals, the matching(vendor, metric_id)MUST also appear in the package’scommitted_metrics[]. Optimizing for a metric the seller isn’t committed to reporting is unverifiable — the buyer has no way to grade the goal. Sellers reject uncommitted vendor_metric goals withTERMS_REJECTED. (This rule is normative for vendor metrics specifically; for seller-nativemetricgoals, the metric is usually always reported by virtue of being seller-native, so the coherence is implicit.) A third precondition — discovery, thatmetric_idappears in the vendor’s publishedmeasurement.metrics[]catalog — is SHOULD this minor and tightens to MUST at the next minor while measurement-vendor adoption of AdCP-conformant capability publication catches up. -
Performance accountability is independent.
performance_standards[]lives in parallel — buyers MAY add a threshold commitment on any metric the seller commits to reporting, regardless of whether they also set an optimization goal on it. The three surfaces (goal / commitment / standard) compose:- Goal only — “push toward this,” no accountability floor
- Standard only — “you owe me ≥ X,” seller decides how to hit it
- Both — optimization steers; standard backstops
Worked example: Adelaide attention end-to-end
A buyer wants to optimize against Adelaide’sattention_score with a threshold of 70. Here’s how the four surfaces line up:
1. Product capability (discovered via get_products):
create_media_buy — the buyer commits to reporting AND sets the optimization goal (both required by the reporting-coherence rule):
performance_standard MAY be layered on top if the buyer wants a contractual floor — e.g., { "metric": "attention_score", "vendor": { "domain": "adelaidemetrics.com" }, "threshold": 65 } — but it’s independent of the goal.
3. Delivery (get_media_buy_delivery response):
73.2 is above the threshold 70, so the goal is met. The measurable_impressions denominator (420000 out of, say, 1M delivered impressions) is the coverage figure for Adelaide’s measurement — vendor SDKs rarely fire on 100% of delivered impressions, and attention-vendor coverage on CTV today is typically 30–60% depending on app-SDK presence. Always compute coverage from measurable_impressions / impressions before reasoning about the reported value.
4. Accountability gap reporting (also in the get_media_buy_delivery response, when commitment isn’t met):
vendor_metric_values for this period, the same (vendor, metric_id) shows up in missing_metrics — same key across the entire lifecycle, so buyer reconciliation is a row-level join.
Performance Monitoring
Real-Time Metrics
Track campaign delivery as it happens:- Impression delivery vs. targets
- Spend pacing against budget
- Click-through rates and engagement
- Conversion tracking for business outcomes
Historical Analysis
Understand performance trends over time:- Daily/hourly breakdowns of key metrics
- Performance comparisons across time periods
- Trend identification for optimization opportunities
Alerting and Notifications
Stay informed of important campaign events:- Delivery alerts for pacing issues
- Performance notifications for significant changes
- Budget warnings before limits are reached
Delivery Methods
Publishers can proactively push reporting data to buyers through webhook notifications or offline file delivery. This eliminates continuous polling and provides timely campaign insights. Webhook Push (Real-time) - HTTP POST to buyer endpoint- Best for: Most buyer-seller relationships
- Latency: Near real-time (seconds to minutes)
- Cost: Standard webhook infrastructure
- Best for: Large buyer-seller pairs (high volume)
- Latency: Scheduled batch delivery (hourly/daily)
- Cost: Significantly lower ($0.01-0.10 per GB vs. $0.50-2.00 per 1M webhooks)
- Format: JSON Lines, CSV, or Parquet files
- Storage: S3, GCS, Azure Blob Storage
Webhook-Based Reporting
Webhook Configuration
Configure reporting webhooks when creating a media buy using thereporting_webhook parameter:
authenticationconfiguration is mandatory (minimum 32 characters)- Bearer tokens: Simple, good for development (Authorization header)
- HMAC-SHA256: Production-recommended, prevents replay attacks (signature headers)
- Credentials exchanged out-of-band during publisher onboarding
- See Security for implementation details
Supported Frequencies
Publishers declare supported reporting frequencies in the product’sreporting_capabilities. Publishers are not required to support all frequencies - choose what makes operational sense for your platform.
hourly: Receive notifications every hour during campaign flight (optional, consider cost/complexity)daily: Receive notifications once per day (most common, recommended for Phase 1)monthly: Receive notifications once per month (timezone specified by publisher)
Available Metrics
Metric availability is declared at two levels:- Product level:
reporting_capabilities.available_metricsdeclares what the platform can report - Format level:
reported_metricson a creative format declares what the format can produce (see Reported Metrics)
impressions and spend are always reported regardless of the intersection. Standard metrics include:
impressions: Ad views (always available)spend: Amount spent (always available)clicks: Click eventsctr: Click-through rateviews: Views at platform-defined thresholdcompleted_views: Video/audio completions (or threshold-based completion when an optimization goal sets a custom view duration)completion_rate: Completion rate (completed_views/impressions)conversions: Post-click or post-view conversionsconversion_value: Monetary value of attributed conversionsroas: Return on ad spendcost_per_acquisition: Cost per conversionnew_to_brand_rate: Fraction of conversions from first-time brand buyersleads: Leads generatedreach: Unique reach (paired withreach_unit). Measurement window declared viareach_window(cumulative/period/rolling). Whenreach_windowis omitted, the window is unspecified — buyers MUST NOT sum reach across rows.reach_window: Window semantics for reach/frequency — object withkind(cumulativesince campaign start,periodfor non-overlapping snapshot,rollingfor trailing window) andperiod: Duration(required forperiodandrolling)frequency: Average frequency per reach unit, measured overreach_windowgrps: Gross Rating Points (for CPP billing)engagements: Direct ad interactions beyond viewing (reactions, taps, opens)engagement_rate: Platform-specific engagement ratefollows: New followers/subscribes attributed to deliverysaves: Saves, bookmarks, playlist adds attributed to delivery (platforms vary in name — Pinterest “repins”, TikTok “video_saves” — all report under this canonical key)profile_visits: Visits to the brand’s in-platform pageviewability: Viewability data (measurable_impressions, viewable_impressions, viewable_rate, viewed_seconds, standard, vendor). Separates MRC and GroupM standards.viewed_secondsis the average in-view duration per measurable impression — reporting-side counterpart to theviewed_secondsoptimization goal, governed by the samestandardthreshold asviewable_rate. The optionalvendorfield carries aBrandRefso the row is self-describing — buyer agents reading delivery in isolation can attribute the numbers to a measurement vendor without joining back topackage.committed_metricsorpackage.performance_standards.quartile_data: Video quartile completion data (q1-q4)dooh_metrics: DOOH-specific metrics (loop plays, screens, venue breakdown)cost_per_click: Cost per click (spend / clicks)cost_per_completed_view: Cost per completed view (spend / completed_views); CPCV pricing scalar for video/audio inventorycpm: Cost per thousand impressions ((spend / impressions) × 1000); universal pricing scalar across CTV, display, mobile/web video, native, audio, and DOOHdownloads: Audio/podcast downloads (IAB Podcast Measurement Technical Guidelines methodology); distinct fromviewsunits_sold: Items sold attributed to delivery (retail-media commerce scalar; distinct fromconversions— one transaction may carry multiple units; attribution-window declared viameasurement_terms)new_to_brand_units: Unit-volume parallel tonew_to_brand_rate— count of units sold to first-time brand buyersplays: Raw play count for DOOH/broadcast inventory (mirrorsforecastable-metric.plays); distinct fromdooh_metrics.loop_plays(per-screen rotation) andimpressions(multiplied audience figure)
requested_metrics to reduce payload size and focus on relevant KPIs.
Vendor-Defined Metrics
The standardavailable_metrics enum is the closed protocol vocabulary. Vendor-defined metrics — proprietary attention scores, emissions per impression, panel-based demographics, brand-lift surveys, in-flight attention panels, custom viewability variants — live in a parallel structured surface:
-
Declaration (
reporting_capabilities.vendor_metrics): each entry is a pointer ({ vendor: BrandRef, metric_id }) into the vendor’s metric catalog. Sellers say “I support this vendor’s metric”; everything else (category, methodology, standard alignment, human-readable documentation) lives at the vendor. Identifiers are namespaced by the vendor — the samemetric_idmay mean different things in different vendors’ vocabularies. -
Discovery anchor: the vendor’s
brand.jsonagents[type='measurement']is the canonical place to find the measurement agent’s URL, capability profile, the methodology, the standards each metric implements, and per-metric documentation. AdCP does not duplicate that metadata on every seller’s per-product extension; buyers resolve it once per vendor (cacheable) when they need it. -
Filter (
get_productsfilters.required_vendor_metrics): each entry pinsvendorand/ormetric_id— at least one. Cross-vendor queries (e.g., “any attention measurement from a vendor that supports it”) are the buyer agent’s responsibility: the agent resolves which vendors offer a category via theirbrand.jsonrecords, then enumerates them as filter entries. Same filter-not-fail convention as the otherrequired_*filters. -
Reporting (
vendor_metric_valueson eachby_packagedelivery row): each value carries{ vendor, metric_id, value, unit?, measurable_impressions?, breakdown? }.measurable_impressionsis the coverage denominator — vendor measurement is rarely 100% of delivered impressions. When the field is present, buyers compute coverage asmeasurable_impressions / impressions; when absent, coverage is unspecified (do not compute a rate or assume full coverage). Thebreakdownslot is the place vendors put structured payloads beyond a single scalar (panel demographics, co-view ratios, incremental decompositions); it is the only escape hatch — the rest of the value envelope is closed. -
Promotion path: when the industry converges on a metric via a published standard, the spec adds it to the closed
available_metricsenum and the vendor extensions become historical aliases. Promotion is anchored on standards-body publication, not on ad-hoc vendor convergence counts. -
Accountability scope: when the seller stamps a vendor metric in
package.committed_metrics(withscope: "vendor"), it is subject to the samemissing_metricscontract onget_media_buy_deliveryas standard metrics. Sellers who can’t credibly attest to a vendor metric SHOULD NOT stamp it incommitted_metrics; absence keeps the metric advisory and reconciliation falls back tovendor_metric_values.measurable_impressionscoverage plus out-of-band verification via the vendor’s measurement agent. The advisory-vs-accountable distinction is now explicit at the contract layer rather than asymmetric across metric scopes.
Publisher Commitment
When a reporting webhook is configured, publishers commit to sending: (campaign_duration / reporting_frequency) + 1 notifications- One notification per frequency period during the campaign
- One final notification when the campaign completes
- If reporting data is delayed beyond the expected delay window, a
"delayed"notification will be sent
Webhook Payload
Reporting webhooks use the same payload structure asget_media_buy_delivery with additional metadata:
notification_type:"scheduled"(regular update),"final"(campaign complete), or"delayed"(data not yet available)sequence_number: Sequential notification number (starts at 1)next_expected_at: ISO 8601 timestamp for next notification (omitted for final notifications)media_buy_deliveries: Array of media buy delivery data (may contain multiple media buys aggregated by publisher)
Timezone Handling
All reporting MUST use UTC. This eliminates DST complexity, simplifies reconciliation, and ensures consistent 24-hour reporting periods.- Daily: 00:00:00Z to 23:59:59Z (always 24 hours)
- Hourly: Top of hour to 59:59 seconds (always 1 hour)
- Monthly: First to last day of month
Delayed Reporting
If reporting data is not available within the product’sexpected_delay_minutes, publishers send a notification with notification_type: "delayed":
Measurement Maturation Windows
For channels where billing-grade data is produced in phases rather than arriving final on day one, sellers declaremeasurement_windows on their products. Each window describes a maturation stage with its own expected availability. This pattern is used across channels:
| Channel | Typical windows |
|---|---|
| Broadcast / linear TV | live (same day) → c3 (~4 days) → c7 (~15–22 days, guarantee basis) |
| DOOH | tentative (same day) → final post-IVT/fraud-check (~1 day, guarantee basis) |
| Digital with IVT filtering | raw → post_givt → post_sivt (~2–3 days, guarantee basis) |
| Podcast | downloads_7d → downloads_30d (guarantee basis) |
is_guarantee_basis — the number both sides reconcile against. The measurement vendor’s processing time is captured in expected_availability_days on each window (it includes both accumulation and processing).
Sellers set expected_delay_minutes on their products to reflect the first-available data pipeline. The measurement_windows array on reporting_capabilities provides per-window timelines.
Delivery data for products with measurement windows includes three fields on each package:
is_final—truewhen the seller considers this data closed for the reporting period.falsewhen the data will be updated (wider window, more processing). Absent when the seller doesn’t distinguish provisional from final.measurement_window— which window this data represents (e.g.,"c3"). References awindow_idfrom the product’smeasurement_windows. Absent for standard digital reporting without windowed maturation.supersedes_window— which previous window this report replaces (e.g.,"live"when C3 data arrives). Absent on the first report for a period.
notification_type: "window_update". This is distinct from adjusted (a correction within the same window).
Measurement window lifecycle example — a broadcast spot airing March 1:
March 2 — Live data arrives (notification_type: scheduled):
window_update):
window_update):
window_update arrives. When is_final: true, this is the number to reconcile against the guarantee. The same lifecycle shape applies to DOOH (tentative → final), digital with IVT filtering (post_givt → post_sivt), podcast (downloads_7d → downloads_30d), and any other channel where data matures in phases — only the window IDs and timing differ.
For how measurement_window appears on billing terms and drives reconciliation and invoicing clocks, see Accountability.
Webhook Aggregation
Publishers SHOULD aggregate webhooks to reduce call volume when multiple media buys share:- Same webhook URL
- Same reporting frequency
- Same reporting period
- Without aggregation: 100 webhooks per day (inefficient)
- With aggregation: 1 webhook per day containing all 100 campaigns (optimal)
media_buy_deliveries array may contain 1 to N media buys per webhook. Buyers should iterate through the array to process each campaign’s data.
Aggregated webhook example:
Partial Failure Handling
When aggregating multiple media buys into a single webhook, publishers must handle cases where some campaigns have data available while others don’t. Approach: Best-Effort Delivery with Status Indicators Publishers SHOULD send aggregated webhooks containing all available data, using status fields to indicate partial availability:partial_data: Boolean indicating if any campaigns are missing dataunavailable_count: Number of campaigns with delayed/missing datastatus: Per-campaign status ("active","reporting_delayed","failed")expected_availability: When delayed data is expected (if known)
- Upstream delays: Some data sources are slower than others
- System degradation: Partial system outage affects subset of campaigns
- Data quality issues: Specific campaigns fail validation, others proceed
- Rate limiting: API limits prevent fetching all campaign data
- Complete system outage: Send
"delayed"notification instead - All campaigns affected: Use
notification_type: "delayed" - Buyer endpoint issues: Circuit breaker handles this (don’t send at all)
- Always include all campaigns in array, even if data unavailable (with status indicator)
- Set
partial_data: trueflag when any campaigns are delayed/failed - Provide
expected_availabilitytimestamp if known - Don’t retry the entire webhook - buyers can poll individual campaigns if needed
- Track partial delivery rates in monitoring to detect systemic issues
Privacy and Compliance
PII Scrubbing for GDPR/CCPA
Publishers MUST scrub personally identifiable information (PII) from all webhook payloads to ensure GDPR and CCPA compliance. Reporting webhooks should contain only aggregated, anonymized metrics. What to Scrub:- User IDs, device IDs, IP addresses
- Email addresses, phone numbers
- Precise geolocation data (latitude/longitude)
- Cookie IDs, advertising IDs (unless aggregated)
- Any custom dimensions containing PII
- Aggregated metrics (impressions, spend, clicks, etc.)
- Coarse geography (city, state, country - not street address)
- Device type categories (mobile, desktop, tablet)
- Browser/OS categories
- Time-based aggregations
- Implement PII scrubbing at the data collection layer, not at webhook delivery
- Ensure aggregation thresholds prevent re-identification (e.g., minimum 10 users per segment)
- Document what data is collected vs. what is shared in webhooks
- Provide data processing agreements (DPAs) for GDPR compliance
- Support GDPR/CCPA data deletion requests
- Do not request PII in
requested_metricsor custom dimensions - Understand that webhook data is aggregated and anonymized
- Implement proper data retention policies
- Include webhook data in privacy policies and user disclosures
Implementation Best Practices
- Handle Arrays: Always process
media_buy_deliveriesas an array, even if it contains one element - Idempotent Handlers: Process duplicate notifications safely (webhooks use at-least-once delivery)
- Sequence Tracking: Use
sequence_numberto detect missing or out-of-order notifications - Fallback Polling: Continue periodic polling as backup if webhooks fail
- Timezone Awareness: Store publisher’s reporting timezone for accurate period calculation
- Validate Frequency: Ensure requested frequency is in product’s
available_reporting_frequencies - Validate Metrics: Ensure requested metrics are in product’s
available_metrics - PII Compliance: Never include user-level data in webhook payloads
Webhook Health Monitoring
Webhook delivery status is tracked through AdCP’s global task management system (see Task Lifecycle). When a media buy is created withreporting_webhook configured, the publisher creates an ongoing task for webhook delivery. Buyers can monitor webhook health using standard task queries.
Benefits of using task management:
- Consistent status tracking across all AdCP operations
- Standard polling/webhook notification patterns
- Existing infrastructure for task status, history, and errors
- No need for media-buy-specific webhook health endpoints
Offline-File-Delivery-Based Reporting
Example: Offline Delivery Publisher pushes daily report files to buyer’s cloud storage:- >100 active campaigns with same buyer
- Hourly reporting requirements (24x cost reduction)
- High data volume (detailed breakdowns, dimensional data)
- Buyer has batch processing infrastructure
get_adcp_capabilities. Polling via get_media_buy_delivery is always available — it is a required task for all media_buy sellers.
reporting_capabilities:
prefix isolation — either way, the buyer can only access data under their account’s path. When multiple buying platforms operate on the same brand, each gets a separate account (scoped by operator/agent), so their data is isolated by prefix.
The bucket location appears on the account object returned by sync_accounts:
get_media_buy_deliveryis a required task for allmedia_buysellers. Polling is always available as a baseline, regardless of which push methods the seller supports.- When
reporting_delivery_methodsincludesofflineand areporting_bucketis present on the account, the seller also pushes detailed delivery data to the bucket. Buyers with batch infrastructure should read from the bucket for efficiency. - Files in the bucket are retained for
file_retention_days(declared on thereporting_bucket). Buyers must read files within this window. get_media_buysalways returns the media buy object with status, totals, and pacing snapshots. Use it for status checks, not detailed reporting.
.jsonl.gz, .csv.gz) to reduce storage and transfer costs. Parquet, Avro, and ORC use internal compression — the top-level compression field is ignored for these formats.
JSON Lines (JSONL)
One media buy delivery per line (newline-delimited JSON). Each line contains a single media buy delivery object with its reporting period and package-level data. Preserves full nested structure. Easy to parse line-by-line for streaming processing. Example JSONL file:CSV
For tabular analysis CSV files require unnesting nested arrays. Each record should be unnested to theby_package level, meaning one row per package with parent-level data (reporting period, media buy info, totals) duplicated.
Example CSV structure:
Parquet
For high-volume analytics Columnar format optimized for analytics workloads. Excellent compression ratios. Supports nested structures natively. Best for data warehouses and big data processing. Example Parquet schema:Avro
For schema-rich streaming pipelines Row-oriented format with embedded schema. Self-describing — readers don’t need external schema files. Handles schema evolution (adding/removing fields) gracefully. Common in Kafka and Hadoop ecosystems. Uses internal compression (snappy, deflate, or zstd). Example Avro schema:ORC
For Hive/Spark analytics Columnar format optimized for read-heavy analytics on Hadoop-ecosystem tools (Hive, Spark, Presto). Predicate pushdown, built-in indexes, and lightweight compression (snappy, zlib, or zstd) reduce I/O. Supports nested structures via struct and array types. ORC uses the same logical schema as Parquet. Choose ORC when your data warehouse is Hive-native; choose Parquet for broader tool compatibility. File Structure: Each file contains one media buy delivery per line (JSONL), row (CSV/Parquet/ORC), or record (Avro). Files may contain:- Multiple media buy deliveries (one per line/row)
- Multiple reporting periods for the same media buy (separate rows)
- Multiple media buys (each with its own rows)
- Process files in chronological order using file timestamps
- Handle duplicate files gracefully (idempotent processing)
- Validate file integrity using checksums if provided
- Monitor for missing files and alert on gaps
Security considerations for offline delivery
Offline files sit at rest forfile_retention_days, so a misconfigured IAM policy leaks historical reporting across tenants. The general security controls apply; the offline-specific requirements:
- Scope access at the IAM layer, not by obscurity. Buyer read access MUST be scoped to
{bucket}/{prefix}/*(S3 bucket policy condition, GCS conditional IAM binding onresource.name.startsWith(...), or Azure SAS scoped to the prefix). A bucket-wide read grant is non-compliant even when the seller only writes under one prefix per account. - Scope listing as well as reads. Prefix scoping MUST cover both object-level operations (
s3:GetObject) and listing (s3:ListBucketwith ans3:prefixcondition). A policy that scopesGetObjectto the prefix but leavesListBucketunscoped lets a buyer enumerate other tenants’ prefix names — a cross-tenant isolation failure even without read access to their objects. The same applies to GCSstorage.objects.listand AzurelistSAS permissions. - Revoke access when the account closes. When the seller emits an
account.statustransition toinactive,suspended, orclosed, the seller MUST stop honoring the associated credentials, and buyers SHOULD treat that status change as the trigger to remove matching IAM trust on their end. A seller IAM role left granted to a decommissioned bucket is a lateral-movement risk.
setup_instructions is a seller-provided URL. It is operator-facing documentation, not agent-consumable content. Buyer agents MUST NOT auto-fetch the URL; they SHOULD surface it to a human operator. If an implementation chooses to fetch it (for example, to preview the target before showing it to the operator), apply webhook URL SSRF validation, and the fetched content MUST NOT be passed into an LLM context without indirect-prompt-injection guarding — seller-controlled text in this field can contain instructions to rotate credentials, change billing, or alter downstream agent behavior.
Data Reconciliation
Theget_media_buy_delivery API is the authoritative source of truth for all campaign metrics. Polling is always available as a baseline. When the seller also supports push-based delivery (webhooks or offline buckets), those methods provide timely data but get_media_buy_delivery remains the reconciliation path.
Reconciliation is important for any reporting delivery method because:
- Webhooks: May be missed due to network failures or circuit breaker drops
- Offline files: May be delayed, corrupted, or fail to process
- Polling: May miss data during API outages
- Late-arriving data: Impressions can arrive 24-48+ hours after initial reporting (all methods)
Reconciliation Process
Buyers SHOULD periodically reconcile delivered data against API to ensure accuracy: Recommended Reconciliation Schedule:- Hourly delivery: Reconcile via API daily
- Daily delivery: Reconcile via API weekly
- Monthly delivery: Reconcile via API at month end + 7 days
- Campaign close: Always reconcile after campaign_end + attribution_window
- Delivery failures: Webhooks missed, offline files corrupted, API timeouts during polling
- Late-arriving data: Impressions attributed after initial reporting (all delivery methods)
- Data corrections: Publisher adjusts metrics after initial reporting
- Processing errors: Buyer-side failures to process delivered data
- Timezone differences: Period boundaries may differ between delivery and API query
- For billing: Always use
get_media_buy_deliveryAPI at campaign end + attribution window - For real-time decisions: Use delivered data (webhook/file/poll) for speed, reconcile later
- For discrepancies: API data wins, update local records accordingly
- For audits: API provides complete historical data, delivered data is ephemeral
- Store webhook
sequence_numberto detect missed notifications - Run automated reconciliation daily for active campaigns
- Alert on discrepancies >2% for impressions or >1% for spend
- Use API data for all financial reporting and invoicing
- Document reconciliation process for audit compliance
Late-Arriving Impressions
Ad serving data often arrives with delays due to attribution windows, offline tracking, and pipeline latency. Publishers declareexpected_delay_minutes in reporting_capabilities:
- Display/Video: Typically 4-6 hours
- Audio: Typically 8-12 hours
- CTV: May be 24+ hours
Handling Late Arrivals
When late data arrives for a previously reported period, resend that period withis_adjusted: true:
- Significant data changes (>2% impression variance or >1% spend variance)
- Final reconciliation at campaign_end + attribution_window
- Data quality corrections
Webhook Reliability
Reporting webhooks follow AdCP’s standard webhook reliability patterns:- At-least-once delivery: Same notification may be delivered multiple times
- Best-effort ordering: Notifications may arrive out of order
- Timeout and retry: Limited retry attempts on delivery failure
Optimization Strategies
Conversion optimization
Set optimization goals on media buy packages to direct delivery toward specific outcomes — target CPC, CPV, ROAS, or CPA. Metric goals (clicks, views) work without event setup. Event goals require configured event sources and conversion data. See Conversion Tracking for the complete setup flow, and Optimization Goals for theoptimization_goals array reference.
Budget optimization
- Reallocation between high and low performing packages via
update_media_buy - Pacing adjustments — switch between
even,asap, orfront_loadeddelivery - Spend efficiency — compare cost per acquisition across packages and shift budget to the best performers
Creative optimization
- Performance analysis by creative asset via
get_media_buy_deliverywith by-creative breakdowns - A/B testing — assign multiple creatives with weights via
creative_assignments - Refresh strategies — swap creatives via
sync_creativesto prevent fatigue
Targeting refinement
- Geographic optimization — adjust
targeting_overlaybased on delivery data by region - Frequency management — tune
frequency_cap(suppress cooldown or max_impressions/per/window cap) based on delivery patterns
Performance Feedback Loop
The performance feedback system enables AI-driven optimization by feeding back business outcomes to publishers. Seeprovide_performance_feedback for detailed API documentation.
Performance Index Concept
A normalized score indicating relative performance:0.0= No measurable value or impact1.0= Baseline/expected performance> 1.0= Above average (e.g., 1.45 = 45% better)< 1.0= Below average (e.g., 0.8 = 20% worse)
Sharing Performance Data
Buyers can voluntarily share performance outcomes using theprovide_performance_feedback task:
Supported Metrics
- overall_performance: General campaign success
- conversion_rate: Post-click or post-view conversions
- brand_lift: Brand awareness or consideration lift
- click_through_rate: Engagement with creative
- completion_rate: Video or audio completion rates
- viewability: Viewable impression rate
- brand_safety: Brand safety compliance
- cost_efficiency: Cost per desired outcome
How Publishers Use Performance Data
Publishers can leverage performance indices to:- Optimize Delivery: Shift impressions to high-performing segments
- Adjust Pricing: Update CPMs based on proven value
- Improve Products: Refine product definitions based on performance patterns
- Enhance Algorithms: Train ML models on actual business outcomes
Privacy and Data Sharing
- Performance feedback sharing is voluntary and controlled by the buyer
- Aggregate performance patterns may be used to improve overall platform performance
- Individual campaign details remain confidential to the buyer-publisher relationship
Dimension breakdowns
Delivery data can be broken down across multiple dimensions within each package. Buyers request specific breakdowns via thereporting_dimensions parameter on get_media_buy_delivery. Each breakdown appears as a by_* array within by_package items, following the same composition pattern as by_creative.
| Dimension | Breakdown field | Key fields | Capability flag |
|---|---|---|---|
| Geography | by_geo | geo_level, geo_code, geo_name | supports_geo_breakdown (object — declares available levels/systems) |
| Device type | by_device_type | device_type | supports_device_type_breakdown |
| Device platform | by_device_platform | device_platform | supports_device_platform_breakdown |
| Audience | by_audience | audience_id, audience_source, audience_name | supports_audience_breakdown |
| Placement | by_placement | placement_id, placement_name | supports_placement_breakdown |
delivery-metrics (impressions, spend, clicks, conversions, etc.) plus its dimension-specific identifier fields. Every entry requires at minimum its identifier(s), impressions, and spend.
Breakdowns are opt-in — no dimension data is returned unless explicitly requested. Sellers that don’t support a requested dimension silently omit it. Each breakdown array has a sibling by_*_truncated boolean indicating whether additional rows exist beyond the requested limit.
Targeting Consistency
Reporting aligns with AdCP’s Targeting approach, enabling:- Consistent analysis across campaign lifecycle
- Granular breakdowns by targeting parameters
- Cross-campaign insights for portfolio optimization
Target → Measure → Optimize
The power of consistent targeting and reporting creates a virtuous cycle:- Target: Define your audience using briefs and overlays (e.g., “Mobile users in major metros”)
- Measure: Report on the same attributes (Track performance by device type and geography)
- Optimize: Feed performance back to improve delivery (Shift budget to high-performing segments)
Standard Metrics
All platforms must support these core metrics:- impressions: Number of ad views
- spend: Amount spent in currency
- clicks: Number of clicks (if applicable)
- ctr: Click-through rate (clicks/impressions)
- conversions: Post-click/view conversions
- viewability: Percentage of viewable impressions
- completion_rate: Video/audio completion percentage
- engagement_rate: Platform-specific engagement metric
Platform-Specific Considerations
Different platforms offer varying reporting and optimization capabilities:Google Ad Manager
- Comprehensive dimensional reporting, real-time and historical data, advanced viewability metrics
Kevel
- Real-time reporting API, custom metric support, flexible aggregation options
Triton Digital
- Audio-specific metrics (completion rates, skip rates), station-level performance data, daypart analysis
Advanced Analytics
Cross-Campaign Analysis
- Portfolio performance across multiple campaigns
- Audience overlap and frequency management
- Budget allocation optimization across campaigns
Predictive Insights
- Performance forecasting based on historical data
- Optimization recommendations from AI analysis
- Trend prediction for proactive adjustments
Response Times
Optimization operations have predictable timing:- Delivery reports: ~60 seconds (data aggregation)
- Campaign updates: Minutes to days (depending on changes)
- Performance analysis: ~1 second (cached metrics)
Best Practices
- Report Frequently: Regular reporting improves optimization opportunities
- Track Pacing: Monitor delivery against targets to avoid under/over-delivery
- Analyze Patterns: Look for performance trends across dimensions
- Consider Latency: Some metrics may have attribution delays
- Normalize Metrics: Use consistent baselines for performance comparison
Integration with Media Buy Lifecycle
Optimization and reporting is the ongoing phase that runs throughout active campaigns:- Connects to Creation: Use learnings to improve future campaign setup
- Guides Updates: Data-driven decisions for campaign modifications
- Enables Scale: Proven strategies can be applied to similar campaigns
- Feeds AI: Performance data improves automated optimization
Related Documentation
get_media_buy_delivery- Retrieve delivery reportsupdate_media_buy- Modify campaigns based on performance- Media Buy Lifecycle - Complete campaign management workflow
- Targeting - Brief-based targeting and overlays