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.

Get Started

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

1

Create a Session

Agent Wallet x402 Payment (0.01 USDC) POST /sessions JWT + Session ID
2

Configure Proxy

Agent sets HTTP_PROXY=http://<sessionId>:<jwt>@host:8080
3

Route Traffic

Agent Request Proxy Relay Bright Data (residential IP) Target Website
4

Swap Location

POST /sessions/:id/location New Country New Exit IP (same session)

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

  • 400 INVALID_COUNTRY -- Invalid ISO 3166-1 alpha-2 country code
  • 429 RATE_LIMITED -- Too many requests
  • 503 SERVICE_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

  • 401 AUTH_EXPIRED -- Invalid or expired JWT
  • 404 SESSION_NOT_FOUND -- Session does not exist
  • 410 SESSION_EXPIRED -- Session TTL elapsed
  • 503 SERVICE_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

  • 400 INVALID_REQUEST -- Missing or malformed request body
  • 400 INVALID_COUNTRY -- Invalid ISO 3166-1 alpha-2 country code
  • 401 AUTH_EXPIRED -- Invalid or expired JWT
  • 404 SESSION_NOT_FOUND -- Session does not exist
  • 410 SESSION_EXPIRED -- Session TTL elapsed
  • 429 RATE_LIMITED -- Swap cooldown not elapsed
  • 503 SERVICE_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

  1. POST /sessions — server returns 402 with payment details in headers
  2. Agent pays 0.01 USDC via on-chain transfer
  3. Agent retries the same request with X-PAYMENT-SIGNATURE header 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"
}

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.

24/7 usage ≈ $2.88/day | 1,000 sessions/day ≈ $10/day