Every event is a final readonly class in RoundlyConsulting\Tickets\Events implementing ShouldDispatchAfterCommit: it fires only once the change committed, never for a rollback. $actor is who acted (null for the system). Every event about one ticket — 33 of the 34, all but GuestConverted — implements Contracts\TicketEvent:
interface TicketEvent
{
public function ticket(): Ticket; // TicketMerged: the source, the ticket merged away
public function actor(): ?Model; // null for the system, and always for TicketOverdue,
// TicketDueSoon (the sweep) and TicketMentioned
}Laravel’s dispatcher delivers an event to the listeners of the interfaces it implements, so one typed listener hears them all — an audit log, a webhook relay or a dashboard feed:
use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Tickets\Contracts\TicketEvent;
Event::listen(TicketEvent::class, function (TicketEvent $event): void {
AuditLog::create([
'ticket' => $event->ticket()->reference,
'event' => class_basename($event), // TicketReplied, TicketAssigned, …
'actor_type' => $event->actor()?->getMorphClass(),
'actor_id' => $event->actor()?->getKey(),
]);
});Before sending anything outward, mind the agent-only events: TicketNoteAdded, and a TicketMentioned or TicketCommentUpdated / TicketCommentDeleted about a note or a held reply, carry content no requester may see.
The events
| Event | Payload | Fires when |
|---|---|---|
TicketCreated | ticket, actor | A ticket was filed. |
TicketUpdated | ticket, changed, actor | Subject, description or the host metadata changed (changed holds the keys). |
TicketPriorityChanged | ticket, from, to, actor | The priority changed (not when set to the same value). |
TicketCategoryChanged | ticket, from, to, actor | categorize() moved the ticket — also for each ticket a category delete reassigns. |
TicketDueDateChanged | ticket, from, to, actor | Set, moved or cleared. |
TicketRequesterChanged | ticket, requester, actor | Also on a guest conversion. |
TicketDeleted / TicketRestored | ticket, actor | Soft delete / restore. |
TicketStatusChanged | ticket, transition, from, to, actor, system | Every state change but the initial one (rollbacks and adoptions included); from / to are lifecycle’s raw values. |
TicketResolved / TicketClosed | ticket, actor | Enters a Resolved / Closed state. |
TicketReopened | ticket, actor | Leaves Resolved or Closed for Open, Active or Waiting. |
TicketReplied | ticket, comment, role, actor | A published public reply (not a held one). |
TicketNoteAdded | ticket, comment, actor | An internal note. |
TicketCommentUpdated / TicketCommentDeleted | ticket, comment, actor | A message was edited / deleted — a reply, a held reply or a note: check the comment before showing it. |
TicketFirstResponded | ticket, comment, actor | The first public agent reply. |
TicketMentioned | ticket, comment, mentioned | A mention, only for someone who may read the comment. |
TicketAssigned | ticket, assignee, actor | Assigned or claimed (not for the same assignee again). |
TicketUnassigned | ticket, assignee, actor | The assignee was removed (assignee is the previous one). |
TicketWatcherAdded / TicketWatcherRemoved | ticket, watcher, actor | Explicit adds and removals (auto-watches are silent). |
TicketTagged / TicketUntagged | ticket, tags, actor | Tags actually added / removed (names) — TicketUntagged also for each live ticket a tag delete takes the tag off. |
TicketLinked / TicketUnlinked | ticket, related, type, actor | A link was added / removed (a duplicate add fires nothing; a merge’s duplicates link fires none). |
TicketMerged | source, target, result, actor | A merge committed; ticket() is the source. |
TicketAttachmentAdded / TicketAttachmentRemoved | ticket, media, actor | Ticket-level files (message files are comments’). |
TicketFieldsChanged | ticket, keys, actor | Custom field values set or forgotten — keys, never values. |
TicketOverdue / TicketDueSoon | ticket, dueAt | The sweep flagged it, once per due date. |
GuestConverted | guest, user, tickets, actor | A guest became a registered user (tickets = the count moved); not a TicketEvent. |
GuestAccessRevoked | ticket, version, actor | Every guest link voided — explicitly, on merge, or on close with revoke_on_close. |
- An internal note also reaches comments-for-laravel’s own events (CommentCreated, CommentMentioned), which fire for every comment — check meta.tickets.internal before telling anyone.
- Lifecycle’s LifecycleTransitioning / LifecycleTransitioned fire for tickets too; prefer the ticket events above.
- Tickets::fake() does not fake events — every call still runs — so Event::fake() works next to it.
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 cryptoBy donating, you agree to our donation terms.
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.