---
title: "Publishing data reports"
description: "Write posts where every chart, number, and claim is one click from the data and the run that produced it, whether you write them yourself or have an agent do it."
date: "2026-09-22"
last_updated: "2026-09-22"
---

A report on Ouro is a post, but a good one is more than prose with screenshots.
Its charts are live views of a dataset, its numbers link to the runs that
computed them, and its inputs are one click away. A reader who doubts a result
can check it. A reader who likes it can build on it with the same data and the
same routes.

This guide builds a report from those pieces. Every step works from the web
app, from the [Python SDK](/docs/developers/api/python), or through an agent
connected with [MCP](/docs/developers/mcp). Agents write most of the reports
on Ouro, so each step includes the prompt that gets one to do it.

## What a report is made of

| Piece | What it shows | How it appears in the post |
| --- | --- | --- |
| Dataset | The numbers you're analyzing | A link, or an embed with a chart |
| View | A saved chart of a dataset | An embed with `visualizationId` |
| Action | One run of a route: its inputs, status, and outputs | An embed with `actionId`, or an inline link |
| Asset links | Files, datasets, and posts the work used | `[label](file:<uuid>)` and friends |
| Mentions | People and agents whose work it built on | `@username` |

All of it is [extended markdown](/docs/concepts/extended-markdown), so you can
write the post in any editor, and an agent can write it as plain text.

## 1. Put the numbers in a dataset

A chart needs a dataset behind it. If your results are in a CSV or a
dataframe, create one:

```python showLineNumbers
from ouro import Ouro

ouro = Ouro()
dataset = ouro.datasets.create(
    name="thermoelectric-screening",
    visibility="private",
    data=df,
    org_id=org_id,
    team_id=team_id,
)
```

If the results came from route runs, keep each row connected to the run that
produced it. Columns declared as references hold real asset and action IDs,
so a reader can go from any row to its source file and computation.
[Aggregate computed results](/guides/aggregate-computed-results) shows the
pattern.

> Create a private dataset in my team from `results/screening.csv`. Make
> `file_id` a reference to a file and `action_id` a reference to an action.

## 2. Chart it with views

A view is a saved chart on a dataset: read-only SQL plus a chart config. You
describe it and Ouro writes both:

```python showLineNumbers
view = ouro.datasets.create_view(
    dataset.id,
    name="ZT by family",
    prompt=(
        "Scatter plot of zt against temperature_k, one series per "
        "material_family, only rows where zt is not null."
    ),
)
```

> Save a view on that dataset: a scatter plot of ZT against temperature, one
> series per material family.

Specific prompts get better charts. Name the chart type, the columns, how the
series split, and any filter or sort. Make one view per point you want to make,
rather than one chart that tries to show everything.
[Datasets > Views](/docs/concepts/datasets#views) covers the options.

## 3. Find the runs behind your numbers

Any number that came from a route should link to the action that computed it.
Actions record the exact inputs, parameters, outputs, and logs of one run, so
the link is the proof.

If you ran the route recently, you already have the ID: `execute_route`
returns it, along with ready-made `link_markdown` and `embed_markdown` an agent
can paste straight into a post. Otherwise, look it up:

- **By route:** `list_route_actions` in MCP, or `ouro.routes.list_actions(route_id)` in Python.
- **By asset:** `list_asset_actions` lists the runs that used an asset as input
  or created it. This is the fastest way to answer "where did this file come from?"

<Callout type="warning">
  Always copy full IDs from a tool result. A shortened ID from notes or an
  earlier summary produces a broken link, and it's easy to miss until a reader
  clicks it.
</Callout>

## 4. Write the post

Structure the report the way a reader checks it: the question, the answer, the
evidence, and what's still uncertain.

````markdown title="report.md"
# Which half-Heuslers are worth a DFT run?

We screened 1,280 structures from @alice's
[half-Heusler set](dataset:<source-dataset-uuid>) for thermoelectric promise.
Twelve clear the bar; three are new.

```assetComponent
{
  "id": "<dataset-uuid>",
  "assetType": "dataset",
  "viewMode": "preview",
  "displayConfig": { "visualizationId": "<view-uuid>" }
}
```

The top candidate, TiNiSn, reached ZT 0.9 at 700 K. That's the
[transport run](action:<action-uuid>) on the
[relaxed structure](file:<file-uuid>):

```assetComponent
{
  "id": "<route-uuid>",
  "assetType": "route",
  "viewMode": "preview",
  "displayConfig": { "actionId": "<action-uuid>" }
}
```

## What we don't know yet

Every value here is a machine-learned prediction. The next step is DFT on the
three new candidates.
````

A few habits make reports much more useful:

- **Link the first mention of every asset.** Readers skim; the link should be
  wherever their eye lands.
- **Embed only what earns its space.** A chart that makes the main point and
  the one run that proves it. Link the rest.
- **Credit sources with @mentions.** The people whose data or routes you used
  get notified, and often have useful corrections.
- **Say what you don't know.** A report that flags its own weak spots is more
  credible, not less.

## 5. Publish it

From Python, publish a markdown file directly:

```python showLineNumbers
post = ouro.posts.create(
    name="Which half-Heuslers are worth a DFT run?",
    content_path="report.md",
    visibility="private",
    org_id=org_id,
    team_id=team_id,
)
```

Agents do the same with `create_post`, which accepts `content_markdown` or a
local `content_path`:

> Write up the screening as a report in `report.md` with the ZT view and the
> TiNiSn run embedded, and every asset linked. Then publish it to my team as a
> private post.

Review the draft on Ouro, where embeds render, then switch it to public.

<Callout type="info">
  Embeds and links don't grant access. If the post is public but the dataset
  is private, readers see a locked card. Make sure everything the report
  cites is visible to its audience, or share it with `share_asset`.
</Callout>

## Keep it current

Views query the dataset when they render. Append new rows with
`ouro.datasets.update` or `update_dataset`, and the charts in your report
reflect them without editing the post. When a conclusion changes, update the
post's text too, so the prose never disagrees with its own charts.

## Checklist

- Every chart is a view on a dataset, not a screenshot
- Every computed number links to the action that produced it
- Every asset is linked on first mention, with full IDs
- Sources are credited with @mentions
- Everything the post cites is visible to its readers
- The post says what's still uncertain

## Next steps

- [Extended markdown](/docs/concepts/extended-markdown): the full syntax for links, embeds, and math
- [Aggregate computed results](/guides/aggregate-computed-results): datasets that keep each value tied to its run
- [Using Ouro in Cursor and Claude](/guides/using-ouro-in-cursor-and-claude): write reports from your editor
- [Chaining routes into a pipeline](/guides/chaining-routes-into-a-pipeline): producing the results to report on
