---
id: WF-CRM-ENT-003
title: Create and save an organization
trigger: >-
  A user creates an organization through its configured form, or an integration
  creates, replaces or patches an organization.
product_area: crm
module: entities
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
inference: []
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
implements:
  - CAP-CRM-ENT-002
  - CAP-CRM-ENT-004
  - CAP-CRM-ENT-005
  - CAP-CRM-ENT-006
  - CAP-CRM-ENT-008
  - CAP-CRM-ENT-012
moves:
  - STATE-CRM-ENT-002
  - STATE-CRM-ENT-003
  - STATE-CRM-ENT-006
crosses:
  - SVC-CRM-ENT-entities
idempotency: >-
  Versioned saves preserve stamps and capture when no content changes.
  Organization creation does not recognize retries: an occupied supplied
  identity conflicts, while a request without that identity can create a second
  organization.
concurrency: >-
  Organization updates check version under the owner row lock. Compliance
  additionally uses its organization advisory lock. Hierarchy enforcement
  rejects the competing parent move that would close a cycle.
hops:
  - 'n': 1
    actor_or_service: Console create screen
    action: >-
      Choose the organization type and fill its configured create form. The live
      console has no general editor for existing organizations.
    anchor: >-
      react-console:app/(console)/console/[tenantId]/crm/organizations/new/page.tsx:NewOrganizationPage
    seam: sync
    on_failure: Show returned field errors in the create form.
    left_inconsistent: No partial business write remains.
    observer: The creator receives form feedback.
    provenance: observed
    source: >-
      react-console:components/organization-editor/organization-editor.tsx:OrganizationEditor
  - 'n': 2
    actor_or_service: Entities route and duplicate guard
    action: >-
      Authorize creation, reject submitted lock changes and evaluate blocking
      duplicate rules with any required override.
    anchor: >-
      services-crm:modules/entities/aws/api/organizations.routes.ts:organizationsRoutes
    seam: sync
    on_failure: >-
      Refuse blocked duplicates unless the caller has override permission and
      supplies a reason.
    left_inconsistent: No partial business write remains.
    observer: The caller receives the result.
    provenance: unknown
    source: FND-CRM-ENT-029
  - 'n': 3
    actor_or_service: Shared record-asset pipeline
    action: Prepare the permanent logo copy before the record transaction.
    anchor: >-
      services-crm:modules/entities/services/assets/entity-assets.ts:entityAssets
    seam: sync
    on_failure: Return an asset error without replacing the saved logo.
    left_inconsistent: Prepared copies from an unsuccessful attempt await cleanup.
    observer: Asset cleanup logs expose deferred removal.
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/organizations.integration.spec.js#refuses
      the update as ObjectInvalid, naming the logo field
  - 'n': 4
    actor_or_service: Entities persistence (one transaction)
    action: >-
      On creation, normalize and validate the aggregate, allocate its number,
      evaluate initial compliance, persist violations and enabled attention,
      then save assets, children, custom values and override audit. Updates lock
      and version-check the owner but do not recalculate compliance.
    anchor: >-
      services-crm:modules/entities/services/persistence/organizations.ts:createOrganization
    seam: sync
    on_failure: Roll back the organization aggregate if any transactional step fails.
    left_inconsistent: A creation number can be spent and prepared files can remain.
    observer: The caller receives the result.
    provenance: test
    source: >-
      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)
  - 'n': 5
    actor_or_service: Database triggers
    action: >-
      Disconnect contacts whose linked organization source is removed, then
      capture the organization revision and event plus revisions and events for
      changed contacts.
    anchor: >-
      services-crm:db/migrations/20261019210829_disconnected_contact_information.sql
    seam: sync
    on_failure: A trigger or capture failure rolls back removal and all resulting changes.
    left_inconsistent: No partial business write remains.
    observer: Organization and affected contact histories show committed work.
    provenance: test
    source: >-
      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)
  - 'n': 6
    actor_or_service: CRM change-event consumers
    action: >-
      Process organization region, linked-contact region, search and duplicate
      reactions after commit.
    anchor: 'services-crm:aws/crm.change-events.ts:crmChangeEventStreams'
    seam: queue
    on_failure: Retry failed SQS handlers and dead-letter after five receives.
    left_inconsistent: The saved organization can precede region and search updates.
    observer: Consumer logs and dead-letter messages identify failures.
    provenance: documented
    source: 'services-crm:docs/change-events.md#The handlers CRM registers'
branches:
  - >-
    Type changes and policy edits await a later compliance trigger or requested
    reprocess.
  - >-
    A parent move changes returned ancestry for descendants because reads derive
    the chain.
waits: []
evidence:
  - >-
    services-crm at 7ea48a41: modules/entities/aws/api/organizations.routes.ts;
    services/persistence/organizations.ts (createOrganization,
    updateOrganization);
    services/organizations/compliance/organization-compliance.ts
    (evaluateNewOrganizationCompliance);
    services/persistence/organization-attention-items.ts;
    docs/disconnected-information.md.
  - >-
    react-console at da630758:
    app/(console)/console/[tenantId]/crm/organizations/new/page.tsx;
    components/organization-editor/organization-editor.tsx.
controls:
  - TC-AZ-001
  - TC-AZ-002
  - TC-AZ-003
  - TC-AZ-004
  - TC-AZ-006
  - TC-DAT-001
  - TC-DAT-002
  - TC-DAT-003
  - TC-IAM-001
  - TC-IAM-005
  - TC-IAM-006
  - TC-LOG-004
  - TC-LOG-005
  - TC-PI-001
  - TC-PI-002
  - TC-PI-003
  - TC-PI-005
  - TC-PRV-001
  - TC-PRV-002
  - TC-TEN-001
  - TC-TEN-002
  - TC-TEN-003
narrative:
  status: verified
  review: NARRATIVE-REVIEW-2026-10-23
narrative_sources:
  - section: What It Does
    provenance: observed
    source: >-
      react-console:app/(console)/console/[tenantId]/crm/organizations/new/page.tsx:NewOrganizationPage
  - section: What It Does
    provenance: observed
    source: >-
      services-crm:modules/entities/aws/api/organizations.routes.ts:organizationsRoutes
  - section: What It Does
    provenance: observed
    source: >-
      services-crm:modules/entities/services/assets/entity-assets.ts:entityAssets
  - section: What It Does
    provenance: observed
    source: >-
      services-crm:modules/entities/services/persistence/organizations.ts:createOrganization
  - section: What It Does
    provenance: observed
    source: >-
      services-crm:db/migrations/20261019210829_disconnected_contact_information.sql
  - section: What It Does
    provenance: documented
    source: 'services-crm:aws/crm.change-events.ts:crmChangeEventStreams'
  - section: What Starts It
    provenance: observed
    source: >-
      react-console:app/(console)/console/[tenantId]/crm/organizations/new/page.tsx:NewOrganizationPage
  - section: What Starts It
    provenance: observed
    source: >-
      services-crm:modules/entities/aws/api/organizations.routes.ts:organizationsRoutes
  - section: What Starts It
    provenance: observed
    source: >-
      services-crm:modules/entities/services/assets/entity-assets.ts:entityAssets
  - section: What Starts It
    provenance: observed
    source: >-
      services-crm:modules/entities/services/persistence/organizations.ts:createOrganization
  - section: What Starts It
    provenance: observed
    source: >-
      services-crm:db/migrations/20261019210829_disconnected_contact_information.sql
  - section: What Starts It
    provenance: documented
    source: 'services-crm:aws/crm.change-events.ts:crmChangeEventStreams'
  - section: The Flow
    provenance: observed
    source: >-
      react-console:app/(console)/console/[tenantId]/crm/organizations/new/page.tsx:NewOrganizationPage
  - section: The Flow
    provenance: observed
    source: >-
      services-crm:modules/entities/aws/api/organizations.routes.ts:organizationsRoutes
  - section: The Flow
    provenance: observed
    source: >-
      services-crm:modules/entities/services/assets/entity-assets.ts:entityAssets
  - section: The Flow
    provenance: observed
    source: >-
      services-crm:modules/entities/services/persistence/organizations.ts:createOrganization
  - section: The Flow
    provenance: observed
    source: >-
      services-crm:db/migrations/20261019210829_disconnected_contact_information.sql
  - section: The Flow
    provenance: documented
    source: 'services-crm:aws/crm.change-events.ts:crmChangeEventStreams'
  - section: Hop by Hop
    provenance: observed
    source: >-
      react-console:app/(console)/console/[tenantId]/crm/organizations/new/page.tsx:NewOrganizationPage
  - section: Hop by Hop
    provenance: observed
    source: >-
      services-crm:modules/entities/aws/api/organizations.routes.ts:organizationsRoutes
  - section: Hop by Hop
    provenance: observed
    source: >-
      services-crm:modules/entities/services/assets/entity-assets.ts:entityAssets
  - section: Hop by Hop
    provenance: observed
    source: >-
      services-crm:modules/entities/services/persistence/organizations.ts:createOrganization
  - section: Hop by Hop
    provenance: observed
    source: >-
      services-crm:db/migrations/20261019210829_disconnected_contact_information.sql
  - section: Hop by Hop
    provenance: documented
    source: 'services-crm:aws/crm.change-events.ts:crmChangeEventStreams'
  - section: When Something Fails
    provenance: observed
    source: >-
      react-console:app/(console)/console/[tenantId]/crm/organizations/new/page.tsx:NewOrganizationPage
  - section: When Something Fails
    provenance: observed
    source: >-
      services-crm:modules/entities/aws/api/organizations.routes.ts:organizationsRoutes
  - section: When Something Fails
    provenance: observed
    source: >-
      services-crm:modules/entities/services/assets/entity-assets.ts:entityAssets
  - section: When Something Fails
    provenance: observed
    source: >-
      services-crm:modules/entities/services/persistence/organizations.ts:createOrganization
  - section: When Something Fails
    provenance: observed
    source: >-
      services-crm:db/migrations/20261019210829_disconnected_contact_information.sql
  - section: When Something Fails
    provenance: documented
    source: 'services-crm:aws/crm.change-events.ts:crmChangeEventStreams'
  - section: Repeats and Races
    provenance: observed
    source: >-
      react-console:app/(console)/console/[tenantId]/crm/organizations/new/page.tsx:NewOrganizationPage
  - section: Repeats and Races
    provenance: observed
    source: >-
      services-crm:modules/entities/aws/api/organizations.routes.ts:organizationsRoutes
  - section: Repeats and Races
    provenance: observed
    source: >-
      services-crm:modules/entities/services/assets/entity-assets.ts:entityAssets
  - section: Repeats and Races
    provenance: observed
    source: >-
      services-crm:modules/entities/services/persistence/organizations.ts:createOrganization
  - section: Repeats and Races
    provenance: observed
    source: >-
      services-crm:db/migrations/20261019210829_disconnected_contact_information.sql
  - section: Repeats and Races
    provenance: documented
    source: 'services-crm:aws/crm.change-events.ts:crmChangeEventStreams'
  - section: What Is Observable
    provenance: observed
    source: >-
      react-console:app/(console)/console/[tenantId]/crm/organizations/new/page.tsx:NewOrganizationPage
  - section: What Is Observable
    provenance: observed
    source: >-
      services-crm:modules/entities/aws/api/organizations.routes.ts:organizationsRoutes
  - section: What Is Observable
    provenance: observed
    source: >-
      services-crm:modules/entities/services/assets/entity-assets.ts:entityAssets
  - section: What Is Observable
    provenance: observed
    source: >-
      services-crm:modules/entities/services/persistence/organizations.ts:createOrganization
  - section: What Is Observable
    provenance: observed
    source: >-
      services-crm:db/migrations/20261019210829_disconnected_contact_information.sql
  - section: What Is Observable
    provenance: documented
    source: 'services-crm:aws/crm.change-events.ts:crmChangeEventStreams'
  - section: What It Does
    provenance: inferred
    source: >-
      react-console:app/(console)/console/[tenantId]/crm/organizations/new/page.tsx:NewOrganizationPage;
      services-crm:modules/entities/aws/api/organizations.routes.ts:organizationsRoutes;
      services-crm:modules/entities/services/assets/entity-assets.ts:entityAssets;
      services-crm:modules/entities/services/persistence/organizations.ts:createOrganization;
      services-crm:db/migrations/20261019210829_disconnected_contact_information.sql;
      services-crm:aws/crm.change-events.ts:crmChangeEventStreams
    note: >-
      The explicitly labeled rationale interprets the existing transaction,
      transport and recovery boundaries; it is not a product decision.
  - section: Open Design Questions
    provenance: documented
    source: 'rhythm-v2-docs:specs/crm/entities/FINDINGS.md#Findings'
---

# Create and save an organization

## What It Does

Organization creation or correction joins identity, organizational context, contact points, files and customer-defined values at one local persistence boundary. Harbor West Chapter can be created beneath its national body with its own address and type policy. Staff receive one saved organization, while dependent projections can follow later.

Our reading is that this flow’s distinct boundaries explain two otherwise surprising results: a create can spend a number without committing, and removing a shared organizational source can also change several people. The built console supplies organization creation but no general organization editor; service save behavior remains relevant to integrations and selected record actions.

## What Starts It

A coordinator creates an organization from the configured form, or an integration submits creation or maintenance input. The request includes its classification and any parent reference. Existing general organization maintenance is available through the service even where the live console has no general editor. Parent moves and detail changes start a guarded organization transaction.

## The Flow

The diagram shows the sequence and service seams. Each synchronous stage waits for its result, while captured-event, queue and scheduled stages can run after the originating request has returned. The hop descriptions below identify the write and failure boundary, so the last arrow must not be mistaken for one atomic transaction across every participant.

```mermaid
sequenceDiagram
  participant U as Caller
  participant C as Organization workflow
  participant D as CRM database
  participant Q as Change queue
  U->>C: create or versioned organization save
  C->>D: write aggregate and source disconnections
  D-->>C: commit owner and affected contact capture
  C-->>U: organization result
  C->>Q: queue region, search and duplicate reactions
```

## Hop by Hop

### 1. Console create screen

The create screen offers active organization types and loads the effective create form for the chosen type. It collects the parent and contact-point values allowed by that form. Existing Organization 360 actions do not amount to a general organization correction form.

### 2. Entities route and duplicate guard

The route establishes create authority, rejects attempted ordinary lock changes and asks the duplicate guard about blocking conditions. Possible-duplicate feedback precedes any business insert. A deliberate override has its own grant and recorded reason rather than relying on the existence of an enabled Create button.

### 3. Shared record-asset pipeline

The logo is copied into permanent file handling before the organization transaction. This prepares a stable reference but introduces a recovery seam: database rollback cannot undo that copy. If later validation fails, cleanup has to retire the abandoned file separately.

### 4. Entities persistence (one transaction)

Persistence validates and normalizes the organization, including parent, type and typed contact points. Creation evaluates initial role compliance without a transition notice; later ordinary saves do not automatically reevaluate policy. Owner, children, custom values and permitted audit work commit together, with updates checking the locked owner version.

### 5. Database triggers

Removing an organization source invokes database disconnection work for each dependent contact. Those snapshots and their version advances commit with the source removal. The database then captures organization history and the affected contacts’ own histories, so a failure cannot leave dangling live source references.

### 6. CRM change-event consumers

Consumers update region, linked-person region, search and duplicate projections after commit. Each handler has its own delivery and retry outcome. A saved organization therefore remains valid even if search is briefly stale or a dependent region update must be retried.

## When Something Fails

### Hop 1 — Console create screen

The failure boundary is: Show returned field errors in the create form. The remaining work or data is: No partial business write remains. The creator receives form feedback.

### Hop 2 — Entities route and duplicate guard

The failure boundary is: Refuse blocked duplicates unless the caller has override permission and supplies a reason. The remaining work or data is: No partial business write remains. The caller receives the result.

### Hop 3 — Shared record-asset pipeline

The failure boundary is: Return an asset error without replacing the saved logo. The remaining work or data is: Prepared copies from an unsuccessful attempt await cleanup. Asset cleanup logs expose deferred removal.

### Hop 4 — Entities persistence (one transaction)

The failure boundary is: Roll back the organization aggregate if any transactional step fails. The remaining work or data is: A creation number can be spent and prepared files can remain. The caller receives the result.

### Hop 5 — Database triggers

The failure boundary is: A trigger or capture failure rolls back removal and all resulting changes. The remaining work or data is: No partial business write remains. Organization and affected contact histories show committed work.

### Hop 6 — CRM change-event consumers

The failure boundary is: Retry failed SQS handlers and dead-letter after five receives. The remaining work or data is: The saved organization can precede region and search updates. Consumer logs and dead-letter messages identify failures. This failure is downstream of committed input; operators investigate its own delivery rather than treating the caller’s original save as failed.

## Repeats and Races

A current-version organization no-op leaves modification stamps and capture unchanged. Organization create differs from contact replay: an occupied supplied identity is refused, and an unkeyed repeated request can create another organization. A caller investigating an ambiguous response must read the intended result rather than assume replay support.

Owner locking and version checks coordinate corrections, while compliance also uses an advisory lock. Concurrent parent changes still pass the hierarchy’s cycle enforcement. A parent move alters read-derived descendant ancestry without writing every descendant, and initial counter allocation can remain spent after a failed create.

## What Is Observable

The creator receives form feedback. The caller receives the result. Asset cleanup logs expose deferred removal. Organization and affected contact histories show committed work. Consumer logs and dead-letter messages identify failures.

The person or initiating caller sees the synchronous result at its own boundary. Captured history establishes committed local changes; logs, durable job state and queue outcomes explain remaining work. A successful database change, accepted downstream request and completed delivery are different observations and must be investigated through the component that owns each one.

## Open Design Questions

The built organization page lacks a general edit form and a revision-history entry, although service operations exist. Type changes do not immediately refresh stored compliance. These findings remain visible rather than implying that every service capability has a complete console path or immediate policy propagation.
