Customer SDK

4 minutes reading time • last modified 12/05/2019

Introduction

LiveChat Customer JS SDK is a set of tools to build a custom chat widget. It allows you to manage multiple chats via LiveChat as a customer using JavaScript.

Is it for me?

If you need to customize the LiveChat widget, using LiveChat Customer JS SDK is one of the options to do this. If you need a fully custom solution and you feel brave, dive into LiveChat Customer JS SDK: we provide methods and events for deep integration with the LiveChat environment.

Keep in mind, however, that interacting with this API requires some development skills.

About LiveChat Customer JS SDK

We provide an asynchronous API, where most methods interacting with a server return promises. To get the promise's fulfillment result, subscribe your handler to the promise's then() method. You can also subscribe to the emitted events with on and off methods.

Not familiar with promises? Read this article to learn more.

We authenticate your sessions by using customer-auth package and expose the created auth object on the returned SDK's instance. In general, you don't have to worry about it nor use the exposed object, but if you need to get the authentication token you can get it through the SDK like this:

customerSDK.auth.getToken().then(token => console.log(token));

Examples

We have prepared a sample chat widget implementation to present the features of LiveChat Customer JS SDK:

How to start

This tutorial will help you get started with using LiveChat Customer JS SDK.

Create an application

First, you need to create an application in the Developers Console (select the Web app (frontend, eg. JavaScript) type).

Install Customer JS SDK

You can use LiveChat Customer JS SDK in two different ways:

Using npm

npm install --save @livechat/customer-sdk

Now, you can import SDK in your code:

import * as CustomerSDK from '@livechat/customer-sdk'

or with a node-style require call:

const CustomerSDK = require('@livechat/customer-sdk')

Using script tag - UMD module hosted on unpkg's CDN

<script src="https://unpkg.com/@livechat/customer-sdk"></script>

If you just want to look around and play with the SDK, check out our sample chat widget implementation.

For the time being you need to register your application in the Developers Console as a "Web app (frontend, eg. JavaScript)" type. Then, you have to pass the configured redirectUri to the init, along with the regular required properties (license and clientId).

Use the API

Now run the init function with the configuration, replacing LICENSE_NUMBER with your LiveChat license number. The function will return the customerSDK instance:

const customerSDK = CustomerSDK.init({
  license: LICENSE_NUMBER,
  clientId: CLIENT_ID
});

With customerSDK, you can attach events:

customerSDK.on("new_event", newEvent => {
  console.log(newEvent);
});

or execute methods:

const chatId = "OU0V0P0OWT";
customerSDK
  .sendEvent(chatId, {
    type: "message",
    text: "Hi!"
  })
  .then(response => {
    console.log(response);
  })
  .catch(error => {
    console.log(error);
  });

Using the API in React Native

If you want to use LiveChat Customer SDK in React Native, keep in mind that we use cookies to authenticate your sessions, so we need some sort of browser environment for that. We've prepared a special wrapper for you to use in React Native, which opens a WebView component to get an authentication token. All you have to do is to import it from our authentication package (no need to install it - the SDK depends on it, so you have it already) and mount it in your React Native application:

import { AuthWebView } from "@livechat/customer-auth";
import { init } from "@livechat/customer-sdk";

export default class App extends React.Component {
  componentDidMount() {
    const customerSDK = init({
      license: LICENSE_NUMBER,
      clientId: CLIENT_ID,
      redirectUri: REDIRECT_URI
    });
    // you can start using customerSDK from now
  }

  render() {
    return (
      <View>
        <AuthWebView />
      </View>
    );
  }
}

Key Concepts

The LiveChat system is based on four basic types of entities - users, chats, events and threads.

  • chats consist of threads and threads consist of events
  • threads are parts of chats,
  • users can add events to chats, which then are automatically added to threads
  • users can participate in many chats at the same time

Threads are a vital part of LiveChat architecture. They provide continuous chat experience (i.e. they never end and you can always add to them) and they group events in smaller logical chunks, e.g. for reporting and caching purposes. However, threads tend to complicate handling various operations like loading more history events. The good part is that you don't have to worry about them most of the time and this SDK is doing the heavy lifting behind the scenes for you. You will get notified about threads' metadata only if you explicitly ask for it - most SDK methods expect only chat IDs.

User

{
	id: 'ed9d4095-45d6-428d-5093-f8ec7f1f81b9',
	type: 'agent',
	name: 'Jane Doe',
	avatar: 'https://livechat-static.com/assets/images/livechat_logo.ae4271fe1a0a2db838dcf075388ee844.png',
}

Chat

{
	id: 'OU0V0P0OWT',
	users: [{
		id: 'ed9d4095-45d6-428d-5093-f8ec7f1f81b9',
		// ... more user properties
	}],
	lastSeenTimestamps: {
		'ed9d4095-45d6-428d-5093-f8ec7f1f81b9': 1503062591000, // might be null
	},
	threads: ['OU0V0U3IMN'],
}

Event

{
	type: 'message',
	text: 'hi!',
    author: 'ed9d4095-45d6-428d-5093-f8ec7f1f81b9', // assigned by server
	id: 'OU0V0U3IMN_1', // assigned by server
    timestamp: 1503062591000, // assigned by server
    customnId: '814.3316641404942', // optional
    thread: 'OU0V4R0OXP',
	properties: {},
}

Threads

{
	id: 'OU0V0U3IMN',
	active: true,
	order: 3,
	users: ['ed9d4095-45d6-428d-5093-f8ec7f1f81b9'],
	lastSeenTimestamps: {
		'ed9d4095-45d6-428d-5093-f8ec7f1f81b9': 1503062591000, // might be null
	},
	events: [ /* events */ ],
}

Methods

closeThread

customerSDK
  .closeThread("ON0X0R0L67")
  .then(response => {
    console.log(response);
  })
  .catch(error => {
    console.log(error);
  });

Arguments:

argumentstypedescription
chatstringChat's id in which thread should get closed

Returned value:

shapetype
successboolean

destroy

This method clears any held resources, removes all listeners and disconnects from the network. After using this method you won't be able to use the destroyed SDK's instance.

customerSDK.destroy();

disconnect

customerSDK.disconnect();

getChatHistory

This method facilitates loading more history events. You need to get access to the history object for certain chat by calling this method. The returned history object has only one method, next, which gives you a promise of { done, value } pair.

  • done - indicates if there is anything more to load
  • value - it's an array of loaded events

You can keep calling history.next() multiple times to load more and more history events (useful for infinite scroll feature). Keep in mind, though, that you generally shouldn't call next while the history is being loaded - we queue those requests so the previous one must resolve before we proceed with the next one.

Such structure like our history object is called an async iterator.

let wholeChatHistoryLoaded = false;

const history = customerSDK.getChatHistory("OU0V0P0OWT");

history.next().then(result => {
  if (result.done) {
    wholeChatHistoryLoaded = true;
  }

  const events = result.value;
  console.log(events);
});

Arguments:

argumentstypedescription
chatstringChat's id for which history object should be returned

getChatsSummary

customerSDK
  .getChatsSummary({
    offset: 0,
    limit: 10
  })
  .then(({ chatsSummary, totalChats }) => {
    console.log(chatsSummary);
    console.log(totalChats);
  })
  .catch(error => {
    console.log(error);
  });

Arguments:

argumentsshapetypedefaultmaxdescription
pagination
offsetnumber0
limitnumber1025

Returned value:

shapetypeshapetypedescription
chatsSummaryobject[]
idChat's id
activeboolean
usersstring[]Users' ids
lastEventobjectEvent
lastEventsPerTypeobjectMap from event types to event objects
lastSeenTimestampsobjectMap from Users' ids to optional lastSeenTimestamps
lastThreadstringThread's id
totalChatsnumber

getChatThreads

In most cases you do not need to use this method directly. If you want to load more events, consider using getChatHistory.

const threads = ["OS0C0W0Z1B", "OS0I0M0J0G", "OT01080705", "OT0E02082U", "OT0E08040G"]
customerSDK.getChatThreads("ON0X0R0L67", threads)
    .then(threads => {
        console.log(threads)
    })
    .catch(error => {
        console.log(error)
    })

Arguments:

argumentsshapetype
chatstring
threadspagestring[]

Returned value:

array of shapestypedescription
idstringThread's id
chatstringChat's id
activestring[]Active state
ordernumberorder (can be used for sorting)
usersstring[]Users' ids
eventsobject[]Events

getChatThreadsSummary

In most cases you do not need to use this method directly. If you want to load more events, consider using getChatHistory.

customerSDK
  .getChatThreadsSummary("ON0X0R0L67", {
    offset: 0,
    limit: 10
  })
  .then(summary => {
    console.log(summary.threadsSummary);
    console.log(summary.totalThreads);
  })
  .catch(error => {
    console.log(error);
  });

Arguments:

argumentsshapetypedefaultmaxdescription
chatstring
pagination
offsetnumber0
limitnumber251000

Returned value:

shapetypeshapetype
threadsSummaryobject[]
idstring
ordernumber
totalEventsnumber
totalThreadsnumber

getPredictedAgent

customerSDK
  .getPredictedAgent()
  .then(agent => {
    console.log(agent);
  })
  .catch(error => {
    console.log(error);
  });

off

This method unsubscribes from emitted events which are described here.

on

This method subscribes to emitted events which are described here.

once

This method subscribes to emitted events which are described here and unsubscribes immediately after the callback gets called.

rateChat

customerSDK
  .rateChat("ON0X0R0L67", {
    value: "good",
    comment: "Agent helped me a lot!"
  })
  .then(rating => {
    console.log(rating);
  })
  .catch(error => {
    console.log(error);
  });

Arguments:

argumentsshapetypedescription
chatDestination chat's id
rating
score0 or 1Rating value
commentstringOptional comment

Returned value:

shapetype
idstringCreated event's id

sendEvent

const event = {
  type: "message"
  // ... other properties specific for the event's type
};

customerSDK
  .sendEvent("ON0X0R0L67", event)
  .then(event => {
    console.log(event);
  })
  .catch(error => {
    console.log(error);
  });

Arguments:

argumentsshapetypedescription
chatstringDestination chat's id
event
typestringType of the event
...Other properties
meta
attachToLastThreadbooleanoptional

sendFile

This method is a little bit special - it returns regular then/catch methods of a Promise and a cancel method which you can use to abort the upload of the file. It also lets you pass onProgress callback function. Keep in mind that the maximum accepted file size is 10 MB.

customerSDK
  .sendFile(
    "ON0X0R0L67",
    {
      file,
      customId // optional
    },
    {
      onProgress: progress => console.log(`upload progress: ${progress}`)
    }
  )
  .then(response => {
    console.log(response.url);
  })
  .catch(error => {
    console.log(error);
  });

Arguments:

argumentsshapetypedescription
chatstringDestination chat's id
data
fileBlob
customIdstringOptional customId for the event
spec
onProgressfunctionThis callback function will receive a progress value - number between 0 and 1

Returned value:

shapetype
urlstring

In React Native instead of passing a blob you need to pass an object of such shape:

const file = {
  uri: uriFromCameraRoll,
  type: "image/jpeg", // optional
  name: "photo.jpg" // optional
};

sendPostback

customerSDK
  .sendPostback("ON0X0R0L67", "OS0C0W0Z1B", "OS0C0W0Z1B01", {
    id: "OS0C0W0Z1B01002",
    toggled: true
  })
  .then(rating => {
    console.log("success");
  })
  .catch(error => {
    console.log(error);
  });

Arguments:

argumentsshapetypedescription
chatstringPostback chat's id
threadstringPostback thread's id
eventstringPostback event's id
postback
idstringPostback button's id
toggledbooleanPostback toggled (default true)

setSneakPeek

This method doesn't return a promise. It just sets the internal sneak peek value. It will be sent to the server only if the target chat is active and only once per 2 seconds (it's throttled).

customerSDK.setSneakPeek("ON0X0R0L67", "what is the price for your ");

Arguments:

argumentstypedescription
chatstringDestination chat id
textstringMessage preview broadcasted to the agent

startChat

customerSDK
  .startChat({
    events: []
  })
  .then(chat => {
    console.log(chat);
  })
  .catch(error => {
    console.log(error);
  });

Arguments:

argumentsshapetypedescription
specificationFor advanced usage
scope
eventsevents[]

updateChatProperties

const properties = {
  property_namespace: {
    sample: "property"
  }
};
customerSDK
  .updateChatProperties("ON0X0R0L67", properties)
  .then(response => {
    console.log(response);
  })
  .catch(error => {
    console.log(error);
  });

Arguments:

argumentsshapetypedescription
chatstring
properties

updateChatThreadProperties

const properties = {
  property_namespace: {
    sample: "property"
  }
};
customerSDK
  .updateChatThreadProperties("ON0X0R0L67", "OS0C0W0Z1B", properties)
  .then(response => {
    console.log(response);
  })
  .catch(error => {
    console.log(error);
  });

Arguments:

argumentsshapetypedescription
chatstring
threadstring
properties

updateCustomer

const properties = {
  name: "John Doe",
  email: "john.doe@example.com",
  fields: {
    custom_property: "BasketValue=10usd",
    any_key_is_ok: "sample custom field"
  }
};
customerSDK.updateCustomer(properties);

Arguments:

argumentsshapetypedescription
properties
namestringOptional name
emailstringOptional email
fieldsobjectOptionl custom fields with string values

updateCustomerPage

customerSDK.updateCustomerPage({
  url: "https://developers.livechatinc.com/",
  title: "LiveChat for Developers"
});

Arguments:

argumentsshapetypedescription
page
urlstring
titlestring

updateLastSeenTimestamp

customerSDK
  .updateLastSeenTimestamp("ON0X0R0L67", 1500646701447)
  .then(response => {
    console.log(response);
  })
  .catch(error => {
    console.log(error);
  });

Arguments:

argumentstypedescription
chatstring
timestampnumberoptional

Returned value:

shapetype
timestampnumber

$$observable

You can consume all emitted events as a stream with most of stream libraries like in example RxJS. We provide an interop point for this, so you can easily convert our SDK to a stream like this:

import Rx from "@reactivex/rxjs";

Rx.Observable.from(visitorSDK).subscribe(([eventName, eventData]) => {
  console.log(eventName, eventData);
});

Events

You can listen for emitted events by subscribing to them (using on method) with your custom JavaScript function. For example, your function can be executed every time a message has been received.

connected

customerSDK.on("connected", payload => {
  console.log("connected");
  console.log(payload.chatsSummary);
  console.log(payload.totalChats);
});

Payload:

shapetypeshapetypedescription
chatsSummaryobject[]
idChat's id
activeboolean
usersstring[]Users' ids
lastEventobjectEvent
lastEventsPerTypeobjectMap from event types to event objects
lastSeenTimestampsobjectMap from Users' ids to optional lastSeenTimestamps
lastThreadstringThread's id
totalChatsnumber

connection_lost

customerSDK.on("connection_lost", () => {
  console.log("connection_lost");
});

This event doesn't carry any additional payload.

connection_restored

customerSDK.on("connection_restored", payload => {
  console.log("connection_restored");
  console.log(payload.chatsSummary);
  console.log(payload.totalChats);
});

Payload:

shapetypeshapetypedescription
chatsSummaryobject[]
idChat's id
activeboolean
usersstring[]Users' ids
lastEventobjectEvent
lastEventsPerTypeobjectMap from event types to event objects
lastSeenTimestampsobjectMap from Users' ids to optional lastSeenTimestamps
lastThreadstringThread's id
totalChatsnumber

customer_id

customerSDK.on("customer_id", id => {
  console.log("customer id is", id);
});

Payload:

argumenttype
idstring

disconnected

customerSDK.on("disconnected", reason => {
  console.log(reason);
});

Payload:

argumenttypedescription
reasonstringOptional

chat_properties_updated

customerSDK.on("chat_properties_updated", payload => {
  console.log(payload.chat);
  console.log(payload.properties);
});

Payload:

shapetypedescription
chatstringChat's id
propertiesobjectChat properties

chat_thread_properties_updated

customerSDK.on("chat_thread_properties_updated", payload => {
  console.log(payload.chat);
  console.log(payload.thread);
  console.log(payload.properties);
});

Payload:

shapetypedescription
chatstringChat's id
threadstringThread's id
propertiesobjectThread properties

last_seen_timestamp_updated

customerSDK.on("last_seen_timestamp_updated", payload => {
  console.log(payload.chat);
  console.log(payload.user);
  console.log(payload.timestamp);
});

Payload:

shapetypedescription
chatstringChat's id
userstringUser's id
timestampnumber

new_event

You should distinguish received events by their types.

customerSDK.on("new_event", ({ chat, event }) => {
  switch (event.type) {
    case "message":
      console.log("new message - ", event.text);
      break;
    default:
      break;
  }
});

Payload:

shapetypedescription
typestringEvent's type
...Other properties

user_data

customerSDK.on("user_data", user => {
  console.log(user);
});

User:

shapetypedescription

user_joined_chat

customerSDK.on("user_joined_chat", ({ user, chat }) => {
  console.log({ user, chat });
});

Payload:

shapetypedescription
userstringUser's ID
chatstringChat's ID

user_left_chat

customerSDK.on("user_left_chat", ({ user, chat }) => {
  console.log({ user, chat });
});

Payload:

shapetypedescription
userstringUser's ID
chatstringChat's ID

user_is_typing

customerSDK.on("user_is_typing", payload => {
  console.log(
    "user with " + payload.user + " id is writing something in " + payload.chat
  );
});

Payload:

shapetypedescription
chatstringChat's id
userstringUser's id

user_stopped_typing

customerSDK.on("user_stopped_typing", payload => {
  console.log(
    "user with " + payload.user + " id stopped writing in " + payload.chat
  );
});

Payload:

shapetypedescription
chatstringChat's id
userstringUser's id

thread_closed

customerSDK.on("thread_closed", ({ chat }) => {
  console.log(chat);
});

Payload:

shapetypedescription
chatstringChat's id

thread_summary

customerSDK.on("thread_summary", summary => {
  console.log(summary);
});

Payload:

shapetypedescription
idstring
chatstringChat's ID
ordernumber
totalEventsnumber

Changelog

[v2.0.0-alpha.0] - 2018-08-17

Initial alpha release.