Actions de workflow personnalisées

Les workflows de HubSpot sont utilisés pour automatiser les processus commerciaux et permettre aux clients de HubSpot d'être plus efficaces. Vous pouvez créer des actions de workflow personnalisées pour intégrer votre service aux workflows de HubSpot.

Tout d'abord, vous définirez votre action personnalisée, y compris les entrées qui doivent être envoyées par l'utilisateur lors de la création d'un workflow, et l'URL qui sera demandée lorsque l'action personnalisée sera exécutée. Ensuite, lorsque les clients installent votre application, ils peuvent ajouter votre action personnalisée à leurs workflows. Lorsque ces workflows sont exécutés, les demandes HTTPS seront envoyées à l'URL configurée avec la charge utile définie. Cet article explique comment :

Avant de commencer, tenez compte des aspects suivants :

https://api.hubspot.com/automation/v4/actions/{appId}?hapikey={API_key}

Définir votre action personnalisée

Pour créer une action de workflow personnalisée, vous devez définir l'action à l'aide des champs suivants. Cette définition indique également le format de demande pour les demandes provenant de HubSpot, ainsi que le traitement des réponses par votre service.

  • actionUrl : l'URL vers laquelle une demande HTTPS est envoyée lorsque l'action est exécutée. Le corps de la demande contient des informations sur l'utilisateur au nom duquel l'action est exécutée et sur les valeurs saisies pour les champs de saisie.
  • inputFields : ces champs définissent l'ensemble des valeurs valides pour les saisies de l'action. Il est possible de fournir une liste statique ou une URL de webhook. Si une URL de webhook est fournie, les options seront récupérées à partir de cette URL lorsque l'action sera modifiée par un client dans l'outil Workflows. Chaque champ est facultatif.
  • outboundFields : les valeurs résultantes de l'action qui peuvent être utilisées par des actions suivantes dans le workflow. Une action personnalisée peut avoir aucun, un ou plusieurs résultats.
  • executionRules : une liste des définitions que vous pouvez spécifier pour identifier les erreurs de votre service vers l'utilisateur qui crée le workflow.
  • labels : une copie qui décrit pour l'utilisateur ce que représentent les champs de l'action et ce que fait l'action. Des libellés en anglais sont requis, mais les libellés peuvent également être indiqués dans l'une de ces langues prises en charge : français (fr), allemand (de), japonais (ja), espagnol (es), portugais du Brésil (pt-br) et néerlandais (nl). La spécification pour chaque langue comprend les champs suivants :
    • actionName : nom de l'action affiché dans le panneau Sélectionner une action de l'éditeur de workflow.
    • actionDescription : une description détaillée de l'action affichée lorsque l'utilisateur configure l'action personnalisée.
    • actionCardContent : une description récapitulative affichée dans la carte de l'action.
    • inputFieldLabels : un objet qui regroupe les définitions de inputFields vers les libellés correspondants que l'utilisateur verra lors de la configuration de l'action.
    • outputFieldLabels : un objet qui regroupe les définitions de outputFields vers les libellés correspondants indiqués dans l'outil Workflows.
    • inputFieldDescriptions : un objet qui regroupe les définitions de inputFields vers les descriptions sous les libellés correspondants.
    • executionRules : un objet qui regroupe les définitions de executionRules vers des messages d'erreur qui seront affichés pour l'utilisateur si celui-ci ne configure pas correctement une action personnalisée. Ces éléments vous permettent de spécifier des messages personnalisés pour les résultats d'exécution d'action sur l'historique des workflows.

Voici un exemple de définition d'action basique :

// { "actionUrl": "https://example.com/hubspot", "inputFields": [ { "typeDefinition": { "name": "widgetName", "type": "string", "fieldType": "text" }, "supportedValueTypes": ["STATIC_VALUE"], "isRequired": true }, { "typeDefinition": { "name": "widgetOwner", "type": "enumeration", "referencedObjectType": "OWNER" }, "supportedValueTypes": ["STATIC_VALUE"] } ], "labels": { "en": { "actionName": "Create Widget", "actionDescription": "This action will create a new widget in our system. So cool!", "actionCardContent": "Create widget {{widgetName}}", "inputFieldLabels": { "widgetName": "Widget Name", "widgetOwner": "Widget Owner" }, "inputFieldDescriptions": { "widgetName": "Enter the full widget name. I support <a href=\"https://hubspot.com\">links</a> too." }, "executionRules": { "alreadyExists": "The widget with name {{ widgetName }} already exists" } } }, "executionRules": [ { "labelName": "alreadyExists", "conditions": { "errorCode": "ALREADY_EXISTS" } } ], "objectTypes": ["CONTACT", "DEAL"] }

Valider la source de la demande

Les demandes effectuées pour votre action personnalisée utiliseront la version 2 de X-HubSpot-Signature. Découvrez-en davantage sur la validation des demandes de HubSpot.

Charges utiles par défaut

Il existe deux types d'appels effectués pour les actions de workflow personnalisées :

  • Récupérations d'options de champ : renseignez une liste des options valides lorsqu'un utilisateur configure un champ.
  • Demandes d'exécution d'action : effectué lorsqu'une action est exécutée par un workflow qui comprend votre action personnalisée. 

Récupérations d'options de champ

Les demandes de récupération d'options sont effectuées lorsqu'un utilisateur configure votre action personnalisée dans le workflow.

Format de demande :

{ "origin": { // The customer's portal ID "portalId": 1, // Your custom action definition ID "actionDefinitionId": 2, // Your custom action definition version "actionDefinitionVersion": 3 }, // The values for the fields that have already been filled out by the workflow user "inputFields": { "widgetName": { "type": "OBJECT_PROPERTY", "propertyName": "widget_name" }, "widgetColor": { "type": "STATIC_VALUE", "value": "blue" } }, "fetchOptions": { // The search query provided by the user. This should be used to filter the returned // options. This will only be included if the previous option fetch returned // `searchable: true` and the user has entered a search query. "q": "option label", // The pagination cursor. This will be the same pagination cursor that was returned by // the previous option fetch; it can be used to keep track of which options have already // been fetched. "after": "1234=" } }

Format de réponse attendu :

// { "options": [ { "label": "Big Widget", "description": "Big Widget", "value": "10" }, { "label": "Small Widget", "description": "Small Widget", "value": "1" } ], // Optional. The pagination cursor. If this is provided, the Workflows app will render // a button to load more results at the bottom of the list of options when a user is // selecting an option, and when the next page is loaded this value will be included in // the request payload under `fetchOptions.after`. "after": "1234=", // Optional. Default is false. If this is true, the Workflows app will render a search // field to allow a user to filter the available options by a search query, and when // a search query is entered by the user, options will be re-fetched with that search // term in the request payload under `fetchOptions.q`. "searchable": true }

Pour limiter le nombre d'options renvoyées par une récupération, vous pouvez définir un curseur de pagination, qui indique aux workflows que davantage d'options peuvent être chargées. Si vous souhaitez que la liste des options soit accessible, vous pouvez renvoyer "searchable": true pour autoriser le filtrage des résultats avec une demande de recherche.

Demandes d'exécution d'action

Les demandes d'exécution sont effectuées lorsqu'un workflow exécute votre action personnalisée par rapport à un objet inscrit.

Format de demande : 

// { "origin": { // The customer's portal ID "portalId": 1, // Your custom action definition ID "actionDefinitionId": 2, // Your custom action definition version "actionDefinitionVersionId": 3 }, "object": { // The type of CRM object that is enrolled in the workflow "objectType": "CONTACT", // The ID of the CRM object that is enrolled in the workflow "objectId": 4, // The values of the properties of the CRM object that were specified // on the objectRequestOptions of the definition "properties": { "firstname": "Test" } }, // The field values specified by the workflow user "inputFields": { "widgetName": "My test widget", "widgetColor": "blue", "widgetSize": "10" }, // A unique ID for this execution. This can be used for idempotency to // deduplicate potential duplicate deliveries from workflows, and it // should also be used as the callbackId if your custom action will be // blocking execution. "callbackId": "ap-123-456-7-8" }

Personnaliser la charge utile avec des fonctions

Vous pouvez personnaliser les demandes qui sont effectuées pour votre action personnalisée en configurant des fonctions sans serveur pour votre action personnalisée.

Personnaliser les récupérations d'options de champ

Il existe deux hooks pour personnaliser le cycle de vie de la récupération d'options de champ :

  • PRE_FETCH_OPTIONS : vous permet de configurer la demande envoyée depuis HubSpot.
  • POST_FETCH_OPTIONS : vous permet de convertir la réponse de votre service dans un format pris en charge par les workflows.

PRE_FETCH_OPTIONS

Format de l'argument de saisie de fonction :

{ "origin": { // The customer's portal ID "portalId": 1, // Your custom action definition ID "actionDefinitionId": 2, // Your custom action definition version "actionDefinitionVersion": 3 }, // Your configured external data field webhook URL "webhookUrl": "https://myapi.com/hubspot/widget-sizes", // The values for the fields that have already been filled out by the workflow user "inputFields": { "widgetName": { "type": "OBJECT_PROPERTY", "propertyName": "widget_name" }, "widgetColor": { "type": "STATIC_VALUE", "value": "blue" } } }

Format de sortie de fonction attendu :

{ // The webhook URL for HubSpot to call "webhookUrl": "https://myapi.com/hubspot/widget-sizes", // Optional. The request body. "body": "{\"widgetName\": \"My new widget\"}", // Optional. A map of custom request headers to add. "httpHeaders": { "My-Custom-Header": "header value" }, // Optional. The Content-Type of the request. Default is application/json. "contentType": "application/json", // Optional. The Accept type of the request. Default is application/json. "accept": "application/json", // Optional. The HTTP method with which to make the request. // Valid values are GET, POST, PUT, PATCH, and DELETE. // Default is POST. "httpMethod": "POST" }

POST_FETCH_OPTIONS

Format de l'argument de saisie de fonction :

// { // The requested field key "fieldKey": "widgetSize", // The webhook response body from your service "responseBody": "{\"widgetSizes\": [10, 1]}" }

Format de sortie de fonction attendu :

// { "options": [ { "label": "Big Widget", "description": "Big Widget", "value": "10" }, { "label": "Small Widget", "description": "Small Widget", "value": "1" } ] }

Personnaliser les demandes d'exécution

Il existe un hook dans le cycle de vie d'exécution de l'action, une fonction PRE_ACTION_EXECUTION. Cette fonction vous permet de configurer la demande envoyée par HubSpot.

Format de l'argument de saisie de fonction :

// { "origin": { // The customer's portal ID "portalId": 1, // Your custom action definition ID "actionDefinitionId": 2, // Your custom action definition version "actionDefinitionVersionId": 3 }, "object": { // The type of CRM object that the custom action is executing against "objectType": "CONTACT", // The ID of the HubSpot CRM object that the custom action is executing against "objectId": 4, // The values of the properties of the CRM object that were specified // on the objectRequestOptions of the definition "properties": { "firstname": "Test" }, }, // The field values specified by the workflow user "inputFields": { "widgetName": "My test widget", "widgetColor": "blue", "widgetSize": "10" }, // A unique ID for this execution. This can be used for idempotency to // deduplicate potential duplicate deliveries from workflows, and it // should also be used as the callbackId if your custom action will be // blocking execution. "callbackId": "ap-123-456-7-8", // The configured action URL from your definition "webhookUrl": "https://myapi.com/hubspot" }

Format de sortie de fonction attendu :

// { // The webhook URL for HubSpot to call "webhookUrl": "https://myapi.com/hubspot", // Optional. The request body. "body": "{\"widgetName\": \"My new widget\", \"widgetColor\": \"blue\"}", // Optional. A map of custom request headers to add. "httpHeaders": { "My-Custom-Header": "header value" }, // Optional. The Content-Type of the request. Default is application/json. "contentType": "application/json", // Optional. The Accept type of the request. Default is application/json. "accept": "application/json", // Optional. The HTTP method with which to make the request. // Valid values are GET, POST, PUT, PATCH, and DELETE. // Default is POST. "httpMethod": "POST" }

Publier votre action personnalisée

Par défaut, votre action personnalisée est créée avec un statut non publié et ne sera visible que dans le portail du développeur associé à votre application HubSpot. Pour rendre votre action personnalisée visible par les clients, mettez à jour l'indicateur published de votre définition d'action et définissez-le sur true.

Tester votre action personnalisée

Vous pouvez tester votre action personnalisée en créant un workflow dans l'outil Workflows et en ajoutant votre action personnalisée.

Exécuter votre action personnalisée

La réussite d'une action est déterminée par l'analyse du code de statut renvoyé par votre service :

  • Codes de statut 2xx : indiquent que l'action a été correctement effectuée.
  • Codes de statut 4xx : indiquent que l'action a échoué.
    • Les codes de statut Taux limité 429 sont l'exception. Ces derniers sont traités comme des nouvelles tentatives et l'en-tête Retry-After est respecté.
  • Codes de statut 5xx : indiquent qu'un problème temporaire est survenu avec votre service et que votre action sera relancée ultérieurement.

Bloquer l'exécution d'une action personnalisée

Les actions personnalisées peuvent bloquer l'exécution du workflow. Au lieu d'exécuter l'action suivante dans le workflow une fois que votre action personnalisée a reçu une réponse Terminé (code de statut 2xx ou 4xx) de votre service, le workflow cessera d'exécuter (bloquera) cette inscription tant que vous n'indiquerez pas au workflow de poursuivre.

Pour bloquer une action personnalisée de façon asynchrone, la réponse de l'exécution de votre action doit avoir le format suivant :

// { "outputFields": { // Required. Must be BLOCK for your custom action to block execution. "hs_execution_state": "BLOCK", // Optional. If not provided, a default expiration of 1 week is used. // Must be specified in ISO 8601 Duration format. // See https://en.wikipedia.org/wiki/ISO_8601#Durations "hs_expiration_duration": "P1WT1H" } }

Vous pouvez éventuellement spécifier une valeur pour le champ hs_default_expiration, après quoi votre action personnalisée sera considérée comme expirée. L'exécution du workflow reprendra alors et l'action suivant votre action personnalisée sera exécutée, même si vous n'avez pas indiqué au workflow de poursuivre.

Terminer une exécution bloquée

Pour terminer une exécution d'action personnalisée bloquée, utilisez le point de terminaison suivant : /callbacks/{appId}/{callbackId}/complete

Le format du corps de demande est :

// { "outputFields": { // Required. The final execution state. Valid values are SUCCESS // (to indicate that your custom action completed successfully) or // FAIL_CONTINUE (to indicate that there was a problem with your // custom action execution) "hs_execution_state": "SUCCESS" } }

Ajouter des messages d'exécution personnalisés

Spécifiez des règles sur votre action qui déterminent le message à afficher sur la page d'historique des workflows lorsque l'action est exécutée. Les règles seront associées aux valeurs de sortie de votre action.  Ces valeurs de sortie doivent être fournies dans le corps de la réponse du webhook au format suivant :

// { "outputFields": { "errorCode": "ALREADY_EXISTS", "widgetName": "Test widget" } }

Les règles executionRules seront testées dans l'ordre indiqué. S'il existe plusieurs correspondances, seul le message de la première règle correspondante s'affichera pour l'utilisateur.

La règle est associée lorsque le résultat d'exécution correspond à une valeur spécifique dans la règle. Prenons l'exemple de cet ensemble de règles executionRules :

// [ { // This matches the key of a label on the action's `labels.LANGUAGE.executionRules` map "labelName": "alreadyExists", "conditions": { "errorCode": "ALREADY_EXISTS" } }, { "labelName": "widgetWrongSize", "conditions": { "errorCode": "WIDGET_SIZE", "sizeError": ["TOO_SMALL", "TOO_BIG"] } }, { "labelName": "widgetInvalidSize", "conditions": { "errorCode": "WIDGET_SIZE" } } ]

Les correspondances suivantes se produiront :

  • {"errorCode": "ALREADY_EXISTS", "widgetName": "Test widget"} : Cela correspond à la première règle, car errorCode équivaut à ALREADY_EXISTS. Dans cet exemple, même s'il y a un résultat widgetName, il ne sera pas utilisé dans la définition de la règle. Ainsi, toute valeur sera autorisée.
  • {"errorCode": "WIDGET_SIZE", "sizeError": "TOO_SMALL"} : Cela correspond à la deuxième règle, car TOO_SMALL est l'un des éléments sizeError correspondants et errorCode est WIDGET_SIZE.
  • {"errorCode": "WIDGET_SIZE", "sizeError": "NOT_A_NUMBER"} : Cela correspond à la troisième règle, car même si errorCode est WIDGET_SIZE, sizeError ne correspond pas à l'une des valeurs spécifiées par la deuxième règle (TOO_SMALL ou TOO_BIG).

Ce mécanisme correspondant vous permet de spécifier des erreurs de secours afin que vous puissiez obtenir des erreurs spécifiques pour des cas d'erreur importants, mais des messages d'erreur plus génériques pour des erreurs moins courantes.

Example 1

A basic example with the following input fields, created for contact and deal workflows:

  • a static input field
  • a dropdown field with options
  • a field whose value is a HubSpot owner
  • a field whose value is pulled from a property (that the user creating the workflow selects) on the enrolled object.
// { "actionUrl": "https://example.com/hubspot", "inputFields": [ { "typeDefinition": { "name": "widgetName", "type": "string", "fieldType": "text" }, "supportedValueTypes": ["STATIC_VALUE"], "isRequired": true }, { "typeDefinition": { "name": "widgetColor", "type": "enumeration", "fieldType": "select", "options": [ { "value": "red", "label": "Red" }, { "value": "blue", "label": "Blue" }, { "value": "green", "label": "Green" } ] }, "supportedValueTypes": ["STATIC_VALUE"] }, { "typeDefinition": { "name": "widgetOwner", "type": "enumeration", "referencedObjectType": "OWNER" }, "supportedValueTypes": ["STATIC_VALUE"] }, { "typeDefinition": { "name": "widgetQuantity", "type": "number" }, "supportedValueTypes": ["OBJECT_PROPERTY"] } ], "labels": { "en": { "actionName": "Create Widget - Example 1", "actionDescription": "This action will create a new widget in our system. So cool!", "actionCardContent": "Create widget {{widgetName}}", "inputFieldLabels": { "widgetName": "Widget Name", "widgetColor": "Widget Color", "widgetOwner": "Widget Owner", "widgetQuantity": "Widget Quantity" }, "inputFieldDescriptions": { "widgetName": "Enter the full widget name. I support <a href=\"https://hubspot.com\">links</a> too.", "widgetColor": "This is the color that will be used to paint the widget." } } }, "objectTypes": ["CONTACT", "DEAL"] }

Exemple 2

L'action personnalisée suivante utilise une fonction sans serveur pour transformer la charge utile envoyée à l'élément actionUrl configuré. Étant donné qu'aucun élément objectTypes n'est spécifié, cette action sera disponible dans tous les types de workflow.

// { "actionUrl": "https://api.example.com/v1/widgets", "inputFields": [ { "typeDefinition": { "name": "widgetName", "type": "string", "fieldType": "text" }, "supportedValueTypes": ["STATIC_VALUE"], "isRequired": true } ], "labels": { "en": { "actionName": "Create Widget - Example 2", "actionCardContent": "Create widget {{widgetName}}", "inputFieldLabels": { "widgetName": "Widget Name" } } }, "functions": [ { "functionType": "PRE_ACTION_EXECUTION", "functionSource": "exports.main = function(event, callback) { return callback(transformRequest(event)); }\nfunction transformRequest(request) { return { webhookUrl: request.webhookUrl, body: JSON.stringify(request.fields), contentType: 'application/x-www-form-urlencoded', accept: 'application/json', httpMethod: 'POST' }; }" } ] }

Exemple 3

L'action personnalisée suivante contient des dépendances de champs et des options récupérées depuis une API. Étant donné que la taille du widget dépend de la couleur du widget, l'utilisateur ne pourra pas saisir une valeur pour la taille du widget tant qu'il n'aura pas choisi une couleur.

Le coût du widget dépend également de la couleur du widget, mais il dépend de la valeur que l'utilisateur sélectionne pour la couleur du widget. L'utilisateur ne pourra pas saisir une valeur pour le coût du widget, sauf si la couleur Red est sélectionnée comme couleur du widget.

// { "actionUrl": "https://example.com/hubspot", "inputFields": [ { "typeDefinition": { "name": "widgetName", "type": "string", "fieldType": "text" }, "supportedValueTypes": ["STATIC_VALUE"], "isRequired": true }, { "typeDefinition": { "name": "widgetColor", "type": "enumeration", "fieldType": "select", "options": [ { "value": "red", "description": "red", "label": "Red" }, { "value": "blue", "description": "blue", "label": "Blue" }, { "value": "green", "description": "green", "label": "Green" } ] }, "supportedValueTypes": ["STATIC_VALUE"], }, { "typeDefinition": { "name": "widgetSize", "type": "enumeration", "fieldType": "select", "optionsUrl": "https://api.example.com/v1/widget-sizes" }, "supportedValueTypes": ["STATIC_VALUE"] }, { "typeDefinition": { "name": "widgetCost", "type": "number", "fieldType": "number" }, "supportedValueTypes": ["OBJECT_PROPERTY"] } ], "inputFieldDependencies": [ { "dependencyType": "SINGLE_FIELD", "controllingFieldName": "widgetColor", "dependentFieldNames": ["widgetSize"] }, { "dependencyType": "CONDITIONAL_SINGLE_FIELD", "controllingFieldName": "widgetColor", "controllingFieldValue": "red", "dependentFieldNames": ["widgetCost"] } ], "labels": { "en": { "actionName": "Create Widget - Example 3", "actionCardContent": "Create widget {{widgetName}}", "inputFieldLabels": { "widgetName": "Widget Name", "widgetColor": "Widget Color", "widgetSize": "Widget Size", "widgetCost": "Widget Cost" } } }, "objectTypes": ["CONTACT", "DEAL"], "functions": [ { "functionType": "PRE_FETCH_OPTIONS", "id": "widgetSize", "functionSource": "exports.main = function(event, callback) { return callback(transformRequest(event)); }\nfunction transformRequest(request) { return { webhookUrl: request.webhookUrl + '?color=' + request.fields.widgetColor.value, body: JSON.stringify(request.fields), httpMethod: 'GET' }; }" } ] }

Exemple 4

L'exemple suivant présente le blocage d'une action personnalisée. L'API des rappels peut être utilisée pour indiquer à HubSpot de terminer l'action et de faire avancer l'objet inscrit vers la prochaine action du workflow.

Vous n'aurez pas besoin de spécifier que l'action bloque au moment de la création de l'action. Cela sera déterminé par la réponse de votre élément actionUrl configuré.

// { "actionUrl": "https://example.com/hubspot", "inputFields": [ { "typeDefinition": { "name": "moonPhase", "type": "enumeration", "fieldType": "select", "options": [ { "value": "full_moon", "description": "Full Moon", "label": "Full" }, { "value": "half_moon", "description": "Half Moon", "label": "Half" }, { "value": "quarter_moon", "description": "Quarter Moon", "label": "Quarter" }, { "value": "new_moon", "description": "New Moon", "label": "New" } ] }, "supportedValueTypes": ["STATIC_VALUE"] } ], "objectRequestOptions": { "properties": ["email"] }, "labels": { "en": { "actionName": "Wait For Moon Phase", "actionCardContent": "Wait until a {{moonPhase}} moon", "inputFieldLabels": { "widgetName": "Moon Phase" } }, "fr": { "actionName": "Attendez la phase lunaire", "actionCardContent": "Attendez la lune {{moonPhase}}", "inputFieldLabels": { "widgetName": "Phase de lune" } } }, "objectTypes": ["CONTACT"] }

Cet article vous a-t-il été utile ? *
Ce formulaire est destiné à recueillir les avis sur la documentation pour les développeurs. Si vous souhaitez faire part de votre avis sur les produits HubSpot, veuillez le partager sur le forum des idéesde la communauté.