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.
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 Keysub: 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:
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:
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:
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:
{ "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):
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:
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:
{ "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:
{ "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:
import pdf_generator_api_clientfrom 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:
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:
curl --request GET \ --url https://us1.pdfgeneratorapi.com/api/v4/documents/{publicId}/actions \ --header "Authorization: Bearer $TOKEN"{ "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-Afterheader 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:
- Product overview: Embeddable document signing
- Template engine: Features
- Data-driven forms: Web Forms Automation
- Security and storage: Data Security and Privacy
- Full reference: API documentation
- Client libraries and MCP server: GitHub
- Start building: Create a free account
