API Reference

Everything you need to integrate PDFForge into your application. Fill PDF forms, generate documents from HTML, and manage your templates programmatically.

Introduction

Overview of the PDFForge REST API.

PDFForge is a REST API for filling PDF forms, generating PDFs from HTML templates, and filling DOCX templates. All requests are made to:

https://api.pdfforge.dev/v1

The API accepts JSON request bodies (except file uploads which use multipart/form-data) and returns JSON responses. All timestamps are in ISO 8601 UTC format.

Key concepts:

  • Templates — Reusable PDF forms, HTML/Handlebars templates, or DOCX templates stored in your account
  • Documents — Generated PDFs or DOCX files created by filling a template with data
  • API Keys — Bearer tokens used to authenticate every request (pk_live_ for production, pk_test_ for testing)

Authentication

Authenticating your API requests.

Every API request must include a valid API key in the Authorization header:

Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

API keys use the format pk_live_ (production) or pk_test_ (testing) followed by 32 random characters. Keys are hashed with SHA-256 before storage — they are shown exactly once at creation time and can never be retrieved again.

You can create and manage API keys from the Dashboard or via the API Keys endpoints below.

Requests with a missing or invalid key receive a 401 Unauthorized response.

Sandbox Mode

Test your integration safely with sandbox API keys.

API keys come in two modes: Live (pk_live_) and Sandbox (pk_test_). Sandbox keys let you test your integration without affecting production quotas or triggering webhooks.

How sandbox mode differs from production:

AspectLive KeysSandbox Keys
DocumentsReal outputWatermarked "SANDBOX — NOT FOR PRODUCTION"
Usage quotaPlan limit/month60 documents/day
Rate limitPlan-basedFixed 60 req/min
Retention7 days – 1 year1 hour
WebhooksTriggeredDisabled

Key behaviors:

  • Sandbox keys access the same templates as live keys (shared data, no isolation)
  • All three document types (PDF fill, HTML generate, DOCX fill) are watermarked
  • Usage events are tracked separately with mode: "test" — they never count against your monthly quota
  • The daily sandbox cap resets at midnight UTC
  • Documents created in sandbox mode expire after 1 hour regardless of your plan

Creating sandbox keys:

Create a sandbox key from the Dashboard by selecting "Test" mode, or via the API:

POST /v1/api-keys
{ "name": "My Test Key", "mode": "test" }

Use sandbox keys exactly like live keys — just swap the Authorization header. The API automatically applies sandbox rules based on the key prefix.

Quick Start

Generate your first PDF in three steps.

Step 1 — Create an API key

Sign in to the Dashboard and create a new API key. Copy the raw key — you will not see it again.

Step 2 — Upload a template

Upload a PDF form, HTML/Handlebars template, or DOCX template via POST /v1/templates. The API detects fillable fields and template tags automatically.

Step 3 — Generate a document

Call POST /v1/documents/fill (for PDF forms and DOCX templates) or POST /v1/documents/generate (for HTML templates) with your template ID and data. You will receive a signed download URL in the response.

That's it. Three API calls to go from zero to a production document.

Templates

Upload, manage, and inspect your PDF, HTML, and DOCX templates.

POST/v1/templates

Upload a PDF form, HTML/Handlebars template, or DOCX template. The API auto-detects fillable fields and template tags.

Content-Type: multipart/form-data
Body Parameters
NameTypeRequiredDescription
fileFilePDF (.pdf), HTML (.html, .hbs, .handlebars), or DOCX (.docx) file
namestringHuman-readable template name

Response 201

{
  "id": "tpl_a1b2c3d4e5f6g7h8",
  "name": "Invoice Template",
  "type": "html",
  "fields": [
    {
      "name": "company",
      "type": "string"
    },
    {
      "name": "amount",
      "type": "number"
    }
  ],
  "created_at": "2026-02-08T10:00:00.000Z"
}
POST/v1/templates/from-pdf

Analyze a static PDF with AI and generate a matching HTML/Handlebars template with detected fields and sample data.

Content-Type: multipart/form-data
Body Parameters
NameTypeRequiredDescription
fileFileA PDF file to analyze (max 32 MB)
namestringTemplate name (defaults to filename without .pdf extension)

Response 201

{
  "id": "tpl_x9y8z7w6v5u4t3s2",
  "name": "Contract Agreement",
  "type": "html",
  "fields": [
    {
      "name": "client_name",
      "type": "text"
    },
    {
      "name": "contract_date",
      "type": "date"
    },
    {
      "name": "total_amount",
      "type": "currency"
    }
  ],
  "analysis": {
    "document_type": "contract",
    "language": "en",
    "page_count": 3,
    "structure_summary": "A service agreement with client details, terms, and signature block.",
    "fields": [
      {
        "variable_name": "client_name",
        "field_type": "text"
      },
      {
        "variable_name": "contract_date",
        "field_type": "date"
      },
      {
        "variable_name": "total_amount",
        "field_type": "currency"
      }
    ],
    "sample_data": {
      "client_name": "Acme Corp",
      "contract_date": "2026-03-15",
      "total_amount": "12,500.00"
    }
  },
  "created_at": "2026-02-08T10:05:00.000Z"
}

This endpoint uses AI-powered analysis to convert your PDF into an HTML template.

PDF must be a valid file and cannot exceed 32 MB.

The generated template is an AI approximation (80–90% fidelity). Review and refine it in the editor before production use.

Embedded JPEG and PNG images are extracted and included. Vector-only logos (PDF drawing operators) cannot be extracted and will be approximated or omitted.

Custom fonts are replaced with web-safe alternatives (Helvetica, Georgia, Courier). Exact font matching is not possible.

Complex layouts (overlapping elements, multi-column text flows, intricate tables) may require manual adjustment.

Checkboxes and form fields are converted to styled HTML elements with Handlebars conditionals — native PDF form widgets are not preserved.

GET/v1/templates

Retrieve all templates belonging to your account, ordered by creation date.

Response 200

{
  "data": [
    {
      "id": "tpl_a1b2c3d4e5f6g7h8",
      "name": "Invoice Template",
      "type": "html",
      "fields_count": 5,
      "created_at": "2026-02-08T10:00:00.000Z"
    },
    {
      "id": "tpl_r4s3t2u1v0w9x8y7",
      "name": "Application Form",
      "type": "pdf_form",
      "fields_count": 12,
      "created_at": "2026-02-07T14:30:00.000Z"
    }
  ]
}
GET/v1/templates/:id

Retrieve full details for a single template, including its field definitions.

Path Parameters
NameTypeRequiredDescription
idstringTemplate ID (e.g. tpl_a1b2c3d4e5f6g7h8)

Response 200

{
  "id": "tpl_a1b2c3d4e5f6g7h8",
  "name": "Invoice Template",
  "type": "html",
  "fields": [
    {
      "name": "company",
      "type": "string"
    },
    {
      "name": "amount",
      "type": "number"
    },
    {
      "name": "date",
      "type": "string"
    }
  ],
  "created_at": "2026-02-08T10:00:00.000Z"
}
GET/v1/templates/:id/fields

Retrieve only the fillable fields for a template. Useful for building dynamic forms.

Path Parameters
NameTypeRequiredDescription
idstringTemplate ID

Response 200

{
  "fields": [
    {
      "name": "full_name",
      "type": "text"
    },
    {
      "name": "email",
      "type": "email"
    },
    {
      "name": "agree_terms",
      "type": "checkbox"
    }
  ]
}
PATCH/v1/templates/:id/fields

Update the type metadata for template fields. Field names cannot be changed — only their types.

Path Parameters
NameTypeRequiredDescription
idstringTemplate ID
Body Parameters
NameTypeRequiredDescription
fieldsarrayArray of { name, type } objects to update

Request Body

{
  "fields": [
    {
      "name": "full_name",
      "type": "text"
    },
    {
      "name": "email",
      "type": "email"
    }
  ]
}

Response 200

{
  "fields": [
    {
      "name": "full_name",
      "type": "text"
    },
    {
      "name": "email",
      "type": "email"
    },
    {
      "name": "agree_terms",
      "type": "checkbox"
    }
  ]
}
GET/v1/templates/:id/content

Retrieve the raw HTML source of an HTML template. Only available for templates with type "html".

Path Parameters
NameTypeRequiredDescription
idstringTemplate ID

Response 200

{
  "content": "<div class=\"invoice\">{{company}} — {{amount}}</div>"
}

Returns 400 INVALID_TEMPLATE_TYPE for PDF form templates.

PATCH/v1/templates/:id

Update a template's name and/or HTML content. When content is updated, fields are automatically re-extracted.

Path Parameters
NameTypeRequiredDescription
idstringTemplate ID
Body Parameters
NameTypeRequiredDescription
namestringNew template name
contentstringNew HTML content (html templates only)

Request Body

{
  "name": "Invoice Template v2",
  "content": "<div class=\"invoice\">{{company}} — {{total}}</div>"
}

Response 200

{
  "id": "tpl_a1b2c3d4e5f6g7h8",
  "name": "Invoice Template v2",
  "type": "html",
  "fields": [
    {
      "name": "company",
      "type": "string"
    },
    {
      "name": "total",
      "type": "string"
    }
  ],
  "created_at": "2026-02-08T10:00:00.000Z"
}

At least one of name or content is required.

Content update is only available for HTML templates.

DELETE/v1/templates/:id

Permanently delete a template and its file from storage.

Path Parameters
NameTypeRequiredDescription
idstringTemplate ID

Response 200

{
  "deleted": true
}

Documents

Fill PDF forms and DOCX templates, generate PDFs from HTML, and manage your documents.

POST/v1/documents/fill

Fill a PDF form or DOCX template with the provided data and receive a download URL for the completed document. Output format matches template type: PDF for pdf_form, DOCX for docx.

Body Parameters
NameTypeRequiredDescription
template_idstringID of a pdf_form or docx template
dataobjectKey-value map of field names to values (string, number, or boolean)
options.flattenbooleanFlatten form fields after filling (PDF only)(default: false)
options.filenamestringOutput filename
options.webhook_urlstringURL to receive a one-time POST with the document response (fire-and-forget, unsigned)

Request Body

{
  "template_id": "tpl_r4s3t2u1v0w9x8y7",
  "data": {
    "full_name": "Jane Smith",
    "email": "jane@example.com",
    "agree_terms": true
  },
  "options": {
    "flatten": true,
    "filename": "application-jane.pdf"
  }
}

Response 201

{
  "id": "doc_m3n4o5p6q7r8s9t0",
  "status": "completed",
  "download_url": "https://storage.pdfforge.dev/documents/...",
  "expires_at": "2026-02-15T10:00:00.000Z",
  "filename": "application-jane.pdf",
  "pages": 2,
  "size_bytes": 45230
}

For DOCX templates, the pages field in the response is null.

Output format matches template type: PDF for pdf_form, DOCX for docx.

POST/v1/documents/generate

Render an HTML/Handlebars template with data and convert to PDF using a headless browser.

Body Parameters
NameTypeRequiredDescription
template_idstringID of an html template
dataobjectTemplate variables passed to Handlebars
options.formatstringPage size: A4, A3, Letter, Legal(default: A4)
options.marginobjectPage margins: { top, right, bottom, left }
options.landscapebooleanLandscape orientation(default: false)
options.filenamestringOutput filename
options.webhook_urlstringURL to receive a one-time POST with the document response (fire-and-forget, unsigned)

Request Body

{
  "template_id": "tpl_a1b2c3d4e5f6g7h8",
  "data": {
    "company": "Acme Corp",
    "items": [
      {
        "name": "Widget",
        "qty": 3,
        "price": 9.99
      },
      {
        "name": "Gadget",
        "qty": 1,
        "price": 24.5
      }
    ]
  },
  "options": {
    "format": "A4",
    "margin": {
      "top": "20mm",
      "bottom": "20mm"
    },
    "filename": "invoice-acme.pdf"
  }
}

Response 201

{
  "id": "doc_k1l2m3n4o5p6q7r8",
  "status": "completed",
  "download_url": "https://storage.pdfforge.dev/documents/...",
  "expires_at": "2026-03-10T10:00:00.000Z",
  "filename": "invoice-acme.pdf",
  "pages": 1,
  "size_bytes": 82104
}
GET/v1/documents

Retrieve a paginated list of documents for your account with optional filters.

Query Parameters
NameTypeRequiredDescription
limitintegerMaximum number of results(default: 50)
offsetintegerPagination offset(default: 0)
statusstringFilter by status: completed, expired, failed
template_idstringFilter by template ID

Response 200

{
  "data": [
    {
      "id": "doc_k1l2m3n4o5p6q7r8",
      "template_id": "tpl_a1b2c3d4e5f6g7h8",
      "template_name": "Invoice Template",
      "status": "completed",
      "filename": "invoice-acme.pdf",
      "size_bytes": 82104,
      "created_at": "2026-02-08T12:00:00.000Z",
      "expires_at": "2026-03-10T12:00:00.000Z"
    }
  ],
  "total": 42,
  "limit": 50,
  "offset": 0
}
GET/v1/documents/:id

Retrieve details for a single document.

Path Parameters
NameTypeRequiredDescription
idstringDocument ID (e.g. doc_k1l2m3n4o5p6q7r8)

Response 200

{
  "id": "doc_k1l2m3n4o5p6q7r8",
  "template_id": "tpl_a1b2c3d4e5f6g7h8",
  "template_name": "Invoice Template",
  "status": "completed",
  "filename": "invoice-acme.pdf",
  "size_bytes": 82104,
  "created_at": "2026-02-08T12:00:00.000Z",
  "expires_at": "2026-03-10T12:00:00.000Z"
}
GET/v1/documents/:id/download

Get a fresh signed download URL for a document. The URL is valid for 24 hours.

Path Parameters
NameTypeRequiredDescription
idstringDocument ID

Response 200

{
  "download_url": "https://storage.pdfforge.dev/documents/...",
  "expires_in": 86400
}

Returns 410 Gone if the document has expired based on your plan's retention period.

DELETE/v1/documents/:id

Permanently delete a document from storage and the database.

Path Parameters
NameTypeRequiredDescription
idstringDocument ID

Response 200

{
  "deleted": true
}

Usage

Track your document generation usage for the current billing period.

GET/v1/usage

Retrieve fill and generate counts for the current calendar month.

Query Parameters
NameTypeRequiredDescription
modestringFilter by key mode: "live", "test", or "all"(default: all)

Response 200

{
  "mode": "all",
  "fill": 12,
  "generate": 38,
  "total": 50,
  "period": {
    "start": "2026-02-01T00:00:00.000Z",
    "end": "2026-02-28T23:59:59.000Z"
  }
}

Billing

Manage subscriptions and check billing status via Stripe integration.

POST/v1/billing/checkout

Create a Stripe Checkout session to subscribe to a paid plan. Redirects the user to Stripe's hosted payment page.

Body Parameters
NameTypeRequiredDescription
planstringTarget plan: "starter", "pro", or "business"

Request Body

{
  "plan": "pro"
}

Response 200

{
  "url": "https://checkout.stripe.com/c/pay/cs_live_..."
}
POST/v1/billing/portal

Create a Stripe Customer Portal session for managing the existing subscription, invoices, and payment methods.

Response 200

{
  "url": "https://billing.stripe.com/p/session/..."
}

Returns 400 NO_SUBSCRIPTION if the account has no active Stripe customer.

GET/v1/billing/status

Retrieve current plan, usage limits, and subscription details.

Response 200

{
  "plan": "pro",
  "limit": 2500,
  "used": 127,
  "remaining": 2373,
  "subscriptionStatus": "active",
  "planPeriodEnd": "2026-03-08T00:00:00.000Z"
}

The limit and remaining fields are null for the Enterprise plan (unlimited).

Account

View and update your account details.

GET/v1/account

Retrieve details for the currently authenticated account.

Response 200

{
  "id": "acc_f1e2d3c4b5a6",
  "email": "jane@example.com",
  "name": "Jane Smith",
  "plan": "pro",
  "createdAt": "2026-01-15T09:30:00.000Z"
}
PATCH/v1/account

Update the account display name.

Body Parameters
NameTypeRequiredDescription
namestringNew display name (1-255 characters)

Request Body

{
  "name": "Jane Smith-Williams"
}

Response 200

{
  "id": "acc_f1e2d3c4b5a6",
  "email": "jane@example.com",
  "name": "Jane Smith-Williams",
  "plan": "pro",
  "createdAt": "2026-01-15T09:30:00.000Z"
}

API Keys

Create, list, and revoke API keys. These endpoints require session authentication (dashboard cookie).

GET/v1/api-keys

List all API keys for your account. The raw key value is never returned — only the prefix is shown.

Response 200

[
  {
    "id": "ak_9a8b7c6d5e4f",
    "prefix": "pk_live_a1b2c3d4",
    "name": "Production Key",
    "lastUsed": "2026-02-08T08:15:00.000Z",
    "createdAt": "2026-01-20T12:00:00.000Z"
  },
  {
    "id": "ak_3f2e1d0c9b8a",
    "prefix": "pk_live_x9y8z7w6",
    "name": "Staging Key",
    "lastUsed": null,
    "createdAt": "2026-02-01T16:45:00.000Z"
  }
]
POST/v1/api-keys

Generate a new API key. The raw key is returned in the response and can never be retrieved again.

Body Parameters
NameTypeRequiredDescription
namestringA human-readable label for the key (1-255 characters)
modestringKey mode: "live" (production) or "test" (sandbox)(default: live)

Request Body

{
  "name": "CI/CD Pipeline",
  "mode": "live"
}

Response 201

{
  "id": "ak_h7g6f5e4d3c2",
  "prefix": "pk_live_q1w2e3r4",
  "name": "CI/CD Pipeline",
  "createdAt": "2026-02-08T14:00:00.000Z",
  "key": "pk_live_q1w2e3r4t5y6u7i8o9p0a1s2d3f4g5h6"
}

The key field is only returned once at creation time. Store it securely.

DELETE/v1/api-keys/:id

Permanently revoke an API key. Any request using this key will immediately receive 401 Unauthorized.

Path Parameters
NameTypeRequiredDescription
idstringAPI key ID

Response 200

{
  "message": "API key revoked"
}

Webhooks

Receive real-time notifications when documents are generated, fail, or expire.

Webhooks notify your application when events occur in your PDFForge account. Each delivery includes an HMAC-SHA256 signature for verification.

Event types:

EventTrigger
`document.completed`Document successfully generated
`document.failed`Document generation failed
`document.expired`Document expired and was cleaned up
`document.deleted`Document was manually deleted

Signature verification:

Every webhook delivery includes an X-PDFForge-Signature header containing an HMAC-SHA256 signature of the request body, signed with your endpoint's secret:

X-PDFForge-Signature: sha256=<hex-digest>

Verify the signature in your handler to ensure the request came from PDFForge. The endpoint secret is returned when you create or retrieve a webhook endpoint.

Per-request webhooks:

In addition to global webhook endpoints, you can pass a webhook_url in the options of any fill or generate request. This fires a one-time, unsigned POST to your URL with the document response payload. Per-request webhooks are useful for async workflows where you want notification for a specific document.

{
  "template_id": "tpl_abc123",
  "data": { "name": "Jane" },
  "options": {
    "webhook_url": "https://your-app.com/hooks/pdfforge"
  }
}

Note: Webhooks are available on Pro plans and above. Sandbox (test) keys do not trigger webhooks.

POST/v1/webhooks

Register a new webhook endpoint to receive event notifications.

Body Parameters
NameTypeRequiredDescription
urlstringHTTPS URL to receive webhook deliveries
eventsstring[]Event types to subscribe to

Request Body

{
  "url": "https://your-app.com/webhooks/pdfforge",
  "events": [
    "document.completed",
    "document.failed"
  ]
}

Response 201

{
  "id": "wh_a1b2c3d4e5f6g7h8",
  "url": "https://your-app.com/webhooks/pdfforge",
  "events": [
    "document.completed",
    "document.failed"
  ],
  "secret": "whsec_k9j8h7g6f5e4d3c2b1a0...",
  "active": true,
  "created_at": "2026-02-16T10:00:00.000Z"
}

The secret is only returned at creation time. Store it securely for signature verification.

URL must use HTTPS.

Maximum 5 webhook endpoints per account.

GET/v1/webhooks

List all webhook endpoints for your account.

Response 200

[
  {
    "id": "wh_a1b2c3d4e5f6g7h8",
    "url": "https://your-app.com/webhooks/pdfforge",
    "events": [
      "document.completed",
      "document.failed"
    ],
    "active": true,
    "created_at": "2026-02-16T10:00:00.000Z"
  }
]
GET/v1/webhooks/:id

Retrieve details for a single webhook endpoint.

Path Parameters
NameTypeRequiredDescription
idstringWebhook endpoint ID

Response 200

{
  "id": "wh_a1b2c3d4e5f6g7h8",
  "url": "https://your-app.com/webhooks/pdfforge",
  "events": [
    "document.completed",
    "document.failed"
  ],
  "active": true,
  "created_at": "2026-02-16T10:00:00.000Z"
}
PATCH/v1/webhooks/:id

Update an existing webhook endpoint's URL, events, or active status.

Path Parameters
NameTypeRequiredDescription
idstringWebhook endpoint ID
Body Parameters
NameTypeRequiredDescription
urlstringNew HTTPS URL
eventsstring[]New event subscriptions
activebooleanEnable or disable the endpoint

Request Body

{
  "events": [
    "document.completed",
    "document.failed",
    "document.expired"
  ],
  "active": true
}

Response 200

{
  "id": "wh_a1b2c3d4e5f6g7h8",
  "url": "https://your-app.com/webhooks/pdfforge",
  "events": [
    "document.completed",
    "document.failed",
    "document.expired"
  ],
  "active": true,
  "created_at": "2026-02-16T10:00:00.000Z"
}
DELETE/v1/webhooks/:id

Permanently delete a webhook endpoint. Pending deliveries will be cancelled.

Path Parameters
NameTypeRequiredDescription
idstringWebhook endpoint ID

Response 200

{
  "deleted": true
}

Errors

Error format, codes, and document retention policies.

All API errors return a consistent JSON envelope:

{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description of what went wrong"
  }
}

Error Codes

CodeHTTPDescription
VALIDATION_ERROR400Request body validation failed
INVALID_INPUT400Missing or malformed input
INVALID_TEMPLATE_TYPE400Wrong template type for the operation
PDF_TOO_LARGE400Uploaded PDF exceeds the 32 MB limit
NO_SUBSCRIPTION400No active Stripe subscription found
UNAUTHORIZED401Missing or invalid API key
TEMPLATE_NOT_FOUND404Template does not exist or is not owned by your account
DOCUMENT_NOT_FOUND404Document does not exist or is not owned by your account
API_KEY_NOT_FOUND404API key does not exist
DOCUMENT_EXPIRED410Document has expired and been cleaned up
USAGE_LIMIT_EXCEEDED429Monthly document generation limit reached
SANDBOX_DAILY_LIMIT429Sandbox daily limit of 60 documents reached

Document Retention

Generated documents are retained based on your plan. After expiration, files are removed from storage and the document status changes to expired.

PlanRetention
Free7 days
Starter30 days
Pro90 days
Business365 days
EnterpriseUnlimited

Sandbox documents (created with pk_test_ keys) always expire after 1 hour regardless of plan.