---
title: "Configuration — Events for Laravel | Roundly"
description: "Every config key with its env variable and default — keys, tables, models, defaults, venues, payments, check-in, posters, URLs, API and notices."
url: https://roundly-consulting.com/open-source/docs/events-for-laravel/configuration
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)

# Configuration

The package works with zero configuration — every key has a default. Publish config/events.php only to override:

```bash
php artisan vendor:publish --tag=events-config
```

Every value is read through a strict reader (Support\\EventsConfig): a malformed duration, timezone, table name, URL template or class throws InvalidEventsConfigurationException (or the toolkit’s InvalidConfigurationException) naming the key — never a silent fallback.

- A blank value means the default.
- Booleans accept on/off, true/false, 1/0 and yes/no.
- Durations are ISO-8601: PT30M, PT2H, P1D.
- Set the key types and the table names before migrating — they shape the columns.

## The config file

```php
// config/events.php (imports and docblocks omitted)
return [
    'primary_key_type' => env('EVENTS_PRIMARY_KEY_TYPE', 'bigint'),
    'key_type' => env('EVENTS_KEY_TYPE', 'bigint'),

    'tables' => [
        'events' => 'events',
        'venues' => 'event_venues',
        'tiers' => 'event_admission_tiers',
        'rsvps' => 'event_rsvps',
        'hosts' => 'event_hosts',
        'invitations' => 'event_invitations',
        'reminder_deliveries' => 'event_reminder_deliveries',
    ],

    'models' => [
        'event' => Event::class,
        'venue' => Venue::class,
        'tier' => AdmissionTier::class,
        'rsvp' => Rsvp::class,
    ],

    'lifecycle' => EventLifecycle::class,

    'organizers' => ['user' => 'App\\Models\\User'],
    'attendees' => ['user' => 'App\\Models\\User'],

    'defaults' => [
        'timezone' => env('EVENTS_TIMEZONE'),
        'duration' => 'PT2H',
        'visibility' => 'public',
        'max_guests' => 0,
        'guest_list' => 'hidden',
        'show_counts' => true,
        'waitlist' => true,
    ],

    'schedule' => ['max_duration' => 'P31D'],

    'venues' => [
        'geocode_on_create' => env('EVENTS_GEOCODE_VENUES', 'off'),
        'default_timezone' => null,
    ],

    'geo' => ['max_radius_km' => 500, 'default_radius_km' => 25],

    'tags' => ['scope' => null],

    'tiers' => ['default_currency' => env('EVENTS_DEFAULT_CURRENCY')],

    'waitlist' => ['stop_promoting_before' => 'PT1H'],

    'payments' => [
        'collector' => NoPaymentCollector::class,
        'hold_for' => 'PT30M',
        'promotion_hold' => 'PT24H',
    ],

    'check_in' => [
        'opens_before' => 'PT3H',
        'closes_after' => 'PT2H',
        'url' => env('EVENTS_CHECK_IN_URL'),
    ],

    'poster' => [
        'bucket' => 'poster',
        'disk' => null,
        'max_kb' => 10240,
        'min_width' => 600,
        'min_height' => 315,
        'fallback_url' => env('EVENTS_POSTER_FALLBACK'),
        'variants' => [
            'thumb' => [400, 300],
            'card' => [800, 450],
            'square' => [1200, 1200],
            'landscape' => [1200, 900],
            'wide' => [1920, 1080],
        ],
    ],

    'urls' => [
        'resolver' => TemplateEventUrls::class,
        'event' => env('EVENTS_URL'),
        'event_link' => env('EVENTS_LINK_URL'),
        'invitation' => env('EVENTS_INVITATION_URL'),
        'rsvp' => env('EVENTS_RSVP_URL'),
    ],

    'seo' => [
        'json_ld' => [
            'type' => 'Event',
            'unlisted' => 'off',
            'expose_online_url' => 'off',
            'organizer_type' => 'Organization',
        ],
    ],

    'feeds' => [
        'keep_cancelled_for' => 'P30D',
        'max_events' => 500,
        'name' => env('APP_NAME', 'Events'),
    ],

    'resources' => [
        'organizer' => EventOrganizerResource::class,
        'host' => EventHostResource::class,
        'attendee' => EventAttendeeResource::class,
        'invitee' => EventInviteeResource::class,
        'actor' => EventActorResource::class,
        'venue_owner' => VenueOwnerResource::class,
    ],

    'routes' => [
        'enabled' => env('EVENTS_ROUTES') ?: 'off',
        'prefix' => 'api',
        'middleware' => ['api', 'throttle:events-api'],
        'auth_middleware' => ['auth'],
        'max_per_page' => 100,
        'rate_limits' => ['api' => 120, 'rsvp' => 20, 'check_in' => 300, 'feeds' => 60],
    ],

    'notifications' => [
        'enabled' => env('EVENTS_NOTIFICATIONS', 'on'),
        'channels' => ['mail'],
        'queue' => null,
        'notify_actor' => false,
        'types' => [
            'invited' => EventInvitationNotification::class,
            'confirmed' => RsvpConfirmedNotification::class,
            'waitlist_promoted' => WaitlistPromotedNotification::class,
            'reminder' => EventReminderNotification::class,
            'updated' => EventUpdatedNotification::class,
            'postponed' => EventPostponedNotification::class,
            'rescheduled' => EventRescheduledNotification::class,
            'cancelled' => EventCancelledNotification::class,
            'moved_online' => EventMovedOnlineNotification::class,
        ],
        'audiences' => [
            'reminder' => ['going'],
            'updated' => ['going', 'held'],
            'postponed' => ['going', 'held', 'waitlisted', 'interested'],
            'rescheduled' => ['going', 'held', 'waitlisted', 'interested'],
            'cancelled' => ['going', 'held', 'waitlisted', 'interested'],
            'moved_online' => ['going', 'held'],
        ],
    ],

    'reminders' => [
        'offsets' => [],
        'max_lateness' => 'PT1H',
        'all_day_at' => '09:00',
    ],

    'authorization' => [
        'register_policy' => true,
        'policy' => EventPolicy::class,
        'venue_policy' => VenuePolicy::class,
        'abilities' => [
            'create' => null,
            'admin' => 'events.admin',
            'venues' => null,
            'shared_venues' => null,
        ],
    ],

    'slugs' => [
        'history' => env('EVENTS_SLUG_HISTORY', 'off'),
        'max_length' => 120,
    ],
];
```

## Keys, tables and models

| Key | Env | Default | Purpose |
| --- | --- | --- | --- |
| `primary_key_type` | `EVENTS_PRIMARY_KEY_TYPE` | bigint | Key of the events table and of every column pointing at an event — lifecycle subjects, tag taggables, media owners, slug history. bigint, uuid or ulid; must equal lifecycle.key\_type and tags.key\_type (and sluggable.key\_type with slug history on). Set it before migrating. |
| `key_type` | `EVENTS_KEY_TYPE` | bigint | Every column pointing at your models — organizer, attendee, co-host, invitee, inviter, venue owner, added\_by, checked\_in\_by. Must equal your users’ key type and lifecycle.actor\_key\_type (and tags.key\_type with organizer-scoped labels). Set it before migrating. |
| `tables.events … tables.reminder_deliveries` | — | events, event\_venues, event\_admission\_tiers, event\_rsvps, event\_hosts, event\_invitations, event\_reminder\_deliveries | The seven table names, each ^\[a-z\_\]\[a-z0-9\_\]{0,63}$. Rename before migrating — events is a common host table name. |
| `models.event / venue / tier / rsvp` | — | Event, Venue, AdmissionTier, Rsvp | A subclass of the package’s model; blank = the package’s. Anything else throws, naming the key. |
| `lifecycle` | — | Lifecycle\\EventLifecycle | The status workflow: a LifecycleDefinition implementing Contracts\\EventLifecycle — see Statuses and lifecycle. |
| `organizers` | — | \['user' => 'App\\Models\\User'\] | API alias => class implementing Organizer — also the classes managedBy() asks in SQL, so list every organizer class, teams included. A class that does not exist is skipped. |
| `attendees` | — | \['user' => 'App\\Models\\User'\] | API alias => attendee class; events:doctor warns about listed classes that cannot be notified. A class that does not exist is skipped. |

## Defaults for new events

| Key | Env | Default | Purpose |
| --- | --- | --- | --- |
| `defaults.timezone` | `EVENTS_TIMEZONE` | null | Used after an explicit timezone and the venue’s; blank = app.timezone. An invalid IANA name throws. |
| `defaults.duration` | — | PT2H | ISO-8601, when neither an end nor a duration is given — ends\_at is never null. |
| `defaults.visibility` | — | public | public, unlisted or private. |
| `defaults.max_guests` | — | 0 | Guests per RSVP. |
| `defaults.guest_list` | — | hidden | hidden, attendees or public. |
| `defaults.show_counts` | — | true | Show the going counts to viewers. |
| `defaults.waitlist` | — | true | A full event waitlists instead of refusing. |
| `schedule.max_duration` | — | P31D | The longest event. |

## Venues, geography and tags

| Key | Env | Default | Purpose |
| --- | --- | --- | --- |
| `venues.geocode_on_create` | `EVENTS_GEOCODE_VENUES` | off | Queue geocoding for every new venue without coordinates. |
| `venues.default_timezone` | — | null | A venue without a timezone takes this, else app.timezone. |
| `geo.max_radius_km` | — | 500 | Largest near-me radius (0.001–20,038). |
| `geo.default_radius_km` | — | 25 | Radius when none is given (≤ max\_radius\_km). |
| `tags.scope` | — | null | null = one global label catalogue; organizer = one per organizer. |

## Ticket types, waitlist, payments, check-in

| Key | Env | Default | Purpose |
| --- | --- | --- | --- |
| `tiers.default_currency` | `EVENTS_DEFAULT_CURRENCY` | null | A plain-number price is read in this currency; blank = money.default\_currency. |
| `waitlist.stop_promoting_before` | — | PT1H | No automatic promotion this close to the start. |
| `payments.collector` | — | Payments\\NoPaymentCollector | Your Contracts\\CollectsAdmissionPayment. |
| `payments.hold_for` | — | PT30M | How long a paid seat is held for the payer — longer than zero. |
| `payments.promotion_hold` | — | PT24H | How long a seat promoted from the waitlist is held — longer than zero. |
| `check_in.opens_before` | — | PT3H | Door scans open this long before the start (or the doors time). |
| `check_in.closes_after` | — | PT2H | And close this long after the end. |
| `check_in.url` | `EVENTS_CHECK_IN_URL` | null | QR payload template with {code} (http or https); else the bare code. |

## Poster

| Key | Env | Default | Purpose |
| --- | --- | --- | --- |
| `poster.bucket` | — | poster | The media-library bucket. |
| `poster.disk` | — | null | Blank = media-library’s default disk. |
| `poster.max_kb` | — | 10240 | Largest upload, at least 1. |
| `poster.min_width / min_height` | — | 600 / 315 | Smallest dimensions, each at least 1. |
| `poster.fallback_url` | `EVENTS_POSTER_FALLBACK` | null | posterUrl() for an event without a poster. |
| `poster.variants` | — | thumb 400×300, card 800×450, square 1200×1200, landscape 1200×900, wide 1920×1080 | name => \[width, height\], positive integers; webp, cover, queued. JSON-LD uses square, landscape and wide. |

## URLs, search engines, feeds

| Key | Env | Default | Purpose |
| --- | --- | --- | --- |
| `urls.resolver` | — | Support\\TemplateEventUrls | Your Contracts\\EventUrls, to call route(). |
| `urls.event` | `EVENTS_URL` | null | Event page template: {uuid}, {slug}, {locale}. |
| `urls.event_link` | `EVENTS_LINK_URL` | null | Unlisted and private events, uuid only: {uuid}, {locale}. |
| `urls.invitation` | `EVENTS_INVITATION_URL` | null | Accept link: {token}, {locale}. |
| `urls.rsvp` | `EVENTS_RSVP_URL` | null | The attendee’s ticket page: {uuid}, {rsvp}, {locale}. |
| `seo.json_ld.type` | — | Event | A schema.org type name (letters only). |
| `seo.json_ld.unlisted` | — | off | Structured data for unlisted events too. |
| `seo.json_ld.expose_online_url` | — | off | The join link instead of the page URL in VirtualLocation. |
| `seo.json_ld.organizer_type` | — | Organization | Or Person. |
| `feeds.keep_cancelled_for` | — | P30D | Cancelled events stay in the public feed this long. |
| `feeds.max_events` | — | 500 | 1–10,000. |
| `feeds.name` | `APP_NAME` | Events | The feed’s calendar name; blank = Events. |

## API resources and routes

| Key | Env | Default | Purpose |
| --- | --- | --- | --- |
| `resources.organizer / host / attendee / invitee / actor / venue_owner` | — | {id, type} resources | Any instantiable JsonResource subclass; it receives the model — see JSON API. |
| `routes.enabled` | `EVENTS_ROUTES` | off | A switch that is not set stays off — also a published routes array without it. |
| `routes.prefix` | — | api | URL prefix. |
| `routes.middleware` | — | \['api', 'throttle:events-api'\] | Every route. |
| `routes.auth_middleware` | — | \['auth'\] | Write routes; the package also refuses writes without a signed-in Eloquent user. |
| `routes.max_per_page` | — | 100 | 1–1000 (the default page is 15 events, 25 attendees or venues). |
| `routes.rate_limits.api / rsvp / check_in / feeds` | — | 120 / 20 / 300 / 60 | Per minute, each at least 1. |

## Notifications and reminders

| Key | Env | Default | Purpose |
| --- | --- | --- | --- |
| `notifications.enabled` | `EVENTS_NOTIFICATIONS` | on | Every notice on or off. |
| `notifications.channels` | — | \['mail'\] | For example \['mail', 'database'\]. |
| `notifications.queue` | — | null | The queue the notices go to. |
| `notifications.notify_actor` | — | false | Tell the person who made the change too. |
| `notifications.types.<type>` | — | the package’s classes | A subclass of the package’s class, or false (off). |
| `notifications.audiences.<type>` | — | see Notifications | Who hears about an event-wide change, by RSVP status. |
| `reminders.offsets` | — | \[\] | ISO-8601 offsets before the start; at most five, each PT5M–P31D. An event’s own list replaces it. |
| `reminders.max_lateness` | — | PT1H | A reminder later than this is dropped. |
| `reminders.all_day_at` | — | 09:00 | Local time (HH:MM) all-day events are reminded at. |

## Authorization and slugs

| Key | Env | Default | Purpose |
| --- | --- | --- | --- |
| `authorization.register_policy` | — | true | Off: register your own policies (a missing class is never a boot failure). |
| `authorization.policy / venue_policy` | — | Policies\\EventPolicy / Policies\\VenuePolicy | Swap with a subclass. |
| `authorization.abilities.create` | — | null | A Gate ability that may create events; null = any authenticated user. |
| `authorization.abilities.admin` | — | events.admin | Manage any event; undefined = nobody. |
| `authorization.abilities.venues` | — | null | A Gate ability that may create venues. |
| `authorization.abilities.shared_venues` | — | null | A Gate ability that creates and manages shared (ownerless) venues; null = admins only. |
| `slugs.history` | `EVENTS_SLUG_HISTORY` | off | A changed slug keeps working and redirects (needs sluggable-migrations). |
| `slugs.max_length` | — | 120 | 16–255. |

## Environment

The common knobs are env-driven, so you rarely publish the config at all:

```bash
EVENTS_KEY_TYPE=uuid
EVENTS_TIMEZONE=Europe/Bratislava
EVENTS_DEFAULT_CURRENCY=EUR
EVENTS_GEOCODE_VENUES=on
EVENTS_URL=https://example.com/{locale}/events/{slug}
EVENTS_LINK_URL=https://example.com/{locale}/e/{uuid}
EVENTS_INVITATION_URL=https://example.com/invitations/{token}
EVENTS_RSVP_URL=https://example.com/events/{uuid}/tickets/{rsvp}
EVENTS_CHECK_IN_URL=https://example.com/check-in/{code}
EVENTS_ROUTES=on
EVENTS_NOTIFICATIONS=on
EVENTS_SLUG_HISTORY=off
```

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