Building a Connector


Hull Connectors

Connectors (previously called “Ships”) are the preferred way for developers to integrate external services with Hull.

Introduction

Connectors make it easy for anyone to build and publish reusable connectors. Connectors are standalone, framework-agnostic, externally hosted apps that interact with Hull APIs.

They can have client and server components. They take care of embedding client code, managing settings and lifecycle and data flow.

Server-side connectors have realtime read-write access to Hull. They do not require any local storage and are stateless by default. They subscribe to Hull, receive data in realtime along with all the needed credentials to act upon it and write data back to hull. Our open source connectors show how this can be use d to integrate with any third-party app.

Client-side connectors can be embedded in any website where hull.js is installed from a simple Point-n-click interface.

You build a Ship by describing the the information needed to run your application and the settings exposed to your users to configure it in a file called manifest.json at the root of your project.

It is then used by Hull to automatically build the admin section in Hull’s Dashboard, expose settings, fetch and embed client-side code, and send events to server-side code.

Our Connectors Monorepo has several examples of open-source Connectors

File Structure

Connectors can have any combination of Server-side code and Client-side code, or both.

Server-side

Checkout The minimal setup for a server-side connector:

  • A package.json setting up the npm start command to launch a web server.
  • A manifest.json file, with a subscriptions entry defining an HTTP endpoint where Hull will send events.
minimal-connector
├── server.js
├── package.json
└── manifest.json

package.json

{
  ...
  "dependencies": {
    "express": "^4.13.3",
    "hull": "^0.5.4"
  },
  "scripts": {
    "start": "node index.js"
  }
}

manifest.json

{
  "name": "Connector Name",
  "description": "Your connector's description",
  "tags": [
    //Activate support for User manual Replays and Account manual Replays
    "batch",
    "batch-accounts",
    //Leave as-is, internal tags
    "smart-notifier",
    "kraken-exports"
  ],
  "logo": "logo.png",
  "picture": "picture.png",
  // Link to Docs
  "readme": "readme.md",
  "version": "0.3.0",

  "private_settings": [
    {
      // See Docs below
      "name": "synchronized_user_segments"
      "type": "array",
      "format": "segment"
    },
    ... See Docs below
  ],
  "settings_sections": [
    ... See Docs below
  ],
  "json": [
    {
      // Exposed Endpoint
      "url": "/config",
      // Method that will be called - setup in `handler.js`
      "handler": "configHandler",
      // How endpoint is configured
      "options": {
        "cacheContextFetch": false
      }
    },
    ...
  ],
  "subscriptions": [
    {
      // Exposed Hull Subscription
      "url": "/hull-ingest",
      "conditions": {
        "channels": {
          // Restrict which types of messages will be sent
          "only": [ "user:update", "account:update" ]
        },
        // Restrict which users and accounts will be sent by relying on Segments
        "segments": {
          "user:update": "private_settings.synchronized_user_segments"
        }
      },
      "channels": [
        {
          //Define Handlers for the different types of messages
          "channel": "user:update",
          //Method Name in handlers.js
          "handler": "userUpdate"
        }
      ]
    }
  ]
}

config.js

// @flow

import type { HullConnectorConfig } from "hull";
import manifest from "../manifest.json";
import fetchToken from "./lib/fetch-token";
import handlers from "./handlers";

export default function connectorConfig(): HullConnectorConfig {
  const {
    LOG_LEVEL,
    SECRET,
    NODE_ENV,
    PORT = 8082,
    OVERRIDE_FIREHOSE_URL
  } = process.env;

  const hostSecret = SECRET || "1234";

  return {
    manifest,
    hostSecret,
    devMode: NODE_ENV === "development",
    port: PORT || 8082,
    handlers: handlers,
    middlewares: [fetchToken],
    cacheConfig: {
      store: "memory",
      ttl: 1
    },
    logsConfig: {
      logLevel: LOG_LEVEL
    },
    clientConfig: {
      firehoseUrl: OVERRIDE_FIREHOSE_URL
    },
    serverConfig: {
      start: true
    }
  };
}

handlers.js

// @flow
import type { HullHandlersConfiguration, Connector } from "hull";

import incomingHandler from "./incoming-handler";
import credentialsHandler from "./credentials-handler";
import statusHandler from "./status-handler";

const handler = async (connector: Connector): HullHandlersConfiguration => {
  return {
    statuses: { statusHandler },
    incoming: { incomingHandler },
    json: {
      credentialsHandler
    }
  };
};

export default handler;

index.js

Booting the connector:

// @flow

import Hull from "hull";
import config from "./config";

new Hull.Connector(config).start();

Client-side

The minimal setup for a simple Ship:

  • A javascript file (let’s call it ship.js), that calls Hull.onEmbed() if you’re using the Hull.js library to embed it;
  • A manifest.json file with an index entry, referencing the javascript file.

Connectors are attached to DOM nodes targeted by a CSS selector. This CSS selector is defined by from inside the Dashboard. See below how Developers can define default values for them.

Hull.js will load the file, and call your callback once for each maching element in the page.

minimal-connector
├── ship.js
└── manifest.json

manifest.json

{
  "name": "minimal-connector",
  "version": "0.1.0",
  "index": "ship.js"
}

ship.js

Hull.onEmbed(function(element, deployment, hull) {
  //Start your connector here
});

Booting your application, client-side

hull.js manages the lifecycle of your Ship when it’s embedded. If your app needs to run some initialization code, you can register a callback function that will be called by hull.js when your app is ready to start.

hull.js has a method called Hull.onEmbed() that you can call in your application code to register this init callback.

For best results, please ensure you’re always using the hull instance that’s passed to you in the callback of Hull.onEmbed(), not the global one.

Example:

<h1>Hello from Minimal Ship</h1>
<script>
  Hull.onEmbed(function(element, deployment, hull) {
    console.log('Hello, i am a Ship and i just started');
    console.log('Here is my root element : ', element);
    console.log('and here is my environment: ', deployment);

    Hull.track("event") //BAD : We're using the global Hull instead of the local one.
    hull.track("event"); //GOOD
    hull.share(...) //GOOD. Notice we're using hull instead of Hull

  });
</script>

This callback’s first argument is the root DOM node of your Ship and the second argument is a Deployment object that represents the instance of your Ship in the page.

It contains your actual Ship object along with its resources and settings, and the context where the Ship is embedded.

The third object is a full copy of the Hull object, customized for your connector instance. This is what you should use from now on.

Here is an example deployment :

{
  "settings": {
    // Those are Deployment-specific settings.
    // The Ship also has Ship-level settings.
    //
    // The settings below are always present. You can declare more
    // in the "deployment_settings" hash of the Manifest.
    "_multi": false, //wether to embed on every matched selector or just the first
    "_placement": "replace", //bottom|top|before|after|replace, where to embed when selector is found
    "_selector": "#my-ship" //css3 selector where to insert connector
    // ...
  },
  "ship": {
    "id": "554b7b05f89a87a527000180",
    "name": "My Simple Ship",
    "manifest": {
      index: '/ship.js'
      //... Entire manifest.json
    },
    "settings": { ... }, //Configured Settings that are described in manifest
    "resources": { ... }, //configured Resources, such as Quizzes
    "translations" : { ... },
    "manifest_url": "https://example.com/my-connector/manifest.json",
    "index": "https://example.com/my-connector/ship.js",
    ...
  },
  "platform": {
    //... Platform Data
  },
  "organization": {
    //... Organization Data
  }
}

Building Connectors

Tooling

To make development and maintenance of connectors easier Hull provides a set of NodeJS development tools and libraries. These are the same projects we are using to build all of our official connectors. To start a new connector, we recommend you checkout the monorepo, and start by copying an existing connector in the packages/connectors folder.

Triggering notifications on the connector overview

Every error level logging call allows to trigger a notification which is sent to the platform via a logger analyzer. You can add a field called hull_summary to the data payload in the log, to specify a user-facing error message. which will be shown to a customer inside the dashboard or via emails:

ctx.client.asUser(claims).logger.error("outgoing.user.error"), {
  hull_summary: "We consumed too many API calls"
});

Note: Only connectors deployed by the Hull team will have the credentials needed to collect this data. Contact us if you want this to work

metrics

Two metrics will be shown on the dashboard: Service API calls and Service API remaining calls. This is how you can increment them in your code:

This are metric names and values for the Instrumentation service

ctx.metric.increment("ship.service_api.remaining", undefined | number);
ctx.metric.value("ship.service_api.call", undefined | number);

Note: Only connectors deployed by the Hull team will have the credentials needed to collect this data. Contact us if you want this to work

Connector Settings

When Building Connectors you will want give users a way to edit some settings. The manifest.json lets you define a list of settings to display. It is also used to automatically build the UI for them in Hull’s Dashboard.

manifest.json

{
  "name" : "My Ship",
  "tags" : [
    "batch",
    "batch-accounts",
    "smart-notifier"
    "kraken-exports"
  ],
  "description": "This achieves world peace",
  "version" : "0.1.0",
  "picture" : "picture.png",
  "readme" : "readme.md",
  "index" : "ship.js",
  "subscriptions" : [ {
    "url" : "/notify",
    "conditions" : {
      "channels": {
        "only": ["user:update", "account:update"]
      },
      "segments": {
        "user:update": ["private_settings.synchronized_user_segments"],
        "account:update": ["private_settings.synchronized_account_segments"]
      }
    }
  } ],
  "schedules": [
    {
      "url": "/sync",
      "type": "interval",
      "value_from": "private_settings.sync_interval", //takes the actual value from this setting
      "default": "30"
    }
  ],
  "settings" : [
    {
      "name"           : "background_color",
      "title"          : "Background Color",
      "description"    : "The Background color of your Ship",
      "type"           : "string",
      "format"         : "color",
      "default"        : "#ffffff"
    },
    {
      "name"           : "logo_url",
      "title"          : "Logo",
      "description"    : "Upload your logo",
      "type"           : "string",
      "format"         : "image",
      "default"        : "http://url.to/my/default/image.jpg"
    },
    {
       "name"          : "awesome",
       "title"         : "Enable Awesomeness",
       "description"   : "Isn't it pretty neat ?",
       "type"          : "boolean",
       "default"       : true
    }
  ],
  "private_settings" : [
    {
      "name" : "api_key",
      "description" : "Super secret API Key",
      "type" : "string"
    }
  ],
  "settings_sections": [
    { "title": "Setup", "properties": ["private_settings.api_key"] }
  ],
  "deployment_settings": [],
  "subscriptions" : [ { "url" : "/notify" } ],
  "html": [
    {
      "title": "Admin Page",
      "url": "/admin",
      "handler": "adminHandler",
      "size": "small",
      "workspace": false,
      "setup": false,
      "editable": false
    }
  ],
  "json": [
    {
      "url": "/prospect",
      "handler": "prospectHandler"
    }
  ],
  "status": {
    "url": "/status",
    "initial_value": "setupRequired",
    "type": "interval",
    "handler": "statusHandler",
    "value": "5"
  },
  "schedules": [
    {
      "url": "/sync",
      "type": "interval",
      "value_from": "private_settings.sync_interval", //takes the actual value from this setting
      "default": "30"
    }
  ]
}

This settings structure is based on JSON Schema with a few additions for custom formats and validation (Check the Manifest reference section for a full list of options).

Common settings

name

required String.

A name for the Ship. Used for the registry.

tags

optional array

A list of tags describing what the Ship can do. Recognized tags:

  • incoming : Receives data from external service
  • outgoing : Sends data to external service
  • batch : Can process users in Batches
  • client : Has Client-side code
  • wideSettings : Enlarges the Sidebar to make more room for the Settings
  • oneColumn : One column Layout

description

optional String.

A description for the Ship. Used for the registry.

version

required String.

The version of your Ship.

picture

optional String.

A relative path to a picture for the Ship. Used for the registry.

tabs

optional Array of Objects

The Tabs array defines the custom tabs to show in the dashboard. You can add one or more tabs as objects with the following format:

"tabs": [
  {
    "title": "Credentials", //The name of the tab
    "url": "admin.html", //The url to load, from the root of the connector, `id`, `secret`, `organization` will be appended so yo can use the Hull Middleware to authenticate
    "size": "small", //supports "small" | "large" | "borderless"
    "editable": false //Boolean defining if a save button will be shown.
    "setup": false //Boolean defining wether this is a Setup / Credentials tab
    "workspace": false //Boolean defining wether this is a work tab (Query editor et. al.)
  }
]

messaging between tab and dashboard

If you have user controls in the custom tab, you can pass data to the Dashboard like this:

  • To send an updated ship configuration to the parent
if (window.parent) {
  window.parent.postMessage(
    JSON.stringify({
      from: "embedded-ship",
      action: "update",
      ship: { private_settings: { code } }
    }),
    "*"
  );
}

When sent this way, the dashboard will detect if the state has changed, and will activate the save button if you display it.

  • To listen to the parent and retrieve the latest Ship configuration
window.addEventListener("message", event => {
  const message = event.data;
  console.log("UPDATING", message.ship.private_settings.query);
  if (message) {
    cont { from, action, ship } = message;
    //from -> hull-dashboard
    //action -> update
    if (from === "hull-dashboard" && action === "update") {
      console.log("Ship Updated", ship);
    }
  }
});

readme

optional String.

A relative path to a markdown file explaining how the connector works, and it’s benefits. Will be shown in the dashboard to help users if available

ui

optional Boolean.

A flag to indicate Hull that there’s no UI to be displayed for a given connector. It’s different from not having client-side code at all. For instance, Connectors only performing tracking will not have a user-facing interface, hence should not show a preview. This flag makes it possible to reflect this in the dashboard.

index

required String.

A relative path to the file that will be injected in the website where the Ship is deployed.

handlers

The Manifest exposes different sections that define how requests to the connector will be handled. All these sections have the same base format:

{
  url,
  handler,
  options
}

The options object accepts the following:

//All handlers except Subscriptions
{
  disableErrorHandling?: boolean, //Do we catch errors
  respondWithError?: boolean, //Do we return the error or just a generic message
  fireAndForget?: boolean, //Do we respond immediately and process in the background
  credentialsFromQuery?: boolean, //Do we look in the Querystring & token for credentials
  credentialsFromNotification?: boolean, //Do we look in the payload for credentials
  dropIfConnectorDisabled?: boolean, //Drops the query if the connector is in Disabled State
  format?: "json" | "html", //Override Return format. Auto configured for all handlers
  bodyParser?: "urlencoded" | "json" //Override Input parsing. Auto configured for all handlers
};

//Only for Notification Handler
{
  disableErrorHandling?: boolean,
  maxTime?: number,
  filter?: {
    user_segments?: string,
    account_segments?: string
  },
  maxSize?: number
};

Here’s a list of the supported handler types:

subscriptions

Specifies endpoints on this connector that Hull will send events to.

"subscriptions": [{
  "url" : "/notify",
  "conditions" : {
    "channels": {
      "only": ["user:update", "account:update"]
    },
    "segments": {
      "user:update": ["private_settings.synchronized_user_segments"],
      "account:update": ["private_settings.synchronized_account_segments"]
    }
  }
}]```

The `url` corresponds to the path that will receive the notification payloads.
The `conditions` allows to declare the filtering rules applies to the subscription.

- `channels` is a whitelist of the type of notifications. Possible values include (`user:update`, `account:update`, `segment:update`, `ship:update`)
- `segments` references the setting used to whitelist specific segments. (These only apply to `user:update` and `account:update` channels)

More docs will be released about those soon. For now, look at the [Hubspot Connector](https://github.com/hull/hull-connectors/tree/master/packages/connectors/hull-hubspot)

### json

optional `Array`

A list of endpoints that are going to respond with a JSON object.
They require some form of authentication (i.e. Token or querystring params) and will usually be used by the dashboard to retrieve some data

```json
"json": [
  { "url": "/foo", "handler": "fooHandler" }
]

incoming

optional Array

A list of endpoints that are going to accept incoming data. They require some form of authentication (i.e. Token or querystring params) and will usually be used to collect incoming webhooks

"incoming": [
  { "url": "/foo", "handler": "fooHandler" }
]

html

optional Array

A list of endpoints that are going to return HTML pages. They require some form of authentication (i.e. Token or querystring params) and will usually be used to display custom content

"html": [
  { "url": "/foo", "handler": "fooHandler" }
]

schedules

optional Array

List of endpoints that will be called by Hull on a specified schedule. The “url” is relative to the connectors’s base URL. Optional additional params can be specified.

The endpoint will receive organizatinon, ship, secret variables in the querystring to allow authentication. With hull-node. The Hull middleware makes it automatic and adds a req.hull object with a Hull client and the current ship

"schedules" : [
  {
    "url" : "/sync",
    "type": "interval",
    "handler": "checkExternalServiceHandler",
    "value": "60"
  }
]

status

optional Object

An endpoint that will be called by Hull to check for the connector’s status. The “url” is relative to the connectors’s base URL. Optional additional params can be specified.

The endpoint will receive the full connector settings when queried

The initial_value param is used at the creation of the connector instance to initialize that value.

The Status displayed on the dashboard is the result of the connector updating the platform. This is how you can control what is being displayed

"status" : {
  "url": "/status",
  "initial_value": "setupRequired",
  "type": "interval",
  "handler": "statusHandler",
  "value": "5"
}

in your code, expose a route like so:

// @flow
import type { HullContext, HullStatusResponse } from "hull";

export default async function statusCheck(
  _ctx: HullContext
): HullStatusResponse {
  return { messages: [], status: "ok" };
}

As a result, the connector will be called every 5 minutes, and will reply and update it’s own status on the platform after performing all the checks you defined.

settings

optional Array.

Public settings are accessible from the javascript page, even for anonymous users. They’re right place to put button colors or Images for instances.

private_settings

optional Array.

Settings that are not exposed to end users. Use them for server side integration that need to access private information like credentials for third party services.

HEADS UP: settings vs private_settings

Connectors can have server-side AND client-side code. The settings and private_settings hashes exist to expose the required parameters to each of those parts of the connector. It means the Settings field is used to store things you’re OK to expose to the entire world. Examples of this would be the public key that you need to use in the client-side scripts of your website. For anything that must not be exposed to the world, use the private_settings hash.

In short: DO NOT STORE SENSITIVE DATA IN THE settings FIELD. USE private_settings instead

deployment_settings

optional Array.

Same as settings, but those settings are scoped to a deployment and not a Ship instance. When you have multiple platforms, the same connector can be deployed to many platforms. In this case, each deployment has a separate set of those. They are configured from the Platform’s settings screen.

settings_sections

required Array

Once you have defined your fields, you can use the settings_sections array to display them in sections:

{
  "settings_sections": [
    {
      "title": "Setup",
      "step": "credentials",
      "description": "Markdown-formatted description of the section",
      "properties": [
        "private_settings.section_1",
        "private_settings.section_2",
        "private_settings.section_3",
      ]
    }
  ]
}

title

The title of the section

description

A markdown-formatted string that shows at the top of the section

step

A special key that marks this section as a credential section. When the connector status returns setupRequired, only this section will be enabled.

properties

An array of JSON paths that resolve to setting entries

Settings field types

Used to describe settings, deployment_settings and private_settings.

string

{
  "name"        : "title",
  "title"       : "Main Title",
  "description" : "The Main title for your app",
  "type"        : "string",
  "default"     : "Hello",
}

enums

{
  "name"        : "position",
  "type"        : "string",
  "enum"        : ["top", "bottom", "left", "right"],
  "default"     : "top"
}

boolean

{
  "name"        : "is_happy",
  "type"        : "boolean",
  "default"     : true
}

number

{
  "name"        : "points",
  "type"        : "number",
  "default"     : 32
}

Segment

A single Hull User Segment

{
  "name"        : "one_segment",
  "type"        : "string",
  "format"      : "segment"
}

Segment Array

An array of Hull User Segments

{
  "name"        : "many_segments",
  "type"        : "array",
  "format"      : "segment"
}

Account Segment

A single Hull Account Segment

{
  "name"        : "one_account_segment",
  "type"        : "string",
  "format"      : "accountSegment"
}

Account Segment Array

An array of Hull Account Segments

{
  "name"        : "many_account_segments",
  "type"        : "array",
  "format"      : "accountSegment"
}

Select field

Select a value from a list of dynamically loaded options. The fields below inherit from the select field type:

  • User Attribute
  • User Attribute Array
  • Account Attribute Array
  • Account Attribute Array
  • Editable Attribute
  • Editable Account Attribute
  • Attribute Mapping
  • Account Attribute Mapping
{
  "name" : "city",
  "type" : "string",
  "format" : "select",
  "options" : {
    "loadOptions": "/select/cities", //Dynamically load Options from Connector
    "allowCreate": true,
    "placeholder": "Pick a city"
  }
}

Options will be loaded from the “loadOptions” endpoint (relative to the connector’s base URL). The endpoint must allow CORS and return an array of option with the following format :

{
  "options" : [
    { "value" : "1", "label" : "One" },
    { "value" : "2", "label" : "Two" }
  ]
}

Or if you want to statically define options:

{
  "name" : "city",
  "type" : "string",
  "format" : "select",
  "enum": ["value1", "value2"], //Statically defined values and Labels
  "enumNames": ["Label 1", "Label 2"],
  "options" : {
    "allowCreate": true,
    "placeholder": "Pick a city"
  }
}

Options can also be grouped with the following format :

{
  {
    "label" : "Group 1",
    "options" : [
      { "value" : "1", "label" : "One" },
      { "value" : "2", "label" : "Two" }
    ]
  },
  {
    "label" : "Group 2",
    "options" : [
      { "value" : "3", "label" : "Three" },
      { "value" : "4", "label" : "Four" }
    ]
  }
}

Use "type" : "array" to allow the selection of multiple values.

User Attribute

A single, already existing Hull User Attribute Traits are a legacy name for attributes Inherits from select

{
  "name"        : "trait",
  "type"        : "string",
  "format"      : "trait"
  "options": {
    "source": "hubspot" //Scopes the trait to a specific group.
    "placeholder": "Pick a Hull field id" //Customize placeholder text
  }

}

User Attribute Array

An array of already existing Hull User Attributes Traits are a legacy name for attributes Inherits from select

{
  "name"        : "trait",
  "format"      : "trait"
  "type"        : "array",
  "options": {
    "source": "hubspot" //Scopes the trait to a specific group.
    "placeholder": "Pick a Hull field id" //Customize placeholder text
  }
}

Account Attribute

A single, already existing Hull Account Attribute Traits are a legacy name for attributes Inherits from select

{
  "name"        : "one_account_trait",
  "type"        : "string",
  "format"      : "accountTrait"
  "options": {
    "source": "hubspot" //Scopes the trait to a specific group.
    "placeholder": "Pick a Hull field id" //Customize placeholder text
  }

}

Account Attribute Array

An array of already existing Hull Account Attribute Traits are a legacy name for attributes Inherits from select

{
  "name"        : "many_account_traits",
  "type"        : "array",
  "format"      : "accountTrait"
  "options": {
    "source": "hubspot" //Scopes the trait to a specific group.
    "placeholder": "Pick a Hull field id" //Customize placeholder text
  }
}

Editable Attribute

A Hull Attribute that can be picked or created. Note the allowCreate field Traits are a legacy name for attributes Inherits from select

{
  "name"        : "trait",
  "type"        : "string",
  "format"      : "trait",
  "options": {
    "source": "hubspot", //Scopes the trait to a specific group.
    "placeholder": "Enter a Hull field id", //Customize placeholder text
    "allowCreate": true //Allows trait creation
  }
}

Editable Account Attribute

A Hull Attibute that can be picked or created. Note the allowCreate field Inherits from select

{
  "name"        : "trait",
  "type"        : "string",
  "format"      : "accountTrait",
  "options": {
    "source": "hubspot", //Scopes the trait to a specific group.
    "placeholder": "Enter a Hull field id", //Customize placeholder text
    "allowCreate": true //Allows trait creation
  }
}

Attribute Mapping

A powerful field that handles the entire mapping between Hull and a service. It allows you to expose a rich UI that handles all of the complexity of defining the various aspect of attribute mapping. Inherits from select

Outgoing mapping example :

{
  "name": "outgoing_user_attributes",
  "title": "Custom Fields Sync (Hull to Hubspot)",
  "type": "array",
  "format": "traitMapping",
  "options": {
    "direction": "outgoing",
    "showOverwriteToggle": true, //Wether to show an overwrite toggle field
    "placeholder": "Hubspot Field", //Customize placeholder text
    "allowCreate": true,
    "pattern" : "^[0-9-A-Z_]+$", // Regular expression used to validate the newly created custom fields
    "loadOptions": "/schema/contact_properties"
  }
}

Incoming mapping example :

{
  "name"        : "entire_attribute_mapping",
  "type"        : "array",
  "format"      : "traitMapping",
  "options": {
    "direction"  : "incoming", // "incoming" / "outgoing"
    "placeholder": "Enter a Hull field id", //Customize placeholder text
    "showOverwriteToggle": false, //Wether to show an overwrite toggle field
    "allowCreate": true, //wether to allow creating new fields in destination
    "loadOptions": "/schema/contact_properties", //URL to load possible options on service side
    "source": "hubspot", //Scopes the attribute to a specific group. Used on the `hull` side to restrict choices to the source namespace
  }
}

The data will be available in the connector in the following format:

{
  "private_settings": {
    "entire_attribute_mapping": [
      { "hull": "last_name", "service": "lastname", "overwrite": false },
      { "hull": "first_name", "service": "firstname", "overwrite": false }
    ]
  }
}

Account Attribute Mapping

A powerful field that handles the entire mapping between Hull and a service. It allows you to expose a rich UI that handles all of the complexity of defining the various aspect of attribute mapping. Inherits from select

Outgoing mapping example :

{
  "name": "outgoing_user_attributes",
  "title": "Custom Fields Sync (Hull to Hubspot)",
  "type": "array",
  "format": "accountTraitMapping",
  "options": {
    "direction": "outgoing",
    "showOverwriteToggle": true, // Wether to show an overwrite toggle field
    "placeholder": "Hubspot Field", // Customize placeholder text
    "allowCreate": true,
    "pattern" : "^[0-9-A-Z_]+$", // Regular expression used to validate the newly created custom fields
    "loadOptions": "/schema/contact_properties"
  },
  "default": [
    {
      "service": "foo",
      "hull": "my_service/foo",
      "overwrite": false
    }
  ]
}

Incoming mapping example :

{
  "name"        : "entire_attribute_mapping",
  "type"        : "array",
  "format"      : "accountTraitMapping",
  "options": {
    "direction"  : "incoming", // "incoming" / "outgoing"
    "placeholder": "Enter a Hull field id", // Customize placeholder text
    "showOverwriteToggle": false, // Wether to show an overwrite toggle field
    "allowCreate": true, // wether to allow creating new fields in destination
    "loadOptions": "/schema/account_properties", // URL to load possible options on service side
    "source": "hubspot" // Scopes the attribute to a specific group. Used on the `hull` side to restrict choices to the source namespace
  },
  "default": [
    {
      "service": "foo",
      "hull": "hubspot/foo",
      "overwrite": false
    }
  ]
}

Event

A single Hull Event

{
  "name"        : "events",
  "format"      : "event"
  "type"        : "string",
}

Events array

An array of Hull Events

{
  "name"        : "events",
  "format"      : "event"
  "type"        : "array",
}

Tags array

An array of free-format text

{
  "name": "tags_filter",
  "title": "A list of tags to filter",
  "type": "array",
  "format": "tags",
  "items": {
    "type": "string"
  }
}

Non-materialized Segments, Attributes and Events

You can define standard segments and standard events that don’t exist in Hull but can be used by the connector. Into the options node, add the standard property. Items will appears on the top of the list, grouped under the “Built-in Segments” label.

{
  "title": "attribute",
  "name": "Attribute",
  "type": "array",
  "format": "traits",
  "options": {
    "standard": {
      "SEGMENTS": "Segment Names"
    }
  }
},
{
  "title": "segment",
  "name": "Segment List",
  "type": "array",
  "format": "segment",
  "options": {
    "standard": {
      "ALL": "All Users",
      "ANONYMOUS": "Anonymous Users (no email or external_id)",
      "IDENTIFIED": "Identified Users (with email)",
    }
  }
},
{
  "name": "Event List",
  "title": "event",
  "type": "string",
  "format": "event",
  "options": {
    "standard": {
      "HULL_ENTER_SEGMENT": "Entering Segment",
      "HULL_LEAVE_SEGMENT": "Leaving Segment"
    }
  }
}

object

{
  "name"        : "sharing_buttons",
  "type"        : "object",
  "properties"  : {
    "facebook"    : {
      "title"       : "Facebook",
      "type"        : "boolean",
      "default"     : true
    },
    "twitter"     : {
      "title"       : "Twitter",
      "type"        : "boolean",
      "default"     : true
    }
  }
}

table

{
  "name": "footer_links",
  "title": "Footer Links",
  "type": "array",
  "format": "table",
  "items": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "title": "Link Name"
      },
      "url": {
        "type": "string",
        "title": "Link URL",
        "format": "uri"
      }
    },
    "required": [ "name", "url" ]
  },
  "default": [
    {
      "name": "A Link here 1",
      "url": "/"
    },
    {
      "name": "A Link here 2",
      "url": "/"
    }
  ]
}

Oauth

A fully packaged oauth authentication UI component. Will handle the oAuth flow. Check the Hubspot Connector for an implementation example.

In the options object, you will need to specify strategy and name

{
  "name": "oauth",
  "title": "Credentials",
  "description": "Authenticate with Hubspot",
  "format": "oauth",
  "type": "string",
  "handler": "oauth",
  "url": "/auth",
  "options": {
    "name": "Hubspot",
    "strategy": {
      "scope": [
        "oauth",
        "contacts",
        "timeline",
        "content"
      ]
    }
  }
}

button

A button that triggers an action. When clicking, it will call the specified URL, which will tigger the handler. You can ask for confirmation by adding a confirm object in the options

{
  "title": "Fetch All Companies",
  "name": "fetch_all_companies",
  "format": "button",
  "url": "/fetch-all-companies",
  "handler": "fetchAllCompanies",
  "options": {
    "confirm": {
      "fireAndForget": true,
      "action": "fetch",
      "text": "You are about to pull all companies from Hubspot into Hull. Please make sure you've configured your account identity properly, and that you have the attributes you'd like pulled configured. Be careful when clicking this multiple times, it can result in long operations. Check out the \"Logs\" tab to track incoming.account.success messages as accounts are ingested",
      "button": "Start Fetching Companies",
      "entity": "accounts"
    }
  }
}

hidden

Used to store settings that can be manipulated, saved but have no UI to edit. Typically tokens.

{
  "name" : "dont_show_me",
  "description" : "Settings with hidden format are not displayed in the Settings editor",
  "type" : "string",
  "format" : "hidden"
}