← Blog

Claude API Invoice Generation Example

2026-10-14 · 5 min read · SubToAPI Team

Can you generate invoices with the Claude API?

Yes. Claude doesn't ship a built-in "invoice" feature, but it's very good at two things invoice generation needs: turning messy order data into clean structured output, and writing the surrounding invoice text (line item descriptions, payment terms, notes) in a consistent tone. The practical pattern is to send Claude your order/customer data, force it to return structured JSON using tool use, and then render that JSON into an HTML or PDF invoice with a template engine on your side.

This article walks through a concrete example: a prompt and tool schema that takes raw order data and returns a complete invoice object, plus the code to call it. If you're prototyping a billing feature, a freelancer invoicing tool, or an internal finance bot, this is the pattern to copy.

The core idea: structured output, not freeform text

Don't ask Claude to "write an invoice" and parse the prose — that's fragile. Instead, define a tool (function) with the exact invoice shape you need, and let Claude's tool-use feature fill it in. This gives you a reliable JSON object every time, which you can then feed into a PDF renderer (Puppeteer, wkhtmltopdf, a React template, whatever you already use for documents).

Here's a tool schema for a basic invoice:

{
  "name": "create_invoice",
  "description": "Generate a structured invoice from order data",
  "input_schema": {
    "type": "object",
    "properties": {
      "invoice_number": { "type": "string" },
      "issue_date": { "type": "string" },
      "due_date": { "type": "string" },
      "customer": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "email": { "type": "string" },
          "address": { "type": "string" }
        },
        "required": ["name", "email"]
      },
      "line_items": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "description": { "type": "string" },
            "quantity": { "type": "number" },
            "unit_price": { "type": "number" },
            "total": { "type": "number" }
          },
          "required": ["description", "quantity", "unit_price", "total"]
        }
      },
      "subtotal": { "type": "number" },
      "tax": { "type": "number" },
      "total_due": { "type": "number" },
      "notes": { "type": "string" }
    },
    "required": ["invoice_number", "customer", "line_items", "total_due"]
  }
}

Example request

A generic call against the Claude Messages API with tool use looks like this:

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 1024,
    "tools": [ /* the create_invoice schema above */ ],
    "tool_choice": {"type": "tool", "name": "create_invoice"},
    "messages": [
      {
        "role": "user",
        "content": "Create an invoice for Acme Corp (billing@acme.com). They ordered 10 hours of consulting at €85/hour and 1 software license at €240. Due in 14 days. Invoice number INV-2041."
      }
    ]
  }'

Claude returns a tool_use block with structured input matching the schema — correct math, consistent formatting, and a filled-in due date. You take that JSON and render it with your invoice template. This works just as well for recurring invoices: pass last month's usage data and a prompt like "generate the monthly invoice for this subscription" and Claude handles the line-item phrasing while your code handles the arithmetic verification.

If you're routing through SubToAPI

If your team already uses Claude through SubToAPI instead of calling Anthropic directly, the request shape is nearly identical — you're just pointing at https://api.subtoapi.app/v1/messages with an application key (sub_live_...) instead of a raw Anthropic key. That's useful for invoice generation specifically because:

const response = await fetch("https://api.subtoapi.app/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    model: "claude-sonnet-4-5",
    max_tokens: 1024,
    tools: [createInvoiceSchema],
    tool_choice: { type: "tool", name: "create_invoice" },
    messages: [
      { role: "user", content: orderSummaryPrompt }
    ]
  })
});

const data = await response.json();
const invoice = data.content.find(b => b.type === "tool_use").input;

Full request/response details, including streaming and error codes, are in the docs and the tool use guide. If you're setting this up for the first time, the quickstart covers getting your key and making your first call in a few minutes.

Rendering the invoice

Once you have the structured JSON, rendering is a normal templating problem, not an AI problem:

  1. Validate the numbers server-side — don't trust total_due blindly, recompute it from line items before anything touches real money.
  2. Feed the validated JSON into an HTML template (Handlebars, React server component, etc.).
  3. Convert to PDF with Puppeteer, wkhtmltopdf, or a hosted PDF API.
  4. Store the JSON alongside the generated document so you can regenerate or audit it later without re-prompting Claude.

That validation step is the one non-negotiable part of this pattern. LLMs are good at formatting and phrasing, not at being your arithmetic source of truth — always recompute totals in code before an invoice goes out.

Keep prompts tight for consistency

For invoice generation specifically, a short, explicit system prompt helps a lot more than a long one:

You generate structured invoices only. Always compute totals correctly
from quantity × unit_price. Use ISO date format. Never invent customer
details not provided in the input. If tax rate is not specified, set
tax to 0.

This prevents Claude from "helpfully" inventing a tax rate or rounding oddly, which is the most common failure mode when teams first automate invoicing with an LLM.

questions

Can Claude generate a PDF invoice directly? No — Claude returns text or structured JSON. You render the PDF yourself using that output with a templating/PDF library like Puppeteer or wkhtmltopdf.

Is tool use required, or can I just ask for JSON in plain text? You can ask for plain JSON, but tool use enforces a schema, which means more reliable parsing and fewer malformed responses, especially at volume.

Should Claude calculate tax and totals? Have it fill in the fields, but always recompute totals and tax server-side before issuing the invoice — don't trust model arithmetic for anything that touches billing.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →