Skip to content

None of these have been run against live Gmail, HubSpot or Google Calendar accounts yet.

Exercise 1

New workflow:

  1. Gmail Trigger (Simplify off, query in:inbox -from:me newer_than:1d) → Code Parse 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 } }];
    
  2. HTTP Request GET /webhook/lq/lead/status?email={{ $json.from }} (On Error → Continue). If not found, POST /webhook/lq/lead with { email, message: text, channel: "email" }; if found, POST /webhook/lq/reply with { lead_id, message: text }.
  3. 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

  1. In decide.js, add an action for x.which === 'reschedule': free the current booking's slot (like cancel.js), set status back to qualified, then run findSlots() and offer as usual.
  2. Add a Reschedule API webhook (POST lq/reschedule) routed to the message lane, and include in the confirmation email: Need a different time? Reply "reschedule" (and treat that word in pick.js as which = 'reschedule').
  3. 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.