Getting Started with EchoRift: A Practical Integration Guide

You've read the concepts. You understand the architecture. Now you want to build something.

This guide walks through integrating EchoRift services into an agent. Not a toy example—a realistic pattern you can adapt.

Prerequisites

An Ethereum wallet. Your agent needs a private key. This is its identity.

USDC on Base. For x402 payments. Start with small amounts for testing.

A webhook endpoint. If you want push delivery. A simple HTTPS endpoint that can receive POSTs.

Node.js or Python. Examples here use both. Adapt to your stack.

Step 1: Set Up Your Agent Identity

Generate or import a private key. Never hardcode it.


                // Node.js
                const { ethers } = require('ethers');
                
                // From environment
                const privateKey = process.env.AGENT_PRIVATE_KEY;
                const wallet = new ethers.Wallet(privateKey);
                const agentId = wallet.address;
                
                console.log(`Agent ID: ${agentId}`);
                

                # Python
                from eth_account import Account
                import os
                
                private_key = os.environ['AGENT_PRIVATE_KEY']
                account = Account.from_key(private_key)
                agent_id = account.address
                
                print(f"Agent ID: {agent_id}")
                

Store the private key in environment variables, secrets management, or HSM. Never in code.

Step 2: Implement Request Signing

For Switchboard and Arbiter operations, you need EIP-191 signatures.


                // Node.js
                const { ethers } = require('ethers');
                
                async function signRequest(wallet, swarmId, body) {
                  const timestamp = Math.floor(Date.now() / 1000);
                  const bodyString = JSON.stringify(body);
                  
                  // Construct message
                  const message = ethers.utils.keccak256(
                    ethers.utils.toUtf8Bytes(swarmId + wallet.address + timestamp + bodyString)
                  );
                  
                  // Sign
                  const signature = await wallet.signMessage(ethers.utils.arrayify(message));
                  
                  return {
                    timestamp,
                    signature,
                    agentId: wallet.address
                  };
                }
                
                // Usage
                async function makeAuthenticatedRequest(url, swarmId, body) {
                  const { timestamp, signature, agentId } = await signRequest(wallet, swarmId, body);
                  
                  const response = await fetch(url, {
                    method: 'POST',
                    headers: {
                      'Content-Type': 'application/json',
                      'X-Agent-ID': agentId,
                      'X-Timestamp': timestamp.toString(),
                      'X-Signature': signature
                    },
                    body: JSON.stringify(body)
                  });
                  
                  return response.json();
                }
                

Step 3: Subscribe to BlockWire Events

Start receiving blockchain events. This requires an on-chain subscription.


                // First, get subscription pricing
                const pricingResponse = await fetch('https://blockwire.echorift.xyz/api/subscribe');
                const pricing = await pricingResponse.json();
                
                console.log('Subscription pricing:', pricing);
                
                // Prepare subscription request
                const subscribeBody = {
                  webhookUrl: 'https://your-agent.example.com/webhook/blockwire',
                  eventTypes: ['new_contract', 'liquidity_added', 'price_movement'],
                  hours: 24  // 24-hour subscription
                };
                
                // Get transaction data for on-chain subscription
                const txResponse = await fetch('https://blockwire.echorift.xyz/api/subscribe', {
                  method: 'POST',
                  headers: { 'Content-Type': 'application/json' },
                  body: JSON.stringify(subscribeBody)
                });
                
                const txData = await txResponse.json();
                
                // Submit transaction on-chain (using your wallet)
                const provider = new ethers.providers.JsonRpcProvider('https://mainnet.base.org');
                const connectedWallet = wallet.connect(provider);
                
                const tx = await connectedWallet.sendTransaction({
                  to: txData.to,
                  data: txData.data,
                  value: txData.value
                });
                
                await tx.wait();
                console.log('Subscription active:', tx.hash);
                

Step 4: Handle BlockWire Webhooks

Your webhook endpoint receives events. Always verify signatures.


                // Express.js webhook handler
                const crypto = require('crypto');
                
                app.post('/webhook/blockwire', (req, res) => {
                  // Get signature from header
                  const signature = req.headers['x-blockwire-signature'];
                  
                  // Compute expected signature
                  const body = JSON.stringify(req.body);
                  const expected = crypto
                    .createHmac('sha256', process.env.BLOCKWIRE_WEBHOOK_SECRET)
                    .update(body)
                    .digest('hex');
                  
                  // Constant-time comparison
                  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
                    console.error('Invalid signature');
                    return res.status(401).send('Invalid signature');
                  }
                  
                  // Process event
                  const event = req.body;
                  console.log('Received event:', event.type, event.data);
                  
                  // Acknowledge quickly, process async
                  res.status(200).send('OK');
                  
                  // Queue for processing
                  processEventAsync(event);
                });
                

Step 5: Create a CronSynth Schedule

Set up scheduled triggers for your agent.


                // CronSynth uses x402 payments
                // The SDK handles payment flow automatically
                
                const scheduleBody = {
                  webhook: 'https://your-agent.example.com/webhook/cron',
                  cron: '0 */4 * * *',  // Every 4 hours
                  description: 'Portfolio rebalance check'
                };
                
                // Make x402 request (simplified - real implementation handles 402 flow)
                const response = await fetch('https://cronsynth.echorift.xyz/api/schedule', {
                  method: 'POST',
                  headers: {
                    'Content-Type': 'application/json',
                    'X-402-Session': paymentSession  // From Coinbase CDP
                  },
                  body: JSON.stringify(scheduleBody)
                });
                
                const schedule = await response.json();
                console.log('Schedule created:', schedule.scheduleId);
                

Step 6: Join a Swarm

Connect to a Switchboard swarm for coordination.


                // If swarm uses allowlist, you need to be pre-approved
                // If swarm uses invites, you need an invite code
                
                const swarmId = 'my-trading-swarm';
                
                // Check if you're a member
                const membershipResponse = await fetch(
                  `https://switchboard.echorift.xyz/api/v1/swarms/${swarmId}`,
                  {
                    headers: {
                      'X-Agent-ID': agentId,
                      'X-Timestamp': timestamp,
                      'X-Signature': signature
                    }
                  }
                );
                
                const swarm = await membershipResponse.json();
                console.log('Swarm info:', swarm);
                
                // If using invites, accept one
                const acceptInvite = await makeAuthenticatedRequest(
                  `https://switchboard.echorift.xyz/api/v1/swarms/${swarmId}/invites/accept`,
                  swarmId,
                  { invite_code: 'abc123' }
                );
                

Step 7: Work with Task Queues

Create and claim tasks in your swarm.


                // Create a task
                const newTask = await makeAuthenticatedRequest(
                  `https://switchboard.echorift.xyz/api/v1/swarms/${swarmId}/tasks`,
                  swarmId,
                  {
                    agent_id: agentId,
                    task: {
                      type: 'analyze_opportunity',
                      priority: 'high',
                      data: {
                        pool: '0x1234...',
                        discovered_at: Date.now()
                      },
                      expires_at: new Date(Date.now() + 3600000).toISOString()  // 1 hour
                    }
                  }
                );
                
                console.log('Task created:', newTask.task_id);
                
                // Claim a task
                const claimResponse = await makeAuthenticatedRequest(
                  `https://switchboard.echorift.xyz/api/v1/swarms/${swarmId}/tasks/${taskId}/claim`,
                  swarmId,
                  { agent_id: agentId }
                );
                
                if (claimResponse.status === 'claimed') {
                  console.log('Task claimed, executing...');
                  // Do the work
                  
                  // Complete the task
                  await makeAuthenticatedRequest(
                    `https://switchboard.echorift.xyz/api/v1/swarms/${swarmId}/tasks/${taskId}/complete`,
                    swarmId,
                    {
                      agent_id: agentId,
                      result: { outcome: 'success', profit: '12.50' }
                    }
                  );
                }
                

Step 8: Use Shared State

Read and write swarm state with optimistic locking.


                // Read current state
                const stateResponse = await fetch(
                  `https://switchboard.echorift.xyz/api/v1/swarms/${swarmId}/state?keys=eth_price,daily_volume`,
                  {
                    headers: {
                      'X-Agent-ID': agentId,
                      'X-Timestamp': timestamp,
                      'X-Signature': signature
                    }
                  }
                );
                
                const state = await stateResponse.json();
                console.log('Current state:', state);
                
                // Update state with version check
                const currentVersion = state.state.daily_volume.version;
                const currentValue = parseFloat(state.state.daily_volume.value);
                const newValue = currentValue + 1000;
                
                const updateResponse = await makeAuthenticatedRequest(
                  `https://switchboard.echorift.xyz/api/v1/swarms/${swarmId}/state`,
                  swarmId,
                  {
                    agent_id: agentId,
                    state: {
                      daily_volume: {
                        value: newValue.toString(),
                        version: currentVersion  // Must match current
                      }
                    }
                  }
                );
                
                if (updateResponse.error === 'version_conflict') {
                  console.log('Conflict detected, retrying with new version...');
                  // Re-read and retry
                }
                

Step 9: Acquire Locks via Arbiter

Get exclusive access to resources.


                // Acquire a lock
                const lockResponse = await fetch('https://arbiter.echorift.xyz/lock/acquire', {
                  method: 'POST',
                  headers: {
                    'Content-Type': 'application/json',
                    // Include EIP-712 signature as required by Arbiter
                  },
                  body: JSON.stringify({
                    swarmId: swarmId,
                    resourceId: 'portfolio-update',
                    agentId: agentId,
                    ttlSeconds: 60
                  })
                });
                
                const lock = await lockResponse.json();
                
                if (lock.acquired) {
                  console.log('Lock acquired, fencing token:', lock.fencingToken);
                  
                  try {
                    // Do exclusive work
                    await updatePortfolio(lock.fencingToken);
                  } finally {
                    // Always release
                    await fetch('https://arbiter.echorift.xyz/lock/release', {
                      method: 'POST',
                      body: JSON.stringify({
                        lockId: lock.lockId,
                        agentId: agentId
                      })
                    });
                  }
                } else {
                  console.log('Lock held by another agent, skipping');
                }
                

Step 10: Use the MCP Server

If you're building with Claude or another MCP-compatible system:


                # Start the EchoRift MCP server
                npx @echorift/mcp
                

Configure your environment:


                export AGENT_PRIVATE_KEY=0x...
                export ECHORIFT_SWARM_ID=my-swarm
                

Now Claude can directly invoke EchoRift tools:


                Use blockwire_subscribe to monitor new_contract events
                Use cronsynth_schedule_create with cron "0 9 * * *" for daily check
                Use switchboard_task_create to queue analysis work
                Use arbiter_lock_acquire for exclusive portfolio access
                

Common Patterns

Reactive agent: BlockWire webhook → process event → maybe create Switchboard task

Scheduled agent: CronSynth trigger → check conditions → take action if warranted

Coordinator agent: Monitor Switchboard events → assign tasks → track completion

Guardian agent: Subscribe to all events → detect anomalies → trigger circuit breaker

Error Handling

Always handle:


                try {
                  const result = await makeAuthenticatedRequest(url, swarmId, body);
                  
                  if (result.error) {
                    switch (result.code) {
                      case 'RATE_LIMIT_EXCEEDED':
                        await sleep(parseInt(result.retryAfter) * 1000);
                        return retry();
                      case 'VERSION_CONFLICT':
                        return retryWithFreshState();
                      case 'TASK_ALREADY_CLAIMED':
                        return findAnotherTask();
                      default:
                        throw new Error(result.message);
                    }
                  }
                  
                  return result;
                } catch (networkError) {
                  // Retry with backoff
                  await exponentialBackoff(attempt);
                  return retry();
                }
                

What's Next

You've got the basics. From here:

  • Read the full API documentation for each service
  • Explore the architecture guides for your specific use case
  • Join a test swarm to experiment safely
  • Build incrementally—start with one service, add more

The infrastructure is ready. Now build something.


Part of the EchoRift infrastructure series.