# 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 require a signed-in account. API, CLI, and MCP conversion requires authentication; supported Web inputs may use a bounded guest preview, with sign-in and account settlement required before download. 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"
```

Signed-in conversion 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-dense PDFs run through the standard conversion pipeline, which routes scanned and complex
pages to the Markovo OCR engine that renders equations as reviewable LaTeX. The legacy
`layout_fidelity=formula` option is still accepted for API compatibility and is handled as standard
conversion at the normal page rate — no separate formula charge applies. 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 "max_credit_units=5000"
```

URL to Markdown uses a JSON estimate endpoint. Supported sources include public HTML/text, public Google Docs, Notion pages and bounded databases, GitHub text resources and discussions, Hacker News threads, and downloadable YouTube videos. Every entry point authorizes a specific public source before fetching it; the sandbox sends no browser cookies or customer credentials and follows bounded public redirects and content rules. Private or login-only sources, Tencent Docs account connections, browser-only pages, generic binary downloads, and remote document images are not supported. API examples use ordinary HTTP clients, not a separate Markovo SDK:

```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
```

Read the estimate, then replace `upl_RETURNED_UPLOAD_ID` below with its returned `upload_id`. Confirm only when `estimated_credit_units` fits your chosen ceiling (15,000 units = 15 Credits):

```bash
curl -X POST "${MARKOVO_BASE_URL}/v1/jobs" \
  -H "Authorization: Bearer ${MARKOVO_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"upload_id":"upl_RETURNED_UPLOAD_ID","max_credit_units":15000}'
```

Ordinary public documents retain the URL fetch-time and word-count rate. YouTube uses `audio-to-markdown` billing at 0.5 Credits per decoded minute, minimum 0.3 Credit, with limits of one hour and 32 MB plus the account upload limit. Captions are not required; video frames are not analyzed. Do not put private, signed, credential-bearing, or access-controlled URLs into this route.

Web guests receive `job_token` with an anonymous URL estimate. The Web flow submits `{"estimates":[{"upload_id":"...","job_token":"..."}]}` to `POST /v1/guest-preview-sessions`, keeps the returned `session_token` for claim after sign-in, and uses `x-markovo-job-token` to poll jobs and view previews. The preview queue has a 5-Credit estimated ceiling and daily limits; download requires sign-in and settlement from account Credits. API-key examples above use authenticated direct confirmation.

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 and Video both use 0.5 Credits per minute, 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 conversion 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. An expired or revoked grant URL returns not found;
request a new grant only while the owned job and asset still exist.

```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 --force-reinstall "https://markovo.net/downloads/markovo-0.1.1-py3-none-any.whl#sha256=4ded66971bb796a692e33b3f37d719ffcf62e58bf7f1361d0a7a26602de010a1"
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
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

For a hosted client, connect `https://markovo.net/mcp` as a Streamable HTTP
server. The client uses OAuth 2.1 Authorization Code with PKCE, opens Markovo's
sign-in and consent screens, and receives only approved account scopes. Do not
create or paste an API key for the remote connection.

Remote MCP exposes nine tools for capabilities, Credit usage, owned jobs,
public-HTTPS URL conversion, verified result images, short-lived asset links,
and billing help. It cannot read a local file and exposes no shell, credential,
payment, or administrator tool. `markovo_convert_url` requires a positive
`max_credit_units` cap. Markovo never tops up Credits or changes a plan
automatically; a low-balance result points the account owner to
`https://markovo.net/app#billing`. See `https://markovo.net/docs/mcp`.

### Local stdio MCP

Local stdio MCP uses `MARKOVO_API_KEY`. Its `markovo_convert_url` tool takes `url`, `out_dir` within `MARKOVO_MCP_ROOT`, `accept_remote_fetch: true`, and `max_credits` in Credits. Remote OAuth MCP instead uses the required positive integer `max_credit_units` and cannot read local files.

The customer MCP server exposes these exact 11 `tools/list` names:

- `markovo_convert`
- `markovo_convert_url`
- `markovo_job_status`
- `markovo_job_assets`
- `markovo_asset_url`
- `markovo_asset_revoke`
- `markovo_usage`
- `markovo_capabilities`
- `markovo_billing`
- `markovo_bundle_verify`
- `markovo_doctor`

It provides 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_API_KEY": "${MARKOVO_API_KEY}",
        "MARKOVO_MCP_ROOT": "/path/to/dedicated/project"
      }
    }
  }
}
```

The human CLI and local stdio MCP use a **15-Credit convenience safety ceiling** when the caller omits the maximum. Markovo obtains an estimate internally before job creation and rejects work estimated above that ceiling; the client does not pause for user confirmation. Set an **explicit bound for deliberate automation**. REST remains explicit, as do direct programmatic client calls. MCP, CLI, and HTTP API all settle against the same `conversion.credits` ledger.

`markovo_convert` requires local filesystem paths (`input_path` and `out_dir`) inside the explicitly configured `MARKOVO_MCP_ROOT`. File tools fail closed when the root is missing and reject parent-directory, absolute-path, and symlink escapes. API keys and the service origin are never MCP tool arguments. Agents without a mounted disk or without access to the source file inside that dedicated root must use REST multipart upload instead. A Word conversion uses the same customer capability contract:

```json
{
  "input_path": "/absolute/path/report.docx",
  "out_dir": "/absolute/path/runs/report",
  "capability_id": "docx-to-markdown",
  "max_credits": 10
}
```

## Output contract

A completed conversion can produce:

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

For a completed Job, `GET /v1/jobs/{job_id}/source-map` returns the
customer-safe page and block provenance used by the browser review workbench.
It uses the same account API key or anonymous `x-markovo-job-token` ownership
check as the result downloads and omits internal providers, routes, paths,
hashes, statistics, and arbitrary metadata. The original source file is not
retained by this endpoint.

## 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`
