Privacy for agents
You may be rendering invoices, contracts, statements, or medical reports through this API. This page lists exactly what we record about each render, what we deliberately never record, and how to opt out of the optional part. If it is not on this page, we do not collect it.
What we record on every render
One append-only row per render attempt in our usage table (on md and render only — the free preview draft endpoint writes no usage row), used for metering, debugging failures, and understanding which kinds of documents agents render. Anonymous promo renders (no auth) write no usage row at all.
- endpoint — which API you called (md, render, or the MCP endpoint's mcp:render_template / mcp:render_markdown).
- inputFormat — markdown or dsl.
- pageSize, pageCount, inputBytes, outputBytes, renderMs, layoutMs, writeMs, tagged — sizes, timings (total, layout pass, and PDF writing), and output flags. Numbers only.
- On md rows, the markdown options the render resolved with (fontFamily, theme) — so the options mix can be compared across endpoints.
- Which client made the call: a low-cardinality kind (cli, vscode, obsidian, curl, browser, …) plus a version when your client identifies itself with the X-MakesPDF-Client header; otherwise the kind is inferred from your User-Agent. Recorded on failure rows too, so we can tell which integrations are hitting errors.
- A counts-only histogram of layout warnings, keyed by warning code (font-not-registered, word-break-measurement-failed, page-split-overflow) — for example a render that had to clip content records {"page-split-overflow": 1}. Counts only: never the warning's message or detail, which can quote the document. Null when the layout ran clean.
- Identity, exactly one of: your user id plus API key id (Bearer / session callers), or your wallet address plus transaction hash (x402 wallet callers).
- A structural fingerprint of the input on every render — counts and sizes only, never text (e.g. number of tables, length of the largest table cell, code block sizes, declared image dimensions). It lets us compare a failure against the documents that normally go through the system. On failure rows it sits alongside a short error code. X-No-Telemetry: true suppresses it on successful renders; failures are still fingerprinted, as they always have been.
- dataShapeHash and dataTopLevelKeys — the shape signal described below. render only; on md both are null.
- Render artifacts (authenticated renders, including preview): your input source and the output PDF, kept for 7 days so you can re-download them from /settings/renders (or the X-Artifact-Id response header). Filing an issue report against an artifact extends its retention to 30 days. Anonymous renders and wallet-paid md / render calls store no artifacts. A wallet-paid enhance job stores its input and output PDFs as artifacts owned by the paying wallet, downloadable only with that job's poll token, on the same retention.
MCP renders
The MCP endpoint (POST /api/v1/mcp) writes the same usage row as the REST endpoints for its billed tools: render_template records like /render — including the data-shape signal — and render_markdown records like /md. The free render_dsl_preview tool, like /preview, writes no usage row at all.
Every MCP request also writes one operational log line — the JSON-RPC method, tool name, HTTP status, duration, caller kind, the outcome, and the error message when a tool failed — and an initialize request logs the client name and version the MCP client declares in its handshake. Tool arguments (DSL, markdown, render data) are never logged, here or anywhere else.
The report_feedback tool is the one exception where text you (the model) write is stored: the summary is kept exactly as submitted, linked to the account the call is made under, for 180 days, then deleted. The tool asks the model not to include document content — no document text, markdown, DSL source or data values — but we cannot guarantee a model complies, so treat the stored text with that in mind. Sending feedback is optional; nothing calls the tool automatically, and it never affects the user’s renders or credits.
The shape signal (no values, ever)
Knowing the shape of the data object — without any of its values — tells us whether agents render mostly invoices or mostly reports, which is genuinely useful product signal. It is captured on two levels:
- dataShapeHash — sha256 over the sorted list of { keyPath, type, isArray } tuples describing your data (walked to depth 6 and at most 2,000 distinct tuples, first array element only). A key survives into the hash only if it is on a fixed allowlist of canonical business-document field names — the ones drawn from UBL, Peppol, India GST, EDI X12, Stripe and Schema.org, such as invoiceNumber, customer, total — plus a handful of container names like items and lineItems. Every other key is replaced by one shared <other> before hashing: an email address, an invoice number, a customer id, a person's name, a SKU, a date. On for every metered render unless you opt out (see below).
- dataTopLevelKeys — the top-level keys of your data object only (no nested keys, no values, at most 20, sorted). The same allowlist applies: a key that is not a canonical field name is recorded as <other>, never verbatim. For example ["customer", "lineItems", "total"] says "invoice" without saying anything about the customer. Launch window only — see below.
The hash lets us correlate recurring templates — “this same shape rendered 50 times in an hour”. Being a plain sha256 with no secret key, it is checkable rather than secret: someone holding both our database and a candidate schema could hash that schema and see whether it was used. It cannot be run backwards into your data, it contains no values, and the only key names that reach it are the allowlisted ones above.
POST /api/v1/md and POST /api/v1/markdown record no data-shape signal — both columns are stored null, because there is no data object. They do record the structural fingerprint listed above on every render. That fingerprint is counts and sizes only, but it is specific enough that someone holding both our database and a candidate document could measure the document and see whether it matches. Send X-No-Telemetry: true to suppress it on successful renders. Anonymous promo renders write no usage row at all; what they do write is covered below.
Anonymous promo renders
When the operator has the anonymous md promo switched on, an unauthenticated call writes no usage row and stores no artifact. It writes one analytics row instead — the only abuse signal we have for a caller with no account. That row holds your IP hashed with a salt that rotates daily, your country, a truncated user-agent string, which client you appear to be, the referring host, page count, input and output sizes, render time (the total plus the layout and PDF-writing split), the resolved options, a counts-only layout-warning histogram, and boolean flags for which markdown features the document used. It also holds a counts-and-sizes fingerprint of the input on every render — plus a short error code on failures. The fingerprint never contains text, and X-No-Telemetry: true suppresses it on successful renders.
It records no hash of your markdown text. The structural fingerprint is not a content hash, but identical documents produce identical fingerprints, so it can be matched against a candidate document as described above. Authenticated and wallet-paid renders never get a content hash either. These rows are deleted after 30 days.
What we never record
- Your data values. Your markdown text and DSL source live only in your own render artifacts above — nowhere else.
- Nested data keys past the top level (a field like patient.diagnosisCode would leak context even without its value).
- Output PDF bytes beyond your own render artifacts above and the 5-minute idempotency cache (kept in short-lived cache storage, not the database).
Opt out
Send X-No-Telemetry: true on any render request to suppress the data-shape signals and the document fingerprint for that request (those columns are stored as null). Payment records are still written — we need them for revenue and refunds — and a failed render still carries its fingerprint and error code, so we can debug what broke. The operational metrics — sizes, timings (including layoutMs / writeMs), the layout-warning counts and the client kind — are not document-shape signals, so the opt-out does not null them.
curl -X POST https://makespdf.com/api/v1/render \
-H "Authorization: Bearer $MAKESPDF_API_KEY" \
-H "Content-Type: application/json" \
-H "X-No-Telemetry: true" \
-d '{"templateId": "invoice-classic", "data": {...}}' -o invoice.pdfLaunch window for top-level keys
dataTopLevelKeys is collected for a launch-learning window only. Review date: 2026-12-11. On or shortly after that date we either stop populating the column and drop it, or publish a decision extending the window here. The hash stays, on the terms described above.
More
The full privacy policy (account data, subprocessors, retention, your rights) lives at /legal/privacy. API questions: jason@makespdf.com.