Skip to content

Render a raw PDF

The same render endpoint, asked for a different representation. Send Accept: application/pdf and Pagr streams the PDF binary as the response body rather than wrapping it in a JSON envelope — so you skip Base64-decoding a JSON field. The document metadata travels in X-Pagr-* response headers instead.

POST /v1/render/{templateId}
POST /v1/render/{templateId}/versions/{version}

Identical to Render a document — only the Accept header differs. The response shape is decided solely by content negotiation, never by flags or by how many documents happened to render.

import asyncio
from pagr import PagrApiClient
async def main():
async with PagrApiClient("pagr_prod_xxxxxxxx") as client:
result = await client.render_pdf(
"8bec66ff-6f3d-4c1e-9a2b-1f0e5d7c4a90",
{"Title": "Acme Q3 Invoice", "Amount": 42},
)
if result.ok:
doc = result.document
print(doc.document_name, doc.page_count, doc.render_duration)
doc.save("out/") # or: pdf_bytes = doc.to_bytes()
else:
# No PDF to stream — the reasons come back as data, not an exception.
print(result.status, result.message)
for issue in result.issues:
print(issue)
asyncio.run(main())
Parameter Type Required Description
templateId string (UUID) Yes The template to render.
version integer Only for the specific-version form The template version number to render.
Parameter Type Default Description
persist boolean true Whether to store the rendered document. When false, X-Pagr-Document-Id and X-Pagr-View-Url are omitted.
language string Language variant to render, for multilingual templates.
Header Value Description
Accept application/pdf Required to get the raw stream. A wildcard (*/*) does not count — the JSON envelope is the default representation.

Exactly the same body as Render a document, with exactly one entry in documents. includeDocument is ignored — the bytes are always streamed.

{
"documents": [
{ "Title": "Acme Q3 Invoice", "Amount": 42 }
]
}

200 OK with Content-Type: application/pdf — the response body is the PDF. The filename comes back in Content-Disposition, and the metadata the JSON envelope would have carried is split across headers:

HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="Acme Q3 Invoice.pdf"
X-Pagr-Document-Id: f61aeff4-2c9d-4b7a-8e10-3a5b9c2d1e00
X-Pagr-Page-Count: 1
X-Pagr-Render-Duration-Ms: 412.7
X-Pagr-View-Url: https://…
Header Description
Content-Disposition attachment; filename="<documentName>.pdf". The SDKs strip the extension so document_name matches the JSON envelope’s no-extension convention.
X-Pagr-Document-Id The stored document id. Omitted when persist=false.
X-Pagr-Page-Count Number of pages.
X-Pagr-Render-Duration-Ms Render time in milliseconds.
X-Pagr-View-Url Signed download URL. Omitted when persist=false.
X-Pagr-Issue-Count Number of non-blocking issues. Omitted when there were none.

A blocked or failed render has no PDF to stream, so the API does not silently change shape. It returns 422 Unprocessable Entity with the ordinary JSON envelope as the body:

{
"status": "failed",
"message": "0 document(s) rendered; 1 skipped.",
"requestedCount": 1,
"renderedCount": 0,
"missingCount": 1,
"issues": [
{ "type": "MissingBinding", "severity": "Warning", "description": "…", "documentIndex": 0 }
],
"documents": []
}

The SDKs treat this as a result, not an exception: result.ok is false and the reasons are in result.status / result.issues. Only genuine protocol failures raise.

SDK Method
Python await client.render_pdf(template_id, json_data, *, version=None, language=None, persist=True, timeout=None)PdfRenderResult
TypeScript await client.renderPdf(templateId, data, options?)PdfRenderResult
Java client.renderPdf(templateId, data[, RenderOptions])PdfRenderResultdata is a JSON String, JsonObject or Map
C# await client.RenderPdfAsync(templateId, data, version, language, persist, timeout, cancellationToken)PdfRenderResult
Ruby client.render_pdf(template_id, json_data, version:, language:, persist:, timeout:)PdfRenderResult
C++ client.render_pdf(template_id, json_data, RenderOptions) / render_pdf_async(…)PdfRenderResult
Status When
406 NotAcceptable Accept: application/pdf on a request with more than one document.
422 The render was blocked — body is the JSON envelope (see above), not an error envelope.

Everything else matches Render a document. See Errors for the full table.