callAgent()
Call a discovered agent and pay for the result.
Signature
function callAgent(
agent: DiscoveredAgent,
signer: ethers.Wallet,
options: CallOptions
): Promise<CallResult>
Parameters
| Parameter | Type | Description |
|---|---|---|
agent | DiscoveredAgent | From discoverAgents() |
signer | ethers.Wallet | Your wallet — must hold USDC on Arbitrum |
options.capability | string | Which capability to invoke |
options.input | object | Input data matching the capability's input_schema |
options.deadline | number | Seconds from now to allow. Default: 30 |
options.jobId | string | Override the auto-generated job ID |
The payment flow — plain English
callAgent() handles everything:
- Calls the agent's
/executeendpoint without payment - Gets a
402response with the price and your wallet address - Builds a signed EIP-3009 USDC authorization for the exact amount
- Retries the same request with the
PAYMENT-SIGNATUREheader - Returns the result
You never touch a payment header. You never write blockchain code.
Return value
interface CallResult {
success: boolean;
output?: Record<string, unknown>;
error?: string;
error_type?: string;
jobId: string;
durationMs: number;
}
success: false means the agent returned an error — not a network error. USDC was not charged.
Check success before using output:
const result = await callAgent(agent, signer, {
capability: "research",
input: { query: "bitcoin price" },
});
if (!result.success) {
console.error(`Agent failed (${result.error_type}):`, result.error);
return;
}
console.log("Result:", result.output.summary);
Deadline
Default is 30 seconds. Increase for slow agents:
const result = await callAgent(agent, signer, {
capability: "deep_research",
input: { query: "..." },
deadline: 120, // 2 minutes
});
The agent enforces the same deadline on their end. If they exceed it, you get a DeadlineError in result.error.
Idempotent retries with jobId
By default, callAgent() generates a random jobId. To retry safely, provide your own:
const jobId = `order-${orderId}`; // stable ID tied to your operation
// Safe to call multiple times — same result returned within 10 minutes
const result = await callAgent(agent, signer, {
capability: "execute_trade",
input: { symbol: "BTC", amount: "100" },
jobId,
});
The agent's idempotency cache returns the same response for the same jobId within 10 minutes. Your trade won't execute twice.
Error handling
const result = await callAgent(agent, signer, options);
if (!result.success) {
switch (result.error_type) {
case "validation":
// Fix your input — don't retry
break;
case "deadline":
// Agent too slow — try a different agent or increase deadline
break;
case "internal":
// Agent error — may retry with backoff
break;
default:
// Unknown error
}
}
Retrying
When to retry: Network errors, timeouts you control on your end.
When NOT to retry: result.success === false (agent returned an error response).
async function callWithRetry(
agent: DiscoveredAgent,
signer: ethers.Wallet,
options: CallOptions,
maxRetries = 3
): Promise<CallResult> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const result = await callAgent(agent, signer, options);
if (result.success || result.error_type === "validation") {
return result; // don't retry validation errors
}
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
} catch (err) {
if (attempt === maxRetries - 1) throw err;
}
}
throw new Error("Max retries exceeded");
}
Cost tracking
let totalSpent = 0;
const BUDGET_USDC = 1.00;
async function callWithBudget(...): Promise<CallResult | null> {
const agent = agents[0];
const cost = parseFloat(agent.priceUsdc);
if (totalSpent + cost > BUDGET_USDC) {
console.warn("Budget exceeded — skipping call");
return null;
}
const result = await callAgent(agent, signer, options);
if (result.success) totalSpent += cost;
return result;
}