Acciones de flujo de trabajo personalizadas

Los workflows de Hub Spot se utilizan para automatizar procesos de negocio y permitir que los clientes de HubSpot sean más eficaces. Puedes crear acciones personalizadas de workflow para integrar tu servicio con workflows de HubSpot

Primero, definirás tu acción personalizada, incluidas las entradas que debe completar el usuario que crea un workflow y la URL que se solicitará cuando se ejecute la acción personalizada. Luego, cuando los clientes instalan tu aplicación, pueden agregar tu acción personalizada a sus flujos de trabajo. Cuando se ejecutan esos flujos de trabajo, las solicitudes HTTPS se enviarán a la URL configurada con la carga que se configure. En este artículo, descubre cómo:

Antes de empezar, ten en cuenta lo siguiente:

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

Define tu acción personalizada

Para crear una acción de flujo de trabajo personalizada, deberás definir la acción usando los siguientes campos. Esta definición también especifica el formato de solicitud para solicitudes procedentes de HubSpot, así como el manejo de respuestas de tu servicio.

  • actionUrl: la URL donde se envía una solicitud HTTPS cuando se ejecuta la acción. El cuerpo de la solicitud contiene información sobre a nombre de qué usuario se está ejecutando la acción y qué valores se ingresaron para los campos de entrada.
  • inputFields: esto define el conjunto de valores válidos para los datos de la acción, ya sea una lista estática o una URL de webhook. Si se proporciona una URL de webhook, las opciones se recuperan desde esa URL cuando la acción se edita por un cliente en la herramienta Workflows. Estos son opcionales para cada campo.
  • outputFields: los valores que se mostrarán en la acción que se pueden usar más tarde en el flujo de trabajo. Una acción personalizada puede tener cero, una o muchas salidas.
  • executionRules: una lista de definiciones que puedes especificar para exponer errores de tu servicio al usuario que crea el workflow.
  • etiquetas: copia que describe al usuario lo que representa los campos de la acción y lo que hace la acción. Se requieren etiquetas en inglés, pero éstas también pueden especificarse en cualquiera de los siguientes idiomas compatibles: Francés (fr), alemán (de), japonés (ja), español (es), portugués de Brasil (pt-br) y holandés (nl). La especificación de cada idioma incluye los siguientes campos:
    • actionName: el nombre de la acción que aparece en el panel Elegir una acción en el editor de flujos de trabajo.
    • actionDescription: una descripción detallada de la acción que se muestra cuando el usuario está configurando la acción personalizada.
    • actionCardContent: una descripción resumida que se muestra en la tarjeta de la acción.
    • inputFieldLabels: un objeto que mapea las definiciones de inputFields a las etiquetas correspondientes que el usuario verá cuando configure la acción.
    • outputFieldLabels: un objeto que mapea las definiciones de outputFields a las etiquetas correspondientes que se muestran en la herramienta de workflows.
    • inputFieldDescriptions: un objeto que mapea las definiciones de inputFields a las descripciones debajo de las etiquetas correspondientes.
    • executionRules: un objeto que mapea las definiciones de tus executionRules a los mensajes de error que se le mostrarán al usuario si desconecta una acción personalizada. Esto permite especificar mensajes personalizados para los resultados de la ejecución de la acción en el historial del flujo de trabajo.

Se especifica un ejemplo de una definición de acción básica a continuación:

// { "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 la fuente de solicitud

Las solicitudes realizadas para tu acción personalizada usarán la versión v2 del X-HubSpot-Signature. Más información sobre cómo validar solicitudes de HubSpot.

Carga predeterminada

Hay dos tipos de llamadas realizadas para acciones de flujo de trabajo personalizadas:

  • Recuperaciones de opciones de campo: se completa una lista de opciones válidas cuando un usuario está configurando un campo.
  • Solicitud de ejecución de acción: se realiza cuando una acción se ejecuta por un flujo de trabajo que incluye tu acción personalizada. 

Recuperación de opciones de campo

Las solicitudes de recuperación de opciones se realizan cuando un usuario está configurando su acción personalizada en su flujo de trabajo.

Formato de solicitud:

{ "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 respuesta 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 el número de opciones que se devuelven por una recuperación de opciones, puedes establecer un cursor de paginación, que le indicará a los flujos de trabajo que se pueden cargar más opciones. Si quieres que sea posible buscar en la lista de opciones, puedes volver a "searchable": true para permitir que los resultados se filtren por una consulta de búsqueda.

Solicitudes de ejecución de acciones

Las solicitudes de ejecución se realizan cuando un flujo de trabajo está ejecutando tu acción personalizada contra un objeto inscrito.

Formato de solicitud: 

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

Personaliza la carga con funciones

Puedes personalizar las solicitudes que se realizan para tu acción personalizada configurando funciones sin servidor para tu acción personalizada.

Personalizar las recuperaciones de opciones de campo

Hay dos ganchos para personalizar el ciclo de vida de recuperación de la opción de campo:

  • PRE_FETCH_OPTIONS: esto te permite configurar la solicitud enviada desde HubSpot.
  • POST_FETCH_OPTIONS: esto te permite transformar la respuesta de tu servicio en un formato que comprende flujos de trabajo.

PRE_FETCH_OPTIONS

Formato de argumento de entrada de función:

{ "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 salida de la función 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 de argumento de entrada de función:

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

Formato de salida de la función esperada:

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

Personalizar las solicitudes de ejecución

Hay un gancho al ciclo de ejecución de la acción, una función PRE_ACTION_EXECUTION. Esta función te permite configurar la solicitud que se envía desde HubSpot.

Formato de argumento de entrada de función:

// { "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 salida de la función 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" }

Publica tu acción personalizada

Por opción predeterminada, tu acción personalizada se crea en un estado sin publicar y sólo será visible en el portal de desarrolladores asociado con tu aplicación de HubSpot. Para que tu acción personalizada sea visible para los clientes, actualiza la bandera publicada en tu definición de acción a verdadero.

Prueba tu acción personalizada

Puedes probar tu acción personalizada creando un workflow en la herramienta de workflows y agregar tu acción personalizada.

Ejecutar tu acción personalizada

El éxito de una acción se determina examinando el código de estado devuelto por tu servicio:

  • Códigos de estado 2xx: esto indica que la acción se ha completado correctamente.
  • Códigos de estado 4xx: esto indica que la acción falló.
    • La excepción aquí son los códigos de estado Tasa limitada 429; estos se vuelven a tratar como reintentos y se respeta el encabezado Reintentar después.
  • Códigos de estado 5xx: esto indica que hubo un problema temporal con tu servicio y tu acción se volverá a intentar más tarde.

Bloquear la ejecución de acciones personalizadas

Las acciones personalizadas pueden bloquear la ejecución del flujo de trabajo. En lugar de ejecutar la siguiente acción en el flujo de trabajo después de la acción personalizada tras recibir una respuesta "completada" (2xx o 4xx) de tu servicio, el flujo de trabajo dejará de ejecutar ("bloquear") esa inscripción hasta que el flujo de trabajo continúe.

Para bloquear una acción personalizada asíncronamente, la respuesta de ejecución de tu acción debe tener el siguiente 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" } }

Opcionalmente, puedes especificar un valor para el campo hs_default_expiration, después de que tu acción personalizada se considere vencida. La ejecución del flujo de trabajo se reanudará y la acción que sigue a la acción personalizada se ejecutará, aunque no le hayas indicado al flujo de trabajo que continúe.

Completar una ejecución bloqueada

Para completar la ejecución de una acción personalizada bloqueada, utiliza el siguiente punto final: /callbacks/{appId}/{callbackId}/complete

El formato del cuerpo de la solicitud es:

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

Añadir mensajes de ejecución personalizados

Reglas específicas sobre tu acción que determinan qué mensaje aparece en la página de historial del flujo de trabajo cuando se ejecuta la acción. Las reglas se emparejarán con los valores de salida de tu acción.  Estos valores de salida se deben proporcionar en el cuerpo de la respuesta del webhook, en el siguiente formato:

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

Las executionRules se probarán en el orden proporcionado. Si hay varias coincidencias, solo el mensaje de la primera regla que coincida se muestra al usuario.

La regla coincide cuando la salida de ejecución corresponde a un valor especificado en la regla. Por ejemplo, considera este 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" } } ]

Se producirían las siguientes coincidencias:

  • {"errorCode": "ALREADY_EXISTS", "widgetName": "Test widget"}: Esto coincidirá con la primera regla, ya que errorCode es igual a ALREADY_EXISTS. En este caso, aunque haya una salida widgetName, no se utiliza en la definición de regla, por lo que se permite cualquier valor.
  • {"errorCode": "WIDGET_SIZE", "sizeError": "TOO_SMALL"}: Esto coincidirá con la segunda regla, ya que TOO_SMALL es uno de los valores de sizeErrorquecoinciden, y errorCode es WIDGET_SIZE.
  • {"errorCode": "WIDGET_SIZE", "sizeError": "NOT_A_NUMBER"}: Esto coincidirá con la tercera regla, ya que aunque el errorCode es WIDGET_SIZE, el sizeError no coincide con ninguno de los valores especificados por la segunda regla(TOO_SMALL o TOO_BIG).

Este mecanismo de coincidencia te permite especificar errores de reserva, por lo que puedes tener errores específicos para casos de error importantes, pero regresa a mensajes de error genéricos para errores más comunes.

Ejemplo 1

Un ejemplo básico con los siguientes campos de entrada, creados para los flujos de trabajo de los contactos y las transacciones:

  • un campo de entrada estático
  • un campo desplegable con opciones
  • un campo cuyo valor es un propietario de HubSpot
  • un campo cuyo valor se extrae de una propiedad (que el usuario que crea el flujo de trabajo selecciona) en el objeto inscrito.
// { "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"] }

Ejemplo 2

La siguiente acción personalizada utiliza una función sin servidor para transformar la carga que se envía a la acción configurada actionUrl. Dado que no se especifican objectTypes ,esta acción estará disponible en todos los tipos de flujos de trabajo.

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

Ejemplo 3

La siguiente acción personalizada tiene dependencias y opciones de campo que se recuperan desde una API. Debido a que el tamaño del widget depende del color del widget, el usuario no podrá introducir un valor para el tamaño del widget hasta que se elija un color del widget

El coste del widget también depende del color del widget, pero está condicionado al valor que el usuario seleccione para el color del widget; el usuario no podrá introducir un valor para el coste del widget, a menos que Red esté seleccionada como el color del 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' }; }" } ] }

Ejemplo 4

El siguiente ejemplo es una acción personalizada de bloqueo. La API de devolución de llamadas se puede usar para indicarle a HubSpot que complete la acción y hacer que el objeto inscrito pase a la siguiente acción en el flujo de trabajo

No necesitarás especificar que la acción se bloquee en el momento en que creas la acción; eso estará determinado por la respuesta de tu actionUrlconfigurada.

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

¿Te resultó útil este artículo?
Con este formulario puedes enviar tu opinión sobre nuestros documentos para desarrolladores. Si tienes comentarios sobre el producto de HubSpot, puedes enviarlos al Foro de ideas.