https://www.encorekit.com/offers. Your app opens that
URL in a plain webview, passes its configuration in the query string, and verifies the
result from your backend. No package to install, no build step, and nothing to ship in a
client release.
Use it when installing an SDK is not on the table: a native app with no Encore integration,
a kiosk or device shell, a partner surface whose build you do not own, or a pilot you want
running this week. Everything else on web should use the
Web SDK, which hands the presentation result back to
your own code.
Three routes serve the identical page, so pick whichever fits your app:
What your host app must get right
The page is built to work in a webview nobody configured, so there is very little to do. These three things are the exceptions, and each one breaks an integration that does not know about it.Open the page at top level, never in an iframe
Load the URL as the webview’s own document. The advertiser handoff navigates the frame the offer page is running in. Inside an iframe that means the advertiser loads into your iframe rather than taking over the view, so the user ends up on a third-party site rendered in a box inside your app, with your own chrome still wrapped around it and no sensible way out. A top-level document is the only arrangement the handoff is designed for. The symptom is easy to recognize once you know it: the offer itself looks fine, and only after a claim does the user end up stuck on an advertiser page inside a small frame. If you see that, the page is being loaded in a frame.Claiming replaces the page, so own the way back
This is the one genuinely surprising behavior, and it only surfaces after a successful claim, which is the worst moment to discover it. A locked webview with no back gesture and no surrounding chrome strands the user on the advertiser’s site.1
Expect the webview to navigate away
After a claim, the webview is showing the advertiser’s site rather than Encore or your app.
That is the normal, successful path, not a failure.
2
Give the user a way back
Provide your own close or back control, either in the chrome around the webview or through
the webview’s own back stack. The offer page cannot return the user to your app, and there is
no confirmation screen to close.
3
Let the navigation through
If your
WebViewClient.shouldOverrideUrlLoading or your
WKNavigationDelegate.decidePolicyFor restricts navigation to your own domain, the handoff is
blocked at the last step. Allow www.encorekit.com and outbound advertiser domains, or pass a
blocked URL to the system browser instead of cancelling it.4
Do not tear the webview down on navigation
A host that closes the webview the moment the URL leaves
encorekit.com closes it on the user
mid-claim.This holds for the hosted page only
The hosted page does that work for you. If you ever hand-roll your own page around@encorekit/web-sdk instead, the behavior changes underneath you.
The SDK reaches the advertiser through window.open, inside the user’s tap on Claim. That
is correct in a browser and a no-op in a webview: window.open returns null in an Android
WebView unless the host sets setSupportMultipleWindows(true) and implements
WebChromeClient.onCreateWindow, and null in a WKWebView unless the host implements
WKUIDelegate.createWebViewWith. There is no second tab to be had, so Claim becomes a dead
tap. The hosted page replaces window.open with a stand-in that navigates the current frame,
which needs no cooperation from the host at all.
Because the advertiser is reached by a same-frame navigation rather than the SDK’s
noopener,noreferrer popup, the page declares a no-referrer policy. Your API key and your
end user’s id never travel to the advertiser in a Referer header. The route is also excluded
from indexing and from the sitemap.The URL contract
Every value is trimmed, and a blank value is treated as absent, so?headline= behaves
exactly like omitting headline. Values longer than 256 characters are truncated rather
than rejected.
Required
Omit either one and the page renders a diagnostic screen naming what is missing, instead of
an offer. Build your URLs against that screen before you ship.
Optional
An unrecognized
useCase or placement does not fall back to the default. The page
refuses to start and names the invalid parameter, because silently presenting the wrong
screen to someone who asked for a specific one is the harder failure to track down.
An invalid language is the exception: the page ignores it, still starts, and follows the
device language.
Targeting attributes
Attributes go to Encore in their own request, before you open the page. You POST them toPOST /users/attributes against the same
userId the link carries, and the page picks them up when it fetches offers.
That call authenticates with your publishable key, the same one the link itself carries in
plain sight, so it can run wherever you assemble the link: a backend, or the app that opens
the webview. Nothing about it requires a server.
1
Store the set
POST the attributes with your publishable key. The write replaces the stored set rather than
merging into it, so send the complete set every time. That also makes a retry safe, though
only a network failure or a
5xx is worth retrying. A 4xx means the request itself needs
fixing, and repeating it produces the same refusal every time.2
Open the link
https://www.encorekit.com/offers?apiKey=...&userId=..., plus whichever of useCase,
placement, headline, subheadline, language, and autoPresentOffer you need.Store the attributes
userId you just stored against.
Worked URLs has the link shapes and a snippet that builds one. A user whose
attributes did not store is still served, just without the targeting the set would have added.
The endpoint reference carries the full
vocabulary and which apps may send which keys, including an age and, from an app rated 18+
in its store listing, the direct identifiers. Read its rules on age before you send one.
Three of them catch people out: a user under 18 is still served offers, a range is sent as its
lowest end, and for a user under 18 only age and countryCode are kept out of
whatever you sent.
Worked URLs
Build the URL
Webview settings
Nothing here is required to make the page work. Both settings buy you better behavior.Turning on DOM storage
AndroidWebView ships with DOM storage turned off. The SDK degrades cleanly, falling
back from localStorage to sessionStorage to in-memory, so nothing crashes and nothing
looks wrong. What it costs you is that every marker the SDK keeps per user is re-derived on
each open: the measurement exposure marker, analytics deduplication markers, the cached
entitlement snapshot, and any queued events still waiting to be delivered. The most visible
effect is an inflated exposure count in reporting.
Two things are unaffected, and worth knowing so you do not go hunting for a problem that is
not there. Identity is stable, because userId arrives in the URL on every open rather
than out of storage. Experiment cohort is stable too, because it is derived
deterministically from that same userId rather than stored and looked up.
One line removes the whole issue:
Load it over HTTPS
Point the webview athttps://www.encorekit.com directly. Cohort assignment uses WebCrypto,
which browsers expose only in a secure context. Serving this page through a plain http://
origin silently drops every user out of measurement.
Verify the entitlement from your server
There is no client bridge, and none is needed. The page and your app never talk to each other. They agree on auserId, and your backend asks Encore what that user holds.
Call GET /entitlements/server with
the same userId you put in the URL. It is HMAC-signed, so it is a backend call only:
Backend (Node)
/encore gateway prefix, and a GET signs a
canonical query string rather than a body. Multi-app projects also send X-Platform.
When the page does not start
Missing or invalid parameters produce a plain screen reading “This link isn’t complete”, followed by the specific problem. Every missing name is listed at once, then every invalid one, so one look is enough:missing apiKey, userId, or missing userId · invalid useCase.
That text is aimed at whoever is assembling the URL. Check it first when a webview opens to
something other than an offer.
A page that starts but presents nothing is a different, normal outcome. It means no offers
are eligible for that user right now, and it renders “Nothing available right now”. That
is neither an error nor a misconfiguration.
See also
- The Claim Flow: what a claim does, and what the claimed result carries
- GET /entitlements/server: the full endpoint contract for the call shown here
- Offer Completed Webhook: the push counterpart
- POST /users/attributes: the full targeting vocabulary, and the rules on sending an age
- Web SDK Quickstart: the full integration, for when you can install a package