Docs
Wrap your agent’s fetch, watch spend land in the union view, and freeze out of band. Three moving parts.
Concepts
- Rails
- Every spend event belongs to a rail: LLM APIs, data vendors, cloud/metered, payments, crypto/x402. The union view is the sum across all of them, per agent.
- Local enforcement
- The SDK holds a policy snapshot and evaluates it in-process. There is no authorize round-trip before a paid call, so SpendWall adds no latency to your agent.
- Out-of-band freeze
- Freezing an agent revokes its ingest key. The SDK notices on its next snapshot poll (default 30s) and blocks locally. SpendWall never sits in your payment path.
- Custody-free
- SpendWall observes and controls; it never holds funds or credentials for the vendors your agents pay.
SDK quickstart
Install the SDK and grab an ingest key from Settings → API keys.
# npm / pnpm / bunpnpm add @spendwall/sdkimport { createSpendwall } from "@spendwall/sdk"const sw = createSpendwall({apiKey: process.env.SPENDWALL_KEY!,agentId: "research-crawler",baseUrl: "https://spendwall.io/api/v1",})// 1. drop-in fetch: records spend, enforces locallyconst res = await sw.fetch(url, opts)// 2. or record spend you priced yourselfsw.recordSpend({ rail: "data", provider: "firecrawl", amountCents: 4 })// 3. flush on shutdownawait sw.close()When you freeze the agent from the dashboard, sw.fetch throws SpendwallFrozenError on the next call after the snapshot refresh. Hard policy blocks throw SpendwallPolicyError.
API reference
Ingest endpoints authenticate with your key: Authorization: Bearer sw_live_…
/api/v1/eventRecord one spend event, or a batch.
{ "agentId": "…", "rail": "llm", "provider": "openai",
"endpoint": "https://api.openai.com/v1/chat/completions",
"amountCents": 42, "outcome": "success" }{ "accepted": 1 }/api/v1/policy?agentId=…The enforcement snapshot the SDK polls (freeze arrives here).
{ "agentId": "…", "status": "active", "revoked": false,
"monthToDateCents": 184230, "policies": [ … ] }/api/v1/reconcileAfter-the-fact true-ups. Same idempotency as /event.
{ "events": [ { "agentId": "…", "amountCents": 12, … } ] }{ "reconciled": 1 }Dashboard endpoints (policies, approvals, freeze, billing) are authenticated with your session and documented in-app.
Environment
Everything is config-driven. Local dev needs nothing: with an empty DATABASE_URL the app runs on an in-process database and the seeded demo org. Each service below degrades gracefully when its keys are absent, so features light up as you add them.
DATABASE_URL- Postgres connection string. Leave empty for the in-process PGlite dev database.
NEXT_PUBLIC_APP_URL- Public base URL of the app. Used for redirects and links.
SPENDWALL_INGEST_URL- Base URL the SDK posts spend events to (defaults to /api/v1).
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY- Clerk auth. Omit both keys to run in demo mode with the seeded org.
CLERK_SECRET_KEY- Clerk server key. Enables real sign-in and org sync.
CLERK_WEBHOOK_SECRET- Verifies Clerk org/user webhooks.
STRIPE_SECRET_KEY- Stripe billing. Without it, checkout returns a demo preview.
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY- Stripe client key for checkout.
STRIPE_WEBHOOK_SECRET- Verifies Stripe subscription webhooks to sync plans.
STRIPE_PRICE_STARTER / _GROWTH / _SCALE- Stripe price IDs for each paid plan.
SLACK_WEBHOOK_URL- Posts alerts and approval requests to Slack.
SLACK_SIGNING_SECRET- Verifies Slack interactive approve/deny actions.