Customer-side Ticket Hooks
The eight hooks over what a customer does: opening, replying, closing and rating.
Overview
Everything a customer can do on the support side lives here: opening a ticket, replying, closing, reopening and rating.
The difficulty of this branch is the question who did it. A sub-user may act on somebody else’s account and a guest may belong to no account at all, so the hooks carry the account and the login separately.
Reference
Following a customer opening a ticket
Runs when a ticket is opened by a customer or through email.
Hook::add('action:ticket.opened_by_client', 10,
function ($ticketAfter, $message, $clientId, $replyId) {
// It arrives as ZERO for an unrecognised sender.
if (!$clientId) { Acme::queueIdentify($ticketAfter); return; }
Acme::classify((int) ($ticketAfter['id'] ?? 0), $message);
});Stopping a customer reply
Runs before a customer replies to a ticket. Replies from the panel, from email and from a guest link all pass here.
Hook::add('gate:ticket.reply_by_client', 10,
function ($ticket, $message, $ownerId, $loginId, $isGuest) {
// For a guest both ids arrive as ZERO.
if ($isGuest && Acme::guestRepliesClosed()) return 'Guest replies are closed.';
return null;
});Following a customer reply
Runs after a customer replies to a ticket.
Hook::add('action:ticket.reply_added_by_client', 10,
function ($ticketAfter, $message, $clientId, $replyId) {
Acme::restartSlaClock((int) ($ticketAfter['id'] ?? 0));
});Following a customer closing a ticket
Runs when a customer closes their own ticket.
Hook::add('action:ticket.closed_by_client', 10, function ($ticket, $ownerId, $loginId) {
// The LOGIN id is the one that belongs in the audit.
Acme::audit('ticket-closed', $loginId, $ownerId);
});Following a customer reopening a ticket
Runs when a customer reopens a closed ticket.
Hook::add('action:ticket.reopened_by_client', 10, function ($ticket, $ownerId, $loginId) {
Acme::restartSlaClock((int) ($ticket['id'] ?? 0));
});Following a rating
Runs when a customer rates a ticket or a single staff reply. Two separate scopes land on the same hook.
ticket or reply. Check this first: the two scopes fill their parameters differently.Hook::add('action:ticket.rated', 10,
function ($scope, $rating, $ticketId, $replyId, $ownerId) {
// Check the scope first: the reply id is ZERO in the ticket scope.
if ($scope === 'reply') { Acme::scoreAgent($replyId, $rating); return; }
Acme::scoreTicket($ticketId, $rating);
});Following a guest viewing
Runs when somebody not signed in views a ticket through a link.
Hook::add('action:ticket.guest.viewed', 10, function ($ticket, $token) {
// Write the access value NOWHERE.
Acme::countGuestView((int) ($ticket['id'] ?? 0));
});Changing a customer message
Runs before the text a customer wrote is saved. New tickets and replies both pass here.
Hook::add('filter:ticket.client_message', 10, function (&$message, $ctx) {
// On a new ticket the ticket id is ZERO.
$message = Acme::stripSecrets($message);
});Pitfalls
The closing, reopening and reply hooks carry two ids: the account the ticket belongs to and the login that actually did it. When a sub-user works on somebody else’s account they differ. The login belongs in the audit record; the permission decision follows the account.
The ticket data holds the sender’s name, email and access value. Whoever sees that value can open the ticket: writing it into your records, your logs or an outside service copies the link to a third place.
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.