display placement option, or remotely in
the Encore portal) feed every composed sentence:
| Value | Local source (display.*) | Remote source (/config) |
|---|---|---|
| App name | display.appDisplayName | The app name set in the portal |
| Trial length | display.trialPeriod (e.g. "1 month") | Composed from the app’s native entitlement value + unit |
| Logo | display.logoUrl | The app icon set in the portal |
The resolution chain
Each value resolves independently, first match wins:- Explicit
header.*, for the header slots only: aheader.title/header.subtitleyou pass always wins over any composed default. - Local
display.*: thedisplayplacement option, a per-placement override of the remote values. - Remote
/config: the app profile the SDK fetches atconfigure()time; the app name, the native entitlement value + unit (composed into a trial period), and the app icon URL. - The row’s terminal: a composed default where one exists; otherwise the field is required and resolution fails (see below).
Trial-period composition
Whendisplay.trialPeriod is not passed, the trial period composes from the remote
entitlement value + unit, with singular units: value 1 + unit months renders
“1 month”; value 3 renders “3 months”. The composed string is display-only and
never parsed back into a number.
What the resolved values render
Every SDK-composed sentence is built from the resolved values; screens never invent copy. WithappDisplayName: "FitTracker" and trialPeriod: "1 month":
| Surface | Slot | Composed default |
|---|---|---|
Offer sheet ('list' · deferred) | header.title | ”Get FitTracker free for 1 month” |
Offer sheet ('list' · deferred) | header.subtitle | ”Claim a partner offer to start” |
| Primer screen | Headline | ”Amazing! Your 1 month free trial of FitTracker is ready” |
| Primer screen | CTA | ”Activate FitTracker trial” |
| Redemption screen | Host row detail | ”Free until August 8, 2026”, derived from the remote numeric trial length and the redemption moment; never settable |
Payment-status header ('thankYou') | header.title | ”Payment Successful” |
header.title / header.subtitle replaces the composed default for that
slot. On 'list' placements in immediate mode, no copy is composed; your header.*
values (or the generic remote copy) apply unchanged.
Which fields are required, and when
appDisplayName, trialPeriod, and logoUrl are required only for 'list'
placements in deferred mode, because requiredness follows the UI that consumes the
value. The composed headline and the primer screen are 'list' UI, so only that context
needs the values; a 'thankYou' + deferred show() renders none of them, and immediate
mode composes nothing.
“Required” means required to resolve, not required to pass: most integrations set the
app name, entitlement, and icon once in the Encore portal and never touch the display
option, so the remote chain satisfies the contract.
When resolution fails
The check runs as a pre-flight inside a deferredshow(),
before any analytics, exposure, or UI, so a broken integration has zero side effects.
show() waits (bounded, ~2.5 s) for the shared remote-config fetch when local values
alone don’t satisfy the contract, then resolves one of two failure classes as a
dismissed result (never a throw):
| Error code | Meaning | What to do |
|---|---|---|
CONFIGURATION_ERROR | The remote config loaded, but a required field is genuinely absent everywhere. The message names each missing field with its two fixes, e.g. appDisplayName (pass display.appDisplayName, or set the app name in the Encore portal). | Fix the integration: set the value in the portal or pass it in display.*. |
NETWORK_ERROR | The remote config was unreachable, so requiredness can’t be judged. Transient. | The next show() call retries the fetch. |
{ status: 'dismissed', reason: { type: 'error', error } }; see
Error Handling.
Resolution is per call, from live sources
Resolution happens fresh at everyshow(), and again at redeem() from the
then-current sources. Nothing display-related is snapshotted onto the pending
transaction: if the portal values change between claim and redemption, the redemption
screen renders the new values. At redeem() the local link of the chain is the
host option (name, logo);
unresolvable values there degrade the screen rather than erroring.
Related
- Redemption Modes: the deferred flow these values power, including the primer screen.
placement(): thedisplayandheaderoptions.redeem(): thehostoverride and the redemption-screen ledger.- Error Handling: the error-as-value contract.