# Document Generator: invoices, receipts and quotes to PDF (`examsens.ai/document-generator`) Actor

Turns JSON rows into PDF invoices, receipts, quotes and packing slips, or into any document from your own HTML template. Computes line totals, discounts, tax by rate, shipping and balance due. Returns a download link for each PDF.

- **URL**: https://apify.com/examsens.ai/document-generator.md
- **Developed by:** [Ed Rayyis](https://apify.com/examsens.ai) (community)
- **Categories:** Business, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $9.00 / 1,000 document generateds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Document Generator: invoices, receipts and quotes to PDF

Turn JSON rows into finished PDF documents. Pick a built in template (invoice, receipt, quote or packing slip) or bring your own HTML template, give one JSON object per document, and get one PDF per row with a download link.

### Built in templates

- Invoice: number, date, due date, order number, bill to and ship to, line items, discounts, tax, shipping, amount paid and balance due. A fully paid invoice gets a PAID stamp.
- Receipt: number, date, payment method, received from, line items and totals. When you give no amount paid, the receipt is marked paid in full.
- Quote: number, date, valid until, prepared for, line items and totals.
- Packing slip: order number, date, ship to and bill to, items with SKU and quantity, and an empty box per line to check off. No prices.

Every template shows your logo, seller details, notes and terms, and uses your accent color. Long item lists run onto more pages with the table header repeated. Page numbers (1 / 3) print at the foot of each page.

### The numbers

Totals are computed with decimal arithmetic and rounded half up to the currency's minor unit (cents, or whole yen for JPY):

- Line amount = quantity × unit price, less the line's discount percent
- Document discount: a percent of the subtotal, a fixed amount, or both
- Tax: each line uses its own `taxRate` or the document's. Tax is computed per rate on the discounted amounts and shown as one line per rate.
- Total = subtotal, less discount, plus tax, plus shipping
- Balance due = total less amount paid

Each dataset item carries the totals as numbers, with tax broken down by rate, so you can check them against your books.

Currency symbols are built in for USD, EUR, GBP, JPY, CNY, INR, KRW, CAD, AUD, NZD, CHF, SEK, NOK, DKK, PLN, BRL, MXN, ZAR, SGD, HKD, ILS, AED and TRY. Any other code prints before the amount, or set your own `currencySymbol`.

### Input

- Template: invoice, receipt, quote, packing slip or custom
- Documents: a JSON list, one object per document
- Shared values: an object merged under every document, so the seller, currency, accent color, payment instructions and terms are written once. A document's own value wins.
- Documents from URL: a URL returning a JSON list of more documents
- Custom HTML template: Jinja2 HTML for the custom template
- PDF: page size (Letter, A4, Legal, A5), margin, page numbers, the field used to name each file, and an option to save the HTML too

Document fields:

| Field | Meaning |
| --- | --- |
| `documentNumber`, `date`, `dueDate`, `validUntil`, `orderNumber` | Header details, printed as given |
| `seller`, `buyer`, `shipTo` | Objects with `name`, `contactName`, `address` (text with line breaks, or a list of lines), `email`, `phone`, `taxId`. `seller.logoUrl` adds a logo. |
| `items` | List of `description`, `details`, `sku`, `quantity` (default 1), `unit`, `unitPrice`, `discountPercent`, `taxRate` |
| `currency`, `currencySymbol` | ISO code (default USD) and an optional symbol |
| `taxRate`, `discountPercent`, `discountAmount`, `shipping`, `amountPaid` | Document level amounts |
| `paymentMethod`, `paymentInstructions`, `notes`, `terms` | Text blocks |
| `title`, `accentColor`, `labels` | Your own heading, a hex color such as `#1f4e79`, and replacement words for any label (for example `{"billTo": "Customer"}`) to print the document in another language |

### Custom templates

Write any document in HTML with Jinja2 placeholders: certificates, letters, tickets, statements. Every document field is available by name, such as `{{ name }}`. When the document has `items`, the computed `items` (with `amountText` and `unitPriceText`) and `totals` (such as `totals.totalText`) are added. A placeholder with no value in the row stops that row with an error that names it. Templates run in a sandbox and values are HTML escaped.

### Output

Each PDF goes to the run's key value store, named from the document number (or the field you choose). The dataset holds one item per row with `index`, `documentNumber`, `template`, `ok`, `totals`, `lineItems`, `pages`, `bytes`, `pdfUrl`, and `htmlUrl` when HTML is saved.

A row that cannot be generated gets `ok: false` and an `error`, for example `item 1 quantity must be a number, got "two"`, `"items" must be a list with at least one line item`, or `'course' is undefined` in a custom template. The other rows still run.

### Pricing

Pay per event: one event per PDF generated. A row that fails is not charged. The run stops when it reaches the maximum charge you set.

### Scope and conduct

Your document data is used only to build the PDFs in your run and is not sent anywhere else. A logo URL is loaded once per document.

### Support

Questions and problems: the Issues tab on this page.

# Actor input Schema

## `template` (type: `string`):

Built in invoice, receipt, quote or packing slip, or your own HTML template.

## `documents` (type: `array`):

One JSON object per document. Common fields: documentNumber, date, dueDate, validUntil, orderNumber, currency, buyer, shipTo, items (description, sku, quantity, unit, unitPrice, discountPercent, taxRate, details), taxRate, discountPercent, discountAmount, shipping, amountPaid, paymentMethod, paymentInstructions, notes, terms. See the README for the full list.

## `defaults` (type: `object`):

Values merged under every document, such as the seller, currency, accent color, payment instructions and terms. A document's own value wins.

## `dataUrl` (type: `string`):

Optional URL of a JSON list of documents, added after the ones above.

## `customTemplate` (type: `string`):

Jinja2 HTML used when the template is custom. Every document field is available by name. When the document has items, the computed items (amountText, unitPriceText) and totals (subtotalText, taxText, totalText and the numbers) are added.

## `pageSize` (type: `string`):

Paper size of the PDF.

## `margin` (type: `string`):

Page margin on every side, in CSS units such as 15mm or 0.6in.

## `pageNumbers` (type: `boolean`):

Print page X / Y at the foot of every page.

## `fileNameField` (type: `string`):

Document field used to name each PDF.

## `saveHtml` (type: `boolean`):

Save the HTML of each document next to its PDF.

## Actor input object example

```json
{
  "template": "invoice",
  "documents": [
    {
      "documentNumber": "INV-1001",
      "date": "2026-10-04",
      "dueDate": "2026-11-03",
      "buyer": {
        "name": "Northwind Studio",
        "address": "200 Market Street\nSpringfield, IL 62701"
      },
      "items": [
        {
          "description": "Website redesign",
          "quantity": 1,
          "unitPrice": 2400
        },
        {
          "description": "Hosting",
          "details": "12 months",
          "quantity": 12,
          "unit": "mo",
          "unitPrice": 15
        }
      ],
      "taxRate": 8.25
    }
  ],
  "defaults": {
    "currency": "USD",
    "seller": {
      "name": "Example Design Co.",
      "address": "1 Main Street\nAnytown, CA 94000",
      "email": "billing@example.com"
    },
    "paymentInstructions": "Bank transfer to account 000000, or pay online.",
    "terms": "Payment due within 30 days."
  },
  "pageSize": "Letter",
  "margin": "15mm",
  "pageNumbers": true,
  "fileNameField": "documentNumber",
  "saveHtml": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "documents": [
        {
            "documentNumber": "INV-1001",
            "date": "2026-10-04",
            "dueDate": "2026-11-03",
            "buyer": {
                "name": "Northwind Studio",
                "address": "200 Market Street\nSpringfield, IL 62701"
            },
            "items": [
                {
                    "description": "Website redesign",
                    "quantity": 1,
                    "unitPrice": 2400
                },
                {
                    "description": "Hosting",
                    "details": "12 months",
                    "quantity": 12,
                    "unit": "mo",
                    "unitPrice": 15
                }
            ],
            "taxRate": 8.25
        }
    ],
    "defaults": {
        "currency": "USD",
        "seller": {
            "name": "Example Design Co.",
            "address": "1 Main Street\nAnytown, CA 94000",
            "email": "billing@example.com"
        },
        "paymentInstructions": "Bank transfer to account 000000, or pay online.",
        "terms": "Payment due within 30 days."
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("examsens.ai/document-generator").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "documents": [{
            "documentNumber": "INV-1001",
            "date": "2026-10-04",
            "dueDate": "2026-11-03",
            "buyer": {
                "name": "Northwind Studio",
                "address": """200 Market Street
Springfield, IL 62701""",
            },
            "items": [
                {
                    "description": "Website redesign",
                    "quantity": 1,
                    "unitPrice": 2400,
                },
                {
                    "description": "Hosting",
                    "details": "12 months",
                    "quantity": 12,
                    "unit": "mo",
                    "unitPrice": 15,
                },
            ],
            "taxRate": 8.25,
        }],
    "defaults": {
        "currency": "USD",
        "seller": {
            "name": "Example Design Co.",
            "address": """1 Main Street
Anytown, CA 94000""",
            "email": "billing@example.com",
        },
        "paymentInstructions": "Bank transfer to account 000000, or pay online.",
        "terms": "Payment due within 30 days.",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("examsens.ai/document-generator").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "documents": [
    {
      "documentNumber": "INV-1001",
      "date": "2026-10-04",
      "dueDate": "2026-11-03",
      "buyer": {
        "name": "Northwind Studio",
        "address": "200 Market Street\\nSpringfield, IL 62701"
      },
      "items": [
        {
          "description": "Website redesign",
          "quantity": 1,
          "unitPrice": 2400
        },
        {
          "description": "Hosting",
          "details": "12 months",
          "quantity": 12,
          "unit": "mo",
          "unitPrice": 15
        }
      ],
      "taxRate": 8.25
    }
  ],
  "defaults": {
    "currency": "USD",
    "seller": {
      "name": "Example Design Co.",
      "address": "1 Main Street\\nAnytown, CA 94000",
      "email": "billing@example.com"
    },
    "paymentInstructions": "Bank transfer to account 000000, or pay online.",
    "terms": "Payment due within 30 days."
  }
}' |
apify call examsens.ai/document-generator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,examsens.ai/document-generator"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/q0MnjDOiczyh6XkHG/builds/UwGJTKyE4c5LvhC6l/openapi.json
