# Draw a figure

> Turn an answer into a picture. Free, and only for answers we vouched for.

`POST https://api.formalogic.io/v1/figures/render`

Request:

| Field | Type | Where | Meaning |
|---|---|---|---|
| `token` | string | body, required | The `figure.token` from an answer. |
| `format` | string | body | `svg` or `png`. |

Response 200: The drawn figure: `image/svg+xml`, or `image/png` when you ask for `png`.

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

| Status | Code | When |
|---|---|---|
| 400 | `SIGNATURE_INVALID` | The token is malformed, altered or expired |
| 422 | `VALIDATION_FAILED` | The body is missing `token`, the `format` is unknown, the answer names data it does not carry, or the figure contains something Forma does not produce |
| 501 | `UNSUPPORTED` | Nothing draws this target |
| 503 | `UPSTREAM_UNAVAILABLE` | A figure could not be drawn because of a problem on our side, or drawing is briefly unavailable. The answer is unaffected; retry after a short wait |

Some answers can be drawn. When one can, its response carries a `figure` block:

```json
{
  "status": "valid",
  "result": { "mark": "bar", "data": { "values": [] } },
  "figure": {
    "drawable": true,
    "target": "vega-lite",
    "token": "v2.dmVnYS1saXRlADE3OTAx…"
  }
}
```

Send that `token` here and you get the picture back.

## Why a token and not an id

Nothing is stored. The token **carries** the artifact — it is the answer plus a
signature proving we produced it — so there is no record to look up and no copy
of your output kept on our side. It expires an hour after the answer.

It also means you never have to rebuild anything. Pass the token through
unchanged; reconstructing the source yourself would have to match the signed
bytes exactly, and a single space in the wrong place is indistinguishable from a
forgery.

## Formats

| `format` | | |
|---|---|---|
| `svg` | the default | vector — what a browser or a document wants |
| `png` | raster | what a model can look at; SVG is not something an agent can read |

The MCP `show_figure` tool defaults to `png` for that reason: its reader is a
model.

## What draws

`vega-lite`, `tikz`, `pictex`, `metapost` and `gnuplot`. TeX-family figures come
back with their text as outlines, so the picture needs no fonts. Anything else
is a `501` naming the target.

## What a figure may contain

A figure is not a document you author — it is output Forma returned, handed
back to us. So the renderer checks that it *is* that: every command in it has to
be one Forma's own output can contain. Anything else is refused by name rather
than run.

That is stricter than it may sound, and deliberately so. TeX is a programming
language, MetaPost can read files, and gnuplot will run a shell command given a
backtick in a chart title. Rather than scan for dangerous constructs — which
fails, because TeX can build any command out of characters — the renderer starts
from the short list of things Forma's output contains and refuses the rest.

The practical consequence: figures Forma returned draw, and hand-written TeX
does not. If you want to typeset your own document, take the source from the
answer and run it yourself — the `tikz` output is a fragment that drops straight
into a `tikzpicture` environment.

## Three states, not two

`drawable` answers a question with three honest outcomes, and the `reason`
says which:

| | |
|---|---|
| `drawable: true` | there is a picture, and the token draws it |
| no renderer | nothing can draw this target — some targets produce data, not pictures |
| not drawn, with a reason | the answer names data it does not carry |

That last one is deliberate. A chart whose data is
`{"url": "sales.csv"}` is pointing at a file we have never seen, so there is
nothing truthful to draw — and we do not go and fetch it. The same applies to a
chart that generates its own rows rather than carrying them. Put the numbers in
your intent and the chart carries them, and draws.

## Free

Drawing costs no credits. You already paid for the answer; what you display it
with should not change the price.
