Zammad System Documentation

Hint

You are currently reading the Zammad system documentation. There are also administrator and user manuals available.

Zammad

Do you receive many emails and want to answer them with a team of agents?

You’re going to love Zammad!

Zammad is a web based open source helpdesk/customer support system with many features to manage customer communication via several channels like telephone, facebook, twitter, chat and emails. It is distributed under version 3 of the GNU AFFERO General Public License (GNU AGPLv3).

The code is open source, and available on GitHub!

Software

1. Client requirements

Please note that, while Zammad being a web application, there’s some requirements for your clients. This ensures that Zammad works as expected.

1.1. Supported Browsers

Zammad/Browser version compatibility

Browser

Remarks

Firefox 78+

(ESR)

(Google) Chrome 83+

This also applies for all Chromium based Browsers like Microsoft Edge

Opera 69+

(based on Chromium 83)

Microsoft Internet Explorer 11

⚠️ Deprecated, will be removed with Zammad 7

Safari 11

Danger

⚠️ Deprecation warning ⚠️

Zammad 7 will no longer support Internet Explorer 11 environments. Users using IE will be forced to use a different browser.

Please note that Zammad heavily uses Javascript which makes it a hard requirement. Some browser addons that hook into page content may interfere with Zammads function which is not a bug. For example the Google Chrome translation module is known to do odd things, especially to state names. Use Zammads internal translations instead.

1.2. Network requirements

Zammad uses web sockets. Some application firewalls may filter these connections. This may lead to decreased browser performance.

There’s a fallback to Ajax which causes a higher application server load and thus should be avoided.

In case you’re having issues with field selection, you can activate the AJAX Mode for “Core Workflows” separately.

2. Server requirements

If you want to install Zammad, you need the following software.

Note

Most of the software versions listed below (unless stated as specific version) are minimum requirements of Zammad. We strongly encourage you to use most current possible versions that are not end of life.

2.1. Ruby Programming Language

Hint

🤓 Only relevant for source code installations

Docker and package installations provide the required ruby dependencies!

Zammad requires Ruby. All required rubygems like ruby on rails are listed in the Gemfile.
The following Ruby version is supported:
Ruby 3.1.3
Zammad/Ruby version compatibility

Zammad

Ruby

5.4+

3.1.3

5.2 - 5.3

3.0.4

5.0 - 5.1

2.7.4

3.4.1 - 4.1

2.6.6

3.4.0

2.6.5

3.1 - 3.3

2.5.5

2.5 - 3.0

2.4.4

2.2. Supported distributions

Zammad provides binary packages for the most recent two stable / long-term-support releases of the supported Linux distributions, until they reach their end-of-life or until they can no longer provide the technical requirements for Zammad. Using of the latest supported stable / long-term-support version is generally recommended.

Below you can find all distributions Zammad provides packages for.

Supported operating system matrix

Distribution

Versions

CentOS / RHEL

8

Debian

11 & 12

OpenSuSE / SLES

Leap 15.x / 15

Ubuntu

20.04 & 22.04

Warning

⚠️ SuSE Tumbleweed does not meet Zammad requirements and thus is not supported!

Note

🤓 What about my specific distribution?! It’s so cool!

If you distribution is not listed, you can still install Zammad. For this you can either use Docker-Compose or Source installation.

We try to provide all current distributions that are supported by Packager.io. This means that we can’t always provide support for your favorite system.

2.3. Package Dependencies

The below dependencies need to be installed on your system. If you’re using the package install, the packages below will automatically installed with the Zammad-Package.

# Debian & Ubuntu
$ apt install libimlib2

# openSUSE
$ zypper install imlib2

# CentOS
$ yum install epel-release
$ yum install imlib2

Note

libimlib2-dev or imlib2-devel are no longer required.
However: If you have to use bundle install for e.g. custom Gems or development, you’ll need to install it!

2.4. Database Server

Zammad will store all content in a Database. You can choose between the following database servers:

  • PostgreSQL 10+

  • MySQL 5.7+ / MariaDB 10.3+ (⚠️ deprecated with Zammad 7.0+)

Danger

Support for MySQL/MariaDB will be dropped in Zammad 7.0 upwards. Make sure to migrate your existing instance of Zammad to PostgreSQL.

For MySQL/MariaDB, the following configuration is required:

  • Use UTF-8 encoding - utf8mb4 for example will fail!

  • Set max_allowed_packet to a value larger than the default of 4 MB (64 MB+ recommended).

You may also want to consider the following settings for your MySQL server:

innodb_file_format = Barracuda
innodb_file_per_table = on
innodb_default_row_format = dynamic
innodb_large_prefix = 1
innodb_file_format_max = Barracuda

2.5. Node.js

Node.js is required for asset compiling.

Package installations come pre-bundled with the correct NodeJS version. A manual installation is not required unless you require NodeJS for other projects.

Node.js is only required on source code installations if you need to change any javascript or stylesheet files via rake assets:precompile.

Zammad/Node.js version compatibility

Zammad

Node.js

6.2+

18.0+

5.2 - 6.1

16.0+

5.0 - 5.1

10.0+

2.6. Reverse Proxy

In a typical web environment today, you use a reverse proxy to deliver the static content of your application. Only the “expensive” app required HTTP requests are forwarded to the application server.

The following reverse proxies are supported:

  • Nginx 1.3+

  • Apache 2.2+

Hint

Some configuration is required, please see Configure the webserver.

2.7 Redis

Starting with Zammad 6.0, Redis is required for realtime communication via web socket.

The installation and configuration is out of our scope. Please follow the official vendor guides and ensure to have a tight security on your installation.

2.8. Elasticsearch (optional)

Zammad uses Elasticsearch to

  1. make the search faster

  2. support advanced features like reports

  3. search for content of email attachments

This becomes increasingly important the higher the number of tickets in your system gets.

Warning

This dependency is optional but strongly recommended!

Zammad will work without it, but search performance will be degraded and the search will be very limited. We recommend using Elasticsearch, as it will boost the usage of Zammad greatly!

Hint

📦 If you install Zammad via package manager…

It’s perfectly safe to manually override the Elasticsearch dependency. The appropriate command line flag will depend on your platform (e.g., --force, --ignore-depends, --skip-broken); check your package manager’s manpage to find out.

Starting with Zammad 4.0 you can decide if you want to use elasticsearch or elasticsearch-oss. Please note that CentOS requires elasticsearch.

Zammad/Elasticsearch version compatibility

Zammad

Elasticsearch

5.2+

>= 7.8, < 9

5.0 - 5.1

>= 7.8, < 8

4.0-4.1

>= 6.5, <= 7.12

3.4-3.6

>= 5.5, <= 7.9

3.3

>= 2.4, <=7.6

3.2

>= 2.4, <=7.5

3.1

>= 2.4, <=7.4

2.0-3.0

>= 2.4, <=5.6

An Elasticsearch plugin is required to index the contents of email attachments: ingest-attachment.

2.9. Optional tools of improved caching and distribution

These features / integrations below are optional. You should consider using them to have a better performance or if you want to use the corresponding feature.

You can also have a look in our 🎛️ Performance Tuning section.

2.9.1 Memcached

Instead of storing Zammads cache files within your filesystem, you can also do so in Memcached. This can allow you to restrict the size of your cache directories to improve performance.

The installation and configuration is out of our scope. Please follow the official vendor guides and ensure to have a tight security on your installation.

2.10 GnuPG (optional)

If you want to use the PGP integration for sending and receiving signed and encrypted emails, you need to install the GnuPG-Tool. Please have a look at the official GnuPG website.

Hardware

You can run Zammad on bare metal or on a virtual machine. Choose what you prefer.

For Zammad and a database server like PostgreSQL we recommend at least:

  • 2 CPU cores

  • 4 GB of RAM (+4 GB if you want to run Elasticsearch on the same server)

For optimal performance up to 40 agents:

  • 4 CPU cores

  • 6 GB of RAM (+6 GB if you want to run Elasticsearch on the same server)

Of course at the end it depends on actual load of concurrent agents and data traffic.

Note

We can’t suggest any disk space recommendations, as this highly depends on how you work. Zammad will always try to recognize the same attachments and store it just once.

Performance Tuning

As the number of active users on your system grows, performance will eventually degrade, leading to:

  • delays for outgoing email,

  • long loading times when viewing or creating tickets,

  • stale or out-of-sync search results, or

  • stale or out-of-sync ticket overviews.

You may see modest improvements by setting certain environment variables for Performance Tuning, such as $WEB_CONCURRENCY or $ZAMMAD_SESSION_JOBS_CONCURRENT.

Install from package

Note

Please ensure to meet Zammads Software requirements beforehand.
This page expects administrative permissions, this is why sudo is not used.

Prerequisites

Additional software dependencies

In addition to already mentioned Package dependencies, some operating systems may require additional packages if not already installed.

$ apt install curl apt-transport-https gnupg

Setup Elasticsearch

Elasticsearch is a dependency of Zammad and needs to be provided before installing Zammad. Please take a look at the following page: Set up Elasticsearch.

Ensure correct locale

For Zammad to function correctly, your system has to use the correct locales.

List your current locale settings.

$ locale |grep "LANG="

If above does not return <lang_code>.utf8 you can correct this issue as follows.

$ apt install locales
$ locale-gen en_US.UTF-8
$ echo "LANG=en_US.UTF-8" > /etc/default/locale

Add Repository and install Zammad

Hint

Packager.io may not be accessible from IPv6-only environments, so make sure to consider this when performing the steps below.

Add Repository
Install Repository Key
$ curl -fsSL https://dl.packager.io/srv/zammad/zammad/key | \
  gpg --dearmor | tee /etc/apt/trusted.gpg.d/pkgr-zammad.gpg> /dev/null
Ubuntu 20.04
$ echo "deb [signed-by=/etc/apt/trusted.gpg.d/pkgr-zammad.gpg] https://dl.packager.io/srv/deb/zammad/zammad/stable/ubuntu 20.04 main"| \
   tee /etc/apt/sources.list.d/zammad.list > /dev/null
Ubuntu 22.04
$ echo "deb [signed-by=/etc/apt/trusted.gpg.d/pkgr-zammad.gpg] https://dl.packager.io/srv/deb/zammad/zammad/stable/ubuntu 22.04 main"| \
   tee /etc/apt/sources.list.d/zammad.list > /dev/null
Install Zammad
$ apt update
$ apt install zammad

Firewall & SELinux

Some parts of these steps may not apply to you, feel free to skip them!

SELinux

$ # Allow nginx or apache to access public files of Zammad and communicate
$ chcon -Rv --type=httpd_sys_content_t /opt/zammad/public/
$ setsebool httpd_can_network_connect on -P
$ semanage fcontext -a -t httpd_sys_content_t /opt/zammad/public/
$ restorecon -Rv /opt/zammad/public/
$ chmod -R a+r /opt/zammad/public/

Firewall

Note

Below only covers the distribution’s default firewall. It may not cover your case.

$ # Open Port 80 and 443 on your Firewall
$ ufw allow 80
$ ufw allow 443
$ ufw reload

Manage services of Zammad

In general Zammad uses three services - these can be (re)started & stopped with the parent zammad.

$ # Zammad service to start all services at once
$ systemctl (status|start|stop|restart) zammad

$ # Zammads internal puma server (relevant for displaying the web app)
$ systemctl (status|start|stop|restart) zammad-web

$ # Zammads background worker - relevant for all delayed- and background jobs
$ systemctl (status|start|stop|restart) zammad-worker

$ # Zammads websocket server for session related information
$ systemctl (status|start|stop|restart) zammad-websocket

Next steps

With this Zammad technically is ready to go. However, you’ll need to follow the following further steps to access Zammads Web-UI and getting started with it.

If you expect usage with 5 agents or more you may also want to consider the following pages.

Install from source

The source installation is the most difficult installation type of Zammad. If you’re not too experienced with Linux and all that, you may want to use another installation type:

Note

Please note that we only use sudo after direct user changes. In all other situations you can expect root being in charge.

Hint

🔎 Looking for MacOS hints? You can find the developer documentation here.

Prerequisites

Software dependencies

Please ensure that you already provided mentioned Software requirements.

Add user

$ useradd zammad -m -d /opt/zammad -s /bin/bash
$ groupadd zammad

Installation

Step 1: Get the source

Note

Not all distributions ship wget and tar by default, you may need to install it manually.

Get the latest stable release of Zammad here. This file will be updated whenever new bug-fixes are applied, so you can update from this URL regularly.

$ cd /opt
$ wget https://ftp.zammad.com/zammad-latest.tar.gz
$ tar -xzf zammad-latest.tar.gz --strip-components 1 -C zammad
$ chown -R zammad:zammad zammad/
$ rm -f zammad-latest.tar.gz

Step 2: Install dependencies

Note

Please have a look at Configure the webserver for detailed instructions.

Zammad requires specific ruby versions. Adapt the commands below if you install older versions. A list of required versions can be found on the Software requirements page.

$ apt update
$ apt install postgresql postgresql-contrib
$ systemctl start postgresql
$ systemctl enable postgresql
Install Node.js
# see https://github.com/nodesource/distributions#debian-and-ubuntu-based-distributions
# for detailed installation instructions.

$ apt install -y ca-certificates curl gnupg
$ mkdir -p /etc/apt/keyrings
$ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
$ NODE_MAJOR=20
  echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
$ apt update
$ apt install nodejs -y
Install RVM
$ apt install curl git patch build-essential bison zlib1g-dev libssl-dev libxml2-dev libxml2-dev autotools-dev\
  libxslt1-dev libyaml-0-2 autoconf automake libreadline-dev libyaml-dev libtool libgmp-dev libgdbm-dev libncurses5-dev\
  pkg-config libffi-dev libimlib2-dev gawk software-properties-common

$ apt-add-repository -y ppa:rael-gc/rvm
$ apt update
$ apt install rvm
Set relevant Environment variables
# Set rails environment specific things
$ echo "export RAILS_ENV=production" >> /opt/zammad/.bashrc
$ echo "export RAILS_SERVE_STATIC_FILES=true" >> /opt/zammad/.bashrc
$ echo "rvm --default use 3.1.3" >> /opt/zammad/.bashrc

# Debian, CentOS & OpenSuSE
$ echo "source /usr/local/rvm/scripts/rvm" >> /opt/zammad/.bashrc
# Ubuntu
$ echo "source /usr/share/rvm/scripts/rvm" >> /opt/zammad/.bashrc
Install Ruby Environment
# Add zammad user to RVM group
$ usermod -a -G rvm zammad

# Install Ruby 3.1.3
$ su - zammad
$ rvm install ruby-3.1.3

# Install bundler, rake and rails
$ rvm use ruby-3.1.3
$ gem install bundler rake rails
After installing bundler, rake and rails we’ll need to install all required gems.
The command depends on the database server you are using.
Install PostgreSQL Dependencies
$ apt install libpq-dev
Install Gems for Zammad
$ su - zammad
$ bundle config set without "test development mysql"
$ bundle install

Step 3: Configure database settings

Tip

🤓 For easiest usage …

If you provide your Zammad user with database creation permission, you can run db:create in the following section. If you don’t want that, you’ll have to create the database manually.

$ cp config/database/database.yml config/database.yml
$ vi config/database.yml

Here’s a sample configuration to give you an idea on how your configuration file could look like. Please also have a look at Configure Database server for deeper details.

production:
   adapter: postgresql
   database: zammad
   pool: 50
   encoding: utf8
   username: zammad
   password: changeme # You can remove this line if you enable socket based authentication!

Hint

If you want to use an existing database server that’s not on the same machine, you can also use host and port to set that up.

For security reasons, ensure that your database configuration is readable for the Zammad user only.

$ chmod 600 /opt/zammad/config/database.yml
$ chown zammad:zammad /opt/zammad/config/database.yml

Step 4: Initialize your database

Warning

Ensure to do this as zammad user in your Zammad directory!

Tip

🤓 Avoid a restart …

You can set the base URL of your Zammad installation by setting the ZAMMAD_HTTP_TYPE and ZAMMAD_FQDN environment variables before initializing the database (see below).

# Example for a base URL of https://zammad.example.com
$ su - zammad
$ export ZAMMAD_HTTP_TYPE=https
$ export ZAMMAD_FQDN=zammad.example.com
$ su - zammad
$ rake db:create      # SKIP IF you already created zammads database (see tip of step 3)
$ rake db:migrate
$ rake db:seed
# Synchronize translations
$ rails r "Locale.sync"
$ rails r "Translation.sync"

Step 5: Pre compile all Zammad assets

$ rake assets:precompile

Step 6: Start Zammad or install as service

Note

Run the following commands as root.

You can start all services by hand or use systemd to start / stop Zammad.

$ cd /opt/zammad/script/systemd
$ ./install-zammad-systemd-services.sh

Manage services of Zammad

In general Zammad uses three services - these can be (re)started & stopped with the parent zammad.

$ # Zammad service to start all services at once
$ systemctl (status|start|stop|restart) zammad

$ # Zammads internal puma server (relevant for displaying the web app)
$ systemctl (status|start|stop|restart) zammad-web

$ # Zammads background worker - relevant for all delayed- and background jobs
$ systemctl (status|start|stop|restart) zammad-worker

$ # Zammads websocket server for session related information
$ systemctl (status|start|stop|restart) zammad-websocket

Firewall & SELinux

Some parts of these steps may not apply to you, feel free to skip them!

SELinux

$ # Allow nginx or apache to access public files of Zammad and communicate
$ chcon -Rv --type=httpd_sys_content_t /opt/zammad/public/
$ setsebool httpd_can_network_connect on -P
$ semanage fcontext -a -t httpd_sys_content_t /opt/zammad/public/
$ restorecon -Rv /opt/zammad/public/
$ chmod -R a+r /opt/zammad/public/

Firewall

Note

Below only covers the distribution’s default firewall. It may not cover your case.

$ # Open Port 80 and 443 on your Firewall
$ ufw allow 80
$ ufw allow 443
$ ufw reload

Next steps

With this Zammad technically is ready to go. However, you’ll need to follow the following further steps to access Zammads Web-UI and getting started with it.

If you expect usage with 5 agents or more you may also want to consider the following pages.

Set up Elasticsearch

Zammad’s search function is powered by Elasticsearch, and requires the ingest attachment plugin.

This guide uses the zammad run command prefix in command line examples. This prefix is only applicable to package installations (i.e., via apt/yum/zypper, or .deb/.rpm files).

If you installed from source, be sure to omit this prefix and run the bare rails ... or rake ... commands instead.

Step 1: Installation

Starting with Zammad 4.0, our packages allow you to decide whether to use elasticsearch or elasticsearch-oss.

elasticsearch-oss users please use below “direct download” tab for further installation steps.

Warning

Above does not apply to CentOS because of compatibility reasons.

$ apt install apt-transport-https sudo wget curl gnupg
$ echo "deb [signed-by=/etc/apt/trusted.gpg.d/elasticsearch.gpg] https://artifacts.elastic.co/packages/7.x/apt stable main"| \
  tee -a /etc/apt/sources.list.d/elastic-7.x.list > /dev/null
$ curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | \
  gpg --dearmor | tee /etc/apt/trusted.gpg.d/elasticsearch.gpg> /dev/null
$ apt update
$ apt install elasticsearch
$ /usr/share/elasticsearch/bin/elasticsearch-plugin install ingest-attachment

After you installed Elasticsearch and its attachment plugin, ensure to enable it by default and start it.

$ systemctl start elasticsearch
$ systemctl enable elasticsearch

Note

🐋 Docker installations on macOS/Windows:

Setting the vm.max_map_count kernel parameter requires additional steps.

Step 2: Suggested Configuration

We use the following settings to optimize the performance of our Elasticsearch servers. Your mileage may vary.

# /etc/elasticsearch/elasticsearch.yml

# Tickets above this size (articles + attachments + metadata)
# may fail to be properly indexed (Default: 100mb).
#
# When Zammad sends tickets to Elasticsearch for indexing,
# it bundles together all the data on each individual ticket
# and issues a single HTTP request for it.
# Payloads exceeding this threshold will be truncated.
#
# Performance may suffer if it is set too high.
http.max_content_length: 400mb

# Allows the engine to generate larger (more complex) search queries.
# Elasticsearch will raise an error or deprecation notice if this value is too low,
# but setting it too high can overload system resources (Default: 1024).
#
# Available in version 6.6+ only.
indices.query.bool.max_clause_count: 2000

Note

For more information on the indices.query.bool.max_clause_count setting, see the Elasticsearch 6.6 release notes.

Step 3: Connect Zammad

Before proceeding here, make sure to install Zammad before running below commands, as this will fail otherwise.

# Set the Elasticsearch server address
$ zammad run rails r "Setting.set('es_url', 'http://localhost:9200')"

# Build the search index
$ zammad run rake zammad:searchindex:rebuild

# Optionally, you can specify a number of CPU cores which are used for
# rebuilding the searchindex, as in the following example with 8 cores:
$ zammad run rake zammad:searchindex:rebuild[8]

Starting with Elasticsearch 8+, you need to use a HTTPS URL in ‘es_url’ as ‘https://localhost:9200’ and configure an authentication (see HTTP Basic below).

Optional settings

# HTTP Basic
$ zammad run rails r "Setting.set('es_user', '<username>')"
$ zammad run rails r "Setting.set('es_password', '<password>')"

Hint

🤔 How do I set up authentication on my Elasticsearch server?

Elasticsearch provides many different authentication methods. Some of them may require paid X-Pack, please check the elastic documentation for more information.

Appendix

List of Indexed Attributes

Below is a comprehensive list of all object attributes indexed by Elasticsearch. In other words, if you wish to find a ticket, article, or user via the Zammad search box, Elasticsearch can match on any (or all) of the fields below.

Note

These fields may vary if you created custom fields (objects) in the admin interface.

Hint

Below you can find some hints:

  • (SLA): Attributes marked as SLA attribute are only set if the ticket is affected by SLA calculation. Please note that some attributes may not be set if specific conditions are not met.

    Also note that some attributes may be reset to null if no longer applicable.

  • note attribute: Note attributes usually are empty if not specified via console or API.

  • Timestamps: All timestamps provided by Zammad are UTC by default. This also applies to times provided by Elasticsearch

Ticket

Tip

🤓 The following indice contains below mentioned information: *_ticket

Ticket-Index

Field

Sample Value

Description

article

#{Article Array}

Array with all articles belonging to the ticket
Please see Article for more details

article_count

1

Number of articles within the ticket

close_at

null, 2021-03-03T14:50:20.673Z

First close time, set once

close_diff_in_min

null, 239, -5

Depends on close_in_min and tells how many minutes the ticket was closed relative to SLAs solution time. (SLA)

close_escalation_at

null, 2021-03-03T15:50:20.673Z

Time stamp when the ticket would escalate in case solution time is violated. (SLA)

close_in_min

null, 11

Value in minutes for how long the ticket was open based on business hours. (SLA)

create_article_sender

Contains these attributes:
  • note: null

  • updated_at: 2021-03-03T14:50:20.812Z

  • name: Customer

  • created_at: 2021-03-03T14:50:20.812Z

  • updated_by_id: 1

  • id: 2

  • created_by_id: 1

Sender of the article (System, Agent, Customer)

create_article_sender_id

1, 2

ID of the user that created the article

create_article_type

Contains these attributes:
  • note: null

  • updated_at: 2021-03-03T14:50:20.812Z

  • name: phone, email, web

  • active: true

  • created_at: 2021-03-03T14:50:20.812Z

  • updated_by_id: 1

  • id: 5

  • created_by_id: 1

  • communication: true, false

Information of first article type and nature

create_article_type_id

5

Type ID of first article

created_at

2021-03-24T16:17:27.210Z

Time stamp of ticket creation

created_by

#{user object}

Complete Payload of user that created the ticket
Please see User for more

created_by_id

3

User ID that created the ticket

customer

#{user object}

Complete payload of the customer that created the ticket
Please see User for more

customer_id

8

Customers User ID

escalation_at

null, 2021-03-24T16:28:38.535Z

Time stamp of the next applicable escalation. One of the following attributes:

  • close_escalation_at

  • first_response_escalation_at

  • update_escalation_at

(SLA)

first_response_at

null, 2021-03-24T16:28:38.303Z

Time stamp of the first communication type reaction to the customer (SLA)

first_response_diff_in_min

null, 10, -6

Depends on first_response_in_min and tells how many minutes the tickets first response took relative to the first response time of your SLA. (SLA)

first_response_in_min

null, 11

Value in minutes for how long the first response took based on the business hours. (SLA)

group

#{group object}

Complete payload of the current tickets group
Please see Group for more

group_id

1

ID of the current group

id

1, 111

ID of the Ticket

last_contact_agent_at

null, 2021-03-24T16:28:38.303Z

Time stamp of last communication type contact of any agent

last_contact_at

null, 2021-03-24T16:28:38.303Z

Time stamp of last communication type contact
Depends on last_contact_agent_at, last_contact_customer_at and “Ticket Last Contact Behaviour” setting

last_contact_customer_at

null, 2021-03-24T16:28:38.303Z

Time stamp of last communication type contact of customer

mention_user_ids

[3, 5]

Array with mentioned or subscribed users IDs

note

null

Note of ticket, only set via console or API

number

1010138, 202006231010138

Ticket number

organization

null, #{organization object}

Complete Payload of user that owns the ticket
Please see Organization for more

organization_id

null, 2

ID of the customers organization

owner

null, #{user object}

Complete Payload of user that owns the ticket
Please see User for more

owner_id

null, 3

User ID of the ticket owner

pending_time

null, 2021-03-24T17:44:06.912Z

Depends on pending states, time stamp for pending time

preferences

n/a, special information for internal functions

May not be available in your system, contains information for internal system functions

priority

#{priority object}

Complete Payload of priority of ticket
Please see Ticket Priority for more

priority_id

2

Priority ID of the ticket

state

#{state object}

Complete Payload of current ticket state
Please see Ticket State for more

state_id

1, 4

ID of current ticket state

tags

["order", "americano"]

Array with all attached tags

time_unit

null, 15

Accounted time units for ticket (total)

title

Feedback Form, Need help

Title / Subject of Ticket

type

null

Ticket type (deprecated)

update_diff_in_min

null, 2021-03-24T16:28:38.303Z

Depends on update_in_min and tells how many minutes the last ticket update took relatively to the update time setting (SLA)

update_escalation_at

null, 2021-03-24T16:28:38.303Z

Time stamp when the ticket would escalate in case update time is violated. (SLA)

update_in_min

null, 5, -10

Value in minutes for how long the last ticket update took based on the business hours and update time. (SLA)

updated_at

2021-03-24T16:28:38.303Z

Last ticket update

updated_by

#{user object}

Complete Payload of the user that updated the ticket
Please see User for more

updated_by_id

1, 3

User ID that updated the ticket

Ticket Priority

Tip

🤓 The following indice contains below mentioned information: *_ticket_priority

Ticket Priority-Index

Field

Sample Value

Description

active

true, false

Defines if the priority is active (available)

created_at

2021-03-03T14:50:20.724Z

Creation date of priority

created_by_id

1

User that created priority

default_create

false, true

Defines if priority is default priority upon ticket creation

id

3

ID of priority

name

3 high

Priority name

note

null

Note for priority that has been set via console or API

ui_color

null, high-priority

CSS class for tickets of priority

ui_icon

null, important

CSS class for ticket icons of priority

updated_at

2021-03-03T14:50:20.724Z

Date of last change

updated_by_id

1

User ID of user last updating the priority

Ticket State

Tip

🤓 The following indice contains below mentioned information: *_ticket_state

Ticket State-Index

Field

Sample Value

Description

active

true, false

Defines if state is active (available)

created_at

2021-03-03T14:50:20.694Z

Creation date

created_by_id

1

User ID that created state

default_create

false, true

Defines if the state is the default state upon ticket creation

default_follow_up

false, true

Defines if the state is the default follow up state on ticket follow ups

id

7

State ID

ignore_escalation

false, true

Defines if SLA calculation is generally ignored for this state

name

pending close

State name

next_state

n/a, #{state object}

Contains all follow up state information if applicable, may not be available depending on the state type

next_state_id

null, 4

State ID of follow up state

note

null

Note that has been set via console or API

state_type

Contains these attributes:
  • created_at: 2021-03-03T14:50:20.582Z

  • created_by_id: 1

  • id: 4

  • name: pending action

  • note: null

  • updated_at: 2021-03-03T14:50:20.582Z

  • updated_by_id: 1

Contains all available information of the states type

state_type_id

4

ID of the state type

updated_at

2021-03-03T14:50:20.694Z

Last update of state

updated_by_id

1

User ID that updated state last

Article

Tip

🤓 The following indice contains below mentioned information: *_ticket

Note

Articles are part of the ticket index. To reduce complexity we decided to provide it in its own table. 🙏

Article-Index

Field

Sample Value

Description

body

Hi,\n\nplease send me:\n1 [...] \n75007 Paris\n\nDavid Bell

Article body in plain text

cc

null, alias@domain.tld

EMail-Addresses set as CC (String)

content_type

text/html

Content type of article

created_at

2021-03-22T03:47:59.290Z

Time stamp of article creation

created_by_id

10

User ID that created the article

from

David Bell <david@example.com>

From field of article creator

id

16

Internal article ID

in_reply_to

null

In-Reply-To Header from emails if applicable

internal

false, true

Defines if article is internal

message_id

null

Message ID of Email if applicable

origin_by_id

null

User ID or original creator if created on behalf another user

preferences

{}

Internal preferences, may be empty, mainly for delivery states

references

null

Contains message references

reply_to

null

Contains reply to header if applicable

sender_id

2

ID of sender type (Customer, System, Agent)

subject

My amazing subject

Article subject

ticket_id

9

Ticket ID the article belongs to

to

support@example.com

EMail address from TO-Header

type_id

1

ID of articles Type (phone, email, web, …)

updated_at

2021-03-22T03:47:59.290Z

Last update

updated_by_id

10

User that updated article

User

Tip

🤓 The following indice contains below mentioned information: *_user

User-Index

Field

Sample Value

Description

active

true, false

Defines if user is active

address

"", Bennelong Point\nSydney NSW 2000

Address string

city

"", Berlin

City string

country

"", Germany

Country string

created_at

2021-03-22T12:47:56.460Z

Creation date of user

created_by_id

1

User ID that created the user

department

"", IT

Department string

email

"", alias@domain.tld

EMail Address of user, if applicable

fax

"", 1234

Fax number

firstname

null, John

Users first name

id

8

Internal User ID

last_login

null, 2021-03-23T12:47:56.460Z

Updated upon every user login

lastname

null, Doe

Users last name

login

auto-1234567, jdoe

Login name, always set and unique, can differ from email

mobile

"", 1232

Mobile phone number

note

""

Note being available via web, console and API

organization

#{organization object}

Complete Payload of the organization the user is member of
Please see Organization for more

organization_id

3

ID of organization the user is member of

out_of_office

false, true

Defines if user has activated out of office function

out_of_office_end_at

null, 2021-03-26

Ending date out of office

out_of_office_replacement_id

null, 3

User ID that replaces this user during out of office period

out_of_office_start_at

null, 2021-03-24

Begin date out of office

permissions

(Array)

Array with all permissions of the user

phone

"", 0061 2 1234 7777

Phone number of user

preferences

{}, #{several preference attributes}

Depends on user and situation, may contain notification_config, locale and other internal system information

role_ids

(Array), [1, 2]

Contains array with role IDs assigned to the user

street

""

Street

updated_at

2021-03-25T00:27:52.308Z

Time stamp of last update

updated_by_id

3

User ID that updated this entry

verified

false, true

Defines if the user has verified the account

vip

false, true

Defines if user has VIP state

web

"", https://zammad.org

Web URL of User

zip

"", 10123

ZIP code

Organization

Tip

🤓 The following indice contains below mentioned information: *_organization

Organization-Index

Field

Sample Value

Description

active

true, false

Defines if organization is active

created_at

2021-03-22T12:47:54.807Z

Creation date

created_by

#{user object}

Complete Payload of the user that created the organzation
Please see User for more

created_by_id

1

User ID that created the organization

domain

null, example.com

Organizations domain

domain_assignment

false, true

Domain assignment depends on domain

id

1

Organization ID

members

#{array of user objects}

Array with complete Payload of the users being member of the organization
Please see User for more

name

Chrispresso Inc.

Organization name

note

Manufacturer of individual coffee products.

Note being available via web, console and API

shared

true, false

Defines if the organization is a sharing one

updated_at

2021-03-22T12:47:54.807Z

Last update time

updated_by

#{user object}

Complete Payload of the user that updated the organization
Please see User for more

updated_by_id

1

User ID that updated the organization

vip

true, false

Defines if the organization has VIP state

Group

Tip

🤓 The following indice contains below mentioned information: *_group

Group-Index

Field

Sample Value

Description

active

true, false

Defines if group is active (available)

assignment_timeout

null, 30

Time in minutes an agent can be inactive until the owner ship is removed

created_at

2021-03-24T23:55:06.980Z

Time stamp of group creation

created_by_id

1

User ID that created the group

email_address

Contains these attributes:
  • active: true

  • channel_id: 3

  • created_at: 2021-03-24T23:54:58.187Z

  • created_by_id: 3

  • email: alias@domain.tld

  • id: 1

  • note: null

  • realname: Zammad GmbH

  • updated_at: 2021-03-24T23:54:58.187Z

  • updated_by_id: 3

  • preferences: null

Contains all available information of the groups email address

email_address_id

3

ID of email address

follow_up_assignment

true, false

Defines if owners are still assigned after follow ups

follow_up_possible

yes, no

Defines if following up on a closed ticket is possible

id

1

Group ID

name

Users, Sales

Group name

note

null

Notes for the group available via web, console and API

signature

Contains these attributes:
  • active: true

  • body: <br>  #{user.firstname} #{user.lastname}<br>--<br>That Inc

  • created_at: 2021-03-03T14:50:19.775Z

  • created_by_id: 1

  • id: 1

  • name: default

  • note: null

  • updated_at: 2021-03-03T14:50:19.775Z

  • updated_by_id: 1

Contains all available information of the groups signature

signature_id

1

Signature ID

updated_at

2021-03-24T23:55:06.980Z

Time stamp of last group update

updated_by_id

3

User ID that updated group

CTI Log

Tip

🤓 The following indice contains below mentioned information: *_cti_log

CTI Log-Index

Field

Sample Value

Description

call_id

00006

Unique Call ID

comment

""

Optional comment

created_at

2021-03-22T11:48:01.703Z

Creation date of Call

direction

in, out

Call direction

done

true, false

Defines if call displays as “to do” within UI

duration_talking_time

27

Call duration in seconds

duration_waiting_time

77

Duration in seconds the caller was waiting for answer

end_at

2021-03-25T08:49:40.647Z

Time stamp of call end

from

493055571600

Calling number

from_comment

null, John, Doe

Display name of calling number if applicable

from_pretty

+49 30 55571600

Pretty version of from

id

8

Internal ID of entry

initialized_at

2021-03-25T08:47:56.753Z

Time stamp of call initialization, usually matches created_at

preferences

(Array)

Contains internal information if required

queue

null, 491711234567890

Queue the call was answered in

start_at

2021-03-25T08:49:13.050Z

Time stamp the call was answered

state

hangup, voicemail

Last state of call

to

491711234567890

Dialed number

to_comment

null, John, Doe

Display name of called number if applicable

to_pretty

+491711234567890

Pretty version of to

updated_at

2021-03-25T08:49:40.647Z

Last update of entry

Chat Session

Tip

🤓 The following indice contains below mentioned information: *_chat_session

Chat Session-Index

Field

Sample Value

Description

chat

Contains these attributes:
  • active: true

  • block_country: null

  • block_ip: null

  • created_at: 2021-03-03T14:50:22.607Z

  • created_by_id: 1

  • id: 1

  • max_queue: 5

  • name: default

  • note: ""

  • preferences: {}

  • public: false

  • updated_at: 2021-03-03T14:50:22.607Z

  • updated_by_id: 1

  • whitelisted_websites: null

Contains various preferences of the chat topic in charge

chat_id

1

ID of Chat topic

created_at

2021-03-25T10:26:24.376Z

Time stamp of chat creation

created_by_id

null

User that created the chat, place holder, currently always null

id

1

ID of Chat Session

messages

(Array) - Array entries contain these attributes:
  • chat_session_id: 1

  • content: Hello dear customer

  • created_at: 2021-03-25T10:26:35.977Z

  • created_by_id: null, 3

  • id: 1

  • updated_at: 2021-03-25T10:26:35.977Z

Array with all messages of chat

name

null, John Doe

Name agent set for chat user, if applicable

preferences

Contains these attributes:
  • dns_name: host.domain.tld

  • geo_ip: {}

  • participants: Array, ["47118371175780", "47118371850300"]

  • remote_ip: 192.168.2.19

  • url: https://zammad.com/en/company/contact

Various internal Meta data of the session_id

session_id

92f2909631f1ad5ff4d5d1e046952be8

Unique Session ID

state

closed

Current state of chat session

tags

(Array), ["order"]

Tags applied to Chat Session by agent, if applicable

updated_at

2021-03-25T10:27:03.341Z

Last update

updated_by_id

null, 3

User ID that last updated session, may be null

user

#{user object}

Complete Payload of the chat agemt
Please see User for more

user_id

3

User ID of chat agent

Install with Docker Compose

Warning

We currently do not support Docker environments in productive use. It’s no problem if you run Zammad on docker, however, support is only provided for Zammad as application!

Docker Compose environments require deeper system know how. If you’re not too familiar with Docker and the way it works, you may want to stick with the package installation instead.

Docker is a container-based software framework for automating deployment of applications. Compose is a tool for defining and running multi-container Docker applications.

Zammads docker images are hosted on Dockerhub.

Hint

By default, docker compose will use a fixed Zammad version like 6.2.0-1, which refers to a specific commit. In this scenario, you are responsible to apply updates by updating the version on your own.

Alternatively, you can also use floating versions that will give you automatic updates via docker compose pull:

# VERSION=6.2     # all patchlevel updates
# VERSION=6       # including minor updates
# VERSION=latest  # all updates of stable versions, including major
# VERSION=develop # bleeding-edge development version (not recommended for production use)

Before you start, make sure to have at least 4 GB of RAM to run the containers.

Install Docker Environment

This documentation expects you already have a working Docker Compose environment. You can find the required documentations for these steps below:

Getting started with zammad-docker-compose

Docker Compose Environment Variables

Zammad’s Docker Compose supports several environment variables that are not set by default. The best way to provide these is within the file .env.

In case our default docker-compose.yml is not good enough, please use docker-compose.override.yml to provide own changes.

Note

Unless stated otherwise, below environment variables count for the whole Zammad stack and not single containers. Below grouping is to help you find them better, but do not reflect their container.

Docker Compose

Variable

Default Value

Description

Additional Info

RESTART

always

By default containers will be restarted in case they stopped for whatever reason.

VERSION

This variables contains the version tag. Example: 3.6.0-20

We update this string from time to time, Docker Hub may contain more current tags to the moment you’re pulling.

Zammad

Variable

Default Value

Description

Additional Info

AUTOWIZARD_JSON

''

This variable allows you to provide initial configuration data for your instance. Autowizard JSON is out of scope of this documentation, however this example file should help.

Tip

This variable is specific to the init container.

ZAMMAD_WEB_CONCURRENCY

(unset)

Allows spawning n workers to allow more simultaneous connections for Zammads web UI. Please also note Configuration via Environment Variables in this regard.

Tip

This variable is specific to the railsserver container.

ZAMMAD_SESSION_JOBS
_CONCURRENT

(unset)

Allows spawning n session job workers to release pressure from Zammads background worker. Please also note Configuration via Environment Variables in this regard.

Tip

This variable is specific to the scheduler container.

ZAMMAD_PROCESS_SCHEDULED
_JOBS_WORKERS

(unset)

Allows spawning 1 independent scheduled job worker to release pressure from Zammads background worker. Please also note Configuration via Environment Variables in this regard.

Tip

This variable is specific to the scheduler container.

ZAMMAD_PROCESS_DELAYED
_JOBS_WORKERS

(unset)

Allows spawning n delayed job workers to release pressure from Zammads background worker. Please also note Configuration via Environment Variables in this regard.

Tip

This variable is specific to the scheduler container.

RAILS_TRUSTED_PROXIES

['127.0.0.1', '::1']

By default Zammad trusts localhost proxies only.

Tip

This variable is specific to the init container.

Danger

⚠ Only change this option if you know what you’re doing! ⚠

Elasticsearch

Variable

Default Value

Description

Additional Info

ELASTICSEARCH_ENABLED

true

Setting this variable to false will allow you to run your Zammad without Elasticsearch. Please note that we strongly advise against doing so.

Tip

This variable is specific to the init container.

ELASTICSEARCH_HOST

zammad-elasticsearch

Provide a host name or address to your external Elasticsearch cluster.

Tip

This variable is specific to the init container.

ELASTICSEARCH_PORT

9200

Provide a different port for Elasticsearch if needed.

Tip

This variable is specific to the init container.

ELASTICSEARCH_SCHEMA

http

By default Elasticsearch is reachable via HTTP.

Tip

This variable is specific to the init container.

ELASTICSEARCH_NAMESPACE

zammad

With this name space all Zammad related indexes will be created. Change this if you’re using external clusters.

Tip

This variable is specific to the init container.

ELASTICSEARCH_REINDEX

true

By default the docker-compose will always re-index upon a restart. On big installations this may be troublesome.

Warning

Disabling this setting requires you to re-index your search index manually whenever that’s needed by upgrading to a new Zammad version!

Tip

This variable is specific to the init container.

ELASTICSEARCH_SSL_VERIFY

true

Allows you to let the compose scripts ignore self signed SSL certificates for your Elasticsearch installation if needed.

Tip

This variable is specific to the init container.

Memcached

Variable

Default Value

Description

Additional Info

MEMCACHE_SERVERS

zammad-memcached:11211

Provide your own Memcached instance if you already have one existing.

Warning

Was MEMCACHED_HOST before 5.0.x!

Redis

Variable

Default Value

Description

Additional Info

REDIS_URL

redis://zammad-redis:6379

Provide your own Redis instance if you already have one.

Warning

This method currently does not allow authentication.

NGINX

Variable

Default Value

Description

Additional Info

NGINX_PORT

8080

The port Nginx will listen on.

Tip

This variable is specific to the nginx container.

NGINX_SERVER_NAME

_

By default the Nginx container of Zammad will respond to all request. You can provide your IP / FQDN if you want to.

Tip

This variable is specific to the nginx container.

NGINX_SERVER_SCHEME

\$scheme

If the Nginx container for Zammad is not the upstream server (aka you’re using another proxy in front of nginx) $scheme may be wrong. You can set the correct scheme http or https if needed.

Tip

This variable is specific to the nginx container.

ZAMMAD_RAILSSERVER_HOST

zammad-railsserver

Host name of the rails server container.

ZAMMAD_RAILSSERVER_PORT

3000

Port of Zammads rails server.

Please also note Configuration via Environment Variables in this regard.

ZAMMAD_WEBSOCKET_HOST

zammad-websocket

Host name of Zammads websocket server.

Tip

This variable is specific to the nginx container.

ZAMMAD_WEBSOCKET_PORT

6042

Port of Zammads websocket server.

Please also note Configuration via Environment Variables in this regard.

Tip

😖 Can’t login because of CSRF token errors?

This usually affects systems with more than one proxy server only. For this to function you may have to tell your web server directly which connection type was used.

Warning

Do not use below options if you’re unsure, they may technically be a security issue!

The following options expect HTTPs connections which should be your goal.

Within your virtual host configuration, locate both directives proxy_set_header X-Forwarded-Proto and replace $scheme by https.

PostgreSQL

Variable

Default Value

Description

Additional Info

POSTGRESQL_HOST

zammad-postgresql

Host name of your PostgreSQL server. Use your own if you already have one.

POSTGRESQL_PORT

5432

Adjust the Port of your PostgreSQL server.

POSTGRESQL_USER

zammad

The database user for Zammad.

POSTGRESQL_PASS

zammad

The password of Zammads database user.

POSTGRESQL_DB

zammad_production

Zammads database to use.

POSTGRESQL_OPTIONS

(unset)

Additional postgresql params to be appended to the database URI.

POSTGRESQL_DB_CREATE

true

By default we will create the required database.

Note

On own database servers this setting might be troublesome.

Tip

This variable is specific to the init container.

Step 1: Clone GitHub repo

Warning

If you’re updating Zammad, below commands will cause values set in .env and docker-compose.override.yml to be lost. You’re expected to check if the docker-compose.yml has changed and if so to adjust it accordingly.

$ git clone https://github.com/zammad/zammad-docker-compose.git
$ cd zammad-docker-compose

Hint

If cloning is too much of a hassle, you can also download the files from https://github.com/zammad/zammad-docker-compose/releases. This will make sure file permissions are preserved.

Step 2: Setting vm.max_map_count for Elasticsearch

Even with running Elasticsearch in a container, you’re required to adjust your host’s settings to ensure a clean runtime.

$ sysctl -w vm.max_map_count=262144

Step 3: Adjust Environment as needed

In some cases our default environment is not what a docker-compose user is looking for. To remove complexity from this page, we outsourced information on this topic.

See Docker Compose Environment Variables

Step 4: Start Zammad using DockerHub images

Warning

Before starting your containers ensure to not use default login data for your Zammad database! See Step 3!

$ docker compose up -d

Hint

🔧 How to run rails/rake commands in containers

The docker entrypoint script sets up environment variables required by Zammad to function properly. That is why calling rails / rake on the console should be done via one of the following methods:

$ docker compose run --rm zammad-railsserver rails r '...your rails command here...'

This will run the command via the docker entrypoint and is recommended. In case you require the use of docker exec, you can use the following command:

$ docker exec zammad-docker-compose-zammad-railsserver-1 /docker-entrypoint.sh rails r '...your rails command here...'

This will manually invoke the docker entrypoint and pass the desired command to it for execution in the proper environment.

Next steps

With this Zammad technically is ready to go. However, you’ll need to follow the following further steps to access Zammads Web-UI and getting started with it.

If you expect usage with 5 agents or more you may also want to consider the following pages.

Install on Kubernetes via Helm

Warning

We currently do not support Kubernetes installations in productive use.

Kubernetes (k8s) is an open-source system for automating deployment, scaling, and management of containerized applications.

For maintainability reasons, this documentation does not cover the installation instructions because these are included in our helm chart anyway. You can find the helm chart in the zammad-helm repository here.

Updating Zammad

Note

🙈 Better safe than sorry

Before updating to a new version, please have a look into the release notes. These will provide further information on new feature and fixes, but also technical remarks that may be relevant during an upgrade!

🤓 What about Zammad upgrade paths…?

In general we do not encourage you to skip Zammad versions or have long update cycles. Zammad potentially stores very sensitive information (personal information) which is why updating is very important.

If you don’t have time for updating all the time (nobody got time for that, right?), please consider using Zammad hosting for your and your customers’ safety.

In case you couldn’t update for a longer time, please ensure to at least update from major to major version. Big version jumps may work but usually go terribly wrong. As example, expecting the current stable version of Zammad being 5.1 and your instance being on Zammad 2.4, your path would look like so: 2.43.04.05.0latest stable (5.1)

Step 1: Ensure dependencies

Before proceeding, double-check that your system environment matches Zammad’s requirements.

Step 2: Stop Zammad
$ systemctl stop zammad
Step 3: Backup Zammad

See Backup and Restore for more information.

Step 4: Clear Zammad cache
$ zammad run rails r "Rails.cache.clear"
Step 5: Update Zammad
$ apt update
$ apt upgrade

Warning

The package comes with maintenance scripts that will run regular tasks during updates for you.

However
Do not run Zammad updates unattended and always have a look on the outputs these helper scripts generate. Ignoring said output may lead to incomplete updates that may corrupt data or lead to issues you find way too late.
Step 6: Run required extra steps

Extra steps needed for updates are mentioned in our release news.

Updating Elasticsearch may be relevant in this step.

Step 7: Log into Zammad

Yes, that’s it!

Updating Elasticsearch

Warning

Updating Elasticsearch does not automatically update it’s plugins! This usually isn’t an issue if Zammad is being updated right after Elasticsearch.

If you want to upgrade your elasticsearch installation, please take a look at the elasticsearch documentation as it will have the most current information for you.

If, for whatever reason, you need to rebuild your search index after upgrading, use:

$ zammad run rake zammad:searchindex:rebuild

Optionally, you can specify a number of CPU cores which are used for rebuilding the searchindex, as in the following example with 8 cores:

$ zammad run rake zammad:searchindex:rebuild[8]

Hint

🤓 Zammad 5.2 comes with changes

As of Zammad 5.2 the reindex command has changed! You will still be able to use the old method until Zammad 6, however, will receive a deprecation warning.

Warning

This step may fail if Zammad is under heavy load: Elasticsearch locks the indices from deletion if you’re pumping in new data, like receiving a new ticket. (This only applies to single-node deployments, not clusters.)

If it does, try killing Zammad first:

$ systemctl stop zammad
$ zammad run rake zammad:searchindex:rebuild
$ systemctl start zammad

Configure the webserver

You can find current sample configuration files for your webserver within contrib/ of your Zammad installation.

If you’re using the package installation, Zammad attempts to automatically install a configuration file to your nginx for you.

Note

The Zammad installation will not automatically set any host- or server name for you.

Docker Compose / Kubernetes users
Please also note the environment information on this page

Adjusting the webserver configuration

Warning

For a quick start, we’re installing a HTTP configuration. You should never use HTTP connections for authentication - instead, we encourage you to use HTTPS!

If Zammad scripts automatically installed your webserver configuration file, ensure to not rename it. Below we’ll cover HTTPs for above reason.

Step 1 - Get a current config file

Copy & overwrite the default zammad.conf by using

$ cp /opt/zammad/contrib/nginx/zammad_ssl.conf /etc/nginx/sites-available/zammad.conf

Your nginx directories may differ, please adjust your commands if needed.

Most common:

  • /etc/nginx/conf.d/

  • /etc/nginx/vhosts.d/

  • /etc/nginx/sites-available/

Step 2 - Adjust the config file

Adjust the just copied file with a text editor of your choice (e.g. vi or nano).

Locate any server_name directive and adjust example.com to the subdomain you have chosen for your Zammad instance.

Now you’ll need to adjust the path and file names for your ssl certificates your obtained on the prior steps. Adjust the following directives to match your setup:

  • ssl_certificate (your ssl certificate)

  • ssl_certificate_key (the certificates private key)

  • ssl_trusted_certificate (the public CA certificate)

Note

Technically this is not a hard requirement, but recommended!

Hint

🤓 Don’t have a dhparam.pem file yet?

You can easily adapt below example to generate this file. It will improve HTTPs security and thus should be used.

You can find the path by looking at your webserver configuration by looking for:

  • ssl_dhparam directive (nginx)

  • SSLOpenSSLConfCmd DHParameters directive (apache2)

$ openssl dhparam -out <path>/dhparam.pem 4096
(Optional) - Adjust HTTPs configuration

Our default configuration aims for a broad support of enduser devices. This may not fit your needs - Mozilla has a great ssl-config generator that should help you to meet your requirements!

Step 3 - Save & reload

Reload your nginx systemctl reload nginx to apply your configuration changes.

If you just installed Zammad, you’ll be greeted by our getting started wizard. 🙌 You now can continue with First steps.

Hint

You’re not seeing Zammads page but a default landing page of your OS?

Ensure that you did restart your webserver - also check if 000-default.conf or default.conf in your vhost directory possibly overrules your configuration.

Sometimes this is also a DNS resolving issue.

Tip

😖 Can’t login because of CSRF token errors?

This usually affects systems with more than one proxy server only. For this to function you may have to tell your web server directly which connection type was used.

Warning

Do not use below options if you’re unsure, they may technically be a security issue!

The following options expect HTTPs connections which should be your goal.

Within your virtual host configuration, locate both directives proxy_set_header X-Forwarded-Proto and replace $scheme by https.

Getting started wizard after installing Zammad

First steps

After successfully installing Zammad you’ll have a couple of options.

Getting Started Wizard

If you visit Zammad’s web page the first time, you’ll be greeted by its Getting Started Wizard. It will guide you through the first most important things.

Walkthrough of Zammads Getting Started Wizard.
Step 1: Create your very first administrator account

The fields should be fairly self explaining.

Zammad does require the following password security by default:

  • Password length of 10 or more

  • 2 upper and 2 lower characters

  • contains at least one digit

Step 2: Provide company information

You can upload a custom logo fitting to your company here. The instance address is detected automatically and only required adjustment in case it’s detected wrong.

All of these settings can be changed within Branding and System settings.

Step 3: E-Mail notification channel

By default Zammad uses sendmail - if that doesn’t fit you can change it to SMTP here.

Zammad uses noreply@<your-fqdn> as sender address by default. SMTP setups might fail - you may want to skip this step with choosing sendmail at this point. You can adjust it later!

Step 4: Your first email channel (optional)

If you want to start right away, you can connect your email account already.

Warning

Zammad reacts to fetched emails by default. If that’s not what you want, skip this step for now.

Learn more about the email channel within the documentation for email channels.

After finishing the wizard you’ll be automatically logged in to the just created account.

First time on the dashboard will provide a small clue intro

Further Steps

In our opinion the next step order would like below sample. You can skip parts you don’t need or adapt. All parts are described within Zammad’s admin documentation.

  1. Configure your required groups

  2. Adjust triggers as needed

  3. Add postmaster filters if needed

  4. Configure SLAs if needed

  5. add email / social media channels & signatures
    (go back to group settings to add outgoing email addresses)
  6. Add Text Modules

  7. Add Organizations

  8. Configure roles if needed

  9. Consider Third Party logins or LDAP integration for easier logins

  10. Add agent accounts (users)

  11. Consider backup strategies for Zammad, see Backup and Restore

From point 5 on you’ll be able to work productive in theory. 🙌

Hint

😖 Are you still lost?

If above list doesn’t help you or you’ll need to jump in a lot faster, you can also get Workshops with one of our Zammad consultants.

Migrating to Zammad

Zammad will migrate the following information:

  • Tickets and their Articles

  • Groups / Queues

  • Organizations

  • Agents and Customers (if applicable)

After migrating to Zammad you’ll want to continue with the First steps to configure Zammad. This has to be done after migration.

Limitations

There might be source dependent limitations which we will be covering on the direct migration pages.

However, these limitations count for all migrations:

  • Migrations are only possible on new instances.

  • Migrations are only possible from one sources. Several migration sources on one instance are not supported.

  • Zammad can’t migrate object types it doesn’t know, migrations will fail.

  • Zammad migrates all or nothing. This means that you can’t deselect specific information specific groups, tickets or users.

Available Migration Options

From Freshdesk

Limitations

Please note below Freshdesk specific limitations. These are additional limitations to the general ones listed.

  • Differential migrations are not supported!
    The general suggestion is to run a test import before to learn how long the migration is going to take.
  • Important: Please note that migration speed highly depends on your Freshdesk plan (API rate limits apply).

  • Due to API limitations Zammad will not show the total number of objects to import, but instead correct them in steps of 100.

  • Your Freshdesk plan has to provide API support. This may not apply to all available plans.

  • User passwords are not migrated and will require the user to use the password reset link on the login page.

Prerequisites

Zammad requires API access which is why you’ll need to create an API key for the migration. The migrator will request your Freshdesk subdomain and API key.

Warning

Ensure to retrieve the API key with a full administrator account. Less privileged users will end in a broken migration.

Importing Freshdesk data

Generally you have two options on how to migrate data. If you have a fairly big instance with a lot of data, you may want to consider using the console over the browser version.

After installing Zammad and configuring your webserver, navigate to your Zammads FQDN in your browser and follow the migration wizard.

Depending on the number of users, tickets and Freshdesk plan this may take some while.

Migration process of freshdesk via UI

Note

😖 Scheduler got interrupted

_images/scheduler-interrupted.png

If this message appears after providing your credentials, please be patient. The migration should start within 5 minutes.

If you receive above message after the migration begun, please consider using the console approach instead and reset the installation.

After migration

As the migration technically skips the getting started wizard, please note that you want to adjust your FQDN settings (FQDN & HTTP-Type).

Hint

How to log in?

Zammad provides admin access to the user whose API token you provided. Use the admins email address and API token provided during the migration to login.

All other users will have to use the password reset function or login methods like LDAP or one click logins.

After successfully migrating your Freshdesk instance, continue with First steps.

Restarting from scratch

Turned wrong at some point? You can find the required commands to reset Zammad in our Dangerzone.

From Kayako

Limitations

Please note below Kayako specific limitations. These are additional limitations to the general ones listed.

  • Differential migrations are not supported!
    The general suggestion is to run a test import before to learn how long the migration is going to take.
  • Selfhosted installations (Kayako classic) are not supported.

  • The following ticket field customizations are being ignored (affects “Scale” plan):

    • Custom ticket states,

    • Custom ticket priorities, and

    • Custom ticket types.

  • Important: Please note that migration speed highly depends on your Kayako plan (API rate limits apply).

  • Your Kayako plan has to provide API support. This may not apply to all available plans.

  • User passwords are not migrated and will require the user to use the password reset link on the login page.

Prerequisites

Zammad requires API access which is why the migrator will request your Kayako-URL, email address and password.

Warning

Ensure to provide an user account with full administrative permissions. Less privileged users will end in a broken migration.

Importing Kayako data

Generally you have two options on how to migrate data. If you have a fairly big instance with a lot of data, you may want to consider using the console over the browser version.

After installing Zammad and configuring your webserver, navigate to your Zammads FQDN in your browser and follow the migration wizard.

Depending on the number of users, tickets and Kayako plan this may take some while.

Migration process of Kayako via UI

Note

😖 Scheduler got interrupted

_images/scheduler-interrupted1.png

If this message appears after providing your credentials, please be patient. The migration should start within 5 minutes.

If you receive above message after the migration begun, please consider using the console approach instead and reset the installation.

After migration

As the migration technically skips the getting started wizard, please note that you want to adjust your FQDN settings (FQDN & HTTP-Type).

Hint

How to log in?

Zammad provides admin access to the user whose login credentials you provided. Use the admins email address and password provided during the migration to login.

All other users will have to use the password reset function or login methods like LDAP or one click logins.

After successfully migrating your Kayako instance, continue with First steps.

Restarting from scratch

Turned wrong at some point? You can find the required commands to reset Zammad in our Dangerzone.

Migration from OTRS

Limitations

Please note below OTRS specific limitations. These are additional limitations to the general ones listed.

  • Password migration works for OTRS >= 3.3 only
    (on older instances a password reset within Zammad will be required)
  • If you plan to import a differential migration after, do not change any data in Zammad!

  • Only customers of tickets are imported

  • Zammad expects your OTRS timestamps to be UTC and won’t adjust them

  • If you plan to import a differential after, do not change any data in Zammad!

Note

Supported OTRS version: 3.1 up to 6.x

Prerequisites
Step 1: Install Znuny4OTRS-Repo

This is a dependency of the OTRS migration plugin.

https://ftp.zammad.com/otrs-migrator-plugins/Znuny4OTRS-Repo-6.0.76.opm
Step 2: Install OTRS migration plugin
https://ftp.zammad.com/otrs-migrator-plugins/Znuny4OTRS-ZammadMigrator-6.0.7.opm

Hint

In some cases restarting your webserver may help to solve internal server errors.

Importing OTRS data

Note

If your OTRS installation is rather huge, you might want to consider using the command line version of this feature. This also applies if you experience Timeouts during the migration.

After installing Zammad and configuring your webserver, navigate to your Zammads FQDN in your Browser and follow the migration wizard.

Depending on the size of your OTRS installation this may take a while.

You can get an idea of this process in the migrator video on vimeo .

After successfully migrating your OTRS installation, continue with First steps.

Importing a differential

Note

This is only possible after finishing an earlier OTRS import successful.

In some cases it might be desirable to update the already imported data from OTRS. This is possible with the following commands.

Run a differential import
>> Setting.set('import_otrs_endpoint', 'http://xxx/otrs/public.pl?Action=ZammadMigrator')
>> Setting.set('import_otrs_endpoint_key', 'xxx')
>> Setting.set('import_mode', true)
>> Setting.set('system_init_done', false)
>> Import::OTRS.diff_worker
After the import has finished, run the following commands
$ Setting.set('import_mode', false)
$ Setting.set('system_init_done', true)
$ Rails.cache.clear

All changes that occurred after your first migration should now also be available within your Zammad installation.

Restarting from scratch

Turned wrong at some point? You can find the required commands to reset Zammad in our Dangerzone.

From Zendesk

Limitations

Please note below Zendesk specific limitations. These are additional limitations to the general ones listed.

  • Differential migrations are not supported!
    The general suggestion is to run a test import before to learn how long the migration is going to take.
  • Important: Please note that migration speed highly depends on your Zendesk plan (API rate limits apply).

  • Your Zendesk plan has to provide API support. This may not apply to all available plans.

  • User passwords are not migrated and will require the user to use the password reset link on the login page.

Prerequisites

Zammad requires API access which is why you’ll need to create an API key for the migration. The migrator will request your Zendesk-URL, email address and API key.

Warning

Ensure to retrieve the API key with a full administrator account. Less privileged users will end in a broken migration.

Importing Zendesk data

Generally you have two options on how to migrate data. If you have a fairly big instance with a lot of data, you may want to consider using the console over the browser version.

After installing Zammad and configuring your webserver, navigate to your Zammads FQDN in your browser and follow the migration wizard.

Depending on the number of users, tickets and Zendesk plan this may take some while.

Migration process of Zendesk via UI

Note

😖 Scheduler got interrupted

_images/scheduler-interrupted2.png

If this message appears after providing your credentials, please be patient. The migration should start within 5 minutes.

If you receive above message after the migration begun, please consider using the console approach instead and reset the installation.

After migration

As the migration technically skips the getting started wizard, please note that you want to adjust your FQDN settings (FQDN & HTTP-Type).

Hint

How to log in?

Zammad provides admin access to the user whose API token you provided. Use the admins email address and API token provided during the migration to login.

All other users will have to use the password reset function or login methods like LDAP or one click logins.

After successfully migrating your Zendesk instance, continue with First steps.

Restarting from scratch

Turned wrong at some point? You can find the required commands to reset Zammad in our Dangerzone.

Note

😖 Missing a migration source?

If we don’t cover your favorite source yet, you’ll have two options. You can either fiddle around by using Zammads powerful API or drop our sales team a message for a custom development or even migrator sponsoring.

🤓 Migrations are available for hosted setups too, contact support for further information!

Console

Zammad uses Ruby on Rails so you can make use of the rails console.

Warning

Please double check your commands before running, as some of those commands might cause data loss or damaged tickets! If you’re unsure, use a test system first!

To open the rails console on the shell you have to enter the following commands.

Start Zammad’s Rails console

Running a single command

The following command will allow you to run a single command, without running a shell (e.g. for automation).

Note

Replace {COMMAND} with your command you want to run.

Tip

If you enter a p in front of your command (e.g. like rails r 'p Delayed::Job.count'), you’ll actually receive a printed output (without you won’t!).

# package installation
$ zammad run rails r '{COMMAND}'

# source installation
$ rails r '{COMMAND}'

Running several commands in a shell

The following command will provide you a rails console. It allows you to run several commands inside it.

This reduces loading times greatly.

# package installation
$ zammad run rails c

# source installation
$ rails c

Hint

Starting Rails Console in Safe Mode

Normally, starting rails console requires certain third-party services to be up and running. You may receive errors and console will refuse to start in case they are not available.

However, it’s possible to start rails console in safe mode by setting a special environment variable. With ZAMMAD_SAFE_MODE=1 set, availability of these services will be ignored.

$ ZAMMAD_SAFE_MODE=1 zammad run rails c
Zammad is running in safe mode. Any third-party services like Redis are ignored.

There was an error trying to connect to Redis via redis://localhost:6379.
Please provide a Redis instance at localhost:6379 or set REDIS_URL to point to a different location.
#<Redis::CannotConnectError: Error connecting to Redis on localhost:6379 (Errno::ECONNREFUSED)>
Loading production environment (Rails 6.1.7.3)
3.1.3 :001 >

Working on the console

Here’s a topic list for quick jumping and better overview.

Query and set / update Zammad settings

Note

Please note that this is not a full command list, if you’re missing commands, feel free to ask over at the Community.

ticket_hook setting

This will give you the ticket hook that you’ll find inside the [] in front of the ticket number. By default this will be Ticket# - you shouldn’t change this setting in a productive system.

>> Setting.get('ticket_hook')
FQDN setting

Get the current FQDN setting of Zammad and, if needed, adjust it.

Note

This setting has no effect on SSL certificates or any web server configurations.

>> Setting.get('fqdn')                    # Get current FQDN
>> Setting.set('fqdn', 'new.domain.tld')  # Set a new FQDN
HTTP(s) setting

This setting indirectly belongs to your FQDN setting and is relevant for variable based URLs (e.g. in notifications) Zammad generated.

Warning

This setting also affects Zammad’s CSRF token behavior. If you set this setting to e.g. HTTPs but you’re using HTTP, logging in will fail!

Note

This setting has no effect on SSL certificates or any web server configurations.

>> Setting.get('http_type')            # Get the current http type
>> Setting.set('http_type', 'https')   # Change the http type to HTTPs
Storage provider setting

The storage provider setting is set to DB on default installations. However, if you receive a lot of attachments or have a fairly busy installation, using the database to store attachments is not the best approach.

Use the following command

>> Setting.get('storage_provider')        # get the current Attachment-Storage
>> Setting.set('storage_provider', 'DB')  # Change Attachment-Storage to database

The following settings are available in a default installation:

  • DB (database)

  • File (Filesystem (/opt/zammad/storage/))

Configuring Elasticsearch

If your Elasticsearch installation changes, you can use the following commands to ensure that Zammad still can access Elasticsearch.

>> Setting.set('es_url', 'http://127.0.0.1:9200')           # Change elasticsearch URL to poll
>> Setting.set('es_user', 'elasticsearch')                  # Change elasticsearch user (e.g. for authentication)
>> Setting.set('es_password', 'zammad')                     # Change the elasticsearch password for authentication
>> Setting.set('es_index', Socket.gethostname + '_zammad')  # Change the index name
>> Setting.set('es_attachment_ignore', %w[.png .jpg .jpeg .mpeg .mpg .mov .bin .exe .box .mbox])  # A list of ignored file extensions (they will not be indexed)
>> Setting.set('es_attachment_max_size_in_mb', 50)          # Limit the Attachment-Size to push to your elasticsearch index
>> Setting.set('es_ssl_verify', 'false')                    # Turn SSL verification on or off
Enable proxy

Zammad needs to use a proxy for network communication? Set it here.

>> Setting.set('proxy', 'proxy.example.com:3128')
>> Setting.set('proxy_username', 'some user')
>> Setting.set('proxy_password', 'some pass')

Advanced customization settings

On this page you can find some settings that you won’t find within the Zammad UI. Those settings might come in handy as it can change Zammad’s behavior.

Note

Please note that this is not a full command list, if you’re missing commands, feel free to ask over at the Community.

Send all outgoing E-Mails to a BCC-Mailbox

This option allows you to send all outgoing E-Mails (not notifications) to a specific mailbox. Please note that this shouldn’t be a mailbox you’re importing already! This will apply to all groups and is a global setting.

>> Setting.set('system_bcc', 'alias@domain.tld')

You can easily check the current BCC-Setting by running the following:

>> Setting.get('system_bcc')
Activate counter on grouped overviews

This is a hidden setting which you can only set via Command-Line. This will globally enable a ticket number value in each heading for grouped elements.

>> Setting.set('ui_table_group_by_show_count', true)  # enable counter on grouped overviews
>> Setting.set('ui_table_group_by_show_count', false) # disable counter on grouped overviews
>> Setting.get('ui_table_group_by_show_count')        # get current setting (`nil` is false)
_images/ui_table_group_by_show_count-example.png
Default ticket type on creation

Zammad allows you to define the default article type upon ticket creation. By default this will be a incoming phone call.

You can choose between

  • phone-in (incoming call, default),

  • phone-out (outgoing call) and

  • email-out (Sending an E-Mail out).

>> Setting.set('ui_ticket_create_default_type', 'email-out')

To check what setting is set currently, simply run:

>> Setting.get('ui_ticket_create_default_type')
Adding a warning to the ticket creation process

If in case you need to give your agent a note or warning during ticket creation, you can do so with the below command.

You can use three different warnings for

  • Incoming Calls :"phone-in"=>"",

  • Outgoing Calls :"phone-out"=>"" and

  • Outgoing E-Mails :"email-out"=>"".

>> Setting.set('ui_ticket_create_notes', {
      :"phone-in"=>"You're about to note a incoming phone call.",
      :"phone-out"=>"You're about to note an outgoing phone call.",
      :"email-out"=>"You're going to send out an E-Mail."
   })

Note

You can use those three sub-settings independently, if you e.g. don’t need a warning on incoming calls, simply leave out :"phone-in"=>"" out of the setting. The setting itself is done within an array ( {} ).

To check what’s currently set, you can use:

>> Setting.get('ui_ticket_create_notes')

Sample of the above setting:

_images/ui_ticket_create_notes.gif
Adding a warning to the article reply process

In case you need to give your agent a warning during the ticket article reply, you can do that with the command below.

You can provide different warnings for different channels and article visibility

  • Internal Notes :"note-internal"=>"",

  • Public Notes :"note-public"=>"",

  • Internal Calls :"phone-internal"=>"",

  • Public Calls :"phone-public"=>"",

  • Internal Emails :"email-internal"=>"" and

  • Public Emails :"email-public"=>"".

>> Setting.set('ui_ticket_add_article_hint', {
      :"note-internal"=>"You are writing an |internal note|, only people of your organization will see it.",
      :"note-public"=>"You are writing a |public note|.",
      :"phone-internal" => "You are writing an |internal phone note|, only people of your organization will see it.",
      :"phone-public"=>"You are writing a |public phone note|.",
      :"email-internal" => "You are writing an |internal Email|, only people of your organization will see it.",
      :"email-public"=>"You are writing a |public Email|."
   })

Note

You can use example sub-settings above independently, if you e.g. don’t need a warning on internal calls, simply leave out :"phone-internal"=>"" out of the setting. The setting itself is in a form of an array ( {} ).

To check what’s currently set, you can use:

>> Setting.get('ui_ticket_add_article_hint')

Sample of the above setting:

_images/ui_ticket_add_article_hint-example.gif
Show Email address of customer on customer selection (ticket creation)

By default Zammad will not display the E-Mail-Addresses of customers. The below option allows you to change this behavior.

>> Setting.set('ui_user_organization_selector_with_email', true)

Get the current state of this setting with:

>> Setting.get('ui_user_organization_selector_with_email')
Change font settings for outgoing HTML mails

Note

Some clients (like Outlook) might fallback to other settings while it might work for other clients.

The below setting allows you to adjust Zammad’s email font setting. This setting does not require a service restart.

>> Setting.set("html_email_css_font", "font-family:'Helvetica Neue', Helvetica, Arial, Geneva, sans-serif; font-size: 12px;")

If you want to check the current setting, you can simply run the below code.

>> Setting.get('html_email_css_font')
Highlight customer’s open ticket count

This option enhances the selected customer’s open tickets count. It highlights the count in different colors if they hit a threshold.

>> Setting.set('ui_sidebar_open_ticket_indicator_colored', true)

Sample of the above setting:

_images/ui_sidebar_open_ticket_indicator_colored.png

Above settings has specific thresholds as follows. You cannot adjust these thresholds.

Situational threshold list for open ticket indication

Situation / View

no indication

warning (orange)

danger (red)

Ticket Zoom

< 2

2

>= 3

New Ticket dialog

0

1

>= 2

Working on user information

Note

Please note that this is not a full command list, if you’re missing commands, feel free to ask over at the Community.

Find user

In order to work on user information or to check for specific information, you’ll need to find it first.

>> User.find(4)                       # We already know the ID of the user
>> User.find_by(email: 'your@email')  # Searching for the user by his Email address
>> User.find_by(login: 'john.doe')    # Searching for the user by his login
Unlock a locked user account

Tip

Unlocking a locked user account is also supported by Zammad’s web UI. Please refer to the admin documentation for more information.

It sometimes happens that a user locks himself out by wildly trying the wrong password multiple times. Depending on your maximum failing login count (default: 10 times), Zammad might lock the account.

The user can’t login any more (forever) if he doesn’t change the password or you reset the counter.

>> u=User.find(**USERID**)
>> u.login_failed=0
>> u.save!

You can also double check if the account is locked by running the following (result needs to be 1 above your limit, so 11 for the default of 10 failing logins)

>> User.find(**USERID**).login_failed
Change / Update Email address of user

If needed, you can simply change the Email address of the user.

Note

Please note that the login attribute is not affected by this and Zammad thus might show different information within the UI.

>> u = User.find(**USERID**)
>> u.email = 'user@exmaple.com'
>> u.save!

You need to find the user ID of the user first for this.

Change / Update Login name of user

Change the user name of the user (e.g. if you want to login with a shorter username instead of a mail address)

>> u = User.find(**USERID**)
>> u.login = 'user@exmaple.com'
>> u.save!

You need to find the user ID of the user first for this.

Set admin rights for user

Don’t have access to Zammad anymore? Grant yourself or another user administrative rights.

>> u = User.find_by(email: 'you@example.com')
>> u.roles = Role.where(name: ['Agent', 'Admin'])
>> u.save!
Set password for user

You or the user did forget his password? No problem! Simply reset it by hand if needed.

>> User.find_by(email: 'you@example.com').update!(password: 'your_new_password')
Remove password for user

If you added a second authentication method (e.g. LDAP) after launch, there still may be a password in Zammad’s own user management. In cases like that users will be able to login with their (local) Zammad password in addition to the credentials stored on the external authentication provider. Simply remove the password stored by Zammad.

>> User.find_by(email: 'you@example.com').update!(password: nil)

Working with ticket information

Note

Please note that this is not a full command list, if you’re missing commands, feel free to ask over at the Community.

Get the RAW mail that Zammad fetched

The following command will help you to check on received EML-files Zammad fetched. This comes in handy if you delete Mails upon fetching and you need to check the EML-file itself.

To get the first articles EML-file, you can use the following command. In our example the ticket number in question is 101234.

>> Ticket.find_by(number:'101234').articles.first.as_raw.content

If needed, you can also get the raw content of later articles (you’ll need to find the correct article though). Again, we expect 101234 to be our ticket number. In the first step we get all article IDs of the ticket, from the list we get, we can then get the articles content.

>> Ticket.find_by(number:'101234').article_ids
=> [4, 3, 2]
>> Ticket::Article.find(3).as_raw.content

Note

If you just use Ticket::Article.find(3) you can see further information (like who sent the mail, when we fetched it, …).

Update all tickets of a specific customer

Warning

Please note that this action can be expensive resource wise, if you have many tickets, this might slow down Zammad.

>> Ticket.where(customer_id: 4).update_all(customer_id: 1)
Priorities

Ticket priorities help your agent to see how important a customer request is. Priorities are not available to customers and, Core wise, have no impact on how Zammad handles a ticket. You can however adjust Zammad’s behavior with e.g. triggers, SLAs and schedulers.

Not sure what priorities are available in the system? Either have a look in any ticket or run the following command.

>> Ticket::Priority.pluck(:name)
Adding priorities for tickets

Ticket priorities come with several attributes, however, the most relevant as of now are: name, default_create and ui_color.

Warning

default_create allows you to define the default priority Zammad should use during ticket creation. However - on default installations this is the priority 2 normal.

You cannot have more than one priority as the default_create priority!

Note

ui_color defines the CSS class to use. On default installations you can either use low-priority (light blue) or high-priority (red). This affects how Zammad displays the ticket titles in overviews.

>> Ticket::Priority.create(
      name: '4 super high',
      default_create: false,
      ui_color: 'high-priority',
      created_by_id: 1,
      updated_by_id: 1
   )
Change priority

If needed you can also set priorities to inactive or rename them if they don’t fit your desired scheme. Renaming would look like so:

>> Ticket::Priority.find_by(name: '1 low').update(name: '1 high')
Get ticket state types

This will show all state types needed for creating new ticket states.

Tip

What are state types?

Zammad uses state types to know what it should do with your state. This allows you to have different types like pending actions, pending reminders or closed states.

State types also indicate the color scheme to be used. You can learn more about that in our user documentation.

If you want to add custom states, have a look in our admin documentation section.

>> Ticket::StateType.pluck(:id, :name)

Above will return both, the type ID and name - e.g.: [[1, "new"], [2, "open"], ....

Add new ticket state

Note

🤓 Missing States you just created?

You might want to use Ticket::State.pluck(:id, :name) to get a listing of all available ticket states.

Tip

🙈 ignoring escalations

You can use ignore_escalation: true, to ignore possible SLA calculations (pending reminder and pending close do this by default).

Non-Pending states

A state that’s not a pending state (e.g. open, closed). Just replace 'open' by whatever you need (like closed).

>> Ticket::State.create_or_update(
     name: 'Developing',
     state_type: Ticket::StateType.find_by(name: 'open'),
     created_by_id: 1,
     updated_by_id: 1,
   )
Pending reminders

A pending reminder state that will send a reminder notification to the agent if the time has been reached.

>> Ticket::State.create_or_update(
     name: 'pending customer feedback',
     state_type: Ticket::StateType.find_by(name: 'pending reminder'),
     ignore_escalation: true,
     created_by_id: 1,
     updated_by_id: 1,
   )
Pending Action

A pending action that will change to another state if “pending till” has been reached.

>> Ticket::State.create_or_update(
     name: 'pending and reopen',
     state_type: Ticket::StateType.find_by(name: 'pending action'),
     ignore_escalation: true,
     next_state: Ticket::State.find_by(name: 'open'),
     created_by_id: 1,
     updated_by_id: 1,
   )
(optional) Disable date and time picker (pending till) for pending states

Starting with Zammad 5.0, Core Workflows automatically handles displaying the “pending till” field for pending states. Below snippet is not required and is only relevant if you don’t want to create a workflow within the UI of Zammad.

Replace pending customer feedback with the pending state of your choice.

>> CoreWorkflow.create_if_not_exists(
      name:               'remove pending till on state "pending customer feedback"',
      object:             'Ticket',
      condition_selected: { 'ticket.state_id'=>{ 'operator' => 'is', 'value' => Ticket::State.find_by(name: 'pending customer feedback').id.to_s } },
      perform:            { 'ticket.pending_time'=> { 'operator' => 'remove', 'remove' => 'true' } },
      created_by_id:      1,
      updated_by_id:      1,
   )
Make new states available to UI

Before being able to use the new states within the WebApp, you need to run the following commands to make them available.

Warning

Please do not replace anything below, state_id is a named attribute which is correct and shall not be replaced!

>> attribute = ObjectManager::Attribute.get(
     object: 'Ticket',
     name: 'state_id',
   )
>> attribute.data_option[:filter] = Ticket::State.by_category(:viewable).pluck(:id)
>> attribute.screens[:create_middle]['ticket.agent'][:filter] = Ticket::State.by_category(:viewable_agent_new).pluck(:id)
>> attribute.screens[:create_middle]['ticket.customer'][:filter] = Ticket::State.by_category(:viewable_customer_new).pluck(:id)
>> attribute.screens[:edit]['ticket.agent'][:filter] = Ticket::State.by_category(:viewable_agent_edit).pluck(:id)
>> attribute.screens[:edit]['ticket.customer'][:filter] = Ticket::State.by_category(:viewable_customer_edit).pluck(:id)
>> attribute.save!
Limit available states for customers

Tip

Core Workflows allows you to achieve below described behavior any time without any issues. No need to use the console if you don’t want to!

By default Zammad allows customers to change Ticket states to open and closed. If this does not meet your requirenments, you can adjust this at anytime. The below example shows how to restrict your customer to only close tickets if needed:

>> attribute = ObjectManager::Attribute.get(
     object: 'Ticket',
     name: 'state_id',
   )
>> attribute.screens['edit']['ticket.customer']['filter'] = Ticket::State.where(name: ['closed']).pluck(:id)
>> attribute.save!

Hint

If you want to allow several different states for customers, you need to provide the state names as array - like so: ['closed', 'open', 'my-amazing-state'] (instead of ['closed']).

You can check the current active states that customers can set like so:

>> ObjectManager::Attribute.get(
     object: 'Ticket',
     name: 'state_id',
   ).screens['edit']['ticket.customer']['filter']

The above will return one or more IDs - if you’re not sure which state they belong to, you can check the state name with the following command. (Ensure to replace {ID} with your returned ID(s))

>> Ticket::State.find({ID}).name

Working with ticket articles

Note

Please note that this is not a full command list, if you’re missing commands, feel free to ask over at the Community.

Count Public “Notes” toward SLAs

Normally, notes don’t count toward service-level agreements. Use the following command to include publicly-visible notes when tracking SLA compliance. (Internal notes will never affect SLA calculations.)

Note

By default, customers are not notified when public notes are added to a ticket. Set up a trigger if you wish to change this behavior.

Warning

Changing this setting will disable the option to delete public notes.

>> Ticket::Article::Type.find_by(name:'note').update!(communication: true)    # Enable SLA to count notes as communication
>> Ticket::Article::Type.find_by(name:'note').update!(communication: false)   # Enable SLA to ignore notes as communication

Working with groups

Note

Please note that this is not a full command list, if you’re missing commands, feel free to ask over at the Community.

To open the rails console on the shell you have to enter the following commands.

Find group
>> Group.find_by(name: 'Users').follow_up_possible

Working with chat logs

Note

Please note that this is not a full command list, if you’re missing commands, feel free to ask over at the Community.

Removing IP address logs

Use the following command to remove all IP address records from closed chats that haven’t been updated in the last seven days:

>> Chat::Session.where(state: 'closed').where('updated_at < ?', 7.days.ago).each do |session|
     next if session.preferences['remote_ip'].blank?

     session.preferences.delete('geo_ip')
     session.preferences.delete('remote_ip')
     session.save!(touch: false)
   end

Other useful commands

Note

Please note that this is not a full command list, if you’re missing commands, feel free to ask over at the Community.

Fetch mails

The below command will do a manual fetch of mail channels. This will also show errors that might appear within that process.

>> Channel.fetch
Reprocess unprocessable mails

When Zammad encounters a mail it cannot parse (e.g. due to a parser bug or a malformed message), it will store the mail in var/spool/unprocessable_mail/<ID>.eml, give up on attempting to parse the mail, and will warn on the monitoring page that there are unprocessed mails.

To force Zammad to reattempt to parse those mails, run the following command:

>> Channel::EmailParser.process_unprocessable_mails

In case of a malformed message (e.g. an invalid email address in one of the header fields), you may need to manually edit the mail before Zammad can process it.

If Zammad fails to process the message, it will remain in the var/spool/unprocessable_mail folder; otherwise it will be removed after it has been parsed successfully.

Add translation

This comes in handy if you e.g. added a new state that you need to translate for several languages.

>> Translation.create_if_not_exists(:locale => 'de-de', :source => "New", :target => "Neu", created_by_id: 1, updated_by_id: 1)

Warning

While Zammad knows further attributes for the Translation model, please do not set them manually. Doing so may interfere with our Weblate translation process and cause you loosing your custom translations.

If you want to translate code base strings that are available within standard code, please use Weblate instead.

Translating attributes

By default Zammad will not translate custom attributes. With the following code you can enable translation. This will translate the attribute display name and the display names of values (if it’s a value field). For this to work, just replace {attribute-name} with the name of your attribute.

>> attribute = ObjectManager::Attribute.find_by(name: '{attribute-name}')
>> attribute.data_option[:translate] = true  # set this to false to disable
                                             # translation again
>> attribute.save!

Note

Translating value display names works for the following attribute types:

  • Boolean

  • Select

  • Tree Select

If you’re translating the display name of e.g. an Integer-attribute, this works as well!

Fill a test system with test data

Danger

Don’t run this in a productive environment! This can slow down Zammad and is hard to revert if you create much!

The below command will add 50 agents, 1000 customers, 20 groups, 40 organizations, 5 new overviews and 100 tickets. You can always use 0 to not create specific items. Zammad will create random data which make no logical sense.

>> FillDb.load(agents: 50,customers: 1000,groups: 20,organizations: 40,overviews: 5,tickets: 100,)

Deleting records

Danger

☠️ The commands listed here cause irrecoverable data loss! Only proceed if you know what you’re doing and you have a backup!

Note

Please note that this is not a full command list, if you’re missing commands, feel free to ask over at the Community.

Removing tickets (and their articles)
# Delete a ticket (specified by database ID)
>> Ticket.find(4).destroy

# Delete all tickets
>> Ticket.destroy_all

# Keep some tickets (specified by database ID); delete the rest
>> tickets_to_keep = [1, 2, 3]
>> Ticket.where.not(id: tickets_to_keep).destroy_all
Removing users

Warning

Customers may not be deleted while they have tickets remaining in the system.

As such, the examples below will delete not only the specified customers, but all tickets associated with them, as well. Below commands remove upon executing without any further warnings.

Hint

If you’re not sure what to do and need to learn more about what Zammad does upon removing users, please consider using Zammad’s UI options in stead.

Our documentation for the data privacy function will help you a lot!

Removing users is possible in 2 ways: A single user and in bulk.

>> User.find_by(email: '<email address>').destroy
Removing organizations

Note

Removing an organization does not delete associated customers.

Step 1: Select organizations
# by "active" status
>> organizations = Organization.where(active: false)

# by name
>> organizations = Organization.where(name: 'Acme')

# by partial match on notes
>> organizations = Organization.where('note LIKE ?', '%foo%')
Step 2: Preview affected organizations
>> puts organizations.map { |org| "ORGANIZATION #{org.name}" }.join("\n")
Step 3: Proceed with deletion
>> organizations.each do |org|
     puts %{Preparing deletion of organization "#{org.name}"...}

     org.members.each do |member|
        puts "  Removing #{member.fullname} from organization..."
        member.update!(organization_id: nil)
     end

     puts "  Deleting #{org.name}..."
     org.destroy
   end
Removing system records
# Remove all online notifications
>> OnlineNotification.destroy_all

# Remove all entries from the Activity Stream (dashboard)
>> ActivityStream.destroy_all

# Remove entries for all recently viewed objects
# (tickets, users, organizations)
>> RecentView.destroy_all

# Remove all history information from tickets, users and organizations
# (dangerous!)
>> History.destroy_all
Reset Zammad installation

Hint

Below commands are incomplete intentionally, error outputs will hint you through! The following operations will cause data loss and are for development / testing only.

Don’t forget to stop Zammad before trying to drop the database!

$ rake db:drop
$ rake db:create
$ rake db:migrate
$ rake db:seed

Start

We will be very happy if you decide to contribute to Zammad. You can do this in several ways. Contributions are mainly done by forking one of our repos on GitHub and creating a pull request with your changes (except for translations, see below). 🚀

You can contribute to:

Plese have a look on our notes on how to contribute below.

All repos can be found at https://github.com/zammad

Zammad Source Code

The Zammad source code can be found on GitHub at https://github.com/zammad/zammad

For more information on how to contribute to Zammad, please have a look at https://zammad.org/participate and at the Developer Manual.

Supported Branches / Versions

The main Zammad repository at https://github.com/zammad/zammad has several branches.

develop
  • This is the current (unreleased) development state of next major release (this will become the new stable branch).

  • Don’t use it for production!

  • Supported with bug and security fixes - see also our Security Policy.

stable
  • This is the current stable release, e.g. Zammad 5.2.

  • Use this branch for production installations.

  • Supported with bug and security fixes - see also our Security Policy.

stable-x.y
  • These are the branches of old versions of Zammad like stable-5.1 for Zammad 5.1.

  • No support for bug or security issues is provided.

Documentation

Do you want to contribute to the Zammad documentation?

Open a new GitHub pull request at

with your changes.

The Zammad documentation is hosted on Read the Docs. You can read it there at

or browse the files via GitHub which also renders the used ReStructuredText markup.

ReStructuredText markup

If you like to edit the docs, use the ReStructuredText markup language. Information about this language can be found at:

Thanks! ❤ ❤ ❤

Zammad Team

Translation

If you want to help us with translation and improve the multi-language support of Zammad and/or the documentation, you are welcome to contribute as well! The translation of Zammad itself and the documentation is done by using Weblate, which is a service for the collaborative translation of projects.

You just have to head over to Zammad’s Weblate instance. You can either create an account (if you don’t have one already) or even sign in with your Github account!

We will cover some basic steps in the following sections to get you started with translating. However, if you want to use some additional features of Weblate and want to dive deeper into it, their translation documentation is a good starting point.

Basics

The translation of Zammad and the translation of the documentation are split into two projects in Weblate. When you click in the top menu under “Projects > Browse all projects”, you can find the overview of the two projects:

Screenshot showing translation projects in Weblate and menu

Screenshot showing translation projects and menu bar of Weblate

Structure of translation projects in Weblate:

  • Documentation

    • User Documentation (latest)

    • User Documentation (pre-release)

    • Admin Documentation (latest)

    • Admin Documentation (pre-release)

  • Zammad

    • Zammad (development version)

    • Zammad (stable version)

    • Some more which aren’t relevant here

Note

It is no big difference if you choose latest or pre-release (for the documentation) or development version or stable version (for Zammad). When Weblate detects the same strings in different versions, they will be used for all versions and only have to be translated once.

After selecting a project (Documentation or Zammad), you will see different sub-projects and their translation status summarized for all languages. These overviews may show a quite low translation rate, which is due to the amount of acive languages.

Here you can select one of the “components”, which is more or less the same as different versions. After selecting one of them, you can see the status of translation for the different languages, as you can see in the following screenshot with an example from Documentation > User Documentation (latest):

Screenshot showing translation status of different languages for the user documentation

Screenshot showing translation status of different languages for the user documentation

Translating

After selecting your languange you want to translate to, a good starting point is to select “Untranslated strings” (or the same meaning in your language, depending on what you have set in your profile).

After that, you will finally see the first untranslated string in the upper field and, in theory, you can start to translate. First a brief overview of the user interface of Weblate:

Screenshot of Weblate translation user interface
  1. Breadcrumbs with path to the current project and language

  2. Translation area itself. You can find the source string (“English (United States)”) at the top and the field for your translation (“French” in this example).

  3. Glossary: here you can find common translations in Zammad context. The terms from the glossary are highlighted in the source strings, as well.

  4. Some useful tabs:

    • Nearby strings: shows you the context of the word or string

    • Automatic suggestions: here you can find automatic suggestions from DeepL and suggestions from similar strings, which are already translated. Use the “Clone to translation” button to insert it in the translation field to apply changes. Use the “Accept” button to accept the suggested translation and automatically switch to the next string.

    • Other languages: here you can get an overview, which languages are translated and you can also see the translated strings (could be useful for languages, which are similar).

Troubleshooting

And finally some notes for “special” source strings, you might see in the documentation projects (see RestructuredText for details):

``example-string``

This is rendered as example-string. Depending on the context, it can be translated or not. In any case, use the `` before and after the string in your translation.

:doc:`example <path/to/document>`

This is a link to another page. Some links doesn’t have the “example” part included, e.g. :doc:`path/to/document. The above “example” is the text, which is shown as link. This part can be translated. The path/to/document may not be translated, otherwise the link would not work anymore.

`some text <https://example.com>`_

This is a link which can refer to an external website. “some text” is the displayed text in the documentation, the part between < and > is the link target. The _ at the end is important and must remain in the translated text.

:admin-docs:`some text </manage-text-modules.html>`

This is a link which refers to external documentation. “some text” is the displayed text in the documentation, the part between < and > is the link target. Note the absence of _ at the end, since this link is using a different construction mechanism.

**example string**

Markup for text (e.g. bold, italics). Alternative: *example string*. These strings can be translated, but the markup labeling (e.g. one or more *) should be adopted true to meaning.

Introduction

Zammad provides a powerful REST-API which allows all operations that are available via UI as well.

This page gives you a first impression for things that generally count for all endpoints and how to adapt.

API clients

There are API clients available. Please note that these clients may not provide access to all available endpoints listed here.

Authentication

Zammad supports three different authentication methods for its API.

HTTP Basic Authentication (username/password)
The username / password must be provided as HTTP header in the HTTP call.
This authentication method can be disabled and may not be available in your system.
$ curl -u {username}:{password} https://{fqdn}/{endpoint}

Note

We strongly suggest against using basic authentication. Use access tokens when ever possible!

HTTP Token Authentication (access token)
The access token must be provided as HTTP header in the HTTP call.
Each user can create several access tokens in their user preferences.
This authentication method can be disabled and may not be available in your system.
$ curl -H "Authorization: Token token={your_token}" https://{fqdn}/{endpoint}
OAuth2 (token access)
The token must be provided as HTTP header in your calls.
This allows 3rd party applications to authenticate against Zammad.
$ curl -H "Authorization: Bearer {your_token}" https://{fqdn}/{endpoint}

Endpoints and example data

For simplicity we’ll not provide specific commands on the next pages, but instead tell the possible call method (e.g. GET) and the endpoint to use (e.g. /api/v1/users). In case Zammad expects information within these endpoint urls, we’ll put them into curly braces like so: /api/v1/users/{user id}

The response format will be a complete JSON response from a default Zammad instance. Please keep in mind that you may see more fields or general information in case you added objects or other information.

Content Type

Zammad returns JSON payloads whenever you retrieve data. If you’re going to provide data, no matter of the general request type, don’t forget to provide the content type application/json as well.

Response Payloads (expand)

Zammad always returns information including hints to all relations. If you need more information than that (because IDs may not be enough) you can also extend your endpoint calls with ?expand=true.

This switch will provide even more information — at least named relations on top of the ID ones. Below you can find two examples to compare - one for ticket and user each.

{
   "active": true,
   "login_failed": 0,
   "verified": false,
   "source": null,
   "login": "chris@chrispresso.com",
   "last_login": "2021-09-23T13:17:24.817Z",
   "id": 3,
   "updated_by_id": 1,
   "organization_id": 2,
   "firstname": "Christopher",
   "lastname": "Miller",
   "email": "chris@chrispresso.com",
   "image": "7a6a0d1d94ad2037153cf3a6c1b49a53",
   "image_source": null,
   "web": "",
   "phone": "",
   "fax": "",
   "mobile": "",
   "department": "",
   "street": "",
   "zip": "",
   "city": "",
   "country": "",
   "address": "",
   "vip": false,
   "note": "",
   "out_of_office": false,
   "out_of_office_start_at": null,
   "out_of_office_end_at": null,
   "out_of_office_replacement_id": null,
   "preferences":
   {
      "notification_config":
      {
         "matrix":
         {
            "create":
            {
               "criteria":
               {
                  "owned_by_me": true,
                  "owned_by_nobody": true,
                  "subscribed": true,
                  "no": true
               },
               "channel":
               {
                  "email": true,
                  "online": true
               }
            },
            "update":
            {
               "criteria":
               {
                  "owned_by_me": true,
                  "owned_by_nobody": true,
                  "subscribed": true,
                  "no": true
               },
               "channel":
               {
                  "email": true,
                  "online": true
               }
            },
            "reminder_reached":
            {
               "criteria":
               {
                  "owned_by_me": true,
                  "owned_by_nobody": false,
                  "no": true
               },
               "channel":
               {
                  "email": true,
                  "online": true
               }
            },
            "escalation":
            {
               "criteria":
               {
                  "owned_by_me": true,
                  "owned_by_nobody": false,
                  "no": true
               },
               "channel":
               {
                  "email": true,
                  "online": true
               }
            }
         },
         "group_ids":
         [
            "2",
            "1",
            "3"
         ]
      },
      "locale": "de-de",
      "intro": true,
      "notification_sound":
      {
         "file": "Xylo.mp3",
         "enabled": true
      },
      "cti": true,
      "tickets_closed": 0,
      "tickets_open": 1
   },
   "created_by_id": 1,
   "created_at": "2021-07-26T14:44:41.066Z",
   "updated_at": "2021-09-23T13:17:24.825Z",
   "role_ids":
   [
      1,
      2
   ],
   "organization_ids":
   [],
   "authorization_ids":
   [],
   "karma_user_ids":
   [
      1
   ],
   "group_ids":
   {
      "1":
      [
         "full"
      ],
      "2":
      [
         "full"
      ],
      "3":
      [
         "full"
      ]
   }
}

Warning

Please note that Core Workflows may restrict access to attributes or values. See Core Workflows limitations to learn more.

Sorting search results

Zammad allows you to sort your search results by field if needed.

sort_by

Append ?sort_by={row name} to your query to sort by a specific row that appears in the search result.

order_by

Append ?order_by={direction} to your query to switch in between ascending and descending order.

Directions are: asc and desc.

Note

Usually you’ll want to combine both parameters in your searches - e.g.: ?query={search string}&sort_by={row name}&order_by={direction}

Actions on behalf of other users

Requirement: the user used for running the query on behalf requires admin.user permission.

Running API queries on behalf of other users allows you to e.g. create tickets by a different user.

To do so, add a new HTTP header named From to your request. The value of this header can be one of the following:

  • user ID

  • user login

  • user email

From is available for all endpoints.

Encoding

The API expects UTF-8 encoding. Keep in mind that especially when using URLs with get options (e.g. ?query=this) you may need to encode your URL accordingly.

If you want to learn more about URL encoding, this Wikipedia article may be of help

User

Note

🤓 To see or not to see

Please note that below samples were provided with admin and ticket.agent permissions. Some attributes / information may not be available in specific situations.

Please see our Permission Guide to get better insights.

me - current user

Required permission: any

GET-Request sent: /api/v1/users/me

Response:

# HTTP-Code 200 Ok

{
   "id": 3,
   "organization_id": 2,
   "login": "chris@chrispresso.com",
   "firstname": "Christopher",
   "lastname": "Miller",
   "email": "chris@chrispresso.com",
   "image": "7a6a0d1d94ad2037153cf3a6c1b49a53",
   "image_source": null,
   "web": "",
   "phone": "",
   "fax": "",
   "mobile": "",
   "department": null,
   "street": "",
   "zip": "",
   "city": "",
   "country": "",
   "address": null,
   "vip": false,
   "verified": false,
   "active": true,
   "note": "",
   "last_login": "2021-11-03T12:26:53.410Z",
   "source": null,
   "login_failed": 0,
   "out_of_office": false,
   "out_of_office_start_at": null,
   "out_of_office_end_at": null,
   "out_of_office_replacement_id": null,
   "preferences":
   {
      "notification_config":
      {
         "matrix":
         {
            "create":
            {
               "criteria":
               {
                  "owned_by_me": true,
                  "owned_by_nobody": true,
                  "subscribed": true,
                  "no": false
               },
               "channel":
               {
                  "email": true,
                  "online": true
               }
            },
            "update":
            {
               "criteria":
               {
                  "owned_by_me": true,
                  "owned_by_nobody": true,
                  "subscribed": true,
                  "no": false
               },
               "channel":
               {
                  "email": true,
                  "online": true
               }
            },
            "reminder_reached":
            {
               "criteria":
               {
                  "owned_by_me": true,
                  "owned_by_nobody": false,
                  "subscribed": false,
                  "no": false
               },
               "channel":
               {
                  "email": true,
                  "online": true
               }
            },
            "escalation":
            {
               "criteria":
               {
                  "owned_by_me": true,
                  "owned_by_nobody": false,
                  "subscribed": false,
                  "no": false
               },
               "channel":
               {
                  "email": true,
                  "online": true
               }
            }
         }
      },
      "locale": "en-us",
      "intro": true
   },
   "updated_by_id": 3,
   "created_by_id": 1,
   "created_at": "2021-11-03T11:57:15.975Z",
   "updated_at": "2021-11-03T12:26:55.642Z",
   "role_ids":
   [
      1,
      2
   ],
   "organization_ids":
   [],
   "authorization_ids":
   [],
   "karma_user_ids":
   [],
   "group_ids":
   {
      "1":
      [
         "full"
      ],
      "2":
      [
         "full"
      ],
      "3":
      [
         "full"
      ]
   }
}

List

Required permission: ticket.agent or admin.user

Note

Technically, any listings will return users own information only.

GET-Request sent: /api/v1/users

Response:

# HTTP-Code 200 Ok

[
   {
      "id": 1,
      "organization_id": null,
      "login": "-",
      "firstname": "-",
      "lastname": "",
      "email": "",
      "image": null,
      "image_source": null,
      "web": "",
      "phone": "",
      "fax": "",
      "mobile": "",
      "department": "",
      "street": "",
      "zip": "",
      "city": "",
      "country": "",
      "address": "",
      "vip": false,
      "verified": false,
      "active": false,
      "note": "",
      "last_login": null,
      "source": null,
      "login_failed": 0,
      "out_of_office": false,
      "out_of_office_start_at": null,
      "out_of_office_end_at": null,
      "out_of_office_replacement_id": null,
      "preferences":
      {},
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:12.786Z",
      "updated_at": "2021-11-03T11:51:12.786Z",
      "role_ids":
      [],
      "organization_ids":
      [],
      "authorization_ids":
      [],
      "karma_user_ids":
      [],
      "group_ids":
      {}
   },
   {
      "id": 2,
      "organization_id": 1,
      "login": "nicole.braun@zammad.org",
      "firstname": "Nicole",
      "lastname": "Braun",
      "email": "nicole.braun@zammad.org",
      "image": null,
      "image_source": null,
      "web": "",
      "phone": "",
      "fax": "",
      "mobile": "",
      "department": "",
      "street": "",
      "zip": "",
      "city": "",
      "country": "",
      "address": "",
      "vip": false,
      "verified": false,
      "active": true,
      "note": "",
      "last_login": null,
      "source": null,
      "login_failed": 0,
      "out_of_office": false,
      "out_of_office_start_at": null,
      "out_of_office_end_at": null,
      "out_of_office_replacement_id": null,
      "preferences":
      {
         "tickets_closed": 0,
         "tickets_open": 1
      },
      "updated_by_id": 2,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:13.703Z",
      "updated_at": "2021-11-03T12:01:05.411Z",
      "role_ids":
      [
         3
      ],
      "organization_ids":
      [],
      "authorization_ids":
      [],
      "karma_user_ids":
      [],
      "group_ids":
      {}
   },
   {
      "id": 3,
      "organization_id": 2,
      "login": "chris@chrispresso.com",
      "firstname": "Christopher",
      "lastname": "Miller",
      "email": "chris@chrispresso.com",
      "image": "7a6a0d1d94ad2037153cf3a6c1b49a53",
      "image_source": null,
      "web": "",
      "phone": "",
      "fax": "",
      "mobile": "",
      "department": null,
      "street": "",
      "zip": "",
      "city": "",
      "country": "",
      "address": null,
      "vip": false,
      "verified": false,
      "active": true,
      "note": "",
      "last_login": "2021-11-03T12:26:53.410Z",
      "source": null,
      "login_failed": 0,
      "out_of_office": false,
      "out_of_office_start_at": null,
      "out_of_office_end_at": null,
      "out_of_office_replacement_id": null,
      "preferences":
      {
         "notification_config":
         {
            "matrix":
            {
               "create":
               {
                  "criteria":
                  {
                     "owned_by_me": true,
                     "owned_by_nobody": true,
                     "subscribed": true,
                     "no": false
                  },
                  "channel":
                  {
                     "email": true,
                     "online": true
                  }
               },
               "update":
               {
                  "criteria":
                  {
                     "owned_by_me": true,
                     "owned_by_nobody": true,
                     "subscribed": true,
                     "no": false
                  },
                  "channel":
                  {
                     "email": true,
                     "online": true
                  }
               },
               "reminder_reached":
               {
                  "criteria":
                  {
                     "owned_by_me": true,
                     "owned_by_nobody": false,
                     "subscribed": false,
                     "no": false
                  },
                  "channel":
                  {
                     "email": true,
                     "online": true
                  }
               },
               "escalation":
               {
                  "criteria":
                  {
                     "owned_by_me": true,
                     "owned_by_nobody": false,
                     "subscribed": false,
                     "no": false
                  },
                  "channel":
                  {
                     "email": true,
                     "online": true
                  }
               }
            }
         },
         "locale": "en-us",
         "intro": true
      },
      "updated_by_id": 3,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:57:15.975Z",
      "updated_at": "2021-11-03T12:26:55.642Z",
      "role_ids":
      [
         1,
         2
      ],
      "organization_ids":
      [],
      "authorization_ids":
      [],
      "karma_user_ids":
      [],
      "group_ids":
      {
         "1":
         [
            "full"
         ],
         "2":
         [
            "full"
         ],
         "3":
         [
            "full"
         ]
      }
   },
   {
      "id": 4,
      "organization_id": 2,
      "login": "jacob@chrispresso.com",
      "firstname": "Jacob",
      "lastname": "Smith",
      "email": "jacob@chrispresso.com",
      "image": "95afc1244af5cb8b77edcd7224c5d5f8",
      "image_source": null,
      "web": "",
      "phone": "",
      "fax": "",
      "mobile": "",
      "department": null,
      "street": "",
      "zip": "",
      "city": "",
      "country": "",
      "address": null,
      "vip": false,
      "verified": false,
      "active": true,
      "note": "",
      "last_login": null,
      "source": null,
      "login_failed": 0,
      "out_of_office": false,
      "out_of_office_start_at": null,
      "out_of_office_end_at": null,
      "out_of_office_replacement_id": null,
      "preferences":
      {
         "notification_config":
         {
            "matrix":
            {
               "create":
               {
                  "criteria":
                  {
                     "owned_by_me": true,
                     "owned_by_nobody": true,
                     "subscribed": true,
                     "no": false
                  },
                  "channel":
                  {
                     "email": true,
                     "online": true
                  }
               },
               "update":
               {
                  "criteria":
                  {
                     "owned_by_me": true,
                     "owned_by_nobody": true,
                     "subscribed": true,
                     "no": false
                  },
                  "channel":
                  {
                     "email": true,
                     "online": true
                  }
               },
               "reminder_reached":
               {
                  "criteria":
                  {
                     "owned_by_me": true,
                     "owned_by_nobody": false,
                     "subscribed": false,
                     "no": false
                  },
                  "channel":
                  {
                     "email": true,
                     "online": true
                  }
               },
               "escalation":
               {
                  "criteria":
                  {
                     "owned_by_me": true,
                     "owned_by_nobody": false,
                     "subscribed": false,
                     "no": false
                  },
                  "channel":
                  {
                     "email": true,
                     "online": true
                  }
               }
            }
         },
         "locale": "en-us"
      },
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:57:16.160Z",
      "updated_at": "2021-11-03T11:57:16.214Z",
      "role_ids":
      [
         1,
         2
      ],
      "organization_ids":
      [],
      "authorization_ids":
      [],
      "karma_user_ids":
      [],
      "group_ids":
      {
         "1":
         [
            "full"
         ],
         "2":
         [
            "full"
         ],
         "3":
         [
            "full"
         ]
      }
   },
   {
      "id": 5,
      "organization_id": 2,
      "login": "emma@chrispresso.com",
      "firstname": "Emma",
      "lastname": "Taylor",
      "email": "emma@chrispresso.com",
      "image": "b64fef91c29105b4a08a2a69be08eda3",
      "image_source": null,
      "web": "",
      "phone": "",
      "fax": "",
      "mobile": "",
      "department": null,
      "street": "",
      "zip": "",
      "city": "",
      "country": "",
      "address": null,
      "vip": false,
      "verified": false,
      "active": true,
      "note": "",
      "last_login": null,
      "source": null,
      "login_failed": 0,
      "out_of_office": false,
      "out_of_office_start_at": null,
      "out_of_office_end_at": null,
      "out_of_office_replacement_id": null,
      "preferences":
      {
         "notification_config":
         {
            "matrix":
            {
               "create":
               {
                  "criteria":
                  {
                     "owned_by_me": true,
                     "owned_by_nobody": true,
                     "subscribed": true,
                     "no": false
                  },
                  "channel":
                  {
                     "email": true,
                     "online": true
                  }
               },
               "update":
               {
                  "criteria":
                  {
                     "owned_by_me": true,
                     "owned_by_nobody": true,
                     "subscribed": true,
                     "no": false
                  },
                  "channel":
                  {
                     "email": true,
                     "online": true
                  }
               },
               "reminder_reached":
               {
                  "criteria":
                  {
                     "owned_by_me": true,
                     "owned_by_nobody": false,
                     "subscribed": false,
                     "no": false
                  },
                  "channel":
                  {
                     "email": true,
                     "online": true
                  }
               },
               "escalation":
               {
                  "criteria":
                  {
                     "owned_by_me": true,
                     "owned_by_nobody": false,
                     "subscribed": false,
                     "no": false
                  },
                  "channel":
                  {
                     "email": true,
                     "online": true
                  }
               }
            }
         },
         "locale": "en-us"
      },
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:57:16.349Z",
      "updated_at": "2021-11-03T11:57:16.409Z",
      "role_ids":
      [
         2
      ],
      "organization_ids":
      [],
      "authorization_ids":
      [],
      "karma_user_ids":
      [],
      "group_ids":
      {
         "1":
         [
            "full"
         ],
         "2":
         [
            "full"
         ],
         "3":
         [
            "full"
         ]
      }
   },
   {
      "id": 6,
      "organization_id": 3,
      "login": "anna@example.com",
      "firstname": "Anna",
      "lastname": "Lopez",
      "email": "anna@example.com",
      "image": "4b1cb1fae2e608ffa72099774e1f57ad",
      "image_source": null,
      "web": "",
      "phone": "415-123-5858",
      "fax": "",
      "mobile": "",
      "department": null,
      "street": "",
      "zip": "",
      "city": "",
      "country": "",
      "address": "Golden Gate Bridge\nSan Francisco, CA 94129",
      "vip": false,
      "verified": false,
      "active": true,
      "note": "likes espresso romano - recommended espresso con panna",
      "last_login": null,
      "source": null,
      "login_failed": 0,
      "out_of_office": false,
      "out_of_office_start_at": null,
      "out_of_office_end_at": null,
      "out_of_office_replacement_id": null,
      "preferences":
      {},
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:57:16.526Z",
      "updated_at": "2021-11-03T11:57:16.611Z",
      "role_ids":
      [
         3
      ],
      "organization_ids":
      [],
      "authorization_ids":
      [],
      "karma_user_ids":
      [],
      "group_ids":
      {}
   },
   {
      "id": 7,
      "organization_id": 3,
      "login": "samuel@example.com",
      "firstname": "Samuel",
      "lastname": "Lee",
      "email": "samuel@example.com",
      "image": "5911d228f3588c36a72d80eb0c1e4d08",
      "image_source": null,
      "web": "",
      "phone": "855-666-7777",
      "fax": "",
      "mobile": "",
      "department": null,
      "street": "",
      "zip": "",
      "city": "",
      "country": "",
      "address": "5201 Blue Lagoon Drive\n8th Floor & 9th Floor\nMiami, FL 33126",
      "vip": false,
      "verified": false,
      "active": true,
      "note": "likes americano, did order two units",
      "last_login": null,
      "source": null,
      "login_failed": 0,
      "out_of_office": false,
      "out_of_office_start_at": null,
      "out_of_office_end_at": null,
      "out_of_office_replacement_id": null,
      "preferences":
      {},
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:57:16.748Z",
      "updated_at": "2021-11-03T11:57:16.861Z",
      "role_ids":
      [
         3
      ],
      "organization_ids":
      [],
      "authorization_ids":
      [],
      "karma_user_ids":
      [],
      "group_ids":
      {}
   },
   {
      "id": 8,
      "organization_id": 3,
      "login": "emily@example.com",
      "firstname": "Emily",
      "lastname": "Adams",
      "email": "emily@example.com",
      "image": "99ba64a89f7783c099c304c9b00ff9e8",
      "image_source": null,
      "web": "",
      "phone": "0061 2 1234 7777",
      "fax": "",
      "mobile": "",
      "department": null,
      "street": "",
      "zip": "",
      "city": "",
      "country": "",
      "address": "Bennelong Point\nSydney NSW 2000",
      "vip": false,
      "verified": false,
      "active": true,
      "note": "did order café au lait, ask next time if the flavor was as expected",
      "last_login": null,
      "source": null,
      "login_failed": 0,
      "out_of_office": false,
      "out_of_office_start_at": null,
      "out_of_office_end_at": null,
      "out_of_office_replacement_id": null,
      "preferences":
      {},
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:57:17.000Z",
      "updated_at": "2021-11-03T11:57:17.060Z",
      "role_ids":
      [
         3
      ],
      "organization_ids":
      [],
      "authorization_ids":
      [],
      "karma_user_ids":
      [],
      "group_ids":
      {}
   },
   {
      "id": 9,
      "organization_id": 4,
      "login": "ryan@example.com",
      "firstname": "Ryan",
      "lastname": "Parker",
      "email": "ryan@example.com",
      "image": "0e405c60b5deb780feb7ebebd37ff5e0",
      "image_source": null,
      "web": "",
      "phone": "0049 30 1234 5678",
      "fax": "",
      "mobile": "",
      "department": null,
      "street": "",
      "zip": "",
      "city": "",
      "country": "",
      "address": "Brandenburger Tor 7\n10117 Berlin",
      "vip": false,
      "verified": false,
      "active": true,
      "note": "no latte but macchiato",
      "last_login": null,
      "source": null,
      "login_failed": 0,
      "out_of_office": false,
      "out_of_office_start_at": null,
      "out_of_office_end_at": null,
      "out_of_office_replacement_id": null,
      "preferences":
      {},
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:57:17.190Z",
      "updated_at": "2021-11-03T11:57:17.250Z",
      "role_ids":
      [
         3
      ],
      "organization_ids":
      [],
      "authorization_ids":
      [],
      "karma_user_ids":
      [],
      "group_ids":
      {}
   },
   {
      "id": 10,
      "organization_id": null,
      "login": "david@example.com",
      "firstname": "David",
      "lastname": "Bell",
      "email": "david@example.com",
      "image": "d829d234f377f231534802df6d5500a7",
      "image_source": null,
      "web": "",
      "phone": "0033 892 12 34 56",
      "fax": "",
      "mobile": "",
      "department": null,
      "street": "",
      "zip": "",
      "city": "",
      "country": "",
      "address": "Eiffel Tower\n5 Avenue Anatole France\n75007 Paris",
      "vip": false,
      "verified": false,
      "active": true,
      "note": "did order viennese melange, ask next time if the flavor was as expected",
      "last_login": null,
      "source": null,
      "login_failed": 0,
      "out_of_office": false,
      "out_of_office_start_at": null,
      "out_of_office_end_at": null,
      "out_of_office_replacement_id": null,
      "preferences":
      {},
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:57:17.495Z",
      "updated_at": "2021-11-03T11:57:17.561Z",
      "role_ids":
      [
         3
      ],
      "organization_ids":
      [],
      "authorization_ids":
      [],
      "karma_user_ids":
      [],
      "group_ids":
      {}
   },
   {
      "id": 11,
      "organization_id": null,
      "login": "olivia@example.com",
      "firstname": "Olivia",
      "lastname": "Ross",
      "email": "olivia@example.com",
      "image": "b6f7a2d56544bb471eb3a3c238c7d964",
      "image_source": null,
      "web": "",
      "phone": "0044 20 1234 5678",
      "fax": "",
      "mobile": "",
      "department": null,
      "street": "",
      "zip": "",
      "city": "",
      "country": "",
      "address": "Westminster\nLondon SW1A 0AA",
      "vip": false,
      "verified": false,
      "active": true,
      "note": "",
      "last_login": null,
      "source": null,
      "login_failed": 0,
      "out_of_office": false,
      "out_of_office_start_at": null,
      "out_of_office_end_at": null,
      "out_of_office_replacement_id": null,
      "preferences":
      {},
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:57:17.741Z",
      "updated_at": "2021-11-03T11:57:17.794Z",
      "role_ids":
      [
         3
      ],
      "organization_ids":
      [],
      "authorization_ids":
      [],
      "karma_user_ids":
      [],
      "group_ids":
      {}
   }
]

Show

Required permission: ticket.agent or admin.user or ticket.customer (shared organization)

Note

Technically, any listings will return user’s own information only.

GET-Request sent: /api/v1/users/{id}

Response:

# HTTP-Code 200 Ok

{
   "id": 11,
   "organization_id": null,
   "login": "olivia@example.com",
   "firstname": "Olivia",
   "lastname": "Ross",
   "email": "olivia@example.com",
   "image": "b6f7a2d56544bb471eb3a3c238c7d964",
   "image_source": null,
   "web": "",
   "phone": "0044 20 1234 5678",
   "fax": "",
   "mobile": "",
   "department": null,
   "street": "",
   "zip": "",
   "city": "",
   "country": "",
   "address": "Westminster\nLondon SW1A 0AA",
   "vip": false,
   "verified": false,
   "active": true,
   "note": "",
   "last_login": null,
   "source": null,
   "login_failed": 0,
   "out_of_office": false,
   "out_of_office_start_at": null,
   "out_of_office_end_at": null,
   "out_of_office_replacement_id": null,
   "preferences": {},
   "updated_by_id": 1,
   "created_by_id": 1,
   "created_at": "2021-11-03T11:57:17.741Z",
   "updated_at": "2021-11-03T11:57:17.794Z",
   "role_ids": [
      3
   ],
   "organization_ids": [],
   "authorization_ids": [],
   "karma_user_ids": [],
   "group_ids": {}
}

Create

Required permission: admin.user or ticket.agent

Note

🤓 This depends on permissions

Agents can’t set user passwords, roles or group permission. Instead Zammad will apply to default sign up role.

Technically, unauthenticated user creation is possible if you manage to provide the required CSRF token (out of scope of this documentation). If you don’t want that, consider disabling user registration.

Tip

🧐 Creation payloads can be big

Unsure which attributes you can use or set? Run a GET query on any fitting user existing in your instance already.

POST-Request sent: /api/v1/users

{
  "firstname": "Jane",
  "lastname": "Doe",
  "email": "jdoe@example.com",
  "login": "jdoe",
  "organization": "Sample Corp.",
  "roles": [
      "Agent",
      "Customer"
   ]
}

Response:

# HTTP-Code 201 Created

{
   "id": 16,
   "organization_id": 5,
   "login": "jdoe",
   "firstname": "Jane",
   "lastname": "Doe",
   "email": "jdoe@example.com",
   "image": null,
   "image_source": null,
   "web": "",
   "phone": "",
   "fax": "",
   "mobile": "",
   "department": null,
   "street": "",
   "zip": "",
   "city": "",
   "country": "",
   "address": null,
   "vip": false,
   "verified": false,
   "active": true,
   "note": "",
   "last_login": null,
   "source": null,
   "login_failed": 0,
   "out_of_office": false,
   "out_of_office_start_at": null,
   "out_of_office_end_at": null,
   "out_of_office_replacement_id": null,
   "preferences": {
      "notification_config": {
         "matrix": {
            "create": {
               "criteria": {
                  "owned_by_me": true,
                  "owned_by_nobody": true,
                  "subscribed": true,
                  "no": false
               },
               "channel": {
                  "email": true,
                  "online": true
               }
            },
            "update": {
               "criteria": {
                  "owned_by_me": true,
                  "owned_by_nobody": true,
                  "subscribed": true,
                  "no": false
               },
               "channel": {
                  "email": true,
                  "online": true
               }
            },
            "reminder_reached": {
               "criteria": {
                  "owned_by_me": true,
                  "owned_by_nobody": false,
                  "subscribed": false,
                  "no": false
               },
               "channel": {
                  "email": true,
                  "online": true
               }
            },
            "escalation": {
               "criteria": {
                  "owned_by_me": true,
                  "owned_by_nobody": false,
                  "subscribed": false,
                  "no": false
               },
               "channel": {
                  "email": true,
                  "online": true
               }
            }
         }
      },
      "locale": "en-us"
   },
   "updated_by_id": 3,
   "created_by_id": 3,
   "created_at": "2021-11-03T14:42:36.855Z",
   "updated_at": "2021-11-03T14:42:36.855Z",
   "role_ids": [
      2,
      3
   ],
   "organization_ids": [],
   "authorization_ids": [],
   "karma_user_ids": [],
   "group_ids": {}
}

Update

Required permission: admin.user or ticket.agent

Note

🤓 This depends on permissions

Agents can’t set user passwords, roles or group permission. Instead Zammad will apply to default sign up role.

PUT-Request sent: /api/v1/users/{id}

{
   "phone": "+49 30 55 57 160 00",
   "department": "Sales",
   "address": "Marienstr. 18\n10117 Berlin"
}

Response:

# HTTP-Code 200 Ok

{
   "id": 16,
   "organization_id": 5,
   "login": "jdoe",
   "firstname": "Jane",
   "lastname": "Doe",
   "email": "jdoe@example.com",
   "image": null,
   "image_source": null,
   "web": "",
   "phone": "+49 30 55 57 160 00",
   "fax": "",
   "mobile": "",
   "department": "Sales",
   "street": "",
   "zip": "",
   "city": "",
   "country": "",
   "address": "Marienstr. 18\n10117 Berlin",
   "vip": false,
   "verified": false,
   "active": true,
   "note": "",
   "last_login": null,
   "source": null,
   "login_failed": 0,
   "out_of_office": false,
   "out_of_office_start_at": null,
   "out_of_office_end_at": null,
   "out_of_office_replacement_id": null,
   "preferences": {
      "notification_config": {
         "matrix": {
            "create": {
               "criteria": {
                  "owned_by_me": true,
                  "owned_by_nobody": true,
                  "subscribed": true,
                  "no": false
               },
               "channel": {
                  "email": true,
                  "online": true
               }
            },
            "update": {
               "criteria": {
                  "owned_by_me": true,
                  "owned_by_nobody": true,
                  "subscribed": true,
                  "no": false
               },
               "channel": {
                  "email": true,
                  "online": true
               }
            },
            "reminder_reached": {
               "criteria": {
                  "owned_by_me": true,
                  "owned_by_nobody": false,
                  "subscribed": false,
                  "no": false
               },
               "channel": {
                  "email": true,
                  "online": true
               }
            },
            "escalation": {
               "criteria": {
                  "owned_by_me": true,
                  "owned_by_nobody": false,
                  "subscribed": false,
                  "no": false
               },
               "channel": {
                  "email": true,
                  "online": true
               }
            }
         }
      },
      "locale": "en-us"
   },
   "updated_by_id": 3,
   "created_by_id": 3,
   "created_at": "2021-11-03T14:42:36.855Z",
   "updated_at": "2021-11-03T14:49:20.018Z",
   "role_ids": [
      2,
      3
   ],
   "organization_ids": [],
   "authorization_ids": [],
   "karma_user_ids": [],
   "group_ids": {}
}

Delete

Required permission: admin.user

Danger

⚠ This is a permanent removal

Please note that removing users cannot be undone. Zammad will also remove references - thus potentially tickets!

Removing users with references in e.g. activity streams is not possible via API - this will be indicated by "error": "Can't delete, object has references.". This is not a bug.

Consider using Data Privacy via UI for more control instead.

DELETE-Request sent: /api/v1/users/{id}

Response:

# HTTP-Code 200 Ok

{}

Organization

List

Required permission: ticket.agent or admin.organization

Note

Technically, customers can only see their own organization if applicable.

GET-Request sent: /api/v1/organizations

Response:

# HTTP-Code 200 Ok

[
   {
      "id": 1,
      "name": "Zammad Foundation",
      "shared": true,
      "domain": "",
      "domain_assignment": false,
      "active": true,
      "note": "",
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2023-07-26T08:44:39.608Z",
      "updated_at": "2023-08-04T12:02:00.018Z",
      "vip": false,
      "member_ids": [
         2
      ],
      "secondary_member_ids": []
   },
   {
      "name": "Chrispresso Inc.",
      "shared": true,
      "domain": "",
      "domain_assignment": false,
      "active": true,
      "note": "Manufacturer of individual coffee products.",
      "vip": false,
      "updated_by_id": 3,
      "id": 2,
      "created_by_id": 1,
      "created_at": "2023-07-26T08:44:48.617Z",
      "updated_at": "2023-08-04T12:01:44.370Z",
      "member_ids": [
         3,
         5,
         4
      ],
      "secondary_member_ids": []
   },
   {
      "name": "Awesome Customer Inc.",
      "shared": true,
      "domain": "",
      "domain_assignment": false,
      "active": true,
      "note": "Global distributor of communication and security products, electrical and electronic wire &amp; cable.",
      "vip": true,
      "updated_by_id": 3,
      "id": 3,
      "created_by_id": 1,
      "created_at": "2023-07-26T08:44:48.632Z",
      "updated_at": "2023-08-04T12:54:30.974Z",
      "member_ids": [
         8,
         7,
         6
      ],
      "secondary_member_ids": []
   },
   {
      "id": 4,
      "name": "Good Customer Inc.",
      "shared": true,
      "domain": "",
      "domain_assignment": false,
      "active": true,
      "note": "Search the world's information, including webpages, images, videos and more. Good Customer has many special features to help you find exactly what you're looking for.",
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2023-07-26T08:44:48.645Z",
      "updated_at": "2023-07-26T08:44:48.645Z",
      "member_ids": [
         9
      ],
      "secondary_member_ids": []
   }
]

Show

Required permission: ticket.agent or admin.organization

Note

Technically, any users in question can only see their own organization.

GET-Request sent: /api/v1/organizations/{id}

Response:

# HTTP-Code 200 Ok

{
   "id": 2,
   "name": "Chrispresso Inc.",
   "shared": true,
   "domain": "",
   "domain_assignment": false,
   "active": true,
   "note": "Manufacturer of individual coffee products.",
   "vip": false,
   "updated_by_id": 3,
   "created_by_id": 1,
   "created_at": "2023-07-26T08:44:48.617Z",
   "updated_at": "2023-08-04T12:01:44.370Z",
   "member_ids": [
      3,
      5,
      4
   ],
   "secondary_member_ids": []
}

Create

Required permission: admin.organization

POST-Request sent: /api/v1/organizations

{
   "name": "Sample Corp.",
   "shared": false,
   "domain": "example.com",
   "domain_assignment": true,
   "active": true,
   "vip": true,
   "note": "Just a sample, aint that nice?",
   "members": [
      "olivia@example.com",
      "david@example.com"
   ]
}

Response:

# HTTP-Code 201 Created

{
   "id": 5,
   "name": "Sample Corp.",
   "shared": false,
   "domain": "example.com",
   "domain_assignment": true,
   "active": true,
   "note": "Just a sample, aint that nice?",
   "updated_by_id": 3,
   "created_by_id": 3,
   "created_at": "2023-08-08T09:12:42.023Z",
   "updated_at": "2023-08-08T09:12:42.602Z",
   "vip": true,
   "member_ids": [
      10,
      11
   ],
   "secondary_member_ids": []
}

Update

Required permission: admin.organization

PUT-Request sent: /api/v1/organizations/{id}

{
   "name": "Sample Corp.",
   "shared": false,
   "domain": "",
   "domain_assignment": false,
   "active": true,
   "note": "This was a triumph - I'm making a note here - H-U-G-E success!",
   "members": [
      "olivia@example.com",
      "david@example.com"
   ]
}

Response:

# HTTP-Code 200 Ok

{
   "id": 5,
   "name": "Sample Corp.",
   "shared": false,
   "domain": "",
   "domain_assignment": false,
   "active": true,
   "note": "This was a triumph - I'm making a note here - H-U-G-E success!",
   "updated_by_id": 3,
   "created_by_id": 3,
   "created_at": "2023-08-08T09:12:42.023Z",
   "updated_at": "2023-08-08T09:16:58.922Z",
   "vip": true,
   "member_ids": [
      10,
      11
   ],
   "secondary_member_ids": []
}

Delete

Required permission: admin.organization

Danger

⚠ This is a permanent removal

Please note that removing organizations cannot be undone.

Removing organizations with references in e.g. activity streams or users is not possible via API - this will be indicated by "error": "Can't delete, object has references.". This is not a bug.

Consider using Data Privacy via UI for more control instead.

DELETE-Request sent: /api/v1/organizations/{id}

Response:

# HTTP-Code 200 Ok

{}

Group

Note

  • Please note that follow_up_possible may not work as expected. The possible values are yes or new_ticket!

  • If you want to create or update subgroups, use :: as delimiter for the names. You also have to name the complete hierarchy in the name. Example: Sales::Europe::South

List

Required permission: admin.group

GET-Request sent: /api/v1/groups

Response:

# HTTP-Code 200 Ok

[
   {
      "id": 1,
      "signature_id": 1,
      "email_address_id": null,
      "name": "Sales",
      "assignment_timeout": null,
      "follow_up_possible": "yes",
      "follow_up_assignment": true,
      "active": true,
      "note": "Standard Group/Pool for Tickets.",
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:13.449Z",
      "updated_at": "2021-11-03T11:57:16.357Z",
      "user_ids": [
         3,
         4,
         5
      ]
   },
   {
      "id": 2,
      "signature_id": null,
      "email_address_id": null,
      "name": "2nd Level",
      "assignment_timeout": null,
      "follow_up_possible": "yes",
      "follow_up_assignment": true,
      "active": true,
      "note": null,
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:57:15.802Z",
      "updated_at": "2021-11-03T11:57:16.361Z",
      "user_ids": [
         3,
         4,
         5
      ]
   },
   {
      "id": 3,
      "signature_id": null,
      "email_address_id": null,
      "name": "Service Desk",
      "assignment_timeout": null,
      "follow_up_possible": "yes",
      "follow_up_assignment": true,
      "active": true,
      "note": null,
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:57:15.807Z",
      "updated_at": "2021-11-03T11:57:16.365Z",
      "user_ids": [
         3,
         4,
         5
      ]
   }
]

Show

Required permission: admin.group

GET-Request sent: /api/v1/groups/{id}

Response:

# HTTP-Code 200 Ok

{
   "id": 2,
   "signature_id": null,
   "email_address_id": null,
   "name": "2nd Level",
   "assignment_timeout": null,
   "follow_up_possible": "yes",
   "follow_up_assignment": true,
   "active": true,
   "note": null,
   "updated_by_id": 1,
   "created_by_id": 1,
   "created_at": "2021-11-03T11:57:15.802Z",
   "updated_at": "2021-11-03T11:57:16.361Z",
   "user_ids": [
      3,
      4,
      5
   ]
}

Create

Required permission: admin.group

POST-Request sent: /api/v1/groups

{
  "name": "Amazing Group",
  "signature_id": 1,
  "email_address_id": 1,
  "assignment_timeout": 180,
  "follow_up_possible": "new_ticket",
  "follow_up_assignment": false,
  "active": true,
  "note": "Look at my group, my group is amazing!"
}

Response:

# HTTP-Code 201 Created

{
   "id": 7,
   "signature_id": 1,
   "email_address_id": 3,
   "name": "Amazing Group",
   "assignment_timeout": 180,
   "follow_up_possible": "new_ticket",
   "follow_up_assignment": false,
   "active": true,
   "note": "Look at my group, my group is amazing!",
   "updated_by_id": 3,
   "created_by_id": 3,
   "created_at": "2021-11-08T13:09:41.526Z",
   "updated_at": "2021-11-08T13:09:41.526Z",
   "user_ids": []
}

Update

Required permission: admin.group

PUT-Request sent: /api/v1/groups/{id}

{
  "name": "Amazing Group",
  "signature_id": 1,
  "email_address_id": 3,
  "assignment_timeout": 0,
  "follow_up_possible": "new_ticket",
  "follow_up_assignment": true,
  "active": true,
  "note": "Look at my group, my group is amazing!"
}

Response:

# HTTP-Code 200 Ok

{
   "id": 7,
   "signature_id": 1,
   "email_address_id": 3,
   "name": "Amazing Group",
   "assignment_timeout": 0,
   "follow_up_possible": "new_ticket",
   "follow_up_assignment": true,
   "active": true,
   "note": "Look at my group, my group is amazing!",
   "updated_by_id": 3,
   "created_by_id": 3,
   "created_at": "2021-11-08T13:09:41.526Z",
   "updated_at": "2021-11-08T13:36:24.571Z",
   "user_ids": []
}

Delete

Required permission: admin.group

Danger

⚠ This is a permanent removal

Please note that removing groups cannot be undone.

Removing organizations with references in e.g. activity streams or tickets is not possible via API - this will be indicated by "error": "Can't delete, object has references.". This is not a bug.

Consider setting affected groups to inactive instead or ensure to move all existing tickets to new groups.

DELETE-Request sent: /api/v1/groups/{id}

Response:

# HTTP-Code 200 Ok

{}

Roles

List

Required permission: admin.role

GET -Request sent: /api/v1/roles

Response:

# HTTP-Code 200 OK

[
   {
      "id": 1,
      "name": "Admin",
      "preferences": {},
      "default_at_signup": false,
      "active": true,
      "note": "To configure your system.",
      "updated_by_id": 3,
      "created_by_id": 1,
      "created_at": "2023-07-26T08:44:37.326Z",
      "updated_at": "2023-08-08T09:45:15.315Z",
      "permission_ids": [
         1,
         43,
         55,
         57,
         65
      ],
      "knowledge_base_permission_ids": [],
      "group_ids": {
         "1": [
            "full"
         ],
         "2": [
            "full"
         ],
         "3": [
            "full"
         ]
      }
   },
   {
      "id": 2,
      "name": "Agent",
      "preferences": {},
      "default_at_signup": false,
      "active": true,
      "note": "To work on Tickets!",
      "updated_by_id": 3,
      "created_by_id": 1,
      "created_at": "2023-07-26T08:44:37.362Z",
      "updated_at": "2023-08-22T11:07:16.532Z",
      "permission_ids": [
         43,
         57,
         60,
         62,
         66
      ],
      "knowledge_base_permission_ids": [],
      "group_ids": {
         "1": [
            "full"
         ],
         "2": [
            "full"
         ],
         "3": [
            "full"
         ]
      }
   },
   {
      "id": 3,
      "name": "Service",
      "preferences": {},
      "default_at_signup": false,
      "active": true,
      "note": "Changed text",
      "updated_by_id": 3,
      "created_by_id": 1,
      "created_at": "2023-07-26T08:44:37.379Z",
      "updated_at": "2023-08-22T11:36:49.504Z",
      "permission_ids": [
         57,
         58
      ],
      "knowledge_base_permission_ids": [],
      "group_ids": {
         "1": [
            "full"
         ],
         "2": [
            "full"
         ],
         "3": [
            "full"
         ]
      }
   }
]

Show

Required permission: admin.role

GET -Request sent: /api/v1/roles/{id}

Response:

# HTTP-Code 200 OK

{
   "id": 2,
   "name": "Agent",
   "preferences": {},
   "default_at_signup": false,
   "active": true,
   "note": "To work on Tickets.",
   "updated_by_id": 3,
   "created_by_id": 1,
   "created_at": "2023-07-26T08:44:37.362Z",
   "updated_at": "2023-08-08T09:59:48.202Z",
   "permission_ids": [
      43,
      57,
      60,
      62,
      66
   ],
   "knowledge_base_permission_ids": [],
   "group_ids": {
      "1": [
         "full"
      ],
      "2": [
         "full"
      ],
      "3": [
         "full"
      ]
   }
}

Create

Required permission: admin.role

POST -Request sent: /api/v1/roles

Request:

{
   "active": true,
   "default_at_signup": false,
   "group_ids": {
      "1": "full",
      "2": "full",
      "3": "full"
   },
   "id": "c-12",
   "name": "VIP service",
   "note": "Handling of VIP customers!",
   "permission_ids": [
      "57",
      "58"
   ]
}

Response:

# HTTP-Code 201 Created

{
   "id": 4,
   "name": "VIP service",
   "preferences": {},
   "default_at_signup": false,
   "active": true,
   "note": "Handling of VIP customers!",
   "updated_by_id": 3,
   "created_by_id": 3,
   "created_at": "2023-08-22T11:24:02.114Z",
   "updated_at": "2023-08-22T11:24:02.111Z",
   "permission_ids": [
      57,
      58
   ],
   "knowledge_base_permission_ids": [],
   "group_ids": {
      "1": [
         "full"
      ],
      "2": [
         "full"
      ],
      "3": [
         "full"
      ]
   }
}

Update

Required permission: admin.role

PUT -Request sent: /api/v1/roles/{id}

Request:

{
   "active": true,
   "default_at_signup": false,
   "group_ids": {
      "1": "full",
      "2": "full",
      "3": "full"
   },
   "name": "Service",
   "note": "Changed text",
   "permission_ids": [
      "57",
      "58"
   ]
}

Response:

# HTTP-Code 200 OK

{
   "updated_at": "2023-08-22T11:36:49.136Z",
   "name": "Service",
   "default_at_signup": false,
   "active": true,
   "note": "Changed text",
   "updated_by_id": 3,
   "id": 3,
   "preferences": {},
   "created_by_id": 1,
   "created_at": "2023-07-26T08:44:37.379Z",
   "permission_ids": [
      57,
      58
   ],
   "knowledge_base_permission_ids": [],
   "group_ids": {
      "1": [
         "full"
      ],
      "2": [
         "full"
      ],
      "3": [
         "full"
      ]
   }
}

Calendar

Note

🤓 Calendars belong to Zammads SLA calculation.

List

Required permission: admin.calendar

GET-Request sent: /api/v1/calendars

Status: 200 Ok

[
   {
      "id":2,
      "name":"Test calendar",
      "timezone":"Europe/Berlin",
      "business_hours":{
         "mon":{
            "active":true,
            "timeframes":[
               [
                  "09:00",
                  "17:00"
               ]
            ]
         },
         "tue":{
            "active":true,
            "timeframes":[
               [
                  "09:00",
                  "17:00"
               ]
            ]
         },
         "wed":{
            "active":true,
            "timeframes":[
               [
                  "09:00",
                  "17:00"
               ]
            ]
         },
         "thu":{
            "active":true,
            "timeframes":[
               [
                  "09:00",
                  "17:00"
               ]
            ]
         },
         "fri":{
            "active":true,
            "timeframes":[
               [
                  "09:00",
                  "17:00"
               ]
            ]
         },
         "sat":{
            "active":false,
            "timeframes":[
               [
                  "10:00",
                  "14:00"
               ]
            ]
         },
         "sun":{
            "active":false,
            "timeframes":[
               [
                  "10:00",
                  "14:00"
               ]
            ]
         }
      },
      "default":false,
      "ical_url":"",
      "public_holidays":{
         "2021-11-10":{
            "active":true,
            "summary":"Feast day 1"
         },
         "2021-11-11":{
            "active":true,
            "summary":"Feast day 2"
         }
      },
      "last_log":null,
      "last_sync":"2021-11-10T13:14:20.835Z",
      "updated_by_id":3,
      "created_by_id":3,
      "created_at":"2021-11-10T13:14:20.835Z",
      "updated_at":"2021-11-10T13:14:20.835Z"
   }
]

Show

Required permission: admin.calendar

GET-Request sent: /api/v1/calendars/{id}

Status: 200 Ok

{
   "id":2,
   "name":"Test calendar",
   "timezone":"Europe/Berlin",
   "business_hours":{
      "mon":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "tue":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "wed":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "thu":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "fri":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "sat":{
         "active":false,
         "timeframes":[
            [
               "10:00",
               "14:00"
            ]
         ]
      },
      "sun":{
         "active":false,
         "timeframes":[
            [
               "10:00",
               "14:00"
            ]
         ]
      }
   },
   "default":false,
   "ical_url":"",
   "public_holidays":{
      "2021-11-10":{
         "active":true,
         "summary":"Feast day 1"
      },
      "2021-11-11":{
         "active":true,
         "summary":"Feast day 2"
      }
   },
   "last_log":null,
   "last_sync":"2021-11-10T13:14:20.835Z",
   "updated_by_id":3,
   "created_by_id":3,
   "created_at":"2021-11-10T13:14:20.835Z",
   "updated_at":"2021-11-10T13:14:20.835Z"
}

Create

Required permission: admin.calendar

POST-Request sent: /api/v1/calendars

{
   "name":"Test calendar",
   "timezone":"Europe/Berlin",
   "business_hours":{
      "mon":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "tue":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "wed":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "thu":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "fri":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "sat":{
         "active":false,
         "timeframes":[
            [
               "10:00",
               "14:00"
            ]
         ]
      },
      "sun":{
         "active":false,
         "timeframes":[
            [
               "10:00",
               "14:00"
            ]
         ]
      }
   },
   "ical_url":"",
   "public_holidays":{
      "2021-11-10":{
         "active":true,
         "summary":"Feast day 1"
      },
      "2021-11-11":{
         "active":true,
         "summary":"Feast day 2"
      }
   },
   "note":"",
   "id":"c-1"
}

Response:

Status: 201 Created

{
   "id":2,
   "name":"Test calendar",
   "timezone":"Europe/Berlin",
   "business_hours":{
      "mon":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "tue":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "wed":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "thu":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "fri":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "sat":{
         "active":false,
         "timeframes":[
            [
               "10:00",
               "14:00"
            ]
         ]
      },
      "sun":{
         "active":false,
         "timeframes":[
            [
               "10:00",
               "14:00"
            ]
         ]
      }
   },
   "default":false,
   "ical_url":"",
   "public_holidays":{
      "2021-11-10":{
         "active":true,
         "summary":"Feast day 1"
      },
      "2021-11-11":{
         "active":true,
         "summary":"Feast day 2"
      }
   },
   "last_log":null,
   "last_sync":"2021-11-10T13:14:20.835Z",
   "updated_by_id":3,
   "created_by_id":3,
   "created_at":"2021-11-10T13:14:20.835Z",
   "updated_at":"2021-11-10T13:14:20.835Z"
}

Update

Required permission: admin.calendar

PUT-Request sent: /api/v1/calendars/{id}

{
   "name":"Test calendar Update",
   "timezone":"Europe/Berlin",
   "default":false,
   "business_hours":{
      "mon":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "tue":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "wed":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "thu":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "fri":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "sat":{
         "active":false,
         "timeframes":[
            [
               "10:00",
               "14:00"
            ]
         ]
      },
      "sun":{
         "active":false,
         "timeframes":[
            [
               "10:00",
               "14:00"
            ]
         ]
      }
   },
   "ical_url":"",
   "public_holidays":{
      "2021-11-10":{
         "active":true,
         "summary":"Feast day 1"
      },
      "2021-11-11":{
         "active":true,
         "summary":"Feast day 2"
      }
   },
   "note":"",
   "id":2
}

Response:

Status: 200 Ok

{
   "id":2,
   "name":"Test calendar Update",
   "timezone":"Europe/Berlin",
   "default":false,
   "ical_url":"",
   "business_hours":{
      "mon":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "tue":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "wed":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "thu":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "fri":{
         "active":true,
         "timeframes":[
            [
               "09:00",
               "17:00"
            ]
         ]
      },
      "sat":{
         "active":false,
         "timeframes":[
            [
               "10:00",
               "14:00"
            ]
         ]
      },
      "sun":{
         "active":false,
         "timeframes":[
            [
               "10:00",
               "14:00"
            ]
         ]
      }
   },
   "public_holidays":{
      "2021-11-10":{
         "active":true,
         "summary":"Feast day 1"
      },
      "2021-11-11":{
         "active":true,
         "summary":"Feast day 2"
      }
   },
   "updated_by_id":3,
   "last_log":null,
   "last_sync":"2021-11-10T13:23:07.455Z",
   "created_by_id":3,
   "created_at":"2021-11-10T13:14:20.835Z",
   "updated_at":"2021-11-10T13:23:07.455Z"
}

Delete

Required permission: admin.calendar

Danger

⚠ This is a permanent removal

Please note that removing Calendar configurations cannot be undone.

Removing calendars with references in SLA configurations is not possible via API - this will be indicated by "error": "Can't delete, object has references.". This is not a bug.

DELETE-Request sent: /api/v1/calendars/{id}

Status: 200 Ok

{}

Service-Level Agreements (SLA)

Note

🤓 SLAs depend on Zammads Calendars.

List

Required permission: admin.sla

GET-Request sent: /api/v1/slas

Response:

# HTTP-Code 200 Ok

[
   {
      "id":2,
      "calendar_id":1,
      "name":"new sla",
      "first_response_time":120,
      "response_time":null,
      "update_time":120,
      "solution_time":120,
      "condition":{
         "ticket.state_id":{
            "operator":"is",
            "value":"2"
         }
      },
      "updated_by_id":3,
      "created_by_id":3,
      "created_at":"2021-11-10T12:54:39.368Z",
      "updated_at":"2021-11-10T12:54:39.368Z"
   }
]

Show

Required permission: admin.sla

GET-Request sent: /api/v1/slas/{id}

Response:

# HTTP-Code 200 Ok

{
   "id":2,
   "calendar_id":1,
   "name":"new sla",
   "first_response_time":120,
   "response_time":null,
   "update_time":120,
   "solution_time":120,
   "condition":{
      "ticket.state_id":{
         "operator":"is",
         "value":"2"
      }
   },
   "updated_by_id":3,
   "created_by_id":3,
   "created_at":"2021-11-10T12:54:39.368Z",
   "updated_at":"2021-11-10T12:54:39.368Z"
}

Create

Required permission: admin.sla

POST-Request sent: /api/v1/slas

{
   "name":"new sla",
   "first_response_time":"120",
   "response_time":"",
   "update_time":"120",
   "solution_time":"120",
   "condition":{
      "ticket.state_id":{
         "operator":"is",
         "value":"2"
      }
   },
   "calendar_id":"1",
}

Response:

# HTTP-Code 201 Created

{
   "id":2,
   "calendar_id":1,
   "name":"new sla",
   "first_response_time":120,
   "response_time":null,
   "update_time":120,
   "solution_time":120,
   "condition":{
      "ticket.state_id":{
         "operator":"is",
         "value":"2"
      }
   },
   "updated_by_id":3,
   "created_by_id":3,
   "created_at":"2021-11-10T12:54:39.368Z",
   "updated_at":"2021-11-10T12:54:39.368Z"
}

Update

Required permission: admin.sla

PUT-Request sent: /api/v1/slas/{id}

{
   "name":"update sla",
   "first_response_time":"120",
   "response_time":"",
   "update_time":"120",
   "solution_time":"120",
   "condition":{
      "ticket.state_id":{
         "operator":"is",
         "value":"2"
      }
   },
   "calendar_id":"1",
   "id":2
}

Response:

# HTTP-Code 200 Ok

{
   "id":2,
   "calendar_id":1,
   "name":"update sla",
   "first_response_time":120,
   "response_time":null,
   "update_time":120,
   "solution_time":120,
   "condition":{
      "ticket.state_id":{
         "operator":"is",
         "value":"2"
      }
   },
   "updated_by_id":3,
   "created_by_id":3,
   "created_at":"2021-11-10T12:54:39.368Z",
   "updated_at":"2021-11-10T13:02:52.053Z"
}

Delete

Required permission: admin.sla

Danger

⚠ This is a permanent removal

Please note that removing SLA configurations cannot be undone.

DELETE-Request sent: /api/v1/slas/{id}

Response:

# HTTP-Code 200 Ok

{}

Ticket Endpoints

Zammad comes with many ticket related endpoints. For better overview, they have been splitted in different sections:

Articles

General information about ticket articles

Some attributes of articles might not be straight forward or come with fairly many options - below list hopefully helps you on this journey.

content_type

Zammad supports text/html for HTML formatted text or text/plain for plain text. This allows you to have better formatting options if you need them.

Zammad web UI usually uses text/html.

type

Zammad supports a huge number of article types. Below list may be incomplete depending on your instance and possibly installed add-ons / custom changes.

If not stated otherwise, all article types below are communication articles and thus affecting SLA calculation in Zammad defaults.

The difference is that communication articles provide the option to reply automatically. Which actions exactly are available depends on the article type and e.g. recipient lists.

Reply and forward buttons on email article
email

This allows you to create incoming or outgoing email articles.

However, this highly depends on the chosen sender.

phone

Indicates phone notes.

web

Usually used by customers only. This type is being used when ever your customer uses the web UI to create articles.

note

When ever a communication does not fit (e.g.: internal notes) choose note. Zammad also uses this article type as default fall back.

This is not a communication article.

sms

This type is being used for Zammads SMS integration.

chat

This article type is technically a place holder and is only available via API.

fax

This article type is technically a place holder and is only available via API.

twitter status & twitter direct-message

These articles types are used by Zammads twitter channel. Technically you can use these to automatically respond to existing requests via twitter.

facebook feed post & facebook feed comment

These articles types are used by Zammads facebook channel. Technically you can use these to automatically respond to existing requests via facebook.

telegram personal-message

Used by Zammads Telegram channel. Technically you can use these to automatically respond to existing requests via Telegram.

internal

This attribute allows you to set the visibility of your articles. For internal visible only use true, for visibly for your customers as well use false.

Warning

🔒 Visibility: internal doesn’t mean it’s silent

If you set an article to internal: true but choose to send an email, please be aware that said Email is still being sent out!

sender

Indicates which use did create the article. You can choose from:

  • Agent

  • Customer

  • System

Warning

Depending of above selection, some article types may not be available or behave different. Please be aware that System causes users not being able to read the bodies (this works similar to Zammads trigger displaying in tickets).

List Articles by Ticket

Required permission: ticket.agent or ticket.customer

GET-Request sent: /api/v1/ticket_articles/by_ticket/{ticket id}

Response:

# HTTP-Code 200 OK

[
   {
      "id": 9,
      "ticket_id": 5,
      "type_id": 1,
      "sender_id": 2,
      "from": "David Bell <david@example.com>",
      "to": "order@chrispresso.com",
      "cc": null,
      "subject": null,
      "reply_to": null,
      "message_id": null,
      "message_id_md5": null,
      "in_reply_to": null,
      "content_type": "text/html",
      "references": null,
      "body": "Hi,<br>\n<br>\n<u>please send me:</u><br>\n1 x Café Kopi susu<br>\n4 x Viennese melange<br>\n<br>\n<u>Delivery Address:</u><br>\nDavid Bell<br>\nEiffel Tower<br>\n5 Avenue Anatole France<br>\n75007 Paris<br>\n<span class=\"js-signatureMarker\"></span>\n<br>\nDavid Bell",
      "internal": false,
      "preferences": {},
      "updated_by_id": 10,
      "created_by_id": 10,
      "origin_by_id": null,
      "created_at": "2021-08-02T11:57:18.068Z",
      "updated_at": "2021-08-02T11:57:18.068Z",
      "attachments": [],
      "type": "email",
      "sender": "Customer",
      "created_by": "david@example.com",
      "updated_by": "david@example.com",
      "time_unit": null
   },
   {
      "id": 10,
      "ticket_id": 5,
      "type_id": 1,
      "sender_id": 1,
      "from": "Emma Taylor via <order@chrispresso.com>",
      "to": "David Bell <david@example.com>",
      "cc": null,
      "subject": null,
      "reply_to": null,
      "message_id": null,
      "message_id_md5": null,
      "in_reply_to": null,
      "content_type": "text/html",
      "references": null,
      "body": "Hi David,<br>\n<br>\nnice, we will ship it to your delivery address:<br>\n<br>\nEiffel Tower\n5 Avenue Anatole France\n75007 Paris.<br>\n<br>\nYou will get it till Wednesday.<br>\n<span class=\"js-signatureMarker\"></span>\n<br>\n--<br>\nGreetings,<br>\n<br>\nEmma Taylor<br>",
      "internal": false,
      "preferences": {},
      "updated_by_id": 5,
      "created_by_id": 5,
      "origin_by_id": null,
      "created_at": "2021-08-03T09:57:18.121Z",
      "updated_at": "2021-08-03T09:57:18.121Z",
      "attachments": [],
      "type": "email",
      "sender": "Agent",
      "created_by": "emma@chrispresso.com",
      "updated_by": "emma@chrispresso.com"
   }
]
List specific article

Required permission: ticket.agent or ticket.customer

GET-Request sent: /api/v1/ticket_articles/{article id}

Response:

# HTTP-Code 200 OK

{
   "id": 9,
   "ticket_id": 5,
   "type_id": 1,
   "sender_id": 2,
   "from": "David Bell <david@example.com>",
   "to": "order@chrispresso.com",
   "cc": null,
   "subject": null,
   "reply_to": null,
   "message_id": null,
   "message_id_md5": null,
   "in_reply_to": null,
   "content_type": "text/html",
   "references": null,
   "body": "Hi,<br>\n<br>\n<u>please send me:</u><br>\n1 x Café Kopi susu<br>\n4 x Viennese melange<br>\n<br>\n<u>Delivery Address:</u><br>\nDavid Bell<br>\nEiffel Tower<br>\n5 Avenue Anatole France<br>\n75007 Paris<br>\n<span class=\"js-signatureMarker\"></span>\n<br>\nDavid Bell",
   "internal": false,
   "preferences": {},
   "updated_by_id": 10,
   "created_by_id": 10,
   "origin_by_id": null,
   "created_at": "2021-08-02T11:57:18.068Z",
   "updated_at": "2021-08-02T11:57:18.068Z",
   "attachments": [],
   "type": "email",
   "sender": "Customer",
   "created_by": "david@example.com",
   "updated_by": "david@example.com",
   "time_unit": null
}
Create

Required permission: ticket.agent or ticket.customer

Tip

If you want to create articles on behalf of other users (e.g. for a phone note), use the origin_by_id attribute. ticket.agent permission is mandatory for this.

POST-Request sent: /api/v1/ticket_articles

{
   "ticket_id": 5,
   "subject": "Call note",
   "body": "Called the customer and discussed their issues.<br/>Turns out these were caused by invalid configurations - solved.",
   "content_type": "text/html",
   "type": "phone",
   "internal": false,
   "sender": "Agent",
   "time_unit": "15"
}

Response:

# HTTP-Code 201 Created

{
   "id": 33,
   "ticket_id": 5,
   "type_id": 5,
   "sender_id": 1,
   "from": "Christopher Miller",
   "to": null,
   "cc": null,
   "subject": "Call note",
   "reply_to": null,
   "message_id": null,
   "message_id_md5": null,
   "in_reply_to": null,
   "content_type": "text/html",
   "references": null,
   "body": "Called the customer and discussed their issues.<br>Turns out these were caused by invalid configurations - solved.",
   "internal": false,
   "preferences": {},
   "updated_by_id": 3,
   "created_by_id": 3,
   "origin_by_id": null,
   "created_at": "2021-11-08T16:13:35.962Z",
   "updated_at": "2021-11-08T16:13:35.962Z",
   "attachments": [],
   "type": "phone",
   "sender": "Agent",
   "created_by": "chris@chrispresso.com",
   "updated_by": "chris@chrispresso.com"
}
Receive attachments

Now that you have all those fancy attachments within your tickets, you may want to download specific ones.

GET-Request sent: /api/v1/ticket_attachment/{ticket id}/{article id}/{attachment id}

Response:

Returned image attachment

Hint

If you’re not sure which articles a ticket / article contains, please retrieve affected articles first.

Linking Tickets

Get

Required permission: ticket.agent or admin

GET-Request sent: /api/v1/links

{
   "link_object": "Ticket",
   "link_object_value": "5"
}

Response:

# HTTP-Code 200 Ok

{
   "links": [
      {
         "link_type": "normal",
         "link_object": "Ticket",
         "link_object_value": 41
      }
   ],
   "assets": {
      "Ticket": {
         "41": {
            "id": 41,
            "group_id": 2,
            "priority_id": 2,
            "state_id": 4,
            "organization_id": 1,
            "number": "93039",
            "title": "test",
            "owner_id": 1,
            "customer_id": 2,
            "note": null,
            "first_response_at": null,
            "first_response_escalation_at": null,
            "first_response_in_min": null,
            "first_response_diff_in_min": null,
            "close_at": "2023-08-04T14:37:07.884Z",
            "close_escalation_at": null,
            "close_in_min": null,
            "close_diff_in_min": null,
            "update_escalation_at": null,
            "update_in_min": null,
            "update_diff_in_min": null,
            "last_close_at": "2023-08-04T14:37:07.883Z",
            "last_contact_at": "2023-08-04T12:02:00.036Z",
            "last_contact_agent_at": null,
            "last_contact_customer_at": "2023-08-04T12:02:00.036Z",
            "last_owner_update_at": null,
            "create_article_type_id": 5,
            "create_article_sender_id": 2,
            "article_count": 1,
            "escalation_at": null,
            "pending_time": null,
            "type": null,
            "time_unit": null,
            "preferences": {},
            "updated_by_id": 3,
            "created_by_id": 3,
            "created_at": "2023-08-04T12:01:59.897Z",
            "updated_at": "2023-08-08T09:24:43.977Z",
            "article_ids": [
               64,
               63
            ],
            "ticket_time_accounting_ids": []
         }
      },
      "Group": {
         "2": {
            "id": 2,
            "signature_id": null,
            "email_address_id": 1,
            "name": "2nd Level",
            "assignment_timeout": null,
            "follow_up_possible": "yes",
            "reopen_time_in_days": null,
            "follow_up_assignment": true,
            "active": true,
            "shared_drafts": true,
            "note": "",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.589Z",
            "updated_at": "2023-07-27T13:04:25.495Z",
            "user_ids": [
               3,
               4,
               5
            ]
         },
         "3": {
            "id": 3,
            "signature_id": null,
            "email_address_id": 1,
            "name": "Service Desk",
            "assignment_timeout": null,
            "follow_up_possible": "yes",
            "reopen_time_in_days": null,
            "follow_up_assignment": true,
            "active": true,
            "shared_drafts": true,
            "note": "",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.602Z",
            "updated_at": "2023-07-26T09:28:36.505Z",
            "user_ids": [
               3,
               4,
               5
            ]
         },
         "1": {
            "id": 1,
            "signature_id": 1,
            "email_address_id": 1,
            "name": "Sales",
            "assignment_timeout": null,
            "follow_up_possible": "yes",
            "reopen_time_in_days": null,
            "follow_up_assignment": true,
            "active": true,
            "shared_drafts": true,
            "note": "Standard Group/Pool for Tickets.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:38.651Z",
            "updated_at": "2023-07-26T09:31:54.224Z",
            "user_ids": [
               3,
               4,
               5
            ]
         }
      },
      "User": {
         "1": {
            "id": 1,
            "organization_id": null,
            "login": "-",
            "firstname": "-",
            "lastname": "",
            "email": "",
            "image": null,
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": "",
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": "",
            "vip": false,
            "verified": false,
            "active": false,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {},
            "updated_by_id": 1,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.217Z",
            "updated_at": "2023-07-26T08:44:37.217Z",
            "role_ids": [],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {}
         },
         "3": {
            "id": 3,
            "organization_id": 2,
            "login": "chris@chrispresso.com",
            "firstname": "Christopher",
            "lastname": "Miller",
            "email": "chris@chrispresso.com",
            "image": "7a6a0d1d94ad2037153cf3a6c1b49a53",
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": null,
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": null,
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "last_login": "2023-08-08T08:03:40.962Z",
            "source": null,
            "login_failed": 1,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "locale": "en-us",
               "notification_config": {
                  "matrix": {
                     "create": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "update": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "reminder_reached": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "escalation": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     }
                  }
               },
               "intro": true,
               "theme": "light"
            },
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.807Z",
            "updated_at": "2023-08-08T08:51:50.662Z",
            "role_ids": [
               1,
               2
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {
               "3": [
                  "full"
               ],
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ]
            }
         },
         "4": {
            "id": 4,
            "organization_id": 2,
            "login": "jacob@chrispresso.com",
            "firstname": "Jacob",
            "lastname": "Smith",
            "email": "jacob@chrispresso.com",
            "image": "95afc1244af5cb8b77edcd7224c5d5f8",
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": null,
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": null,
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "locale": "en-us",
               "notification_config": {
                  "matrix": {
                     "create": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "update": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "reminder_reached": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "escalation": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     }
                  }
               }
            },
            "updated_by_id": 1,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:49.390Z",
            "updated_at": "2023-07-26T08:44:49.585Z",
            "role_ids": [
               1,
               2
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {
               "3": [
                  "full"
               ],
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ]
            }
         },
         "5": {
            "id": 5,
            "organization_id": 2,
            "login": "emma@chrispresso.com",
            "firstname": "Emma",
            "lastname": "Taylor",
            "email": "emma@chrispresso.com",
            "image": "b64fef91c29105b4a08a2a69be08eda3",
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": null,
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": null,
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "locale": "en-us",
               "notification_config": {
                  "matrix": {
                     "create": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "update": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "reminder_reached": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "escalation": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     }
                  }
               }
            },
            "updated_by_id": 1,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:49.766Z",
            "updated_at": "2023-07-26T08:44:49.970Z",
            "role_ids": [
               2
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {
               "3": [
                  "full"
               ],
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ]
            }
         },
         "2": {
            "id": 2,
            "organization_id": 1,
            "login": "nicole.braun@zammad.org",
            "firstname": "Nicole",
            "lastname": "Braun",
            "email": "nicole.braun@zammad.org",
            "image": null,
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": "",
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": "",
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "tickets_closed": 22,
               "tickets_open": 1
            },
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:39.646Z",
            "updated_at": "2023-08-04T14:37:11.400Z",
            "role_ids": [
               3
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {}
         }
      },
      "Role": {
         "1": {
            "id": 1,
            "name": "Admin",
            "preferences": {},
            "default_at_signup": false,
            "active": true,
            "note": "To configure your system.",
            "updated_by_id": 1,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.326Z",
            "updated_at": "2023-07-26T08:44:37.326Z",
            "permission_ids": [
               1,
               43,
               55,
               65
            ],
            "knowledge_base_permission_ids": [],
            "group_ids": {}
         },
         "2": {
            "id": 2,
            "name": "Agent",
            "preferences": {},
            "default_at_signup": false,
            "active": true,
            "note": "To work on Tickets.",
            "updated_by_id": 1,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.362Z",
            "updated_at": "2023-07-26T08:44:37.362Z",
            "permission_ids": [
               43,
               57,
               60,
               62,
               66
            ],
            "knowledge_base_permission_ids": [],
            "group_ids": {}
         },
         "3": {
            "id": 3,
            "name": "Customer",
            "preferences": {},
            "default_at_signup": true,
            "active": true,
            "note": "People who create Tickets ask for help.",
            "updated_by_id": 1,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.379Z",
            "updated_at": "2023-07-28T07:22:53.613Z",
            "permission_ids": [
               44,
               47,
               48,
               50,
               54,
               58
            ],
            "knowledge_base_permission_ids": [],
            "group_ids": {}
         }
      },
      "Organization": {
         "2": {
            "name": "Chrispresso Inc.",
            "shared": true,
            "domain": "",
            "domain_assignment": false,
            "active": true,
            "note": "Manufacturer of individual coffee products.",
            "vip": false,
            "updated_by_id": 3,
            "id": 2,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.617Z",
            "updated_at": "2023-08-04T12:01:44.370Z",
            "member_ids": [
               3,
               4,
               5
            ],
            "secondary_member_ids": []
         },
         "1": {
            "id": 1,
            "name": "Zammad Foundation",
            "shared": true,
            "domain": "",
            "domain_assignment": false,
            "active": true,
            "note": "",
            "updated_by_id": 1,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:39.608Z",
            "updated_at": "2023-08-04T12:02:00.018Z",
            "vip": false,
            "member_ids": [
               2
            ],
            "secondary_member_ids": []
         }
      }
   }
}

Add

Required permission: ticket.agent or admin

POST-Request sent: /api/v1/links/add

{
   "link_type": "normal",
   "link_object_target": "Ticket",
   "link_object_target_value": 11,
   "link_object_source": "Ticket",
   "link_object_source_number": "93010"
}

Note

The value for link_object_target has to be the ticket ID. The value for the link_object_source_number has to be the ticket number.

Response:

# HTTP-Code 201 Created

{
   "id": 11,
   "link_type_id": 1,
   "link_object_source_id": 1,
   "link_object_source_value": 10,
   "link_object_target_id": 1,
   "link_object_target_value": 11,
   "created_at": "2023-08-08T11:46:44.108Z",
   "updated_at": "2023-08-08T11:46:44.108Z"
}

Delete

Required permission: ticket.agent or admin

DELETE-Request sent: /api/v1/links/remove

{
   "link_type": "normal",
   "link_object_source": "Ticket",
   "link_object_source_value": 93010,
   "link_object_target": "Ticket",
   "link_object_target_value": 11
}

Response:

# HTTP-Code 201 Created

{ }

Mentions

Warning

The mention endpoint depends on the group permissions and if the user you’re using is an agent. Because of this tickets may or may not be available.

List

Required permission: ticket.agent or ticket.customer

GET-Request sent: /api/v1/mentions

# HTTP-Code 200 Ok

{
  mentions: [
    {
      "id":2,
      "mentionable_type":"Ticket",
      "mentionable_id":1,
      "user_id":3,
      "updated_by_id":3,
      "created_by_id":3,
      "created_at":"2021-03-16T08:51:08.985Z",
      "updated_at":"2021-03-16T08:51:08.985Z"
    },
    {
      "id":3,
      "mentionable_type":"Ticket",
      "mentionable_id":1,
      "user_id":4,
      "updated_by_id":4,
      "created_by_id":4,
      "created_at":"2021-03-16T08:51:08.986Z",
      "updated_at":"2021-03-16T08:51:08.986Z"
    },
  ]
}

Create

Required permission: ticket.agent

POST-Request sent: /api/v1/mentions

{
  "mentionable_type": "Ticket",
  "mentionable_id": 12,
}

Response:

# HTTP-Code 201 Created

{
  "id":2,
  "mentionable_type":"Ticket",
  "mentionable_id":1,
  "user_id":3,
  "updated_by_id":3,
  "created_by_id":3,
  "created_at":"2021-03-16T08:51:08.985Z",
  "updated_at":"2021-03-16T08:51:08.985Z"
}

The mention will be created for the user of the current session.

Delete

Required permission: ticket.agent

DELETE-Request sent: /api/v1/mentions/{id}

Response:

# HTTP-Code 200 Ok

{
  "id":2,
  "mentionable_type":"Ticket",
  "mentionable_id":1,
  "user_id":3,
  "updated_by_id":3,
  "created_by_id":3,
  "created_at":"2021-03-16T08:51:08.985Z",
  "updated_at":"2021-03-16T08:51:08.985Z"
}

Priorities

List

Required permission: admin.object or ticket.agent or ticket.customer

GET-Request sent: /api/v1/ticket_priorities

Response:

# HTTP-Code 200 Ok

[
   {
      "id": 1,
      "name": "1 low",
      "default_create": false,
      "ui_icon": "low-priority",
      "ui_color": "low-priority",
      "note": null,
      "active": true,
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:13.559Z",
      "updated_at": "2021-11-03T11:51:13.572Z"
   },
   {
      "id": 2,
      "name": "2 normal",
      "default_create": true,
      "ui_icon": null,
      "ui_color": null,
      "note": null,
      "active": true,
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:13.570Z",
      "updated_at": "2021-11-03T11:51:13.570Z"
   },
   {
      "id": 3,
      "name": "3 high",
      "default_create": false,
      "ui_icon": "important",
      "ui_color": "high-priority",
      "note": null,
      "active": true,
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:13.579Z",
      "updated_at": "2021-11-03T11:51:13.579Z"
   }
]

Show

Required permission: admin.object or ticket.agent or ticket.customer

GET-Request sent: /api/v1/ticket_priorities/{id}

Response:

# HTTP-Code 200 Ok

{
   "id": 3,
   "name": "3 high",
   "default_create": false,
   "ui_icon": "important",
   "ui_color": "high-priority",
   "note": null,
   "active": true,
   "updated_by_id": 1,
   "created_by_id": 1,
   "created_at": "2021-11-03T11:51:13.579Z",
   "updated_at": "2021-11-03T11:51:13.579Z"
}

Create

Required permission: admin.object

POST-Request sent: /api/v1/ticket_priorities

{
   "name": "4 disaster",
   "default_create": false,
   "ui_icon": "important",
   "ui_color": "high-priority",
   "note": "Added via API for disasterious situations."
}

Response:

# HTTP-Code 201 Created

{
   "id": 4,
   "name": "4 disaster",
   "default_create": false,
   "ui_icon": "important",
   "ui_color": "high-priority",
   "note": "Added via API for disasterious situations.",
   "active": true,
   "updated_by_id": 3,
   "created_by_id": 3,
   "created_at": "2021-11-08T15:31:57.704Z",
   "updated_at": "2021-11-08T15:31:57.704Z"
}

Update

Required permission: admin.object

PUT-Request sent: /api/v1/ticket_priorities/{id}

{
   "ui_icon": "",
   "ui_color": "",
   "note": "Adjusted via API - not so important"
}

Response:

# HTTP-Code 200 Ok

{
   "id": 3,
   "ui_icon": "",
   "ui_color": "",
   "note": "Adjusted via API - not so important",
   "updated_by_id": 3,
   "name": "3 high",
   "default_create": false,
   "active": true,
   "created_by_id": 1,
   "created_at": "2021-11-03T11:51:13.579Z",
   "updated_at": "2021-11-08T15:33:12.181Z"
}

Delete

Required permission: admin.object

Danger

⚠ This is a permanent removal

Please note that removing priorities cannot be undone.

Removing ticket priorities with references in tickets is not possible via API - this will be indicated by "error": "Can't delete, object has references.". This is not a bug.

Consider either setting said priority to active: false or adjust all tickets with the to remove priority to another priority.

DELETE-Request sent: /api/v1/ticket_priorities/{id}

Response:

# HTTP-Code 200 Ok

{}

Shared Drafts

Show

Required permission: ticket.agent.

GET-Request sent: /api/v1/tickets/{ticket id}/shared_draft

Sample response (base64 coded inline image is cut off):

# HTTP-Code 200 OK

{
   "shared_draft_id": 2,
   "assets": {
      "TicketSharedDraftZoom": {
         "2": {
            "id": 2,
            "ticket_id": 58,
            "new_article": {
               "body": "<div>Some text for a shared draft. <br></div><div><br></div><div>Some inline image:<img tabindex=\"0\" style=\"width: 1000px; max-width: 100%;\" src=\"data:image/jpeg;base64,/9j/4AAQSkZJRg[...]",
               "type": "email",
               "internal": false,
               "subtype": "",
               "in_reply_to": "",
               "to": "jane@doe.com",
               "cc": "",
               "subject": "",
               "from": "Christopher Miller",
               "ticket_id": 58,
               "content_type": "text/html",
               "sender_id": 1,
               "type_id": 1,
               "preferences": {
                  "security": {
                     "encryption": {},
                     "sign": {
                        "success": true
                     },
                     "type": "S/MIME"
                  }
               }
            },
            "ticket_attributes": {
               "group_id": "2",
               "owner_id": "4",
               "state_id": "2",
               "priority_id": "2"
            },
            "created_by_id": 3,
            "updated_by_id": 3,
            "created_at": "2023-08-18T13:28:13.279Z",
            "updated_at": "2023-08-18T13:28:13.279Z"
         }
      },
      "User": {
         "3": {
            "login_failed": 0,
            "last_login": "2023-08-18T12:31:24.645Z",
            "updated_by_id": 1,
            "id": 3,
            "organization_id": 2,
            "login": "chris@chrispresso.com",
            "firstname": "Christopher",
            "lastname": "Miller",
            "email": "chris@chrispresso.com",
            "image": "7a6a0d1d94ad2037153cf3a6c1b49a53",
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": "",
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": "",
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "source": null,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "locale": "en-us",
               "notification_config": {
                  "matrix": {
                     "create": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "update": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "reminder_reached": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "escalation": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     }
                  }
               },
               "intro": true,
               "theme": "light",
               "two_factor_authentication": {}
            },
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.807Z",
            "updated_at": "2023-08-18T12:31:24.670Z",
            "role_ids": [
               1,
               2
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         },
         "1": {
            "id": 1,
            "organization_id": null,
            "login": "-",
            "firstname": "-",
            "lastname": "",
            "email": "",
            "image": null,
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": "",
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": "",
            "vip": false,
            "verified": false,
            "active": false,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {},
            "updated_by_id": 1,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.217Z",
            "updated_at": "2023-07-26T08:44:37.217Z",
            "role_ids": [],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {}
         },
         "4": {
            "id": 4,
            "organization_id": 2,
            "login": "jacob@chrispresso.com",
            "firstname": "Jacob",
            "lastname": "Smith",
            "email": "jacob@chrispresso.com",
            "image": "95afc1244af5cb8b77edcd7224c5d5f8",
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": null,
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": null,
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "locale": "en-us",
               "notification_config": {
                  "matrix": {
                     "create": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "update": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "reminder_reached": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "escalation": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     }
                  }
               },
               "theme": "light"
            },
            "updated_by_id": 4,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:49.390Z",
            "updated_at": "2023-08-18T06:43:28.448Z",
            "role_ids": [
               1,
               2
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         },
         "5": {
            "id": 5,
            "organization_id": 2,
            "login": "emma@chrispresso.com",
            "firstname": "Emma",
            "lastname": "Taylor",
            "email": "emma@chrispresso.com",
            "image": "b64fef91c29105b4a08a2a69be08eda3",
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": null,
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": null,
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "locale": "en-us",
               "notification_config": {
                  "matrix": {
                     "create": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "update": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "reminder_reached": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "escalation": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     }
                  }
               },
               "secondaryAction": "closeTab"
            },
            "updated_by_id": 5,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:49.766Z",
            "updated_at": "2023-08-09T09:51:34.110Z",
            "role_ids": [
               2
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         }
      },
      "Role": {
         "1": {
            "id": 1,
            "name": "Admin",
            "preferences": {},
            "default_at_signup": false,
            "active": true,
            "note": "To configure your system.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.326Z",
            "updated_at": "2023-08-08T09:45:15.315Z",
            "permission_ids": [
               1,
               43,
               55,
               57,
               65
            ],
            "knowledge_base_permission_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         },
         "2": {
            "id": 2,
            "name": "Agent",
            "preferences": {},
            "default_at_signup": false,
            "active": true,
            "note": "To work on Tickets.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.362Z",
            "updated_at": "2023-08-08T09:59:48.202Z",
            "permission_ids": [
               43,
               57,
               60,
               62,
               66
            ],
            "knowledge_base_permission_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         }
      },
      "Group": {
         "1": {
            "id": 1,
            "signature_id": 1,
            "email_address_id": 1,
            "name": "Sales",
            "assignment_timeout": null,
            "follow_up_possible": "yes",
            "reopen_time_in_days": null,
            "follow_up_assignment": true,
            "active": true,
            "shared_drafts": true,
            "note": "Standard Group/Pool for Tickets.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:38.651Z",
            "updated_at": "2023-08-08T09:59:48.072Z",
            "user_ids": [
               4,
               5,
               3
            ]
         },
         "2": {
            "id": 2,
            "signature_id": null,
            "email_address_id": 1,
            "name": "2nd Level",
            "assignment_timeout": null,
            "follow_up_possible": "yes",
            "reopen_time_in_days": null,
            "follow_up_assignment": true,
            "active": true,
            "shared_drafts": true,
            "note": "",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.589Z",
            "updated_at": "2023-08-08T09:59:48.148Z",
            "user_ids": [
               4,
               5,
               3
            ]
         },
         "3": {
            "id": 3,
            "signature_id": null,
            "email_address_id": 1,
            "name": "Service Desk",
            "assignment_timeout": null,
            "follow_up_possible": "yes",
            "reopen_time_in_days": null,
            "follow_up_assignment": true,
            "active": true,
            "shared_drafts": true,
            "note": "",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.602Z",
            "updated_at": "2023-08-08T09:59:48.185Z",
            "user_ids": [
               4,
               5,
               3
            ]
         }
      },
      "Organization": {
         "2": {
            "id": 2,
            "name": "Chrispresso Inc.",
            "shared": true,
            "domain": "",
            "domain_assignment": false,
            "active": true,
            "note": "Manufacturer of individual coffee products.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.617Z",
            "updated_at": "2023-08-04T12:01:44.370Z",
            "vip": false,
            "member_ids": [
               3,
               4,
               5
            ],
            "secondary_member_ids": []
         }
      }
   }
}

Create

Required permission: ticket.agent.

PUT-Request sent: /api/v1/tickets/{ticket id}/shared_draft

{
     "form_id": "367646073",
     "new_article": {
             "body": "This is some text.",
             "cc": "",
             "content_type": "text/html",
             "from": "Christopher Miller",
             "in_reply_to": "",
             "internal": true,
             "sender_id": 1,
             "subject": "",
             "subtype": "",
             "ticket_id": 61,
             "to": "",
             "type": "note",
             "type_id": 10
     },
     "ticket_attributes": {
             "group_id": "2",
             "owner_id": "4",
             "priority_id": "2",
             "state_id": "2"
     }
}

Response:

# HTTP-Code 201 Create

{
   "shared_draft_id": 8,
   "assets": {
      "TicketSharedDraftZoom": {
         "8": {
            "id": 8,
            "ticket_id": 61,
            "new_article": {
               "body": "This is some text.",
               "cc": "",
               "content_type": "text/html",
               "from": "Christopher Miller",
               "in_reply_to": "",
               "internal": true,
               "sender_id": 1,
               "subject": "",
               "subtype": "",
               "ticket_id": 61,
               "to": "",
               "type": "note",
               "type_id": 10
            },
            "ticket_attributes": {
               "group_id": "2",
               "owner_id": "4",
               "priority_id": "2",
               "state_id": "2"
            },
            "created_by_id": 3,
            "updated_by_id": 3,
            "created_at": "2023-08-21T06:27:46.889Z",
            "updated_at": "2023-08-21T06:27:46.889Z"
         }
      },
      "User": {
         "3": {
            "login_failed": 0,
            "last_login": "2023-08-21T06:23:06.390Z",
            "updated_by_id": 1,
            "id": 3,
            "organization_id": 2,
            "login": "chris@chrispresso.com",
            "firstname": "Christopher",
            "lastname": "Miller",
            "email": "chris@chrispresso.com",
            "image": "7a6a0d1d94ad2037153cf3a6c1b49a53",
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": "",
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": "",
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "source": null,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "locale": "en-us",
               "notification_config": {
                  "matrix": {
                     "create": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "update": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "reminder_reached": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "escalation": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     }
                  }
               },
               "intro": true,
               "theme": "light",
               "two_factor_authentication": {}
            },
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.807Z",
            "updated_at": "2023-08-21T06:23:06.430Z",
            "role_ids": [
               1,
               2
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         },
         "1": {
            "id": 1,
            "organization_id": null,
            "login": "-",
            "firstname": "-",
            "lastname": "",
            "email": "",
            "image": null,
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": "",
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": "",
            "vip": false,
            "verified": false,
            "active": false,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {},
            "updated_by_id": 1,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.217Z",
            "updated_at": "2023-07-26T08:44:37.217Z",
            "role_ids": [],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {}
         },
         "4": {
            "id": 4,
            "organization_id": 2,
            "login": "jacob@chrispresso.com",
            "firstname": "Jacob",
            "lastname": "Smith",
            "email": "jacob@chrispresso.com",
            "image": "95afc1244af5cb8b77edcd7224c5d5f8",
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": null,
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": null,
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "locale": "en-us",
               "notification_config": {
                  "matrix": {
                     "create": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "update": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "reminder_reached": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "escalation": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     }
                  }
               },
               "theme": "light"
            },
            "updated_by_id": 4,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:49.390Z",
            "updated_at": "2023-08-18T06:43:28.448Z",
            "role_ids": [
               1,
               2
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         },
         "5": {
            "id": 5,
            "organization_id": 2,
            "login": "emma@chrispresso.com",
            "firstname": "Emma",
            "lastname": "Taylor",
            "email": "emma@chrispresso.com",
            "image": "b64fef91c29105b4a08a2a69be08eda3",
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": null,
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": null,
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "locale": "en-us",
               "notification_config": {
                  "matrix": {
                     "create": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "update": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "reminder_reached": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "escalation": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     }
                  }
               },
               "secondaryAction": "closeTab"
            },
            "updated_by_id": 5,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:49.766Z",
            "updated_at": "2023-08-09T09:51:34.110Z",
            "role_ids": [
               2
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         }
      },
      "Role": {
         "1": {
            "id": 1,
            "name": "Admin",
            "preferences": {},
            "default_at_signup": false,
            "active": true,
            "note": "To configure your system.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.326Z",
            "updated_at": "2023-08-08T09:45:15.315Z",
            "permission_ids": [
               1,
               43,
               55,
               57,
               65
            ],
            "knowledge_base_permission_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         },
         "2": {
            "id": 2,
            "name": "Agent",
            "preferences": {},
            "default_at_signup": false,
            "active": true,
            "note": "To work on Tickets.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.362Z",
            "updated_at": "2023-08-08T09:59:48.202Z",
            "permission_ids": [
               43,
               57,
               60,
               62,
               66
            ],
            "knowledge_base_permission_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         }
      },
      "Group": {
         "1": {
            "id": 1,
            "signature_id": 1,
            "email_address_id": 1,
            "name": "Sales",
            "assignment_timeout": null,
            "follow_up_possible": "yes",
            "reopen_time_in_days": null,
            "follow_up_assignment": true,
            "active": true,
            "shared_drafts": true,
            "note": "Standard Group/Pool for Tickets.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:38.651Z",
            "updated_at": "2023-08-08T09:59:48.072Z",
            "user_ids": [
               4,
               5,
               3
            ]
         },
         "2": {
            "id": 2,
            "signature_id": null,
            "email_address_id": 1,
            "name": "2nd Level",
            "assignment_timeout": null,
            "follow_up_possible": "yes",
            "reopen_time_in_days": null,
            "follow_up_assignment": true,
            "active": true,
            "shared_drafts": true,
            "note": "",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.589Z",
            "updated_at": "2023-08-08T09:59:48.148Z",
            "user_ids": [
               4,
               5,
               3
            ]
         },
         "3": {
            "id": 3,
            "signature_id": null,
            "email_address_id": 1,
            "name": "Service Desk",
            "assignment_timeout": null,
            "follow_up_possible": "yes",
            "reopen_time_in_days": null,
            "follow_up_assignment": true,
            "active": true,
            "shared_drafts": true,
            "note": "",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.602Z",
            "updated_at": "2023-08-08T09:59:48.185Z",
            "user_ids": [
               4,
               5,
               3
            ]
         }
      },
      "Organization": {
         "2": {
            "id": 2,
            "name": "Chrispresso Inc.",
            "shared": true,
            "domain": "",
            "domain_assignment": false,
            "active": true,
            "note": "Manufacturer of individual coffee products.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.617Z",
            "updated_at": "2023-08-04T12:01:44.370Z",
            "vip": false,
            "member_ids": [
               3,
               4,
               5
            ],
            "secondary_member_ids": []
         }
      }
   }
}

Update

Required permission: ticket.agent

PATCH-Request sent: /api/v1/tickets/{ticket id}/shared_draft

{
     "form_id": "367646073",
     "new_article": {
             "body": "Changed body.",
             "cc": "",
             "content_type": "text/html",
             "from": "Jacob Smith",
             "in_reply_to": "",
             "internal": false,
             "preferences": {
                     "security": {
                             "encryption": {},
                             "sign": {
                                     "success": true
                             },
                             "type": "S/MIME"
                     }
             },
             "sender_id": 1,
             "subject": "",
             "subtype": "",
             "ticket_id": 61,
             "to": "nicole.braun@zammad.org",
             "type": "email",
             "type_id": 1
     }
}

Response:

# HTTP-Code 200 OK

{
   "shared_draft_id": 8,
   "assets": {
      "TicketSharedDraftZoom": {
         "8": {
            "ticket_id": 61,
            "new_article": {
               "body": "Changed body.",
               "cc": "",
               "content_type": "text/html",
               "from": "Jacob Smith",
               "in_reply_to": "",
               "internal": false,
               "preferences": {
                  "security": {
                     "encryption": {},
                     "sign": {
                        "success": true
                     },
                     "type": "S/MIME"
                  }
               },
               "sender_id": 1,
               "subject": "",
               "subtype": "",
               "ticket_id": 61,
               "to": "nicole.braun@zammad.org",
               "type": "email",
               "type_id": 1
            },
            "updated_by_id": 3,
            "id": 8,
            "ticket_attributes": {
               "group_id": "2",
               "owner_id": "4",
               "priority_id": "2",
               "state_id": "2"
            },
            "created_by_id": 3,
            "created_at": "2023-08-21T06:27:46.889Z",
            "updated_at": "2023-08-21T06:39:14.776Z"
         }
      },
      "User": {
         "3": {
            "login_failed": 0,
            "last_login": "2023-08-21T06:23:06.390Z",
            "updated_by_id": 1,
            "id": 3,
            "organization_id": 2,
            "login": "chris@chrispresso.com",
            "firstname": "Christopher",
            "lastname": "Miller",
            "email": "chris@chrispresso.com",
            "image": "7a6a0d1d94ad2037153cf3a6c1b49a53",
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": "",
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": "",
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "source": null,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "locale": "en-us",
               "notification_config": {
                  "matrix": {
                     "create": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "update": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "reminder_reached": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "escalation": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     }
                  }
               },
               "intro": true,
               "theme": "light",
               "two_factor_authentication": {}
            },
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.807Z",
            "updated_at": "2023-08-21T06:23:06.430Z",
            "role_ids": [
               1,
               2
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         },
         "1": {
            "id": 1,
            "organization_id": null,
            "login": "-",
            "firstname": "-",
            "lastname": "",
            "email": "",
            "image": null,
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": "",
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": "",
            "vip": false,
            "verified": false,
            "active": false,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {},
            "updated_by_id": 1,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.217Z",
            "updated_at": "2023-07-26T08:44:37.217Z",
            "role_ids": [],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {}
         },
         "4": {
            "id": 4,
            "organization_id": 2,
            "login": "jacob@chrispresso.com",
            "firstname": "Jacob",
            "lastname": "Smith",
            "email": "jacob@chrispresso.com",
            "image": "95afc1244af5cb8b77edcd7224c5d5f8",
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": null,
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": null,
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "locale": "en-us",
               "notification_config": {
                  "matrix": {
                     "create": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "update": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "reminder_reached": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "escalation": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     }
                  }
               },
               "theme": "light"
            },
            "updated_by_id": 4,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:49.390Z",
            "updated_at": "2023-08-18T06:43:28.448Z",
            "role_ids": [
               1,
               2
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         },
         "5": {
            "id": 5,
            "organization_id": 2,
            "login": "emma@chrispresso.com",
            "firstname": "Emma",
            "lastname": "Taylor",
            "email": "emma@chrispresso.com",
            "image": "b64fef91c29105b4a08a2a69be08eda3",
            "image_source": null,
            "web": "",
            "phone": "",
            "fax": "",
            "mobile": "",
            "department": null,
            "street": "",
            "zip": "",
            "city": "",
            "country": "",
            "address": null,
            "vip": false,
            "verified": false,
            "active": true,
            "note": "",
            "last_login": null,
            "source": null,
            "login_failed": 0,
            "out_of_office": false,
            "out_of_office_start_at": null,
            "out_of_office_end_at": null,
            "out_of_office_replacement_id": null,
            "preferences": {
               "locale": "en-us",
               "notification_config": {
                  "matrix": {
                     "create": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "update": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": true,
                           "subscribed": true,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "reminder_reached": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     },
                     "escalation": {
                        "criteria": {
                           "owned_by_me": true,
                           "owned_by_nobody": false,
                           "subscribed": false,
                           "no": false
                        },
                        "channel": {
                           "email": true,
                           "online": true
                        }
                     }
                  }
               },
               "secondaryAction": "closeTab"
            },
            "updated_by_id": 5,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:49.766Z",
            "updated_at": "2023-08-09T09:51:34.110Z",
            "role_ids": [
               2
            ],
            "two_factor_preference_ids": [],
            "organization_ids": [],
            "authorization_ids": [],
            "overview_sorting_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         }
      },
      "Role": {
         "1": {
            "id": 1,
            "name": "Admin",
            "preferences": {},
            "default_at_signup": false,
            "active": true,
            "note": "To configure your system.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.326Z",
            "updated_at": "2023-08-08T09:45:15.315Z",
            "permission_ids": [
               1,
               43,
               55,
               57,
               65
            ],
            "knowledge_base_permission_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         },
         "2": {
            "id": 2,
            "name": "Agent",
            "preferences": {},
            "default_at_signup": false,
            "active": true,
            "note": "To work on Tickets.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:37.362Z",
            "updated_at": "2023-08-08T09:59:48.202Z",
            "permission_ids": [
               43,
               57,
               60,
               62,
               66
            ],
            "knowledge_base_permission_ids": [],
            "group_ids": {
               "1": [
                  "full"
               ],
               "2": [
                  "full"
               ],
               "3": [
                  "full"
               ]
            }
         }
      },
      "Group": {
         "1": {
            "id": 1,
            "signature_id": 1,
            "email_address_id": 1,
            "name": "Sales",
            "assignment_timeout": null,
            "follow_up_possible": "yes",
            "reopen_time_in_days": null,
            "follow_up_assignment": true,
            "active": true,
            "shared_drafts": true,
            "note": "Standard Group/Pool for Tickets.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:38.651Z",
            "updated_at": "2023-08-08T09:59:48.072Z",
            "user_ids": [
               4,
               5,
               3
            ]
         },
         "2": {
            "id": 2,
            "signature_id": null,
            "email_address_id": 1,
            "name": "2nd Level",
            "assignment_timeout": null,
            "follow_up_possible": "yes",
            "reopen_time_in_days": null,
            "follow_up_assignment": true,
            "active": true,
            "shared_drafts": true,
            "note": "",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.589Z",
            "updated_at": "2023-08-08T09:59:48.148Z",
            "user_ids": [
               4,
               5,
               3
            ]
         },
         "3": {
            "id": 3,
            "signature_id": null,
            "email_address_id": 1,
            "name": "Service Desk",
            "assignment_timeout": null,
            "follow_up_possible": "yes",
            "reopen_time_in_days": null,
            "follow_up_assignment": true,
            "active": true,
            "shared_drafts": true,
            "note": "",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.602Z",
            "updated_at": "2023-08-08T09:59:48.185Z",
            "user_ids": [
               4,
               5,
               3
            ]
         }
      },
      "Organization": {
         "2": {
            "id": 2,
            "name": "Chrispresso Inc.",
            "shared": true,
            "domain": "",
            "domain_assignment": false,
            "active": true,
            "note": "Manufacturer of individual coffee products.",
            "updated_by_id": 3,
            "created_by_id": 1,
            "created_at": "2023-07-26T08:44:48.617Z",
            "updated_at": "2023-08-04T12:01:44.370Z",
            "vip": false,
            "member_ids": [
               3,
               4,
               5
            ],
            "secondary_member_ids": []
         }
      }
   }
}

Remove

Required permission: ticket.agent

DELETE-Request sent: /api/v1/tickets/{ticket id}/shared_draft

Response:

# HTTP-Code 200 OK

{
   "shared_draft_id": 3
}

States

Warning

Creating, changing or removing states via below endpoints is not recommended! You can do this in UI, please have a look here.

List

Required permission: admin.object or ticket.agent or ticket.customer

GET-Request sent: /api/v1/ticket_states

Response:

# HTTP-Code 200 Ok

[
   {
      "id": 1,
      "state_type_id": 1,
      "name": "new",
      "next_state_id": null,
      "ignore_escalation": false,
      "default_create": true,
      "default_follow_up": false,
      "note": null,
      "active": true,
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:13.504Z",
      "updated_at": "2021-11-03T11:51:13.520Z"
   },
   {
      "id": 2,
      "state_type_id": 2,
      "name": "open",
      "next_state_id": null,
      "ignore_escalation": false,
      "default_create": false,
      "default_follow_up": true,
      "note": null,
      "active": true,
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:13.518Z",
      "updated_at": "2021-11-03T11:51:13.518Z"
   },
   {
      "id": 3,
      "state_type_id": 3,
      "name": "pending reminder",
      "next_state_id": null,
      "ignore_escalation": true,
      "default_create": false,
      "default_follow_up": false,
      "note": null,
      "active": true,
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:13.528Z",
      "updated_at": "2021-11-03T11:51:13.528Z"
   },
   {
      "id": 4,
      "state_type_id": 5,
      "name": "closed",
      "next_state_id": null,
      "ignore_escalation": true,
      "default_create": false,
      "default_follow_up": false,
      "note": null,
      "active": true,
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:13.535Z",
      "updated_at": "2021-11-03T11:51:13.535Z"
   },
   {
      "id": 5,
      "state_type_id": 6,
      "name": "merged",
      "next_state_id": null,
      "ignore_escalation": true,
      "default_create": false,
      "default_follow_up": false,
      "note": null,
      "active": true,
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:13.540Z",
      "updated_at": "2021-11-03T11:51:13.540Z"
   },
   {
      "id": 6,
      "state_type_id": 7,
      "name": "removed",
      "next_state_id": null,
      "ignore_escalation": true,
      "default_create": false,
      "default_follow_up": false,
      "note": null,
      "active": false,
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:13.546Z",
      "updated_at": "2021-11-03T11:51:13.546Z"
   },
   {
      "id": 7,
      "state_type_id": 4,
      "name": "pending close",
      "next_state_id": 4,
      "ignore_escalation": true,
      "default_create": false,
      "default_follow_up": false,
      "note": null,
      "active": true,
      "updated_by_id": 1,
      "created_by_id": 1,
      "created_at": "2021-11-03T11:51:13.553Z",
      "updated_at": "2021-11-03T11:51:13.553Z"
   }
]

Show

Required permission: admin.object or ticket.agent or ticket.customer

GET-Request sent: /api/v1/ticket_states/{id}

Response:

# HTTP-Code 200 Ok

{
   "id": 4,
   "state_type_id": 5,
   "name": "closed",
   "next_state_id": null,
   "ignore_escalation": true,
   "default_create": false,
   "default_follow_up": false,
   "note": null,
   "active": true,
   "updated_by_id": 1,
   "created_by_id": 1,
   "created_at": "2021-11-03T11:51:13.535Z",
   "updated_at": "2021-11-03T11:51:13.535Z"
}

Create

Required permission: admin.object

Note

Below payload makes use of state_type_id which is a instance specific set of IDs. State types indicate how the state will work.

As there’s no endpoint for retreiving these, please use the rails console.

POST-Request sent: /api/v1/ticket_states

{
   "name": "in progress",
   "state_type_id": 2,
   "ignore_escalation": true,
   "active": true
}

Response:

# HTTP-Code 201 Created

{
   "id": 8,
   "state_type_id": 2,
   "name": "in progress",
   "next_state_id": null,
   "ignore_escalation": true,
   "default_create": false,
   "default_follow_up": false,
   "note": null,
   "active": true,
   "updated_by_id": 3,
   "created_by_id": 3,
   "created_at": "2021-11-08T15:08:21.671Z",
   "updated_at": "2021-11-08T15:08:21.671Z"
}

Update

Required permission: admin.object

PUT-Request sent: /api/v1/ticket_states/{id}

{
   "note": "State created & updated via API"
}

Response:

# HTTP-Code 200 Ok

{
   "id": 8,
   "note": "State created &amp; updated via API",
   "updated_by_id": 3,
   "name": "in progress",
   "state_type_id": 2,
   "next_state_id": null,
   "ignore_escalation": true,
   "default_create": false,
   "default_follow_up": false,
   "active": true,
   "created_by_id": 3,
   "created_at": "2021-11-08T15:08:21.671Z",
   "updated_at": "2021-11-08T15:13:32.370Z"
}

Delete

Required permission: admin.object

Danger

⚠ This is a permanent removal

Please note that removing ticket states cannot be undone.

Removing ticket states with references in tickets is not possible via API - this will be indicated by "error": "Can't delete, object has references.". This is not a bug.

Consider either setting said state to active: false or adjust all tickets with the to remove state to another state.

DELETE-Request sent: /api/v1/ticket_states/{id}

Response:

# HTTP-Code 200 Ok

{}

Tags

Ticket scope

List

Required permission: ticket.agent or admin.tag

GET-Request sent: /api/v1/tags?object=Ticket&o_id={ticket id}

Sample response:

# HTTP-Code 200 OK

{
    "tags": [
        "americano",
        "complaint"
    ]
}
Add

Required permission: ticket.agent or admin.tag

POST-Request sent: /api/v1/tags/add

{
    "item": "{tag name}",
    "object": "Ticket",
    "o_id": {ticket id}
}

Hint

This will create the tag if it doesn’t exist and the user has permission to do so.

Response:

# HTTP-Code 201 Created

true
Remove

Required permission: ticket.agent or admin.tag

DELETE-Request sent: /api/v1/tags/remove

{
    "item": "{tag name}",
    "object": "Ticket",
    "o_id": "{ticket id}"
}

Response:

# HTTP-Code 201 Created

true

Administration scope

Admin - List

Required permission: admin.tag

GET-Request sent: /api/v1/tag_list

Sample response:

# HTTP-Code 200 OK

[
    {
        "id": 1,
        "name": "americano",
        "count": 0
    },
    {
        "id": 2,
        "name": "complaint",
        "count": 0
    },
    {
        "id": 3,
        "name": "viennese melange",
        "count": 0
    }
]
Admin - Create

Required permission: admin.tag

POST-Request sent: /api/v1/tag_list

{
  "name": "tag 5"
}

Response:

# HTTP-Code 200 OK

{}
Admin - Rename

Required permission: admin.tag

PUT-Request sent: /api/v1/tag_list/{tag id}

{
  "name": "order"
}

Response:

# HTTP-Code 200 OK

{}
Admin - Delete

Required permission: admin.tag

DELETE-Request sent: /api/v1/tag_list/{tag id}

Response:

# HTTP-Code 200 OK

{}

Tickets

Warning

Ticket endpoints depend on group permissions and if the user you’re using is an agent. Because of this tickets may or may not be available.

List

Required permission: ticket.agent or ticket.customer

GET-Request sent: /api/v1/tickets

Response:

# HTTP-Code 200 Ok

[
   {
      "id": 1,
      "group_id": 1,
      "priority_id": 2,
      "state_id": 1,
      "organization_id": 1,
      "number": "22001",
      "title": "Welcome to Zammad!",
      "owner_id": 1,
      "customer_id": 2,
      "note": null,
      "first_response_at": null,
      "first_response_escalation_at": null,
      "first_response_in_min": null,
      "first_response_diff_in_min": null,
      "close_at": null,
      "close_escalation_at": null,
      "close_in_min": null,
      "close_diff_in_min": null,
      "update_escalation_at": null,
      "update_in_min": null,
      "update_diff_in_min": null,
      "last_contact_at": "2021-11-03T11:51:13.790Z",
      "last_contact_agent_at": null,
      "last_contact_customer_at": "2021-11-03T11:51:13.790Z",
      "last_owner_update_at": null,
      "create_article_type_id": 5,
      "create_article_sender_id": 2,
      "article_count": 1,
      "escalation_at": null,
      "pending_time": null,
      "type": null,
      "time_unit": null,
      "preferences": {},
      "updated_by_id": 2,
      "created_by_id": 2,
      "created_at": "2021-11-03T11:51:13.759Z",
      "updated_at": "2021-11-03T11:51:13.809Z"
   },
   {
      "id": 2,
      "group_id": 1,
      "priority_id": 2,
      "state_id": 4,
      "organization_id": 3,
      "number": "22002",
      "title": "Order 777555",
      "owner_id": 3,
      "customer_id": 6,
      "note": null,
      "first_response_at": null,
      "first_response_escalation_at": null,
      "first_response_in_min": null,
      "first_response_diff_in_min": null,
      "close_at": null,
      "close_escalation_at": null,
      "close_in_min": null,
      "close_diff_in_min": null,
      "update_escalation_at": null,
      "update_in_min": null,
      "update_diff_in_min": null,
      "last_contact_at": "2021-05-04T16:57:17.920Z",
      "last_contact_agent_at": "2021-05-03T10:57:17.904Z",
      "last_contact_customer_at": "2021-05-04T16:57:17.920Z",
      "last_owner_update_at": null,
      "create_article_type_id": 1,
      "create_article_sender_id": 2,
      "article_count": 3,
      "escalation_at": null,
      "pending_time": null,
      "type": null,
      "time_unit": null,
      "preferences": {},
      "updated_by_id": 6,
      "created_by_id": 6,
      "created_at": "2021-05-03T09:57:17.837Z",
      "updated_at": "2021-11-03T11:57:17.927Z"
   },

   ...
]

Show

Required permission: ticket.agent or ticket.customer

GET-Request sent: /api/v1/tickets/{ticket id}

Response:

# HTTP-Code 200 Ok

{
   "id": 3,
   "group_id": 1,
   "priority_id": 2,
   "state_id": 4,
   "organization_id": 3,
   "number": "22003",
   "title": "Order 787556",
   "owner_id": 3,
   "customer_id": 7,
   "note": null,
   "first_response_at": null,
   "first_response_escalation_at": null,
   "first_response_in_min": null,
   "first_response_diff_in_min": null,
   "close_at": null,
   "close_escalation_at": null,
   "close_in_min": null,
   "close_diff_in_min": null,
   "update_escalation_at": null,
   "update_in_min": null,
   "update_diff_in_min": null,
   "last_contact_at": "2021-06-03T09:57:17.987Z",
   "last_contact_agent_at": "2021-06-03T09:57:17.987Z",
   "last_contact_customer_at": "2021-06-01T11:57:17.935Z",
   "last_owner_update_at": null,
   "create_article_type_id": 1,
   "create_article_sender_id": 2,
   "article_count": 2,
   "escalation_at": null,
   "pending_time": null,
   "type": null,
   "time_unit": null,
   "preferences": {},
   "updated_by_id": 4,
   "created_by_id": 7,
   "created_at": "2021-06-01T11:57:17.935Z",
   "updated_at": "2021-11-03T11:57:17.997Z"
}

Create

Required permission: ticket.agent or ticket.customer

Tip

🐱‍👤 On behalf of users

If you want to create tickets on behalf of other users, use the customer_id attribute. ticket.agent is mandatory for this. Use guess:{email address} to save an API call if you don’t know the user’s ID or want to create the user in question ("customer_id": "guess:jane@doe.com").

📣 Add mention subscription right away

Add the mentions attribute to your ticket payload and provide an array of user ids to directly subscribe them during ticket creation.

E.g.: "mentions": [1, 5, 7, 8],

POST-Request sent: /api/v1/tickets

{
   "title": "Help me!",
   "group": "2nd Level",
   "customer": "david@example.com",
   "article": {
      "subject": "My subject",
      "body": "I am a message!",
      "type": "note",
      "internal": false
   }
}

Response:

# HTTP-Code 201 Created

{
   "id": 19,
   "group_id": 2,
   "priority_id": 2,
   "state_id": 1,
   "organization_id": null,
   "number": "22019",
   "title": "Help me!",
   "owner_id": 1,
   "customer_id": 10,
   "note": null,
   "first_response_at": null,
   "first_response_escalation_at": null,
   "first_response_in_min": null,
   "first_response_diff_in_min": null,
   "close_at": null,
   "close_escalation_at": null,
   "close_in_min": null,
   "close_diff_in_min": null,
   "update_escalation_at": null,
   "update_in_min": null,
   "update_diff_in_min": null,
   "last_contact_at": null,
   "last_contact_agent_at": null,
   "last_contact_customer_at": null,
   "last_owner_update_at": null,
   "create_article_type_id": 10,
   "create_article_sender_id": 1,
   "article_count": 1,
   "escalation_at": null,
   "pending_time": null,
   "type": null,
   "time_unit": null,
   "preferences": {},
   "updated_by_id": 3,
   "created_by_id": 3,
   "created_at": "2021-11-08T14:17:41.913Z",
   "updated_at": "2021-11-08T14:17:41.994Z",
   "article_ids": [
      30
   ],
   "ticket_time_accounting_ids": []
}

Hint

For more article attributes and options have a look into Articles.

Update

Required permission: ticket.agent or ticket.customer

PUT-Request sent: /api/v1/tickets/{ticket id}

{
   "title": "No help for you",
   "group": "Sales",
   "state": "open",
   "priority": "3 high",
   "article": {
      "subject": "Update via API",
      "body": "Here's my reason for updating this ticket...",
      "internal": true
   }
}

Note

Above example provides an article. This article is a new article and does not affect any existing ones.

Response:

# HTTP-Code 200 Ok

{
   "id": 19,
   "group_id": 1,
   "priority_id": 3,
   "state_id": 2,
   "organization_id": null,
   "number": "22019",
   "title": "No help for you",
   "owner_id": 1,
   "customer_id": 10,
   "note": null,
   "first_response_at": null,
   "first_response_escalation_at": null,
   "first_response_in_min": null,
   "first_response_diff_in_min": null,
   "close_at": null,
   "close_escalation_at": null,
   "close_in_min": null,
   "close_diff_in_min": null,
   "update_escalation_at": null,
   "update_in_min": null,
   "update_diff_in_min": null,
   "last_contact_at": null,
   "last_contact_agent_at": null,
   "last_contact_customer_at": null,
   "last_owner_update_at": null,
   "create_article_type_id": 10,
   "create_article_sender_id": 1,
   "article_count": 2,
   "escalation_at": null,
   "pending_time": null,
   "type": null,
   "time_unit": null,
   "preferences": {},
   "updated_by_id": 3,
   "created_by_id": 3,
   "created_at": "2021-11-08T14:17:41.913Z",
   "updated_at": "2021-11-08T14:18:53.426Z",
   "article_ids": [
      31,
      30
   ],
   "ticket_time_accounting_ids": []
}

Tip

Adding attachments

Attachment payloads are identical to the POST method, just use PUT instead.

Delete

Required permission: admin

Danger

⚠ This is a permanent removal

Please note that removing tickets cannot be undone. All data (e.g.: articles & attachments) will be lost.

DELETE-Request sent: /api/v1/tickets/{ticket id}

Response:

# HTTP-Code 200 Ok
{}

Time Accounting

List

Required permission: ticket.agent or admin.time_accounting

GET-Request sent: /api/v1/tickets/{ticket id}/time_accountings

Sample response:

# HTTP-Code 200 OK

[
   {
      "id": 6,
      "ticket_id": 50,
      "ticket_article_id": 87,
      "time_unit": "15.0",
      "type_id": 3,
      "created_by_id": 3,
      "created_at": "2023-08-16T08:11:49.315Z",
      "updated_at": "2023-08-16T08:11:49.315Z"
   },
   {
      "id": 7,
      "ticket_id": 50,
      "ticket_article_id": 88,
      "time_unit": "30.0",
      "type_id": 2,
      "created_by_id": 3,
      "created_at": "2023-08-16T08:12:02.249Z",
      "updated_at": "2023-08-16T08:12:02.249Z"
   },
   {
      "id": 8,
      "ticket_id": 50,
      "ticket_article_id": 89,
      "time_unit": "35.0",
      "type_id": 4,
      "created_by_id": 3,
      "created_at": "2023-08-16T08:12:29.910Z",
      "updated_at": "2023-08-16T08:12:29.910Z"
   }
]

Show

Required permission: ticket.agent or admin.time_accounting

GET-Request sent: /api/v1/tickets/{ticket id}/time_accountings/{timeaccounting id}

Sample response:

# HTTP-Code 200 OK

{
   "id": 7,
   "ticket_id": 50,
   "ticket_article_id": 88,
   "time_unit": "30.0",
   "type_id": 2,
   "created_by_id": 3,
   "created_at": "2023-08-16T08:12:02.249Z",
   "updated_at": "2023-08-16T08:12:02.249Z"
}

Create

Required permission: ticket.agent or admin.time_accounting

POST-Request sent: /api/v1/tickets/{ticket id}/time_accountings

{
   "time_unit": "60.0",
   "type_id": 4
}

Response:

# HTTP-Code 201 Created

{
   "id": 9,
   "ticket_id": 50,
   "ticket_article_id": null,
   "time_unit": "60.0",
   "type_id": 4,
   "created_by_id": 3,
   "created_at": "2023-08-16T08:30:36.138Z",
   "updated_at": "2023-08-16T08:30:36.138Z"
}

Update

Required permission: admin.time_accounting

PUT-Request sent: /api/v1/tickets/{ticket id}/time_accounting/{timeaccounting id}

{
   "id": 7,
   "time_unit": "15.0",
   "type_id": 4
}

Response:

# HTTP-Code 200 OK

{
   "ticket_id": 50,
   "time_unit": "15.0",
   "type_id": 4,
   "id": 7,
   "ticket_article_id": 88,
   "created_by_id": 3,
   "created_at": "2023-08-16T08:12:02.249Z",
   "updated_at": "2023-08-16T08:24:00.788Z"
}

Remove

Required permission: admin.time_accounting

DELETE-Request sent: /api/v1/tickets/{ticket id}/time_accountings/{timeaccounting id}

Response:

# HTTP-Code 200 OK

Generic CTI

This page describes the generic CTI API scopes and functionalities.

Warning

🚧 Limitations / Notes🚧

  • Authentication on this endpoint works fundamentally different compared to the rest of the API.

  • API clients do not work with the CTI endpoints unless explicitly stated by the client vendor!

  • The CTI endpoints are relevant for PBX systems only.

Features

Here’s a small condensed list of the possibilities this CTI API provides.

Inbound
Outbound
Endpoint

The endpoint can be found in the generic CTI integration and contains a unique token which acts as authentication. Make sure to keep this endpoint URL safe.

Hint

Generic CTI configuration and the correct endpoint can be found in your Zammad integration settings and are documented in our admin documentation.

Please also note the there listed requirements and limitations.
All options that require returns (e.g. blocking, manipulating outgoing caller IDs) rely on configurations within the Zammad CTI integration page.
Events

There are several events in terms of an ongoing call. These actions always come from your PBX system and may be:

In some situations Zammad may provide a return on your PBX calls (e.g. a reject) if you blocked a specific caller. Zammad will never initiate specific actions with your PBX. Zammad is a passive component in all described cases.

New call

Available attributes and sample data for newCall events

Attribute

Possible value

Description

event

newCall

Tell Zammad there’s a new call

from

e.g. 493055571600, 02214710334, anonymous

Number that initiated the call

to

e.g. 49221470334, 03023125771

Number that is being called

direction

in or out

The call direction - if your agent initiates a call this will be out

callId

e.g. 53ba82e2bd6d12d9fb2d3838f0cfb070, 5fb9532f40da834a, 123456789

An ID that is unique for the call. Zammad will use this ID to identify an existing call with following actions (e.g. like answering or hanging up)

This ID must be unique per call session.

user

e.g. John Doe, [Alice, Bob]

The user(s) real name involved. You may have to provide array style ([]) params depending on the call method you choose.

If the direction is out, this is the name of the calling person(s).
If the direction is in, this is the name of the called person(s).

This value is optional.

queue

e.g. support, sales

An optional queue name, this option is relevant for the Caller Log Filter

There’s two options on how to POST the relevant data to Zammad.

Example:

Below calls have been sent with the following configuration. This is important for you to understand the returns we’re showing here.

Outbound:

  • Destination caller ID 4989* set outbound caller ID 498999998145 with note “All from munich”

  • Destination caller ID 4930* set outbound caller ID 493023125877 “All from Berlin”

Other settings:

  • Default caller ID for outbound calls 496990009111

POST-Request sent: https://{FQDN-Zammad}/api/v1/cti/{instance specific token}

Outbound

Payload:

{
   "event": "newCall",
   "from": "493023125741",
   "to": "492214710334",
   "direction": "out",
   "callId": "f4ebd2be-7b9a-4d58-94c2-eb06a3c2ce76",
   "user": "Christopher Miller"
}

Returns:

{
   "action": "dial",
   "caller_id": "496990009111",
   "number": "492214710334"
}

Sample curl command:

$ curl --request POST 'https://{FQDN-Zammad}/api/v1/cti/{instance specific token}' \
   --header 'Content-Type: application/json' \
   --data-raw '{
      "event": "newCall",
      "from": "493023125741",
      "to": "492214710334",
      "direction": "out",
      "callId": "f4ebd2be-7b9a-4d58-94c2-eb06a3c2ce76",
      "user": "Christopher Miller"
   }'
Inbound

Payload:

{
   "event": "newCall",
   "from": "493023125741",
   "to": "492214710334",
   "direction": "in",
   "callId": "307fa962-de8d-4ffc-817b-7f6993204159",
   "user": ["Christopher Miller", "Emma Taylor"]
}

Response:

{}

Sample curl command:

$ curl --request POST 'https://{FQDN-Zammad}/api/v1/cti/{instance specific token}' \
   --header 'Content-Type: application/json' \
   --data-raw '{
      "event": "newCall",
      "from": "493023125741",
      "to": "492214710334",
      "direction": "in",
      "callId": "307fa962-de8d-4ffc-817b-7f6993204159",
      "user": ["Christopher Miller", "Emma Taylor"]
   }'

Situation specific responses

Depending on the chosen call direction, Zammad will return either a (optionally) configured call ID or (optionally) block a caller. If your Zammad hasn’t configured one or both options, the return will be empty.

Note

This has to be supported by your PBX in order to work.

If an incoming new call matches a to block number, Zammad will return the following.

{
  "action": "reject",
  "reason": "busy"
}

If no to block number matches, Zammad will return the following.

{}

Warning

Your PBX still needs to end the call (hangup event). Other wise the call will not just appear within Zammads caller log but also appear as ringing call.

The next logical steps within call session context would be:

Call hangup

Available attributes and sample data for hangup events

Attribute

Possible value

Description

event

hangup

Tell Zammad that somebody hung up the call.

from

e.g. 493055571600, 02214710334, anonymous

Number that initiated the call

to

e.g. 49221470334, 03023125771

Number that is being called

direction

in or out

The call direction - if your agent initiates a call this will be out

callId

e.g. 53ba82e2bd6d12d9fb2d3838f0cfb070, 5fb9532f40da834a, 123456789

An ID that is unique for the call. Zammad will use this ID to identify an existing call with following actions (e.g. like answering or hanging up)

This ID must be unique per call session.

cause

Cause type

Description

normalClearing

One of the parties hung up after the call was established.

busy

The called party was busy

cancel

The caller hung up before the called party picked up

noAnswer

The called party rejected the call (e.g. through a DND setting)

congestion

The called party could not be reached

notFound

The called number does not exist or called party is offline

forwarded

The call was forwarded to a different party

This defines the reason of the hangup. Zammad evaluates the cause and indicates e.g. missed calls accordingly in the caller log.

answeringNumber

e.g. 42, jdoe, jdoe@example.com, 3

Zammad will look up for a user with given value, the following attributes will be evaluated in given order:

  • user.phone

  • user.login

  • user.id

This value is optional.

There’s two options on how to POST the relevant data to Zammad.

Example:

Below calls have been sent with the following configuration. This is important for you to understand the returns we’re showing here.

Outbound:

  • Destination caller ID 4989* set outbound caller ID 498999998145 with note “All from munich”

  • Destination caller ID 4930* set outbound caller ID 493023125877 “All from Berlin”

Other settings:

  • Default caller ID for outbound calls 496990009111

POST-Request send: https://{FQDN-Zammad}/api/v1/cti/{instance specific token}

Outbound

Payload:

{
   "event": "hangup",
   "from": "493023125741",
   "to": "492214710334",
   "direction": "out",
   "callId": "f4ebd2be-7b9a-4d58-94c2-eb06a3c2ce76",
   "cause": "cancel"
}

Response:

{}

Sample curl command:

$ curl --request POST 'https://{FQDN-Zammad}/api/v1/cti/{instance specific token}' \
   --header 'Content-Type: application/json' \
   --data-raw '{
      "event": "hangup",
      "from": "493023125741",
      "to": "492214710334",
      "direction": "out",
      "callId": "f4ebd2be-7b9a-4d58-94c2-eb06a3c2ce76",
      "cause": "cancel"
   }'
Inbound

Payload:

{
   "event": "hangup",
   "from": "493023125741",
   "to": "492214710334",
   "direction": "in",
   "callId": "307fa962-de8d-4ffc-817b-7f6993204159",
   "answeringNumber": "emma@chrispresso.com",
   "cause": "normalClearing"
}

Response:

{}

Sample curl command:

$ curl --request POST 'https://{FQDN-Zammad}/api/v1/cti/{instance specific token}' \
   --header 'Content-Type: application/json' \
   --data-raw '{
      "event": "hangup",
      "from": "493023125741",
      "to": "492214710334",
      "direction": "in",
      "callId": "307fa962-de8d-4ffc-817b-7f6993204159",
      "answeringNumber": "emma@chrispresso.com",
      "cause": "normalClearing"
   }'

Call answered

Available attributes and sample data for answered events

Attribute

Possible value

Description

event

answer

Tell Zammad that someone answered the call.

from

e.g. 493055571600, 02214710334, anonymous

Number that initiated the call

to

e.g. 49221470334, 03023125771

Number that is being called

direction

in or out

The call direction - if your agent initiates a call this will be out

callId

e.g. 53ba82e2bd6d12d9fb2d3838f0cfb070, 5fb9532f40da834a, 123456789

An ID that is unique for the call. Zammad will use this ID to identify an existing call with following actions (e.g. like answering or hanging up)

This ID must be unique per call session.

answeringNumber

e.g. 42, jdoe, jdoe@example.com, 49221470351, 03023125184

Zammad will look up for a user with given value, the following attributes will be evaluated in given order:

  • user.phone

  • user.login

  • user.id

This value is optional.

user

e.g. John Doe, [Alice, Bob]

The user(s) real name involved. You may have to provide array style ([]) params depending on the call method you choose.

If the direction is out, this is the name of the calling person(s).
If the direction is in, this is the name of the called person(s).

This value is optional.

There’s two options on how to POST the relevant data to Zammad.

Example:

Below calls have been sent with the following configuration. This is important for you to understand the returns we’re showing here.

Outbound:

  • Destination caller ID 4989* set outbound caller ID 498999998145 with note “All from munich”

  • Destination caller ID 4930* set outbound caller ID 493023125877 “All from Berlin”

Other settings:

  • Default caller ID for outbound calls 496990009111

POST-Request sent: https://{FQDN-Zammad}/api/v1/cti/{instance specific token}

Outbound

Payload:

{
   "event": "answer",
   "from": "493023125741",
   "to": "492214710334",
   "direction": "out",
   "callId": "9f1840cb-8be9-4d3a-8200-3da2937085f0",
   "caller": "Christopher Miller"
}

Response:

{}

Sample curl command:

$ curl --request POST 'https://{FQDN-Zammad}/api/v1/cti/{instance specific token}' \
   --header 'Content-Type: application/json' \
   --data-raw '{
      "event": "answer",
      "from": "493023125741",
      "to": "492214710334",
      "direction": "out",
      "callId": "9f1840cb-8be9-4d3a-8200-3da2937085f0",
      "caller": "Christopher Miller"
   }'
Inbound

Payload:

{
   "event": "answer",
   "from": "493023125741",
   "to": "492214710334",
   "direction": "in",
   "callId": "307fa962-de8d-4ffc-817b-7f6993204159",
   "answeringNumber": "emma@chrispresso.com",
   "caller": ["Christopher Miller", "Emma Taylor"]
}

Response:

{}

Sample curl command:

$ curl --request POST 'https://{FQDN-Zammad}/api/v1/cti/{instance specific token}' \
   --header 'Content-Type: application/json' \
   --data-raw '{
      "event": "answer",
      "from": "493023125741",
      "to": "492214710334",
      "direction": "in",
      "callId": "307fa962-de8d-4ffc-817b-7f6993204159",
      "answeringNumber": "emma@chrispresso.com",
      "caller": ["Christopher Miller", "Emma Taylor"]
   }'
The next logical step within call session context would be:

Online Notification

Note

The availability of notification highly depends on the users permission and chosen notification settings.

Please note that the best results are always achieved with Agents.

List

Required permission: any

Tip

Use the expand request to know the affected objects. Otherwise you’ll need to find out what ID stands for which object type.

GET-Request sent: /api/v1/online_notifications?expand=true

Response:

# HTTP-Code 200 Ok

[
   {
      "id": 4,
      "o_id": 6,
      "object_lookup_id": 2,
      "type_lookup_id": 1,
      "user_id": 3,
      "seen": false,
      "updated_by_id": 8,
      "created_by_id": 8,
      "created_at": "2021-11-09T13:15:42.628Z",
      "updated_at": "2021-11-09T13:15:42.637Z",
      "user": "chris@chrispresso.com",
      "object": "Ticket",
      "type": "create",
      "created_by": "emily@example.com",
      "updated_by": "emily@example.com"
   },
   {
      "id": 3,
      "o_id": 8,
      "object_lookup_id": 2,
      "type_lookup_id": 2,
      "user_id": 3,
      "seen": false,
      "updated_by_id": 4,
      "created_by_id": 4,
      "created_at": "2021-11-09T13:10:42.628Z",
      "updated_at": "2021-11-09T13:15:42.635Z",
      "user": "chris@chrispresso.com",
      "object": "Ticket",
      "type": "update",
      "created_by": "jacob@chrispresso.com",
      "updated_by": "jacob@chrispresso.com"
   },
   {
      "id": 2,
      "o_id": 3,
      "object_lookup_id": 2,
      "type_lookup_id": 1,
      "user_id": 3,
      "seen": true,
      "updated_by_id": 6,
      "created_by_id": 6,
      "created_at": "2021-11-09T12:45:42.625Z",
      "updated_at": "2021-11-09T13:15:42.632Z",
      "user": "chris@chrispresso.com",
      "object": "Ticket",
      "type": "create",
      "created_by": "anna@example.com",
      "updated_by": "anna@example.com"
   },
   {
      "id": 1,
      "o_id": 2,
      "object_lookup_id": 2,
      "type_lookup_id": 1,
      "user_id": 3,
      "seen": true,
      "updated_by_id": 5,
      "created_by_id": 5,
      "created_at": "2021-11-09T11:45:42.624Z",
      "updated_at": "2021-11-09T13:15:42.629Z",
      "user": "chris@chrispresso.com",
      "object": "Ticket",
      "type": "create",
      "created_by": "emma@chrispresso.com",
      "updated_by": "emma@chrispresso.com"
   }
]

Show

Required permission: any

GET-Request sent: /api/v1/online_notifications/{id}

Response:

# HTTP-Code 200 Ok

{
   "id": 4,
   "o_id": 6,
   "object_lookup_id": 2,
   "type_lookup_id": 1,
   "user_id": 3,
   "seen": false,
   "updated_by_id": 8,
   "created_by_id": 8,
   "created_at": "2021-11-09T13:15:42.628Z",
   "updated_at": "2021-11-09T13:15:42.637Z"
}

Update

Required permission: any

PUT-Request sent: /api/v1/online_notifications/{id}

{
  "seen": true
}

Response:

# HTTP-Code 200 Ok

{
   "id": 4,
   "seen": true,
   "updated_by_id": 3,
   "o_id": 6,
   "object_lookup_id": 2,
   "type_lookup_id": 1,
   "user_id": 3,
   "created_by_id": 8,
   "created_at": "2021-11-09T13:15:42.628Z",
   "updated_at": "2021-11-09T13:25:00.004Z"
}

Delete

Required permission: any

DELETE-Request sent: /api/v1/online_notifications/{id}

Response:

# HTTP-Code 200 Ok

{}

Mark all as read

Required permission: any

POST-Request sent: /api/v1/online_notifications/mark_all_as_read

Response:

# HTTP-Code 200 Ok

{}

Object

Danger

Adjusting objects via API can cause serious issues with your instance. Proceed with absolute caution and ensure to adjust any of Zammads default fields.

If you want to hide fields, consider Core Workflows instead. For states and priorities use either API endpoints or rails console.

List

Required permission: admin.object

GET-Request sent: /api/v1/object_manager_attributes

Response:

# HTTP-Code 200 Ok

[
   {
      "id": 2,
      "name": "customer_id",
      "display": "Customer",
      "data_type": "user_autocompletion",
      "data_option": {
         "relation": "User",
         "autocapitalize": false,
         "multiple": false,
         "guess": true,
         "null": false,
         "limit": 200,
         "placeholder": "Enter Person or Organization/Company",
         "minLengt": 2,
         "translate": false,
         "permission": [
            "ticket.agent"
         ]
      },
      "data_option_new": {},
      "editable": false,
      "active": true,
      "screens": {
         "create_top": {
            "-all-": {
               "null": false
            }
         },
         "edit": {}
      },
      "to_create": false,
      "to_migrate": false,
      "to_delete": false,
      "to_config": false,
      "position": 10,
      "created_by_id": 1,
      "updated_by_id": 1,
      "created_at": "2021-11-09T13:12:32.677Z",
      "updated_at": "2021-11-09T13:12:32.677Z",
      "object": "Ticket",
      "deletable": false,
      "not_deletable_reason": "This attribute is referenced by Overview: My Tickets and thus cannot be deleted!"
   },
   {
      "id": 1,
      "name": "title",
      "display": "Title",
      "data_type": "input",
      "data_option": {
         "type": "text",
         "maxlength": 200,
         "null": false,
         "translate": false
      },
      "data_option_new": {},
      "editable": false,
      "active": true,
      "screens": {
         "create_top": {
            "-all-": {
               "null": false
            }
         },
         "edit": {}
      },
      "to_create": false,
      "to_migrate": false,
      "to_delete": false,
      "to_config": false,
      "position": 15,
      "created_by_id": 1,
      "updated_by_id": 1,
      "created_at": "2021-11-09T13:12:32.671Z",
      "updated_at": "2021-11-09T13:12:32.671Z",
      "object": "Ticket",
      "deletable": false
   },
   {
      "id": 3,
      "name": "type",
      "display": "Type",
      "data_type": "select",
      "data_option": {
         "default": "",
         "options": {
            "Incident": "Incident",
            "Problem": "Problem",
            "Request for Change": "Request for Change"
         },
         "nulloption": true,
         "multiple": false,
         "null": true,
         "translate": true,
         "maxlength": 255
      },
      "data_option_new": {},
      "editable": true,
      "active": false,
      "screens": {
         "create_middle": {
            "-all-": {
               "null": false,
               "item_class": "column"
            }
         },
         "edit": {
            "ticket.agent": {
               "null": false
            }
         }
      },
      "to_create": false,
      "to_migrate": false,
      "to_delete": false,
      "to_config": false,
      "position": 20,
      "created_by_id": 1,
      "updated_by_id": 1,
      "created_at": "2021-11-09T13:12:32.686Z",
      "updated_at": "2021-11-09T13:12:32.686Z",
      "object": "Ticket",
      "deletable": true
   },
   {
      "id": 4,
      "name": "group_id",
      "display": "Group",
      "data_type": "select",
      "data_option": {
         "default": "",
         "relation": "Group",
         "relation_condition": {
            "access": "full"
         },
         "nulloption": true,
         "multiple": false,
         "null": false,
         "translate": false,
         "only_shown_if_selectable": true,
         "permission": [
            "ticket.agent",
            "ticket.customer"
         ],
         "maxlength": 255
      },
      "data_option_new": {},
      "editable": false,
      "active": true,
      "screens": {
         "create_middle": {
            "-all-": {
               "null": false,
               "item_class": "column"
            }
         },
         "edit": {
            "ticket.agent": {
               "null": false
            }
         }
      },
      "to_create": false,
      "to_migrate": false,
      "to_delete": false,
      "to_config": false,
      "position": 25,
      "created_by_id": 1,
      "updated_by_id": 1,
      "created_at": "2021-11-09T13:12:32.690Z",
      "updated_at": "2021-11-09T13:12:32.690Z",
      "object": "Ticket",
      "deletable": false
   },
   {
      "id": 5,
      "name": "owner_id",
      "display": "Owner",
      "data_type": "select",
      "data_option": {
         "default": "",
         "relation": "User",
         "relation_condition": {
            "roles": "Agent"
         },
         "nulloption": true,
         "multiple": false,
         "null": true,
         "translate": false,
         "permission": [
            "ticket.agent"
         ],
         "maxlength": 255
      },
      "data_option_new": {},
      "editable": false,
      "active": true,
      "screens": {
         "create_middle": {
            "-all-": {
               "null": true,
               "item_class": "column"
            }
         },
         "edit": {
            "-all-": {
               "null": true
            }
         }
      },
      "to_create": false,
      "to_migrate": false,
      "to_delete": false,
      "to_config": false,
      "position": 30,
      "created_by_id": 1,
      "updated_by_id": 1,
      "created_at": "2021-11-09T13:12:32.694Z",
      "updated_at": "2021-11-09T13:12:32.694Z",
      "object": "Ticket",
      "deletable": false,
      "not_deletable_reason": "This attribute is referenced by Trigger: customer notification (on owner change); Overview: My assigned Tickets,My pending reached Tickets,Unassigned & Open and thus cannot be deleted!"
   },
   {
      "id": 6,
      "name": "state_id",
      "display": "State",
      "data_type": "select",
      "data_option": {
         "relation": "TicketState",
         "nulloption": true,
         "multiple": false,
         "null": false,
         "default": 2,
         "translate": true,
         "filter": [
            2,
            1,
            3,
            4,
            6,
            7
         ],
         "maxlength": 255
      },
      "data_option_new": {},
      "editable": false,
      "active": true,
      "screens": {
         "create_middle": {
            "ticket.agent": {
               "null": false,
               "item_class": "column",
               "filter": [
                  2,
                  1,
                  3,
                  4,
                  7
               ]
            },
            "ticket.customer": {
               "item_class": "column",
               "nulloption": false,
               "null": true,
               "filter": [
                  1,
                  4
               ],
               "default": 1
            }
         },
         "edit": {
            "ticket.agent": {
               "nulloption": false,
               "null": false,
               "filter": [
                  2,
                  3,
                  4,
                  7
               ]
            },
            "ticket.customer": {
               "nulloption": false,
               "null": true,
               "filter": [
                  2,
                  4
               ],
               "default": 2
            }
         }
      },
      "to_create": false,
      "to_migrate": false,
      "to_delete": false,
      "to_config": false,
      "position": 40,
      "created_by_id": 1,
      "updated_by_id": 1,
      "created_at": "2021-11-09T13:12:32.706Z",
      "updated_at": "2021-11-09T13:12:32.706Z",
      "object": "Ticket",
      "deletable": false,
      "not_deletable_reason": "This attribute is referenced by Trigger: auto reply (on new tickets); Overview: My Organization Tickets,My Tickets,My assigned Tickets,My pending reached Tickets,My replacement Tickets,Open,Open Banana Items,Pending reached,Unassigned & Open,VIP Customers and thus cannot be deleted!"
   },
   {
      "id": 7,
      "name": "pending_time",
      "display": "Pending till",
      "data_type": "datetime",
      "data_option": {
         "future": true,
         "past": false,
         "diff": 24,
         "null": true,
         "translate": true,
         "permission": [
            "ticket.agent"
         ]
      },
      "data_option_new": {},
      "editable": false,
      "active": true,
      "screens": {
         "create_middle": {
            "-all-": {
               "null": false,
               "item_class": "column"
            }
         },
         "edit": {
            "-all-": {
               "null": false
            }
         }
      },
      "to_create": false,
      "to_migrate": false,
      "to_delete": false,
      "to_config": false,
      "position": 41,
      "created_by_id": 1,
      "updated_by_id": 1,
      "created_at": "2021-11-09T13:12:32.713Z",
      "updated_at": "2021-11-09T13:12:32.713Z",
      "object": "Ticket",
      "deletable": false,
      "not_deletable_reason": "This attribute is referenced by Overview: My pending reached Tickets,Pending reached and thus cannot be deleted!"
   },
   {
      "id": 8,
      "name": "priority_id",
      "display": "Priority",
      "data_type": "select",
      "data_option": {
         "relation": "TicketPriority",
         "nulloption": false,
         "multiple": false,
         "null": false,
         "default": 2,
         "translate": true,
         "maxlength": 255
      },
      "data_option_new": {},
      "editable": false,
      "active": true,
      "screens": {
         "create_middle": {
            "ticket.agent": {
               "null": false,
               "item_class": "column"
            }
         },
         "edit": {
            "ticket.agent": {
               "null": false
            }
         }
      },
      "to_create": false,
      "to_migrate": false,
      "to_delete": false,
      "to_config": false,
      "position": 80,
      "created_by_id": 1,
      "updated_by_id": 1,
      "created_at": "2021-11-09T13:12:32.718Z",
      "updated_at": "2021-11-09T13:12:32.718Z",
      "object": "Ticket",
      "deletable": false
   },

   [ ... ]
]

Show

Required permission: admin.object

GET-Request sent: /api/v1/object_manager_attributes/{id}

Response:

# HTTP-Code 200 Ok

{
   "id": 18,
   "object_lookup_id": 1,
   "name": "email",
   "display": "Email",
   "data_type": "input",
   "data_option": {
      "type": "email",
      "maxlength": 150,
      "null": true,
      "item_class": "formGroup--halfSize"
   },
   "data_option_new": {},
   "editable": false,
   "active": true,
   "screens": {
      "signup": {
         "-all-": {
            "null": false
         }
      },
      "invite_agent": {
         "-all-": {
            "null": false
         }
      },
      "invite_customer": {
         "-all-": {
            "null": false
         }
      },
      "edit": {
         "-all-": {
            "null": true
         }
      },
      "create": {
         "-all-": {
            "null": true
         }
      },
      "view": {
         "-all-": {
            "shown": true
         }
      }
   },
   "to_create": false,
   "to_migrate": false,
   "to_delete": false,
   "to_config": false,
   "position": 400,
   "created_by_id": 1,
   "updated_by_id": 1,
   "created_at": "2021-11-09T13:12:32.784Z",
   "updated_at": "2021-11-09T13:12:32.784Z"
}

Create

Required permission: admin.object

POST-Request sent: /api/v1/object_manager_attributes

Payload:

{
   "name": "sample_boolean",
   "object": "Ticket",
   "display": "Sample Boolean",
   "active": true,
   "position": 1550,
   "data_type": "boolean",
   "data_option": {
      "options": {
         "true": "very correct indeed",
         "false": "very incorrect indeed"
      }
   },
   "screens": {
      "create_middle": {
         "ticket.customer": {
            "shown": true,
            "required": false,
            "item_class": "column"
         },
         "ticket.agent": {
            "shown": true,
            "required": false,
            "item_class": "column"
         }
      },
      "edit": {
         "ticket.customer": {
            "shown": true,
            "required": false
         },
         "ticket.agent": {
            "shown": true,
            "required": true
         }
      }
   }
}

Response:

# HTTP-Code 201 Created

{
   "id": 50,
   "object_lookup_id": 2,
   "name": "sample_boolean",
   "display": "Sample Boolean",
   "data_type": "boolean",
   "data_option": {
      "options": {
         "false": "very incorrect indeed",
         "true": "very correct indeed"
      },
      "default": null,
      "null": true,
      "relation": ""
   },
   "data_option_new": {},
   "editable": true,
   "active": true,
   "screens": {
      "create_middle": {
         "ticket.customer": {
            "shown": true,
            "required": false,
            "item_class": "column"
         },
         "ticket.agent": {
            "shown": true,
            "required": false,
            "item_class": "column"
         }
      },
      "edit": {
         "ticket.customer": {
            "shown": true,
            "required": false
         },
         "ticket.agent": {
            "shown": true,
            "required": true
         }
      }
   },
   "to_create": true,
   "to_migrate": true,
   "to_delete": false,
   "to_config": false,
   "position": 1550,
   "created_by_id": 3,
   "updated_by_id": 3,
   "created_at": "2021-11-12T18:18:23.208Z",
   "updated_at": "2021-11-12T18:18:23.208Z"
}

Note

Please note that above payloads cover ticket objects. This is fine in most situations, except if you’re looking at the default object permissions. This is why we’re listing these separate for you to view.

The attribute object controls which context is being used:

  • Ticket

  • User

  • Organisation

  • Group

"screens": {
   "create_middle": {
      "ticket.customer": {
         "shown": true,
         "required": false,
         "item_class": "column"
      },
      "ticket.agent": {
         "shown": true,
         "required": false,
         "item_class": "column"
      }
   },
   "edit": {
      "ticket.customer": {
         "shown": true,
         "required": false
      },
      "ticket.agent": {
         "shown": true,
         "required": true
      }
   }
}

Update

Required permission: admin.object

Except on the request method, payloads or updating and creating objects are identical. For full payload samples thus scroll up to Create.

Zammad will return two attributes during update: data_option and data_option_new. The first attribute contains the current active values and the second one the new to be values (they’ll become active after executing the database migrations).

PUT-Request sent: /api/v1/object_manager_attributes/{id}

{
   "id": 50,
   "name": "sample_boolean",
   "object": "Ticket",
   "display": "Sample Boolean",
   "data_type": "boolean",
   "position": 1200,
   "data_option": {
      "options": {
         "true": "yes",
         "false": "no"
      },
      "default": "false"
   }
}

Note

Ensure to provide data_option. Zammad is very picky if you leave out this attribute. Please note that changing the object type after creation is not possible.

Response:

# HTTP-Code 200 Ok

{
   "name": "sample_boolean",
   "display": "Sample Boolean",
   "data_type": "boolean",
   "position": 1200,
   "data_option_new": {
      "options": {
         "false": "no",
         "true": "yes"
      },
      "default": false,
      "null": true,
      "relation": ""
   },
   "data_option": {
      "options": {
         "false": "very incorrect indeed",
         "true": "very correct indeed"
      },
      "default": null,
      "null": true,
      "relation": ""
   },
   "object_lookup_id": 2,
   "to_config": true,
   "editable": true,
   "id": 50,
   "updated_by_id": 3,
   "active": true,
   "screens": {
      "create_middle": {
         "ticket.customer": {
            "shown": true,
            "required": false,
            "item_class": "column"
         },
         "ticket.agent": {
            "shown": true,
            "required": false,
            "item_class": "column"
         }
      },
      "edit": {
         "ticket.customer": {
            "shown": true,
            "required": false
         },
         "ticket.agent": {
            "shown": true,
            "required": true
         }
      }
   },
   "to_create": false,
   "to_migrate": false,
   "to_delete": false,
   "created_by_id": 3,
   "created_at": "2021-11-12T18:18:23.208Z",
   "updated_at": "2021-11-12T19:30:20.883Z"
}

Delete

Required permission: admin.object

DELETE-Request sent: /api/v1/object_manager_attributes/{id}

Response:

# HTTP-Code 200 Ok

{}

Execute Database Migrations

Required permission: admin.object

Warning

After executing the database migrations a restart of Zammad is mandatory. If configured Zammad also can restart automatically (this is the case on Hosted environments) – expect a short downtime.

POST-Request sent: /api/v1/object_manager_attributes_execute_migrations

Response:

# HTTP-Code 200 Ok

{}

User Access Token

List

Required permission: user_preferences.access_token

GET-Request sent: /api/v1/user_access_token

Response:

# HTTP-Code 200 Ok

{
   "tokens": [
      {
         "id": 2,
         "user_id": 3,
         "action": "api",
         "label": "test",
         "preferences": {
            "permission": [
               "user_preferences.access_token"
            ]
         },
         "last_used_at": "2021-11-11T14:29:22.765Z",
         "expires_at": null,
         "created_at": "2021-11-10T23:17:46.570Z",
         "updated_at": "2021-11-11T14:29:22.765Z"
      },
      {
         "id": 1,
         "user_id": 3,
         "action": "api",
         "label": "full",
         "preferences": {
            "permission": [
               "admin",
               "ticket.agent"
            ]
         },
         "last_used_at": "2021-11-10T23:12:06.078Z",
         "expires_at": null,
         "created_at": "2021-11-09T13:17:20.446Z",
         "updated_at": "2021-11-10T23:12:06.078Z"
      }
   ],
   "permissions": [
      {
         "id": 1,
         "name": "admin",
         "note": "Admin Interface",
         "preferences": {},
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.544Z",
         "updated_at": "2021-11-09T13:12:31.544Z"
      },
      {
         "id": 32,
         "name": "admin.api",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "API"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.754Z",
         "updated_at": "2021-11-09T13:12:31.754Z"
      },
      {
         "id": 26,
         "name": "admin.branding",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Branding"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.713Z",
         "updated_at": "2021-11-09T13:12:31.713Z"
      },
      {
         "id": 11,
         "name": "admin.calendar",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Calendar"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.626Z",
         "updated_at": "2021-11-09T13:12:31.626Z"
      },
      {
         "id": 25,
         "name": "admin.channel_chat",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Channel - Chat"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.704Z",
         "updated_at": "2021-11-09T13:12:31.704Z"
      },
      {
         "id": 18,
         "name": "admin.channel_email",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Channel - Email"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.665Z",
         "updated_at": "2021-11-09T13:12:31.665Z"
      },
      {
         "id": 20,
         "name": "admin.channel_facebook",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Channel - Facebook"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.676Z",
         "updated_at": "2021-11-09T13:12:31.676Z"
      },
      {
         "id": 17,
         "name": "admin.channel_formular",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Channel - Formular"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.659Z",
         "updated_at": "2021-11-09T13:12:31.659Z"
      },
      {
         "id": 22,
         "name": "admin.channel_google",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Channel - Google"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.687Z",
         "updated_at": "2021-11-09T13:12:31.687Z"
      },
      {
         "id": 23,
         "name": "admin.channel_microsoft365",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Channel - Microsoft 365"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.693Z",
         "updated_at": "2021-11-09T13:12:31.693Z"
      },
      {
         "id": 24,
         "name": "admin.channel_sms",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Channel - SMS"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.699Z",
         "updated_at": "2021-11-09T13:12:31.699Z"
      },
      {
         "id": 21,
         "name": "admin.channel_telegram",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Channel - Telegram"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.682Z",
         "updated_at": "2021-11-09T13:12:31.682Z"
      },
      {
         "id": 19,
         "name": "admin.channel_twitter",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Channel - Twitter"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.671Z",
         "updated_at": "2021-11-09T13:12:31.671Z"
      },
      {
         "id": 16,
         "name": "admin.channel_web",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Channel - Web"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.654Z",
         "updated_at": "2021-11-09T13:12:31.654Z"
      },
      {
         "id": 40,
         "name": "admin.core_workflow",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Core Workflow"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.807Z",
         "updated_at": "2021-11-09T13:12:31.807Z"
      },
      {
         "id": 36,
         "name": "admin.data_privacy",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Data Privacy"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.783Z",
         "updated_at": "2021-11-09T13:12:31.783Z"
      },
      {
         "id": 3,
         "name": "admin.group",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Groups"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.565Z",
         "updated_at": "2021-11-09T13:12:31.565Z"
      },
      {
         "id": 31,
         "name": "admin.integration",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Integrations"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.748Z",
         "updated_at": "2021-11-09T13:12:31.748Z"
      },
      {
         "id": 59,
         "name": "admin.knowledge_base",
         "note": "Create and setup %s",
         "preferences": {
            "translations": [
               "Knowledge Base"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.992Z",
         "updated_at": "2021-11-09T13:12:31.992Z"
      },
      {
         "id": 9,
         "name": "admin.macro",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Macros"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.612Z",
         "updated_at": "2021-11-09T13:12:31.612Z"
      },
      {
         "id": 37,
         "name": "admin.maintenance",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Maintenance"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.789Z",
         "updated_at": "2021-11-09T13:12:31.789Z"
      },
      {
         "id": 35,
         "name": "admin.monitoring",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Monitoring"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.777Z",
         "updated_at": "2021-11-09T13:12:31.777Z"
      },
      {
         "id": 33,
         "name": "admin.object",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Objects"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.760Z",
         "updated_at": "2021-11-09T13:12:31.760Z"
      },
      {
         "id": 5,
         "name": "admin.organization",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Organizations"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.579Z",
         "updated_at": "2021-11-09T13:12:31.579Z"
      },
      {
         "id": 6,
         "name": "admin.overview",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Overviews"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.591Z",
         "updated_at": "2021-11-09T13:12:31.591Z"
      },
      {
         "id": 30,
         "name": "admin.package",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Packages"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.738Z",
         "updated_at": "2021-11-09T13:12:31.738Z"
      },
      {
         "id": 15,
         "name": "admin.report_profile",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Report Profiles"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.648Z",
         "updated_at": "2021-11-09T13:12:31.648Z"
      },
      {
         "id": 4,
         "name": "admin.role",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Roles"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.572Z",
         "updated_at": "2021-11-09T13:12:31.572Z"
      },
      {
         "id": 14,
         "name": "admin.scheduler",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Scheduler"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.642Z",
         "updated_at": "2021-11-09T13:12:31.642Z"
      },
      {
         "id": 28,
         "name": "admin.security",
         "note": "Manage %s Settings",
         "preferences": {
            "translations": [
               "Security"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.725Z",
         "updated_at": "2021-11-09T13:12:31.725Z"
      },
      {
         "id": 38,
         "name": "admin.session",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Sessions"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.795Z",
         "updated_at": "2021-11-09T13:12:31.795Z"
      },
      {
         "id": 27,
         "name": "admin.setting_system",
         "note": "Manage %s Settings",
         "preferences": {
            "translations": [
               "System"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.719Z",
         "updated_at": "2021-11-09T13:12:31.719Z"
      },
      {
         "id": 12,
         "name": "admin.sla",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "SLA"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.631Z",
         "updated_at": "2021-11-09T13:12:31.631Z"
      },
      {
         "id": 10,
         "name": "admin.tag",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Tags"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.619Z",
         "updated_at": "2021-11-09T13:12:31.619Z"
      },
      {
         "id": 7,
         "name": "admin.text_module",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Text Modules"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.598Z",
         "updated_at": "2021-11-09T13:12:31.598Z"
      },
      {
         "id": 29,
         "name": "admin.ticket",
         "note": "Manage %s Settings",
         "preferences": {
            "translations": [
               "Ticket"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.731Z",
         "updated_at": "2021-11-09T13:12:31.731Z"
      },
      {
         "id": 8,
         "name": "admin.time_accounting",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Time Accounting"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.606Z",
         "updated_at": "2021-11-09T13:12:31.606Z"
      },
      {
         "id": 34,
         "name": "admin.translation",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Translations"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.767Z",
         "updated_at": "2021-11-09T13:12:31.767Z"
      },
      {
         "id": 13,
         "name": "admin.trigger",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Triggers"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.637Z",
         "updated_at": "2021-11-09T13:12:31.637Z"
      },
      {
         "id": 2,
         "name": "admin.user",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Users"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.556Z",
         "updated_at": "2021-11-09T13:12:31.556Z"
      },
      {
         "id": 39,
         "name": "admin.webhook",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Webhooks"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.801Z",
         "updated_at": "2021-11-09T13:12:31.801Z"
      },
      {
         "id": 55,
         "name": "chat",
         "note": "Access to %s",
         "preferences": {
            "translations": [
               "Chat"
            ],
            "disabled": true
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.901Z",
         "updated_at": "2021-11-09T13:12:31.901Z"
      },
      {
         "id": 56,
         "name": "chat.agent",
         "note": "Access to %s",
         "preferences": {
            "translations": [
               "Chat"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.909Z",
         "updated_at": "2021-11-09T13:12:31.909Z"
      },
      {
         "id": 57,
         "name": "cti",
         "note": "CTI",
         "preferences": {
            "disabled": true
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.916Z",
         "updated_at": "2021-11-09T13:12:31.916Z"
      },
      {
         "id": 58,
         "name": "cti.agent",
         "note": "Access to %s",
         "preferences": {
            "translations": [
               "CTI"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.922Z",
         "updated_at": "2021-11-09T13:12:31.922Z"
      },
      {
         "id": 60,
         "name": "knowledge_base",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Knowledge Base"
            ],
            "disabled": true
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.999Z",
         "updated_at": "2021-11-09T13:12:31.999Z"
      },
      {
         "id": 61,
         "name": "knowledge_base.editor",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Knowledge Base Editor"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:32.006Z",
         "updated_at": "2021-11-09T13:12:32.006Z"
      },
      {
         "id": 62,
         "name": "knowledge_base.reader",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Knowledge Base Reader"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:32.012Z",
         "updated_at": "2021-11-09T13:12:32.012Z"
      },
      {
         "id": 51,
         "name": "report",
         "note": "Report Interface",
         "preferences": {},
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.875Z",
         "updated_at": "2021-11-09T13:12:31.875Z"
      },
      {
         "id": 52,
         "name": "ticket",
         "note": "Ticket Interface",
         "preferences": {
            "disabled": true
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.880Z",
         "updated_at": "2021-11-09T13:12:31.880Z"
      },
      {
         "id": 53,
         "name": "ticket.agent",
         "note": "Access to Agent Tickets based on Group Access",
         "preferences": {
            "plugin": [
               "groups"
            ]
         },
         "active": true,
         "allow_signup": false,
         "created_at": "2021-11-09T13:12:31.888Z",
         "updated_at": "2021-11-09T13:12:31.888Z"
      },
      {
         "id": 41,
         "name": "user_preferences",
         "note": "User Preferences",
         "preferences": {},
         "active": true,
         "allow_signup": true,
         "created_at": "2021-11-09T13:12:31.812Z",
         "updated_at": "2021-11-09T13:12:31.812Z"
      },
      {
         "id": 44,
         "name": "user_preferences.access_token",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Token Access"
            ]
         },
         "active": true,
         "allow_signup": true,
         "created_at": "2021-11-09T13:12:31.829Z",
         "updated_at": "2021-11-09T13:12:31.829Z"
      },
      {
         "id": 48,
         "name": "user_preferences.avatar",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Avatar"
            ]
         },
         "active": true,
         "allow_signup": true,
         "created_at": "2021-11-09T13:12:31.852Z",
         "updated_at": "2021-11-09T13:12:31.852Z"
      },
      {
         "id": 49,
         "name": "user_preferences.calendar",
         "note": "Access to %s",
         "preferences": {
            "translations": [
               "Calendars"
            ],
            "required": [
               "ticket.agent"
            ]
         },
         "active": true,
         "allow_signup": true,
         "created_at": "2021-11-09T13:12:31.857Z",
         "updated_at": "2021-11-09T13:12:31.857Z"
      },
      {
         "id": 47,
         "name": "user_preferences.device",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Devices"
            ]
         },
         "active": true,
         "allow_signup": true,
         "created_at": "2021-11-09T13:12:31.847Z",
         "updated_at": "2021-11-09T13:12:31.847Z"
      },
      {
         "id": 45,
         "name": "user_preferences.language",
         "note": "Change %s",
         "preferences": {
            "translations": [
               "Language"
            ]
         },
         "active": true,
         "allow_signup": true,
         "created_at": "2021-11-09T13:12:31.834Z",
         "updated_at": "2021-11-09T13:12:31.834Z"
      },
      {
         "id": 46,
         "name": "user_preferences.linked_accounts",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Linked Accounts"
            ]
         },
         "active": true,
         "allow_signup": true,
         "created_at": "2021-11-09T13:12:31.840Z",
         "updated_at": "2021-11-09T13:12:31.840Z"
      },
      {
         "id": 43,
         "name": "user_preferences.notifications",
         "note": "Manage %s",
         "preferences": {
            "translations": [
               "Notifications"
            ],
            "required": [
               "ticket.agent"
            ]
         },
         "active": true,
         "allow_signup": true,
         "created_at": "2021-11-09T13:12:31.823Z",
         "updated_at": "2021-11-09T13:12:31.823Z"
      },
      {
         "id": 50,
         "name": "user_preferences.out_of_office",
         "note": "Change %s",
         "preferences": {
            "translations": [
               "Out of Office"
            ],
            "required": [
               "ticket.agent"
            ]
         },
         "active": true,
         "allow_signup": true,
         "created_at": "2021-11-09T13:12:31.868Z",
         "updated_at": "2021-11-09T13:12:31.868Z"
      },
      {
         "id": 42,
         "name": "user_preferences.password",
         "note": "Change %s",
         "preferences": {
            "translations": [
               "Password"
            ]
         },
         "active": true,
         "allow_signup": true,
         "created_at": "2021-11-09T13:12:31.818Z",
         "updated_at": "2021-11-09T13:12:31.818Z"
      }
   ]
}

Create

Required permission: user_preferences.access_token

POST-Request sent: /api/v1/user_access_token

{
   "name": "My amazing test",
   "permission": ["cti.agent","ticket.agent"],
   "expires_at": "2021-12-21"
}

Response:

# HTTP-Code 200 Ok

{
   "token": "M4oXJgB_8WiMNWzSdrDv3K3YXJywDh52BqC7IKV-NnM_Cf_bd_SkS6zyIWNZKJXw"
}

Note

Above returned token is the API token. This value is provided once after creation and can’t be retrieved after.

Delete

Required permission: user_preferences.access_token

DELETE-Request sent: /api/v1/user_access_token/{id}

Response:

# HTTP-Code 200 Ok

{}

Backup and Restore

Zammad comes with a collection of scripts for easy backup & restore for default installations. These scripts are located within /opt/zammad/contrib/backup.

Warning

⚖️ Important things to note beforehand

These scripts do not come with any warranty and may not work in your specific use case. This depends on the configuration and installation type of your instance.

You should always regularly test and review the functionality! If the script functionality or scope is not working for your cases, feel free to copy these to a independent location and adjust the scripts as needed.

Getting Started

Backup configuration

Before you can run either a backup or restoration, the scripts requires you to provide a configuration file. We’re shipping a config.dist within the /opt/zammad/contrib/backup directory which you can simply rename.

To do so run the following commands as either root or zammad user.

$ cd /opt/zammad/contrib/backup/
$ mv config.dist config

If below default values are not working for you or your installation in general, this is the best moment to adjust the configuration file as needed.

After this you’ll be ready to continue with either creating your first backup or restoring an existing backup.

BACKUP_DIR

Default: /var/tmp/zammad_backup

Tell the backup script where to write your backup files to.

Ensure that the user you’re going to use for backing up Zammad (either root or zammad by default) has enough permissions to write into the target directory structure.

In case the directory is not available yet, the backup script will attempt to create the directory.

Make also sure to have enough space available on the backup location. Zammad always creates full backups. While we do compress backups, expect worst case ratios of 1 (no compression at all) depending on your attachments!

HOLD_DAYS

Default: 10

How many days should the backup script keep old backups? This value contains 60 minutes grace period (so e.g. 10 days plus 1 hour) for safety reasons.

Old backups are removed before creating the actual (current) backup.

Example:

  • 0 will keep the last 25 hours worth of backup

  • -1 will always remove all available backups (aka only keep current backup)

FULL_FS_DUMP

Default: yes (accepts: yes or no)

Setting this option to no allows you to only backup usage data without any environmental files from your old host. This allows you to backup your Zammad database together with the attachments you’ve stored within the file system.

Please refer Storage Settings to learn how to change the storage location of your attachments.

If you can’t decide, our clear suggestion is setting this to no.

DEBUG

Default: no (accepts: yes or no)

Having issues and want to fiddle around? Setting this option to yes may help you with this. It contains useful debug messages at strategic points.

Warning

This option potentially returns sensitive information to standard out! Do not use this option in productive environments or ensure to turn it off after testing.

Create Backup

Preparation

Before running your first backup, please have a look at the Backup configuration to set it up correctly.

Backup

In general, running a Zammad backup is as simple as running:

$ /opt/zammad/contrib/backup/zammad_backup.sh

Please make sure to test the backup function manually with the user you’re planning to backup first. This ensures that your backup really is running as expected.

The backup process should look like this one:

# Zammad backup started - Fri Jan 21 17:53:44 CET 2022!

creating file backup...
 ... as full dump
creating postgresql backup...
Ensuring dump permissions ...

# Zammad backuped successfully - Fri Jan 21 17:53:57 CET 2022!

Sample backup process with default settings.

Additional Information
  • The backup script can be either run as zammad or root user.

  • Stopping Zammad is not required (but suggested) technically. It may be required in your case!

  • Keep in mind that a running Zammad instance keeps changing data which may be an issue during long backup runs

Hint

😖 Having trouble backing up?

Have a look at the troubleshooting section to address your issues.

Restore

Before Starting

Hint

♻️ Migrating from host to host…?

Please refer Migrate Zammad to New Host for more additional information about migrating Zammad.

Warning

This documentation page expects a fully installed Zammad version.
It also expects you to restore Zammad on the same host and version!

Before restoring your backup, please note the following:

  • The restoration process stops & restarts the Zammad service. This means you usually have to run the restoration script as root user.

    • This is mandatory for package installations

    • On Source code installations, this does not work because of different environments - you could load it beforehand as root user to have access to Zammad specific commands.

    • If both approaches above do not fit for your case, consider adjusting the backup and restore scripts to your need in an independent directory. You’re working out of script and documentation scope!

  • PostgreSQL based installations will drop and re-create the database! MySQL / MariaDB based installations restore on the existing database.

  • You require at least twice the backed up Zammad instance size of free storage. If you have the dump only, factor 3 could be a good number.

Restore
Step 1: Copy your backup files to a fitting location (if needed)

This basically is a given usually if you run a normal restore. Ensure that the user you’re using for restoration is allowed to read the backup files - writing is required for /opt/zammad/.

The Zammad backup consists of two files. This is their format:

<timestamp>_zammad_db.psql.gz
<timestamp>_zammad_files.tar.gz

There’s also two symlinks in your backup directory showing to the newest backup created.

Step 2: Configure the backup script (if needed)
On new installation it’s required. For restoration this mainly affects the backup file location.
Please consult Backup configuration for more.
Step 3: Run the restore

Restoration works in two possible ways, depending on how interactive you want to go.

Warning

Restoring old backups may overwrite your database.yml. You can find out if that’s the case by having a look into the file .tar.gz within the config directory. If you can see a database.yml there, ensure to save the original version before restoring.

If you found the trap already, you can try the Database Helper: (re)set password.

$ /opt/zammad/contrib/backup/zammad_restore.sh

Provide the requested information to the script wait for the restoration to finish. Depending on the size of your backup and host performance this may take some time.

The restore operation should look like this:

# Zammad restore started - Fri Jan 21 17:54:13 CET 2022!

The restore will delete your current database!
Be sure to have a backup available!

Please ensure to have twice the storage of the uncompressed backup size!


Note that the restoration USUALLY requires root permissions as services are stopped!


Enter 'yes' if you want to proceed!
Restore?: yes
Enter file date to restore:
20220120124714
20220121175344
File date: 20220121175344
Enter db date to restore:
20220120124714
20220121175344
DB date: 20220121175344
# Stopping Zammad
# Checking requirements
# ... Dropping current database zammad
Dropped database 'zammad'
# ... Creating database zammad for owner zammad
CREATE DATABASE
# Restoring PostgreSQL DB
# Restoring Files
# Ensuring correct file permissions ...
# Clearing Cache ...
# Starting Zammad

# Zammad restored successfully - Fri Jan 21 17:54:34 CET 2022!

Sample backup process.

Step 4: Re-install Zammad if restoring a full filesystem restore

Zammads backup scripts backup the whole filesystem of Zammad. This is mainly for backward compatibility but not a hard requirement.

If your filesystem dump contains attachments only (the tar will contain a storage folder only) skip this step!

For a better overview, please see: step 9 of our migration path.

Step 5: Apply missing environmental settings

Note

This does not apply to Docker images, as the following settings should be applied upon every start automatically.

If you’ve set any environmental settings like higher web concurrency due to required 🎛️ Performance Tuning, please re-apply your settings now.

If not already done, please install Elasticsearch now (if you want to use it). Follow Step 3: Connect Zammad to reconfigure your installation for Elasticsearch use and rebuild the search index.

You are now ready to continue your work. The rebuild of your search index can safely run during your work, but will cause a degraded search performance and may lead to temporarily not found data.

Hint

😖 Having trouble restoring?

Have a look at the troubleshooting section to address your issues.

Migrate Zammad to New Host

This is a proof of concept, not a full how to. Your environment may be different. Please note that the steps described on this page are an addition to backing up and restoring. They’re not meant to stand alone - we’ll link and note this at the relevant parts.

If anything goes wrong, please consult the Zammad Community or consider paid support options.

Hint

Migrating from Zammad SaaS? Skip to step 7. For restoration, you’ve received an attachment dump! 🤓

Warning

Restoration & Migration on docker based installation may differ. While the steps are the same on most parts, it is not covered by this documentation!

Step 1: Note down your environmental adjustments

This mainly affects performance tuning settings. This will be important after restoring.

Step 2: Install Zammad on the destination host

For the easiest restoration path possible, please install the same version like your origin instance. You could also consider updating the old instance before migrating.

Choose between these installation types:
Step 3: Activate maintenance mode

This ends agents and customers sessions. Learn more about the maintenance mode in Zammad.

Step 4: Disable your communication channels

This is just a safety measurement. As our restore scripts starts Zammad automatically, this may help if something is not in a correct state.

Step 5: Stop and disable Zammad

Make sure to no longer have Zammad change data before backing up.

$ systemctl disable zammad
$ systemctl stop zammad
Step 6: Backup!

Follow our documentation part for backup creation.

Remember if you’ve created a full filesystem dump or only backed up your attachments. This will be important for the restoration.

If you want to go with the easiest way, consider only dumping your attachments. Learn more on our configuration page.

Step 7: Transfer your backup files

You’ll find the backup location within the config file in the backup directory. Make sure to adjust the backup configuration on the destination host according to our configuration page to provide the correct backup file directory.

Provide the file location you transferred the backup files to.

Step 8: Restore your backup

Follow the steps 1 to 3 of our restoration page to restore the backup on the new host.

If you’re running a source code installation, we recommend install the same version beforehand. This reduces environment fiddling a lot.

Warning

Restoring old backups may overwrite your database.yml. You can find out if that’s the case by having a look into the file .tar.gz within the config directory. If you can see a database.yml there, ensure to save the original version before restoring.

If you found the trap already, you can try the Database Helper: (re)set password.

Important

Stop Zammad after the restoration has finished.

If you experience issues during restoration, please consult Troubleshooting Backup & Restore.

Step 9: Run required maintenance tasks after restoring

After successful restoration, please continue below depending if you’ve only backed up your attachments or had a full filesystem dump.

Note

Keep in mind that docker-compose and source code installations do not know zammad run. Below commands show the package installation way, just remove all zammad run parts from the commands and run them.

This means: zammad run rails c would be rails c.

Step 9.1: Clear the cache
$ zammad run rails r "Rails.cache.clear"
Step 10: Apply missing environmental settings

If you’ve set any environmental settings like higher web concurrency due to required 🎛️ Performance Tuning, please re-apply your settings now.

If not already done, please install Elasticsearch now (if you want to use it). Follow Step 3: Connect Zammad to reconfigure your installation for Elasticsearch use and rebuild the search index.

You are now ready to continue your work. The rebuild of your search index can safely run during your work, but will cause a degraded search performance and may lead to temporarily not found data.

Step 11: Re-enable Channels and deactivate maintenance mode

Set the previous deactivated channels back to active if you’re sure everything was successful. At this point Zammad will start to change data!

After verifying the functionality of your channels, allow your agents and customers back in by disabling the maintenance mode.

Learn more about the maintenance mode in Zammad.

Hint

Migrated from Zammad SaaS or switching providers?

Please make sure that your notification and FQDN configuration is still correct. Other wise you may have unexpected issues like not receiving notifications or non functional authentications (3rd party).

Step 12 (optional): Update Zammad to latest possible version

In case the backup source was not on the latest possible version, please update your Zammad installation now.

In case your installed version is fairly old, please note the upgrade path notes on our updating zammad page.

Troubleshooting Backup & Restore

If you encountered errors, they possibly can be corrected.

Hint

🥸 Your issue is not listed…?

Please consult the Zammad Community for technical assistance.

Exit Codes

Our backup & restore scripts come with exit codes to help you finding a solution. However, we do not guarantee a complete error handling.

Beside the exit codes, there are also error messages returned to standard out.

Exit code list

Code

Description / Situation

0

The script finished successfully (or the error is not handled).

1

This is a general error. Most often used for script aborts due to incorrect information provided or information missing.

2

There was an error with database handling. This usually either happens if your database server does not meet script requirements, login data being invalid or „broken‟ database dumps.

3

There were issues with file / folder permissions.

Classics

Here’s some classics you may encounter.

password authentication failed or peer authentication failed

This indicates that the password of your Zammad DB user is either different from your database.yml or the wrong database server may be contacted.

„But my Zammad instance is running, how can it be wrong?‟

Zammad may fall back to socket connection which is why you didn’t notice.

What to do …

Ensure that the provided user credentials are correct. You can also consider to use the Database Helper: (re)set password script, you can find in the backup directory.

Ident authentication failed for user

This indicates your database server does require ident authentication. That authentication method is not supported by our scripts.

What to do …

Check pg_hba.conf of your PostgreSQL-Server and adjust it if needed.

Usually authentication can be allowed like so:

# THIS IS A SAMPLE AND MAY NOT FIT YOUR ENVIRONMENT
host    all             all             127.0.0.1/32            md5
host    all             all             ::1/128                 md5

Please consult the offical PostgreSQL documentation for this, as this is out of our documentation scope.

WARNING: You don't seem to have any attachments in the file system!

This indicate you’ve set FULL_FS_DUMP to no but your instance currently does not save attachments to file system.

This warning will be shown once before creating an empty directory to allow the backup process to continue successfully.

If you believe that this is an error, please see Storage Settings. In case the issue posts, please consult the Zammad Community.

Helper scripts

Danger

☠️ The following scripts are potentially destructive ☠️

You should never run scrips which scopes you don’t understand. Below scripts potentially can make things worse which is why you should evaluate them before hand.

You’re running these scripts at your own risk.

If we found a script is helping you more than 30 lines of new documentation, we may have added a helper script.

Database Helper: (re)set password
Limitations
  • This script is working for PostgreSQL installations only.

  • Only local database servers are supported (script changes user).

  • This script requires to be run as root or similar privileged user!

Scopes

Mostly the following installation types will be affected / relevant:

  • package installations (especially CentOS & SUSE)

  • possibly source code installations

Functionality

The script will do the following actions depending on the situation automatically for you. It will double tab by asking for your confirmation up front.

  • If database.yml contains an empty password line, a new password will be generated, and set for the database user of Zammad, and saved to the configuration file.

  • If database.yml contains a password, it will be used to set the password of the Zammad database user.

  • Please note that the script will automatically stop and start Zammad!

Usage

Run /opt/zammad/contrib/backup/zammad_db_user_helper.sh and follow the instructions. No specific configurations are required.

If errors occur the script will try to bring Zammad back online before exitting. Please ensure that your service is running.

Before you continue, please also note the listed limitations to save your precious time.

🔨 Adjust script settings

Learn more about configuration options for backup and restore to see scopes better.

🗃️ Create Backups

How to create full dumps of your Zammad installation.

🗄️ Restore Backups

Update went wrong and you need to go back? How to restore your instance on a new or the same host.

🔀 Migrating to new hosts

This is a general summary on how to best migrate Zammad from host to host. We’ll reference to backup creation and restoration as needed.

🔥 Troubleshooting

Things hit the fan? This page might help you out of that pit.

🤝 Helper scripts

These scripts may be helpful if Backup & Restore does not work as expected. However note that these are potentially destructive.

Limitations

Please note the following limitations which may affect script functionality or availability.

  • Restoration via script on docker and source code based installations may not work and is out of scope of this documentation as of now

  • Backup & Restore is only available for PostgreSQL and MySQL / MariaDB like installations

  • Starting with Zammad 5.0 the scripts require user & password authentication. This is supported by most of our installation types

  • Backup & Restore is always a full dump of everything (no incrementals)

  • Restoring or backing up specific information (e.g. Tickets, Users, …) is not supported

  • Switching / Converting database installations is not possible
    Refer this guide to manually switch databases: Migrate to PostgreSQL server
  • Environmental settings (like e.g. Configuration via Environment Variables) are not backed up and thus require you to manually set them on a new host

  • Restoration into a older Zammad version is not possible nor supported

  • Do not attempt to restore backup files from custom scripts with the provided scripts by Zammad. This is most likely subject to fail or bring issues you may discover too late.

Configuration via Environment Variables

Use these environment variables to configure Zammad’s behavior at runtime.

Note

🙋 What’s an environment variable, and how do I “use” it?

Unfortunately, that question has a very long answer that goes beyond the scope of this article. How you set environment variables will depend on how you installed Zammad (e.g., source, package, or Docker).

But for package installations, here’s a short answer:

# set OPTION to "value"
$ zammad config:set OPTION=value
$ systemctl restart zammad

# get OPTION
$ zammad config:get OPTION

# unset OPTION
$ zammad config:unset OPTION
$ systemctl restart zammad

To learn more, do some googling on environment variables and the shell environment (or execution environment) in Unix.

Important

While below options and remarks affect all installation types of Zammad, please note that environment variables mentioned may be named different for installations based on docker-compose and kubernetes.

General Options

APP_RESTART_CMD

The command Zammad will use to automatically restart the server after changes have been made in the Object Manager. (E.g., "systemctl restart zammad")

If this is undefined, you will have to restart manually after making changes in the Object Manager. Please keep in mind that Zammad runs as unprivileged user. This means that you have to allow the Zammad user via e.g. sudoers to run the required restart command.

Default: unset

GPG_PATH

Defines the path to the GPG installation. This is only needed if you installed Zammad from Source, if you want to use different versions of PGP on your machine or if your PGP installation differs from the standard installation.

Default: unset

RAILS_LOG_TO_STDOUT

Print output directly to standard output instead of /var/log/zammad/production.log.

This setting can be overwritten during update on package installations. Use enabled to turn this option on only until the next update. Use true to turn it on permanently.

Default: unset

ZAMMAD_SAFE_MODE

Ignore availability of third-party services when running Zammad commands. Possible values: 1 or true

Warning

Be careful when running Zammad commands on production systems in safe mode.

While it may allow an escape hatch for certain commands, it has a potential to break regular Zammad operations.

Default: unset

ZAMMAD_HTTP_TYPE

Defines the HTTP protocol of your instance. Possible values: http or https

Default: http

ZAMMAD_FQDN

Defines the fully qualified domain name of the system.

Default: zammad.example.com

🖧 Network Options

Note

Remember to update your web server config to reflect any changes you make here.

ZAMMAD_BIND_IP

The IP address that the web server is bound to.

Default: 127.0.0.1

ZAMMAD_RAILS_PORT

The port that the web server is exposed on.

Default: 3000

ZAMMAD_WEBSOCKET_PORT

The port that the web socket server is exposed on.

Default: 6042

🎛️ Performance Tuning

Each of below settings comes with its own tradeoffs.

There are no “recommended values” here; the optimal configuration will depend on your system’s resources and typical application load.

Proceed with caution; when adjusting any of these settings, there is a point at which performance will begin to degrade rather than improve, or other problems will begin to crop up.

Below settings may consume all available database connections. Please consider the database server configuration section for more.

To find out how many users are currently on Zammad, you can use the rails command below:

$ zammad run rails r "p Sessions.list.uniq.count"
WEB_CONCURRENCY

How many instances of the application server to keep open at a time.

Increasing this can reduce loading times when too many users are on Zammad at once.

Default: unset

ZAMMAD_SESSION_JOBS_CONCURRENT

How many instances of the session worker to run at a time.

Increasing this can speed up background jobs (like the scheduler) when too many users are on Zammad at once.

Generally speaking, it should only be useful to adjust this setting if you have more than 40 active users at a time.

Warning

🥵 Session workers can be extremely CPU-intensive.

In some cases, they can reach 100% CPU utilization on their own. Increasing this setting is safer on systems with more cores.

Default: unset

ZAMMAD_PROCESS_SCHEDULED_JOBS_WORKERS

Allows spawning an independent process just for processing scheduled jobs like LDAP syncs. This can free up Zammads background worker for other tasks when running tasks that require fairly long.

Default: unset
Maximum number of workers: 1

Danger

Disable processing of scheduled jobs by setting ZAMMAD_PROCESS_SCHEDULED_JOBS_DISABLE.

Doing so on productive instances will draw important parts of your instance not working. WE STRONGLY encourage against using this flag.

ZAMMAD_PROCESS_DELAYED_JOBS_WORKERS

How many processes should work on delayed jobs?

Increasing this can improve issues with delayed jobs stacking up in your system. You may want to try to use ZAMMAD_SESSION_JOBS_CONCURRENT before though.

Default: unset
Maximum number of workers: 16

Warning

🥵 This option can be very CPU-intensive.

Danger

Disable processing of delayed jobs by setting ZAMMAD_PROCESS_DELAYED_JOBS_DISABLE.

Doing so on productive instances will draw important parts of your instance not working. WE STRONGLY encourage against using this flag.


Note

The options listed below allow you to distribute Zammad processes over several application nodes. Even if that’s not your goal, they may provide great benefits on bigger installations.

Please note that distribution of processes on several nodes is out of the scope of this documentation for various reasons.

REDIS_URL
Store your web socket connection information within Redis.
To do so, tell Zammad where to find your Redis instance: redis://your.redis.server:6379

If not provided, Zammad falls back to file system (/opt/zammad/tmp/websocket_*).

Default: unset

MEMCACHE_SERVERS
Store your application cache files within Memcached.
To do so, tell Zammad where to find your Memcached instance: your.memcached.server:11211

If not provided, Zammad falls back to file system (/opt/zammad/tmp/cache*).

Memcached allows you to restrict the maximum size Zammad may store as cache. This comes in handy in terms of performance and keeping caching files small. 1 GB should be a reasonable size.

Storage Options

S3_URL

Allows you to provide your S3 configuration. Please have a look in our admin documentation, where the setup of S3 storage is described.

Format / example: https://key:secret@s3.eu-central-1.amazonaws.com/zammad-storage-bucket?region=eu-central-1&force_path_style=true

Configure Database server

Note

Parts of this page also applies to both supported database servers. We can’t provide a complete how to and will only enlighten the relevant parts for Zammad.

Within database.yml (config/ directory) you can define the allowed pool size. By default each Zammad process takes up to 50 connections (pool: 50).

This should be fairly enough for every use case. If you experience database connection timeouts or similar pool errors, this usually indicates to other issues that are relevant to your PostgreSQL.

Note

Below only affects PostgreSQL-Servers. All relevant steps for MySQL are mentioned on Software because they’re relevant before installation.

Below you can the locations of the relevant PostgreSQL configuration files to adjust. Keep in mind that versions may differ from your setup - adapt where needed.

/etc/postgresql/{your version}/main/postgresql.conf
Adjust max_connections (mandatory)

Zammad will take up to 200 connections by default, with below command you can raise this limit fairly high.

# Raise maximum allowed number of connections
$ sed -i "/max_connections/c\max_connections = 2000" <postgresql-configuration-file>

# Apply changes by restarting postgresql and Zammad (in this order)
$ systemctl restart postgresql zammad
Adjust PostgreSQL for bigger instances (optional)

Warning

Check below settings first and ensure your system is able to provide the requirements! Below settings are what we found to be useful, everything else is out of scope of this documentation!

# Caching improvements
$ sed -i "/shared_buffers/c\shared_buffers = 2GB" <postgresql-configuration-file>
$ sed -i "/temp_buffers/c\temp_buffers = 256MB" <postgresql-configuration-file>
$ sed -i "/work_mem/c\work_mem = 10MB" <postgresql-configuration-file>
$ sed -i "/max_stack_depth/c\max_stack_depth = 5MB" <postgresql-configuration-file>

# Apply changes by restarting postgresql and Zammad (in this order)
$ systemctl restart postgresql zammad

Migrate to PostgreSQL server

Support for MySQL/MariaDB will be dropped in Zammad 7.0 upwards. Make sure to migrate your existing instance of Zammad to PostgreSQL.

The following guide will provide you with a rough direction through that migration process.

Note

🤓 Zammad version requirement ahead

Below commands will only work with Zammad 5.3.0 or higher. Please make sure to update the latest (MySQL supported) version first.

Warning

Proof of concept ahead

As the technical details may differ from system to system, this guide comes without any warranty. Please proceed at your own risk. In doubt please refer to the documentation of the tools used.

Preparation

  1. Stop Zammad:

    $ systemctl stop zammad
    
  2. Create a backup of your instance.

Install PostgreSQL

$ apt update
$ apt install postgresql postgresql-contrib
$ systemctl start postgresql
$ systemctl enable postgresql

Please also have a look at Configure Database server.

Nothing to do, continue with the next step. 🎉

Install pgloader

$ apt update
$ apt install pgloader

Create pgloader command file

Create a command file for pgloader with:

$ zammad run rake zammad:db:pgloader > /tmp/pgloader-command

Afterwards, you need to tweak the created file with the correct URL of the target PostgreSQL server.

-- Adjust the PostgreSQL URL below to correct value before executing this command file.
INTO pgsql://zammad:pgsql_password@localhost/zammad

You will at least need to replace psql_password placeholder in the provided example.

Verify the rest of the MySQL credentials in the command file, they should reflect the configuration of your current environment.

Database Credentials

Adjust the configuration file to fill in the credentials for your new PostgreSQL server. Use postgresql as adapter.

Tip

🤓 For easiest usage …

If you provide your Zammad user with database creation permission, you can run db:create in the following section. If you don’t want that, you’ll have to create the database manually.

Create empty database

Now you need to create an empty database in PostgreSQL.

$ zammad run rake db:create

Migrate

You can check your configuration by running pgloader in a dry run first:

$ pgloader --dry-run /tmp/pgloader-command

Finishing

After the migration has completed, you’ll better clear some cache files:

$ zammad run rails r 'Rails.cache.clear'
$ systemctl start zammad

Privacy & Data Retention

How long does Zammad hold onto user data? How can I manage its user data retention behavior?

On-Premises Data

The following kinds of data are stored locally on the production system:

Tickets and users

By default, Zammad never automatically deletes tickets or users.

To enable automatic deletion of tickets after a given interval, use the scheduler.

To manually delete users and all their associated tickets (e.g. in compliance with a “Right to Forget” request under the GDPR), you can use the data privacy functions in the admin panel or use the console.

Chat sessions

Once a chat session has been marked closed, it is scheduled for automatic deletion 12 months later.

IP address logs for chat sessions can be manually deleted by following the directions here.

CTI caller log

The caller log shows only the 60 most recent entries. Each entry in the caller log is automatically deleted after 12 months.

Log files

Zammad writes log files to disk (typically under /opt/zammad/log/).

Package installations will set up a separate system utility called logrotate to rename and archive (or rotate) log files on a nightly basis and remove old logs after 14 days.

If installing from source, it is strongly recommended to configure logrotate or a similar log management utility; Zammad will not purge old logs on its own.

User sessions

Zammad maintains session information about every user currently logged in.

This information is automatically purged when a user logs out, and can be viewed or manually deleted via the admin panel (under System → Sessions). Users may also delete their own session information via the user preferences menu, under Device.

Session information includes IP address (and possibly geographic location), browser, time of original login, and time of last visit.

Data Privacy Tasks

Each entry in the data privacy task list is automatically deleted after 12 months.

External Services

Zammad utilizes third-party web services for certain functions, meaning that user data may occasionally be sent or exposed to third parties. These functions can be individually disabled in the admin panel under Settings → System → Services.

Note

By default, the third-party services that Zammad relies on are mostly ones hosted and managed by the Zammad Foundation itself, but Zammad can be extended to interface with other services instead.

The source code for these third-party service integrations can be found here.

Images

No private images or personally-identifying information are stored on images.zammad.com.

The Images service caches publicly-available images from sources like Gravatar and serves them to the Zammad application as user avatars and organization logos. These images are discovered using MD5 digests of user email addresses and organization domain names. User avatars are cached for 7 days; organization logos are cached for 30 days.

GeoCalendar

No user information is stored or cached on geo.zammad.com.

As part of its service-level agreement (SLA) functionality, Zammad requires detailed, localized calendar information (e.g., to set the time zone and accommodate national holidays and daylight savings time). The GeoCalendar service is used to retrieve this information.

GeoIP

No user information is stored or cached on geo.zammad.com.

One of Zammad’s security features is to track user sessions based on the user’s browser and country of origin. Suspicious login activity from a different browser or country may trigger Zammad to dispatch an alert email to the affected user. The GeoIP service is used to associate IP addresses to a geographic origin.

Geolocation

Since Zammad’s geolocation service relies on Google’s Geocoding API, its use is subject to the Google Privacy Policy.

Zammad uses geolocation to associate tickets with locations to support map-style ticket overviews, which display tickets as points on a map rather than items in a list.

Troubleshooting and FAQ

This guide will discuss frequently asked questions and how to resolve common problems with Zammad.

Note

🤓 Troubleshooting unsuccessful or issue not described?

If you can’t solve your issue using the provided troubleshooting steps or can’t find your particular issue described here, feel free to ask the community for technical assistance.

Data missing from the Web-UI / Search data missing or incomplete

A commonly reported issue is data missing from the Web-UI. This could be tickets, articles, users or anything else indexed by Elasticsearch and can be caused by missing or incomplete indexes.

If you are experiencing this issue and installed Elasticsearch according to Set up Elasticsearch, please follow these steps to make sure Elasticsearch is working correctly.

Step 1: Verify Elasticsearch is running
# check elasticsearch status
$ systemctl status elasticsearch

This should output something like the following, make sure it says Active: active (running):

 elasticsearch.service - Elasticsearch
   Loaded: loaded (/lib/systemd/system/elasticsearch.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2021-07-20 09:38:21 UTC; 1h 4min ago
   Docs: https://www.elastic.co
   Main PID: 1790 (java)

Otherwise, try starting it and check again:

# restart elasticsearch and check status
$ systemctl restart elasticsearch
$ systemctl status elasticsearch

Warning

If this fails, your Elasticsearch installation is probably broken.
Try completely purging and reinstalling Elasticsearch according to Set up Elasticsearch
Step 2: Verify the ingest-attachment plugin is installed correctly
# list installed elasticsearch plugins
$ /usr/share/elasticsearch/bin/elasticsearch-plugin list

The output should include ingest-attachment.

Otherwise, try reinstalling the ingest-attachment plugin and check again:

$ /usr/share/elasticsearch/bin/elasticsearch-plugin remove ingest-attachment
$ /usr/share/elasticsearch/bin/elasticsearch-plugin install ingest-attachment

$ systemctl restart elasticsearch

$ /usr/share/elasticsearch/bin/elasticsearch-plugin list
Step 3: Verify Zammad can access Elasticsearch and rebuild the indexes
# force zammad to drop and rebuild the elasticsearch indexes
$ zammad run rake zammad:searchindex:rebuild

Optionally, you can specify a number of CPU cores which are used for rebuilding the searchindex, as in the following example with 8 cores:

$ zammad run rake zammad:searchindex:rebuild[8]

This should start rebuilding the indexes and output it’s progress:

Dropping indexes... done.
Deleting pipeline... done.
Creating indexes... done.
Creating pipeline... done.
Reloading data...
   - Chat::Session...
      done in 0 seconds.
   - Cti::Log...
      done in 0 seconds.

[...]

Depending on the system performance and amount of data, this can take a while to complete. Please let this task finish completely and wait until it drops you back to the console.

If this fails or throws an error, there might be something else wrong with your installation. Make sure you followed the complete Elasticsearch set up and integration procedure according to Set up Elasticsearch.

Tip

In many situations where you’re not successful with above steps, you may want to check Elasticsearch’s log file: /var/log/elasticsearch/elasticsearch.log.

After completing these steps, you should have verified your Elasticsearch installation is running and rebuilt the indexes. If this does not resolve your issue, feel free to ask the community.

Single Sign-On for Kerberos

This guide will discuss how to set up single sign-on using Microsoft Active Directory.

Note

SSO can only be configured on self-hosted installations.

Login screen with SSO button for one-click login.

As of Zammad 3.5, enabling SSO adds a new button to the sign-in page.

Conceptual Overview

Like every other web application out there, Zammad has its own logic for signing users up, storing their passwords, authenticating them, and managing their sessions.

If your IT department keeps its own user identity store (like Active Directory), Zammad’s SSO support allows you to leverage that existing auth system so that anyone with an account on your local intranet will 1) automatically have an account in Zammad and 2) be able to log in with a single click.

Note

If you don’t have this IT infrastructure but still want one-click login, see Third-Party Authentication for alternatives.

How does it work?

Once enabled, single sign-on activates an endpoint at https://your.zammad.host/auth/sso. When the Zammad server receives a GET request at this endpoint with a valid username in any one of the following:

  • an X-Forwarded-User request header

  • a REMOTE_USER web server environment variable

  • an HTTP_REMOTE_USER web server environment variable

it creates a new session for that user.

Note

😬 Wait. SSO allows you to sign in with only a username?

In principle, yes.

How is that okay?

In this guide, we configure our web server (Apache) to intercept all requests to the /auth/sso endpoint. Instead of forwarding them to Zammad, Apache initiates a three-sided login process (Kerberos authentication) between the itself, the user, and the Active Directory server.

If Active Directory doesn’t recognize the user or their password, Zammad never sees the request, and the session is never created.

What does this all mean?

It means there are many ways you could set up SSO—you don’t need to follow this guide or even use Active Directory or Kerberos—but if you don’t know what you’re doing, you’re going to end up with a massive security hole.

Getting Started

Hint

😵 Too busy to handle it on your own?

We’ve got you covered. Our experts offer custom-tailored workshops to get your team up and running fast and with confidence. Just drop us a line!

You will need:

  • a Microsoft Active Directory environment with

    • root access

    • support for AES 256-bit encryption

  • a Zammad host with

    • root access

    • a fully-qualified domain name (FQDN)

  • some familiarity with system administration (e.g., Apache configuration)

For best results, set up LDAP integration to make sure your Active Directory and Zammad user accounts are always in sync.

Step 1: Configure Active Directory

In the Kerberos authentication scheme, the authentication server (Active Directory) needs to maintain shared secrets with the service (Zammad). To make this possible, we need to register a service principal name (SPN) for Zammad on Active Directory.

Note

These directions have been confirmed on Windows Server 2016.

1a. Create a service account

You may use an existing service account if you have one. Admin privileges are not required; a normal user account will do.

Login screen with SSO button for one-click login.

Select “This account supports Kerberos AES 256 bit encryption” under Properties > Account > Account options.

1b. Register an SPN for Zammad

Replace the following placeholders in the command below:

<zammad-host>

Zammad FQDN

<service-acct>

Service account logon name

<password>

Password of the service account (Option /pass * did prove to not work)

<domain>

Windows domain

<master-domain-controller>

Master domain controller IP/FQD

Below command will prompt for the users password:

$ setspn -s HTTP/<zammad-host> <service-acct>
$ ktpass /princ HTTP/<zammad-host>@<DOMAIN> \
         /mapuser <service-acct> \
         /crypto AES256-SHA1 \
         /ptype KRB5_NT_PRINCIPAL \
         /pass <password> -SetPass +DumpSalt \
         /target <master-domain-controller> \
         /out zammad.keytab

1c. Note the secret key and version number

The output of the command above contains important data for Step 2e below:

Using legacy password setting method
Failed to set property 'servicePrincipalName' to 'HTTP/<zammad-host>' on Dn 'CN=Zammad Service,DC=<domain>,DC=<tld>': 0x13.
WARNING: Unable to set SPN mapping data.
If <service-acct> already has an SPN mapping installed for HTTP/<zammad-host>, this is no cause for concern.
Building salt with principalname HTTP/<zammad-host> and domain <domain> (encryption type 18)...
Hashing password with salt "<domain><service-acct>".
Key created.
Output keytab to zammad.keytab:
Keytab version: 0x502
keysize 67 <service-acct>@<domain> ptype 1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x12 (AES256-SHA1) keylength 32 (0x5ee827c30c736dd4095c9cbe146eabc216415b1ddb134db6aabd61be8fdf7fb1)

On the last line, take note of:

the secret key

in parentheses at the end (0x5ee827…)

the secret key version number

preceded by vno (3)

Step 2: Remove NGINX, Set up Apache + Kerberos

Next, the Zammad host must be configured to support Kerberos (and to accept auth credentials provided by the Active Directory server).

In most cases, you would have to recompile NGINX from source with an extra module to enable Kerberos support. To get around this, we will use Apache, which offers Kerberos support through a plug-in module instead.

Note

All commands in this section must be run as root (or with sudo).

2a. Turn off NGINX

Warning

This will take your Zammad instance offline until Apache is fully configured and running.

$ systemctl stop nginx     # turn off nginx
$ systemctl disable nginx  # keep it off after reboot

If you wish to minimize downtime, you can save this step for last; just bear in mind that Apache will not start if the port it wants to listen on is being used by NGINX.

If for any reason you can’t complete this tutorial, simply turn off Apache and restore NGINX:

$ systemctl stop apache2
$ systemctl disable apache2
$ systemctl enable nginx
$ systemctl start nginx

2b. Pre-Configure Apache

This documentation expects a already working Apache configuration. Please see Configure the webserver before continuing.

2c. Install further Apache dependencies

$ apt update
$ apt install krb5-user libapache2-mod-auth-kerb

2d. Enable Apache modules

SSO requires modules that are not enabled by default. By default you can use a2enmod to do so.

$ a2enmod auth_kerb rewrite
$ systemctl restart apache2

2e. Configure Kerberos

Kerberos realm configuration is how you tell the Zammad server how to reach the domain controller (Active Directory server).

Replace the following placeholders in the sample config below:

<domain>

Windows domain

<domain-controller>

Domain controller IP/FQDN(s)

<master-domain-controller>

Master domain controller IP/FQDN

(must not be read-only, but can be the same as <domain-controller>)

# /etc/krb5.conf

[libdefaults]
  default_realm = <DOMAIN>
  default_tkt_enctypes = aes256-cts-hmac-sha1-96
  default_tgs_enctypes = aes256-cts-hmac-sha1-96
  permitted_enctypes = aes256-cts-hmac-sha1-96

  kdc_timesync = 1
  ccache_type = 4
  forwardable = false
  proxiable = false
  fcc-mit-ticketflags = false

[realms]
        # multiple KDCs ok (one `kdc = ...` definition per line)
        <DOMAIN> = {
                kdc = <domain-controller>
                admin_server = <master-domain-controller>
                default_domain = <domain>
        }

[domain_realm]
        .<domain> = <DOMAIN>
        <domain> = <DOMAIN>

2f. Generate keytab

Apache needs a Kerberos keytab (key table) to manage its shared secrets with the domain controller.

Replace the following placeholders in the commands below:

<zammad-host>

Zammad FQDN

<domain>

Windows domain

<secret-key>

Secret key (omit the leading 0x)

<vno>

Secret key version number

The secret key and version number were found in Step 1: Configure Active Directory above.

$ ktutil

ktutil: addent -key -p HTTP/<zammad-host>@<DOMAIN> -k <vno> -e aes256-cts
Key for HTTP/<zammad-host>@<domain> (hex): <secret-key>

ktutil: list  # confirm the entry was added successfully
slot KVNO Principal
---- ---- ---------------------------------------------------------------
   1    3 HTTP/<zammad-host>@<DOMAIN>

ktutil: wkt /root/zammad.keytab  # write keytab to disk

ktutil: quit

Then, place the keytab in the Apache config directory and set the appropriate permissions:

$ mv /root/zammad.keytab /etc/apache2/
$ chown www-data:www-data /etc/apache2/zammad.keytab
$ chmod 400 /etc/apache2/zammad.keytab

2g. Configure Apache

Add the following directive to the end of the virtual host configuration file to create your Kerberos SSO endpoint at /auth/sso:

Replace the following placeholders in the command below:

<zammad-host>

Zammad FQDN

<domain>

Windows domain

The configuration for CentOS and OpenSUSE below contains two Krb5KeyTab lines! Keep only the one you need.

<LocationMatch "/auth/sso">
   SSLRequireSSL
   AuthType Kerberos
   AuthName "Your Zammad"
   KrbMethodNegotiate On
   KrbVerifyKDC On
   KrbMethodK5Passwd On
   KrbAuthRealms <DOMAIN>
   KrbLocalUserMapping on                 # strips @REALM suffix from REMOTE_USER variable
   KrbServiceName HTTP/<zammad-host>@<DOMAIN>
   Krb5KeyTab /etc/apache2/zammad.keytab  # Ubuntu, Debian, & openSUSE
   Krb5KeyTab /etc/httpd/zammad.keytab    # CentOS
   require valid-user

   RewriteEngine On
   RewriteCond %{LA-U:REMOTE_USER} (.+)
   RewriteRule . - [E=RU:%1,NS]
   RequestHeader set X-Forwarded-User "%{RU}e" env=RU
</LocationMatch>

2g. Restart Apache to apply changes

$ systemctl restart apache2

Step 3: Enable SSO in Zammad

Next, enable “Authencation via SSO” in Zammad’s Admin Panel under Settings > Security > Third-Party Applications:

“Authentication via SSO” toggle button in the Admin Panel

In Zammad 3.5, this option adds a Sign in using SSO button to the sign-in page.

Note

On older versions of Zammad, visit https://your.zammad.host/auth/sso to sign in.

Step 4: Configure Client System (Windows Only)

For the full SSO experience (i.e., for passwordless, one-click sign-in), Zammad users must:

  1. be on the Active Directory server’s local intranet; and

  2. modify their network settings for the Zammad host to be treated as a local intranet server.

In-browser login prompt for single sign-on

Without this step, users must enter their Active Directory credentials during SSO.

Tip

This setting can be centrally managed across the entire intranet using a group policy object (GPO).

  1. Add your Zammad FQDN in Internet Options under Security > Local Intranet > Sites > Advanced.

  2. Select “Require server verification (https:) for all sites in this zone”.

  3. Under Security level for this zone > Custom level… > Settings > User Authentication > Logon, select “Automatic logon only in Intranet Zone”.

Adding Zammad as a single sign-on site in Windows Internet options

Troubleshooting

  • Are all relevant FQDNs/hostnames reachable from your Active Directory and Zammad servers (including each other’s)?

  • Are the system clocks of your Active Directory and Zammad servers synchronized within five minutes of each other? (Kerberos is a time-sensitive protocol.)

Errors in Apache Logs

Tip

Try raising your Apache log level temporarily.

Add LogLevel debug to your virtual host configuration, then restart the service to apply the changes.

“an unsupported mechanism was requested”

Does your Active Directory service account have Kerberos AES 256-bit encryption enabled?

If for some reason your server does not support AES 256-bit encryption, the LDAP Wiki has more information about Kerberos encryption types.

“failed to verify krb5 credentials: Key version is not available”

Did you use the exact version number (vno) provided by ktpass when generating your keytab?

Try generating it again, just to be sure.

“unspecified GSS failure. Minor code may provide more information (, No key table entry found for HTTP/FQDN@DOMAIN)”

Does the service name you provided to setspn exactly match the one you used when generating your keytab?

Try generating it again, just to be sure.

“No key table entry found for HTTP/FQDN@DOMAIN”

Does your virtual host configuration’s KrbServiceName setting exactly match the service name you provided to setspn?

This setting is case-sensitive.

“Warning: received token seems to be NTLM, which isn’t supported by the Kerberos module. Check your IE configuration”

Is your Zammad host accessible at an FQDN? This error may indicate that you configured your Zammad host as a numeric IP address instead.

“Cannot decrypt ticket for HTTP/FQDN@DOMAIN”

Did you make sure to change the password on your Active Directory service account after enabling 256-bit AES encryption?

And did you make sure to register the SPN (with ktpass) and generate your keytab (with ktutil) after changing your password?

Try running kinit -k -t <path to keytab> HTTP/<zammad-host>@<DOMAIN>. If no output is returned, you’re good - if you see “kinit: Preauthentication failed while getting initial credentials” your credentials provided were wrong or you used /pass * during ktpass command.

“failed when verifying KDC” and “failed to verify krb5 credentials: Decrypt integrity check failed”

Ensure KrbServiceName is the correct ServiceName provided via setspn.

Ensure your Active Directory supports the encryption method configured.

If all above is correct and the rest of FAQ also is ensured, make sure your client does not cache the results. klist purge clears the clients cache - a reboot of your client would do too.

Reporting Tools (Third party)

This guide will discuss how to set up third party reporting tools with Zammad.

Screenshot showing a Grafana dashboard fed from Zammad data.

Use third party reporting tools to boost your reporting capabilities.

Note

🚧 Limitations 🚧

Please note that this guide expects all requirements to be up and running. We will not cover core configurations of each tool. Please also note that we can’t support you with configuration of your specific third party tool.

🤓 Specific use cases

You may have specific use cases which we can’t cover in this documentation. The following sub pages and also our List of Indexed Attributes should provide enough information to help you!

Getting Started

You will need

  • A instance of the reporting tool of your choice (hosted or self-hosted)

  • (read) access to your Elasticsearch index

    Warning

    Never expose Elasticsearch to the public if you’re not sure how to do it. Especially never without authentication! Zammad stores very sensitive information within the Elasticsearch Index.

  • a Zammad instance (version 4 or above) that supports your use case

Third Party Reporting Tools known to be working

Grafana

Grafana allows you to query, visualize and alert on metrics your Zammad installation stores within the Elasticsearch indexes.

Sample Dashboard with metrics from a Zammad instance.

Note

🚧 Limitations 🚧

Please note that this guide expects all requirements to be up and running. We will not cover core configurations of each tool. Please also note that we can’t support you with configuration of your specific third party tool.

🤓 Specific use cases

You may have specific use cases which we can’t cover in this documentation. The following sub pages and also our List of Indexed Attributes should provide enough information to help you!

Overview
Quickly jump to…
You will need
  • A Grafana 10.3+ instance (hosted or self hosted)

  • (read) access to your Elasticsearch index

    Warning

    Never expose Elasticsearch to the public if you’re not sure how to do it. Especially never without authentication! Zammad stores very sensitive information within the Elasticsearch Index.

  • a Zammad instance (version 4 or above) that supports your use case

Setting up required data sources

Hint

🤓 You may not need all data sources

Before we start: The data sources always follow the same scheme. We reduced below information to name, time field name and index name. Everything else relies on your environment and is out of our scope.

Note

Please replace zammad_production_ with your fitting prefix.

ES - Chat Sessions:
Index name: zammad_production_chat_session
Time field name: created_at
ES - CTI Log:
Index name: zammad_production_cti_log
Time field name: start_at
ES - Ticket Articles:
Index name: zammad_production_ticket
Time field name: article.created_at
ES - Tickets by closed_at:
Index name: zammad_production_ticket
Time field name: close_at
ES - Tickets by created_at:
Index name: zammad_production_ticket
Time field name: created_at
ES - Tickets by first_response_at:
Index name: zammad_production_ticket
Time field name: first_response_at

With above data sources you basically have everything you need to start building your own dashboards. 🎉

Tip

🤓 Not sure about your index names?

Querying your Elasticsearch like below

# Replace localhost:9200 with the IP/URL of your setup if needed
$ curl http://localhost:9200/_aliases?pretty=true

will return a list that looks similar to the following:

{
  "zammad_production_knowledge_base_translation" : {
    "aliases" : { }
  },
  "zammad_production_ticket_priority" : {
    "aliases" : { }
  },
  "zammad_production_stats_store" : {
    "aliases" : { }
  },
  "zammad_production_organization" : {
    "aliases" : { }
  },
  "zammad_production_cti_log" : {
    "aliases" : { }
  },
  "zammad_production_group" : {
    "aliases" : { }
  },
  "zammad_production_knowledge_base_answer_translation" : {
    "aliases" : { }
  },
  "zammad_production_ticket" : {
    "aliases" : { }
  },
  "zammad_production_ticket_state" : {
    "aliases" : { }
  },
  "zammad_production_chat_session" : {
    "aliases" : { }
  },
  "zammad_production_user" : {
    "aliases" : { }
  },
  "zammad_production_knowledge_base_category_translation" : {
    "aliases" : { }
  }
}
The Dashboards

If you want to get inspired, you can also use our sample dashboards as mentioned below. These dashboards can also be found on GitHub.

Importing an existing Dashboard

Navigate to ➕ → Import and either upload the json file you received or use the grafana.com ID. During importing you can provide a dashboard name and folder. You’ll also be asked to map the data sources to your environment. If you used our data source names above, you can simply search for the same name.

Screencast showing how to import existing dashboards by grafana.com ID

Importing existing dashboards by ID

Ticket statistics

Tip

🤓 Grafana.com ID: 14222

Screenshot showing the Ticket dashboard with demo data.
This dashboard provides graphs for:
  • ticket opening and closing 2

  • created articles

  • ticket SLA (in time and violation) per type 2 3

It also contains specific ticket and article meta information:
  • ticket group distribution

  • sender ratio (e.g. Customer / Agent) 1

  • article type ratio (e.g. email, phone) 1

  • article content type

  • escalation ratios 2

  • average first response, update time and close time 3

  • top 10

    • organization of ticket customer 2

    • ticket customers 2

    • ticket owners 2

    • average accounted time on ticket

    • ticket tags 2

  • last 10 escalated tickets

Required data sources:
  • ES - Ticket Articles

  • ES - Tickets by created_at

  • ES - Tickets by closed_at

1(1,2)

Specific reference IDs are not the same on every instance and thus the panel may not work or show incorrect data. Check the panels description on how to find our the relations on your system.

2(1,2,3,4,5,6,7)

Some values are not available as time series information. This means we can only display the last value of the field in question.

3(1,2)

Requires SLA function to be active. Negative values indicate SLA violations.

Chat-Session statistics

Tip

🤓 Grafana.com ID: 14224

Screenshot showing the Chat dashboard with demo data.
This dashboard provides graphs for:
  • Chat session creations

It also contains specific chat session meta information:
  • top 10

    • chat tags

    • chat agents

    • chat exit pages

    • city origins

  • chat topic ratio

  • average number of messages within chat-sessions

  • average chatting time

  • World map with chat origin countries

Required data sources:
  • ES - Chat Sessions

CTI-Log statistics

Tip

🤓 Grafana.com ID: 14223

Screenshot showing the CTI dashboard with demo data.
This dashboard provides graphs for:
  • number of calls per direction (in / out)

It also contains specific chat session meta information:
  • call ratio (in / out)

  • average waiting time

  • average talking time

  • top 10

    • callers (in)

    • call answerers (in)

Required data sources:
  • ES - CTI Log

Note

Want to use another tool?

Don’t worry, if it does support Elasticsearch Indexes, you may be good to go! See List of Indexed Attributes for available indexes.