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

# AI decisioning: next-best-action per profile

> Pick the next-best message variant, channel, and send hour for one profile with a conversion bandit — feed in candidate actions, apply consent and timing eligibility, and read the full scored breakdown.

# AI decisioning

AI decisioning answers one question per profile: given the actions you could take right now, which one is most likely to convert? You pass the candidate actions — each a message variant on a channel, optionally pinned to a send hour, with its observed results so far — and the decision endpoint returns the best pick plus every candidate's score. This guide covers what the engine decides, what you feed it, how to read the output, the dashboard tester, and when decisioning is the right tool versus a campaign-triggered flow or a plain segment.

The same endpoint backs the dashboard page at **Audience → Decisioning**. The UI and the API run the same decision.

## 1. What AI decisioning is

Each call to `POST /api/v1/cdp/decisioning/decide` makes one decision for one profile: which **action** to take next. An action is the combination of three things:

* **Variant** — which message variant to send (`subject_a`, `winback_v2`).
* **Channel** — where to send it (email, SMS, WhatsApp, push).
* **Send hour** — when to send it, as a local hour 0–23 (optional; an action with no hour is available at any time).

The engine scores every candidate action with a conversion bandit and returns the winner. Because it balances exploiting the current best performer against exploring under-sampled options, the pick keeps improving as each send's outcome feeds the next call's counts.

Three decisioning siblings answer different questions — pick the one that matches yours:

| Surface           | Question                                                                           | Endpoint                                |
| ----------------- | ---------------------------------------------------------------------------------- | --------------------------------------- |
| AI decisioning    | Which **action** (variant × channel × time) for this profile?                      | `POST /api/v1/cdp/decisioning/decide`   |
| Offer decisioning | Which **offer or promo** is this contact eligible for, and is it worth its budget? | `POST /api/v1/cdp/offers/decide`        |
| Recommendations   | Which **products** will this profile buy next?                                     | `POST /api/v1/cdp/recommendations/rank` |

Offer decisioning solves eligibility — active windows, per-contact caps, budgets, exclusivity groups, segment and cart gates — and can hold out a control cohort so you measure incremental ROI. Use it when the question is "which promotion", and AI decisioning when the question is "which variant, on which channel, at what time". Recommendations ranks a product catalog, not message actions (see [Decision output vs. the recommendations list](#3-decision-output-vs-the-recommendations-list)).

## 2. Inputs the engine consumes

A decision request carries three kinds of input, all resolved by you before the call — the engine itself is stateless and stores nothing:

1. **Candidate actions** (`variants`, 1–50 per call) — each with a `variant_id`, a `channel`, an optional `send_time_hour`, and the evidence that action has accumulated: `trials` (sends or impressions) and `successes` (conversions). Build the candidates from your campaign setup; roll up the counts from your own event data — a conversion is an event you already track in the CDP, so the counts come out of your segments, computed traits, and event rollups rather than a new stored model.
2. **Eligibility gates** — an action marked `eligible: false` is never chosen, and an `eligible_channels` list restricts the decision to channels the profile can actually receive on. Resolve consent and frequency-cap status for the profile first (the decisioning tester in the dashboard does this automatically — see the walkthrough below), then pass the allowlist so a suppressed channel is never the winning action.
3. **Decision context** — a `target_send_hour` (when you intend to send; actions pinned to a different hour drop out of contention), a `policy` (`bayes_ucb` default, `ucb1`, or `epsilon_greedy`), and optional tuning knobs (`epsilon`, `exploration_z`, `prior_alpha`, `prior_beta`). Most callers leave all of these at the defaults.

Decisioning runs after your audiences and scores exist — it decides over actions for the profiles you already identified, it does not identify them. Reach it from the segments you built in [CDP audiences](/guides/cdp-segments) and the propensity scores from [CDP predictive models](/guides/cdp-predictive-models); segment membership tells you *who* to contact, decisioning tells you *how*.

## 3. Decision output vs. the recommendations list

The response gives you the pick and the transparency to audit it:

```json theme={null}
{
  "profile_id": "cnt_9f3d21",
  "policy": "bayes_ucb",
  "chosen": {
    "variant_id": "subject_b",
    "channel": "email",
    "send_time_hour": 18,
    "trials": 1100,
    "successes": 99,
    "posterior_mean": 0.089,
    "score": 0.112,
    "eligible": true
  },
  "explored": true,
  "eligible_count": 3,
  "total_trials": 2600,
  "scores": [ /* every action, ranked by score descending */ ],
  "default_policy": "bayes_ucb"
}
```

* `chosen` — the action to take, or `null` when nothing is eligible (for example a profile opted out on every channel). Always handle `null`: the correct action then is to not send.
* `explored` — `true` when the bandit passed over the current best performer to gather evidence on a less-proven action. Expect occasional explore picks; that is how the engine discovers a new winner instead of locking onto an early leader.
* `scores` — the full ranking with each action's estimated conversion rate (`posterior_mean`) and ranking score, so you can see *why* the winner won and what it beat.
* `eligible_count` / `total_trials` — how many actions were genuinely in contention and how much evidence the decision rested on.

All three policies rank on the same evidence — conversions per trial — and differ only in how they trade exploitation against exploration. `bayes_ucb` (the default) is deterministic and reproducible; `ucb1` forces untried actions first; `epsilon_greedy` explores a fixed share of the time.

**How this differs from `POST /api/v1/cdp/recommendations/rank`:** recommendations answers "which products" — it ranks a product catalog by a profile's affinities and returns a list (a top-N) with a per-factor breakdown, for personalizing a catalog or product grid. AI decisioning answers "which action" — it picks one winner per profile across variant, channel, and timing, and returns the audit breakdown around that single pick. A typical flow uses both: decide the action and channel here, then fill the message with ranked products from recommendations.

## 4. Dashboard walkthrough

Open **Audience → Decisioning** to run a decision interactively. The tester posts to the same endpoint, so what you see is what an integration gets.

1. **Define the candidate actions.** Each row is one action: a variant id, a channel from the channel picker, an optional send hour, and the trials and successes observed so far. Add or remove rows to match your campaign's variants.
2. **Choose the policy.** The policy selector defaults to `bayes_ucb`; the advanced fields (`epsilon`, `exploration_z`, priors) map to the optional request parameters and can usually stay blank.
3. **Narrow by profile and timing (optional).** Enter a **Profile id** to resolve a real contact: the tester reads that profile's consent and frequency-cap status and automatically holds out actions on channels the contact opted out of or where a frequency cap is exhausted — each exclusion is labelled with its reason on the result card. Set a **Target send hour** to decide for a specific hour; actions pinned to a different hour are excluded from the pick.
4. **Run the decision.** The result shows the chosen action, whether the pick was an exploration, and the full scored table. When you set no target hour, the **send-hour schedule** panel shows the best action per send hour — the same timing gate the engine applies for a target hour, run across every candidate hour — so you can read a full day's schedule in one view.

The page is role-gated in the UI, mirroring the API: only owner, admin, and developer roles can run decisions.

## 5. API usage

Decide the next-best action for one profile:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/cdp/decisioning/decide \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_id": "cnt_9f3d21",
    "eligible_channels": ["email", "sms"],
    "target_send_hour": 18,
    "variants": [
      { "variant_id": "subject_a",  "channel": "email", "send_time_hour": 9,  "trials": 1200, "successes": 84 },
      { "variant_id": "subject_b",  "channel": "email", "send_time_hour": 18, "trials": 1100, "successes": 99 },
      { "variant_id": "winback_v2", "channel": "sms",   "send_time_hour": null, "trials": 300, "successes": 21 }
    ]
  }'
```

With `target_send_hour: 18`, `subject_a` (pinned to hour 9) is still scored and reported but cannot be chosen; the pick falls between the hour-18 email variant and the any-time SMS variant.

To keep a campaign learning, call the endpoint once per profile per send, then record the outcome and fold it into that action's `trials`/`successes` for the next call. If the profile opted out of SMS since the last decision, drop `sms` from `eligible_channels` — excluded actions still appear in `scores` with `eligible: false`, so the audit trail survives the suppression.

Limits and gates:

* **Rate limit:** 60 requests per minute per tenant, matching the other CDP compute endpoints.
* **Roles:** owner, admin, or developer — decisioning drives outbound spend, so it shares the computed-traits gate.
* **Payload bounds:** 1–50 actions per call; `successes` never exceeds `trials` (higher values are clamped).
* **Stateless:** nothing is persisted and nothing is sent. The endpoint only decides — delivery still happens through your campaign, and outbound voice and SMS dispatch stays on the Devotel softswitch.

For the full request/response schema, see the [CDP API reference](/api-reference/endpoints/cdp).

## 6. When to use what

| Need                                                                              | Reach for                                              | Why                                                                                                     |
| --------------------------------------------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
| Pick one variant, channel, and send time **for one profile right now**            | AI decisioning                                         | Learns per profile from conversion evidence and audits every pick.                                      |
| Run one message over a **whole audience on a schedule**                           | [Campaign end-to-end](/guides/campaign-end-to-end)     | A campaign-triggered flow executes a fixed message over a segment; it doesn't learn per profile.        |
| Define **who** is in the audience at all                                          | [CDP audiences](/guides/cdp-segments)                  | Computed-trait segments group profiles by attributes and behavior; decisioning then decides per member. |
| Score profiles by **churn, conversion intent, or lifetime value** before you act  | [CDP predictive models](/guides/cdp-predictive-models) | Scores prioritize whom to contact; decisioning picks the action for the contacted profile.              |
| Pick **which promotion** a contact qualifies for, with budget and holdout control | Offer decisioning (`/api/v1/cdp/offers/decide`)        | Solves offer eligibility and measures incremental ROI with a control cohort.                            |
| Rank **which products** to put in front of a profile                              | Recommendations (`/api/v1/cdp/recommendations/rank`)   | Catalog affinity ranking, not action selection.                                                         |

A composed example: build a churn-risk audience with the predictive models, let decisioning pick the variant/channel/hour per member, hold the chosen variant out when a frequency cap blocks the channel, and send through your campaign.

## See also

* [CDP audiences](/guides/cdp-segments) — segments, computed traits, and activation
* [CDP predictive models](/guides/cdp-predictive-models) — churn, conversion intent, and value scores that feed your candidate pool
* [Campaign end-to-end](/guides/campaign-end-to-end) — executing the action decisioning picks
* [Frequency caps](/guides/frequency-caps) — the cap status that feeds channel eligibility
* [CDP API reference](/api-reference/endpoints/cdp) — full decisioning, offer, and recommendations endpoint contracts
