Skip to main content
Base URL: https://api.formepdf.com All /v1/* endpoints require an API key passed as a Bearer token:
Authorization: Bearer forme_sk_...
Create API keys in the dashboard.

Render PDF (sync)

POST /v1/render/:slug Renders a PDF from a template and returns the file directly. Path params: slug — your template’s URL slug. Body: JSON object passed as template data. All fields are forwarded to your JSX template function. Optional fields:
  • s3 — upload the PDF to your S3 bucket instead of returning bytes (see S3 Upload below)
  • saveboolean, default true. Every render is auto-saved to your Documents with source: "generated". Set false to skip saving.
  • saveNamestring, optional custom document name. Default: {slug}-{YYYY-MM-DD} (e.g. invoice-2026-04-03).
  • metadataobject, optional developer-defined key-value pairs stored on the saved document. Useful for tagging renders with your own identifiers (customer ID, department, environment, etc.). See Metadata below.
curl https://api.formepdf.com/v1/render/invoice \
  -H "Authorization: Bearer forme_sk_abc123..." \
  -H "Content-Type: application/json" \
  -d '{"clientName": "Jane Smith", "date": "2024-01-15", "dueDate": "2024-02-15", "items": [{"description": "Consulting", "quantity": 10, "unitPrice": 150}]}' \
  --output invoice.pdf
Response: 200 OK with Content-Type: application/pdf body.

Render PDF (async)

POST /v1/render/:slug/async Queues a render job and returns immediately. Use this for large documents or when you don’t need the PDF inline. Body: Same as sync, plus:
  • webhookUrl (optional) — URL to POST the result to when rendering completes
curl -X POST https://api.formepdf.com/v1/render/invoice/async \
  -H "Authorization: Bearer forme_sk_abc123..." \
  -H "Content-Type: application/json" \
  -d '{"clientName": "Jane Smith", "date": "2024-01-15", "dueDate": "2024-02-15", "items": [{"description": "Consulting", "quantity": 10, "unitPrice": 150}], "webhookUrl": "https://example.com/webhook"}'
Response: 202 Accepted
{ "jobId": "clxyz...", "status": "pending" }

Webhook payload

When the job completes, Forme POSTs to your webhookUrl:
{
  "jobId": "clxyz...",
  "status": "complete",
  "pdfBase64": "JVBERi0xLjQK..."
}
On failure:
{
  "jobId": "clxyz...",
  "status": "failed",
  "error": "Missing required field: \"items\""
}

Poll Job Status

GET /v1/jobs/:jobId Check the status of an async render job.
curl https://api.formepdf.com/v1/jobs/clxyz... \
  -H "Authorization: Bearer forme_sk_abc123..."
Response:
{
  "id": "clxyz...",
  "status": "complete",
  "pdfBase64": "JVBERi0xLjQK...",
  "completedAt": "2024-01-15T12:00:00.000Z"
}
Possible status values: pending, processing, complete, failed.

S3 Upload

Pass an s3 object in the sync render body to upload the PDF directly to your S3-compatible bucket instead of returning the bytes.
curl -X POST https://api.formepdf.com/v1/render/invoice \
  -H "Authorization: Bearer forme_sk_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "clientName": "Jane Smith",
    "date": "2024-01-15",
    "dueDate": "2024-02-15",
    "items": [{"description": "Consulting", "quantity": 10, "unitPrice": 150}],
    "s3": {
      "bucket": "my-pdfs",
      "key": "invoices/inv-001.pdf",
      "region": "us-east-1",
      "accessKeyId": "AKIA...",
      "secretAccessKey": "secret..."
    }
  }'
Response: 200 OK
{ "url": "https://my-pdfs.s3.us-east-1.amazonaws.com/invoices/inv-001.pdf" }
S3 config fields:
FieldRequiredDescription
bucketYesS3 bucket name
keyYesObject key (path) in the bucket
accessKeyIdYesAWS access key ID
secretAccessKeyYesAWS secret access key
region*AWS region (e.g. us-east-1). Required unless endpoint is set.
endpoint*Custom S3-compatible endpoint URL. Required unless region is set.
Works with any S3-compatible service (AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces).

Extract Embedded Data

POST /v1/extract Extract embedded JSON data from a PDF that was rendered with embedData. Send the raw PDF bytes as the request body with Content-Type: application/pdf.
curl -X POST https://api.formepdf.com/v1/extract \
  -H "Authorization: Bearer forme_sk_abc123..." \
  -H "Content-Type: application/pdf" \
  --data-binary @invoice.pdf
Response: 200 OK
{ "data": { "clientName": "Jane Smith", "items": [...] } }
Returns 404 if no embedded data is found.

Flatten Forms

POST /v1/render/:slug?flattenForms=true Render a PDF with all form fields flattened — interactive fields are converted to static content. Useful for filling a form template with data and sending a non-editable PDF. Pass flattenForms=true as a query parameter on any render endpoint (sync or async).

Metadata

Tag rendered documents with your own key-value pairs for organization and retrieval.

Adding metadata

Pass a metadata object in the render request body:
{
  "clientName": "Jane Smith",
  "items": [{ "description": "Consulting", "quantity": 10, "unitPrice": 150 }],
  "metadata": {
    "customerId": "cust_123",
    "department": "billing",
    "environment": "production"
  }
}
Metadata is merged into the document record alongside system fields (renderTimeMs, templateSlug).

Filtering by metadata

Query saved documents by metadata values:
curl "https://api.formepdf.com/v1/documents?metadata.customerId=cust_123" \
  -H "Authorization: Bearer forme_sk_abc123..."
Multiple filters are ANDed together (maximum 5 per request):
curl "https://api.formepdf.com/v1/documents?metadata.department=billing&metadata.environment=production" \
  -H "Authorization: Bearer forme_sk_abc123..."

Limits

ConstraintLimit
Keys per document20
Key length100 characters
Value typesstring or number
String value length500 characters
Metadata filters per query5

Resources

List and retrieve your templates, documents, redaction templates, and certificates programmatically. All resource endpoints return paginated responses:
{
  "data": [...],
  "total": 42,
  "limit": 20,
  "offset": 0,
  "hasMore": true
}
Pagination query params (all endpoints):
ParamDefaultDescription
limit20Results per page (1-100)
offset0Number of results to skip

List Templates

GET /v1/templates Returns your templates (without JSX source or sample data). Query params: search — filter by name or slug.
curl https://api.formepdf.com/v1/templates \
  -H "Authorization: Bearer forme_sk_abc123..."

# Search by name
curl "https://api.formepdf.com/v1/templates?search=invoice" \
  -H "Authorization: Bearer forme_sk_abc123..."
Response item:
{
  "id": "clxyz...",
  "name": "Invoice",
  "slug": "invoice",
  "documentType": "invoice",
  "createdAt": "2024-01-15T12:00:00.000Z",
  "updatedAt": "2024-01-20T15:30:00.000Z"
}

Get Template

GET /v1/templates/:slug
curl https://api.formepdf.com/v1/templates/invoice \
  -H "Authorization: Bearer forme_sk_abc123..."
Response: { "data": { ... } } with the same fields as the list item.

List Documents

GET /v1/documents Returns your rendered documents with metadata. Query params:
ParamDescription
sourceFilter by source: generated, uploaded, redacted, merged, certified
fromISO date — only documents created on or after
toISO date — only documents created on or before
metadata.*Filter by metadata values (max 5 filters). Example: metadata.customerId=cust_123
# All documents
curl https://api.formepdf.com/v1/documents \
  -H "Authorization: Bearer forme_sk_abc123..."

# Filter by source and date range
curl "https://api.formepdf.com/v1/documents?source=generated&from=2024-01-01&to=2024-01-31" \
  -H "Authorization: Bearer forme_sk_abc123..."

# Filter by metadata
curl "https://api.formepdf.com/v1/documents?metadata.customerId=cust_123" \
  -H "Authorization: Bearer forme_sk_abc123..."
Response item:
{
  "id": "clxyz...",
  "name": "invoice-2024-01-15",
  "source": "generated",
  "templateId": "clxyz...",
  "metadata": { "customerId": "cust_123" },
  "expiresAt": null,
  "createdAt": "2024-01-15T12:00:00.000Z",
  "updatedAt": "2024-01-15T12:00:00.000Z",
  "template": { "id": "clxyz...", "name": "Invoice", "slug": "invoice" }
}

Get Document

GET /v1/documents/:id Returns a single document with a presigned download URL (1-hour expiry).
curl https://api.formepdf.com/v1/documents/clxyz... \
  -H "Authorization: Bearer forme_sk_abc123..."
Response: { "data": { ...document, "downloadUrl": "https://..." } }

List Redaction Templates

GET /v1/redaction-templates Query params: search — filter by name or slug.
curl https://api.formepdf.com/v1/redaction-templates \
  -H "Authorization: Bearer forme_sk_abc123..."
Response item:
{
  "id": "clxyz...",
  "name": "HIPAA Patient Record",
  "slug": "hipaa-patient-record",
  "description": "Redacts SSN, DOB, and patient names",
  "patterns": [{ "pattern": "Patient:", "pattern_type": "Literal" }],
  "presets": ["ssn", "date-of-birth"],
  "createdAt": "2024-01-15T12:00:00.000Z",
  "updatedAt": "2024-01-20T15:30:00.000Z"
}

Get Redaction Template

GET /v1/redaction-templates/:slug
curl https://api.formepdf.com/v1/redaction-templates/hipaa-patient-record \
  -H "Authorization: Bearer forme_sk_abc123..."
Response: { "data": { ... } } with the same fields as the list item.

List Certificates

GET /v1/certificates Returns your uploaded signing certificates (without PEM data).
curl https://api.formepdf.com/v1/certificates \
  -H "Authorization: Bearer forme_sk_abc123..."
Response item:
{
  "id": "clxyz...",
  "name": "Production Signing Cert",
  "subject": "Acme Corp",
  "expiresAt": "2025-12-31T23:59:59.000Z",
  "createdAt": "2024-01-15T12:00:00.000Z"
}
Resource listing endpoints require the hosted API (Team plan or above). Self-hosted users should manage resources through their own database.

Rate Limits

  • 100 requests per minute per API key
  • Monthly render limits depend on your plan:
PlanMonthly rendersTemplates
Free5003
Pro5,000Unlimited
Team25,000Unlimited
Business100,000Unlimited
When you hit a rate limit, the API returns 429 Too Many Requests. When you exceed your monthly render quota, it returns 429 with a message to upgrade.

Error Format

All errors return JSON:
{ "error": "Human-readable error message" }
Common HTTP status codes:
  • 400 — Invalid request (missing fields, bad S3 config)
  • 401 — Missing or invalid API key
  • 404 — Template or resource not found
  • 429 — Rate limit or usage limit exceeded
  • 502 — S3 upload failed
  • 500 — Internal server error