Staff Reply Hooks
The eight hooks over staff replies, internal notes and assignment.
Overview
Every point where staff touch a ticket lives here: writing a reply, editing it, deleting it, adding an internal note and assigning.
There are three reply hooks and they should not be confused: the one staff write, the one a customer writes and the one a scheduled task writes. The last carries its parameters in a single array.
Reference
Stopping a staff reply
Runs before a staff reply is sent.
Hook::add('gate:ticket.reply', 10, function ($ticket, $message, $admin_id) {
// The signature is already in: allow for it when measuring length.
if (Acme::containsSecret($message)) return 'The reply holds a value that must not be shared.';
return null;
});Following a staff reply
Runs after a staff reply is added.
Hook::add('action:ticket.reply_added', 10, function ($ticket, $reply_id, $reply) {
Acme::stopSlaClock((int) ($ticket['id'] ?? 0));
});Following an automatic reply
Runs when a scheduled task adds a reply to a ticket. Unlike its siblings the parameters arrive in a single array.
Hook::add('action:ticket.replied', 10, function ($context) {
// One array: different from its siblings.
$ticket = $context['request'] ?? [];
Acme::noteAutoReply((int) ($ticket['id'] ?? 0));
});Following a reply being edited
Runs after a reply is edited.
Hook::add('action:ticket.reply_updated', 10, function ($ticketId, $replyId, $reply) {
// The previous version is not passed.
Acme::reindexReply($replyId, $reply['message'] ?? '');
});Following a reply being deleted
Runs after a reply is deleted.
Hook::add('action:ticket.reply_deleted', 10, function ($ticketId, $replyId, $reply) {
Acme::dropFromIndex('reply', $replyId);
});Stopping an internal note
Runs before staff add an internal note. Notes are never shown to the customer.
Hook::add('gate:ticket.note_add', 10, function ($ticket, $message, $admin_id) {
if (Acme::tooLong($message)) return 'The note is too long.';
return null;
});Following an internal note
Runs after an internal note is added.
Hook::add('action:ticket.note_added', 10, function ($ticket, $admin_id, $note) {
// The note CAN be empty.
if (!$note) return;
Acme::mirrorNote((int) ($ticket['id'] ?? 0), $note['message'] ?? '');
});Following an assignment
Runs when a ticket is assigned to staff or unassigned.
Hook::add('action:ticket.assigned', 10,
function ($ticket, $old_assigned_id, $new_assigned_id) {
// Zero means it was unassigned.
if (!$new_assigned_id) { Acme::backToPool((int) ($ticket['id'] ?? 0)); return; }
Acme::notifyAgent($new_assigned_id, (int) ($ticket['id'] ?? 0));
});Pitfalls
The third parameter of the internal note hook is empty when the note could not be read. A listener reaching straight into it fails there; test on the first line.
Its siblings give the ticket, the reply id and the reply as separate parameters; the scheduled-task reply carries all of it in a single context array. A listener expecting the same signature works with empty values here.
Related Articles
- Support Ticket Hooks
- Customer Account Hooks
- How Hooks Work
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.