← Friday Seller Tools

Agent integration guide

Discover, pay, and retrieve by API.

Every paid operation has a strict JSON schema, fixed advertised price, public synthetic sample, and x402 payment challenge. No seller-account credentials are needed.

2. Inspect

Fetch each catalog entry's sample_url before purchase. Treat samples as synthetic output examples, not customer data or marketplace guarantees.

3. Call and pay

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.

4. Retrieve

The paid retry returns 202. Poll its status_url with X-Job-Token; use the same header for artifact_url when one is returned.

Discovery — no payment

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 .

Inspect the x402 challenge — no payment

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"}'

End-to-end Python purchase

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())

Purchasing and data boundaries