From e76b38460657d18f63fc300b8429f245b5ccf281 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Wed, 29 Jul 2026 01:53:18 -0400 Subject: [PATCH] Add ticket reply API endpoints GET /api/v1/ticket_replies/read.php and POST create.php, so an RMM or monitoring system can append to a ticket it did not open. Replies default to Internal so an integration cannot email a client by omitting a parameter. Public replies mark first response, notify the contact and watchers, and fire the same custom actions as the agent reply handler. An optional ticket_status also sets the status, and resolves the ticket and marks the resolution SLA when set to 4. Replies are always joined to tickets so ticket_client_id is checked against the key user's client scope, and the client_id named on a write must match the ticket's own client. The reply is attributed to the user the API key runs as. read.php resolves ticket_reply_by_name, since that column holds a user_id on Internal and Public replies but a contact_id on Client ones. --- api/v1/enforce_api_rbac.php | 1 + api/v1/ticket_replies/create.php | 187 +++++++++++++++++++ api/v1/ticket_replies/read.php | 96 ++++++++++ api/v1/ticket_replies/ticket_reply_model.php | 45 +++++ 4 files changed, 329 insertions(+) create mode 100644 api/v1/ticket_replies/create.php create mode 100644 api/v1/ticket_replies/read.php create mode 100644 api/v1/ticket_replies/ticket_reply_model.php diff --git a/api/v1/enforce_api_rbac.php b/api/v1/enforce_api_rbac.php index 08811f0b..c36ac9e3 100644 --- a/api/v1/enforce_api_rbac.php +++ b/api/v1/enforce_api_rbac.php @@ -124,6 +124,7 @@ $resource_module = [ 'networks' => 'module_support', 'software' => 'module_support', 'tickets' => 'module_support', + 'ticket_replies' => 'module_support', 'technicians' => 'module_support', 'clients' => 'module_client', 'contacts' => 'module_client', diff --git a/api/v1/ticket_replies/create.php b/api/v1/ticket_replies/create.php new file mode 100644 index 00000000..e40c3358 --- /dev/null +++ b/api/v1/ticket_replies/create.php @@ -0,0 +1,187 @@ +##- Please type your reply above this line -##

Hello $contact_name,

Your ticket regarding $ticket_subject has been updated.

--------------------------------
$reply
--------------------------------

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Status: $ticket_status_name
Portal: View ticket

--
$company_name - Support
$from_email
$company_phone"; + + $data = []; + + // Email ticket contact + if (filter_var($contact_email, FILTER_VALIDATE_EMAIL)) { + $data[] = [ + 'from' => $from_email, + 'from_name' => $from_name, + 'recipient' => $contact_email, + 'recipient_name' => $contact_name, + 'subject' => $subject, + 'body' => $body + ]; + } + + // Also email all the watchers + $watcher_body = $body . "

----------------------------------------
YOU ARE A COLLABORATOR ON THIS TICKET"; + $sql_watchers = mysqli_query($mysqli, "SELECT watcher_name, watcher_email FROM ticket_watchers WHERE watcher_ticket_id = $ticket_id"); + while ($watcher_row = mysqli_fetch_assoc($sql_watchers)) { + $watcher_name = escapeSql($watcher_row['watcher_name']); + $watcher_email = escapeSql($watcher_row['watcher_email']); + + if (filter_var($watcher_email, FILTER_VALIDATE_EMAIL)) { + $data[] = [ + 'from' => $from_email, + 'from_name' => $from_name, + 'recipient' => $watcher_email, + 'recipient_name' => $watcher_name, + 'subject' => $subject, + 'body' => $watcher_body + ]; + } + } + + if (!empty($data)) { + addToMailQueue($data); + } + + } + + } + + } + +} + +// Output +require_once '../create_output.php'; diff --git a/api/v1/ticket_replies/read.php b/api/v1/ticket_replies/read.php new file mode 100644 index 00000000..1819d997 --- /dev/null +++ b/api/v1/ticket_replies/read.php @@ -0,0 +1,96 @@ +