Generating PDFs

Anvil allows you to dynamically generate new PDFs using JSON data you provide via the /api/v1/generate-pdf REST endpoint. You can generate PDFs from your own HTML and CSS, or structured Markdown.

PDF generation is useful for agreements, invoices, disclosures, or any other text-heavy documents.

Generate Invoice PDF Example

Features

  • Number of pages are dynamically generated to fit your content
  • Documents are generated with customizable page width, height, and margin, with 8.5" x 11" pages as the default
  • Convert your HTML and CSS into PDFs
  • Supports Markdown formatting, including tables, images, headings, etc.

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 Generation endpoint in action with our PDF Generation Postman Collection.

Generating a PDF

POST to /api/v1/generate-pdf 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/generate-pdf
{
// Optional - set the type of generation
// Accepts `html` or `markdown` (default)
"type": "markdown",
// Optional - set the title encoded into the PDF document
"title": "Example Invoice",
// Required - the data to actually fill the PDF
// `data` accepts either HTML/CSS or Markdown in a structured JSON format
"data": ...
// Optional - page settings
// width - width of the page in pixels
// height - height of the page in pixels
// margin - margin between the page border and content in pixels
// margin* - margin on a specific side
// pageCount - display a page counter at location
// 'topLeft', 'topCenter', 'topRight'
// 'bottomLeft', 'bottomCenter', 'bottomRight'
"page": {
"width": "612px",
"height": "792px",
"margin": "50px",
"marginTop": "30px",
"marginBottom": "30px",
"marginLeft": "60px",
"marginRight": "60px",
"pageCount": "bottomCenter",
},
// The following options are only used by `type: "markdown"` generation
// Optional - set the font size / text color of the PDF
"fontSize": 16,
"textColor": "#171717",
// Optional - show or hide the timestamp shown at the bottom of the PDF
// defaults to true
"includeTimestamp": true,
// Optional - display a logo on the right corner of the first page
"logo": {
"src": "https://example.com/mtnlogo.png",
"maxWidth": 200,
"maxHeight": 200,
},
}
// => Raw PDF bytes

There are two types of data accepted by our PDF generation API: HTML / CSS and Markdown. Both approaches are covered in the next section.

HTML & CSS to PDF

The PDF generation API will create the PDF with data in HTML & CSS format. Please make sure to set the type property to "html":

{
...,
// Required to set to use HTML & CSS as input
"type": "html",
"data": {
"html": `
<h1 class='header-one'>What is Lorem Ipsum?</h1>
<p>
Lorem Ipsum is simply dummy text of the printing and typesetting
industry. Lorem Ipsum has been the industry's standard dummy text
ever since the <strong>1500s</strong>, when an unknown printer took
a galley of type and scrambled it to make a type specimen book.
</p>
<h3 class='header-two'>Where does it come from?</h3>
<p>
Contrary to popular belief, Lorem Ipsum is not simply random text.
It has roots in a piece of classical Latin literature from
<i>45 BC</i>, making it over <strong>2000</strong> years old.
</p>
`,
"css": `
body { font-size: 14px; color: #171717; }
.header-one { text-decoration: underline; }
.header-two { font-style: underline; }
`,
},
}

An example with curl:

curl \
-X POST \
-u YOUR_API_KEY: \
-H 'Content-Type: application/json' \
-d '{ "type": "html", "title": "World", "data": { "html": "<h1>HTML to PDF</h1>", "css": "h1 { color: purple; }" } }' \
https://app.useanvil.com/api/v1/generate-pdf > test.pdf

Note: The logo, includeTimestamp, fontSize, and textColor payload properties are ignored when generating a PDF using HTML & CSS.

Supported Format of data

data is an object consisting of the html and css properties.

{
title: '...',
data: {
html: '<p>some html</p>',
css: 'p { color: blue; }',
},
}
html

Accepts HTML in string format.

{
data: {
html: "<h1 id='hello' class='title' style='color:grey;'>Hello World!</h1><p>Lots of paperwork</p>",
},
}
css

Accepts CSS in string format.

{
data: {
css: "body { font-size: 14px; } #hello { color: #00ff77; } .title { font-weight: bold; } p { margin-bottom: 0px; }",
},
}

Note: CSS Flexbox and Grid are currently not supported.

Markdown to PDF

The PDF generation API will create the PDF with an array of objects, each supporting Markdown.

{
...,
"data": [
{
"label": "Name",
"content": "Sally Smith",
},
{
"content": "Lorem **ipsum** dolor sit _amet_...",
"fontSize": 12,
"textColor": "#616161",
},
{
"table": {
"rows": [
["Description", "Quantity", "Price"],
["4x Large Wigets", "4", "$40.00"],
["10x Medium Sized Widgets in dark blue", "10", "$100.00"],
["6x Small Widgets in white", "6", "$60.00"],
],
"columnOptions": [
{ "align": "left", "width": "60%" },
{ "align": "center", "width": "100px" },
{ "align": "right" },
],
"firstRowHeaders": true,
"rowGridlines": true,
"columnGridlines": true,
"verticalAlign": "center",
},
},
],
}

An example with curl:

curl \
-X POST \
-u YOUR_API_KEY: \
-H 'Content-Type: application/json' \
-d '{ "title": "Hello", "data": [ { "label": "Hello World", "content": "I like turtles" } ] }' \
https://app.useanvil.com/api/v1/generate-pdf > test.pdf

Supported Format of data

data is specified as an array of objects. The objects have a handful of supported keys. Objects can contain multiple keys.

{
title: '...',
data: [
{
label: 'A label',
content: 'Some content',
fontSize: 12,
textColor: '#171717',
},
// ... more data to output
]
}
label

Generates a bolded label

{
data: [{
label: 'A label',
}]
}
heading

Generates a larger bolded heading intended to break up sections of a document.

{
data: [{
heading: 'Some Heading',
}]
}
content

Generates a block of text. Supports multiline text and markdown formatting, including headings, bold, italic, bullets, tables, blockquotes, links, images, etc.

{
data: [{
content: 'Some content\n\nAnother line of content with [a link](https://google.com)',
}]
}
table

While the content key supports markdown tables, they can be difficult to generate, so we provide a specific table key.

{
data: [{
table: {
// (required) The data!
rows: [
['Description', 'Quantity', 'Price']
['A widget', '3', '$3']
['Some other widget', '10', '$10']
],
// (optional) firstRowHeaders defaults to true
// set to false for no header row on the table
firstRowHeaders: true,
// (optional) rowGridlines / columnGridlines defaults to false
// set to true to display gridlines in-between rows or columns
rowGridlines: true,
columnGridlines: false,
// (optional) verticalAlign defaults to 'top'
// adjust vertical alignment of table text
// accepts 'top', 'center', or 'bottom'
verticalAlign: 'center',
// (optional) columnOptions - An array of columnOption objects.
// You do not need to specify all columns. Accepts an
// empty object indicating no overrides on the
// specified column.
//
// Supported keys for columnOption:
// align (optional) - adjust horizontal alginment of table text
// accepts 'left', 'center', or 'right'; defaults to 'left'
// width (optional) - adjust the width of the column
// accepts width in pixels or as percentage of the table width
columnOptions: [
{ align: 'left' }, // the default
{ align: 'center', width: '100px' },
{ align: 'right', width: '20%' },
],
}
}]
}
fontSize / textColor

Applies the specified font size or text color to that section of the PDF.

data: [{
content: 'Lorem ipsum',
fontSize: 20, // must be an int within 5 to 30
textColor: '#006ec2', // must be a 6 digit hex code
}]

By default, the Anvil logo will be displayed on the right corner of the first page.

To remove the logo, set the logo property to false.

You can also set your own logo:

pdf-logo-example

{
logo: {
// (required) Supports http(s) and data URIs
src: 'https://example.com/mtnlogo.png'
// (optional) The max size to display the logo
// * Default is 200px
// * Maximum is 500px
maxWidth: 200,
maxHeight: 200,
},
title: 'Logo Example',
data: [{
content: 'Hello _World_!'
}]
}

Using the Node Client

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

import fs from 'fs'
import Anvil from '@anvilco/anvil'
const apiKey = '7j2JuUWmN4fGjBxsCltWaybHOEy3UEtt'
const exampleData = {
"title": "My PDF Title",
"data": [{
"label": "Hello World!"
}]
}
const anvilClient = new Anvil({ apiKey })
const {
statusCode,
data
} = await anvilClient.generatePDF(exampleData)
console.log(statusCode) // => 200
// Data will be the filled PDF raw bytes
fs.writeFileSync('output.pdf', data, { encoding: null })

Encrypting Data Payloads

You can encrypt the data sent in to fill the PDF. Setup an RSA keypair, then 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/generate-pdf
{
// Encrypt with your RSA key
"data": 'an encrypted JSON string'
// Other metadata
"title": "Some Title",
}
// => Raw PDF bytes

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/generate-pdf
{
// empty request body!
}
// => 400
{
"name":"ValidationError",
"fields":[{
"message":"Required",
"property":"data"
}]
}

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

Get started today

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