Skip to content

Trade Bridge

Lafko edited this page Jul 6, 2026 · 9 revisions

← Home


Trade Bridge (Browser Extension)

The Trade Bridge lets a browser extension do all trade-site work (live search, fetching, whispering) while POEFixer does only the in-game buying. This keeps the trade-site traffic inside your real browser session — no cookie copying, no cf_clearance hassle — and is the foundation for a future "run POEFixer on another PC" mode.

You can use the free official extension, or build your own against the documented protocol below.

The Trade Bridge requires your POEFixer license to have the Trade feature. If Trade is configured as paid on the server, you need a valid key; if it is free, anyone can use it. When not entitled, the bridge replies entitlement_required and refuses buys.


1. How it works

Browser extension                         POEFixer
  live-search WebSocket  ─┐
  fetch item details      │  ws://127.0.0.1:PORT   in-game buying:
  whisper (teleport)      ├─────────────────────►  wait teleport → click → verify
  (Force teleport,        │   buy_request           → auto-stash → /hideout
   In-demand retry,       │ ◄─────────────────────  → report result
   Rate limiting)        ─┘   buy_result
  • The extension owns everything that touches the trade site.
  • POEFixer owns everything in the game client, and exposes a local WebSocket server (the Trade Bridge) that the extension connects to.
  • In this mode POEFixer's built-in tls-client is not used, and the trade-site controls (Connections, Trade Cookies, Force teleport, In-demand, Rate limiting) move into the extension and are hidden/disabled in POEFixer.

2. Install the extension

The extension ships inside POEFixer. In your POEFixer folder open Resources\extension\:

<POEFixer folder>\Resources\extension\
  ├─ poefixer-extension-chrome.zip     ← Chrome / Edge
  ├─ poefixer-extension-firefox.zip    ← Firefox
  └─ chrome\                            ← unpacked copy used by the "Assisted install" button

A fully silent one-click install is not possible — browsers block programmatic extension installs by design. You unzip the file for your browser and load it once. POEFixer's Assisted install… button (Configuration → Trade) does the Chrome/Edge part for you: it copies the unpacked chrome\ folder's path to the clipboard and opens this guide, so you can skip the unzip.

Chrome / Edge

  1. Unzip Resources\extension\poefixer-extension-chrome.zip to any folder (so manifest.json is at the top of it). Or click Assisted install… in POEFixer and use the copied path — then skip to step 4.
  2. Open chrome://extensions (Edge: edge://extensions).
  3. Enable Developer mode (top-right).
  4. Click Load unpacked and select that folder (or paste the Assisted-install path).
  5. The extension icon appears in the toolbar; it stays loaded across restarts.

After a POEFixer update, the bundled extension is refreshed — open chrome://extensions and click Reload on the extension card to pick up the new version (or re-unzip and Load unpacked).

Firefox

Release Firefox only installs Mozilla-signed extensions, so use temporary loading:

  1. Unzip Resources\extension\poefixer-extension-firefox.zip to any folder.
  2. Open about:debugging#/runtime/this-firefox.
  3. Click Load Temporary Add-on… and pick the manifest.json from the unzipped folder.
  4. It stays loaded until you restart Firefox (repeat after a restart).
  • Persistent: install a Mozilla/AMO-signed .xpi by opening it in Firefox and confirming the permission prompt.

Then connect

  1. In POEFixer: Configuration → Trade → Trade data source → Browser extension, note the Port (default 47362), and click Start bridge.
  2. In the extension popup, set the same Port — it should read Host: connected, and the status in POEFixer turns green and shows the extension's name and real version, e.g. "Extension connected (PoeFixerExt/1.1.2)" (the version comes from the extension's manifest — an outdated extension is flagged here).
  3. Log in to the trade site in that browser, then add your Trade Links and press Play (see §3).

3. Use it

Everything is controlled from POEFixer — the extension popup only sets the connection Port.

  1. In POEFixer: Configuration → Trade → Data source = Browser extension, set the Port, click Start bridge.
  2. In the extension popup, confirm Host: connected and the same Port.
  3. Log in to the trade site in the same browser.
  4. In POEFixer's Trade Links (Connections tab) add your trade2 search URLs and press Play — the search is pushed to the extension and runs there; Stop removes it. If the extension is not connected yet, Play opens a helper dialog instead: start the bridge right from it, or jump to Configuration → Trade for install help.
  5. Set behavior in POEFixer (Configuration → Trade): Force teleport, Item is in demand → teleport anyway, Rate limiting — POEFixer pushes these to the extension, which just obeys.
  6. When a listing matches, the extension whispers and POEFixer buys in-game. All extension activity appears in POEFixer's Logs (category Trade).

Reliable live feed (automatic): for each active search the extension opens a pinned background tab to the trade page and connects the live WebSocket inside that page (correct Origin + your session cookies — exactly like the site does). Just stay logged in to the trade site; you don't open Live Search manually. The Trade Link shows Connecting… until the live WS is up, then Live. Closing that pinned tab stops the search.

Manual Buy (buy a search in bulk, now): instead of waiting for live listings, press the Shopping Cart icon on any Trade Link to bulk-buy immediately. The extension fetches the cheapest matching listings for that search (up to the item count you set, honoring that link's currency filters), groups them by seller, and whispers + buys each through POEFixer — applying the same spend limits and auto-stash, with a single return to hideout at the end. Manual Buy and live search (Play) are mutually exclusive: stop your live searches before starting a Manual Buy, and vice-versa. If the extension is not connected, pressing the cart opens the same helper dialog as Play (start the bridge / open install settings).


4. Build your own extension — Trade Bridge API (PTBP)

The protocol is plain JSON over a WebSocket. Anyone can implement it. POEFixer hosts the server; your client connects to ws://127.0.0.1:<port> (default 47362, configurable in Settings).

The host honors buys only when your license has the Trade entitlement. Otherwise you receive {"type":"error","code":"entitlement_required"}. (Authentication details are intentionally not documented.)

4.1 Handshake

Send hello first; the host replies welcome.

// client → host
{ "type":"hello", "proto":1, "client":"MyExtension/1.0", "version":"1.1.0", "token":"<optional>" }
// host → client
{ "type":"welcome", "proto":1, "host":"POEFixer", "allowed":true, "paid":false, "ready":false }
  • version is the extension's manifest version (semver string, e.g. "1.1.0"). If your extension is older than the version POEFixer expects, it detects the mismatch, suppresses live searches, and disables buying until you reinstall the latest extension. POEFixer shows a warning in Settings → Trade. A buy attempted while outdated receives {"type":"error","code":"outdated_extension"}.
  • allowed:false → not entitled (treat as "buy a key"); the host will reject buys with entitlement_required.
  • token is only needed if the user enabled a pairing token in POEFixer (off by default).

After welcome (and whenever the user changes anything in POEFixer), the host pushes a config message. The program is the single control surface, so your extension must monitor exactly these searches and obey this behavior:

// host → client
{ "type":"config",
  "behavior":{ "forceTeleport":false, "inDemandRetry":true, "rateLimit":true },
  "searches":[ { "realm":"poe2", "league":"Standard", "searchId":"", "note":"" } ] }

4.2 Readiness — whisper only when ready

The host pushes status periodically:

{ "type":"status", "ready":true, "inGame":true, "attached":true, "busy":false }

You MUST only whisper/teleport when the last status.ready === true. ready = entitled && attached && in-game && !busy. Whispering while not ready teleports the character with no buyer driving the trade.

4.3 Buy flow

After your whisper succeeds (the character is teleporting), send buy_request:

// client → host
{ "type":"buy_request", "id":"r-42", "seq":7,
  "group":{
    "searchId":"", "groupId":"g-9", "isLastGroup":true,
    "items":[ {
      "itemId":"...", "stashX":3, "stashY":1, "w":1, "h":1,
      "currency":"chaos", "amount":50,
      "seller":"AccountName", "stashName":"~price 50 chaos",
      "name":"...", "typeLine":"...", "baseType":"...", "rarity":"Rare",
      "iconUrl":"https://...", "ilvl":82, "corrupted":false, "identified":true,
      "explicitMods":[ "..." ], "implicitMods":[ "..." ],
      "league":"Standard", "realm":"poe2",
      "hideoutToken":"...", "indexedTime":"..."
    } ] } }

The host acks it, optionally emits progress, then a single result:

{ "type":"ack", "ackSeq":7 }
{ "type":"buy_progress", "id":"r-42", "phase":"teleporting" }
{ "type":"buy_result", "id":"r-42", "seq":11,
  "items":[ { "itemId":"...", "ok":true, "reason":"verified" } ],
  "summary":{ "bought":1, "failed":0 } }
  • group.searchId is the search the listing came from; the host uses it to bind the buy to the right Trade Link (falls back to the first active search if omitted). Send it for correct multi-search behavior.
  • stashX/stashY are the item's coordinates in the seller's shop; w/h its size — these drive the in-game click.
  • Price may be sent flat (currency/amount, as above) or nested ("price":{"currency","amount"}) — the host accepts both.
  • hideoutToken is carried for logging only (you already used it to whisper).
  • You must ack each buy_result: send {"type":"ack","ackSeq":<buy_result.seq>}.

4.4 Whisper-stage events + logs (ext → host)

Whisper-stage outcomes (keep POEFixer's logs complete):

{ "type":"trade_event", "seq":8, "stage":"whisper", "outcome":"in_demand", "itemId":"...", "detail":"" }
// outcome ∈ in_demand | whisper_failed | teleport_failed | fetch_error

A trade_event may also carry the full item object (same fields as a buy_request item). teleport_failed = the seller's listing was still in demand even after a forced re-whisper; sent with its item, POEFixer records it in Trade Logs as a Teleport Fail. fetch_error / whisper_failed / in_demand are informational and go to the general Logs (category Trade).

Forward your activity to POEFixer's log (recommended — all extension logs should surface in the program):

{ "type":"log", "level":"info", "message":"" }   // shown in POEFixer Logs (category Trade)

Report each search's live-WS state so POEFixer's Trade Link shows Connecting…/Live:

{ "type":"search_status", "searchId":"", "state":"connected" }   // state ∈ connected | closed | error

4.5 Manual buy (host-driven bulk buy)

When the user clicks Manual Buy (the cart icon) on a Trade Link, the host sends manual_buy; your extension performs a one-shot bulk buy of that search and reports lifecycle/progress with manual_buy_status. The per-seller purchases use the ordinary buy_request flow (§4.3) with isLastGroup:false; the host fires the final /hideout once it receives state:"done".

// host → client: start a one-shot bulk buy of one saved search
{ "type":"manual_buy", "id":"mb-…", "seq":12,
  "search":{ "realm":"poe2", "league":"Standard", "searchId":"", "note":"" },
  "itemCount":10,                               // 0 = all results
  "filters":{ "chaos":{ "min":0, "max":0 } } }  // currency → {min,max}; 0 = unbounded
// host → client: cancel an in-progress bulk buy
{ "type":"manual_buy_cancel", "id":"mb-…", "seq":13 }
// client → host: lifecycle + progress (ack-tracked like trade_event)
{ "type":"manual_buy_status", "id":"mb-…", "seq":4,
  "state":"started|progress|done|cancelled|error",
  "processed":3, "bought":2, "failed":1, "total":10, "detail":"" }
  • Resolve the search, fetch the cheapest matching listings, apply filters, cap at itemCount (0 = all), group by seller, and buy each group via buy_request (isLastGroup:false).
  • Send manual_buy_status started at the top, progress as groups complete, and exactly one terminal done / cancelled / error.

4.6 Reliability

  • buy_request and trade_event carry an incrementing seq; the host acks them. Keep un-acked frames and resend them on reconnect (sorted by seq).
  • buy_request.id is the idempotency key — a re-sent id returns the recorded buy_result (or error: duplicate while still in flight). Use a stable, unique id per buy.
  • buy_result carries a seq that you ack.

4.7 Errors

{ "type":"error", "code":"entitlement_required|not_in_game|busy|duplicate|bad_request|unauthorized|proto_unsupported|outdated_extension", "id":"r-42", "detail":"..." }

4.8 Minimal client (JavaScript)

const ws = new WebSocket("ws://127.0.0.1:47362");
let ready = false, seq = 0, behavior = {}, searches = [];
const log = (message) => ws.send(JSON.stringify({ type:"log", level:"info", message }));
ws.onopen = () => ws.send(JSON.stringify({ type:"hello", proto:1, client:"MyExt/1.0", version:"1.1.0" }));
ws.onmessage = (e) => {
  const m = JSON.parse(e.data);
  if (m.type === "welcome" && !m.allowed) console.warn("Trade entitlement required");
  if (m.type === "config") { behavior = m.behavior; searches = m.searches; } // program controls you
  if (m.type === "status")  ready = m.ready;
  if (m.type === "buy_result") ws.send(JSON.stringify({ type:"ack", ackSeq:m.seq }));
};
// monitor `searches`, obey `behavior`; after YOUR whisper succeeds and ready === true:
function buy(items) {
  ws.send(JSON.stringify({ type:"buy_request", id:"r-"+Date.now(), seq:++seq,
    group:{ groupId:"g", isLastGroup:true, items } }));
}

5. Troubleshooting

  • Host: disconnected — POEFixer not running, bridge not started, or wrong port. Start the bridge in Settings → Trade; match the port in the popup.
  • entitlement_required — your license lacks the Trade feature (paid on the server).
  • not_in_game — POEFixer isn't attached/in-game; wait for status.ready.
  • Live search never fires — make sure you're logged in to the trade site in the same browser. The extension automatically opens its own pinned background tab per search and runs the live WebSocket inside that page; the Trade Link shows Connecting… then Live. If it stays on Connecting…, open the pinned tab to check you're logged in.
  • Port in use — change the port in Settings → Trade and in the popup, then restart the bridge.

← Home

Clone this wiki locally