• 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 » WordPress Development Recipe: Secure token-based API authentication for Pipedrive custom leads API in custom plugins

WordPress Development Recipe: Secure token-based API authentication for Pipedrive custom leads API in custom plugins

Setting Up Secure Token-Based API Authentication for Pipedrive Custom Leads

This recipe details the implementation of secure token-based authentication for a custom WordPress plugin interacting with the Pipedrive API, specifically for managing custom lead fields. We’ll focus on generating, storing, and validating API tokens to ensure secure data exchange.

Prerequisites

  • A functional WordPress installation.
  • A Pipedrive account with API access enabled.
  • Basic understanding of WordPress plugin development and REST API concepts.
  • Composer installed for dependency management.

1. Generating and Storing Pipedrive API Tokens

For security and manageability, Pipedrive API tokens should not be hardcoded. We’ll implement a mechanism within your WordPress plugin to securely store these tokens. A common approach is to use WordPress’s options API, but for enhanced security, consider encrypting sensitive data or using environment variables if your hosting environment supports it. For this recipe, we’ll demonstrate storing the token in the WordPress options table, with a note on encryption.

1.1. User Interface for Token Input

Create an administration page within your plugin to allow users to input their Pipedrive API token. This page should be accessible from the WordPress admin menu.

Plugin File Structure (Example)

Assume your plugin is structured as follows:

  • my-pipedrive-plugin/
  • my-pipedrive-plugin.php (main plugin file)
  • admin/
  • admin/class-pipedrive-admin.php
  • includes/
  • includes/class-pipedrive-api.php

admin/class-pipedrive-admin.php

This class will handle the creation of the admin menu and the settings page.

<?php
/**
 * Pipedrive Admin Class.
 */
class Pipedrive_Admin {

    /**
     * Constructor.
     */
    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_plugin_page' ) );
        add_action( 'admin_init', array( $this, 'page_init' ) );
    }

    /**
     * Add options page.
     */
    public function add_plugin_page() {
        add_options_page(
            __( 'Pipedrive API Settings', 'my-pipedrive-plugin' ),
            __( 'Pipedrive API', 'my-pipedrive-plugin' ),
            'manage_options',
            'pipedrive-api-settings',
            array( $this, 'create_admin_page' )
        );
    }

    /**
     * Options page callback.
     */
    public function create_admin_page() {
        ?>
        <div class="wrap">
            <h1><?php _e( 'Pipedrive API Settings', 'my-pipedrive-plugin' ); ?></h1>
            <form method="post" action="options.php">
                
            </form>
        </div>
        <?php
    }

    /**
     * Register and add settings.
     */
    public function page_init() {
        register_setting(
            'pipedrive_option_group', // Option group
            'pipedrive_api_token',     // Option name
            array( $this, 'sanitize_api_token' ) // Sanitize callback
        );

        add_settings_section(
            'pipedrive_setting_section', // ID
            __( 'Pipedrive API Configuration', 'my-pipedrive-plugin' ), // Title
            array( $this, 'print_section_info' ), // Callback
            'pipedrive-api-settings' // Page
        );

        add_settings_field(
            'pipedrive_api_token_field', // ID
            __( 'Pipedrive API Token', 'my-pipedrive-plugin' ), // Title
            array( $this, 'api_token_callback' ), // Callback
            'pipedrive-api-settings', // Page
            'pipedrive_setting_section' // Section
        );
    }

    /**
     * Print the section introduction.
     */
    public function print_section_info() {
        print __( 'Enter your Pipedrive API token below. You can find this in your Pipedrive account settings under "Personal preferences" > "API".', 'my-pipedrive-plugin' );
    }

    /**
     * Get the html for the API token field.
     */
    public function api_token_callback() {
        $api_token = get_option( 'pipedrive_api_token' );
        printf(
            '<input type="text" id="pipedrive_api_token_field" name="pipedrive_api_token" value="%s" class="regular-text" />',
            esc_attr( $api_token )
        );
    }

    /**
     * Sanitize API token input.
     *
     * @param string $input The input from the user.
     * @return string The sanitized input.
     */
    public function sanitize_api_token( $input ) {
        // Basic sanitization: remove whitespace.
        // For enhanced security, consider encryption here.
        return sanitize_text_field( trim( $input ) );
    }
}

// Initialize the admin class
if ( is_admin() ) {
    new Pipedrive_Admin();
}

1.2. Storing the Token Securely

The `register_setting` function in `admin/class-pipedrive-admin.php` saves the token to the `wp_options` table. While `sanitize_text_field` provides basic sanitization, for production environments, consider encrypting the token before storing it. WordPress doesn’t have a built-in robust encryption API for arbitrary data, but you can leverage libraries like the defuse/php-encryption library. This would involve generating an encryption key (stored securely, perhaps in `wp-config.php` as a constant) and encrypting/decrypting the token whenever it’s read or written.

2. Implementing the Pipedrive API Client

Create a class to handle all interactions with the Pipedrive API. This class will retrieve the stored API token and use it for authentication.

includes/class-pipedrive-api.php

<?php
/**
 * Pipedrive API Client Class.
 */
class Pipedrive_API_Client {

    private $api_token;
    private $api_base_url = 'https://api.pipedrive.com/v1/';

    /**
     * Constructor.
     */
    public function __construct() {
        $this->api_token = get_option( 'pipedrive_api_token' );
        // If using encryption, decrypt the token here.
        // Example: $this->api_token = Pipedrive_Encryption::decrypt( get_option( 'pipedrive_api_token_encrypted' ) );
    }

    /**
     * Check if API token is set.
     *
     * @return bool
     */
    public function is_token_set() {
        return ! empty( $this->api_token );
    }

    /**
     * Make a GET request to the Pipedrive API.
     *
     * @param string $endpoint The API endpoint.
     * @param array  $args     Optional arguments.
     * @return array|WP_Error The API response or a WP_Error object.
     */
    public function get( $endpoint, $args = array() ) {
        return $this->request( 'GET', $endpoint, $args );
    }

    /**
     * Make a POST request to the Pipedrive API.
     *
     * @param string $endpoint The API endpoint.
     * @param array  $args     Optional arguments.
     * @return array|WP_Error The API response or a WP_Error object.
     */
    public function post( $endpoint, $args = array() ) {
        return $this->request( 'POST', $endpoint, $args );
    }

    /**
     * Make a PUT request to the Pipedrive API.
     *
     * @param string $endpoint The API endpoint.
     * @param array  $args     Optional arguments.
     * @return array|WP_Error The API response or a WP_Error object.
     */
    public function put( $endpoint, $args = array() ) {
        return $this->request( 'PUT', $endpoint, $args );
    }

    /**
     * Make a DELETE request to the Pipedrive API.
     *
     * @param string $endpoint The API endpoint.
     * @param array  $args     Optional arguments.
     * @return array|WP_Error The API response or a WP_Error object.
     */
    public function delete( $endpoint, $args = array() ) {
        return $this->request( 'DELETE', $endpoint, $args );
    }

    /**
     * Core request method.
     *
     * @param string $method   HTTP method (GET, POST, PUT, DELETE).
     * @param string $endpoint The API endpoint.
     * @param array  $args     Optional arguments.
     * @return array|WP_Error The API response or a WP_Error object.
     */
    private function request( $method, $endpoint, $args = array() ) {
        if ( ! $this->is_token_set() ) {
            return new WP_Error( 'pipedrive_api_error', __( 'Pipedrive API token is not configured.', 'my-pipedrive-plugin' ) );
        }

        $url = trailingslashit( $this->api_base_url ) . ltrim( $endpoint, '/' );

        $request_args = array(
            'method'  => strtoupper( $method ),
            'headers' => array(
                'Authorization' => 'Bearer ' . $this->api_token,
                'Content-Type'  => 'application/json',
            ),
            'timeout' => 30, // Adjust timeout as needed
        );

        if ( 'GET' === strtoupper( $method ) ) {
            // For GET requests, arguments are typically query parameters.
            if ( ! empty( $args ) ) {
                $url = add_query_arg( $args, $url );
            }
        } else {
            // For POST, PUT, DELETE, arguments are in the body.
            if ( ! empty( $args ) ) {
                $request_args['body'] = json_encode( $args );
            }
        }

        $response = wp_remote_request( $url, $request_args );

        if ( is_wp_error( $response ) ) {
            return $response;
        }

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

        if ( $response_code >= 400 ) {
            // Pipedrive API errors are usually in the 'error' key.
            $error_message = isset( $data['error'] ) ? $data['error'] : $response_body;
            return new WP_Error( 'pipedrive_api_error', sprintf( __( 'Pipedrive API Error (%d): %s', 'my-pipedrive-plugin' ), $response_code, $error_message ) );
        }

        return $data;
    }

    /**
     * Example: Get custom fields for leads.
     *
     * @return array|WP_Error
     */
    public function get_lead_custom_fields() {
        return $this->get( 'leadFields' );
    }

    /**
     * Example: Create a new lead.
     *
     * @param array $lead_data Lead data.
     * @return array|WP_Error
     */
    public function create_lead( $lead_data ) {
        // Ensure custom fields are passed correctly.
        // Pipedrive expects custom fields in a 'custom_fields' array.
        // Example: $lead_data['custom_fields'] = ['field_id_1' => 'value1', 'field_id_2' => 'value2'];
        return $this->post( 'leads', $lead_data );
    }

    // Add more methods for other Pipedrive API operations as needed.
}

3. Integrating with WordPress Actions and Filters

Now, let’s hook into WordPress to use the Pipedrive API client. For instance, you might want to sync custom lead data when a WordPress form is submitted or when a specific post type is created.

Example: Syncing Data on Form Submission

Assume you have a form submission handler. You would instantiate the `Pipedrive_API_Client` and use its methods.

<?php
// In your main plugin file (my-pipedrive-plugin.php) or a dedicated handler file.

// Include the API client class.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-pipedrive-api.php';

/**
 * Handles form submission and syncs data to Pipedrive.
 */
function my_pipedrive_handle_form_submission() {
    // Check if the form was submitted and if the nonce is valid.
    if ( isset( $_POST['my_pipedrive_form_submit'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'my_pipedrive_submit_nonce' ) ) {

        $pipedrive_client = new Pipedrive_API_Client();

        if ( ! $pipedrive_client->is_token_set() ) {
            // Handle error: API token not configured.
            // You might want to display an admin notice or log this.
            error_log( 'Pipedrive API token is not set. Cannot sync lead.' );
            return;
        }

        // Prepare lead data from $_POST.
        $lead_data = array(
            'title' => sanitize_text_field( $_POST['lead_title'] ),
            'person_id' => isset( $_POST['person_id'] ) ? intval( $_POST['person_id'] ) : null, // Example: Link to an existing person
            'organization_id' => isset( $_POST['organization_id'] ) ? intval( $_POST['organization_id'] ) : null, // Example: Link to an existing organization
            // Add other standard Pipedrive lead fields as needed.
        );

        // Handle custom fields.
        // Assuming your form has fields like 'custom_field_email', 'custom_field_phone'.
        // You need to know the Pipedrive custom field IDs.
        $custom_fields_to_sync = array();
        if ( ! empty( $_POST['custom_field_email'] ) ) {
            // Replace 'YOUR_EMAIL_CUSTOM_FIELD_ID' with the actual ID from Pipedrive.
            $custom_fields_to_sync['YOUR_EMAIL_CUSTOM_FIELD_ID'] = sanitize_email( $_POST['custom_field_email'] );
        }
        if ( ! empty( $_POST['custom_field_phone'] ) ) {
            // Replace 'YOUR_PHONE_CUSTOM_FIELD_ID' with the actual ID from Pipedrive.
            $custom_fields_to_sync['YOUR_PHONE_CUSTOM_FIELD_ID'] = sanitize_text_field( $_POST['custom_field_phone'] );
        }

        if ( ! empty( $custom_fields_to_sync ) ) {
            $lead_data['custom_fields'] = $custom_fields_to_sync;
        }

        // Make the API call to create the lead.
        $result = $pipedrive_client->create_lead( $lead_data );

        if ( is_wp_error( $result ) ) {
            // Handle API error.
            error_log( 'Pipedrive lead creation failed: ' . $result->get_error_message() );
            // You might want to redirect back with an error message.
        } else {
            // Lead created successfully.
            // $result will contain the Pipedrive API response.
            $lead_id = $result['data']['id'];
            // You can now store this lead_id in your WordPress database if needed.
            // For example, as a post meta if this form submission is tied to a post.
            // update_post_meta( $post_id, '_pipedrive_lead_id', $lead_id );
        }
    }
}
// Hook this function to your form submission action.
// For example, if you're using a shortcode and processing it within the shortcode handler.
// Or if you have a specific AJAX endpoint.
// add_action( 'my_custom_form_submission_hook', 'my_pipedrive_handle_form_submission' );
?>

4. Handling API Errors and Rate Limiting

Pipedrive’s API has rate limits. Your client should be designed to handle these gracefully. The `WP_Error` objects returned by `wp_remote_request` and our `Pipedrive_API_Client` are crucial for this.

4.1. Error Handling Strategy

Always check the return value of API calls for `is_wp_error()`. Log errors for debugging. Inform the user if the action cannot be completed due to an API issue. For critical operations, consider implementing retry mechanisms with exponential backoff for transient errors (e.g., 5xx server errors or rate limit exceeded errors).

4.2. Rate Limiting Considerations

Pipedrive’s API typically returns a 429 Too Many Requests status code when rate limits are exceeded. Your `request` method in `Pipedrive_API_Client` should detect this specific error code and implement a retry strategy. This might involve pausing execution for a short period (e.g., 5-10 seconds) and retrying the request. For more sophisticated handling, you might want to cache API responses for a short duration or implement a queueing system for outgoing API requests.

// Inside the Pipedrive_API_Client::request method, after checking for WP_Error:

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

        // Check for rate limiting (429)
        if ( 429 === $response_code ) {
            // Implement retry logic here. This is a simplified example.
            // A more robust solution would involve a loop, retry count, and backoff.
            $retry_after = $response->get_header( 'Retry-After' ); // Pipedrive might provide this header
            $delay = $retry_after ? intval( $retry_after ) : 10; // Default to 10 seconds if header is missing

            error_log( sprintf( 'Pipedrive API rate limit exceeded. Retrying in %d seconds.', $delay ) );
            sleep( $delay ); // Pause execution

            // You would typically call $this->request() again here,
            // possibly with a retry counter to prevent infinite loops.
            // For simplicity, this example just logs and returns an error.
            return new WP_Error( 'pipedrive_api_rate_limit', __( 'Pipedrive API rate limit exceeded. Please try again later.', 'my-pipedrive-plugin' ) );
        }

        if ( $response_code >= 400 ) {
            // ... existing error handling ...
        }
// ... rest of the method

5. Best Practices and Further Enhancements

  • Encryption: As mentioned, encrypting the API token is highly recommended for production. Use a strong encryption library and manage your encryption keys securely.
  • Environment Variables: If your hosting allows, consider using environment variables for API keys instead of storing them in the WordPress options table. This is generally more secure.
  • Logging: Implement comprehensive logging for all API interactions, including successful requests and errors. This is invaluable for debugging.
  • Asynchronous Operations: For high-volume operations, consider using WordPress Cron or a dedicated job queue system (like Redis Queue or RabbitMQ) to process Pipedrive API requests asynchronously. This prevents long page load times and improves user experience.
  • Dependency Management: Use Composer to manage external libraries (like encryption libraries) for your plugin.
  • Input Validation: Always validate and sanitize all data before sending it to the Pipedrive API, and also validate data received from Pipedrive.
  • Custom Field Mapping: For custom fields, maintain a clear mapping between your WordPress form fields/data structures and Pipedrive’s custom field IDs. This mapping could be stored in plugin settings or a dedicated configuration file.

Conclusion

By following this recipe, you can implement a robust and secure token-based authentication system for your WordPress plugin’s Pipedrive API integrations. Remember to prioritize security by encrypting sensitive credentials and to handle API errors and rate limits effectively for a reliable user experience.

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

  • Optimizing p99 database query response latency in multi-site Domain-driven architecture (DDD) blocks custom tables
  • How to design a modular Action-hook Event Mediator architecture for enterprise-level custom plugins
  • Step-by-Step Guide to building a custom database optimizer portal block for Gutenberg using Next.js headless configurations
  • Optimizing WooCommerce cart response times by lazy loading custom user transaction ledgers assets
  • Step-by-Step Guide: Offloading high-frequency custom subscription logs metadata writes to a Redis KV store

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 (41)
  • 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 (66)
  • WordPress Plugin Development (72)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Optimizing p99 database query response latency in multi-site Domain-driven architecture (DDD) blocks custom tables
  • How to design a modular Action-hook Event Mediator architecture for enterprise-level custom plugins
  • Step-by-Step Guide to building a custom database optimizer portal block for Gutenberg using Next.js headless configurations

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