---
id: CON-CRM-ENT-002
name: Organization
feature: FEAT-CRM-ENT-02
product_area: crm
module: entities
revision: 1
status: approved
product_intent: proposed
implementation_evidence: observed
source_sha:
  services-crm: 7ea48a414c42eade10d60753b8bcc323b1dd726f
  react-console: da63075876b2c5f4a6af07d457bb24ea8bf4c0c7
definition: An organization records a named body independently of the people who represent it.
owned_by: MOD-CRM-ENT
acted_on_by:
  - CAP-CRM-ENT-002
  - CAP-CRM-ENT-003
  - CAP-CRM-ENT-004
  - CAP-CRM-ENT-005
  - CAP-CRM-ENT-006
  - CAP-CRM-ENT-008
  - CAP-CRM-ENT-009
  - CAP-CRM-ENT-011
  - CAP-CRM-ENT-012
  - CAP-CRM-ENT-013
  - CAP-CRM-ENT-014
  - CAP-CRM-ENT-015
  - CAP-CRM-ENT-016
visible_to:
  - ACT-CRM-001
  - ACT-CRM-002
  - ACT-CRM-003
  - ACT-CRM-004
  - ACT-CRM-005
relationships:
  - to: CON-CRM-ENT-003
    statement: Affiliations connect people and organizations.
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-organization-affiliations.table.ts'
  - to: CON-CRM-ENT-033
    statement: Record safeguards preserve and explain changes to the owning record.
    provenance: observed
    source: 'services-crm:modules/entities/services/persistence/record-locks.ts'
facets:
  - name: Classification and role rules
    meaning: Organization types can restrict roles and define role-holder bounds.
    used_for: Classifying bodies and defining role coverage expectations.
  - name: Hierarchy and people
    meaning: Parentage and effective affiliations provide structural and human context.
    used_for: Finding people within a body or its descendants.
  - name: Addresses and phones
    meaning: Typed organizational details may supply linked contact information.
    used_for: Keeping shared contact points current.
  - name: Custom values and recognition
    meaning: 'Custom values, logo, number and online details describe the body.'
    used_for: Recognizing and maintaining organizational information.
  - name: Compliance state
    meaning: Computed status and violations describe configured role coverage.
    used_for: Reviewing organizations needing attention.
  - name: Reprocess runs
    meaning: Resumable runs recalculate role coverage for existing organizations.
    used_for: Refreshing stored compliance after configuration changes.
  - name: Notices
    meaning: Template and recipient-role choices support compliance notifications.
    used_for: Previewing and delivering notices on noncompliance transitions.
  - name: Status and archive
    meaning: Standing and archive are separate facets of organizational lifecycle.
    used_for: Setting aside a body without losing its relationships.
evidence:
  - >-
    services-crm at 7ea48a414c42eade10d60753b8bcc323b1dd726f: docs/entities-api.md;
    docs/affiliations/README.md; docs/archive.md; docs/record-locks.md; docs/record-history.md;
    docs/disconnected-information.md; docs/milestones/CRM-M1-contacts-and-organizations.md and
    CRM-M2-affiliations-roles-compliance.md; modules/entities/db/contacts.table.ts,
    organizations.table.ts, contact-organization-affiliations.table.ts;
    services/persistence/record-locks.ts; services/affiliations/affiliation-date-reconciliation.ts;
    services/organizations/compliance/contact-role-compliance.ts.
  - >-
    react-console at da63075876b2c5f4a6af07d457bb24ea8bf4c0c7:
    components/forms/contacts/affiliation-components.tsx;
    components/contact-editor/contact-editor.tsx;
    acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0201.spec.ts; existing capability and
    journey evidence bindings in this corpus.
approved_by: Andrew
approved_at: 2026-10-22T00:00:00.000Z
narrative:
  status: verified
  review: NARRATIVE-REVIEW-2026-10-23
narrative_sources:
  - section: Why It Exists
    provenance: inferred
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/organizations.field-rules.integration.spec.js#refuses
      a name of 201 characters, naming name;
      services-crm:modules/entities/tests/e2e/organizations.e2e.spec.js#keeps the id the body named,
      which is no UUIDv7;
      services-crm:modules/entities/tests/integration/services/persistence/organizations.integration.spec.js#renumbers
      after the tenant's highest organization number, never its contacts';
      services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#refuses an organization
      of a type the tenant does not have (409), and the delete of a type an organization has (409);
      services-crm:modules/entities/tests/e2e/organizations.e2e.spec.js#answers 409 for a parent the
      tenant does not have;
      services-crm:modules/entities/tests/integration/db/organizations.table.integration.spec.js#refuses
      the second move by organizations_parent_not_descendant once the first commits;
      services-crm:modules/entities/tests/integration/services/persistence/organizations.integration.spec.js#answers
      the descendant, never saved again, its ancestry under the new parent;
      services-crm:modules/entities/tests/integration/db/organizations.table.integration.spec.js#refuses
      a status outside the closed list by organizations_status_check;
      services-crm:modules/entities/tests/unit/db/organization-domain-name.spec.js#derives the
      domain name without the protocol or the www prefix;
      services-crm:modules/entities/tests/integration/services/persistence/organizations.integration.spec.js#refuses
      the update as ObjectInvalid, naming the logo field; FND-CRM-ENT-029;
      services-crm:docs/milestones/CRM-M1-contacts-and-organizations.plan.md#2. Scope;
      services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers 400 for a cursor it did
      not answer; services-crm:modules/entities/services/persistence/contacts.ts:listContactFields;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.queries.integration.spec.js#answers
      a contact whose affiliation to the organization is not its primary one;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.queries.integration.spec.js#includes
      a contact whose affiliation in effect to an organization of the walk is not its primary one
      (M2-D3); services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers only the
      contacts a picker may offer: not the inactive, not the deceased;
      services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers only the fields
      `?fields=` named, with the id;
      services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers the other tenant its own
      contact for that id, to a context that covers it;
      services-crm:modules/entities/aws/api/contacts.routes.ts:contactsRoutes;
      services-crm:modules/entities/tests/e2e/application-access-enforced.e2e.spec.js#is refused
      where the assignment DENIES crm.application.access, though the route itself is served;
      services-crm:modules/entities/tests/e2e/platform-scope.e2e.spec.js#refuses every one of them
      to a staff slice reaching every tenant, 403 at the tenant boundary;
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#CRM-ACC-0064;
      react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0064.spec.ts#CRM-ACC-0064;
      services-crm:modules/entities/tests/e2e/display-links.e2e.spec.js#answers a presigned GET that
      lasts 15 minutes and downloads as an attachment for a document custom field;
      services-crm:docs/milestones/CRM-M1-contacts-and-organizations.md#D-01;
      services-crm:modules/entities/services/quick-search/quick-search-indexer.ts:quickSearchIndexerOf;
      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:modules/entities/tests/integration/services/organizations/compliance/organization-compliance.integration.spec.js#with
      a type that holds it to no rule, or with no type: no status and no violations;
      services-crm:modules/entities/tests/integration/services/organizations/compliance/organization-compliance.integration.spec.js#with
      a type that holds it to rules: not in compliance, every rule with a minimum violated (it has
      no contact yet);
      services-crm:modules/entities/tests/integration/services/organizations/compliance/organization-compliance.integration.spec.js#brings
      an organization into compliance for a role held on a current affiliation that is not primary;
      services-crm:modules/entities/tests/integration/services/organizations/compliance/compliance-change-events.integration.spec.js#a
      contact saved with the notes DISABLE_CONTACT_ROLE_COMPLIANCE_CHECK: nothing (its delete still
      records);
      services-crm:modules/entities/tests/integration/services/organizations/compliance/organization-compliance.integration.spec.js#writes
      nothing for a duplicate delivery, or a second run on an unchanged organization;
      services-crm:modules/entities/tests/e2e/organization-compliance.e2e.spec.js#keep them against
      a PUT that sends others back;
      services-crm:modules/entities/tests/integration/services/organizations/compliance/organization-compliance.integration.spec.js#is
      not submitted again while the organization stays out of compliance, nor for a type that does
      not notify;
      services-crm:modules/entities/tests/integration/services/organizations/compliance/compliance-reads.integration.spec.js#leaves
      out the contact whose addresses sit past the primary slot, no secondary address standing in;
      services-crm:docs/entities-api.md#`compliance_notification_template`: the e-mail the type
      sends;
      services-crm:modules/entities/tests/integration/services/persistence/compliance-reprocesses.integration.spec.js#refuses
      a second running run for the tenant;
      services-crm:modules/entities/tests/e2e/email-previews.e2e.spec.js#answers 403 to a caller who
      may administer types but may not read the records the answer contains;
      services-crm:modules/entities/tests/e2e/organization-compliance.e2e.spec.js#answers 400 for a
      template both named and typed in, or neither;
      services-crm:modules/entities/services/persistence/organization-types.ts:organizationTypeProblems;
      services-crm:modules/entities/tests/integration/services/persistence/organization-attention-items.queries.integration.spec.js#answers
      the organizations out of compliance by id, and no other;
      services-crm:modules/entities/services/templates/compliance-notification-template.ts:effectiveTemplateChange;
      services-crm:modules/entities/services/persistence/organization-types.ts:updateOrganizationType;
      services-crm:modules/entities/tests/integration/services/templates/compliance-notification-template.integration.spec.js#keeps
      the delete and logs the type id when Platform cannot clear it: the type is gone either way;
      services-crm:modules/entities/tests/e2e/archive.e2e.spec.js#refuses a caller who may update
      contacts but not archive them, 403;
      services-crm:modules/entities/tests/e2e/archive.e2e.spec.js#refuses an archive with no
      version, 400, naming it;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.archive.integration.spec.js#keeps
      the status it had: archive is not a status;
      services-crm:modules/entities/tests/integration/services/persistence/organizations.archive.integration.spec.js#keeps
      the trimmed reason as unarchive_reason;
      services-crm:modules/entities/tests/e2e/archive.e2e.spec.js#refuses a replacement, 409
      RecordArchived; services-crm:modules/entities/tests/e2e/archive.e2e.spec.js#lists it for
      archived=only, and only it; services-crm:docs/archive.md#Lists and pickers;
      services-crm:modules/entities/tests/integration/services/persistence/organizations.archive.integration.spec.js#keeps
      the ended affiliation naming the organization's id, with its name and its archive instant;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.archive.integration.spec.js#is
      kept through a plain save, which never writes it;
      react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0046.spec.ts#CRM-ACC-0046;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#removes
      every owned element with it, the foreign keys cascading;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#bumps
      the version of that contact and stamps the system, not the caller that deleted (M1-D6);
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
      the delete as IllegalOperation, naming the element that is locked;
      services-crm:modules/entities/tests/e2e/organizations.e2e.spec.js#answers 409 to a delete
      while the contact is affiliated with it;
      services-crm:modules/entities/tests/integration/services/assets/retire-entity-assets.integration.spec.js#detaches
      the photo in the delete's own transaction;
      services-crm:modules/entities/tests/unit/services/persistence/endpoint-deletions.spec.js#never
      runs a statement when it is off;
      services-crm:docs/milestones/CRM-M1-contacts-and-organizations.md#CRM-Q10
    note: 'Business need inferred from the sourced record behavior, without inventing product history.'
  - section: What It Controls
    provenance: observed
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/organizations.field-rules.integration.spec.js#refuses
      a name of 201 characters, naming name;
      services-crm:modules/entities/tests/e2e/organizations.e2e.spec.js#keeps the id the body named,
      which is no UUIDv7;
      services-crm:modules/entities/tests/integration/services/persistence/organizations.integration.spec.js#renumbers
      after the tenant's highest organization number, never its contacts';
      services-crm:modules/entities/tests/e2e/organization-types.e2e.spec.js#refuses an organization
      of a type the tenant does not have (409), and the delete of a type an organization has (409);
      services-crm:modules/entities/tests/e2e/organizations.e2e.spec.js#answers 409 for a parent the
      tenant does not have;
      services-crm:modules/entities/tests/integration/db/organizations.table.integration.spec.js#refuses
      the second move by organizations_parent_not_descendant once the first commits;
      services-crm:modules/entities/tests/integration/services/persistence/organizations.integration.spec.js#answers
      the descendant, never saved again, its ancestry under the new parent;
      services-crm:modules/entities/tests/integration/db/organizations.table.integration.spec.js#refuses
      a status outside the closed list by organizations_status_check;
      services-crm:modules/entities/tests/unit/db/organization-domain-name.spec.js#derives the
      domain name without the protocol or the www prefix;
      services-crm:modules/entities/tests/integration/services/persistence/organizations.integration.spec.js#refuses
      the update as ObjectInvalid, naming the logo field; FND-CRM-ENT-029;
      services-crm:docs/milestones/CRM-M1-contacts-and-organizations.plan.md#2. Scope;
      services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers 400 for a cursor it did
      not answer; services-crm:modules/entities/services/persistence/contacts.ts:listContactFields;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.queries.integration.spec.js#answers
      a contact whose affiliation to the organization is not its primary one;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.queries.integration.spec.js#includes
      a contact whose affiliation in effect to an organization of the walk is not its primary one
      (M2-D3); services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers only the
      contacts a picker may offer: not the inactive, not the deceased;
      services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers only the fields
      `?fields=` named, with the id;
      services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers the other tenant its own
      contact for that id, to a context that covers it;
      services-crm:modules/entities/aws/api/contacts.routes.ts:contactsRoutes;
      services-crm:modules/entities/tests/e2e/application-access-enforced.e2e.spec.js#is refused
      where the assignment DENIES crm.application.access, though the route itself is served;
      services-crm:modules/entities/tests/e2e/platform-scope.e2e.spec.js#refuses every one of them
      to a staff slice reaching every tenant, 403 at the tenant boundary;
      services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#CRM-ACC-0064;
      react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0064.spec.ts#CRM-ACC-0064;
      services-crm:modules/entities/tests/e2e/display-links.e2e.spec.js#answers a presigned GET that
      lasts 15 minutes and downloads as an attachment for a document custom field;
      services-crm:docs/milestones/CRM-M1-contacts-and-organizations.md#D-01;
      services-crm:modules/entities/services/quick-search/quick-search-indexer.ts:quickSearchIndexerOf;
      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:modules/entities/tests/integration/services/organizations/compliance/organization-compliance.integration.spec.js#with
      a type that holds it to no rule, or with no type: no status and no violations;
      services-crm:modules/entities/tests/integration/services/organizations/compliance/organization-compliance.integration.spec.js#with
      a type that holds it to rules: not in compliance, every rule with a minimum violated (it has
      no contact yet);
      services-crm:modules/entities/tests/integration/services/organizations/compliance/organization-compliance.integration.spec.js#brings
      an organization into compliance for a role held on a current affiliation that is not primary;
      services-crm:modules/entities/tests/integration/services/organizations/compliance/compliance-change-events.integration.spec.js#a
      contact saved with the notes DISABLE_CONTACT_ROLE_COMPLIANCE_CHECK: nothing (its delete still
      records);
      services-crm:modules/entities/tests/integration/services/organizations/compliance/organization-compliance.integration.spec.js#writes
      nothing for a duplicate delivery, or a second run on an unchanged organization;
      services-crm:modules/entities/tests/e2e/organization-compliance.e2e.spec.js#keep them against
      a PUT that sends others back;
      services-crm:modules/entities/tests/integration/services/organizations/compliance/organization-compliance.integration.spec.js#is
      not submitted again while the organization stays out of compliance, nor for a type that does
      not notify;
      services-crm:modules/entities/tests/integration/services/organizations/compliance/compliance-reads.integration.spec.js#leaves
      out the contact whose addresses sit past the primary slot, no secondary address standing in;
      services-crm:docs/entities-api.md#`compliance_notification_template`: the e-mail the type
      sends;
      services-crm:modules/entities/tests/integration/services/persistence/compliance-reprocesses.integration.spec.js#refuses
      a second running run for the tenant;
      services-crm:modules/entities/tests/e2e/email-previews.e2e.spec.js#answers 403 to a caller who
      may administer types but may not read the records the answer contains;
      services-crm:modules/entities/tests/e2e/organization-compliance.e2e.spec.js#answers 400 for a
      template both named and typed in, or neither;
      services-crm:modules/entities/services/persistence/organization-types.ts:organizationTypeProblems;
      services-crm:modules/entities/tests/integration/services/persistence/organization-attention-items.queries.integration.spec.js#answers
      the organizations out of compliance by id, and no other;
      services-crm:modules/entities/services/templates/compliance-notification-template.ts:effectiveTemplateChange;
      services-crm:modules/entities/services/persistence/organization-types.ts:updateOrganizationType;
      services-crm:modules/entities/tests/integration/services/templates/compliance-notification-template.integration.spec.js#keeps
      the delete and logs the type id when Platform cannot clear it: the type is gone either way;
      services-crm:modules/entities/tests/e2e/archive.e2e.spec.js#refuses a caller who may update
      contacts but not archive them, 403;
      services-crm:modules/entities/tests/e2e/archive.e2e.spec.js#refuses an archive with no
      version, 400, naming it;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.archive.integration.spec.js#keeps
      the status it had: archive is not a status;
      services-crm:modules/entities/tests/integration/services/persistence/organizations.archive.integration.spec.js#keeps
      the trimmed reason as unarchive_reason;
      services-crm:modules/entities/tests/e2e/archive.e2e.spec.js#refuses a replacement, 409
      RecordArchived; services-crm:modules/entities/tests/e2e/archive.e2e.spec.js#lists it for
      archived=only, and only it; services-crm:docs/archive.md#Lists and pickers;
      services-crm:modules/entities/tests/integration/services/persistence/organizations.archive.integration.spec.js#keeps
      the ended affiliation naming the organization's id, with its name and its archive instant;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.archive.integration.spec.js#is
      kept through a plain save, which never writes it;
      react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0046.spec.ts#CRM-ACC-0046;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#removes
      every owned element with it, the foreign keys cascading;
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#bumps
      the version of that contact and stamps the system, not the caller that deleted (M1-D6);
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
      the delete as IllegalOperation, naming the element that is locked;
      services-crm:modules/entities/tests/e2e/organizations.e2e.spec.js#answers 409 to a delete
      while the contact is affiliated with it;
      services-crm:modules/entities/tests/integration/services/assets/retire-entity-assets.integration.spec.js#detaches
      the photo in the delete's own transaction;
      services-crm:modules/entities/tests/unit/services/persistence/endpoint-deletions.spec.js#never
      runs a statement when it is off;
      services-crm:docs/milestones/CRM-M1-contacts-and-organizations.md#CRM-Q10
---

# Organization

## What It Is

An Organization is the independent CRM identity of a body an association works with, such as a member firm, chapter, national association, or school. It is created from a name and organizational information and has its own workspace identity and organization number. Staff can describe the body even before any person is affiliated with it.

The organization owns its addresses, phones, and customer-defined values and can have a parent organization. Its current ancestry is derived from that parent chain. People relate to the organization through their own affiliations, so a departing representative does not take the organization's identity or information away.

## Why It Exists

Our reading is that association staff need to separate a body from its changing representatives. If the firm exists only as a field on one contact, replacing that contact can lose the shared organizational context. Independent organization records let multiple people reference the same body and let hierarchy and policy remain understandable when representatives join, leave, or return.

## What It Is Not

An Organization is not the workspace that owns the CRM data, and its place in an organizational hierarchy does not grant application access. It is not an affiliation or a roster row: those describe people's relationships to it. Organization status, archive, and role compliance answer different questions. An organization type named Member Firm does not itself establish membership rights or a financial account.

## What It Controls

Its parent determines the ancestry and descendant structure used by organizational reads and people discovery. Its contact points can provide sources for affiliated people's linked details. Organization Type classifies the body and can supply role restrictions, compliance expectations, and presentation assignments. The stored compliance result is calculated by the compliance capability rather than controlled by arbitrary client values.

Archive changes ordinary availability while retaining identity and relationships. Locks govern ordinary mutation and deletion, and dependencies can prevent hard deletion even when all affiliations are ended. Version and history preserve the explanation of committed changes. These distinctions let staff understand why a closed firm may remain readable while no longer being offered for new work.

## How It Is Used

The CRM Editor creates organizations, records parents, and connects people through contact-owned affiliations. The CRM Reader inspects organizational context and permitted people views. The CRM Administrator manages type policy, archive, and safeguards. Find current affiliates across an organization hierarchy explains when a national body and its chapters supply a wider current-person list.

Good use keeps the hierarchy structural and the affiliations human: moving a chapter's parent differs from moving a member's employer. Staff select an appropriate people date when investigating past representation. They inspect compliance as a stored assessment with defined reevaluation triggers, rather than assuming a recent type edit has already refreshed every organization.

## Example

Harbor National has a western chapter represented by two members. Staff create the chapter beneath the national organization and add each person's dated affiliation. One representative leaves, so their tie becomes past; the other continues. The chapter still has its independent name, contact points, ancestry, and identity.

If the national body reorganizes its councils, a parent move changes the chapter's derived ancestry on the next read without altering the representatives' affiliations. If the chapter later closes, archive can remove it from ordinary discovery while those people's historical connections remain interpretable. Deletion must still account for dependencies.

## How It Relates

Organization Type supplies the body's classification and role policy. Affiliations connect Contact identities to it, and Contact Roles on those ties supply coverage for Organization Compliance. Contact Details and Communication Preferences includes organization-owned sources as well as person-owned values. Record Protection and History supplies safeguards. Platform metadata and notifications own definitions, files, templates, and delivery; other applications own their program or financial records about the organization.
