1. Discover
Read /skills.json for the compact catalog, /openapi.json for request schemas, or /llms.txt for a short index.
Agent integration guide
Every paid operation has a strict JSON schema, fixed advertised price, public synthetic sample, and x402 payment challenge. No seller-account credentials are needed.
Read /skills.json for the compact catalog, /openapi.json for request schemas, or /llms.txt for a short index.
Fetch each catalog entry's sample_url before purchase. Treat samples as synthetic output examples, not customer data or marketplace guarantees.
POST schema-valid JSON. A paid deployment returns 402 Payment Required; an x402 client signs the accepted Base USDC authorization and retries the same request.
The paid retry returns 202. Poll its status_url with X-Job-Token; use the same header for artifact_url when one is returned.
The catalog names the exact endpoint, fixed USD price, OpenAPI schema reference, sample, checkout, and skill document for every enabled tool.
curl -sS https://friday-seller-tools-production.up.railway.app/skills.json | \
jq '.skills[] | {id, price, endpoint_url, input_schema_ref, sample_url}'
curl -sS https://friday-seller-tools-production.up.railway.app/v1/examples/title_optimizer | jq .
This raw request lets an agent validate the operation and inspect the server's payment requirements. On the paid deployment it stops at 402; curl does not sign or spend.
curl --include --request POST \
https://friday-seller-tools-production.up.railway.app/v1/listing/title-optimizer \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: replace-with-a-unique-value' \
--data '{"product":{"marketplace":"amazon_us","brand":"Example Brand","product_name":"Insulated Stainless Steel Water Bottle","category":"water bottles","features":["24 ounce capacity","leak-resistant lid"],"benefits":["Reusable for daily hydration"],"target_audience":"commuters and hikers","keywords":["insulated water bottle","steel bottle"],"verified_claims":[],"forbidden_claims":[],"materials":["stainless steel"],"price_usd":24.0},"current_title":"Example Brand Water Bottle"}'
Install pip install 'x402[evm,httpx]>=2.16,<3', set EVM_PRIVATE_KEY outside source control, and fund that scoped wallet with USDC on eip155:8453. This example caps accepted payment requirements at $0.02 and automatically handles the 402 retry.
import asyncio
import json
import os
import uuid
from eth_account import Account
from x402 import max_amount, x402Client
from x402.http.clients import x402HttpxClient
from x402.mechanisms.evm.exact import ExactEvmScheme
BASE_URL = "https://friday-seller-tools-production.up.railway.app"
NETWORK = "eip155:8453"
MAX_PAYMENT = 20_000 # $0.02 USDC, in 6-decimal base units
request_body = {
"product": {
"marketplace": "amazon_us",
"brand": "Example Brand",
"product_name": "Insulated Stainless Steel Water Bottle",
"category": "water bottles",
"features": [
"24 ounce capacity",
"leak-resistant lid"
],
"benefits": [
"Reusable for daily hydration"
],
"target_audience": "commuters and hikers",
"keywords": [
"insulated water bottle",
"steel bottle"
],
"verified_claims": [],
"forbidden_claims": [],
"materials": [
"stainless steel"
],
"price_usd": 24.0
},
"current_title": "Example Brand Water Bottle"
}
async def main():
# Use a scoped purchasing wallet funded only for the agent's approved budget.
signer = Account.from_key(os.environ["EVM_PRIVATE_KEY"])
payments = x402Client()
payments.register(NETWORK, ExactEvmScheme(signer))
payments.register_policy(max_amount(MAX_PAYMENT))
async with x402HttpxClient(payments, timeout=30.0) as client:
response = await client.post(
BASE_URL + "/v1/listing/title-optimizer",
json=request_body,
headers={"Idempotency-Key": str(uuid.uuid4())},
)
response.raise_for_status()
job = response.json() # HTTP 202 after the automatic paid retry
for _ in range(90):
status = await client.get(
job["status_url"],
headers={"X-Job-Token": job["job_token"]},
)
status.raise_for_status()
result = status.json()
if result["status"] == "succeeded":
print(json.dumps(result["result"], indent=2))
return
if result["status"] == "failed":
raise RuntimeError(result.get("error") or "job failed")
await asyncio.sleep(2)
raise TimeoutError("job did not finish within 180 seconds")
asyncio.run(main())
job_token private. Job inputs and artifacts expire after 24 hours by default.