---
id: CON-CRM-ENT-004
name: Affiliation Type
feature: FEAT-CRM-ENT-06
product_area: crm
module: entities
revision: 1
status: draft
product_intent: proposed
implementation_evidence: observed
source_sha:
  services-crm: 7ea48a414c42eade10d60753b8bcc323b1dd726f
  react-console: da63075876b2c5f4a6af07d457bb24ea8bf4c0c7
definition: An affiliation type describes the kind of relationship a person has with an organization.
owned_by: MOD-CRM-ENT
facets: []
evidence:
  - >-
    `services-crm` at `7ea48a414c42eade10d60753b8bcc323b1dd726f`:
    `modules/entities/db/affiliation-types.table.ts` records this concept.
narrative:
  status: verified
  review: NARRATIVE-REVIEW-2026-10-23
narrative_sources:
  - section: Why It Exists
    provenance: inferred
    source: >-
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#answers 400 naming the
      field for a create whose name is nothing but spaces;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#answers 400 for an id
      that is not a configuration id, saying what an id may hold;
      services-crm:modules/entities/tests/integration/services/persistence/contact-roles.integration.spec.js#refuses
      it with the legacy words, naming the organization types whose rules depend on it;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0196.spec.ts#CRM-ACC-0196;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#refuses the delete (409)
      while an affiliation of a contact is of that type;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#refuses a caller who may
      only read affiliation types the change of the default (403);
      services-crm:modules/entities/tests/integration/services/persistence/affiliation-types.integration.spec.js#keeps
      the mappings, as an omitted collection means unchanged;
      services-crm:modules/entities/tests/e2e/contact-roles.e2e.spec.js#answers 400 for a reorder
      that leaves a role of the tenant out, naming it;
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#D-11;
      services-crm:modules/entities/tests/e2e/contact-roles.e2e.spec.js#answers a portal user of the
      tenant;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0199.spec.ts#CRM-ACC-0199;
      services-crm:modules/entities/tests/e2e/contact-affiliations.e2e.spec.js#answers 400 for an
      organization the tenant does not have;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0202.spec.ts#CRM-ACC-0202;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0177.spec.ts#CRM-ACC-0177;
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#refuses
      two affiliations newly marked primary at once;
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#refuses
      a primary affiliation that starts after today;
      services-crm:modules/entities/tests/integration/services/persistence/contact-affiliations.integration.spec.js#makes
      nothing primary in its place;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
      an affiliation that ends before it starts;
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#finds
      periods that share their last and first day overlapping;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
      to move the affiliation to another organization under the same id (docs/affiliations/README.md
      §0b.2);
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#counts
      only current and upcoming affiliations against the limit: ended history is unlimited (M2-D5);
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#ends
      the previous primary the day before the effective date by default, and its open roles with it;
      react-console:lib/affiliations/section.ts:affiliationBeingLeft;
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#gives
      the contact the primary affiliation's job title when the save changed only the affiliation's;
      services-crm:modules/entities/tests/e2e/contact-information-sources.e2e.spec.js#refuses 400 a
      second contact naming the same affiliation id, saying another affiliation has it;
      services-crm:modules/entities/tests/e2e/contact-affiliations.e2e.spec.js#answers 403
      permission_missing to a caller who may read organizations but not contacts;
      react-console:components/console/organization/affiliation-dialogs.tsx:ContactPrimaryInfo;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0172.spec.ts#CRM-ACC-0172;
      services-crm:docs/disconnected-information.md#What changed;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a link to a record of an organization other than the affiliation’s;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a new link through an upcoming affiliation, naming the path;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a value that differs from the organization’s in letter case, naming the field (A19);
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#writes
      nothing to the contact: its version is the one before;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#turns
      the linked work address into a disconnected row holding its last value, not a copy (D-25);
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#refuses a client
      that asks for a row to be disconnected, naming the field;
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#is refused 409 on a
      row that is no longer disconnected;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#makes
      an independent copy of the resolved value when the row is saved as copied with no value of its
      own;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a save that removes the affiliation and keeps the rows linked through it;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#links
      nothing when the choice is an empty list;
      services-crm:modules/entities/tests/integration/services/affiliations/affiliation-date-reconciliation.follow-ups.integration.spec.js#links
      every default of its type on the start day when the save stated no choice; FND-CRM-ENT-010
    note: Business need inferred from the sourced behavior; no historical motivation is attributed.
  - section: What It Controls
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#answers 400 naming the
      field for a create whose name is nothing but spaces;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#answers 400 for an id
      that is not a configuration id, saying what an id may hold;
      services-crm:modules/entities/tests/integration/services/persistence/contact-roles.integration.spec.js#refuses
      it with the legacy words, naming the organization types whose rules depend on it;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0196.spec.ts#CRM-ACC-0196;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#refuses the delete (409)
      while an affiliation of a contact is of that type;
      services-crm:modules/entities/tests/e2e/affiliation-types.e2e.spec.js#refuses a caller who may
      only read affiliation types the change of the default (403);
      services-crm:modules/entities/tests/integration/services/persistence/affiliation-types.integration.spec.js#keeps
      the mappings, as an omitted collection means unchanged;
      services-crm:modules/entities/tests/e2e/contact-roles.e2e.spec.js#answers 400 for a reorder
      that leaves a role of the tenant out, naming it;
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#D-11;
      services-crm:modules/entities/tests/e2e/contact-roles.e2e.spec.js#answers a portal user of the
      tenant;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0199.spec.ts#CRM-ACC-0199;
      services-crm:modules/entities/tests/e2e/contact-affiliations.e2e.spec.js#answers 400 for an
      organization the tenant does not have;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0202.spec.ts#CRM-ACC-0202;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0177.spec.ts#CRM-ACC-0177;
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#refuses
      two affiliations newly marked primary at once;
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#refuses
      a primary affiliation that starts after today;
      services-crm:modules/entities/tests/integration/services/persistence/contact-affiliations.integration.spec.js#makes
      nothing primary in its place;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
      an affiliation that ends before it starts;
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#finds
      periods that share their last and first day overlapping;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
      to move the affiliation to another organization under the same id (docs/affiliations/README.md
      §0b.2);
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#counts
      only current and upcoming affiliations against the limit: ended history is unlimited (M2-D5);
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#ends
      the previous primary the day before the effective date by default, and its open roles with it;
      react-console:lib/affiliations/section.ts:affiliationBeingLeft;
      services-crm:modules/entities/tests/unit/services/persistence/contact-affiliations.spec.js#gives
      the contact the primary affiliation's job title when the save changed only the affiliation's;
      services-crm:modules/entities/tests/e2e/contact-information-sources.e2e.spec.js#refuses 400 a
      second contact naming the same affiliation id, saying another affiliation has it;
      services-crm:modules/entities/tests/e2e/contact-affiliations.e2e.spec.js#answers 403
      permission_missing to a caller who may read organizations but not contacts;
      react-console:components/console/organization/affiliation-dialogs.tsx:ContactPrimaryInfo;
      react-console:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0172.spec.ts#CRM-ACC-0172;
      services-crm:docs/disconnected-information.md#What changed;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a link to a record of an organization other than the affiliation’s;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a new link through an upcoming affiliation, naming the path;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a value that differs from the organization’s in letter case, naming the field (A19);
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#writes
      nothing to the contact: its version is the one before;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#turns
      the linked work address into a disconnected row holding its last value, not a copy (D-25);
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#refuses a client
      that asks for a row to be disconnected, naming the field;
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#is refused 409 on a
      row that is no longer disconnected;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#makes
      an independent copy of the resolved value when the row is saved as copied with no value of its
      own;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a save that removes the affiliation and keeps the rows linked through it;
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#links
      nothing when the choice is an empty list;
      services-crm:modules/entities/tests/integration/services/affiliations/affiliation-date-reconciliation.follow-ups.integration.spec.js#links
      every default of its type on the start day when the save stated no choice; FND-CRM-ENT-010
---

# Affiliation Type

## What It Is

An Affiliation Type names the kind of connection a person has with an organization, such as Employer or Member Firm. It is a workspace catalogue definition used by individual affiliations, not a relationship belonging to one person. Authorized users create it with a name and stable configuration identity and can describe the category for staff.

It can also carry default mappings for organization-supplied contact points. These describe which organization address or phone purpose can supply which destination purpose on a contact. The workspace can select one affiliation type as its default for the Organization command.

## Why It Exists

Our reading is that organizational connections need a vocabulary beyond the organization's name. A person can have an employment tie and another kind of relationship to the same body, and staff need to tell those meanings apart. Shared definitions also let administrators offer consistent contact-point mappings without every coordinator reconstructing the same source choices during entry.

## What It Is Not

An Affiliation Type is not an Affiliation: the type describes the category while the actual tie identifies the person, organization, and period. It is not an Organization Type or Contact Role, which classify the body and its responsibilities respectively. Default mappings are suggestions applied at defined moments, not a command to replace every existing person's information when configuration changes.

## What It Controls

Choosing a type gives the affiliation its relationship meaning and participates in same-type overlap validation. Default mappings can fill empty contact address or phone destinations through an effective tie, using the coordinator's supported selection. An explicit empty choice differs from omitting a choice. Future affiliations defer application until eligible start processing.

The workspace default supplies the type used when the Organization command creates a tie without an explicit type. Deactivation preserves existing affiliations and dataset columns while retiring new choices. References and default selection can prevent deletion. Editing mappings does not retroactively rewrite saved contacts, so catalogue maintenance and existing data repair remain separate work.

## How It Is Used

The CRM Administrator configures the workspace's relationship vocabulary and default sharing choices. The CRM Editor selects a type when adding a tie and reviews the applicable Link When Added options. Configure an affiliation type and use it on a contact connects administration with later person work. Staff retire a used category through deactivation rather than assuming it can be deleted while relationships still name it.

## Example

An association defines Member Firm with organization billing address mapped to contact work address. A coordinator adds Ruth's current relationship to Harbor Engineering using that type and selects the mapping. Her empty work-address destination can receive a link. A later mapping change applies to eligible later actions, not a silent rewrite of Ruth's existing choice. Retiring Member Firm keeps her affiliation intelligible.

## How It Relates

Affiliation Type classifies Affiliation and can use address and phone type facets from Contact Details and Communication Preferences in its mappings. CRM Settings holds the selected workspace default. Contact and Organization supply the connected identities. Contact Role is a separate catalogue consumed by responsibility assignment and compliance. Platform datasets expose the relationship columns; their query behavior remains Platform's responsibility.
