The flow
Every integration is the same five-stage flow; only the selection mode varies.1
Select offers
One
POST in your chosen selection mode: ranked feed, full catalog, single message pick, or a permanent creator link.2
Deliver on your surface
Render the offer in your UI, message, or page. On the feed, confirm each offer’s impression with
POST /offers/impressions/{impressionUid}/delivered when it is actually shown.3
The user claims
Route the tap to the offer’s tokenized
clickUrl. Encore records an attributed transaction (bound to your app, the offer, the creative, and your userId) and 302-redirects to the advertiser. See The claim model.4
Activation is verified
The completion is verified server-side on the advertiser’s side. Only a verified completion counts, and pays.
5
React to the completion
Encore POSTs a signed
offer_completed event to your server: see Receive Completion Events.Choosing a selection mode
Rule of thumb: if the offer appears at a moment, use the feed. Per-offer exposure measurement (the served/delivered split below) exists only on the feed, so feed-based integrations are the ones whose impressions Encore can fully account for.
Authentication
Every endpoint authenticates with the publishable key from your dashboard, passed in theX-API-Key header: pk_live_* for production, pk_test_* for development.
X-Platform header so Encore knows which app to resolve; otherwise the request is rejected with a 401 carrying X-Platform header required for multi-app projects (the key is valid; the header is what’s missing):
X-Platform must name a platform your project actually has an app for. In particular,
X-Platform: api resolves only if the project contains an API-platform app, created
in the dashboard: sending it without one returns 401 Invalid API key even though the
key is valid, so the fix is to create the API app (or name a platform the project does
have), never to rotate the key. A single-app key does not need X-Platform.
An API app receives offers as data only. Encore’s server-rendered offer surfaces
exist for iOS, Android, and web apps through their SDKs; an api app is never served
one, so on this tier every screen the user sees is yours.
What a test key serves
Whether a serve response carries your real inventory or the demo catalog is decided by the key. No header, build flag, or SDK setting overrides that choice. The one carve-out is not a serve response at all:POST /offers/catalog with includeAllRegions: true is a config-time discovery mode, and it returns your real inventory under either key.
X-Platform is not an exception to this. It resolves which app in a multi-app project handles the request, and that changes which of your campaigns are eligible. It does not change whether you are served your inventory or the demo catalog, and the demo catalog itself is the same whichever app resolves.
The demo catalog exists so you can exercise a complete claim without a live campaign, a real advertiser, or a payout. Most demo offers point at a fake advertiser page Encore hosts, and they differ in how they settle so you can rehearse each branch your integration has to handle:
- one settles the moment the demo form is submitted, for the straightforward path
- one settles after a short delay, so you can watch a claim resolve on a later poll
- one never settles at all, so you can see what an abandoned conversion looks like
- one leads to a real App Store listing, for the app-return path
quickInstructions on the offer’s
creative: it states the behavior in plain words, so you can tell a slow settlement from a
broken one without guessing.
Tap through, then close the page WITHOUT submitting.
Read it from the response rather than from a list here, because the catalog can change and
the response cannot go stale.
One demo offer is deliberately excluded from strict-unlock requests. Each offer advertises a settlement time, and a strict-unlock request filters the catalog to offers that advertise a time within the ceiling it sends. One demo offer advertises a time well beyond the usual ceiling, so a strict-unlock request does not receive it. That absence is the intended behavior: it lets you confirm your integration handles a catalog that a strict-unlock request has narrowed.
An offer’s advertised settlement time is what the filter reads. It is separate from how long that offer actually takes to settle.
Every demo offer is marked. Offers carry an isDemo field on every offer-serving
surface, so you never have to infer it from the brand name or the key you happened to send:
true on a demo offer. On ordinary inventory the shape differs by surface:
/offers/search serializes the field as false, while the other surfaces omit it. Test for
truthiness, which is correct everywhere, rather than comparing against false or checking
whether the key exists.
Use it to label a demo offer in a development build, and, more importantly, to stop one
reaching a real user: a test key shipped in a release build serves this catalog, and isDemo
is the check that turns that into a visible failure rather than a subtly wrong experience.
Demo conversions are inert on purpose. They never create a payable obligation and never count against a user’s trial limits, so you can claim the same demo offer as many times as you need.
What “not recorded” means. A test key still mints links and accepts
delivered calls. It simply records no impressions, clicks, or conversions from them. Use pk_live_* for anything that should show up in your dashboard.Creator links follow the same rule. The environment is captured when the link is minted, because the click itself is an end user opening a short URL with no key attached. So a link minted with pk_test_* carries the test flag, and its clicks are excluded from your analytics rather than counted as production traffic. A test mint and a live mint of the same creator and campaign are separate links with separate slugs, so a test never taints the live one.Creator links do differ from the rest of this page in one way: they mint against your real campaigns even under a test key. The demo catalog is not part of creator-link minting.The impression model
The API splits “we handed you an offer” from “the user actually saw it”:- Served (the baseline). When the feed returns offers, the server records each returned offer as served. This is bookkeeping, not an impression: calling the feed is not the impression, because you fetch a batch and may only ever show some of it.
- Delivered (the impression). When you actually render an offer, you confirm it with
POST /offers/impressions/{impressionUid}/deliveredusing the per-offerimpressionUidfrom the feed response. An offer you fetch but never confirm stays in the served baseline, never a confirmed impression.
delivered yet reads zero impressions against real served traffic. That reads as an unfinished integration rather than an idle app, and the confirmation call is what closes it.
The claim model
Every offer carries a tokenizedclickUrl. When the user taps, you open that URL; Encore records an attributed transaction and 302-redirects the user to the advertiser.
A claim is not a conversion. The claim is a fact about the offer flow: the user tapped through. The completion is verified separately on the advertiser side, and only a verified completion pays out. To react to completions from your server (grant a reward, credit a balance), use the offer_completed webhook.
What’s in this section
Rewarded Actions (start here)
The one end-to-end walkthrough: the complete worked feed pattern, from milestone to attributed claim
Curated Catalog
Pull your addressable inventory and curate your own surface
Automated Messaging
Append an offer to outbound automated messages
Creator Links
Permanent per-creator tracking links and a stats endpoint
Receive Completion Events
Wire the offer_completed webhook to react to verified completions
/offers/feed, /offers/impressions/{impressionUid}/delivered, /offers/catalog, /offers/message, Creator Links endpoints, /users/attributes, and the offer_completed webhook.