HubSpot Workflows
5 min readUpdated August 2026

How to Trigger Custom Code from HubSpot Workflows via Webhook

A comprehensive guide on calling Workflood custom code actions in TypeScript or Python directly from HubSpot workflow webhook steps.

Architecture Overview

HubSpot Workflows provide a native **"Send a webhook"** action. By pointing this action to your unique Workflood action webhook URL, you can trigger complex TypeScript or Python scripts immediately when a contact, company, deal, or ticket meets enrollment criteria.

**Key Benefits of this pattern:** - Execute heavy multi-object logic without hitting HubSpot Data Hub limits. - Run Python 3.11 or TypeScript with full npm/pip packages. - Keep 60-second to 5-minute execution timeouts instead of HubSpot's 20-second hard cap.

Configuring the Webhook Action in HubSpot

Follow these steps to wire your HubSpot workflow to Workflood:

  • Open your HubSpot Workflow (Contact-based, Deal-based, or Company-based).
  • Click the **+ (Plus)** button to add an action and select **Send a webhook**.
  • Set the **Method** to `POST`.
  • In the **Webhook URL** field, paste your Workflood Action Webhook URL:
  • `https://workflood.dev/api/h/[ACTION_ID]?token=[SECRET_TOKEN]`
  • Under **Request Body**, select **Customize request body** to pass specific record tokens, or **Include all properties** for full CRM payload transmission.

Recommended Webhook Payload Structure

When configuring customized JSON payloads in HubSpot, structure your payload to include the primary object ID and relevant context properties:

hubspot-webhook-payload.json
{
  "objectId": "{{ contact.hs_object_id }}",
  "email": "{{ contact.email }}",
  "companyId": "{{ contact.associatedcompanyid }}",
  "dealId": "{{ deal.hs_object_id }}",
  "amount": "{{ deal.amount }}",
  "portalId": 12345678,
  "eventType": "DEAL_STAGE_CHANGE"
}

Handling the Payload in Your Workflood Handler

In your Workflood TypeScript or Python action, the payload is automatically injected as the first parameter of the `main` handler:

action-handler.ts
export default async function main(input: {
  objectId?: string
  email?: string
  dealId?: string
  amount?: string
  [key: string]: unknown
}) {
  const raw = Array.isArray(input) ? input[0] : input
  const dealId = raw.dealId || raw.objectId

  if (!dealId) {
    throw new Error('Missing dealId in webhook payload')
  }

  // Fetch deal details using pre-injected HubSpot SDK
  const deal = await hubspot.client.crm.deals.basicApi.getById(dealId, ['dealname', 'amount'])
  
  // Custom computation / enrichment
  const formattedArr = Number(deal.properties.amount || 0) * 1.15

  // Write back to HubSpot
  await hubspot.client.crm.deals.basicApi.update(dealId, {
    properties: { projected_expansion_arr: formattedArr.toFixed(2) }
  })

  return { success: true, dealId, updatedArr: formattedArr }
}

Security: Timing-Safe Webhook Token Verification

Every Workflood webhook endpoint includes a cryptographically randomized 64-character token in the query parameters. Requests missing or presenting an invalid token are rejected with a `401 Unauthorized` before code execution or sandbox allocation occurs.

Turnkey Recipe Available

Associate Deals to Marketing Event on Post-Attendance Creation

Automatically link deals created within 3 months of contact marketing event attendance to the attended event for multi-touch revenue attribution.

Frequently Asked Questions

Does HubSpot wait for the Workflood action to finish?

HubSpot webhooks receive an immediate 202 Accepted response upon queueing. Workflood executes the action asynchronously in its dedicated gVisor sandbox and updates HubSpot via the API client.

What happens if the action fails?

Workflood logs the failure with full stack traces, provides a 1-click dry-run replay, and can trigger instant Slack or email alerts to your engineering channel.