Filling PDFs

Anvil allows you to fill templatized PDFs using JSON data you provide. You can fill them in two ways:

  1. With the /api/v1/fill REST endpoint. The endpoint will allow you to fill PDFs that do not require e-signatures.
  2. When creating signature packets in the Etch E-sign API. PDF filling is baked into the E-sign API calls.


First you will need an API key. You can find your API key on the Organization Settings -> API Settings page. We provide a node API client that wraps authentication.

For more information on generating an API key and handling API authentication, check out the API getting started article.

Postman Collection

Quickly see the PDF Filling endpoint in action with our PDF Filling Postman Collection.

Creating a PDF Template

Next you will need a PDF Template. A PDF template holds the configuration for a single PDF file. It defines the location of fields on PDF pages, and the type of each field (e.g. date, phone number, etc.). The API allows you to fill the template as many times as you need with different data each time.

On your dashboard, click the + to upload a new PDF Template.

Uploading a PDF Form

Once uploaded, Anvil attempts to find the fields for you. Use the field editor to draw new fields, change field types and names, or modify field locations. Picking the correct types makes filling the PDF easier; compound types (e.g. addresses, names, multiline fields) allow you to fill multiple fields with a single value, and many types (e.g. phone number, date, dollar, SSN) provide formatting for you.

Editing a PDF Template

When you finish editing fields, click over to the API Info tab. It shows everything you need to fill the template with the API, including an example payload to quickly get started.

Viewing PDF Template API Info

Filling a PDF Template

POST to your new PDF template's URL with the data you want to embed in the PDF. Anvil will respond with the raw binary data for the filled PDF.

// Optional - set the title encoded into the PDF document
"title": "Some Title",
// Optional - default is 10
"fontSize": 8,
// Optional - default is dark blue
"textColor": "#333333",
// Required - the data to actually fill the PDF
"data": {
"someName": "Bobby",
"someDate": "2018-10-31",
"anAddress": {
"street1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip": "94106"
// => Raw PDF bytes
  • Keys in data correspond to a field id in the PDF template.
  • Values in data must be in a format corresponding to their field type. See all field types for more info.
  • Any fields not specified or specified with a null or undefined will be ignored.
  • The response body will be raw binary data for the filled PDF.
  • Anvil does not save the data sent to this endpoint.
curl \
-H 'Content-Type: application/json' \
-d '{ "data": { "someKey": "some data" } }' \{pdfTemplateID}.pdf > test.pdf

Encrypting Data Payloads

You can encrypt the data sent in to fill the PDF. Setup an RSA keypair, encrypt the string value of the data property with your public key. You can use our node encryption library.

// Encrypt with your RSA key
"data": 'an encrypted JSON string'
// Other metadata
"title": "Some Title",
// => Raw PDF bytes

Using the Node Client

For convenience, we provide a node API client. It handles authentication, filling the PDF, and handling errors to help with debugging.

import fs from 'fs'
import Anvil from '@anvilco/anvil'
const pdfTemplateID = 'kA6Da9CuGqUtc6QiBDRR'
const apiKey = '7j2JuUWmN4fGjBxsCltWaybHOEy3UEtt'
const exampleData = {
"title": "My PDF Title",
"fontSize": 10,
"textColor": "#CC0000",
"data": {
"someFieldId": "Hello World!"
const anvilClient = new Anvil({ apiKey })
const {
} = await anvilClient.fillPDF(pdfTemplateID, exampleData)
console.log(statusCode) // => 200
// Data will be the filled PDF raw bytes
fs.writeFileSync('output.pdf', data, { encoding: null })

Handling Errors

Should you run into any errors, the response status will be >= 400, and the body will be a JSON payload.

// empty request body!
// => 400

Request Size Limit

At this time, the request body is limited to a maximum size of 100k (102,400 bytes). Exceeding the limit will result in a 413 - Request Too Large error. When rendering images, it is good practice to send them in as URLs.

Rate Limits

This API enforces separate rate limits for Development and Production API keys.

  • Production: 200 requests over 5 seconds
  • Development: 10 requests over 5 seconds

Each response will contain a few headers that keep track of usage:

HTTP Status: 200
X-RateLimit-Limit: 200
X-RateLimit-Remaining: 196
X-RateLimit-Reset: 148086765

When exceeded, the API will respond with a 429 status code plus a Retry-After header indicating how many seconds to wait:

HTTP Status: 429
Retry-After: 5

Field IDs

Each field on the PDF is referenced in the data with a unique ID. You can set your own IDs to make a PDF template easier to fill via the Human Readable ID field.

Human Readable ID

Your new id will be reflected in the API Info tab:

Human Readable ID API Info

Combining Fields

You may have a PDF that requires the same data to be placed in multiple fields on the PDF. For example, if someone's name was required to be placed at the bottom of each page.

You can combine fields by setting the Human Readable ID on each field to the same value. Then sending { name: "Robin Jones" } will fill any fields with the name human readable ID.

Field Types

Each field has a type (e.g. phone, date, address, etc.). When you pass data to fill a PDF, the data will be formatted based on the type's rules before being inserted onto the PDF.

For many types, formatting rules can be chosen in the PDF template editor (e.g. dates, dollar, number, etc.).

Specifying a field's format

Finding Field Types

The PDF template API Info tab shows field information for each field including the id, type, and anything else pertinent to filling the PDF.

Field type information on API info tab

All Field Types

This shows all field types and the format they need to be specified in the payload's data parameter.

// Strings
shortText: String
longText: String
email: String
ssn: String // in format '123121234'
ein: String // in format '921234567'
date: String // in format 'YYYY-MM-DD'
radioGroup: String // ID of the child field that should be selected
// Image fields accept a publicly accessible http URL of your image (max size:
// 10MB), or a 'data:' URL (max size of all data URLs in a payload: 100k). If an
// http image is too large or the URL is not a valid image, the image field will
// not be filled. The max request body size is 100k, requests with data URLs
// over 100k will respond with a 413 error code.
imageFile: String
// Bools
checkbox: Boolean
// Numbers
number: Number
dollar: Number
integer: Number
percent: Number // 0 to 100
// Complex types
// "Boxes Per Letter" in the UI. This will render each character
// in a separate field. e.g. [][][][] with 'Unicorn' input will
// render [U][n][i][c]
charList: String
// "Boxes Per Line" in the UI. It represents several boxes stacked
// into lines. Each newline or element in the array will be rendered
// into the subsequent box.
textWrap: String or Array of lines
// A fullName represents a group of boxes for
// firstName, middle initial, and lastName
fullName: Object {
firstName: 'Bobby',
mi: 'W',
lastName: 'Jones'
usAddress: Object {
street1: '123 Main St',
city: 'San Francisco',
state: 'CA',
zip: '94106',
phone: Object {
num: '555113333',
// Region information is optional, default is US. Currently
// supported regions are US, MX, CA, GB and any related regions
// using the +1 (US, CA, GU, etc.), +44 (GB, IM, GG, JE), or +52
// country codes.
// If `baseRegion` and `region` match, it will display the number
// as a local number, without country code information.
// e.g.
// { num: '4355345345', region: 'US', baseRegion: 'US' } => '(435) 534-5345'
// { num: '4355345345', region: 'US', baseRegion: 'MX' } => '+1 (435) 534-5345'
// { num: '4355345345', region: 'MX', baseRegion: 'MX' } => '435 534 5345'
// { num: '4355345345', region: 'MX', baseRegion: 'US' } => '+52 435 534 5345'
region: 'US',
baseRegion: 'US'

Get started today

Start filling, generating, and signing PDFs from your app. Every account comes with free access to the Developer API.