API Reference & Developer Guide
Generate PDFs programmatically using our REST API. This guide covers API key management and how to integrate PDF generation into your applications.
Overview
The API allows you to:
- Generate PDFs programmatically
- Integrate with your custom applications
- Automate document workflows
- Build custom integrations
API Key Management
What is an API Key?
An API key is a unique identifier that authenticates your API requests. It:
- Identifies your team
- Authorizes access to your templates
- Tracks API usage
- Must be kept secure
Generating an API Key
Step 1: Navigate to API Settings
- Go to Settings in the main navigation
- Click on API or API Keys
- You'll see the API key management section
Step 2: Generate a Key
- Click Generate API Key
- A new key is created with
pk_prefix - The key is displayed (copy it immediately)
Important: The full API key is only shown once. Store it securely!
Step 3: Copy and Store
- Click the Copy button
- Store the key in a secure location:
- Environment variables
- Secret management system (AWS Secrets Manager, HashiCorp Vault, etc.)
- Secure configuration file (not in source code!)
Viewing Your API Key
- Only the creation date is shown after initial generation
- You can reveal the key by clicking "Show"
- For security, consider regenerating if you forgot the key
Regenerating an API Key
If your key is compromised or you need a new one:
- Go to Settings → API
- Click Regenerate Key
- Confirm the regeneration
- A new key is created; the old key is invalidated
Warning: Regenerating invalidates the previous key. Update all applications using the old key.
Deleting an API Key
To remove API access:
- Go to Settings → API
- Click Delete Key
- Confirm deletion
- All API requests will fail until a new key is generated
Authentication
API Key Header
Include your API key in the request header:
Authorization: Bearer pk_your_api_key_here
Example Request
curl -X POST https://api.yourapp.com/api/pdf/generate \
-H "Authorization: Bearer pk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"templateId": "template-uuid",
"recordId": "001XX000003XXXXX"
}'
Endpoints
Generate PDF
Generate a PDF from a template with merged data.
Endpoint:
POST /api/pdf/generate
Headers:
Authorization: Bearer pk_your_api_key
Content-Type: application/json
Request Body:
{
"templateId": "string (required)",
"recordId": "string (optional)"
}
| Parameter | Type | Required | Description |
|---|---|---|---|
templateId | string | Yes | UUID of the template to use |
recordId | string | No | ID of the CRM record to merge data from |
Response:
{
"success": true,
"pdf": "base64_encoded_pdf_data",
"filename": "document.pdf"
}
Example - Bash/cURL:
curl -X POST https://api.yourapp.com/api/pdf/generate \
-H "Authorization: Bearer pk_abc123def456..." \
-H "Content-Type: application/json" \
-d '{
"templateId": "550e8400-e29b-41d4-a716-446655440000",
"recordId": "0015f00000XXXXXXABC"
}'
Example - JavaScript/Node.js:
const response = await fetch('https://api.yourapp.com/api/pdf/generate', {
method: 'POST',
headers: {
'Authorization': 'Bearer pk_abc123def456...',
'Content-Type': 'application/json'
},
body: JSON.stringify({
templateId: '550e8400-e29b-41d4-a716-446655440000',
recordId: '0015f00000XXXXXXABC'
})
});
const { pdf, filename } = await response.json();
// Convert base64 to buffer for file operations
const pdfBuffer = Buffer.from(pdf, 'base64');
Example - Python:
import requests
import base64
response = requests.post(
'https://api.yourapp.com/api/pdf/generate',
headers={
'Authorization': 'Bearer pk_abc123def456...',
'Content-Type': 'application/json'
},
json={
'templateId': '550e8400-e29b-41d4-a716-446655440000',
'recordId': '0015f00000XXXXXXABC'
}
)
data = response.json()
pdf_bytes = base64.b64decode(data['pdf'])
# Save to file
with open('output.pdf', 'wb') as f:
f.write(pdf_bytes)
Error Handling
HTTP Status Codes
| Status | Description |
|---|---|
200 | Success |
400 | Bad Request - Invalid parameters |
401 | Unauthorized - Invalid or missing API key |
403 | Forbidden - No permission for resource |
404 | Not Found - Template or record not found |
429 | Too Many Requests - Rate limit exceeded |
500 | Server Error - Contact support |
Error Response Format
{
"success": false,
"error": {
"code": "TEMPLATE_NOT_FOUND",
"message": "The specified template does not exist"
}
}
Common Error Codes
| Code | Description | Solution |
|---|---|---|
INVALID_API_KEY | API key is invalid | Check key is correct |
TEMPLATE_NOT_FOUND | Template doesn't exist | Verify template ID |
RECORD_NOT_FOUND | CRM record not found | Verify record ID |
INTEGRATION_ERROR | CRM connection failed | Reconnect integration |
SUBSCRIPTION_REQUIRED | No active subscription | Upgrade plan |
RATE_LIMIT_EXCEEDED | Too many requests | Slow down requests |
Error Handling Example
try {
const response = await fetch('/api/pdf/generate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ templateId, recordId })
});
if (!response.ok) {
const error = await response.json();
switch (error.error.code) {
case 'TEMPLATE_NOT_FOUND':
console.error('Template not found. Please check the template ID.');
break;
case 'INVALID_API_KEY':
console.error('Invalid API key. Please regenerate your key.');
break;
default:
console.error('Error:', error.error.message);
}
return;
}
const { pdf } = await response.json();
// Process the PDF...
} catch (err) {
console.error('Network error:', err);
}
Rate Limits
Default Limits
| Plan | Requests per Minute | Requests per Day |
|---|---|---|
| Free | 10 | 100 |
| Solo | 30 | 500 |
| Starter | 60 | 2,000 |
| Enterprise | Custom | Custom |
Handling Rate Limits
When rate limited, the API returns:
- HTTP Status:
429 Too Many Requests - Header:
Retry-After: <seconds>
Example handling:
const response = await fetch('/api/pdf/generate', { ... });
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
console.log(`Rate limited. Retry after ${retryAfter} seconds.`);
// Wait and retry
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
// Retry the request...
}
Integration Examples
Salesforce Apex Integration
public class PDFGenerator {
private static final String API_KEY = 'pk_your_api_key';
private static final String API_ENDPOINT = 'https://api.yourapp.com/api/pdf/generate';
@future(callout=true)
public static void generatePDF(String templateId, String recordId) {
HttpRequest req = new HttpRequest();
req.setEndpoint(API_ENDPOINT);
req.setMethod('POST');
req.setHeader('Authorization', 'Bearer ' + API_KEY);
req.setHeader('Content-Type', 'application/json');
Map<String, String> body = new Map<String, String>{
'templateId' => templateId,
'recordId' => recordId
};
req.setBody(JSON.serialize(body));
Http http = new Http();
HttpResponse res = http.send(req);
if (res.getStatusCode() == 200) {
Map<String, Object> result = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
String pdfBase64 = (String) result.get('pdf');
// Process the PDF...
}
}
}
Node.js/Express Webhook
const express = require('express');
const fetch = require('node-fetch');
const app = express();
app.use(express.json());
const PDF_API_KEY = process.env.PDF_API_KEY;
app.post('/webhook/generate-pdf', async (req, res) => {
const { templateId, recordId, email } = req.body;
try {
// Generate PDF
const pdfResponse = await fetch('https://api.yourapp.com/api/pdf/generate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${PDF_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ templateId, recordId })
});
const { pdf, filename } = await pdfResponse.json();
// Send via email, save to storage, etc.
// ...
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000);
Python/Django Integration
import requests
import base64
from django.conf import settings
from django.http import HttpResponse
def generate_pdf(request, template_id, record_id):
response = requests.post(
'https://api.yourapp.com/api/pdf/generate',
headers={
'Authorization': f'Bearer {settings.PDF_API_KEY}',
'Content-Type': 'application/json'
},
json={
'templateId': template_id,
'recordId': record_id
}
)
if response.status_code == 200:
data = response.json()
pdf_bytes = base64.b64decode(data['pdf'])
return HttpResponse(
pdf_bytes,
content_type='application/pdf',
headers={
'Content-Disposition': f'attachment; filename="{data["filename"]}"'
}
)
return HttpResponse(status=response.status_code)
Security Best Practices
1. Keep Keys Secret
- Never commit API keys to source control
- Use environment variables
- Use secret management services
Bad:
const API_KEY = 'pk_abc123...'; // Never do this!
Good:
const API_KEY = process.env.PDF_API_KEY;
2. Rotate Keys Regularly
- Regenerate keys periodically
- Immediately rotate if key may be compromised
- Update all applications using the key
3. Use Server-Side Only
- Never expose API keys in client-side code
- Make API calls from your server
- Use a backend proxy for frontend applications
4. Monitor Usage
- Review API logs for unusual activity
- Set up alerts for excessive usage
- Track which applications use your API
5. Restrict Access
- Only generate keys for necessary services
- Delete unused keys
- Use separate keys for different environments
Troubleshooting
API Key Not Working
Check:
- Key is correctly copied (no extra spaces)
- Key hasn't been regenerated/deleted
- Authorization header format is correct
- Subscription is active
PDF Not Generating
Check:
- Template ID exists and is accessible
- Record ID exists in connected CRM
- CRM integration is connected
- Template has valid merge fields
Slow Response Times
Check:
- CRM data source responsiveness
- Template complexity
- Table size and related records count
- Network connectivity
Next Steps
- Templates - Create templates to use with API
- Integrations - Connect data sources
- Subscription Plans - API limits by plan
