WP Webhooks / Blog / WordPress internals
Article · WordPress internals

The Complete as_schedule_single_action Reference

as_schedule_single_action documentation: full PHP signature, the timestamp and $unique parameters, return value, and code examples for delayed jobs.

8 min 2026-07-10
#action-scheduler#wordpress#php

TL;DR: as_schedule_single_action() queues a one-time background job that fires at a specific Unix timestamp by calling do_action( $hook, ...$args ).

  • Signature: as_schedule_single_action( int $timestamp, string $hook, array $args = [], string $group = '', bool $unique = false, int $priority = 10 )
  • Returns the integer action ID from wp_actionscheduler_actions; with $unique = true it returns the existing ID instead of inserting a duplicate.
  • It is the persistent, retryable equivalent of wp_schedule_single_event() — backed by database tables, not the cron option.

/ Signature

What is the as_schedule_single_action signature?

as_schedule_single_action() schedules one background action to run once, at or after a given Unix timestamp. It is a global function shipped with Action Scheduler, the background-job library maintained by WooCommerce and bundled inside WooCommerce, Jetpack, and hundreds of other plugins. When the scheduled time arrives, the queue runner calls do_action( $hook, ...$args ), so the code that actually runs is an ordinary WordPress hook callback registered with add_action().

PHP — as_schedule_single_action full signature

as_schedule_single_action(
    int    $timestamp,           // Unix time of the earliest run
    string $hook,                // action hook fired via do_action()
    array  $args     = [],       // args spread into the callback
    string $group    = '',       // label for filtering / bulk cancel
    bool   $unique   = false,    // skip if an identical action is pending
    int    $priority = 10        // 0-255, lower runs first (AS 3.5+)
): int;  // returns the action ID, or 0 on failure

The $unique parameter was added in Action Scheduler 3.3.0 and $priority in 3.5.0. On older bundled versions those trailing arguments are simply ignored, so passing them is safe but has no effect until the host site ships a recent enough copy.

/ Parameters

What does each as_schedule_single_action parameter do?

The first two arguments are required; the rest are optional. Getting $args and $group right matters most, because they are what the deduplication and cancellation functions match on.

ParameterTypePurpose
$timestampintEarliest Unix time to run. The action fires on the first queue pass at or after this moment — not exactly on it.
$hookstringThe action hook name. Register a callback with add_action( $hook, … ) or nothing runs.
$argsarrayValues spread into the callback as do_action( $hook, ...$args ). Keep it small — args are serialised into the row.
$groupstringFree-text label used to filter in the admin UI and to bulk-cancel with as_unschedule_all_actions().
$uniqueboolWhen true, skip insertion if a pending/running action with the same hook, args and group already exists.
$priorityint0–255 (default 10). Lower numbers are claimed first within the same timestamp. Added in AS 3.5.0.

/ Timestamp

How precise is the $timestamp argument?

The timestamp is a floor, not an alarm clock. Action Scheduler runs a queue on each WP-Cron pass (or Action Scheduler's own runner), claims a batch of actions whose scheduled_date is in the past, and executes them. So an action set for time() + 5 runs on the first queue pass after those five seconds — which could be a few seconds late on a busy site, or longer if no traffic triggers WP-Cron. If you need clock-aligned execution (always at 06:00), use as_schedule_cron_action() instead; if you need it to run as soon as possible with no delay target at all, use as_enqueue_async_action().

Lifecycle of an action created by as_schedule_single_actionA call to as_schedule_single_action inserts one pending row in wp_actionscheduler_actions stamped with the target timestamp. Once that time passes the queue runner claims the row, marks it in-progress, and calls do_action on the hook with the stored args. A clean run marks the action complete; a thrown exception marks it failed and Action Scheduler retries it.

not yet

yes

returns cleanly

throws

as_schedule_single_action()
timestamp + hook + args + group

pending row
wp_actionscheduler_actions

timestamp reached?

queue runner claims row
status = in-progress

do_action( hook, ...args )

status = complete

status = failed
retried by Action Scheduler

FIG 01 — Lifecycle of a single scheduled action

/ Return value

What does as_schedule_single_action return?

It returns the integer action ID — the primary key of the new row in the wp_actionscheduler_actions table. Store it if you plan to cancel or inspect the action later. Two edge cases change what you get back:

  1. Duplicate skipped. When $unique = true and a matching pending or in-progress action already exists, the call returns that existing action's ID and inserts nothing.
  2. Failure. If the action store cannot save the row, the function returns 0. Treat a falsy return as "not scheduled" rather than assuming success.
Every scheduled action maps to a WordPress hook. When the action runs, Action Scheduler calls do_action($hook, ...$args) — the callback is just a normal hook handler. — Action Scheduler usage guide

/ Deduplication

How does $unique prevent duplicate single actions?

Passing $unique = true makes the scheduling call idempotent: before inserting, Action Scheduler looks for a pending or running action with the same hook, the same serialised $args, and the same group. If one exists, no new row is created. This is the race-safe way to guarantee "at most one queued job" for an event, because the check and the insert happen inside the same call rather than in your application code. The matching is exact, though — a different argument value, order, or type counts as a distinct action. For the guard-based alternative and the exact matching rules, see preventing duplicate scheduled actions.

PHP — schedule one delayed, deduplicated webhook

// Deliver a webhook 30s after checkout — but only once per order
$action_id = as_schedule_single_action(
    time() + 30,
    'myplugin_deliver_order_webhook',
    [ 'order_id' => $order_id ],
    'order-webhooks',
    true            // unique: repeated saves won't queue a 2nd delivery
);

add_action( 'myplugin_deliver_order_webhook', function( $order_id ) {
    $order = wc_get_order( $order_id );
    wp_remote_post( 'https://example.com/hook', [
        'body' => wp_json_encode( $order->get_data() ),
    ] );
} );

/ vs WP-Cron

How is it different from wp_schedule_single_event?

Both schedule a one-time hook, but the storage and reliability differ sharply. wp_schedule_single_event() stores every event in a single serialised cron row in wp_options, runs on visitor page loads, keeps no history, and never retries. as_schedule_single_action() stores one row per action, is claimed by a dedicated queue runner, retries failures, and logs every attempt.

Featurewp_schedule_single_eventas_schedule_single_action
StorageOne serialised cron option rowOne DB row per action, indexed
TriggerVisitor page loads (WP-Cron)Dedicated queue runner batch
RetryNone — one attemptAutomatic retry on failure
HistoryNone once it runsFull attempt log in wp-admin
DedupManual — read the cron array$unique = true, race-safe
Cancelwp_unschedule_event by timestampas_unschedule_action by hook/args/group

/ In practice

Do you call as_schedule_single_action for webhooks directly?

Usually not. Firing an outbound webhook when a WordPress or WooCommerce event happens is event-driven, not time-delayed: you hook do_action and POST on the event itself, ideally behind a queue that adds retry with exponential backoff and a delivery log. You reach for as_schedule_single_action() in your own code when you need a delayed one-off job that is not a webhook: a reminder email, a cleanup pass, a deferred API sync. For the full function family — async, recurring, cron, query and cancel — see the Action Scheduler PHP API reference.

FAQ

Common questions always ask.

Don't see yours? Open an issue on GitHub or check the full reference in the API docs.

What is the signature of as_schedule_single_action? +
as_schedule_single_action( int $timestamp, string $hook, array $args = [], string $group = '', bool $unique = false, int $priority = 10 ) and it returns the integer action ID. The first two arguments — the Unix timestamp and the hook name — are required; the rest are optional. $unique was added in Action Scheduler 3.3.0 and $priority in 3.5.0.
What does as_schedule_single_action return? +
It returns the integer action ID — the primary key of the new row in wp_actionscheduler_actions. When $unique is true and a matching pending or in-progress action already exists, it returns that existing action ID without inserting a duplicate. If the row cannot be saved it returns 0, so a falsy return means the action was not scheduled.
Does the action run exactly at the timestamp? +
No. The timestamp is the earliest time the action can run, not an exact alarm. Action Scheduler executes it on the first queue pass at or after that moment, which can be a few seconds late on a busy site or longer if no traffic triggers the queue. For clock-aligned execution use as_schedule_cron_action instead.
How is as_schedule_single_action different from wp_schedule_single_event? +
Both schedule a one-time hook, but as_schedule_single_action stores one indexed row per action in dedicated database tables, is run by a dedicated queue runner, retries on failure, and logs every attempt. wp_schedule_single_event stores events in a single serialised cron option, runs on visitor page loads, keeps no history, and never retries.
How do I stop as_schedule_single_action creating duplicates? +
Pass $unique = true as the fifth argument. Before inserting, Action Scheduler checks for a pending or running action with the same hook, serialised args, and group; if one exists it returns that ID instead of creating a second row. Because the check and insert happen in one call, it is race-safe.
Ready

Your next automation is
one sentence away.

$ wp plugin install flowsystems-webhook-actions --activate