Skip to main content
In this guide, we will create a simple web chat application from scratch using HTML, CSS, and Vanilla JavaScript to integrate with the Customer Chat Web API. This tutorial will help you understand what each piece of the code does and how it all fits together to enable a customer to chat with a live agent.

When to use the Customer Chat API

Use the Customer Chat API to build a custom chat experience for customers or integrate Text’s messaging into your own application. Instead of using the standard chat widget, the Customer Chat API lets you send messages as a customer via API calls. In short, the Customer Chat API is the right tool if you need to programmatically act on behalf of a customer in a chat (sending messages, retrieving chat history). By contrast, you would use the Agent Chat API if you needed to send messages as an agent or bot. In this guide, we’ll focus on the customer side.

What this guide covers

  • Using the Customer Authorization flow to obtain a customer access token using the simple Cookie grant method (ideal for web apps).
  • Using the Customer Chat Web API (v3.6) to start a chat and send messages as the customer.
  • Setting up the required OAuth client configuration.
  • Writing the complete code for a sample application and instructions for running it locally with Node and npm.
  • Show how the agent will receive the messages in Text.

Prerequisites

Before you begin, make sure you have the following:

Prepare an OAuth client

Before coding, create an OAuth client and copy your credentials. Go to Settings → API access → OAuth clients and create a new OAuth client. Configure:
  • Display name - a name to identify your OAuth client
  • Redirect URI whitelist - set this to http://localhost:8080 (where the sample app runs)
  • Client type - select Server-side app
After creating, Text shows your Client ID and Secret key once — copy the Client ID. You’ll also need your organization ID, which you can retrieve by calling list-organizations. For our purposes (Customer Chat API), you typically do not need to configure any custom scopes. The customer token generated via the cookie grant flow will implicitly allow chat actions for that customer on your license. For our purposes (Customer Chat API), you typically do not need to configure any custom scopes in the app. The customer’s token we’ll generate will implicitly allow chat actions for that customer on your license. Agent-side scopes like chats--all:rw or customers:own are not needed here because we are not using an agent token to impersonate customers — we will obtain a direct customer token via the cookie grant flow.

Create a local project

Now you can set up your development environment:
  1. Clone the sample app repository: Open and clone the sample app repository available on GitHub.
  2. Initialize npm: Open a terminal in the project folder and run the following command:
For the app to work properly (and handle the authentication cookies), we set up a local web server in the repository (rather than opening the HTML file directly). We use a http-server tool to serve our files on http://localhost:8080. We have already added an npm script in the package.json to automatically start the server with npm start:
You can also use other static server tools or even the Live Server extension in VSCode — any method to serve the HTML on a local URL is fine.

Index file

You will find the index.html code in the sample app repository. The index file contains CSS styles, a simple chat widget with operational buttons, and a script tag to load the app.js application file.

Application logic

The file app.js contains all the JavaScript needed to connect our front-end chat interface with the Customer Chat API. We’ll explain each major function in the script, which corresponds to different features of the chat app. Overview of the script: At the top of the file, we set up some configuration and state variables:
  • clientId and organizationId, set to the values you copied when setting up your OAuth client (replace the placeholders with your actual IDs).
  • We then declare variables like:
    • customerToken and customerId (to store the authenticated customer’s token and ID),
    • currentChatId and currentThreadId (to track the ongoing chat session),
    • isChatActive (a boolean flag),
    • helpers like lastEventIds (a Set to keep track of message IDs we’ve already displayed) and pollingIntervalId (for managing the polling timer).
Now, let’s go through the core functions one by one:

Customer authorization

Before our customer can start chatting, we need to authorize them and get an access token. The authorizeCustomer() function handles this:
Here’s what this does:
  1. It prepares a payload for the authentication request, including our clientId, organizationId, and specifying grant_type: "cookie". We use the customer authorization API endpoint (/v2/customer/token) to check if the user has a valid login session (cookie) and issue an access token for the Customer Chat API. The redirect_uri is included as part of the OAuth flow (though in our case, with grant_type: "cookie", no actual redirection happens; it just uses the cookie).
  2. We then make a POST request to the accounts service with credentials: "include", meaning the browser will include authentication cookies for the accounts domain. Note that this requires that you are logged in to your Text account in your browser. If you are not logged in, this request will fail, so make sure you have an active session by logging into your account in the same browser before running the app.
  3. If the response is successful, we parse the JSON data. It contains an access_token (which we store in customerToken), and an entity_id (the customer’s ID stored in customerId).
  4. We log a confirmation to the console (“customer authorized”) with the ID for debugging purposes.
Then we call listChats() to see if this customer already has an existing chat open. The listChats() function calls the API to get a list of chats for this customer. It looks at the first chat (if any) to determine if the customer has an existing chat.
Important: Our APIs differentiate between chats and threads. A specific customer can have only one active chat started on a specific license, and this chat contains threads. The chats in Text are continuous — when a chat is created, it can then be deactivated and resumed. Resuming a chat starts a new thread inside this chat, and deactivating a chat closes the currently open thread.
If an open or pending chat exists, our listChats function will set currentChatId to that chat’s ID, currentThreadId to the last thread in that chat, and isChatActive to whether that chat is still active. This way, we can reattach to an ongoing conversation if the user refreshes the page or comes back later. If no chat is found, it means the customer has no active conversation, and we initialize our state to none (and disable the End chat button via updateCloseButtonState() accordingly). Finally, if we did get a customerToken, we start the polling mechanism by calling startPolling() (we’ll explain polling soon). This means as soon as the user is authorized, our app will begin checking for any incoming messages in case there’s an active chat or if one gets started. In summary, authorizeCustomer() runs automatically when the app loads (we attach it to run on DOMContentLoaded). It silently authenticates the user using their login cookie and prepares the app to either resume an existing chat or start a new one. If authorization fails (for example, if the user is not logged in), an error will be logged. In a production-ready app, you could handle this by prompting the user to log in.

Start or resume chat

Once the user is authorized, they can send a message. We have two functions to manage chat sessions: startChat() for beginning a brand new chat if the customer has none, and resumeChat() for reopening a chat that exists but has ended. Starting a new chat: The startChat(initialMessage) function is called when the customer sends a message and currentChatId is not set (meaning no chat is in progress yet). It will create a new chat session with the first message:
Resuming an existing chat: The resumeChat() function is used if currentChatId exists but isChatActive is false (meaning the chat has ended, but we still have its ID).

Send message

The sendMessage(messageText) function is invoked whenever the user sends a message. This function decides whether to start a new chat, resume an old chat, or simply send the message on an active chat.

Deactivate chat

The deactivateChat() function is triggered when the user clicks the End chat button.

Poll for messages

Because we’re building a client outside of the standard chat widget, we use polling — periodically checking the server for new events in the chat every 3 seconds.

Putting it all together

At the bottom of app.js, event listeners are wired up on page load:

Running the app

  1. Start your local server: Navigate to your project folder and run npm start. The terminal will show a local URL, for example http://localhost:8080.
  2. Open the app in your browser: Visit the local URL. You should see the styled chat window.
  3. Log in if needed: Ensure you are logged into your Text account in the same browser. The app uses cookie-based authorization.
  4. Send a message: Type a greeting and press Enter or click Send. Your message should appear in the chat area immediately.
  5. See agent’s response: If an agent replies, their message will appear after up to 3 seconds (the polling interval).
  6. End the chat: Click the End chat button. A system message “The chat has been closed.” will appear and polling will stop.
  7. Try resuming: Sending another message after ending the chat will automatically call resumeChat() to reopen the conversation.

From here, you can expand this basic app in many ways — better error handling, a login prompt if cookie auth fails, real-time (RTM) API instead of polling, or rich messages.