InvoiceFlow

Machine credentials

InvoiceFlow for agents

InvoiceFlow accepts a scoped bearer API key on its JSON API, so an autonomous agent or a script can create and manage invoices with no browser session. Every key belongs to exactly one human account — the agent acts as that account, with that account’s permissions, and nothing more.

1 A human issues the key, once

Sign in, then call POST /api/keys with { "name": "my-agent" }. The response contains the key exactly once:

HTTP 201
{ "key": "ifk_<64 hex>", "prefix": "ifk_ab12cd34",
  "warning": "This key is shown once and cannot be recovered." }

Store it in the agent’s secret store and send it as Authorization: Bearer ifk_<64 hex>. If it is lost, issue a new one — it cannot be recovered. Revoking is immediate.

Why a key cannot mint a key. POST /api/keys and DELETE /api/keys/{id} are session-only on purpose. A leaked agent key can therefore never replicate its own credentials — it can do invoice work until a human revokes it, and that is all.

2 What a key can do

MethodPathPurpose
GET/api/invoiceslist invoices (paginated)
POST/api/invoicescreate an invoice
GET/api/invoices/{id}read one invoice
PATCH/api/invoices/{id}status transition (DRAFT to SENT)
PUT/api/invoices/{id}edit a draft
GET/api/invoices/{id}/pdfrender the PDF (binary, %PDF-)
GET/api/clientslist clients
POST/api/clientscreate a client
GET/api/estimateslist estimates
POST/api/estimatescreate an estimate
POST/api/estimates/{id}/convertestimate to invoice
GET/api/dashboard/statsaggregate totals
GET/api/export/invoicesCSV export
GET/api/export/clientsCSV export

Deliberately not available to a key

  • POST /api/keys — a key cannot mint another key
  • DELETE /api/keys/{id} — revocation is human-only
  • POST /api/invoices/{id}/send — sends email, session only
  • POST /api/invoices/{id}/share — mints a public link, session only
  • /api/stripe/connect — binds a payment account to a human

3 Create an invoice

curl -X POST https://invoiceflow-ten-azure.vercel.app/api/invoices \
  -H "Authorization: Bearer $INVOICEFLOW_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "clx...",
    "dueDate": "2026-12-02",
    "currency": "USD",
    "tax": 0,
    "notes": "work performed by an autonomous agent",
    "items": [
      { "description": "Agent integration work",
        "quantity": 2, "unitPrice": 150 }
    ]
  }'
HTTP 201
{
  "invoice": {
    "id": "cmvy...",
    "invoiceNumber": "INV-202611-0001",
    "subtotal": 300,
    "tax": 0,
    "total": 300,
    "status": "DRAFT"
  }
}

Payload rules

The schema is strict: unknown keys are rejected with 400, not ignored. issueDate is not accepted — it is derived server-side; only dueDate is sent.

  • Totals and the invoice number are always server-derived in integer cents. A client-supplied total is rejected.
  • items takes 1–500 entries; quantity × unit price is rounded once per line, then summed in cents.
  • status defaults to DRAFT; the other accepted value is SENT.

4 Errors an agent must handle

CodeMeaningCorrect reaction
400bad payload or unknown keyfix the body, do not retry blindly
401missing, malformed, or revoked keystop; ask the human for a new key
404not found in your accountverify the id
409state conflict (editing a non-draft)re-read, then decide
429rate limitedback off

5 Verified against production

The following was executed against the live site with a real database record, then the test record was deleted:

no auth                       -> 401
bogus bearer                  -> 401
valid key  POST /api/invoices -> 201  INV-202611-0001  total 300  DRAFT
GET  /api/invoices            -> 200  (reads the same invoice)
GET  /api/invoices/{id}/pdf   -> 200  2345 bytes  magic %PDF-
GET  /api/dashboard/stats     -> 200
POST /api/keys using a key    -> 401  (cannot mint credentials)

6 Security notes

  • Keys are stored only as SHA-256 hashes. The plaintext exists once, at creation, and is never retrievable.
  • A short prefix is kept so a human can identify a key in a list without revealing it.
  • Lookup is by hash; lastUsedAt is updated on use for auditing.
  • Setting revokedAt stops a key immediately — no restart, no cache to wait out.

Ready to let an agent run your invoicing?

Sign in, create a key, and hand it to your agent. The key can only do invoice work, and you can revoke it at any time.

Sign in to issue a key