x402MCPPolicy governed wallets

Build agents that can pay for services without holding keys.

Gordon handles wallet custody, policy, payment, and audit so your agent does not have to. It acts as the control plane for agent-to-machine (A2M) and agent-to-agent (A2A) transactions with paid services.

Register

Create an account, then an agent — each agent gets its own Base wallet and scoped API key.

Configure

Curated services come pre-enabled; set per-service spend caps, domains, and approval thresholds.

Settle

Authorize x402, confirm receipts, and audit every call.

Getting started

Quickstart

Sign in at withgordon.ai, create an agent, fund its wallet, review services, then call it via the API, SDK, or MCP. See the full quickstart for a step-by-step walkthrough.

# Install the Gordon SDK
npm install @withgordon/core

# Environment — agent key + secret from the dashboard
GORDON_PLATFORM_URL=https://api.withgordon.ai
GORDON_AGENT_API_KEY=gak_pub_...
GORDON_AGENT_API_SECRET=gak_sec_...
Model

Core concepts

Agent

A scoped actor with an API key. Agents request service payments but never hold private keys.

Service

A catalog provider such as search, scrape, crawl, enrich, or infrastructure.

Operation

A callable method on a service with a method, endpoint, price, and settlement terms.

Policy

Per-agent controls: operation subset, spend caps, approval thresholds, and allowed domains.

Settlement

The x402 authorization, payment headers, provider response proof, and receipt status.

Buyer

The user or organization that owns policies, agents, wallets, and receipts.

Authentication

API keys

Gordon uses agent API keys for all programmatic access. OTP email login is only for the browser dashboard — every SDK call, direct API call, and MCP tool call uses a key+secret pair scoped to a single agent.

Keys are created when you create or rotate an agent. The secret is shown once at creation time. Store it securely — if lost, rotate the key.

Authorization header format

Authorization: Bearer {API_KEY}:{API_SECRET}

# Example
Authorization: Bearer gak_pub_Smcyy7PL02LUeUlVq7WqkByA:gak_sec_qiQ7PfyipgrNNUCXOrmS7sfN...

The gak_pub_ key identifies your agent and is safe to log. The gak_sec_ secret authorizes requests — never embed it in client code.

Never use OTP tokens in programmatic code.
OTP sessions are for the browser dashboard only — they expire and are not suitable for agent API calls.
Authentication

Creating an agent

An agent holds the API key pair, the on-chain USDC wallet, and the spend policy.

Option A — Dashboard

  1. Sign in at withgordon.ai via OTP email.
  2. Go to Agents → New agent and give it a name.
  3. Gordon provisions a CDP custody wallet on Base and pre-enables the curated catalog — copy the gak_sec_ shown once.
  4. Open the agent and click Fund to deposit USDC on Base.
  5. Review the pre-enabled services — set per-call caps or disable any you don't want.

Option B — Management API

# Step 1 — request OTP
curl -X POST https://api.withgordon.ai/auth/request-otp \
  -H "Content-Type: application/json" \
  -d '{"email": "you@example.com"}'

# Step 2 — verify OTP → short-lived session JWT
curl -X POST https://api.withgordon.ai/auth/verify-otp \
  -H "Content-Type: application/json" \
  -d '{"email": "you@example.com", "code": "123456"}'
# → { "token": "<SESSION_JWT>", "user": { "id": "usr_..." } }

# Step 3 — mint a long-lived Personal Access Token (1 year)
curl -X POST https://api.withgordon.ai/users/{userId}/api-tokens \
  -H "Authorization: Bearer <SESSION_JWT>"
# → { "token": "<PAT>", "expires_in": 31536000 }

# Step 4 — create an agent
curl -X POST https://api.withgordon.ai/users/{userId}/agents \
  -H "Authorization: Bearer <PAT>" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-prod-agent"}'
# → { "agent": { "id": "agent_...", "api_key_public": "gak_pub_..." },
#     "api_secret": "gak_sec_...",   ← shown ONCE — store immediately
#     "wallet": { "address": "0x...", "chain": "base-mainnet" } }
Store api_secret immediately.
The secret is returned exactly once. If lost, use POST /agents/{agentId}/rotate-key.

Enable a service for the agent

curl -X POST https://api.withgordon.ai/agents/{agentId}/services \
  -H "Authorization: Bearer <PAT>" \
  -H "Content-Type: application/json" \
  -d '{
    "service_id": "exa",
    "max_per_call_units": 50000,
    "max_per_day_units": 5000000
  }'
# → { "agent_id": "...", "service_id": "...", "max_per_call_units": 50000, ... }
Authentication

Rotate & verify keys

# Rotate agent key — returns a fresh gak_pub + gak_sec pair
POST https://api.withgordon.ai/agents/{agentId}/rotate-key
Authorization: Bearer {USER_JWT}

# Verify your key is working (200 = valid)
curl https://api.withgordon.ai/services \
  -H "Authorization: Bearer gak_pub_...:gak_sec_..."
Services

Discover services

The catalog is public — no auth required to browse. New agents are auto-enrolled into the curated catalog at permissive limits; the buyer can tighten caps or disable services per agent. Any service not in an agent's enabled set is blocked (service_not_enabled) — enforcement is fail-closed.

GEThttps://api.withgordon.ai/services

Query parameters

categorystring

Filter by category: search, scrape, ai, data, infrastructure, finance.

trust_statusstring

Filter by trust level: listed, discovered, probe_passed (verified).

limitinteger

Max results. Default 50.

curl "https://api.withgordon.ai/services?category=search"
# → { "services": [{ "id": "...", "slug": "exa", "name": "Exa Search", ... }] }
Services

Service detail

GEThttps://api.withgordon.ai/services/{serviceId}

serviceId accepts either a UUID or the slug (e.g. exa). Returns all callable operations with pricing and live x402 payment requirements.

{
  "id": "0b3f2c1a-7e44-4a90-9b1e-2c8d5f6a1b20",
  "slug": "exa",
  "name": "Exa Search",
  "operations": [
    {
      "availability": "paid_x402",
      "operation_id": "search.web",
      "label": "Web Search",
      "method": "POST",
      "endpoint": "https://api.exa.ai/search",
      "estimated_price_units": 7000,   // 7000 units = $0.007
      "payment": {
        "scheme": "exact",
        "network": "base",
        "token": "USDC",
        "amount_units": 7000,
        "pay_to": "0x6d6E695b09861467c7d462f5AAF31cF3540B9192"
      }
    }
  ]
}

All prices are in Gordon micro-units: 1,000,000 units = $1.00 USD. So 7,000 units = $0.007.

Payments

x402 payment lifecycle

  1. 11. Call the providerMake a normal HTTP request. If payment is required the provider returns HTTP 402 with a v1 X-Payment-Required header, a v2 payment-required header, or a v2 JSON body.
  2. 22. Extract payment requirementDecode the provider requirement and pass the full object to Gordon. Gordon selects a supported Base USDC EIP-3009 payment option from v2 accepts arrays.
  3. 33. POST /x402/authorizeSend the requirement to Gordon. Gordon checks your agent policy, signs using the CDP wallet, and returns X-Payment headers.
  4. 44. Retry the providerReplay the request with the X-Payment header. The provider verifies the USDC TransferWithAuthorization and returns the result.
  5. 55. POST /x402/settlements/:id/completeSend the X-Payment-Response header back to Gordon to mark the receipt as confirmed.

Decoded provider examples

// x402 v1 — decoded X-Payment-Required header
{
  "scheme": "exact",
  "network": "base",
  "token": "USDC",
  "amount": "7000",
  "payTo": "0x6d6E695b09861467c7d462f5AAF31cF3540B9192",
  "nonce": "0x..."
}

// x402 v2 — payment-required header or 402 response body
{
  "x402Version": 2,
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:8453",
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "amount": "7000",
    "payTo": "0x6d6E695b09861467c7d462f5AAF31cF3540B9192",
    "maxTimeoutSeconds": 60
  }]
}
USDC only. No ETH required.
Gordon signs native USDC EIP-3009 authorizations on Base. The provider facilitator submits the transaction and sponsors gas, so the agent wallet only needs USDC.
Payments

Authorize payment

POSThttps://api.withgordon.ai/x402/authorize

Requires agent key authentication.

Request body

idempotency_keystringrequired

Unique key for this call. Safe to retry — repeated keys return the cached authorization.

service_idstringrequired

Service slug (e.g. exa). Required — Gordon uses this to enforce per-agent enablement and spend policy.

operation_idstringrequired

Operation ID (e.g. search.web). Required when service_id is set.

payment_requirementobjectrequired

The decoded X-Payment-Required object from the provider 402 response.

max_payment_unitsintegerrequired

Maximum you will pay in micro-units (1,000,000 = $1.00). Authorization is blocked if the requirement exceeds this.

original_requestobjectrequired

Provider request metadata: url, method, optional body_hash (SHA-256 hex that binds authorization/audit metadata to the exact request body), optional session_id.

// Success — 200 OK
{
  "transaction_id": "8510fc02-c699-42ea-aafc-04f0e18dd3d5",
  "settlement_id": "85e6f79c-4e6a-4f95-b18c-1baabd45f879",
  "payment_headers": {
    "X-Payment": "eyJ4NDAyVmVyc2lvbiI6Miwic2NoZW1lIjoiZ..."
  },
  "expires_at": "2026-05-27T05:36:36.000Z"
}

// Policy block — 403
{ "error": "service_not_enabled", "code": "SERVICE_NOT_ENABLED",
  "message": "Enable at https://withgordon.ai/account/agents/{agentId}/services" }

// Spend cap exceeded — 402
{ "error": "amount_exceeds_per_call_limit",
  "amount_units": 50000, "max_per_call_units": 10000 }
Payments

Confirm receipt

After the provider returns 200, send its payment-response proof to Gordon. Gordon extracts the transaction hash and independently verifies the successful, exact USDC transfer on Base before marking the settlement confirmed.

Always complete paid calls.
Spend is reserved when Gordon authorizes the payment, but skipping this step leaves the settlement pending and prevents a finalized, on-chain-verified receipt from appearing in audit views.
POSThttps://api.withgordon.ai/x402/settlements/{settlementId}/complete
curl -X POST https://api.withgordon.ai/x402/settlements/85e6f79c-.../complete \
  -H "Authorization: Bearer gak_pub_...:gak_sec_..." \
  -H "Content-Type: application/json" \
  -d '{ "payment_response_header": { "X-Payment-Response": "eyJzZXR0bGVkIjp0cnVlLCJ..." } }'

# → { "settlement_id": "85e6f79c-...", "receipt_status": "confirmed" }
Audit

Fetch receipts

GEThttps://api.withgordon.ai/x402/settlements/{settlementId}
GEThttps://api.withgordon.ai/agents/{agentId}/settlements?limit=25
{
  "id": "85e6f79c-4e6a-4f95-b18c-1baabd45f879",
  "transaction_id": "8510fc02-c699-42ea-aafc-04f0e18dd3d5",
  "agent_id": "agent_yzmxwq3v48",
  "receipt_status": "confirmed",
  "amount_units": 7000,
  "network": "eip155:8453",
  "token": "USDC",
  "service_id": "0b3f2c1a-7e44-4a90-9b1e-2c8d5f6a1b20",
  "operation_id": "search.web",
  "authorized_at": "2026-05-27T05:31:36.000Z",
  "settled_at": "2026-05-27T05:31:38.000Z"
}

Dashboard receipt page: withgordon.ai/account/receipts/{transactionId}

Build

SDK

The SDK wraps the full authorize → retry → confirm flow in a single gordon.fetch() call.

import { Gordon } from "@withgordon/core";

const gordon = new Gordon({
  platformUrl: "https://api.withgordon.ai",
  agentApiKey: process.env.GORDON_AGENT_API_KEY!,
  agentApiSecret: process.env.GORDON_AGENT_API_SECRET!,
});

// Drop-in replacement for fetch — handles 402 automatically.
// receipt is null when the provider returns 2xx without requiring payment.
const result = await gordon.fetch("https://api.exa.ai/search", {
  method: "POST",
  serviceId: "exa",
  operationId: "search.web",
  maxPaymentUnits: 10000,
  body: JSON.stringify({ query: "agentic payments", numResults: 5 }),
  headers: { "Content-Type": "application/json" },
});

if (result.receipt?.confirmed) {
  console.log("Paid", result.receipt.amount_units / 1_000_000, "USD");
}
Build

MCP server

Gordon runs as a remote HTTP MCP server — no local install needed. See the MCP install guide for Cursor, Claude Desktop, VS Code, Codex, and Windsurf configs.

// Any MCP client — remote HTTP config
{
  "mcpServers": {
    "gordon": {
      "url": "https://api.withgordon.ai/mcp",
      "headers": { "Authorization": "Bearer gak_pub_...:gak_sec_..." }
    }
  }
}
gordon_find_serviceNatural language search — describe what you want, get matching operation strings and pricing.
gordon_requestCall a catalog service by URL with service_id + operation_id. Gordon handles 402 → authorize → retry → confirm.
gordon_call_serviceCall by slug.operationId (e.g. exa.search.web). Enforces enabled-service policy automatically.
gordon_list_servicesBrowse the catalog. Filter by category or trust status.
gordon_get_serviceFull service record — operations, pricing, live x402 terms.
gordon_list_enabled_servicesServices this agent is permitted to pay, with spend caps.
gordon_get_balanceThis agent's USDC wallet balance and per-service daily spend remaining.
gordon_get_receiptFetch a settlement receipt by ID.
gordon_get_audit_logRecent payment receipts for this agent.
Which call tool should I use?
Prefer gordon_call_service when you know the catalog operation string such as exa.search.web. Use gordon_request when you already have a raw provider URL and need to associate it with a service and operation.
Build

MCP quickstart

Three steps: install → review services → call it.

Step 1 — add Gordon to your client

Get your agent key from Agents → Connect. See the full MCP guide for Cursor, Claude Desktop, VS Code, Codex, and Windsurf.

Step 2 — review services (optional)

Curated services are enabled by default on new agents. To tighten, go to Agents → your agent → Services and set a per-call cap or disable services you don't want.

Step 3 — discover and call

// In any MCP client connected to Gordon:
gordon_find_service({ query: "web search" })
// → Exa, Brave, etc. with operation strings and pricing

gordon_call_service({
  operation: "exa.search.web",
  params: { query: "latest x402 integrations", numResults: 5 },
  max_payment_units: 10000    // $0.01 cap
})

settlement_status values

"confirmed"Payment made and settlement confirmed on-chain. Receipt is final.
"failed"Payment sent but on-chain confirmation failed — thrown as SETTLEMENT_NOT_CONFIRMED error. Check gordon_get_receipt.
nullNo payment was required (provider returned 200 directly).
Build

Agent discovery (SKILL.md)

Gordon publishes a machine-readable capability file at a public URL. Agents running in Claude Code, Cursor, Codex, or any agent with a fetch tool can retrieve it to learn Gordon's full tool set, auth format, spend semantics, and failure behavior.

GEThttps://api.withgordon.ai/SKILL.md

No auth required. Cache-Control: public, max-age=3600. The file is served by the Gordon MCP host, not this frontend. If you hit a 404, the MCP server may be restarting — retry after 30 seconds or fall back to https://www.withgordon.ai/docs.

// In Claude Code, Cursor, or any agent with web_fetch:
const skill = await web_fetch("https://api.withgordon.ai/SKILL.md");
// Gordon's full tool set, install config, spend semantics, and failure handling
For agent developers:
Instruct your agent to fetch https://api.withgordon.ai/SKILL.md before its first tool call. This ensures correct tool names, spend semantics, and graceful failure handling — even without prior training data on Gordon.

Gordon also serves an llms.txt index pointing LLMs to docs, SKILL.md, and the public service catalog.

Safety

Policy controls

New agents are auto-enrolled into the curated catalog at permissive limits. Tighten caps, restrict operations, or disable services per agent. Enforcement is fail-closed: any service not in the agent's enabled set is blocked (service_not_enabled). When a service is enabled, all its operations are allowed by default — tighten with enabled_operations if needed.

POST https://api.withgordon.ai/agents/{agentId}/services
Authorization: Bearer {USER_JWT}

{
  "service_id": "exa",
  // Leave enabled_operations out to allow all operations (default)
  // "enabled_operations": ["search.web"],   // optional: restrict to specific ops
  "max_per_call_units": 10000,           // $0.01 per call — default $1.00
  "max_per_day_units": 1000000,          // $1.00 per day — default $50.00
  "require_approval_above_units": 500000 // escalate above $0.50 — default $10.00
  // "allowed_domains": ["example.com"]  // optional: scrape/crawl domain allowlist
}
Reference

Full API reference

See the API reference page for complete schemas, request/response shapes, and error codes. Quick reference:

GET/servicesnoneList catalog services (public).
GET/services/:idnoneService detail with operations and live pricing.
POST/x402/authorizeagent keyAuthorize and sign x402 payment.
POST/x402/checkagent keyPolicy-only preflight; authorize still re-evaluates.
POST/x402/settlements/:id/completeagent keyConfirm provider receipt.
GET/x402/settlements/:idagent keyFetch a settlement receipt.
GET/agents/:id/settlementsagent keyList settlements for this agent.
GET/agents/:id/servicesuser JWTList enabled services.
POST/agents/:id/servicesuser JWTEnable a service with spend policy.
DELETE/agents/:id/services/:serviceIduser JWTDisable a service for this agent.
PATCH/agents/:id/services/:serviceId/policyuser JWTUpdate an enabled service policy.
PUT/agents/:id/policiesuser JWTUpsert the agent spending constitution.
POST/agents/:id/rotate-keyuser JWTRotate agent key — returns new pair.

Base URL: https://api.withgordon.ai · See Error codes for 4xx/5xx response shapes.

Testing

Test locally

cd /path/to/new-gordon
AUTH_SECRET=... npx vitest run --workspace gordon-core

# E2E against staging
GORDON_PLATFORM_URL=https://api.withgordon.ai \
GORDON_AGENT_API_KEY=gak_pub_... \
GORDON_AGENT_API_SECRET=gak_sec_... \
  npx tsx packages/mcp/src/e2e-test.ts