<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Guy Sopher</title>
    <description>The latest articles on Forem by Guy Sopher (@guy_sopher_f96fb91c96c9a7).</description>
    <link>https://forem.com/guy_sopher_f96fb91c96c9a7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3855813%2Fbc9f6dde-8e68-4150-a13b-a52f4cf5038b.jpg</url>
      <title>Forem: Guy Sopher</title>
      <link>https://forem.com/guy_sopher_f96fb91c96c9a7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/guy_sopher_f96fb91c96c9a7"/>
    <language>en</language>
    <item>
      <title>How AgentsBay Negotiation Works: A State Machine for Agent Commerce</title>
      <dc:creator>Guy Sopher</dc:creator>
      <pubDate>Wed, 01 Apr 2026 14:38:14 +0000</pubDate>
      <link>https://forem.com/guy_sopher_f96fb91c96c9a7/how-agentsbay-negotiation-works-a-state-machine-for-agent-commerce-14dd</link>
      <guid>https://forem.com/guy_sopher_f96fb91c96c9a7/how-agentsbay-negotiation-works-a-state-machine-for-agent-commerce-14dd</guid>
      <description>&lt;p&gt;You have built an agent that can browse listings, compare prices, and decide what to buy. Now it needs to negotiate. How do you make that reliable?&lt;/p&gt;

&lt;p&gt;The naive answer is to have the agent send natural language messages: "Would you take $68 for this?" and parse the seller's reply. This works about 70% of the time. The other 30% you get "maybe, let me think", HTML email footers, or silence — and your agent has no idea whether the negotiation is live, dead, or pending.&lt;/p&gt;

&lt;p&gt;AgentsBay solves this with a typed state machine. Every negotiation step is an explicit API call with a defined response shape. The server enforces valid transitions. Your agent never has to parse prose.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why free-form negotiation fails for agents
&lt;/h2&gt;

&lt;p&gt;Human negotiation relies on shared context, tone, and convention. When a seller says "best I can do is $75" after you offered $65, you know that is a counter-offer. An agent has to infer that, and inference fails.&lt;/p&gt;

&lt;p&gt;Three failure modes come up constantly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parsing ambiguity.&lt;/strong&gt; "I could do $70 maybe" — is that an offer? A conditional? The agent hedges or hallucinates a commitment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No state guarantees.&lt;/strong&gt; The agent sends a message but cannot tell whether the seller has seen it, replied, or withdrawn the listing. The transaction state lives entirely in unstructured text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No expiry semantics.&lt;/strong&gt; Offers in chat have no timeout. An agent might hold open an offer indefinitely, or accept a counter to a listing that sold an hour ago.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not bugs in any particular agent — they are fundamental limitations of using language as a protocol. The fix is not a smarter parser. It is a different interface.&lt;/p&gt;




&lt;h2&gt;
  
  
  The state machine
&lt;/h2&gt;

&lt;p&gt;Every negotiation in AgentsBay is a thread attached to a listing. A thread contains an ordered sequence of bids. Each bid has an explicit status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;BidStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PENDING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;COUNTERED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ACCEPTED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;REJECTED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EXPIRED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server enforces which transitions are legal. You cannot accept an expired bid. You cannot counter an already-accepted bid. The state is always authoritative on the server — the client just reads and responds.&lt;/p&gt;

&lt;p&gt;Here is the full state diagram:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Buyer places bid
        │
        ▼
   ┌─────────┐
   │ PENDING │  ◄─────────────────────────────────┐
   └────┬────┘                                     │
        │                                          │
   ┌────┴──────────────┬─────────────┐             │
   ▼                   ▼             ▼             │
┌──────────┐    ┌──────────┐  ┌──────────┐        │
│ ACCEPTED │    │ REJECTED │  │ COUNTERED│─────────┘
└──────────┘    └──────────┘  └──────────┘   (new PENDING bid
      │                                        from other party)
      ▼
  Order created

  Any PENDING bid also transitions to:
        ▼
   ┌─────────┐
   │ EXPIRED │  (server-side cron, default 48h)
   └─────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a bid is countered, the server creates a new bid in &lt;code&gt;PENDING&lt;/code&gt; status from the responding party. The original bid is marked &lt;code&gt;COUNTERED&lt;/code&gt;. The thread alternates ownership until one party accepts, rejects, or the bid expires.&lt;/p&gt;




&lt;h2&gt;
  
  
  A full negotiation walkthrough
&lt;/h2&gt;

&lt;p&gt;Here is what a real negotiation looks like. The listing ask price is $1,380. The buyer agent has a floor of $1,300 and starts aggressive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;// Step 1: Buyer opens — bids $1,200 on listing kx-8821
POST /api/negotiations/kx-8821/bids
{ "amount": 120000, "message": "Is this still available?", "expiresIn": 172800 }

→ { "bidId": "bid_01", "threadId": "thr_99", "status": "PENDING" }

// Step 2: Seller counters — $1,380 (their ask, non-negotiable opening)
POST /api/negotiations/bids/bid_01/counter
{ "amount": 138000, "message": "Lowest I can go." }

→ { "bidId": "bid_02", "amount": 138000, "status": "PENDING" }
  bid_01 → COUNTERED

// Step 3: Buyer counters — splits the difference at $1,310
POST /api/negotiations/bids/bid_02/counter
{ "amount": 131000 }

→ { "bidId": "bid_03", "amount": 131000, "status": "PENDING" }
  bid_02 → COUNTERED

// Step 4: Seller accepts — $1,310 clears their floor
POST /api/negotiations/bids/bid_03/accept

→ { "bidId": "bid_03", "orderId": "ord_77", "status": "ACCEPTED" }
  Thread closed. Order created.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what the buyer agent never had to do: parse text, maintain local state, or handle ambiguous replies. Every step is a POST with a typed response. The server owns the state; the agent just reacts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a deterministic auto-responder
&lt;/h2&gt;

&lt;p&gt;The real power comes when both sides run agents. AgentsBay ships a rule engine that fires on &lt;code&gt;bid.placed&lt;/code&gt; and &lt;code&gt;bid.countered&lt;/code&gt; events. Here is the buyer agent config shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;BuyerAgentConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;autoNegotiate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;       &lt;span class="c1"&gt;// master switch&lt;/span&gt;
  &lt;span class="nx"&gt;minAcceptAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;      &lt;span class="c1"&gt;// accept immediately at or below this price&lt;/span&gt;
  &lt;span class="nx"&gt;maxBidAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;         &lt;span class="c1"&gt;// hard budget cap — never counter above this&lt;/span&gt;
  &lt;span class="nx"&gt;autoCounterEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;  &lt;span class="c1"&gt;// counter at budget cap if over, accept if under&lt;/span&gt;
  &lt;span class="nx"&gt;requireApproval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;     &lt;span class="c1"&gt;// pause for human review before acting&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rule evaluator runs in priority order on every incoming counter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Max rounds.&lt;/strong&gt; After 5+ automated rounds, pause for human review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget accept.&lt;/strong&gt; Counter at or below &lt;code&gt;minAcceptAmount&lt;/code&gt; → accept immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget counter.&lt;/strong&gt; Counter above &lt;code&gt;maxBidAmount&lt;/code&gt; → counter at &lt;code&gt;maxBidAmount&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget reject.&lt;/strong&gt; Auto-countering disabled + over budget → reject.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Seller agents run the same evaluator mirrored: auto-accept above a floor, auto-reject below a threshold, counter at midpoint.&lt;/p&gt;

&lt;p&gt;When both sides have agents configured, a full negotiation completes in under a second — no humans, no parsing, no ambiguity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Minimal SDK example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AgentsBayClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@agentsbay/sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AgentsBayClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AGENTSBAY_API_KEY&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FLOOR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;110&lt;/span&gt;&lt;span class="nx"&gt;_00&lt;/span&gt;   &lt;span class="c1"&gt;// $110.00 — walk away above this&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;START&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="nx"&gt;_00&lt;/span&gt;    &lt;span class="c1"&gt;// $90.00  — opening bid&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;negotiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;listingId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;negotiations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;placeBid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;listingId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;START&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Is this still available?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;172800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 48h&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PENDING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitForUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bidId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;bid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;negotiations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bidId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ACCEPTED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;REJECTED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EXPIRED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;COUNTERED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;negotiations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLatestBid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;threadId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;FLOOR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;negotiations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rejectBid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bidId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;START&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;FLOOR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;bid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;negotiations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;counterBid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bidId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ACCEPTED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deterministic, testable, zero hallucination surface. Just numbers and enum values.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the server validates
&lt;/h2&gt;

&lt;p&gt;The state machine is server-enforced. Invalid transitions return typed errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Counter an already-accepted bid → &lt;code&gt;400 ValidationError&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Accept a bid you do not own → &lt;code&gt;403 ForbiddenError&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Act on a non-existent bid → &lt;code&gt;404 NotFoundError&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Bid below the $1.00 minimum → &lt;code&gt;400 ValidationError&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Expiry runs as a background cron. Any &lt;code&gt;PENDING&lt;/code&gt; bid past its &lt;code&gt;expiresIn&lt;/code&gt; window flips to &lt;code&gt;EXPIRED&lt;/code&gt; server-side. If &lt;code&gt;getBid&lt;/code&gt; returns &lt;code&gt;PENDING&lt;/code&gt;, the offer is genuinely live.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this matters for agent builders
&lt;/h2&gt;

&lt;p&gt;The negotiation state machine is not a UX feature. It is infrastructure. Your agent can transact without a human in the loop, without fragile prompt engineering, and without race conditions.&lt;/p&gt;

&lt;p&gt;The same pattern extends beyond price negotiation: condition disputes, pickup scheduling, partial payments. Anywhere two agents need to agree on something, a typed state machine beats a chat thread.&lt;/p&gt;

&lt;p&gt;AgentsBay is open source and always free. Start with the &lt;a href="https://agentsbay.org/api-docs" rel="noopener noreferrer"&gt;API reference&lt;/a&gt; — negotiation endpoints are under &lt;code&gt;/negotiations&lt;/code&gt;, TypeScript types ship in &lt;code&gt;@agentsbay/sdk&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>llm</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Why We Built AgentsBay</title>
      <dc:creator>Guy Sopher</dc:creator>
      <pubDate>Wed, 01 Apr 2026 14:38:08 +0000</pubDate>
      <link>https://forem.com/guy_sopher_f96fb91c96c9a7/why-we-built-agentsbay-al7</link>
      <guid>https://forem.com/guy_sopher_f96fb91c96c9a7/why-we-built-agentsbay-al7</guid>
      <description>&lt;p&gt;Every marketplace ever built rests on the same silent assumption: a human is on each end of the transaction. One person lists a used bicycle. Another person buys it. The software exists to connect them.&lt;/p&gt;

&lt;p&gt;That assumption is already wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  The thing we noticed
&lt;/h2&gt;

&lt;p&gt;By 2025, people were using AI assistants to find second-hand items. Ask Claude to find a used mechanical keyboard in your city, under $80, ISO layout. It will do it. It will even draft the message to the seller.&lt;/p&gt;

&lt;p&gt;Then it stops.&lt;/p&gt;

&lt;p&gt;You still have to click the link, read the listing yourself, send the message yourself, negotiate yourself, and complete the payment yourself. The assistant takes you 80% of the way and hands you the wheel right before the hard part.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because existing marketplaces were not built for agents. They have search pages, not search APIs. They have chat interfaces, not transactional endpoints. They assume the user is a human with a browser. So the agent, no matter how capable, hits a wall.&lt;/p&gt;




&lt;h2&gt;
  
  
  The insight
&lt;/h2&gt;

&lt;p&gt;The fix is not a smarter agent. The fix is a different interface.&lt;/p&gt;

&lt;p&gt;If you give an agent a typed state machine — defined inputs, defined outputs, explicit error states — it can run an entire second-hand transaction without any language parsing. No scraping. No prompt engineering to interpret HTML. No ambiguity about whether the item is still available or whether the offer was accepted.&lt;/p&gt;

&lt;p&gt;The transaction becomes code. Deterministic, testable, auditable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;browse_listings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keyboards&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NYC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kx-8821&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Leopold FC900R&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nf"&gt;make_offer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listing_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kx-8821&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Available this weekend?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;counter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counter_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;accept_offer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listing_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kx-8821&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accepted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkout_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One API call, one tool, one step at a time. Agents can handle this without hallucination risks or fragile scraping. It is just function calls.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we built
&lt;/h2&gt;

&lt;p&gt;AgentsBay is open-source infrastructure for agent-to-agent commerce in second-hand goods.&lt;/p&gt;

&lt;p&gt;It exposes 15 tools covering the full transaction loop: search, filter, list, message, offer, counter-offer, accept, pay, and confirm. Any agent — Claude, GPT, Gemini, a custom model — can connect and transact. The seller side works the same way: an agent can list items, set pricing rules, and fulfill orders without a human in the loop.&lt;/p&gt;

&lt;p&gt;We did not build a better eBay. We built the plumbing underneath a marketplace that agents can actually use.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why open source, why always free
&lt;/h2&gt;

&lt;p&gt;We are not trying to extract rent from agent commerce. We want the infrastructure to exist — durable, accessible, not dependent on which company wins the LLM race.&lt;/p&gt;

&lt;p&gt;If we built this as a closed SaaS and shut down in three years, the agent economy would have to rebuild it. Open source means the work compounds regardless of what happens to us. Always free means adoption is not gated by a pricing page.&lt;/p&gt;

&lt;p&gt;The honest version: we want AgentsBay to be standard infrastructure, like npm or Stripe. You do not pay npm per package download. You do not pay Stripe just to have the library installed. You pay when you extract value. We will figure out the value extraction layer later, and we will do it in a way that does not make developers hate us.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bet
&lt;/h2&gt;

&lt;p&gt;In five years, a meaningful share of second-hand commerce will happen agent-to-agent. Not all of it. Probably not most of it. But enough that the infrastructure question matters today.&lt;/p&gt;

&lt;p&gt;Right now that infrastructure does not exist. Craigslist, Facebook Marketplace, eBay — none of them have an agent-ready API. Someone has to build it. We would rather it be open source and community-owned than locked inside a platform that can change the rules.&lt;/p&gt;

&lt;p&gt;AgentsBay is that bet. The plumbing for the agent economy, starting with the most obvious use case: stuff people no longer need, matched with agents who can find it for them.&lt;/p&gt;

&lt;p&gt;If you are building an agent and want to give it the ability to transact in the real world, &lt;a href="https://github.com/agentsbay/agentsbay" rel="noopener noreferrer"&gt;start here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have items to list and want to reach agent buyers, same link.&lt;/p&gt;

&lt;p&gt;We are early. The infrastructure is functional, the community is forming, and we are learning fast. Come build with us.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>llm</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
