Back to overview

Creating Emails with AI

Scripting & Buttons Published on 05.11.2025

Tutorial: Creating Emails with AI

This guide explains the functionality and code of the action_create_mail_ai.php script. This script allows users to generate a complete email draft using an AI model, simply by providing a topic or subject line.

How It Works: The User's Journey

The process is designed to be simple and intuitive for the end-user:

  1. Step 1: Provide a Topic
    The user clicks the custom button, and a dialog box appears asking for the email's topic (e.g., "Invitation to the team meeting on Friday").
  2. Step 2: AI Generation
    After confirming, a loading spinner is shown while the topic is sent to the backend. The PHP script then prompts the OpenAI API to write an email based on this topic.
  3. Step 3: Save to Database
    The AI's HTML-formatted response is received by the backend. The script then creates a new record in the current database table, populating fields like betreff (subject), datum (date), and text (the AI-generated HTML).
  4. Step 4: Edit and Finalize
    The user is automatically redirected to the edit screen for the newly created email record, where they can review, modify, and finalize the draft.

Code Deep Dive

The script is divided into two main parts: the PHP backend for processing and the JavaScript frontend for user interaction.

PHP Backend Logic

The PHP code handles security, communicates with the AI, and interacts with the database.

1. Initialization and Security

The script starts by including necessary files, checking for an active user session, and verifying that the user has permission to add records to the specified table.

require_once '../init.php';
require_once 'ai_helpers.php';

// --- Access Control & Initialization ---
if (!isset($_SESSION['user'])) {
    api_error_response(403, 'AUTH_REQUIRED', 'Not authenticated.');
}

$table_id = (int)($_GET['table_id'] ?? 0);
// ... permission checks ...
$table_definition = DBGet('custom_tables', $table_id);
if (!$table_definition || !check_user_permission($table_definition['table_name'], 'can_add')) {
    api_error_response(403, 'PERMISSION_DENIED', 'No permission to add records.');
}

2. The AI API Call: call_openai_api_for_text()

This is a specialized function to communicate with the OpenAI API. Unlike other helpers that might expect a JSON response, this one is designed to retrieve raw text or HTML content.

function call_openai_api_for_text(string $system_prompt, string $user_prompt, string $api_key): string {
    // ... cURL setup ...
    $data = [
        'model' => 'gpt-4o',
        'messages' => [
            ['role' => 'system', 'content' => $system_prompt],
            ['role' => 'user', 'content' => $user_prompt],
        ],
        'temperature' => 0.7,
    ];
    // ... cURL execution and error handling ...

    $result = json_decode($response, true);

    // ... Error checking for HTTP status and API errors ...

    return $result['choices'][0]['message']['content'] ?? '';
}

3. Handling the POST Request

This is the core of the backend logic, executed when the frontend sends the user's topic.

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    try {
        check_csrf_token();

        $mail_topic = trim($_POST['mail_topic'] ?? '');
        if (empty($mail_topic)) {
            throw new Exception('The email topic cannot be empty.');
        }

        // 1. Prepare the AI prompts
        $system_prompt = "You are a helpful assistant who writes professional emails. The user provides a topic. Write a suitable email in HTML format. Return only the HTML body of the email, without surrounding text, comments, or explanations.";
        $user_prompt = "Write a polite and professional email on the following topic: '{$mail_topic}'. Return the response as a complete HTML body.";

        // 2. Call the AI
        $ai_response_html = call_openai_api_for_text($system_prompt, $user_prompt, $apiKey);

        // 3. Clean the AI response
        if (preg_match('/```html\s*(.*?)\s*```/s', $ai_response_html, $matches)) {
            $ai_response_html = $matches[1];
        }

        // 4. Prepare and insert the new record
        $record_data = [
            'betreff' => $mail_topic,
            'datum' => date('Y-m-d H:i:s'),
            'team' => $_SESSION['user']['team'],
            'text' => $ai_response_html,
            'created_by_user_id' => $_SESSION['user']['id']
        ];
        $new_record_id = DBInsert($physical_table_name, array_keys($record_data), array_values($record_data));

        // ... error handling for DBInsert ...

        // 5. Send success response with redirect URL
        header('Content-Type: application/json');
        echo json_encode([
            'success' => true,
            'data' => [
                'redirect' => "custom_table_record_edit.php?table_id={$table_id}&id={$new_record_id}"
            ]
        ]);
        exit();

    } catch (Exception $e) {
        api_error_response(500, 'AI_PROCESSING_ERROR', 'Error during AI processing: ' . $e->getMessage());
    }
}

JavaScript Frontend Logic

The inline <script> tag at the end of the file handles all user interactions within the browser.

(async () => {
    // Step 1: Show a dialog to get the topic from the user.
    const confirmed = await showDialog({
        title: '<i class="fa-solid fa-robot me-2"></i> Create New Email via AI',
        text: `
            <p>Enter the topic or subject for the new email here.</p>
            <input type="text" id="mailTopicInput" class="form-control" placeholder="e.g., Invitation to team meeting on Friday">
        `,
        confirmButtonText: 'Create Email »',
        cancelButtonText: 'Cancel',
        width: '600px',
    });

    if (!confirmed) return; // User cancelled

    const mailTopic = document.getElementById('mailTopicInput').value.trim();
    if (!mailTopic) {
        // Show an error if the input is empty
        setTimeout(() => showDialog({ title: 'Error', text: 'Please provide a topic for the email!', okText: 'OK' }), 200);
        return;
    }

    // Step 2: Show a loading spinner.
    showDialog({ title: 'Generating Email...', text: '<div class="text-center p-4"><div class="spinner-border text-primary"></div></div>', okText: '' });

    try {
        // Step 3: Send the data to the PHP backend.
        const postUrl = '<?= $_SERVER['PHP_SELF'] ?>?table_id=<?= $table_id ?>';
        const formData = new FormData();
        formData.append('mail_topic', mailTopic);
        formData.append('csrf_token', '<?= generate_csrf_token() ?>');

        const response = await fetch(postUrl, {
            method: 'POST',
            body: formData
        });

        const result = await response.json();

        // Close the loading dialog
        const loadingModalElement = document.getElementById('dynamicModal');
        if (loadingModalElement) {
            bootstrap.Modal.getInstance(loadingModalElement)?.hide();
        }

        if (!response.ok || !result.success) {
            throw new Error(result.message || 'An unknown error occurred.');
        }

        // Step 4: Redirect on success.
        if (result.data && result.data.redirect) {
            window.location.href = result.data.redirect;
        }

    } catch (error) {
        // ... error handling to close spinner and show an error dialog ...
    }
})();

The frontend script uses the existing showDialog() helper function for all user interactions. It makes an asynchronous fetch call to its own PHP file, handles the JSON response, and performs the final redirection, providing a seamless experience.