Thursday 3 PM. Acme Corp asks on WhatsApp: Did you say 2 revisions or 3? You search WhatsApp revisions — no result because you said it on the kickoff Zoom, summarized in email thread Re: Kickoff. You search Gmail Acme revision — 7 results, one with 2 revisions but is that latest? Slack DM from their designer yesterday says 3 revisions approved? You answer 2 with gut, not proof. Friday they expect third revision free, citing Slack DM you never linked to the project.
Freelance communication debt is not too many messages. It's decisions scattered across no FK.
Why WhatsApp + Email + Slack + HubSpot Comments Lose Decisions
You communicate where client is — correct instinct, wrong architecture:
- WhatsApp — fast, searchable only on phone, no
clientIdsrc/lib/db/schema.ts:13, noprojectIdsrc/lib/db/schema.ts:25. Thread forAcme invoice questionburied underMomchats. You star message, star 40 more, stars meaningless. - Email — threads split
Re:,Fwd:, CC loses context. DecisionApprove homepage heroin email12:04not visible fromTasks → Todaysrc/app/api/tasks/route.ts:98when you build hero. You hunt instead of build. - Slack —
Acmechannel + DM + thread = 3 places for one decision. NoportalTokenssrc/lib/db/schema-public.ts:169self-serve; client pings you because they can't seeproject.status active|completedsrc/lib/db/schema.ts:21. - HubSpot / Notion — CRM comment holds
Call notesbut not linked totasks prioritysrc/lib/db/schema.ts:52orinvoices amountsrc/lib/db/schema.ts:69orvaultItems AES-256-GCMsrc/lib/vault/crypto.ts:22. You add comment, then re-type same decision into Trello card comment — string, not FK. - Sheets/Excel —
Communication logtab withDate | Client | Decisiontyped asAcme — approved. Next monthAcmebecomesAcme Pvt→ filter misses history. NoonDelete:cascadesrc/lib/db/schema.ts:26, no atomictenant_${id}src/lib/db/tenant.ts:47.
Result: you spend 45 minutes daily reconstructing What did we agree? before doing work. Each tool holds a piece; brain holds the JOIN. Brain is slow JOIN.
The Linked Communication System: One Dossier, Every Thread Pointed There
In RunoSO, communication is centralized by linkage, not by forcing clients to your app. You still reply on WhatsApp where client lives — but every decision lands on Clients → Acme dossier src/app/(dashboard)/clients/[id]/page.tsx:
clients(id, name, email, phone, company, status lead|active|inactive src/lib/db/schema.ts:5, notes text)
1—∞ projects(clientId fk, description, notes, deadline, budget)
1—∞ tasks(projectId fk, status todo|in-progress|done, dueDate)
1—∞ invoices(clientId, projectId)
portalTokens(tenantId+clientId unique, token, revokedAt src/lib/db/schema-public.ts:169)
notifications(userId, type, title, href src/lib/db/schema-public.ts:190) — batch, not streamHow it works daily:
- Single note field per client/project. After any WhatsApp/email/Slack decision, paste 1-line summary into
clients.notesorprojects.notessrc/lib/db/schema.ts:14,32—2026-08-29 Acme: 2 revisions cap, hosting excluded, via WhatsApp voice 1:20. 10 seconds, FK-linked, searchable later without hunting 4 apps. Notion can't enforce link;notes textwithclientIddoes. - Decisions become tasks.
Acme: 3 revisions approved →newtasks projectId=Acme Growth Revamp priority:medium dueDatesrc/lib/db/schema.ts:45. NowTodayFocusCardsrc/components/layout/NotificationsBell.tsxsurfaces commitment where work happens, not where chat happened. - Portal as shared memory.
POST /api/clients/[id]/portalsrc/app/api/clients/[id]/portal/route.ts:169→portal/[token]showsprojectswithtotalTasks/doneTaskssrc/app/api/projects/route.ts:28+invoices paid|sent|overdue+ asset links. Client checks portal before pingingWhere's invoice?—portalTokens revokedAtsrc/lib/db/schema-public.ts:175controls access without new login. - Batched notifications.
cron/dailysrc/app/api/cron/daily/route.ts:275generatesnotificationsrows forinvoice sent→overdue,domains expiring 14/7/3/1/0. You checkNotificationsBelltwice daily (12 PM, 5 PM) batch, not real-time pings that fragment deep worksrc/components/layout/NotificationsBell.tsx.
You didn't add a tool. You added a place decisions return to.
The Communication Cadence That Prevents Chasing
Structure beats responsiveness:
- Kickoff:
portal link + Notes + deadline 4 weeksvisible before first message. Client who can self-serve pings less. - Weekly: Friday 4 PM portal update — 3 bullets in
project.notes, movetasks status. No Sundayquick update?because rhythm exists.taskStreaks isCompleteDaysrc/lib/db/schema.ts:262closes week. - On decision: 1-line
notes+taskcreation if work, orinvoiceif paid change. Takes 45 seconds, saves 25-minute archaeology next week. - Batched replies: 12 PM + 5 PM reply windows.
portalanswers 80%Where's...?so batch is enough. Sheets has no batch model — every DM is interrupt.
Freelancers who reply fastest don't retain most. Those whose decisions are findable do.
Mistakes That Scatter Threads
Mistake 1: Using new tool per client.
Acme on Slack, Beta on WhatsApp, Gamma on email feels bespoke; search becomes grep brain 3 APIs. Use client tool but centralize note to clients.notes — one FK survives tool.
Mistake 2: Logging decisions as chat, not linked task.
Approved hero staying in WhatsApp star never appears in GET /api/tasks?view=today src/app/api/tasks/route.ts:98. Promote decision to tasks.projectId or transactions so work queue reflects truth.
Mistake 3: Letting portal stay empty.
Creating portalTokens but never updating projects status makes portal lies. Empty portal trains client to ping you. Keep status active|on-hold|completed + deadline current — 30 sec weekly — portal stays trust.
HubSpot / Notion / Excel vs Linked Dossier
| Communication need | HubSpot + Notion + Sheets + WhatsApp groups | RunoSO tenant_${id} linked dossier | |
|---|---|---|---|
We agreed 2 revisions | Hunt 4 apps, 3 spellings Acme | clients.notes + projects.notes linked clientId uuid — one search | |
| Decision → work | Re-type into Trello card | tasks projectId fk auto appears in Today + totalTasks/doneTasks | |
Client asks Where's invoice? | Forward email, DM login | portalTokens portal/[id] shows `invoices sent | paid self-serve + sendInvoiceEmail portal link src/lib/emails/send.ts:347` |
Overdue nag | Manual follow-up thread | cron daily sent→overdue src/app/api/cron/daily/route.ts:69 notifications batch | |
Vault Q Where's staging login? | Search 1Password flat acme-server ?? 4 results | vaultItems clientId/projectId masked •••• src/app/api/vault/route.ts:25 one dossier, one click | |
| Scaling to 6 clients | 6 tool invites, 14 DM contexts | Same tenant_${id} FK cascade graph src/lib/db/schema.ts:45 — one login |
HubSpot timelines are flat logs. tenant_${id} is a graph with FK — queries replace hunting.
Install the System This Week
- Today: Pick your top 3
activeclients. Open eachClients → detail→ paste last week's 3 decisions intonoteswith date + channel. Tagstatus active. You're already 80% linked. - Tomorrow: Create portal for one.
POST /api/clients/[id]/portal→ sendportal/[token]with message: Your live progress + invoices here, updates Fridays 4 PM. Count pings next 7 days vs prior week. - Set batch windows: 12 PM + 5 PM check
NotificationsBell+ reply. Mute after 7 PM —portalcoversWhere's ...?for you,autoRenew:truesrc/lib/db/schema.ts:154covers domain anxiety. - Rule: Decision =
notes+taskorinvoice. After every client call 5 min: summarize toproject.notes, spawn tasks if needed. Promise becomes queue, not memory.
Threads will still live where clients live. Decisions will live where you work. Linked beats scattered.
RunoSO is your client dossier — linked clients → projects → tasks → invoices → vault → portal in one dashboard. Stop hunting threads, start pointing them to one place.
→ Unify Your Threads — Try RunoSO Free
By Gautam Parmar src/app/(marketing)/about/page.tsx:44 — solo founder, Next.js 16 Tailwind v4 Neon Drizzle AES-256-GCM, building in public on X.
Keep Reading
- Keep scope tight: freelance proposal to project handover.
- Protect handover: where freelancers should never store passwords.
- Daily execution: freelance daily system today backlog streaks.
FAQ
Should I force clients to my portal chat? No. Reply where client is (WhatsApp/email), then log decision to clients.notes/projects.notes src/lib/db/schema.ts:14,32 and tasks.projectId. Portal is read-only shared memory portalTokens src/lib/db/schema-public.ts:169, not new chat to train them on.
How is this different from HubSpot? HubSpot holds Deal + timeline flat; vault, assets_domains, transactions live elsewhere. RunoSO tenant_${id} links budget numeric + tasks + invoices + vaultItems clientId/projectId AES-256 under one clientId uuid — one dossier query, not 4 tool hops.
What if I have 50+ client threads? Filter clients status:lead|active|inactive src/lib/db/schema.ts:5 weekly src/app/(dashboard)/clients/page.tsx, batch notifications twice daily, use portal self-serve to cut 60% Where's...? pings. Linked dossiers scale; hunt doesn't.



