Batch Document Generation
Generate hundreds or thousands of documents in a single API call using RenderDoc batch generation.
When to Use Batch Generation
Batch generation is ideal for:
- Monthly invoicing - Generate invoices for all customers at once
- Report distribution - Create personalized reports for each team member
- Certificate generation - Produce certificates for all event attendees
- Contract processing - Generate contracts for multiple parties
Basic Batch Request
import { RenderDoc } from '@renderdoc/sdk';
const client = new RenderDoc({ apiKey: process.env.RENDERDOC_API_KEY });
const result = await client.documents.generateBatch({
templateId: 'invoice-template',
format: 'pdf',
documents: [
{
variables: {
invoiceNumber: 'INV-001',
customerName: 'Acme Corporation',
amount: 1500.00
},
filename: 'invoice-acme'
},
{
variables: {
invoiceNumber: 'INV-002',
customerName: 'Beta Industries',
amount: 2300.00
},
filename: 'invoice-beta'
},
{
variables: {
invoiceNumber: 'INV-003',
customerName: 'Gamma Systems',
amount: 890.00
},
filename: 'invoice-gamma'
}
]
});
console.log('Batch ID:', result.batchId);
console.log('Total documents:', result.totalDocuments);
Batch Response
{
"batchId": "batch_xyz789abc",
"status": "processing",
"totalDocuments": 3,
"completedDocuments": 0,
"failedDocuments": 0,
"createdAt": "2025-01-15T10:00:00Z"
}
Checking Batch Status
Poll the batch status endpoint to monitor progress:
async function waitForBatch(client, batchId) {
console.log(`Waiting for batch ${batchId}...`);
while (true) {
const status = await client.documents.getBatch(batchId);
console.log(`Progress: ${status.completedDocuments}/${status.totalDocuments}`);
if (status.status === 'completed') {
console.log('Batch completed!');
return status;
}
if (status.status === 'failed') {
throw new Error(`Batch failed: ${status.error}`);
}
// Wait 2 seconds before checking again
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
const batchResult = await waitForBatch(client, result.batchId);
// Download all documents
for (const doc of batchResult.documents) {
if (doc.status === 'completed') {
console.log(`Download: ${doc.downloadUrl}`);
} else {
console.error(`Failed: ${doc.jobId} - ${doc.error}`);
}
}
Using Webhooks (Recommended)
For production, use webhooks instead of polling:
1. Set Up Webhook
const webhook = await client.webhooks.create({
url: 'https://your-server.com/webhooks/renderdoc',
events: ['batch.completed', 'document.generated', 'document.failed']
});
console.log('Webhook secret:', webhook.secret);
2. Submit Batch
const result = await client.documents.generateBatch({
templateId: 'invoice-template',
format: 'pdf',
documents: invoiceData,
metadata: {
batchName: 'January 2025 Invoices',
triggeredBy: 'monthly-billing-job'
}
});
console.log('Batch submitted:', result.batchId);
// Don't wait - webhook will notify when complete
3. Handle Webhook Events
import express from 'express';
import { verifyWebhookSignature } from '@renderdoc/sdk';
const app = express();
app.post('/webhooks/renderdoc', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = verifyWebhookSignature({
payload: req.body.toString(),
signature: req.headers['x-renderdoc-signature'],
secret: process.env.WEBHOOK_SECRET
});
switch (event.type) {
case 'batch.completed':
console.log(`Batch ${event.data.batchId} completed!`);
console.log(`Generated: ${event.data.completedDocuments}`);
console.log(`Failed: ${event.data.failedDocuments}`);
processBatchResults(event.data);
break;
case 'document.generated':
console.log(`Document ready: ${event.data.downloadUrl}`);
break;
case 'document.failed':
console.error(`Document failed: ${event.data.error}`);
break;
}
res.status(200).send('OK');
} catch (error) {
console.error('Webhook error:', error);
res.status(400).send('Invalid signature');
}
});
Real-World Example: Monthly Invoice Generation
import { RenderDoc } from '@renderdoc/sdk';
import { getCustomersWithPendingInvoices, saveInvoiceUrl } from './database';
async function generateMonthlyInvoices() {
const client = new RenderDoc({ apiKey: process.env.RENDERDOC_API_KEY });
// Get all customers that need invoices
const customers = await getCustomersWithPendingInvoices();
console.log(`Generating ${customers.length} invoices...`);
// Prepare batch documents
const documents = customers.map(customer => ({
variables: {
invoiceNumber: `INV-${Date.now()}-${customer.id}`,
customerName: customer.name,
customerEmail: customer.email,
address: customer.address,
lineItems: customer.pendingCharges,
subtotal: customer.subtotal,
tax: customer.tax,
total: customer.total,
dueDate: getNextMonthDate()
},
filename: `invoice-${customer.id}-${getMonthYear()}`,
metadata: {
customerId: customer.id,
billingPeriod: getMonthYear()
}
}));
// Submit batch
const batch = await client.documents.generateBatch({
templateId: 'monthly-invoice',
format: 'pdf',
documents
});
console.log(`Batch submitted: ${batch.batchId}`);
// Wait for completion
const result = await waitForBatch(client, batch.batchId);
// Save download URLs to database
for (const doc of result.documents) {
if (doc.status === 'completed') {
await saveInvoiceUrl(doc.metadata.customerId, doc.downloadUrl);
}
}
console.log(`Generated ${result.completedDocuments} invoices`);
console.log(`Failed: ${result.failedDocuments}`);
}
Batch Limits
| Plan | Max Documents per Batch | Concurrent Batches |
|---|---|---|
| Free | 10 | 1 |
| Starter | 100 | 3 |
| Growth | 500 | 10 |
| Scale | 1000 | Unlimited |
Error Handling
Some documents may fail while others succeed:
const result = await waitForBatch(client, batchId);
const successful = result.documents.filter(d => d.status === 'completed');
const failed = result.documents.filter(d => d.status === 'failed');
console.log(`Successful: ${successful.length}`);
console.log(`Failed: ${failed.length}`);
// Handle failures
for (const doc of failed) {
console.error(`Document ${doc.jobId} failed: ${doc.error}`);
// Retry logic
if (doc.error.includes('temporary')) {
await retryDocument(doc);
}
}
Best Practices
1. Use Meaningful Filenames
documents: customers.map(c => ({
variables: { ... },
filename: `invoice-${c.name.toLowerCase().replace(/\s+/g, '-')}-${month}`
}))
2. Include Metadata for Tracking
documents: customers.map(c => ({
variables: { ... },
metadata: {
customerId: c.id,
orderId: c.orderId,
type: 'monthly-invoice'
}
}))
3. Process in Chunks for Large Batches
const BATCH_SIZE = 500;
async function processLargeBatch(allDocuments) {
const batches = [];
for (let i = 0; i < allDocuments.length; i += BATCH_SIZE) {
const chunk = allDocuments.slice(i, i + BATCH_SIZE);
const batch = await client.documents.generateBatch({
templateId: 'invoice-template',
format: 'pdf',
documents: chunk
});
batches.push(batch.batchId);
}
// Wait for all batches
const results = await Promise.all(
batches.map(id => waitForBatch(client, id))
);
return results;
}
Next Steps
- Set up webhooks for production batch processing
- Generate Excel reports for data exports
- API Reference for full batch API details