Prepared for J2 Systems

Customer Portal:
System Design & Data Flow

How order information moves from QuickBooks to your customers — without anyone at J2 typing it twice, without exposing your accounting system, and without adding a second place for your team to look.

Plumbline StudioKyle Knight
July 2026Revision C · superseded 11 Aug 2026
Superseded — retained for history (11 August 2026)

This page describes Revision C, from July. Two of its conclusions have since been overtaken and it should not be used to plan work:

Current design: the order confirmation walkthrough.

Where we are today

Every order is entered twice, and the customer can't see any of it.

This is the path an order takes from the moment a deposit lands. The break in the middle — the manual re-entry — is what everything downstream waits on.

QuickBooks Estimate → invoice → deposit Where an order is won Manual re-entry Estimate no. · customer · job name deposit date · lead time · ship-to Typed a second time, by hand AppSheet Tracker Runs the shop · permanent archive Internal only Customer No visibility · calls to ask Confirmation form by email phone, email and a PDF that comes back signed — or doesn't
01

The same order, typed twice

Every won job is re-keyed from QuickBooks into the tracker by hand. Slow, and where drift between the two systems begins.

02

The confirmation form is a round trip

Order Confirmation Form Rev 2.0 says it plainly: nothing starts until every item is confirmed and approved. Today it goes out as a PDF and comes back signed — or stalls somewhere no one can see.

03

So the clock stays invisible

The customer can't see that their own missing ship-to address is holding the lead time at zero. They find out when they call to ask why nothing has moved.

The proposed system

One way in. One place J2 looks. One place the customer looks.

Order data enters once, in QuickBooks, and flows outward automatically. Critically, this adds no second internal screen — the tracker stays where your team already works, and customer confirmations land in it as columns rather than in a new inbox somewhere else.

INTERNAL — J2 ONLY · NEVER REACHABLE FROM THE PORTAL CUSTOMER-FACING QuickBooks Online Estimates · invoices · deposits Entered once. Nothing writes back. deposit paid Sync Service Verifies Intuit signature, reads the record, maps the fields Watchdog Runs on its own schedule, outside the sync "Did it run? No → shout." checks Text / email alert Only ever speaks up when something is broken J2 Operations Data Layer One record per project — what everything else reads from Order records Lead-time clock Confirmations SendGrid Customer emails are sent from here via SendGrid — never from QuickBooks. Failed imports are written into the tracker as flagged rows, not buried in a log. writes AppSheet Tracker The one place J2 looks. Unchanged for the shop floor. Auto-filled — no re-typing Customer answers land as columns Widget: "last sync 8:02 AM" OPTIONAL — INSTEAD OF Operations Platform Submittals · sales reps searchable archive Replaces the tracker if the tracker is worth replacing. Never both at once. or SECURITY BOUNDARY — no path from here back to QuickBooks Customer Portal Order confirmation · lead-time clock documents · photos · messages The only customer-facing surface customer answers → tracker row + email Customer actions never write to QuickBooks. Anything touching money stays a human step at J2.
Automated data flow Customer answer — lands on the tracker row, plus an email Monitoring & security boundary

The change from the first draft. An earlier version of this drawing showed a separate J2-facing screen alongside the tracker. That was a mistake — it recreated the very duplication this project exists to remove, just at the screen layer instead of the keyboard. There is now exactly one place J2 looks, and which one it is depends on the tracker question below. Never both.

Discovery finding · the question the whole build rested on

Can QuickBooks fill the tracker by itself? Yes.

Before designing anything we needed to know whether the double entry could actually be removed — because if it couldn't, the tracker would have to be rebuilt. It can.

The mechanism. QuickBooks Online publishes webhook events when an invoice or payment is created or updated. Those events carry identifiers rather than full records, so the sync service reads the complete order back through the QuickBooks API and maps only the fields J2 has approved. AppSheet accepts inbound writes through its own REST API, so the tracker row can be created — and later updated with the customer's answers — programmatically.

What we verifiedResult
Webhook coverageInvoice and Payment, on create, update, delete and void
Payload contentsEntity type, id, operation and timestamp only — a follow-up read is required by design
Endpoint requirementsPublic HTTPS with HMAC-SHA256 verification of Intuit's signature header
Writing to the trackerAppSheet REST API accepts add, edit and delete from an external service
Rate limits40 requests per minute per company file; webhooks don't consume read credits
Cost at J2's volumeNone. Free-tier allowance is far beyond this workload

Two ways this can land

Path A · expected

Keep AppSheet exactly as it is

The tracker stays the shop's tool and fills itself. Customer answers arrive as new columns on the row Levi already reads. Nobody learns anything new — the re-typing simply stops.

Path B · fallback

Replace the tracker

If AppSheet's plan or sync behavior gets in the way, the Operations Platform takes its place — same columns, same archive, same attachments, no re-entry. That platform is also available by choice, not only as a fallback.

Either way the customer portal is unaffected. It reads the data layer, not the tracker — so it ships on schedule regardless of how this resolves.

How J2 finds out something happened

Email and the tracker. No new place to check.

A customer confirming their address shouldn't require anyone at J2 to open a new application. Three mechanisms cover everything, and two of them are things your team already uses.

MechanismWhat it carriesWhy this form
Tracker columns Confirmation status, date completed, which items are outstanding, submittal required or waived, customer questions It's the row Levi already reads. State belongs next to the job, not in a separate queue.
Email A customer completed their confirmation and the clock has started; a customer asked a question; a submittal was approved Push, not poll. Nobody has to remember to go and look, and it lands where J2 already works.
Watchdog alert The sync missed a scheduled run, or a specific record failed to import This is the one that can't live inside the tracker — see below.

Why the watchdog sits outside. A health indicator written by the sync itself goes quiet exactly when it should be shouting: if the sync dies, the indicator freezes on its last value and the tracker looks perfectly normal while going quietly stale. So the tracker carries a timestamp — "last sync 8:02 AM" — because a stale timestamp is obviously wrong to anyone glancing at it, while a stale "OK" is invisible. The actual alarm runs on its own schedule, outside the sync, and asks one question: did it run? A separate failure mode — one record that won't map while everything else is fine — is written into the tracker as a flagged row, so a missing job shows up in the same place as every other job.

What is being built

The portal is the project. The platform is a choice.

Two distinct pieces of work, sharing one foundation. The first is what we agreed. The second exists because the same data layer makes it inexpensive — not because the first needs it.

In scope

Customer Portal & QuickBooks sync

Order data enters once. The tracker fills itself. Customers get a live order confirmation, a visible lead-time clock, documents, photos and messages — and J2 stops fielding "where is my order" calls.

Double entryRemoved
Internal screens addedNone
Customer-facing surfacesOne
Optional — separately scoped

Operations Platform

The tracker as a purpose-built interface rather than a spreadsheet: submittal state on every order, a screen per sales rep reachable from outside the business, and a searchable archive with photos, work orders and cut files attached.

ReplacesThe AppSheet tracker
Required for the portalNo
Becomes necessary ifAppSheet can't be automated
To settle together

Five open questions.

None block the design. All shape the build, and each is quicker to answer in a room than over email.

 QuestionWhy it matters
1 How do customers get in — accounts, or a link per order? Accounts suit repeat customers and give them a portfolio of jobs; per-order links ask nothing of the customer at all. This is the one decision that shapes the login work, so it wants settling first.
2 How much can a customer do beyond looking? Confirming their own form is clearly safe. Requesting changes is useful but creates work. Where the line sits is an operations decision, not a technical one.
3 Which tracker columns are the customer's business? Material source, hardware status and internal review stay behind the wall. The proposed customer-visible set is in the portal preview — it needs your redline.
4 What counts as "everything complete" on an order? The clock starts when the confirmation is complete. Since submittals aren't always required, we need the rule that decides when an order qualifies.
5 Is the tracker worth replacing? Not a question for Thursday. Once the sync is live and the re-typing has stopped, you'll know whether the spreadsheet is still holding you back.

Next step on our side. Access to QuickBooks and to the tracker. Everything here is designed from the 28 July conversation and your own Order Confirmation Form — confirming it against live data is what turns it into a build.