# Signaling Quickstart (/en/realtime-media/rtm/quickstart/web)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

Use Signaling SDK to add low-latency, high-concurrency signaling and synchronization capabilities to your app.

Signaling also helps you enhance the user experience in Video Calling, Voice Calling, Interactive Live Streaming, and Broadcast Streaming applications.

This page shows you how to use the Signaling SDK to rapidly build a simple application that sends and receives messages. It shows you how to integrate the Signaling SDK in your project and implement pub/sub messaging through [Message channels](/en/realtime-media/rtm/build/work-with-channels/message-channel). To get started with stream channels, follow this guide to create a basic Signaling app and then refer to the [Stream channels](/en/realtime-media/rtm/build/work-with-channels/stream-channel) guide.

      
  
      
  
      
    ## Understand the tech [#understand-the-tech-2]

    To use Signaling features in your app, you initialize a Signaling client instance and add event listeners. To connect to Signaling, you login using an authentication token. To send a message to a message channel, you publish the message. Signaling creates a channel when a user subscribes to it. To receive messages other users publish to a channel, your app listens for events.

    To create a pub/sub session for Signaling, implement the following steps in your app:

    <Accordions>
      <Accordion title="Signaling workflow">
        ![Signaling workflow for Web](https://assets-docs.agora.io/images/signaling/get-started-workflow-javascript.svg)
      </Accordion>
    </Accordions>

    ## Prerequisites [#prerequisites-2]

    To implement the code presented on this page you need to have:

    * An Agora [account](/en/realtime-media/rtm/manage-agora-account) and [project](/en/realtime-media/rtm/manage-agora-account).

    * [Enabled Signaling](/en/realtime-media/rtm/manage-agora-account) in Agora Console

    * A [supported browser](/en/realtime-media/rtm/reference/supported-platforms).

    * A JavaScript package manager such as [npm](https://www.npmjs.com/package/npm).

    * Ensure that a firewall is not blocking your network communication.

    <CalloutContainer type="info">
      <CalloutDescription>
        Signaling 2.x is an enhanced version compared to 1.x with a wide range of new features. It follows a new pricing structure. See [Pricing](/en/realtime-media/rtm/reference/pricing) for details.
      </CalloutDescription>
    </CalloutContainer>

    ## Project setup [#project-setup-2]

    ### Create a project [#create-a-project-2]

    To create a folder for your project, and an `index.html` file for your app, execute the following commands in the terminal:

    ```bash
    mkdir HelloWorld
    cd HelloWorld
    touch index.html
    ```

    ### Integrate the SDK [#integrate-the-sdk-2]

    Use either of the following methods to integrate Signaling SDK into your project.

    **Using CDN**

    1. [Download](/en/api-reference/sdks?product=signaling\&platform=web) the latest version of Signaling SDK for Web.

    2. Add the following code to your project to reference the SDK:

       ```js
       <script src="path_to_sdk/agora-rtm.x.y.z.min.js"></script>
       ```

       Replace `x.y.z` with the specific SDK version number, such as 2.2.0. To get the latest version number, check the [Release notes](/en/realtime-media/rtm/reference/release-notes).

    **Using npm**

    1. Install the SDK using npm

       ```js
       npm install agora-rtm-sdk
       ```

    2. Import `AgoraRTM` from the SDK

       ```js
       import AgoraRTM from 'agora-rtm-sdk';
       ```

    ## Implement Signaling [#implement-signaling-2]

    A complete code sample that implements the basic features of Signaling is presented here for your reference. To use the sample code, copy the following lines into the `index.html` file:

    **Complete sample code for Signaling**

    ```js
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Text Display App</title>
      <style>
        #container {
          width: 800px;
          margin: 0 auto;
          padding: 20px;
        }
        #textDisplay {
          width: 100%;
          height: 800px;
          border: 1px solid #b0b0b0;
          margin-bottom: 20px;
          overflow: auto;
          text-align: left;
          padding: 10px;
          box-sizing: border-box;
        }
        #inputContainer {
          display: flex;
          align-items: center;
        }
        #textInput {
          width: calc(100% - 100px);
          padding: 5px;
          margin-right: 10px;
        }
        #submitButton {
          width: 90px;
          padding: 5px;
        }
      </style>
      <script src="./AgoraRTM-production.js"></script>
    </head>
    <body>
      <script>
        const { RTM } = AgoraRTM;
        // Fill in the App ID of your project.
        const appId = "your_appId";
        // Fill in your user ID.
        const userId = "your_userId";
        // Fill in your channel name.
        const msChannelName = "Chat_room";
        const buttonClick = () => {
          var input = document.getElementById("textInput");
          publishMessage(input.value);
          input.value = '';
        }
        const setupRTM = async () => {
          // Initialize the RTM client.
          try {
            rtm = new RTM(appId, userId);
          } catch (status) {
            console.log("Error");
            console.log(status);
          }
          // Add the event listener.
          // Message event handler.
          rtm.addEventListener("message", event => {
            showMessage(event.publisher, event.message);
          });
          // Presence event handler.
          rtm.addEventListener("presence", event => {
            if (event.eventType === "SNAPSHOT") {
              showMessage("INFO", "I Join");
            }
            else {
              showMessage("INFO", event.publisher + " is " + event.eventType);
            }
          });
          // Connection state changed event handler.
          rtm.addEventListener("status", event => {
            // The current connection state.
            const currentState = event.state;
            // The reason why the connection state changes.
            const changeReason = event.reason;
            showMessage("INFO", JSON.stringify(event));
          });
          // Log in the RTM server.
          try {
            const result = await rtm.login({  token: 'your_token' });
            console.log(result);
          } catch (status) {
            console.log(status);
          }
          // Subscribe to a channel.
          try {
            const result = await rtm.subscribe(msChannelName);
            console.log(result);
          } catch (status) {
            console.log(status);
          }
        }
        const publishMessage = async (message) => {
          const payload = { type: "text", message: message };
          const publishMessage = JSON.stringify(payload);
          const publishOptions = { channelType: 'MESSAGE'}
          try {
            const result = await rtm.publish(msChannelName, publishMessage, publishOptions);
            showMessage(userId, publishMessage);
            console.log(result);
          } catch (status) {
            console.log(status);
          }
        }
        const showMessage = (user, msg) => {
          // Get text from the text box.
          const inputText = textInput.value;
          const newText = document.createTextNode(user + ": " + msg);
          const newLine = document.createElement("br");
          textDisplay.appendChild(newText);
          textDisplay.appendChild(newLine);
        }
        window.onload = setupRTM;
      </script>
      <div id="container">
        <h1>Hello RTM !</h1>
        <div>
          <div id="textDisplay"></div>
        </div>
        <div id="inputContainer">
          <input type="text" id="textInput" placeholder="Enter text">
          <button id="submitButton" onclick="buttonClick()"> Send </button>
        </div>
      </div>
    </body>
    </html>
    ```

    Follow the implementation steps to understand the core API calls in the sample code or use the snippets in your own code.

    ### Initialize the Signaling engine [#initialize-the-signaling-engine-2]

    Before calling any other Signaling SDK API, initialize an `RTM` object instance.

    ```js
    try {
      const rtm = new RTM(appId, userId);
    } catch (status) {
      console.log("Error");
      console.log(status);
    }
    ```

    ### Add an event listener [#add-an-event-listener-2]

    The event listener enables you to implement the processing logic in response to Signaling events. Use the following code to handle event notifications or display received messages:

    ```js
    // Message event handler.
    rtm.addEventListener("message", event => {
      showMessage(event.publisher, event.message);
    });

    // Presence event handler.
    rtm.addEventListener("presence", event => {
      if (event.eventType === "SNAPSHOT") {
        showMessage("INFO", "I Join");
      }
      else {
        showMessage("INFO", event.publisher + " is " + event.eventType);
      }
    });

    // Connection state changed event handler.
    rtm.addEventListener("status", event => {
      // The current connection state.
      const currentState = event.state;
      // The reason why the connection state changes.
      const changeReason = event.reason;
      showMessage("INFO", JSON.stringify(event));
    });
    ```

    ### Log in to Signaling [#log-in-to-signaling-2]

    To connect to Signaling and access Signaling network resources, such as sending messages, and subscribing to channels, call `login`.

    During a login operation, the client attempts to establish a connection with Signaling. Once the connection is established, the client transmits heartbeat information to the Signaling server at fixed intervals to keep the client active until the client actively logs out or is disconnected. The connection is interrupted when timeout occurs. During this period, users may freely access the Signaling network resources subject to their own permissions and usage restrictions.

    ```js
    // Log in to Signaling
    try {
      const result = await rtm.login({ token: 'your_token' });
      console.log(result);
    } catch (status) {
      console.log(status);
    }
    ```

    Use the `login` return value, or listen to the `status` event notification to confirm that login is successful or obtain the error code and cause of login failure. When performing a login operation, the client's network connection state is `CONNECTING`. After a successful login, the state is updated to `CONNECTED`.

    <CalloutContainer type="info">
      <CalloutTitle>
        Best practice
      </CalloutTitle>

      <CalloutDescription>
        To continuously monitor the network connection state of the client, best practice is to continue to listen for `status` event notifications throughout the life cycle of the application. For further details, see [Event listeners](/en/realtime-media/rtm/build/send-and-receive-messages/add-event-listener).
      </CalloutDescription>
    </CalloutContainer>

    <CalloutContainer type="warning">
      <CalloutDescription>
        After a user successfully logs into Signaling, the application's PCU increases, which affects your billing data.
      </CalloutDescription>
    </CalloutContainer>

    ### Send a message [#send-a-message-1]

    To distribute a message to all subscribers of a message channel, call `publish`. The following code sends a string type message.

    ```js
    // Send a message to a channel
    const publishMessage = async (message) => {
      const payload = { type: "text", message: message };
      const publishMessage = JSON.stringify(payload);
      const publishOptions = { channelType: 'MESSAGE'}
      try {
        const result = await rtm.publish(msChannelName, publishMessage, publishOptions);
        showMessage(userId, publishMessage);
        console.log(result);
      } catch (status) {
        console.log(status);
      }
    }
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        Before calling `publish` to send a message, serialize the message payload as a string.
      </CalloutDescription>
    </CalloutContainer>

    ### Subscribe and unsubscribe [#subscribe-and-unsubscribe-2]

    To receive messages sent to a channel, call `subscribe` to subscribe to channel messages:

    ```js
    // Subscribe to a channel
    try {
      const result = await rtm.subscribe(msChannelName);
      console.log(result);
    } catch (status) {
      console.log(status);
    }
    ```

    When you no longer need to receive messages from a channel, call `unsubscribe` to unsubscribe from the channel:

    ```js
    // Unsubscribe from a channel
    try {
        const result = await rtm.unsubscribe(msChannelName);
        console.log(result);
    } catch (status) {
        console.log(status);
    }

    ```

    For more information about sending and receiving messages see [Message channels](/en/realtime-media/rtm/build/work-with-channels/message-channel) and [Stream channels](/en/realtime-media/rtm/build/work-with-channels/stream-channel).

    ### Log out of Signaling [#log-out-of-signaling-2]

    When a user no longer needs to use Signaling, call `logout`. Logging out means closing the connection between the client and Signaling. The user is automatically logged out or unsubscribed from all message and stream channels. Other users in the channel receive a `presence` notification of the user leaving the channel.

    ```js
    // Logout of Signaling
    try {
        const result = await rtm.logout();
    } catch (status) {
        const { operation, reason, errorCode } = status;
        console.log(`${operation} failed, the error code is ${errorCode}, because of: ${reason}.`);
    }
    ```

    ## Test Signaling [#test-signaling-2]

    Take the following steps to test the sample code:

    1. Use the [Token Builder](https://agora-token-generator-demo.vercel.app/) to generate a Signaling token:

       1. Select Signaling from the Agora products dropdown.
       2. Fill in your app ID and app certificate from [Agora Console](https://console.agora.io/v2). Leave the remaining fields blank.
       3. Click **Generate Token**.

    2. In `index.html`, replace `your_appid` and `your_token` values in the code with your project's app ID and the generated token.

    3. Update the value for the `userId` with an integer value.

    4. Save the file and run it in your browser.

    5. Make a copy of the project. Use the same `app_id` but a different `userId`. Launch another instance of the page in a separate browser tab.

    6. Type a message in the text input box in either instance and click the **Send** button. The message is displayed in the other app instance.

    7. Swap the sending and receiving instances and send another message.

       Congratulations! You have successfully integrated Signaling into your project.

    ## Reference [#reference-2]

    This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.

    ### Token authentication [#token-authentication-2]

    In this guide you retrieve a temporary token from a [Token Builder](https://agora-token-generator-demo.vercel.app/). To understand how to create an authentication server for development purposes, see [Secure authentication with tokens](./build/connect-and-authenticate/authentication-workflow).

    ### Sample project [#sample-project]

    Explore and experiment with the [Signaling Online demo](https://digitallysavvy.github.io/RTM2/). To download and customize the source code, refer to the [GitHub repository](https://github.com/digitallysavvy/RTM2).

    ### API reference [#api-reference-2]

    * [API reference](/en/api-reference/api-ref/signaling)
    * [Event listeners](./build/send-and-receive-messages/add-event-listener)

    
  
      
  
      
  
      
  
