Book Demos from PostHog Events with n8n
Ready to automate?
Browse 5,000+ copy-paste n8n workflow templates.
When a user hits a meaningful moment in your product — completing onboarding, reaching a trial limit, activating a key feature — they are at peak intent. That window is short. If your sales or success team sees the signal hours later and sends a generic follow-up, the moment has often already passed.
Most SaaS teams track these product signals in PostHog. The data is there. The problem is acting on it fast enough, and at scale. Manually scanning dashboards and crafting personalized outreach for every engaged user breaks down the moment you have more than a handful of signups per day.
An n8n workflow closes that gap. When PostHog detects a qualifying event, n8n fires immediately — extracting the user's details, building a personalized Cal.com booking link, and delivering an invitation within seconds. No one on your team needs to lift a finger.
What This Workflow Does
This workflow listens for a specific PostHog event (such as trial_limit_reached, onboarding_complete, or upgrade_intent), then automatically sends the user a booking invitation with a pre-filled Cal.com scheduling link. When the recipient clicks through, their name and email are already populated in the Cal.com booking form, removing friction from the process.
The entire sequence completes in under five seconds from the moment the PostHog event fires.
Prerequisites
- A PostHog Cloud or self-hosted PostHog instance with the trigger event already firing in production
- A Cal.com account with at least one event type configured (for example, a 30-minute demo slot)
- An n8n instance, cloud or self-hosted
- An SMTP account or email-sending service reachable from your n8n instance
Building the Workflow
Step 1: Create the Webhook Trigger in n8n
Add a Webhook Trigger node to your canvas. Set the HTTP method to POST and copy the generated webhook URL. This is the endpoint PostHog will call each time a matching event fires.
Keep the node active — n8n shows a "Listening" status while it waits for an incoming request. Send a test event from PostHog to confirm the connection before building the rest of the workflow.
Step 2: Configure a PostHog Webhook Destination
In PostHog, navigate to Data pipeline → Destinations and create a new webhook destination. Paste the n8n webhook URL as the target. Under Filters, select the specific event name you want to act on — for example, trial_limit_reached.
PostHog sends a JSON payload for each matching event containing the user's distinct ID, email address, display name, and any custom properties attached to the event. The exact field paths depend on how your PostHog events are instrumented, but email typically lives at properties.$set.email or properties.email.
Step 3: Filter Incomplete Events with an IF Node
Not every PostHog event payload will include a user email — anonymous sessions, server-side events, or partially identified users can arrive without one. Add an IF node directly after the Webhook Trigger and configure it to check that the email field is present and non-empty.
Route the false branch to a No-op node so the workflow exits cleanly for unidentifiable events. Only the true branch — events with a confirmed email — should continue to the next steps.
Step 4: Extract User Properties with a Set Node
After the IF node's true branch, add a Set node. Use it to pull clean, named values out of the PostHog payload:
- userEmail — mapped from the email field in the event properties
- userName — mapped from the name field, with an expression fallback that derives a first name from the email address if the name property is absent
- eventName — the PostHog event that fired, useful for routing or logging
- distinctId — the PostHog user ID, useful for deduplication later
This Set node creates a flat set of variables you can reference cleanly in subsequent nodes without repeating long nested paths.
Step 5: Build the Personalized Cal.com Booking URL
Cal.com supports query-string pre-filling for booking pages. Appending ?name=Jane+Doe&email=jane@example.com to any Cal.com event URL opens the scheduling page with those fields already filled in. This is one of the most effective ways to reduce booking friction — the user skips the form and goes straight to picking a time.
Add a second Set node to construct this URL. Use n8n expressions to URL-encode the user's name and email and append them to your Cal.com event link, storing the result in a field called bookingUrl. Your Cal.com base URL looks like https://cal.com/yourteam/demo — replace yourteam and demo with your actual username and event slug.
If you want to validate that the event type is still active or pull dynamic availability data, you can optionally add an HTTP Request node here to call the Cal.com REST API (GET /api/v1/event-types) before constructing the link. For most setups this is unnecessary — a static base URL is sufficient.
Step 6: Send the Invitation Email
Add a Send Email node and configure it with:
- To: the
userEmailvalue from the Set node - Subject: something specific to your product — for example, "Let's walk through [Product] together"
- Body: a short, direct message — two or three sentences — with
bookingUrlas the main call-to-action
Keep the email brief. A user who just hit an activation event is already engaged; a long email adds friction before the click. The only job of this email is to get them to open their calendar.
Example n8n Workflow: End-to-End
Here is the complete node sequence:
- Webhook Trigger — receives the PostHog POST request with the event payload
- IF node — checks that the email field is present and non-empty; exits cleanly if not
- Set node (user fields) — maps
userEmail,userName,distinctId, andeventNamefrom the payload - Set node (booking URL) — constructs the pre-filled Cal.com link using URL-encoded user data
- Send Email node — delivers the personalized booking invitation to the user
Five nodes total. The workflow can be set up and tested in under thirty minutes.
Extending the Workflow
Route by event type: Replace the IF node with a Switch node to branch on eventName. A user who hit their trial limit gets a different email and a different Cal.com event type (perhaps a 15-minute "what's holding you back?" call) than one who just completed onboarding (a 30-minute product walkthrough).
Prevent duplicate invitations: Add a Google Sheets or database lookup after Step 3 to check whether you've already sent this user a booking link in the last 14 days. Log the distinctId and timestamp on every send. Route duplicates to a No-op node rather than sending again.
Pass context to Cal.com: Cal.com also accepts a notes query parameter. You can pre-populate it with the triggering event name and any relevant user properties — plan tier, company name, days since signup — so whoever takes the call has context before the meeting starts.
Why This Outperforms Manual Outreach
Speed: Most teams review PostHog data once a day at best. By the time someone sees an activation signal and sends a manual email, 24 hours have passed. This workflow responds in seconds.
Personalization at no extra cost: The booking link arrives pre-filled with the user's name and email. They skip the form entirely. Fewer steps means a measurably higher conversion rate from email click to confirmed booking.
Scales without adding headcount: It does not matter whether two users trigger the event in a day or two hundred. Every one receives the same fast, personalized response without anyone on your team doing additional work.
Get the Template
If you want to skip the manual build, n8nresources.dev/templates has a curated collection of workflow templates for PostHog, Cal.com, and dozens of other developer and SaaS tools. Import a pre-built starting point and customize the event name, Cal.com slug, and email copy for your product.
Enjoyed this article?
Share it with others who might find it useful