Skip to content

CAP-CRM-ENT-003Locate people and organizations

Markdown

CAP-CRM-ENT-003

Locate people and organizations

Markdown
  • DocumentApproved
  • Product intentProposed
  • ImplementationObserved

KindCapabilityProduct areaCRMModuleEntitiesRevision1Approved byAndrewApproved at2026-10-22

Read atservices-crm 7ea48a41react-console da630758

The Formal Contract

The narrative above explains; the sections below are authoritative — the rules, scenarios and tests this document binds.

Outcomes
A list walks the tenant’s records a page at a time with a stable cursor.The people beneath an organization are the same set wherever they are listed.A record returns every stored detail, and another tenant’s record is never reachable.
Surfaces
consoleapi
Intent sources
services-crm:docs/milestones/CRM-M1-contacts-and-organizations.plan.md#3. How it works for the userservices-crm:docs/milestones/CRM-M1-contacts-and-organizations.md#M1-D1services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#M2-D3services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.md#CRM-ACC-0064services-crm:docs/milestones/CRM-M1-contacts-and-organizations.md#D-01

Controls

16 controls apply, from the corpus control catalogue.

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-LOG-004 TC-LOG-005 TC-PI-001 TC-PRV-001 TC-PRV-002 TC-TEN-001 TC-TEN-002 TC-TEN-003

Repositories Read

RepositoryPaths
undefined:modules/entities/
undefined:docs/milestones/CRM-M1-contacts-and-organizations.md
undefined:docs/milestones/CRM-M2-affiliations-roles-compliance.md
undefined:modules/entities/aws/api/contacts.routes.ts
undefined:modules/entities/services/persistence/contacts.ts
undefined:modules/entities/services/quick-search/quick-search-indexer.ts
undefined:modules/entities/tests/e2e/application-access-enforced.e2e.spec.js
undefined:modules/entities/tests/e2e/contacts.e2e.spec.js
undefined:modules/entities/tests/e2e/display-links.e2e.spec.js
undefined:modules/entities/tests/e2e/platform-scope.e2e.spec.js
undefined:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js
undefined:modules/entities/tests/integration/services/persistence/contacts.queries.integration.spec.js
undefined:app/(console)/console/[tenantId]/crm/contacts/
undefined:acceptance/crm/playwright/journeys/affiliations/CRM-ACC-0189.spec.ts
undefined:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0050.spec.ts
undefined:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0064.spec.ts

Where It Lives

Permissions

KeyChecked atScope
crm.contacts.readapitenant
crm.organizations.readapitenant
crm.contacts.queryservicetenant
crm.organizations.queryservicetenant
crm.application.accessservicetenant

Business Rules

15 rules, 9 to be proved before approval. A rule's product intent is derived from its origin: 6 confirmed by a product decision, the rest inheriting this capability's proposed.

IdRuleKindEnforced atProvenance
01must prove
Proposed
Contact and organization lists use number order, return the requested page size from 1 to 100 with 25 as the default, supply an opaque continuation cursor and reject unsupported parameters, invalid limits and unrecognized cursors.
✓ Accepted example: limit=2 over five contacts walks three pages, the last without a cursor.✗ Rejected example: limit=101 is rejected 400.
validationapi, servicetest
services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers 400 for a cursor it did not answer
02must prove
Proposed
Name search checks display and sort names without case sensitivity, treats % and _ as literal characters, accepts up to 100 characters and disregards blank input; nicknames can match within computed names, while e-mails and record numbers are excluded.
✓ Accepted example: "smith, j" finds "Smith, John".✗ Rejected example: a search of 101 characters is rejected.
validationapi, serviceobserved
03must prove
Confirmed
An organization filter returns each contact once when any affiliation to that organization is effective on the workspace’s current day, whether primary or secondary, and returns no contacts for an unknown organization.
policyservicetest
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
04must prove
Confirmed
The descendant listing returns each currently affiliated contact once across the organization and all descendants, without paging, in a depth-first traversal ordered by organization and contact names, skipping cycles and returning nothing for an unknown root.
calculationservice, databasetest
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)
05must prove
Confirmed
Selecting contacts through the list excludes contacts whose status is inactive or deceased.
policyapi, servicetest
services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers only the contacts a picker may offer: not the inactive, not the deceased
06
Proposed
A record read includes its collections and current creator and modifier display names, or only the requested projected fields together with its id.
policyapi, servicetest
services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers only the fields `?fields=` named, with the id
07must prove
Proposed
Routes operate within their path’s tenant, reject callers without tenant reach and conflicting tenant ids in the body, and treat matching ids in separate tenants as separate records.
permissionapi, service, databasetest
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
08must prove
Confirmed
After authorization, routes naming a merged-away record return 410 with its survivor id, while ids that never existed return 404.
origin: D-20 (cited in services-crm:modules/entities/aws/api/contacts.routes.ts)
policyapiobserved
09must prove
Proposed
CRM routes reject callers lacking application access even when they hold the record permissions.
permissionapitest
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
10must prove
Proposed
A Rhythm staff grant spanning every tenant does not cross a protected customer tenant boundary; customer access requires a customer-level grant.
permissionapitest
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
11
Confirmed
Contact and organization dataset queries require both the dataset query permission and the corresponding record read permission.
permissionservicedocumented
12
Proposed
Contact reads include affiliated organization names even when the caller lacks organization read permission.
policyservicetest
13
Proposed
Record and list reads add public image links for photos and logos and 15-minute attachment download links for documents; writes disregard returned display links.
calculationapi, servicetest
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
14
Confirmed
The product should display stored contact details through metadata-driven Contact 360 cards and identify communication restrictions and deceased status in the header.
policydocumented
15
Proposed
Quick Search processes committed record changes through change events, allowing a successful save to precede an updated search result.
policyserviceobserved

Scenarios

IdGivenWhenThenProvenance
01Vic works at Acme Chapter but his primary organization is his school.The staff member lists Acme National's people and filters the contacts list by Acme Chapter.Vic is in both returns.test
02A contact merged into another.The old id is read.410 with the survivor's id, and the console is saved on the survivor.test

Edge Cases

IdGivenWhenThenProvenance
03A parent cycle one, two, three, one.The people beneath two are listed.Each organization is walked once and nothing is missed.test

The Markdown files under specs/ in rhythmsoftware/rhythm-v2-docs are the canonical source of truth. This reader is a generated, read-only projection of them.