Product · Agency operations
ClientOS
Service checkout, a branded client portal, delivery tracking, helpdesk and billing for agencies selling productized services, on the agency’s own server.
- Self-hosted platform · Client portal
- 2026

Customer story · Dot Mirror
A link building agency left Wayfront and owns its portal now.
Our partners trust us with their clients’ names, and for years that list sat on someone else’s servers. ClientOS put the whole operation on our own server, under our own name, and the monthly bill simply went away.
What changed
- monthly portal bill, one license paid once
- $0
- kept over three years against Wayfront Pro
- $6,274
- of client history moved: orders, messages, invoices, files
- 100%
- from install to live on clients.dotmirror.com
- 7 days
A white-label link building and digital PR agency, moved with its partners, orders and years of history onto its own server in one week.
About ClientOS
Introduction
Agencies that sell productized services — link building, SEO, content, editing — tend to run on a patchwork: a form here, an invoice there, status updates by email, and a spreadsheet holding it together. ClientOS replaces the patchwork with one platform the agency runs on its own server. Clients order from service pages that are also the checkout, track delivery and approve work in a branded portal, and pay through subscriptions with automatic dunning; the agency runs orders, helpdesk and reporting from a role-gated admin, with margin visible to the agency and never to the client.
Details
- Project
- ClientOS
- Timeframe
- 2026
- Type
- Self-hosted platform · Client portal
- Live
- getclientos.com
- Next.js
- NestJS
- PostgreSQL
- Redis
- Drizzle
- Stripe
- Playwright
- Docker
- Caddy
- Problem
- Selling and delivering productized services across forms, email, invoices and spreadsheets means slow updates, lost context and no clear view of margin per order.
- What we built
- A platform with service pages that take the order (packages, add-ons, coupons), a branded client portal, delivery with client approval, a helpdesk with response targets, recurring billing with automatic retries and margin reporting per service — installed on the agency’s own server, one install per agency.
- Outcome
- Shipped as ClientOS, a licensed product agencies install on their own server with one command. Client data stays on that server; only the license check-in reaches us.
- staff permissions, across built-in and custom roles
- 122
- payment gateways: Stripe, PayPal and three for crypto
- 5
- default order statuses, each one the agency can edit
- 6
- command to install it on the agency’s own server
- 1
Inside the admin.
The console an agency runs its orders, clients and money from, rebuilt here in code from its own screens. Pick a page, or click the sidebar.
Money, throughput, and what is about to slip
The ops dashboard is put together panel by panel from the viewer’s role: the month’s revenue and margin, whose move each open order is waiting on, what is past its promise, and how loaded each person on the team is.
- Revenue, collected and gross margin
- Waiting on us, or on clients
- Past the promise, on our time
- Team capacity per person
Recreated from the admin’s interface. Labels, columns and statuses are ClientOS’s own; the agency, clients, people, orders and figures are samples.
The parts, up close.
The workings behind the admin and the portal, at full size.
Order flow
Six statuses that move themselves
Nobody sets an order’s status by hand. The brief, Start order, a hand-over, the client’s answer and Cancel move it, and the agency can rename and recolour each one.
Time
A clock that knows whose move it is
Each order counts down to its due date and splits the time spent with the team from the time spent waiting on the client.
Helpdesk
A response target per priority
The first reply is due within a set number of hours, and the clock stops while a ticket is pending on the client.
Client portal
The client signs it off
In the portal the client approves a hand-over or asks for a revision. One nobody answers is approved automatically after seven days.
Messages
An offer they can pay from the chat
Staff price a custom bundle inside a conversation; accepting it raises the invoice and paying it places the order.
Roles
Six roles, 110 permissions
Each role is a set of grants the sidebar and the server both read, so the menu and the door never disagree. Agencies can add their own.
Store
Five ways to put a price on it
Pricing model and billing cycle are two separate choices, so a service can be tiered and one-time, or fixed and monthly.
What went into it.
- 01
Sell
Service pages with packages, add-ons, volume pricing and coupons, priced on the server, with the brief collected in the portal once the order is paid.
- 02
Deliver
Six default order statuses the agency can rename, client approval or a revision request on each hand-over, and a helpdesk whose response clock pauses while waiting on the client.
- 03
Get paid
Recurring invoices charged to a saved card with automatic retries, invoices in the portal, and margin reporting that never exposes cost to clients.
How it is built.
The running parts
- Portal and admin
- Two Next.js apps, one for clients and one for the agency, sharing a design system. Every write goes through a server action.
- API
- NestJS on Fastify, and the only process with a database connection.
- Domain package
- Pricing, statuses, response targets and permissions as plain functions with no framework in them, which is where most of the tests live.
- Data
- PostgreSQL through Drizzle, which is also the job queue. Redis is optional, for presence only; files go to S3-compatible storage.
- Deployment
- Ready-built Docker images behind Caddy, which handles HTTPS and is the only container with open ports, with a migration step that runs before the API starts.
The hard parts
- 01
One install, one agency
Each agency runs its own copy, so its clients’ data never shares a database with anyone else’s. Inside it, each client company sees only its own orders, and the API connects as a least-privilege role, refusing to start in production as a superuser.
- 02
Cost never reaches a client
Responses are built against a whitelist of fields, and the version with cost in it is chosen only for staff allowed to see finance. Portal queries never select cost, and a test walks every response looking for anything named cost or margin.
- 03
A license that survives a bad connection
The license is signed and bound to the agency’s domain, and the install checks in twice a day. If it cannot, a seven-day grace period runs before the admin turns read-only; the client portal and the data are never touched.
- 04
A response clock that pauses fairly
When a ticket is waiting on the client, the clock stops. When it resumes, the due time moves by exactly the length of the pause — or by the open time left, when business hours are on.
- 05
Recurring billing without charging twice
The platform keeps its own renewal clock and raises one invoice per cycle, which the database enforces. The saved card is charged with a key unique to that invoice and attempt; a failed charge is retried on days one, three and five before the plan is paused.
- 06
One price, settled once
A single function applies base price, add-ons, volume discount, promo and credit in that order, for the checkout and the server alike. Payment can be confirmed by the return page, a webhook or a reconcile job, and a row lock makes sure it counts once.
Want to see more?
Start here
Have a problem worth solving?
Tell us what is slow, manual or missing. You will hear back within one working day — then get a written scope and a fixed quote, or an honest no.
- # New project brief
- ## What is slow, manual or missing?
- - [x] Written scope
- - [x] Fixed quote
- - [x] Or an honest no

