Enterprise document workflows share a common challenge: generating hundreds or thousands of personalized documents without creating a maintenance nightmare. Invoice batches, quarterly reports, personalized contracts, compliance certificates, each requires unique data merged into a consistent template.
The Autype API solves this with a dedicated bulk rendering endpoint that processes up to 100 documents per job in parallel, with webhook callbacks for real-time completion notifications. This guide shows you how to architect production-ready document pipelines that scale.
The Architecture: How Bulk Rendering Works
Autype separates job submission from rendering. When you submit a bulk render job, it enters a queue and processes asynchronously. Each document in the batch renders independently, and the API collects all outputs into a single ZIP file.
This architecture means your application never blocks waiting for renders. You submit the job, receive a job ID, and continue processing other tasks. When rendering completes, Autype either calls your webhook or you poll for status.
Setting Up Your Environment
Before making API calls, you need an API key from your Autype dashboard (Settings → API Keys). All requests use the X-API-Key header for authentication.
# Store your API key as an environment variable
export AUTYPE_API_KEY="your-api-key-here"
# Base URL for all API calls
BASE_URL="https://api.autype.com/api/v1/dev"
Understanding Template Variables
The bulk rendering system relies on template variables defined in your document. Variables are placeholders that get replaced with actual data at render time. Autype supports several variable types.
Simple Text Variables
The most common use case is text substitution. In your document template, use {{variableName}} syntax:
# Invoice for {{clientName}}
Date: {{invoiceDate}}
Due: {{dueDate}}
Total: {{currency}}{{totalAmount}}
When rendering, you provide the values:
{
"clientName": "Acme Corporation",
"invoiceDate": "2024-01-15",
"dueDate": "2024-02-15",
"currency": "€",
"totalAmount": "12,500.00"
}
Complex Variable Types
For more sophisticated templates, Autype supports structured variable types:
List Variables for dynamic bullet points:
{
"services": {
"type": "list",
"items": ["Consulting", "Implementation", "Training"],
"ordered": true
}
}
Table Variables for invoice line items or product catalogs:
{
"lineItems": {
"type": "table",
"columns": ["Description", "Quantity", "Unit Price", "Total"],
"data": [
["Consulting Hours", "40", "€150", "€6,000"],
["Software License", "1", "€5,000", "€5,000"],
["Support Package", "12", "€125", "€1,500"]
]
}
}
Image Variables for logos and signatures:
{
"companyLogo": {
"type": "image",
"src": "https://your-cdn.com/logo.png",
"width": 200,
"align": "center"
}
}
Creating a Bulk Render Job
The POST /bulk-render endpoint accepts a JSON payload with your template document ID, output format, and an array of variable sets.
Basic Bulk Render Request
curl -X POST "https://api.autype.com/api/v1/dev/bulk-render" \
-H "X-API-Key: $AUTYPE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"documentId": "550e8400-e29b-41d4-a716-446655440000",
"format": "PDF",
"items": [
{
"clientName": "Acme Corporation",
"invoiceDate": "2024-01-15",
"totalAmount": "€12,500.00"
},
{
"clientName": "Beta Industries",
"invoiceDate": "2024-01-15",
"totalAmount": "€8,750.00"
},
{
"clientName": "Gamma Solutions",
"invoiceDate": "2024-01-15",
"totalAmount": "€15,200.00"
}
]
}'
The response includes a bulkJobId for tracking:
{
"bulkJobId": "bulk_abc123def456",
"status": "PENDING",
"format": "PDF",
"totalItems": 3,
"completedItems": 0,
"failedItems": 0,
"createdAt": "2024-01-15T10:30:00.000Z"
}
Using Files for Bulk Data
For large batches, uploading a CSV or Excel file is more practical than embedding data in JSON. The POST /bulk-render/file endpoint accepts multipart form data:
curl -X POST "https://api.autype.com/api/v1/dev/bulk-render/file" \
-H "X-API-Key: $AUTYPE_API_KEY" \
-F "file=@invoices.csv" \
-F "documentId=550e8400-e29b-41d4-a716-446655440000" \
-F "format=PDF"
Your CSV file should have column headers matching your variable names:
clientName,invoiceDate,totalAmount
Acme Corporation,2024-01-15,€12,500.00
Beta Industries,2024-01-15,€8,750.00
Gamma Solutions,2024-01-15,€15,200.00
Implementing Webhook Callbacks
Polling works for small batches, but webhooks are essential for production systems. When you include a webhook configuration, Autype sends a POST request to your URL when the job completes or fails.
Webhook Configuration
Add the webhook object to your bulk render request:
curl -X POST "https://api.autype.com/api/v1/dev/bulk-render" \
-H "X-API-Key: $AUTYPE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"documentId": "550e8400-e29b-41d4-a716-446655440000",
"format": "PDF",
"items": [...],
"webhook": {
"url": "https://your-app.com/webhooks/autype/bulk-complete",
"headers": {
"X-Webhook-Secret": "your-webhook-secret"
}
}
}'
Handling Webhook Payloads
Your webhook endpoint receives a POST with the job status:
{
"bulkJobId": "bulk_abc123def456",
"status": "COMPLETED",
"format": "PDF",
"totalItems": 100,
"completedItems": 98,
"failedItems": 2,
"downloadUrl": "https://api.autype.com/api/v1/dev/bulk-render/bulk_abc123def456/download?token=signed-token",
"completedAt": "2024-01-15T10:35:22.000Z"
}
A typical webhook handler in Node.js:
// Express.js webhook handler
app.post('/webhooks/autype/bulk-complete', async (req, res) => {
const { bulkJobId, status, downloadUrl, completedItems, failedItems } = req.body;
// Verify webhook secret
const secret = req.headers['x-webhook-secret'];
if (secret !== process.env.WEBHOOK_SECRET) {
return res.status(401).send('Invalid secret');
}
if (status === 'COMPLETED') {
console.log(`Bulk job ${bulkJobId} completed: ${completedItems} successful, ${failedItems} failed`);
// Download the ZIP file
const response = await fetch(downloadUrl);
const buffer = await response.buffer();
// Process the ZIP (store in S3, send to users, etc.)
await storeDocuments(bulkJobId, buffer);
// Trigger next workflow step
await notifyComplete(bulkJobId, completedItems, failedItems);
} else if (status === 'FAILED') {
console.error(`Bulk job ${bulkJobId} failed`);
await alertTeam(bulkJobId);
}
res.status(200).send('OK');
});
Polling for Job Status
If webhooks are not an option, poll the status endpoint:
curl "https://api.autype.com/api/v1/dev/bulk-render/bulk_abc123def456" \
-H "X-API-Key: $AUTYPE_API_KEY"
Response when processing:
{
"bulkJobId": "bulk_abc123def456",
"status": "PROCESSING",
"format": "PDF",
"totalItems": 100,
"completedItems": 45,
"failedItems": 0,
"createdAt": "2024-01-15T10:30:00.000Z"
}
Response when complete:
{
"bulkJobId": "bulk_abc123def456",
"status": "COMPLETED",
"format": "PDF",
"totalItems": 100,
"completedItems": 100,
"failedItems": 0,
"downloadUrl": "https://api.autype.com/api/v1/dev/bulk-render/bulk_abc123def456/download?token=...",
"completedAt": "2024-01-15T10:35:22.000Z"
}
Implementing Exponential Backoff Polling
For production systems, implement exponential backoff:
import time
import requests
def wait_for_bulk_job(bulk_job_id, api_key, max_wait_seconds=600):
base_url = "https://api.autype.com/api/v1/dev"
headers = {"X-API-Key": api_key}
wait_time = 2 # Start with 2 seconds
elapsed = 0
while elapsed < max_wait_seconds:
response = requests.get(
f"{base_url}/bulk-render/{bulk_job_id}",
headers=headers
)
data = response.json()
if data["status"] == "COMPLETED":
return data["downloadUrl"]
elif data["status"] == "FAILED":
raise Exception(f"Bulk job failed: {data.get('error')}")
# Still processing, wait and retry
time.sleep(wait_time)
elapsed += wait_time
wait_time = min(wait_time * 1.5, 30) # Cap at 30 seconds
raise TimeoutError(f"Bulk job did not complete within {max_wait_seconds} seconds")
Downloading the Results
When a bulk job completes, download the ZIP file containing all rendered documents:
# Using the signed download URL (no API key needed)
curl -o batch.zip "https://api.autype.com/api/v1/dev/bulk-render/bulk_abc123def456/download?token=..."
# Or with API key authentication
curl -o batch.zip \
-H "X-API-Key: $AUTYPE_API_KEY" \
"https://api.autype.com/api/v1/dev/bulk-render/bulk_abc123def456/download"
The ZIP file structure preserves order, with documents named by their index:
batch.zip
├── document_001.pdf
├── document_002.pdf
├── document_003.pdf
└── ...
Error Handling and Retry Logic
Production pipelines need robust error handling. Bulk jobs can have partial failures where some documents succeed and others fail.
Handling Partial Failures
Check the failedItems count in the webhook payload or status response:
if (data.failedItems > 0) {
// Log which items failed
console.warn(`${data.failedItems} documents failed to render`);
// Your retry logic here
const failedIndices = await identifyFailedItems(data.bulkJobId);
await retryFailedItems(failedIndices);
}
Validation Before Submission
Use the validation endpoint to catch errors before submitting a bulk job:
curl -X POST "https://api.autype.com/api/v1/dev/render/validate" \
-H "X-API-Key: $AUTYPE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"config": {
"document": { "type": "pdf" },
"variables": { "testVar": "value" },
"sections": [...]
}
}'
Response for invalid documents:
{
"valid": false,
"errors": [
{
"path": "sections.0.content.2.text",
"message": "Variable {{undefinedVar}} not found in variables object",
"code": "UNDEFINED_VARIABLE"
}
]
}
Performance Considerations
Bulk rendering performance depends on document complexity. For typical business documents:
| Document Size | Batch of 10 | Batch of 50 | Batch of 100 |
|---|---|---|---|
| 5 pages | 4 seconds | 15 seconds | 28 seconds |
| 20 pages | 12 seconds | 45 seconds | 85 seconds |
| 50 pages | 25 seconds | 90 seconds | 180 seconds |
Each bulk render item costs 4 credits (vs. 5 for single renders), making bulk rendering both faster and more cost-effective for batches.
Real-World Example: Monthly Invoice Generation
Here is a complete example generating monthly invoices from a CRM export:
import fs from 'fs';
import fetch from 'node-fetch';
const API_KEY = process.env.AUTYPE_API_KEY;
const BASE_URL = 'https://api.autype.com/api/v1/dev';
async function generateMonthlyInvoices(invoiceData, templateId) {
// Submit bulk job
const response = await fetch(`${BASE_URL}/bulk-render`, {
method: 'POST',
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
documentId: templateId,
format: 'PDF',
items: invoiceData,
webhook: {
url: 'https://api.mycompany.com/webhooks/invoices/complete',
headers: { 'X-Internal-Secret': process.env.INTERNAL_SECRET }
}
})
});
const job = await response.json();
console.log(`Started bulk job ${job.bulkJobId} with ${job.totalItems} invoices`);
return job.bulkJobId;
}
// Load invoice data from CSV export
const csvData = fs.readFileSync('./monthly_invoices.csv', 'utf8');
const invoices = parseCsv(csvData);
// Generate all invoices
generateMonthlyInvoices(invoices, '550e8400-e29b-41d4-a716-446655440000');
Summary
Autype's bulk rendering API provides the foundation for scalable document automation. Key takeaways:
- Submit once, render in parallel - Jobs process asynchronously with up to 100 documents per batch
- Use webhooks - Eliminate polling in production; let Autype notify you when work completes
- Validate early - Catch template errors before submitting large batches
-
Handle partial failures - Monitor
failedItemsand implement retry logic for failed documents - Leverage file uploads - CSV and Excel support simplifies large batch submission
The combination of parallel processing, webhook notifications, and flexible variable types makes this API suitable for everything from monthly invoice runs to real-time contract generation in your sales pipeline.
For more details on the document JSON schema and variable types, see the Autype API documentation.
Top comments (0)