/ Article — Action Scheduler

Action Scheduler API Functions: Complete PHP Reference

as_schedule_single_action, as_schedule_recurring_action, as_enqueue_async_action, as_schedule_cron_action — every scheduling function with its full parameter list, return values, and real examples you can drop into production.

TL;DR

  • Action Scheduler exposes four scheduling functions: single_action, recurring_action, cron_action, and enqueue_async_action
  • All functions return an integer action ID; pass $unique = true to skip duplicates automatically
  • as_enqueue_async_action runs in the next queue cycle with no timestamp; as_schedule_single_action fires at a specific Unix time
  • The Webhook Actions plugin uses Action Scheduler under the hood — no manual API calls needed for webhook delivery
/ Overview

What is the Action Scheduler PHP API?

Action Scheduler is a scalable, auditable background-job library for WordPress, maintained by WooCommerce and documented at actionscheduler.org. It ships inside WooCommerce and is available as a standalone plugin. Its PHP API is a set of global functions that let you enqueue, schedule, and cancel background actions without touching WordPress's built-in WP-Cron machinery.

Every scheduled action maps to a WordPress hook. When the action fires, Action Scheduler calls do_action( $hook, ...$args ), so the callback is a regular hook handler. The queue runner — a separate process that pulls and executes pending actions — is the part that makes delivery reliable: it runs outside the page-load cycle, retries failed actions, and keeps a full attempt log.

The API has four scheduling functions, two query functions, and two cancellation functions. All are documented in the official usage guide.

/ Single Action

How does as_schedule_single_action work?

as_schedule_single_action schedules a one-time action to run at a specific Unix timestamp. It is the Action Scheduler equivalent of wp_schedule_single_event — but backed by a persistent database queue instead of the cron option.

PHP — as_schedule_single_action signature
// Schedule a one-time action at $timestamp as_schedule_single_action( int $timestamp, // Unix timestamp — when to run string $hook, // WordPress action hook to fire array $args = [], // Arguments passed to do_action( $hook, ...$args ) string $group = '', // Group name for batching / filtering bool $unique = false, // Skip if identical action already pending int $priority = 10 // Queue priority — lower fires first ): int; // Returns the action ID (or existing ID when $unique=true) // Real-world usage: send webhook 5 seconds from now $action_id = as_schedule_single_action( time() + 5, 'my_plugin_send_webhook', [ 'order_id' => $order_id, 'endpoint' => $url ], 'webhooks', true // skip duplicate if same order_id already queued );

The return value is the integer action ID stored in the wp_actionscheduler_actions table. When $unique is true and an identical pending action exists, the existing action's ID is returned without creating a duplicate. This is the primary idempotency mechanism in the Action Scheduler API.

/ Recurring

How does as_schedule_recurring_action differ?

as_schedule_recurring_action schedules an action to fire repeatedly at a fixed interval in seconds. After each execution, Action Scheduler automatically re-enqueues the next occurrence at $timestamp + N * $interval. Unlike WP-Cron's wp_schedule_event, missed runs do not stack — Action Scheduler always schedules the next slot after the previous one completes.

PHP — as_schedule_recurring_action signature
as_schedule_recurring_action( int $timestamp, // Timestamp of first run int $interval_in_seconds, // Seconds between runs (e.g. 3600 = hourly) string $hook, array $args = [], string $group = '', bool $unique = false, int $priority = 10 ): int; // Register on activation — guard with as_has_scheduled_action if ( ! as_has_scheduled_action( 'my_plugin_hourly_sync' ) ) { as_schedule_recurring_action( time(), 3600, 'my_plugin_hourly_sync', [], 'sync' ); }
/ Async

What does as_enqueue_async_action do?

as_enqueue_async_action queues an action to run as soon as the next queue-processing cycle picks it up — no future timestamp required. It is the right choice when you want work to happen in the background immediately, without the overhead of computing a timestamp or managing intervals.

The function has the same signature as the scheduling functions but without $timestamp. The action is stored with a status of pending and the queue runner claims it in its next batch.

PHP — as_enqueue_async_action
// Run ASAP — next queue cycle, outside the current request $action_id = as_enqueue_async_action( 'my_plugin_process_import', [ 'file' => $upload_path ], 'imports', true // unique — skip if already queued for this file ); // The current HTTP response can now return immediately. // add_action() handler fires later, in the queue runner process. add_action( 'my_plugin_process_import', function( $file ) { // heavy work here — runs outside the user's request } );
/ Cron Action

How does as_schedule_cron_action schedule on a cron expression?

as_schedule_cron_action schedules an action on a cron schedule expression (e.g. 0 6 * * * for 6 AM daily). Unlike as_schedule_recurring_action, which fires at a fixed interval from the previous run, a cron action fires at wall-clock times — it will always run at 6 AM regardless of when the previous run finished. Use it when clock-alignment matters (nightly reports, hourly syncs at the top of the hour).

PHP — as_schedule_cron_action
as_schedule_cron_action( time(), // find next occurrence after this timestamp '0 6 * * *', // standard cron expression: 06:00 UTC daily 'my_plugin_morning_report', [], 'reports' );
/ Query & Cancel

How do you query and cancel scheduled actions?

Two query functions let you inspect the queue before scheduling. as_has_scheduled_action( $hook, $args, $group ) returns true if any matching action is pending or running — use it to avoid duplicate scheduling. as_next_scheduled_action( $hook, $args, $group ) returns the Unix timestamp of the next run, or false if nothing is scheduled.

To remove actions: as_unschedule_action cancels the next occurrence; as_unschedule_all_actions cancels every matching action across all future occurrences. Both accept the same ($hook, $args, $group) signature and are safe to call even if no action is scheduled.

PHP — Query and cancel
// Guard before scheduling if ( as_has_scheduled_action( 'my_plugin_sync', [], 'sync' ) ) { return; // already queued } // Next run timestamp (false if nothing scheduled) $next = as_next_scheduled_action( 'my_plugin_sync' ); // Cancel next occurrence only as_unschedule_action( 'my_plugin_sync', [], 'sync' ); // Cancel all occurrences (use on plugin deactivation) as_unschedule_all_actions( 'my_plugin_sync' );
/ Deduplication

How does the $unique parameter prevent duplicate actions?

When $unique = true, Action Scheduler checks whether an identical action — same $hook, $args, and $group — already exists in a pending or running state before inserting. If one exists, the scheduling call returns the existing action's ID without creating a duplicate.

This is critical for webhook dispatch: if an order is saved multiple times in quick succession (common during WooCommerce checkout), you only want one delivery queued. Without $unique = true, every save fires a new action and the receiving endpoint gets duplicate payloads. The deduplication check compares the serialized $args array, so different argument values always create separate actions even with $unique = true.

Alternatively, the Webhook Actions plugin handles deduplication automatically via its built-in queue: each bound hook fires at most one queued delivery per trigger per webhook, without requiring manual $unique management in application code.

/ Comparison

Raw WP-Cron vs Action Scheduler: what is the difference?

FeatureRaw WP-Cron (wp_schedule_*)Action Scheduler (as_schedule_*)
Storagewp_options row — no per-action recordDedicated DB tables — one row per action
Delivery logNone — no attempt historyFull attempt log with timestamps and error text
RetryNo retry — one attempt onlyAutomatic retry with configurable attempts
ConcurrencyNo concurrency control — all jobs share one processConfigurable concurrent batches via filter
Claim lockingNo — duplicate processing possible under loadDB-level claim lock prevents duplicate execution
DeduplicationNone — manual workaround required$unique parameter checks pending/running queue
Admin UINone in coreStatus, logs, and manual cancel in WP admin
/ Webhook Integration

How do Webhook Actions and Action Scheduler work together?

The Webhook Actions plugin auto-detects Action Scheduler and uses it as the queue runner when available. When Action Scheduler is present, every triggered webhook delivery is enqueued via as_enqueue_async_action and executed outside the current PHP request. When Action Scheduler is absent, the plugin falls back to WP-Cron automatically.

From a developer perspective you do not call the AS API directly. You bind a WordPress hook (e.g. woocommerce_order_status_changed) to a webhook endpoint in the plugin's admin UI, and the plugin handles the rest: payload serialisation, queuing, header injection (X-Event-Id, X-Event-Timestamp, X-Webhook-Id), exponential-backoff retry (1 min → 2 min → 4 min → 8 min, capped at 1 hour, 5 attempts default), and delivery logging. The fswa_max_attempts and fswa_backoff_delay filters let you tune the retry schedule.

See the Action Scheduler overview for the queue-runner architecture and the concurrent batches guide for tuning under high load.

/ FAQ

Common questions

It returns an integer action ID — the primary key of the new row in wp_actionscheduler_actions. When $unique = true and a matching pending action already exists, it returns that existing action's ID without inserting a new row.
as_enqueue_async_action runs the action in the next available queue cycle with no specific timestamp — as soon as the queue runner picks it up. as_schedule_single_action runs at a specific future Unix timestamp. Use async when you want immediate background execution; use single when you need to delay or throttle execution to a specific time.
Pass $unique = true as the fifth argument to any scheduling function. Action Scheduler checks for an existing pending or running action with the same hook, args, and group before inserting. The check compares serialised args, so different argument values always create separate actions.
Nothing — both as_unschedule_action and as_unschedule_all_actions are safe to call with no matching actions. They perform a no-op rather than raising an error. It is safe to call these on plugin deactivation regardless of whether any actions were ever scheduled.
No. Action Scheduler ships bundled with WooCommerce but is also available as a standalone package via Composer (woocommerce/action-scheduler) or as a standalone plugin. Multiple plugins can bundle their own copy; Action Scheduler automatically resolves version conflicts and uses the highest version present.