PDF templates / Statement

SaaS Usage Statement

One-page A4 SaaS usage statement / invoice: vendor and account header, billing period, a stacked daily-usage bar chart split by render channel, a line-item charges table with totals aligned to the same columns, payment details and a total-due card. Charts render from each call's data.

First page of the SaaS Usage Statement template, rendered with its sample data
statement-usage.pdfpage 1 of 1

DSL source

The whole template. Learn the language in the template DSL guide.

js
const chargeCols = [["Item", "1fr"], ["Qty", "auto", "right"], ["Rate", "auto", "right"], ["Amount", "auto", "right"]];

const template = doc({ size: "A4", title: "Usage statement {{statementNumber}}", padding: [26, 30, 26, 30] },

  // Masthead
  hdr(
    r({ margin: [0, 0, 0, 6] },
      col("62%",
        s({ "font-size": 8, "font-weight": "bold", color: "#0E7490", "letter-spacing": 1.5 }, "{{vendor.name}}"),
        s({ "font-size": 8, color: "#6B7280", margin: [2, 0, 0, 0] }, "{{vendor.product}} · {{vendor.website}}"),
      ),
      col("38%", { align: "right" },
        s({ "font-size": 13, "font-weight": "bold", color: "#0F172A" }, "Usage Statement"),
        s({ "font-size": 8, color: "#6B7280", margin: [2, 0, 0, 0] }, "{{statementNumber}}"),
      ),
    ),
    hr(0, "#0E7490"),
  ),

  // Account and billing period
  r({ margin: [7, 0, 10, 0] },
    col("50%",
      s({ "font-size": 7, color: "#6B7280", "font-weight": "bold", "letter-spacing": 1 }, "BILLED TO"),
      s({ "font-size": 10, "font-weight": "bold", color: "#0F172A", margin: [2, 0, 0, 0] }, "{{account.name}}"),
      s({ "font-size": 8.5, color: "#475569" }, "{{account.id}} · {{account.email}}"),
    ),
    col("50%", { align: "right" },
      s({ "font-size": 7, color: "#6B7280", "font-weight": "bold", "letter-spacing": 1 }, "BILLING PERIOD"),
      s({ "font-size": 10, "font-weight": "bold", color: "#0F172A", margin: [2, 0, 0, 0] }, "{{period.label}}"),
      s({ "font-size": 8.5, color: "#475569" }, "Issued {{issuedOn}} · Due {{dueDate}}"),
    ),
  ),

  // Daily usage over the billing period
  r({ padding: [8, 10, 8, 10], "background-color": "#F8FAFC", "border-color": "#E2E8F0" },
    col("100%",
      s({ "font-size": 7.5, "font-weight": "bold", color: "#0E7490", "letter-spacing": 1.5 }, "DAILY RENDER VOLUME"),
      chart({
        type: "bar",
        data: "{{dailyUsage}}",
        x: "day",
        y: [
          { key: "api", label: "API" },
          { key: "dashboard", label: "Dashboard" },
          { key: "batch", label: "Batch" },
        ],
        stacked: true,
        width: 515,
        height: 140,
        "x-title": "Day of billing period",
        "y-title": "Renders",
      }),
    ),
  ),
  gap(8),

  // Line items
  s({ "font-size": 11, "font-weight": "bold", color: "#0F172A", margin: [0, 0, 3, 0] }, "Charges this period"),
  table(chargeCols, "c in charges",
    [["{{c.item}}", "1fr"], ["{{c.qty | number}}", "auto", "right"], ["{{c.rate | currency}}", "auto", "right"], ["{{c.amount | currency}}", "auto", "right"]],
  ),
  totals(
    [
      ["Subtotal", "{{summary.subtotal | currency}}"],
      ["Volume discount (5%)", "−{{summary.discount | currency}}"],
      ["{{summary.taxLabel}}", "{{summary.tax | currency}}"],
      ["Total due", "{{summary.total | currency}}", true],
    ],
    chargeCols,
  ),
  gap(9),

  // Payment details and amount due
  r(
    col("58%",
      s({ "font-size": 11, "font-weight": "bold", color: "#0F172A", margin: [0, 0, 3, 0] }, "Payment details"),
      s({ "font-size": 8.5, color: "#334155", "line-height": 1.4 }, "{{payment.method}}"),
      s({ "font-size": 8.5, color: "#334155", "line-height": 1.4 }, "Auto-pay scheduled {{payment.autoPayDate}} · Reference {{payment.reference}}"),
      s({ "font-size": 8.5, color: "#334155", "line-height": 1.4 }, "Questions? {{payment.email}}"),
    ),
    col("42%",
      r({ padding: [10, 12, 10, 12], "background-color": "#0E7490" },
        col("100%",
          r(
            col("55%",
              s({ "font-size": 7.5, color: "#A5F3FC", "font-weight": "bold", "letter-spacing": 1 }, "TOTAL DUE"),
            ),
            col("45%", { align: "right" },
              s({ "font-size": 7.5, color: "#CFFAFE" }, "by {{dueDate}}"),
            ),
          ),
          r({ margin: [3, 0, 0, 0] },
            col("100%", { align: "right" },
              s({ "font-size": 18, "font-weight": "bold", color: "#FFFFFF" }, "{{summary.total | currency}}"),
            ),
          ),
        ),
      ),
    ),
  ),
  gap(8),

  s({ "font-size": 7.5, color: "#64748B", "line-height": 1.35 }, "{{disclosures}}"),

  ftrPages("{{vendor.name}} · Usage statement {{statementNumber}}"),
);

const sampleData = {
  vendor: {
    name: "Northline Studio, Inc.",
    product: "Northline Platform",
    website: "platform.northline.studio",
  },
  statementNumber: "INV-2026-09-4821",
  account: {
    name: "Cascadia Retail Group",
    id: "ACCT-4821",
    email: "billing@cascadiaretail.com",
  },
  period: { label: "1–30 September 2026" },
  issuedOn: "1 October 2026",
  dueDate: "15 October 2026",
  dailyUsage: [
    { day: 1, api: 1080, dashboard: 420, batch: 240 },
    { day: 2, api: 1370, dashboard: 530, batch: 310 },
    { day: 3, api: 1230, dashboard: 480, batch: 270 },
    { day: 4, api: 880, dashboard: 340, batch: 200 },
    { day: 5, api: 730, dashboard: 280, batch: 170 },
    { day: 6, api: 820, dashboard: 320, batch: 180 },
    { day: 7, api: 1460, dashboard: 570, batch: 330 },
    { day: 8, api: 1410, dashboard: 550, batch: 320 },
    { day: 9, api: 1330, dashboard: 510, batch: 300 },
    { day: 10, api: 1270, dashboard: 490, batch: 290 },
    { day: 11, api: 1050, dashboard: 410, batch: 230 },
    { day: 12, api: 750, dashboard: 290, batch: 170 },
    { day: 13, api: 790, dashboard: 310, batch: 180 },
    { day: 14, api: 1490, dashboard: 580, batch: 340 },
    { day: 15, api: 1440, dashboard: 560, batch: 320 },
    { day: 16, api: 1360, dashboard: 530, batch: 300 },
    { day: 17, api: 1290, dashboard: 500, batch: 290 },
    { day: 18, api: 1140, dashboard: 440, batch: 260 },
    { day: 19, api: 860, dashboard: 330, batch: 200 },
    { day: 20, api: 770, dashboard: 300, batch: 170 },
    { day: 21, api: 1540, dashboard: 600, batch: 340 },
    { day: 22, api: 1480, dashboard: 570, batch: 340 },
    { day: 23, api: 1400, dashboard: 540, batch: 320 },
    { day: 24, api: 1310, dashboard: 510, batch: 300 },
    { day: 25, api: 1160, dashboard: 450, batch: 260 },
    { day: 26, api: 910, dashboard: 350, batch: 200 },
    { day: 27, api: 840, dashboard: 320, batch: 190 },
    { day: 28, api: 1560, dashboard: 600, batch: 360 },
    { day: 29, api: 1510, dashboard: 590, batch: 340 },
    { day: 30, api: 1430, dashboard: 550, batch: 320 },
  ],
  charges: [
    { item: "Platform subscription — base plan, 24 seats", qty: 24, rate: 120, amount: 2880 },
    { item: "Included render allowance — 50,000 renders", qty: 50000, rate: 0, amount: 0 },
    { item: "Overage renders — 7,520 beyond plan", qty: 7520, rate: 0.02, amount: 150.40 },
    { item: "Premium support — Tier 2 SLA", qty: 1, rate: 299, amount: 299 },
    { item: "Custom font licences", qty: 3, rate: 16.33, amount: 48.99 },
    { item: "EU data residency add-on", qty: 1, rate: 75, amount: 75 },
  ],
  summary: {
    subtotal: 3453.39,
    discount: 172.67,
    taxLabel: "Sales tax (8.8%)",
    tax: 288.7,
    total: 3569.42,
    currency: "USD",
  },
  payment: {
    method: "Visa •••• 4242 — auto-pay",
    autoPayDate: "15 October 2026",
    reference: "NL-4821-0926",
    email: "billing@northline.studio",
  },
  disclosures: "Charges cover 1–30 September 2026 and are billed in arrears. Overage renders are metered per successful PDF. Disputes must be raised within 30 days of the statement date. Prices in USD.",
};

Sample data

json
{
  "vendor": {
    "name": "Northline Studio, Inc.",
    "product": "Northline Platform",
    "website": "platform.northline.studio"
  },
  "statementNumber": "INV-2026-09-4821",
  "account": {
    "name": "Cascadia Retail Group",
    "id": "ACCT-4821",
    "email": "billing@cascadiaretail.com"
  },
  "period": {
    "label": "1–30 September 2026"
  },
  "issuedOn": "1 October 2026",
  "dueDate": "15 October 2026",
  "dailyUsage": [
    {
      "day": 1,
      "api": 1080,
      "dashboard": 420,
      "batch": 240
    },
    {
      "day": 2,
      "api": 1370,
      "dashboard": 530,
      "batch": 310
    },
    {
      "day": 3,
      "api": 1230,
      "dashboard": 480,
      "batch": 270
    },
    {
      "day": 4,
      "api": 880,
      "dashboard": 340,
      "batch": 200
    },
    {
      "day": 5,
      "api": 730,
      "dashboard": 280,
      "batch": 170
    },
    {
      "day": 6,
      "api": 820,
      "dashboard": 320,
      "batch": 180
    },
    {
      "day": 7,
      "api": 1460,
      "dashboard": 570,
      "batch": 330
    },
    {
      "day": 8,
      "api": 1410,
      "dashboard": 550,
      "batch": 320
    },
    {
      "day": 9,
      "api": 1330,
      "dashboard": 510,
      "batch": 300
    },
    {
      "day": 10,
      "api": 1270,
      "dashboard": 490,
      "batch": 290
    },
    {
      "day": 11,
      "api": 1050,
      "dashboard": 410,
      "batch": 230
    },
    {
      "day": 12,
      "api": 750,
      "dashboard": 290,
      "batch": 170
    },
    {
      "day": 13,
      "api": 790,
      "dashboard": 310,
      "batch": 180
    },
    {
      "day": 14,
      "api": 1490,
      "dashboard": 580,
      "batch": 340
    },
    {
      "day": 15,
      "api": 1440,
      "dashboard": 560,
      "batch": 320
    },
    {
      "day": 16,
      "api": 1360,
      "dashboard": 530,
      "batch": 300
    },
    {
      "day": 17,
      "api": 1290,
      "dashboard": 500,
      "batch": 290
    },
    {
      "day": 18,
      "api": 1140,
      "dashboard": 440,
      "batch": 260
    },
    {
      "day": 19,
      "api": 860,
      "dashboard": 330,
      "batch": 200
    },
    {
      "day": 20,
      "api": 770,
      "dashboard": 300,
      "batch": 170
    },
    {
      "day": 21,
      "api": 1540,
      "dashboard": 600,
      "batch": 340
    },
    {
      "day": 22,
      "api": 1480,
      "dashboard": 570,
      "batch": 340
    },
    {
      "day": 23,
      "api": 1400,
      "dashboard": 540,
      "batch": 320
    },
    {
      "day": 24,
      "api": 1310,
      "dashboard": 510,
      "batch": 300
    },
    {
      "day": 25,
      "api": 1160,
      "dashboard": 450,
      "batch": 260
    },
    {
      "day": 26,
      "api": 910,
      "dashboard": 350,
      "batch": 200
    },
    {
      "day": 27,
      "api": 840,
      "dashboard": 320,
      "batch": 190
    },
    {
      "day": 28,
      "api": 1560,
      "dashboard": 600,
      "batch": 360
    },
    {
      "day": 29,
      "api": 1510,
      "dashboard": 590,
      "batch": 340
    },
    {
      "day": 30,
      "api": 1430,
      "dashboard": 550,
      "batch": 320
    }
  ],
  "charges": [
    {
      "item": "Platform subscription — base plan, 24 seats",
      "qty": 24,
      "rate": 120,
      "amount": 2880
    },
    {
      "item": "Included render allowance — 50,000 renders",
      "qty": 50000,
      "rate": 0,
      "amount": 0
    },
    {
      "item": "Overage renders — 7,520 beyond plan",
      "qty": 7520,
      "rate": 0.02,
      "amount": 150.4
    },
    {
      "item": "Premium support — Tier 2 SLA",
      "qty": 1,
      "rate": 299,
      "amount": 299
    },
    {
      "item": "Custom font licences",
      "qty": 3,
      "rate": 16.33,
      "amount": 48.99
    },
    {
      "item": "EU data residency add-on",
      "qty": 1,
      "rate": 75,
      "amount": 75
    }
  ],
  "summary": {
    "subtotal": 3453.39,
    "discount": 172.67,
    "taxLabel": "Sales tax (8.8%)",
    "tax": 288.7,
    "total": 3569.42,
    "currency": "USD"
  },
  "payment": {
    "method": "Visa •••• 4242 — auto-pay",
    "autoPayDate": "15 October 2026",
    "reference": "NL-4821-0926",
    "email": "billing@northline.studio"
  },
  "disclosures": "Charges cover 1–30 September 2026 and are billed in arrears. Overage renders are metered per successful PDF. Disputes must be raised within 30 days of the statement date. Prices in USD."
}

Render it with your data

Any API key can render this template by its ID. This call uses the sample data; add a data object to use your own. See the API reference.

bash
curl -X POST https://makespdf.com/api/v1/render \
  -H "Authorization: Bearer $MAKESPDF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"templateId": "statement-usage"}' \
  -o output.pdf