Scheduled Tasks

7 views Markdown

The four endpoints that list scheduled tasks, switch them and run them by hand.

Overview

A scheduled task is the definition of work an installation does by itself: issuing invoices, suspending services, sending notices. These four endpoints list the tasks, show their jobs, switch them on and off and run them by hand.

A task and a job are not the same. A task is a rule, and every run of it leaves one or more jobs in the queue. The counts in the task list describe that rule's jobs.

Some tasks run in two stages: one that works out what to do, and one that carries out what was found. The counts and the filters respect that split.

Reference

Listing the Tasks

get/api/v1/admin/automation/tasks
Automation/GetAutomationTasks admin

Returns every scheduled task on the installation with its state.

Response fields data[] — 14
taskstringThe task key. The other endpoints take this value.
display_namestringThe name it shows under.
descriptionstringWhat the task does.
frequencystringHow often it runs: minute, hour, day, month, unknown.
disabledboolWhether the task was switched off.
registeredboolWhether a handler exists to carry the task out. When false the task sits in the list unable to run.
last_run_atstring | nullWhen it last ran.
next_run_atstring | nullWhen it next runs.
pendingintHow many jobs wait.
processingintHow many are running.
today_completedintHow many completed today. On a two-stage task only the execute stage counts.
today_failedintHow many failed. Counted across the period the records are kept.
last_errorstring | nullThe last error message.
last_error_atstring | nullWhen that error happened.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/automation/tasks' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch('https://panel.example.com/api/v1/admin/automation/tasks', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const { data } = await res.json();

const broken = data.filter((t) => ! t.registered);
$ch = curl_init('https://panel.example.com/api/v1/admin/automation/tasks');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// SWITCHED OFF and NO HANDLER are different things: the second points at a gap in setup.
$tasks  = Api::Automation()->GetAutomationTasks()['data'];
$broken = array_filter($tasks, fn ($t) => ! $t['registered']);

Listing a Task's Jobs

get/api/v1/admin/automation/tasks/{task}/jobs
Automation/GetAutomationTaskJobs admin cursor paging

Returns the queue jobs belonging to one task.

Query 5
status_filterstringFilters by status.
stage_filterstringFilters by stage. On a two-stage task the discovery and the execution are separate.
qstringSearches the rows freely.
after_idintFetches rows after this id.
limitintHow many rows to return. Clamped between one and a hundred.
Response fields data — 3
rowsarrayThe job rows.
has_moreboolWhether more rows remain.
next_afterintThe id to hand to the next page.
Errors 2
task_required422No task key was given.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/automation/tasks/invoice-create/jobs?limit=50' \
  -H "Authorization: Bearer $API_KEY"
const url = new URL(`https://panel.example.com/api/v1/admin/automation/tasks/${task}/jobs`);
url.searchParams.set('limit', '50');

const res  = await fetch(url, {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const { data } = await res.json();

// Sonraki sayfa: url.searchParams.set('after_id', data.next_after)
$ch = curl_init('https://panel.example.com/api/v1/admin/automation/tasks/' . $task . '/jobs?' . http_build_query(['limit' => 50]));
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// This endpoint walks FORWARD while the general feed walks back. Do not mix the two.
$page = Api::Automation()->GetAutomationTaskJobs(['task' => $task], ['limit' => 50])['data'];
$next = Api::Automation()->GetAutomationTaskJobs(['task' => $task], [
    'after_id' => $page['next_after'],
]);

Switching a Task On and Off

put/api/v1/admin/automation/tasks/{task}/status
Automation/UpdateAutomationTaskStatus admin

Decides whether a task runs at all.

Body 1
enabledintreqSwitches the task on or off.
Response fields data — 2
taskstringThe task key.
enabledboolHow the task now stands.
Errors 4
enabled_required422Neither on nor off was given.
task_required422No task key was given.
not_found404No such task.
insufficient_scope403The key lacks the required scope.
Request
curl -X PUT 'https://panel.example.com/api/v1/admin/automation/tasks/invoice-create/status' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"enabled":0}'
const res = await fetch(`https://panel.example.com/api/v1/admin/automation/tasks/${task}/status`, {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ enabled: 0 }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/automation/tasks/' . $task . '/status');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['enabled' => 0]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Switching off does not clear the QUEUE: what waits is picked up once it comes back on.
Api::Automation()->UpdateAutomationTaskStatus(['task' => $task, 'enabled' => 0]);

Running a Task Now

post/api/v1/admin/automation/tasks/{task}/run
Automation/RunAutomationTask admin it gets queued

Queues a task without waiting for its time.

Body
No body is needed, send an empty one. The task key in the path says which one to run.
Response fields data — 3
taskstringThe task key.
dispatchedboolWhether it went into the queue. It does not mean the task finished.
job_idintThe id of the job opened.
Errors 4
task_required422No task key was given.
no_handler422The task has no handler.
blocked_by_gate422A hook refused the run.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/automation/tasks/invoice-create/run' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch(`https://panel.example.com/api/v1/admin/automation/tasks/${task}/run`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const { data } = await res.json();
console.log(data.job_id);
$ch = curl_init('https://panel.example.com/api/v1/admin/automation/tasks/' . $task . '/run');
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);
// The call QUEUES the task rather than running it; follow the result by the job id.
$jobId = Api::Automation()->RunAutomationTask(['task' => $task])['data']['job_id'];
$job   = Api::Automation()->GetAutomationJob(['queue' => 'cron', 'id' => $jobId])['data'];

Pitfalls

Switched off and missing a handler are different

A task can be switched off, or it can have no handler at all. The second is not a choice but a gap in the setup: the task sits in the list, never runs, and the run-now call errors. Read the two fields apart when writing monitoring.

Running now does not run now

The run endpoint puts the task in the queue, and the scheduled worker does the work. By the time the response arrives the job may not have started, so follow the job id that comes back to see the outcome. With a dead worker the job is never picked up and waits quietly.

The two listings walk opposite ways

The task-jobs endpoint pages forward: it fetches what comes after the id you give. The general job feed goes backward. Code using both in one loop and mixing up the cursor fields either never advances or returns the same page.

Switching off does not empty the queue

Switching a task off stops it leaving new jobs and does not clear what already waits. Switch it back on and those older jobs get picked up too, running at a moment you did not expect. For a task going off for a long while, consider cancelling what waits as well.

Today's counts look at different windows

In the task list the completed count covers today while the failed count covers the whole period the records are kept. Putting the two side by side to work out a success rate gives a wrong answer. For counts over one window, count the task's jobs yourself with the status filter.

Was this helpful?

Thanks for your feedback!

Still Need Help?

Our support team is here around the clock for anything you can't find above.