Krishnan's Personal Website


Home | About | Blog | Skills | Innovations | Work with Me | Connect with me | Other Links


Building a Call Center Web Application: A Step-by-Step Technical Deep Dive



Published On: May 15 2025
Written By: Krishnan Sethuraman
Category: Software Engineering


Building a Call Center Web Application

Our sales team does a lot of outbound calls to prospects. In the earlier days of cold calling, spreadsheets were convenient. All the contact info of the prospects was maintained in a spreadsheet and we would add two columns call status and comments to update on what's happening with each of the prospects.

It's all fine in the hustle phase when you are trying to build a business. But once you have a meaningful revenue and customer base spreadsheets will do more harm than good.

Some of the problems my companies faced were:

  1. Too many spreadsheets to maintain.
  2. No way to track and analyse important metrics
  3. Losing important prospect contact information in spreadsheets.
  4. Losing track of spreadsheets in use. 

In these circumstances managing outbound calls using spreadsheets quickly becomes a nightmare. At Geedesk, we faced these exact problems. Our agents were manually maintaining Excel sheets, tracking call statuses, and leads in a chaotic manner. To fix this, I decided to build a lightweight, robust call center web application using Laravel, Livewire, and Vue.js.

This is the first project where I have used Livewire.

This post outlines the entire journey - from problem statement to technology decisions, architectural choices, and technical implementation. If you’re a developer or a technical founder looking to build a similar internal system, then follow along. I am sure you will love it. 

 

Problem Statement

As we scaled our sales team the overall prospect count also increased. Initially it all appeared to be working but eventually the number of deals closed started going down. The prospect count kept increasing but the deals closed was going down gradually.

At first glance this made no sense to me. That is when I decided to look closely at what was going on and to my astonishment, I figured out that every group or individual has their own spreadsheets to collect the prospect information.

These spreadsheets were then randomly shared to the agents who made the calls. Some agents had four spreadsheets and some agents had none. On top of all that the agents would call these prospects and in one or two attempts if they did not get any positive reply they would simply ignore it and once the list was exhausted the spreadsheet would be forgotten.

However this was not the biggest issue. The biggest issue was that the prospects who did not respond positively in the present were forgotten. No drip email campaigns or product updates were sent to them.

We not only lost the prospect information but also had no metrics and were simply shooting in the dark. 

 

The Solution

To solve this problem we decided to go shopping. We did not need a call center application. Our requirements were slightly different.

We wanted the calling application to pull the prospects from our CRM and then assign it to specific agents who would then start calling them.

This application should connect with a third party service like Twilio or Exotel or an EPBX server so that calls can be made without a physical phone.

A new challenge

To our surprise we could not find a single application that solved this specific problem. 
Maybe it was our small budget, we thought. But increasing the budget did not give us a proper fool proof solution.

I was surprised, with almost two decades of saas no one built something like this? Or the truth is maybe my our requirements are pretty strange. So the only option left with us was to build this on our own.

The desired features and our approach

We decided to just build the MVP. The MVP will have all the features we need and that's all. It will have a basic UI but nothing fancy.

The features that we want this application to have:

  1. This will be a standalone web application that is integrated with the CRM as a third party.
  2. This application should also be hosted on-premise so that we can integrate it with your local EPBX.
  3. It should have all features like managing and assigning calls to agents like calling from a web interface recording the calls etc.
  4. Sync new prospects from the CRM and assign it to the agents.
  5. Option to update prospects like adding notes and marking call back date time etc.
  6. Sync updates to the CRM so that CRM data remains the single source of truth across the organization.
  7. Monitoring work logs of the agents and determining the efforts and rewarding them appropriately.
  8. Getting important metrics on a dashboard to understand what's happening. 

Though the first step was to list down the features wish list the second step was to brutally look at the features and only implement the bare minimum in the MVP.

So the MVP features that we decided was:

  1. Sync new prospects from the CRM
  2. Assign prospects to agents for calling
  3. Record agent work logs.
  4. Call center module to display prospects and option to record call notes and other statuses like did not pick up or call back.
  5. Sync call notes and status updates back to the CRM at the end of work day. 

The MVP is the critical part here. It's more than enough to run the operation and to put a system in place.

 

Tech Stack Selection

For the tech state we went by the books and decided to go with Laravel. I have worked with teams that have used Livewire extensively to build modular web applications so I decided to use Livewire this time to avoid wasting time writing frontend code for an internal tool. 

I also did not waste time in UI/UX, but I made sure that the UI was functional and did not come in the way of the team from accomplishing the defined objective. 

As this web application will be hosted on premise I decided to have docker from day one so that setting it up locally becomes a breeze. 

To summarise this is the tech stack.

  • Laravel – Chosen for backend. Familiar, stable, and expressive.
  • Livewire – Used to avoid manually writing tons of JavaScript.
  • Vue.js – Used only where Livewire wasn’t performant (e.g., modals, complex UI interactions).
  • MySQL – Chosen for simplicity and fast relational queries.


During the course of the project I played with Alpine.js but decided not to use it as it had some learning curve. I decided not to waste any time at all and went with Vuejs as I knew it quite well. Also with Vuejs I will be able to copy and paste a lot of code from my other applications, thus speeding up development time.

 

Feature Breakdown and Architecture

 

1.Data Sync Architecture 

While thinking of a better way to sync the prospect data from one database to multiple databases (for now it's one database but in the future it can be multiple databases as we might have call centers in multiple locations) the best way forward would be to have a single central database. 

So at a very high level the central database which is called the back office database Syncs the prospect information and the agent information from the CRM. 

It then syncs the prospects to individual on premise database based on certain factors. These certain factors will be considered later, so for now it just syncs to one or more locations. 

The local databases will maintain the primary key from the backoffice database for each record. This will come in handy then the updates prospects information is synced back to the CRM database.

<?php
public function handle()
{
    $response = Http::get('https://crm.local/api/prospects');
    foreach ($response->json() as $prospect) {
        Prospect::updateOrCreate(['external_id' => $prospect['id']], [
            'name' => $prospect['name'],
            'phone' => $prospect['phone'],
            'email' => $prospect['email'],
        ]);
    }
}

 

2. Authentication

For authentication I used Auth, Laravel’s built-in authentication system. 

It helped me keep the authentication system simple and straight forward. All the sessions are managed by Auth and I do not have to explicitly declare them in my code. 

Until recently I have been writing my own login logic as I felt it gave me more control. But ever since I started using Auth I have become a fan of it. 

To ensure not all routes are accessible without login I added auth middleware to all the routes.

<?php
if (Auth::attempt([
     'email' => $this->email,
     'password' => $this->password,
     'status' => USERS::STATUS_ACTIVE
 ])) 
{
     return redirect('/home');
}
session()->flash('error', 'Invalid credentials.');

 

3. User Roles

For now, I have kept the user roles simple. There will be only two user roles. 1. Admin and 2. Callers.

The admins will have additional privileges like viewing the logs and changing the timezone.

The callers will be able to see the calls/prospects assigned to them and to change status of the calls and add call notes. Later, I will introduce a manager role and use Laravel’s Gates or Spatie’s Roles/Permissions for more granularity.

 

4. Calling Workflow

For our international efforts I will use a third-party service like Twilio or Messagebird. So Twilio calling API will be integrated with the application and all calls will be made from the web interface.

For India, I am not happy with the existing service providers and the quality of the work. So we will be using our own EPBX server for making calls. The EPBX will be integrated with the application via custom built APIs to make the calling happen via the web interface.

For MVP we have decided not to have the EPBX in place. The whole intention of the MVP is to get the desired metrics and put a process in place. For calling we have given our agents smartphones with sim cards to make the calls.

calling application workflow

 

4. Cron Jobs

I have created three cron jobs that run in regular intervals.

The first cron job is called (add name here) and is scheduled to run every 10 minutes. The sole purpose of this cron job is to connect with the back office API to get any new prospects if available. If any new prospects are found they are created in the call center application database. I am also maintaining a status of prospects that are successfully synced on the back office database so that synced prospects do not get fetched again.

The second cron job runs every 60 minutes on the backoffice office side which regularly fetches new prospects from the CRM and stores it in its database for the on premise call center application to fetch.

The third cron job runs on the call center application side and runs everyday after the end of the business day. It fetches all the updates like prospect notes, prospect status changes and other updated information to the CRM database via an endpoint on the CRM side. This ensures that the CRM is always the single source of truth.

Two important scheduled jobs:

  1. Two P2 Prospect sync job a(runs every hour)
  2. Reminder for callbacks (sends email reminders to callers for scheduled callbacks)
<?php
In App\Console\Kernel.php:
$schedule->command('prospect:sync')->hourly();

 

Design Philosophy

  1. Livewire-first approach to keep things reactive without JavaScript
  2. Fallback to Vue.js only when Livewire performance lagged
  3. Minimal styling – used TailwindCSS for utility classes
  4. Internal-first – No need for public access or user registration flows
  5. No DB joins from CRM – Always sync via API to decouple the systems

 

What’s Next?

  1. EPBX Integration: SIP/WebRTC via Asterisk or Twilio
  2. Role-based dashboards for Team Leads
  3. AI-based call scoring
  4. Real-time call tracking

 

Final Thoughts

Building this app was less about flashy UI and more about solving a business problem effectively. Laravel and Livewire allowed rapid prototyping. Vue.js gave us performance where we needed it. And by syncing CRM data via API and building independent logic for calling, we created a scalable foundation for future EPBX integration or third party integration.

If you're running a growing sales team and struggling with spreadsheets, this approach is worth replicating. I have added the source code to a public repository. 

 

Related Articles

  1. Building a New Blogging Platform with Laravel
  2. How to Boost Your Laravel Application’s Performance