---
title: "Status lifecycle — Appointments for Laravel | Roundly"
description: "The guarded status state machine — transitions, helper methods, labels, colours and the InvalidStatusTransitionException."
url: https://roundly-consulting.com/open-source/docs/appointments-for-laravel/status-lifecycle
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)

# Status lifecycle

Status is a guarded state machine. Legal transitions persist and fire AppointmentStatusChanged; illegal ones throw InvalidStatusTransitionException; moving to the current status is a silent no-op:

```php
use RoundlyConsulting\Appointments\Enums\Status;
use RoundlyConsulting\Appointments\Facades\Appointments;

Appointments::for($appointment)->confirm();      // pending → confirmed
Appointments::for($appointment)->cancel();       // pending/confirmed → cancelled
Appointments::for($appointment)->complete();     // confirmed → completed
Appointments::for($appointment)->decline();      // pending → declined
Appointments::for($appointment)->markNoShow();   // confirmed → no_show
Appointments::for($appointment)->transition(Status::Confirmed);   // the generic form

// the same shortcuts on the model — they go through the facade's manager too
$appointment->confirm();
$appointment->transitionTo(Status::Confirmed);
```

## Allowed transitions

| Status | Value | Colour | Can move to |
| --- | --- | --- | --- |
| `Status::Pending` | pending | amber | confirmed, declined, cancelled |
| `Status::Confirmed` | confirmed | blue | completed, cancelled, no\_show |
| `Status::Cancelled` | cancelled | gray | — (final) |
| `Status::Completed` | completed | green | — (final) |
| `Status::Declined` | declined | red | — (final) |
| `Status::NoShow` | no\_show | orange | — (final) |

The handle’s shortcuts and the model’s confirm(), cancel(), complete(), decline(), markNoShow() and transitionTo() all end in Appointments::for($appointment)->transition() on the manager, which runs TransitionAppointmentAction — so Appointments::fake() records them wherever they are called.

## Inspecting the machine

Ask the enum what’s possible — handy for showing only the buttons that make sense:

```php
use RoundlyConsulting\Appointments\Enums\Status;

$appointment->status;                                    // Status enum (cast)
$appointment->status->transitions();                     // e.g. [Status::Completed, Status::Cancelled, Status::NoShow]
$appointment->status->canTransitionTo(Status::Pending);  // false
$appointment->status->isFinal();                         // true for cancelled, completed, declined, no_show
Status::default();                                       // Status::Pending
```

## Handling illegal moves

```php
use RoundlyConsulting\Appointments\Exceptions\InvalidStatusTransitionException;

try {
    $appointment->complete();       // still pending
} catch (InvalidStatusTransitionException $e) {
    $e->from;                       // Status::Pending
    $e->to;                         // Status::Completed
    $e->getMessage();               // Cannot transition appointment status from "pending" to "completed".
}
```

## Labels, colours and options

Status, ParticipantRole and Frequency carry the enums-for-laravel Helpers trait, so they come ready for UI and validation. color() is specific to Status:

```php
use RoundlyConsulting\Appointments\Enums\Status;

$appointment->status->label();   // "Confirmed" (from the enums Helpers trait)
$appointment->status->color();   // a colour token for UI badges, e.g. "blue"
Status::values();                // Collection: 'pending', 'confirmed', …
Status::labels();                // Collection: "Pending", "Confirmed", …
Status::options();               // value/label/name option DTOs for a <select>
Status::validationRule();        // "in:pending,confirmed,cancelled,completed,declined,no_show"
```

label() passes the headlined value through Laravel’s \_\_() — Pending, Confirmed, No Show — so you translate labels with a JSON translation file such as lang/sk.json:

```json
{
    "Pending": "Čaká na potvrdenie",
    "Confirmed": "Potvrdený",
    "No Show": "Neprítomnosť"
}
```

## 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)
