Supported products
Requires one of the following products or higher.
Content HubContent HubEnterprise
Last modified: August 22, 2025
Serverless functions provide a way to execute JavaScript through HubSpot on the server-side, preventing it from being exposed to the client. This can be especially important for making API requests that contain sensitive information, such as an API key or other credentials. When the function is invoked, HubSpot executes the function on its back-end, which also means you don’t need to provide your own back-end servers to make requests for your CMS website. HubSpot’s serverless functions use the NodeJS runtime. In this guide, you’ll walk through creating a simple serverless function that fetches a quote from a third-party service, then displays that quote on a website page. To build and deploy the serverless function to your account, you’ll use a HubSpot developer project.
Please note:While the previously documented method of uploading serverless functions to the design manager is still supported, it’s recommended to use developer projects, as they include a private app for easier authentication while also allowing for third-party dependencies. The content of this guide has been updated to use the newest method. You can find reference information for design manager-based serverless functions in the reference section.
For a high-level review of serverless functions, check out the serverless functions overview. You can also check out the HubSpot Developers YouTube channel for a walkthrough of using serverless functions in a CMS project.

Prerequisites

Before starting this tutorial, you’ll need:
1

Create a project locally

Start by creating a project locally so that you can build and deploy it to your account.
  • In the terminal, navigate to the directory where you’ll be storing your project using the cd command.
cd Documents/Dev/serverless-function-project
  • Run hs project create to create a new project.
hs project create
  • Follow the terminal prompts to create your project. For the template, select Create an empty project (no template).
Terminal prompt option for creating an empty project with no template
  • Navigate into the new project directory using the cd command. For example, if you named your project my new project:
cd my-new-project
2

Add a serverless function to the project

Once your project has been created, open it in your preferred editor, such as VS Code. HubSpot will have automatically created a project directory along with an empty src directory and a hsproject.json configuration file. To add a serverless function to the project, create an app directory inside the src directory, then add the following directories and files within it:
  • app/app.json: the app configuration file.
  • app/app.functions: the serverless function directory. You can use any name you’d like, as long as it ends in .functions.
  • app/app.functions/function.js: the JavaScript code that will be executed when the function is invoked.
  • app/app.functions/serverless.json: the serverless function configuration file.
  • app/app.functions/package.json: includes necessary dependencies.
Project structure after adding the above files and folders to the project directory
  • Copy the example code below into your respective local app.json, function.js, serverless.json, and package.json files. Under each .json code block, you’ll also find a table containing field definitions.
{
"name": "Serverless function app",
"description": "This app runs a serverless function to fetch a quote using the Zen Quotes API.",
"scopes": ["crm.objects.contacts.read", "crm.objects.contacts.write"],
"uid": "serverless-function-app",
"public": false
}
FieldTypeDescription
nameStringThe name of the app, which will display in HubSpot’s UI.
descriptionStringThe app’s description, which will display in HubSpot’s UI.
scopesArrayThe scopes that the app has access to for authenticating requests with the private app access token. The two scopes above are the minimum required scopes. No additional scopes are added for this tutorial, as you won’t need to use the private app access token for the request you’ll be making.
uidStringThe app’s unique identifier. This can be any string, but should meaningfully identify the app. HubSpot will identify the app by this ID so that you can change the app’s name locally or in HubSpot without removing historical or stateful data, such as logs.
publicStringSet to false for private apps.
  • After adding the above code, save your changes.
3

Upload the project to HubSpot

With your changes saved, you can now upload the project to HubSpot to build and deploy the app and serverless function.
  • In the terminal, run hs project upload.
hs project upload
  • Confirm that you want to create the project in the account. You won’t need to confirm again this for after initial creation. The terminal will display the current status of the build and deploy steps as they progress.
  • Once the upload completes, run hs project open to view the project in HubSpot.
hs project open
In HubSpot, you’re able to view the project details, build and deploy logs, serverless function logs, manage the project and app, and more. Learn more about managing projects in HubSpot.
4

Test the function

With the serverless function deployed, you can invoke it by hitting its public URL. Serverless functions built with developer projects have the following public URL structure: https://<domain>/hs/serverless/<endpoint-path-from-config>.
  • <domain>: you can use any domain connected to the account. For example, if both website.com and subdomain.brand.com are connected to the account, you could call the function using https://website.com/hs/serverless/<path> or https://subdomain.brand.com/hs/serverless/<path>.
  • <endpoint-path-from-config>: the value in the path field in serverless.json.
Based on the example code provided in this tutorial, the public URL to invoke the function will be: https://<domain>/hs/serverless/fetch-quote.
In the URL to call the function, the endpoint path is global rather than scoped to the app or project. If you have identical endpoint paths across multiple apps or projects, the most recently deployed endpoint function will take precedence.
To view the function’s output, navigate to that URL in your browser, replacing the domain with one of your HubSpot-hosted domains. If you haven’t connected a custom domain, you can use one of the default domains that HubSpot provides: <hubId>.hs-sites.com (e.g., 123456.hs-sites.com). Your browser should display the data returned by the Zen Quotes API.
Browser displaying the data returned by the Zen Quotes API
5

Invoke the function from a website page

Now that you’ve created the serverless function and confirmed that it returns data, implement it into a page to see the data returned in a more realistic context. For this tutorial, you’ll create a page template containing some custom JavaScript and HTML to execute the function and display the response data.First, create a new page template:
  • In your local environment, create a new directory to contain your page template. For the purposes of this tutorial, create this directory outside of the project directory created by hs project create.
  • In the terminal, navigate into that directory using the cd command.
cd Documents/Dev/serverless-page
  • Run hs create template "serverless-template" to create a new template named serverless-template.
hs create template "serverless-template"
  • Select the page template type.
  • Open the newly created page template file in your editor, then replace the boilerplate code with the code below. Be sure to replace <domain> in the function endpoint URL (fetch('http://<domain>/hs/serverless/fetch-quote')) with a domain that’s connected to your HubSpot account.
<!--
    templateType: page
    label: Serverless function example template
    isAvailableForNewContent: true
-->
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>{{ content.html_title }}</title>
    <meta name="description" content="{{ content.meta_description }}" />
    {{ standard_header_includes }}
  </head>
  <body>
    {% module "page_template_logo" path="@hubspot/logo" label="Logo" %}
    <!-- Button to fetch quote via serverless function -->
    <div class="serverless-container">
      <h2>Get quote</h2>
      <p class="subtitle">Click the button to fetch and display a quote.
      <button id="data-button" type="button">Get quote!</button>
      <div id="data-container">
        <p class="data">
      </div>
    </div>
    <!-- End button section -->

    {{ standard_footer_includes }}

    <!-- JavaScript that invokes the serverless function
     and adds returned data into the "data" text element -->
    <script>
      const dataButton = document.getElementById('data-button');
      const dataContainer = document.getElementById('data-container');

      dataButton.addEventListener('click', function (e) {
        console.log('Button clicked!');

        // Show loading state
        dataContainer.innerText = 'Loading...';
        // Replace <domain> with your own domain
        fetch('http://<domain>/hs/serverless/fetch-quote')
          .then((response) => {
            if (!response.ok) {
              throw new Error(`HTTP error! Status: ${response.status}`);
            }
            // Parse the JSON response
            return response.json();
          })

          .then((data) => {
            console.log('Raw data received:', data);

            // Clear container
            dataContainer.innerText = '';

            // Create the paragraph element
            const newDataElement = document.createElement('p');
            newDataElement.innerText = `"${data.q}" — ${data.a}`;
            dataContainer.appendChild(newDataElement);
          });
      });
    </script>
  </body>
</html>
  • Save the file, then run hs upload to upload it to HubSpot. Follow the terminal prompts to select the local source and destination path. For this tutorial, you can just press Enter for each prompt to use the default paths.
Next, create a new page from the template in HubSpot.
  • Navigate to the website pages dashboard of your HubSpot account by running the hs open website-pages.
hs open website-pages
See the full list of available open shortcuts by running hs open --list.
  • In your browser, click Create in the upper right to create a new page.
  • In the dialog box, select a domain to use, then assign a Page name. The domain of the page will need to match the domain that you’re using to invoke the serverless function to avoid cross-origin (CORS) errors. You can either select that domain from the dropdown menu, or update the endpoint URL in the page template code (fetch('http://<domain>/hs/serverless/fetch-quote')) to use the domain that you select in this step.
  • Click Create page.
  • On the template selection screen, search for your new template. You can use the label or the file name to search for the template (e.g., “Serverless function example page”). The template will appear under Other templates, as it’s not part of a theme.
Other templates section showing the newly created page template
  • Click Select template.
  • In the page editor, click Preview in the upper right.
Page editor preview button
  • Click Open in a new tab.
In the new tab, you should now be able to click the button to fetch and display quotes.
Demonstration of example button that fetches and displays quotes from the Zen Quotes API

Next steps

Now that you’ve created and implemented a serverless function that interacts with a third-party API, there are a few ways to continue building up your serverless function usage, such as:
  • Including authentication in a request to make calls to HubSpot APIs or other third-party authenticated APIs.
  • Implement the button and JavaScript code into a module rather than hardcode it into the page template. This would give you a more portable option, enabling the function to be invoked from any page that the module is added to. To do so, you would create a module, then add the HTML into the module’s module.html file, and the JavaScript into the module.js file.
<div class="serverless-container">
<h2>Get quote</h2>
<p class="subtitle">Click the button to fetch and display a quote.</p>
<button id="data-button" type="button">Get quote!</button>
<div id="data-container">
<p class="data"></p>
</div>
</div>