Rendering
Three endpoints: render and store a statement, render a throwaway preview with unsaved edits applied, and download a stored render.The renderer emits a self-contained HTML document. The logo is embedded as base64 and there are no external requests, so the browser’s print-to-PDF produces the finished statement. Ask for
format: "pdf" and the server does that print step for you, returning a PDF instead. See PDF output.POST /api/funds/{report_id}/render
Render the report and store the output.Request body
All fields optional. Anything omitted falls back to the period saved on the report’s configuration.string
Display label shown in the header, for example
Q1 2026.string
YYYY-MM-DD. Drives the chart x-axis and the fee accrual window.string
YYYY-MM-DD. The as-of stamp and the FX snapshot date.string
default:"html"
html or pdf. See PDF output. Any other value returns 400.Response
download_url to get the document.
The report is always rendered at portfolio scope. If a manager runs several LP mandates, each mandate gets its own portfolio and its own report; there is no per-investor parameter here. For per-investor figures, see the LP Portal.
Errors
The failures that behave this way:404 is returned for an unknown report_id.
Layout warnings
compact_overflow and recap_overflow are advisory. When set, the document was still produced, but part of it will clip when printed. Show a banner rather than blocking.
compact_overflow is only ever computed for reports whose layout is compact. On the default statement layout and on institutional it is always null, so most integrations will never see it populated.
estimated_height_px, budget_height_px, overflow_px, n_strategies, message). It is null when the recap fits, is disabled, has no data, or spans multiple print pages by design.
POST /api/funds/{report_id}/render-preview
Render with unsaved configuration applied, and return the document inline. Nothing is written: no database changes, no stored file. This is what drives live editing, where the client debounces the edit form and posts the dirty patch.Request body
All optional.string
Display label,
period_start and period_end behave as on /render above.object
A patch in the same shape
PUT /config accepts, applied in memory before rendering.Responses
422 is the expected outcome while a user is mid-typing. Keep the last good preview on screen and show the message rather than swapping in a broken frame.
Because this endpoint reports validation failures properly, it is also the right tool for diagnosing a /render that returns 500.
Warnings ride on response headers so they can be read without parsing the document:
X-Recap-Overflow-Px: pixels by which the recap page exceeds its budget. Absent when the recap fits, is disabled, or has no data.
GET /output/{filename}
Download a stored render. Returns the HTML document as an attachment.{report_id}_fund_{random}.html. Access is checked against the report id embedded in the filename, so learning a URL is not enough to read another fund’s statement. Read access is sufficient, which is what lets a recipient download a statement they cannot re-render.
PDF output
Server-side PDF is rolling out. If
format: "pdf" answers 503 on your environment, it has not reached it yet; the client-side path below works everywhere in the meantime.Ask the server for it
Addformat: "pdf" to the /render body, or post to the dedicated route:
POST /api/funds/{report_id}/render-pdf takes the same body and returns the same response as /render; it exists so a client that only ever wants PDFs has a URL to hardcode instead of remembering a body flag. filename and download_url point at a .pdf, and GET /output/{filename} serves it as application/pdf.
The server is not building a different document. It renders the same HTML and prints it with the same browser engine you would use yourself, so the PDF matches the HTML exactly.
Conversions are capped and queued server-side, so a burst of PDF requests waits rather than failing.

