Create and issue a credit note
What changed
Two new endpoints complete the credit-note lifecycle over the API.
POST /v1/credit-notes creates a credit note. With status omitted or set to
draft, it records the note and its lines with no ledger effect. With
status: "issued", it also posts the issue journal in the same request, so you can
create and issue in a single call.
curl -X POST https://api.speybooks.com/v1/credit-notes \
-H "Authorization: Bearer $TOKEN" \
-d '{
"creditNoteType": "sales",
"contactId": "cont_42",
"issueDate": "2026-06-18",
"reason": "Goods returned",
"lines": [
{ "description": "Returned widget", "quantity": "1",
"unitPrice": "100.00", "vatRate": 20, "accountId": "acc_4000" }
]
}'
{
"data": {
"creditNoteId": "cn_318",
"creditNoteNumber": "CN-0001",
"status": "draft"
}
}
POST /v1/credit-notes/{id}/issue issues a draft, moving it to issued and posting
the journal.
curl -X POST https://api.speybooks.com/v1/credit-notes/cn_318/issue \
-H "Authorization: Bearer $TOKEN"
{
"data": { "creditNoteId": "cn_318", "status": "issued" }
}
Why it matters
Until now you could read, allocate, de-allocate and void credit notes over the API, but the entry point was missing: a note had to exist before any of that applied. These two endpoints close that gap, so the whole credit-note lifecycle is now yours to drive programmatically, from creation through to settlement or void. Create a draft and review it before it touches your VAT position, or create and issue in one call when you already know the figures. Either way the document number is drawn only once the note is committed, so a rejected request never burns a number and your credit-note sequence stays gap-free for your records and your auditor.
Proof and impact
A create-as-issued and a standalone issue both post the issue journal inside the same database transaction as the state change, so a note cannot be issued without its journal or carry a journal without being issued. That equivalence is verified in both directions on every deploy. Issuing requires every line to carry an account; a create-as-issued with an unmapped line is rejected before the document number is drawn (enforced in the credit-note schema, migration 107).
Both endpoints are idempotent under an Idempotency-Key: a replay returns the
original note rather than creating a second. Both require a signed-in user; an API
key is refused with 403, the same posture as void and de-allocation, so every
credit-note creation and issue carries a named human actor on its audit record.
Money on the wire is exact: line quantity and unitPrice are decimal strings and
vatRate is an integer percentage, never a floating-point number.
Breaking changes: none. These are additive endpoints.
Known issues
None.