Feature

A clean portal for the people who pay you.

On Studio and Agency, every client contact you mark as portal-enabled gets a magic-link login to a per-client view of their invoices, current balance, and history. No password to forget. No app to learn. Scoped tightly so one client can never see another client's data.

Plans: Studio · Agency

Free for solo freelancers · No credit card required

What's in it

  • Passwordless magic-link login

    Client types their email; we send a one-time link with a 15-minute TTL. The token is hashed at rest, single-use, and rate-limited to 5 per 10 minutes per email. Enumeration-resistant by design — wrong email, disabled contact, and rate-limit drop all return the same neutral message.

  • Per-client scoping, structurally

    Portal sessions live in a separate cookie (PORTAL_SESSION_SECRET) from the team Auth.js session. The proxy branches on /portal/* first so a team cookie never reaches the portal and vice versa. Reads pass through a forActor() helper plus an explicit clientId filter — verified by integration tests.

  • What the client sees

    Their invoices (draft invoices stay hidden until sent), current outstanding balance, payment history, and a single 'Pay' button on each open invoice that hits your Stripe Connect account directly.

  • Per-org branding

    The portal header shows your org name and logo (if uploaded). Your clients see your brand — not a generic Hoursmith chrome.

  • Activity-logged on both sides

    Magic-link requests, portal logins, and portal payments all write activity rows in your org's audit log. You see who-came-when alongside your own team's edits.

  • Graceful downgrade behavior

    If your org moves back to Free, in-flight portal sessions see a 'portal disabled' notice instead of stale data. Individual shareable invoice links still work — view tracking is on every plan.

How it works

  1. 01

    1. Mark a contact portal-enabled

    On the client's contacts card, toggle 'Portal access' for the contact who should be able to log in. Multiple contacts per client supported.

  2. 02

    2. Share the portal URL

    Your client visits /portal/login on hoursmith.app, types their email, gets the magic link in their inbox.

  3. 03

    3. They click → they're in

    Verified token, signed cookie issued, redirected to their per-client portal home.

  4. 04

    4. They pay or sign out

    Online payment (Studio+) hits your own Stripe via the same Connect flow as the public invoice link. Sign out clears the cookie immediately.

Common questions

  • Can a client see another client's invoices?

    Structurally, no. Reads run through forActor(orgId, clientId) which filters at the Prisma extension layer, plus an explicit clientId in the where clause, plus a post-condition re-check. Three layers — integration tests actively try to break this and all attempts return 0 rows.

  • What if a client loses access to their email?

    Add a second portal-enabled contact at the same client. Both contacts get independent magic-link flow; either can log in. There's no shared password to reset.

  • Can I disable a contact's portal access without deleting them?

    Yes. Toggle off 'Portal access' on the contact and any future magic-link requests for that email are silently dropped. Existing sessions expire on their own TTL.

  • Is this on the Free plan?

    No. Client portal requires Studio or Agency. Free plans still get per-invoice shareable links and view tracking, which is the right tool for most one-off invoices anyway — the portal pays off for repeat-billing relationships.

Related

Try it free, in two minutes.

Create an account, set up your first client, and log an hour. The invoice will already be on the way to building itself.

Free for solo freelancers · No credit card · Cancel any time

Skip to content