APIs & Integrations

Virtual Agent API

Table of Contents

What is the Virtual Agent API?

Sparkcentral virtual agents allow enterprises to add bots to the platform as if they were real agents. They can respond to incoming messages, resolve conversations, apply topics, set contact attributes, etc. Instead of limiting the bot platforms that can be integrated, a generic API is provided that you can use to integrate with any platform. In order to integrate your favorite bot platform with Sparkcentral, you will need to provide an HTTPS endpoint. Sparkcentral will call this endpoint every time a conversation is assigned to your virtual agent, and when the contact sent a new message. If you are using a synchronous bot platform, you can immediately answer to this request with the response. If you are using an asynchronous bot platform, you can also send a POST request to Sparkcentral to send a reply to a conversation.

Two types of Virtual Agents

Inception 

An Inception virtual agent will automatically pick up private conversations that start in the channels it has access to. If an inception virtual agent needs a human agent, it will place the conversation in the new queue at this point any agent can jump in. The inception virtual agent can also resolve a conversation if no human assistance is required.

  • Because an inception virtual agent automatically picks up conversations in the channel it has access to, you cannot create multiple inception virtual agents that have access to the same channel. They would create a conflict.

Delegation 

A Delegation virtual agent will not automatically pick up conversations. Instead a human agent can give a conversation to a delegation virtual agent at any time of the conversation. The delegation virtual agent will take over at this point, and converse with the contact. The handoff rule determines what should happen if a delegation virtual agent needs a human agent. If “New Queue” is selected, the conversation will end up in the new queue to be picked up by any agent. If “previous agent” is selected, the delegation virtual agent will assign the conversation back to the previous human agent.

  • Multiple delegation virtual agents can have access to the same channel. A human agent has to specifically select the virtual agent that needs to take over the conversation.

Add a Virtual Agent

Navigate to Settings, Virtual AgentsCustom Virtual Agents, then click on the Add Custom Virtual Agentbutton.

  1. Name your Virtual Agent- this name will appear in the rest of the app as “{Name} VA”.
  2. Specify if you want your Virtual Agent to be activated as an Inception or a Delegation agent
  3. The Webhook Url must be provided to properly send conversation information to your Virtual Agent (see the Webhooks section for more information).
  4. Fill out the rest of your Virtual Agent information, and the accompanied handoff rules. Click on Save.
  5. After which you then can nominate a channel access for this Virtual Agent. 
Screen Shot 2021-02-01 at 9.06.16 AM

Timeout Virtual Agent

  • configure when to do handoff if the autoresponder stops responding for any reason. Maximum time is 60 mins.

Timeout Contact

  • configure when to do a handoff if the contact stops responding. Maximum time is 60 mins.
 

Virtual Agent Timeout/Error Handling and Contact Timeout Handling

If the virtual agent connection times out or an error occurs, or if the contact times out, you can do the following:

  • Mark the conversation as new or resolved
  • Automatically apply a topic
  • If the conversation is marked as resolved, you can also automatically send a standard reply to the customer

 

Edit / Delete a Virtual Agent

In the Virtual Agents settings tab you can click on the  , to Edit your virtual agent. The Edit modal is where you can see the Shared Secret generated for this Virtual Agent (see the Securing Webhooks section for more information). Click on the trash bin icon should you wish to delete your virtual agent. 

Webhooks

When a conversation is assigned to the Virtual Agent you registered in the previous step, Sparkcentral will send you an event via the url you configured.

There are three important events that are sent:

  1. CONVERSATION_STARTED
  2. CONVERSATION_DELEGATED
  3. INBOUND_MESSAGE_RECEIVED

 

COMMON FIELDS
In all events you receive, you will find certain common fields.

  • type: A string that defines what kind of event occurred. (currently CONVERSATION_STARTED or INBOUND_MESSAGE_RECEIVED). New events can be added in the future. Avoid responding with an error to unknown values, instead ignore them. Depending on this type, the structure of data will be different.
  • version: A number designating the version of the type of request. Currently, the version is always 1. Version will be used in the future for introducing non-backward compatible changes.
  • idempotencyKey: A string that uniquely identifies each event. When a timeout occurs when sending you the event, (or we receive an error response), we will retry the event. This key can help you to ensure that a request is processed only once.
  • timestamp: The timestamp when we sent the request. This is used to counter possible replay attacks.
  • data: An object that contains structured data for the specific type. For example, an INBOUND_MESSAGE_RECEIVED type event has fields such as conversationId and message. Fields may be added in the future.

CONVERSATION_STARTED

The CONVERSATION_STARTED event is sent when a conversation is assigned to your Inception Virtual Agent. You will only receive messages and will only be able to reply to messages as long as the conversation is assigned to the Inception Virtual Agent.

As part of this event you receive extra information about the contact profile trying to contact you, the channel over which they are contacting you, and the contact attributes. The contact attributes include medium specific information, information added manually before, and information coming from previous CRM lookups. Note that the Inception Virtual Agent will receive the CONVERSATION_STARTED event before any configured CRM lookup is performed and therefore the event will not include any new information from the CRM.

After the CONVERSATION_STARTED event, you’ll receive an INBOUND_MESSAGE_RECEIVED event for each message the contact sends, starting from the beginning of the conversation.

{
  "idempotencyKey": "933d877e-a13f-4958-93a4-d5d4941bf624",
  "version": 1,
  "type": "CONVERSATION_STARTED",
  "timestamp": "2019-01-17T16:46:51.908736Z",
  "data": {
    "conversationId": "0-01d90bc1b13-000-9a9ca0d8",
    "medium": { "id": "fb" },
    "channel": {
      "id": "0-019a0b43ad3-000-471aa28a",
      "name": "Sparkcentral support channel"
    },
    "contactProfile": {
      "id": "0-01835f0fec3-000-0a6c390a",
      "mediumContactProfileId": "975734236828364800",
      "primaryIdentifier": "jan_spark",
      "secondaryIdentifier": "Jan Spark",
      "pictureUrl": "http://image.com/sticky/default_profile_images/default_profile_normal.png"
    },
    "contactAttributes": [
      { "attribute": "company", "value": "Sparkcentral", "source": "AGENT" },
      {
        "attribute": "fb-profile-image",
        "value": "https://image.com/sticky/default_profile_images/default_profile_normal.png",
        "source": "MEDIUM"
      },
      {
        "attribute": "fb-verified",
        "value": "Not Verified",
        "source": "MEDIUM"
      },
      {
        "attribute": "fb-account-created",
        "value": "March 19, 2018",
        "source": "MEDIUM"
      },
      {
        "attribute": "fb-handle-name",
        "value": "jan_spark",
        "source": "MEDIUM"
      },
      { "attribute": "fb-name", "value": "Jan Spark", "source": "MEDIUM" },
      {
        "attribute": "spark-url",
        "value": "http://www.sparkcentral.com",
        "source": "AGENT"
      }
    ]
  }
}  

CONVERSATION_DELEGATED

The CONVERSATION_DELEGATED event is sent when a conversation is assigned to your Delegation Virtual Agent. Like the CONVERSATION_STARTED event this contains extra information about the contact profile trying to contact you, the channel over which they are contacting you, and the contact attributes.

After the CONVERSATION_DELEGATED event, you’ll receive an INBOUND_MESSAGE_RECEIVED event for each message the contact sends, starting from when the conversation has been assigned to the virtual agent. Unlike the CONVERSATION_STARTED event, the CONVERSATION_DELEGATED event is often not immediately followed by an INBOUND_MESSAGE_RECEIVED. Instead it is expected the virtual agent will ask the first question to the contact. After you’ve received a CONVERSATION_DELEGATED event, you have 2 minutes to ask this first question. See the requirements for more details.

{
  "timestamp": "2019-03-28T14:29:56.727041Z",
  "idempotencyKey": "8fa424c3-a88b-4f02-bd38-4e292a27597b",
  "version": 1,
  "type": "CONVERSATION_DELEGATED",
  "data": {
    "conversationId": "0-01f20ec5e10-000-860d4dde",
    "medium": { "id": "fb" },
    "channel": {
      "id": "0-01e25845518-000-19087686",
      "name": "JansDevFbChannel4"
    },
    "contactProfile": {
      "id": "0-01f116a7f9c-000-b5d375aa",
      "mediumContactProfileId": "cd067e88519aa063b15e9944",
      "primaryIdentifier": "Anonymous",
      "secondaryIdentifier": "Young Parrot",
      "pictureUrl": null
    },
    "contactAttributes": [
      {
        "attribute": "fb-page-title",
        "value": "In-Web Messaging Demo",
        "source": "MEDIUM"
      },
      {
        "attribute": "fb-page-url",
        "value": "https://messenger-demoapp-dev/#JansDevSmoochChannel4",
        "source": "MEDIUM"
      },
      {
        "attribute": "fb-browser-language",
        "value": "en-US",
        "source": "MEDIUM"
      },
      {
        "attribute": "fb-domain",
        "value": "messenger-demoapp-dev",
        "source": "MEDIUM"
      },
      {
        "attribute": "fb-sdk-version",
        "value": "1.14.2",
        "source": "MEDIUM"
      }
    ]
  }
} 

INBOUND_MESSAGE_RECEIVED

After the conversation is assigned to the Virtual Agent and after you received a CONVERSATION_STARTED or a CONVERSATION_DELEGATED event, you will start receiving INBOUND_MESSAGE_RECEIVED events. These events occur every time the contact sends a message. Note that we send the conversation topics with each INBOUND_MESSAGE_RECEIVED event.

{
  "idempotencyKey": "cc75552a-1a78-11e9-855e-6d1e71016abf",
  "version": 1,
  "type": "INBOUND_MESSAGE_RECEIVED",
  "timestamp": "2019-01-17T16:56:16.108626Z",
  "data": {
    "conversationId": "0-01d90bc1b13-000-9a9ca0d8",
    "message": { 
      "messageId": "cc75552a-1a78-11e9-855e-6d1e71016abf",
      "text": "Hello" 
    },
    "conversationTopics": [ "Topic1", "Topic2" ]
  }
} 

RESPONSE

Your response to the webhook should be a 200 OK. In the body you can return the response you want to send the contact.

{
  "sendMessage": { "text": "Hi! How can I help you?", "attachment": "funny_cat.gif" },
  "applyTopics": ["Hotel Reservation"],
  "applyTags": ["Happy"],
  "setContactAttributes": { "account_number": "19758293529351" },
  "complete": "HANDOVER"
} 
  • sendMessage: The message you want to send to the contact. This field is optional. You can send only text, only an attachment or both at the same time. If you want to send an attachment, you need to upload it first. Therefore it is recommended to use the asynchronous flow. (See below)
  • applyTopics: The list of topics you want to apply to the conversation. (eg, the intent or action that your Virtual Agent matched). Topics that do not exist in Sparkcentral will be ignored. This field is optional.
  • applyTags: The list of tags you want to apply to the message from the contact. Tags that do not exist in Sparkcentral, will be ignored. It’s only possible to use applyTags in response to an INBOUND_MESSAGE_RECEIVED. It’s also possible to tag a specific message by specifying the messageId. In that case you can respond using "applyTags": [{"messageId": "cc75552a-1a78-11e9-855e-6d1e71016abf", "tag": "Happy"}]. This field is optional.
  • setContactAttributes: The attributes you want to set on a contact. The object is a map between the attribute definition’s alias and value to set. This field is optional.
  • complete. This can be either HANDOVER if you want to give the conversation to another agent, or RESOLVED if you want to resolve the conversation. This field is optional. When you pass HANDOVER, the handoff rule you configured in settings determines what will happen next. If the handoff rule is “No one”, the conversation is placed in the new queue without an owner. Any human agent can pick up the conversation. If the rule has been set to “Previous agent”, the conversation will be assigned back to the previous human agent. If there was no previous agent, the conversation is placed in the new queue without an owner.

 

If you are integrating with an asynchronous bot platform, you can simply return an empty json body {}, and send this message using a POST request (see Virtual Agent API Section). It’s also recommended to use the REST api when you want to send an attachment. You can respond with {}, then upload an attachment using a PUT request, and then finally send the attachment using a POST request.

SECURING WEBHOOKS

When setting up a Virtual Agent, you received a secret key you can use to verify if an incoming webhook request really comes from Sparkcentral without alterations. In the request headers of each webhook call you will find the X-Sparkcentral-Signature. This contains a HMAC-SHA256 signature based on the body of the request. Both the secret key you received and the signature are encoded as hexadecimal strings. Most languages come with libraries out of the box to verify this signature. Here is some sample code to verify it in NodeJS.

const sparkcentralSecret = "..."; //do not share!
const expectedSignature = request.headers["X-Sparkcentral-Signature"];
const actualSignature = crypto
  .createHmac("sha256", Buffer.from(sparkcentralSecret, "hex"))
  .update(request.body, "utf-8")
  .digest("hex");
if (actualSignature !== expectedSignature) {
  throw new createError.Unauthorized("X-Sparkcentral-Signature wrong");
} 

NOTE: Ensure you calculate the signature off the body as is, before you deserialize it from JSON. During the calculation of the signature, all whitespace is considered significant.

As part of the request body, you will find a timestamp. This is the time a request was sent. To prevent replay attacks, it is recommended to verify that this timestamp is no older than 5 minutes.

if (
  moment(JSON.parse(request.body).timestamp).isBefore(
    moment().subtract(5, "minutes")
  )
) {
  throw new createError.Unauthorized("Request too old");
} 

REQUIREMENTS

To provide a good customer experience, there are some non-functional requirements that are imposed on the webhook. When a webhook is sent, you have 10 seconds to respond with a 200 OK. If a timeout occurs, we will retry 3 times using an exponential backoff (up to 2 seconds). If the failures persisted during the retries, the assigned conversation will be handed over to a human agent by placing it in the new queue.

When the contact sends a message through Sparkcentral, by default we expect the Virtual Agent to reply to that message within 5 minutes (using either the response to the webhook call or the REST api). You can configure the timeout in the settings screen for your Virtual Agent (Timeout Virtual Agent). If the Virtual Agent does not answer the contact, by default the conversation will be placed in the new queue for a human agent to pick up. This can also be configured in the settings screen. If you prefer, you can automatically resolve the conversation and send a message to the contact (eg, ‘Please try again in a little while’). The Virtual Agent could also decide to immediately return control by sending RESOLVED or HANDOVER in the complete field.

Similarly after a CONVERSATION_DELEGATED event, your virtual agent has 5 minutes to pose a question to the contact by default. If the virtual agent fails to do this, the conversation will be handed back to the previous owner of the conversation or be placed in the new queue depending on the handoff rule.

Authorization

If you want to send the replies asynchronously or manipulate the conversation in your fulfillment code, you will need to call the Virtual Agent REST API. The Virtual Agent API uses the Client Credential Grant flow of the Oauth 2.0 specification for client authentication and authorization.

Clients will be provided a client_id and client_secret to authenticate with Sparkcentral’s authorization server and will receive an access_token to use for API request authorization. The access_token should be used in an authorization header, but optionally can be passed as a query string parameter (see request/response details below). When the access_token expires the API will return a 401 unauthorized. The client can automate this by generating a new access token and replaying the failed request with the fresh access token as documented in the following section. This is the same client_id and client_secret you might have received for Proactive Messaging API or Realtime Reporting API.

Sparkcentral users with admin rights can generate API keys in Settings,Integration & APIs,Rest API Keys.

CLIENT CREDENTIAL GRANT

Retrieve an access_token to make authorized API requests. When the access_token expires, use this endpoint to generate a new token.

REQUEST

$ curl -X POST https://public-api.sparkcentral.com/oauth2/token -H 'content-type: application/x-www-form-urlencoded' -d 'grant_type=client_credentials&client_id=YOUR_CLIENT_ID_HERE& client_secret=YOUR_CLIENT_SECRET_HERE&scope=client-read'

or

$ curl -X POST https://public-api-eu.sparkcentral.com/oauth2/token -H 'content-type: application/x-www-form-urlencoded' -d 'grant_type=client_credentials&client_id=YOUR_CLIENT_ID_HERE& client_secret=YOUR_CLIENT_SECRET_HERE&scope=client-read'

ParameterStatusDescription
grant_typerequiredMust be set to client_credentials
client_idrequiredSparkcentral provided client identifier
client_secretrequiredSparkcentral provided client secret
scoperequiredMust be set to client-read

RESPONSE

{ “token_type”: “bearer”, “access_token”: “a1b2c3d4e5f6”, “expires_in”: 43200 }

ParameterDescription
token_typeThe token type to be used in the authorization header. This will always be bearer for client credentials grant.
access_tokenThe token used to authorize requests. This should be added to requests as an authorization header: Authorization: Bearer a1b2c3d4e5f6. The API will also accept access_token as a query string parameter, however using the authorization header is the preferred method.
expires_inThe number of seconds until the token expires (12 hours)

Most platforms have out of the box support for OAuth2. For example in Nodejs using the axios-oauth-client, you can do the following:

const axios = require('axios');
const oauth = require('axios-oauth-client');
const tokenProvider = require('axios-token-interceptor');
const restClient = axios.create({ baseURL: 'https://public-api.sparkcentral.com' }); //or public-api-eu.sparkcentral.com in EU
const getClientCredentials = oauth.client(axios, {
    url: 'https://public-api.sparkcentral.com/oauth2/token',
    grant_type: 'client_credentials',
    client_id: process.env.SPARKCENTRAL_CLIENT_ID,
    client_secret: process.env.SPARKCENTRAL_CLIENT_SECRET,
    scope: 'client-read'
});
restClient.interceptors.request.use(oauth.interceptor(tokenProvider, getClientCredentials));  

Virtual Agent API

CONVERSATIONS

POST  /virtual-agent/conversations/{conversationId}

This allows you to manipulate the conversation in the same way as you would when responding to a webhook request. You can send a reply, add a topic and/or hand off the conversation. Each property in the body is optional. If you only want to send a message you can send {"sendMessage": {"text": "Hi!"}}. If you want to apply a topic and complete, but not send a message you can for example send {"applyTopics":["Spam"], "complete": "RESOLVED"}.

Please note that only topics that exactly match a topic in the platform will be applied to the conversation (case insensitive). Non-existing topics will be ignored. At the moment, no fuzzy matching will occur and typographical errors will result in a non-existing topic.

The same applies to tags. Only tags that exactly match a tag in the platform will be applied to a specific message (case insensitive). Non-existing tags are ignored. Note that it is mandatory to specify the messageId when using the REST api. The messageId is part of every INBOUND_MESSAGE_RECEIVED event.

If you want to send an attachment, you’ll need to first upload the attachment in a separate call (PUT /virtual-agent/conversations/{conversationId}/attachments/{filename}), and then use the filename when sending the message ({"sendMessage": {"attachment": ""}}).

Please note that if the contact already has a value saved for the attribute definition with a particular alias, the update will be ignored. If any of the attribute definitions are configured as CRM Lookup values, a CRM Lookup will be performed after the attributes are set. For the best performance, we recommend setting contact attributes along with the response to indicate the conversation is completed. Note that the Virtual Agent will not receive the new attributes from the CRM response, until the next CONVERSATION_STARTED or CONVERSATION_DELEGATED event is received for a conversation with a contact.

 

Example URI

POST /virtual-agent/conversations/conversationId
 

conversationId           string (required) 

                                        Conversation ID. Is sent as part of the webhook payload

Headers
Content-Type: application/json
Authorization: Bearer <token>
 
Body
{
    "sendMessage": { "text": "<message>", "attachment": "<filename>" },
    "applyTopics": [ "<topic>" ],
    "applyTags": [{"messageId": "<messageId>", "tag": "<tag>"}]
    "setContactAttributes": { "<attribute_alias>": "<attribute_value>" },
    "complete": "HANDOVER or RESOLVED"
}
 
Schema
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "A representation of what to do with a conversation",
  "type": "object",
  "required": [],
  "properties": {
    "sendMessage": {
      "type": "object",
      "required": ["text"],
      "properties": {
          "text": {
              "type": "string"
          }
      },
      "description": "The message you want to send",
    },
    "applyTopics": {
      "type": "array",
      "items": {
          "type": "string"
      },
      "description": "The topics that you want to apply to a conversation."
    },
    "applyTags": {
      "type": "array",
      "items": {
          "type": "object",
          "required": ["tag", "messageId"],
          "properties": {
            "tag": {
              "type": "string"
            },
            "messageId": {
              "type": "string"
            }
          }
      },
      "description": "The topics that you want to apply to a conversation."
    },
    "setContactAttributes": {
      "type": "object",
      "propertyNames": {
        "pattern": "^[a-zA-Z0-9_]+$"
      },
      "additionalProperties": true,
      "description": "The attributes that you want to set on a contact."
    },
    "complete": {
      "type": "string",
      "description": "Finish a conversation by handing it off to a human agent or by marking the conversation resolved",
      "enum": ["HANDOVER", "RESOLVED"]
    }
  }
}

The command(s) are accepted for execution.

Headers

Content-Type: application/json
 

Body

{}

Body

The request is malformed.

Body

Unauthorized: when using an invalid or expired access token.

Body

The conversation id does not exist or is no longer assigned to the virtual agent.

ATTACHMENTS

PUT    /virtual-agent/conversations/{conversationId}/attachments/{filename}

This allows you to upload an attachment that you can send in a conversation. You need to upload the attachment first, and then you can send a message with a text and an attachment. For example, if you uploaded an attachment to /virtual-agent/conversations//attachments/cat.jpg, you can send that attachment by sending {"sendMessage": {"text": "This is a cat!", "attachment": "cat.jpg"}} to /virtual-agent/conversations/. The filename should be unique within a conversation, and is visible to the contact if they download your attachment.

The Content-Type header should contain the correct mime-type of the attachment (eg, ‘image/jpeg’). The Content-Length header should contain the size of the attachment in bytes. Attachments should be less than 10 megabytes, otherwise an error might occur.

 

Example URI

PUT /virtual-agent/conversations/conversationId/attachments/filename
conversationId            string (required) 

                              Conversation ID. Is sent as part of the webhook payload

filename                       string (required) 

                              Filename of the attachment. The filename can only contain upper and lowercase                                        letters, numbers, -, _ and ..

Headers

Authorization: Bearer <token>
Content-Type: <mimetype of the attachment>
Content-Length: <size of the attachment in bytes>
 

Body

binary data

The attachment is uploaded and ready to use in your conversation. Note: after uploading the attachment to Sparkcentral, you still need to explicitely send it to the contact.

Headers

Content-Type: application/json
 

Body

{}

Body

The request is malformed. This can happen because the filename contains an invalid character, or because the attachment type cannot be sent to the medium.

Body

Unauthorized: when using an invalid or expired access token.

Body

The conversation id does not exist or is no longer assigned to the virtual agent.

Was this article helpful?

You already voted!

We're always happy to help you ❤️!

Did you know you could help us too?

We love feedback from our users. It’s incredibly important for our business. That’s why a positive recommendation from your company could really make a difference!