Implementing human-in-the-loop for treasury AI agents: where the gate goes and what it must return
A working pattern for pausing a treasury agent before it moves money: what the approval request carries, how the agent blocks and resumes, and which failures you have to design for yourself.
The gate belongs before the irreversible call, not after the draft
When implementing human-in-the-loop for treasury AI agents, place the approval gate directly between the agent's internal decision and the network call that initiates an irreversible financial transfer. Writing an approval record after dispatching an outbound API call provides zero financial agent safety: once an automated clearing house (ACH) file transmits or an instant payment network receives an instruction, software cannot pull that execution back.
Treasury workflows split cleanly into reversible operations and irreversible operations. Reversible actions include querying account balances, reading bank statements, drafting batch payment files, categorizing ledger entries, and modeling cash projections. Gating these passive steps causes alert fatigue and trains reviewers to rubber-stamp requests. Reversible steps do not need gates. Irreversible operations require hard, synchronous stops. In modern corporate treasury, three actions cannot be undone cleanly after execution:
- Outbound wire, FedNow, or ACH origination transfers. Under the Nacha Operating Rules, ACH returns and reversals operate under rigid regulatory criteria and tight reversal windows, while real-time networks like the Federal Reserve FedNow Service settle irrevocable transfers within seconds.
- Modifications to vendor bank account numbers, routing numbers, or remittance addresses. Attackers exploit agent tool sets specifically to execute unauthorized changes to vendor master records.
- Modifications to signing thresholds, account authority rules, or operational user roles that dictate who can authorize transactions later.
Implementing human-in-the-loop for treasury AI agents prevents the critical production failure where an autonomous model executes a destructive transaction and logs an audit trail only after the funds leave the firm. AgentDraft lets an agent pause any consequential action for human sign-off: it opens an approval request carrying a one-line summary and a JSON evidence payload, a person approves or denies it in the dashboard with an optional note, and the agent reads the outcome back. The gated action does not have to be one AgentDraft performs: a wire transfer, a ledger migration, or a merchant refund is gated the same way. Every transition lands in the append-only audit trail and fires an approval.* webhook.
The requesting agent decides for itself when to open an approval request. AgentDraft does not yet provide a policy engine that auto-requires approval by action class, amount threshold, or role, and there are no escalation chains or multi-approver quorums: a single workspace human resolves each request. Your agent application code must enforce the boundaries that evaluate whether a payment requires human review before issuing the API request.
What the approval request must carry for a treasury action
An approval gate fails if the human reviewer cannot evaluate the transaction from the payload alone. Context must be complete. Do not send open-ended model summaries, natural language prompts, or speculative summaries. The evidence payload is an immutable, machine-readable artifact that an internal finance auditor can inspect six months later.
Structure the evidence payload with deterministic financial data. Amounts must be recorded as integers in minor currency units (such as cents for USD or EUR) to eliminate floating-point rounding errors. Include the payment rail, counterparty identifiers, source liquidity account, and the external invoice or enterprise resource planning (ERP) voucher ID that triggered the run:
{
"summary": "Transfer $42,500.00 USD to Acme Industrial via FedNow for Invoice #INV-88219",
"evidence": {
"action_type": "disbursement",
"payment_rail": "fednow",
"amount_minor_units": 4250000,
"currency": "USD",
"source_account_id": "acct_treasury_op_01",
"destination_bank_name": "JPMorgan Chase",
"destination_routing_transit_number": "021000021",
"destination_account_last4": "8841",
"destination_token": "tok_recipient_992a71bf",
"vendor_id": "vend_acme_industrial",
"invoice_id": "INV-88219",
"fx_rate": null,
"fx_timestamp": null,
"ledger_balance_checked_at": "2026-10-30T14:12:05Z",
"agent_stated_reason": "Matching approved AP voucher INV-88219 due on 2026-10-31",
"correlation_id": "corr_disb_20261030_INV88219"
}
}For cross-border payments or foreign exchange operations, include the exact rate quote and its lock expiration timestamp. Reviewers need to see whether an FX quote has expired without needing to query a treasury management system manually. If the payment runs against a volatile currency pair, record both the source minor units and the calculated target disbursement amount.
Keep the summary string human-readable and strictly on one line. It is the first string rendered in the review list, and approvers scan it to differentiate similar transactions. Omit plaintext secrets, raw API tokens, and full unmasked bank account numbers from the evidence payload. Store a vault token or masked account reference (for example, the last four digits) and let your execution layer resolve sensitive credentials server-side after approval.
Attach a persistent correlation_id inside the evidence payload. This correlation ID must match the idempotency key passed to the banking or payment processor API downstream. AgentDraft records state-changing agent actions in an append-only audit trail, ensuring that the approval transitions, request creation, and webhook events share a traceable lineage with the execution attempt.
Blocking, resuming, and the retry rules that keep the gate honest
An agent interacting with an approval API must model the request as an asynchronous distributed state machine. The state pending is a normal working condition. It is neither an execution error nor an exception to swallow. An agent must halt execution if an approval request times out or disconnects.
| Status | Agent behavior | Next legal transitions | Terminal state |
|---|---|---|---|
requested | Persist state, yield compute or register webhook listener, start cutoff timer. | approved, denied, expired | No |
approved | Verify payload integrity, issue downstream payment with idempotency key. | None | Yes |
denied | Halt payment execution, write rejection note to ERP/ledger, stop workflow. | None | Yes |
expired | Cancel payment intent, alert treasury operations, release local locks. | None | Yes |
State transitions are strictly one-way. Define an operational deadline for every high-stakes agent approval. Financial rails operate on strict clearing schedules: Fedwire funds transfers close at 7:00 PM Eastern Time, and same-day ACH batches carry rigid intermediate cut-offs. If an approval request stays open past the target rail's cut-off window, the agent must treat the request as expired. Executing an approval several hours late can lead to settlement on the wrong value date, incurring overdraft charges or contractual late fees.
When the human decides, AgentDraft dispatches a webhook to your receiver endpoint. AgentDraft signs every webhook delivery with an X-AgentDraft-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256> header computed over <t>. followed by the raw request body, using the workspace's signing secret. The receiver verifies it; AgentDraft recommends that receivers reject any timestamp more than 300 seconds (5 minutes) from the current time to block replays, and its documented reference verifier does so. Signed webhook delivery is available on every plan, including the free Developer tier.
Timestamp-bounded replay protection is standard security practice for sensitive data transmission, as documented in RFC 6238. Implement this signature check on your raw incoming request bytes before deserializing the JSON payload:
import hmac
import hashlib
import time
def verify_agentdraft_webhook(raw_payload: bytes, signature_header: str, secret: str) -> bool:
pairs = dict(item.split("=", 1) for item in signature_header.split(","))
timestamp_str = pairs.get("t")
received_signature = pairs.get("v1")
if not timestamp_str or not received_signature:
return False
delivery_timestamp = int(timestamp_str)
current_timestamp = int(time.time())
# Enforce the 300-second window to prevent message replay attacks
if abs(current_timestamp - delivery_timestamp) > 300:
return False
signed_payload = f"{delivery_timestamp}.".encode("utf-8") + raw_payload
expected_signature = hmac.new(
secret.encode("utf-8"),
signed_payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected_signature, received_signature)Once verified, the agent must inspect the event transition. If the status is denied, the agent must treat the decision as terminal. It must record the human approver's note to the system of record and cease execution. The agent must treat a denial as final without retrying under altered summary strings. If the status is approved, resume execution using the exact correlation_id established at request creation as the payment gateway's idempotency key. A duplicate webhook delivery or network retry will then safely collide at the bank gateway without initiating a second disbursement.
Scoping the treasury agent's credentials so the gate cannot be bypassed
An approval gate implemented purely in application code is an incomplete control. If the worker process running the AI model holds direct write access or unconstrained API keys to bank accounts, a prompt injection attack or runtime logic bug can bypass the gate entirely. Keep credentials segregated. The security model must separate the agent proposing the action from the service executing it.
The OWASP Application Security Verification Standard specifies that automated actors must operate under strict least-privilege credential scopes. AgentDraft agents authenticate with bearer API keys prefixed avs_live_, stored argon2id-hashed. Each key carries explicit scopes (availability:read, bookings:read, bookings:write, mailbox:read, mailbox:write, rules:read, approvals:request); new keys default to availability:read and bookings:write, and approvals:request must be granted per key. Read the AgentDraft identity documentation for detailed scoping instructions.
Structure your infrastructure into two decoupled components:
- The treasury reasoning agent analyzes cash positions, drafts wire proposals, and calls the AgentDraft API with an API key restricted strictly to
approvals:request. This service holds no bank API credentials and possesses no network ingress to core banking endpoints. - The execution worker has zero generative AI capabilities. It listens for
approval.approvedwebhooks, validates the cryptographic signature, callsGET /v1/approvals/{id}to independently verify the status on the server, extracts the pre-approved evidence payload, and attaches banking credentials to submit the transfer.
Provision distinct API keys for each agent in your workspace. Do not share a single workspace token across separate workers. When an agent experiences an anomalous execution loop, revoking its specific avs_live_ key stops that agent immediately without degrading other active treasury workers or calendar automations.
Where the human actually decides, and why not in chat
Approvals are decided in the AgentDraft dashboard. AgentDraft emails the workspace owner a notification linking to the queue, but the decision itself is made signed in: there are deliberately no approve-from-email links, because an unauthenticated one-click approve is an attack surface. Slack, Discord, Teams, SMS and push delivery are not available today.
Chat webhooks and email links present substantial risks for high-stakes agent approval in treasury operations. Chat systems routinely expose notifications across team channels, mobile notifications, and shared desktop screens. An unauthenticated one-click HTTP GET link sent via email can be pre-fetched or triggered by corporate email security gateways, anti-malware URL scanners, or inadvertent user taps, causing unintended financial authorization.
Humans sign in to the AgentDraft dashboard with a passkey (WebAuthn), with a magic link as the bootstrap and recovery path. Passkeys eliminate phishing. Enforcing WebAuthn ensures that approvals stem from hardware-backed, phishing-resistant credentials, aligning with the highest authentication assurance levels outlined in NIST SP 800-63B. The approver's physical presence and cryptographic key binding validate the identity of the person approving a multimillion-dollar disbursement run.
The gated action does not have to be one AgentDraft performs: a deploy, a migration, or a refund is gated the same way. If your treasury automation also manages collateral movements, initiates liquidity sweeps, or flags ledger discrepancies, you route those operations through the exact same dashboard review queue. The human evaluates the context within an authenticated browser session, reviews the unmasked summary, inspects the evidence JSON, and enters an explicit rejection reason if denying the request.
The audit record an auditor will actually ask for
Financial auditors, internal controllers, and forensic investigators inspect the provenance of automated movements. Audit logs require exact lineage. When an autonomous pipeline disburses funds, the primary audit question is: Which actor proposed this specific action, what exact data was displayed to the approver, and who authorized the state change?
approval_id: The unique identifier generated by AgentDraft when the agent requests authorization.agent_id: The authenticated agent identity linked to the bearer key prefix.requested_at: The ISO 8601 timestamp generated at record creation.evidence_hash: A SHA-256 hash of the canonical JSON evidence payload submitted at creation time. This proves the data displayed to the human reviewer was not altered before execution.decided_by: The user ID and email of the authenticated workspace operator who resolved the gate.decision: The terminal state (approved,denied, orexpired).decision_note: The optional text string supplied by the human operator explaining their decision.downstream_transaction_id: The external transaction reference or bank reference generated upon execution.
Audit retention is per-tier and enforced on read as well as on write, so the retention claim holds even though deletion is lazy. The free Developer tier enforces a 7-day audit retention window. A 7-day retention period works for sandbox environments and local verification, but production financial agent safety requires historical records that persist across financial quarters. Review your plan retention settings on the AgentDraft pricing page before routing production financial workflows.
Maintain relational integrity across your databases by indexing the join keys. A simple reconciliation query joins the approval record, the banking idempotency log, and your internal accounting ledger:
SELECT
a.approval_id,
a.agent_id,
a.decided_by,
a.decision,
a.decided_at,
p.payment_id,
p.amount_cents,
p.idempotency_key,
l.ledger_entry_id,
l.posted_at
FROM treasury_approvals a
INNER JOIN banking_payments p
ON a.correlation_id = p.idempotency_key
LEFT JOIN general_ledger l
ON p.payment_id = l.source_transaction_id
WHERE a.requested_at >= '2026-10-01'
AND (p.status = 'settled' OR a.decision = 'denied');Monitor this query for orphaned records in both directions: payment records that lack an associated approval_id indicate a security bypass, while approved requests lacking a downstream transaction indicate silent execution drops.
Failure modes to design for before the first real payment
Distributed workflows encounter predictable operational failures. Before connecting an agent to production banking infrastructure, implement direct code mitigations for these five failure modes.
1. Approval arrives after the clearing rail cut-off
A human signs into the dashboard and clicks approve at 5:05 PM Eastern Time for a wire transfer that required submission by 5:00 PM. If the execution worker blindly processes the approval, the bank will either reject the instruction or hold it overnight, incurring unintended interest rate exposure. Deadlines require active checks. The execution worker must evaluate whether the current time exceeds the execution deadline specified in the original evidence payload. If expired, mark the job as unexecutable, write a warning to the audit log, and notify the treasury desk.
2. The reviewer approves the wrong request due to ambiguous summaries
When multiple automated agents handle invoice processing, an approver might see five pending requests that each read "Transfer funds to Supplier." If the reviewer cannot distinguish between them, they will inadvertently authorize the wrong batch. Enforce strict template rules in your agent code: the one-line summary must include the exact formatted currency amount, the counterparty trading name, and the primary document reference.
3. Agent network retries duplicate the approval request
If your agent encounters an HTTP 504 Gateway Timeout while submitting an approval request, an unconstrained retry loop will generate duplicate approval items in the reviewer queue. Store the pending approval_id locally alongside your transaction record before dispatching the request. Deduplicate every outbound call. Deduplicate incoming execution requests on the correlation_id so that your application refuses to open a second gate if one is in requested status.
4. Webhook replay or out-of-order delivery
Network edge routers and proxies can deliver webhooks out of order or replay previously delivered payloads. If an agent processes a delayed approval.requested event after an approval.denied event, an improperly designed handler might reset the internal job state back to pending. Reject deliveries older than 300 seconds using the X-AgentDraft-Signature header. Treat the webhook as an alert to query authoritative state from GET /v1/approvals/{id}. Avoid updating financial balances solely from the webhook payload.
5. Agent process crashes mid-wait
If the background worker hosting your agent crashes, restarts, or deploys while an approval request is pending in the dashboard, the in-memory execution thread dies. Design the agent to persist its execution state to a persistent database before making the initial approval call. On service boot, query the database for pending approvals and poll AgentDraft's read endpoint or re-bind the webhook listeners to restore state gracefully.
A minimal implementation checklist for implementing human-in-the-loop for treasury AI agents
Work through these steps in order when implementing human-in-the-loop for treasury AI agents. Test each step in an isolated staging environment before enabling execution keys on live accounts.
- Generate an AgentDraft agent key with prefix
avs_live_. Confirm that its permissions containapprovals:requestand exclude sensitive operational write scopes. Test that this key cannot perform administrative dashboard modifications. - Write an explicit schema validator using Pydantic, Zod, or JSON Schema within your agent codebase. Verify that transaction values are formatted as integers in minor units, and verify that sensitive account numbers are tokenized or masked.
- Add the POST request to your agent's execution path. Capture and persist the returned
approval_idto your primary application database before yielding the thread. - Add an explicit settlement cut-off timestamp to the payload. If the webhook or poll response returns after this timestamp, mark the transaction expired and halt.
- Implement the HMAC-SHA256 signature verifier on your webhook endpoint. Send a test webhook with a simulated timestamp 301 seconds in the past, and confirm that your application rejects it with an HTTP 401 or 403.
- Connect the
approval.approvedexecution worker to a mock bank API. Dispatch the same approval webhook twice. Ensure the payment gateway receives the identicalcorrelation_idas an idempotency key and processes only a single debit. - Verify that your logging system captures the
approval_id, theevidence_hash, and the resulting banking confirmation number in a single unified index.
Signed webhook delivery is included on the Developer tier, so you can execute and validate this entire sequence without a corporate credit card. Remember that the policy logic deciding when to request approval must live in your agent codebase: AgentDraft handles the gate, the reviewer interface, and the audit trail, but does not provide an automated rules engine to decide which payments require review.
Frequently asked questions
When should a treasury AI agent require human approval?
An agent should require human approval before executing any irreversible state change. This includes all outbound payment rails (wires, ACH credit origination, FedNow, RTP), modifications to vendor beneficiary details, foreign exchange spot or forward contracts, and changes to payment authorization rules. Passive tasks like account reconciliation, ledger tagging, balance queries, and draft creation do not require human gates.
Should a treasury AI agent require approval for every payment, or only large ones?
During initial deployment, gate every payment regardless of size until your logging and reconciliation pipelines are proven in production. Once established, engineering teams define value thresholds directly in application code, such as auto-executing recurring utility invoices under specific limits while gating one-off disbursements. Because AgentDraft does not provide an automated policy engine based on amounts, you must code this threshold evaluation directly into your agent's tool-calling logic.
How does the agent wait for an approval without blocking the whole workflow?
Do not hold open synchronous HTTP connections or block execution worker threads while waiting for a human. When the agent submits an approval request, it receives an approval_id. The agent should write this ID and its current state to its database and yield its worker thread. When the reviewer acts in the dashboard, AgentDraft fires an approval.approved or approval.denied webhook. Your webhook handler matches the ID to the database record, loads the context, and resumes the workflow asynchronously.
What happens if the approver never responds before the payment cut-off?
The agent must monitor rail-specific clearing deadlines and treat an unanswered request past the cut-off window as expired. The execution worker must decline to submit late payments to the banking rail, transition the internal status to expired, and notify the treasury desk. Unanswered requests must not roll over into automated late execution without explicit re-authorization.
Can approvals be granted from Slack or email instead of the dashboard?
Approvals are decided in the AgentDraft dashboard. AgentDraft emails the workspace owner a notification linking to the queue, but the decision itself is made signed in: there are deliberately no approve-from-email links, because an unauthenticated one-click approve is an attack surface. Slack, Discord, Teams, SMS and push delivery are not available today. For operations involving movement of capital, requiring authenticated sign-in via WebAuthn passkeys provides necessary assurance of reviewer identity.
The gate is a state machine, not a checkbox
Treating human review as a loose chat prompt or an unverified email button creates severe vulnerabilities in corporate treasury systems. A human-in-the-loop gate is an asynchronous, cryptographically signed distributed state machine. It must sit before the irreversible payment network call, hold a comprehensive evidence payload, enforce cut-off deadlines, and join cleanly to downstream banking idempotency keys.
Design your system around the operational boundaries: your agent evaluates when to gate, a single authenticated human reviews the evidence in the dashboard, and an append-only log records the outcome. Implementing these controls keeps your autonomous workflows reliable, auditable, and secure when moving real money.
Create a sandbox for your gate on AgentDraft at https://agentdraft.io/pricing. AgentDraft's Developer tier is free with no card required: 1 seat, 3 agents, 1 mailbox, 1 connected calendar, 50 bookings a month, and 7-day audit retention.