Recipe: Register a scheduled cron task

Goal

Register a scheduled task that fires on a fixed external schedule (via FastCron) rather than relying on WordPress’s request-driven wp-cron.php.


Prerequisites

  • A sibling plugin with its own slug
  • FastCron configured on the customer site (admin task — see Integration: FastCron)
  • An understanding that callbacks are invoked via apply_filters($cron_id, []) — return values are discarded

Steps

1. Choose a cron id

Convention: mam_<plugin_slug>_<task_name> (snake_case). The id is the dispatch handle. Must be unique site-wide.

Example: mam_my_plugin_nightly_sync.

2. Register the cron entry

add_filter( 'mam_cron_manager', function ( array $crons ): array {

    $crons['mam_my_plugin_nightly_sync'] = array(
        'title'      => 'My Plugin: Nightly Sync',
        'expression' => '0 2 * * *',                  // 2am daily, in site timezone
        'callback'   => 'mam_my_plugin_nightly_sync_action',
    );

    return $crons;
} );

3. Implement the callback

function mam_my_plugin_nightly_sync_action( $unused = array() ) {

    // Do the work.
    $this->sync_recent_changes();

    // Return value is discarded.
    return array();
}
add_filter( 'mam_my_plugin_nightly_sync', 'mam_my_plugin_nightly_sync_action' );

⚠️ The callback is invoked via apply_filters($cron_id, []), not do_action. Use add_filter to register, not add_action.

4. Verify in the admin

Mobile App Manager → Scheduled Jobs lists every registered cron. Your entry appears with the title and expression. The list page is mam_setcron_list (includes/setcron-manager/cron-list.php).

5. Confirm FastCron is configured

If tsl-setting-setcron-api is '1' or empty, FastCron is disabled and your cron never fires. Admin must enter a real FastCron API token in Mobile App Manager → Setcron Manager.

6. Test

The fastest test: trigger the AJAX endpoint directly.

curl 'https://example.com/wp-admin/admin-ajax.php?action=mam_setcron_processor'

This simulates a FastCron tick. Watch your callback’s logs / side effects.


Cron expression cheatsheet

*  *  *  *  *
│  │  │  │  └── day of week (0-6, 0=Sunday)
│  │  │  └───── month (1-12)
│  │  └──────── day of month (1-31)
│  └─────────── hour (0-23)
└────────────── minute (0-59)

0 2 * * *     → 2am daily
*/15 * * * *  → every 15 minutes
0 0 * * 0     → Sunday midnight
0 9-17 * * 1-5 → on the hour, 9am-5pm, Mon-Fri

⚠️ Resolution is bounded by FastCron’s tick frequency. If FastCron pings every 5 minutes, * * * * * runs every 5 minutes, not every minute.


Patterns

Heavy work → queue + drain pattern

function mam_my_plugin_nightly_sync_action( $unused = array() ) {
    // Don't do all the work inline; queue and return.
    foreach ( $this->get_pending_items() as $item_id ) {
        wp_schedule_single_event( time() + 60, 'my_plugin_process_item', array( $item_id ) );
    }
}

Idempotent reads / writes

function mam_my_plugin_nightly_sync_action( $unused = array() ) {
    $last_run = (int) get_option( 'mam_my_plugin_nightly_last_run', 0 );

    if ( time() - $last_run < 12 * HOUR_IN_SECONDS ) {
        // Already ran in the last 12 hours — skip (defends against multiple ticks within a window)
        return;
    }

    update_option( 'mam_my_plugin_nightly_last_run', time() );

    $this->do_work();
}

Gotchas

  • apply_filters not do_action. Don’t use add_action to register the callback — use add_filter.
  • Cron id must be unique. Two plugins registering the same id silently overwrite.
  • No retry semantics. A failed callback isn’t retried until the next matching tick.
  • Long callbacks block the tick. If your callback runs for 5 minutes, no other crons matching the same tick fire until you return.
  • FastCron API-token sentinel value '1' means “feature disabled” — a real token is non-numeric.
  • Time-zone resolution for offset zones (+05:30) can fall through to UTC. Use named timezones.

  • Integration: FastCron
  • Notification queue and cron
  • Hook: mam_cron_manager

Metadata

Field Value
Article type Recipe (Developer)
Plugin slug mam-main
Applies to plugin version 2.1.11+
Category Extending MAM Suite
Audience PHP developer
Estimated time 15–30 minutes
Last verified 2026-05-02
Contents

    Need Support?

    Can’t find the answer you’re looking for? Don’t worry we’re here to help!