# Sync Markdown

> Page: https://docs.plotwork.dev/api/post-v1-markdown-sync. Index of every page: https://docs.plotwork.dev/llms.txt

Turn a document's diagram fences into pictures.

`POST /v1/markdown/sync`

## TL;DR

| Question | Answer |
|---|---|
| What does it do? | Turn a document's diagram fences into pictures. |
| Which tool does the same? | [`sync_markdown`](https://docs.plotwork.dev/mcp/tools/sync-markdown.md) |
| What does it take? | A JSON body of type [`SyncMarkdownInput`](https://docs.plotwork.dev/api/schemas.md#syncmarkdowninput). |
| What does it return? | Status 200 with a body of type [`SyncMarkdownResponse`](https://docs.plotwork.dev/api/schemas.md#syncmarkdownresponse). |
| What can go wrong? | `400` the body failed schema validation; `401` the api key is missing or not valid; `403` the account is on the free plan, which does not include the rest drawing and checking routes (plan.rest_not_included); `413` the body is larger than the size cap; `429` too many requests. Every failure uses the one error envelope. |

## What it does

Takes a whole Markdown document and its path and answers with the rewritten document (every diagram fence wrapped in a collapsed source section with its picture and a text description), the SVG files to write, and a report per fence with errors that carry line numbers counted in the document sent. With convertMermaid set, every Mermaid fence that can be converted becomes a diagram fence first; the others are left exactly as they were and reported. The route reads and writes no file: the caller writes the returned document and files. A fence with errors is a normal 200 result with status failed in the report. Mode check changes nothing and only reports. Prefer this route over converting, validating, and rendering each fence yourself.

## Request body

The body has the type [`SyncMarkdownInput`](https://docs.plotwork.dev/api/schemas.md#syncmarkdowninput).

| Field | Type | Required | Description |
|---|---|---|---|
| `markdown` | `string` | yes | The whole Markdown document, exactly as it is on disk, with its line endings. Up to 10000000 characters. at least 1 character; at most 10000000 characters. |
| `documentPath` | `string` | yes | Where the document lives, relative to the repository root, with forward slashes, for example docs/guide.md. Picture links and file paths are worked out from it. at least 1 character; at most 300 characters. |
| `assetsDir` | `string` | no | The directory for the SVG files, relative to the repository root. The default is assets/diagrams next to the document. at least 1 character; at most 300 characters. |
| `prefix` | `string` | no | Text put in front of every picture file name, for documents that share one assets directory. By default a titled diagram is named from its title and an untitled one from the document name. at least 1 character; at most 32 characters. |
| `convertMermaid` | `boolean` | no | When true, every ```mermaid fence that can be converted is replaced by a ```diagram fence first, then drawn. Fences that cannot be converted are left exactly as they were and reported. default false. |
| `mode` | `"sync" \| "check"` | no | sync (the default) returns the rewritten document and the files to write. check changes nothing: it only reports whether a picture is stale or a fence would convert. default "sync". |
| `files` | `"inline" \| "none"` | no | inline (the default) returns each SVG file with its content. none returns each with path, bytes, sha256, and viewUrl instead, so the answer stays small. default "inline". |
| `page` | `integer` | no | Which page of files to return, from 1; pictures are paged in document order, light and dark together. minimum 1. |
| `pageSize` | `integer` | no | Pictures per page, at most 30. Setting page or pageSize turns paging on. minimum 1; maximum 30. |
| `settings` | `object` | no | The repo's plotwork.config.json, if any. Schema: https://docs.plotwork.dev/schemas/plotwork.config.json. |

## Responses

| Status | Meaning | Body |
|---|---|---|
| 200 | The rewritten document, the files to write, and the report. | [`SyncMarkdownResponse`](https://docs.plotwork.dev/api/schemas.md#syncmarkdownresponse) |
| 400 | The body failed schema validation. | [`ErrorResponse`](https://docs.plotwork.dev/api/schemas.md#errorresponse) |
| 401 | The API key is missing or not valid. | [`ErrorResponse`](https://docs.plotwork.dev/api/schemas.md#errorresponse) |
| 403 | The account is on the Free plan, which does not include the REST drawing and checking routes (plan.rest_not_included). | [`ErrorResponse`](https://docs.plotwork.dev/api/schemas.md#errorresponse) |
| 413 | The body is larger than the size cap. | [`ErrorResponse`](https://docs.plotwork.dev/api/schemas.md#errorresponse) |
| 429 | Too many requests. | [`ErrorResponse`](https://docs.plotwork.dev/api/schemas.md#errorresponse) |

## Response body

A 200 answer has the type [`SyncMarkdownResponse`](https://docs.plotwork.dev/api/schemas.md#syncmarkdownresponse).

| Field | Type | Required | Description |
|---|---|---|---|
| `mode` | `"sync" \| "check"` | yes | The mode that ran. |
| `markdown` | `string` | yes | The document to write back: every diagram fence wrapped in its collapsed source section, with the picture block and text description under it, and everything else unchanged. In check mode this is the document that was sent. |
| `changed` | `boolean` | yes | True when the document or a file differs from what was sent. In check mode this says the pictures are stale or a fence would convert. |
| `files` | `object[]` | yes | The SVG files to write, light and dark for each picture that was refreshed, with paths relative to the repository root. Empty in check mode. A picture that was already current has no files. |
| `files[].path` | `string` | yes | Where to write the file, relative to the repository root. |
| `files[].content` | `string` | no | The file contents; absent when files is none. |
| `files[].encoding` | `"utf8"` | yes | How content is encoded. Always utf8: the files are SVG text. |
| `files[].bytes` | `integer` | no | Only when files is none: the size of the file in bytes. |
| `files[].sha256` | `string` | no | Only when files is none: the SHA-256 of the content, in hex, to check a file fetched later. |
| `files[].viewUrl` | `string` | no | Only when files is none: a link that shows this diagram in a browser. |
| `report` | `object[]` | yes | One entry per fence handled, in document order. |
| `report[].id` | `string \| null` | yes | The picture id, which names the SVG files; null for a fence that has none (a mermaid fence that was not converted). |
| `report[].source` | `"diagram" \| "mermaid"` | yes | diagram for a fence that was already Plotwork, mermaid for one that came from Mermaid. |
| `report[].kind` | `string \| null` | yes | The diagram kind, or null when it could not be read. |
| `report[].line` | `integer` | yes | The 1-based line of the opening fence in the document that was sent. minimum 1. |
| `report[].status` | `"synced" \| "converted" \| "unchanged" \| "skipped" \| "failed"` | yes | synced: the picture was written or refreshed. converted: a mermaid fence became a diagram fence and was drawn. unchanged: the picture was already current. skipped: a cap stopped it, see message. failed: it has errors, the fence is left as it was. |
| `report[].diagnostics` | `Diagnostic[]` | yes | Errors and warnings, with line numbers counted in the document that was sent. For a converted fence they point at the Mermaid lines. |
| `report[].diagnostics[].code` | `"E100" \| "E101" \| "E110" \| "E111" \| "E120" \| "E121" \| "E122" \| "E123" \| "E124" \| "E125" \| "E130" \| "E131" \| "E132" \| "E133" \| "E134" \| "E135" \| "E136" \| "E137" \| "E138" \| "E139" \| "E140" \| "W200" \| "W201" \| "W202" \| "W203" \| "W204" \| "W205" \| "W206" \| "W207" \| "W500" \| "M100" \| "M101" \| "M102" \| "M103" \| "M104" \| "M110" \| "M200" \| "M201" \| "M202" \| "M203" \| "M204" \| "M205" \| "M206" \| "M207" \| "M208" \| "M209" \| "M210" \| "M211" \| "M212" \| "M213" \| "M214" \| "M215" \| "M216" \| "M217" \| "M218" \| "E300" \| "E301" \| "E302" \| "E303" \| "E304" \| "W300" \| "W301" \| "W302" \| "W303" \| "M120" \| "M220" \| "M221" \| "M222" \| "E340" \| "E341" \| "E342" \| "E343" \| "E344" \| "E345" \| "W320" \| "W321" \| "W322" \| "W323" \| "M240" \| "M241" \| "M242" \| "E360" \| "E361" \| "E362" \| "E363" \| "E364" \| "E365" \| "E366" \| "E367" \| "W330" \| "W331" \| "W332" \| "W333" \| "W334" \| "M250" \| "M251" \| "E400" \| "E401" \| "E402" \| "E403" \| "W350" \| "W351" \| "W352" \| "W353" \| "M270" \| "M271" \| "E420" \| "E421" \| "E422" \| "E423" \| "E424" \| "W360" \| "W361" \| "W362" \| "W363" \| "W364" \| "M180" \| "M280" \| "M281" \| "M282" \| "E320" \| "E321" \| "E322" \| "E323" \| "E324" \| "E325" \| "E326" \| "E327" \| "E328" \| "E329" \| "E330" \| "W310" \| "W311" \| "W312" \| "W313" \| "W314" \| "M130" \| "M131" \| "M132" \| "M230" \| "M231" \| "M232" \| "M233" \| "M234" \| "M235" \| "M236" \| "M237" \| "M238" \| "E380" \| "E381" \| "E382" \| "E383" \| "W340" \| "W341" \| "W342" \| "W343" \| "M260" \| "I100" \| "I101" \| "I102" \| "I103" \| "I104" \| "I105" \| "I200" \| "I201" \| "I202" \| "I203" \| "I204" \| "I206" \| "R100" \| "R101" \| "R200" \| "R201" \| "R202" \| "R203" \| "R204" \| "R205" \| "R206" \| "R207" \| "S100" \| "S200" \| "S101" \| "S102" \| "S103" \| "S104" \| "S201" \| "S202" \| "S203"` | yes | Stable code to branch on; E-codes are errors, W-codes are warnings. |
| `report[].diagnostics[].severity` | `"error" \| "warning"` | yes | Errors stop rendering; warnings never do. |
| `report[].diagnostics[].message` | `string` | yes | What is wrong, in one sentence. |
| `report[].diagnostics[].line` | `integer` | yes | 1-based line in the submitted source. minimum 1. |
| `report[].diagnostics[].column` | `integer` | yes | 1-based column in that line. minimum 1. |
| `report[].diagnostics[].length` | `integer` | yes | Number of characters the problem covers. minimum 1. |
| `report[].diagnostics[].hint` | `string \| null` | yes | A concrete suggestion for fixing it, or null. |
| `report[].message` | `string \| null` | yes | A plain sentence when something needs saying, such as why a fence was skipped; otherwise null. |
| `summary` | `object` | yes |  |
| `summary.diagrams` | `integer` | yes | Fences handled: synced, converted, unchanged, skipped, and failed together. |
| `summary.synced` | `integer` | yes |  |
| `summary.converted` | `integer` | yes |  |
| `summary.unchanged` | `integer` | yes |  |
| `summary.skipped` | `integer` | yes |  |
| `summary.failed` | `integer` | yes |  |
| `summary.mermaidIgnored` | `integer` | yes | Mermaid fences left alone because convertMermaid was false. |
| `summary.files` | `integer` | yes | Files to write. |
| `summary.outputBytes` | `integer` | yes | Bytes in the returned document and files together. |
| `caps` | `object` | yes |  |
| `caps.hit` | `boolean` | yes | True when a cap stopped some of the work. Nothing is cut silently: see messages. |
| `caps.messages` | `string[]` | yes | One plain sentence per cap that was hit, saying what was skipped and what to do. |
| `caps.maxDiagrams` | `integer` | yes | Most fences handled in one call. |
| `caps.maxOutputBytes` | `integer` | yes | Most bytes in the returned document and files together. |
| `pages` | `object` | no | Present when page or pageSize was sent: which pictures files holds, and the next page. |
| `pages.page` | `integer` | yes |  |
| `pages.pageSize` | `integer` | yes |  |
| `pages.pages` | `integer` | yes | Pages in all. |
| `pages.pictures` | `integer` | yes | Pictures in all, each a light and a dark file. |
| `pages.nextPage` | `integer \| null` | yes | The page to ask for next, or null on the last one. |
| `next` | `string` | yes | The next step in one sentence. |

## See also

| Doc | Why |
|---|---|
| [API reference](https://docs.plotwork.dev/api/reference.md) | Every endpoint, and the error envelope |
| [Schemas](https://docs.plotwork.dev/api/schemas.md) | Every request and response type |
| [`sync_markdown`](https://docs.plotwork.dev/mcp/tools/sync-markdown.md) | The same operation as an MCP tool |
| [Tool reference](https://docs.plotwork.dev/mcp/tools.md) | The operations as callable tools |
