See it run
Run a whole outbound pipeline offline with no keys, then run a keyless pipeline twice and watch the second run skip what the first one paid for
What you'll learn
Your first run. You have gtme and no API keys, and you want to see a whole pipeline work before connecting anything.
- run an outbound pipeline offline from recorded responses
- read a receipt
- why a second run costs less and delivers nobody twice
This needs gtme and nothing else. No API keys and no accounts, and it spends $0 and sends nothing. If you don't have the binary yet, install it first.
Make a folder, keep the ledger inside it so demo people stay out of your real one, install the two Apollo adapters the demo names, and run the demo offline:
mkdir -p gtme-start && cd gtme-start
export GTME_LEDGER=./ledger.db
curl -fsSLO https://raw.githubusercontent.com/gtme-run/gtme/main/examples/demo.yaml
gtme adapters add apollo/search apollo/enrich instantly/add-to-campaign
gtme run demo.yaml --simulate
Vendor adapters come from gtme's registry, not the binary. adapters add downloads each one, checks it against its recorded responses, and installs it; it needs no keys.
The output is similar to the following:
simulate: ignoring missing credentials (3 plan problems:
- step "source": missing credential APOLLO_API_KEY (set it in the environment or run `gtme secret set APOLLO_API_KEY`)
- step "reveal": missing credential APOLLO_API_KEY (set it in the environment or run `gtme secret set APOLLO_API_KEY`)
- step "send": missing credential INSTANTLY_API_KEY (set it in the environment or run `gtme secret set INSTANTLY_API_KEY`))
simulate: recorded responses only — no network, no spend, nothing sends, nothing persists
run 01M3FBEHWBNKYCDXYHPW1WNMX1 (demo)
...
run 01M3FBEHWBNKYCDXYHPW1WNMX1 — done (SIMULATED — recorded responses only; nothing sent, nothing persisted)
step adapter in out empty cached filtered failed cost avoided
source apollo/search 0 1 - 0 - - $0 -
fit ai/filter 1 1 - 0 - - $0 -
reveal apollo/enrich 1 1 - 0 - - $0.0100 -
lines ai/compose 1 1 - 0 - - $0 -
send instantly/add-to-campaign 1 0 - 0 - - $0 -
send: preflight skipped — the target is not read under --simulate; --dry-run checks it
send: resolved variables for 1 record(s) — review, then run again without --dry-run to arm:
nh:ed48d7bfcbdb5e0ce36005a8cec0be0812e91c1f4a4f542bc86eed418173f417
first_line: "Fixture first line for nh:ed48d7bfcbdb5e0ce36005a8cec0be0812e91c1f4a4f542bc86eed418173f417"
first_name: "Jane"
total: $0.0100 (estimated) spent
That's a receipt: one row per step, with what went in, what came out, and what it cost. The three "missing credential" lines aren't an error; they're the keys a real run would need. Simulate never touches the network: vendor steps answer from recorded responses, and AI steps return canned text that says so. The $0.0100 on the reveal row is what a real run would have paid, and 1 held on the send row means the email variables were printed and nothing was sent. A simulated run writes nothing down, so running it again gives the same receipt.
The file you just ran
demo.yaml is a whole outbound pipeline in 30 lines. Here it is with the comments stripped:
Search Apollo, keep the people an AI judge says own outbound tooling, and pay for contact details only after the judge says yes. Then write a first line and add them to an Instantly campaign. Each use: names an adapter as vendor/verb. The next section is about two of these lines: cache: 30d on reveal and idempotency: email on send.
Run something twice
The second file runs armed, which means for real: it writes to the ledger file you set. It still spends $0 and sends nothing. hello.yaml reads three fictional people from a CSV, scores them with demo/enrich, keeps anyone scoring 70 or more, and writes the keepers to out.csv in this folder. demo/enrich is gtme's built-in scorer: no vendor, a pretend $0.01 per record, so the receipt has real arithmetic.
-
Download the pipeline and its CSV:
curl -fsSLO https://raw.githubusercontent.com/gtme-run/gtme/main/examples/hello.yaml curl -fsSLO https://raw.githubusercontent.com/gtme-run/gtme/main/examples/contacts.csv -
Run it. This is the armed run; the $0.03 on the receipt is the pretend price:
gtme run hello.yamlrun 01M3FBESW58AW0AZHNBDV97VFX (hello) ... step adapter in out empty cached filtered failed cost avoided source csv/source 0 3 - 0 - - $0 - score demo/enrich 3 3 - 0 - - $0.0300 - keep sql/filter 3 1 - 0 2 - $0 - out csv/deliver 1 1 - 0 - - $0 - total: $0.0300 (estimated) spent -
Run it again:
gtme run hello.yamlrun 01M3FBESX4GP5SGBR0BAEKSWF6 (hello) ... step adapter in out empty cached filtered failed cost avoided source csv/source 0 3 - 0 - - $0 - score demo/enrich 3 0 - 3 - - $0 $0.0300 keep sql/filter 3 1 - 0 2 - $0 - out csv/deliver 1 0 - 0 - - $0 - out: 1 already delivered total: $0 spent, $0.0300 avoided via cache (3 records skipped)
Read the score row: cached 3, avoided $0.0300. Each person already had a score in the ledger, inside its 30-day window, so the scorer didn't run. Then read the line under the table, out: 1 already delivered. Jane went to out.csv last time under the same email, so she wasn't delivered again; that's idempotency: email. It holds per delivery target, so the same person can still be added to a different campaign on purpose.
The counter is real and the dollars are pretend. With your own rows and a real price behind a step, the avoided column is money you didn't spend.
-
See what the ledger kept about Jane:
gtme show jane.doe@acme.com{ "deliveries": [ { "created_at": "2026-09-26T17:15:56.685Z", "run_id": "01M3FBESW58AW0AZHNBDV97VFX", "scope": "out.csv", "status": "accepted", "target": "csv/deliver" } ], "entity_type": "person", "fields": { "company_domain": "acme.com", "demo.note": "synthetic — demo/enrich called no vendor", "demo.score": 100, "email": "jane.doe@acme.com", "full_name": "Jane Doe", "title": "VP Marketing" }, "identity_key": "jane.doe@acme.com", "identity_key_tier": "email" }The
deliveriesentry is the row that stopped her second delivery, anddemo.noteis the scorer admitting it called no one.gtme showonly reads.
When you're done, rm ledger.db out.csv and the demo is gone. Your real ledger at ~/.gtme/ledger.db was never touched.
Next
Your CSV runs this shape over your own contacts with one Anthropic API key. It costs a few cents on the model. If Claude Code is doing the typing, the agent page has the line to paste.