Let’s say you have users on your website, mobile or desktop application and you want to provide them the opportunity to seek help or otherwise communicate with people you have designated as SaleMove Operators.

If you are running a website, then the simplest solution is to use the JavaScript API and disregard this guide completely, but if not or the JavaScript API does not fit your bill, then the SaleMove REST API might be what you are looking for.

Want to get straight to the point? Check out a sample ruby console application that uses all the REST API endpoints that are discussed in this guide.

Prerequisites

Before starting, make sure that you have received a developer API token from your contact at SaleMove and that you know the ID of your Site, you need them to perform HTTP requests.

Introduction

Given that you have a site configured for SaleMove, all users from all kinds of systems which have a way of performing HTTP requests can be integrated with SaleMove. All you need to do is to provide an user interface and hook it up to the SaleMove APIs. Engaging a Visitor (the user of your application) and a SaleMove Operator can be split into four steps:

  1. Find an available Operator
  2. Request an Engagement with the Operator
  3. Set up webhooks
  4. Chat with the Operator

The use case

Let’s do away with the abstract and continue with a concrete scenario. Say you have a mobile application which is used by a man called Vincent Vega and Vincent is in dire need to reach out to an expert who knows how to administer an adrenaline shot to the heart. Luckily, an expert called Jules Winnfield is logged in to SaleMove as an Operator and has the required expertise. If all goes well Vincent and Jules can chat and Vincent can resolve the situation in a satisfactory manner.

Vincent and Jules

Find an available Operator

Let’s put your developer API token and Site ID to use and find currently online Operators on your Site using the GET /operators endpoint:

curl --globoff --request GET \
    --header 'Authorization: Token $developer_api_token' \
    --header 'Accept: application/vnd.salemove.v1+json' \
    'https://api.salemove.com/operators?site_ids[]=$site_id&include_offline=false'

The response should look something like this:

 {
  "last_page": "https://api.salemove.com/operators?page=1",
  "operators": [
    {
      "href": "https://api.salemove.com/operators/b79b0256-82f0-4bc4-ab67-390fc0edc73e",
      "id": "b79b0256-82f0-4bc4-ab67-390fc0edc73e",
      "name": "Jules Winnfield",
      "email": "jules@wallace-boys.com",
      "available": true,
      "enabled": true,
      "picture": {
        "url": "http://wiki.tarantino.info/images/Jules.jpg"
      },
      "phone": "+15550123"
    }
  ]
}

If there are multiple Operators online and available, you could show a selection in your UI so that Vincent can select the Operator that he deems most capable.

If there are no Operators online and available, then an Engagement cannot be requested and Vincent needs to manage on his own, but let’s proceed assuming that Jules is ready to assist.

Request an Engagement with the Operator

A SaleMove Engagement is an interaction between an Operator and a Visitor. You have chosen Operator Jules for an Engagement, what about a Visitor? SaleMove has no idea who Vincent Vega is at this moment.

Jules talking to Vincent

The POST /engagement_requests endpoint allows you to request an Engagement by specifying the desired Visitor attributes, without a Visitor pre-existing in SaleMove. A Visitor is automatically created for the lifetime of the Engagement and you can use the Visitor authentication headers from the response to perform actions as would any Visitor who is registered with SaleMove (e.g Visitors using the JavaScript API). Let’s send such an Engagement Request to Operator Jules, specifying that it’s Vincent who is seeking assistance.

curl --request POST \
    --header 'Authorization: Token $developer_api_token' \
    --header "Accept: application/vnd.salemove.v1+json" \
    --header 'Content-Type: application/json' \
    --data-binary '{
      "media": "text",
      "operator_id": "b79b0256-82f0-4bc4-ab67-390fc0edc73e",
      "new_site_visitor": {
        "site_id": "6a78ce08-683b-49bf-9904-922c15410448", 
        "name": "Vincent Vega"
      },
      "webhooks":[
        {
          "url": "https://server.com/salemove/events/any",
          "method": "POST",
          "events": [
            "engagement.start", 
            "engagement.end", 
            "engagement.request.failure", 
            "engagement.chat.message"
          ]
        }
      ]
    }' \
  https://api.salemove.com/engagement_requests

Let’s investigate the parameters (which are also described in our documentation):

  • media: As there is no persistent connection for streaming audio/video, text is the only option.
  • operator_id: This is Jules’s ID that was received in the GET /operators response
  • new_site_visitor: The Site that the Visitor is coming from and the Visitor’s name are specified here. The name is optional.
  • webhooks: A list of webhooks that SaleMove will send events to, refer to the webhook documentation, but setting up webhooks is also discussed in the next section.

The response of the POST request looks like this:

{
  "id": "4106914f-7ed0-4f80-a4ee-3296ec43f50d",
  "timeout": 30,
  "site_id": "6a78ce08-683b-49bf-9904-922c15410448",
  "visitor_authentication": {
    "Authorization": "SessionId 6e84899d-8ae3-4b59-a482-7e7d2407c016",
    "X-Salemove-Visit-Session-Id": "1969"
  }
}

The timeout field in the response signifies how many seconds (counting from the moment that the server receives the request) Operator Jules has to accept the Engagement Request, before the Engagement Request automatically times out.

The visitor_authentication field contains all the headers that are required to authenticate as the newly created Visitor called Vincent. You can use these to cancel the Engagement Request, chat with Jules or to end the Engagement.

Set up webhooks

SaleMove uses webhooks to notify integrators of events. A webhook is nothing more than an endpoint exposed in your web server that SaleMove will send HTTP requests to. If you want to validate that it is in fact SaleMove who makes requests to your server, include a shared secret key in the headers field in the webhook configuration like this:

{
  "url": "https://server.com/salemove/events/any",
  "method": "POST",
  "headers": {"Salemove-Secret-Key": "FkjBfivFYGjcukoTcMC9Jw"},
  "events": ["engagement.start", "engagement.end", "engagement.request.failure", "engagement.chat.message"]
}

SaleMove does not reuse or introspect the headers, so you can use any validation mechanism that you desire (but please don’t send your AWS secret keys to us, we really don’t want them).

Here is an example of a dummy application that implements a single route for webhook events:

require 'sinatra'
require 'json'

post '/salemove/events/any' do
  puts 'Salemove event occurred'

  event = JSON.parse(request.body.read)

  case event['event_type']
  when 'engagement.start'
    puts "Engagement started: #{event.inspect}"
  when 'engagement.end'
    puts "Engagement ended: #{event.inspect}"
  when 'engagement.request.failure'
    puts "Engagement Request failed #{event.inspect}"
  when 'engagement.chat.message'
    puts "Operator sent a chat message: #{event.inspect}"
  end

  status 204
end

Canceling an Engagement Request

Let’s say that Vincent has already solved the whole medical situation and Jules’s assistance is no longer required. If so, then you can cancel the Engagement Request using the PATCH /engagement_requests/:engagement_request_id endpoint:

curl --request PATCH \
    --header 'Authorization: SessionId 6e84899d-8ae3-4b59-a482-7e7d2407c016' \
    --header 'X-Salemove-Visit-Session-Id: 1969' \
    --header 'Accept: application/vnd.salemove.v1+json' \
    --header 'Content-Type: application/json' \
    --data-binary '{
      "action": "cancel"
    }' \
  'https://api.salemove.com/engagement_requests/4106914f-7ed0-4f80-a4ee-3296ec43f50d'

Notice how the Visitor authentication credentials from the POST /engagement_requests response are used to authenticate this request. The Engagement Request ID in the URL is the same ID that was received in the POST /engagement_requests response.

At this moment an engagement.request.failure event is fired and an HTTP request containing the event payload is sent to your server if you included the engagement.request.failure event in the webhook configuration.

The Engagement Request is declined

It is possible that Operator Jules does not want to get into Vincent’s mess and declines the Engagement Request. The Engagement Request fails, Jules and Vincent do not engage.

When this occurs, an engagement.request.failure event is fired and life goes on. You can choose a new Operator and try again.

Example JSON payload for the engagement.request.failure event:

{
  "event_type": "engagement.request.failure",
  "event_id": "e8bacbef-fee6-4bb7-8c02-b7b1871b98d9",
  "dispatched_at": "2017-01-01T00:00:00.0Z",
  "engagement_request": {
    "id": "4106914f-7ed0-4f80-a4ee-3296ec43f50d",
    "visit_id": "5e051189-de92-41bb-85f9-906a49e3ebd5",
    "operator_id": "b79b0256-82f0-4bc4-ab67-390fc0edc73e",
    "fail_reason": "rejected"
  }
}

The Engagement Request times out

It is also possible that Operator Jules is in a sticky situation (e.g being held at gunpoint in a diner) and is unable to respond to the request. In this case the Engagement Request times out automatically after the number of seconds that were specified in the POST /engagement_requests response.

Jules at the diner

To no-one’s big surprise, an engagement.request.failure event is fired here as well.

The Engagement Request is accepted

If Jules is ready to help Vincent and accepts the Engagement Request, then an Engagement is created and an engagement.start event is fired:

{
  "event_type": "engagement.start",
  "event_id": "e8bacbef-fee6-4bb7-8c02-b7b1871b98d9",
  "dispatched_at": "2017-01-01T00:00:00.0Z",
  "engagement": {
    "id": "c71379b7-4e32-4dd4-a549-04f90f959dd5",
    "engagement_request_id": "4106914f-7ed0-4f80-a4ee-3296ec43f50d",
    "visit_id": "5e051189-de92-41bb-85f9-906a49e3ebd5",
    "current_sub_engagement": {
      "id": "fc3aa55f-e6ad-4f4f-9b6b-28541d316a4c",
      "operator_id": "b79b0256-82f0-4bc4-ab67-390fc0edc73e",
      "site_id": "6a78ce08-683b-49bf-9904-922c15410448"
    }
  }
}

By subscribing to this event you can get the Engagement ID (c71379b7-4e32-4dd4-a549-04f90f959dd5 here) that you’ll need to send chat messages during the Engagement.

Chat with the Operator

The whole point of having an Engagement is for the Operator and Visitor to exchange information. The most basic method of changing information on the web is sending and receiving chat messages.

Sending a chat message

In an ongoing Engagement, chat messages can be sent using the PUT engagements/:engagement_id/chat_messages/:id endpoint.

curl --request PUT \
    --header 'Authorization: SessionId 6e84899d-8ae3-4b59-a482-7e7d2407c016' \
    --header 'X-Salemove-Visit-Session-Id: 1969' \
    --header 'Accept: application/vnd.salemove.v1+json' \
    --header 'Content-Type: application/json' \
    --data-binary '{
      "content": "They call it a Royale with cheese."
    }' \
    'https://api.salemove.com/engagements/c71379b7-4e32-4dd4-a549-04f90f959dd5/chat_messages/9d44a18a-1e10-4d0f-8448-d9e46e786816'

Notice that this endpoint requires a v4 UUID (e.g 9d44a18a-1e10-4d0f-8448-d9e46e786816) in the request URL. This allows the endpoint to be idempotent so you can implement retries should the PUT request fail as duplicate messages with the same ID are ignored server-side.

Receiving chat messages

Whenever one participant calls the PUT engagements/:engagement_id/chat_messages/:id endpoint, an engagement.chat.message event is triggered. Note that this message is only sent to the recipient of the message, the event is not sent back to the sender. You can use this event to display messages sent by Jules in Vincent’s UI.

End the Engagement

If Vincent has found the interaction to be satisfactory and wants to continue on his merry way, the Engagement can be ended using the PATCH /engagements/:engagement_id endpoint. Calling this endpoint marks the Engagement as ended, Operator Jules is notified of the transition and no further chat messages can be exchanged.

curl --request PATCH \
    --header 'Authorization: SessionId 6e84899d-8ae3-4b59-a482-7e7d2407c016' \
    --header 'X-Salemove-Visit-Session-Id: 1969' \
    --header 'Accept: application/vnd.salemove.v1+json' \
    --header 'Content-Type: application/json' \
    --data-binary '{
      "action": "end"
    }' \
  'https://api.salemove.com/engagements/c71379b7-4e32-4dd4-a549-04f90f959dd5'

At this point, whether it was Vincent or Jules who ended the Engagement, an engagement.end event is fired.

What’s next?

SaleMove’s REST API exposes endpoints to access real-time and historical data, statistics and much more.