Merchant tools
Nine tools that run a catalogue from your own key
| Tool | Acts by | What it does |
|---|---|---|
activate_merchant | transaction + REST | Sets the on-chain active flag and creates the backend records |
add_product | transaction + REST | Writes a product on-chain and syncs its metadata |
set_product_commission | transaction | Changes one product's commission rate |
set_product_active | transaction | Lists or delists one product |
grant_merchant_role | transaction | Delegates one capability to another address |
revoke_merchant_role | transaction | Takes that capability back |
set_merchant_profile | REST | Replaces the public profile copy |
check_product_readiness | REST | Probes an endpoint's referral wiring |
get_merchant_stats | REST | Reads the merchant's own revenue figures |
Repeating a call is the retry path, not a hazard. Every write here is idempotent: the transaction is skipped when the chain already says what you are asking for, and an already-registered record counts as done. A call that failed partway through can simply be made again.
The six on-chain tools take an optional chain_id, a decimal string overriding the CHAIN_ID
environment variable. The three REST tools do not.
The signer settings these tools read — MERCHANT_REGISTRY_ADDRESS, PRODUCT_REGISTRY_ADDRESS and
REFERRER_API_URL, beside the key and chain settings — are in the signer's
configuration.
activate_merchant
Brings a merchant online. Three steps in one call, in this order because each is a precondition of
the next: set the on-chain active flag (MerchantRegistry.setActive), register the agent record,
register the merchant record. The merchant registration needs an agents row, and refuses a
merchant that is not active on-chain.
Call this before set_merchant_profile or add_product, both of which need the backend record
it creates.
{ "merchant_id": "42" }Parameters
| Name | Type | Notes |
|---|---|---|
merchant_id | string | Required. Decimal. The signing key must own it |
chain_id | string | Optional. Decimal, overrides CHAIN_ID |
Returns
tx_hash, already_active, and agent_registered / merchant_registered, each created or
already_registered. A partial run reports which steps landed.
Suspended merchants cannot reactivate themselves. Suspension is an admin action on
MerchantRegistry and this tool cannot undo it.
add_product
Writes a product on-chain (ProductRegistry.addProduct, which holds the commission rate and the
active flag) and syncs its metadata to the backend so it becomes discoverable.
A product with only the on-chain half is payable but invisible. If the chain write lands and
the metadata sync does not, the tool says so — repeat the call and only the sync is retried,
because addProduct itself cannot be re-sent.
{
"merchant_id": "42",
"product_id": "0x1f9a…",
"commission_bps": 1000,
"endpoint_url": "https://api.example.com/x402/rpc",
"name": "Low-latency RPC"
}Parameters
| Name | Type | Notes |
|---|---|---|
merchant_id | string | Required. Decimal |
product_id | string | Required. 0x-prefixed 32-byte hex, unique per merchant — typically keccak256 of a slug you choose |
commission_bps | integer | Required. 0–10000; 1000 = 10% |
endpoint_url | string | Required. The product's x402 payment endpoint |
name | string | Required. Buyers and discovery see it |
description | string | Optional |
category | string | Optional. A slug from GET /v1/product-categories, such as chain.rpc. Anything else is refused before addProduct is sent |
product_card_uri | string | Optional |
chain_id | string | Optional |
Returns
tx_hash, already_on_chain, commission_bps as it now stands on-chain, metadata_synced, and
next_steps. If the product already exists at a different rate the rate is not changed, and
commission_warning says so — use set_product_commission.
Requires the merchant to be active on-chain and registered with the backend. All products are repeat-purchase; one-off products were retired from the protocol.
Afterwards, call check_product_readiness on the endpoint_url.
set_product_commission
Changes the commission rate a merchant pays referrers for one product
(ProductRegistry.setCommission).
The new rate is read live by SplitRouter at settlement. It applies to every purchase that
settles after this call — including ref links already issued and payments already authorised under
the old rate. Nothing on-chain locks the previous rate.
{ "merchant_id": "42", "product_id": "0x1f9a…", "commission_bps": 1500 }Parameters
| Name | Type | Notes |
|---|---|---|
merchant_id | string | Required |
product_id | string | Required. On-chain bytes32, not a slug |
commission_bps | integer | Required. 0–10000 |
chain_id | string | Optional |
Returns
tx_hash, the new commission_bps, and set_by — the signer address that sent it.
The key must own merchant_id or hold COMMISSION_MANAGER_ROLE for it. That role is separate
from PRODUCT_MANAGER_ROLE, so a key that can add products cannot necessarily reprice them.
set_product_active
Lists or delists one product (ProductRegistry.setProductActive).
active is required, so a call that omits it fails rather than delisting.
{ "merchant_id": "42", "product_id": "0x1f9a…", "active": false }Parameters
| Name | Type | Notes |
|---|---|---|
merchant_id | string | Required |
product_id | string | Required |
active | boolean | Required. true accepts new purchases and ref links, false stops them |
chain_id | string | Optional |
Returns
tx_hash, active, and set_by.
Delisting does not touch existing escrows or published reviews, and does not remove the product —
set it active again to relist. The key must own merchant_id or hold PRODUCT_MANAGER_ROLE.
Errors
| Message | Cause |
|---|---|
active is required (true to list the product, false to delist it) | The field was omitted |
grant_merchant_role
Delegates one capability of a merchant to another address (MerchantRegistry.grantRole),
without handing over the merchant identity.
| Role | Lets the holder |
|---|---|
SETTLEMENT_SIGNER_ROLE | Sign the merchant's x402 /verify and /settle requests to the facilitator |
PRODUCT_MANAGER_ROLE | Add and delist products |
COMMISSION_MANAGER_ROLE | Change commission rates |
The three are independent — granting one grants nothing else — and each is scoped to this merchant
alone. The signing key must own merchant_id: holding a role never lets a key pass that role
on.
SETTLEMENT_SIGNER_ROLE is how a merchant server gets its own hot key instead of running the owner
key, which controls rates, delisting and the payout wallet. Grant before deploying that key —
the facilitator refuses a signature it cannot place, on every sale, whatever its rollout setting.
{ "merchant_id": "42", "role": "SETTLEMENT_SIGNER_ROLE", "account": "0xabc…" }Parameters
| Name | Type | Notes |
|---|---|---|
merchant_id | string | Required. The signing key must own it |
role | string | Required. One of the three above |
account | string | Required. The 0x address to delegate to |
chain_id | string | Optional |
Returns
tx_hash, role_id (the role's bytes32), has_role — the state after the call — and
no_change, true when the account already held it and nothing was sent.
revoke_merchant_role
Takes a per-merchant role back (MerchantRegistry.revokeRole). Same arguments and the same
ownership rule as grant_merchant_role.
Revoking takes effect immediately. For SETTLEMENT_SIGNER_ROLE, stop that key signing first
— otherwise its next /verify or /settle is refused and sales fail. To rotate a settlement key:
grant the new one, deploy it, then revoke the old one.
{ "merchant_id": "42", "role": "SETTLEMENT_SIGNER_ROLE", "account": "0xabc…" }Returns
tx_hash, has_role (false after a successful revoke) and no_change.
set_merchant_profile
Sets the merchant's profile — public description and landing page URL, and the default
commission the console prefills for new products — through POST /v1/merchant/profile, signed
with the merchant's own key.
This is a partial update. Pass only the fields to change: an omitted field keeps its stored value, and an empty string clears a text field. At least one field is required.
{ "merchant_id": "42", "default_commission_bps": 1000 }Parameters
| Name | Type | Notes |
|---|---|---|
merchant_id | string | Required. The signing key must own it |
description | string | Optional. Empty clears it |
landing_page_url | string | Optional. http/https only. Empty clears it |
default_commission_bps | integer | Optional. 0–10000. Off-chain default; on-chain commission stays per product (set_product_commission) |
The description and URL are public. The backend owns the limits and its rejection names the
offending field — see POST /v1/merchant/profile. Requires
activate_merchant first: without a backend record this fails with a conflict pointing there.
check_product_readiness
Probes a product's x402 endpoint and reports whether it is wired for referral payments
(POST /v1/merchant/product/readiness). The backend fetches the resource with the referral-mode
header set and checks three things: that it answers 402, that payTo is this chain's
SplitRouter, and that the attribution token is echoed back.
A misconfigured endpoint returns pass: false with a per-check breakdown. That is a successful
probe, not an error.
{ "merchant_id": "42", "resource": "https://api.example.com/x402/rpc" }Parameters
| Name | Type | Notes |
|---|---|---|
merchant_id | string | Required — but it only selects the signing key |
resource | string | Required. The product's x402 endpoint URL |
Returns
pass, resource, and checks — each with name, ok and a detail when it failed.
The endpoint is authenticated but not owner-scoped, so this does not assert that you own the
resource. Call it after add_product and before advertising a product: an endpoint that fails
these checks takes payments that never attribute a referral.
get_merchant_stats
Reads a merchant's own performance figures (GET /v1/merchant/{id}/stats).
Owner-only, which is why it lives on referrer-signer rather than the keyless server: a merchant
sees only its own revenue.
{ "merchant_id": "42" }Returns
conversions, revenue_gross, escrow_holding, escrow_settled, product_count and
active_product_count. Amounts are USDC base units, six decimals. product_count includes
inactive products; active_product_count is the subset that is listed.
Next steps
- Signer tools — the fourteen signing and transaction tools on the same server
- Discovery tools —
get_merchant,get_productandget_products, the keyless reads of what these tools write - Merchant routes — the REST surface three of these wrap