API Reference

Agent Toolbelt

The x402 service marketplace — discover and call the gateway's paid tools with the same wallet that pays for chat

Solvela is not only an LLM gateway. The same wallet, the same 402 challenge, and the same settlement path also buy tools: gateway-hosted endpoints that take USDC-SPL per call and return structured data. Today the toolbelt holds web search and Solana token prices.

Everything on this page is machine-discoverable at GET /v1/services — no API key, no account, no signup.

Discovery: GET /v1/services

Returns the service registry. Free, unauthenticated, and the canonical machine-readable answer to "what can I buy here?".

curl https://api.solvela.ai/v1/services
{
  "object": "list",
  "data": [
    {
      "id": "solana-price",
      "name": "Solana Price Data",
      "category": "data",
      "endpoint": "/v1/solana/price",
      "x402_enabled": true,
      "internal": true,
      "description": "x402-paid Solana token prices (Jupiter upstream); per-mint USD price, batch up to 50 mints, unknown mints returned as null",
      "pricing": "$0.00105/query (incl. 5% platform fee)",
      "chains": ["solana"],
      "source": "config",
      "price_per_request_usdc": 0.001
    },
    {
      "id": "web-search",
      "name": "Web Search",
      "category": "search",
      "endpoint": "/v1/search",
      "x402_enabled": true,
      "internal": true,
      "description": "x402-paid web search (Tavily upstream); normalized title/url/snippet results",
      "pricing": "$0.0105/query (incl. 5% platform fee)",
      "chains": ["solana"],
      "source": "config",
      "price_per_request_usdc": 0.01
    }
  ],
  "total": 2
}

Response fields

FieldTypeDescription
idstringStable machine identifier ([a-z0-9-], max 64 chars)
namestringHuman-readable service name
categorystringGrouping label — search, data, …
endpointstringRelative path for internal (gateway-hosted) services; absolute https:// URL for external ones
x402_enabledbooleanWhether the endpoint requires x402 payment
internalbooleantrue when the gateway itself serves the endpoint
descriptionstring | nullFree-text description
pricingstringHuman-readable pricing label, fee-inclusive
chainsstring[]Payment chains — always ["solana"] today
sourcestring"config" (loaded from config/services.toml) or "api" (registered at runtime)
price_per_request_usdcfloat | nullFlat per-call price before the 5% platform fee

Entries are sorted alphabetically by id, so the response is deterministic.

Filtering

Query paramEffect
?category=<cat>Only services in that category (case-insensitive)
?internal=trueOnly gateway-hosted services
?internal=falseOnly external, third-party x402 endpoints
curl "https://api.solvela.ai/v1/services?category=data"

Note

price_per_request_usdc is the pre-fee price. pricing is the post-fee label. For web-search, 0.01 and $0.0105/query describe the same tool — the difference is the 5% platform fee. Always pay the amount from the 402 challenge, never a number you computed yourself.

POST /v1/search

x402-paid web search. The gateway holds the upstream search key (Tavily) and calls it server-side; your client never sees it.

Price: $0.01 provider cost + $0.0005 platform fee = $0.0105 per call (10500 atomic USDC).

Request

curl -X POST https://api.solvela.ai/v1/search \
  -H "Content-Type: application/json" \
  -H "payment-signature: <base64 PaymentPayload>" \
  -d '{"query": "solana x402 protocol", "max_results": 5}'
FieldTypeRequiredNotes
querystringyesTrimmed; must be non-empty and at most 2000 characters
max_resultsintegernoClamped to 1..=20. Omit to use the adapter's default

The request body is capped at 64 KiB.

Response

{
  "query": "solana x402 protocol",
  "results": [
    {
      "title": "x402: an HTTP-native payment protocol",
      "url": "https://example.com/x402",
      "snippet": "x402 revives HTTP 402 Payment Required as a machine-payable ..."
    }
  ],
  "provider": "tavily"
}

results may be empty. provider names the adapter that answered and is informational — do not branch on it.

Warning

The price is flat and charged per call, not per result. A query that returns zero results costs exactly the same $0.0105. Payment is settled before the upstream search runs.

POST /v1/solana/price

x402-paid USD prices for SPL token mints. The gateway queries the Jupiter Price API server-side.

Price: $0.001 provider cost + $0.00005 platform fee = $0.00105 per call (1050 atomic USDC).

Request

curl -X POST https://api.solvela.ai/v1/solana/price \
  -H "Content-Type: application/json" \
  -H "payment-signature: <base64 PaymentPayload>" \
  -d '{"mints": [
        "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        "So11111111111111111111111111111111111111112"
      ]}'
FieldTypeRequiredNotes
mintsstring[]yes1 to 50 entries. Each must be a base58 address decoding to exactly 32 bytes

The request body is capped at 16 KiB. An empty list, a list over 50 entries, or any non-base58 mint is a 400 — rejected before any payment is taken.

Response

{
  "prices": [
    {
      "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "price": {
        "usd_price": 0.9998736983419257,
        "decimals": 6,
        "block_id": 431537809,
        "price_change_24h": -0.0024366454583391443
      }
    },
    {
      "mint": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
      "price": null
    }
  ],
  "source": "jupiter",
  "as_of": "2026-07-08T00:00:00Z"
}
FieldTypeDescription
prices[].mintstringThe requested mint, echoed back
prices[].priceobject | nullnull when the upstream has no reliable price for that mint
prices[].price.usd_pricenumberUSD price of one whole token
prices[].price.decimalsinteger | nullToken decimals (display metadata)
prices[].price.block_idinteger | nullChain slot the price was derived at (recency indicator)
prices[].price.price_change_24hnumber | null24-hour percentage change (-3.99 = −3.99%)
sourcestringAdapter that answered ("jupiter"). Informational
as_ofstringRFC 3339 UTC timestamp of when the upstream answered

Behavior you can rely on

  • One entry per requested mint, in request order. The prices array is a positional mirror of your mints array.
  • An unpriceable mint is a null, never a batch failure. The upstream omits mints it cannot price; the gateway normalizes each omission to an explicit price: null so a single bad mint never poisons the other 49.
  • Send each mint once. Duplicates are tolerated but the repeat comes back price: null — the upstream entry is consumed by the first occurrence.
  • The flat fee is charged regardless of how many mints resolve. A 50-mint request where every entry comes back null costs the same $0.00105 as one that resolves all 50.
  • Responses are not cached. Prices are time-sensitive, so every paid call hits the upstream. (Solvela's response cache is also wallet-agnostic, which would be wrong for market data.)

Payment

Both tools speak the same x402 dialect as the rest of the gateway: no payment-signature header returns a 402 carrying the quote.

{
  "x402_version": 2,
  "resource": { "url": "/v1/solana/price", "method": "POST" },
  "accepts": [
    {
      "scheme": "exact",
      "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
      "amount": "1050",
      "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "pay_to": "<gateway recipient wallet>",
      "max_timeout_seconds": 300
    }
  ],
  "cost_breakdown": {
    "provider_cost": "0.001000",
    "platform_fee": "0.000050",
    "total": "0.001050",
    "currency": "USDC",
    "fee_percent": 5
  },
  "error": "Payment required"
}

Sign a transfer for accepts[0].amount, base64-encode the PaymentPayload, and resend with the payment-signature header. The cost_breakdown always separates provider cost from the 5% platform fee.

Accepted schemes

SchemeAdvertised in the 402?Accepted?
exactYesYes
channelNo — invoked by the clientYes
escrowNoNo — rejected as a scheme/payload mismatch

Note

These tools are narrower than /v1/chat/completions, which also accepts escrow. The marketplace tools cost a fraction of a cent, so escrow's two-transaction round trip buys nothing. An escrow payload sent to a toolbelt endpoint is rejected outright — never silently downgraded to a direct transfer.

One channel, every surface

This is where the toolbelt earns its name. A spend-down channel is funded once on-chain, and its vouchers are accepted on /v1/chat/completions, /v1/messages, /v1/search, and /v1/solana/price. An agent that opens one channel can then reason, search, and price tokens across hundreds of calls without a single further Solana transaction — each call just signs a fresh cumulative voucher over that exact request body.

Channel draws on the toolbelt are strictly serial per channel and carry the same guarantee as everywhere else: the gateway can never settle more than the highest cumulative amount you have signed, and never more than your deposit.

Note

Channel support ships behind an operator flag. When channels are disabled — or the gateway is running without PostgreSQL or Redis — a voucher-paid request returns 404, never a silently un-ledgered draw.

Errors

StatusWhen
400Malformed body, empty query / mints, over-cap batch, non-base58 mint, or a payment payload whose network / asset / pay_to / method does not match the 402
402No payment-signature header (body is the PaymentRequired object above), or verification failed / replayed
403The payer wallet requires per-tenant budgeting — use POST /v1/chat/completions, which enforces that matrix
404Channel voucher supplied but channels are unavailable, or the channel is not open
502The upstream tool provider failed
503The tool is not configured on this gateway (missing TAVILY_API_KEY for search, or a missing/unpriced registry entry for either tool)

Body validation runs before any payment work. A request that could never have run — empty query, 51 mints, a mint that isn't base58 — is a 400 with nothing charged, no 402 issued, and no settlement attempted.

Upstream failure: the settle posture differs by scheme

A 502 from these endpoints has a static body and never leaks upstream internals:

{ "error": "search provider error" }
{ "error": "price provider error" }

What it costs you depends on how you paid:

SchemeOrder of operationsUpstream fails →
exactSettle on-chain, then call the toolYou are charged. The USDC moved on-chain before the tool ran; it cannot be un-moved. A spend row and a receipt are written, and the 502 still carries an x-solvela-receipt header so the charge stays auditable.
channelCall the tool, then advance the ledgerYou are not charged. The voucher is not persisted, the channel's cumulative last does not advance, your deposit is untouched, and no spend or receipt row is written. Re-sign the same cumulative on your next attempt.

Warning

This asymmetry is real, not an oversight. An exact payment is an on-chain transfer that must be confirmed before the gateway will serve you — once confirmed, a failing upstream is a charge-without-delivery event that the receipt makes visible. A channel draw is an off-chain ledger entry the gateway controls, so it can honestly withhold the debit when delivery fails. If charge-on-upstream-failure is unacceptable for your workload, pay with a channel.

Configuration

Tools are registered in config/services.toml, which is the single source of truth for both the price the route quotes and the entry GET /v1/services lists. Operators enable them like this:

# Web search — requires an upstream key; absent → /v1/search returns 503.
export TAVILY_API_KEY=tvly-...

# Solana prices — the Jupiter lite host is keyless, so no key is required.
# Set this only to use the keyed host (same contract, higher rate limits).
export JUPITER_API_KEY=...

For /v1/solana/price the registry entry alone is the enablement gate: remove or unprice the solana-price entry and the route returns 503 — never a free or default-priced response.

Note

Source: crates/gateway/src/routes/search.rs, crates/gateway/src/routes/price.rs, and config/services.toml in the monorepo. The MCP server exposes both tools as web_search and solana_price, handling the 402 dance for you.