Staff Reply Hooks
The nine 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.
There is one more question, separate from the content: whose name goes on the message. The author hook settles that before the message is written.
Reference
Changing the author name and signature
Changes the author name the customer sees on a staff message, and the signature attached to it, before the message is written.
ticket_id (still zero on the ticket creation form), lang (the language the ticket was opened in), user_id (the ticket owner; zero for a guest ticket), staff_id, source, explicit_name, ai. source takes five values. Write paths: panel, panel-create, api. Preview paths: panel-form, panel-create-form.explicit_name is true the caller chose the author deliberately; leave the name alone.Hook::add('filter:ticket.reply_author', 10, function (&$name, &$signature, $ctx) {
// The caller named its own author: API author_name, an AI persona. Leave it alone.
if (!empty($ctx['explicit_name'])) return;
// The preview paths run on every page load: cause no side effects here.
$agent = Acme::pickAgent((int) ($ctx['ticket_id'] ?? 0), (string) ($ctx['lang'] ?? ''));
if (!$agent) return;
$name = $agent['name'];
$signature = $agent['signature'];
});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 signature box on the reply form takes its value from this hook. The hook then runs again when the reply is written. That has two consequences. Your listener also runs on every page load, so it must not bump a counter or write a record there. And if you set the signature unconditionally, the box is a preview rather than an input: an edit the operator makes in it is overwritten at write time.
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
Дякуємо за відгук!
Наша служба підтримки на зв’язку цілодобово з усього, чого ви не знайшли вище.