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

Each transition dispatches an event carrying the relevant model. All events live in RoundlyConsulting\Approvals\Events and expose their payload as public properties:

EventPayloadFired when
ApprovalRequestedapprovala pending decision is asked for (ask())
ApprovalApprovedapprovala decision is approved
ApprovalRejectedapprovala decision is rejected
ApprovalCancelledapprovala decision is withdrawn (cancel())
ApprovalExpiredapprovala pending or approved decision lapses
ApprovalRequestResolvedrequesta request reaches approved, rejected or expired
ApprovalToggledactor, entity, hasBeenApprovedtoggle() / toggleApproval() runs
ApprovalStageOpenedstagea staged request opens a stage
ApprovalStageClearedstagea stage clears as approved
ApprovalDelegateddelegationan approver delegates authority
ApprovalDelegationRevokeddelegationa delegation is revoked
ApprovalStatusChangedsubject, from, to, actorumbrella — every status transition of a decision or request, alongside the granular event

The umbrella event

Subscribe to ApprovalStatusChanged once to observe all transitions: a decision approved, rejected, withdrawn — including by a toggle or by a change of mind that supersedes it — or expired, and a request resolved or expired. It carries the subject (the Approval or ApprovalRequest that changed), the from and to ApprovalStatus, and the actor when one is known — null for a resolved request:

use RoundlyConsulting\Approvals\Events\ApprovalStatusChanged;

class AuditApprovalChanges
{
    public function handle(ApprovalStatusChanged $event): void
    {
        // $event->from, $event->to, $event->subject, $event->actor
        logger()->info("approval moved {$event->from->value} -> {$event->to->value}");
    }
}

Granular listeners

use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Approvals\Events\ApprovalApproved;
use RoundlyConsulting\Approvals\Events\ApprovalRequestResolved;

Event::listen(function (ApprovalApproved $event): void {
    $approval = $event->approval;   // the Approval model
});

Event::listen(function (ApprovalRequestResolved $event): void {
    $request = $event->request;     // approved, rejected or expired — see $request->status
    $subject = $request->subject;   // e.g. the Release
});

An ask() creates a pending decision rather than changing one, so it fires only ApprovalRequested. Stage changes and delegation also fire only their own events — not the umbrella one.

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.