Skip to main content

Supported products

This page contains reference information for CMS serverless functions built with developer projects on version 2026.09 of the developer platform, the current version at the time of writing. These functions are public endpoints served from https://<domain>/hs/serverless/<path>, which you can call from your website pages. Check developer platform versioning for the latest version.
Don’t build serverless functions on 2025.1 or 2025.2. Version 2025.1 is sunset, and 2025.2 doesn’t support serverless functions. If you have a project on a version older than 2026.03, migrate it to the latest version. Projects already on 2026.03 run the same runtime and don’t need to be migrated at this time.
For serverless functions built using the design manager, which are being sunset, see the serverless functions (design manager) reference and migrate design manager serverless functions to developer projects. For private functions that you call from UI extensions rather than from your website, see the developer platform serverless functions reference. For a high-level overview of serverless functions, see the serverless functions overview. To build your first function, check out the get started guide.

Project structure

Serverless functions belong to an app with static auth and private distribution. Apps that use OAuth can’t include serverless functions. To create a project with an app and an example endpoint function, run:
To add an endpoint function to an existing project’s app, run hs project add --features app-function-endpoint from the project directory. Functions live in the app’s functions directory. Each function is a pair of files: a *-hsmeta.json configuration file and the JavaScript file it points to. One package.json file holds the dependencies for every function in the directory.
hs project upload sends only hsproject.json and the src/ directory to HubSpot. Files at the project root, such as a README, aren’t uploaded.

Function configuration

Each function has its own *-hsmeta.json file. The file name can be anything that ends in -hsmeta.json.
fetch-quote-hsmeta.json
The runtime is set by the platform version in hsproject.json, not by the function configuration. Functions on 2026.03 and later run Node.js 24. There’s no runtime, version, or environment field. To give a function values it reads at runtime, use secrets.

Function file

A function file exports an async function named main, which receives the context object and returns the response. Use await with try/catch rather than promise chains or callbacks. Below is the function from the get started guide, which fetches a quote from the Zen Quotes API.
fetch-quote.js
Keep the following in mind:
  • Catch every error. If a function throws, it returns a 500 response whose body includes the raw error message, which anyone who calls the endpoint can see. Log the details and return a generic message instead.
  • Await all work before you return. Work that’s still running when the function returns may never finish, including follow-up requests whose result the caller doesn’t need.
  • Don’t log secrets. Anyone with access to the app can read its logs.

Responses

The returned object sets the response: To redirect the visitor, return a 301 status code with a Location header:

package.json

List the packages your functions import in src/app/functions/package.json. When you upload the project, HubSpot installs them and bundles them with your function code. Packages must be published to the public npm registry. Nothing is preloaded, so list every package your functions use. To add a package, run npm install in the src/app/functions/ directory. For example, to add axios:
This updates package.json:
Learn more about adding npm packages.

Endpoints

A public function is invoked by calling its URL, which has the following structure: https://<domain>/hs/serverless/<path> 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 function’s path is fetch-quote, the full URL would be: https://website.com/hs/serverless/fetch-quote When you call a function from a page on your site, use a relative URL, such as /hs/serverless/fetch-quote. The request then goes to the page’s own domain, which avoids cross-origin (CORS) errors.
Endpoint paths are global across the account rather than scoped to an app or project. If two projects use the same path, the most recently deployed function takes precedence.
hs project upload builds the project and deploys the build as soon as it succeeds, so an endpoint is public as soon as you upload it. To upload without deploying, add --skip-auto-deploy. You can redeploy any previous build from the project in HubSpot.

Authentication

Functions can authenticate requests with the app’s built-in access token or with secrets. In the function file, you read both with process.env.
  • App access token: every function can read process.env.PRIVATE_APP_ACCESS_TOKEN to call HubSpot APIs. The token has the scopes listed in the app’s requiredScopes in app-hsmeta.json. Don’t add it to secretKeys. Calls made with the token count against your API call limits.
  • Secrets: store other credentials, such as third-party API keys, as secrets with hs secret add <NAME>, then list each secret’s name in the function’s config.secretKeys array. Learn more about managing secrets.
The app access token is available only after the app is installed in the account. After your first upload, run hs project install-app from the project directory. Until the app is installed, HubSpot API calls that use the token fail. If you change the app’s scopes later, upload the project and run hs project install-app again, so the token gets the new scopes.
To find the scopes a HubSpot API call needs, open that endpoint’s page in the API reference. Keep oauth in requiredScopes, along with any scopes your functions use. Below is the same request authenticated with the app access token and with a secret.
For this request, the app’s requiredScopes would include hubdb.rows.read.

Context object

The context object passed to a public function contains information about the request.
Public functions don’t receive context.query; read query parameters from context.params. Functions built with developer projects also don’t receive context.limits, which design manager functions used to report remaining executions. A function that exceeds the execution limits returns a 429 response.

Headers

If you need to know the headers of the client that’s calling your endpoint, you can access them through context.headers. Below, review some of the common headers. For a full list, see MDN’s HTTP headers documentation.

Logs

Every call to a function is logged, along with the output of any console.log() and console.error() statements. To view the logs, run hs project open to open the project in HubSpot, open the app, then click the Logs tab. hs project logs --function <uid> prints a link to the logs, but doesn’t show log lines for public functions.

Limits

Serverless functions are intended to be fast and have a narrow focus. Keep the following limits in mind:
  • Each function has a maximum of 15 seconds of execution time. Give every retry or polling loop a limit, so a function doesn’t run until it times out.
  • Functions that exceed the account’s execution limits return a 429 response. There’s no field in context that reports how close you are to the limits, so handle 429 responses in the code that calls the function, for example by retrying after a delay.
  • Functions may experience cold starts after periods of inactivity. To reduce their impact, keep functions lightweight and minimize the number of external API calls.
  • Requests with a body must use a content type of application/json. Form posts (application/x-www-form-urlencoded) return a 415 response, so send form data as JSON.
  • Only privately distributed apps with static auth can include serverless functions.
Last modified on November 3, 2026