Skip to main content
This guide is for sellers publishing linear TV inventory and for buyer agents planning, trafficking, and reconciling it. Linear television spots use the canonical video_hosted format. They do not require a new canonical: the product narrows hosted video to a fixed duration and file specification with a single video slot. Network, station, daypart, clearance, and audience measurement are media-buy facts, not creative facts. Radio has its own guide.

Reference format declarations

The following :15, :30, and :60 declarations use a common US delivery profile: 16:9 at 1920×1080 or larger, 29.97 fps, MPEG-2 4:2:2 video with PCM audio in an MXF wrapper, and ATSC A/85 loudness (-24 LKFS ±2 dB, true peak -2 dBTP). A station or network may publish a different accepted profile (EBU R 128 loudness in Europe, for example), and the buyer matches the declaration it is offered rather than this one. See Delivery files for the file fields, including the experimental loudness object.

:15 spot

:30 spot

:60 spot

Each unit length is its own format option, and a product may offer any subset. Other lengths (:10, :05) use the same shape with a different duration_ms_exact.

Delivery files

Broadcast traffic systems ingest a mezzanine file, not a web-playable MP4. A video_hosted option narrows to that file with containers, video_codecs, and audio_codecs. The codec slugs name codec families, not recording formats or profiles. avc_intra and xavc are H.264-based but distinct from h264: a product that accepts h264 does not thereby accept them. Wrapper (containers) and bitrate bounds are declared separately; chroma subsampling, profile, and scan type are not machine-declarable on video_hosted yet, so a seller states them in the product description or its delivery spec sheet. Other mezzanine codecs (for example DNxHD) are not yet in the enum, and captions only states whether captions are required.

Loudness (experimental)

loudness states the audio loudness a delivered spot MUST meet. It is optional and experimental in AdCP 3.3, so sellers that populate it list creative.broadcast_loudness in experimental_features. The three numeric fields carry the same semantics as loudness_lufs, loudness_tolerance_db, and true_peak_dbfs on audio_hosted. standard labels the requirement; the numeric fields are what the receiver checks, and standard alone does not imply numeric values. Declare all three numeric fields when the requirement is to be checked.

Creative identifiers

Use the manifest’s industry_identifiers list for the traffic or clearance identifier the broadcaster expects. AdCP supports ad_id, isci, clearcast_clock, and idcrea. Ad-ID is the common US television identifier; ISCI persists in some station traffic systems; Clearcast and IDcrea serve the UK and France. Choose the identifier your station’s traffic system keys on. A manifest may carry more than one identifier (for example both ad_id and isci when a traffic system still keys on ISCI). Each duration cut carries its own identifiers: the :15 and :30 versions of one spot are two creatives.
An airing ties back to its creative in two hops. by_spot[].creative_id is the AdCP creative identifier you synced, and that creative’s manifest carries the industry_identifiers value the station’s traffic system keys on. by_spot rows do not carry the logged Ad-ID or ISCI themselves; the seller maps the logged identifier to your creative_id.

No VAST or tracker semantics

The reference declarations contain only the required video_main file slot. A linear spot is played out by the station’s automation system, where no VAST tag, impression pixel, click tracker, or clickthrough URL can operate. Their absence is part of the format contract; do not add tracker slots or represent these spots as video_vast. Streaming and connected-TV inventory is a separate execution context. It may use video_hosted with explicitly declared trackers or video_vast for VAST delivery; see Video and CTV. A spot simulcast on a streaming service does not make trackers operational on the linear airing, and a linear buy’s counts do not come from trackers at all.

Networks, stations, and dayparts

Where a spot runs is described outside the creative, in three places:
  • Inventory — a product’s publisher_properties select properties from the seller’s adagents.json. Linear properties carry station_id (a call sign), facility_id (a regulator-prefixed license such as fcc:73953), or network_id identifiers. Model the product by who commits the order: a network or rep-firm package is one product even when it fulfills across affiliates, while station-direct inventory is a product per selling station or group. See Broadcast TV package forecast.
  • Daypart on the buy — daypart_targets on the package’s targeting overlay (days, a timezone where inventory_local evaluates each station’s own clock, and whole-hour start_hour/end_hour) when the product supports it. Experimental in AdCP 3.3 (media_buy.daypart_granularity): an entry may instead carry start_time and end_time as HH:MM, so a window such as 19:30–23:00 or a break at a programme junction is expressible. A product declares the finest clock it honors in overlay_support.daypart_targets.time_granularity (hour, quarter_hour, or minute), and a seller rejects a finer time than it declares with UNSUPPORTED_FEATURE rather than rounding. See Minute-resolution dayparts.
  • Daypart on the report — daypart on a by_spot row is a seller-defined label such as prime_time or prime_access. There is no daypart enum, so a buyer reconciling across sellers compares the as-run aired_at against the daypart it bought rather than matching labels.

Delivery measurement and reconciliation

Linear TV plans and guarantees on measured audience: panel, set-top-box, and automatic-content-recognition data licensed from a currency or measurement provider, not served-event counts. What is invoiced depends on the pricing model. CPM and CPP bill on the currency count; a per-spot unit-rate buy bills on aired units, where the as-run log is the evidence of what aired. A cpm (or cpp) option with fixed_price is a fixed rate the seller sets; it is never a bid, and auction semantics apply only when fixed_price is absent, which broadcast sellers do not use. Three declarations carry the audience contract, and a fourth question (did it air?) is answered by separate evidence.
  • Demographic notation — demographic_system and demographic on the forecast (and on the CPP pricing option) say how the audience is written. nielsen notation is A25-54, P2+, W18-49. The enum also carries videoamp (age bands with an optional gender prefix, such as P25-54) and nmo (Netherlands: age bands such as 25-67 or M25-54); the notation strings for both are still being confirmed with the providers, so treat the examples as provisional. The worked example below writes its demo in nielsen notation while VideoAmp counts it; a seller whose provider publishes its own notation declares demographic_system: "videoamp" instead. Notation is not a provider.
  • Provider identity — measurement_source on forecast and delivery rows names whose data produced the numbers, as a lowercase slug (nielsen, videoamp, ispot). The same demographic_system: "nielsen" notation can be measured by different providers. The contract names the billing vendor as a brand key ({ "domain": "videoamp.com" }, resolved through brand.json) in measurement_terms.billing_measurement.vendor (nielsen.com, videoamp.com, ispot.tv). A seller offering the same inventory under more than one currency publishes a forecast per measurement_source; each forecast belongs to one product or proposal, so that means one per currency.
  • Maturation — reporting_capabilities.measurement_windows declares the stages: live (the airing day), c3 (live plus three days of time-shifted viewing), and c7 (live plus seven). A product typically marks one window is_guarantee_basis: true, but which window a deal settles on is a term of the deal: read it from billing_measurement.measurement_window, not from convention. A window’s expected_availability_days says when the vendor publishes it; for C7 that is roughly two to three weeks after the airing. Until a row is is_final: true for the billing window, the number is provisional and not an invoicing source; a later window supersedes an earlier one with its own provisional-to-final lifecycle. See Billing authority.
Later windows only add time-shifted viewing, so live and c3 undershoot c7. Use them for pacing, and do not invoice or declare a shortfall on a provisional row. Airings from different weeks of one flight mature on different days, so a buyer reconciling a long flight pulls date slices and reads each at the window it has reached.

Reconciliation contract

Two kinds of evidence come back, and they answer different questions. The rules:
  • Airing evidence is not a counting source. Station logs, affidavits, and playout records prove that a spot aired. The audience currency turns airings into the audience-size number. Never infer audience from airings, and never infer an airing from audience: aired_at is what proves an airing.
  • As-run is reported apart from audience. A product that declares supports_spot_breakdown with available_metrics: [] provides airing evidence only; by_spot rows then carry no audience numbers. A product may instead declare metrics at spot grain, which appear when a later window supersedes an earlier one. A guarantee and its invoice reconcile against package rows (and the by_demographic row for the contracted demo), and the buyer does not rebuild package totals from spot rows.
  • Spot lifecycle is experimental and declared. In AdCP 3.3 a seller may also report scheduled, preempted, and makegood rows through by_spot[].spot_status, and says which it reports in supports_spot_breakdown.available_statuses (experimental, media_buy.spot_lifecycle). Only aired and makegood rows carry aired_at and prove an airing; a scheduled or preempted row carries scheduled_at instead and is not airing evidence. A status the seller does not list proves nothing: the absence of preempted rows is not evidence that nothing was preempted. See Optimization and reporting.
  • spot_id is stable. The same spot_id is reported under Live, C3, and C7 (and, where the seller reports lifecycle, from scheduled through aired or preempted), so a buyer joins as-run evidence to later audience rows on it. Audience rows join on (media_buy_id, package_id, reporting_period, measurement_window).
  • Completeness is explicit. by_spot_truncated: false means the as-run log is complete for the period. A buyer reconciling airings requests the spot dimension without a limit. A seller that cannot attribute a creative to an airing omits creative_id rather than guessing.
  • Who attests the airing is not declared. A buyer cannot tell a station traffic log from a third-party airing verification from the rows alone, and affidavit references are not carried on the wire today. Take both from the seller’s product description and the contract.

Worked example: a CPM impression guarantee on a demo

Pinnacle Media buys for Nova Brands from Harbor Broadcasting. Nova wants five million impressions among adults 25–54, delivered through :15 and :30 spots, counted by VideoAmp on the C7 window. The currency, window, and variance are agreed before the first spot airs.

Today’s limits

This works today with four gaps. Read them before building on it:
  • The demo is bound on the forecast, not on the price. A CPM pricing option has no demographic parameters, unlike a CPP option (parameters.demographic_system, demographic). measurement_terms.billing_measurement binds the vendor, the window, and the variance, but has no demographic field.
  • The guaranteed volume has no typed definition. purchases[].impressions is the contracted volume, and a buyer can require a floor on it with refine_proposals constraints.impressions (a hard minimum summed across purchases). The schema does not say that number counts measured audience, in which demo, from which vendor, or on which window. Those facts sit on the forecast and billing_measurement, and the forecast is outside commercial_terms, so it is not covered by terms_digest. Treat the demo as a term agreed in the human contract outside AdCP, not one a protocol agent can verify.
  • :30 equivalization is not expressible. Nothing declares whether a :15 impression counts as half a :30 unit or the factor to apply. A CPM prices impressions regardless of unit length, so on a buy that mixes :15 and :30 spots the equivalization rule is a term to settle outside the protocol.
  • No under-delivery trigger, and the schedule on the wire is experimental. makegood_policy does not name a volume shortfall as a trigger. A seller may report the planned schedule as scheduled rows in by_spot, but that field is experimental and seller-declared, so a buyer cannot assume one. See Makegoods.
Demo-bound CPM and :30 equivalization are tracked for 3.4 in #8001.

1. The seller publishes the product

The product is a linear_tv package with two video_hosted format options, one CPM price, and the measurement it will stand behind. VideoAmp is the billing vendor and C7 is the guarantee window. The final C7 record is due within 360 hours of the window closing, and the seller publishes it on get_media_buy_delivery. supports_spot_breakdown.available_metrics is empty, so spot rows are airing evidence only. The seller also declares the experimental spot lifecycle (available_statuses) and break position (supports_break_position), and lists media_buy.spot_lifecycle in experimental_features on get_adcp_capabilities.

2. The buyer asks for the outcome

The buyer fixes the goal and volume, and states the demo in the brief because the goal cannot carry it. Send criteria.outcome_target only to a seller that declares media_buy.outcome_target in get_adcp_capabilities; a seller that does not rejects the structured form with UNSUPPORTED_FEATURE, and the buyer falls back to the brief alone. cost_per is not sent: it is rejected on measured_impressions, and the CPM rate is the price. The goal is a planning input, not a guarantee.

3. The seller’s proposal carries the contract

The draft states the guaranteed volume, the price, the formats, and the agreed measurement terms on the purchase. Finalizing and accepting it (refine_proposals, accept_proposal) commits commercial_terms. The forecast repeats the demo and the currency so delivery rows can be compared against it, but it is not part of the committed terms and its numbers may change between draft and committed proposal.
The forecast’s measured_impressions is the same count that delivery reports as impressions with a measurement_source; in a forecast, plain impressions means the seller’s own ad-server count. To change a measurement term (a different vendor or window, a different variance), the buyer states the change in refine_proposals ask and reads the revised commercial_terms and terms_digest; there is no typed measurement-terms input on refinement. The deprecated create_media_buy path instead returns TERMS_REJECTED for a term the seller cannot support; see Accountability. If the seller confirms through a booking round or sales-desk review, accept_proposal returns a submitted arm that may carry expected_confirmation_at (added in 3.3), an estimate of when to check back. It is not a commitment, so schedule a status check for that time rather than polling tightly. Once accept_proposal returns the media buy and its purchase_bindings[], the buyer syncs each cut with sync_creatives, carrying its own identifier as in Creative identifiers, and assigns it to the package. Station clearance and delivery deadlines are outside the protocol.

4. Delivery: as-run first, audience later

The buyer reads delivery with the demographic breakdown for the contracted demo and the spot breakdown. A reconciliation pull omits the spot limit so the log is complete; this example caps it at four rows to stay short.
During the flight, rows are provisional: Live and C3 numbers with is_final: false. As-run evidence is available from the first reporting cycle, about 48 hours after an airing (expected_delay_minutes). Once C7 closes and the vendor publishes, the seller reports the settled row:

5. Reconciliation

The buyer reconciles two streams, each against its own evidence:
  1. Did it air? Start from the schedule. This seller reports scheduled rows (experimental, declared in available_statuses), so the planned spots arrive on the wire and each spot_id later resolves to an aired row or a preempted row. A seller that does not declare scheduled leaves the schedule outside the protocol, in the buyer’s traffic confirmation or the contract. Compare it against the complete by_spot log (by_spot_truncated: false). A scheduled spot that resolves to a row with aired_at is evidenced. A preempted row did not air, and the log here shows the replacement: spot_0431 is a makegood row whose replaces_spot_id is spot_0408. A scheduled spot with no row at all is unevidenced, and it is the basis for a makegood claim. creative_id and daypart on each aired row confirm that the right cut ran in the daypart the buyer bought, and break_position (experimental, supports_break_position) places the spot in its break. None of this involves an audience number.
  2. How many people? Read the by_demographic row for the contracted demo (A25-54 in nielsen notation, with the age interval echoed) and confirm by_demographic_suppressed is false. The package row must match the agreed billing_measurement: measurement_window: "c7" with is_final: true and measurement_source: "videoamp". The package row carries no demographic of its own, so the by_demographic row is how the buyer confirms the number is for the demo. 4,870,000 against the 5,000,000 guarantee is a shortfall of 130,000 impressions.
  3. What is billed? This example bills delivered impressions at the CPM, capped at the purchase budget: 4,870,000 ÷ 1,000 × $32 = $155,840, the spend on the final row. Over-delivery is not billed beyond the cap. That convention is a term of the deal. max_variance_percent is not a tolerance on the shortfall against the guarantee; it bounds how far two counts of the same thing may diverge before resolution is triggered. A buyer that holds its own VideoAmp or iSpot subscription reports its count with report_usage, as described in Billing authority.
  4. Is the shortfall remedied? The invoice above already prices out the 130,000 impressions, so an invoice_adjustment would be redundant here. Whether a volume shortfall triggers a remedy at all is a term of the deal (see Makegoods); additional_delivery would fill toward the guarantee and credit would carry the value forward.
If the C7 row has not arrived by the deadline (finalization_deadline_hours, counted from the close of the C7 window), the counterparty may fall back to its own attestation for invoicing. After the deadline the buyer is exposed: the seller’s own attestation could rest on provisional Live or C3 numbers, which understate C7. Set the deadline to match the vendor’s published availability, and watch expected_availability on the delivery row (or a reporting_delayed webhook) before it elapses.

Makegoods

measurement_terms.makegood_policy.available_remedies is a menu the seller commits to, ordered by seller preference: additional_delivery (extra airings), credit (a credit toward future buys on the account), and invoice_adjustment (a reduced invoice). A buyer proposing a remedy the seller does not support is rejected with TERMS_REJECTED at negotiation. The schema names performance-standard breaches, billing-variance breaches, and missed finalization deadlines as triggers. A volume shortfall against a guarantee is not among them, so whether and how it triggers a remedy is a term of the deal outside the protocol. Proposing, accepting, and disputing a remedy also happen out of band. Report-time spot lifecycle (experimental in AdCP 3.3). A seller that declares media_buy.spot_lifecycle in experimental_features can report a preemption and its replacement in by_spot:
  • A spot keeps one spot_id as it moves from scheduled to aired or to preempted. scheduled_at carries the planned slot (for a preempted row, the slot that was lost), and a preempted row has no aired_at.
  • A makegood row is a separate airing with its own spot_id, an aired_at, and a replaces_spot_id naming the preempted spot. A planned replacement is a scheduled row that already carries replaces_spot_id, so the buyer sees the link before it airs.
  • The seller lists the statuses it reports in supports_spot_breakdown.available_statuses. A status that is not listed proves nothing, so a buyer must not read missing preempted or makegood rows as “nothing was preempted”. A preemption surfaces as a preempted row on the next report; there is no separate notification, and a seller does not send these rows to callers pinned below 3.3.
These fields can change within 3.x; see experimental status and the spot breakdown reference. Not in 3.3. The criteria for an acceptable replacement airing (same or better daypart, same network or station, same unit length, a rating floor, inside the flight) are not machine-readable, and neither is an interactive replacement offer. Those are agreed in the human contract outside AdCP. Makegood airings can also land after the flight and after C7 closes, and they mature on their own windows. Contract-time pre-approval criteria for replacements are planned for 3.4 in #5683. See Video, Radio, Billing authority, Accountability, get_media_buy_delivery, and Creative manifests.