POST structured invoice data, get a branded, tax-correct PDF and the computed breakdown. Base URL in production: https://api.cginvoicer.com/v1. Locally, routes live under /api/v1.
This is v1: single-invoice generation and the supporting API. CSV batch, the SEO funnel, live Stripe billing and the stateful tier (saved clients, gapless numbering) are fast-follows.
The hard part — and the moat — is the calculation engine: it computes line totals, discounts, per-rate tax and the grand total deterministically, in integer minor units, and returns the breakdown in the response.
Clone, configure Postgres, mint a key, and run:
npm install
cp .env.example .env # set DATABASE_URL, etc.
npm run db:push # create tables
npm run keys:mint -- --email [email protected] --plan starter --live
npm run dev # http://localhost:3037keys:mint prints the key once. Without --live it mints a sandbox sk_test_ key. Then call the API:
curl -s http://localhost:3037/api/v1/invoices \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d @examples/eu-invoice.json | jqStorage defaults to a local driver (no cloud credentials needed); set the S3_* environment variables to switch to Cloudflare R2 or S3.
Pass your key as a bearer token: Authorization: Bearer sk_live_.... Keys are hashed at rest (only a sha256 hash and a non-secret prefix are stored). sk_test_ keys run in sandbox/test mode and sk_live_ keys in production; both produce clean PDFs.
All money is integer minor units (cents) on the wire; currencyis an ISO-4217 code. Prices are tax-exclusive (net) — tax is added on top and shown separately. Display formatting (symbol, grouping, decimals) happens only at render time via Intl.NumberFormat.
Rounding policy: per-line + per-tax-rate, half-up.
line_subtotal = round(quantity × unit_price) per line.subtotal = Σ line nets.grand_total = taxable + tax_total + shipping.Numbering caveat:v1 is stateless — you supply invoice.number; we validate its format only, never sequence or dedupe. Gapless per-customer numbering is a stateful-tier feature.
| Method | Path | Description |
|---|---|---|
| GET | /v1/templates | List visual themes. |
| GET | /v1/locales | List tax locales with their required fields. |
| GET | /v1/document-types/invoice?locale=EU | The merged JSON Schema for a locale. |
| POST | /v1/invoices | Generate one invoice. 201 JSON, or a PDF via format:"pdf" / Accept: application/pdf. |
| POST | /v1/invoices/validate | Validate input and return computed totals. Free, no render, no metering. |
| GET | /v1/invoices/{id} | Invoice metadata. |
| GET | /v1/invoices/{id}/download | Re-signed redirect to the PDF. |
Each locale carries its own required fields, mandatory document wording and tax presentation. Miss a required field and you get a 422.
| Locale | Tax | Title | Required | Notes |
|---|---|---|---|---|
| US | Sales Tax | Invoice | - | Single rate; EIN optional. |
| EU | VAT | Invoice | seller.vat_id | Per-rate VAT breakdown; reverse charge. |
| AU | GST | Tax Invoice | seller.abn | Mandatory “Tax Invoice” wording. |
| GENERIC | Tax | Invoice | - | Arbitrary rate, or none. |
Three typeset themes compose with your logo and accent colour: classic (serif, ruled), modern (accent header band), and minimal (monochrome, hairline). Pass theme and an optional accent_color in the request. Line tables repeat their header across pages and keep totals from orphaning.
Errors return { "error": { "code", "message", "errors?" } } with a relevant status:
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Structural schema error (base schema). |
| 401 | unauthorized | Missing or invalid API key. |
| 402 | quota_exceeded | Over the plan's monthly quota. |
| 422 | unprocessable | Unsupported locale/theme, or a locale-mandated field missing. |
| 429 | rate_limited | Rate limit exceeded. |
Usage is metered per calendar month; exceeding your plan returns a 402.
| Plan | Price | Invoices / mo | Notes |
|---|---|---|---|
| Free / Test | $0 | 10 | sk_test_ keys, sandbox. |
| Starter | $19 | 100 | Live keys, all themes/locales. |
| Pro | $79 | 500 | + batch CSV, webhook on generate. |
| Scale | $249 | 2,500 | + priority render, higher rate limit. |
| Overage | $0.10/ea | - | Metered pay-as-you-go. |