QBO Sandbox vs. TenkeyBridge Sandbox: The Same Request, Side by Side
- qbo sandbox vs desktop
- quickbooks api playground
- compare quickbooks online and desktop api

Quick answers
Is Intuit's QuickBooks Online sandbox the same as QuickBooks Desktop? No — it's a hosted QBO company with QBO's data model and behavior; a Desktop company file is a different product with different semantics.
What is the TenkeyBridge playground? A public page at try.tenkeybridge.com that sends common requests to both Intuit's QBO developer sandbox and the TenkeyBridge sandbox and shows both responses side by side.
What stays the same between the two? The REST shape — OAuth2, resource paths, the JSON envelope, Id/SyncToken on entities, and query strings sent to /query.
What changes? Desktop-specific semantics and coverage gaps: which fields exist, which entities are supported, and which operations a company file allows. Those are enumerated in the compatibility matrix.
What's the recommended workflow? Build against QBO, point the same client at the TenkeyBridge sandbox, diff the responses, then run against a real Desktop file before shipping.
Why a side-by-side matters more than a spec
If you already ship against the QuickBooks Online API, you have a working mental model: an access token, a realm ID, GET /v3/company/{realmId}/invoice/{id}, a QueryResponse wrapper, optimistic concurrency via SyncToken. The question when a Desktop customer shows up isn't "how does the QBO API work." It's "how much of what I already wrote survives contact with a company file?"
Documentation can answer that in the abstract. A diff answers it concretely. Reading "we support sparse updates on Invoice" is a claim; seeing the two JSON bodies next to each other, with the fields that differ highlighted, is evidence you can act on in an afternoon.
That's the whole purpose of the public playground. It isn't a demo of a product UI — it's a request runner. Pick a request, it fires against Intuit's QuickBooks Online developer sandbox and against the TenkeyBridge sandbox, and you read both responses. No signup, no OAuth dance, no company file.
The three ways to test, in order
The testing guide lays out three environments. They are not interchangeable, and picking the wrong one for the question you're asking wastes a day.
- The public playground. Zero setup. Good for one question: does the response shape match what my code already parses? You can't write to it with your own payloads or model your customer's chart of accounts. Use it for five minutes of orientation and for settling arguments in a design review.
- The TenkeyBridge sandbox. A real API host with real OAuth2, backed by a seeded Desktop-style company. This is where your integration test suite runs. You get tokens, you create and update entities, you see
SyncTokenincrement, you get faults back when you do something a company file won't allow. It's also where you discover that an entity you rely on isn't covered yet — far cheaper to discover there than during a customer onboarding. - A real Desktop or Enterprise company file. Your customer's chart of accounts, their items, their custom fields, their 2014-era data entry habits, their multi-user setup. Nothing simulates this. Everything you ship should touch one before it's called done.
The mistake is skipping step 2 and going straight from playground to a customer's production file. The playground tells you the envelope matches. It does not tell you how your writes behave.
What the diff actually shows you
The envelope is the same
This is the boring result and the important one. A read against either side comes back in the shape your QBO client already deserializes:
{
"Invoice": {
"Id": "1047",
"SyncToken": "0",
"DocNumber": "1047",
"TxnDate": "2024-03-14",
"CustomerRef": { "value": "58", "name": "ExampleCo" },
"TotalAmt": 1250.00,
"Line": [ /* ... */ ]
},
"time": "2024-03-14T10:22:41.118-07:00"
}
Same wrapper key, same time field, same Ref objects with value and name. Queries go to the same place and take the same string:
GET /v3/company/{realmId}/query?query=select * from Invoice where TxnDate > '2024-01-01' maxresults 20
Authorization: Bearer {token}
Accept: application/json
If your HTTP layer, token refresh, pagination loop, and deserializers survive the switch untouched, that's most of the integration cost gone. The playground is the fastest way to confirm it rather than take it on faith.
Id and SyncToken behave the way your concurrency code expects
Desktop's native model doesn't speak in Id and SyncToken — it has its own identifiers and edit-sequence semantics underneath. TenkeyBridge maps that to the QBO-style pair, so your read-modify-write loop keeps working: fetch, send back the current SyncToken, get a bumped token on success, get a fault on a stale one.
What's worth looking at carefully in the diff is the values. Id values are opaque strings on both sides — do not assume they're sequential, comparable, or stable across companies. If any code of yours sorts, ranges, or arithmetics on Id, the diff is where you find out.
Update semantics are the other place to slow down. If you've been sending full-replacement updates because QBO tolerates them, Desktop is less forgiving about what you can silently blow away in a record that a bookkeeper has been editing. We went into that in detail in sparse updates vs. full replacement, and it's the single change most likely to bite an otherwise clean port.
Query support is a subset, and the subset is the interesting part
The QBO query language is already a restricted dialect of SQL — no joins, limited operators, maxresults/startposition for pagination. Against Desktop, the supported surface is narrower again in places, because some filters simply don't have an efficient equivalent on a company file.
The practical consequence: a query that returns rows against QBO may come back as a fault against Desktop. Running your actual query strings — not the sample ones — through the sandbox is the only reliable way to find them. Build a small harness that replays every query literal in your codebase and logs the status. It's an hour of work and it finds things a read-through of the docs won't.
Faults look like faults
Errors come back in the QBO Fault envelope rather than as a raw qbXML status code or an HRESULT:
{
"Fault": {
"Error": [{
"Message": "Object Not Found",
"Detail": "Object Not Found : Something you're trying to use has been made inactive.",
"code": "610"
}],
"type": "ValidationFault"
},
"time": "2024-03-14T10:24:02.431-07:00"
}
Your existing error handling, retry classification, and alerting keep working. But the underlying causes are Desktop causes — a locked file, a record in use by another user, a company file closed to the date you're posting into. The mapping between Desktop's native status codes and QBO-style faults is worth understanding before you start triaging support tickets; we mapped them out here.
Where Desktop semantics genuinely differ
The sandbox docs carry a "Differences from real QuickBooks" section for a reason, and the compatibility matrix is the authoritative list. Rather than paraphrase it badly, the honest framing is this:
- Coverage is per-entity and per-operation. Some entities are read-only. Some fields that exist in QBO have no Desktop counterpart and are absent rather than null.
- A sandbox is a seeded company, not a customer's company. Preferences, closing dates, user permissions, custom fields, and the chart of accounts all vary per file, and those variations change what a request does. The sandbox can't model yours.
- Desktop deployments are environments, not just endpoints. Multi-user mode, hosted desktops, and RDS setups introduce conditions that no sandbox reproduces — covered separately in integrating with multi-user and hosted QuickBooks Desktop.
Read the matrix before you scope the work, not after. If an entity your product depends on isn't covered, that's a planning fact, and it's better known on day one.
A workflow you can actually follow
1. Build against QBO. Nothing changes here. Intuit's developer sandbox is free, well documented, and the fastest place to iterate on business logic.
2. Open the playground and run the requests you care about. Five minutes. You're confirming the envelope and spotting any field-level surprises in the entities at the center of your product.
3. Point your client at the TenkeyBridge sandbox. Change the base URL and the OAuth2 config; leave the rest of your code alone. Run your existing test suite. Record what fails. Most failures will cluster into three buckets: unsupported entity, unsupported query filter, update semantics.
4. Diff deliberately, not by eye. Capture both responses to disk and run a structural diff. Eyeballing JSON misses absent fields; a diff tool doesn't. Keep the fixtures — they become regression tests.
5. Work the compatibility matrix against your failure list. Each failure is either a documented gap (plan around it), a semantics difference (change your code), or a bug (report it). Sorting them into those three piles is the real output of this exercise.
6. Test against a real company file. Ideally a copy of a customer's, with a customer-shaped chart of accounts. If you don't have QuickBooks installed and don't want to, that's a solvable problem.
7. Then pilot. One customer, read-heavy first, writes behind a flag.
What the comparison doesn't prove
Being straight about it: a passing side-by-side diff means your client code speaks the protocol. It does not mean your integration works for Pat Owner at ExampleCo, whose file has 40,000 transactions, three custom fields on Invoice, a closing date in the recent past, and two people in multi-user mode at 9am. Protocol compatibility is necessary and not sufficient.
The point of running the diff early is that it collapses the uncertainty that can be collapsed cheaply — the shape of the JSON, the token flow, the query strings — so that the work left is the work that genuinely requires a real file. That's a better place to spend your integration budget than rediscovering the response envelope by hand.
FAQ
Is the Intuit sandbox the same as QuickBooks Desktop?
No. Intuit's developer sandbox is a hosted QuickBooks Online company. It uses QBO's data model, QBO's field set, and QBO's behavior. QuickBooks Desktop and Enterprise are separate products with a local company file, a different underlying data model, and different constraints. A request that succeeds against the QBO sandbox tells you nothing definitive about Desktop — which is exactly why comparing the two responses is useful.
What does the playground actually compare?
It sends a set of common requests to both Intuit's QBO developer sandbox and the TenkeyBridge sandbox and renders both responses. You're comparing response shape, field presence, Id/SyncToken behavior, and query results for the same operation. You can't run arbitrary writes with your own payloads there — for that, get credentials for the sandbox proper.
Which test environment should I use when?
Playground for orientation and quick shape checks. Sandbox with real OAuth2 for your automated test suite and anything involving writes, concurrency, or error paths. A real Desktop or Enterprise file for anything that depends on a customer's actual chart of accounts, preferences, closing dates, or multi-user environment. In practice you'll use all three, in that order.
Do I need QuickBooks Desktop installed to use the sandbox?
No. The sandbox is a hosted API endpoint; you need an HTTP client and OAuth2 credentials. You will want access to a real company file before you go live, but not to start.
Can I reuse my QBO integration tests against the TenkeyBridge sandbox?
Mostly. If your tests are written against the REST API rather than against an Intuit SDK's internals, pointing them at a different base URL is usually the extent of the change. Expect a handful of failures on entities or query filters outside the supported set — treat those as your scoping list, not as a broken test suite. The swap checklist walks through the rest.
Are Id values portable between a QBO company and a Desktop file?
No. Entity IDs are scoped to a company and are not stable across systems. If you're migrating or dual-writing, maintain your own mapping table keyed by your own identifiers rather than assuming an ID means the same thing on both sides. If you want to run the diff yourself, the playground is at try.tenkeybridge.com and the quickstart gets you sandbox credentials in a few minutes.