Contacts
Contacts are how a user, company, or customer records ways to reach them — email addresses, phone numbers, and (as a closely related sibling feature) social profile links. Any model can gain contacts by using one trait.

Contact methods
Rather than a free-text "type" field, every contact points at a ContactMethod — a configurable catalog record (email, phone, fax, and so on) with is_required, is_social, is_recommended, and status flags for validation and UI hints.
The is_social flag is what separates "contacts" from "socials" — they're stored in the same table family, but Contact scopes itself to non-social methods by default via a global scope. Social is a separate model using the same IsContact trait, implicitly scoped to is_social = true methods instead. See the note at the bottom of this page.
Attaching to any model
// The owning model, and the contact method it uses (app/Concerns/IsContact.php)...
$contact->contactable() : Illuminate\Database\Eloquent\Relations\MorphTo
$contact->method() : Illuminate\Database\Eloquent\Relations\BelongsToTrait Methods (HasContacts)
// Every contact belonging to this model, and just the primary one...
$model->contacts() : Illuminate\Database\Eloquent\Relations\MorphMany
$model->primaryContact() : Illuminate\Database\Eloquent\Relations\MorphOne
// Determine whether this model has any contacts...
$model->hasContacts() : bool
// Determine whether this model owns a given contact...
$model->ownsContact(Contact $contact) : boolUser, Company, and Customer all gain these through App\Concerns\HasContacts, following the same Has* / Is* pairing used throughout Reshape's polymorphic features (see Addresses for the equivalent pattern).
Actions (app/Actions/Contacts/)
// Create a contact...
CreateContact::create(array $input) : App\Models\Contact
// Update a contact...
UpdateContact::update(Contact $contact, array $input) : void
// Delete a contact (refuses to delete the primary one)...
DeleteContact::delete(Contact $contact) : voidAutomatic primary management
Exactly one contact per contactable is marked is_primary at any time, enforced entirely through model saving/saved events rather than application code remembering to do it. The first contact you add is automatically primary; marking a different one primary demotes the rest. Address uses the identical pattern — see Addresses.
Socials
Social profile links (Twitter/X, LinkedIn, GitHub, etc.) reuse the exact same contact/method machinery with is_social = true, exposed through a parallel HasSocials trait.
Trait Methods (HasSocials)
// Every social contact belonging to this model...
$model->socials() : Illuminate\Database\Eloquent\Relations\MorphMany
// Determine whether this model has any socials...
$model->hasSocials() : bool
// Determine whether this model owns a given social contact...
$model->ownsSocial(Social $social) : boolActions (app/Actions/Contacts/)
// Create a social contact...
CreateSocial::create(array $input) : App\Models\Social
// Update a social contact...
UpdateSocial::update(Social $social, array $input) : void
// Delete a social contact...
DeleteSocial::delete(Social $social) : voidIf you're building a "social links" UI, this is the feature to reach for rather than a bespoke model.