Learn how to build agent tools, which are custom workflow actions that can be used by AI agents.
workflow-actions
directory in the project, along with a *-hsmeta.json
configuration file for the tool. Each tool and workflow action you create should have its own *-hsmeta.json
configuration file.supportedClients
field.hsproject.json
must have its platformVersion
set to 2025.2
. This version is set automatically on all of the boilerplate project types, but will need to be manually updated for projects on older versions.
Note that, when upgrading a project to version 2025.2
you’ll need to adhere to the new *-hsmeta.json
configuration file standards for your app configuration and its features.
Agent tools and custom workflow actions are both contained within the app’s workflow-actions
directory.
supportedClients
field must include the AGENTS
client, along with other agent-specific fields, such as toolType
.Fields marked with * are required
Field | Type | Description |
---|---|---|
uid * | String | An internal unique identifier for the agent tool. |
type * | String | The type of component, which should be workflow-action in this case. |
actionUrl * | String | The URL that the tool will make a POST request to. The URL must be a publicly accessible endpoint and cannot be a serverless function defined within the developer project. |
supportedClients * | Array | An array of objects that specifies the clients that support the action. Each object in the array should have a client key with a string value indicating the client type. Values include:
toolType and llmConfig.actionDescription fields. |
toolType * | String | The category of tool functionality. Can be one of:
|
llmConfig.actionDescription * | String | The llmConfig object contains the actionDescription field, which describes the tool to the AI agent. This allows the agent to determine how and when to invoke the tool, along with how to structure the input data. This description is only visible to the agent and will never be shown to users. Learn more about writing effective tool descriptions. |
isPublished | Boolean | Determines whether the definition is visible in accounts that installed your app. By default, this is set to false . |
inputFields | Array | The fields that will be sent to the external service via the actionUrl . |
labels.<locale> * | String | Locale key that maps to the locale definition. At a minimum, an english label (en ) and its definition must be defined. |
labels.<locale>.inputFieldDescriptions | Object | An object that defines the details for the inputs for your action. In the example above, this object includes message and priority fields. |
labels.<locale>.inputFieldOptionLabels | Object | An object that’s required if your input field(s) have options. Provides a map of input field option labels, keyed by the option’s value or label. |
labels.<locale>.outputFieldLabels | Object | An object that maps the definitions from outputFields to the corresponding labels that appear in the agent UI. |
labels.<locale>.actionName * | String | The action’s name as displayed in the agent UI. |
labels.<locale>.appDisplayName * | String | The name of the section in the tool selection panel where all tools appear. If appDisplayName is defined for multiple tools, the first one found will be used. |
labels.<locale>.actionCardContent | String | A summarized description shown in the action’s card. |
labels.<locale>.executionRules | Object | An object that maps the definitions from your executionRules to messages that will appear in the agent UI. |
objectTypes | Array | The available CRM object types that this action can be used with. If empty, the action will be available for all object types. |
outputFields | Array | An array containing the fields and values that the tool will output. Response data must be formatted as comma separated string-string value pairs. Learn more about output fields. |
executionRules | Object | A list of definitions you can specify to surface errors from your service to the user in the agent UI. |
llmConfig.actionDescription
field, you can help agents understand how and when to invoke a tool, and how to structure input data. The description is only ever visible to the agent.
When writing the description:
actionDescription
: Use this tool when the user asks for a summary of one or more meetings. Provide a list of meeting IDs or transcript URLs as input. If no specific meeting is identified, use the most recent one. Summaries should include key topics, action items, and assigned responsibilities.
actionDescription
: Use this tool when the user needs to create an invoice document. Inputs should include recipient
, items
(with description, quantity, and price), and dueDate
. All currency values must be in USD unless otherwise specified. If items
are missing, do not generate a document. Returns a downloadable PDF link.
outputFields
is an array that defines the fields that can contain values returned by the response when the tool is executed. Output field definitions are similar to input field definitions:
outputFields
response sent to HubSpot must be structured as a JSON object with key-value pairs, where both keys and values are strings, as shown below. The keys should correspond with your defined output field names, with the values containing the actual returned data.
toolType
is set to TAKE_ACTION
, you can include a follow-up CTA that lets users navigate to a HubSpot CRM record on click. To do so, add the following fields to the outputFields
object in the response sent to HubSpot.
Field | Type | Description |
---|---|---|
ctaCrmObjectType | String | The type of CRM record (e.g., contact) |
ctaCrmObjectId | String | The ID of the CRM record to navigate to. |
ctaLabel | String | Optionally, you can specify a label. By default, HubSpot will try to autogenerate a label. |
POST
request is sent to the actionUrl
. The request will include the v2
x-hubspot-signature
, which you can use to validate the request.
The request body will include the input field values along with context about the account, user, and agent.
Field | Type | Description |
---|---|---|
callbackId | String | A unique ID assigned to the execution. You can use this value for execution blocking. |
origin | Object | Metadata about the account, user, and tool associated with the request. |
context | Object | Additional context about the agent and tool. |
inputFields | Object | Input field data included in the request. |
hs_execution_state
field in your response to HubSpot. This field can be set to one of the following values:
SUCCESS
: the execution has completed successfully and can proceed.FAIL_CONTINUE
: the execution has failed, but will proceed.BLOCK
: the execution is temporarily blocked and will not proceed until it’s updated via the automation API or when the block expires.outputFields
to include an hs_execution_state
of BLOCK
:
Prop | Type | Description |
---|---|---|
hs_execution_state | String | Set to BLOCK to prevent the action from continuing to execute. |
hs_expiration_duration | String | By default, actions are blocked for one week. Use this field to specify a different block expiration (ISO 8601 duration format). |
POST
request to https://api.hubspot.com/callbacks/{callbackId}/complete
, where callbackId
is the value provided in the original request body sent by HubSpot to your service.
In the request body, set the hs_execution_state
to either SUCCESS
or FAIL_CONTINUE
, depending on the execution status.
inputFields
) to be required during active development. Once a field has been set to required and the project is uploaded, you cannot remove or update the field. You should only set a field to required once you’re confident in the field’s details, such as its name
and type
. The reason for this limitation is that changing required fields would break any active workflows that include the action.
actionName
, inputFields
, and labels
should clearly communicate their usage and utility to both humans and the agent. These fields in particular are used by the agent to understand when to invoke the action and how to pass data to the tool. As you build your tools, keep in mind that LLMs may need more explicit descriptions than human users. For example, while a human might intuitively understand a field labeled Date
, an LLM might prefer Event start date (YYYY-MM-DD)
.
Ideally, tools should be built so that agents don’t require additional instructions to use it. However, there are cases where field details alone may not be sufficient for the agent. For example, it may not understand the intended order of operations for executing tools that are dependent on the output of other tools (e.g., a ‘Send Email’ tool might depend on a ‘Get Contact Info’ tool running first).
While building a tool, you should test it in the agent without adding to the agent’s instructions first to better understand how it interprets the tool. Through testing, you’ll be able to determine whether the reasoning engine performs correctly on its own or if it needs additional instructions.
actionName
, inputField
, and label
values. Tools are most effective and reliable when they’re designed for specific tasks with a focused set of parameters. For complex operations, consider whether it would be more effective to create multiple, simpler tools versus a single tool with an excessive number of inputs.
"Blog title"
"Blog title (must include the product name 'HubSpot CRM')"
"Social post content"
"Social post content (keep under 280 characters)"
"Social post content (must mention our Q4 sale, include #HubSpot, and stay under 280 characters)"
llmConfig.actionDescription
for clearer tool purpose and usagelabels
properties for better parameter extractionPOST
request to the tool’s actionUrl
. Agent tool invocation is authenticated by validating the X-HubSpot-Signature
header sent with the request. This is the same system HubSpot uses for validating webhook requests.