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)
save — boolean, default true. Every render is auto-saved to your Documents with source: "generated". Set false to skip saving.
saveName — string, optional custom document name. Default: {slug}-{YYYY-MM-DD} (e.g. invoice-2026-04-03).
metadata — object, 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:
| Field | Required | Description |
|---|
bucket | Yes | S3 bucket name |
key | Yes | Object key (path) in the bucket |
accessKeyId | Yes | AWS access key ID |
secretAccessKey | Yes | AWS 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).
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.
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).
Tag rendered documents with your own key-value pairs for organization and retrieval.
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).
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
| Constraint | Limit |
|---|
| Keys per document | 20 |
| Key length | 100 characters |
| Value types | string or number |
| String value length | 500 characters |
| Metadata filters per query | 5 |
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):
| Param | Default | Description |
|---|
limit | 20 | Results per page (1-100) |
offset | 0 | Number 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:
| Param | Description |
|---|
source | Filter by source: generated, uploaded, redacted, merged, certified |
from | ISO date — only documents created on or after |
to | ISO 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:
| Plan | Monthly renders | Templates |
|---|
| Free | 500 | 3 |
| Pro | 5,000 | Unlimited |
| Team | 25,000 | Unlimited |
| Business | 100,000 | Unlimited |
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.
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