-
Notifications
You must be signed in to change notification settings - Fork 0
Trade Bridge
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_requiredand refuses buys.
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-clientis 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.
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.
-
Unzip
Resources\extension\poefixer-extension-chrome.zipto any folder (somanifest.jsonis at the top of it). Or click Assisted install… in POEFixer and use the copied path — then skip to step 4. - Open
chrome://extensions(Edge:edge://extensions). - Enable Developer mode (top-right).
- Click Load unpacked and select that folder (or paste the Assisted-install path).
- The extension icon appears in the toolbar; it stays loaded across restarts.
After a POEFixer update, the bundled extension is refreshed — open
chrome://extensionsand click Reload on the extension card to pick up the new version (or re-unzip and Load unpacked).
Release Firefox only installs Mozilla-signed extensions, so use temporary loading:
-
Unzip
Resources\extension\poefixer-extension-firefox.zipto any folder. - Open
about:debugging#/runtime/this-firefox. - Click Load Temporary Add-on… and pick the
manifest.jsonfrom the unzipped folder. - It stays loaded until you restart Firefox (repeat after a restart).
-
Persistent: install a Mozilla/AMO-signed
.xpiby opening it in Firefox and confirming the permission prompt.
- In POEFixer: Configuration → Trade → Trade data source → Browser extension, note the Port (default
47362), and click Start bridge. - 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).
- Log in to the trade site in that browser, then add your Trade Links and press Play (see §3).
Everything is controlled from POEFixer — the extension popup only sets the connection Port.
- In POEFixer: Configuration → Trade → Data source = Browser extension, set the Port, click Start bridge.
- In the extension popup, confirm Host: connected and the same Port.
- Log in to the trade site in the same browser.
- 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.
- 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.
- 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).
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.)
Send hello first; the host replies welcome.
-
versionis 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 withentitlement_required. -
tokenis 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":"…" } ] }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.
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.searchIdis 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/stashYare the item's coordinates in the seller's shop;w/hits 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. -
hideoutTokenis carried for logging only (you already used it to whisper). - You must ack each
buy_result: send{"type":"ack","ackSeq":<buy_result.seq>}.
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_errorA
trade_eventmay also carry the fullitemobject (same fields as abuy_requestitem).teleport_failed= the seller's listing was still in demand even after a forced re-whisper; sent with itsitem, POEFixer records it in Trade Logs as a Teleport Fail.fetch_error/whisper_failed/in_demandare 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 | errorWhen 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 atitemCount(0 = all), group by seller, and buy each group viabuy_request(isLastGroup:false). - Send
manual_buy_statusstartedat the top,progressas groups complete, and exactly one terminaldone/cancelled/error.
-
buy_requestandtrade_eventcarry an incrementingseq; the host acks them. Keep un-acked frames and resend them on reconnect (sorted byseq). -
buy_request.idis the idempotency key — a re-sent id returns the recordedbuy_result(orerror: duplicatewhile still in flight). Use a stable, unique id per buy. -
buy_resultcarries aseqthat you ack.
{ "type":"error", "code":"entitlement_required|not_in_game|busy|duplicate|bad_request|unauthorized|proto_unsupported|outdated_extension", "id":"r-42", "detail":"..." }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 } }));
}- 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 forstatus.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.