Skip to content

Extend the Znuny REST API with Custom Generic Interface Operations

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.

How a Znuny custom operation fits together

Section titled “How a Znuny custom operation fits together”
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:

  1. Perl operation — receives mapped request data, authenticates, calls Znuny kernel APIs, and returns a stable result.
  2. SysConfig XML — registers the controller and operation so it appears in the admin interface.
  3. Webservice YAML — associates the operation type with a REST path and HTTP methods.
  4. Znuny package — installs every file and imports or upgrades the webservice configuration.

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.

Step 1 — Implement the operation backend

Section titled “Step 1 — Implement the operation backend”
Class: Kernel::GenericInterface::Operation::<Controller>::<Name>
File: Kernel/GenericInterface/Operation/<Controller>/<Name>.pm
Type: <Controller>::<Name>

Generic Interface operation classes inherit from Kernel::GenericInterface::Operation::Common. It provides authentication and structured errors.

Kernel/GenericInterface/Operation/TicketAICatalog/Base.pm
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.

Kernel/GenericInterface/Operation/TicketAICatalog/QueueList.pm
package 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.

Step 2 — Register the module in Znuny SysConfig

Section titled “Step 2 — Register the module in Znuny SysConfig”

Znuny looks for settings named:

GenericInterface::Operation::Module###<Controller>::<Name>

Use the Znuny/OTRS-compatible <otrs_config> root:

Kernel/Config/Files/XML/OpenTicketAIConnector.xml
<?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
- POST

The 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.

Step 4 — Build the Znuny package lifecycle

Section titled “Step 4 — Build the Znuny package lifecycle”

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-list
Terminal window
curl -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.

  1. Add the operation module with authentication, validation, and a stable Data contract.
  2. Register the type in an <otrs_config> SysConfig XML file.
  3. Add matching provider-operation and route keys to the webservice YAML.
  4. Add every file to the <otrs_package> manifest and declare tested Framework versions.
  5. Import or update the webservice during package install and upgrade.
  6. Restrict the API user to required groups, queues, and actions.
  7. Test the package and operation on every supported Znuny release.
  8. Confirm the operation in Admin → Web Services and smoke-test via HTTPS.
  • Operation is absent: Check SysConfig XML, Controller, Name, package file list, and configuration rebuild.
  • Route returns 404: The YAML was not imported or route and operation keys differ.
  • Authentication fails: Basic Auth credentials do not match the mapped API login.
  • Operation returns no data: Check Valid flags and group/queue permissions.
  • Package works only on one release: Review the manifest Framework tags and test kernel API compatibility.
  • Upgrade misses new routes: Ensure setup loads the complete packaged YAML and calls 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.

Frequently asked questions

How can I expose custom REST endpoints in Znuny using the Generic Interface?

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

What are the essential components and file structure for a custom Znuny Generic Interface operation?

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

What are the best practices for securing custom Znuny REST API operations?

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

How are custom Generic Interface operations registered and routed in Znuny?

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

What should be considered when testing a custom Znuny REST API endpoint?

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

What are common troubleshooting steps for custom Znuny Generic Interface operations?

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

Can Softoft assist with developing custom Znuny API extensions?

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