Core concepts

Triage

Optional classification of incoming mail: a category, whether a reply is needed, and how urgent it is.

What triage adds

When the operator enables triage on a deployment, each new incoming message is analyzed after it is stored, and the result is attached to the message as triage. Lists, searches, and thread reads carry it, and they accept filters on it.

Triage describes the message. It does not authorize anything: it never sends, labels, releases quarantine, or takes any other action. Use it to decide what your agent looks at first.

The triage object

triage.status is pending, complete, or failed. A completed result looks like this:

"triage": {
  "status": "complete",
  "model": "jev-latest",
  "version": 1,
  "analyzedAt": "2026-09-20T18:07:46.000Z",
  "durationMs": 640,
  "bodyTruncated": false,
  "usage": { "inputTokens": 812, "outputTokens": 40 },
  "category": { "value": "sales", "confidence": 0.91, "probabilities": { "sales": 0.91, "support": 0.05, "other": 0.04 } },
  "needsReply": { "value": true, "probability": 0.88 },
  "urgency": { "value": "normal", "score": 1.2, "confidence": 0.74, "probabilities": { "low": 0.2, "normal": 0.6, "high": 0.18, "critical": 0.02 } }
}
FieldMeaning
category.valuebilling, support, sales, personal, notification, or other.
category.confidence, category.probabilitiesHow sure the model is, and the full distribution.
needsReply.probabilityProbability that the message called for a response when it arrived.
needsReply.valuetrue at 0.8 or above, false at 0.2 or below, otherwise null (uncertain).
urgency.scoreProbability-weighted position from 0 (low) to 3 (critical).
urgency.valueThe rounded score as low, normal, high, or critical, or null when confidence is below 0.5.
model, version, analyzedAt, bodyTruncatedWhich model and rubric produced the result, when, and whether the input was cut.

A failed result carries a fixed code (provider_unavailable, provider_rejected, or invalid_response) and failedAt. Failures never block reading or replying.

Filtering

listMessages, searchMessages, and listThreads accept:

ParameterValues
categorybilling, support, sales, personal, notification, other
needsReplyyes, no, uncertain
urgencylow, normal, high, critical
curl "https://api.goshenemail.com/v1/inboxes/support%40agents.goshenemail.com/threads?needsReply=yes&urgency=high" \
  -H "Authorization: Bearer $GOSHENEMAIL_API_KEY"

On a thread, the filters apply to the latest incoming message. Once the agent replies, the thread's triage is cleared until the next message arrives, so needsReply=yes naturally lists conversations waiting on you.

What the model sees

The sender, up to 50 recipients, the subject, the received time, and the first 32,768 characters of the plain-text body. It does not see HTML, attachments, the Bcc list, or any credential. bodyTruncated: true marks a cut body.

Quarantined messages are not analyzed. If a person releases one, analysis runs then.

Reading the thresholds

The 0.8/0.2 reply thresholds and the 0.5 urgency confidence floor are display rules, not accuracy guarantees. The dashboard shows a "Maybe" category when confidence is below 0.5 and calls out uncertain reply and urgency judgments. Test on your own mail before letting triage drive automation, and keep a person in the loop for anything the model marks critical.

When triage is absent

Deployments without a TypeSafe key store mail normally and omit triage entirely. The initial email.received webhook can carry status: "pending"; read the message again for the completed result. There is no separate completion webhook and no backfill of older mail.