Rust + TypeScript · MIT · Open Source

Usage metering for
the agent economy

Drop-in metering for APIs that serve AI agents.
Track every request, attribute it to an agent,
and issue cryptographic usage receipts.

Service X — 4 lines
// axum middleware — wrap any route
let meter = AgentMeter::new(MeterConfig {
    service_id: "my-api".into(),
    ..Default::default()
});

let app = Router::new()
    .route("/api/generate", post(handler)
        .layer(AgentMeterLayer::new(meter)
            .with_receipt_secret("svc-secret")));
Agent Y — sign + reconcile
let client = AgentClient::new(ClientConfig {
    agent_id: "agent-y".into(),
    signing_secret: Some("shared-secret".into()),
    service_url: "https://api.agentmeter.io".into(),
    ..Default::default()
});

// Signed request → receipt in response headers
let resp = client.call("POST", "/api/generate", body).await?;

// Diff local log against service records
let report = client.reconcile().await?;
// matched: 47 · agent_only: 2 · service_only: 0

How it works

Two parties. One correlation key. Cryptographic proofs on both sides.

1

Agent signs the request

Before sending, Agent Y computes HMAC(secret, body) and sends it as X-Agent-Signature. This signature is the unforgeable correlation key between both parties' records.

2

Service meters and issues a receipt

The axum middleware verifies the signature, builds a UsageRecord, stores it, and injects X-Usage-Receipt: HMAC(svc_secret, sig) into the response. The receipt proves Service X recorded the request.

3

Both parties reconcile

Agent Y downloads Service X's usage records and diffs them against its local log. Unmatched records surface as agent_only (not metered) or service_only (unexpected charges).

🔑

HMAC-SHA256 signing

Every request body is signed. Service X can't fabricate Agent Y's signatures. Compatible byte-for-byte with the TypeScript SDK.

🌳

Merkle attestation

Batches ship with a Merkle root signed over all records. Any tampering in transit invalidates the root — detectable by either party.

⚖️

Reconciliation

Periodic diff by requestSignature. Matched, unmatched, and discrepant records surfaced with counts. Same model as financial statements.

Transports

Records go wherever you need them. Composable and swappable.

Memory
In-process. All clones share the same buffer. Best for development and testing.
SQLite feature = "sqlite"
Persistent local storage. WAL mode. Survives restarts. No external database needed.
HTTP
Batches records and POSTs them to a backend. Retries with exponential backoff. Handles 429 + Retry-After.
Attestation
Wraps any transport. Each batch gets a signed Merkle root attestation before forwarding.

Install

Service X — Cargo.toml
# Core + axum middleware
agent-meter = "0.1"
agent-meter-axum = "0.1"

# Optional: persistent SQLite storage
agent-meter = { version = "0.1", features = ["sqlite"] }
Agent Y — Cargo.toml
# Agent-side: signing + reconciliation
agent-meter-client = "0.1"
TypeScript (npm)
npm install @oztenbot/agent-meter

Hosted billing backend

Send records to api.agentmeter.io instead of running your own SQLite. Queryable usage history, invoicing, and rate limiting included.

HttpTransport::new(HttpTransportOptions {
    url: "https://api.agentmeter.io/v1/usage/batch".into(),
    headers: [("Authorization", "Bearer sk-...")].into(),
    ..Default::default()
})