edit_sequence, are hard to reproduce on purpose.
A simulated connection replaces all of that. It’s a fake QuickBooks Desktop company file hosted by QuBe
Sync, driven by the same Web Connector session code as a real connection, so your requests, webhooks and
workflows go through the same code they will in production. It answers within seconds, and you can make it fail
however you like.
Simulated connections are in beta. The simulator doesn’t match real QuickBooks in every case yet, and its
answers change as it gets closer: a change that makes it more accurate isn’t treated as a breaking change.
The simulator’s endpoints, its fault names and the
simulated connection type are stable. A request it
doesn’t model is answered with status 1030 rather than a guess, and the gaps we know about are listed
below. Test against real QuickBooks before you launch, and if an
answer differs from what real QuickBooks says, tell us at [email protected].Create one
It’s connected as soon as it exists. There’s nothing to install and no onboarding. From the CLI:qube connections create --simulated --name "Test company". Simulated connections are never billed.
What’s in the company file
Every simulated company starts with the same small, realistic data set, so your tests can rely on it:
Customers, vendors, employees, items and transactions such as invoices, bills and payments can be queried,
added and modified. The simulator enforces QuickBooks’ rules along the way: names are unique across lists
(3100), references must exist (3140), an amount needs its two decimals (
40.00, not 40; 3040), and
updates need the current edit_sequence (3200). As on real QuickBooks, a 3200’s qbXML answer carries
the record as it stands, current edit_sequence included, though the JSON answer passes on only the
error. Values keep
their spacing as QuickBooks keeps it: a name’s double space or a note’s line break comes back exactly as
sent, and a full_name filter finds a record by that spelling, or by one with extra spaces, but a
single-spaced filter does not find a double-spaced name. Iterated queries page like the real thing.
The company, host and preferences queries answer from the company file too: its preferences are a new
file’s, with account numbers, class tracking, inventory and estimates on, one currency and sales tax off,
and they can’t be changed. Requests it doesn’t model yet, such as reports, are answered with status 1030,
“not supported by this implementation”.
Reset it to the starting data at any time:
Transactions and their lines
Transactions keep their lines the way QuickBooks keeps them, so you can build and read line items against the simulator:
Every line gets a
txn_line_id. A line that names an item, account, customer or item group that doesn’t
exist is refused with 3140. Queries return item, group and expense lines only with include_line_items
(a journal entry’s lines come back either way). Lines come back in the order QuickBooks received them.
Prices, amounts and totals
Prices, amounts and totals
An item line with no price starts from the item’s: its sales price on a sale, its purchase cost on a
purchase. A
quantity and an amount without a price imply one. Otherwise amount is quantity
times the price, with a quantity of 1 when none is given. An expense line carries the amount you
send. An item-group line expands into one line per item in the group, each item’s quantity times the
group’s.Totals come from the lines:A bill posts to Accounts Payable and falls due by its terms, or 10 days after its date.
Updating lines
Updating lines
Updating lines follows QuickBooks’ rule:
Item and group lines count as one list. A purchase’s expense lines are a separate list, replaced only
when you send
expense_line_mod.Journal entries
Journal entries
Journal entries keep their debit and credit lines.
journal_line_mod changes them by txn_line_id
under the same rule as other lines, and journal_line_type (Debit or Credit) moves a line to the
other side. A new line needs a type.An entry whose debits and credits don’t balance is refused with 3180 when it’s created and 3170 when
it’s updated, with the message “Transaction not in balance.” Both codes and full messages match what
real QuickBooks answers. An entry created with an entry-level memo is saved without it and answered
with a status 530 warning (The field "Memo" is not supported by this implementation.), as real
QuickBooks does; a memo on a line is kept.Lines inside a group
Lines inside a group
Lines inside a group can be updated too: an
invoice_line_group_mod (or a purchase’s
item_group_line_mod) with its own line updates changes the group’s lines by txn_line_id under the
same rule, and a new group quantity scales its lines. Intuit doesn’t document how real QuickBooks
handles these updates. The simulator copies how a group behaves when it’s added, so test group
updates against real QuickBooks too.Payments, deposits, voids and custom fields
Payments
Payments
Payments pay invoices down: an
applied_to_txn_add’s payment_amount lowers the invoice’s
balance_remaining and marks it paid at zero. A set_credit applies a credit memo first: its
applied_amount comes off the invoice’s balance_remaining and the credit memo’s credit_remaining.
An invoice.create can apply credit memos with set_credit the same way.The part of a payment the invoice no longer needs is its unused_payment: a total_amount above the
payment_amounts keeps the rest, and is_auto_apply: false keeps all of it, applied to nothing. As on
real QuickBooks, a payment’s unused_credits is all the credit its customer has when you read it: what
its credit memos have left, plus the unused_payment of its other payments (never this one’s), whether
or not this payment applied any, so it falls to 0 once that credit is applied elsewhere. Each
applied_to_txn_ret gives the invoice’s balance_remaining and txn_date as they are when you read
the payment, not when it was made: after an invoice.update raises the total, a payment’s
balance_remaining rises with it. Its amount is the payment’s part only.A payment_amount more than the invoice’s balance_remaining is refused with 3210 (“You cannot pay
more than the amount due.”) and nothing is saved, as on real QuickBooks. The simulator caps a credit
at what’s available and what the invoice owes, and a payment that the line’s own credits leave more
than the invoice needs at what’s left; real QuickBooks may refuse those too. A payment sent without a
ref_number has none, as on real QuickBooks, which doesn’t number payments the way it numbers
invoices.Deleting a payment
Deleting a payment
Deleting a payment (
txn.delete) gives each invoice it paid its balance_remaining and
applied_amount back and takes it out of the invoice’s linked_txn. As on real QuickBooks, the
invoice’s time_modified moves to the deletion but its edit_sequence doesn’t. Credits the payment’s
own set_credit applied stay applied.Linked transactions
Linked transactions
The simulator lists linked transactions as real QuickBooks lists them:
Under a payment, each gives the other transaction’s
txn_id, txn_type (CreditMemo or
ReceivePayment), txn_date, ref_number, link_type AMTTYPE, and what it took off the invoice
as a negative amount.Every list shows what’s applied when you read it, with each transaction’s current txn_date, so a
credit applied after a payment shows up under that payment. For the total applied to an invoice, read
its applied_amount, since a payment’s linked_txn leaves out the payment’s own part.What a payment does to the invoice
What a payment does to the invoice
As real QuickBooks does it, the invoice’s
applied_amount is negative (an invoice paid 80.00 reads
-80.00), and its balance_remaining is its total plus applied_amount. The invoice’s
time_modified moves to the payment’s time, but its edit_sequence doesn’t, so the edit_sequence
you last read still updates it. A credit memo whose credit is applied keeps its edit_sequence the
same way.In the other direction, an invoice.update that changes a line’s amount (even with the total
unchanged) or the invoice’s txn_date moves the time_modified of every payment and credit memo
applied to it, and leaves their edit_sequence, so a sync by modified_date_range_filter picks them
up again. One that changes only the memo or a line’s desc leaves them alone.Reading payments back
Reading payments back
receive_payment.query returns what each payment was applied to (applied_to_txn_ret) only with
include_line_items, as real QuickBooks does. Without it, a payment that paid invoices comes back
without them. The answer to receive_payment.create always has them.A payment of credits alone
A payment of credits alone
A payment of credits alone (
total_amount 0.00, every applied_to_txn_add a set_credit with no
payment_amount) applies the credits but creates no payment, as in real QuickBooks. Its answer has no
txn_id of its own: applied_to_txn_ret lists only each invoice’s txn_id and txn_type. Read the
invoice or credit memo again to see what was applied, and don’t store an id for that payment. The
credit then shows as a linked_txn on the invoice and under each of its payments.Deposits
Deposits
A payment received without a
deposit_to_account_ref goes to Undeposited Funds, as in QuickBooks.
receive_payment_to_deposit.query lists it until a deposit takes it. A deposit.create line with a
payment_txn_id deposits that payment for its whole amount, and a line with an account_ref and
amount deposits money from that account. deposit_total is the sum of the lines.As in real QuickBooks, each payment a deposit takes gets the deposit’s time as its time_modified and
keeps its edit_sequence. A payment that isn’t waiting to be deposited (unknown, deposited already, or
received straight into a bank account) is refused with 3120. Deleting a deposit puts its payments back
in Undeposited Funds.Voids
Voids
txn_void.execute keeps the transaction and sets its totals and every line’s amount and quantity to
0, for bills, checks, credit card charges and credits, credit memos, invoices, item receipts, journal
entries, sales receipts and vendor credits. Each line keeps its rate, the memo gets VOID: in front,
and a voided invoice reads back is_paid true, even one nothing paid. It keeps its ref_number.An invoice that payments paid is voided too, as real QuickBooks does: each payment keeps its
total_amount, loses its applied_to_txn_ret for that invoice, and what it had applied there is added
to its unused_payment, ready to apply to another invoice. Real QuickBooks does all this for an
invoice; for the other types it’s the simulator’s reading. An unknown txn_id, or one of another
type, gets 3120.Custom fields and private data extensions
Custom fields and private data extensions
data_ext_def.create, .update, .delete and .query keep definitions per company, unique by
owner_id and data_ext_name. Following Intuit’s rules:A custom field assigned to Customer also reaches credit memos, estimates, invoices, sales orders and
sales receipts. To get one onto sales orders, assign it to Customer, or use a private extension
assigned to SalesOrder; the refusal says so.
data_ext.create, .update and .delete set and clear a value on a list object or a transaction
whose type the definition is assigned to. data_ext.update also sets a first value, as it does on
real QuickBooks, so you don’t need data_ext.create before it. A query that sends owner_id gets the
values back as data_ext_ret, for those owners only.A custom field’s definition comes back as real QuickBooks returns it: data_ext_id 0,
data_ext_format_string “Any text”, and assign_to_object listing the transactions a Customer field
reaches. Refusals of what Intuit rules out (a custom field on a sales order, a value with no
definition) use 3180, 3170, 3100 and 3120; Intuit doesn’t document those codes, so treat them as
approximate. An unknown txn_id gets 3120, as real QuickBooks answers it.link_to_txn_id, or a bill’s linked_txn),
price levels, account types (a card charge can name any account), date-driven terms, payment discounts, is_auto_apply (the payment is received
unapplied), changing what an update to an invoice or payment applies, sales receipts waiting in
Undeposited Funds, data extension values’ types and required flags, or QuickBooks refusing a
transaction with no lines. It answers 1030 rather than guess for: voiding a deposit, bill payment,
statement charge, credit card refund or inventory adjustment, a transaction that’s already void, an
invoice with a credit memo applied to it, or a credit memo that has been applied; a data extension on
a transaction line or on the company; and a deposit with cash back.
Other differences we know of:
Make it fail
PUT /connections/{id}/simulator sets faults. Each PUT replaces the whole set, so send every fault
you want armed at once. Resetting clears them all.
GET /connections/{id}/simulator shows the current faults and a summary of the company file, and
POST /connections/{id}/simulator/sync runs the fake Web Connector right away, like clicking Update
Selected.
- CLI
- Dashboard
--next lost arms next_lost_response, and --next ok clears whichever next-request fault is armed.
--stall takes not_checking_in or quickbooks_unavailable, and --stall off clears it.Test your stall handling
A real connection stalls when your requests wait past a grace of at least 15 minutes, so thestalled fault is how you try your connection.stalled and connection.resumed
handler without one. Give the connection a webhook_url, then:
- Set
{"faults": {"stalled": "not_checking_in"}}, orquickbooks_unavailable, thestall_reasonyou want to see. - Queue a request. It waits, as it would on a computer that’s switched off, and within seconds you get
connection.stalledwith thatstall_reason. If a request is already waiting when you set the fault, the event comes right away. - Optionally, set the other reason:
connection.stalledcomes again, with the new reason and a newerrevision, as when a computer is turned on before QuickBooks is open. - Clear the fault (a
PUTwithout it, or a reset). You getconnection.resumed, and the waiting requests are answered.
connection.resumed also comes, within a few minutes, if you
delete the waiting requests. With the fault set, POST /connections/{id}/simulator/sync runs nothing and answers
"result": "stalled".
A test plan for your integration
Walk every path before your customers do:Then test against the real thing
The simulator models QuickBooks’ behavior, not every corner of it. Before you launch, run your integration against a real copy of QuickBooks Desktop: Intuit gives developers a free not-for-resale license.In a sandbox app, an iterated query stops after 4 pages, with
iteration_state
max_sandbox_iterations_reached, on simulated and real connections alike.
