Blog · May 1, 2026 · ~9 min read
FreshBooks retainer tracker: why the client portal doesn’t answer “how many hours do I have left?”
FreshBooks is unusual in this category in that it ships three things you’d expect to make the recurring “how many hours do I have left?” client email go away: time tracking, a retainer billing object, and a real client portal with login. So if you bill a retainer through FreshBooks, the in-the-box solution to the question is supposedly already on your client’s desk. They have an account. They can log in. They can see the retainer. And yet they email you anyway. The reason isn’t the obvious one (“clients don’t bother logging in”). It’s structural: FreshBooks’ portal answers the billing question, not the status question, and the recurring email is a status question wearing a billing question’s clothes.
What FreshBooks’ client portal actually shows
Worth being literal about it. When your retainer client logs into my.freshbooks.com via the link in your invoice email, the page they land on is a billing dashboard. They see: outstanding invoices (with a fat “Pay” button), paid invoices (with a date stamp), proposals you’ve sent, signed contracts, and — if you’ve set up the retainer billing object — the recurring retainer line item with its monthly charge and renewal date. It’s a clean page. It’s a useful page. FreshBooks’ product team built it for the situation it was built for: here is what you owe me, here is what you’ve paid, here is what we agreed. A bookkeeper would love it. An auditor would love it. A retainer client mid-cycle doesn’t love it, because the question they came with isn’t answered by anything on the page.
The retainer client opens the portal with one specific question in mind: am I about to be on the hook for an overage? That question is answered by a single piece of information: hours remaining in the current cycle. The FreshBooks portal does not show that number. It can’t. The portal knows that you bought 20 hours per month and that this month’s invoice was paid; it doesn’t know how many of those 20 hours have been worked yet, because the time entries are still on your side of the FreshBooks UI — on the My Time / Reports surface, which clients don’t see. The retainer object on the client side is a recurring charge, not a burn-down. So the client lands on a page full of invoices, doesn’t find the answer, and reaches for the email instead.
Three structural mismatches between the portal and a retainer status page
The cosmetic gap is small — FreshBooks could in theory render a burn-down number on the portal page tomorrow if they wanted to — but the architectural gap is wider than that. The portal and a retainer status page are different kinds of object. Three specific places where they diverge:
1. Invoice cadence vs. in-cycle question
The FreshBooks portal updates on the invoice cadence. New invoice issued: portal updates. Invoice paid: portal updates. Outside those two events, nothing on the portal moves — the page on the 14th of the month looks identical to the page on the 7th of the month, because nothing billable has happened in between (from the portal’s point of view). The portal is event-triggered, and the events are billing events.
The hours-remaining question is asked on a different cadence entirely. It comes up on the 14th of the month, the 22nd, the 28th — whenever the client has scoped a piece of work and wants to check whether the retainer can absorb it. None of those days are billing events; they’re project-flow events. The portal is structurally silent about them. So even when the client does log in (about half do, on first invoice; far fewer log in twice), the page doesn’t move between their visits, and they correctly conclude there’s nothing to come back for. They go straight to the email loop instead.
2. Login wall vs. glance check
A retainer status page is a glance product — the entire point is that the client looks at it for three seconds, says “okay, eight left, fine,” and walks away. Anything that adds friction in front of those three seconds defeats the product. A login wall adds friction. Even a perfectly-designed login flow — remembered email, autofilled password, biometric prompt — is at best ten extra seconds and at worst a password-reset side quest because the client hasn’t logged in for two months and the cookie expired.
FreshBooks’ portal is a real account. It has to be: it shows financial information (paid/unpaid invoices) that’s sensitive to the client’s business, and it has to be auth-gated. That’s the right call for the billing surface. It’s the wrong shape for the status surface. The client opens their inbox to ask “how many hours left?” precisely because the portal is friction and the email isn’t. Putting the answer behind the same friction the email was an escape from doesn’t change the equilibrium — it just adds a place the client doesn’t go.
3. Retainer as billing object vs. retainer as status page
The retainer in FreshBooks is a billing object: an amount, a frequency, a renewal date, a payment status. That model is correct for billing — it’s how the IRS thinks about retainers, it’s how your accountant thinks about retainers, and it’s how your bookkeeping software has to think about retainers. The recurring charge, the paid/unpaid flag, and the renewal date are exactly the right fields for that view.
A retainer-as-status-page is a different object with different fields. The fields are: cap (e.g. 20 hours), burn (e.g. 12 hours used), remaining (8 hours), cycle (resets the 1st), and work log (what those hours went into). Those fields aren’t in FreshBooks’ retainer billing object because they don’t belong there — they belong on a separate page that reads from your time entries instead of from your billing setup. FreshBooks could ship that page, but it would be a different page. The retainer-as-billing-object isn’t failing at being a retainer status page; it’s succeeding at being a retainer billing object, which is the job it was designed for.
What the client’s behaviour tells us
The signal is the email itself. If the FreshBooks portal answered the hours-remaining question, retainer clients on FreshBooks-billed retainers wouldn’t email; they’d log in and look. The fact that they email anyway — reliably, monthly, sometimes more than monthly — tells you the portal isn’t in their decision tree for that question. They might use it to pay invoices (mostly via the email link, not by logging in). They might pull it up to check a payment status. They don’t pull it up to check hours remaining, because they’ve learned (correctly) that the page they’d land on doesn’t carry the answer.
This is the same pattern as the underlying status-vs-billing confusion that makes the email recurring across all retainer setups, regardless of which billing tool is upstream. It’s also exactly the same shape as the Toggl shared-report mismatch at the other end of the stack: a tool aimed at a different question gets pressed into service for the hours-remaining question, and the client behaviour reveals the misfit faster than any feature spec sheet would. The fix is the same in both cases: a page shaped for the actual question, addressed to the actual audience, on a URL that doesn’t require an account.
What a FreshBooks-shaped retainer status page should look like
Same FreshBooks data. Different render. Take the Detailed Time export — the same one your accountant uses to verify hours billed against hours worked — and pass it to a layer that knows three things the FreshBooks portal doesn’t:
- The cap — e.g. 20 hours / month. Now “remaining” is computable from the entries that are already in FreshBooks.
- The cycle — e.g. resets the 1st of each month. Now “current” means today’s burn-down, not last invoice’s totals.
- The audience — the client, not your accountant. No login wall, no payment surface, no proposals stack. One page, one number, one progress bar, one cycle reset date, one work log.
That’s the whole spec. FreshBooks already has the time entries; the retainer status page just reframes them. The output is one URL per retainer, with a fat 8 hours remain at the top, a progress bar under it, the cycle reset date next to it, and the FreshBooks Detailed Time entries below the fold for the client who wants to see what those hours went into. No login. No portal. Just a URL.
That’s the shape HourTab’s FreshBooks integration ships. You drag in the same Detailed Time CSV you already export for invoicing — the one with Date, Notes, and Duration columns — we auto-map them, you set the cap and the cycle once, and the URL goes to the client. From then on, re-importing a fresh CSV updates the same URL the client already bookmarked. FreshBooks keeps doing the billing job it’s good at; HourTab adds the status page that was missing.
When the FreshBooks portal is the right answer
It’s worth being explicit about when the built-in FreshBooks client portal is in fact the right answer, because for some retainer shapes it is. If your retainer is billed as a fixed monthly amount with no hours cap — truly “buy me for $X / month, work whatever you work” — the hours-remaining question doesn’t apply, and a separate status URL is solving a problem your client doesn’t have. The FreshBooks portal handles invoicing fine, your client never asks the question, and you don’t need a second page. Same goes for retainers where the cap is generous enough that no client has ever come close to it, or for one-off project work that’s scoped in advance and billed at completion.
The line is whether the cap is a real constraint that gets pressed against. If it is — if there’s any month where the client could plausibly run out — the hours-remaining question will get asked, the portal won’t answer it, and the email loop will resume. Knowing which retainer shape you’re running is the difference between a setup that retires the email and a setup that just adds another login the client doesn’t use.
The takeaway in one line
FreshBooks’ client portal is shaped for billing — invoices, payments, recurring charges — and that shape is correct for the job it was built for. The hours-remaining question is a different shape: it wants a cap, a cycle, and a current burn-down on a page anyone can open, not a billing dashboard behind a login. If your retainer has a cap that gets pressed against, the portal can’t answer the question your client keeps emailing about — not because FreshBooks is doing anything wrong, but because the question is on the status surface and the portal is on the billing surface. The fix is to add the status surface, not to wait for the billing surface to grow into it.
If you bill retainers through FreshBooks and you recognize the loop this post is about, you can see how the FreshBooks-CSV-to-public-URL flow works on the integration page, or read the case for why a public URL beats a client portal regardless of which billing tool is upstream. The waitlist on the pricing section is open — free for the first retainer, no credit card — and the only email we’ll send is the one when your first share URL is ready.