Engineering8 min read
Guardrails for an agent that can move money
Letting software issue refunds sounds scary until you see the limits, approvals and audit trail around every single action.
The first question every finance team asks about Volt is the right one: what stops it from refunding everyone?
The answer is that Volt never calls your payment provider directly. Every action runs through a small, boring layer we call the action gate.
Every action has a contract
An action is defined once, with typed inputs and explicit limits:
export const refundCharge = defineAction({
name: "refund_charge",
input: { chargeId: "string", amount: "money" },
limits: {
maxAmount: 100_00,
maxPerCustomerPerMonth: 2,
},
approval: ({ amount }) => (amount > 100_00 ? "billing-team" : "none"),
});
The model can only propose a call. The gate validates the input, checks the limits and either runs it, queues it for approval or refuses with a reason the agent can explain to the customer.
Four layers of protection
- Scoped keys. Each action gets its own restricted API key, so a refund action cannot read customer lists.
- Hard limits. Amounts, counts and time windows are enforced in code, not in a prompt.
- Approvals. Above a threshold, a human approves from Slack or the Volt inbox in one click.
- Audit trail. Every proposal, decision and response is stored with the conversation that caused it.
Prompts describe intent. Limits enforce it. Never confuse the two.
Testing before going live
Before an action is enabled we replay a month of real tickets in a simulation and list every call the agent would have made. Most teams find one or two policies they never wrote down, and write them down.