Ten sales decisions, each one answered by a model that only picks from a fixed list.
A decision model takes a situation and a set of allowed answers, then returns a probability for every answer in one pass. No text generated. Laya answers in roughly 33 milliseconds and costs nothing to self-host.
Inside: all ten decisions with their answer sets already written, which model to run each on, the three ways to run them, and a test sheet to check them against your own data before you trust any of it.
First one running in ten minutes.
AI Driven, Personalized Outreach on LinkedIn: Prosp 💜
Setup: What a Decision Model Is, and Which to Use
A decision model does one thing: takes a situation and a fixed list of allowed answers, and returns a probability for every answer in a single pass.
No text generated. That is why it is fast and cheap enough to run across a whole list.
What this category actually is
Three shipped in 2026. TypeSafe launched Jev on 15 September after two years in stealth, and Convai Innovations released Laya three days later under Apache 2.0. OpenAI entered with the Decisions API at DevDay 2026, handling real-time classification and routing with fixed answer sets.
All three take the situation and the list of allowed answers, and return a probability for every answer in one pass, with no text generated at all.
Why that matters for sales. Most of the work in a sales process is not writing. It is sorting. Is this a buyer or a peer. Is this reply a yes, a not now, or a no. Does this person control budget.
A frontier model can answer those, but it writes a paragraph to do it and charges accordingly. A decision model returns the answer and a confidence number, which is the only part you were going to use.
The category suits agent routing, ticket triage, and other classification jobs that do not need a full response.
Which model to run each decision on
| Jev | Laya | OpenAI Decisions | |
|---|---|---|---|
| Who | TypeSafe | Convai Innovations | OpenAI |
| Deployment | Hosted API | Self-hosted or hosted | API, preview |
| Licence | Closed | Apache 2.0 | Closed |
| Cost | $0.042 per 1M input tokens | $0 self-hosted | See OpenAI pricing |
| Option ceiling | Up to 255 for a choice | Care advised above ~20 | Fixed answer sets |
Laya is a 421M-parameter classifier answering typed questions in a single 33 millisecond forward pass.
The rule of thumb for these ten decisions:
Under 20 answers, data can leave your machine: use a hosted API. Fastest to get running, no infrastructure.
Under 20 answers, data cannot leave your machine: self-host Laya. Download the checkpoints from Hugging Face, roughly 808MB for the English model, and run them on your own hardware.
More than 50 answers, or you need soft accuracy rather than just the top pick: Jev.
Eight of the ten decisions here have fewer than six answers, so almost all of them run comfortably on any of the three.
One compatibility note. Laya's answer payload is schema-identical to Jev's, so the server is a baseUrl change away for anything already written against the hosted API. Prototype hosted, move to self-hosted without rewriting.
Getting one running
Self-hosted Laya, for data that stays on your machine:
pip install "laya[serve]"
LAYA_PRELOAD=1 laya-serve
# binds 0.0.0.0:8000 with the checkpoints resident
# set LAYA_API_KEY to turn on bearer auth
The three checkpoints are laya, laya-multilingual and laya-typed-decisions.
Hosted, if you want something running in ten minutes. Jev is in early access and reachable through gateways including Vercel, OpenRouter and Cloudflare. Both models are also available through Runware's /v1/systemone endpoint, referenced as typesafe:jev@latest and runware:laya@1.
The endpoint and schemas: POST /v1/systemone, with Choice, Noul and Score answer types. Choice picks one from a list. Noul is the yes/no. Score returns a number.
Check current pricing and availability before planning around it. This category shipped in September 2026 and is moving quickly.
The arithmetic, so you can check it
Why running one of these across a whole list is reasonable rather than a stunt.
Laya, self-hosted, 33ms per decision
1,000 connections 33 seconds
10,000 connections 5.5 minutes
25,000 connections 13.75 minutes
Batched and parallelised, considerably faster.
Cost at $0 self-hosted.
Hosted, at $0.042 per 1M input tokens
A connection record is roughly 50 tokens.
25,000 records = 1.25M tokens = about 5 cents.
Run the numbers on your own list before building anything. Those are published figures, not measurements from your data, and your record size and batching will change both.
The point is the order of magnitude. Sorting a whole connection list against your ICP costs minutes and pennies rather than a project. That is what makes decisions 1 and 6 worth doing at all.
Before you trust any of it
The test sheet is on the last page of this guide and it is not optional.
A decision model returns a confident answer whether or not it is right. On a list of 25,000 that is 25,000 confident answers, and you will not read them.
The minimum before any decision goes into production:
1 Label 100 records by hand. Yourself, not a model.
2 Run the decision on the same 100.
3 Count the disagreements.
4 Read every disagreement. The model is sometimes right and
your label was lazy.
5 Set the confidence threshold from what you learn, not from
a default.
Under 85% agreement on a yes/no decision means the answer set is wrong or the question is ambiguous, not that the model is bad.
Decisions 1 to 5: Sorting the List
Five decisions that run across a whole list. These are the ones where speed and cost actually change what is possible.
Each has the question, the answer set already written, the model to use, and the paste-in prompt.
1. Is this new connection someone we sell to?
The one that justifies the whole category. Run it across every connection you have, then across new ones weekly.
TYPE Choice
ANSWERS buyer / influencer / peer / competitor / irrelevant
MODEL Any. Five options, runs anywhere.
INPUT name, headline, company, company size, location
The paste-in prompt:
You are sorting LinkedIn connections against an ICP.
OUR ICP:
[paste: exact job titles that actually signed, company type and
size band, who this is explicitly not for]
For the record below, return exactly one of:
buyer could sign off on this purchase
influencer would be involved but cannot sign
peer same work as us, not a customer
competitor sells something similar
irrelevant outside the ICP entirely
Return the label and your confidence 1 to 10. Nothing else.
Below 7 confidence, return "unsure" instead.
RECORD:
[name, headline, company, size, location]
Why five answers rather than yes/no. Peer and competitor behave completely differently from irrelevant, and you want them separated. A peer is a referral source. A competitor is someone to never pitch. Collapsing both into "no" loses the useful half.
The output is a filtered list, not a verdict. Buyers go into outreach. Influencers go into a nurture list. Peers go into a content audience. That routing is the value.
2. Is this commenter a buyer, a peer or a competitor?
Same shape as decision 1, different input and one extra signal.
TYPE Choice
ANSWERS buyer / influencer / peer / competitor / irrelevant
MODEL Any.
INPUT name, headline, company, THE COMMENT TEXT, post topic
The comment text is the addition that matters. A headline tells you what they do. A comment tells you what they think about the problem, which is a much better signal of whether they have it.
You are sorting people who commented on a LinkedIn post.
OUR ICP:
[paste]
THE POST WAS ABOUT: [topic]
For the record below, return exactly one of:
buyer / influencer / peer / competitor / irrelevant
Weight the COMMENT more heavily than the headline. Someone
whose comment describes having this problem is a buyer even if
their title is ambiguous.
Someone promoting their own competing solution in the comment is
a competitor regardless of their headline.
Return the label and confidence 1 to 10. Below 7, return
"unsure".
RECORD:
[name, headline, company, comment text]
Run this before you import anyone. Post engagers are the highest-intent cold source available, and sorting them before they enter a sequence is the difference between a 60-person warm list and a 400-person cold one.
3. Is this reply interested, not now or no?
The highest-volume decision in any sales process, and the one most worth automating.
TYPE Choice
ANSWERS interested / timing / objection / wrong_person /
not_interested / auto_reply / unclear
MODEL Any. Seven options.
INPUT the reply text, plus the message it replied to
You are classifying a reply to a cold outreach message.
WE SENT: [the message]
THEY REPLIED: [the reply]
Return exactly one of:
interested wants more, or asks about the offer
timing interested but not now. Note any date named.
objection price, fit, incumbent or capacity
wrong_person not their remit
not_interested a clear no
auto_reply out of office or automated
unclear anything you are not confident about
Return the label, your confidence 1 to 10, and the exact
sentence that decided it.
BELOW 8 CONFIDENCE, return "unclear". Sarcasm, brevity and
politeness all read the same in text, and a misread reply sent
under someone's name is theirs to apologise for.
The threshold is 8, not 7. This decision routes to an actual message going out, so the bar is higher than for a list sort.
Never auto-send on not_interested. The classification is the automation. The reply is not.
wrong_person is the most valuable label and the most commonly discarded. Route it to a human who asks who owns it, and feed the answer back into the ICP. One corrected job title is worth more than the deal.
4. Is this company hiring for our buyer?
The strongest budget signal available, and it is sitting in public.
TYPE Noul (yes/no) plus a Choice for the tier
ANSWERS yes / no, then: our_buyer / adjacent / unrelated
MODEL Any.
INPUT job title, posting body, posting date, company
You are checking whether a job posting signals a buying window.
OUR OFFER SUPPORTS OR REPLACES: [the role or function]
For the posting below, return exactly one of:
our_buyer hiring for the role our offer supports or
replaces, so there is budget and an unmet need
adjacent hiring in the same function but not the role
unrelated neither
ALSO return the posting age in days.
If the posting is over 45 days old, return "stale" regardless of
the match. Either it is filled or the budget moved.
If the posting appears to be a repost, flag it. A repost means
the first attempt failed, which is a stronger signal than a new
posting.
RETURN the label, the age, the repost flag, and confidence.
POSTING:
[title, body, date, company]
The repost flag is the output worth building for. A company reposting has a live need, allocated budget, and recent evidence their own process is not working. Nothing else on a public page combines those three.
5. Did this company just raise money?
TYPE Choice
ANSWERS raised_recent / raised_old / no_raise / unclear
MODEL Any.
INPUT company name, recent news or announcement text, date
You are checking whether a company has recently raised capital.
For the text below, return exactly one of:
raised_recent a funding event within the last 90 days
raised_old a funding event over 90 days ago
no_raise no funding event mentioned
unclear ambiguous or unsourced
RETURN the label, the date of the event if stated, and
confidence.
If no date is stated, return "unclear" rather than guessing.
An undated funding claim is not a signal.
TEXT:
[announcement or news item]
The 90 day window is the point. Thirty to ninety days post-announcement is when the money is being allocated. Before that they are still celebrating; after that it is committed.
One rule for whatever you write next: reference what they said they would spend it on, never the amount. Naming the figure reads as scraping a database.
This signal does not apply to every buyer type. A funding round moves a VC-backed company and means nothing to a bootstrapped one. Check the company type before weighting it.
Decisions 6 to 10: Routing and Pipeline
Five decisions that run on individual records rather than whole lists. Routing, qualification and pipeline hygiene.
Lower volume, higher consequence, so the confidence thresholds are higher.
6. Is this lead already in the CRM?
The unglamorous one that prevents the most embarrassment.
TYPE Choice
ANSWERS exact_match / likely_match / different_person /
same_company_different_person / unclear
MODEL Any.
INPUT the new record, plus candidate matches from the CRM
You are checking whether a new lead already exists in the CRM.
NEW RECORD: [name, company, title, linkedin_url]
CANDIDATE MATCHES: [the CRM records that might be the same]
Return exactly one of:
exact_match same person, same company
likely_match same person, company changed
same_company_different_person different contact there
different_person not a match
unclear cannot tell
RETURN the label, the matching record ID if any, and confidence.
BELOW 8 CONFIDENCE, return "unclear" and route to a human. A
wrong merge is harder to undo than a duplicate.
Treat a LinkedIn URL match as decisive. Treat a name match alone
as never decisive.
likely_match is the useful label. Someone who moved companies is not a duplicate, they are a warm lead with a new budget and ninety days to show a change. Flagging them as a dedupe hit loses the best signal in your database.
Never auto-merge. Flag, route, let a person decide. A merged record with history is a loss you cannot undo.
7. Call or nurture email for this inbound lead?
TYPE Choice
ANSWERS call_now / call_scheduled / nurture / disqualify
MODEL Any.
INPUT the form fill, the page they came from, company data
You are routing an inbound lead.
OUR ICP: [paste]
OUR PRICE BAND: [paste]
For the lead below, return exactly one of:
call_now in ICP, can afford it, and arrived from a
high-intent page (pricing, demo, contact)
call_scheduled in ICP but arrived from a content page
nurture adjacent fit, or no buying signal yet
disqualify fails the ICP or the price band
RETURN the label, the reason in under 12 words, and confidence.
Weight the SOURCE PAGE heavily. Someone who filled a form on a
pricing page is a different lead from someone who downloaded a
guide, even with identical job titles.
LEAD:
[form fields, source page, company size, industry]
Speed is the whole point of this decision. An inbound lead routed in seconds rather than at the next pipeline review is a materially different conversation, because they are still at their desk thinking about it.
disqualify needs a human glance before anything is sent. Automated routing is fine. Automated rejection is not.
8. Can this person sign off the budget?
TYPE Choice
ANSWERS economic_buyer / influencer / champion / gatekeeper /
unknown
MODEL Any.
INPUT title, seniority, company size, anything they have said
You are assessing whether a contact can approve a purchase.
OUR PRICE BAND: [paste]
For the contact below, return exactly one of:
economic_buyer can approve spend at our price band
influencer shapes the decision, cannot approve
champion advocates internally, cannot approve
gatekeeper controls access, not the decision
unknown insufficient evidence
RETURN the label, the evidence, and confidence.
DEFAULT TO "unknown". Authority is not reliably inferable from a
title, and a confident wrong answer here produces a forecast
built on someone who cannot buy.
A VP at a 40-person company usually can. A VP at a 4,000-person
company usually cannot. Weight company size as heavily as title.
CONTACT:
[title, seniority, company size, what they have said]
This is the decision most likely to be wrong, which is why it defaults to unknown. Four UNKNOWNs in a deal is not a late-stage deal regardless of how long it has been open.
Use it to flag single-threading, not to score the deal. A deal where the only contact is a champion needs a second thread, and that is the action this decision should trigger.
9. Is this deal going quiet?
The most predictive pipeline signal and the one nobody surfaces.
TYPE Choice
ANSWERS healthy / slowing / stalled / dead
MODEL Any.
INPUT days since last contact, who initiated it, stage,
whether a next step has a date
You are assessing deal momentum.
For the deal below, return exactly one of:
healthy movement in the last 7 days, next step has a date
slowing 8 to 14 days, or the next step has no date
stalled 15 to 30 days, or we have initiated the last three
contacts
dead over 30 days with no response
RETURN the label, the days since last movement, and the single
question that would unstick it.
WEIGHT "who initiated" heavily. Three consecutive contacts
initiated by us is a stalled deal regardless of how friendly the
replies were.
DEAL:
[last contact date, initiator, stage, next step and its date]
"Who initiated the last three contacts" is the field that makes this work. A deal where the rep chases and the buyer responds politely looks alive in the CRM and is not.
Run it weekly across the whole pipeline. The output is a list of deals to do something about, which is a better Monday than a forecast review.
10. Which rep owns this lead?
TYPE Choice
ANSWERS one option per rep, plus unassigned
MODEL Any. Jev if you have more than 20 reps, since its
choice ceiling is 255.
INPUT the lead record, plus the ownership rules
You are assigning a lead to a rep.
OWNERSHIP RULES, in priority order:
1 Existing relationship. If any rep has contacted this company
in the last 90 days, it is theirs.
2 Named account. If the company is on a rep's named list.
3 Territory. [your rules]
4 Segment. [your rules]
5 Round robin among available reps.
For the lead below, return exactly one rep name, or
"unassigned" if no rule applies cleanly.
RETURN the rep, WHICH RULE DECIDED IT, and confidence.
If two rules conflict, return "unassigned" and name the
conflict. Never pick between two reps with a claim.
LEAD:
[company, size, location, segment, prior contact history]
Returning which rule decided it is the part that prevents arguments. A routing decision nobody can audit becomes a routing decision everybody disputes.
Conflicts go to a human, always. Two reps with a claim on the same account is a management decision, and a model picking silently is how you lose a rep.
The confidence thresholds, collected
| Decision | Threshold | Below it |
|---|---|---|
| 1. New connection | 7 | unsure, no outreach |
| 2. Commenter | 7 | unsure, no outreach |
| 3. Reply | 8 | unclear, human reads it |
| 4. Hiring signal | 7 | drop the row |
| 5. Funding | 8 | unclear, no signal claimed |
| 6. CRM match | 8 | unclear, never auto-merge |
| 7. Inbound routing | 7 | nurture, the safe default |
| 8. Budget authority | 8 | unknown, which is fine |
| 9. Deal momentum | 7 | flag for review |
| 10. Rep assignment | 9 | unassigned, human decides |
The threshold rises with the consequence. A misread connection costs one wasted row. A misassigned account costs a rep relationship, so decision 10 sits at 9.
Set these from your own test data, not from this table. The numbers here are a starting point for the first run, and the test sheet tells you where they actually belong.
The Three Ways to Run Them, and the Test Sheet
Each decision runs three ways. Start with the prompt, because it needs nothing installed and it tells you whether the answer set is right.
Only build the agent once the prompt works. Only build the workflow once the agent works.
Way one: the prompt
Paste it into Claude or ChatGPT. Nothing installed, nothing configured.
What it is for: checking the answer set is right before you build anything. If the labels are wrong, the agent and the workflow are wrong too, and they take longer to fix.
How to use it properly:
1 Take 20 real records from your own data.
2 Label them yourself first. Write the labels down.
3 Paste the prompt, then the 20 records.
4 Compare.
5 Read every disagreement. Half the time your label was lazy.
If you disagree on more than three of twenty, the answer set is wrong, not the model. Usually it means two labels overlap, or there is a case the set does not cover.
The commonest fix is adding an explicit escape label. Every decision in this guide has one: unsure, unclear, unknown or unassigned. A forced choice between five wrong options produces confident nonsense.
Way two: the agent
Sorts a whole list. Save to .claude/skills/.
---
name: decision-runner
description: Run a decision across a whole list and return a
sorted file. Use when you have more than 20 records.
---
INPUT: a CSV, and which decision to run.
PER ROW:
1 Apply the decision's answer set exactly. No labels outside it.
2 Return the label, the confidence, and the evidence.
3 Below the threshold, return the escape label.
OUTPUT a new CSV with three added columns: label, confidence,
evidence. Sorted by label, then confidence descending.
ALSO return a summary:
Count per label
How many fell below the threshold
The five lowest-confidence rows, in full, for a human to read
THE LAST ONE IS NOT OPTIONAL. The lowest-confidence rows are
where the answer set is failing, and reading five of them tells
you more than the counts do.
NEVER write a label outside the defined set, even for a row that
fits none of them. That is what the escape label is for.
NEVER drop a row. A row you cannot classify still appears in the
output, with the escape label.
Run it on 100 rows before you run it on 25,000. The summary tells you whether the thresholds are set right, and fixing them after a full run means running it again.
Way three: the workflow
Runs automatically, on a trigger or a schedule.
The shape, in any workflow tool:
TRIGGER new row, new reply, webhook, or a schedule
↓
FETCH pull the record and whatever context the decision
needs
↓
DECIDE POST to /v1/systemone with the question, the answer
set, and the state
↓
BRANCH route on the returned label
↓
BELOW a separate branch for anything under the threshold.
THRESHOLD It goes to a human, not to the default path.
↓
WRITE update the record with the label, the confidence and
the timestamp
↓
LOG append to a decisions log, always
The two branches people leave out:
The below-threshold branch. Without it, a low-confidence answer takes whichever path the label points at, which defeats the threshold entirely.
The log. Every decision, with its input, its label and its confidence. When someone asks why a lead was routed to the wrong rep in March, that file is the answer. Without it the system is unauditable and nobody will trust it by month three.
Never let a workflow send a message. Classify, route, draft, and stop. A human presses send on anything that reaches a person.
The test sheet
Build this once per decision, before it goes anywhere near production.
COLUMNS
record_id
the input, in full
my_label you, by hand, before seeing the model
model_label
model_confidence
agree yes or no
notes only on disagreements
ROWS: 100 minimum. Drawn randomly, not cherry-picked.
The process:
1 Label all 100 yourself FIRST. Do not look at the model
output while labelling.
2 Run the decision.
3 Fill in the agree column.
4 Read every disagreement and write a note.
5 Count how many disagreements were the model being right.
Reading the result:
| Agreement | What it means |
|---|---|
| Over 90% | Ready. Set the threshold at the lowest confidence that was still correct. |
| 85 to 90% | Usable with a human reviewing anything below threshold. |
| Under 85% | The answer set is wrong or the question is ambiguous. Fix the set, not the model. |
Step 5 is the one people skip and the most useful. On a well-written decision, a meaningful share of disagreements are cases where your own label was careless. That is worth knowing before you conclude the model is unreliable.
Re-run the sheet quarterly. Your ICP drifts, your market changes, and a decision calibrated in January is answering a slightly different question by June.
Setup for non-coders
Nothing here needs a developer. The order matters more than the tooling.
TEN MINUTES
Pick decision 1 or decision 3. Both are high volume and
immediately useful.
Paste the prompt into Claude or ChatGPT.
Run it on 20 of your own records.
Compare against your own labels.
ONE HOUR
Build the test sheet. 100 rows.
Set the threshold from what you find.
ONE AFTERNOON
Get an API key from a hosted provider, or self-host Laya if
your data cannot leave your machine.
Run the decision across 100 rows through the API.
Check the output matches what the prompt produced.
ONE DAY
Build the workflow. Trigger, fetch, decide, branch, write, log.
Include the below-threshold branch and the log.
Run it on a small list first.
Do not start with the workflow. A workflow built on an untested answer set runs a wrong decision reliably, at volume, which is worse than not running it.
Do not run all ten. Pick one, get it working end to end, then add the second. Ten decisions all half-configured is a system nobody trusts.
Where the sending happens
None of these ten decisions send anything. They sort, route and flag.
Decisions 1, 2 and 3 feed directly into outreach, and that half runs on Prosp: post engagers imported straight from a post URL, the sequence built on a canvas with native branching on reply, and a unified inbox where decision 3 runs over one queue rather than several logins.
The constraint that governs how many of your sorted leads you can actually contact: roughly 20 connection requests a day and 100 messages a week, account-wide rather than per campaign. A decision model that returns 435 warm leads from 25,000 has done its job. Working through 435 at 20 a day is five months, which is why decisions 1 and 2 matter more than the volume you started with.
The decisions sort them. Prosp reaches them 💜