[Decision model]

Typed decisions on scanned mail

Jev is a System One model: it takes a text state and typed questions and returns a choice, a score or a 0–1 truth value, with probabilities your code can branch on. Scanned mail already arrives as saved page text, so the only thing to build is the questions and the branches.

Jev does not generate text, draft a letter or send mail. It reads text only, so the worker passes OCR pages, never images. Physical handling and mailed replies stay behind the owner's approval.

recommended shape
inbound.pages_ready event
worker reads pages_url
state + atomic questions → Jev
typed answers with confidence
code branches, guardrails first
proposal or dry run → approval
Keyword rules decide
Whether a letter is worth a model call at all: literal, case-insensitive terms on the envelope at arrival or on the pages after a scan. No model, no cost.
Which pages to send: inbound.keywords_matched names the rule, the matched terms, where and page_numbers.
A rule fires at most once per item and page set, so the gate cannot re-trigger a run on the same text.
Jev decides
Category and routing: a choice from a fixed list with per-option probabilities.
Urgency on your rubric: a score, so a coefficient in code changes priorities, not a prompt.
Facts about intent: a noul for "expects a written reply" and for "asks for money or credentials", the second used purely as a guardrail.
[Workflow]
1

The receiver verifies the Standard Webhooks signature, stores the immutable event ID, and answers 2xx only after the event is durably queued.

2

The worker fetches pages_url with an inbound.read key and keeps pages whose OCR is ready or needs review. Nothing readable means wait, not guess.

3

Page text becomes the Jev state, with uncertain spans counted. It is data for the questions, never an instruction to the worker.

4

Branch in order: money or credential requests to a person; bulk mail filed; low confidence or an unknown route to review; only then a proposal.

5

A forward is proposed with expected_version and an idempotency key and waits for the owner. A reply is priced with dry_run=true before anything exists.

Technical pattern
Subscribe to inbound.pages_ready, plus inbound.keywords_matched when rules gate the run. inbound.received is too early: the envelope is photographed, the contents are not scanned.
Treat sample: true and the test environment as rehearsal: record the decision, propose nothing.
Store the Jev answers in outbound metadata; they come back on the mail record and every status callback, so the reason for a letter travels with it.
Persist probabilities beside outcomes. That is the dataset for tuning thresholds and the audit trail for why a letter was forwarded.
State to send
item.id, item.reference, item.kind
item.sender as the scan read it, marked unverified
readable pages joined with page markers
count of needs_review pages and uncertain_spans
matched rule and terms, when a rule opened the gate
environment and sample
POSTTypeSafe evaluatestate + questions
// One request: the page text as state, every question evaluated in parallel.
{
  "state": "<readable pages from pages_url, joined with page markers>",
  "questions": [
    { "id": "route", "type": "choice",
      "question": "Which team should handle this letter?",
      "options": ["accounts_payable", "legal", "permits", "personal", "unknown"] },
    { "id": "urgency", "type": "score",
      "question": "How time-sensitive is the requested action?",
      "rubric": { "1": "no action requested", "5": "a stated deadline within 14 days" } },
    { "id": "wants_written_reply", "type": "noul",
      "statement": "The document asks the recipient to respond in writing by mail." },
    { "id": "asks_for_money", "type": "noul",
      "statement": "The document asks the reader to pay, wire funds or share account credentials." }
  ]
}

// Answers: route.choice + probabilities + confidence, urgency.score, *.noul (0–1)
Branch order
asks_for_money.noul > 0.6: escalate to a person, always, whatever else was answered.
bulk marketing likely and urgency.score ≤ 2: file it. No physical action, no cost.
route.confidence < 0.8, any page needing review, or route.choice = unknown: review queue with the probabilities attached.
legal or urgency.score ≥ 4: propose forwarding the original; the owner approves.
wants_written_reply.noul > 0.7: draft with your LLM, dry-run the cost, then submit with requires_approval=true and a cost cap.