---
id: CAP-PFM-QRY-013
title: Render a report for download
feature: FEAT-PFM-QRY-04
product_area: platform
module: queries
revision: 1
status: draft
product_intent: proposed
implementation_evidence: observed
supersedes:
  - CAP-RPT-PRN-001
  - CAP-RPT-PRN-002
  - CAP-RPT-PRN-003
  - CAP-RPT-PRN-004
  - CAP-RPT-PRN-005
  - CAP-RPT-PRN-006
  - CAP-RPT-PRN-010
  - CAP-RPT-SUB-010
  - CAP-RPT-SUB-011
  - CAP-RPT-SUB-012
  - CAP-RPT-DSH-023
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources: []
purpose: Produce a report file through a controlled rendering session evaluated as the requester.
outcomes:
  - Staff receive report files suitable for circulation.
  - Rendering checks the requester’s current access.
  - Failed or excessive rendering does not publish a partial file.
concepts:
  - CON-PFM-QRY-003
  - CON-PFM-QRY-004
actors:
  - ACT-PFM-013
  - ACT-PFM-012
surfaces:
  - console
  - api
  - job
preconditions: []
postconditions: []
permissions:
  - key: platform.reports.use
    where_checked: api
    scope: tenant
  - key: 'platform.queries.read (to follow and download, CAP-014)'
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-PFM-QRY-013-01
    proved_by: [ATS-PFM-QRY-013-01, ATS-PFM-QRY-013-14, ATS-PFM-QRY-014-12, TST-PFM-QRY-231, TST-PFM-QRY-232, TST-PFM-QRY-334]
    statement: >-
      Report export requires report use and a visible report, returns not found for an inaccessible report and
      writes nothing for rejected requests.
    kind: permission
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/integration/services/rendering/report-exports.integration.spec.js#answers
      a colleague who may not see the private report 404, writing nothing
    origin: none
  - id: BR-CAP-PFM-QRY-013-02
    proved_by: [ATS-PFM-QRY-013-02, TST-PFM-QRY-334]
    statement: 'All report kinds support PDF, only coded and table reports support Excel, and report CSV is rejected.'
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/integration/services/rendering/report-exports.integration.spec.js#refuses
      an Excel export of a dashboard, writing nothing
    origin: none
  - id: BR-CAP-PFM-QRY-013-03
    proved_by: [ATS-PFM-QRY-013-03, TST-PFM-QRY-334]
    statement: >-
      Print parameters preserve submitted values and accept at most fifty names, text up to five hundred
      characters and lists up to one hundred entries, rejecting unsupported values and the renderer’s reserved
      name.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/integration/services/rendering/report-exports.integration.spec.js#refuses
      the renderer’s own parameter name at parameters, writing nothing
    origin: none
  - id: BR-CAP-PFM-QRY-013-04
    proved_by: [ATS-PFM-QRY-013-04, ATS-PFM-QRY-013-15, ATS-PFM-QRY-015-19, TST-PFM-QRY-333]
    statement: >-
      Before opening a browser, rendering rechecks the report and requester, failing deleted or newly
      inaccessible reports and lost report-use access as access lost.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/integration/services/rendering/render-report.integration.spec.js#fails
      a requester who may not use reports access_lost, saying so plainly
    origin: none
  - id: BR-CAP-PFM-QRY-013-05
    proved_by: [ATS-PFM-QRY-013-05, TST-PFM-QRY-409]
    statement: >-
      Print-page API calls run internally as the requester through an allowed route set in the export
      workspace, with other origins, operations and routes refused and logged without query strings.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/unit/services/rendering/api-interception.spec.ts#aborts a route
      of Platform off the allowlist, a write among them
    origin: none
  - id: BR-CAP-PFM-QRY-013-06
    proved_by: [ATS-PFM-QRY-012-12, ATS-PFM-QRY-013-06, ATS-PFM-QRY-013-13, TST-PFM-QRY-111, TST-PFM-QRY-333]
    statement: >-
      Each page request renews its access check, and revoked access fails the export rather than producing a
      file of refusal messages.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/integration/services/rendering/render-report.integration.spec.js#fails
      the export access_lost rather than printing a page of refusals
    origin: none
  - id: BR-CAP-PFM-QRY-013-07
    proved_by: [ATS-PFM-QRY-013-07, TST-PFM-QRY-113, TST-PFM-QRY-333]
    statement: >-
      Rendering rejects work beyond five minutes, PDFs above two hundred pages, and invalid or oversized Excel
      files above one hundred megabytes, without storing a rejected file.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/integration/services/rendering/render-report.integration.spec.js#fails
      a PDF past its page limit too_many_pages
    origin: none
  - id: BR-CAP-PFM-QRY-013-08
    proved_by: [ATS-PFM-QRY-013-08, TST-PFM-QRY-333, TST-PFM-QRY-334]
    statement: Completed files use the report name and workspace date and remain available for thirty days.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/integration/services/rendering/render-report.integration.spec.js#keeps
      the file thirty days from its completion, as the bucket’s rule removes it
    origin: none
  - id: BR-CAP-PFM-QRY-013-09
    proved_by: [ATS-PFM-QRY-011-16, ATS-PFM-QRY-013-09, ATS-PFM-QRY-013-12, TST-PFM-QRY-110]
    statement: Report deletion leaves completed files downloadable but causes queued rendering to fail for lost access.
    kind: lifecycle
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: documented
    source: 'services-platform:docs/queries/report-rendering.md#2. The export row'
    origin: none
  - id: BR-CAP-PFM-QRY-013-10
    proved_by: [ATS-PFM-QRY-013-10, TST-PFM-QRY-027, TST-PFM-QRY-112, TST-PFM-QRY-409]
    statement: >-
      Rendering uses a fresh browser without a login session or stored credentials, disables service workers
      and downloads, and rejects page-opened connections.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/unit/services/rendering/api-interception.spec.ts#records a
      WebSocket the page opened as refused
    origin: none
  - id: BR-CAP-PFM-QRY-013-11
    proved_by: [ATS-PFM-QRY-013-11, TST-PFM-QRY-333]
    statement: >-
      Internal rendering failures retry through the queue and become a generic final failure on the last
      attempt.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/queries/tests/integration/services/rendering/render-report.integration.spec.js#records
      internal on the last receive, with the sentence that never says what went wrong
    origin: none
scenarios:
  - id: SC-CAP-PFM-QRY-013-01
    given: A report render begins while its requester has access.
    when: A required grant is revoked during page requests.
    then: The export ends as access lost rather than storing refusal content.
    provenance: test
    source: >-
      services-platform:modules/queries/tests/integration/services/rendering/render-report.integration.spec.js#fails
      the export access_lost rather than printing a page of refusals
edge_cases: []
constraints: []
depends_on:
  - CAP-PFM-QRY-011
related:
  - CAP-PFM-QRY-011
  - CAP-PFM-QRY-012
  - CAP-PFM-QRY-014
  - CAP-PFM-QRY-015
journeys:
  - JNY-PFM-QRY-004
open_questions:
  - OQ-PFM-QRY-002
operations:
  - startReportExport
tables:
  - query-exports.table.ts
  - query-export-requests.table.ts
evidence:
  - 'Pinned reference: services-platform:docs/queries/report-rendering.md'
  - >-
    Pinned reference:
    services-platform:modules/queries/tests/integration/services/rendering/render-report.integration.spec.js
  - >-
    Pinned reference:
    services-platform:modules/queries/tests/integration/services/rendering/report-exports.integration.spec.js
  - 'Pinned reference: services-platform:modules/queries/tests/unit/services/rendering/api-interception.spec.ts'
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-DAT-004, TC-DAT-005, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-001, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-SEC-001, TC-SEC-002, TC-SEC-003, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-VEN-001, TC-VEN-002, TC-VEN-003]
exercised_by: [FAS-PFM-QRY-04-01, FAS-PFM-QRY-04-03, FAS-PFM-QRY-04-04, FAS-PFM-QRY-05-04, FAS-PFM-QRY-05-05]
suites: [TST-PFM-QRY-027, TST-PFM-QRY-110, TST-PFM-QRY-111, TST-PFM-QRY-112, TST-PFM-QRY-113, TST-PFM-QRY-231, TST-PFM-QRY-232, TST-PFM-QRY-333, TST-PFM-QRY-334, TST-PFM-QRY-409]
---

# Render a report for download

## Purpose

Produce a report file through a controlled rendering session evaluated as the requester.

## How It Works

A person requests a report file in a supported format. Platform creates an export record for the renderer to process. Before opening the print page, it checks that the requester still has access. The renderer uses a fresh browser and supplies permitted page parameters. Its API requests are answered internally under the requester’s current access. Requests outside the permitted surface are stopped. Readiness, time and file-size checks decide whether the rendered file can be stored. A successful export becomes downloadable for its retention period, while a failure explains the reason without exposing internal details.

## Relationships

Rendering uses report presentations and the shared export records; scheduled report delivery requests that output.
