# Markovo Developer Guide

Markovo converts files and explicitly authorized public HTTPS pages into clean Markdown through one account-metered API. PDF is live; public URL import, DOCX, PPTX, XLSX, TXT, Markdown, JSON, XML, HTML, CSV, PNG, JPEG, WebP, TIFF, BMP, EPUB, Notion Export ZIP, audio, and video audio tracks are authenticated Beta inputs. Every active plan includes API, CLI, and MCP access within its limits; Free includes one active API key.

- Base URL: `https://markovo.net`
- Create or revoke keys: `https://markovo.net/app#developer`
- Capability discovery: `GET /v1/capabilities`
- Pricing catalog: `GET /v1/catalog`
- OpenAPI 3.1 schema: `https://markovo.net/openapi.json`
- API key prefix: `mk_live_`
- Required scope: `markovo:convert`

Never place an API key in browser code, public repositories, prompts, or analytics. Plaintext is shown once when the key is created.

## Environment

```bash
export MARKOVO_BASE_URL="https://markovo.net"
export MARKOVO_API_KEY="mk_live_..."
```

## API

The compatibility shortcut estimates, confirms the Credit ceiling, and queues one or more supported files. New clients should set integer `max_credit_units` (`1 Credit = 1,000 credit_units`) and send the matching `capability_id`. `max_credits` remains compatible with decimal values down to `0.001`.

For retry-safe automation, use `POST /v1/estimates` followed by `POST /v1/jobs`. The returned `upload_id` permanently identifies that confirmation: the first confirmation returns `201`, and an identical replay returns the existing job with `200` without creating a duplicate job or reserving additional Credits. A replay with a different ceiling returns the non-retryable `409 idempotency_conflict` error. The official CLI and MCP client use this two-step flow and only replay the same confirmation after an ambiguous network failure. Do not automatically retry the `/v1/convert` compatibility shortcut after an unknown response.

```bash
curl -X POST "${MARKOVO_BASE_URL}/v1/convert" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}" \
  -F "file=@paper.pdf" \
  -F "capability_id=pdf-to-markdown" \
  -F "mode=fast" \
  -F "max_credit_units=30000"
```

Authenticated Beta example:

```bash
curl -X POST "${MARKOVO_BASE_URL}/v1/convert" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}" \
  -F "file=@notes.md" \
  -F "capability_id=text-to-markdown" \
  -F "mode=fast" \
  -F "max_credit_units=1000"
```

Formula-region Beta is opt-in for signed-in PDFs. Add `layout_fidelity=formula` to API estimates or
use the CLI flag below. Markovo detects bounded formula crops before confirmation, shows the normal
PDF fee plus 0.7 Credit per detected region, and adds no formula fee when no region is found. The
result must be reviewed against the original source.

```bash
curl -X POST "${MARKOVO_BASE_URL}/v1/convert" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}" \
  -F "file=@formula.pdf" \
  -F "capability_id=pdf-to-markdown" \
  -F "layout_fidelity=formula" \
  -F "max_credit_units=5000"
```

URL to Markdown Beta has a separate JSON estimate endpoint because it fetches a remote public page rather than accepting an uploaded file. Every entry point requires explicit consent before network access. The sandbox accepts static public HTTPS HTML/text only, sends no browser cookies or customer credentials, follows bounded public redirects and robots rules, and shows the confirmed maximum before conversion:

```bash
curl -X POST "${MARKOVO_BASE_URL}/v1/url-estimates" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/public-report","accept_remote_fetch":true}'

markovo url-convert https://example.com/public-report \
  --out runs/public-report --max-credits 1 --accept-remote-fetch
```

Use the returned `upload_id` with `POST /v1/jobs` exactly as for a file estimate. Do not put private, signed, credential-bearing, or access-controlled URLs into this route.

CSV uses the same flow. Active Credit v2 billing has a 0.07 Credit minimum and then follows measured non-empty cells at 0.05 Credit per 2,000 cells with unit-level rounding:

```bash
curl -X POST "${MARKOVO_BASE_URL}/v1/convert" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}" \
  -F "file=@people.csv" \
  -F "capability_id=csv-to-markdown" \
  -F "mode=fast" \
  -F "max_credit_units=1000"
```

Image OCR uses the same account and History, preserves the source image in the bundle, and bills 0.5 Credit per actual OCR page. The server may choose the best conversion method without changing the confirmed price:

```bash
curl -X POST "${MARKOVO_BASE_URL}/v1/convert" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}" \
  -F "file=@scan.png" \
  -F "capability_id=image-to-markdown" \
  -F "mode=fast" \
  -F "max_credit_units=3000"
```

Audio accepts MP3, WAV, M4A, AAC, FLAC, OGG, OPUS, and WMA. Video accepts MP4, MOV, M4V, WebM, MKV, AVI, and WMV and transcribes only its audio track. Active Credit v2 billing follows actual decoded milliseconds: Audio uses 1 Credit per minute and Video uses 4 Credits per minute, both with a 0.3 Credit minimum, a 50 MiB limit, and a 60-minute limit. The server decodes duration before confirmation, reserves the exact estimate, then settles verified duration without exceeding the confirmed ceiling. Failed jobs spend nothing.

```bash
curl -X POST "${MARKOVO_BASE_URL}/v1/convert" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}" \
  -F "file=@meeting.mp3" \
  -F "capability_id=audio-to-markdown" \
  -F "mode=fast" \
  -F "max_credit_units=60000"

curl -X POST "${MARKOVO_BASE_URL}/v1/convert" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}" \
  -F "file=@recording.mp4" \
  -F "capability_id=video-to-markdown" \
  -F "mode=fast" \
  -F "max_credit_units=60000"
```

The transcript includes timestamps, detected language, decoded duration, and actual Credits. This Beta does not analyze video frames, identify speakers, or guarantee a verbatim transcript.

Office files use the same bounded flow. Active Credit v2 billing is 0.1 Credit per measured DOCX page or detected PPTX slide:

```bash
markovo convert report.docx --out runs/report --max-credits 10
markovo convert deck.pptx --out runs/deck --max-credits 3
```

Modern XLSX workbooks have a 0.11 Credit minimum, then use 0.05 Credit per 2,000 measured non-empty cells with unit-level rounding. Formulas remain text; Markovo does not calculate them or follow external links:

```bash
markovo convert workbook.xlsx --out runs/workbook --max-credits 5
```

Poll the returned `job_id`, then download Markdown:

```bash
curl "${MARKOVO_BASE_URL}/v1/jobs/{job_id}" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}"

curl -L "${MARKOVO_BASE_URL}/v1/jobs/{job_id}/download?format=md" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}" \
  -o output.md
```

For explicit cost confirmation, use `POST /v1/estimates` followed by `POST /v1/jobs`. `GET /v1/jobs` lists only the current account's jobs. Job lookup, downloads, quality reports, and deletion enforce account ownership.

To give an AI client temporary online access to one extracted image, first list
the manifest-verified assets, then create a 60–600 second grant. The URL is a
bearer capability: do not log, forward, or persist it. It can be revoked early;
the underlying object storage is never exposed.

```bash
curl "${MARKOVO_BASE_URL}/v1/jobs/{job_id}/assets" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}"

curl -X POST "${MARKOVO_BASE_URL}/v1/jobs/{job_id}/asset-grants" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"asset_path":"assets/chart.png","expires_in_seconds":300}'
```

## CLI

Install the pinned client and use the same API key and Credit balance:

```bash
python -m pip install "https://markovo.net/downloads/markovo-0.1.0-py3-none-any.whl"
markovo doctor --json
markovo convert paper.pdf --out runs/paper --mode fast --max-credits 30
markovo convert notes.md --out runs/notes --mode fast --max-credits 5
markovo convert people.csv --out runs/people --mode fast --max-credits 5
markovo convert scan.png --out runs/scan --mode fast --max-credits 3
markovo convert report.docx --out runs/report --mode fast --max-credits 10
markovo convert deck.pptx --out runs/deck --mode fast --max-credits 3
markovo convert workbook.xlsx --out runs/workbook --mode fast --max-credits 5
markovo convert meeting.mp3 --out runs/meeting --mode fast --max-credits 60
markovo convert recording.mp4 --out runs/recording --mode fast --max-credits 60
markovo convert formula.pdf --out runs/formula --max-credits 5 --layout-fidelity formula
markovo assets {job_id}
markovo asset-url {job_id} assets/chart.png --expires-in-seconds 300
markovo asset-revoke {job_id} {grant_id}
markovo usage
markovo billing
```

`markovo billing` returns a secure browser URL. Customer conversion uses the remote product API; local development tools require an explicit development flag.

## MCP

The MCP server exposes remote conversion, job status, temporary Bundle image
access, usage, billing, and diagnostics over stdio. Use
`markovo_job_assets` → `markovo_asset_url`; revoke early with
`markovo_asset_revoke`.

```json
{
  "mcpServers": {
    "markovo": {
      "command": "markovo-mcp",
      "env": {
        "MARKOVO_BASE_URL": "https://markovo.net",
        "MARKOVO_API_KEY": "${MARKOVO_API_KEY}"
      }
    }
  }
}
```

Every conversion tool call must set a Credit ceiling. The MCP, CLI, and HTTP API all settle against the same `conversion.credits` ledger.

## Output contract

A completed conversion can produce:

- `output.md`
- `assets/`
- `metadata.json`
- `source_map.json`
- `quality_report.json`
- `bundle.zip`

## Errors

Public errors contain `code`, `message`, `request_id`, `retryable`, and `docs_url`. Retry only when `retryable` is true. Do not retry quota, ownership, validation, or Credit-ceiling errors without changing the request.

## AI-readable references

- Full public docs: `https://markovo.net/docs`
- Capability status and roadmap: `https://markovo.net/capabilities.md`
- AI integration contract: `https://markovo.net/ai-native.md`
- Site index for language models: `https://markovo.net/llms.txt`
- Machine-readable HTTP contract: `https://markovo.net/openapi.json`
