# Updates and line items
Source: https://www.desktopaccountingapi.com/docs/quickbooks/updates-and-line-items/

> Update QuickBooks records safely with revisionNumber, clear fields with null, and add, keep, change or remove 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

| 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 `POST`s. 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

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:

```json
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.

**TypeScript**

```ts
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;
  }
}
```

**Python**

```python
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":
            raise
```

## Omitted fields and null

- A field you leave out stays as it is.
- A field you send is set to that value.
- `null` clears a field, where QuickBooks allows clearing it. Clearable fields are nullable in the API reference. Sending `null` to any other field returns `400 FIELD_NOT_CLEARABLE`.

In the SDKs, leaving a parameter out omits it, and passing `null` (`None` in Python) sends `null`.

## 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. An `id` on its own leaves the line exactly as it is.
- **Add a line** with `"id": "-1"`.

```json
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

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.

> **Caution:**
> `"lines": []` deletes every ordinary line, as long as the transaction keeps some other line, such as a line group. An update that would leave a transaction with no lines at all is rejected with `400 INVALID_PARAMETER`. To change only the header of a transaction, such as its memo or due date, leave the line arrays out entirely.

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

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

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](https://www.desktopaccountingapi.com/docs/platform/supported-environments/#documented-incompatibilities).

## 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](https://www.desktopaccountingapi.com/docs/guides/idempotency/).
