Last updated: September 2026

Jev for community management

Jev automates the triage, not the replying. One Choice question sorts every incoming reply and mention into exactly one bucket and hands back a confidence number, so your code can skip the noise, rank what is left and show a person only the things that need one. Two hundred mentions a day works out to an estimated 13 cents a month.

The constraint to know before you design anything: X restricted programmatic replies on self-serve tiers on 23 February 2026. No tool on a self-serve tier can auto-reply to someone else's post, OpenTweet included. So the reply delivery path is a browser extension that puts the text in X's own composer for you to submit. Original posts and threads still publish through one API call. That split runs through everything below.

Name the buckets by destination

The common mistake is designing buckets around topics. Topics are infinite and none of them tell you what to do. Destinations are few, and the whole point of triage is to stop looking at something. The test for a good bucket list: if two buckets end in the same action, merge them. If one bucket has two destinations, split it.

Bucket
Where it goes
Who acts
question
Replies feed with a drafted answer
You, through the extension composer
complaint
Replies feed, top of the queue
You, same day
bug_report
Your issue tracker, with a link back to the post
An engineer
lead
DM campaigns, as a candidate
You approve every send by hand
praise
Counted in analytics, occasionally answered
Usually nobody
spam
Skipped, logged, sampled weekly
Nobody
troll
Skipped, logged, never surfaced
Nobody
off_topic
Skipped
Nobody

This is a Choice, not a set of Nouls, because a mention gets exactly one destination. Choice returns the winning option, a probability for every option summing to 1, and a confidence number collapsed from that distribution. The confidence is the reason to use Choice here at all: it is what tells you whether the sort was clear enough to act on without a person. Choice supports up to 255 options, so a deeper taxonomy is possible, but eight destinations is already more than most teams act on differently.

Alongside it, send independent Nouls for the flags that cut across every bucket. Whether the mention is time-sensitive has nothing to do with which bucket it lands in, so it must not compete for the same probability mass. Which primitive answers which shape of question is covered on Choice, Score and Noul.

One request per mention

The post being replied to goes into the state alongside the mention, because "off topic" and "bug report" are both judgments about the relationship between the two. Pin jev-1.13.0 rather than jev-latest, so your routing thresholds do not shift the day the alias moves.

request.json
{
  "state": {
    "our_post": "Bluesky cross-posting is live. Same draft, both networks, one call.",
    "mention": "does this handle the 300-grapheme limit or do I get a silent truncation? asking because the last tool I tried just cut it",
    "author_handle": "@dana_builds"
  },
  "model": "jev-1.13.0",
  "questions": {
    "bucket": {
      "type": "choice",
      "instructions": "Which single queue should this mention go to",
      "criteria": {
        "question": "Asks something answerable about the product, the platform, or how to do a thing.",
        "complaint": "Reports a bad experience with us, without enough detail to file as a bug.",
        "bug_report": "Describes specific broken behaviour, with steps, an error, or an expected result.",
        "lead": "Signals intent to buy, evaluate, or switch from a competitor.",
        "praise": "Positive with nothing to action.",
        "spam": "Unsolicited promotion, referral links, or a credential grab.",
        "troll": "Attacks a person rather than engaging with the subject.",
        "off_topic": "Unrelated to us or to the post it replied to."
      }
    },
    "time_sensitive": {
      "type": "noul",
      "instructions": "Waiting a day to respond makes the outcome materially worse.",
      "criteria": {
        "true": "The author is blocked right now, about to churn, or publicly waiting on us.",
        "false": "A general question, a feature idea, or a comment with no deadline."
      }
    },
    "needs_a_person": {
      "type": "noul",
      "instructions": "Answering this well requires judgment or information a templated response cannot supply."
    }
  }
}

Every declared question is evaluated in one parallel pass, so the Choice and both Nouls cost a single round trip. An illustrative response in the documented shape, with values written to show the fields rather than captured from a call:

response.json
{
  "model": "jev-1.13.0",
  "answers": {
    "bucket": {
      "type": "choice",
      "choice": "question",
      "probabilities": {
        "question": 0.782,
        "bug_report": 0.169,
        "complaint": 0.041,
        "lead": 0.004,
        "praise": 0.002,
        "spam": 0.001,
        "troll": 0.000,
        "off_topic": 0.001
      },
      "confidence": 0.641
    },
    "time_sensitive": { "type": "noul", "noul": 0.212 },
    "needs_a_person": { "type": "noul", "noul": 0.874 }
  },
  "usage": { "input_tokens": 524, "output_tokens": 96 }
}

Read the shapes. The Choice carries choice, probabilities and confidence. Each Noul carries only its noul float, with no confidence field at all. That asymmetry is the single most mis-stated detail about this API, and it decides your code: for a Noul, uncertainty means a value near 0.5, not a separate number to read.

The interesting part of the illustrated answer is the confidence of 0.641. The bucket is question at 0.782, but bug_report took 0.169, which is honest: the author is asking a question and reporting that a previous tool truncated silently. That is the case a hard classifier would get confidently wrong and a confidence-gated one holds back.

Skip narrowly, surface generously

TypeSafe's own guidance is that the answer tells you what and the confidence tells you whether to act, and that different actions in the same system should be gated at different levels depending on the consequences of being wrong. For an inbox that means auto-skipping is a short list, because a skipped question is a customer you ignored, while a surfaced piece of spam costs one second of scrolling.

route.ts
const AUTO_ROUTE_AT = 0.85;   // clear enough that software can move it
const HOLD_BELOW    = 0.50;   // flat distribution, a person sorts this one

// Skipping is only safe where being wrong costs nothing, so it is a short list.
const SKIPPABLE = new Set(['spam', 'troll', 'off_topic']);

interface Triage {
  bucket: { choice: string; confidence: number };
  time_sensitive: { noul: number };
  needs_a_person: { noul: number };
}

export function route(t: Triage) {
  const { choice, confidence } = t.bucket;

  // Low confidence means the distribution was flat, not that the bucket is
  // wrong. Either way it is not something to act on unattended.
  if (confidence < HOLD_BELOW) {
    return { queue: 'unsorted', priority: 0, draft: false };
  }

  if (SKIPPABLE.has(choice) && confidence >= AUTO_ROUTE_AT) {
    return { queue: 'skipped', priority: 0, draft: false };
  }

  // Everything else surfaces to a human. Order it, do not filter it.
  const priority =
    (t.time_sensitive.noul >= 0.7 ? 2 : 0) +
    (choice === 'complaint' || choice === 'bug_report' ? 1 : 0);

  return { queue: choice, priority, draft: t.needs_a_person.noul < 0.6 };
}

Three deliberate choices in that function. Only spam, troll and off-topic can be skipped, and only at high confidence. Low confidence goes to an unsorted tray rather than a guessed bucket, because a flat distribution is information and hiding it is not. And priority orders the human queue instead of filtering it, which is the difference between a triage system people trust and one they turn off. How to fit the two cut points to your own inbox is on setting a Jev confidence threshold.

Ship it in shadow mode first

Run the triage alongside your current inbox for a week and log the pinned model id, the full answers object and your thresholds, without changing what anyone sees. Then compare the buckets against what you actually did with each mention. Re-scoring the same archive after a rule change costs almost nothing, so there is no reason to guess.

The delivery constraint, stated plainly

On 23 February 2026, X restricted programmatic replies through POST /2/tweets. An app can only reply when the original author mentions it or quote-posts it. The restriction covers the pay-per-use tier as well, and only Enterprise access is exempt. OpenTweet posts through the official X API, so it applies to OpenTweet too. Any product promising you automated mass replying on a self-serve plan is either on Enterprise access or is not doing what it says.

What still works: threads. A thread is a reply to your own post, which the restriction does not touch, so a thread publishes through the API like any other post.

What that leaves for replies is a handoff, and OpenTweet layers it in three tiers. The browser extension inserts the drafted text straight into X's composer. If the extension is not installed, an intent URL prefills the composer instead. If that is unavailable, the text goes to your clipboard and the post opens in a new tab. Every tier ends with a person pressing post. None of them send anything on your behalf.

This is a feature as much as a constraint. A triage layer that ends in a human keystroke cannot produce the failure mode everyone building on social APIs is actually afraid of, which is a thousand confidently wrong replies going out overnight. Jev returns a probability. It does not return permission.

Two more limits worth designing around

Jev is text only. No image, audio or video input, so a mention whose entire content is a screenshot reaches your rules as an empty state. And Jev generates no text at all, so it cannot draft the reply it just triaged. That is a language model's job, and the two-model split is the point: Jev decides, an LLM writes, OpenTweet publishes.

What a month of triage costs

The illustrative request above comes to an estimated 520 input tokens. At $0.042 per 1M input tokens with output free, that is an estimated $0.000022 per mention.

Workload
Calls
Jev cost
1,000 mentions
1,000 requests
about $0.02
200 mentions a day for a month
6,000 requests
about $0.13
2,000 mentions a day for a month
60,000 requests
about $1.32
One million mentions
1,000,000 requests
about $22

Calculated at an estimated 520 input tokens per mention and $0.042 per 1M input tokens, September 2026. Excludes whatever your language model charges to draft the replies, which will be the larger line item by a wide margin.

That last sentence is the real finding. Once triage costs an estimated 2 cents per 1,000 mentions, the expensive part of a community workflow is writing, not deciding, and you only pay for writing on the mentions that survived the filter. Filtering first is what makes the drafting budget work.

Latency matters less here than in a feed, but for reference TypeSafe reports 70ms to 500ms end to end, and you can try a seven-question scorer free and without signing up at will it go viral.

Where each bucket lands in OpenTweet

  • Replies. Questions, complaints and bug reports, ordered by the priority your routing function computed, each with a draft next to it and the composer handoff to send it.
  • DM campaigns. The lead bucket, as a candidate with a lead state. Every send is approved by hand, by design, and there is no bulk path.
  • Analytics. The bucket mix over time is the most useful number a community manager has. Complaints climbing from 4% to 11% of mentions is a signal no engagement chart shows you.
  • The MCP server. 43 tools, so the whole loop can run from Claude Code or Cursor: triage with Jev, draft with the model you are already talking to, queue with OpenTweet.

And the publishing half, which is unrestricted. One POST to /api/v1/posts targets X, Bluesky and LinkedIn from the same body. Note it is /posts, not /tweets:

bash
curl -X POST https://opentweet.io/api/v1/posts \
  -H "Authorization: Bearer ot_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Five questions came up twice this week, so here are the answers in one place.",
    "is_thread": true,
    "thread_tweets": [
      "1. Yes, Bluesky counts graphemes and has a separate byte ceiling. We check both before sending.",
      "2. No silent truncation. A post too long for a target is reported as skipped, with the reason."
    ],
    "publish_now": true,
    "platforms": ["x", "bluesky"]
  }'

text is the lead post and thread_tweets holds the replies only, up to 25 parts including the lead. The call is synchronous: it returns the real per-network outcome in the same response.

7-day free trial. Cancel anytime.

Frequently asked questions

Can Jev automate community management?

It can automate the triage, not the replying. One Choice question sorts every incoming mention into a bucket and returns a confidence number, so software can skip the noise and rank what is left. Jev generates no text at all, so it cannot write the reply, and it accepts text only, so it cannot read a screenshot.

Can I auto-reply to mentions through the X API?

No, and no other tool on a self-serve tier can either. On 23 February 2026 X restricted programmatic replies through POST /2/tweets: an app can only reply when the original author mentions it or quote-posts it. That applies to the pay-per-use tier too, and only Enterprise access is exempt. Threads still work, because a thread is a reply to your own post, which is unaffected.

So how does a drafted reply actually get sent?

Through a browser extension that inserts the text into X’s own composer, which you then submit yourself. OpenTweet layers the delivery: the extension first, an intent URL that prefills the composer as a fallback, then the clipboard plus the post opened in a new tab. Every path ends with a person pressing post. There is no bulk send and there never will be.

Should the buckets be a Choice or several Nouls?

A Choice, because triage produces exactly one destination. Choice returns the winning option, a probability for every option, and a confidence number, which is what lets you auto-route the clear cases and hold the ambiguous ones. Use separate Nouls alongside it for the orthogonal flags, such as whether the mention is time-sensitive.

What does it cost to triage a day of mentions?

An estimated 2 cents per 1,000 mentions. One Choice over eight buckets plus two Nouls is roughly 520 input tokens, at $0.042 per 1M input tokens with output free. Two hundred mentions a day works out to an estimated $0.13 a month.

Can Jev decide what to publish without a human?

It can tell you how likely a statement about a draft is true. Whether that is enough to publish unattended is your call, and the confidence number is the natural place to put the human-in-the-loop gate. A probability is not a policy decision, and a schema-valid answer can still be factually wrong.

Triage with Jev. Publish with OpenTweet.

One REST API, 43 MCP tools and one calendar for X, Bluesky and LinkedIn, from $11.99 a month. No bulk auto-replies, on purpose.

7-day free trial. Cancel anytime.