- Before you commit (the gate to build on):
POST /v1/sessions/estimatereturnswould_run: falsewith the parsed intent, what’smissing, and the reason — free, at $0, always callable. Validate user-supplied queries here before offering to run them. - If you create anyway:
POST /v1/sessionsreturns201with a running session and a reservation, and within seconds the run ends with anabstainevent (reasonattached) → statusabstained, $0 settled, the reservation released in full. Nothing was dispatched; the parse’s reasoning is in the event log.
The shapes to program against
The estimate, declining ($0, before any session exists):201, then on the stream):
"status": "abstained", "spend_usd": 0.
An earlier draft of this contract described a synchronous
422 declined_to_spend
at POST /v1/sessions. The live API does not emit it — clients must not gate on a
422, and error-handling code keyed to this HTTP code is dead code today. The code
stays reserved in the catalog (open enum); if a synchronous decline ever ships it
will be a dated-version change announced in the changelog.How to fix a decline
- Read the estimate’s
parsed/missingagainst your intent. The common fixes: name a concrete entity (“vinyl records”, “espresso machines”, not “something nice”), and phrase a shopping question rather than a general-knowledge one. - Gate on
would_run— it’s free and it’s the same parse the create uses. - If you know the exact catalog URL, the question-free path is extractions (planned — not yet available).
- If you believe the decline is wrong, include the
fn-request-id(estimate) or the session id (abstained run) in a support message — both join to our logs.
Reproduce it
Deterministic on anfn_test_ key with the
FN_TEST_DECLINE sentinel — a 201 create whose run streams
the abstain and ends abstained at $0, exactly like production:
POST /v1/sessions/estimate {"query": "FN_TEST_DECLINE …"} → would_run: false.