Tracking trends and conversation insights

Use the Bloobability v2 decide endpoint to judge conversations against your own policies with the instant or full model, estimate how often each policy applies, and track how those rates move over time.

The Bloobability decide endpoint answers one question per policy: how likely is it that this policy holds for this conversation? You send a conversation and the policies you care about, such as "customer asked for a refund," "customer is at risk of churning," or "agent resolved the issue," and get back a calibrated probability for each one.

That single primitive is enough to build two things:

  • Conversation insights: tag what happened in any one conversation, for routing, review queues, or a live dashboard.
  • Trends: measure how often a policy applies across many conversations, and watch that rate move week over week.

Note

This is a v4 feature under the AI tag. All requests go to https://api.blooio.com/v4. Make sure the version toggle at the top of the docs is set to v4.

How it works

Every result carries two calibrated numbers between 0 and 1:

  • probability: the policy holds for this conversation.
  • answerable: the conversation contains enough to judge the policy at all. A low answerable never changes probability; it tells you how much the conversation had to go on.

There is no verdict and no built-in threshold, and neither model points to the messages behind a result. The call is stateless: nothing is stored, there's no idempotency key, and replaying a request re-runs inference. You bring the conversations; Blooio judges them.

Choose a tier

Every request names one of two models with tier. Nothing routes between them automatically, nothing falls back from one to the other, and the response's tier always echoes your choice.

instant full
Best for Short, explicit questions where the conversation states the deciding fact Reversals ("actually, cancel that"), conditions, several speakers, long threads, and conversations that may be missing a fact
Server time, short thread, 1 / 6 policies 8.2 / 24.9 ms 25.8 / 83.9 ms
Accuracy on 900 fresh public questions 77.9% 88.8%
Flags missing information No: caught 0 of 60 deliberately unanswerable cases Yes: caught 60 of 60 in the same screen

Server times are medians measured on the RTX 4090 that serves both tiers, for a four-message thread sent one request at a time. They exclude the internet and the public API in front of the model, and the two tiers take turns on one GPU, so the time you see per request is higher; they are not a latency guarantee.

When unsure, use full. Choose by how hard each question is, not by how many you ask: one policy that depends on a reversal can need full, while several simple, stated-fact policies run well on instant. On instant, read answerable as a weak hint only; it does not reliably tell "unlikely" apart from "the conversation never said."

Step 1: Define your policies

A policy is an id (the stable key you'll track over time) and a definition (a plain-language statement that is true or false of the conversation). There's nothing to register: any definition works. Write definitions the way you'd brief a new support lead: plain, specific, one idea each.

[
  { "id": "refund_request", "definition": "The customer currently asks for their money back." },
  { "id": "churn_risk", "definition": "The customer signals they may cancel or stop using the product." },
  { "id": "positive_sentiment", "definition": "The customer expresses satisfaction or praise." }
]

The model reads only the definition; the id is echoed back unchanged so you can join results to your own records.

Step 2: Decide a conversation

Send your tier, the messages in chronological order, and your policies. Every message needs a unique id and non-blank text. The other message fields are optional, and you should send them only when they're real:

  • role is free text: customer, agent, manager, tool, or your own.
  • speaker_id tells apart two people who share a role, such as two agents.
  • timestamp is RFC 3339 with an offset, and must not decrease from one message to the next.

Add a top-level assessment_time when a policy depends on "now" (for example, "the customer has waited more than a day").

curl -X POST https://api.blooio.com/v4/ai/bloobability/v2/decide \
  -H "Authorization: Bearer YOUR_BLOOIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tier": "full",
    "messages": [
      { "id": "m1", "role": "customer", "speaker_id": "c1", "text": "This is the third time it broke. I want my money back." },
      { "id": "m2", "role": "agent", "speaker_id": "a1", "text": "I am sorry. Let me pull up your order." }
    ],
    "policies": [
      { "id": "refund_request", "definition": "The customer currently asks for their money back." },
      { "id": "churn_risk", "definition": "The customer signals they may cancel or stop using the product." }
    ]
  }'
Try it

The response returns one result per policy, in the same order you sent them:

{
  "tier": "full",
  "model_version": "bloobability-v2-full-392ad04431a8",
  "policies": [
    { "id": "refund_request", "probability": 0.97, "answerable": 0.99 },
    { "id": "churn_risk", "probability": 0.41, "answerable": 0.72 }
  ]
}

The probabilities above are illustrative.

Reading a result

Field What it tells you
probability The answer. The estimated probability that the policy holds. This is the number to store and aggregate.
answerable How well the conversation supports judging the policy. On full, use it to separate "unlikely" from "can't tell."
tier The model that answered, always the one you asked for.
model_version The exact weights and calibration that produced the numbers. Store it alongside them.

If your application needs a yes/no, choose the threshold yourself.

Step 3: Estimate a rate across many conversations

To answer "what share of this week's conversations were refund requests?", decide each conversation and average the probabilities for that policy across the cohort.

Warning

Average probabilities rather than counting conversations above a threshold. A threshold count throws away everything below the line, so it drifts with wherever you put the line. The average uses every conversation's probability, not just the ones above it.

const BLOO_API_KEY = process.env.BLOO_API_KEY;

const POLICIES = [
  { id: "refund_request", definition: "The customer currently asks for their money back." },
  { id: "churn_risk", definition: "The customer signals they may cancel or stop using the product." },
];

async function decide(messages) {
  const res = await fetch("https://api.blooio.com/v4/ai/bloobability/v2/decide", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${BLOO_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ tier: "full", messages, policies: POLICIES }),
  });
  if (!res.ok) throw new Error(`decide failed: ${res.status}`);
  return res.json();
}

// conversations: [{ id, messages: [{ id, role, text }] }, ...]
async function policyRates(conversations) {
  const totals = Object.fromEntries(POLICIES.map((p) => [p.id, 0]));

  for (const convo of conversations) {
    const { policies } = await decide(convo.messages);
    for (const r of policies) totals[r.id] += r.probability;
  }

  const n = conversations.length || 1;
  return Object.fromEntries(
    Object.entries(totals).map(([id, sum]) => [id, sum / n]),
  );
}

// { refund_request: 0.18, churn_risk: 0.07 }: the estimated share of the cohort.

Each rate is the estimated fraction of conversations in the cohort where that policy applies. Store these per bucket (day, week, agent, segment) and you have the raw material for trends.

Step 4: Track a trend over time

Bucket your conversations by period, run policyRates on each bucket, and compare. The result is a time series of prevalence you can chart or alert on.

// buckets: [{ label: "2026-W01", conversations: [...] }, ...]
async function trend(buckets) {
  const series = [];
  for (const bucket of buckets) {
    series.push({ label: bucket.label, rates: await policyRates(bucket.conversations) });
  }
  return series;
}

To keep a trend meaningful, hold three things constant:

  • Same policy definitions. Use the identical definition in every bucket. A wording change is a new measurement, not a continuation of the old one.
  • Same model. Keep one tier for the whole series and check that model_version stays the same. The two tiers are different models and don't return identical probabilities. Blooio's own Trends feature uses full.
  • Comparable cohorts. Compare like with like (same channel, segment, or funnel stage) so a shift in the mix doesn't masquerade as a shift in behavior.

Warning

Trust direction more than exact magnitude. Calibration was fitted on research data and isn't guaranteed to hold identically for every cohort, policy wording, or future period. Treat "refund rate is up" as the reliable signal, and an exact point change as an estimate.

Step 5: Route the hard cases to a human

For per-conversation actions (auto-tagging, routing, closing tickets), choose your own thresholds and send everything uncertain to review:

  • A high probability with a high answerable is the strongest case for acting.
  • A middling probability, or a low answerable on full, is a good candidate for review.
  • Use full for anything you'd act on without review. instant is fast, but it misses more reversals and doesn't flag missing facts.

The endpoint gives you numbers, not decisions: it doesn't certify any threshold as safe for automation, so validate yours on your own conversations before acting without review.

Errors

Errors use the standard v4 shape, { "error": { "code", "message", "details" } }. details never echoes your conversation or policy text, and 5xx errors carry none.

Status Meaning
400 The body doesn't match the contract: an unknown tier, unknown or null fields, blank strings, text that isn't valid Unicode, duplicate keys or IDs, or out-of-order timestamps. details names the failing reason, path, and rule.
413 The body is over 1 MiB.
422 The conversation and policies don't fit the chosen tier's context. Nothing is truncated: shorten the thread or send fewer policies per request.
429 / 503 The model is busy or restarting (queue_full, queue_timeout, model_unavailable). Retry with backoff.
503 This request made the model fail (inference_timeout, gpu_memory_exhausted). Retrying it unchanged won't help: shorten the thread or send fewer policies.
502 / 504 The model host gave a bad answer or didn't answer in time. Retry later.

A request can carry up to 128 policies and 2,048 messages. Those are transport limits, not a promise that the conversation fits the model.

Limitations

Warning

Know these before you build on the scores:

  • Domain. Built for English, text-only customer conversations. Attachments aren't read, and other languages or genres are unmeasured.
  • Calibrated, not perfect. A probability of 0.9 does not mean "correct exactly nine times in ten" for every policy and cohort. Both tiers are overconfident on sentences built to trick word-overlap shortcuts: on one such public set, 36 of full's 233 answers at 90% or more were wrong. The numbers are most reliable in aggregate, for rates and trends.
  • instant doesn't detect missing information. Its answerable flagged none of the 60 deliberately unanswerable cases in our latest screen.
  • Model-derived labels. Scores reflect a model's judgment of your definition, not human-verified ground truth. Where that judgment is systematically off, the scores will be off the same way.

Recap

  • Define stable policies (id + definition) and keep the wording fixed over time.
  • Decide conversations with POST /v4/ai/bloobability/v2/decide; read probability and answerable.
  • Pick full unless your questions are short and explicit and speed matters most; then instant.
  • Estimate rates by averaging probabilities, not by counting conversations over a threshold.
  • Track trends with one fixed tier, identical definitions, and comparable cohorts; trust direction over magnitude.
  • Pick your own thresholds, and send uncertain cases to review.

See the complete field reference on the Decide policies against a conversation endpoint page, and the benchmarks behind each tier on the Bloobability v2 model page.