Skip to main content
Reward the user’s accomplishment with a brand-funded offer, in your own UI, at the moment your own code already detects. Rewarded actions is the Encore API’s most common integration, so this guide is the group’s one end-to-end walkthrough of the whole flow (overview); the guides cover only what differs per selection mode. It walks the full Reward Users use case with a streak example: a news app whose reader just finished her 7th consecutive day. The app shows its own “you’re all caught up: 7-day streak” screen, and inside that screen renders one brand offer card with its own button. Encore picks the offer; everything the user sees is yours. Use this pattern when your code owns the moment (a streak, a milestone, a completed purchase) and you render the surface. A good placement label for milestone and streak moments is milestone_reached (purchase moments: post_purchase). Use it as the name of this surface in your own code and analytics. The feed request itself carries no placement field; the request identifies your integration (clientId) and the end user (userId).

The complete pattern

1

Your milestone logic fires

Nothing Encore-specific happens here. The trigger is code you were writing anyway.
2

Fetch the feed

When the streak screen is about to show, ask Encore for ranked offers for this user. One call, from your server or your app:
userId must be your stable identifier for this end user: it seeds offer ranking and becomes the user on the transaction created when they tap (see Gotchas). attributes.countryCode and attributes.language are required; attributes.platform is optional for API-platform apps. The full field contract, including X-Platform for multi-app projects, is in the feed reference.The response is a ranked array (full response contract: feed reference); for a single reward card you render offers[0]:
An empty offers array means no eligible offer for this user right now: show your streak screen without the reward card and move on (never block your own moment on it).
3

Render the offer in your UI

Map the response fields onto your own card, inside your streak screen:Your button, your copy, your layout: Encore ships no UI here. If you render more than one offer, cycle through the array at your own pace and repeat steps 4 and 5 per offer.
4

Confirm the impression when the card renders

The feed call is not the impression. The moment the offer card actually appears on screen, confirm it with that offer’s impressionUid:
deliveredTimestamp is required: Unix seconds at render time. The server responds 202 { "success": true } (full contract).The discipline is render-then-confirm, never confirm without render:
  • Fire one delivered call per offer, at the moment it becomes visible: not when you fetch the feed, and never for offers the user didn’t see.
  • If a call clearly failed (network error, non-2xx), retry it. The server accepts repeated confirmations but does not guarantee deduplication; treat one successful delivered per rendered offer as the contract.
  • These confirmations are what record an offer as actually seen. Skip them and your rendered offers are indistinguishable from ones the user never saw.
5

The user taps: route to clickUrl

Wire your button to open the offer’s clickUrl (in-app browser, Custom Tab, SFSafariViewController, or a new web tab). That link is the claim: Encore records an attributed transaction, bound to your app, the offer, the creative, and the userId from step 2, and 302-redirects the user to the advertiser.Because clickUrl is a plain branded short link, you can also let the user share the offer with a friend: drop it into a share sheet, and whoever opens it gets the same attributed redirect.A claim is not a conversion. The user tapping through earns nothing yet; the activation is verified server-side on the brand’s side, and only a verified completion counts (and pays). If your app should react when that happens, subscribe to the offer_completed webhook.

Gotchas

  • Keep userId stable. Ranking, click attribution, and completion verification all key on it. A per-session or rotating id fragments the user across transactions and breaks attribution.
  • Feed, not catalog. Per-offer impressionUids and delivered confirmation exist only on /offers/feed; rewarded-action moments always use the feed, so their impressions are fully accounted for.
  • Don’t cache offers across sessions. Each clickUrl is a short-lived tokenized link that expires server-side, and ranking is per-request. Fetch a fresh feed each time the moment fires, and only once per rendered moment, since feed retries aren’t deduplicated (each call creates new links and analytics rows).
  • Don’t build your own eligibility layer. Entitlement, geo, and ranking re-run server-side on every feed call; an empty offers array is the server telling you there’s nothing to show right now. Render your moment without the card and try again at the next moment.
  • Geo is required. attributes.countryCode must be two uppercase letters (ISO 3166-1 alpha-2) or the request is a 400. Offers not redeemable in that country are never returned.

Verifying your integration

What you can observe end-to-end today:
  1. Feed responses. A 200 with a non-empty offers array (with a pk_live_* key) means auth, eligibility, and targeting are wired. An empty array is a valid response, not an error: retest with a user/geo you know has eligible offers.
  2. Delivered confirmations. Each render-time confirmation returns 202 { "success": true }. A 400 means a malformed impressionUid (it must be the UUID from the feed response) or a missing deliveredTimestamp.
  3. Attributed transactions. Tapping clickUrl should land the user on the advertiser page via the 302. If you’ve configured the offer_completed webhook, a verified completion later POSTs to your endpoint with the same user binding.
pk_test_* keys accept delivered calls and clicks but don’t track them; use pk_live_* for anything you want to count.