7-2 Basics of Custom Code Action
Run Node.js code directly within your workflows and interact with external services and the HubSpot API.
β‘ Custom Code Action specifications
runtime:Node.js 18.x (npm package is @hubspot/api-client etc. are pre-installed)
timeout:20 seconds (if exceeded, the workflow will be treated as an error)
input:Configure contact property values ββin the workflow event.inputFields Receive at
output:callback() can return property values ββand pass them to subsequent steps.
Custom Code Action β Basic structure
const hubspot = require(
'@hubspot/api-client');
exports.main =
async (event, callback) => {
const {
email,
firstname,
hs_object_id: contactId,
} = event.inputFields;
const client =
new hubspot.Client({
accessToken: process.env.HUBSPOT_ACCESS_TOKEN,
});
try {
const response =
await fetch(
`https://api.external-service.com/user?email=${email}`,
{ headers: {
'Authorization':
`Bearer ${process.env.EXTERNAL_API_KEY}` } }
);
const data =
await response.json();
callback({
outputFields: {
external_score: String(data.score),
external_status: data.status,
sync_timestamp:
new Date().toISOString(),
},
});
}
catch (err) {
console.error(
'Custom code error:', err.message);
callback({ outputFields: {} });
}
};
Custom Code Action β Practical example: Notify Slack
const https = require(
'https');
exports.main =
async (event, callback) => {
const { firstname, lastname, email, dealname, amount } = event.inputFields;
const message = {
blocks: [
{
type:
'section',
text: {
type:
'mrkdwn',
text:
`π *New deal: ${dealname}*\nPerson in charge: ${firstname} ${lastname} (${email})\nAmount: Β₯${Number(amount).toLocaleString()}`,
},
},
],
};
await fetch(process.env.SLACK_WEBHOOK_URL, {
method:
'POST',
headers: {
'Content-Type':
'application/json' },
body: JSON.stringify(message),
});
callback({ outputFields: { slack_notified:
'true' } });
};
Custom Code Action β Practical Example: Lead Scoring with AI
exports.main =
async (event, callback) => {
const {
jobtitle, company, num_employees,
hs_analytics_source, recent_conversion_event_name
} = event.inputFields;
const res =
await fetch(
'https://api.anthropic.com/v1/messages', {
method:
'POST',
headers: {
'x-api-key': process.env.ANTHROPIC_API_KEY,
'anthropic-version':
'2023-06-01',
'content-type':
'application/json',
},
body: JSON.stringify({
model:
'claude-haiku-4-5-20251001',
max_tokens:
256,
messages: [{
role:
'user',
content:
`Please return the score from 0 to 100 and the reason from the lead information below in JSON.
Job type: ${jobtitle}, Company: ${company}, Number of employees: ${num_employees},
Source: ${hs_analytics_source}, Conversion: ${recent_conversion_event_name}
Format: {"score": number, "reason": "reason"}`,
}],
}),
});
const data =
await res.json();
const parsed = JSON.parse(data.content[
0].text);
callback({
outputFields: {
ai_lead_score: String(parsed.score),
ai_score_reason: parsed.reason,
},
});
};
β Secret management:
Confidential information such as API keys can be registered in HubSpot under Settings β Private Apps β Secrets.
process.env.SECRET_NAME Please refer to. Don't write it directly into your code.
7-3γServerless Functions
An HTTP endpoint running on your HubSpot host. Used for form post-processing, webhook reception, and external collaboration.
π Serverless Functions specifications
Endpoint:https://your-portal.hs-sites.com/_hcms/api/function-name
runtime:Node.js 18.x
timeout:10 seconds (must return response)
certification:Can be secured with HubSpot Cookie (logged in user) or API key
file:within the theme serverless/ placed in folder
serverless/contact-lookup.js
const hubspot = require(
'@hubspot/api-client');
exports.main =
async (context, sendResponse) => {
const { email } = context.params;
if (!email) {
return sendResponse({
statusCode:
400,
body: { error:
'email is required' },
});
}
const client =
new hubspot.Client({
accessToken: process.env.HUBSPOT_ACCESS_TOKEN,
});
const result =
await client.crm.contacts.searchApi.doSearch({
filterGroups: [{
filters: [{ propertyName:
'email', operator:
'EQ', value: email }],
}],
properties: [
'firstname',
'lastname',
'email',
'lifecyclestage'],
limit:
1,
});
if (result.results.length ===
0) {
return sendResponse({ statusCode:
404, body: { found:
false } });
}
sendResponse({
statusCode:
200,
body: { found:
true, contact: result.results[
0].properties },
});
};
serverless.json β Function configuration file
{
"runtime":
"nodejs18.x",
"version":
"1.0",
"secrets": [
"HUBSPOT_ACCESS_TOKEN"],
"endpoints": {
"contact-lookup": {
"method":
"GET",
"file":
"contact-lookup.js"
},
"form-handler": {
"method":
"POST",
"file":
"form-handler.js"
}
}
}
7-4 Run Agent step (Breeze AI cooperation)
Invoke the Breeze AI agent in your workflow to process contact data with AI.
π€ What you can do with the Run Agent step
In the Run Agent step of the workflow, you can specify a Breeze AI agent and pass contact properties as input.
Automatic generation of email text, classification of inquiry content, scoring, translation, summarizationEmbed AI processing such as as a no-code workflow step.
Processing results can also be written back to contact properties.
π‘ Typical use cases for Run Agent
Lead classification:Automatically categorizes inquiries into ``interested in products,'' ``support issues,'' and ``recruitment inquiries.''
Personalization:AI automatically generates the text of sales emails based on contact attributes
Sentiment analysis:Calculate sentiment scores for support tickets and automatically prioritize them
translation:Automatically translate multilingual inquiries into Japanese and save in properties
How to use with Custom Code Action:
Run Agent can be used by simply writing a prompt on the settings screen.no codeThis is the AI ββstep.
On the other hand, Custom Code Action allows you to freely write logic in Node.js.
Choose Run Agent for simple AI processing, or choose Custom Code Action for complex logic or external API combinations.
7-5 Summary of this chapter
β
Chapter 7 Checklist
- Understood the difference and usage of Custom Code Action, Serverless Functions, and Run Agent.
- Custom Code Action
event.inputFields and callback(outputFields) learned how to use
- External API calls, Slack notifications, and AI scoring can be implemented with Custom Code Actions
- secret
process.env can be managed securely via
- You can implement HTTP endpoints with Serverless Functions
- You can design AI processing using Run Agent steps.
About the next chapter (Chapter 8):Learn webhooks and event-driven design. We will explain Webhook signature verification, external system cooperation patterns, and idempotent processing.