---
title: "as_enqueue_async_action: PHP Signature & Parameters"
description: "as_enqueue_async_action documentation: PHP signature, the $unique parameter, plus as_schedule_single_action and cancel functions with code examples."
url: "https://wpwebhooks.org/blog/action-scheduler-api-functions/"
date: "2026-05-24"
---

# as_enqueue_async_action: PHP Signature & Parameters

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](https://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](https://actionscheduler.org/usage/).

/ Single Action

## How does **as\_schedule\_single\_action** work?

[`as_schedule_single_action`](https://actionscheduler.org/usage/#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`](https://developer.wordpress.org/reference/functions/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 (see [preventing duplicate scheduled actions](https://wpwebhooks.org/blog/action-scheduler-prevent-duplicate-actions/) for the `$unique` vs guard trade-off and the exact matching rules). `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](https://wordpress.org/plugins/flowsystems-webhook-actions/) 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?**

| Feature | Raw WP-Cron (wp\_schedule\_\*) | Action Scheduler (as\_schedule\_\*) |
| --- | --- | --- |
| Storage | wp\_options row — no per-action record | Dedicated DB tables — one row per action |
| Delivery log | None — no attempt history | Full attempt log with timestamps and error text |
| Retry | No retry — one attempt only | Automatic retry with configurable attempts |
| Concurrency | No concurrency control — all jobs share one process | Configurable concurrent batches via filter |
| Claim locking | No — duplicate processing possible under load | DB-level claim lock prevents duplicate execution |
| Deduplication | None — manual workaround required | $unique parameter checks pending/running queue |
| Admin UI | None in core | Status, logs, and manual cancel in WP admin |

/ Webhook Integration

## How do Webhook Actions and Action Scheduler **work together?**

The [Webhook Actions plugin](https://wpwebhooks.org/wordpress-webhook-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](https://wpwebhooks.org/blog/action-scheduler-wordpress/) for the queue-runner architecture and the [concurrent batches guide](https://wpwebhooks.org/blog/action-scheduler-concurrent-batches-filter/) for tuning under high load.

## Structured data

```json
{"@context":"https://schema.org","@type":"Article","headline":"as_enqueue_async_action: PHP Signature & Parameters","description":"as_enqueue_async_action documentation: PHP signature, the $unique parameter, plus as_schedule_single_action and cancel functions with code examples.","datePublished":"2026-05-24","dateModified":"2026-05-24","author":{"@type":"Person","name":"Mateusz Skorupa","url":"https://wpwebhooks.org/about/"},"publisher":{"@type":"Organization","name":"WP Webhooks","url":"https://wpwebhooks.org"},"url":"https://wpwebhooks.org/blog/action-scheduler-api-functions/","image":"https://wpwebhooks.org/og_image.jpg","keywords":["action scheduler","as schedule single action","as schedule recurring action","as enqueue async action","action scheduler api","wordpress background jobs","action scheduler functions reference"]}

{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"WP Webhooks","item":"https://wpwebhooks.org/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://wpwebhooks.org/blog/"},{"@type":"ListItem","position":3,"name":"as_enqueue_async_action: PHP Signature & Parameters","item":"https://wpwebhooks.org/blog/action-scheduler-api-functions/"}]}

{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"What does as_schedule_single_action return?","acceptedAnswer":{"@type":"Answer","text":"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."}},{"@type":"Question","name":"What is the difference between as_enqueue_async_action and as_schedule_single_action?","acceptedAnswer":{"@type":"Answer","text":"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."}},{"@type":"Question","name":"How do I prevent duplicate scheduled actions?","acceptedAnswer":{"@type":"Answer","text":"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."}},{"@type":"Question","name":"What happens when as_unschedule_action is called and nothing is scheduled?","acceptedAnswer":{"@type":"Answer","text":"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."}},{"@type":"Question","name":"Does Action Scheduler require WooCommerce?","acceptedAnswer":{"@type":"Answer","text":"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."}}]}
```
