> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encorekit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Automated Messaging Integration

> Append Encore offers to your outbound automated messages via a single REST call — the endpoint returns the offer's link plus context fields, and you compose and deliver the message through whatever channel you already use.

## 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.

```mermaid theme={null}
sequenceDiagram
    participant User as End user
    participant Channel as Messaging channel
    participant You as Your platform
    participant Encore

    User->>Channel: incoming message
    Channel->>You: webhook / accessibility event
    You->>You: count link.encorekit.com links<br/>already in this thread
    You->>Encore: POST /offers/message (if below cap)
    Encore-->>You: { offer: { clickUrl, title, perk, ... } }
    You->>You: compose a text message from the offer fields
    You->>Channel: send offer as a text message
    Channel->>User: Encore offer message
    You->>Channel: send your client's configured auto-reply
    Channel->>User: client's normal response
```

<Note>
  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.
</Note>

### 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.

```bash theme={null}
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.

```http theme={null}
POST https://api.encorekit.com/encore/publisher/sdk/v1/offers/message
Content-Type: application/json
```

```json theme={null}
{
  "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.

```bash theme={null}
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"
    }
  }'
```

| Field                    | Type                              | Required | Notes                                                                                                                                                                                                                                              |
| ------------------------ | --------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `clientId`               | string                            | ✓        | Stable 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. |
| `chatId`                 | string                            | ✓        | Conversation / 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.countryCode` | ISO-3166 alpha-2                  | ✓        | Two uppercase letters (e.g. `"US"`, `"GB"`, `"CA"`). Drives geo-targeting and the country dimension on analytics.                                                                                                                                  |
| `attributes.language`    | string                            | ✓        | BCP-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 field             | WhatsApp Cloud API                                                                                                                   | Instagram (Messenger Platform)                                   | Facebook (Messenger Platform)                                    |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- | ---------------------------------------------------------------- |
| `chatId`                 | `entry[].changes[].value.metadata.phone_number_id` or your own thread key                                                            | `messaging[].thread.id` if present, else `messaging[].sender.id` | `messaging[].thread.id` if present, else `messaging[].sender.id` |
| `clientId`               | Integrator-defined (your operator's stable ID — restaurant ID, account ID, etc.). Same value across platforms for the same operator. |                                                                  |                                                                  |
| `attributes.countryCode` | Your platform's view of the end user's country (account profile, IP lookup, etc.).                                                   |                                                                  |                                                                  |
| `attributes.language`    | Your platform's view of the end user's language preference, or the language they're actively chatting in.                            |                                                                  |                                                                  |
| `attributes.platform`    | Your 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](#sending-the-message)).

```json theme={null}
{
  "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.

| Field                  | Type             | Notes                                                                                                                                                                                                                                           |
| ---------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `success`              | `true`           | Always `true` on a 200. Non-success outcomes come back as 4xx/5xx; see [Status codes](#status-codes).                                                                                                                                           |
| `offer`                | object \| `null` | `null` when no eligible offer was found for this user (geo-targeted out, no active campaigns, all creatives filtered by platform). See below.                                                                                                   |
| `offer.clickUrl`       | URL              | Branded 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.title`          | string           | Headline of the offer. Use as the lead line if you compose your own message.                                                                                                                                                                    |
| `offer.description`    | string \| `null` | Short supporting line about the offer (e.g. `"Songs & Podcasts"` for Apple Music). May be `null`.                                                                                                                                               |
| `offer.imageUrl`       | URL              | Creative image. Attach it as a media message if your channel supports media; otherwise omit — a text-only message works fine.                                                                                                                   |
| `offer.advertiserName` | string           | The advertiser / brand name (e.g. `"Apple Music"`).                                                                                                                                                                                             |
| `offer.perk`           | string \| `null` | The 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

```json theme={null}
{
  "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:

```text theme={null}
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.

<Note>
  **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.
</Note>

<Note>
  `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.
</Note>

### 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

| Code          | Meaning                                                                                                           |
| ------------- | ----------------------------------------------------------------------------------------------------------------- |
| `200`         | Success. `offer` may be `null` if no eligible offer.                                                              |
| `400`         | Malformed request (missing required field, invalid `attributes.countryCode`, etc.). Don't retry; fix the request. |
| `401`         | Missing or invalid `X-API-Key`.                                                                                   |
| `409`         | Your client has exceeded their trial limit (rare).                                                                |
| `429`         | Rate-limited. Back off + retry.                                                                                   |
| `503`         | Server 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.

<Note>
  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.
</Note>

***

## End-to-end example

A complete flow, from incoming message to outbound text message:

```text theme={null}
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.
```
