---
name: install-snhp
description: Install SNHP into this codebase so buyers' price messages (WhatsApp, SMS, email, web chat, CRM leads) get replies negotiated inside the business's own rules, checked against live stock, with a signed record for every deal. Use when the user asks to install, set up, integrate or connect SNHP, or wants an AI to answer haggling or price questions without going below a floor.
compatibility: Needs HTTPS access to https://snhp.dev and an SNHP access key in the environment variable SNHP_ACCESS_KEY.
metadata:
  version: "0.1.0"
  docs: "https://snhp.dev/developers"
---

# Install SNHP

SNHP answers a buyer's price message for a business. The business states its rules once as a **policy**: list price,
the lowest price, and what it will give a discount for (cash, a trade-in, closing this week). For each message,
SNHP's engine decides the offer inside those rules, code checks it, and a small language model writes the words.
Deals come with a signed receipt anyone can verify. The server stores no conversations: you keep a `state` per
conversation and send it back with the next message.

## Rules you must keep while integrating

1. **The key stays secret.** Read it from `SNHP_ACCESS_KEY`. Never commit it, log it, put it in a URL or send it to
   the browser. If the user has no key, tell them: pilot businesses get one at https://snhp.dev/#pilot.
2. **Prices come from SNHP, never from your code.** Do not compute, round, discount or override a price. Send
   `reply_with_offer` exactly as returned when it is there (it carries the offer line the rules were checked on),
   otherwise `reply`.
3. **Rules live in the policy, not in code.** Floors, list prices and what can be traded belong in policy JSON the
   business can change. Never hard-code a floor.
4. **Stock goes with every message.** Look up current stock for the item (and any stocked extras) and pass it as
   `stock` on every call. Never let a deal through on an item you can't confirm is in stock.
5. **A human approves replies unless the user says otherwise.** Start in copilot mode: drafts appear for a person
   to send. Switch to automatic sending only when the user asks for it.
6. **Keep `state` per conversation**, keyed by the conversation or the buyer's contact. Start without one.

## Steps

### 1. Find where buyers write
Look for the inbound message path: a WhatsApp Business (Cloud API) webhook, Twilio SMS/WhatsApp, an email handler,
a website chat widget, or a CRM lead webhook. If there is more than one, or none, ask the user which channel to
connect first. Note where outbound replies are sent from.

### 2. Find the stock source
Look for the inventory: a database table, a dealer feed export (CSV/XML from the DMS, vAuto, HomeNet), a Google
Merchant feed, a Shopify/UCP catalog, or an ERP. Write a small `lookup_stock(item_id)` that returns stock records in a
shape SNHP reads (see `references/stock-records.md`): at minimum `{"id": ..., "availability": ...}` or a dealer feed's
`{"vin" | "stock_number": ..., "status": ...}`, with a timestamp if the source has one.

### 3. Set up the policy
One policy per thing being sold (for a car lot: one per vehicle; for a catalog: one per product or price list).
- Start from a template (`GET https://snhp.dev/v1/policy/templates`, e.g. `used-car`) or have SNHP compile the
  business's own words (`POST /v1/policy/compile {"description": "..."}`, needs the key).
- Fill in each item's list price and lowest price from the business's data. Ask the user how their lowest price is
  set (a fixed amount, a percentage under list, a per-item field) if the code doesn't say.
- Bind stock: add `"inventory": {"item": "<VIN or SKU>", "options": {"<lever>.<option>": "<SKU of a stocked extra>"},
  "max_age_s": 900}`.
- Check it: `POST /v1/policy/check {"policy": ...}` must return `ok: true`.
- Store policies where the business can edit them (a JSON file per item, a database column, or generated from the
  inventory at request time). Not in code constants.

### 4. Connect the message path
On each buyer message:
```
state  = load_state(conversation_id)            # None for a new conversation
stock  = lookup_stock(item_id)                  # a list of stock records
resp   = POST https://snhp.dev/v1/reply
         headers: {"X-SNHP-Key": env SNHP_ACCESS_KEY, "Content-Type": "application/json"}
         body:    {"policy": policy, "message": text, "state": state, "stock": stock,
                   "flags": {...}}              # only facts YOUR records confirm, never what the buyer claims
save_state(conversation_id, resp["state"])
```
The text to send is `resp.get("reply_with_offer") or resp["reply"]`. Then route by `resp["kind"]`:

| kind | what to do |
|---|---|
| `offer` | send (or queue for approval) the text; `resp["offer"]` (or `resp["menu"]`) has the structured terms |
| `deal` | send the text; store `resp["receipt"]` with the sale; hand off to paperwork |
| `handoff` | send the text; notify a person on the team (approval needed, or stock to confirm) |
| `decline` | send the text; the conversation is over (e.g. the item is no longer available) |

`resp["done"]` is true when the conversation has ended. Errors come back as HTTP 4xx with a `detail` message; show
it to the team, never to the buyer. If your platform supports MCP instead of HTTP, the same contract is the
`draft_negotiation_reply` tool at `https://snhp.dev/mcp/` (pass the key as `access_key`).

### 5. Store and verify receipts
Keep each deal's `receipt` with the sale. Anyone can check it: `POST /v1/receipt/verify {"receipt", "policy"}`
re-checks the signature and every rule.

### 6. Prove it works before going live
Write these as automated tests (or a script) against the real API with the user's key:
1. **A normal conversation**: drive 3-4 buyer messages; get the simulated buyer's next line from
   `POST /v1/practice/buyer {"policy", "history"}`. Every `offer.price` is at or above the policy's floor and `offer.inside_policy` is true.
2. **Out of stock**: send the item's stock record as sold (`{"id": "<item>", "availability": "SoldOut"}`). Expect
   `kind == "decline"` and no `offer`.
3. **Stale stock at the deal**: drive to a yes with a stock record older than `max_age_s`. Expect
   `kind == "handoff"` and no `receipt`.
4. **The floor holds**: a buyer asks for a price far below the floor. No offer goes below it.
5. **The key never leaks**: grep logs and client bundles for `snhp_`.

### 7. Leave instructions for the next agent
Add the block in `assets/agents-md-block.md` to the repository's `AGENTS.md` (create the file if there is none; if
there is a `CLAUDE.md` and no `AGENTS.md`, add it to `CLAUDE.md`). Fill in the paths. Only ever edit the text between
the `BEGIN:snhp-agent-rules` and `END:snhp-agent-rules` markers.

### 8. Report back
Tell the user: which channel is connected, where the adapter and stock lookup live, where policies are stored and
how to change them (the business can also edit rules at https://snhp.dev/studio), which mode it runs in (copilot or
automatic), and the test results.
