Skip to main content
The QuickBooks node connects a workflow to QuickBooks Online. Pick an Operation: Look up reads a record in the middle of a run and hands it to the steps after it, Insert creates a record such as an invoice or a bill, Update writes approved values back to an existing record, and Create Bill Against Purchase Order posts a verified purchase invoice match as a bill. A following step is required after a lookup and optional after a write: a write can end a branch, or hand the created record to the steps after it.

Configuration

Transient delivery failures are retried automatically by the platform. Use the node’s retry field to set the maximum number of attempts, between 1 and 5 (default 3).
At least one rule is required in Look up. A lookup with no rules would read the whole table, so the node refuses to save until you add one.

Connector

Pick a connector of type QuickBooks Online. The connector supplies the OAuth token and the realm, so the node needs no URL or HTTP method: Ingestly builds the request from the connector and the entity you choose. Pointing this node at a Business Central connector fails the step rather than quietly reading from Business Central. A connector with no base URL fails the step with a message saying so: reconnect it first.

Entity selection

QuickBooks Online does not publish a fetchable schema document, so the Entity field is populated from a built-in catalog. The catalog covers two groups:
  • Master data, the tables a workflow usually looks a record up in: Customer, Vendor, Item and Account.
  • Transactions: Invoice, Estimate, SalesReceipt, CreditMemo, Payment, RefundReceipt, Bill, PurchaseOrder, BillPayment and VendorCredit.
How the picker behaves:
  • Once the catalog loads, search the list to pick an entity, or type a name and choose Use “<name>” to enter one that is not listed.
  • With the catalog loaded, click the refresh action next to the field to re-request it. Since the catalog is built in rather than fetched from QuickBooks, its contents only change when Ingestly adds support for more entities.
  • If the catalog fails to load (or none has loaded yet), the field falls back to a plain text input with no refresh action: type the entity name directly once the field is no longer disabled.
For a Look up, the picker keeps the full read catalogue; for a write it offers only the ten transaction entities.
In Update an operation naming an entity outside those ten fails the step, naming the operation, before anything is sent. Insert checks no such list: an entity you type in by hand is sent as typed, and QuickBooks decides whether it accepts the write.
The entity has to be a plain identifier: letters, digits and underscores, starting with a letter or an underscore. Spaces and slashes around the name are trimmed off before that check, so /Vendor/ is stored and read as Vendor. A dot, a space inside the name, a slash inside the name, a leading digit, a template expression, or anything else is refused when you save. A name that is nothing but slashes or spaces is refused as a missing entity. Casing is forgiving: customer resolves to the catalog’s Customer. That trimming applies to Entity only. An entry in Select has to be a plain identifier on its own, with no surrounding slashes to strip.

Operation

Look up

Reads a record out of QuickBooks Online in the middle of a run and hands it to the steps after it. Use it to resolve something the document only refers to: extract a bill, find the vendor or the purchase order it names, then reconcile the two.

Result

First match (default). Asks QuickBooks for a single record. The matched record is the payload, so a downstream reference reads a field straight off it: {{FindVendor.payload.DisplayName}}. When nothing matched, payload is null. All matches. Asks for a page of records. The payload is the array itself, so {{FindVendor.payload}} is what a Loop or a $each iterates, and {{FindVendor.payload[0].DisplayName}} reads the first row. When nothing matched, the payload is an empty array rather than null, so a $each over it is a no-op instead of a resolution failure.
All matches returns at most 100 records, and there is no paging. A query that matches more than 100 returns the first 100 and reports matchCount as 100, so matchCount alone cannot tell a query that matched exactly 100 from one that matched thousands. metadata.quickBooks.truncated is what tells them apart: it is true only when records were left behind. Narrow the rules, or branch on truncated, when the answer has to be complete.

Rules and operators

A rule has three parts: a Field, an Operator and a Value. Rules combine with AND: a record has to satisfy every rule to match.
  • Field names a column on the QuickBooks record, not a path into upstream data. Pick it from the connector’s catalog for the chosen entity, or type it. It has to be a plain identifier, or a dotted path of plain identifiers, because QuickBooks has real nested query fields such as MetaData.LastUpdatedTime. A template expression in this box is refused.
  • Value is the template-bearing half, and that is the inverse of a Validate rule. {{extract.payload.vendor_name}} in the value box is the normal case.
QuickBooks publishes nine operators: Every one of them requires a value.
Three operators this operation does not have
The Business Central node’s Look up operation publishes twelve operators. This one publishes nine. Not Equals, Is Empty and Is Not Empty are missing, because Intuit’s documented operator list has no equivalents for them. Ingestly refuses all three when you save and again when the query is compiled, so there is no way to reach them by hand-editing a workflow either.
A QuickBooks lookup has no way to express “this field has a value” or “this field is empty”. There is no operator for either test, and no combination of the nine that stands in for one. If a workflow has to branch on whether a QuickBooks field is populated, read the record with a rule you can express, then test the field downstream with an If node, whose is empty and is not empty operators work on data already in the run.
A literal % is refused
% is the wildcard in QuickBooks’ query language, and there is no confirmed way to search for a literal one. So Contains, Starts With and Ends With refuse a value containing %, in two places:
  • When you save. A % typed anywhere into the value of one of those three rules is rejected with an error naming the field and the operator. You cannot save the workflow until you remove it.
  • When the step runs. A template that resolves to a value containing % fails the run. {{extract.payload.terms}} is clean when you save it, and if the extracted text turns out to be 50% deposit, the step fails with the same message.
The second half is the one you actually hit. Nothing can see inside a template at authoring time, so a workflow bound to extracted text saves without a word and then fails on the first document whose text happens to contain a percent sign. If a value could carry a %, strip it in a Transform step before the rule reads it, or match on a field that cannot contain one. Stripping it inline does not work: the save-time check reads the value text as typed, so {{extract.payload.terms | replace: '%', ''}} is refused because the filter’s own argument contains a %. Switching the rule to Equals or In List avoids the restriction entirely; only the three text-search operators refuse a %.
Business Central has no such restriction: its OData text functions have no wildcard semantics, so % there is an ordinary character.
Every rule value is written into the query as a quoted literal, which is what Intuit’s own documented examples do for numbers and dates as well as text. There is no value type to choose here, and the node refuses one if an import or the assistant writes it in.This is the one place the two lookups differ on values. The Business Central node’s Look up operation has a Value Type control, because its OData filter rejects a quoted literal against a numeric, date or boolean column.
Values that resolve to nothing
A value box is filled in at save time, but it is resolved at run time, and a template can resolve to nothing. For five of the nine operators, an empty resolved value would match every record in the table, so the step fails instead:
  • Contains, Starts With, Ends With, Greater Than and Greater Than or Equal refuse an empty resolved value, naming the field and the operator.
  • Equals, Less Than and Less Than or Equal are allowed through, because those stay narrow rather than widening.
  • In List needs at least one non-empty entry after the commas are split.
This is a run-time failure, not a save-time one: a rule using one of those five operators, bound to a field the extract did not produce, saves cleanly and fails when it runs. The other three do not fail. An Equals, Less Than or Less Than or Equal rule whose value resolves to nothing compares the field against an empty string, so the lookup runs and, unless a record’s field is itself blank, reports found as false: a missing extraction reads as “not found”, and nothing tells you the question was never asked. Bind the value to a field you know the upstream step emits, and treat found: false after an Equals rule as “check the extraction” as well as “check the company”.
Reserved field names
null, true and false are literals in QuickBooks query expressions rather than field names, so a rule whose field is exactly one of them is refused when you save, in any casing. QuickBooks’ own grammar would read the word as a literal rather than as your column, so the rule would compare something other than the field you meant.

Select

Select limits which fields come back. Leave it empty to take everything the connector sends. Pick each name from the connector’s catalog, or type it and choose Add. Each entry is a plain identifier, and unlike a rule’s field it does not accept a dotted path. QuickBooks returns exactly the fields you select. If the record feeds a QuickBooks node in Update, through a Reconcile or directly, keep Id and SyncToken in the list: the write-back reads the record id from Id, and every QuickBooks update operation has to carry the record’s SyncToken.
There is no Expand field on this node. Expand is an OData concept and QuickBooks has no OData surface. The Business Central node’s Look up operation has one.

When nothing matches

A lookup that matches nothing is a successful step, not an error. The step completes, payload is empty (null in First match, [] in All matches), and metadata.quickBooks.found is false. That is deliberate. “This vendor does not exist in QuickBooks” is a business outcome your workflow should route on, not an outage. Only a query that could not be asked fails the step: a bad connector, a rejected query, an HTTP error from QuickBooks, or a lookup that did not answer within its Timeout (45 seconds unless you set one). Route on it with the node’s own ports. In Look up the node has no plain output: it leaves through one of two ports instead. With Fail if not found off, exactly one of the two fires. Connect the path that uses the record to Found and whatever “we could not find it” should do to Not found: create the record with a QuickBooks node in Insert, send it to a Review for a human to resolve, or nothing at all, since a run that leaves through an unconnected port simply stops there and still completes. At least one of the two has to be connected. With Fail if not found on, connect Found to the next step; no matches fail the step instead of taking Not found.

Fail if not found

Enable Fail if not found when a matching record is required. With it on, no matches fail the step instead of taking the Not found branch; leave Continue on fail off and the error output unconnected to stop the run. It is off by default, keeping the Found / Not found branches, applies to both result modes, and leaves connection and API errors on their normal error handling.
Do not test for a missing record with an If node that reads {{FindVendor.payload.DisplayName}}. A no-match payload is null, so a reference through it resolves to nothing, which is the same thing a genuine record with a blank DisplayName produces. The Not found port is the routing answer; metadata.quickBooks.found is the field to read when a later step needs the fact inside a template.

Insert

The default operation. Creates a new record from the current document. The body auto-scaffolds the entity’s fields plus its Line array, built from the columns carried into this step.

Update

Writes changes back to an existing QuickBooks Online record. The body is an operations document, the same idea as Business Central’s, but a different shape: QuickBooks has no addressable line records, so every operation is a whole-entity create, update, or delete. There is no separate line operation.

Record source

Choose where the target record’s id, and its line rows, come from. Reconcile (default, recommended). Ingestly builds the write plan from an upstream Reconcile step’s verified match, and keeps its safety gate.
Line id path and Line id column work together: the server stamps a matched or removed line row’s connector line id only when both are set, reading the id from Line id path and writing it under the Line id column key. If the two disagree (for example, the column renamed to rowId while the body still reads {{$item.lineId}}), the id lands where nothing looks; left blank, line operations resolve their id to nothing. QuickBooks defaults both fields once a connector and entity are chosen, so leaving them alone is normally correct: open Advanced only if this entity’s line id field differs from the default.
Two safety behaviors apply: if the reconcile result has a blocking mismatch and no approved review, the step fails instead of writing; if nothing changed and there is nothing to send, the step succeeds without sending a request.
Direct (opt-in). No Reconcile node needed. You supply the record id, and optionally the line arrays, directly.
Direct skips the Reconcile safety gate: whatever record id and line references resolve below are written as-is, with nothing verifying them against the source document first. Switch to Reconcile when that verification matters.
For line pairing too complex for a template to express (for example, matching document lines to fetched connector lines by more than one key), add a Transform step upstream that joins the two sides and emits rows carrying both, then point Direct’s Lines field at that array.
Direct never fetches the connector record, so {{$writeback.record.*}} has nothing to read: a body that references it under Direct is a design-time error. This matters most for QuickBooks, whose update operations require values.SyncToken (its concurrency token) and whose auto-generated body binds it from {{$writeback.record.SyncToken}}.Under Direct, bind SyncToken from whichever upstream node already supplied the record instead, for example {{lookup.payload.SyncToken}}.

The operations document

Because QuickBooks has no addressable line records, Line is an embedded array on the document itself. A QuickBooks update is one operation whose values.Line array is generated by a $each directive over the write plan:
parentEntity and parentId are Business Central concepts. Setting either on a QuickBooks operation is a design-time error: QuickBooks has no line sub-resource for them to target. Add line rows to values.Line instead.
The $each directive only expands because it sits as the array’s own element ("Line": [ { "$each": ... } ]); the same object in a value position ("Line": { "$each": ... }) is not a directive and is written through literally. See row expansion. QuickBooks resends the full Line array on every update, so a line left out of it is treated as removed. The generated body only wires in {{$writeback.updates}} (matched lines); to also create new lines in the same write, add a second row template over {{$writeback.adds}} inside the array:
Use {{$writeback.*}}, not {{writeback.*}}. There is no reserved bare writeback head: an unprefixed reference is an ordinary node reference and fails if no node named writeback exists upstream. The $ prefix is what lets a workflow also have a node named writeback with no ambiguity.

Atomicity

Business Central’s $batch is transactional: every operation succeeds, or none do. QuickBooks is not. QuickBooks applies every operation in an operations document independently through its own batch endpoint, even when the document has only one operation. If a step’s operations document has more than one operation (for example, a step that writes several records in one run), some can succeed while others fail.
A partial failure fails the step instead of reporting success. Ingestly’s error names which operations applied and which did not, plus any operation QuickBooks reported no outcome for. This step never retries a partial failure automatically, and a retry is not selective: it resends the entire configured body again, exactly as written, including every operation that already applied. Before you retry, edit the body so it contains only the operations that still need to run, then retry and confirm the resend.

Create bill against purchase order

Use this operation after a Purchase invoice Reconcile whose purchase order source is a QuickBooks Look up. Choose the Matched invoice result. The node inherits the purchase order, vendor, invoice number, proposed quantities, connection and review policy from that match. You do not configure an entity, a body or individual line updates. Leave Posting date blank to let QuickBooks date the bill, or point it at a date field. When price checks are off in Reconcile, the purchase order’s own unit prices apply. QuickBooks has no separate receiving step, so the bill is the whole posting. The operation creates one vendor Bill linked to the purchase order and to each billed purchase order line, using the Create bill against purchase order receiving mode in Reconcile. Receipt quantities and units are not part of this flow and are hidden in configuration and review.
QuickBooks cannot validate purchase order balances and create the bill in one transaction. Ingestly rereads the purchase order and its linked transactions immediately before creating the bill and stops if anything changed, but a change made in QuickBooks inside that window is not locked out. Business Central posts atomically; QuickBooks does not. The capability status beside the matched invoice says so.

Configurations the operation refuses

Supported today: item-based purchase order lines, partial invoices, and prices, tax codes, customer, class and billable status carried from the purchase order line. Everything below is checked before any write and refuses the whole post rather than billing part of the order. Handle those orders in QuickBooks. Quantities already billed come from the purchase order’s linked transactions. If those links cannot be read line by line, the balance stays unknown and blocks the match rather than being treated as zero. Billing more than was ordered needs an over-receipt allowance in Reconcile, which acts as an over-billing allowance here; QuickBooks has no code of its own to permit it. Preview and Test Step do not post. A successful run returns the confirmed bill, the operation ID and the posting time. The receipts list is always empty for QuickBooks. The output also carries the purchase order after posting (order) with its number and status (open, closed, or unknown when QuickBooks could not be read again after the bill was confirmed); QuickBooks keeps no archived order versions, so order.archive is always null. Every attempt is sent with a stable request ID and carries a marker identifying the saved operation. Before creating anything, a retry looks for a bill carrying that marker, so an interrupted attempt is recovered rather than repeated. If QuickBooks refuses the bill, or a duplicate already exists, the step reports why and stays retryable once you fix it in QuickBooks. If QuickBooks leaves the outcome unconfirmed, the step reports Recovery required and sends nothing further. Do not create a second bill to work around an unresolved posting result. To build the complete flow, pick Post a purchase invoice against a purchase order in the workflow templates with a QuickBooks connection. Applying the template replaces the canvas in one undoable edit.

Duplicate check

Applies when Operation is Insert; the panel is not available in Update, and a lookup has nothing to guard. An Update writes to a record that already exists, so a lost response can’t be resolved by looking the record up the same way a lost create can; it fails the step rather than risk a wrong resolution.
Turn on Duplicate check to identify the record this step creates, so that if the connection drops after QuickBooks accepts the write but before Ingestly sees the response, a repeat of that attempt can be resolved instead of guessed at. Leave it off and an unresolved attempt like that fails instead.
  • Entity: defaults to the node’s configured entity.
  • Keys: one or more field/value pairs that identify the record by its natural key, for example Field: DocNumber, Value: {{extract.payload.invoiceNumber}}.
QuickBooks does not yet support automatic recovery. When a delivery outcome is unknown, Ingestly cannot look the record up, so the attempt fails rather than risk creating a duplicate. Configuring a duplicate check on this node protects against a false duplicate; it does not yet resolve the unknown attempt for you.

Body template

The body auto-generates from the entity, operation, and the columns carried into this step. Edit the body directly and your changes are preserved: the scaffold only regenerates while the body is still exactly what it last generated, so a hand-edited body is never overwritten. Use the Generate body action to reset a body back to a fresh scaffold.

Advanced

The Advanced section exposes the raw body template editor (with autocomplete and validation) and the header editor. JSON bodies support row expansion with $each, $when, and $case.

Inputs and outputs

Allowed inputs: any trigger, and any action node that produces a result payload (extract, validation, transform, filter, split, classify, merge, parse, review, wait, if, switch, loop, variable, store, prompt, reconcile, HTTP, call workflow, Business Central, QuickBooks, callback). A QuickBooks node can feed a QuickBooks node: look the vendor up, then write the bill. Output: An Insert or Update sends the request to QuickBooks Online and makes the response available to later steps. Request-level fields land under metadata.quickBooks (statusCode, method, url, attempts, requestSucceeded). A following step is optional: the node can end a branch, or feed the steps after it. A Look up hands the matched record (or the array of them) down as payload, with the outcome under metadata.quickBooks (found, matchCount, truncated, statusCode). It leaves through its Found or Not found port rather than a plain output (see When nothing matches), so every following step hangs off one of the two. A lookup costs 0 credits, and a dry run does not skip it: the query reaches your live company. It reads a record; it does not read the document.

Using the result downstream

After an Insert or Update, the node’s response is available to later steps as payload, exactly as QuickBooks returned it: Ingestly does not unwrap or rename anything. The shape depends on the operation:
  • Insert posts the record and QuickBooks answers with the created record under a key named after the entity, with its id at Id. For an Invoice the new id is {{NodeName.payload.Invoice.Id}}, and its concurrency token is {{NodeName.payload.Invoice.SyncToken}}. There is no {{NodeName.payload.id}} on a QuickBooks response, and autocomplete does not offer one.
  • Update always goes through QuickBooks’ batch endpoint, so the payload is a BatchItemResponse array with one entry per operation. Each entry carries its bId and the updated record under the entity key: {{NodeName.payload.BatchItemResponse[0].Invoice.Id}}.
  • An Update with nothing to send (the reconcile found no changes) makes no request at all. The step succeeds with a null payload, statusCode 204 and method NONE.
After a Look up, payload is the connector record itself, with no wrapper around it, so a downstream reference names the QuickBooks field directly:
Ingestly does not know the shape of a QuickBooks record, so autocomplete stops at payload. The fields under it still resolve at run time; they are simply not offered as suggestions. Use Select to make it obvious which fields a step is meant to be reading.
metadata.quickBooks carries the request and the outcome:
A QuickBooks update needs the record’s SyncToken. A Look up in First match mode is a clean way to fetch it: read it downstream as {{FindInvoice.payload.SyncToken}} and bind it into the operations document of a QuickBooks node in Update.
The node sits in the Actions group of the node picker. In Insert or Update it behaves as an output: a following step is optional, it cannot be disabled, and Test Step is not offered because a test would write to your live company. In Look up a following step is required, the node may be disabled, and Test Step runs the real query. Test Step is also refused on any node below an Insert or Update, since a test run executes every step above the one being tested.

Error output

Turn on Error output in the node’s settings to add an Error port beside the node’s other ports. When the write fails after its retries, or a lookup fails, the run follows that port instead of failing, so you can route the failure to a Review, a notification, or a compensating step. With no error edge drawn, a failed step fails the run exactly as before. A no-match takes this port only when Fail if not found is on. With the option off, no matches are a successful lookup and take Not found.
Inside a loop body the Error output toggle is not offered and the error port never renders. Whether a failing item stops the loop is the loop’s own If an item fails setting instead. See Routing failures.

Reconcile

Compare the looked-up record against the document

Business Central

The same three operations against Business Central

If

Branch on whether the lookup found anything

Connectors

Set up a QuickBooks Online connector