Skip to main content
Question

How to sync Google Sheets rows to a single QuickBooks Invoice (with negative lines) without complex loops

  • July 6, 2026
  • 2 replies
  • 10 views

I was browsing the community archives and saw a closed thread from ​@AndyPeter  about trying to compile Google Sheet rows into a single QBO invoice with dynamic line items (and handling negative discount/credit rows).

The standard native workaround usually involves complex search loops, "Lookup Spreadsheet Rows" modules, and multi-step filters. This can be incredibly fragile and structurally struggles when QBO rejects negative numbers in standard line detail.

I wanted to share a much cleaner, 3-step alternative that uses a webhook and a custom JavaScript payload to reshape the array. This bypasses the native connector entirely and handles both positive and negative lines dynamically.

The Logic:

  1. Catch the Google Sheet rows via Webhook.

  2. Run a custom JavaScript block to reshape the product objects into the clean nested JSON array QuickBooks expects (routing positive lines to SalesItemLineDetail and negative lines to DiscountLineDetail).

  3. Push a single POST request directly to QBO’s /sales/Invoice REST API endpoint.

This eliminates 10+ Zapier steps and prevents the sync from crashing on discounts.

If anyone (or ​@AndyPeter ) is currently struggling with mapping nested line-item arrays from Sheets to QBO, drop a comment below and I'll gladly share the JS schema we use to bypass this.

2 replies

SamB
Community Manager
Forum|alt.badge.img+11
  • Community Manager
  • July 6, 2026

Hi ​@Samuel Oliver 👋

That's a great way to handle it! We'd love it if you could share an example of the JavaScript code you're using for that here so that others can try it in their Zap workflows as well. And I'd be happy to drop a link to that example code on the original closed topic so it's easier for anyone who comes across that one to find their way here.


Hi ​@SamB ! Thanks for reaching out. I would absolutely love it if you could link
this back to the original topic—hopefully, it saves some folks a few hours of
troubleshooting!

For anyone looking to implement this via a Code by Zapier (JavaScript) step,
here is a simplified version of the logic we use to split positive line items
and negative discounts into the specific formats the QuickBooks API requires.

When you pass your Google Sheets rows into this step as a JSON array (via
inputData.rawItems), the script iterates through them and shapes the
QBO-compliant payload:

// Parse the incoming array of Google Sheet rows
const items = JSON.parse(inputData.rawItems || "[]");

let qboLineItems = [];

items.forEach(item => {
let amount = parseFloat(item.amount);

if (amount >= 0) {
// 1. Structure standard positive line items
qboLineItems.push({
"Amount": amount,
"DetailType": "SalesItemLineDetail",
"SalesItemLineDetail": {
"ItemRef": {
"value": item.productId // Mapped from your QBO Product Library
},
"Qty": item.qty,
"UnitPrice": item.unitPrice
},
"Description": item.description
});
} else {
// 2. Structure negative line items (Discounts/Credits)
// Note: QBO requires the amount to be passed as an absolute value here
qboLineItems.push({
"Amount": Math.abs(amount),
"DetailType": "DiscountLineDetail",
"DiscountLineDetail": {
"DiscountAccountRef": {
"value": item.discountAccountId
}
},
"Description": "Applied Discount/Credit from Sheets"
});
}
});

// Output the formatted array to push into a Custom Request (POST) module
output = [{ formattedLineItems: qboLineItems }];

Note for builders: You will then map formattedLineItems directly into the "Data"
field of a Zapier Webhooks (Custom Request - POST) action pointing to the
/sales/Invoice endpoint.

Let me know if anyone needs help mapping their specific Sheet variables to the
input data!