Skip to main content
Reserved code — not emitted today. Before any money moves, the parse decides whether spending on this query could honestly deliver. When the answer is no — there’s no purchasable entity, the ask isn’t catalog-shaped, the constraints can’t be shopped — the API declines to spend. How that reaches you is not an HTTP error:
  1. Before you commit (the gate to build on): POST /v1/sessions/estimate returns would_run: false with the parsed intent, what’s missing, and the reason — free, at $0, always callable. Validate user-supplied queries here before offering to run them.
  2. If you create anyway: POST /v1/sessions returns 201 with a running session and a reservation, and within seconds the run ends with an abstain event (reason attached) → status abstained, $0 settled, the reservation released in full. Nothing was dispatched; the parse’s reasoning is in the event log.
This is the abstain posture end to end: a stated $0 outcome, not a failure dressed up as an empty result — and not a synchronous error your create call must catch.

The shapes to program against

The estimate, declining ($0, before any session exists):
The run ending, if you create anyway (201, then on the stream):
…and the session settles as "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/missing against 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 an fn_test_ key with the FN_TEST_DECLINE sentinel — a 201 create whose run streams the abstain and ends abstained at $0, exactly like production:
And rehearse the pre-spend gate with the same sentinel on the estimate: POST /v1/sessions/estimate {"query": "FN_TEST_DECLINE …"}would_run: false.