Skip to main content
What show() resolves to. Either nothing ever appeared, or the interaction ran and the record carries two independent funnels plus how the sheet went away. show() never rejects. Presentation-level failures arrive here as values.

Structure

The two funnels are independent axes, not competing cases: a single presentation can claim an offer and run a purchase, and 2.0 records both. not_attempted is a real value, meaning “the funnel was open and nothing entered it”, never a missing one.
There is no unlocked boolean, on purpose. What a claim means is a property of the variant flow that served it, so an SDK-computed verdict over app-global config could contradict the flow that actually ran. The record is facts; the policy is yours.

Reading it

When the mechanism matters, branch on the axes directly:

PublisherOutcome

Your funnel: what your purchase controller reported back.
Treat pending as neither a conversion nor an abandonment. It is Ask to Buy (parental approval) on iOS, or SCA or a pending Play transaction on Android: the user has not been charged and has not walked away, and the store’s webhook is the source of truth. Folding it into cancelled in your own reporting reproduces exactly the misreporting the 1.x boolean handler caused.

AdvertiserOutcome

Encore’s funnel: how far the offer claim got.
result.claim is shorthand for the offer behind a claimed or verified outcome, and null otherwise. Read result.advertiser directly when you need to tell the two apart.

ClaimedOffer

On iOS an offer is a campaign, so offerId equals campaignId there. transactionId joins this record to a later strict_unlock_verified outcome and to the server-side completion event that can land days afterwards; it is nullable, because the transaction write can fail while the claim still happened.

DismissReason

How a presented sheet went away. These strings are the frozen cross-platform vocabulary, and the same values land in decline_reason on sdk_offer_sheet_dismissed.

NotPresentedReason

Why no sheet ever appeared.
use_case_unavailable is a correct no-op, not an error. It never falls back to the churn-intervention sheet, because putting an in-app-purchase screen in front of a user who was meant to be rewarded is worse than presenting nothing.