Markdown Renderer
markdown.render
Overview
You send Markdown and we return HTML that is safe to put straight on a page. Three grammars are available: strict CommonMark, GFM with tables, task lists, strikethrough and bare-URL links, and squillify-safe, which adds footnotes and GitHub-style callouts. Everything goes through an allowlist sanitizer, so scripts, event handlers, and dangerous URL schemes never survive, whatever your raw-HTML policy. You also get heading anchors, an optional table of contents, and a manifest of every image the document points at. Math, diagrams, and citations are selected through the closed components object. We render and sanitize — we do not fetch images, follow links, run code, or produce PDFs. Choosing between this and markdown.render_math: this is the one for a whole document, equations included — use components.math.mode and the same typesetting engine runs. Reach for markdown.render_math only when you need the expressions on their own as SVG fragments without the prose, the result written to a file, a malformed expression to fail the call instead of being preserved as source, or more than 100 expressions in one pass. This call is billed once. Parse, emit and sanitize cost 2 credits. Math that actually typesets adds 5 (the same weight as markdown.render_math); diagrams that actually render add 5 (markdown.render_diagrams); citations that actually resolve add 2 (markdown.render_citations). Those extras are included in this parent debit — the specialized skills are not invoked and not billed again. The settled total is 2 through 14 credits and is reported on _meta.usage. Omit components and we still reserve 12 credits up front, because math and diagrams default to auto; set a component's mode to off when you want it out of both the reservation and the work. Accepts file input via file_ref — no need to extract file content first.
Markdown from an agent or an end user is untrusted input, and turning it into HTML safely takes a sanitizer, a fixed grammar, and a stable theme — not a regex.
Reliability
STRICT and byte-deterministic for identical input at a fixed engine version, math included. Validation (not retryable): MARKDOWN_PROFILE_UNSUPPORTED and MARKDOWN_INVALID_ENCODING, which names the byte offset and is worth retrying after re-encoding. Fixed caps: MARKDOWN_COMPLEXITY_LIMIT for node count, nesting depth, or more than 100 expressions, and OUTPUT_LIMIT_EXCEEDED for a result too large to return inline. With math enabled only, the sidecar can answer RENDERER_BUSY, RENDER_TIMEOUT, or INTERNAL_RENDERER_UNAVAILABLE — all retryable, and never downgraded into math-less HTML, because a billed success that quietly dropped your equations would be worse than a failure.
Context footprint
Inline input up to 1048576 characters, or 2097152 bytes through file_ref. HTML is returned inline and is typically several times the size of its source, so a large document is better read from a file_ref than pasted. You are billed 2 credits for Markdown alone, plus 5 if math typesets, 5 if diagrams render, and 2 if citations resolve — one charge on this call, never a second charge for markdown.render_math, markdown.render_diagrams, or markdown.render_citations. Before we read the document we reserve credits from the component modes you sent (12 if you omit components, because math and diagrams default to auto). After the render, _meta.usage.totalUnits is what we actually charged.
When to use it
When Markdown you did not write has to become displayable HTML. This is the default choice for a whole document, math included — use components.math.mode=auto and the expressions are typeset by the same engine markdown.render_math uses.
When you need another document format, server-side highlighting, or anything that reaches out to the network. Also when the job is the equations rather than the document: markdown.render_math is the one that returns expressions on their own as SVG fragments, writes the result to a file, fails the call on a malformed expression instead of preserving it as source, or accepts more than the 100 expressions this skill caps at.
Powered by commonmark-java for parsing, jsoup for sanitization, and MathJax for optional math typesetting.
Scope
Non-goals
- Does not fetch, validate, or embed the images and links it reports
- Does not produce PDF, DOCX, or other document formats
- Does not execute code blocks or evaluate expressions
- Does not highlight code server-side — it labels the language for your highlighter
- Does not repair invalid Markdown
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
File limits
Constraints this skill applies to your input and generated files. The “Limited by” column shows the static source of each cap; none of these can be raised by upgrading your plan.
| Limit | Value | Limited by |
|---|---|---|
| Maximum size per input file | 2 MiB | Skill maximum |
How we handle your data
What this means
This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.
Squillify guarantees
- Runs inside Squillify using a self-contained open-source component
- This skill runs inside Squillify.
- Payloads are not sent to an external service by this skill.
- Self contained open source component
- Squillify does not permanently store submitted payloads.
- Telemetry records metadata only, not submitted content.
- Submitted payloads are not used for training.
- Submitted payloads are not reviewed by humans.
- This skill does not target sensitive data.
- The response may contain submitted or transformed content.
Your responsibility
Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| markdown | string | No | The Markdown to render, up to 1048576 characters. Omit it only when you send file_ref — one of the two is required, and sending neither is a validation error. If you send both, file_ref wins and _meta._input_conflict_resolved says so. |
| file_ref | string | No | A stored Markdown file (squillify://files/f-...) we read instead of inline text, up to 2097152 bytes. Omit it to render the markdown field instead — one of the two is required. The file must be valid UTF-8: we do not guess at broken bytes, we tell you which offset is wrong. |
| profile | string | No | Which grammar to apply: commonmark (strict, raw HTML escaped), gfm (adds tables, task lists, strikethrough, bare-URL autolinks), or squillify-safe (adds footnotes, callouts, front matter). Defaults to squillify-safe. |
| outputFormat | string | No | html_fragment (default) returns an article element you drop into your own page; html_document returns a complete page with the stylesheet in its head. |
| htmlPolicy | string | No | What to do with HTML written inside the Markdown: escape shows it as text, drop removes it and keeps the text, sanitize keeps what survives the allowlist. Defaults to the profile's own policy — escape for commonmark and gfm, sanitize for squillify-safe. |
| theme | string | No | squillify (default, styled from the Squillify Design System), none (structural classes only, bring your own CSS), light, dark, or auto (neutral light with a CSS-only dark override). |
| includeCss | boolean | No | Whether html_document embeds the stylesheet in its head. Defaults to true. A fragment never carries a style element, so this has no effect there — _meta.cssIncluded always reports what actually happened. |
| baseUri | string | No | An http or https URL that relative links and images resolve against. Resolution is pure string arithmetic: we never open a connection or look up a host. Omit it to leave relative URLs exactly as written. |
| options | object | No | Optional flags, all booleans: generateHeadingIds (default true), generateToc (false), footnotes (true), callouts (true), codeLineNumbers (false), codeCopyMetadata (true), openExternalLinksInNewTab (false). An option the profile has no extension for is ignored rather than refused. Unknown keys are rejected. |
| components | object | No | Closed component configuration for math, diagrams, and citations. Each entry has mode auto or off. Math and diagrams default to auto; citations default to off so ordinary document rendering never requires bibliography metadata. A component that actually runs adds its standalone credit weight to this call (5 for math, 5 for diagrams, 2 for citations) and is not billed as a second skill. Set citations.mode to auto and provide components.citations.bibliography to resolve citation syntax; citations.errorPolicy uses report (default) or fail, matching markdown.render_citations. Unknown components, options, and modes are rejected. |
| limits | object | No | Optional: timeoutMs (default 8000, clamped to 30000) for math typesetting, and maxDiagnostics (default 200, clamped to 2000). Unknown keys are rejected. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| html | string | Yes | The rendered HTML: an article element for html_fragment, a complete page for html_document. Every id it contains is prefixed md- so two renders can share a page. |
| mimeType | string | Yes | Always text/html; charset=utf-8. |
| toc | array | Yes | One entry per heading in document order: level (1-6), text (the heading as plain text) and id (the anchor exactly as it appears in the html, prefix included). Empty when generateHeadingIds is off. |
| assets | array | Yes | Every image the document points at, in document order, without repeats: type (always "image"), url (after baseUri resolution), alt (null when the document gave none) and external (true when it sits off your baseUri origin). We list them; we never fetch them. |
| diagnostics | array | Yes | Anything worth telling you about the document, each with code, severity, message, optional source range, and optional suggestion. Shared validation findings come first in ruleset, source-position, severity, code order; render-execution findings such as MARKDOWN_MATH_RENDER_SKIPPED follow deterministically. Severity describes the source finding, not the call outcome: a successful render may contain error diagnostics when the selected grammar recovered deterministically. |
| errors | integer | Yes | Number of error diagnostics before maxDiagnostics truncates the returned array. This is a source-quality count, not an execution-status flag; use the top-level success or failure result to determine whether rendering completed. |
| warnings | integer | Yes | Number of warning diagnostics before maxDiagnostics truncates the returned array. |
| security | object | Yes | What the sanitizer took out: htmlPolicy (the policy applied), removedElements, removedAttributes and blockedUrls. All zero on a document that contained nothing unsafe. |
| _meta | object | Yes | profile and profileVersion, parser and parserVersion, sanitizerVersion, theme and themeVersion, cssIncluded (whether a stylesheet was actually embedded), inputChars, inputBytes, nodeCount, mathExpressions, truncated (whether diagnostics were cut at maxDiagnostics), usage (the settled markdown-composite-v1 credit allocation: base 2 plus each component that ran), and, only when it applies, _input_conflict_resolved. No timing field: two identical requests must return identical bytes. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"markdown.render","input":{"markdown":"# Formula\n\n$E=mc^2$","components":{"math":{"mode":"auto"},"diagrams":{"mode":"off"},"citations":{"mode":"off"}}}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("markdown.render", {"markdown":"# Hello\n\nWorld"});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| MARKDOWN_PROFILE_UNSUPPORTED | 400 | No | The request payload failed this skill's input validation. |
| MARKDOWN_INVALID_ENCODING | 400 | No | The request payload failed this skill's input validation. |
| MARKDOWN_COMPLEXITY_LIMIT | 429 | No | A size, quota, rate, or time limit was exceeded. |
| RENDERER_BUSY | 429 | Yes | A size, quota, rate, or time limit was exceeded. |
| RENDER_TIMEOUT | 503 | Yes | A dependency required by this skill is unavailable. |
| INTERNAL_RENDERER_UNAVAILABLE | 503 | Yes | A dependency required by this skill is unavailable. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not fetch, validate, or embed the images and links it reports
- Does not produce PDF, DOCX, or other document formats
- Does not execute code blocks or evaluate expressions
- Does not highlight code server-side — it labels the language for your highlighter
- Does not repair invalid Markdown
- Converting Markdown to PDF or Word
- Highlighting code on the server
- Downloading or checking the images a document references
- Producing the typeset expressions on their own, or writing them to a file — that is markdown.render_math
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "markdown.render",
"input": {
"markdown": "# Hello\n\nWorld"
}
}Examples
A heading and a paragraph, with the anchor the renderer generated
{
"markdown": "# Hello\n\nWorld"
}{
"warnings": 0,
"html": "<article class=\"sqfy-markdown sqfy-markdown-v2 sqfy-theme-squillify\">\n <h1 id=\"md-hello\">Hello</h1>\n <p>World</p>\n</article>",
"errors": 0,
"toc": [
{
"text": "Hello",
"id": "md-hello",
"level": 1
}
],
"mimeType": "text/html; charset=utf-8",
"diagnostics": [],
"security": {
"htmlPolicy": "sanitize",
"removedElements": 0,
"removedAttributes": 0,
"blockedUrls": 0
},
"_meta": {
"sanitizerVersion": "1.18.3",
"themeVersion": "sqfy-markdown-v2",
"inputChars": 14,
"mathExpressions": 0,
"parserVersion": "0.24.0",
"truncated": false,
"parser": "commonmark",
"cssIncluded": false,
"inputBytes": 14,
"diagrams": 0,
"usage": {
"pricingModel": "markdown-composite-v1",
"totalUnits": 2,
"baseUnits": 2,
"components": {}
},
"profile": "squillify-safe",
"theme": "squillify",
"citations": 0,
"nodeCount": 4,
"profileVersion": "1"
},
"assets": []
}Relative links and images resolved against a base, and the image listed
{
"markdown": "See the [guide](docs/guide.md) and .",
"baseUri": "https://example.com/site/"
}{
"warnings": 0,
"html": "<article class=\"sqfy-markdown sqfy-markdown-v2 sqfy-theme-squillify\">\n <p>See the <a href=\"https://example.com/site/docs/guide.md\">guide</a> and <img src=\"https://example.com/site/chart.png\" alt=\"chart\">.</p>\n</article>",
"errors": 0,
"toc": [],
"mimeType": "text/html; charset=utf-8",
"diagnostics": [],
"security": {
"htmlPolicy": "sanitize",
"removedElements": 0,
"removedAttributes": 0,
"blockedUrls": 0
},
"_meta": {
"sanitizerVersion": "1.18.3",
"themeVersion": "sqfy-markdown-v2",
"inputChars": 55,
"mathExpressions": 0,
"parserVersion": "0.24.0",
"truncated": false,
"parser": "commonmark",
"cssIncluded": false,
"inputBytes": 55,
"diagrams": 0,
"usage": {
"pricingModel": "markdown-composite-v1",
"totalUnits": 2,
"baseUnits": 2,
"components": {}
},
"profile": "squillify-safe",
"theme": "squillify",
"citations": 0,
"nodeCount": 8,
"profileVersion": "1"
},
"assets": [
{
"url": "https://example.com/site/chart.png",
"alt": "chart",
"type": "image",
"external": false
}
]
}Recommended use cases
- Publish agent-written or user-written Markdown on a page without trusting it
- Render documentation with anchors and a table of contents
- Render technical documentation that mixes prose and equations, via components.math.mode=auto
- Preview a stored Markdown file exactly as it will appear
Avoid for
- Converting Markdown to PDF or Word
- Highlighting code on the server
- Downloading or checking the images a document references
- Producing the typeset expressions on their own, or writing them to a file — that is markdown.render_math
Related skills
Composable with
Safety & privacy
- Runs without calling any external service.
- Your payloads are never stored permanently.
- Telemetry captures metadata only — never your payload contents.
- We never train models on your payloads.
- No human reviews your payloads.