Skip to main content
Video shows up in two very different shapes, and AdCP models them as two different asset types with opposite rules. Knowing which one you’re holding explains why some video assets need a width and height and others need none at all.

Two kinds of video creative

A hosted video file is a concrete encoded file you host — an MP4 you hand over and that gets served directly. It has one resolution, one duration, one codec: the facts are baked into the file. A VAST tag is not a file. It’s a pointer (a URL, or inline VAST XML) to a runtime decision made by a video ad server. When a player calls that tag, the ad server can return several renditions of different sizes and bitrates, an adaptive stream, or an interactive ad — and the player picks the one that fits the screen it’s playing on. The geometry isn’t decided until serve time. These map to the two canonical video formats in the glossary: video_hosted and video_vast.
The asset must match the format. A raw video file (MP4) is a video_hosted asset — it is not a VAST tag, and labeling it video_vast does not make it one. A video_vast creative must carry an actual VAST document (a tag URL or inline VAST XML). A raw file sent under a video_vast label is not AdCP-schema compliant: publishers that validate schema reject the buy, and others silently drop the creative. If all you have is a hosted file, use video_hosted; reserve video_vast for a real VAST tag.
Compatibility does not change the asset type. On a lenient compatibility path, a seller may accept a correctly typed video asset in a slot whose declared format is VAST-shaped. Apostra still emits asset_type: video and preserves the file’s intrinsic dimensions; it never relabels an MP4 as a VAST document. This seller-boundary adaptation is not canonical video_vast authoring, and a strict validator can still reject the slot mismatch.

Sending a VAST tag to an older seller

No buyer action is needed when a selected seller still accepts the older AdCP creative format. If your creative carries a real VAST tag and is pinned to one unconstrained video_vast option, Apostra sends the standard VAST identity that the seller expects while keeping your saved creative and product selection in the current canonical format. An exact format mapping published by the seller always takes priority. Apostra does not guess when the option has size or duration constraints, declarations conflict, or another creative format has no exact mapping. In those cases the request stops with an actionable format error before the creative is sent. An older seller receives one format identity per creative, and its identity includes a player size when the product states one. So a VAST creative assigned to several of one seller’s products must fit a single identity. When some of those products state a player size and others leave it open, the creative is not sent to that seller: its row in the sync_creatives result fails with ADCP_CREATIVE_FORMAT_PROJECTION_FAILED, naming each package’s format. The other creatives in the request are still sent. Until the AdCP SDK picks the one identity every product accepts (adcp-client#3506), assign the products that state a size and those that don’t to two creatives with their own creative_ids, such as the original and a copy.

Why a file has dimensions and a tag doesn’t

A hosted video file has an intrinsic native resolution — there is a real, single width and height you can read straight out of the file. So the video asset requires width and height. Requiring them is just recording a fact that already exists; an ad server can’t place the file correctly without them. A VAST tag has no single dimension. Inside the VAST response, each rendition carries its own width and height, and the player selects one at request time. There is no “the tag is 1920×1080” — asking a tag for one pixel size would throw away exactly the flexibility VAST exists to provide. So the vast asset carries no width/height at all. This is deliberate, not an omission. The short version: dimensions belong to a file, not to a tag.

Where size and duration rules live

If the asset itself doesn’t carry a shape, how does a seller say “this slot takes 16:9 video up to 30 seconds”? Those are constraints, and constraints live on the format, not the asset:
  • Aspect ratio and orientation (16:9, vertical/horizontal/square)
  • Size ranges (minimum and maximum width/height)
  • Duration ranges (a maximum length, or an exact required duration)
  • Containers and codecs the format accepts
This split is the core idea: an asset describes the content you’re sending; a format describes what a placement will accept. A hosted file is checked against those constraints up front because its facts are known. A VAST tag is checked at serve time, when the actual rendition is chosen.

Accepted formats

For a hosted video file, the delivery container is MP4 (H.264 video, AAC audio) — the format every player and ad server supports. Editing/source formats like MOV (QuickTime/ProRes) are mezzanines: convert them to MP4 before delivery. WebM is a browser-playback format, not a delivery creative. For a VAST tag, the container question doesn’t apply to the asset — the renditions and their codecs live inside the VAST response, and the player chooses one that it can play. When in doubt, check the format’s requirements: a format states the containers and codecs it accepts, so an agent can pick the right asset before sending it.

Declare a VAST tag’s version

Set vast_version on the asset when you create or update a creative that registers a tag. The accepted values are the versions AdCP defines — 2.0, 3.0, 4.0, 4.1, 4.2, 4.3 — and anything else is rejected, because a value outside that set would be refused further down the chain by any consumer validating against the spec. A tag’s own document is fetched at request time, so what you declare is an assertion. When you save a VAST tag, Apostra also requests it server-side the way a player would — following wrappers and redirects — and records what it returned. Declare what your ad server actually returns; if you declare nothing, the version Apostra read from the tag is used.
A VAST entry in linked_assets takes the same vast_version field. In V3, save_creative takes it as vastVersion on a VAST asset, for a tag URL and for inline VAST XML sent as a dataUrl. The V3 creative read and the save_creative response return the stored version as assets[].vastVersion.
Declare the version for inline VAST whenever you know it. Apostra reads a version only from an InLine ad it reaches, so inline XML that wraps a tag Apostra cannot fetch at save time is saved without one. Managed ad-server destinations check the version against the versions they hold delivery evidence for. The version is the one you declared, else the one Apostra read from the tag when you saved it, for every way a tag can arrive (url_asset, linked_assets, or a slot). A tag URL that has neither a declared nor a recorded version is not refused. When Apostra sends the creative to a managed destination it reads the tag again, uses the version the tag returns, and keeps it beside the asset so later sends do not need to read it. This covers a tag that could not be read when it was saved and creatives saved before Apostra recorded versions. If the tag cannot be read at that moment either, the creative is not sent on that attempt and Apostra tries again on the next sync. It reports destination_vast_version_unavailable, the sync outcome is destination_profile_refused with must_act: apostra, and you usually do not need to change the creative. Declare vast_version on the asset if you would rather not depend on that read. Inline VAST XML you saved is stored as a document and read the same way, and Apostra reads a version only from an InLine ad it reaches, so XML that wraps a tag Apostra cannot fetch needs a declared vast_version. A version outside a destination’s evidence returns destination_profile_not_certified rather than a rejection, because the limit is what we have proven, not what the ad server refuses.

What Apostra reads from the tag

Saving a VAST tag records an inspection of it: the version of each document in the wrapper chain and of the final InLine ad, the registrable domains the chain passed through, and whether the tag returned an ad, an empty response, an error, or could not be read (ad, no_ad, error, unreadable). Apostra’s request carries none of a real player’s context, so an ad server may answer it differently from how it answers a player. The save is never refused because of what the tag returned. The creative’s warnings tell you when:
  • the tag returned no ad (an empty VAST response);
  • the tag returned an error — an <Error>-only response (with its IAB error code when it names one), malformed XML, or a wrapper chain that loops or runs longer than a player will follow;
  • you declared one VAST version but the tag returns another. The version you declared is kept.
A tag Apostra could not fetch (a timeout, a blocked or private address, an HTTP error) is saved without a warning, because that says nothing about the tag itself. A tag can also change after it was saved, so the warnings describe the moment it was inspected. The same facts, and a preview that plays the tag’s video, are returned when you preview the creative. See preview a VAST tag.

Managed upload produces a portable hosted video

Managed MP4 upload is an AdCP creative capability, not a Draft Digital, GAM, or FreeWheel-specific feature. On the Buyer V2 or shared V3 connector, a buyer selects an advertiser and opens upload_creative_asset; both expose the same Task and storage flow, and no destination connection or provider account is required. Where the managed video rollout is enabled, its browser Task uploads an MP4 of up to 2,000,000,000 bytes directly into private storage. That source remains bound to the exact buyer, authenticated principal, and advertiser; upload does not select or contact a destination. Upload completion does not immediately create a public file. The source first passes checksum, file-signature, malware-policy, and media inspection. While that work is running, the upload task reports a processing state such as quarantined or inspecting. Once the exact source generation reaches promoted, the task returns an opaque HTTPS delivery_url and a source_uid. The source reference is an opaque, owner-bound continuation identity, not a signed storage capability. The Task completion activity can hand it to the assistant for save_creative; every use rechecks the customer, principal, and advertiser. Finalization alone creates no Creative or campaign membership. To assemble a promoted buyer-owned MP4, call save_creative with campaignId, formatKind, the campaign product’s exact formatOptionRef, and sourceAssets; bind the MP4 to the format’s video slot and include every other required media slot, such as an uploaded thumbnail. Supply required copy and destination slots through social and landing_page_url. Apostra re-proves the source owner and attaches the Creative to the campaign. It is then added to every media buy on that campaign whose product accepts its format, like any other Creative, and sellers receive a link to the MP4 that they fetch once when they ingest it. The V2 adopt_managed_video_source operation remains unchanged for legacy provider-bound sources and is not a fallback for a buyer-owned V3 source. See Bring your own creative. The hosted URL is adapter-neutral. It can be passed to GAM, FreeWheel, another built-in adapter, or an external AdCP sales agent without changing its asset type. It is not a VAST tag and it does not by itself prove that every destination accepts the source encoding. Apostra still applies the exact destination profile before a managed adapter is called, and an external sales agent may apply its own declared format constraints. Each URL resolves only the promoted object generation and checksum recorded at publication. It supports HEAD, full GET, and a single byte range; expires with the retained source and fails closed after revocation or source lifecycle changes. It does not expose a bucket, object name, signed cloud-storage URL, customer name, advertiser name, or original filename. Responses use no-store, so this first version is an authorization-checked origin rather than an unpriced CDN cache. Basic upload and inspection consume no IUs. Source-delivery bytes are measured as a zero-IU candidate so Apostra can price actual origin/CDN cost before any Rate Card charge is introduced. Transcoding, retained rendition storage, and VAST publication remain separate activities; uploading a file does not silently perform or charge for them.

Managed ad-server compatibility

For a managed ad-server storefront, Apostra checks a hosted video’s actual file metadata against the certified destination profile before sync_creatives changes anything in the ad server. Values written in the request do not replace the inspected file’s size, duration, dimensions, codecs, container, or bitrate. The currently certified standard Google Ad Manager path accepts an HTTPS-hosted MP4 with H.264 video and AAC audio at 1920×1080, from 0.3 seconds through 20 minutes, up to 1.9 GB and 60,000 kbps. The bitrate is the current live-tested safe envelope, not a claim about GAM’s absolute maximum. GAM creates its own delivery transcodes after accepting the source. Every managed FreeWheel connection accepts a buyer-supplied HTTPS VAST tag of any version AdCP defines: 2.0, 3.0, 4.0, 4.1, 4.2 or 4.3. Apostra sends your tag to FreeWheel unchanged; it does not convert it to another version. No per-buyer or per-publisher enrollment is needed. If FreeWheel refuses a tag, its own validation error is returned on the creative result. A FreeWheel media buy is booked only after its creatives are trafficked, so a refused tag never leaves a booked buy running without its creative. Every managed Google Ad Manager connection also accepts a buyer-supplied HTTPS VAST tag URL of any of those versions. Google Ad Manager serves a third-party VAST tag only by redirecting to a URL, so Apostra sends it the address of its hosted VAST wrapper around your tag, and Google Ad Manager requests that address each time it serves the ad. The wrapper fills the macros in your tag URL, such as {CACHEBUSTER} and {GDPR}, with the values Google Ad Manager sends. See Macros in a VAST or DAAST tag URL. Inline VAST XML cannot be sent to Google Ad Manager: the creative result is destination_profile_rejected with the reason ingress:vast_xml, and nothing reaches the ad server. Save the tag as a URL tag, or host the document and send its URL. Hosted video files keep the Google Ad Manager path described above.
Hosted MP4 ingress and the managed VAST 4.2 publisher still require authenticated certification before Apostra calls FreeWheel. Send FreeWheel a VAST tag rather than a video file.
The video options a managed ad-server product advertises follow the same destination profiles. Product discovery lists a video_hosted or video_vast format option only when the source’s ad server accepts that format kind, so you are not offered an option whose creatives would always be refused. Other format kinds, and sources that are not managed Google Ad Manager or FreeWheel connections, are listed as the seller declares them. This check covers the format kind only. Whether a particular creative is accepted, such as VAST XML sent inline, is still decided when you call sync_creatives, with the reasons described below. The per-creative result tells you what to do next:
  • destination_profile_not_certified means the authenticated integration path is still unknown. Do not retry the same destination; use a certified route or wait for certification.
  • destination_rendition_required means the source could be made compatible, but a managed output is required. Unless your account is enrolled in the managed rendition VAST canary, provide a file that already matches the destination profile.
  • destination_profile_rejected means the creative cannot use that profile. Read the returned reasons and replace or correct the source. A reason of ingress:vast_xml means the creative carries VAST XML inline where the destination takes only a URL. A reason of missing:<field> means the destination requires evidence the creative does not carry, such as a hosted file whose metadata could not be inspected. A VAST tag URL with no recorded version is not rejected for that: it is read when it is sent; see the next item.
  • destination_vast_version_unavailable means Apostra could not read the version of a VAST tag URL when it tried to send it. This is on our side: the creative is not sent on this attempt and is read again on the next sync, usually with no change from you. Declare vast_version on the asset to avoid the read.
  • destination_destructive_sync_blocked means a mixed delete_missing request could not be safely filtered. Correct the rejected creatives or retry without delete_missing; no creatives on that destination leg were forwarded.
These checks apply to hosted URLs at creative sync. Managed upload is separate from Apostra’s general chat-attachment limit, and it never converts an MP4 into VAST merely by publishing the file.

How a VAST tag’s fit is confirmed

Because a VAST tag resolves at request time, its fit to a placement is guaranteed at serve time: the player (or the ad server’s creative profile) selects a rendition matching the slot’s size, picks an allowed codec/bitrate, and enforces the duration cap. A check at submission time can only ever be best-effort — a tag can chain through wrappers, return different renditions per request, carry an adaptive manifest, or be an interactive ad with no static rendition to measure. So a dimensionless VAST asset is normal and expected; the matching happens later, where the actual rendition is known.

Duration

Duration behaves like dimensions split across the two shapes. A hosted file has an intrinsic duration, so it’s read from the file and carried alongside the dimensions. For a VAST tag, duration is the one whole-ad fact the tag exposes, and the ad server reads it from the tag. Either way, a placement’s maximum length is a format constraint — checked against the file up front, or against the rendition at serve time. When a buyer supplies a VAST duration, Apostra keeps that declaration for the placement-duration check if the tag cannot be read. A seller format is selected only from a duration parsed from the VAST document. Apostra never uses a buyer declaration, or a rendition’s width and height, to choose between a seller’s duration-specific VAST formats. If the document has no readable duration, the creative receives a named format refusal instead of a guessed seller format. When Apostra forwards a VAST tag to a seller, the asset carries duration_ms when the creative has one: the duration you declared, otherwise the duration Apostra measured from the tag. It is the AdCP “expected media duration”, so a seller can read it but should treat a declared value as your assertion, not proof. A tag with neither value is sent without duration_ms. Apostra does not send tracking_events for a tag: it asserts facts about the VAST document that only an inspection of the document can establish. It sends macro_declarations only for macros in the tag URL itself, as described in Macros in a VAST or DAAST tag URL.