---
id: CAP-PFM-QRY-005
title: Provide a customer with a tailored dataset
feature: FEAT-PFM-QRY-01
product_area: platform
module: queries
revision: 1
status: draft
product_intent: proposed
implementation_evidence: observed
supersedes: []
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - >-
    services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
    (Phase 1 accepted by Andrew on 2026-10-12)
purpose: >-
  Give selected customers a versioned dataset that Rhythm staff author and maintain without changing each
  customer’s application.
outcomes:
  - Selected customers receive a tailored dataset.
  - Publication checks identify affected saved questions.
  - Reader permissions still govern custom data.
concepts:
  - CON-PFM-QRY-001
actors:
  - ACT-PFM-015
  - ACT-PFM-013
surfaces:
  - console
  - api
preconditions: []
postconditions: []
permissions:
  - key: hq.custom-query-tables.read
    where_checked: api
    scope: global
  - key: hq.custom-query-tables.author
    where_checked: api
    scope: global
  - key: hq.custom-query-tables.publish
    where_checked: api
    scope: global
  - key: hq.custom-query-tables.assign
    where_checked: api
    scope: global
rules:
  - id: BR-CAP-PFM-QRY-005-01
    proved_by: [ATS-PFM-QRY-005-01, TST-PFM-QRY-223]
    statement: >-
      Staff permissions separately govern custom-table reading, drafting and preview, publication and
      lifecycle changes, and assignment; customer users cannot reach these routes and authoring does not grant
      publication.
    kind: permission
    enforced_at:
      - api
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/e2e/custom-tables.e2e.spec.js#is refused to a caller who may
      author but not publish, the two being separate keys
    origin: >-
      services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
      (Phase 1 accepted by Andrew on 2026-10-12)
  - id: BR-CAP-PFM-QRY-005-02
    proved_by: [ATS-PFM-QRY-005-02, TST-PFM-QRY-223]
    statement: A custom-table identity cannot be reused for a second definition.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/e2e/custom-tables.e2e.spec.js#refuses a second definition under
      the same id, an id being permanent and never reused
    origin: >-
      services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
      (Phase 1 accepted by Andrew on 2026-10-12)
  - id: BR-CAP-PFM-QRY-005-03
    proved_by: [ATS-PFM-QRY-005-03, TST-PFM-QRY-223]
    statement: >-
      Draft analysis checks permitted read behaviour, identifies source access requirements and proposes
      output fields, returning rejected statements as analysis problems.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/e2e/custom-tables.e2e.spec.js#answers a statement the rules
      refuse as 200 and its problems, an analysis being data and not an error
    origin: >-
      services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
      (Phase 1 accepted by Andrew on 2026-10-12)
  - id: BR-CAP-PFM-QRY-005-04
    proved_by: [ATS-PFM-QRY-005-04, TST-PFM-QRY-223]
    statement: >-
      Custom definitions accept only supported platform placeholders and reject user-declared parameters or
      unknown placeholders.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/e2e/custom-tables.e2e.spec.js#refuses a draft that declares
      parameters as 400, a definition taking only the platform's own placeholders
    origin: >-
      services-platform:docs/queries/semantic-query-layer.md#A value asked for when a saved query runs
      (`$parameter`, 2026-10-13) (Andrew, 2026-10-13)
  - id: BR-CAP-PFM-QRY-005-05
    proved_by: [ATS-PFM-QRY-004-09, ATS-PFM-QRY-005-05, TST-PFM-QRY-223]
    statement: >-
      Analysis, draft validation, publication and preview use the configured validation workspace; absent
      configuration leaves reads available but returns unavailable for those operations, and another workspace
      cannot be previewed.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/e2e/custom-tables.e2e.spec.js#refuses a preview of any other
      tenant, a customer’s rows needing a staff access grant there is no way to hold
    origin: >-
      services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
      (Phase 1 accepted by Andrew on 2026-10-12)
  - id: BR-CAP-PFM-QRY-005-06
    proved_by: [ATS-PFM-QRY-005-06, ATS-PFM-QRY-005-18, TST-PFM-QRY-223]
    statement: >-
      A breaking publication waits for acknowledgment of every affected saved query and otherwise returns the
      changes and dependent query identities.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/e2e/custom-tables.e2e.spec.js#refuses the publish as 409 rather
      than breaking the query quietly
    origin: >-
      services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
      (Phase 1 accepted by Andrew on 2026-10-12)
  - id: BR-CAP-PFM-QRY-005-07
    proved_by: [ATS-PFM-QRY-005-07, TST-PFM-QRY-223]
    statement: >-
      Assigned active publications appear in the dataset catalog; deprecated publications still resolve and
      disabled publications produce an explicit execution refusal.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-platform:modules/queries/services/custom-tables/assignment-visibility.ts:visibleCustomTables'
    origin: >-
      services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
      (Phase 1 accepted by Andrew on 2026-10-12)
  - id: BR-CAP-PFM-QRY-005-08
    proved_by: [ATS-PFM-QRY-005-08]
    statement: >-
      A custom dataset requires every permission its statement reads, marks readers without them unauthorized,
      and refuses execution with the missing permission identified.
    kind: permission
    enforced_at:
      - service
    must_prove: true
    provenance: documented
    source: 'services-platform:docs/queries/semantic-query-layer.md#14.2 What resolves a custom dataset'
    origin: >-
      services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
      (Phase 1 accepted by Andrew on 2026-10-12)
  - id: BR-CAP-PFM-QRY-005-09
    proved_by: [ATS-PFM-QRY-005-09, TST-PFM-QRY-223, TST-PFM-QRY-372]
    statement: >-
      Pins reference published versions, with the most specific covering pin taking precedence; unpinned
      grants preserve broader pins, and clearing a pin follows another covering pin or the latest publication.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-platform:modules/queries/services/custom-tables/assignment-visibility.ts:pinnedVersionForTenant'
    origin: none
  - id: BR-CAP-PFM-QRY-005-10
    proved_by: [ATS-PFM-QRY-001-13, ATS-PFM-QRY-005-10, ATS-PFM-QRY-005-17, TST-PFM-QRY-223]
    statement: >-
      Deprecation removes a custom dataset from selection lists while preserving execution, whereas disabling
      it refuses execution with a reason.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/e2e/custom-tables.e2e.spec.js#disables it, which refuses a run
      of it rather than answering nothing
    origin: >-
      services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
      (Phase 1 accepted by Andrew on 2026-10-12)
  - id: BR-CAP-PFM-QRY-005-11
    proved_by: [ATS-PFM-QRY-005-11]
    statement: >-
      A published statement whose stored fingerprint no longer matches is refused without exposing statement
      text.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: documented
    source: 'services-platform:docs/queries/semantic-query-layer.md#14.2 What resolves a custom dataset'
    origin: >-
      services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
      (Phase 1 accepted by Andrew on 2026-10-12)
  - id: BR-CAP-PFM-QRY-005-12
    proved_by: [ATS-PFM-QRY-005-12, TST-PFM-QRY-223]
    statement: >-
      Custom-table history attributes lifecycle actions to their actors without retaining statement text or
      record values.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/e2e/custom-tables.e2e.spec.js#keeps no statement and no value of
      the data in the history, which is what it is allowed to carry
    origin: >-
      services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
      (Phase 1 accepted by Andrew on 2026-10-12)
  - id: BR-CAP-PFM-QRY-005-13
    proved_by: [ATS-PFM-QRY-005-13, TST-PFM-QRY-223]
    statement: >-
      An assigned custom table’s reference exposes a reverse one-to-many relationship from its referenced
      dataset only in workspaces covered by the assignment.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/e2e/custom-tables.e2e.spec.js#is refused the join by the tenant
      no assignment names, the definition not being a dataset there
    origin: none
  - id: BR-CAP-PFM-QRY-005-14
    proved_by: [ATS-PFM-QRY-005-14, TST-PFM-QRY-223]
    statement: >-
      Changes to definitions, drafts and assignments must use their read versions, and stale versions are
      rejected.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/e2e/custom-tables.e2e.spec.js#refuses a save at a version
      somebody else has moved on from as 409
    origin: >-
      services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
      (Phase 1 accepted by Andrew on 2026-10-12)
  - id: BR-CAP-PFM-QRY-005-15
    proved_by: [ATS-PFM-QRY-005-15, TST-PFM-QRY-250]
    statement: >-
      Revalidation revisits active or deprecated publications and pinned versions, records meaningful health
      changes without statement text or data, limits repeated passing history to once daily, and skips without
      a validation workspace.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-platform:modules/queries/services/custom-tables/revalidate-custom-tables.ts:revalidateCustomTables
    origin: none
  - id: BR-CAP-PFM-QRY-005-16
    proved_by: [ATS-PFM-QRY-005-16, TST-PFM-QRY-371]
    statement: >-
      Analysis rejects statements that write or lock records, contain recursion, use forbidden schemas or
      functions, or fail the single permitted-read requirement.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-platform:modules/queries/services/custom-tables/analyze-sql.ts:analyzeSql'
    origin: >-
      services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md#Status
      (Phase 1 accepted by Andrew on 2026-10-12)
scenarios:
  - id: SC-CAP-PFM-QRY-005-01
    given: Rhythm staff publish an organization-count definition assigned to one customer.
    when: That customer lists and executes its new dataset.
    then: The definition is available and returns only the workspace’s own organizations.
    provenance: test
    source: >-
      services-platform:modules/queries/tests/e2e/custom-tables.e2e.spec.js#runs it and answers rows of the
      tenant’s own organizations
edge_cases: []
constraints: []
depends_on:
  - CAP-PFM-QRY-001
related:
  - CAP-PFM-QRY-001
  - CAP-PFM-QRY-006
journeys: []
open_questions: []
operations:
  - createCustomQueryTable
  - updateCustomQueryTable
  - saveCustomQueryTableDraft
  - analyzeCustomQueryTableDraft
  - previewCustomQueryTable
  - publishCustomQueryTable
  - revalidateCustomQueryTables
  - assignCustomQueryTable
  - unassignCustomQueryTable
  - pinCustomQueryTableAssignment
  - listCustomQueryTables
  - getCustomQueryTable
  - getCustomQueryTableUsage
  - getCustomQueryTableVersion
  - listCustomQueryTableVersions
  - listCustomQueryTableAssignments
  - listCustomQueryTableEvents
tables:
  - custom-query-tables.table.ts
  - custom-query-table-versions.table.ts
  - custom-query-table-columns.table.ts
  - custom-query-table-assignments.table.ts
  - custom-query-table-events.table.ts
evidence:
  - >-
    Pinned reference:
    services-platform:docs/queries/custom-query-tables/rhythm-custom-query-tables-architecture.md
  - 'Pinned reference: services-platform:docs/queries/semantic-query-layer.md'
  - 'Pinned reference: services-platform:modules/queries/services/custom-tables/analyze-sql.ts'
  - 'Pinned reference: services-platform:modules/queries/services/custom-tables/assignment-visibility.ts'
  - 'Pinned reference: services-platform:modules/queries/services/custom-tables/revalidate-custom-tables.ts'
  - 'Pinned reference: services-platform:modules/queries/tests/e2e/custom-tables.e2e.spec.js'
controls: [TC-AVL-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-CRY-004, TC-IAM-001, TC-IAM-002, TC-IAM-003, TC-IAM-004, TC-IAM-005, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-PI-001, TC-PI-003, TC-PI-005, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-VEN-001, TC-VEN-002, TC-VEN-003]
exercised_by: [FAS-PFM-QRY-01-02, FAS-PFM-QRY-01-03, FAS-PFM-QRY-01-04]
suites: [TST-PFM-QRY-223, TST-PFM-QRY-250, TST-PFM-QRY-371, TST-PFM-QRY-372]
---

# Provide a customer with a tailored dataset

## Purpose

Give selected customers a versioned dataset that Rhythm staff author and maintain without changing each customer’s application.

## How It Works

Rhythm staff create a custom definition and edit its draft. Analysis reports unsafe statement forms and proposes the fields the result exposes. Preview uses a designated validation workspace. Publication produces a version that assignments make available to selected customers. Those customers then use the definition through ordinary dataset queries. A breaking publication reports affected saved questions for acknowledgment. Later pinning behaviour controls which publication a covered workspace uses. Deprecation and disabling have different effects on continued use. Revalidation records changed health when an application’s database shape or grants no longer support a publication. The product should confirm authority for the later behaviours outside the accepted first phase.

## Relationships

Tailored tables extend dataset discovery. Saved questions and recipes can refer to their published definitions.
