Updates and line items
QuickBooks Desktop updates follow two rules that surprise people: every update must prove it saw the latest version, and sending a line array replaces all lines of that kind. Both prevent quiet data loss in a file that people also edit by hand.
Creating, updating, deleting
Section titled “Creating, updating, deleting”| Action | Request | Success |
|---|---|---|
| Create | POST /v1/quickbooks-desktop/{resource} |
201 with the new object |
| Retrieve | GET /v1/quickbooks-desktop/{resource}/{id} |
200 |
| Update | POST /v1/quickbooks-desktop/{resource}/{id} |
200 with the updated object |
| Delete (transactions) | DELETE /v1/quickbooks-desktop/{resource}/{id} |
200 with { "id", "objectType", "refNumber", "deleted": true } |
| Void (16 transaction types) | POST /v1/quickbooks-desktop/{resource}/{id}/void |
200 with { "id", "objectType", "createdAt", "updatedAt", "refNumber", "voided": true } |
There is no PUT or PATCH; updates are partial POSTs. List objects such as customers, vendors and items are not deleted through the typed API. Deactivate them with isActive: false, which is also what QuickBooks recommends once a record has history.
revisionNumber
Section titled “revisionNumber”Every QuickBooks object has a revisionNumber that changes each time anyone edits it, through the API or in the QuickBooks window. An update must send the revisionNumber you last read:
POST /v1/quickbooks-desktop/customers/80000012-1730312001{ "revisionNumber": "1730312567", "phone": "555-0142"}If someone changed the customer since you read it, the update fails with 409 QBD_REVISION_NUMBER_STALE and nothing changes. Retrieve the record again, reapply your change to the fresh copy, and send the new revisionNumber. We never fetch the current revision for you, because that would overwrite the other person’s edit without anyone noticing.
import { DesktopAccountingApi, ApiError } from "@desktopaccountingapi/quickbooks-desktop";
const qb = new DesktopAccountingApi().forEndUser("eu_01j9...");const id = "80000012-1730312001";
for (let attempt = 0; attempt < 3; attempt++) { const current = await qb.qbd.customers.retrieve(id); try { await qb.qbd.customers.update(id, { revisionNumber: current.revisionNumber, phone: "555-0142" }); break; } catch (err) { if (err instanceof ApiError && err.code === "QBD_REVISION_NUMBER_STALE") continue; throw err; }}from desktopaccountingapi import APIError, DesktopAccountingApi
qb = DesktopAccountingApi().for_end_user("eu_01j9...")customer_id = "80000012-1730312001"
for attempt in range(3): current = qb.qbd.customers.retrieve(customer_id) try: qb.qbd.customers.update(customer_id, revision_number=current.revision_number, phone="555-0142") break except APIError as err: if err.code != "QBD_REVISION_NUMBER_STALE": raiseOmitted fields and null
Section titled “Omitted fields and null”- A field you leave out stays as it is.
- A field you send is set to that value.
nullclears a field, where QuickBooks allows clearing it. Clearable fields are nullable in the API reference. Sendingnullto any other field returns400 FIELD_NOT_CLEARABLE.
In the SDKs, leaving a parameter out omits it, and passing null (None in Python) sends null.
Line items
Section titled “Line items”Transactions with lines (invoices, bills, estimates, sales orders, sales receipts, credit memos, purchase orders, checks, journal entries and others) have one or more line arrays: lines and lineGroups on sales forms and purchase orders, expenseLines, itemLines and itemGroupLines on bills, checks, credit card charges and credits, item receipts and vendor credits, plus application lists on payments.
On update, each line array works as a complete replacement:
- Omit the array to leave those lines untouched.
- Send the array to replace all lines of that kind. Lines you do not include are deleted.
- Keep a line by sending its
id. Fields you send on it change; fields you omit keep their values. Anidon its own leaves the line exactly as it is. - Add a line with
"id": "-1".
POST /v1/quickbooks-desktop/invoices/1A4F-1730312567{ "revisionNumber": "1730312567", "lines": [ { "id": "1A50-1730312567" }, { "id": "1A51-1730312567", "quantity": 3 }, { "id": "-1", "itemId": "80000007-1730311020", "quantity": 1, "rate": "45.00" } ]}This keeps the first line unchanged, changes the quantity on the second, removes any other existing ordinary lines, and adds a new line at the end. Any line groups on the invoice stay as they are, because lineGroups was left out.
Arrays you leave out stay unchanged
Section titled “Arrays you leave out stay unchanged”QuickBooks itself rebuilds every line of a transaction as soon as an update touches any of them, and deletes whatever the update does not list, including line groups when you only sent lines. The API prevents that: when you send some line arrays of a transaction and leave out others, it first reads the transaction and sends each existing entry of the arrays you left out back to QuickBooks by id, so they are kept unchanged. You see this read as a separate request in the request log, just before the update.
Your revisionNumber still protects the update. If anyone changes the transaction between that read and the update, the update fails with 409 QBD_REVISION_NUMBER_STALE and nothing changes. If the read itself fails, for example because the connection is offline, the update is not sent and the error says so. The read happens before the API responds, also for Prefer: respond-async requests.
Ordinary lines are listed before line groups when you change lines, so a group that sat between ordinary lines on the form moves below them.
Some line fields can be set only when a line is created; for example, links to other transactions cannot be added to an existing line. The reference for each update operation lists its line input fields.
Line groups
Section titled “Line groups”An item group expands into several lines on a form. Create one with a lineGroups entry that names the group item; QuickBooks adds its component lines. On output, lineGroups contains the group and its lines. To keep a group while you change other lines, leave lineGroups out or send the group’s id; to remove one group, send lineGroups without it.
Void or delete
Section titled “Void or delete”Voiding keeps the transaction in the audit trail with zero amounts. Deleting removes it; deleted transactions stay visible for 90 days through GET /v1/quickbooks-desktop/deleted-transactions. Accountants usually prefer void for anything already sent to a customer. A few transaction types support only one of the two; see Supported environments.
Writes are safe to retry
Section titled “Writes are safe to retry”Every create, update, delete and void accepts an Idempotency-Key, which the SDKs send for you. See Idempotency and safe retries.