Build a read-only Paystack query agent: 1) Define tools: list_transactions (GET /transaction with filters), get_transaction (GET /transaction/:id), search_by_email (GET /transaction?customer=email). 2) Pass tools to Claude API. 3) When Claude calls a tool, execute it server-side using PAYSTACK_SECRET_KEY from env — the key never reaches the LLM. 4) Return tool results to the LLM for synthesis. Keep the agent strictly read-only. Do not include transfer, refund, or charge tools until you have safety rails in place.
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic();
// Tool definitions — the LLM sees these, not the key
const tools = [
{
name: 'list_transactions',
description: 'List Paystack transactions with optional filters. Returns array of transaction objects.',
input_schema: {
type: 'object',
properties: {
status: { type: 'string', enum: ['success', 'failed', 'pending'] },
from: { type: 'string', description: 'ISO date string, e.g. 2026-07-01' },
to: { type: 'string', description: 'ISO date string' },
perPage: { type: 'number', description: 'Results per page, max 100' },
},
},
},
{
name: 'get_transaction',
description: 'Get a single Paystack transaction by ID.',
input_schema: {
type: 'object',
properties: { id: { type: 'number' } },
required: ['id'],
},
},
{
name: 'search_by_email',
description: 'Find all transactions from a specific customer email.',
input_schema: {
type: 'object',
properties: { email: { type: 'string' } },
required: ['email'],
},
},
];
// Server-side execution — key never leaves this function
async function executeTool(toolName, toolInput) {
var headers = { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY };
if (toolName === 'list_transactions') {
var params = new URLSearchParams(toolInput).toString();
var res = await fetch('https://api.paystack.co/transaction?' + params, { headers });
return (await res.json()).data;
}
if (toolName === 'get_transaction') {
var res = await fetch('https://api.paystack.co/transaction/' + toolInput.id, { headers });
return (await res.json()).data;
}
if (toolName === 'search_by_email') {
var res = await fetch('https://api.paystack.co/transaction?customer=' + toolInput.email, { headers });
return (await res.json()).data;
}
}
// Agent loop
async function runPaystackAgent(userQuestion) {
var messages = [{ role: 'user', content: userQuestion }];
while (true) {
var response = await client.messages.create({
model: 'claude-opus-4-6',
max_tokens: 1024,
tools,
messages,
});
if (response.stop_reason === 'end_turn') {
return response.content.find(b => b.type === 'text')?.text;
}
if (response.stop_reason === 'tool_use') {
// Execute all tool calls
var toolResults = [];
for (var block of response.content) {
if (block.type === 'tool_use') {
var result = await executeTool(block.name, block.input);
toolResults.push({ type: 'tool_result', tool_use_id: block.id, content: JSON.stringify(result) });
}
}
messages.push({ role: 'assistant', content: response.content });
messages.push({ role: 'user', content: toolResults });
}
}
}
Example usage:
var answer = await runPaystackAgent('How much revenue did we collect last week, broken down by success vs failed?');
console.log(answer);
// → "Last week you processed NGN 1,247,500 in successful transactions (47 payments) and NGN 83,200 in failed attempts (12 payments)."
See why you must never give the LLM your secret key for the security reasoning behind this pattern.
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