Endpoints

Ask an oracle

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

POSThttps://api.formalogic.io/v1/oracles/{name}/ask

Ask an oracle for an artifact, and get back a verdict on it.

Request

namestringpath, requiredAn oracle name from GET /v1/oracles.
intentstringbody, requiredWhat you want, in plain language. Not a template and not a specification — describe the thing, not how to build it.

min length 1 · max length 4000

formatstring | nullbodyOne 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_databooleanbodyCharts 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".
viewsstring[]bodyViews 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.
curl -X POST https://api.formalogic.io/v1/oracles/chart/ask \
  -H "Authorization: Bearer $FORMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"intent":"grouped bar chart of quarterly sales, Q1-Q4: North 120, 135, 150, 160; South 90, 110, 105, 130"}'

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).

oraclestringEchoed back
formatstringThe format actually produced
statusenumvalid, invalid or error
creditsintegerWhat this call actually cost
request_idstringThe same id as the x-request-id header. Quote it in feedback or to support
resultobjectvalid onlyThe artifact, in format
verified_bystringvalid onlyThe version of the rules that accepted it
figureobjectvalid, from an oracle that drawsWhether the answer can be drawn, and the token to draw it — see Draw a figure
figure.drawablebooleanvalid, from an oracle that drawsWhether the token draws a picture
figure.targetstringvalid, from an oracle that drawsWhat would draw it, such as vega-lite or tikz
figure.tokenstringwhen drawableSend it to POST /v1/figures/render. Valid for an hour
figure.reasonstringwhen not drawableWhy it cannot be drawn
intentobjectvalid, sometimesWhether the figure shows what was asked: passed (boolean), faithful (0–1) and problems (strings). vector-graphics only, and present only when that check ran
dataenumvalid chartssupplied: every value came from the request. generated: Forma wrote illustrative values, which you allowed with allow_generated_data
reasonenuminvalid onlyunsupported_feature, chart_type_mismatch or no_valid_answer; detail says the same in a sentence
detailstringinvalid, errorPlain-language explanation
retryablebooleanerror onlyAlways true when present
viewsobjectvalid, when views were asked forEach 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

401KEY_MISSINGKEY_UNKNOWNKEY_REVOKEDKEY_EXPIREDThe key is missing, unknown, revoked or expired — see Authentication
403ORG_DISABLEDThe key is fine and its organisation is not active — see Authentication
404NOT_FOUNDNo such oracle, or no organisation for this key
422VALIDATION_FAILEDThe 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
422DATA_REQUIREDA chart request does not give the values to draw, and allow_generated_data is not true. Not billed
402QUOTA_EXCEEDEDNot enough credits for the call; see Limits and credits
200no codestatus: "error" — the oracle could not answer. Not billed; retry

Every code, and what to do about it, is on Errors.

The request is generated from the API's OpenAPI document; the response and errors are held to real responses and to the Errors page.

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 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, 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 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. If a retry must not double-charge, store the first verdict and check for it before asking again.