Houdrik
A regional B2B/B2C services brand

Customer-service platform for a regional services brand

Replaced a patchwork of shared inboxes, Excel sheets, and an inherited backend nobody owned with a proper customer-service platform. Shipped in three months, fixed scope.

Cover · case-customer-service-portal

The problem

The client served their first hundred customers from a shared inbox, a marketing site, a Google Drive folder of contracts, and an Excel sheet that tracked status. The setup worked. The next hundred customers broke it.

By the time we walked in, three different staff members maintained three slightly different versions of "the truth" about open service requests. Customer emails got lost between the inbox and the spreadsheet. Contracts lived in a Drive folder structured by whoever uploaded the file last. The custom backend that wired some of this together had been written by a contractor who was no longer reachable, and nobody on the current team had read the code.

There was no audit trail. No structured search across a customer's history. No way for a customer to check status without sending another email. The team was about to hire two more support staff to keep up — which would have made the coordination problem worse, not better.

What we built

We replaced the patchwork with a single customer-service platform, shipped in three months on a fixed scope. The split is conventional and intentional: a public marketing site, a customer area sitting on top of an API, and internal staff tools built on a back-office tool extended in the right places.

The public site is the marketing surface — server-rendered for SEO, i18n where the business actually serves more than one language, no client-side JavaScript where a server response will do. It is the cheapest part of the system to operate and the one the customer sees first, so it gets the polish budget.

The customer area is where the work happens. A customer signs up, lands in an authenticated dashboard, completes a profile, opens a service request, uploads supporting documents, watches the status timeline update, and messages staff through a threaded conversation tied to the request. No more "did you get my email" exchanges. The state of every request is visible to the customer who owns it and the staff member assigned to it, in the same shape, at the same time.

The internal admin is a back-office tool extended where extension earns its keep — custom list views with the filters staff actually use, inline actions for triage and assignment, comment threads on every request, scheduled work attached to the customer record, reporting views that staff can pull without asking us. We did not build a separate "ops dashboard" application. The back-office tool, treated as a real product surface, covered ninety percent of what the team needed.

Underneath sits a relational database with a schema designed for the queries the business actually runs — partial indexes on open requests, semi-structured columns for the customer-specific fields that vary by service type, foreign keys everywhere a relationship is real. A background-job queue handles the things that should not block a request: email and push notifications for status updates, reminders for unanswered requests, nightly reports, document virus scans on upload.

Auth is session-based for staff and customers — no tokens floating around, no SaaS auth vendor. Role-based access distinguishes staff roles (admin, editor, viewer) from customer roles (account owner, delegated user). Every state-changing action writes to an audit log keyed by user and request, queryable from the admin. Search runs over database full-text indexes across customer history, scoped by role.

A payment provider is wired in for the paid services that needed it, behind the same background worker so webhook handling does not block the request thread. The integration is generic enough that swapping providers later is a contained change.

Outcome

  • Customer self-service replaced the inbound email queue for the common cases. Staff opened the inbox for exceptions, not for status checks.
  • Spreadsheets and shared documents stopped being the source of truth. The database is.
  • Every state change has an audit row. Compliance review went from "we'll dig through email" to "here is the query".
  • Search across customer history works. Staff stopped asking other staff what happened on a request last quarter.
  • The team did not need the two extra hires they had planned. They added one, and that person had a system to work in.
  • We shipped on time, on the fixed scope, with end-of-sprint demos every two weeks against the real URL.

What we cut

We did not build a mobile app. The customer area is mobile-responsive web, which covered the actual use case. A native app would have doubled the engagement and earned nothing the responsive build did not.

We did not build a custom workflow engine for staff routing. Admin actions plus three background tasks did the job. A workflow engine was a feature we could add later if the routing logic outgrew code, and it did not.

We did not migrate the historical email archive into the new system. The cost of that migration was disproportionate to the value of having search over old threads, and the client agreed. Old email stays in old email.

We did not build analytics dashboards beyond the half-dozen reports staff actually used on day one. Dashboards are easy to add later from clean data. Dashboards on top of dirty data are wasted work.

Hand-over

The client owns the code, the infrastructure, the runbooks, and the deployment pipeline. The repo is theirs, the database cluster is in their cloud account, the domain is on their registrar. We left a written runbook for the on-call rotation, an architecture document covering every decision we made, and a backlog of the things we deliberately deferred. No lock-in. No retainer required to keep the lights on.

If they want us back for a follow-up scope, the door is open. If they want to take it from here on their own, the system is built to be readable by an engineer who did not write it. That was the brief.

Got an app that needs to last?

Take it from prototype to production.

Reply within one business day. Vibecoded MVP, AI-built draft, half-finished project, or a working product that's starting to crack — all welcome.

Start a project