# Ask an oracle

> The one call that costs credits, and the one that returns a verdict.

`POST https://api.formalogic.io/v1/oracles/{name}/ask`

Request:

| Field | Type | Where | Meaning |
|---|---|---|---|
| `name` | string | path, required | An oracle `name` from `GET /v1/oracles`. |
| `intent` | string | body, required | What you want, in plain language. Not a template and not a specification — describe the thing, not how to build it. |
| `format` | string \| null | body | One of the oracle's `formats`. Omit it for the oracle's `default_format`. A format the oracle cannot produce is refused with the ones it can. |
| `allow_generated_data` | boolean | body | Charts only. `false` (the default): a chart is drawn only from values the request gives, and a request without them is refused with 422 DATA_REQUIRED, unbilled. `true`: illustrative values are written when the request gives none, and the answer says `"data": "generated"`. |
| `views` | string[] | body | Views to return beside a valid answer, by name, from the format's `views` in `GET /v1/oracles` (for example `summary`). Each comes back in `views` as text built from `result` alone; `result` is the same with or without them. Free. A view the format does not offer is refused with the ones it does, before anything runs. |

Response 200: The verdict. `status` is `valid` (with `result` and a `verified_by` citation), `invalid` (the check refused it — billable, because the verdict is the product), or `error` (ours, unbilled, retryable).

| Field | Type | Present | Meaning |
|---|---|---|---|
| `oracle` | string | always | Echoed back |
| `format` | string | always | The format actually produced |
| `status` | enum | always | `valid`, `invalid` or `error` |
| `credits` | integer | always | What this call actually cost |
| `request_id` | string | always | The same id as the `x-request-id` header. Quote it in feedback or to support |
| `result` | object | `valid` only | The artifact, in `format` |
| `verified_by` | string | `valid` only | The version of the rules that accepted it |
| `figure` | object | `valid`, from an oracle that draws | Whether the answer can be drawn, and the token to draw it — see Draw a figure |
| `figure.drawable` | boolean | `valid`, from an oracle that draws | Whether the token draws a picture |
| `figure.target` | string | `valid`, from an oracle that draws | What would draw it, such as `vega-lite` or `tikz` |
| `figure.token` | string | when `drawable` | Send it to `POST /v1/figures/render`. Valid for an hour |
| `figure.reason` | string | when not `drawable` | Why it cannot be drawn |
| `intent` | object | `valid`, sometimes | Whether the figure shows what was asked: `passed` (boolean), `faithful` (0–1) and `problems` (strings). `vector-graphics` only, and present only when that check ran |
| `data` | enum | `valid` charts | `supplied`: every value came from the request. `generated`: Forma wrote illustrative values, which you allowed with `allow_generated_data` |
| `reason` | enum | `invalid` only | `unsupported_feature`, `chart_type_mismatch` or `no_valid_answer`; `detail` says the same in a sentence |
| `detail` | string | `invalid`, `error` | Plain-language explanation |
| `retryable` | boolean | `error` only | Always `true` when present |
| `views` | object | `valid`, when `views` were asked for | Each view asked for, by name, as text built from `result` alone; `null` if it could not be built. `result` is the same with or without it |

Errors (plus 401 `KEY_MISSING`, `KEY_UNKNOWN`, `KEY_REVOKED`, `KEY_EXPIRED` and 403 `ORG_DISABLED` on every endpoint):

| Status | Code | When |
|---|---|---|
| 404 | `NOT_FOUND` | No such oracle, or no organisation for this key |
| 422 | `VALIDATION_FAILED` | The body is missing `intent`, or it is empty or over 4000 characters; or a `format` this oracle cannot produce (`context.available` lists the ones it can). `context.parameter` names the field |
| 422 | `DATA_REQUIRED` | A chart request does not give the values to draw, and `allow_generated_data` is not `true`. Not billed |
| 402 | `QUOTA_EXCEEDED` | Not enough credits for the call; see Limits and credits |
| 200 | — | `status: "error"` — the oracle could not answer. Not billed; retry |

## Example response

```json
{
  "oracle": "chart",
  "format": "vega-lite",
  "status": "valid",
  "credits": 1,
  "request_id": "req_01K…",
  "result": { "$schema": "https://vega.github.io/schema/vega-lite/v6.json", "mark": "bar" },
  "verified_by": "chart@6baf09f0",
  "figure": { "drawable": true, "target": "vega-lite", "token": "v2.dmVnYS1saXRl…" },
  "data": "supplied"
}
```

`status` is a verdict, not a health check — all three arrive with HTTP `200`.
See [Verdicts](https://docs.formalogic.io/verdicts) for which are billed and which are worth retrying.

## Notes

**A chart is drawn from the values you give.** Forma does not fetch or look up
data. A chart request with no values in it is refused with `422 DATA_REQUIRED`
before anything runs; one whose chart would need values the request does not
contain is refused the same way. Neither is billed. Send
`"allow_generated_data": true` to accept illustrative values instead. Every valid
chart says which it is in `data`: `supplied` or `generated`. A chart with no
values at all is refused even with the flag. `vector-graphics` and `coverage-policy` ignore the flag.

**Ask for a view to get the answer as text as well.** Send `"views": ["summary"]`
to an oracle format that lists `summary` under `views` in
[List oracles](https://docs.formalogic.io/list-oracles), and a `valid` answer carries `views.summary`: the
answer written out as plain text, built from `result` and nothing else. `result`
is the same with or without it, and it costs nothing more. A view the format
does not offer is refused with `422` before anything runs, naming the ones it
does; nothing is billed.

**`valid` is about the answer, not the world.** It means the output passed its
format's checks. It does not mean the numbers are true; `data` says where they
came from.

**A chart of a different kind is not an answer.** Ask for a candlestick and get a
line, and the verdict is `invalid` with `reason: "chart_type_mismatch"`. Billed
like any `invalid`. Try another format: not every format can draw every kind.

**`format` is read from the body and from nowhere else.** The API never consults
an environment variable, so a request sent by `curl` and the same request sent by
the [MCP tools](https://docs.formalogic.io/mcp) mean the same thing.

**Allow 120 seconds.** Reaching a verdict can take that long. No streaming, no
polling endpoint.

**One ask is one billable unit**, however long it took.

**There is no idempotency key.** Two identical requests are two asks and two
charges, and because the same intent can return a different artifact they may
differ — see [Determinism](https://docs.formalogic.io/citations#determinism). If a retry must not
double-charge, store the first verdict and check for it before asking again.
