Notification Queue

11 views Markdown

The nine endpoints that watch, rescue and clear the e-mails and messages waiting to go out.

Overview

E-mails and text messages are not sent straight away; they go into a queue and are worked through in the background. These nine endpoints watch that queue, rescue what got stuck and clear what piled up.

The message itself is stored encrypted and comes back from no endpoint. What you get here is the subject, the recipient and the status. To read a message that went out, the place is the activity logs.

The shape is almost the same as the module queue, but two endpoints differ. Retry here also accepts queued items, while sending now accepts only queued ones.

Reference

Listing the Queue

get/api/v1/admin/tools/notification-queue
Tools/GetNotificationQueue admin the message never returns

Returns the e-mails and text messages waiting to go out.

Query parameters 5
statusstringFilters by status.
channelstringFilters by channel: e-mail or text message.
pageintDefaults to 1.
limitintDefaults to 25, maximum 100.
searchstringSearches the records.
Response fields data[] — 16
idintId of the queue item.
channelstringmail or sms.
recipientstringThe recipient's address or number.
recipient_namestringThe recipient's name.
subjectstringThe subject of the notice.
statusstringpending is queued, processing is going out, sent went, failed did not.
priorityintThe sending priority.
user_idintId of the client it concerns.
user_full_namestring | nullThe client's name.
attemptsintHow many times it was tried.
max_attemptsintHow many tries it gets.
batch_idstring | nullThe batch id. It ties together the notices from one bulk send.
created_atstring | nullWhen it entered the queue.
scheduled_atstring | nullWhen it is due to go.
processed_atstring | nullWhen it was processed.
next_retrystring | nullWhen the next try is due.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl -G 'https://panel.example.com/api/v1/admin/tools/notification-queue' \
  -H "Authorization: Bearer $API_KEY" \
  -d status=failed \
  -d channel=mail
const url = new URL('https://panel.example.com/api/v1/admin/tools/notification-queue');
url.searchParams.set('status', 'failed');
url.searchParams.set('channel', 'mail');

const res  = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();
$url = 'https://panel.example.com/api/v1/admin/tools/notification-queue?' . http_build_query([
    'status'  => 'failed',
    'channel' => 'mail',
]);

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// The message itself comes back from NO endpoint; beyond the subject and recipient there is no content.
$failed = Api::Tools()->GetNotificationQueue([], ['status' => 'failed'])['data'];

The Queue Counters

get/api/v1/admin/tools/notification-queue/stats
Tools/GetNotificationQueueStats admin

Returns how many notices are waiting and how many went out.

Response fields data — 5
totalintTotal notices in the queue.
pendingintHow many are queued.
processingintHow many are going out.
sentintHow many were sent.
failedintHow many failed.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/tools/notification-queue/stats' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch('https://panel.example.com/api/v1/admin/tools/notification-queue/stats', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/notification-queue/stats');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// A pending count that keeps growing is the sign that the sending pipeline has stopped.
$stats = Api::Tools()->GetNotificationQueueStats()['data'];

Item Detail

get/api/v1/admin/tools/notification-queue/{id}
Tools/GetNotificationQueueItem admin the step log

Returns one notice together with the record of its sending attempts.

Response fields data — 17
idintId of the queue item.
channelstringmail or sms.
recipientstringThe recipient's address or number.
recipient_namestringThe recipient's name.
subjectstringThe subject of the notice.
statusstringpending is queued, processing is going out, sent went, failed did not.
priorityintThe sending priority.
user_idintId of the client it concerns.
user_full_namestring | nullThe client's name.
attemptsintHow many times it was tried.
max_attemptsintHow many tries it gets.
batch_idstring | nullThe batch id. It ties together the notices from one bulk send.
created_atstring | nullWhen it entered the queue.
scheduled_atstring | nullWhen it is due to go.
processed_atstring | nullWhen it was processed.
next_retrystring | nullWhen the next try is due.
process_logsarrayThe step-by-step record of the attempts. Why a notice did not go is written here.
Errors 3
invalid_id422The id is not valid.
not_found404No such queue item.
insufficient_scope403The key lacks the required scope.
Request
curl 'https://panel.example.com/api/v1/admin/tools/notification-queue/201' \
  -H "Authorization: Bearer $API_KEY"
const res  = await fetch('https://panel.example.com/api/v1/admin/tools/notification-queue/201', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/notification-queue/201');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Even the detail does NOT carry the body: to read a sent message, look in the activity log.
$item = Api::Tools()->GetNotificationQueueItem(['id' => 201])['data'];
$why  = end($item['process_logs']);

Retrying an Item

post/api/v1/admin/tools/notification-queue/{id}/retry
Tools/RetryNotificationQueueItem admin

Puts a notice back in line to be sent.

Body
No body is needed. The notice is addressed by the path parameter; send an empty body.
Response fields data
dataobjectThe notice as it stands after the reset. Same shape as the item detail endpoint.
Errors 3
not_found404No such queue item.
not_retryable422Only failed or queued notices can be retried. One that already went cannot be sent again from here.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/tools/notification-queue/201/retry' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.example.com/api/v1/admin/tools/notification-queue/201/retry', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/notification-queue/201/retry');
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 retry also accepts QUEUED items, which is how you move one up the line.
Api::Tools()->RetryNotificationQueueItem(['id' => 201]);

Sending an Item Now

post/api/v1/admin/tools/notification-queue/{id}/run
Tools/RunNotificationQueueItem admin goes immediately

Sends the notice there and then, without waiting for the background worker, and returns the outcome.

Body
No body is needed. The notice is addressed by the path parameter; send an empty body.
Response fields data — 3
task_successboolWhether the send succeeded.
task_messagestringThe error message when it did not.
itemobjectThe notice as it stands after the send.
Errors 3
not_found404No such queue item.
not_pending422Only queued notices can be sent. A failed one has to be retried into the queue first.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/tools/notification-queue/201/run' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.example.com/api/v1/admin/tools/notification-queue/201/run', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/notification-queue/201/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);
// A failed notice cannot be sent directly: retry it into the queue first, then run it.
Api::Tools()->RetryNotificationQueueItem(['id' => 201]);
$result = Api::Tools()->RunNotificationQueueItem(['id' => 201])['data'];

if (!$result['task_success']) {
    $why = $result['task_message'];
}

Deleting an Item

delete/api/v1/admin/tools/notification-queue/{id}
Tools/DeleteNotificationQueueItem admin

Takes a notice out of the queue. One that had not gone never goes.

Response fields data — 2
deletedboolWhether the delete succeeded.
idintId of the deleted item.
Errors 2
not_found404No such queue item.
insufficient_scope403The key lacks the required scope.
Request
curl -X DELETE 'https://panel.example.com/api/v1/admin/tools/notification-queue/201' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.example.com/api/v1/admin/tools/notification-queue/201', {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/notification-queue/201');
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);
// A notice already going out CANNOT be deleted; wait until the send finishes.
Api::Tools()->DeleteNotificationQueueItem(['id' => 201]);

Deleting in Bulk

post/api/v1/admin/tools/notification-queue/bulk-delete
Tools/BulkDeleteNotificationQueue admin

Takes several notices out of the queue.

Body 1
idsint[]requiredIds of the notices to delete.
Response fields data — 2
deletedboolWhether the delete ran.
countintHow many were deleted. It can be fewer than you sent, because ones going out are skipped.
Errors 2
ids_required422No id was given.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/tools/notification-queue/bulk-delete' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"ids":[201,202]}'
const res = await fetch('https://panel.example.com/api/v1/admin/tools/notification-queue/bulk-delete', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ ids: [201, 202] }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/notification-queue/bulk-delete');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['ids' => [201, 202]]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Ones going out are skipped quietly: compare the deleted count with what you sent.
$ids      = [201, 202];
$response = Api::Tools()->BulkDeleteNotificationQueue(['ids' => $ids]);

$skipped = count($ids) - $response['data']['count'];

Retrying Everything That Failed

post/api/v1/admin/tools/notification-queue/retry-all-failed
Tools/RetryAllFailedNotificationQueue admin the whole queue

Puts every failed notice in the queue back in line.

Body
No body is needed. The action covers the whole queue and cannot be narrowed; send an empty body.
Response fields data — 1
retriedintHow many were queued again.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/tools/notification-queue/retry-all-failed' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.example.com/api/v1/admin/tools/notification-queue/retry-all-failed', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/notification-queue/retry-all-failed');
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);
// Unlike the module queue, this endpoint does return a COUNT.
$count = Api::Tools()->RetryAllFailedNotificationQueue()['data']['retried'];

Clearing What Was Sent

post/api/v1/admin/tools/notification-queue/clear-sent
Tools/ClearSentNotificationQueue admin

Deletes the sent notices from the queue.

Body
No body is needed. The action covers the whole queue and cannot be narrowed; send an empty body.
Response fields data — 1
clearedboolWhether the clear ran.
Errors 1
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/tools/notification-queue/clear-sent' \
  -H "Authorization: Bearer $API_KEY"
const res = await fetch('https://panel.example.com/api/v1/admin/tools/notification-queue/clear-sent', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/tools/notification-queue/clear-sent');
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);
// Clearing the queue does not delete the SENT RECORD: what went out stays in the activity log.
Api::Tools()->ClearSentNotificationQueue();

Pitfalls

The message itself comes back from no endpoint

The queue endpoints give the subject, the recipient and the status. The body is stored encrypted and is absent even from the detail. To see what a client was actually sent, look in the activity logs rather than the queue.

The two endpoints accept different statuses

The retry takes both failed and queued notices; sending now takes only queued ones. So sending a failed notice immediately means retrying it into the queue first and running it after. Trying it in one step answers not_pending.

An item going out cannot be deleted

A notice currently going out cannot be deleted and is skipped quietly in a bulk delete. The gap between how many ids you sent and the count you get back is what was skipped. Without checking it, a notice you thought you cancelled may already be on its way.

The two queues differ on the bulk endpoints

On the notification queue the retry-all endpoint returns how many notices were queued again. The same endpoint on the module queue does not. Writing one helper for both queues means accounting for that.

Clearing the queue does not delete the sent record

Clearing the sent notices removes queue rows only. What was sent stays in the activity logs, so no history is lost. Confusing the two leads to treating data as gone when it is not.

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.