For developers · For agents

API & MCP reference

Every action a human can take in Flow Invoicer is also available programmatically. Issue invoices from Slack. Hand a scoped key to your AI agent. Reconcile payments from a script.

Available on the Pro plan

Bearer token auth

Generate a ddm_live_* key in Settings → API keys. Send it on every request.

MCP server built-in

Point Claude Code, Cursor, or your own MCP client at https://ddmflow.com/mcp.

Org-isolated

Every request is scoped to your workspace. Row-level security in Postgres.

Authentication

All API requests require an API key in the Authorization header. Keys start with ddm_live_ and are scoped to the workspace they were created in. Each key is shown once at creation — store it somewhere safe.

All requestsbash
curl https://ddmflow.com/api/clients \
  -H "Authorization: Bearer ddm_live_xxxxxxxxxxxxxxxxxxxx"

Generate one at Settings → API keys (Pro plan or higher).

Base URL

https://ddmflow.com/api

All API endpoints sit beneath this base. JSON request + response.

MCP server

Flow Invoicer ships a built-in MCP server so your AI agent can call tools directly — no glue code required. Same auth, same tier gating.

Claude Codebash
claude mcp add --transport http flow-invoicer https://ddmflow.com/mcp \
  --header "Authorization: Bearer ddm_live_xxxxx"

The MCP server exposes the same tools as the REST API: list_clients, create_invoice, record_payment, download_invoice_pdf, and more. The model handles tool calls automatically.

Issue an invoice

The headline endpoint. Two ways to specify the customer: client_id (existing) or client(inline new). Totals are computed server-side from your line items — don't pass totals; they'll be overwritten.

POST /api/invoicesbash
curl -X POST https://ddmflow.com/api/invoices \
  -H "Authorization: Bearer ddm_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "client": { "name": "Acme Corp", "email": "billing@acme.com" },
    "payment_terms": "Net 30",
    "items": [
      { "description": "May consulting", "quantity": 12, "unit_price": 750 },
      { "description": "Project setup", "quantity": 1,  "unit_price": 1500 }
    ]
  }'
Response 201json
{
  "id": "8a9c0b40-…",
  "invoice_number": "INV-007",
  "status": "draft",
  "client": { "name": "Acme Corp", "email": "billing@acme.com" },
  "subtotal": 10500,
  "tax_amount": 1575,
  "total_amount": 12075,
  "currency": "ZAR",
  "due_date": "2026-06-14"
}

Endpoint inventory

Full REST surface — paste this into your editor and let your AI agent discover the rest:

MethodPathDescriptionTier
GET/api/clientsList clients
POST/api/clientsCreate client
GET/api/clients/{id}Get client
PUT/api/clients/{id}Update client
DELETE/api/clients/{id}Delete client
GET/api/itemsList catalogue items
POST/api/itemsCreate catalogue item
GET/api/invoicesList invoices (filter by status)
POST/api/invoicesCreate invoice ⭐
GET/api/invoices/{id}Get invoice with lines
PATCH/api/invoices/{id}/statusChange status
POST/api/invoices/{id}/paymentsRecord payment
POST/api/invoices/{id}/pdfGenerate PDF + signed URL
POST/api/invoices/{id}/sendEmail invoice to client
GET/api/quotesList quotesadvanced
POST/api/quotesCreate quoteadvanced
POST/api/quotes/{id}/convertConvert to invoiceadvanced
GET/api/recurring-invoicesList recurring schedulesadvanced
POST/api/recurring-invoicesCreate scheduleadvanced
POST/api/recurring-invoices/{id}/generateGenerate next nowadvanced
GET/api/expensesList expensesadvanced
POST/api/expensesCreate expenseadvanced
GET/api/reports/revenueMonthly revenue (12mo)pro
GET/api/reports/agingAging bucketspro
GET/api/reports/top-clientsTop 10 by revenuepro
GET/api/reports/taxVAT collected vs accruedpro

Rate limits & errors

Soft limit: ~120 requests/minute per API key. Hit it and you'll receive HTTP 429 with a rate_limited code. Wait a few seconds and retry.

All errors follow a consistent shape:

Error responsejson
{
  "error": {
    "code": "validation_error",
    "message": "client_id is required",
    "details": { "field": "client_id" }
  }
}

Error codes: unauthorized (401), forbidden (403), not_found (404), gone (410, expired portal link), validation_error (422), subscription_required(402, your tier doesn't include this), rate_limited (429), conflict (409), internal_error (500).

Ready to build

Get your API key

Sign in, upgrade to Pro (or use your 7-day trial), generate a scoped key, and you're off.

Open API Keys