sync_event_sources configures where events come from, and log_event sends the events themselves.
Event data feeds into delivery reporting (conversions, ROAS, cost per acquisition) and enables optimization goals on media buy packages.
The flow
This shows the recommended order. In practice, media buys can be created before events are flowing — the seller begins optimizing once sufficient event history accumulates.Event source
An event source represents a channel through which conversion or marketing events are collected — a website pixel, mobile SDK, server-to-server integration, CRM import, or seller- or platform-native owned property such as a channel, profile, feed, podcast, or newsletter list. Configure event sources withsync_event_sources. You provide an event_source_id, optional name, event_types, action_source, surface, and allowed_domains. The response includes additional fields for each source:
Buyer-managed vs seller-managed
Buyer-managed sources are ones you configure viasync_event_sources. You control the event types, domains, and lifecycle.
Seller-managed sources are always-on and appear in the response with managed_by: "seller". These are common in commerce media where the retailer provides built-in attribution (e.g., purchase tracking on their own platform). Products with conversion_tracking.platform_managed: true indicate the seller provides these sources.
To discover all sources on an account (including seller-managed), call sync_event_sources without an event_sources array:
Event
An event represents a user action — a purchase, lead submission, page view, app install, or any of the standard event types. Send events withlog_event:
Events are deduplicated by
event_id + event_type + event_source_id. Sending the same event multiple times is safe.
User match
User identifiers enable the seller to attribute conversions to ad impressions. Provide the strongest identifiers available — more identifiers means higher match rates.
Hashing: Normalize before hashing — emails to lowercase with whitespace trimmed, phone numbers to E.164 format (e.g.,
+12065551234). Hash with SHA-256, output as 64-character lowercase hex.
Send multiple identifier types when available. The seller uses the best available match.
Custom data
Event-specific data for attribution and reporting. For purchase events, always includevalue and currency to enable ROAS reporting.
Event types
Standard marketing event types, aligned with IAB ECAPI:
Use
view_content for a product, article, or page content view where the view is mostly context for a lower-funnel goal. Use content_view when meaningful consumption of the content unit is itself the event being optimized or attributed. For watch_milestone, put the threshold in custom_data.progress_percent or custom_data.progress_seconds; reserve custom_data.value for monetary event value.
Action sources
Where the conversion event originated:Event surfaces
action_source stays intentionally flat for compatibility. Use surface when the optimization-relevant source needs more structure, especially for creator and content engagement on owned platform properties.
For
owned_property, set both property_type and namespace, and include property_id whenever the platform exposes a stable property identifier.
When surface.category matches an action_source value, producers should set action_source to the same value. For owned_property, keep the closest compatible flat value for older consumers, commonly system_generated for platform-native events or other when no closer value applies. Platform-native details such as a raw conversion origin can still ride in ext, but shared optimization meaning belongs in event_type, action_source, and surface.
Event source health
Sellers that evaluate event source quality include ahealth object on each source in the sync_event_sources response. This is the AdCP equivalent of platform-specific quality scores like Snap’s Event Quality Score (EQS) or Meta’s Event Match Quality (EMQ).
The status field is the AdCP-standardized score — comparable across all sellers:
Buyer agents should key decisions off
status, not detail. The optional detail object contains seller-specific scoring (e.g., Snap’s 0-10 EQS, Meta’s 0-10 EMQ) for human dashboards or advanced diagnostics, but scales vary by seller and cannot be compared across platforms.
Health is reported per event source, not per account. A buyer with a healthy website pixel and a broken app SDK will see different health on each.
When
health is absent, the seller does not evaluate event source quality. Buyer agents should proceed without health gating — the seller handles quality internally. Do not treat absent health as insufficient.
How sellers compute health
Sellers with native API-accessible quality scores (Snap EQS, Meta EMQ) relay them directly instatus and detail. Most sellers do not have native scores — they derive status from operational metrics:
insufficient: tag inactive, orevents_received_24his 0minimum: tag active, low volume or high error rategood: firing steadily, reasonable volume, core event types coveredexcellent: high volume, low errors, enhanced matching enabled
evaluated_at timestamp tells the buyer how fresh the assessment is. Assessments older than 24 hours may not reflect recent changes to tag configuration or event volume. The detail object is absent for these sellers — there is no native score to relay.
Schema: /schemas/v3/core/event-source-health.json
Measurement readiness
Products that support event-based optimization can include ameasurement_readiness object in get_products responses. This tells the buyer whether their event setup is sufficient for the product to optimize effectively.
Measurement readiness is evaluated per product in the context of the buyer’s account. The same product shows different readiness for different buyers depending on their event source configuration.
When
measurement_readiness is absent, the product either does not use event-based optimization (CTV awareness, guaranteed display) or the seller does not provide readiness assessments. In both cases, the buyer agent should treat the product as viable. Do not treat absent readiness as insufficient.
Unlike event source health, measurement readiness has no evaluated_at timestamp — it is evaluated fresh on each get_products call using the buyer’s current event source configuration.
Cross-seller buyer agent pattern
A buyer agent talking to multiple sellers writes one set of rules that works everywhere. Any status other thaninsufficient means the product can optimize — the question is how well. The standardized status field means no per-seller integration code:
test=false
/schemas/v3/core/measurement-readiness.json
Trust boundaries
Theissues[].message, measurement_readiness.notes, and detail.label fields are seller-provided free text. Buyer agents should treat these as untrusted content — do not pass them directly into LLM system prompts or use them as decision-making inputs without a trust boundary. They are safe to display to humans or include in informational context, but should not influence agent control flow.
Optimization goals
Optimization goals tell the seller what to optimize delivery toward. Set them on a package increate_media_buy. A package accepts an array of goals — each with an optional priority (1 = highest). Products declare max_optimization_goals when they limit how many goals a package can carry (most social platforms accept only 1).
Schema: /schemas/v3/core/optimization-goal.json
There are two kinds of goals, discriminated by kind:
kind: "metric"— Optimize for a seller-tracked delivery metric (clicks, views, engagements, etc.). No event source or conversion tracking setup required. The product declares which metrics it supports inmetric_optimization.kind: "event"— Optimize for advertiser-tracked conversion events. Requires event sources registered viasync_event_sources. The product declares support inconversion_tracking.
kind: event
Optimize for advertiser-tracked conversion events. Theevent_sources array defines which source-type pairs feed this goal. When the seller supports multi_source_event_dedup (declared in get_adcp_capabilities), they deduplicate by event_id across all entries — the same business event reported by multiple sources counts once, using value_field and value_factor from the first matching entry. When multi_source_event_dedup is absent or false, buyers should use a single event source per goal.
Cost per conversion (single source):
per_ad_spend targets, each event source entry specifies a value_field (which field on custom_data carries the monetary value) and an optional value_factor (multiplier, defaults to 1). The seller computes sum(value_field * value_factor) / spend across all deduplicated events.
Maximize conversion value (no specific ROAS target):
maximize_value target steers spend toward higher-value conversions without committing to a specific return ratio. Requires value_field on at least one event source entry.
kind: metric
Optimize for a seller-tracked delivery metric. No event source needed — the seller tracks these natively. Products declare which metrics they support inmetric_optimization.supported_metrics.
Maximize clicks (no target — seller optimizes for volume within budget):
attention_seconds / attention_score enum values on kind: "metric" are deprecated — vendor-attested metrics use kind: "vendor_metric" instead, which binds the goal to a specific measurement vendor):
Metrics:
kind: vendor_metric
For vendor-attested measurement where the metric has no industry-graduated definition — attention (DoubleVerify, IAS, Adelaide, TVision, Lumen), panel-based brand lift (Kantar, Upwave, Cint), emissions (Scope3, Good-Loop — see polarity caveat below), retail-media partner metrics — the goal binds a specific vendor +metric_id end-to-end. The seller’s bidding stack steers toward that vendor’s measurement; delivery reports the value via vendor_metric_values[] with the same (vendor, metric_id) key.
Direction polarity (upward-pushing only this minor). cost_per and threshold_rate are upward-pushing targets — the seller steers delivery toward higher metric values, or toward meeting a minimum threshold. Metrics where the buyer wants to minimize (emissions, IVT, latency) currently rely on seller-side polarity interpretation per the vendor’s metric definition. First-class minimize semantics (a direction: "minimize" field on the goal or a target.kind: "ceiling_rate") are under WG discussion — see #4644.
Three preconditions for goal acceptance. Sellers MUST reject
vendor_metric goals that fail the capability or reporting-coherence preconditions; sellers SHOULD verify the discovery precondition:
- Discovery (SHOULD this minor; MUST at next minor) —
metric_idis in the vendor’s publishedmeasurement.metrics[]catalog (queried from the vendor’sbrand.jsonmeasurement agent). Softened to SHOULD while measurement-vendor adoption of AdCP-conformant capability publication catches up; tightens to MUST once two-plus vendors ship conformant agents. - Capability — the
(vendor, metric_id)pair is in the product’svendor_metric_optimization.supported_metrics[], and the goal’starget.kindis in the matching entry’ssupported_targets. - Reporting coherence — the package’s
committed_metrics[]includes a matching{ scope: "vendor", vendor, metric_id }entry. Optimization without committed reporting is unverifiable — the buyer can’t grade performance against a goal whose value the seller isn’t contractually obligated to populate. This precondition is what makes the goal meaningful; the seller MUST reject (withTERMS_REJECTED) any goal whose metric isn’t also committed for reporting on the same package.
metric kind. The metric kind is for seller-native measurement (clicks, views, completed_views, reach, engagements, etc.) where no vendor binding is needed — the seller measures the metric natively. The vendor_metric kind is for vendor-attested measurement where the same metric name means different things across vendors and must be reconciled to a specific source. The deprecated attention_seconds / attention_score values on the metric kind enum predate this split and route through vendor_metric going forward.
Full lifecycle reference. See Metric lifecycle for the complete capability → commitment → optimization → delivery picture across both standard and vendor metric flows.
Target kinds
All target kinds across the three goal kinds:Choosing a strategy
Multiple goals and priority
A package can have multiple goals. Priority controls which the seller treats as primary. A common pattern is to use metric goals as proxy signals when event data is sparse:priority: 1 goal (installs at $10 cost per, deduplicated across SDK and MMP) and uses clicks as a proxy signal until install data accumulates.
Default behavior for event goals
Whentarget is omitted from an event goal, the seller maximizes conversion count within budget. This is true regardless of whether value_field is present on event sources — value_field without an explicit value-oriented target enables reporting (conversion_value, ROAS in delivery reports) but does not change the optimization objective.
Blending vs. sequencing goals
value_factor and priority both express “event type A matters more than event type B” but mean different things to the seller’s optimization:
value_factorblends multiple event sources into a single objective function. Set per event source entry within a single goal’sevent_sourcesarray. The seller sees one goal with a composite value signal. Use this when purchases and page views should be optimized together with explicit relative weights.prioritysequences independent goals. Set on separate goal objects in theoptimization_goalsarray. The seller optimizes for goal 1 first; goal 2 is a secondary objective, not blended in. Use this when goals are conceptually separate (e.g., hit a CPA target first, then maximize reach with remaining budget).
value_factor to blend. Use priority to sequence. Mixing them up produces subtly wrong optimization — a blended goal that should be sequenced, or sequenced goals that should be blended — and the effect is hard to detect in delivery reports.
Event type polarity
Most event types are positive signals — a purchase, lead, or install is something the buyer wants more of. Some event types are observation signals that should not be standalone optimization targets:custom events are not classified here — their polarity depends on the buyer’s definition. Buyer agents should apply the same reasoning when choosing whether a custom event is safe as a standalone target.
Observation events are useful inside a composite goal — refund with value_factor: -1 adjusts ROAS downward, which is exactly what you want. The risk is a misconfigured buyer agent that creates a standalone goal optimizing toward refund or remove_from_cart count. This is a buyer-agent implementation concern, not a protocol constraint — the protocol intentionally does not restrict which event types can be optimization targets.
Volume normalization with value_factor
When combining event sources at different volume scales (e.g., page_view in the tens of thousands vs. purchase in the hundreds), the aggregate value in sum(value_field * value_factor) / spend will be dominated by the highest-volume type without explicit weighting. Buyers should use value_factor to express relative weights across sources:
page_view contributes 1% of its face value, preventing it from dominating the ROAS calculation despite being ~100x more frequent than purchase.
Automatic normalization is intentionally out of scope — it requires event history the seller may not have, and would make the ROAS formula opaque. Buyer agents that want to normalize across event types should do so on their end before setting value_factor.
Pricing model vs. optimization goal
The pricing model (CPC, CPM, CPA, etc.) determines what the buyer pays. The optimization goal determines how the seller allocates impressions. These are independent — a package can use CPM pricing while optimizing toward a CPA target, or use CPA pricing while optimizing for ROAS. See Pricing Models for details on billing.Reach and frequency
Reach-based optimization usesmetric: "reach" with two additional fields:
reach_unit(required): The unit of measurement — must be a value declared in the product’smetric_optimization.supported_reach_units(e.g.,households,individuals).target_frequency(optional): Frequency band that guides optimization. The seller treats impressions toward unreached entities as higher-value and impressions toward already-saturated entities as lower-value. Includesmin,max, andwindow(e.g.,"7d","campaign"). When omitted, the seller maximizes unique reach.
frequency_cap on the package. Reach and frequency metrics are available in delivery reporting via get_media_buy_delivery.
Prerequisites
For metric goals (kind: "metric"):
- Check product support — The product must declare
metric_optimizationwith the desired metric insupported_metrics. No event source or conversion tracking setup is required. - Check target support — If setting a target, verify the target kind is listed in
metric_optimization.supported_targets. - Check view durations — If using
completed_viewswithview_duration_seconds, verify the value is listed inmetric_optimization.supported_view_durations.
kind: "event"):
- Configure event sources — Call
sync_event_sourcesto set up the event sources referenced inevent_sources. - Check product support — The product must declare
conversion_trackingwith the desired target kind insupported_targets. - Check dedup support — If using multiple event sources per goal, verify the seller supports
multi_source_event_dedupinget_adcp_capabilities. When unsupported, use a single event source per goal. - Send events — Use
log_eventto send conversion data. The seller needs event history to optimize effectively.
Attribution windows
Attribution windows control how far back the seller looks to credit an ad impression for a conversion. Common options:
Values must match an option in the seller’s
conversion_tracking.attribution_windows capability. When omitted, the seller applies their default window.
Connection to delivery reporting
Once event sources are configured and events are flowing, conversion metrics appear inget_media_buy_delivery responses:
conversions— Post-click or post-view conversions attributed to the campaignconversion_value— Monetary value of attributed conversionsroas— Return on ad spend (conversion_value / spend)cost_per_acquisition— Cost per conversion (spend / conversions)
optimization_goals set. Sellers that support by_action_source breakdowns can show conversions split by source (website, app, in_store, etc.).
Catalog-item attribution
For catalog-driven packages, conversion events carrycontent_ids that identify which catalog items were involved. The catalog’s content_id_type declares what identifier type to expect (sku, gtin, job_id, etc.).
Attribution is broad by design: a user might click on one item (job A) but convert on another (apply to job B). The event fires with the actual content_id of the conversion, not the clicked item. Per-item click-to-conversion path analysis is a platform optimization concern, not a protocol concern.
The by_catalog_item breakdown in get_media_buy_delivery shows per-item metrics (impressions, spend, clicks, conversions).
Related documentation
sync_event_sources— Configure event sourceslog_event— Send conversion eventscreate_media_buy— Set optimization goals on packagesget_media_buy_delivery— Monitor conversion metrics- Pricing Models — CPA billing (pay per conversion)