Migrating from Zammad
Organisation admins can import a Zammad helpdesk from Organisation Settings → Migrate → Zammad. Each selected Zammad group becomes one restricted TOW project with a default board and an enabled customer portal form.
This is a REST-based, one-time import. It is not a Zammad backup and it does not continuously synchronize later source changes.
Before you connect
Use either a Zammad API token or a username and password. The credential must be able to read the required REST APIs and must have explicit read or full ticket access for every group you want to select. A group without either permission remains visible during discovery but cannot be selected.
Discovery reads the current user, groups, ticket states and priorities, roles, the complete users and customer-organisations collections, Object Manager attributes, time-accounting types, tickets, and their articles. /api/v1/object_manager_attributes is a required discovery read even when custom-field import is disabled. A normal agent-scoped credential may therefore be insufficient; verify these collection-level reads before starting.
The migration supports normal TLS verification and an optional PEM-encoded custom certificate authority. Disabling TLS verification should be limited to controlled testing.
TOW sends API tokens using Zammad's documented Authorization: Token token=… header and follows the documented page and per_page pagination model. See the Zammad REST API introduction.
Ticket enumeration uses Zammad's SQL-backed /api/v1/tickets collection and filters selected groups in TOW. Elasticsearch is not required, and the importer does not depend on the search index or its result window.
Wizard
The migration wizard requires each decision in this order:
- Connect: enter the base URL, authentication mode, credential, and TLS settings.
- Select groups: choose every accessible group to import and disable any optional content categories you do not need.
- Review statuses: confirm a TOW workflow category for every ticket state referenced by the selected groups. Common state names are prefilled, but the complete mapping must be confirmed.
- Convert time: when time accounting is enabled, enter the positive number of minutes represented by one Zammad unit.
- Match agents: review active Zammad agents against existing organisation members, create pending accounts where appropriate, or omit a source agent.
- Review and start: inspect the final selection and acknowledge the one-pass behavior.
- Progress: monitor the worker, review warnings, and open the resulting projects.
Zammad time values are unitless. TOW does not infer whether a value means minutes or hours. It multiplies each entry by the confirmed minutes-per-unit value, rounds to the nearest second, and retains the original decimal and activity type as source metadata. See Zammad time accounting.
Imported data
The core import always includes tickets, customers, the full article conversation, and portal submissions. Optional switches control attachments, time entries, tags, links, checklists, and compatible ticket custom fields.
The importer also preserves:
- Group names, hierarchy, notes, and source IDs.
- One TOW workflow status for every referenced Zammad ticket state.
- Source ticket numbers when they are safe to reuse, otherwise a TOW number plus a Zammad-number alias.
- Active mapped owners as assignees and active mapped agents with current group access as project members.
- Customer organisations as visible task fields and source metadata, without creating TOW organisations.
- Public articles as portal-visible content and internal articles as staff-only comments.
- Safe links and sanitized HTML converted to Markdown or plain text.
- Public attachments on both the task and submission; internal attachments remain staff-only. Inline files referenced by an article's HTML are imported even when Zammad omits them from the article's
attachmentslist. - Priorities, labels, worklogs, normal relationships, unambiguous parent/child relationships, supported checklists, and compatible Object Manager fields.
Knowledge bases, SLA definitions, triggers, macros, templates, calendars, shared drafts, avatars, raw EML, and complete audit history are outside this importer.
Security and failure behavior
Credentials are encrypted in the migration run and removed when the run reaches a terminal state. TOW does not forward credentials across origins during redirects, bounds retries and payload sizes, checks cancellation during long source operations, and redacts token values from migration errors and metadata.
An authorization failure for an enabled capability fails the run instead of silently producing a partial helpdesk. A missing checklist API is reported as an unsupported Zammad version and the checklist option is disabled. Individual missing or deleted records and files are recorded as item-level warnings where the remainder of a ticket can still be imported safely.
Historical content is written without sending portal invitations, content notifications, automation events, or watcher subscriptions. Only the migration completion or failure notification is emitted.
Zammad remains live while the import runs. Concurrent source changes can be missed, and TOW does not perform a reconciliation or drift-verification pass afterwards. Avoid source changes during the migration window.
Rerunning
A rerun against the same Zammad instance reuses source-object mappings, appends missing comments and relationships, and repairs failed or previously undiscovered inline attachments. It preserves local project, task, workflow, portal-form, comment, membership, and grant edits.
Completed runs no longer retain source credentials. To repair an existing installation, start a new Zammad migration in the same TOW organisation, connect to the same Zammad instance, and select the same source groups and attachment category. The new run reuses the existing mappings instead of duplicating tickets.
The completed run reports inline-attachment repair counters in its progress details: body references found, references missing from Zammad's attachment list, files downloaded, files reused, failed files, and unresolved references. Confirm that unresolved references is zero after the repair run, then spot-check affected ticket descriptions and public portal views.
If a mapped TOW target was deliberately deleted, the rerun does not recreate it. Review item warnings after every rerun.
Server setting
migrations.zammad_page_size controls the Zammad REST page size and defaults to 100. TOW accepts values from 1 through 100. Server admins can edit Zammad migration page size on the Jobs tab in Server Settings, or set ZAMMAD_MIGRATION_PAGE_SIZE. Restart the backend and migration worker after changing YAML or environment configuration.
Local simulator
Automated tests use the repository's isolated Zammad simulator. It is never included in production or development Compose.
From the repository root:
./scripts/start_zammad_server.sh
./scripts/start_zammad_server.sh medium
./scripts/start_zammad_server.sh large
The default smoke profile listens on port 18084 and prints the fixed test-only token, Basic credential, and Docker-gateway-aware URL. Medium and large profiles lazily generate 500 and 10,000 deterministic tickets.
Manual real-instance acceptance fixture
The manual fixture CLI is only for a disposable or staging Zammad instance. It creates a uniquely named, manifest-backed fixture in an explicitly selected existing test group.
Zammad triggers, notifications, and webhooks can run while this tool seeds or cleans data. Use an automation-excluded group or a disposable instance. Never run it against an uncontrolled production helpdesk.
Put the PAT in the process environment or ignored .env.extra file:
export ZAMMAD_PAT='your-disposable-instance-token'
uv run --project backend --group dev python scripts/zammad_fixture.py \
https://zammad-staging.example \
seed \
--group-id 42
Optional --custom-field-name must name an existing active ticket text field. Optional --checklist-template-id must identify an existing template. The tool reuses them and never creates a role, runs Object Manager migrations, or restarts Zammad.
Seed prints a manifest path. After UI acceptance and an idempotency rerun, clean it:
uv run --project backend --group dev python scripts/zammad_fixture.py \
https://zammad-staging.example \
cleanup \
--manifest .tow/zammad-fixtures/20260726T120000Z-example.json
Cleanup runs in dependency order, treats missing resources as already clean, continues after individual failures, and deactivates irreducible residue. Acceptance is complete only when it reports zero remaining disposable resources. Manifests contain resource IDs only; credentials and source content are never stored in them.