
# Document conversion

Convert a document between markup and structured formats from a single API call — `markdown`, `html`, `docx`, `odt`, `rtf`, `epub`, `latex`, `rst`, `org`, `textile`, `mediawiki`, `plain`, plus slide decks (`pptx`), Jupyter notebooks (`ipynb`), `asciidoc`, GitHub-flavored markdown (`gfm`), `jira` markup, and `typst`. Backed by `pandoc` on a dedicated worker; priced per MB of input, and the conversion matrix itself is free to query so you can check a from→to pair before you pay.

```bash
curl -X POST https://api.relaystation.ai/v1/doc/convert \
  -H 'Authorization: Bearer rs_live_<key>' \
  -H 'Idempotency-Key: readme-to-docx-20260611' \
  -H 'Content-Type: application/json' \
  -d '{ "file": { "inline": "<base64 markdown>" }, "from": "markdown", "to": "docx" }'
```

Or on the lodestone path — no account, a signed x402 payment instead of an API key:

```bash
curl -X POST https://api.relaystation.ai/v1/doc/convert \
  -H 'X-Payment: <base64 EIP-3009 authorization>' \
  -H 'Idempotency-Key: readme-to-docx-20260611' \
  -H 'Content-Type: application/json' \
  -d '{ "file": { "inline": "<base64 markdown>" }, "from": "markdown", "to": "docx" }'
```

## Pick your lane

Three converters cover three different jobs — pick by your source:

- **Markup / structured → markup / structured** is this endpoint (`/v1/doc/convert`, pandoc). Markdown ↔ HTML ↔ docx ↔ LaTeX ↔ rst ↔ org ↔ epub and more.
- **Office-binary → PDF** (docx/xlsx/pptx/odt/…) is [`POST /v1/pdf/from-office`](https://cputools.relaystation.ai/docs/pdf-tools) (LibreOffice).
- **HTML → PDF** is [`POST /v1/pdf/from-html`](https://cputools.relaystation.ai/docs/pdf-tools) (headless Chromium).

## Request options

`POST /v1/doc/convert` takes:

| Field | Type | Default | Notes |
|---|---|---|---|
| `file` | input source, required | — | `{ "inline": "<base64>" }` ≤ 4 MiB, or `{ "inputKey": "..." }` from `POST /v1/cputools/upload-url` for larger files — see [passing & receiving files](/docs/receiving-outputs) |
| `filename` | string | — | used to sniff `from` by extension (`.md`, `.docx`, `.tex`, …) when `from` is omitted |
| `from` | string | sniffed from `filename` | source format (`markdown`, `html`, `docx`, …). Neither `from` nor a recognizable `filename` → free `422 SOURCE_FORMAT_REQUIRED` |
| `to` | string, required | — | target format. An unsupported pair → free `422 UNSUPPORTED_CONVERSION` that lists the valid targets for your source |
| `referenceDoc` | input source | — | a branded `docx`/`pptx` template whose styling the output inherits. Same shape as `file` (`{ "inline": "<base64>" }` or `{ "inputKey": "..." }`). **Only valid when `to` is `docx` or `pptx`** — any other target → free `422 REFERENCE_DOC_UNSUPPORTED`; the template must itself be an OOXML/ZIP document → else free `422 REFERENCE_DOC_FORMAT_INVALID` |
| `citations` | object | — | turn on citation processing — `{ "bibliography": <source>, "format"?: "bib"\|"json"\|"yaml", "csl"?: <source> }`. See [Citations & bibliographies](#citations--bibliographies) |
| `options.standalone` | boolean | `true` | produce a complete document (header/wrapper) rather than a fragment; pass `false` for a bare fragment |

**Caps and sandboxing.** Input ≤ 25 MB (`cputools.doc.max_mb`, operator-tunable; over → `413 INPUT_TOO_LARGE`, pre-charge and free). pandoc runs with `--sandbox` on a dedicated worker — a crafted document can't read files or reach the network from inside the conversion. The result comes back in the standard output envelope (inline when small, presigned URL when large) — see [Receiving outputs](/docs/receiving-outputs).

## The conversion matrix — free to query

`GET /v1/doc/formats` returns the exact supported pairs, lossy flags, and the PDF composition path. Free, read-only, no auth:

```bash
curl https://api.relaystation.ai/v1/doc/formats
```

The static table below is a snapshot of that live endpoint — same data, and `GET /v1/doc/formats` is always authoritative (the operator can enable/disable pairs without a redeploy). The ten source formats and where each can go (`*` = lossy):

| From | To |
|---|---|
| `markdown` | html, docx, **pptx**, odt, rtf, epub, latex, rst, org, textile, mediawiki, asciidoc, gfm, jira, typst, plain* |
| `html` | markdown, docx, odt, rtf, epub, latex, rst, org, plain* |
| `docx` | markdown*, html*, odt*, rtf*, epub*, latex*, rst*, org*, plain* |
| `latex` | markdown, html, docx, odt, rst, plain* |
| `odt` | markdown*, html*, docx*, latex*, rst*, plain* |
| `epub` | markdown*, html*, docx*, latex*, plain* |
| `rst` | markdown, html, docx, latex, plain* |
| `org` | markdown, html, docx, latex, plain* |
| `textile` | markdown, html, docx, plain* |
| `mediawiki` | markdown, html, docx, plain* |

New target formats — `pptx`, `ipynb`, `asciidoc`, `gfm`, `jira`, and `typst` — are rolling onto the matrix; `asciidoc` and `jira` are output-only (writer formats, not sources), while `ipynb`, `gfm`, and `typst` work as both source and target. `GET /v1/doc/formats` is always the authoritative, up-to-the-minute list — query it before you build a path.

## Slide decks (markdown → pptx)

Convert structured markdown into a PowerPoint deck (`to: "pptx"`). pandoc maps your document structure onto slides: a level-1 or level-2 heading starts a new slide, and a horizontal rule (`---`) also begins a new slide — so you control the deck layout from plain markdown.

```bash
curl -X POST https://api.relaystation.ai/v1/doc/convert \
  -H 'Authorization: Bearer rs_live_<key>' \
  -H 'Idempotency-Key: outline-to-deck-20260612' \
  -H 'Content-Type: application/json' \
  -d '{ "file": { "inline": "<base64 markdown>" }, "from": "markdown", "to": "pptx" }'
```

## Branded output (reference templates)

For `docx` and `pptx` targets you can supply a `referenceDoc` — your own styled template document — and the converted output inherits its fonts, colors, master slides, and styles. Pass it inline or by storage key, the same way you pass `file`:

```bash
curl -X POST https://api.relaystation.ai/v1/doc/convert \
  -H 'Authorization: Bearer rs_live_<key>' \
  -H 'Idempotency-Key: branded-report-20260612' \
  -H 'Content-Type: application/json' \
  -d '{ "file": { "inline": "<base64 markdown>" }, "from": "markdown", "to": "docx", "referenceDoc": { "inputKey": "<key from upload-url>" } }'
```

A `referenceDoc` is only meaningful for `docx`/`pptx` output — for any other target you get a free `422 REFERENCE_DOC_UNSUPPORTED`. The template itself must be an OOXML document (a `.docx`/`.pptx`, which is a ZIP package); supplying something else (a PDF, an image, a plain-text file) is a free `422 REFERENCE_DOC_FORMAT_INVALID` before you are charged. The template is server-side styling, so it does not add to your bill: you still pay only per MB of the **primary** input.

## Citations & bibliographies

Pass `citations` to turn on pandoc's `--citeproc`: in-document citation markers (`[@smith2020]`, `[@doe2019, pp. 33-35]`) are replaced with formatted citations, and a bibliography section is appended listing the works cited. It works for **any** target format (html, docx, latex, …) — and a document with no citation markup simply comes back with no bibliography (a safe no-op).

```jsonc
{
  "file":  { "inline": "<base64 markdown with [@key] citations>" },
  "from":  "markdown",
  "to":    "html",
  "citations": {
    "bibliography": { "inputKey": "<key from upload-url>" },  // or { "inline": "<base64>" }
    "format": "bib",            // "bib" (BibTeX/BibLaTeX, default) | "json" (CSL-JSON) | "yaml" (CSL-YAML)
    "csl":    { "inline": "<base64 .csl stylesheet>" }        // optional — omit for the default style
  }
}
```

| Field | Type | Default | Notes |
|---|---|---|---|
| `citations.bibliography` | input source, required | — | the reference database — BibTeX/BibLaTeX, CSL-JSON, or CSL-YAML. Same `{ inline }` / `{ inputKey }` shape as `file` |
| `citations.format` | enum | `bib` | the bibliography file's format: `bib`, `json`, or `yaml` |
| `citations.csl` | input source | — | a [CSL](https://citationstyles.org/) stylesheet to control the citation/bibliography style. Omit for the built-in **chicago-author-date** default (embedded — no network) |

Citation files are auxiliary, like a `referenceDoc`: they don't add to your bill — you still pay only per MB of the **primary** input. Everything runs under the same `--sandbox` (no file/network access from inside the document).

## Want a PDF?

pandoc has no PDF writer without a LaTeX engine, so PDF is a two-step composition: convert to HTML here, then render to PDF via [`POST /v1/pdf/from-html`](https://cputools.relaystation.ai/docs/pdf-tools). The matrix response advertises this path under `compositions`. (Citations render in the HTML step, so the composed PDF carries the formatted bibliography too.)

## Billing

One price: **$0.0003 per MB of input** (`cputools.doc.convert`, min 1 MiB). `GET /v1/doc/formats` is free. Charge-on-attempt — a conversion that can't be delivered throws and the wrapper reverses the charge. Every billable call requires an `Idempotency-Key`; a same-key retry returns the cached result without re-charging.

## MCP tools

Callable over MCP at `https://api.relaystation.ai/mcp`: `doc_convert` (billable) and `doc_formats` (free, read-only). Same auth, same prices as the HTTP routes.

## Next

[Quickstart](/docs/quickstart) · [Authentication](/docs/authentication) · [x402 wire format](/docs/x402) · [Media](/docs/media) · [API reference](/api-reference) · PDF + Office tools at [cputools.relaystation.ai](https://cputools.relaystation.ai)
