CoPortalDocs

Power features · Integrations

QuickBooks Online

End-to-end reference for the QuickBooks Online integration — what syncs, what doesn't, every setting and what it changes, and the most common ways agencies actually run it. Setup lives in Settings → Integrations.

What you get

CoPortal's QuickBooks integration is the deepest of all our accounting connectors. It covers the full lifecycle of a billing record:

  • Outbound — push CoPortal invoices, quotes, and customers into QuickBooks (one-click or auto)
  • Inbound — bulk-import existing QB customers, historical invoices (with their payment state), and estimates into CoPortal
  • Linked — once a record is synced once, every future change updates the same QB record (no duplicates)
  • Configurable per-direction — auto-sync, sync-as-draft, and display-only payments are independent toggles you can flip in any combination

Available on Studio and above. There's no per-record API charge — sync as many invoices as you like.

The contract — who owns what

The integration only works cleanly if both sides have a clear job. Here's the split:

CoPortal owns

  • • Creating invoices & quotes (with project, client, line items)
  • • Editing those records — line items, due dates, notes, recurring
  • • Marking paid for internal use (cashflow, dashboards, commissions, client portal)
  • • The client-facing layer: portal, reminders, client comms, file requests

QuickBooks owns

  • • Bank matching and payment reconciliation
  • • Marking invoices paid (this is the canonical action)
  • • The legal/tax record of cash receipts
  • • Anything an accountant traditionally does — reports, journals, period close

In one line: CoPortal originates the invoice and runs the client side; QuickBooks owns the money side. Marking an invoice paid in CoPortal updates CoPortal only — it never pushes to QB. Marking it paid in QB will, after the next CoPortal release, pull back automatically into CoPortal.

Lifecycle of an invoice — visual flow

End-to-end, here's how a single invoice moves between the two systems:

  1. 1

    CoPortal

    You create the invoice (client, line items, VAT, due date).

    pushes to

    QuickBooks

    Lands as Awaiting Payment with your DocNumber preserved.

  2. 2

    CoPortal

    You edit the invoice (change line items, due date, add notes…).

    updates in

    QuickBooks

    Same QB invoice is updated in place — never duplicated.

  3. 3

    CoPortal

    You create a quote (single-tier or multi-option).

    pushes to

    QuickBooks

    Lands as an Estimate (Pending). Multi-option quotes only sync the accepted tier after acceptance.

  4. 4

    CoPortal

    You mark the invoice paid in CoPortal (e.g. client paid you and you want CoPortal's dashboard / cashflow / portal to reflect it).

    no push

    QuickBooks

    Nothing changes in QB. CoPortal updates internally only — safe to do anytime.

  5. 5

    QuickBooks

    Your accountant marks the invoice paid in QB (bank match, payment receipt, journal entry — however they record cash).

    pulls into

    CoPortal

    Invoice flips to Paid in CoPortal automatically — dashboard, cashflow, commissions, client portal all refresh on the next hourly pull.

Note: Step 5 runs on a scheduled job hourly. CoPortal scans every QB-linked open invoice in your workspace, asks QB for the current paid amount, and writes the delta as a payment row when QB has more paid than CoPortal records. Manual Preview invoicesimport is still available if you want to trigger it on demand or import historic records that haven't been linked yet.

Connecting QuickBooks

Go to Settings → Integrations and click Connect QuickBooks. You'll be redirected to Intuit, sign in to QuickBooks Online, pick the company you want CoPortal to connect to, and authorise. CoPortal handles the OAuth dance and stores your access + refresh tokens encrypted at rest (AES-256-GCM). Tokens refresh automatically on a rolling 100-day window — no manual reconnects under normal use.

After connecting, the QuickBooks card in Settings shows the company name and how many invoices/clients are currently linked. The connection is workspace-wide — every team member with access to invoices and clients can sync.

Note: Sandbox vs Production is environment-driven. Production is the default; for development, set QB_SANDBOX appropriately. The OAuth flow auto-targets the correct host.

Outbound — what CoPortal pushes to QuickBooks

Invoices

Every invoice gets a Sync to QuickBooks button. On first sync CoPortal:

  • Finds or creates the QB customer (by stored qb_customer_id first, then by email, otherwise creates new)
  • Ensures a single shared service item exists (CoPortal uses one “Professional Services” item — your QB chart of accounts stays clean)
  • Builds the QB invoice with your invoice number as DocNumber, line items, VAT/GST rate, due date, and notes
  • Stores the QB invoice ID + sync timestamp on the CoPortal record so the next sync updates this same QB invoice

Re-syncing reads the QB invoice's SyncToken first, then PATCHes — so subsequent edits update the existing QB record rather than creating a duplicate. Use this freely.

Quotes (QuickBooks Estimates)

Quotes push as QuickBooks Estimates with status mapping:

  • Draft → Draft
  • Sent → Pending
  • Accepted → Accepted
  • Declined / Expired → Closed

Multi-option (tiered) quotes only sync the accepted tier — until acceptance, the headline option goes as a single estimate so QB doesn't see four parallel estimates for the same job.

Clients

Clients are synced lazily — the first time you push an invoice or quote for a client without a qb_customer_id, CoPortal creates the QB customer and stores the link. You can also bulk-create QB customers via the Preview customers import (see below) and CoPortal will reuse those links going forward.

Inbound — what you can import from QuickBooks

Customers

Click Preview customers on the QB card to see every QB customer tagged:

  • New — not in CoPortal yet, will be created
  • Duplicate email — already exists in your CoPortal clients (skipped unless you tick it)
  • Linked — already linked to a CoPortal client

A read-only field-mapping table shows how QB fields land in CoPortal (DisplayName → name, primary email → email, primary phone → phone, billing address → address). Tick the rows you want, confirm, and the chosen customers land as CoPortal clients with their QB link recorded.

Invoices (with payment history)

Click Preview invoices. Every QB invoice shows up tagged with both:

  • Paid status — paid, partial, unpaid, draft, or voided (derived from QB's TotalAmt minus Balance)
  • Import action — will import, already imported, no CoPortal client (need to import the client first), or number conflict (the DocNumber clashes with one of your CoPortal invoices)

On import, CoPortal:

  • Preserves the QB DocNumber as the CoPortal invoice number — falls back to QB-<DocNumber> if it would clash with one you already issued
  • Mirrors partial payments — any QB invoice with an outstanding balance lands in CoPortal with the already-paid amount written as an invoice_payments row, so the outstanding balance is correct from day one
  • Deduplicates by QB invoice ID — re-running an import won't duplicate
  • Lets you optionally re-email unpaid invoices to clients via CoPortal at import time (off by default — clients usually got the QB email already)
Note: Imported QB invoices land in CoPortal with reminders disabled, since CoPortal didn't send the original email and shouldn't start dunning the client out of nowhere. Toggle reminders on per-invoice if you want to opt that specific record into CoPortal's reminder cron.

Estimates

QB estimates can be imported as CoPortal proposals via the same preview flow. Status carries over (Pending → Sent, Accepted → Accepted, Closed → Declined).

Settings — every toggle and what it changes

All three live on the QuickBooks card in Settings → Integrations. They're independent — flip any combination.

Auto-sync

default: off
When on, every save of a CoPortal invoice fires a fire-and-forget push to QuickBooks. New invoices auto-create the QB record on first save; edits update it. When off, the per-invoice Sync to QuickBooks button is the only way data leaves CoPortal — useful when you want to control timing (e.g. push a batch on Friday afternoon, not as you draft).

Sync as draft

default: off
When on, invoices are pushed with a PrivateNote marking them as drafts — you finalise/send from inside QuickBooks. When off, invoices are pushed as approved/sent and treated as live in QB. Useful when an accountant wants to review before billing the client from the QB side.

Display-only payments

default: off
When on, marking an invoice paid in CoPortal will never push paid status or payment objects to QuickBooks — the accountant owns payment state in QB. When off, behaviour is the same today (CoPortal's QB sync doesn't push payment status either way) but turning it on locks the contract: future code paths can't accidentally start syncing payments. Recommended on if your accountant runs the books in QuickBooks and you want CoPortal as the agency-side management layer.

Common ways teams run it

Three patterns cover almost every agency. Pick the one that matches your accountant's workflow.

Flow A — Display-only mirror (most common)

When to use it · Your accountant runs the books in QuickBooks; CoPortal is your client-facing layer

Toggles: Auto-sync ON · Sync as draft OFF · Display-only payments ON

How it flows:

  • You issue invoices in CoPortal (with quotes, project context, the works)
  • Each invoice auto-pushes to QB on send — your accountant sees it land instantly
  • Client pays → you mark paid in CoPortal so cash flow / dashboards / commissions update. Nothing changes in QB.
  • Accountant matches the bank transaction in QuickBooks like they always do — they own payment state there.
  • Periodically use Preview invoices import if you want to mirror the accountant's payments back into CoPortal for the few records you didn't mark paid yet.

This is the cleanest split — no fights over “wait, who marked this paid?” CoPortal is the originator of new records; QB is the system of record for cash receipts.

Flow B — CoPortal as source of truth

When to use it · You handle billing yourself; QuickBooks is just a back-office mirror

Toggles: Auto-sync ON · Sync as draft OFF · Display-only payments OFF

How it flows:

  • You issue invoices in CoPortal; auto-sync pushes them to QB live
  • Client pays via Paystack / EFT / card → you record the payment in CoPortal
  • The next time that invoice gets edited and saved, the auto-sync push includes the current status — QB stays in step
  • Imports run once at onboarding to bring historical billing into CoPortal, then it's mostly outbound forever

Best if you don't use QB's bank feed / matching workflow at all and just want a QB mirror for tax filing.

Flow C — Draft review

When to use it · You draft, the accountant reviews + sends from QB

Toggles: Auto-sync ON · Sync as draft ON · Display-only payments ON

How it flows:

  • You draft an invoice in CoPortal with all the project details
  • It pushes to QB as a draft with a [Draft — review in QuickBooks before sending] note
  • Accountant reviews and sends from QB. Payment state is owned in QB.
  • You see the QB number / paid status by running an invoice preview import periodically.

Common in tightly-controlled finance setups (regulated industries, companies with strict approvals).

What happens when I mark an invoice paid in CoPortal?

With Display-only payments OFF (default): nothing reaches QuickBooks. CoPortal's QB sync only pushes invoice creation/update — no payment objects, no Status flips. The next time that invoice is edited and re-saved (auto-sync) or you click Sync to QuickBooks, the push still only includes invoice fields, not payments.

With Display-only payments ON: same behaviour today, but the toggle locks the contract going forward. If a future CoPortal release adds payment-object pushing for QB, this flag will stop it firing for your workspace.

Either way: paying in CoPortal updates CoPortal (cash flow, dashboard tiles, commissions, automations like “invoice paid” triggers, the client's portal). Your accountant's QB stays untouched until they record the payment there themselves.

Important: This is different from Xero. Xero's sync code doesmap “paid” status into the push payload — the equivalent toggle on Xero clamps it back to AUTHORISED. The QB integration was already display-only by design; this toggle exists for parity and to lock the contract.

What does NOT sync

  • Payment objects from CoPortal → QB. Period. (Mark in CoPortal, your accountant matches in QB.)
  • Time entries, expenses, project tasks — these live in CoPortal only. QB doesn't know about them.
  • QB's chart of accounts customisation — every CoPortal line item maps to one shared service item to keep QB tidy.
  • QB items / products — CoPortal doesn't maintain a parallel item list. Line item descriptions and unit prices come from CoPortal directly.
  • QB classes / locations / departments — not currently supported.
  • Automated retries — if a sync fails because the QB token was revoked or QB is down, the push is dropped (auto-sync is fire-and-forget). You'll need to re-trigger by editing/saving or clicking the manual sync button.

Disconnecting

From the QuickBooks card click Disconnect. CoPortal deletes the encrypted token row and stops every push. Existing CoPortal records keep their qb_invoice_id / qb_customer_idas harmless metadata — if you reconnect later (same QB company), every linked record finds its match without re-importing.

For a complete revoke, also remove CoPortal from QuickBooks → Settings → Apps. CoPortal does not call Intuit's revoke endpoint on disconnect by default.

Troubleshooting

“QuickBooks not connected” appears mid-flight

The refresh token expired or was revoked from the QB side. Reconnect from Settings → Integrations → QuickBooks → Connect. Existing record links survive.

An invoice number conflict on import

Means the QB invoice has the same DocNumber as one of your CoPortal invoices. CoPortal will fall back to QB-<DocNumber>automatically — no manual fix needed.

I imported a QB invoice but reminders started going out

They shouldn't — imports default reminders to off. If they did, check the per-invoice reminders toggle (now exposed on the invoice editor since phase 98) and turn it back off.

I marked paid in CoPortal but nothing happened in QB

That's the intended behaviour — CoPortal's QB sync doesn't push payment status (with or without the “Display-only payments” toggle). Your accountant records the payment in QB. The reverse direction (QB → CoPortal) runs automatically once an hour, so the next pull will mirror their entry back to CoPortal.

I want CoPortal to mark QB invoices paid for me

Not currently supported, by design. Payment state is owned by QuickBooks; CoPortal mirrors it back hourly. If your team genuinely needs CoPortal-side payments to push out to QB, email hello@coportal.io.

QB-paid invoice still shows as unpaid in CoPortal

The hourly pull runs on the top of each hour. If you just marked it paid in QB, wait up to an hour. If it's been longer, the most likely causes are: the CoPortal invoice doesn't have a qb_invoice_idlinking it to the QB record (push it once from CoPortal to establish the link), the QB connection's tokens expired (reconnect from Settings → Integrations), or the QB invoice was voided rather than paid.