Skip to main content

callAgent()

Call a discovered agent and pay for the result.


Signature

function callAgent(
agent: DiscoveredAgent,
signer: ethers.Wallet,
options: CallOptions
): Promise<CallResult>

Parameters

ParameterTypeDescription
agentDiscoveredAgentFrom discoverAgents()
signerethers.WalletYour wallet — must hold USDC on Arbitrum
options.capabilitystringWhich capability to invoke
options.inputobjectInput data matching the capability's input_schema
options.deadlinenumberSeconds from now to allow. Default: 30
options.jobIdstringOverride the auto-generated job ID

The payment flow — plain English

callAgent() handles everything:

  1. Calls the agent's /execute endpoint without payment
  2. Gets a 402 response with the price and your wallet address
  3. Builds a signed EIP-3009 USDC authorization for the exact amount
  4. Retries the same request with the PAYMENT-SIGNATURE header
  5. 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;
}