API reference

Generate PDFs from JSON using invoice, receipt, and report templates.

Quick start

  1. Get your API key — Sign in to the dashboard, create an API key, and copy it (shown once).
  2. Make your first call — Send a POST to /api/v1/generate with X-API-Key and JSON body (see below).
  3. Get your PDF — The response is raw PDF bytes (Content-Type: application/pdf). Save to a file (for example curl -o out.pdf).

Minimal invoice example (replace YOUR_API_KEY):

curl
curl -X POST "https://pdfapi-nu.vercel.app/api/v1/generate" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"template":"invoice","data":{"companyName":"Acme Design Co.","fromAddress":"100 Market St\nSan Francisco, CA","toAddress":"Jane Buyer\n200 Oak Ave\nAustin, TX","lineItems":[{"description":"Brand kit","quantity":1,"unitPrice":2500},{"description":"Monthly retainer","quantity":3,"unitPrice":800}],"taxRate":0.0825}}' \
  -o invoice.pdf

Authentication

Every request to /api/v1/generate must include a valid API key issued from your account. Create and manage keys in the dashboard.

Send the key in the X-API-Key header ( case-insensitive). There is no query-string or Bearer token flow for this endpoint.

Base URL

https://pdfapi-nu.vercel.app

All paths below are appended to this origin (for local development, use your own http://localhost:3000).

POST /api/v1/generate

Renders a PDF from a template discriminator and a data object. Request body must be JSON.

Success
200 — PDF bytes, Content-Disposition: attachment; filename="{template}.pdf"

Template: invoice

"template": "invoice". Tax is expressed as a decimal rate (for example 0.1 for 10%). The server adds line totals, subtotal, tax amount, and grand total for rendering.

data — JSON Schema

JSON Schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["companyName", "fromAddress", "toAddress", "lineItems", "taxRate"],
  "properties": {
    "companyName": { "type": "string", "minLength": 1 },
    "fromAddress": { "type": "string", "minLength": 1 },
    "toAddress": { "type": "string", "minLength": 1 },
    "lineItems": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "required": ["description", "quantity", "unitPrice"],
        "properties": {
          "description": { "type": "string", "minLength": 1 },
          "quantity": { "type": "number", "exclusiveMinimum": 0 },
          "unitPrice": { "type": "number", "minimum": 0 }
        },
        "additionalProperties": false
      }
    },
    "taxRate": { "type": "number", "minimum": 0 }
  },
  "additionalProperties": false
}

Example request body

JSON
{
  "template": "invoice",
  "data": {
    "companyName": "Acme Design Co.",
    "fromAddress": "100 Market St\nSan Francisco, CA",
    "toAddress": "Jane Buyer\n200 Oak Ave\nAustin, TX",
    "lineItems": [
      { "description": "Brand kit", "quantity": 1, "unitPrice": 2500 },
      { "description": "Monthly retainer", "quantity": 3, "unitPrice": 800 }
    ],
    "taxRate": 0.0825
  }
}

curl

bash
curl -X POST "https://pdfapi-nu.vercel.app/api/v1/generate" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"template":"invoice","data":{"companyName":"Acme Design Co.","fromAddress":"100 Market St\nSan Francisco, CA","toAddress":"Jane Buyer\n200 Oak Ave\nAustin, TX","lineItems":[{"description":"Brand kit","quantity":1,"unitPrice":2500},{"description":"Monthly retainer","quantity":3,"unitPrice":800}],"taxRate":0.0825}}' \
  -o invoice.pdf

Template: receipt

"template": "receipt". paymentMethodLast4 must be exactly four digits.

data — JSON Schema

JSON Schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["merchantName", "date", "items", "amountPaid", "paymentMethodLast4"],
  "properties": {
    "merchantName": { "type": "string", "minLength": 1 },
    "date": { "type": "string", "minLength": 1 },
    "items": {
      "type": "array",
      "minItems": 1,
      "items": { "type": "string", "minLength": 1 }
    },
    "amountPaid": { "type": "number", "minimum": 0 },
    "paymentMethodLast4": { "type": "string", "pattern": "^[0-9]{4}$" }
  },
  "additionalProperties": false
}

Example request body

JSON
{
  "template": "receipt",
  "data": {
    "merchantName": "Corner Cafe",
    "date": "2026-04-01",
    "items": ["Latte", "Blueberry muffin"],
    "amountPaid": 12.47,
    "paymentMethodLast4": "4242"
  }
}

curl

bash
curl -X POST "https://pdfapi-nu.vercel.app/api/v1/generate" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"template":"receipt","data":{"merchantName":"Corner Cafe","date":"2026-04-01","items":["Latte","Blueberry muffin"],"amountPaid":12.47,"paymentMethodLast4":"4242"}}' \
  -o receipt.pdf

Template: report

"template": "report". tableData is an array of objects with string keys; each value may be a string, number, boolean, or null.

data — JSON Schema

JSON Schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["title", "dateRange", "summaryStats", "tableData"],
  "properties": {
    "title": { "type": "string", "minLength": 1 },
    "dateRange": { "type": "string", "minLength": 1 },
    "summaryStats": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["label", "value"],
        "properties": {
          "label": { "type": "string", "minLength": 1 },
          "value": { "oneOf": [{ "type": "string" }, { "type": "number" }] }
        },
        "additionalProperties": false
      }
    },
    "tableData": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": {
          "oneOf": [
            { "type": "string" },
            { "type": "number" },
            { "type": "boolean" },
            { "type": "null" }
          ]
        }
      }
    }
  },
  "additionalProperties": false
}

Example request body

JSON
{
  "template": "report",
  "data": {
    "title": "Weekly operations",
    "dateRange": "2026-03-25 to 2026-03-31",
    "summaryStats": [
      { "label": "Orders", "value": 1284 },
      { "label": "Revenue", "value": "$48,200" }
    ],
    "tableData": [
      { "sku": "A-1", "units": 90, "inStock": true },
      { "sku": "B-4", "units": 12, "inStock": false }
    ]
  }
}

curl

bash
curl -X POST "https://pdfapi-nu.vercel.app/api/v1/generate" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"template":"report","data":{"title":"Weekly operations","dateRange":"2026-03-25 to 2026-03-31","summaryStats":[{"label":"Orders","value":1284},{"label":"Revenue","value":"$48,200"}],"tableData":[{"sku":"A-1","units":90,"inStock":true},{"sku":"B-4","units":12,"inStock":false}]}}' \
  -o report.pdf

GET /api/v1/templates

Returns metadata for templates available on the generate endpoint. No API key required.

Response shape: { "templates": [ { "id", "name", "description" } ] }

curl
curl -s "https://pdfapi-nu.vercel.app/api/v1/templates"

Example response JSON:

JSON
{
  "templates": [
    {
      "id": "invoice",
      "name": "Invoice",
      "description": "Itemized invoice with line totals, tax, and grand total."
    },
    {
      "id": "receipt",
      "name": "Receipt",
      "description": "Merchant receipt with items, amount paid, and card last four digits."
    },
    {
      "id": "report",
      "name": "Report",
      "description": "Summary stats and a flexible key-value table."
    }
  ]
}

Error responses

Errors use JSON with a consistent shape: error.code, error.message, and optional error.details.

400 — Validation error

Malformed JSON or payload does not match the schema for the template.

Example
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request payload",
    "details": {
      "formErrors": [],
      "fieldErrors": {
        "data": ["Invalid input"]
      }
    }
  }
}

401 — Invalid or missing API key

Omitting X-API-Key or sending an unknown key.

Example
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid API key"
  }
}

429 — Rate limit or quota exceeded

Too many requests per minute (RATE_LIMITED, may include Retry-After header) or monthly PDF cap reached (QUOTA_EXCEEDED).

Rate limit
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Too many requests",
    "details": {
      "retryAfterSeconds": 42
    }
  }
}
Monthly quota
{
  "error": {
    "code": "QUOTA_EXCEEDED",
    "message": "Monthly PDF quota exceeded",
    "details": {
      "limit": 200,
      "used": 200,
      "period": "2026-04"
    }
  }
}

500 — Server error

Unexpected failure or render error; see error.code for specifics.

Example
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Something went wrong"
  }
}

Rate limits

Limits apply per account (aggregated across your API keys). RPM is a per-minute request cap; monthly values are PDF generations per UTC calendar month.

TierMonthly PDFsRPM
Free2005
Starter2,00030
Growth10,00060
Scale50,000120

Code examples

curl

Write PDF to disk with -o.

bash
curl -X POST "https://pdfapi-nu.vercel.app/api/v1/generate" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"template":"invoice","data":{"companyName":"Acme Design Co.","fromAddress":"100 Market St\nSan Francisco, CA","toAddress":"Jane Buyer\n200 Oak Ave\nAustin, TX","lineItems":[{"description":"Brand kit","quantity":1,"unitPrice":2500},{"description":"Monthly retainer","quantity":3,"unitPrice":800}],"taxRate":0.0825}}' \
  -o invoice.pdf

Python (requests)

Python
import requests

url = "https://pdfapi-nu.vercel.app/api/v1/generate"
headers = {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
}
payload = {
    "template": "invoice",
    "data": {
        "companyName": "Acme",
        "fromAddress": "123 St",
        "toAddress": "456 Ave",
        "lineItems": [
            {"description": "Item", "quantity": 1, "unitPrice": 100},
        ],
        "taxRate": 0.1,
    },
}

r = requests.post(url, headers=headers, json=payload, timeout=120)
r.raise_for_status()

with open("invoice.pdf", "wb") as f:
    f.write(r.content)
print("Wrote invoice.pdf")

Node.js (fetch)

Requires Node 18+ for global fetch.

JavaScript
const url = "https://pdfapi-nu.vercel.app/api/v1/generate";
const res = await fetch(url, {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    template: "invoice",
    data: {
      companyName: "Acme",
      fromAddress: "123 St",
      toAddress: "456 Ave",
      lineItems: [{ description: "Item", quantity: 1, unitPrice: 100 }],
      taxRate: 0.1,
    },
  }),
});

if (!res.ok) {
  const err = await res.json().catch(() => ({}));
  throw new Error(`HTTP ${res.status}: ${JSON.stringify(err)}`);
}

const buf = Buffer.from(await res.arrayBuffer());
await import("node:fs/promises").then((fs) => fs.writeFile("invoice.pdf", buf));
console.log("Wrote invoice.pdf");

AI agent integration

  • Tool design — Expose a single tool such as generate_pdf with arguments template and data matching the JSON schemas above. Map tool output to a POST body.
  • Secrets — Store X-API-Key in environment variables or your agent runtime secret store; never embed keys in prompts or logs.
  • Binary responses — The success body is PDF bytes, not JSON. Agents should write to a file, upload to object storage, or base64-encode for downstream tools — not parse as JSON.
  • Errors — On non-2xx responses, parse JSON for error.code and retry with backoff only for 429 (respect Retry-After when present).
  • Validation — Validate data against the template schema before calling the API to reduce 400s and cost.