Stop emailing PDFs.
Each client gets a read-only portal where they can see their own invoices and approved hours. One URL, one login, no shared inboxes. Hosted card and ACH pay arrives Q2 2026 via Stripe Connect.
A read-only window into the work you've done.
Invoices
Open and paid invoices in one list. Click any line to download the PDF. Filter by status or date range.
Approved hours
Approved time, by project and date range. The same numbers that show up on their invoice โ visible before the invoice is even sent.
Per-contact login
Add multiple contacts under one client. Each gets their own login (no shared password). Deactivate a contact when someone leaves their company without affecting the rest.
Read-only access for client contacts.
Linked to your client records via PortalClientId. The Client role only sees their own client's data — never another client's, never internal projects, never user-level cost rates. Lives under /portal in your subdomain.
- Per-client access scoped via PortalClientId
- Open + paid invoice list with PDF download
- Approved hours by project and date range
- Multiple contacts per client, each with own login
- Hardened authorization — no cross-client leakage
Stripe Connect — coming Q2 2026.
The pieces are staged: every invoice already has a PublicAccessToken for a hosted-pay URL, and the Tenant model is wired for Stripe Customer + Subscription IDs. The remaining work is the Stripe Connect onboarding flow and the public pay-page UI. Q2 2026.
- Public pay URL per invoice (token already on every invoice)
- Card + ACH at Stripe's standard rates
- Auto-mark Paid on successful payment
- Receipt emailed to the contact and the agency
What the portal does today, what's coming.
Today: read-only invoice list, PDF download, approved hours visibility, per-contact login, hardened cross-client isolation. Already in production.
Q2 2026: hosted pay via Stripe Connect. The PublicAccessToken on every invoice and the Stripe ID fields on every tenant are already in place โ we're finishing the onboarding flow.
Roadmap: client-side approval of quotes / proposals (Q3 2026), client-uploaded documents (Q4 2026). Both follow the same scoped-by-PortalClientId pattern.
Common questions
Can the client see internal hourly rates?
No. The portal shows the totals on their invoices and approved hours by project. Cost rates and your team's per-user billable rates are never exposed — that's enforced at the data layer, not the UI.
How do clients log in?
Each client contact you add gets an invitation email with a link to set their password. After that they sign in at /portal under your subdomain. The Client role is locked to portal pages only — they can't reach any internal page.
Can I have multiple contacts per client?
Yes. Add as many as you need under one client record. Each contact has their own login. Deactivate a contact when someone leaves their company — the rest keep working.
Can the client see other clients' work?
Never. The Client role is scoped to a single PortalClientId. Every portal query filters by it. There's no admin-level switch that bypasses this — even SuperAdmins can't impersonate a Client into another Client's data.
When does hosted pay arrive?
Q2 2026. The pieces are already in the codebase (PublicAccessToken on every invoice, Stripe IDs on every tenant). We're finishing the Stripe Connect onboarding flow and the public pay-page UI.