Skip to content

Repository files navigation

HubSpot CRM Sync

Laravel 10 application that maintains a local, read-optimized mirror of HubSpot CRM and presents an authenticated sales dashboard.

Setup

composer install
cp .env.example .env
php artisan key:generate

Configure the database and a durable queue connection in .env, then set:

HUBSPOT_TOKEN=pat-na1-...
HUBSPOT_QUEUE=hubspot
QUEUE_CONNECTION=database

Run the included migrations:

php artisan migrate

HubSpot private app scopes

Grant read access for owners and each enabled CRM object: companies, contacts, deals, tasks, calls, meetings, emails, notes, products, line items, and quotes. Grant the matching CRM schema/property read scopes and deal pipeline read access. In HubSpot's current scope names these are generally crm.objects.<object>.read, crm.schemas.<object>.read, and crm.objects.owners.read; the exact list exposed by HubSpot varies by subscription and enabled products.

Start with only the resources enabled in config/hubspot.php. A missing scope produces a 403 and is intentionally not retried.

Sync commands

# Incremental sync of everything
php artisan hubspot:sync

# One resource
php artisan hubspot:sync contacts

# Configured groups: reference, sales, activities, commerce, relationships
php artisan hubspot:sync sales

# Ignore the local cursor
php artisan hubspot:sync contacts --full

# Inspect local synchronization state
php artisan hubspot:status

All sync commands dispatch jobs; they do not perform API work in the console process.

Queue and scheduler

Run at least one dedicated worker:

php artisan queue:work --queue=hubspot --tries=3

Production cron must invoke Laravel's scheduler every minute:

* * * * * cd /path/to/hubspotSync && php artisan schedule:run >> /dev/null 2>&1

The schedule refreshes deals every 5 minutes, companies/contacts and activities every 10 minutes, relationships every 30 minutes, commerce hourly, and reference metadata daily at 02:00. Multi-resource runs are chained in the configured synchronization order. Scheduler overlap locks, single-server scheduling, and unique resource jobs prevent duplicate concurrent imports. Use a shared cache driver when running multiple application servers.

Real-time webhooks

Expose this HTTPS endpoint publicly and enter the exact URL as the private app's webhook target:

POST https://your-domain.example/api/webhooks/hubspot

Then configure:

HUBSPOT_CLIENT_SECRET=the_secret_from_the_private_app_auth_tab
HUBSPOT_WEBHOOK_URL=https://your-domain.example/api/webhooks/hubspot

Subscribe in HubSpot to creation, property-change, and deletion events for the CRM objects you need. Contacts, companies, deals, tasks, calls, meetings, emails, notes, products, line items, and quotes are recognized. Unsupported events are recorded as ignored.

The endpoint validates both current v3 signatures and the v1 signatures used by legacy private-app webhook subscriptions. It stores each HubSpot event ID once, immediately returns HTTP 202, and queues an authoritative API fetch before upserting the local record. Deletion events mark the local row archived. Keep the hubspot queue worker running for webhook processing.

Dashboard

GET /dashboard is available immediately. Add the application's preferred authentication middleware before exposing it outside a trusted network.

The dashboard is guaranteed never to call HubSpot. Its controller delegates to a local database query, and the view renders only synchronized Eloquent data. HubSpot outages or rate limits therefore cannot block a dashboard request.

Testing

php artisan test
./vendor/bin/pint --test

Focused tests fake Laravel HTTP and verify bearer authentication, 100-record cursor pagination, incremental Search API filters, 429/5xx retries, and command job dispatch.

About

Hub Spot CRM data Sync Service

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages