Need a custom Znuny REST endpoint?
Softoft develops secure Generic Interface operations, Znuny packages, automated tests, deployment, and maintainable API contracts.
In this guide: Add secure custom REST operations to the Znuny Generic Interface, with the OpenTicketAIConnector catalogue operations as a real-world package example.
Related: Znuny REST API · Web Services · Plugin development
Znuny’s Generic Interface includes standard ticket operations, but integrations often need data or actions those operations do not expose: queue details, Dynamic Field option lists, configuration catalogues, or application-specific commands. A maintainable Znuny REST API extension combines Perl code, SysConfig registration, REST routing, package lifecycle management, and permissions.
Need a custom Znuny REST endpoint?
Softoft develops secure Generic Interface operations, Znuny packages, automated tests, deployment, and maintainable API contracts.
Book a 15-minute introductory call to discuss the connected system and required operations.
flowchart LR client[IntegrationClient] --> transport["Generic Interface REST Transport"] transport --> mapping[WebserviceRoute] mapping --> operation[CustomPerlOperation] operation --> znunyCore["Znuny Kernel System APIs"] operation --> json[StableJSONContract]
Four artifacts must agree:
The examples are taken from OpenTicketAIConnector (connector-code/): controller TicketAICatalog, SysConfig in OpenTicketAIConnector.xml, and Kernel::System::OpenTicketAIConnector::ZnunySetup. Catalogue operations cover data that standard ticket operations do not expose well. Rename controller and webservice for your own package.
Class: Kernel::GenericInterface::Operation::<Controller>::<Name>File: Kernel/GenericInterface/Operation/<Controller>/<Name>.pmType: <Controller>::<Name>Generic Interface operation classes inherit from Kernel::GenericInterface::Operation::Common. It provides authentication and structured errors.
package Kernel::GenericInterface::Operation::TicketAICatalog::Base;
use strict;use warnings;
use Kernel::System::VariableCheck qw(IsHashRefWithData IsArrayRefWithData);use parent qw(Kernel::GenericInterface::Operation::Common);
our $ObjectManagerDisabled = 1;
sub new { my ( $Type, %Param ) = @_;
my $Self = {}; bless( $Self, $Type );
for my $Needed (qw(DebuggerObject WebserviceID)) { if ( !$Param{$Needed} ) { return { Success => 0, ErrorMessage => "Got no $Needed!", }; } $Self->{$Needed} = $Param{$Needed}; }
return $Self;}
sub _AuthOrError { my ( $Self, %Param ) = @_;
my ( $UserID, $UserType ) = $Self->Auth(%Param); if ( !$UserID ) { return ( undef, $Self->ReturnError( ErrorCode => 'TicketAICatalog.AuthFail', ErrorMessage => 'Authentication failed!', ), ); } return ( $UserID, undef );}
sub _IsDropdownFieldType { my ( $Self, $FieldType ) = @_;
return 0 if !$FieldType; return 1 if $FieldType eq 'Dropdown'; return 1 if $FieldType eq 'Multiselect'; return 1 if $FieldType eq 'Einzelauswahl'; return 1 if $FieldType eq 'Mehrfachauswahl'; return 0;}
1;$ObjectManagerDisabled = 1 is required for these operation classes. Use a shared base only for behavior genuinely shared by multiple endpoints.
Runpackage Kernel::GenericInterface::Operation::TicketAICatalog::QueueList;
use strict;use warnings;
use parent qw(Kernel::GenericInterface::Operation::TicketAICatalog::Base);
our $ObjectManagerDisabled = 1;
sub Run { my ( $Self, %Param ) = @_;
my ( $UserID, $Error ) = $Self->_AuthOrError(%Param); return $Error if $Error;
my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue'); my %Queues = $QueueObject->QueueList( Valid => 0 );
my @Items; for my $QueueID ( sort { $a <=> $b } keys %Queues ) { my %Queue = $QueueObject->QueueGet( ID => $QueueID ); next if !%Queue; push @Items, { ID => $QueueID + 0, Name => $Queue{Name} // $Queues{$QueueID}, Comment => $Queue{Comment} // '', Valid => ( ( $Queue{ValidID} // 1 ) == 1 ) ? 1 : 0, }; }
return { Success => 1, Data => { Item => \@Items, }, };}
1;Mapped request fields are available through $Param{Data}. Validate required fields before calling a kernel API. The connector’s DynamicFieldValues operation does that for dropdown fields:
# Kernel/GenericInterface/Operation/TicketAICatalog/DynamicFieldValues.pm (excerpt)my $Name = $Data->{Name} // $Data->{DynamicFieldName} // '';if ( !$Name ) { return $Self->ReturnError( ErrorCode => 'TicketAICatalog.MissingName', ErrorMessage => 'Dynamic field Name is required.', );}
my $FieldConfig = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldGet( Name => $Name );if ( !IsHashRefWithData($FieldConfig) ) { return $Self->ReturnError( ErrorCode => 'TicketAICatalog.NotFound', ErrorMessage => "Dynamic field '$Name' not found.", );}Read-only catalogue operations are safest. For mutations, enforce type and value checks, make repeated requests idempotent where possible, and use explicit routes.
Znuny looks for settings named:
GenericInterface::Operation::Module###<Controller>::<Name>Use the Znuny/OTRS-compatible <otrs_config> root:
<?xml version="1.0" encoding="utf-8"?><otrs_config version="2.0" init="Application"> <Setting Name="GenericInterface::Operation::Module###TicketAICatalog::QueueList" Required="0" Valid="1"> <Description Translatable="1">OTAI catalogue: list queues.</Description> <Navigation>GenericInterface::Operation::ModuleRegistration</Navigation> <Value> <Hash> <Item Key="Name">QueueList</Item> <Item Key="Controller">TicketAICatalog</Item> <Item Key="ConfigDialog">AdminGenericInterfaceOperationDefault</Item> </Hash> </Value> </Setting></otrs_config>After package installation and configuration rebuild, verify that TicketAICatalog::QueueList is selectable in Admin → Web Services.
Declare the operation and route in the packaged webservice YAML:
Provider: Operation: queue-list: Type: TicketAICatalog::QueueList Description: Lists queues with ID, name, comment, and validity. MappingInbound: Type: Simple Config: KeyMapDefault: MapTo: '' MapType: Keep ValueMap: UserLogin: ValueMapRegEx: .*: custom-api-user MappingOutbound: Type: Simple Config: KeyMapDefault: MapTo: '' MapType: Keep
Transport: Type: HTTP::REST Config: MaxLength: '1000000' RouteOperationMapping: queue-list: Route: /queue-list RequestMethod: - GET - POSTThe queue-list keys must match. Use a dedicated API agent with minimum group and queue permissions. The login rewrite is an additional restriction, not a replacement for HTTPS, strong credentials, network controls, and input validation.
Znuny package manifests use an <otrs_package> root. Declare Framework versions that match the Znuny releases you actually test and support. Include every backend, XML, YAML, and setup file:
<Filelist> <File Permission="644" Location="Kernel/GenericInterface/Operation/TicketAICatalog/Base.pm"/> <File Permission="644" Location="Kernel/GenericInterface/Operation/TicketAICatalog/QueueList.pm"/> <File Permission="644" Location="Kernel/Config/Files/XML/MyZnunyConnector.xml"/> <File Permission="644" Location="var/webservices/MyZnunyConnector.yml"/></Filelist>Install, reinstall, and upgrade hooks should call a Znuny-specific setup module:
<CodeInstall Type="post"><![CDATA[ $Kernel::OM->Get('Kernel::System::OpenTicketAIConnector::ZnunySetup')->Install();]]></CodeInstall><CodeReinstall Type="post"><![CDATA[ $Kernel::OM->Get('Kernel::System::OpenTicketAIConnector::ZnunySetup')->Install();]]></CodeReinstall><CodeUpgrade Type="post"><![CDATA[ $Kernel::OM->Get('Kernel::System::OpenTicketAIConnector::ZnunySetup')->Install();]]></CodeUpgrade><CodeUninstall Type="pre"><![CDATA[ $Kernel::OM->Get('Kernel::System::OpenTicketAIConnector::ZnunySetup')->Uninstall();]]></CodeUninstall>Znuny setup reads SysConfig (OpenTicketAIConnector::AgentPassword must be set before install), then creates the API agent and webservice:
# Kernel/System/OpenTicketAIConnector/ZnunySetup.pm (excerpt)sub Install { my ( $Self, %Param ) = @_;
my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); my $LogObject = $Kernel::OM->Get('Kernel::System::Log');
my $Password = $ConfigObject->Get('OpenTicketAIConnector::AgentPassword') || ''; if ( !$Password ) { $LogObject->Log( Priority => 'error', Message => 'OpenTicketAIConnector: set OpenTicketAIConnector::AgentPassword in System Configuration before install.', ); return; }
my $UserLogin = $ConfigObject->Get('OpenTicketAIConnector::AgentUserLogin') || 'open_ticket_ai'; my $Email = $ConfigObject->Get('OpenTicketAIConnector::AgentEmail') || 'ai@otai.local'; my $WebServiceName = $ConfigObject->Get('OpenTicketAIConnector::WebServiceName') || 'OpenTicketAI'; my $GroupsSetting = $ConfigObject->Get('OpenTicketAIConnector::AgentGroups') || 'admin;users;stats;ai-agent'; my @Groups = grep { length $_ } split /;/, $GroupsSetting;
my $UserOK = $Self->_EnsureAgentUser( UserLogin => $UserLogin, Password => $Password, Email => $Email, Groups => \@Groups, );
my $WebServiceOK = $Self->_EnsureWebService( WebServiceName => $WebServiceName, ); ...}On package upgrades, update the existing webservice instead of creating a duplicate.
The installation path can be /znuny/ or /otrs/; use the path configured in your environment:
https://helpdesk.example/znuny/nph-genericinterface.pl/Webservice/MyZnunyConnector/queue-listcurl -sS -u 'custom-api-user:API_PASSWORD' \ -X POST \ 'https://helpdesk.example/znuny/nph-genericinterface.pl/Webservice/MyZnunyConnector/queue-list'Test the package on every claimed Znuny Framework version. Cover valid responses, authentication failure, insufficient queue permissions, invalid input, empty data, response size, reinstall, upgrade, and uninstall.
Data contract.<otrs_config> SysConfig XML file.<otrs_package> manifest and declare tested Framework versions.Controller, Name, package file list, and configuration rebuild.Valid flags and group/queue permissions.WebserviceUpdate.A durable extension needs more than a Perl proof of concept: contract design, least-privilege access, package compatibility, automated tests, upgrade handling, and operational documentation.
Let Softoft build your Znuny Generic Interface extension
Custom operation design, Perl implementation, webservice configuration, packaging, deployment, testing, and ongoing maintenance.
Book a 15-minute call to review the endpoints, Znuny version, and connected application.
Znuny automation & custom integrations
Integrate Znuny seamlessly with your IT landscape: We implement web services, business process workflows, and REST integrations.
To expose custom REST endpoints in Znuny, you must integrate four key artifacts. First, implement a Perl operation that defines the backend logic, inherits from Kernel::GenericInterface::Operation::Common, and handles authentication and data processing. Second, register this operation in SysConfig XML using the GenericInterface::Operation::Module###<Controller>::<Name> setting, ensuring it appears in the admin interface. Third, configure Webservice YAML to associate your operation type with a specific REST path and HTTP methods, enabling routing. Finally, bundle all these files and configurations into a Znuny package for proper installation, upgrade, and lifecycle management. This structured approach ensures maintainability and security for your custom API extensions.Quellen / Sources:- Znuny REST API- Web Services- System architecture — Znuny documentation
A custom Znuny Generic Interface operation follows a specific class and file naming convention: Kernel::GenericInterface::Operation::<Controller>::<Name> for the class and Kernel/GenericInterface/Operation/<Controller>/<Name>.pm for the file. The operation class must inherit from Kernel::GenericInterface::Operation::Common to leverage built-in authentication and structured error handling. It requires $ObjectManagerDisabled = 1. Key methods include new for object initialization, which validates essential parameters like DebuggerObject and WebserviceID. The core logic resides in the Run method, where mapped request data ($Param{Data}) is processed, kernel APIs are called, and a stable JSON result is returned. For authentication, the _AuthOrError helper method is typically used.Quellen / Sources:- Plugin development- System architecture — Znuny documentation
Securing custom Znuny REST API operations involves several best practices. Always implement robust authentication using methods like _AuthOrError within your operation's Perl backend to ensure only authorized users can access the endpoint. Crucially, validate all incoming request fields available through $Param{Data} before calling any kernel APIs, preventing injection or unexpected behavior. Read-only catalogue operations are generally the safest, as they do not alter system state. For operations that involve data mutations, enforce strict type and value checks on all input. Where possible, design repeated requests to be idempotent, meaning they produce the same result regardless of how many times they are executed. Finally, use explicit routes to clearly define the scope and intent of each mutable operation.Quellen / Sources:- Znuny REST API- Web Services
Custom Generic Interface operations are registered and routed through a two-step configuration process. First, the operation module must be registered in Znuny's SysConfig. This is done via an XML file (e.g., OpenTicketAIConnector.xml) that uses the <otrs_config> root and defines a setting named GenericInterface::Operation::Module###<Controller>::<Name>. This registration makes the operation visible and available within the Znuny administration interface. Second, to make the operation accessible via REST, a Webservice YAML file is used. This YAML configuration associates the registered operation type with a specific REST path and defines the allowed HTTP methods (e.g., GET, POST). When the YAML is imported, the Generic Interface REST Transport can then correctly map incoming HTTP requests to your custom Perl operation.Quellen / Sources:- GenericTicketConnectorREST - Znuny Documentation- Web Services
Thorough testing of a custom Znuny REST API endpoint is crucial. It is essential to test the package on every claimed Znuny Framework version to ensure compatibility. When calling and testing the route, be aware that the installation path can be /znuny/ or /otrs/; always use the path configured in your specific environment. Your test cases should cover a wide range of scenarios, including: valid responses for expected inputs, authentication failures when credentials are incorrect or missing, insufficient queue permissions for specific actions, invalid input data, cases where empty data is returned, and the impact of response size. Additionally, test the behavior during reinstallation and upgrade processes to ensure smooth transitions and data integrity.Quellen / Sources:- Znuny REST API- Znuny Add-ons, Plugins & Extensions | Znuny Community Docs
Troubleshooting custom Znuny Generic Interface operations often involves checking several configuration points. If the operation is absent in the admin interface or not recognized, verify the SysConfig XML for correct Controller and Name settings, ensure it's included in the package file list, and rebuild the configuration. If the route returns a 404 error, confirm that the Webservice YAML was successfully imported and that the route and operation keys in the YAML precisely match the registered operation. For authentication failures, ensure that the Basic Auth credentials used by the client accurately match the mapped API login configured in Znuny. If the operation returns no data, debug the Perl backend's Run method to check for issues in data retrieval or processing, including any conditional logic that might prevent data from being returned.Quellen / Sources:- Web Services- Create a note-internal using Generic Interface
Yes, Softoft provides comprehensive services for developing and implementing custom Znuny API extensions. Our expertise covers the entire lifecycle of a custom Generic Interface operation, starting from the initial operation design to ensure it meets your integration needs. We handle the Perl development for the backend logic, ensuring it's efficient, secure, and adheres to Znuny's architecture. This includes webservice configuration (SysConfig XML and Webservice YAML), robust packaging into a Znuny OPM file, thorough automated testing, seamless deployment, and ongoing maintenance to ensure the API contract remains stable and functional over time. We aim to deliver maintainable and secure API solutions tailored to your specific requirements.Quellen / Sources:- Need a custom Znuny REST endpoint?- Book a 15-minute introductory call