---
name: render-pdf
description: "Render Markdown into a polished, paginated PDF with BlinkPDF's hosted MCP server. Use when an agent must turn Markdown — headings, tables, images, code, running headers and footers — into a downloadable PDF and return a signed link."
license: Proprietary
---

# Render Markdown to PDF with BlinkPDF

BlinkPDF turns Markdown into a print-ready PDF and returns a short-lived,
signed download URL. It is exposed to agents over the Model Context Protocol
(MCP); there are no inline PDF bytes to handle.

## Connect

- MCP endpoint: `https://mcp.blinkpdf.io/mcp` (Streamable HTTP, stateless).
- Auth: send your BlinkPDF API key as a bearer token —
  `Authorization: Bearer bp_...` (or `x-api-key: bp_...`). A `401` means the
  key is wrong or revoked; fix the key, do not retry blindly.

## Use the `render_pdf` tool

Call `render_pdf` with:

- `markdown` (required) — the document body.
- `metadata` (optional) — PDF metadata; `metadata.title` is the document title.
- `page` (optional) — size, orientation, margin, sheet background.
- `furniture` (optional) — running headers/footers; set `omitFirstPage: true`
  for a blank cover, `omitLastPage: true` to suppress furniture on the closing
  page (multi-page only), or `lastPage` for a distinct closing sign-off
  (ignored on single-page documents).
- `rules` (optional) — surgical presentation overrides as `{ select, set }`
  entries (headings, lists, tables, code, blockquotes, images, inline). Covers
  per-image styling (`select: { type: "image", url|alt|nth }`) and per-heading
  numbering/outline (`set.numbering`/`set.outline`).
- `numbering` (optional) — document heading auto-numbering policy.

## Copy-ready examples

### 1. Simple document
Markdown plus an optional metadata.title — defaults are publication-grade.

```json
{
  "markdown": "# Weekly Update\n\nShort status for the team.\n\n## Highlights\n\n- Shipped the new dashboard\n- Cut render latency by 30%",
  "metadata": {
    "title": "Weekly Update"
  }
}
```

### 2. GFM article
Rich markdown with light styles (GFM alert, striped table, code block).

```json
{
  "markdown": "# Q3 Summary\n\n> [!NOTE]\n> Draft for review.\n\n| KPI | Value |\n| --- | ---: |\n| Revenue | $1.2M |\n\n```ts\nexport const ready = true;\n```",
  "metadata": {
    "title": "Q3 Summary"
  },
  "rules": [
    {
      "select": {
        "type": "table"
      },
      "set": {
        "stripe": {
          "odd": "#f7f9fc"
        }
      }
    },
    {
      "select": {
        "type": "blockquote",
        "kind": "note"
      },
      "set": {
        "borderLeft": {
          "width": 3,
          "color": "#0969da"
        }
      }
    }
  ]
}
```

### 3. Multi-page report
Page break, page margins, heading pagination, table borders, and furniture with a blank cover.

```json
{
  "markdown": "# Annual Report\n\nExecutive summary for stakeholders.\n\n<!-- pagebreak -->\n\n## Financials\n\n| Year | ARR |\n| --- | ---: |\n| 2024 | $4M |\n\n## Outlook\n\nGrowth continues into the next fiscal year.",
  "metadata": {
    "title": "Annual Report"
  },
  "page": {
    "size": "Letter",
    "margin": {
      "top": 72,
      "right": 72,
      "bottom": 72,
      "left": 72
    }
  },
  "rules": [
    {
      "select": {
        "type": "heading",
        "depth": 2
      },
      "set": {
        "pageBreakBefore": true
      }
    },
    {
      "select": {
        "type": "table"
      },
      "set": {
        "borders": {
          "kind": "header",
          "stroke": {
            "width": 1,
            "color": "#1a73e8"
          }
        }
      }
    }
  ],
  "furniture": {
    "omitFirstPage": true,
    "slots": {
      "topRight": {
        "content": [
          {
            "kind": "documentTitle"
          }
        ]
      },
      "bottomCenter": {
        "content": [
          {
            "kind": "text",
            "value": "Page "
          },
          {
            "kind": "pageNumber"
          },
          {
            "kind": "text",
            "value": " of "
          },
          {
            "kind": "totalPages"
          }
        ]
      }
    }
  }
}
```

### 4. One-pager
Letter layout with sheet background, outline disabled, and a minimal footer stamp.

```json
{
  "markdown": "# Product Brief\n\nOne page, no bookmarks.\n\n- Ship date: Q3\n- Owner: Platform",
  "metadata": {
    "title": "Product Brief"
  },
  "page": {
    "size": "Letter",
    "orientation": "portrait",
    "margin": 48,
    "background": {
      "color": "#f8fafc"
    }
  },
  "outline": {
    "enabled": false
  },
  "furniture": {
    "slots": {
      "bottomRight": {
        "content": [
          {
            "kind": "text",
            "value": "CONFIDENTIAL",
            "style": {
              "size": 8,
              "color": "#64748b"
            }
          }
        ]
      }
    }
  }
}
```

It returns a delivery receipt text block, a `resource_link` download URL, and
`structuredContent` — never inline PDF bytes.

## Document size

BlinkPDF does **not** silently truncate markdown. The only product input caps
are per-tier UTF-8 JSON body bytes (free 128 KiB, pro 2 MiB, business 5 MiB). Successful calls
report measured body size and `withinLimit` in the result text (and on
validate in `structuredContent.limits`), including the phrase
`Input accepted` when `withinLimit` is true. Trust those numbers — do not
invent a ~15–25k character product limit, and do not claim truncation when the
result text shows Input accepted / withinLimit: true. Host tool-result budgets
are not BlinkPDF markdown cuts.

## After every render

- Read `diagnostics` and `renderedAsRequested` in `structuredContent`.
  `status` can be `ok` while `renderedAsRequested` is `false` (e.g. an image
  failed to fetch) — the PDF may omit images. Conversely, `status` can be
  `degraded` with only warnings while `renderedAsRequested` stays `true` — the
  delivery receipt then says `Degraded (warnings only)` and still rendered as
  requested.
- Prefer `https://` image URLs in Markdown; they are fetched server-side and
  keep the request small. Large `data:` base64 images often exceed the body
  limit — reserve `data:` for tiny icons.
- The download URL is signed and expires 24 hours after it is issued; read the
  exact instant from `structuredContent.expiresAt` and deliver or fetch the
  link before then. Default storage is 24h ephemeral. To keep a file, pass
  `persist: true` on `render_pdf`, or call `persist_pdf` with `ids`
  (length 1 is fine) while the 24h objects still exist. Persistent files
  remain, but links still expire in 24h — call `list_pdfs` for a fresh URL.

## Hand off to the user

Present the delivery receipt text from the tool result **verbatim** — the block
starting with `BlinkPDF · Delivery receipt`. Do not paraphrase, bury the link,
or substitute a local file path for the signed URL. Use
`structuredContent.deliveryReceipt` if your client surfaces structured output
instead of the text block.

## Billing

Free-tier renders may hit rate limits or return `402 spend_cap_reached`.
Upgrades are **human-in-the-loop**: the operator signs in on Blink PDF Platform
(`/login`) and upgrades on `/billing` — agents cannot open Stripe on their own.
API keys are created on `/api-keys`. Plan limits and routes are published as
`agent_commerce` on `/.well-known/oauth-authorization-server` and
`/.well-known/blinkpdf-commerce.json`. See `https://mcp.blinkpdf.io/auth.md#billing`.

## Reference resources

Start from the copy-ready examples above. Pull these MCP resources only for keys not
shown in your chosen example:
`blinkpdf://reference/render-options`, `/furniture`, `/images`, `/glyphs`,
`/tables`, `/styles`.
