HubSpot Integration

Generate PDFs from HubSpot records using the DataToPDF API.

Prerequisites

  1. A DataToPDF account with a Starter plan or higher (API access required)
  2. An API key generated from Settings > API Key
  3. A PDF template configured with HubSpot as the data source
  4. HubSpot connected to your DataToPDF account

Setup

Connect your HubSpot account in DataToPDF:

  1. Go to Settings > Integrations > HubSpot
  2. Click Connect HubSpot
  3. Authorize DataToPDF to access your HubSpot data

API Usage

Endpoint

POST https://datatopdf.app/api/v1/pdf/generate

Request

{
  "templateId": "your_template_id",
  "recordId": "hubspot_record_id"
}

The recordId should be the HubSpot record ID (numeric ID) for the object type configured in your template (contacts, companies, deals, etc.).

HubSpot Workflows

You can automate PDF generation using HubSpot workflows with either Webhooks or Custom Code Actions.

Option 1: Webhook Action

Use a webhook to call the DataToPDF API directly from a workflow.

Setup

  1. Go to Automation > Workflows in HubSpot
  2. Create or edit a workflow (e.g., deal-based workflow)
  3. Add a Webhook action
  4. Configure the webhook:
SettingValue
MethodPOST
URLhttps://datatopdf.app/api/v1/pdf/generate
Request headersX-API-Key: pk_your_api_key_here<br>Content-Type: application/json
Request bodySee below

Request Body:

{
  "templateId": "clx1234567890",
  "recordId": "{{deal.hs_object_id}}"
}

Replace {{deal.hs_object_id}} with the appropriate token for your object type:

  • Contacts: {{contact.hs_object_id}}
  • Companies: {{company.hs_object_id}}
  • Deals: {{deal.hs_object_id}}
  • Tickets: {{ticket.hs_object_id}}

Example: Generate PDF when deal is won

  1. Create a deal-based workflow
  2. Set enrollment trigger: Deal stage is Closed Won
  3. Add Webhook action with the configuration above
  4. Activate the workflow

Option 2: Custom Code Action

For more control (e.g., saving the PDF to HubSpot Files), use a Custom Code action.

Setup

  1. Go to Automation > Workflows in HubSpot
  2. Create or edit a workflow
  3. Add a Custom code action
  4. Select Python or Node.js
  5. Add your API key as a secret: DATATOPDF_API_KEY

Python Custom Code

import requests
import os

def main(event):
    # Get the record ID from the enrolled object
    record_id = event.get('object', {}).get('objectId')
    template_id = 'clx1234567890'  # Your template ID
    api_key = os.environ.get('DATATOPDF_API_KEY')

    # Generate PDF
    response = requests.post(
        'https://datatopdf.app/api/v1/pdf/generate',
        headers={
            'Content-Type': 'application/json',
            'X-API-Key': api_key
        },
        json={
            'templateId': template_id,
            'recordId': str(record_id)
        },
        timeout=30
    )

    if response.ok:
        data = response.json()
        return {
            'outputFields': {
                'pdf_generated': True,
                'pdf_base64': data['pdf']
            }
        }
    else:
        error_data = response.json()
        return {
            'outputFields': {
                'pdf_generated': False,
                'error_message': error_data.get('error', 'Unknown error')
            }
        }

Node.js Custom Code

const axios = require('axios');

exports.main = async (event, callback) => {
  const recordId = event.object.objectId;
  const templateId = 'clx1234567890';  // Your template ID
  const apiKey = process.env.DATATOPDF_API_KEY;

  try {
    const response = await axios.post(
      'https://datatopdf.app/api/v1/pdf/generate',
      {
        templateId: templateId,
        recordId: String(recordId)
      },
      {
        headers: {
          'Content-Type': 'application/json',
          'X-API-Key': apiKey
        },
        timeout: 30000
      }
    );

    callback({
      outputFields: {
        pdf_generated: true,
        pdf_base64: response.data.pdf
      }
    });
  } catch (error) {
    callback({
      outputFields: {
        pdf_generated: false,
        error_message: error.response?.data?.error || error.message
      }
    });
  }
};

Workflow Use Cases

TriggerActionDescription
Deal stage = Closed WonGenerate invoice PDFAuto-generate invoice when deal closes
Deal stage = Contract SentGenerate contract PDFCreate contract document for signature
Contact createdGenerate welcome PDFSend welcome packet to new contacts
Ticket status = ResolvedGenerate summary PDFCreate support case summary
Form submissionGenerate quote PDFCreate quote based on form data

Combining with Email

After generating a PDF in a workflow, you can:

  1. Store the base64 output in a custom property
  2. Use a subsequent webhook to send the PDF via email (using your email service API)
  3. Use Operations Hub to upload the PDF to HubSpot Files and attach to a note

Examples

cURL

curl -X POST https://datatopdf.app/api/v1/pdf/generate \
  -H "Content-Type: application/json" \
  -H "X-API-Key: pk_your_api_key_here" \
  -d '{
    "templateId": "clx1234567890",
    "recordId": "12345"
  }'

JavaScript (HubSpot Custom Card / Serverless Function)

const axios = require('axios');

exports.main = async (context = {}) => {
  const { recordId } = context.parameters;
  const templateId = 'clx1234567890';
  const apiKey = process.env.DATATOPDF_API_KEY;

  try {
    const response = await axios.post(
      'https://datatopdf.app/api/v1/pdf/generate',
      {
        templateId,
        recordId: recordId.toString()
      },
      {
        headers: {
          'Content-Type': 'application/json',
          'X-API-Key': apiKey
        }
      }
    );

    // Return base64 PDF data
    return {
      statusCode: 200,
      body: {
        pdf: response.data.pdf,
        contentType: response.data.contentType
      }
    };
  } catch (error) {
    return {
      statusCode: error.response?.status || 500,
      body: {
        error: error.response?.data?.error || error.message
      }
    };
  }
};

Python (HubSpot Workflow Custom Code Action)

import requests
import base64
import os

def main(event):
    record_id = event.get('inputFields', {}).get('record_id')
    template_id = 'clx1234567890'
    api_key = os.environ.get('DATATOPDF_API_KEY')

    response = requests.post(
        'https://datatopdf.app/api/v1/pdf/generate',
        headers={
            'Content-Type': 'application/json',
            'X-API-Key': api_key
        },
        json={
            'templateId': template_id,
            'recordId': str(record_id)
        }
    )

    if response.ok:
        data = response.json()
        return {
            'outputFields': {
                'pdf_base64': data['pdf'],
                'success': True
            }
        }
    else:
        error_data = response.json()
        return {
            'outputFields': {
                'error': error_data.get('error', 'Unknown error'),
                'success': False
            }
        }

Node.js (HubSpot Private App)

const hubspot = require('@hubspot/api-client');
const fetch = require('node-fetch');

async function generatePdfForDeal(dealId, templateId) {
  const apiKey = process.env.DATATOPDF_API_KEY;

  const response = await fetch('https://datatopdf.app/api/v1/pdf/generate', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': apiKey
    },
    body: JSON.stringify({
      templateId: templateId,
      recordId: dealId.toString()
    })
  });

  const data = await response.json();

  if (!response.ok) {
    throw new Error(`${data.code}: ${data.error}`);
  }

  return Buffer.from(data.pdf, 'base64');
}

// Example: Generate PDF and upload to HubSpot as engagement
async function generateAndAttachPdf(dealId, templateId) {
  const hubspotClient = new hubspot.Client({ accessToken: process.env.HUBSPOT_ACCESS_TOKEN });

  // Generate PDF
  const pdfBuffer = await generatePdfForDeal(dealId, templateId);

  // Upload to HubSpot Files API
  const uploadResponse = await hubspotClient.files.filesApi.upload({
    file: pdfBuffer,
    fileName: `Deal_${dealId}_Document.pdf`,
    folderId: 'your-folder-id'
  });

  // Attach to deal as note
  await hubspotClient.crm.objects.notes.basicApi.create({
    properties: {
      hs_note_body: 'PDF document generated',
      hs_attachment_ids: uploadResponse.id
    },
    associations: [{
      to: { id: dealId },
      types: [{ associationCategory: 'HUBSPOT_DEFINED', associationTypeId: 214 }]
    }]
  });

  return uploadResponse.id;
}

Supported HubSpot Objects

The API supports all standard HubSpot objects that you've configured in your template:

  • Contacts
  • Companies
  • Deals
  • Tickets
  • Custom Objects

The object type is determined by your template configuration. Make sure the recordId matches the object type configured in your template.

Response Format

Success (200)

{
  "pdf": "base64-encoded-pdf-data",
  "contentType": "application/pdf"
}

Error Response

{
  "error": "Human-readable error message",
  "code": "ERROR_CODE"
}

Error Codes

CodeDescription
UNAUTHORIZEDMissing API key
INVALID_API_KEYInvalid or revoked API key
RATE_LIMIT_EXCEEDEDToo many requests (30/minute limit)
SUBSCRIPTION_REQUIREDActive subscription required
GENERATION_LIMIT_EXCEEDEDMonthly PDF limit reached
TEMPLATE_NOT_FOUNDTemplate not found
CONNECTION_NOT_FOUNDHubSpot connection not configured
DATA_FETCH_ERRORFailed to fetch HubSpot data
GENERATION_FAILEDPDF generation failed

Rate Limits

  • 30 requests per minute per API key
  • Monthly generation limits based on your subscription plan

Best Practices

  1. Store API keys securely - Use environment variables or secrets management
  2. Handle rate limits - Implement retry logic with exponential backoff
  3. Validate record IDs - Ensure the record ID exists before making API calls
  4. Use webhooks for automation - Trigger PDF generation from HubSpot workflow webhooks
  5. Cache templates - If generating multiple PDFs, minimize template lookups