# For AI agents

How an autonomous agent discovers this service, pays, and what it gets back.

## Discovery

Three artifacts are published for machines:

1. **[`skill.md`](https://github.com/nirholas/x402-hotel-search/blob/main/skill.md)**
   (repo root, also served at `{BASE_URL}/skill.md`) — plain-language
   instructions an LLM can read directly: endpoints, prices, params, response
   schemas, error codes, and budgeting notes.
2. **`{BASE_URL}/.well-known/x402`** — the machine-readable price sheet
   (`x402Version`, `resources[]` with price/networks/asset/input+outputSchema).
   Each resource carries an `accepts[]` array listing **both rails**, so a
   budgeting agent knows before it spends whether it can pay from its Base
   balance, its Solana balance, or either. Registries like
   [x402scan.com](https://x402scan.com), the **x402 Bazaar**, and
   [agentic.market](https://agentic.market) index this format — submit your
   deployment URL there so agents find you without prior knowledge.
3. **`openapi.json`** (OpenAPI 3.1, including the 402 response schema) for
   codegen-style clients.

Every `accepts[]` entry in a 402 also carries an `outputSchema` with two halves:
`input` describes how to build the request (method, path and query parameters)
and `output` is the JSON Schema of the 200 body you get once you have paid. Both
are generated from `openapi.json`, so a single 402 challenge is enough to call
`/search` or `/offer/:offerId` correctly without fetching anything else first.

## Protocol version

This service speaks **x402 v1** — `x402Version: 1` in every challenge. That is
what the shipped `x402-fetch` clients expect, so it is the version to code
against today. x402 v2 changes the challenge shape (`extensions.bazaar.schema`,
CAIP-2 network identifiers) and is a planned future upgrade for agentcash
compatibility; it is not served yet.

## Paying — two rails, your pick

Every paid route answers an unpaid request with a 402 whose `accepts[]` array
holds one payment-requirements object per rail:

| Rail | Network | Asset | payTo |
| --- | --- | --- | --- |
| EVM | `base-sepolia` (or `base`) | USDC | `0x40252CFDF8B20Ed757D61ff157719F33Ec332402` |
| Solana | `solana` (or `solana-devnet`) | USDC | `WwwuGbqHrwF5RG89KhUbmRWEvjnRH9k5kVM5p7T3WwW` |

Match on `network`, sign for that chain, and retry with `X-PAYMENT`. The price,
the route, and the returned artifact are identical either way.

### EVM with `x402-fetch`

```ts
import { wrapFetchWithPayment } from "x402-fetch";
import { privateKeyToAccount } from "viem/accounts";

const payFetch = wrapFetchWithPayment(fetch, privateKeyToAccount(process.env.PRIVATE_KEY), 50_000n);
const res = await payFetch(
  "https://hotels.example.com/search?cityCode=NYC&checkInDate=2026-09-15&checkOutDate=2026-09-18",
);
const { source, offers } = await res.json();   // rooms delivered now, not queued
```

The wrapper handles 402 → sign EIP-3009 USDC authorization → retry. The third
argument caps spend in atomic units: `50_000n` refuses anything over $0.05.

### Solana

```ts
const res = await fetch(url);                        // 402
const { accepts } = await res.json();
const sol = accepts.find(a => a.network.startsWith("solana"));

// Build an SPL USDC transfer of `sol.maxAmountRequired` (atomic, 6 decimals)
// to `sol.payTo` for the mint in `sol.asset`, sign it, and wrap it:
const header = Buffer.from(JSON.stringify({
  x402Version: 1, scheme: "exact", network: sol.network,
  payload: { transaction: signedTxBase64 },
})).toString("base64");

const paid = await fetch(url, { headers: { "X-PAYMENT": header } });
const artifact = await paid.json();
```

If `extra.feePayer` is present on the Solana accept, that sponsor account pays
the SOL network fee — the caller needs only USDC.

## What you get back

- **`/search`** — complete offers, cheapest first: hotel name, star rating,
  address, coordinates, amenity codes, room type and bed configuration, board
  type, cancellation policy, and both total and per-night price.
- **`/offer/:offerId`** — the same offer plus an explicit `priceBreakdown` and a
  `priceGuarantee` string spelling out how firm the rate is.
- The USDC settlement receipt is in the `X-PAYMENT-RESPONSE` response header —
  base64 JSON with `rail` (`evm` | `solana`), `network`, `transaction`, and
  `payer`. Decode with `decodeXPaymentResponse` from `x402-fetch`, or
  `JSON.parse(atob(header))`.

Nothing here is a job you come back for. Every paid response contains the thing
you bought.

## Don't double-pay for the same information

`/search` already returns full offers. Calling `/offer/:offerId` on every result
doubles your cost for data you are holding. Pay for it when you need the
breakdown spelled out, or a fresh re-price on the one candidate a user is
actually choosing.

## Always read `source`

Every paid response carries `source: "amadeus" | "fixture"`. `"fixture"` means
the operator has no Amadeus keys configured and the numbers are deterministic
synthetic data. They are stable and useful for building against — and wrong as
real rates. If you surface prices to a user, surface the source with them.
`GET /health` answers the same question for free before you spend anything.

## Budgeting

- Shop a city: **$0.005**.
- Drill into one candidate: **+$0.003**.
- Read `maxAmountRequired` from the 402 rather than hardcoding prices, so an
  operator's repricing never surprises you.

## MCP integration

To give Claude these abilities as tools (`search_hotels`, `get_hotel_offer`,
plus a free `service_info`), see
[`examples/mcp-tool.md`](https://github.com/nirholas/x402-hotel-search/blob/main/examples/mcp-tool.md) —
a complete MCP server plus the `claude_desktop_config.json` entry.

## Operator checklist for agent traffic

- Keep `/.well-known/x402` accurate — agents budget from it before paying, and it
  must list both rails if you accept both.
- Keep both `PAY_TO_ADDRESS` and `SOLANA_PAY_TO_ADDRESS` set unless you mean to
  turn a rail off; dropping one halves the wallets that can pay you.
- Set `PUBLIC_BASE_URL` in production so the `resource` in your 402 matches your
  real URL.
- On live Amadeus, `/search` costs two upstream calls (city → hotel ids → offers).
  Price the route above that combined cost.
- List the deployment on x402scan.com / the x402 Bazaar / agentic.market.

Questions or listing help: **nichxbt@gmail.com**
