---
id: CAP-PFM-OPS-008
title: Remember a person’s visits to records
feature: FEAT-PFM-OPS-02
product_area: platform
module: operations
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: observed
supersedes:
  - CAP-CRM-AUD-006
  - CAP-CRM-EXP-017
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-platform:docs/audit-events.md#2026-10-12 — Final acceptance (accepted by Andrew)'
  - 'services-platform:docs/audit-events.md#What an event means, and what it does not (Andrew, 2026-10-10)'
purpose: Retain attributed visits so people can return to their recent record work.
outcomes:
  - Accepted delivery repeats retain one visit event.
  - The visit can feed personal recent-work queries without exposing reporting errors.
concepts:
  - CON-PFM-OPS-002
actors:
  - ACT-PFM-002
surfaces:
  - console
  - api
preconditions:
  - 'The caller holds view-reporting authority, which built-in reading roles include.'
postconditions:
  - Accepted repeat deliveries share one event and rejected reports leave no new event.
permissions:
  - key: platform.record-views.create
    where_checked: api
    scope: tenant
  - key: crm.contacts.read
    where_checked: service
    scope: tenant
  - key: crm.organizations.read
    where_checked: service
    scope: tenant
  - key: committees.committees.read
    where_checked: service
    scope: tenant
rules:
  - id: BR-CAP-PFM-OPS-008-01
    proved_by: [ATS-PFM-OPS-008-01, TST-PFM-OPS-060]
    statement: >-
      The view-reporting registry accepts contacts, organizations and committees and rejects unsupported
      record types with the supported vocabulary.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/services/record-views/record-view.integration.spec.js#refuses
      a record type the registry does not hold, naming the types it does
    origin: 'services-platform:docs/audit-events.md#Which record types report views'
  - id: BR-CAP-PFM-OPS-008-02
    proved_by: [ATS-PFM-OPS-008-02, TST-PFM-OPS-060]
    statement: >-
      View reporting resolves the target under the caller’s record-read and dataset-query authority before
      writing; refused or missing targets create no event.
    kind: permission
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/services/record-views/record-view.integration.spec.js#writes
      nothing at all for any of those refusals
    origin: 'services-platform:docs/audit-events.md#2026-10-12 — Final acceptance'
    examples:
      positive: A request satisfying the stated conditions receives the permitted result.
      negative: A request outside the allowed conditions cannot establish the claimed result.
  - id: BR-CAP-PFM-OPS-008-03
    proved_by: [ATS-PFM-OPS-008-03, TST-PFM-OPS-060]
    statement: Organization views require organization readership rather than contact readership.
    kind: permission
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/services/record-views/record-view.integration.spec.js#refuses
      an organization when the caller holds only contact read, because opening one needs organization read
    origin: none
  - id: BR-CAP-PFM-OPS-008-04
    proved_by: [ATS-PFM-OPS-008-04, ATS-PFM-OPS-008-13, ATS-PFM-OPS-010-10, TST-PFM-OPS-062]
    statement: >-
      The server derives attribution and event identity from verified execution context and refuses request
      bodies that supply those values or undeclared fields.
    kind: validation
    enforced_at:
      - api
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/audit/tests/unit/aws/api/audit-api-mapper.spec.ts#refuses a body naming the
      actor, which is how a trail would be written in somebody else’s name
    origin: none
  - id: BR-CAP-PFM-OPS-008-05
    proved_by: [ATS-PFM-OPS-008-05, ATS-PFM-OPS-008-12, TST-PFM-OPS-060]
    statement: >-
      A repeat visit key for the same record returns the original event as a duplicate, while reuse for
      another record is refused and a fresh visit needs a fresh key.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/services/record-views/record-view.integration.spec.js#reports
      the retry as a duplicate rather than refusing it, because the client has nothing to fix
    origin: none
  - id: BR-CAP-PFM-OPS-008-06
    proved_by: [ATS-PFM-OPS-008-06, TST-PFM-OPS-058]
    statement: >-
      Visit keys use UUID form, are normalized to lower case and are unique within a person’s workspace
      history.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/services/persistence/audit-events.integration.spec.js#lower-cases
      the occurrence key, so one visit cannot become two by changing its case
    origin: none
  - id: BR-CAP-PFM-OPS-008-07
    proved_by: [ATS-PFM-OPS-008-07, TST-PFM-OPS-058]
    statement: >-
      View metadata admits only short application and screen tokens and stores no record name, image or
      content.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/services/persistence/audit-events.integration.spec.js#refuses
      a metadata value outside the token grammar, naming the key
    origin: none
  - id: BR-CAP-PFM-OPS-008-08
    proved_by: [ATS-PFM-OPS-008-08, TST-PFM-OPS-048]
    statement: >-
      Console tracking records one visit after a successfully displayed record navigation; tab changes retain
      that visit, returning or reloading creates another, and unresolved screens report nothing.
    kind: policy
    enforced_at:
      - console
    must_prove: true
    provenance: test
    source: >-
      react-console:e2e/controlled/record-views.controlled.spec.ts#switching tab on the same record is still
      one visit
    origin: none
  - id: BR-CAP-PFM-OPS-008-09
    proved_by: [ATS-PFM-OPS-008-09, TST-PFM-OPS-048]
    statement: >-
      View-reporting errors remain invisible to the person; retryable transport, throttle and server failures
      reuse the visit key, while client refusals are not retried.
    kind: policy
    enforced_at:
      - console
    must_prove: true
    provenance: test
    source: >-
      react-console:e2e/controlled/record-views.controlled.spec.ts#a failure to report a visit never reaches
      the person, and the retries are one visit
    origin: none
  - id: BR-CAP-PFM-OPS-008-10
    proved_by: [ATS-PFM-OPS-008-10]
    statement: >-
      The custom-record read endpoint records a server-attributed view with a newly generated key rather than
      relying on console reporting.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-platform:modules/custom-objects/aws/api/custom-object-records.routes.ts:recordViewEvent'
    origin: none
scenarios:
  - id: SC-CAP-PFM-OPS-008-01
    given: A reader navigates to a successfully loaded contact.
    when: Contact 360 reports the navigation visit.
    then: The visit is retained and can feed the person’s Recent Items query.
    provenance: inferred
    source: >-
      react-console:e2e/controlled/record-views.controlled.spec.ts#reports one visit per navigation, and the
      header lists the records newest first
    note: >-
      This illustrative example combines behavior established by the cited test and the capability’s
      implementation evidence; it is not an independently bound end-to-end proof.
  - id: SC-CAP-PFM-OPS-008-02
    given: The caller can read contacts but lacks organization readership.
    when: The caller reports a visit to an organization.
    then: The reporting operation refuses it and leaves the trail unchanged.
    provenance: inferred
    source: >-
      services-platform:modules/audit/tests/integration/services/record-views/record-view.integration.spec.js#refuses
      an organization when the caller holds only contact read, because opening one needs organization read
    note: >-
      This illustrative example combines behavior established by the cited test and the capability’s
      implementation evidence; it is not an independently bound end-to-end proof.
edge_cases:
  - id: SC-CAP-PFM-OPS-008-03
    given: A visit key already identifies another record.
    when: A client reuses that key for a new record report.
    then: The server asks for a fresh visit key and preserves the original event.
    provenance: inferred
    source: >-
      services-platform:modules/audit/tests/integration/services/record-views/record-view.integration.spec.js#tells
      the client to mint a new key for each visit
    note: >-
      This illustrative example combines behavior established by the cited test and the capability’s
      implementation evidence; it is not an independently bound end-to-end proof.
constraints:
  - 'Accepted first and repeated reports both return success, distinguished by the duplicate indicator.'
depends_on: []
related:
  - CAP-PFM-OPS-009
  - CAP-PFM-OPS-010
journeys:
  - JNY-PFM-OPS-003
open_questions: []
evidence:
  - >-
    services-platform at ad3f6a6: modules/audit/aws/api/audit.routes.ts (recordRecordView),
    record-views.schemas.ts, registry/viewable-record-types.ts, services/record-views/record-view.ts,
    target-records.ts, services/persistence/audit-events.ts; record-view, audit-events integration suites;
    audit-api-mapper, audit-evaluated-keys and registry unit suites; record-views e2e; docs/audit-events.md;
    modules/custom-objects/aws/api/custom-object-records.routes.ts.
  - >-
    react-console at baf6dae: lib/record-views/tracking/*, lib/api/audit.ts,
    components/console/contact/contact-360-live.tsx,
    components/console/organization/organization-360-live.tsx; e2e/controlled/record-views.controlled.spec.ts,
    e2e/dev/record-views.dev.spec.ts.
  - >-
    react-console at baf6dae: acceptance/crm/inventory/capabilities.json CAP-CRM-AUD-006 and CAP-CRM-EXP-017,
    business-rules.json RUL-CRM-AUD-014, RUL-CRM-EXP-041, RUL-CRM-EXP-042 (verified; the Recently Viewed lists
    of CAP-CRM-EXP-017 are the query layer's).
  - 'Operations this capability covers: recordRecordView. Permission keys: platform.record-views.create.'
  - >-
    Examination: proposed acceptance matrices retain candidate test references; incomplete proof is recorded
    by FND-PFM-OPS-028.
controls: [TC-AVL-004, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-PI-001, TC-PI-002, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-VEN-001, TC-VEN-002, TC-VEN-003]
exercised_by: [FAS-PFM-OPS-02-01, FAS-PFM-OPS-02-02, FAS-PFM-OPS-02-03, FAS-PFM-OPS-02-04, FAS-PFM-OPS-02-05]
suites: [TST-PFM-OPS-048, TST-PFM-OPS-058, TST-PFM-OPS-060, TST-PFM-OPS-062]
---

# Remember a person’s visits to records

## Purpose

Retain attributed visits so people can return to their recent record work.

## How It Works

A successfully displayed record screen begins a visit and reports it using a unique visit key. The backend resolves the record through the caller’s authorized query context before accepting the report. Attribution comes from the verified request rather than fields supplied by the browser. A delivery retry uses the same key, and the response indicates when that event already exists. Leaving the screen and returning starts another visit. Tab changes on the same screen retain the current visit. Reporting failures do not interrupt the person’s record work. Custom records use a separate server-side read hook whose visits are created even without a console report.

## Relationships

Record owners establish the activity, and authorized queries use visits to support recent work without replacing raw history.
