AgentCamo
Camouflage-as-a-service for on-chain agents.
Residential proxy sessions paid with x402 micropayments.
Hot-swappable geo-targeting. 5-minute sessions. One API call to start.
Quickstart
Three steps to route traffic through a residential IP
# Step 1: Create a session (x402 payment happens automatically)
curl -s -X POST https://agentcamo.xyz/sessions \
-H "Content-Type: application/json" \
-d '{"countryCode": "us"}' | jq .
# Response:
# {
# "sessionId": "a1b2c3d4-...",
# "token": "eyJhbGciOiJIUzI1NiIs...",
# "proxyHost": "agentcamo.xyz",
# "proxyPort": 8080,
# "proxyAuth": "a1b2c3d4-...:eyJhbGciOiJIUzI1NiIs...",
# "country": "us",
# "expiresAt": "2026-03-02T12:05:00.000Z"
# }
# Step 2: Route traffic through the proxy
curl -x "http://SESSION_ID:JWT_TOKEN@agentcamo.xyz:8080" \
http://httpbin.org/ip
# Step 3: Swap to a different country
curl -s -X POST https://agentcamo.xyz/sessions/SESSION_ID/location \
-H "Authorization: Bearer JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"countryCode": "sg"}'
How It Works
Create a Session
Configure Proxy
HTTP_PROXY=http://<sessionId>:<jwt>@host:8080
Route Traffic
Swap Location
API Reference
Base URL: https://agentcamo.xyz
GET /health None Service health check
Response 200
{
"status": "ok",
"redis": "connected"
}
Response 503
{
"status": "degraded",
"redis": "disconnected"
}
GET /countries None List supported countries
Response 200
{
"countries": [
{ "code": "us", "name": "United States" },
{ "code": "gb", "name": "United Kingdom" },
{ "code": "sg", "name": "Singapore" },
{ "code": "de", "name": "Germany" },
{ "code": "jp", "name": "Japan" }
]
}
249 countries supported. All lowercase ISO 3166-1 alpha-2 codes.
POST /sessions x402 Create a proxy session
Rate limit: 10 requests per IP per minute
Request Body
{
"countryCode": "us",
"identity": "my-trading-bot"
}
Both fields optional. countryCode defaults to "us".
Response 201
{
"sessionId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"proxyHost": "agentcamo.xyz",
"proxyPort": 8080,
"proxyAuth": "f47ac10b-58cc-4372-a567-0e02b2c3d479:eyJhbGciOiJIUzI1NiIs...",
"country": "us",
"expiresAt": "2026-03-02T12:05:00.000Z"
}
Errors
400INVALID_COUNTRY -- Invalid ISO 3166-1 alpha-2 country code429RATE_LIMITED -- Too many requests503SERVICE_UNAVAILABLE -- Redis or upstream unavailable
GET /sessions/:id JWT Get session details
Response 200
{
"sessionId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"country": "us",
"exitIp": "104.28.231.97",
"exitCountry": "US",
"expiresAt": "2026-03-02T12:05:00.000Z",
"remainingSeconds": 247,
"switchCount": 1,
"switchHistory": [
{ "from": "us", "to": "gb", "exitIp": "185.76.10.42", "at": 1740830700 }
]
}
exitIp and exitCountry may be null (probe is best-effort).
Errors
401AUTH_EXPIRED -- Invalid or expired JWT404SESSION_NOT_FOUND -- Session does not exist410SESSION_EXPIRED -- Session TTL elapsed503SERVICE_UNAVAILABLE -- Redis or upstream unavailable
POST /sessions/:id/location JWT Swap exit country
Request Body
{
"countryCode": "gb"
}
Response 200
{
"country": "gb",
"exitIp": "185.76.10.42",
"exitCountry": "GB",
"nextSwapAvailableAt": "2026-03-02T12:06:30.000Z"
}
90-second cooldown between swaps.
Errors
400INVALID_REQUEST -- Missing or malformed request body400INVALID_COUNTRY -- Invalid ISO 3166-1 alpha-2 country code401AUTH_EXPIRED -- Invalid or expired JWT404SESSION_NOT_FOUND -- Session does not exist410SESSION_EXPIRED -- Session TTL elapsed429RATE_LIMITED -- Swap cooldown not elapsed503SERVICE_UNAVAILABLE -- Redis or upstream unavailable
Authentication
x402 Payment (Session Creation)
Agent Camo uses the x402 protocol for session payments. When you call POST /sessions, the server returns a 402 Payment Required response. Your x402 client handles the payment automatically and retries the request.
EVM (Base Sepolia)
import { wrapFetchWithPayment } from "@x402/fetch";
import { viemSigner } from "@x402/evm";
import { createWalletClient, http } from "viem";
import { baseSepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: baseSepolia,
transport: http(),
});
const payFetch = wrapFetchWithPayment(fetch, viemSigner(wallet));
const res = await payFetch("https://agentcamo.xyz/sessions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ countryCode: "us" }),
});
const session = await res.json();
Solana (Devnet)
import { wrapFetchWithPayment } from "@x402/fetch";
import { solanaSigner } from "@x402/svm";
import { Keypair } from "@solana/web3.js";
const keypair = Keypair.fromSecretKey(
Uint8Array.from(JSON.parse(process.env.SOLANA_PRIVATE_KEY))
);
const payFetch = wrapFetchWithPayment(fetch, solanaSigner(keypair));
const res = await payFetch("https://agentcamo.xyz/sessions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ countryCode: "us" }),
});
const session = await res.json();
Manual 402 Flow
POST /sessions— server returns402with payment details in headers- Agent pays 0.01 USDC via on-chain transfer
- Agent retries the same request with
X-PAYMENT-SIGNATUREheader containing the payment proof
JWT Usage
The token from the session response is used in two contexts:
REST endpoints — Authorization: Bearer
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
https://agentcamo.xyz/sessions/SESSION_ID
Proxy — Basic auth (auto-extracted)
curl -x "http://SESSION_ID:eyJhbGciOiJIUzI1NiIs...@agentcamo.xyz:8080" \
http://httpbin.org/ip
Same token, two contexts. TTL matches session (5 minutes).
Proxy Usage
Proxy URL pattern:
http://<sessionId>:<jwt>@agentcamo.xyz:8080
HTTP Forward Proxy
Set the HTTP_PROXY env var or use proxy options in your HTTP client. Used for plain HTTP targets.
CONNECT Tunnel
HTTPS targets use CONNECT automatically. Most HTTP clients handle this transparently.
Client Examples
curl
curl -x "http://SESSION_ID:JWT@agentcamo.xyz:8080" http://httpbin.org/ip
Node.js (undici)
import { ProxyAgent } from "undici";
const agent = new ProxyAgent(`http://${session.proxyAuth}@agentcamo.xyz:8080`);
const res = await fetch("http://httpbin.org/ip", { dispatcher: agent });
Python (requests)
proxies = {"http": f"http://{session['proxyAuth']}@agentcamo.xyz:8080",
"https": f"http://{session['proxyAuth']}@agentcamo.xyz:8080"}
r = requests.get("http://httpbin.org/ip", proxies=proxies)
SSRF Protection — Requests to private/internal IPs (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) are blocked with 403.
Geo-Swap
Request
curl -s -X POST https://agentcamo.xyz/sessions/SESSION_ID/location \
-H "Authorization: Bearer JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"countryCode": "sg"}'
Response
{
"country": "sg",
"exitIp": "175.176.33.12",
"exitCountry": "SG",
"nextSwapAvailableAt": "2026-03-02T12:06:30.000Z"
}
-
90-second cooldown between swaps —
nextSwapAvailableAttells you when -
429withRetry-Afterheader if you swap too fast -
Country codes: ISO 3166-1 alpha-2, lowercase. Get the full list from
GET /countries - Each swap = new residential exit IP in the target country
Error Codes
| Code | Status | Meaning |
|---|---|---|
| AUTH_EXPIRED | 401 / 407 | JWT missing, malformed, or expired. Proxy returns 407 with Proxy-Authenticate: Bearer realm="geocamo" header. |
| SESSION_EXPIRED | 410 | Session TTL (5 minutes) has elapsed. Create a new session. |
| SESSION_NOT_FOUND | 404 | Session ID doesn't exist. It may have expired and been cleaned up. |
| RATE_LIMITED | 429 | Too many requests. Per-IP rate limit on session creation (10/min) or geo-swap cooldown (90s). Check Retry-After header. |
| INVALID_COUNTRY | 400 | Not a valid ISO 3166-1 alpha-2 country code. Use GET /countries for the full list. |
| INVALID_REQUEST | 400 | Missing required field (e.g., countryCode on location swap). |
| PROXY_UPSTREAM_ERROR | 502 | Upstream residential proxy connection failed. Retry or create a new session. |
| SERVICE_UNAVAILABLE | 503 | Internal service error (Redis down, etc.). Retry after a moment. |
| IP_VERIFICATION_FAILED | — | Reserved. Not currently returned in HTTP responses. |
Error Response Shape
{
"error": "Human-readable error message",
"code": "ERROR_CODE",
"details": {}
}
Pricing
0.01 USDC per 5-minute session
Base Sepolia (EVM testnet) or Solana Devnet. Mainnet coming soon.