Messaging protocol

Read this when you need the low-level message shape. Most projects should still stay on RTO_form_api.js or the shipped helper layers.
Product model Safe tab control RTO_helpers guidance
Use raw protocol only when you need it
Most projects should stay on RTO_form_api.js or the shipped helper layers. Read this page when you need the exact message shape or want to debug an edge case deeply.

Overview

RTO uses window.postMessage to exchange messages between your page and the extension. The helper RTO_form_api.js handles:

  • Generating requestId values
  • Waiting for the matching *Result message
  • Timeouts (TIMEOUT)
Recommendation
Use RTO_form_api.js unless you are building your own wrapper.

External add-on protocol — introduced in v8.4.0, current in v8.5.0

RTO v8.4.0 introduced direct inter-extension messages from trusted official add-ons. The protocol remains current in RTO v8.5.0. The add-on is identified by the browser extension runtime, then RTO Original applies its Trust Policy before returning capabilities or accepting commands.

Not an open port
Unknown external senders are refused. RTO original still enforces RTO Lock, allow-list checks and command capabilities.

Modern allow-list flow

In 8.4.0, a page should still ask through allowlistAddRequest. The plugin then prompts on the matching master tab, and the master tab answers with either allowlistAddConfirm or allowlistAddReject.

Message Direction Role
allowlistAddRequest page → extension Ask for a host to be added from a normal page.
promptAllowlistAdd extension → master tab Visible prompt sent to the master tab UI.
allowlistAddConfirm master tab → extension Approve the pending request.
allowlistAddReject master tab → extension Reject the pending request.
allowlistAddRequestResult extension → page Final success or failure returned to the requesting page.
Important
Direct page-side allowlistAdd is no longer the normal public flow. It is intentionally blocked unless the plugin/internal UI performs the confirmation step.

Message envelope

Outgoing messages usually contain:

  • origin: a page-side marker (docs default: window.RTO_PAGE_ORIGIN)
  • type: for example "detect", "openTab", or "command"
  • requestId: unique ID to match replies

The extension responds with type + "Result" (example: detectResult, commandResult).

Important
The origin value is a protocol marker, not a security boundary. Security is enforced by the extension (allow-list + controlled tabs). For raw code, always filter messages strictly (see examples).

Raw detect example

// Raw detect (without RTO_form_api.js)
(function(){
  const requestId = "detect_" + Date.now() + "_" + Math.random().toString(16).slice(2);
  const pageOrigin = window.RTO_PAGE_ORIGIN || "rto-docs";
  let t;

  function done(){
    if(t) clearTimeout(t);
    window.removeEventListener("message", onMsg);
  }

  function onMsg(ev){
    const d = ev.data;
    if(ev.source !== window) return;                 // same window only
    if(!d || typeof d !== "object") return;
    if(d.origin !== pageOrigin) return;              // protocol marker must match
    if(d.requestId !== requestId) return;
    if(d.type !== "detectResult") return;

    done();
    console.log("detect result:", d);
  }

  window.addEventListener("message", onMsg);

  t = setTimeout(function(){
    done();
    console.warn("detect timeout");
  }, 1500);

  window.postMessage({
    origin: pageOrigin,
    type: "detect",
    requestId: requestId
  }, "*");
})();

Raw command example

DOM actions are sent as type:"command" with an action name and a payload object.

// Raw command (example: getText)
(function(){
  const requestId = "cmd_" + Date.now() + "_" + Math.random().toString(16).slice(2);
  const pageOrigin = window.RTO_PAGE_ORIGIN || "rto-docs";
  let t;

  function done(){
    if(t) clearTimeout(t);
    window.removeEventListener("message", onMsg);
  }

  function onMsg(ev){
    const d = ev.data;
    if(ev.source !== window) return;
    if(!d || typeof d !== "object") return;
    if(d.origin !== pageOrigin) return;
    if(d.requestId !== requestId) return;
    if(d.type !== "commandResult") return;

    done();
    console.log("command result:", d);
  }

  window.addEventListener("message", onMsg);

  t = setTimeout(function(){
    done();
    console.warn("command timeout");
  }, 5000);

  window.postMessage({
    origin: pageOrigin,
    type: "command",
    requestId: requestId,
    action: "getText",
    payload: {
      tabKey: "demoTab",
      selector: "h1"
    }
  }, "*");
})();
Note
Even if you write raw messages, you still need allow-list permission and a controlled tab for most actions.

Next