Skip to main content

Player manifest reference

The manifest is the contract between the design plugin and the player. It declares what to render, how to route between variants for a given viewer, and what happens when a viewer interacts.

It is produced by the After Effects plugin at publish time — you do not normally write one by hand — but understanding it explains what a template can and cannot do.

{
"version": "1.0.0",
"lottie": { "defaultUrl": "https://…/animation.json" }
}

version and lottie are the only required fields.

Content​

FieldTypePurpose
lottieobjectThe animation. defaultUrl plus optional variants
videoobjectBackground video. defaultUrl, optional variants, and mute
audioarrayAudio tracks. Each has id, defaultUrl, optional variants, and primary
posterobjectStill shown before playback. url plus optional variants
fontsarrayVectorized glyph artifacts, merged into the Lottie before render

fonts​

Each entry is a .font.json artifact the player fetches and merges into the animation before rendering. This is why personalized text renders in the correct typeface with no runtime webfont dependency — no @font-face, no FOUT, and no reliance on the recipient's machine having the font.

KeyPurpose
familyFont family name as referenced by the Lottie
fNameFont name used inside the animation
urlWhere to fetch the glyph artifact
styleOptional style qualifier
ascentOptional ascent override

Personalization​

FieldTypePurpose
dataSchemaobjectDeclares the data points the template expects
computedarrayValues derived from the supplied data before rendering

dataSchema​

Each key describes one data point:

KeyPurpose
typestring, number, or boolean
requiredWhether the viewer data must supply it
label, descriptionHuman-facing text for the backoffice
fallbackValue used when the data does not supply one

fallback is what makes a template resilient: a missing data point renders the fallback rather than failing.

computed​

Each entry is { key, expr, outputType? }. The expression is evaluated against the viewer's data before rendering, and key is written back into the data — so later computed fields can build on earlier ones.

Available operations include arithmetic and rounding (+, round, floor, ceil), logic (if, and, or, in), string concatenation (cat), and formatting helpers:

OperationPurpose
formatCLPChilean peso currency formatting
formatNumberNumber formatting
formatPercentPercentage formatting
formatDateDate formatting (YYYY, MMM, LL, short, long, numeric…)

This is what lets a template show "you saved $1.234.567 this year" from a raw number, without the client's system having to pre-format anything.

Routing between variants​

lottie, video, audio, poster, textTracks and transcript all accept variants, and they all use the same rule shape:

{
"variants": {
"rules": [
{ "dataKey": "segment", "equals": "premium", "src": "https://…/premium.json" }
]
}
}

A rule matches when the named dataKey in the viewer's data equals the given value. The matched rule supplies the asset — overlayUrl, videoUrl, audioUrl, posterUrl, src, or inline text for transcripts. A viewer matching no rule gets the default.

Comparison is strict, so a rule should key off a boolean or numeric computed field rather than a raw data point: host data often arrives as strings, and "true" does not equal true. The publisher emits a computed for exactly this reason.

The poster follows its own routing, independent of the video's. A template that routes its video by a data point normally wants the still to follow the same branch — otherwise the image advertises a version of the video the viewer will not be shown — but the two are declared separately, so they can diverge deliberately.

This is how one template serves many audiences: the routing happens per viewer, at view time, without rendering a separate video per segment.

Accessibility​

FieldTypePurpose
textTracksarrayTimed captions painted over the video
transcriptobjectFull text alternative for the whole video

These are not the same thing, and the distinction matters for compliance.

textTracks are timed captions — each has id, label, srcLang, src, optional variants and default. They serve a viewer who can see the video but not hear it.

transcript is the WCAG 1.2.1 / 1.2.8 text alternative: the full text of the video, useful to a screen reader, which timed captions are not. It carries {dataKey} tokens that are interpolated with the viewer's resolved data — a transcript has to describe what this viewer saw — and it supports the same variants routing, so a viewer routed to another composition reads that composition's text.

The text is inline rather than a separate file. On a 40-variant template the transcripts measured 53 KB raw but 1.7 KB gzipped, because they are ~91% identical — external files would have bought nothing.

Interactions​

interactions maps a CTA id to what happens when it is clicked. Keys are matched against the emitted CTA id, case-insensitively.

KeyPurpose
navigateOpens the URL held in the data point named by linkDataKey, with optional target
setDataMerges values into the live data, re-resolves computed fields and variant rules, and re-renders in place
nameHuman label, reported as cta_name on the cta.clicked event. Falls back to the CTA id
questionGroups several CTAs into one survey question

setData​

setData re-renders in place — no navigation, no reload. Values may be literal scalars or JSON-logic expressions evaluated against live data at click time, which is what enables flows like a bounded quantity stepper.

The player evaluates each value defensively and keeps the prior data when a value is not evaluable. A malformed value is never rejected at manifest load, because doing so would fail the entire manifest instead of no-op'ing a single key.

When both are present​

Both run, in a defined order: the data patch is applied first — so a link the patch itself sets is the one that opens — then the link opens synchronously inside the click, and the re-render is queued behind it. The click autopauses, as any navigation does.

question​

Survey answers are authored as independent CTAs, so without question a click is just a click. Give two CTAs the same question and the player additionally emits interaction.engaged, with the question as interaction_id / interaction_name and the CTA's label as interaction_value — which is what lets analytics report a distribution ("62% answered Yes") rather than two unrelated counts.

It is analytics-only and does not affect playback.