Template2PDF API Documentation

This service enables you to generate custom PDFs from Google Docs templates by replacing placeholders, appending table rows, adding images, and sending webhooks upon completion.


TL;DR:

Create a Google Document with {{placeholders}}.
POST a JSON payload containing the Google document_id and your key value pairs to /convert_jobs
To fetch the generated PDF, either poll the returned convert_job url until document_url is available or use a webhook to receive a notification where to fetch the PDF.

Authentication either via API key in the URL or via oauth2 Bearer token.

Supported: Normal substitutions, tables, images and even images in tables! Let me know if you need more. sven+feature@template2pdf.com


1 - Create a Google Doc as template.

Write some text, create a table and look where you would like to have an image.

Use placeholders like {{quote_id}} or {{customer_name}} in your document.

For tables, use placeholders like {{row_item_title}} or {{row_item_quantity}} inside the table row you want to replicate.

For images, use placeholders like {{image_sales}} or {{image_logo}} where you want to insert the image.

Once done, you can auto extract a basic json structure to use in your calls.


2 - Generate a PDF


Authentication

Authentication is required to access the Template2PDF API. You can authenticate using either an API key or an OAuth2 Bearer token.

API Key

https://<your-api-key>@template2pdf.com
You can find your API key in your account settings.

OAuth2

Header: Authorization Bearer YOUR_OAUTH2_ACCESS_TOKEN
Authorization Endpoint: https://template2pdf.com/oauth/authorize
Token Endpoint: https://template2pdf.com/oauth/token
You can register a new OAuth2 client in your account settings.

Example - creating a convert job

curl -X POST https://<your-api-key>@template2pdf.com/convert_jobs.json \
-H "Content-Type: application/json" \
-d '{
  "document_id": "1bhgye-ZITHzIL0RzyFiUITNR20R1z_1q6FR91rdy_ek",
  "substitutions": {
    "{{quote_id}}": "2342",
    "{{prepared_date}}": "2025-10-01",
    "{{exp_date}}": "2025-12-01",
    "{{customer_name}}": "Sven Tantau",
    "{{address_line_0}}": "123 Main St",
    "{{address_line_1}}": "Anytown, USA",
    "{{total}}": "$640,-",
    "{{sales_name}}": "John Doe",
    "{{sales_email}}": "test@test.com"
  },
  "table_data": [
    [
      {
        "{{row_item_title}}": "Software",
        "{{row_item_quantity}}": "1",
        "{{row_item_price}}": "$200,-",
        "{{row_item_total}}": "$200,-"
      },
      {
        "{{row_item_title}}": "Support Hours",
        "{{row_item_quantity}}": "4",
        "{{row_item_price}}": "$80,-",
        "{{row_item_total}}": "$320,-"
      },
      {
        "{{row_item_title}}": "Nice Box",
        "{{row_item_quantity}}": "1",
        "{{row_item_price}}": "$120,-",
        "{{row_item_total}}": "$120,-"
      }
    ]
  ],
  "images": {
    "{{image_sales}}": {
      "url": "https://beastiebytes.com/images/me.png",
      "width": 50,
      "height": 50
    }
  }
}'
import requests
import requests
from requests.auth import HTTPBasicAuth

url = "https://template2pdf.com/convert_jobs.json"

headers = {
    "Content-Type": "application/json"
}
data = {
  "document_id": "1bhgye-ZITHzIL0RzyFiUITNR20R1z_1q6FR91rdy_ek",
  "substitutions": {
    "{{quote_id}}": "2342",
    "{{prepared_date}}": "2025-10-01",
    "{{exp_date}}": "2025-12-01",
    "{{customer_name}}": "Sven Tantau",
    "{{address_line_0}}": "123 Main St",
    "{{address_line_1}}": "Anytown, USA",
    "{{total}}": "$640,-",
    "{{sales_name}}": "John Doe",
    "{{sales_email}}": "test@test.com"
  },
  "table_data": [
    [
      {
        "{{row_item_title}}": "Software",
        "{{row_item_quantity}}": "1",
        "{{row_item_price}}": "$200,-",
        "{{row_item_total}}": "$200,-"
      },
      {
        "{{row_item_title}}": "Support Hours",
        "{{row_item_quantity}}": "4",
        "{{row_item_price}}": "$80,-",
        "{{row_item_total}}": "$320,-"
      },
      {
        "{{row_item_title}}": "Nice Box",
        "{{row_item_quantity}}": "1",
        "{{row_item_price}}": "$120,-",
        "{{row_item_total}}": "$120,-"
      }
    ]
  ],
  "images": {
    "{{image_sales}}": {
      "url": "https://beastiebytes.com/images/me.png",
      "width": 50,
      "height": 50
    }
  }
}

response = requests.post(url, headers=headers, json=data, auth=HTTPBasicAuth('<your-api-key>', ''))
print(response.json())


<?php

$url = "https://<your-api-key>@template2pdf.com/convert_jobs.json";
$data = [
  "document_id" => "1bhgye-ZITHzIL0RzyFiUITNR20R1z_1q6FR91rdy_ek",
  "substitutions" => [
    "{{quote_id}}" => "2342",
    "{{prepared_date}}" => "2025-10-01",
    "{{exp_date}}" => "2025-12-01",
    "{{customer_name}}" => "Sven Tantau",
    "{{address_line_0}}" => "123 Main St",
    "{{address_line_1}}" => "Anytown, USA",
    "{{total}}" => "$640,-",
    "{{sales_name}}" => "John Doe",
    "{{sales_email}}" => "test@test.com"
  ],
  "table_data" => [
    [
      [
        "{{row_item_title}}" => "Software",
        "{{row_item_quantity}}" => "1",
        "{{row_item_price}}" => "$200,-",
        "{{row_item_total}}" => "$200,-"
      ],
      [
        "{{row_item_title}}" => "Support Hours",
        "{{row_item_quantity}}" => "4",
        "{{row_item_price}}" => "$80,-",
        "{{row_item_total}}" => "$320,-"
      ],
      [
        "{{row_item_title}}" => "Nice Box",
        "{{row_item_quantity}}" => "1",
        "{{row_item_price}}" => "$120,-",
        "{{row_item_total}}" => "$120,-"
      ]
    ]
  ],
  "images" => [
    "{{image_sales}}" => [
      "url" => "https://beastiebytes.com/images/me.png",
      "width" => 50,
      "height" => 50
    ]
  ]
];


$options = [
    'http' => [
        'header' => "Content-Type: application/json\r\n",
        'method' => 'POST',
        'content' => json_encode($data)
    ]
];

$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);

if ($result === FALSE) {
    die('Error');
}

var_dump($result);
?>
require 'net/http'
require 'uri'
require 'json'

uri = URI.parse("https://template2pdf.com/convert_jobs.json")
headers = {
  "Accept" => "application/json",
  "Content-Type" => "application/json"
}

data = {
  "document_id" => "1bhgye-ZITHzIL0RzyFiUITNR20R1z_1q6FR91rdy_ek",
  "substitutions" => {
    "{{quote_id}}" => "2342",
    "{{prepared_date}}" => "2025-10-01",
    "{{exp_date}}" => "2025-12-01",
    "{{customer_name}}" => "Sven Tantau",
    "{{address_line_0}}" => "123 Main St",
    "{{address_line_1}}" => "Anytown, USA",
    "{{total}}" => "$640,-",
    "{{sales_name}}" => "John Doe",
    "{{sales_email}}" => "test@test.com"
  },
  "table_data" => [
    [
      {
        "{{row_item_title}}" => "Software",
        "{{row_item_quantity}}" => "1",
        "{{row_item_price}}" => "$200,-",
        "{{row_item_total}}" => "$200,-"
      },
      {
        "{{row_item_title}}" => "Support Hours",
        "{{row_item_quantity}}" => "4",
        "{{row_item_price}}" => "$80,-",
        "{{row_item_total}}" => "$320,-"
      },
      {
        "{{row_item_title}}" => "Nice Box",
        "{{row_item_quantity}}" => "1",
        "{{row_item_price}}" => "$120,-",
        "{{row_item_total}}" => "$120,-"
      }
    ]
  ],
  "images" => {
    "{{image_sales}}" => {
      "url" => "https://beastiebytes.com/images/me.png",
      "width" => 50,
      "height" => 50
    }
  }
}

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == "https") # Enable HTTPS

request = Net::HTTP::Post.new(uri.request_uri, headers)
request.basic_auth('<your-api-key>', '') # Pass the key as the username, and leave the password empty
request.body = data.to_json

response = http.request(request)
puts response.body
This will return the json representation of the created convert job.
{
  "id": 10438,
  "payload": "<original payload>",
  "status": "created",
  "document_url": null,
  "page_count": null,
  "error_message": null,
  "created_at": "2024-12-19T04:08:02.362Z",
  "updated_at": "2024-12-19T04:08:13.684Z",
  "url": "https://template2pdf.com/convert_jobs/10438.json"
}

Step 3 - Pick up the PDF

After creating a convert job, you can retrieve the generated PDF using one of the following methods:


A) Polling

  1. Fetch the Convert Job Data
    • Perform a GET request to the url provided in the initial response.
    • The API will return a JSON object representing the current state of the convert job.
  2. Check the Status
    • Inspect the status key in the JSON response.
    • If status is created or processing, the job is still in progress.
    • If status is completed, the job is done, and the PDF is ready.
    • If status is failed, an error occurred during the conversion process.
  3. Retrieve the PDF URL
    • Once the status is completed, look for the document_url key in the JSON.
    • The document_url contains the direct link to download the generated PDF.

B) Webhook

  1. Include a Webhook in the Initial Payload
    • Add a webhook object in the request payload with the following fields:
      • url: The endpoint to notify.
      • method: The HTTP method for the webhook (e.g., POST).
      • headers: Optional headers for the request.
      {
        "document_id": "1bhgye-ZITHzIL0RzyFiUITNR20R1z_1q6FR91rdy_ek",
        ....
        "webhook": {
          "url": "https://example.com/webhook",
          "method": "POST",
          "headers": {
            "Content-Type": "application/json"
          }
        }
      }
  2. Wait for the Webhook Notification
    • Once the PDF is ready, the API will send a request to the provided webhook URL.
    • The payload (convert_job json) will contain the document_url to the generated PDF.

Summary

Method Description Pros Cons
Polling Fetch the job data periodically using the job URL. Simple to implement. Requires periodic requests.
Webhook Receive automatic notifications when the job is done. Real-time updates, no polling needed. Requires webhook setup.

Choose the method that best suits your integration needs. For real-time updates, use the webhook approach. For simpler setups, polling works effectively.


Details

Here are the details for creating a convert job to generate a PDF from a Google Docs template.

POST /convert_jobs.json

Creates a new convert job to process a Google Docs template.

Request Headers

Request Body Parameters

Field Type Description
document_id string Google document ID of the template. Required.
substitutions object Key-value pairs for placeholder substitutions.
table_data array Table row data (optional). Each row is an array of objects.
images object Images to add with defined placeholders, URLs, and sizes (optional).
webhook object Webhook details for completion notification.

Document ID

The document_id is a unique identifier for your Google Docs template. It can be found in the URL of the document. For example:

https://docs.google.com/document/d/1bhgye-ZITHzIL0RzyFiUITNR20R1z_1q6FR91rdy_ek/edit

In this case, the document_id is:

{
"document_id": "1bhgye-ZITHzIL0RzyFiUITNR20R1z_1q6FR91rdy_ek",
...

To ensure your document is accessible by the Template2PDF service:

  1. Open your Google Document.
  2. Click Share in the top-right corner.
  3. In the sharing settings, select Anyone with the link and set the permission to Viewer.
  4. Copy the link and extract the document_id from the URL.

Substitutions

The substitutions parameter allows you to replace placeholders in the Google Docs template with dynamic values. Each placeholder in the document must be unique. To make that easier you should consider wrapping all keys with something like double curly braces (e.g., {{placeholder}}).


It is not required to specifically use double curly braces, but if you follow that convention, template2pdf is able to parse the document and extract a structure for you.
{
  ...
  "substitutions": {
    "{{quote_id}}": "2342",
    "{{prepared_date}}": "2025-10-01",
    "{{exp_date}}": "2025-12-01",
    "{{customer_name}}": "Sven Tantau",
    "{{address_line_0}}": "123 Main St",
    "{{address_line_1}}": "Anytown, USA",
    "{{total}}": "$640,-",
    "{{sales_name}}": "John Doe",
    "{{sales_email}}": "test@test.com"},
  ...
}

Table Data Array

The table_data parameter is an array of arrays, where each inner array defines rows in the table. Each object corresponds to a column in a row, mapping placeholder keys to their respective values.

You do not have to, but if you prefix your table-row keys with row_, template2pdf will be able to parse the document and extract a structure for you.
{
  ...
  "table_data": [
    [
      {
        "{{row_item_title}}": "Software",
        "{{row_item_quantity}}": "1",
        "{{row_item_price}}": "$200"
      }
    ]
  ],
  ...
}

Images Object

The images parameter allows you to add images to your generated PDF. Each key represents a placeholder in the template, and the value is an object containing the image's URL and optional size specifications (width and height).

It is not required to prefix your image keys with image_, but if you follow this convention, template2pdf is able to parse the document and extract a structure for you.
{
  "images": {
    "{{image_sales}}": {
      "url": "https://example.com/image.png",
      "width": 100,
      "height": 100
    },
    "{{image_logo}}": {
      "url": "https://example.com/logo.png",
      "width": 50,
      "height": 50
    }
  },
  ...
}
Field Type Description
url string The URL of the image to insert. Required.
width number The width of the image in pixels. Defaults to 50 if not specified.
height number The height of the image in pixels. Defaults to 50 if not specified.

Webhook Object

{
  ...
  "webhook": {
    "url": "https://example.com/webhook",
    "method": "POST",
    "headers": {
      "Content-Type": "application/json"
    }
  },
  ...
}
Field Type Description
url string The webhook URL to receive completion notifications. Required.
method string The HTTP method to use for the webhook. Defaults to POST.
headers object Headers to include in the webhook request. Defaults to {"Content-Type": "application/json"}.

Webhook Payload

Payload will be the json representation of the completed convert job.

{
  "id": 12345,
  "document_url": "https://template2pdf.com/example_output.pdf",
  "status": "completed",
  "error_message": null
}

GET /convert_jobs.json

List all convert jobs.


GET /convert_jobs/:id.json

Get a specific convert job by ID.


DELETE /convert_jobs/:id.json

Delete a specific convert job by ID.


Auto Structure Extraction

You can call the API to fetch a basic template json payload for a given document_id


GET /docs_structure/build.json?id=:id

Fetch a ready to use payload.


GET /docs_structure/show.json?id=:id

Just the used keys in a simple structure.

Errors

Template2PDF uses conventional HTTP response codes to indicate the success or failure of an API request. In general, codes in the 2xx range indicate success, codes in the 4xx range indicate an error due to the provided information (e.g., a required parameter is missing), and codes in the 5xx range indicate an error on the server's end.

Start your free trail!

Sign up now

OMG Cookies!

Not proud of it, but I would like to use google analytics to track your visit.
You can of course decline and still use the site.
But I would be very happy if you accept. :)
For more information, please read the privacy policy and cookie policy.

Reject additional cookies     Accept additional cookies