Store Setup — Getting the Pier39 Seller Agent Live on Your Storefront

This guide walks you through everything a store has to do to make itself negotiable by AI shoppers using the negotiate.v1 protocol. End state: any AI shopper agent (Claude, ChatGPT, custom bots) can land on your domain, discover that you negotiate, and close a deal.

The core onboarding is 30 minutes for a developer who already has a hosting setup. The conceptual work — defining floors and concession levers — usually takes longer because it requires real merchant judgment, not just config.


Before you start

You'll need:


Step 1 — Build your catalog

The catalog is a single JSON file. For each product, you provide three public fields (the shopper sees these) and three private fields (only the merchant agent sees them):

Public Private
name, subtitle, description floor — the lowest price you'd accept
list_price levers — 5–7 non-cash concessions, cheapest first
condition, year_made inventory_note — short context (urgency, manager latitude)

Minimum example:

{
  "store": {
    "name": "Your Store",
    "city": "Your City, ST",
    "rep_name": "Casey",
    "tagline": "Short tagline.",
    "policy_default": "Free shipping. 30-day return. Manufacturer warranty.",
    "about": "1-2 sentence story about your store."
  },
  "products": [
    {
      "id": "widget-pro",
      "kind": "widget",
      "name": "Widget Pro",
      "subtitle": "Short marketing line",
      "description": "1-2 sentence product description.",
      "year_made": 2024,
      "condition": "Brand new",
      "list_price": 199,
      "floor": 165,
      "levers": [
        "Free expedited shipping (~$15 retail)",
        "Bonus accessory kit ($29 retail)",
        "Extend warranty 12 → 24 months",
        "$30 future-order credit, valid 12 months",
        "Free setup video and quick-start kit"
      ],
      "inventory_note": "5 in stock. Aged 14 days. Standard latitude."
    }
  ]
}

The three private fields are the merchant judgment piece — see the Appendix for how to set floors, design levers, write inventory notes, and how to scale this to hundreds or thousands of products without doing it by hand.

A complete two-product working example lives at pier39-merchant-server/examples/tiny_store/catalog.json.


Step 2 — Install the merchant skill

The library auto-discovers the skill at ~/.claude/skills/pier39-merchant/. Get it there:

git clone https://github.com/sanjana-pier39/pier39-skills /tmp/pier39-skills
mkdir -p ~/.claude/skills
cp -R /tmp/pier39-skills/plugins/pier39/skills/pier39-merchant ~/.claude/skills/

Verify it landed:

ls ~/.claude/skills/pier39-merchant/SKILL.md
# should print the path with no error

If you can't put it at ~/.claude/skills/, you can put it anywhere and set PIER39_MERCHANT_SKILL_PATH=/path/to/pier39-merchant instead.


Step 3 — Install and run the server

pip install pier39-merchant-server
export ANTHROPIC_API_KEY=sk-ant-...

Then a 6-line Python script — call it serve.py, put it next to your catalog.json:

from pier39_merchant_server import serve

serve(
    catalog_path="catalog.json",
    host="0.0.0.0",
    port=8000,
)

Run it:

python serve.py

You should see:

  pier39-merchant-server listening on http://0.0.0.0:8000
  Catalog: /path/to/catalog.json
  Skill:   /Users/you/.claude/skills/pier39-merchant
  Model:   claude-sonnet-4-6
  (Ctrl-C to stop)

That's the entire backend. It serves all the endpoints the protocol requires — discovery file, catalog, chat — plus rate limiting, CORS, and OPTIONS preflight.


Step 4 — Verify the API end-to-end

In another terminal:

# 1. Discovery descriptor
curl -s http://localhost:8000/negotiate.json | python3 -m json.tool | head -10
# expect: "negotiate_protocol": "negotiate.v1"

# 2. Public catalog (hidden fields stripped)
curl -s http://localhost:8000/api/store/catalog | python3 -m json.tool

# 3. Start a chat session
curl -s "http://localhost:8000/api/store/chat/start?product_id=widget-pro"
# expect: {"session_id":"...","greeting":"Hi! I'm Casey...","next":"..."}

# 4. Send a shopper turn
SID=<paste session_id>
curl -s "http://localhost:8000/api/store/chat/$SID/say?message=Could+you+do+%24170+with+the+accessory+kit%3F"
# expect: {"message":"...merchant reply...","closed":false,"next":"..."}

If all four return real data and the merchant replies feel sane (anchored, conversational, holding the floor), you're done with backend setup.


Step 5 — Deploy to production

The server runs anywhere you can run Python. Three popular paths:

Fly.io (single command, ~5 min)

fly launch                       # answer the prompts, accept defaults
fly secrets set ANTHROPIC_API_KEY=sk-ant-...
fly deploy

Point your DNS at the Fly app, attach a TLS cert with fly certs add, and you're live.

Render

Add a new Web Service, point it at your Git repo, set the run command to python serve.py, and add ANTHROPIC_API_KEY as an env var. Render handles TLS automatically.

Your existing app server

The library is a stdlib ThreadingHTTPServer — you can run it next to your existing app on a different port and reverse-proxy /negotiate.json, /.well-known/negotiate.json, /llms.txt, and /api/store/* to it from your main nginx/Caddy config.

Don't use serverless

Serverless platforms like Vercel functions or AWS Lambda will work for the discovery files but break for chat — sessions are in-memory and instances cycle. If you need serverless, swap the in-process session store for Redis or DynamoDB.


Step 7 — Get listed in the public directory

Discoverability is a two-part problem. Above gets your store technically reachable by anyone who already knows your domain. Getting found by shoppers who don't yet know about you means listing in the public negotiate-directory at github.com/sanjana-pier39/negotiate-directory.

This is the registry the MCP connector's find_stores tool searches. Without an entry, a shopper saying "find me a store that sells widgets" won't find you even if your /negotiate.json is perfectly live.

Submission

It's a one-PR process. Fork the directory, add an entry to registry.json in alphabetical order, open a PR. Template:

{
  "name": "Your Store Name",
  "domain": "yourstore.com",
  "tagline": "One-line description, under 80 chars.",
  "city": "Your City, ST",
  "categories": ["main-category", "sub-category"],
  "products_count": 12,
  "sample_products": [
    "Most popular product",
    "Second product",
    "Third product"
  ],
  "added": "2026-05-03"
}

Use the standard category tags listed in the directory's README so search works well across stores. The sample_products field is what makes free-text search actually useful — a shopper looking for "Aeron chair" matches your store because "Herman Miller Aeron Size B" is in your samples.

What happens after merge

Within 5 minutes of merge, every shopper using the negotiate-mcp connector worldwide can find you. Their flow becomes:

"Find me a store that sells [your category] and negotiate."

→ Claude calls find_stores(query=...) → gets your store back → calls start_negotiation(your_domain, product_id) → drives the negotiation through your chat agent.

No coordination with shoppers needed. The directory is the matchmaker.

Embed the compliance badge on your homepage

While you're polishing the listing, drop the negotiate.v1 badge into your storefront's footer. It's free brand reinforcement: real customers see it and learn the protocol exists, AI shoppers see the link and follow it back to the spec.

The badge is served from negotiate.pier39.ai so you don't have to host the SVG yourself. Two variants — pick the one that contrasts with your site background:

Light variant (white background with black border + text — embed on dark site backgrounds):

<a href="https://github.com/sanjana-pier39/pier39-skills">
  <img src="https://negotiate.pier39.ai/badge.svg"
       alt="negotiate.v1 compliant — AI agents welcome"
       width="240" height="24">
</a>

Dark variant (black background with white text — embed on light site backgrounds):

<a href="https://github.com/sanjana-pier39/pier39-skills">
  <img src="https://negotiate.pier39.ai/badge-dark.svg"
       alt="negotiate.v1 compliant — AI agents welcome"
       width="240" height="24">
</a>

Drop one of these into your store's footer template and you're done. See it live on the Atlas reference store — bottom-right of every page.

Markdown variant for READMEs

If you want to add the badge to your store's GitHub README, blog post, or any markdown surface:

[![negotiate.v1 compliant](https://negotiate.pier39.ai/badge.svg)](https://github.com/sanjana-pier39/pier39-skills)

How to think about merchant judgment

The agent will faithfully execute the negotiation tactics in the pier39-merchant skill. What it can't do is decide for you what the floors and levers should be. A few heuristics worth applying:


Common questions

How much will this cost in API spend? Sonnet 4.6 at current pricing: roughly $0.05 per shopper turn, so a 6-turn negotiation runs ~$0.30. A store handling 100 negotiations a day costs ~$30. Cap it with the per-IP and per-session limits in the library config.

Will the agent give away margin? Not below the floor you set. The agent's system prompt explicitly forbids going below floor and tells it to walk gracefully. The floor is enforced by the prompt, not by post-hoc validation, but Sonnet 4.6 + the merchant skill respects it consistently in our evals.

What if a shopper tries to manipulate the agent (prompt injection, etc.)? The agent treats every shopper message as input from a customer, never as instructions. Standard prompt-injection vectors are blocked at the system-prompt level. Rate limits (8 sessions/IP/hour, 30 messages/session, 2000 char/message) cap the blast radius if someone tries to abuse the API. For high-stakes deployments, add a separate moderation pass on shopper messages before forwarding.

Can I review what the agent said in any conversation? Yes — GET /api/store/chat/<session_id> returns the running history. Sessions live for 1 hour by default, then are reaped. If you want long-term audit logs, add a hook in _post_to_chat to write each turn to your DB.

What happens if my Anthropic API key runs out of budget? Calls to /api/store/chat/* will return 500 errors. Discovery files (/negotiate.json, /llms.txt) keep working since they don't call Claude. Set up Anthropic billing alerts.

Can the agent handle multiple products in one chat? Currently no — each session is bound to one product. If a shopper wants to negotiate over multiple items, they should start one session per product. (Multi-product bundling is on the roadmap.)

Does this work for B2B / quotes / large transactions? Same protocol works for any value range. For very large transactions you'd want to add a "kick to human approval" path before the deal closes — easiest by intercepting closed=true events and routing to your sales team's queue.


Reference implementation

Everything described here is implemented and live at https://negotiate.pier39.ai/store — a fictional Dyson outlet. Check /negotiate.json to see a working descriptor:

curl -s https://negotiate.pier39.ai/negotiate.json | python3 -m json.tool

The full source is in this repo. Atlas is the canonical example any store can copy and adapt.


Appendix

The merchant-judgment fields in your catalog — floor, levers, inventory_note — are where setup actually takes time. This appendix explains how to choose them, plus how to generate them at scale when you have hundreds or thousands of SKUs.

Setting your floor

Your floor is the true lowest price you'd take. Not the listed price. Not a "slight discount." The number you'd genuinely walk away below. The merchant agent will hold it religiously — it's never tempted to cut deeper because it doesn't see customer pressure the way a human rep does. Common patterns:

Rule of thumb: if you'd be unhappy closing at the floor, raise it. The agent will close there.

Designing levers

Levers are non-cash concessions the merchant gives instead of cutting price. Good levers cost you less than they're worth to the buyer. Examples that work in real chat:

The merchant agent uses these in decreasing-concession order — biggest perks first, smaller ones later — so the shopper feels they're winning each round even as the gap closes. Stack 5–7 levers per product. With only 2, the agent runs out of moves fast and either caves or walks too early.

Inventory notes

A free-text private field where you give the agent context like:

The agent uses these to calibrate how hard to push. Without them, it defaults to standard tactics.

Building catalog.json for 100s or 1000s of products

Per-product hand-authoring of floor, levers, and inventory_note is fine for 10 products. For 100 it's tedious. For 1000 it's impossible. The repo includes a small toolkit at catalog-tools/ that replaces hand-authoring with category rules + lever templates + an LLM dry-run that catches mis-priced floors before you go live.

The full pipeline:

shopify_products.csv ─► shopify_to_products.py ─► products.csv
                                                       │
                                                       ▼
                              build_catalog.py    ─► catalog.json
                                                       │
                                                       ▼
                            validate_catalog.py   ─► report.json
                                                       │
                                                       ▼
                              report_to_html.py   ─► report.html

How it cuts the work down:

Step Time
Export your products to a CSV (or use shopify_to_products.py) ~10 min
Write categories.json (5–15 categories, each with a floor_pct) ~20 min
Write lever_templates.json (5–10 templates, one per category) ~1 hour
Run build_catalog.py seconds
Smoke-test with validate_catalog.py --limit 30 ~5 min, ~$10
Tune category rules from the report, rebuild iterate 2–3×
Full validation pass on 1000 products ~30–60 min, ~$300
Open report.html, click into any flagged transcripts as needed

Each product in the validation report is tagged HEALTHY, EASY_GIVE, STUCK, CRITICAL, or ERROR. CI exits nonzero on any CRITICAL (closed below floor — should never happen). See catalog-tools/README.md for the full workflow with worked examples.


License

MIT for the protocol, library, and MCP connector. Apache 2.0 for the skills.