Templates
Save reusable PDF layouts as templates, then generate documents by passing JSON data. pdfRelay supports three template types, each suited to different workflows.
Template types
React
RecommendedWrite JSX components that receive your data as props. The most powerful option — supports everything.
Best for: invoices, reports, proposals, any document with dynamic content.
- + Loops, conditionals, computed values
- + Component composition & reuse
- + Dynamic data binding via props
- + Full CSS & inline styles
- + Sign fields (
<sign-field>) - + Data fields (
<data-field>) - + Live preview in dashboard editor
Visual Editor
WYSIWYG editor — format text, add tables, and insert fields without writing code.
Best for: simple contracts, letters, NDAs, forms.
- + Rich text, headings, lists
- + Tables & images
- + Sign fields (toolbar insert)
- + Data fields (merge fields)
- − No loops or conditionals
- − Limited layout control
PDF Upload
Upload an existing PDF and place sign fields on it. The document content is fixed — only the fields change per request.
Best for: standard contracts, agreements, legal documents.
- + Use any existing PDF
- + Drag-and-drop field placement
- + Reusable across different signers
- + Sign fields with role mapping
- − No dynamic content
- − No data binding
React templates
React templates are the most powerful option. Write JSX components that receive your data as props, and pdfRelay renders them to PDF.
Creating via API
Create a React template by sending a POST request with the JSX source, a unique name, and the target environment:
POST https://pdfrelay.com/api/v1/templates
Authorization: Bearer sk_live_...
Content-Type: application/json
{
"name": "monthly-invoice",
"description": "Standard monthly invoice with line items",
"environment": "live",
"jsx": "export default function Invoice({ company, items, total }) {\n return (\n <div style={{ fontFamily: 'sans-serif', padding: 40 }}>\n <h1>{company.name}</h1>\n <table>\n <tbody>\n {items.map((item, i) => (\n <tr key={i}>\n <td>{item.description}</td>\n <td>${item.price.toFixed(2)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n <p>Total: ${total.toFixed(2)}</p>\n </div>\n );\n}",
"testData": "{\n \"company\": { \"name\": \"Acme Corp\" },\n \"items\": [{ \"description\": \"Web Design\", \"price\": 2500 }],\n \"total\": 2500\n}"
}| Field | Required | Description |
|---|---|---|
name | Yes | Unique name used to reference the template in API calls |
jsx | Yes | JSX source code with an export default function component |
environment | Yes | "test" or "live" — test templates only work with test API keys |
description | No | Human-readable description |
testData | No | JSON string of sample data for the dashboard preview |
Response includes the created template with its id and version (starts at 1). Update a template with PUT /v1/templates/:id — the version auto-increments on each update.
Templates can also be created and edited in the dashboard template editor, which provides a live preview, test data editing, and sign field insertion.
Writing templates
- 1Must have a
export defaultfunction component - 2Props come from the
dataobject you pass at render time - 3You can define helper components and functions in the same file
- 4Use inline styles as objects — all CSS properties are supported
Sign fields in React templates
Add sign fields directly in your JSX using the <sign-field> element. Fields flow naturally with document content — no manual coordinate positioning.
<sign-field type="signature" signer="client" required>
Sign here
</sign-field>
<sign-field type="date" signer="client" required>Date</sign-field>
<sign-field type="checkbox" signer="client" label="I agree to the terms">
check
</sign-field>Supported field types
| Type | Description | Default size |
|---|---|---|
signature | Drawn or typed signature | 200 x 40 pt |
initials | Drawn or typed initials | 60 x 30 pt |
name | Full name text input | 150 x 30 pt |
date | Date (auto-filled with current date) | 150 x 30 pt |
email | Email address | 150 x 30 pt |
text | Freeform text input | 150 x 30 pt |
checkbox | Checkbox toggle | 16 x 16 pt |
Field attributes
| Attribute | Required | Description |
|---|---|---|
type | Yes | Field type (signature, initials, name, date, etc.) |
signer | Yes | Signer role identifier (e.g., "client", "witness") |
required | No | If present, field must be filled to complete signing |
label | No | Display label shown to the signer |
placeholder | No | Placeholder text for text inputs |
Data fields (merge fields)
In React templates, data binding happens naturally via props — just use {props.fieldName}. In the visual editor, use the Data Field toolbar button to insert merge fields like {{client.name}} that are replaced with real data at render time.
Rendering a template
Pass the template name and your data as JSON:
POST https://pdfrelay.com/api/v1/render
{
"template": "monthly-invoice",
"data": {
"company": { "name": "Acme Corp" },
"client": { "name": "Jane Smith" },
"items": [
{ "description": "Web Design", "price": 2500 },
{ "description": "Hosting", "price": 199.99 }
],
"total": 2699.99
}
}PDF templates via API
PDF templates are typically created in the dashboard using the drag-and-drop field editor. However, you can also create them programmatically by first uploading a PDF, then creating a template with a field manifest:
Step 1: Upload the PDF
POST https://pdfrelay.com/api/documents/upload
Authorization: Bearer sk_live_...
Content-Type: multipart/form-data
file: (your PDF file)
// Response:
{
"id": "doc_abc123",
"name": "Standard NDA",
"page_count": 3,
"download_url": "/api/v1/documents/doc_abc123/download?token=d_xyz"
}Step 2: Create the template with field positions
POST https://pdfrelay.com/api/templates/pdf
Authorization: Bearer sk_live_...
Content-Type: application/json
{
"name": "standard-nda",
"description": "Company NDA with two signers",
"document_id": "doc_abc123",
"environment": "live",
"field_manifest": [
{
"signer": "client",
"field_type": "signature",
"required": true,
"label": "Client Signature",
"page": 3,
"x": 72,
"y": 500,
"width": 200,
"height": 40
},
{
"signer": "client",
"field_type": "date",
"required": true,
"label": null,
"page": 3,
"x": 72,
"y": 550,
"width": 120,
"height": 28
},
{
"signer": "witness",
"field_type": "signature",
"required": true,
"label": "Witness Signature",
"page": 3,
"x": 350,
"y": 500,
"width": 200,
"height": 40
}
]
}Field positions are in PDF points (72 points = 1 inch) from the top-left corner of the page. Page numbers are 1-based.
Signing with templates
Any template type can be used for signing. Pass the template name and signers to create a signing request:
POST https://pdfrelay.com/api/v1/sign
{
"template": "standard-nda",
"data": { "company": "Acme Corp", "effective_date": "April 1, 2026" },
"signers": [
{ "role": "client", "name": "Jane Smith", "email": "jane@example.com" },
{ "role": "witness", "name": "John Doe", "email": "john@example.com" }
]
}For PDF upload templates, the data field is not needed — the document content is fixed. Only signers is required, mapping roles to actual people.
Versioning
Every time you update a template, the version number increments automatically. The render response tells you which version was used. You can update templates at any time without redeploying your application.
API endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/templates | List all templates |
| POST | /v1/templates | Create a React/HTML template |
| GET | /v1/templates/:id | Get a template by ID |
| PUT | /v1/templates/:id | Update a template (bumps version) |
| DELETE | /v1/templates/:id | Deactivate a template |
| POST | /v1/render | Render a template to PDF |
| POST | /v1/sign | Create a signing request from a template |
Environments
Each template has an environment ("test" or "live"). Test templates can only be rendered with test API keys, and live templates with live keys. This lets you safely iterate on layouts without affecting production.