For every AI-initiated financial action, log: 1) Agent session ID and model version. 2) The user instruction or system event that triggered the action. 3) Tool name called (e.g., "paystack_transfer"). 4) Parameters passed (recipient, amount, reason). 5) Paystack API response (transfer_code, status). 6) Timestamp. 7) Whether human review occurred and who approved. Store logs in append-only storage — never let the agent modify or delete its own logs.
// Append-only audit log for every AI-initiated financial action
async function logAgentAction({ sessionId, modelVersion, trigger, toolName, params, response, humanApproved = null }) {
// Write BEFORE executing the action — so even failures are logged
var logEntry = {
id: crypto.randomUUID(),
ts: new Date().toISOString(),
session_id: sessionId,
model_version: modelVersion, // e.g. "claude-opus-4-6"
trigger, // the instruction or event that caused this action
tool_name: toolName, // e.g. "paystack_transfer"
params: JSON.stringify(params), // recipient, amount, reason — serialized
response: null, // filled after execution
human_approved: humanApproved, // { by: "bonaventure@mctaba.com", ts: "..." } or null
status: 'pending',
};
var entry = await db.aiAuditLog.insert(logEntry);
return {
logId: entry.id,
// Call this after execution to record the outcome
recordOutcome: async (apiResponse, status) => {
await db.aiAuditLog.update(entry.id, {
response: JSON.stringify(apiResponse),
status, // 'success' | 'failed' | 'blocked'
});
},
};
}
// Usage: log before transfer, record outcome after
async function agentTransfer({ sessionId, modelVersion, trigger, recipientCode, amount }) {
var audit = await logAgentAction({
sessionId, modelVersion, trigger,
toolName: 'paystack_transfer',
params: { recipientCode, amount },
});
try {
var res = await fetch('https://api.paystack.co/transfer', {
method: 'POST',
headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY },
body: JSON.stringify({ source: 'balance', amount, recipient: recipientCode, reference: 'agent_' + audit.logId }),
});
var data = await res.json();
await audit.recordOutcome(data, data.status ? 'success' : 'failed');
return data;
} catch (err) {
await audit.recordOutcome({ error: err.message }, 'failed');
throw err;
}
}
CREATE OR REPLACE FUNCTION prevent_audit_modification()
RETURNS TRIGGER AS $$
BEGIN
RAISE EXCEPTION 'AI audit log entries cannot be modified or deleted';
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER no_audit_update BEFORE UPDATE ON ai_audit_log FOR EACH ROW EXECUTE FUNCTION prevent_audit_modification();
CREATE TRIGGER no_audit_delete BEFORE DELETE ON ai_audit_log FOR EACH ROW EXECUTE FUNCTION prevent_audit_modification();
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
See agentic payout safety rails for what to prevent before an action reaches the audit log.
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