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 — and any human shopper can chat with your AI sales rep through a normal web chat widget. Same backend, same merchant agent, both audiences.
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.
What you get
Once setup is complete:
- A live
/negotiate.jsondescriptor at your domain advertising your store as negotiable. - A merchant agent (Claude with the
pier39-merchantskill) handling every incoming negotiation, applying real tactics: anchor-holding, cheapest-first concessions, decreasing-concession patterns, walk-away discipline. - Discoverability for AI shoppers everywhere:
/llms.txt, meta tags, agent-guide pages. - A working chat API any human or AI shopper can drive.
- Optional: an embeddable chat widget for your existing storefront.
You stay in control: the agent never sells below the floor you set, never makes promises beyond the levers you authorize, and stops when message/session limits are hit.
Before you start
You'll need:
- An Anthropic API key. console.anthropic.com. The merchant agent makes one Claude API call per turn (~$0.05–$0.15 with claude-sonnet-4-6).
- Python 3.10+ on the machine that will host the chat backend.
- A way to run a long-lived Python service. Anywhere that holds open HTTP connections works: Fly.io, Render, Railway, your existing app server, or a VM. Serverless platforms with short timeouts (Vercel functions, AWS Lambda) are not ideal because chat sessions live in memory.
- A domain you control. The protocol relies on serving
/negotiate.jsonat your store's domain. - A product list with real prices and your real walk-away numbers. This is the merchant judgment piece — see Step 1.
Step 1 — Build your catalog
This is the most important step and it's not just data entry. For each product, you need three things public, and three things private:
| Public (any shopper sees) | Private (only the merchant agent sees) |
|---|---|
name, subtitle, description |
floor — the lowest price you'd accept |
list_price |
levers — concessions the agent can offer |
condition, year_made |
inventory_note — context (urgency, manager latitude) |
The merchant agent uses the public fields when talking to shoppers. The private fields go only into the agent's internal system prompt and are stripped out of every public API response.
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:
- Outlet / overstock: floor at 80–88% of list (room to discount but margin protected).
- Used / consignment: floor at 75–85% of list, sometimes lower for aged inventory.
- Premium / scarce: floor at 92–96% of list (small room only).
- Service / experience-bundled: floor close to list because value is in the bundle, not the discount.
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:
- Free expedited shipping (real cost: $5–$10. Perceived value: $25–$50.)
- Bundle a $50-MSRP accessory you have overstocked (real cost: $5. Perceived value: $50.)
- Extend warranty from 12 to 24 months (real cost: marginal claims. Perceived value: clear.)
- Free setup, install, or training (real cost: an hour of staff time. Perceived value: high.)
- Future-purchase credit ($30 toward next order, valid 12 months — costs you nothing if unredeemed, locks in repeat).
- Manager-approved bonus (e.g., engraved gift box) that signals special handling.
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.
Inventory notes
A free-text private field where you give the agent context like:
- "6 in stock; this one's the cleanest box. Aged 33 days; manager approved $80 latitude."
- "Only 1 in stock — single-owner provenance. Aged 12 days. Hold the line."
- "End-of-quarter; category bonus tier kicks in at one more sale."
The agent uses these to calibrate how hard to push. Without them, it defaults to standard tactics.
Catalog format
The catalog is a single JSON file:
{
"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 with model, included accessories.",
"year_made": 2024,
"condition": "Brand new" or "Excellent (8.5/10), light scuff on...",
"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."
}
]
}
See pier39-merchant-server/examples/tiny_store/catalog.json for a complete two-product working example.
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 (see "Customizing further" below).
Step 6 (optional) — Embed a chat widget for human shoppers
The chat backend is plain HTTP/JSON, so embedding a chat widget in your existing storefront takes one HTML page + 50 lines of JavaScript. Copy store/product.html from the Atlas demo — it's a working reference that:
- Renders a "Chat with Casey" button
- Opens a slide-in chat panel on click
- Posts to your existing
/api/store/chat/*endpoints (CORS is already permissive) - Displays a typing indicator while the agent thinks
- Detects
closed: trueand disables further messages
Strip the Pier39-specific styling and drop it into your existing product pages. The JS at the bottom of product.html is what does the chat work — copy that into your existing JS bundle and wire it to your existing button.
If you don't want a widget at all (AI-shoppers only), skip this entirely. The protocol works without any UI.
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:
[](https://github.com/sanjana-pier39/pier39-skills)
Step 8 — Tell the world you're negotiable
Once you're in the directory and the badge is on your homepage, your store is automatically discoverable by:
- Claude Desktop / Cowork users with the Pier39 MCP connector installed. They say "find me a store that sells [X]" or "negotiate at yourdomain.com" and Claude does the rest.
- Claude.ai users with web browsing. They prompt Claude with your URL; Claude reads
/llms.txtand follows the GET endpoints. - ChatGPT / other AI agents that can do web fetches and call HTTP endpoints.
- Anyone building custom agents in Python, JS, or any language.
- Real human customers clicking the chat widget on your product pages (if you embedded it).
- Search engines crawling
/negotiate.jsonand/llms.txt—pier39-merchant-serverautomatically serves/robots.txt,/sitemap.xml, and aLinkheader pointing at the protocol descriptor on every response.
The badge in your footer is also a quiet conversion driver: even non-technical visitors learn the term "negotiate.v1" by seeing it repeatedly across compliant stores.
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:
- Don't set the floor where you'd be unhappy. The agent will close at the floor, frequently. If you'd be unhappy at that price, raise it.
- Set 5-7 levers per product, not 2. The decreasing-concession pattern only works if there's a ladder. With two levers, the agent runs out fast.
- Make the cheapest lever genuinely cheap. Free shipping, a $10 freebie, a code-for-next-order — these cost you almost nothing and feel valuable to the shopper. They should be the first concessions offered.
- Audit the transcripts. The chat sessions live in memory, but you can log them to disk by extending the library — see "Customizing further." Read the first dozen for each product, then tune the floor and lever order.
Customizing further
The pier39-merchant-server library is intentionally small and readable. To customize:
- Persist transcripts. Subclass
MerchantAppand override_post_to_chatto also write the turn to your DB or log file before returning. - Persist sessions across restarts. Replace the in-memory
_sessionsdict with a Redis-backed store. The session shape is just{product_id, system, history, ...}. - Use a different model. Pass
model="claude-opus-4-6"for sharper negotiation (more $ per turn) ormodel="claude-haiku-4-5"for cheaper (less skill nuance). - Add custom routes. Pull
app.make_handler_class(), subclass it, and add your own routes alongside the negotiate ones. - Use your own merchant skill. Point
skill_path=at your own SKILL.md. As long as it teaches negotiation tactics, the framework doesn't care.
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.
See also
- PROTOCOL.md — the formal negotiate.v1 spec
- pier39-merchant-server/README.md — library reference
- mcp-connector/README.md — MCP install for Claude Desktop users
- negotiate-directory — public store registry (where you submit to be findable)
- pier39-skills — the merchant + shopper Claude Agent Skills
License
MIT for the protocol, library, and MCP connector. Apache 2.0 for the skills.