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

# API Tutorial

> Complete guide to the Recommend and Respond APIs — authentication, request/response formats, decision traces, experiments, and computed values.

## Overview

This tutorial covers everything you need to integrate with KaireonAI's two core APIs:

* **Recommend API** — Get personalized offer recommendations for a customer
* **Respond API** — Record customer interactions (impressions, clicks, conversions)

## Prerequisites

Before starting, ensure you have:

* A running KaireonAI instance (local or `https://playground.kaireonai.com`)
* At least one active Decision Flow with Offers, channels, and Creatives configured
* Your tenant ID (found in **Settings > General**)
* An API key (generated in **Settings > API Explorer**)

## Authentication

All API requests require two headers:

| Header        | Description                                          |
| ------------- | ---------------------------------------------------- |
| `x-tenant-id` | Your tenant identifier                               |
| `x-api-key`   | Your API key (hashed with HMAC-SHA256 on the server) |

```bash theme={null}
curl -H "x-tenant-id: your-tenant-id" \
     -H "x-api-key: your-api-key" \
     https://your-instance.com/api/v1/recommend
```

<Note>
  The Recommend and Respond APIs are the public **data plane** — any `krn_`
  API key can call them, including default keys minted without scopes.
  Management endpoints (the **control plane**) require a first-party session,
  an MCP connection, or a key minted with the `control-plane` scope; see
  [API Keys](/api-reference/api-keys).
</Note>

<Warning>
  API keys are hashed on the server using HMAC-SHA256 with the `API_KEY_PEPPER` environment variable. Store your API key securely — it cannot be retrieved after creation.
</Warning>

## Base URL

| Environment       | Base URL                                  |
| ----------------- | ----------------------------------------- |
| Local development | `http://localhost:3000/api/v1`            |
| Playground        | `https://playground.kaireonai.com/api/v1` |
| Production        | Your custom domain + `/api/v1`            |

***

## Recommend API

The Recommend API is the primary decisioning endpoint. It executes a Decision Flow for a specific customer and returns ranked, personalized offers.

### Endpoint

```
POST /api/v1/recommend
```

### Request Body

```json theme={null}
{
  "customerId": "SBX-000042",
  "decisionFlowKey": "retail-rewards-decision-pipeline",
  "limit": 3,
  "channel": "email",
  "placement": "hero_offer",
  "attributes": {
    "tier": "gold",
    "source": "campaign_q1"
  }
}
```

| Field             | Type   | Required | Description                                                                      |
| ----------------- | ------ | -------- | -------------------------------------------------------------------------------- |
| `customerId`      | string | Yes      | The customer to generate recommendations for                                     |
| `decisionFlowKey` | string | No       | Decision flow key (or name) to execute — uses the routed/default flow if omitted |
| `limit`           | number | No       | Maximum offers to return (default: 5)                                            |
| `channel`         | string | No       | Filter by delivery channel                                                       |
| `placement`       | string | No       | Named placement slot within the channel                                          |
| `attributes`      | object | No       | Request-time attributes available as `attributes.*` in formulas                  |

Every POST response includes the pipeline funnel counts in `meta` — there is no separate debug flag to enable them on this endpoint.

### Response

```json theme={null}
{
  "recommendationId": "rec_abc123",
  "customerId": "SBX-000042",
  "decisionFlowKey": "retail-rewards-decision-pipeline",
  "count": 3,
  "decisions": [
    {
      "offerId": "...",
      "offerName": "Retail Rewards: Discount — 10 Day High",
      "rank": 1,
      "score": 0.89,
      "creativeId": "...",
      "channelType": "email",
      "content": {
        "subject": "Special offer just for you",
        "body": "<p>Save on your next visit</p>"
      },
      "personalization": {}
    },
    {
      "offerId": "...",
      "offerName": "Retail Rewards: BOGO — 5 Day Hard",
      "rank": 2,
      "score": 0.74,
      "creativeId": "...",
      "channelType": "email",
      "content": {
        "subject": "Buy one, get one free",
        "body": "<p>Bring a friend this weekend</p>"
      },
      "personalization": {}
    }
  ],
  "meta": {
    "totalCandidates": 60,
    "afterQualification": 45,
    "afterContactPolicy": 42,
    "degradedScoring": false
  },
  "timestamp": "2026-03-10T14:30:00Z"
}
```

### Response Fields

| Field                         | Description                                                                                                                   |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `decisions[]`                 | Ordered array of recommended offers (highest score first)                                                                     |
| `decisions[].score`           | Final composite score after ranking                                                                                           |
| `decisions[].content`         | Rendered creative content with personalization variables resolved                                                             |
| `decisions[].personalization` | Computed values from the Compute stage                                                                                        |
| `recommendationId`            | Always returned (equal to `interactionId`). Use it to fetch the decision trace and to attribute outcomes via the Respond API. |

Once you receive recommendations, record how customers interact with them using the Respond API.

***

## Respond API

The Respond API records customer interactions with recommendations. KaireonAI feeds this data back into behavioral metrics, contact policies, and experiment analysis.

### Endpoint

```
POST /api/v1/respond
```

### Request Body

```json theme={null}
{
  "customerId": "cust_12345",
  "recommendationId": "trace_abc123",
  "rank": 1,
  "outcome": "click",
  "idempotencyKey": "idem_click_cust12345_abc123",
  "outcomeDetails": {
    "clickPosition": "hero_banner",
    "deviceType": "mobile"
  }
}
```

| Field              | Type   | Required    | Description                                                                                                                 |
| ------------------ | ------ | ----------- | --------------------------------------------------------------------------------------------------------------------------- |
| `customerId`       | string | Yes         | The customer who interacted                                                                                                 |
| `outcome`          | string | Yes         | The type of interaction (or `interactionType`) — see [Outcome Types](/studio/outcome-types)                                 |
| `creativeId`       | string | Conditional | Identifies the creative directly. Provide **either** `creativeId` **or** `recommendationId` + `rank` to identify the offer. |
| `recommendationId` | string | Conditional | Links back to the original recommendation; with `rank`, it resolves the offer/creative/channel for you.                     |
| `rank`             | number | No          | Which offer from the recommendation (1-based); used with `recommendationId`.                                                |
| `channelId`        | string | No          | The channel the interaction occurred on (resolved automatically when using `recommendationId` + `rank`).                    |
| `idempotencyKey`   | string | No          | Unique key to prevent duplicate recordings (or send the `Idempotency-Key` header).                                          |
| `outcomeDetails`   | object | No          | Arbitrary key-value data attached to the interaction.                                                                       |

### Response

```json theme={null}
{
  "interactionId": "int_xyz789",
  "recommendationId": "trace_abc123",
  "customerId": "cust_12345",
  "outcome": "click",
  "classification": "positive",
  "rank": 1,
  "offerName": "Retail Rewards: Discount — 10 Day High",
  "creativeName": "Discount email — hero",
  "channelName": "Email",
  "status": "recorded",
  "timestamp": "2026-03-10T15:45:00Z"
}
```

<Info>
  `outcome` echoes the key you recorded (e.g. `click`); `classification` is the
  outcome type's sentiment — `positive`, `negative`, or `neutral`. A repeat call
  with the same idempotency key returns `status: "already_recorded"`.
</Info>

### Common Outcome Types

| Type             | When to Record                                                      |
| ---------------- | ------------------------------------------------------------------- |
| `impression`     | Customer was shown the recommendation                               |
| `click`          | Customer clicked/tapped the recommendation                          |
| `conversion`     | Customer completed the desired action (purchase, application, etc.) |
| `dismiss`        | Customer explicitly dismissed the recommendation                    |
| `not_interested` | Customer indicated disinterest                                      |
| `opt_out`        | Customer opted out of this type of communication                    |

***

## Advanced Topics

### Decision Traces

KaireonAI records a forensic trace of the entire decision process for each POST recommendation (subject to the tenant's trace sample rate). Retrieve it with the `recommendationId` from the response:

```bash theme={null}
GET /api/v1/decision-traces/:recommendationId
```

<Note>
  Decision traces are a **control-plane** endpoint: a default data-plane-only
  API key gets `403` here. Use a browser session or a key minted with the
  `control-plane` scope (see [API Keys](/api-reference/api-keys)).
</Note>

**Trace response:**

```json theme={null}
{
  "recommendationId": "trace_abc123",
  "customerId": "cust_12345",
  "decisionFlowId": "df_credit_cards",
  "stages": [
    {
      "name": "enrich",
      "duration": 12,
      "input": { "customerId": "cust_12345" },
      "output": { "customer.loyalty_score": 85, "customer.credit_score": 720 }
    },
    {
      "name": "compute",
      "duration": 3,
      "computed": {
        "act_platinum_cc": { "personalized_rate": 17.49 },
        "act_cashback": { "personalized_rate": 18.24 }
      }
    },
    {
      "name": "filter",
      "duration": 5,
      "candidatesIn": 15,
      "candidatesOut": 8,
      "filtered": ["act_home_loan (segment_required)", "act_auto_loan (propensity < 0.4)"]
    },
    {
      "name": "score",
      "duration": 8,
      "scores": {
        "act_platinum_cc": 0.89,
        "act_cashback": 0.74
      }
    },
    {
      "name": "rank",
      "duration": 2,
      "finalOrder": ["act_platinum_cc", "act_cashback"]
    }
  ],
  "totalDuration": 30,
  "processedAt": "2026-03-10T14:30:00Z"
}
```

<Tip>
  Decision traces are invaluable for debugging why a specific offer was or was not recommended. Enable tracing in staging environments and at a sample rate in production (configurable in **Settings > General**).
</Tip>

### Experiments

When an A/B experiment is active, KaireonAI automatically assigns customers to control or variant groups. The response surfaces the assignment at the top level via `experimentVariant` and `controlGroup`, and each decision reflects the assigned creative:

```json theme={null}
{
  "experimentVariant": "variant_a",
  "controlGroup": false,
  "decisions": [
    {
      "offerId": "act_platinum_cc",
      "creativeId": "treat_platinum_email_variant_a",
      "rank": 1,
      "score": 0.89
    }
  ]
}
```

Track experiment results in **Algorithms > Experiments**, where KaireonAI calculates:

* Conversion rate per variant
* Uplift (percentage improvement over control)
* Statistical significance (z-test)
* Confidence interval

### Computed Values in Responses

The `personalization` field in each recommendation contains the evaluated computed values from the Decision Flow's Compute stage. These values are calculated per customer using:

* **Offer custom fields** — `base_rate`, `annual_fee`, etc.
* **Enriched customer data** — `customer.loyalty_score`, `customer.credit_score`
* **Request-time attributes** — `attributes.tier`, `attributes.device`

```json theme={null}
{
  "personalization": {
    "personalized_rate": 17.49,
    "annual_fee_waiver": true,
    "bonus_points": 5000
  }
}
```

See [Computed Values](/tutorials/computed-values) for formula syntax and configuration details.

***

## Error Handling

### Common Error Responses

| Status | Code                          | Description                                                                                              |
| ------ | ----------------------------- | -------------------------------------------------------------------------------------------------------- |
| 400    | `"VALIDATION_ERROR"`          | Invalid request body (missing required fields, wrong types)                                              |
| 401    | `"UNAUTHORIZED"`              | Missing or invalid API key                                                                               |
| 403    | `"FORBIDDEN"`                 | Tenant does not have access to the requested resource                                                    |
| 404    | `"NOT_FOUND"`                 | Decision Flow, customer, or Offer not found                                                              |
| 429    | `"PLAYGROUND_QUOTA_EXCEEDED"` | Playground decision limit reached (see [Licensing & Deployment Options](/governance-security/licensing)) |
| 500    | `"INTERNAL_ERROR"`            | Server error — contact support                                                                           |

**Error response format:**

```json theme={null}
{
  "error": "Validation failed",
  "code": "VALIDATION_ERROR",
  "details": [
    { "field": "customerId", "message": "customerId is required" }
  ]
}
```

***

## Rate Limiting

KaireonAI protects the platform with a sliding-window rate limit of **1,000 requests per minute** per tenant. Playground tenants are limited to **100 requests per minute**.

When rate-limited, the API returns:

```
HTTP 429 Too Many Requests
Retry-After: 12
```

<Info>
  Rate limits apply per tenant, not per API key. Multiple API keys for the same tenant share the same rate limit window.
</Info>

## What's Next?

<CardGroup cols={2}>
  <Card title="Platform Walkthrough" icon="play" href="/get-started/walkthrough">
    Follow the end-to-end setup guide with hands-on examples.
  </Card>

  <Card title="Decision Flows" icon="diagram-project" href="/decisioning/decision-flows">
    Learn how the decisioning pipeline works under the hood.
  </Card>

  <Card title="Formula Reference" icon="function" href="/tutorials/formula-reference">
    Complete syntax reference for computed value formulas.
  </Card>

  <Card title="Behavioral Metrics" icon="chart-bar" href="/studio/behavioral-metrics">
    Build dynamic rules from interaction history.
  </Card>

  <Card title="Sample Data" icon="flask" href="/get-started/sample-data">
    Load demo data to test the APIs with realistic content.
  </Card>

  <Card title="MCP Quickstart" icon="plug" href="/tutorials/mcp-quickstart">
    Connect your AI IDE to query and configure KaireonAI.
  </Card>
</CardGroup>
