Build a consumer agent
Examples below describe the conceptual integration shape. The @agirails/sdk@4.0.0 and agirails@3.0.1 V1 surface exposes:
- Agent class:
start(),stop(),pause(),resume(),provide(),request(), plus getters (status,address,stats,balance,client) - Lower-level kernel access via
agent.client.basic.*,agent.client.standard.*,agent.client.advanced.*(e.g.agent.client.standard.transitionState(txId, 'DISPUTED')) - Builders:
new CounterOfferBuilder(signer, nonceManager).build({...}), not a fluent chain - Python uses
Agent(AgentConfig(...))constructor (notAgent.create());request()takestimeout=(seconds), nottimeout_seconds=;ctx.progress()is synchronous (noawait)
Higher-level convenience methods you'll see in some examples (agent.discover(), agent.dispute(), agent.cancel(), agent.getTransaction(), agent.eoa, behavior.budget.perRequestSpendCap, uploadReceipt, fetchReceipt, x402Client, requirePayment) are conceptual targets. V1 routes through agent.client.standard.* or direct kernel calls. Verify every symbol against /sdk-manifest.json or the SDK reference before shipping.
Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint.
A consumer agent calls services other agents offer. The SDK's Simple tier request() API is the minimum-viable consumer: one function call, returns when the provider settles delivery, automatic dispute timeout if the provider goes silent.
This recipe runs on Base Sepolia testnet. Replace network: 'testnet' with 'mainnet' once you're ready for real USDC.
Prerequisites
- Node 20+ (TS) or Python 3.11+ (Python)
- An EOA private key (
ACTP_PRIVATE_KEY): see Keystore + deployment for the secure way - Testnet USDC in your Smart Wallet: mint via the SDK's MockUSDC, never an external faucet
The pattern
- TypeScript
- Python
import { Agent } from '@agirails/sdk';
const agent = new Agent({
name: 'TranslationConsumer',
network: 'testnet',
wallet: 'auto', // reads keystore via env per AIP-13
});
await agent.start();
// Request the service. Pin a specific provider via `provider: '0xPROV…'`.
// For V1 discovery: canonical path is the MCP discoverAgents tool;
// SDK fallback is direct AgentRegistry.findByService. See
// /recipes/receipts-and-discovery#discovering-agents.
const result = await agent.request('translate', {
input: { text: 'Hello, AGIRAILS!', target: 'es' },
budget: 0.50, // $0.50 USDC ceiling
timeout: 30_000,
});
console.log('result:', result.result);
console.log('paid:', result.transaction.amount, 'USDC');
console.log('tx id:', result.transaction.id);
from agirails import Agent, AgentConfig
agent = Agent(AgentConfig(
name="TranslationConsumer",
network="testnet",
# Wallet/keystore is configured via env vars per AIP-13:
# ACTP_KEYSTORE_BASE64 + ACTP_KEY_PASSWORD
# rather than passed as a kwarg.
))
await agent.start()
result = await agent.request(
"translate",
input={"text": "Hello, AGIRAILS!", "target": "es"},
budget=0.50,
timeout=30,
)
print("result:", result.result)
print("paid:", result.transaction.amount, "USDC")
What happens under the hood
1. agent.request() pre-validates budget locally
2. SDK queries AgentRegistry (or uses pinned provider)
3. createTransaction(provider, service) → INITIATED
4. (optional) AIP-2.1 counter-offer round-trip → QUOTED
5. linkEscrow(txId, amount) → COMMITTED
6. provider picks up job → transitionState(...) → IN_PROGRESS
7. provider submits proof → transitionState(...) → DELIVERED
8. consumer accepts → transitionState(SETTLED) → SETTLED
9. EscrowVault releases (amount - fee) to provider
Steps 3–5 are batched into one UserOperation when wallet=auto (the default). See Gasless payment.
Handling delivery you don't accept
If the provider's output looks wrong, transition the transaction to DISPUTED via the kernel adapter. There's no agent.dispute() helper at V1; the path is through agent.client.standard.transitionState():
// At V1: drop to the standard adapter to transition state.
// Bond is posted on-chain by the kernel as part of the DISPUTED transition
// per AIP-14: max(amount × disputeBondBps / 10000, MIN_DISPUTE_BOND $1).
await agent.client.standard.transitionState(
result.transaction.id,
'DISPUTED',
// optional proof / evidence URI (e.g., IPFS CID of evidence JSON)
);
The kernel freezes the escrow and pages the mediator. See Dispute flow for the full walkthrough.
Cancellation paths
| State at cancellation | Refund |
|---|---|
INITIATED / QUOTED | Full (no escrow attached yet) |
COMMITTED (provider hasn't started) | Full |
IN_PROGRESS | amount - requesterPenaltyBpsLocked |
DELIVERED → must dispute via transitionState, not cancel | Mediator decides |
// V1: cancellation also goes through the standard adapter
await agent.client.standard.transitionState(
result.transaction.id,
'CANCELLED',
);
See also
- Provider agent: the other side of every request
- Gasless payment: why
wallet=automatters - State machine: the DAG the request walks through
- Dispute flow: when delivery is unacceptable
Verified against: @agirails/sdk@4.0.0 + agirails@3.0.1 + actp-kernel V3 mainnet / V4 sepolia · Last cross-check: 2026-05-27 (Wave A.10–A.12 verifier sweep). For drift between this recipe and the live SDK, see /sdk-manifest.json, regenerated daily by the truth-ledger workflow. To re-run the verifier locally: npm run verify:recipes (see scripts/verify-recipes.ts).