HubSpot Connector
HubSpot Connector
Connect your HubSpot CRM to Brevo via Tajo for bidirectional contact sync, deal tracking, engagement data, and unified marketing automation across both platforms.
Overview
| Property | Value |
|---|---|
| Platform | HubSpot |
| Category | CRM |
| Setup Complexity | Medium |
| Official Integration | Yes |
| Data Synced | Contacts, Companies, Deals, Tickets, Events |
| API Base URL | https://api.hubapi.com |
Features
- Bidirectional contact sync - Keep contacts in sync between HubSpot and Brevo in real time
- Deal pipeline tracking - Sync deal stages and values for revenue-based segmentation
- Company data sync - Associate contacts with company records and firmographic data
- Ticket integration - Track support tickets for customer health scoring
- Engagement tracking - Sync email opens, clicks, meetings, calls, and notes
- Custom object support - Map HubSpot custom objects to Brevo attributes
- Workflow triggers - Use HubSpot lifecycle stage changes to trigger Brevo automations
- Webhook events - Real-time notifications for CRM data changes
Prerequisites
Before you begin, ensure you have:
- A HubSpot account (Free, Starter, Professional, or Enterprise)
- A HubSpot private app or OAuth app with required scopes
- A Brevo account with API access
- A Tajo account
Authentication
Private App Access Token (Recommended)
Create a private app in HubSpot for direct API access with granular scope control.
- Go to HubSpot Settings > Integrations > Private Apps
- Create a new private app
- Configure required scopes
- Copy the access token
curl -X GET "https://api.hubapi.com/crm/v3/objects/contacts" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json"OAuth 2.0
Use OAuth 2.0 for multi-account integrations that require user authorization.
# Authorization URLhttps://app.hubspot.com/oauth/authorize?client_id={client_id}&scope=crm.objects.contacts.read&redirect_uri={redirect_uri}Required Scopes
crm.objects.contacts.readcrm.objects.contacts.writecrm.objects.companies.readcrm.objects.deals.readcrm.objects.deals.writecrm.objects.custom.readcrm.schemas.custom.readConfiguration
Basic Setup
connectors: hubspot: enabled: true access_token: "${HUBSPOT_ACCESS_TOKEN}"
# Data sync options sync: contacts: true companies: true deals: true tickets: true engagements: true
# Sync direction direction: bidirectional # or 'hubspot_to_brevo' | 'brevo_to_hubspot'
# List assignment in Brevo lists: all_contacts: 10 qualified_leads: 11 customers: 12Field Mapping
Map HubSpot properties to Brevo contact attributes:
Default Mappings
| Parameter | Type | Description |
|---|---|---|
email required | string | Contact email (primary identifier) |
firstname optional | string | Maps to FIRSTNAME attribute in Brevo |
lastname optional | string | Maps to LASTNAME attribute in Brevo |
phone optional | string | Maps to SMS attribute for WhatsApp/SMS |
company optional | string | Associated company name |
lifecyclestage optional | string | HubSpot lifecycle stage (subscriber, lead, MQL, SQL, customer) |
hs_lead_status optional | string | Lead qualification status |
hubspot_owner_id optional | string | Assigned sales owner ID |
Custom Property Mapping
field_mapping: # Standard fields email: email firstname: FIRSTNAME lastname: LASTNAME phone: SMS
# CRM fields lifecyclestage: LIFECYCLE_STAGE hs_lead_status: LEAD_STATUS company: COMPANY_NAME
# Deal metrics hs_total_deal_value: DEAL_VALUE num_associated_deals: DEAL_COUNT
# Custom properties preferred_channel: PREFERRED_CHANNEL customer_segment: SEGMENTAPI Endpoints
CRM Objects
| Method | Endpoint | Description |
|---|---|---|
GET | /crm/v3/objects/contacts | List contacts |
POST | /crm/v3/objects/contacts | Create a contact |
PATCH | /crm/v3/objects/contacts/{id} | Update a contact |
GET | /crm/v3/objects/companies | List companies |
GET | /crm/v3/objects/deals | List deals |
POST | /crm/v3/objects/deals | Create a deal |
GET | /crm/v3/objects/tickets | List tickets |
Associations
| Method | Endpoint | Description |
|---|---|---|
GET | /crm/v4/objects/{objectType}/{objectId}/associations/{toObjectType} | Get associations |
PUT | /crm/v4/objects/{objectType}/{objectId}/associations/{toObjectType}/{toObjectId} | Create association |
Engagements
| Method | Endpoint | Description |
|---|---|---|
GET | /crm/v3/objects/calls | List call engagements |
GET | /crm/v3/objects/emails | List email engagements |
GET | /crm/v3/objects/meetings | List meetings |
GET | /crm/v3/objects/notes | List notes |
GET | /crm/v3/objects/tasks | List tasks |
Events
Contact Events
| Event | Trigger | Use Case |
|---|---|---|
contact.creation | New contact created | Welcome flow trigger |
contact.propertyChange | Contact property updated | Attribute sync |
contact.merge | Contacts merged | Deduplication handling |
contact.deletion | Contact deleted | Cleanup in Brevo |
Deal Events
| Event | Trigger | Use Case |
|---|---|---|
deal.creation | New deal created | Sales notification |
deal.propertyChange | Deal stage changed | Pipeline automation |
deal.deletion | Deal removed | Revenue reporting |
Company Events
| Event | Trigger | Use Case |
|---|---|---|
company.creation | New company added | Account-based marketing |
company.propertyChange | Company data updated | Firmographic sync |
Code Examples
Initialize Connector
import { TajoClient } from '@tajo/sdk';
const tajo = new TajoClient({ apiKey: process.env.TAJO_API_KEY, brevoApiKey: process.env.BREVO_API_KEY});
// Connect HubSpotawait tajo.connectors.connect('hubspot', { accessToken: process.env.HUBSPOT_ACCESS_TOKEN});Run Contact Sync
// Full bidirectional syncawait tajo.connectors.sync('hubspot', { type: 'full', resources: ['contacts', 'companies', 'deals'], direction: 'bidirectional', since: '2023-01-01'});
// Check sync statusconst status = await tajo.connectors.status('hubspot');console.log(status);// {// connected: true,// lastSync: '2024-01-15T10:30:00Z',// contactsSynced: 34200,// companiesSynced: 5100,// dealsSynced: 2340// }Handle Webhook Events
// Handle HubSpot webhook notificationsapp.post('/webhooks/hubspot', async (req, res) => { const signature = req.get('X-HubSpot-Signature-v3');
// Verify webhook signature if (!verifyHubSpotSignature(req.body, signature)) { return res.status(401).send('Unauthorized'); }
for (const event of req.body) { await tajo.connectors.handleWebhook('hubspot', { eventType: event.subscriptionType, objectId: event.objectId, propertyName: event.propertyName, propertyValue: event.propertyValue }); }
res.status(200).send('OK');});Rate Limits
HubSpot applies rate limits per private app or OAuth app:
| Plan | Rate Limit | Burst Limit |
|---|---|---|
| Free/Starter | 100 requests/10 seconds | 150 requests/10 seconds |
| Professional | 150 requests/10 seconds | 200 requests/10 seconds |
| Enterprise | 200 requests/10 seconds | 250 requests/10 seconds |
| API add-on | 200 requests/10 seconds | 250 requests/10 seconds |
Additional limits:
- Search API: 5 requests/second per app
- Batch operations: 100 records per batch request
- Daily limit: 500,000 requests/day (OAuth apps)
Rate Limit Handling
HubSpot returns a 429 Too Many Requests response when limits are exceeded. Use exponential backoff and monitor the X-HubSpot-RateLimit-* headers.
Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| 401 Unauthorized | Expired or invalid token | Regenerate private app token or refresh OAuth token |
| Contact not synced | Missing email property | HubSpot contacts require an email for Brevo sync |
| Duplicate contacts | No deduplication rule | Configure merge rules in HubSpot |
| Webhook not received | Subscription not active | Re-register webhook subscriptions |
| Property not mapped | Custom property not created | Create the property in HubSpot first |
Debug Mode
Enable verbose logging:
connectors: hubspot: debug: true log_level: verbose log_webhooks: trueTest Connection
tajo connectors test hubspot# ✓ API connection successful# ✓ Contacts readable# ✓ Companies readable# ✓ Deals readable# ✓ Webhooks registeredBest Practices
- Use private apps over API keys - API keys are deprecated; use private apps for better security
- Implement bidirectional sync carefully - Avoid infinite loops by tracking sync source
- Map lifecycle stages - Use HubSpot lifecycle stages to segment contacts in Brevo
- Batch API requests - Use batch endpoints for bulk operations to stay within rate limits
- Monitor webhook delivery - Set up retry logic and dead letter handling
- Use incremental sync - Sync only changed records using the
lastmodifieddateproperty
Security
- Private App Tokens - Scoped access tokens with granular permissions
- OAuth 2.0 - Industry-standard authorization with refresh token rotation
- Webhook signatures - HMAC-based signature verification (v3)
- TLS encryption - All API communication encrypted in transit
- Scoped permissions - Minimum required scope access per integration