White-Label Client Report PDFs for Agencies
Agency reporting is a recurring layout problem: every client has its own branding, the data changes every month, and one broken chart can reach the client before anyone notices. This guide shows a white-label report pipeline with Fork PDF.
1. The report pipeline
The simplest pipeline is data → HTML template → PDF → delivery. Save the report layout as a template, pass each client's data, and generate one PDF per client and period.
const report = await client.generate({
templateId: 'YOUR_REPORT_TEMPLATE_ID',
data: {
client: { name: 'Acme', logoUrl: 'https://cdn.example.com/acme.png' },
brand: { primary: '#0f172a' },
period: 'Q3 2026',
metrics: [
{ label: 'Revenue', value: '$1.24M', change: '+18%' },
{ label: 'Leads', value: '4,820', change: '+9%' }
]
},
downloadBuffer: true
});
2. Multi-client branding
Use one template with data-driven branding: logo URL, primary color, client name, and footer. Avoid inline image data; use image URLs. One tested template is easier to maintain than one template per client.
const brand = {
logoUrl: client.logoUrl,
primary: client.brandColor,
footer: client.name + ' · Confidential'
};
Fork PDF does not add its own branding to the generated PDF. The finished report is your brand.
3. Keep charts with commentary
A chart that lands on a different page from its commentary is the classic report break. Use print CSS for fixed blocks and a testCase for dynamic content.
const chart = doc.querySelector('#revenue-chart');
const note = doc.querySelector('#revenue-note');
if (!chart || !note) {
return { success: false, reason: 'Missing chart or commentary' };
}
if (chart.pages[0] !== note.pages[0]) {
return {
success: false,
patch: { compactCharts: true },
reason: 'Chart and commentary split across pages'
};
}
return { success: true };
See Prevent page breaks inside tables and charts.
4. TOC and page numbers
Reports need accurate page numbers. Measure headings after layout, then patch the TOC and re-render. The TOC guide has the full pattern.
Table of contents with real page numbers
5. Validate every client report
Run a layout check per report: chart/commentary, table row counts, TOC values, footer, and logo. Return custom keys in testResults so your CI or delivery job can flag the account before the client sees it.
const rows = doc.querySelectorAll('table.metrics tbody tr');
const footer = doc.querySelector('.report-footer');
return {
success: Boolean(rows.length && footer),
client: 'Acme',
period: 'Q3 2026',
rows: rows.length
};
See Automated PDF layout testing.
6. Deliver at scale
For monthly batches, use webhook: true and let the API deliver each result to your endpoint. Verify the signature, download the file, and store it in your own client portal before the URL expires.
Async PDF generation with webhooks · Webhook delivery
7. White-label checklist
- Logo and brand colors come from data, not hardcoded per client.
- Fonts support every client language and currency.
- Page size, margins, and footer are consistent across reports.
- Footer includes page numbers and confidentiality text.
- Layout check covers chart/commentary, TOC, and totals.
- Test with long client names, large numbers, and missing metrics.
Test one report template
Generate a sample client report and inspect the layout check.