Quotes
Quotes are proposals sent to contacts before work begins. They are first-class records stored independently of invoices, with their own numbering sequence and an enforced lifecycle. Converting an accepted quote produces a draft sales invoice linked back to the quote in both directions.
Lifecycle
draft → sent | declined
sent → accepted | declined | expired
accepted → converted | declined
- draft: editable and deletable; the only state a quote can be
created in (besides
sent) and the only state it can be edited or deleted in. No transition returns a quote to draft. - sent: delivered to the contact; immutable.
- accepted: the contact has agreed; eligible for conversion.
- declined: the contact rejected the quote; terminal.
- converted: an invoice has been created from this quote; terminal. Reachable only through the convert endpoint, never through the status endpoint.
- expired: reserved for future expiry automation. No API path currently produces it; see Expiry below.
Transitions are enforced server-side. A request for a transition outside the table above is rejected. Clients do not need to (and cannot) police the lifecycle themselves.
Conversion
Converting an accepted quote runs as a single atomic transaction:
the quote is locked, a draft sales invoice is created with a new
invoice number from the standard sequence, every line is copied
penny for penny (the quote and invoice calculation paths share the
same arithmetic, so the copy holds by construction), the quote and
invoice are linked to each other in both directions, the quote
status advances to converted, and two audit records are written.
All of it commits or none of it does.
Amounts
Two formats, by direction:
- Requests carry
quantityandunitPriceas exact decimal strings (for example"2.5"and"100.00"), both strictly positive, withunitPricein pounds.vatRateis an integer percent (0 to 100). Sending numbers instead of strings, or non-positive values, is rejected. - Responses carry computed amounts (
subtotal,vatAmount,total, and per-linenetAmount,vatAmount,lineTotal) as integers in minor units (pence).quantityandunitPriceare echoed back as exact decimal strings at stored precision.
All calculation happens server-side with exact decimal arithmetic. The preview endpoint uses the identical calculation path to create, so what you preview is what you get on submit.
Numbering
Quotes use the pattern QT-{sequence} (e.g. QT-0001). Numbers are
issued atomically from the organisation's document sequence; the
sequence is monotonically increasing and independent of year, and
duplicate numbers are impossible under concurrent creation
(AX-CON-001).
Expiry
Quotes have a validUntil date. A sent quote past its
validUntil keeps the status sent; it is not automatically
transitioned. Expiry is surfaced two ways: the list endpoint's
expiredCount stat counts sent quotes past their date, and
status=expired is accepted as a list filter for the same set.
Endpoints
https://app.speybooks.com/api/v1/quotes/List quotesPOSThttps://app.speybooks.com/api/v1/quotes/Create a new quotePOSThttps://app.speybooks.com/api/v1/quotes/previewPreview quote calculationsGEThttps://app.speybooks.com/api/v1/quotes/{id}Get quote detailsPUThttps://app.speybooks.com/api/v1/quotes/{id}Update a draft quoteDELETEhttps://app.speybooks.com/api/v1/quotes/{id}Delete a draft quotePOSThttps://app.speybooks.com/api/v1/quotes/{id}/convertConvert an accepted quote to a sales invoicePATCHhttps://app.speybooks.com/api/v1/quotes/{id}/statusUpdate quote status