Fahad Anwar Muneer Contributor, 5centsCDN | Video Live Streaming | CDN | Restream

Who Fills In Your Ad Macros? SSAI vs Player vs Ad Server

Here is a confusion that costs streaming publishers real time and real ad revenue, and almost everyone hits it at least once. An ad isn’t targeting correctly, or a stream of ad requests is going out with device information missing, and the finger-pointing begins. The publisher assumes their ad-insertion provider generates those values. The ad provider correctly says they don’t fill them. And somewhere in the middle, the actual answer — that some of those values were supposed to come from the publisher’s own app and be passed through ad macros all along — goes unnoticed while everyone blames the wrong layer.

This happens because video ad delivery involves at least three distinct parties, each responsible for a different job, and the boundaries between them are genuinely easy to misread. The core mechanism at the center of the confusion is the ad macro: a placeholder in an ad request URL that gets swapped for a real value at request time. Understanding which party supplies each value, which party substitutes it into the macro, and which party simply receives it is the difference between smooth targeting and a frustrating multi-day support thread that ends with everyone realizing the fix was upstream the whole time. This guide explains how ad macros work, walks through who is responsible for what, and uses a common real-world mix-up to make the boundaries concrete.

An ad tag with [DEVICEIP] and [DEVICEUA] macros substituted with real values at request time
How a macro becomes a real value

What an Ad Macro Actually Is

Start with the mechanism, because the responsibility question only makes sense once the mechanism is clear.

When a video player or streaming platform requests an ad, it calls an ad tag — a URL that returns a VAST response describing the ad. But a single static ad tag has to work for millions of different viewers, on different devices, in different places, under different privacy rules. It cannot hard-code the specifics of any one viewer. Instead, the tag is written with placeholders — macros — that stand in for values that are only known at the exact moment a real viewer requests a real ad.

A macro looks like a token wrapped in square brackets sitting inside the URL, such as [DEVICEIP], [DEVICEUA], or [IFA] (the identifier for advertising). These tokens are standardized so that any compliant party can recognize them. When the ad is requested, whichever party is making that request is expected to replace the entire placeholder, brackets included, with the actual value for that viewer — the real IP address, the real user-agent string, the real advertising ID. A tag containing ?ip=[DEVICEIP]&ua=[DEVICEUA] becomes ?ip=8.8.8.8&ua=Mozilla%2F5.0… before it reaches the ad server. That swap is macro substitution, and it is how one static tag turns into a live, per-viewer request carrying the context an ad server needs to choose and measure an ad.

The critical thing to notice, and the seed of the whole confusion, is that substitution is only half the story. A macro can only be filled with a value that someone actually knows and provides. If the real value never reaches the party doing the substitution, there is nothing to substitute, and the macro either goes out empty or, worse, goes out as a raw unreplaced placeholder. So the real question is never just “who fills the macro” — it is “who *knows* the value, and did they pass it to the party that fills the macro?”

The Three Parties and Their Three Different Jobs

Video ad delivery in a server-side world involves three roles, and each has a job the others cannot do for it. Keeping them straight is the entire point.

Client app supplies values, SSAI layer substitutes macros, ad server receives and responds
The three parties and their jobs

The client app: supplies the values only it knows

The application the viewer is actually using — a Roku channel, a mobile app, a smart-TV app, a web player — is the only party that knows certain things about the viewing environment. It knows the device’s advertising identifier, because that ID is provided by the device platform to the app. It knows the device make and model, the app bundle or store ID, the user’s consent and privacy choices, and details about the content the viewer selected. These are environment facts that live on the client and nowhere else. For them to be used in an ad request, the app has to read them and pass them along — typically by appending them to the playback or ad-session URL it uses to start the stream. If the app doesn’t collect and forward a value, no downstream party can conjure it, because no downstream party has access to it.

The SSAI layer: substitutes the values into the ad request

The server-side ad insertion platform sits between the app and the ad server. When an ad break arrives, the SSAI layer is the party that performs the actual VAST request to the ad server on the viewer’s behalf, and therefore it is the party that performs the macro substitution. It takes the values it has — the ones the app forwarded to it, plus a few it can observe itself, like the requesting IP in some configurations — and it writes them into the macros in the ad tag before calling the ad server.

This is a substitution role, not a generation role. The SSAI layer fills [DEVICEMAKE] with the make the app told it about; it does not know, and cannot invent, a make that the app never sent. This distinction is precisely where publishers most often go wrong: they assume the SSAI platform is producing the device data, when in fact it is relaying and formatting device data that originated in the app.

The ad provider or SSP: receives the request and returns an ad

The ad server, supply-side platform, or demand partner is the party the request is ultimately sent to. It receives the fully-substituted ad request, reads the values it was given, uses them to decide which ad to serve, and returns the VAST response. Crucially, in the standard model this party does *not* fill in the standardized device and environment macros — those are expected to arrive already populated by the party that made the request. The ad provider consumes those values; it does not source them. This is why an ad provider can quite correctly say “we don’t fill those values” — because under the industry’s own guidance, filling standardized VAST player macros is the job of whoever performs the VAST request, which is the player or the SSAI server, not the ad server receiving it.

There is a genuine subtlety worth naming here, because it is a second source of confusion. Ad servers do have their *own* proprietary macros that they populate internally — but those are a different category from the standardized VAST player macros that carry device and environment data. When someone says “the ad server fills macros,” they may be thinking of those proprietary ad-server macros, which is true but unrelated to the device-level values that have to come from the client. Two different macro systems, two different owners, one very easy conflation.

A Concrete Example: The Roku Parameters Mix-Up

Abstract roles are easy to nod along to and hard to apply, so here is the kind of real situation where they matter, told in general terms.

A publisher running a Roku channel notices that certain values in their ad requests — the advertising identifier, the device make, the device model — look like query parameters being generated somewhere in the delivery chain. Seeing those parameters appear alongside the ad request, the publisher reasonably assumes the SSAI provider is generating them, and when targeting looks off, they raise it with the ad provider. The ad provider checks and responds, correctly, that they do not fill those values — they have no macro-substitution role for device data; they simply receive whatever arrives and respond with an ad.

Both of those observations are true, and yet the problem persists, because the actual source of the values was never the SSAI layer *or* the ad provider. On Roku, as on other connected-TV platforms, the advertising identifier, device make, and device model are values the *app* is responsible for reading from the platform and appending to the playback URL it hands to the SSAI layer. The SSAI layer then substitutes those app-provided values into the ad-request macros. If the app is the thing populating them — which it is — then a gap or a mismatch in those parameters is an app-side configuration question, not something the SSAI provider generates from nothing or the ad provider fills in. The values were the client’s to supply all along; the SSAI layer’s role was only to carry them into the macros, and the ad provider’s role was only to read them once they arrived.

The resolution, in this pattern, is not to escalate further with the ad provider, who has correctly said the values aren’t theirs to fill, nor to ask the SSAI layer to generate data it has no access to. It is to confirm that the app is correctly reading the device advertising ID, make, and model from the platform and appending them to the session it opens — because that is the one link in the chain where those particular values actually originate.

Decision flow: is the value a device fact, a server-observable, or an ad decision
Tracing a missing value to its owner

How to Tell Which Party a Value Belongs To

The Roku example generalizes into a simple diagnostic you can apply to any missing or wrong value in an ad request. The question to ask is: where does this value physically originate?

If the value is a fact about the viewer’s device or environment — the advertising ID, device make and model, app identifier, consent and privacy signals, the specific content selected — it originates on the client, and the app must collect and forward it. No server-side party can create it. If the value is something observable at the moment the server makes the request — such as the requesting IP address or a server-side user-agent in certain setups — the SSAI layer can supply it directly. And if the value is a decision about *which* ad to serve, that belongs to the ad server or demand partner, which produces it in the VAST response. Trace the value to its true origin, and the responsible party becomes obvious; skip that step, and you end up asking a party to provide something they never had.

A useful habit is to inspect the actual ad-request URL when something looks wrong. If you see a raw, unreplaced macro like a literal [IFA] or [DEVICEMAKE] still sitting in brackets in the outgoing request, that tells you the substitution step didn’t happen or the value wasn’t available to substitute — which points you at whether the app forwarded the value and whether the substituting layer received it. If you see the macro replaced but with an empty or clearly wrong value, the substitution happened but the source value was missing or bad, which again points upstream to whoever was supposed to supply it. Reading the request itself turns a finger-pointing exercise into a factual one.

Why This Model Exists at All

It is fair to ask why the responsibility is split this way instead of having one party handle everything. The answer is that the split follows access and trust, and once you see that, the design stops looking arbitrary.

Device and environment facts are split off to the client because the client is the only party with legitimate access to them — the app is where the viewer’s device and consent choices actually live, and platform rules increasingly require that this data be sourced there rather than inferred elsewhere. Substitution is handed to whoever performs the request because that party is the one assembling the actual HTTP call, and it would be redundant and error-prone to have anyone else try to rewrite the URL.

And ad selection stays with the ad server because that is the party with the campaigns, the pricing, and the targeting logic. The industry’s shared guidance for server-side ad insertion macros, published by the IAB Tech Lab, formalizes exactly this division, precisely so that different platforms and vendors can interoperate without renegotiating who does what every time — a party performing the VAST request fills the standardized player macros, using values the client environment provides, and the ad server consumes them. The standardization is what lets a publisher combine an app on one platform, an SSAI layer from another vendor, and an ad provider from a third, and still have the whole chain work.

Getting It Right the First Time

The single most useful mental model here is to stop thinking of ad values as something a single black box “generates” and start thinking of each value as having a specific origin and a specific journey. Device and environment values start life in the client app and must be forwarded by it. The SSAI layer substitutes those forwarded values into the ad request and adds what it can legitimately observe. The ad server receives the assembled request and returns an ad. When a value is missing or wrong, the fix lives at the point where that specific value was supposed to originate — and for device-level data on connected-TV platforms, that point is almost always the app.

Internalizing this saves the multi-day support threads, the misdirected escalations, and the quiet targeting and revenue losses that come from asking the wrong layer to do a job it was never responsible for. The ad provider isn’t withholding values it should be filling; the SSAI layer isn’t failing to generate data it never had; the app simply needs to send what only it can know.

5centsCDN operates the SSAI and delivery layer of this chain — the part that reads your ad break, performs the VAST request, substitutes the values your app provides into the ad macros, and stitches the returned ad into your stream. If you are setting up ad insertion and want the macro flow configured correctly from the start, or you are debugging why certain values aren’t reaching your ad server, our ad insertion and ad manager tooling handles the substitution and routing side, and you can validate what your tags actually carry with our VAST validator. For the wider picture of who decides which ad wins, our guide to DSP vs SSP vs ad exchange maps the demand side. When you want a second set of eyes on where a value is going missing in your chain, talk to our team.

Frequently Asked Questions

Does the SSAI provider generate the device advertising ID and device make/model?

No. The SSAI layer substitutes those values into ad macros, but it can only use values the client app supplies. The advertising ID, make, and model originate in the app and must be appended to the playback/session URL.

Who fills in VAST macros?

Per IAB Tech Lab guidance, standardized VAST player macros are filled by the party that performs the VAST request — the player or the SSAI server on its behalf — not the ad server receiving the request.

Why does the ad provider say they don’t fill these values?

Because they’re correct: standardized device/environment macros are filled by whoever makes the VAST request (player/SSAI), not the ad server. The ad server consumes those values to pick an ad; it doesn’t source them.

Where do Roku device values come from in an ad request?

On Roku and other CTV platforms, the app reads the advertising identifier, device make, and model from the platform and appends them to the session URL. The SSAI layer then substitutes them into the ad macros.

How do I debug a missing value in an ad request?

Inspect the outgoing ad-request URL. A raw unreplaced [MACRO] means substitution didn’t happen or the value was unavailable; an empty/wrong value means the source value was missing. Trace the value to where it originates — usually the app for device data.