Skip to content

PDF

Generate PDF documents from HTML templates. Uses Go's text/template syntax for dynamic content and pure Go rendering (no external binaries required).

Configuration

connector "pdf" {
  type         = "pdf"
  template     = "./templates/invoice.html"
  page_size    = "A4"
  font         = "Helvetica"
  margin_left  = 15
  margin_top   = 15
  margin_right = 15
  output_dir   = "./pdfs"
}
Option Type Default Description
template string Default HTML template file path (can be overridden per-request via payload)
page_size string "A4" Page size: A4, Letter, Legal
font string "Helvetica" Default font family
margin_left number 15 Left margin in mm
margin_top number 15 Top margin in mm
margin_right number 15 Right margin in mm
output_dir string "." Default output directory for save operation

Operations

Operation Description Output
generate Returns PDF as base64 binary (for HTTP responses) _binary, _content_type, _filename, size
save Saves PDF to file system file_path, filename, size

generate (default)

Returns the PDF as a binary HTTP response. The REST connector automatically detects the _binary and _content_type fields and serves the raw PDF with appropriate headers (Content-Type: application/pdf, Content-Disposition: attachment).

connector "invoice_pdf" {
  type      = "pdf"
  template  = "./templates/invoice.html"
  page_size = "A4"
}

flow "download_invoice" {
  from {
    connector = "api"
    operation = "GET /invoices/:id/pdf"
  }

  step "invoice" {
    connector = "db"
    query     = "SELECT * FROM invoices WHERE id = ?"
    params    = [input.params.id]
  }

  transform {
    filename = "'invoice-' + step.invoice.number + '.pdf'"
    number   = "step.invoice.number"
    date     = "step.invoice.date"
    total    = "step.invoice.total"
    customer = "step.invoice.customer_name"
  }

  to {
    connector = "invoice_pdf"
    operation = "generate"
  }
}

Response: The browser receives a PDF file download directly.

save

Writes the PDF to the filesystem. The file is saved to output_dir/filename.

connector "report_pdf" {
  type       = "pdf"
  template   = "./templates/report.html"
  output_dir = "./reports"
}

flow "archive_report" {
  from {
    connector = "api"
    operation = "POST /reports/generate"
  }

  transform {
    filename = "'report-' + now() + '.pdf'"
    title    = "input.title"
    data     = "input.data"
  }

  to {
    connector = "report_pdf"
    operation = "save"
  }
}

Response:

{"file_path": "./pdfs/report-2026-03-16.pdf", "filename": "report-2026-03-16.pdf", "size": 24576}

Payload Fields

The transform block builds the payload sent to the PDF connector:

Field Type Required Description
template string No Override the connector-level template path for this request
filename string No Output filename (default: document.pdf)
(any other) any No Template variables available as {{.field_name}}

All fields except template and filename are passed as template data.

Template resolution order: payload template field > connector config template > flow to.target fallback.

HTML Templates

Templates use Go template syntax with HTML markup. The template is rendered first (variable substitution, loops, conditionals), then the resulting HTML is converted to PDF.

Supported HTML Elements

Element Rendering
h1 - h6 Headings with decreasing font sizes (24px - 10px)
p Paragraphs with spacing
strong / b Bold text
em / i Italic text
table, tr, th, td Tables with borders and header highlighting
ul / ol, li Bulleted and numbered lists
hr Horizontal rule
br Line break
img Images (local files only)
div Container (inherits styles)

Supported Inline CSS

Apply styles via the style attribute on any element:

Property Values Example
text-align left, center, right style="text-align: right"
font-size Pixel values style="font-size: 18px"
color Hex colors style="color: #333333"
background-color Hex colors style="background-color: #f0f0f0"

Template Syntax

<!-- Variable substitution -->
<h1>Invoice #{{.number}}</h1>
<p>Date: {{.date}}</p>
<p>Customer: {{.customer}}</p>

<!-- Conditionals -->
{{if .discount}}
<p style="color: #cc0000">Discount: {{.discount}}%</p>
{{end}}

<!-- Loops -->
<table>
  <tr><th>Item</th><th>Qty</th><th>Price</th><th>Total</th></tr>
  {{range .items}}
  <tr>
    <td>{{.name}}</td>
    <td>{{.quantity}}</td>
    <td>${{.price}}</td>
    <td>${{.line_total}}</td>
  </tr>
  {{end}}
</table>

<hr>
<p style="text-align: right"><strong>Total: ${{.total}}</strong></p>

Complete Example

Invoice PDF Generation

Connector:

connector "api" {
  type   = "rest"
  driver = "server"
  port   = 3000
}

connector "db" {
  type   = "database"
  driver = "postgres"
  dsn    = env("DATABASE_URL")
}

connector "pdf" {
  type      = "pdf"
  template  = "./templates/invoice.html"
  page_size = "A4"
  font      = "Helvetica"
}

Flow:

flow "get_invoice_pdf" {
  from {
    connector = "api"
    operation = "GET /invoices/:id/pdf"
  }

  step "invoice" {
    connector = "db"
    query     = "SELECT * FROM invoices WHERE id = ?"
    params    = [input.params.id]
  }

  step "items" {
    connector = "db"
    query     = "SELECT * FROM invoice_items WHERE invoice_id = ?"
    params    = [input.params.id]
  }

  transform {
    filename = "'invoice-' + step.invoice.number + '.pdf'"
    number   = "step.invoice.number"
    date     = "step.invoice.date"
    customer = "step.invoice.customer_name"
    items    = "step.items"
    total    = "step.invoice.total"
  }

  to {
    connector = "pdf"
    operation = "generate"
  }
}

Template (templates/invoice.html):

<h1 style="color: #2c3e50">Invoice #{{.number}}</h1>
<hr>
<p><strong>Date:</strong> {{.date}}</p>
<p><strong>Customer:</strong> {{.customer}}</p>

<table>
  <tr>
    <th>Item</th>
    <th>Quantity</th>
    <th>Price</th>
    <th>Total</th>
  </tr>
  {{range .items}}
  <tr>
    <td>{{.name}}</td>
    <td>{{.quantity}}</td>
    <td>${{.price}}</td>
    <td>${{.line_total}}</td>
  </tr>
  {{end}}
</table>

<hr>
<p style="text-align: right; font-size: 18px">
  <strong>Total: ${{.total}}</strong>
</p>

Usage:

curl http://localhost:3000/invoices/42/pdf --output invoice.pdf

Binary HTTP Responses

When using the generate operation, the PDF connector returns special fields that the REST connector recognizes:

  • _binary — Base64-encoded PDF content
  • _content_typeapplication/pdf
  • _filename — Suggested filename for Content-Disposition header

This mechanism works for any binary content, not just PDFs. Any flow that returns _binary + _content_type will be served as a binary download by the REST connector.


Full configuration reference: See PDF in the Configuration Reference.