When you're building autonomous agents with LangChain, cellular connectivity is rarely part of the design conversation—until your agent needs to operate outside WiFi range, scrape region-locked data, or maintain uptime during infrastructure failures. Traditional eSIM APIs require pre-registration, API keys, and monthly billing cycles. Your agent can't purchase connectivity the moment it needs it.
The x402 protocol—HTTP 402 Payment Required extended with stablecoin facilitators—lets LangChain agents buy eSIM data packages in real time. The agent makes a standard HTTP request, receives a 402 challenge with a Polygon USDC payment address, sends the stablecoin, and gets the eSIM activation payload. No developer account, no credit card on file, no human in the loop.
This tutorial walks through integrating x402 eSIM purchases into a LangChain agent. You'll build a custom tool that lets your agent autonomously provision cellular connectivity when WiFi fails, then use that eSIM to continue executing tasks. We're using Python 3.11, LangChain 0.2.x, and the Coinbase x402 facilitator on Polygon.
Why LangChain Needs x402 for eSIM
LangChain agents typically assume stable internet via the host machine's default network interface. That breaks in three scenarios:
- Mobility edge cases: your agent runs on a Raspberry Pi in a vehicle, field deployment, or pop-up retail environment where WiFi isn't guaranteed.
- Geographic bypass: the agent needs an IP address in a specific country to access region-locked APIs or test localized features.
- Failover resilience: the primary network drops; the agent needs a backup path to call external tools (weather APIs, payment gateways, inference endpoints).
Traditional eSIM providers (Twilio, 1NCE, Soracom) require API keys and pre-funded accounts. Your agent can't decide at 3 AM on a Tuesday that it needs Japanese connectivity for the next 6 hours—it needs you to log in, add a payment method, and provision the SIM manually. With x402, the agent holds a wallet with 50 USDC, detects the network failure, sends a 402 payment for a 5GB Japan eSIM ($8.50 in our pricing), and activates it in under 12 seconds median latency.
Coinbase published the x402 spec in late 2025; we implemented the cellular eSIM dispatch layer. The protocol is simple: the server returns 402 Payment Required with a JSON body containing the facilitator address and amount. The client pays. The server verifies the transaction on-chain and releases the resource. No OAuth, no session cookies, no rate-limit tokens.
Architecture: LangChain Tool + x402 Flow
Here's the end-to-end sequence when your LangChain agent calls the custom purchase_esim tool:
sequenceDiagram
participant Agent as LangChain Agent
participant Tool as purchase_esim Tool
participant API as esimx402.com API
participant Facilitator as Coinbase x402 Facilitator
participant Chain as Polygon Network
Agent->>Tool: purchase_esim(region="JP", duration_hours=6)
Tool->>API: POST /v1/esim/purchase {region, duration_hours}
API-->>Tool: 402 Payment Required + facilitator payload
Tool->>Facilitator: POST /pay {to, amount, currency}
Facilitator->>Chain: transfer 8.5 USDC to API address
Chain-->>Facilitator: tx hash 0xABC123...
Facilitator-->>Tool: {tx_hash, confirmation}
Tool->>API: POST /v1/esim/purchase + proof header
API->>Chain: verify tx on Polygon
API-->>Tool: 200 OK + {iccid, activation_code, apn_config}
Tool-->>Agent: eSIM ready, switching default route
The LangChain agent never sees the payment step—it just calls a tool function and gets back an activation payload. Under the hood, the tool:
- Makes the initial API request
- Parses the 402 challenge (JSON schema defined in the x402 spec)
- Calls the facilitator endpoint (we use Coinbase's hosted facilitator at
x402-facilitator.coinbase.com) - Retries the original request with the transaction proof header
- Returns the eSIM credentials to the agent's tool output
The agent's ReAct loop treats this like any other tool: "I need internet in Japan → call purchase_esim → apply the config → resume task."
Step 1: Install Dependencies
You need LangChain, an HTTP client that handles 402 responses, and a library to interact with the Coinbase facilitator. We're using httpx for async requests and coinbase-x402-client (pip package maintained by Coinbase).
# requirements.txt
langchain==0.2.16
langchain-openai==0.1.19 # or langchain-anthropic, etc.
httpx==0.27.0
coinbase-x402-client==1.3.2
python-dotenv==1.0.1
Install:
pip install -r requirements.txt
You also need a Polygon wallet with USDC. The agent's wallet private key goes in .env (never commit this):
# .env
POLYGON_WALLET_PRIVATE_KEY=0x1234abcd... # your agent's wallet
ESIMX402_BASE_URL=https://esimx402.com/v1
OPENAI_API_KEY=sk-proj-... # or ANTHROPIC_API_KEY, etc.
The wallet needs ~20 USDC to cover a few eSIM purchases (our pricing averages $6-12 per package depending on region and duration). Polygon gas is negligible—under $0.01 per transaction.
Step 2: Build the x402 Payment Handler
Before we write the LangChain tool, we need a utility function that handles the 402 challenge. This is generic—you can reuse it for any x402-enabled API, not just eSIM. We'll add detailed error handling and logging to make this production-ready.
import httpx
import os
from coinbase_x402_client import FacilitatorClient
from dotenv import load_dotenv
import asyncio
import logging
load_dotenv()
logger = logging.getLogger(__name__)
class X402PaymentHandler:
def __init__(self):
self.facilitator = FacilitatorClient(
private_key=os.getenv("POLYGON_WALLET_PRIVATE_KEY"),
network="polygon" # mainnet; use "polygon-amoy" for testnet
)
async def request_with_payment(self, url: str, method: str = "POST", json_payload: dict = None):
"""Make an HTTP request; handle 402 by paying and retrying."""
async with httpx.AsyncClient(timeout=30.0) as client:
# Initial request
logger.info(f"Making initial {method} request to {url}")
response = await client.request(method, url, json=json_payload)
if response.status_code != 402:
response.raise_for_status()
return response.json()
# Parse 402 challenge
challenge = response.json()
facilitator_url = challenge["facilitator"]
amount_usdc = float(challenge["amount"])
recipient = challenge["recipient"]
logger.info(f"Received 402 challenge: {amount_usdc} USDC to {recipient}")
# Execute payment via Coinbase facilitator
tx_hash = await self.facilitator.pay(
to=recipient,
amount=amount_usdc,
currency="USDC"
)
logger.info(f"Payment submitted: tx {tx_hash}")
# Retry request with proof header
headers = {"X-Payment-Proof": tx_hash}
response = await client.request(method, url, json=json_payload, headers=headers)
response.raise_for_status()
return response.json()
Key behaviors:
- If the API returns anything except 402, we raise an error or return the data.
- On 402, we extract the facilitator endpoint, payment amount, and recipient address from the response body.
- We call
facilitator.pay()(Coinbase's SDK handles signing the transaction and broadcasting to Polygon). - We retry the original request with the
X-Payment-Proofheader set to the transaction hash. - The server verifies the transaction on-chain before returning the eSIM payload.
This handler is async because LangChain 0.2.x supports async tools, and eSIM activation involves network I/O that can block for 8-12 seconds.
Step 3: Define the LangChain Tool
Now we wrap the payment handler in a LangChain StructuredTool. The agent will call this when it needs connectivity.
from langchain.tools import StructuredTool
from pydantic import BaseModel, Field
import json
class PurchaseESIMInput(BaseModel):
region: str = Field(description="Two-letter ISO country code (e.g., 'JP', 'US', 'DE')")
duration_hours: int = Field(description="How long the eSIM should remain active (6-720 hours)", ge=6, le=720)
class PurchaseESIMOutput(BaseModel):
iccid: str
activation_code: str
apn_config: dict
expires_at: str
async def purchase_esim_impl(region: str, duration_hours: int) -> str:
"""Purchase an eSIM via x402 and return activation details."""
handler = X402PaymentHandler()
base_url = os.getenv("ESIMX402_BASE_URL")
payload = {
"region": region.upper(),
"duration_hours": duration_hours
}
result = await handler.request_with_payment(
url=f"{base_url}/esim/purchase",
method="POST",
json_payload=payload
)
# Return structured data as JSON string for the agent
return json.dumps({
"iccid": result["iccid"],
"activation_code": result["activation_code"],
"apn_config": result["apn_config"],
"expires_at": result["expires_at"]
})
purchase_esim_tool = StructuredTool.from_function(
func=purchase_esim_impl,
name="purchase_esim",
description="Buy cellular eSIM connectivity for a specific region. Use when WiFi is unavailable or you need an IP in a specific country. Returns activation details.",
args_schema=PurchaseESIMInput,
return_direct=False
)
The args_schema ensures the agent knows what parameters are valid. LangChain's function-calling models (the reasoning model powering the agent) will format the tool invocation correctly. The tool returns a JSON string because LangChain agents expect string outputs from tools by default.
Step 4: Integrate the Tool into an Agent
Here's a minimal ReAct agent that uses the eSIM tool alongside a web search tool. When the agent detects a network failure or needs region-specific data, it can autonomously purchase connectivity.
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_react_agent
from langchain.prompts import PromptTemplate
from langchain_community.tools import DuckDuckGoSearchRun
# Define tools
search_tool = DuckDuckGoSearchRun()
tools = [purchase_esim_tool, search_tool]
# ReAct prompt template
react_prompt = PromptTemplate.from_template(
"""You are an autonomous agent with access to eSIM purchasing.
Tools available:
{tools}
Tool Names: {tool_names}
When you encounter network errors or need data from a specific region, use the purchase_esim tool to buy connectivity. After purchasing, you can assume the device has switched to the new eSIM profile.
Question: {input}
Thought: {agent_scratchpad}
"""
)
# Initialize agent
llm = ChatOpenAI(model="gpt-4", temperature=0)
agent = create_react_agent(llm, tools, react_prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)
# Run agent
result = await agent_executor.ainvoke({
"input": "Search for the current weather in Tokyo. If you can't access Japanese weather APIs, purchase a Japan eSIM and retry."
})
print(result["output"])
When you run this, the agent will:
- Attempt to search for Tokyo weather.
- If the search tool fails (simulated network error or region block), the agent reasons that it needs Japanese connectivity.
- Calls
purchase_esim(region="JP", duration_hours=6). - The tool executes the 402 payment flow, waits for on-chain confirmation, and returns the eSIM activation details.
- The agent assumes the eSIM is active and retries the search.
In production, you'd add logic to actually apply the eSIM profile to the device (e.g., via nmcli on Linux or the iOS eSIM API on mobile agents). For server-based agents, you might configure the HTTP client to route through a cellular modem that accepts eSIM QR codes. Our use cases page covers deployment patterns for edge devices versus cloud-hosted agents.
Tradeoffs and Edge Cases
This approach has three main limitations:
Settlement latency: Polygon block time is ~2 seconds, but the facilitator needs 3-5 confirmations before releasing the eSIM payload. Median end-to-end latency is 8.3 seconds (our production P50). If your agent needs sub-second failover, x402 on Polygon won't meet that SLA. Base (Coinbase's L2) has faster finality but higher gas costs for USDC transfers; we chose Polygon because our median transaction is $7.80 and gas is under a penny.
Wallet key management: the agent's private key is in plaintext in .env. For production agents, use a hardware wallet (Ledger via USB), AWS KMS, or a threshold signature service. The Coinbase facilitator SDK supports external signers—you can pass a signing callback instead of a raw private key.
eSIM activation on headless devices: the code above returns activation credentials, but applying them requires OS-level access. On Linux, you can script nmcli to write the eSIM profile to ModemManager. On macOS/iOS, you need the eSIM entitlement in your app's provisioning profile. For cloud-hosted agents (e.g., running in a Lambda or EC2 instance), you'd route traffic through a separate device with a cellular modem—the agent makes the purchase, sends the activation code to the modem over a local API, and the modem handles profile installation.
Production Patterns: Multi-Agent Failover
We've seen two dominant patterns in production:
Pattern 1: Coordinator-worker with shared wallet: a coordinator agent monitors worker agents' network health. When a worker reports connectivity loss, the coordinator purchases an eSIM on behalf of that worker and pushes the activation code via a message queue (SQS, Redis Pub/Sub). The worker applies the profile and resumes. This centralizes wallet management—only the coordinator holds the private key.
Pattern 2: Individual wallets + slippage budget: each agent has its own wallet pre-loaded with 30-50 USDC. When an agent needs connectivity, it purchases directly without asking permission. The risk is that a buggy agent buys eSIMs in a loop and drains its wallet. We mitigate this with a simple rate limit: each agent can purchase at most 2 eSIMs per 24-hour window (enforced client-side in the tool's implementation by checking a local SQLite ledger before calling the API).
Both patterns work. Pattern 1 is safer for high-value agents (trading bots, compliance monitors). Pattern 2 scales better for fleets of low-criticality agents (web scrapers, price aggregators). The docs include reference implementations for both patterns with Redis and DynamoDB state management.
Next Steps
You now have a working LangChain agent that can autonomously purchase eSIM connectivity via x402. To extend this:
- Add a health-check tool that pings a known endpoint and triggers eSIM purchase if the response fails.
- Integrate with the eSIMx402 docs for advanced features like multi-region bundles (buy 3 regions at once for a 15% discount) or data top-ups (extend an active eSIM's quota mid-session).
- Explore our use cases page for agentic payment patterns beyond eSIM—the x402 handler you built works for any 402-enabled API.
The full code from this tutorial (with error handling and retry logic) is in our quickstart repo.
FAQ
Q: Can I use this with other agent frameworks like AutoGPT or Bedrock AgentCore?
Yes. The X402PaymentHandler class is framework-agnostic—it's just async Python. For AutoGPT, wrap purchase_esim_impl in an AutoGPT command. For AWS Bedrock AgentCore, expose it as a Lambda-backed action group. The 402 payment flow is identical across frameworks.
Q: What happens if the agent's wallet runs out of USDC mid-purchase?
The facilitator.pay() call will raise an InsufficientFundsError (from the Coinbase SDK). You should catch this in the tool and return a descriptive error to the agent: "Unable to purchase eSIM: wallet balance too low. Current balance: 2.1 USDC, required: 8.5 USDC." The agent can then log the issue or notify a human operator.
Q: How do I test this without spending real USDC?
Use Polygon Amoy testnet. Change the FacilitatorClient network parameter to "polygon-amoy" and fund your test wallet with testnet USDC from the Polygon faucet. Our API supports testnet mode—set ESIMX402_BASE_URL=https://testnet.esimx402.com/v1 in your .env. Testnet eSIMs return mock activation codes and don't provision real cellular connectivity.
Q: Can the agent purchase multiple eSIMs in parallel for different regions?
Yes. The X402PaymentHandler is stateless except for the wallet private key. If your agent needs connectivity in three regions simultaneously (e.g., a scraper fleet hitting Japan, Germany, and Brazil APIs), you can call purchase_esim_tool three times in parallel using asyncio.gather(). Each call creates a separate Polygon transaction. The only constraint is gas throughput—Polygon allows ~100 transactions per second per wallet address, so you won't hit that limit in typical agent workloads.
Q: What happens if the payment transaction fails or gets stuck in the mempool?
The Coinbase facilitator SDK includes retry logic with exponential backoff. If the transaction doesn't confirm within 30 seconds, the SDK will re-submit with higher gas. If it still fails after 3 attempts, the facilitator.pay() call raises a TransactionFailedError. Your tool should catch this and return an error to the agent explaining that the purchase failed. The agent can decide whether to retry (maybe network congestion) or switch to a different failover strategy.
Q: Does the agent need to hold a separate wallet for each eSIM, or can I reuse one wallet for all purchases?
One wallet for all purchases is fine. The x402 protocol doesn't require unique payer addresses—our API verifies that the transaction matches the expected amount and recipient, not that it came from a specific sender. Using one wallet simplifies key management. If you want per-agent accounting, tag each transaction in your local ledger with an agent_id and reconcile later, or use HD wallet derivation to create deterministic child wallets per agent (path m/44'/60'/0'/0/{agent_index}).