Sparkcentral 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, and set contact attributes. Instead of limiting the bot platforms that can be integrated, a generic API is provided that you can use to integrate with any platform.

To integrate your favorite bot platform with Sparkcentral, you will need to provide an HTTPS endpoint. Sparkcentral calls this endpoint every time a conversation is assigned to your virtual agent and whenever the contact sends a new message. If you're using a synchronous bot platform, you can immediately answer this request with the response. If you're using an asynchronous bot platform, you can also send a POST request to Sparkcentral to send a reply to a conversation. 

virtual-agent-api.png

Two types of virtual agents

Inception

An inception virtual agent automatically picks 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 so that any agent can respond. 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.

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 point in the conversation. The delegation virtual agent will take over at this point. The handoff rule determines what should happen if a delegation virtual agent needs a human agent. If “New queue” is selected, the conversation moves to the New queue to be picked up by any agent. If “previous agent” is selected, the delegation virtual agent assigns the conversation back to the previous human agent.

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

Add a virtual agent

To add a virtual agent:

  1. On the Settings tab, expand Virtual Agents, select Custom Virtual Agents, and then select Add Custom Virtual Agent.
  2. Enter a name for the virtual agent. This name will appear in Sparkcentral as “{Name} VA”.
  3. Specify whether you want your virtual agent to be activated as an Inception agent or a Delegation agent.
  4. The Webhook URL must be provided to properly send conversation information to your virtual agent (see the Webhooks section for more information).
  5. Fill out the remaining fields as you like, including any handoff rules you want to add.
  6. Select Save.
  7. Scroll down to the Channel Access section, and use the toggles to select a channel or channels where this virtual agent can be accessed.

Timeout virtual agent

You can configure two types of contact rules for virtual agents:

  • Timeout Virtual Agent - Determines when a handoff will occur if the virtual agent stops responding for any reason. The maximum is 60 minutes.
  • Timeout Contact - Determines when a handoff will occur if the contact (user) stops responding. The maximum is 60 minutes.

Error handling

If the virtual agent connection times out, 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, automatically send a standard reply to the customer

Edit or delete a virtual agent

To edit a virtual agent, on the Custom Virtual Agents page, select the Edit icon. To delete a virtual agent, select the Trash icon.

View the secret for a virtual agent

On the Custom Virtual Agent spage, select the Edit icon next to the virtual agent. You can view or copy the shared secret generated for this virtual agent on the Edit Custom Virtual Agent page. See the Securing Webhooks section for more information.

Webhooks

When a conversation is assigned to the virtual agent you registered in the previous section, Sparkcentral sends you an event via the URL you configured.

Three important events are sent:

  • CONVERSATION_STARTED
  • CONVERSATION_DELEGATED
  • INBOUND_MESSAGE_RECEIVED

Common fields

All events have 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 typeof request. Currently, the version is always 1. Versions 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.

Copy
{
"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 is 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 that 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.

Copy
{
"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.

Copy
{
"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 to the contact:

Copy
{
"sendMessage": { "text": "Hi! How can I help you?", "attachment": "funny_cat.gif" },
"applyTopics": ["Hotel Reservation"],
"applyTags": ["Happy"],
"setContactAttributes": { "account_number": "19758293529351" },
"complete": "HANDOVER"
}
  • sendMessage: (Optional) The message you want to send to the contact. You can send only text, only an attachment, or both at the same time. If you want to send an attachment, you must upload it firs, so we recommend using the asynchronous flow.
  • applyTopics: (Optional) The list of topics you want to apply to the conversation (the intent or action that your Virtual Agent matched). Topics that do not exist in Sparkcentral will be ignored.
  • 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"}].
  • 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.
  • complete. (Optional) This can be either HANDOVER if you want to give the conversation to another agent, or RESOLVED if you want to resolve the conversation. 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. We also recommend using the REST API when you want to send an attachment. You can respond with {}, upload an attachment using a PUT request, and then send the attachment using a POST request.

Securing webhooks

When setting up a virtual agent, you received a secret key that you can use to verify whether an incoming webhook request really comes from Sparkcentral without alterations. In the request headers of each webhook call is the X-Sparkcentral-Signature. This contains an 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:

Copy
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: Make sure you calculate the signature off the body as is, before you deserialize it from JSON. During the calculation of the signature, all white space 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, we recommend verifying that this timestamp is no older than 5 minutes:
Copy
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, some non-functional requirements 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 is placed in the New queue for a human agent to pick up. This can also be configured in Settings. If you prefer, you can automatically resolve the conversation and send a message to the contact (such as "Please try again in a little while"). The virtual agent could also decide to immediately return control by sending RESOLVED or HANDOVER in thecomplete 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 is handed back to the previous owner of the conversation or 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'll 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 are provided a client_id and client_secret to authenticate with Sparkcentral’s authorization server and 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 in the following sections). When the access_token expires, the API returns 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 by expanding Integration & APIs and selecting 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

Copy
$ 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

Copy
$ 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'
  
Parameter Status Description
grant_type required Must be set to client_credentials
client_id required Sparkcentral provided client identifier
client_secret required Sparkcentral provided client secret
scope required Must be set to client-read

RESPONSE

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

Parameter Description
token_type The token type to be used in the authorization header. This will always be bearer for client credentials grant.
access_token The 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_in The 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:

Copy
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 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"}.

Note that only topics that exactly match a topic in the platform will be applied to the conversation (case insensitive). Non-existing topics are 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 messageIdwhen 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 file name when sending the message ({"sendMessage": {"attachment": ""}}).

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 is performed after the attributes are set. For the best performance, we recommend setting contact attributes along with the response to indicate that 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

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 text and an attachment. For example, if you upload 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 file name should be unique within a conversation and is visible to the contact if they download your attachment.

The Content-Typeheader should contain the correct mime-type of the attachment (such as ‘image/jpeg’). The Content-Lengthheader should contain the size of the attachment in bytes. Attachments should be fewer than 10 megabytes to prevent error.

 

Example URI

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