[{"data":1,"prerenderedAt":109},["ShallowReactive",2],{"doc-quickstart":3},{"blocks":4,"markdown":108},[5,8,10,12,16,18,74,80,82,84,86],{"kind":6,"html":7},"html","\u003Cp>Both ways need a Forma account. \u003Ca href=\"https:\u002F\u002Fapp.formalogic.io\" target=\"_blank\" rel=\"noopener noreferrer\">Sign up\u003C\u002Fa> and open the\nconsole once — that creates your organisation. The free plan includes \u003Cstrong>20\ncredits a month\u003C\u002Fstrong>; an answer costs 1 credit whether it is \u003Ccode class=\"chip chip--valid\">valid\u003C\u002Fcode> or \u003Ccode class=\"chip chip--invalid\">invalid\u003C\u002Fcode>,\nand an \u003Ccode class=\"chip chip--error\">error\u003C\u002Fcode> is free.\u003C\u002Fp>\n\u003Ch2 id=\"in-your-ai-assistant\">In your AI assistant\u003C\u002Fh2>\n\u003Cp>A minute or two in most clients — ChatGPT needs Developer mode turned on first.\u003C\u002Fp>\n",{"kind":9},"connect",{"kind":6,"html":11},"\u003Cp>To check it is connected, ask your assistant to run \u003Ccode>forma_status\u003C\u002Fcode>: it answers\nwith your organisation and plan.\u003C\u002Fp>\n\u003Cp>Then ask for something. This exact request came back \u003Ccode class=\"chip chip--valid\">valid\u003C\u002Fcode>:\u003C\u002Fp>\n",{"kind":13,"lang":14,"code":15},"code","text","A right triangle with legs 3 and 4 and hypotenuse 5, each side labelled with its length, and the right angle marked with a small square.",{"kind":6,"html":17},"\u003Cp>What comes back is a verdict with the TikZ source and its \u003Ccode>verified_by\u003C\u002Fcode>\ncitation. Where your client can show it, the figure is drawn inline; otherwise\nask the agent to show it — see \u003Ca href=\"\u002Ffigures\">Draw a figure\u003C\u002Fa>.\u003C\u002Fp>\n\u003Ch2 id=\"from-code\">From code\u003C\u002Fh2>\n\u003Cp>An API key and one request.\u003C\u002Fp>\n",{"kind":19,"steps":20},"steps",[21,33,54,62,69],{"title":22,"blocks":23},"Get a key",[24,26,30],{"kind":6,"html":25},"\u003Cp>Keys are shown once, when they are created. Export it for the commands below.\u003C\u002Fp>\n",{"kind":27,"path":28,"label":29},"console","\u002Fkeys","Create an API key",{"kind":13,"lang":31,"code":32},"bash","export FORMA_API_KEY=fk_live_…",{"title":34,"blocks":35},"Ask an oracle",[36,38,52],{"kind":6,"html":37},"\u003Cp>Put the numbers in the intent. Forma does not fetch data: a chart request that\ndoes not give its values is refused with \u003Ccode>422 DATA_REQUIRED\u003C\u002Fcode>, unbilled. To get a\nsketch with illustrative values instead, send \u003Ccode>&quot;allow_generated_data&quot;: true\u003C\u002Fcode>; the\nanswer then says \u003Ccode>&quot;data&quot;: &quot;generated&quot;\u003C\u002Fcode>.\n\u003Ccode>format\u003C\u002Fcode> is optional: \u003Ccode>chart\u003C\u002Fcode> answers in \u003Ccode>vega-lite\u003C\u002Fcode> unless you ask for another,\n\u003Ccode>vector-graphics\u003C\u002Fcode> in \u003Ccode>tikz\u003C\u002Fcode>.\u003C\u002Fp>\n",{"kind":39,"tabs":40},"code-group",[41,44,48],{"lang":31,"label":42,"code":43},"curl","curl https:\u002F\u002Fapi.formalogic.io\u002Fv1\u002Foracles\u002Fchart\u002Fask \\\n  -H \"Authorization: Bearer $FORMA_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"intent\": \"grouped bar chart of quarterly sales, Q1-Q4: North 120, 135, 150, 160; South 90, 110, 105, 130\"}'",{"lang":45,"label":46,"code":47},"python","Python","import os, httpx\n\nanswer = httpx.post(\n    \"https:\u002F\u002Fapi.formalogic.io\u002Fv1\u002Foracles\u002Fchart\u002Fask\",\n    headers={\"Authorization\": f\"Bearer {os.environ['FORMA_API_KEY']}\"},\n    json={\"intent\": \"grouped bar chart of quarterly sales, Q1-Q4: North 120, 135, 150, 160; South 90, 110, 105, 130\"},\n    timeout=120,\n).json()",{"lang":49,"label":50,"code":51},"ts","TypeScript","const answer = await fetch('https:\u002F\u002Fapi.formalogic.io\u002Fv1\u002Foracles\u002Fchart\u002Fask', {\n  method: 'POST',\n  headers: {\n    Authorization: `Bearer ${process.env.FORMA_API_KEY}`,\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({ intent: 'grouped bar chart of quarterly sales, Q1-Q4: North 120, 135, 150, 160; South 90, 110, 105, 130' }),\n}).then((r) => r.json())",{"kind":6,"html":53},"\u003Cp>Allow \u003Cstrong>120 seconds\u003C\u002Fstrong>: reaching a verdict can take that long. There is no\nstreaming and no polling endpoint — the call returns when the verdict does.\u003C\u002Fp>\n",{"title":55,"blocks":56},"Read the verdict",[57,60],{"kind":13,"lang":58,"code":59},"json","{\n  \"oracle\": \"chart\",\n  \"format\": \"vega-lite\",\n  \"status\": \"valid\",\n  \"credits\": 1,\n  \"result\": {\n    \"$schema\": \"https:\u002F\u002Fvega.github.io\u002Fschema\u002Fvega-lite\u002Fv6.json\",\n    \"data\": { \"values\": [{ \"quarter\": \"Q1\", \"region\": \"North\", \"sales\": 120 }, …] },\n    \"mark\": \"bar\",\n    \"encoding\": { \"x\": { \"field\": \"quarter\" }, \"xOffset\": { \"field\": \"region\" }, \"y\": { \"field\": \"sales\" }, … }\n  },\n  \"verified_by\": \"chart@6baf09f0\",\n  \"figure\": { \"drawable\": true, \"target\": \"vega-lite\", \"token\": \"v2.dmVnYS1saXRl…\" }\n}",{"kind":6,"html":61},"\u003Cp>Four fields carry the whole idea:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>status\u003C\u002Fcode> — the \u003Cstrong>verdict\u003C\u002Fstrong>: \u003Ccode class=\"chip chip--valid\">valid\u003C\u002Fcode>, \u003Ccode class=\"chip chip--invalid\">invalid\u003C\u002Fcode> (refused, with \u003Ccode>detail\u003C\u002Fcode>) or\n\u003Ccode class=\"chip chip--error\">error\u003C\u002Fcode> (ours, not billed). \u003Ca href=\"\u002Fverdicts\">Verdicts\u003C\u002Fa> has what each means.\u003C\u002Fli>\n\u003Cli>\u003Ccode>result\u003C\u002Fcode> — the artifact, present only on \u003Ccode class=\"chip chip--valid\">valid\u003C\u002Fcode>. Here it is a Vega-Lite spec\nyou can render yourself.\u003C\u002Fli>\n\u003Cli>\u003Ccode>verified_by\u003C\u002Fcode> — \u003Cstrong>the version of the rules the answer passed\u003C\u002Fstrong>. Store it next to\nthe artifact.\u003C\u002Fli>\n\u003Cli>\u003Ccode>credits\u003C\u002Fcode> — what this call actually cost.\u003C\u002Fli>\n\u003C\u002Ful>\n",{"title":63,"blocks":64},"Draw it",[65,67],{"kind":6,"html":66},"\u003Cp>Or let Forma draw it: send the \u003Ccode>figure.token\u003C\u002Fcode> back. It is free, returns SVG by\ndefault (\u003Ccode>&quot;format&quot;: &quot;png&quot;\u003C\u002Fcode> for a raster), and the token is valid for an hour.\u003C\u002Fp>\n",{"kind":13,"lang":31,"code":68},"curl --fail-with-body https:\u002F\u002Fapi.formalogic.io\u002Fv1\u002Ffigures\u002Frender \\\n  -H \"Authorization: Bearer $FORMA_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"token\": \"v2.dmVnYS1saXRl…\"}' -o chart.svg",{"title":70,"blocks":71},"Check what it cost",[72],{"kind":6,"html":73},"\u003Cp>The \u003Ca href=\"https:\u002F\u002Fapp.formalogic.io\u002Fusage\" target=\"_blank\" rel=\"noopener noreferrer\">Usage\u003C\u002Fa> page shows the call you just made:\nwhich oracle, which key, what verdict, what it cost.\u003C\u002Fp>\n",{"kind":75,"tone":76,"blocks":77},"callout","tip",[78],{"kind":6,"html":79},"\u003Cp>An \u003Ccode class=\"chip chip--invalid\">invalid\u003C\u002Fcode> verdict arrives as a \u003Ccode>200\u003C\u002Fcode>: the call worked, and the check said\nno. Handle the HTTP status and the verdict separately — see\n\u003Ca href=\"\u002Fhandling-verdicts\">Handle every verdict\u003C\u002Fa>.\u003C\u002Fp>\n",{"kind":6,"html":81},"\u003Ch2 id=\"list-what-you-can-ask\">List what you can ask\u003C\u002Fh2>\n",{"kind":13,"lang":31,"code":83},"curl https:\u002F\u002Fapi.formalogic.io\u002Fv1\u002Foracles \\\n  -H \"Authorization: Bearer $FORMA_API_KEY\"",{"kind":6,"html":85},"\u003Cp>Free. The catalogue is the contract — read it at start-up rather than hardcoding\nnames, and a new oracle becomes available to you without a deploy.\u003C\u002Fp>\n\u003Ch2 id=\"next\">Next\u003C\u002Fh2>\n",{"kind":87,"cards":88},"cards",[89,94,99,104],{"title":90,"href":91,"body":92,"eyebrow":93},"How Forma works","\u002Fhow-oracles-work","What a verdict promises, and what it does not.","Get started",{"title":95,"href":96,"body":97,"eyebrow":98},"Handle every verdict","\u002Fhandling-verdicts","Client code that branches on each outcome.","Guides",{"title":100,"href":101,"body":102,"eyebrow":103},"Connect your agent","\u002Fconnect","The same calls as tools, for Claude, Cursor and other MCP clients.","Connect",{"title":105,"href":106,"body":107,"eyebrow":98},"Gate CI on a verdict","\u002Fgate-ci","Generate an artifact in CI; fail the build unless it comes back valid.","# Quickstart\n\n> Two ways in — your AI assistant, or code. Pick the one you work in.\n\nBoth ways need a Forma account. [Sign up](https:\u002F\u002Fapp.formalogic.io) and open the\nconsole once — that creates your organisation. The free plan includes **20\ncredits a month**; an answer costs 1 credit whether it is `valid` or `invalid`,\nand an `error` is free.\n\n## In your AI assistant\n\nA minute or two in most clients — ChatGPT needs Developer mode turned on first.\n\nConnect an assistant: add the remote MCP server https:\u002F\u002Fmcp.formalogic.io\u002Fmcp (no API key; you sign in with your Forma account). Or paste this into any agent:\n\n```\nConnect to Forma, a remote MCP server at https:\u002F\u002Fmcp.formalogic.io\u002Fmcp. Add it as a remote HTTP MCP server named \"forma\" with no API key: it uses OAuth, so I will sign in with my Forma account in the browser when you connect. If you need the exact steps for this client, read https:\u002F\u002Fdocs.formalogic.io\u002Fconnect. Once connected, call forma_status and tell me which account you are connected as, then call list_oracles to show what you can ask.\n```\n\nTo check it is connected, ask your assistant to run `forma_status`: it answers\nwith your organisation and plan.\n\nThen ask for something. This exact request came back `valid`:\n\n```text\nA right triangle with legs 3 and 4 and hypotenuse 5, each side labelled with its length, and the right angle marked with a small square.\n```\n\nWhat comes back is a verdict with the TikZ source and its `verified_by`\ncitation. Where your client can show it, the figure is drawn inline; otherwise\nask the agent to show it — see [Draw a figure](https:\u002F\u002Fdocs.formalogic.io\u002Ffigures).\n\n## From code\n\nAn API key and one request.\n\n**Get a key**\n\nKeys are shown once, when they are created. Export it for the commands below.\n\n[Create an API key](https:\u002F\u002Fapp.formalogic.io\u002Fkeys)\n\n```bash\nexport FORMA_API_KEY=fk_live_…\n```\n\n**Ask an oracle**\n\nPut the numbers in the intent. Forma does not fetch data: a chart request that\ndoes not give its values is refused with `422 DATA_REQUIRED`, unbilled. To get a\nsketch with illustrative values instead, send `\"allow_generated_data\": true`; the\nanswer then says `\"data\": \"generated\"`.\n`format` is optional: `chart` answers in `vega-lite` unless you ask for another,\n`vector-graphics` in `tikz`.\n\n```bash curl\ncurl https:\u002F\u002Fapi.formalogic.io\u002Fv1\u002Foracles\u002Fchart\u002Fask \\\n  -H \"Authorization: Bearer $FORMA_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"intent\": \"grouped bar chart of quarterly sales, Q1-Q4: North 120, 135, 150, 160; South 90, 110, 105, 130\"}'\n```\n\n```python Python\nimport os, httpx\n\nanswer = httpx.post(\n    \"https:\u002F\u002Fapi.formalogic.io\u002Fv1\u002Foracles\u002Fchart\u002Fask\",\n    headers={\"Authorization\": f\"Bearer {os.environ['FORMA_API_KEY']}\"},\n    json={\"intent\": \"grouped bar chart of quarterly sales, Q1-Q4: North 120, 135, 150, 160; South 90, 110, 105, 130\"},\n    timeout=120,\n).json()\n```\n\n```ts TypeScript\nconst answer = await fetch('https:\u002F\u002Fapi.formalogic.io\u002Fv1\u002Foracles\u002Fchart\u002Fask', {\n  method: 'POST',\n  headers: {\n    Authorization: `Bearer ${process.env.FORMA_API_KEY}`,\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({ intent: 'grouped bar chart of quarterly sales, Q1-Q4: North 120, 135, 150, 160; South 90, 110, 105, 130' }),\n}).then((r) => r.json())\n```\n\nAllow **120 seconds**: reaching a verdict can take that long. There is no\nstreaming and no polling endpoint — the call returns when the verdict does.\n\n**Read the verdict**\n\n```json\n{\n  \"oracle\": \"chart\",\n  \"format\": \"vega-lite\",\n  \"status\": \"valid\",\n  \"credits\": 1,\n  \"result\": {\n    \"$schema\": \"https:\u002F\u002Fvega.github.io\u002Fschema\u002Fvega-lite\u002Fv6.json\",\n    \"data\": { \"values\": [{ \"quarter\": \"Q1\", \"region\": \"North\", \"sales\": 120 }, …] },\n    \"mark\": \"bar\",\n    \"encoding\": { \"x\": { \"field\": \"quarter\" }, \"xOffset\": { \"field\": \"region\" }, \"y\": { \"field\": \"sales\" }, … }\n  },\n  \"verified_by\": \"chart@6baf09f0\",\n  \"figure\": { \"drawable\": true, \"target\": \"vega-lite\", \"token\": \"v2.dmVnYS1saXRl…\" }\n}\n```\n\nFour fields carry the whole idea:\n\n- `status` — the **verdict**: `valid`, `invalid` (refused, with `detail`) or\n  `error` (ours, not billed). [Verdicts](https:\u002F\u002Fdocs.formalogic.io\u002Fverdicts) has what each means.\n- `result` — the artifact, present only on `valid`. Here it is a Vega-Lite spec\n  you can render yourself.\n- `verified_by` — **the version of the rules the answer passed**. Store it next to\n  the artifact.\n- `credits` — what this call actually cost.\n\n**Draw it**\n\nOr let Forma draw it: send the `figure.token` back. It is free, returns SVG by\ndefault (`\"format\": \"png\"` for a raster), and the token is valid for an hour.\n\n```bash\ncurl --fail-with-body https:\u002F\u002Fapi.formalogic.io\u002Fv1\u002Ffigures\u002Frender \\\n  -H \"Authorization: Bearer $FORMA_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"token\": \"v2.dmVnYS1saXRl…\"}' -o chart.svg\n```\n\n**Check what it cost**\n\nThe [Usage](https:\u002F\u002Fapp.formalogic.io\u002Fusage) page shows the call you just made:\nwhich oracle, which key, what verdict, what it cost.\n\nAn `invalid` verdict arrives as a `200`: the call worked, and the check said\nno. Handle the HTTP status and the verdict separately — see\n[Handle every verdict](https:\u002F\u002Fdocs.formalogic.io\u002Fhandling-verdicts).\n\n## List what you can ask\n\n```bash\ncurl https:\u002F\u002Fapi.formalogic.io\u002Fv1\u002Foracles \\\n  -H \"Authorization: Bearer $FORMA_API_KEY\"\n```\n\nFree. The catalogue is the contract — read it at start-up rather than hardcoding\nnames, and a new oracle becomes available to you without a deploy.\n\n## Next\n\n- [How Forma works](https:\u002F\u002Fdocs.formalogic.io\u002Fhow-oracles-work): What a verdict promises, and what it does not.\n- [Handle every verdict](https:\u002F\u002Fdocs.formalogic.io\u002Fhandling-verdicts): Client code that branches on each outcome.\n- [Connect your agent](https:\u002F\u002Fdocs.formalogic.io\u002Fconnect): The same calls as tools, for Claude, Cursor and other MCP clients.\n- [Gate CI on a verdict](https:\u002F\u002Fdocs.formalogic.io\u002Fgate-ci): Generate an artifact in CI; fail the build unless it comes back valid.\n",1793819973842]