Skip to main content

Overview

If you display offers to your users on your own surface, Encore can supply the offers to fill it. You call one REST endpoint, get back the set of offers your app is allowed to serve to a specific end user, and render them in your own UI. There is no SDK to install and no webhook to host. Encore does the eligibility work for you. Every response is already filtered by two rules you cannot override:
  • Entitlement. Only offers your app is permitted to serve (your account’s opt-outs and any allow-list restrictions) are ever returned.
  • Geography. Only offers redeemable in the end user’s country are returned. Offers a user could not redeem are never shown.
You do the presentation work: pick which of the returned offers to feature, order them, and fall back to others when your first choices are not available for a given user.

Typical usage pattern

Two hard filters, entitlement and geography, always run on Encore’s side, so any offer you receive is one your app is allowed to serve and the end user can redeem. Your curation, which offers to feature and in what order, is a soft filter you apply on top, on your side. That split is the whole pattern. Once, at configuration time. Call the endpoint with includeAllRegions: true to pull your full addressable inventory. Each offer includes its targetCountries, so you can see where every offer is available. Choose the offers you want to feature and save their ids in your own configuration. Re-run whenever you want to refresh what is available to you. This view skips the geography filter and is for planning only, not for display. On every render. Call the endpoint with a stable userId for the end user and their country (or their forwarded IP). Encore returns the offers that user is eligible for, already entitlement- and geography-filtered, each creative carrying a clickUrl. Then, on your side:
  1. Keep the returned offers whose id is in your featured list, in your preferred order.
  2. If none of your featured offers came back for this user, fall back to the other returned offers so your surface is never empty.
  3. Render each offer, and when the user taps one, send them to that creative’s clickUrl so the tap is attributed (see Click tracking and attribution).
Why filter on your side rather than sending us your featured ids? Because the hard filters can remove some of your picks for a given user (for example, offers that are all US-only when the user is in Romania). If we narrowed to your list on the server, you would be left with nothing to show. Returning the whole eligible set lets you fall back gracefully. And you can never accidentally show an ineligible offer, because everything we return has already passed the hard filters.

Authentication

Pass your publishable key in the X-API-Key header:
X-API-Key: pk_live_...
If your key spans multiple platform-specific apps (a multi-app project), also send an X-Platform header so we know which app to resolve; otherwise the request is rejected with X-Platform header required for multi-app projects:
X-Platform: ios   # one of: ios | android | web
A single-app key does not need X-Platform. Your app is identified entirely by the key (plus platform, if multi-app). You never name another app, and you only ever receive offers your app is entitled to.

Serving a user

curl -X POST https://api.encorekit.com/encore/publisher/sdk/v1/offers/catalog \
  -H "X-API-Key: pk_live_..." \
  -H "X-Platform: ios" \
  -H "Content-Type: application/json" \
  -d '{ "userId": "<your stable end-user id>", "countryCode": "US" }'
Response:
{
  "success": true,
  "offers": [
    {
      "id": "b1a7c0de-0000-4000-8000-000000000001",
      "name": "Apple Music - 3 months free",
      "perk": "3 months of Apple Music free",
      "oldPrice": "10.99",
      "newPrice": "0.00",
      "targetCountries": ["US", "CA"],
      "organization": {
        "id": "…",
        "name": "Apple",
        "description": "…",
        "url": "https://www.apple.com",
        "logoUrl": "https://…/apple-logo.png"
      },
      "creatives": [
        {
          "id": "…",
          "title": "Get 3 months of Apple Music free",
          "subtitle": "…",
          "description": "…",
          "primaryImageUrl": "https://…/creative.png",
          "logoUrl": "https://…/logo.png",
          "additionalImages": ["https://…/alt.png"],
          "ctaText": "Claim Offer",
          "supportedPlatforms": ["ios", "android", "web"],
          "clickUrl": "https://api.encorekit.com/encore/click/eyJhcHBJZCI6…"
        }
      ]
    }
  ],
  "metadata": { "total": 8, "limit": 8, "offset": 0, "hasMore": false },
  "locale": "en"
}
Each offer carries the fields you need to render an offer card — the offer’s name, perk, optional oldPrice/newPrice, the advertiser (organization), and one or more creatives (image, title, description, CTA, supported platforms) — plus each creative’s clickUrl. It deliberately does not include Encore’s internal or commercial fields (payout amounts, the raw advertiser destination URL, tracking parameters, internal notes/config): to send a user to an offer you always use the clickUrl, never a raw link (see Click tracking and attribution). By default the whole eligible set is returned, so you normally take it all and curate on your side; to page, pass limit (and offset) and hasMore tells you when to stop.

Geography is required

Encore never serves an offer a user cannot redeem, so the serve call needs to know the end user’s country. Provide it one of two ways:
  • countryCode in the body (ISO 3166-1 alpha-2, for example "US", "GB"). Use this when you call from your server and already know the user’s country.
  • The end user’s IP, forwarded via the standard X-Forwarded-For header. Use this when the call originates from the user’s device, or when your proxy passes the user’s IP through.
If neither is present, the call returns 400. We do not guess a default country, because that could serve the wrong region’s offers.

Diagnosing a missing pick

The response contains only offers eligible for this user, so a featured offer you expected may be absent. To tell why, compare against your config-time inventory (the includeAllRegions call): if the offer is in that inventory but not in this response, its targetCountries did not include the user’s country. If it is not in the inventory at all, your app is not entitled to it. See Typical usage pattern for the full request-and-filter loop.

Click tracking and attribution

Every creative in a serve response carries a clickUrl. When the end user taps an offer, route them to that creative’s clickUrl. Encore records an attributed transaction, then responds with a 302 redirect that sends the user on to the advertiser. You do not build any tracking links yourself and you do not host a redirect. A few rules keep attribution correct:
  • Use the clickUrl exactly as returned. Treat it as opaque and do not modify, wrap, or rebuild it. Each clickUrl is signed and bound to a specific offer, creative, and user, so any change invalidates it.
  • Send a stable userId on every serve call. Use the same value for the same end user across renders. That identifier is what the recorded transaction is attributed to, and it drives per-user protections at tap time.
  • One clickUrl per creative. Route the user to the clickUrl on the exact creative they tapped, not a shared or reused one.
For example, when a user taps the Apple Music offer, open the clickUrl from that offer’s creative:
https://api.encorekit.com/encore/click/eyJhbGciOi…
Encore handles the rest: it records the click and redirects the user to Apple. Config mode has no click URLs. A call with includeAllRegions: true is for planning, not display, so it returns no userId binding and no clickUrls. Only serve calls (scoped to a user and their country) return clickUrls you can route taps to.

Building your curation

To see everything your app can offer, regardless of any single user’s geography, call with includeAllRegions:
curl -X POST https://api.encorekit.com/encore/publisher/sdk/v1/offers/catalog \
  -H "X-API-Key: pk_live_..." \
  -H "X-Platform: ios" \
  -H "Content-Type: application/json" \
  -d '{ "includeAllRegions": true }'
This returns your full addressable inventory (entitlement still applies), with each offer’s targetCountries. Use it to decide which offers to feature and to understand each offer’s coverage. Do not render offers straight from this view to end users, since it is not geography-filtered. Anything you display must come from a serve call scoped to that user’s country.

Request fields

FieldTypeNotes
userIdstring, required on serveA stable identifier for the end user you are serving. Bound into each creative’s clickUrl so a tap records an attributed transaction for that user. Use the same value across renders for the same user. Not used in config mode (includeAllRegions), which returns no clickUrls.
clientIdstring, optionalIdentifier for the operator or sub-publisher on whose behalf offers are shown. Carried through as an analytics dimension. Defaults to userId when omitted.
countryCodestring (ISO 3166-1 alpha-2), optionalEnd user’s country. Overrides IP-based detection. Required, via this field or a forwarded IP, unless includeAllRegions is set.
includeAllRegionsboolean, optionalConfig-time only. Skips geography filtering and returns your full inventory with targetCountries. Not for display.
limitinteger 1 to 100, optionalPage size. Omit to return ALL eligible offers (the default).
offsetinteger, optionalPage offset (default 0).
Full field-level detail lives in the API reference.

What Encore handles vs what you handle

Handled by Encore (server side)Handled by you (client side)
Which offers your app may serve (entitlement)Which eligible offers to feature
Which offers are redeemable in the user’s country (geography)Ordering and layout
Attaching each offer’s creatives and organizationFalling back to other offers when your picks are unavailable
Removing offers with no renderable creativeRendering the offers in your own layout