Skip to main content
This page documents all server-side API endpoints available in the HubSpot Form Builder application.

Base URL

All API endpoints are served from the Express server running on port 3001 by default.
http://localhost:3001

OAuth Endpoints

Manage HubSpot OAuth authentication flow.

Initiate OAuth Flow

GET /oauth/hubspot/install
Redirects the user to HubSpot’s OAuth authorization page. Location: main/server/src/oauth.ts:46

Flow

  1. Generates random state token for CSRF protection
  2. Stores state in memory with timestamp
  3. Builds authorization URL with:
    • client_id from environment
    • redirect_uri from environment
    • scope from environment
    • state token
  4. Redirects user to HubSpot

Environment Variables Required

HUBSPOT_CLIENT_ID
string
required
HubSpot application client ID
HUBSPOT_REDIRECT_URI
string
required
OAuth callback URL (must match HubSpot app settings)
HUBSPOT_SCOPES
string
required
Space-separated OAuth scopes (e.g., “forms”)

Response

302 Redirect to https://app.hubspot.com/oauth/authorize?...

OAuth Callback

GET /oauth/hubspot/callback?code=xxx&state=yyy
Handles the OAuth callback from HubSpot and exchanges authorization code for access token. Location: main/server/src/oauth.ts:53

Query Parameters

code
string
required
Authorization code from HubSpot
state
string
required
CSRF protection token (must match stored state)

Request Flow

  1. Validates code and state parameters
  2. Verifies state token exists in store
  3. Exchanges code for tokens via POST to https://api.hubapi.com/oauth/v1/token
  4. Stores tokens in memory with expiration
  5. Redirects to frontend URL or returns JSON

Token Exchange Request

POST https://api.hubapi.com/oauth/v1/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&client_id=xxx
&client_secret=xxx
&redirect_uri=xxx
&code=xxx

Success Response (with FRONTEND_URL)

302 Redirect to {FRONTEND_URL}?connected=true&portalId=123456

Success Response (without FRONTEND_URL)

connected
boolean
Always true on success
portalId
number | null
HubSpot portal (account) ID
{
  "connected": true,
  "portalId": 123456
}

Error Responses

400
Bad Request
Invalid or missing state/code parameters
{
  "error": "Invalid state or code"
}
500
Internal Server Error
Token exchange failed or server error
{
  "error": "Token exchange failed",
  "details": "..."
}

Token Storage

Tokens are stored in an in-memory Map:
type TokenRecord = {
  accessToken: string;
  refreshToken: string;
  expiresAt: number; // Unix timestamp in milliseconds
  portalId?: number;
};
Note: Tokens are lost on server restart. For production, implement persistent storage.

Check OAuth Status

GET /oauth/hubspot/status
Checks if the server has valid OAuth tokens. Location: main/server/src/oauth.ts:118

Response

connected
boolean
Whether any OAuth tokens are stored
{
  "connected": true
}
Note: This only checks if tokens exist, not if they’re valid or expired.

Logout

POST /oauth/hubspot/logout
Clears all stored OAuth tokens. Location: main/server/src/oauth.ts:123

Response

success
boolean
Always true
message
string
Confirmation message
{
  "success": true,
  "message": "Session closed successfully"
}

Forms API Endpoints

Fetch HubSpot forms and their schemas.

List Forms

GET /api/forms
Retrieves all available HubSpot forms. Location: main/server/src/forms.ts:126

Authentication

Requires valid OAuth token in server’s token store.

Response

forms
HubSpotForm[]
Array of form objects
{
  "forms": [
    {
      "id": "abc-123",
      "name": "Contact Form",
      "createdAt": 1640000000000,
      "updatedAt": 1650000000000
    },
    {
      "id": "def-456",
      "name": "Newsletter Signup",
      "createdAt": 1640000000000,
      "updatedAt": 1650000000000
    }
  ]
}

HubSpotForm Type

id
string
Unique form identifier
name
string
Form display name
createdAt
number
Creation timestamp (milliseconds)
updatedAt
number
Last update timestamp (milliseconds)

Error Responses

401
Unauthorized
Not connected to HubSpot or no valid token
{
  "error": "Not connected to HubSpot"
}
500
Internal Server Error
Failed to fetch from HubSpot API
{
  "error": "Failed to fetch forms from HubSpot",
  "details": "..."
}

HubSpot API Called

GET https://api.hubapi.com/marketing/v3/forms
Authorization: Bearer {accessToken}
Content-Type: application/json

Get Form Schema

GET /api/forms/abc-123
Retrieves detailed schema for a specific form, including all fields and their configurations. Location: main/server/src/forms.ts:168

Path Parameters

formId
string
required
The HubSpot form ID

Authentication

Requires valid OAuth token in server’s token store.

Response

schema
FormSchema
Normalized form schema
debug
object
Debug information about the form structure
{
  "schema": {
    "id": "abc-123",
    "name": "Contact Form",
    "fields": [
      {
        "name": "email",
        "label": "Email Address",
        "type": "email",
        "required": true,
        "options": null,
        "validation": null
      },
      {
        "name": "country",
        "label": "Country",
        "type": "select",
        "required": false,
        "options": [
          { "label": "United States", "value": "US" },
          { "label": "Canada", "value": "CA" }
        ],
        "validation": null
      }
    ]
  },
  "debug": {
    "groupCount": 1,
    "fieldCount": 0
  }
}

FormSchema Type

id
string
Form identifier
name
string
Form name
fields
FieldSchema[]
Array of field definitions

FieldSchema Type

name
string
Field identifier (unique within form)
label
string
Display label for the field
type
string
Field type (text, email, phone, number, textarea, select, dropdown, radio, checkbox, multiple_checkboxes, etc.)
required
boolean
Whether the field is required
options
FieldOption[] | undefined
Available options for select/radio/checkbox fields
validation
Record<string, unknown> | undefined
Validation rules

FieldOption Type

label
string
Display text for the option
value
string
Submitted value for the option

Error Responses

400
Bad Request
Missing formId parameter
{
  "error": "Missing formId"
}
401
Unauthorized
Not connected to HubSpot
{
  "error": "Not connected to HubSpot"
}
500
Internal Server Error
Failed to fetch form details from HubSpot
{
  "error": "Failed to fetch form details from HubSpot",
  "details": "..."
}

HubSpot API Called

GET https://api.hubapi.com/marketing/v3/forms/{formId}
Authorization: Bearer {accessToken}
Content-Type: application/json

Field Normalization

The endpoint normalizes HubSpot’s field structure:
  1. Extracts fields from formFieldGroups, fieldGroups, or fields arrays
  2. Normalizes field types from type, fieldType, or inputType
  3. Normalizes labels from label or labelText
  4. Normalizes options from options or choices arrays
  5. Filters out invalid fields (missing name)
  6. Ensures consistent option structure (label + value)

Health Check

GET /health
Simple health check endpoint to verify server is running. Location: main/server/src/index.ts:27

Response

{
  "status": "ok"
}

CORS Configuration

The server allows CORS requests from:
  • http://localhost:5173 (default Vite dev server)
  • Any *.trycloudflare.com subdomain
  • Origin header matches allowed origins
Credentials: Enabled Location: main/server/src/index.ts:10-24

Error Handling

All endpoints follow consistent error response format:
{
  error: string;      // Human-readable error message
  details?: string;   // Optional detailed error information
}

Common Status Codes

200
OK
Request successful
302
Found
Redirect (OAuth flow)
400
Bad Request
Invalid request parameters
401
Unauthorized
Not authenticated or session expired
500
Internal Server Error
Server error or external API failure

Token Management

Tokens are stored in an in-memory Map with the following structure:
const tokenStore = new Map<string, TokenRecord>();

type TokenRecord = {
  accessToken: string;
  refreshToken: string;
  expiresAt: number;
  portalId?: number;
};
Key: Portal ID (or “default”) Limitations:
  • Tokens are lost on server restart
  • No automatic token refresh implemented
  • Single portal support (last connected portal)
  • No expiration checking on token usage
Production Recommendations:
  • Implement persistent token storage (database, Redis)
  • Add automatic token refresh logic
  • Support multiple portals per user
  • Check token expiration before use
  • Implement proper session management