API reference

Docmill API

JSON in, documents out. One REST API renders PDF, DOCX, and XLSX files from templates — including four built-in starters and templates drafted for you by AI.

Base URL: https://api.docmill.io · All request and error bodies are JSON. Successful document renders return the binary file unless you ask for base64.

Quickstart

From nothing to an invoice PDF in under a minute. Sign up for a free key (50 documents/month, no card), then render the built-in starter:invoice template.

# 1. Create a free account — the API key is shown once, save it
curl -s https://api.docmill.io/signup \
  -H "Content-Type: application/json" \
  -d '{"name": "Ada Lovelace", "email": "ada@example.com"}'
# → 201 {"account_id":"acc_…","api_key":"dk_live_…","plan":"free"}

export DOCMILL_API_KEY=dk_live_your_key_here

# 2. Render an invoice PDF from a starter template
curl https://api.docmill.io/v1/documents \
  -H "Authorization: Bearer $DOCMILL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "starter:invoice",
    "data": {
      "invoiceNumber": "INV-2026-0001",
      "issueDate": "2026-08-29", "dueDate": "2026-09-28",
      "currency": "USD",
      "company": {"name": "Acme Studio LLC", "address": "100 Market St", "email": "billing@acme.studio"},
      "client":  {"name": "Northwind Traders", "address": "1 Harbor Way"},
      "items": [{"description": "Design sprint", "quantity": 1, "unitPrice": 4800}],
      "subtotal": 4800, "taxRate": 8, "taxAmount": 384, "total": 5184
    }
  }' --output invoice.pdf
# → invoice.pdf (%PDF-1.7 …)

That is the whole loop. Everything below is detail: your own templates, DOCX/XLSX output, base64 and async modes, and billing.

Authentication

Every /v1/* endpoint requires a bearer token — the API key you received at signup, starting with dk_live_:

Authorization: Bearer dk_live_…

Requests without a valid key return 401 {"error": "invalid or missing API key"}. Keys are shown once at signup; store them in a secret manager, not in workflow definitions. The examples on this page assume export DOCMILL_API_KEY=dk_live_….

Sign up

POST /signup

Creates an account and issues an API key. No authentication required; rate-limited per IP. New accounts start on the free plan: 50 documents/month with a small watermark line rendered into PDFs.

FieldTypeDescription
name requiredstringYour name or company name.
email requiredstringContact email for the account.
curl https://api.docmill.io/signup \
  -H "Content-Type: application/json" \
  -d '{"name": "Ada Lovelace", "email": "ada@example.com"}'
# 201 Created
{
  "account_id": "acc_8c1f2b6d94e04a31b7aa",
  "api_key": "dk_live_5f2e…",   ← shown once — save it now
  "plan": "free"
}

Usage

GET /v1/usage

Your current month's document consumption against the plan quota. Every successful render — sync, async job, or AI template generation — counts as one document.

curl https://api.docmill.io/v1/usage \
  -H "Authorization: Bearer $DOCMILL_API_KEY"
# 200 OK
{
  "plan": "free",
  "month": "2026-08",
  "docs_used": 12,
  "docs_included": 50,
  "docs_remaining": 38
}

Generate a document

POST /v1/documents

Renders a template with your data. By default the response is the binary file with Content-Type set to the document's MIME type and Content-Disposition: attachment; filename="document.pdf" (or .docx/.xlsx).

FieldTypeDescription
template_id one ofstringA saved template id (tpl_…) or a starter id (starter:invoice, starter:quote, starter:report, starter:certificate).
html one ofstringInline Handlebars HTML instead of a saved template. Inline HTML renders to pdf output only. One of template_id or html is required.
data optionalobjectJSON bound into the template. Defaults to {}.
output optionalstringpdf (default), docx, or xlsx. HTML templates render to PDF only; a DOCX or XLSX template renders to its own format — the output must match the template kind.
pdf_options optionalobjectPage setup for PDF output — see PDF options.
Query paramEffect
?encoding=base64Return JSON with the file base64-encoded instead of raw bytes — see Base64 responses.
?async=1Queue the render and return 202 with a job id — see Async jobs.
# Render with a saved template
curl https://api.docmill.io/v1/documents \
  -H "Authorization: Bearer $DOCMILL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "tpl_9f2c41d6a8be4a17c0de",
    "data": {"customer": "Northwind Traders", "amount": 5184},
    "output": "pdf"
  }' --output document.pdf
# Inline HTML — no saved template needed (PDF only)
curl https://api.docmill.io/v1/documents \
  -H "Authorization: Bearer $DOCMILL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<h1>Receipt {{number}}</h1><p>{{money amount currency}} received. Thank you!</p>",
    "data": {"number": "R-1001", "amount": 49, "currency": "USD"}
  }' --output receipt.pdf

Render failures return 422 with a machine-readable code; quota exhaustion returns 429 with an upgrade URL. See Errors.

Base64 responses

Automation platforms and agents often prefer JSON over raw bytes. Add ?encoding=base64 to get the file wrapped in JSON:

curl "https://api.docmill.io/v1/documents?encoding=base64" \
  -H "Authorization: Bearer $DOCMILL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"template_id": "starter:certificate",
       "data": {"recipientName": "Jordan Ellis",
                "courseName": "Advanced Workflow Automation",
                "completedAt": "2026-08-20",
                "issuerName": "Automation Academy"}}'
# 200 OK
# {"content_type": "application/pdf", "extension": "pdf", "data_base64": "JVBERi0xLjc…"}

PDF options

The pdf_options object controls page setup for PDF output (ignored for DOCX/XLSX):

FieldTypeDescription
pageFormatstring"A4" (default), "Letter", or "Legal".
landscapebooleanLandscape orientation. Defaults to false.
marginMmnumberUniform page margin in millimeters. Defaults to 14.
"pdf_options": {"pageFormat": "Letter", "landscape": true, "marginMm": 10}

Async jobs

For bulk runs, add ?async=1. The request is validated, queued, and acknowledged immediately with 202; poll the job until it is done and decode data_base64.

curl "https://api.docmill.io/v1/documents?async=1" \
  -H "Authorization: Bearer $DOCMILL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"template_id": "starter:report", "data": {"title": "Growth report", "period": "August 2026"}}'
# 202 Accepted
# {"job_id": "job_c41d6a8be4a17c0de9f2", "status": "queued"}

GET /v1/jobs/:id

curl https://api.docmill.io/v1/jobs/job_c41d6a8be4a17c0de9f2 \
  -H "Authorization: Bearer $DOCMILL_API_KEY"
# 200 OK
{
  "job_id": "job_c41d6a8be4a17c0de9f2",
  "status": "done",              ← queued | processing | done | error
  "content_type": "application/pdf",
  "data_base64": "JVBERi0xLjc…",
  "error": null                  ← failure message when status is "error"
}

Templates

GET /v1/templates

Lists your saved templates plus the four built-in read-only starters every account can render immediately:

Starter idNameDescription
starter:invoiceInvoiceClean line-item invoice with totals, tax, and payment terms.
starter:quoteQuoteSales quote with validity window and per-item options.
starter:reportMonthly reportMetric summary report with a highlights table.
starter:certificateCertificateCompletion certificate, landscape, presentation-ready.
curl https://api.docmill.io/v1/templates \
  -H "Authorization: Bearer $DOCMILL_API_KEY"
# 200 OK
{
  "templates": [
    {"id": "tpl_9f2c41d6a8be4a17c0de", "name": "Consulting invoice", "kind": "html",
     "created_at": "2026-08-29T09:12:33.000Z", "updated_at": "2026-08-29T09:12:33.000Z"}
  ],
  "starters": [
    {"id": "starter:invoice", "name": "Invoice", "kind": "html",
     "description": "Clean line-item invoice with totals, tax, and payment terms.",
     "readonly": true}
    … quote, report, certificate
  ]
}

POST /v1/templates

Saves a reusable template. HTML templates are Handlebars sources rendered to PDF; DOCX templates are Word files with {{marker}} commands; XLSX templates are spreadsheets with ${marker} placeholders — upload the binary kinds as base64.

FieldTypeDescription
name requiredstringDisplay name.
kind optionalstringhtml (default), docx, or xlsx.
htmlstringHandlebars HTML source. Required when kind is html.
file_base64stringBase64-encoded template file. Required when kind is docx or xlsx.
sample_data optionalobjectExample payload stored with the template, handy for connectors and teammates.
curl https://api.docmill.io/v1/templates \
  -H "Authorization: Bearer $DOCMILL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Receipt",
    "kind": "html",
    "html": "<h1>Receipt {{number}}</h1><p>{{money amount currency}} received from {{customer}}.</p>",
    "sample_data": {"number": "R-1001", "amount": 49, "currency": "USD", "customer": "Northwind"}
  }'
# 201 Created
# {"id": "tpl_9f2c41d6a8be4a17c0de", "name": "Receipt", "kind": "html"}

POST /v1/templates/generate

The template editor you don't have to learn: describe the document in plain language (or paste an old HTML document to imitate) and the AI drafts a Handlebars HTML template plus sample data showing exactly which fields to send. The result is saved to your account unless you pass save: false. Each generation counts as one document of quota.

FieldTypeDescription
prompt one ofstringPlain-language description of the document you need.
sample_html one ofstringExisting HTML to restyle into a template. One of prompt or sample_html is required.
name optionalstringTemplate name; defaults to an AI-suggested one.
save optionalbooleanDefault true (201 with an id). Pass false to preview only (200, id is null).
curl https://api.docmill.io/v1/templates/generate \
  -H "Authorization: Bearer $DOCMILL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "A consulting invoice with line items, 8% tax, EU VAT number, and 30-day payment terms"}'
# 201 Created
{
  "id": "tpl_41d6a8be4a17c0de9f2c",
  "name": "Consulting invoice",
  "kind": "html",
  "html": "<html>…{{#each items}}…{{/each}}…</html>",
  "sample_data": {"items": [{"description": "…", "quantity": 1, "unitPrice": 1200}], "vatNumber": "EU123…"}
}

Returns 503 if the server has no AI key configured, and 429 when your monthly quota is already spent.

GET /v1/templates/:id

Fetches one template, including its HTML source and stored sample data. Every template is plain HTML underneath — inspect it, tweak it, own it.

curl https://api.docmill.io/v1/templates/tpl_9f2c41d6a8be4a17c0de \
  -H "Authorization: Bearer $DOCMILL_API_KEY"
# 200 OK
{
  "id": "tpl_9f2c41d6a8be4a17c0de",
  "name": "Receipt",
  "kind": "html",
  "html_source": "<h1>Receipt {{number}}</h1>…",
  "sample_data_json": "{\"number\":\"R-1001\",\"amount\":49}",
  "created_at": "2026-08-29T09:12:33.000Z",
  "updated_at": "2026-08-29T09:12:33.000Z"
}

DELETE /v1/templates/:id

curl -X DELETE https://api.docmill.io/v1/templates/tpl_9f2c41d6a8be4a17c0de \
  -H "Authorization: Bearer $DOCMILL_API_KEY"
# 200 OK  {"deleted": true}

Both return 404 {"error": "template not found"} for unknown ids (starters cannot be fetched or deleted this way — they are read-only).

Templating & helpers

HTML templates are standard Handlebars: {{field}}, nested paths ({{client.name}}), {{#each}}, {{#if}}, and ../ to reach parent scope inside loops. Missing fields render as empty strings rather than failing. Five formatting helpers are registered for every template:

HelperSignatureBehavior
money{{money value currency}}Formats a number as currency in en-US style, e.g. $4,800.00. currency is an optional 3-letter ISO code (default USD; anything else falls back to USD). Non-numeric values render empty.
formatDate{{formatDate value style}}Parses a date string or Date; renders the long en-US form (August 29, 2026) by default, or pass "short" for 8/29/26. Invalid dates render empty.
lineTotal{{lineTotal qty unitPrice}}Returns qty × unitPrice as a number (0 if either is not numeric). Usually wrapped in money.
multiply{{multiply a b}}Returns a × b as a raw number.
sum{{sum items "field"}}Sums field across an array of objects; call as {{sum numbers}} to sum an array of plain numbers. Non-numeric entries count as 0; a non-array returns 0.

A complete miniature example — template, data, and what the PDF shows:

<!-- template -->
<h1>Invoice {{number}} &middot; {{formatDate issueDate}}</h1>
<table>
{{#each items}}
  <tr><td>{{this.description}}</td>
      <td>{{this.quantity}} × {{money this.unitPrice ../currency}}</td>
      <td>{{money (lineTotal this.quantity this.unitPrice) ../currency}}</td></tr>
{{/each}}
  <tr><td colspan="2"><b>Total</b></td>
      <td><b>{{money (sum items "amount") currency}}</b></td></tr>
</table>
// data
{
  "number": "INV-7",
  "issueDate": "2026-08-29",
  "currency": "EUR",
  "items": [
    {"description": "Design sprint",  "quantity": 1,  "unitPrice": 4800, "amount": 4800},
    {"description": "Implementation", "quantity": 32, "unitPrice": 120,  "amount": 3840}
  ]
}
# rendered
Invoice INV-7 · August 29, 2026
Design sprint     1 × €4,800.00    €4,800.00
Implementation   32 × €120.00      €3,840.00
Total                              €8,640.00

Tip: templates are complete HTML pages — include your own <style> block for fonts, tables, and layout. The starter templates (fetch one with a render, or study the samples on the landing page) are good references.

Billing

Paid plans are handled through Stripe. Upgrading is two calls: create a checkout session, open the returned URL in a browser, pay. Manage or cancel any time through the customer portal.

POST /v1/billing/checkout

FieldTypeDescription
plan requiredstring"starter", "growth", or "scale".
curl https://api.docmill.io/v1/billing/checkout \
  -H "Authorization: Bearer $DOCMILL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"plan": "growth"}'
# 200 OK  {"url": "https://checkout.stripe.com/c/pay/cs_live_…"}
# open the URL in a browser to complete payment

POST /v1/billing/portal

Returns a Stripe customer-portal link where you can change plan, cancel, and download invoices.

curl -X POST https://api.docmill.io/v1/billing/portal \
  -H "Authorization: Bearer $DOCMILL_API_KEY"
# 200 OK  {"url": "https://billing.stripe.com/p/session/…"}

GET /v1/billing

curl https://api.docmill.io/v1/billing \
  -H "Authorization: Bearer $DOCMILL_API_KEY"
# 200 OK
{
  "plan": "growth",
  "billing_configured": true,
  "has_subscription": true
}

Errors & rate limits

Errors are JSON with an error message; render failures add a machine-readable code:

# 422 Unprocessable Entity
{
  "error": "Template failed to compile: Parse error on line 3 …",
  "code": "template_compile_error"
}
StatusMeaning
400Invalid body — missing required field, unknown output format, inline HTML with non-PDF output, or a template kind that cannot render to the requested format.
401Invalid or missing API key.
404Template or job not found (ids are scoped to your account).
422Render failed. code is one of missing_template, template_compile_error (bad Handlebars syntax), template_data_error (template crashed on your data), browser_error (PDF engine failure).
429Monthly quota reached — the body includes an upgrade URL — or rate limited (120 requests/min per key; signup is rate-limited per IP).
503AI template generation is not configured on this server.
# 429 Too Many Requests (quota)
{
  "error": "monthly quota reached (50 documents on the free plan)",
  "upgrade": "https://docmill.io/pricing"
}

Plans & quotas

One document costs one document: per-document metering, any format, any page count within reason. No credit blocks, no per-page multipliers.

PlanPriceDocuments / monthNotes
Free$050Small watermark line on PDFs. All formats and starters included.
Starter$19/mo1,000No watermark. AI template generation.
Growth$49/mo5,000Async bulk jobs, priority support.
Scale$99/mo15,000Volume tiers above on request.

Check consumption any time with GET /v1/usage; upgrade with POST /v1/billing/checkout or from the pricing page.

Connectors

The API works from anything that can send HTTP, but the connectors are the fast path — all free; your plan meters documents, nothing else.

n8n

Community node n8n-nodes-docmill (n8n 1.0+). Install via Settings → Community Nodes, add your key under Credentials → Docmill API.

Operations: Generate Document (file lands in a binary property, ready for email or storage nodes), Generate Template (AI), and List Templates.

Zapier

The Docmill app authenticates with your API key and adds a Generate Document action: pick a template (yours or a starter), map JSON data from earlier steps, choose PDF/DOCX/XLSX.

The generated file is passed to later steps as a real file, plus a template-list trigger powers the dropdown.

Make

The Docmill app's Generate Document module returns the file as binary output, so it pipes directly into Email, Google Drive, Dropbox, and similar modules. Template dropdown included.

MCP (AI agents)

@docmill/mcp-server is a stdio MCP server (MIT) for Claude and other agents. Tools: list_templates, generate_document (renders and saves the file to disk), create_template (AI drafting), and get_usage.

MCP client configuration:

{
  "mcpServers": {
    "docmill": {
      "command": "npx",
      "args": ["-y", "@docmill/mcp-server"],
      "env": {"DOCMILL_API_KEY": "dk_live_…"}
    }
  }
}