Generate a PDF in Ruby on Rails
The simplest way to generate a PDF in Ruby on Rails is to call the Docweave API from a controller or a background job with Net::HTTP or Faraday: POST your rendered view HTML to /api/v1/pdf and write the returned bytes to disk — no headless browser to install or run yourself.
From a controller, using Net::HTTP
Render the invoice view to a string with render_to_string, POST it to Docweave, then stream the PDF straight back to the browser with send_data:
# app/controllers/invoices_controller.rb
require "net/http"
require "json"
class InvoicesController < ApplicationController
DOCWEAVE_URL = URI("https://docweave.dev/api/v1/pdf")
def download_pdf
invoice = Invoice.find(params[:id])
html = render_to_string(
template: "invoices/show",
layout: "pdf",
locals: { invoice: invoice }
)
request = Net::HTTP::Post.new(DOCWEAVE_URL)
request["Authorization"] = "Bearer #{Rails.application.credentials.docweave_api_key}"
request["Content-Type"] = "application/json"
request["Accept"] = "application/pdf"
request.body = {
source: { type: "html", html: html },
options: { format: "A4" },
idempotencyKey: "invoice-#{invoice.id}"
}.to_json
response = Net::HTTP.start(DOCWEAVE_URL.host, DOCWEAVE_URL.port, use_ssl: true) do |http|
http.request(request)
end
unless response.is_a?(Net::HTTPSuccess)
raise "Docweave render failed: #{response.code} #{response.body}"
end
send_data response.body,
filename: "invoice-#{invoice.id}.pdf",
type: "application/pdf",
disposition: "attachment"
end
endFrom a background job, using Faraday
For larger documents or scheduled generation, do the same render-and-post inside an ActiveJob, then write the PDF to disk (or hand it off to Active Storage):
# app/jobs/generate_invoice_pdf_job.rb
require "faraday"
class GenerateInvoicePdfJob < ApplicationJob
queue_as :pdfs
retry_on StandardError, wait: :polynomially_longer, attempts: 3
def perform(invoice_id)
invoice = Invoice.find(invoice_id)
html = ApplicationController.render(
template: "invoices/show",
layout: "pdf",
assigns: { invoice: invoice }
)
response = Faraday.post("https://docweave.dev/api/v1/pdf") do |req|
req.headers["Authorization"] = "Bearer #{Rails.application.credentials.docweave_api_key}"
req.headers["Content-Type"] = "application/json"
req.headers["Accept"] = "application/pdf"
req.body = {
source: { type: "html", html: html },
options: { format: "A4" },
idempotencyKey: "invoice-#{invoice.id}"
}.to_json
end
raise "Docweave render failed: #{response.status}" unless response.success?
pdf_path = Rails.root.join("tmp", "pdfs", "invoice-#{invoice.id}.pdf")
FileUtils.mkdir_p(pdf_path.dirname)
File.binwrite(pdf_path, response.body)
end
end…or from a Ruby AI agent, via MCP
If your agent runs in Ruby, or your workflow calls tools over MCP from any language, the Docweave MCP server exposes a generate_pdf tool — the same underlying render, no HTTP boilerplate to write:
# From a Ruby-based AI agent, over MCP — no HTTP plumbing:
generate_pdf({
"source" => {
"type" => "html",
"html" => "<h1>Invoice INV-042</h1><p>Total due: $1,280.00</p>"
},
"outputPath" => "/tmp/invoice-042.pdf"
})
# Any MCP client (Claude, an agent framework, a custom Ruby MCP client)
# can call this tool once the server is registered — start it with:
# npx @docweave/mcpFAQ
What's the simplest way to generate a PDF in Ruby on Rails?
Render your view to a string with render_to_string (in a controller) or ApplicationController.render (in a job), then POST that HTML to /api/v1/pdf with an Authorization: Bearer header using Net::HTTP or Faraday, and write the response to disk or stream it with send_data. There's no headless browser to install, configure, or keep patched inside your Rails app.
Do I need wicked_pdf, Grover, or Prawn in a Rails app to generate PDFs?
No. wicked_pdf and Grover wrap a local wkhtmltopdf or headless Chrome binary you have to install and maintain in your deploy environment, and Prawn builds PDFs programmatically rather than from HTML/CSS. Calling the Docweave API from a controller or job only needs an HTTP client Rails already has — Net::HTTP or Faraday — so the rendering engine lives outside your app.
Can I generate a PDF from a live Rails route instead of rendering HTML myself?
Yes, if the page is publicly reachable: set source to { "type" => "url", "url" => "https://yourapp.com/invoices/42" } instead of rendering HTML. For pages behind authentication, render_to_string or ApplicationController.render is the safer path since Docweave never needs your session or login flow.
How do I avoid double-billing if an ActiveJob retries a failed PDF request?
Pass an idempotencyKey in the request body, such as invoice-#{invoice.id}. If retry_on re-runs the job after a timeout or dropped connection, a repeat request with the same key returns the original PDF instead of rendering — and billing — again. Rendering is billed per document, not per page.
Ready to generate PDFs from Rails in production?
Get an API key