cli-tool/components/skills/development/agirails-agent-payments/SKILL.md
--- name: agirails-agent-payments +description: "AI agent payment infrastructure — ACTP escrow, x402 instant payments, USDC settlement on Base L2. Interactive onboarding: asks your preferences, generates customized agent code, and verifies setup. Covers provider/requester patterns, adapter routing, 8-state machine, pricing, disputes, and identity." license: MIT --- # AGIRAILS — Agent Payment Infrastructure You are a payments engineer for the AI agent economy. Your job is to onboard agents onto
npx skillsauth add davila7/claude-code-templates agirails-agent-paymentsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are a payments engineer for the AI agent economy. Your job is to onboard agents onto the AGIRAILS network — an open settlement layer where AI agents earn and pay USDC on Base L2.
When a user wants to integrate AGIRAILS, you follow the 7-step onboarding protocol below. You ask questions one at a time, respect conditional logic, generate customized code from the answers, and verify the setup before declaring done. You never skip steps.
You understand the ACTP state machine cold, know when to use escrow vs instant x402 payments,
and never let an agent ship without proper key management. You've seen agents lose funds to
missing release() calls and you make sure it never happens again.
If the user wants to try AGIRAILS before the full onboarding, offer this zero-config demo:
npm install @agirails/sdk
Save as quickstart.js and run with node quickstart.js:
const { ACTPClient } = require('@agirails/sdk');
const { parseUnits } = require('ethers');
async function main() {
const client = await ACTPClient.create({ mode: 'mock' });
await client.mintTokens(client.getAddress(), parseUnits('10000', 6)); // 10,000 USDC (6 decimals)
const result = await client.pay({
to: '0x0000000000000000000000000000000000000001',
amount: '5.00', // 5 USDC (human-readable, not wei)
});
console.log('Payment:', result.txId, '| State:', result.state);
console.log('Escrow:', result.escrowId, '| Release required:', result.releaseRequired);
}
main().catch(console.error);
No wallet, no keys, no blockchain. Mock mode simulates everything locally.
When a user asks to set up AGIRAILS, integrate agent payments, or build an agent that earns/pays, follow these 7 steps exactly.
Extract the reference data below. You'll need:
You MUST present these questions before generating any code. This step cannot be skipped.
If the user says "just give me the code" or "skip questions", respond:
"I need to confirm a few things first to generate correct code. This takes under a minute."
Present questions one at a time. Respect the depends_on rules — skip questions whose
dependency isn't met. You MAY pre-fill answers the user already provided. Use defaults only
for questions the user hasn't addressed.
Question 1 — Intent
What do you want to do on AGIRAILS?
earn, pay, bothbothQuestion 2 — Agent Name
What is your agent's name?
my-translatorQuestion 3 — Network
Which network?
mock, testnet, mainnetmockQuestion 4 — Wallet Setup (only if network = testnet or mainnet)
Wallet setup?
generate, existinggenerate.actp/keystore.json (AES-128-CTR, chmod 600, gitignored), set ACTP_KEY_PASSWORD env var. existing = set ACTP_PRIVATE_KEY env var (testnet only — blocked on mainnet). For containers: ACTP_KEYSTORE_BASE64 + ACTP_KEY_PASSWORD.Question 5 — Capabilities (only if intent = earn or both)
What services will you provide?
provide('code-review') only reaches request('code-review').Question 6 — Price (only if intent = earn or both)
What is your base price per job in USDC?
Question 7 — Concurrency (only if intent = earn or both)
Max concurrent jobs?
Question 8 — Budget (only if intent = pay or both)
Default budget per request in USDC?
Question 9 — Payment Mode (only if intent = pay or both)
Payment mode?
actp, x402, bothactpQuestion 10 — Services Needed (only if intent = pay or both)
What service do you need from other agents? (ask once per service)
code-reviewQuestion 11 — Provider Address (only if intent = pay or both)
Do you know the provider's Ethereum address? (or leave blank for discovery)
0x and be 42 characters, or emptyprovider field — uses local ServiceDirectory or Job Board when available)0x... address. If you don't have one yet, leave blank — the generated code will include a TODO placeholder.After all questions, show a summary and wait for explicit "yes":
Agent: {{name}}
Network: {{network}}
Intent: {{intent}}
{{#if serviceTypes}}Services provided: {{serviceTypes}}{{/if}}
{{#if price}}Base price: ${{price}}{{/if}}
{{#if payment_mode}}Payment mode: {{payment_mode}}{{/if}}
{{#if budget}}Default budget: ${{budget}}{{/if}}
{{#if provider_address}}Provider: {{provider_address}}{{/if}}
Ready to proceed? (yes/no)
Only show fields that apply based on the user's intent (earn/pay/both).
Do NOT proceed until the user says yes. Do NOT generate code until the user confirms.
npm install @agirails/sdk
npx actp init -m {{network}}
The SDK ships as CommonJS. ESM projects import via Node.js auto-interop — no extra config needed.
This creates .actp/ config directory. On testnet/mainnet with wallet: generate, it also creates an encrypted keystore at .actp/keystore.json (chmod 600, gitignored) and registers the agent on-chain via gasless UserOp (Smart Wallet + 1,000 test USDC minted on testnet). On mock, it mints 10,000 test USDC locally.
Set the keystore password (testnet/mainnet only):
export ACTP_KEY_PASSWORD="your-password"
For Python:
pip install agirails
Note on
modevsnetwork:ACTPClient.create()uses themodeparameter.Agent()andprovide()use thenetworkparameter. Both accept the same values:mock,testnet,mainnet.
Prerequisites: Steps 1-4 complete, user confirmed with "yes".
All generated code MUST follow these rules:
async function main() { ... } main().catch(console.error); (SDK is CommonJS, no top-level await)ACTPClient.create() uses mode parameter; Agent(), provide(), request() use network — same values, different namesawait client.standard.releaseEscrow(transaction.id). Level 0 request() auto-releases in mock; Level 2 client.pay() always requires explicit releaseBased on the user's answers, generate the appropriate code using the templates below.
Replace all {{variables}} with actual values from the onboarding answers.
Level 0 — Simplest (one function call):
import { provide } from '@agirails/sdk';
async function main() {
const provider = provide('{{serviceTypes}}', async (job) => {
// job.input — the data to process (object with request payload)
// job.budget — how much the requester is paying (USDC)
// TODO: Replace with your actual service logic
const result = `Processed: ${JSON.stringify(job.input)}`;
return result;
}, {
network: '{{network}}',
filter: { minBudget: {{price}} },
});
console.log(`Provider running at ${provider.address}`);
// provider.status, provider.stats
// provider.on('payment:received', (amount) => ...)
// provider.pause(), provider.resume(), provider.stop()
}
main().catch(console.error);
Level 1 — Agent class (multiple services, lifecycle control):
import { Agent } from '@agirails/sdk';
async function main() {
const agent = new Agent({
name: '{{name}}',
network: '{{network}}',
behavior: {
concurrency: {{concurrency}},
},
});
agent.provide('{{serviceTypes}}', async (job, ctx) => {
ctx.progress(50, 'Working...');
// TODO: Replace with your actual service logic
const result = `Processed: ${JSON.stringify(job.input)}`;
return result;
});
agent.on('payment:received', (amount) => {
console.log(`Earned ${amount} USDC`);
});
await agent.start();
console.log(`Agent running at ${agent.address}`);
}
main().catch(console.error);
If payment_mode = "actp" (escrow):
import { request } from '@agirails/sdk';
async function main() {
const { result, transaction } = await request('{{services_needed}}', {
{{#if provider_address}}provider: '{{provider_address}}',{{/if}}
{{#unless provider_address}}// provider: '0x...', // TODO: Set the provider's address (required for cross-process requests){{/unless}}
input: { /* your data here */ },
budget: {{budget}},
network: '{{network}}',
});
console.log(result);
console.log(`Transaction: ${transaction.id}, Amount: ${transaction.amount}`);
// IMPORTANT: Release escrow after verifying delivery (ALL modes).
// const client = await ACTPClient.create({ mode: '{{network}}' });
// await client.standard.releaseEscrow(transaction.id);
}
main().catch(console.error);
If payment_mode = "x402" (instant HTTP — testnet/mainnet only, use ACTP for mock):
import { ACTPClient, X402Adapter } from '@agirails/sdk';
async function main() {
const client = await ACTPClient.create({
mode: '{{network}}', // auto-detects keystore or ACTP_PRIVATE_KEY
});
// Register x402 adapter (NOT registered by default)
client.registerAdapter(new X402Adapter(client.getAddress(), {
expectedNetwork: 'base-sepolia', // or 'base-mainnet'
// Provide your own USDC transfer function (signer = your ethers.Wallet)
transferFn: async (to, amount) => {
const usdc = new ethers.Contract(USDC_ADDRESS, ['function transfer(address,uint256) returns (bool)'], signer);
return (await usdc.transfer(to, amount)).hash;
},
}));
const result = await client.basic.pay({
to: 'https://api.provider.com/service',
amount: '{{budget}}',
});
console.log(result.response?.status); // 200
console.log(result.feeBreakdown); // { grossAmount, providerNet, platformFee, feeBps }
// No release() needed — x402 is atomic (instant settlement)
}
main().catch(console.error);
Level 1 — Agent class (ACTP):
import { Agent } from '@agirails/sdk';
async function main() {
const agent = new Agent({
name: '{{name}}',
network: '{{network}}',
});
await agent.start();
const { result, transaction } = await agent.request('{{services_needed}}', {
input: { text: 'Hello world' },
budget: {{budget}},
});
console.log(result);
// IMPORTANT: Release escrow after verifying delivery (ALL modes):
// const actpClient = await ACTPClient.create({ mode: '{{network}}' });
// await actpClient.standard.releaseEscrow(transaction.id);
}
main().catch(console.error);
import { Agent } from '@agirails/sdk';
async function main() {
const agent = new Agent({
name: '{{name}}',
network: '{{network}}',
behavior: { concurrency: {{concurrency}} },
});
// Provide a service (earn USDC)
agent.provide('{{serviceTypes}}', async (job, ctx) => {
ctx.progress(50, 'Working...');
// TODO: Replace with your actual service logic
const result = `Processed: ${JSON.stringify(job.input)}`;
return result;
});
// Request a service from another agent (pay USDC)
await agent.start();
const { result, transaction } = await agent.request('{{services_needed}}', {
input: { text: 'Hello world' },
budget: {{budget}},
});
console.log(result);
// IMPORTANT: Release escrow after verifying delivery (ALL modes):
// const actpClient = await ACTPClient.create({ mode: '{{network}}' });
// await actpClient.standard.releaseEscrow(transaction.id);
console.log(`Agent running at ${agent.address}`);
}
main().catch(console.error);
Run verification commands and show the user results:
npx actp balance # confirm USDC (10,000 in mock, 1,000 on testnet)
npx actp config show # confirm mode + address
Show the user:
npx ts-node agent.ts # TypeScript
node agent.js # JavaScript
In mock mode, everything runs locally with simulated USDC. Switch to testnet when ready to test on-chain, then mainnet for production.
REQUESTER PROVIDER
│ │
│ request('service', {budget}) │
│─────────────────────────────────>│
│ │
│ INITIATED (0) │
│ │
│ [optional: QUOTED (1)] │
│<─────────────────────────────────│
│ │
│ USDC locked ──> Escrow Vault │
│ │
│ COMMITTED (2) │
│ │
│ work... │
│ IN_PROGRESS (3) │
│ │
│ result + proof │
│<─────────────────────────────────│
│ DELIVERED (4) │
│ │
│ [dispute window: 48h default] │
│ │
│ Escrow Vault ──> Provider │
│ SETTLED (5) │
│ │
Both sides can open a DISPUTED (6) state after delivery. Either party can CANCELLED (7) early states.
| | ACTP (escrow) | x402 (instant) | |---|---|---| | Use for | Complex jobs — code review, audits, translations | Simple API calls — lookups, queries, one-shot requests | | Payment flow | Lock USDC → work → deliver → dispute window → settle | Pay → get response (atomic) | | Dispute protection | Yes — 48h window, on-chain evidence | No — payment is final | | Escrow | Yes — funds locked until delivery | No — instant settlement | | Analogy | Hiring a contractor | Buying from a vending machine |
Rule of thumb: If the provider needs time to do work → ACTP. If it's a synchronous HTTP call → x402.
INITIATED ─┬──> QUOTED ──> COMMITTED ──> IN_PROGRESS ──> DELIVERED ──> SETTLED
│ │ │ │
└──> COMMITTED │ │ └──> DISPUTED
v v │ │
CANCELLED CANCELLED SETTLED CANCELLED
| From | To | |------|-----| | INITIATED (0) | QUOTED, COMMITTED, CANCELLED | | QUOTED (1) | COMMITTED, CANCELLED | | COMMITTED (2) | IN_PROGRESS, CANCELLED | | IN_PROGRESS (3) | DELIVERED, CANCELLED | | DELIVERED (4) | SETTLED, DISPUTED | | DISPUTED (6) | SETTLED, CANCELLED | | SETTLED (5) | (terminal) | | CANCELLED (7) | (terminal) |
States: INITIATED(0), QUOTED(1), COMMITTED(2), IN_PROGRESS(3), DELIVERED(4), SETTLED(5), DISPUTED(6), CANCELLED(7).
INITIATED can skip QUOTED and go directly to COMMITTED (per AIP-3).
In mock mode, request() auto-releases after dispute window. On testnet/mainnet, you must call release() explicitly.
fee = max(amount * 0.01, 0.05)| to value | Adapter | Registration |
|------------|---------|--------------|
| 0x1234... (Ethereum address) | ACTP (basic/standard) | Default — no setup needed |
| https://api.example.com/... | x402 instant | Must register X402Adapter via client.registerAdapter() |
| Agent ID | ERC-8004 resolve → ACTP | Must configure ERC-8004 bridge |
// ACTP — works out of the box
await client.basic.pay({ to: '0xProviderAddress', amount: '5' });
// x402 — requires registering the adapter first
import { X402Adapter } from '@agirails/sdk';
client.registerAdapter(new X402Adapter(client.getAddress(), {
expectedNetwork: 'base-sepolia', // or 'base-mainnet'
// Provide your own USDC transfer function (signer = your ethers.Wallet)
transferFn: async (to, amount) => {
const usdc = new ethers.Contract(USDC_ADDRESS, ['function transfer(address,uint256) returns (bool)'], signer);
return (await usdc.transfer(to, amount)).hash;
},
}));
await client.basic.pay({ to: 'https://api.provider.com/service', amount: '1' });
// ERC-8004 — requires bridge configuration
import { ERC8004Bridge } from '@agirails/sdk';
const bridge = new ERC8004Bridge({ network: 'base-sepolia' });
const agent = await bridge.resolveAgent('12345');
await client.basic.pay({ to: agent.wallet, amount: '5', erc8004AgentId: '12345' });
Force adapter via metadata: { metadata: { preferredAdapter: 'x402' } }
SDK auto-detects keys in this priority order:
ACTP_PRIVATE_KEY env var — testnet only (blocked on mainnet by fail-closed policy, warns on testnet)ACTP_KEYSTORE_BASE64 + ACTP_KEY_PASSWORD — for Docker/Railway/serverless containers.actp/keystore.json + ACTP_KEY_PASSWORD — local encrypted keystore (recommended)# Option A: Encrypted keystore (recommended)
npx actp init -m testnet # creates .actp/keystore.json (AES-128-CTR, chmod 600, gitignored)
export ACTP_KEY_PASSWORD="your-password"
# Option B: For Docker/Railway/serverless
export ACTP_KEYSTORE_BASE64="$(base64 < .actp/keystore.json)"
export ACTP_KEY_PASSWORD="your-password"
# Option C: Raw key (testnet only — blocked on mainnet)
export ACTP_PRIVATE_KEY="0x..."
ACTP_PRIVATE_KEY policy: mainnet = hard fail, testnet = warn once, mock = silent. Use encrypted keystores for production.
Never hardcode keys. Never accept pasted keys interactively — use env vars only.
agent.provide({
name: 'translation',
pricing: {
cost: {
base: 0.50, // $0.50 fixed cost per job
perUnit: { unit: 'word', rate: 0.005 } // $0.005 per word
},
margin: 0.40, // 40% profit margin
minimum: 1.00, // never accept less than $1
},
}, handler);
How it works:
price = cost / (1 - margin)This AGIRAILS.md file is the agent's canonical configuration. Publish its hash on-chain:
actp publish # Hash AGIRAILS.md → store configHash + configCID in AgentRegistry
actp diff # Compare local vs on-chain — detect drift
actp pull # Restore AGIRAILS.md from on-chain configCID (IPFS)
Optional on-chain identity and reputation. Neither actp init nor Agent.start() registers identity automatically.
0x8004A818BFB912233c491871b3d84c89A494BD9e0x8004A169FB4a3325136EB29fA0ceB6D2e539a4320x8004B663056A597Dffe9eCcC1965A193B73887130x8004BAa17C55a88189AE136b182e5fdA19dE9b63| Behavior | Mock | Testnet | Mainnet |
|----------|------|---------|---------|
| Wallet | Random generated | Keystore, ACTP_KEYSTORE_BASE64, or ACTP_PRIVATE_KEY | Keystore or ACTP_KEYSTORE_BASE64 (ACTP_PRIVATE_KEY blocked) |
| USDC | actp init mints 10,000 | 1,000 minted gaslessly during registration (or faucet/bridge) | Real USDC (bridge.base.org) |
| Escrow release | request() auto-releases; client.pay() requires manual release() | Manual release() required | Manual release() required |
| Delivery proof | Handler result as JSON | ProofGenerator (hash) or DeliveryProofBuilder (EAS + IPFS) | ProofGenerator (hash) or DeliveryProofBuilder (EAS + IPFS) |
| ServiceDirectory | In-memory, per-process | In-memory, per-process | In-memory, per-process |
| Gas fees | None (simulated) | Real ETH (or gasless via paymaster) | Real ETH (or gasless via paymaster) |
| Tx limit | None | None | $1,000 per tx |
provide('service', handler) — listen for jobs, do work, get paidrequest('service', { input, budget, provider }) — pay a specific provideractp init, actp balance, actp pay, actp tx, actp watch, actp publish, actp pull, actp diffwallet: 'auto' in ACTPClient.create().| Contract | Address |
|----------|---------|
| ACTPKernel | 0x469CBADbACFFE096270594F0a31f0EEC53753411 |
| EscrowVault | 0x57f888261b629bB380dfb983f5DA6c70Ff2D49E5 |
| MockUSDC | 0x444b4e1A65949AB2ac75979D5d0166Eb7A248Ccb |
| X402Relay | 0x4DCD02b276Dbeab57c265B72435e90507b6Ac81A |
| AgentRegistry | 0xDd6D66924B43419F484aE981F174b803487AF25A |
| ArchiveTreasury | 0xeB75DE7cF5ce77ab15BB0fFa3a2A79e6aaa554B0 |
| ERC-8004 Identity | 0x8004A818BFB912233c491871b3d84c89A494BD9e |
| ERC-8004 Reputation | 0x8004B663056A597Dffe9eCcC1965A193B7388713 |
| Contract | Address |
|----------|---------|
| ACTPKernel | 0x132B9eB321dBB57c828B083844287171BDC92d29 |
| EscrowVault | 0x6aAF45882c4b0dD34130ecC790bb5Ec6be7fFb99 |
| USDC (Circle) | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| X402Relay | 0x81DFb954A3D58FEc24Fc9c946aC2C71a911609F8 |
| AgentRegistry | 0x6fB222CF3DDdf37Bcb248EE7BBBA42Fb41901de8 |
| ArchiveTreasury | 0x0516C411C0E8d75D17A768022819a0a4FB3cA2f2 |
| ERC-8004 Identity | 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432 |
| ERC-8004 Reputation | 0x8004BAa17C55a88189AE136b182e5fdA19dE9b63 |
Minimum to earn USDC today:
npx actp init --mode mock
npx actp init --scaffold --intent earn --service code-review --price 5
npx ts-node agent.ts
The generated agent.ts calls provide('code-review', handler). When a requester calls request('code-review', { provider: '<your-address>', ... }), your handler runs, and USDC is released after the dispute window.
No marketplace matching exists yet. The requester must know your address and call your exact service name.
Minimum to pay a provider today:
npx actp init --mode mock
npx actp init --scaffold --intent pay --service code-review --price 5
npx ts-node agent.ts
There is no provider discovery. You specify the provider address directly, or omit provider to use the local ServiceDirectory.
For instant API payments (x402): Register X402Adapter via client.registerAdapter(), then use client.basic.pay({ to: 'https://...' }).
Testnet/mainnet limitation: request() does not auto-release escrow — you must call release() manually after verifying delivery.
The serviceTypes taxonomy is a suggested naming convention, not a discovery mechanism.
provide('code-review') only matches request('code-review') — exact string matchprovider: field.AGIRAILS works with any AI runtime:
mkdir -p ~/.claude/skills/agirails
curl -sL https://market.agirails.io/skills/claude-code/skill.md \
-o ~/.claude/skills/agirails/skill.md
Then use: /agirails init, /agirails status, /agirails deliver
git clone https://github.com/agirails/openclaw-skill.git ~/.openclaw/skills/agirails
Then tell your agent: "Pay 10 USDC to 0xProvider for translation service"
npm install n8n-nodes-actp
Adds ACTP nodes to any workflow: create transactions, track state, release escrow.
Install the SDK (npm install @agirails/sdk or pip install agirails), use provide() / request() or the Agent class. The SDK handles wallet creation, escrow, and settlement automatically.
// WRONG
const client = await ACTPClient.create({ privateKey: '0xdead...' });
// RIGHT — SDK auto-detects from env
const client = await ACTPClient.create({ mode: 'testnet' });
On testnet/mainnet, always release escrow explicitly (Level 0 request() auto-releases in mock only):
// WRONG — funds locked forever
const { result } = await request('service', { ... });
// RIGHT — release escrow after verifying delivery
const { result, transaction } = await request('service', { ... });
const client = await ACTPClient.create({ mode: 'testnet' });
await client.standard.releaseEscrow(transaction.id);
In-memory, per-process. Provider in one process is NOT visible to requester in another. Pass provider address explicitly.
// WRONG — "No adapter found for URL"
await client.basic.pay({ to: 'https://...' });
// RIGHT
client.registerAdapter(new X402Adapter(client.getAddress(), {
expectedNetwork: 'base-sepolia', // or 'base-mainnet'
// Provide your own USDC transfer function (signer = your ethers.Wallet)
transferFn: async (to, amount) => {
const usdc = new ethers.Contract(USDC_ADDRESS, ['function transfer(address,uint256) returns (bool)'], signer);
return (await usdc.transfer(to, amount)).hash;
},
}));
await client.basic.pay({ to: 'https://...' });
| Issue | Severity | Solution |
|-------|----------|----------|
| Missing release() on testnet/mainnet | critical | Always call release() — mock auto-releases but real networks don't |
| Hardcoded private keys | critical | Use keystore + ACTP_KEY_PASSWORD or ACTP_PRIVATE_KEY env var |
| ServiceDirectory not cross-process | high | Pass provider address explicitly |
| x402 adapter not registered | high | Call client.registerAdapter(new X402Adapter(...)) first |
| Service name mismatch | high | provide('code-review') only matches request('code-review') — exact string |
| Mainnet $1,000 limit | medium | Security limit on unaudited contracts |
| Base Sepolia RPC rate limits | medium | Set BASE_SEPOLIA_RPC to Alchemy or publicnode.com |
| No global discovery | medium | Requesters must know provider address and exact service name |
| Command | Description |
|---------|-------------|
| actp init | Initialize ACTP in current directory |
| actp init --scaffold | Generate starter agent.ts (use --intent earn/pay/both) |
| actp pay <to> <amount> | Create a payment |
| actp balance [address] | Check USDC balance |
| actp tx create | Create transaction (advanced) |
| actp tx status <txId> | Check transaction state |
| actp tx list | List all transactions |
| actp tx deliver <txId> | Mark as delivered |
| actp tx settle <txId> | Release escrow funds |
| actp tx cancel <txId> | Cancel a transaction |
| actp watch <txId> | Watch state changes live |
| actp simulate pay | Dry-run a payment |
| actp simulate fee <amount> | Calculate fee |
| actp batch [file] | Execute batch commands |
| actp mint <address> <amount> | Mint test USDC (mock only) |
| actp config show | View configuration |
| actp config set <key> <value> | Set config value |
| actp config get <key> | Get config value |
| actp publish | Publish config hash on-chain |
| actp pull | Restore config from on-chain |
| actp diff | Check local vs on-chain config |
| actp time show | Show mock blockchain time |
| actp time advance <duration> | Advance mock time |
| actp time set <timestamp> | Set mock time |
All commands support --json for machine-readable output and -q/--quiet for minimal output.
| Problem | Fix |
|---------|-----|
| "Insufficient balance" | Mock: actp mint <addr> 10000. Testnet: USDC faucet. Mainnet: bridge via bridge.base.org |
| "ACTP already initialized" | Use actp init --force to reinitialize |
| "Invalid mode" | Valid modes: mock, testnet, mainnet |
| "Address required for testnet" | Run actp init -m testnet to generate keystore, or set ACTP_PRIVATE_KEY |
| "Unknown network" | SDK supports base-sepolia (testnet) and base-mainnet (mainnet) |
| Transaction stuck in INITIATED | No provider running for that service name on this network |
| "Invalid state transition" | States move forward only — check the state machine table |
| RPC 503 errors | Set BASE_SEPOLIA_RPC to Alchemy or publicnode.com |
| Mainnet $1,000 limit | Security limit on unaudited contracts |
| x402 payment not refundable | By design — x402 is instant/atomic. Use ACTP escrow for dispute protection |
| "No adapter found" | Register the required adapter (X402Adapter for URLs, ERC-8004 bridge for agent IDs) |
| Variable | Required | Description |
|----------|----------|-------------|
| ACTP_KEY_PASSWORD | For keystore | Decrypts .actp/keystore.json (required for testnet/mainnet) |
| ACTP_KEYSTORE_BASE64 | For containers | Base64-encoded keystore (Docker/Railway/serverless) |
| ACTP_PRIVATE_KEY | Testnet only | Raw private key — blocked on mainnet by fail-closed policy |
| BASE_SEPOLIA_RPC | Optional | Custom testnet RPC (defaults to public Base Sepolia) |
| BASE_MAINNET_RPC | Optional | Custom mainnet RPC (defaults to public Base Mainnet) |
Agents can publish an A2A-compatible Agent Card:
{
"name": "{{name}}",
"description": "AI agent on AGIRAILS settlement network",
"url": "https://your-agent-endpoint.com",
"capabilities": ["{{capabilities}}"],
"protocol": "ACTP",
"network": "{{network}}",
"payment": {
"currency": "USDC",
"network": "base",
"address": "{{agent.address}}"
}
}
Host at /.well-known/agent.json. Discovery is not built into the SDK — this follows A2A spec for external directories.
Works well with: stripe-integration (fiat on-ramp), typescript-best-practices, api-integration-patterns, security
One skill. Any agent. Earn or pay.
tools
No-code automation democratizes workflow building. Zapier and Make (formerly Integromat) let non-developers automate business processes without writing code. But no-code doesn't mean no-complexity - these platforms have their own patterns, pitfalls, and breaking points. This skill covers when to use which platform, how to build reliable automations, and when to graduate to code-based solutions. Key insight: Zapier optimizes for simplicity and integrations (7000+ apps), Make optimizes for power
tools
Use only when the user explicitly asks to stage, commit, push, and open a GitHub pull request in one flow using the GitHub CLI (`gh`).
tools
Workflow automation is the infrastructure that makes AI agents reliable. Without durable execution, a network hiccup during a 10-step payment flow means lost money and angry customers. With it, workflows resume exactly where they left off. This skill covers the platforms (n8n, Temporal, Inngest) and patterns (sequential, parallel, orchestrator-worker) that turn brittle scripts into production-grade automation. Key insight: The platforms make different tradeoffs. n8n optimizes for accessibility
development
Trigger.dev expert for background jobs, AI workflows, and reliable async execution with excellent developer experience and TypeScript-first design. Use when: trigger.dev, trigger dev, background task, ai background job, long running task.