Human in the Loop Design for AI Payment Operations
Human-in-the-loop pattern: 1) AI agent proposes the action with its reasoning. 2) Proposal goes to human reviewer (Slack message with Approve/Reject buttons, or an admin approval queue). 3) Human approves → agent executes via the Paystack API. Human rejects → agent receives rejection and informs the user. Operations requiring human approval: any transfer above NGN 50,000, new bank recipient creation, bulk payouts, refunds to different accounts than the original payment. Operations that can be automated: payment link creation, subscription status queries, transaction lookups, refunds below threshold for verified customers.
Approval Gate Design
| Operation | Automation Level | Rationale |
|---|---|---|
| Transaction lookup | Full auto | Read-only, no financial impact |
| Payment link creation | Full auto | Customer still chooses to pay; reversible |
| Subscription status check | Full auto | Read-only |
| Refund < NGN 5,000 (verified customer) | Auto with audit log | Low value, reversible |
| Refund > NGN 5,000 | Human approval required | Material value, irreversible |
| Transfer to allowlist recipient | Human approval above threshold | Irreversible, financial impact |
| New recipient creation | Always human approval | Expands allowlist — high security impact |
| Bulk payout | Always human approval | Multiplied financial impact |
| Subscription cancellation | Human approval (churn opportunity) | Revenue impact, opportunity to retain |
// Approval gate middleware — wraps any write tool
async function withApproval({ action, params, threshold, agentSessionId, channel = '#payments-approvals' }) {
// Check if this action requires approval based on params
var requiresApproval = action === 'transfer' && params.amount > threshold
|| action === 'refund' && params.amount > 5000_00
|| action === 'create_recipient' // always requires approval
|| action === 'bulk_transfer'; // always requires approval
if (!requiresApproval) {
return { approved: true, auto: true };
}
// Queue for human approval
var requestId = crypto.randomUUID();
await db.pendingApprovals.insert({ requestId, action, params, agentSessionId, status: 'pending', created_at: new Date() });
// Notify via Slack with interactive buttons
await sendSlackInteractiveMessage(channel, {
text: 'AI payment action requires approval',
blocks: buildApprovalMessage(action, params, requestId),
});
// Poll for approval (max 30 minutes, then escalate)
return new Promise((resolve) => {
var pollInterval = setInterval(async () => {
var record = await db.pendingApprovals.get(requestId);
if (record.status === 'approved') {
clearInterval(pollInterval);
resolve({ approved: true, approvedBy: record.approved_by, auto: false });
}
if (record.status === 'rejected') {
clearInterval(pollInterval);
resolve({ approved: false, rejectedBy: record.rejected_by });
}
}, 5000);
// Escalate after 30 minutes without decision
setTimeout(() => {
clearInterval(pollInterval);
resolve({ approved: false, reason: 'approval_timeout' });
}, 30 * 60 * 1000);
});
}
Learn More
See audit trails for AI financial actions for logging approved actions.
Key Takeaways
- ✓AI proposes, human approves, AI executes — the three-step pattern for any irreversible payment action.
- ✓Build the approval gate at the tool layer, not the prompt layer — so it cannot be bypassed by prompt manipulation.
- ✓Use Slack interactive messages or an admin UI queue for approval — out-of-band channels are more secure than in-chat confirmation.
- ✓Set clear SLAs for human review — if approval takes too long, the AI should escalate, not auto-proceed.
- ✓Log every proposal, approval, rejection, and execution with timestamps and approver identity.
Frequently Asked Questions
- What if the human reviewer is unavailable and the action is time-sensitive?
- Design an escalation path: if the primary approver has not responded in X minutes, escalate to a secondary approver or a manager. If neither responds within the timeout window, the action is rejected automatically and the user is notified that manual intervention is required. Never auto-proceed after timeout — the absence of approval is a rejection by default.
- Can the AI agent provide a recommendation alongside the approval request?
- Yes — and it should. The approval request should include: what action is proposed, the AI's reasoning, relevant context (customer history, normal pattern for this type of transaction), and any risk signals. A well-contextualized approval request takes a human reviewer 30 seconds instead of 5 minutes. The AI's job is to make human review faster, not to push humans to approve blindly.
- Is in-chat approval (customer says "confirm") secure enough for payment actions?
- Not for significant amounts. In-chat confirmation can be bypassed by prompt injection — a crafted user message might manipulate the agent into treating an earlier message as a confirmation. Out-of-band approval (Slack message to your team, admin UI button) is more secure because it requires a human with system access to take action, not just a message in the same conversation thread.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon