ruLog in to Senler

Embedded page

Page setup

The embedded page is the plugin's user interface inside the cabinet: users can configure connections and accounts, view data, and work with other plugin features there. The developer controls the page's contents. For a Plugin application, open the Embedded page section.

Page setup. 1. Embedded page section
1. Embedded page section

The Show the application's main embedded page switch adds a separate plugin page item to the project cabinet. When it is disabled, that item disappears, but the main URL continues to be used automatically by agent tool and automation step configurators.

Main URL and developer mode

Enter the single address used for the main page and for tool and step configurators in Primary URL. If the protocol is omitted, the cabinet saves the address with https://; an explicit http:// is allowed for local development. Use HTTPS for normal publication. The page must allow framing and must not rely on navigating the whole top-level window.

Enable Developer mode so application team members open the page from a separate developer URL. This URL can use http://localhost or a separate test environment; a domain without a protocol also receives https://. Regular users of the installed application continue to open the main URL.

After changing a URL or developer mode, use the save action.

Page setup. Highlighted elements: 2. Show the application's main embedded page switch; 3. Primary URL; 4. Developer mode; 5. developer URL; 6. save action
2. Show the application's main embedded page switch · 3. Primary URL · 4. Developer mode · 5. developer URL · 6. save action

A separate Application actions setting is located below. It publishes selected backend methods to MCP and has its own save button; the embedded-page switch and URL do not affect it.

Testing and launch modes

Select Test and choose an available project. When developer mode is enabled, testing opens the developer URL; otherwise, it opens the main URL. Testing remains available when showing the main page in the project menu is disabled.

Testing and launch modes. 2. available project
2 / 2
2. available project

Every launch path uses the same version 2 URL bootstrap contract: a one-time launch_code, senler_context_version=2, senler_mode, senler_theme=light|dark, and senler_language=ru|en. The mode is installed, test, tool_configurator, or automation_step_configurator.

The senler_* parameters are only for initial rendering before Bridge connects. Application, project, installation, agent, automation, and node identifiers are provided only in the validated Senler Bridge context.launch. The application server must trust only a verified launch_code. Standard OAuth/API authorization with granted permissions is still required to read or change project data; launch_code does not replace it.

Verifying launch_code

launch_code has the form <payload>.<signature>. Both parts use base64url. The payload contains version: 1, project_id, the expires_at expiration time in Unix seconds, and a random nonce; the code is valid for 2 minutes. The signature is HMAC-SHA256 of the encoded payload using the application's Client Secret.

Verify the code on the application server before showing data: compare the signature without leaking timing information, validate the version and expiration, reject a reused nonce, and then create the application's own short-lived session. Do not write the complete URL or launch_code to public logs. If the code has expired, the user must close and reopen the page or configurator to receive a fresh code.

Node.js verification example:

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyLaunchCode(code, clientSecret) {
  const parts = code.split(".");
  if (parts.length !== 2 || !parts[0] || !parts[1]) {
    throw new Error("Invalid launch_code format");
  }

  const [encodedPayload, encodedSignature] = parts;
  const actual = Buffer.from(encodedSignature, "base64url");
  const expected = createHmac("sha256", clientSecret)
    .update(encodedPayload)
    .digest();

  if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
    throw new Error("Invalid launch_code signature");
  }

  const payload = JSON.parse(
    Buffer.from(encodedPayload, "base64url").toString("utf8"),
  );
  const now = Math.floor(Date.now() / 1000);
  if (
    payload.version !== 1 ||
    typeof payload.project_id !== "string" ||
    typeof payload.expires_at !== "number" ||
    typeof payload.nonce !== "string" ||
    payload.expires_at < now
  ) {
    throw new Error("Expired or invalid launch_code payload");
  }

  return payload;
}

The application is responsible for tracking used nonce values and creating a session. There is no separate exchange of launch_code for a Senler token, and the code does not replace OAuth.

Embedded application context and elements

Connect Senler Bridge in the same way as in the tool configurator. For a regular page, context.launch.type is embedded_page; the context contains app_id, project_id, an optional installation_id, and installed or test mode. Configurators use tool_configurator and automation_step_configurator; the latter is documented in Automation steps. This lets one interface support different flows without reading IDs from unverified query parameters.

To let the cabinet agent explain, highlight, open, or edit elements inside the application, add IDs under the app.* namespace and register the standard handler:

import {
  clearSenlerBridgeElementHighlight,
  createSenlerBridgeClient,
  executeSenlerBridgeElementAction,
} from "@senler/ui/bridge";

const bridge = createSenlerBridgeClient({ parentOrigin });
await bridge.connect();

const unsubscribeAction = bridge.onElementAction((request) =>
  executeSenlerBridgeElementAction(request),
);
const unsubscribeClear = bridge.onElementHighlightClear(() =>
  clearSenlerBridgeElementHighlight(),
);
<button
  data-ai-reveals-context-id="app.orders.create.form"
  data-ai-reveal-action="click"
>
  Create order
</button>

<form data-ai-context-id="app.orders.create.form">
  <input data-ai-context-id="app.orders.create.customer" />
  <button data-ai-context-id="app.orders.create.submit">Save</button>
</form>

The supported actions are highlight, scroll_to, focus, click, fill, clear, select, and toggle. A target must be visible and unique for its data-ai-context-id; fill, clear, and select work only with native input, textarea, or select elements, while toggle works with checkboxes, radio buttons, or an element with role="switch". If a field is inside a tab, dropdown, accordion, or dialog, mark the opener with data-ai-reveals-context-id; Bridge can follow up to four such steps.

The current Senler Bridge passes context_id, action, and an optional value. It does not pass entity_type, entity_id, or role: Cabinet rejects such commands with embedded_target_qualifiers_unsupported. This handler therefore cannot distinguish rows sharing a context ID by their entity IDs. This is a Bridge limitation, not a limitation of markup on the host website, where the widget supports targeting a specific entity. Support for custom dropdowns in the host widget also does not automatically apply inside an embedded application.

Cabinet sends a command only to a single visible application responsible for the requested context. Multiple matching applications cause ambiguous_embedded_target; a hidden or unavailable matching application causes embedded_frame_unavailable. The order in which windows were opened does not select the target. Close the extra window or open the intended application before trying again.

Use stable semantic IDs such as app.orders.filter.status, and mark the corresponding explanations in the application documentation with the same IDs. Do not include a project, user, translated label, or random DOM ID. This lets the agent find the right explanation and perform the same flow in Russian, English, narrow, and wide layouts.

Call unsubscribeAction(), unsubscribeClear(), and bridge.destroy() when the page unmounts.

The embedded frame allows scripts, forms, modal windows, downloads, popups, clipboard access, full-screen mode, and the microphone. Treat browser capabilities outside this list as unavailable until separately verified.

Save changes with the save action. If the test does not open, check the URL, Content-Security-Policy/X-Frame-Options headers, whether the browser can reach the local server, and errors inside the iframe.