• 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 implement custom Cron API (wp_schedule_event) endpoints with token authentication in Gutenberg blocks

How to implement custom Cron API (wp_schedule_event) endpoints with token authentication in Gutenberg blocks

Leveraging `wp_schedule_event` for Custom API Endpoints with Token Authentication

WordPress’s built-in cron system, `wp_cron`, is powerful but often requires custom scheduling for specific tasks. When these tasks involve external API calls or require secure, authenticated access, directly exposing `wp_schedule_event` can be problematic. This guide details how to implement custom API endpoints that are triggered by scheduled events, secured with token authentication, and callable from Gutenberg blocks.

Setting Up the Scheduled Event

First, we need to register a custom cron schedule and hook into it. This is typically done within a plugin’s main file or an `includes` file. We’ll define a daily schedule for demonstration purposes.

Registering a Custom Schedule

Use the `cron_schedules` filter to add your custom interval. This allows for more granular control than the default WordPress intervals.

/**
 * Add custom cron schedules.
 *
 * @param array $schedules Existing schedules.
 * @return array Modified schedules.
 */
function my_custom_cron_schedules( $schedules ) {
    $schedules['daily_custom'] = array(
        'interval' => DAY_IN_SECONDS,
        'display'  => esc_html__( 'Every Day (Custom)' ),
    );
    return $schedules;
}
add_filter( 'cron_schedules', 'my_custom_cron_schedules' );

Scheduling the Event

Once the schedule is registered, we can use `wp_schedule_event` to hook into it. This function should be called once, typically on plugin activation, to ensure the event is set up.

/**
 * Schedule the custom cron event on plugin activation.
 */
function my_schedule_custom_api_event() {
    if ( ! wp_next_scheduled( 'my_custom_api_event_hook' ) ) {
        wp_schedule_event( time(), 'daily_custom', 'my_custom_api_event_hook' );
    }
}
register_activation_hook( __FILE__, 'my_schedule_custom_api_event' );

/**
 * Hook for the custom API event.
 */
function my_custom_api_event_handler() {
    // This is where the API call or processing will happen.
    // For demonstration, we'll just log a message.
    error_log( 'Custom API event triggered at ' . current_time( 'mysql' ) );

    // In a real-world scenario, you would make an HTTP request here.
    // Example:
    // $response = wp_remote_post( 'https://your-external-api.com/endpoint', array(
    //     'headers' => array(
    //         'Authorization' => 'Bearer YOUR_SECURE_TOKEN',
    //         'Content-Type'  => 'application/json',
    //     ),
    //     'body' => json_encode( array( 'data' => 'some_value' ) ),
    // ) );
    // if ( is_wp_error( $response ) ) {
    //     error_log( 'Error calling external API: ' . $response->get_error_message() );
    // } else {
    //     error_log( 'External API response: ' . wp_remote_retrieve_body( $response ) );
    // }
}
add_action( 'my_custom_api_event_hook', 'my_custom_api_event_handler' );

Implementing Token-Authenticated API Endpoints

For security, especially when dealing with sensitive data or actions, direct cron execution isn’t ideal. Instead, the cron event should trigger an internal WordPress REST API endpoint. This endpoint will then perform the actual task and should be secured with a token.

Creating a Custom REST API Endpoint

We’ll register a new route using `register_rest_route`. This route will be accessible via `wp-json/myplugin/v1/process-event`.

/**
 * Register custom REST API endpoint.
 */
function my_register_custom_api_route() {
    register_rest_route( 'myplugin/v1', '/process-event', array(
        'methods'             => 'POST',
        'callback'            => 'my_handle_custom_api_request',
        'permission_callback' => 'my_verify_api_token',
    ) );
}
add_action( 'rest_api_init', 'my_register_custom_api_route' );

Token Authentication Callback

The `permission_callback` is crucial. It checks for a valid token. For simplicity, we’ll use a hardcoded token stored in `wp-config.php`. In production, consider more robust methods like JWT or OAuth.

/**
 * Verify API token for authentication.
 *
 * @param WP_REST_Request $request Full data about the request.
 * @return bool|WP_Error True if the request has permission, WP_Error object otherwise.
 */
function my_verify_api_token( WP_REST_Request $request ) {
    // Retrieve token from request header.
    $auth_header = $request->get_header( 'Authorization' );
    if ( empty( $auth_header ) ) {
        return new WP_Error( 'rest_not_logged_in', 'Authorization header missing.', array( 'status' => 401 ) );
    }

    // Expected format: 'Bearer YOUR_SECURE_TOKEN'
    list( $token_type, $token ) = explode( ' ', $auth_header );

    if ( $token_type !== 'Bearer' || empty( $token ) ) {
        return new WP_Error( 'rest_invalid_token', 'Invalid token format.', array( 'status' => 401 ) );
    }

    // Retrieve your secure token from a secure location (e.g., wp-config.php).
    // Define MY_SECURE_API_TOKEN in wp-config.php.
    if ( ! defined( 'MY_SECURE_API_TOKEN' ) || MY_SECURE_API_TOKEN === false ) {
        error_log( 'MY_SECURE_API_TOKEN is not defined in wp-config.php.' );
        return new WP_Error( 'rest_server_error', 'API token configuration error.', array( 'status' => 500 ) );
    }

    if ( hash_equals( MY_SECURE_API_TOKEN, $token ) ) {
        return true; // Token is valid
    } else {
        return new WP_Error( 'rest_invalid_token', 'Invalid API token.', array( 'status' => 401 ) );
    }
}

The API Endpoint Callback Function

This function is executed when the REST API endpoint is called and authentication passes. It will perform the actual task, such as making an external API call.

/**
 * Callback function for the custom API endpoint.
 *
 * @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.
 */
function my_handle_custom_api_request( WP_REST_Request $request ) {
    // Perform the actual task here.
    // For example, making an external API call.
    $external_api_url = 'https://your-external-api.com/process';
    $api_key = defined('MY_EXTERNAL_API_KEY') ? MY_EXTERNAL_API_KEY : ''; // Get from wp-config.php

    if ( empty( $api_key ) ) {
        return new WP_Error( 'rest_server_error', 'External API key not configured.', array( 'status' => 500 ) );
    }

    $body_data = array(
        'timestamp' => current_time( 'mysql' ),
        'site_url'  => get_site_url(),
        // Add any other relevant data from the request or WordPress
    );

    $response = wp_remote_post( $external_api_url, array(
        'method'  => 'POST',
        'headers' => array(
            'Authorization' => 'Bearer ' . $api_key,
            'Content-Type'  => 'application/json',
        ),
        'body'    => json_encode( $body_data ),
        'timeout' => 30, // Adjust timeout as needed
    ) );

    if ( is_wp_error( $response ) ) {
        error_log( 'Error calling external API: ' . $response->get_error_message() );
        return new WP_Error( 'rest_api_error', 'Failed to communicate with external API.', array( 'status' => 500 ) );
    }

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

    if ( $response_code >= 200 && $response_code < 300 ) {
        // Success
        return new WP_REST_Response( array(
            'success' => true,
            'message' => 'External API processed successfully.',
            'data'    => $response_body,
        ), 200 );
    } else {
        // Handle API errors
        error_log( 'External API returned an error: ' . $response_code . ' - ' . wp_remote_retrieve_body( $response ) );
        return new WP_Error( 'rest_api_error', 'External API returned an error.', array( 'status' => $response_code, 'details' => $response_body ) );
    }
}

Modifying the Cron Handler to Call the REST API

Now, we need to update the original cron handler (`my_custom_api_event_handler`) to trigger our secure REST API endpoint instead of performing the task directly. This ensures the token authentication is always enforced.

/**
 * Cron handler that triggers the secure REST API endpoint.
 */
function my_custom_api_event_handler() {
    error_log( 'Cron event triggered. Attempting to call REST API endpoint.' );

    // Define your secure token. Ensure MY_SECURE_API_TOKEN is set in wp-config.php.
    if ( ! defined( 'MY_SECURE_API_TOKEN' ) || MY_SECURE_API_TOKEN === false ) {
        error_log( 'MY_SECURE_API_TOKEN is not defined. Cannot call REST API.' );
        return;
    }
    $secure_token = MY_SECURE_API_TOKEN;

    // Construct the REST API URL.
    $rest_url = rest_url( 'myplugin/v1/process-event' );

    // Make the POST request to the REST API endpoint.
    $response = wp_remote_post( $rest_url, array(
        'method'  => 'POST',
        'headers' => array(
            'Authorization' => 'Bearer ' . $secure_token,
            'Content-Type'  => 'application/json',
        ),
        'timeout' => 15, // Shorter timeout for internal calls
    ) );

    if ( is_wp_error( $response ) ) {
        error_log( 'Error calling internal REST API endpoint: ' . $response->get_error_message() );
    } else {
        $response_code = wp_remote_retrieve_response_code( $response );
        $response_body = wp_remote_retrieve_body( $response );
        if ( $response_code >= 200 && $response_code < 300 ) {
            error_log( 'Successfully triggered REST API endpoint. Response: ' . $response_body );
        } else {
            error_log( 'REST API endpoint returned an error: ' . $response_code . ' - ' . $response_body );
        }
    }
}
// Ensure this hook is correctly registered and the schedule is active.
// The previous `add_action( 'my_custom_api_event_hook', 'my_custom_api_event_handler' );` should remain.

Configuration in `wp-config.php`

For security and ease of management, sensitive tokens should not be hardcoded directly in plugin files. Use `wp-config.php`.

// Add these lines to your wp-config.php file.
// Ensure these are unique and strong secrets.

// Token for authenticating internal API calls from cron.
define( 'MY_SECURE_API_TOKEN', 'your_very_secret_and_long_token_here_12345' );

// API key for your external service.
define( 'MY_EXTERNAL_API_KEY', 'your_external_api_key_here_abcde' );

Triggering from Gutenberg Blocks (Client-Side)

While the cron job handles scheduled execution, you might also want to trigger this API endpoint manually from a Gutenberg block. This requires client-side JavaScript to make an authenticated request.

Client-Side JavaScript for Gutenberg

This JavaScript code would be enqueued for your block. It makes a `POST` request to the REST API endpoint, including the authorization header.

// Assuming this is part of your block's JavaScript file.
// You'll need to enqueue this script properly.

document.addEventListener('DOMContentLoaded', function() {
    const triggerButton = document.getElementById('my-custom-api-trigger');

    if (triggerButton) {
        triggerButton.addEventListener('click', function() {
            const token = 'your_very_secret_and_long_token_here_12345'; // Ideally, this token is not hardcoded here.
                                                                    // Consider fetching it securely or using nonce if applicable.
            const apiUrl = '/wp-json/myplugin/v1/process-event';

            fetch(apiUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer ' + token
                }
            })
            .then(response => {
                if (!response.ok) {
                    // Handle non-OK responses (e.g., 401, 500)
                    return response.json().then(err => { throw err; });
                }
                return response.json();
            })
            .then(data => {
                console.log('API call successful:', data);
                alert('Action completed successfully!');
            })
            .catch(error => {
                console.error('API call failed:', error);
                alert('An error occurred: ' + (error.message || 'Unknown error'));
            });
        });
    }
});

Important Security Note: Exposing the `MY_SECURE_API_TOKEN` directly in client-side JavaScript is a significant security risk. This approach is only suitable for development or internal tools where the token is not exposed to untrusted users. For production environments, consider:

  • Using WordPress nonces for client-to-server communication and then having the server-side code (which has access to the token) make the external API call.
  • Implementing a dedicated API key management system.
  • Using OAuth or JWT for more sophisticated authentication flows.

Deactivating the Cron Event

It's good practice to unschedule the cron event when the plugin is deactivated to prevent orphaned events.

/**
 * Unschedule the custom cron event on plugin deactivation.
 */
function my_unschedule_custom_api_event() {
    $timestamp = wp_next_scheduled( 'my_custom_api_event_hook' );
    if ( $timestamp ) {
        wp_unschedule_event( $timestamp, 'my_custom_api_event_hook' );
    }
}
register_deactivation_hook( __FILE__, 'my_unschedule_custom_api_event' );

Summary and Best Practices

By combining WordPress's `wp_schedule_event` with its REST API and token-based authentication, you can create robust, secure, and scheduled background tasks. Key takeaways include:

  • Use `cron_schedules` for custom intervals.
  • Schedule events on plugin activation and unschedule on deactivation.
  • Route cron tasks through authenticated REST API endpoints for security.
  • Store sensitive tokens securely in `wp-config.php`.
  • Be extremely cautious when exposing tokens to client-side JavaScript; prefer server-side execution or nonce-based security.
  • Implement comprehensive error logging for both cron execution and API calls.

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