Nonagon Link — AI Agent Developer Guide

Integration guide for calling Nonagon Link paid APIs with USDC from AI agents


1. Overview

Nonagon Link is an agentic payment gateway compliant with the x402 protocol (Coinbase). AI agents can use paid APIs in the following two ways.

MethodBest suited forRequired implementation
MCP (recommended)MCP-capable AI such as Claude / GPT-4MCP server connection settings only
Direct x402 protocol integrationCustom agents / SDK embeddingIntegration of @x402/fetch

2. Prerequisites

  • A Solana wallet for payments (USDC / SOL will be paid from this wallet). Use mainnet for production, or devnet to try things out at no cost
  • USDC token holdings (production = real USDC on mainnet / testing = free airdrop on devnet)
  • Node.js >= 20.0.0 (for the MCP method)

Preparing a Solana wallet and USDC for payments (the example below uses devnet for testing)

# Generate a keypair with the Solana CLI
solana-keygen new --outfile ~/.config/solana/devnet.json --no-bip39-passphrase

# Check the public key
solana-keygen pubkey ~/.config/solana/devnet.json

# Airdrop devnet SOL (for transaction fees)
solana airdrop 2 --url devnet

# devnet USDC is distributed automatically in the Nonagon Link test environment

3. Method A: Connect via Nonagon Link (recommended)

MCP (Model Context Protocol) is a standard protocol that lets AI agents such as Claude / GPT-4 call external tools. Nonagon Link provides the Nonagon Link MCP server, allowing AI agents to call paid APIs transparently.

3-1. Setup

See the MCP setup guide for details.

For Claude Code:

.claude/settings.json:

{
  "mcpServers": {
    "nonagon-link": {
      "command": "npx",
      "args": ["tsx", "apps/nonagon_link/src/mcp/server.ts"],
      "cwd": "/path/to/nonagon_link",
      "env": {
        "NONAGON_PRIVATE_KEY": "<private key of the Solana wallet used for payments (Base58)>",
        "NONAGON_NETWORK": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
        "NONAGON_LINK_BASE_URL": "https://api.nnglink.ai",
        "NONAGON_PAY_TO": "8yXpNyyPg9iGdMST8BRyz7HEJVbQG81VP6kHBCKXrdH8",
        "LOG_LEVEL": "info"
      }
    }
  }
}

Even when using HTTP stream, the runtime binds only to 127.0.0.1, so treat it as intended for local HTTP clients on the same machine. Do not expose it as a public endpoint over the network.

In HTTP stream mode, setting MCP_AUTH_TOKEN is required. Set MCP_AUTH_TOKEN on the server side, and always attach Authorization: Bearer <MCP_AUTH_TOKEN> on the client side as well.

Use a CSPRNG-generated secret of at least 32 bytes for MCP_AUTH_TOKEN. The published configuration examples only accept values of at least 43 base64url characters or at least 64 hex characters.

3-2. Available tools

pay_and_call

Pays a paid API and retrieves data: Solana USDC, plus JPYC / TEST_JPYSC with an EVM key and NONAGON_EVM_PAY_TO, and EVM USDC only when MCP_MAX_PAYMENT_EVM_USDC_CAP is set (see the MCP setup guide).

ParameterTypeRequiredDescription
proxyUrlstring (URL)RequiredURL of the API to call (HTTPS only)
method"GET" | "POST"OptionalHTTP method (default: GET)
bodyobjectOptionalBody of the POST request
maxPaymentUsdcnumberRequiredMaximum Solana USDC this call may pay (greater than 0, up to 0.100 by default; the ceiling is MCP_MAX_PAYMENT_USDC_CAP). Not used for EVM USDC. If the amount requested by the 402 exceeds this, an error is returned without paying
maxPaymentJpynumberOptionalMaximum for yen-denominated listings (JPYC / TEST_JPYSC): greater than 0, up to MCP_MAX_PAYMENT_JPY_CAP (100 by default), at most 4 decimals. Without it a yen 402 is not paid. Needs NONAGON_EVM_PRIVATE_KEY and NONAGON_EVM_PAY_TO
maxPaymentEvmUsdcnumberOptionalMaximum for EVM USDC listings: greater than 0, up to MCP_MAX_PAYMENT_EVM_USDC_CAP, at most 6 decimals. EVM USDC is paid only when that cap is set, and only with this argument

⚠️ EVM payments (JPYC, EVM USDC): an EVM payment is a signed EIP-3009 authorization, and any third party that receives it (such as the host you call) can execute it on-chain itself. The money can only go to the treasury in NONAGON_EVM_PAY_TO, but you then get no service and no payment record. There is no session or daily total limit: keep each cap as low as you need, keep the EVM wallet balance small, and do not pay_and_call URLs you do not trust. When paymentStatus is unknown, do not retry; check authorizationState(from, nonce) on the token contract.

Usage example (request to Claude):

Please call the following API with pay_and_call:
- URL: https://api.nnglink.ai/api/proxy/weather-api/current
- Method: POST
- Body: { "city": "Tokyo" }
- Payment limit: 0.05 USDC

Example response:

{
  "success": true,
  "data": {
    "city": "Tokyo",
    "temperature": 22,
    "condition": "sunny"
  },
  "paymentStatus": "paid",
  "paymentInfo": {
    "currency": "USDC",
    "amount": "0.01",
    "amountUsdc": "0.01",
    "txSignature": "5xKt...",
    "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
    "payer": "<your wallet>",
    "paidAt": "2026-10-05T00:00:00.000Z"
  }
}

paymentStatus is paid, not_paid or unknown. On unknown (a signed payment was sent but the result is not confirmed), do not retry automatically: a retry can pay twice. For EVM payments, check authorizationState(from, nonce). For yen tokens (JPYC / TEST_JPYSC), amount is in that currency and amountUsdc is null.

Decide that nothing was paid only from paymentStatus being not_paid (a free API or a reused token, for example). paymentInfo can be null while paymentStatus is unknown (an EVM payment whose result is unknown), so a null paymentInfo alone does not mean nothing was paid.

get_active_tokens

Retrieves summaries of currently valid masked tokens and their remaining validity time.

The public response returns only summaries containing maskedToken; raw token values are never exposed.

Example response:

{
  "tokens": [
    {
      "maskedToken": "agt_abcd...mnop",
      "proxyUrl": "https://api.nnglink.ai/api/proxy/weather-api/current",
      "expiresAt": "2026-05-11T12:00:00Z",
      "remainingMinutes": 45
    }
  ]
}

search_listings

Searches paid APIs and returns price, payment compatibility, schemas, ratings, health, decision gaps, and a proxyUrl for pay_and_call.

Note: To use this tool, the MCP server must be configured with NONAGON_LINK_BASE_URL (e.g. https://api.nnglink.ai).

ParameterTypeRequiredDescription
qstringOptionalSearch keyword (partial match on name / description, up to 200 characters)
categorystringOptionalFilter by category (up to 40 characters)
chain / networkIdstringOptionalFilter by payment chain or CAIP-2 network
currency / maxPricestring / numberOptionalCurrency and per-call budget; currency is required with maxPrice
healthstringOptionalFilter by healthy, unhealthy, or unknown
minRatingCountnumberOptionalMinimum settled-payment rating count
payableViaMcpOnlybooleanOptionalReturn only listings payable on this MCP server's configured network
sort / orderstringOptionalDefaults to decision_readiness / desc
limitnumberOptionalNumber of results (1–10, default 5)

decision_readiness ranks pre-purchase inspectability. It does not guarantee response correctness or data quality.

get_payment_history

Fetches the payment history for this MCP server's wallets (NONAGON_PRIVATE_KEY, plus NONAGON_EVM_PRIVATE_KEY when set), including inline payments made via pay_and_call. History is scoped to the wallet, not the session.

With an EVM wallet configured, both wallets' histories are merged newest first and each row carries wallet (solana / ethereum). wallets reports each wallet's result (status: ok / error); if one wallet fails, the other wallet's rows are returned with partial: true. For EVM payments a missing history row does not prove the authorization was not executed (on unknown, check authorizationState(from, nonce)).

Note: To use this tool, the MCP server must be configured with NONAGON_LINK_BASE_URL.

ParameterTypeRequiredDescription
limitnumberOptionalNumber of records (1–50, default 10)
statusstringOptionalFilter by payment status (settled / authorized / failed / refunded, etc.)

get_spending

Fetches cumulative spending (settled + authorized) for this MCP server's wallets by period, with a per-currency breakdown. Useful for budget tracking.

The top-level total / count / byStatus / byCurrency / period / from are the Solana wallet's values, and total / count / byStatus are USDC only. With an EVM wallet configured the response also has wallets (each wallet's byCurrency) and assets (one entry per wallet × currency). Amounts are never added across wallets or currencies; read assets for JPYC and for the EVM wallet. If one wallet fails, the response has partial: true and only the other wallet's values.

Note: To use this tool, the MCP server must be configured with NONAGON_LINK_BASE_URL.

ParameterTypeRequiredDescription
periodstringOptionalAggregation period: day (24h) / week (7d) / month (30d) / all (default)

4. Method B: Direct x402 protocol integration

4-1. Overview of the x402 protocol

x402 is a micropayment protocol that uses the HTTP 402 (Payment Required) response.

Agent → Provider API → 402 Payment Required (PaymentRequirements)
Agent → Signs a USDC transfer transaction offline on Solana
Agent → Provider API + X-PAYMENT-* headers
Provider → Nonagon Link /verify → verification OK
Provider → Returns response
Provider → Nonagon Link /settle → submitted to the blockchain

4-2. Integration using @x402/fetch

import { wrapFetchWithPayment, x402Client } from '@x402/fetch'
import { ExactSvmScheme } from '@x402/svm'
import { createKeyPairSignerFromBytes } from '@solana/kit'
import bs58 from 'bs58'

// 1. Initialize the Solana signer (private key of the payment wallet)
const privateKeyBytes = bs58.decode(process.env.NONAGON_PRIVATE_KEY!)
const signer = await createKeyPairSignerFromBytes(privateKeyBytes)

// 2. Initialize the x402 client
const client = new x402Client()
client.register(
  'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', // mainnet (devnet is solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1)
  new ExactSvmScheme(signer)
)

// 3. Create a payment-enabled fetch
const payFetch = wrapFetchWithPayment(fetch, client)

// 4. Call the API (if a 402 is returned, it automatically pays and retries the request)
const response = await payFetch(
  'https://api.nnglink.ai/api/proxy/weather-api/current',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ city: 'Tokyo' }),
  }
)

const data = await response.json()
console.log(data)

4-3. Facilitator API reference

GET /api/facilitator/supported

Returns information about supported blockchains and tokens.

Response:

{
  "x402Version": 2,
  "kinds": [
    {
      "scheme": "exact",
      "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
      "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "feePayer": "<facilitator fee payer public key>"
    }
  ]
}

POST /api/facilitator/verify

Verifies the Agent's partially signed transaction and issues a settleToken.

Request: Conforms to the x402 protocol specification (HMAC-SHA256 signature + nonce)

Response:

{
  "valid": true,
  "settleToken": "<JWT>"
}

POST /api/facilitator/settle

Co-signs the verified transaction, submits it to the blockchain, and finalizes the payment.

Request: settleToken + HMAC-SHA256 signature

Response:

FieldDescription
successtrue (payment settled)
transactionSolana transaction signature
networke.g. solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
payerAgent wallet address
accessTokenIssued access token (agt_ format). Treat as a secret — never log it
accessTokenExpiresAtAccess token expiry (ISO 8601)

5. Searching for APIs

5-1. Listing search API

GET /api/listings?category=weather&sort=price_usdc&order=asc&limit=10
ParameterDescription
qKeyword search (partial match on name / description)
categoryFilter by category
chain / networkId / currencyFilter by payment chain, CAIP-2 network, or currency
maxPriceMaximum price in the selected currency; currency is required
health / minRatingCountFilter by current health and settled-payment rating evidence
sortSort by update time, price, name, rating, uptime, or decision_readiness (default: updated_at)
orderSort order (asc / desc, default: desc)
limitNumber of results (default: 20, max: 100)
offsetPagination (default: 0)

Response:

{
  "total": 42,
  "limit": 20,
  "offset": 0,
  "listings": [
    {
      "id": "uuid",
      "name": "Weather API",
      "description": "API that returns the current weather",
      "category": "weather",
      "endpointUrl": "https://api.nnglink.ai/api/proxy/weathercorp/weather-api",
      "httpMethod": "POST",
      "priceUsdc": "0.010000",
      "listingType": "proxy",
      "timeoutSeconds": 60,
      "providerId": "uuid",
      "providerName": "WeatherCorp",
      "hasSchema": true,
      "hasExample": true,
      "hasResponseSchema": true,
      "hasResponseSample": true,
      "coverage": null,
      "updatedAt": "2026-05-10T09:00:00Z",
      "openapiUrl": "/api/providers/<providerId>/listings/<id>/openapi"
    }
  ]
}

5-2. Provider public information API

GET /api/providers/{id}/public

Retrieves the Provider's public profile and the list of Listings it offers.


6. Calling the Proxy API

When calling a Provider API through the Nonagon Link proxy:

POST /api/proxy/{providerSlug}/{listingSlug}
  • Authentication: x402 protocol (automatic payment) or a pre-obtained access token
  • The Provider's API Key is injected server-side by Nonagon Link (never exposed to the Agent)
  • SSRF protection: private IPs / loopback are automatically rejected

7. Security considerations

Private key management

  • Manage NONAGON_PRIVATE_KEY via environment variables and never hardcode it in code
  • Use Secrets Manager or similar in CI/CD

USDC balance

  • Confirm you have a sufficient USDC balance before paying
  • If the balance is insufficient, a 402 response is returned

Network

  • devnet: for testing (free USDC)
  • mainnet: for production (real USDC)
  • Be careful not to mix up environments

8. Error codes

HTTPCodeDescription
402PAYMENT_REQUIREDUSDC payment required
401UNAUTHORIZEDAuthentication failed / invalid token
403FORBIDDENInsufficient permissions
404NOT_FOUNDListing / Provider not found
422NONCE_EXPIREDnonce expired (5 minutes)
429RATE_LIMITEDRate limit exceeded
502PROXY_PROVIDER_ERRORThe Provider API returned an error
503SERVICE_UNAVAILABLEService temporarily unavailable

9. SDKs and reference links

ResourceURL
x402 protocol specificationhttps://www.x402.org
@x402/fetch (Agent SDK)https://www.npmjs.com/package/@x402/fetch
@x402/svm (Solana)https://www.npmjs.com/package/@x402/svm
MCP specificationhttps://modelcontextprotocol.io
MCP setup guide/docs/mcp

当サイトでは、利用状況の把握と改善のためにアクセス解析 (Google アナリティクス) を使用します。「同意する」を選ぶと、閲覧ページ等の計測情報が Google LLC(米国)へ送信されます。詳細: プライバシーポリシー / 利用者情報の外部送信について