---
title: "Notifications and reminders — Events for Laravel | Roundly"
description: "Nine queued, after-commit notices in English and Slovak — recipients and delivery checks, payloads, swapping a notice, silent writes and reminders."
url: https://roundly-consulting.com/open-source/docs/events-for-laravel/notifications
language: en
---

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

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

# Notifications and reminders

Every notice extends Notifications\\EventNotification: ShouldQueue, sent only after the commit, on events.notifications.queue, through events.notifications.channels (mail, and database if you like), in the recipient’s locale with times in the event’s own timezone and its abbreviation. None carries a class name or another attendee’s data.

| Type | Class | Sent when | To |
| --- | --- | --- | --- |
| `invited` | `EventInvitationNotification` | An invitation is sent — or, sent on a draft, once it is published (with a fresh token). | The invitee (accept link; encrypted on the queue). |
| `confirmed` | `RsvpConfirmedNotification` | An RSVP enters going. | The attendee — the check-in code as text and the ticket link, never a QR image in mail. |
| `waitlist_promoted` | `WaitlistPromotedNotification` | A waitlisted party gets seats. | The attendee — at once for a free seat; for a paid one from the payment job, with the pay link. |
| `reminder` | `EventReminderNotification` | events:sweep, at each reminder offset. | going |
| `updated` | `EventUpdatedNotification` | A change of the start, end, all-day flag, timezone, venue, join link or attendance mode on an announced event. | going, held |
| `postponed · rescheduled · cancelled` | EventPostponed… · EventRescheduled… · EventCancelledNotification | Postponed · rescheduled · cancelled (including the holds the cancellation released). | going, held, waitlisted, interested |
| `moved_online` | `EventMovedOnlineNotification` | An announced event moved online. | going, held |

## Recipients and delivery

- Recipients come from the audience by RSVP status (events.notifications.audiences.<type>), each once, without the person who made the change (unless notify\_actor), and only those who may see the event and can be notified — a model without notification routes is skipped, and events:doctor counts them.
- At delivery each notice checks again (shouldSend()): the event still exists and is visible to the recipient, and their RSVP still puts them in the audience — a reminder queued for someone who declined a minute later is dropped.
- A failing notice is reported, never thrown back into the write that caused it, and a rolled-back write sends nothing. The database channel never stores a check-in code or an invitation token.
- Mail has a subject with the event’s title, a greeting, the body lines and one button — the accept link for invited (events.urls.invitation), the ticket link for confirmed (events.urls.rsvp), the pay link for a held waitlist\_promoted, else the event page (events.urls.event); no button without a URL. All-day events show the date only.

## The database channel

toArray() — what database stores — is plain data, with no class names:

```json
{
  "type": "rescheduled",
  "event": { "id": "uuid", "title": "Jazz Night", "starts_at": "2026-12-05T20:00:00+01:00", "timezone": "Europe/Bratislava", "all_day": false },
  "previous_starts_at": "2026-11-14T19:00:00+00:00",
  "reason": "The band is ill"
}
```

| Type | Extra keys |
| --- | --- |
| `invited` | `message` |
| `confirmed` | `rsvp {id, status, guests}` |
| `waitlist_promoted` | `rsvp {id, status}` |
| `reminder` | `offset` |
| `updated` | changed (attribute names) |
| `postponed, cancelled` | `reason` |
| `rescheduled` | `previous_starts_at, reason` |
| `moved_online` | — |

## Changing a notice

```php
// config/events.php
'notifications' => [
    'channels' => ['mail', 'database'],
    'types' => [
        'cancelled' => App\Notifications\EventCancelledWithRefund::class,   // must extend the package's class
        'updated' => false,                                                  // off
    ],
    'audiences' => ['postponed' => ['going', 'held']],
],
```

```php
use Illuminate\Notifications\Messages\MailMessage;
use RoundlyConsulting\Events\Notifications\EventCancelledNotification;

final class EventCancelledWithRefund extends EventCancelledNotification
{
    public function toMail(object $notifiable): MailMessage
    {
        return parent::toMail($notifiable)->line(__('Your ticket is refunded within 5 days.'));
    }
}
```

A class that is not the package’s own subtype throws, naming the key, and EVENTS\_NOTIFICATIONS=off turns every notice off. The texts live in events::notifications (en, sk) — each type has a subject, a greeting and a body, plus shared lines — with the placeholders :title and :when. Override these hooks in a subclass:

| Hook | Default |
| --- | --- |
| `toMail(object $notifiable): MailMessage` | Subject, greeting, lines(), action(). |
| `toArray(object $notifiable): array` | type, event, payload() |
| `via(object $notifiable): array` | `events.notifications.channels` |
| `shouldSend(object $notifiable, string $channel): bool` | The delivery-time re-check — keep calling parent::shouldSend(). |
| `lines() · payload() · action() (protected)` | The body lines · the type’s extra keys · \[label, url\] or null. |
| `line(string $key, array $replace = []) · when(?CarbonImmutable $moment = null) (protected)` | A translated line of this type · a time in the event’s timezone and the reader’s locale. |

## Silent writes

```php
Events::for($event)->silently()->edit()->title(['en' => 'Jazz Night (fixed typo)'])->save();
Events::for($event)->silently()->reschedule('2026-12-05 20:00');
```

A silent handle’s event-wide changes — update, postpone, reschedule, cancel, move online — dispatch the domain events, flagged silent, but send no notice. Personal confirmations — a seat confirmed, a promotion, an invitation — are not event-wide and still go to the person concerned. Over the API, ?notify=0 does the same for PATCH /events/{event}.

## Reminders

```php
// config/events.php — every event
'reminders' => [
    'offsets' => ['P1D', 'PT2H'],     // at most five, each PT5M–P31D
    'max_lateness' => 'PT1H',
    'all_day_at' => '09:00',
],

// one event's own list replaces the config
Events::for($event)->edit()->reminders(['P2D'])->save();
```

- Offsets are ISO-8601 durations before the start, between PT5M and P31D and at most five — in events.reminders.offsets for every event, or an event’s own ->reminders(\[...\]). A configured list outside the bounds throws InvalidEventsConfigurationException naming the key, and events:doctor reports it.
- An offset’s day part is calendar arithmetic in the event’s timezone — P1D is the day before at the same local time, 23 or 25 hours of real time across a DST change — and its time part is absolute: PT2H is two hours.
- All-day events count back from events.reminders.all\_day\_at (09:00 local) on their first day: P1D is 09:00 the day before, PT1H 08:00 on the day.
- Exactly once: events:sweep claims each reminder by inserting (event, offset, starts\_at) into event\_reminder\_deliveries, so any number of concurrent sweepers send it once. A reschedule changes starts\_at, so reminders re-arm.
- Skipped: an offset due before the current schedule was announced — no “tomorrow” reminder for an event created an hour ahead — one more than events.reminders.max\_lateness (PT1H) late, anything at or after the start, and events that are not scheduled or rescheduled.
- RemindersDispatched(Event, string $offset, int $recipients) fires, and SweepResult counts reminders and notified.

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