Why polling is the wrong model for agent spending
Most developers start with polling: call GET /api/wallets/:id/balance every 60 seconds, check if anything changed, react if it did. This works for simple monitoring dashboards. It fails for autonomous agents.
The problem is latency. If your agent makes 50 purchases between your 60-second polling intervals, you have no idea when each dollar left. Budget exhaustion is discovered an hour later, not when it happened. A runaway loop spending $0.01 per call doesn't trigger any alert until you've paid $300 for a task that should have cost $3.
Webhooks solve this. When a purchase succeeds, Trove fires an HTTP POST to your endpoint with the transaction details — vendor, amount, timestamp, wallet ID. Your handler reacts immediately. No polling, no gaps.
The webhook flow for AI agent spending
The event types your handler needs
Trove fires webhooks for every significant wallet event. The three you need first:
- purchase.succeeded — A purchase was recorded. This is your bread-and-butter event. Every API call your agent makes triggers this. Log it, dashboard it, build your cost attribution model on it.Fields: wallet_id, transaction_id, amount_usd, vendor, description, timestamp
- budget.exhausted — The wallet hit its limit. The agent tried to purchase and got a 402. This is your most critical alert. Slack it, email it, pause your agent's queue. This means your agent is now operating without spending capability.Fields: wallet_id, budget_limit_usd, last_transaction_id
- purchase.failed — A purchase attempt returned an error (not over-budget — that would be 402). If your agent is hitting network errors or vendor failures, this is how you see it.Fields: wallet_id, error_code, error_message, vendor
Building the webhook handler
The handler has three jobs: verify the signature, parse the event, respond fast. Everything else happens asynchronously.
Verify the signature
Every webhook from Trove includes an HMAC-SHA256 signature in X-Trove-Signature. If you skip this step, anyone can POST fake events to your handler and pollute your spending data.
Handle events by type
Parse the event and route it to the right handler. Separate the acknowledgment from the processing so slow downstream calls don't cause Trove to retry.
Handle the budget exhaustion alert
This is the event that makes webhooks worth the setup. When your agent hits its budget ceiling, you want to know immediately — and your infrastructure should respond automatically.
What to log and why
Every purchase.succeeded event is a data point for your cost attribution model. If you store them in a simple table, you can answer questions that matter:
Which vendors cost the most? SELECT vendor, SUM(amount_usd) FROM spending_events GROUP BY vendor ORDER BY SUM DESC LIMIT 10;
Which agent is burning budget fastest? SELECT wallet_id, SUM(amount_usd) FROM spending_events WHERE created_at > NOW() - INTERVAL '1 hour' GROUP BY wallet_id;
Are there spikes that indicate a runaway loop? SELECT DATE_TRUNC('minute', created_at) AS minute, SUM(amount_usd) FROM spending_events WHERE wallet_id = $1 AND created_at > NOW() - INTERVAL '2 hours' GROUP BY minute ORDER BY minute;
The storage model is simple. One Postgres table: spending_events(event_id, wallet_id, amount_usd, vendor, description, created_at). Write every event to it, index on wallet_id and created_at. That's enough to build a real-time dashboard, run cost attribution queries, and catch runaway loops before the bill gets out of hand.
Making the Slack alert actionable
A "wallet exhausted budget" Slack message is useful. An interactive Slack message that lets you fix the problem without leaving Slack is better.
When your webhook fires a budget.exhausted event, post a Slack message with a button. The button opens a modal with the current wallet balance, a budget increase input, and a confirm button. When the user confirms, your Slack endpoint calls Trove's wallet update API and posts a success message back to the channel.
The feedback loop: agent spends to limit → webhook fires → Slack alert with interactive fix → you increase budget → agent resumes. All in under 60 seconds. No Terraform, no Ops team, no email chains.
Delivery and retry behavior
Trove expects your handler to return HTTP 200 within 5 seconds. If it returns a non-200 status or times out, Trove retries with exponential backoff: 15 seconds, then 60 seconds, then 5 minutes, then 1 hour. After 4 failed attempts, the event is dropped.
Because you acknowledge immediately and process asynchronously, your handler should always return 200 fast. The retry logic protects against temporary network issues, not your handler's processing time.
If you need guaranteed delivery for critical events (like budget.exhausted), add your own dead-letter queue. On every failed downstream call (Slack, database write), push the event to a Redis list. A separate worker retries the queue every 60 seconds. This ensures you never drop a budget exhaustion alert even if your Slack webhook is down.
Get started with the /try playground
The playground lets you trigger a webhook locally by making purchases from a sandbox wallet. Watch your endpoint receive events in real time, test your signature verification, and see how fast your handler responds. No production infrastructure needed to validate the integration.
Test webhook integration in 2 minutes
The playground auto-provisions a sandbox wallet. Point it at your local endpoint (ngrok works) and watch every event fire as you make purchases.
Open the PlaygroundFor the full webhook event reference and signature verification examples in Python, Ruby, and Go, see the Trove API documentation.