Validate a render request body
Checks the same TypeBox schema and cross-field semantic rules as POST /v1/render — including googleFontsUrl / googleFontsUrls (mutually exclusive), the merged family pool, and rule set.family/set.font references to declared families — without generating a PDF, metering usage, or recording a successful render audit. Use this to iterate on rules, furniture, and related options before spending a render. Every authenticated call is rate-limited per plan tier against its own quota, kept independent from the POST /v1/render quota — a validate burst never spends render throughput and vice versa — so a burst on either endpoint may return 429 independently of the other. When Google Fonts URLs are present, each declared family is additionally checked for existence on Google Fonts (a cheap stylesheet probe, never the font file), so a misspelled family is caught here as 400 google_font_not_found instead of at render time. A 200 includes typography (declared pool, active body/heading, assignments, and unused pool members) when URLs are present. A 200 means valid at check time only — it does not guarantee the later render succeeds (Google may be unreachable between the two calls). Every 200 also carries imageDiagnostics: the render-time image and layout diagnostics (decode failures, remote fetch/SSRF rejections, oversize, unsupported MIME, rule overlay/mask asset failures, and layout warnings such as a wrap image placed as a block because it is taller than the page or a table row forced to split) computed render-free, each object byte-identical to the entry POST /v1/render reports in X-Render-Diagnostics, so a dry-run sees that an image will not decode or fetch, or that a layout warning will fire, without spending a render. The array is empty when nothing fires. The only accepted query parameter is ?preview=resolved|layout|glyphs; any other query parameter, or an unrecognized preview value, returns 400 invalid_input.
Authorizations
API key issued from your Blink PDF account, prefixed bp_. Send it in the x-api-key header on every request. The documentation and health endpoints are exempt.
Query Parameters
Optional preview mode (no PDF bytes). layout runs a dry-run pagination and returns pages; resolved returns the post-cascade effective set per node in resolved.
resolved, layout Body
Markdown source plus optional document metadata, font selection, page layout, viewer hints, style overrides, and page furniture. Only markdown is required; everything else falls back to safe defaults. When metadata.title is omitted the title is derived from the first Markdown heading; a document with no heading and no title is rendered without a title (no /Title) and is consequently not PDF/UA-1 conformant. Image references use standard Markdown URLs: data: is inline, http(s):// URLs are fetched by the service under strict security defaults (private-IP rejection always-on), and unsupported schemes are reported as render diagnostics. Local images can also be staged out of band and referenced from Markdown as blink://asset/<sha256>, where the handle is the base64url SHA-256 of the bytes; the bytes are fetched at render time and do not count toward the request input size limit. Staged images can also be laid out as pages — one per page or a grid — via the imageSequence option, without hand-authored Markdown. See components.examples.RenderRequest.Minimal, components.examples.RenderRequest.WithRules, components.examples.RenderRequest.WithImages, components.examples.RenderRequest.WithLocalAssets, components.examples.RenderRequest.WithImageSequence, components.examples.RenderRequest.WithOutline, components.examples.RenderRequest.WithHeadingNumbering, components.examples.RenderRequest.WithPageBackground, and components.examples.RenderRequest.WithFurniture for canonical request shapes.
Markdown source plus optional document metadata, font selection, page layout, viewer hints, style overrides, and page furniture. Only markdown is required; everything else falls back to safe defaults. When metadata.title is omitted the title is derived from the first Markdown heading; a document with no heading and no title is rendered without a title (no /Title) and is consequently not PDF/UA-1 conformant. Image references use standard Markdown URLs: data: is inline, http(s):// URLs are fetched by the service under strict security defaults (private-IP rejection always-on), and unsupported schemes are reported as render diagnostics. Local images can also be staged out of band and referenced from Markdown as blink://asset/<sha256>, where the handle is the base64url SHA-256 of the bytes; the bytes are fetched at render time and do not count toward the request input size limit. Staged images can also be laid out as pages — one per page or a grid — via the imageSequence option, without hand-authored Markdown. See components.examples.RenderRequest.Minimal, components.examples.RenderRequest.WithRules, components.examples.RenderRequest.WithImages, components.examples.RenderRequest.WithLocalAssets, components.examples.RenderRequest.WithImageSequence, components.examples.RenderRequest.WithOutline, components.examples.RenderRequest.WithHeadingNumbering, components.examples.RenderRequest.WithPageBackground, and components.examples.RenderRequest.WithFurniture for canonical request shapes.
Markdown source to render to PDF
262144Convenience alias for metadata.title. When both are present metadata.title wins. Defaults to the first Markdown heading when neither is set
1 - 1024\SPDF document metadata embedded in the rendered file. These fields are visible to anyone who opens the PDF — never include PHI, secrets, or other sensitive data.
Ordered list of hosted font families; earlier families take precedence, later ones provide fallback coverage. Every family is available on every plan. When googleFontsUrl or googleFontsUrls is set, the Google family takes the body/heading slot and any weighted family in the stack is ignored (the stack then supplies only the emoji and code roles). Emoji families are appended automatically when omitted
1 - 4 elementsinter, lora, source-serif-4, noto-emoji-mono, twemoji-mozilla, jetbrains-mono Unified font selection: pool declaration/overrides, custom named roles, hosted heading variant policy, and per-family static weight maps for the four-variant PDF model. Body, heading, and per-depth heading families are assigned with rules (select + set.family); positional URL defaults apply otherwise
A text family: a hosted weighted key, a declared Google Fonts display name, or a typography.named role reference
"inter"A single Google Fonts URL (alias of googleFontsUrls[0]). Do not pass together with googleFontsUrls. Accepted forms: CSS API (https://fonts.googleapis.com/css2?family=…), specimen (https://fonts.google.com/specimen/<Family>), or share (https://fonts.google.com/share?selection.family=…). Weight axes set the min/max endpoint weights fetched for the four-variant model. Up to 4 families per URL, and at most 4 in the merged pool across both URLs (two URLs do not raise the total)
2048^https://fonts\.(googleapis|google)\.com/\S+$One or two Google Fonts URLs merged into a single family pool (max 4 distinct families). Default: first family of the first URL → body; first family of the second URL (or second family of the first URL) → heading. Use a rule set.family (or reorder URLs) to override
1 - 2 elementsA single Google Fonts URL (alias of googleFontsUrls[0]). Do not pass together with googleFontsUrls. Accepted forms: CSS API (https://fonts.googleapis.com/css2?family=…), specimen (https://fonts.google.com/specimen/<Family>), or share (https://fonts.google.com/share?selection.family=…). Weight axes set the min/max endpoint weights fetched for the four-variant model. Up to 4 families per URL, and at most 4 in the merged pool across both URLs (two URLs do not raise the total)
2048^https://fonts\.(googleapis|google)\.com/\S+$Page geometry and appearance: size (named preset or [width, height] in points), orientation, margins, and optional full-sheet background
PDF viewer hints — document language, page layout, page mode, and viewer preferences (toolbar/menubar visibility, fit-to-window, duplex, print scaling)
Surgical presentation rules over standard Markdown nodes.
256Ergonomic per-image style overrides by position or exact URL, without authoring a full rules selector entry. For document-order indexing (true reading order across interleaved block and inline images), or for matching by anything other than an exact URL, use rules with select.nth/select.url and type: 'image' instead.
256Design preset resolved server-side to a fixed bundle of typography/page/furniture/numbering/rules, then merged under your own typography/page/furniture/rules (your values win per field; your rules win on conflicting cascade leaves). Picking a theme never lets the server read your Markdown — the bundle is deterministic, so the same source plus the same theme yields the same PDF. An unknown or malformed slug resolves to report-clean and never fails the request; the fallback is reported by resolvedTheme on /v1/render/validate and the x-render-theme-fallback response header on /v1/render. One of: report-clean, report-executive, report-data, blog-modern, blog-dark, blog-editorial, academic-classic, academic-modern, thesis, docs-light, docs-dark, api-reference, corporate, proposal, invoice.
64"report-clean"
"report-executive"
"report-data"
"blog-modern"
"blog-dark"
"blog-editorial"
"academic-classic"
"academic-modern"
"thesis"
"docs-light"
"docs-dark"
"api-reference"
"corporate"
"proposal"
"invoice"
Auto-numbering policy. Opt-in via enabled: true. Counters reset on the next deeper-depth heading and propagate across the document. Passing any of all, byDepth, depthRange, or trailingSeparator together with enabled: false returns 400 — those fields configure an active numbering policy, so they require enabled: true.
Page furniture (running headers and footers). slots applies to every page; set omitFirstPage: true to suppress furniture on the cover page, or pass firstPage to override the first page only. Set omitLastPage: true to suppress furniture on the closing page, or pass lastPage to override the final page only (when totalPages > 1). Optional pageOverrides supplies arbitrary per-page exceptions (later matches win per field). Optional watermark draws faint text behind body content. Optional decorations draws band rules such as masthead hairlines
PDF outline (bookmarks). When omitted, an outline is emitted with the default depth range [1, 6] for any document containing headings. Pass depthRange alone to constrain depths. Set enabled: false to suppress the outline entirely. Passing depthRange together with enabled: false returns 400 — choose one intent.
Lay out staged images as pages without hand-authored Markdown: one per page or tiled in a grid, fit contain/cover, orientation: auto matching each page to its image. Defaults: fit is contain for one_per_page and cover for grid; orientation is auto; size is A4; padding is 0 (full-bleed). The sequence renders after the Markdown body; with empty markdown the document is the sequence alone. Each image still decodes against the per-render megapixel cap, so downscale very large folders or split them across renders. See components.examples.RenderRequest.WithImageSequence
When true, each remote http(s):// image is fetched once and its bytes are reused on later renders of the same request, so repeated renders produce a byte-identical PDF even if the origin changes; reuse lasts up to 24 hours, after which the image is fetched again. Defaults to false, fetching every remote image fresh on each render.
Response
The body is valid for rendering. No PDF bytes are returned. The response carries imageDiagnostics — the render-time image and layout diagnostics (decode/fetch failures, rejected schemes, oversize, forced layout changes) computed render-free, at parity with the render X-Render-Diagnostics header.
The request body passed TypeBox validation and semantic checks
Which design theme this request resolved to. Always present on a successful validate (report-clean with fellBack: false when no theme was requested), so an agent can confirm the bundle the render will use.
Canonical per-account render digest when the caller is authenticated; used by MCP delivery deduplication and returned again as the x-blinkpdf-content-digest response header on a later render. For renders whose Markdown references blink://asset/<id> handles, the digest additionally varies by the calling credential, so two API keys of one account never share an asset-resolved render or its cached signed link; renders without asset handles stay per-account. It is a digest of the request/config, NOT a hash of the output PDF bytes — an inert change (a rule that matches nothing) shifts the digest without changing the rendered bytes, so compare the file bytes to detect a visual change.
Opaque owning account id when the caller is authenticated.
The caller's enforcement billing tier (free/pro/business) when authenticated, so a client can report the per-tier request-body limit without a second call. Absent for an unauthenticated (db-less/auth-disabled) validate.
Per-rule match preview: how many nodes each rules[] entry touched and where, so an agent can iterate without spending a render. A matched: 0 entry is a likely typo in the selector.
Total page count from a dry-run pagination, present only when ?preview=layout is requested. No PDF bytes are produced.
Per-node page assignment from the dry-run pagination, present only when ?preview=layout is requested. Cross-reference node with rules[].nodes/resolved; split marks nodes that cross a page boundary. A figure caption@… node appears after its image@….
furniture.pageOverrides whose pageNumber/pageIndex selector matches no page in the paginated document (a silently-inert override), each as { index, reason }. Present only with ?preview=layout, and only when at least one override is unmatched. A sectionStarts override is content-dependent and not reported here.
Per-image resolved geometry from the dry-run layout, present only when ?preview=layout is requested, in public top-left PDF points (origin at the page top-left, Y growing downward). One entry per drawn image in document order — empty for a document with no images — so a caller can assert resolved boxes without inspecting the PDF.
Present (an object) whenever ?preview=resolved is requested — {} when no rule matched any node, so "checked, nothing applied" is distinguishable from "not checked". The rule-contributed set per matched node (the post-cascade merge of every rule that targeted it), keyed by node id (<label>@<line>:<column>, the same ids as rules[].nodes; a matched table cell is keyed cell@<line>:<column>, distinct from its whole-table table@… id). Every id in rules[].nodes appears here so the two reconcile deterministically — a node a rule matched but whose props were all inapplicable or fully shadowed maps to {}. It is a cascade delta, not the full effective style: a node no rule matched is absent, and theme defaults for properties no rule sets are applied at render time and are not echoed here. Effective body/heading families and per-depth weights are in typography.active / typography.resolvedWeights.
Present only when ?preview=glyphs is requested. Codepoints in the document with no covering glyph in the resolved font cascade (the tofu render would otherwise produce), predicted render-free without fetching images. Empty array means full coverage.
Present when numbering.enabled is true and at least one heading is numbered: the render-free resolved counter string per numbered heading, in document order, honoring numbering format/prefix/depthRange and any per-heading set.numbering skip/reset. Lets a caller confirm 1. vs Chapter 1. without a render. A heading skipped or outside the depth range is absent.
Informational typography notes: variant_collapsed when a variantMap flattens bold onto regular, weight_not_in_fetch when a mapped weight is not fetched. Never invalidates the request.
Cheap render-free diagnostics from the resolved rule sets, e.g. a circle mask on a non-square image box. Always present on a successful validate (an empty array when there are no warnings or no rules), so an empty list means "checked, no warnings" rather than "not checked". Never invalidates the request.
Render-time image and layout diagnostics computed render-free — decode failures, remote fetch/SSRF rejections, oversize, unsupported MIME, rule overlay/mask asset failures, and layout warnings such as a wrap image placed as a block because it is taller than the page, or a table row forced to split — in source order where available, each object byte-identical to the entry render serializes into X-Render-Diagnostics. Always present on a successful validate (an empty array when nothing fires), so an empty list means "checked, nothing to report" rather than "not checked". This closes the dry-run gap where a malformed image or a forced layout change rendered as degraded but validate reported valid: true with no signal. Glyph, typography, and rule advisories stay in their own fields (glyphs, typographyNotes, ruleWarnings).
Non-blocking advisories for rules that pass schema validation but are semantically inert (§5.3): a set property not valid for the selected node category, a selector axis not valid for the selected type, a heading depth outside 1–6, or a selector that combines more than one position axis (first/last/nth). A rule that matched zero nodes is reported here as a rule_no_match advisory (and also inline as rules[].warning for convenience), matching the render X-Render-Rule-Warnings header so the same condition reads identically on validate and render. Never invalidates the request.