SalesCalls CRM is a self-hostable cold calling and lead management system built for sales teams that want full control over their data. It covers the full cold calling workflow: managing lead lists, logging calls with outcomes, scheduling follow-ups, emailing leads directly from the platform, and tracking team performance across projects.
The app is structured around projects, with each project having its own pipeline columns and lead pool. Admins can create projects, configure custom pipeline stages, manage team members, and assign leads across users. Regular users see only the leads assigned to or shared with them. The call log is the core of the product: every call records the outcome, duration, notes, and a follow-up date, and the dashboard surfaces what is due today.
Custom fields let admins extend the lead record with any fields their sales process needs, without touching the code. Email is sent directly from the app using per-user SMTP configuration, keeping deliverability tied to the user's own sending domain rather than a shared platform address.
The project is open source under MIT and available to download and self-host on any PHP/MySQL server.
My Role
Designed and built the full application as sole developer: data model, backend logic, UI, and admin panel
Designed the lead permission model: owners have full control, shared users can view or edit depending on the permission level granted, admins see everything
Built the project and pipeline column system: each project has configurable stages, leads move through them, and the view can be filtered by column
Built the call log system with outcome tracking (no answer, left message, interested, not interested, callback, converted), duration, notes, and follow-up date
Built per-user SMTP email configuration so each user sends from their own address rather than a shared platform address
Built the admin panel: user management, project management, column configuration, custom field builder, and team overview
Shipped CSV import and export for lead lists
Architecture
Single PHP application running on MySQL. No framework: plain PHP with PDO for database access, session-based auth with role checks on every page. The data model is built around projects, leads, and calls. A leads table holds all lead records scoped to a project_id. A lead_shares table handles per-lead sharing with explicit permission levels (view or edit). Calls are logged per lead_id and user_id with outcome and follow-up data.
Custom fields are stored in a custom_fields table and their values in lead_custom_values. This lets admins add new data fields to leads without schema changes: the custom_fields table defines the field type and label, and the values table stores the input per lead.
Email is sent via PHPMailer using credentials stored in an email_settings table per user. The admin panel includes an SMTP configuration screen where each user sets their own sending details. Notifications are stored in a notifications table and surfaced in the app header.
Key Decisions
Lead permission model with owner, shared-view, and shared-edit tiers
Sales leads often need to be worked collaboratively but not symmetrically. An account owner may want to share a lead with a colleague who can add call notes but not delete the lead or change the ownership. The three-level permission model (owner full control, shared-edit, shared-view) was designed around that real workflow rather than a simpler all-or-nothing sharing approach.
Per-user SMTP instead of a shared sending address
Cold outreach sent from a shared platform address (like noreply@salescalls.example.com) looks like automated bulk mail and lands in spam more often. Using the user's own SMTP credentials means the email comes from their actual sending domain, which is better for deliverability and means replies go directly to the right person.
Custom fields without schema changes
Different sales teams need different data on a lead record. Some need an industry field, some need a company size, some need a specific qualification question. Rather than trying to anticipate every field upfront, the custom fields system lets admins define new fields via the UI without touching the database schema. The trade-off is that these fields are not queryable the same way native columns are, but for CRM data that is an acceptable constraint.
Self-hosted MIT open source
Sales teams often have compliance requirements around where lead data is stored, or simply a preference for not putting contact lists in a third-party SaaS. Releasing the app as self-hostable MIT open source means any team can run it on their own server with full data control. The deployment requirement is a standard PHP/MySQL stack, which is available on any shared hosting plan.
Challenges
Building a permission system that is flexible enough without becoming complex to manage
The first version of lead sharing had only two states: owned or shared with no distinction. That turned out to be too coarse: teams wanted a way to give a colleague visibility into a lead without giving them edit access. Adding the view/edit distinction solved the immediate problem but required revisiting the permission checks on every data-modifying action in the app to make sure they enforced the right level.
Custom fields that work for filtering without becoming a performance problem
Storing custom field values in a separate table with a lead_id foreign key is flexible, but querying leads filtered by a custom field value requires a join. For small teams with a few hundred leads this is fine. For larger datasets it could become slow. The current approach indexes on lead_id in the values table and accepts the join cost as reasonable for the expected scale of a self-hosted CRM.