Metadata-Version: 2.4
Name: zammad-python-client
Version: 0.1.1
Summary: A typed Python client for the Zammad REST API
Author-email: Stefan Schulte-Ortbeck <info@codefighters.de>
License: MIT
Project-URL: Repository, https://gitlab.codefighters.de/python/zammad-python-client
Project-URL: Changelog, https://gitlab.codefighters.de/python/zammad-python-client/-/blob/main/CHANGELOG.md
Keywords: zammad,helpdesk,api,client
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.28.0
Provides-Extra: dotenv
Requires-Dist: python-dotenv>=1.0.0; extra == "dotenv"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: responses>=0.23; extra == "dev"
Requires-Dist: mypy>=1.8; extra == "dev"
Requires-Dist: types-requests; extra == "dev"
Dynamic: license-file

# zammad-python-client

A typed Python client for the [Zammad](https://zammad.org) REST API. `requests`
underneath, `py.typed`, no other dependencies. Built for Zammad 7.x and used by
[`python/zammad-mcp`](https://gitlab.codefighters.de/python/zammad-mcp).

Two layers:

- **Curated**: `client.tickets`, `client.ticket_articles`, `client.tags`,
  `client.time_accountings`, `client.users`, `client.organizations`,
  `client.groups`, `client.ticket_states`, `client.ticket_priorities`,
  `client.knowledge_base`, plus plain CRUD collections (`client.text_modules`,
  `client.macros`, `client.slas`, ...). One call per question, sensible defaults.
- **Complete**: `client.api.<controller>.<action>()`, one method per route of
  Zammad's own routing — 612 routes, 118 controllers, generated from
  `config/routes/*.rb` of the pinned Zammad version. See
  [`docs/endpoints.md`](docs/endpoints.md). Nothing the web UI can do is out of
  reach, including the knowledge base, two-factor, channels and integrations.

## Install

```bash
pip install zammad-python-client
```

Published to PyPI on every tag; the same wheel also lands in this project's GitLab
package registry for internal CI. From a checkout: `pip install -e ".[dev]"`.

## Use

```python
from zammad import ZammadClient

c = ZammadClient("https://zammad.example.com", token="...")   # Authorization: Token token=...

c.version()                                        # "7.2.2"
c.users.current()                                  # the token's user
open_ = c.tickets.search_tickets("state.name:open owner.login:me", limit=20)
t = c.tickets.get(42, expand=True)                 # names next to ids
c.ticket_articles.by_ticket(42)
c.ticket_articles.add(42, "<p>Looked at it.</p>")  # internal note (default)
c.ticket_articles.add(42, "<p>Fixed.</p>", article_type="email", internal=False, to="customer@example.com")
c.time_accountings.book(42, 15)                    # unit is instance config
c.tags.add_to(42, "billing")

kb = c.knowledge_base.init()                       # bases, locales, categories, answers as assets
c.knowledge_base.search("vpn", flavor="agent")
a = c.knowledge_base.create_answer(1, category_id=3, title="VPN", body_html="<p>…</p>", kb_locale_id=1)
c.knowledge_base.set_internal(1, a["id"])         # draft -> internal (agents only)

# Everything else: the generated layer, named after Zammad's controllers and actions.
c.api.settings.index_by_area("Ticket::Base")
c.api.user_two_factors.personal_configuration()
c.api.channels_email.index()
```

Acting as another user (needs `admin.user`): every method takes
`on_behalf_of="login-or-email-or-id"`, sent as Zammad's `From` header.

Pagination: `list(page=, per_page=)` is one page, `iter_all()` / `list_all()`
walk pages (Zammad returns bare arrays without a total, so a short page ends the
walk). `expand=True` adds related names next to the `*_id` fields.

## Errors

`ZammadError` covers everything; `ZammadAuthError` (401), `ZammadPermissionError`
(403, the token lacks a permission), `ZammadNotFoundError` (404, which Zammad also
uses for "not visible to you") and `ZammadRateLimitError` (429 that could not be
waited out; `retry_after` when known) narrow it.

## Keeping up with Zammad

```bash
scripts/extract-routes.py --ref stable      # or --zammad /path/to/checkout
scripts/generate-resources.py
pytest
```

`docs/routes.json` records the Zammad version it came from. Rails `resources`
blocks (knowledge base, two-factor, checklists, …) are expanded by hand in
`scripts/extract-routes.py`; the script says which route files contain such
blocks so a new one is not missed. CI fails when `generated.py` and
`docs/routes.json` disagree.

## Development

```bash
uv venv && uv pip install -e ".[dev]"
pytest -q            # mocks only, never the network
mypy
scripts/verify-live.py   # read-only, needs ZAMMAD_URL + ZAMMAD_TOKEN in .env
```

Releases: bump `__version__` in `src/zammad/__init__.py`, update the changelog,
tag `vX.Y.Z`; CI publishes to PyPI (needs the `PYPI_TOKEN` CI variable) and to the
project's GitLab registry.
