Install
openclaw skills install hubspot-apiHubSpot CRM API integration with managed OAuth. Manage contacts, companies, deals, and associations. Use this skill when users want to create or update CRM records, search contacts, or sync data with HubSpot. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).
openclaw skills install hubspot-apiAccess the HubSpot CRM API with managed OAuth authentication. Create and manage contacts, companies, deals, and their associations.
CLI:
maton hubspot contact list -L 10 --properties email,firstname,lastname
maton api '/hubspot/crm/v3/objects/contacts?limit=10&properties=email,firstname,lastname'
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/hubspot/crm/v3/objects/contacts?limit=10&properties=email,firstname,lastname')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
https://api.maton.ai/hubspot/{native-api-path}
Maton proxies requests to api.hubapi.com and automatically injects your OAuth token.
NPM:
npm install -g @maton-ai/cli
Homebrew:
brew install maton-ai/cli/maton
CLI:
maton login # Opens browser for API key
maton login --interactive # Skip browser, paste API key directly
maton whoami # Show current auth state
Manual:
MATON_API_KEY:export MATON_API_KEY="YOUR_API_KEY"
Manage your HubSpot OAuth connections at https://api.maton.ai.
CLI:
maton connection list hubspot --status ACTIVE
maton api -X GET /connections -f app=hubspot -f status=ACTIVE
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections?app=hubspot&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
CLI:
maton connection create hubspot
maton api /connections -f app=hubspot
Python:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'hubspot'}).encode()
req = urllib.request.Request('https://api.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
CLI:
maton connection view {connection_id}
maton api /connections/{connection_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "{connection_id}",
"status": "ACTIVE",
"creation_time": "2025-12-08T07:20:53.488460Z",
"last_updated_time": "2026-01-31T20:03:32.593153Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "hubspot",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
CLI:
maton connection delete {connection_id}
maton api -X DELETE /connections/{connection_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple HubSpot connections, specify which one to use:
CLI:
maton hubspot contact list --properties email,firstname,lastname --connection {connection_id}
maton api /hubspot/crm/v3/objects/contacts --connection {connection_id}
Python:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/hubspot/crm/v3/objects/contacts')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple connections, always specify the connection to ensure requests go to the intended account.
GET /hubspot/crm/v3/objects/contacts?limit=100&properties=email,firstname,lastname,phone
Example:
maton hubspot contact list --properties email,firstname,lastname,phone -L 100
GET /hubspot/crm/v3/objects/contacts/{contactId}?properties=email,firstname,lastname
Example:
maton hubspot contact view <contactId> --properties email,firstname,lastname
POST /hubspot/crm/v3/objects/contacts
Content-Type: application/json
{
"properties": {
"email": "john@example.com",
"firstname": "John",
"lastname": "Doe",
"phone": "+1234567890"
}
}
Example:
maton hubspot contact create --set email=john@example.com --set firstname=John --set lastname=Doe --set phone=+1234567890
PATCH /hubspot/crm/v3/objects/contacts/{contactId}
Content-Type: application/json
{
"properties": {
"phone": "+0987654321"
}
}
Example:
maton hubspot contact update <contactId> --set phone=+0987654321
DELETE /hubspot/crm/v3/objects/contacts/{contactId}
Example:
maton hubspot contact archive <contactId>
POST /hubspot/crm/v3/objects/contacts/search
Content-Type: application/json
{
"filterGroups": [{
"filters": [{
"propertyName": "email",
"operator": "EQ",
"value": "john@example.com"
}]
}],
"properties": ["email", "firstname", "lastname"]
}
Example:
maton hubspot contact search --filter email:EQ:john@example.com --properties email,firstname,lastname
GET /hubspot/crm/v3/objects/companies?limit=100&properties=name,domain,industry
Example:
maton hubspot company list --properties name,domain,industry -L 100
GET /hubspot/crm/v3/objects/companies/{companyId}?properties=name,domain,industry
Example:
maton hubspot company view <companyId> --properties name,domain,industry
POST /hubspot/crm/v3/objects/companies
Content-Type: application/json
{
"properties": {
"name": "Acme Corp",
"domain": "acme.com",
"industry": "COMPUTER_SOFTWARE"
}
}
Example:
maton hubspot company create --set name='Acme Corp' --set domain=acme.com --set industry=COMPUTER_SOFTWARE
Note: The industry property requires specific enum values (e.g., COMPUTER_SOFTWARE, FINANCE, HEALTHCARE). Use the List Properties endpoint to get valid values.
PATCH /hubspot/crm/v3/objects/companies/{companyId}
Content-Type: application/json
{
"properties": {
"industry": "COMPUTER_SOFTWARE",
"numberofemployees": "50"
}
}
Example:
maton hubspot company update <companyId> --set industry=COMPUTER_SOFTWARE --set numberofemployees=50
DELETE /hubspot/crm/v3/objects/companies/{companyId}
Example:
maton hubspot company delete <companyId>
POST /hubspot/crm/v3/objects/companies/search
Content-Type: application/json
{
"filterGroups": [{
"filters": [{
"propertyName": "domain",
"operator": "CONTAINS_TOKEN",
"value": "*"
}]
}],
"properties": ["name", "domain"],
"limit": 10
}
Example:
maton hubspot company search --filter 'domain:CONTAINS_TOKEN:*' --properties name,domain -L 10
GET /hubspot/crm/v3/objects/deals?limit=100&properties=dealname,amount,dealstage
Example:
maton hubspot deal list --properties dealname,amount,dealstage -L 100
GET /hubspot/crm/v3/objects/deals/{dealId}?properties=dealname,amount,dealstage
Example:
maton hubspot deal view <dealId> --properties dealname,amount,dealstage
POST /hubspot/crm/v3/objects/deals
Content-Type: application/json
{
"properties": {
"dealname": "New Deal",
"amount": "10000",
"dealstage": "appointmentscheduled"
}
}
Example:
maton hubspot deal create --set dealname='New Deal' --set amount=10000 --set dealstage=appointmentscheduled
PATCH /hubspot/crm/v3/objects/deals/{dealId}
Content-Type: application/json
{
"properties": {
"amount": "15000",
"dealstage": "qualifiedtobuy"
}
}
Example:
maton hubspot deal update <dealId> --set amount=15000 --set dealstage=qualifiedtobuy
DELETE /hubspot/crm/v3/objects/deals/{dealId}
Example:
maton hubspot deal delete <dealId>
PUT /hubspot/crm/v4/objects/{fromObjectType}/{fromObjectId}/associations/{toObjectType}/{toObjectId}
Content-Type: application/json
[{"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 279}]
Example:
maton hubspot associations create --from contacts:<fromObjectId> --to companies:<toObjectId> --type 279
Common association type IDs:
279 - Contact to Company3 - Deal to Contact341 - Deal to CompanyGET /hubspot/crm/v4/objects/{objectType}/{objectId}/associations/{toObjectType}
Example:
maton hubspot associations list --from contacts:12345 --to companies
Native batch subcommands are available for contact, company, and deal.
POST /hubspot/crm/v3/objects/{objectType}/batch/read
Content-Type: application/json
{
"properties": ["email", "firstname"],
"inputs": [{"id": "123"}, {"id": "456"}]
}
Example:
maton hubspot contact batch-read --id 123,456 --properties email,firstname
POST /hubspot/crm/v3/objects/{objectType}/batch/create
Content-Type: application/json
{
"inputs": [
{"properties": {"email": "one@example.com", "firstname": "One"}},
{"properties": {"email": "two@example.com", "firstname": "Two"}}
]
}
Example:
maton hubspot contact batch-create --data '[{"properties":{"email":"one@example.com","firstname":"One"}},{"properties":{"email":"two@example.com","firstname":"Two"}}]'
POST /hubspot/crm/v3/objects/{objectType}/batch/update
Content-Type: application/json
{
"inputs": [
{"id": "123", "properties": {"firstname": "Updated"}},
{"id": "456", "properties": {"firstname": "Also Updated"}}
]
}
Example:
maton hubspot contact batch-update --data '[{"id":"123","properties":{"firstname":"Updated"}},{"id":"456","properties":{"firstname":"Also Updated"}}]'
POST /hubspot/crm/v3/objects/{objectType}/batch/archive
Content-Type: application/json
{
"inputs": [{"id": "123"}, {"id": "456"}]
}
Example:
maton hubspot contact batch-archive --id 123,456
GET /hubspot/crm/v3/properties/{objectType}
Example:
maton hubspot properties list --type contacts
EQ - Equal toNEQ - Not equal toLT / LTE - Less than / Less than or equalGT / GTE - Greater than / Greater than or equalCONTAINS_TOKEN - Contains tokenNOT_CONTAINS_TOKEN - Does not contain tokenList endpoints return a paging.next.after cursor:
{
"results": [...],
"paging": {
"next": {
"after": "12345"
}
}
}
Use the after query parameter to fetch the next page:
GET /hubspot/crm/v3/objects/contacts?limit=100&after=12345
const response = await fetch('https://api.maton.ai/hubspot/crm/v3/objects/contacts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
},
body: JSON.stringify({
properties: { email: 'john@example.com', firstname: 'John' }
})
});
import os
import requests
response = requests.post(
'https://api.maton.ai/hubspot/crm/v3/objects/contacts',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
json={'properties': {'email': 'john@example.com', 'firstname': 'John'}}
)
industry property on companies requires specific enum valuescurl -g when URLs contain brackets (fields[], sort[], records[]) to disable glob parsingjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments. You may get "Invalid API key" errors when piping.| Status | Meaning |
|---|---|
| 400 | Missing HubSpot connection |
| 401 | Invalid or missing Maton API key |
| 429 | Rate limited (10 req/sec per account) |
| 4xx/5xx | Passthrough error from HubSpot API |
MATON_API_KEY environment variable is set:echo $MATON_API_KEY
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
hubspot. For example:https://api.maton.ai/hubspot/crm/v3/objects/contactshttps://api.maton.ai/crm/v3/objects/contacts