---
title: "Configuration — Contacts for Laravel | Roundly"
description: "Every config key and its default — model, table, key type, auto-primary, country code, verification, custom kinds and relationship kinds."
url: https://roundly-consulting.com/open-source/docs/contacts-for-laravel/configuration
language: en
---

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

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

# Configuration

The published config/contacts.php:

```php
return [
    'model' => RoundlyConsulting\Contacts\Models\Contact::class,
    'table' => 'contacts',
    'key_type' => env('CONTACTS_KEY_TYPE', 'bigint'),

    'auto_primary' => true,
    'require_owner_for_primary' => false,
    'default_country_code' => env('CONTACTS_DEFAULT_COUNTRY_CODE'),

    'verification' => [
        'ttl' => env('CONTACTS_VERIFICATION_TTL', 60),
        'style' => env('CONTACTS_VERIFICATION_STYLE', 'code'),
        'code_length' => env('CONTACTS_VERIFICATION_CODE_LENGTH', 6),
        'token_length' => env('CONTACTS_VERIFICATION_TOKEN_LENGTH', 32),
        'max_attempts' => env('CONTACTS_VERIFICATION_MAX_ATTEMPTS', 5),
    ],

    'types' => [
        // 'whatsapp' => ['label' => 'WhatsApp', 'icon' => 'chat', 'rules' => ['required', 'string']],
    ],

    'relationship_kinds' => [
        // 'works_at' => 'Works at', 'spouse_of' => 'Spouse of',
    ],
];
```

## Every key

| Key | Default | Env | Purpose |
| --- | --- | --- | --- |
| `model` | Contact::class | — | Eloquent model for contacts. Must be the package’s Contact or extend it — anything else throws InvalidConfigurationException. |
| `table` | contacts | — | Table contacts live in; read by the migration and by Contact::getTable(). Blank = not set (contacts); any other value must be a string. |
| `key_type` | bigint | `CONTACTS_KEY_TYPE` | Key type of owner\_id: bigint, uuid or ulid. Anything else throws InvalidConfigurationException. Fixed when the migration runs. |
| `auto_primary` | true | — | The first contact of a kind added for an owner becomes its primary; when the primary leaves a kind (deleted, synced away, moved), the next by position takes over. |
| `require_owner_for_primary` | false | — | Only owned contacts may be primary; otherwise PrimaryContactConflict is thrown. |
| `default_country_code` | null | `CONTACTS_DEFAULT_COUNTRY_CODE` | Best-effort dialling prefix for phone numbers entered in national format — it replaces the trunk 0 (see Contact kinds). Written 421, +421 or 1-264 (1–4 digits, no leading zero); not set (null or blank) means none — a national number with a trunk 0 is then rejected as an invalid phone. |
| `verification.ttl` | 60 | `CONTACTS_VERIFICATION_TTL` | Minutes a verification token stays valid, 1–525600 (a year). |
| `verification.style` | code | `CONTACTS_VERIFICATION_STYLE` | code for a numeric one-time code, token for a random hex string. Exact and lower-case: anything else throws, so a typo never downgrades a token to a 6-digit code. |
| `verification.code_length` | 6 | `CONTACTS_VERIFICATION_CODE_LENGTH` | Number of digits when style is code, 1–72. |
| `verification.token_length` | 32 | `CONTACTS_VERIFICATION_TOKEN_LENGTH` | Bytes of randomness when style is token, 1–36 — hex-encoded, so the string is twice as long; bcrypt reads only the first 72 characters. |
| `verification.max_attempts` | 5 | `CONTACTS_VERIFICATION_MAX_ATTEMPTS` | Wrong guesses a token survives, 1–1000. The guess that spends the last one voids the token. |
| `types` | \[\] | — | Register custom kinds and override the label, icon or rules of built-in kinds. A kind => definition map; label and icon are strings (blank = not set, so the kind’s own) and rules a list of rule strings. |
| `relationship_kinds` | \[\] | — | Allow-list for relateTo() / relationsOfKind(). Empty or not set (null or blank) = free-form; a list or kind => label map restricts kinds. Any other non-array value throws. |

The two switches accept env-style strings ('true'/'false', '1'/'0', 'on'/'off', 'yes'/'no'), and the numeric keys accept integer strings ('30'). A key that is not set (absent, null, or blank: '' or whitespace, as a bare CONTACTS\_VERIFICATION\_TTL= line gives) takes its default. Anything else — a mistyped switch or style, a TTL, length or attempt count that is not a whole number ('five', '5.5') or is out of range, a non-string table, a country code that isn’t one, a malformed types or relationship\_kinds entry — throws the package toolkit’s InvalidConfigurationException naming the key, rather than being read as a default or clamped.

## Environment

The deployment-sensitive keys are env-backed, so you rarely need to publish the file:

```bash
CONTACTS_KEY_TYPE=bigint
CONTACTS_DEFAULT_COUNTRY_CODE=421
CONTACTS_VERIFICATION_TTL=60
CONTACTS_VERIFICATION_STYLE=code
CONTACTS_VERIFICATION_CODE_LENGTH=6
CONTACTS_VERIFICATION_TOKEN_LENGTH=32
CONTACTS_VERIFICATION_MAX_ATTEMPTS=5
```

## Custom contact model

The Contact model is intentionally not final. Extend it and point contacts.model at your class — the trait, actions, vCard exporter, notification routing, factory and test matchers all resolve it from there:

```php
namespace App\Models;

use RoundlyConsulting\Contacts\Models\Contact as BaseContact;

class Contact extends BaseContact
{
    // your own relations, accessors and scopes
}

// config/contacts.php
'model' => App\Models\Contact::class,
```

The configured class must be the package’s Contact or a subclass of it; anything else — a foreign model, a class that doesn’t exist — throws InvalidConfigurationException naming contacts.model instead of being silently replaced. Change the table through contacts.table rather than a $table property — the model’s getTable() honours the config value.

## Inspecting the configuration

The package reports its configuration to Laravel’s about command:

```bash
php artisan about --only=contacts
```

| Line | Shows |
| --- | --- |
| Model | Class basename of the resolved contact model. |
| Table | The configured table name. |
| Auto primary | ON / OFF. |
| Require owner for primary | ON / OFF. |
| Default country code | SET / NONE — never the prefix itself. |
| Verification | Style, TTL and attempt limit, e.g. code, 60m, 5 attempts. |
| Custom types | A count, e.g. 2 registered — never the kind names. |
| Relationship kinds | FREE-FORM, or a count such as 3 allowed. |

Registered kinds and relationship kinds are your domain vocabulary, so they are reported as counts only — never by name. The country code is reported by presence, never its value. A malformed table, country code, verification setting, types or relationship\_kinds value shows as INVALID instead of failing the command.

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