The core primitive: the wallet
Budget management in Trove starts with the wallet. A wallet is a spending container attached to a single agent. It has a hard budget limit, a current balance, and a transaction ledger. Every API call your agent makes goes through the wallet — and the wallet enforces the limit before any charge commits.
This is different from setting a credit limit on a card. The wallet API is programmatic: your agent's code calls POST /api/wallets/:id/purchase with an amount, and the wallet decides whether to record the transaction or return a 402. No human involvement, no card decline surprise, no external processor interference.
Base URL: https://trove-qjow.polsia.app/api — All requests require X-Trove-Key: tvpk_your_api_key header. All amounts are in USD. All timestamps are ISO 8601.
Endpoint reference
Wallets
POST
/api/wallets
Create a wallet for an agent
const res = await fetch('https://trove-qjow.polsia.app/api/wallets', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Trove-Key': 'tvk_your_key'
},
body: JSON.stringify({
agent_id: 'support-bot-v2',
name: 'Support Bot June Budget',
budget_limit_usd: 100.00
})
});
const { wallet } = await res.json();
// wallet.id, wallet.budget_limit_usd, wallet.balance_usd, wallet.status
import httpx
res = httpx.post(
'https://trove-qjow.polsia.app/api/wallets',
headers={'X-Trove-Key': 'tvk_your_key'},
json={
'agent_id': 'support-bot-v2',
'name': 'Support Bot June Budget',
'budget_limit_usd': 100.00
}
)
wallet = res.json()['wallet']
GET
/api/wallets/:id
Get wallet details
GET
/api/wallets/:id/balance
Get current balance and remaining budget
{
"wallet_id": "wa_01HX...",
"balance_usd": 23.47,
"budget_limit_usd": 100.00,
"remaining_budget": 76.53,
"transaction_count": 47,
"status": "active"
}
Spending
POST
/api/wallets/:id/purchase
Record a purchase — enforces budget atomically
const res = await fetch(
`https://trove-qjow.polsia.app/api/wallets/${wallet.id}/purchase`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Trove-Key': 'tvk_your_key'
},
body: JSON.stringify({
amount_usd: 0.08,
vendor: 'openai',
description: 'gpt-4o: customer support ticket #8841'
})
}
);
if (res.status === 402) {
// Budget exhausted — skip the OpenAI call
const { available } = await res.json();
alertBudgetExhausted(wallet.id, available);
} else {
// Transaction recorded — proceed
}
Top-ups and credits
POST
/api/wallets/:id/topup
Add funds to a wallet
POST
/api/wallets/:id/credit
Credit the wallet (e.g., for a vendor refund)
Transaction history
GET
/api/wallets/:id/transactions
Paginated transaction ledger
const res = await fetch(
`https://trove-qjow.polsia.app/api/wallets/${wallet.id}/transactions?limit=20&offset=0`,
{ headers: { 'X-Trove-Key': 'tvk_your_key' } }
);
const { transactions, pagination } = await res.json();
// transactions[0]: { id, amount_usd, vendor, description, created_at }
Analytics
GET
/api/analytics
Aggregate spending across all wallets
const res = await fetch(
'https://trove-qjow.polsia.app/api/analytics?period=30d',
{ headers: { 'X-Trove-Key': 'tvk_your_key' } }
);
const { total_spent, by_vendor, by_wallet, period_start, period_end } =
await res.json();
Error codes at a glance
| HTTP |
Scenario |
Response body |
| 200 |
Purchase recorded successfully |
{ success: true, transaction_id, new_balance } |
| 402 |
Over budget |
{ success: false, message: "Insufficient funds", available, requested } |
| 404 |
Wallet not found |
{ error: "Wallet not found" } |
| 401 |
Invalid or missing API key |
{ error: "Invalid API key" } |
| 422 |
Invalid amount (negative, zero, non-numeric) |
{ error: "Invalid amount_usd" } |
Putting it together: a budget-aware agent
Here's the full pattern for building an agent that respects its budget. Check the balance before expensive calls, record every purchase through the wallet, and handle the 402 gracefully when the limit is hit.
async function callWithBudgetGuard(walletId, amountUsd, callFn) {
// 1. Check balance before calling expensive APIs
const { remaining_budget } = await fetchBalance(walletId);
if (remaining_budget < amountUsd) {
console.warn(`Budget too low: $${remaining_budget} left, need $${amountUsd}`);
return { skipped: true, reason: 'budget_exhausted' };
}
// 2. Make the external API call (OpenAI, Serper, etc.)
const result = await callFn();
// 3. Record the purchase through the wallet
const purchaseRes = await recordPurchase(walletId, {
amount_usd: amountUsd,
vendor: result.vendor,
description: result.description
});
// 4. Handle wallet rejection
if (purchaseRes.status === 402) {
// This shouldn't happen if the pre-check passed — log and alert
alertAnomaly(walletId, 'Purchase failed after balance check');
return { skipped: true, reason: 'purchase_blocked' };
}
return { skipped: false, result };
}
import httpx
async def call_with_budget_guard(wallet_id: str, amount_usd: float, call_fn):
balance = fetch_balance(wallet_id)
if balance['remaining_budget'] < amount_usd:
print(f"Budget too low: ${balance['remaining_budget']} left")
return {"skipped": True, "reason": "budget_exhausted"}
result = await call_fn()
purchase_res = record_purchase(wallet_id, {
"amount_usd": amount_usd,
"vendor": result["vendor"],
"description": result["description"]
})
if purchase_res.status_code == 402:
print("ERROR: Purchase blocked by wallet — check balance")
return {"skipped": True, "reason": "purchase_blocked"}
return {"skipped": False, "result": result}
Tracking spend velocity
The balance endpoint gives you a snapshot. For real-time monitoring, poll the analytics endpoint or set up a webhook for budget.threshold_reached (fires when the agent hits 80% of its limit). Here's a simple velocity check:
const res = await fetch(
`https://trove-qjow.polsia.app/api/wallets/${walletId}/transactions?limit=20`,
{ headers: { 'X-Trove-Key': 'tvk_your_key' } }
);
const { transactions } = await res.json();
const last20 = transactions.slice(0, 20);
const now = Date.now();
const first = new Date(last20[19].created_at).getTime();
const velocity = (20 / (now - first)) * 60_000; // tx/min
if (velocity > 5) {
alert(`Unusual velocity: ${velocity.toFixed(1)} tx/min — check for runaway loop`);
}
Pro tip: Store the last 100 transactions in a local ring buffer so you can compute velocity without querying the API on every call. Update the buffer when you record a purchase.
Resetting and topping up
Trove wallets do not auto-reset on a calendar schedule — the budget limit is the ceiling, not a recurring allowance. To build a monthly budget cycle, call POST /api/wallets/:id/reset at the start of your billing period:
// Monthly reset — call this on the 1st of each month
await fetch(
`https://trove-qjow.polsia.app/api/wallets/${walletId}/reset`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Trove-Key': 'tvk_your_key'
},
body: JSON.stringify({
reset_balance_to: 0,
topup_amount: 100.00
})
}
);
This resets the balance to zero, then adds the top-up amount. The transaction ledger is preserved for historical reporting — your monthly spend data is never lost.
Test the full API in the playground
Sandbox wallet with $100. Make purchases, hit the 402, test the reset. No setup required.
Open the Playground
Full endpoint summary
| Method |
Endpoint |
Description |
| POST |
/api/wallets |
Create a wallet for an agent |
| GET |
/api/wallets |
List all wallets on your API key |
| GET |
/api/wallets/:id |
Get wallet details |
| GET |
/api/wallets/:id/balance |
Get current balance and remaining budget |
| POST |
/api/wallets/:id/purchase |
Record a purchase (enforces budget) |
| POST |
/api/wallets/:id/topup |
Add funds to the wallet |
| POST |
/api/wallets/:id/credit |
Credit the wallet (e.g., refund) |
| POST |
/api/wallets/:id/reset |
Reset balance and optionally top up |
| GET |
/api/wallets/:id/transactions |
Paginated transaction ledger |
| GET |
/api/analytics |
Aggregate spend across all wallets |
For webhook integration, authentication details, and SDK setup, see the full API documentation.