Supported products
Requires one of the following products or higher.
Content HubContent HubEnterprise
Last modified: August 22, 2025
Please note:This page contains reference documentation for newer, project-based serverless functions. For serverless functions built using the design manager, check out the serverless functions (design manager) reference. If you’re looking to implement serverless functions in a UI extension, check out the CRM customization guide.
On this page, you’ll find reference information for serverless functions built with developer projects. Though conceptually the same as serverless functions built with the design manager, project-based serverless functions require a different serverless.json configuration, enable the use of third-party dependencies, and have more direct access to private app access tokens for authentication. For a high-level overview of serverless functions, see the serverless functions overview. And to get started building project-based serverless functions, check out the get started guide.

File structure

Serverless functions are packaged within a .functions directory within the app directory of a project. The .functions directory can have any name you’d like, as long as it ends in .functions. In the .functions directory, you’ll need to include three files:
  • serverless.json: the configuration file for the serverless function.
  • function.js: A JavaScript file containing the code to execute. This file can have any name, and you’ll reference the file name in serverless.json.
  • A package.json configuration file to contain the dependencies needed to execute the function.
project-folder/
│── src/
├──── app
     ├── app.json
     ├── app.functions/
       ├── function.js
       ├── package.json
       └── serverless.json
└─ hsproject.json
Below, learn more about each file.

Serverless.json

The serverless.json file stores the serverless function configuration. This is a required file, and maps your functions to their endpoints.
Please note:If you’re upgrading your existing serverless functions to be built using developer projects, note that the runtime, environment, and version fields have been removed.
{
  "appFunctions": {
     "functionName": {
      "file": "function.js",
      "secrets": [],
      "endpoint": {
        "path": "fetch-quote",
        "method": ["GET"]
       }
    }
  }
}

FieldTypeDescription
functionNameObjectAn object containing the serverless function’s configuration details. This object can have any name.
fileStringThe name of the JavaScript file containing the serverless function code to execute.
secretsArrayAn array containing names of secrets that the function will use for authenticating requests. Learn more about authentication.
endpointObjectAn object containing details about the endpoint that you can hit to invoke the function. The path field defines the last value in the /hs/serverless/<path> endpoint path, while the method field defines the request method. Learn more about endpoints.
Please note:Do not assign the same name to your secrets and environment variables. Doing so will result in conflicts when returning their values in the function.

Function file

Serverless functions built with developer projects use the NodeJS runtime (version 18 and higher). Lower versions of Node cannot be specified. In addition, serverless functions should be asynchronous, using await and try/catch instead of promise chaining. Below is an example of JavaScript code that can be executed by a serverless function. This code is taken from the get started with serverless functions guide and fetches a quote via the Zen Quotes API. Learn more about authenticating requests.
const axios = require('axios');

exports.main = async (context) => {
  try {
    // Make GET request to the ZenQuotes API
    const response = await axios.get('https://zenquotes.io/api/random');

    // Extract the quote data (first item in the array)
    const quoteData = response.data[0];

    // Log the quote and author to console
    console.log(`"${quoteData.q}" — ${quoteData.a}`);

    // Return a properly formatted response with status code and body
    return {
      statusCode: 200,
      body: quoteData,
      headers: {
        'Content-Type': 'application/json',
      },
    };
  } catch (error) {
    // Handle any errors that occur during the request
    console.error('Error fetching quote:', error.message);

    // Return an error response
    return {
      statusCode: 500,
      body: { error: 'Failed to fetch quote' },
      headers: {
        'Content-Type': 'application/json',
      },
    };
  }
};

package.json

In the package.json file, you can specify dependencies to include in the dependencies field. When the app is built, dependencies will be bundled with your function code. All dependencies must be published to NPM and be public. For example, if you wanted to add the lodash library in a serverless function, you would first update package.json to include the dependency:
{
  "name": "example-serverless-function",
  "version": "0.1.0",
  "author": "HubSpot",
  "license": "MIT",
  "dependencies": {
    "@hubspot/api-client": "^7.0.1",
    "axios": "^0.27.2",
    "lodash": "^4.17.21"
  }
}

Endpoints

Serverless functions for the CMS are invoked by calling its public URL, which has the following structure: https://<domain>/hs/serverless/<path>.
ParameterDescription
<domain>A domain connected to the HubSpot account. This can be any connected domain, including the default <hubId>.hs-sites.com domain.
/hs/serverless/The path reserved for serverless functions. All endpoints exist inside this path.
<path>The path value specified in the serverless.json file.
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>. If the serverless function’s path field was set to fetch-quote, the full URL would be: https://website.com/hs/serverless/fetch-quote

Authentication

Serverless functions requests built with projects can be authenticated using either the app’s private app access token or a secret. Calls authenticated with private app access tokens count against your API call limits.
  • Private app access tokens can be used for authenticating HubSpot API requests, using the value PRIVATE_APP_ACCESS_TOKEN.
  • Secrets can be referenced by name, and must also be included in the secrets array of the serverless.json file. To create, manage, and view secrets associated with your account, use the set of hs secrets CLI commands.
  • In the function file, authentication for app tokens and secrets are both accessed with process.env.
Below is an example of the same request but using the private app access token or secret for authentication (found in the headers object).
const axios = require('axios');

exports.main = async (context = {}) => { try { const response = await axios.get( `https://api.hubspot.com/cms/v3/hubdb/tables/109906251/rows?sort=random()&limit=1`, { headers: { Authorization: `Bearer ${process.env.PRIVATE_APP_ACCESS_TOKEN}`, 'Content-Type': 'application/json', }, } ); return response.data.results; } catch (error) { console.log('Error details:', error); } };

Context object

The context object contains contextual information about the function’s execution, stored in the following parameters.
ParameterDescription
accountIdThe HubSpot account ID containing the function.
bodyPopulated if the request method is POST with a content type of application/json.
contactIf the request is from a cookied contact, the contact object will be populated with a set of basic contact properties along with the following information:
  • vid: The contact’s visitor ID.
  • isLoggedIn: when using CMS Memberships, this will be true if the contact is logged in to the domain.
  • listMemberships: an array of contact list IDs that this contact is a member of.
headersContains the headers sent from the client hitting your endpoint.
paramsPopulated with query string values, along with any HTML Form-POSTed values. These are structured as a map with strings as keys and an array of strings for each value.

Headers

If you need to know the headers of the client that’s hitting your endpoint, you can access them through context.headers, similar to how you would access information through context.body. Below, review some of the common headers that HubSpot provides. For a full list, see MDN’s HTTP headers documentation.
headerDescription
acceptCommunicates which content types the client understands, expressed as MIME types. See MDN.
accept-encodingCommunicates the content encoding the client understands. See MDN.
accept-languageCommunicates which human language and locale is preferred. See MDN.
cache-controlHolds directives for caching. See MDN.
connectionCommunicates whether the network connection stays open. See MDN.
cookieContains cookies sent by the client. See MDN.
hostCommunicates the domain name and TCP port number of a listening server. See MDN.
true-client-ipIP address of the end-user. See Cloudflare true-client-ip.
upgrade-insecure-requestsCommunicates the clients preference for an encrypted and authenticated response. See MDN.
user-agentVendor-defined string identifying the application, operating system, application vendor, and version. See MDN.
x-forwarded-forIdentifies the originating IP address of a client through a proxy or load balancer. See MDN.

Redirect by sending a header

You can perform a redirect from your serverless function by sending a response with a location header and 301 statusCode.
sendResponse({
  statusCode: 301,
  headers: {
    Location: 'https://www.example.com',
  },
});

Limits

Serverless functions are intended to be fast and have a narrow focus. To enable quick calls and responses, HubSpot serverless functions have the following limits:
  • 50 secrets per account.
  • 128MB of memory.
  • No more than 100 endpoints per HubSpot account.
  • You must use contentType of application/json when calling a function.
  • Serverless function logs are stored for 90 days.
  • 6MB on an AWS Lambda invocation payload.
Execution limits
  • Each function has a maximum of 10 seconds of execution time.
  • Each account is limited to 600 total execution seconds per minute.
This means either of these scenarios can happen:
  • 60 function executions that take 10 seconds each to complete.
  • 6,000 function executions that take 100 milliseconds to complete.
Functions that exceed those limits will throw an error. Execution count and time limits will return a 429 response. The execution time of each function is included in the serverless function logs. To assist in avoiding these limits, limit data is provided automatically to the function context during execution. You can use that to influence your application to stay within those limits. For example, if your application requires polling your endpoint, then you can return with your data a variable to influence the frequency of the polling. That way when traffic is high you can slow the rate of polling avoiding hitting limits, then ramp it back up when traffic is low.