Skip to main content

Overview

A Creative in v2 is a creative manifest — a structured bundle containing uploaded assets (images, video, HTML, VAST tags), an optional brief, brand info auto-resolved from the campaign’s advertiser, and a target ADCP format. Delivery is campaign-scoped: a manifest reaches a media buy through the campaign it is attached to.
New in v2: the campaign is the default home for a creative. Creatives are created directly under a campaign at /api/v2/buyer/campaigns/:campaignId/creatives/..., rather than being maintained as an advertiser-level catalog that campaigns reference. The model is also fully manifest-aligned with the AdCP creative manifest spec.
Advertiser-level creatives still exist, as a staging and reuse shelf rather than the delivery model. You can create a master under an advertiser before any campaign exists, list an advertiser’s creatives, and promote a creative to the advertiser library — see Advertiser-scoped creative listing and Advertiser library: assign and unassign below. An advertiser-level master carries advertiser_id with no campaign_id and never delivers until it is assigned to a campaign.
Field-name exception: Creative manifests use snake_case field names (creative_id, campaign_id, format_id, format_kind, format_option_ref, template_id, brand_domain, target_format_ids) because they are passed through verbatim to the ADCP wire-format. This is an intentional exception to v2’s general camelCase convention — see the migration guide for context.

Why this changed

V1 treated the advertiser-level catalog as the primary home for a creative, reused across campaigns. V2 makes the campaign the default home so:
  • Each campaign sees exactly the formats its selected products require (creativeFormats.required)
  • Asset uploads happen in a single multipart request with the manifest metadata
  • Format and template auto-detection runs against the campaign’s product mix
  • Tracking macros and brand info are resolved from the campaign + advertiser context
The result is fewer mismatches between selected products and uploaded creatives, and a cleaner per-campaign creative library.

Key fields

Asset types

V2 assets cover the full ADCP type spectrum: URL assets are typed via url_type: CLICKTHROUGH, TRACKER_PIXEL, TRACKER_SCRIPT, or VAST. Webhook assets enable server-side dynamic content rendering (DCO) with HMAC or API-key signing.

Uploaded assets and assembled Creatives

The upload Task and the Creative library hold different things. The Task puts private JPEG, PNG, or MP4 bytes into managed storage. During transport it uses an expiring opaque asset_ref; successful advertiser-scoped finalization requires and returns a durable asset_id for the advertiser’s asset library and retains the original filename as its display name. After finalization, the Task records a conversation receipt with the asset ID when one exists, saved name and tags, content type, byte size, digest, and any verified video facts. For a provider-scoped upload without a reusable Library asset ID, the digest is byte identity for the receipt only: it is not an assetId or sourceUid, cannot be passed to save_creative or provider adoption, and does not imply that later attachment is possible. On external hosts, that Task stays open after the receipt so its private continuation reference remains available for any separately requested follow-up; close the Task when no follow-up is needed. The receipt reports the upload only; creating or attaching a Creative, syncing data, contacting a provider, and delivering media still require a separate request. The transport asset_ref, provider identities, signed upload URLs, storage paths, delivery_url, and source_uid are excluded from the receipt.

Browse an advertiser’s Creative Library

In Apostra, select an advertiser and then choose Manage → Creatives. You can also ask Apostra to show me my creatives after selecting the advertiser. Both paths open the same Creative Library and show the advertiser name and ID at the top. The Library stays within that advertiser if the surrounding workspace changes or you reload the page. The Creative Library opens in a list grouped by Creative format, with Creatives that do not have a format in Uncategorized at the end of the list. Choose the grid toggle to switch layouts for the current browser session. If no loaded Creative has a format, the Library shows one ungrouped list and does not offer format grouping. Select a Creative or Asset to open a previewer beside the list on wide layouts and in a sheet on narrow ones. The list keeps its search, filters, grouping, and scroll position while the previewer shows the available preview, summary facts, and attachment choices. Close it with Escape or Back to creatives to continue browsing. Opening Creatives from a campaign or from a link shared by your agent opens the same Library with that campaign selected. Existing Campaign Creative Assets bookmarks also move to this shared Library instead of opening the retired screen. Apostra restores the advertiser and campaign when you reload a valid link. A campaign or Creative that does not belong to the selected advertiser, or that you cannot access, is refused rather than opened under a different advertiser. Campaign focus lists the Creatives currently attached to that campaign and supports text search. Choose All advertiser Creatives to browse reusable Creatives, open one, and attach it to the selected campaign. Creative requirements opens the campaign’s current format requirements and mapping task. Those requirements come from the signed format declarations on the campaign’s selected Products. Uploading a file still creates an independent Library asset; you can then assemble a Creative from that asset and attach the saved Creative to the campaign. Preview remains tied to the exact saved Creative revision. Use All Creatives for every visible, non-archived Creative owned by the advertiser. Use Promoted shelf for the smaller set marked for reuse. The current view, search text, number loaded, and total remain visible. If more results are available, the Library says the list is incomplete and lets you load the next page. Use the Collections filter to show Assets or Creatives saved in one of the advertiser’s pre-defined collections. The filter matches any selected collection and never crosses advertiser boundaries. An unavailable or foreign collection ID is refused instead of returning an empty Library. The Library loads up to 2,000 active collections for this filter and notes when additional collections are not shown. Opening a Creative loads its exact saved revision through Apostra’s preview origin. If the Creative changes, refresh the Library before opening the new revision. Upload asset starts the existing upload Task; completing that Task adds an asset, not an assembled Creative. Product selection remains required before assembling a new Creative. The Catalogs tab lists the advertiser’s connected catalog feeds beside Creatives and Assets. Each card shows its item count, source, most recent sync, and whether the feed is Healthy or Stale (with the reason). Connect a catalog and Open take you to the existing catalog manager; Build creatives is shown as a forthcoming workflow and does not start work yet. For an object response instead of the visual Library, use V3 search({kind: "creative", filter: {advertiserId}}). Use V3 search({kind: "creative_asset", filter: {advertiserId}}) to find active assets, optionally filtering by text, media kind, or labels. Both Creative and asset reads return labels; filter.labels matches every named dimension and any supplied value within each dimension. Dimension names and their allowed values belong to the buyer account, so a teammate who can use that account sees the same vocabulary. Attached labels and search results remain limited to the advertiser in the request. The Library is shared across the customer: anyone with read access to an advertiser can find, attach, and deliver that advertiser’s assets. Each asset keeps its original uploader for audit and provenance. tags is the legacy asset-column filter; new label writes use the built-in open tags dimension and asset finalisation also keeps that legacy column in sync. Label values are slugs: lower-case ASCII letters and numbers, separated by single hyphens, with a maximum of 64 characters. Promoted MP4 records include verifiedMedia with the verified container, video codec, audio codec, duration, width, and height for format compatibility checks. Promoted audio records include the verified container, audio codec, bitrate, channel count, duration, and sample rate. They do not expose storage locations or delivery URLs. Images remain capped at 30 MB, WAV and MP3 audio at 50 MB, and MP4 video at 2 GB. Audio is available as an independent Library asset and can be used in save_creative with formatKind: "audio_hosted" and its asset ID in sourceAssets. Use V3 search({kind: "creative_format", filter: {advertiserId, productId}}) before assembly to list canonical format options on the selected product. Pass the exact opaque Product ID returned by buyer discovery on Apostra, including its sf2: prefix. Apostra resolves its storefront, inventory source, and seller-local product identity without falling back to a matching raw ID. productId is required to keep the catalogue read bounded. You can narrow the read further with sellerId or formatKind. The result includes a signed opaque id, the exact formatOptionRef, and the seller’s canonical declaration in AdCP’s shape: formatKind and its AdCP params, under the names AdCP defines for that kind. params.slots lists the slots as AdCP canonical slots (asset_group_id, asset_type, required, and min, max, max_chars and max_size_kb when they apply); a slot without min and max takes one asset, and a slot with max above one takes a list. save_creative enforces these limits: it refuses text longer than the slot’s max_chars (or the kind’s matching *_max_chars parameter, such as headline_max_chars, when that is smaller), a file larger than max_size_kb, and a list shorter than min or longer than max. A carousel cards slot with min: 2 and max: 10 accepts three cards and refuses one. When the seller declared no slots, params.slots lists the slots the format kind implies: AdCP’s own default slots for that kind, under AdCP’s slot names. A display image creative fills image_main, hosted audio fills audio_main, a DAAST tag fills daast_tag, a VAST tag fills vast_tag, and an image_carousel fills cards. The names Apostra used before adopting AdCP’s (image, audio and daast) still fill the same slots when the seller declared none, so nothing you send today stops working; what a creative is stored under does not change yet. When the seller declared its own slots, send the seller’s slot names: those slots carry no aliases. For an unpinned carousel, Apostra keeps accepting the card counts it has always accepted (1 to 100), even though AdCP’s default is 2 to 10, until enforcing AdCP’s range is announced. A kind that had no slots to check before, such as native_in_feed, responsive_creative or sponsored_placement, now lists AdCP’s default slots for it, but none of them is required yet, so a manifest that passed before still passes. Only AdCP fields appear inside the declaration: seller rules AdCP has no field for, such as several accepted aspect ratios, extra video file types or a minimum text length, are not shown, but save_creative still enforces them. When a seller declares them, the read also preserves AdCP option fields such as display_name, sample_render_url, tracker and macro declarations, technical-completeness and canonical-only flags, seller preference, locale and channel policy, and experimental. Absent seller declarations stay absent. It also identifies the seller by display name and id. The read does not require a campaign or destination and makes no provider call or setup request. Seller-owned product declarations are available without a provider connection; direct-adapter products keep their existing active-connection visibility rules. Use the returned opaque id with get({kind: "creative_format", id, advertiserId}) to read the same declaration again. The read excludes legacy global format catalogue entries. V3 save_creative turns asset-library records into one format-bound Creative. For campaign-independent assembly, send advertiserId, the selected format id as creativeFormatId, and sourceAssets; each source binding supplies assetId and names its required image or video slot. Add mode: "draft" to save a partial advertiser-owned assembly before every required slot is ready, or, with creativeFormatId, before any slot is filled. Create it with advertiserId, or with campaignId, which resolves the owning advertiser and records the campaign the Creative is meant for. Revise it with the same creativeId, the current stateRevision as expectedRevision, the signed creativeFormatId, and the complete replacement slot state. Use mode: "complete" when the assembly is ready for full format validation. A draft never attaches to a campaign or contacts a provider. A completed assembly joins a campaign only when its draft was created with campaignId, and only when the complete save succeeds. Apostra revalidates the signed format and derives its canonical kind and option on every save. Supply copy and destination slots through social and landing_page_url. A stale revision fails without changing the Creative, and a replacement never silently merges with its prior state. For a campaign-scoped create, send campaignId, formatKind, and the campaign product’s exact formatOptionRef; that legacy save also attaches the new Creative to the campaign. Calls that omit mode retain complete-save behavior. Apostra rejects expired or foreign assets, stale or mismatched format selections, undeclared slots, wrong media types, duplicate slot values, and missing required slots at completion.

Set a creative library role

Buyer Accounts can use save_creative with an existing creativeId, its advertiserId, the current expectedRevision, and libraryRole to classify an existing creative as evergreen or reference. Send idempotencyKey with every libraryRole write; it is required for this shape and optional for other save_creative calls. Omitting libraryRole leaves the creative’s role unchanged, so existing calls behave as before. An evergreen creative remains deliverable. A reference creative remains in the creative library for reuse but cannot be attached to a campaign or media buy; a creative already attached to delivery cannot be changed to reference. Both refusals return VALIDATION_ERROR. Repeating a role write with the same idempotencyKey returns its original result. Reusing that key with a different request returns CONFLICT. libraryRole is Buyer Account-only. Sellers receive no new field or workflow, and reference creatives are never sent to them. The saved result is the Creative manifest. An advertiser-level assembly appears once under All creatives and its authoritative V3 search and readback contain durable asset rows, state, the current numeric stateRevision, and membership.campaignIds. To associate a completed advertiser Creative before a seller package exists, call save_creative with its creativeId, advertiserId, and campaignId only. Repeating that call is safe. The association records campaign membership; it does not assign a package, create provider delivery, or create spend. Those are separate later states. To set the full set of campaigns an advertiser Creative runs on, call save_creative with creativeId, advertiserId, and campaignIds: the list replaces membership, so a campaign missing from it is detached and a new one attached. Omitting campaignIds never detaches. Attaching checks the campaign’s sellers: when the campaign has media buys and no seller’s products accept the Creative’s format, the call fails and names those sellers; a campaign with no media buys yet accepts any complete Creative, while a media buy whose products declare no accepted formats cannot be checked, so the save fails closed. Attaching a Creative to a campaign that has draft media buys also sends it to those buys’ sellers for review. A Creative live on an active or paused media buy cannot be detached until that buy ends. Every way of changing membership, including the Library picker and the singular campaignId association, shares one lock protocol, so concurrent membership writes to the same Creative or campaign do not overlap: a write that finds another one in progress, or whose target set changed while it was being prepared, returns CONFLICT and should be retried with the full set. The same edge is set from the campaign side with save_campaign.creativeIds and from the Creative Library picker; readback is membership.campaignIds on the Creative and creatives on the campaign. To send an advertiser Creative again to the media buys of the campaigns it is already on, call save_creative with its creativeId, advertiserId, and resync: true and nothing else. Resync does not change membership. It re-sends the Creative to every live media buy on those campaigns, including active and paused buys, and waits for each seller to confirm. Media buys that have ended (failed, canceled, rejected, or completed) are left out, because they can no longer take a Creative and would otherwise make every result a failure. If a seller’s storefront approved the Creative but could not deliver it, resync sends it again. A delivery that already succeeded or is still in progress is not sent twice, and a Creative the storefront rejected stays rejected. The result lists each campaign as confirmed, with the live media buys whose seller received the Creative, or failed, with the error and whether retrying can help. A campaign whose media buys have all ended is reported as failed. Use it when a delivery failed before the cause was fixed. Draft media buys are not resynced this way: a Creative reaches a draft buy’s seller automatically when it is saved or attached. The call returns an error when no campaign received the Creative, and it is refused before any seller is contacted when the Creative is a draft, has no click-through destination where one is required, or is on no campaign. The existing opaque revision remains the buyer-preview projection digest. The same completion view can load that exact saved state through Show preview. Creative collections contain assembled Creatives, not the independent uploaded assets. V2 MCP sessions can open the same upload Task, but asset search/get and assetId assembly are V3-only.

Preview a saved Creative

In the Creative Library, select Show preview to run the current saved revision in an isolated frame. The frame uses Apostra’s render origin. It does not embed a provider preview or run the Creative in the Library page. V3 clients can request the same read with get({kind: "creative", id, advertiserId, include: ["preview"]}), or use the Creative’s campaign as sourceId. The preview field contains only renderUrl, expiresAt, the opaque preview revision, and the exact current numeric stateRevision. If the saved format has no first-party renderable asset, it contains a reason instead. A VAST creative also carries vastTag, the facts Apostra read from the tag when it was saved (see preview a VAST tag). Existing get calls omit preview unless it is included, but every Creative read now returns the whole-state stateRevision and no longer forwards the legacy V2 provider preview URL or tracker fields. The buyer preview renders direct image, video, and audio source assets, and a VAST tag as described below. HTML, DAAST, and ZIP sources return a not-renderable reason because their nested documents can load provider resources or measurement trackers that are not safe to project into this read.

Preview a VAST tag

A VAST tag previews as the one video file it resolves to. When the preview loads, Apostra follows the tag on its own servers, through wrappers and redirects, with test values in place of the tag’s macros. It gives the frame only the URL of one progressive MP4 or WebM file from the ad the tag returns. The frame never requests the tag, so the preview does not depend on the ad server allowing cross-origin reads, and no impression, tracking, or verification URL in the document is requested. A preview therefore does not register an impression, although your ad server can still log the request for the tag itself. The vastTag field reports what the tag returned when it was saved: the outcome (ad, no_ad, error, or unreadable), its IAB error code if it named one, the VAST version and the version you declared, the wrapper chain with the version and registrable domain of each document, the duration and skip offset, the ad system and Universal Ad ID, the media files, and whether it uses VPAID, SIMID, OMID verification, a mezzanine file, companions, or non-linear ads. It is present only when the tag has been inspected; a tag saved before inspection existed previews without it. When those facts already show there is nothing to play, the read returns a reason and the vastTag facts but no renderUrl: the tag returned no ad or an error, or its ad has no progressive MP4 or WebM file (a VPAID or streaming-only ad). A tag that could not be fetched when it was saved says nothing about the tag, so its preview is still attempted. If the tag no longer resolves to a playable file when the preview loads, the frame says the creative is not supported. A tag can change after it was saved, so the facts describe the moment it was inspected. Preview URLs expire after 15 minutes and stop working sooner if the Creative changes. Request a new preview after expiry or whenever the returned revision or stateRevision differs from the Creative read. Storage URLs, provider preview HTML, and measurement trackers are not returned by the V3 Creative read. A VAST url asset or linked asset also takes vast_version (vastVersion on a V3 save_creative asset) — one of 2.0, 3.0, 4.0, 4.1, 4.2, 4.3. Declare it; a tag that declares none takes the version Apostra read from the tag when it was saved, and a managed ad-server destination checks that version against the ones it holds delivery evidence for. A tag with neither fails creative sync. Saving a tag also inspects what it returns and warns about an empty or error response or a version that differs from the one declared. See video creatives.

HTML5 bundles

Canonical html5 creatives use one ZIP asset in the html5_bundle slot, with an optional backup_image. Buyer chat inspects these packages without running their JavaScript, preserves the ZIP, and carries the declared dimensions into seller matching. Generic multi-creative ZIPs continue to expand into separate creatives. Direct multipart ZIP creation and reclassification are rejected; use the inspected buyer-chat upload path. An imported bundle is not automatically delivery-ready. Media-buy sync requires an explicit landing-page URL, and compatibility fails closed when a seller declares an HTML5 runtime constraint that inspection cannot verify.

Single HTML files

A single HTML file without a ZIP is AdCP display_tag inline markup: the seller serves it byte for byte, and it needs no landing-page URL. Saving one without format_kind, or with format_kind: "display_tag", stores it as display_tag, and Apostra records its AdCP markup_type when the markup’s structure decides it (an <iframe> tag is iframe_javascript, a <script> tag is javascript). AdCP’s html5 format is a ZIP bundle. A file whose own script declares or assigns clickTag (for example var clickTag = "https://…") is an HTML5 banner: saved without format_kind or with format_kind: "html5", or attached in buyer chat, it is repackaged as a bundle with the file as its index.html entry point, beside any file uploaded with it that the markup refers to by name. The bundle is inspected like any other HTML5 ZIP and needs a landing-page URL to deliver; the literal URL the banner’s clickTag declaration assigns is used when you send none. A banner sent with a file its markup does not refer to by name is refused: send the banner and its files as a ZIP instead. Its size comes from the markup’s ad.size, or from a fixed width and height in format_params when the markup declares none; the markup itself is never rewritten. A clickTag parameter in a tag’s URL, a comment or an attribute does not make a file a banner. Saved with format_kind: "display_tag", a banner is stored as a tag. Any other single HTML file saved with format_kind: "html5" is refused.

Lifecycle

1

Discover required formats

GET /api/v2/buyer/campaigns/:campaignId/creatives/templates returns available templates plus the selected products’ required formats. V2-native declarations are returned in campaign_format_options; legacy format references remain in campaign_format_ids for compatibility. Each product’s requirements are also broken out under campaign_products. The legacy named-format registry across agents is available at GET /api/v2/buyer/formats.
2

Create the manifest

POST a multipart request with files + JSON metadata. Apostra auto-classifies assets, processes HTML to rewrite local references to CDN URLs, inserts ADCP macros, and generates a preview. If the campaign already has media buys, a complete new manifest is sent to their sellers automatically, including the seller of a draft buy that has not been submitted, which receives it for creative review; no separate sync_creatives call is required. See Creative approval.
3

Validate coverage

GET /api/v2/buyer/campaigns/:id returns creativeFormats: { required, covered, missing }. Upload more manifests until missing is empty.
4

Propagate to media buys

On campaign execute, each manifest a seller does not already hold in its current version syncs to that seller for the formats it supports. A ready manifest is registered for delivery in the same transaction as its assets, so it cannot disappear between upload and assignment. Sync status is reflected in sync_status on each manifest.
5

Update or replace

PUT the manifest with new files, deletions, or reclassifications. Updates flow back to media buys.

Media-buy compatibility results

When a media buy inherits creatives from its campaign, Apostra checks each creative against the selected products before delivery. An inherited creative that cannot serve those products is omitted and reported in creative_compatibility_warnings with a status of incompatible or unresolved and a reason. Older creative records that do not contain a usable format are reported in legacy_creatives_excluded. The create_media_buys batch receipt returns those arrays for creatives omitted while it stages new media buys. A receipt for an unchanged staged batch is a no-op and does not repeat the earlier omission diagnostics; use the original staging receipt to repair the inherited creatives. Historical static-image labels such as display_300x250, banner_300x250, and display_300x250_nongenerative can serve a product’s same-sized display_300x250_image placement without retyping the saved creative. Apostra sends the product’s advertised format reference and still checks the creative’s asset type and dimensions before delivery. Other legacy-format differences remain incompatible rather than being widened by format family. Apostra preserves an imported legacy format label for traceability, but a saved creative’s canonical format_kind is authoritative for campaign and product matching. New saved creatives must resolve a canonical kind. If an older creative’s assets, media type, dimensions, and import provenance do not identify one format kind deterministically, Apostra quarantines it for explicit upgrade instead of guessing. Seller-specific format references are applied only when the creative is adapted for that seller; they do not change the saved master. If a create-media-buy request explicitly names an incompatible or unresolved creative, the request is rejected instead of creating a buy that silently drops it. Integrations should surface these response fields so a buyer can replace or correct the creative before launch. For creatives your integration creates, use the canonical format_kind field. It never contains an agent URL. Apostra only preserves a URL-bearing format_id when an external third-party system explicitly supplied that legacy reference.

Conversational creative sessions

Creative sessions are the agent-native path for buyers who want to generate, compare, refine, validate, and save creatives without handling manifest details directly.

Composer drafts

For a campaign, the Creative Library can open a Composer Task without Creative Engines. Choose Snap Story, Meta Reels, Display 300×250, or Audio :30, then fill its copy and uploaded-asset slots, name the draft, add its click-through URL, and save it. The Task shows which slots are filled and a live preview, then saves the assembly draft in the campaign’s Creative Session without selecting or calling an engine. Use the host chat to request changes. The Task does not provide a second change bar and reloads the session after chat updates it. Saving a composer draft keeps work in review; it does not create a campaign Creative, attach it to delivery, or make it live. When Creative Engines is not enabled, the Task keeps assembly controls available and labels generative controls unavailable. Ask your Apostra account team to enable it for the Buyer Account; generation also needs a connected engine and applicable provider setup. The intended user flow is:
  1. Idea — the buyer describes the channel, audience, goal, and source assets.
  2. Draft gallery — a creative adapter returns multiple draft variants for human review.
  3. Refine — the buyer gives natural-language feedback like “make the book more prominent” or “less busy.”
  4. Evaluate — draft checks are advisory; final hard/blocking checks stop finalization.
  5. Finalize — the approved leaf is saved as a campaign creative manifest.
When Apostra generates directions in separate calls and the request includes request.idempotency_key, give each direction a distinct short label. Provider keys use the first 40 characters after lowercasing, replacing characters outside a-z and 0-9 with hyphens, and trimming edge hyphens. Labels such as Summer sale and summer-sale therefore collide. Apostra rejects that batch before calling a generator; change the labels before retrying. Existing keys for unambiguous directions remain unchanged. Use POST /api/v2/buyer/campaigns/:campaignId/creative-sessions to start a session. The response is designed for MCPUI and includes the draft variants[], asset_store, renderer_capabilities, rendering_model, staged evaluator badges, and format_renders[] once a final is approved. When refining a variant, send only the request settings you want to change for that refinement. Omitted settings retain the session’s saved values. Nested objects merge; arrays and scalar values replace the supplied field. Explicit null values in config are forwarded to the provider for validation. A string creative_brief replaces the brief used for that refinement. Provider setting overrides do not change the session defaults, and the selected variant remains the parent. If a finalization response is lost, read the session before retrying. Once the session reports finalized, repeating the same selected variant, name, and message returns its saved result without creating another campaign creative. Retries still require current campaign write access. Different inputs return a conflict; use the saved creative_id to read or update the campaign creative. For older finalized sessions that did not retain the original request, retry without name or message overrides. This retry behaviour applies after the finalized session has been saved. It does not guarantee recovery from an interruption between creative creation and session persistence, or from concurrent finalization requests. A session that does not report finalized is not proof that no creative was created.
Creative sessions are review workflows. They do not replace campaign creative manifests. Finalization is the transition point where a selected draft/refinement becomes a normal campaign creative with creative_id, legacy or canonical format identity, assets, preview, and sync status.

Required placement shapes

When a campaign has required placements, a format_renders[] tile is produced per placement at its real size. A placement with an extreme aspect ratio — a wide leaderboard or a tall skyscraper — only renders at its true shape if the generator can compose that ratio natively. Gemini and FAL do; OpenAI generates a fixed set of shapes and crops to fit. If your session’s connected generator would crop such a placement and you have also connected a native-ratio generator (Gemini or FAL) for that advertiser, that one placement is rendered on the native-ratio generator instead — on your own connected key for it, billed to that vendor account, the same as any generation on that provider. Other placements in the session stay on your session generator. When no native-ratio generator is connected, the placement still renders on the session generator with an advisory note that it may crop; connect one to fix the shape.

Asset lifecycle

Creative production uses three related but distinct asset layers: For locked product, package, logo, or book-cover assets:
  • Set locked_asset: true, can_transform: false, role: "product" (or the closest role), and specific preservation_notes.
  • Keep provenance separate from rights. source: "upload" or source: "dam" says where the asset came from; rights.status says whether it is cleared.
  • rights.status: "unknown" is a non-blocking review warning for final approval. restricted or expired blocks finalization.
  • If the source image includes studio background or whitespace, provide render_crop, subject_bounds, or a transparent cutout/mask when available. Cropping is a preservation-safe fallback; a real cutout produces cleaner final compositions.

Asset URLs must be publicly fetchable

Every asset URL on a finalized creative manifest is forwarded verbatim to the seller’s ad server, which fetches it from its own infrastructure with no Apostra credentials. So a manifest asset URL has to be a publicly reachable https:// (or http://) URL — those are the only two schemes accepted when you submit one. Apostra-internal storage locations are not deliverable, even though they resolve for you while you are authoring. That includes gs:// object URIs, scope3-<kind>:// internal handles, and https://storage.googleapis.com/... URLs that address an Apostra-managed private bucket. These are refused wherever they appear. Schemes other than http(s): — tel:, market:// and the like — are refused too, both at upload and whenever the creative is rebuilt from its manifest for delivery. A manifest row that already stores one is not re-refused on a path that forwards the stored payload as-is, so an existing deep link keeps working there. Those refusals name the scheme, so you can tell them apart from a storage refusal. The check runs when the creative is projected for delivery, so it can surface on sync_creatives, on media-buy execution, and on a media-buy update that assigns creatives — not only at upload time. If an asset is not deliverable you get a 422 INVALID_REQUEST naming the refused creative:
The subject is the creative when the refusal comes from syncing or executing an existing creative, and the individual asset (for example hero-2024) when it comes from building the creative in the first place. If your id contains characters we do not echo back — a dot or a space, say — the message says “A creative on this request…” instead of naming it, and field reads creative. To fix it, publish the asset at a public HTTPS URL you control (or a CDN), then update the manifest asset to point at that URL and retry.
The message deliberately does not say which storage location was detected, and on the sync/execute path it names the creative rather than the individual asset slot. If you cannot tell which asset in a multi-asset manifest was refused, contact support — Apostra retains the exact slot and the specific reason in server-side logs and can tell you which one to fix.

Seller-specific versions

The campaign’s selected products determine which seller formats need coverage. Use the creative template/format endpoints before and after finalization:
  1. GET /api/v2/buyer/campaigns/:campaignId/creatives/templates shows required campaign formats and available templates.
  2. Start a creative session against the best matching creative adapter and target format.
  3. Finalize the chosen direction into a creative manifest.
  4. Re-check creativeFormats on GET /api/v2/buyer/campaigns/:campaignId.
  5. If creativeFormats.missing still contains seller formats, generate or adapt seller-specific manifests for those formats.
Seller-specific versions should keep a common creative direction and source-asset lineage, but each saved manifest must match the actual seller format it covers. For example, the same “Wimbledon book hero” direction may need separate square image, portrait story, audio, video, or HTML executions depending on the selected products. format_renders[] is the review surface for publisher/format previews. It helps a buyer inspect what the final creative looks or sounds like in a particular canonical renderer. The actual delivery object remains the saved creative manifest and its synced seller-specific format coverage.

Performance loop

Creative work does not end at finalization. The full performance cycle is:
  1. Create or update campaign goals — use performanceConfig on the campaign when optimizing toward events or metrics.
  2. Launch with format coverage — execute only after required creative formats are covered.
  3. Collect delivery and outcome data — use reporting plus conversion/measurement ingestion (log_event, measurement data sync, or measurement records depending on integration).
  4. Compare creative performance — inspect performance by campaign, media buy, package, and creative where reporting dimensions are available.
  5. Iterate — duplicate or start a new creative session from the winning direction, then refine the creative based on observed performance and seller feedback.
The Anjali/The Shift prototype exercises the creative build, preview, evaluator, and finalization loop. A complete performance cycle also needs live delivery, measurement ingestion, reporting by creative, and an optimization decision that feeds a new creative iteration.

Evaluator strategy

Creative evaluators answer different questions at different moments. Treat them as a selected set of checks, not a single score. Draft evaluator warnings should guide refinement. Final hard/blocking failures prevent finalization. Non-blocking final warnings, such as unknown source-asset rights, should be shown as review notes. Performance evaluators should only run after delivery/reporting data exists; pre-flight checks cannot prove a creative will win. Longer term, evaluator selection should come from the creative session context:
  • source assets and lock/rights metadata
  • modality and renderer type
  • campaign brief and brand guidance
  • selected seller formats and publisher policies
  • available adapter/seller evaluator capabilities
  • postflight reporting and measurement data

Common operations

Create a creative manifest (multipart)

The endpoint accepts a metadata JSON field plus files[] form fields:
Creative creation is a single multipart request: a JSON metadata field plus zero or more files parts (-F "files=@..."). The server accepts up to the configured MAX_FILE_COUNT files per request and pairs each file with the matching assets[].filename in the metadata.
File limits: 50 MB per file, 20 files per request. Allowed MIME types include images (JPEG, PNG, GIF, WebP, SVG, HEIC), video (MP4, WebM, QuickTime), audio (MP3, WAV, OGG), HTML/CSS/JS, fonts, PDF, and XML/markdown.

List manifests

Filters: search, includeFrequencyCaps. Pagination: take, skip.

Get / update / delete

Render an ADCP preview

Calls the configured ADCP creative agent or platform adapter’s preview_creative tool. Provider-backed renders are returned as external links and are never executed inline. Availability and fail-closed behavior vary by platform; see Creative preview support.

Advertiser-scoped creative listing

GET /api/v2/buyer/advertisers/:advertiserId/creatives returns all creatives scoped to the advertiser — both promoted library creatives (evergreen/reference role) and flight-specific creatives attached to any campaign under this advertiser. A creative inherits the campaign’s advertiser_id at creation time, so it appears in this list regardless of campaign attachment. Use ?promoted=true to filter to library creatives only (those explicitly saved with a role). Without this filter, the full set is returned — including creatives uploaded via the campaign-level creative dashboard that have not been promoted. The advertiser creative listing also accepts ?search, ?role, ?source, ?format_kind, ?asset_type, and ?dimensions filters alongside standard ?take/?skip pagination.

Advertiser library: assign and unassign

Creatives can be saved to an advertiser library (promoted to evergreen or reference role) and then attached to campaigns via the advertiser-scoped endpoints:

Creative roles

Use evergreen for a creative that is ready to attach to a campaign or media buy and send to a seller. Use reference only for material that informs creative work; reference material is never deliverable. To make an existing reference creative deliverable, call save to library again with the evergreen role, then retry the campaign or media-buy action. If delivery returns creative_reference_role_not_deliverable, it includes the affected creative IDs and this is the required remedy. A creative already attached to a campaign or media buy cannot be demoted to reference.

Ownership validation

Both assign and unassign enforce advertiser ownership: the creative and the target campaign must belong to the same advertiser. If they belong to different advertisers, the endpoint returns HTTP 422 with error code INVALID_REQUEST.
A creative with no advertiser_id (not yet scoped to an advertiser) can be assigned to any campaign, and the backfill sets its advertiser_id to match the campaign’s.

Collection fan-out

When you assign a collection to a campaign, members whose advertiser_id conflicts with the campaign’s advertiser are silently skipped. The response includes an ownership_excluded count so you can detect this. Check that all expected creatives appear in the campaign after a collection assign, and promote any skipped creatives to the correct advertiser before retrying.

Advertiser creative collections

An advertiser can group its saved creatives into named collections that live with the advertiser rather than with a campaign. A collection is non-executable: it holds creatives, it does not deliver them. Attach a collection from a campaign to make its creatives available there. Changes to a collection carry a revision check so concurrent edits fail loudly instead of overwriting each other. In the Creative Library page, creatives and collections show in a grid or a list, switchable from the toolbar, with the same rows, search, and filters in both. See Manage advertiser creative collections.

Format coverage on the campaign

When you GET /api/v2/buyer/campaigns/:id, the response includes:
missing is the prompt to upload more creatives. Coverage is computed against the formats every selected product needs.

Tracking macros

When the format supports it, Apostra auto-injects:
  • An impression tracker URL as a TRACKER_PIXEL URL asset
  • A click tracker URL that wraps the original clickthrough — the original is preserved in the rurl query parameter
An advertiser can turn either one off in its tracking config (v2 impression_tracker_enabled and click_tracker_enabled, V3 tracking.impressionTrackerEnabled and tracking.clickTrackerEnabled). With the click tracker off, the click-through URL reaches the seller unwrapped, with Apostra’s identity macros filled; see Click-URL parameters. When the hosted tag wrapper is available in an environment, VAST and DAAST tags are sent to sellers as a hosted VAST or DAAST wrapper on our own origin. The wrapper follows the buyer-supplied tag and carries our impression, click, and playback trackers for start, first quartile, midpoint, third quartile, and completion. Those trackers are not also sent as separate assets, so an impression is counted once. Until then, VAST and DAAST tags keep their existing seller payload, including their separate tracker assets, and their macros are rewritten as described under Macros when the tag URL is sent as it is.

Macros in a VAST or DAAST tag URL

The seller’s ad server sees only our wrapper URL, never your tag URL, so it cannot fill the macros inside your URL. Interchange fills them for the seller.
  • What is recognized. Interchange reads the macros in your tag URL in the dialect of the ad server that issued it, for example Campaign Manager 360’s ord=[timestamp], ${GDPR} and ${GDPR_CONSENT_<id>}. The cache-buster, GDPR flag, consent string, GPP string and section ids, and US privacy string are filled; anything else, such as [APIFRAMEWORKS], is left as you wrote it. Tokens in the host part of the URL are never touched.
  • What the seller receives. The wrapper URL carries one parameter for each filled macro in AdCP canonical syntax (adcp_cachebuster={CACHEBUSTER}, adcp_gdpr={GDPR}, adcp_gdpr_consent={GDPR_CONSENT}), and the creative’s VAST asset lists them in macro_declarations as macros the seller resolves. The seller’s sales agent translates them into its ad server’s syntax. We keep no per-ad-server dialects on the buyer side.
  • What your tag receives. Each time a player asks for the wrapper, the values the seller sent are written into your URL where you put the token, in your own dialect. A cache-buster the seller did not fill is replaced with a fresh random number from Interchange. If the GDPR flag is missing or still a literal placeholder, the flag and every consent string go out empty rather than as literal tokens; a flag that arrives without a consent string goes out with an empty consent string. Interchange never invents a consent value.
  • Values are checked. A value that is too long, uses characters other than letters, digits and . _ ~ - (a GDPR flag must be 0 or 1), or is still a macro placeholder is treated as not sent. Values are URL-encoded and only replace your tokens; they cannot change the host or add parameters.
  • Caching. A wrapper whose tag URL has macros is never shared between players (Cache-Control: private, no-store), so each ad request gets its own cache-buster. A wrapper without macros keeps its five-minute public cache.
The preview shows your tag URL filled the same way, as for a seller that sends no values.

Macros when the tag URL is sent as it is

When the hosted wrapper does not carry your tag (it is not available in the environment, or the sync is a seller dry run), the seller receives your tag URL itself. Players and ad servers do not fill Campaign Manager 360’s [timestamp] or the ${GDPR} family in a redirect URL, so Interchange sends the seller a copy of the URL with those tokens written as AdCP macros, which the seller’s sales agent does fill. The creative you saved is not changed: the stored tag URL, the preview and the creative library keep your URL byte for byte. Only the copy sent to the seller differs.
  • Each rewritten token is declared. The creative’s VAST asset lists one macro_declarations entry per rewritten token, saying which occurrence in the URL it is and that the seller resolves it. Each token you wrote that the seller can fill appears once for each place you used it.
  • Consent names your vendor. For ${GDPR_CONSENT_755} the declaration asks the seller to write its own consent token for vendor 755, the vendor id your token carries. Interchange never supplies a vendor id of its own: a consent token with no vendor id, or with an id that is not a positive whole number, stays as you wrote it, as does a GPP string that names a vendor.
  • Everything else stays as you wrote it. A token Interchange cannot map, such as [APIFRAMEWORKS] or [BREAKPOSITION], and any token in the host part of the URL, goes to the seller exactly as written. Interchange records the tokens it left in its sync logs; it does not guess a meaning for them.
  • What a seller does with a macro it has no value for. AdCP lets a seller drop a parameter only when the macro is the whole value of a query parameter and the seller resolves the value itself. Campaign Manager 360 carries its tokens as ;key=value path parameters, and the consent translation is not a value the seller resolves, so for those the declaration says to reject rather than drop: a seller that advertises it can fill the macro and then has no value may reject the creative. The declarations are not marked required, so a seller that does not support a macro at all reports it as unsupported, does not fail the sync, and leaves the AdCP token unfilled.

How our measurement trackers reach a seller with your VAST or DAAST tag

Interchange’s own impression and playback trackers reach the seller one of two ways, depending on whether the hosted VAST wrapper is in use for that delivery.
  • Through the hosted wrapper. When the wrapper is used, it carries the impression tracker and the start, quartile and complete trackers itself, and Interchange sends the seller no separate tracker assets. See the macro and wrapper sections above and below.
  • As standard AdCP assets, when your tag goes to the seller unwrapped. The creative’s assets then carry our trackers beside your tag. The impression is a url asset with url_type: "tracker_pixel" (AdCP never models the Impression URL as a tracker event). The start, first quartile, midpoint, third quartile and complete moments are each a vast_tracker asset with the matching vast_event (start, firstQuartile, midpoint, thirdQuartile, complete); a DAAST tag gets daast_tracker assets with daast_event instead. The seller’s sales agent assembles these into the VAST TrackingEvents block it serves.
Each tracker URL arrives with our own identifiers already filled in (creative, campaign, media buy and package). The serve-time macros left in it, such as {CACHEBUSTER} and {DOMAIN}, are listed in the asset’s macro_declarations as macros the seller resolves; with no value the seller omits that parameter. {AXEM} and {TMPX} are left in only when the seller’s format option declares that it fills them (AdCP macro_resolution_capabilities); otherwise their parameters are removed before the creative is sent. A token outside the AdCP macro list is declared as preserved so nobody rewrites it. To see this before a seller does, read macroPreview on the V3 save_creative result. For each tracker on the saved creative it gives the compiled URL, the macros every seller of that format fills, the macros only some of them fill, the macros Apostra fills, and the macros that stay literal. When the creative is not tied to a seller format, the preview says which macros the seller fills is unknown rather than guessing. A seller’s own list is the macro_resolution_capabilities on the V3 get({ kind: "creative_format" }) read. See See how a saved creative’s trackers expand. Only a creative sent on the canonical wire carries these assets. A creative the seller receives under a legacy { agent_url, id } format reference carries only the assets that format declares, so a VAST tag sent that way reaches the seller without our trackers whenever the hosted wrapper is not in use. Readiness and certification coverage report such a creative as unable to carry our impression tracker rather than assuming the wrapper is available. Impression trackers you configured on the advertiser or campaign go to the seller the same way, beside your tag: each is a url asset with url_type: "tracker_pixel", with Apostra’s identifiers filled in and its serve-time macros declared for the seller to resolve. Two cases carry no tracker of yours: a tag the seller receives as the hosted wrapper, which holds only Apostra’s trackers, and a tag sent under a seller’s legacy format reference, as above. Apostra records each tracker it did not send.

What the hosted VAST wrapper says about your tag

The wrapper is its own VAST document, so it states a version, what to do with a pod, and where a player reports a failure. Each is chosen from what Interchange read when you saved the tag (see the vast_inspection record on the creative).
  • Version. The wrapper uses the newest VAST version any document in your tag’s chain returns, not the version you declared, because a tag can return an older version than the one declared (Campaign Manager 360 returns VAST 2.0 unless the tag asks for another). A tag Interchange could not read keeps the version you declared, and VAST 4.2 when none was declared. The vast_version on the seller’s copy of the creative is the wrapper’s version, since that is the document the seller’s player requests. Audio VAST creatives stay on VAST 4.1 to 4.3, the versions audio accepts, even when the tag returns an older one.
  • Ad pods. IAB VAST says a wrapper with allowMultipleAds="false" accepts only the first stand-alone ad of the response it requests, and an ad that carries a sequence is not stand-alone, so a pod would play nothing. When Interchange found a pod in your tag (more than one ad with a sequence), the wrapper sets allowMultipleAds="true" and is written in VAST 4.0 or later, the first version whose schema carries that attribute. Otherwise it keeps allowMultipleAds="false", so a tag that returns several alternates, or one Interchange could not read, still fills one slot with one ad. VAST has no way for a seller to say whether a slot takes a pod, so the seller’s player decides what to do with the pod it receives.
  • No ad. A VAST 4 wrapper sets fallbackOnNoAd="true": when your tag answers with no ad, the player may fill the slot with another stand-alone ad rather than failing the ad break. It is not set on VAST 2.0 and 3.0 wrappers, whose schemas have no such attribute.
  • Errors. A VAST wrapper carries an <Error> URI on Interchange’s delivery origin. A player that cannot play the ad requests it with the IAB error code filled in (for example 303, no VAST response after a wrapper, or 400, general linear error), and Interchange counts the report by code. A player that does not fill the code, such as a VAST 2.0 player, is counted as reporting no code. This is operational visibility for Interchange; it does not change what is delivered.
A wrapper that was already published for a creative keeps its document until the creative changes or its tag is read differently from before. It then gets a new wrapper, and the seller receives the new wrapper URL on the next sync. Uploaded video and audio files (video_hosted and audio_hosted) carry Apostra’s trackers as pixel_tracker assets in the tracker slots the seller’s format declares: the impression tracker in impression_tracker, and the playback trackers in start_tracker, first_quartile_tracker, midpoint_tracker, third_quartile_tracker, and complete_tracker. AdCP 3.2 has no standard playback event for pixel_tracker, so playback trackers are sent with event: custom and custom_event_name set to start, first_quartile, midpoint, third_quartile, or complete. Your own impression trackers are sent in the same impression_tracker slot, after Apostra’s. If the seller’s slot cannot hold both, your trackers take it and Apostra’s impression tracker is left out. A seller whose format declares none of these slots receives no Apostra impression or playback tracker with the file. The click tracker still reaches the seller in the landing-page URL, as for other formats. Apostra resolves identity macros when the creative is synced for a media buy, including a campaign creative sync, which fills each media buy’s own ids. When you edit a creative that is assigned to a live media buy, the update reaches that seller once per assigned media buy, routed to that buy’s inventory and filled with that buy’s ids, so an edit never replaces a seller’s filled copy with placeholder ids. It fills {MEDIA_BUY_ID}, {CREATIVE_ID}, and {CAMPAIGN_ID} in Apostra tracker URLs and in the click-URL parameters resolved from the advertiser, campaign, and creative layers. In tracker URLs {MEDIA_BUY_ID} is your media buy’s own id (the mb_… id the API returns), the same on every send of that buy. {CAMPAIGN_ID} is the media buy’s own campaign, and an empty value when the media buy has none. {PACKAGE_ID} resolves to the package when the sync places the creative in exactly one package of that media buy, and to an empty value otherwise, because one synced creative can be assigned to multiple packages. In click-URL parameters the media-buy value is the storefront transaction id used for attribution; a parameter whose identity macro has no value for that sync is omitted instead of carrying a different id. How click-URL parameters treat macros the seller fills, such as {DOMAIN}, is described in Click-URL parameters. The same identity macros written directly into your click-through URL are filled in the destination the click tracker carries in rurl, so the shopper reaches your landing page with real ids rather than placeholder text. There, {PACKAGE_ID} can resolve to an empty value for the same reason as in tracker URLs. Macros the seller’s ad server fills, such as {DOMAIN}, {APP_BUNDLE}, and {PAGE_URL}, are filled in your click-through URL when the shopper clicks. Ad servers do not fill macros inside rurl, so Apostra also puts each one your click-through URL uses on the click tracker as its own parameter (for example domain={DOMAIN} or purl={PAGE_URL}), where the seller’s ad server fills it. Apostra’s click redirect then copies that value into your landing page URL. A macro is filled only when the seller provides its value; otherwise the token is removed, so the shopper never sees placeholder text. {PAGE_URL} gives your landing page the full address of the page where the ad ran. Apostra uses that address only to build the redirect and stores only the page’s host with the click, because a full page address can contain personal data. Apostra’s impression tracker also asks the seller’s ad server for the site or app the ad ran on, {DOMAIN} and {APP_BUNDLE}, whether or not the format or the advertiser’s macro list names them. A value reaches Apostra only when the creative arrives at the seller with a slot for the tracker and the seller’s ad server fills the macro; where either is missing, the impression is still counted, with no site or app. Google Ad Manager fills {DOMAIN} from the ad request’s page URL and {APP_BUNDLE} only on video ad requests, as listed in Google Ad Manager sellers. Apostra never asks for the full page address on an impression. Creatives that exist today keep their current tracker until it is regenerated, which happens when a creative is edited, copied, or moved to another campaign. Serve-time environment macros such as {CACHEBUSTER}, {DEVICE_TYPE}, {COUNTRY}, and consent strings remain literal in the outbound asset for the publisher/ad server to fill when the impression serves. Self-contained HTML is the exception: its embedded Apostra tracker fires directly in the browser, so Apostra injects an HTML-safe URL with no unresolved placeholders, so it records no site or app: no ad server fills a macro inside the creative’s hosted HTML. The tracking.supported_macros array on the manifest response lists the macros the format supports.

Tracker templates

The manifest response returns the trackers as templates in tracking.impression_tracker_url_template and tracking.click_tracker_url_template. A template carries identity placeholders, {MEDIA_BUY_ID} and {PACKAGE_ID}, and inside the click tracker’s rurl any {CREATIVE_ID} or {CAMPAIGN_ID} from your click-URL parameters. Only Apostra can fill those ids: a seller’s ad server has no value for them, so a template copied into an ad server by hand records every impression and click with placeholder text instead of ids, and none of them can be attributed. Do not traffic a template. When Apostra syncs the creative to a media buy, it sends the seller trackers with these ids filled for that media buy, as described above. Serve-time macros such as {CACHEBUSTER}, and {AXEM} and {TMPX} on the impression tracker, are correct in a template and in a synced tracker: the ad server fills them when the impression serves. A synced tracker keeps axem and tmpx only for a seller that declares it fills them. tracking.impression_tracker_url and tracking.click_tracker_url are deprecated aliases that return the same templates. They are removed on 9 November 2026; read the _template fields instead.
Only one clickthrough URL per manifest. If multiple URL assets are flagged url_type: CLICKTHROUGH, manifest creation fails.
The injected impression and click trackers fire to ping.apostra.com — Apostra’s own measurement domain in production (/agentic/imp for impressions, /agentic/clk for clicks, and /agentic/start, /agentic/1q, /agentic/mid, /agentic/3q, and /agentic/comp for video and audio playback). Because it is Apostra’s domain rather than a registered third party, Google Ad Manager doesn’t recognize it as a known ad-tech vendor (see below). It powers impression counting, frequency capping, emissions measurement, and conversion attribution.

What the tracker carries

The impression URL carries identifiers and tokens — never raw PII: The click tracker carries the same identifiers except axem and tmpx, and preserves the original clickthrough in the rurl parameter.
axem and tmpx are filled by the TMP Router and are populated only when the publisher has connected TMP — without it they resolve to empty, and the pixel still counts impressions but carries no emissions or frequency-cap signal.
Where the format or advertiser tracking config enables them, the tracker URL includes consent macros as ad-server placeholders — gdpr (whether GDPR applies), gdpr_c (the TCF consent string), and us_p (the US Privacy string). The ad server fills them from the user’s consent state at serve time and forwards them to the endpoint. These are the only consent signals the pixel carries today; there is no GPP (Global Privacy Platform) string.

Clearing the Google Ad Manager “unknown ad technology” warning

When a creative is trafficked through Google Ad Manager, this clearing step is the publisher’s action (a GAM admin), not the buyer’s. GAM scans the creative for ad technology URLs and flags any domain it doesn’t recognize on Google’s Ad Technology Providers list. Because ping.apostra.com is an Apostra measurement domain, it surfaces under Associated ad technology providers as an unknown URL with the note “We found unknown URLs that may affect serving eligibility.” When the network has Check reservation creatives for user consent enabled, an unrecognized domain blocks the creative from serving — including non-personalized requests — in the EEA, the UK, and Switzerland; the Brazil (LGPD) block applies regardless of that setting. The creative serves normally everywhere else. To clear it, the publisher adds ping.apostra.com as a custom ad partner URL in the network’s EU user consent settings (reachable from the Go to settings link on the warning; the page requires specific GAM permissions, as the warning notes). Selecting “No ad technology providers to declare” does not clear a URL GAM has already detected in the creative.

Format catalog

To inspect formats required by the campaign’s selected products:

Propagation to media buys

At campaign execute, the manifest is synced to each sales agent that supports its selected legacy or canonical format identity. Creating a complete manifest after the campaign already has non-draft media buys also propagates it to their active sellers, without a separate sync_creatives call. The same manifest can serve multiple media buys; later updates re-sync. Image and HTML5 creatives need a destination (clickUrl or a click-through/landing-page asset) to deliver. A video VAST creative needs either clickUrl or a non-empty VideoClicks/ClickThrough in its Inline VAST document; a Wrapper with a non-empty VASTAdTagURI delegates that destination to the downstream VAST. Use sync_status to confirm publishers have received the latest version. synced is true only when every current sales-agent link succeeded; agent_count counts successful agents, and last_synced_at comes only from successful links. A seller note appears only with that seller’s rejected or changes-requested verdict for the current Creative revision. A local validation or delivery failure reads as Sync failed, never as seller feedback, and a later submission returns to Reviewing until the seller responds. The Creative Library detail shows the same links as a Seller approval table: Approved appears once the seller approves a delivered version, while Rejected and Changes requested remain only for the seller’s current verdict. When a Creative has a click URL, its collapsed Destination section shows the authored URL, the delivered URL after effective additions, and whether each addition came from the advertiser, campaign, or Creative.

How a VAST URL is read before it syncs

When a VAST tag is saved as a URL, Interchange fetches it from the server, the way a player would, to measure its duration and to look for a click-through destination. Before the request goes out, known macros in the URL are filled with safe test values: [timestamp], %%CACHEBUSTER%% and the other cache-buster forms get a fresh random number, ${GDPR} becomes 0, a consent string (${GDPR_CONSENT_755}) becomes empty, and device, app and player fields the server cannot know (for example [APIFRAMEWORKS] or [OMIDPARTNER]) are sent empty. Interchange follows up to 5 redirects, each over https and each checked against the same private-address rules as the first request, with 5 seconds allowed per request and 10 seconds for the whole chain. The saved URL is never changed. The filled copy is used only for this check. How macros are filled when the tag is delivered to a seller is described under Macros in a VAST or DAAST tag URL. If the server answers with an error status (or a redirect with no destination), a creative with no landing_page_url is held as unverified, because the check cannot show whether a click-through exists; add a landing_page_url or fix the tag. A timeout, a redirect loop, more than 5 redirects, an http:// hop, or a body that is not XML does not block the sync, and does not count as proof of a destination either. A tag read successfully with no click-through still fails with VAST_DESTINATION_REQUIRED.

Creative transport modes

Apostra chooses the creative transport mode per sales agent:
  • Creative library sync — sellers that advertise a creative library through creative.has_creative_library: true receive manifests through sync_creatives, and packages reference them with creative_assignments.
  • Inline creative delivery — sellers that advertise creative.has_creative_library: false receive the creative manifest inline on each package that needs it.
When the seller behind a storefront accepts only legacy { agent_url, id } format references, Apostra delivers a creative on the reference the booked product advertises for the creative’s format_kind. If that product’s references are not formats its named creative agent defines, no creative can match them: the sync fails, and the error names the seller’s product and the undefined format references. Only the seller can fix this, by declaring formats its creative agent defines. For legacy or unknown capabilities (creative.has_creative_library is null or absent), Apostra falls back to the seller’s advertised tools and uses sync_creatives when available. During initial media-buy execution, Apostra reuses creatives that have current-revision evidence in the same seller account’s library. Those creatives remain creative_assignments on retries instead of being re-sent as inline creates. If sync_creatives fails for the remaining new creatives and the seller allows inline creatives, only those unsynced creatives fall back inline during execution; existing library creatives remain assignments. Media-buy updates retain the seller-wide inline fallback when sync fails. If the seller is library-only and a required new creative fails to sync, the create or update request fails synchronously instead of silently continuing.

Campaign

Owning campaign — formats come from its products

Brand

Brand info auto-resolved from the advertiser