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

  1. Go to Settings in the main navigation
  2. Click on API or API Keys
  3. You'll see the API key management section

Step 2: Generate a Key

  1. Click Generate API Key
  2. A new key is created with pk_ prefix
  3. The key is displayed (copy it immediately)

Important: The full API key is only shown once. Store it securely!

Step 3: Copy and Store

  1. Click the Copy button
  2. 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:

  1. Go to Settings → API
  2. Click Regenerate Key
  3. Confirm the regeneration
  4. 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:

  1. Go to Settings → API
  2. Click Delete Key
  3. Confirm deletion
  4. 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)"
}
ParameterTypeRequiredDescription
templateIdstringYesUUID of the template to use
recordIdstringNoID 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

StatusDescription
200Success
400Bad Request - Invalid parameters
401Unauthorized - Invalid or missing API key
403Forbidden - No permission for resource
404Not Found - Template or record not found
429Too Many Requests - Rate limit exceeded
500Server Error - Contact support

Error Response Format

{
  "success": false,
  "error": {
    "code": "TEMPLATE_NOT_FOUND",
    "message": "The specified template does not exist"
  }
}

Common Error Codes

CodeDescriptionSolution
INVALID_API_KEYAPI key is invalidCheck key is correct
TEMPLATE_NOT_FOUNDTemplate doesn't existVerify template ID
RECORD_NOT_FOUNDCRM record not foundVerify record ID
INTEGRATION_ERRORCRM connection failedReconnect integration
SUBSCRIPTION_REQUIREDNo active subscriptionUpgrade plan
RATE_LIMIT_EXCEEDEDToo many requestsSlow 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

PlanRequests per MinuteRequests per Day
Free10100
Solo30500
Starter602,000
EnterpriseCustomCustom

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:

  1. Key is correctly copied (no extra spaces)
  2. Key hasn't been regenerated/deleted
  3. Authorization header format is correct
  4. Subscription is active

PDF Not Generating

Check:

  1. Template ID exists and is accessible
  2. Record ID exists in connected CRM
  3. CRM integration is connected
  4. Template has valid merge fields

Slow Response Times

Check:

  1. CRM data source responsiveness
  2. Template complexity
  3. Table size and related records count
  4. Network connectivity

Next Steps