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.

Authentication

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.

POST https://app.useanvil.com/api/v1/fill/{pdfTemplateID}.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 \
-X POST \
-u YOUR_API_KEY: \
-H 'Content-Type: application/json' \
-d '{ "data": { "someKey": "some data" } }' \
https://app.useanvil.com/api/v1/fill/{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.

POST https://app.useanvil.com/api/v1/fill/{pdfTemplateID}.pdf
{
// 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 {
statusCode,
data
} = 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.

POST https://app.useanvil.com/api/v1/fill/{pdfTemplateID}.pdf
{
// empty request body!
}
// => 400
{
"name":"ValidationError",
"fields":[{
"message":"Required",
"property":"data"
}]
}

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 Field Alias field.

Field Alias

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

Field Alias 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 Field Alias on each field to the same value. Then sending { name: "Robin Jones" } will fill any fields with the name field alias.

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'
}

Repeating PDF pages

You can repeat pages within your PDF template. Say you have a 3 page PDF with an invoice page as the 2nd page. You can repeat this page any number of times and end up with a 4+ page PDF.

Check out our PDF page repeating tutorial to get up to speed with an example invoice.

There are 2 ways to repeat pages in your template:

  1. Overflow items: you have a PDF with a page that has room for a static n rows, but you have n + m items to display. An example is an invoice with a static number of line items.
  2. Explicit repeat: you want to repeat a page with totally different data for each repeat. An example is if you have a consent page in your PDF that needs to be repeated for n people.

These two operations can be combined to both overflow and explicitly repeat in the same fill call.

To discuss both of these operations, we'll use an invoice example.

invoice pdf template

Overflow items

Our invoice example shows only 6 rows for line items. The goal of overflow repeating in our invoice example is to render more than 6 items. When we fill the PDF with more than 6 items, we want the page to repeat so all items are rendered on a page.

A little setup is required to make overflowing work. For each column in your table, you want to wrap all column fields in an array. Click one of the fields in your column, click the Advanced tab, create an array, then add all the other column items to the array. All fields in an array must have the same type. Here's an example with our amount column.

array field created

Now, do the same with any other columns on the PDF. You can give each of your columns a Field Alias to make filling easier.

Once you have all your columns set up, you will be able to send a fill request with an array of data. If the array of data is longer than the number of fields, the page will repeat.

For example, this request will produce only 1 invoice page:

POST https://app.useanvil.com/api/v1/fill/{pdfTemplateID}.pdf
{
"title": "My Invoice",
"data": {
"clientName": "Acme Co.",
"invoiceId": "#1234",
"amounts": [
1.11,
2.22,
3.33,
4.44,
5.55,
6.66,
]
}
}

Add one more item to the amounts array, and the page will repeat:

POST https://app.useanvil.com/api/v1/fill/{pdfTemplateID}.pdf
{
"title": "My Invoice",
"data": {
"clientName": "Acme Co.",
"invoiceId": "#1234",
"amounts": [
1.11,
2.22,
3.33,
4.44,
5.55,
6.66,
7.77, // will be in the first amount row on the 2nd page
]
}
}

You can fill all your columns in this way in the same payload. It will repeat pages based on the longest array:

POST https://app.useanvil.com/api/v1/fill/{pdfTemplateID}.pdf
{
"title": "My Invoice",
"data": {
"clientName": "Acme Co.",
"invoiceId": "#1234",
"amounts": [
1.11,
2.22,
3.33,
4.44,
5.55,
6.66,
7.77, // will be in the first amount row on the 2nd page
]
"quantities": [
1,
2,
3,
4,
5,
6,
7, // will be in the first quantity row on the 2nd page
]
}
}

Explicit repeat

With and explicit repeat, you can repeat a page n times with different payloads. In our invoice case, you could repeat the invoice multiple times for different invoice numbers for the same client.

The explicit repeat case requires no special field setup. All you need to do is send array data to non-array fields.

Say Acme Co. had 2 invoices with different items and totals. We just pass those values in as an array. The following code will result in two separate invoice pages: one for invoice #1111 for $100, and one for invoice #2222 for $200.

POST https://app.useanvil.com/api/v1/fill/{pdfTemplateID}.pdf
{
"title": "My Invoice",
"data": {
// Since clientName is not an array, it will be applied to all pages.
"clientName": "Acme Co.",
"invoiceId": ["#1111", '#2222'],
"totalAmount": [100, 200],
// The line items info is covered in the next section...
}
}

Combining overflow and explicit repeats

Extending our multiple invoice scenario above, we'd want to separate the line items out for each invoice. To do this, we'd pass in an array of arrays for each overflow column. It will repeat the page for each item in the top level arrays, then use the 2nd level arrays to do the overflow repeating.

For example, the following payload will create a 3 page PDF.

  • Page 1: Invoice #1111, Amounts 1.11 - 1.66
  • Page 2: Invoice #1111, Amounts 1.77
  • Page 3: Invoice #2222, Amounts 2.11-2.33
POST https://app.useanvil.com/api/v1/fill/{pdfTemplateID}.pdf
{
"title": "My Invoice",
"data": {
// Since clientName is not an array, it will be applied to all pages.
"clientName": "Acme Co.",
"invoiceId": ["#1111", '#2222'],
"totalAmount": [100, 200],
"amounts": [
[
// Invoice #1111 data
1.11,
1.22,
1.33,
1.44,
1.55,
1.66,
1.77, // renders on page 2 of invoice #1111
],
[
// Invoice #2222 data: page 3
2.11,
2.22,
2.33,
]
]
"quantities": [
// ... 2 arrays here
]
}
}

Filling page numbers

Dynamic page numbers can be injected into the document by way of template strings sent to any Short Text or Long Text field. This is especially useful when using the page repeating features described above.

Create a field, and make sure it's a text type (not a numeric type!). The default Short Text type is sufficient. Then give it a good Field Alias so it's easy to fill.

pdf page field

Pass the template strings into the fill payload:

POST https://app.useanvil.com/api/v1/fill/{pdfTemplateID}.pdf
{
"title": "Page number test",
"textColor": "#CC0000",
"data": {
"pageNumber": "{{pageNumber}}",
"totalPages": "{{totalPages}}",
// You can use multiple variables a string
"allPageInformation": "Page {{pageNumber}} of {{totalPages}}",
}
}

And the page numbers are rendered in the resulting PDF:

pdf page numbers

Template strings can be used in any Short Text or Long Text field. Several page-related variables are supported in template strings:

  • pageNumber - Page number within the PDF
  • pageIndex - 0-based pageNumber, i.e. pageNumber - 1
  • totalPages - Total number of pages in the PDF
  • pageRepeatNumber - Page number within this repeat loop. It will be 1 if there is no repeating
  • pageRepeatIndex - 0-based pageRepeatNumber
  • pageRepeatTotal - Total number of pages within this repeat loop

Get started today

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