Event signal:lesson.completed| Destination:HubSpot| Use case:Lesson Completion Flows| Typical setup:~15 minutes
Workflow outcome
The workflow starts with lesson.completed from your course product and ends with HubSpot holding the lesson, module, and progress context your lifecycle team actually uses.
Before this pattern, progress lives inside the LMS while CRM follow-up stays generic or delayed. After it is in place, your app emits one event, Meshes handles the HubSpot delivery, and HubSpot-native automation can react from current progress state.
Why teams care
Education products, coaching programs, and cohort-based offers need more than signup data in the CRM. They need the CRM to reflect where the student is in the program right now.
That turns HubSpot into a live progress surface for nurture, customer success outreach, and milestone-based upsells instead of a static lead database that never catches up to the learning experience.
This is also where custom sync code gets messy fast. Every added milestone, list, or property change creates more delivery logic unless the event handoff is handled by a routing layer.
What it depends on
These pages stay focused on the workflow outcome, but the setup still needs the right workspace, destination connection, and event path underneath.
You need a workspace and publishable key so your app can emit lesson.completed.
Authorize HubSpot in Meshes and confirm the progress properties or milestone lists you plan to use already exist.
Read moreYour product should emit lesson.completed with the course, lesson, and progress context HubSpot needs.
Decide which fields belong in HubSpot properties and which milestones should be represented as separate list membership states.
The source event
For HubSpot, the event is useful when it carries the identity and progress fields your lifecycle or coaching team actually segments on. That usually means who completed the lesson, what lesson it was, and how far through the course the student has moved.
Event payload
lesson.completed{
"student_id": "stu_2048",
"email": "nina@academyflow.io",
"lesson_id": "lesson_customer-research",
"lesson_title": "Customer Research Fundamentals",
"module_id": "module_growth-foundations",
"course_id": "course_creator-accelerator",
"completion_percentage": 62
}What matters most
HubSpot uses email as the stable contact identifier for the progress handoff.
lesson_title
Makes the current student milestone readable inside the CRM without another lookup.
completion_percentage
Supports progress-aware lifecycle branching, milestone reporting, or success outreach timing.
module_id
Lets you distinguish which stage of the curriculum the student just reached.
course_id
Keeps multi-course workspaces explicit so the CRM state reflects the right program.
Field mapping view
| Event field | Destination target | Why it matters |
|---|---|---|
| HubSpot contact email | Identifies which contact record should receive the progress update. | |
| lesson_title | Current lesson property | Shows the exact milestone the student just completed. |
| completion_percentage | Progress percentage or milestone property | Gives downstream HubSpot workflows a progress signal they can react to. |
| module_id | Module property or milestone list logic | Separates one module milestone from another without guessing from timestamps. |
| course_id | Course property or audience boundary | Keeps different programs from sharing the same CRM follow-up by accident. |
The destination connection
In your Meshes workspace, create the HubSpot connection that will receive lesson.completed. Before you build the rule, confirm the custom progress properties and any milestone lists already exist in HubSpot so Meshes can map the event into the CRM cleanly.
lesson.completed events.Where Meshes matters
Most teams do not need another destination. They need the destination to stay in sync without embedding its delivery quirks, retries, and mapping logic into the product code path.
In Meshes, create one or more HubSpot rules for lesson.completed. Use Update Property to project the lesson, module, and progress fields into the contact record, and add an Add to List rule when a milestone should move the student into a HubSpot-managed sequence.
A sample event
This is the part teams like: the source event stays readable and product-shaped while Meshes owns the destination-facing complexity.
TypeScript example
import MeshesEventsClient from '@mesheshq/events';
const events = new MeshesEventsClient(process.env.WORKSPACE_PUBLISHABLE_KEY!);
await events.emit({
event: 'lesson.completed',
resource: 'course',
resource_id: 'course_creator-accelerator',
payload: {
student_id: 'stu_2048',
email: 'nina@academyflow.io',
lesson_id: 'lesson_customer-research',
lesson_title: 'Customer Research Fundamentals',
module_id: 'module_growth-foundations',
course_id: 'course_creator-accelerator',
completion_percentage: 62,
},
});Destination outcome
Use Send Test Event in Meshes or emit one representative lesson.completed event from your product, then inspect the HubSpot contact. You should see the mapped progress fields updated immediately, and any milestone list rule should place the student into the next HubSpot-managed lifecycle path.
Operational visibility
The difference between a nice demo and a usable product workflow is whether you can see what happened when the destination is slow, misconfigured, or unavailable.
In Meshes
lesson.completed event appears in Meshes and the HubSpot rules are matched.Why teams buy Meshes
What's next
Use Case
See the broader lesson.completed fan-out pattern across CRM, lifecycle email, Slack, and milestone automation.
Integration
Review the HubSpot connection flow, supported actions, and event result details.
Open linkDocs
Use the Meshes event API or SDK to emit lesson.completed from your product.
Compare
Compare one progress-aware CRM handoff with maintaining student-sync glue code yourself.
Open linkGuide
Route the same lesson.completed signal into progress-based Mailchimp email automation.
Guide
See another behavior-driven guide where lifecycle follow-up starts from real progress instead of a timer.
Open linklesson.completed once, let Meshes update the CRM handoff, and keep progress-based nurture or coaching workflows grounded in real student movement.