The open-source example project: an agent that pays for its own data

A walkthrough of harpd-dev/example-agent — a TypeScript agent that surfs a paid weather API, stays under budget, and submits its own weekly spend report. The reference implementation for the two-asset strategy: OSS entry that funnels to the hosted product.

harpd-dev/example-agent is the repo we send people to when they ask “so what does an agent that actually uses Harpd look like?” It’s a single-file TypeScript agent that:

  1. Decides it needs weather data
  2. Pays an x402 endpoint $0.01 per call in USDC
  3. Stays inside a $1/day budget
  4. Verifies every settlement onchain
  5. Posts a weekly spend report to a webhook

The whole thing is ~150 lines and runs on node with no build step. The point of it is to be the simplest possible demonstration of the two-asset strategy: an open-source entry that’s useful on its own, with a clear upgrade path to the hosted Harpd layer when you outgrow it.

The structure

example-agent/
├── agent.ts          # the full agent — ~150 lines
├── budget.ts         # policy definition (copy-paste from the template post)
├── package.json
└── README.md

Two files of code, one of policy, one of docs. We deliberately resist making it more complex — anything you’d want to extract into a helper should be in @harpd/observe or @harpd/agent-budget-policy instead.

The interesting 20 lines

import { harpdPlugin } from '@harpd/observe'
import { defineBudget, evaluate } from '@harpd/agent-budget-policy'

const budget = defineBudget({
  perCall:  { usdMax: 0.05 },
  perAgent: { 'weather-agent': { usd: 1, window: '1d' } },
  perEndpoint: { 'https://paid-weather.example.com/v1': { usd: 1, window: '1d' } },
})

const observe = harpdPlugin({
  apiKey: process.env.HARPD_KEY!,       // works in observe-only mode without Harpd
  endpoint: 'https://api.harpd.com',
  budgetControl: true,
})

async function callWeather(city: string) {
  const intent = {
    agentId: 'weather-agent',
    endpoint: 'https://paid-weather.example.com/v1',
    amountUsd: 0.01,
  }
  const ok = evaluate(budget, intent)
  if (!ok.allowed) {
    console.log('deny:', ok.reason, 'retry after', ok.retryAfter)
    return null
  }
  // Observe the intent (before-payment)
  await observe.hooks['before-payment'](intent)
  // Pay + verify (uses x402 client under the hood)
  const res = await payAndFetch(intent)
  // Observe the settlement (after-settlement)
  await observe.hooks['after-settlement']({ ...intent, txHash: res.txHash })
  return res.data
}

That’s the whole flow. The evaluate call is local and synchronous — it never blocks on the network. The Harpd calls are fire-and-forget (queued with exponential backoff in the SDK). The actual payment goes through any x402 client you like.

What you get for free from this repo

  • Working budgeting out of the box. Change the numbers in budget.ts and you have a different tier policy. Nothing else to touch.
  • Onchain verification for every settlement, via @harpd/observe’s after-settlement hook.
  • A weekly CSV report of spend, posted to a webhook — the same export a CFO would get from the hosted dashboard, generated locally.
  • A denial path that’s structured enough for an LLM to read and route around, not a thrown exception.

The upgrade path

The example agent runs entirely on open source. When you outgrow it, the upgrade is two lines:

- agent.ts uses local CSV report
+ Use Harpd's hosted export (drop the local webhook code)
- evaluate() decisions happen locally only
+ Add `budgetControl: true` in harpdPlugin → Harpd also enforces centrally

Nothing rewrites. The events you were already emitting become the dashboard’s data source. The policy you were already evaluating becomes the team-wide policy, visible in the dashboard, editable without a deploy.

That’s the design intent: the open-source layer is the product’s first draft. Adding Harpd turns it into a multi-tenant, audited, dashboard-managed version of itself. Not a different product — the same product, with the parts you couldn’t build yourself filled in.

Next