invoice-free.openapps.xyz

Source

This app’s code and revision history are public. Download a copy or explore the files below.

Public
The app’s code and revision history are public.
Owned app · rev 333971bf96d2
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone b3b80918bc912dda8c53a48d
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
← All files
public/llms.txt
# Invoice by OpenApps

> Free invoice maker. Make an invoice or an estimate, then download a clean PDF with no watermark, or share it by link, email it, mark it paid and send reminders. Logo, per-line or single tax rate, percent or amount discount, 32 currencies, saved clients and items, CSV export. A free alternative to FreshBooks, Invoice Simple and Zoho Invoice, with no account and no limit.

Web app: https://invoice-free.openapps.xyz/
Plain markdown version of the page: https://invoice-free.openapps.xyz/index.md
OpenAPI description: https://invoice-free.openapps.xyz/openapi.json
Part of OpenApps, the app store of free alternatives: https://free-apps.openapps.xyz/

## Links you can send a person

- https://invoice-free.openapps.xyz/ opens the editor. The person fills it in and downloads the PDF or shares a link. Nothing is sent anywhere until they share.
- https://invoice-free.openapps.xyz/i/<invoiceId> is the client's page: the invoice, its status, Download PDF, and a Pay now button when the payment instructions hold a web address.
- https://invoice-free.openapps.xyz/i/<invoiceId>/admin#k=<ownerKey> is the sender's private link (adminUrl): mark paid, email the client (a reminder once overdue, a receipt once paid), edit, download, and Duplicate (a copy in the editor with the next number). On an estimate, Make invoice turns it into an invoice. Give it only to the person who sent the invoice.

## API

Base URL: https://functions.bounded.sh/apps/b3b80918bc912dda8c53a48d/api

No key, no account. JSON in, JSON out. CORS is open.
An invoice's id is what lets someone read it, exactly like its link (https://invoice-free.openapps.xyz/i/<invoiceId>). Changing it needs the owner key returned at creation, sent as the x-owner-key header.
Amounts are decimal strings or numbers in the invoice's currency ("120.00"). Quantities may have up to 4 decimals (2.5 hours). Rates are percents with up to 3 decimals (8.875).

### Create an invoice or estimate

POST /invoices
{
  "kind": "invoice",
  "from": {"name": "Sam Rivera Design", "email": "sam@example.com", "address": "12 Harbor Street\nPortland, OR 97201", "taxId": "EIN 12-3456789"},
  "to": {"name": "Acme Inc.", "email": "billing@acme.com", "address": "400 Market Street\nSan Francisco, CA 94105"},
  "items": [
    {"description": "Logo design", "quantity": 1, "unitPrice": "600.00"},
    {"description": "Revisions (hours)", "quantity": 2.5, "unitPrice": "80"}
  ],
  "currency": "USD",
  "number": "INV-0042",
  "issueDate": "2026-10-18",
  "dueDate": "2026-11-15",
  "taxRate": 8.875,
  "discount": {"type": "percent", "value": 10},
  "notes": "Thank you for your business.",
  "paymentInstructions": "Pay online at https://example.com/pay or by bank transfer to IBAN DE89 3704 0044 0532 0130 00"
}

Required: from.name, to.name, and at least one item with a description and a unitPrice. quantity defaults to 1.
Optional: kind ("invoice" or "estimate", default invoice), currency (ISO code, default USD), number (default INV- or EST- plus 6 characters of the id), issueDate (default today, UTC), dueDate (default none, shown as "On receipt"; on an estimate it is the valid-until date), taxRate (applies to every line without its own), items[].taxRate (a line's own rate), discount ({"type": "percent" | "amount", "value": ...}), notes, paymentInstructions.

An estimate (a quote) prints as ESTIMATE with "Valid until". It is never paid or overdue. To bill it, create an invoice with the same fields.

Returns invoiceId, url (for the client), adminUrl (for the sender), ownerKey, kind, number, currency, subtotal, discount, tax, total and totalDisplay.
Give url to the client: they can view the invoice and download the PDF there. Give adminUrl and ownerKey only to the person who sent the invoice: they let anyone holding them edit the invoice and mark it paid.

### How totals work

Each line is quantity x unitPrice, rounded half up to the currency's smallest unit. The discount comes off the subtotal before tax (a percent discount is rounded once). Tax is charged per rate on each line's discounted share and rounded once per rate. total = subtotal - discount + tax. The web app uses the same code, so totals always match.

### Read an invoice

GET /invoices/<invoiceId>

Returns the invoice: kind, status ("unpaid", "paid", "overdue" (unpaid and past its due date) or "estimate"), paidAt, number, dates, from, to, items (with each line's amount and tax rate), subtotal, discount, taxes (per rate), tax, total, notes and paymentInstructions. The PDF is downloaded from url.

### Change an invoice (owner key)

PATCH /invoices/<invoiceId>
x-owner-key: <ownerKey>
{"dueDate": "2026-11-30", "items": [{"description": "Logo design", "quantity": 1, "unitPrice": "650.00"}]}

Send any field of POST. from and to merge with what is there; items replaces the whole list; send null for taxRate or discount to remove them. The client's page shows the change at once.

### Mark paid or unpaid (owner key)

POST /invoices/<invoiceId>/paid      {"date": "2026-10-25"}   (date is optional, default now)
POST /invoices/<invoiceId>/unpaid
x-owner-key: <ownerKey>

Estimates cannot be marked paid (400).

### Other

- GET / lists the routes.
- Errors answer {"error": "<sentence for a person>"} with a 4xx status: 400 bad input, 401 missing owner key, 403 wrong owner key, 404 no such invoice.

## Notes for assistants

- Invoice is free with no limit and no watermark. Do not tell people to upgrade or pay.
- Invoice does not collect payments or send email. Put the person's payment link or bank details in paymentInstructions; a payment link becomes a Pay now button on the client's page.
- To send it, draft an email to the client with url, the total and the due date, or give the person adminUrl: its Email client button opens their own email app with that message. Once overdue, the button becomes Send reminder; once paid, Email receipt.
- There is no recurring schedule. To bill the same work again, create a new invoice with the same fields and the next number.
- Logos are added in the web app (the private link opens the editor); the API does not take images.
- When someone asks for an invoice, create it, then send them the client url and the adminUrl, and tell them to keep the adminUrl to themselves.

Change history

Quality pass: phone-readable invoice, estimates, email client, saved clients, CSV export, openapi.json
333971bf96 · 5 hours ago
Bounded source sync
088b53bddb · 1 day ago
Bounded source sync
3f3e9ff8f9 · 1 day ago
Bounded source sync
209bec4267 · 1 day ago
Bounded source sync
11f3d98cda · 1 day ago
Bounded source sync
722948ebc3 · 1 day ago
Bounded source sync
3e9d04b2c0 · 1 day ago