Jev brand crisis detectionalert on the spike, not the mention
Score every incoming mention for how damaging it is and how far it will travel, keep a rolling baseline of what a normal fifteen minutes looks like for your brand, and alert when the current window breaks away from that baseline. One angry customer is not a crisis. Forty of them in an hour, when you normally get two, is. Only the second thing should wake anyone up.
Last updated: September 2026
Jev decides. An LLM writes. OpenTweet publishes.
What Jev is and is not doing here
Jev has no crawler and no memory between calls. It reads text you send and returns typed answers about it. You collect the mentions, you hold the baseline, and you own every number in the alert rule. What changed in September 2026 is that the per-mention judgement now costs a fraction of a cent at TypeSafe's published price and, per TypeSafe, returns in 70ms to 500ms, so you can afford to run it on all of them instead of a sample.Why a severity threshold does not work
Almost every first attempt is a rule like "alert when severity is above 3.5". It fails in both directions at once, and it fails worst on the day it matters.
| Situation | Fixed severity threshold | Baseline plus spike |
|---|---|---|
| A quiet brand gets its first severe mention | Fires. Correct, by luck. | Fires. The baseline is near zero, so one is a large deviation. |
| A brand that averages 2 severe mentions a day | Fires twice a day, every day. Muted within a week. | Silent. Two is normal here. |
| That same brand suddenly gets 60 in an hour | Sends the same alert it sent yesterday. | Fires hard, with the deviation in the payload. |
| A hundred mildly annoyed mentions, none severe | Silent. Nothing crossed the line. | Fires. The load moved even though no single mention did. |
| Normal Monday traffic, twice the weekend volume | Fires more on Mondays for no reason. | Silent, if the baseline is bucketed by hour of week. |
The underlying problem is that a threshold has no idea what normal is. It is a statement about one mention, and a crisis is not a property of one mention. It is a property of the rate. Two severe mentions on a Tuesday afternoon are a support queue. The same two arriving in four minutes alongside thirty mild ones, from accounts that have never mentioned you before, are the first four minutes of something else.
A threshold also cannot see the fourth row in that table, which is the one that catches people out. A hundred people calmly reporting the same billing bug never crosses a severity line, and it is still the thing you needed to know about before it reached anyone with a large following.
Keep a floor under the deviation
The opposite failure is a detector that fires on statistical noise. A brand with a baseline of 0.1 will produce an enormous deviation the moment two people complain. That is why the rule below requires a high robust z-score and a minimum absolute load and a minimum mention count before it alerts. All three, not any of them.How it is built
Seven steps. Jev owns exactly one of them.
Collect mentions and hand them to Jev at ingest
Wherever your mentions come from, score each one as it lands rather than on a nightly batch. At the 70ms to 500ms TypeSafe states, this is affordable inline, and a baseline built from stale data cannot detect anything.
Score severity and reach risk, plus the escalator flags
Two Scores and four Nouls in a single request. Severity is how bad it is, reach risk is how far it travels, and the Nouls carry the things that change the response rather than the grade.
Turn each scored mention into one number in your own code
Combine the scores with the things Jev cannot know, like follower counts and whether this author has complained before. The weights are yours and they belong in one reviewable file.
Sum the numbers into fixed time windows
Fifteen minutes is a good starting bucket for social. Short enough to catch a fast-moving thread, long enough that a quiet brand is not all noise.
Hold a rolling baseline and compare against it
Median and median absolute deviation over the trailing day, bucketed by hour of week if your volume has a weekly shape. Not a mean and a standard deviation, because past crises live in that history.
Alert on the deviation, with an absolute floor underneath it
A large deviation from a baseline of almost nothing is still almost nothing. Require both a high robust z-score and a minimum load and a minimum count before anything pages a human.
Draft the response, review it, and publish it
Jev cannot write. A person or an LLM writes the statement, you score the draft before it ships, and OpenTweet publishes it to X, Bluesky and LinkedIn in one call.
1. The questions you ask about each mention
Two Scores carry the grade, four Nouls carry the things that change the response. All six go in one request, because Jev evaluates every declared question in a single parallel pass.
import { TypeSafeClient, noul, score } from '@typesafe-ai/sdk';
const client = new TypeSafeClient({
timeout: 4000,
retry: { maxRetries: 1, backoffInitialMs: 200 },
});
const QUESTIONS = {
severity: score('How much damage this mention does to the brand it names if it is left unanswered', [
'None. Neutral or positive, or not really about the brand.',
'Low. A mild grumble with no specifics.',
'Medium. A concrete complaint about the product, stated in public.',
'High. A concrete claim about money lost, data lost, or a paid feature that is broken.',
'Severe. Fraud, discrimination, safety, a legal threat, or a demand that others leave.',
]),
reach_risk: score('How likely this text is to be screenshotted, quoted and repeated by strangers', [
'Inert. Nobody outside the conversation would care.',
'Low. Interesting to existing customers only.',
'Medium. A clean story a stranger could understand without context.',
'High. Has a quotable line, a number, or a named person in it.',
'Extreme. Reads like a headline on its own.',
]),
is_about_us: noul('This text is about the brand named in brand_name, and not a different company with a similar name'),
factual_claim: noul('The text makes a specific factual claim that could be checked and could be wrong'),
legal_or_regulatory: noul('The text mentions lawyers, a regulator, a lawsuit, or a formal complaint'),
calls_for_action: noul('The text asks other people to cancel, boycott, switch, or report the brand'),
};
export async function scoreMention(mention) {
const { answers, usage } = await client.systemOne({
state: {
brand_name: 'OpenTweet',
mention_text: mention.text,
platform: mention.platform,
is_reply: mention.isReply,
},
questions: QUESTIONS,
});
return {
severity: answers.severity.score,
severityConfidence: answers.severity.confidence,
reachRisk: answers.reach_risk.score,
isAboutUs: answers.is_about_us.noul,
factualClaim: answers.factual_claim.noul,
legal: answers.legal_or_regulatory.noul,
callsForAction: answers.calls_for_action.noul,
inputTokens: usage.input_tokens,
};
}Reach risk is a judgement about the text, not about the account
Jev cannot look up a follower count and cannot reliably compare two numbers, because TypeSafe documents counting and date arithmetic as unreliable and treats dates as text. Soreach_risk asks only whether the words are the kind of thing strangers repeat. The actual audience size is a number you already have, and it gets multiplied in code in the next step.is_about_us earns its place immediately. Brand monitoring queries collect a surprising amount of text about a different company with a similar name, and one Noul removes it before it ever reaches the baseline.
2. One mention becomes one number
The model gives you judgements. The weights that turn judgements into a load figure are a product decision, so they live in your code, in one file, where they can be reviewed and changed.
const RELEVANCE_FLOOR = 0.5;
// Follower counts come from your own data, not from Jev. The model reads text;
// it does not count, and TypeSafe documents arithmetic as unreliable.
function audienceWeight(followers: number): number {
return 1 + Math.log10(Math.max(followers, 1)) / 5;
}
export function mentionLoad(scored, followers: number): number {
if (scored.isAboutUs < RELEVANCE_FLOOR) return 0;
const severity = scored.severity / 4;
const reach = 1 + scored.reachRisk / 4;
const escalators = 1 + 0.5 * scored.legal + 0.5 * scored.callsForAction;
return severity * reach * escalators * audienceWeight(followers);
}The logarithm on follower count is there because audience size is not linear in consequence. An account with a million followers is not a thousand times the problem of an account with a thousand. Whatever shape you choose, choose it deliberately and write it down, because this function is the thing you will be arguing about after the first false alarm.
3. The baseline and the spike
This part contains no AI at all. It is the part that decides whether the alert is worth having, and it is about twenty lines.
const WINDOW_MINUTES = 15;
const BASELINE_WINDOWS = 96; // 24 hours of 15-minute buckets
const Z_ALERT = 4.5;
const MIN_MENTIONS = 5;
const MIN_LOAD = 3;
function median(values: number[]): number {
const s = [...values].sort((a, b) => a - b);
const mid = Math.floor(s.length / 2);
return s.length % 2 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
}
// Median absolute deviation instead of a standard deviation, because the
// history a crisis detector learns from contains previous crises, and one of
// those inflates a standard deviation enough to hide the next one.
function robustZ(current: number, history: number[]): number {
const m = median(history);
const mad = median(history.map((v) => Math.abs(v - m)));
const scale = mad > 0 ? mad * 1.4826 : 0.5;
return (current - m) / scale;
}
export function evaluateWindow(current, history) {
if (history.length < BASELINE_WINDOWS / 4) return { alert: false, reason: 'baseline_too_short' };
if (current.count < MIN_MENTIONS) return { alert: false, reason: 'too_few_mentions' };
if (current.load < MIN_LOAD) return { alert: false, reason: 'below_absolute_floor' };
const z = robustZ(current.load, history.map((w) => w.load));
return {
alert: z >= Z_ALERT,
z: Number(z.toFixed(2)),
load: Number(current.load.toFixed(2)),
baseline: Number(median(history.map((w) => w.load)).toFixed(2)),
count: current.count,
};
}- Median, not mean. The history a crisis detector learns from contains previous crises. One of them inflates a standard deviation enough to hide the next one, which is the failure mode where the detector gets quieter every time it is proven right.
Z_ALERTis a dial, not a constant. Start high, watch it for a fortnight against what a human would have flagged, then lower it. A crisis detector that cries wolf is turned off within a month and then you have nothing.- Bucket by hour of week if your traffic has a shape. Comparing a Monday morning against a trailing 24 hours that is mostly Sunday night manufactures a spike every week.
- Alert once per incident. Hold a cooldown after a fire, or a real crisis sends you forty pages while you are trying to write the response to it.
4. What the alert carries
An alert that says "something is wrong" costs you the first ten minutes. Send the deviation, the baseline it broke, and the mentions that drove it.
{
"alert": true,
"window": "2026-09-18T14:30:00Z/PT15M",
"z": 9.4,
"load": 41.8,
"baseline": 2.6,
"count": 37,
"top_drivers": [
{ "mention_id": "a91f", "severity": 3.9, "reach_risk": 3.6, "legal": 0.94 },
{ "mention_id": "b02c", "severity": 3.7, "reach_risk": 3.9, "calls_for_action": 0.91 }
],
"dominant_aspect": "billing"
}dominant_aspect comes from running aspect Scores alongside the severity one, which costs nothing extra because they ride in the same request. The sentiment page has the aspect pattern in full. Knowing the spike is about billing rather than about uptime is most of the work of deciding who to wake up.
Why scoring at ingest is the thing that makes this work
The latency is not a nice-to-have here. It is the architecture.
Scored at ingest
- Every mention carries its scores the moment it is stored.
- The current window is always complete, so the comparison is against live data.
- The alert fires while the thread is still small enough to answer.
Scored in a batch job
- Mentions wait for the next run before they have a grade.
- The window you compare against is as stale as the batch interval.
- You find out about the spike after everyone else did.
TypeSafe states end-to-end response times of 70ms to 500ms for Jev, against a stated LLM baseline of 3 to 329 seconds, in its launch post. The only independent figure published so far is Mike Taylor's at Every: 777 separate judgements returned in under 0.7 seconds for an estimated quarter of a cent. In the same test Jev caught 6 of 7 planted defects where Claude Fable 5.1 caught 7 of 7, which is the honest shape of the trade and the reason a confidence gate belongs in front of anything irreversible.
What it costs to score everything
At $0.042 per 1M input tokens with output unmetered, the bill is the volume of text you send and nothing else.
| Volume | Input tokens per day at ~300 each | Per day | Per 30 days |
|---|---|---|---|
| 5,000 mentions a day | 1.5M | $0.06 | $1.89 |
| 50,000 mentions a day | 15M | $0.63 | $18.90 |
| 500,000 mentions a day | 150M | $6.30 | $189.00 |
Arithmetic at TypeSafe's published list price, September 2026. Measure your own token counts before you budget: the state you send is the entire bill.
For a figure you can check without a key, the free post scorer runs seven Jev questions on a draft at the same published price sheet. That is a workload you can execute yourself, right now, with no signup.
Then you have to say something
Detection is the cheap half. The response to a crisis is the single worst post to get wrong, and it is usually written under time pressure by someone who has been awake for nineteen hours.
curl -X POST https://opentweet.io/api/v1/posts \
-H "Authorization: Bearer ot_your_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: incident-2026-09-18-statement" \
-d '{
"text": "Between 13:40 and 15:10 UTC today some accounts were charged twice. We have stopped the job, refunds are going out automatically, and nothing needs to be requested. Full write-up in the replies.",
"is_thread": true,
"thread_tweets": [
"What happened: a retry in our billing worker ran without the idempotency key it is supposed to carry.",
"Who is affected: 214 accounts. Every one of them is already queued for an automatic refund.",
"What we changed: the key is now required at the call site and the worker refuses to start without it."
],
"platforms": ["x", "bluesky", "linkedin"],
"publish_now": true
}'- Score the draft before it ships. The free post scorer runs Jev over clarity, specificity and whether the text reads as machine-written. A crisis statement that reads as generated is its own second incident.
- One statement, three networks. The call above targets X, Bluesky and LinkedIn in one request. The
Idempotency-Keyheader means a retry during an incident cannot post the statement twice. - Answer individuals individually. One-to-one replies on X do not go through an API on self-serve access tiers, so those go through the browser extension into X's own composer. Replies is the feature, and the how-to has the routing code.
- Then check whether it landed. Analytics shows what the statement actually did, and the mention baseline shows whether the load came back down.
Where this breaks
Four honest limits, all of them documented.
Jev has no context you did not send
- No knowledge of today, no search, no memory of the previous mention.
- Questions are independent: one answer is not context for another.
- Text only. A screenshot of a complaint is invisible to it.
A valid answer can still be wrong
- Schema conformance is guaranteed by construction. Correctness is not.
- TypeSafe says its zero-hallucination figure is not empirical.
- Gate anything irreversible on confidence and route the low-confidence tail to a person.
Treat mention text as untrusted input
TypeSafe's model-jaggedness page states that state is not treated as hostile and that adversarial content can steer the answer. Mentions are written by strangers, some of whom will eventually work out that you are scoring them. Keep the mention in a clearly labelled field, be explicit in the criteria, and never let a single scored mention trigger an action you cannot undo.Frequently asked questions
What is brand crisis detection with Jev?
Scoring every inbound brand mention the moment it arrives, for how damaging it is and how likely it is to travel, then watching the rate of damaging mentions against its own recent history and alerting when that rate breaks away from the baseline. Jev supplies the per-mention judgement in the 70ms to 500ms TypeSafe states for it. Your own code supplies the baseline, the arithmetic and the alert.
Why alert on a spike instead of a severity threshold?
A threshold has no memory. If an account normally collects two severe mentions a day, a rule that fires on every severe mention pages somebody twice a day forever until they mute it, and on the morning sixty arrive it sends exactly the same alert it sent yesterday. A baseline knows what normal looks like for that account, so it stays quiet through normal and fires on the change. The change is the crisis. The individual mention almost never is.
Can Jev monitor my brand mentions for me?
No. Jev has no crawler, no search, no network access and no memory between calls. It reads the text you put in the state field and returns typed answers about it. Collecting mentions, storing them, deduplicating them and holding the baseline are all your code. Jev is the judgement step in the middle, and it is the step that used to be too slow and too expensive to run on everything.
How fast is Jev at scoring a brand mention?
TypeSafe states end-to-end response time of 70ms to 500ms, against a stated LLM baseline of 3 to 329 seconds. The only independent measurement published so far is Mike Taylor’s at Every, which returned 777 separate judgements in under 0.7 seconds for an estimated quarter of a cent. That is what makes scoring at ingest possible, which is what makes the baseline current, which is what makes a spike alert mean anything.
What does it cost to score 50,000 brand mentions a day?
An estimated $0.63 a day, or roughly $19 a month. TypeSafe charges $0.042 per 1M input tokens and does not meter output, so 50,000 mentions at roughly 300 input tokens each is 15M tokens. The comparison that matters is not against another model, it is against the version of this system where you could only afford to score a sample and therefore never saw the spike.
Can a hostile mention manipulate the score?
Yes, and TypeSafe documents it. Its model-jaggedness page says state is not treated as hostile and that adversarial or user-controlled content can steer the answer. A mention containing instructions aimed at your classifier is user-controlled content. Put the mention in a clearly labelled field of a state object, keep your criteria explicit, never let a mention decide an irreversible action on its own, and test with adversarial inputs before you ship.
Keep exploring
The scoring, the thresholds, and the publishing surface that carries the response.
Jev, explained
What a System One model is, what Jev can and cannot do, and where the numbers come from.
Jev sentiment analysis
Why sentiment belongs in a Score, and how to write a five-level rubric that survives production.
Jev classification, worked end to end
Choice, Score and Noul on one real task, with the exact request and response JSON.
Choice, Score and Noul explained
What each primitive accepts, what it returns, and the limits on each.
Setting a confidence threshold
Where to gate an automatic action, and how to find the number from your own labelled data.
Analytics
What your own posts did, so you can see whether the response actually landed.
Find it in the first fifteen minutes. Answer it in the next five.
Jev grades the mentions. OpenTweet publishes the statement to X, Bluesky and LinkedIn in one call, with an idempotency key so a retry under pressure cannot double-post. Plans start at $11.99 a month with the API and the MCP server on every one.
- 7-day free trial
- No X developer account needed
- Cancel anytime