# Share an HTML Report From Prefect

Canonical: https://commareports.com/ci/prefect-html-report
Published: 2026-09-09

> A Prefect flow writes the report inside an ephemeral work pool container and the artifact panel takes markdown, not documents. Publish from the flow so the reader gets a URL.

# Share an HTML report from Prefect

Prefect makes the orchestration part pleasant: the flow is Python, the retries
are declarative, the run is observable. Then a task writes `report.html` inside
a container from an ephemeral work pool, and the last mile is the same one
every pipeline has.

- **The container is temporary.** The file is gone with the infrastructure that
  ran the flow, usually within minutes.
- **Artifacts are summaries.** Markdown, tables and links attached to a run —
  useful, and not the place for a charted document.
- **The UI is behind a login.** Prefect Cloud or a self-hosted server, either
  way scoped to the people who operate pipelines rather than the people who
  read their output.
- **A bucket relocates the problem.** Writing to S3 means the reader now needs
  bucket credentials to see a report.

## Publish from the flow

```python
import json, os, urllib.request
from prefect import flow
from prefect.artifacts import create_link_artifact

REPORT_ID = "rep_…"  # created once; PATCHed from every run

@flow
def daily_quality_report():
    html = build_report_html()
    body = json.dumps({"title": "Daily data quality", "html": html}).encode()
    req = urllib.request.Request(
        f"https://commareports.com/api/v1/reports/{REPORT_ID}",
        data=body,
        method="PATCH",
        headers={
            "Authorization": f"Bearer {os.environ['COMMA_API_TOKEN']}",
            "Content-Type": "application/json",
        },
    )
    urllib.request.urlopen(req).read()
    create_link_artifact(
        key="quality-report",
        link=f"https://commareports.com/p/{REPORT_ID}",
        description="Rendered report",
    )
```

The link artifact is the part that makes this discoverable: the run page in
Prefect now points at the document, and the document is readable by people who
have never seen a Prefect run page.

Three details that matter:

- **`PATCH` a saved id, don't `POST`.** POSTing every run scatters orphan
  links; one id gives the flow a permanent URL and a revision per run.
- **Publish unconditionally.** An `on_failure` hook on the flow publishes the
  digest for the run that broke, which is the one somebody is asking about.
- **Scope the token to `reports:write`** and keep it in a Secret block — see
  [API tokens](/docs/api-tokens).

## What the reader gets

- **A URL and nothing to install.** No Prefect account, no bucket credentials.
- **The document rendered faithfully** — a Plotly figure stays interactive, a
  pandas table stays a table. See
  [sharing a Plotly chart](/share-plotly-html) and
  [sharing a pandas DataFrame](/share-pandas-dataframe-html).
- **Comments anchored to the failing row** — see
  [commenting on HTML](/comment-on-html).
- **A revision history** answering "when did this start drifting", without
  anyone keeping dated copies in a folder.

## Worth knowing

- **Subflows and mapped tasks.** Publish once from the parent rather than once
  per mapped run; the rate limit is 60 requests/minute per token, and 300
  separate links help nobody.
- **HTML body: 5 MB.** Aggregate inline, extract attached.
- **Multi-file output** needs the folder treatment — see
  [sharing an HTML folder](/share-html-folder).
- **Nothing re-executes on Comma.** A published report is a snapshot; if you
  want a refresh without a scheduler at all, that is a
  [routine](/docs/routines).

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision
history.

**[Create your first report →](https://commareports.com/)**

### Related

- [Publish from CI](/ci) — the general pipeline pattern
- [Share an Airflow report](/ci/airflow-html-report) ·
  [Share a Dagster asset report](/ci/dagster-html-report)
- [Share an EDA report](/share-eda-report) ·
  [Scheduled HTML reports](/features/routines/scheduled-html-reports)
