Skip to main content

Overview

If your platform automates outbound messages for your clients — on WhatsApp, Messenger, Instagram, SMS, or any other text channel — Encore can supply one extra outbound message per conversation: a complimentary, brand-safe offer (Apple Music free trial, Disney+ trial, etc.) sent alongside the response your client already sends. The integration is one REST call; no SDK to install, no per-message webhook to host. You pass us just the conversation identifier and three targeting attributes. We return the offer’s link + context fields — a branded short clickUrl plus title, description, imageUrl, advertiserName, and the campaign perk. We don’t compose a message for you: you build the reply from these fields and send it as a text message through whatever messaging API you already use.

When to call us

Two triggers, both gated by a max-ads-per-thread check on your side:
  1. First message of a new conversation thread. Always eligible. Call Encore, send the offer message first, then your client’s normal automated reply as a follow-up.
  2. First message after a long idle gap (a “re-engagement” — the diner went quiet for some hours/days and returned). Also eligible, but only if you haven’t already shown the configured max number of Encore offers in the same thread.
Counting prior offers is simple: every Encore offer link is a branded short link on the link.encorekit.com host. Scan the existing message history in the thread for outbound links to that host and compare to your per-thread cap (e.g. 3). If you’re below the cap, call us; if at the cap, skip.
Sequence matters: send the offer message before your client’s reply, not after. The offer reads as a brief “while you wait” footer to the conversation rather than a postscript stapled onto the end.

When not to call

  • Mid-thread replies — only first-of-thread and post-idle re-engagement triggers should fire an offer.
  • The per-thread cap has been reached (counted via link.encorekit.com links in the message history).
  • The end user has opted out (out of scope at v1; recipient-level cooldown is a planned feature).

Authentication

Pass your publishable key in the X-API-Key header. That’s the only auth header required.
X-API-Key: pk_live_...
Use pk_test_... against the sandbox project, pk_live_... against production. Same shape as our iOS / Android / Web SDKs.

Request

The request is minimal — just the conversation identity and three targeting attributes.
POST https://api.encorekit.com/encore/publisher/sdk/v1/offers/message
Content-Type: application/json
{
  "clientId": "client-42",
  "chatId": "8b3f7c10-9d4e-4d2a-91a1-3f0e8a1c0b5d",
  "attributes": {
    "countryCode": "US",
    "language": "en",
    "platform": "ios"
  }
}

Try it with curl

Drop in your publishable key and run.
curl --location --request POST 'https://api.encorekit.com/encore/publisher/sdk/v1/offers/message' \
  --header 'X-API-Key: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "clientId": "client-42",
    "chatId": "8b3f7c10-9d4e-4d2a-91a1-3f0e8a1c0b5d",
    "attributes": {
      "countryCode": "US",
      "language": "en",
      "platform": "ios"
    }
  }'
FieldTypeRequiredNotes
clientIdstringStable per-operator identifier — the business, account, or operator your platform is sending on behalf of (e.g. a restaurant ID). Carried through as an analytics dimension so impressions, clicks, and conversions can be broken down per client.
chatIdstringConversation / thread identifier on the messaging platform. Used as the messaging-surface userId — drives bandit seeding, the click-time transaction, and the userId dimension on the click event.
attributes.countryCodeISO-3166 alpha-2Two uppercase letters (e.g. "US", "GB", "CA"). Drives geo-targeting and the country dimension on analytics.
attributes.languagestringBCP-47 language tag (e.g. "en", "es"). Drives creative locale resolution.
attributes.platform"android" | "ios" | "web"The end user’s device platform — drives creative compatibility filtering.

Where each field comes from in the platform webhook

If you integrate on a Meta channel, both required fields map to values Meta puts in the inbound webhook payload — you don’t need to invent or join anything. Per platform:
Encore fieldWhatsApp Cloud APIInstagram (Messenger Platform)Facebook (Messenger Platform)
chatIdentry[].changes[].value.metadata.phone_number_id or your own thread keymessaging[].thread.id if present, else messaging[].sender.idmessaging[].thread.id if present, else messaging[].sender.id
clientIdIntegrator-defined (your operator’s stable ID — restaurant ID, account ID, etc.). Same value across platforms for the same operator.
attributes.countryCodeYour platform’s view of the end user’s country (account profile, IP lookup, etc.).
attributes.languageYour platform’s view of the end user’s language preference, or the language they’re actively chatting in.
attributes.platformYour platform’s view of the end user’s device, if known (otherwise pick the most likely surface).

Response

The response returns the offer’s link + context fields — the branded short clickUrl plus title, description, imageUrl, advertiserName, and perk. There is no channel-specific message payload and no server-composed message to forward; you compose and send the message yourself (see Sending the message).
{
  "success": true,
  "offer": {
    "clickUrl": "https://link.encorekit.com/aB3xY7q2",
    "title": "Free Apple Music for 6 months",
    "description": "Songs & Podcasts",
    "imageUrl": "https://storage.googleapis.com/encore-assets-prod/creatives/2c643fc5-91cb-4b0a-ba0a-0536c2c4f871/primary-1778695603093.png",
    "advertiserName": "Apple Music",
    "perk": "6 months free"
  }
}
The response carries no internal identifiers (impressionId, offer.id, campaignId, creativeId, and requestId are not returned — they’re still computed server-side for analytics) and no suggestedMessage or ctaText. You build the message yourself from the fields below.
FieldTypeNotes
successtrueAlways true on a 200. Non-success outcomes come back as 4xx/5xx; see Status codes.
offerobject | nullnull when no eligible offer was found for this user (geo-targeted out, no active campaigns, all creatives filtered by platform). See below.
offer.clickUrlURLBranded short link on the fixed host link.encorekit.com. A plain URL — embed it as text in your message; it tracks the click and redirects to the advertiser’s landing page. End-user-visible. Always include it in the message you send.
offer.titlestringHeadline of the offer. Use as the lead line if you compose your own message.
offer.descriptionstring | nullShort supporting line about the offer (e.g. "Songs & Podcasts" for Apple Music). May be null.
offer.imageUrlURLCreative image. Attach it as a media message if your channel supports media; otherwise omit — a text-only message works fine.
offer.advertiserNamestringThe advertiser / brand name (e.g. "Apple Music").
offer.perkstring | nullThe campaign value proposition (e.g. "6 months free"), or null. Use it to describe the deal in the message you compose.

When there’s no eligible offer

{
  "success": true,
  "offer": null
}
Never a 404. If offer is null, skip the promo step entirely and send only your client’s normal automated reply. Common reasons: geo-targeted out, your client has no eligible campaigns, all eligible creatives were filtered by recipient platform.

Sending the message

You send the offer to the end user yourself, as a normal text message, through whatever messaging API or channel you already use. There is no Encore-specific payload or template to forward, and we don’t compose a message for you — you build the text from the offer fields. Build the message from the fields — title, description, perk, advertiserName. The only hard requirement: the branded short clickUrl must appear in the text so the tap attributes correctly.

Example message

A simple, effective plain-text message for raw-text channels:
Get {perk} by claiming now: {clickUrl} — Unlocked by our partner EncoreKit
→ e.g. Get 6 months free by claiming now: https://link.encorekit.com/aB3xY7q2 — Unlocked by our partner EncoreKit This is a recommendation, not a required format — lead with title, attach imageUrl, or match your client’s tone however you like.
Recommended — append the partner sign-off. End the message you send with Unlocked by our partner EncoreKit. The clickUrl points to the link.encorekit.com host — one the end user wasn’t expecting from the business they were chatting with — so naming the partner keeps them comfortable tapping the link.
clickUrl is a branded short link on the fixed host link.encorekit.com. It’s a plain URL, so it renders and links correctly in any text channel — WhatsApp, Messenger, Instagram, SMS, RCS, etc. — with no interactive template or Graph API body required.

Attaching the image (optional)

If your channel supports media messages, you can attach offer.imageUrl as the message image and put your composed copy in the caption. If your channel is text-only, omit the image — the message still works as plain text.

Status codes

CodeMeaning
200Success. offer may be null if no eligible offer.
400Malformed request (missing required field, invalid attributes.countryCode, etc.). Don’t retry; fix the request.
401Missing or invalid X-API-Key.
409Your client has exceeded their trial limit (rare).
429Rate-limited. Back off + retry.
503Server temporarily over capacity (request queue full, dependency degraded). Retry with backoff.
5xx (other)Encore-side failure — proceed without the promo message, send only your client’s normal reply.

Retry guidance

  • Idempotency is on your side. The server no longer deduplicates retries — every call generates a fresh short link and analytics row. Only call /offers/message once per inbound message you’ve decided to attach an offer to (de-dupe inbound webhooks on your side before calling).
  • Use exponential backoff with jitter (e.g. 200ms → 1s → 5s, max 3 retries) for transient failures (429 / 503 / network).
  • After exhausting retries, drop the call and proceed with your client’s normal reply — never degrade end-user experience because of an Encore outage.
Treat any non-200 response as a soft fallback: skip the promo step and send your client’s configured auto-reply on its own. The end-user experience should never degrade because of an Encore outage.

End-to-end example

A complete flow, from incoming message to outbound text message:
1. End user +14155551234 texts your client's business number
   +14155556789 on WhatsApp. Meta delivers the webhook to your platform.

2. Your platform receives the webhook / accessibility event and decides
   (per the "When to call us" rules above) that this conversation is
   eligible for an Encore offer.

3. Your platform POSTs to Encore (new chat → fresh chatId):

   POST /encore/publisher/sdk/v1/offers/message
   X-API-Key: pk_live_abc123...
   {
     "clientId": "client-42",
     "chatId": "8b3f7c10-9d4e-4d2a-91a1-3f0e8a1c0b5d",
     "attributes": { "countryCode": "US", "language": "en", "platform": "ios" }
   }

4. Encore responds with the offer's link + context fields (see Response):

   {
     "success": true,
     "offer": {
       "clickUrl": "https://link.encorekit.com/aB3xY7q2",
       "title": "Free Apple Music for 6 months",
       "advertiserName": "Apple Music",
       "perk": "6 months free",
       ...
     }
   }

5. Your platform composes a text message from those fields (including the
   clickUrl, ending with the partner sign-off) and sends it to the end user
   through your own WhatsApp API as the body:

   "Get 6 months free by claiming now:
    https://link.encorekit.com/aB3xY7q2 — Unlocked by our partner EncoreKit"

6. Your platform then sends your client's configured auto-reply normally:

   "Thanks for your message! Our team will get back to you shortly."

7. End user taps the link.encorekit.com short link → OS browser opens →
   advertiser landing page → attribution chain closes.