Skip to main content

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: 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).

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 and the propensity scores from 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:
  • 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.
  • exploredtrue 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:
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.

6. When to use what

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