Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

Connect AI agents to HubSpot

Scalekit connector
Open markdown

The HubSpot connector lets your AI agent act in each user's HubSpot account. Each user signs in to HubSpot once, and Scalekit stores and refreshes their tokens, so your agent never handles credentials. It comes with 457 tools.

Tools
457
What they doRead · write · destructive
198 · 179 · 80198 read179 write80 destructive
Users sign in with
OAuth app
Scalekit's or your own

What you can do

  • Manage contacts: create, update, search, and list contacts; batch create, update, upsert, read, and archive
  • Manage companies and deals: create and update company records and deals; batch create, update, upsert, read, and archive
  • Manage tickets and tasks: create and update support tickets; create tasks with due dates and priorities
  • Batch operations with inline associations: create contacts, companies, deals, or tickets and link them to related records in a single call
  • Log engagements: record calls, meetings, notes, and emails against any CRM record
  • Search, associate, and extend: full-text search across all CRM objects, batch-manage associations, list owners, discover properties, and work with custom objects

Setup

  1. Install the SDK

    Terminal window
    npm install @scalekit-sdk/node dotenv
  2. Set your credentials

    Add your Scalekit credentials to your .env file. Find values in app.scalekit.com > Developers > API Credentials.

    .env
    SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
    SCALEKIT_CLIENT_ID=<your-client-id>
    SCALEKIT_CLIENT_SECRET=<your-client-secret>
  3. Create the HubSpot connection

    In AgentKit > Connections, create a HubSpot connection. The name you give it is the connection_name your code passes. See Configure connections.

    Scalekit credentials are available for HubSpot, so you don't need to register an OAuth app. To show your own app on the consent screen, use your own credentials instead.

    Use your own OAuth app

    Register your Scalekit environment with the HubSpot connector so Scalekit handles the authentication flow and token lifecycle for you. The connection name you create will be used to identify and invoke the connection programmatically. Then complete the configuration in your application as follows:

    1. Set up auth redirects

      • In Scalekit dashboard, go to AgentKit > Connections > Create Connection. Find HubSpot and click Create. Copy the redirect URI. It looks like https://<SCALEKIT_ENVIRONMENT_URL>/sso/v1/oauth/<CONNECTION_ID>/callback.

        Copy redirect URI from Scalekit dashboard

      • Log in to your HubSpot developer dashboard, click Manage apps, click Create app, and select Public app. If you already have an existing HubSpot app, open that app instead — see the Choosing a HubSpot app type section above for guidance on Public, Private, and legacy apps.

      • Go to Auth > Auth settings > Redirect URL, paste the redirect URI from Scalekit, and click Save.

        Adding redirect URL to HubSpot

      • Under Auth > Auth settings > Scopes, select the scopes your application needs. The scopes you select here must match exactly what you configure in Scalekit. For a read-only CRM enrichment flow, start with:

        crm.objects.contacts.read
        crm.objects.companies.read
        crm.objects.deals.read

        These assume a modern Public app with dotted scope names. For legacy apps or the full scope reference, see the Required and optional scopes section on this page.

    2. Get client credentials

      • In your HubSpot app, go to Auth > Auth settings.

      • Copy your Client ID and Client Secret.

    3. Add credentials in Scalekit

      • In Scalekit dashboard, go to AgentKit > Connections and open the connection you created.

      • Enter your credentials:

        • Client ID (from your HubSpot app)
        • Client Secret (from your HubSpot app)
        • Permissions (OAuth scope strings such as crm.objects.contacts.read, entered exactly as configured in the HubSpot app). For a full list of available scopes and guidance on optional scopes, see the Required and optional scopes section on this page.

        Add credentials in Scalekit dashboard

      • Click Save.

  4. Authorize a user and make your first call

    quickstart.mts
    import { ScalekitClient } from '@scalekit-sdk/node'
    import 'dotenv/config'
    import { createInterface } from 'node:readline/promises'
    const scalekit = new ScalekitClient(
    process.env.SCALEKIT_ENVIRONMENT_URL,
    process.env.SCALEKIT_CLIENT_ID,
    process.env.SCALEKIT_CLIENT_SECRET,
    )
    const actions = scalekit.actions
    const connector = 'hubspot'
    const identifier = 'user_123'
    // Generate an authorization link for the user
    const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
    console.log('Authorize HubSpot:', link)
    const rl = createInterface({ input: process.stdin, output: process.stdout })
    await rl.question('Press Enter after authorizing...')
    rl.close()
    // Make your first call — list CRM owners
    const result = await actions.executeTool({
    connector,
    identifier,
    toolName: 'hubspot_owners_list',
    toolInput: {},
    })
    console.log('HubSpot owners:', result)
    Terminal window
    npx tsx quickstart.mts

    Each user signs in once. See Authorize a user for the full flow and statuses.

HubSpot has three app shapes. The shape you choose determines which OAuth flow, scope format, and Scalekit configuration apply.

App typeOAuth redirectScope formatUse with Scalekit
Public appSupportedModern (crm.objects.contacts.read)Recommended
Private appNot supportedN/A — static API token onlyNot supported
Legacy / developer-account appSupportedBare strings (contacts, automation)Supported — enter bare strings in Permissions

Public apps are the standard choice for production integrations. They support the OAuth redirect flow that Scalekit manages, and they use the modern dotted scope format.

Private apps issue static API tokens and have no OAuth redirect endpoint. Scalekit’s HubSpot connector requires an OAuth flow, so Private apps are not compatible.

Legacy apps (older apps created in HubSpot developer test accounts before the current console) still support OAuth but use an older scope vocabulary. If you already have a legacy app, you can connect it — you just need to enter the older bare scope strings exactly as HubSpot lists them in that app’s Auth > Scopes page.

HubSpot’s OAuth connection requires one scope and supports up to 23 optional scopes. Grant only the scopes your tools actually need — a smaller scope set means a simpler consent screen and a faster app review for public listings.

Required scope

oauth — included automatically on every HubSpot connection. You do not need to add it manually.

Optional scopes

Add scopes that match the tools you plan to call. Common choices:

ScopeEnables
crm.objects.contacts.readRead contacts
crm.objects.contacts.writeCreate and update contacts
crm.objects.companies.readRead companies
crm.objects.companies.writeCreate and update companies
crm.objects.deals.readRead deals
crm.objects.deals.writeCreate and update deals
crm.objects.line_items.readRead line items
crm.objects.line_items.writeCreate and update line items
crm.objects.quotes.readRead quotes
crm.lists.readRead contact lists
crm.lists.writeCreate and manage contact lists
ticketsRead and write support tickets
formsRead forms and form submissions
automationRead and trigger workflows and engagements
e-commerceProducts and orders

See HubSpot’s scope reference for the full list.

Configure optional scopes in your HubSpot app

In your HubSpot app, go to Auth > Auth settings > Scopes. You’ll see three categories: Required scopes (always requested), Conditionally required scopes, and Optional scopes (requested only when the user’s account has access to them).

HubSpot Scopes page showing Required, Conditionally required, and Optional scopes sections

Click Add new scope and select the optional scopes your app needs. Optional scopes let users without access to a feature still install your app — HubSpot simply skips those scopes at consent time.

Enable the same optional scopes in Scalekit

  1. Open the connection in AgentKit > Connections.
  2. In the Permissions field, enter the scopes you need, space-separated. Example for a read-only CRM flow: crm.objects.contacts.read crm.objects.companies.read crm.objects.deals.read.
  3. Make sure the scope set here matches exactly what you’ve configured in your HubSpot app. A mismatch causes an invalid_scope error when the user authorizes.

Most HubSpot batch and update tools require record IDs. Always fetch IDs from the API — never guess or hard-code them.

ResourceTool to get IDField in response
Contact IDhubspot_contacts_search or hubspot_contacts_listresults[].id
Company IDhubspot_companies_searchresults[].id
Deal IDhubspot_deals_searchresults[].id
Ticket IDhubspot_tickets_searchresults[].id
Line Item IDhubspot_deal_line_items_getresults[].id
Product IDhubspot_products_listresults[].id
Owner IDhubspot_owners_listresults[].id
Pipeline IDhubspot_deal_pipelines_listresults[].id
Pipeline Stage IDhubspot_deal_pipelines_listresults[].stages[].id
Custom Object Type IDhubspot_schemas_listresults[].objectTypeId
Custom Object Record IDhubspot_custom_object_records_searchresults[].id
Quote IDhubspot_quote_getid

Association type IDs

When linking records, use the correct association_type_id for the object pair:

From → ToAssociation Type ID
Contact → Company (primary)1
Contact → Company279
Contact → Deal4
Contact → Ticket15
Deal → Contact3
Deal → Company5
Ticket → Contact16
Ticket → Company340
Line Item → Deal20
Company → Contact280
Company → Deal6

Tools

Pass the exact name to execute_tool
Try in PlaygroundRequest a tool
  • hubspot_account_details_getRetrieve account details for the HubSpot portal including hub ID, timezone, currency, and data hosting location.Read-only

    Get Account Details

    Retrieve account details for the HubSpot portal including hub ID, timezone, currency, and data hosting location.

    Inputs

    This tool takes no inputs.

    Also accepts schema_version and tool_version to pin a version.

  • hubspot_all_files_listList files stored in the HubSpot file manager, with pagination and date-range filtering.Read-only

    List Files

    List files stored in the HubSpot file manager, with pagination and date-range filtering. Requires the 'files' scope.

    Inputs

    afterstring
    Pagination cursor for the next page of results.
    archivedboolean
    If true, return only archived (soft-deleted) records.
    created_afterstring
    Return records created on or after this ISO 8601 datetime.
    created_beforestring
    Return records created on or before this ISO 8601 datetime.
    limitinteger
    Maximum number of results to return per page (max 100).
    parent_folder_idstring
    Restrict results to files inside a specific folder.
    propertiesstring
    Comma-separated list of additional file properties to include in the response.
    sortstring
    Property to sort results by; prefix with '-' for descending order.
    updated_afterstring
    Return records updated on or after this ISO 8601 datetime.
    updated_beforestring
    Return records updated on or before this ISO 8601 datetime.

    Also accepts schema_version and tool_version to pin a version.

  • hubspot_association_labels_listList all association label definitions between two CRM object types.Read-only

    List Association Labels

    List all association label definitions between two CRM object types.

    Inputs

    from_object_typestringrequired
    From object type.
    to_object_typestringrequired
    To object type.

    Also accepts schema_version and tool_version to pin a version.

  • hubspot_association_limits_getRetrieve the configured association limits between two specific CRM object types.Read-only

    Get Association Limits

    Retrieve the configured association limits between two specific CRM object types.

    Inputs

    from_object_typestringrequired
    The source CRM object type.
    to_object_typestringrequired
    The target CRM object type.
  • hubspot_association_limits_listRetrieve all configured association limits across every object type pair in the account.Read-only

    List Association Limits

    Retrieve all configured association limits across every object type pair in the account.

    Inputs

    This tool takes no inputs.

  • hubspot_associations_batch_readRetrieve associations (including labels) for many CRM records at once in a single batch call, given their IDs.Read-only

    Batch Read Associations

    Retrieve associations (including labels) for many CRM records at once in a single batch call, given their IDs.

    Inputs

    from_object_typestringrequired
    The CRM object type of the source records.
    inputsstringrequired
    JSON array of {"id": "<record_id>"} objects to fetch associations for. Pass as a JSON array via the SDK, not as a string.
    to_object_typestringrequired
    The CRM object type to fetch associations for.
  • hubspot_audit_logs_getRetrieve account audit logs filtered by user, event type, object type, or date range.Read-only

    Get Audit Logs

    Retrieve account audit logs filtered by user, event type, object type, or date range.

    Inputs

    acting_user_idinteger
    Filter by user ID who performed the action.
    afterstring
    Pagination cursor.
    fill_final_timestampboolean
    Include final timestamp in response.
    limitinteger
    Maximum number of results per page.
    occurred_afterstring
    Return logs after this timestamp.
    occurred_beforestring
    Return logs before this timestamp.
    sortstring
    Sort parameters.

    Also accepts schema_version and tool_version to pin a version.

  • hubspot_blog_author_getRetrieve a single blog author from HubSpot CMS by author ID.Read-only

    Get Blog Author

    Retrieve a single blog author from HubSpot CMS by author ID. Requires the 'content' scope.

    Inputs

    author_idstringrequired
    ID of the blog author to retrieve.
    propertystring
    Comma-separated list of blog author properties to include in the response.

    Also accepts schema_version and tool_version to pin a version.

Workflows

Proxy API call
const result = await actions.request({
connectionName: 'hubspot',
identifier: 'user_123',
path: '/crm/v3/owners',
method: 'GET',
});
console.log(result);
Create a contact
const contact = await actions.executeTool({
connector: 'hubspot',
identifier: 'user_123',
toolName: 'hubspot_contact_create',
toolInput: {
email: 'jane.smith@acme.com',
firstname: 'Jane',
lastname: 'Smith',
jobtitle: 'VP of Engineering',
company: 'Acme Corp',
lifecyclestage: 'lead',
},
});
console.log('Created contact ID:', contact.id);
Search deals
const deals = await actions.executeTool({
connector: 'hubspot',
identifier: 'user_123',
toolName: 'hubspot_deals_search',
toolInput: {
query: 'enterprise',
filterGroups: JSON.stringify([{
filters: [{ propertyName: 'dealstage', operator: 'EQ', value: 'qualifiedtobuy' }]
}]),
properties: 'dealname,amount,dealstage,closedate',
limit: 10,
},
});
console.log('Found deals:', deals.results);
Log a call
const call = await actions.executeTool({
connector: 'hubspot',
identifier: 'user_123',
toolName: 'hubspot_call_log',
toolInput: {
hs_call_title: 'Q4 Renewal Discussion',
hs_timestamp: new Date().toISOString(),
hs_call_body: 'Discussed renewal terms. Customer is interested in the enterprise plan.',
hs_call_direction: 'OUTBOUND',
hs_call_duration: 900000, // 15 minutes in ms
hs_call_status: 'COMPLETED',
},
});
console.log('Logged call ID:', call.id);
Create and associate a ticket
// Create the ticket
const ticket = await actions.executeTool({
connector: 'hubspot',
identifier: 'user_123',
toolName: 'hubspot_ticket_create',
toolInput: {
subject: 'Cannot export data to CSV',
hs_pipeline_stage: '1', // "New" stage
content: 'Customer reports that the CSV export button is unresponsive on the Reports page.',
hs_ticket_priority: 'HIGH',
},
});
// Associate with a contact
await actions.executeTool({
connector: 'hubspot',
identifier: 'user_123',
toolName: 'hubspot_association_create',
toolInput: {
from_object_type: 'tickets',
from_object_id: ticket.id,
to_object_type: 'contacts',
to_object_id: '12345',
},
});
console.log('Ticket created and associated:', ticket.id);