Decisions

Ask structured questions about a piece of text - which team, how frustrated, is it true - and get typed answers back instead of prose. For routing tickets, scoring feedback and checking emails or forms.

8 min read · Updated 28 September 2026

Decisions are for the questions you ask about the same kind of text again and again: which team should handle this ticket, how upset is this customer, does this email ask for a refund. Paste the text, write the questions, and each one comes back in the shape you set: one of your options, a level on your scale, or a value between false and true. You get answers you can act on, not paragraphs to read.

Choice and score answers show how sure the model is and how the probability spread across your options, so you can see at a glance whether a call was clear-cut or close. Decisions run on Jev 1.13 by TypeSafe AI, a decision model: instead of writing prose, it answers your questions in the shapes you define.

Question types

Decisions lives under Apps, on the card that reads Classify, score and check text with structured questions. One decision can ask several questions about the same text, and each question has one of three types:

TypeWhat you writeWhat you get back
ChoiceA question and its options. Each option has a name and a When it applies description.The option the model picked, a confidence, and the probability of every option.
ScoreA question and its levels, from lowest to highest, each described in a sentence. The levels are numbered 0, 1, 2 and so on.The level, a score on that scale such as 1.9 / 2, a confidence, and the probability of every level.
True / falseA statement, plus optional criteria for when it holds and when it doesn’t.A value from 0 (false) to 1 (true), such as 0.97.

Make a decision

  1. Start a new decision

    Go to Apps and choose Decisions. The page lists your past decisions; select New decision.

  2. Paste the text

    Under TEXT, paste what you want to check: a support ticket, a review, an email, a form submission.

  3. Add your questions

    Under QUESTIONS, add a question with Choice, Score or True / false.

    • Choice. Write the question, for example Which team should handle this? Give each option a name and describe When it applies. Add option adds another.
    • Score. Write the question, for example How frustrated is the customer? Describe each level, from lowest to highest. Add level adds one, and the up and down arrows reorder them.
    • True / false. Write the statement, for example The customer is threatening to cancel. Optionally, fill in the Criteria, answered from 0 (false) to 1 (true): TRUE for when it holds (Mentions a prior attempt or ticket) and FALSE for when it doesn’t.
  4. Check the model

    The MODEL panel shows the model that will answer. Jev 1.13 by TypeSafe AI is the default.

  5. Run it

    Select Run decision. The answers appear in the ANSWERS panel. To try a different wording, edit the text or the questions and select Run again.

Example: triage a support ticket

This is the run in the recording above. Northwind’s support team gets a message like this one from a customer, Marta:

Hi, I ordered a merino throw (order NW-4471) and it still hasn't arrived.
This is the second order from you that has gone missing. I'm seriously
thinking about cancelling my membership. - Marta

Three questions about it:

QuestionTypeOptions or levelsAnswer
Which team should handle this?Choicebilling: Payments, refunds and invoices. shipping: Delivery, tracking and missing parcels. product: Questions about our products.shipping, 100% confidence
How frustrated is the customer?Score0 Calm, 1 Frustrated but civil, 2 Angry, ready to leaveAngry, ready to leave: 1.9 / 2, 79% confidence (0%, 14% and 86% for the three levels)
The customer is threatening to cancelTrue / falseNo criteria0.97

Each answer maps to an action: the ticket goes to the shipping queue, and the frustration score and the cancellation flag mark it for a fast, careful reply before Marta leaves.

Read the answers

  • Choice. The option the model picked, with a confidence such as 100% confidence, and every option’s probability as a bar with a percentage. The bars show how close the call was: a near-even split between two options is worth a human look, a clear winner usually isn’t.
  • Score. The level the text landed on, a score on your scale such as 1.9 / 2, a confidence such as 79% confidence, and each level’s probability. The score can fall between two levels, so it is useful for sorting as well as for labelling.
  • True / false. A value between 0 and 1 on a bar from False to True. The closer to 1, the more clearly the statement holds; the closer to 0, the more clearly it doesn’t.

Saved decisions

Every decision is saved with its text, its questions and its answers. The Decisions page lists the decisions you have made in a table with Text, Model, Status and Created columns, and New decision starts another.

A saved decision is a record of exactly what was asked and what came back, which helps when someone later asks why a ticket went where it did.

Guardrails and access

Decisions follow the same workspace rules as chat:

  • Guardrails. Your workspace’s guardrails check the text and every question and description before the model sees them. Depending on how they are set up, sensitive details are redacted or the run is blocked.
  • Model access and data residency. Your workspace’s model access and data residency settings apply to Jev like to any other model. If they rule it out for you, you can’t run a decision with it.

From the API

Decisions have their own REST endpoints, so you can ask the same questions about every new ticket, review or email from your own code. All paths are relative to https://api.stickyprompts.com/api/v1, and every request sends your API key as a bearer token in the Authorization header. New to the API? Start with the Quickstart.

The key needs these permissions (in the app’s key editor they read Decision Get, Decision Manage and Decision Delete):

Decisions are not available as an MCP tool; use the REST endpoints.

List the decision models

GET /api/v1/decision_model
Permission decision_get

Returns the decision models as a JSON array (trimmed):

[
  {
    "ID": 1,
    "name": "Jev 1.13",
    "selectable": true
  }
]

Pass the model’s ID as decision_model_id. Each model also has version, powered_by, description and default. As with chat models, selectable: false means your workspace’s model access or data-residency rules block the model for you, and restriction_reason says why. The examples below use 1, the ID Jev 1.13 had when this page was written; look it up rather than hard-coding it.

Run a decision

POST /api/v1/decision

Runs the decision synchronously - the call returns when the answers are ready - and saves it with your other decisions.

Body
decision_model_id integer required
Model ID from GET /decision_model.
state string required
The text to check.
questions object required
At least one question, each under a key you choose, such as team or urgent. The answers come back under the same keys.

Each question has a type, its instructions (the question or statement) and its criteria:

typeIn the appcriteria
choiceChoiceRequired. An object that maps each option name to when it applies.
scoreScoreRequired. An array of level descriptions, lowest first. The first is level 0.
noulTrue / falseOptional. An object with a true key (when it holds), a false key (when it doesn’t), or both. No other keys are allowed.

This request asks one choice question and one true / false question:

curl -X POST https://api.stickyprompts.com/api/v1/decision \
  -H "Authorization: Bearer $STICKY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "decision_model_id": 1,
    "state": "My parcel never arrived and the tracking link is broken. Second time this month!",
    "questions": {
      "team": {
        "type": "choice",
        "instructions": "Which team should handle this?",
        "criteria": {
          "billing": "Payments and refunds",
          "shipping": "Delivery and tracking"
        }
      },
      "urgent": {
        "type": "noul",
        "instructions": "The customer needs a fast reply"
      }
    }
  }'
import os
import requests

BASE = "https://api.stickyprompts.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['STICKY_API_KEY']}"}

r = requests.post(
    f"{BASE}/decision",
    headers=HEADERS,
    json={
        "decision_model_id": 1,
        "state": "My parcel never arrived and the tracking link is broken. Second time this month!",
        "questions": {
            "team": {
                "type": "choice",
                "instructions": "Which team should handle this?",
                "criteria": {
                    "billing": "Payments and refunds",
                    "shipping": "Delivery and tracking",
                },
            },
            "urgent": {
                "type": "noul",
                "instructions": "The customer needs a fast reply",
            },
        },
    },
)
r.raise_for_status()
decision = r.json()
if decision["status"] != "done":
    raise RuntimeError(decision["error"])
answers = decision["answers"]
print(answers["team"]["choice"], answers["urgent"]["noul"])
const BASE = "https://api.stickyprompts.com/api/v1";
const headers = { Authorization: `Bearer ${process.env.STICKY_API_KEY}` };

const res = await fetch(`${BASE}/decision`, {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({
    decision_model_id: 1,
    state: "My parcel never arrived and the tracking link is broken. Second time this month!",
    questions: {
      team: {
        type: "choice",
        instructions: "Which team should handle this?",
        criteria: {
          billing: "Payments and refunds",
          shipping: "Delivery and tracking",
        },
      },
      urgent: {
        type: "noul",
        instructions: "The customer needs a fast reply",
      },
    },
  }),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const decision = await res.json();
if (decision.status !== "done") throw new Error(decision.error);
console.log(decision.answers.team.choice, decision.answers.urgent.noul);

The response is 201 with the saved decision. Trimmed, with an illustrative ID and slug:

{
  "ID": 27,
  "slug": "q4m8x2kd",
  "decision_model_id": 1,
  "state": "My parcel never arrived and the tracking link is broken. Second time this month!",
  "status": "done",
  "error": "",
  "answers": {
    "team": {
      "type": "choice",
      "choice": "shipping",
      "confidence": 1,
      "probabilities": { "billing": 0, "shipping": 1 }
    },
    "urgent": {
      "type": "noul",
      "noul": 0.87
    }
  }
}

The response also returns questions as they were sent to the model (after any guardrail redaction), the embedded decision_model, and resolved_model, the exact model version that answered.

Score questions

A score question sends its levels as an array, lowest first:

{
  "frustration": {
    "type": "score",
    "instructions": "How frustrated is the customer?",
    "criteria": ["Calm", "Frustrated but civil", "Angry, ready to leave"]
  }
}

Its answer has the score, a confidence, a legend that maps each level number to your description, and each level’s probabilities, keyed by level number. The shape looks like this (values illustrative):

{
  "frustration": {
    "type": "score",
    "score": 1,
    "confidence": 1,
    "legend": { "0": "Calm", "1": "Frustrated but civil", "2": "Angry, ready to leave" },
    "probabilities": { "0": 0, "1": 1, "2": 0 }
  }
}

When a decision fails

  • 400 - state is missing, a question is invalid (no instructions, a choice or score without criteria, noul criteria with keys other than true and false, or an unknown type), or decision_model_id doesn’t match a model.
  • 400 with a code - a guardrail blocked the text or a question.
  • 403 - your workspace’s model access rules restrict the model.
  • 429 - the workspace’s quota or plan limit is reached. See Errors, pagination and quotas.

List, read and delete decisions

GET /api/v1/decision
Permission decision_get

Lists your decisions, newest first. Supports page and size, plus status (processing, done or error) to filter. The total is in the X-Total-Count header - see pagination.

GET /api/v1/decision/:id
Permission decision_get

One decision by ID or slug, with its text, questions and answers. A decision that doesn’t exist or isn’t yours returns 404 {"error":"Decision not found"}.

DELETE /api/v1/decision/:id

Deletes a decision by its numeric ID. Returns 204, also when nothing matched.

Ideas

  • Route tickets. A Choice question with your teams as options sends each ticket to the right queue.
  • Score review sentiment. A Score question from Unhappy to Delighted turns a pile of reviews into a number you can sort and chart.
  • Check emails and forms for a condition. A True / false statement such as The email asks for a refund or The form includes an order number flags the ones that need action.
  • Ask all three at once. One decision can route, score and check the same text, as in the example above.