• 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 OpenAI Completion API endpoints into WordPress custom plugins using REST API Controllers

How to securely integrate OpenAI Completion API endpoints into WordPress custom plugins using REST API Controllers

Leveraging WordPress REST API Controllers for Secure OpenAI Integration

Integrating powerful AI capabilities, such as those offered by OpenAI’s Completion API, into a WordPress ecosystem demands a robust and secure approach. For expert WordPress developers, the native REST API, specifically its controller architecture, provides an ideal framework. This allows for the creation of custom endpoints that can securely handle API requests, manage authentication, and process responses without exposing sensitive credentials or logic directly within frontend JavaScript or less secure PHP contexts.

Setting Up the OpenAI API Key Securely

The OpenAI API key is a critical credential. It should never be hardcoded directly into plugin files, especially not in a way that could be exposed via version control or client-side code. The most secure method within WordPress is to utilize the WordPress options API, storing the key as a constant defined in wp-config.php or as a protected option. For this example, we’ll assume it’s defined as a constant for immediate availability.

First, define your API key in wp-config.php:

define( 'OPENAI_API_KEY', 'your_secret_openai_api_key_here' );

Next, ensure your plugin checks for the existence of this constant before attempting to use it.

Creating a Custom REST API Controller

WordPress’s REST API is extensible through classes that implement the WP_REST_Controller interface. This provides a structured way to define routes, endpoints, and their associated callback functions. We’ll create a controller for handling AI completions.

Create a new PHP file for your controller, for instance, inc/class-openai-completion-controller.php within your custom plugin’s directory.

<?php
/**
 * OpenAI Completion REST API Controller.
 *
 * Handles requests to the OpenAI Completion API.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

class OpenAI_Completion_Controller extends WP_REST_Controller {

    /**
     * The base route for this controller.
     *
     * @var string
     */
    protected $namespace = 'myplugin/v1';

    /**
     * The route for the completion endpoint.
     *
     * @var string
     */
    protected $rest_base = 'complete';

    /**
     * Register the routes for the OpenAI completion endpoint.
     */
    public function register_routes() {
        register_rest_route( $this->namespace, '/' . $this->rest_base, array(
            array(
                'methods'             => WP_REST_Server::CREATABLE, // Use CREATABLE for POST requests
                'callback'            => array( $this, 'get_completion' ),
                'permission_callback' => array( $this, 'permissions_check' ),
                'args'                => $this->get_endpoint_args_for_item_schema( true ),
            ),
        ) );
    }

    /**
     * Get the schema for the endpoint arguments.
     *
     * @return array Endpoint arguments.
     */
    public function get_endpoint_args_for_item_schema( $public = false ) {
        return array(
            'prompt' => array(
                'required'          => true,
                'type'              => 'string',
                'description'       => __( 'The prompt to send to the OpenAI API.', 'myplugin' ),
                'sanitize_callback' => 'sanitize_text_field',
                'validate_callback' => 'rest_validate_request_arg',
            ),
            'model' => array(
                'required'          => false,
                'type'              => 'string',
                'description'       => __( 'The OpenAI model to use (e.g., "text-davinci-003"). Defaults to "gpt-3.5-turbo-instruct".', 'myplugin' ),
                'default'           => 'gpt-3.5-turbo-instruct',
                'sanitize_callback' => 'sanitize_text_field',
                'validate_callback' => 'rest_validate_request_arg',
            ),
            'max_tokens' => array(
                'required'          => false,
                'type'              => 'integer',
                'description'       => __( 'The maximum number of tokens to generate.', 'myplugin' ),
                'default'           => 150,
                'sanitize_callback' => 'absint',
                'validate_callback' => array( $this, 'validate_max_tokens' ),
            ),
            // Add other parameters as needed (temperature, top_p, etc.)
        );
    }

    /**
     * Validate max_tokens argument.
     *
     * @param mixed $value The value to validate.
     * @param WP_REST_Request $request The request object.
     * @return WP_Error|bool True if the value is valid, WP_Error otherwise.
     */
    public function validate_max_tokens( $value, $request ) {
        if ( $value < 1 || $value > 2048 ) { // Example limit, adjust as per OpenAI's limits
            return new WP_Error( 'rest_invalid_param', __( 'max_tokens must be between 1 and 2048.', 'myplugin' ), array( 'status' => 400 ) );
        }
        return true;
    }

    /**
     * Check if the current user has permission to access the endpoint.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_Error|bool True if the request has permission, WP_Error otherwise.
     */
    public function permissions_check( $request ) {
        // Example: Only allow authenticated users with 'edit_posts' capability.
        // Adjust this based on your security requirements.
        if ( ! current_user_can( 'edit_posts' ) ) {
            return new WP_Error( 'rest_forbidden', esc_html__( 'You do not have permission to perform this action.', 'myplugin' ), array( 'status' => rest_authorization_required_code() ) );
        }

        // Ensure the API key is defined.
        if ( ! defined( 'OPENAI_API_KEY' ) || empty( OPENAI_API_KEY ) ) {
            return new WP_Error( 'rest_server_error', esc_html__( 'OpenAI API key is not configured.', 'myplugin' ), array( 'status' => 500 ) );
        }

        return true;
    }

    /**
     * Handles the OpenAI completion request.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function get_completion( WP_REST_Request $request ) {
        $prompt     = $request->get_param( 'prompt' );
        $model      = $request->get_param( 'model' );
        $max_tokens = $request->get_param( 'max_tokens' );

        // Basic validation (already done by get_endpoint_args_for_item_schema, but good for clarity)
        if ( empty( $prompt ) ) {
            return new WP_Error( 'rest_invalid_param', __( 'Prompt cannot be empty.', 'myplugin' ), array( 'status' => 400 ) );
        }

        // Prepare the request to OpenAI API
        $api_url = 'https://api.openai.com/v1/completions'; // For older models like text-davinci-003
        // For chat models, use: 'https://api.openai.com/v1/chat/completions' and adjust payload structure

        $body = array(
            'model'      => $model,
            'prompt'     => $prompt,
            'max_tokens' => $max_tokens,
            // Add other parameters like 'temperature', 'top_p', etc. as needed
        );

        $response = wp_remote_post( $api_url, array(
            'method'  => 'POST',
            'headers' => array(
                'Authorization' => 'Bearer ' . OPENAI_API_KEY,
                'Content-Type'  => 'application/json',
            ),
            'body'    => json_encode( $body ),
            'timeout' => 60, // Adjust timeout as necessary
        ) );

        if ( is_wp_error( $response ) ) {
            return new WP_Error( 'openai_api_error', $response->get_error_message(), array( 'status' => 500 ) );
        }

        $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 ) {
            $error_message = isset( $data['error']['message'] ) ? $data['error']['message'] : __( 'An unknown error occurred with the OpenAI API.', 'myplugin' );
            return new WP_Error( 'openai_api_error', $error_message, array( 'status' => $response_code ) );
        }

        // Process and return the completion
        if ( isset( $data['choices'][0]['text'] ) ) {
            return new WP_REST_Response( array(
                'success' => true,
                'completion' => trim( $data['choices'][0]['text'] ),
            ), 200 );
        } else {
            return new WP_Error( 'openai_api_error', __( 'Unexpected response format from OpenAI API.', 'myplugin' ), array( 'status' => 500 ) );
        }
    }
}

Registering the Controller

To make your controller active, you need to hook into the rest_api_init action and instantiate your controller.

In your plugin’s main file (e.g., myplugin.php), add the following:

/**
 * Load the REST API controller.
 */
function myplugin_register_openai_controller() {
    require_once plugin_dir_path( __FILE__ ) . 'inc/class-openai-completion-controller.php';
    $controller = new OpenAI_Completion_Controller();
    $controller->register_routes();
}
add_action( 'rest_api_init', 'myplugin_register_openai_controller' );

Understanding the Controller Components

  • $namespace and $rest_base: Define the URL structure for your endpoint (e.g., /wp-json/myplugin/v1/complete).
  • register_routes(): This method registers the actual endpoint. We’re using WP_REST_Server::CREATABLE to specify that this endpoint accepts POST requests.
  • permissions_check(): Crucial for security. This callback determines if the current user has the necessary privileges to access the endpoint. In this example, we’re checking for the edit_posts capability, but you should tailor this to your plugin’s specific needs. It also verifies that the OpenAI API key is defined.
  • get_endpoint_args_for_item_schema(): Defines the expected parameters for the endpoint, including their types, whether they are required, and importantly, sanitize_callback and validate_callback functions to clean and validate incoming data. This prevents injection attacks and ensures data integrity.
  • get_completion(): The core logic. This method retrieves parameters from the request, constructs the payload for the OpenAI API, makes the HTTP request using wp_remote_post, handles potential errors, and returns the AI-generated completion.
  • wp_remote_post(): WordPress’s built-in function for making HTTP requests. It’s used here to send the prompt to the OpenAI API. Note the inclusion of the Authorization header with the API key.
  • Error Handling: Robust error handling is implemented for both WordPress’s HTTP request failures and OpenAI API specific errors, returning appropriate WP_Error objects.

Security Considerations and Best Practices

  • API Key Management: As mentioned, never hardcode keys. Use wp-config.php constants or secure options. Consider using environment variables for more advanced deployments.
  • Capability Checks: The permissions_check method is paramount. Grant access only to users who genuinely need it. For public-facing features, you might need to implement nonce verification and check for logged-in status or specific user roles.
  • Input Sanitization and Validation: Always sanitize and validate all incoming data using WordPress functions (e.g., sanitize_text_field, absint) and custom validation callbacks. This is handled in get_endpoint_args_for_item_schema.
  • Rate Limiting: Implement rate limiting on your custom endpoint to prevent abuse and excessive API calls, which can incur significant costs. This can be done using WordPress transients or a dedicated plugin.
  • HTTPS Enforcement: Ensure your WordPress site uses HTTPS. The communication with OpenAI is already over HTTPS, but securing your own site is fundamental.
  • Logging: Implement detailed logging for API requests and responses (especially errors) for debugging and auditing purposes. Be careful not to log sensitive data like full API keys.
  • Model Selection: Be mindful of the models you expose. Some models are more expensive or have different capabilities. Allow users to select models if appropriate, but restrict choices to safe and cost-effective options.
  • Error Response Detail: Avoid exposing overly technical error messages to the client. Generalize error messages while logging specific details server-side.

Consuming the Endpoint from Frontend JavaScript

Once the REST API controller is set up, you can consume it from your plugin’s JavaScript. It’s essential to use WordPress’s wp.apiFetch for making requests, as it handles nonces and authentication automatically.

// Assuming you've enqueued this script and passed the nonce
// Example: wp_localize_script( 'my-plugin-script', 'myPluginData', array( 'rest_url' => rest_url(), 'nonce' => wp_create_nonce( 'wp_rest' ) ) );

if ( typeof wp !== 'undefined' && typeof wp.apiFetch !== 'undefined' ) {
    const promptText = 'Write a short poem about AI.';
    const endpoint = 'myplugin/v1/complete'; // Matches the namespace and rest_base

    wp.apiFetch( {
        path: endpoint,
        method: 'POST',
        data: {
            prompt: promptText,
            model: 'gpt-3.5-turbo-instruct', // Or another model
            max_tokens: 100
        }
    } )
    .then( response => {
        console.log( 'Success:', response );
        if ( response.success && response.completion ) {
            alert( 'AI Completion: ' + response.completion );
        } else {
            alert( 'Error: Could not get completion.' );
        }
    } )
    .catch( error => {
        console.error( 'Error:', error );
        alert( 'An error occurred: ' + error.message );
    } );
} else {
    console.error( 'WordPress REST API fetch is not available.' );
}

This approach ensures that your OpenAI API key remains server-side, your requests are authenticated and authorized within the WordPress context, and your data is properly validated and sanitized, leading to a secure and reliable integration.

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

  • How to analyze and reduce CPU consumption of custom Factory Method design structures event mediators
  • WordPress Development Recipe: High-efficiency server-side rendering for Gutenberg blocks using Readonly classes
  • How to securely integrate SendGrid transactional mailer endpoints into WordPress custom plugins using Filesystem API
  • How to design secure Algolia Search API webhook listeners using signature validation and payload queues
  • WordPress Development Recipe: Implementing a secure lock mechanism for multi-worker Cron tasks with Shortcode API

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 (42)
  • 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 (114)
  • WordPress Plugin Development (122)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • How to analyze and reduce CPU consumption of custom Factory Method design structures event mediators
  • WordPress Development Recipe: High-efficiency server-side rendering for Gutenberg blocks using Readonly classes
  • How to securely integrate SendGrid transactional mailer endpoints into WordPress custom plugins using Filesystem API

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