How to Send Product Events to HubSpot from Your SaaS: Direct API vs Zapier vs Meshes
There are three common ways to send product events like signups, upgrades, and cancellations from your SaaS to HubSpot: build the integration directly against HubSpot's API, send events through Zapier, or use an event routing layer like Meshes. Each approach has different tradeoffs in engineering effort, reliability, and long-term flexibility.
There are three common ways to send product events from your SaaS to HubSpot: build the integration directly against HubSpot's API, send events through a no-code middleware like Zapier, or use an event routing layer like Meshes.
Each option can work. The right choice depends on how much engineering time you want to spend, how reliable the delivery needs to be, and whether HubSpot is your only destination or just the first of several.
This post compares all three approaches with code and practical tradeoffs so you can pick the right fit for your stage.
Comparison at a glance
| Approach | Setup time | Reliability | Multi-destination support | Multi-tenant support | Best for |
|---|
| Direct HubSpot API | Days to weeks for a production-ready integration | You build retries, queues, alerting, and replay | Separate code per destination | You build it | Teams that only need HubSpot and want full control |
| Zapier | Fastest to get working | Better than a one-off webhook, but still task-based and operationally limited | Usually one Zap per destination or workflow | Not practical for customer-specific connected accounts | Quick prototypes and simple internal workflows |
| Meshes | Fast setup without building integration plumbing yourself | Built for retries, routing, and observability | One event can fan out to many destinations | Built around workspace isolation | SaaS teams that expect more than one destination or more than one customer connection |
Why this matters
Your product knows when someone signs up, upgrades, cancels, invites a teammate, or hits a usage milestone. Your CRM usually does not.
That gap matters more than it seems. If HubSpot is missing product context, your sales and marketing data drifts out of sync. Lifecycle stages become stale. Workflows trigger late or not at all. Revenue signals stay trapped inside your app instead of reaching the systems your team actually uses.
The three approaches below solve that gap in very different ways.
Approach 1: Direct HubSpot API integration
The most obvious option is to call HubSpot directly from your backend whenever a product event happens.
Here is a simplified example using the official HubSpot Node.js SDK. It is useful for showing the shape of the integration, but it is not a full production implementation:
import hubspot from '@hubspot/api-client';
const client = new hubspot.Client({
accessToken: process.env.HUBSPOT_ACCESS_TOKEN,
numberOfApiCallRetries: 3,
});
async function syncSignupToHubSpot(event: {
email: string;
firstName: string;
lastName: string;
plan: string;
source: string;
}) {
const properties = {
email: event.email,
firstname: event.firstName,
lastname: event.lastName,
lifecyclestage: 'lead',
hs_lead_status: 'NEW',
signup_source: event.source,
product_plan: event.plan,
signup_date: new Date().toISOString(),
};
try {
await client.crm.contacts.basicApi.create({ properties });
} catch (err) {
console.error('HubSpot sync failed:', err);
}
}
At first glance, this looks straightforward. But once the integration has to serve real customers in production, the complexity expands quickly.
OAuth and token lifecycle. If your customers are connecting their own HubSpot accounts, you need the full OAuth flow: authorization redirect, token exchange, secure token storage, refresh token handling, expiration logic, and protection against refresh race conditions.
Rate limits and queueing. HubSpot's publicly distributed OAuth apps are limited to 110 requests every 10 seconds, and the CRM Search API has its own separate burst limit. That means meaningful event volume eventually requires request throttling, queueing, and retry strategy rather than just "call the SDK and hope."
Retries and dead-letter handling. In production, a failed request is not just a log line. You need to distinguish between permanent failures and retryable ones, back off correctly, surface alerts, and preserve failed events for replay.
Field mapping. Your product schema and HubSpot property schema are rarely a perfect match. Signups, upgrades, cancellations, trial conversions, and usage milestones all need different mapping logic. Some of those fields may also depend on custom properties that do not exist yet in the target HubSpot account.
Operational overhead. Once you go beyond a single event type, you are also maintaining queues, alerting, dashboards, support tooling, and migration code whenever HubSpot changes behavior or your own schema evolves.
None of this means the direct approach is wrong. It just means the real comparison is not "one API call versus a platform." It is "one API call plus all the reliability and maintenance work around it."
Best for: Teams that only need HubSpot, have the engineering capacity to own the integration long term, and want full control over every API call.
Watch out for: OAuth edge cases, retry policy, replay tooling, custom property management, and the maintenance burden that comes with every new event type.
Approach 2: Zapier or no-code middleware
If you want something faster than a custom HubSpot integration, the common alternative is to send your events into Zapier and let Zapier connect the rest.
The basic pattern looks like this:
- Your app sends a webhook when an event occurs
- Zapier receives the payload
- Zapier maps the fields into HubSpot
- Zapier creates or updates the record
From your app's point of view, the code is simple:
async function sendToZapier(event: {
type: string;
email: string;
firstName: string;
plan: string;
}) {
await fetch(process.env.ZAPIER_WEBHOOK_URL!, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(event),
});
}
That simplicity is the appeal. You do not need to build HubSpot OAuth, property mapping UI, or direct API calls yourself.
But the tradeoffs show up quickly.
Pricing scales with tasks. Zapier is extremely approachable at low volume, but the pricing model is still task-based. If one event triggers multiple downstream actions, task usage climbs faster than many teams expect.
You still own the event handoff. Your app still has to send the webhook reliably. If that request fails before it reaches Zapier, you still need your own retry strategy or the event is gone.
Limited control over failure handling. Zapier is convenient, but it is not built as a dedicated event delivery layer for your product. Failed steps, replay workflows, and high-volume fan-out are not where it feels strongest.
Workflow sprawl. As you add destinations, conditions, and edge cases, logic moves into Zapier's UI. That can be fast at first and messy later, especially when you want to version, test, or audit the behavior.
Multi-tenant SaaS is awkward. If each of your customers needs to connect their own HubSpot account, Zapier becomes much less attractive. It works best when you are wiring together your own internal tools, not building customer-isolated infrastructure into your product.
Best for: Early validation, simple internal automations, and teams that want the fastest possible path to "something working."
Watch out for: Task-based costs, brittle workflow sprawl, limited replay control, and the fact that you still need reliable event delivery from your app into Zapier.
Approach 3: Event routing layer (Meshes)
The third option is to treat product events as a routing problem instead of a one-off integration problem.
With an event routing layer, your app emits events once. The routing layer handles delivery, retries, mapping, and fan-out to whichever destinations are configured for that workspace.
Here is the same idea with Meshes:
import { MeshesEventsClient } from '@mesheshq/events';
const meshes = new MeshesEventsClient(process.env.MESHES_PUBLISHABLE_KEY!);
await meshes.emit({
event: 'contact.created',
payload: {
email: 'jane@example.com',
first_name: 'Jane',
last_name: 'Doe',
plan: 'pro',
utm_source: 'google-ads',
},
});
That single event can then be routed to HubSpot and other destinations without changing your application code each time.
The main difference is where the complexity lives.
One integration point. Your app emits a stable event model. Adding a new destination is a configuration problem instead of a rebuild-your-backend problem.
Reliability as a platform concern. Retries, backoff, dead letters, and delivery visibility live in the routing layer instead of being recreated inside your app for each destination.
Workspace isolation. If you are building a multi-tenant SaaS, each customer can have their own isolated connections and routing rules without you hand-rolling the same tenant-aware plumbing over and over.
Less destination-specific code. Your application code focuses on emitting business events. The destination-specific mapping and delivery logic lives outside your core app.
Best for: SaaS teams that expect multiple destinations, customer-specific integrations, or production requirements that go beyond "fire one webhook and hope it lands."
Watch out for: If you truly only need one HubSpot connection and a handful of event types, this may be more platform than you need on day one.
A more practical way to compare the three
The fastest way to understand the tradeoffs is to ask one question:
What happens when you need to add a second destination or a second customer-specific HubSpot connection?
With the direct API approach, you are now building more integration code.
With Zapier, you are now building more workflow surface area.
With an event routing layer, you are usually extending configuration on top of the same event model you already have.
That is the point where the right answer often becomes clearer.
Current cost framing
Cost is part of the decision, but it is important to compare the right things.
A direct integration looks cheap at first because there is no platform bill. In reality, you are paying in engineering time, support burden, queueing infrastructure, monitoring, and maintenance.
Zapier looks cheap because it gets you to a demo quickly. That can absolutely be the right call. Paid plans currently start at $19.99/month for 750 tasks, and Team starts at $69/month for 2,000 tasks. But task-based pricing has a way of getting more expensive once one product event starts triggering several downstream actions.
Meshes is the opposite tradeoff. You are paying for the routing layer up front, but you are not also building and maintaining that routing layer yourself. Meshes currently starts free at 100 events per month, with Builder at $41/month annually and Pro at $166/month annually.
Which approach should you choose?
Choose the direct HubSpot API if HubSpot is the only destination that matters, the integration surface is small, and your team wants maximum control.
Choose Zapier if you want to validate the workflow quickly, keep engineering effort low, and you do not yet need strong multi-tenant or multi-destination architecture.
Choose Meshes if you want a single event model that can support HubSpot now and other destinations later without repeatedly rebuilding the plumbing.
Most SaaS teams do not stay in the same stage forever. A direct integration can be the right starting point. Zapier can be the right shortcut. But if you expect multiple destinations, customer-specific connections, or production-grade delivery requirements, it is usually worth thinking about the routing layer earlier than you think.