With Generic widgets, you can build a simple, yet powerful widget. A Generic widget is made of one or several sections
, each of them containing one or several items
.
Use Cases
Built-in items
can allow the following use-cases:
- Open a URL in a modal or a new tab (eg. open the user profile from your internal CRM)
- Send a request to your API (eg. quickly refund your user, start a debug sequence, etc.)
- Display and edit data from your API (eg. display the current plan of your user)
Widget Schema
A generic widget is defined as the following (see the full reference):
{
"version": "1.0",
"sections": [
{
"id": "information",
"title": "Information",
"items": [
// Items…
]
},
// More sections…
]
}
Sections
Sections serve as groups for items. For a simple widget, you'll likely use only one section, but for a more complex widget, you may want to organize items by category, use-case, etc.
By default, all sections are deployed. Operators can choose to minimize a section by clicking on it. This "minimized" / "deployed" state is saved in permanent storage, meaning that it remains the same after reloading the Crisp app, restarting the browser, etc.
Sections can also be fully hidden with the help of Conditions.
Items
Sections are populated with items. Two types of built-in items exist:
button
: represents a button, to proceed to various actions from itdata
: allows to display and edit data from various sources
Button Items
Any button item can have its label
and color
customized:
{
// <other properties eluded>
"value": {
// <other properties eluded>
"label": "View account 🔎",
"color": "blue"
}
}
Link Button Item
A button item of type link
is a button that opens a link in a new tab. For example, to open an CRM page, to view the full details of a customer's order, etc.
The url
property represents the actual URL of the page to open. You can choose to attach additional data to it, and even to build it dynamically.
Modal Button Item
A button item of type modal
is a button that opens a link in a modal, above the Crisp app itself. This allows the user to view another page without having to leave the current conversation. Controls ("Open in new tab", "Close", "Minimize" and "Move" buttons) are available bellow the modal.
The url
property represents the actual URL of the page to display. You can choose to attach additional data to it, and even to build it dynamically.
The page displayed in the modal can be an already existing page of yours, for example the user profile from your internal CRM. You can also choose to build custom pages, powered by our widget JS SDK:
Hook Button Item
A button item of type hook
is a button that sends an event to your plugin's action URL, to trigger an internal flow on your side. For example, to quickly refund your user, to start a debug sequence, etc.
View Events for more information.
Submit Button Item
A button item of type submit
is a button that sends an event to your plugin's action URL, with all the values of the data items matching the group identifier. This is exclusively built to mimic forms. The submit button item and the data items must belong to the same section.
View Events for more information.
Data Item
An item of type data
can display data from different sources (eg. static data, Crisp data, data from your API). Supported types are string
, number
and boolean
.
Depending on the value
property, the behavior of the data item changes. Please refer to the Widget Reference for more details.
Data can also be edited by operators, if you choose to allow it (via the editable
property). When editing a data, a "Data Edit" event is immediately sent to the plugin's action URL (unless if the group
identifier is set, in that case a "Button" event is sent when the corresponding submit
button is clicked).
Data Binding
Data binding is a syntax allowing you to access to Crisp data right in your generic widget. This can be pretty handy to dynamically build a button URL, to display a section according to a condition, etc. In other cases, this can eliminate the need for you to make additional requests to our REST API.
The syntax is simple: just wrap the path of the Crisp data you want to access to with a pair of brackets ({}
). For example, {conversation.meta.data.order_status}
will be replaced by the actual value of the conversation custom data order_status
. If the value of that custom data was to change, then {conversation.meta.data.order_status}
would be automatically refreshed with the new value.
Data binding also supports comparison operators (==
, ===
, !=
, !==
) and logical operators (&&
, ||
).
Usages
Conditions
Conditions are used to dynamically hide and show sections and items:
// Example 1: show a section / item only if the conversation custom data or contact custom data `order_id` exists
"condition": "{conversation.meta.data.order_id || conversation.contact_data.order_id}"
// Example 2: show a section / item only if the conversation custom data `order_status` is "finished"
"condition": "{conversation.meta.data.order_status === 'finished'}"
// Example 3: show a section / item only if the conversation custom data `order_status` is "finished" or "pending"
"condition": "{conversation.meta.data.order_status === 'finished' || conversation.meta.data.order_status === 'pending'}"
// Example 4: show a section / item only if the conversation is pending
"condition": "{conversation.state === 'pending'}"
Link Button Item / Modal Button Item
The url
property of link
and modal
button items can be built with data binding inside:
// Example 1: build a button URL with data binding
"url": "https://acme.com/orders/order/{conversation.meta.data.order_id || conversation.contact_data.order_id}/details/"
Button Item / Data Item
Any button item (link
, modal
, hook
and submit
) and data item (only when fetching the value) has a data
property that can be used to pass additional data along the request (as query parameters).
Data binding is supported by this property:
// Example 1: embed various data
"data": {
"order_status": "{conversation.meta.data.order_status || conversation.contact_data.order_status}",
"conversation_state": "{conversation.state}",
"user_verified": "{conversation.is_verified === true}",
"operator": "{operator.email}"
}
Data Item
The value
property of a data item can be bound to Crisp data. This can be useful to show an information that the Crisp app doesn't usually show, to display a specific custom data while having the regular "Visitor Data" widget closed, etc.
// Example 1: display a custom data
"value": "{conversation.meta.data.order_status || conversation.contact_data.order_status}"
// Example 2: display the conversation creation date
"value": "{conversation.created_at}"
Modifiers
Modifiers can be used to alter the way data is rendered:
url_encode
: whether to URL-encode or not the data. Please note that data is URL-encoded by default when embedded inside the URL of an iFrame Widget, Modal Button Item or Hook Button Item. Usage:{DATA_PATH | url_encode}
,{DATA_PATH | url_encode=true}
,{DATA_PATH | url_encode=false}
.
// Example 1: disable URL-encoding for data embedded inside an iFrame Widget URL
"url": "https://acme.com/widget.html?order_id={conversation.meta.data.order_id | url_encode=false}"
// Example 2: URL-encode the data of a Data Item
"value": "{conversation.created_at | url_encode}"
Events
Two types of events exist: button
and action
. They occur in the following cases:
Type | Item | Occurs when… | Method | Target URL |
---|---|---|---|---|
Button Event | Link Button Item | …clicking on the button | GET | The url defined in the button |
Button Event | Modal Button Item | …clicking on the button | GET | The url defined in the button |
Action Event | Hook Button Item | …clicking on the button | POST | The plugin's action URL |
Action Event | Submit Button Item | …clicking on the button | POST | The plugin's action URL |
Action Event | Data Item | …fetching the value (either automatically or manually) | POST | The plugin's action URL |
Action Event | Data Item | …editing the value | POST | The plugin's action URL |
Attached data
Data is passed along those events, either as query parameters (for GET requests), or as a body payload (for POST requests):
Default data:
website_id
token
Optional data:
locale
: only for Link Button Item and Modal Button Itemsession_id
: only if your plugin has at least onewebsite:conversation:*
token scope granted
Additional data: You can also pass along your own data, by declaring them in the
data
property of the corresponding item. The values can be literal strings, or data bindings (see Data binding for more information):{ // <other properties eluded> "value": { // <other properties eluded> "data": { // Literal string "seed": "abcde", // Data binding "order_status": "{conversation.meta.data.order_status}" } } }
Button events
Following is an example of a GET request URL (for example, when clicking on a Modal Button Item), with aforementioned query parameters attached to it:
"https://acme.com/widget/modal.html?session_id=session_4396ad07-53d9-47a7-81ba-3b6278bf5fae&website_id=a63d1c82-809d-4ff0-be7b-9c82b24cfcf3&token=c99e0df2-db6f-4b0c-9505-83fcbb2da627&locale=en&seed=abcde&order_status=finished"
Action events
As per the table above, Action events are sent to your plugin's action URL. To configure the action URL of your plugin, go to the Marketplace, then to your plugin, then Settings.
Action event general format
An Action event HTTP POST
body is JSON-encoded and formatted as such:
{
"action" : { // The action type and method (see examples below)
"type" : "type",
"method" : "method"
},
"widget" : { // The widget the event occurred in
"section_id" : "section_id",
"item_id" : "item_id"
},
"origin" : { // The website and conversation the event occurred in
"website_id" : "website_id",
"session_id" : "session_id"
},
"payload" : {}, // The event payload (format depends on event type)
"timestamp" : "timestamp" // The UNIX timestamp of the event (in milliseconds)
}
Requests will be sent to your server with the Crisp-HooksDeliver 1.0 (+https://crisp.chat)
User Agent, so make sure to whitelist it.
Redeliveries attempts
Your server must respond with a 200
HTTP code to such requests, otherwise they will be considered as failed. In such case, further attempts will be done (between 2 and 3 attempts), each spaced several seconds apart (between 10 and 15 seconds). If your server still fails to respond to those additional attempts, the request will be dropped and considered as definitely failed. Information about the current delivery attempt and the maximum number of attempts, are available in the X-Delivery-Attempt-Count
and X-Delivery-Attempt-Limit
headers.
Verifying events signature
Action Events that are received by your server should be verified before being processed. The secret from your Production Token can be used to verify received Action Events.
The verification algorithm is the same as the one used for Web Hooks, and is described in the Web Hooks Reference.
We recommend using the node-crisp-api
library to verify Action Events signatures:
const express = require("express");
const bodyParser = require("body-parser");
const Crisp = require("crisp-api");
// Configuration
const SECRET = "<your_web_hooks_secret_here>";
// Create the Crisp client (it lets you access both the REST API and RTM events)
var CrispClient = new Crisp();
// Configure your Crisp authentication tokens ('plugin' token)
CrispClient.authenticateTier("plugin", "<token_identifier>", "<token_key>");
// Context
const app = express();
const port = 3997;
app.use(bodyParser.json());
// Handlers
app.post("/", (request, response) => {
response.sendStatus(200);
console.log(
`<-- ${request.protocol}://${request.get("host")}${request.originalUrl}\n`,
request.body
);
let _timestamp = request.headers["x-crisp-request-timestamp"],
_signature = request.headers["x-crisp-signature"];
// Verify signature
let _verified = CrispClient.verifyWidget(
SECRET, request.body, _timestamp, _signature
);
console.log(
`${_signature} |remote| <- SIGNATURE -> |local| (hidden)`, _verified
);
});
// Initializers
app.listen(port, () => {
console.log(`Hooks server listening at http://localhost:${port}`);
});
Action event examples
Hook Button Item
{
"action": {
"type": "button",
"method": null
},
"widget": {
"section_id": "section_information",
"item_id": "item_button_hook"
},
"origin": {
"website_id": "a63d1c82-809d-4ff0-be7b-9c82b24cfcf3",
"session_id": "session_4396ad07-53d9-47a7-81ba-3b6278bf5fae"
},
"payload": {
"data": {
"seed": "abcde",
"order_status": "finished"
}
},
"timestamp": 1633625955045
}
Submit Button Item
{
"action": {
"type": "button",
"method": null
},
"widget": {
"section_id": "section_information",
"item_id": "item_button_submit"
},
"origin": {
"website_id": "a63d1c82-809d-4ff0-be7b-9c82b24cfcf3",
"session_id": "session_4396ad07-53d9-47a7-81ba-3b6278bf5fae"
},
"payload": {
"value": {
"data_user_creation_date": "new value",
"data_user_id": "new value"
},
"data": {
"seed": "abcde",
"order_status": "finished"
}
},
"timestamp": 1633625955045
}
Data Item (Fetch)
{
"action": {
"type": "data",
"method": "fetch"
},
"widget": {
"section_id": "section_information",
"item_id": "item_data"
},
"origin": {
"website_id": "a63d1c82-809d-4ff0-be7b-9c82b24cfcf3",
"session_id": "session_4396ad07-53d9-47a7-81ba-3b6278bf5fae"
},
"payload": {
"data": {
"seed": "abcde",
"order_status": "finished"
}
},
"timestamp": 1633625955045
}
For this request, your server must respond with a 200
HTTP code and a response body, containing the data that was requested. Supported data types are string
, number
and boolean
:
{
"data": {
"value": "my item value"
}
}
Data Item (Edit)
{
"action": {
"type": "data",
"method": "edit"
},
"widget": {
"section_id": "section_information",
"item_id": "item_data"
},
"origin": {
"website_id": "a63d1c82-809d-4ff0-be7b-9c82b24cfcf3",
"session_id": "session_4396ad07-53d9-47a7-81ba-3b6278bf5fae"
},
"payload": {
"value": "new value"
},
"timestamp": 1633625955045
}