# Data pull report — a replayable Reelier skill

Public ECB euro reference FX rates from the Frankfurter open-data API, bound into a small report — read-only

A real, read-only Reelier skill against a live public API — no account, no keys, no writes. Every replay proves: The Frankfurter open-data API answers with the euro as base and each reported rate as a number.

- Steps: 1 · Assertions: 5 · Effect: read (counts parsed from the file, never hand-typed)
- Endpoints: https://api.frankfurter.dev/v1/latest?symbols=USD,GBP
- Canonical file: https://raw.githubusercontent.com/seldonframe/reelier/main/examples/portfolio/data-pull-report.skill.md
- HTML page: https://www.reelier.com/skills/data-pull-report

## What this checks

One step: an HTTP GET to the Frankfurter open-data API for today's ECB euro reference rates, filtered to USD and GBP. Five assertions: the API answered 200, base is the euro, date matches /^\d{4}-\d{2}-\d{2}$/, and both rates.USD and rates.GBP are numbers — identity and shape pinned, values left free to move.

Rates move; the report's shape doesn't. The day's numbers are bound onto the receipt (date, USD, GBP) rather than pinned, so every replay records the fresh values while a healthy pull stays the same shape — no exchange rate to rot by tomorrow.

The API publishes on TARGET working days and carries the last published rates forward, so a weekend or holiday pull still returns a well-formed report and stays green — the skill checks that the data API answered honestly, not that a market was open today.

## The skill file (verbatim)

```markdown
---
name: data-pull-report
description: Public ECB euro reference FX rates from the Frankfurter open-data API, bound into a small report — read-only
---

<!-- synced from seldonframe/reelier examples/portfolio — edit there -->

# Data pull report

Inputs: (none — this file replays green as-is; see the personalization note)

<!--
  The "pull data on a schedule" pattern: GET a public JSON data API and bind
  several values onto the receipt as a small report. This one hits the
  Frankfurter open-data API (the European Central Bank's euro reference
  rates) — public, no key, no auth. The reference base is the euro, so `base`
  is pinned as identity while the rates themselves are left free to move day
  to day.

  Personalization: swap the two currency codes for the ones YOUR report cares
  about — the portfolio README's "Point these at YOUR project" section has
  the one-command version. The skill grammar has no default-value syntax for
  {{var}} holes — an unbound {{var}} is an explicit error, never a guessed
  fallback — so this file ships with literals that replay green with zero
  flags.

  Endpoint verified live on 2026-07-21: 200, base "EUR", date "2026-07-21".
-->

Rates move; the report's shape doesn't. The assertions pin the shape — the
API answered 200, the base is the euro, the date looks like a date, and each
reported rate is a number — never a specific exchange value that would rot by
tomorrow. The day's numbers are bound onto the receipt (date, USD, GBP), so
every replay records the fresh values while a healthy pull stays the same
shape.

## Steps

### Step 1 — Latest euro reference rates from the open-data API
- intent: Pull today's ECB euro reference rates from the Frankfurter API and check the report shape
- action: http.get {"url": "https://api.frankfurter.dev/v1/latest?symbols=USD,GBP"}
- assert: status == 200
- assert: json.base == "EUR"
- assert: json.date matches /^\d{4}-\d{2}-\d{2}$/
- assert: json.rates.USD is number
- assert: json.rates.GBP is number
- bind: date = json.date
- bind: USD = json.rates.USD
- bind: GBP = json.rates.GBP
- effect: read
```

## Run it yourself

`reelier run` takes a local file, so fetch the canonical copy first (or clone the repo). The replay writes its receipt to `.reelier/runs/`.

```sh
curl -fsSL https://raw.githubusercontent.com/seldonframe/reelier/main/examples/portfolio/data-pull-report.skill.md -o data-pull-report.skill.md
npx -y reelier@latest run data-pull-report.skill.md

# or from a clone:
git clone https://github.com/seldonframe/reelier
cd reelier && npx -y reelier@latest run examples/portfolio/data-pull-report.skill.md
```

## Seed receipt — 2026-07-21

Replayed for real with the published CLI (resolved to reelier@0.14.0) on 2026-07-21; the run record confirms passed: true and 0 LLM tokens (input + output). Verbatim output:

```
✓ Step 1 — Latest euro reference rates from the open-data API [passed] 248ms

PASSED: 1/1 steps ok, 0 failed, 248ms total
```

Standing audit log: a GitHub Action replays all five portfolio skills every 6 hours (plus on demand) — https://github.com/seldonframe/reelier/actions/workflows/portfolio-replay.yml. A failing endpoint produces an honestly failing receipt, never a retried-until-green one.

## Point it at your project

Swap the two currency codes (`USD`, `GBP`) for the ones your report cares about — each code appears identically in the URL's `symbols` list, its assert, and its bind, so one sed retargets all three consistently. To point at a different data or reporting API entirely, replace the URL and the `json.*` dot-paths so they match your API's response shape. The skill grammar has no default-value syntax for {{var}} holes — an unbound {{var}} is an explicit error, never a guessed fallback — so this file ships with literals that replay green with zero flags.

```sh
sed 's/USD/CAD/g; s/GBP/JPY/g' data-pull-report.skill.md > my-fx-report.skill.md \
  && npx -y reelier@latest run my-fx-report.skill.md
```

---

Reelier — agents make claims, Reelier writes receipts. AGPL-3.0.
Portfolio index: https://www.reelier.com/skills (twin: https://www.reelier.com/skills.md) · Replay ledger: https://www.reelier.com/replays · Machine-readable summary: https://www.reelier.com/llms.txt
