Download the PHP package zammad/zammad-api-client-php without Composer
On this page you can find all versions of the php package zammad/zammad-api-client-php. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package zammad-api-client-php
Zammad API Client for PHP (v3)
PSR-compliant PHP client for the Zammad REST API. PHP 8.1+.
We want your feedback! Report a bug · Start a discussion
Quick Start
1. Install via Composer (published on Packagist):
Using Laravel or Symfony? See Framework integration to
resolve ZammadClient from the container instead of constructing it by hand.
2. Connect — point the client at your Zammad instance and pass a personal access token:
3. Try it — fetch, create, update and search tickets:
That's it. The rest of this README covers authentication options,
the DTO
and error handling. Runnable recipes live in
examples/cookbook/.
How to use
This library offers three interaction styles. Choose based on your use case:
| Style | API | Best for |
|---|---|---|
| Repository + DTOs (recommended) | $client->ticket()->find(), create(), patch(), delete() |
Type-safe CRUD, IDE autocomplete, explicit intent. Use this by default. |
| Stateful Resource | $client->ticket()->resource($id)->save() / destroy() |
Interactive editing — mutate properties step by step, only changes are sent. |
| Raw HTTP | $client->getHandler()->get(), delete(), etc. |
Calling endpoints that have no dedicated repository. Escape hatch. |
Repositories are accessed via typed accessors: $client->ticket(), $client->user(), $client->organization(), $client->group(), $client->ticketArticle(), $client->ticketState(), $client->ticketPriority(), $client->tag(), $client->textModule(), $client->link(). The underlying repo() method is internal.
Connecting
Fetching
Accessing values
Creating
Updating
Stateful resource
$repo->resource($id) returns a Resource wrapper — not a DTO. Properties are accessed and mutated via __get/__set magic (not typed properties), and changes are automatically tracked. save() sends only modified fields; destroy() sends DELETE.
Use this for interactive workflows where you read, modify, then write. For single-field changes, prefer patch().
Choosing the right update method
patch() is the only update method. It accepts arrays, TicketUpdateDTO, or any DTO (via toArray()). Zammad uses HTTP PUT for all updates and merges the payload with the existing resource. Null values are excluded from all request bodies, so absent fields are never overwritten.
| Signature | What it sends | Use case |
|---|---|---|
patch($id, $array) |
Only the explicit array keys | Change one or two known fields. Safest. |
patch($id, $updateDto) |
Only the non-null DTO fields | IDE autocomplete on the mutable fields. |
patch($id, $dto) |
All non-null properties of the DTO (toArray()) |
Replace multiple fields using a full DTO. |
resource($id)->save() |
Only actually changed fields (tracked) | Interactive editing with change tracking. |
Deleting
All repositories expose a delete() method. Repositories implementing DeletableInterface perform the actual API call. Other repositories throw a BadMethodCallException — catchable, unlike a fatal error.
| Repository | delete() |
Notes |
|---|---|---|
TicketRepository |
✓ | |
UserRepository |
✓ | |
GroupRepository |
✓ | |
OrganizationRepository |
✓ | |
TextModuleRepository |
✓ | |
TagRepository |
exception | Throws BadMethodCallException; use add() / remove() |
LinkRepository |
exception | Throws BadMethodCallException; use add() / remove() |
TicketArticleRepository |
exception | Zammad API does not allow article deletion |
TicketStateRepository |
exception | System resource, read-only |
TicketPriorityRepository |
exception | System resource, read-only |
Searching
Listing all
Ticket articles
Tags
CSV import
All import() methods return an array — the Zammad API response containing import statistics (rows processed, skipped, errors).
CSV format follows Zammad's import specification (header row with field names matching API field names).
Examples
The primary example is the examples/cookbook/ directory — runnable recipes covering tickets, stateful resources, pagination, error handling, impersonation, and search. Run them against any Zammad instance:
The env vars are named
...UNIT_TESTS...for historical reasons. They are used by integration tests and the cookbook example. Unit tests (make test) need no env vars.
For user and organization CRUD examples, refer to the integration tests in docs/migration-v3-examples.md.
Authentication
| ConnectionConfig property | Type | Default | Description |
|---|---|---|---|
maxRetries |
int |
3 |
Number of retries on HTTP 429 before throwing RateLimitException |
verifySsl |
bool |
true |
Verify SSL certificate of the Zammad server |
timeout |
int |
30 |
Total request timeout in seconds |
connectTimeout |
int |
10 |
Connection timeout in seconds |
logger |
?LoggerInterface |
null |
PSR-3 Logger for HTTP request/retry logging |
Framework integration
Prefer to resolve ZammadClient from your framework's container instead of
constructing it by hand? Bridges are provided for Laravel and Symfony — configure
credentials once, then inject the same shared client everywhere.
Full runnable setups: examples/cookbook/07-laravel.php
and examples/cookbook/08-symfony.php.
Laravel
The service provider is auto-discovered (Laravel 5.5+). Publish the config and set
your credentials in .env:
Then inject the shared ZammadClient anywhere via the container:
For older Laravel versions, register ZammadAPIClient\Bridge\LaravelServiceProvider::class
in config/app.php manually.
Symfony
Register the bundle in config/bundles.php:
Configure credentials in config/packages/zammad.yaml:
Then type-hint ZammadClient in any service — autowiring injects the shared instance:
Error Handling
All errors are typed exceptions:
| Exception | HTTP | Auto-retry | Properties |
|---|---|---|---|
AuthenticationException |
401 | no | $e->getMessage() |
ForbiddenException |
403 | no | $e->getMessage() |
NotFoundException |
404 | no | $e->getMessage() |
ValidationException |
422 | no | $e->errors (array, per-field) |
RateLimitException |
429 | yes | $e->retryAfterSeconds |
ServerErrorException |
5xx | no | $e->getMessage() |
NetworkException |
— | no | DNS, timeout, connection refused |
Data Transfer Objects (DTOs)
Each repository returns typed DTOs. Below are the fields for each DTO. Fields marked yes have no default and are required in the constructor. Fields marked creation are nullable in the type signature but required by the Zammad API when creating a new resource — omitting them will result in a ValidationException (422).
The id field is available both as a property ($dto->id) and a convenience method ($dto->id()). Both return the same server-assigned ID; prefer the property for readability.
TicketDTO
| Field | Type | Required | Notes |
|---|---|---|---|
title |
string |
yes | Subject line of the ticket |
group_id |
?int |
— | Group responsible for the ticket |
priority_id |
?int |
— | References TicketPriority; resolve by name via TicketPriorityRepository |
state_id |
?int |
— | References TicketState; resolve by name via TicketStateRepository |
organization_id |
?int |
— | Derived from customer's organization |
customer_id |
?int |
creation | End-user who submitted the ticket (Zammad requires this on create) |
owner_id |
?int |
— | Agent assigned to the ticket |
number |
?string |
— | Human-readable ticket number (read-only) |
id |
?int |
— | Server-assigned (null before creation) |
pending_time |
?DateTimeImmutable |
— | ISO 8601 datetime for pending states |
article |
?array |
— | Optional initial article. Array shape: { subject: string, body: string, type: string, internal?: bool, content_type?: string, ... }. Use TicketArticleType enum constants (e.g. TicketArticleType::Note->value) for type-safe type values. |
created_at |
?DateTimeImmutable |
— | Server-assigned (read-only) |
updated_at |
?DateTimeImmutable |
— | Server-assigned (read-only) |
customFields |
array |
— | Zammad custom fields (string => mixed). Named camelCase per Zammad API convention. |
TicketUpdateDTO
Used with patch() for partial ticket updates. Only non-null fields are sent to the API.
| Field | Type | Notes |
|---|---|---|
title |
?string |
|
state_id |
?int |
|
priority_id |
?int |
|
group_id |
?int |
|
owner_id |
?int |
|
customer_id |
?int |
|
note |
?string |
Adds an internal note (article type 'note') on update |
pending_time |
?DateTimeImmutable |
ISO 8601 datetime for pending states |
UserDTO
| Field | Type | Required | Notes |
|---|---|---|---|
login |
?string |
— | Unique username |
email |
?string |
— | Primary email address |
firstname |
?string |
— | |
lastname |
?string |
— | |
phone |
?string |
— | |
organization_id |
?int |
— | Primary organization |
organization_ids |
?array |
— | Array of secondary organization IDs |
role_ids |
?array |
— | Array of role IDs (e.g. [2] for Agent) |
active |
?bool |
— | Whether the user account is active |
id |
?int |
— | Server-assigned |
created_at |
?DateTimeImmutable |
— | Read-only |
updated_at |
?DateTimeImmutable |
— | Read-only |
customFields |
array |
— |
OrganizationDTO
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string |
yes | Display name |
note |
?string |
— | |
active |
?bool |
— | |
id |
?int |
— | Server-assigned |
created_at |
?DateTimeImmutable |
— | Read-only |
updated_at |
?DateTimeImmutable |
— | Read-only |
customFields |
array |
— |
GroupDTO
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string |
yes | Display name |
note |
?string |
— | |
active |
?bool |
— | |
id |
?int |
— | Server-assigned |
created_at |
?DateTimeImmutable |
— | Read-only |
updated_at |
?DateTimeImmutable |
— | Read-only |
customFields |
array |
— |
TicketArticleDTO
| Field | Type | Notes |
|---|---|---|
ticket_id |
?int |
Parent ticket |
type |
?string |
Channel type: TicketArticleType::Note->value ('note'), Email ('email'), Phone ('phone'), Sms ('sms'), Web ('web') |
body |
?string |
Message content |
content_type |
?string |
MIME type: 'text/plain' or 'text/html' |
subject |
?string |
Subject line for email-type articles |
from |
?string |
Sender address/name |
to |
?string |
Recipient address |
cc |
?string |
CC address |
internal |
?bool |
Whether it's an internal note (hidden from customer) |
in_reply_to |
?string |
Message-ID for threading |
reply_to |
?string |
Reply-To address |
message_id |
?string |
Message-ID of this article |
origin_by_id |
?int |
User who created the article (for impersonation) |
sender |
?string |
Read-only: 'Customer', 'Agent', etc. |
type_id |
?int |
Read-only |
sender_id |
?int |
Read-only |
created_by_id |
?int |
Read-only |
updated_by_id |
?int |
Read-only |
created_by |
?string |
Read-only |
updated_by |
?string |
Read-only |
time_unit |
?float |
Time accounting (minutes) |
attachments |
?array |
Array of {filename, data (base64), mime-type?} |
id |
?int |
Server-assigned |
created_at |
?DateTimeImmutable |
Read-only |
updated_at |
?DateTimeImmutable |
Read-only |
TicketStateDTO
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string |
yes | Display label (e.g. 'open', 'closed') |
state_type_id |
?int |
— | Determines Zammad's automation behaviour |
note |
?string |
— | |
active |
?bool |
— | |
id |
?int |
— | Server-assigned |
created_at |
?DateTimeImmutable |
— | Read-only |
updated_at |
?DateTimeImmutable |
— | Read-only |
TicketPriorityDTO
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string |
yes | Display label (e.g. '2 normal', '3 high') |
note |
?string |
— | |
active |
?bool |
— | |
id |
?int |
— | Server-assigned |
created_at |
?DateTimeImmutable |
— | Read-only |
updated_at |
?DateTimeImmutable |
— | Read-only |
TextModuleDTO
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string |
yes | Display name |
keywords |
?string |
— | Space-separated search keywords |
content |
?string |
— | Template body with optional #{...} variables |
note |
?string |
— | |
active |
?bool |
— | |
id |
?int |
— | Server-assigned |
created_at |
?DateTimeImmutable |
— | Read-only |
updated_at |
?DateTimeImmutable |
— | Read-only |
TagDTO
| Field | Type | Notes |
|---|---|---|
id |
?int |
Tag-assignment ID |
object |
?string |
Object class name (e.g. 'Ticket') |
o_id |
?int |
Numeric ID of the tagged object |
value |
?string |
Tag string (e.g. 'urgent', 'bug') |
LinkDTO
| Field | Type | Notes |
|---|---|---|
id |
?int |
Server-assigned |
link_type_id |
?int |
|
link_type |
?string |
'normal', 'parent', or 'child' |
link_object_source |
?string |
Source object type |
link_object_source_value |
?int |
Source object ID |
link_object_target |
?string |
Target object type |
link_object_target_value |
?int |
Target object ID |
created_at |
?DateTimeImmutable |
Read-only |
updated_at |
?DateTimeImmutable |
Read-only |
Impersonation
Development
Unit tests
No environment variables needed. Unit tests mock the HTTP layer and run fully isolated.
Integration tests
These require a running Zammad instance and authentication credentials:
| Variable | Required | Default | Description |
|---|---|---|---|
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL |
Yes | http://localhost:3000 |
Zammad server URL (without /api/v1) |
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_TOKEN |
No | — | Token authentication (preferred) |
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_USERNAME |
No* | — | Username for basic auth |
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_PASSWORD |
No* | — | Password for basic auth |
* Either ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_TOKEN or USERNAME+PASSWORD must be set.
Migration from v2
See docs/migration-v3-examples.md for side-by-side code examples. v2 reference documentation is preserved in docs/v2-reference.md.
| v2 | v3 |
|---|---|
new Client(['url' => ..., 'http_token' => ...]) |
ZammadClient::withToken($url, ...) |
$client->resource(TICKET)->get(1) |
$client->ticket()->find(1) |
$ticket->getValue('title') |
$ticket->title |
$ticket->getValues() |
$ticket->toArray() |
$ticket->setValue('title', 'x'); $ticket->save() |
$client->ticket()->patch(1, ['title' => 'x']) |
if ($ticket->hasError()) { $ticket->getError(); } |
catch (NotFoundException $e) { $e->getMessage(); } |
$client->resource(TICKET)->search('term') |
$client->ticket()->search('term') |
$client->resource(TICKET)->all() |
$client->ticket()->all() |
$ticket->delete() |
$client->ticket()->delete($id) |
$client->resource(TAG)->add($ticketId, 'tag', 'Ticket') |
$client->tag()->add('Ticket', $ticketId, 'tag') (order changed) |
License
AGPL-3.0 or MIT.
All versions of zammad-api-client-php with dependencies
psr/http-client Version ^1.0
psr/http-factory Version ^1.0
guzzlehttp/guzzle Version ^7.10
guzzlehttp/psr7 Version ^2.7
psr/log Version ^3.0