Skip to main content

Quotes

Quotes in Naqood are lightweight invoices whose status is quote. They never hit the ledger until you convert them into a draft or sent invoice, so you can run a full CPQ flow without touching accounting. This guide focuses on how to list, create, and convert quotes through the public GraphQL API.

Prerequisites

  • A Naqood API secret or OAuth-issued secret scoped to the target organization
  • The organization slug for every request
  • A role with the Invoices permission set (quotes reuse the invoice endpoints)

Quote shape

Quotes return the same fields as Invoice objects. Key properties to know:

FieldTypeNotes
idID!Stable identifier for follow-up mutations.
numberInt!Quote numbers live in the same sequence as invoices.
statusString!Always quote until you convert the document.
contactIdID!Customer the quote is addressed to.
contactDetailsString!Serialized snapshot of bill-to information.
dateDate!Displayed on the PDF; does not affect ledgers.
lineItems[JSONObject!]!Items being quoted (see Products & Services).
currencyString!Defaults to organization currency. Provide exchangeRate for FX quotes.
subtotal / vat / totalStringCalculated using the same rules as invoices.
createdAt / updatedAtTimestamp!Audit timestamps.

Because quotes are not posted, the journalEntries and baseJournalEntry fields are empty until the quote becomes an invoice.

Fetch a single quote

Use the invoice query with the quote ID:

query Quote($quoteID: ID!) {
invoice(invoiceID: $quoteID) {
id
number
status
date
contact {
id
name
}
lineItems
subtotal
vat
total
}
}

List quotes with filters

query Quotes(
$slug: Slug!
$offset: Int
$limit: Int
$orderBy: OrderByInput
$filter: JSONObject
) {
organization(slug: $slug) {
invoices(
offset: $offset
limit: $limit
orderBy: $orderBy
filter: $filter
) {
count
entries {
id
number
status
date
total
contact {
id
name
}
}
}
}
}

Apply filter: { status: { equalTo: "quote" } } so the dataset only returns quotes. The standard operators (equalTo, greaterThan, in, and/or) are supported for date ranges, contacts, currencies, and more.

Create a quote

Call createInvoice and set status to quote:

mutation CreateQuote($input: CreateInvoiceInput!) {
createInvoice(input: $input) {
id
number
status
total
}
}

Required input fields:

FieldRequiredNotes
slugOrganization issuing the quote.
contactIdCustomer to quote.
contactDetailsSerialized address block rendered on the PDF.
dateReference date shown to the customer.
paymentTermType / paymentTermDaysDefine the validity window displayed on the quote.
bankAccountIdIncluded on the PDF if the customer accepts the quote as-is.
lineItemsArray of line payloads with quantities, pricing, VAT, and accounts.
statusSet to quote.

Optional fields let you attach logos/stamps, set currency, exchangeRate, customize the document title (hero heading), the header (subtitle beneath the title), and the comment body copy shown before the header.

Update or duplicate quotes

mutation UpdateQuote($input: UpdateInvoiceInput!) {
updateInvoice(input: $input) {
id
status
total
updatedAt
}
}
  • Only quotes (and draft invoices) can be edited. Sent invoices become read-only.
  • To copy a quote, fetch its data via the queries above and pass the same payload back to createInvoice with status: "quote".
  • When the customer accepts, call updateInvoice with the quote id and set status to draft. From there you can either make final edits or send the invoice immediately.

Email a quote

mutation SendQuote($input: SendInvoiceToCustomerInput!) {
sendInvoiceToCustomer(input: $input)
}

Supply the quote id and optional recipient list. The PDF renderer uses the same template as invoices, but the document retains status = quote until you explicitly convert it.

Delete a quote

mutation DeleteQuote($quoteID: ID!) {
deleteInvoice(invoiceID: $quoteID)
}

Deleting a quote removes it permanently because no ledger entries exist yet. If you mistakenly deleted a sent invoice, the mutation would void it instead.