Skip to main content

Generate PDF Documents

Create professional PDF documents from templates using the RenderDoc API.


Overview

RenderDoc allows you to generate PDF documents dynamically from templates. This tutorial covers:

  • Creating a PDF template (via AI, gallery, or manually)
  • Generating PDFs via the API
  • Batch document generation
  • Webhook notifications for async processing

This is perfect for invoices, receipts, reports, contracts, certificates, and any document you need to generate programmatically.


Prerequisites

Before starting, make sure you have:

  • An active RenderDoc account
  • An API key with document generation permissions
  • A published PDF template (see Create a Template)

How PDF Generation Works

RenderDoc uses templates to generate PDFs:

  1. Create a Template - Design your PDF layout in the visual editor
  2. Call the API - Send a request with your template ID and variables
  3. Receive the PDF - Get a download URL for the generated document
Template + Variables → API Call → PDF Document

Quick Start

Generate your first PDF in under a minute:

Step 1: Get Your Template ID

  1. Log in to your RenderDoc Dashboard
  2. Go to Templates
  3. Find your template and copy the Template ID (UUID or slug)

Step 2: Make an API Call

curl -X POST https://api.renderdoc.dev/api/v1/documents/generate \
-H "Authorization: Bearer rd_sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"templateId": "invoice-template",
"format": "pdf",
"variables": {
"invoiceNumber": "INV-2025-001",
"customerName": "Acme Corp",
"total": "$1,250.00"
}
}'

Step 3: Download Your PDF

The API returns a download URL:

{
"id": "doc_abc123xyz",
"status": "completed",
"format": "pdf",
"downloadUrl": "https://cdn.renderdoc.dev/documents/doc_abc123xyz.pdf",
"expiresAt": "2025-12-30T12:00:00.000Z"
}

Download your PDF from the downloadUrl before it expires.


Code Examples

JavaScript / Node.js

const API_KEY = process.env.RENDERDOC_API_KEY;

async function generateInvoice(invoiceData) {
const response = await fetch('https://api.renderdoc.dev/api/v1/documents/generate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
templateId: 'invoice-template',
format: 'pdf',
variables: {
invoiceNumber: invoiceData.number,
invoiceDate: invoiceData.date,
customerName: invoiceData.customer.name,
customerAddress: invoiceData.customer.address,
items: invoiceData.items,
subtotal: invoiceData.subtotal,
tax: invoiceData.tax,
total: invoiceData.total,
},
}),
});

const result = await response.json();

if (!response.ok) {
throw new Error(result.message);
}

console.log('PDF generated:', result.downloadUrl);
return result;
}

// Usage
generateInvoice({
number: 'INV-2025-001',
date: 'December 29, 2025',
customer: {
name: 'Acme Corp',
address: '123 Business St, New York, NY 10001',
},
items: [
{ description: 'Consulting', quantity: 10, price: '$1,500.00' },
{ description: 'Software License', quantity: 1, price: '$500.00' },
],
subtotal: '$2,000.00',
tax: '$170.00',
total: '$2,170.00',
});

Python

import requests
import os

API_KEY = os.environ.get('RENDERDOC_API_KEY')
API_URL = 'https://api.renderdoc.dev/api/v1/documents/generate'

def generate_invoice(invoice_data):
response = requests.post(
API_URL,
headers={
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json',
},
json={
'templateId': 'invoice-template',
'format': 'pdf',
'variables': {
'invoiceNumber': invoice_data['number'],
'invoiceDate': invoice_data['date'],
'customerName': invoice_data['customer']['name'],
'customerAddress': invoice_data['customer']['address'],
'items': invoice_data['items'],
'subtotal': invoice_data['subtotal'],
'tax': invoice_data['tax'],
'total': invoice_data['total'],
},
},
)

result = response.json()

if not response.ok:
raise Exception(result.get('message', 'Unknown error'))

print(f"PDF generated: {result['downloadUrl']}")
return result

# Usage
generate_invoice({
'number': 'INV-2025-001',
'date': 'December 29, 2025',
'customer': {
'name': 'Acme Corp',
'address': '123 Business St, New York, NY 10001',
},
'items': [
{'description': 'Consulting', 'quantity': 10, 'price': '$1,500.00'},
{'description': 'Software License', 'quantity': 1, 'price': '$500.00'},
],
'subtotal': '$2,000.00',
'tax': '$170.00',
'total': '$2,170.00',
})

Using the RenderDoc SDK

import RenderDoc from '@renderdoc/sdk';

const client = new RenderDoc(process.env.RENDERDOC_API_KEY);

const result = await client.documents.generate({
templateId: 'invoice-template',
format: 'pdf',
variables: {
invoiceNumber: 'INV-2025-001',
customerName: 'Acme Corp',
total: '$2,170.00',
},
});

console.log('Download URL:', result.downloadUrl);

Batch Generation

Generate multiple documents in a single API call:

const response = await fetch('https://api.renderdoc.dev/api/v1/documents/generate/batch', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
templateId: 'invoice-template',
format: 'pdf',
documents: [
{
variables: {
invoiceNumber: 'INV-001',
customerName: 'Customer A',
total: '$500.00',
},
},
{
variables: {
invoiceNumber: 'INV-002',
customerName: 'Customer B',
total: '$750.00',
},
},
{
variables: {
invoiceNumber: 'INV-003',
customerName: 'Customer C',
total: '$1,200.00',
},
},
],
}),
});

const batch = await response.json();
console.log('Batch ID:', batch.batchId);
console.log('Documents:', batch.documents);

Batch Response

{
"batchId": "batch_xyz789",
"status": "processing",
"total": 3,
"completed": 0,
"failed": 0,
"documents": [
{ "id": "doc_001", "status": "pending" },
{ "id": "doc_002", "status": "pending" },
{ "id": "doc_003", "status": "pending" }
]
}

Check Batch Status

const status = await fetch(
`https://api.renderdoc.dev/api/v1/documents/batches/${batchId}`,
{
headers: { 'Authorization': `Bearer ${API_KEY}` },
}
).then(r => r.json());

console.log('Batch status:', status);

Webhook Notifications

Get notified when documents are generated:

Configure Webhook

  1. Go to SettingsWebhooks in your dashboard
  2. Add a webhook endpoint URL
  3. Select events: document.generated, document.failed, batch.completed

Webhook Payload

{
"event": "document.generated",
"timestamp": "2025-12-29T10:30:00.000Z",
"data": {
"id": "doc_abc123",
"templateId": "invoice-template",
"format": "pdf",
"downloadUrl": "https://cdn.renderdoc.dev/documents/doc_abc123.pdf",
"expiresAt": "2025-12-30T10:30:00.000Z",
"metadata": {
"invoiceNumber": "INV-2025-001"
}
}
}

Webhook Handler Example

// Express.js webhook handler
app.post('/webhooks/renderdoc', (req, res) => {
const { event, data } = req.body;

switch (event) {
case 'document.generated':
console.log(`Document ready: ${data.downloadUrl}`);
// Download and store the PDF
// Notify the user
break;

case 'document.failed':
console.error(`Document failed: ${data.error}`);
// Handle the error
// Retry if appropriate
break;

case 'batch.completed':
console.log(`Batch complete: ${data.total} documents`);
// Process all documents
break;
}

res.status(200).send('OK');
});

Output Formats

RenderDoc supports multiple output formats:

FormatExtensionUse Case
pdf.pdfDocuments, invoices, reports
xlsx.xlsxSpreadsheets, data exports

Generate Excel

const result = await fetch('https://api.renderdoc.dev/api/v1/documents/generate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
templateId: 'report-template',
format: 'xlsx', // Excel format
variables: {
reportTitle: 'Monthly Sales Report',
month: 'December 2025',
data: salesData,
},
}),
}).then(r => r.json());

Custom Filenames

Specify a custom filename for the generated document:

const result = await fetch('https://api.renderdoc.dev/api/v1/documents/generate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
templateId: 'invoice-template',
format: 'pdf',
filename: 'Invoice-{{invoiceNumber}}-{{customerName}}', // Dynamic filename
variables: {
invoiceNumber: 'INV-2025-001',
customerName: 'AcmeCorp',
},
}),
}).then(r => r.json());

// Result: Invoice-INV-2025-001-AcmeCorp.pdf

Metadata

Add metadata to track documents:

const result = await fetch('https://api.renderdoc.dev/api/v1/documents/generate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
templateId: 'invoice-template',
format: 'pdf',
variables: { /* ... */ },
metadata: {
orderId: 'order-123',
customerId: 'cust-456',
source: 'api',
},
}),
}).then(r => r.json());

Metadata is returned in webhooks and can be used to correlate documents with your systems.


Real-World Example: Invoice System

Complete example integrating with an order system:

class InvoiceService {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.renderdoc.dev/api/v1';
}

async generateInvoice(order) {
// Calculate totals
const subtotal = order.items.reduce(
(sum, item) => sum + item.quantity * item.price,
0
);
const tax = subtotal * (order.taxRate / 100);
const total = subtotal + tax;

// Format items for template
const formattedItems = order.items.map(item => ({
description: item.name,
quantity: item.quantity,
unitPrice: this.formatCurrency(item.price),
amount: this.formatCurrency(item.quantity * item.price),
}));

// Generate PDF
const response = await fetch(`${this.baseUrl}/documents/generate`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
templateId: 'invoice-template',
format: 'pdf',
filename: `Invoice-${order.invoiceNumber}`,
variables: {
invoiceNumber: order.invoiceNumber,
invoiceDate: this.formatDate(order.createdAt),
dueDate: this.formatDate(order.dueDate),
companyName: 'Your Company',
companyAddress: '123 Business St, City, ST 12345',
customerName: order.customer.name,
customerAddress: order.customer.address,
customerEmail: order.customer.email,
items: formattedItems,
subtotal: this.formatCurrency(subtotal),
taxRate: order.taxRate,
taxAmount: this.formatCurrency(tax),
total: this.formatCurrency(total),
paymentTerms: 'Payment due within 30 days.',
},
metadata: {
orderId: order.id,
customerId: order.customer.id,
},
}),
});

return response.json();
}

formatCurrency(amount) {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(amount);
}

formatDate(date) {
return new Date(date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
}
}

// Usage
const invoiceService = new InvoiceService(process.env.RENDERDOC_API_KEY);

const order = {
id: 'order-123',
invoiceNumber: 'INV-2025-001',
createdAt: new Date(),
dueDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
taxRate: 8.5,
customer: {
id: 'cust-456',
name: 'John Doe',
email: '[email protected]',
address: '456 Customer Lane, New York, NY 10001',
},
items: [
{ name: 'Product A', quantity: 2, price: 99.99 },
{ name: 'Product B', quantity: 1, price: 149.99 },
],
};

invoiceService.generateInvoice(order)
.then(result => console.log('Invoice generated:', result.downloadUrl))
.catch(err => console.error('Error:', err));

PDF Design Best Practices

Layout

  1. Use standard page sizes - A4 for international, Letter for US
  2. Set appropriate margins - 40-60px for printing
  3. Keep it single page - When possible, fit content on one page
  4. Use visual hierarchy - Clear headings, grouped sections

Typography

  1. Professional fonts - Arial, Helvetica, Georgia work best
  2. Readable sizes - 10-12pt for body, 14-18pt for headings
  3. Consistent alignment - Right-align numbers for easy scanning
  4. Adequate spacing - White space improves readability

Branding

  1. Include your logo - Top-left is traditional for invoices
  2. Use brand colors - Sparingly for headers and accents
  3. Professional footer - Contact info and company details

Troubleshooting

PDF Not Generating

Solutions:

  1. ✅ Verify template exists and is published
  2. ✅ Check all required variables are provided
  3. ✅ Ensure API key has document generation permissions
  4. ✅ Check for validation errors in the response

Layout Issues in PDF

Solutions:

  1. ✅ Preview the template before generating
  2. ✅ Use fixed widths for columns in tables
  3. ✅ Avoid very long unbroken text
  4. ✅ Test with maximum expected data length

Missing Data in PDF

Solutions:

  1. ✅ Check variable names match exactly (case-sensitive)
  2. ✅ Verify nested object structure matches template
  3. ✅ Ensure array data is properly formatted
  4. ✅ Check the Variables tab in template editor

Download URL Expired

Solutions:

  1. ✅ Download immediately after generation
  2. ✅ Use webhooks for async processing
  3. ✅ Re-generate the document if needed
  4. ✅ Consider storing documents in your own storage

Next Steps

Now that you can generate PDF documents: