> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How AdCP thinks about video

> Why a hosted video carries pixel dimensions but a VAST tag doesn't — and where size, duration, and format rules actually live.

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](/v2/concepts/glossary): `video_hosted` and `video_vast`.

<Note>
  **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.
</Note>

<Note>
  **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.
</Note>

## 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 version is the one thing about a tag that cannot be measured: the document is
fetched at request time, so nothing about it is knowable when you submit it.
Because you are asserting it, declare what your ad server actually returns.

```json theme={null}
{
  "url_asset": {
    "url": "https://ads.example.com/tag.xml",
    "url_type": "vast",
    "vast_version": "2.0"
  }
}
```

Managed ad-server destinations check the declared version against the versions
they hold delivery evidence for. A tag that declares no version is not eligible
for those routes and fails creative sync with a `missing:vastVersion` reason —
so omitting it is not the safe default. 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.

## 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 `clickUrl`. Apostra re-proves the source owner and records the
campaign membership as `destination_required` without contacting a provider.
When V2 `attach_creatives_to_campaign` later selects a compatible Meta package,
Apostra resolves the durable Library asset under the current buyer,
advertiser, account, and credential immediately before Meta delivery. A revoked
or archived source, another destination, or a mismatched format fails before
Meta is called.
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](/v2/buyer/creatives/bring-your-own-creative#turning-an-uploaded-video-into-a-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.

The authenticated Talpa FreeWheel path has one positive direct-sold trafficking
proof for an external HTTPS VAST 2.0 URL: the integration test created the
FreeWheel creative resource, bound it to an ad unit node, then unbound and
deleted it.
Buyer-supplied VAST 2.0 can therefore use that exact, enrolled connection path.
This does not mean Talpa or FreeWheel supports only VAST 2.0; the test did not
establish a version ceiling, fetch or play the tag, or use Apostra-managed VAST.
FreeWheel's separate public programmatic policy does not define this direct-
sold Publisher API path. Other FreeWheel connections remain on the uncertified
profile even when they use the same adapter type.

<Warning>
  VAST 3.x/4.x, hosted MP4 ingress, and the managed VAST 4.2 publisher still
  require authenticated certification before Apostra calls FreeWheel. They
  are unverified—not declared unsupported. A seller-specific policy can narrow a
  certified profile but cannot turn an untested value into evidence.
</Warning>

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](/v2/buyer/creatives/rendition-vast-publication),
  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
  `missing:<field>` means the destination requires evidence the creative does
  not carry — `missing:vastVersion` for a tag with no declared version, or a
  hosted file whose metadata could not be inspected.
* `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 Murph'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.
