Deploy Nash on your storefront in five minutes
This page walks through the hosted Nash onboarding flow at negotiate.pier39.ai/store_onboarding/start. It's for merchants who don't want to write code, install Python libraries, or read setup guides. You fill out a short form, drop in your product CSV, and walk away with a zipped Nash deployment bundle ready to run.
The whole flow takes about five minutes. Three of them are filling in form fields.
Before you start
You need three things ready:
- Your store name and city. Two text fields. That's it.
- A product CSV. Either a Shopify product export (Admin → Products → Export → CSV), or any spreadsheet you save as CSV with the columns
id, name, list_price, category, description. - An Anthropic API key. Get one at console.anthropic.com. Costs about $0.05–$0.15 per negotiation. The form doesn't store your key — it gets written into a file inside the zip you download, and never travels anywhere else.
The five steps
1. Open the form
Visit negotiate.pier39.ai/store_onboarding/start. You'll see a single page with three sections: Store, Products, API key.
2. Tell us about your store
Four short fields:
| Field | Example |
|---|---|
| Store name | Atlas Premium Appliance |
| City | Atlanta, GA |
| Sales rep name | Casey (this is the persona the AI takes on when chatting with shoppers) |
| Tagline | Authorized partner. Outlet pricing. |
These show up in the chat agent's introduction and on your store's discovery file.
3. Get your products in
Two ways:
Option A — Connect Shopify (one click). If you run a Shopify store, click Connect Shopify at the top of the Products section. Enter your store domain (yourstore.myshopify.com), approve the read-products permission on Shopify, and your catalog gets pulled in automatically. No CSV export needed. The form picks back up where you left off, with all your products loaded.
Option B — Upload a CSV. Drag your file onto the dashed box (or click to browse). Two CSV shapes are accepted:
- Shopify export — if you exported from Shopify Admin → Products → Export → CSV. The form parses the standard Shopify columns (
Handle, Title, Body (HTML), Vendor, Variant Price, Variant Inventory Qty, Tags). -
Generic CSV — any spreadsheet with at least
id, name, list_price, category, description. Optional columns (brand, accessory_name, accessory_price, …) feed into per-product lever templates — see the appendix. -
Default category — every product gets tagged into a "negotiation profile" that determines its floor (lowest acceptable price) and the lever stack the agent can offer. There are six profiles available; the dropdown picks the fallback used when no per-product signal applies. See the Appendix — Categories and levers for the full table, the resolution order, and how lever strings are generated for each product.
If your CSV mixes lifecycles (some outlet, some clearance, some open-box) you don't have to upload separate files. Generic CSVs use the per-row category column; Shopify exports auto-map any tag whose name matches a category, and you can override anything in the tag-mapping UI that appears after upload. Full details in the appendix.
4. Add your API key
Paste your ANTHROPIC_API_KEY (starts with sk-ant-...). If you haven't got one yet, leave this blank — the bundle will include a placeholder you can fill in later.
5. Build and download
Click Build my store bundle. The form does its work in about a second and your browser auto-downloads yourstore-negotiable.zip.
If anything goes wrong, you'll see a red error message right under the button (typically: "CSV had no usable rows" — usually means the file format dropdown doesn't match the file you uploaded).
What's inside the zip
yourstore-negotiable/
├── catalog.json Your full product catalog with floors and concession levers per item
├── serve.py Six-line script to start the merchant backend
├── .env Your Anthropic API key (don't commit this to git)
├── Dockerfile For deploying to any cloud
├── fly.toml Pre-configured for Fly.io with your store-slug app name
└── README.md Quickstart for running locally and deploying
Open catalog.json in a text editor before going live and check two things per product:
floor— is this really the lowest price you'd take? The agent will close at this number. If you'd be unhappy at that price, raise it.levers— are the bundled accessories and warranties accurate for your store? The defaults assume you sell physical goods with manufacturer warranties.
Run it locally
After unzipping, in your terminal:
cd yourstore-negotiable
pip install pier39-merchant-server
python serve.py
That starts the merchant backend on http://localhost:8000. In another terminal, sanity-check:
curl http://localhost:8000/negotiate.json
curl http://localhost:8000/api/store/catalog
If both return JSON, you're live. Try a chat session:
curl "http://localhost:8000/api/store/chat/start?product_id=<your-product-id>"
Deploy to the cloud
Fly.io is the fastest path. Three commands:
fly launch --no-deploy --copy-config --yes
fly secrets set ANTHROPIC_API_KEY=$(grep ANTHROPIC_API_KEY .env | cut -d= -f2)
fly deploy
That gets you a working URL like yourstore-negotiate.fly.dev. To use your own domain:
- At your DNS provider, add a CNAME from
negotiate.yourstore.com(or whatever subdomain you want) to your Fly app. - Run
fly certs add negotiate.yourstore.com— Fly handles TLS automatically.
Render, Railway, or your existing app server work too — anywhere that runs Python and stays alive between requests.
Get found by AI shoppers
Once your store is live at a real domain, list yourself in the negotiate-directory at github.com/sanjana-pier39/negotiate-directory. It's a one-PR process — fork the repo, add an entry to registry.json, open the PR.
Within five minutes of the PR merging, every AI shopper using the protocol can find you. Their flow becomes:
"Find me a store that sells [your category] and negotiate."
→ Their AI 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.
What you can change later
Everything in the bundle is plain text. You can edit any of it after the fact:
- Add or remove products — edit
catalog.jsondirectly, then redeploy. - Change floors and levers — same file. The agent rereads the catalog on every chat session, so changes apply immediately on next deploy.
- Tune the Nash seller agent's behavior — set
PIER39_MERCHANT_SKILL_PATHto a path with your own version of the Nash seller skill (defaults to the one bundled with the library). - Switch to a cheaper or smarter model — pass
model="claude-haiku-4-5"(cheaper) ormodel="claude-opus-4-6"(sharper) intoserve()inserve.py.
Common questions
Do I have to use Fly.io?
No. The bundle's Dockerfile works on any container host (Render, Railway, Google Cloud Run, AWS Fargate, your own VM). The fly.toml is just a convenience.
Can I use this without an API key?
You can build and download the bundle without one, but the chat agent won't work until you set ANTHROPIC_API_KEY in .env or as a Fly secret.
Will the form save my data? No. The form is stateless. Your CSV, your store info, and your API key get processed in memory to build the zip, then thrown away.
What if my CSV has weird columns?
Pick "Generic CSV" and make sure your file has at least id, name, list_price, category, description. Other columns are ignored. If your column names don't match, rename them in your spreadsheet before uploading.
What if my products don't fit any of the default categories?
Pick the closest one as your default and let it apply to everything. After you download the bundle, edit catalog.json and adjust the floor and levers for any product that needs a tighter or looser policy.
Can I assign different categories to different products in one upload?
Yes — three ways. Generic CSV: include a category column, each row gets its own value. Shopify CSV: use Shopify Tags whose names match category names (e.g. outlet, open-box, clearance) and they auto-route. For arbitrary Shopify tags (refurb, final-sale, etc.), the form shows a tag-mapping UI after you upload the CSV — point each tag at a category from a dropdown. See the Mixing categories section above for the full priority order.
How much will this cost me to run? Roughly $0.30 per six-turn negotiation in Anthropic API fees. A store handling 100 negotiations a day costs about $30/day. The protocol library and the directory are free; hosting costs depend on where you deploy (Fly.io's free tier is enough for low traffic).
Can I get help?
Email sanjana@pier39.ai. Or open an issue at github.com/sanjana-pier39/pier39-skills.
Appendix — Categories and levers
This is the technical detail behind Step 3. Skip it for the basic flow; come back when you want to understand exactly what the form generated and how to tune it.
The six categories
Every product in your catalog is tagged into one of these "negotiation profiles." The profile sets two things: the floor (mathematically enforced lowest price) and which lever template the Nash seller agent uses for non-cash concessions.
| Category | Floor (% of list) | Lever template | When to use |
|---|---|---|---|
premium_new |
95% | small_appliance_premium |
High-demand, low-discount items |
standard_new |
88% | small_appliance_standard |
Regular new inventory |
outlet |
85% | small_appliance_standard |
Standard outlet/overstock items |
open_box |
78% | open_box |
Open-box / display units |
aged_30plus |
75% | aged_inventory |
Items aged 30+ days |
clearance |
65% | clearance |
End-of-life, get-it-out-the-door |
Floor enforcement is in the agent's system prompt: it explicitly forbids closing below the floor and instructs the agent to walk away gracefully. The agent doesn't see customer pressure the way a human rep does, so the floor holds consistently.
How categories are assigned to products
The form uses three signals, in this priority order:
- Generic CSV — per-row
categorycolumn. If you uploaded a generic CSV, each row'scategorycell is read directly. A single file can mix all six categories across products. - Shopify CSV — explicit tag mapping. After uploading a Shopify export, the form lists every unique tag and lets you map each one to a category from a dropdown. Whatever you set here wins.
- Shopify CSV — auto-map by tag name. Tags whose names match a category (case-insensitive, hyphens/spaces normalized to underscores) auto-route. So
outlet,clearance,open-box,aged-30plusroute automatically without any UI action. - Default category dropdown — fallback. Anything not covered by the above falls through to whatever the dropdown is set to.
So a Shopify export with five products tagged outlet, open-box, aged-30plus, clearance, and fancy-tag produces five products at five different floors with zero config — the first four auto-map; the fifth falls through to the dropdown default.
How levers are generated
Each category points to a lever template — a list of 4–7 strings, ordered cheapest-give to biggest-give. The Nash seller agent uses these top-down during a negotiation. The small_appliance_standard template (used by outlet and standard_new) looks like this:
1. "Free expedited 2-day shipping (~${ship_cost} retail value)"
2. "Bundle a free {accessory_name} (${accessory_price} retail) — currently overstocked"
3. "Extend {brand} warranty from {base_warranty_years} to {extended_warranty_years} years"
4. "Free year-supply of replacement supplies (${filter_price} retail)"
5. "${credit} credit toward any next order, valid 12 months"
6. "Free 12-month enrollment in our loyalty program"
The {variables} get filled per-product. For each product:
| Variable | Source | Default if missing |
|---|---|---|
{brand} |
Shopify Vendor column, or brand in generic CSV |
the manufacturer |
{ship_cost} |
ship_cost column (generic CSV) |
35 |
{accessory_name} |
accessory_name column (generic CSV) |
bonus accessory |
{accessory_price} |
accessory_price column (generic CSV) |
49 |
{filter_price} |
filter_price column (generic CSV) |
55 |
{credit} |
credit column (generic CSV) |
30 |
{base_warranty_years} |
base_warranty_years column |
2 |
{extended_warranty_years} |
extended_warranty_years column |
4 |
{city} |
city column |
your city |
A Dyson V15 vacuum priced at $649 with the Shopify tag outlet ends up with these levers:
"levers": [
"Free expedited 2-day shipping (~$35 retail value)",
"Bundle a free bonus accessory ($49 retail) — currently overstocked",
"Extend Dyson warranty from 2 to 4 years",
"Free year-supply of replacement supplies ($55 retail)",
"$30 credit toward any next order, valid 12 months",
"Free 12-month enrollment in our loyalty program"
]
{brand} got Dyson from the Shopify Vendor column. The rest are package defaults — Shopify exports don't include accessory names, replacement-part prices, or credit policies, so those fields fall through.
Making levers store-specific
Two paths:
Edit catalog.json after download. Open the file in a text editor. Each product's levers is a plain JSON array of strings. Replace "bonus accessory" with whatever you'd actually bundle for that SKU, fix $49 retail to the real value, drop levers that don't apply, add ones that do. The agent rereads the catalog on every chat session, so changes take effect on next deploy. About five minutes per product if you're being thorough; less when batching.
Use a Generic CSV with extra columns. Switching the form's "CSV format" to "Generic CSV" lets you supply the variables directly per row. Columns the renderer recognizes:
id, name, list_price, category, description,
brand, accessory_name, accessory_price, filter_price,
ship_cost, credit, city,
extended_warranty_years, base_warranty_years,
in_stock, days_on_lot
Fill these once in your spreadsheet before exporting and every lever gets correctly substituted. Useful for stores with consistent accessory bundles (e.g., a bike shop where every bike comes with a free helmet — set accessory_name=helmet, accessory_price=85 in every row).