---
title: "Events & exceptions — Appointments for Laravel | Roundly"
description: "The seven appointment and participant events, their payloads and when they fire, plus every exception the package throws."
url: https://roundly-consulting.com/open-source/docs/appointments-for-laravel/events
language: en
---

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

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

# Events & exceptions

The package fires an event for every change, so confirmations, calendar sync and dashboards stay out of your scheduling code. All live in RoundlyConsulting\\Appointments\\Events:

| Event | Payload | Dispatched when |
| --- | --- | --- |
| `AppointmentCreated` | $appointment | An appointment row is inserted. |
| `AppointmentUpdated` | $appointment | An appointment is saved with changes — including reschedules and status transitions. |
| `AppointmentRescheduled` | $appointment, $previousStartsAt | reschedule() moves an appointment. |
| `AppointmentStatusChanged` | $appointment, $from, $to | A status transition happens — directly or via the approval sync. |
| `ParticipantCreated` | $participant | A participant is added for the first time. |
| `ParticipantUpdated` | $participant | A participant row is saved with changes — or a removed participant is added again and its row restored. |
| `ParticipantDeleted` | $participant | A participant is removed (soft delete) — one event per row. |

```php
use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Appointments\Enums\Status;
use RoundlyConsulting\Appointments\Events\AppointmentStatusChanged;

Event::listen(function (AppointmentStatusChanged $event): void {
    if ($event->to === Status::Confirmed) {
        foreach ($event->appointment->participants as $participant) {
            // notify $participant->participant that the booking is confirmed
        }
    }
});
```

## Timing notes

- Every package event implements ShouldDispatchAfterCommit: inside a database transaction it fires once that commits, and never for a write that rolls back.
- A booking is written in one transaction, so AppointmentCreated fires after its participants, contacts and approval request are stored — and a refused or failed booking fires nothing.
- A recurring series is one transaction too: its events fire once the whole series commits, and a series that rolls back fires none.
- reschedule() and status transitions save the model, so AppointmentUpdated fires alongside AppointmentRescheduled or AppointmentStatusChanged.
- A status set at creation with withStatus() doesn’t fire AppointmentStatusChanged, and neither does a transition to the current status.

## Exceptions

| Exception | Thrown when | Carries |
| --- | --- | --- |
| `InvalidStatusTransitionException` | A status transition the state machine doesn’t allow. | $from, $to |
| `SchedulingConflictException` | Creating, rescheduling or adding a participant overlaps a participant’s booking while conflict prevention is on. | $conflicts — Collection of Appointment |
| `DuplicateParticipantException` | The same model is listed twice on a new appointment, or participants()->add() gets a model that already takes part. | — |
| `ParticipantNotFoundException` | participants()->remove() gets a model that doesn’t take part, or a participant row of another appointment. | — |
| `InvalidScheduleException` | A duration under one minute, or an end that doesn’t come after the start — from the builder, an AppointmentData or reschedule(). Thrown before anything is written. | — |
| `InvalidRecurrenceException` | A RecurrenceData with an interval or count below one, or a weekday outside 1–7. | — |
| `CannotReviewAppointmentException` | review() for an unverified author while reviews.require\_verified\_attendance is on. | — |
| `AppointmentsException` | Abstract base class of all the exceptions above. | — |

All live in RoundlyConsulting\\Appointments\\Exceptions and extend AppointmentsException, so one catch covers the package.

## 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/appointments-for-laravel.md)
