Tiered tool design: Tier 1 (auto-execute): get_transaction, get_subscription_status, list_customer_payments — safe to run without human review. Tier 2 (require human confirmation): initiate_refund, cancel_subscription, create_payment_link for existing debt — agent proposes, human approves, agent executes. Never let write tools auto-execute in a customer service context. Confirmation can be a Slack approval message, admin UI queue, or explicit customer "confirm" reply in the same chat thread, depending on the action's reversibility.
// Tier 1: auto-execute (read-only)
var readTools = [
{ name: 'get_transaction', description: 'Get transaction details by reference.' /* ... */ },
{ name: 'get_subscription_status', description: 'Check subscription status.' /* ... */ },
{ name: 'list_recent_payments', description: 'List customer's recent payments.' /* ... */ },
];
// Tier 2: require approval (write operations)
var writeTools = [
{
name: 'request_refund',
description: 'Request a refund for a transaction. Queues for human approval — does not execute immediately.',
input_schema: {
type: 'object',
properties: {
transaction_reference: { type: 'string' },
reason: { type: 'string' },
amount: { type: 'number', description: 'Amount in kobo. Omit for full refund.' },
},
required: ['transaction_reference', 'reason'],
},
},
{
name: 'request_subscription_cancellation',
description: 'Request cancellation of a subscription. Queues for human approval.',
input_schema: {
type: 'object',
properties: { subscription_code: { type: 'string' }, reason: { type: 'string' } },
required: ['subscription_code', 'reason'],
},
},
];
// Execute write tools — always queue, never execute immediately
async function executeWriteTool(toolName, input, customerId, agentSessionId) {
var requestId = await db.pendingActions.insert({
tool: toolName,
input,
customer_id: customerId,
agent_session_id: agentSessionId,
status: 'awaiting_approval',
created_at: new Date(),
});
// Notify human team via Slack
await sendSlackMessage({
channel: '#support-approvals',
text: 'AI refund request from customer ' + customerId + ': ' + JSON.stringify(input),
blocks: [
{ type: 'section', text: { type: 'mrkdwn', text: '*Action:* ' + toolName + '
*Params:* ' + JSON.stringify(input) } },
{
type: 'actions',
elements: [
{ type: 'button', text: { type: 'plain_text', text: 'Approve' }, value: requestId, action_id: 'approve_action' },
{ type: 'button', text: { type: 'plain_text', text: 'Reject' }, value: requestId, action_id: 'reject_action', style: 'danger' },
],
},
],
});
return { status: 'queued', requestId, message: 'Your request has been submitted and will be processed within 2 business hours.' };
}
When the human approves via Slack, your Slack webhook handler executes the actual Paystack API call and updates the customer.
See human-in-the-loop design for AI payment operations for the broader framework.
KES 2,999
The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.
Start Paystack Course