Ações de fluxo de trabalho personalizados

Os fluxos de trabalho do HubSpot são usados para automatizar processos comerciais e permitir que os clientes da HubSpot sejam mais eficientes. Você pode criar ações de fluxo de trabalho personalizadas para integrar seu serviço a fluxos de trabalho do HubSpot.

Primeiro, você definirá a ação personalizada, incluindo as entradas que precisam ser preenchidas pelo usuário que cria um fluxo de trabalho e o URL que será solicitado quando a ação personalizada for executada. Depois, quando os clientes instalarem seu aplicativo, eles poderão adicionar sua ação personalizada aos fluxos de trabalho. Quando esses fluxos de trabalho forem executados, as solicitações HTTPS serão enviadas ao URL configurado com a carga útil configurada. Neste artigo, saiba como:

Antes de começar, anote o seguinte:

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

Definir sua ação personalizada

Para criar uma ação de fluxo de trabalho personalizada, você precisará definir a ação usando os seguintes campos. Essa definição também especifica o formato das solicitações que vêm do HubSpot, bem como o tratamento das respostas do seu serviço.

  • actionUrl: o URL para o qual uma solicitação HTTPS é enviada quando a ação é executada. O corpo da solicitação conterá informações sobre o usuário para o qual ação está sendo executada e quais valores foram inseridos para os campos de entrada.
  • inputFields: definem o conjunto de valores válidos para as entradas das ações. Uma lista estática ou uma URL de webhook pode ser fornecida. Se uma URL do webhook for fornecida, as opções serão buscadas desse URL sempre que a ação for editada por um cliente na ferramenta Fluxos de trabalho. Estes são opcionais para cada campo.
  • outputFields: os valores que a ação enviarpa que podem ser usado por ações posteriores no fluxo de trabalho. Uma ação personalizada pode ter zero, uma ou muitas saídas.
  • executionRules: uma lista de definições que você pode especificar para revelar erros do seu serviço ao usuário que cria o fluxo de trabalho.
  • labels: texto que descreve o que os campos de ação representam e o que a ação faz ao usuário. Rótulos em inglês são obrigatórios, mas os rótulos podem ser especificados em qualquer um dos seguintes idiomas permitidos: francês (fr), alemão (de), japonês (ja), espanhol (es), português do Brasil (pt-br) e holandês (nl). A especificação para cada idioma inclui os seguintes campos:
    • actionName: o nome da ação mostrado no painel Escolha uma ação no editor de fluxo de trabalho.
    • actionDescription: uma descrição detalhada da ação exibida quando o usuário está configurando a ação personalizada.
    • actionCardContent: uma descrição resumida exibida no cartão de ação.
    • inputFieldLabels: um objeto que mapeia as definições de inputFields para os rótulos correspondentes que o usuário verá ao configurar a ação.
    • outputFieldLabels: um objeto que mapeia as definições de outputFields para os rótulos correspondentes mostrados na ferramenta de fluxos de trabalho.
    • inputFieldDescriptions: um objeto que mapeia as definições de inputFields para as descrições abaixo dos rótulos correspondentes.
    • executionRules: um objeto que mapeia as definições de executionRules para mensagens de erro que serão exibidas ao usuário se eles configurarem errado uma ação personalizada. Isso permite especificar mensagens personalizadas para resultados de execução de ação no histórico do fluxo de trabalho.

Um exemplo de definição de ação básica é especificado abaixo:

// { "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"] }

Validar a origem da solicitação

As solicitações feitas para sua ação personalizada usarão a versão v2 do X-HubSpot-Signature. Saiba mais sobre como validar solicitações do HubSpot.

Payloads padrão

Há dois tipos de chamadas feitas para ações de fluxo de trabalho personalizado:

  • Buscas de opção de campo: preencha uma lista de opções válidas quando um usuário está configurando um campo.
  • Solicitações de execução de ação: feitas quando uma ação está sendo executada por um fluxo de trabalho que inclui a ação personalizada. 

Buscas de opções de campo

As solicitações para buscar opções são feitas quando um usuário está configurando sua ação personalizada no fluxo de trabalho deles.

Formato de solicitação:

{ "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=" } }

Formato de resposta esperado:

// { "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 }

Para limitar o número de opções que retornam em uma busca, você pode definir um cursor de paginação, que dirá os fluxos de trabalho que mais opções podem ser carregadas. Se quiser que a lista de opções seja pesquisável, você pode retornar "searchable": true para permitir que os resultados sejam filtrados por uma consulta de pesquisa.

Solicitações de execução de ação

As solicitações de execução são feitas quando um fluxo de trabalho está executando sua ação personalizada em relação a um objeto inscrito.

Formato de solicitação: 

// { "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" }

Personalizar a carga útil com funções

Você pode personalizar as solicitações feitas para sua ação personalizada configurando funções sem servidor para a ação personalizada.

Personalizar buscas de opções de campo

Há dois hooks para personalizar o ciclo de vida de busca de opção de campo:

  • PRE_FETCH_OPTIONS: isso permite configurar a solicitação enviada do HubSpot.
  • POST_FETCH_OPTIONS: isso permite transformar a resposta do seu serviço em um formato compreendido pelos fluxos de trabalho.

PRE_FETCH_OPTIONS

Formato do argumento de entrada de função:

{ "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" } } }

Formato de saída de função esperada:

{ // 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

Formato do argumento de entrada de função:

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

Formato de saída de função esperada:

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

Personalizar solicitações de execução

Há um hook no ciclo de vida de execução de ação, uma função PRE_ACTION_EXECUTION. Essa função permite configurar a solicitação enviada do HubSpot.

Formato do argumento de entrada de função:

// { "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" }

Formato de saída de função esperada:

// { // 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" }

Publicar sua ação personalizada

Por padrão, sua ação personalizada é criada em um estado não publicado e estará visível no portal de desenvolvedor associado ao seu aplicativo da HubSpot. Para tornar sua ação personalizada visível aos clientes, atualize o sinalizador publicado na definição da ação para true.

Testar sua ação personalizada

Você pode testar sua ação personalizada ao criar um fluxo de trabalho na ferramenta de fluxos de trabalho e adicionar sua ação personalizada.

Executar sua ação personalizada

O sucesso de uma ação é determinado ao examinar o código de status que seu serviço retornou:

  • Códigos de status 2xx: isso indica que a ação foi concluída com sucesso.
  • Códigos de status 4xx: isso indica que a ação falhou.
    • A exceção aqui são os códigos de status de taxa limitada 429. Eles são retratados como novas tentativas e o cabeçalho Retry-After é respeitado.
  • Códigos de status 5xx: isso indica que houve um problema temporário com seu serviço e sua ação será enviada novamente em um momento posterior.

Bloquear a execução da ação personalizada

Ações personalizadas podem bloquear a execução do fluxo de trabalho. Em vez de executar a próxima ação no fluxo de trabalho após a ação personalizada depois de receber uma resposta “concluída” (código de status 2xx ou 4xx) do seu serviço, o fluxo de trabalho deixará de executar ("bloqueará") essa inscrição até você instruir o fluxo de trabalho para continuar.

Para bloquear uma ação personalizada, sua resposta de execução de ação precisa ter o seguinte formato:

// { "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" } }

Você pode optar por especificar um valor para o campo hs_default_expiration, após a qual a sua ação personalizada será considerada expirada. A execução do fluxo de trabalho será retomada e a ação que se segue à ação personalizada será executada, mesmo que você não tenha instruído o fluxo de trabalho para continuar.

Concluir uma execução bloqueada

Para concluir uma execução de ação personalizada bloqueada, use o endpoint a seguir: /callbacks/{appId}/{callbackId}/complete

O formato do corpo da solicitação é:

// { "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" } }

Adicionar mensagens de execução personalizadas

Especifique as regras na sua ação que determinam qual mensagem é exibida na página de histórico do fluxo de trabalho quando a ação for executada. As regras corresponderão aos valores de saída da sua ação.  Esses valores de saída devem ser fornecidos no corpo de resposta do webhook, no seguinte formato:

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

O executionRules será testado na ordem fornecida. Se houver várias correspondências, apenas a mensagem da primeira regra que corresponde será mostrada ao usuário.

A regra corresponde quando a saída de execução corresponde a um valor especificado na regra. Por exemplo, considere esse conjunto de 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" } } ]

As seguintes correspondências ocorrerão:

  • {"errorCode": "ALREADY_EXISTS", "widgetName": "Test widget"}: Isso corresponderia à primeira regra, pois errorCode é igual a ALREADY_EXISTS. Nesse caso, embora haja uma saída widgetNameela não é usada na definição de regra, de forma que qualquer valor é permitido.
  • {"errorCode": "WIDGET_SIZE", "sizeError": "TOO_SMALL"}: Isso corresponderia à segunda regra, pois TOO_SMALL é um dos sizeError correspondentes e errorCode é WIDGET_SIZE.
  • {"errorCode": "WIDGET_SIZE", "sizeError": "NOT_A_NUMBER"}: Isso corresponderia à terceira regra, pois, embora errorCode seja WIDGET_SIZE, o sizeError não corresponde a nenhum dos valores especificados pela segunda regra (TOO_SMALL ou TOO_BIG).

Esse mecanismo de correspondência permite especificar erros de fallback, para que você possa ter erros específicos para casos de erro importantes, mas retornar a mensagens de erro mais genéricas para erros menos comuns.

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"] }

Exemplo 2

A ação personalizada a seguir usa uma função sem servidor para transformar o payload que é enviado ao actionUrl configurado. Como nenhum objectTypes é especificado, essa ação estará disponível em todos os tipos de fluxos de trabalho.

// { "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' }; }" } ] }

Exemplo 3

A ação personalizada a seguir tem dependências de campo e opções que são buscadas de uma API. Como o tamanho do widget depende da cor do widget, o usuário não poderá inserir um valor para o tamanho do widget até que uma cor de widget seja escolhida.

O custo do widget também depende da cor do widget, mas é condicional no valor que o usuário seleciona para a cord o widget. O usuário não poderá inserir um valor para o custo do widget, a menos que a cor do widget seja selecionada.

// { "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' }; }" } ] }

Exemplo 4

O exemplo a seguir é uma ação personalizada de bloqueio. A API de retorno de chamada pode ser usada para informar a HubSpot para concluir a ação e fazer com que o objeto inscrito continue para a próxima ação no fluxo de trabalho.

Você não precisará especificar que a ação bloqueia no momento de criação da ação. Isso será determinado pela resposta do actionUrl configurado.

// { "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"] }

Was this article helpful? *
This form is used for documentation feedback only. Learn how to get help with HubSpot...