NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

Every state change dispatches a plain event in RoundlyConsulting\Teams\Events, so you can send notifications, write audit logs or sync other systems without forking:

EventPropertiesFired when
TeamMemberAdded$memberA new membership is created, or a removed or lapsed one is revived by a re-add ($member->wasRecentlyCreated tells them apart).
TeamMemberRoleChanged$member, $previousRoleA member’s role changes.
TeamMemberDeleted$memberA membership is removed (soft-deleted).
InviteCreated$inviteAn invite is created.
InviteAccepted$invite, $memberAn invite is accepted.
InviteResent$inviteAn invite’s code and expiry are rotated.
InviteRevoked$inviteAn invite is revoked.
TeamOwnershipTransferred$team, $previousOwner, $newOwnerOwnership is transferred.
JoinRequestCreated$joinRequestA user requests to join.
JoinRequestApproved$joinRequestA request is approved (also on auto-approval).
JoinRequestDenied$joinRequestA request is denied.
JoinRequestExpired$requestA pending request auto-declines on expiry.
MembershipExpiringSoon$memberA membership lapses within the report window (--notify).
MembershipExpired$memberAn expired membership is pruned — by teams:members:prune, Teams::members()->prune() or model:prune.
TeamContextSet$teams, $multiple, $restored; team()The current team context changed — setCurrent() ($teams empty when cleared), switchTo(), setCurrentMany(), the teams.context middleware, runAs() entry and restore, or a queued job restoring its dispatcher’s context ($restored = true; not when its membership re-check fails).
TeamMoved$team, $parent, $previousParentKeyA team was placed under a parent or detached to a root.

Note the property name: JoinRequestExpired carries $request, the other join-request events carry $joinRequest. With the permissions bridge on, the package itself listens to TeamMemberDeleted, MembershipExpired and TeamMemberAdded (revived rows only) to forget the member’s grants in that team.

use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Teams\Events\InviteAccepted;
use RoundlyConsulting\Teams\Events\TeamMemberRoleChanged;

Event::listen(function (InviteAccepted $event): void {
    logger()->info('Invite accepted', [
        'invite' => $event->invite->id,
        'member' => $event->member->id,
    ]);
});

Event::listen(function (TeamMemberRoleChanged $event): void {
    // $event->member->role is the new role; $event->previousRole the old one
});

Notification bridge

Publish teams-stubs to get App\Listeners\TeamEventSubscriber — a queued subscriber with handlers for TeamMemberAdded, TeamMemberRoleChanged and InviteCreated. Drop your own Notification classes into its handlers; it runs on notifications.queue_connection, falling back to queue.default. Register it:

// app/Providers/AppServiceProvider.php → boot()
use App\Listeners\TeamEventSubscriber;
use Illuminate\Support\Facades\Event;

Event::subscribe(TeamEventSubscriber::class);

Extend its subscribe() map to react to any other event. The package ships the wiring; your app owns the content.

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

By 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.