Skip to content

Repository files navigation

HubSpot Request Verify

npm license

A small, zero runtime dependency utility for verifying requests sent by HubSpot webhooks, custom apps, and UI extensions.

It supports HubSpot signature versions v1, v2, and v3, uses timing-safe signature comparisons, and rejects v3 requests outside HubSpot's five-minute timestamp window.

Requirements

  • Node.js 22 or newer
  • Application client secret from HubSpot

Installation

npm install hubspot-request-verify

Usage

Pass the incoming Web API Request and your HubSpot app's client secret to verifyRequest:

import { verifyRequest } from "hubspot-request-verify";

export async function handleHubSpotRequest(
  request: Request
): Promise<Response> {
  const clientSecret = process.env.HUBSPOT_CLIENT_SECRET;

  if (!clientSecret) {
    return new Response("Server is not configured", { status: 500 });
  }

  const result = await verifyRequest(request, clientSecret);

  if (!result.isValid) {
    console.warn(`Rejected HubSpot request: ${result.reason}`);
    return new Response("Invalid signature", { status: 401 });
  }

  // Verification clones the request, so its body is still available here.
  const payload = await request.json();
  console.log(`Verified HubSpot ${result.version} request`, payload);

  return new Response("OK");
}

verifyRequest detects the signature version from the request headers and returns a discriminated result:

type VerificationResult =
  | { isValid: true; version: "v1" | "v2" | "v3" }
  | {
      isValid: false;
      version?: "v1" | "v2" | "v3";
      reason: string;
    };

The package also exports getSignatureVersion, verifyV1Signature, verifyV2Signature, and verifyV3Signature for cases where lower-level control is useful.

All verification functions require secret to be a non-empty string and throw a TypeError otherwise. Verification failures caused by the request itself — missing headers, an unknown version, an expired v3 timestamp, a signature mismatch — are returned as { isValid: false } results rather than thrown.

This package is distributed as ESM only and does not include a separate CommonJS build.

Signature versions

verifyRequest detects the signature version from the request headers. HubSpot requests can include signatures for more than one version: when x-hubspot-signature-version declares v1 or v2, that version is used even if x-hubspot-signature-v3 is also present. Otherwise, the presence of x-hubspot-signature-v3 selects v3.

Which version a request uses is determined by HubSpot.

Replay protection. Only v3 includes a signed timestamp, so only v3 rejects requests outside the five-minute window. For v1 and v2, verifyRequest confirms the request was signed with your client secret, but those versions carry nothing that distinguishes a fresh request from a replayed one. If replay protection matters for your endpoint, implement it alongside verification.

While undocumented, x-hubspot-request-timestamp may also appear on v1 and v2 requests, but it is not covered by those signatures and can be altered or removed without invalidating them.

Important

Signature verification depends on the exact HTTP method, URL, and raw request body received from HubSpot. Construct the Request with the original values and verify it before parsing or changing its contents. Keep the client secret on the server and never expose it to browser code.

You can review the HubSpot Documentation for more information.

Security

If you have found a security vulnerability please see SECURITY.md for steps on how to report.

Contributing

If you'd like to contribute to the project please see the guide.

License

MIT

About

Small utility for verifying signatures in HubSpot requests from UI Extensions, Webhooks, and custom Apps

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages