# Scheduled Bulk Tasks

https://dev.wisecp.com/es/scheduled-bulk-tasks

The six endpoints that send saved campaigns by themselves at a set time.

## Overview

A scheduled task sends a saved campaign **by itself** at the time you set. The task holds the schedule, not the message: the text and the audience live on the template.

There are two patterns. A **one-off** task runs on a date and is done. A **repeating** one is built from a month, a day and a time. Leaving a part out means "every", so day one with no month is the first of every month.

## Reference

### Listing the Tasks

get/api/v1/admin/tools/bulk/scheduled-tasks

`Tools/GetBulkScheduledTasks` admin paged

Returns the plan for the bulk notifications that send themselves.

Query parameters 3

pageintDefaults to 1.

limitintDefaults to 25, maximum 100.

searchstringSearches the tasks.

Response fields data[] — 12

idintId of the task.

template_namestringName of the campaign it sends.

template_typestringThe channel: `mail` or `sms`.

statusstring`active` is running, `paused` is stopped.

periodstring`onetime` fires once, `recurring` repeats.

period_datetimestring | nullWhen a one-off task fires.

period_monthint | nullThe month of a repeating task.

period_dayint | nullThe day of a repeating task.

period_hourint | nullThe hour of a repeating task.

period_minuteint | nullThe minute of a repeating task.

created_atstring | nullWhen it was created.

last_execstring | nullWhen it last ran. Empty means it never has.

Errors 1

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res  = await fetch('https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// An empty last-run time says the task has NEVER run; it may be paused.
$tasks = Api::Tools()->GetBulkScheduledTasks()['data'];

$never = array_filter($tasks, fn (array $t): bool => $t['last_exec'] === null);
```

### Creating a Task

post/api/v1/admin/tools/bulk/scheduled-tasks

`Tools/CreateBulkScheduledTask` admin 201

Opens a task that sends an existing campaign by itself at the time you set.

Body 6

template_idintrequiredId of the campaign to send. The campaign must not already be marked for automatic sending.

schedule_periodstring`onetime` or `recurring`. It defaults to one-off.

sending_timestringWhen to send. **Required** on a one-off.

period_monthintThe month of a repeating task.

period_dayintThe day of a repeating task.

period_timestringThe time of a repeating task.

Response fields data — 12

idintId of the task.

template_namestringName of the campaign it sends.

template_typestringThe channel: `mail` or `sms`.

statusstring`active` is running, `paused` is stopped.

periodstring`onetime` fires once, `recurring` repeats.

period_datetimestring | nullWhen a one-off task fires.

period_monthint | nullThe month of a repeating task.

period_dayint | nullThe day of a repeating task.

period_hourint | nullThe hour of a repeating task.

period_minuteint | nullThe minute of a repeating task.

created_atstring | nullWhen it was created.

last_execstring | nullWhen it last ran. Empty means it never has.

Errors 4

template_required422No campaign was given.

not_found404The source campaign was not found.

schedule_required422No schedule was given.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X POST 'https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"template_id":5,"schedule_period":"onetime","sending_time":"2026-02-15 10:00:00"}'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    template_id: 5,
    schedule_period: 'onetime',
    sending_time: '2026-02-15 10:00:00',
  }),
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'template_id'     => 5,
        'schedule_period' => 'onetime',
        'sending_time'    => '2026-02-15 10:00:00',
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// The first of every month at 08:00: day 1, month left out (every month).
Api::Tools()->CreateBulkScheduledTask([
    'template_id'     => 5,
    'schedule_period' => 'recurring',
    'period_day'      => 1,
    'period_time'     => '08:00',
]);
```

### Changing the Schedule

patch/api/v1/admin/tools/bulk/scheduled-tasks/{id}

`Tools/UpdateBulkScheduledTask` admin the schedule only

Changes when the task runs. The campaign itself is untouched.

Body 5

schedule_periodstring`onetime` or `recurring`. It defaults to one-off.

sending_timestringWhen to send. **Required** on a one-off.

period_monthintThe month of a repeating task.

period_dayintThe day of a repeating task.

period_timestringThe time of a repeating task.

Response fields data — 12

idintId of the task.

template_namestringName of the campaign it sends.

template_typestringThe channel: `mail` or `sms`.

statusstring`active` is running, `paused` is stopped.

periodstring`onetime` fires once, `recurring` repeats.

period_datetimestring | nullWhen a one-off task fires.

period_monthint | nullThe month of a repeating task.

period_dayint | nullThe day of a repeating task.

period_hourint | nullThe hour of a repeating task.

period_minuteint | nullThe minute of a repeating task.

created_atstring | nullWhen it was created.

last_execstring | nullWhen it last ran. Empty means it never has.

Errors 3

not_found404No such scheduled task.

schedule_required422No schedule was given.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X PATCH 'https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks/3' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"schedule_period":"recurring","period_day":1,"period_time":"08:00"}'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks/3', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    schedule_period: 'recurring',
    period_day: 1,
    period_time: '08:00',
  }),
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks/3');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PATCH',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'schedule_period' => 'recurring',
        'period_day'      => 1,
        'period_time'     => '08:00',
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// To change the message use the template update endpoint, NOT this one.
Api::Tools()->UpdateBulkTemplate(['id' => 5, 'subject' => 'Updated']);
Api::Tools()->UpdateBulkScheduledTask(['id' => 3, 'period_time' => '09:00']);
```

### Pausing a Task

post/api/v1/admin/tools/bulk/scheduled-tasks/{id}/pause

`Tools/PauseBulkScheduledTask` admin

Stops the task. The plan stops but is not deleted.

Body —

——No body is needed. The id in the path names the task; send an empty body.

Response fields data — 12

idintId of the task.

template_namestringName of the campaign it sends.

template_typestringThe channel: `mail` or `sms`.

statusstring`active` is running, `paused` is stopped.

periodstring`onetime` fires once, `recurring` repeats.

period_datetimestring | nullWhen a one-off task fires.

period_monthint | nullThe month of a repeating task.

period_dayint | nullThe day of a repeating task.

period_hourint | nullThe hour of a repeating task.

period_minuteint | nullThe minute of a repeating task.

created_atstring | nullWhen it was created.

last_execstring | nullWhen it last ran. Empty means it never has.

Errors 2

not_found404No such scheduled task.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X POST 'https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks/3/pause' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks/3/pause', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks/3/pause');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// Pausing does not stop notices already IN THE QUEUE: if the send started, delete them from the queue.
Api::Tools()->PauseBulkScheduledTask(['id' => 3]);
```

### Resuming a Task

post/api/v1/admin/tools/bulk/scheduled-tasks/{id}/resume

`Tools/ResumeBulkScheduledTask` admin a past time is missed

Puts a paused task back into service.

Body —

——No body is needed. The id in the path names the task; send an empty body.

Response fields data — 12

idintId of the task.

template_namestringName of the campaign it sends.

template_typestringThe channel: `mail` or `sms`.

statusstring`active` is running, `paused` is stopped.

periodstring`onetime` fires once, `recurring` repeats.

period_datetimestring | nullWhen a one-off task fires.

period_monthint | nullThe month of a repeating task.

period_dayint | nullThe day of a repeating task.

period_hourint | nullThe hour of a repeating task.

period_minuteint | nullThe minute of a repeating task.

created_atstring | nullWhen it was created.

last_execstring | nullWhen it last ran. Empty means it never has.

Errors 2

not_found404No such scheduled task.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X POST 'https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks/3/resume' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks/3/resume', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks/3/resume');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// A one-off whose time has passed does not run on resume: move the schedule forward first.
$task = Api::Tools()->ResumeBulkScheduledTask(['id' => 3])['data'];

if ($task['period'] === 'onetime' && $task['period_datetime'] < $now) {
    Api::Tools()->UpdateBulkScheduledTask([
        'id'           => 3,
        'sending_time' => $tomorrow,
    ]);
}
```

### Deleting a Task

delete/api/v1/admin/tools/bulk/scheduled-tasks/{id}

`Tools/DeleteBulkScheduledTask` admin its records go too

Deletes the scheduled task and its run records. The campaign stays.

Response fields data — 2

deletedboolWhether the delete succeeded.

idintId of the deleted task.

Errors 2

not_found404No such scheduled task.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X DELETE 'https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks/3' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks/3', {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/bulk/scheduled-tasks/3');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'DELETE',
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// Deleting the task does not delete the CAMPAIGN: the template stays and can still be sent by hand.
Api::Tools()->DeleteBulkScheduledTask(['id' => 3]);
```

## Pitfalls

> **A task whose time has passed does not run on resume**
> 
> Pause a one-off task, resume it after its send time has passed, and it **never runs**. A past time does not come round again, and nothing tells you. Move the time forward first.

> **The task does not hold the message**
> 
> These endpoints change the **schedule** and nothing else. Correcting the text, the subject or the audience means going to the template endpoints. A change there takes effect on the next run.

> **Pausing does not stop notices already sent**
> 
> Pausing stops the **next** runs only. After a run the notices are already in the queue and keep going out. Stopping them means deleting the pending rows in the notification queue.

> **The campaign must not already send itself**
> 
> Creating a task requires that the source campaign is not already marked for automatic sending. Otherwise the same announcement can go out by two routes at once. The campaign's state for this is on the template detail.

> **Leaving a part out means "every"**
> 
> On a repeating task, a month or a day left out counts as **every** value. Giving only a time and forgetting the day sends the announcement daily. Read the task back from the list and check the pattern after creating it.

## Related Articles

- [Bulk Templates](https://dev.wisecp.com/en/bulk-templates)
- [Bulk Messaging](https://dev.wisecp.com/en/bulk-messaging)
- [Notification Queue](https://dev.wisecp.com/en/notification-queue)
