Esta guía te enseñará a crear un plugin de WordPress personalizado para enviar los datos de tus formularios de Elementor Pro directamente a tu cuenta de GoHighLevel (GHL). Esta solución es robusta, segura y te da control total sobre la información enviada.
Beneficios de este método:
- Automatización: Envía leads y contactos a GHL sin intervención manual.
- Seguridad: Tus claves API se almacenan de forma segura, y la integración opera dentro del entorno de WordPress.
- Organización: El código está contenido en un plugin dedicado, separado de tu tema.
- Flexibilidad: Fácil de adaptar para múltiples formularios y diferentes acciones en GHL (como aplicar etiquetas específicas).
Requisitos Previos:
- WordPress con Elementor Pro instalado y activo.
- Una cuenta de GoHighLevel con acceso a tu API Key y el Location ID donde quieres enviar los contactos.
- Acceso a los archivos de tu servidor (vía FTP, cPanel File Manager, etc.) para crear el plugin y editar el archivo
.htaccess. - Un editor de texto para copiar y pegar el código.
Paso 1: Configurar Variables de Entorno en .htaccess
Para mantener tus credenciales seguras, las almacenaremos fuera del código del plugin.
- Accede al archivo
.htaccessen el directorio raíz de tu instalación de WordPress (generalmentepublic_html/). - Añade las siguientes líneas al final del archivo:
SetEnv GHL_API_KEY "API_KEY_DE_GOHIGHLEVEL"SetEnv GHL_LOCATION_ID "LOCATION_ID_DE_GOHIGHLEVEL" - Reemplaza los textos en mayúsculas con tu API Key real de GoHighLevel y tu Location ID.
- Guarda el archivo
.htaccess.
Paso 2: Crear la Estructura de Carpetas del Plugin
- En tu computadora, crea una carpeta principal para tu plugin. Un buen nombre podría ser
mi-elementor-ghl-integracion. - Dentro de la carpeta
mi-elementor-ghl-integracion/, crea la siguiente estructura:mi-elementor-ghl-integracion.php(Este será el archivo principal del plugin)includes/(Una subcarpeta llamadaincludes)common-ghl-functions.php(Dentro deincludes/)
form-configs/(Una subcarpeta llamadaform-configs)- (Aquí irán los archivos de configuración para cada formulario, ej.
newsletter-form-handler.php)

- También necesitaremos un directorio para los logs dentro de
wp-content/uploads/. El plugin intentará crearlo, pero es bueno saberlo:wp-content/uploads/me-ghl-logs/(usaremosme-ghl-como prefijo para “Mi Elementor GHL”).
Paso 3: Código del Archivo de Funciones Comunes
Este archivo contendrá funciones reutilizables para el logging y para enviar datos a GHL.
Crea el archivo includes/common-ghl-functions.php y pega el siguiente código:
<?php
// /wp-content/plugins/mi-elementor-ghl-integracion/includes/common-ghl-functions.php
if ( ! defined( 'ABSPATH' ) ) {
exit; // No acceso directo.
}
/**
* Escribe un mensaje en el archivo de log del plugin.
*/
if (!function_exists('me_ghl_write_log')) {
function me_ghl_write_log(string $message): void {
if (!defined('ME_GHL_LOG_DIR') || !defined('ME_GHL_LOG_FILE')) {
error_log("ME-GHL Log Error: Constantes de log no definidas. Mensaje: " . $message);
return;
}
if (!is_dir(ME_GHL_LOG_DIR)) {
if (!@wp_mkdir_p(ME_GHL_LOG_DIR)) { // Intenta crear recursivamente
error_log("ME-GHL Log Error: No se pudo crear dir de logs: " . ME_GHL_LOG_DIR . ". Mensaje: " . $message);
return;
}
}
@file_put_contents(ME_GHL_LOG_FILE, "[" . date("Y-m-d H:i:s") . "] " . $message . "\n", FILE_APPEND);
}
}
/**
* Envía datos de contacto a la API de GoHighLevel.
*/
if (!function_exists('me_ghl_send_contact_to_ghl')) {
function me_ghl_send_contact_to_ghl(array $contactPayloadArray, string $ghlApiKey): array {
$result = [
'success' => false, 'http_code' => 0, 'response_body' => '', 'error_message' => ''
];
if (empty($ghlApiKey)) {
me_ghl_write_log("Error en me_ghl_send_contact_to_ghl: GHL API Key está vacía.");
$result['error_message'] = "GHL API Key no proporcionada.";
return $result;
}
$ghl_api_url = 'https://rest.gohighlevel.com/v1/contacts/';
$ghl_payload_json = json_encode($contactPayloadArray);
if (json_last_error() !== JSON_ERROR_NONE) {
$jsonErrorMsg = json_last_error_msg();
me_ghl_write_log('Error: No se pudo codificar el payload para GHL. Error JSON: ' . $jsonErrorMsg . ' | Payload Array: ' . print_r($contactPayloadArray, true));
$result['error_message'] = 'Error de codificación JSON para GHL: ' . $jsonErrorMsg;
return $result;
}
$args = [
'method' => 'POST',
'timeout' => 30,
'headers' => [
'Content-Type' => 'application/json; charset=utf-8',
'Authorization' => 'Bearer ' . $ghlApiKey,
],
'body' => $ghl_payload_json,
];
me_ghl_write_log("Enviando a GHL (Common Func): " . $ghl_payload_json);
$response = wp_remote_post( $ghl_api_url, $args );
if ( is_wp_error( $response ) ) {
$result['error_message'] = $response->get_error_message();
me_ghl_write_log("Error WP_Error GHL (Common Func): " . $result['error_message']);
} else {
$result['http_code'] = wp_remote_retrieve_response_code( $response );
$result['response_body'] = wp_remote_retrieve_body( $response );
if ( $result['http_code'] >= 200 && $result['http_code'] < 300 ) {
$result['success'] = true;
me_ghl_write_log("Éxito GHL (Common Func)! HTTP: " . $result['http_code'] . " | Respuesta (primeros 200c): " . substr($result['response_body'], 0, 200));
} else {
$result['error_message'] = "Error API GHL (" . $result['http_code'] . ")";
me_ghl_write_log($result['error_message'] . " | Respuesta: " . $result['response_body']);
}
}
return $result;
}
}
?>
Paso 4: Código del Archivo Principal del Plugin
Este archivo inicializa el plugin, define constantes de log, incluye las funciones comunes y establece el “router” para los formularios.
Crea el archivo mi-elementor-ghl-integracion.php y pega el siguiente código:
PHP
<?php
/**
* Plugin Name: Mi Integración Elementor a GHL
* Plugin URI: https://tudominio.com/
* Description: Maneja los envíos de formularios de Elementor Pro para enviar datos a GoHighLevel.
* Version: 1.0.0
* Author: Tu Nombre/Empresa
* Author URI: https://tudominio.com/
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: me-ghl-integration
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // No acceso directo.
}
// --- Constantes para el Log del Plugin ---
if (!defined('ME_GHL_LOG_DIR')) {
define('ME_GHL_LOG_DIR', WP_CONTENT_DIR . '/uploads/me-ghl-logs/'); // Directorio para logs
}
if (!defined('ME_GHL_LOG_FILE')) {
define('ME_GHL_LOG_FILE', ME_GHL_LOG_DIR . 'elementor_ghl_router_log.txt'); // Log principal del router
}
// --- Incluir funciones comunes PRIMERO ---
$path_to_common_functions = plugin_dir_path( __FILE__ ) . 'includes/common-ghl-functions.php';
// Diagnóstico de ruta (escribe en el error_log de PHP del servidor)
// Puedes comentar/eliminar este bloque de error_log una vez que confirmes que funciona.
error_log("---------------------------------------------------------------------");
error_log("[ME-GHL Plugin Init] Verificando common functions: " . $path_to_common_functions);
if (file_exists($path_to_common_functions)) {
if (is_readable($path_to_common_functions)) {
require_once $path_to_common_functions;
error_log("[ME-GHL Plugin Init] Archivo common functions INCLUIDO.");
} else {
error_log("[ME-GHL Plugin Init] ERROR: common functions existe PERO NO ES LEGIBLE. Revisa permisos.");
return;
}
} else {
error_log("[ME-GHL Plugin Init] ERROR CRÍTICO: common functions NO SE ENCONTRÓ. El plugin no funcionará. Ruta: " . $path_to_common_functions);
return;
}
error_log("---------------------------------------------------------------------");
/**
* Función principal que se engancha a TODOS los envíos de formularios de Elementor Pro.
*/
function me_ghl_elementor_form_submission_router( $record, $handler ) {
$form_name = $record->get_form_settings( 'form_name' );
if (function_exists('me_ghl_write_log')) {
me_ghl_write_log("Router: Nuevo envío. Formulario: " . sanitize_text_field($form_name));
} else {
error_log("Router ERROR CRÍTICO: me_ghl_write_log no definida. Funciones comunes no cargadas. Form: " . sanitize_text_field($form_name));
return;
}
$form_configs_path = plugin_dir_path( __FILE__ ) . 'form-configs/';
switch ($form_name) {
// EJEMPLO PARA UN FORMULARIO DE NEWSLETTER
case 'mi_newsletter_form': // **REEMPLAZA** con el "Form Name" de tu formulario de newsletter en Elementor
$handler_file = $form_configs_path . 'mi-formulario-newsletter-handler.php'; // Nombre del archivo handler
me_ghl_write_log("Router: Cargando handler para 'mi_newsletter_form' desde: " . $handler_file);
if (file_exists($handler_file)) {
if (is_readable($handler_file)) {
require_once $handler_file;
// **REEMPLAZA** 'me_ghl_process_mi_newsletter_form' con el nombre de la función en tu handler
if (function_exists('me_ghl_process_mi_newsletter_form')) {
me_ghl_process_mi_newsletter_form( $record, $handler );
} else {
me_ghl_write_log("Router ERROR: Función para 'mi_newsletter_form' no definida en " . $handler_file);
}
} else {
me_ghl_write_log("Router ERROR: Archivo handler '" . $handler_file . "' no es legible.");
}
} else {
me_ghl_write_log("Router AVISO: Archivo handler para '" . sanitize_text_field($form_name) . "' no encontrado en: " . $handler_file);
}
break;
// EJEMPLO PARA UN FORMULARIO DE CONTACTO DEL FOOTER
case 'mi_form_footer_contacto': // **REEMPLAZA** con el "Form Name" de tu formulario de footer
$handler_file = $form_configs_path . 'mi-formulario-footer-handler.php'; // Nombre del archivo handler
me_ghl_write_log("Router: Cargando handler para 'mi_form_footer_contacto' desde: " . $handler_file);
if (file_exists($handler_file)) {
if (is_readable($handler_file)) {
require_once $handler_file;
// **REEMPLAZA** 'me_ghl_process_mi_footer_form' con el nombre de la función en tu handler
if (function_exists('me_ghl_process_mi_footer_form')) {
me_ghl_process_mi_footer_form( $record, $handler );
} else {
me_ghl_write_log("Router ERROR: Función para 'mi_form_footer_contacto' no definida en " . $handler_file);
}
} else {
me_ghl_write_log("Router ERROR: Archivo handler '" . $handler_file . "' no es legible.");
}
} else {
me_ghl_write_log("Router AVISO: Archivo handler para '" . sanitize_text_field($form_name) . "' no encontrado en: " . $handler_file);
}
break;
default:
me_ghl_write_log("Router AVISO: Formulario '" . sanitize_text_field($form_name) . "' no tiene un 'case' configurado.");
break;
}
}
add_action( 'elementor_pro/forms/new_record', 'me_ghl_elementor_form_submission_router', 10, 2 );
?>
Paso 5: Código para un Manejador de Formulario Específico (Ejemplo: Newsletter)
Este archivo define la lógica particular para un formulario.
Crea el archivo form-configs/mi-formulario-newsletter-handler.php (o el nombre que coincida con tu case en el switch) y pega:
PHP
<?php
// /wp-content/plugins/mi-elementor-ghl-integracion/form-configs/mi-formulario-newsletter-handler.php
if ( ! defined( 'ABSPATH' ) ) {
exit; // No acceso directo.
}
/**
* Función manejadora específica para el formulario 'mi_newsletter_form'.
* Esta es llamada por el router en el archivo principal del plugin.
*/
function me_ghl_process_mi_newsletter_form( $record, $handler ) { // **ASEGÚRATE QUE ESTE NOMBRE DE FUNCIÓN COINCIDA CON EL SWITCH**
// Asegúrate que la función de log esté disponible
if (!function_exists('me_ghl_write_log')) {
error_log("Error crítico en mi-formulario-newsletter-handler.php: me_ghl_write_log no está definida.");
return;
}
$form_specific_log_prefix = "Form Handler [mi_newsletter_form]: ";
me_ghl_write_log($form_specific_log_prefix . "Iniciando procesamiento.");
// 1. Cargar API Key de GHL
$ghlApiKey = getenv('GHL_API_KEY');
if (empty($ghlApiKey)) {
me_ghl_write_log($form_specific_log_prefix . "Error Crítico: GHL_API_KEY no disponible.");
// $handler->add_error_message("Error de configuración del servidor."); // Opcional
return;
}
// 2. Extraer y Sanitizar Datos
$fields = $record->get_formatted_data();
// **ACCIÓN REQUERIDA POR TI:** Ajusta 'id_campo_email_elementor' al ID exacto de tu campo de email en ESTE formulario de Elementor.
$elementor_email_field_id = 'id_campo_email_elementor';
$email_input = $fields[$elementor_email_field_id] ?? null;
if (empty($email_input)) {
me_ghl_write_log($form_specific_log_prefix . "Error: Email no encontrado con ID de campo Elementor: '" . sanitize_text_field($elementor_email_field_id) . "'. Datos recibidos: " . json_encode($fields));
// $handler->add_error_message("El campo de email es obligatorio."); // Opcional
return;
}
$email_sanitized = filter_var(trim($email_input), FILTER_SANITIZE_EMAIL);
if (!filter_var($email_sanitized, FILTER_VALIDATE_EMAIL)) {
me_ghl_write_log($form_specific_log_prefix . "Error: Formato de email inválido: " . htmlspecialchars($email_input));
// $handler->add_error_message("Por favor, ingresa un email válido."); // Opcional
return;
}
me_ghl_write_log($form_specific_log_prefix . "Email validado: " . $email_sanitized);
$name_sanitized = '';
// **ACCIÓN REQUERIDA POR TI (Opcional):** Si tienes un campo de nombre, ajusta 'id_campo_nombre_elementor'.
// $elementor_name_field_id = 'id_campo_nombre_elementor';
// $name_input = $fields[$elementor_name_field_id] ?? '';
// if (!empty($name_input)) {
// $name_sanitized = sanitize_text_field(trim($name_input));
// me_ghl_write_log($form_specific_log_prefix . "Nombre extraído: " . htmlspecialchars($name_sanitized));
// }
// 3. Preparar Payload para GHL
$ghlLocationIdFromEnv = getenv('GHL_LOCATION_ID');
if (empty($ghlLocationIdFromEnv)) {
me_ghl_write_log($form_specific_log_prefix . "Error Crítico: GHL_LOCATION_ID no configurada en .htaccess.");
return;
}
// **ACCIÓN REQUERIDA POR TI:** Define las etiquetas y fuente para ESTE formulario.
$ghl_tags = ['newsletter_signup', 'mi_sitio_web'];
$ghl_source = 'Formulario Newsletter (Mi Sitio Web)';
$ghl_payload_array = [
'email' => $email_sanitized,
'source' => $ghl_source,
'locationId' => $ghlLocationIdFromEnv,
'tags' => $ghl_tags
];
if (!empty($name_sanitized)) {
$ghl_payload_array['firstName'] = $name_sanitized;
}
// 4. LLAMAR A LA FUNCIÓN COMÚN DE ENVÍO A GHL
if (!function_exists('me_ghl_send_contact_to_ghl')) {
me_ghl_write_log($form_specific_log_prefix . "Error crítico: La función me_ghl_send_contact_to_ghl no está definida.");
// $handler->add_error_message("Error de configuración del plugin."); // Opcional
return;
}
$ghl_result = me_ghl_send_contact_to_ghl($ghl_payload_array, $ghlApiKey);
// 5. MANEJAR RESPUESTA DE GHL
if ($ghl_result['success']) {
me_ghl_write_log($form_specific_log_prefix . "Datos enviados exitosamente a GHL.");
// $handler->add_success_message("¡Gracias por suscribirte!"); // Opcional
} else {
me_ghl_write_log($form_specific_log_prefix . "Fallo al enviar a GHL. HTTP Code: " . ($ghl_result['http_code'] ?? 'N/A') . " Error: " . ($ghl_result['error_message'] ?? 'Desconocido') . " Respuesta GHL: " . ($ghl_result['response_body'] ?? ''));
// $handler->add_error_message("Hubo un problema al procesar tu suscripción."); // Opcional
}
}
?>
Paso 6: Configurar Formularios en Elementor
- Para cada formulario que quieras integrar (ej. tu formulario de newsletter):
- Ve a la configuración del formulario en Elementor.
- Asegúrate de que tenga un “Nombre del Formulario” (Form Name) único. Este nombre DEBE COINCIDIR con uno de los
caseen elswitchde tu archivomi-elementor-ghl-integracion.php(ej.mi_newsletter_form). - Para cada campo (email, nombre, etc.), ve a la pestaña “Avanzado” del campo y anota o define su “ID de Campo” (Field ID). Usarás estos IDs en el archivo handler correspondiente (ej. en
mi-formulario-newsletter-handler.phppara$elementor_email_field_id).
- Importante: Con este método de plugin, NO necesitas usar la acción “Webhook” en la sección “Actions After Submit” de Elementor. El plugin se encarga de interceptar todos los envíos de Elementor Pro a través del hook
elementor_pro/forms/new_record.
Paso 7: Instalar y Activar el Plugin
- Coloca los tres archivos (
mi-elementor-ghl-integracion.php,includes/common-ghl-functions.php, y tu primer archivo handler comoform-configs/mi-formulario-newsletter-handler.php) en la estructura de carpetasmi-elementor-ghl-integracion/. - Comprime la carpeta
mi-elementor-ghl-integracion/en un archivo.zip. - En tu panel de WordPress, ve a “Plugins” > “Añadir nuevo” > “Subir plugin”.
- Selecciona el archivo
.zipy haz clic en “Instalar ahora”. - Activa el plugin.
Paso 8: Pruebas y Depuración
- Envía tu formulario de Elementor configurado.
- Revisa primero el
error_logde PHP de tu servidor. Busca las líneas de diagnóstico[ME-GHL Plugin Init]para confirmar quecommon-ghl-functions.phpse carga. - Luego, revisa el log personalizado del plugin:
wp-content/uploads/me-ghl-logs/elementor_ghl_router_log.txt. Este te mostrará el flujo del router y cualquier error que las funciones de log del plugin capturen. - Verifica si el contacto llega a GoHighLevel con las etiquetas y datos correctos.
Paso 9: Añadir Más Formularios
- En Elementor: Dale un “Form Name” único a tu nuevo formulario y anota los IDs de sus campos.
- Crea un nuevo archivo handler en la carpeta
form-configs/(ej.mi-otro-formulario-handler.php). Puedes copiarmi-formulario-newsletter-handler.phpcomo plantilla. - Modifica el nuevo archivo handler:
- Cambia el nombre de la función (ej. de
me_ghl_process_mi_newsletter_formame_ghl_process_mi_otro_formulario_form). - Ajusta el
$form_specific_log_prefix. - Actualiza los
$elementor_..._field_idpara que coincidan con los IDs de campo de tu nuevo formulario. - Define las
$ghl_tagsy$ghl_sourceespecíficas para este nuevo formulario.
- Cambia el nombre de la función (ej. de
- En el archivo principal del plugin (
mi-elementor-ghl-integracion.php):- Añade un nuevo
casealswitchpara el “Form Name” de tu nuevo formulario, que incluya su nuevo archivo handler y llame a su nueva función de procesamiento.
- Añade un nuevo
Espero que este tutorial detallado y los códigos generalizados te sirvan de guía. Recuerda reemplazar los placeholders (como “TU_API_KEY…”, “mi_newsletter_form”, “id_campo_email_elementor”) con tus valores reales.