Skip to content

Outbound communications and payments

The situation

A support or finance team lets an agent act on customers' behalf: reply to tickets by email, post in a shared channel, issue refunds, pay supplier invoices. Every one of those effects leaves your systems — a sent email cannot be recalled, a payout cannot be un-sent.

What goes wrong without a gate

Each action is small and each one is plausible. The failure is the run: a loop that replies to the same ticket queue twice emails every customer twice; a refund policy misread as "refund anything under €2,000" is fine for one ticket and is €40,000 by the end of the afternoon. Nothing per-call is over a threshold, so a per-call check passes all of it. This is the structuring pattern banks have watched for in payments for decades — split one large move into many small ones.

Three representative actions

One customer reply. Outbound, irreversible (sent is sent), single. Allowed — and counted toward the session's external_sends budget.

Verdict: allow · rule reeflex.policy/default_allow

{
  "agent":     { "id": "agent:support", "session_id": "sess-support-5520" },
  "action":    { "namespace": "mail", "verb": "emit", "ability": "email/send" },
  "target":    { "kind": "mailbox", "ref": "customer:83311", "environment": "production" },
  "magnitude": { "count": 1 },
  "axes":      { "reversibility": "irreversible", "blast_radius": "single", "externality": "outbound" },
  "approval":  { "present": false }
}

The 51st send in one session. external_sends defaults to 50 per session. The 51st outbound action — of any kind: email, chat post, webhook — is held for a person, who sees the session's history and decides whether the run is the job or a loop.

Verdict: require_approval · rule reeflex.policy/cumulative_budget · call 51 · reason contains external_sends — sends 1–50 are this same envelope, each allowed.

{
  "agent":     { "id": "agent:support", "session_id": "sess-support-5521" },
  "action":    { "namespace": "mail", "verb": "emit", "ability": "email/send" },
  "target":    { "kind": "mailbox", "ref": "customer:83311", "environment": "production" },
  "magnitude": { "count": 1 },
  "axes":      { "reversibility": "irreversible", "blast_radius": "single", "externality": "outbound" },
  "approval":  { "present": false }
}

The third €1,800 refund. The money budget is per currency (EUR 5,000 by default) and reads params.amount and params.currency from whatever verb carries them. Two refunds of €1,800 are €3,600; the third would take the session to €5,400, and is held.

Verdict: require_approval · rule reeflex.policy/cumulative_budget · call 3 · reason contains money — refunds 1–2 are this same envelope, each allowed.

{
  "agent":     { "id": "agent:support", "session_id": "sess-support-5522" },
  "action":    { "namespace": "payments", "verb": "transact", "ability": "refunds/create" },
  "target":    { "kind": "charge", "ref": "charge:ch_3Q0x", "environment": "production" },
  "params":    { "amount": 1800, "currency": "EUR" },
  "magnitude": { "count": 1 },
  "axes":      { "reversibility": "irreversible", "blast_radius": "single", "externality": "outbound" },
  "approval":  { "present": false }
}

Mixed currencies do not escape it: the budget sums each currency's share of its own limit, so €4,999 and $4,999 in one session is 1.9 budgets, not two half-used ones. No exchange rate is involved. The per-currency limits in budgets.rego are illustrative defaults an operator must review.

How to deploy it

Whichever adapter carries the agent's tools — reeflex-mcp for MCP tools, the n8n node for workflow steps, the LiteLLM connector for a gateway fleet. The lines that matter are two: the send/pay tools declared externality: outbound, and the payment tools passing the amount as params.amount with an ISO 4217 params.currency. The limits themselves are in budgets.rego; see the policy guide.

What Reeflex does NOT do here

  • It does not read the email. Whether a reply is rude, wrong, or leaks a customer's data is content; Reeflex prices the act of sending, not what is sent.
  • It does not know an amount the adapter did not pass. A payment tool whose amount is not mapped to params.amount is charged nothing against the money budget.
  • It does not track across sessions. The ledger is per session over a rolling window (3,600 s by default); an agent that opens a new session per refund starts each one at zero.
  • It does not hold the first refund of €4,000. One action under the limit, in a fresh session, is allowed; the budget is about the run, not the single payment. A per-action ceiling is a rule you add.