Add an Embedded App

Embed an external app or webpage directly into the agent's workspace as an iframe

An embedded app widget displays your own webpage or application inside the agent's workspace as an iframe. Glassix passes the open ticket's details to the widget, so the widget can display information about the customer the agent is currently speaking with.

📘

This page covers embedded app widgets: iframes that appear inside the agent's workspace.

Overview

Embedded app widgets make it easy for agents to see the right information and take quick actions to support your users or convert your leads. They're great for streamlining existing workflows and enabling new ones by bringing other services into the Inbox.

Let's say your agents use a knowledge base or an external CRM while they're chatting. Instead of opening it in a separate window, you can add a widget that displays the CRM within the conversation details in your workspace, allowing agents to quickly view and use the information they need at any time.

Widget Types

When you create a widget, you choose where it appears in the workspace. The location you pick is passed to your iframe as the type query string parameter.

Typetype parameterWhere it appears
Ticket Details PanelTicketDetailsPanelIn the ticket details panel, alongside an open conversation.
Chat Widget Welcome ScreenChatWidgetWelcomeScreenOn the chat widget's welcome screen.

Create an Embedded App Widget

  1. Go to Settings.

    Click on the symbol in the agent dashboard.

  2. Click DevelopersEmbedded App Widget.
  3. Click New App.
  4. In the URL text box, type the URL.
🚧

Note:

The target URL must start with https://.

  1. In the Name text box, type a name of your choice.
  2. In the Height scroll bar, set the height of your widget in pixels. Valid values are 50–450 px. Anything outside that range falls back to 200 px.
  3. Click Save.

Once saved, each widget has the following properties:

  • Name — the name of the widget app.
  • URL — the webpage or external application you want to display inside the widget.
  • Height — the default height of the widget, in pixels.
  • Widget App Id — the widget's unique identifier, passed to your iframe as applicationId.
  • Secret — used to verify the token Glassix sends to your iframe. See Verify the Token.
  • Type — where the widget appears. See Widget Types.

How Glassix Calls Your Iframe

Widgets are iframes shown in the agent's workspace. In each ticket, an iframe appears with the source of the URL you defined, plus these query string parameters:

NameDescription
applicationIdUUID of your app widget.
departmentIdUniqlu ID specific to the department.
ticketIdThe ID of the ticket the agent currently has open.
localeen, es, or he.
userNameThe user name of the logged-in agent.
typeWhere the widget is displayed: TicketDetailsPanel or ChatWidgetWelcomeScreen. See Widget Types.
tokenJWT token (HMAC SHA-512) signed with your app secret. The token contains the applicationId, departmentId, and ticketId as claims. Use it to verify the request is legitimate.

Example

Here's an example of an iframe source with all the parameters:

https://www.example.com/your-iframe.html?applicationId=a5db986f-dad8-4170-b7e4-3c453a50c887&departmentId=eea74d6e-da20-4d0c-a9ce-ed164e31b5d1&locale=en&ticketId=733393&token=eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8y&type=TicketDetailsPanel&[email protected]

Verify the Token

Anyone can put together a URL with the right query string. Treat the raw parameters as untrusted, and verify the signed token on your backend before you act on any of them. The signing key is the Secret shown in your widget's settings in Glassix.

// On YOUR backend. Never trust the raw query params alone.
const jwt = require("jsonwebtoken");

function verifyGlassixToken(token) {
  // Secret = the widget Secret shown in the Glassix widget settings.
  const payload = jwt.verify(token, process.env.GLASSIX_WIDGET_SECRET, {
    algorithms: ["HS512"],
  });
  // Trust these (signed) claims, not the URL query string:
  return {
    applicationId: payload.applicationId,
    departmentId: payload.departmentId,
    ticketId: payload.ticketId,
  };
}
🚧

Note:

Keep your Secret on your server. Don't ship it in client-side JavaScript, where anyone loading your page can read it.


Receive Ticket Data

Add the following script to your HTML document body. Any time an agent switches between tickets, the ticket's details are automatically sent to you. This means you don't need to send a GET ticket request every time the agent switches tickets.

🚧

Note:

You must also add the embedded-app-iframe.js script to your HTML for this to work. See Styling and Auto-Height.

<script>
    window.onTicketLoaded = function (ticket) {
    }
</script>

The Ticket Object

onTicketLoaded receives the ticket in this shape:

// ticket passed to window.onTicketLoaded(ticket)
{
  "id": 733393,
  "departmentId": "eea74d6e-...",
  "field1": "...", "field2": "...", // ... through field10 (custom fields)
  "culture": "en-US",
  "ticketType": 0,
  "tags": ["vip"],
  "owner": { },
  "state": "Open",
  "open": "2026-06-30T10:00:00Z",      // opened-on timestamp (string)
  "lastActivity": "2026-06-30T10:05:00Z",
  "uniqueArgument": "...",
  "primaryProtocolType": "Mail",        // channel, e.g. Mail / WhatsApp / SMS
  "details": { },
  "participants": [
    {
      "id": "...",
      "name": "...",
      "displayName": "...",
      "protocolType": "Mail",
      "subProtocolType": "...",
      "identifier": "[email protected]", // <-- customer email / phone / channel id
      "type": 1,
      "userName": "...",
      "contactId": "..."
    }
  ],
  "transactions": null                  // not included in onTicketLoaded
}

Two things to watch for:

  • There's no dedicated email field. The customer's email address or phone number is in participant.identifier, and which one it is depends on the channel.
  • transactions is always null here. It's intentionally excluded from onTicketLoaded. If you need the conversation's messages, fetch them with the API get ticket endpoint.

Styling and Auto-Height

Add the following script to your HTML document header. It handles the iframe styling and automatic height resizing based on your content.

<script src="https://cdn.glassix.com/clients/embedded-app-iframe.js"></script>

The font family, font size, and more should match the styling of the agent's workspace, and this script takes care of that for you.

An iframe's height isn't dynamic according to its content, so without this script, the iframe stays at the fixed default height you set when you created the widget. embedded-app-iframe.js detects height changes and notifies the parent window. The panel still caps the rendered height.

The script calls this function when it has finished loading all the resources:

<script>
    window.onDoneLoadingResources = function () {
        }
</script>

Requirements and Troubleshooting

  • Your iframe must be served over HTTPS. Glassix runs on HTTPS, so mixed content is blocked.
  • Do not set the X-Frame-Options header on your page. If you use a Content Security Policy, allow framing by Glassix with a frame-ancestors directive.
  • Set the widget Height between 50 and 450 px. Values outside that range default to 200 px.
  • Include embedded-app-iframe.js for styling and automatic height resizing. Without it, the iframe stays at its fixed default height.
  • If your widget loads but doesn't respond when the agent switches tickets, check that embedded-app-iframe.js is present, onTicketLoaded doesn't fire without it.

Full Example

This example shows the three things that matter: reading the parameters, implementing onTicketLoaded, and including the client script. Plug your own logic into onTicketLoaded.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <script src="https://cdn.glassix.com/clients/embedded-app-iframe.js"></script>
</head>
<body>
    <h3>Customer info</h3>
    <div id="info">Loading ticket...</div>

    <script>
        // 1) Params Glassix adds to your URL (verify `token` on your server - see "Verify the token")
        const params = new URLSearchParams(location.search);

        // 2) Called every time the agent opens/switches a ticket
        window.onTicketLoaded = function (ticket) {
            const customer = (ticket.participants || [])[0] || {};
            document.getElementById("info").innerText =
                "Ticket #" + ticket.id + " - " + (customer.identifier || "unknown");
            // -> here you would call YOUR system using customer.identifier
        };
    </script>
</body>
</html>

Did this page help you?