AI integration is a major trend in customer relationship management. Recent surveys indicate that around 61% of companies plan to add AI to their CRM systems within the next three years. AI-driven assistants can automate repetitive tasks, analyze customer data, interpret intent, and even generate content. For example, leading CRM platforms already use generative AI to draft emails, create call summaries, and suggest follow-up actions for sales reps. These capabilities dramatically boost productivity – companies report 30–50% faster response times and improved customer engagement by leveraging AI assistants.
In this tutorial, we’ll build a custom AI-enhanced CRM from scratch using: PHP 8 for core logic, MySQL for data storage, Bootstrap 5 for a responsive front-end, and the OpenAI API for intelligent features (like auto-generated emails and summaries). We’ll develop locally using XAMPP (an Apache–PHP–MySQL bundle) for convenience. By the end, you will have a working CRM that manages contacts and activities, supports user logins, and uses AI to assist with content generation.
To get started, set up your development environment and project structure:
Add Bootstrap 5: We will use Bootstrap 5 for a responsive UI. Include the Bootstrap 5 CSS via CDN in the <head> of your HTML files. For instance, add:
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css" rel="stylesheet">
With the environment set up, we can proceed to design the database schema and build the application step by step.
Our CRM will have three core tables: contacts, users, and notes. This simple schema is common in many CRM systems:
In practice, the users table often includes a role or type (e.g., sales rep vs manager) to control access to certain features. Our design will use a role field to distinguish regular sales users from managers.
Below is the SQL schema for the database. You can execute this SQL in phpMyAdmin (SQL tab) or via a MySQL client to create the crm_ai database and tables:
CREATE DATABASE IF NOT EXISTS crm_ai;
USE crm_ai;
CREATE TABLE contacts (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL,
company VARCHAR(100),
status VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
role ENUM('sales','manager') DEFAULT 'sales'
);
CREATE TABLE notes (
id INT AUTO_INCREMENT PRIMARY KEY,
contact_id INT NOT NULL,
user_id INT NOT NULL,
note TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (contact_id) REFERENCES contacts(id),
FOREIGN KEY (user_id) REFERENCES users(id)
);
A few points about this schema:
Secure Password Storage: Notice that the users.password column is 255 characters – this is to accommodate hashed passwords. Never store plaintext passwords. We will use PHP password_hash() to store a one-way hash of the password for each user account, which is a security best practice.
With the database ready, we can connect our PHP application to MySQL. In the project directory (ai_crm), create a file called db.php with the following code:
<?php
// db.php: Database connection
$servername = "localhost";
$username = "root";
$password = ""; // MySQL root password (empty string by default in XAMPP)
$dbname = "crm_ai";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
?>
This script uses the PHP mysqli extension to connect to MySQL. We assume default XAMPP credentials (username “root” with an empty password). If your MySQL setup has a different user or password, update the $username and $password accordingly.
Include (require) this db.php file in any PHP script that needs database access. For example, at the top of your other PHP files (after starting a session, if applicable), do:
require ‘db.php’;
This way, you can run SQL queries using the $conn connection object. For instance, mysqli_query($conn, $sql) or using prepared statements, as we’ll see in the login code.
Let’s create the user interface for our CRM. We will use Bootstrap 5 components to quickly build a clean, responsive UI.
Navigation Bar: First, add a navigation bar on all pages when a user is logged in. Create a file header.php (or you can directly include nav HTML in each page) with the following Bootstrap navbar code:
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<a class="navbar-brand" href="#">My CRM</a>
<button class="navbar-toggler"
type="button" data-bs-toggle="collapse"
data-bs-target="#navbarNav" aria-controls="navbarNav"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="contacts.php">Contacts</a>
</li>
<li class="nav-item">
<a class="nav-link" href="notes.php">Activities</a>
</li>
<!-- Add more nav items as needed -->
</ul>
</div>
</div>
</nav>
This creates a top navbar with a brand title “My CRM” and two links: Contacts and Activities. The navbar-expand-lg class makes it responsive (collapsible on smaller screens), and the bg-primary navbar-dark gives it a dark background. You can include this navbar on pages like contacts.php and notes.php via PHP include (e.g., <?php include ‘header.php’; ?>) to keep a consistent layout.
Contacts List Page: Create a page contacts.php to display the list of contacts from the database. Use a Bootstrap table for a clean look:
<?php
// contacts.php
session_start();
require 'db.php';
// (Make sure user is logged in; we'll add session check later)
?>
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Bootstrap CSS link as added earlier -->
<title>Contacts</title>
</head>
<body>
<?php include 'header.php'; ?>
<div class="container mt-4">
<h2>Contacts</h2>
<table class="table table-striped">
<thead>
<tr>
<th>Name</th><th>Email</th><th>Company</th><th>Status</th>
</tr>
</thead>
<tbody>
<?php
$result = $conn->query("SELECT * FROM contacts");
while($row = $result->fetch_assoc()):
?>
<tr>
<td><?= htmlspecialchars($row['name']) ?></td>
<td><?= htmlspecialchars($row['email']) ?></td>
<td><?= htmlspecialchars($row['company']) ?></td>
<td><?= htmlspecialchars($row['status']) ?></td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
</div>
</body>
</html>
In the above snippet, we fetch all contacts from the database and loop through them to output each contact Name, Email, Company, and Status in a table row. We use htmlspecialchars() to safely output data. The table has the class table table-striped, which gives it a nice striped row style via Bootstrap.
You can similarly create a notes.php page to list activities (notes) recorded, or perhaps to show a timeline of recent interactions. For brevity, we won’t detail that here, but it would be a similar approach: query the notes table (likely joined with contact and user to display names), and output in a table or list format.
At this stage, your application can display contact data. Next, let’s implement user accounts so that we can restrict access to these pages.
For our CRM, we’ll allow users to register and log in. User authentication will ensure that only authorized users can access the contacts and notes.
Registration (register.php): Create a simple registration form (register.php) that allows a new user to sign up with a username and password. In the form handler, we will insert a new user into the users table. Use PHP password_hash() to securely hash the password before storing it.
<?php
// register.php (registration logic)
require 'db.php';
if (isset($_POST['register'])) {
$username = trim($_POST['username']);
$password = trim($_POST['password']);
// Hash the password for secure storage
$hash = password_hash($password, PASSWORD_DEFAULT);
// Prepare and execute the insert query
$stmt = $conn->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, 'sales')");
$stmt->bind_param("ss", $username, $hash);
if ($stmt->execute()) {
header("Location: login.php?registered=1");
exit;
} else {
$error = "Registration failed: " . $stmt->error;
}
}
?>
<!-- HTML form for registration -->
<form method="post" action="">
<h2>Register</h2>
<?php if(isset($error)) echo "<p class='text-danger'>$error</p>"; ?>
<div class="mb-3">
<label>Username:</label>
<input type="text" name="username" class="form-control" required>
</div>
<div class="mb-3">
<label>Password:</label>
<input type="password" name="password" class="form-control" required>
</div>
<button type="submit" name="register" class="btn btn-primary">Sign Up</button>
</form>
In this code, when the form is submitted, we grab the username and password from $_POST. We immediately hash the password with PASSWORD_DEFAULT (which currently uses bcrypt). Then we use a prepared statement to insert the new user into the users table with a default role of ‘sales’. After successful registration, we redirect to the login page with a flag registered=1 (so we could show a “Registration successful, please log in” message on the login page if desired).
Login (login.php): The login script will accept a username and password, check the credentials, and start a session for the user. Create login.php with a form and this PHP logic:
<?php
// login.php (login logic)
require 'db.php';
session_start();
if (isset($_POST['login'])) {
$username = trim($_POST['username']);
$password = trim($_POST['password']);
// Fetch the user by username
$stmt = $conn->prepare("SELECT id, password, role FROM users WHERE username=?");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($uid, $hash, $role);
if ($stmt->fetch() && password_verify($password, $hash)) {
// Credentials are correct – set session variables
$_SESSION['uid'] = $uid;
$_SESSION['role'] = $role;
header("Location: contacts.php");
exit;
} else {
$error = "Invalid username or password";
}
}
?>
<!-- HTML form for login -->
<form method="post" action="">
<h2>Login</h2>
<?php if(isset($error)) echo "<p class='text-danger'>$error</p>"; ?>
<div class="mb-3">
<label>Username:</label>
<input type="text" name="username" class="form-control" required>
</div>
<div class="mb-3">
<label>Password:</label>
<input type="password" name="password" class="form-control" required>
</div>
<button type="submit" name="login" class="btn btn-primary">Login</button>
</form>
This script checks the provided credentials against the database. We select the stored hash for the username, then use password_verify() to compare the entered password with the stored hash. If they match, we initialize the session and store the user’s ID and role in $_SESSION. We then redirect to contacts.php (the main app page). If the credentials are wrong, we set an error message.
Session Protection: To protect the internal pages (like contacts and notes), add a session check at the top of those PHP files. For example, at the very top of contacts.php (before any HTML output):
session_start();
if (!isset($_SESSION['uid'])) {
header("Location: login.php");
exit;
}
This ensures that if a user is not logged in (no session), they cannot directly access the page and will be redirected to the login form. You might include this snippet via a common file (e.g., auth.php) to avoid repetition.
Using sessions, we can also enforce role-based restrictions. For instance, if $_SESSION[‘role’] is ‘sales’, you might hide or disable admin-only features. In our simple app, we won’t implement extensive role differences, but you could easily extend it (e.g., only managers can see an “Analytics” page or delete contacts).
At this point, we have a functional CRM where users can sign up, log in, and view a list of contacts. Now, let’s add the exciting part – integrating AI features via OpenAI.
One of the main value-adds of this CRM is the integration of AI to assist users. We’ll use the OpenAI API to generate content, such as drafting follow-up emails or summarizing notes. Specifically, we’ll integrate a GPT-4.1 model (OpenAI GPT-4.1-mini, which offers fast, cost-effective performance for generation tasks).
Set up OpenAI API access: If you don’t already have one, create an account on OpenAI and obtain an API key. Then, in your project, install the OpenAI PHP client library via Composer:
composer require openai-php/client
This will add the OpenAI API client to your project (make sure you have Composer installed on your system and run this in the ai_crm directory). The OpenAI PHP client requires PHP 8.1+ and uses Guzzle (an HTTP client) under the hood. If prompted, allow it to install php-http/discovery or run composer require guzzlehttp/guzzle to ensure HTTP requests can be made.
Next, create a file to initialize the OpenAI client. We can call it bootstrap.php (or include it in an existing config file). In bootstrap.php:
<?php
require __DIR__ . '/vendor/autoload.php'; // Composer autoload
// Load API key from environment or config
$openaiApiKey = 'YOUR_OPENAI_API_KEY';
// If using a .env file, you can load it via vlucas/phpdotenv here (not shown for brevity)
// Initialize OpenAI client
use OpenAI\OpenAI;
$openai = OpenAI::client($openaiApiKey);
$model = 'gpt-4.1-mini'; // specify the OpenAI model to use
?>
In a real setup, you should store the API key securely, for example, in a .env file or a config not exposed via the web. (If you create an .env file with OPENAI_API_KEY=…, you can use vlucas/phpdotenv to load it, as hinted in the prompt. The key point is not to hard-code sensitive keys in your public code.)
Now, to use the OpenAI client, require bootstrap.php wherever you need AI features (after including vendor/autoload.php). For example, in a script that handles AI drafting, you would do require ‘bootstrap.php’; to get the $openai client object ready.
Generate Email Draft Feature: Let’s add a feature on the contact detail page where a user can click a button to have an AI draft an email to that contact. Assume you have a page contact_view.php that shows details for a single contact (perhaps reached by clicking a contact name on the contacts list). On that page, include a button like:
<button class="btn btn-success" id="btnGenerateEmail">
💡 Generate Follow-Up Email
</button>
<div id="draftArea" class="mt-3"></div>
This button (with an idea lightbulb icon) will trigger the AI email generation. We’ll use JavaScript to handle the click and call a server-side script to get the AI-generated text. For example, add a script on the page:
<script>
document.getElementById('btnGenerateEmail').onclick = function() {
fetch('ai_draft.php?contact_id=<?= $contactId ?>')
.then(response => response.text())
.then(data => {
document.getElementById('draftArea').innerText = data;
});
};
</script>
This uses the Fetch API to request ai_draft.php with the current contact ID. Now let’s create ai_draft.php, which will: 1) read the contact ID from the request, 2) fetch that contact info from the database, 3) call the OpenAI API to generate an email draft, and 4) output the draft text.
<?php
// ai_draft.php
require 'db.php';
require 'bootstrap.php'; // ensures $openai client is available
// Get contact info
$contactId = $_GET['contact_id'] ?? 0;
$contactRes = $conn->query("SELECT name, company FROM contacts WHERE id=" . intval($contactId));
$contact = $contactRes->fetch_assoc();
if (!$contact) {
http_response_code(404);
echo "Contact not found";
exit;
}
// Prepare the prompt for the AI model
$contactName = $contact['name'];
$company = $contact['company'] ?: 'their company';
$prompt = "You are a helpful sales assistant. Write a friendly follow-up email to {$contactName} at {$company}, thanking them for the last meeting and suggesting next steps.";
// Call OpenAI API to get a completion (draft email)
$response = $openai->chat()->create([
'model' => $model,
'messages' => [
['role' => 'user', 'content' => $prompt]
]
]);
$emailDraft = $response->choices[0]->message->content;
// Output the generated email draft
echo $emailDraft;
This script uses the OpenAI client’s chat completion endpoint. We construct a prompt instructing the AI to write a follow-up email to the contact, and we include the contact name and company for personalization. We then call $openai->chat()->create() with the chosen model (e.g., GPT-4.1-mini) and a single user message containing our prompt. The API returns a response object from which we extract the generated content (choices[0]->message->content). We echo that content, which the front-end JS will insert into the page.
With this in place, when the user clicks Generate Follow-Up Email, the AI will produce a draft email tailored to that contact. The draft text appears in the #draftArea div, and the sales rep can copy/paste or edit it as needed before sending.
Note: GPT-4.1-mini is a hypothetical model name used for illustration (OpenAI model names evolve; in practice, you might use gpt-3.5-turbo for cost efficiency or gpt-4 for higher quality, depending on your API access). Adjust the model name based on what’s available and suitable in the OpenAI API.
Generating an email draft is useful, but we can go a step further and send the email directly from the CRM. For this, we’ll integrate an email-sending library. PHPMailer is a classic choice which is used in projects like WordPress and Drupal.
Install PHPMailer via Composer:
composer require phpmailer/phpmailer
This will make PHPMailer available in our project (autoloaded by Composer). We can then create a script (say send_email.php) to actually send an email using SMTP. For example, using Gmail SMTP (just as a common scenario):
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php'; // ensure PHPMailer is loaded
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$toEmail = $_POST['to'];
$subject = $_POST['subject'];
$body = $_POST['body'];
$mail = new PHPMailer(true);
try {
//Server settings
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com'; // Set SMTP server (example: Gmail)
$mail->SMTPAuth = true;
$mail->Username = '[email protected]'; // SMTP username
$mail->Password = 'your_gmail_app_password'; // SMTP password (use an app password if 2FA enabled)
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mail->Port = 465;
//Recipients
$mail->setFrom('[email protected]', 'Your Name');
$mail->addAddress($toEmail);
//Content
$mail->Subject = $subject;
$mail->Body = $body;
$mail->send();
echo "Email sent successfully";
} catch (Exception $e) {
echo "Email could not be sent. Error: {$mail->ErrorInfo}";
}
}
In a real application, you would gather the $toEmail, $subject, and $body from a form (perhaps pre-filled with the AI draft so the user can review/edit it, then send). The above is a basic example: it configures PHPMailer to use Gmail SMTP, sets the email headers and body, and sends the message.
PHPMailer handles the complexities of SMTP and is more reliable than the PHP built-in mail() function. You would adjust the SMTP settings for your actual email server (e.g., use your company SMTP server or a service like SendGrid).
Before deploying this CRM or adding more features, consider these best practices and tips:
Congratulations! You now have a working AI-enhanced CRM that combines traditional web development with cutting-edge AI. Here’s a recap of what we built:
Feel free to enhance this CRM further. For instance, implement editing and deleting of contacts and notes, add filtering options, or include a dashboard page that summarizes key stats (using Chart.js for graphs of activities or leads). The integration of AI opens up many possibilities – you could use it to analyze the sentiment of notes, prioritize leads, or generate meeting summaries from call transcripts.
Bonus: As a next step, you might create a condensed quick-start guide or a README version of this project. It would summarize the setup and key code in a single page, which is helpful for documentation or if you’re sharing the project on GitHub. This comprehensive tutorial can serve as the in-depth explanation, while a one-page guide could provide a high-level overview for quick reference.
By combining PHP, MySQL, and Bootstrap with OpenAI API, we’ve shown how even a small custom CRM can gain powerful features. This fusion of classic web tech with AI services is a potent example of why AI integration is high on the agenda for many companies. With your new AI-enhanced CRM, routine tasks become easier and your team can focus more on building customer relationships, while the AI takes care of the grunt work. Happy coding, and enjoy your smarter CRM!

Hassan Tahir wrote this article, drawing on his experience to clarify WordPress concepts and enhance developer understanding. Through his work, he aims to help both beginners and professionals refine their skills and tackle WordPress projects with greater confidence.
Bret Lee
Great Tutorial! Integrating AI into CRM systems is a game-changer for productivity.