-
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.
First, in POEFixer: Configuration → Trade → Trade data source → Browser extension. Note the Port (default 47362) and click Start bridge (right next to the Port). The status beside it turns green ("Extension connected") once the extension handshakes.
A fully silent one-click install is not possible — browsers block programmatic extension installs by design. POEFixer's Assisted install… button does the parts it can (stages the extension folder, copies its path to the clipboard, opens this guide); you finish the last 2–3 clicks in the browser.
- Open
chrome://extensions(Edge:edge://extensions). - Enable Developer mode (top-right).
- Click Load unpacked and select the extension folder (the path the Assisted-install button copied, or
extension/dist/chrome). - The extension icon appears in the toolbar. It stays loaded across restarts.
Release Firefox only installs Mozilla-signed extensions.
-
Testing: open
about:debugging#/runtime/this-firefox→ Load Temporary Add-on → pickextension/dist/firefox/manifest.json(removed on restart). -
Persistent: install the signed
.xpi(AMO self-distributed) by opening it in Firefox and confirming the permission prompt.
Enterprise-policy force-install (ExtensionInstallForcelist / ExtensionSettings) can auto-install from a hosted package, but it shows "managed by your organization", needs admin rights, still requires store hosting/signing, and may trip antivirus. Use only if you know what you're doing.
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.
- 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.
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.
-
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 | 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", "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" }));
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.