> For the complete documentation index, see [llms.txt](https://docs.screendesk.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.screendesk.io/helpdesk-integrations/hubspot/rule-based-chatflows.md).

# Rule-based chatflows

Add a recording request and completion check to a HubSpot rule-based chatbot, with copyable Node.js code.

{% hint style="info" %}
**Development preview.** This setup depends on the upcoming Screendesk chatbot API. It is not available on the production service yet. Screenshots were captured in an unpublished HubSpot test chatflow. Read the [preview status](/helpdesk-integrations/hubspot/chatbots-and-agents.md) before configuring a customer-facing flow.
{% endhint %}

Use this setup when your HubSpot chatbot follows a sequence of questions and branches. A server-side code action requests a recording, waits while the customer records, and checks the same request when they return.

## What you need

* A connected Screendesk workspace and its [dedicated chatbot token](/helpdesk-integrations/hubspot/chatbot-token.md).
* HubSpot **Data Hub Professional or Enterprise** for the **Run a code snippet** action.
* Permission to edit the chatflow.
* A known HubSpot contact before the recording step. Collect the customer's email first if the visitor is not already identified.
* A test endpoint supplied for the preview, or the production endpoint after release.

This example attaches the recording to the contact timeline. It does not automatically discover the ticket or conversation thread that the visitor is viewing.

## 1. Choose where to offer a recording

Open **Service → Chatflows** and edit your rule-based chatbot. A useful place is after the customer describes the problem and provides their email, but before a troubleshooting or human-handoff step.

For example:

1. Ask what the customer needs help with.
2. Collect their email if it is not already known.
3. Offer to record the problem.
4. Run the Screendesk code action when they accept.
5. Continue to your existing troubleshooting or handoff after completion or skipping.

HubSpot limits the actions available directly after the welcome message. If **Run a code snippet** is missing at the very beginning, add an initial question or prompt, then add the code action after it.

## 2. Add the code action

Click the **+** between the relevant actions and choose **Run a code snippet**. Use a descriptive internal name, such as **Request a Screendesk recording**, and select **Node 24.x**.

![HubSpot Run a code snippet editor using Node 24.x in an unpublished formatting test](https://3820804400-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfW6XSzJSKsNyZnOkSJPt%2Fuploads%2FNSzVUkn2PIHcBnnD3i4g%2Fchatflow-code-editor.png?alt=media)

*The screenshot shows a small formatting-only test. Use the complete integration example below to create real recording requests.*

## 3. Configure the complete example

Set these two values before saving:

| Setting     | Value                                                                                                       |
| ----------- | ----------------------------------------------------------------------------------------------------------- |
| `BASE_URL`  | `https://app.screendesk.io`. Confirm that the chatbot preview is enabled for your workspace before testing. |
| `BOT_TOKEN` | Your dedicated workspace chatbot token, configured server-side. Do not use a general API token.             |

The code first tries the `SCREENDESK_HUBSPOT_BOT_TOKEN` environment variable. If your editor does not provide environment secrets, replace `REPLACE_WITH_HUBSPOT_CHATBOT_TOKEN` only in HubSpot's restricted server-side editor. Keep the credential out of customer messages and public website code.

```javascript
// HubSpot chatflow "Run a code snippet", Node.js 24.
// Configure this only in HubSpot's server-side editor. Never ship the token in
// your website, chat widget JavaScript, a URL, or the message sent to a visitor.
const axios = require('axios');
const BASE_URL = 'https://app.screendesk.io';
const BOT_TOKEN = process.env.SCREENDESK_HUBSPOT_BOT_TOKEN || 'REPLACE_WITH_HUBSPOT_CHATBOT_TOKEN';
const escapeHtml = (value) => String(value).replace(/[&<>"']/g, (character) => ({
  '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
})[character]);

exports.main = async (event, callback) => {
  const state = event.customState || event.session?.customState || {};
  const requestId = state.screendeskRequestId;
  const reply = event.userMessage?.message;
  const cancelled = reply === 'Continue without a recording' || reply === 'skip_recording';
  if (cancelled) {
    callback({ botMessage: 'No problem. Let’s continue here.', responseExpected: false, customState: state });
    return;
  }

  try {
    if (BOT_TOKEN === 'REPLACE_WITH_HUBSPOT_CHATBOT_TOKEN') throw new Error('Missing configuration');
    const options = { headers: { Authorization: `Bearer ${BOT_TOKEN}` }, timeout: 15000 };
    const endpoint = `${BASE_URL}/api/v2/hubspot/recording_requests`;
    const response = requestId
      ? await axios.get(`${endpoint}/${encodeURIComponent(requestId)}`, options)
      : await axios.post(endpoint, {
        contact_id: event.session?.vid,
        customer_email: event.session?.properties?.CONTACT?.email?.value
      }, options);
    const result = response.data;
    const nextState = { ...state, screendeskRequestId: result.request_id };

    if (result.status === 'received') {
      callback({
        botMessage: 'Thanks, your recording has been received. Let’s continue here.',
        responseExpected: false,
        customState: nextState
      });
    } else if (result.status === 'expired') {
      callback({
        botMessage: 'That recording link has expired. Please ask our support team for a new one.',
        responseExpected: false,
        customState: nextState
      });
    } else {
      callback({
        botMessage: `<a href="${escapeHtml(result.recording_url)}" target="_blank" rel="noopener noreferrer">Record your screen</a><br>Return to this chat after sending your recording.`,
        quickReplies: [
          { value: 'recording_sent', label: 'I’ve sent my recording' },
          { value: 'skip_recording', label: 'Continue without a recording' }
        ],
        responseExpected: true,
        customState: nextState
      });
    }
  } catch {
    callback({
      botMessage: 'I couldn’t check the recording right now. We can continue with your support request here.',
      responseExpected: false,
      customState: state
    });
  }
};
```

## 4. Save and connect the next step

Save the action and keep its default next step connected to your existing troubleshooting or human handoff. The code uses `responseExpected: true` while waiting for the recording, so the next visitor reply returns to the same snippet.

When the recording is received, skipped, expired, or the API cannot be reached, the example uses `responseExpected: false` and follows the configured next step. Make that next step useful even when a customer does not provide a recording.

The example stores `screendeskRequestId` in `customState`. Keep this state intact if you adapt the flow. Otherwise, a later reply can create a new request instead of checking the one already shown to the customer.

## 5. Understand the customer messages

| Situation                          | What the example does                                                                           |
| ---------------------------------- | ----------------------------------------------------------------------------------------------- |
| First request                      | Offers **Record your screen**, **I've sent my recording**, and **Continue without a recording** |
| Customer returns before submission | Checks the original request, shows the same link, and keeps waiting                             |
| Recording has been submitted       | Confirms that the recording was received, then continues the flow                               |
| Customer skips                     | Continues support without requiring a recording                                                 |
| Request expired                    | Asks the customer to request a new link from support, then continues                            |
| API or configuration failure       | Continues the support request without claiming a recording was received                         |

The confirmation means the upload was saved. It does not claim that the bot watched the video, that transcription is complete, or that a HubSpot note has already been delivered.

## 6. Keep the link clickable and readable

The example sends an HTML anchor with the visible label **Record your screen**. Its `href` contains the exact recording URL returned by Screendesk, with HTML escaping to preserve query parameters safely.

![Real HubSpot chatbot preview showing the labeled recording link](https://3820804400-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfW6XSzJSKsNyZnOkSJPt%2Fuploads%2FW6IMGMCrcTGPcEGQyRTD%2Fchatbot-wrapped-link-preview.png?alt=media)

*Browser-verified formatting with a placeholder destination. In the actual integration, the destination is the signed link returned by the request API.*

HTML anchors rendered correctly in the tested HubSpot chatbot. Markdown syntax such as `[Record your screen](URL)` appeared literally, so do not substitute Markdown in `botMessage`. The link opens in a new tab; the customer returns to the original chat after submission.

## 7. Preview before publishing

Use a HubSpot test contact and an unpublished flow. HubSpot's preview can perform real actions, so use test records throughout.

1. Complete the initial question and email step.
2. Reach the recording action and confirm the labeled link appears.
3. Select **I've sent my recording** before uploading. The bot should remain pending.
4. Open the recording link, make a short test recording, and submit it.
5. Return to chat and select **I've sent my recording** again.
6. Confirm the receipt message and the next flow step.
7. Open the test contact in HubSpot and check its timeline for the Screendesk recording note.
8. Run a separate preview with **Continue without a recording** and confirm that support continues.

The complete rule-based conversation with a real uploaded recording remains a rollout check. The API/upload/contact-note path and the chatbot's HTML link rendering were verified separately. See [Testing and troubleshooting](/helpdesk-integrations/hubspot/chatbot-testing-and-troubleshooting.md) for the full validation matrix.

## Adapting the destination

If your server-side integration already knows the correct ticket or conversation ID, it can supply `ticket_id` or `thread_id` when creating the request. Do not ask the customer for these internal IDs or choose the contact's latest ticket automatically.

HubSpot's documented snippet event provides a contact ID but does not guarantee a conversation thread ID. A thread-specific implementation needs a trusted source for that context and the additional conversation permissions described in the [overview](/helpdesk-integrations/hubspot/chatbots-and-agents.md).

Official reference: [HubSpot code snippet actions](https://developers.hubspot.com/docs/api-reference/latest/conversations/chat-configuration/working-with-chatbots).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.screendesk.io/helpdesk-integrations/hubspot/rule-based-chatflows.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
