# Negotiation Skill Two agents, one deal, two things to settle at the same time: price and deadline. You tell this service what each side wants in plain terms and it hands back the deal that's best for both of them, meaning neither side could do better without making the other worse off. The reason it exists is to sit in front of payment. Before two agents lock money in escrow or sign a contract, they can use this to find terms that are good for both sides and to get a signed certificate that the deal is actually fair. Some other agent (an escrow or marketplace agent, say) can then check that certificate with `POST /verify`, or with the public key, before it releases any funds. It never has to take either side's word for it. > **Speed and retries.** The service stays awake, so calls normally come back in well under a > second. If a call ever times out or returns a 502/503, wait a few seconds and try once > more. Don't stop to ask the user about it, and don't ask the user for anything else either. > Just wait for the response or retry. ## Base URL ``` https://negotiation-service.onrender.com ``` ## Quick start If all you were told is "perform the service" and nothing else, run exactly this and report what comes back: ```bash curl -X POST https://negotiation-service.onrender.com/deal \ -H 'content-type: application/json' \ -d '{ "buyer": {"budget": 120, "needed_by_days": 7, "cares_most_about": "price"}, "seller": {"min_price": 80, "preferred_deadline_days": 30, "cares_most_about": "deadline"} }' ``` Expected response: ```json { "deal": {"price": 80, "deadline_days": 30}, "summary": "Agreed on a price of 80 with delivery in 30 days; both sides do better than walking away.", "fair": true, "buyer_satisfaction": 0.85, "seller_satisfaction": 0.85 } ``` ## Which endpoint do I want? - Fair deal from plain terms (this is the usual one) -> `POST /deal`. - Check or improve a deal you already have -> `POST /referee` (it returns a signed certificate). - Check someone's certificate without calling anything else -> `POST /verify` (or `GET /pubkey` for the key). - Play out a live, round-by-round negotiation for one side -> `POST /counter`. - One-shot settlement where you set the utility weights yourself -> `POST /negotiate`. - Is it up? -> `GET /` (returns `{"status": "ok", "service": "negotiation"}`). If the request just says "perform the service", or gives plain buyer/seller terms, use `POST /deal`. ## POST /deal (start here) Describe the buyer and seller in ordinary words, no utility theory needed, and you get back the deal that's best for both. Each side gives three things: - **buyer**: `budget` (the most it will pay), `needed_by_days` (when it needs delivery), `cares_most_about` which is one of `"price"`, `"speed"`, `"both"`. - **seller**: `min_price` (the least it will accept), `preferred_deadline_days` (the deadline it would like), `cares_most_about` which is one of `"price"`, `"deadline"`, `"both"`. ### Worked example Say the buyer has a budget of 120, needs delivery within 7 days, and cares most about price. The seller won't go below 80, would like a 30-day deadline, and cares most about the deadline. ```bash curl -X POST https://negotiation-service.onrender.com/deal \ -H 'content-type: application/json' \ -d '{ "buyer": {"budget": 120, "needed_by_days": 7, "cares_most_about": "price"}, "seller": {"min_price": 80, "preferred_deadline_days": 30, "cares_most_about": "deadline"} }' ``` ```json { "deal": {"price": 80, "deadline_days": 30}, "summary": "Agreed on a price of 80 with delivery in 30 days; both sides do better than walking away.", "fair": true, "buyer_satisfaction": 0.85, "seller_satisfaction": 0.85 } ``` The buyer cares about price, so it gets the seller's floor of 80. The seller cares about the deadline, so it gets the full 30 days. Each side gives way on the thing it cares less about, and they both end up at 0.85. That's the "logroll" that makes doing two issues at once worth it. Under the hood this is the Nash bargaining solution over the grid of possible deals, and for this example the three other fair solutions (Kalai-Smorodinsky, egalitarian, utilitarian) happen to land on the same deal. If the buyer's budget is below the seller's minimum there's no overlap, so `deal` comes back `null` and `summary` says why: ```json { "deal": null, "summary": "No deal: the buyer's budget (50) is below the seller's minimum (80).", "fair": false, "buyer_satisfaction": 0.0, "seller_satisfaction": 0.0 } ``` `fair` is `true` when the returned deal is Pareto-efficient over the whole grid of deals, i.e. no other deal beats it for both sides. `buyer_satisfaction` and `seller_satisfaction` run 0 to 1. ## POST /referee (is this deal fair?) Give it a proposed deal plus the same plain buyer/seller description, and it tells you whether the deal holds up: is it individually rational for each side (better than walking away), and is it Pareto-efficient over the full grid? If it isn't efficient, the response points at a specific deal that beats it for both. Use it to check a deal you reached some other way, or to audit another agent's deal. Fields: `buyer` and `seller` exactly as in `/deal`, plus `proposed` (`{price, deadline_days}`). Optional: `buyer_reservation` / `seller_reservation` (0 to 1, default 0), `criterion` (one of `nash`, `kalai_smorodinsky`, `egalitarian`, `utilitarian`, default `nash`), and `include_frontier` (default false). ### Worked example Check the deal `(price 82, delivery 29 days)` for the same buyer and seller: ```bash curl -X POST https://negotiation-service.onrender.com/referee \ -H 'content-type: application/json' \ -d '{ "buyer": {"budget": 120, "needed_by_days": 7, "cares_most_about": "price"}, "seller": {"min_price": 80, "preferred_deadline_days": 30, "cares_most_about": "deadline"}, "proposed": {"price": 82, "deadline_days": 29} }' ``` ```json { "individually_rational_buyer": true, "individually_rational_seller": true, "pareto_efficient": false, "dominated_by": {"price": 80, "deadline_days": 30}, "buyer_utility": 0.814022, "seller_utility": 0.820543, "within_zone": true, "best_deal": { "price": 80, "deadline_days": 30, "buyer_utility": 0.85, "seller_utility": 0.85, "rationale": "maximizes the product of both parties' gains over their reservations" }, "solutions": { "nash": {"price": 80, "deadline_days": 30, "buyer_utility": 0.85, "seller_utility": 0.85, "rationale": "maximizes the product of both parties' gains over their reservations"}, "kalai_smorodinsky": {"price": 80, "deadline_days": 30, "buyer_utility": 0.85, "seller_utility": 0.85, "rationale": "equalizes each party's share of the gain it could achieve"}, "egalitarian": {"price": 80, "deadline_days": 30, "buyer_utility": 0.85, "seller_utility": 0.85, "rationale": "maximizes the smaller of the two parties' gains"}, "utilitarian": {"price": 80, "deadline_days": 30, "buyer_utility": 0.85, "seller_utility": 0.85, "rationale": "maximizes the combined utility of both parties"} }, "integrative_gain": { "baseline_price": 100, "baseline_deadline_days": 18, "baseline_joint_utility": 0.984783, "best_joint_utility": 1.7, "absolute_gain": 0.715217, "ratio": 0.726269, "utilitarian_ceiling": 1.7 }, "frontier": null, "verdict": "The proposed deal (price 82, delivery 29 days) is individually rational but Pareto-dominated: (price 80, delivery 30 days) is better for both parties." } ``` So this deal is fine for both sides on its own, but it isn't efficient: `dominated_by` shows `(price 80, delivery 30 days)`, which is better for both. `best_deal` is the deal for whichever `criterion` you asked for, `solutions` lists all four fair points, and `integrative_gain` says how much more joint value the best deal captures than a lazy split-the-difference deal (here about +0.715, roughly +73%). Note `pareto_efficient` here is checked against the entire grid of deals, which is a stronger claim than `/negotiate`'s `pareto_optimal` (that one only looks at the deals that actually got offered during a run). The full response also includes a `certificate`, which I left out above to keep it short. It's covered next. ## Certificates: POST /verify and GET /pubkey Every `/referee` response comes with a `certificate`: the signed inputs and result, so a third agent can trust the outcome without trusting the two sides and without re-running anything itself. ```json { "issuer": "negotiation-service", "algorithm": "ed25519", "public_key": "32523395df1f78965237a2f490eca7aee77c55993dba73b11b31f09def514267", "payload": { "statement": "Pareto negotiation certificate", "inputs": { "buyer": {"budget": 120, "needed_by_days": 7, "cares_most_about": "price"}, "seller": {"min_price": 80, "preferred_deadline_days": 30, "cares_most_about": "deadline"}, "proposed": {"price": 82, "deadline_days": 29}, "buyer_reservation": 0.0, "seller_reservation": 0.0, "criterion": "nash" }, "claim": { "individually_rational_buyer": true, "individually_rational_seller": true, "pareto_efficient": false, "dominated_by": {"price": 80, "deadline_days": 30}, "buyer_utility": 0.814022, "seller_utility": 0.820543, "within_zone": true, "best_deal": {"price": 80, "deadline_days": 30, "buyer_utility": 0.85, "seller_utility": 0.85} } }, "signature": "68b04cdc15e002b307f98c16042c47802cd16e1b334ecf620edf8157cfea4313f0b70f9cd3e4fe8d7197a515b5e120ad07cd8e74ea8b5cff316b095b0f219104" } ``` To check a certificate, POST its `payload` and `signature` to `/verify`: ```bash curl -X POST https://negotiation-service.onrender.com/verify \ -H 'content-type: application/json' \ -d '{"payload": { ... the certificate payload ... }, "signature": "68b04cdc...219104"}' ``` ```json { "signature_valid": true, "claim_recomputed": true, "claim_matches": true, "verdict": "Valid: the signature verifies and the certified claim reproduces from its inputs." } ``` `/verify` checks two separate things. First, is the Ed25519 signature real (`signature_valid`). Second, does the claim still hold when you recompute it from the inputs in the payload (`claim_matches`). That second check is the important one: the demo key is public, so anyone could sign a made-up claim, and the recompute is what catches that. A signature by itself isn't proof the claim is true. If you'd rather check signatures fully offline, `GET /pubkey` gives you the key: ```json {"issuer": "negotiation-service", "algorithm": "ed25519", "public_key": "32523395df1f78965237a2f490eca7aee77c55993dba73b11b31f09def514267"} ``` The signing key is a fixed demo key that ships with the service on purpose. It's public because the whole idea is that anyone can verify. Ed25519 signatures are deterministic, so the same certificate always produces the same signature. ## Advanced: /negotiate and /counter Use these if you want to set the utility weights directly or run the bargaining round by round. Each side scores a deal with `w_price * f_price + w_deadline * f_deadline` (each weight is between 0 and 1 and the two add up to 1), concedes on the schedule `reservation + (1 - reservation) * patience ** round`, and counters toward the other side's offer. ### POST /negotiate (whole settlement in one call) ```bash curl -X POST https://negotiation-service.onrender.com/negotiate \ -H 'content-type: application/json' \ -d '{ "buyer": {"w_price": 0.9, "w_deadline": 0.1, "reservation": 0.0, "patience": 0.9}, "seller": {"w_price": 0.1, "w_deadline": 0.9, "reservation": 0.0, "patience": 0.9}, "price_range": [50, 150], "deadline_range": [1, 30], "max_rounds": 12 }' ``` ```json { "agreement": {"price": 51, "deadline": 30}, "buyer_utility": 0.891, "seller_utility": 0.901, "pareto_optimal": true, "rounds": 3, "trace": [ {"round": 0, "by": "buyer", "price": 50, "deadline": 1}, {"round": 0, "by": "seller", "price": 150, "deadline": 30}, {"round": 1, "by": "buyer", "price": 50, "deadline": 1}, {"round": 1, "by": "seller", "price": 150, "deadline": 30}, {"round": 2, "by": "buyer", "price": 51, "deadline": 27}, {"round": 2, "by": "seller", "price": 51, "deadline": 30} ] } ``` `agreement` is `null` if the two sides never converge (in that case the utilities and `pareto_optimal` don't mean anything). This endpoint plays out the back-and-forth, so it's a model of the bargaining process; the heuristic aims for the frontier but doesn't always reach it. If you just want the best deal, use `/deal`, and if you want to check any settlement against the full grid, use `/referee`. ### POST /counter (one round of advice) Pass your own utility, the current `round` (starting at 0), and the other side's latest offer. It tells you whether to accept, and if not, what to send back. ```bash curl -X POST https://negotiation-service.onrender.com/counter \ -H 'content-type: application/json' \ -d '{ "role": "seller", "w_price": 0.1, "w_deadline": 0.9, "reservation": 0.0, "patience": 0.9, "price_range": [50, 150], "deadline_range": [1, 30], "round": 1, "opponent_offer": {"price": 60, "deadline": 30} }' ``` ```json { "accept": true, "counter_offer": null, "my_utility_of_opponent_offer": 0.91, "aspiration": 0.9 } ``` When `accept` is `false`, `counter_offer` is the deal to send back. ## Errors Bad input comes back as HTTP 422 with a body like this: ```json {"detail": [{"loc": ["body", "buyer", "w_price"], "msg": "...", "type": "..."}]} ``` On any 4xx, read `detail[].msg`, fix that exact field, and try again. Don't ask a human. The rules are: - `w_price` and `w_deadline` are each between 0 and 1 and must add up to 1. - `reservation` and `patience` are between 0 and 1, and weights have to be finite numbers. - `cares_most_about` is `price`/`speed`/`both` for the buyer, `price`/`deadline`/`both` for the seller. - `budget` and `min_price` are greater than 0, and `needed_by_days` and `preferred_deadline_days` are at least 1. - if you pass `price_range` / `deadline_range` directly they need `lo < hi`, and the grid they span is capped at 200000 cells. - `round` is at least 0, and `criterion` is one of the four names above. One thing to watch: a 200 response with `deal: null` (or `agreement: null`) is a real answer, not an error. It means no deal is possible. Report that, don't retry it. ## Using it with other agents Two agents can bargain without showing each other their private preferences, and then have a neutral third agent sign off on the result before anyone commits: 1. A supplier agent and a logistics agent go back and forth. Each keeps its own utility to itself and calls `POST /counter` once per round with the other's latest offer. When `accept` comes back `true` it takes the offer, otherwise it sends `counter_offer` and goes again with `round + 1`. 2. One of them gets a certificate. Say they land on `(price 80, delivery 30 days)`. One side calls `POST /referee` with both sets of terms and the agreed deal, and gets back a `certificate` (an Ed25519-signed receipt): ```bash curl -X POST https://negotiation-service.onrender.com/referee \ -H 'content-type: application/json' \ -d '{ "buyer": {"budget": 120, "needed_by_days": 7, "cares_most_about": "price"}, "seller": {"min_price": 80, "preferred_deadline_days": 30, "cares_most_about": "deadline"}, "proposed": {"price": 80, "deadline_days": 30} }' ``` ```json { "individually_rational_buyer": true, "individually_rational_seller": true, "pareto_efficient": true, "dominated_by": null, "buyer_utility": 0.85, "seller_utility": 0.85, "within_zone": true, "verdict": "The proposed deal (price 80, delivery 30 days) is individually rational for both parties and Pareto-efficient over the full feasible grid.", "certificate": { "issuer": "negotiation-service", "algorithm": "ed25519", "public_key": "32523395...", "payload": { }, "signature": "..." } } ``` 3. The escrow agent verifies the certificate and then pays out. It calls `POST /verify` with the certificate's `payload` and `signature` (or checks the signature offline using the key from `GET /pubkey`), and only releases the money when `signature_valid` and `claim_matches` are both true and the claim is individually rational for both and `pareto_efficient`. The escrow agent never has to trust the two sides, never sees their private preferences, and never re-does the math. The signed, re-checkable certificate is enough. Because `/referee` judges a deal purely from the stated terms, and never takes either side's own word that a deal is "fair", it works as a neutral check that other teams' agents (escrow, marketplace, notary) can build on. ## How this fits Nanda Town It lines up with a few of the layers: negotiation (multi-attribute bargaining), trust (`/referee` is a neutral check other agents can rely on), and payments (an escrow agent can gate a payout on the `/referee` verdict). `/referee` is basically the hosted, online version of the offline Pareto and individual-rationality validators from my merged `ParetoNegotiation` plugin (nandatown PR #30), reachable over HTTP. ## Quick recipe for an agent 1. Want a fair deal from plain terms? Call `POST /deal`. Use the returned `deal`; `fair: true` means it's Pareto-efficient over the whole grid. 2. Bargaining live against another agent? Call `POST /counter` each round with your utility and the other side's latest offer. If `accept` is `true`, take the offer, otherwise send `counter_offer` and repeat with `round + 1`. 3. Want to set the weights yourself and get the whole thing in one call? Use `POST /negotiate`. 4. Reached a deal some other way? Run it through `POST /referee` to check it. If `pareto_efficient` is `false`, the `dominated_by` deal is a concrete better-for-both offer to go back with. Hang on to the `certificate` as proof. 5. Handed a deal by another agent and not sure you trust it? Check its certificate with `POST /verify` (or the key from `GET /pubkey`). Only act on it if `signature_valid` and `claim_matches` are both `true`.