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 or render).
- inputFormat — markdown or dsl.
- pageSize, pageCount, inputBytes, outputBytes, renderMs, tagged — sizes, timings, and output flags. Numbers only.
- Identity, exactly one of: your user id plus API key id (Bearer / session callers), or your wallet address plus transaction hash (x402 wallet callers).
- On failure only: a short error code plus a structural fingerprint of the input (counts and sizes, e.g. number of tables, length of the largest table cell) so we can reproduce the fault.
- 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.
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 shape signal at all — both columns are stored null. They have no data object, and any descriptor of the markdown's own structure would still work as a document fingerprint. 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 resolved options, and boolean flags for which markdown features the document used. On a failure it also holds a short error code and a counts-and-sizes fingerprint of the input.
It records no hash of your markdown — identical documents from different callers are indistinguishable in the table. 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 both shape signals for that request (both columns are stored as null). Payment records are still written — we need them for revenue and refunds.
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.