# How a pipe works

A pipe is a YAML file. Each step names a block, or another pipe with `block: pipe:<name>@<range>`. Updated 2026-10-17. HTML: https://asipipes.com/how-it-works.

## Why your bot is better with pipes

You ask your bot to watch things. A pipe is the written-down way it watches.

Example ask: "Tell me every morning if any of my tools shipped a security fix." Another everyday ask: "Alert me if BTC drops below $90k."

Without pipes:

- It invents a new plan each time you ask.
- Monday's answer can disagree with Tuesday's.
- It writes a long new reply even when nothing changed.
- It sometimes names a source it never opened.
- You cannot show what it actually checked.
- It stops when you close the chat.

With pipes:

- The same question follows the same steps.
- You get the same kind of answer when the inputs match.
- The reply is the pipe's result, which is smaller than the raw sources.
- It only reports what the pipe fetched.
- A receipt lists exactly what it checked.
- It runs while you sleep, and the dashboard shows the run.

What your bot saves, measured Oct 18, 2026 (full table at https://asipipes.com/benchmarks):

- 92% fewer tokens across three checks: SEC 8-K filings, a 4-feed news digest, and a 7-symbol price snapshot. 9,137 tokens versus 116,827.
- 99.4% fewer tokens when nothing is new: 142 instead of 25,574. Get pinged only when something is new: no polling.
- Exact, replayable output. 30 of 30 runs matched an independent parse. 29 of 29 replays reproduced the receipt.

Benefits:

- Reliable. Same question, same answer.
- Cheaper. No re-thinking the same task. A repeated step is reused from cache.
- Always on. Schedules run without you.
- Provable. Every run has a receipt you can replay.
- Safe. Each pipe declares exactly what it can touch.
- Visible. The dashboard lists each run, the result, and the receipt.

A day with your bot (example, not a live run):

- 7:00 morning digest of security fixes.
- 2:00 price alert if BTC is under $90k. The run leaves a receipt either way.
- 6:00 you open the dashboard and see both runs.

## Anatomy of a pipe

`github-releases-digest` watches GitHub release feeds. Example data below is shortened from the recorded snapshot.

1. input `repos`: `["tokio-rs/tokio", "bytecodealliance/wasmtime"]`
2. `fetch` `fetch_http`: `https://github.com/${item}/releases.atom`
3. `entries` `parse_rss`
4. `with_repo` `regex_extract`: repo and tag, for example `bytecodealliance/wasmtime` `v49.0.3`
5. `unique` `dedupe`
6. `new` `diff_since_last`
7. `ordered` `sort`
8. `format` `format`
9. output `text` and `count`

Example output:

```
New releases:
- bytecodealliance/wasmtime v49.0.3 (2026-10-14T17:17:37Z)
- tokio-rs/tokio tokio-1.53.3 (2026-10-13T20:49:48Z)
```

Receipt from that snapshot: `b3:32fcd8189b88e670634706ae5cc0ca8bb11e711f2eb0637dd614bbbabd621e29`.

`pipes replay` reruns the snapshot offline and checks the same receipt. The hash covers the pipe content id, the input hash, each step output hash, each recording hash, and the final output hash.

## Chaining pipes

`release-radar` runs two pipes and merges them.

```yaml
- id: releases
  block: pipe:github-releases-digest@^0.1
  with:
    repos: "${inputs.repos}"
- id: advisories
  block: pipe:security-advisories@^0.1
  with:
    keyword: "${inputs.keyword}"
```

The parent must allow `github.com` and `rustsec.org`. The lockfile pins the ranges to content ids:

```
github-releases-digest@^0.1 = b3:461f0752b1e60498a29505f7cc490a067c99706ec05790b5dbf68f2b8b6a2361
security-advisories@^0.1 = b3:a99d8390695e47234b980e82b71b35bd3754b8e081536837372a051e7b1df661
```

These library pipes are 0.1.0, so the range is `^0.1`. A 1.x pipe uses `@^1`. The combined receipt lists `releases`, then `releases/fetch`, then `advisories` and its steps, then `rows` and `digest`. Example data from one local run: `b3:60e9350ce3376611a3b8d71eb74a8fbb9a458f657a15b7cc51f72cb83c514c41`. A later run differs when the feeds change. Replay of that snapshot checks the same hash.

The parent receipt includes `releases`, then `releases/fetch` and the other sub-pipe steps, and the recordings from both pipes. Nesting stops at 8. A cycle or a missing permission fails validation.

`price-news-alert` chains `crypto-price-threshold` into `news-digest`. `dev-brief` chains `changelog-rss-diff` and `hn-keyword-watch`.

## How your agent calls it

```json
{
  "name": "pipe__release-radar",
  "arguments": { "repos": ["tokio-rs/tokio"], "keyword": "wasmtime" }
}
```

The result includes `output`, `run_id`, and `receipt`. Example data, not a live hosted run. Sign in at https://asipipes.com/login with an invite code, create a key, and call https://asipipes.com/mcp.

## Get pinged when something new happens

A webhook fires when a run's `diff_since_last` result has new records. Empty runs stay quiet. The POST body is JSON: `event` (`new_items` or `run_failed`), `pipe`, `version`, `run_id`, `receipt`, `count`, `items` (capped, with `truncated`), `run_url`, and `at`. Set `on_failure` to also hear about a failed run.

```
curl -s -X POST https://asipipes.com/v1/notifications \
  -H "Authorization: Bearer $PIPES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/hooks/asipipes","pipe":"github-releases-digest","on_failure":false}'
```

MCP:

```json
{
  "name": "add_notification",
  "arguments": {
    "url": "https://example.com/hooks/asipipes",
    "pipe": "github-releases-digest"
  }
}
```

With a shared secret, each delivery sends `Asipipes-Signature: sha256=<hex>` and `Asipipes-Timestamp`. The hex is HMAC-SHA256 of `timestamp + "." + raw body`. `Asipipes-Delivery` is the same id on every retry.

```python
import hashlib, hmac
def signed(secret: str, timestamp: str, body: bytes) -> str:
    mac = hmac.new(secret.encode(), timestamp.encode() + b"." + body, hashlib.sha256)
    return "sha256=" + mac.hexdigest()
ok = hmac.compare_digest(signed(secret, timestamp, raw_body), header)
```

Beta Free includes 100 deliveries a day. Five failed attempts over about an hour disable the webhook, and the reason is on the target. `POST /v1/notifications/{id}/test` sends one ping.

## Determinism

- Same pipe content, same inputs, and the same lock produce the same output.
- An LLM step is cached by a hash of the model, the prompt, and the item.
- `--frozen` turns a cache miss into an error.
