PDF Generator API
Document review and signing flow

Embed document generation and signing into your app with one API

Most e-signature APIs assume you already have a finished PDF. PDF Generator API works the other way around: you can generate a document from your own templates and data, or bring in a PDF you already have, and open either in the same embeddable review-and-sign experience. You make one API call, get back a URL, and drop it into your app.

This guide walks through both paths end to end:

  • Generate a document from a template, then sign it.
  • Import an existing PDF, then sign it.

By the end, you will have a working viewer URL you can embed in an iframe or redirect to, plus the calls to read back versions and the audit log. For the product overview, see the embeddable document signing page. For the full reference, see the API documentation.

Document review and signing flow

How the Document Viewer works

The review-and-sign experience is hosted in a Document Viewer. You reach it the same way every time: by setting output=viewer in the request. Instead of returning a file or a base64 blob, the API returns a URL that opens the document directly in the guided flow, where the signer confirms their details, applies a typed, drawn, or uploaded signature, and completes the process. Every action is versioned and automatically written to an audit log.

There are three ways to open the viewer: generate a document, store an existing PDF, or collect data with a Web Form. This article covers the first two in detail.

Before you start

You need a PDF Generator API account. Start a free trial at app.pdfgeneratorapi.com/signup, then grab your API Key and API Secret from your Account Settings.

A few facts to keep handy:

  • Base URL for every endpoint: https://us1.pdfgeneratorapi.com/api/v4
  • Rate limits: 2 requests per second and 60 per minute per IP. Over-the-limit returns HTTP 429 with a Retry-After header.
  • Authentication is JWT, generated server-side (more on this below).

Official client libraries

Every example below is shown as raw HTTP (cURL) so it works in any language, plus a client-library version. The clients are auto-generated from the OpenAPI spec and available for JavaScript, PHP, Python, Ruby, Java, and Go. Browse them all on GitHub:

There is also an MCP server if you want to drive generation from an AI agent, and a Postman collection for poking at endpoints before you write any code.

Step 1: Authenticate

PDF Generator API uses JSON Web Tokens. You sign a short-lived token server-side with your API Secret and send it as a Bearer token on each request. Never generate tokens in the browser, since that would expose your secret.

  • iss: your API Key
  • sub: your workspace identifier (the email you signed up with, for the master workspace)
  • exp: expiry as a Unix timestamp, kept short

Signing is HMAC-SHA256. Here it is in Node with the jsonwebtoken library:

javascript
const jwt = require("jsonwebtoken");
function createToken() {
return jwt.sign(
{
iss: process.env.PDFGEN_API_KEY,
sub: process.env.PDFGEN_WORKSPACE, // e.g. you@example.com
exp: Math.floor(Date.now() / 1000) + 30, // valid for 30 seconds
},
process.env.PDFGEN_API_SECRET
);
}

And in Python with pyjwt:

javascript
import os, time, jwt
def create_token():
payload = {
"iss": os.environ["PDFGEN_API_KEY"],
"sub": os.environ["PDFGEN_WORKSPACE"],
"exp": int(time.time()) + 30,
}
return jwt.encode(payload, os.environ["PDFGEN_API_SECRET"], algorithm="HS256")

For quick tests, you can also mint a temporary token from your Account Settings that is valid for 15 minutes, so you can try the calls below without wiring up signing first. The full details are in the authentication docs.

Step 2, path A: Generate a document, then sign it

First, design a template once in the browser-based drag-and-drop editor. The editor extracts data fields from a sample JSON payload, so at request time you just send the data. See the features page for what the template engine can do (dynamic tables, conditional content, an expression language, barcodes, and more).

Then call Generate Document with output set to viewer. You pass the template ID and the data to merge:

bash
curl --request POST \
--url https://us1.pdfgeneratorapi.com/api/v4/documents/generate \
--header "Authorization: Bearer $TOKEN" \
--header "Content-Type: application/json" \
--data '{
"template": {
"id": "1024",
"data": {
"disclosing_party": "ACME Inc.",
"receiving_party": "John Coyote"
}
},
"format": "pdf",
"output": "viewer",
"name": "NDA Signing"
}'

The response gives you the viewer URL in response:

json
{
"response": "https://app.pdfgeneratorapi.com/viewer/…",
"meta": {
"name": "NDA Signing.pdf",
"display_name": "NDA Signing",
"content_type": "application/pdf"
}
}

The same call using the JavaScript client (npm install pdf-generator-api-client):

javascript
const PDFGeneratorAPI = require("pdf-generator-api-client");
const client = PDFGeneratorAPI.ApiClient.instance;
client.authentications["JSONWebTokenAuth"].accessToken = createToken();
const documents = new PDFGeneratorAPI.DocumentsApi();
const request = {
template: { id: "1024", data: { disclosing_party: "ACME Inc.", receiving_party: "John Coyote" } },
format: "pdf",
output: "viewer",
name: "NDA Signing",
};
documents.generateDocument(request, (error, data) => {
if (error) return console.error(error);
console.log("Viewer URL:", data.response);
});

Take data.response, hand it to your frontend, and you are done. (Method and model names for each language are listed in that client’s DocumentsApi docs on GitHub.)

Step 2, path B: Import an existing PDF, then sign it

Already producing PDFs somewhere else? Push the finished file to storage and get a review URL back. Call Store Document with output set to viewer. Provide the file as a public HTTPS file_url:

bash
curl --request POST \
--url https://us1.pdfgeneratorapi.com/api/v4/documents \
--header "Authorization: Bearer $TOKEN" \
--header "Content-Type: application/json" \
--data '{
"file_url": "https://example.com/contracts/nda-signing.pdf",
"name": "NDA Signing",
"output": "viewer"
}'

Or send the bytes directly as file_base64 instead of file_url:

json
{
"file_base64": "JVBERi0xLjcKJ…",
"name": "NDA Signing",
"output": "viewer"
}

The response includes the viewer URL and a public_id you should keep, since you use it to read versions and the audit log later:

json
{
"response": "https://app.pdfgeneratorapi.com/viewer/…",
"meta": {
"name": "NDA Signing.pdf",
"display_name": "NDA Signing",
"content_type": "application/pdf",
"public_id": "bac8381bce1982e5f6957a0f52371336"
}
}

The Python client (pip install git+https://github.com/pdfgeneratorapi/python-client.git) version:

javascript
import pdf_generator_api_client
from pdf_generator_api_client.rest import ApiException
configuration = pdf_generator_api_client.Configuration(access_token=create_token())
with pdf_generator_api_client.ApiClient(configuration) as api_client:
documents = pdf_generator_api_client.DocumentsApi(api_client)
request = {
"file_url": "https://example.com/contracts/nda-signing.pdf",
"name": "NDA Signing",
"output": "viewer",
}
try:
result = documents.store_document(request)
print("Viewer URL:", result.response)
except ApiException as e:
print("Error:", e)

Step 3: Embed the viewer

The viewer URL is a full, responsive web page. You have two options.

Redirect the user to it:

window.location.href = viewerUrl;

Or embed it in your own layout with an iframe:

<iframe src="https://app.pdfgeneratorapi.com/viewer/…" style="width: 100%; height: 100vh; border: 0;" allow="fullscreen"> </iframe>

In the viewer, the signer enters their details, applies a signature, and confirms. Nothing to build on your side.

Here is an example rendering of the iframe:

Step 4: Read versions and the audit trail

After signing, you often need proof of what happened. Two endpoints give it to you, both keyed on the public_id from the store or generate response.

Every signature and review creates a new version. Pull the history with Get Document Versions:

bash
curl --request GET \
--url https://us1.pdfgeneratorapi.com/api/v4/documents/{publicId}/versions \
--header "Authorization: Bearer $TOKEN"

Every interaction is recorded in the audit log. Read it with Get Document Actions:

bash
curl --request GET \
--url https://us1.pdfgeneratorapi.com/api/v4/documents/{publicId}/actions \
--header "Authorization: Bearer $TOKEN"
json
{
"response": [
{
"action": "opened",
"person": { "first_name": "John", "last_name": "Coyote", "email": "coyote@acme.com", "ip": "203.0.113.24" },
"created_at": "2026-07-01 09:55:03"
},
{
"action": "signed",
"person": { "first_name": "John", "last_name": "Coyote", "email": "coyote@acme.com", "id_code": "39001010000", "ip": "203.0.113.24" },
"created_at": "2026-07-01 09:57:12"
}
]
}

Actions include accepted and declined, each with the signer’s identity, IP address, and a timestamp. Poll this endpoint to detect completion, or, if you collect through a Web Form, use its send action to post the result to your own endpoint.

Bonus: collect the data with a Web Form

If you would rather not build the input UI either, a Web Form collects the data, generates the document from the answers, and drops the user into the signing flow on submit. Enable the “Sign the document” action on the form, and the redirect happens automatically. The form endpoints are in the Forms API reference.

Production notes

  • Keep tokens server-side and short-lived. Sign each request with a fresh JWT that expires in seconds. Your API Secret never leaves your backend.
  • Handle 429s. Respect the Retry-After header and back off. For high volume, the API also offers batch and asynchronous generation.
  • Storage, retention, and cleanup. Documents are stored encrypted, with configurable long-term retention. You can point the service at your own storage and set an automated cleanup period. See Data Security and Privacy.
  • Errors. Failed auth and validation return clear messages and standard HTTP status codes. Wrap calls in the error handling provided by your client library.

Wrap up

Two calls, one flow. Generate Document builds the PDF from your template and data, Store Document takes a PDF you already have, and either one opens the same review-and-sign viewer when you set output=viewer. Versions and the audit trail come for free.

Useful links:

Interested in Document Automation?

Meet Michal Liska. He is our pre-sales engineer and operations manager. He is the best person to help you achieve your goals and answer any questions you might have.

Book a meeting with Michal ›

Michal Liska