• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » How to securely integrate Pipedrive custom leads API endpoints into WordPress custom plugins using Block Patterns API

How to securely integrate Pipedrive custom leads API endpoints into WordPress custom plugins using Block Patterns API

Securing Pipedrive API Credentials in WordPress

Integrating external APIs like Pipedrive into WordPress requires robust security practices, especially when handling sensitive credentials. Storing API keys directly within plugin files or the database in plain text is a critical vulnerability. A more secure approach involves leveraging WordPress’s built-in mechanisms for managing options and employing environment variables for sensitive data.

For this integration, we’ll assume you have a Pipedrive API token. The recommended method for storing this token is via environment variables. This keeps your credentials out of your version control system and separates configuration from code.

Registering Custom Settings and Fields for Pipedrive API Keys

WordPress provides the Settings API to create custom administration pages and fields. We’ll use this to create a secure input field for the Pipedrive API token. This field will be registered and then saved using WordPress’s `update_option()` function, but critically, we will *not* directly expose the API token in the WordPress database if it’s managed via environment variables.

First, let’s define the settings page and fields. This code should reside in your main plugin file or an included configuration file.

/**
 * Register Pipedrive API settings page and fields.
 */
function pipedrive_register_settings() {
    // Register settings group
    register_setting( 'pipedrive_options_group', 'pipedrive_api_token' );

    // Add settings section
    add_settings_section(
        'pipedrive_api_section',
        __( 'Pipedrive API Settings', 'your-text-domain' ),
        'pipedrive_api_section_callback',
        'pipedrive-settings'
    );

    // Add API token field
    add_settings_field(
        'pipedrive_api_token',
        __( 'Pipedrive API Token', 'your-text-domain' ),
        'pipedrive_api_token_callback',
        'pipedrive-settings',
        'pipedrive_api_section'
    );
}
add_action( 'admin_init', 'pipedrive_register_settings' );

/**
 * Callback for the API section description.
 */
function pipedrive_api_section_callback() {
    echo '<p>' . __( 'Enter your Pipedrive API token. For security, it is recommended to use environment variables.', 'your-text-domain' ) . '</p>';
}

/**
 * Callback for the API token field.
 */
function pipedrive_api_token_callback() {
    $api_token = get_option( 'pipedrive_api_token' );
    $env_token = getenv('PIPEDRIVE_API_TOKEN'); // Check environment variable

    // Prioritize environment variable if set
    if ( $env_token ) {
        echo '<input type="password" name="pipedrive_api_token" id="pipedrive_api_token" value="' . esc_attr( $env_token ) . '" class="regular-text" readonly disabled />';
        echo '<p class="description">' . __( 'API token is loaded from environment variable (PIPEDRIVE_API_TOKEN). Manual input is disabled.', 'your-text-domain' ) . '</p>';
    } else {
        echo '<input type="password" name="pipedrive_api_token" id="pipedrive_api_token" value="' . esc_attr( $api_token ) . '" class="regular-text" />';
        echo '<p class="description">' . __( 'Enter your Pipedrive API token here. If an environment variable PIIPEDRIVE_API_TOKEN is set, it will be used instead.', 'your-text-domain' ) . '</p>';
    }
}

/**
 * Add settings page to the admin menu.
 */
function pipedrive_add_settings_page() {
    add_options_page(
        __( 'Pipedrive Settings', 'your-text-domain' ),
        __( 'Pipedrive', 'your-text-domain' ),
        'manage_options',
        'pipedrive-settings',
        'pipedrive_settings_page_html'
    );
}
add_action( 'admin_menu', 'pipedrive_add_settings_page' );

/**
 * Render the settings page HTML.
 */
function pipedrive_settings_page_html() {
    // Check user capabilities
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    ?>
    

<?php echo esc_html( get_admin_page_title() ); ?>

In this code:

  • register_setting() registers the option group and the specific option name (`pipedrive_api_token`).
  • add_settings_section() and add_settings_field() define the structure of our settings page.
  • pipedrive_api_token_callback() renders the input field. It checks for the `PIPEDRIVE_API_TOKEN` environment variable. If found, it displays the token as read-only and disabled, indicating it's managed externally. Otherwise, it shows a standard password input field.
  • pipedrive_add_settings_page() hooks into `admin_menu` to add our settings page under the "Settings" menu.
  • pipedrive_settings_page_html() generates the HTML for the settings page, including the form and submission button.
  • pipedrive_sanitize_api_token() is crucial. It's hooked into the `sanitize_option_pipedrive_api_token` filter. If the `PIPEDRIVE_API_TOKEN` environment variable is set, it returns that value, effectively overriding any input from the form. If not, it sanitizes the user-provided input. This prevents saving a potentially insecure token directly from the form if an environment variable is the preferred method.

Retrieving the Pipedrive API Token Securely

When your plugin needs to make API calls, it should retrieve the token using a function that prioritizes the environment variable.

/**
 * Get the Pipedrive API token, prioritizing environment variables.
 *
 * @return string|false The API token or false if not found.
 */
function get_pipedrive_api_token() {
    $env_token = getenv('PIPEDRIVE_API_TOKEN');
    if ( $env_token ) {
        return $env_token;
    }

    // Fallback to WordPress option if environment variable is not set.
    // Note: This assumes the option was saved securely or is acceptable in your deployment.
    $option_token = get_option( 'pipedrive_api_token' );
    if ( ! empty( $option_token ) ) {
        return $option_token;
    }

    return false; // Token not found
}

This function, get_pipedrive_api_token(), first checks for the environment variable. If it exists, that token is returned. Only if the environment variable is not found does it fall back to retrieving the value stored in the WordPress options table. This ensures that your most secure credential source is always used.

Integrating with Pipedrive Custom Lead API Endpoints

Now, let's integrate this with Pipedrive's custom lead API. Pipedrive's API typically uses a base URL and requires an API token for authentication, usually passed as a query parameter or in an HTTP header.

We'll create a simple function to add a custom lead, demonstrating how to use the retrieved API token.

/**
 * Add a custom lead to Pipedrive.
 *
 * @param array $lead_data Associative array of lead data.
 * @return array|WP_Error API response or WP_Error on failure.
 */
function add_pipedrive_custom_lead( $lead_data ) {
    $api_token = get_pipedrive_api_token();
    if ( ! $api_token ) {
        return new WP_Error( 'pipedrive_api_error', __( 'Pipedrive API token not configured.', 'your-text-domain' ) );
    }

    $api_url = 'https://api.pipedrive.com/v1/leads'; // Pipedrive Leads API endpoint

    // Prepare data for the request
    $body = json_encode( $lead_data );

    // Make the API request using WordPress HTTP API for better integration and security
    $response = wp_remote_post( $api_url, array(
        'method'    => 'POST',
        'timeout'   => 45,
        'headers'   => array(
            'Content-Type'  => 'application/json',
            'Authorization' => 'Bearer ' . $api_token, // Common authentication method
            // Alternatively, Pipedrive might use a query parameter:
            // 'api_token' => $api_token,
        ),
        'body'      => $body,
        'data_format' => 'body',
    ) );

    if ( is_wp_error( $response ) ) {
        return $response; // Return WP_Error object
    }

    $response_code = wp_remote_retrieve_response_code( $response );
    $response_body = wp_remote_retrieve_body( $response );
    $data = json_decode( $response_body, true );

    if ( $response_code >= 200 && $response_code < 300 ) {
        // Success
        return $data;
    } else {
        // Handle API errors
        $error_message = isset( $data['error'] ) ? $data['error'] : __( 'Unknown Pipedrive API error.', 'your-text-domain' );
        return new WP_Error( 'pipedrive_api_error', sprintf( __( 'Pipedrive API Error (%d): %s', 'your-text-domain' ), $response_code, $error_message ) );
    }
}

// Example usage:
// $lead_info = array(
//     'title' => 'New Website Inquiry',
//     'person_id' => null, // Or a specific person ID if known
//     'organization_id' => null, // Or a specific organization ID
//     'owner_id' => 123, // Replace with actual owner ID
//     'status' => 'open',
//     'notes' => 'Inquiry from WordPress contact form.',
//     'custom_fields' => array(
//         'cf_123' => 'Value for custom field 123' // Replace with actual custom field key
//     )
// );
// $result = add_pipedrive_custom_lead( $lead_info );
// if ( is_wp_error( $result ) ) {
//     error_log( 'Failed to add Pipedrive lead: ' . $result->get_error_message() );
// } else {
//     // Lead added successfully
//     error_log( 'Pipedrive lead added successfully: ' . print_r( $result, true ) );
// }

Key points in this integration:

  • We use wp_remote_post() from the WordPress HTTP API. This is preferred over cURL directly as it's more integrated with WordPress, handles SSL verification, and respects WordPress configurations.
  • The API token is passed in the Authorization header as a Bearer token. Always consult Pipedrive's API documentation for the exact authentication method required.
  • Error handling is crucial. We check for is_wp_error() and also inspect the HTTP response code and body for API-specific errors.
  • The $lead_data array should be structured according to Pipedrive's API specifications for creating leads. This includes mandatory fields like `title` and potentially `owner_id`, and can include `person_id`, `organization_id`, and custom fields.

Leveraging Block Patterns for UI Integration

To make this lead creation process user-friendly within WordPress, we can use Block Patterns. A block pattern is a reusable collection of blocks that can be inserted into a post or page. We can create a custom block pattern that includes a form to capture lead details and then triggers our add_pipedrive_custom_lead() function via JavaScript.

First, register a custom block pattern category if you haven't already.

/**
 * Register custom block pattern category.
 */
function register_pipedrive_block_patterns_category() {
    register_block_pattern_category(
        'pipedrive-forms',
        array( 'label' => __( 'Pipedrive Forms', 'your-text-domain' ) )
    );
}
add_action( 'init', 'register_pipedrive_block_patterns_category' );

Next, create the block pattern itself. This pattern will include a form. The form submission will be handled by JavaScript, which will make an AJAX request to your WordPress backend.

Save this pattern in your theme's `patterns` directory or register it via PHP. For a plugin, registering via PHP is more robust.

/**
 * Register custom block patterns.
 */
function register_pipedrive_block_patterns() {
    $pattern = array(
        'title'       => __( 'Pipedrive Lead Capture Form', 'your-text-domain' ),
        'description' => __( 'A form to capture lead details and send them to Pipedrive.', 'your-text-domain' ),
        'content'     => '
            
            

Capture New Lead

Fill out the form below to add a new lead to Pipedrive.
















', 'categories' => array( 'pipedrive-forms' ), 'keywords' => array( 'pipedrive', 'lead', 'form', 'crm' ), ); register_block_pattern( 'your-plugin-slug/pipedrive-lead-capture-form', $pattern ); } add_action( 'init', 'register_pipedrive_block_patterns' );

Handling Form Submissions via AJAX

The HTML form within the block pattern needs JavaScript to handle its submission. This JavaScript will send the form data to a WordPress AJAX endpoint.

First, create a PHP function to handle the AJAX request. This function will call our add_pipedrive_custom_lead() function.

/**
 * AJAX handler for Pipedrive lead form submission.
 */
function ajax_handle_pipedrive_lead_submission() {
    // Verify nonce for security
    check_ajax_referer( 'pipedrive_lead_nonce', 'security' );

    // Sanitize and validate incoming data
    $lead_data = array();
    $lead_data['title'] = isset( $_POST['lead_name'] ) ? sanitize_text_field( $_POST['lead_name'] ) : '';
    $lead_data['email'] = isset( $_POST['lead_email'] ) ? sanitize_email( $_POST['lead_email'] ) : '';
    $lead_data['phone'] = isset( $_POST['lead_phone'] ) ? sanitize_text_field( $_POST['lead_phone'] ) : '';
    $lead_data['organization_name'] = isset( $_POST['lead_company'] ) ? sanitize_text_field( $_POST['lead_company'] ) : ''; // Pipedrive uses organization_name for new orgs
    $lead_data['notes'] = isset( $_POST['lead_notes'] ) ? sanitize_textarea_field( $_POST['lead_notes'] ) : '';

    // Basic validation
    if ( empty( $lead_data['title'] ) ) {
        wp_send_json_error( __( 'Lead name is required.', 'your-text-domain' ) );
    }

    // You might want to map company to organization_id if it exists, or create a new one.
    // For simplicity, we're passing organization_name which Pipedrive might use to find/create.
    // Consult Pipedrive API docs for exact handling of organizations.

    // Call the function to add the lead to Pipedrive
    $result = add_pipedrive_custom_lead( $lead_data );

    if ( is_wp_error( $result ) ) {
        wp_send_json_error( array(
            'message' => $result->get_error_message(),
            'code'    => $result->get_error_code(),
        ) );
    } else {
        wp_send_json_success( array(
            'message' => __( 'Lead successfully added to Pipedrive!', 'your-text-domain' ),
            'data'    => $result,
        ) );
    }
}
add_action( 'wp_ajax_add_pipedrive_lead', 'ajax_handle_pipedrive_lead_submission' );
add_action( 'wp_ajax_nopriv_add_pipedrive_lead', 'ajax_handle_pipedrive_lead_submission' ); // For logged-out users

Now, the JavaScript to enqueue and handle the AJAX request. This script should be enqueued on the frontend.

/**
 * Enqueue custom JavaScript for the Pipedrive form.
 */
function enqueue_pipedrive_form_script() {
    // Only enqueue on pages where the form might be used, or conditionally based on block presence.
    // For simplicity, we'll enqueue it everywhere for this example.
    // A more advanced approach would check if the block pattern is present.

    wp_enqueue_script(
        'pipedrive-form-handler',
        plugin_dir_url( __FILE__ ) . 'js/pipedrive-form-handler.js', // Path to your JS file
        array( 'jquery' ),
        '1.0.0',
        true // Load in footer
    );

    // Localize script to pass AJAX URL and nonce
    wp_localize_script( 'pipedrive-form-handler', 'pipedrive_ajax_object', array(
        'ajax_url' => admin_url( 'admin-ajax.php' ),
        'nonce'    => wp_create_nonce( 'pipedrive_lead_nonce' ),
    ) );
}
add_action( 'wp_enqueue_scripts', 'enqueue_pipedrive_form_script' );

Create the js/pipedrive-form-handler.js file:

jQuery(document).ready(function($) {
    $('#pipedrive-lead-form').on('submit', function(e) {
        e.preventDefault(); // Prevent default form submission

        var form = $(this);
        var statusDiv = $('#pipedrive-form-status');
        var submitButton = form.find('button[type="submit"]');

        // Disable button and show loading state
        submitButton.prop('disabled', true).text('Processing...');
        statusDiv.html('

Submitting lead...

'); var formData = { 'action': 'add_pipedrive_lead', // Corresponds to wp_ajax_add_pipedrive_lead 'security': pipedrive_ajax_object.nonce, // Pass the nonce 'lead_name': form.find('#lead_name').val(), 'lead_email': form.find('#lead_email').val(), 'lead_phone': form.find('#lead_phone').val(), 'lead_company': form.find('#lead_company').val(), 'lead_notes': form.find('#lead_notes').val() }; $.post(pipedrive_ajax_object.ajax_url, formData, function(response) { if (response.success) { statusDiv.html('

' + response.data.message + '

'); form.trigger('reset'); // Clear the form } else { var errorMessage = response.data.message || 'An unknown error occurred.'; statusDiv.html('

Error: ' + errorMessage + '

'); } }).fail(function(xhr, status, error) { statusDiv.html('

AJAX request failed: ' + error + '

'); }).always(function() { // Re-enable button submitButton.prop('disabled', false).text('Add Lead to Pipedrive'); }); }); });

This setup provides a secure and integrated way to manage Pipedrive API credentials and leverage its custom lead API within your WordPress site, enhanced by the flexibility of Gutenberg Block Patterns.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Reducing database query bloat in Sage Roots modern environments layouts using custom lazy loaders
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Firebase Realtime DB handlers
  • Reducing Largest Contentful Paint (LCP) by optimizing custom script enqueuing structures in legacy plugins
  • How to implement native Redis caching layers for high-volume custom taxonomy queries in Carbon Fields custom wrappers
  • Building secure B2B pricing grids with custom REST API Controllers endpoints and role overrides

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (48)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (182)
  • WordPress Plugin Development (197)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Reducing database query bloat in Sage Roots modern environments layouts using custom lazy loaders
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Firebase Realtime DB handlers
  • Reducing Largest Contentful Paint (LCP) by optimizing custom script enqueuing structures in legacy plugins

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala