Skip to content

Internally extending the OTOBO core system

In this article, you will learn how to customize OTOBO directly in the core – via XML configuration, Perl modules, and templates. We will show you step-by-step how to integrate your own “HelloWorld” module into the system.


All customizations are located below your OTOBO clone in the Kernel/ directory:

Kernel/
├─ Config/Files/ # XML registrations
│ └─ XML/
├─ System/ # Business logic modules (Core)
├─ Modules/ # Frontend controllers (Agent/Customer)
├─ Output/HTML/Standard/ # Template Toolkit (TT) templates
└─ Language/ # Translations

New modules and routes are registered via XML. Create a file HelloWorld.xml in Kernel/Config/Files/XML/:

<?xml version="1.0" encoding="UTF-8"?>
<otobo_config version="2.0" init="Application">
<!-- 1. Register frontend module -->
<Setting Name="Frontend::Module###AgentHelloWorld" Required="1" Valid="1">
<Navigation>Frontend::Agent::ModuleRegistration</Navigation>
<Value>
<Item ValueType="FrontendRegistration">
<Hash>
<Item Key="Group"><Array><Item>users</Item></Array></Item>
<Item Key="Description" Translatable="1">HelloWorld module</Item>
<Item Key="Title" Translatable="1">HelloWorld</Item>
<Item Key="NavBarName">HelloWorld</Item>
</Hash>
</Item>
</Value>
</Setting>
</otobo_config>

Create your logic in Kernel/System/HelloWorld.pm:

package Kernel::System::HelloWorld;
use strict;
use warnings;
our @ObjectDependencies = ();
sub new {
my ($Type, %Param) = @_;
return bless {}, $Type;
}
sub GetHelloWorldText {
my ($Self, %Param) = @_;
return $Self->_FormatText(String => 'Hello World');
}
sub _FormatText {
my ($Self, %Param) = @_;
return uc $Param{String};
}
1;

In Kernel/Modules/AgentHelloWorld.pm, you integrate your logic into the Agent frontend:

package Kernel::Modules::AgentHelloWorld;
use strict;
use warnings;
sub new { bless {}, shift }
sub Run {
my ($Self, %Param) = @_;
my $HelloObj = $Kernel::OM->Get('Kernel::System::HelloWorld');
my $LayoutObj = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my %Data;
$Data{Text} = $HelloObj->GetHelloWorldText();
return
$LayoutObj->Header(Title => 'HelloWorld')
. $LayoutObj->NavigationBar()
. $LayoutObj->Output(
TemplateFile => 'AgentHelloWorld',
Data => \%Data,
)
. $LayoutObj->Footer();
}
1;

Create the following template in Kernel/Output/HTML/Standard/AgentHelloWorld.tt:

[% Data.Text %]
<p>This is your self-created HelloWorld module!</p>

  1. Reload:

    Terminal window
    bin/otobo.Console.pl Maint::Config::Rebuild
  2. Clear cache:

    Terminal window
    bin/otobo.Console.pl Maint::Cache::Delete
  3. Open browser: Agent interface → Menu → “HelloWorld”


  • Declare ObjectDependencies cleanly (e.g., DB, Layout).
  • Don’t forget POD documentation in Perl modules.
  • Maintain translations under Kernel/Language/en_*.pm.
  • Set up unit tests with Mojolicious (optional).
  • After every change, perform a Config rebuild & Clear cache.

With this, you have a solid template for implementing further core extensions in OTOBO. Have fun developing!

Frequently asked questions

Where do OTOBO core customizations live?

Customizations go under the Kernel/ directory: Config/Files/XML for registrations, System for business logic, Modules for frontend controllers, and Output/HTML/Standard for templates.

How are new OTOBO modules registered?

New modules and routes are registered via XML files in Kernel/Config/Files/XML/, for example a HelloWorld.xml frontend module setting.

What does the HelloWorld example demonstrate?

It shows how to add a core Perl module, register it in XML, and render it with a Template Toolkit template in the agent interface.