Skip to content

Instantly share code, notes, and snippets.

@indiamos
Last active February 3, 2026 23:44
Show Gist options
  • Select an option

  • Save indiamos/11c4921e23479de135e96e19bbe0b3e6 to your computer and use it in GitHub Desktop.

Select an option

Save indiamos/11c4921e23479de135e96e19bbe0b3e6 to your computer and use it in GitHub Desktop.
QuickBooks Online: Fix 'Craig Carlson' SyncToken Error when enabling classes

QuickBooks Online Sandbox: Fix "Craig Carlson" Error

Problem

When trying to enable settings in QuickBooks Online sandbox accounts, you encounter this error:

"You and Craig Carlson were working on this at the same time.
Craig Carlson finished before you did, so your work was not saved."

This is a SyncToken conflict. The sandbox UI has a bug that prevents these settings from being enabled through the normal interface.

Solution

Use the QuickBooks API directly with the correct SyncToken pattern. This script demonstrates the pattern by enabling class tracking.

If you found this from the Intuit Developer forum: This gist addresses the same Craig Carlson error when (1) enabling account numbers in the sandbox chart of accounts, or (2) turning on categories (class tracking) in the sandbox. Both use the Preferences endpoint and the steps below. For account numbers, see Enabling account numbers (chart of accounts). For categories/classes, run the script or use the Postman guide Request 2.

Quick start

Prerequisites

  • curl and jq installed
  • QuickBooks sandbox OAuth access token
  • Your sandbox Realm ID (Company ID)

Usage

On macOS and Linux, make the script executable once (required so you can run ./enable_qbo_classes.sh):

chmod +x enable_qbo_classes.sh

Then set your credentials and run the script:

export QBO_REALM_ID='your_realm_id'
export QBO_ACCESS_TOKEN='your_access_token'
./enable_qbo_classes.sh

On Windows (e.g. Git Bash or WSL), you can run the script without chmod by invoking bash explicitly: bash enable_qbo_classes.sh.

Or inline:

QBO_REALM_ID='123' QBO_ACCESS_TOKEN='token' ./enable_qbo_classes.sh

How it works

The script uses the Preferences endpoint and follows QuickBooks' required SyncToken pattern:

  1. GET /v3/company/{realmId}/preferences to retrieve the current SyncToken and AccountingInfoPrefs
  2. POST to /v3/company/{realmId}/preferences?operation=update with that SyncToken, setting AccountingInfoPrefs.ClassTrackingPerTxn and ClassTrackingPerTxnLine to true
  3. Verify by GETting Preferences again and checking both flags

This avoids the "Craig Carlson" error by properly synchronizing with QuickBooks' optimistic locking mechanism.

Adapting for other settings

This script enables class tracking via Preferences.AccountingInfoPrefs:

  • ClassTrackingPerTxn: true — assign one class to the entire transaction
  • ClassTrackingPerTxnLine: true — assign a class per line

The same "Craig Carlson" (SyncToken) error appears when enabling other settings in the sandbox (e.g. account numbers, departments). The fix is the same: use the API with the correct SyncToken pattern. Many Intuit Developer forum posts ask about this; the solution is always GET the entity → use the returned SyncToken in your update.

Enabling account numbers (chart of accounts)

If you get the Craig Carlson error when turning on account numbers in the sandbox, use the Preferences endpoint (same as this script), but set UseAccountNumbers instead of the class-tracking flags:

  1. GET /v3/company/{realmId}/preferences and note the SyncToken.
  2. POST to /v3/company/{realmId}/preferences?operation=update with:
    • sparse: true
    • SyncToken: the value from step 1
    • AccountingInfoPrefs: { "UseAccountNumbers": true }

The Postman guide (Request 3) and Settings Reference show this explicitly. You can adapt the bash script by changing the update body to send UseAccountNumbers: true in AccountingInfoPrefs instead of (or in addition to) the class-tracking fields.

Other preferences

To enable other Preferences (e.g. departments, sales form options), keep the same GET-Preferences → POST-update pattern and change the body to the relevant prefs. See the Settings Reference for locations of other settings.

Note on API vs. docs: Intuit's API exposes class tracking via Preferences (AccountingInfoPrefs.ClassTrackingPerTxn and ClassTrackingPerTxnLine), which matches the UI (Settings → Advanced → Categories). Some docs also mention IsClassTrackingOn under CompanyInfo; when we tried updating that via the CompanyInfo update endpoint, the API returned "Unsupported Operation." Using the Preferences endpoint worked. If your environment or Intuit's docs differ, try the approach that works for your app or check the latest Preferences and CompanyInfo docs.

Getting your credentials

1. Get your realm ID

Your Realm ID (Company ID) is in the QuickBooks URL when you're logged into your sandbox:

https://app.sandbox.qbo.intuit.com/app/company/{REALM_ID}/...

2. Get your OAuth access token

You need to implement OAuth 2.0 flow to get an access token. See:

Troubleshooting

Error: "Stale Object Error"

Cause: The SyncToken changed between the GET and POST requests (very rare).

Solution: Just re-run the script. It will fetch a fresh SyncToken.

Error: "Invalid access token"

Cause: Your OAuth token expired or is invalid.

Solution:

  1. Generate a new OAuth access token
  2. Make sure you're using a token with the correct scopes (com.intuit.quickbooks.accounting)

Error: "Entity Not Found"

Cause: Wrong Realm ID.

Solution:

  1. Verify your Realm ID is correct
  2. Make sure you're using the sandbox environment, not production

Setting already enabled

The script will detect whether class tracking is already enabled and exit gracefully:

Class tracking is already enabled (per-txn and per-line).

Understanding SyncToken

QuickBooks uses optimistic locking via SyncToken to prevent conflicting updates:

  • Every entity has a SyncToken that increments with each change
  • You must provide the current SyncToken when updating
  • If your SyncToken doesn't match (because someone else updated it), you get a "Stale Object Error"
  • The "Craig Carlson" error is QuickBooks' user-friendly way of saying "your SyncToken is stale"

The solution: Always GET the entity first to retrieve the current SyncToken, then immediately POST your update with that token.

Why this works (and the UI doesn't)

The QuickBooks sandbox UI appears to have a bug where it:

  1. Doesn't properly refresh the SyncToken before updates, or
  2. Has race conditions that cause stale tokens

By using the API directly and following the proper GET-then-POST pattern, we bypass this UI bug entirely.

Production vs. sandbox

This script is designed for sandbox environments. For production:

  1. Change the base URL:

    export QBO_BASE_URL='https://quickbooks.api.intuit.com'
  2. Use your production Realm ID and OAuth token

  3. Be careful! Production changes affect real company data.

Additional examples

See the Postman Examples for manual API examples and the Settings Reference for a list of other settings you can enable using this pattern.

References

Credits

This script and documentation were generated with assistance from:

License

MIT - Use freely, no warranty provided.

#!/bin/bash
# QuickBooks Online: Enable Classes Script
# Uses the Preferences endpoint (AccountingInfoPrefs) and the SyncToken pattern.
set -e # Exit on error
# Configuration - UPDATE THESE VALUES
REALM_ID="${QBO_REALM_ID:-}" # Your QuickBooks Realm ID (Company ID)
ACCESS_TOKEN="${QBO_ACCESS_TOKEN:-}" # Your OAuth access token
BASE_URL="${QBO_BASE_URL:-https://sandbox-quickbooks.api.intuit.com}"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Check if required variables are set
if [ -z "$REALM_ID" ] || [ -z "$ACCESS_TOKEN" ]; then
echo -e "${RED}Error: QBO_REALM_ID and QBO_ACCESS_TOKEN must be set${NC}"
echo ""
echo "Usage:"
echo " export QBO_REALM_ID='your_realm_id'"
echo " export QBO_ACCESS_TOKEN='your_access_token'"
echo " ./enable_qbo_classes.sh"
echo ""
echo "Or set them inline:"
echo " QBO_REALM_ID='123' QBO_ACCESS_TOKEN='token' ./enable_qbo_classes.sh"
exit 1
fi
PREFERENCES_URL="${BASE_URL}/v3/company/${REALM_ID}/preferences"
echo -e "${YELLOW}Step 1: Getting Preferences to retrieve SyncToken...${NC}"
# Step 1: GET Preferences
RESPONSE=$(curl -s -w "\n%{http_code}" \
-X GET \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Accept: application/json" \
"$PREFERENCES_URL")
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_CODE" != "200" ]; then
echo -e "${RED}Error: Failed to get preferences (HTTP $HTTP_CODE)${NC}"
echo "$BODY" | jq '.' 2>/dev/null || echo "$BODY"
exit 1
fi
# Extract SyncToken and Id (Preferences response may use .Preferences or .preferences)
SYNC_TOKEN=$(echo "$BODY" | jq -r '(.Preferences // .preferences).SyncToken // empty')
PREF_ID=$(echo "$BODY" | jq -r '(.Preferences // .preferences).Id // empty')
CLASS_PER_TXN=$(echo "$BODY" | jq -r '(.Preferences // .preferences).AccountingInfoPrefs.ClassTrackingPerTxn // false')
CLASS_PER_LINE=$(echo "$BODY" | jq -r '(.Preferences // .preferences).AccountingInfoPrefs.ClassTrackingPerTxnLine // false')
if [ -z "$SYNC_TOKEN" ]; then
echo -e "${RED}Error: Could not extract SyncToken from preferences response${NC}"
echo "$BODY" | jq '.'
exit 1
fi
echo -e "${GREEN}✓ Retrieved SyncToken: ${SYNC_TOKEN}${NC}"
echo -e "${GREEN}✓ Current ClassTrackingPerTxn: ${CLASS_PER_TXN}${NC}"
echo -e "${GREEN}✓ Current ClassTrackingPerTxnLine: ${CLASS_PER_LINE}${NC}"
if [ "$CLASS_PER_TXN" = "true" ] && [ "$CLASS_PER_LINE" = "true" ]; then
echo -e "${YELLOW}Class tracking is already enabled (per-txn and per-line).${NC}"
exit 0
fi
echo ""
echo -e "${YELLOW}Step 2: Updating Preferences to enable class tracking...${NC}"
# Step 2: POST update Preferences (sparse: only AccountingInfoPrefs we care about)
UPDATE_BODY=$(jq -n \
--arg syncToken "$SYNC_TOKEN" \
--arg id "$PREF_ID" \
'{
sparse: true,
SyncToken: $syncToken,
AccountingInfoPrefs: {
ClassTrackingPerTxn: true,
ClassTrackingPerTxnLine: true
}
} | if $id != "" then . + {Id: $id} else . end')
UPDATE_RESPONSE=$(curl -s -w "\n%{http_code}" \
-X POST \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d "$UPDATE_BODY" \
"${PREFERENCES_URL}?operation=update")
UPDATE_HTTP_CODE=$(echo "$UPDATE_RESPONSE" | tail -n1)
UPDATE_BODY_RESPONSE=$(echo "$UPDATE_RESPONSE" | sed '$d')
if [ "$UPDATE_HTTP_CODE" != "200" ]; then
echo -e "${RED}Error: Failed to update preferences (HTTP $UPDATE_HTTP_CODE)${NC}"
echo "$UPDATE_BODY_RESPONSE" | jq '.' 2>/dev/null || echo "$UPDATE_BODY_RESPONSE"
if echo "$UPDATE_BODY_RESPONSE" | grep -q "Stale Object Error\|SyncToken"; then
echo ""
echo -e "${YELLOW}This looks like a SyncToken error. Re-run the script to get a fresh SyncToken.${NC}"
fi
exit 1
fi
echo -e "${GREEN}✓ Successfully updated preferences.${NC}"
echo ""
echo -e "${YELLOW}Step 3: Verifying class tracking is enabled...${NC}"
# Step 3: Verify
VERIFY_RESPONSE=$(curl -s -w "\n%{http_code}" \
-X GET \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Accept: application/json" \
"$PREFERENCES_URL")
VERIFY_HTTP_CODE=$(echo "$VERIFY_RESPONSE" | tail -n1)
VERIFY_BODY=$(echo "$VERIFY_RESPONSE" | sed '$d')
if [ "$VERIFY_HTTP_CODE" = "200" ]; then
NEW_PER_TXN=$(echo "$VERIFY_BODY" | jq -r '(.Preferences // .preferences).AccountingInfoPrefs.ClassTrackingPerTxn // false')
NEW_PER_LINE=$(echo "$VERIFY_BODY" | jq -r '(.Preferences // .preferences).AccountingInfoPrefs.ClassTrackingPerTxnLine // false')
if [ "$NEW_PER_TXN" = "true" ] && [ "$NEW_PER_LINE" = "true" ]; then
echo -e "${GREEN}✓ Class tracking is now enabled (per-txn and per-line).${NC}"
exit 0
else
echo -e "${YELLOW}⚠ Class tracking may not be fully on. PerTxn=${NEW_PER_TXN} PerLine=${NEW_PER_LINE}${NC}"
exit 1
fi
else
echo -e "${YELLOW}⚠ Could not verify (HTTP $VERIFY_HTTP_CODE), but update returned success.${NC}"
exit 0
fi

Using Postman to Enable QuickBooks Settings

If you prefer to use Postman instead of the bash script, follow these examples. They use the Preferences endpoint (same as the script). The CompanyInfo update endpoint returns "Unsupported Operation" for enabling classes, so we use Preferences.

Setup: Create environment variables

Create a Postman environment with these variables:

Variable Example Value Description
qbo_base_url https://sandbox-quickbooks.api.intuit.com Sandbox API base URL
qbo_realm_id 1234567890 Your QuickBooks Company ID
qbo_access_token eyJlbmMiOiJBMTI4Q0... Your OAuth 2.0 access token
qbo_sync_token (auto-populated) Current SyncToken from GET Preferences
qbo_pref_id (auto-populated) Preferences Id (optional, set by Request 1)

Request 1: Get preferences & SyncToken

This request fetches the current Preferences and extracts the SyncToken (and optional Id). Run this first; the test script saves the values to your environment.

Request details

Method: GET
URL: {{qbo_base_url}}/v3/company/{{qbo_realm_id}}/preferences

Headers:

Authorization: Bearer {{qbo_access_token}}
Accept: application/json

Tests script (save SyncToken)

Add this to the "Tests" tab:

const response = pm.response.json();
const prefs = response.Preferences || response.preferences;

if (prefs) {
  pm.environment.set("qbo_sync_token", prefs.SyncToken);
  if (prefs.Id) pm.environment.set("qbo_pref_id", prefs.Id);

  console.log("✓ SyncToken:", prefs.SyncToken);
  console.log("✓ Id:", prefs.Id || "(none)");

  const accounting = prefs.AccountingInfoPrefs || {};
  console.log("\n📊 Current class tracking:");
  console.log("  ClassTrackingPerTxn:", accounting.ClassTrackingPerTxn);
  console.log("  ClassTrackingPerTxnLine:", accounting.ClassTrackingPerTxnLine);

  if (accounting.ClassTrackingPerTxn && accounting.ClassTrackingPerTxnLine) {
    console.log("\n✓ Class tracking already enabled");
  } else {
    console.log("\n→ Run Request 2 to enable class tracking");
  }
} else {
  console.error("❌ Failed to parse Preferences from response");
}

Expected response shape

{
  "Preferences": {
    "Id": "1",
    "SyncToken": "3",
    "AccountingInfoPrefs": {
      "ClassTrackingPerTxn": false,
      "ClassTrackingPerTxnLine": false,
      "UseAccountNumbers": false
    }
  }
}

(API may return preferences in lowercase; the test script handles both.)

Request 2: Enable classes

Updates Preferences to turn on class tracking (per transaction and per line). Uses the SyncToken from Request 1.

Request details

Method: POST
URL: {{qbo_base_url}}/v3/company/{{qbo_realm_id}}/preferences?operation=update

Headers:

Authorization: Bearer {{qbo_access_token}}
Content-Type: application/json
Accept: application/json

Body (raw JSON):

If you have qbo_pref_id set, include Id; otherwise omit it (the script uses it only when present):

{
  "sparse": true,
  "SyncToken": "{{qbo_sync_token}}",
  "AccountingInfoPrefs": {
    "ClassTrackingPerTxn": true,
    "ClassTrackingPerTxnLine": true
  }
}

Optional: add "Id": "{{qbo_pref_id}}" at the top level if your GET response included an Id.

Tests script (verify success)

const response = pm.response.json();

if (pm.response.code === 200) {
  const prefs = response.Preferences || response.preferences;
  if (prefs) {
    pm.environment.set("qbo_sync_token", prefs.SyncToken);
    console.log("✓ Class tracking updated. New SyncToken:", prefs.SyncToken);
  }
} else {
  console.error("❌ Failed:", pm.response.text());
}

Request 3: Enable account numbers

Account numbers are also under Preferences. Run Request 1 again to get a fresh SyncToken after any previous update, then run this.

Method: POST
URL: {{qbo_base_url}}/v3/company/{{qbo_realm_id}}/preferences?operation=update

Body (raw JSON):

{
  "sparse": true,
  "SyncToken": "{{qbo_sync_token}}",
  "AccountingInfoPrefs": {
    "UseAccountNumbers": true
  }
}

(Add "Id": "{{qbo_pref_id}}" if you use it in Request 2.)

Request 4: Verify settings

Method: GET
URL: {{qbo_base_url}}/v3/company/{{qbo_realm_id}}/preferences

Same as Request 1. Check the response: AccountingInfoPrefs.ClassTrackingPerTxn and ClassTrackingPerTxnLine should be true after enabling classes.

Complete Postman collection (preferences-based)

You can import this JSON into Postman. It uses the Preferences endpoint for enabling classes (and optionally account numbers).

{
  "info": {
    "name": "QuickBooks Online - Enable Settings (Preferences)",
    "description": "Fix 'Craig Carlson' error by enabling class tracking via Preferences API",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "1. Get Preferences (fetch SyncToken)",
      "event": [
        {
          "listen": "test",
          "script": {
            "exec": [
              "const response = pm.response.json();",
              "const prefs = response.Preferences || response.preferences;",
              "if (prefs) {",
              "    pm.environment.set('qbo_sync_token', prefs.SyncToken);",
              "    if (prefs.Id) pm.environment.set('qbo_pref_id', prefs.Id);",
              "    console.log('✓ SyncToken:', prefs.SyncToken);",
              "}"
            ]
          }
        }
      ],
      "request": {
        "method": "GET",
        "header": [
          { "key": "Authorization", "value": "Bearer {{qbo_access_token}}" },
          { "key": "Accept", "value": "application/json" }
        ],
        "url": "{{qbo_base_url}}/v3/company/{{qbo_realm_id}}/preferences"
      }
    },
    {
      "name": "2. Enable Classes",
      "event": [
        {
          "listen": "test",
          "script": {
            "exec": [
              "if (pm.response.code === 200) {",
              "    const r = pm.response.json();",
              "    const p = r.Preferences || r.preferences;",
              "    if (p) pm.environment.set('qbo_sync_token', p.SyncToken);",
              "    console.log('✓ Classes enabled');",
              "}"
            ]
          }
        }
      ],
      "request": {
        "method": "POST",
        "header": [
          { "key": "Authorization", "value": "Bearer {{qbo_access_token}}" },
          { "key": "Content-Type", "value": "application/json" },
          { "key": "Accept", "value": "application/json" }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\n  \"sparse\": true,\n  \"SyncToken\": \"{{qbo_sync_token}}\",\n  \"AccountingInfoPrefs\": {\n    \"ClassTrackingPerTxn\": true,\n    \"ClassTrackingPerTxnLine\": true\n  }\n}"
        },
        "url": "{{qbo_base_url}}/v3/company/{{qbo_realm_id}}/preferences?operation=update"
      }
    },
    {
      "name": "3. Enable Account Numbers",
      "event": [
        {
          "listen": "test",
          "script": {
            "exec": [
              "if (pm.response.code === 200) {",
              "    const r = pm.response.json();",
              "    const p = r.Preferences || r.preferences;",
              "    if (p) pm.environment.set('qbo_sync_token', p.SyncToken);",
              "    console.log('✓ Account numbers enabled');",
              "}"
            ]
          }
        }
      ],
      "request": {
        "method": "POST",
        "header": [
          { "key": "Authorization", "value": "Bearer {{qbo_access_token}}" },
          { "key": "Content-Type", "value": "application/json" },
          { "key": "Accept", "value": "application/json" }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\n  \"sparse\": true,\n  \"SyncToken\": \"{{qbo_sync_token}}\",\n  \"AccountingInfoPrefs\": {\n    \"UseAccountNumbers\": true\n  }\n}"
        },
        "url": "{{qbo_base_url}}/v3/company/{{qbo_realm_id}}/preferences?operation=update"
      }
    },
    {
      "name": "4. Verify Settings",
      "request": {
        "method": "GET",
        "header": [
          { "key": "Authorization", "value": "Bearer {{qbo_access_token}}" },
          { "key": "Accept", "value": "application/json" }
        ],
        "url": "{{qbo_base_url}}/v3/company/{{qbo_realm_id}}/preferences"
      }
    }
  ]
}

(Postman may require the url field to be an object with raw, host, path; if import fails, create the requests manually using the details above.)

Workflow

  1. Set up your environment with qbo_base_url, qbo_realm_id, and qbo_access_token.
  2. Run Request 1 to get the current SyncToken (saved automatically).
  3. Run Request 2 to enable class tracking.
  4. To enable account numbers: run Request 1 again (fresh SyncToken), then Request 3.
  5. Run Request 4 to verify.

Troubleshooting in Postman

"Stale Object Error"

Re-run Request 1 to get a fresh SyncToken, then run the update request again.

Viewing environment variables

Use the eye icon (👁️) in the top-right to see current values, including qbo_sync_token.

Console logging

Open View → Show Postman Console to see output from the test scripts.

QuickBooks Online Company Settings Reference

This reference lists common company settings and where they live in the QuickBooks API.

The two main endpoints

1. Preferences endpoint (this script)

Endpoint: GET / POST /v3/company/{realmId}/preferences (POST with ?operation=update)

Used by this gist: The script and Postman examples enable class tracking via Preferences:

  • AccountingInfoPrefs.ClassTrackingPerTxn — track classes per transaction
  • AccountingInfoPrefs.ClassTrackingPerTxnLine — track classes per line

Other common Preferences settings:

Setting Name Location Description
UseAccountNumbers AccountingInfoPrefs.UseAccountNumbers Enable account numbers in chart of accounts
ClassTrackingPerTxn AccountingInfoPrefs.ClassTrackingPerTxn Track classes per transaction
ClassTrackingPerTxnLine AccountingInfoPrefs.ClassTrackingPerTxnLine Track classes per transaction line
TrackDepartments AccountingInfoPrefs.TrackDepartments Enable department tracking

Pattern: GET preferences → read SyncToken → POST to preferences?operation=update with sparse: true, current SyncToken, and the prefs you want to change.

2. CompanyInfo endpoint (limited use)

Endpoint: /v3/company/{realmId}/companyinfo/{companyId}?operation=update

Settings location: CompanyInfo.NameValue[] array (e.g. IsClassTrackingOn, TrackDepartments, UseLocations).

Important: In practice, the CompanyInfo update operation returns "Unsupported Operation" (e.g. when enabling classes). So for class tracking and similar settings, use the Preferences endpoint as in this gist. CompanyInfo is still useful for reading company info and name/value settings via the query API.

How to use this script's pattern

The provided script uses the Preferences endpoint. It:

  1. GETs /v3/company/{realmId}/preferences
  2. Extracts SyncToken (and optional Id)
  3. POSTs to preferences?operation=update with AccountingInfoPrefs.ClassTrackingPerTxn and ClassTrackingPerTxnLine set to true

To enable another Preferences setting (e.g. account numbers), keep the same flow and change the update body, for example:

{
  "sparse": true,
  "SyncToken": "{currentSyncToken}",
  "AccountingInfoPrefs": {
    "UseAccountNumbers": true
  }
}

Include Id in the body if the GET response included one (the script does this when present).

For settings in CompanyInfo.NameValue (read-only via update)

Some settings are documented under CompanyInfo.NameValue (e.g. IsClassTrackingOn, TrackDepartments, UseLocations). The CompanyInfo update endpoint does not accept these updates in practice ("Unsupported Operation"). You can still read them via:

curl -X GET \
  -H "Authorization: Bearer ${TOKEN}" \
  "https://sandbox-quickbooks.api.intuit.com/v3/company/${REALM_ID}/query?query=SELECT * FROM CompanyInfo MAXRESULTS 1" \
  | jq '.QueryResponse.CompanyInfo[0].CompanyInfo.NameValue'

For enabling class tracking, use the Preferences endpoint and AccountingInfoPrefs as in this gist.

Finding Setting Names

For preferences (recommended for toggling settings):

curl -s -X GET \
  -H "Authorization: Bearer ${TOKEN}" \
  "https://sandbox-quickbooks.api.intuit.com/v3/company/${REALM_ID}/preferences" \
  | jq '.Preferences.AccountingInfoPrefs'

For CompanyInfo.NameValue (read-only for update purposes):

curl -s -X GET \
  -H "Authorization: Bearer ${TOKEN}" \
  "https://sandbox-quickbooks.api.intuit.com/v3/company/${REALM_ID}/query?query=SELECT%20*%20FROM%20CompanyInfo%20MAXRESULTS%201" \
  | jq '.QueryResponse.CompanyInfo[0].CompanyInfo.NameValue'

(URL-encode the query parameter if needed.)

Common preferences settings (use these for updates)

AccountingInfoPrefs

Setting Name Type Description
UseAccountNumbers boolean Display account numbers
ClassTrackingPerTxn boolean Track classes per transaction
ClassTrackingPerTxnLine boolean Track classes per line
TrackDepartments boolean Enable department tracking

SalesFormsPrefs

Setting Name Type Description
CustomTxnNumbers boolean Use custom transaction numbers
AllowDeposit boolean Allow deposits on sales forms
AllowDiscount boolean Allow discounts on sales forms

VendorAndPurchasesPrefs

Setting Name Type Description
TrackingByCustomer boolean Track expenses by customer
BillableExpenseTracking boolean Track billable expenses

CompanyInfo.NameValue (read / documented; update not supported)

These appear in API docs and query responses, but the CompanyInfo update endpoint does not accept updates in practice:

Setting Name Type Description
IsClassTrackingOn boolean Class tracking (use Preferences instead to enable)
TrackDepartments boolean Department tracking
UseLocations boolean Location tracking
MultiCurrencyEnabled boolean Multi-currency ⚠️ Cannot be disabled once enabled
AutoApplyCredit boolean Automatically apply credits
AutoApplyPayments boolean Automatically apply payments

Notes

  • Always use sparse: true when updating Preferences.
  • Always include the current SyncToken from a GET request.
  • SyncToken increments with each change; re-GET before each update if you do multiple changes.
  • Some settings can only be enabled, not disabled (one-way).
  • Multi-currency can only be enabled once and cannot be disabled.
  • Some settings require specific QuickBooks subscription levels.

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment