Next-best-action decisioning
Orbit ships next-best-product (recommendations) and next-best-channel — decisioning closes the third gap: next-best-action. Given one profile’s candidate actions — each a message variant on a channel at a send-time, with the trials and conversions you have already observed for it — a multi-armed bandit picks the action most likely to convert that profile next and returns the full scored breakdown. The Decisioning tester in the dashboard exercises it interactively; the same decision is exposed programmatically on the CDP API. Decisioning only decides — it never sends. Rendering the chosen variant and dispatching the message belongs to the caller (a campaign or an orchestrator you operate), so the policy can be tried, tuned, and measured without any outbound traffic.Section 1 — How decisioning works
Every request carries its own state — the engine derives the pick from the counts in the request body and stores nothing:- Candidate arms — the actions eligible for this profile right now. Each arm is a variant × channel × optional send-hour combination with its observed
trials(sends or impressions) andsuccesses(conversions). A request scores at most 50 arms. - Policy — the bandit strategy that balances exploiting the current best arm against exploring under-sampled ones. Three policies are available:
- Tuning (optional) — an explore probability for
epsilon_greedy, an uncertainty multiplier and beta prior forbayes_ucb. Leave any of them unset and the engine applies its defaults. - Eligibility gates (optional) — a decision only picks among what the profile may actually receive:
- A per-arm eligible switch removes an arm from selection while keeping it in the scored breakdown — the way to pass through an upstream suppression without losing the comparison.
- An eligible-channel allowlist restricts the pick to channels the profile can be contacted on.
- A Profile id (a contact id or an external CDP profile id) resolves the profile’s own constraints — channels it has opted out of, and channels where a frequency-cap slot is exhausted — and every arm on a suppressed channel is scored but never chosen.
- A target send hour (0–23 local) decides for one specific hour: arms pinned to a different hour are held out of selection, while anytime arms stay eligible.
Section 2 — The Decisioning tester
The tester (Audience → Decisioning in the dashboard) runs the policy end to end against your own counts — nothing you enter is sent to a customer or persisted into the CDP.- Enter the candidate arms. One row per action: variant id, channel (picked from the live channel list, so a typo can’t split your conversion stats), an optional send hour, observed trials and successes, and the eligible switch.
- Pick a policy. Start with Bayesian UCB; switch to UCB1 or epsilon-greedy to see how the pick moves when the explore/exploit balance changes.
- Optionally scope the decision. A Profile id ties the run to a stored profile — the tester resolves the id as you type, warns when the value could never match a profile (an email, a phone number, or free text), and applies the profile’s opt-outs and exhausted frequency-cap slots as per-channel exclusions. An eligible-channel allowlist and a target send hour narrow the pick the same way.
- Optionally tune the policy. Blank fields keep the engine defaults.
- Decide. The result names the chosen arm — its variant, channel, and recommended send time — and lists the full scored breakdown below.
Reading the scored breakdown
Every arm appears in ranked order with the numbers behind the ranking — this is how you explain a decision rather than merely read it:- Posterior mean is the arm’s estimated conversion rate given its counts — a 1,200-trial arm at 7% is trusted far more than a 3-trial arm at 33%.
- Score is the policy’s ranking value. Under
bayes_ucbit exceeds the posterior mean by the uncertainty bonus, so a thin-data arm can outrank a proven one until it is sampled; an em-dash marks a value the policy cannot express (such as an unplayed arm). - Chosen / Explored flags tell you the pick and whether the policy took it by exploiting the leader or deliberately exploring.
- Excluded badges attribute every held-out arm to the gate that held it out — the eligible switch, the channel allowlist, the target send hour, or a suppression the resolved Profile id applied — so a gate that quietly matched nothing reads as such instead of passing for a working filter.
- The per-hour schedule — shown when arms carry send hours and no target hour is set — lists the best eligible arm for each candidate hour, so an arm’s send time is a visible decision input, not a label.
Section 3 — Using decisioning with campaigns
The tester is the interactive front door; the same decision is available programmatically atPOST /api/v1/cdp/decisioning/decide with the same inputs and response shape (snake_case field names, full parameter detail in the CDP API reference). It is role-gated to owner, admin, or developer, like the other CDP compute surfaces, and rate-limited to 60 requests per minute per tenant.
Two properties shape how you integrate it:
- You keep the counts. The surface is stateless: roll conversions up from your campaign and CDP event data and pass each arm’s current
trialsandsuccessesper request. A campaign orchestrator calls it once per profile per send decision, then renders and dispatches the chosen action through the normal send path. - The profile is the unit. One request decides for one profile against the arms you supply — the same pick for the same counts, every time, under the deterministic policies.
Decisioning vs A/B testing
They answer different questions and complement each other rather than compete:- An A/B test answers “which message is better for this audience?” — it splits a campaign’s audience across up to four fixed variants, holds the assignment constant, and produces a per-variant result you can defend with a sample size. Use it when the question is about the creative and you need a winner you can prove.
- Decisioning answers “which action is best for this profile right now?” — it picks among variant × channel × send-time arms per individual, shifting traffic toward the current leader automatically as counts accumulate. Use it when the question is about routing each profile to its best action and you want the mix to adapt without a manual winner call.
Section 4 — Guardrails: when decisioning is not appropriate
Scope the bandit deliberately — it is a decision primitive, not a hosted optimization service:- You need volume per arm. A bandit adapts as evidence arrives, but it cannot manufacture evidence: with near-zero trials on every arm there is nothing to exploit and the pick jitters between under-sampled arms. Establish a baseline — an A/B test or a fixed rollout — before letting a policy allocate traffic.
- You need explainable creative comparisons, use A/B. A bandit’s pick has no sample-size guarantee and no significance claim. When leadership needs “variant B beat variant A by 12% with 95% confidence,” that answer comes from an A/B test, not from a bandit breakdown.
- Non-negotiable content does not belong in an arm. Regulatory or legal copy, transactional confirmations, and other must-send-uniform messages have no variant to choose among — decisioning only makes sense where alternatives genuinely exist.
- Counts must be honest. The engine trusts the
trials/successesyou pass. Numbers mixed across different audiences, time windows, or conversion definitions produce a confident pick on an incomparable basis; keep one measurement definition per arm set. - Per-request, not batch. One call decides for one profile. It is not a pre-computed assignment table you can look up later — the orchestrator asks at decision time.
Cross-references
- Next-best-product recommendations — the sibling affinity ranker for catalog items.
- A/B test a campaign — fixed-variant experiments when you need a provable winner.
- Campaign lifecycle — the send path the chosen action dispatches through.
- CDP API reference — full request and response detail for
decisioning/decide.