HubSpot custom coded actions: call an outside API safely
A HubSpot custom code action runs Node.js or Python in a workflow for 20 seconds, with secrets as env vars. Start slow API jobs there; don't wait.

A HubSpot custom coded action is a workflow step that runs your own JavaScript (Node.js) or Python code for each enrolled record, on Data Hub Professional or Enterprise. Secrets you add to the action arrive as environment variables, so the code can call an outside API with a key HubSpot keeps out of the code. The action must finish within 20 seconds, so it can start a slow job but not wait for it.
The HubSpot facts come from HubSpot's Custom code workflow actions developer page. The Sume facts come from Create a run and Authentication. All were read on 2026-09-29. Sume has no official HubSpot app; the action makes a plain HTTPS call.
What are the limits of a custom code action?
HubSpot marks Python as beta; Node.js is the default language.
| Limit | Value |
|---|---|
| Plans | Data Hub Professional or Enterprise |
| Run time | Must finish within 20 seconds |
| Memory | Up to 128 MB |
| Secrets | Environment variables; all secret values together at most 1000 characters |
| Properties passed in | Up to 50 per action |
| String output values | Up to 65,000 characters |
| Rate limit setting | Off by default; executions per second, minute or hour |
How do I call an external API from the action?
A few settings in the action, then the code. The example starts a Sume Format run for an enrolled deal.
- In the workflow, add the Custom code action. Under Secret, add your API key, for example as
SUME_API_KEY. - Under Properties to include in code, add the fields the call needs; they arrive in
event.inputFields. The enrolled record's id isevent.object.objectId. - Use
axios, which HubSpot lists among the Node.js libraries you canrequire(). - Return the job or run id with
callback({ outputFields: … })and define it under Data outputs, so a later step can store it on the record.
const axios = require("axios");
exports.main = async (event, callback) => {
const dealId = event.object.objectId;
const res = await axios.post(
"https://api.sume.com/v1/formats/acme/product-promo/runs",
{
instruction: "15-second vertical promo",
input: { product_name: event.inputFields["dealname"] },
generation_spend_cap_usd: 20,
communication: { webhook_url: "https://example.com/hooks/sume" },
},
{
headers: {
Authorization: `Bearer ${process.env.SUME_API_KEY}`,
"Idempotency-Key": `hubspot-deal-${dealId}-v1`,
},
},
);
callback({ outputFields: { sume_run_id: res.data.data.id } });
};Will HubSpot retry the call, and can that bill twice?
HubSpot retries when the call fails with a rate-limit error, or a 429 or 5XX error from axios or @hubspot/api-client: it reattempts the action for up to three days, starting one minute after the failure, with gaps of up to eight hours. Each attempt runs your code again, so each sends the create again.
That is safe only with a stable key. Sume's docs say to derive the Idempotency-Key from the thing being made, such as the record id plus a version you bump for a deliberate re-run. The same key with the same body returns the original run with no second run and no second charge; a changed body under the same key is 409 idempotency_conflict and nothing runs. Don't use Math.random or the time: HubSpot notes that Math.random can repeat across executions, and a key that changes defeats the check.
How do I get the result back into HubSpot?
Not in the same action. Sume's video docs say one video generation typically takes 30 seconds to several minutes, and the action has 20. The create answers 202 with the run receipt. Set communication.webhook_url to an HTTPS endpoint you run, which receives one signed POST when the run completes or fails, and have that endpoint update the record. Signed webhooks for video runs covers verifying it.
Cap the spend per record with generation_spend_cap_usd, up to the platform maximum of $500, and use the action's rate limit so a large enrollment doesn't send every create at once.
Sources
Related posts
More in Integrations
- IFTTT webhooks: call an API that needs an API key
IFTTT's Webhooks service can send any web request with custom headers, on Pro plans. Route paid API calls through your own endpoint.
- Looping by Zapier: send one API request per item in a list
Looping by Zapier runs every later action once per list value, all in parallel, up to 500 times. Pace and key paid API calls to match.
- Make.com error handling: retry a paid API call without duplicates
Make.com has five error handlers; Retry stores the failed bundle and reruns it. Send a fixed Idempotency-Key so a rerun can't bill twice.
- n8n error workflow: make it fire when an API job fails
An n8n error workflow runs only when an execution fails. A remote job that ends failed is a normal 200 read, so check its status and throw with Stop And Error.
Written by Sume