Core concepts

What an oracle is, what is available to you, and why the names are what they are.

An oracle takes a request in plain language and returns an artifact that has passed its checks. It is the unit you buy, the unit you call and the unit you are billed for. Today chart, vector-graphics and coverage-policy each cost 1 credit per answer, valid or invalid; an error is free.

Available to you

The console lists the oracles your organisation can call, read from the deployment rather than written here — so it cannot describe an oracle that is not actually served.

Real answers from chart, as they were returned:

AskedA candlestick chart of a stock over 20 trading days: a thin rule from low to high and a bar from open to close for each day, green when close is above open and red when below. The price axis must not start at zero; scale it to the price range (about 95 to 125) so the candles are tall and readable.
What came back for: A candlestick chart of a stock over 20 trading days: a thin rule from low to high and a bar from open to close for each day, green when close is above open and red when below. The price axis must not start at zero; scale it to the price range (about 95 to 125) so the candles are tall and readable.What came back for: A candlestick chart of a stock over 20 trading days: a thin rule from low to high and a bar from open to close for each day, green when close is above open and red when below. The price axis must not start at zero; scale it to the price range (about 95 to 125) so the candles are tall and readable.

The result exactly as returned, in vega-lite — the source chart@6baf09f0 was checked against.

result · vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v6.json",
  "name": "stock_candlestick",
  "data": {
    "values": [
      {
        "day": "2026-01-05",
        "low": 98,
        "open": 100,
        "close": 104,
        "high": 107,
        "direction": "up"
      },
      {
        "day": "2026-01-06",
        "low": 101,
        "open": 104,
        "close": 102,
        "high": 106,
        "direction": "down"
      },
      {
        "day": "2026-01-07",
        "low": 99,
        "open": 102,
        "close": 106,
        "high": 109,
        "direction": "up"
      },
      {
        "day": "2026-01-08",
        "low": 104,
        "open": 106,
        "close": 110,
        "high": 112,
        "direction": "up"
      },
      {
        "day": "2026-01-09",
        "low": 107,
        "open": 110,
        "close": 108,
        "high": 113,
        "direction": "down"
      },
      {
        "day": "2026-01-12",
        "low": 105,
        "open": 108,
        "close": 113,
        "high": 116,
        "direction": "up"
      },
      {
        "day": "2026-01-13",
        "low": 110,
        "open": 113,
        "close": 111,
        "high": 115,
        "direction": "down"
      },
      {
        "day": "2026-01-14",
        "low": 108,
        "open": 111,
        "close": 116,
        "high": 119,
        "direction": "up"
      },
      {
        "day": "2026-01-15",
        "low": 113,
        "open": 116,
        "close": 118,
        "high": 121,
        "direction": "up"
      },
      {
        "day": "2026-01-16",
        "low": 115,
        "open": 118,
        "close": 114,
        "high": 120,
        "direction": "down"
      },
      {
        "day": "2026-01-19",
        "low": 111,
        "open": 114,
        "close": 117,
        "high": 119,
        "direction": "up"
      },
      {
        "day": "2026-01-20",
        "low": 114,
        "open": 117,
        "close": 121,
        "high": 124,
        "direction": "up"
      },
      {
        "day": "2026-01-21",
        "low": 118,
        "open": 121,
        "close": 119,
        "high": 123,
        "direction": "down"
      },
      {
        "day": "2026-01-22",
        "low": 116,
        "open": 119,
        "close": 115,
        "high": 122,
        "direction": "down"
      },
      {
        "day": "2026-01-23",
        "low": 112,
        "open": 115,
        "close": 118,
        "high": 120,
        "direction": "up"
      },
      {
        "day": "2026-01-26",
        "low": 115,
        "open": 118,
        "close": 122,
        "high": 125,
        "direction": "up"
      },
      {
        "day": "2026-01-27",
        "low": 119,
        "open": 122,
        "close": 120,
        "high": 124,
        "direction": "down"
      },
      {
        "day": "2026-01-28",
        "low": 117,
        "open": 120,
        "close": 116,
        "high": 121,
        "direction": "down"
      },
      {
        "day": "2026-01-29",
        "low": 113,
        "open": 116,
        "close": 119,
        "high": 121,
        "direction": "up"
      },
      {
        "day": "2026-01-30",
        "low": 116,
        "open": 119,
        "close": 123,
        "high": 125,
        "direction": "up"
      }
    ]
  },
  "title": "20-Day Stock Candlestick Chart",
  "layer": [
    {
      "mark": {
        "type": "rule",
        "size": 1
      },
      "encoding": {
        "x": {
          "field": "day",
          "type": "ordinal"
        },
        "y": {
          "field": "low",
          "type": "quantitative",
          "scale": {
            "zero": false,
            "domain": [
              95,
              125
            ]
          },
          "axis": {
            "title": "Price"
          }
        },
        "y2": {
          "field": "high",
          "type": "quantitative"
        }
      }
    },
    {
      "mark": "bar",
      "encoding": {
        "x": {
          "field": "day",
          "type": "ordinal"
        },
        "y": {
          "field": "open",
          "type": "quantitative",
          "scale": {
            "zero": false,
            "domain": [
              95,
              125
            ]
          },
          "axis": {
            "title": "Price"
          }
        },
        "y2": {
          "field": "close",
          "type": "quantitative"
        },
        "color": {
          "field": "direction",
          "type": "nominal",
          "scale": {
            "domain": [
              "up",
              "down"
            ],
            "range": [
              "green",
              "red"
            ]
          }
        }
      }
    }
  ]
}
validverified_by chart@6baf09f0chart · vega-lite · 2026-09-28

The same list over HTTP is GET /v1/oracles.

Names are domain words

You ask the chart oracle, the vector-graphics oracle or the coverage-policy oracle — named for what you want, not for what produces it.

That is a deliberate boundary. You are buying an answer about charts. What sits behind the name is ours to change, and nothing about it appears in the name or in the response, so an integration of yours cannot come to depend on it and then break when we improve it.

Formats

An oracle declares the formats it can produce. Pass format to choose one; omit it for default_format.

Asking for a format an oracle cannot produce is refused and the refusal names the ones it can. That is the one error an agent can act on without a human, which is why it is shaped that way rather than as a bare rejection.

A format is not always a superset of the others

Formats of the same oracle are not equally expressive. A request that succeeds for one can be refused for another — the refusal is about what that format can express, never about whether your request was reasonable.

chart is the live example. vega-lite expresses the most; gnuplot expresses less, and refuses rather than quietly dropping what it cannot carry — time axes, values it would have to fetch rather than be given, and transformations that happen before plotting rather than during it.

plotly, echarts and chartjs return a document for that library — a Plotly figure, an ECharts option, a Chart.js configuration — with every number already computed, so the same chart shows the same values in all of them. Because the numbers are computed before you receive them, all three need the data inline and refuse a chart whose values are at an address. Beyond that:

Format Also refuses
plotly trails, maps, images, small multiples (row, column) of anything but bars, lines, areas and points or split by dates or numbers, a radial value that is negative or falls outside the circle, an arc layered with another mark or with arcs on other scales, a shape on anything but a point or read from an ordered field, a size on anything but a point, an opacity on anything but a point or a bar, a size or opacity read from unordered categories, a range of colours on a numeric colour, colour on two channels at once, and a range between dates written with a time zone
echarts the same, axis format strings, a bar or band running between two dates, and a bar ranged along x unless y is a category
chartjs the same as echarts (except bars ranged along x), and box plots, heatmaps, text labels, a colour scale, a line across the whole plot, a range along a date axis, a second view or a band beside a colour legend (except a layer of arcs), an rgb() or hsl() colour on anything drawn below full opacity, and small multiples

Where a format cannot carry something without refusing the whole chart, the answer says so as a warning: echarts and chartjs take their width and height from the page, and chartjs draws a time axis as categories.

A refusal of this kind still costs a credit. Deciding that something cannot be rendered is the work — the same work as deciding it can. The table above is here so that is a known limit rather than something you discover one credit at a time.

vector-graphics has the same shape across its three formats. tikz and metapost express the most; pictex expresses less and refuses rather than approximating — it has no arbitrary stroke colours, no fills, and no coloured text, and it names which of those you asked for.

vector-graphics computes the geometry

Describe a figure by what it is, not by where each mark goes: the midpoint of two points, where two lines meet, a point partway along a ray, a circle tangent to a line, a line perpendicular to another, a length equal to a distance. The coordinates in the TikZ you get back are computed for you.

Say in your intent what the figure must show — three points collinear, two lines perpendicular — and it is checked. A figure that cannot show it comes back invalid, naming what failed and by how much, rather than looking right and being wrong.

A returned figure is fitted to its canvas, with labels clear of the lines, trees and rows of boxes laid out from their text, and one consistent style: construction lines in grey, extensions dashed, the subject in a heavier line, fills tinted with matching outlines.

Some figures need things vector-graphics does not draw: colour gradients, embedded images, animation, fill patterns, transparency or shading. A request that cannot be drawn without one is answered invalid with detail naming the feature, instead of an approximation. See Verdicts.

vector-graphics returns source, not a picture

The chart formats return a document something else can draw. The vector-graphics formats return TeX-family source — tikz, pictex and metapost are all things you typeset. You get a fragment to place in a document, not an image. To see it drawn, a valid answer carries a figure token — see Draw a figure.

Two real answers from vector-graphics, with the TikZ each one returned:

AskedA 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.
What came back for: 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.

The result exactly as returned, in tikz — the source vector-graphics@c738bf88 was checked against.

result · tikz
\begin{tikzpicture}[x=1pt,y=1pt]
  \path[use as bounding box] (0,0) rectangle (294.56,234.83);
  \draw[draw=black!45,line width=0.4pt] (39.75,20.5) -- (39.75,43.25) -- (17,43.25);
  \draw[draw=black,line width=0.6pt,fill=none] (17,20.5) -- (277,20.5) -- (17,215.5) -- cycle;
  \draw[draw=black,line width=0.6pt] (17,20.5) -- (277,20.5);
  \draw[draw=black,line width=0.6pt] (17,20.5) -- (17,215.5);
  \draw[draw=black,line width=0.6pt] (277,20.5) -- (17,215.5);
  \filldraw[draw=none,line width=0.4pt,fill=black] (17,20.5) circle[radius=1.3pt];
  \filldraw[draw=none,line width=0.4pt,fill=black] (277,20.5) circle[radius=1.3pt];
  \filldraw[draw=none,line width=0.4pt,fill=black] (17,215.5) circle[radius=1.3pt];
  \node[text=black,anchor=center,font=\fontsize{10pt}{12pt}\selectfont] at (11.67,13.42) {$a$};
  \node[text=black,anchor=center,font=\fontsize{10pt}{12pt}\selectfont] at (283.78,20.5) {$b$};
  \node[text=black,anchor=center,font=\fontsize{10pt}{12pt}\selectfont] at (11.95,222.58) {$c$};
  \node[text=black,anchor=center,font=\fontsize{10pt}{12pt}\selectfont] at (147,12.25) {4};
  \node[text=black,anchor=center,font=\fontsize{10pt}{12pt}\selectfont] at (10.5,118) {3};
  \node[text=black,anchor=center,font=\fontsize{10pt}{12pt}\selectfont] at (151.9,125.45) {5};
\end{tikzpicture}
validverified_by vector-graphics@c738bf88vector-graphics · tikz · 2026-09-30

coverage-policy answers questions about a coverage policy, as JSON

coverage-policy takes a question about the Medicare Local Coverage Determination for total knee arthroplasty (LCD L36575, version 21), such as "Can I get a knee replacement if I'm under 65?", and returns the parts of that policy that answer it: the rules, the named conditions and statements they rely on, and the policy passage each part comes from. Every entry in an answer is taken from the policy; nothing is written for the question. It records what the policy says; it does not decide whether a patient qualifies.

Its format, lookup, is a document: result is an object, and a valid answer carries no figure block. The object's keys are policy, review, asked, not_found, scope, rules, conditions, statements, reason_topics, notes, passages, examples, open_questions, other_documents and source_passages.

  • asked says, in words, which parts of the policy were looked up.
  • not_found lists the searches that found no entry and the questions no rule answers. Search matches exact words, so a search for "X-rays" finds nothing in a policy that says "radiography". A question about something the policy is silent on, such as an age limit, gets an entry here rather than a guess.
  • An entry names the passages it cites under sources, by id, and source_passages holds each passage's pages, section and words.
  • review says how far the policy's representation has been reviewed. Today it is a draft that has not had clinical review.

Send "views": ["summary"] with the question to also get the answer as text a person can read, in views.summary. It is built from result alone: each rule and condition by name with its pages, the lists of what must be true, the policy's own wording quoted, what the policy does not mention, and the review note. It never names an entry by its id. For "Can I get a knee replacement if I'm under 65?" it begins:

text
From Total Knee Arthroplasty (LCD L36575 version 21; revision effective 2025-11-06).

The policy does not mention "age" or "65".

Indications for a first total replacement (indication, pages 7, 8)
The policy says: "Noridian will consider total knee replacement surgery medically necessary in the following circumstances:"
For a first total knee replacement.
It applies when any one of these is true:
- The advanced joint disease pathway (explained below)
- Failure of a previous osteotomy
- Distal femur fracture

A question about another policy or another procedure is still answered from this one policy: the answer is what LCD L36575 says, which may not be what was asked about.

Callers that send format: "json" still get the earlier answer for a passage of a policy (its scope, rules, conditions and statements). That format is no longer listed and will be retired.

What result holds

A format that produces a document returns it decoded — result is an object. A format that produces a script or plain text returns it as a string. You already know which, because you named the format and the response echoes it back beside the result.

Every plan, every oracle

Nothing is gated by tier. An agent that cannot rely on a tool being there stops reaching for it, so what a plan buys is volume, never access.

New oracles

They appear in GET /v1/oracles and in the console — with no change on your side. If your integration reads the catalogue at start-up rather than hardcoding names, that is all it takes.