# Zammad MCP Server — 206 tools via DADL

The Zammad DADL turns Zammad's API into an MCP server that Claude, GPT or any MCP-compatible agent can consume directly. One YAML file declares all 206 tools — ticket, checklist, user, organization, calendar, email, and more — and ToolMesh serves them at runtime. No Python boilerplate, no per-endpoint code, no separate MCP server process.

Below: the endpoint coverage matrix, a two-block ToolMesh setup, the full tool reference grouped by Zammad feature area, required credential scopes.

**Source:** [Zammad REST API](https://docs.zammad.org/en/latest/api/intro.html)

**Updated:** 2026-05-22

**Tags:** project-management, crud, user-management, notifications, logging, issue-tracking, automation, security, auth:apikey

## Which Zammad endpoints are covered?

**93%** (205 of ~220 endpoints).

**Focus:** tickets (CRUD + search + history + merge + summarize + tags + links + mentions + shared_draft + time_accounting), ticket_articles (CRUD + attachments inline base64), ticket_states/priorities (CRUD), global search (across all models + per-model + tag autocomplete), users (CRUD + me + search + password + preferences + devices + access_tokens + avatar + email_verify), organizations (CRUD + search), groups (CRUD), roles (CRUD), online_notifications (list + mark_seen + mark_all_read), object_manager_attributes (CRUD + execute_migrations), calendars (CRUD), slas (CRUD), knowledge_bases (init + categories + answers + publish/archive + permissions + reorder + attachments), checklists (CRUD + items + templates), macros, triggers, overviews, jobs (schedulers), reports, signatures, text_modules, templates, email_addresses, postmaster_filters, translations, settings, tag_list admin + tag_search, data_privacy_tasks, activity_stream, karma, monitoring (health/status/amount_check)

**Missing:** external_credentials (OAuth setup for Twitter/Facebook/Google/Microsoft), form_submit (public embed form), upload_caches (form_id-based -- Web-UI internal), getting_started wizard, sessions long-polling (/message_send + /message_receive), email channel probe/verify sub-actions, channels generic CRUD, two-factor admin

*Last reviewed: 2026-05-22*

## How do you configure the Zammad DADL?

1. Log in to your Zammad instance as the user who will own the token
2. Click your avatar (bottom-left of the sidebar) -> Profile -> Token Access
3. If 'Token Access' is hidden, an admin must grant the role permission 'user_preferences.access_token' (Admin -> Roles)
4. Click 'Create', fill in a label (e.g. 'ToolMesh Integration')
5. Tick the required permissions: ticket.agent (or ticket.customer), admin.user, admin.organization, knowledge_base.editor, etc. -- the token cannot exceed the user's own permissions
6. Optionally set an expiry date (YYYY-MM-DD)
7. Click 'Create' -- the token is shown ONCE, copy it immediately (cannot be recovered, only revoked)
8. Provide only the raw token value in your credential store -- ToolMesh prepends 'Token token=' automatically

**Environment variable:** `CREDENTIAL_ZAMMAD_TOKEN`

[Authentication docs](https://docs.zammad.org/en/latest/api/intro.html)

*The url MUST include the /api/v1 suffix, e.g.
"https://helpdesk.example.com/api/v1". Zammad is self-hosted, no
central endpoint.

Impersonation modes (X-On-Behalf-Of):
  - Off            -- no header sent, action recorded as token owner.
  - Pass-through   -- the calling agent supplies X-On-Behalf-Of per
                      call (e.g. forwards the end-user's email).
  - Fixed user     -- operator pins a value in backends.yaml under
                      headers.X-On-Behalf-Of. Tool-level header still
                      wins when supplied.

The token cannot exceed the creating user's permissions. For full
admin access, create the token as an admin account; for pure
customer self-service flows, create it as a customer.
*

## How do you install the Zammad MCP server with ToolMesh?

Add to your `backends.yaml`:

```yaml
- name: zammad
  transport: rest
  dadl: zammad.dadl
  url: "https://helpdesk.example.com/api/v1"
  # Optional fixed-user impersonation: pin every outbound request
  # to act on behalf of a specific Zammad user. Comment out for
  # default (token owner) behaviour or pass-through mode.
  # headers:
  #   X-On-Behalf-Of: "bot@example.com"

```

Set the credential:

```
CREDENTIAL_ZAMMAD_TOKEN=your-token-here
```

## What 206 tools does the Zammad DADL expose?

- **GET** `list_tickets` — List all tickets visible to the authenticated user. Paginated. Use search_tickets for filtered queries -- this endpoint has no filter parameters beyond pagination.
- **GET** `get_ticket` — Retrieve a single ticket by ID. Set all_articles=true to include the full article list under .articles.
- **POST** `create_ticket` — Create a new ticket with an initial article. Required: title, group (group name or group_id), customer (email or customer_id), and an article object {subject, body, type, internal}. Returns the created ticket with its first article. Use X-On-Behalf-Of header to record the ticket as created by another user.
- **PUT** `update_ticket` — Update an existing ticket. Any field can be partially updated. Pass an 'article' object alongside other fields to append a new article in the same call (common pattern for replying with a state change).
- **DELETE** `delete_ticket` — Permanently delete a ticket. Requires admin permission. Irreversible -- consider closing/merging instead.
- **GET** `search_tickets` — Full-text ticket search using Elasticsearch query syntax when ES is enabled, or limited SQL otherwise. Examples: 'state.name:open', 'priority.name:"2 normal" AND tags:feedback', 'customer.email:*@example.com AND created_at:[2026-01-01 TO 2026-12-31]'.
- **GET** `get_ticket_history` — Get the full audit history for a ticket -- all changes, article additions, state transitions, with timestamps and acting users.
- **POST** `merge_tickets` — Merge slave ticket into master ticket. All articles from slave move to master; slave is closed with state 'merged'. IRREVERSIBLE.
- **POST** `summarize_ticket` — Get an AI-generated summary of the ticket. Returns the existing summary or triggers async generation -- retry after ~30 seconds if generation is in progress.
- **GET** `global_search` — Search across ALL searchable models (Ticket, User, Organization, KnowledgeBase::Answer::Translation, Chat::Session). Returns a mixed array of result objects each tagged with .type. Use search_in_model if you only need one type -- it's much cheaper.
- **GET** `search_in_model` — Search within a single model. Cheaper and more focused than global_search. {model} must be lowercased model name: 'ticket', 'user', 'organization', 'knowledge_base/answer/translation', 'chat/session'. THIS is the canonical way to find a ticket by free text (e.g. 'find ticket about offline printer').
- **GET** `tag_search_autocomplete` — Tag-name autocomplete -- returns up to ~25 tag names matching the prefix. Used by UI search boxes.
- **GET** `list_articles_by_ticket` — List all articles belonging to a ticket. Returns chronological array.
- **GET** `get_article` — Retrieve a single ticket article by ID.
- **PUT** `update_article` — Update an existing ticket article. The most common use is toggling the `internal` flag (publish an internal note, or hide a previously public article). Other editable fields (subject, body) depend on Zammad's "ui_ticket_zoom_article_*_edit" settings -- by default, body editing is allowed for a limited time after creation by the author and by agents with the relevant permission.
- **POST** `create_article` — Append a new article to an existing ticket. Common pattern: reply via email (type=email, sender=Agent), internal note (type=note, internal=true), or customer-side message (type=web, sender=Customer with X-On-Behalf-Of=<customer email>).
- **GET** `download_attachment` — Download an attachment file. Returns the raw binary content.
- **GET** `list_tags_for_object` — Get all tags attached to a specific object (ticket, user, organization, KB answer).
- **POST** `add_tag` — Attach a tag to an object. Creates the tag if it does not exist (unless tag administration restricts this).
- **DELETE** `remove_tag` — Remove a tag from an object. Does not delete the tag definition itself.
- **GET** `list_ticket_links` — List all links between tickets. Pass link_object_value as the ticket NUMBER (not ID).
- **POST** `add_ticket_link` — Create a link between two tickets. link_type controls direction: 'normal' (peer), 'parent' (source is parent), 'child' (source is child).
- **DELETE** `remove_ticket_link` — Remove a link between two tickets.
- **GET** `list_my_mentions` — List all mentions targeting the authenticated user (or impersonated user).
- **POST** `create_mention` — Add a mention -- subscribes the current user to updates on a mentionable object (typically a ticket).
- **DELETE** `delete_mention` — Remove a mention -- unsubscribes from updates.
- **GET** `list_time_accounting` — List all time-accounting entries for a ticket.
- **GET** `get_time_accounting` — Get a single time-accounting entry.
- **POST** `create_time_accounting` — Record time spent on a ticket. time_unit is in minutes (decimals allowed).
- **PUT** `update_time_accounting` — Update an existing time entry. Admin permission required.
- **DELETE** `delete_time_accounting` — Delete a time-accounting entry. Admin permission required.
- **GET** `get_shared_draft` — Get the shared draft for a ticket (collaborative reply being composed).
- **PUT** `create_shared_draft` — Create the shared draft for a ticket.
- **PATCH** `update_shared_draft` — Update the shared draft for a ticket.
- **DELETE** `delete_shared_draft` — Delete the shared draft for a ticket.
- **GET** `list_ticket_priorities` — List all ticket priorities (e.g. '1 low', '2 normal', '3 high').
- **GET** `get_ticket_priority` — Get a single ticket priority.
- **POST** `create_ticket_priority` — Create a new ticket priority. Admin permission required.
- **PUT** `update_ticket_priority` — Update a ticket priority.
- **DELETE** `delete_ticket_priority` — Delete a ticket priority. Fails if the priority is in use.
- **GET** `list_ticket_states` — List all ticket states (e.g. new, open, pending reminder, pending close, closed, merged, removed).
- **GET** `get_ticket_state` — Get a single ticket state.
- **POST** `create_ticket_state` — Create a new ticket state. state_type_id references a state type (new/open/closed/pending action/pending reminder/merged/removed).
- **PUT** `update_ticket_state` — Update a ticket state.
- **DELETE** `delete_ticket_state` — Delete a ticket state. Fails if the state is in use.
- **GET** `get_me` — Get the currently authenticated user. With X-On-Behalf-Of header, returns the impersonated user.
- **GET** `list_users` — List users. Use search_users for filtered queries.
- **GET** `get_user` — Get a single user by ID.
- **GET** `search_users` — Search users by firstname/lastname/login/email (Elasticsearch syntax when enabled). Returns matching users.
- **POST** `create_user` — Create a new user. Email or login is required as a primary identifier.
- **PUT** `update_user` — Update a user. All fields are optional -- only supplied fields are changed.
- **DELETE** `delete_user` — Permanently delete a user. NOT recommended -- prefer marking inactive or using a GDPR data_privacy_task.
- **POST** `change_my_password` — Change the authenticated user's password. Requires the current password.
- **POST** `request_password_reset` — Initiate a password reset for a user (sends a reset email).
- **POST** `verify_password_reset` — Complete a password reset using the token from the reset email.
- **POST** `send_email_verification` — Send a verification email to the supplied address. Used for self-signup flows.
- **POST** `verify_email_token` — Complete an email verification by submitting the token from the verification email.
- **POST** `upload_my_avatar` — Upload an avatar image for the authenticated user. Both fields are data-URLs (e.g. 'data:image/png;base64,iVBOR...'). avatar_full is the original; avatar_resize is the cropped version (typically square).
- **GET** `get_user_avatar` — Download a user avatar by its hash (returned in user objects under image).
- **POST** `unlock_user` — Unlock a user account that was locked due to failed login attempts.
- **GET** `get_my_preferences` — Get the authenticated user's preferences (notification settings, locale, etc.).
- **PUT** `update_my_preferences` — Update the authenticated user's preferences. Body shape mirrors the user.preferences field.
- **GET** `list_my_devices` — List devices that have authenticated as the current user (browsers, mobile apps).
- **DELETE** `revoke_device` — Revoke a device session for the current user.
- **GET** `list_my_access_tokens` — List API tokens owned by the authenticated user, plus the catalog of permissions available to assign.
- **POST** `create_my_access_token` — Create a new personal access token. Token value is returned ONCE in the response under .token -- store immediately. Permissions cannot exceed the user's own.
- **DELETE** `delete_my_access_token` — Revoke a personal access token by ID.
- **GET** `list_data_privacy_tasks` — List pending and completed GDPR deletion tasks.
- **GET** `get_data_privacy_task` — Get a single data-privacy task.
- **POST** `create_data_privacy_task` — Schedule the deletion of a user (GDPR right to be forgotten). Currently only User is deletable.
- **DELETE** `cancel_data_privacy_task` — Cancel a pending data-privacy task.
- **GET** `list_organizations` — List organizations.
- **GET** `get_organization` — Get a single organization.
- **GET** `search_organizations` — Search organizations (Elasticsearch syntax when enabled).
- **POST** `create_organization` — Create a new organization.
- **PUT** `update_organization` — Update an organization.
- **DELETE** `delete_organization` — Delete an organization. Fails if the organization has users or tickets.
- **GET** `list_groups` — List all groups (ticket inboxes / queues).
- **GET** `get_group` — Get a single group.
- **POST** `create_group` — Create a new group. Nested groups use '::' as separator (e.g. 'Sales::Europe').
- **PUT** `update_group` — Update a group.
- **DELETE** `delete_group` — Delete a group. Fails if the group contains tickets or users.
- **GET** `list_roles` — List all roles (permission bundles).
- **GET** `get_role` — Get a single role.
- **POST** `create_role` — Create a new role with the given permissions.
- **PUT** `update_role` — Update a role.
- **GET** `list_online_notifications` — List the authenticated user's in-app notifications. Use expand=true for human-friendly object labels.
- **GET** `get_online_notification` — Get a single online notification.
- **PUT** `update_online_notification` — Update notification status (typically mark as seen).
- **DELETE** `delete_online_notification` — Dismiss / delete a single notification.
- **POST** `mark_all_notifications_read` — Mark all of the current user's notifications as seen.
- **GET** `list_object_attributes` — List all object-manager attributes (built-in + custom fields) across Ticket, User, Organization, Group.
- **GET** `get_object_attribute` — Get a single object-manager attribute definition.
- **POST** `create_object_attribute` — Create a custom field. NOTE: changes do not take effect until execute_object_manager_migrations is called.
- **PUT** `update_object_attribute` — Update a custom field. Requires execute_object_manager_migrations to apply.
- **DELETE** `delete_object_attribute` — Delete a custom field. Requires execute_object_manager_migrations to apply.
- **POST** `execute_object_manager_migrations` — Apply pending object-manager schema migrations. MUST be called after create/update/delete of any custom field.
- **GET** `list_calendars` — List business-hour calendars (used for SLA escalation timing).
- **GET** `get_calendar` — Get a single calendar.
- **POST** `create_calendar` — Create a business-hour calendar.
- **PUT** `update_calendar` — Update a calendar.
- **DELETE** `delete_calendar` — Delete a calendar. Fails if used by an SLA.
- **GET** `list_slas` — List Service Level Agreements.
- **GET** `get_sla` — Get a single SLA.
- **POST** `create_sla` — Create an SLA. Time values are in minutes.
- **PUT** `update_sla` — Update an SLA.
- **DELETE** `delete_sla` — Delete an SLA.
- **POST** `init_knowledge_base` — Initial KB overview -- returns the list of KBs, categories, and answer summaries.
- **GET** `get_knowledge_base` — Get a single Knowledge Base by ID.
- **PATCH** `update_knowledge_base` — Update KB settings (visibility, custom address, etc.).
- **GET** `get_kb_permissions` — Get role-based permissions for a KB. Returns an array of {role_id, access} where access is admin|editor|reader|none.
- **PUT** `set_kb_permissions` — Set role-based permissions for a KB. Pass full permissions array (replaces existing).
- **PATCH** `reorder_kb_root_categories` — Set the display order of top-level KB categories. Pass the full ordered list of category IDs.
- **GET** `get_kb_category` — Get a single KB category.
- **POST** `create_kb_category` — Create a KB category. translations_attributes is an array of {title, locale}.
- **PATCH** `update_kb_category` — Update a KB category.
- **DELETE** `delete_kb_category` — Delete a KB category. Fails if it has child categories or answers.
- **GET** `get_kb_category_permissions` — Get role permissions for a specific KB category (override category-level access).
- **PUT** `set_kb_category_permissions` — Set role permissions for a KB category (overrides KB-wide permissions for this subtree).
- **PATCH** `reorder_kb_subcategories` — Set the display order of subcategories within a category.
- **PATCH** `reorder_kb_answers` — Set the display order of answers within a category.
- **GET** `get_kb_answer` — Get a KB answer. Use include_contents=<translation_id> to inline the body content.
- **POST** `create_kb_answer` — Create a KB answer. translations_attributes must contain at least one {title, content (HTML), locale}.
- **PATCH** `update_kb_answer` — Update a KB answer.
- **DELETE** `delete_kb_answer` — Delete a KB answer.
- **POST** `publish_kb_answer` — Publish a KB answer publicly.
- **POST** `internal_kb_answer` — Publish a KB answer to internal audience only.
- **POST** `archive_kb_answer` — Archive a KB answer (soft-hide without delete).
- **POST** `unarchive_kb_answer` — Restore an archived KB answer.
- **POST** `upload_kb_answer_attachment` — Attach a file to a KB answer. Multipart upload -- file_url is fetched by ToolMesh and forwarded as multipart/form-data.
- **DELETE** `delete_kb_answer_attachment` — Remove an attachment from a KB answer.
- **GET** `get_checklist` — Get a single ticket checklist.
- **POST** `create_checklist` — Create a checklist on a ticket, optionally from a template.
- **PATCH** `update_checklist` — Update checklist (rename, reorder items).
- **DELETE** `delete_checklist` — Delete a checklist (and all its items).
- **GET** `get_checklist_item` — Get a single checklist item.
- **POST** `create_checklist_item` — Add an item to a checklist.
- **PATCH** `update_checklist_item` — Update a checklist item (text and/or checked state).
- **DELETE** `delete_checklist_item` — Delete a checklist item.
- **GET** `list_checklist_templates` — List checklist templates.
- **GET** `get_checklist_template` — Get a single checklist template.
- **POST** `create_checklist_template` — Create a reusable checklist template.
- **PATCH** `update_checklist_template` — Update a checklist template.
- **DELETE** `delete_checklist_template` — Delete a checklist template.
- **GET** `list_macros` — List macros (named action-bundles applied to tickets).
- **GET** `get_macro` — Get a single macro.
- **POST** `create_macro` — Create a macro. perform is a map of attribute -> {value, ...}.
- **PUT** `update_macro` — Update a macro.
- **DELETE** `delete_macro` — Delete a macro.
- **GET** `list_triggers` — List automation triggers.
- **GET** `get_trigger` — Get a single trigger.
- **POST** `create_trigger` — Create an event trigger. condition selects matching tickets; perform applies actions.
- **PUT** `update_trigger` — Update a trigger.
- **DELETE** `delete_trigger` — Delete a trigger.
- **GET** `list_overviews` — List saved ticket overviews (custom queues / views).
- **GET** `get_overview` — Get a single overview.
- **POST** `create_overview` — Create an overview (named ticket query) shared with selected roles.
- **PUT** `update_overview` — Update an overview.
- **DELETE** `delete_overview` — Delete an overview.
- **GET** `list_jobs` — List scheduler jobs (recurring tasks executed on a timer).
- **GET** `get_job` — Get a single scheduler job.
- **POST** `create_job` — Create a recurring scheduler job.
- **PUT** `update_job` — Update a scheduler job.
- **DELETE** `delete_job` — Delete a scheduler job.
- **POST** `generate_report` — Generate a report dataset (counts/aggregates) for a given metric over a date range.
- **GET** `list_report_sets` — List available report sets (configured profiles and backends).
- **GET** `list_signatures` — List email signatures.
- **GET** `get_signature` — Get a single signature.
- **POST** `create_signature` — Create an email signature. body supports Zammad variable placeholders.
- **PUT** `update_signature` — Update a signature.
- **DELETE** `delete_signature` — Delete a signature.
- **GET** `list_text_modules` — List text modules (canned replies / snippets).
- **GET** `get_text_module` — Get a single text module.
- **POST** `create_text_module` — Create a text module. keywords trigger autocomplete in the compose box.
- **PUT** `update_text_module` — Update a text module.
- **DELETE** `delete_text_module` — Delete a text module.
- **GET** `list_templates` — List ticket-creation templates.
- **GET** `get_template` — Get a single template.
- **POST** `create_template` — Create a ticket template. options is a map of attribute -> value used to pre-fill the create form.
- **PUT** `update_template` — Update a template.
- **DELETE** `delete_template` — Delete a template.
- **GET** `list_email_addresses` — List configured email-sender addresses.
- **GET** `get_email_address` — Get a single email address.
- **POST** `create_email_address` — Add a sender email address linked to an outbound channel.
- **PUT** `update_email_address` — Update an email address.
- **DELETE** `delete_email_address` — Delete an email address.
- **GET** `list_postmaster_filters` — List postmaster filters (email pre-processing rules: match headers, set ticket properties).
- **GET** `get_postmaster_filter` — Get a single postmaster filter.
- **POST** `create_postmaster_filter` — Create a postmaster filter. match selects emails by header; perform assigns ticket fields.
- **PUT** `update_postmaster_filter` — Update a postmaster filter.
- **DELETE** `delete_postmaster_filter` — Delete a postmaster filter.
- **GET** `list_all_tags` — List all defined tags with usage counts.
- **POST** `create_tag_definition` — Create a tag in the catalog (without attaching it to an object yet).
- **PUT** `rename_tag` — Rename a tag everywhere it is used.
- **DELETE** `delete_tag_definition` — Delete a tag from the catalog and detach it from all objects.
- **GET** `list_translations` — List UI string translations.
- **GET** `list_admin_translations` — Get all translations for a locale, including admin-overridable strings.
- **GET** `list_settings` — List system settings. Returns full setting objects -- use jq to filter by area or name.
- **GET** `get_setting` — Get a single setting by ID.
- **PUT** `update_setting` — Update a system setting. Only state_current can typically be changed; structure depends on the setting.
- **GET** `get_activity_stream` — Get the activity stream for the authenticated user (recent changes across visible objects).
- **GET** `get_recent_viewed` — Get the list of recently viewed objects for the authenticated user.
- **GET** `list_karma_users` — Get the Karma gamification leaderboard. Returns an array of {user_id, score, level} entries (level e.g. 'Beginner', 'Master'). Karma awards points for actions like first-response-within-SLA, ticket-closed, KB-answer-published. Often disabled by admins -- 404 means the feature is not enabled.
- **GET** `monitoring_health_check` — Health check endpoint. NOTE: requires a separate monitoring token passed as ?token=... query parameter (configured in Admin -> System -> Monitoring), NOT the user access token.
- **GET** `monitoring_status` — System status / counts. Requires monitoring token (see monitoring_health_check).
- **GET** `monitoring_amount_check` — Volume check (e.g. tickets created in last period).

## What composite workflows does the Zammad DADL provide?

- **FN** `reply_and_close` — Append an outbound email reply to a ticket and close it in one atomic call. Common agent workflow.
- **FN** `create_ticket_with_articles` — Create a ticket and append additional follow-up articles in sequence. Useful for migrating existing conversations.
- **FN** `find_or_create_customer` — Look up a customer by email; create them if they do not exist. Returns the user object.

## Which DADLs are related to Zammad?

- [Zulip](https://www.dadl.ai/d/zulip/) — Zulip REST API — messages, channels (streams), topics, users, user groups, subscriptions, reactions, drafts, scheduled messages, saved snippets, presence, real-time events, invitations, attachments, custom emoji, linkifiers, and server settings. Self-hosted or Zulip Cloud.
- [Alertmanager](https://www.dadl.ai/d/alertmanager/) — Prometheus Alertmanager API v2 -- alerts, silences, receivers, alert groups, status, and operational health
- [BookStack](https://www.dadl.ai/d/bookstack/) — BookStack wiki and documentation platform REST API -- shelves, books, chapters, pages (HTML/Markdown content), attachments, image gallery, comments, cross-entity search, exports (HTML/Markdown/PDF/ZIP), ZIP imports, users, roles, content permissions, recycle bin, audit log and tags. Hierarchy: shelves > books > chapters > pages.
- [ELITEDOMAINS](https://www.dadl.ai/d/elitedomains/) — ELITEDOMAINS domain registrar API -- register/transfer/manage .de and gTLD domains, redirector & inline DNS, AuthInfo/AuthInfo2 transfer codes, RGP catcher backorders, contact handles, and marketplace offers (Sedo, sale pages)
- [Graylog](https://www.dadl.ai/d/graylog/) — Graylog REST API -- log search (Views/Search + legacy universal), streams, pipelines, inputs, alerts, events, dashboards, users, roles, sidecars, index management, and cluster administration. Targets Graylog 6.x.
- [Hacker News](https://www.dadl.ai/d/hackernews/) — Hacker News API — read-only access to stories, comments, polls, jobs, users, and live feeds from news.ycombinator.com

---

**Canonical URL:** https://www.dadl.ai/d/zammad/
**Raw DADL:** https://github.com/DunkelCloud/dadl-registry/blob/main/zammad.dadl
