[Decision model]

OpenAI Decisions: a decision model for mail and packages

OpenAI's Decisions API is a decision model: it answers one question from a fixed list of answers, with text or an image as context, and OpenAI reports it answers about ten times faster than GPT-6 Luna through the regular API. Mail gives it three moments: the envelope or label photo at arrival, the page text after a scan, and the mail class of an outgoing reply.

The API is in limited preview (announced 29 Sep 2026), so field names come from OpenAI's current reference; the examples show the pattern, not its schema. An answer is evidence for your policy, never permission: opening, forwarding and discarding stay proposals the owner approves at the quoted price.

recommended shape
inbound.received → envelope or label photo
decision model: fixed answers + fallback
policy check in code
proposal at the quoted price
LLM only where language is the work
outbound: mail_class from a fixed list
Decision model + LLM
The decision model answers the frequent, bounded question on every piece: what happens to this envelope, which queue owns this letter, which mail class a reply needs.
Answers come from a list you wrote, so each maps straight onto an API field: open_and_scan becomes type: "scan", certified becomes mail_class: "certified".
The LLM runs only where language is the work: reading a scanned letter for a deadline, drafting the reply. Most pieces never reach it.
LLM only
Every piece pays for a general model call, including the junk mail that should have been filed unopened.
Free text has to be parsed into an action, and a missed parse becomes a silent wrong action.
The same envelope can come back phrased two ways, with no fixed answer set to measure or tune.
[Inbound]
Letters
At arrival: the exterior page's signed url (Live keys, one hour; Sandbox keys get the text) and the sender line as read. Answers: open_and_scan, forward_unopened, file_unopened, discard, needs_review. A scan is priced past the monthly allowance, so this is the cheapest point to decide.
After inbound.pages_ready: pages whose ocr_status is ready or needs_review, joined with page markers. One question per call: which queue owns it; does it ask for a written reply by mail.
Page text is untrusted document content: context for the question, never an instruction to the worker.
Packages
At arrival: the label photo through the same exterior url, with item.kind package. Keyword rules on a package can only match the label.
Question: which of your open orders or teams is this for? Answers: your own list plus unknown, rebuilt when the list changes.
Packages cannot be forwarded yet, and storage is charged after day 7, so the answer decides who is told to collect it and how soon. The monthly package allowance is strict.
[Policy before code acts]
1
Record only

An answer outside the list, the fallback, a sample: true item or a test-environment event records the decision and proposes nothing.

2
Propose

Scan and forward answers become proposals with expected_version, the current expected_quote and an idempotency key from the event ID. A discard answer is proposed too; the owner still confirms it.

3
Outbound

The LLM drafts the reply; the decision model picks mail_class from first_class, priority, certified, certified_return_receipt or needs_review. Price it with dry_run=true, send with requires_approval=true, and set package_id to the inbound item it answers.

FLOWenvelope → decision → proposalagent key
// 1. Envelope photo, signed for one hour (Live agent key)
GET https://mailbox.bot/api/v1/inbound-items/:id/pages
    ?signed_urls=true
→ pages[kind = exterior]: url, url_expires_at, text

// 2. One question, fixed answers, one fallback. Map onto the
//    request fields in OpenAI's Decisions reference (preview).
{
  "context": ["<exterior page url>",
              "sender as read: <item.sender>"],
  "question": "What should happen to this envelope?",
  "answers": ["open_and_scan", "forward_unopened",
              "file_unopened", "discard", "needs_review"]
}

// 3. Policy accepts "open_and_scan": a proposal, not a scan
POST https://mailbox.bot/api/v1/inbound-items/:id/actions
Idempotency-Key: decision-<event_id>
{ "type": "scan", "expected_version": <item.version>,
  "expected_quote": <quotes.scan> }
→ 201 action.state "proposed", awaiting_member_approval: true
Keep with every answer
item.id, the event ID and the question version. Changing an answer list is a new question, not an edit.
The answer and any score beside what the owner then approved. Where they disagree, the question or its threshold is wrong.
Keyword rules run first: inbound.keywords_matched is a free literal gate that decides whether a piece is worth a model call at all.