Ruby SDK

Last updated March 23, 2026

We provide a Ruby SDK to connect to PDFMonkey. This gem is the quickest way to use our API with Ruby.

GitHub: https://github.com/pdfmonkey/pdfmonkey-ruby

Installation

Add this line to your application’s Gemfile:

gem 'pdfmonkey'

And then execute:

$ bundle

Or install it yourself as:

$ gem install pdfmonkey

Usage

Setting up authentication

Using the default environment variable

PDFMonkey looks for the PDFMONKEY_PRIVATE_KEY environment variable. This variable should contain your private key obtained at https://dashboard.pdfmonkey.io/account.

PDFMONKEY_PRIVATE_KEY=j39ckj4…

Setting credentials manually

You can set up your credentials explicitly in your application:

Pdfmonkey.configure do |config|
  config.private_key = 'j39ckj4…'
end

Per-request credentials

Configuration is global. If you need per-request credentials (e.g. multi-tenant scenarios), use with_adapter:

config = Pdfmonkey::Configuration.new
config.private_key = 'tenant-specific-key'

adapter = Pdfmonkey::Adapter.new(config: config)

Pdfmonkey.with_adapter(adapter) do
  Pdfmonkey::Document.generate!(
    document_template_id: 'b13ebd75-…',
    payload: { name: 'John Doe' }
  )
end

All operations inside the block use the given adapter. Calls outside the block continue using the global configuration.

Documents

Synchronous generation

If you want to wait for a document’s generation before continuing with your workflow, use the generate! method. It requests a document generation and waits for it to succeed or fail before returning.

document = Pdfmonkey::Document.generate!(
  document_template_id: 'b13ebd75-d290-409b-9cac-8f597ae3e785',
  payload: { name: 'John Doe' }
)

document.status       # => 'success'
document.download_url # => 'https://…'

The download URL is temporary

The download URL of a document is only valid for 1 hour. Past this delay, reload the document to obtain a new one:

document.reload!

Asynchronous generation

PDFMonkey was created with an asynchronous workflow in mind. It provides webhooks to inform you of a document’s generation success or failure.

To leverage this behavior and continue working while your document is being generated, use the generate method:

document = Pdfmonkey::Document.generate(
  document_template_id: 'b13ebd75-d290-409b-9cac-8f597ae3e785',
  payload: { name: 'John Doe' }
)

document.status       # => 'pending'
document.download_url # => nil

If you have a webhook URL set up, it will be called with your document once the generation is complete. You can simulate it for testing with the following cURL command:

curl <url of your app> \
  -X POST \
  -H 'Content-Type: application/json' \
  -d '{
        "document": {
          "id": "76bebeb9-9eb1-481a-bc3c-faf43dc3ac81",
          "app_id": "d9ec8249-65ae-4d50-8aee-7c12c1f9683a",
          "created_at": "2020-01-02T03:04:05.000+01:00",
          "document_template_id": "f7fbe2b4-a57c-46ee-8422-5ae8cc37daac",
          "meta": "{\"_filename\":\"my-doc.pdf\"}",
          "output_type": "pdf",
          "status": "success",
          "updated_at": "2020-01-02T03:04:15.000+01:00",
          "xml_data": null,
          "payload": "{\"name\": \"John Doe\"}",
          "download_url": "https://example.com/76bebeb9-9eb1-481a-bc3c-faf43dc3ac81.pdf",
          "checksum": "ac0c2b6bcc77e2b01dc6ca6a9f656b2d",
          "failure_cause": null,
          "filename": "my-doc.pdf",
          "generation_logs": [],
          "preview_url": "https://preview.pdfmonkey.io/pdf/web/viewer.html?file=…",
          "public_share_link": "https://example.com/76bebeb9-9eb1-481a-bc3c-faf43dc3ac81.pdf"
        }
      }'

Draft documents

You can create a draft document that won’t be queued for generation.

This is useful for embedding a preview via preview_url before triggering generation. That’s what we do in the PDFMonkey dashboard to show you a preview of your document before generating it, using an iframe.
draft = Pdfmonkey::Document.create_draft(
  document_template_id: 'b13ebd75-d290-409b-9cac-8f597ae3e785',
  payload: { name: 'John Doe' }
)
draft.status      # => 'draft'
draft.preview_url # => 'https://…'

# When ready, trigger generation and wait for completion:
draft.generate!
draft.status       # => 'success'
draft.download_url # => 'https://…'

# Or trigger generation without waiting:
draft.generate
draft.status # => 'pending'

Attaching meta data

In addition to the document’s payload you can add meta data when generating a document. Pass the meta: keyword argument to the generate! and generate methods:

meta = {
  _filename: 'john-doe-contract.pdf',
  client_id: '123xxx123'
}

document = Pdfmonkey::Document.generate!(
  document_template_id: template_id,
  payload: payload,
  meta: meta
)
document.meta
# => '{"_filename":"john-doe-contract.pdf","client_id":"123xxx123"}'

document = Pdfmonkey::Document.generate(
  document_template_id: template_id,
  payload: payload,
  meta: meta
)
document.meta
# => '{"_filename":"john-doe-contract.pdf","client_id":"123xxx123"}'

Image generation

Image generation uses the same API flow as PDF generation. The template’s output_type attribute indicates whether it produces 'pdf' or 'image' output. Image-specific options are passed through the meta parameter:

doc = Pdfmonkey::Document.generate!(
  document_template_id: template_id,
  payload: payload,
  meta: {
    _type: 'png',       # webp (default), png, or jpg
    _width: 800,        # pixels
    _height: 600,       # pixels
    _quality: 80        # webp only, default 100
  }
)
doc.download_url # => URL to the generated image

Updating a document

document.update!(status: 'pending')

Listing documents

cards = Pdfmonkey::Document.list_cards(page: 1, status: 'success')

cards.each do |card|
  puts card.id
  puts card.status
end

cards.current_page # => 1
cards.total_pages  # => 5

# Navigate pages
next_cards = cards.next_page
prev_cards = cards.prev_page

You can filter by document_template_id:, status:, workspace_id:, and updated_since:.

Fetching a document

Prefer fetch_card over fetch

Fetching a full document includes its payload, meaning it could be large depending on the data you provided. We strongly recommend using only fetch_card unless you have a specific reason to fetch the full document.

You can fetch an existing document using .fetch (or its explicit alias .fetch_full):

document = Pdfmonkey::Document.fetch('76bebeb9-9eb1-481a-bc3c-faf43dc3ac81')

To fetch just the lightweight card representation (recommended):

card = Pdfmonkey::Document.fetch_card('76bebeb9-9eb1-481a-bc3c-faf43dc3ac81')

Deleting a document

You can delete an existing document using the .delete method:

Pdfmonkey::Document.delete('76bebeb9-9eb1-481a-bc3c-faf43dc3ac81')
#=> true

Alternatively you can call the #delete! method on a document instance:

document.delete!
#=> true

Error handling

API errors and network errors raise exceptions:

begin
  document = Pdfmonkey::Document.generate(
    document_template_id: template_id,
    payload: data
  )
rescue Pdfmonkey::ApiError => e
  e.message     # => "Document template must exist"
  e.errors      # => ["Document template must exist"]
  e.status_code # => 422
rescue Pdfmonkey::ConnectionError => e
  e.message # => "Failed to open TCP connection to api.pdfmonkey.io:443 ..."
end

When using generate!, an additional exception may be raised if the document’s status is 'error' or 'failure':

begin
  document = Pdfmonkey::Document.generate!(
    document_template_id: template_id,
    payload: data
  )
rescue Pdfmonkey::GenerationError => e
  e.message  # => "Document generation failed: Template error"
  e.document # => #<Pdfmonkey::Document …> (the failed document)
end

All exception classes inherit from Pdfmonkey::Error, so you can rescue broadly:

begin
  document = Pdfmonkey::Document.generate!(
    document_template_id: template_id,
    payload: data
  )
rescue Pdfmonkey::Error => e
  puts "Something went wrong: #{e.message}"
end

Templates

Fetching a template

Full templates can be large

Fetching a full template includes its body and settings, which can be large. Use list_cards when you only need metadata.
template = Pdfmonkey::Template.fetch('b13ebd75-d290-409b-9cac-8f597ae3e785')
template.identifier # => 'my-invoice'
template.body       # => '<h1>Invoice</h1>…'  (published version)
template.body_draft # => '<h1>Invoice v2</h1>…'  (draft version)

You can also use the explicit alias .fetch_full:

template = Pdfmonkey::Template.fetch_full('b13ebd75-d290-409b-9cac-8f597ae3e785')

Creating a template

When creating a template, attributes like body, scss_style, settings, sample_data, and pdf_engine_id are automatically written to their draft counterparts (body_draft, scss_style_draft, etc.):

template = Pdfmonkey::Template.create(
  identifier: 'my-invoice',
  body: '<h1>Invoice</h1>'
)
template.body_draft # => '<h1>Invoice</h1>'

Updating a template

Like create, update! writes to the draft fields:

template.update!(body: '<h1>Updated Invoice</h1>')
template.body_draft # => '<h1>Updated Invoice</h1>'

Publishing a template

Once you’re happy with the draft, publish it to copy all draft fields to their published counterparts:

template.publish!
template.body # => '<h1>Updated Invoice</h1>'

Listing templates

cards = Pdfmonkey::Template.list_cards(workspace_id: 'f4ab650c-…')

Deleting a template

Pdfmonkey::Template.delete('b13ebd75-…')
# or
template.delete!

Template Folders

# List folders
folders = Pdfmonkey::TemplateFolder.list

# Create a folder
folder = Pdfmonkey::TemplateFolder.create(identifier: 'invoices')

# Fetch a folder
folder = Pdfmonkey::TemplateFolder.fetch('folder-id')

# Update a folder
folder.update!(identifier: 'receipts')

# Delete a folder
Pdfmonkey::TemplateFolder.delete('folder-id')
# or
folder.delete!

To create a template inside a specific folder, pass the template_folder_id:

folder = Pdfmonkey::TemplateFolder.create(identifier: 'invoices')

template = Pdfmonkey::Template.create(
  identifier: 'monthly-invoice',
  body: '<h1>Invoice</h1>',
  template_folder_id: folder.id
)

Snippets

Snippets are reusable HTML components that can be included in templates.

# List snippets
snippets = Pdfmonkey::Snippet.list

# Create a snippet
snippet = Pdfmonkey::Snippet.create(
  identifier: 'header',
  code: '<div class="header">…</div>',
  workspace_id: 'f4ab650c-…'
)

# Fetch a snippet
snippet = Pdfmonkey::Snippet.fetch('snippet-id')

# Update a snippet
snippet.update!(code: '<div class="header">Updated</div>')

# Delete a snippet
Pdfmonkey::Snippet.delete('snippet-id')
# or
snippet.delete!

Workspaces

Workspaces are read-only resources. They can be listed and fetched but not created, updated, or deleted through the API.

# List workspaces
workspaces = Pdfmonkey::Workspace.list_cards

workspaces.each do |workspace|
  puts workspace.identifier
end

# Fetch a workspace
workspace = Pdfmonkey::Workspace.fetch('workspace-id')
workspace.identifier # => 'my-app'

Webhooks

Webhooks allow you to receive notifications when documents are generated.

# Create a webhook for all templates in a workspace
webhook = Pdfmonkey::Webhook.create(
  url: 'https://example.com/webhooks/pdfmonkey',
  event: 'document.generation.completed',
  workspace_id: 'f4ab650c-…'
)

# Optionally restrict to specific templates
webhook = Pdfmonkey::Webhook.create(
  url: 'https://example.com/webhooks/pdfmonkey',
  event: 'document.generation.completed',
  workspace_id: 'f4ab650c-…',
  document_template_ids: ['tpl-1', 'tpl-2']
)

# You can also specify a custom channel for routing
webhook = Pdfmonkey::Webhook.create(
  url: 'https://example.com/webhooks/pdfmonkey',
  event: 'document.generation.completed',
  workspace_id: 'f4ab650c-…',
  custom_channel: 'invoices'
)

# Delete a webhook
Pdfmonkey::Webhook.delete('webhook-id')
# or
webhook.delete!

Engines

List available PDF rendering engines:

engines = Pdfmonkey::Engine.list

engines.each do |engine|
  puts "#{engine.name} (deprecated: #{engine.deprecated_on || 'no'})"
end

You can use an engine when creating a template:

engines = Pdfmonkey::Engine.list
v4 = engines.find { |e| e.name == 'v4' }

template = Pdfmonkey::Template.create(
  identifier: 'my-template',
  body: '<h1>Hello</h1>',
  pdf_engine_id: v4.id
)

Current User

Retrieve information about the authenticated user:

user = Pdfmonkey::CurrentUser.fetch

user.email               # => 'user@example.com'
user.current_plan        # => 'pro'
user.available_documents # => 1000

Pagination

All list methods return Pdfmonkey::Collection objects that support pagination:

collection = Pdfmonkey::Document.list_cards(page: 1)

collection.current_page     # => 1
collection.total_pages      # => 5
collection.next_page_number # => 2
collection.prev_page_number # => nil

# Navigate to next/previous pages
next_page = collection.next_page  # => Collection or nil
prev_page = collection.prev_page  # => Collection or nil
Collections are Enumerable. Methods like .each, .map, and .select operate on the items of the current page only – they do not automatically fetch subsequent pages.
# These all act on the current page's items
collection.each { |item| puts item.id }
collection.map(&:status)
collection.select { |item| item.status == 'success' }

# To process all pages, navigate manually
page = Pdfmonkey::Template.list_cards(page: 1)
while page
  page.each { |item| process(item) }
  page = page.next_page
end

Serialization

All resources support to_json and to_h:

document.to_json # => '{"document":{"id":"…","status":"success",…}}'
document.to_h    # => {id: "…", status: "success", errors: nil, …}
to_json wraps attributes under the resource’s API member key, omits nil values, and strips the errors attribute (it is intended for API requests). Use to_h when you need the full attribute hash for logging or caching.

Frequently asked questions

How do I install the PDFMonkey Ruby SDK?
Add gem ‘pdfmonkey’ to your Gemfile and run bundle install, or run gem install pdfmonkey directly. Set your API key via the PDFMONKEY_PRIVATE_KEY environment variable or configure it in an initializer.
What is the difference between generate! and generate in the PDFMonkey Ruby gem?
generate! is synchronous—it blocks until the document reaches success or failure, then returns the document with a download URL. generate is asynchronous—it returns immediately with a pending status, and you handle the result via webhooks.
Can I use per-request API keys with the PDFMonkey Ruby SDK?
Yes. Create a Pdfmonkey::Configuration with a tenant-specific key, wrap it in an Adapter, and pass it to Pdfmonkey.with_adapter. All operations inside the block use that adapter while calls outside continue using the global configuration.
How do I handle errors in the PDFMonkey Ruby SDK?
Rescue Pdfmonkey::ApiError for API errors (includes status_code and errors), Pdfmonkey::ConnectionError for network issues, or Pdfmonkey::GenerationError when using generate! and the document fails. All inherit from Pdfmonkey::Error for broad rescuing.