DocumentationRelease Notes
Log In
Documentation

Real-time state API

The real-time state API provides information about the current state of the BeyondTrust Appliance B Series. Data include logged-in representatives, support sessions in progress, and queue statuses. The real-time state API provides a summary of the current system state rather than a history of events. It can answer such questions as:

  • What sessions are waiting in a queue?
  • What sessions have a representative in them?
  • What sessions have been in the system for longer than X minutes?
  • What is the estimated wait time for a session?
  • How many chat support sessions are in progress?
  • How many RDP sessions are in progress?
  • How many sessions started from a session key or the issue submission form are in progress?
  • Which representatives are logged in?
  • Which representatives are available to accept sessions from a specific queue?
  • In which sessions is a representative participating?
  • How long has a representative been logged in?
  • Which representatives are idle, are busy, or have automatic session assignment disabled?

📘

Note

The real-time state API is an authenticated API. For instructions on using authenticated APIs using OAuth, see Authenticate to the API.

The real-time state API is structured as tables These data must be parsed by the integration, following specific procedures A JavaScript library is provided to facilitate use of the socket-based real-time state API in a browser.

ℹ️

Note

The real-time state API is not able to answer questions about historical data. For example, some questions the real-time state API cannot answer include:

  • How many sessions has a representative participated in during the last X hours?
  • What is the total amount of time a representative has been idle during the last X hours?
  • What is the total amount of time a representative was participating in at least Y sessions during the last X hours?
  • How long has a representative been idle, busy, or available?

For this type of historical question, see Archive.

Protocol

In order to retrieve and maintain data using the real-time state API, a certain protocol must be followed. The protocol is divided into four main parts, shown in order of execution:

  • Connection
    • The integration connects to the B Series Appliance via a secure connection.
  • Authentication
    • The integration sends credentials for authentication.
    • The server authenticates the credentials. The account used must have permission to use the real-time state API.
    • The server sends an authentication response to the integration.
  • Model subscriptions
    • The integration subscribes to one or more tables in the model.
    • The server sends a full copy of the subscribed tables to the integration.
    • The integration is expected to maintain a copy of the tables.
  • Model updates
    • The server pushes future updates of the subscribed tables to the integration.
    • The integration is expected to update its copy of the tables.

The connection, authentication, and model subscription phases are serial. All messages in those phases must be sent in the correct order, and all messages are required. Messages in the model updates phase can arrive in any order. Messages are encoded using JSON (JavaScript Object Notation), except for the connection phase.

The real-time state API has certain limits:

  • A maximum of thirty connected integrations may receive model updates simultaneously.
  • Connected integrations receive updates with a maximum latency of twenty seconds.
  • Integrations must have sufficient bandwidth to receive updates. Integrations that get too far behind the real-time stream are automatically disconnected.

⚠️

Important

Information will be sent from the B Series Appliance within binary websocket messages, and data sent to the B Series Appliance can be sent within text websocket messages. Each message sent to or received from the B Series Appliance is terminated with a newline character (\n). It may be necessary to trim this character before parsing the JSON received. Similarly, a newline character must be appended to the end of all messages sent.

Connection

During the connection phase of the protocol, the integration makes a secure web socket connection to the B Series Appliance.

Integration connects to the B Series Appliance using a secure web socket

sock = new WebSocket ("wss://<hostname>/nw")
sock.SubProtocol = "ingredi state api"

ℹ️

Note

The subprotocol must be set appropriately. It must also be URL encoded when sent to the server (e.g. "ingredi%20state%20api")

Authentication

During the authentication phase of the protocol, the integration authenticates with the B Series Appliance.

Integration sends credentials to the B Series Appliance

Integration → B Series Appliance

{
    "type" : "authenticate",
    "credentials" :
    {
        "bearer_token" : "<OAuth 2.0 bearer token>"
    }
}\n

B Series Appliance verifies the credentials and sends an authentication response to the integration

B Series Appliance → Integration

{
    "type" : "authenticate_response",
    "success" : true,
    // or
	// "success" : false,
	"reason" : "reason if success == false"
}\n

After authenticating, the integration must subscribe to one or more tables in the model.

Model subscriptions

During the model subscriptions phase of the protocol, the integration tells the B Series Appliance the parts of the system state model for which it wants to receive updates.

Integration subscribes to the model

Integration → B Series Appliance

{
    "type" : "subscribe",
    "tables" : "all"
    // or
	// "tables" : ["customer_client", "..."]
}\n

The tables name/value pair can be either "all" to subscribe to all tables or an array of table names in the model.

B Series Appliance confirms the subscription

B Series Appliance → Integration

{
    "type" : "subscribe_response",
    "timestamp" : <UNIX timestamp>,
    "success" : true,
    // or
	// "success" : false,
	"reason" : "reason if success == false"
}\n

In the subscribe response, the timestamp is the B Series Appliance's current time. This is useful for doing time calculations when the integration's clock is skewed from the B Series Appliance's clock.

After receiving the subscribe response, the integration starts to receive model updates from the B Series Appliance.

Model updates

During the model updates phase of the protocol, the integration receives system state model updates from the B Series Appliance. The integration does not request model updates. Instead, model updates are sent automatically by the B Series Appliance as resources permit.

ℹ️

Note

If the B Series Appliance sends a large update, the data may be broken up multiple parts. Check to see if the message terminates with a newline character (\n). If it does not, append further messages until you receive a message ending with a newline, indicating you have reached the end of the data.

Update model message

B Series Appliance → Integration

{
    "type" : "model_update"
    // "insert" is specified only if rows need to be inserted.
	"insert" :
	{
		// One or more table names are specified as keys.
		"<table>" :
		{
			// One or more row IDs are specified as keys.
			"<id>" :
			{
				// One or more field names are specified as keys.
				// Some fields in the table may not be listed.
				"<field>" : "<value>",
				// ...
			},
			// ...
		},
		// ...
	},
	// "update" is specified only if rows need to be updated
	"update" :
	{
		// This object has the same syntax as "insert"
	},
	// "delete" is specified only if rows need to be deleted
	"delete" :
	{
		// One or more table names are specified as keys.
		// Each value is an array of row IDs to delete.
		"<table>" : ["<id>", "<id>", ...],
		// ...
	},
}\n

At least one of insert, update, or delete will be specified, and a combination thereof could also be specified. When the message is received, the integration should update its copy of the state model:

  • For insert, the integration should insert into the specified s a row for each given and having the given s for the specified s.
  • For update , the integration should update existing rows for the given
  • s and the given s, modifying them to have the given s for the specified s.
  • For delete, the integration should delete rows in the given
  • s with the given s.

    Truncate model message

    B Series Appliance → Integration

    {
        "type" : "truncate_model"
    }\n
    

    After receiving this message, the integration should delete all rows from all tables.

    System state

    The system state model of the real-time state API functions similarly to a database.

    • It is composed of tables.
    • Each table has one or more fields.
    • Each table has zero or more rows.
    • Tables can relate to each other via rows that serve as functional keys.

    In the initial state of the model, no rows exist in any tables. The system state model is updated in real time to reflect system state transitions, such as when clients connect and support sessions begin. The system state model persists until the B Series Appliance's processes are started. After a process restart, the model is reset to its initial state. The time between the model's initial state and the next reset is referred to as the model's lifetime.

    Tables in the real-time system state model

    The section below details the tables that exist in the system state model, along with the fields that exist in each table.

    customer_client

    Stores all customer clients connected to the B Series Appliance. Note that a support_session can exist without a customer_client. This typically occurs when the customer closes the customer client and the representative does not immediately terminate the session in the representative console.

    (id)An integer that identifies this table row. This ID is unique for the lifetime of the model.
    "client_type":"[string]"The type of customer client running on the endpoint or communicating with the endpoint. May be one of desktop, mobile, rdp, vnc, vpro, shell, or web_chat.
    "created_timestamp":[timestamp]A UNIX timestamp (UTC) representing the time at which this table row was created.
    "elevated":[boolean]1: The customer client is running in an elevated context.
    0: The customer client is running in a user context.
    "hostname":"[string]"The endpoint's hostname.
    "operating_system":"[string]"The endpoint's operating system.
    "support_session_id":[integer]The foreign key that links this table row to other table rows that reference this session.

    Stores all customer clients connected to the B Series Appliance. Note that a support_session can exist without a customer_client. This typically occurs when the customer closes the customer client and the representative does not immediately terminate the session in the representative console.

    queue

    Stores all active support session queues. A queue is active if one or more of its members are logged in or if it is a persistent queue.

    (id)An integer that identifies this table row. This ID is unique for the lifetime of the model.
    "code_name":"[string]"The code name assigned to the support team. Personal queues do not have code names.
    "created_timestamp":[timestamp]A UNIX timestamp (UTC) representing the time at which this table row was created.
    "name":"[string]"The support team's display name.
    "support_team_id":"[string]"The support team's unique identifier. The ID is not shown for personal queues.
    "type":"[string]"The type of queue. May be one of prewait, team, or personal.

    representative

    Stores all representatives logged into a representative console

    (id)An integer that identifies this table row. This ID is unique for the lifetime of the model.
    "created_timestamp":[timestamp]A UNIX timestamp (UTC) representing the time at which this table row was created.
    "private_display_name":"[string]"The representative's private display name.
    "public_display_name":"[string]"The representative's public display name.
    "queue_id":[integer]The unique ID serving as the foreign key that links this table row with other table rows that reference this queue.
    "routing_available":[boolean]1: The representative is available to handle new support sessions.
    0: The representative is unavailable to handle new support sessions.
    "routing_busy":[boolean]1: The representative has reached their maximum number of support sessions for session assignment.
    0: The representative is not marked as busy.
    "routing_enabled":[boolean]1: The representative has automatic session assignment enabled.
    0: The representative has automatic session assignment disabled.
    "routing_status_id": integer]The User Status ID which uniquely identifies a User Status.
    "routing_idle":[boolean]1: The representative is idle.
    0: The representative is active.
    "skill_code_names":"[string]"A comma-separated list of skill code names assigned to this representative.
    "type":"[string]"The type of representative connection. May be one of normal or invited.
    "user_id":[integer]The representative's unique ID which identifies them in BeyondTrust.
    "username":"[string]"The username this representative uses to authenticate to BeyondTrust.

    representative_queue

    Stores logged-in representatives in relation to their support queues. While team leads and managers have access to their team members' personal queues, that access is not represented in this table.

    (id)An integer that identifies this table row. This ID is unique for the lifetime of the model.
    "created_timestamp":[timestamp]A UNIX timestamp (UTC) representing the time at which this table row was created.
    "queue_id":[integer]The unique ID serving as the foreign key that links this table row with other table rows that reference this queue.
    "representative_id":[integer]The unique ID serving as the foreign key that links this table row with other table rows that reference this representative.
    "role":[string]The user's role in the team. May be one of member, lead, or manager.

    representative_support_session

    Stores representatives participating in support sessions.

    (id)An integer that identifies this table row. This ID is unique for the lifetime of the model.
    "created_timestamp":[timestamp]A UNIX timestamp (UTC) representing the time at which this table row was created.
    "representative_id":[integer]The unique ID serving as the foreign key that links this table row with other table rows that reference this representative.
    "support_session_id":[integer]The foreign key that links this table row to other table rows that reference this session.

    support_session

    Stores all active support sessions.

    (id)An integer that identifies this table row. This ID is unique for the lifetime of the model.
    "created_timestamp":[timestamp]A UNIX timestamp (UTC) representing the time at which this table row was created.
    "customer_company":"[string]"The end user's company name.
    "customer_company_code":"[string]"The end user's company code.
    "customer_description":"[string]"The issue description as provided by the customer.
    "customer_name":"[string]"The end user's name.
    "estimated_pickup_timestamp":[timestamp]The time at which the system expects this session to be accepted or transferred. This could be a time in the past or future.
    "issue_code_name":"[string]"The code name of the issue selected by the customer on the front-end survey.
    "issue_description":"[string]"The description of the issue selected by the customer on the front-end survey.
    "lsid":"[string]"The logging session ID that uniquely identifies this session. This LSID can be used in /login > Reports, with the reporting API, in the integration client, etc.
    "primary_team_queue_id":[integer]The unique ID serving as the foreign key that links this table row with other table rows that reference this queue. This field stores the last team queue that owned the session before the session ended.
    "priority":[integer]The session's priority. May be one of 1 (high), 2 (medium), or 3 (low).
    "public_site_id":[integer]The ID of the public site associated with the session.
    "public_site_name":"[string]"The name of the public site associated with the session.
    "queue_entry_timestamp":[timestamp]The time at which this session entered its current queue.
    "queue_id":[integer]The unique ID serving as the foreign key that links this table row with other table rows that reference this queue. This field stores the queue in which the session currently resides.
    "start_method":"[string]"The method with which the session was started. May be one of session_key (the seven-digit code or the URL), rep_list, issue_submission, , local_jump, remote_jump, shell_jump, local_rdp, remote_rdp, local_vnc, remote_vnc, or vpro.

    support_session_attribute

    Stores custom session attributes assigned to active support sessions.

    (id)An integer that identifies this table row. This ID is unique for the lifetime of the model.
    "code_name":"[string]"The code name assigned to the support session attribute.
    "created_timestamp":[timestamp]A UNIX timestamp (UTC) representing the time at which this table row was created.
    "support_session_id":[integer]The foreign key that links this table row to other table rows that reference this session.
    "value":"[string]"The value of the attribute.

    support_session_skill

    Stores skills assigned to active support sessions.

    (id)An integer that identifies this table row. This ID is unique for the lifetime of the model.
    "code_name":"[string]"The code name assigned to the skill.
    "created_timestamp":[timestamp]A UNIX timestamp (UTC) representing the time at which this table row was created.
    "support_session_id":[integer]The foreign key that links this table row to other table rows that reference this session.

    presentation_session

    Stores data about the presentation sessions.

    (id)An integer that identifies this table row. This ID is unique for the lifetime of the model.
    "created_timestamp":[timestamp]A UNIX timestamp (UTC) representing the time at which this table row was created.
    "lsid":[guid]The logging session ID that uniquely identifies this session. This LSID can be used on /login > Reports and in the Reporting API, among other places.
    "presentation_name":"[string]"The name of the presentation.

    attendee

    Stores all information about attendees who have joined a presentation.

    "connected":[boolean]1: The presentation client connected to the B Series Appliance and joined the presentation.
    0: The presentation client did not connect to the B Series Appliance.
    "created_timestamp":[timestamp]A UNIX timestamp (UTC) representing the time at which this table row was created.
    "hostname":"[string]"The attendee's hostname.
    "name":"[string]"The name of the attendee.
    "operating_system":"[string]"The attendee's operating system.
    "presentation_session_id":[integer]The logging session ID that uniquely identifies this presentation.
    "private_ip":"[string]"The attendee computer's private IP address.
    "public_ip":"[string]"The attendee computer's public IP address.
    (id)An integer that identifies this table row. This ID is unique for the lifetime of the model.

    presenter

    Stores the information about the presenter who has started presenting their screen.

    (id)An integer that identifies this table row. This ID is unique for the lifetime of the model.
    "created_timestamp":[timestamp]A UNIX timestamp (UTC) representing the time at which this table row was created.
    "presentation_session_id":[integer]The logging session ID that uniquely identifies this presentation.
    "representative_id":[integer]The unique ID serving as the foreign key that links this table row with other table rows that reference this representative.

    JavaScript library

    This JavaScript library is provided to facilitate use of the socket-based real-time state API in a browser. It simplifies interactions with the API in the following ways:

    • Direct interaction with the WebSocket API is not necessary.
    • The connection, authentication, and table registration steps are handled for you using the credentials, B Series Appliance information, and table names you provide.
    • The server's JSON messages are buffered and decoded automatically.
    • Model changes are provided as JavaScript objects instead of JSON strings.
    • Several methods of subscribing to model changes are provided to help filter, transfer, and store the data more easily.

    Browser requirements

    This API will only work in browsers that support binary WebSockets.

    Usage

    To use this JavaScript library, your HTML page must reference the state.js file located on your BeyondTrust Appliance B Series.

    A script tag would look something like the example below, where "support.example.com" is your B Series Appliance's hostname:

    <script src="https://support.example.com/api/state.js"></script>
    

    This script tag must be included in your HTML page before any of your own code that uses the API.

    ℹ️

    Note

    Examples in this document use a simple script tag and the API’s global BomgarState() function. However, state.js also contains a universal module definition (UMD) wrapper that allows it to be used with various JavaScript module systems such as Browserify or RequireJS.

    JavaScript library basics

    For basic functionality of the JavaScript library for the real-time state API, only the following two functions are necessary.

    BomgarState(options)

    Make one call to the BomgarState() function to provide the API with connection and authentication information and with the tables you wish to receive updates for.

    Arguments

    • options ( Object )
      • bearerToken (String))
        • Required
      • host ( String )
        • Required.
      • port ( Integer )
        • Optional. Defaults to 443.
      • company ( String )
        • Required.
      • tables ( String | String[] )
        • Optional. Can be an array of table names or the string all. Defaults to all.

    ℹ️

    Note

    For more information on how to generate the bearerToken, please see Authenticate to the API.

    Returns

    • ( Observable<Object> ): An observable sequence of all model changes as objects, parsed from the original JSON.

    ℹ️

    Note

    You must call .subscribe() on this returned observable object to actually initiate the connection to the server. Calling BomgarState() merely defines the necessary connection and authentication parameters.

    Example

    //Provide the API with the connection and authentication parameters
    var allChangesObservable = BomgarState({
        host: 'companyname.support.example.com',
        port: 443, // Optional
        company: 'companyname',
        bearerToken: 'bearerToken',
        tables: ['customer_client', 'representative'] // Or 'all'
    });
    
    // This triggers the actual connection and authentication:
    var subscription = allChangesObservable.subscribe();
    

    .subscribe([onChange], [onError], [onCompleted])

    Calling the subscribe() method on the observable returned by BomgarState() initiates the connection and authentication with your B Series Appliance over a WebSocket. If successful, the API then sends your table list to the server. subscribe() also allows you to optionally register callback functions for model changes, errors, and connection closures.

    Arguments

    • onChange ( Function ): Function that the API invokes for every model change received from the server. The function should accept one argument: an object describing the model changes. The structure of this object is described in Model Updates.
    • onError ( Function ): Function that the API invokes 0 or 1 times if a fatal error occurs during connection, authentication, or table registration.
    • onCompleted ( Function ): Function that the API invokes once when the connection closes normally.

    Returns

    • ( Object ): Returns an object representing the subscription. If you need to programmatically close the connection, call .dispose() on this object.

    Detailed

    The following methods are available on the object returned by BomgarState() (the object called allChangesObservable in the example in JavaScript library basics). These methods allow you to subscribe to more focused changes based on table names and change types. Some developers may find these more convenient to use than registering a single callback function for all tables and change types using subscribe() as seen with the basic usage.

    .changesForTable(tableName)

    Calling this function subscribes only to model changes for the given table name.

    Arguments:

    • tableName ( String ): The name of a table you requested from BomgarState() using the tables option.

    Returns ( Observable<ChangeObject> ): An observable of changes to the given table. Change objects have "name" and "data"

    Properties:

    {
        "name": "<change type>", // "insert", "update", or "delete"
        "data": {
            "<id>": {
                // Varies depending on table and change type
                },
            // Other rows
        }
    }
    

    Example:

    // Get all the change messages for the 'representative' table in one subscription
    allChangesObservable.changesForTable('representative')
        .subscribe(function onRepTableChange(repChangeObject) {
            // 'repChangeObject.name' contains the change name 'insert', 'update', or 'delete'
            // 'repChangeObject.data' contains the data related to the change from 
               changeObject[change][tableName] int he '.subscribe()' example
            switch (repChangeObject.name) {
                case 'insert':
                    // Use 'repChangeObject.data' to perform an insert
                    break;
                case 'update':
                    // Use 'repChangeObject.data' to perform an update
                    break;
                case 'delete':
                    // Use 'repChangeObject.data' to perform a delete
                    break;
            }
        });
    

    .tableInserts(tableName)

    Calling this function subscribes only to inserts for the given table name.

    Arguments:

    • tableName ( String ): The name of a table you requested from BomgarState() using the tables option.

    Returns ( Observable<InsertObject> ): An observable of inserts into the given table. Insert objects contain one or more property names that are IDs for the table row and also contain property values that are objects containing the data to insert.

    {
        "<id>": {
            // Varies depending on table
        },
        // Other inserts
    }
    

    Example

    // Create a subscription for inserts into the 'customer_client' table
    allChangesObservable.tableInserts('customer_client')
        .subscribe(function onCustomerClientInsert(insertObject) {
            // Insert rows
        });
    

    .tableUpdates(tableName)

    Calling this function subscribes only to updates for the given table name.

    Arguments:

    • tableName ( String ): The name of a table you requested from BomgarState() using the tables option.

    Returns ( Observable<UpdateObject> ): An observable of updates for the given table. Update objects contain one or more property names that are IDs for the table row and also contain property values that are objects containing the data to update.

    {
        "<id>": {
            // Varies depending on table
        },
        // Other updates
    }
    

    Example

    // Create a subscription for updates to the 'customer_client' table
    allChangesObservable.tableUpdates('customer_client')
        .subscribe(function onCustomerClientUpdate(updateObject) {
            // Update rows
        });
    

    .tableDeletions(tableName)

    Calling this function subscribes only to deletions for the given table name.

    Arguments:

    • tableName ( String ): The name of a table you requested from BomgarState() using the tables option.

    Returns ( Observable<Deletion[]> ): An observable of deletions for the given table. Deletion objects are arrays of row IDs:

    [
        "<id>",
        // Other IDs
    ]
    

    Example

    // Createa subscription for deletions from the 'customer_client' table
    allChangesObservable.tableDeletions('customer_client')
        .subscribe(function onCustomerClientDeletion(deletionObject) {
            // Delete rows
        });
    

    .truncations()

    Calling this function subscribes to notifications that all tables should be truncated.

    Arguments:

    • None

    Returns ( Observable<TruncationObject> ): An observable of truncations. Truncation objects contain a property named type whose value is truncate_model.

    Advanced

    In addition to the methods documented here, every observable returned by this API exposes many other operator functions documented by the Reactive Extensions / RxJS (v4) project. If you wish to explore these other operators, you may find the resources on ReactiveX.io helpful. If you encounter sections that are language-specific, only the RxJS content is applicable.

    Working demonstration

    The code examples below are the source of a working demo that shows the JSON data emitted by the three types of subscriptions.

    index.html

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>BeyondTrust Real-Time State API Demo</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width">
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
    
    <div id="container">
        <div class="row">
            <section id="cust">
                <h2>Customer Messages
                    <span class="subtitle">.tableInserts('customer_client')tableUpdates('customer_client')tableDeletions('customer_client')
                </h2>
                <div class="messages"></div>
            </section>
            <section id="rep">
                <h2>Representative Messages
                    <span class="subtitle">.changesForTable('representative')</span>
                </h2>
                <div class="messages"></div>
            </section>
        </div>
        <div class="row">
            <section id="all">
                <h2>Raw Messages
                    <span class="subtitle">.subscribe(onNext, onError, onCompleted)</span>
                </h2>
                <div class="messages"></div>
            </section>
        </div>
    </div>
    
    <script src="https://[YOUR B Series Appliance HOSTNAME HERE]/api/state.js"></script>
    <script type="text/javascript">
        var repSection = document.getElementById('rep');
        var custSection = document.getElementById('cust');
        var allSection = document.getElementById('all');
    
        var messages$ = bomgarState({
            host: '[YOUR B Series Appliance HOSTNAME HERE]',
            port: 443,
            company: '[YOUR COMPANY NAME HERE]',
            username: '[YOUR USERNAME HERE]',
            password: '[YOUR PASSWORD HERE]',
            tables: 'all' // or an array like ['customer_client', 'representative']
        });
    
        var subscription = messages$.subscribe(
            function onNext(message) {
                appendMessage('Message:', message, allSection);
            },
            function onError(error) {
                appendMessage('An error occurred:', error, allSection, 'red');
                console.error('An error occurred: %s', error);
            },
            function onCompleted() {
                appendMessage('The End', 'Connection closed.', allSection, '#f50');
                console.warn('Connection closed. No more messages will be received.');
            }
        );
    
        messages$.changesForTable('representative')
            .subscribe(function onRepChange(message) {
                appendMessage('Change: ', message, repSection);
            });
    
        messages$.tableInserts('customer_client')
            .subscribe(function onCustInsert(message) {
                appendMessage('Insert: ', message, custSection, 'green');
            });
    
        messages$.tableUpdates('customer_client')
            .subscribe(function onCustUpdate(message) {
                appendMessage('Update: ', message, custSection, 'blue');
            });
    
        messages$.tableDeletions('customer_client')
            .subscribe(function onCustDeletion(message) {
                appendMessage('Deletion: ', message, custSection, 'red');
            });
    
        function appendMessage(label, message, section, color) {
            var messages = section.querySelector('.messages');
            var div = document.createElement('div');
            var hr = document.createElement('hr');
            var h4 = document.createElement('h4');
            h4.textContent = label;
    
            var p = document.createElement('p');
            p.style.color = color || 'black';
            p.textContent = JSON.stringify(message, null, '\t');
    
            div.appendChild(h4);
            div.appendChild(p);
            messages.appendChild(hr);
            messages.appendChild(div);
            setTimeout(function() {
                messages.scrollTop = messages.scrollHeight;
            }, 250);
    }
    </script>
    </body>
    </html>
    

©2003-2025 BeyondTrust Corporation. All Rights Reserved. Other trademarks identified on this page are owned by their respective owners. BeyondTrust is not a chartered bank or trust company, or depository institution. It is not authorized to accept deposits or trust accounts and is not licensed or regulated by any state or federal banking authority.