Properties
| Name | Type | Description |
|---|---|---|
| results | array | One row per request item, in request order. A single-item batch (`items` of length 1) still returns an array of one row. |
| account | string | Opaque owning account id when the caller is authenticated. |
| tier | string | 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 |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://artifacts.apis.io/blinkpdf-io/json-schema/blinkpdf-io-render-validate-ok-schema.json",
"title": "Render.ValidateOk",
"x-generated": "2026-10-30",
"x-method": "derived",
"x-generator": "derive-json-schema.py",
"x-source": "openapi/blinkpdf-io-openapi.json#/components/schemas/Render.ValidateOk",
"type": "object",
"required": [
"results"
],
"properties": {
"results": {
"description": "One row per request item, in request order. A single-item batch (`items` of length 1) still returns an array of one row.",
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"required": [
"ok",
"index",
"impact",
"diagnostics"
],
"properties": {
"ok": {
"description": "This item passed TypeBox validation and semantic checks.",
"const": true,
"type": "boolean"
},
"index": {
"description": "Zero-based position of this item in the request `items` array.",
"type": "number"
},
"clientItemId": {
"description": "Echoed back when the request item supplied one.",
"type": "string"
},
"contentDigest": {
"description": "Canonical per-account render digest when the caller is authenticated and the body has no `security` (an encrypted PDF is never cached or digested); 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, or that set `pinRemoteImages`, the digest additionally varies by the calling credential, so two API keys of one account never share an asset-resolved or pinned render or its cached signed link; other renders 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.",
"type": "string"
},
"typography": {
"type": "object",
"required": [
"declared",
"active",
"assignments",
"unused"
],
"properties": {
"declared": {
"$ref": "#/$defs/Problem.validation_failed.errors.item.allowedKeys"
},
"active": {
"description": "The body/heading families this request configures (its intent). These are not coverage-checked here, so the family a multi-script document actually renders in may differ — cross-reference `?preview=glyphs` and the render `X-Render-Fonts`/`X-Render-Font-Fallback` headers.",
"type": "object",
"required": [
"body",
"heading"
],
"properties": {
"body": {
"description": "The configured body family (request intent), not necessarily the family the render embeds. When this family cannot cover a codepoint used in body text, the render falls back to `inter` for the whole body role — the effective family is then reported by the render `X-Render-Fonts` header (and flagged by `X-Render-Font-Fallback`). Use `?preview=glyphs` to predict those fallbacks render-free.",
"type": "string"
},
"heading": {
"description": "The configured heading family (request intent), not necessarily the family the render embeds. When this family cannot cover a codepoint used in heading text, the render falls back to `inter` for the whole heading role — the effective family is then reported by the render `X-Render-Fonts` header (and flagged by `X-Render-Font-Fallback`). Use `?preview=glyphs` to predict those fallbacks render-free.",
"type": "string"
}
}
},
"assignments": {
"$ref": "#/$defs/Render.ValidateOk.results.item.anyOf0.typography.assignments"
},
"unused": {
"$ref": "#/$defs/Problem.validation_failed.errors.item.allowedKeys"
},
"resolvedWeights": {
"description": "Effective per-role (and per heading depth) weights resolved from `variantMap`, so a per-level weight is confirmable without rendering.",
"type": "array",
"items": {
"type": "object",
"required": [
"role",
"family"
],
"properties": {
"role": {
"type": "string"
},
"depth": {
"type": "number"
},
"family": {
"type": "string"
},
"regular": {
"type": "number"
},
"italic": {
"type": "number"
},
"bold": {
"type": "number"
},
"boldItalic": {
"type": "number"
}
}
}
}
}
},
"rules": {
"description": "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.",
"type": "array",
"items": {
"type": "object",
"required": [
"index",
"selector",
"category",
"matched",
"nodes",
"nodesOmitted"
],
"properties": {
"index": {
"description": "Index of the rule in the request `rules` array",
"type": "number"
},
"selector": {
"description": "Canonical (normalized) form of the selector",
"type": "object",
"patternProperties": {
"^(.*)$": {}
}
},
"category": {
"description": "Node category the selector targets (`block`/`inline`), which decides `set` property validity",
"type": "string",
"enum": [
"block",
"inline"
]
},
"matched": {
"description": "Number of document nodes the rule matched",
"type": "number"
},
"nodes": {
"description": "Matched node ids that `resolved` lists, in this rule's document order. A node id is `<label>@<line>:<column>` where `<label>` is the node type, except a matched table *cell* (a `column`/`row`/`region`-scoped table rule) which is labelled `cell@<line>:<column>` so it is distinguishable from its whole-table `table@<line>:<column>` id. `nodesOmitted` counts this rule's other matches; `matched` stays the total. At most 256 ids are listed across `resolved`.",
"type": "array",
"items": {
"type": "string"
}
},
"nodesOmitted": {
"minimum": 0,
"description": "This rule's matches that `resolved` does not list, counted exactly; `0` when `nodes` lists every match.",
"type": "integer"
},
"warning": {
"description": "Present when the rule matched zero nodes; the most specific cause — `depth_out_of_range` or `invalid_axis_for_type` when the selector can never match, else `rule_no_match` (a likely typo or a node the document simply lacks). Mirrors the corresponding `ruleWarnings[]` code.",
"type": "string",
"enum": [
"rule_no_match",
"depth_out_of_range",
"invalid_axis_for_type"
]
}
}
}
},
"layout": {
"$ref": "#/$defs/Layout.Report"
},
"resolvedImages": {
"description": "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, the first 256 at most — empty for a document with no images — so a caller can assert resolved boxes without inspecting the PDF. `resolvedImagesOmitted` counts the rest.",
"type": "array",
"items": {
"type": "object",
"required": [
"index",
"url",
"mode",
"layer",
"page",
"box"
],
"properties": {
"index": {
"description": "0-based document image index in source order (block images first, then inline), stable between this preview and the rendered PDF",
"type": "number"
},
"url": {
"description": "The Markdown image source URL",
"type": "string"
},
"mode": {
"description": "How the image is positioned: `block` and `inline` flow with the text, `wrap` floats with text wrapping beside it, `pin` anchors to a fixed page position, and `bleed` fills to the page edge.",
"type": "string",
"enum": [
"block",
"inline",
"wrap",
"pin",
"bleed"
]
},
"layer": {
"description": "The page plane the image draws on: `content` for in-flow images, or `page-background`/`page-foreground` for a `pin`/`bleed` image placed behind or in front of the content.",
"type": "string",
"enum": [
"content",
"page-background",
"page-foreground"
]
},
"page": {
"description": "1-based page the image lands on; a repeating `pin`/`bleed` image yields one entry per page it lands on",
"type": "number"
},
"box": {
"description": "The image’s drawn rectangle in public top-left PDF points (origin at the page top-left, Y growing downward)",
"type": "object",
"required": [
"x",
"y",
"width",
"height"
],
"properties": {
"x": {
"description": "Left edge X in PDF points, measured from the page left edge",
"type": "number"
},
"y": {
"description": "Top edge Y in PDF points, measured down from the page top edge",
"type": "number"
},
"width": {
"description": "Drawn width in PDF points, after sizing and fit",
"type": "number"
},
"height": {
"description": "Drawn height in PDF points, after sizing and fit",
"type": "number"
}
}
}
}
}
},
"resolvedImagesOmitted": {
"minimum": 0,
"description": "Present with `resolvedImages`: how many drawn images lie past the first 256 it lists, counted exactly; `0` when it lists them all.",
"type": "integer"
},
"resolved": {
"description": "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` of the first 256 matched nodes (the post-cascade merge of every rule that targeted one), 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), in the order the preview records them. A listed id that a rule matched but whose props were all inapplicable or fully shadowed maps to `{}`. Every id here appears in `nodes` of a rule that matched it. `resolvedOmitted` counts the matched nodes past this object; `rules[].nodesOmitted` counts that rule's matches that are not listed here, and `matched` stays the total. 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`.",
"type": "object",
"patternProperties": {
"^(.*)$": {
"type": "object",
"patternProperties": {
"^(.*)$": {}
}
}
}
},
"resolvedOmitted": {
"minimum": 0,
"description": "Present with `resolved`: how many matched nodes lie past the first 256 it lists, counted exactly; `0` when it lists them all.",
"type": "integer"
},
"glyphs": {
"description": "Present only when `?preview=glyphs` is requested. Codepoints in the Markdown body with no covering glyph in the resolved font cascade (the tofu render would otherwise produce), predicted render-free without fetching images or paginating, listed in the order and under the per-code cap of `diagnostics`. Furniture text and SVG text are judged on the laid-out paint, so they appear in `diagnostics`, not here. Empty array means full coverage.",
"type": "array",
"items": {
"type": "object",
"required": [
"code",
"severity",
"impact",
"message"
],
"properties": {
"code": {
"description": "Glyph diagnostic code",
"type": "string"
},
"severity": {
"description": "Severity bucket (error/warn/info)",
"type": "string"
},
"impact": {
"description": "What it changed in the PDF against the request, graded by BlinkPDF so no client keeps a table of codes: `content` (part of what was asked is missing, cut off, covered, or may not be read), `appearance` (everything asked is painted and readable, only its look or navigation differs: another face or cut, a smaller size, another place or page break, a decoration, link, or bookmark dropped), `none` (an advisory: the PDF is as asked). A code BlinkPDF adds later comes graded too.",
"type": "string",
"enum": [
"none",
"appearance",
"content"
]
},
"message": {
"description": "Names the uncovered codepoint and the font lacking it; on an omission, how many more there are",
"type": "string"
},
"omitted": {
"minimum": 1,
"description": "Present only on the entry that closes a code's listing: how many more occurrences of `code` at `severity` there are past the first 10, which are not listed (the cap of `diagnostics`).",
"type": "integer"
}
}
}
},
"numbering": {
"description": "Present when `numbering.enabled` is true: the render-free resolved counter string of the first 256 numbered headings, in document order, honoring `numbering` format/prefix/depthRange and any per-heading `set.numbering` skip/reset. `numberingOmitted` counts the rest. Lets a caller confirm `1.` vs `Chapter 1.` without a render. A heading skipped or outside the depth range is absent. An empty array means numbering ran and numbered nothing.",
"type": "array",
"items": {
"type": "object",
"required": [
"node",
"label"
],
"properties": {
"node": {
"description": "Heading node id (`heading@<line>:<column>`), matching `rules[].nodes`/`resolved`",
"type": "string"
},
"label": {
"description": "The exact counter string the render prefixes to this heading (e.g. `1.`, `1.2.`, `Chapter 1.`)",
"type": "string"
}
}
}
},
"numberingOmitted": {
"minimum": 0,
"description": "Present with `numbering`: how many numbered headings lie past the first 256 it lists, counted exactly; `0` when it lists them all.",
"type": "integer"
},
"typographyNotes": {
"$ref": "#/$defs/Render.ValidateOk.results.item.anyOf0.typographyNotes"
},
"staticWarnings": {
"description": "Cheap render-free diagnostics from the resolved rule sets and the Markdown document, e.g. a circle mask on a non-square image box or an image with no alt text. At most 10 of each code are listed, in document order; `staticWarningsOmitted` counts the rest. 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.",
"type": "array",
"items": {
"type": "object",
"required": [
"code",
"node",
"hint"
],
"properties": {
"code": {
"description": "Cheap pre-render diagnostic kind",
"type": "string",
"enum": [
"mask_circle_nonsquare",
"overlay_no_margin",
"syntax_highlight_unsupported_language",
"align_value_not_applicable",
"table_widths_column_mismatch",
"table_widths_degenerate",
"table_widths_partial_zero",
"internal_link_unresolved",
"heading_level_skip",
"ordered_marker_literal",
"unordered_marker_counter_format",
"image_url_blocked",
"image_url_malformed",
"image_empty_alt",
"furniture_no_content",
"furniture_fragment_empty",
"page_override_inert",
"numbering_depth_unmatched",
"outline_depth_unmatched"
]
},
"node": {
"description": "Node id (`type@line:column`) the warning applies to",
"type": "string"
},
"hint": {
"description": "Actionable fix for the author",
"type": "string"
}
}
}
},
"staticWarningsOmitted": {
"minimum": 0,
"description": "Present with `staticWarnings`: how many warnings lie past the first 10 of their code, counted exactly; `0` when it lists them all.",
"type": "integer"
},
"impact": {
"description": "The grade of the whole PDF: the worst `impact` among the `diagnostics`, `typographyNotes`, `ruleWarnings`, and `staticWarnings` entries (where a rule or static advisory counts `content` when an image it names cannot be fetched (`image_url_blocked`, `image_url_malformed`), `appearance` when a value the body set is dropped and no other value of the body decides it (`set_prop_not_applicable`, `set_prop_partial`, `invalid_axis_for_type`, `conflicting_position_axis`, `ordered_marker_literal`, `ordered_marker_is_bullet`, `align_needs_cell_scope`, `mask_circle_nonsquare`, `syntax_highlight_unsupported_language`, `align_value_not_applicable`, `table_widths_column_mismatch`, `table_widths_degenerate`, `table_widths_partial_zero`, `internal_link_unresolved`, `unordered_marker_counter_format`), and `none` when what the body decides is painted (`depth_out_of_range`, `set_action_inert`, `rule_fully_shadowed`, `rule_no_match`, `overlay_no_margin`, `heading_level_skip`, `image_empty_alt`, `furniture_no_content`, `furniture_fragment_empty`, `page_override_inert`, `numbering_depth_unmatched`, `outline_depth_unmatched`)); the value `POST /v1/render` writes as `X-Render-Impact` for the same body, from the same grade, so a dry run tells lost content from a look change without a table of codes, every occurrence counted, listed or not (`content` over `appearance` over `none`). `none` is a PDF made as requested; `appearance` one that holds everything asked, readable, with a look or navigation that differs; `content` one that misses part of what was asked.",
"type": "string",
"enum": [
"none",
"appearance",
"content"
]
},
"diagnostics": {
"description": "The list `POST /v1/render` writes to `X-Render-Diagnostics` for this body, computed render-free by the same builder: same entries, same shape, same order (severity first, errors before warns before infos, then document position). Covers every class — image sourcing/decode and rule overlay/mask assets, layout warnings such as a wrap image placed as a block or a table row forced to split, missing glyphs, bold or italic text its family has no cut for (`font-variant` at the first such run), text in a script BlinkPDF does not shape yet (`script-not-shaped` at each text that holds it), a header or footer slot or rule whose color you did not set that lies on an image decoration, whose color BlinkPDF cannot read (`ink-unverified` at the pointer of that slot or rule), a header or footer slot whose color you set has, at full opacity, less than 3:1 contrast against the page background (`text-low-contrast` at the pointer of that slot), and document-level notes such as `title-omitted` or `glyph-component-dropped` — whether or not the document has images. At most 10 occurrences of each code and severity are listed; the rest of that code and severity are counted by one entry carrying `omitted` (the count) right after its last listed occurrence, and still count toward `X-Render-Status` and `X-Render-Impact`, so the list grows with the codes a document reports, not with its size. Not capped by the header budget: when render cannot fit the list in `X-Render-Diagnostics` (`X-Render-Diagnostics-Truncated`), that bounded header may leave codes out (`X-Render-Diagnostics-Summary` names them all), while this array still lists every entry. Always present on a successful validate; an empty array means render reports no diagnostics. Typography and rule advisories stay in their own fields (`typographyNotes`, `ruleWarnings`).",
"type": "array",
"items": {
"$ref": "#/$defs/Render.Diagnostic"
}
},
"ruleWarnings": {
"description": "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.",
"type": "array",
"items": {
"type": "object",
"required": [
"index",
"code",
"hint"
],
"properties": {
"index": {
"description": "Index of the rule in the request `rules` array",
"type": "number"
},
"code": {
"description": "Semantic advisory kind",
"type": "string",
"enum": [
"set_prop_not_applicable",
"set_prop_partial",
"depth_out_of_range",
"invalid_axis_for_type",
"conflicting_position_axis",
"ordered_marker_literal",
"ordered_marker_is_bullet",
"align_needs_cell_scope",
"set_action_inert",
"rule_fully_shadowed",
"rule_no_match"
]
},
"hint": {
"description": "Actionable fix for the author",
"type": "string"
},
"prop": {
"description": "`set` property that does not apply (`set_prop_not_applicable`)",
"type": "string"
},
"axis": {
"description": "Selector axis not valid for the type (`invalid_axis_for_type`)",
"type": "string"
},
"type": {
"description": "Selected node type(s) checked against",
"type": "string"
},
"depth": {
"description": "Out-of-range heading depth (`depth_out_of_range`)",
"type": "number"
}
}
}
}
}
},
{
"anyOf": [
{
"$ref": "#/$defs/Row.validation_failed.render"
},
{
"$ref": "#/$defs/Row.content_not_paginable"
},
{
"$ref": "#/$defs/Row.image_too_large"
},
{
"$ref": "#/$defs/Row.invalid_google_fonts_url"
},
{
"$ref": "#/$defs/Row.google_font_not_found"
},
{
"$ref": "#/$defs/Row.google_font_weight_unavailable"
},
{
"$ref": "#/$defs/Row.asset_kind_mismatch"
},
{
"$ref": "#/$defs/Row.font_embedding_prohibited"
},
{
"$ref": "#/$defs/Row.font_malformed"
},
{
"$ref": "#/$defs/Row.font_source_unavailable"
},
{
"$ref": "#/$defs/Row.memory_budget_exceeded"
},
{
"$ref": "#/$defs/Row.server_busy"
},
{
"$ref": "#/$defs/Row.memory_share_exceeded"
},
{
"$ref": "#/$defs/Row.table_too_many_columns"
},
{
"$ref": "#/$defs/Row.service_unavailable"
}
]
}
]
}
},
"account": {
"description": "Opaque owning account id when the caller is authenticated.",
"type": "string"
},
"tier": {
"description": "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.",
"type": "string"
}
},
"$defs": {
"Error.font_malformed.fontFault": {
"additionalProperties": false,
"description": "Cause and tables of a `font_malformed` refusal. Same value on every surface that reads the font",
"type": "object",
"required": [
"cause",
"tables"
],
"properties": {
"cause": {
"description": "Why the font was refused: `parseBudget` (a table needs more parse work than the safety budget), `outOfBounds` (truncated, or an offset outside the font data), `unsupportedFormat` (not an sfnt font, or a table format or version the engine does not read), `invalidStructure` (a structural rule of the OpenType specification is broken), or `outOfFaceReferences` (tables reference glyph ids past the face)",
"type": "string",
"enum": [
"parseBudget",
"outOfBounds",
"unsupportedFormat",
"invalidStructure",
"outOfFaceReferences"
]
},
"tables": {
"minItems": 1,
"description": "sfnt tables the fault is in (`CFF` for the `CFF ` table), or `sfnt` for the font file as a whole, `WOFF` or `WOFF2` for the web font container it was uploaded in; every referencing table on `outOfFaceReferences`",
"type": "array",
"items": {
"minLength": 1,
"type": "string"
}
}
}
},
"Error.google_font_not_found.didYouMean": {
"description": "Present when a missing family name is a typo of a sibling family declared in the same request; each entry carries that family as `value`",
"type": "array",
"items": {
"$ref": "#/$defs/Error.google_font_not_found.didYouMean.item"
}
},
"Error.google_font_not_found.didYouMean.item": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"value": {
"type": "string"
},
"index": {
"type": "integer"
}
}
},
"Error.google
# --- truncated at 32 KB (141 KB total) ---
# Full source: https://artifacts.apis.io/blinkpdf-io/json-schema/blinkpdf-io-render-validate-ok-schema.json
Work with this as data
Every JSON Schema here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for schemas
4 MCP tools reach this
find_json_schemasBrowse and filter every JSON Schema in the catalog.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.
Call it yourself
curl for this page
This JSON Schema
curl "https://apis.io/api/v1/json-schemas/blinkpdf-io-render-validate-ok"
All schemas
curl "https://apis.io/api/v1/json-schemas?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.