---
title: "Assignment and watchers — Tickets for Laravel | Roundly"
description: "One assignee per ticket — a user or a team — with claim(), eligibility and default assignees, plus watchers, auto-watch and requester hand-overs."
url: https://roundly-consulting.com/open-source/docs/tickets-for-laravel/assignment-watchers
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Tickets for Laravel](https://roundly-consulting.com/open-source/docs/tickets-for-laravel.md)

# Assignment and watchers

One assignee per ticket: a morph to any model implementing Contracts\\Assignee.

```php
Tickets::for($ticket)->by($lead)->assign($agent);    // TicketAssigned
Tickets::for($ticket)->by($agent)->claim();          // the actor takes it
Tickets::for($ticket)->by($lead)->unassign();        // TicketUnassigned
$ticket->assignTo($agent, $lead);                    // model convenience, through the manager
```

- The assignee must be eligible (isEligibleTicketAssignee()), else InvalidAssigneeException. With Concerns\\IsTicketAssignee, an Authenticatable is eligible when it is an agent for this ticket — it passes the agent ability and the AgentScope lets it work on the ticket — so nobody is assigned a ticket they could not open. Any other model always is.
- The same assignee again is a no-op: no activity, no event.
- Assigning a ticket in the initial state runs the Assigned automation (new → open), and the assignee starts watching (tickets.watchers.auto.assignee).
- claim() needs an actor who is an eligible assignee. In the API, assigning someone else needs the assign ability; any agent may claim; the holder may unassign themselves.
- At creation without an assignee, Contracts\\AssignmentStrategy::assigneeFor($ticket) is asked — by default the nearest category’s default assignee.

## A team as assignee

Its members are the ones notified:

```php
use RoundlyConsulting\Tickets\Concerns\IsTicketAssignee;
use RoundlyConsulting\Tickets\Contracts\Assignee;
use RoundlyConsulting\Tickets\Models\Ticket;

final class Team extends Model implements Assignee
{
    use IsTicketAssignee;

    public function ticketNotifiables(): iterable
    {
        return $this->members()->where('active', true)->get();
    }

    public function isEligibleTicketAssignee(Ticket $ticket): bool
    {
        return true;
    }
}
```

Add the class to tickets.assignables (\['team' => Team::class\]) for the API. teams-for-laravel works as the team model.

## Your own assignment rule

Bind a Contracts\\AssignmentStrategy (assigneeFor(Ticket): ?Model) through tickets.assignment.strategy or the container — round-robin, load-based, whatever your desk needs. It runs on the stored ticket, with its project, category, priority and channel already set.

## Watchers

Followers of a ticket — users, any model, or a TicketGuest (a CC’d guest):

```php
Tickets::for($ticket)->by($agent)->watchers()->add($colleague)->remove($former);
Tickets::for($ticket)->by($user)->watchers()->watch();      // the actor; ->unwatch()
Tickets::for($ticket)->watchers()->has($colleague);         // bool
Tickets::for($ticket)->watchers()->all();                   // TicketWatcher rows, watcher loaded
```

- Unique per (ticket, watcher); adding twice is a no-op. watch() without an actor throws InvalidWatcherException.
- Auto-watch (tickets.watchers.auto.\*): the requester on creation, the assignee on assignment, agents who write, and (off by default) a mentioned reader. Auto-watches are silent — no activity, no event. TicketWatcher::$auto tells them apart; add() on someone already auto-watching makes the watch a chosen one.
- A requester hand-over (changeRequester()) removes the previous requester’s automatic watch, so they no longer view the ticket or receive its notifications; a watch an agent added on purpose stays. A previous guest requester’s links are revoked too.
- A non-agent watcher may view the ticket and its public thread and reply as a participant (tickets.watchers.can\_view). Watcher lists are agent-only in the API.
- TicketWatcherAdded / TicketWatcherRemoved fire for explicit changes; their activity entries are internal.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/tickets-for-laravel.md)
