v2:
use ZammadAPIClient\Client;
$client = new Client([
'url' => 'https://zammad.example/api/v1',
'http_token' => 'your-token',
]);v3:
use ZammadAPIClient\Core\Auth;
use ZammadAPIClient\ZammadClient;
$client = ZammadClient::create(
'https://zammad.example',
Auth::token('your-token'),
);v2:
new Client(['url' => $url, 'http_token' => '...']); // Token
new Client(['url' => $url, 'oauth2_token' => '...']); // OAuth2
new Client(['url' => $url, 'username' => '...', 'password' => '...']); // Basicv3:
ZammadClient::create($url, Auth::token('...')); // Token
ZammadClient::create($url, Auth::oauth2('...')); // OAuth2
ZammadClient::create($url, Auth::basic('...', '...')); // Basicv2:
$ticket = $client->resource(ResourceType::TICKET)->get(1);
$title = $ticket->getValue('title');v3:
$ticket = $client->ticket()->find(1);
$title = $ticket->title;v2:
$title = $ticket->getValue('title');
$data = $ticket->getValues();v3:
$title = $ticket->title;
$data = $ticket->toArray();v2:
$ticket = $client->resource(ResourceType::TICKET);
$ticket->setValue('title', 'My ticket');
$ticket->setValue('group_id', 1);
$ticket->save();v3:
$ticket = $client->ticket()->create(new TicketDTO(
title: 'My ticket',
group_id: 1,
));v2:
$ticket = $client->resource(ResourceType::TICKET)->get(1);
$ticket->setValue('title', 'Updated');
$ticket->setValue('state_id', 3);
$ticket->save();v3:
// Via array
$client->ticket()->patch(1, ['title' => 'Updated']);
// Via TicketUpdateDTO
$client->ticket()->patch(1, new TicketUpdateDTO(
title: 'Updated',
));
// Via TicketDTO (same behavior — all non-null fields are sent)
$client->ticket()->patch(1, new TicketDTO(
title: 'Updated',
state_id: 3,
));v2:
$ticket = $client->resource(ResourceType::TICKET)->get(1);
$ticket->setValue('title', 'New');
$ticket->setValue('state_id', 3);
$ticket->save(); // Sends all values back to Zammadv3:
$resource = $client->ticket()->resource(1);
$resource->title = 'New';
$resource->state_id = 3;
$resource->save(); // Sends only {title, state_id}
// Delete
$resource->destroy();v2:
$ticket = $client->resource(ResourceType::TICKET)->get(1);
$ticket->delete();v3:
$client->ticket()->delete(1);v2:
$tickets = $client->resource(ResourceType::TICKET)->search('some text');
if (!is_array($tickets)) {
print $tickets->getError();
} else {
foreach ($tickets as $t) {
echo $t->getValue('title');
}
}v3:
try {
foreach ($client->ticket()->search('some text') as $ticket) {
echo $ticket->title;
}
} catch (NotFoundException $e) {
echo $e->getMessage();
}v2:
$tickets = $client->resource(ResourceType::TICKET)->all();v3:
foreach ($client->ticket()->all() as $ticket) {
echo $ticket->title;
}v2:
$page = $client->resource(ResourceType::TICKET)->all(1, 25);v3:
$list = $client->ticket()->list(['per_page' => 25]);
$list->page(1); // First page
$list->page(2); // Second page
$list->pageNext(); // Next page
$list->pagePrev(); // Previous page
$list->each(function ($t) { echo $t->title; });v2:
$ticket = $client->resource(ResourceType::TICKET)->get(999);
if ($ticket->hasError()) {
echo $ticket->getError();
}v3:
try {
$ticket = $client->ticket()->find(999);
} catch (NotFoundException $e) {
echo $e->getMessage(); // "Resource not found: tickets/999"
} catch (ValidationException $e) {
print_r($e->errors); // Per-field validation messages
} catch (AuthenticationException $e) {
// Invalid credentials
} catch (RateLimitException $e) {
echo $e->retryAfterSeconds; // Auto-retried, only thrown on exhaustion
} catch (ServerErrorException $e) {
// 5xx server error
} catch (NetworkException $e) {
// DNS, timeout, connection refused
}v2:
$ticket = $client->resource(ResourceType::TICKET)->get(1);
$articles = $ticket->getTicketArticles();v3:
foreach ($client->ticketArticle()->getForTicket(1) as $article) {
echo $article->body;
}v2:
$ticket_article = $client->resource(ResourceType::TICKET_ARTICLE);
$content = $ticket_article->getAttachmentContent(23);v3:
$content = $client->ticketArticle()->getAttachmentContent(
ticketId: 1,
articleId: 5,
attachmentId: 23,
);v2:
$client->resource(ResourceType::TAG)->add($ticketId, 'urgent', 'Ticket');
$client->resource(ResourceType::TAG)->remove($ticketId, 'urgent', 'Ticket');
$tag = $client->resource(ResourceType::TAG)->get($ticketId, 'Ticket');
$tags = $tag->getValue('tags');v3:
$client->tag()->add('Ticket', $ticketId, 'urgent');
$client->tag()->remove('Ticket', $ticketId, 'urgent');
foreach ($client->tag()->all(['object' => 'Ticket', 'o_id' => $ticketId]) as $tag) {
echo $tag->value;
}
$results = $client->tag()->tagSearch('urg');v2:
$csv = file_get_contents('users.csv');
$client->resource(ResourceType::USER)->import($csv);v3:
$csv = file_get_contents('users.csv');
$client->user()->import($csv);v2:
$client->setOnBehalfOfUser('myuser');
// ... API calls ...
$client->unsetOnBehalfOfUser();v3:
use ZammadAPIClient\Core\Transport\ImpersonationHandler;
$asUser = new ZammadClient(new ImpersonationHandler($client->getHandler(), 1));
$asUser->ticket()->find(42);
// $client itself keeps acting as the authenticated userBreaking: v2 toggled impersonation on the client itself, so the state was global and had to be reset. v3 scopes it to a separate client instance — there is nothing to unset and nothing can leak between calls. A user ID, login or email all work (
int|string).No credentials are passed to
ImpersonationHandler: it decorates$client's existing handler and inherits its authentication, adding only theFromheader.
v3 (recommended — typed accessors with IDE autocomplete):
$client->ticket()->find(1);
$client->user()->find(1);
$client->organization()->find(1);
$client->group()->find(1);
$client->role()->all();
$client->ticketArticle()->getForTicket(1);
$client->ticketState()->all();
$client->ticketPriority()->all();
$client->tag()->add('Ticket', 1, 'urgent');
$client->textModule()->find(1);
$client->link()->find(1);The underlying repo() method is @internal — prefer the typed accessors.