Merchant setup
Describe your API
Turn existing endpoints into resources — by hand, from an OpenAPI document, or with the interactive wizard.
A resource is one capability an agent can invoke: an endpoint on your backend, plus its input schema, its price and where it is exposed. The map key is the resource id — it is the MCP tool name and the HTTP path segment, so it must be unique and use only A-Z a-z 0-9 . _ -.
Create the resources
resources:
basic_weather: # free: no payments block needed
name: Basic Weather
description: Current basic weather for a city.
input:
type: object
properties:
city:
type: string
description: City name, e.g. "Berlin"
required: [city]
additionalProperties: false
backend:
type: http
method: GET
url: ${MERCHANT_API_BASE_URL}/api/weather/{city}
timeoutMs: 5000
pricing:
type: free
expose: [http, mcp]
premium_report: # paid: 0.01 USDC per call
name: Premium Market Report
input:
type: object
properties: {}
additionalProperties: false
backend:
type: http
method: GET
url: ${MERCHANT_API_BASE_URL}/api/report
timeoutMs: 10000
headers:
Authorization: Bearer ${BACKEND_TOKEN}
pricing:
type: fixed
amount: "0.01"
currency: USDC
expose: [http, mcp]
payments: [x402]{city} is filled from validated input and URL-encoded. Input the URL does not consume becomes the query string on GET/DELETE and the JSON body otherwise.
agent-commerce import openapi ./openapi.yaml \
--base-url https://api.example.com \
--tag orders \
--output orders.agent-commerce.yamlThe importer writes a reviewable resources: fragment. It deliberately leaves out pricing and expose — an OpenAPI document has no opinion on what an operation costs or who may see it — so the fragment will not load until you add them. Credentials are never imported.
- Review the drafts
Each entry carries a review comment and describes the API shape only.
- Decide pricing and exposure
Add
pricing(free or a fixed amount) andexposeto each resource.--freeand--expose http,mcpwrite them for you. - Add backend authentication
Under
backend.headers, as${ENV_VAR}placeholders — never a literal credential. - Merge and check
Merge under
resources:inconfig.yaml, then runagent-commerce validateandagent-commerce doctor.
Supported: OpenAPI 3.0–3.2, YAML or JSON, internal $ref, application/json bodies, primitive path and query parameters. Full option list and support matrix: import openapi.
agent-commerce init # interactive
agent-commerce init --yes # non-interactive defaults, for scriptsThe wizard asks for:
- your backend base URL;
- which example resources to expose — a free weather lookup and a paid report;
- which protocols to enable — HTTP and MCP;
- whether to enable x402, and your merchant settlement address.
The answers are validated in memory before anything is written; an invalid combination writes nothing and names the field to fix. Use the result as a starting point, then replace the example resources with your own.
Input schemas
Schemas are closed by default at every level: an object that omits additionalProperties gets false. A resource with no input accepts nothing. The validator enforces type, properties, required, additionalProperties, primitives and enum; pattern, format, minLength and friends are ignored, and validate warns when you use them.
Path, query and body together
When one operation needs all three, name where each comes from with backend.inputBindings. Input that no binding names is not forwarded at all.
create_order:
name: Create an order
input:
type: object
properties:
path:
type: object
properties:
userId: { type: string }
required: [userId]
additionalProperties: false
query:
type: object
properties:
notify: { type: boolean }
additionalProperties: false
body:
type: object
properties:
productId: { type: string }
quantity: { type: integer }
required: [productId]
additionalProperties: false
required: [path, body]
additionalProperties: false
backend:
type: http
method: POST
url: ${MERCHANT_API_BASE_URL}/users/{userId}/orders
inputBindings:
path: path
query: query
body: body
pricing:
type: free
expose: [http, mcp]The binding rules are checked at load and again before pricing, so a malformed request never settles a payment and then fails to reach your backend. See backend.inputBindings.
Pricing
| pricing.type | Needs | Behaviour |
|---|---|---|
free | nothing | delivered without a payment proof |
fixed | amount (decimal string), currency, and payments | answers 402 until a valid proof is presented |
dynamic | — | rejected at load |