Skip to main content
The deferred flow renders SDK-composed copy about your product: the offer sheet headline, the primer screen, and the redemption screen all say things like “Get FitTracker free for 1 month”. This page is the single authority for where those values come from, which are required, and what happens when they don’t resolve. Three host display values (set locally via the display placement option, or remotely in the Encore portal) feed every composed sentence:
ValueLocal source (display.*)Remote source (/config)
App namedisplay.appDisplayNameThe app name set in the portal
Trial lengthdisplay.trialPeriod (e.g. "1 month")Composed from the app’s native entitlement value + unit
Logodisplay.logoUrlThe app icon set in the portal

The resolution chain

Each value resolves independently, first match wins:
  1. Explicit header.*, for the header slots only: a header.title / header.subtitle you pass always wins over any composed default.
  2. Local display.*: the display placement option, a per-placement override of the remote values.
  3. Remote /config: the app profile the SDK fetches at configure() time; the app name, the native entitlement value + unit (composed into a trial period), and the app icon URL.
  4. The row’s terminal: a composed default where one exists; otherwise the field is required and resolution fails (see below).
Empty and whitespace-only strings count as absent at every link.

Trial-period composition

When display.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. With appDisplayName: "FitTracker" and trialPeriod: "1 month":
SurfaceSlotComposed 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 screenHeadline”Amazing! Your 1 month free trial of FitTracker is ready”
Primer screenCTA”Activate FitTracker trial”
Redemption screenHost 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”
An explicit 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 deferred show(), 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 codeMeaningWhat to do
CONFIGURATION_ERRORThe 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_ERRORThe remote config was unreachable, so requiredness can’t be judged. Transient.The next show() call retries the fetch.
Both surface as { status: 'dismissed', reason: { type: 'error', error } }; see Error Handling.

Resolution is per call, from live sources

Resolution happens fresh at every show(), 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.