[{"data":1,"prerenderedAt":19},["ShallowReactive",2],{"doc-handling-verdicts":3},{"blocks":4,"markdown":18},[5,8,12,14,16],{"kind":6,"html":7},"html","\u003Cp>The mistake to avoid is collapsing three different situations into one\n\u003Ccode>try\u002Fexcept\u003C\u002Fcode>. A refusal, an outage and a spent allowance need three different\nresponses, and two of them should never reach a retry loop.\u003C\u002Fp>\n\u003Ch2 id=\"the-shape\">The shape\u003C\u002Fh2>\n",{"kind":9,"lang":10,"code":11},"code","python","import httpx\n\ndef ask(intent: str, oracle: str = \"chart\") -> dict:\n    r = httpx.post(\n        f\"{BASE}\u002Fv1\u002Foracles\u002F{oracle}\u002Fask\",\n        headers={\"Authorization\": f\"Bearer {KEY}\"},\n        json={\"intent\": intent},\n        timeout=120,                       # a verdict can take this long\n    )\n\n    # ---- refusals: the request never reached an oracle -----------------\n    if r.status_code == 401:\n        # KEY_MISSING \u002F KEY_UNKNOWN \u002F KEY_REVOKED \u002F KEY_EXPIRED.\n        # A human has to act. Never retry.\n        raise Fatal(r.json()[\"error\"])\n    if r.status_code == 402:\n        # QUOTA_EXCEEDED. The credential is fine; the allowance is spent.\n        # A different key will not help — quota is per organisation.\n        raise OutOfCredits(r.json()[\"context\"])\n    if r.status_code == 404:\n        raise Fatal(\"no such oracle — read GET \u002Fv1\u002Foracles\")\n    if r.status_code == 422:\n        # Includes \"this oracle cannot produce that format\", and the\n        # response names the ones it can.\n        raise Fatal(r.json())\n    r.raise_for_status()\n\n    # ---- verdicts: an oracle ran -------------------------------------\n    answer = r.json()\n    if answer[\"status\"] == \"error\":\n        return retry_with_backoff()        # ours, unbilled\n    if answer[\"status\"] == \"invalid\":\n        return rejected(answer[\"detail\"])  # billed, and a real answer\n    return answer[\"result\"]                # store verified_by with it",{"kind":6,"html":13},"\u003Ch2 id=\"the-distinctions-that-matter\">The distinctions that matter\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>\u003Ccode>402\u003C\u002Fcode> is not \u003Ccode>401\u003C\u002Fcode>.\u003C\u002Fstrong> Your credential is fine. A client that treats a spent\nquota as an auth failure re-prompts a human for a key that was never the problem.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>\u003Ccode class=\"chip chip--invalid\">invalid\u003C\u002Fcode> is not an exception.\u003C\u002Fstrong> It is the product working. Surface it to\nwhoever asked, do not swallow it, and do not retry the identical intent hoping\nfor a different roll.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>\u003Ccode class=\"chip chip--error\">error\u003C\u002Fcode> is the only thing worth retrying automatically\u003C\u002Fstrong>, and it is free.\u003C\u002Fp>\n\u003Ch2 id=\"store-the-citation\">Store the citation\u003C\u002Fh2>\n",{"kind":9,"lang":10,"code":15},"save(artifact=answer[\"result\"], checked_against=answer[\"verified_by\"])",{"kind":6,"html":17},"\u003Cp>An artifact without its \u003Ccode>verified_by\u003C\u002Fcode> is just a file. See\n\u003Ca href=\"\u002Fcitations\">Citations\u003C\u002Fa>.\u003C\u002Fp>\n\u003Ch2 id=\"if-an-agent-is-the-caller\">If an agent is the caller\u003C\u002Fh2>\n\u003Cp>The \u003Ca href=\"\u002Fmcp\">MCP tools\u003C\u002Fa> do the transport half of this for you and hand your\nagent structured \u003Ccode>{&quot;error&quot;: {&quot;kind&quot;: …}}\u003C\u002Fcode> objects it can reason about.\u003C\u002Fp>\n","# Handle every verdict\n\n> Client code that branches correctly on all three outcomes and on the refusals around them.\n\nThe mistake to avoid is collapsing three different situations into one\n`try\u002Fexcept`. A refusal, an outage and a spent allowance need three different\nresponses, and two of them should never reach a retry loop.\n\n## The shape\n\n```python\nimport httpx\n\ndef ask(intent: str, oracle: str = \"chart\") -> dict:\n    r = httpx.post(\n        f\"{BASE}\u002Fv1\u002Foracles\u002F{oracle}\u002Fask\",\n        headers={\"Authorization\": f\"Bearer {KEY}\"},\n        json={\"intent\": intent},\n        timeout=120,                       # a verdict can take this long\n    )\n\n    # ---- refusals: the request never reached an oracle -----------------\n    if r.status_code == 401:\n        # KEY_MISSING \u002F KEY_UNKNOWN \u002F KEY_REVOKED \u002F KEY_EXPIRED.\n        # A human has to act. Never retry.\n        raise Fatal(r.json()[\"error\"])\n    if r.status_code == 402:\n        # QUOTA_EXCEEDED. The credential is fine; the allowance is spent.\n        # A different key will not help — quota is per organisation.\n        raise OutOfCredits(r.json()[\"context\"])\n    if r.status_code == 404:\n        raise Fatal(\"no such oracle — read GET \u002Fv1\u002Foracles\")\n    if r.status_code == 422:\n        # Includes \"this oracle cannot produce that format\", and the\n        # response names the ones it can.\n        raise Fatal(r.json())\n    r.raise_for_status()\n\n    # ---- verdicts: an oracle ran -------------------------------------\n    answer = r.json()\n    if answer[\"status\"] == \"error\":\n        return retry_with_backoff()        # ours, unbilled\n    if answer[\"status\"] == \"invalid\":\n        return rejected(answer[\"detail\"])  # billed, and a real answer\n    return answer[\"result\"]                # store verified_by with it\n```\n\n## The distinctions that matter\n\n**`402` is not `401`.** Your credential is fine. A client that treats a spent\nquota as an auth failure re-prompts a human for a key that was never the problem.\n\n**`invalid` is not an exception.** It is the product working. Surface it to\nwhoever asked, do not swallow it, and do not retry the identical intent hoping\nfor a different roll.\n\n**`error` is the only thing worth retrying automatically**, and it is free.\n\n## Store the citation\n\n```python\nsave(artifact=answer[\"result\"], checked_against=answer[\"verified_by\"])\n```\n\nAn artifact without its `verified_by` is just a file. See\n[Citations](https:\u002F\u002Fdocs.formalogic.io\u002Fcitations).\n\n## If an agent is the caller\n\nThe [MCP tools](https:\u002F\u002Fdocs.formalogic.io\u002Fmcp) do the transport half of this for you and hand your\nagent structured `{\"error\": {\"kind\": …}}` objects it can reason about.\n",1793819974130]