---
title: "Configuration — Teams for Laravel | Roundly"
description: "Every config key and its default — models, morph key type, role driver and cache, invite and membership lifecycle, approvals and the Gate."
url: https://roundly-consulting.com/open-source/docs/teams-for-laravel/configuration
language: en
---

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

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

# Configuration

Once the migrations are published and run, the package works with zero configuration — every key has a default, most of them env-backed. Publish config/teams.php only to change them. The published file in full:

```php
use RoundlyConsulting\Teams\Models\Invite;
use RoundlyConsulting\Teams\Models\JoinRequest;
use RoundlyConsulting\Teams\Models\Member;
use RoundlyConsulting\Teams\Models\Team;
use RoundlyConsulting\Teams\Models\TeamRole;

return [

    'models' => [
        'team' => Team::class,
        'member' => Member::class,
        'invite' => Invite::class,
        'team_role' => TeamRole::class,
        'join_request' => JoinRequest::class,
    ],

    'key_type' => env('TEAMS_KEY_TYPE', 'bigint'),

    'roles' => [
        'provider' => env('TEAMS_ROLES_PROVIDER', 'array'),
        'owner' => 'owner',
        'admin' => 'admin',
        'default' => env('TEAMS_DEFAULT_ROLE', 'member'),
        'per_team' => env('TEAMS_PER_TEAM_ROLES', false),
        'cache' => [
            'enabled' => env('TEAMS_ROLES_CACHE', false),
            'store' => env('TEAMS_ROLES_CACHE_STORE'),
            'key' => env('TEAMS_ROLES_CACHE_KEY', 'teams.roles'),
            'ttl' => env('TEAMS_ROLES_CACHE_TTL', 3600),
        ],
    ],

    'invites' => [
        'expires_after' => env('TEAMS_INVITES_EXPIRES_AFTER', '7 days'),
        'code_length' => env('TEAMS_INVITES_CODE_LENGTH', 32),
    ],

    'members' => [
        'prune_after' => env('TEAMS_MEMBERS_PRUNE_AFTER', '30 days'),
        'expiring_within' => env('TEAMS_MEMBERS_EXPIRING_WITHIN', 7),
    ],

    'join_requests' => [
        'prune_after' => env('TEAMS_JOIN_REQUESTS_PRUNE_AFTER', '30 days'),
    ],

    'approvals' => [
        'enabled' => env('TEAMS_APPROVALS', false),
        'rule' => env('TEAMS_APPROVALS_RULE', 'unanimous'),
        'quorum' => env('TEAMS_APPROVALS_QUORUM'),
    ],

    'gate' => [
        'register' => env('TEAMS_REGISTER_GATE', true),
        'prefix' => env('TEAMS_GATE_PREFIX', 'teams'),
        'owner_ability' => env('TEAMS_GATE_OWNER_ABILITY', 'owner'),
    ],

    'notifications' => [
        'queue_connection' => env('TEAMS_NOTIFY_CONNECTION'),
    ],

];
```

## Every key

| Key | Default | Env | Purpose |
| --- | --- | --- | --- |
| `models.team` | Team::class | — | Team model. Must extend the packaged Team. |
| `models.member` | Member::class | — | Membership model. |
| `models.invite` | Invite::class | — | Invite model. |
| `models.team_role` | TeamRole::class | — | Per-team role override model. |
| `models.join_request` | JoinRequest::class | — | Join-request model. |
| `key_type` | bigint | `TEAMS_KEY_TYPE` | Key type of the polymorphic columns — bigint, uuid or ulid. Read by the migrations; anything else throws InvalidConfigurationException. |
| `roles.provider` | array | `TEAMS_ROLES_PROVIDER` | Role storage: array (in code) or database (the team\_roles table); anything else throws. |
| `roles.owner` | owner | — | Role given to a team’s creator and to a new owner. |
| `roles.admin` | admin | — | Role the previous owner is demoted to on transfer. |
| `roles.default` | member | `TEAMS_DEFAULT_ROLE` | Role used when none is supplied, e.g. approving a join request. |
| `roles.per_team` | false | `TEAMS_PER_TEAM_ROLES` | Enable per-team role overrides. Off = global roles, zero extra queries. |
| `roles.cache.enabled` | false | `TEAMS_ROLES_CACHE` | Cache the database role map; flushed on every role change. |
| `roles.cache.store` | null | `TEAMS_ROLES_CACHE_STORE` | Cache store; null = the default store. Tags are used when supported. |
| `roles.cache.key` | teams.roles | `TEAMS_ROLES_CACHE_KEY` | Cache key for the role map. |
| `roles.cache.ttl` | 3600 | `TEAMS_ROLES_CACHE_TTL` | Cache lifetime in seconds, 1–31536000. |
| `invites.expires_after` | 7 days | `TEAMS_INVITES_EXPIRES_AFTER` | Default invite expiry when none is supplied, also used on resend — a positive interval such as 7 days or P7D. |
| `invites.code_length` | 32 | `TEAMS_INVITES_CODE_LENGTH` | Length of the random invite code, 8–128 (URL-safe alphanumerics). |
| `members.prune_after` | 30 days | `TEAMS_MEMBERS_PRUNE_AFTER` | Zero or positive interval after a membership’s expiry before teams:members:prune or model:prune deletes it. |
| `members.expiring_within` | 7 | `TEAMS_MEMBERS_EXPIRING_WITHIN` | Default window in days (1–3660) for teams:members:expiring and MembershipExpiringSoon. |
| `join_requests.prune_after` | 30 days | `TEAMS_JOIN_REQUESTS_PRUNE_AFTER` | Zero or positive interval after a resolved request’s last update before model:prune deletes it. |
| `approvals.enabled` | false | `TEAMS_APPROVALS` | Route join requests through approvals-for-laravel for multi-admin sign-off. |
| `approvals.rule` | unanimous | `TEAMS_APPROVALS_RULE` | Default ApprovalRule when the handle sets none: unanimous, quorum, any or weighted. Anything else throws InvalidConfigurationException. |
| `approvals.quorum` | null | `TEAMS_APPROVALS_QUORUM` | Default quorum (a positive integer) when the join-request handle sets none. |
| `gate.register` | true | `TEAMS_REGISTER_GATE` | Register the Gate hook and the Blade directives. |
| `gate.prefix` | teams | `TEAMS_GATE_PREFIX` | Ability prefix, e.g. teams.manage-billing. |
| `gate.owner_ability` | owner | `TEAMS_GATE_OWNER_ABILITY` | Short ability that resolves to ownership: teams.owner. |
| `notifications.queue_connection` | null | `TEAMS_NOTIFY_CONNECTION` | Queue connection for the publishable event subscriber stub. |

## Environment

The common switches are env-driven, so you rarely need the published file at all:

```bash
TEAMS_KEY_TYPE=uuid                  # set before you migrate
TEAMS_ROLES_PROVIDER=database
TEAMS_ROLES_CACHE=true
TEAMS_DEFAULT_ROLE=member
TEAMS_PER_TEAM_ROLES=true
TEAMS_INVITES_EXPIRES_AFTER="14 days"
TEAMS_MEMBERS_EXPIRING_WITHIN=14
TEAMS_APPROVALS=true
TEAMS_REGISTER_GATE=true
```

## Strict values

The config file passes env values through raw, and the package reads them strictly. The bool keys take env strings as they come: true/1/on/yes switch a flag on and false/0/off/no switch it off; the int keys take integer strings such as "3600". A key that is not set — absent, null, or blank ('' or whitespace, as a bare TEAMS\_APPROVALS= line gives) — takes its default. Anything else throws an InvalidConfigurationException naming the key instead of quietly reading as a default:

- a mistyped switch (TEAMS\_APPROVALS=disabled), role provider (databse) or approval rule;
- an integer that is not one (abc, 7.5) or is out of range — an invite code shorter than 8 or longer than 128, for example;
- an interval Carbon cannot parse (seven days) or that is negative — invites.expires\_after must also be above zero;
- a non-string role key, cache store or key, gate prefix or owner ability.

php artisan about shows such a value as INVALID instead of failing.

The interval error is worded in the current locale (teams::errors.invalid\_config\_value, English and Slovak). The boolean, integer, choice and string errors come from the toolkit’s Config readers and stay English.

## Morph key type

key\_type is read by the migrations, so set it before you migrate. It types the polymorphic owner, member, invited\_by, requester and responded\_by columns — use uuid or ulid when the models behind them use UUID or ULID primary keys. Your morph targets must share one key type; any other value throws an InvalidConfigurationException when the migrations run.

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