Invoice writes require a signed-in user
What changed
All six invoice write routes now require a signed-in user: create, update, status change, record payment, reverse payment, and delete. An API key calling any of them receives a 403 instead of proceeding. Reading invoices with an API key is unchanged.
curl -X POST https://api.speybooks.com/v1/invoices \
-H "Authorization: Bearer sk_live_...redacted" \
-d '{ "invoiceType": "sales", "contactId": "cont_42", "issueDate": "2025-01-15", "dueDate": "2025-02-15", "lines": [ ... ] }'
{
"success": false,
"error": {
"code": "forbidden",
"message": "Invoice write routes require a signed-in user"
}
}
Why it matters
Writing an invoice moves your ledger, so it needs an accountable person behind it, not a machine credential. This release makes that rule explicit and brings invoices in line with the credit-note routes, which already hold the same posture. Before now, an API key reaching a write route would receive a 500 rather than a clean refusal — the route faulted instead of refusing. You now get a precise, documented 403 up front, so you know API keys are read-only on invoices and can build your integration around a user session for anything that changes one.
Proof and impact
The check runs at the API route layer and returns before any change is made and before an idempotency key is claimed, so a refused call leaves nothing behind: no partial write, no consumed key, no drawn document number. It is verified by route-level tests that exercise all six write routes with a key and confirm the refusal, alongside the normal signed-in paths. The live smoke test confirms 403 on write and 200 on read with the same key.
Breaking changes: none for a working integration, because an API key could not successfully write an invoice before this release. If you have code that calls these routes with a key, move it to a user session.