Agent SDK
Invoicing for things that don't use browsers.
One bearer key. Plain HTTPS. No OAuth dance, no cookies, no session, no headless Chrome. An agent can create a client, issue an invoice, send it, and hand back a payable link — in four calls.
curl -O https://invoiceflow-ten-azure.vercel.app/sdk/invoiceflow-client.mjs
import { InvoiceFlowClient } from "./invoiceflow-client.mjs";
const iflow = new InvoiceFlowClient({ apiKey: process.env.INVOICEFLOW_API_KEY });
const { client } = await iflow.createClient({ name: "Acme", email: "ap@acme.example" });
const { invoice } = await iflow.createInvoice({ clientId: client.id, /* ... */ });
const { url } = await iflow.shareInvoice(invoice.id);Verify it yourself, without a key
The security claims below are assertions you can run right now. No account, no credential, no secrets — every one of them is a refusal, which is exactly what makes them safe to publish.
$ node verify-no-key.mjs ok anon GET /api/invoices -> 401 ok anon GET /api/clients -> 401 ok anon GET /api/dashboard/stats -> 401 ok forged key POST /api/clients -> 401 ok malformed key rejected locally ok anon POST /api/keys -> 401 ok constructor refuses empty apiKey 8 passed, 0 failed
Download: /sdk/verify-no-key.mjs
The verifier above proves everything that must be refused. It cannot prove the thing the product actually claims — that an authenticated caller gets a payable link. That is a separate claim, so it gets a separate script.
$ node verify-paid-path.mjs # dry run: no key, no writes, no egress === DRY RUN — nothing sent, no account touched === 1. GET /api/me prove the key is live 2. POST /api/clients create a uniquely tagged client 3. POST /api/invoices create invoice (items + dueDate + currency) 4. assert server-derived total == 25000 cents 5. POST /api/invoices/:id/share assert url is https + *.stripe.com 6. (skipped) email — pass --send to exercise delivery $ INVOICEFLOW_API_KEY=ifk_... node verify-paid-path.mjs [PASS] authenticated: GET /api/me responded [PASS] created client [PASS] payload carries no client-supplied totals [PASS] created invoice [PASS] server-derived total matches request [PASS] share returned a url field [PASS] url is https and on stripe.com 7 passed, 0 failed
Download: /sdk/verify-paid-path.mjs · runs without a key, refuses a key that cannot be valid before transmitting it, and defaults to DRAFT so no email is sent unless you pass --send.
MCP server
An agent should not have to read a REST document to send an invoice. This ships as a Model Context Protocol server with no dependencies — seven tools, added to a host in six lines of config:
{
"mcpServers": {
"invoiceflow": {
"command": "node",
"args": ["/abs/path/invoiceflow-mcp.mjs"],
"env": { "INVOICEFLOW_API_KEY": "ifk_..." }
}
}
}/sdk/invoiceflow-mcp.mjs · /sdk/test-mcp.mjs — 14 conformance assertions, no key and no network required.
The tool set is deliberately narrow. Nothing can send email, delete, refund, or pay: creation is additive and a human can undo it, while an agent that emails a customer on every retry cannot take that back. Amounts are integer cents named unitPrice, and a non-integer is refused with an explanation instead of being rounded, because 25.5 silently meaning 25 cents is the defect most likely to reach a paying customer.
How keys work
- Format
ifk_<64 hex>, shown once at creation. - Only a SHA-256 hash and a non-secret 12-char prefix are stored. The key is unrecoverable — revoke and re-issue.
- Scoped to exactly one account. It is a machine credential, not a session.
- It cannot move funds. It can create a payable invoice; it cannot pay one.
Endpoints
/api/invoicesList invoices (paginated)/api/invoicesCreate an invoice/api/invoices/:idRead one invoice/api/invoices/:id/sendEmail it to the client/api/clientsList clients/api/clientsCreate a client/api/dashboard/statsAccount + countsIf you are an autonomous agent
There is no idempotency key. A retriedcreateInvoice after a timeout can duplicate a real invoice. Read back before retrying.
Sending has side effects. sendInvoice emails a human. Do not retry it blindly.
Cents, not dollars. unitPrice: 25000 is $250.00.
404 means “not yours” too. The API refuses to disclose whether another account's invoice exists.
Full error table, response envelopes, and the measured behaviour list are in the README. To issue invoices, you need an API key from Settings.