Quickstart

Enforce a cap, then verify a signed demo receipt.

Install the SDK, wrap your provider client, set a daily cap. The next time your agent tries to spend over your cap, AgentGuard throws before the provider is called, so that blocked request is not dispatched or billed by the provider.

Step 1

Install

Pick your runtime. The SDK has zero hard dependencies on any specific provider, you bring your own OpenAI / Anthropic / Bedrock client.

npm install @agentguard-run/spend
pip install agentguard-spend
Step 2

Wrap your provider client

One call. Pass in your existing client, declare a cap, get back a guarded client you use the same way.

// Anthropic uses its dedicated Messages binding.
import Anthropic from '@anthropic-ai/sdk';
import { withSpendGuardAnthropic } from '@agentguard-run/spend';

const policy = {
  id: 'daily-cap-v1',
  name: 'Daily cap',
  scope: { tenantId: 'acme' },
  caps: [
    { amountCents: 2000, window: 'per_day', action: 'block' }
  ],
  mode: 'enforce',
  version: 1,
  effectiveFrom: new Date().toISOString()
};

const guarded = withSpendGuardAnthropic(new Anthropic(), {
  policy,
  scope: { tenantId: 'acme', agentId: 'my-agent' }
});

await guarded.messages.create({
  model: 'claude-opus-4-7',
  max_tokens: 1024,
  messages: [{ role: 'user', content: 'hello' }]
});
# One line. Drop-in.
from agentguard_spend import easy_install
from anthropic import Anthropic

client = easy_install(Anthropic(), daily_cap_dollars=20)

client.messages.create(
    model="claude-opus-4-7",
    max_tokens=1024,
    messages=[{"role": "user", "content": "hello"}]
)
What just happened

Every Anthropic Messages call through this wrapped client now runs a local preflight. Cost is projected from estimated input tokens and max_tokens. If the configured scope's daily spend would cross $20, AgentGuardBlockedError is thrown and the provider method is not invoked. This snippet enforces the cap but does not sign until you add config.signingKeys.

Step 3

Watch it block

Drop the cap to a few cents and trigger a real call. You'll see the block fire instantly.

try {
  await guarded.messages.create({ /* ... */ });
} catch (err) {
  if (err.name === 'AgentGuardBlockedError') {
    console.log(err.toString());  // human-readable trace
    console.log(err.decision); // policy decision; not a signed entry by itself
  }
}
from agentguard_spend import AgentGuardBlockedError

try:
    client.messages.create(...)
except AgentGuardBlockedError as err:
    print(err)                    # color-coded trace
    print(err.decision.action)    # "block"
    print(err.decision.cap_hit)   # which cap fired
Step 4

Verify a receipt

Direct wrappers create signed entries only when config.signingKeys is supplied. The CLI demo creates local throwaway keys and a real signed chain, so you can exercise verification without a provider call.

$ agentguard demo
...
verify this receipt: agentguard verify

$ agentguard verify
✓ full chain valid
✓ entry hashes match canonical JSON
✓ signatures match supplied public key
✓ no sequence gaps (ledger appears complete)
import { verifyChain } from '@agentguard-run/spend';

const result = await verifyChain(entries, publicKey);
console.log(result.ok ? 'valid' : result.reason);

Next steps

In-memory state is single-process only. The public package exports an NDJSON decision-log store, while Redis and Postgres implementations are present in source but are not exported by the 0.16.0 package map. Implement the store interfaces or wait for supported adapter subpaths before promising cross-worker state.

Capabilities matrix → All integrations → Storage status →