Not sending email to customer

Infos:

  • Zammad version: 6.5.1
  • Installation type: Docker Compose
  • Operating system: Ubuntu
  • Browser: Latest version of Safari

Expected behavior:

  • After creating a ticket, the customer should receive a confirmation email.

Actual behavior:

  • I filled out the ticket form and successfully submitted it.
  • The system received the ticket and correctly forwards the ticket to the agent, but no notification email is sent to the customer.
  • I have double-checked the email configuration, customer settings, and triggers, but the system still does not send emails to the customer.
  • The form was customize by me and here is the function:

add_action(‘rest_api_init’, function () {
register_rest_route(‘support/v1’, ‘/ticket’, array(
‘methods’ => ‘POST’,
‘callback’ => ‘handle_support_ticket’,
‘permission_callback’ => ‘__return_true’,
));
});

function handle_support_ticket(WP_REST_Request $request) {

$p = $request->get_body_params();
$files = $request->get_file_params();
$firstname      = sanitize_text_field($p['firstname'] ?? '');
$lastname       = sanitize_text_field($p['lastname'] ?? '');
$email          = sanitize_email($p['email'] ?? '');
$phone          = sanitize_text_field($p['phone'] ?? '');
$department     = sanitize_text_field($p['department'] ?? '');
$tax_code       = sanitize_text_field($p['tax_code'] ?? '');
$body           = sanitize_textarea_field($p['body'] ?? '');
$group_id       = intval($p['group_id'] ?? 0);
$product_type   = sanitize_text_field($p['product_type'] ?? '');
$product_code   = sanitize_text_field($p['product_code'] ?? '');
$product_name   = sanitize_text_field($p['product_name'] ?? '');

if (empty($email) || !is_email($email)) {
    return new WP_Error('bad_request', 'Địa chỉ email không hợp lệ.', ['status' => 400]);
}
if (empty($firstname) || empty($lastname) || empty($body) || empty($group_id)) {
    return new WP_Error('bad_request', 'Vui lòng điền đầy đủ các trường bắt buộc.', ['status' => 400]);
}

if (($group_id === 7 || $group_id === 11) && (empty($product_code) || empty($product_name))) {
    return new WP_Error('bad_request', 'Vui lòng nhập Mã và Tên sản phẩm cho yêu cầu Hỗ trợ kỹ thuật/Bảo hành.', ['status' => 400]);
}
if ($group_id === 8 && empty($product_type)) {
    return new WP_Error('bad_request', 'Vui lòng chọn Loại sản phẩm cho yêu cầu Báo giá.', ['status' => 400]);
}

if ($group_id === 8 && !empty($product_type)) {
    if ($product_type === '9')  $group_id = 9;
    if ($product_type === '10') $group_id = 10;
}

$body_details = "";
if ($product_code || $product_name) {
    $body_details .= "\n\n--- Thông tin sản phẩm ---\n";
    if ($product_code) $body_details .= "Mã sản phẩm: {$product_code}\n";
    if ($product_name) $body_details .= "Tên sản phẩm: {$product_name}\n";
}
if ($tax_code)   $body_details .= "\nMã số thuế: {$tax_code}";
if ($department) $body_details .= "\nĐơn vị/Tổ chức: {$department}";
if ($phone)      $body_details .= "\nSĐT: {$phone}";

$final_body = $body . $body_details;

$api_url   = "https://{my web adress}/api/v1";
$api_token = "{My Token}";

$ch = curl_init("$api_url/users/search?query=" . urlencode($email));
curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ["Authorization: Token token=$api_token", "Content-Type: application/json"], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 20 ]);
$user_response = curl_exec($ch);
if (curl_errno($ch)) { error_log("Zammad user search curl error: " . curl_error($ch)); /* ... handle error ... */ }
curl_close($ch);
$user = json_decode($user_response, true);
$customer_id = (!empty($user) && isset($user[0]['id'])) ? $user[0]['id'] : null;

if (!$customer_id) {
    $new_user_data = [ "firstname" => $firstname, "lastname" => $lastname ?: "-", "email" => $email, "phone" => $phone, "department" => $department, "roles" => ["Customer"] ];
    $ch = curl_init("$api_url/users");
    curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ["Authorization: Token token=$api_token", "Content-Type: application/json"], CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($new_user_data), CURLOPT_TIMEOUT => 20 ]);
    $create_user_response = curl_exec($ch);
    if (curl_errno($ch)) { error_log("Zammad create user curl error: " . curl_error($ch)); /* ... handle error ... */ }
    curl_close($ch);
    $new_user = json_decode($create_user_response, true);
    if (!empty($new_user['id'])) {
        $customer_id = $new_user['id'];
    } else {
        error_log("Zammad create user FAILED: " . $create_user_response);
        return new WP_Error('user_creation_failed', 'Không thể tạo user trên hệ thống hỗ trợ.', ['status' => 500, 'details' => json_decode($create_user_response)]);
    }
}

$attachment_ids = [];
if (!empty($files['attachments'])) {
    $attachments = [];
    if (is_array($files['attachments']['name'])) {
        $attachments = $files['attachments'];
    } else {
        foreach ($files['attachments'] as $key => $value) { $attachments[$key] = [$value]; }
    }
    foreach ($attachments['tmp_name'] as $i => $tmp_name) {
        if ($attachments['error'][$i] === UPLOAD_ERR_OK) {
            $file_path = $tmp_name;
            $file_name = $attachments['name'][$i];
            $mime = mime_content_type($file_path) ?: 'application/octet-stream';
            $ch = curl_init("$api_url/upload");
            curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ["Authorization: Token token=$api_token"], CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => ['file' => new CURLFile($file_path, $mime, $file_name)], CURLOPT_TIMEOUT => 60 ]);
            $upload_response = curl_exec($ch);
            if (curl_errno($ch)) { error_log("Zammad upload curl error for {$file_name}: " . curl_error($ch)); }
            curl_close($ch);
            $upload_json = json_decode($upload_response, true);
            if (!empty($upload_json['id'])) {
                $attachment_ids[] = $upload_json['id'];
            } else {
                error_log("Zammad upload FAILED for file {$file_name}: " . $upload_response);
            }
        }
    }
}

$ticket_data = [
    "title"       => "Yêu cầu hỗ trợ từ $firstname $lastname",
    "group_id"    => $group_id,
    "customer_id" => $customer_id,
    "article"     => [ "subject" => "Hỗ trợ từ $firstname $lastname", "body" => $final_body, "type" => "note", "internal" => false, "attachments" => $attachment_ids ],
];

$ch = curl_init("$api_url/tickets");
curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ["Authorization: Token token=$api_token", "Content-Type: application/json"], CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($ticket_data), CURLOPT_TIMEOUT => 30 ]);
$response = curl_exec($ch);
if (curl_errno($ch)) { error_log("Zammad ticket curl error: " . curl_error($ch)); /* ... handle error ... */ }
curl_close($ch);

error_log("Zammad ticket request: " . json_encode($ticket_data));
error_log("Zammad ticket response: " . $response);

$result = json_decode($response, true);
if (!empty($result['id'])) {
    // Trả về cả success và ticket number để JS sử dụng
    return new WP_REST_Response(['success' => true, 'ticket' => $result['number']], 200);
}

$msg = $result['error'] ?? 'Không thể tạo ticket. Vui lòng thử lại.';
return new WP_Error('ticket_creation_failed', $msg, ['status' => 500, 'details' => $result]);

}

I used the default trigger: