# Building an integration

To successfully integrate with Virtuoso, a custom integration needs to implement at least one of the following optional mechanisms:

  • a webhook endpoint, which will be used by Virtuoso to notify the integration of an organization requesting to install or uninstall it, or the occurrence of one of the events that the integration is subscribed to;
  • a configuration endpoint, through which an organization with an active installation of the integration can configure its particular settings.
  • an interactive endpoint, which will be used by Virtuoso to notify the integration of an action on one of the interactive menu items (see below) defined by the integration.

To build an integration:

  1. Click on the organization management icon on the left side of the Dashboard;
  2. Select the Integrations tab;
  3. Click on the Create new integration button at the top of the Recommended list and you'll be taken to an empty integration edition page.

# Integration fields

This page is divided into four main areas which, together, contain all the fields needed to set up an integration.

Integration edition page

Integration details
  • Name: the name which will identify the integration (it must be unique).
  • Icon (optional): an image which can help users identify the integration.
  • Enabled: if toggled on, it will activate the integration within Virtuoso, i.e., Virtuoso will start listening to requests from the integration and send requests to it, based on the list of subscribed events and other integration configurations. See some extra details on enabling / disabling an integration below.
  • Description (optional): a brief explanation of what the integration does.
  • Webhook URL (optional): the integration URL to which at least installation and uninstallation requests will be sent. Virtuoso will also send to this URL events specified in the Event subscription area, and actions requests, in case you don't provide a Request URL (see Interactive menus below).
  • Configuration (optional): define the mechanism that will be used to configure the integration installation. Two options are available:
    • Form: shows a configuration form directly in the details page of the integration installation (see Configuring an integration installation). You can find instructions on how to build the form in the UI augmentation section.
    • URL: uses an external page from where you can configure the integration installation.
Security

The fields in this area will be filled after building the integration.

  • Client key (read only): a unique identifier which can be used by the integration to authenticate itself within Virtuoso. See security considerations below.
  • Client secret (read only): a unique secret which can be used by the integration to authenticate itself within Virtuoso. See security considerations below.
  • Signing secret (read only): a unique secret which can be used by the integration to validate if the received requests originated in Virtuoso and were not tampered with. See security considerations below.
Event subscription
  • Event subscription enabled: if toggled on, it will allow the integration to subscribe to virtuoso events, being notified of when they happen.
  • Subscribed events (optional): a list of events available for subscription. See the list of available virtuoso events below.
Interactive menus
  • Interactive menus enabled: when toggled on, the defined integration actions will become visible in their respective Virtuoso menus.
  • Request URL (optional): URL used in the POST request that is made when you perform an action (e.g. a click) on an interactive menu item. When enabling interactive menus, you need to provide a valid request URL or webhook URL. Virtuoso sends the request to the latter when you don't provide the request URL.
  • Actions (optional): menu items composed by a name, a description, a Virtuoso menu, and a callback ID.
    • Virtuoso adds the actions in the specified menus, showing the integration icon followed by the action's name, and displays the action's description in a tooltip when you hover the added menu item.
    • When creating an action, you can also build a form to collect additional data from the user before Virtuoso sends the POST request (see UI augmentation on how to build one). The payload will depend on the menu where the action is shown, and the callback ID is sent alongside with the payload to help integrations identify the triggered action.
    • If the action contains a form, its context will also depend on the menu where the action is shown, and the payload will contain an additional payload property with the values of the submitted form.
    • See the list of available target menus and the payload format for each of them below.

Integration action modal

While editing an integration, you can test the Webhook URL and the Request URL by clicking on the icon near the field . This will trigger a test request to the integration, which might be useful also to test the signature verification. See Verifying the event notification request below.

# Build confirmation

After filling the fields needed for the integration, click on the Add integration button. You'll be redirected to the integrations tab in the organization management page, and a confirmation message will be shown.

Integration build confirmation

This message will also feature the aforementioned security fields, client key, client secret, and signing secret. See the security considerations below.

The newly built integration should now appear in the Recommended list and be available for installation. To change the set up of any custom integration, open its context menu and click on Edit.

# Enabling / disabling an integration

If you do not want to receive events from Virtuoso, you can disable your organization's custom integrations.

You may do this either through the integration's context menu or the edition page, by turning off the Enabled switch at the top of the page and then clicking the Update button.

Disabling an integration

Disabling an integration will affect all its installations. If your integrations as been made public, be aware that installations by other organizations will also stop working.

# Security considerations

The client key and client secret pair is used to identify and authenticate the integration within Virtuoso, while the signing secret is used to validate if the events originated in Virtuoso and weren't tampered with.

For security reasons, the client secret and the signing secret are only shown to you at the time of their creation. You should copy the values and store them in a secure location so that your custom application can access and use them when needed. You can, however, generate a new client secret or signing secret at any time through the edition page, invalidating the previous ones for that integration.

Integration client key / secret

These credentials can be used to trigger actions in Virtuoso. Please, keep them secure and be careful with whom you share them with.

Note that we will never require you to send the client secret in a request. This is used only locally to sign each of the messages that you send.

# Talking with Virtuoso

A common information flow for an integration would be described by the following diagram:

Integration flow

  1. Upon first installation, Virtuoso will send an installation notification event to the integration when an organization installs it, containing the installationId. Important: for Virtuoso to consider the installation successful, the integration must respond with a 2XX HTTP status code.
  2. After an integration is installed, when any of the events that the integration has subscribed to is triggered, Virtuoso will send the event to integration's webhook. Each event notification request is structured as shown below.
  3. If upon receiving the event, you need more information from Virtuoso, the integration can use the Virtuoso API to retrieve it. Virtuoso offers a simple and secure way for integrations to obtain authentication tokens on the fly.

# Integration installation notification event

When the integration has the Webhook URL defined, it will automatically receive install and uninstall integration events. It must send a 2XX response to the install integration event in order for the action to be accepted by Virtuoso.

Example of an installation notification event request
curl --location --request POST '<your-webhook-url>' \
--header 'Content-Type: application/json' \
--header 'X-Virtuoso-Request-Timestamp: 1590064214' \
--header 'X-Virtuoso-Signature: f206dfcf0cd63d23b4a287d9b85a5ed4cc39b2fb85b87469f1d93673cc6eff66' \
--data-raw '{
	"installationId": "bd411a74-99ff-4b3e-b5f5-97b0d6b55d8c",
	"user": {
		"userId": 1,
		"organizationId": 1,
		"username": "admin",
		"email": "[email protected]",
		"name": "admin"
	},
	"event":  {
		"kind": "INTEGRATION",
		"eventName": "integration:install",
		"eventCategory": "integration",
		"organizationId": 1,
		"userId": 1,
		"installationId": "bd411a74-99ff-4b3e-b5f5-97b0d6b55d8c",
		"eventType":"INSTALL"
	}
}'

# Virtuoso event notification request

Every event notification sent by Virtuoso will provide the necessary information to allow the integration to react accordingly.

An event notification payload contains the following fields:

  • installationId: The unique integration installation identifier associated with the organization;
  • event: An object representing the event (for more information see Available virtuoso events).
  • user (optional): An object containing information like the username, name, and email;
Example of an event notification request - plan execution finished
curl --location --request POST '<your-webhook-url>' \
--header 'Content-Type: application/json' \
--header 'X-Virtuoso-Request-Timestamp: 1590064214' \
--header 'X-Virtuoso-Signature: f206dfcf0cd63d23b4a287d9b85a5ed4cc39b2fb85b87469f1d93673cc6eff66' \
--data-raw '{
	"installationId": "bd411a74-99ff-4b3e-b5f5-97b0d6b55d8c",
	"event":  {
        "kind": "PLAN_EXECUTION",
        "eventName": "execution_plan:execution_finished",
        "eventCategory": "execution_plan",
		"organizationId": 1,
		"userId": 1,
		"projectId": 1,
		"planId": 1,
		"eventType":"EXECUTION_FINISHED"
	}
	"user": {
		"userId": 1,
		"organizationId": 1,
		"username": "member",
		"email": "[email protected]",
		"name": "SpotQA Member"
	},
}'

# Verifying the event notification request

Virtuoso uses the signing secret to create a signature that is sent as header (X-Virtuoso-Signature) in every event notification request that Virtuoso makes. By comparing the signature received with the signature that your integration computes you can ensure that the request originated in Virtuoso, who is the only entity who has access to the signing secret besides the integration.

The signature is computed from the $timestamp:$payload string and signed using the integration signing secret. The code examples below show you how to verify it.

Java
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class Signer {

  private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";

  public static String sign(String textToSign, String secret)
      throws NoSuchAlgorithmException, InvalidKeyException {

    var decodedSecret = new SecretKeySpec(Base64.getDecoder().decode(secret),
        HMAC_SHA256_ALGORITHM);
    Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
    mac.init(decodedSecret);

    StringBuilder sb = new StringBuilder();
    for (byte b : mac.doFinal(textToSign.getBytes())) {
      sb.append(String.format("%02x", b));
    }

    return sb.toString();
  }

  public static boolean verify(String timeInMillis, String payload, String secret,
      String receivedSignature) throws InvalidKeyException, NoSuchAlgorithmException {

    String textToSign = String.format("%s:%s", timeInMillis, payload);
    String generatedSignature = Signer.sign(textToSign, secret);

    return generatedSignature.equals(receivedSignature);
  }

  public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException {
    final String SIGNING_SECRET = "K2eWUZiDp+EREJNLFrk6vFIB6augrXnPx+XOvq+X6uw=";

    // Content of the "X-Virtuoso-Request-Timestamp" header
    String timeInMillis = String.valueOf(System.currentTimeMillis());

    // Request payload
    var payload = "A json containing the event data";

    String textToSign = String.format("%s:%s", timeInMillis, payload);

    // Content of the "X-Virtuoso-Signature" header
    String receivedSignature = Signer.sign(textToSign, SIGNING_SECRET);

    if (Signer.verify(timeInMillis, payload, SIGNING_SECRET, receivedSignature)) {
      System.out.println("Request is valid");
    } else {
      System.out.println("Request is invalid");
    }
  }
}
Javascript
const crypto = require('crypto');

function sign(textToSign, secret) {
  const signature = crypto.createHmac('SHA256', Buffer.from(secret, 'base64'))
    .update(textToSign)
    .digest('hex');

  return signature
}

function verify(timeInMillis, payload, secret, receivedSignature) {
  const textToSign = `${timeInMillis}:${payload}`
  const generatedSignature = sign(textToSign, secret);

  return generatedSignature === receivedSignature
}

const SIGNING_SECRET = 'K2eWUZiDp+EREJNLFrk6vFIB6augrXnPx+XOvq+X6uw=';

// Content of the "X-Virtuoso-Request-Timestamp" header
const timeInMillis = Date.now();

// Request payload
const payload = 'A json containing the event data';

const textToSign = `${timeInMillis}:${payload}`;

// Content of the "X-Virtuoso-Signature" header
const receivedSignature = sign(textToSign, SIGNING_SECRET);

if (verify(timeInMillis, payload, SIGNING_SECRET, receivedSignature)) {
  console.log('Request is valid');
} else {
  console.log('Request is invalid');
}

# Authenticating an integration in Virtuoso

To access the context of the organization where the integration is installed (e.g., to read data or perform actions), you need to authenticate first.

The request payload must contain the following information:

  • installationId (UUID): You receive this in every event (including the installation event)
  • clientKey (String): This is provided to you in the integration configuration page.
  • timeInMillis (Long): Current epoch in millis.
  • signature (string value of HMacSha256): See the section below on how this is computed.
Example authentication request (payload and response)
curl --location --request POST 'https://api.virtuoso.qa/api/auth/login/integration?envelope=false' \
--header 'Content-Type: application/json' \
--data-raw '{
    "installationId": "<installation identifier>",
    "clientKey": "<integration client key>",
    "timeInMillis": <time in millis used in the signature>,
    "signature": "<signed string>"
}'

Response:
{
    "success": true,
    "item": {
        "token": "5ca64217-9f05-49e9-8b03-b77c8d58c2f4",
        "expires": "2020-04-07 15:15:58.554"
    }
}

# Generating the authentication signature

The authentication signature is computed from the following string: $installationId:$timeMillis:$clientKey signed using the integration client secret.

Note that the client key is a binary value that is base64 encoded, so you may have to decode this before passing it to your signature function; but fear not, we have code examples below, in different programming languages, to show you how.

Java

See Verifying the event notification request for the definition of the Signer class.

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class Authenticator {

  public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
    final String INSTALLATION_ID = "32ad2b70-a520-4765-98ff-764d482aaf6e";
    final String CLIENT_KEY = "ApZuVA9s1TS27Ngy5vrE1Fa7jtyBwV40";
    final String CLIENT_SECRET = "F/eromwqZl3b4P1imIo8uNi9MBMiVAnsCSScI0oFHrY=";

    Long timeInMillis = System.currentTimeMillis();
    String textToSign = String.format("%s:%s:%s", INSTALLATION_ID, timeInMillis, CLIENT_KEY);

    // Signer is the same class used in the example for "Verifying the event notification request"
    String signature = Signer.sign(textToSign, CLIENT_SECRET);

    System.out.println("signature value is: " + signature);
  }
}
Javascript

See Verifying the event notification request for the definition of the sign function.

const INSTALLATION_ID = "32ad2b70-a520-4765-98ff-764d482aaf6e";
const CLIENT_KEY = "ApZuVA9s1TS27Ngy5vrE1Fa7jtyBwV40";
const CLIENT_SECRET = "F/eromwqZl3b4P1imIo8uNi9MBMiVAnsCSScI0oFHrY=";

const timeInMillis = Date.now();
const textToSign = `${INSTALLATION_ID}:${timeInMillis}:${CLIENT_KEY}`

// sign is the same function used in the example for "Verifying the event notification request"
console.log(`signature value is:: ${sign(textToSign, CLIENT_SECRET)}`)
Golang
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
    "encoding/base64"
	"fmt"
	"time"
)

func main() {
	installationId := "32ad2b70-a520-4765-98ff-764d482aaf6e"
	timeInMillis := time.Now().Unix() * 1000
	clientKey := "ApZuVA9s1TS27Ngy5vrE1Fa7jtyBwV40"
	secretKey := "F/eromwqZl3b4P1imIo8uNi9MBMiVAnsCSScI0oFHrY="

    decodedSecret, _ := base64.StdEncoding.DecodeString(secretKey)
	signature := hmac.New(sha256.New, decodedSecret)
	signature.Write([]byte(fmt.Sprintf("%s:%d:%s", installationId, timeInMillis, clientKey)))

	stringValue := hex.EncodeToString(signature.Sum(nil))
	fmt.Println("signature value is: " + stringValue)
}
Python
import time
import base64
import hmac
import hashlib

def HmacSha256(secret, content) : 
   return hmac.new(base64.b64decode(str.encode(secret)),content.encode(), hashlib.sha256)      .hexdigest()

installationId = "32ad2b70-a520-4765-98ff-764d482aaf6e"
timeInMillis = int(round(time.time() * 1000))
clientKey = "ApZuVA9s1TS27Ngy5vrE1Fa7jtyBwV40"
secretKey = "F/eromwqZl3b4P1imIo8uNi9MBMiVAnsCSScI0oFHrY="

signature = HmacSha256(secretKey, "%s:%s:%s" % (installationId, timeInMillis, clientKey))   

print ("signature value is: " + signature)
Ruby
require 'openssl'
require 'base64'

installationId = "32ad2b70-a520-4765-98ff-764d482aaf6e"
timeInMillis = 	(Time.now.to_f * 1000).floor.to_s
clientKey = "ApZuVA9s1TS27Ngy5vrE1Fa7jtyBwV40"
secretKey = "F/eromwqZl3b4P1imIo8uNi9MBMiVAnsCSScI0oFHrY="

signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), Base64.decode64(secretKey), installationId + ":" + timeInMillis.to_s + ":" + clientKey)

puts("signature value is: " + signature)
Shell / Bash
installationId="32ad2b70-a520-4765-98ff-764d482aaf6e"
timeInMillis=$(echo $(($(date +%s%N)/1000000)))
clientKey="ApZuVA9s1TS27Ngy5vrE1Fa7jtyBwV40"
secretKey="F/eromwqZl3b4P1imIo8uNi9MBMiVAnsCSScI0oFHrY="

hexSecretKey=$(echo $secretKey | base64 -d -i | hexdump -v -e '/1 "%02x" ' )
signature=$(echo -n $installationId:$timeInMillis:$clientKey | openssl dgst -sha256 -mac HMAC -macopt hexkey:$hexSecretKey )

echo "signature value is: $signature"
Google Script (e.g., Google Sheets)
function loginAndGetToken(installationId){
  
  var clientKey = "APP_CLIENT_KEY"
  var clientSecretBinary = "APP_CLIENT_SECRET"
  
  var timeMillis = Number(new Date().getTime()).toFixed(0)
  var signatureStringBinary = Utilities.base64Decode(Utilities.base64Encode(`${installationId}:${timeMillis}:${clientKey}`))
  
  var signature = Utilities.computeHmacSha256Signature(signatureStringBinary, Utilities.base64Decode(clientSecretBinary)).reduce(function(str,chr){
    chr = (chr < 0 ? chr + 256 : chr).toString(16);
    return str + (chr.length==1?'0':'') + chr;
  },'')
    
  var payload = {
    "installationId": installationId,
    "clientKey": clientKey,
    "timeInMillis": timeMillis,
    "signature": signature
  }
  
  var res = UrlFetchApp.fetch(
    `https://api.virtuoso.qa/api/auth/login/integration?envelope=false`,
    {
      method             : 'post',
      contentType        : 'application/json',
      payload            : JSON.stringify(payload),
      muteHttpExceptions : true,
    })
  
  Logger.log(`Virtuoso Integration Login status: ${res.getResponseCode()}`)
  
  return JSON.parse(res.getContentText()).token
}

# Available virtuoso events

Below is the list of all Virtuoso events available for subscription. When a subscribed event occurs, a request is sent to the Webhook URL with a payload which identifies the type of event and all the information associated with it.

List of events
  • project:created
  • project:updated
  • project:archived
  • project:restored
  • project:access_level_updated
  • goal:created
  • goal:updated
  • goal:archived
  • goal:restored
  • exploration:launched
  • exploration:started
  • exploration:finished
  • execution:launched
  • execution:started
  • execution:finished
  • sync:launched
  • sync:started
  • sync:finished
  • graph_sync:launched
  • graph_sync:started
  • graph_sync:finished
  • snapshot:created
  • snapshot:updated
  • snapshot:archived
  • snapshot:restored
  • snapshot:committed
  • snapshot:rolled_back
  • test_suite:created
  • test_suite:updated
  • test_suite:deleted
  • test_suite:user_list_changed
  • test_case:updated
  • test_case:archived
  • test_step:updated
  • webhook:created
  • webhook:updated
  • webhook:deleted
  • webhook:archived
  • webhook:restored
  • webhook_delivery:created
  • test_data_table:created
  • test_data_table:updated
  • test_data_table:deleted
  • test_data_table:values_updated
  • test_data_attribute:created
  • test_data_attribute:updated
  • test_data_attribute:deleted
  • failure:snapshot_goal_import
  • goal_snapshot:imported
  • test_data_attribute:created
  • test_data_attribute:updated
  • test_data_attribute:deleted
  • script:created
  • script:updated
  • script:deleted
  • execution_plan:created
  • execution_plan:updated
  • execution_plan:archived
  • execution_plan:restored
  • execution_plan:deleted
  • execution_plan:execution_finished

# Available target menus for integration actions

The following list contains all Virtuoso menus that can hold integration actions. When you perform an action, a request is sent to the Request URL with a payload that identifies the action and contain information associated with it.

Checkpoint
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "event": {
    "kind": "CHECKPOINT_INTEGRATION_ACTION",
    "timestamp": 1593191530755,
    "callbackId": "callbackId",
    "checkpoints": [
      {
        "id": 5632,
        "snapshotId": 12268,
        "goalId": 1,
        "name": "TC1",
        "title": "Go to Dynamic Graph",
        "archived": false,
        "clonedFrom": 5625,
        "canonicalId": "be92094f-c313-4913-a618-f9e4dfa9c20c"
      }
    ],
    "eventType": "CHECKPOINT",
    "eventCategory": "action",
    "eventName": "action:checkpoint"
  },
  "configuration": {
    "Configuration field name": "https://my.config.url"
  },
  "payload": {
    "a name": "User text"
  }
}
Execution
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "event": {
    "kind": "EXECUTION_INTEGRATION_ACTION",
    "timestamp": 1593196341124,
    "callbackId": "callbackId",
    "job": {
      "id": 7870,
      "snapshotId": 12236,
      "goalId": 2,
      "status": 3,
      "submitDate": 1592325096157,
      "startDate": 1592325096199,
      "endDate": 1592325108657,
      "launchedBy": 254,
      "triggerType": 1,
      "type": "EXECUTION",
      "statisticsCalculated": true
    },
    "journeyIds": [],
    "eventType": "EXECUTION",
    "eventCategory": "action",
    "eventName": "action:execution"
  },
  "configuration": {
    "Configuration field name": "https://my.config.url"
  },
  "payload": {
    "a name": "User text"
  }
}
Goal item
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "event": {
    "kind": "GOAL_INTEGRATION_ACTION",
    "timestamp": 1593191324659,
    "callbackId": "callbackId",
    "project": {
      "id": 1,
      "name": "Test Project",
      "archived": false
    },
    "goals": [
      {
        "id": 1,
        "projectId": 1,
        "name": "Desktop Functional",
        "archived": false
      }
    ],
    "eventType": "GOAL",
    "eventCategory": "action",
    "eventName": "action:goal"
  },
  "configuration": {
    "Configuration field name": "https://my.config.url"
  }
}
Goals
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "event": {
    "kind": "GOAL_INTEGRATION_ACTION",
    "timestamp": 1593191736477,
    "callbackId": "callbackId",
    "project": {
      "id": 1,
      "name": "Test Project",
      "archived": false
    },
    "goals": [],
    "eventType": "GOAL",
    "eventCategory": "action",
    "eventName": "action:goal"
  },
  "configuration": {
    "Configuration field name": "https://my.config.url"
  },
  "payload": {
    "a name": "User text"
  }
}
Extension item
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "event": {
    "kind": "EXTENSION_INTEGRATION_ACTION",
    "timestamp": 1593196167117,
    "callbackId": "callbackId",
    "project": {
      "id": 1,
      "name": "Test Project",
      "archived": false
    },
    "extensions": [
      {
        "id": 414,
        "name": "org script",
        "script": "TextDecoderStream",
        "inputs": [],
        "modifiedDate": 1580987833272,
        "async": false,
        "directive": false,
        "resources": []
      }
    ],
    "eventType": "EXTENSION",
    "eventCategory": "action",
    "eventName": "action:extension"
  },
  "configuration": {
    "Configuration field name": "https://my.config.url"
  },
  "payload": {
    "a name": "User text"
  }
}
Extensions
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "event": {
    "kind": "EXTENSION_INTEGRATION_ACTION",
    "timestamp": 1593196175795,
    "callbackId": "callbackId",
    "project": {
      "id": 1,
      "name": "Test Project",
      "archived": false
    },
    "extensions": [],
    "eventType": "EXTENSION",
    "eventCategory": "action",
    "eventName": "action:extension"
  },
  "configuration": {
    "Configuration field name": "https://my.config.url"
  },
  "payload": {
    "a name": "User text"
  }
}
Journey
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "event": {
    "kind": "JOURNEY_INTEGRATION_ACTION",
    "timestamp": 1593191523783,
    "callbackId": "callbackId",
    "journey": {
      "id": 8010,
      "snapshotId": 12268,
      "goalId": 1,
      "name": "Suite 1",
      "title": "Dynamic Graph 0-2",
      "archived": false,
      "canonicalId": "552d004b-d0b1-46c3-ab1a-10eafad8b192",
      "tags": []
    },
    "eventType": "JOURNEY",
    "eventCategory": "action",
    "eventName": "action:journey"
  },
  "configuration": {
    "Configuration field name": "https://my.config.url"
  },
  "payload": {
    "a name": "User text"
  }
}
Journey execution
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "action": {
    "interactionConfig": {
      "id": 2817,
      "name": "Journey execution action",
      "type": "JOURNEY_EXECUTION",
      "callbackId": "callbackId"
    },
    "journeyId": 7971,
    "executionId": 7804,
    "kind": "JOURNEY_EXECUTION"
  }
}
Plan item
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "event": {
    "kind": "PLAN_INTEGRATION_ACTION",
    "timestamp": 1593196278131,
    "callbackId": "callbackId",
    "project": {
      "id": 1,
      "name": "Test Project",
      "archived": false
    },
    "plans": [
      {
        "id": 265,
        "goalId": 1,
        "snapshotId": 4,
        "name": "Simple plan",
        "createdDate": 1571133589021,
        "modifiedBy": 254,
        "modifiedDate": 1591031942899,
        "archived": false,
        "dataDriven": false,
        "uuid": "1eb96220-8770-4530-ac17-10786f453882"
      }
    ],
    "eventType": "PLAN",
    "eventCategory": "action",
    "eventName": "action:plan"
  },
  "configuration": {
    "Configuration field name": "https://my.config.url"
  },
  "payload": {
    "a name": "User text"
  }
}
Plans
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "event": {
    "kind": "PLAN_INTEGRATION_ACTION",
    "timestamp": 1593196271185,
    "callbackId": "callbackId",
    "project": {
      "id": 1,
      "name": "Test Project",
      "archived": false
    },
    "plans": [],
    "eventType": "PLAN",
    "eventCategory": "action",
    "eventName": "action:plan"
  },
  "configuration": {
    "Configuration field name": "https://my.config.url"
  },
  "payload": {
    "a name": "User text"
  }
}
Project
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "event": {
    "kind": "PROJECT_INTEGRATION_ACTION",
    "timestamp": 1593189691024,
    "callbackId": "callbackId",
    "project": {
      "id": 1,
      "name": "Test Project",
      "archived": false
    },
    "eventType": "PROJECT",
    "eventCategory": "action",
    "eventName": "action:project"
  },
  "configuration": {
    "Configuration field name": "https://my.config.url"
  },
  "payload": {
    "a name": "User text"
  }
}
Test data table item
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "event": {
    "kind": "DATA_TABLE_INTEGRATION_ACTION",
    "timestamp": 1593196189579,
    "callbackId": "callbackId",
    "project": {
      "id": 1,
      "name": "Test Project",
      "archived": false
    },
    "dataTables": [
      {
        "id": 1285,
        "projectId": 1,
        "name": "Created from csv",
        "title": ""
      }
    ],
    "eventType": "DATA_TABLE",
    "eventCategory": "action",
    "eventName": "action:data_table"
  },
  "configuration": {
    "Configuration field name": "https://my.config.url"
  },
  "payload": {
    "a name": "User text"
  }
}
Test data tables
{
  "installationId": "c9cfe1b1-a71e-4597-adc2-5011d0a15172",
  "user": {
    "userId": 1,
    "username": "admin",
    "email": "[email protected]",
    "name": "Admin",
    "avatar": "",
    "active": true
  },
  "event": {
    "kind": "DATA_TABLE_INTEGRATION_ACTION",
    "timestamp": 1593196182601,
    "callbackId": "callbackId",
    "project": {
      "id": 1,
      "name": "Test Project",
      "archived": false
    },
    "dataTables": [],
    "eventType": "DATA_TABLE",
    "eventCategory": "action",
    "eventName": "action:data_table"
  },
  "configuration": {
    "Configuration field name": "https://my.config.url"
  },
  "payload": {
    "a name": "User text"
  }
}
Last Updated: 7/2/2020, 10:38:17 AM