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:
- Decides it needs weather data
- Pays an x402 endpoint $0.01 per call in USDC
- Stays inside a $1/day budget
- Verifies every settlement onchain
- 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.tsand 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.