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

# Channels

> Define delivery mechanisms for recommendations — email, push, SMS, in-app, web, WhatsApp, webhook, and direct mail.

<Note>
  **See also**: [Channels REST API reference](/api-reference/channels) for request/response shapes, status codes, and error semantics.
</Note>

A **Channel** defines *how* a recommendation reaches a customer. While [Offers](/studio/offers) define *what* to recommend and [Decision Flows](/decisioning/decision-flows) decide *which* offer to show, Channels handle the last mile: delivery mode, impression tracking, and provider integration. Each channel can have multiple **placements** (named slots like `hero_banner` or `sidebar_widget`) and connects to [Creatives](/studio/creatives) that contain the actual content.

When you create a channel, KaireonAI automatically routes it to your default Decision Flow so it works immediately — no manual wiring required.

<Info>
  Channels are delivery-agnostic containers. The same offer can be delivered through email, push, and in-app simultaneously, each with different creatives optimized for that channel.
</Info>

***

## How Channels Work

1. **You create a channel** with a type (email, push, etc.), delivery mode, and impression tracking method
2. **You add placements** — named slots where content appears (e.g., `hero_banner`, `checkout_upsell`)
3. **You create creatives** — content variants linked to specific offers and channels
4. **At recommendation time**, the Decision Flow matches candidates to creatives for the requested channel and allocates them across placements

***

## Channel Types

| Type          | Description                      | Common Use Cases                            |
| ------------- | -------------------------------- | ------------------------------------------- |
| `email`       | Email delivery                   | Marketing campaigns, transactional messages |
| `push`        | Mobile push notifications        | Real-time alerts, engagement nudges         |
| `sms`         | SMS text messages                | Time-sensitive alerts, verification         |
| `in_app`      | In-application messages          | Contextual recommendations, banners         |
| `web`         | Web browser (banners, overlays)  | Website personalization                     |
| `webhook`     | HTTP callback to external system | Custom integrations, third-party platforms  |
| `whatsapp`    | WhatsApp Business messages       | Conversational commerce, order updates      |
| `direct_mail` | Physical mail                    | High-value customer outreach                |

### Retail Rewards Example

| Channel              | Type     | Placements                             | Use Case                                                     |
| -------------------- | -------- | -------------------------------------- | ------------------------------------------------------------ |
| Mobile App           | `in_app` | `home_carousel`, `order_screen_upsell` | BOGO offers, loyalty-points promotions shown during ordering |
| Retail Rewards Email | `email`  | `header_offer`, `body_cross_sell`      | Weekly promotional emails with personalized offers           |
| Push Notifications   | `push`   | `morning_digest`, `happy_hour_alert`   | Time-sensitive flash sales, nearby store promos              |
| Web                  | `web`    | `hero_banner`, `sidebar_widget`        | Website personalization for logged-in rewards members        |

***

## Delivery Modes

Each channel operates in one of four modes that control how recommendations reach the customer:

<AccordionGroup>
  <Accordion title="API (Real-time)">
    External systems call the `/api/v1/recommend` endpoint to pull decisions in real time. The response includes ranked offers with their creatives, and the calling system renders them.

    **Best for:** Web personalization, in-app messages, real-time push, webhook integrations.

    **Latency:** Typically under 200ms end-to-end.
  </Accordion>

  <Accordion title="File (Batch)">
    Recommendations are exported as a file for ingestion by an external system. Selecting **File (Batch)** just marks the channel as file-producing — the file's format, columns, and destination are configured **on the campaign**, not here.

    **Where to configure the file:** the **File Output** section of the [campaign editor](/operations-reporting/runs#file-output-configuration) (`Run.fileConfig`). One config drives every file-mode channel in the campaign, so two campaigns sharing this channel can output different files. Supported formats are CSV, TSV, JSON, and JSONL; the destination is a download-only artifact or an Amazon S3 connector; columns map to built-in decision fields or segment-schema attributes with optional transforms — see [File Output Configuration](/operations-reporting/runs#file-output-configuration).

    **Best for:** Email campaign systems, direct mail vendors, data warehouse ingestion, batch CRM integrations.
  </Accordion>

  <Accordion title="Integration (Provider)">
    Delivers recommendations via a platform integration with a specific provider. Provider credentials are stored encrypted in `providerConfig`.

    **Best for:** Managed delivery through SendGrid, Twilio, Firebase, WhatsApp Business API.

    **Flow:** During a batch run, creative content is assembled and — when live delivery is enabled — sent through the provider stack via `sendWithTracking`, which creates a `ChannelDelivery` record per send for tracking and status callbacks.

    **Delivery is gated.** Real provider sends happen only when the tenant has explicitly opted in via `TenantSettings.liveDelivery` (default `false`). Each integration-mode item is reported with an honest status:

    | Status      | When                                                                     |
    | ----------- | ------------------------------------------------------------------------ |
    | `delivered` | `liveDelivery` is on **and** the provider accepted the send              |
    | `simulated` | `liveDelivery` is off (the default) — nothing was transmitted            |
    | `skipped`   | No recipient address resolved, or no provider configured for the channel |

    <Warning>
      With `liveDelivery` off (the default), integration-mode channels never transmit — every item is reported as `simulated`. Real sends require enabling `TenantSettings.liveDelivery` **and** having a configured provider plus a resolved recipient address.
    </Warning>
  </Accordion>

  <Accordion title="Manual (Human Workflow)">
    Recommendations appear in a queue for agents or relationship managers to act on. No automated delivery — KaireonAI tracks decisions and outcomes.

    **Best for:** High-touch sales, advisory conversations, branch operations, call center agents.
  </Accordion>
</AccordionGroup>

***

## Placements

A **placement** is a named slot within a channel where content can appear. Placements let you request recommendations for a specific slot, track performance per placement, and control how many offers appear in each position.

| Channel | Placement             | `slotType`     | `maxSlots` |
| ------- | --------------------- | -------------- | ---------- |
| Web     | `hero_banner`         | `banner`       | 1          |
| Web     | `sidebar_widget`      | `card`         | 3          |
| Email   | `header_offer`        | `banner`       | 1          |
| Email   | `body_recommendation` | `card`         | 3          |
| In-App  | `home_carousel`       | `card`         | 5          |
| Push    | `morning_digest`      | `notification` | 1          |

When used with the [Composable Pipeline](/data/transforms/composable-pipeline) Group node, placements control how ranked offers are allocated across multiple slots in a single response.

### Placement Fields

| Field         | Type    | Required | Default    | Description                                            |
| ------------- | ------- | -------- | ---------- | ------------------------------------------------------ |
| `name`        | string  | Yes      | --         | Unique slot identifier (e.g., `hero_banner`)           |
| `description` | string  | No       | `""`       | Human-readable description                             |
| `slotType`    | string  | No       | `"banner"` | Visual type: `banner`, `card`, `modal`, `notification` |
| `maxSlots`    | integer | No       | `1`        | Maximum offers this placement can show                 |
| `schema`      | object  | No       | `{}`       | Content schema requirements                            |
| `targeting`   | object  | No       | `{}`       | Targeting rules for this placement                     |

***

## Impression Tracking

Channels control how customer impressions (views) are recorded. This affects contact policy enforcement and dashboard metrics.

<Tabs>
  <Tab title="Explicit (Default)">
    The client application calls the [Respond API](/api-reference/introduction) after displaying the recommendation. You get precise control over when an impression is counted.

    ```bash theme={null}
    POST /api/v1/respond
    {
      "customerId": "cust_123",
      "offerId": "act_abc",
      "channelId": "ch_email",
      "outcome": "impression"
    }
    ```

    **When to use:** Any channel where you can confirm the customer actually saw the recommendation (web, in-app, email opens).
  </Tab>

  <Tab title="Implicit">
    KaireonAI automatically creates an impression record when the Recommend API returns an offer through this channel. No separate Respond API call needed.

    <Warning>
      Implicit tracking counts every recommendation as an impression, even if the customer never actually sees it. Use only for channels with guaranteed delivery (e.g., push with confirmed delivery).
    </Warning>
  </Tab>
</Tabs>

***

## Delivery Providers

When using **Integration** delivery mode, KaireonAI sends messages through configured providers. All providers support delivery tracking, idempotency, circuit breakers, and retry logic.

### Delivery states and retries

Every dispatch creates a delivery record keyed by an idempotency hash of
(interaction, channel, creative), so the same decision can never contact a
customer twice.

| Status                                         | Meaning                                                                                                                                           |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pending`                                      | Not yet handed to a provider. Normal for a **Manual** channel, and the resting state while the circuit breaker is open or between retry attempts. |
| `sent`                                         | Accepted by the provider. Never re-sent.                                                                                                          |
| `delivered` / `opened` / `clicked` / `bounced` | Provider callbacks against the stored message id.                                                                                                 |
| `failed`                                       | The retry ladder is exhausted (`maxAttempts`, default 3).                                                                                         |

A `pending` delivery with attempts remaining is **re-attempted** the next time
the send is invoked for that interaction; each attempt is claimed atomically, so
two concurrent callers produce one dispatch, not two. Once `attempts` reaches
`maxAttempts` the record becomes `failed` and is not retried again.

<Warning>
  Before 2026-08-15 the idempotency check short-circuited on **any** existing
  record, including `pending`. A delivery the circuit breaker had never dispatched
  could therefore never be dispatched — it stayed `pending` permanently and never
  surfaced as a failure, despite `maxAttempts` implying otherwise. If you are
  running an older build, look for stranded records with:

  ```sql theme={null}
  SELECT status, count(*), max(attempts)
  FROM channel_deliveries
  WHERE "tenantId" = '<tenant>'
  GROUP BY status;
  ```

  Records at `pending` with `attempts` below `maxAttempts` were never sent.
</Warning>

| Provider              | Channel Type  | Configuration                              |
| --------------------- | ------------- | ------------------------------------------ |
| **AWS SES**           | `email`       | Region, From Email, Reply-To               |
| **Twilio**            | `sms`         | Account SID, Auth Token, From Number       |
| **Amazon SNS**        | `sms`, `push` | Region, Auth Mode, Sender ID               |
| **Firebase**          | `push`        | Project ID, Service Account JSON           |
| **WhatsApp (Meta)**   | `whatsapp`    | Phone Number ID, Access Token, API Version |
| **WhatsApp (Twilio)** | `whatsapp`    | Account SID, Auth Token, From Number       |
| **Webhook**           | `webhook`     | Target URL, Headers                        |

### WhatsApp Integration

KaireonAI supports WhatsApp Business messaging through two providers:

<Tabs>
  <Tab title="Meta Cloud API (Recommended)">
    Direct integration with the official WhatsApp Business API. Supports template messages, text messages, and media messages.

    **Setup:**

    1. Create a Meta Business account and WhatsApp Business app at [developers.facebook.com](https://developers.facebook.com)
    2. Get your Phone Number ID and permanent access token
    3. Configure in **Settings > Integrations > WhatsApp** or via environment variables:
       * `WHATSAPP_PROVIDER=meta`
       * `WHATSAPP_PHONE_NUMBER_ID=your_phone_number_id`
       * `WHATSAPP_ACCESS_TOKEN=your_access_token`
    4. Set up the webhook at `POST /api/v1/webhooks/whatsapp` for delivery status callbacks
  </Tab>

  <Tab title="Twilio WhatsApp">
    Uses Twilio's WhatsApp Business API integration (same SDK as SMS).

    **Setup:**

    1. Enable WhatsApp in your Twilio account
    2. Get a WhatsApp-approved number
    3. Configure via environment variables:
       * `WHATSAPP_PROVIDER=twilio`
       * `WHATSAPP_TWILIO_ACCOUNT_SID=your_sid`
       * `WHATSAPP_TWILIO_AUTH_TOKEN=your_token`
       * `WHATSAPP_TWILIO_FROM_NUMBER=+14155238886`
  </Tab>
</Tabs>

***

## Auto-Routing to Decision Flows

When you create a channel or add a placement, KaireonAI automatically creates **FlowRoutes** linking them to the tenant's [default Decision Flow](/decisioning/decision-flows#auto-routing). New channels work immediately without manual routing setup.

* **New channel** -- FlowRoute created to the default flow
* **New placement** -- FlowRoute created to the default flow

You can override any auto-created route from the channel detail page or the Decision Flow configuration.

<Info>
  Auto-routing only creates routes to the **default** flow. To route a channel to a different flow, create the route manually or change the default flow first.
</Info>

***

## Provider Config

Each channel can store provider-specific configuration for external delivery:

```json theme={null}
{
  "provider": "sendgrid",
  "apiKey": "SG.xxxxx",
  "fromEmail": "offers@yourcompany.com",
  "fromName": "Your Company",
  "templateEngine": "handlebars"
}
```

<Note>
  Provider config is stored encrypted. API keys and credentials are never returned in GET responses.
</Note>

***

## Creating a Channel

<Steps>
  <Step title="Navigate to Channels">
    Go to **Studio > Channels** in the sidebar.
  </Step>

  <Step title="Click + New Channel">
    Click the **+ New Channel** button.
  </Step>

  <Step title="Select type and delivery mode">
    Choose a channel type (email, push, etc.) and delivery mode (API, File, Integration, Manual).
  </Step>

  <Step title="Set impression tracking">
    Choose **Explicit** (default) or **Implicit**.
  </Step>

  <Step title="Add placements (optional)">
    Define named slots. Each placement has a name, slot type, and max offers.
  </Step>

  <Step title="Configure provider (optional)">
    Enter provider credentials for external delivery.
  </Step>

  <Step title="Save">
    The channel is immediately available for creatives and Decision Flows.
  </Step>
</Steps>

***

## Field Reference

| Field            | Type   | Required | Default      | Description                                                                                                                                                                                                                                                                                                                                   |
| ---------------- | ------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`           | string | Yes      | --           | Display name (1--255 chars)                                                                                                                                                                                                                                                                                                                   |
| `channelType`    | string | No       | `"email"`    | `email`, `push`, `sms`, `in_app`, `web`, `webhook`, `whatsapp`, `direct_mail`                                                                                                                                                                                                                                                                 |
| `status`         | enum   | No       | `"draft"`    | `draft`, `active`, `paused`, `archived`                                                                                                                                                                                                                                                                                                       |
| `description`    | string | No       | `""`         | Free-text description                                                                                                                                                                                                                                                                                                                         |
| `deliveryMode`   | enum   | No       | `"api"`      | `api`, `file`, `integration`, `manual`                                                                                                                                                                                                                                                                                                        |
| `impressionMode` | enum   | No       | `"explicit"` | `explicit` or `implicit`                                                                                                                                                                                                                                                                                                                      |
| `providerConfig` | object | No       | `{}`         | Provider credentials (stored encrypted)                                                                                                                                                                                                                                                                                                       |
| `fileConfig`     | object | No       | `{}`         | **Deprecated** — file output now lives on the campaign (`Run.fileConfig`, see [File Output Configuration](/operations-reporting/runs#file-output-configuration)); the batch executor no longer reads this column. Still accepted on create/update for backward compatibility (validated when non-empty) but has no effect on generated files. |
| `placements`     | array  | No       | --           | Named slots (see Placement Fields above)                                                                                                                                                                                                                                                                                                      |

***

## API Quick Reference

### Create

```bash theme={null}
POST /api/v1/channels
```

```json theme={null}
{
  "name": "Retail Rewards Mobile",
  "channelType": "in_app",
  "deliveryMode": "api",
  "impressionMode": "explicit",
  "placements": [
    { "name": "home_carousel", "description": "Homepage offer carousel", "slotType": "card", "maxSlots": 5 },
    { "name": "order_screen_upsell", "description": "Add-on suggestion during ordering", "slotType": "card", "maxSlots": 2 }
  ]
}
```

**Response:** `201 Created` with the full channel object.

### List / Update / Delete

```bash theme={null}
GET /api/v1/channels          # List all channels
PUT /api/v1/channels          # Update (send id in body)
DELETE /api/v1/channels?id={id}  # Delete
```

<Warning>
  Deleting a channel also removes all associated placements. Creatives linked to this channel become orphaned and should be reassigned or deleted.
</Warning>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Creatives" icon="palette" href="/studio/creatives">
    Create content variants for each offer on this channel.
  </Card>

  <Card title="Decision Flows" icon="sitemap" href="/decisioning/decision-flows">
    Build pipelines that select and deliver offers through channels.
  </Card>

  <Card title="Composable Pipeline" icon="cubes" href="/data/transforms/composable-pipeline">
    Use the Group node to allocate offers across placements.
  </Card>

  <Card title="Contact Policies" icon="shield" href="/decisioning/contact-policies">
    Set frequency caps and suppression rules per channel.
  </Card>
</CardGroup>
