Module 25 · ai · intermediate
Solutions 25 · AI Lead Qualifier and Booking Agent
None of these have been run against live Gmail, HubSpot or Google Calendar accounts yet.
Exercise 1
New workflow:
- Gmail Trigger (Simplify off, query
in:inbox -from:me newer_than:1d) → CodeParse Reply: take the sender address and strip quoted text:const m = $json; const from = (m.from?.value?.[0]?.address ?? '').toLowerCase(); const text = String(m.text ?? '').split(/\nOn .+wrote:|\n>/)[0].trim(); return [{ json: { from, text, threadId: m.threadId, subject: m.subject } }]; - HTTP Request GET
/webhook/lq/lead/status?email={{ $json.from }}(On Error → Continue). If not found, POST/webhook/lq/leadwith{ email, message: text, channel: "email" }; if found, POST/webhook/lq/replywith{ lead_id, message: text }. - Gmail Reply to a message (message id from the trigger) with
{{ $json.reply }}.
status.js already accepts ?email=. Watch for loops: the trigger's query excludes your own messages.
Exercise 2
After Join Outcome, add IF {{ $json.booked || $json.action === 'handoff' }} → HubSpot Contact → Create or Update (email, firstname from name, company) → HubSpot Engagement → Create (type note, associated contact id) with body:
{{ $('Join Outcome').item.json.summary }}
{{ $('Join Outcome').item.json.booking ? 'Meeting: ' + $('Join Outcome').item.json.booking.label : 'Handoff requested' }}
Both On Error → Continue, so a CRM outage never blocks the booking reply.
Exercise 3
In the cancel lane: Cancel Booking → IF {{ Boolean($json.event_id) }} → Google Calendar Event → Delete (calendar from Settings, event id {{ $json.event_id }}, send updates all, On Error → Continue) → respond. Report event_deleted: !$json.error in the response.
Stretch
- In
decide.js, add an action forx.which === 'reschedule': free the current booking's slot (likecancel.js), set status back toqualified, then runfindSlots()and offer as usual. - Add a
Reschedule APIwebhook (POSTlq/reschedule) routed to the message lane, and include in the confirmation email:Need a different time? Reply "reschedule"(and treat that word inpick.jsaswhich = 'reschedule'). - The old Google event must be deleted (Exercise 3) after the new one is created, not before, so the lead is never left without a meeting.